From fd17ba1d105d773b8cccb1f7863fc15e1fcd7922 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 21 Mar 2022 16:45:30 +0800 Subject: [PATCH 0001/2187] machine_manager: add 'server' and 'nowait' member for Socket Allow not assign 'server' and 'nowait' for chardev. e.g. '-chardev socket,id=chardevid,path=/path/to/socket_file' Signed-off-by: Yan Wang --- devices/src/legacy/chardev.rs | 16 +++++-- machine_manager/src/cmdline.rs | 13 +++++- machine_manager/src/config/chardev.rs | 62 ++++++++++++++++++++------- virtio/src/console.rs | 2 +- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index a1a120593..a80233c66 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -92,7 +92,17 @@ impl Chardev { self.input = Some(master_arc.clone()); self.output = Some(master_arc); } - ChardevType::Socket(path) => { + ChardevType::Socket { + path, + server, + nowait, + } => { + if !*server || !*nowait { + bail!( + "Argument \'server\' and \'nowait\' is need for chardev \'{}\'", + path + ); + } let sock = UnixListener::bind(path.clone()) .chain_err(|| format!("Failed to bind socket for chardev, path:{}", path))?; self.listener = Some(sock); @@ -200,7 +210,7 @@ fn get_notifier_handler( } None }), - ChardevType::Socket(_) => Box::new(move |_, _| { + ChardevType::Socket { .. } => Box::new(move |_, _| { let mut locked_chardev = chardev.lock().unwrap(); let (stream, _) = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); @@ -273,7 +283,7 @@ impl EventNotifierHelper for Chardev { )); } } - ChardevType::Socket(_) => { + ChardevType::Socket { .. } => { if let Some(listener) = chardev.lock().unwrap().listener.as_ref() { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 24c34abcd..b6846e717 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -485,7 +485,18 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< } if let Some(cfg) = vm_config.chardev.remove(&chardev) { - if let ChardevType::Socket(path) = cfg.backend { + if let ChardevType::Socket { + path, + server, + nowait, + } = cfg.backend + { + if !server || !nowait { + bail!( + "Argument \'server\' and \'nowait\' is need for chardev \'{}\'", + path + ); + } sock_paths.push(path); } else { bail!("Only socket-type of chardev can be used for monitor"); diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 2eb435bef..ba2d483a1 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -26,7 +26,11 @@ const MIN_GUEST_CID: u64 = 3; pub enum ChardevType { Stdio, Pty, - Socket(String), + Socket { + path: String, + server: bool, + nowait: bool, + }, File(String), } @@ -54,15 +58,17 @@ impl ConfigCheck for ChardevConfig { .into()); } - if let ChardevType::Socket(path) | ChardevType::File(path) = &self.backend { - if path.len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( - "socket path".to_string(), - MAX_PATH_LENGTH, - ) - .into()); - } + let len = match &self.backend { + ChardevType::Socket { path, .. } => path.len(), + ChardevType::File(path) => path.len(), + _ => 0, + }; + if len > MAX_PATH_LENGTH { + return Err( + ErrorKind::StringLengthTooLong("socket path".to_string(), MAX_PATH_LENGTH).into(), + ); } + Ok(()) } } @@ -92,15 +98,11 @@ fn check_chardev_args(cmd_parser: CmdParser) -> Result<()> { if server.ne("") { bail!("No parameter needed for server"); } - } else { - bail!("Argument \'server\' is needed for socket-type chardev."); } if let Some(nowait) = nowait { if nowait.ne("") { bail!("No parameter needed for nowait"); } - } else { - bail!("Argument \'nowait\' is needed for socket-type chardev."); } } _ => (), @@ -117,6 +119,22 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { }; let backend = cmd_parser.get_value::("")?; let path = cmd_parser.get_value::("path")?; + let server = if let Some(server) = cmd_parser.get_value::("server")? { + if server.ne("") { + bail!("No parameter needed for server"); + } + true + } else { + false + }; + let nowait = if let Some(nowait) = cmd_parser.get_value::("nowait")? { + if nowait.ne("") { + bail!("No parameter needed for nowait"); + } + true + } else { + false + }; check_chardev_args(cmd_parser)?; let chardev_type = if let Some(backend) = backend { match backend.as_str() { @@ -124,7 +142,11 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { "pty" => ChardevType::Pty, "socket" => { if let Some(path) = path { - ChardevType::Socket(path) + ChardevType::Socket { + path, + server, + nowait, + } } else { return Err(ErrorKind::FieldIsMissing("path", "socket-type chardev").into()); } @@ -386,7 +408,11 @@ mod tests { assert_eq!(console_cfg.id, "console1"); assert_eq!( console_cfg.chardev.backend, - ChardevType::Socket("/path/to/socket".to_string()) + ChardevType::Socket { + path: "/path/to/socket".to_string(), + server: true, + nowait: true, + } ); let mut vm_config = VmConfig::default(); @@ -436,7 +462,11 @@ mod tests { assert_eq!(bdf.addr, (1, 2)); assert_eq!( console_cfg.chardev.backend, - ChardevType::Socket("/path/to/socket".to_string()) + ChardevType::Socket { + path: "/path/to/socket".to_string(), + server: true, + nowait: true, + } ); let mut vm_config = VmConfig::default(); diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 1192d28f9..f758a84e9 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -233,7 +233,7 @@ impl ConsoleHandler { )); } } - ChardevType::Socket(_) => { + ChardevType::Socket { .. } => { if let Some(stream_fd) = locked_chardev.stream_fd { notifiers.push(EventNotifier::new( NotifierOperation::Delete, -- Gitee From 697c4ee07104f32a788ecdd244a2eef4a9b91766 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 29 Mar 2022 20:21:40 +0800 Subject: [PATCH 0002/2187] vhost-user-net: support parsing command line parameter Support parsing command line parameter parsing of vhost-user net. Signed-off-by: Yan Wang --- machine/src/micro_vm/mod.rs | 1 + machine/src/standard_vm/mod.rs | 1 + machine_manager/src/config/network.rs | 53 ++++++++++++++++++++++++--- virtio/src/vhost/kernel/net.rs | 2 + 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index ad5850afb..e416ec279 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1144,6 +1144,7 @@ impl DeviceInterface for LightMachine { iothread: None, queues: 2, mq: false, + socket_path: None, }; if let Some(fds) = args.fds { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 05914a02e..c1e00e5b9 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -600,6 +600,7 @@ impl StdMachine { iothread: args.iothread.clone(), queues: conf.queues, mq: conf.queues > 2, + socket_path: None, }; dev.check()?; dev diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 70e29aa9a..505a483bb 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -17,7 +17,8 @@ use super::{ pci_args_check, }; use crate::config::{ - CmdParser, ConfigCheck, ExBool, VmConfig, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + ChardevType, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, + MAX_VIRTIO_QUEUE, }; use crate::qmp::{qmp_schema, QmpChannel}; @@ -31,6 +32,7 @@ pub struct NetDevcfg { pub vhost_fds: Option>, pub ifname: String, pub queues: u16, + pub chardev: Option, } impl Default for NetDevcfg { @@ -42,6 +44,7 @@ impl Default for NetDevcfg { vhost_fds: None, ifname: "".to_string(), queues: 2, + chardev: None, } } } @@ -82,6 +85,7 @@ pub struct NetworkInterfaceConfig { pub iothread: Option, pub queues: u16, pub mq: bool, + pub socket_path: Option, } impl NetworkInterfaceConfig { @@ -102,6 +106,7 @@ impl Default for NetworkInterfaceConfig { iothread: None, queues: 2, mq: false, + socket_path: None, } } } @@ -125,7 +130,7 @@ impl ConfigCheck for NetworkInterfaceConfig { } if let Some(vhost_type) = self.vhost_type.as_ref() { - if vhost_type != "vhost-kernel" { + if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" { return Err(ErrorKind::UnknownVhostType.into()); } } @@ -146,6 +151,13 @@ impl ConfigCheck for NetworkInterfaceConfig { .into()); } + if self.socket_path.is_some() && self.socket_path.as_ref().unwrap().len() > MAX_PATH_LENGTH + { + return Err( + ErrorKind::StringLengthTooLong("socket path".to_string(), MAX_PATH_LENGTH).into(), + ); + } + Ok(()) } } @@ -169,7 +181,7 @@ pub fn parse_netdev(cmd_parser: CmdParser) -> Result { } else { "".to_string() }; - if netdev_type.ne("tap") { + if netdev_type.ne("tap") && netdev_type.ne("vhost-user") { bail!("Unsupported netdev type: {:?}", &netdev_type); } if let Some(net_id) = cmd_parser.get_value::("id")? { @@ -203,6 +215,11 @@ pub fn parse_netdev(cmd_parser: CmdParser) -> Result { if vhost.into() { net.vhost_type = Some(String::from("vhost-kernel")); } + } else if netdev_type.eq("vhost-user") { + net.vhost_type = Some(String::from("vhost-user")); + } + if let Some(chardev) = cmd_parser.get_value::("chardev")? { + net.chardev = Some(chardev); } if let Some(vhost_fd) = parse_fds(&cmd_parser, "vhostfd")? { net.vhost_fds = Some(vhost_fd); @@ -219,7 +236,7 @@ pub fn parse_netdev(cmd_parser: CmdParser) -> Result { if net.vhost_fds.is_some() && net.vhost_type.is_none() { bail!("Argument \'vhostfd\' is not needed for virtio-net device"); } - if net.tap_fds.is_none() && net.ifname.eq("") { + if net.tap_fds.is_none() && net.ifname.eq("") && netdev_type.ne("vhost-user") { bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device"); } @@ -268,6 +285,30 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result { + if *server || *nowait { + bail!( + "Argument \'server\' or \'nowait\' is not need for chardev \'{}\'", + path + ); + } + netdevinterfacecfg.socket_path = Some(path.clone()); + } + _ => { + bail!("Chardev {:?} backend should be socket type.", &chardev); + } + } + } else { + bail!("Chardev: {:?} not found for character device", &chardev); + } + } } else { bail!("Netdev: {:?} not found for net device", &netdev); } @@ -284,6 +325,7 @@ pub fn get_netdev_config(args: Box) -> Result Date: Tue, 29 Mar 2022 16:44:31 +0800 Subject: [PATCH 0003/2187] vhost-user-net: support vhost-user net framework Support vhost-user net framework. Signed-off-by: Yan Wang --- machine/src/lib.rs | 17 +++-- virtio/src/lib.rs | 1 + virtio/src/vhost/mod.rs | 2 +- virtio/src/vhost/user/mod.rs | 3 + virtio/src/vhost/user/net.rs | 140 +++++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 virtio/src/vhost/user/net.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e759bd839..977440fbe 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -107,7 +107,7 @@ pub use micro_vm::LightMachine; use pci::{PciBus, PciDevOps, PciHost, RootPort}; pub use standard_vm::StdMachine; use virtio::{ - BlockState, RngState, VhostKern, VirtioConsoleState, VirtioDevice, VirtioMmioState, + BlockState, RngState, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioState, VirtioNetState, }; @@ -504,10 +504,17 @@ pub trait MachineOps { let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_net(vm_config, cfg_args)?; let device: Arc> = if device_cfg.vhost_type.is_some() { - Arc::new(Mutex::new(VhostKern::Net::new( - &device_cfg, - self.get_sys_mem(), - ))) + if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { + Arc::new(Mutex::new(VhostKern::Net::new( + &device_cfg, + self.get_sys_mem(), + ))) + } else { + Arc::new(Mutex::new(VhostUser::Net::new( + &device_cfg, + self.get_sys_mem(), + ))) + } } else { let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone()))); MigrationManager::register_device_instance_mutex_with_id( diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 909099a0c..d0cd6ab69 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -124,6 +124,7 @@ pub use net::*; pub use queue::*; pub use rng::{Rng, RngState}; pub use vhost::kernel as VhostKern; +pub use vhost::user as VhostUser; pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; pub use virtio_pci::VirtioPciDevice; diff --git a/virtio/src/vhost/mod.rs b/virtio/src/vhost/mod.rs index 412b3ca3a..08ef95caf 100644 --- a/virtio/src/vhost/mod.rs +++ b/virtio/src/vhost/mod.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod kernel; -mod user; +pub mod user; use std::sync::{Arc, Mutex}; diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 17d8dfffd..6a96c0468 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -12,4 +12,7 @@ mod client; mod message; +mod net; mod sock; + +pub use net::Net; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs new file mode 100644 index 000000000..b6fad08fd --- /dev/null +++ b/virtio/src/vhost/user/net.rs @@ -0,0 +1,140 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cmp; +use std::io::Write; +use std::sync::{Arc, Mutex}; + +use address_space::AddressSpace; +use machine_manager::config::NetworkInterfaceConfig; +use util::byte_code::ByteCode; +use util::num_ops::{read_u32, write_u32}; +use vmm_sys_util::eventfd::EventFd; + +use super::super::super::errors::{ErrorKind, Result}; +use super::super::super::{ + net::VirtioNetConfig, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_TYPE_NET, +}; + +/// Number of virtqueues. +const QUEUE_NUM_NET: usize = 2; +/// Size of each virtqueue. +const QUEUE_SIZE_NET: u16 = 256; + +/// Network device structure. +pub struct Net { + #[allow(dead_code)] + /// Configuration of the vhost user network device. + net_cfg: NetworkInterfaceConfig, + /// Bit mask of features supported by the backend. + device_features: u64, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, + /// Virtio net configurations. + device_config: VirtioNetConfig, + #[allow(dead_code)] + /// System address space. + mem_space: Arc, +} + +impl Net { + pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { + Net { + net_cfg: cfg.clone(), + device_features: 0_u64, + driver_features: 0_u64, + device_config: VirtioNetConfig::default(), + mem_space: mem_space.clone(), + } + } +} + +impl VirtioDevice for Net { + /// Realize vhost user network device. + fn realize(&mut self) -> Result<()> { + Ok(()) + } + + /// Get the virtio device type, refer to Virtio Spec. + fn device_type(&self) -> u32 { + VIRTIO_TYPE_NET + } + + /// Get the count of virtio device queues. + fn queue_num(&self) -> usize { + QUEUE_NUM_NET + } + + /// Get the queue size of virtio device. + fn queue_size(&self) -> u16 { + QUEUE_SIZE_NET + } + + /// Get device features from host. + fn get_device_features(&self, features_select: u32) -> u32 { + read_u32(self.device_features, features_select) + } + + /// Set driver features by guest. + fn set_driver_features(&mut self, page: u32, value: u32) { + let mut features = write_u32(value, page); + let unsupported_features = features & !self.device_features; + if unsupported_features != 0 { + warn!( + "Received acknowledge request with unsupported feature for vhost net: 0x{:x}", + features + ); + features &= !unsupported_features; + } + self.driver_features |= features; + } + + /// Read data of config from guest. + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + let config_slice = self.device_config.as_bytes(); + let config_size = config_slice.len() as u64; + if offset >= config_size { + return Err(ErrorKind::DevConfigOverflow(offset, config_size).into()); + } + if let Some(end) = offset.checked_add(data.len() as u64) { + data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; + } + + Ok(()) + } + + /// Write data to config from guest. + fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + let data_len = data.len(); + let config_slice = self.device_config.as_mut_bytes(); + let config_len = config_slice.len(); + if offset as usize + data_len > config_len { + return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + } + + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + + Ok(()) + } + + /// Activate the virtio device, this function is called by vcpu thread when frontend + /// virtio driver is ready and write `DRIVER_OK` to backend. + fn activate( + &mut self, + _mem_space: Arc, + _interrupt_cb: Arc, + _queues: &[Arc>], + _queue_evts: Vec, + ) -> Result<()> { + Ok(()) + } +} -- Gitee From bac640d978f87edca1bbe80b15b373263440bb34 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 29 Mar 2022 17:17:02 +0800 Subject: [PATCH 0004/2187] vhost-user-net: support irqfd Support register/unregister irqfd for vhost-user net. Signed-off-by: Yan Wang --- machine/src/lib.rs | 12 ++- machine/src/standard_vm/mod.rs | 6 +- virtio/src/lib.rs | 9 +++ virtio/src/vhost/user/net.rs | 13 +++ virtio/src/virtio_pci.rs | 139 +++++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 6 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 977440fbe..3633b5535 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -489,7 +489,7 @@ pub trait MachineOps { let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_blk(vm_config, cfg_args)?; let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); - self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func)?; + self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false)?; MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), device, @@ -503,6 +503,7 @@ pub trait MachineOps { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_net(vm_config, cfg_args)?; + let mut need_irqfd = false; let device: Arc> = if device_cfg.vhost_type.is_some() { if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { Arc::new(Mutex::new(VhostKern::Net::new( @@ -510,6 +511,7 @@ pub trait MachineOps { self.get_sys_mem(), ))) } else { + need_irqfd = true; Arc::new(Mutex::new(VhostUser::Net::new( &device_cfg, self.get_sys_mem(), @@ -524,7 +526,7 @@ pub trait MachineOps { ); device }; - self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func)?; + self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, need_irqfd)?; self.reset_bus(&device_cfg.id)?; Ok(()) } @@ -601,10 +603,11 @@ pub trait MachineOps { bdf: &PciBdf, device: Arc>, multi_func: bool, + need_irqfd: bool, ) -> Result<()> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; let sys_mem = self.get_sys_mem(); - let pcidev = VirtioPciDevice::new( + let mut pcidev = VirtioPciDevice::new( id.to_string(), devfn, sys_mem.clone(), @@ -612,6 +615,9 @@ pub trait MachineOps { parent_bus, multi_func, ); + if need_irqfd { + pcidev.enable_need_irqfd(); + } pcidev .realize() .chain_err(|| "Failed to add virtio pci device")?; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index c1e00e5b9..4d1e1ce18 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -566,7 +566,7 @@ impl StdMachine { let blk_id = blk.id.clone(); let blk = Arc::new(Mutex::new(Block::new(blk))); - self.add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction) + self.add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) .chain_err(|| "Failed to add virtio pci block device")?; MigrationManager::register_device_instance_mutex_with_id( @@ -610,12 +610,12 @@ impl StdMachine { if dev.vhost_type.is_some() { let net = Arc::new(Mutex::new(VhostKern::Net::new(&dev, self.get_sys_mem()))); - self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction) + self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, false) .chain_err(|| "Failed to add virtio net device")?; } else { let net_id = dev.id.clone(); let net = Arc::new(Mutex::new(virtio::Net::new(dev))); - self.add_virtio_pci_device(&args.id, pci_bdf, net.clone(), multifunction) + self.add_virtio_pci_device(&args.id, pci_bdf, net.clone(), multifunction, false) .chain_err(|| "Failed to add virtio net device")?; MigrationManager::register_device_instance_mutex_with_id( VirtioNetState::descriptor(), diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index d0cd6ab69..b66dd6e54 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -325,4 +325,13 @@ pub trait VirtioDevice: Send { fn update_config(&mut self, _dev_config: Option>) -> Result<()> { bail!("Unsupported to update configuration") } + + /// Set guest notifiers for notifying the guest. + /// + /// # Arguments + /// + /// * `_queue_evts` - The notifier events from host. + fn set_guest_notifiers(&mut self, _queue_evts: &[EventFd]) -> Result<()> { + Ok(()) + } } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index b6fad08fd..6a26761a4 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -44,6 +44,8 @@ pub struct Net { #[allow(dead_code)] /// System address space. mem_space: Arc, + /// The notifier events from host. + call_events: Vec, } impl Net { @@ -54,6 +56,7 @@ impl Net { driver_features: 0_u64, device_config: VirtioNetConfig::default(), mem_space: mem_space.clone(), + call_events: Vec::::new(), } } } @@ -137,4 +140,14 @@ impl VirtioDevice for Net { ) -> Result<()> { Ok(()) } + + /// Set guest notifiers for notifying the guest. + fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { + for fd in queue_evts.iter() { + let cloned_evt_fd = fd.try_clone().unwrap(); + self.call_events.push(cloned_evt_fd); + } + + Ok(()) + } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 9eae77791..f53a648b0 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -17,6 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; use error_chain::ChainedError; +use hypervisor::kvm::{MsiVector, KVM_FDS}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use pci::config::{ RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, @@ -463,6 +464,11 @@ pub struct VirtioPciState { queue_num: usize, } +struct GsiMsiRoute { + irq_fd: Option, + gsi: i32, +} + /// Virtio-PCI device structure #[derive(Clone)] pub struct VirtioPciDevice { @@ -492,6 +498,10 @@ pub struct VirtioPciDevice { queues: Arc>>>>, /// Multi-Function flag. multi_func: bool, + /// If the device need to register irqfd to kvm. + need_irqfd: bool, + /// Maintains a list of GSI with irqfds that are registered to kvm. + gsi_msi_routes: Arc>>, } impl VirtioPciDevice { @@ -522,9 +532,15 @@ impl VirtioPciDevice { interrupt_cb: None, queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))), multi_func, + need_irqfd: false, + gsi_msi_routes: Arc::new(Mutex::new(Vec::new())), } } + pub fn enable_need_irqfd(&mut self) { + self.need_irqfd = true; + } + fn assign_interrupt_cb(&mut self) { let cloned_common_cfg = self.common_config.clone(); let cloned_msix = self.config.msix.clone(); @@ -632,6 +648,7 @@ impl VirtioPciDevice { let cloned_pci_device = self.clone(); let cloned_mem_space = self.sys_mem.clone(); + let cloned_gsi_routes = self.gsi_msi_routes.clone(); let common_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { let value = match data.len() { 1 => data[0] as u32, @@ -703,8 +720,23 @@ impl VirtioPciDevice { locked_queues.push(arc_queue.clone()); } + let queue_num = cloned_pci_device.device.lock().unwrap().queue_num(); + let call_evts = NotifyEventFds::new(queue_num); let queue_evts = cloned_pci_device.notify_eventfds.clone().events; if let Some(cb) = cloned_pci_device.interrupt_cb.clone() { + if cloned_pci_device.need_irqfd { + if let Err(e) = cloned_pci_device + .device + .lock() + .unwrap() + .set_guest_notifiers(&call_evts.events) + { + error!( + "Failed to set guest notifiers, error is {}", + e.display_chain() + ); + } + } if let Err(e) = cloned_pci_device.device.lock().unwrap().activate( cloned_pci_device.sys_mem.clone(), cb, @@ -726,6 +758,17 @@ impl VirtioPciDevice { cloned_pci_device.devfn, &cloned_pci_device.dev_id, ); + + drop(locked_queues); + if cloned_pci_device.need_irqfd + && !virtio_pci_register_irqfd( + &cloned_pci_device, + &cloned_gsi_routes, + &call_evts.events, + ) + { + return false; + } } if old_dev_status != 0 @@ -736,6 +779,10 @@ impl VirtioPciDevice { .device_status == 0 { + if cloned_pci_device.need_irqfd { + virtio_pci_unregister_irqfd(cloned_gsi_routes.clone()); + } + let mut locked_queues = cloned_pci_device.queues.lock().unwrap(); locked_queues.clear(); if cloned_pci_device.device_activated.load(Ordering::Acquire) { @@ -1216,6 +1263,98 @@ impl MigrationHook for VirtioPciDevice { } } +fn virtio_pci_register_irqfd( + pci_device: &VirtioPciDevice, + gsi_routes: &Arc>>, + call_fds: &[EventFd], +) -> bool { + let locked_msix = if let Some(msix) = &pci_device.config.msix { + msix.lock().unwrap() + } else { + error!("Failed to get msix in virtio pci device configure"); + return false; + }; + + let locked_queues = pci_device.queues.lock().unwrap(); + let mut locked_gsi_routes = gsi_routes.lock().unwrap(); + for (queue_index, queue_mutex) in locked_queues.iter().enumerate() { + let vector = queue_mutex.lock().unwrap().vring.get_queue_config().vector; + let entry = locked_msix.get_message(vector as u16); + let msix_vector = MsiVector { + msg_addr_lo: entry.address_lo, + msg_addr_hi: entry.address_hi, + msg_data: entry.data, + masked: false, + #[cfg(target_arch = "aarch64")] + dev_id: pci_device.dev_id.load(Ordering::Acquire) as u32, + }; + + let gsi = match KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .allocate_gsi() + { + Ok(g) => g as i32, + Err(e) => { + error!("Failed to allocate gsi, error is {}", e); + return false; + } + }; + + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .add_msi_route(gsi as u32, msix_vector) + .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {}", e)); + + KVM_FDS + .load() + .commit_irq_routing() + .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {}", e)); + + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .register_irqfd(&call_fds[queue_index], gsi as u32) + .unwrap_or_else(|e| error!("Failed to register irq, error is {}", e)); + + let gsi_route = GsiMsiRoute { + irq_fd: Some(call_fds[queue_index].try_clone().unwrap()), + gsi, + }; + locked_gsi_routes.push(gsi_route); + } + + true +} + +fn virtio_pci_unregister_irqfd(gsi_routes: Arc>>) { + let mut locked_gsi_routes = gsi_routes.lock().unwrap(); + for route in locked_gsi_routes.iter() { + if let Some(fd) = &route.irq_fd.as_ref() { + KVM_FDS + .load() + .unregister_irqfd(fd, route.gsi as u32) + .unwrap_or_else(|e| error!("Failed to unregister irq, error is {}", e)); + + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .release_gsi(route.gsi as u32) + .unwrap_or_else(|e| error!("Failed to release gsi, error is {}", e)); + } + } + locked_gsi_routes.clear(); +} + #[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; -- Gitee From e6a3bdfb9dcae6c9619f10dff94a92e026d9e1ba Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 29 Mar 2022 20:23:39 +0800 Subject: [PATCH 0005/2187] vhost-user-net: implement the realize() and activate() function Implement the realize() and activate() function. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 2 + virtio/src/vhost/user/mod.rs | 1 + virtio/src/vhost/user/net.rs | 130 +++++++++++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 6 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b66dd6e54..68dd3dde6 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -183,6 +183,8 @@ pub const VIRTIO_NET_F_GUEST_UFO: u32 = 10; pub const VIRTIO_NET_F_HOST_TSO4: u32 = 11; /// Device can receive UFO. pub const VIRTIO_NET_F_HOST_UFO: u32 = 14; +/// Device can merge receive buffers. +pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15; /// Control channel is available. pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17; /// Device supports multi queue with automatic receive steering. diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 6a96c0468..fc6b8c7fa 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -15,4 +15,5 @@ mod message; mod net; mod sock; +use client::VhostUserClient; pub use net::Net; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 6a26761a4..e73e6b3c2 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -20,10 +20,16 @@ use util::byte_code::ByteCode; use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::eventfd::EventFd; -use super::super::super::errors::{ErrorKind, Result}; +use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::{ - net::VirtioNetConfig, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_TYPE_NET, + net::{build_device_config_space, VirtioNetConfig}, + Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, + VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_TYPE_NET, }; +use super::super::VhostOps; +use super::VhostUserClient; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; @@ -32,7 +38,6 @@ const QUEUE_SIZE_NET: u16 = 256; /// Network device structure. pub struct Net { - #[allow(dead_code)] /// Configuration of the vhost user network device. net_cfg: NetworkInterfaceConfig, /// Bit mask of features supported by the backend. @@ -41,9 +46,10 @@ pub struct Net { driver_features: u64, /// Virtio net configurations. device_config: VirtioNetConfig, - #[allow(dead_code)] /// System address space. mem_space: Arc, + /// Vhost user client + client: Option, /// The notifier events from host. call_events: Vec, } @@ -56,6 +62,7 @@ impl Net { driver_features: 0_u64, device_config: VirtioNetConfig::default(), mem_space: mem_space.clone(), + client: None, call_events: Vec::::new(), } } @@ -64,6 +71,40 @@ impl Net { impl VirtioDevice for Net { /// Realize vhost user network device. fn realize(&mut self) -> Result<()> { + let socket_path = self + .net_cfg + .socket_path + .as_ref() + .map(|path| path.to_string()) + .chain_err(|| "vhost-user: socket path is not found")?; + let client = VhostUserClient::new(&self.mem_space, &socket_path, self.queue_num() as u64) + .chain_err(|| { + "Failed to create the client which communicates with the server for vhost-user net" + })?; + client + .add_event_notifier() + .chain_err(|| "Failed to add event nitifier for vhost-user net")?; + + self.device_features = client + .get_features() + .chain_err(|| "Failed to get features for vhost-user net")?; + + let features = 1 << VIRTIO_F_VERSION_1 + | 1 << VIRTIO_NET_F_GUEST_CSUM + | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_UFO + | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_UFO + | 1 << VIRTIO_NET_F_MRG_RXBUF + | 1 << VIRTIO_F_RING_EVENT_IDX; + self.device_features &= features; + + self.client = Some(client); + + if let Some(mac) = &self.net_cfg.mac { + self.device_features |= build_device_config_space(&mut self.device_config, mac); + } + Ok(()) } @@ -135,9 +176,86 @@ impl VirtioDevice for Net { &mut self, _mem_space: Arc, _interrupt_cb: Arc, - _queues: &[Arc>], - _queue_evts: Vec, + queues: &[Arc>], + queue_evts: Vec, ) -> Result<()> { + let client = match &self.client { + None => return Err("Failed to get client for vhost-user net".into()), + Some(client_) => client_, + }; + + client + .set_owner() + .chain_err(|| "Failed to set owner for vhost-user net")?; + + let features = self.driver_features & !(1 << VIRTIO_NET_F_MAC); + client + .set_features(features) + .chain_err(|| "Failed to set features for vhost-user net")?; + + client + .set_mem_table() + .chain_err(|| "Failed to set mem table for vhost-user net")?; + + for (queue_index, queue_mutex) in queues.iter().enumerate() { + let queue = queue_mutex.lock().unwrap(); + let actual_size = queue.vring.actual_size(); + let queue_config = queue.vring.get_queue_config(); + + client.set_vring_enable(queue_index, false).chain_err(|| { + format!( + "Failed to set vring enable for vhost-user net, index: {}, false", + queue_index, + ) + })?; + client + .set_vring_num(queue_index, actual_size) + .chain_err(|| { + format!( + "Failed to set vring num for vhost-user net, index: {}, size: {}", + queue_index, actual_size, + ) + })?; + client + .set_vring_addr(&queue_config, queue_index, 0) + .chain_err(|| { + format!( + "Failed to set vring addr for vhost-user net, index: {}", + queue_index, + ) + })?; + client.set_vring_base(queue_index, 0).chain_err(|| { + format!( + "Failed to set vring base for vhost-user net, index: {}", + queue_index, + ) + })?; + client + .set_vring_kick(queue_index, &queue_evts[queue_index]) + .chain_err(|| { + format!( + "Failed to set vring kick for vhost-user net, index: {}", + queue_index, + ) + })?; + + client + .set_vring_call(queue_index, &self.call_events[queue_index]) + .chain_err(|| { + format!( + "Failed to set vring call for vhost-user net, index: {}", + queue_index, + ) + })?; + + client.set_vring_enable(queue_index, true).chain_err(|| { + format!( + "Failed to set vring enable for vhost-user net, index: {}", + queue_index, + ) + })?; + } + Ok(()) } -- Gitee From 379c984ed487b1401293ba2b9107bf3aa154c9e5 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 26 Feb 2022 09:39:58 +0800 Subject: [PATCH 0006/2187] vhost-user: delete old client and realize new client Delete all releated event from epoll loop for vhost-user client. Then realize new client in `reset` function. Signed-off-by: Yan Wang --- virtio/src/vhost/user/client.rs | 51 ++++++++++++++++++++++++++++++++- virtio/src/vhost/user/net.rs | 22 ++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 215fa3754..ea7a6223f 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -18,7 +18,9 @@ use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; use machine_manager::event_loop::EventLoop; -use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::super::super::{ @@ -37,6 +39,8 @@ struct ClientInternal { sock: VhostUserSock, // Maximum number of queues which is supported. max_queue_num: u64, + // EventFd for client reset. + delete_evt: EventFd, } #[allow(dead_code)] @@ -45,6 +49,7 @@ impl ClientInternal { ClientInternal { sock, max_queue_num, + delete_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } } @@ -69,6 +74,25 @@ impl ClientInternal { Ok(body) } + + fn delete_evt_handler(&mut self) -> Vec { + vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.sock.domain.get_stream_raw_fd(), + None, + EventSet::HANG_UP, + vec![], + ), + EventNotifier::new( + NotifierOperation::Delete, + self.delete_evt.as_raw_fd(), + None, + EventSet::IN, + vec![], + ), + ] + } } impl EventNotifierHelper for ClientInternal { @@ -98,6 +122,21 @@ impl EventNotifierHelper for ClientInternal { EventSet::HANG_UP, handlers, )); + + // Register event notifier for delete_evt. + let cloned_client = client_handler.clone(); + let handler: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + Some(cloned_client.lock().unwrap().delete_evt_handler()) + }); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + client_handler.lock().unwrap().delete_evt.as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + notifiers } } @@ -260,6 +299,16 @@ impl VhostUserClient { Ok(()) } + + pub fn delete_event(&self) -> Result<()> { + self.client + .lock() + .unwrap() + .delete_evt + .write(1) + .chain_err(|| ErrorKind::EventFdWrite)?; + Ok(()) + } } impl VhostOps for VhostUserClient { diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index e73e6b3c2..2e28dd1c6 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -268,4 +268,26 @@ impl VirtioDevice for Net { Ok(()) } + + fn deactivate(&mut self) -> Result<()> { + self.call_events.clear(); + Ok(()) + } + + fn reset(&mut self) -> Result<()> { + self.device_features = 0_u64; + self.driver_features = 0_u64; + self.device_config = VirtioNetConfig::default(); + + let client = match &self.client { + None => return Err("Failed to get client when reseting vhost-user net".into()), + Some(client_) => client_, + }; + client + .delete_event() + .chain_err(|| "Failed to delete vhost-user net event")?; + self.client = None; + + self.realize() + } } -- Gitee From 813c619e992ac1286698bdb14532a00e978303e9 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 21 Mar 2022 17:05:03 +0800 Subject: [PATCH 0007/2187] standard_vm: Add "socket" and "connect" syscall in whitelist Vhost-user net will use "socket" and "connect" syscall when rebooting vm. So, add them to whitelist. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 4 ++-- docs/design.ch.md | 2 +- docs/design.md | 2 +- machine/src/standard_vm/aarch64/syscall.rs | 6 ++++-- machine/src/standard_vm/x86_64/syscall.rs | 6 ++++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 34f192fd8..ad2cfa39b 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -565,14 +565,14 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 46 | 46 | -| q35 | 49 | 51 | +| q35 | 51 | 53 | * aarch64 | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 44 | 45 | -| virt | 48 | 47 | +| virt | 50 | 49 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/docs/design.ch.md b/docs/design.ch.md index 8dd774bf5..716f3bd7f 100644 --- a/docs/design.ch.md +++ b/docs/design.ch.md @@ -25,7 +25,7 @@ StratoVirt的核心架构如下图所示,从上到下分为三层: - OCI兼容性:StratoVirt与isula和kata容器配合使用,可以完美融入Kubernetes生态系统; - 多平台支持:全面支持Intel和ARM平台; - 可扩展性:StratoVirt保留接口和设计,用于导入更多特性,甚至扩展到标准虚拟化支持; -- 安全性:运行时系统调用数小于47; +- 安全性:运行时系统调用数小于54; ## 实现 diff --git a/docs/design.md b/docs/design.md index e66249977..b707c0f88 100644 --- a/docs/design.md +++ b/docs/design.md @@ -42,7 +42,7 @@ integrated in Kubernetes ecosystem perfectly; - Multi-platform support: Fully support for Intel and Arm platform; - Expansibility: StratoVirt reserves interface and design for importing more features, even expand to standard virtualization support; -- Security: less than 52 syscalls while running; +- Security: less than 54 syscalls while running; ## Implementation diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index f44c5f765..66fa0ae89 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 45 syscalls -/// * aarch64-unknown-musl: 44 syscalls +/// * aarch64-unknown-gnu: 47 syscalls +/// * aarch64-unknown-musl: 46 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -113,6 +113,8 @@ pub fn syscall_whitelist() -> Vec { madvise_rule(), BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), + BpfRule::new(libc::SYS_socket), + BpfRule::new(libc::SYS_connect), ] } diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 1fe711b69..223e171c5 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 46 syscalls -/// * x86_64-unknown-musl: 48 syscalls +/// * x86_64-unknown-gnu: 48 syscalls +/// * x86_64-unknown-musl: 50 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -120,6 +120,8 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_readlinkat), #[cfg(target_env = "musl")] BpfRule::new(libc::SYS_readlink), + BpfRule::new(libc::SYS_socket), + BpfRule::new(libc::SYS_connect), ] } -- Gitee From cdc1d7e05dc94d0b6c7f2f5a689bb1451ec563ac Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 31 Mar 2022 11:43:23 +0800 Subject: [PATCH 0008/2187] vhost-user-net: add doc for vhost-uset net Add doc for vhost-uset net. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index ad2cfa39b..1ec12ffaa 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -240,7 +240,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo Virtio-net is a virtual Ethernet card in VM. It can enable the network capability of VM. Six properties are supported for netdev. -* tap: the type of net device. NB: currently only tap is supported. +* tap/vhost-user: the type of net device. NB: currently only tap and vhost-user is supported. * id: unique netdev id. * ifname: name of tap device in host. * fd: the file descriptor of opened tap device. @@ -290,6 +290,16 @@ given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatica -device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC,mq=on] ``` +StratoVirt also supports vhost-user net to get a higher performance by ovs-dpdk. Currently, only +virtio pci net device support vhost-user net. + +```shell +# virtio pci net device +-chardev socket,id=chardevid,path=socket_path +-netdev vhost-user,id=netdevid,chardev=chardevid +-device virtio-net-pci,netdev=netdevid,id=netid,mac=12:34:56:78:9A:BC,bus=pci.0,addr=0x2.0x0 +``` + *How to set a tap device?* ```shell @@ -321,6 +331,31 @@ $ ifconfig qbr0 up; ifconfig tap1 up $ ifconfig qbr0 1.1.1.1 ``` +*How to create port by ovs-dpdk?* + +```shell +# Start open vSwitch daemons +$ ovs-ctl start +# Initialize database +$ ovs-vsctl init +# Dpdk init +$ ovs-vsctl set Open_vSwitch . other_config:dpdk-init=true +# Set up dpdk lcore mask +$ ovs-vsctl set Open_vSwitch . other_config:dpdk-lcore-mask=0xf +# Set up hugepages for dpdk-socket-mem (2G) +$ ovs-vsctl set Open_vSwitch . other_config:dpdk-socket-mem=1024 +# Set up PMD(Pull Mode Driver) cpu mask +$ ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0xf +# Add bridge +$ ovs-vsctl add-br ovs_br -- set bridge ovs_br datapath_type=netdev +# Add port +$ ovs-vsctl add-port ovs_br port1 -- set Interface port1 type=dpdkvhostuser +$ ovs-vsctl add-port ovs_br port2 -- set Interface port2 type=dpdkvhostuser +# Set num of rxq/txq +$ ovs-vsctl set Interface port1 options:n_rxq=1,n_txq=1 +$ ovs-vsctl set Interface port2 options:n_rxq=1,n_txq=1 +``` + ### 2.4 Virtio-console Virtio console is a general-purpose serial device for data transfer between the guest and host. @@ -540,7 +575,7 @@ Five properties can be set for chardev. # redirect methods -chardev stdio,id=chardev_id -chardev pty,id=chardev_id --chardev socket,id=chardev_id,path=socket_path,server,nowait +-chardev socket,id=chardev_id,path=socket_path[,server,nowait] -chardev file,id=chardev_id,path=file_path ``` -- Gitee From afaba66aaef842c49faf74aeea57a299230bb20d Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 2 Apr 2022 14:23:41 +0800 Subject: [PATCH 0009/2187] machine_manager: add test for command line of vhost-user net Add test for command line of vhost-user net. Signed-off-by: Yan Wang --- devices/src/legacy/chardev.rs | 2 +- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/chardev.rs | 23 +++++++++++++++++++++++ machine_manager/src/config/network.rs | 19 +++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index a80233c66..1f7b4a159 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -99,7 +99,7 @@ impl Chardev { } => { if !*server || !*nowait { bail!( - "Argument \'server\' and \'nowait\' is need for chardev \'{}\'", + "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", path ); } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index b6846e717..875d7fa88 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -493,7 +493,7 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< { if !server || !nowait { bail!( - "Argument \'server\' and \'nowait\' is need for chardev \'{}\'", + "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", path ); } diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index ba2d483a1..1567ed855 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -497,4 +497,27 @@ mod tests { assert_eq!(vsock_config.vhost_fd, Some(4)); assert!(vsock_config.check().is_ok()); } + + #[test] + fn test_chardev_config_cmdline_parser() { + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_chardev("socket,id=test_id,path=/path/to/socket") + .is_ok()); + assert!(vm_config + .add_chardev("socket,id=test_id,path=/path/to/socket") + .is_err()); + if let Some(char_dev) = vm_config.chardev.remove("test_id") { + assert_eq!( + char_dev.backend, + ChardevType::Socket { + path: "/path/to/socket".to_string(), + server: false, + nowait: false, + } + ); + } else { + assert!(false); + } + } } diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 505a483bb..820a5e56c 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -598,6 +598,25 @@ mod tests { let net_cfg = "virtio-net-pci,id=net1,netdev=eth1,bus=pcie.0,addr=0x1.0x2,mac=12:34:56:78:9A:BC,multifunction=on"; assert!(parse_net(&mut vm_config, net_cfg).is_ok()); + + // For vhost-user net + assert!(vm_config.add_netdev("vhost-user,id=netdevid").is_ok()); + let net_cfg = + "virtio-net-pci,id=netid,netdev=netdevid,bus=pcie.0,addr=0x2.0x0,mac=12:34:56:78:9A:BC"; + let net_cfg_res = parse_net(&mut vm_config, net_cfg); + assert!(net_cfg_res.is_ok()); + let network_configs = net_cfg_res.unwrap(); + assert_eq!(network_configs.id, "netid"); + assert_eq!(network_configs.vhost_type, Some("vhost-user".to_string())); + assert_eq!(network_configs.mac, Some("12:34:56:78:9A:BC".to_string())); + + assert!(vm_config + .add_netdev("vhost-user,id=netdevid2,chardev=chardevid2") + .is_ok()); + let net_cfg = + "virtio-net-pci,id=netid2,netdev=netdevid2,bus=pcie.0,addr=0x2.0x0,mac=12:34:56:78:9A:BC"; + let net_cfg_res = parse_net(&mut vm_config, net_cfg); + assert!(net_cfg_res.is_err()); } #[test] -- Gitee From 2fb18a6e6fae938010a09093a1699ac173c51dd0 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:21:29 +0800 Subject: [PATCH 0010/2187] machine/standard_vm: add aarch64 related ACPI tables in trait AcpiBuilder In order to use the ACPI standard on the aarch64 platform, the following additional ACPI tables need to be added. 1.GTDT(Generic Timer Description Table) 2.IORT(IO Remapping Table) 3.SPCR(Serial Port Console Redirection Table) Signed-off-by: Jiajie Li --- machine/src/standard_vm/mod.rs | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 4d1e1ce18..4c924b704 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -131,6 +131,24 @@ trait StdMachineOps: AcpiBuilder { .chain_err(|| "Failed to build ACPI MADT table")?; xsdt_entries.push(madt_addr); + #[cfg(target_arch = "aarch64")] + { + let gtdt_addr = self + .build_gtdt_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI GTDT table")?; + xsdt_entries.push(gtdt_addr); + + let iort_addr = self + .build_iort_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI IORT table")?; + xsdt_entries.push(iort_addr); + + let spcr_addr = self + .build_spcr_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI SPCR table")?; + xsdt_entries.push(spcr_addr); + } + let mcfg_addr = Self::build_mcfg_table(&acpi_tables, &mut loader) .chain_err(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); @@ -270,6 +288,60 @@ trait AcpiBuilder { bail!("Not implemented"); } + /// Build ACPI GTDT table, returns the offset of ACPI GTDT table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + #[cfg(target_arch = "aarch64")] + fn build_gtdt_table( + &self, + _acpi_data: &Arc>>, + _loader: &mut TableLoader, + ) -> Result + where + Self: Sized, + { + Ok(0) + } + + /// Build ACPI IORT table, returns the offset of ACPI IORT table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + #[cfg(target_arch = "aarch64")] + fn build_iort_table( + &self, + _acpi_data: &Arc>>, + _loader: &mut TableLoader, + ) -> Result + where + Self: Sized, + { + Ok(0) + } + + /// Build ACPI SPCR table, returns the offset of ACPI SPCR table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + #[cfg(target_arch = "aarch64")] + fn build_spcr_table( + &self, + _acpi_data: &Arc>>, + _loader: &mut TableLoader, + ) -> Result + where + Self: Sized, + { + Ok(0) + } + /// Build ACPI MCFG table, returns the offset of ACPI MCFG table in `acpi_data`. /// /// # Arguments -- Gitee From 9af6da7bce4f6bfd0c61c15fad3825c2bb3f59a4 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:37:06 +0800 Subject: [PATCH 0011/2187] standard_vm/aarch64: Add implement of aarch64 related ACPI tables Add implement of GTDT, IORT and SPCR. Note that in SPCR, the interrupt irq number of PL011 need add extra SGI and PPI base. Signed-off-by: Jiajie Li --- acpi/src/acpi_table.rs | 17 ++++ machine/src/standard_vm/aarch64/mod.rs | 134 ++++++++++++++++++++++++- machine/src/standard_vm/mod.rs | 30 ++++++ 3 files changed, 180 insertions(+), 1 deletion(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index f7de8e302..969f49480 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -16,6 +16,23 @@ use super::aml_compiler::AmlBuilder; /// Offset of checksum field in ACPI table. pub const TABLE_CHECKSUM_OFFSET: u32 = 9; +pub const INTERRUPT_PPIS_COUNT: u32 = 16; +pub const INTERRUPT_SGIS_COUNT: u32 = 16; +/// GTDT irq number for timer. +pub const ACPI_GTDT_ARCH_TIMER_VIRT_IRQ: u32 = 11; +pub const ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ: u32 = 13; +pub const ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ: u32 = 14; +pub const ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ: u32 = 10; +pub const ACPI_GTDT_INTERRUPT_MODE_LEVEL: u32 = 0; +pub const ACPI_GTDT_CAP_ALWAYS_ON: u32 = 4; +/// IORT node types, reference: ARM Document number: ARM DEN 0049B, October 2015. +pub const ACPI_IORT_NODE_ITS_GROUP: u8 = 0x00; +pub const ACPI_IORT_NODE_PCI_ROOT_COMPLEX: u8 = 0x02; +/// Interrupt controller structure types for MADT. +pub const ACPI_MADT_GENERIC_CPU_INTERFACE: u8 = 11; +pub const ACPI_MADT_GENERIC_DISTRIBUTOR: u8 = 12; +pub const ACPI_MADT_GENERIC_REDISTRIBUTOR: u8 = 14; +pub const ACPI_MADT_GENERIC_TRANSLATOR: u8 = 15; #[repr(C, packed)] #[derive(Default, Copy, Clone)] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 3fd2de30f..fad7c8e26 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -17,6 +17,16 @@ use std::fs::OpenOptions; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; +use acpi::{ + AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiTable, AmlBuilder, + AmlDevice, AmlInteger, AmlNameDecl, AmlScope, AmlScopeBuilder, AmlString, TableLoader, + ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, + ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, ACPI_GTDT_CAP_ALWAYS_ON, + ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, + ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, + ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, + INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, +}; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; @@ -506,7 +516,129 @@ impl MachineOps for StdMachine { } } -impl AcpiBuilder for StdMachine {} +impl AcpiBuilder for StdMachine { + fn build_gtdt_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + let mut gtdt = AcpiTable::new(*b"GTDT", 2, *b"STRATO", *b"VIRTGTDT", 1); + gtdt.set_table_len(96); + + // Secure EL1 interrupt + gtdt.set_field(48, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ + INTERRUPT_PPIS_COUNT); + // Secure EL1 flags + gtdt.set_field(52, ACPI_GTDT_INTERRUPT_MODE_LEVEL); + + // Non secure EL1 interrupt + gtdt.set_field(56, ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ + INTERRUPT_PPIS_COUNT); + // Non secure EL1 flags + gtdt.set_field(60, ACPI_GTDT_INTERRUPT_MODE_LEVEL | ACPI_GTDT_CAP_ALWAYS_ON); + + // Virtual timer interrupt + gtdt.set_field(64, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ + INTERRUPT_PPIS_COUNT); + // Virtual timer flags + gtdt.set_field(68, ACPI_GTDT_INTERRUPT_MODE_LEVEL); + + // Non secure EL2 interrupt + gtdt.set_field(72, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ + INTERRUPT_PPIS_COUNT); + // Non secure EL2 flags + gtdt.set_field(76, ACPI_GTDT_INTERRUPT_MODE_LEVEL); + + let gtdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, >dt) + .chain_err(|| "Fail to add GTDT table to loader")?; + Ok(gtdt_begin as u64) + } + + fn build_iort_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + let mut iort = AcpiTable::new(*b"IORT", 2, *b"STRATO", *b"VIRTIORT", 1); + iort.set_table_len(124); + + // Number of IORT nodes is 2: ITS group node and Root Complex Node. + iort.set_field(36, 2_u32); + // Node offset + iort.set_field(40, 48_u32); + + // ITS group node + iort.set_field(48, ACPI_IORT_NODE_ITS_GROUP); + // ITS node length + iort.set_field(49, 24_u16); + // ITS count + iort.set_field(64, 1_u32); + + // Root Complex Node + iort.set_field(72, ACPI_IORT_NODE_PCI_ROOT_COMPLEX); + // Length of Root Complex node + iort.set_field(73, 52_u16); + // Mapping counts of Root Complex Node + iort.set_field(80, 1_u32); + // Mapping offset of Root Complex Node + iort.set_field(84, 32_u32); + // Cache of coherent device + iort.set_field(88, 1_u32); + // Memory flags of coherent device + iort.set_field(95, 3_u8); + // Identity RID mapping + iort.set_field(108, 0xffff_u32); + // Without SMMU, id mapping is the first node in ITS group node + iort.set_field(116, 48_u32); + + let iort_begin = StdMachine::add_table_to_loader(acpi_data, loader, &iort) + .chain_err(|| "Fail to add IORT table to loader")?; + Ok(iort_begin as u64) + } + + fn build_spcr_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + let mut spcr = AcpiTable::new(*b"SPCR", 2, *b"STRATO", *b"VIRTSPCR", 1); + spcr.set_table_len(80); + + // Interface type: ARM PL011 UART + spcr.set_field(36, 3_u8); + // Bit width of AcpiGenericAddress + spcr.set_field(41, 8_u8); + // Access width of AcpiGenericAddress + spcr.set_field(43, 1_u8); + // Base addr of AcpiGenericAddress + spcr.set_field(44, MEM_LAYOUT[LayoutEntryType::Uart as usize].0); + // Interrupt Type: Arm GIC Interrupt + spcr.set_field(52, 1_u8 << 3); + // Irq number used by the UART + let mut uart_irq: u32 = 0; + for dev in self.sysbus.devices.iter() { + let mut locked_dev = dev.lock().unwrap(); + if locked_dev.get_type() == SysBusDevType::PL011 { + uart_irq = locked_dev.get_sys_resource().unwrap().irq as u32; + break; + } + } + spcr.set_field(54, uart_irq + INTERRUPT_SGIS_COUNT + INTERRUPT_PPIS_COUNT); + // Set baud rate: 3 = 9600 + spcr.set_field(58, 3_u8); + // Stop bit + spcr.set_field(60, 1_u8); + // Hardware flow control + spcr.set_field(61, 2_u8); + // PCI Device ID: it is not a PCI device + spcr.set_field(64, 0xffff_u16); + // PCI Vendor ID: it is not a PCI device + spcr.set_field(66, 0xffff_u16); + + let spcr_begin = StdMachine::add_table_to_loader(acpi_data, loader, &spcr) + .chain_err(|| "Fail to add SPCR table to loader")?; + Ok(spcr_begin as u64) + } +} impl MachineLifecycle for StdMachine { fn pause(&self) -> bool { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 4c924b704..075e6fe33 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -260,6 +260,36 @@ trait StdMachineOps: AcpiBuilder { /// Standard machine struct should at least implement `build_dsdt_table`, `build_madt_table` /// and `build_mcfg_table` function. trait AcpiBuilder { + /// Add ACPI table to the end of table loader, returns the offset of ACPI table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + /// `table` - ACPI table. + fn add_table_to_loader( + acpi_data: &Arc>>, + loader: &mut TableLoader, + table: &AcpiTable, + ) -> Result { + let mut locked_acpi_data = acpi_data.lock().unwrap(); + let table_begin = locked_acpi_data.len() as u32; + locked_acpi_data.extend(table.aml_bytes()); + let table_end = locked_acpi_data.len() as u32; + // Drop the lock of acpi_data to avoid dead-lock when adding entry to + // TableLoader, because TableLoader also needs to acquire this lock. + drop(locked_acpi_data); + + loader.add_cksum_entry( + ACPI_TABLE_FILE, + table_begin + TABLE_CHECKSUM_OFFSET, + table_begin, + table_end - table_begin, + )?; + + Ok(table_begin as u64) + } + /// Build ACPI DSDT table, returns the offset of ACPI DSDT table in `acpi_data`. /// /// # Arguments -- Gitee From 9d98c634888fcbd13c56b42d634deab6b20042e3 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:42:11 +0800 Subject: [PATCH 0012/2187] legacy/pl011: add implementation of AmlBuilder for PL011 Add aml description for pl011 device. Note that interrupt type is Edge, and the irq number need to add the SGI and PPI base. Signed-off-by: Jiajie Li --- devices/src/legacy/pl011.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 13343813f..34a5114ef 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlMemory32Fixed, AmlNameDecl, AmlReadAndWrite, AmlResTemplate, AmlResourceUsage, - AmlScopeBuilder, AmlString, + AmlScopeBuilder, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; @@ -416,7 +416,7 @@ impl MigrationHook for PL011 {} impl AmlBuilder for PL011 { fn aml_bytes(&self) -> Vec { let mut acpi_dev = AmlDevice::new("COM0"); - acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("ARMH0001".to_string()))); + acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("ARMH0011".to_string()))); acpi_dev.append_child(AmlNameDecl::new("_UID", AmlInteger(0))); let mut res = AmlResTemplate::new(); @@ -426,10 +426,10 @@ impl AmlBuilder for PL011 { self.res.region_size as u32, )); // SPI start at interrupt number 32 on aarch64 platform. - let irq_base = 32_u32; + let irq_base = INTERRUPT_PPIS_COUNT + INTERRUPT_SGIS_COUNT; res.append_child(AmlExtendedInterrupt::new( AmlResourceUsage::Consumer, - AmlEdgeLevel::Level, + AmlEdgeLevel::Edge, AmlActiveLevel::High, AmlIntShare::Exclusive, vec![self.res.irq as u32 + irq_base], -- Gitee From ac2d70606b23e1714820545d933f19937e3e4e96 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:46:45 +0800 Subject: [PATCH 0013/2187] legacy/fwcfg: add implementation of AmlBuilder for FwCfgMem Add aml description for FwCfgMem in aarch64 plantform. Signed-off-by: Jiajie Li --- devices/src/legacy/fwcfg.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 866bab297..df613e43d 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -12,12 +12,13 @@ use std::sync::{Arc, Mutex}; -use acpi::AmlBuilder; -#[cfg(target_arch = "x86_64")] use acpi::{ - AmlDevice, AmlInteger, AmlIoDecode, AmlIoResource, AmlNameDecl, AmlResTemplate, - AmlScopeBuilder, AmlString, + AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, AmlString, }; +#[cfg(target_arch = "x86_64")] +use acpi::{AmlIoDecode, AmlIoResource}; +#[cfg(target_arch = "aarch64")] +use acpi::{AmlMemory32Fixed, AmlReadAndWrite}; use address_space::{AddressSpace, GuestAddress}; #[cfg(target_arch = "x86_64")] use byteorder::LittleEndian; @@ -1198,7 +1199,20 @@ pub trait FwCfgOps { #[cfg(target_arch = "aarch64")] impl AmlBuilder for FwCfgMem { fn aml_bytes(&self) -> Vec { - Vec::new() + let mut acpi_dev = AmlDevice::new("FWCF"); + acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("QEMU0002".to_string()))); + acpi_dev.append_child(AmlNameDecl::new("_STA", AmlInteger(0xB))); + acpi_dev.append_child(AmlNameDecl::new("_CCA", AmlInteger(0x1))); + + let mut res = AmlResTemplate::new(); + res.append_child(AmlMemory32Fixed::new( + AmlReadAndWrite::ReadWrite, + self.res.region_base as u32, + self.res.region_size as u32, + )); + acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); + + acpi_dev.aml_bytes() } } -- Gitee From a4023f772875ccd0adb7eeab6bb3d4ca98624f43 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 09:53:47 +0800 Subject: [PATCH 0014/2187] standard_vm/aarch64: add acpi tables to mainboard To use acpi in aarch64 plantform, the DSDT and MADT tables need to change. Add GIC aml descrition in MADT and add the aml description of the devices on the mainboard to the DSDT. Signed-off-by: Jiajie Li --- machine/src/standard_vm/aarch64/mod.rs | 95 +++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index fad7c8e26..c2f0df2f8 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -431,11 +431,11 @@ impl MachineOps for StdMachine { .add_devices(vm_config) .chain_err(|| "Failed to add devices")?; - let boot_config = if !is_migrate { + let (boot_config, fwcfg) = if !is_migrate { let fwcfg = locked_vm.add_fwcfg_device()?; - Some(locked_vm.load_boot_source(Some(&fwcfg))?) + (Some(locked_vm.load_boot_source(Some(&fwcfg))?), Some(fwcfg)) } else { - None + (None, None) }; locked_vm.cpus.extend(::init_vcpu( @@ -462,6 +462,12 @@ impl MachineOps for StdMachine { .chain_err(|| ErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } + if let Some(fwcfg) = fwcfg { + locked_vm + .build_acpi_tables(&fwcfg) + .chain_err(|| "Failed to create ACPI tables")?; + } + locked_vm.register_power_event(&locked_vm.power_button)?; if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { @@ -638,6 +644,89 @@ impl AcpiBuilder for StdMachine { .chain_err(|| "Fail to add SPCR table to loader")?; Ok(spcr_begin as u64) } + + fn build_dsdt_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); + + // 1. CPU info. + let cpus_count = self.cpus.len() as u64; + let mut sb_scope = AmlScope::new("\\_SB"); + for cpu_id in 0..cpus_count { + let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); + dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0007".to_string()))); + dev.append_child(AmlNameDecl::new("_UID", AmlInteger(cpu_id))); + sb_scope.append_child(dev); + } + + // 2. Create pci host bridge node. + sb_scope.append_child(self.pci_host.lock().unwrap().clone()); + dsdt.append_child(sb_scope.aml_bytes().as_slice()); + + // 3. Info of devices attached to system bus. + dsdt.append_child(self.sysbus.aml_bytes().as_slice()); + + let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) + .chain_err(|| "Fail to add DSDT table to loader")?; + Ok(dsdt_begin as u64) + } + + fn build_madt_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); + madt.set_table_len(44); + + // 1. GIC Distributor. + let mut gic_dist = AcpiGicDistributor::default(); + gic_dist.type_id = ACPI_MADT_GENERIC_DISTRIBUTOR; + gic_dist.length = 24; + gic_dist.base_addr = MEM_LAYOUT[LayoutEntryType::GicDist as usize].0; + gic_dist.gic_version = 3; + madt.append_child(&gic_dist.aml_bytes()); + + // 2. GIC CPU. + let cpus_count = self.cpus.len() as u64; + for cpu_index in 0..cpus_count { + let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); + let mpidr_mask: u64 = 0x007f_ffff; + let mut gic_cpu = AcpiGicCpu::default(); + gic_cpu.type_id = ACPI_MADT_GENERIC_CPU_INTERFACE; + gic_cpu.length = 80; + gic_cpu.cpu_interface_num = cpu_index as u32; + gic_cpu.processor_uid = cpu_index as u32; + gic_cpu.flags = 5; + gic_cpu.mpidr = mpidr & mpidr_mask; + gic_cpu.vgic_interrupt = ARCH_GIC_MAINT_IRQ + INTERRUPT_PPIS_COUNT; + madt.append_child(&gic_cpu.aml_bytes()); + } + + // 3. GIC Redistributor. + let mut gic_redist = AcpiGicRedistributor::default(); + gic_redist.type_id = ACPI_MADT_GENERIC_REDISTRIBUTOR; + gic_redist.range_length = MEM_LAYOUT[LayoutEntryType::GicRedist as usize].1 as u32; + gic_redist.base_addr = MEM_LAYOUT[LayoutEntryType::GicRedist as usize].0; + gic_redist.length = 16; + madt.append_child(&gic_redist.aml_bytes()); + + // 4. GIC Its. + let mut gic_its = AcpiGicIts::default(); + gic_its.type_id = ACPI_MADT_GENERIC_TRANSLATOR; + gic_its.length = 20; + gic_its.base_addr = MEM_LAYOUT[LayoutEntryType::GicIts as usize].0; + madt.append_child(&gic_its.aml_bytes()); + + let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) + .chain_err(|| "Fail to add MADT table to loader")?; + Ok(madt_begin as u64) + } } impl MachineLifecycle for StdMachine { -- Gitee From 4b07441e53a4df3272cd176f5e782ec3e9315eb8 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 30 Sep 2021 10:00:28 +0800 Subject: [PATCH 0015/2187] machine/standard_vm: add psci descrition in FADT PSCI is Power State Coordination Interface on arm plantform, which is for KVM to handle vm shutdown and reboot. This needs compliant with hardware reduce bit in FADT flags. But on x86 plantfrom, the shutdown and reboot of vm rely on ACPI which don't need hardware reduce bit. Therefore, architecture macros are used to configure flags respectively. Signed-off-by: Jiajie Li --- machine/src/standard_vm/mod.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 075e6fe33..883fafae6 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -429,7 +429,7 @@ trait AcpiBuilder { where Self: Sized, { - let mut fadt = AcpiTable::new(*b"FACP", 6, *b"STRATO", *b"VIRTFSCP", 1); + let mut fadt = AcpiTable::new(*b"FACP", 6, *b"STRATO", *b"VIRTFACP", 1); fadt.set_table_len(208_usize); // PM1A_EVENT bit, offset is 56. @@ -441,8 +441,13 @@ trait AcpiBuilder { // PM_TMR_BLK bit, offset is 76. #[cfg(target_arch = "x86_64")] fadt.set_field(76, 0x608); - // FADT flag: disable HW_REDUCED_ACPI bit. - fadt.set_field(112, 1 << 10 | 1 << 8); + #[cfg(target_arch = "aarch64")] + { + // FADT flag: enable HW_REDUCED_ACPI bit on aarch64 plantform. + fadt.set_field(112, 1 << 20 | 1 << 10 | 1 << 8); + // ARM Boot Architecture Flags + fadt.set_field(129, 0x3_u16); + } // FADT minor revision fadt.set_field(131, 3); // X_PM_TMR_BLK bit, offset is 208. @@ -453,6 +458,8 @@ trait AcpiBuilder { #[cfg(target_arch = "x86_64")] { + // FADT flag: disable HW_REDUCED_ACPI bit on x86 plantform. + fadt.set_field(112, 1 << 10 | 1 << 8); // Reset Register bit, offset is 116. fadt.set_field(116, 0x01_u8); fadt.set_field(117, 0x08_u8); -- Gitee From 4efdc9ce7e981708ce95c3094e76601c1b22d240 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 10 Mar 2022 09:04:50 +0800 Subject: [PATCH 0016/2187] pci/host: add pio description to pci host aml On aarch64 plantform, the pio range description in pci host aml byte is necessary for vm which is booted from raw image. Add it to fix the bug of standard boot from raw image failure on aarch64 plantform. Signed-off-by: Jiajie Li --- machine/src/standard_vm/aarch64/mod.rs | 1 + pci/src/bus.rs | 2 ++ pci/src/host.rs | 29 +++++++++++++++++++++----- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c2f0df2f8..34d546922 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -153,6 +153,7 @@ impl StdMachine { &sys_mem, MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize], MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize], + MEM_LAYOUT[LayoutEntryType::PciePio as usize], ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 8fb9d1e37..e88e9c2de 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -299,6 +299,8 @@ mod tests { &sys_mem, (0xB000_0000, 0x1000_0000), (0xC000_0000, 0x3000_0000), + #[cfg(target_arch = "aarch64")] + (0xF000_0000, 0x1000_0000), ))) } diff --git a/pci/src/host.rs b/pci/src/host.rs index 1713c2e95..10d3a1e64 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -14,14 +14,13 @@ use std::sync::{Arc, Mutex}; use acpi::{ AmlAddressSpaceDecode, AmlBuilder, AmlByte, AmlCacheable, AmlDWord, AmlDWordDesc, AmlDevice, - AmlEisaId, AmlNameDecl, AmlPackage, AmlReadAndWrite, AmlResTemplate, AmlScopeBuilder, - AmlWordDesc, AmlZero, + AmlEisaId, AmlISARanges, AmlNameDecl, AmlPackage, AmlReadAndWrite, AmlResTemplate, + AmlScopeBuilder, AmlWordDesc, AmlZero, }; #[cfg(target_arch = "x86_64")] use acpi::{ - AmlAnd, AmlArg, AmlCreateDWordField, AmlElse, AmlEqual, AmlISARanges, AmlIf, AmlInteger, - AmlIoDecode, AmlIoResource, AmlLNot, AmlLocal, AmlMethod, AmlName, AmlOr, AmlReturn, AmlStore, - AmlToUuid, + AmlAnd, AmlArg, AmlCreateDWordField, AmlElse, AmlEqual, AmlIf, AmlInteger, AmlIoDecode, + AmlIoResource, AmlLNot, AmlLocal, AmlMethod, AmlName, AmlOr, AmlReturn, AmlStore, AmlToUuid, }; use address_space::{AddressSpace, GuestAddress, RegionOps}; use sysbus::{errors::Result as SysBusResult, SysBusDevOps}; @@ -57,6 +56,8 @@ pub struct PciHost { config_addr: u32, pcie_ecam_range: (u64, u64), pcie_mmio_range: (u64, u64), + #[cfg(target_arch = "aarch64")] + pcie_pio_range: (u64, u64), } impl PciHost { @@ -71,6 +72,7 @@ impl PciHost { sys_mem: &Arc, pcie_ecam_range: (u64, u64), pcie_mmio_range: (u64, u64), + #[cfg(target_arch = "aarch64")] pcie_pio_range: (u64, u64), ) -> Self { #[cfg(target_arch = "x86_64")] let io_region = sys_io.root().clone(); @@ -88,6 +90,8 @@ impl PciHost { config_addr: 0, pcie_ecam_range, pcie_mmio_range, + #[cfg(target_arch = "aarch64")] + pcie_pio_range, } } @@ -336,6 +340,19 @@ impl AmlBuilder for PciHost { 0xf300, )); } + #[cfg(target_arch = "aarch64")] + { + let pcie_pio = self.pcie_pio_range; + crs.append_child(AmlDWordDesc::new_io( + AmlAddressSpaceDecode::Positive, + AmlISARanges::EntireRange, + 0, + pcie_pio.0 as u32, + (pcie_pio.0 + pcie_pio.1) as u32 - 1, + 0, + pcie_pio.1 as u32, + )); + } crs.append_child(AmlDWordDesc::new_memory( AmlAddressSpaceDecode::Positive, AmlCacheable::NonCacheable, @@ -443,6 +460,8 @@ pub mod tests { &sys_mem, (0xB000_0000, 0x1000_0000), (0xC000_0000, 0x3000_0000), + #[cfg(target_arch = "aarch64")] + (0xF000_0000, 0x1000_0000), ))) } -- Gitee From 0b8764eb29ad144b223a0d40f05dca018d214d65 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Sat, 26 Mar 2022 16:27:38 +0800 Subject: [PATCH 0017/2187] machine/standard_vm: update ACPI table for usage of HighPcieEcam In order to increase the number of devices on the pci root bus, uses the HighPcieEcam address space, so it is necessary to modify the corresponding content in the ACPI table to ensure that the guest obtains the correct configuration information. Signed-off-by: Jiajie Li --- machine/src/standard_vm/aarch64/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 34d546922..48b773bf9 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -152,7 +152,7 @@ impl StdMachine { pci_host: Arc::new(Mutex::new(PciHost::new( &sys_mem, MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize], - MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize], + MEM_LAYOUT[LayoutEntryType::PcieMmio as usize], MEM_LAYOUT[LayoutEntryType::PciePio as usize], ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 883fafae6..1c9820457 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -383,10 +383,21 @@ trait AcpiBuilder { Self: Sized, { let mut mcfg = AcpiTable::new(*b"MCFG", 1, *b"STRATO", *b"VIRTMCFG", 1); - let ecam_addr: u64 = MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].0; // Bits 20~28 (totally 9 bits) in PCIE ECAM represents bus number. let bus_number_mask = (1 << 9) - 1; - let max_nr_bus = (MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].1 >> 20) & bus_number_mask; + let ecam_addr: u64; + let max_nr_bus: u64; + #[cfg(target_arch = "x86_64")] + { + ecam_addr = MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].0; + max_nr_bus = (MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].1 >> 20) & bus_number_mask; + } + #[cfg(target_arch = "aarch64")] + { + ecam_addr = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; + max_nr_bus = + (MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1 >> 20) & bus_number_mask; + } // Reserved mcfg.append_child(&[0_u8; 8]); -- Gitee From b156c638d0aaf11a92535212df20c39a2fdd692d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 7 Apr 2022 18:56:17 +0800 Subject: [PATCH 0018/2187] machine_manager: use write! in TempCleaner::clean When using info! and other macros to print the log, a lock will be held. During the processing, if the called function (such as display_chain) triggers the system signal, the handle_signal_sys function will be called, and the info! macro will also be called in the TempCleaner::clean function to print the log, may cause deadlock. Signed-off-by: zhouli57 --- machine_manager/src/temp_cleaner.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/temp_cleaner.rs b/machine_manager/src/temp_cleaner.rs index 4a4683267..bda0cfb8d 100644 --- a/machine_manager/src/temp_cleaner.rs +++ b/machine_manager/src/temp_cleaner.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::fs; +use std::io::Write; static mut GLOBAL_TEMP_CLEANER: Option = None; @@ -45,9 +46,20 @@ impl TempCleaner { if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { while let Some(path) = tmp.paths.pop() { if let Err(ref e) = fs::remove_file(&path) { - error!("Failed to delete console / socket file:{} :{}", &path, e); + write!( + &mut std::io::stderr(), + "Failed to delete console / socket file:{} :{} \r\n", + &path, + e + ) + .expect("Failed to write to stderr"); } else { - info!("Delete file: {} successfully.", &path); + write!( + &mut std::io::stdout(), + "Delete file: {} successfully.\r\n", + &path + ) + .expect("Failed to write to stdout"); } } } -- Gitee From 81a60dca2cf23248c9cc5a633c0e9dfe131af137 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 30 Mar 2022 09:28:24 +0800 Subject: [PATCH 0019/2187] syscall: add `TUNGETFEATURES` ioctl to syscall whitelist It will call `TUNGETFEATURES` ioctl in the process of hot plugging virtio net device. Add it to syscall whitelist. Signed-off-by: Xinle.Guo --- machine/src/micro_vm/syscall.rs | 3 ++- machine/src/standard_vm/aarch64/syscall.rs | 3 ++- machine/src/standard_vm/x86_64/syscall.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 81637a388..1e5c2bb19 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -12,7 +12,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; use virtio::VhostKern::*; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/futex.h @@ -144,6 +144,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_FEATURES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_MEM_TABLE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_NET_SET_BACKEND() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, TUNGETFEATURES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index f44c5f765..1a2d4495c 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -12,7 +12,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; use vfio::{ VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, @@ -144,6 +144,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_NET_SET_BACKEND() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_GET_FEATURES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_RESET_OWNER() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, TUNGETFEATURES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 1fe711b69..d0e7c4ced 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -12,7 +12,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; use vfio::{ VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, @@ -151,6 +151,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_NET_SET_BACKEND() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_GET_FEATURES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_RESET_OWNER() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, TUNGETFEATURES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) -- Gitee From a7a3defd6f560b0fc80517bca96be77264c024ce Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 30 Mar 2022 09:34:54 +0800 Subject: [PATCH 0020/2187] virtio/net: fix failed to hot unplug tap device in microvm The root cause is that when hot unplug net device, it will call `update_config` function. And it still need to send None status to channel if there is no tap deivce. Otherwise, receiver will never return. Signed-off-by: Xinle.Guo --- virtio/src/net.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 59fed99a7..cbd843453 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -974,14 +974,23 @@ impl VirtioDevice for Net { self.realize()?; if let Some(senders) = &self.senders { - if let Some(mut taps) = self.taps.take() { - for (index, sender) in senders.iter().enumerate() { - let tap = taps.remove(index); - sender - .send(Some(tap)) - .chain_err(|| ErrorKind::ChannelSend("tap fd".to_string()))?; + for (index, sender) in senders.iter().enumerate() { + match self.taps.take() { + Some(taps) => { + let tap = taps + .get(index) + .cloned() + .chain_err(|| format!("Failed to get index {} tap", index))?; + sender + .send(Some(tap)) + .chain_err(|| ErrorKind::ChannelSend("tap fd".to_string()))?; + } + None => sender + .send(None) + .chain_err(|| "Failed to send status of None to channel".to_string())?, } } + self.update_evt .write(1) .chain_err(|| ErrorKind::EventFdWrite)?; -- Gitee From ad467f0f236a20caace7f6d7d0bae392c7ce0945 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 7 Apr 2022 20:11:38 +0800 Subject: [PATCH 0021/2187] standard_vm: add SYS_getcwd syscall in whitelist When RUST_BACKTRACE=1 is set in the environment, the SYS_getcwd syscall need to be added. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 4 ++-- docs/design.ch.md | 2 +- docs/design.md | 2 +- machine/src/standard_vm/aarch64/syscall.rs | 5 +++-- machine/src/standard_vm/x86_64/syscall.rs | 5 +++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1ec12ffaa..7ae9aac49 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -600,14 +600,14 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 46 | 46 | -| q35 | 51 | 53 | +| q35 | 52 | 54 | * aarch64 | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 44 | 45 | -| virt | 50 | 49 | +| virt | 51 | 50 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/docs/design.ch.md b/docs/design.ch.md index 716f3bd7f..0c96eb48f 100644 --- a/docs/design.ch.md +++ b/docs/design.ch.md @@ -25,7 +25,7 @@ StratoVirt的核心架构如下图所示,从上到下分为三层: - OCI兼容性:StratoVirt与isula和kata容器配合使用,可以完美融入Kubernetes生态系统; - 多平台支持:全面支持Intel和ARM平台; - 可扩展性:StratoVirt保留接口和设计,用于导入更多特性,甚至扩展到标准虚拟化支持; -- 安全性:运行时系统调用数小于54; +- 安全性:运行时系统调用数小于55; ## 实现 diff --git a/docs/design.md b/docs/design.md index b707c0f88..cd297fd84 100644 --- a/docs/design.md +++ b/docs/design.md @@ -42,7 +42,7 @@ integrated in Kubernetes ecosystem perfectly; - Multi-platform support: Fully support for Intel and Arm platform; - Expansibility: StratoVirt reserves interface and design for importing more features, even expand to standard virtualization support; -- Security: less than 54 syscalls while running; +- Security: less than 55 syscalls while running; ## Implementation diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 66fa0ae89..496bdfa9f 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 47 syscalls -/// * aarch64-unknown-musl: 46 syscalls +/// * aarch64-unknown-gnu: 48 syscalls +/// * aarch64-unknown-musl: 47 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -115,6 +115,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_readlinkat), BpfRule::new(libc::SYS_socket), BpfRule::new(libc::SYS_connect), + BpfRule::new(libc::SYS_getcwd), ] } diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 223e171c5..875ca4649 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 48 syscalls -/// * x86_64-unknown-musl: 50 syscalls +/// * x86_64-unknown-gnu: 49 syscalls +/// * x86_64-unknown-musl: 51 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -122,6 +122,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_socket), BpfRule::new(libc::SYS_connect), + BpfRule::new(libc::SYS_getcwd), ] } -- Gitee From 9355bbcb8f181fdcb5a8ba39e4ed3ee7de8ce749 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 6 Jan 2022 17:32:26 +0800 Subject: [PATCH 0022/2187] multiqueue: add parseing block multiqueue cmdline rule It provides `num-queues=N` to enable multiple queue. As a result, the block I/O performance will be improvided. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 5 +++-- machine/src/micro_vm/mod.rs | 1 + machine_manager/src/config/drive.rs | 30 +++++++++++++++++++++++++---- machine_manager/src/machine.rs | 7 +++++++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1ec12ffaa..b8aa10032 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -216,6 +216,8 @@ Nine properties are supported for virtio block device. * if: drive type, for block drive, it should be `none`. If not set, default is `none` (optional) * format: the format of block image, default value `raw`. NB: currently only `raw` is supported. (optional) If not set, default is raw. +* num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, +the default block queue number is 1. For virtio-blk-pci, two more properties are required. * bus: name of bus which to attach. @@ -231,8 +233,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo -device virtio-blk-device,drive=drive_id,id=blkid[,iothread=iothread1,serial=serial_num] # virtio pci block device. -drive id=drive_id,file=path_on_host[,readonly=off,direct=off,throttling.iops-total=200] --device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num] - +-device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N] ``` ### 2.3 Virtio-net diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index e416ec279..fc8595501 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1113,6 +1113,7 @@ impl DeviceInterface for LightMachine { serial_num: None, iothread: None, iops: None, + queues: 1, }; match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 88d78f29f..0386dc4e4 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -20,7 +20,9 @@ use super::{ errors::{ErrorKind, Result}, pci_args_check, }; -use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH}; +use crate::config::{ + CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, +}; const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; @@ -36,6 +38,7 @@ pub struct BlkDevConfig { pub serial_num: Option, pub iothread: Option, pub iops: Option, + pub queues: u16, } impl Default for BlkDevConfig { @@ -48,6 +51,7 @@ impl Default for BlkDevConfig { serial_num: None, iothread: None, iops: None, + queues: 1, } } } @@ -182,6 +186,17 @@ impl ConfigCheck for BlkDevConfig { .into()); } + if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u16 { + return Err(ErrorKind::IllegalValue( + "number queues of block device".to_string(), + 1, + true, + MAX_VIRTIO_QUEUE as u64, + true, + ) + .into()); + } + Ok(()) } } @@ -228,7 +243,8 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result Result("num-queues")? { + blkdevcfg.queues = queues; + } + if let Some(drive_arg) = &vm_config.drives.remove(&blkdrive) { blkdevcfg.path_on_host = drive_arg.path_on_host.clone(); blkdevcfg.read_only = drive_arg.read_only; @@ -444,7 +464,7 @@ mod tests { .is_ok()); let blk_cfg_res = parse_blk( &mut vm_config, - "virtio-blk-device,drive=rootfs,id=rootfs,iothread=iothread1,serial=111111", + "virtio-blk-device,drive=rootfs,id=rootfs,iothread=iothread1,serial=111111,num-queues=4", ); assert!(blk_cfg_res.is_ok()); let blk_device_config = blk_cfg_res.unwrap(); @@ -453,6 +473,7 @@ mod tests { assert_eq!(blk_device_config.direct, true); assert_eq!(blk_device_config.read_only, false); assert_eq!(blk_device_config.serial_num, Some(String::from("111111"))); + assert_eq!(blk_device_config.queues, 4); let mut vm_config = VmConfig::default(); assert!(vm_config @@ -471,7 +492,7 @@ mod tests { assert!(vm_config .add_drive("id=rootfs,file=/path/to/rootfs,readonly=off,direct=on") .is_ok()); - let blk_cfg = "virtio-blk-pci,id=rootfs,bus=pcie.0,addr=0x1.0x2,drive=rootfs,serial=111111"; + let blk_cfg = "virtio-blk-pci,id=rootfs,bus=pcie.0,addr=0x1.0x2,drive=rootfs,serial=111111,num-queues=4"; let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg); assert!(blk_cfg_res.is_ok()); let drive_configs = blk_cfg_res.unwrap(); @@ -480,6 +501,7 @@ mod tests { assert_eq!(drive_configs.direct, true); assert_eq!(drive_configs.read_only, false); assert_eq!(drive_configs.serial_num, Some(String::from("111111"))); + assert_eq!(drive_configs.queues, 4); let pci_bdf = get_pci_bdf(blk_cfg); assert!(pci_bdf.is_ok()); diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 02d890000..65fb547f6 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -310,6 +310,13 @@ pub trait DeviceInterface { }; vec_props.push(prop); } + if typename.contains("virtio-blk") { + let prop = DeviceProps { + name: "num-queues".to_string(), + prop_type: "uint16".to_string(), + }; + vec_props.push(prop); + } Response::create_response(serde_json::to_value(&vec_props).unwrap(), None) } -- Gitee From 87c4aabea7624d60afb922fdbbb451ff0c186f92 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 6 Jan 2022 17:35:45 +0800 Subject: [PATCH 0023/2187] multiqueue: add multiple queue for block device Update block.rs, virtio-pci.rs and lib.rs to add multiqueue support. It can improve block I/O performance. Signed-off-by: Xinle.Guo --- virtio/src/block.rs | 190 ++++++++++++++++++++++++++------------- virtio/src/lib.rs | 2 + virtio/src/virtio_pci.rs | 3 + 3 files changed, 132 insertions(+), 63 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 696dac59d..d65fd06c7 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -39,9 +39,10 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_BLK_F_FLUSH, - VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_ID_BYTES, - VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, + VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_BLOCK, }; /// Number of virtqueues. @@ -819,6 +820,61 @@ impl EventNotifierHelper for BlockIoHandler { } } +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioBlkGeometry { + cylinders: u16, + heads: u8, + sectors: u8, +} + +impl ByteCode for VirtioBlkGeometry {} + +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioBlkConfig { + /// The capacity in 512 byte sectors. + capacity: u64, + /// The maximum segment size. + size_max: u32, + /// Tne maximum number of segments. + seg_max: u32, + /// Geometry of the block device. + geometry: VirtioBlkGeometry, + /// Block size of device. + blk_size: u32, + /// Exponent for physical block per logical block. + physical_block_exp: u8, + /// Alignment offset in logical blocks. + alignment_offset: u8, + /// Minimum I/O size without performance penalty in logical blocks. + min_io_size: u16, + /// Optimal sustained I/O size in logical blocks. + opt_io_size: u32, + /// Writeback mode. + wce: u8, + /// Reserved data. + unused: u8, + /// Number of virtio queues, only available when `VIRTIO_BLK_F_MQ` is set. + num_queues: u16, + /// The maximum discard sectors for one segment. + max_discard_sectors: u32, + /// The maximum number of discard segments in a discard command. + max_discard_seg: u32, + /// Discard commands must be aligned to this number of sectors. + discard_sector_alignment: u32, + /// The maximum number of write zeros sectors. + max_write_zeroes_sectors: u32, + /// The maximum number of segments in a write zeroes command. + max_write_zeroes_seg: u32, + /// Deallocation of one or more of the sectors. + write_zeroes_may_unmap: u8, + /// Reserved data. + unused1: [u8; 3], +} + +impl ByteCode for VirtioBlkConfig {} + /// State of block device. #[repr(C)] #[derive(Clone, Copy, Desc, ByteCode)] @@ -829,7 +885,7 @@ pub struct BlockState { /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, /// Config space of the block device. - config_space: [u8; 16], + config_space: VirtioBlkConfig, } /// Block device structure. @@ -845,7 +901,7 @@ pub struct Block { /// Callback to trigger interrupt. interrupt_cb: Option>, /// The sending half of Rust's channel to send the image file. - sender: Option>, + senders: Option>>, /// Eventfd for config space update. update_evt: EventFd, /// Eventfd for device deactivate. @@ -860,7 +916,7 @@ impl Default for Block { disk_sectors: 0, state: BlockState::default(), interrupt_cb: None, - sender: None, + senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } @@ -875,7 +931,7 @@ impl Block { disk_sectors: 0, state: BlockState::default(), interrupt_cb: None, - sender: None, + senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } @@ -884,14 +940,9 @@ impl Block { fn build_device_config_space(&mut self) { // capacity: 64bits let num_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; - for i in 0..8 { - self.state.config_space[i] = (num_sectors >> (8 * i)) as u8; - } - - // seg_max=128-2: 32bits - for i in 0..4 { - self.state.config_space[12 + i] = (126 >> (8 * i)) as u8; - } + self.state.config_space.capacity = num_sectors; + // seg_max = 128-2: 32bits + self.state.config_space.seg_max = 126; } } @@ -919,6 +970,11 @@ impl VirtioDevice for Block { self.build_device_config_space(); + if self.blk_cfg.queues > 1 { + self.state.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; + self.state.config_space.num_queues = self.blk_cfg.queues; + } + let mut disk_size = DUMMY_IMG_SIZE; if !self.blk_cfg.path_on_host.is_empty() { @@ -959,9 +1015,7 @@ impl VirtioDevice for Block { } self.disk_sectors = disk_size >> SECTOR_SHIFT; - for i in 0..8 { - self.state.config_space[i] = (self.disk_sectors >> (8 * i)) as u8; - } + self.state.config_space.capacity = self.disk_sectors; Ok(()) } @@ -981,7 +1035,7 @@ impl VirtioDevice for Block { /// Get the count of virtio device queues. fn queue_num(&self) -> usize { - QUEUE_NUM_BLK + self.blk_cfg.queues as usize } /// Get the queue size of virtio device. @@ -1006,14 +1060,13 @@ impl VirtioDevice for Block { /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_len = self.state.config_space.len() as u64; + let config_slice = self.state.config_space.as_bytes(); + let config_len = config_slice.len() as u64; if offset >= config_len { return Err(ErrorKind::DevConfigOverflow(offset, config_len).into()); } if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all( - &self.state.config_space[offset as usize..cmp::min(end, config_len) as usize], - )?; + data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; } Ok(()) @@ -1022,13 +1075,13 @@ impl VirtioDevice for Block { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let config_len = self.state.config_space.len(); + let config_slice = self.state.config_space.as_mut_bytes(); + let config_len = config_slice.len(); if offset as usize + data_len > config_len { return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); } - self.state.config_space[(offset as usize)..(offset as usize + data_len)] - .copy_from_slice(data); + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); Ok(()) } @@ -1043,33 +1096,39 @@ impl VirtioDevice for Block { mut queue_evts: Vec, ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); - let (sender, receiver) = channel(); - self.sender = Some(sender); + let mut senders = Vec::new(); + + for queue in queues.iter() { + let (sender, receiver) = channel(); + senders.push(sender); + + let mut handler = BlockIoHandler { + queue: queue.clone(), + queue_evt: queue_evts.remove(0), + mem_space: mem_space.clone(), + disk_image: self.disk_image.clone(), + disk_sectors: self.disk_sectors, + direct: self.blk_cfg.direct, + serial_num: self.blk_cfg.serial_num.clone(), + aio: None, + driver_features: self.state.driver_features, + receiver, + update_evt: self.update_evt.as_raw_fd(), + deactivate_evt: self.deactivate_evt.as_raw_fd(), + interrupt_cb: interrupt_cb.clone(), + iothread: self.blk_cfg.iothread.clone(), + leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), + }; - let mut handler = BlockIoHandler { - queue: queues[0].clone(), - queue_evt: queue_evts.remove(0), - mem_space, - disk_image: self.disk_image.clone(), - disk_sectors: self.disk_sectors, - direct: self.blk_cfg.direct, - serial_num: self.blk_cfg.serial_num.clone(), - aio: None, - driver_features: self.state.driver_features, - receiver, - update_evt: self.update_evt.as_raw_fd(), - deactivate_evt: self.deactivate_evt.as_raw_fd(), - interrupt_cb, - iothread: self.blk_cfg.iothread.clone(), - leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), - }; + handler.aio = Some(handler.build_aio()?); - handler.aio = Some(handler.build_aio()?); + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), + self.blk_cfg.iothread.as_ref(), + )?; + } - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), - self.blk_cfg.iothread.as_ref(), - )?; + self.senders = Some(senders); Ok(()) } @@ -1087,21 +1146,25 @@ impl VirtioDevice for Block { .downcast_ref::() .unwrap() .clone(); + // microvm type block device don't support multiple queue. + self.blk_cfg.queues = QUEUE_NUM_BLK as u16; } else { self.blk_cfg = Default::default(); } self.realize()?; - if let Some(sender) = &self.sender { - sender - .send(( - self.disk_image.take(), - self.disk_sectors, - self.blk_cfg.serial_num.clone(), - self.blk_cfg.direct, - )) - .chain_err(|| ErrorKind::ChannelSend("image fd".to_string()))?; + if let Some(senders) = &self.senders { + for sender in senders { + sender + .send(( + self.disk_image.clone(), + self.disk_sectors, + self.blk_cfg.serial_num.clone(), + self.blk_cfg.direct, + )) + .chain_err(|| ErrorKind::ChannelSend("image fd".to_string()))?; + } self.update_evt .write(1) @@ -1155,7 +1218,8 @@ mod tests { use std::{thread, time::Duration}; use vmm_sys_util::tempfile::TempFile; - const CONFIG_SPACE_SIZE: usize = 16; + const QUEUE_NUM_BLK: usize = 1; + const CONFIG_SPACE_SIZE: usize = 60; const VIRTQ_DESC_F_NEXT: u16 = 0x01; const VIRTQ_DESC_F_WRITE: u16 = 0x02; const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; @@ -1194,10 +1258,10 @@ mod tests { assert_eq!(block.disk_sectors, 0); assert_eq!(block.state.device_features, 0); assert_eq!(block.state.driver_features, 0); - assert_eq!(block.state.config_space.len(), 16); + assert_eq!(block.state.config_space.as_bytes().len(), CONFIG_SPACE_SIZE); assert!(block.disk_image.is_none()); assert!(block.interrupt_cb.is_none()); - assert!(block.sender.is_none()); + assert!(block.senders.is_none()); // Realize block device: create TempFile as backing file. block.blk_cfg.read_only = true; @@ -1228,7 +1292,7 @@ mod tests { assert!(block .write_config(CONFIG_SPACE_SIZE as u64 + 1, &expect_config_space) .is_err()); - let errlen_config_space = [0u8; 17]; + let errlen_config_space = [0u8; CONFIG_SPACE_SIZE + 1]; assert!(block.write_config(0, &errlen_config_space).is_err()); // Invalid read read_config_space = expect_config_space; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 68dd3dde6..cd06c832a 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -209,6 +209,8 @@ pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0; pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: u16 = 1; /// The maximum pairs of multiple queue. pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000; +/// Support more than one virtqueue. +pub const VIRTIO_BLK_F_MQ: u32 = 12; /// The IO type of virtio block, refer to Virtio Spec. /// Read. diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index f53a648b0..3ffbd7b65 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -702,6 +702,9 @@ impl VirtioPciDevice { .queues_config; let mut locked_queues = cloned_pci_device.queues.lock().unwrap(); for q_config in queues_config.iter_mut() { + if !q_config.ready { + continue; + } q_config.addr_cache.desc_table_host = cloned_mem_space .get_host_address(q_config.desc_table) .unwrap_or(0); -- Gitee From 19d2a4fc89cfd957ad64116635a478fa36786a80 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 7 Jan 2022 11:40:20 +0800 Subject: [PATCH 0024/2187] multiqueue: add num-queues attribute for hot plugging block For hot plug qmp command: `device_add: ..., num-queues`, update qmp schema to support hot plug multiple queue block device. Signed-off-by: Xinle.Guo --- machine/src/standard_vm/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 4d1e1ce18..53eb22dd2 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -557,6 +557,7 @@ impl StdMachine { serial_num: args.serial_num.clone(), iothread: args.iothread.clone(), iops: conf.iops, + queues: args.queues.unwrap_or(1), }; dev.check()?; dev diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 0058e9429..17dc7e540 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -482,6 +482,8 @@ pub struct device_add { pub iothread: Option, pub multifunction: Option, pub host: Option, + #[serde(rename = "num-queues")] + pub queues: Option, } pub type DeviceAddArgument = device_add; -- Gitee From 1e91617ad7e9cc881d312bc8b9d8602f4d17ff3a Mon Sep 17 00:00:00 2001 From: ace-yan Date: Sun, 17 Apr 2022 15:49:41 +0800 Subject: [PATCH 0025/2187] Updated build test image documentation. Signed-off-by: Yan Wen --- tests/hydropper/docs/IMAGE_BUILD.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/hydropper/docs/IMAGE_BUILD.md b/tests/hydropper/docs/IMAGE_BUILD.md index 2547c3e64..4485cfa21 100644 --- a/tests/hydropper/docs/IMAGE_BUILD.md +++ b/tests/hydropper/docs/IMAGE_BUILD.md @@ -1,6 +1,6 @@ # 构建测试镜像 -1. 请于openEuler官网,下载所需版本的stratovirt_img和vmlinux.bin。(以下以openEuler-21.03-stratovirt-x86_64.img为例) +1. 请于openEuler官网,下载所需版本的stratovirt_img。(以下以openEuler-22.03-LTS-stratovirt-x86_64.img为例) - 地址:https://openeuler.org/zh/download/ @@ -15,13 +15,13 @@ - 扩容stratovirt_img ```shell - cat extend.img >> openEuler-21.03-stratovirt-x86_64.img + cat extend.img >> openEuler-22.03-LTS-stratovirt-x86_64.img ``` - 调整文件系统大小 ```shell - e2fsck -f openEuler-21.03-stratovirt-x86_64.img && resize2fs openEuler-21.03-stratovirt-x86_64.img + e2fsck -f openEuler-22.03-LTS-stratovirt-x86_64.img && resize2fs openEuler-22.03-LTS-stratovirt-x86_64.img ``` 3. 添加依赖包 @@ -29,13 +29,13 @@ - 挂载镜像 ```shell - mount openEuler-21.03-stratovirt-x86_64.img /mnt + mount openEuler-22.03-LTS-stratovirt-x86_64.img /mnt ``` -- 配置在线yum源,请参考: [开发环境准备.md](https://gitee.com/openeuler/docs/blob/stable2-21.03/docs/zh/docs/ApplicationDev/开发环境准备.md)。由于stratovirt_img内没有vi等编辑工具,建议先在主机上创建文件openEuler.repo,并配置好yum源,完成后将openEuler.repo拷贝到镜像内。 +- 配置DNS服务配置文件(/etc/resolv.conf)。挂载镜像中的etc/resolv.conf文件为空,需要配置DNS服务才能更新yum源。 ```shell - cp ./openEuler.repo /mnt/etc/yum.repos.d + cp /etc/resolv.conf /mnt/etc/resolv.conf ``` - 进入镜像挂载目录,通过yum命令安装依赖包。 @@ -43,6 +43,7 @@ ```shell cd /mnt chroot . + echo "set enable-bracketed-paste off" > /root/.inputrc yum -y install openssh ``` -- Gitee From df70286aea86114ca7a1a542aef0631e9313e9ea Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Wed, 20 Apr 2022 15:56:51 +0800 Subject: [PATCH 0026/2187] test_microvm_virtio_blk: fix _get_lsblk_info bug Since version 2.37, lsblk will display empty block devices, which will cause false positives in the hotplug check. Fix it by filtering devices whose size is 0B. Signed-off-by: Zuo Xiaoxian --- .../functional/test_microvm_virtio_blk.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py b/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py index 1dde7b621..220581c8c 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py @@ -11,6 +11,7 @@ # See the Mulan PSL v2 for more details. """Test microvm virtio block""" +import json import os import logging from subprocess import run @@ -29,14 +30,15 @@ def _get_lsblk_info(test_vm): """ retdict = {} if test_vm.ssh_session is not None: - _output = test_vm.ssh_session.cmd_output("lsblk") - for line in _output.split("\n"): - temp = line.split() - if len(temp) == 6: - name = temp[0] - size = temp[3] - readonly = temp[4] - if name not in retdict: + _output = json.loads(test_vm.ssh_session.cmd_output("lsblk -J")) + blockdevices = _output.get("blockdevices", []) + for dic in blockdevices: + mountpoints = dic.get("mountpoints", []) + if len(mountpoints) != 0 and None in mountpoints: + name = dic.get("name", "") + size = dic.get("size", "") + readonly = dic.get("ro", None) + if size != "0B" and name not in retdict: retdict[name] = {"size": size, "readonly": readonly} return retdict -- Gitee From 69ca1822c0b859a3362ae31aa87a39a1da6316de Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Sat, 23 Apr 2022 16:26:00 +0800 Subject: [PATCH 0027/2187] machine_manager: add testcase for memory_unit_conversion. 1. Add testcase to test memory arguments parsing. Signed-off-by: Ming Yang --- machine_manager/src/config/machine_config.rs | 82 ++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 92fddab92..e649ea466 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -323,4 +323,86 @@ mod tests { assert!(machine_config.check().is_ok()); } + + #[test] + fn test_memory_unit_conversion() { + let test_string = "6G"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_ok()); + let ret = ret.unwrap(); + assert_eq!(ret, 6 * 1024 * 1024 * 1024); + + let test_string = "6g"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_ok()); + let ret = ret.unwrap(); + assert_eq!(ret, 6 * 1024 * 1024 * 1024); + + let test_string = "6M"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_ok()); + let ret = ret.unwrap(); + assert_eq!(ret, 6 * 1024 * 1024); + + let test_string = "6m"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_ok()); + let ret = ret.unwrap(); + assert_eq!(ret, 6 * 1024 * 1024); + + // default unit is MiB + let test_string = "6"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_ok()); + let ret = ret.unwrap(); + assert_eq!(ret, 6 * 1024 * 1024); + + let test_string = "G6"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "G6G"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "6Gg"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "6gG"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "g6G"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "G6g"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "M6"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "M6M"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "6Mm"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "6mM"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "m6M"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + + let test_string = "M6m"; + let ret = memory_unit_conversion(test_string); + assert!(ret.is_err()); + } } -- Gitee From abdd341c93a34f92eb6dd0b0c3595891cc40d3af Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Sat, 23 Apr 2022 16:50:20 +0800 Subject: [PATCH 0028/2187] machine_manager: add testcase for parse_device_id. Signed-off-by: Ming Yang --- machine_manager/src/config/devices.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index cfc972c5b..08e558115 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -38,3 +38,23 @@ pub fn parse_device_id(device_config: &str) -> Result { Ok(String::new()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_device_id() { + let test_conf = "virtio-blk-device,drive=rootfs,id=blkid"; + let ret = parse_device_id(test_conf); + assert!(ret.is_ok()); + let id = ret.unwrap(); + assert_eq!("blkid", id); + + let test_conf = "virtio-blk-device,drive=rootfs"; + let ret = parse_device_id(test_conf); + assert!(ret.is_ok()); + let id = ret.unwrap(); + assert_eq!("", id); + } +} -- Gitee From 94e47d9233a217295bb80bf09f1544decea7d249 Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Sat, 23 Apr 2022 17:06:52 +0800 Subject: [PATCH 0029/2187] machine_manager: add testcase for machine type parsing. 1. Machine type is configured from string. this testcase is used to test the from_str function of MachineType. Signed-off-by: Ming Yang --- machine_manager/src/config/machine_config.rs | 79 ++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index e649ea466..829a733c3 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -405,4 +405,83 @@ mod tests { let ret = memory_unit_conversion(test_string); assert!(ret.is_err()); } + + #[test] + fn test_machine_type() { + let test_string = "none"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::None); + + let test_string = "None"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::None); + + let test_string = "NONE"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::None); + + let test_string = "no"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_err()); + + let test_string = "microvm"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::MicroVm); + + let test_string = "MICROVM"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::MicroVm); + + let test_string = "machine"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_err()); + + #[cfg(target_arch = "x86_64")] + { + let test_string = "q35"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::StandardVm); + + let test_string = "Q35"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::StandardVm); + + let test_string = "virt"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_err()); + } + + #[cfg(target_arch = "aarch64")] + { + let test_string = "virt"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::StandardVm); + + let test_string = "VIRT"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_ok()); + let machine_type = machine_type.unwrap(); + assert_eq!(machine_type, MachineType::StandardVm); + + let test_string = "q35"; + let machine_type = MachineType::from_str(test_string); + assert!(machine_type.is_err()); + } + } } -- Gitee From 25a5fe8fb1727360cd0d635ffe30000738c2c225 Mon Sep 17 00:00:00 2001 From: Zuo xiaoxian Date: Sun, 24 Apr 2022 19:57:43 +0800 Subject: [PATCH 0030/2187] test_microvm_vmlife: Remove test case for shutdown in vm. Microvm does not use the method of shutting down in the vm, so remove this test case. Signed-off-by: Zuo Xiaoxian --- .../testcases/microvm/functional/test_microvm_vmlife.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_vmlife.py b/tests/hydropper/testcases/microvm/functional/test_microvm_vmlife.py index 8923af60f..929839fd0 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_vmlife.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_vmlife.py @@ -78,14 +78,6 @@ def test_microvm_destroy(microvm, destroy_value): test_vm.destroy(signal=destroy_value) -@pytest.mark.system -def test_microvm_inshutdown(microvm): - """Test a normal microvm inshutdown""" - test_vm = microvm - test_vm.launch() - test_vm.inshutdown() - - @pytest.mark.acceptance def test_microvm_pause_resume(microvm): """Test a normal microvm pause""" -- Gitee From 8a41f8ea3f00c7c34b9af4302e601e581aeb5f71 Mon Sep 17 00:00:00 2001 From: Zuo xiaoxian Date: Sun, 24 Apr 2022 20:14:30 +0800 Subject: [PATCH 0031/2187] test_microvm_cpu_features: Support lscpu 2.37 in guest os. Since version 2.37, lscpu use Cluster instead of Socket on the aarch64 machine which doesn't have ACPI PPTT. So when the Cluster appears, the number of sockets is no longer checked. Signed-off-by: Zuo Xiaoxian --- .../microvm/functional/test_microvm_cpu_features.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py index 17573c7cb..9be504329 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py @@ -97,6 +97,13 @@ def _check_cpu_topology(test_microvm, expected_cpu_count, "Core(s) per socket": str(expected_cores_per_socket), "Socket(s)": str(int(expected_cpu_count / expected_cores_per_socket / expected_threads_per_core)), } + status, output = test_microvm.serial_cmd("lscpu") + assert status == 0, str(output) + if "Core(s) per cluster" in output and "aarch64" in platform.machine(): + expected_cpu_topology["Core(s) per cluster"] = expected_cpu_topology["Core(s) per socket"] + del expected_cpu_topology["Core(s) per socket"] + expected_cpu_topology["Cluster(s)"] = expected_cpu_topology["Socket(s)"] + del expected_cpu_topology["Socket(s)"] _check_guest_cmd_output(test_microvm, "lscpu", None, ':', expected_cpu_topology) -- Gitee From 6df19003a0a9ca6a9918caa4315d18cc23296a05 Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Wed, 27 Apr 2022 21:21:07 +0800 Subject: [PATCH 0032/2187] machine_manager: add testcase for add_memory and add machine function. Signed-off-by: Ming Yang --- machine_manager/src/config/machine_config.rs | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 829a733c3..bab513858 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -484,4 +484,85 @@ mod tests { assert!(machine_type.is_err()); } } + + #[test] + fn test_add_memory() { + let mut vm_config = VmConfig::default(); + let memory_cfg = "size=8"; + let mem_cfg_ret = vm_config.add_memory(memory_cfg); + assert!(mem_cfg_ret.is_ok()); + let mem_size = vm_config.machine_config.mem_config.mem_size; + assert_eq!(mem_size, 8 * 1024 * 1024); + + let memory_cfg = "size=8m"; + let mem_cfg_ret = vm_config.add_memory(memory_cfg); + assert!(mem_cfg_ret.is_ok()); + let mem_size = vm_config.machine_config.mem_config.mem_size; + assert_eq!(mem_size, 8 * 1024 * 1024); + + let memory_cfg = "size=8G"; + let mem_cfg_ret = vm_config.add_memory(memory_cfg); + assert!(mem_cfg_ret.is_ok()); + let mem_size = vm_config.machine_config.mem_config.mem_size; + assert_eq!(mem_size, 8 * 1024 * 1024 * 1024); + } + + #[test] + fn test_add_machine() { + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,dump-guest-core=on,mem-share=on,accel=kvm,usb=off"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_ok()); + let machine_cfg = vm_config.machine_config; + assert_eq!(machine_cfg.mach_type, MachineType::None); + assert_eq!(machine_cfg.mem_config.dump_guest_core, true); + assert_eq!(machine_cfg.mem_config.mem_share, true); + + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,dump-guest-core=off,mem-share=off,accel=kvm,usb=off"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_ok()); + let machine_cfg = vm_config.machine_config; + assert_eq!(machine_cfg.mach_type, MachineType::None); + assert_eq!(machine_cfg.mem_config.dump_guest_core, false); + assert_eq!(machine_cfg.mem_config.mem_share, false); + + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,accel=kvm-tcg"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_err()); + + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,usb=on"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_err()); + + #[cfg(target_arch = "aarch64")] + { + let mut vm_config = VmConfig::default(); + let memory_cfg_str = + "type=none,dump-guest-core=off,mem-share=off,accel=kvm,usb=off,gic-version=3"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_ok()); + let machine_cfg = vm_config.machine_config; + assert_eq!(machine_cfg.mach_type, MachineType::None); + assert_eq!(machine_cfg.mem_config.dump_guest_core, false); + assert_eq!(machine_cfg.mem_config.mem_share, false); + + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,gic-version=-1"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_err()); + + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,gic-version=256"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_err()); + + let mut vm_config = VmConfig::default(); + let memory_cfg_str = "type=none,gic-version=4"; + let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); + assert!(machine_cfg_ret.is_err()); + } + } } -- Gitee From 2cb4add11bd21121b6a2749e83c46d4f766c4d12 Mon Sep 17 00:00:00 2001 From: Ming Yang Date: Wed, 27 Apr 2022 22:31:59 +0800 Subject: [PATCH 0033/2187] machine_manager: add testcases. 1. Add test case for add_mem_path function to check if the path is correctly added. 2. Add test case for enable_mem_prealloc to check if mem_prealloc is enabled. 3. Add test case for add_cpu to check if the cpu is corretly configured. Signed-off-by: Ming Yang --- machine_manager/src/config/machine_config.rs | 82 ++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index bab513858..bf0c21e9b 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -565,4 +565,86 @@ mod tests { assert!(machine_cfg_ret.is_err()); } } + + #[test] + fn test_add_mem_path() { + let mut vm_config = VmConfig::default(); + let memory_path_str = "/path/to/memory-backend"; + let mem_path = vm_config.machine_config.mem_config.mem_path.clone(); + // defalut value is none. + assert!(mem_path.is_none()); + let mem_cfg_ret = vm_config.add_mem_path(memory_path_str); + assert!(mem_cfg_ret.is_ok()); + let mem_path = vm_config.machine_config.mem_config.mem_path; + assert!(mem_path.is_some()); + let mem_path = mem_path.unwrap(); + assert_eq!(mem_path, memory_path_str); + } + + #[test] + fn test_enable_memory_prealloc() { + let mut vm_config = VmConfig::default(); + let mem_prealloc = vm_config.machine_config.mem_config.mem_prealloc; + // default value is false. + assert_eq!(mem_prealloc, false); + vm_config.enable_mem_prealloc(); + let mem_prealloc = vm_config.machine_config.mem_config.mem_prealloc; + assert_eq!(mem_prealloc, true); + } + + #[test] + fn test_add_cpu() { + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=8,cores=1,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_ok()); + let nr_cpu = vm_config.machine_config.nr_cpus; + assert_eq!(nr_cpu, 8); + + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=9,sockets=8,cores=1,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=0,sockets=0,cores=1,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=254,sockets=254,cores=1,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_ok()); + let nr_cpu = vm_config.machine_config.nr_cpus; + assert_eq!(nr_cpu, 254); + + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=255,sockets=255,cores=1,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=4,cores=2,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=2,cores=2,threads=2"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=1,cores=4,threads=2"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=1,cores=2,threads=4"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + } } -- Gitee From 32497d58fd6329ca94b11577054868f7c3dc7a85 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 8 Apr 2022 15:22:20 +0800 Subject: [PATCH 0034/2187] x86/acpi: wrapper some of acpi builder code to `add_table_to_loader` function There are remain a large number of duplicate codes in acpi builder. Useing ``add_table_to_loader` function here to wapper the code. Signed-off-by: Xinle.Guo --- machine/src/standard_vm/x86_64/mod.rs | 40 ++++++--------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 954013f16..1388c25f9 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -22,8 +22,8 @@ use std::sync::{Arc, Condvar, Mutex}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, - AmlPackage, AmlScope, AmlScopeBuilder, AmlString, TableLoader, ACPI_TABLE_FILE, - IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, TABLE_CHECKSUM_OFFSET, + AmlPackage, AmlScope, AmlScopeBuilder, AmlString, TableLoader, IOAPIC_BASE_ADDR, + LAPIC_BASE_ADDR, }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -556,6 +556,7 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> super::errors::Result { + use super::errors::ResultExt; let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -583,21 +584,8 @@ impl AcpiBuilder for StdMachine { package.append_child(AmlInteger(0)); dsdt.append_child(AmlNameDecl::new("_S5", package).aml_bytes().as_slice()); - let mut locked_acpi_data = acpi_data.lock().unwrap(); - let dsdt_begin = locked_acpi_data.len() as u32; - locked_acpi_data.extend(dsdt.aml_bytes()); - let dsdt_end = locked_acpi_data.len() as u32; - // Drop the lock of acpi_data to avoid dead-lock when adding entry to - // TableLoader, because TableLoader also needs to acquire this lock. - drop(locked_acpi_data); - - loader.add_cksum_entry( - ACPI_TABLE_FILE, - dsdt_begin + TABLE_CHECKSUM_OFFSET, - dsdt_begin, - dsdt_end - dsdt_begin, - )?; - + let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) + .chain_err(|| "Fail to add DSTD table to loader")?; Ok(dsdt_begin as u64) } @@ -606,6 +594,7 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> super::errors::Result { + use super::errors::ResultExt; let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.append_child(LAPIC_BASE_ADDR.as_bytes()); @@ -633,21 +622,8 @@ impl AcpiBuilder for StdMachine { madt.append_child(&lapic.aml_bytes()); }); - let mut locked_acpi_data = acpi_data.lock().unwrap(); - let madt_begin = locked_acpi_data.len() as u32; - locked_acpi_data.extend(madt.aml_bytes()); - let madt_end = locked_acpi_data.len() as u32; - // Drop the lock of acpi_data to avoid dead-lock when adding entry to - // TableLoader, because TableLoader also needs to acquire this lock. - drop(locked_acpi_data); - - loader.add_cksum_entry( - ACPI_TABLE_FILE, - madt_begin + TABLE_CHECKSUM_OFFSET, - madt_begin, - madt_end - madt_begin, - )?; - + let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) + .chain_err(|| "Fail to add DSTD table to loader")?; Ok(madt_begin as u64) } } -- Gitee From 2250d195a46b14840f04bf2d08509a72f6c8737d Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Sat, 23 Apr 2022 14:49:15 +0800 Subject: [PATCH 0035/2187] pci/host: enable acpi hot plug feature for aarch64 Under the ACPI standard, if the pci host does not define a `_OSC` method, then the guest will not be able to use the hot plug/unplug related features of the pci device. Fix it by adding the _OSC method on the aarch64 platform. Signed-off-by: Jiajie Li --- pci/src/host.rs | 134 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 35 deletions(-) diff --git a/pci/src/host.rs b/pci/src/host.rs index 10d3a1e64..c84a6becc 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -13,15 +13,14 @@ use std::sync::{Arc, Mutex}; use acpi::{ - AmlAddressSpaceDecode, AmlBuilder, AmlByte, AmlCacheable, AmlDWord, AmlDWordDesc, AmlDevice, - AmlEisaId, AmlISARanges, AmlNameDecl, AmlPackage, AmlReadAndWrite, AmlResTemplate, - AmlScopeBuilder, AmlWordDesc, AmlZero, + AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlByte, AmlCacheable, AmlCreateDWordField, + AmlDWord, AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, + AmlInteger, AmlLNot, AmlLocal, AmlMethod, AmlName, AmlNameDecl, AmlOr, AmlPackage, + AmlReadAndWrite, AmlResTemplate, AmlReturn, AmlScopeBuilder, AmlStore, AmlToUuid, AmlWordDesc, + AmlZero, }; #[cfg(target_arch = "x86_64")] -use acpi::{ - AmlAnd, AmlArg, AmlCreateDWordField, AmlElse, AmlEqual, AmlIf, AmlInteger, AmlIoDecode, - AmlIoResource, AmlLNot, AmlLocal, AmlMethod, AmlName, AmlOr, AmlReturn, AmlStore, AmlToUuid, -}; +use acpi::{AmlIoDecode, AmlIoResource}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use sysbus::{errors::Result as SysBusResult, SysBusDevOps}; @@ -262,6 +261,98 @@ impl SysBusDevOps for PciHost { } } +#[cfg(target_arch = "x86_64")] +fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { + let mut method = AmlMethod::new("_OSC", 4, false); + method.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(0), "CDW1")); + let mut if_obj_0 = AmlIf::new(AmlEqual::new( + AmlArg(0), + AmlToUuid::new("33db4d5b-1ff7-401c-9657-7441c03dd766"), + )); + if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(4), "CDW2")); + if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(8), "CDW3")); + let cdw3 = AmlName("CDW3".to_string()); + if_obj_0.append_child(AmlStore::new(cdw3.clone(), AmlLocal(0))); + if_obj_0.append_child(AmlAnd::new(AmlLocal(0), AmlInteger(0x1f), AmlLocal(0))); + let mut if_obj_1 = AmlIf::new(AmlLNot::new(AmlEqual::new(AmlArg(1), AmlInteger(1)))); + let cdw1 = AmlName("CDW1".to_string()); + if_obj_1.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x08), cdw1.clone())); + if_obj_0.append_child(if_obj_1); + let mut if_obj_2 = AmlIf::new(AmlLNot::new(AmlEqual::new(cdw3.clone(), AmlLocal(0)))); + if_obj_2.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x10), cdw1.clone())); + if_obj_0.append_child(if_obj_2); + if_obj_0.append_child(AmlStore::new(AmlLocal(0), cdw3)); + method.append_child(if_obj_0); + let mut else_obj_0 = AmlElse::new(); + else_obj_0.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x04), cdw1)); + method.append_child(else_obj_0); + method.append_child(AmlReturn::with_value(AmlArg(3))); + pci_host_bridge.append_child(method); +} + +#[cfg(target_arch = "aarch64")] +fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { + // _OSC means Operating System Capabilities. + pci_host_bridge.append_child(AmlNameDecl::new("SUPP", AmlInteger(0))); + pci_host_bridge.append_child(AmlNameDecl::new("CTRL", AmlInteger(0))); + let mut method = AmlMethod::new("_OSC", 4, false); + method.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(0), "CDW1")); + // The id is for PCI Host Bridge Device. + let mut if_obj_0 = AmlIf::new(AmlEqual::new( + AmlArg(0), + AmlToUuid::new("33db4d5b-1ff7-401c-9657-7441c03dd766"), + )); + // Get value from argument for SUPP and CTRL. + if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(4), "CDW2")); + if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(8), "CDW3")); + if_obj_0.append_child(AmlStore::new( + AmlName("CDW2".to_string()), + AmlName("SUPP".to_string()), + )); + if_obj_0.append_child(AmlStore::new( + AmlName("CDW3".to_string()), + AmlName("CTRL".to_string()), + )); + if_obj_0.append_child(AmlStore::new( + AmlAnd::new(AmlName("CTRL".to_string()), AmlInteger(0x1d), AmlLocal(0)), + AmlName("CTRL".to_string()), + )); + let mut if_obj_1 = AmlIf::new(AmlLNot::new(AmlEqual::new(AmlArg(1), AmlInteger(1)))); + if_obj_1.append_child(AmlAnd::new( + AmlName("CDW1".to_string()), + AmlInteger(0x08), + AmlName("CDW1".to_string()), + )); + if_obj_0.append_child(if_obj_1); + let mut if_obj_2 = AmlIf::new(AmlLNot::new(AmlEqual::new( + AmlName("CDW3".to_string()), + AmlName("CTRL".to_string()), + ))); + if_obj_2.append_child(AmlOr::new( + AmlName("CDW1".to_string()), + AmlInteger(0x10), + AmlName("CDW1".to_string()), + )); + if_obj_0.append_child(if_obj_2); + if_obj_0.append_child(AmlStore::new( + AmlName("CTRL".to_string()), + AmlName("CDW3".to_string()), + )); + // For pci host, kernel will use _OSC return value to determine + // whether native_pcie_hotplug is enabled or not. + if_obj_0.append_child(AmlReturn::with_value(AmlArg(3))); + method.append_child(if_obj_0); + let mut else_obj_0 = AmlElse::new(); + else_obj_0.append_child(AmlOr::new( + AmlName("CDW1".to_string()), + AmlInteger(0x04), + AmlName("CDW1".to_string()), + )); + else_obj_0.append_child(AmlReturn::with_value(AmlArg(3))); + method.append_child(else_obj_0); + pci_host_bridge.append_child(method); +} + impl AmlBuilder for PciHost { fn aml_bytes(&self) -> Vec { let mut pci_host_bridge = AmlDevice::new("PCI0"); @@ -270,34 +361,7 @@ impl AmlBuilder for PciHost { pci_host_bridge.append_child(AmlNameDecl::new("_ADR", AmlZero)); pci_host_bridge.append_child(AmlNameDecl::new("_UID", AmlZero)); - #[cfg(target_arch = "x86_64")] - { - let mut method = AmlMethod::new("_OSC", 4, false); - method.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(0), "CDW1")); - let mut if_obj_0 = AmlIf::new(AmlEqual::new( - AmlArg(0), - AmlToUuid::new("33db4d5b-1ff7-401c-9657-7441c03dd766"), - )); - if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(4), "CDW2")); - if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(8), "CDW3")); - let cdw3 = AmlName("CDW3".to_string()); - if_obj_0.append_child(AmlStore::new(cdw3.clone(), AmlLocal(0))); - if_obj_0.append_child(AmlAnd::new(AmlLocal(0), AmlInteger(0x1f), AmlLocal(0))); - let mut if_obj_1 = AmlIf::new(AmlLNot::new(AmlEqual::new(AmlArg(1), AmlInteger(1)))); - let cdw1 = AmlName("CDW1".to_string()); - if_obj_1.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x08), cdw1.clone())); - if_obj_0.append_child(if_obj_1); - let mut if_obj_2 = AmlIf::new(AmlLNot::new(AmlEqual::new(cdw3.clone(), AmlLocal(0)))); - if_obj_2.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x10), cdw1.clone())); - if_obj_0.append_child(if_obj_2); - if_obj_0.append_child(AmlStore::new(AmlLocal(0), cdw3)); - method.append_child(if_obj_0); - let mut else_obj_0 = AmlElse::new(); - else_obj_0.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x04), cdw1)); - method.append_child(else_obj_0); - method.append_child(AmlReturn::with_value(AmlArg(3))); - pci_host_bridge.append_child(method); - } + build_osc_for_aml(&mut pci_host_bridge); let pcie_ecam = self.pcie_ecam_range; let pcie_mmio = self.pcie_mmio_range; -- Gitee From fb5dd89955fbef934a8516d0ff371696cf54ef33 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 12 Apr 2022 21:12:45 +0800 Subject: [PATCH 0036/2187] NUMA: add code to parse NUMA node cmdline and guidance document 1. Base on `-object memory-backend-ram...` and `-numa node...` param in the StratoVirt cmdline, the NUMA cpus, memory size, host NUMA policy configurantion is described. 2. We can set the relative distance between all NUMA nodes from the optional `-numa dist...` cmdline. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 44 ++- machine_manager/src/cmdline.rs | 11 + machine_manager/src/config/machine_config.rs | 152 ++++++++- machine_manager/src/config/mod.rs | 53 +++- machine_manager/src/config/numa.rs | 307 +++++++++++++++++++ 5 files changed, 559 insertions(+), 8 deletions(-) create mode 100644 machine_manager/src/config/numa.rs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index ff586eaf2..46a536384 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -103,7 +103,41 @@ $ cat /proc/meminfo ... -mem-path /path/to/hugepages ... ``` -### 1.5 Kernel and Kernel Parameters +### 1.5 NUMA node +The optional NUMA node element gives the opportunity to create a virtual machine with non-uniform memory accesses. +The application of NUMA node is that one region of memory can be set as fast memory, another can be set as slow memory. + +Each NUMA node is given a list of command lines option, there will be described in detail below. +1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] + It describes the size and id of each memory zone, the policy of binding to host memory node. + you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. + The optional policies are default, preferred, bind, interleave and local. +2. -numa node,cpus=0-1,memdev=mem0 + It describes id and cpu set of the NUMA node, and the id belongs to which memory zone. +3. -numa dist,src=0,dst=0,val=10 + It describes the distance between source and destination. The default of source to source is 10, + source to destination is 20. And if you choose not to set these parameters, the VM will set the default values. + +The following command shows how to set NUMA node: + +```shell +# The number of cpu must be set to be the same as numa node cpu. +-smp 4 + +# The memory size must be set to be the same as numa node mem. +-m 4G + +-object memory-backend-ram,size=2G,id=mem0,[host-nodes=0,policy=bind] +-object memory-backend-ram,size=2G,id=mem1,[host-nodes=1,policy=bind] +-numa node,nodeid=0,cpus=0-1,memdev=mem0 +-numa node,nodeid=1,cpus=2-3,memdev=mem1 +[-numa dist,src=0,dst=0,val=10] +[-numa dist,src=0,dst=1,val=20] +[-numa dist,src=1,dst=0,val=20] +[-numa dist,src=1,dst=1,val=10] +``` + +### 1.6 Kernel and Kernel Parameters StratoVirt supports to launch PE or bzImage (only x86_64) format linux kernel 4.19 and can also set kernel parameters for VM. @@ -118,7 +152,7 @@ And the given kernel parameters will be actually analyzed by boot loader. -append "console=ttyS0 rebook=k panic=1 pci=off tsc=reliable ipv6.disable=1" ``` -### 1.6 Initrd Configuration +### 1.7 Initrd Configuration StratoVirt supports to launch VM by a initrd (boot loader initialized RAM disk) as well. @@ -131,7 +165,7 @@ If you want to use initrd as rootfs, `root=/dev/ram` and `rdinit=/bin/sh` must b -initrd /path/to/initrd ``` -### 1.7 Global config +### 1.8 Global config Users can set the global configuration using the -global parameter. @@ -143,7 +177,7 @@ One property can be set: -global pcie-root-port.fast-unplug=1 ``` -### 1.8 Logging +### 1.9 Logging StratoVirt supports to output log to stderr and log file. @@ -159,7 +193,7 @@ You can enable StratoVirt's logging by: StratoVirt's log-level depends on env `STRATOVIRT_LOG_LEVEL`. StratoVirt supports five log-levels: `trace`, `debug`, `info`, `warn`, `error`. The default level is `error`. -### 1.9 Daemonize +### 1.10 Daemonize StratoVirt supports to run as a daemon. diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 875d7fa88..62435324f 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -372,6 +372,16 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true) .required(false), ) + .arg( + Arg::with_name("numa") + .multiple(true) + .long("numa") + .value_name("node,nodeid=0,cpus=0-1,memdev=mem0> \ + -numa \ + -object Result { add_args_to_config_multi!((args.values_of("chardev")), vm_cfg, add_chardev); add_args_to_config_multi!((args.values_of("device")), vm_cfg, add_device); add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); + add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); if let Some(s) = args.value_of("trace") { add_trace_events(&s)?; diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index bf0c21e9b..5f5f8676e 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -15,7 +15,7 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result, ResultExt}; -use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig}; +use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_NODES, MAX_STRING_LENGTH}; const DEFAULT_CPUS: u8 = 1; const DEFAULT_MEMSIZE: u64 = 256; @@ -49,6 +49,49 @@ impl FromStr for MachineType { } } +#[repr(u32)] +#[derive(PartialEq, Eq)] +pub enum HostMemPolicy { + Default = 0, + Preferred = 1, + Bind = 2, + Interleave = 3, + Local = 4, + NotSupported = 5, +} + +impl From for HostMemPolicy { + fn from(str: String) -> HostMemPolicy { + match str.to_lowercase().as_str() { + "default" => HostMemPolicy::Default, + "preferred" => HostMemPolicy::Preferred, + "bind" => HostMemPolicy::Bind, + "interleave" => HostMemPolicy::Interleave, + "local" => HostMemPolicy::Local, + _ => HostMemPolicy::NotSupported, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MemZoneConfig { + pub id: String, + pub size: u64, + pub host_numa_node: Option, + pub policy: String, +} + +impl Default for MemZoneConfig { + fn default() -> Self { + MemZoneConfig { + id: String::new(), + size: 0, + host_numa_node: None, + policy: String::from("bind"), + } + } +} + /// Config that contains machine's memory information config. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct MachineMemConfig { @@ -57,6 +100,7 @@ pub struct MachineMemConfig { pub dump_guest_core: bool, pub mem_share: bool, pub mem_prealloc: bool, + pub mem_zones: Option>, } impl Default for MachineMemConfig { @@ -67,6 +111,7 @@ impl Default for MachineMemConfig { dump_guest_core: true, mem_share: false, mem_prealloc: false, + mem_zones: None, } } } @@ -242,9 +287,74 @@ impl VmConfig { pub fn enable_mem_prealloc(&mut self) { self.machine_config.mem_config.mem_prealloc = true; } + + pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { + let mut cmd_parser = CmdParser::new("mem_zone"); + cmd_parser + .push("") + .push("id") + .push("size") + .push("host-nodes") + .push("policy"); + cmd_parser.parse(mem_zone)?; + + let mut zone_config = MemZoneConfig::default(); + if let Some(id) = cmd_parser.get_value::("id")? { + if id.len() > MAX_STRING_LENGTH { + return Err( + ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into(), + ); + } + zone_config.id = id; + } else { + return Err(ErrorKind::FieldIsMissing("id", "memory-backend-ram").into()); + } + if let Some(mem) = cmd_parser.get_value::("size")? { + zone_config.size = memory_unit_conversion(&mem)?; + } else { + return Err(ErrorKind::FieldIsMissing("size", "memory-backend-ram").into()); + } + if let Some(host_nodes) = cmd_parser.get_value::("host-nodes")? { + if host_nodes >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "host_nodes".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + zone_config.host_numa_node = Some(host_nodes); + } + if let Some(policy) = cmd_parser.get_value::("policy")? { + if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { + return Err(ErrorKind::InvalidParam("policy".to_string(), policy).into()); + } + zone_config.policy = policy; + } + + if self.machine_config.mem_config.mem_zones.is_some() { + self.machine_config + .mem_config + .mem_zones + .as_mut() + .unwrap() + .push(zone_config.clone()); + } else { + self.machine_config.mem_config.mem_zones = Some(vec![zone_config.clone()]); + } + + Ok(zone_config) + } } -fn memory_unit_conversion(origin_value: &str) -> Result { +/// Convert memory units from GiB, Mib to Byte. +/// +/// # Arguments +/// +/// * `origin_value` - The origin memory value from user. +pub fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) { @@ -302,6 +412,7 @@ mod tests { mem_share: false, dump_guest_core: false, mem_prealloc: false, + mem_zones: None, }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, @@ -647,4 +758,41 @@ mod tests { let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); assert!(cpu_cfg_ret.is_err()); } + + #[test] + fn test_add_mem_zone() { + let mut vm_config = VmConfig::default(); + let zone_config_1 = vm_config + .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind") + .unwrap(); + assert_eq!(zone_config_1.id, "mem1"); + assert_eq!(zone_config_1.size, 2147483648); + assert_eq!(zone_config_1.host_numa_node, Some(1)); + assert_eq!(zone_config_1.policy, "bind"); + + let zone_config_2 = vm_config + .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1") + .unwrap(); + assert_eq!(zone_config_2.host_numa_node, None); + assert_eq!(zone_config_2.policy, "bind"); + + assert!(vm_config + .add_mem_zone("-object memory-backend-ram,size=2G") + .is_err()); + assert!(vm_config + .add_mem_zone("-object memory-backend-ram,id=mem1") + .is_err()); + } + + #[test] + fn test_host_mem_policy() { + let policy = HostMemPolicy::from(String::from("default")); + assert!(policy == HostMemPolicy::Default); + + let policy = HostMemPolicy::from(String::from("interleave")); + assert!(policy == HostMemPolicy::Interleave); + + let policy = HostMemPolicy::from(String::from("error")); + assert!(policy == HostMemPolicy::NotSupported); + } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 4c4e2bcd3..49d9832f1 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -85,6 +85,7 @@ pub use drive::*; pub use iothread::*; pub use machine_config::*; pub use network::*; +pub use numa::*; pub use pci::*; pub use rng::*; pub use vfio::*; @@ -97,6 +98,7 @@ mod drive; mod iothread; mod machine_config; mod network; +mod numa; mod pci; mod rng; mod vfio; @@ -114,10 +116,12 @@ pub const MAX_PATH_LENGTH: usize = 4096; pub const MAX_VIRTIO_QUEUE: usize = 1024; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; +pub const MAX_NODES: u32 = 128; #[derive(Debug, Clone)] pub enum ObjConfig { Rng(RngObjConfig), + Zone(MemZoneConfig), } fn parse_rng_obj(object_args: &str) -> Result { @@ -157,6 +161,7 @@ pub struct VmConfig { pub pflashs: Option>, pub dev_name: HashMap, pub global_config: HashMap, + pub numa_nodes: Vec<(String, String)>, } impl VmConfig { @@ -240,7 +245,17 @@ impl VmConfig { if self.object.get(&id).is_none() { self.object.insert(id, object_config); } else { - bail!("Object: {:?} has been added"); + bail!("Object: {} has been added", id); + } + } + "memory-backend-ram" => { + let zone_config = self.add_mem_zone(object_args)?; + let id = zone_config.id.clone(); + let object_config = ObjConfig::Zone(zone_config); + if self.object.get(&id).is_none() { + self.object.insert(id, object_config); + } else { + bail!("Object: {} has been added", id); } } _ => { @@ -490,6 +505,42 @@ pub fn add_trace_events(config: &str) -> Result<()> { bail!("trace: events file must be set."); } +pub struct IntegerList(pub Vec); + +impl FromStr for IntegerList { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + let mut integer_list = Vec::new(); + let lists: Vec<&str> = s.trim().split(',').collect(); + for list in lists.iter() { + let items: Vec<&str> = list.split('-').collect(); + if items.len() > 2 { + return Err(()); + } + + let start = items[0] + .parse::() + .map_err(|_| error!("Invalid value {}", items[0]))?; + integer_list.push(start); + if items.len() == 2 { + let end = items[1] + .parse::() + .map_err(|_| error!("Invalid value {}", items[1]))?; + if start >= end { + return Err(()); + } + + for i in start..end { + integer_list.push(i + 1); + } + } + } + + Ok(IntegerList(integer_list)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs new file mode 100644 index 000000000..35024fb3a --- /dev/null +++ b/machine_manager/src/config/numa.rs @@ -0,0 +1,307 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result}; +use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; +use std::collections::BTreeMap; + +const MIN_NUMA_DISTANCE: u8 = 10; + +#[derive(Default, Debug)] +pub struct NumaDistance { + pub destination: u32, + pub distance: u8, +} + +#[derive(Default, Debug)] +pub struct NumaConfig { + pub numa_id: u32, + pub cpus: Vec, + pub distances: Option>, + pub size: u64, + pub mem_dev: String, +} + +#[derive(Default)] +pub struct NumaNode { + pub cpus: Vec, + pub distances: BTreeMap, + pub size: u64, +} + +pub type NumaNodes = BTreeMap; + +pub fn check_numa_node(numa_nodes: &NumaNodes, vm_config: &mut VmConfig) -> Result<()> { + let mut total_ram_size = 0_u64; + let mut total_cpu_num = 0_u8; + let mut max_cpu_idx = 0_u8; + for node in numa_nodes.iter() { + total_ram_size += node.1.size; + total_cpu_num += node.1.cpus.len() as u8; + for idx in &node.1.cpus { + if max_cpu_idx <= *idx { + max_cpu_idx = *idx; + } + } + } + + if total_ram_size != vm_config.machine_config.mem_config.mem_size { + bail!( + "Total memory {} of NUMA nodes is not equals to memory size {}", + total_ram_size, + vm_config.machine_config.mem_config.mem_size, + ); + } + if total_cpu_num != vm_config.machine_config.nr_cpus { + bail!( + "Total cpu numbers {} of NUMA nodes is not equals to smp {}", + total_cpu_num, + vm_config.machine_config.nr_cpus, + ); + } + if max_cpu_idx != vm_config.machine_config.nr_cpus - 1 { + bail!("Error to configure CPU sets, please check you cmdline again"); + } + + Ok(()) +} + +pub fn parse_numa_mem(numa_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("numa"); + cmd_parser + .push("") + .push("nodeid") + .push("cpus") + .push("memdev"); + cmd_parser.parse(numa_config)?; + + let mut config: NumaConfig = NumaConfig::default(); + if let Some(node_id) = cmd_parser.get_value::("nodeid")? { + if node_id >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "nodeid".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + config.numa_id = node_id; + } else { + return Err(ErrorKind::FieldIsMissing("nodeid", "numa").into()); + } + if let Some(cpus) = cmd_parser + .get_value::("cpus") + .map_err(|_| ErrorKind::ConvertValueFailed(String::from("u64"), "cpus".to_string()))? + .map(|v| v.0.iter().map(|e| *e as u8).collect()) + { + config.cpus = cpus; + } else { + return Err(ErrorKind::FieldIsMissing("cpus", "numa").into()); + } + if let Some(mem_dev) = cmd_parser.get_value::("memdev")? { + config.mem_dev = mem_dev; + } else { + return Err(ErrorKind::FieldIsMissing("memdev", "numa").into()); + } + + Ok(config) +} + +pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { + let mut cmd_parser = CmdParser::new("numa"); + cmd_parser.push("").push("src").push("dst").push("val"); + cmd_parser.parse(numa_dist)?; + + let mut dist: NumaDistance = NumaDistance::default(); + let numa_id = if let Some(src) = cmd_parser.get_value::("src")? { + if src >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "src".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + src + } else { + return Err(ErrorKind::FieldIsMissing("src", "numa").into()); + }; + if let Some(dst) = cmd_parser.get_value::("dst")? { + if dst >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "dst".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + dist.destination = dst; + } else { + return Err(ErrorKind::FieldIsMissing("dst", "numa").into()); + } + if let Some(val) = cmd_parser.get_value::("val")? { + if val < MIN_NUMA_DISTANCE { + bail!("NUMA distance shouldn't be less than 10"); + } + dist.distance = val; + } else { + return Err(ErrorKind::FieldIsMissing("val", "numa").into()); + } + + if numa_id == dist.destination && dist.distance != MIN_NUMA_DISTANCE { + bail!("Local distance of node {} should be 10.", numa_id); + } + + Ok((numa_id, dist)) +} + +impl VmConfig { + pub fn add_numa(&mut self, numa_config: &str) -> Result<()> { + let mut cmd_params = CmdParser::new("numa"); + cmd_params.push(""); + + cmd_params.get_parameters(numa_config)?; + if let Some(numa_type) = cmd_params.get_value::("")? { + self.numa_nodes.push((numa_type, numa_config.to_string())); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_numa_mem() { + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_numa("-numa node,nodeid=0,cpus=0-1,memdev=mem0") + .is_ok()); + assert!(vm_config + .add_numa("-numa node,nodeid=1,cpus=2-1,memdev=mem1") + .is_ok()); + assert!(vm_config + .add_numa("-numa node,nodeid=2,memdev=mem2") + .is_ok()); + assert!(vm_config.add_numa("-numa node,nodeid=3,cpus=3-4").is_ok()); + + let numa = vm_config.numa_nodes.get(0).unwrap(); + let numa_config = parse_numa_mem(numa.1.as_str()).unwrap(); + assert_eq!(numa_config.cpus, vec![0, 1]); + assert_eq!(numa_config.mem_dev, "mem0"); + + let numa = vm_config.numa_nodes.get(1).unwrap(); + assert!(parse_numa_mem(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(2).unwrap(); + assert!(parse_numa_mem(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(3).unwrap(); + assert!(parse_numa_mem(numa.1.as_str()).is_err()); + } + + #[test] + fn test_parse_numa_distance() { + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,src=0,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,src=0,dst=1").is_ok()); + + let numa = vm_config.numa_nodes.get(0).unwrap(); + let dist = parse_numa_distance(numa.1.as_str()).unwrap(); + assert_eq!(dist.0, 0); + assert_eq!(dist.1.destination, 1); + assert_eq!(dist.1.distance, 10); + + let numa = vm_config.numa_nodes.get(1).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(2).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(3).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); + } + + #[test] + fn test_check_numa_nodes() { + let mut vm_config = VmConfig::default(); + vm_config.machine_config.nr_cpus = 4; + vm_config.machine_config.mem_config.mem_size = 2147483648; + + let numa_node1 = NumaNode { + cpus: vec![0, 1], + distances: Default::default(), + size: 1073741824, + }; + let numa_node2 = NumaNode { + cpus: vec![2, 3], + distances: Default::default(), + size: 1073741824, + }; + + let mut numa_nodes = BTreeMap::new(); + numa_nodes.insert(0, numa_node1); + numa_nodes.insert(1, numa_node2); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_ok()); + + let numa_node3 = NumaNode { + cpus: vec![2], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(2, numa_node3); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node4 = NumaNode { + cpus: vec![2, 3, 4], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node4); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node5 = NumaNode { + cpus: vec![3, 4], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node5); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node6 = NumaNode { + cpus: vec![0, 1], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node6); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node7 = NumaNode { + cpus: vec![2, 3], + distances: Default::default(), + size: 2147483648, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node7); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + } +} -- Gitee From eb1fb03170c0a4732c13582758a5154d70b3a223 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 12 Apr 2022 21:14:12 +0800 Subject: [PATCH 0037/2187] NUMA: export NUMA node to the guest on x86_64 architecture 1.Set the SRAT table to acpi, which is needed to describe NUMA nodes, and how memory ranges and CPUs are attached to them. 2.Set the SLIT table to acpi, it defines distance between NUMA nodes. Signed-off-by: Xinle.Guo --- acpi/src/acpi_table.rs | 62 +++++++++++ address_space/src/host_mmap.rs | 1 + machine/src/lib.rs | 76 +++++++++++++- machine/src/standard_vm/mod.rs | 85 +++++++++++++++- machine/src/standard_vm/x86_64/mod.rs | 141 +++++++++++++++++++++++++- 5 files changed, 356 insertions(+), 9 deletions(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 969f49480..ea999fcfc 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -226,6 +226,68 @@ impl AmlBuilder for AcpiRsdp { } } +/// ACPI SRAT processor affinity structure. +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct AcpiSratProcessorAffinity { + /// Type ID. + pub type_id: u8, + /// The length of this structure. + pub length: u8, + /// Bit [7:0] of the proximity domain to which the processor belongs. + pub proximity_lo: u8, + /// The processor local APIC ID. + pub local_apic_id: u8, + /// The processor affinity flags. + pub flags: u32, + /// The processor local SAPIC EID. + pub local_sapic_eid: u8, + /// Bit [31:8] of the proximity domain to which the processor belongs. + pub proximity_hi: [u8; 3], + /// The clock domain to which the processor belongs. + pub clock_domain: u32, +} + +impl ByteCode for AcpiSratProcessorAffinity {} + +impl AmlBuilder for AcpiSratProcessorAffinity { + fn aml_bytes(&self) -> Vec { + Vec::from(self.as_bytes()) + } +} + +/// ACPI SRAT memory affinity structure. +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct AcpiSratMemoryAffinity { + /// Type ID. + pub type_id: u8, + /// The length of this structure. + pub length: u8, + /// Represents the proximity domain to which the "range of memory" belongs. + pub proximity_domain: u32, + /// Reserved field. + pub reserved1: u16, + /// The base address of the memory range. + pub base_addr: u64, + /// The length of the memory range. + pub range_length: u64, + /// Reserved field. + pub reserved2: u32, + /// Indicates whether memory is enabled and can be hot plugged. + pub flags: u32, + /// Reserved field. + pub reserved3: u64, +} + +impl ByteCode for AcpiSratMemoryAffinity {} + +impl AmlBuilder for AcpiSratMemoryAffinity { + fn aml_bytes(&self) -> Vec { + Vec::from(self.as_bytes()) + } +} + /// This module describes ACPI MADT's sub-tables on x86_64 platform. #[cfg(target_arch = "x86_64")] pub mod madt_subtable { diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 1a6b0a839..0132c551e 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -490,6 +490,7 @@ mod test { dump_guest_core: false, mem_share: false, mem_prealloc: false, + mem_zones: None, }; let host_mmaps = create_host_mmaps(&addr_ranges, &mem_config, 1).unwrap(); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3633b5535..b5cb06900 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -111,6 +111,7 @@ use virtio::{ VirtioNetState, }; +use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; @@ -125,10 +126,11 @@ use devices::InterruptController; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::VcpuFd; use machine_manager::config::{ - get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, - parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, - parse_vsock, MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, - FAST_UNPLUG_ON, + check_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, + parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, + parse_virtconsole, parse_virtio_serial, parse_vsock, MachineMemConfig, NumaConfig, + NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, + VmConfig, FAST_UNPLUG_ON, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; @@ -683,6 +685,72 @@ pub trait MachineOps { Ok(()) } + /// Add numa nodes information to standard machine. + /// + /// # Arguments + /// + /// * `vm_config` - VM Configuration. + fn add_numa_nodes(&mut self, vm_config: &mut VmConfig) -> Result> { + if vm_config.numa_nodes.is_empty() { + return Ok(None); + } + + let mut numa_nodes: NumaNodes = BTreeMap::new(); + vm_config.numa_nodes.sort_by(|p, n| n.0.cmp(&p.0)); + for numa in vm_config.numa_nodes.iter() { + match numa.0.as_str() { + "node" => { + let numa_config: NumaConfig = parse_numa_mem(numa.1.as_str())?; + if numa_nodes.contains_key(&numa_config.numa_id) { + bail!("Numa node id is repeated {}", numa_config.numa_id); + } + let mut numa_node = NumaNode { + cpus: numa_config.cpus, + ..Default::default() + }; + + if let Some(object_cfg) = vm_config.object.remove(&numa_config.mem_dev) { + if let ObjConfig::Zone(zone_config) = object_cfg { + numa_node.size = zone_config.size; + } + } else { + bail!( + "Object for memory-backend-ram {} config not found", + numa_config.mem_dev + ); + } + numa_nodes.insert(numa_config.numa_id, numa_node); + } + "dist" => { + let dist: (u32, NumaDistance) = parse_numa_distance(numa.1.as_str())?; + if !numa_nodes.contains_key(&dist.0) { + bail!("Numa node id is not found {}", dist.0); + } + if !numa_nodes.contains_key(&dist.1.destination) { + bail!("Numa node id is not found {}", dist.1.destination); + } + + if let Some(n) = numa_nodes.get_mut(&dist.0) { + if n.distances.contains_key(&dist.1.destination) { + bail!( + "Numa destination info {} repeat settings", + dist.1.destination + ); + } + n.distances.insert(dist.1.destination, dist.1.distance); + } + } + _ => { + bail!("Unsupported args for NUMA node: {}", numa.0.as_str()); + } + } + } + + check_numa_node(&numa_nodes, vm_config)?; + + Ok(Some(numa_nodes)) + } + /// Add peripheral devices. /// /// # Arguments diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 897147930..3d72efeaf 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -76,7 +76,7 @@ use error_chain::ChainedError; use errors::{Result, ResultExt}; use machine_manager::config::{ get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, NetworkInterfaceConfig, - PciBdf, VmConfig, + NumaNode, NumaNodes, PciBdf, VmConfig, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -153,6 +153,17 @@ trait StdMachineOps: AcpiBuilder { .chain_err(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); + let srat_addr = self + .build_srat_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI SRAT table")?; + xsdt_entries.push(srat_addr); + + if let Some(numa_nodes) = self.get_numa_nodes() { + let slit_addr = Self::build_slit_table(numa_nodes, &acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI SLIT table")?; + xsdt_entries.push(slit_addr); + } + let xsdt_addr = Self::build_xsdt_table(&acpi_tables, &mut loader, xsdt_entries)?; let mut locked_fw_cfg = fw_cfg.lock().unwrap(); @@ -185,6 +196,8 @@ trait StdMachineOps: AcpiBuilder { fn get_vm_config(&self) -> &Mutex; + fn get_numa_nodes(&self) -> &Option; + /// Register event notifier for reset of standard machine. /// /// # Arguments @@ -558,6 +571,76 @@ trait AcpiBuilder { Ok(facs_begin as u64) } + /// Build ACPI SRAT CPU table. + /// # Arguments + /// + /// `proximity_domain` - The proximity domain. + /// `node` - The NUMA node. + /// `srat` - The SRAT table. + fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable); + + /// Build ACPI SRAT memory table. + /// # Arguments + /// + /// `base_addr` - The base address of the memory range. + /// `proximity_domain` - The proximity domain. + /// `node` - The NUMA node. + /// `srat` - The SRAT table. + fn build_srat_mem( + &self, + base_addr: u64, + proximity_domain: u32, + node: &NumaNode, + srat: &mut AcpiTable, + ) -> u64; + + /// Build ACPI SRAT table, returns the offset of ACPI SRAT table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + fn build_srat_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> Result; + + /// Build ACPI SLIT table, returns the offset of ACPI SLIT table in `acpi_data`. + /// + /// # Arguments + /// + /// `numa_nodes` - The information of NUMA nodes. + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + fn build_slit_table( + numa_nodes: &NumaNodes, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> Result { + let mut slit = AcpiTable::new(*b"SLIT", 1, *b"STRATO", *b"VIRTSLIT", 1); + slit.append_child((numa_nodes.len() as u64).as_bytes()); + + let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); + for (id, node) in numa_nodes.iter().enumerate() { + let distances = &node.1.distances; + for i in existing_nodes.iter() { + let dist: u8 = if id as u32 == *i { + 10 + } else if let Some(distance) = distances.get(i) { + *distance + } else { + 20 + }; + slit.append_child(dist.as_bytes()); + } + } + + let slit_begin = StdMachine::add_table_to_loader(acpi_data, loader, &slit) + .chain_err(|| "Fail to add SLIT table to loader")?; + Ok(slit_begin as u64) + } + /// Build ACPI XSDT table, returns the offset of ACPI XSDT table in `acpi_data`. /// /// # Arguments diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 1388c25f9..d3a0118de 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -21,9 +21,9 @@ use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use acpi::{ - AcpiIoApic, AcpiLocalApic, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, - AmlPackage, AmlScope, AmlScopeBuilder, AmlString, TableLoader, IOAPIC_BASE_ADDR, - LAPIC_BASE_ADDR, + AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, + AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, + AmlString, TableLoader, IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -32,7 +32,9 @@ use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SE use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; -use machine_manager::config::{BootSource, PFlashConfig, SerialConfig, VmConfig}; +use machine_manager::config::{ + BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, +}; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, @@ -57,6 +59,8 @@ use syscall::syscall_whitelist; use util::byte_code::ByteCode; const VENDOR_ID_INTEL: u16 = 0x8086; +const HOLE_640K_START: u64 = 0x000A_0000; +const HOLE_640K_END: u64 = 0x0010_0000; /// The type of memory layout entry on x86_64 #[repr(usize)] @@ -105,6 +109,8 @@ pub struct StdMachine { /// VM power button, handle VM `Shutdown` event. power_button: EventFd, vm_config: Mutex, + /// List of guest NUMA nodes information. + numa_nodes: Option, } impl StdMachine { @@ -145,6 +151,7 @@ impl StdMachine { power_button: EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?, vm_config: Mutex::new(vm_config.clone()), + numa_nodes: None, }) } @@ -309,6 +316,10 @@ impl StdMachineOps for StdMachine { fn get_vm_config(&self) -> &Mutex { &self.vm_config } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } } impl MachineOps for StdMachine { @@ -418,6 +429,7 @@ impl MachineOps for StdMachine { let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; + locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_io, @@ -566,6 +578,7 @@ impl AcpiBuilder for StdMachine { let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0007".to_string()))); dev.append_child(AmlNameDecl::new("_UID", AmlInteger(cpu_id))); + dev.append_child(AmlNameDecl::new("_PXM", AmlInteger(0))); sb_scope.append_child(dev); } @@ -626,6 +639,126 @@ impl AcpiBuilder for StdMachine { .chain_err(|| "Fail to add DSTD table to loader")?; Ok(madt_begin as u64) } + + fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { + for cpu in node.cpus.iter() { + srat.append_child( + &AcpiSratProcessorAffinity { + length: size_of::() as u8, + proximity_lo: proximity_domain as u8, + local_apic_id: *cpu, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + } + + fn build_srat_mem( + &self, + base_addr: u64, + proximity_domain: u32, + node: &NumaNode, + srat: &mut AcpiTable, + ) -> u64 { + let mem_below_4g = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + let mem_above_4g = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; + + let mut mem_base = base_addr; + let mut mem_len = node.size; + let mut next_base = mem_base + mem_len; + // It contains the hole from 604Kb to 1Mb + if mem_base <= HOLE_640K_START && next_base > HOLE_640K_START { + mem_len -= next_base - HOLE_640K_START; + if mem_len > 0 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr: mem_base, + range_length: mem_len, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + + if next_base <= HOLE_640K_END { + next_base = HOLE_640K_END; + return next_base; + } + mem_base = HOLE_640K_END; + mem_len = next_base - HOLE_640K_END; + } + + // It contains the hole possibly from mem_below_4g(2G) to mem_below_4g(4G). + if mem_base <= mem_below_4g && next_base > mem_below_4g { + mem_len -= next_base - mem_below_4g; + if mem_len > 0 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr: mem_base, + range_length: mem_len, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + mem_base = mem_above_4g; + mem_len = next_base - mem_below_4g; + next_base = mem_base + mem_len; + } + + if mem_len > 0 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr: mem_base, + range_length: mem_len, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + + next_base + } + + fn build_srat_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + if self.numa_nodes.is_none() { + return Ok(0); + } + + let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); + srat.append_child(&[1_u8; 4_usize]); + srat.append_child(&[0_u8; 8_usize]); + + let mut next_base = 0_u64; + for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { + self.build_srat_cpu(*id, node, &mut srat); + next_base = self.build_srat_mem(next_base, *id, node, &mut srat); + } + + let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) + .chain_err(|| "Fail to add SRAT table to loader")?; + Ok(srat_begin as u64) + } } impl MachineLifecycle for StdMachine { -- Gitee From c2c3abf58db80274364a4947c8fa7631d1d0abfc Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 1 Mar 2022 12:41:35 +0800 Subject: [PATCH 0038/2187] NUMA: introduces NUMA node feature for the AArch64 platform This commit append numa node memory and cpus affinity to the fdt When creating virtual machine. `no-acpi` mode will be introduced in the feture to open FDT NUMA mode. Signed-off-by: Xinle.Guo --- machine/src/standard_vm/aarch64/mod.rs | 76 ++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 48b773bf9..f302ea207 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -36,7 +36,7 @@ use devices::legacy::{ use devices::{InterruptController, InterruptControllerConfig}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; -use machine_manager::config::{BootSource, PFlashConfig, SerialConfig, VmConfig}; +use machine_manager::config::{BootSource, NumaNodes, PFlashConfig, SerialConfig, VmConfig}; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, @@ -125,6 +125,8 @@ pub struct StdMachine { reset_req: EventFd, /// Device Tree Blob. dtb_vec: Vec, + /// List of guest NUMA nodes information. + numa_nodes: Option, } impl StdMachine { @@ -163,6 +165,7 @@ impl StdMachine { reset_req: EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| ErrorKind::InitEventFdErr("reset_req".to_string()))?, dtb_vec: Vec::new(), + numa_nodes: None, }) } @@ -401,6 +404,7 @@ impl MachineOps for StdMachine { locked_vm .register_reset_event(&locked_vm.reset_req, clone_vm) .chain_err(|| "Fail to register reset event")?; + locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_mem, @@ -1039,6 +1043,8 @@ trait CompileFDTHelper { fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; /// Function that helps to generate the chosen node. fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + /// Function that helps to generate numa node distances. + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; } impl CompileFDTHelper for StdMachine { @@ -1123,14 +1129,31 @@ impl CompileFDTHelper for StdMachine { } fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { - let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.sys_mem.memory_end_address().raw_value() - - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let node = "memory"; - let memory_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; - fdt.end_node(memory_node_dep)?; + if self.numa_nodes.is_none() { + let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let mem_size = self.sys_mem.memory_end_address().raw_value() + - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let node = "memory"; + let memory_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.end_node(memory_node_dep)?; + + return Ok(()); + } + + // Set NUMA node information. + let mut mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + for (id, node) in self.numa_nodes.as_ref().unwrap().iter().enumerate() { + let mem_size = node.1.size; + let node = format!("memory@{:x}", mem_base); + let memory_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.set_property_u32("numa-node-id", id as u32)?; + fdt.end_node(memory_node_dep)?; + mem_base += mem_size; + } Ok(()) } @@ -1216,6 +1239,39 @@ impl CompileFDTHelper for StdMachine { Ok(()) } + + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + if self.numa_nodes.is_none() { + return Ok(()); + } + + let distance_node_dep = fdt.begin_node("distance-map")?; + fdt.set_property_string("compatible", "numa-distance-map-v1")?; + + let mut matrix = Vec::new(); + let numa_nodes = self.numa_nodes.as_ref().unwrap(); + let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); + for (id, node) in numa_nodes.iter().enumerate() { + let distances = &node.1.distances; + for i in existing_nodes.iter() { + matrix.push(id as u32); + matrix.push(*i as u32); + let dist: u32 = if id as u32 == *i { + 10 + } else if let Some(distance) = distances.get(i) { + *distance as u32 + } else { + 20 + }; + matrix.push(dist); + } + } + + fdt.set_property_array_u32("distance-matrix", matrix.as_ref())?; + fdt.end_node(distance_node_dep)?; + + Ok(()) + } } impl device_tree::CompileFDT for StdMachine { @@ -1232,7 +1288,7 @@ impl device_tree::CompileFDT for StdMachine { self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; - + self.generate_distance_node(fdt)?; fdt.end_node(node_dep)?; Ok(()) -- Gitee From 00c2d08551a662778d3acc8b28503477f5bb55d1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 8 Apr 2022 14:32:03 +0800 Subject: [PATCH 0039/2187] NUMA: enable NUMA node for AArch64 ACPI table The AArch64 platform provides NUMA node feature for ACPI table, Which means on AArch64 platform, the NUMA can be set in FTD and ACPI two ways. Signed-off-by: Xinle.Guo --- acpi/src/acpi_table.rs | 26 ++++++++ machine/src/standard_vm/aarch64/mod.rs | 90 +++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 9 deletions(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index ea999fcfc..308514fa1 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -256,6 +256,32 @@ impl AmlBuilder for AcpiSratProcessorAffinity { } } +/// ACPI SRAT GICC affinity structure. +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct AcpiSratGiccAffinity { + /// Type ID. + pub type_id: u8, + /// The length of this structure. + pub length: u8, + /// Represents the proximity domain to which the "range of memory" belongs. + pub proximity_domain: u32, + /// The ACPI processor UID of the associated GICC + pub process_uid: u32, + /// The GICC affinity flags. + pub flags: u32, + /// The clock domain to which the processor belongs. + pub clock_domain: u32, +} + +impl ByteCode for AcpiSratGiccAffinity {} + +impl AmlBuilder for AcpiSratGiccAffinity { + fn aml_bytes(&self) -> Vec { + Vec::from(self.as_bytes()) + } +} + /// ACPI SRAT memory affinity structure. #[repr(C, packed)] #[derive(Default, Copy, Clone)] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index f302ea207..974f3dacf 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,18 +14,19 @@ mod pci_host_root; mod syscall; use std::fs::OpenOptions; +use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use acpi::{ - AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiTable, AmlBuilder, - AmlDevice, AmlInteger, AmlNameDecl, AmlScope, AmlScopeBuilder, AmlString, TableLoader, - ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, - ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, ACPI_GTDT_CAP_ALWAYS_ON, - ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, - ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, - ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, - INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, + AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, + AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, + AmlScopeBuilder, AmlString, TableLoader, ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, + ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, + ACPI_GTDT_CAP_ALWAYS_ON, ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX, ACPI_MADT_GENERIC_CPU_INTERFACE, + ACPI_MADT_GENERIC_DISTRIBUTOR, ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, + ARCH_GIC_MAINT_IRQ, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -36,7 +37,9 @@ use devices::legacy::{ use devices::{InterruptController, InterruptControllerConfig}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; -use machine_manager::config::{BootSource, NumaNodes, PFlashConfig, SerialConfig, VmConfig}; +use machine_manager::config::{ + BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, +}; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, @@ -301,6 +304,10 @@ impl StdMachineOps for StdMachine { fn get_vm_config(&self) -> &Mutex { &self.vm_config } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } } impl MachineOps for StdMachine { @@ -732,6 +739,71 @@ impl AcpiBuilder for StdMachine { .chain_err(|| "Fail to add MADT table to loader")?; Ok(madt_begin as u64) } + + fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { + for cpu in node.cpus.iter() { + srat.append_child( + &AcpiSratGiccAffinity { + type_id: 3_u8, + length: size_of::() as u8, + proximity_domain, + process_uid: *cpu as u32, + flags: 1, + clock_domain: 0_u32, + } + .aml_bytes(), + ); + } + } + + fn build_srat_mem( + &self, + base_addr: u64, + proximity_domain: u32, + node: &NumaNode, + srat: &mut AcpiTable, + ) -> u64 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr, + range_length: node.size, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + base_addr + node.size + } + + fn build_srat_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + if self.numa_nodes.is_none() { + return Ok(0); + } + + let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); + // Reserved + srat.append_child(&[1_u8; 4_usize]); + // Reserved + srat.append_child(&[0_u8; 8_usize]); + + let mut next_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { + self.build_srat_cpu(*id, node, &mut srat); + next_base = self.build_srat_mem(next_base, *id, node, &mut srat); + } + + let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) + .chain_err(|| "Fail to add SRAT table to loader")?; + Ok(srat_begin as u64) + } } impl MachineLifecycle for StdMachine { -- Gitee From 18af5b3061afd0c9dff285b430be556e75889e97 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 12 Apr 2022 16:54:57 +0800 Subject: [PATCH 0040/2187] NUMA: apply NUMA bind policy to memory Add NUMA bind policy to specify which host NUMA node should be used to be defined as backend of the current memory zone. --- address_space/src/host_mmap.rs | 54 ++++++++++++++++++++++++++++++++-- address_space/src/lib.rs | 2 +- machine/src/lib.rs | 6 +++- util/src/lib.rs | 1 + util/src/syscall.rs | 54 ++++++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 util/src/syscall.rs diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 0132c551e..e9e773958 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -16,13 +16,20 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; use std::thread; -use machine_manager::config::MachineMemConfig; +use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; use crate::errors::{Result, ResultExt}; use crate::{AddressRange, GuestAddress}; -use util::unix::{do_mmap, host_page_size}; +use util::{ + syscall::mbind, + unix::{do_mmap, host_page_size}, +}; const MAX_PREALLOC_THREAD: u8 = 16; +/// Verify existing pages in the mapping. +const MPOL_MF_STRICT: u32 = 1; +/// Move pages owned by this process to conform to mapping. +const MPOL_MF_MOVE: u32 = 2; /// FileBackend represents backend-file of `HostMemMapping`. #[derive(Clone)] @@ -278,6 +285,49 @@ pub fn create_host_mmaps( Ok(mappings) } +/// Set host memory backend numa policy. +/// +/// # Arguments +/// +/// * `mem_mappings` - The host virtual address of mapped memory information. +/// * `mem_zones` - Memory zone config. +pub fn set_host_memory_policy( + mem_mappings: &[Arc], + mem_zones: &Option>, +) -> Result<()> { + if mem_zones.is_none() || mem_mappings.is_empty() { + return Ok(()); + } + + let mut host_addr_start = mem_mappings.get(0).map(|m| m.host_address()).unwrap(); + for zone in mem_zones.as_ref().unwrap() { + let node_id = if let Some(id) = zone.host_numa_node { + id as usize + } else { + 0_usize + }; + + let mut nmask = vec![0_u64; node_id / 64 + 1]; + nmask[node_id / 64] |= 1_u64 << (node_id % 64); + let policy = HostMemPolicy::from(zone.policy.clone()); + if policy == HostMemPolicy::Default { + bail!("Failed to set default host policy"); + } + mbind( + host_addr_start, + zone.size, + policy as u32, + nmask, + node_id as u64 + 1, + MPOL_MF_STRICT | MPOL_MF_MOVE, + ) + .chain_err(|| "Failed to call mbind")?; + host_addr_start += zone.size; + } + + Ok(()) +} + /// Record information of memory mapping. pub struct HostMemMapping { /// Record the range of one memory segment. diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 5b337e215..2f432e092 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -93,7 +93,7 @@ mod state; pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; -pub use host_mmap::{create_host_mmaps, FileBackend, HostMemMapping}; +pub use host_mmap::{create_host_mmaps, set_host_memory_policy, FileBackend, HostMemMapping}; #[cfg(target_arch = "x86_64")] pub use listener::KvmIoListener; pub use listener::KvmMemoryListener; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index b5cb06900..9dc3f951e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -118,7 +118,9 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; -use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; +use address_space::{ + create_host_mmaps, set_host_memory_policy, AddressSpace, KvmMemoryListener, Region, +}; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] @@ -184,6 +186,8 @@ pub trait MachineOps { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) .chain_err(|| "Failed to mmap guest ram.")?; + set_host_memory_policy(&mem_mappings, &mem_config.mem_zones) + .chain_err(|| "Failed to set host memory NUMA policy.")?; } sys_mem diff --git a/util/src/lib.rs b/util/src/lib.rs index 2e2deae96..504bdeddc 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -38,6 +38,7 @@ pub mod unix; pub mod logger; #[macro_use] pub mod offsetof; +pub mod syscall; pub mod trace; pub mod errors { diff --git a/util/src/syscall.rs b/util/src/syscall.rs new file mode 100644 index 000000000..d6eb30814 --- /dev/null +++ b/util/src/syscall.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use libc::{c_void, syscall, SYS_mbind}; + +use super::errors::Result; + +/// This function set memory policy for host NUMA node memory range. +/// +/// * Arguments +/// +/// * `addr` - The memory range starting with addr. +/// * `len` - Length of the memory range. +/// * `mode` - Memory policy mode. +/// * `node_mask` - node_mask specifies physical node ID. +/// * `max_node` - The max node. +/// * `flags` - Mode flags. +pub fn mbind( + addr: u64, + len: u64, + mode: u32, + node_mask: Vec, + max_node: u64, + flags: u32, +) -> Result<()> { + let res = unsafe { + syscall( + SYS_mbind, + addr as *mut c_void, + len, + mode, + node_mask.as_ptr(), + max_node + 1, + flags, + ) + }; + if res < 0 { + bail!( + "Failed to apply host numa node policy, error is {}", + std::io::Error::last_os_error() + ); + } + + Ok(()) +} -- Gitee From bd29776eb604bd5f24fc74281ff0bd8ed03cd929 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Mon, 16 May 2022 22:21:51 +0800 Subject: [PATCH 0041/2187] remove macro-use-extern-crate code the use of extern crate is being phased out in the 2018 edition. To bring macros from extern crates into scope, it is recommended to use a use import. Signed-off-by: Yan Wen --- acpi/src/acpi_device.rs | 1 + acpi/src/lib.rs | 7 ++----- acpi/src/table_loader.rs | 1 + address_space/src/host_mmap.rs | 8 ++++--- address_space/src/lib.rs | 9 ++------ address_space/src/listener.rs | 5 +++-- address_space/src/region.rs | 3 +++ address_space/src/state.rs | 2 ++ boot_loader/src/aarch64/mod.rs | 1 + boot_loader/src/lib.rs | 7 ++----- boot_loader/src/x86_64/direct_boot/mod.rs | 6 ++++-- boot_loader/src/x86_64/mod.rs | 1 + boot_loader/src/x86_64/standard_boot/elf.rs | 1 + boot_loader/src/x86_64/standard_boot/mod.rs | 2 ++ cpu/src/aarch64/core_regs.rs | 5 +++-- cpu/src/aarch64/mod.rs | 8 ++++--- cpu/src/lib.rs | 15 ++++--------- cpu/src/x86_64/mod.rs | 6 ++++-- .../src/interrupt_controller/aarch64/gicv3.rs | 5 +++-- .../src/interrupt_controller/aarch64/state.rs | 5 +++-- devices/src/interrupt_controller/mod.rs | 2 ++ devices/src/legacy/chardev.rs | 2 ++ devices/src/legacy/fwcfg.rs | 3 ++- devices/src/legacy/mod.rs | 2 ++ devices/src/legacy/pflash.rs | 3 ++- devices/src/legacy/pl011.rs | 2 ++ devices/src/legacy/pl031.rs | 2 ++ devices/src/legacy/rtc.rs | 1 + devices/src/legacy/serial.rs | 3 +++ devices/src/lib.rs | 7 ------- hypervisor/src/kvm/interrupt.rs | 1 + hypervisor/src/kvm/mod.rs | 5 ++++- hypervisor/src/kvm/state.rs | 1 + hypervisor/src/lib.rs | 12 ++--------- machine/src/lib.rs | 13 +++--------- machine/src/micro_vm/mod.rs | 10 +++++++-- machine/src/standard_vm/aarch64/mod.rs | 4 +++- .../src/standard_vm/aarch64/pci_host_root.rs | 1 + machine/src/standard_vm/mod.rs | 5 ++++- machine/src/standard_vm/x86_64/ich9_lpc.rs | 3 ++- machine/src/standard_vm/x86_64/mch.rs | 3 ++- machine/src/standard_vm/x86_64/mod.rs | 12 ++++++----- machine_manager/src/config/balloon.rs | 1 + machine_manager/src/config/chardev.rs | 1 + machine_manager/src/config/drive.rs | 2 ++ machine_manager/src/config/machine_config.rs | 1 + machine_manager/src/config/mod.rs | 4 ++++ machine_manager/src/config/network.rs | 1 + machine_manager/src/config/numa.rs | 5 ++++- machine_manager/src/config/pci.rs | 2 ++ machine_manager/src/config/rng.rs | 2 ++ machine_manager/src/event_loop.rs | 4 +++- machine_manager/src/lib.rs | 7 ++----- machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/signal_handler.rs | 1 - machine_manager/src/socket.rs | 2 ++ migration/src/device_state.rs | 2 ++ migration/src/lib.rs | 10 ++------- migration/src/manager.rs | 1 + migration/src/snapshot.rs | 2 ++ migration_derive/src/lib.rs | 5 +---- ozone/src/capability.rs | 1 + ozone/src/cgroup.rs | 1 + ozone/src/handler.rs | 1 + ozone/src/main.rs | 7 +++---- pci/src/bus.rs | 2 ++ pci/src/hotplug.rs | 1 + pci/src/lib.rs | 18 ++++++---------- pci/src/msix.rs | 3 +++ pci/src/root_port.rs | 8 ++++--- src/main.rs | 7 ++----- sysbus/src/lib.rs | 8 +++---- util/src/aio/libaio.rs | 3 ++- util/src/aio/raw.rs | 1 + util/src/bitmap.rs | 1 + util/src/leak_bucket.rs | 1 + util/src/lib.rs | 21 +++++++------------ util/src/loop_context.rs | 3 +-- util/src/num_ops.rs | 3 ++- util/src/seccomp.rs | 2 ++ util/src/syscall.rs | 1 + util/src/tap.rs | 2 ++ util/src/trace.rs | 1 + util/src/unix.rs | 2 ++ vfio/src/lib.rs | 10 +++------ vfio/src/vfio_dev.rs | 2 ++ vfio/src/vfio_pci.rs | 5 +++-- virtio/src/balloon.rs | 6 ++++-- virtio/src/block.rs | 4 +++- virtio/src/console.rs | 4 +++- virtio/src/lib.rs | 14 +++---------- virtio/src/net.rs | 3 +++ virtio/src/queue.rs | 2 ++ virtio/src/rng.rs | 3 +++ virtio/src/vhost/kernel/mod.rs | 6 +++++- virtio/src/vhost/kernel/net.rs | 2 ++ virtio/src/vhost/kernel/vsock.rs | 3 +++ virtio/src/vhost/user/client.rs | 2 ++ virtio/src/vhost/user/net.rs | 1 + virtio/src/vhost/user/sock.rs | 1 + virtio/src/virtio_mmio.rs | 3 +++ virtio/src/virtio_pci.rs | 4 +++- 102 files changed, 243 insertions(+), 179 deletions(-) diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index 3c68a6ac0..b907aa675 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -14,6 +14,7 @@ use std::time::Instant; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; +use log::error; // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index b691ed9fd..82688bb81 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -10,11 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; - mod acpi_device; #[allow(dead_code)] pub mod acpi_table; @@ -37,6 +32,8 @@ pub const ACPI_TABLE_LOADER_FILE: &str = "etc/table-loader"; pub const ACPI_RSDP_FILE: &str = "etc/acpi/rsdp"; pub mod errors { + use error_chain::error_chain; + error_chain! { errors { FileEntryExist(name: String) { diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 6bb2bba55..3703750e0 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex}; +use error_chain::bail; use util::byte_code::ByteCode; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index e9e773958..4f9da2cdc 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -16,15 +16,17 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; use std::thread; +use error_chain::bail; +use log::{error, info}; use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; - -use crate::errors::{Result, ResultExt}; -use crate::{AddressRange, GuestAddress}; use util::{ syscall::mbind, unix::{do_mmap, host_page_size}, }; +use crate::errors::{Result, ResultExt}; +use crate::{AddressRange, GuestAddress}; + const MAX_PREALLOC_THREAD: u8 = 16; /// Verify existing pages in the mapping. const MPOL_MF_STRICT: u32 = 1; diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 2f432e092..1077a445e 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -77,13 +77,6 @@ //! } //! ``` -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate migration_derive; - mod address; mod address_space; mod host_mmap; @@ -101,6 +94,8 @@ pub use listener::{Listener, ListenerReqType}; pub use region::{FlatRange, Region, RegionIoEventFd, RegionType}; pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index aa07b215a..b9865adec 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -13,14 +13,15 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::kvm_userspace_memory_region; use kvm_ioctls::{IoEventAddress, NoDatamatch}; +use log::{debug, warn}; +use util::{num_ops::round_down, unix::host_page_size}; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, FlatRange, RegionIoEventFd, RegionType}; -use util::{num_ops::round_down, unix::host_page_size}; const MEM_READ_ONLY: u32 = 1 << 1; diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 7c9e8da91..f90a88dc2 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -13,6 +13,9 @@ use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; +use error_chain::bail; +use log::{debug, warn}; + use crate::address_space::FlatView; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, AddressSpace, FileBackend, GuestAddress, HostMemMapping, RegionOps}; diff --git a/address_space/src/state.rs b/address_space/src/state.rs index eb34e91ce..901dc4457 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -16,8 +16,10 @@ use std::mem::size_of; use std::sync::Arc; use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; + use migration::errors::{ErrorKind, Result, ResultExt}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::unix::host_page_size; diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index c9608cb26..1b5b3c4b0 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -17,6 +17,7 @@ use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{errors::ErrorKind as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; +use log::info; use util::byte_code::ByteCode; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index 9a3426442..66ed338bd 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -73,11 +73,6 @@ //! } //! ``` -#[macro_use] -extern crate log; -#[macro_use] -extern crate error_chain; - #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] mod aarch64; @@ -99,6 +94,8 @@ pub use x86_64::X86BootLoader as BootLoader; pub use x86_64::X86BootLoaderConfig as BootLoaderConfig; pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { Io(std::io::Error); diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index da0c12383..1435f7235 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -18,8 +18,12 @@ use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; +use error_chain::bail; +use log::info; use util::byte_code::ByteCode; +use self::gdt::setup_gdt; +use self::mptable::setup_isa_mptable; use super::bootparam::{BootParams, RealModeKernelHeader, UNDEFINED_ID}; use super::{X86BootLoader, X86BootLoaderConfig}; use super::{ @@ -27,8 +31,6 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::errors::{ErrorKind, Result, ResultExt}; -use gdt::setup_gdt; -use mptable::setup_isa_mptable; /// Load bzImage linux kernel to Guest Memory. /// diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs index 0c22ced55..4cbe22c4f 100644 --- a/boot_loader/src/x86_64/mod.rs +++ b/boot_loader/src/x86_64/mod.rs @@ -62,6 +62,7 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use devices::legacy::FwCfgOps; +use error_chain::bail; use kvm_bindings::kvm_segment; use crate::errors::Result; diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index a76e78920..17b0406e0 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{FwCfgEntryType, FwCfgOps}; +use error_chain::bail; use util::byte_code::ByteCode; use util::num_ops::round_up; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 49e8a0302..705da226a 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -19,6 +19,8 @@ use std::sync::Arc; use address_space::AddressSpace; use devices::legacy::{FwCfgEntryType, FwCfgOps}; +use error_chain::bail; +use log::{error, info}; use util::byte_code::ByteCode; use self::elf::load_elf_kernel; diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index bc46f69a9..4f0726a32 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -20,6 +20,7 @@ use kvm_bindings::{ }; use kvm_ioctls::VcpuFd; use util::byte_code::ByteCode; +use util::offset_of; use vmm_sys_util::{ errno, ioctl::{ioctl_with_mut_ref, ioctl_with_ref}, @@ -248,13 +249,13 @@ pub fn set_core_regs(vcpu_fd: &VcpuFd, core_regs: kvm_regs) -> Result<()> { set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpsr.into(), - &core_regs.fp_regs.fpsr.as_bytes().to_vec(), + core_regs.fp_regs.fpsr.as_bytes(), )?; set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpcr.into(), - &core_regs.fp_regs.fpcr.as_bytes().to_vec(), + core_regs.fp_regs.fpcr.as_bytes(), )?; Ok(()) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 1c427e7f6..de6163e30 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -23,12 +23,14 @@ use kvm_bindings::{ use kvm_ioctls::VcpuFd; use vmm_sys_util::fam::FamStructWrapper; +pub use self::caps::ArmCPUCaps; +use self::caps::CpregListEntry; +use self::core_regs::{get_core_regs, set_core_regs}; use crate::errors::{Result, ResultExt}; use crate::CPU; -pub use caps::ArmCPUCaps; -use caps::CpregListEntry; -use core_regs::{get_core_regs, set_core_regs}; + use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; type CpregList = FamStructWrapper; diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 64a1fc5ec..37a4ee7da 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -27,17 +27,6 @@ //! - `x86_64` //! - `aarch64` -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate machine_manager; -#[cfg(target_arch = "aarch64")] -#[macro_use] -extern crate util; -#[macro_use] -extern crate migration_derive; #[cfg(target_arch = "aarch64")] #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] @@ -46,6 +35,8 @@ mod aarch64; mod x86_64; pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { Signal(vmm_sys_util::errno::Error); @@ -120,6 +111,8 @@ use std::time::Duration; use kvm_ioctls::{VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; +use log::{error, info, warn}; +use machine_manager::event; use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; use vmm_sys_util::signal::{register_signal_handler, Killable}; diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 37e0f8478..f376a0835 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -15,18 +15,20 @@ mod cpuid; use std::sync::{Arc, Mutex}; +use error_chain::bail; use kvm_bindings::{ kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; use self::cpuid::host_cpuid; use crate::errors::{Result, ResultExt}; use crate::CPU; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use util::byte_code::ByteCode; const ECX_EPB_SHIFT: u32 = 3; const X86_FEATURE_HYPERVISOR: u32 = 31; diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 79ebb27b3..c8f07fb03 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -12,14 +12,15 @@ use std::sync::{Arc, Mutex}; -use kvm_ioctls::DeviceFd; - use super::{ state::{GICv3ItsState, GICv3State}, GICConfig, GICDevice, UtilResult, }; use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; + use hypervisor::kvm::KVM_FDS; +use kvm_ioctls::DeviceFd; +use log::error; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use migration::{MigrationManager, MigrationRestoreOrder}; use util::device_tree::{self, FdtBuilder}; diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index d4da00bd6..2f54c1b6f 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -12,12 +12,13 @@ use std::mem::size_of; -use libc::c_uint; - use super::gicv3::{GICv3, GICv3Access, GICv3Its}; use super::GIC_IRQ_INTERNAL; use crate::interrupt_controller::errors::Result; + +use libc::c_uint; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; /// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index e17d5c479..773dfaf8e 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -34,6 +34,8 @@ pub use aarch64::GICConfig as InterruptControllerConfig; pub use aarch64::InterruptController; pub mod errors { + use error_chain::error_chain; + error_chain! { errors { #[cfg(target_arch = "aarch64")] diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 1f7b4a159..f8a8e685b 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -17,7 +17,9 @@ use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use error_chain::bail; use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; +use log::{error, info}; use machine_manager::machine::{PathInfo, PTY_PATH}; use machine_manager::{ config::{ChardevConfig, ChardevType}, diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index df613e43d..ae3f6ac75 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -23,7 +23,8 @@ use address_space::{AddressSpace, GuestAddress}; #[cfg(target_arch = "x86_64")] use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 6835a9214..bd4d9474e 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -26,6 +26,8 @@ //! - `aarch64` pub mod errors { + use error_chain::error_chain; + error_chain! { links { SysBus(sysbus::errors::Error, sysbus::errors::ErrorKind); diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 36f708dba..6b12749c4 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -17,7 +17,8 @@ use std::sync::{Arc, Mutex}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{debug, error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::num_ops::{deposit_u32, extract_u32}; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 34a5114ef..84c31bf31 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -19,11 +19,13 @@ use acpi::{ }; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; +use log::{debug, error}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index e7f671506..897c71919 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -16,7 +16,9 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use acpi::AmlBuilder; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; +use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index a549bfb03..c87d7036c 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -17,6 +17,7 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; +use log::{debug, error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 11e3c1b2e..cc2abd679 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -19,11 +19,14 @@ use acpi::{ AmlResourceUsage, AmlScopeBuilder, }; use address_space::GuestAddress; +use error_chain::bail; use hypervisor::kvm::KVM_FDS; +use log::error; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 09423adbd..2a7318645 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -16,13 +16,6 @@ //! - interrupt controller (aarch64) //! - legacy devices, such as serial devices -#[macro_use] -extern crate log; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate migration_derive; - mod interrupt_controller; pub mod legacy; diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 8c27cc0a9..33596a1db 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -17,6 +17,7 @@ use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; use util::bitmap::Bitmap; use vmm_sys_util::ioctl::ioctl_with_val; +use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; use crate::errors::{Result, ResultExt}; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 80012bbfd..d88e9f88c 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -22,9 +22,12 @@ use arc_swap::ArcSwap; use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; +use log::error; use once_cell::sync::Lazy; - use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{ + ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, +}; use crate::errors::{Result, ResultExt}; #[cfg(target_arch = "x86_64")] diff --git a/hypervisor/src/kvm/state.rs b/hypervisor/src/kvm/state.rs index f38ad064b..ec61cb23c 100644 --- a/hypervisor/src/kvm/state.rs +++ b/hypervisor/src/kvm/state.rs @@ -13,6 +13,7 @@ use kvm_bindings::{ kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, }; +use migration_derive::{ByteCode, Desc}; use super::KVM_FDS; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 9fb446d61..b9acc6904 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -12,18 +12,10 @@ //! This crate offers interfaces for different kinds of hypervisors, such as KVM. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate vmm_sys_util; -#[cfg(target_arch = "x86_64")] -#[macro_use] -extern crate migration_derive; - #[allow(clippy::upper_case_acronyms)] pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9dc3f951e..0bc2c9d5c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,17 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate machine_manager; -#[cfg(target_arch = "x86_64")] -#[macro_use] -extern crate vmm_sys_util; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -125,6 +117,7 @@ use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; +use error_chain::bail; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::VcpuFd; use machine_manager::config::{ diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index fc8595501..c189c0d65 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -29,6 +29,8 @@ //! - `aarch64` pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); @@ -104,11 +106,15 @@ use virtio::{ }; use vmm_sys_util::eventfd::EventFd; +use self::errors::{ErrorKind, Result}; use super::{ errors::{ErrorKind as MachineErrorKind, Result as MachineResult}, MachineOps, }; -use errors::{ErrorKind, Result}; + +use error_chain::bail; +use log::error; +use machine_manager::event; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use syscall::syscall_whitelist; @@ -921,7 +927,7 @@ impl DeviceInterface for LightMachine { current: true, qom_path: String::from("/machine/unattached/device[") + &cpu_index.to_string() - + &"]".to_string(), + + "]", halted: false, props: Some(cpu_instance), CPU: cpu_index as isize, diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 974f3dacf..90c65ce74 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -35,11 +35,13 @@ use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; use devices::{InterruptController, InterruptControllerConfig}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; +use log::error; use machine_manager::config::{ BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; +use machine_manager::event; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 36789f1de..1b5b5d439 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex, Weak}; +use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 3d72efeaf..6e7a9e7a0 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -18,6 +18,7 @@ mod x86_64; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; +use log::error; use machine_manager::event_loop::EventLoop; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; @@ -27,6 +28,8 @@ pub use x86_64::StdMachine; #[allow(clippy::upper_case_acronyms)] pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -72,7 +75,7 @@ use acpi::{ }; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use errors::{Result, ResultExt}; use machine_manager::config::{ get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, NetworkInterfaceConfig, diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 24a8770d3..d6e2eb303 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -18,6 +18,8 @@ use std::sync::{ use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use error_chain::ChainedError; +use log::{debug, error}; +use pci::config::CLASS_CODE_ISA_BRIDGE; use pci::config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, @@ -29,7 +31,6 @@ use vmm_sys_util::eventfd::EventFd; use super::VENDOR_ID_INTEL; use crate::standard_vm::errors::Result; -use pci::config::CLASS_CODE_ISA_BRIDGE; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 2c93b28a4..05f87da36 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -13,7 +13,8 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{Region, RegionOps}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index d3a0118de..6066e7e82 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -29,34 +29,36 @@ use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; +use log::error; use machine_manager::config::{ BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; +use machine_manager::event; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; +use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; use sysbus::{SysBus, SysBusDevOps}; +use syscall::syscall_whitelist; +use util::byte_code::ByteCode; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; use self::ich9_lpc::SLEEP_CTRL_OFFSET; - use super::errors::{ErrorKind, Result}; use super::{AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; use crate::MachineOps; -use mch::Mch; -use syscall::syscall_whitelist; -use util::byte_code::ByteCode; const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index ec19445f9..7a7d9376a 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 1567ed855..e525f5c5e 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 0386dc4e4..f592e598e 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -14,6 +14,8 @@ use std::fs::metadata; use std::os::linux::fs::MetadataExt; use std::path::Path; +use error_chain::bail; +use log::error; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 5f5f8676e..7a7fe4289 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -12,6 +12,7 @@ use std::str::FromStr; +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 49d9832f1..554b42b33 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -11,6 +11,8 @@ // See the Mulan PSL v2 for more details. pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); @@ -107,6 +109,8 @@ use std::any::Any; use std::collections::HashMap; use std::str::FromStr; +use error_chain::bail; +use log::error; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::trace::enable_trace_events; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 820a5e56c..fa32d368b 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 35024fb3a..c97938817 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -10,9 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::BTreeMap; + +use error_chain::bail; + use super::errors::{ErrorKind, Result}; use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; -use std::collections::BTreeMap; const MIN_NUMA_DISTANCE: u8 = 10; diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 9b6409280..9c6db3c48 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; + use super::errors::{ErrorKind, Result, ResultExt}; use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::ExBool; diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 349bf31cf..e1caff629 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; + use super::errors::{ErrorKind, Result}; use super::{pci_args_check, ObjConfig}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 72ef64f7f..bc4e177e7 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -14,10 +14,12 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::{process, thread}; +use super::config::IothreadConfig; use crate::machine::IOTHREADS; use crate::qmp::qmp_schema::IothreadInfo; -use super::config::IothreadConfig; +use error_chain::bail; +use log::info; use util::loop_context::{EventLoopContext, EventLoopManager, EventNotifier}; /// This struct used to manage all events occur during VM lifetime. diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index a544c609d..f76598a08 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -21,11 +21,6 @@ //! 2. The API interface over VM inside and outside. //! 3. Configuration for VM and its devices. -#[macro_use] -extern crate log; -#[macro_use] -extern crate error_chain; - pub mod cmdline; pub mod config; pub mod event_loop; @@ -36,6 +31,8 @@ pub mod socket; pub mod temp_cleaner; pub mod errors { + use error_chain::error_chain; + error_chain! { links { ConfigParser(crate::config::errors::Error, crate::config::errors::ErrorKind); diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 12e493ab9..4694e8d6a 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -37,6 +37,7 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; +use log::{info, warn}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 0cd4524f5..138a576ed 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -9,7 +9,6 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -extern crate vmm_sys_util; use crate::temp_cleaner::TempCleaner; use std::io::Write; diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 594ef64a3..aa31717f6 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -16,6 +16,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::sync::{Arc, Mutex, RwLock}; +use error_chain::bail; +use log::{error, info}; use util::leak_bucket::LeakBucket; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use vmm_sys_util::epoll::EventSet; diff --git a/migration/src/device_state.rs b/migration/src/device_state.rs index de9c16cf8..e3f64d61c 100644 --- a/migration/src/device_state.rs +++ b/migration/src/device_state.rs @@ -12,6 +12,7 @@ use std::cmp::Ordering; +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::Result; @@ -169,6 +170,7 @@ impl DeviceStateDesc { #[cfg(test)] pub mod tests { use super::{DeviceStateDesc, FieldDesc, StateTransfer, VersionCheck}; + use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; #[derive(Default)] diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 9ccab5f3f..491a8d01d 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -14,14 +14,6 @@ //! //! Offer snapshot and migration interface for VM. -#[macro_use] -extern crate error_chain; -#[cfg(test)] -#[macro_use] -extern crate migration_derive; -#[macro_use] -extern crate log; - mod device_state; mod header; mod manager; @@ -33,6 +25,8 @@ pub use manager::{MigrationHook, MigrationManager, MigrationRestoreOrder}; pub use status::MigrationStatus; pub mod errors { + use error_chain::error_chain; + use super::status::MigrationStatus; error_chain! { diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 1ea194e26..2ca89a258 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use log::{debug, info}; use std::cmp; use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index 30ee13c75..35e7c0e40 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -16,6 +16,8 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::path::PathBuf; +use error_chain::bail; +use log::info; use util::byte_code::ByteCode; use util::reader::BufferReader; use util::unix::host_page_size; diff --git a/migration_derive/src/lib.rs b/migration_derive/src/lib.rs index a71ab54f0..a219f1ec3 100644 --- a/migration_derive/src/lib.rs +++ b/migration_derive/src/lib.rs @@ -48,12 +48,9 @@ //! 2. The `ByteCode` derive to auto add `ByteCode` trait and its relying trait for //! struct, such as `Default`, `Sync`, `Send`. -#[macro_use] -extern crate syn; - use proc_macro::TokenStream; use quote::quote; -use syn::DeriveInput; +use syn::{parse_macro_input, DeriveInput}; mod attr_parser; mod field_parser; diff --git a/ozone/src/capability.rs b/ozone/src/capability.rs index e7eae704f..356748d47 100644 --- a/ozone/src/capability.rs +++ b/ozone/src/capability.rs @@ -13,6 +13,7 @@ //! Remove all capability for ozone when uid is 0, use -capability cap_* to add capability. use crate::{syscall, ErrorKind, Result, ResultExt}; +use error_chain::bail; use std::{collections::HashMap, io::Write}; const CAPS_V3: u32 = 0x20080522; diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index 8ce65394a..e06cf6e00 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::{ collections::HashMap, fs::{self, OpenOptions}, diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 4b27dd20f..4e37441bf 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -21,6 +21,7 @@ use std::{ process::Stdio, }; +use error_chain::bail; use util::arg_parser::ArgMatches; const BASE_OZONE_PATH: &str = "/srv/ozone"; diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 4cb998480..27507878b 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -10,11 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; +use error_chain::{error_chain, quick_main}; -use args::create_args_parser; -use handler::OzoneHandler; +use crate::args::create_args_parser; +use crate::handler::OzoneHandler; mod args; mod capability; diff --git a/pci/src/bus.rs b/pci/src/bus.rs index e88e9c2de..107239f01 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -14,6 +14,8 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use address_space::Region; +use error_chain::bail; +use log::debug; use super::config::{SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM}; use super::hotplug::HotplugOps; diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index 9edd0fa29..5b80f40bf 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::sync::{Arc, Mutex}; use crate::{errors::Result, PciBus, PciDevOps}; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 87eb16373..51eca4be8 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -10,16 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate migration_derive; -#[macro_use] -extern crate machine_manager; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -63,9 +56,10 @@ use std::{ }; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; -use config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; -use errors::Result; +use crate::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use crate::errors::Result; const BDF_FUNC_SHIFT: u8 = 3; @@ -220,7 +214,7 @@ pub trait PciDevOps: Send { /// * `parent_bus` - Parent bus of pci devices. pub fn init_multifunction( multifunction: bool, - config: &mut Vec, + config: &mut [u8], devfn: u8, parent_bus: Weak>, ) -> Result<()> { diff --git a/pci/src/msix.rs b/pci/src/msix.rs index cbf27ea1a..c3daa418c 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -14,8 +14,11 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{GuestAddress, Region, RegionOps}; +use error_chain::bail; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; use crate::config::{CapId, PciConfig, RegionType, SECONDARY_BUS_NUM}; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 3dc97dd4c..cff4fe960 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -13,12 +13,14 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use once_cell::sync::OnceCell; - use address_space::Region; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{error, info}; +use machine_manager::event; use machine_manager::qmp::{qmp_schema as schema, QmpChannel}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use once_cell::sync::OnceCell; use util::byte_code::ByteCode; use super::config::{ diff --git a/src/main.rs b/src/main.rs index 1b974ee82..d312ce165 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,15 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; - use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; +use error_chain::{bail, error_chain, quick_main}; +use log::{error, info}; use machine::{LightMachine, MachineOps, StdMachine}; use machine_manager::{ cmdline::{check_api_channel, create_args_parser, create_vmconfig}, diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 9b0999012..3dd0a4b52 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -10,10 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -29,10 +28,11 @@ use std::sync::{Arc, Mutex}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use error_chain::bail; use hypervisor::kvm::KVM_FDS; use vmm_sys_util::eventfd::EventFd; -use errors::{Result, ResultExt}; +use crate::errors::{Result, ResultExt}; pub struct SysBus { #[cfg(target_arch = "x86_64")] diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index eaac9059e..2e77901c6 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::Result; +use error_chain::bail; use kvm_bindings::__IncompleteArrayField; pub const IOCB_FLAG_RESFD: u32 = 1; @@ -104,7 +105,7 @@ impl LibaioContext { Ok(LibaioContext { ctx, max_size }) } - pub fn submit(&self, nr: i64, iocbp: &mut Vec<*mut IoCb>) -> Result<()> { + pub fn submit(&self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; if ret < 0 { bail!("Failed to submit aio, return {}.", ret); diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 27f6f0392..98422f9b0 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::Result; +use error_chain::bail; use libc::{c_void, fdatasync, pread, pwrite}; use std::os::unix::io::RawFd; diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 8b0336887..2b6ea5e75 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::cmp::Ord; use std::mem::size_of; diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index d67d750a6..313e10997 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -14,6 +14,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::time::Instant; +use log::error; use vmm_sys_util::eventfd::EventFd; use crate::loop_context::EventLoopContext; diff --git a/util/src/lib.rs b/util/src/lib.rs index 504bdeddc..abbd7a211 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -10,14 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -extern crate libc; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate vmm_sys_util; -#[macro_use] -extern crate log; - pub mod aio; pub mod arg_parser; pub mod bitmap; @@ -28,20 +20,20 @@ pub mod daemonize; pub mod device_tree; pub mod leak_bucket; mod link_list; +pub mod logger; pub mod loop_context; pub mod num_ops; +pub mod offsetof; pub mod reader; pub mod seccomp; -pub mod tap; -pub mod unix; -#[macro_use] -pub mod logger; -#[macro_use] -pub mod offsetof; pub mod syscall; +pub mod tap; pub mod trace; +pub mod unix; pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { KvmIoctl(kvm_ioctls::Error); @@ -157,6 +149,7 @@ pub mod errors { } use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; +use log::debug; use once_cell::sync::Lazy; use std::sync::Mutex; use vmm_sys_util::terminal::Terminal; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 2c9c8c0af..ce3aa5db4 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -10,14 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -extern crate vmm_sys_util; - use std::collections::BTreeMap; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; use libc::{c_void, read}; +use log::{error, warn}; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index fd902eeed..2f09db691 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -10,7 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -//! This module implements some operations of Rust primitive types. +use log::error; +// This module implements some operations of Rust primitive types. /// Calculate the aligned-up u64 value. /// diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 40ab17eb9..2f1f0d19e 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -77,6 +77,8 @@ //! ``` //! This programe will be trapped. +use error_chain::bail; + use crate::errors::Result; use crate::offset_of; diff --git a/util/src/syscall.rs b/util/src/syscall.rs index d6eb30814..6294e09f9 100644 --- a/util/src/syscall.rs +++ b/util/src/syscall.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use libc::{c_void, syscall, SYS_mbind}; use super::errors::Result; diff --git a/util/src/tap.rs b/util/src/tap.rs index 4154d522b..5bed417a7 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -10,11 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; +use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; use super::errors::{Result, ResultExt}; diff --git a/util/src/trace.rs b/util/src/trace.rs index 9a267d039..3f6748505 100644 --- a/util/src/trace.rs +++ b/util/src/trace.rs @@ -5,6 +5,7 @@ use std::ops::Deref; use std::sync::Arc; use arc_swap::ArcSwap; +use log::error; use once_cell::sync::Lazy; use crate::errors::{Result, ResultExt}; diff --git a/util/src/unix.rs b/util/src/unix.rs index c7d177282..7d2815296 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -16,10 +16,12 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; +use error_chain::bail; use libc::{ c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, CMSG_LEN, CMSG_SPACE, MSG_NOSIGNAL, MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, }; +use log::error; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 9787513ce..4ee431dbc 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -10,14 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate vmm_sys_util; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { PciErr(pci::errors::Error, pci::errors::ErrorKind); @@ -53,6 +48,7 @@ use std::sync::{Arc, Mutex}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO}; use kvm_ioctls::DeviceFd; +use log::error; use once_cell::sync::Lazy; use vfio_dev::VfioGroup; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index db0c58df6..908d8a5f4 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -21,6 +21,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; use kvm_bindings::{ kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, }; @@ -28,6 +29,7 @@ use vfio_bindings::bindings::vfio; use vmm_sys_util::ioctl::{ ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, }; +use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index abc8f6c44..6680df29a 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -18,8 +18,9 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; #[cfg(target_arch = "aarch64")] use pci::config::SECONDARY_BUS_NUM; use pci::config::{ @@ -306,7 +307,7 @@ impl VfioPciDevice { Ok(vfio_bars) } - fn fixup_msix_region(&self, vfio_bars: &mut Vec) -> Result<()> { + fn fixup_msix_region(&self, vfio_bars: &mut [VfioBar]) -> Result<()> { let msix_info = self .msix_info .as_ref() diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index a2aa067c2..9379d4a39 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -22,9 +22,11 @@ use std::{ use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{error, warn}; use machine_manager::{ - config::BalloonConfig, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, qmp::QmpChannel, + config::BalloonConfig, event, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, + qmp::QmpChannel, }; use util::{ bitmap::Bitmap, diff --git a/virtio/src/block.rs b/virtio/src/block.rs index d65fd06c7..15c054d51 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,12 +21,14 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::error; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::aio::{Aio, AioCb, AioCompleteFunc, IoCmd, Iovec}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; diff --git a/virtio/src/console.rs b/virtio/src/console.rs index f758a84e9..b48a60b79 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -17,12 +17,14 @@ use std::{cmp, usize}; use address_space::AddressSpace; use devices::legacy::{Chardev, InputReceiver}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{debug, error, warn}; use machine_manager::{ config::{ChardevType, VirtioConsole}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index cd06c832a..5af20f3f0 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -25,18 +25,9 @@ //! - `x86_64` //! - `aarch64` -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate machine_manager; -#[macro_use] -extern crate vmm_sys_util; -#[macro_use] -extern crate migration_derive; - pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { Io(std::io::Error); @@ -131,6 +122,7 @@ pub use virtio_pci::VirtioPciDevice; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use error_chain::bail; use machine_manager::config::ConfigCheck; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index cbd843453..172528f93 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -18,11 +18,14 @@ use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; use address_space::AddressSpace; +use error_chain::bail; +use log::{error, warn}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 13409a875..0e5779c36 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -17,6 +17,8 @@ use std::sync::atomic::{fence, Ordering}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; +use error_chain::bail; +use log::{error, warn}; use util::byte_code::ByteCode; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index ff87fc2d5..4b3770cc2 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -27,6 +27,9 @@ use util::loop_context::{ }; use util::num_ops::{read_u32, write_u32}; +use error_chain::bail; +use log::{error, warn}; +use migration_derive::{ByteCode, Desc}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index ccc85e957..39a7c2f23 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -24,6 +24,8 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; +use error_chain::ChainedError; +use log::{debug, error}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -31,11 +33,13 @@ use util::loop_context::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; +use vmm_sys_util::{ + ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, +}; use super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; use super::{VhostNotify, VhostOps}; -use crate::error_chain::ChainedError; /// Refer to VHOST_VIRTIO in /// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index ec5ee9f68..d1ceb7fac 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -17,6 +17,8 @@ use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use error_chain::bail; +use log::warn; use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 1489a97f3..f2dbeca8c 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -15,8 +15,11 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; +use log::warn; use machine_manager::{config::VsockConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index ea7a6223f..e76f0865f 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -17,6 +17,8 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; +use error_chain::bail; +use log::{info, warn}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 2e28dd1c6..9875af550 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -15,6 +15,7 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use log::warn; use machine_manager::config::NetworkInterfaceConfig; use util::byte_code::ByteCode; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index 422e1f28a..9cbab183a 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -13,6 +13,7 @@ use std::mem::size_of; use std::os::unix::io::RawFd; +use error_chain::bail; use libc::{c_void, iovec}; use util::unix::UnixSock; diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index d85e889d6..6db9687bf 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -15,9 +15,12 @@ use std::sync::{Arc, Mutex}; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; +use log::{error, warn}; #[cfg(target_arch = "x86_64")] use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 3ffbd7b65..98c956945 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -16,9 +16,11 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use pci::config::{ RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, -- Gitee From d24bf97876edd9c71ba88d7ebc5a209567131fd7 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 10:07:53 +0800 Subject: [PATCH 0042/2187] config/drive: add bootindex for BlkDevConfig To enable the firmware to determine the boot sequence of bootable devices, the bootindex attribute needs to be added to the blk device. The related DeviceAddArgument in qmp also need add bootindex attribute. Signed-off-by: Jiajie Li --- machine/src/micro_vm/mod.rs | 1 + machine/src/standard_vm/mod.rs | 1 + machine_manager/src/config/drive.rs | 15 +++- machine_manager/src/qmp/qmp_schema.rs | 121 +++++++++++++------------- 4 files changed, 75 insertions(+), 63 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c189c0d65..5f314c291 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1120,6 +1120,7 @@ impl DeviceInterface for LightMachine { iothread: None, iops: None, queues: 1, + boot_index: None, }; match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6e7a9e7a0..7aace2751 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -764,6 +764,7 @@ impl StdMachine { iothread: args.iothread.clone(), iops: conf.iops, queues: args.queues.unwrap_or(1), + boot_index: args.boot_index, }; dev.check()?; dev diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index f592e598e..9850ba293 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -41,6 +41,14 @@ pub struct BlkDevConfig { pub iothread: Option, pub iops: Option, pub queues: u16, + pub boot_index: Option, +} + +#[derive(Debug, Clone)] +pub struct BootIndexInfo { + pub boot_index: u8, + pub id: String, + pub dev_path: String, } impl Default for BlkDevConfig { @@ -54,6 +62,7 @@ impl Default for BlkDevConfig { iothread: None, iops: None, queues: 1, + boot_index: None, } } } @@ -252,11 +261,11 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result("bootindex") { - bail!("Failed to parse \'bootindex\': {:?}", &e); + let mut blkdevcfg = BlkDevConfig::default(); + if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { + blkdevcfg.boot_index = Some(boot_index); } - let mut blkdevcfg = BlkDevConfig::default(); let blkdrive = if let Some(drive) = cmd_parser.get_value::("drive")? { drive } else { diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 17dc7e540..95f8b9eaa 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -484,6 +484,7 @@ pub struct device_add { pub host: Option, #[serde(rename = "num-queues")] pub queues: Option, + pub boot_index: Option, } pub type DeviceAddArgument = device_add; @@ -1744,7 +1745,7 @@ mod tests { fn test_qmp_unexpected_arguments() { // qmp: quit. let json_msg = r#" - { + { "execute": "quit" } "#; @@ -1757,8 +1758,8 @@ mod tests { // unexpected arguments for quit. let json_msg = r#" - { - "execute": "quit" , + { + "execute": "quit" , "arguments": "isdf" } "#; @@ -1771,7 +1772,7 @@ mod tests { // qmp: stop. let json_msg = r#" - { + { "execute": "stop" } "#; @@ -1784,8 +1785,8 @@ mod tests { // unexpected arguments for stop. let json_msg = r#" - { - "execute": "stop" , + { + "execute": "stop" , "arguments": "isdf" } "#; @@ -1798,7 +1799,7 @@ mod tests { // qmp: cont. let json_msg = r#" - { + { "execute": "cont" } "#; @@ -1811,8 +1812,8 @@ mod tests { // unexpected arguments for count. let json_msg = r#" - { - "execute": "cont" , + { + "execute": "cont" , "arguments": "isdf" } "#; @@ -1825,7 +1826,7 @@ mod tests { // qmp: query-hotpluggable-cpus. let json_msg = r#" - { + { "execute": "query-hotpluggable-cpus" } "#; @@ -1838,8 +1839,8 @@ mod tests { // unexpected arguments for query-hotpluggable-cpus. let json_msg = r#" - { - "execute": "query-hotpluggable-cpus" , + { + "execute": "query-hotpluggable-cpus" , "arguments": "isdf" } "#; @@ -1852,7 +1853,7 @@ mod tests { // qmp: query-cpus. let json_msg = r#" - { + { "execute": "query-cpus" } "#; @@ -1865,8 +1866,8 @@ mod tests { // unexpected arguments for query-cpus. let json_msg = r#" - { - "execute": "query-cpus" , + { + "execute": "query-cpus" , "arguments": "isdf" } "#; @@ -1879,7 +1880,7 @@ mod tests { // qmp: query-ststus. let json_msg = r#" - { + { "execute": "query-status" } "#; @@ -1892,8 +1893,8 @@ mod tests { // unexpected arguments for query-status. let json_msg = r#" - { - "execute": "query-status" , + { + "execute": "query-status" , "arguments": "isdf" } "#; @@ -1909,11 +1910,11 @@ mod tests { fn test_wrong_qmp_arguments() { // right arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driver":"virtio-net-mmio", + "id":"net-0", + "driver":"virtio-net-mmio", "addr":"0x0" } } @@ -1927,11 +1928,11 @@ mod tests { // unknow arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driver":"virtio-net-mmio", + "id":"net-0", + "driver":"virtio-net-mmio", "addr":"0x0", "UnknowArg": "should go to error" } @@ -1946,11 +1947,11 @@ mod tests { // wrong spelling arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driv":"virtio-net-mmio", + "id":"net-0", + "driv":"virtio-net-mmio", "addr":"0x0" } } @@ -1964,8 +1965,8 @@ mod tests { // right arguments for device_del. let json_msg = r#" - { - "execute": "device_del" , + { + "execute": "device_del" , "arguments": { "id": "net-1" } @@ -1980,8 +1981,8 @@ mod tests { // wrong arguments for device_del. let json_msg = r#" - { - "execute": "device_del" , + { + "execute": "device_del" , "arguments": { "value": "h8i" } @@ -1998,7 +1999,7 @@ mod tests { // missing arguments for getfd. let json_msg = r#" - { + { "execute": "getfd" } "#; @@ -2011,8 +2012,8 @@ mod tests { // unexpected arguments for getfd. let json_msg = r#" - { - "execute": "getfd" , + { + "execute": "getfd" , "arguments": "isdf" } "#; @@ -2025,10 +2026,10 @@ mod tests { // right arguments for getfd. let json_msg = r#" - { + { "execute": "getfd", - "arguments": { - "fdname": "fd1" + "arguments": { + "fdname": "fd1" } } "#; @@ -2041,17 +2042,17 @@ mod tests { // right arguments for blockdev-add. let json_msg = r#" - { + { "execute": "blockdev-add", "arguments": { - "node-name": "drive-0", + "node-name": "drive-0", "file": { - "driver": "file", + "driver": "file", "filename": "/path/to/block" - }, + }, "cache": { "direct": true - }, + }, "read-only": false } } @@ -2065,11 +2066,11 @@ mod tests { // right arguments for device-add. let json_msg = r#" - { + { "execute": "device_add", "arguments": { - "id": "drive-0", - "driver": "virtio-blk-mmio", + "id": "drive-0", + "driver": "virtio-blk-mmio", "addr": "0x1" } } @@ -2083,10 +2084,10 @@ mod tests { // right arguments for netdev-add. let json_msg = r#" - { + { "execute": "netdev_add", "arguments": { - "id": "net-0", + "id": "net-0", "ifname":"tap0" } } @@ -2103,8 +2104,8 @@ mod tests { fn test_unsupported_commands() { // unsupported qmp command. let json_msg = r#" - { - "execute": "hello-world" , + { + "execute": "hello-world" , } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2116,8 +2117,8 @@ mod tests { // unsupported qmp command, and unknow field. let json_msg = r#" - { - "execute": "hello-world" , + { + "execute": "hello-world" , "arguments": { "msg": "hello", } @@ -2135,8 +2136,8 @@ mod tests { fn test_qmp_commands() { // query-version let json_msg = r#" - { - "execute": "query-version" + { + "execute": "query-version" } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2148,8 +2149,8 @@ mod tests { // query-target let json_msg = r#" - { - "execute": "query-target" + { + "execute": "query-target" } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2161,8 +2162,8 @@ mod tests { // query-commands let json_msg = r#" - { - "execute": "query-commands" + { + "execute": "query-commands" } "#; let err_msg = match serde_json::from_str::(json_msg) { -- Gitee From cb315c6cc140887e045485ac97a727ebae9961e6 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 19:14:38 +0800 Subject: [PATCH 0043/2187] legacy/fwcfg: add modify_file_callback interface The boot sequence information of bootable devices in the firmware is stored in the file entry named `boot order`. This entry may need to be updated. So add the corresponding interface function to FwCfgOps. Signed-off-by: Jiajie Li --- devices/src/legacy/fwcfg.rs | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index ae3f6ac75..00730c343 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -506,6 +506,42 @@ impl FwCfgCommon { Ok(()) } + fn modify_file_callback( + &mut self, + filename: &str, + data: Vec, + select_cb: Option, + write_cb: Option, + allow_write: bool, + ) -> Result<()> { + if self.files.len() >= self.file_slots as usize { + return Err(ErrorKind::FileSlotsNotAvailable(filename.to_owned()).into()); + } + + let file_name_bytes = filename.to_string().as_bytes().to_vec(); + // Make sure file entry is existed. + let index = self + .files + .iter() + .position(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) + .ok_or_else(|| ErrorKind::EntryNotFound(filename.to_owned()))?; + self.files[index].size = data.len() as u32; + + // Update FileDir entry + let mut bytes = Vec::new(); + let file_length_be = BigEndian::read_u32((self.files.len() as u32).as_bytes()); + bytes.append(&mut file_length_be.as_bytes().to_vec()); + for value in self.files.iter() { + bytes.append(&mut value.as_be_bytes()); + } + self.update_entry_data(FwCfgEntryType::FileDir as u16, bytes)?; + + // Update File entry + self.entries[FW_CFG_FILE_FIRST as usize + index] = + FwCfgEntry::new(data, select_cb, write_cb, allow_write); + Ok(()) + } + // Fetch FwCfgDma request from guest and handle it fn handle_dma_request(&mut self) -> Result<()> { let dma_addr = self.dma_addr; @@ -1195,6 +1231,17 @@ pub trait FwCfgOps { self.fw_cfg_common() .add_file_callback(filename, data, None, None, true) } + + /// Modify a file entry to FwCfg device, without callbacks, write-allow. + /// + /// # Arguments + /// + /// * `filename` - Name of the file + /// * `data` - Raw data bytes of the file to be modified + fn modify_file_entry(&mut self, filename: &str, data: Vec) -> Result<()> { + self.fw_cfg_common() + .modify_file_callback(filename, data, None, None, true) + } } #[cfg(target_arch = "aarch64")] -- Gitee From 21f61ae0911ed4c1e8f0c4eb74dd8bce6fd793ee Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Fri, 21 Jan 2022 09:18:13 +0800 Subject: [PATCH 0044/2187] legacy/fwcfg: add UT test for modify_file_callback function Add UT test for `modify_file_callback` function. There are two situations in this. 1. successful modify. 2. try to modify a not exist file entry, which will fail. Signed-off-by: Jiajie Li --- devices/src/legacy/fwcfg.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 00730c343..a77f3047f 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1424,6 +1424,38 @@ mod test { let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); + let file_data = Vec::new(); + assert_eq!( + fwcfg_common + .add_file_callback("bootorder", file_data.clone(), None, None, false) + .is_ok(), + true + ); + let id = fwcfg_common.files[0].select; + fwcfg_common.select_entry(id); + let entry = fwcfg_common.get_entry_mut().unwrap(); + assert_eq!(entry.data, file_data); + + let modify_file_data = "/pci@ffffffffffffffff/pci-bridge@3/scsi@0,1/disk@0,0" + .as_bytes() + .to_vec(); + assert_eq!( + fwcfg_common + .modify_file_callback("bootorder", modify_file_data.clone(), None, None, true) + .is_ok(), + true + ); + let id = fwcfg_common.files[0].select; + fwcfg_common.select_entry(id); + let entry = fwcfg_common.get_entry_mut().unwrap(); + assert_eq!(entry.data, modify_file_data); + + assert_eq!( + fwcfg_common + .modify_file_callback("abc", modify_file_data.clone(), None, None, true) + .is_err(), + true + ); let file_data = vec![0x33; 500]; assert_eq!( fwcfg_common -- Gitee From b1a69e3fb0841e6649ba330c2c8112227e4a8b92 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 19:20:31 +0800 Subject: [PATCH 0045/2187] pci: add get_dev_path for bootindex The boot sequence information of bootable devices in the firmware includes the priority and device path. The format of the device path is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names" (a leading slash is expected and not returned): /driver-name@unit-address[:device-arguments][] Signed-off-by: Jiajie Li --- pci/src/lib.rs | 47 +++++++++++++++++++++++++++++++++++++++- pci/src/root_port.rs | 7 ++++++ virtio/src/virtio_pci.rs | 15 +++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 51eca4be8..ddb8a395e 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -52,7 +52,7 @@ pub use root_port::RootPort; use std::{ mem::size_of, - sync::{Mutex, Weak}, + sync::{Arc, Mutex, Weak}, }; use byteorder::{ByteOrder, LittleEndian}; @@ -202,6 +202,51 @@ pub trait PciDevOps: Send { fn devfn(&self) -> Option { None } + + /// Get the path of the PCI bus where the device resides. + fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { + let locked_parent_bus = parent_bus.lock().unwrap(); + let parent_dev_path = if locked_parent_bus.name.eq("pcie.0") { + String::from("/pci@ffffffffffffffff") + } else { + // This else branch will not be executed currently, + // which is mainly to be compatible with new PCI bridge devices. + // unwrap is safe because pci bus under root port will not return null. + locked_parent_bus + .parent_bridge + .as_ref() + .unwrap() + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_dev_path() + .unwrap() + }; + parent_dev_path + } + + /// Fill the device patch accroding to parent device patch and device function. + fn populate_dev_path(&self, parent_dev_path: String, devfn: u8, dev_type: &str) -> String { + let mut dev_path = parent_dev_path; + dev_path.push_str(dev_type); + + let slot = pci_slot(devfn); + dev_path.push_str(&slot.to_string()); + + let function = pci_func(devfn); + if function != 0 { + dev_path.push(','); + dev_path.push_str(&function.to_string()); + } + + dev_path + } + + /// Get firmware device path. + fn get_dev_path(&self) -> Option { + None + } } /// Init multifunction for pci devices. diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index cff4fe960..2e506fc7d 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -427,6 +427,13 @@ impl PciDevOps for RootPort { Ok(()) } } + + fn get_dev_path(&self) -> Option { + let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_dev_path = self.get_parent_dev_path(parent_bus); + let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/pci-bridge@"); + Some(dev_path) + } } impl HotplugOps for RootPort { diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 98c956945..6e4f314e9 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1123,6 +1123,21 @@ impl PciDevOps for VirtioPciDevice { .reset() .chain_err(|| "Fail to reset virtio device") } + + fn get_dev_path(&self) -> Option { + let parent_bus = self.parent_bus.upgrade().unwrap(); + match self.device.lock().unwrap().device_type() { + VIRTIO_TYPE_BLOCK => { + // The virtio blk device is identified as a single-channel SCSI device, + // so add scsi controler indetification without channel, scsi-id and lun. + let parent_dev_path = self.get_parent_dev_path(parent_bus); + let mut dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); + dev_path.push_str("/disk@0,0"); + Some(dev_path) + } + _ => None, + } + } } impl StateTransfer for VirtioPciDevice { -- Gitee From d85605987c0552cb2a00160f7c600e69f0471b46 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 10:37:39 +0800 Subject: [PATCH 0046/2187] machine: add necessary attribute and interface for bootindex Add the attribute for saving the bootindex information of the device to the `StdMachine` struct and save the FwCfg device. And the interface for obtaining the sysbus and bootindex information struct is provided. Signed-off-by: Jiajie Li --- machine/src/lib.rs | 17 ++++++++++++++--- machine/src/micro_vm/mod.rs | 4 ++++ machine/src/standard_vm/aarch64/mod.rs | 24 ++++++++++++++++++++++-- machine/src/standard_vm/x86_64/mod.rs | 22 +++++++++++++++++++++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 0bc2c9d5c..4ddf75f63 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -98,6 +98,7 @@ mod standard_vm; pub use micro_vm::LightMachine; use pci::{PciBus, PciDevOps, PciHost, RootPort}; pub use standard_vm::StdMachine; +use sysbus::SysBus; use virtio::{ BlockState, RngState, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioState, VirtioNetState, @@ -123,9 +124,9 @@ use kvm_ioctls::VcpuFd; use machine_manager::config::{ check_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, - parse_virtconsole, parse_virtio_serial, parse_vsock, MachineMemConfig, NumaConfig, - NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, - VmConfig, FAST_UNPLUG_ON, + parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, MachineMemConfig, + NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, + VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; @@ -469,6 +470,16 @@ pub trait MachineOps { bail!("No pci host found"); } + fn get_sys_bus(&mut self) -> &SysBus; + + fn get_fwcfg_dev(&mut self) -> Result>> { + bail!("No FwCfg deivce found"); + } + + fn get_boot_order_list(&self) -> Option>>> { + None + } + fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 5f314c291..8f146be1e 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -607,6 +607,10 @@ impl MachineOps for LightMachine { &self.sys_mem } + fn get_sys_bus(&mut self) -> &SysBus { + &self.sysbus + } + #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { use crate::errors::ResultExt; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 90c65ce74..f2c0d6c8e 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -39,7 +39,7 @@ use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use log::error; use machine_manager::config::{ - BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, + BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -49,7 +49,7 @@ use machine_manager::machine::{ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -132,6 +132,10 @@ pub struct StdMachine { dtb_vec: Vec, /// List of guest NUMA nodes information. numa_nodes: Option, + /// List contains the boot order of boot devices. + boot_order_list: Arc>>, + /// FwCfg device. + fwcfg_dev: Option>>, } impl StdMachine { @@ -171,6 +175,8 @@ impl StdMachine { .chain_err(|| ErrorKind::InitEventFdErr("reset_req".to_string()))?, dtb_vec: Vec::new(), numa_nodes: None, + boot_order_list: Arc::new(Mutex::new(Vec::new())), + fwcfg_dev: None, }) } @@ -287,6 +293,7 @@ impl StdMachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, ) .chain_err(|| "Failed to realize fwcfg device")?; + self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) } @@ -534,6 +541,19 @@ impl MachineOps for StdMachine { fn get_pci_host(&mut self) -> StdResult<&Arc>> { Ok(&self.pci_host) } + + fn get_sys_bus(&mut self) -> &SysBus { + &self.sysbus + } + + fn get_fwcfg_dev(&mut self) -> Result>> { + // Unwrap is safe. Because after standard machine realize, this will not be None.F + Ok(self.fwcfg_dev.clone().unwrap()) + } + + fn get_boot_order_list(&self) -> Option>>> { + Some(self.boot_order_list.clone()) + } } impl AcpiBuilder for StdMachine { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 6066e7e82..c2b2218b9 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -34,7 +34,7 @@ use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use log::error; use machine_manager::config::{ - BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, + BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -113,6 +113,10 @@ pub struct StdMachine { vm_config: Mutex, /// List of guest NUMA nodes information. numa_nodes: Option, + /// List contains the boot order of boot devices. + boot_order_list: Arc>>, + /// FwCfg device. + fwcfg_dev: Option>>, } impl StdMachine { @@ -154,6 +158,8 @@ impl StdMachine { .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?, vm_config: Mutex::new(vm_config.clone()), numa_nodes: None, + boot_order_list: Arc::new(Mutex::new(Vec::new())), + fwcfg_dev: None, }) } @@ -299,6 +305,7 @@ impl StdMachineOps for StdMachine { let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) .chain_err(|| "Failed to realize fwcfg device")?; + self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) } @@ -562,6 +569,19 @@ impl MachineOps for StdMachine { fn get_pci_host(&mut self) -> Result<&Arc>> { Ok(&self.pci_host) } + + fn get_sys_bus(&mut self) -> &SysBus { + &self.sysbus + } + + fn get_fwcfg_dev(&mut self) -> MachineResult>> { + // Unwrap is safe. Because after standard machine realize, this will not be None.F + Ok(self.fwcfg_dev.clone().unwrap()) + } + + fn get_boot_order_list(&self) -> Option>>> { + Some(self.boot_order_list.clone()) + } } impl AcpiBuilder for StdMachine { -- Gitee From 6b377a4b8af2f8ad7580a53e7da7abda66ae9841 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 20:03:40 +0800 Subject: [PATCH 0047/2187] machine/src: add reset_fwcfg_boot_order interface When the realize function of StdMachine is called, we add boot index information to fwcfg file entry named `bootorder`. This function sorts the bootindex structure item based on the bootindex and invokes the interface provided by FwCfgOps to update the file entry information. Signed-off-by: Jiajie Li --- machine/src/lib.rs | 67 ++++++++++++++++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 4 ++ machine/src/standard_vm/x86_64/mod.rs | 4 ++ 3 files changed, 75 insertions(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4ddf75f63..54db7851f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -494,6 +494,73 @@ pub trait MachineOps { Ok(()) } + fn reset_fwcfg_boot_order(&mut self) -> Result<()> { + let boot_order_vec = self.get_boot_order_list().unwrap(); + let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone(); + locked_boot_order_vec.sort_by(|x, y| x.boot_index.cmp(&y.boot_index)); + let mut fwcfg_boot_order_string = String::new(); + for item in &locked_boot_order_vec { + fwcfg_boot_order_string.push_str(&item.dev_path); + fwcfg_boot_order_string.push('\n'); + } + fwcfg_boot_order_string.push('\0'); + + let fwcfg = self.get_fwcfg_dev()?; + fwcfg + .lock() + .unwrap() + .modify_file_entry("bootorder", fwcfg_boot_order_string.as_bytes().to_vec()) + .chain_err(|| "Fail to add bootorder entry for standard VM.")?; + Ok(()) + } + + /// Add boot index of device. + /// + /// # Arguments + /// + /// * `bootindex` - The boot index of the device. + /// * `dev_path` - The firmware device path of the device. + /// * `dev_id` - The id of the device. + fn add_bootindex_devices( + &mut self, + boot_index: u8, + dev_path: &str, + dev_id: &str, + ) -> Result<()> { + // Unwrap is safe because StdMachine will overwrite this function, + // which ensure boot_order_list is not None. + let boot_order_list = self.get_boot_order_list().unwrap(); + if boot_order_list + .lock() + .unwrap() + .iter() + .any(|item| item.boot_index == boot_index) + { + bail!("Failed to add duplicated bootindex {}.", boot_index); + } + + boot_order_list.lock().unwrap().push(BootIndexInfo { + boot_index, + id: dev_id.to_string(), + dev_path: dev_path.to_string(), + }); + + Ok(()) + } + + /// Delete boot index of device. + /// + /// # Arguments + /// + /// * `dev_id` - The id of the device. + fn del_bootindex_devices(&self, dev_id: &str) { + // Unwrap is safe because StdMachine will overwrite this function, + // which ensure boot_order_list is not None. + let boot_order_list = self.get_boot_order_list().unwrap(); + let mut locked_boot_order_list = boot_order_list.lock().unwrap(); + locked_boot_order_list.retain(|item| item.id != dev_id); + } + fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index f2c0d6c8e..6c202a9f7 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -459,6 +459,10 @@ impl MachineOps for StdMachine { (None, None) }; + locked_vm + .reset_fwcfg_boot_order() + .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index c2b2218b9..5e38d85f7 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -485,6 +485,10 @@ impl MachineOps for StdMachine { StdMachine::arch_init()?; locked_vm.register_power_event(&locked_vm.power_button)?; + locked_vm + .reset_fwcfg_boot_order() + .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } -- Gitee From 019946aea6c11b2e10dfd3dad45818da6dd16778 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 8 Feb 2022 10:43:20 +0800 Subject: [PATCH 0048/2187] machine/src: update bootindex information during hot plug and unplug When a VM does hot plug and unplug, the maintained bootindex information struct needs to be updated accordingly to ensure that the firmware boot sequence is correct when the VM reboot. Signed-off-by: Jiajie Li --- machine/src/lib.rs | 15 ++++++++++++--- machine/src/standard_vm/mod.rs | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 54db7851f..609f0bfe4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -566,7 +566,15 @@ pub trait MachineOps { let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_blk(vm_config, cfg_args)?; let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); - self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false)?; + let pci_dev = self + .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) + .chain_err(|| "Failed to add virtio pci device")?; + if let Some(bootindex) = device_cfg.boot_index { + if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { + self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id) + .chain_err(|| "Fail to add boot index")?; + } + } MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), device, @@ -681,7 +689,7 @@ pub trait MachineOps { device: Arc>, multi_func: bool, need_irqfd: bool, - ) -> Result<()> { + ) -> Result>> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; let sys_mem = self.get_sys_mem(); let mut pcidev = VirtioPciDevice::new( @@ -695,10 +703,11 @@ pub trait MachineOps { if need_irqfd { pcidev.enable_need_irqfd(); } + let clone_pcidev = Arc::new(Mutex::new(pcidev.clone())); pcidev .realize() .chain_err(|| "Failed to add virtio pci device")?; - Ok(()) + Ok(clone_pcidev) } /// Set the parent bus slot on when device attached diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 7aace2751..80d16d2bc 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -774,9 +774,17 @@ impl StdMachine { let blk_id = blk.id.clone(); let blk = Arc::new(Mutex::new(Block::new(blk))); - self.add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) + let pci_dev = self + .add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) .chain_err(|| "Failed to add virtio pci block device")?; + if let Some(bootindex) = args.boot_index { + if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { + self.add_bootindex_devices(bootindex, &dev_path, &args.id) + .chain_err(|| "Fail to add boot index")?; + } + } + MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), blk, @@ -831,6 +839,7 @@ impl StdMachine { &net_id, ); } + Ok(()) } @@ -1036,7 +1045,13 @@ impl DeviceInterface for StdMachine { let locked_pci_host = pci_host.lock().unwrap(); if let Some((bus, dev)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &device_id) { match handle_unplug_request(&bus, &dev) { - Ok(()) => Response::create_empty_response(), + Ok(()) => { + let locked_dev = dev.lock().unwrap(); + let dev_id = locked_dev.name(); + drop(locked_pci_host); + self.del_bootindex_devices(&dev_id); + Response::create_empty_response() + } Err(e) => Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, -- Gitee From 97ed5751f066f76df0f346c7fd4f95c9dc1812f8 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 20:25:03 +0800 Subject: [PATCH 0049/2187] machine/src: add reset_all_devices function When a VM is started or restarted, all devices need to be reset. The corresponding interface is implemented and added to the reboot handler function. In addition, the bootindex structure is updated during the reboot to ensure a successful reboot. Signed-off-by: Jiajie Li --- machine/src/lib.rs | 23 ++++++++++++++++++++++- machine/src/standard_vm/aarch64/mod.rs | 18 ++++++------------ machine/src/standard_vm/x86_64/mod.rs | 16 +++++++--------- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 609f0bfe4..4b81e7da7 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -98,7 +98,7 @@ mod standard_vm; pub use micro_vm::LightMachine; use pci::{PciBus, PciDevOps, PciHost, RootPort}; pub use standard_vm::StdMachine; -use sysbus::SysBus; +use sysbus::{SysBus, SysBusDevOps}; use virtio::{ BlockState, RngState, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioState, VirtioNetState, @@ -480,6 +480,26 @@ pub trait MachineOps { None } + fn reset_all_devices(&mut self) -> Result<()> { + let sysbus = self.get_sys_bus(); + for dev in sysbus.devices.iter() { + dev.lock() + .unwrap() + .reset() + .chain_err(|| "Fail to reset sysbus device")?; + } + + if let Ok(pci_host) = self.get_pci_host() { + pci_host + .lock() + .unwrap() + .reset() + .chain_err(|| "Fail to reset pci host")?; + } + + Ok(()) + } + fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { @@ -495,6 +515,7 @@ pub trait MachineOps { } fn reset_fwcfg_boot_order(&mut self) -> Result<()> { + // unwrap is safe because stand machine always make sure it not return null. let boot_order_vec = self.get_boot_order_list().unwrap(); let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone(); locked_boot_order_vec.sort_by(|x, y| x.boot_index.cmp(&y.boot_index)); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 6c202a9f7..9db391f15 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -183,7 +183,7 @@ impl StdMachine { pub fn handle_reset_request(vm: &Arc>) -> Result<()> { use crate::errors::ResultExt; - let locked_vm = vm.lock().unwrap(); + let mut locked_vm = vm.lock().unwrap(); let mut fdt_addr: u64 = 0; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { @@ -208,18 +208,12 @@ impl StdMachine { ) .chain_err(|| "Fail to write dtb into sysmem")?; - for dev in locked_vm.sysbus.devices.iter() { - dev.lock() - .unwrap() - .reset() - .chain_err(|| "Fail to reset sysbus device")?; - } locked_vm - .pci_host - .lock() - .unwrap() - .reset() - .chain_err(|| "Fail to reset pci host")?; + .reset_all_devices() + .chain_err(|| "Fail to reset all devices")?; + locked_vm + .reset_fwcfg_boot_order() + .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.resume() diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 5e38d85f7..1e93c85c2 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -45,7 +45,7 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::{SysBus, SysBusDevOps}; +use sysbus::SysBus; use syscall::syscall_whitelist; use util::byte_code::ByteCode; use util::loop_context::EventLoopManager; @@ -166,7 +166,7 @@ impl StdMachine { pub fn handle_reset_request(vm: &Arc>) -> MachineResult<()> { use crate::errors::ResultExt as MachineResultExt; - let locked_vm = vm.lock().unwrap(); + let mut locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { MachineResultExt::chain_err(cpu.kick(), || { @@ -176,13 +176,11 @@ impl StdMachine { cpu.set_to_boot_state(); } - for dev in locked_vm.sysbus.devices.iter() { - MachineResultExt::chain_err(dev.lock().unwrap().reset(), || { - "Fail to reset sysbus device" - })?; - } - MachineResultExt::chain_err(locked_vm.pci_host.lock().unwrap().reset(), || { - "Fail to reset pci host" + MachineResultExt::chain_err(locked_vm.reset_all_devices(), || { + "Fail to reset all devices" + })?; + MachineResultExt::chain_err(locked_vm.reset_fwcfg_boot_order(), || { + "Fail to update boot order imformation to FwCfg device" })?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { -- Gitee From c5f0c615ab2d0f5e3a75b5b7f7c47eaee25588ad Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 20 Jan 2022 20:29:17 +0800 Subject: [PATCH 0050/2187] docs/config_guidebook: add bootindex instructions for use Add usage of bootindex in config_guidebook. Signed-off-by: Jiajie Li --- docs/config_guidebook.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 46a536384..a6d047d29 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -7,7 +7,7 @@ StratoVirt can only be launched via cmdline arguments. ### 1.1 Machine Config General configuration of machine, including -* type: The type of machine, three types of machine are available: "none", "microvm", +* type: The type of machine, three types of machine are available: "none", "microvm", "q35"(x86_64 platform) and "virt" (aarch64 platform). * dump-guest-core: Including guest memory in coredump file or not, default value is true. * mem-share: Guest memory is sharable with other processes or not. @@ -33,9 +33,9 @@ Four properties are supported for `smp`. * sockets: the number of socket. (optional). If not set, default is the value of `cpus`. * cores: the number of core. (optional). If not set, default is one. * threads: the number of thread. (optional). If not set, default is one. -NB: the arguments of cpu topology is used to interconnect with libvirt, but the cpu topology of StratoVirt -is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). -If it is configured, the sockets number should equals to the number of cpu, `cores` should be `1` +NB: the arguments of cpu topology is used to interconnect with libvirt, but the cpu topology of StratoVirt +is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). +If it is configured, the sockets number should equals to the number of cpu, `cores` should be `1` and `threads` should be `1`. @@ -64,7 +64,7 @@ Default VM memory size is 256M. The supported VM memory size is among [256M, 512 ``` #### 1.3.2 Memory Prealloc -Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure +Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure memory prealloc. ```shell @@ -252,6 +252,9 @@ Nine properties are supported for virtio block device. If not set, default is raw. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, the default block queue number is 1. +* bootindex: the boot order of block device. (optional) If not set, the priority is lowest. +The number ranges from 1 to 255, the smaller the number, the higher the priority. +It determines the order of bootable devices which firmware will use for booting the guest OS. For virtio-blk-pci, two more properties are required. * bus: name of bus which to attach. @@ -267,7 +270,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo -device virtio-blk-device,drive=drive_id,id=blkid[,iothread=iothread1,serial=serial_num] # virtio pci block device. -drive id=drive_id,file=path_on_host[,readonly=off,direct=off,throttling.iops-total=200] --device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N] +-device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N,bootindex=1] ``` ### 2.3 Virtio-net @@ -278,10 +281,10 @@ Six properties are supported for netdev. * tap/vhost-user: the type of net device. NB: currently only tap and vhost-user is supported. * id: unique netdev id. * ifname: name of tap device in host. -* fd: the file descriptor of opened tap device. +* fd: the file descriptor of opened tap device. * fds: file descriptors of opened tap device. * queues: the optional queues attribute controls the number of queues to be used for either multiple queue virtio-net or vhost-net device. -NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, +NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, the tap device would be created according to `ifname`. Eight properties are supported for virtio-net-device or virtio-net-pci. @@ -298,7 +301,7 @@ It has no effect when vhost is set. Two more properties are supported for virtio pci net device. * bus: name of bus which to attach. * addr: including slot number and function number. The first number represents slot number -of device and the second one represents function number of it. For virtio pci net device, it +of device and the second one represents function number of it. For virtio pci net device, it is a single function device, the function number should be set to zero. ```shell @@ -310,10 +313,10 @@ is a single function device, the function number should be set to zero. -device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC,mq=on] ``` -StratoVirt also supports vhost-net to get a higher performance in network. It can be set by +StratoVirt also supports vhost-net to get a higher performance in network. It can be set by giving `vhost` property, and one more property is supported for vhost-net device. -* vhostfd: fd for vhost-net device, it could be configured when `vhost=on`. If this argument is not +* vhostfd: fd for vhost-net device, it could be configured when `vhost=on`. If this argument is not given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatically. ```shell @@ -549,7 +552,7 @@ Four parameters are supported for pcie root port. * addr: including slot number and function number. * id: the name of secondary bus. * chassis: the number of chassis. Interconnect with libvirt only.(optional). -* multifunction: whether to open multi function for pcie root port.(optional). +* multifunction: whether to open multi function for pcie root port.(optional). If not set, default value is false. ```shell @@ -672,9 +675,9 @@ $ ./stratovirt \ * incoming: the path of the template. See [Snapshot and Restore](./snapshot.md) for details. - + ## 6. Ozone -Ozone is a lightweight secure sandbox for StratoVirt, it provides secure environment for StratoVirt +Ozone is a lightweight secure sandbox for StratoVirt, it provides secure environment for StratoVirt by limiting resources of StratoVirt using 'namespace'. Please run ozone with root permission. ### 6.1 Usage @@ -711,7 +714,7 @@ About the arguments: ### 6.2 Example As ozone uses a directory to mount as a root directory, after ozone is launched, the directory "/srv/zozne/{exec_file}/{name}" will be created. (Where, `exec_file` is the executable binary file, usually it is `stratovirt`, while `name` is the name of ozone, it is given by users, but the length of it should be no more than 255 bytes.) In order to run ozone normally, please make sure that the directory "/srv/zozne/{exec_file}/{name}" does not exists before launching ozone. -On top of that, the path-related arguments are different. They are all in the current(`./`) directory. +On top of that, the path-related arguments are different. They are all in the current(`./`) directory. For net name space, it can be created by the following command with name "mynet": ```shell -- Gitee From 4dea3de2fd5f3f0b08e6123671a024e7dcd648d4 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Fri, 20 May 2022 14:35:29 +0800 Subject: [PATCH 0051/2187] pci/host: add _CCA attribute for pci host on aarch64 In DSDT table, the _CCA attribute of pci host determines whether guest support pci device dma or not. To support VFIO feature, pci dma is necessary. So set _CCA to one in pci host on aarch64 plantform. Signed-off-by: Jiajie Li --- pci/src/host.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pci/src/host.rs b/pci/src/host.rs index c84a6becc..3baf26cee 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -12,6 +12,8 @@ use std::sync::{Arc, Mutex}; +#[cfg(target_arch = "aarch64")] +use acpi::AmlOne; use acpi::{ AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlByte, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, @@ -360,6 +362,12 @@ impl AmlBuilder for PciHost { pci_host_bridge.append_child(AmlNameDecl::new("_CID", AmlEisaId::new("PNP0A03"))); pci_host_bridge.append_child(AmlNameDecl::new("_ADR", AmlZero)); pci_host_bridge.append_child(AmlNameDecl::new("_UID", AmlZero)); + #[cfg(target_arch = "aarch64")] + { + // CCA: Cache Coherency Attribute, which determines whether + // guest supports DMA features in pci host on aarch64 platform. + pci_host_bridge.append_child(AmlNameDecl::new("_CCA", AmlOne)); + } build_osc_for_aml(&mut pci_host_bridge); -- Gitee From 9cc5c8fa22ac268979409f2d5f194ecd9f0a9fb8 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Mon, 23 May 2022 17:09:15 +0800 Subject: [PATCH 0052/2187] standard_vm/x86_64: add default bootorder information to FwCfg device The Fwcfg device saves information about the boot order, and when the blk device with the boot order information is added, it needs to be updated. There is no default boot order information on x86 platforms, so an error occurs when updating. Add the corresponding default boot order information to fix the problem. Signed-off-by: Jiajie Li --- machine/src/standard_vm/x86_64/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 1e93c85c2..bf9f07727 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -28,7 +28,10 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; -use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; +use devices::legacy::{ + errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, + SERIAL_ADDR, +}; use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; @@ -180,7 +183,7 @@ impl StdMachine { "Fail to reset all devices" })?; MachineResultExt::chain_err(locked_vm.reset_fwcfg_boot_order(), || { - "Fail to update boot order imformation to FwCfg device" + "Fail to update boot order information to FwCfg device" })?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { @@ -301,6 +304,11 @@ impl StdMachineOps for StdMachine { fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, ncpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; + let boot_order = Vec::::new(); + fwcfg + .add_file_entry("bootorder", boot_order) + .chain_err(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; + let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) .chain_err(|| "Failed to realize fwcfg device")?; self.fwcfg_dev = Some(fwcfg_dev.clone()); -- Gitee From 64fab078d3a8060f551a6a02a3319281e0c3b6a2 Mon Sep 17 00:00:00 2001 From: linmaolin Date: Wed, 13 Apr 2022 21:36:24 +0800 Subject: [PATCH 0053/2187] move KvmDevice into mod.rs for supporting for gicv2 Signed-off-by: linmaolin --- .../src/interrupt_controller/aarch64/gicv3.rs | 44 +----------------- .../src/interrupt_controller/aarch64/mod.rs | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index c8f07fb03..a83f48347 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex}; use super::{ state::{GICv3ItsState, GICv3State}, GICConfig, GICDevice, UtilResult, + KvmDevice, }; use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; @@ -29,49 +30,6 @@ use util::device_tree::{self, FdtBuilder}; const SZ_64K: u64 = 0x0001_0000; const KVM_VGIC_V3_REDIST_SIZE: u64 = 2 * SZ_64K; -/// A wrapper for kvm_based device check and access. -pub struct KvmDevice; - -impl KvmDevice { - fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr: 0, - flags: 0, - }; - fd.has_device_attr(&attr) - .chain_err(|| "Failed to check device attributes for GIC.")?; - Ok(()) - } - - fn kvm_device_access( - fd: &DeviceFd, - group: u32, - attr: u64, - addr: u64, - write: bool, - ) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr, - flags: 0, - }; - - if write { - fd.set_device_attr(&attr) - .chain_err(|| "Failed to set device attributes for GIC.")?; - } else { - let mut attr = attr; - fd.get_device_attr(&mut attr) - .chain_err(|| "Failed to get device attributes for GIC.")?; - }; - - Ok(()) - } -} - /// Access wrapper for GICv3. pub trait GICv3Access { /// Returns `gicr_attr` of `vCPU`. diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index c4f5aa0ba..3dc7ea3ae 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -14,6 +14,8 @@ mod gicv3; #[allow(dead_code)] mod state; +use kvm_ioctls::DeviceFd; + pub use gicv3::GICv3; use std::sync::Arc; @@ -29,6 +31,50 @@ use super::errors::{ErrorKind, Result, ResultExt}; // First 32 are private to each CPU (SGIs and PPIs). pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; +/// A wrapper for kvm_based device check and access. +pub struct KvmDevice; + +impl KvmDevice { + fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr: 0, + flags: 0, + }; + fd.has_device_attr(&attr) + .chain_err(|| "Failed to check device attributes for GIC.")?; + Ok(()) + } + + fn kvm_device_access( + fd: &DeviceFd, + group: u32, + attr: u64, + addr: u64, + write: bool, + ) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr, + flags: 0, + }; + + if write { + fd.set_device_attr(&attr) + .chain_err(|| "Failed to set device attributes for GIC.")?; + } else { + let mut attr = attr; + fd.get_device_attr(&mut attr) + .chain_err(|| "Failed to get device attributes for GIC.")?; + }; + + Ok(()) + } +} + + /// Configure a Interrupt controller. pub struct GICConfig { /// Config GIC version -- Gitee From 181c0c5d7416d058dc118e0f4a2c51a442349cb1 Mon Sep 17 00:00:00 2001 From: linmaolin Date: Wed, 25 May 2022 17:50:20 +0800 Subject: [PATCH 0054/2187] gicv2: adding support for gicv2 on microvm Signed-off-by: linmaolin --- .../src/interrupt_controller/aarch64/gicv2.rs | 325 ++++++++++++++++++ .../src/interrupt_controller/aarch64/gicv3.rs | 75 ++-- .../src/interrupt_controller/aarch64/mod.rs | 73 ++-- devices/src/interrupt_controller/mod.rs | 6 +- devices/src/lib.rs | 2 +- machine/src/micro_vm/mod.rs | 21 +- machine/src/standard_vm/aarch64/mod.rs | 14 +- 7 files changed, 433 insertions(+), 83 deletions(-) create mode 100644 devices/src/interrupt_controller/aarch64/gicv2.rs diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs new file mode 100644 index 000000000..c15eb48b8 --- /dev/null +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -0,0 +1,325 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::marker::{Send, Sync}; +use std::sync::{Arc, Mutex}; + +use address_space::AddressSpace; +use hypervisor::kvm::KVM_FDS; +use kvm_ioctls::DeviceFd; +use log::error; +use machine_manager::machine::{KvmVmState, MachineLifecycle}; +use util::device_tree::{self, FdtBuilder}; + +use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; +use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; + +// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. +const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; +const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; + +/// Configure a v2 Interrupt controller. +pub struct GICv2Config { + /// GIC distributor address range. + pub dist_range: (u64, u64), + /// GIC cpu interface address range. + pub cpu_range: (u64, u64), + /// GIC v2m range . + pub v2m_range: Option<(u64, u64)>, + /// GIC system memory. + pub sys_mem: Option>, +} + +/// Access wrapper for GICv2. +pub trait GICv2Access { + /// Returns `gicr_attr` of `vCPU`. + fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64; + + fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()>; + + fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()>; +} + +struct GicCpuInterfaceRegion { + /// Base address. + base: u64, + /// Size of Cpu Interface region. + size: u64, +} + +struct GicDistGuestRegion { + /// Base address. + base: u64, + /// Size of Cpu Interface region. + size: u64, +} + +/// A wrapper around creating and managing a `GICv2`. +pub struct GICv2 { + /// The fd for the GICv2 device. + fd: DeviceFd, + /// Maximum irq number. + nr_irqs: u32, + /// GICv2 cpu interface region. + cpu_interface_region: GicCpuInterfaceRegion, + /// Guest physical address space of the GICv2 distributor register mappings. + dist_guest_region: GicDistGuestRegion, + /// Lifecycle state for GICv2. + state: Arc>, +} + +impl GICv2 { + fn new(config: &GICConfig) -> Result { + let v2config = match config.v2.as_ref() { + Some(v2) => v2, + None => return Err(ErrorKind::InvalidConfig("no v2 config found".to_string()).into()), + }; + + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, + fd: 0, + flags: 0, + }; + + let gic_fd = KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_device(&mut gic_device) + .chain_err(|| "Failed to create GICv2 device")?; + + let cpu_interface_region = GicCpuInterfaceRegion { + base: v2config.dist_range.0 + KVM_VGIC_V2_DIST_SIZE, + size: KVM_VGIC_V2_CPU_SIZE, + }; + let dist_guest_region = GicDistGuestRegion { + base: v2config.dist_range.0, + size: v2config.dist_range.1, + }; + + let gicv2 = GICv2 { + fd: gic_fd, + nr_irqs: config.max_irq, + cpu_interface_region, + dist_guest_region, + state: Arc::new(Mutex::new(KvmVmState::Created)), + }; + + Ok(gicv2) + } + + fn realize(&self) -> Result<()> { + KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; + + // Init the interrupt number support by the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, + &self.nr_irqs as *const u32 as u64, + true, + ) + .chain_err(|| "Failed to set GICv2 attribute: irqs")?; + + // Finalize the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + 0, + true, + ) + .chain_err(|| "KVM failed to initialize GICv2")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST), + &self.dist_guest_region.base as *const u64 as u64, + true, + ) + .chain_err(|| "Failed to set GICv2 attribute: distributor address")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU), + &self.cpu_interface_region.base as *const u64 as u64, + true, + ) + .chain_err(|| "Failed to set GICv2 attribute: cpu address")?; + + *self.state.lock().unwrap() = KvmVmState::Running; + + Ok(()) + } + + fn device_fd(&self) -> &DeviceFd { + &self.fd + } +} + +impl MachineLifecycle for GICv2 { + fn pause(&self) -> bool { + // VM change state will flush REDIST pending tables into guest RAM. + if KvmDevice::kvm_device_access( + self.device_fd(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, + 0, + true, + ) + .is_ok() + { + *self.state.lock().unwrap() = KvmVmState::Running; + true + } else { + false + } + } + + fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + let state = self.state.lock().unwrap(); + if *state != old { + error!("GICv2 lifecycle error: state check failed."); + return false; + } + drop(state); + + match (old, new) { + (KvmVmState::Running, KvmVmState::Paused) => self.pause(), + _ => true, + } + } +} + +impl GICv2Access for GICv2 { + fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { + (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK as u64) + | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) + } + + fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + offset, + gicd_value as *mut u32 as u64, + write, + ) + .chain_err(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) + } + + fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + self.vcpu_gicr_attr(offset, cpu), + gicc_value as *mut u64 as u64, + write, + ) + .chain_err(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) + } +} + +impl GICDevice for GICv2 { + fn create_device(gic_conf: &GICConfig) -> Result> { + let gicv2 = Arc::new(GICv2::new(gic_conf)?); + + Ok(gicv2) + } + + fn realize(&self) -> Result<()> { + self.realize().chain_err(|| "Failed to realize GICv2")?; + + Ok(()) + } + + fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()> { + let gic_reg = vec![ + self.dist_guest_region.base, + self.dist_guest_region.size, + self.cpu_interface_region.base, + self.cpu_interface_region.size, + ]; + + let intc_node_dep = fdt.begin_node("intc")?; + fdt.set_property_string("compatible", "arm,cortex-a15-gic")?; + fdt.set_property("interrupt-controller", &Vec::new())?; + fdt.set_property_u32("#interrupt-cells", 0x3)?; + fdt.set_property_u32("phandle", device_tree::GIC_PHANDLE)?; + fdt.set_property_u32("#address-cells", 0x2)?; + fdt.set_property_u32("#size-cells", 0x2)?; + fdt.set_property_array_u64("reg", &gic_reg)?; + + let gic_intr = [ + device_tree::GIC_FDT_IRQ_TYPE_PPI, + 0x9, + device_tree::IRQ_TYPE_LEVEL_HIGH, + ]; + + fdt.set_property_array_u32("interrupts", &gic_intr)?; + + fdt.end_node(intc_node_dep)?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use hypervisor::kvm::KVMFds; + use serial_test::serial; + + use super::super::GICVersion; + use super::super::GICv2Config; + use super::*; + + #[test] + #[serial] + fn test_create_gicv2() { + let kvm_fds = KVMFds::new(); + if kvm_fds.vm_fd.is_none() { + return; + } + KVM_FDS.store(Arc::new(kvm_fds)); + + let gic_conf = GICConfig { + version: Some(GICVersion::GICv2), + vcpu_count: 4, + max_irq: 192, + v2: Some(GICv2Config { + dist_range: (0x0800_0000, 0x0001_0000), + cpu_range: (0x080A_0000, 0x00F6_0000), + v2m_range: None, + sys_mem: None, + }), + v3: None, + }; + assert!(GICv2::new(&gic_conf).is_ok()); + } +} diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index a83f48347..7cc73e338 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -14,8 +14,7 @@ use std::sync::{Arc, Mutex}; use super::{ state::{GICv3ItsState, GICv3State}, - GICConfig, GICDevice, UtilResult, - KvmDevice, + GICConfig, GICDevice, KvmDevice, UtilResult, }; use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; @@ -30,6 +29,18 @@ use util::device_tree::{self, FdtBuilder}; const SZ_64K: u64 = 0x0001_0000; const KVM_VGIC_V3_REDIST_SIZE: u64 = 2 * SZ_64K; +/// Configure a v3 Interrupt controller. +pub struct GICv3Config { + /// Config msi support + pub msi: bool, + /// GIC distributor address range. + pub dist_range: (u64, u64), + /// GIC redistributor address range, support multiple redistributor regions. + pub redist_region_ranges: Vec<(u64, u64)>, + /// GIC ITS address ranges. + pub its_range: Option<(u64, u64)>, +} + /// Access wrapper for GICv3. pub trait GICv3Access { /// Returns `gicr_attr` of `vCPU`. @@ -88,8 +99,10 @@ pub struct GICv3 { impl GICv3 { fn new(config: &GICConfig) -> Result { - config.check_sanity()?; - + let v3config = match config.v3.as_ref() { + Some(v3) => v3, + None => return Err(ErrorKind::InvalidConfig("no v3 config found".to_string()).into()), + }; let mut gic_device = kvm_bindings::kvm_create_device { type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, fd: 0, @@ -108,8 +121,8 @@ impl GICv3 { }; // Calculate GIC redistributor regions' address range according to vcpu count. - let base = config.redist_region_ranges[0].0; - let size = config.redist_region_ranges[0].1; + let base = v3config.redist_region_ranges[0].0; + let size = v3config.redist_region_ranges[0].1; let redist_capability = size / KVM_VGIC_V3_REDIST_SIZE; let redist_region_count = std::cmp::min(config.vcpu_count, redist_capability); let mut redist_regions = vec![GicRedistRegion { @@ -119,7 +132,7 @@ impl GICv3 { }]; if config.vcpu_count > redist_capability { - let high_redist_base = config.redist_region_ranges[1].0; + let high_redist_base = v3config.redist_region_ranges[1].0; let high_redist_region_count = config.vcpu_count - redist_capability; let high_redist_attr = (high_redist_region_count << 52) | high_redist_base | 0x1; @@ -136,12 +149,12 @@ impl GICv3 { nr_irqs: config.max_irq, its_dev: None, redist_regions, - dist_base: config.dist_range.0, - dist_size: config.dist_range.1, + dist_base: v3config.dist_range.0, + dist_size: v3config.dist_range.1, state: Arc::new(Mutex::new(KvmVmState::Created)), }; - if let Some(its_range) = config.its_range { + if let Some(its_range) = v3config.its_range { gicv3.its_dev = Some(Arc::new( GICv3Its::new(&its_range).chain_err(|| "Failed to create ITS")?, )); @@ -517,7 +530,8 @@ mod tests { use hypervisor::kvm::KVMFds; use serial_test::serial; - use super::super::GICConfig; + use super::super::GICVersion; + use super::super::GICv3Config; use super::*; #[test] @@ -530,13 +544,16 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_conf = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), + version: Some(GICVersion::GICv3), vcpu_count: 4, max_irq: 192, - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, + v2: None, + v3: Some(GICv3Config { + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, + }), }; assert!(GICv3::new(&gic_conf).is_ok()); } @@ -551,13 +568,16 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_config = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + version: Some(GICVersion::GICv3), vcpu_count: 4_u64, max_irq: 192_u32, - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, + v2: None, + v3: Some(GICv3Config { + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, + }), }; let gic = GICv3::new(&gic_config).unwrap(); assert!(gic.its_dev.is_none()); @@ -574,13 +594,16 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_config = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + version: Some(GICVersion::GICv3), vcpu_count: 210_u64, max_irq: 192_u32, - msi: true, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], - its_range: Some((0x0808_0000, 0x0002_0000)), + v3: Some(GICv3Config { + msi: true, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], + its_range: Some((0x0808_0000, 0x0002_0000)), + }), + v2: None, }; let gic = GICv3::new(&gic_config).unwrap(); diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 3dc7ea3ae..d45bd063b 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -10,13 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod gicv2; mod gicv3; #[allow(dead_code)] mod state; use kvm_ioctls::DeviceFd; -pub use gicv3::GICv3; +pub use gicv2::{GICv2, GICv2Config}; +pub use gicv3::{GICv3, GICv3Config}; use std::sync::Arc; @@ -31,6 +33,12 @@ use super::errors::{ErrorKind, Result, ResultExt}; // First 32 are private to each CPU (SGIs and PPIs). pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; +/// GIC version type. +pub enum GICVersion { + GICv2, + GICv3, +} + /// A wrapper for kvm_based device check and access. pub struct KvmDevice; @@ -74,43 +82,26 @@ impl KvmDevice { } } - -/// Configure a Interrupt controller. pub struct GICConfig { /// Config GIC version - pub version: u32, + pub version: Option, /// Config number of CPUs handled by the device pub vcpu_count: u64, /// Config maximum number of irqs handled by the device pub max_irq: u32, - /// Config msi support - pub msi: bool, - /// GIC distributor address range. - pub dist_range: (u64, u64), - /// GIC redistributor address range, support multiple redistributor regions. - pub redist_region_ranges: Vec<(u64, u64)>, - /// GIC ITS address ranges. - pub its_range: Option<(u64, u64)>, + /// v2 config. + pub v2: Option, + /// v3 config. + pub v3: Option, } impl GICConfig { fn check_sanity(&self) -> Result<()> { - if self.version != kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 { - return Err(ErrorKind::InvalidConfig("GIC only support GICv3".to_string()).into()); - }; - - if self.vcpu_count > 256 || self.vcpu_count == 0 { - return Err( - ErrorKind::InvalidConfig("GIC only support maximum 256 vcpus".to_string()).into(), - ); - } - if self.max_irq <= GIC_IRQ_INTERNAL { return Err( ErrorKind::InvalidConfig("GIC irq numbers need above 32".to_string()).into(), ); } - Ok(()) } } @@ -152,9 +143,17 @@ impl InterruptController { /// /// * `gic_conf` - Configuration for `GIC`. pub fn new(gic_conf: &GICConfig) -> Result { - Ok(InterruptController { - gic: GICv3::create_device(gic_conf).chain_err(|| "Failed to realize GIC")?, - }) + gic_conf.check_sanity()?; + let gic = match &gic_conf.version { + Some(GICVersion::GICv3) => GICv3::create_device(gic_conf), + Some(GICVersion::GICv2) => GICv2::create_device(gic_conf), + // Try v3 first if no version specified. + None => GICv3::create_device(gic_conf).or_else(|_| GICv2::create_device(gic_conf)), + }; + let intc = InterruptController { + gic: gic.chain_err(|| "Failed to realize GIC")?, + }; + Ok(intc) } pub fn realize(&self) -> Result<()> { @@ -183,30 +182,14 @@ mod tests { #[test] fn test_gic_config() { let mut gic_conf = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), + version: Some(GICVersion::GICv3), vcpu_count: 4, max_irq: 192, - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, + v2: None, + v3: None, }; assert!(gic_conf.check_sanity().is_ok()); - gic_conf.version = 3; - assert!(gic_conf.check_sanity().is_err()); - gic_conf.version = kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(); - assert!(gic_conf.check_sanity().is_ok()); - - gic_conf.vcpu_count = 257; - assert!(gic_conf.check_sanity().is_err()); - gic_conf.vcpu_count = 0; - assert!(gic_conf.check_sanity().is_err()); - gic_conf.vcpu_count = 24; - assert!(gic_conf.check_sanity().is_ok()); - - assert!(gic_conf.check_sanity().is_ok()); - gic_conf.max_irq = 32; assert!(gic_conf.check_sanity().is_err()); } diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 773dfaf8e..dbaa81b25 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -29,7 +29,11 @@ mod aarch64; #[cfg(target_arch = "aarch64")] -pub use aarch64::GICConfig as InterruptControllerConfig; +pub use aarch64::GICConfig as ICGICConfig; +#[cfg(target_arch = "aarch64")] +pub use aarch64::GICv2Config as ICGICv2Config; +#[cfg(target_arch = "aarch64")] +pub use aarch64::GICv3Config as ICGICv3Config; #[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 2a7318645..849ac5394 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -21,6 +21,6 @@ pub mod legacy; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ - errors as IntCtrlErrs, InterruptController, InterruptControllerConfig, + errors as IntCtrlErrs, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, }; pub use legacy::errors as LegacyErrs; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 8f146be1e..55e0ce126 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -75,7 +75,7 @@ use devices::legacy::PL031; use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; #[cfg(target_arch = "aarch64")] -use devices::{InterruptController, InterruptControllerConfig}; +use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] @@ -493,10 +493,7 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "aarch64")] fn init_interrupt_controller(&mut self, vcpu_count: u64) -> MachineResult<()> { // Interrupt Controller Chip init - let intc_conf = InterruptControllerConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - vcpu_count, - max_irq: 192, + let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], redist_region_ranges: vec![ @@ -505,6 +502,20 @@ impl MachineOps for LightMachine { ], its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), }; + let v2 = ICGICv2Config { + dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], + cpu_range: MEM_LAYOUT[LayoutEntryType::GicCpu as usize], + v2m_range: None, + sys_mem: None, + }; + // Passing both v2 and v3, leave GIC self to decide which one to use. + let intc_conf = ICGICConfig { + version: None, + vcpu_count, + max_irq: 192, + v3: Some(v3), + v2: Some(v2), + }; let irq_chip = InterruptController::new(&intc_conf)?; self.irq_chip = Some(Arc::new(irq_chip)); self.irq_chip.as_ref().unwrap().realize()?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 9db391f15..349adc9a8 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -34,7 +34,7 @@ use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; -use devices::{InterruptController, InterruptControllerConfig}; +use devices::{ICGICConfig, ICGICv3Config, InterruptController}; use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use log::error; @@ -319,10 +319,7 @@ impl MachineOps for StdMachine { } fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { - let intc_conf = InterruptControllerConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - vcpu_count, - max_irq: 192, + let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], redist_region_ranges: vec![ @@ -331,6 +328,13 @@ impl MachineOps for StdMachine { ], its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), }; + let intc_conf = ICGICConfig { + version: None, + vcpu_count, + max_irq: 192, + v2: None, + v3: Some(v3), + }; let irq_chip = InterruptController::new(&intc_conf)?; self.irq_chip = Some(Arc::new(irq_chip)); self.irq_chip.as_ref().unwrap().realize()?; -- Gitee From 8c87f9b5e6908ac4d561f87bd74c81ae407ddf86 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 25 May 2022 14:41:01 +0800 Subject: [PATCH 0055/2187] vfio: add ethernet vf driver into kernel It should set `CONFIG_xxVF=y` to add ethernet vf driver into kernel. And then guest OS can load SR-IOV network card as pass through device. Signed-off-by: Xinle.Guo --- docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 | 8 ++++---- docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 | 8 ++++---- docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 | 8 ++++---- docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 b/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 index c2159dd40..85b78de4b 100644 --- a/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 +++ b/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 @@ -1409,15 +1409,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set CONFIG_NET_VENDOR_NETSWIFT=y diff --git a/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 b/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 index b817bc9b0..6ed9be72f 100644 --- a/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 +++ b/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 @@ -1368,15 +1368,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set CONFIG_NET_VENDOR_NETSWIFT=y diff --git a/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 b/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 index 3d89d8a96..4ec28e45d 100644 --- a/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 +++ b/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 @@ -1463,15 +1463,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set # CONFIG_IGC is not set diff --git a/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 b/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 index bcc32e783..d52a12d6a 100644 --- a/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 +++ b/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 @@ -1399,15 +1399,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set # CONFIG_IGC is not set -- Gitee From 70d29565ab991cf080c66d203d0e636bfda11cc2 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 24 May 2022 11:25:13 +0800 Subject: [PATCH 0056/2187] guest numa: fix the error that using `default` and `local` policy to bind guest memory to host memory 1.It doesn't support `local` bind policy as mbind() argument, so remove it from now on. 2.For the `default` bind policy, it needs to deal with max_node special, so max_node must be 0 with `default` bind policy. Signed-off-by: Xinle.Guo --- address_space/src/host_mmap.rs | 10 ++++++---- docs/config_guidebook.md | 2 +- machine_manager/src/config/machine_config.rs | 4 +--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 4f9da2cdc..d2ef05690 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -303,7 +303,7 @@ pub fn set_host_memory_policy( let mut host_addr_start = mem_mappings.get(0).map(|m| m.host_address()).unwrap(); for zone in mem_zones.as_ref().unwrap() { - let node_id = if let Some(id) = zone.host_numa_node { + let mut node_id = if let Some(id) = zone.host_numa_node { id as usize } else { 0_usize @@ -312,15 +312,17 @@ pub fn set_host_memory_policy( let mut nmask = vec![0_u64; node_id / 64 + 1]; nmask[node_id / 64] |= 1_u64 << (node_id % 64); let policy = HostMemPolicy::from(zone.policy.clone()); - if policy == HostMemPolicy::Default { - bail!("Failed to set default host policy"); + if policy != HostMemPolicy::Default { + // We need to pass node_id + 1 as mbind() max_node argument. + // It is kind of linux bug or feature which will cut off the last node. + node_id += 1; } mbind( host_addr_start, zone.size, policy as u32, nmask, - node_id as u64 + 1, + node_id as u64, MPOL_MF_STRICT | MPOL_MF_MOVE, ) .chain_err(|| "Failed to call mbind")?; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a6d047d29..5aa88e52c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -111,7 +111,7 @@ Each NUMA node is given a list of command lines option, there will be described 1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] It describes the size and id of each memory zone, the policy of binding to host memory node. you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. - The optional policies are default, preferred, bind, interleave and local. + The optional policies are default, preferred, bind and interleave. 2. -numa node,cpus=0-1,memdev=mem0 It describes id and cpu set of the NUMA node, and the id belongs to which memory zone. 3. -numa dist,src=0,dst=0,val=10 diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 7a7fe4289..42c44d9e2 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -57,8 +57,7 @@ pub enum HostMemPolicy { Preferred = 1, Bind = 2, Interleave = 3, - Local = 4, - NotSupported = 5, + NotSupported = 4, } impl From for HostMemPolicy { @@ -68,7 +67,6 @@ impl From for HostMemPolicy { "preferred" => HostMemPolicy::Preferred, "bind" => HostMemPolicy::Bind, "interleave" => HostMemPolicy::Interleave, - "local" => HostMemPolicy::Local, _ => HostMemPolicy::NotSupported, } } -- Gitee From ddf2bace8d5bdb7c106ed10b82bb38ac95737a9a Mon Sep 17 00:00:00 2001 From: ace-yan Date: Thu, 5 May 2022 22:07:48 +0800 Subject: [PATCH 0057/2187] Fix incorrect use of from_raw_parts_mut function in trait ByteCode. pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] The function of from_raw_parts is to Forms a slice from a pointer and a length. The len argument is the number of elements, not the number of bytes. data must be valid for reads for len * mem::size_of::() many bytes, and it must be properly aligned. The original code incorrectly used from_raw_parts in the from_bytes function. The correct use is that the second parameter of len should equal to 1 in the from_raw_parts function. like this.: let obj_array = unsafe { from_raw_parts::(data.as_ptr() as *const _, 1) }; Signed-off-by: Yan Wen --- util/src/byte_code.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 0c607b1ff..667d822d1 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -37,8 +37,9 @@ pub trait ByteCode: Default + Copy + Send + Sync { if data.len() != size_of::() { return None; } - let obj_array = unsafe { from_raw_parts::(data.as_ptr() as *const _, data.len()) }; - Some(&obj_array[0]) + + // SAFETY: The pointer is properly aligned and point to an initialized instance of T. + unsafe { data.as_ptr().cast::().as_ref() } } /// Creates an mutable object (impl trait `ByteCode`) from a mutable slice of bytes @@ -50,9 +51,9 @@ pub trait ByteCode: Default + Copy + Send + Sync { if data.len() != size_of::() { return None; } - let obj_array = - unsafe { from_raw_parts_mut::(data.as_mut_ptr() as *mut _, data.len()) }; - Some(&mut obj_array[0]) + + // SAFETY: The pointer is properly aligned and point to an initialized instance of T. + unsafe { data.as_mut_ptr().cast::().as_mut() } } } -- Gitee From 2833bd5da907bbd050c98430f1f47baa0dc5b498 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 3 Jun 2022 22:46:48 +0800 Subject: [PATCH 0058/2187] Fix handler::tests::test_disinfect_process test case. Signed-off-by: Yan Wen --- ozone/src/handler.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 4e37441bf..af6c2f860 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -332,10 +332,9 @@ fn disinfect_process() -> Result<()> { let fd_entries = read_dir(SELF_FD).chain_err(|| "Failed to open process fd proc")?; for entry in fd_entries { if entry.is_err() { - continue; + break; } - let entry = entry?; - let file_name = entry.file_name(); + let file_name = entry.unwrap().file_name(); let file_name = file_name.to_str().unwrap_or("0"); let fd = file_name.parse::().unwrap_or(0); -- Gitee From e930d3513222dc1c8e73e7bc2c76c3be413834ca Mon Sep 17 00:00:00 2001 From: uran0sH Date: Thu, 12 May 2022 14:14:03 +0800 Subject: [PATCH 0059/2187] Add x86 CPU Topology Support Users can use "-smp n,maxcpus=,sockets=,dies=,cores=,threads=" to configure the cpu topology in x86. Signed-off-by: uran0sH --- cpu/src/aarch64/mod.rs | 17 +++ cpu/src/lib.rs | 67 ++++++-- cpu/src/x86_64/mod.rs | 101 ++++++++++++- docs/config_guidebook.md | 17 ++- machine/src/lib.rs | 5 +- machine/src/micro_vm/mod.rs | 17 ++- machine/src/standard_vm/aarch64/mod.rs | 9 +- machine/src/standard_vm/x86_64/mod.rs | 17 ++- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/machine_config.rs | 151 +++++++++++++------ 10 files changed, 322 insertions(+), 81 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 1c427e7f6..ecfdffc40 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -62,6 +62,23 @@ pub struct ArmCPUBootConfig { pub boot_pc: u64, } +#[derive(Default, Copy, Clone)] +pub struct ArmCPUTopology { + threads: u8, + cores: u8, + clusters: u8, +} + +impl ArmCPUTopology { + pub fn new() -> Self { + ArmCPUTopology::default(); + } + + pub fn set_topology(mut self) -> Self { + self + } +} + /// AArch64 CPU architect information #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 64a1fc5ec..092d3d731 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -104,12 +104,16 @@ pub use aarch64::ArmCPUBootConfig as CPUBootConfig; pub use aarch64::ArmCPUCaps as CPUCaps; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUState as ArchCPU; +#[cfg(target_arch = "aarch64")] +pub use aarch64::ArmCPUTopology as CPUTopology; #[cfg(target_arch = "x86_64")] use x86_64::caps::X86CPUCaps as CPUCaps; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUState as ArchCPU; +#[cfg(target_arch = "x86_64")] +pub use x86_64::X86CPUTopology as CPUTopology; use std::cell::RefCell; use std::sync::atomic::fence; @@ -158,7 +162,7 @@ pub enum CpuLifecycleState { #[allow(clippy::upper_case_acronyms)] pub trait CPUInterface { /// Realize `CPU` structure, set registers value for `CPU`. - fn realize(&self, boot: &CPUBootConfig) -> Result<()>; + fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()>; /// Start `CPU` thread and run virtual CPU in kvm. /// @@ -290,7 +294,7 @@ impl CPU { } impl CPUInterface for CPU { - fn realize(&self, boot: &CPUBootConfig) -> Result<()> { + fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()> { let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { return Err( @@ -303,6 +307,14 @@ impl CPUInterface for CPU { .unwrap() .set_boot_config(&self.fd, boot) .chain_err(|| "Failed to realize arch cpu")?; + + #[cfg(target_arch = "x86_64")] + self.arch_cpu + .lock() + .unwrap() + .set_cpu_topology(topology) + .chain_err(|| "Failed to realize arch cpu")?; + self.boot_state.lock().unwrap().set(&self.arch_cpu); Ok(()) } @@ -700,6 +712,8 @@ impl CPUThreadWorker { pub struct CpuTopology { /// Number of sockets in VM. pub sockets: u8, + /// Number of dies on one socket. + pub dies: u8, /// Number of cores in VM. pub cores: u8, /// Number of threads in VM. @@ -717,15 +731,26 @@ impl CpuTopology { /// /// # Arguments /// - /// * `nr_cpus`: Number of vcpus. - pub fn new(nr_cpus: u8) -> Self { + /// * `nr_cpus`: Number of vcpus in one VM. + /// * `nr_threads`: Number of threads on one core. + /// * `nr_cores`: Number of cores on one socket + /// * `nr_sockets`: Number of sockets on in one VM. + pub fn new( + nr_cpus: u8, + nr_threads: u8, + nr_cores: u8, + nr_dies: u8, + nr_sockets: u8, + max_cpus: u8, + ) -> Self { let mask: Vec = vec![1; nr_cpus as usize]; Self { - sockets: nr_cpus, - cores: 1, - threads: 1, + sockets: nr_sockets, + dies: nr_dies, + cores: nr_cores, + threads: nr_threads, nrcpus: nr_cpus, - max_cpus: nr_cpus, + max_cpus, online_mask: Arc::new(Mutex::new(mask)), } } @@ -752,11 +777,9 @@ impl CpuTopology { /// /// * `vcpu_id` - ID of vcpu. pub fn get_topo(&self, vcpu_id: usize) -> (u8, u8, u8) { - let cpu_per_socket = self.cores * self.threads; - let cpu_per_core = self.threads; - let socketid: u8 = vcpu_id as u8 / cpu_per_socket; - let coreid: u8 = (vcpu_id as u8 % cpu_per_socket) / cpu_per_core; - let threadid: u8 = (vcpu_id as u8 % cpu_per_socket) % cpu_per_core; + let socketid: u8 = vcpu_id as u8 / (self.dies * self.cores * self.threads); + let coreid: u8 = (vcpu_id as u8 / self.threads) % self.cores; + let threadid: u8 = vcpu_id as u8 % self.threads; (socketid, coreid, threadid) } } @@ -929,6 +952,7 @@ mod tests { let microvm_cpu_topo = CpuTopology { sockets: test_nr_cpus, + dies: 1, cores: 1, threads: 1, nrcpus: test_nr_cpus, @@ -941,10 +965,27 @@ mod tests { assert_eq!(microvm_cpu_topo.get_topo(8), (8, 0, 0)); assert_eq!(microvm_cpu_topo.get_topo(15), (15, 0, 0)); + let mask = Vec::with_capacity(test_nr_cpus as usize); + let microvm_cpu_topo1 = CpuTopology { + sockets: 1, + dies: 2, + cores: 4, + threads: 2, + nrcpus: test_nr_cpus, + max_cpus: test_nr_cpus, + online_mask: Arc::new(Mutex::new(mask)), + }; + + assert_eq!(microvm_cpu_topo1.get_topo(0), (0, 0, 0)); + assert_eq!(microvm_cpu_topo1.get_topo(4), (0, 2, 0)); + assert_eq!(microvm_cpu_topo1.get_topo(8), (0, 0, 0)); + assert_eq!(microvm_cpu_topo1.get_topo(15), (0, 3, 1)); + let test_nr_cpus: u8 = 32; let mask = Vec::with_capacity(test_nr_cpus as usize); let test_cpu_topo = CpuTopology { sockets: 2, + dies: 1, cores: 4, threads: 2, nrcpus: test_nr_cpus, diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 37e0f8478..43ed5e1ea 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -49,6 +49,11 @@ const MSR_LIST: &[u32] = &[ const MSR_IA32_MISC_ENABLE: u32 = 0x01a0; const MSR_IA32_MISC_ENABLE_FAST_STRING: u64 = 0x1; +const ECX_INVALID: u32 = 0u32 << 8; +const ECX_THREAD: u32 = 1u32 << 8; +const ECX_CORE: u32 = 2u32 << 8; +const ECX_DIE: u32 = 5u32 << 8; + /// X86 CPU booting configure information #[allow(clippy::upper_case_acronyms)] #[derive(Default, Clone)] @@ -72,6 +77,27 @@ pub struct X86CPUBootConfig { pub pml4_start: u64, } +#[allow(clippy::upper_case_acronyms)] +#[derive(Default, Copy, Clone)] +pub struct X86CPUTopology { + threads: u8, + cores: u8, + dies: u8, +} + +impl X86CPUTopology { + pub fn new() -> Self { + X86CPUTopology::default() + } + + pub fn set_topology(mut self, toplogy: (u8, u8, u8)) -> Self { + self.threads = toplogy.0; + self.cores = toplogy.1; + self.dies = toplogy.2; + self + } +} + /// The state of vCPU's register. #[allow(clippy::upper_case_acronyms)] #[repr(C)] @@ -79,6 +105,10 @@ pub struct X86CPUBootConfig { #[desc_version(compat_version = "0.1.0")] pub struct X86CPUState { nr_vcpus: u32, + nr_threads: u32, + nr_cores: u32, + nr_dies: u32, + nr_sockets: u32, apic_id: u32, regs: kvm_regs, sregs: kvm_sregs, @@ -112,6 +142,10 @@ impl X86CPUState { apic_id: vcpu_id, nr_vcpus, mp_state, + nr_threads: 1, + nr_cores: 1, + nr_dies: 1, + nr_sockets: 1, ..Default::default() } } @@ -153,6 +187,18 @@ impl X86CPUState { Ok(()) } + /// Set cpu topology + /// + /// # Arguments + /// + /// * `topology`: 0: threads, 1: cores, 2: dies + pub fn set_cpu_topology(&mut self, topology: &X86CPUTopology) -> Result<()> { + self.nr_threads = topology.threads as u32; + self.nr_cores = topology.cores as u32; + self.nr_dies = topology.dies as u32; + Ok(()) + } + /// Reset register value with `X86CPUState`. /// /// # Arguments @@ -344,6 +390,9 @@ impl X86CPUState { } fn setup_cpuid(&self, vcpu_fd: &Arc) -> Result<()> { + let core_offset = 32u32 - (self.nr_threads - 1).leading_zeros(); + let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset; + let pkg_offset = (32u32 - (self.nr_dies - 1).leading_zeros()) + die_offset; let sys_fd = match Kvm::new() { Ok(fd) => fd, _ => bail!("setup_cpuid: Open /dev/kvm failed"), @@ -406,20 +455,56 @@ impl X86CPUState { entry.ecx = entry.index & 0xff; match entry.index { 0 => { - entry.eax = 0u32; - entry.ebx = 1u32; - entry.ecx |= 1u32 << 8; + entry.eax = core_offset; + entry.ebx = self.nr_threads; + entry.ecx |= ECX_THREAD; + } + 1 => { + entry.eax = pkg_offset; + entry.ebx = self.nr_threads * self.nr_cores; + entry.ecx |= ECX_CORE; + } + _ => { + entry.eax = 0; + entry.ebx = 0; + entry.ecx |= ECX_INVALID; + } + } + } + 0x1f => { + if self.nr_dies < 2 { + entry.eax = 0; + entry.ebx = 0; + entry.ecx = 0; + entry.edx = 0; + continue; + } + + entry.edx = self.apic_id as u32; + entry.ecx = entry.index & 0xff; + + match entry.index { + 0 => { + entry.eax = core_offset; + entry.ebx = self.nr_threads; + entry.ecx |= ECX_THREAD; } 1 => { - entry.eax = 32u32 - self.nr_vcpus.leading_zeros(); - entry.ebx = self.nr_vcpus; - entry.ecx |= 2u32 << 8; + entry.eax = die_offset; + entry.ebx = self.nr_cores * self.nr_threads; + entry.ecx |= ECX_CORE; + } + 2 => { + entry.eax = pkg_offset; + entry.ebx = self.nr_dies * self.nr_cores * self.nr_threads; + entry.ecx |= ECX_DIE; } _ => { - entry.ebx = 0xff; + entry.eax = 0; + entry.ebx = 0; + entry.ecx |= ECX_INVALID; } } - entry.ebx &= 0xffff; } 0x8000_0002..=0x8000_0004 => { // Passthrough host cpu model name directly to guest diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index ff586eaf2..c902cf8a3 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -30,18 +30,19 @@ This allows you to set the maximum number of VCPUs that VM will support. The max By default, after booted, VM will online all CPUs you set. Four properties are supported for `smp`. * cpus: the number of VCPUs. -* sockets: the number of socket. (optional). If not set, default is the value of `cpus`. -* cores: the number of core. (optional). If not set, default is one. -* threads: the number of thread. (optional). If not set, default is one. -NB: the arguments of cpu topology is used to interconnect with libvirt, but the cpu topology of StratoVirt -is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). -If it is configured, the sockets number should equals to the number of cpu, `cores` should be `1` -and `threads` should be `1`. +* maxcpus: the number of max VCPUs. +* sockets: the number of socket. (optional). If not set, its value depends on the value of `maxcpus`. +* dies: the number of dies. (optional). If not set, default is one. +* cores: the number of core. (optional). If not set, its value depends on the value of `maxcpus`. +* threads: the number of thread. (optional). If not set, its value depends on the value of `maxcpus`. +NB: the arguments of cpu topology is used to interconnect with libvirt. The x86 cpu topology has supported. But the ARM cpu topology of StratoVirt is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). + +If it is configured, sockets * dies * cores * threads must be equal to maxcpus, and maxcpus should be larger than cpus. ```shell # cmdline --smp [cpus=]n[,sockets=n,cores=1,threads=1] +-smp [cpus=]n[,maxcpus=,sockets=,dies=,cores=,threads=] ``` ### 1.3 Memory diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3633b5535..07c7b7354 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -118,7 +118,7 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; -use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU}; +use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; @@ -221,6 +221,7 @@ pub trait MachineOps { fn init_vcpu( vm: Arc>, nr_cpus: u8, + topology: &CPUTopology, fds: &[Arc], boot_cfg: &Option, ) -> Result>> @@ -253,7 +254,7 @@ pub trait MachineOps { if let Some(boot_config) = boot_cfg { for cpu_index in 0..nr_cpus as usize { cpus[cpu_index as usize] - .realize(boot_config) + .realize(boot_config, topology) .chain_err(|| { format!( "Failed to realize arch cpu register for CPU {}/KVM", diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index fc8595501..189e8acea 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -66,7 +66,7 @@ use std::vec::Vec; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CpuLifecycleState, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState, CpuTopology, CPU}; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; #[cfg(target_arch = "x86_64")] @@ -224,7 +224,14 @@ impl LightMachine { } Ok(LightMachine { - cpu_topo: CpuTopology::new(vm_config.machine_config.nr_cpus), + cpu_topo: CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.max_cpus, + ), cpus: Vec::new(), #[cfg(target_arch = "aarch64")] irq_chip: None, @@ -739,9 +746,15 @@ impl MachineOps for LightMachine { } else { None }; + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, + &topology, &vcpu_fds, &boot_config, )?); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 48b773bf9..07a2fb8a0 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -131,7 +131,14 @@ impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new(vm_config.machine_config.nr_cpus); + let cpu_topo = CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.max_cpus, + ); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) .chain_err(|| ErrorKind::CrtIoSpaceErr)?; let sysbus = SysBus::new( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 954013f16..0e6dc159d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -27,7 +27,7 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; @@ -111,7 +111,14 @@ impl StdMachine { pub fn new(vm_config: &VmConfig) -> MachineResult { use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new(vm_config.machine_config.nr_cpus); + let cpu_topo = CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.max_cpus, + ); let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) .chain_err(|| MachineErrorKind::CrtMemSpaceErr)?; let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) @@ -449,9 +456,15 @@ impl MachineOps for StdMachine { } else { (None, None) }; + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, + &topology, &vcpu_fds, &boot_config, )?); diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 875d7fa88..95adff176 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -94,7 +94,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("smp") .long("smp") - .value_name("[cpus=]n") + .value_name("[cpus=]n[,maxcpus=cpus][,dies=dies][,sockets=sockets][,cores=cores][,threads=threads]") .help("set the number of CPUs to 'n' (default: 1)") .takes_value(true), ) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index bf0c21e9b..d2879fc04 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -18,6 +18,11 @@ use super::errors::{ErrorKind, Result, ResultExt}; use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig}; const DEFAULT_CPUS: u8 = 1; +const DEFAULT_THREADS: u8 = 1; +const DEFAULT_CORES: u8 = 1; +const DEFAULT_DIES: u8 = 1; +const DEFAULT_SOCKETS: u8 = 1; +const DEFAULT_MAX_CPUS: u8 = 1; const DEFAULT_MEMSIZE: u64 = 256; const MAX_NR_CPUS: u64 = 254; const MIN_NR_CPUS: u64 = 1; @@ -77,6 +82,11 @@ impl Default for MachineMemConfig { pub struct MachineConfig { pub mach_type: MachineType, pub nr_cpus: u8, + pub nr_threads: u8, + pub nr_cores: u8, + pub nr_dies: u8, + pub nr_sockets: u8, + pub max_cpus: u8, pub mem_config: MachineMemConfig, } @@ -86,6 +96,11 @@ impl Default for MachineConfig { MachineConfig { mach_type: MachineType::MicroVm, nr_cpus: DEFAULT_CPUS, + nr_threads: DEFAULT_THREADS, + nr_cores: DEFAULT_CORES, + nr_dies: DEFAULT_DIES, + nr_sockets: DEFAULT_SOCKETS, + max_cpus: DEFAULT_MAX_CPUS, mem_config: MachineMemConfig::default(), } } @@ -185,7 +200,9 @@ impl VmConfig { let mut cmd_parser = CmdParser::new("smp"); cmd_parser .push("") + .push("maxcpus") .push("sockets") + .push("dies") .push("cores") .push("threads") .push("cpus"); @@ -195,42 +212,69 @@ impl VmConfig { let cpu = if let Some(cpu) = cmd_parser.get_value::("")? { cpu } else if let Some(cpu) = cmd_parser.get_value::("cpus")? { + if cpu == 0 { + return Err( + ErrorKind::IllegalValue("cpu".to_string(), 1, true, u64::MAX, true).into(), + ); + } cpu } else { return Err(ErrorKind::FieldIsMissing("cpus", "smp").into()); }; - if let Some(sockets) = cmd_parser.get_value::("sockets")? { - if sockets.ne(&cpu) { - bail!("Invalid \'sockets\' arguments for \'smp\', it should equal to the number of cpus"); - } - } - if let Some(cores) = cmd_parser.get_value::("cores")? { - if cores.ne(&1) { - bail!("Invalid \'cores\' arguments for \'smp\', it should be \'1\'"); - } - } - if let Some(threads) = cmd_parser.get_value::("threads")? { - if threads.ne(&1) { - bail!("Invalid \'threads\' arguments for \'smp\', it should be \'1\'"); - } - } + let mut sockets = cmd_parser.get_value::("sockets")?.unwrap_or_default(); + + let dies = cmd_parser.get_value::("dies")?.unwrap_or(1); + + let mut cores = cmd_parser.get_value::("cores")?.unwrap_or_default(); + + let mut threads = cmd_parser.get_value::("threads")?.unwrap_or_default(); + + let mut max_cpus = cmd_parser.get_value::("maxcpus")?.unwrap_or_default(); + + (max_cpus, sockets, cores, threads) = adjust_topology(cpu, max_cpus, sockets, dies, cores, threads); // limit cpu count if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&cpu) { return Err(ErrorKind::IllegalValue( "CPU number".to_string(), - MIN_NR_CPUS, + MIN_NR_CPUS as u64, true, - MAX_NR_CPUS, + MAX_NR_CPUS as u64, true, ) .into()); } - // it is safe, as value limited before - self.machine_config.nr_cpus = cpu as u8; + // limit cpu count + if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&max_cpus) { + return Err(ErrorKind::IllegalValue( + "MAX CPU number".to_string(), + MIN_NR_CPUS as u64, + true, + MAX_NR_CPUS as u64, + true, + ) + .into()); + } + + if max_cpus < cpu || sockets * dies * cores * threads != max_cpus { + return Err(ErrorKind::IllegalValue( + "maxcpus".to_string(), + cpu as u64, + true, + (sockets * dies * cores * threads) as u64, + true, + ) + .into()); + } + self.machine_config.nr_cpus = cpu as u8; + self.machine_config.nr_threads = threads as u8; + self.machine_config.nr_cores = cores as u8; + self.machine_config.nr_dies = dies as u8; + self.machine_config.nr_sockets = sockets as u8; + self.machine_config.max_cpus = max_cpus as u8; Ok(()) } @@ -244,6 +288,44 @@ impl VmConfig { } } +fn adjust_topology( + cpu: u64, + mut max_cpus: u64, + mut sockets: u64, + dies: u64, + mut cores: u64, + mut threads: u64, +) -> (u64, u64, u64, u64) { + if max_cpus == 0 { + if sockets * dies * cores * threads > 0 { + max_cpus = sockets * dies * cores * threads; + } else { + max_cpus = cpu; + } + } + + if cores == 0 { + if sockets == 0 { + sockets = 1; + } + if threads == 0 { + threads = 1; + } + cores = max_cpus / (sockets * dies * threads); + } else if sockets == 0 { + if threads == 0 { + threads = 1; + } + sockets = max_cpus / (dies * cores * threads); + } + + if threads == 0 { + threads = max_cpus / (sockets * dies * cores); + } + + (max_cpus, sockets, cores, threads) +} + fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) @@ -305,7 +387,12 @@ mod tests { }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, - nr_cpus: MIN_NR_CPUS as u8, + nr_cpus: 1, + nr_cores: 1, + nr_threads: 1, + nr_dies: 1, + nr_sockets: 1, + max_cpus: MIN_NR_CPUS as u8, mem_config: memory_config, }; assert!(machine_config.check().is_ok()); @@ -622,29 +709,5 @@ mod tests { let cpu_cfg_str = "cpus=255,sockets=255,cores=1,threads=1"; let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=4,cores=2,threads=1"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=2,cores=2,threads=2"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=1,cores=4,threads=2"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=1,cores=2,threads=4"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); } } -- Gitee From 43b78f04c880964c0cd5153ecf833bae8526d86d Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 7 Jun 2022 11:54:47 +0800 Subject: [PATCH 0060/2187] standard_vm: check bootorder legitimacy before adding pci blk devices When encountering a situation where the boot order is duplicated, since the device has completed the realize, it needs to be unrealized, the process is cumbersome and error-prone. So instead, check the legitimacy of boot order before adding the device. Signed-off-by: Jiajie Li --- machine/src/lib.rs | 32 +++++++++++++++++++------------- machine/src/standard_vm/mod.rs | 8 ++++++-- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4b81e7da7..46a966c5a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -535,19 +535,12 @@ pub trait MachineOps { Ok(()) } - /// Add boot index of device. + /// Check the boot index of device is duplicated or not. /// /// # Arguments /// /// * `bootindex` - The boot index of the device. - /// * `dev_path` - The firmware device path of the device. - /// * `dev_id` - The id of the device. - fn add_bootindex_devices( - &mut self, - boot_index: u8, - dev_path: &str, - dev_id: &str, - ) -> Result<()> { + fn check_bootindex(&mut self, boot_index: u8) -> Result<()> { // Unwrap is safe because StdMachine will overwrite this function, // which ensure boot_order_list is not None. let boot_order_list = self.get_boot_order_list().unwrap(); @@ -560,13 +553,23 @@ pub trait MachineOps { bail!("Failed to add duplicated bootindex {}.", boot_index); } + Ok(()) + } + + /// Add boot index of device. + /// + /// # Arguments + /// + /// * `bootindex` - The boot index of the device. + /// * `dev_path` - The firmware device path of the device. + /// * `dev_id` - The id of the device. + fn add_bootindex_devices(&mut self, boot_index: u8, dev_path: &str, dev_id: &str) { + let boot_order_list = self.get_boot_order_list().unwrap(); boot_order_list.lock().unwrap().push(BootIndexInfo { boot_index, id: dev_id.to_string(), dev_path: dev_path.to_string(), }); - - Ok(()) } /// Delete boot index of device. @@ -586,14 +589,17 @@ pub trait MachineOps { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_blk(vm_config, cfg_args)?; + if let Some(bootindex) = device_cfg.boot_index { + self.check_bootindex(bootindex) + .chain_err(|| "Fail to add virtio pci blk device for invalid bootindex")?; + } let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) .chain_err(|| "Failed to add virtio pci device")?; if let Some(bootindex) = device_cfg.boot_index { if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { - self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id) - .chain_err(|| "Fail to add boot index")?; + self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); } } MigrationManager::register_device_instance_mutex_with_id( diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 80d16d2bc..25cc3a356 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -772,6 +772,11 @@ impl StdMachine { bail!("Drive not found"); }; + if let Some(bootindex) = args.boot_index { + self.check_bootindex(bootindex) + .chain_err(|| "Fail to add virtio pci blk device for invalid bootindex")?; + } + let blk_id = blk.id.clone(); let blk = Arc::new(Mutex::new(Block::new(blk))); let pci_dev = self @@ -780,8 +785,7 @@ impl StdMachine { if let Some(bootindex) = args.boot_index { if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { - self.add_bootindex_devices(bootindex, &dev_path, &args.id) - .chain_err(|| "Fail to add boot index")?; + self.add_bootindex_devices(bootindex, &dev_path, &args.id); } } -- Gitee From 3f961edd35427a6d03e45ec58fcaea403a4c7acb Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Tue, 31 May 2022 20:25:19 +0800 Subject: [PATCH 0061/2187] standard_vm: adjust the logic of adding FwCFG devices `add_fwcfg_device` has nothing to do with migrate, so move it out of the migrate condition range. But for `build_acpi_tables`, it doesn't need to be called under migrate condition. Signed-off-by: Jiajie Li --- machine/src/standard_vm/aarch64/mod.rs | 17 +++++++---------- machine/src/standard_vm/x86_64/mod.rs | 11 ++++++----- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 349adc9a8..8aa77e3b2 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -449,12 +449,15 @@ impl MachineOps for StdMachine { locked_vm .add_devices(vm_config) .chain_err(|| "Failed to add devices")?; + let fwcfg = locked_vm.add_fwcfg_device()?; - let (boot_config, fwcfg) = if !is_migrate { - let fwcfg = locked_vm.add_fwcfg_device()?; - (Some(locked_vm.load_boot_source(Some(&fwcfg))?), Some(fwcfg)) + let boot_config = if !is_migrate { + locked_vm + .build_acpi_tables(&fwcfg) + .chain_err(|| "Failed to create ACPI tables")?; + Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { - (None, None) + None }; locked_vm @@ -485,12 +488,6 @@ impl MachineOps for StdMachine { .chain_err(|| ErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } - if let Some(fwcfg) = fwcfg { - locked_vm - .build_acpi_tables(&fwcfg) - .chain_err(|| "Failed to create ACPI tables")?; - } - locked_vm.register_power_event(&locked_vm.power_button)?; if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index bf9f07727..42f19b989 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -469,12 +469,12 @@ impl MachineOps for StdMachine { .init_ich9_lpc(clone_vm) .chain_err(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; + let fwcfg = locked_vm.add_fwcfg_device()?; - let (boot_config, fwcfg) = if !is_migrate { - let fwcfg = locked_vm.add_fwcfg_device()?; - (Some(locked_vm.load_boot_source(Some(&fwcfg))?), Some(fwcfg)) + let boot_config = if !is_migrate { + Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { - (None, None) + None }; locked_vm.cpus.extend(::init_vcpu( vm.clone(), @@ -483,11 +483,12 @@ impl MachineOps for StdMachine { &boot_config, )?); - if let Some(fwcfg) = fwcfg { + if !is_migrate { locked_vm .build_acpi_tables(&fwcfg) .chain_err(|| "Failed to create ACPI tables")?; } + StdMachine::arch_init()?; locked_vm.register_power_event(&locked_vm.power_button)?; -- Gitee From 8dd2fba809a19cd5a85980465065073bd58cb7f0 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Mon, 13 Jun 2022 17:11:14 +0800 Subject: [PATCH 0062/2187] Revert "Merge branch 'dev' of https://gitee.com/openeuler/stratovirt into x86_cpu_topo" Because CI is failed, so we need to revert. This reverts commit d35c284292818d69437752bb343b81c9eca1a9dc, reversing changes made to e930d3513222dc1c8e73e7bc2c76c3be413834ca. --- acpi/src/acpi_device.rs | 1 - acpi/src/acpi_table.rs | 88 ----- acpi/src/lib.rs | 7 +- acpi/src/table_loader.rs | 1 - address_space/src/host_mmap.rs | 59 +--- address_space/src/lib.rs | 11 +- address_space/src/listener.rs | 5 +- address_space/src/region.rs | 3 - address_space/src/state.rs | 2 - boot_loader/src/aarch64/mod.rs | 1 - boot_loader/src/lib.rs | 7 +- boot_loader/src/x86_64/direct_boot/mod.rs | 6 +- boot_loader/src/x86_64/mod.rs | 1 - boot_loader/src/x86_64/standard_boot/elf.rs | 1 - boot_loader/src/x86_64/standard_boot/mod.rs | 2 - cpu/src/aarch64/core_regs.rs | 5 +- cpu/src/aarch64/mod.rs | 8 +- cpu/src/lib.rs | 15 +- cpu/src/x86_64/mod.rs | 6 +- .../src/interrupt_controller/aarch64/gicv2.rs | 325 ------------------ .../src/interrupt_controller/aarch64/gicv3.rs | 118 ++++--- .../src/interrupt_controller/aarch64/mod.rs | 117 +++---- .../src/interrupt_controller/aarch64/state.rs | 5 +- devices/src/interrupt_controller/mod.rs | 8 +- devices/src/legacy/chardev.rs | 2 - devices/src/legacy/fwcfg.rs | 82 +---- devices/src/legacy/mod.rs | 2 - devices/src/legacy/pflash.rs | 3 +- devices/src/legacy/pl011.rs | 2 - devices/src/legacy/pl031.rs | 2 - devices/src/legacy/rtc.rs | 1 - devices/src/legacy/serial.rs | 3 - devices/src/lib.rs | 9 +- docs/config_guidebook.md | 71 +--- .../standard_vm/kernel_config_4.19_aarch64 | 8 +- .../standard_vm/kernel_config_4.19_x86_64 | 8 +- .../standard_vm/kernel_config_5.10_aarch64 | 8 +- .../standard_vm/kernel_config_5.10_x86_64 | 8 +- hypervisor/src/kvm/interrupt.rs | 1 - hypervisor/src/kvm/mod.rs | 5 +- hypervisor/src/kvm/state.rs | 1 - hypervisor/src/lib.rs | 12 +- machine/src/lib.rs | 209 +---------- machine/src/micro_vm/mod.rs | 36 +- machine/src/standard_vm/aarch64/mod.rs | 226 ++---------- .../src/standard_vm/aarch64/pci_host_root.rs | 1 - machine/src/standard_vm/mod.rs | 110 +----- machine/src/standard_vm/x86_64/ich9_lpc.rs | 3 +- machine/src/standard_vm/x86_64/mch.rs | 3 +- machine/src/standard_vm/x86_64/mod.rs | 239 +++---------- machine_manager/src/cmdline.rs | 11 - machine_manager/src/config/balloon.rs | 1 - machine_manager/src/config/chardev.rs | 1 - machine_manager/src/config/drive.rs | 17 +- machine_manager/src/config/machine_config.rs | 151 +------- machine_manager/src/config/mod.rs | 57 +-- machine_manager/src/config/network.rs | 1 - machine_manager/src/config/numa.rs | 310 ----------------- machine_manager/src/config/pci.rs | 2 - machine_manager/src/config/rng.rs | 2 - machine_manager/src/event_loop.rs | 4 +- machine_manager/src/lib.rs | 7 +- machine_manager/src/qmp/mod.rs | 1 - machine_manager/src/qmp/qmp_schema.rs | 121 ++++--- machine_manager/src/signal_handler.rs | 1 + machine_manager/src/socket.rs | 2 - migration/src/device_state.rs | 2 - migration/src/lib.rs | 10 +- migration/src/manager.rs | 1 - migration/src/snapshot.rs | 2 - migration_derive/src/lib.rs | 5 +- ozone/src/capability.rs | 1 - ozone/src/cgroup.rs | 1 - ozone/src/handler.rs | 1 - ozone/src/main.rs | 7 +- pci/src/bus.rs | 2 - pci/src/host.rs | 8 - pci/src/hotplug.rs | 1 - pci/src/lib.rs | 65 +--- pci/src/msix.rs | 3 - pci/src/root_port.rs | 15 +- src/main.rs | 7 +- sysbus/src/lib.rs | 8 +- util/src/aio/libaio.rs | 3 +- util/src/aio/raw.rs | 1 - util/src/bitmap.rs | 1 - util/src/byte_code.rs | 11 +- util/src/leak_bucket.rs | 1 - util/src/lib.rs | 20 +- util/src/loop_context.rs | 3 +- util/src/num_ops.rs | 3 +- util/src/seccomp.rs | 2 - util/src/syscall.rs | 55 --- util/src/tap.rs | 2 - util/src/trace.rs | 1 - util/src/unix.rs | 2 - vfio/src/lib.rs | 10 +- vfio/src/vfio_dev.rs | 2 - vfio/src/vfio_pci.rs | 5 +- virtio/src/balloon.rs | 6 +- virtio/src/block.rs | 4 +- virtio/src/console.rs | 4 +- virtio/src/lib.rs | 14 +- virtio/src/net.rs | 3 - virtio/src/queue.rs | 2 - virtio/src/rng.rs | 3 - virtio/src/vhost/kernel/mod.rs | 6 +- virtio/src/vhost/kernel/net.rs | 2 - virtio/src/vhost/kernel/vsock.rs | 3 - virtio/src/vhost/user/client.rs | 2 - virtio/src/vhost/user/net.rs | 1 - virtio/src/vhost/user/sock.rs | 1 - virtio/src/virtio_mmio.rs | 3 - virtio/src/virtio_pci.rs | 19 +- 114 files changed, 490 insertions(+), 2390 deletions(-) delete mode 100644 devices/src/interrupt_controller/aarch64/gicv2.rs delete mode 100644 machine_manager/src/config/numa.rs delete mode 100644 util/src/syscall.rs diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index b907aa675..3c68a6ac0 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -14,7 +14,6 @@ use std::time::Instant; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; -use log::error; // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 308514fa1..969f49480 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -226,94 +226,6 @@ impl AmlBuilder for AcpiRsdp { } } -/// ACPI SRAT processor affinity structure. -#[repr(C, packed)] -#[derive(Default, Copy, Clone)] -pub struct AcpiSratProcessorAffinity { - /// Type ID. - pub type_id: u8, - /// The length of this structure. - pub length: u8, - /// Bit [7:0] of the proximity domain to which the processor belongs. - pub proximity_lo: u8, - /// The processor local APIC ID. - pub local_apic_id: u8, - /// The processor affinity flags. - pub flags: u32, - /// The processor local SAPIC EID. - pub local_sapic_eid: u8, - /// Bit [31:8] of the proximity domain to which the processor belongs. - pub proximity_hi: [u8; 3], - /// The clock domain to which the processor belongs. - pub clock_domain: u32, -} - -impl ByteCode for AcpiSratProcessorAffinity {} - -impl AmlBuilder for AcpiSratProcessorAffinity { - fn aml_bytes(&self) -> Vec { - Vec::from(self.as_bytes()) - } -} - -/// ACPI SRAT GICC affinity structure. -#[repr(C, packed)] -#[derive(Default, Copy, Clone)] -pub struct AcpiSratGiccAffinity { - /// Type ID. - pub type_id: u8, - /// The length of this structure. - pub length: u8, - /// Represents the proximity domain to which the "range of memory" belongs. - pub proximity_domain: u32, - /// The ACPI processor UID of the associated GICC - pub process_uid: u32, - /// The GICC affinity flags. - pub flags: u32, - /// The clock domain to which the processor belongs. - pub clock_domain: u32, -} - -impl ByteCode for AcpiSratGiccAffinity {} - -impl AmlBuilder for AcpiSratGiccAffinity { - fn aml_bytes(&self) -> Vec { - Vec::from(self.as_bytes()) - } -} - -/// ACPI SRAT memory affinity structure. -#[repr(C, packed)] -#[derive(Default, Copy, Clone)] -pub struct AcpiSratMemoryAffinity { - /// Type ID. - pub type_id: u8, - /// The length of this structure. - pub length: u8, - /// Represents the proximity domain to which the "range of memory" belongs. - pub proximity_domain: u32, - /// Reserved field. - pub reserved1: u16, - /// The base address of the memory range. - pub base_addr: u64, - /// The length of the memory range. - pub range_length: u64, - /// Reserved field. - pub reserved2: u32, - /// Indicates whether memory is enabled and can be hot plugged. - pub flags: u32, - /// Reserved field. - pub reserved3: u64, -} - -impl ByteCode for AcpiSratMemoryAffinity {} - -impl AmlBuilder for AcpiSratMemoryAffinity { - fn aml_bytes(&self) -> Vec { - Vec::from(self.as_bytes()) - } -} - /// This module describes ACPI MADT's sub-tables on x86_64 platform. #[cfg(target_arch = "x86_64")] pub mod madt_subtable { diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index 82688bb81..b691ed9fd 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -10,6 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + mod acpi_device; #[allow(dead_code)] pub mod acpi_table; @@ -32,8 +37,6 @@ pub const ACPI_TABLE_LOADER_FILE: &str = "etc/table-loader"; pub const ACPI_RSDP_FILE: &str = "etc/acpi/rsdp"; pub mod errors { - use error_chain::error_chain; - error_chain! { errors { FileEntryExist(name: String) { diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 3703750e0..6bb2bba55 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -12,7 +12,6 @@ use std::sync::{Arc, Mutex}; -use error_chain::bail; use util::byte_code::ByteCode; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index d2ef05690..1a6b0a839 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -16,22 +16,13 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; use std::thread; -use error_chain::bail; -use log::{error, info}; -use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; -use util::{ - syscall::mbind, - unix::{do_mmap, host_page_size}, -}; +use machine_manager::config::MachineMemConfig; use crate::errors::{Result, ResultExt}; use crate::{AddressRange, GuestAddress}; +use util::unix::{do_mmap, host_page_size}; const MAX_PREALLOC_THREAD: u8 = 16; -/// Verify existing pages in the mapping. -const MPOL_MF_STRICT: u32 = 1; -/// Move pages owned by this process to conform to mapping. -const MPOL_MF_MOVE: u32 = 2; /// FileBackend represents backend-file of `HostMemMapping`. #[derive(Clone)] @@ -287,51 +278,6 @@ pub fn create_host_mmaps( Ok(mappings) } -/// Set host memory backend numa policy. -/// -/// # Arguments -/// -/// * `mem_mappings` - The host virtual address of mapped memory information. -/// * `mem_zones` - Memory zone config. -pub fn set_host_memory_policy( - mem_mappings: &[Arc], - mem_zones: &Option>, -) -> Result<()> { - if mem_zones.is_none() || mem_mappings.is_empty() { - return Ok(()); - } - - let mut host_addr_start = mem_mappings.get(0).map(|m| m.host_address()).unwrap(); - for zone in mem_zones.as_ref().unwrap() { - let mut node_id = if let Some(id) = zone.host_numa_node { - id as usize - } else { - 0_usize - }; - - let mut nmask = vec![0_u64; node_id / 64 + 1]; - nmask[node_id / 64] |= 1_u64 << (node_id % 64); - let policy = HostMemPolicy::from(zone.policy.clone()); - if policy != HostMemPolicy::Default { - // We need to pass node_id + 1 as mbind() max_node argument. - // It is kind of linux bug or feature which will cut off the last node. - node_id += 1; - } - mbind( - host_addr_start, - zone.size, - policy as u32, - nmask, - node_id as u64, - MPOL_MF_STRICT | MPOL_MF_MOVE, - ) - .chain_err(|| "Failed to call mbind")?; - host_addr_start += zone.size; - } - - Ok(()) -} - /// Record information of memory mapping. pub struct HostMemMapping { /// Record the range of one memory segment. @@ -544,7 +490,6 @@ mod test { dump_guest_core: false, mem_share: false, mem_prealloc: false, - mem_zones: None, }; let host_mmaps = create_host_mmaps(&addr_ranges, &mem_config, 1).unwrap(); diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 1077a445e..5b337e215 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -77,6 +77,13 @@ //! } //! ``` +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate migration_derive; + mod address; mod address_space; mod host_mmap; @@ -86,7 +93,7 @@ mod state; pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; -pub use host_mmap::{create_host_mmaps, set_host_memory_policy, FileBackend, HostMemMapping}; +pub use host_mmap::{create_host_mmaps, FileBackend, HostMemMapping}; #[cfg(target_arch = "x86_64")] pub use listener::KvmIoListener; pub use listener::KvmMemoryListener; @@ -94,8 +101,6 @@ pub use listener::{Listener, ListenerReqType}; pub use region::{FlatRange, Region, RegionIoEventFd, RegionType}; pub mod errors { - use error_chain::error_chain; - error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index b9865adec..aa07b215a 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -13,15 +13,14 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; -use error_chain::{bail, ChainedError}; +use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; use kvm_bindings::kvm_userspace_memory_region; use kvm_ioctls::{IoEventAddress, NoDatamatch}; -use log::{debug, warn}; -use util::{num_ops::round_down, unix::host_page_size}; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, FlatRange, RegionIoEventFd, RegionType}; +use util::{num_ops::round_down, unix::host_page_size}; const MEM_READ_ONLY: u32 = 1 << 1; diff --git a/address_space/src/region.rs b/address_space/src/region.rs index f90a88dc2..7c9e8da91 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -13,9 +13,6 @@ use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; -use error_chain::bail; -use log::{debug, warn}; - use crate::address_space::FlatView; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, AddressSpace, FileBackend, GuestAddress, HostMemMapping, RegionOps}; diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 901dc4457..eb34e91ce 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -16,10 +16,8 @@ use std::mem::size_of; use std::sync::Arc; use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; - use migration::errors::{ErrorKind, Result, ResultExt}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::unix::host_page_size; diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index 1b5b3c4b0..c9608cb26 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -17,7 +17,6 @@ use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{errors::ErrorKind as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; -use log::info; use util::byte_code::ByteCode; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index 66ed338bd..9a3426442 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -73,6 +73,11 @@ //! } //! ``` +#[macro_use] +extern crate log; +#[macro_use] +extern crate error_chain; + #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] mod aarch64; @@ -94,8 +99,6 @@ pub use x86_64::X86BootLoader as BootLoader; pub use x86_64::X86BootLoaderConfig as BootLoaderConfig; pub mod errors { - use error_chain::error_chain; - error_chain! { foreign_links { Io(std::io::Error); diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 1435f7235..da0c12383 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -18,12 +18,8 @@ use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; -use error_chain::bail; -use log::info; use util::byte_code::ByteCode; -use self::gdt::setup_gdt; -use self::mptable::setup_isa_mptable; use super::bootparam::{BootParams, RealModeKernelHeader, UNDEFINED_ID}; use super::{X86BootLoader, X86BootLoaderConfig}; use super::{ @@ -31,6 +27,8 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::errors::{ErrorKind, Result, ResultExt}; +use gdt::setup_gdt; +use mptable::setup_isa_mptable; /// Load bzImage linux kernel to Guest Memory. /// diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs index 4cbe22c4f..0c22ced55 100644 --- a/boot_loader/src/x86_64/mod.rs +++ b/boot_loader/src/x86_64/mod.rs @@ -62,7 +62,6 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use devices::legacy::FwCfgOps; -use error_chain::bail; use kvm_bindings::kvm_segment; use crate::errors::Result; diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 17b0406e0..a76e78920 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -16,7 +16,6 @@ use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{FwCfgEntryType, FwCfgOps}; -use error_chain::bail; use util::byte_code::ByteCode; use util::num_ops::round_up; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 705da226a..49e8a0302 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -19,8 +19,6 @@ use std::sync::Arc; use address_space::AddressSpace; use devices::legacy::{FwCfgEntryType, FwCfgOps}; -use error_chain::bail; -use log::{error, info}; use util::byte_code::ByteCode; use self::elf::load_elf_kernel; diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index 4f0726a32..bc46f69a9 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -20,7 +20,6 @@ use kvm_bindings::{ }; use kvm_ioctls::VcpuFd; use util::byte_code::ByteCode; -use util::offset_of; use vmm_sys_util::{ errno, ioctl::{ioctl_with_mut_ref, ioctl_with_ref}, @@ -249,13 +248,13 @@ pub fn set_core_regs(vcpu_fd: &VcpuFd, core_regs: kvm_regs) -> Result<()> { set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpsr.into(), - core_regs.fp_regs.fpsr.as_bytes(), + &core_regs.fp_regs.fpsr.as_bytes().to_vec(), )?; set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpcr.into(), - core_regs.fp_regs.fpcr.as_bytes(), + &core_regs.fp_regs.fpcr.as_bytes().to_vec(), )?; Ok(()) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index c66503f64..ecfdffc40 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -23,14 +23,12 @@ use kvm_bindings::{ use kvm_ioctls::VcpuFd; use vmm_sys_util::fam::FamStructWrapper; -pub use self::caps::ArmCPUCaps; -use self::caps::CpregListEntry; -use self::core_regs::{get_core_regs, set_core_regs}; use crate::errors::{Result, ResultExt}; use crate::CPU; - +pub use caps::ArmCPUCaps; +use caps::CpregListEntry; +use core_regs::{get_core_regs, set_core_regs}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; type CpregList = FamStructWrapper; diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 079989179..092d3d731 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -27,6 +27,17 @@ //! - `x86_64` //! - `aarch64` +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate machine_manager; +#[cfg(target_arch = "aarch64")] +#[macro_use] +extern crate util; +#[macro_use] +extern crate migration_derive; #[cfg(target_arch = "aarch64")] #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] @@ -35,8 +46,6 @@ mod aarch64; mod x86_64; pub mod errors { - use error_chain::error_chain; - error_chain! { foreign_links { Signal(vmm_sys_util::errno::Error); @@ -115,8 +124,6 @@ use std::time::Duration; use kvm_ioctls::{VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; -use log::{error, info, warn}; -use machine_manager::event; use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; use vmm_sys_util::signal::{register_signal_handler, Killable}; diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index c36e42d8d..43ed5e1ea 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -15,20 +15,18 @@ mod cpuid; use std::sync::{Arc, Mutex}; -use error_chain::bail; use kvm_bindings::{ kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; -use util::byte_code::ByteCode; use self::cpuid::host_cpuid; use crate::errors::{Result, ResultExt}; use crate::CPU; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use util::byte_code::ByteCode; const ECX_EPB_SHIFT: u32 = 3; const X86_FEATURE_HYPERVISOR: u32 = 31; diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs deleted file mode 100644 index c15eb48b8..000000000 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::marker::{Send, Sync}; -use std::sync::{Arc, Mutex}; - -use address_space::AddressSpace; -use hypervisor::kvm::KVM_FDS; -use kvm_ioctls::DeviceFd; -use log::error; -use machine_manager::machine::{KvmVmState, MachineLifecycle}; -use util::device_tree::{self, FdtBuilder}; - -use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; -use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; - -// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. -const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; -const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; - -/// Configure a v2 Interrupt controller. -pub struct GICv2Config { - /// GIC distributor address range. - pub dist_range: (u64, u64), - /// GIC cpu interface address range. - pub cpu_range: (u64, u64), - /// GIC v2m range . - pub v2m_range: Option<(u64, u64)>, - /// GIC system memory. - pub sys_mem: Option>, -} - -/// Access wrapper for GICv2. -pub trait GICv2Access { - /// Returns `gicr_attr` of `vCPU`. - fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64; - - fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()>; - - fn access_gic_cpu( - &self, - offset: u64, - cpu: usize, - gicc_value: &mut u64, - write: bool, - ) -> Result<()>; -} - -struct GicCpuInterfaceRegion { - /// Base address. - base: u64, - /// Size of Cpu Interface region. - size: u64, -} - -struct GicDistGuestRegion { - /// Base address. - base: u64, - /// Size of Cpu Interface region. - size: u64, -} - -/// A wrapper around creating and managing a `GICv2`. -pub struct GICv2 { - /// The fd for the GICv2 device. - fd: DeviceFd, - /// Maximum irq number. - nr_irqs: u32, - /// GICv2 cpu interface region. - cpu_interface_region: GicCpuInterfaceRegion, - /// Guest physical address space of the GICv2 distributor register mappings. - dist_guest_region: GicDistGuestRegion, - /// Lifecycle state for GICv2. - state: Arc>, -} - -impl GICv2 { - fn new(config: &GICConfig) -> Result { - let v2config = match config.v2.as_ref() { - Some(v2) => v2, - None => return Err(ErrorKind::InvalidConfig("no v2 config found".to_string()).into()), - }; - - let mut gic_device = kvm_bindings::kvm_create_device { - type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, - fd: 0, - flags: 0, - }; - - let gic_fd = KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_device(&mut gic_device) - .chain_err(|| "Failed to create GICv2 device")?; - - let cpu_interface_region = GicCpuInterfaceRegion { - base: v2config.dist_range.0 + KVM_VGIC_V2_DIST_SIZE, - size: KVM_VGIC_V2_CPU_SIZE, - }; - let dist_guest_region = GicDistGuestRegion { - base: v2config.dist_range.0, - size: v2config.dist_range.1, - }; - - let gicv2 = GICv2 { - fd: gic_fd, - nr_irqs: config.max_irq, - cpu_interface_region, - dist_guest_region, - state: Arc::new(Mutex::new(KvmVmState::Created)), - }; - - Ok(gicv2) - } - - fn realize(&self) -> Result<()> { - KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; - - // Init the interrupt number support by the GIC. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, - 0, - &self.nr_irqs as *const u32 as u64, - true, - ) - .chain_err(|| "Failed to set GICv2 attribute: irqs")?; - - // Finalize the GIC. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), - 0, - true, - ) - .chain_err(|| "KVM failed to initialize GICv2")?; - - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST), - &self.dist_guest_region.base as *const u64 as u64, - true, - ) - .chain_err(|| "Failed to set GICv2 attribute: distributor address")?; - - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU), - &self.cpu_interface_region.base as *const u64 as u64, - true, - ) - .chain_err(|| "Failed to set GICv2 attribute: cpu address")?; - - *self.state.lock().unwrap() = KvmVmState::Running; - - Ok(()) - } - - fn device_fd(&self) -> &DeviceFd { - &self.fd - } -} - -impl MachineLifecycle for GICv2 { - fn pause(&self) -> bool { - // VM change state will flush REDIST pending tables into guest RAM. - if KvmDevice::kvm_device_access( - self.device_fd(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, - 0, - true, - ) - .is_ok() - { - *self.state.lock().unwrap() = KvmVmState::Running; - true - } else { - false - } - } - - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - let state = self.state.lock().unwrap(); - if *state != old { - error!("GICv2 lifecycle error: state check failed."); - return false; - } - drop(state); - - match (old, new) { - (KvmVmState::Running, KvmVmState::Paused) => self.pause(), - _ => true, - } - } -} - -impl GICv2Access for GICv2 { - fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { - (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) - & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK as u64) - | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) - & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) - } - - fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, - offset, - gicd_value as *mut u32 as u64, - write, - ) - .chain_err(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) - } - - fn access_gic_cpu( - &self, - offset: u64, - cpu: usize, - gicc_value: &mut u64, - write: bool, - ) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, - self.vcpu_gicr_attr(offset, cpu), - gicc_value as *mut u64 as u64, - write, - ) - .chain_err(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) - } -} - -impl GICDevice for GICv2 { - fn create_device(gic_conf: &GICConfig) -> Result> { - let gicv2 = Arc::new(GICv2::new(gic_conf)?); - - Ok(gicv2) - } - - fn realize(&self) -> Result<()> { - self.realize().chain_err(|| "Failed to realize GICv2")?; - - Ok(()) - } - - fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()> { - let gic_reg = vec![ - self.dist_guest_region.base, - self.dist_guest_region.size, - self.cpu_interface_region.base, - self.cpu_interface_region.size, - ]; - - let intc_node_dep = fdt.begin_node("intc")?; - fdt.set_property_string("compatible", "arm,cortex-a15-gic")?; - fdt.set_property("interrupt-controller", &Vec::new())?; - fdt.set_property_u32("#interrupt-cells", 0x3)?; - fdt.set_property_u32("phandle", device_tree::GIC_PHANDLE)?; - fdt.set_property_u32("#address-cells", 0x2)?; - fdt.set_property_u32("#size-cells", 0x2)?; - fdt.set_property_array_u64("reg", &gic_reg)?; - - let gic_intr = [ - device_tree::GIC_FDT_IRQ_TYPE_PPI, - 0x9, - device_tree::IRQ_TYPE_LEVEL_HIGH, - ]; - - fdt.set_property_array_u32("interrupts", &gic_intr)?; - - fdt.end_node(intc_node_dep)?; - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use hypervisor::kvm::KVMFds; - use serial_test::serial; - - use super::super::GICVersion; - use super::super::GICv2Config; - use super::*; - - #[test] - #[serial] - fn test_create_gicv2() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let gic_conf = GICConfig { - version: Some(GICVersion::GICv2), - vcpu_count: 4, - max_irq: 192, - v2: Some(GICv2Config { - dist_range: (0x0800_0000, 0x0001_0000), - cpu_range: (0x080A_0000, 0x00F6_0000), - v2m_range: None, - sys_mem: None, - }), - v3: None, - }; - assert!(GICv2::new(&gic_conf).is_ok()); - } -} diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 7cc73e338..79ebb27b3 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -12,15 +12,14 @@ use std::sync::{Arc, Mutex}; +use kvm_ioctls::DeviceFd; + use super::{ state::{GICv3ItsState, GICv3State}, - GICConfig, GICDevice, KvmDevice, UtilResult, + GICConfig, GICDevice, UtilResult, }; use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; - use hypervisor::kvm::KVM_FDS; -use kvm_ioctls::DeviceFd; -use log::error; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use migration::{MigrationManager, MigrationRestoreOrder}; use util::device_tree::{self, FdtBuilder}; @@ -29,16 +28,47 @@ use util::device_tree::{self, FdtBuilder}; const SZ_64K: u64 = 0x0001_0000; const KVM_VGIC_V3_REDIST_SIZE: u64 = 2 * SZ_64K; -/// Configure a v3 Interrupt controller. -pub struct GICv3Config { - /// Config msi support - pub msi: bool, - /// GIC distributor address range. - pub dist_range: (u64, u64), - /// GIC redistributor address range, support multiple redistributor regions. - pub redist_region_ranges: Vec<(u64, u64)>, - /// GIC ITS address ranges. - pub its_range: Option<(u64, u64)>, +/// A wrapper for kvm_based device check and access. +pub struct KvmDevice; + +impl KvmDevice { + fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr: 0, + flags: 0, + }; + fd.has_device_attr(&attr) + .chain_err(|| "Failed to check device attributes for GIC.")?; + Ok(()) + } + + fn kvm_device_access( + fd: &DeviceFd, + group: u32, + attr: u64, + addr: u64, + write: bool, + ) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr, + flags: 0, + }; + + if write { + fd.set_device_attr(&attr) + .chain_err(|| "Failed to set device attributes for GIC.")?; + } else { + let mut attr = attr; + fd.get_device_attr(&mut attr) + .chain_err(|| "Failed to get device attributes for GIC.")?; + }; + + Ok(()) + } } /// Access wrapper for GICv3. @@ -99,10 +129,8 @@ pub struct GICv3 { impl GICv3 { fn new(config: &GICConfig) -> Result { - let v3config = match config.v3.as_ref() { - Some(v3) => v3, - None => return Err(ErrorKind::InvalidConfig("no v3 config found".to_string()).into()), - }; + config.check_sanity()?; + let mut gic_device = kvm_bindings::kvm_create_device { type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, fd: 0, @@ -121,8 +149,8 @@ impl GICv3 { }; // Calculate GIC redistributor regions' address range according to vcpu count. - let base = v3config.redist_region_ranges[0].0; - let size = v3config.redist_region_ranges[0].1; + let base = config.redist_region_ranges[0].0; + let size = config.redist_region_ranges[0].1; let redist_capability = size / KVM_VGIC_V3_REDIST_SIZE; let redist_region_count = std::cmp::min(config.vcpu_count, redist_capability); let mut redist_regions = vec![GicRedistRegion { @@ -132,7 +160,7 @@ impl GICv3 { }]; if config.vcpu_count > redist_capability { - let high_redist_base = v3config.redist_region_ranges[1].0; + let high_redist_base = config.redist_region_ranges[1].0; let high_redist_region_count = config.vcpu_count - redist_capability; let high_redist_attr = (high_redist_region_count << 52) | high_redist_base | 0x1; @@ -149,12 +177,12 @@ impl GICv3 { nr_irqs: config.max_irq, its_dev: None, redist_regions, - dist_base: v3config.dist_range.0, - dist_size: v3config.dist_range.1, + dist_base: config.dist_range.0, + dist_size: config.dist_range.1, state: Arc::new(Mutex::new(KvmVmState::Created)), }; - if let Some(its_range) = v3config.its_range { + if let Some(its_range) = config.its_range { gicv3.its_dev = Some(Arc::new( GICv3Its::new(&its_range).chain_err(|| "Failed to create ITS")?, )); @@ -530,8 +558,7 @@ mod tests { use hypervisor::kvm::KVMFds; use serial_test::serial; - use super::super::GICVersion; - use super::super::GICv3Config; + use super::super::GICConfig; use super::*; #[test] @@ -544,16 +571,13 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_conf = GICConfig { - version: Some(GICVersion::GICv3), + version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), vcpu_count: 4, max_irq: 192, - v2: None, - v3: Some(GICv3Config { - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, - }), + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, }; assert!(GICv3::new(&gic_conf).is_ok()); } @@ -568,16 +592,13 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_config = GICConfig { - version: Some(GICVersion::GICv3), + version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, vcpu_count: 4_u64, max_irq: 192_u32, - v2: None, - v3: Some(GICv3Config { - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, - }), + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, }; let gic = GICv3::new(&gic_config).unwrap(); assert!(gic.its_dev.is_none()); @@ -594,16 +615,13 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_config = GICConfig { - version: Some(GICVersion::GICv3), + version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, vcpu_count: 210_u64, max_irq: 192_u32, - v3: Some(GICv3Config { - msi: true, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], - its_range: Some((0x0808_0000, 0x0002_0000)), - }), - v2: None, + msi: true, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], + its_range: Some((0x0808_0000, 0x0002_0000)), }; let gic = GICv3::new(&gic_config).unwrap(); diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index d45bd063b..c4f5aa0ba 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -10,15 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod gicv2; mod gicv3; #[allow(dead_code)] mod state; -use kvm_ioctls::DeviceFd; - -pub use gicv2::{GICv2, GICv2Config}; -pub use gicv3::{GICv3, GICv3Config}; +pub use gicv3::GICv3; use std::sync::Arc; @@ -33,75 +29,42 @@ use super::errors::{ErrorKind, Result, ResultExt}; // First 32 are private to each CPU (SGIs and PPIs). pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; -/// GIC version type. -pub enum GICVersion { - GICv2, - GICv3, -} - -/// A wrapper for kvm_based device check and access. -pub struct KvmDevice; - -impl KvmDevice { - fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr: 0, - flags: 0, - }; - fd.has_device_attr(&attr) - .chain_err(|| "Failed to check device attributes for GIC.")?; - Ok(()) - } - - fn kvm_device_access( - fd: &DeviceFd, - group: u32, - attr: u64, - addr: u64, - write: bool, - ) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr, - flags: 0, - }; - - if write { - fd.set_device_attr(&attr) - .chain_err(|| "Failed to set device attributes for GIC.")?; - } else { - let mut attr = attr; - fd.get_device_attr(&mut attr) - .chain_err(|| "Failed to get device attributes for GIC.")?; - }; - - Ok(()) - } -} - +/// Configure a Interrupt controller. pub struct GICConfig { /// Config GIC version - pub version: Option, + pub version: u32, /// Config number of CPUs handled by the device pub vcpu_count: u64, /// Config maximum number of irqs handled by the device pub max_irq: u32, - /// v2 config. - pub v2: Option, - /// v3 config. - pub v3: Option, + /// Config msi support + pub msi: bool, + /// GIC distributor address range. + pub dist_range: (u64, u64), + /// GIC redistributor address range, support multiple redistributor regions. + pub redist_region_ranges: Vec<(u64, u64)>, + /// GIC ITS address ranges. + pub its_range: Option<(u64, u64)>, } impl GICConfig { fn check_sanity(&self) -> Result<()> { + if self.version != kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 { + return Err(ErrorKind::InvalidConfig("GIC only support GICv3".to_string()).into()); + }; + + if self.vcpu_count > 256 || self.vcpu_count == 0 { + return Err( + ErrorKind::InvalidConfig("GIC only support maximum 256 vcpus".to_string()).into(), + ); + } + if self.max_irq <= GIC_IRQ_INTERNAL { return Err( ErrorKind::InvalidConfig("GIC irq numbers need above 32".to_string()).into(), ); } + Ok(()) } } @@ -143,17 +106,9 @@ impl InterruptController { /// /// * `gic_conf` - Configuration for `GIC`. pub fn new(gic_conf: &GICConfig) -> Result { - gic_conf.check_sanity()?; - let gic = match &gic_conf.version { - Some(GICVersion::GICv3) => GICv3::create_device(gic_conf), - Some(GICVersion::GICv2) => GICv2::create_device(gic_conf), - // Try v3 first if no version specified. - None => GICv3::create_device(gic_conf).or_else(|_| GICv2::create_device(gic_conf)), - }; - let intc = InterruptController { - gic: gic.chain_err(|| "Failed to realize GIC")?, - }; - Ok(intc) + Ok(InterruptController { + gic: GICv3::create_device(gic_conf).chain_err(|| "Failed to realize GIC")?, + }) } pub fn realize(&self) -> Result<()> { @@ -182,14 +137,30 @@ mod tests { #[test] fn test_gic_config() { let mut gic_conf = GICConfig { - version: Some(GICVersion::GICv3), + version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), vcpu_count: 4, max_irq: 192, - v2: None, - v3: None, + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, }; assert!(gic_conf.check_sanity().is_ok()); + gic_conf.version = 3; + assert!(gic_conf.check_sanity().is_err()); + gic_conf.version = kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(); + assert!(gic_conf.check_sanity().is_ok()); + + gic_conf.vcpu_count = 257; + assert!(gic_conf.check_sanity().is_err()); + gic_conf.vcpu_count = 0; + assert!(gic_conf.check_sanity().is_err()); + gic_conf.vcpu_count = 24; + assert!(gic_conf.check_sanity().is_ok()); + + assert!(gic_conf.check_sanity().is_ok()); + gic_conf.max_irq = 32; assert!(gic_conf.check_sanity().is_err()); } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 2f54c1b6f..d4da00bd6 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -12,13 +12,12 @@ use std::mem::size_of; +use libc::c_uint; + use super::gicv3::{GICv3, GICv3Access, GICv3Its}; use super::GIC_IRQ_INTERNAL; use crate::interrupt_controller::errors::Result; - -use libc::c_uint; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; /// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index dbaa81b25..e17d5c479 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -29,17 +29,11 @@ mod aarch64; #[cfg(target_arch = "aarch64")] -pub use aarch64::GICConfig as ICGICConfig; -#[cfg(target_arch = "aarch64")] -pub use aarch64::GICv2Config as ICGICv2Config; -#[cfg(target_arch = "aarch64")] -pub use aarch64::GICv3Config as ICGICv3Config; +pub use aarch64::GICConfig as InterruptControllerConfig; #[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; pub mod errors { - use error_chain::error_chain; - error_chain! { errors { #[cfg(target_arch = "aarch64")] diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index f8a8e685b..1f7b4a159 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -17,9 +17,7 @@ use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use error_chain::bail; use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; -use log::{error, info}; use machine_manager::machine::{PathInfo, PTY_PATH}; use machine_manager::{ config::{ChardevConfig, ChardevType}, diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index a77f3047f..df613e43d 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -23,8 +23,7 @@ use address_space::{AddressSpace, GuestAddress}; #[cfg(target_arch = "x86_64")] use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; -use error_chain::{bail, ChainedError}; -use log::{error, warn}; +use error_chain::ChainedError; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; @@ -506,42 +505,6 @@ impl FwCfgCommon { Ok(()) } - fn modify_file_callback( - &mut self, - filename: &str, - data: Vec, - select_cb: Option, - write_cb: Option, - allow_write: bool, - ) -> Result<()> { - if self.files.len() >= self.file_slots as usize { - return Err(ErrorKind::FileSlotsNotAvailable(filename.to_owned()).into()); - } - - let file_name_bytes = filename.to_string().as_bytes().to_vec(); - // Make sure file entry is existed. - let index = self - .files - .iter() - .position(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) - .ok_or_else(|| ErrorKind::EntryNotFound(filename.to_owned()))?; - self.files[index].size = data.len() as u32; - - // Update FileDir entry - let mut bytes = Vec::new(); - let file_length_be = BigEndian::read_u32((self.files.len() as u32).as_bytes()); - bytes.append(&mut file_length_be.as_bytes().to_vec()); - for value in self.files.iter() { - bytes.append(&mut value.as_be_bytes()); - } - self.update_entry_data(FwCfgEntryType::FileDir as u16, bytes)?; - - // Update File entry - self.entries[FW_CFG_FILE_FIRST as usize + index] = - FwCfgEntry::new(data, select_cb, write_cb, allow_write); - Ok(()) - } - // Fetch FwCfgDma request from guest and handle it fn handle_dma_request(&mut self) -> Result<()> { let dma_addr = self.dma_addr; @@ -1231,17 +1194,6 @@ pub trait FwCfgOps { self.fw_cfg_common() .add_file_callback(filename, data, None, None, true) } - - /// Modify a file entry to FwCfg device, without callbacks, write-allow. - /// - /// # Arguments - /// - /// * `filename` - Name of the file - /// * `data` - Raw data bytes of the file to be modified - fn modify_file_entry(&mut self, filename: &str, data: Vec) -> Result<()> { - self.fw_cfg_common() - .modify_file_callback(filename, data, None, None, true) - } } #[cfg(target_arch = "aarch64")] @@ -1424,38 +1376,6 @@ mod test { let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); - let file_data = Vec::new(); - assert_eq!( - fwcfg_common - .add_file_callback("bootorder", file_data.clone(), None, None, false) - .is_ok(), - true - ); - let id = fwcfg_common.files[0].select; - fwcfg_common.select_entry(id); - let entry = fwcfg_common.get_entry_mut().unwrap(); - assert_eq!(entry.data, file_data); - - let modify_file_data = "/pci@ffffffffffffffff/pci-bridge@3/scsi@0,1/disk@0,0" - .as_bytes() - .to_vec(); - assert_eq!( - fwcfg_common - .modify_file_callback("bootorder", modify_file_data.clone(), None, None, true) - .is_ok(), - true - ); - let id = fwcfg_common.files[0].select; - fwcfg_common.select_entry(id); - let entry = fwcfg_common.get_entry_mut().unwrap(); - assert_eq!(entry.data, modify_file_data); - - assert_eq!( - fwcfg_common - .modify_file_callback("abc", modify_file_data.clone(), None, None, true) - .is_err(), - true - ); let file_data = vec![0x33; 500]; assert_eq!( fwcfg_common diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index bd4d9474e..6835a9214 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -26,8 +26,6 @@ //! - `aarch64` pub mod errors { - use error_chain::error_chain; - error_chain! { links { SysBus(sysbus::errors::Error, sysbus::errors::ErrorKind); diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 6b12749c4..36f708dba 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -17,8 +17,7 @@ use std::sync::{Arc, Mutex}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::{bail, ChainedError}; -use log::{debug, error, warn}; +use error_chain::ChainedError; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::num_ops::{deposit_u32, extract_u32}; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 84c31bf31..34a5114ef 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -19,13 +19,11 @@ use acpi::{ }; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 897c71919..e7f671506 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -16,9 +16,7 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use acpi::AmlBuilder; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; -use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index c87d7036c..a549bfb03 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -17,7 +17,6 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; -use log::{debug, error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index cc2abd679..11e3c1b2e 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -19,14 +19,11 @@ use acpi::{ AmlResourceUsage, AmlScopeBuilder, }; use address_space::GuestAddress; -use error_chain::bail; use hypervisor::kvm::KVM_FDS; -use log::error; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 849ac5394..09423adbd 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -16,11 +16,18 @@ //! - interrupt controller (aarch64) //! - legacy devices, such as serial devices +#[macro_use] +extern crate log; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate migration_derive; + mod interrupt_controller; pub mod legacy; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ - errors as IntCtrlErrs, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, + errors as IntCtrlErrs, InterruptController, InterruptControllerConfig, }; pub use legacy::errors as LegacyErrs; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 3048524cb..c902cf8a3 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -7,7 +7,7 @@ StratoVirt can only be launched via cmdline arguments. ### 1.1 Machine Config General configuration of machine, including -* type: The type of machine, three types of machine are available: "none", "microvm", +* type: The type of machine, three types of machine are available: "none", "microvm", "q35"(x86_64 platform) and "virt" (aarch64 platform). * dump-guest-core: Including guest memory in coredump file or not, default value is true. * mem-share: Guest memory is sharable with other processes or not. @@ -65,7 +65,7 @@ Default VM memory size is 256M. The supported VM memory size is among [256M, 512 ``` #### 1.3.2 Memory Prealloc -Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure +Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure memory prealloc. ```shell @@ -104,41 +104,7 @@ $ cat /proc/meminfo ... -mem-path /path/to/hugepages ... ``` -### 1.5 NUMA node -The optional NUMA node element gives the opportunity to create a virtual machine with non-uniform memory accesses. -The application of NUMA node is that one region of memory can be set as fast memory, another can be set as slow memory. - -Each NUMA node is given a list of command lines option, there will be described in detail below. -1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] - It describes the size and id of each memory zone, the policy of binding to host memory node. - you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. - The optional policies are default, preferred, bind and interleave. -2. -numa node,cpus=0-1,memdev=mem0 - It describes id and cpu set of the NUMA node, and the id belongs to which memory zone. -3. -numa dist,src=0,dst=0,val=10 - It describes the distance between source and destination. The default of source to source is 10, - source to destination is 20. And if you choose not to set these parameters, the VM will set the default values. - -The following command shows how to set NUMA node: - -```shell -# The number of cpu must be set to be the same as numa node cpu. --smp 4 - -# The memory size must be set to be the same as numa node mem. --m 4G - --object memory-backend-ram,size=2G,id=mem0,[host-nodes=0,policy=bind] --object memory-backend-ram,size=2G,id=mem1,[host-nodes=1,policy=bind] --numa node,nodeid=0,cpus=0-1,memdev=mem0 --numa node,nodeid=1,cpus=2-3,memdev=mem1 -[-numa dist,src=0,dst=0,val=10] -[-numa dist,src=0,dst=1,val=20] -[-numa dist,src=1,dst=0,val=20] -[-numa dist,src=1,dst=1,val=10] -``` - -### 1.6 Kernel and Kernel Parameters +### 1.5 Kernel and Kernel Parameters StratoVirt supports to launch PE or bzImage (only x86_64) format linux kernel 4.19 and can also set kernel parameters for VM. @@ -153,7 +119,7 @@ And the given kernel parameters will be actually analyzed by boot loader. -append "console=ttyS0 rebook=k panic=1 pci=off tsc=reliable ipv6.disable=1" ``` -### 1.7 Initrd Configuration +### 1.6 Initrd Configuration StratoVirt supports to launch VM by a initrd (boot loader initialized RAM disk) as well. @@ -166,7 +132,7 @@ If you want to use initrd as rootfs, `root=/dev/ram` and `rdinit=/bin/sh` must b -initrd /path/to/initrd ``` -### 1.8 Global config +### 1.7 Global config Users can set the global configuration using the -global parameter. @@ -178,7 +144,7 @@ One property can be set: -global pcie-root-port.fast-unplug=1 ``` -### 1.9 Logging +### 1.8 Logging StratoVirt supports to output log to stderr and log file. @@ -194,7 +160,7 @@ You can enable StratoVirt's logging by: StratoVirt's log-level depends on env `STRATOVIRT_LOG_LEVEL`. StratoVirt supports five log-levels: `trace`, `debug`, `info`, `warn`, `error`. The default level is `error`. -### 1.10 Daemonize +### 1.9 Daemonize StratoVirt supports to run as a daemon. @@ -253,9 +219,6 @@ Nine properties are supported for virtio block device. If not set, default is raw. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, the default block queue number is 1. -* bootindex: the boot order of block device. (optional) If not set, the priority is lowest. -The number ranges from 1 to 255, the smaller the number, the higher the priority. -It determines the order of bootable devices which firmware will use for booting the guest OS. For virtio-blk-pci, two more properties are required. * bus: name of bus which to attach. @@ -271,7 +234,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo -device virtio-blk-device,drive=drive_id,id=blkid[,iothread=iothread1,serial=serial_num] # virtio pci block device. -drive id=drive_id,file=path_on_host[,readonly=off,direct=off,throttling.iops-total=200] --device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N,bootindex=1] +-device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N] ``` ### 2.3 Virtio-net @@ -282,10 +245,10 @@ Six properties are supported for netdev. * tap/vhost-user: the type of net device. NB: currently only tap and vhost-user is supported. * id: unique netdev id. * ifname: name of tap device in host. -* fd: the file descriptor of opened tap device. +* fd: the file descriptor of opened tap device. * fds: file descriptors of opened tap device. * queues: the optional queues attribute controls the number of queues to be used for either multiple queue virtio-net or vhost-net device. -NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, +NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, the tap device would be created according to `ifname`. Eight properties are supported for virtio-net-device or virtio-net-pci. @@ -302,7 +265,7 @@ It has no effect when vhost is set. Two more properties are supported for virtio pci net device. * bus: name of bus which to attach. * addr: including slot number and function number. The first number represents slot number -of device and the second one represents function number of it. For virtio pci net device, it +of device and the second one represents function number of it. For virtio pci net device, it is a single function device, the function number should be set to zero. ```shell @@ -314,10 +277,10 @@ is a single function device, the function number should be set to zero. -device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC,mq=on] ``` -StratoVirt also supports vhost-net to get a higher performance in network. It can be set by +StratoVirt also supports vhost-net to get a higher performance in network. It can be set by giving `vhost` property, and one more property is supported for vhost-net device. -* vhostfd: fd for vhost-net device, it could be configured when `vhost=on`. If this argument is not +* vhostfd: fd for vhost-net device, it could be configured when `vhost=on`. If this argument is not given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatically. ```shell @@ -553,7 +516,7 @@ Four parameters are supported for pcie root port. * addr: including slot number and function number. * id: the name of secondary bus. * chassis: the number of chassis. Interconnect with libvirt only.(optional). -* multifunction: whether to open multi function for pcie root port.(optional). +* multifunction: whether to open multi function for pcie root port.(optional). If not set, default value is false. ```shell @@ -676,9 +639,9 @@ $ ./stratovirt \ * incoming: the path of the template. See [Snapshot and Restore](./snapshot.md) for details. - + ## 6. Ozone -Ozone is a lightweight secure sandbox for StratoVirt, it provides secure environment for StratoVirt +Ozone is a lightweight secure sandbox for StratoVirt, it provides secure environment for StratoVirt by limiting resources of StratoVirt using 'namespace'. Please run ozone with root permission. ### 6.1 Usage @@ -715,7 +678,7 @@ About the arguments: ### 6.2 Example As ozone uses a directory to mount as a root directory, after ozone is launched, the directory "/srv/zozne/{exec_file}/{name}" will be created. (Where, `exec_file` is the executable binary file, usually it is `stratovirt`, while `name` is the name of ozone, it is given by users, but the length of it should be no more than 255 bytes.) In order to run ozone normally, please make sure that the directory "/srv/zozne/{exec_file}/{name}" does not exists before launching ozone. -On top of that, the path-related arguments are different. They are all in the current(`./`) directory. +On top of that, the path-related arguments are different. They are all in the current(`./`) directory. For net name space, it can be created by the following command with name "mynet": ```shell diff --git a/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 b/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 index 85b78de4b..c2159dd40 100644 --- a/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 +++ b/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 @@ -1409,15 +1409,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -CONFIG_IGB=y -CONFIG_IGBVF=y +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -CONFIG_IXGBEVF=y +# CONFIG_IXGBEVF is not set CONFIG_I40E=y # CONFIG_I40E_DCB is not set -CONFIG_I40EVF=y +# CONFIG_I40EVF is not set # CONFIG_ICE is not set # CONFIG_FM10K is not set CONFIG_NET_VENDOR_NETSWIFT=y diff --git a/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 b/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 index 6ed9be72f..b817bc9b0 100644 --- a/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 +++ b/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 @@ -1368,15 +1368,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -CONFIG_IGB=y -CONFIG_IGBVF=y +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -CONFIG_IXGBEVF=y +# CONFIG_IXGBEVF is not set CONFIG_I40E=y # CONFIG_I40E_DCB is not set -CONFIG_I40EVF=y +# CONFIG_I40EVF is not set # CONFIG_ICE is not set # CONFIG_FM10K is not set CONFIG_NET_VENDOR_NETSWIFT=y diff --git a/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 b/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 index 4ec28e45d..3d89d8a96 100644 --- a/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 +++ b/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 @@ -1463,15 +1463,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -CONFIG_IGB=y -CONFIG_IGBVF=y +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -CONFIG_IXGBEVF=y +# CONFIG_IXGBEVF is not set CONFIG_I40E=y # CONFIG_I40E_DCB is not set -CONFIG_I40EVF=y +# CONFIG_I40EVF is not set # CONFIG_ICE is not set # CONFIG_FM10K is not set # CONFIG_IGC is not set diff --git a/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 b/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 index d52a12d6a..bcc32e783 100644 --- a/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 +++ b/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 @@ -1399,15 +1399,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -CONFIG_IGB=y -CONFIG_IGBVF=y +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -CONFIG_IXGBEVF=y +# CONFIG_IXGBEVF is not set CONFIG_I40E=y # CONFIG_I40E_DCB is not set -CONFIG_I40EVF=y +# CONFIG_I40EVF is not set # CONFIG_ICE is not set # CONFIG_FM10K is not set # CONFIG_IGC is not set diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 33596a1db..8c27cc0a9 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -17,7 +17,6 @@ use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; use util::bitmap::Bitmap; use vmm_sys_util::ioctl::ioctl_with_val; -use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; use crate::errors::{Result, ResultExt}; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index d88e9f88c..80012bbfd 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -22,12 +22,9 @@ use arc_swap::ArcSwap; use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; -use log::error; use once_cell::sync::Lazy; + use vmm_sys_util::eventfd::EventFd; -use vmm_sys_util::{ - ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, -}; use crate::errors::{Result, ResultExt}; #[cfg(target_arch = "x86_64")] diff --git a/hypervisor/src/kvm/state.rs b/hypervisor/src/kvm/state.rs index ec61cb23c..f38ad064b 100644 --- a/hypervisor/src/kvm/state.rs +++ b/hypervisor/src/kvm/state.rs @@ -13,7 +13,6 @@ use kvm_bindings::{ kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, }; -use migration_derive::{ByteCode, Desc}; use super::KVM_FDS; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index b9acc6904..9fb446d61 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -12,10 +12,18 @@ //! This crate offers interfaces for different kinds of hypervisors, such as KVM. +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate vmm_sys_util; +#[cfg(target_arch = "x86_64")] +#[macro_use] +extern crate migration_derive; + #[allow(clippy::upper_case_acronyms)] pub mod errors { - use error_chain::error_chain; - error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 69c25b0c9..07c7b7354 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,9 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate machine_manager; +#[cfg(target_arch = "x86_64")] +#[macro_use] +extern crate vmm_sys_util; +pub mod errors { error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -98,35 +106,29 @@ mod standard_vm; pub use micro_vm::LightMachine; use pci::{PciBus, PciDevOps, PciHost, RootPort}; pub use standard_vm::StdMachine; -use sysbus::{SysBus, SysBusDevOps}; use virtio::{ BlockState, RngState, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioState, VirtioNetState, }; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; -use address_space::{ - create_host_mmaps, set_host_memory_policy, AddressSpace, KvmMemoryListener, Region, -}; +use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; -use error_chain::bail; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::VcpuFd; use machine_manager::config::{ - check_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, - parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, MachineMemConfig, - NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, - VfioConfig, VmConfig, FAST_UNPLUG_ON, + get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, + parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, + parse_vsock, MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, + FAST_UNPLUG_ON, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; @@ -180,8 +182,6 @@ pub trait MachineOps { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) .chain_err(|| "Failed to mmap guest ram.")?; - set_host_memory_policy(&mem_mappings, &mem_config.mem_zones) - .chain_err(|| "Failed to set host memory NUMA policy.")?; } sys_mem @@ -471,36 +471,6 @@ pub trait MachineOps { bail!("No pci host found"); } - fn get_sys_bus(&mut self) -> &SysBus; - - fn get_fwcfg_dev(&mut self) -> Result>> { - bail!("No FwCfg deivce found"); - } - - fn get_boot_order_list(&self) -> Option>>> { - None - } - - fn reset_all_devices(&mut self) -> Result<()> { - let sysbus = self.get_sys_bus(); - for dev in sysbus.devices.iter() { - dev.lock() - .unwrap() - .reset() - .chain_err(|| "Fail to reset sysbus device")?; - } - - if let Ok(pci_host) = self.get_pci_host() { - pci_host - .lock() - .unwrap() - .reset() - .chain_err(|| "Fail to reset pci host")?; - } - - Ok(()) - } - fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { @@ -515,88 +485,12 @@ pub trait MachineOps { Ok(()) } - fn reset_fwcfg_boot_order(&mut self) -> Result<()> { - // unwrap is safe because stand machine always make sure it not return null. - let boot_order_vec = self.get_boot_order_list().unwrap(); - let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone(); - locked_boot_order_vec.sort_by(|x, y| x.boot_index.cmp(&y.boot_index)); - let mut fwcfg_boot_order_string = String::new(); - for item in &locked_boot_order_vec { - fwcfg_boot_order_string.push_str(&item.dev_path); - fwcfg_boot_order_string.push('\n'); - } - fwcfg_boot_order_string.push('\0'); - - let fwcfg = self.get_fwcfg_dev()?; - fwcfg - .lock() - .unwrap() - .modify_file_entry("bootorder", fwcfg_boot_order_string.as_bytes().to_vec()) - .chain_err(|| "Fail to add bootorder entry for standard VM.")?; - Ok(()) - } - - /// Add boot index of device. - /// - /// # Arguments - /// - /// * `bootindex` - The boot index of the device. - /// * `dev_path` - The firmware device path of the device. - /// * `dev_id` - The id of the device. - fn add_bootindex_devices( - &mut self, - boot_index: u8, - dev_path: &str, - dev_id: &str, - ) -> Result<()> { - // Unwrap is safe because StdMachine will overwrite this function, - // which ensure boot_order_list is not None. - let boot_order_list = self.get_boot_order_list().unwrap(); - if boot_order_list - .lock() - .unwrap() - .iter() - .any(|item| item.boot_index == boot_index) - { - bail!("Failed to add duplicated bootindex {}.", boot_index); - } - - boot_order_list.lock().unwrap().push(BootIndexInfo { - boot_index, - id: dev_id.to_string(), - dev_path: dev_path.to_string(), - }); - - Ok(()) - } - - /// Delete boot index of device. - /// - /// # Arguments - /// - /// * `dev_id` - The id of the device. - fn del_bootindex_devices(&self, dev_id: &str) { - // Unwrap is safe because StdMachine will overwrite this function, - // which ensure boot_order_list is not None. - let boot_order_list = self.get_boot_order_list().unwrap(); - let mut locked_boot_order_list = boot_order_list.lock().unwrap(); - locked_boot_order_list.retain(|item| item.id != dev_id); - } - fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_blk(vm_config, cfg_args)?; let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); - let pci_dev = self - .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) - .chain_err(|| "Failed to add virtio pci device")?; - if let Some(bootindex) = device_cfg.boot_index { - if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { - self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id) - .chain_err(|| "Fail to add boot index")?; - } - } + self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false)?; MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), device, @@ -711,7 +605,7 @@ pub trait MachineOps { device: Arc>, multi_func: bool, need_irqfd: bool, - ) -> Result>> { + ) -> Result<()> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; let sys_mem = self.get_sys_mem(); let mut pcidev = VirtioPciDevice::new( @@ -725,11 +619,10 @@ pub trait MachineOps { if need_irqfd { pcidev.enable_need_irqfd(); } - let clone_pcidev = Arc::new(Mutex::new(pcidev.clone())); pcidev .realize() .chain_err(|| "Failed to add virtio pci device")?; - Ok(clone_pcidev) + Ok(()) } /// Set the parent bus slot on when device attached @@ -791,72 +684,6 @@ pub trait MachineOps { Ok(()) } - /// Add numa nodes information to standard machine. - /// - /// # Arguments - /// - /// * `vm_config` - VM Configuration. - fn add_numa_nodes(&mut self, vm_config: &mut VmConfig) -> Result> { - if vm_config.numa_nodes.is_empty() { - return Ok(None); - } - - let mut numa_nodes: NumaNodes = BTreeMap::new(); - vm_config.numa_nodes.sort_by(|p, n| n.0.cmp(&p.0)); - for numa in vm_config.numa_nodes.iter() { - match numa.0.as_str() { - "node" => { - let numa_config: NumaConfig = parse_numa_mem(numa.1.as_str())?; - if numa_nodes.contains_key(&numa_config.numa_id) { - bail!("Numa node id is repeated {}", numa_config.numa_id); - } - let mut numa_node = NumaNode { - cpus: numa_config.cpus, - ..Default::default() - }; - - if let Some(object_cfg) = vm_config.object.remove(&numa_config.mem_dev) { - if let ObjConfig::Zone(zone_config) = object_cfg { - numa_node.size = zone_config.size; - } - } else { - bail!( - "Object for memory-backend-ram {} config not found", - numa_config.mem_dev - ); - } - numa_nodes.insert(numa_config.numa_id, numa_node); - } - "dist" => { - let dist: (u32, NumaDistance) = parse_numa_distance(numa.1.as_str())?; - if !numa_nodes.contains_key(&dist.0) { - bail!("Numa node id is not found {}", dist.0); - } - if !numa_nodes.contains_key(&dist.1.destination) { - bail!("Numa node id is not found {}", dist.1.destination); - } - - if let Some(n) = numa_nodes.get_mut(&dist.0) { - if n.distances.contains_key(&dist.1.destination) { - bail!( - "Numa destination info {} repeat settings", - dist.1.destination - ); - } - n.distances.insert(dist.1.destination, dist.1.distance); - } - } - _ => { - bail!("Unsupported args for NUMA node: {}", numa.0.as_str()); - } - } - } - - check_numa_node(&numa_nodes, vm_config)?; - - Ok(Some(numa_nodes)) - } - /// Add peripheral devices. /// /// # Arguments diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d655af024..189e8acea 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -29,8 +29,6 @@ //! - `aarch64` pub mod errors { - use error_chain::error_chain; - error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); @@ -75,7 +73,7 @@ use devices::legacy::PL031; use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; #[cfg(target_arch = "aarch64")] -use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController}; +use devices::{InterruptController, InterruptControllerConfig}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] @@ -106,15 +104,11 @@ use virtio::{ }; use vmm_sys_util::eventfd::EventFd; -use self::errors::{ErrorKind, Result}; use super::{ errors::{ErrorKind as MachineErrorKind, Result as MachineResult}, MachineOps, }; - -use error_chain::bail; -use log::error; -use machine_manager::event; +use errors::{ErrorKind, Result}; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use syscall::syscall_whitelist; @@ -500,7 +494,10 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "aarch64")] fn init_interrupt_controller(&mut self, vcpu_count: u64) -> MachineResult<()> { // Interrupt Controller Chip init - let v3 = ICGICv3Config { + let intc_conf = InterruptControllerConfig { + version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + vcpu_count, + max_irq: 192, msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], redist_region_ranges: vec![ @@ -509,20 +506,6 @@ impl MachineOps for LightMachine { ], its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), }; - let v2 = ICGICv2Config { - dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], - cpu_range: MEM_LAYOUT[LayoutEntryType::GicCpu as usize], - v2m_range: None, - sys_mem: None, - }; - // Passing both v2 and v3, leave GIC self to decide which one to use. - let intc_conf = ICGICConfig { - version: None, - vcpu_count, - max_irq: 192, - v3: Some(v3), - v2: Some(v2), - }; let irq_chip = InterruptController::new(&intc_conf)?; self.irq_chip = Some(Arc::new(irq_chip)); self.irq_chip.as_ref().unwrap().realize()?; @@ -625,10 +608,6 @@ impl MachineOps for LightMachine { &self.sys_mem } - fn get_sys_bus(&mut self) -> &SysBus { - &self.sysbus - } - #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { use crate::errors::ResultExt; @@ -955,7 +934,7 @@ impl DeviceInterface for LightMachine { current: true, qom_path: String::from("/machine/unattached/device[") + &cpu_index.to_string() - + "]", + + &"]".to_string(), halted: false, props: Some(cpu_instance), CPU: cpu_index as isize, @@ -1148,7 +1127,6 @@ impl DeviceInterface for LightMachine { iothread: None, iops: None, queues: 1, - boot_index: None, }; match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 25eaa1b55..07a2fb8a0 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,19 +14,18 @@ mod pci_host_root; mod syscall; use std::fs::OpenOptions; -use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use acpi::{ - AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, - AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, - AmlScopeBuilder, AmlString, TableLoader, ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, - ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, - ACPI_GTDT_CAP_ALWAYS_ON, ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, - ACPI_IORT_NODE_PCI_ROOT_COMPLEX, ACPI_MADT_GENERIC_CPU_INTERFACE, - ACPI_MADT_GENERIC_DISTRIBUTOR, ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, - ARCH_GIC_MAINT_IRQ, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, + AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiTable, AmlBuilder, + AmlDevice, AmlInteger, AmlNameDecl, AmlScope, AmlScopeBuilder, AmlString, TableLoader, + ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, + ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, ACPI_GTDT_CAP_ALWAYS_ON, + ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, + ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, + ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, + INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -34,14 +33,10 @@ use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; -use devices::{ICGICConfig, ICGICv3Config, InterruptController}; -use error_chain::{bail, ChainedError}; +use devices::{InterruptController, InterruptControllerConfig}; +use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; -use log::error; -use machine_manager::config::{ - BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, -}; -use machine_manager::event; +use machine_manager::config::{BootSource, PFlashConfig, SerialConfig, VmConfig}; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, @@ -49,7 +44,7 @@ use machine_manager::machine::{ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::{SysBus, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -130,12 +125,6 @@ pub struct StdMachine { reset_req: EventFd, /// Device Tree Blob. dtb_vec: Vec, - /// List of guest NUMA nodes information. - numa_nodes: Option, - /// List contains the boot order of boot devices. - boot_order_list: Arc>>, - /// FwCfg device. - fwcfg_dev: Option>>, } impl StdMachine { @@ -181,16 +170,13 @@ impl StdMachine { reset_req: EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| ErrorKind::InitEventFdErr("reset_req".to_string()))?, dtb_vec: Vec::new(), - numa_nodes: None, - boot_order_list: Arc::new(Mutex::new(Vec::new())), - fwcfg_dev: None, }) } pub fn handle_reset_request(vm: &Arc>) -> Result<()> { use crate::errors::ResultExt; - let mut locked_vm = vm.lock().unwrap(); + let locked_vm = vm.lock().unwrap(); let mut fdt_addr: u64 = 0; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { @@ -215,12 +201,18 @@ impl StdMachine { ) .chain_err(|| "Fail to write dtb into sysmem")?; + for dev in locked_vm.sysbus.devices.iter() { + dev.lock() + .unwrap() + .reset() + .chain_err(|| "Fail to reset sysbus device")?; + } locked_vm - .reset_all_devices() - .chain_err(|| "Fail to reset all devices")?; - locked_vm - .reset_fwcfg_boot_order() - .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + .pci_host + .lock() + .unwrap() + .reset() + .chain_err(|| "Fail to reset pci host")?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.resume() @@ -294,7 +286,6 @@ impl StdMachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, ) .chain_err(|| "Failed to realize fwcfg device")?; - self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) } @@ -314,10 +305,6 @@ impl StdMachineOps for StdMachine { fn get_vm_config(&self) -> &Mutex { &self.vm_config } - - fn get_numa_nodes(&self) -> &Option { - &self.numa_nodes - } } impl MachineOps for StdMachine { @@ -326,7 +313,10 @@ impl MachineOps for StdMachine { } fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { - let v3 = ICGICv3Config { + let intc_conf = InterruptControllerConfig { + version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + vcpu_count, + max_irq: 192, msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], redist_region_ranges: vec![ @@ -335,13 +325,6 @@ impl MachineOps for StdMachine { ], its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), }; - let intc_conf = ICGICConfig { - version: None, - vcpu_count, - max_irq: 192, - v2: None, - v3: Some(v3), - }; let irq_chip = InterruptController::new(&intc_conf)?; self.irq_chip = Some(Arc::new(irq_chip)); self.irq_chip.as_ref().unwrap().realize()?; @@ -425,7 +408,6 @@ impl MachineOps for StdMachine { locked_vm .register_reset_event(&locked_vm.reset_req, clone_vm) .chain_err(|| "Fail to register reset event")?; - locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_mem, @@ -464,10 +446,6 @@ impl MachineOps for StdMachine { (None, None) }; - locked_vm - .reset_fwcfg_boot_order() - .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; - locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, @@ -550,19 +528,6 @@ impl MachineOps for StdMachine { fn get_pci_host(&mut self) -> StdResult<&Arc>> { Ok(&self.pci_host) } - - fn get_sys_bus(&mut self) -> &SysBus { - &self.sysbus - } - - fn get_fwcfg_dev(&mut self) -> Result>> { - // Unwrap is safe. Because after standard machine realize, this will not be None.F - Ok(self.fwcfg_dev.clone().unwrap()) - } - - fn get_boot_order_list(&self) -> Option>>> { - Some(self.boot_order_list.clone()) - } } impl AcpiBuilder for StdMachine { @@ -770,71 +735,6 @@ impl AcpiBuilder for StdMachine { .chain_err(|| "Fail to add MADT table to loader")?; Ok(madt_begin as u64) } - - fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { - for cpu in node.cpus.iter() { - srat.append_child( - &AcpiSratGiccAffinity { - type_id: 3_u8, - length: size_of::() as u8, - proximity_domain, - process_uid: *cpu as u32, - flags: 1, - clock_domain: 0_u32, - } - .aml_bytes(), - ); - } - } - - fn build_srat_mem( - &self, - base_addr: u64, - proximity_domain: u32, - node: &NumaNode, - srat: &mut AcpiTable, - ) -> u64 { - srat.append_child( - &AcpiSratMemoryAffinity { - type_id: 1, - length: size_of::() as u8, - proximity_domain, - base_addr, - range_length: node.size, - flags: 1, - ..Default::default() - } - .aml_bytes(), - ); - base_addr + node.size - } - - fn build_srat_table( - &self, - acpi_data: &Arc>>, - loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; - if self.numa_nodes.is_none() { - return Ok(0); - } - - let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); - // Reserved - srat.append_child(&[1_u8; 4_usize]); - // Reserved - srat.append_child(&[0_u8; 8_usize]); - - let mut next_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { - self.build_srat_cpu(*id, node, &mut srat); - next_base = self.build_srat_mem(next_base, *id, node, &mut srat); - } - - let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) - .chain_err(|| "Fail to add SRAT table to loader")?; - Ok(srat_begin as u64) - } } impl MachineLifecycle for StdMachine { @@ -1146,8 +1046,6 @@ trait CompileFDTHelper { fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; /// Function that helps to generate the chosen node. fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; - /// Function that helps to generate numa node distances. - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; } impl CompileFDTHelper for StdMachine { @@ -1232,31 +1130,14 @@ impl CompileFDTHelper for StdMachine { } fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { - if self.numa_nodes.is_none() { - let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.sys_mem.memory_end_address().raw_value() - - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let node = "memory"; - let memory_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; - fdt.end_node(memory_node_dep)?; - - return Ok(()); - } - - // Set NUMA node information. - let mut mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - for (id, node) in self.numa_nodes.as_ref().unwrap().iter().enumerate() { - let mem_size = node.1.size; - let node = format!("memory@{:x}", mem_base); - let memory_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; - fdt.set_property_u32("numa-node-id", id as u32)?; - fdt.end_node(memory_node_dep)?; - mem_base += mem_size; - } + let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let mem_size = self.sys_mem.memory_end_address().raw_value() + - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let node = "memory"; + let memory_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.end_node(memory_node_dep)?; Ok(()) } @@ -1342,39 +1223,6 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { - if self.numa_nodes.is_none() { - return Ok(()); - } - - let distance_node_dep = fdt.begin_node("distance-map")?; - fdt.set_property_string("compatible", "numa-distance-map-v1")?; - - let mut matrix = Vec::new(); - let numa_nodes = self.numa_nodes.as_ref().unwrap(); - let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); - for (id, node) in numa_nodes.iter().enumerate() { - let distances = &node.1.distances; - for i in existing_nodes.iter() { - matrix.push(id as u32); - matrix.push(*i as u32); - let dist: u32 = if id as u32 == *i { - 10 - } else if let Some(distance) = distances.get(i) { - *distance as u32 - } else { - 20 - }; - matrix.push(dist); - } - } - - fdt.set_property_array_u32("distance-matrix", matrix.as_ref())?; - fdt.end_node(distance_node_dep)?; - - Ok(()) - } } impl device_tree::CompileFDT for StdMachine { @@ -1391,7 +1239,7 @@ impl device_tree::CompileFDT for StdMachine { self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; - self.generate_distance_node(fdt)?; + fdt.end_node(node_dep)?; Ok(()) diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 1b5b5d439..36789f1de 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -12,7 +12,6 @@ use std::sync::{Arc, Mutex, Weak}; -use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 80d16d2bc..897147930 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -18,7 +18,6 @@ mod x86_64; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; -use log::error; use machine_manager::event_loop::EventLoop; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; @@ -28,8 +27,6 @@ pub use x86_64::StdMachine; #[allow(clippy::upper_case_acronyms)] pub mod errors { - use error_chain::error_chain; - error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -75,11 +72,11 @@ use acpi::{ }; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; -use error_chain::{bail, ChainedError}; +use error_chain::ChainedError; use errors::{Result, ResultExt}; use machine_manager::config::{ get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, NetworkInterfaceConfig, - NumaNode, NumaNodes, PciBdf, VmConfig, + PciBdf, VmConfig, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -156,17 +153,6 @@ trait StdMachineOps: AcpiBuilder { .chain_err(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); - let srat_addr = self - .build_srat_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI SRAT table")?; - xsdt_entries.push(srat_addr); - - if let Some(numa_nodes) = self.get_numa_nodes() { - let slit_addr = Self::build_slit_table(numa_nodes, &acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI SLIT table")?; - xsdt_entries.push(slit_addr); - } - let xsdt_addr = Self::build_xsdt_table(&acpi_tables, &mut loader, xsdt_entries)?; let mut locked_fw_cfg = fw_cfg.lock().unwrap(); @@ -199,8 +185,6 @@ trait StdMachineOps: AcpiBuilder { fn get_vm_config(&self) -> &Mutex; - fn get_numa_nodes(&self) -> &Option; - /// Register event notifier for reset of standard machine. /// /// # Arguments @@ -574,76 +558,6 @@ trait AcpiBuilder { Ok(facs_begin as u64) } - /// Build ACPI SRAT CPU table. - /// # Arguments - /// - /// `proximity_domain` - The proximity domain. - /// `node` - The NUMA node. - /// `srat` - The SRAT table. - fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable); - - /// Build ACPI SRAT memory table. - /// # Arguments - /// - /// `base_addr` - The base address of the memory range. - /// `proximity_domain` - The proximity domain. - /// `node` - The NUMA node. - /// `srat` - The SRAT table. - fn build_srat_mem( - &self, - base_addr: u64, - proximity_domain: u32, - node: &NumaNode, - srat: &mut AcpiTable, - ) -> u64; - - /// Build ACPI SRAT table, returns the offset of ACPI SRAT table in `acpi_data`. - /// - /// # Arguments - /// - /// `acpi_data` - Bytes streams that ACPI tables converts to. - /// `loader` - ACPI table loader. - fn build_srat_table( - &self, - acpi_data: &Arc>>, - loader: &mut TableLoader, - ) -> Result; - - /// Build ACPI SLIT table, returns the offset of ACPI SLIT table in `acpi_data`. - /// - /// # Arguments - /// - /// `numa_nodes` - The information of NUMA nodes. - /// `acpi_data` - Bytes streams that ACPI tables converts to. - /// `loader` - ACPI table loader. - fn build_slit_table( - numa_nodes: &NumaNodes, - acpi_data: &Arc>>, - loader: &mut TableLoader, - ) -> Result { - let mut slit = AcpiTable::new(*b"SLIT", 1, *b"STRATO", *b"VIRTSLIT", 1); - slit.append_child((numa_nodes.len() as u64).as_bytes()); - - let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); - for (id, node) in numa_nodes.iter().enumerate() { - let distances = &node.1.distances; - for i in existing_nodes.iter() { - let dist: u8 = if id as u32 == *i { - 10 - } else if let Some(distance) = distances.get(i) { - *distance - } else { - 20 - }; - slit.append_child(dist.as_bytes()); - } - } - - let slit_begin = StdMachine::add_table_to_loader(acpi_data, loader, &slit) - .chain_err(|| "Fail to add SLIT table to loader")?; - Ok(slit_begin as u64) - } - /// Build ACPI XSDT table, returns the offset of ACPI XSDT table in `acpi_data`. /// /// # Arguments @@ -764,7 +678,6 @@ impl StdMachine { iothread: args.iothread.clone(), iops: conf.iops, queues: args.queues.unwrap_or(1), - boot_index: args.boot_index, }; dev.check()?; dev @@ -774,17 +687,9 @@ impl StdMachine { let blk_id = blk.id.clone(); let blk = Arc::new(Mutex::new(Block::new(blk))); - let pci_dev = self - .add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) + self.add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) .chain_err(|| "Failed to add virtio pci block device")?; - if let Some(bootindex) = args.boot_index { - if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { - self.add_bootindex_devices(bootindex, &dev_path, &args.id) - .chain_err(|| "Fail to add boot index")?; - } - } - MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), blk, @@ -839,7 +744,6 @@ impl StdMachine { &net_id, ); } - Ok(()) } @@ -1045,13 +949,7 @@ impl DeviceInterface for StdMachine { let locked_pci_host = pci_host.lock().unwrap(); if let Some((bus, dev)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &device_id) { match handle_unplug_request(&bus, &dev) { - Ok(()) => { - let locked_dev = dev.lock().unwrap(); - let dev_id = locked_dev.name(); - drop(locked_pci_host); - self.del_bootindex_devices(&dev_id); - Response::create_empty_response() - } + Ok(()) => Response::create_empty_response(), Err(e) => Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index d6e2eb303..24a8770d3 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -18,8 +18,6 @@ use std::sync::{ use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use error_chain::ChainedError; -use log::{debug, error}; -use pci::config::CLASS_CODE_ISA_BRIDGE; use pci::config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, @@ -31,6 +29,7 @@ use vmm_sys_util::eventfd::EventFd; use super::VENDOR_ID_INTEL; use crate::standard_vm::errors::Result; +use pci::config::CLASS_CODE_ISA_BRIDGE; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 05f87da36..2c93b28a4 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -13,8 +13,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{Region, RegionOps}; -use error_chain::{bail, ChainedError}; -use log::{debug, error}; +use error_chain::ChainedError; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 9313035b6..0e6dc159d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -21,51 +21,42 @@ use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use acpi::{ - AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, - AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, - AmlString, TableLoader, IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, + AcpiIoApic, AcpiLocalApic, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, + AmlPackage, AmlScope, AmlScopeBuilder, AmlString, TableLoader, ACPI_TABLE_FILE, + IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, TABLE_CHECKSUM_OFFSET, }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; -use devices::legacy::{ - errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, - SERIAL_ADDR, -}; -use error_chain::{bail, ChainedError}; +use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; +use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; -use log::error; -use machine_manager::config::{ - BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, -}; -use machine_manager::event; +use machine_manager::config::{BootSource, PFlashConfig, SerialConfig, VmConfig}; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; -use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::SysBus; -use syscall::syscall_whitelist; -use util::byte_code::ByteCode; +use sysbus::{SysBus, SysBusDevOps}; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; use vmm_sys_util::eventfd::EventFd; -use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; use self::ich9_lpc::SLEEP_CTRL_OFFSET; + use super::errors::{ErrorKind, Result}; use super::{AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; use crate::MachineOps; +use mch::Mch; +use syscall::syscall_whitelist; +use util::byte_code::ByteCode; const VENDOR_ID_INTEL: u16 = 0x8086; -const HOLE_640K_START: u64 = 0x000A_0000; -const HOLE_640K_END: u64 = 0x0010_0000; /// The type of memory layout entry on x86_64 #[repr(usize)] @@ -114,12 +105,6 @@ pub struct StdMachine { /// VM power button, handle VM `Shutdown` event. power_button: EventFd, vm_config: Mutex, - /// List of guest NUMA nodes information. - numa_nodes: Option, - /// List contains the boot order of boot devices. - boot_order_list: Arc>>, - /// FwCfg device. - fwcfg_dev: Option>>, } impl StdMachine { @@ -167,16 +152,13 @@ impl StdMachine { power_button: EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?, vm_config: Mutex::new(vm_config.clone()), - numa_nodes: None, - boot_order_list: Arc::new(Mutex::new(Vec::new())), - fwcfg_dev: None, }) } pub fn handle_reset_request(vm: &Arc>) -> MachineResult<()> { use crate::errors::ResultExt as MachineResultExt; - let mut locked_vm = vm.lock().unwrap(); + let locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { MachineResultExt::chain_err(cpu.kick(), || { @@ -186,11 +168,13 @@ impl StdMachine { cpu.set_to_boot_state(); } - MachineResultExt::chain_err(locked_vm.reset_all_devices(), || { - "Fail to reset all devices" - })?; - MachineResultExt::chain_err(locked_vm.reset_fwcfg_boot_order(), || { - "Fail to update boot order information to FwCfg device" + for dev in locked_vm.sysbus.devices.iter() { + MachineResultExt::chain_err(dev.lock().unwrap().reset(), || { + "Fail to reset sysbus device" + })?; + } + MachineResultExt::chain_err(locked_vm.pci_host.lock().unwrap().reset(), || { + "Fail to reset pci host" })?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { @@ -311,14 +295,8 @@ impl StdMachineOps for StdMachine { fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, ncpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; - let boot_order = Vec::::new(); - fwcfg - .add_file_entry("bootorder", boot_order) - .chain_err(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; - let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) .chain_err(|| "Failed to realize fwcfg device")?; - self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) } @@ -338,10 +316,6 @@ impl StdMachineOps for StdMachine { fn get_vm_config(&self) -> &Mutex { &self.vm_config } - - fn get_numa_nodes(&self) -> &Option { - &self.numa_nodes - } } impl MachineOps for StdMachine { @@ -451,7 +425,6 @@ impl MachineOps for StdMachine { let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; - locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_io, @@ -504,10 +477,6 @@ impl MachineOps for StdMachine { StdMachine::arch_init()?; locked_vm.register_power_event(&locked_vm.power_button)?; - locked_vm - .reset_fwcfg_boot_order() - .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; - if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } @@ -592,19 +561,6 @@ impl MachineOps for StdMachine { fn get_pci_host(&mut self) -> Result<&Arc>> { Ok(&self.pci_host) } - - fn get_sys_bus(&mut self) -> &SysBus { - &self.sysbus - } - - fn get_fwcfg_dev(&mut self) -> MachineResult>> { - // Unwrap is safe. Because after standard machine realize, this will not be None.F - Ok(self.fwcfg_dev.clone().unwrap()) - } - - fn get_boot_order_list(&self) -> Option>>> { - Some(self.boot_order_list.clone()) - } } impl AcpiBuilder for StdMachine { @@ -613,7 +569,6 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> super::errors::Result { - use super::errors::ResultExt; let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -623,7 +578,6 @@ impl AcpiBuilder for StdMachine { let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0007".to_string()))); dev.append_child(AmlNameDecl::new("_UID", AmlInteger(cpu_id))); - dev.append_child(AmlNameDecl::new("_PXM", AmlInteger(0))); sb_scope.append_child(dev); } @@ -642,8 +596,21 @@ impl AcpiBuilder for StdMachine { package.append_child(AmlInteger(0)); dsdt.append_child(AmlNameDecl::new("_S5", package).aml_bytes().as_slice()); - let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) - .chain_err(|| "Fail to add DSTD table to loader")?; + let mut locked_acpi_data = acpi_data.lock().unwrap(); + let dsdt_begin = locked_acpi_data.len() as u32; + locked_acpi_data.extend(dsdt.aml_bytes()); + let dsdt_end = locked_acpi_data.len() as u32; + // Drop the lock of acpi_data to avoid dead-lock when adding entry to + // TableLoader, because TableLoader also needs to acquire this lock. + drop(locked_acpi_data); + + loader.add_cksum_entry( + ACPI_TABLE_FILE, + dsdt_begin + TABLE_CHECKSUM_OFFSET, + dsdt_begin, + dsdt_end - dsdt_begin, + )?; + Ok(dsdt_begin as u64) } @@ -652,7 +619,6 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> super::errors::Result { - use super::errors::ResultExt; let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.append_child(LAPIC_BASE_ADDR.as_bytes()); @@ -680,129 +646,22 @@ impl AcpiBuilder for StdMachine { madt.append_child(&lapic.aml_bytes()); }); - let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) - .chain_err(|| "Fail to add DSTD table to loader")?; - Ok(madt_begin as u64) - } - - fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { - for cpu in node.cpus.iter() { - srat.append_child( - &AcpiSratProcessorAffinity { - length: size_of::() as u8, - proximity_lo: proximity_domain as u8, - local_apic_id: *cpu, - flags: 1, - ..Default::default() - } - .aml_bytes(), - ); - } - } - - fn build_srat_mem( - &self, - base_addr: u64, - proximity_domain: u32, - node: &NumaNode, - srat: &mut AcpiTable, - ) -> u64 { - let mem_below_4g = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 - + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; - let mem_above_4g = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; - - let mut mem_base = base_addr; - let mut mem_len = node.size; - let mut next_base = mem_base + mem_len; - // It contains the hole from 604Kb to 1Mb - if mem_base <= HOLE_640K_START && next_base > HOLE_640K_START { - mem_len -= next_base - HOLE_640K_START; - if mem_len > 0 { - srat.append_child( - &AcpiSratMemoryAffinity { - type_id: 1, - length: size_of::() as u8, - proximity_domain, - base_addr: mem_base, - range_length: mem_len, - flags: 1, - ..Default::default() - } - .aml_bytes(), - ); - } - - if next_base <= HOLE_640K_END { - next_base = HOLE_640K_END; - return next_base; - } - mem_base = HOLE_640K_END; - mem_len = next_base - HOLE_640K_END; - } - - // It contains the hole possibly from mem_below_4g(2G) to mem_below_4g(4G). - if mem_base <= mem_below_4g && next_base > mem_below_4g { - mem_len -= next_base - mem_below_4g; - if mem_len > 0 { - srat.append_child( - &AcpiSratMemoryAffinity { - type_id: 1, - length: size_of::() as u8, - proximity_domain, - base_addr: mem_base, - range_length: mem_len, - flags: 1, - ..Default::default() - } - .aml_bytes(), - ); - } - mem_base = mem_above_4g; - mem_len = next_base - mem_below_4g; - next_base = mem_base + mem_len; - } - - if mem_len > 0 { - srat.append_child( - &AcpiSratMemoryAffinity { - type_id: 1, - length: size_of::() as u8, - proximity_domain, - base_addr: mem_base, - range_length: mem_len, - flags: 1, - ..Default::default() - } - .aml_bytes(), - ); - } - - next_base - } - - fn build_srat_table( - &self, - acpi_data: &Arc>>, - loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; - if self.numa_nodes.is_none() { - return Ok(0); - } - - let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); - srat.append_child(&[1_u8; 4_usize]); - srat.append_child(&[0_u8; 8_usize]); - - let mut next_base = 0_u64; - for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { - self.build_srat_cpu(*id, node, &mut srat); - next_base = self.build_srat_mem(next_base, *id, node, &mut srat); - } + let mut locked_acpi_data = acpi_data.lock().unwrap(); + let madt_begin = locked_acpi_data.len() as u32; + locked_acpi_data.extend(madt.aml_bytes()); + let madt_end = locked_acpi_data.len() as u32; + // Drop the lock of acpi_data to avoid dead-lock when adding entry to + // TableLoader, because TableLoader also needs to acquire this lock. + drop(locked_acpi_data); + + loader.add_cksum_entry( + ACPI_TABLE_FILE, + madt_begin + TABLE_CHECKSUM_OFFSET, + madt_begin, + madt_end - madt_begin, + )?; - let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) - .chain_err(|| "Fail to add SRAT table to loader")?; - Ok(srat_begin as u64) + Ok(madt_begin as u64) } } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 7e61a40b8..95adff176 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -372,16 +372,6 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true) .required(false), ) - .arg( - Arg::with_name("numa") - .multiple(true) - .long("numa") - .value_name("node,nodeid=0,cpus=0-1,memdev=mem0> \ - -numa \ - -object Result { add_args_to_config_multi!((args.values_of("chardev")), vm_cfg, add_chardev); add_args_to_config_multi!((args.values_of("device")), vm_cfg, add_device); add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); - add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); if let Some(s) = args.value_of("trace") { add_trace_events(&s)?; diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index 7a7d9376a..ec19445f9 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index e525f5c5e..1567ed855 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 9850ba293..0386dc4e4 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -14,8 +14,6 @@ use std::fs::metadata; use std::os::linux::fs::MetadataExt; use std::path::Path; -use error_chain::bail; -use log::error; use serde::{Deserialize, Serialize}; use super::{ @@ -41,14 +39,6 @@ pub struct BlkDevConfig { pub iothread: Option, pub iops: Option, pub queues: u16, - pub boot_index: Option, -} - -#[derive(Debug, Clone)] -pub struct BootIndexInfo { - pub boot_index: u8, - pub id: String, - pub dev_path: String, } impl Default for BlkDevConfig { @@ -62,7 +52,6 @@ impl Default for BlkDevConfig { iothread: None, iops: None, queues: 1, - boot_index: None, } } } @@ -261,11 +250,11 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result("bootindex")? { - blkdevcfg.boot_index = Some(boot_index); + if let Err(ref e) = cmd_parser.get_value::("bootindex") { + bail!("Failed to parse \'bootindex\': {:?}", &e); } + let mut blkdevcfg = BlkDevConfig::default(); let blkdrive = if let Some(drive) = cmd_parser.get_value::("drive")? { drive } else { diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index f3c5b0137..d2879fc04 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -12,11 +12,10 @@ use std::str::FromStr; -use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result, ResultExt}; -use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_NODES, MAX_STRING_LENGTH}; +use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig}; const DEFAULT_CPUS: u8 = 1; const DEFAULT_THREADS: u8 = 1; @@ -55,47 +54,6 @@ impl FromStr for MachineType { } } -#[repr(u32)] -#[derive(PartialEq, Eq)] -pub enum HostMemPolicy { - Default = 0, - Preferred = 1, - Bind = 2, - Interleave = 3, - NotSupported = 4, -} - -impl From for HostMemPolicy { - fn from(str: String) -> HostMemPolicy { - match str.to_lowercase().as_str() { - "default" => HostMemPolicy::Default, - "preferred" => HostMemPolicy::Preferred, - "bind" => HostMemPolicy::Bind, - "interleave" => HostMemPolicy::Interleave, - _ => HostMemPolicy::NotSupported, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct MemZoneConfig { - pub id: String, - pub size: u64, - pub host_numa_node: Option, - pub policy: String, -} - -impl Default for MemZoneConfig { - fn default() -> Self { - MemZoneConfig { - id: String::new(), - size: 0, - host_numa_node: None, - policy: String::from("bind"), - } - } -} - /// Config that contains machine's memory information config. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct MachineMemConfig { @@ -104,7 +62,6 @@ pub struct MachineMemConfig { pub dump_guest_core: bool, pub mem_share: bool, pub mem_prealloc: bool, - pub mem_zones: Option>, } impl Default for MachineMemConfig { @@ -115,7 +72,6 @@ impl Default for MachineMemConfig { dump_guest_core: true, mem_share: false, mem_prealloc: false, - mem_zones: None, } } } @@ -330,66 +286,6 @@ impl VmConfig { pub fn enable_mem_prealloc(&mut self) { self.machine_config.mem_config.mem_prealloc = true; } - - pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { - let mut cmd_parser = CmdParser::new("mem_zone"); - cmd_parser - .push("") - .push("id") - .push("size") - .push("host-nodes") - .push("policy"); - cmd_parser.parse(mem_zone)?; - - let mut zone_config = MemZoneConfig::default(); - if let Some(id) = cmd_parser.get_value::("id")? { - if id.len() > MAX_STRING_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into(), - ); - } - zone_config.id = id; - } else { - return Err(ErrorKind::FieldIsMissing("id", "memory-backend-ram").into()); - } - if let Some(mem) = cmd_parser.get_value::("size")? { - zone_config.size = memory_unit_conversion(&mem)?; - } else { - return Err(ErrorKind::FieldIsMissing("size", "memory-backend-ram").into()); - } - if let Some(host_nodes) = cmd_parser.get_value::("host-nodes")? { - if host_nodes >= MAX_NODES { - return Err(ErrorKind::IllegalValue( - "host_nodes".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ) - .into()); - } - zone_config.host_numa_node = Some(host_nodes); - } - if let Some(policy) = cmd_parser.get_value::("policy")? { - if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { - return Err(ErrorKind::InvalidParam("policy".to_string(), policy).into()); - } - zone_config.policy = policy; - } - - if self.machine_config.mem_config.mem_zones.is_some() { - self.machine_config - .mem_config - .mem_zones - .as_mut() - .unwrap() - .push(zone_config.clone()); - } else { - self.machine_config.mem_config.mem_zones = Some(vec![zone_config.clone()]); - } - - Ok(zone_config) - } } fn adjust_topology( @@ -430,12 +326,7 @@ fn adjust_topology( (max_cpus, sockets, cores, threads) } -/// Convert memory units from GiB, Mib to Byte. -/// -/// # Arguments -/// -/// * `origin_value` - The origin memory value from user. -pub fn memory_unit_conversion(origin_value: &str) -> Result { +fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) { @@ -493,7 +384,6 @@ mod tests { mem_share: false, dump_guest_core: false, mem_prealloc: false, - mem_zones: None, }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, @@ -820,41 +710,4 @@ mod tests { let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); assert!(cpu_cfg_ret.is_err()); } - - #[test] - fn test_add_mem_zone() { - let mut vm_config = VmConfig::default(); - let zone_config_1 = vm_config - .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind") - .unwrap(); - assert_eq!(zone_config_1.id, "mem1"); - assert_eq!(zone_config_1.size, 2147483648); - assert_eq!(zone_config_1.host_numa_node, Some(1)); - assert_eq!(zone_config_1.policy, "bind"); - - let zone_config_2 = vm_config - .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1") - .unwrap(); - assert_eq!(zone_config_2.host_numa_node, None); - assert_eq!(zone_config_2.policy, "bind"); - - assert!(vm_config - .add_mem_zone("-object memory-backend-ram,size=2G") - .is_err()); - assert!(vm_config - .add_mem_zone("-object memory-backend-ram,id=mem1") - .is_err()); - } - - #[test] - fn test_host_mem_policy() { - let policy = HostMemPolicy::from(String::from("default")); - assert!(policy == HostMemPolicy::Default); - - let policy = HostMemPolicy::from(String::from("interleave")); - assert!(policy == HostMemPolicy::Interleave); - - let policy = HostMemPolicy::from(String::from("error")); - assert!(policy == HostMemPolicy::NotSupported); - } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 554b42b33..4c4e2bcd3 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -11,8 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod errors { - use error_chain::error_chain; - error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); @@ -87,7 +85,6 @@ pub use drive::*; pub use iothread::*; pub use machine_config::*; pub use network::*; -pub use numa::*; pub use pci::*; pub use rng::*; pub use vfio::*; @@ -100,7 +97,6 @@ mod drive; mod iothread; mod machine_config; mod network; -mod numa; mod pci; mod rng; mod vfio; @@ -109,8 +105,6 @@ use std::any::Any; use std::collections::HashMap; use std::str::FromStr; -use error_chain::bail; -use log::error; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::trace::enable_trace_events; @@ -120,12 +114,10 @@ pub const MAX_PATH_LENGTH: usize = 4096; pub const MAX_VIRTIO_QUEUE: usize = 1024; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; -pub const MAX_NODES: u32 = 128; #[derive(Debug, Clone)] pub enum ObjConfig { Rng(RngObjConfig), - Zone(MemZoneConfig), } fn parse_rng_obj(object_args: &str) -> Result { @@ -165,7 +157,6 @@ pub struct VmConfig { pub pflashs: Option>, pub dev_name: HashMap, pub global_config: HashMap, - pub numa_nodes: Vec<(String, String)>, } impl VmConfig { @@ -249,17 +240,7 @@ impl VmConfig { if self.object.get(&id).is_none() { self.object.insert(id, object_config); } else { - bail!("Object: {} has been added", id); - } - } - "memory-backend-ram" => { - let zone_config = self.add_mem_zone(object_args)?; - let id = zone_config.id.clone(); - let object_config = ObjConfig::Zone(zone_config); - if self.object.get(&id).is_none() { - self.object.insert(id, object_config); - } else { - bail!("Object: {} has been added", id); + bail!("Object: {:?} has been added"); } } _ => { @@ -509,42 +490,6 @@ pub fn add_trace_events(config: &str) -> Result<()> { bail!("trace: events file must be set."); } -pub struct IntegerList(pub Vec); - -impl FromStr for IntegerList { - type Err = (); - - fn from_str(s: &str) -> std::result::Result { - let mut integer_list = Vec::new(); - let lists: Vec<&str> = s.trim().split(',').collect(); - for list in lists.iter() { - let items: Vec<&str> = list.split('-').collect(); - if items.len() > 2 { - return Err(()); - } - - let start = items[0] - .parse::() - .map_err(|_| error!("Invalid value {}", items[0]))?; - integer_list.push(start); - if items.len() == 2 { - let end = items[1] - .parse::() - .map_err(|_| error!("Invalid value {}", items[1]))?; - if start >= end { - return Err(()); - } - - for i in start..end { - integer_list.push(i + 1); - } - } - } - - Ok(IntegerList(integer_list)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index fa32d368b..820a5e56c 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs deleted file mode 100644 index c97938817..000000000 --- a/machine_manager/src/config/numa.rs +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::collections::BTreeMap; - -use error_chain::bail; - -use super::errors::{ErrorKind, Result}; -use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; - -const MIN_NUMA_DISTANCE: u8 = 10; - -#[derive(Default, Debug)] -pub struct NumaDistance { - pub destination: u32, - pub distance: u8, -} - -#[derive(Default, Debug)] -pub struct NumaConfig { - pub numa_id: u32, - pub cpus: Vec, - pub distances: Option>, - pub size: u64, - pub mem_dev: String, -} - -#[derive(Default)] -pub struct NumaNode { - pub cpus: Vec, - pub distances: BTreeMap, - pub size: u64, -} - -pub type NumaNodes = BTreeMap; - -pub fn check_numa_node(numa_nodes: &NumaNodes, vm_config: &mut VmConfig) -> Result<()> { - let mut total_ram_size = 0_u64; - let mut total_cpu_num = 0_u8; - let mut max_cpu_idx = 0_u8; - for node in numa_nodes.iter() { - total_ram_size += node.1.size; - total_cpu_num += node.1.cpus.len() as u8; - for idx in &node.1.cpus { - if max_cpu_idx <= *idx { - max_cpu_idx = *idx; - } - } - } - - if total_ram_size != vm_config.machine_config.mem_config.mem_size { - bail!( - "Total memory {} of NUMA nodes is not equals to memory size {}", - total_ram_size, - vm_config.machine_config.mem_config.mem_size, - ); - } - if total_cpu_num != vm_config.machine_config.nr_cpus { - bail!( - "Total cpu numbers {} of NUMA nodes is not equals to smp {}", - total_cpu_num, - vm_config.machine_config.nr_cpus, - ); - } - if max_cpu_idx != vm_config.machine_config.nr_cpus - 1 { - bail!("Error to configure CPU sets, please check you cmdline again"); - } - - Ok(()) -} - -pub fn parse_numa_mem(numa_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("numa"); - cmd_parser - .push("") - .push("nodeid") - .push("cpus") - .push("memdev"); - cmd_parser.parse(numa_config)?; - - let mut config: NumaConfig = NumaConfig::default(); - if let Some(node_id) = cmd_parser.get_value::("nodeid")? { - if node_id >= MAX_NODES { - return Err(ErrorKind::IllegalValue( - "nodeid".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ) - .into()); - } - config.numa_id = node_id; - } else { - return Err(ErrorKind::FieldIsMissing("nodeid", "numa").into()); - } - if let Some(cpus) = cmd_parser - .get_value::("cpus") - .map_err(|_| ErrorKind::ConvertValueFailed(String::from("u64"), "cpus".to_string()))? - .map(|v| v.0.iter().map(|e| *e as u8).collect()) - { - config.cpus = cpus; - } else { - return Err(ErrorKind::FieldIsMissing("cpus", "numa").into()); - } - if let Some(mem_dev) = cmd_parser.get_value::("memdev")? { - config.mem_dev = mem_dev; - } else { - return Err(ErrorKind::FieldIsMissing("memdev", "numa").into()); - } - - Ok(config) -} - -pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { - let mut cmd_parser = CmdParser::new("numa"); - cmd_parser.push("").push("src").push("dst").push("val"); - cmd_parser.parse(numa_dist)?; - - let mut dist: NumaDistance = NumaDistance::default(); - let numa_id = if let Some(src) = cmd_parser.get_value::("src")? { - if src >= MAX_NODES { - return Err(ErrorKind::IllegalValue( - "src".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ) - .into()); - } - src - } else { - return Err(ErrorKind::FieldIsMissing("src", "numa").into()); - }; - if let Some(dst) = cmd_parser.get_value::("dst")? { - if dst >= MAX_NODES { - return Err(ErrorKind::IllegalValue( - "dst".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ) - .into()); - } - dist.destination = dst; - } else { - return Err(ErrorKind::FieldIsMissing("dst", "numa").into()); - } - if let Some(val) = cmd_parser.get_value::("val")? { - if val < MIN_NUMA_DISTANCE { - bail!("NUMA distance shouldn't be less than 10"); - } - dist.distance = val; - } else { - return Err(ErrorKind::FieldIsMissing("val", "numa").into()); - } - - if numa_id == dist.destination && dist.distance != MIN_NUMA_DISTANCE { - bail!("Local distance of node {} should be 10.", numa_id); - } - - Ok((numa_id, dist)) -} - -impl VmConfig { - pub fn add_numa(&mut self, numa_config: &str) -> Result<()> { - let mut cmd_params = CmdParser::new("numa"); - cmd_params.push(""); - - cmd_params.get_parameters(numa_config)?; - if let Some(numa_type) = cmd_params.get_value::("")? { - self.numa_nodes.push((numa_type, numa_config.to_string())); - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_numa_mem() { - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_numa("-numa node,nodeid=0,cpus=0-1,memdev=mem0") - .is_ok()); - assert!(vm_config - .add_numa("-numa node,nodeid=1,cpus=2-1,memdev=mem1") - .is_ok()); - assert!(vm_config - .add_numa("-numa node,nodeid=2,memdev=mem2") - .is_ok()); - assert!(vm_config.add_numa("-numa node,nodeid=3,cpus=3-4").is_ok()); - - let numa = vm_config.numa_nodes.get(0).unwrap(); - let numa_config = parse_numa_mem(numa.1.as_str()).unwrap(); - assert_eq!(numa_config.cpus, vec![0, 1]); - assert_eq!(numa_config.mem_dev, "mem0"); - - let numa = vm_config.numa_nodes.get(1).unwrap(); - assert!(parse_numa_mem(numa.1.as_str()).is_err()); - let numa = vm_config.numa_nodes.get(2).unwrap(); - assert!(parse_numa_mem(numa.1.as_str()).is_err()); - let numa = vm_config.numa_nodes.get(3).unwrap(); - assert!(parse_numa_mem(numa.1.as_str()).is_err()); - } - - #[test] - fn test_parse_numa_distance() { - let mut vm_config = VmConfig::default(); - assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=10").is_ok()); - assert!(vm_config.add_numa("-numa dist,dst=1,val=10").is_ok()); - assert!(vm_config.add_numa("-numa dist,src=0,val=10").is_ok()); - assert!(vm_config.add_numa("-numa dist,src=0,dst=1").is_ok()); - - let numa = vm_config.numa_nodes.get(0).unwrap(); - let dist = parse_numa_distance(numa.1.as_str()).unwrap(); - assert_eq!(dist.0, 0); - assert_eq!(dist.1.destination, 1); - assert_eq!(dist.1.distance, 10); - - let numa = vm_config.numa_nodes.get(1).unwrap(); - assert!(parse_numa_distance(numa.1.as_str()).is_err()); - let numa = vm_config.numa_nodes.get(2).unwrap(); - assert!(parse_numa_distance(numa.1.as_str()).is_err()); - let numa = vm_config.numa_nodes.get(3).unwrap(); - assert!(parse_numa_distance(numa.1.as_str()).is_err()); - } - - #[test] - fn test_check_numa_nodes() { - let mut vm_config = VmConfig::default(); - vm_config.machine_config.nr_cpus = 4; - vm_config.machine_config.mem_config.mem_size = 2147483648; - - let numa_node1 = NumaNode { - cpus: vec![0, 1], - distances: Default::default(), - size: 1073741824, - }; - let numa_node2 = NumaNode { - cpus: vec![2, 3], - distances: Default::default(), - size: 1073741824, - }; - - let mut numa_nodes = BTreeMap::new(); - numa_nodes.insert(0, numa_node1); - numa_nodes.insert(1, numa_node2); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_ok()); - - let numa_node3 = NumaNode { - cpus: vec![2], - distances: Default::default(), - size: 1073741824, - }; - numa_nodes.remove(&1); - numa_nodes.insert(2, numa_node3); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); - - let numa_node4 = NumaNode { - cpus: vec![2, 3, 4], - distances: Default::default(), - size: 1073741824, - }; - numa_nodes.remove(&1); - numa_nodes.insert(1, numa_node4); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); - - let numa_node5 = NumaNode { - cpus: vec![3, 4], - distances: Default::default(), - size: 1073741824, - }; - numa_nodes.remove(&1); - numa_nodes.insert(1, numa_node5); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); - - let numa_node6 = NumaNode { - cpus: vec![0, 1], - distances: Default::default(), - size: 1073741824, - }; - numa_nodes.remove(&1); - numa_nodes.insert(1, numa_node6); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); - - let numa_node7 = NumaNode { - cpus: vec![2, 3], - distances: Default::default(), - size: 2147483648, - }; - numa_nodes.remove(&1); - numa_nodes.insert(1, numa_node7); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); - } -} diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 9c6db3c48..9b6409280 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; - use super::errors::{ErrorKind, Result, ResultExt}; use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::ExBool; diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index e1caff629..349bf31cf 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; - use super::errors::{ErrorKind, Result}; use super::{pci_args_check, ObjConfig}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index bc4e177e7..72ef64f7f 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -14,12 +14,10 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::{process, thread}; -use super::config::IothreadConfig; use crate::machine::IOTHREADS; use crate::qmp::qmp_schema::IothreadInfo; -use error_chain::bail; -use log::info; +use super::config::IothreadConfig; use util::loop_context::{EventLoopContext, EventLoopManager, EventNotifier}; /// This struct used to manage all events occur during VM lifetime. diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index f76598a08..a544c609d 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -21,6 +21,11 @@ //! 2. The API interface over VM inside and outside. //! 3. Configuration for VM and its devices. +#[macro_use] +extern crate log; +#[macro_use] +extern crate error_chain; + pub mod cmdline; pub mod config; pub mod event_loop; @@ -31,8 +36,6 @@ pub mod socket; pub mod temp_cleaner; pub mod errors { - use error_chain::error_chain; - error_chain! { links { ConfigParser(crate::config::errors::Error, crate::config::errors::ErrorKind); diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 4694e8d6a..12e493ab9 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -37,7 +37,6 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; -use log::{info, warn}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 95f8b9eaa..17dc7e540 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -484,7 +484,6 @@ pub struct device_add { pub host: Option, #[serde(rename = "num-queues")] pub queues: Option, - pub boot_index: Option, } pub type DeviceAddArgument = device_add; @@ -1745,7 +1744,7 @@ mod tests { fn test_qmp_unexpected_arguments() { // qmp: quit. let json_msg = r#" - { + { "execute": "quit" } "#; @@ -1758,8 +1757,8 @@ mod tests { // unexpected arguments for quit. let json_msg = r#" - { - "execute": "quit" , + { + "execute": "quit" , "arguments": "isdf" } "#; @@ -1772,7 +1771,7 @@ mod tests { // qmp: stop. let json_msg = r#" - { + { "execute": "stop" } "#; @@ -1785,8 +1784,8 @@ mod tests { // unexpected arguments for stop. let json_msg = r#" - { - "execute": "stop" , + { + "execute": "stop" , "arguments": "isdf" } "#; @@ -1799,7 +1798,7 @@ mod tests { // qmp: cont. let json_msg = r#" - { + { "execute": "cont" } "#; @@ -1812,8 +1811,8 @@ mod tests { // unexpected arguments for count. let json_msg = r#" - { - "execute": "cont" , + { + "execute": "cont" , "arguments": "isdf" } "#; @@ -1826,7 +1825,7 @@ mod tests { // qmp: query-hotpluggable-cpus. let json_msg = r#" - { + { "execute": "query-hotpluggable-cpus" } "#; @@ -1839,8 +1838,8 @@ mod tests { // unexpected arguments for query-hotpluggable-cpus. let json_msg = r#" - { - "execute": "query-hotpluggable-cpus" , + { + "execute": "query-hotpluggable-cpus" , "arguments": "isdf" } "#; @@ -1853,7 +1852,7 @@ mod tests { // qmp: query-cpus. let json_msg = r#" - { + { "execute": "query-cpus" } "#; @@ -1866,8 +1865,8 @@ mod tests { // unexpected arguments for query-cpus. let json_msg = r#" - { - "execute": "query-cpus" , + { + "execute": "query-cpus" , "arguments": "isdf" } "#; @@ -1880,7 +1879,7 @@ mod tests { // qmp: query-ststus. let json_msg = r#" - { + { "execute": "query-status" } "#; @@ -1893,8 +1892,8 @@ mod tests { // unexpected arguments for query-status. let json_msg = r#" - { - "execute": "query-status" , + { + "execute": "query-status" , "arguments": "isdf" } "#; @@ -1910,11 +1909,11 @@ mod tests { fn test_wrong_qmp_arguments() { // right arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driver":"virtio-net-mmio", + "id":"net-0", + "driver":"virtio-net-mmio", "addr":"0x0" } } @@ -1928,11 +1927,11 @@ mod tests { // unknow arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driver":"virtio-net-mmio", + "id":"net-0", + "driver":"virtio-net-mmio", "addr":"0x0", "UnknowArg": "should go to error" } @@ -1947,11 +1946,11 @@ mod tests { // wrong spelling arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driv":"virtio-net-mmio", + "id":"net-0", + "driv":"virtio-net-mmio", "addr":"0x0" } } @@ -1965,8 +1964,8 @@ mod tests { // right arguments for device_del. let json_msg = r#" - { - "execute": "device_del" , + { + "execute": "device_del" , "arguments": { "id": "net-1" } @@ -1981,8 +1980,8 @@ mod tests { // wrong arguments for device_del. let json_msg = r#" - { - "execute": "device_del" , + { + "execute": "device_del" , "arguments": { "value": "h8i" } @@ -1999,7 +1998,7 @@ mod tests { // missing arguments for getfd. let json_msg = r#" - { + { "execute": "getfd" } "#; @@ -2012,8 +2011,8 @@ mod tests { // unexpected arguments for getfd. let json_msg = r#" - { - "execute": "getfd" , + { + "execute": "getfd" , "arguments": "isdf" } "#; @@ -2026,10 +2025,10 @@ mod tests { // right arguments for getfd. let json_msg = r#" - { + { "execute": "getfd", - "arguments": { - "fdname": "fd1" + "arguments": { + "fdname": "fd1" } } "#; @@ -2042,17 +2041,17 @@ mod tests { // right arguments for blockdev-add. let json_msg = r#" - { + { "execute": "blockdev-add", "arguments": { - "node-name": "drive-0", + "node-name": "drive-0", "file": { - "driver": "file", + "driver": "file", "filename": "/path/to/block" - }, + }, "cache": { "direct": true - }, + }, "read-only": false } } @@ -2066,11 +2065,11 @@ mod tests { // right arguments for device-add. let json_msg = r#" - { + { "execute": "device_add", "arguments": { - "id": "drive-0", - "driver": "virtio-blk-mmio", + "id": "drive-0", + "driver": "virtio-blk-mmio", "addr": "0x1" } } @@ -2084,10 +2083,10 @@ mod tests { // right arguments for netdev-add. let json_msg = r#" - { + { "execute": "netdev_add", "arguments": { - "id": "net-0", + "id": "net-0", "ifname":"tap0" } } @@ -2104,8 +2103,8 @@ mod tests { fn test_unsupported_commands() { // unsupported qmp command. let json_msg = r#" - { - "execute": "hello-world" , + { + "execute": "hello-world" , } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2117,8 +2116,8 @@ mod tests { // unsupported qmp command, and unknow field. let json_msg = r#" - { - "execute": "hello-world" , + { + "execute": "hello-world" , "arguments": { "msg": "hello", } @@ -2136,8 +2135,8 @@ mod tests { fn test_qmp_commands() { // query-version let json_msg = r#" - { - "execute": "query-version" + { + "execute": "query-version" } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2149,8 +2148,8 @@ mod tests { // query-target let json_msg = r#" - { - "execute": "query-target" + { + "execute": "query-target" } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2162,8 +2161,8 @@ mod tests { // query-commands let json_msg = r#" - { - "execute": "query-commands" + { + "execute": "query-commands" } "#; let err_msg = match serde_json::from_str::(json_msg) { diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 138a576ed..0cd4524f5 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -9,6 +9,7 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +extern crate vmm_sys_util; use crate::temp_cleaner::TempCleaner; use std::io::Write; diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index aa31717f6..594ef64a3 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -16,8 +16,6 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::sync::{Arc, Mutex, RwLock}; -use error_chain::bail; -use log::{error, info}; use util::leak_bucket::LeakBucket; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use vmm_sys_util::epoll::EventSet; diff --git a/migration/src/device_state.rs b/migration/src/device_state.rs index e3f64d61c..de9c16cf8 100644 --- a/migration/src/device_state.rs +++ b/migration/src/device_state.rs @@ -12,7 +12,6 @@ use std::cmp::Ordering; -use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::Result; @@ -170,7 +169,6 @@ impl DeviceStateDesc { #[cfg(test)] pub mod tests { use super::{DeviceStateDesc, FieldDesc, StateTransfer, VersionCheck}; - use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; #[derive(Default)] diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 491a8d01d..9ccab5f3f 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -14,6 +14,14 @@ //! //! Offer snapshot and migration interface for VM. +#[macro_use] +extern crate error_chain; +#[cfg(test)] +#[macro_use] +extern crate migration_derive; +#[macro_use] +extern crate log; + mod device_state; mod header; mod manager; @@ -25,8 +33,6 @@ pub use manager::{MigrationHook, MigrationManager, MigrationRestoreOrder}; pub use status::MigrationStatus; pub mod errors { - use error_chain::error_chain; - use super::status::MigrationStatus; error_chain! { diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 2ca89a258..1ea194e26 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::{debug, info}; use std::cmp; use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index 35e7c0e40..30ee13c75 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -16,8 +16,6 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::path::PathBuf; -use error_chain::bail; -use log::info; use util::byte_code::ByteCode; use util::reader::BufferReader; use util::unix::host_page_size; diff --git a/migration_derive/src/lib.rs b/migration_derive/src/lib.rs index a219f1ec3..a71ab54f0 100644 --- a/migration_derive/src/lib.rs +++ b/migration_derive/src/lib.rs @@ -48,9 +48,12 @@ //! 2. The `ByteCode` derive to auto add `ByteCode` trait and its relying trait for //! struct, such as `Default`, `Sync`, `Send`. +#[macro_use] +extern crate syn; + use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, DeriveInput}; +use syn::DeriveInput; mod attr_parser; mod field_parser; diff --git a/ozone/src/capability.rs b/ozone/src/capability.rs index 356748d47..e7eae704f 100644 --- a/ozone/src/capability.rs +++ b/ozone/src/capability.rs @@ -13,7 +13,6 @@ //! Remove all capability for ozone when uid is 0, use -capability cap_* to add capability. use crate::{syscall, ErrorKind, Result, ResultExt}; -use error_chain::bail; use std::{collections::HashMap, io::Write}; const CAPS_V3: u32 = 0x20080522; diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index e06cf6e00..8ce65394a 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::{ collections::HashMap, fs::{self, OpenOptions}, diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 4e37441bf..4b27dd20f 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -21,7 +21,6 @@ use std::{ process::Stdio, }; -use error_chain::bail; use util::arg_parser::ArgMatches; const BASE_OZONE_PATH: &str = "/srv/ozone"; diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 27507878b..4cb998480 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -10,10 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::{error_chain, quick_main}; +#[macro_use] +extern crate error_chain; -use crate::args::create_args_parser; -use crate::handler::OzoneHandler; +use args::create_args_parser; +use handler::OzoneHandler; mod args; mod capability; diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 107239f01..e88e9c2de 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -14,8 +14,6 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use address_space::Region; -use error_chain::bail; -use log::debug; use super::config::{SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM}; use super::hotplug::HotplugOps; diff --git a/pci/src/host.rs b/pci/src/host.rs index 3baf26cee..c84a6becc 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -12,8 +12,6 @@ use std::sync::{Arc, Mutex}; -#[cfg(target_arch = "aarch64")] -use acpi::AmlOne; use acpi::{ AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlByte, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, @@ -362,12 +360,6 @@ impl AmlBuilder for PciHost { pci_host_bridge.append_child(AmlNameDecl::new("_CID", AmlEisaId::new("PNP0A03"))); pci_host_bridge.append_child(AmlNameDecl::new("_ADR", AmlZero)); pci_host_bridge.append_child(AmlNameDecl::new("_UID", AmlZero)); - #[cfg(target_arch = "aarch64")] - { - // CCA: Cache Coherency Attribute, which determines whether - // guest supports DMA features in pci host on aarch64 platform. - pci_host_bridge.append_child(AmlNameDecl::new("_CCA", AmlOne)); - } build_osc_for_aml(&mut pci_host_bridge); diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index 5b80f40bf..9edd0fa29 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::sync::{Arc, Mutex}; use crate::{errors::Result, PciBus, PciDevOps}; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index ddb8a395e..87eb16373 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -10,9 +10,16 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate migration_derive; +#[macro_use] +extern crate machine_manager; +pub mod errors { error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -52,14 +59,13 @@ pub use root_port::RootPort; use std::{ mem::size_of, - sync::{Arc, Mutex, Weak}, + sync::{Mutex, Weak}, }; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; -use crate::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; -use crate::errors::Result; +use config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use errors::Result; const BDF_FUNC_SHIFT: u8 = 3; @@ -202,51 +208,6 @@ pub trait PciDevOps: Send { fn devfn(&self) -> Option { None } - - /// Get the path of the PCI bus where the device resides. - fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { - let locked_parent_bus = parent_bus.lock().unwrap(); - let parent_dev_path = if locked_parent_bus.name.eq("pcie.0") { - String::from("/pci@ffffffffffffffff") - } else { - // This else branch will not be executed currently, - // which is mainly to be compatible with new PCI bridge devices. - // unwrap is safe because pci bus under root port will not return null. - locked_parent_bus - .parent_bridge - .as_ref() - .unwrap() - .upgrade() - .unwrap() - .lock() - .unwrap() - .get_dev_path() - .unwrap() - }; - parent_dev_path - } - - /// Fill the device patch accroding to parent device patch and device function. - fn populate_dev_path(&self, parent_dev_path: String, devfn: u8, dev_type: &str) -> String { - let mut dev_path = parent_dev_path; - dev_path.push_str(dev_type); - - let slot = pci_slot(devfn); - dev_path.push_str(&slot.to_string()); - - let function = pci_func(devfn); - if function != 0 { - dev_path.push(','); - dev_path.push_str(&function.to_string()); - } - - dev_path - } - - /// Get firmware device path. - fn get_dev_path(&self) -> Option { - None - } } /// Init multifunction for pci devices. @@ -259,7 +220,7 @@ pub trait PciDevOps: Send { /// * `parent_bus` - Parent bus of pci devices. pub fn init_multifunction( multifunction: bool, - config: &mut [u8], + config: &mut Vec, devfn: u8, parent_bus: Weak>, ) -> Result<()> { diff --git a/pci/src/msix.rs b/pci/src/msix.rs index c3daa418c..cbf27ea1a 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -14,11 +14,8 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{GuestAddress, Region, RegionOps}; -use error_chain::bail; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; use crate::config::{CapId, PciConfig, RegionType, SECONDARY_BUS_NUM}; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 2e506fc7d..3dc97dd4c 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -13,14 +13,12 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; +use once_cell::sync::OnceCell; + use address_space::Region; -use error_chain::{bail, ChainedError}; -use log::{error, info}; -use machine_manager::event; +use error_chain::ChainedError; use machine_manager::qmp::{qmp_schema as schema, QmpChannel}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; -use once_cell::sync::OnceCell; use util::byte_code::ByteCode; use super::config::{ @@ -427,13 +425,6 @@ impl PciDevOps for RootPort { Ok(()) } } - - fn get_dev_path(&self) -> Option { - let parent_bus = self.parent_bus.upgrade().unwrap(); - let parent_dev_path = self.get_parent_dev_path(parent_bus); - let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/pci-bridge@"); - Some(dev_path) - } } impl HotplugOps for RootPort { diff --git a/src/main.rs b/src/main.rs index d312ce165..1b974ee82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,12 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; -use error_chain::{bail, error_chain, quick_main}; -use log::{error, info}; use machine::{LightMachine, MachineOps, StdMachine}; use machine_manager::{ cmdline::{check_api_channel, create_args_parser, create_vmconfig}, diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 3dd0a4b52..9b0999012 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -10,9 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; +#[macro_use] +extern crate error_chain; +pub mod errors { error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -28,11 +29,10 @@ use std::sync::{Arc, Mutex}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; -use error_chain::bail; use hypervisor::kvm::KVM_FDS; use vmm_sys_util::eventfd::EventFd; -use crate::errors::{Result, ResultExt}; +use errors::{Result, ResultExt}; pub struct SysBus { #[cfg(target_arch = "x86_64")] diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 2e77901c6..eaac9059e 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use super::Result; -use error_chain::bail; use kvm_bindings::__IncompleteArrayField; pub const IOCB_FLAG_RESFD: u32 = 1; @@ -105,7 +104,7 @@ impl LibaioContext { Ok(LibaioContext { ctx, max_size }) } - pub fn submit(&self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { + pub fn submit(&self, nr: i64, iocbp: &mut Vec<*mut IoCb>) -> Result<()> { let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; if ret < 0 { bail!("Failed to submit aio, return {}.", ret); diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 98422f9b0..27f6f0392 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use super::Result; -use error_chain::bail; use libc::{c_void, fdatasync, pread, pwrite}; use std::os::unix::io::RawFd; diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 2b6ea5e75..8b0336887 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::cmp::Ord; use std::mem::size_of; diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 667d822d1..0c607b1ff 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -37,9 +37,8 @@ pub trait ByteCode: Default + Copy + Send + Sync { if data.len() != size_of::() { return None; } - - // SAFETY: The pointer is properly aligned and point to an initialized instance of T. - unsafe { data.as_ptr().cast::().as_ref() } + let obj_array = unsafe { from_raw_parts::(data.as_ptr() as *const _, data.len()) }; + Some(&obj_array[0]) } /// Creates an mutable object (impl trait `ByteCode`) from a mutable slice of bytes @@ -51,9 +50,9 @@ pub trait ByteCode: Default + Copy + Send + Sync { if data.len() != size_of::() { return None; } - - // SAFETY: The pointer is properly aligned and point to an initialized instance of T. - unsafe { data.as_mut_ptr().cast::().as_mut() } + let obj_array = + unsafe { from_raw_parts_mut::(data.as_mut_ptr() as *mut _, data.len()) }; + Some(&mut obj_array[0]) } } diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 313e10997..d67d750a6 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -14,7 +14,6 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::time::Instant; -use log::error; use vmm_sys_util::eventfd::EventFd; use crate::loop_context::EventLoopContext; diff --git a/util/src/lib.rs b/util/src/lib.rs index abbd7a211..2e2deae96 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -10,6 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +extern crate libc; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate vmm_sys_util; +#[macro_use] +extern crate log; + pub mod aio; pub mod arg_parser; pub mod bitmap; @@ -20,20 +28,19 @@ pub mod daemonize; pub mod device_tree; pub mod leak_bucket; mod link_list; -pub mod logger; pub mod loop_context; pub mod num_ops; -pub mod offsetof; pub mod reader; pub mod seccomp; -pub mod syscall; pub mod tap; -pub mod trace; pub mod unix; +#[macro_use] +pub mod logger; +#[macro_use] +pub mod offsetof; +pub mod trace; pub mod errors { - use error_chain::error_chain; - error_chain! { foreign_links { KvmIoctl(kvm_ioctls::Error); @@ -149,7 +156,6 @@ pub mod errors { } use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; -use log::debug; use once_cell::sync::Lazy; use std::sync::Mutex; use vmm_sys_util::terminal::Terminal; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ce3aa5db4..2c9c8c0af 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -10,13 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +extern crate vmm_sys_util; + use std::collections::BTreeMap; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; use libc::{c_void, read}; -use log::{error, warn}; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 2f09db691..fd902eeed 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -10,8 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::error; -// This module implements some operations of Rust primitive types. +//! This module implements some operations of Rust primitive types. /// Calculate the aligned-up u64 value. /// diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 2f1f0d19e..40ab17eb9 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -77,8 +77,6 @@ //! ``` //! This programe will be trapped. -use error_chain::bail; - use crate::errors::Result; use crate::offset_of; diff --git a/util/src/syscall.rs b/util/src/syscall.rs deleted file mode 100644 index 6294e09f9..000000000 --- a/util/src/syscall.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use error_chain::bail; -use libc::{c_void, syscall, SYS_mbind}; - -use super::errors::Result; - -/// This function set memory policy for host NUMA node memory range. -/// -/// * Arguments -/// -/// * `addr` - The memory range starting with addr. -/// * `len` - Length of the memory range. -/// * `mode` - Memory policy mode. -/// * `node_mask` - node_mask specifies physical node ID. -/// * `max_node` - The max node. -/// * `flags` - Mode flags. -pub fn mbind( - addr: u64, - len: u64, - mode: u32, - node_mask: Vec, - max_node: u64, - flags: u32, -) -> Result<()> { - let res = unsafe { - syscall( - SYS_mbind, - addr as *mut c_void, - len, - mode, - node_mask.as_ptr(), - max_node + 1, - flags, - ) - }; - if res < 0 { - bail!( - "Failed to apply host numa node policy, error is {}", - std::io::Error::last_os_error() - ); - } - - Ok(()) -} diff --git a/util/src/tap.rs b/util/src/tap.rs index 5bed417a7..4154d522b 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -10,13 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; -use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; use super::errors::{Result, ResultExt}; diff --git a/util/src/trace.rs b/util/src/trace.rs index 3f6748505..9a267d039 100644 --- a/util/src/trace.rs +++ b/util/src/trace.rs @@ -5,7 +5,6 @@ use std::ops::Deref; use std::sync::Arc; use arc_swap::ArcSwap; -use log::error; use once_cell::sync::Lazy; use crate::errors::{Result, ResultExt}; diff --git a/util/src/unix.rs b/util/src/unix.rs index 7d2815296..c7d177282 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -16,12 +16,10 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; -use error_chain::bail; use libc::{ c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, CMSG_LEN, CMSG_SPACE, MSG_NOSIGNAL, MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, }; -use log::error; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 4ee431dbc..9787513ce 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -10,9 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate vmm_sys_util; +pub mod errors { error_chain! { links { PciErr(pci::errors::Error, pci::errors::ErrorKind); @@ -48,7 +53,6 @@ use std::sync::{Arc, Mutex}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO}; use kvm_ioctls::DeviceFd; -use log::error; use once_cell::sync::Lazy; use vfio_dev::VfioGroup; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 908d8a5f4..db0c58df6 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -21,7 +21,6 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; use kvm_bindings::{ kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, }; @@ -29,7 +28,6 @@ use vfio_bindings::bindings::vfio; use vmm_sys_util::ioctl::{ ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, }; -use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 6680df29a..abc8f6c44 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -18,9 +18,8 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::{bail, ChainedError}; +use error_chain::ChainedError; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; #[cfg(target_arch = "aarch64")] use pci::config::SECONDARY_BUS_NUM; use pci::config::{ @@ -307,7 +306,7 @@ impl VfioPciDevice { Ok(vfio_bars) } - fn fixup_msix_region(&self, vfio_bars: &mut [VfioBar]) -> Result<()> { + fn fixup_msix_region(&self, vfio_bars: &mut Vec) -> Result<()> { let msix_info = self .msix_info .as_ref() diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 9379d4a39..a2aa067c2 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -22,11 +22,9 @@ use std::{ use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use error_chain::{bail, ChainedError}; -use log::{error, warn}; +use error_chain::ChainedError; use machine_manager::{ - config::BalloonConfig, event, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, - qmp::QmpChannel, + config::BalloonConfig, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, qmp::QmpChannel, }; use util::{ bitmap::Bitmap, diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 15c054d51..d65fd06c7 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,14 +21,12 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress}; -use error_chain::{bail, ChainedError}; -use log::error; +use error_chain::ChainedError; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::aio::{Aio, AioCb, AioCompleteFunc, IoCmd, Iovec}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; diff --git a/virtio/src/console.rs b/virtio/src/console.rs index b48a60b79..f758a84e9 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -17,14 +17,12 @@ use std::{cmp, usize}; use address_space::AddressSpace; use devices::legacy::{Chardev, InputReceiver}; -use error_chain::{bail, ChainedError}; -use log::{debug, error, warn}; +use error_chain::ChainedError; use machine_manager::{ config::{ChardevType, VirtioConsole}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 5af20f3f0..cd06c832a 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -25,9 +25,18 @@ //! - `x86_64` //! - `aarch64` -pub mod errors { - use error_chain::error_chain; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate machine_manager; +#[macro_use] +extern crate vmm_sys_util; +#[macro_use] +extern crate migration_derive; +pub mod errors { error_chain! { foreign_links { Io(std::io::Error); @@ -122,7 +131,6 @@ pub use virtio_pci::VirtioPciDevice; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use error_chain::bail; use machine_manager::config::ConfigCheck; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 172528f93..cbd843453 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -18,14 +18,11 @@ use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; use address_space::AddressSpace; -use error_chain::bail; -use log::{error, warn}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 0e5779c36..13409a875 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -17,8 +17,6 @@ use std::sync::atomic::{fence, Ordering}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; -use error_chain::bail; -use log::{error, warn}; use util::byte_code::ByteCode; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 4b3770cc2..ff87fc2d5 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -27,9 +27,6 @@ use util::loop_context::{ }; use util::num_ops::{read_u32, write_u32}; -use error_chain::bail; -use log::{error, warn}; -use migration_derive::{ByteCode, Desc}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 39a7c2f23..ccc85e957 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -24,8 +24,6 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use error_chain::ChainedError; -use log::{debug, error}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -33,13 +31,11 @@ use util::loop_context::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; -use vmm_sys_util::{ - ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, -}; use super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; use super::{VhostNotify, VhostOps}; +use crate::error_chain::ChainedError; /// Refer to VHOST_VIRTIO in /// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index d1ceb7fac..ec5ee9f68 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -17,8 +17,6 @@ use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use error_chain::bail; -use log::warn; use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index f2dbeca8c..1489a97f3 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -15,11 +15,8 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; -use log::warn; use machine_manager::{config::VsockConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index e76f0865f..ea7a6223f 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -17,8 +17,6 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; -use error_chain::bail; -use log::{info, warn}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 9875af550..2e28dd1c6 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -15,7 +15,6 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use log::warn; use machine_manager::config::NetworkInterfaceConfig; use util::byte_code::ByteCode; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index 9cbab183a..422e1f28a 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -13,7 +13,6 @@ use std::mem::size_of; use std::os::unix::io::RawFd; -use error_chain::bail; use libc::{c_void, iovec}; use util::unix::UnixSock; diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 6db9687bf..d85e889d6 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -15,12 +15,9 @@ use std::sync::{Arc, Mutex}; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; -use log::{error, warn}; #[cfg(target_arch = "x86_64")] use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 6e4f314e9..3ffbd7b65 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -16,11 +16,9 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::{bail, ChainedError}; +use error_chain::ChainedError; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; use pci::config::{ RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, @@ -1123,21 +1121,6 @@ impl PciDevOps for VirtioPciDevice { .reset() .chain_err(|| "Fail to reset virtio device") } - - fn get_dev_path(&self) -> Option { - let parent_bus = self.parent_bus.upgrade().unwrap(); - match self.device.lock().unwrap().device_type() { - VIRTIO_TYPE_BLOCK => { - // The virtio blk device is identified as a single-channel SCSI device, - // so add scsi controler indetification without channel, scsi-id and lun. - let parent_dev_path = self.get_parent_dev_path(parent_bus); - let mut dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); - dev_path.push_str("/disk@0,0"); - Some(dev_path) - } - _ => None, - } - } } impl StateTransfer for VirtioPciDevice { -- Gitee From a56372a863cab111d2f20b056ad30bb285eee7f4 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Mon, 13 Jun 2022 17:11:17 +0800 Subject: [PATCH 0063/2187] Revert "Add x86 CPU Topology Support" Because CI is failed, we need to revert. This reverts commit e930d3513222dc1c8e73e7bc2c76c3be413834ca. --- cpu/src/aarch64/mod.rs | 17 --- cpu/src/lib.rs | 67 ++------ cpu/src/x86_64/mod.rs | 101 +------------ docs/config_guidebook.md | 17 +-- machine/src/lib.rs | 5 +- machine/src/micro_vm/mod.rs | 17 +-- machine/src/standard_vm/aarch64/mod.rs | 9 +- machine/src/standard_vm/x86_64/mod.rs | 17 +-- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/machine_config.rs | 151 ++++++------------- 10 files changed, 81 insertions(+), 322 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index ecfdffc40..1c427e7f6 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -62,23 +62,6 @@ pub struct ArmCPUBootConfig { pub boot_pc: u64, } -#[derive(Default, Copy, Clone)] -pub struct ArmCPUTopology { - threads: u8, - cores: u8, - clusters: u8, -} - -impl ArmCPUTopology { - pub fn new() -> Self { - ArmCPUTopology::default(); - } - - pub fn set_topology(mut self) -> Self { - self - } -} - /// AArch64 CPU architect information #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 092d3d731..64a1fc5ec 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -104,16 +104,12 @@ pub use aarch64::ArmCPUBootConfig as CPUBootConfig; pub use aarch64::ArmCPUCaps as CPUCaps; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUState as ArchCPU; -#[cfg(target_arch = "aarch64")] -pub use aarch64::ArmCPUTopology as CPUTopology; #[cfg(target_arch = "x86_64")] use x86_64::caps::X86CPUCaps as CPUCaps; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUState as ArchCPU; -#[cfg(target_arch = "x86_64")] -pub use x86_64::X86CPUTopology as CPUTopology; use std::cell::RefCell; use std::sync::atomic::fence; @@ -162,7 +158,7 @@ pub enum CpuLifecycleState { #[allow(clippy::upper_case_acronyms)] pub trait CPUInterface { /// Realize `CPU` structure, set registers value for `CPU`. - fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()>; + fn realize(&self, boot: &CPUBootConfig) -> Result<()>; /// Start `CPU` thread and run virtual CPU in kvm. /// @@ -294,7 +290,7 @@ impl CPU { } impl CPUInterface for CPU { - fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()> { + fn realize(&self, boot: &CPUBootConfig) -> Result<()> { let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { return Err( @@ -307,14 +303,6 @@ impl CPUInterface for CPU { .unwrap() .set_boot_config(&self.fd, boot) .chain_err(|| "Failed to realize arch cpu")?; - - #[cfg(target_arch = "x86_64")] - self.arch_cpu - .lock() - .unwrap() - .set_cpu_topology(topology) - .chain_err(|| "Failed to realize arch cpu")?; - self.boot_state.lock().unwrap().set(&self.arch_cpu); Ok(()) } @@ -712,8 +700,6 @@ impl CPUThreadWorker { pub struct CpuTopology { /// Number of sockets in VM. pub sockets: u8, - /// Number of dies on one socket. - pub dies: u8, /// Number of cores in VM. pub cores: u8, /// Number of threads in VM. @@ -731,26 +717,15 @@ impl CpuTopology { /// /// # Arguments /// - /// * `nr_cpus`: Number of vcpus in one VM. - /// * `nr_threads`: Number of threads on one core. - /// * `nr_cores`: Number of cores on one socket - /// * `nr_sockets`: Number of sockets on in one VM. - pub fn new( - nr_cpus: u8, - nr_threads: u8, - nr_cores: u8, - nr_dies: u8, - nr_sockets: u8, - max_cpus: u8, - ) -> Self { + /// * `nr_cpus`: Number of vcpus. + pub fn new(nr_cpus: u8) -> Self { let mask: Vec = vec![1; nr_cpus as usize]; Self { - sockets: nr_sockets, - dies: nr_dies, - cores: nr_cores, - threads: nr_threads, + sockets: nr_cpus, + cores: 1, + threads: 1, nrcpus: nr_cpus, - max_cpus, + max_cpus: nr_cpus, online_mask: Arc::new(Mutex::new(mask)), } } @@ -777,9 +752,11 @@ impl CpuTopology { /// /// * `vcpu_id` - ID of vcpu. pub fn get_topo(&self, vcpu_id: usize) -> (u8, u8, u8) { - let socketid: u8 = vcpu_id as u8 / (self.dies * self.cores * self.threads); - let coreid: u8 = (vcpu_id as u8 / self.threads) % self.cores; - let threadid: u8 = vcpu_id as u8 % self.threads; + let cpu_per_socket = self.cores * self.threads; + let cpu_per_core = self.threads; + let socketid: u8 = vcpu_id as u8 / cpu_per_socket; + let coreid: u8 = (vcpu_id as u8 % cpu_per_socket) / cpu_per_core; + let threadid: u8 = (vcpu_id as u8 % cpu_per_socket) % cpu_per_core; (socketid, coreid, threadid) } } @@ -952,7 +929,6 @@ mod tests { let microvm_cpu_topo = CpuTopology { sockets: test_nr_cpus, - dies: 1, cores: 1, threads: 1, nrcpus: test_nr_cpus, @@ -965,27 +941,10 @@ mod tests { assert_eq!(microvm_cpu_topo.get_topo(8), (8, 0, 0)); assert_eq!(microvm_cpu_topo.get_topo(15), (15, 0, 0)); - let mask = Vec::with_capacity(test_nr_cpus as usize); - let microvm_cpu_topo1 = CpuTopology { - sockets: 1, - dies: 2, - cores: 4, - threads: 2, - nrcpus: test_nr_cpus, - max_cpus: test_nr_cpus, - online_mask: Arc::new(Mutex::new(mask)), - }; - - assert_eq!(microvm_cpu_topo1.get_topo(0), (0, 0, 0)); - assert_eq!(microvm_cpu_topo1.get_topo(4), (0, 2, 0)); - assert_eq!(microvm_cpu_topo1.get_topo(8), (0, 0, 0)); - assert_eq!(microvm_cpu_topo1.get_topo(15), (0, 3, 1)); - let test_nr_cpus: u8 = 32; let mask = Vec::with_capacity(test_nr_cpus as usize); let test_cpu_topo = CpuTopology { sockets: 2, - dies: 1, cores: 4, threads: 2, nrcpus: test_nr_cpus, diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 43ed5e1ea..37e0f8478 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -49,11 +49,6 @@ const MSR_LIST: &[u32] = &[ const MSR_IA32_MISC_ENABLE: u32 = 0x01a0; const MSR_IA32_MISC_ENABLE_FAST_STRING: u64 = 0x1; -const ECX_INVALID: u32 = 0u32 << 8; -const ECX_THREAD: u32 = 1u32 << 8; -const ECX_CORE: u32 = 2u32 << 8; -const ECX_DIE: u32 = 5u32 << 8; - /// X86 CPU booting configure information #[allow(clippy::upper_case_acronyms)] #[derive(Default, Clone)] @@ -77,27 +72,6 @@ pub struct X86CPUBootConfig { pub pml4_start: u64, } -#[allow(clippy::upper_case_acronyms)] -#[derive(Default, Copy, Clone)] -pub struct X86CPUTopology { - threads: u8, - cores: u8, - dies: u8, -} - -impl X86CPUTopology { - pub fn new() -> Self { - X86CPUTopology::default() - } - - pub fn set_topology(mut self, toplogy: (u8, u8, u8)) -> Self { - self.threads = toplogy.0; - self.cores = toplogy.1; - self.dies = toplogy.2; - self - } -} - /// The state of vCPU's register. #[allow(clippy::upper_case_acronyms)] #[repr(C)] @@ -105,10 +79,6 @@ impl X86CPUTopology { #[desc_version(compat_version = "0.1.0")] pub struct X86CPUState { nr_vcpus: u32, - nr_threads: u32, - nr_cores: u32, - nr_dies: u32, - nr_sockets: u32, apic_id: u32, regs: kvm_regs, sregs: kvm_sregs, @@ -142,10 +112,6 @@ impl X86CPUState { apic_id: vcpu_id, nr_vcpus, mp_state, - nr_threads: 1, - nr_cores: 1, - nr_dies: 1, - nr_sockets: 1, ..Default::default() } } @@ -187,18 +153,6 @@ impl X86CPUState { Ok(()) } - /// Set cpu topology - /// - /// # Arguments - /// - /// * `topology`: 0: threads, 1: cores, 2: dies - pub fn set_cpu_topology(&mut self, topology: &X86CPUTopology) -> Result<()> { - self.nr_threads = topology.threads as u32; - self.nr_cores = topology.cores as u32; - self.nr_dies = topology.dies as u32; - Ok(()) - } - /// Reset register value with `X86CPUState`. /// /// # Arguments @@ -390,9 +344,6 @@ impl X86CPUState { } fn setup_cpuid(&self, vcpu_fd: &Arc) -> Result<()> { - let core_offset = 32u32 - (self.nr_threads - 1).leading_zeros(); - let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset; - let pkg_offset = (32u32 - (self.nr_dies - 1).leading_zeros()) + die_offset; let sys_fd = match Kvm::new() { Ok(fd) => fd, _ => bail!("setup_cpuid: Open /dev/kvm failed"), @@ -455,56 +406,20 @@ impl X86CPUState { entry.ecx = entry.index & 0xff; match entry.index { 0 => { - entry.eax = core_offset; - entry.ebx = self.nr_threads; - entry.ecx |= ECX_THREAD; - } - 1 => { - entry.eax = pkg_offset; - entry.ebx = self.nr_threads * self.nr_cores; - entry.ecx |= ECX_CORE; - } - _ => { - entry.eax = 0; - entry.ebx = 0; - entry.ecx |= ECX_INVALID; - } - } - } - 0x1f => { - if self.nr_dies < 2 { - entry.eax = 0; - entry.ebx = 0; - entry.ecx = 0; - entry.edx = 0; - continue; - } - - entry.edx = self.apic_id as u32; - entry.ecx = entry.index & 0xff; - - match entry.index { - 0 => { - entry.eax = core_offset; - entry.ebx = self.nr_threads; - entry.ecx |= ECX_THREAD; + entry.eax = 0u32; + entry.ebx = 1u32; + entry.ecx |= 1u32 << 8; } 1 => { - entry.eax = die_offset; - entry.ebx = self.nr_cores * self.nr_threads; - entry.ecx |= ECX_CORE; - } - 2 => { - entry.eax = pkg_offset; - entry.ebx = self.nr_dies * self.nr_cores * self.nr_threads; - entry.ecx |= ECX_DIE; + entry.eax = 32u32 - self.nr_vcpus.leading_zeros(); + entry.ebx = self.nr_vcpus; + entry.ecx |= 2u32 << 8; } _ => { - entry.eax = 0; - entry.ebx = 0; - entry.ecx |= ECX_INVALID; + entry.ebx = 0xff; } } + entry.ebx &= 0xffff; } 0x8000_0002..=0x8000_0004 => { // Passthrough host cpu model name directly to guest diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index c902cf8a3..ff586eaf2 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -30,19 +30,18 @@ This allows you to set the maximum number of VCPUs that VM will support. The max By default, after booted, VM will online all CPUs you set. Four properties are supported for `smp`. * cpus: the number of VCPUs. -* maxcpus: the number of max VCPUs. -* sockets: the number of socket. (optional). If not set, its value depends on the value of `maxcpus`. -* dies: the number of dies. (optional). If not set, default is one. -* cores: the number of core. (optional). If not set, its value depends on the value of `maxcpus`. -* threads: the number of thread. (optional). If not set, its value depends on the value of `maxcpus`. +* sockets: the number of socket. (optional). If not set, default is the value of `cpus`. +* cores: the number of core. (optional). If not set, default is one. +* threads: the number of thread. (optional). If not set, default is one. +NB: the arguments of cpu topology is used to interconnect with libvirt, but the cpu topology of StratoVirt +is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). +If it is configured, the sockets number should equals to the number of cpu, `cores` should be `1` +and `threads` should be `1`. -NB: the arguments of cpu topology is used to interconnect with libvirt. The x86 cpu topology has supported. But the ARM cpu topology of StratoVirt is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). - -If it is configured, sockets * dies * cores * threads must be equal to maxcpus, and maxcpus should be larger than cpus. ```shell # cmdline --smp [cpus=]n[,maxcpus=,sockets=,dies=,cores=,threads=] +-smp [cpus=]n[,sockets=n,cores=1,threads=1] ``` ### 1.3 Memory diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 07c7b7354..3633b5535 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -118,7 +118,7 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; -use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; +use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; @@ -221,7 +221,6 @@ pub trait MachineOps { fn init_vcpu( vm: Arc>, nr_cpus: u8, - topology: &CPUTopology, fds: &[Arc], boot_cfg: &Option, ) -> Result>> @@ -254,7 +253,7 @@ pub trait MachineOps { if let Some(boot_config) = boot_cfg { for cpu_index in 0..nr_cpus as usize { cpus[cpu_index as usize] - .realize(boot_config, topology) + .realize(boot_config) .chain_err(|| { format!( "Failed to realize arch cpu register for CPU {}/KVM", diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 189e8acea..fc8595501 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -66,7 +66,7 @@ use std::vec::Vec; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CpuLifecycleState, CpuTopology, CPU}; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; #[cfg(target_arch = "x86_64")] @@ -224,14 +224,7 @@ impl LightMachine { } Ok(LightMachine { - cpu_topo: CpuTopology::new( - vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - vm_config.machine_config.nr_sockets, - vm_config.machine_config.max_cpus, - ), + cpu_topo: CpuTopology::new(vm_config.machine_config.nr_cpus), cpus: Vec::new(), #[cfg(target_arch = "aarch64")] irq_chip: None, @@ -746,15 +739,9 @@ impl MachineOps for LightMachine { } else { None }; - let topology = CPUTopology::new().set_topology(( - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, - &topology, &vcpu_fds, &boot_config, )?); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 07a2fb8a0..48b773bf9 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -131,14 +131,7 @@ impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new( - vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - vm_config.machine_config.nr_sockets, - vm_config.machine_config.max_cpus, - ); + let cpu_topo = CpuTopology::new(vm_config.machine_config.nr_cpus); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) .chain_err(|| ErrorKind::CrtIoSpaceErr)?; let sysbus = SysBus::new( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 0e6dc159d..954013f16 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -27,7 +27,7 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; @@ -111,14 +111,7 @@ impl StdMachine { pub fn new(vm_config: &VmConfig) -> MachineResult { use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new( - vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - vm_config.machine_config.nr_sockets, - vm_config.machine_config.max_cpus, - ); + let cpu_topo = CpuTopology::new(vm_config.machine_config.nr_cpus); let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) .chain_err(|| MachineErrorKind::CrtMemSpaceErr)?; let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) @@ -456,15 +449,9 @@ impl MachineOps for StdMachine { } else { (None, None) }; - let topology = CPUTopology::new().set_topology(( - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, - &topology, &vcpu_fds, &boot_config, )?); diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 95adff176..875d7fa88 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -94,7 +94,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("smp") .long("smp") - .value_name("[cpus=]n[,maxcpus=cpus][,dies=dies][,sockets=sockets][,cores=cores][,threads=threads]") + .value_name("[cpus=]n") .help("set the number of CPUs to 'n' (default: 1)") .takes_value(true), ) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index d2879fc04..bf0c21e9b 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -18,11 +18,6 @@ use super::errors::{ErrorKind, Result, ResultExt}; use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig}; const DEFAULT_CPUS: u8 = 1; -const DEFAULT_THREADS: u8 = 1; -const DEFAULT_CORES: u8 = 1; -const DEFAULT_DIES: u8 = 1; -const DEFAULT_SOCKETS: u8 = 1; -const DEFAULT_MAX_CPUS: u8 = 1; const DEFAULT_MEMSIZE: u64 = 256; const MAX_NR_CPUS: u64 = 254; const MIN_NR_CPUS: u64 = 1; @@ -82,11 +77,6 @@ impl Default for MachineMemConfig { pub struct MachineConfig { pub mach_type: MachineType, pub nr_cpus: u8, - pub nr_threads: u8, - pub nr_cores: u8, - pub nr_dies: u8, - pub nr_sockets: u8, - pub max_cpus: u8, pub mem_config: MachineMemConfig, } @@ -96,11 +86,6 @@ impl Default for MachineConfig { MachineConfig { mach_type: MachineType::MicroVm, nr_cpus: DEFAULT_CPUS, - nr_threads: DEFAULT_THREADS, - nr_cores: DEFAULT_CORES, - nr_dies: DEFAULT_DIES, - nr_sockets: DEFAULT_SOCKETS, - max_cpus: DEFAULT_MAX_CPUS, mem_config: MachineMemConfig::default(), } } @@ -200,9 +185,7 @@ impl VmConfig { let mut cmd_parser = CmdParser::new("smp"); cmd_parser .push("") - .push("maxcpus") .push("sockets") - .push("dies") .push("cores") .push("threads") .push("cpus"); @@ -212,69 +195,42 @@ impl VmConfig { let cpu = if let Some(cpu) = cmd_parser.get_value::("")? { cpu } else if let Some(cpu) = cmd_parser.get_value::("cpus")? { - if cpu == 0 { - return Err( - ErrorKind::IllegalValue("cpu".to_string(), 1, true, u64::MAX, true).into(), - ); - } cpu } else { return Err(ErrorKind::FieldIsMissing("cpus", "smp").into()); }; - let mut sockets = cmd_parser.get_value::("sockets")?.unwrap_or_default(); - - let dies = cmd_parser.get_value::("dies")?.unwrap_or(1); - - let mut cores = cmd_parser.get_value::("cores")?.unwrap_or_default(); - - let mut threads = cmd_parser.get_value::("threads")?.unwrap_or_default(); - - let mut max_cpus = cmd_parser.get_value::("maxcpus")?.unwrap_or_default(); - - (max_cpus, sockets, cores, threads) = adjust_topology(cpu, max_cpus, sockets, dies, cores, threads); + if let Some(sockets) = cmd_parser.get_value::("sockets")? { + if sockets.ne(&cpu) { + bail!("Invalid \'sockets\' arguments for \'smp\', it should equal to the number of cpus"); + } + } + if let Some(cores) = cmd_parser.get_value::("cores")? { + if cores.ne(&1) { + bail!("Invalid \'cores\' arguments for \'smp\', it should be \'1\'"); + } + } + if let Some(threads) = cmd_parser.get_value::("threads")? { + if threads.ne(&1) { + bail!("Invalid \'threads\' arguments for \'smp\', it should be \'1\'"); + } + } // limit cpu count if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&cpu) { return Err(ErrorKind::IllegalValue( "CPU number".to_string(), - MIN_NR_CPUS as u64, + MIN_NR_CPUS, true, - MAX_NR_CPUS as u64, - true, - ) - .into()); - } - - // limit cpu count - if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&max_cpus) { - return Err(ErrorKind::IllegalValue( - "MAX CPU number".to_string(), - MIN_NR_CPUS as u64, - true, - MAX_NR_CPUS as u64, - true, - ) - .into()); - } - - if max_cpus < cpu || sockets * dies * cores * threads != max_cpus { - return Err(ErrorKind::IllegalValue( - "maxcpus".to_string(), - cpu as u64, - true, - (sockets * dies * cores * threads) as u64, + MAX_NR_CPUS, true, ) .into()); } + // it is safe, as value limited before self.machine_config.nr_cpus = cpu as u8; - self.machine_config.nr_threads = threads as u8; - self.machine_config.nr_cores = cores as u8; - self.machine_config.nr_dies = dies as u8; - self.machine_config.nr_sockets = sockets as u8; - self.machine_config.max_cpus = max_cpus as u8; + Ok(()) } @@ -288,44 +244,6 @@ impl VmConfig { } } -fn adjust_topology( - cpu: u64, - mut max_cpus: u64, - mut sockets: u64, - dies: u64, - mut cores: u64, - mut threads: u64, -) -> (u64, u64, u64, u64) { - if max_cpus == 0 { - if sockets * dies * cores * threads > 0 { - max_cpus = sockets * dies * cores * threads; - } else { - max_cpus = cpu; - } - } - - if cores == 0 { - if sockets == 0 { - sockets = 1; - } - if threads == 0 { - threads = 1; - } - cores = max_cpus / (sockets * dies * threads); - } else if sockets == 0 { - if threads == 0 { - threads = 1; - } - sockets = max_cpus / (dies * cores * threads); - } - - if threads == 0 { - threads = max_cpus / (sockets * dies * cores); - } - - (max_cpus, sockets, cores, threads) -} - fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) @@ -387,12 +305,7 @@ mod tests { }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, - nr_cpus: 1, - nr_cores: 1, - nr_threads: 1, - nr_dies: 1, - nr_sockets: 1, - max_cpus: MIN_NR_CPUS as u8, + nr_cpus: MIN_NR_CPUS as u8, mem_config: memory_config, }; assert!(machine_config.check().is_ok()); @@ -709,5 +622,29 @@ mod tests { let cpu_cfg_str = "cpus=255,sockets=255,cores=1,threads=1"; let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=4,cores=2,threads=1"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=2,cores=2,threads=2"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=1,cores=4,threads=2"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); + + // not supported yet + let mut vm_config = VmConfig::default(); + let cpu_cfg_str = "cpus=8,sockets=1,cores=2,threads=4"; + let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); + assert!(cpu_cfg_ret.is_err()); } } -- Gitee From 71f12c9d0663d0cc9ac8afe194e0486200bbbeca Mon Sep 17 00:00:00 2001 From: uran0sH Date: Mon, 13 Jun 2022 19:48:38 +0800 Subject: [PATCH 0064/2187] Add x86 CPU Topology Support Users can use "-smp n,maxcpus=,sockets=,dies=,cores=,threads=" to configure the cpu topology in x86. Signed-off-by: uran0sH --- cpu/src/aarch64/mod.rs | 27 ++++ cpu/src/lib.rs | 70 +++++++--- cpu/src/x86_64/mod.rs | 101 ++++++++++++-- docs/config_guidebook.md | 18 +-- machine/src/lib.rs | 5 +- machine/src/micro_vm/mod.rs | 17 ++- machine/src/standard_vm/aarch64/mod.rs | 12 +- machine/src/standard_vm/x86_64/mod.rs | 17 ++- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/machine_config.rs | 135 +++++++++++++------ 10 files changed, 321 insertions(+), 83 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 1c427e7f6..77e195df4 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -62,6 +62,24 @@ pub struct ArmCPUBootConfig { pub boot_pc: u64, } +#[allow(dead_code)] +#[derive(Default, Copy, Clone)] +pub struct ArmCPUTopology { + threads: u8, + cores: u8, + clusters: u8, +} + +impl ArmCPUTopology { + pub fn new() -> Self { + ArmCPUTopology::default() + } + + pub fn set_topology(self, _topology: (u8, u8, u8)) -> Self { + self + } +} + /// AArch64 CPU architect information #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] @@ -161,6 +179,15 @@ impl ArmCPUState { Ok(()) } + /// Set cpu topology + /// + /// # Arguments + /// + /// * `topology` - ARM CPU Topology + pub fn set_cpu_topology(&mut self, _topology: &ArmCPUTopology) -> Result<()> { + Ok(()) + } + /// Reset register value in `Kvm` with `ArmCPUState`. /// /// # Arguments diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 64a1fc5ec..b8f849290 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -104,12 +104,16 @@ pub use aarch64::ArmCPUBootConfig as CPUBootConfig; pub use aarch64::ArmCPUCaps as CPUCaps; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUState as ArchCPU; +#[cfg(target_arch = "aarch64")] +pub use aarch64::ArmCPUTopology as CPUTopology; #[cfg(target_arch = "x86_64")] use x86_64::caps::X86CPUCaps as CPUCaps; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUState as ArchCPU; +#[cfg(target_arch = "x86_64")] +pub use x86_64::X86CPUTopology as CPUTopology; use std::cell::RefCell; use std::sync::atomic::fence; @@ -158,7 +162,7 @@ pub enum CpuLifecycleState { #[allow(clippy::upper_case_acronyms)] pub trait CPUInterface { /// Realize `CPU` structure, set registers value for `CPU`. - fn realize(&self, boot: &CPUBootConfig) -> Result<()>; + fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()>; /// Start `CPU` thread and run virtual CPU in kvm. /// @@ -290,7 +294,7 @@ impl CPU { } impl CPUInterface for CPU { - fn realize(&self, boot: &CPUBootConfig) -> Result<()> { + fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()> { let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { return Err( @@ -303,6 +307,13 @@ impl CPUInterface for CPU { .unwrap() .set_boot_config(&self.fd, boot) .chain_err(|| "Failed to realize arch cpu")?; + + self.arch_cpu + .lock() + .unwrap() + .set_cpu_topology(topology) + .chain_err(|| "Failed to realize arch cpu")?; + self.boot_state.lock().unwrap().set(&self.arch_cpu); Ok(()) } @@ -700,6 +711,8 @@ impl CPUThreadWorker { pub struct CpuTopology { /// Number of sockets in VM. pub sockets: u8, + /// Number of dies on one socket. + pub dies: u8, /// Number of cores in VM. pub cores: u8, /// Number of threads in VM. @@ -713,19 +726,26 @@ pub struct CpuTopology { } impl CpuTopology { - /// Init CpuTopology structure. - /// - /// # Arguments - /// - /// * `nr_cpus`: Number of vcpus. - pub fn new(nr_cpus: u8) -> Self { + /// * `nr_cpus`: Number of vcpus in one VM. + /// * `nr_threads`: Number of threads on one core. + /// * `nr_cores`: Number of cores on one socket + /// * `nr_sockets`: Number of sockets on in one VM. + pub fn new( + nr_cpus: u8, + nr_threads: u8, + nr_cores: u8, + nr_dies: u8, + nr_sockets: u8, + max_cpus: u8, + ) -> Self { let mask: Vec = vec![1; nr_cpus as usize]; Self { - sockets: nr_cpus, - cores: 1, - threads: 1, + sockets: nr_sockets, + dies: nr_dies, + cores: nr_cores, + threads: nr_threads, nrcpus: nr_cpus, - max_cpus: nr_cpus, + max_cpus, online_mask: Arc::new(Mutex::new(mask)), } } @@ -752,11 +772,9 @@ impl CpuTopology { /// /// * `vcpu_id` - ID of vcpu. pub fn get_topo(&self, vcpu_id: usize) -> (u8, u8, u8) { - let cpu_per_socket = self.cores * self.threads; - let cpu_per_core = self.threads; - let socketid: u8 = vcpu_id as u8 / cpu_per_socket; - let coreid: u8 = (vcpu_id as u8 % cpu_per_socket) / cpu_per_core; - let threadid: u8 = (vcpu_id as u8 % cpu_per_socket) % cpu_per_core; + let socketid: u8 = vcpu_id as u8 / (self.dies * self.cores * self.threads); + let coreid: u8 = (vcpu_id as u8 / self.threads) % self.cores; + let threadid: u8 = vcpu_id as u8 % self.threads; (socketid, coreid, threadid) } } @@ -929,6 +947,7 @@ mod tests { let microvm_cpu_topo = CpuTopology { sockets: test_nr_cpus, + dies: 1, cores: 1, threads: 1, nrcpus: test_nr_cpus, @@ -941,10 +960,27 @@ mod tests { assert_eq!(microvm_cpu_topo.get_topo(8), (8, 0, 0)); assert_eq!(microvm_cpu_topo.get_topo(15), (15, 0, 0)); + let mask = Vec::with_capacity(test_nr_cpus as usize); + let microvm_cpu_topo1 = CpuTopology { + sockets: 1, + dies: 2, + cores: 4, + threads: 2, + nrcpus: test_nr_cpus, + max_cpus: test_nr_cpus, + online_mask: Arc::new(Mutex::new(mask)), + }; + + assert_eq!(microvm_cpu_topo1.get_topo(0), (0, 0, 0)); + assert_eq!(microvm_cpu_topo1.get_topo(4), (0, 2, 0)); + assert_eq!(microvm_cpu_topo1.get_topo(8), (0, 0, 0)); + assert_eq!(microvm_cpu_topo1.get_topo(15), (0, 3, 1)); + let test_nr_cpus: u8 = 32; let mask = Vec::with_capacity(test_nr_cpus as usize); let test_cpu_topo = CpuTopology { sockets: 2, + dies: 1, cores: 4, threads: 2, nrcpus: test_nr_cpus, diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 37e0f8478..1122715bf 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -49,6 +49,11 @@ const MSR_LIST: &[u32] = &[ const MSR_IA32_MISC_ENABLE: u32 = 0x01a0; const MSR_IA32_MISC_ENABLE_FAST_STRING: u64 = 0x1; +const ECX_INVALID: u32 = 0u32 << 8; +const ECX_THREAD: u32 = 1u32 << 8; +const ECX_CORE: u32 = 2u32 << 8; +const ECX_DIE: u32 = 5u32 << 8; + /// X86 CPU booting configure information #[allow(clippy::upper_case_acronyms)] #[derive(Default, Clone)] @@ -72,6 +77,27 @@ pub struct X86CPUBootConfig { pub pml4_start: u64, } +#[allow(clippy::upper_case_acronyms)] +#[derive(Default, Copy, Clone)] +pub struct X86CPUTopology { + threads: u8, + cores: u8, + dies: u8, +} + +impl X86CPUTopology { + pub fn new() -> Self { + X86CPUTopology::default() + } + + pub fn set_topology(mut self, toplogy: (u8, u8, u8)) -> Self { + self.threads = toplogy.0; + self.cores = toplogy.1; + self.dies = toplogy.2; + self + } +} + /// The state of vCPU's register. #[allow(clippy::upper_case_acronyms)] #[repr(C)] @@ -79,6 +105,10 @@ pub struct X86CPUBootConfig { #[desc_version(compat_version = "0.1.0")] pub struct X86CPUState { nr_vcpus: u32, + nr_threads: u32, + nr_cores: u32, + nr_dies: u32, + nr_sockets: u32, apic_id: u32, regs: kvm_regs, sregs: kvm_sregs, @@ -112,6 +142,10 @@ impl X86CPUState { apic_id: vcpu_id, nr_vcpus, mp_state, + nr_threads: 1, + nr_cores: 1, + nr_dies: 1, + nr_sockets: 1, ..Default::default() } } @@ -153,6 +187,18 @@ impl X86CPUState { Ok(()) } + /// Set cpu topology + /// + /// # Arguments + /// + /// * `topology` - X86 CPU Topology + pub fn set_cpu_topology(&mut self, topology: &X86CPUTopology) -> Result<()> { + self.nr_threads = topology.threads as u32; + self.nr_cores = topology.cores as u32; + self.nr_dies = topology.dies as u32; + Ok(()) + } + /// Reset register value with `X86CPUState`. /// /// # Arguments @@ -344,6 +390,9 @@ impl X86CPUState { } fn setup_cpuid(&self, vcpu_fd: &Arc) -> Result<()> { + let core_offset = 32u32 - (self.nr_threads - 1).leading_zeros(); + let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset; + let pkg_offset = (32u32 - (self.nr_dies - 1).leading_zeros()) + die_offset; let sys_fd = match Kvm::new() { Ok(fd) => fd, _ => bail!("setup_cpuid: Open /dev/kvm failed"), @@ -406,20 +455,56 @@ impl X86CPUState { entry.ecx = entry.index & 0xff; match entry.index { 0 => { - entry.eax = 0u32; - entry.ebx = 1u32; - entry.ecx |= 1u32 << 8; + entry.eax = core_offset; + entry.ebx = self.nr_threads; + entry.ecx |= ECX_THREAD; + } + 1 => { + entry.eax = pkg_offset; + entry.ebx = self.nr_threads * self.nr_cores; + entry.ecx |= ECX_CORE; + } + _ => { + entry.eax = 0; + entry.ebx = 0; + entry.ecx |= ECX_INVALID; + } + } + } + 0x1f => { + if self.nr_dies < 2 { + entry.eax = 0; + entry.ebx = 0; + entry.ecx = 0; + entry.edx = 0; + continue; + } + + entry.edx = self.apic_id as u32; + entry.ecx = entry.index & 0xff; + + match entry.index { + 0 => { + entry.eax = core_offset; + entry.ebx = self.nr_threads; + entry.ecx |= ECX_THREAD; } 1 => { - entry.eax = 32u32 - self.nr_vcpus.leading_zeros(); - entry.ebx = self.nr_vcpus; - entry.ecx |= 2u32 << 8; + entry.eax = die_offset; + entry.ebx = self.nr_cores * self.nr_threads; + entry.ecx |= ECX_CORE; + } + 2 => { + entry.eax = pkg_offset; + entry.ebx = self.nr_dies * self.nr_cores * self.nr_threads; + entry.ecx |= ECX_DIE; } _ => { - entry.ebx = 0xff; + entry.eax = 0; + entry.ebx = 0; + entry.ecx |= ECX_INVALID; } } - entry.ebx &= 0xffff; } 0x8000_0002..=0x8000_0004 => { // Passthrough host cpu model name directly to guest diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index ff586eaf2..9e79d88b5 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -30,18 +30,20 @@ This allows you to set the maximum number of VCPUs that VM will support. The max By default, after booted, VM will online all CPUs you set. Four properties are supported for `smp`. * cpus: the number of VCPUs. -* sockets: the number of socket. (optional). If not set, default is the value of `cpus`. -* cores: the number of core. (optional). If not set, default is one. -* threads: the number of thread. (optional). If not set, default is one. -NB: the arguments of cpu topology is used to interconnect with libvirt, but the cpu topology of StratoVirt -is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). -If it is configured, the sockets number should equals to the number of cpu, `cores` should be `1` -and `threads` should be `1`. +* maxcpus: the number of max VCPUs. +* sockets: the number of socket. (optional). If not set, its value depends on the value of `maxcpus`. +* dies: the number of dies. (optional). If not set, default is one. +* cores: the number of core. (optional). If not set, its value depends on the value of `maxcpus`. +* threads: the number of thread. (optional). If not set, its value depends on the value of `maxcpus`. + +NB: the arguments of cpu topology is used to interconnect with libvirt. The x86 cpu topology has supported. But the ARM cpu topology of StratoVirt is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). + +If it is configured, sockets * dies * cores * threads must be equal to maxcpus, and maxcpus should be larger than cpus. ```shell # cmdline --smp [cpus=]n[,sockets=n,cores=1,threads=1] +-smp [cpus=]n[,maxcpus=,sockets=,dies=,cores=,threads=] ``` ### 1.3 Memory diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3633b5535..07c7b7354 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -118,7 +118,7 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; -use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU}; +use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; @@ -221,6 +221,7 @@ pub trait MachineOps { fn init_vcpu( vm: Arc>, nr_cpus: u8, + topology: &CPUTopology, fds: &[Arc], boot_cfg: &Option, ) -> Result>> @@ -253,7 +254,7 @@ pub trait MachineOps { if let Some(boot_config) = boot_cfg { for cpu_index in 0..nr_cpus as usize { cpus[cpu_index as usize] - .realize(boot_config) + .realize(boot_config, topology) .chain_err(|| { format!( "Failed to realize arch cpu register for CPU {}/KVM", diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index fc8595501..189e8acea 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -66,7 +66,7 @@ use std::vec::Vec; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CpuLifecycleState, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState, CpuTopology, CPU}; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; #[cfg(target_arch = "x86_64")] @@ -224,7 +224,14 @@ impl LightMachine { } Ok(LightMachine { - cpu_topo: CpuTopology::new(vm_config.machine_config.nr_cpus), + cpu_topo: CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.max_cpus, + ), cpus: Vec::new(), #[cfg(target_arch = "aarch64")] irq_chip: None, @@ -739,9 +746,15 @@ impl MachineOps for LightMachine { } else { None }; + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, + &topology, &vcpu_fds, &boot_config, )?); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 48b773bf9..e78700686 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -29,7 +29,7 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; @@ -131,7 +131,14 @@ impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new(vm_config.machine_config.nr_cpus); + let cpu_topo = CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.max_cpus, + ); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) .chain_err(|| ErrorKind::CrtIoSpaceErr)?; let sysbus = SysBus::new( @@ -442,6 +449,7 @@ impl MachineOps for StdMachine { locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, + &CPUTopology::new(), &vcpu_fds, &boot_config, )?); diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 954013f16..0e6dc159d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -27,7 +27,7 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; @@ -111,7 +111,14 @@ impl StdMachine { pub fn new(vm_config: &VmConfig) -> MachineResult { use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new(vm_config.machine_config.nr_cpus); + let cpu_topo = CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.max_cpus, + ); let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) .chain_err(|| MachineErrorKind::CrtMemSpaceErr)?; let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) @@ -449,9 +456,15 @@ impl MachineOps for StdMachine { } else { (None, None) }; + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, + &topology, &vcpu_fds, &boot_config, )?); diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 875d7fa88..95adff176 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -94,7 +94,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("smp") .long("smp") - .value_name("[cpus=]n") + .value_name("[cpus=]n[,maxcpus=cpus][,dies=dies][,sockets=sockets][,cores=cores][,threads=threads]") .help("set the number of CPUs to 'n' (default: 1)") .takes_value(true), ) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index bf0c21e9b..fb428358c 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -18,6 +18,11 @@ use super::errors::{ErrorKind, Result, ResultExt}; use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig}; const DEFAULT_CPUS: u8 = 1; +const DEFAULT_THREADS: u8 = 1; +const DEFAULT_CORES: u8 = 1; +const DEFAULT_DIES: u8 = 1; +const DEFAULT_SOCKETS: u8 = 1; +const DEFAULT_MAX_CPUS: u8 = 1; const DEFAULT_MEMSIZE: u64 = 256; const MAX_NR_CPUS: u64 = 254; const MIN_NR_CPUS: u64 = 1; @@ -77,6 +82,11 @@ impl Default for MachineMemConfig { pub struct MachineConfig { pub mach_type: MachineType, pub nr_cpus: u8, + pub nr_threads: u8, + pub nr_cores: u8, + pub nr_dies: u8, + pub nr_sockets: u8, + pub max_cpus: u8, pub mem_config: MachineMemConfig, } @@ -86,6 +96,11 @@ impl Default for MachineConfig { MachineConfig { mach_type: MachineType::MicroVm, nr_cpus: DEFAULT_CPUS, + nr_threads: DEFAULT_THREADS, + nr_cores: DEFAULT_CORES, + nr_dies: DEFAULT_DIES, + nr_sockets: DEFAULT_SOCKETS, + max_cpus: DEFAULT_MAX_CPUS, mem_config: MachineMemConfig::default(), } } @@ -185,7 +200,9 @@ impl VmConfig { let mut cmd_parser = CmdParser::new("smp"); cmd_parser .push("") + .push("maxcpus") .push("sockets") + .push("dies") .push("cores") .push("threads") .push("cpus"); @@ -195,26 +212,28 @@ impl VmConfig { let cpu = if let Some(cpu) = cmd_parser.get_value::("")? { cpu } else if let Some(cpu) = cmd_parser.get_value::("cpus")? { + if cpu == 0 { + return Err( + ErrorKind::IllegalValue("cpu".to_string(), 1, true, u64::MAX, true).into(), + ); + } cpu } else { return Err(ErrorKind::FieldIsMissing("cpus", "smp").into()); }; - if let Some(sockets) = cmd_parser.get_value::("sockets")? { - if sockets.ne(&cpu) { - bail!("Invalid \'sockets\' arguments for \'smp\', it should equal to the number of cpus"); - } - } - if let Some(cores) = cmd_parser.get_value::("cores")? { - if cores.ne(&1) { - bail!("Invalid \'cores\' arguments for \'smp\', it should be \'1\'"); - } - } - if let Some(threads) = cmd_parser.get_value::("threads")? { - if threads.ne(&1) { - bail!("Invalid \'threads\' arguments for \'smp\', it should be \'1\'"); - } - } + let sockets = cmd_parser.get_value::("sockets")?.unwrap_or_default(); + + let dies = cmd_parser.get_value::("dies")?.unwrap_or(1); + + let cores = cmd_parser.get_value::("cores")?.unwrap_or_default(); + + let threads = cmd_parser.get_value::("threads")?.unwrap_or_default(); + + let max_cpus = cmd_parser.get_value::("maxcpus")?.unwrap_or_default(); + + let (max_cpus, sockets, cores, threads) = + adjust_topology(cpu, max_cpus, sockets, dies, cores, threads); // limit cpu count if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&cpu) { @@ -228,8 +247,23 @@ impl VmConfig { .into()); } - // it is safe, as value limited before + if max_cpus < cpu || sockets * dies * cores * threads != max_cpus { + return Err(ErrorKind::IllegalValue( + "maxcpus".to_string(), + cpu as u64, + true, + (sockets * dies * cores * threads) as u64, + true, + ) + .into()); + } + self.machine_config.nr_cpus = cpu as u8; + self.machine_config.nr_threads = threads as u8; + self.machine_config.nr_cores = cores as u8; + self.machine_config.nr_dies = dies as u8; + self.machine_config.nr_sockets = sockets as u8; + self.machine_config.max_cpus = max_cpus as u8; Ok(()) } @@ -244,6 +278,44 @@ impl VmConfig { } } +fn adjust_topology( + cpu: u64, + mut max_cpus: u64, + mut sockets: u64, + dies: u64, + mut cores: u64, + mut threads: u64, +) -> (u64, u64, u64, u64) { + if max_cpus == 0 { + if sockets * dies * cores * threads > 0 { + max_cpus = sockets * dies * cores * threads; + } else { + max_cpus = cpu; + } + } + + if cores == 0 { + if sockets == 0 { + sockets = 1; + } + if threads == 0 { + threads = 1; + } + cores = max_cpus / (sockets * dies * threads); + } else if sockets == 0 { + if threads == 0 { + threads = 1; + } + sockets = max_cpus / (dies * cores * threads); + } + + if threads == 0 { + threads = max_cpus / (sockets * dies * cores); + } + + (max_cpus, sockets, cores, threads) +} + fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) @@ -305,7 +377,12 @@ mod tests { }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, - nr_cpus: MIN_NR_CPUS as u8, + nr_cpus: 1, + nr_cores: 1, + nr_threads: 1, + nr_dies: 1, + nr_sockets: 1, + max_cpus: MIN_NR_CPUS as u8, mem_config: memory_config, }; assert!(machine_config.check().is_ok()); @@ -622,29 +699,5 @@ mod tests { let cpu_cfg_str = "cpus=255,sockets=255,cores=1,threads=1"; let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=4,cores=2,threads=1"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=2,cores=2,threads=2"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=1,cores=4,threads=2"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); - - // not supported yet - let mut vm_config = VmConfig::default(); - let cpu_cfg_str = "cpus=8,sockets=1,cores=2,threads=4"; - let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); - assert!(cpu_cfg_ret.is_err()); } } -- Gitee From f98271d07851376e11046b2f87ae7f9e95436364 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Tue, 14 Jun 2022 15:10:04 +0800 Subject: [PATCH 0065/2187] Revert "Revert "Merge branch 'dev' of https://gitee.com/openeuler/stratovirt into x86_cpu_topo"" This reverts commit 8dd2fba809a19cd5a85980465065073bd58cb7f0. --- acpi/src/acpi_device.rs | 1 + acpi/src/acpi_table.rs | 88 +++++ acpi/src/lib.rs | 7 +- acpi/src/table_loader.rs | 1 + address_space/src/host_mmap.rs | 59 +++- address_space/src/lib.rs | 11 +- address_space/src/listener.rs | 5 +- address_space/src/region.rs | 3 + address_space/src/state.rs | 2 + boot_loader/src/aarch64/mod.rs | 1 + boot_loader/src/lib.rs | 7 +- boot_loader/src/x86_64/direct_boot/mod.rs | 6 +- boot_loader/src/x86_64/mod.rs | 1 + boot_loader/src/x86_64/standard_boot/elf.rs | 1 + boot_loader/src/x86_64/standard_boot/mod.rs | 2 + cpu/src/aarch64/core_regs.rs | 5 +- cpu/src/aarch64/mod.rs | 8 +- cpu/src/lib.rs | 15 +- cpu/src/x86_64/mod.rs | 6 +- .../src/interrupt_controller/aarch64/gicv2.rs | 325 ++++++++++++++++++ .../src/interrupt_controller/aarch64/gicv3.rs | 118 +++---- .../src/interrupt_controller/aarch64/mod.rs | 117 ++++--- .../src/interrupt_controller/aarch64/state.rs | 5 +- devices/src/interrupt_controller/mod.rs | 8 +- devices/src/legacy/chardev.rs | 2 + devices/src/legacy/fwcfg.rs | 82 ++++- devices/src/legacy/mod.rs | 2 + devices/src/legacy/pflash.rs | 3 +- devices/src/legacy/pl011.rs | 2 + devices/src/legacy/pl031.rs | 2 + devices/src/legacy/rtc.rs | 1 + devices/src/legacy/serial.rs | 3 + devices/src/lib.rs | 9 +- docs/config_guidebook.md | 71 +++- .../standard_vm/kernel_config_4.19_aarch64 | 8 +- .../standard_vm/kernel_config_4.19_x86_64 | 8 +- .../standard_vm/kernel_config_5.10_aarch64 | 8 +- .../standard_vm/kernel_config_5.10_x86_64 | 8 +- hypervisor/src/kvm/interrupt.rs | 1 + hypervisor/src/kvm/mod.rs | 5 +- hypervisor/src/kvm/state.rs | 1 + hypervisor/src/lib.rs | 12 +- machine/src/lib.rs | 209 ++++++++++- machine/src/micro_vm/mod.rs | 36 +- machine/src/standard_vm/aarch64/mod.rs | 226 ++++++++++-- .../src/standard_vm/aarch64/pci_host_root.rs | 1 + machine/src/standard_vm/mod.rs | 110 +++++- machine/src/standard_vm/x86_64/ich9_lpc.rs | 3 +- machine/src/standard_vm/x86_64/mch.rs | 3 +- machine/src/standard_vm/x86_64/mod.rs | 239 ++++++++++--- machine_manager/src/cmdline.rs | 11 + machine_manager/src/config/balloon.rs | 1 + machine_manager/src/config/chardev.rs | 1 + machine_manager/src/config/drive.rs | 17 +- machine_manager/src/config/machine_config.rs | 151 +++++++- machine_manager/src/config/mod.rs | 57 ++- machine_manager/src/config/network.rs | 1 + machine_manager/src/config/numa.rs | 310 +++++++++++++++++ machine_manager/src/config/pci.rs | 2 + machine_manager/src/config/rng.rs | 2 + machine_manager/src/event_loop.rs | 4 +- machine_manager/src/lib.rs | 7 +- machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 121 +++---- machine_manager/src/signal_handler.rs | 1 - machine_manager/src/socket.rs | 2 + migration/src/device_state.rs | 2 + migration/src/lib.rs | 10 +- migration/src/manager.rs | 1 + migration/src/snapshot.rs | 2 + migration_derive/src/lib.rs | 5 +- ozone/src/capability.rs | 1 + ozone/src/cgroup.rs | 1 + ozone/src/handler.rs | 1 + ozone/src/main.rs | 7 +- pci/src/bus.rs | 2 + pci/src/host.rs | 8 + pci/src/hotplug.rs | 1 + pci/src/lib.rs | 65 +++- pci/src/msix.rs | 3 + pci/src/root_port.rs | 15 +- src/main.rs | 7 +- sysbus/src/lib.rs | 8 +- util/src/aio/libaio.rs | 3 +- util/src/aio/raw.rs | 1 + util/src/bitmap.rs | 1 + util/src/byte_code.rs | 11 +- util/src/leak_bucket.rs | 1 + util/src/lib.rs | 20 +- util/src/loop_context.rs | 3 +- util/src/num_ops.rs | 3 +- util/src/seccomp.rs | 2 + util/src/syscall.rs | 55 +++ util/src/tap.rs | 2 + util/src/trace.rs | 1 + util/src/unix.rs | 2 + vfio/src/lib.rs | 10 +- vfio/src/vfio_dev.rs | 2 + vfio/src/vfio_pci.rs | 5 +- virtio/src/balloon.rs | 6 +- virtio/src/block.rs | 4 +- virtio/src/console.rs | 4 +- virtio/src/lib.rs | 14 +- virtio/src/net.rs | 3 + virtio/src/queue.rs | 2 + virtio/src/rng.rs | 3 + virtio/src/vhost/kernel/mod.rs | 6 +- virtio/src/vhost/kernel/net.rs | 2 + virtio/src/vhost/kernel/vsock.rs | 3 + virtio/src/vhost/user/client.rs | 2 + virtio/src/vhost/user/net.rs | 1 + virtio/src/vhost/user/sock.rs | 1 + virtio/src/virtio_mmio.rs | 3 + virtio/src/virtio_pci.rs | 19 +- 114 files changed, 2390 insertions(+), 490 deletions(-) create mode 100644 devices/src/interrupt_controller/aarch64/gicv2.rs create mode 100644 machine_manager/src/config/numa.rs create mode 100644 util/src/syscall.rs diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index 3c68a6ac0..b907aa675 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -14,6 +14,7 @@ use std::time::Instant; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; +use log::error; // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 969f49480..308514fa1 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -226,6 +226,94 @@ impl AmlBuilder for AcpiRsdp { } } +/// ACPI SRAT processor affinity structure. +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct AcpiSratProcessorAffinity { + /// Type ID. + pub type_id: u8, + /// The length of this structure. + pub length: u8, + /// Bit [7:0] of the proximity domain to which the processor belongs. + pub proximity_lo: u8, + /// The processor local APIC ID. + pub local_apic_id: u8, + /// The processor affinity flags. + pub flags: u32, + /// The processor local SAPIC EID. + pub local_sapic_eid: u8, + /// Bit [31:8] of the proximity domain to which the processor belongs. + pub proximity_hi: [u8; 3], + /// The clock domain to which the processor belongs. + pub clock_domain: u32, +} + +impl ByteCode for AcpiSratProcessorAffinity {} + +impl AmlBuilder for AcpiSratProcessorAffinity { + fn aml_bytes(&self) -> Vec { + Vec::from(self.as_bytes()) + } +} + +/// ACPI SRAT GICC affinity structure. +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct AcpiSratGiccAffinity { + /// Type ID. + pub type_id: u8, + /// The length of this structure. + pub length: u8, + /// Represents the proximity domain to which the "range of memory" belongs. + pub proximity_domain: u32, + /// The ACPI processor UID of the associated GICC + pub process_uid: u32, + /// The GICC affinity flags. + pub flags: u32, + /// The clock domain to which the processor belongs. + pub clock_domain: u32, +} + +impl ByteCode for AcpiSratGiccAffinity {} + +impl AmlBuilder for AcpiSratGiccAffinity { + fn aml_bytes(&self) -> Vec { + Vec::from(self.as_bytes()) + } +} + +/// ACPI SRAT memory affinity structure. +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct AcpiSratMemoryAffinity { + /// Type ID. + pub type_id: u8, + /// The length of this structure. + pub length: u8, + /// Represents the proximity domain to which the "range of memory" belongs. + pub proximity_domain: u32, + /// Reserved field. + pub reserved1: u16, + /// The base address of the memory range. + pub base_addr: u64, + /// The length of the memory range. + pub range_length: u64, + /// Reserved field. + pub reserved2: u32, + /// Indicates whether memory is enabled and can be hot plugged. + pub flags: u32, + /// Reserved field. + pub reserved3: u64, +} + +impl ByteCode for AcpiSratMemoryAffinity {} + +impl AmlBuilder for AcpiSratMemoryAffinity { + fn aml_bytes(&self) -> Vec { + Vec::from(self.as_bytes()) + } +} + /// This module describes ACPI MADT's sub-tables on x86_64 platform. #[cfg(target_arch = "x86_64")] pub mod madt_subtable { diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index b691ed9fd..82688bb81 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -10,11 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; - mod acpi_device; #[allow(dead_code)] pub mod acpi_table; @@ -37,6 +32,8 @@ pub const ACPI_TABLE_LOADER_FILE: &str = "etc/table-loader"; pub const ACPI_RSDP_FILE: &str = "etc/acpi/rsdp"; pub mod errors { + use error_chain::error_chain; + error_chain! { errors { FileEntryExist(name: String) { diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 6bb2bba55..3703750e0 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex}; +use error_chain::bail; use util::byte_code::ByteCode; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 1a6b0a839..d2ef05690 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -16,13 +16,22 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; use std::thread; -use machine_manager::config::MachineMemConfig; +use error_chain::bail; +use log::{error, info}; +use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; +use util::{ + syscall::mbind, + unix::{do_mmap, host_page_size}, +}; use crate::errors::{Result, ResultExt}; use crate::{AddressRange, GuestAddress}; -use util::unix::{do_mmap, host_page_size}; const MAX_PREALLOC_THREAD: u8 = 16; +/// Verify existing pages in the mapping. +const MPOL_MF_STRICT: u32 = 1; +/// Move pages owned by this process to conform to mapping. +const MPOL_MF_MOVE: u32 = 2; /// FileBackend represents backend-file of `HostMemMapping`. #[derive(Clone)] @@ -278,6 +287,51 @@ pub fn create_host_mmaps( Ok(mappings) } +/// Set host memory backend numa policy. +/// +/// # Arguments +/// +/// * `mem_mappings` - The host virtual address of mapped memory information. +/// * `mem_zones` - Memory zone config. +pub fn set_host_memory_policy( + mem_mappings: &[Arc], + mem_zones: &Option>, +) -> Result<()> { + if mem_zones.is_none() || mem_mappings.is_empty() { + return Ok(()); + } + + let mut host_addr_start = mem_mappings.get(0).map(|m| m.host_address()).unwrap(); + for zone in mem_zones.as_ref().unwrap() { + let mut node_id = if let Some(id) = zone.host_numa_node { + id as usize + } else { + 0_usize + }; + + let mut nmask = vec![0_u64; node_id / 64 + 1]; + nmask[node_id / 64] |= 1_u64 << (node_id % 64); + let policy = HostMemPolicy::from(zone.policy.clone()); + if policy != HostMemPolicy::Default { + // We need to pass node_id + 1 as mbind() max_node argument. + // It is kind of linux bug or feature which will cut off the last node. + node_id += 1; + } + mbind( + host_addr_start, + zone.size, + policy as u32, + nmask, + node_id as u64, + MPOL_MF_STRICT | MPOL_MF_MOVE, + ) + .chain_err(|| "Failed to call mbind")?; + host_addr_start += zone.size; + } + + Ok(()) +} + /// Record information of memory mapping. pub struct HostMemMapping { /// Record the range of one memory segment. @@ -490,6 +544,7 @@ mod test { dump_guest_core: false, mem_share: false, mem_prealloc: false, + mem_zones: None, }; let host_mmaps = create_host_mmaps(&addr_ranges, &mem_config, 1).unwrap(); diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 5b337e215..1077a445e 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -77,13 +77,6 @@ //! } //! ``` -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate migration_derive; - mod address; mod address_space; mod host_mmap; @@ -93,7 +86,7 @@ mod state; pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; -pub use host_mmap::{create_host_mmaps, FileBackend, HostMemMapping}; +pub use host_mmap::{create_host_mmaps, set_host_memory_policy, FileBackend, HostMemMapping}; #[cfg(target_arch = "x86_64")] pub use listener::KvmIoListener; pub use listener::KvmMemoryListener; @@ -101,6 +94,8 @@ pub use listener::{Listener, ListenerReqType}; pub use region::{FlatRange, Region, RegionIoEventFd, RegionType}; pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index aa07b215a..b9865adec 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -13,14 +13,15 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::kvm_userspace_memory_region; use kvm_ioctls::{IoEventAddress, NoDatamatch}; +use log::{debug, warn}; +use util::{num_ops::round_down, unix::host_page_size}; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, FlatRange, RegionIoEventFd, RegionType}; -use util::{num_ops::round_down, unix::host_page_size}; const MEM_READ_ONLY: u32 = 1 << 1; diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 7c9e8da91..f90a88dc2 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -13,6 +13,9 @@ use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; +use error_chain::bail; +use log::{debug, warn}; + use crate::address_space::FlatView; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, AddressSpace, FileBackend, GuestAddress, HostMemMapping, RegionOps}; diff --git a/address_space/src/state.rs b/address_space/src/state.rs index eb34e91ce..901dc4457 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -16,8 +16,10 @@ use std::mem::size_of; use std::sync::Arc; use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; + use migration::errors::{ErrorKind, Result, ResultExt}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::unix::host_page_size; diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index c9608cb26..1b5b3c4b0 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -17,6 +17,7 @@ use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{errors::ErrorKind as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; +use log::info; use util::byte_code::ByteCode; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index 9a3426442..66ed338bd 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -73,11 +73,6 @@ //! } //! ``` -#[macro_use] -extern crate log; -#[macro_use] -extern crate error_chain; - #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] mod aarch64; @@ -99,6 +94,8 @@ pub use x86_64::X86BootLoader as BootLoader; pub use x86_64::X86BootLoaderConfig as BootLoaderConfig; pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { Io(std::io::Error); diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index da0c12383..1435f7235 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -18,8 +18,12 @@ use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; +use error_chain::bail; +use log::info; use util::byte_code::ByteCode; +use self::gdt::setup_gdt; +use self::mptable::setup_isa_mptable; use super::bootparam::{BootParams, RealModeKernelHeader, UNDEFINED_ID}; use super::{X86BootLoader, X86BootLoaderConfig}; use super::{ @@ -27,8 +31,6 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::errors::{ErrorKind, Result, ResultExt}; -use gdt::setup_gdt; -use mptable::setup_isa_mptable; /// Load bzImage linux kernel to Guest Memory. /// diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs index 0c22ced55..4cbe22c4f 100644 --- a/boot_loader/src/x86_64/mod.rs +++ b/boot_loader/src/x86_64/mod.rs @@ -62,6 +62,7 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use devices::legacy::FwCfgOps; +use error_chain::bail; use kvm_bindings::kvm_segment; use crate::errors::Result; diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index a76e78920..17b0406e0 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{FwCfgEntryType, FwCfgOps}; +use error_chain::bail; use util::byte_code::ByteCode; use util::num_ops::round_up; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 49e8a0302..705da226a 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -19,6 +19,8 @@ use std::sync::Arc; use address_space::AddressSpace; use devices::legacy::{FwCfgEntryType, FwCfgOps}; +use error_chain::bail; +use log::{error, info}; use util::byte_code::ByteCode; use self::elf::load_elf_kernel; diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index bc46f69a9..4f0726a32 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -20,6 +20,7 @@ use kvm_bindings::{ }; use kvm_ioctls::VcpuFd; use util::byte_code::ByteCode; +use util::offset_of; use vmm_sys_util::{ errno, ioctl::{ioctl_with_mut_ref, ioctl_with_ref}, @@ -248,13 +249,13 @@ pub fn set_core_regs(vcpu_fd: &VcpuFd, core_regs: kvm_regs) -> Result<()> { set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpsr.into(), - &core_regs.fp_regs.fpsr.as_bytes().to_vec(), + core_regs.fp_regs.fpsr.as_bytes(), )?; set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpcr.into(), - &core_regs.fp_regs.fpcr.as_bytes().to_vec(), + core_regs.fp_regs.fpcr.as_bytes(), )?; Ok(()) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 77e195df4..a85ca8231 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -23,12 +23,14 @@ use kvm_bindings::{ use kvm_ioctls::VcpuFd; use vmm_sys_util::fam::FamStructWrapper; +pub use self::caps::ArmCPUCaps; +use self::caps::CpregListEntry; +use self::core_regs::{get_core_regs, set_core_regs}; use crate::errors::{Result, ResultExt}; use crate::CPU; -pub use caps::ArmCPUCaps; -use caps::CpregListEntry; -use core_regs::{get_core_regs, set_core_regs}; + use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; type CpregList = FamStructWrapper; diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index b8f849290..be310f7ec 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -27,17 +27,6 @@ //! - `x86_64` //! - `aarch64` -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate machine_manager; -#[cfg(target_arch = "aarch64")] -#[macro_use] -extern crate util; -#[macro_use] -extern crate migration_derive; #[cfg(target_arch = "aarch64")] #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] @@ -46,6 +35,8 @@ mod aarch64; mod x86_64; pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { Signal(vmm_sys_util::errno::Error); @@ -124,6 +115,8 @@ use std::time::Duration; use kvm_ioctls::{VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; +use log::{error, info, warn}; +use machine_manager::event; use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; use vmm_sys_util::signal::{register_signal_handler, Killable}; diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 1122715bf..173e566e9 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -15,18 +15,20 @@ mod cpuid; use std::sync::{Arc, Mutex}; +use error_chain::bail; use kvm_bindings::{ kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, Msrs, KVM_MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; use self::cpuid::host_cpuid; use crate::errors::{Result, ResultExt}; use crate::CPU; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use util::byte_code::ByteCode; const ECX_EPB_SHIFT: u32 = 3; const X86_FEATURE_HYPERVISOR: u32 = 31; diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs new file mode 100644 index 000000000..c15eb48b8 --- /dev/null +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -0,0 +1,325 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::marker::{Send, Sync}; +use std::sync::{Arc, Mutex}; + +use address_space::AddressSpace; +use hypervisor::kvm::KVM_FDS; +use kvm_ioctls::DeviceFd; +use log::error; +use machine_manager::machine::{KvmVmState, MachineLifecycle}; +use util::device_tree::{self, FdtBuilder}; + +use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; +use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; + +// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. +const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; +const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; + +/// Configure a v2 Interrupt controller. +pub struct GICv2Config { + /// GIC distributor address range. + pub dist_range: (u64, u64), + /// GIC cpu interface address range. + pub cpu_range: (u64, u64), + /// GIC v2m range . + pub v2m_range: Option<(u64, u64)>, + /// GIC system memory. + pub sys_mem: Option>, +} + +/// Access wrapper for GICv2. +pub trait GICv2Access { + /// Returns `gicr_attr` of `vCPU`. + fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64; + + fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()>; + + fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()>; +} + +struct GicCpuInterfaceRegion { + /// Base address. + base: u64, + /// Size of Cpu Interface region. + size: u64, +} + +struct GicDistGuestRegion { + /// Base address. + base: u64, + /// Size of Cpu Interface region. + size: u64, +} + +/// A wrapper around creating and managing a `GICv2`. +pub struct GICv2 { + /// The fd for the GICv2 device. + fd: DeviceFd, + /// Maximum irq number. + nr_irqs: u32, + /// GICv2 cpu interface region. + cpu_interface_region: GicCpuInterfaceRegion, + /// Guest physical address space of the GICv2 distributor register mappings. + dist_guest_region: GicDistGuestRegion, + /// Lifecycle state for GICv2. + state: Arc>, +} + +impl GICv2 { + fn new(config: &GICConfig) -> Result { + let v2config = match config.v2.as_ref() { + Some(v2) => v2, + None => return Err(ErrorKind::InvalidConfig("no v2 config found".to_string()).into()), + }; + + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, + fd: 0, + flags: 0, + }; + + let gic_fd = KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_device(&mut gic_device) + .chain_err(|| "Failed to create GICv2 device")?; + + let cpu_interface_region = GicCpuInterfaceRegion { + base: v2config.dist_range.0 + KVM_VGIC_V2_DIST_SIZE, + size: KVM_VGIC_V2_CPU_SIZE, + }; + let dist_guest_region = GicDistGuestRegion { + base: v2config.dist_range.0, + size: v2config.dist_range.1, + }; + + let gicv2 = GICv2 { + fd: gic_fd, + nr_irqs: config.max_irq, + cpu_interface_region, + dist_guest_region, + state: Arc::new(Mutex::new(KvmVmState::Created)), + }; + + Ok(gicv2) + } + + fn realize(&self) -> Result<()> { + KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; + + // Init the interrupt number support by the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, + &self.nr_irqs as *const u32 as u64, + true, + ) + .chain_err(|| "Failed to set GICv2 attribute: irqs")?; + + // Finalize the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + 0, + true, + ) + .chain_err(|| "KVM failed to initialize GICv2")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST), + &self.dist_guest_region.base as *const u64 as u64, + true, + ) + .chain_err(|| "Failed to set GICv2 attribute: distributor address")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU), + &self.cpu_interface_region.base as *const u64 as u64, + true, + ) + .chain_err(|| "Failed to set GICv2 attribute: cpu address")?; + + *self.state.lock().unwrap() = KvmVmState::Running; + + Ok(()) + } + + fn device_fd(&self) -> &DeviceFd { + &self.fd + } +} + +impl MachineLifecycle for GICv2 { + fn pause(&self) -> bool { + // VM change state will flush REDIST pending tables into guest RAM. + if KvmDevice::kvm_device_access( + self.device_fd(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, + 0, + true, + ) + .is_ok() + { + *self.state.lock().unwrap() = KvmVmState::Running; + true + } else { + false + } + } + + fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + let state = self.state.lock().unwrap(); + if *state != old { + error!("GICv2 lifecycle error: state check failed."); + return false; + } + drop(state); + + match (old, new) { + (KvmVmState::Running, KvmVmState::Paused) => self.pause(), + _ => true, + } + } +} + +impl GICv2Access for GICv2 { + fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { + (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK as u64) + | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) + } + + fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + offset, + gicd_value as *mut u32 as u64, + write, + ) + .chain_err(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) + } + + fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + self.vcpu_gicr_attr(offset, cpu), + gicc_value as *mut u64 as u64, + write, + ) + .chain_err(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) + } +} + +impl GICDevice for GICv2 { + fn create_device(gic_conf: &GICConfig) -> Result> { + let gicv2 = Arc::new(GICv2::new(gic_conf)?); + + Ok(gicv2) + } + + fn realize(&self) -> Result<()> { + self.realize().chain_err(|| "Failed to realize GICv2")?; + + Ok(()) + } + + fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()> { + let gic_reg = vec![ + self.dist_guest_region.base, + self.dist_guest_region.size, + self.cpu_interface_region.base, + self.cpu_interface_region.size, + ]; + + let intc_node_dep = fdt.begin_node("intc")?; + fdt.set_property_string("compatible", "arm,cortex-a15-gic")?; + fdt.set_property("interrupt-controller", &Vec::new())?; + fdt.set_property_u32("#interrupt-cells", 0x3)?; + fdt.set_property_u32("phandle", device_tree::GIC_PHANDLE)?; + fdt.set_property_u32("#address-cells", 0x2)?; + fdt.set_property_u32("#size-cells", 0x2)?; + fdt.set_property_array_u64("reg", &gic_reg)?; + + let gic_intr = [ + device_tree::GIC_FDT_IRQ_TYPE_PPI, + 0x9, + device_tree::IRQ_TYPE_LEVEL_HIGH, + ]; + + fdt.set_property_array_u32("interrupts", &gic_intr)?; + + fdt.end_node(intc_node_dep)?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use hypervisor::kvm::KVMFds; + use serial_test::serial; + + use super::super::GICVersion; + use super::super::GICv2Config; + use super::*; + + #[test] + #[serial] + fn test_create_gicv2() { + let kvm_fds = KVMFds::new(); + if kvm_fds.vm_fd.is_none() { + return; + } + KVM_FDS.store(Arc::new(kvm_fds)); + + let gic_conf = GICConfig { + version: Some(GICVersion::GICv2), + vcpu_count: 4, + max_irq: 192, + v2: Some(GICv2Config { + dist_range: (0x0800_0000, 0x0001_0000), + cpu_range: (0x080A_0000, 0x00F6_0000), + v2m_range: None, + sys_mem: None, + }), + v3: None, + }; + assert!(GICv2::new(&gic_conf).is_ok()); + } +} diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 79ebb27b3..7cc73e338 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -12,14 +12,15 @@ use std::sync::{Arc, Mutex}; -use kvm_ioctls::DeviceFd; - use super::{ state::{GICv3ItsState, GICv3State}, - GICConfig, GICDevice, UtilResult, + GICConfig, GICDevice, KvmDevice, UtilResult, }; use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; + use hypervisor::kvm::KVM_FDS; +use kvm_ioctls::DeviceFd; +use log::error; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use migration::{MigrationManager, MigrationRestoreOrder}; use util::device_tree::{self, FdtBuilder}; @@ -28,47 +29,16 @@ use util::device_tree::{self, FdtBuilder}; const SZ_64K: u64 = 0x0001_0000; const KVM_VGIC_V3_REDIST_SIZE: u64 = 2 * SZ_64K; -/// A wrapper for kvm_based device check and access. -pub struct KvmDevice; - -impl KvmDevice { - fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr: 0, - flags: 0, - }; - fd.has_device_attr(&attr) - .chain_err(|| "Failed to check device attributes for GIC.")?; - Ok(()) - } - - fn kvm_device_access( - fd: &DeviceFd, - group: u32, - attr: u64, - addr: u64, - write: bool, - ) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr, - flags: 0, - }; - - if write { - fd.set_device_attr(&attr) - .chain_err(|| "Failed to set device attributes for GIC.")?; - } else { - let mut attr = attr; - fd.get_device_attr(&mut attr) - .chain_err(|| "Failed to get device attributes for GIC.")?; - }; - - Ok(()) - } +/// Configure a v3 Interrupt controller. +pub struct GICv3Config { + /// Config msi support + pub msi: bool, + /// GIC distributor address range. + pub dist_range: (u64, u64), + /// GIC redistributor address range, support multiple redistributor regions. + pub redist_region_ranges: Vec<(u64, u64)>, + /// GIC ITS address ranges. + pub its_range: Option<(u64, u64)>, } /// Access wrapper for GICv3. @@ -129,8 +99,10 @@ pub struct GICv3 { impl GICv3 { fn new(config: &GICConfig) -> Result { - config.check_sanity()?; - + let v3config = match config.v3.as_ref() { + Some(v3) => v3, + None => return Err(ErrorKind::InvalidConfig("no v3 config found".to_string()).into()), + }; let mut gic_device = kvm_bindings::kvm_create_device { type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, fd: 0, @@ -149,8 +121,8 @@ impl GICv3 { }; // Calculate GIC redistributor regions' address range according to vcpu count. - let base = config.redist_region_ranges[0].0; - let size = config.redist_region_ranges[0].1; + let base = v3config.redist_region_ranges[0].0; + let size = v3config.redist_region_ranges[0].1; let redist_capability = size / KVM_VGIC_V3_REDIST_SIZE; let redist_region_count = std::cmp::min(config.vcpu_count, redist_capability); let mut redist_regions = vec![GicRedistRegion { @@ -160,7 +132,7 @@ impl GICv3 { }]; if config.vcpu_count > redist_capability { - let high_redist_base = config.redist_region_ranges[1].0; + let high_redist_base = v3config.redist_region_ranges[1].0; let high_redist_region_count = config.vcpu_count - redist_capability; let high_redist_attr = (high_redist_region_count << 52) | high_redist_base | 0x1; @@ -177,12 +149,12 @@ impl GICv3 { nr_irqs: config.max_irq, its_dev: None, redist_regions, - dist_base: config.dist_range.0, - dist_size: config.dist_range.1, + dist_base: v3config.dist_range.0, + dist_size: v3config.dist_range.1, state: Arc::new(Mutex::new(KvmVmState::Created)), }; - if let Some(its_range) = config.its_range { + if let Some(its_range) = v3config.its_range { gicv3.its_dev = Some(Arc::new( GICv3Its::new(&its_range).chain_err(|| "Failed to create ITS")?, )); @@ -558,7 +530,8 @@ mod tests { use hypervisor::kvm::KVMFds; use serial_test::serial; - use super::super::GICConfig; + use super::super::GICVersion; + use super::super::GICv3Config; use super::*; #[test] @@ -571,13 +544,16 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_conf = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), + version: Some(GICVersion::GICv3), vcpu_count: 4, max_irq: 192, - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, + v2: None, + v3: Some(GICv3Config { + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, + }), }; assert!(GICv3::new(&gic_conf).is_ok()); } @@ -592,13 +568,16 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_config = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + version: Some(GICVersion::GICv3), vcpu_count: 4_u64, max_irq: 192_u32, - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, + v2: None, + v3: Some(GICv3Config { + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, + }), }; let gic = GICv3::new(&gic_config).unwrap(); assert!(gic.its_dev.is_none()); @@ -615,13 +594,16 @@ mod tests { KVM_FDS.store(Arc::new(kvm_fds)); let gic_config = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + version: Some(GICVersion::GICv3), vcpu_count: 210_u64, max_irq: 192_u32, - msi: true, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], - its_range: Some((0x0808_0000, 0x0002_0000)), + v3: Some(GICv3Config { + msi: true, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], + its_range: Some((0x0808_0000, 0x0002_0000)), + }), + v2: None, }; let gic = GICv3::new(&gic_config).unwrap(); diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index c4f5aa0ba..d45bd063b 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -10,11 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod gicv2; mod gicv3; #[allow(dead_code)] mod state; -pub use gicv3::GICv3; +use kvm_ioctls::DeviceFd; + +pub use gicv2::{GICv2, GICv2Config}; +pub use gicv3::{GICv3, GICv3Config}; use std::sync::Arc; @@ -29,42 +33,75 @@ use super::errors::{ErrorKind, Result, ResultExt}; // First 32 are private to each CPU (SGIs and PPIs). pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; -/// Configure a Interrupt controller. +/// GIC version type. +pub enum GICVersion { + GICv2, + GICv3, +} + +/// A wrapper for kvm_based device check and access. +pub struct KvmDevice; + +impl KvmDevice { + fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr: 0, + flags: 0, + }; + fd.has_device_attr(&attr) + .chain_err(|| "Failed to check device attributes for GIC.")?; + Ok(()) + } + + fn kvm_device_access( + fd: &DeviceFd, + group: u32, + attr: u64, + addr: u64, + write: bool, + ) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr, + flags: 0, + }; + + if write { + fd.set_device_attr(&attr) + .chain_err(|| "Failed to set device attributes for GIC.")?; + } else { + let mut attr = attr; + fd.get_device_attr(&mut attr) + .chain_err(|| "Failed to get device attributes for GIC.")?; + }; + + Ok(()) + } +} + pub struct GICConfig { /// Config GIC version - pub version: u32, + pub version: Option, /// Config number of CPUs handled by the device pub vcpu_count: u64, /// Config maximum number of irqs handled by the device pub max_irq: u32, - /// Config msi support - pub msi: bool, - /// GIC distributor address range. - pub dist_range: (u64, u64), - /// GIC redistributor address range, support multiple redistributor regions. - pub redist_region_ranges: Vec<(u64, u64)>, - /// GIC ITS address ranges. - pub its_range: Option<(u64, u64)>, + /// v2 config. + pub v2: Option, + /// v3 config. + pub v3: Option, } impl GICConfig { fn check_sanity(&self) -> Result<()> { - if self.version != kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 { - return Err(ErrorKind::InvalidConfig("GIC only support GICv3".to_string()).into()); - }; - - if self.vcpu_count > 256 || self.vcpu_count == 0 { - return Err( - ErrorKind::InvalidConfig("GIC only support maximum 256 vcpus".to_string()).into(), - ); - } - if self.max_irq <= GIC_IRQ_INTERNAL { return Err( ErrorKind::InvalidConfig("GIC irq numbers need above 32".to_string()).into(), ); } - Ok(()) } } @@ -106,9 +143,17 @@ impl InterruptController { /// /// * `gic_conf` - Configuration for `GIC`. pub fn new(gic_conf: &GICConfig) -> Result { - Ok(InterruptController { - gic: GICv3::create_device(gic_conf).chain_err(|| "Failed to realize GIC")?, - }) + gic_conf.check_sanity()?; + let gic = match &gic_conf.version { + Some(GICVersion::GICv3) => GICv3::create_device(gic_conf), + Some(GICVersion::GICv2) => GICv2::create_device(gic_conf), + // Try v3 first if no version specified. + None => GICv3::create_device(gic_conf).or_else(|_| GICv2::create_device(gic_conf)), + }; + let intc = InterruptController { + gic: gic.chain_err(|| "Failed to realize GIC")?, + }; + Ok(intc) } pub fn realize(&self) -> Result<()> { @@ -137,30 +182,14 @@ mod tests { #[test] fn test_gic_config() { let mut gic_conf = GICConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), + version: Some(GICVersion::GICv3), vcpu_count: 4, max_irq: 192, - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, + v2: None, + v3: None, }; assert!(gic_conf.check_sanity().is_ok()); - gic_conf.version = 3; - assert!(gic_conf.check_sanity().is_err()); - gic_conf.version = kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(); - assert!(gic_conf.check_sanity().is_ok()); - - gic_conf.vcpu_count = 257; - assert!(gic_conf.check_sanity().is_err()); - gic_conf.vcpu_count = 0; - assert!(gic_conf.check_sanity().is_err()); - gic_conf.vcpu_count = 24; - assert!(gic_conf.check_sanity().is_ok()); - - assert!(gic_conf.check_sanity().is_ok()); - gic_conf.max_irq = 32; assert!(gic_conf.check_sanity().is_err()); } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index d4da00bd6..2f54c1b6f 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -12,12 +12,13 @@ use std::mem::size_of; -use libc::c_uint; - use super::gicv3::{GICv3, GICv3Access, GICv3Its}; use super::GIC_IRQ_INTERNAL; use crate::interrupt_controller::errors::Result; + +use libc::c_uint; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; /// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index e17d5c479..dbaa81b25 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -29,11 +29,17 @@ mod aarch64; #[cfg(target_arch = "aarch64")] -pub use aarch64::GICConfig as InterruptControllerConfig; +pub use aarch64::GICConfig as ICGICConfig; +#[cfg(target_arch = "aarch64")] +pub use aarch64::GICv2Config as ICGICv2Config; +#[cfg(target_arch = "aarch64")] +pub use aarch64::GICv3Config as ICGICv3Config; #[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; pub mod errors { + use error_chain::error_chain; + error_chain! { errors { #[cfg(target_arch = "aarch64")] diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 1f7b4a159..f8a8e685b 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -17,7 +17,9 @@ use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use error_chain::bail; use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; +use log::{error, info}; use machine_manager::machine::{PathInfo, PTY_PATH}; use machine_manager::{ config::{ChardevConfig, ChardevType}, diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index df613e43d..a77f3047f 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -23,7 +23,8 @@ use address_space::{AddressSpace, GuestAddress}; #[cfg(target_arch = "x86_64")] use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; @@ -505,6 +506,42 @@ impl FwCfgCommon { Ok(()) } + fn modify_file_callback( + &mut self, + filename: &str, + data: Vec, + select_cb: Option, + write_cb: Option, + allow_write: bool, + ) -> Result<()> { + if self.files.len() >= self.file_slots as usize { + return Err(ErrorKind::FileSlotsNotAvailable(filename.to_owned()).into()); + } + + let file_name_bytes = filename.to_string().as_bytes().to_vec(); + // Make sure file entry is existed. + let index = self + .files + .iter() + .position(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) + .ok_or_else(|| ErrorKind::EntryNotFound(filename.to_owned()))?; + self.files[index].size = data.len() as u32; + + // Update FileDir entry + let mut bytes = Vec::new(); + let file_length_be = BigEndian::read_u32((self.files.len() as u32).as_bytes()); + bytes.append(&mut file_length_be.as_bytes().to_vec()); + for value in self.files.iter() { + bytes.append(&mut value.as_be_bytes()); + } + self.update_entry_data(FwCfgEntryType::FileDir as u16, bytes)?; + + // Update File entry + self.entries[FW_CFG_FILE_FIRST as usize + index] = + FwCfgEntry::new(data, select_cb, write_cb, allow_write); + Ok(()) + } + // Fetch FwCfgDma request from guest and handle it fn handle_dma_request(&mut self) -> Result<()> { let dma_addr = self.dma_addr; @@ -1194,6 +1231,17 @@ pub trait FwCfgOps { self.fw_cfg_common() .add_file_callback(filename, data, None, None, true) } + + /// Modify a file entry to FwCfg device, without callbacks, write-allow. + /// + /// # Arguments + /// + /// * `filename` - Name of the file + /// * `data` - Raw data bytes of the file to be modified + fn modify_file_entry(&mut self, filename: &str, data: Vec) -> Result<()> { + self.fw_cfg_common() + .modify_file_callback(filename, data, None, None, true) + } } #[cfg(target_arch = "aarch64")] @@ -1376,6 +1424,38 @@ mod test { let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); + let file_data = Vec::new(); + assert_eq!( + fwcfg_common + .add_file_callback("bootorder", file_data.clone(), None, None, false) + .is_ok(), + true + ); + let id = fwcfg_common.files[0].select; + fwcfg_common.select_entry(id); + let entry = fwcfg_common.get_entry_mut().unwrap(); + assert_eq!(entry.data, file_data); + + let modify_file_data = "/pci@ffffffffffffffff/pci-bridge@3/scsi@0,1/disk@0,0" + .as_bytes() + .to_vec(); + assert_eq!( + fwcfg_common + .modify_file_callback("bootorder", modify_file_data.clone(), None, None, true) + .is_ok(), + true + ); + let id = fwcfg_common.files[0].select; + fwcfg_common.select_entry(id); + let entry = fwcfg_common.get_entry_mut().unwrap(); + assert_eq!(entry.data, modify_file_data); + + assert_eq!( + fwcfg_common + .modify_file_callback("abc", modify_file_data.clone(), None, None, true) + .is_err(), + true + ); let file_data = vec![0x33; 500]; assert_eq!( fwcfg_common diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 6835a9214..bd4d9474e 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -26,6 +26,8 @@ //! - `aarch64` pub mod errors { + use error_chain::error_chain; + error_chain! { links { SysBus(sysbus::errors::Error, sysbus::errors::ErrorKind); diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 36f708dba..6b12749c4 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -17,7 +17,8 @@ use std::sync::{Arc, Mutex}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{debug, error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::num_ops::{deposit_u32, extract_u32}; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 34a5114ef..84c31bf31 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -19,11 +19,13 @@ use acpi::{ }; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; +use log::{debug, error}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index e7f671506..897c71919 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -16,7 +16,9 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use acpi::AmlBuilder; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; +use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index a549bfb03..c87d7036c 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -17,6 +17,7 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; +use log::{debug, error, warn}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 11e3c1b2e..cc2abd679 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -19,11 +19,14 @@ use acpi::{ AmlResourceUsage, AmlScopeBuilder, }; use address_space::GuestAddress; +use error_chain::bail; use hypervisor::kvm::KVM_FDS; +use log::error; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 09423adbd..849ac5394 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -16,18 +16,11 @@ //! - interrupt controller (aarch64) //! - legacy devices, such as serial devices -#[macro_use] -extern crate log; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate migration_derive; - mod interrupt_controller; pub mod legacy; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ - errors as IntCtrlErrs, InterruptController, InterruptControllerConfig, + errors as IntCtrlErrs, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, }; pub use legacy::errors as LegacyErrs; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 9e79d88b5..999e43e61 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -7,7 +7,7 @@ StratoVirt can only be launched via cmdline arguments. ### 1.1 Machine Config General configuration of machine, including -* type: The type of machine, three types of machine are available: "none", "microvm", +* type: The type of machine, three types of machine are available: "none", "microvm", "q35"(x86_64 platform) and "virt" (aarch64 platform). * dump-guest-core: Including guest memory in coredump file or not, default value is true. * mem-share: Guest memory is sharable with other processes or not. @@ -66,7 +66,7 @@ Default VM memory size is 256M. The supported VM memory size is among [256M, 512 ``` #### 1.3.2 Memory Prealloc -Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure +Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure memory prealloc. ```shell @@ -105,7 +105,41 @@ $ cat /proc/meminfo ... -mem-path /path/to/hugepages ... ``` -### 1.5 Kernel and Kernel Parameters +### 1.5 NUMA node +The optional NUMA node element gives the opportunity to create a virtual machine with non-uniform memory accesses. +The application of NUMA node is that one region of memory can be set as fast memory, another can be set as slow memory. + +Each NUMA node is given a list of command lines option, there will be described in detail below. +1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] + It describes the size and id of each memory zone, the policy of binding to host memory node. + you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. + The optional policies are default, preferred, bind and interleave. +2. -numa node,cpus=0-1,memdev=mem0 + It describes id and cpu set of the NUMA node, and the id belongs to which memory zone. +3. -numa dist,src=0,dst=0,val=10 + It describes the distance between source and destination. The default of source to source is 10, + source to destination is 20. And if you choose not to set these parameters, the VM will set the default values. + +The following command shows how to set NUMA node: + +```shell +# The number of cpu must be set to be the same as numa node cpu. +-smp 4 + +# The memory size must be set to be the same as numa node mem. +-m 4G + +-object memory-backend-ram,size=2G,id=mem0,[host-nodes=0,policy=bind] +-object memory-backend-ram,size=2G,id=mem1,[host-nodes=1,policy=bind] +-numa node,nodeid=0,cpus=0-1,memdev=mem0 +-numa node,nodeid=1,cpus=2-3,memdev=mem1 +[-numa dist,src=0,dst=0,val=10] +[-numa dist,src=0,dst=1,val=20] +[-numa dist,src=1,dst=0,val=20] +[-numa dist,src=1,dst=1,val=10] +``` + +### 1.6 Kernel and Kernel Parameters StratoVirt supports to launch PE or bzImage (only x86_64) format linux kernel 4.19 and can also set kernel parameters for VM. @@ -120,7 +154,7 @@ And the given kernel parameters will be actually analyzed by boot loader. -append "console=ttyS0 rebook=k panic=1 pci=off tsc=reliable ipv6.disable=1" ``` -### 1.6 Initrd Configuration +### 1.7 Initrd Configuration StratoVirt supports to launch VM by a initrd (boot loader initialized RAM disk) as well. @@ -133,7 +167,7 @@ If you want to use initrd as rootfs, `root=/dev/ram` and `rdinit=/bin/sh` must b -initrd /path/to/initrd ``` -### 1.7 Global config +### 1.8 Global config Users can set the global configuration using the -global parameter. @@ -145,7 +179,7 @@ One property can be set: -global pcie-root-port.fast-unplug=1 ``` -### 1.8 Logging +### 1.9 Logging StratoVirt supports to output log to stderr and log file. @@ -161,7 +195,7 @@ You can enable StratoVirt's logging by: StratoVirt's log-level depends on env `STRATOVIRT_LOG_LEVEL`. StratoVirt supports five log-levels: `trace`, `debug`, `info`, `warn`, `error`. The default level is `error`. -### 1.9 Daemonize +### 1.10 Daemonize StratoVirt supports to run as a daemon. @@ -220,6 +254,9 @@ Nine properties are supported for virtio block device. If not set, default is raw. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, the default block queue number is 1. +* bootindex: the boot order of block device. (optional) If not set, the priority is lowest. +The number ranges from 1 to 255, the smaller the number, the higher the priority. +It determines the order of bootable devices which firmware will use for booting the guest OS. For virtio-blk-pci, two more properties are required. * bus: name of bus which to attach. @@ -235,7 +272,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo -device virtio-blk-device,drive=drive_id,id=blkid[,iothread=iothread1,serial=serial_num] # virtio pci block device. -drive id=drive_id,file=path_on_host[,readonly=off,direct=off,throttling.iops-total=200] --device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N] +-device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N,bootindex=1] ``` ### 2.3 Virtio-net @@ -246,10 +283,10 @@ Six properties are supported for netdev. * tap/vhost-user: the type of net device. NB: currently only tap and vhost-user is supported. * id: unique netdev id. * ifname: name of tap device in host. -* fd: the file descriptor of opened tap device. +* fd: the file descriptor of opened tap device. * fds: file descriptors of opened tap device. * queues: the optional queues attribute controls the number of queues to be used for either multiple queue virtio-net or vhost-net device. -NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, +NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, the tap device would be created according to `ifname`. Eight properties are supported for virtio-net-device or virtio-net-pci. @@ -266,7 +303,7 @@ It has no effect when vhost is set. Two more properties are supported for virtio pci net device. * bus: name of bus which to attach. * addr: including slot number and function number. The first number represents slot number -of device and the second one represents function number of it. For virtio pci net device, it +of device and the second one represents function number of it. For virtio pci net device, it is a single function device, the function number should be set to zero. ```shell @@ -278,10 +315,10 @@ is a single function device, the function number should be set to zero. -device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC,mq=on] ``` -StratoVirt also supports vhost-net to get a higher performance in network. It can be set by +StratoVirt also supports vhost-net to get a higher performance in network. It can be set by giving `vhost` property, and one more property is supported for vhost-net device. -* vhostfd: fd for vhost-net device, it could be configured when `vhost=on`. If this argument is not +* vhostfd: fd for vhost-net device, it could be configured when `vhost=on`. If this argument is not given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatically. ```shell @@ -517,7 +554,7 @@ Four parameters are supported for pcie root port. * addr: including slot number and function number. * id: the name of secondary bus. * chassis: the number of chassis. Interconnect with libvirt only.(optional). -* multifunction: whether to open multi function for pcie root port.(optional). +* multifunction: whether to open multi function for pcie root port.(optional). If not set, default value is false. ```shell @@ -640,9 +677,9 @@ $ ./stratovirt \ * incoming: the path of the template. See [Snapshot and Restore](./snapshot.md) for details. - + ## 6. Ozone -Ozone is a lightweight secure sandbox for StratoVirt, it provides secure environment for StratoVirt +Ozone is a lightweight secure sandbox for StratoVirt, it provides secure environment for StratoVirt by limiting resources of StratoVirt using 'namespace'. Please run ozone with root permission. ### 6.1 Usage @@ -679,7 +716,7 @@ About the arguments: ### 6.2 Example As ozone uses a directory to mount as a root directory, after ozone is launched, the directory "/srv/zozne/{exec_file}/{name}" will be created. (Where, `exec_file` is the executable binary file, usually it is `stratovirt`, while `name` is the name of ozone, it is given by users, but the length of it should be no more than 255 bytes.) In order to run ozone normally, please make sure that the directory "/srv/zozne/{exec_file}/{name}" does not exists before launching ozone. -On top of that, the path-related arguments are different. They are all in the current(`./`) directory. +On top of that, the path-related arguments are different. They are all in the current(`./`) directory. For net name space, it can be created by the following command with name "mynet": ```shell diff --git a/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 b/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 index c2159dd40..85b78de4b 100644 --- a/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 +++ b/docs/kernel_config/standard_vm/kernel_config_4.19_aarch64 @@ -1409,15 +1409,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set CONFIG_NET_VENDOR_NETSWIFT=y diff --git a/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 b/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 index b817bc9b0..6ed9be72f 100644 --- a/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 +++ b/docs/kernel_config/standard_vm/kernel_config_4.19_x86_64 @@ -1368,15 +1368,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set CONFIG_NET_VENDOR_NETSWIFT=y diff --git a/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 b/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 index 3d89d8a96..4ec28e45d 100644 --- a/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 +++ b/docs/kernel_config/standard_vm/kernel_config_5.10_aarch64 @@ -1463,15 +1463,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set # CONFIG_IGC is not set diff --git a/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 b/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 index bcc32e783..d52a12d6a 100644 --- a/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 +++ b/docs/kernel_config/standard_vm/kernel_config_5.10_x86_64 @@ -1399,15 +1399,15 @@ CONFIG_NET_VENDOR_INTEL=y # CONFIG_E100 is not set # CONFIG_E1000 is not set # CONFIG_E1000E is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set +CONFIG_IGB=y +CONFIG_IGBVF=y # CONFIG_IXGB is not set CONFIG_IXGBE=y # CONFIG_IXGBE_DCB is not set -# CONFIG_IXGBEVF is not set +CONFIG_IXGBEVF=y CONFIG_I40E=y # CONFIG_I40E_DCB is not set -# CONFIG_I40EVF is not set +CONFIG_I40EVF=y # CONFIG_ICE is not set # CONFIG_FM10K is not set # CONFIG_IGC is not set diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 8c27cc0a9..33596a1db 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -17,6 +17,7 @@ use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; use util::bitmap::Bitmap; use vmm_sys_util::ioctl::ioctl_with_val; +use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; use crate::errors::{Result, ResultExt}; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 80012bbfd..d88e9f88c 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -22,9 +22,12 @@ use arc_swap::ArcSwap; use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; +use log::error; use once_cell::sync::Lazy; - use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{ + ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, +}; use crate::errors::{Result, ResultExt}; #[cfg(target_arch = "x86_64")] diff --git a/hypervisor/src/kvm/state.rs b/hypervisor/src/kvm/state.rs index f38ad064b..ec61cb23c 100644 --- a/hypervisor/src/kvm/state.rs +++ b/hypervisor/src/kvm/state.rs @@ -13,6 +13,7 @@ use kvm_bindings::{ kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, }; +use migration_derive::{ByteCode, Desc}; use super::KVM_FDS; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 9fb446d61..b9acc6904 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -12,18 +12,10 @@ //! This crate offers interfaces for different kinds of hypervisors, such as KVM. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate vmm_sys_util; -#[cfg(target_arch = "x86_64")] -#[macro_use] -extern crate migration_derive; - #[allow(clippy::upper_case_acronyms)] pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 07c7b7354..69c25b0c9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,17 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate machine_manager; -#[cfg(target_arch = "x86_64")] -#[macro_use] -extern crate vmm_sys_util; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -106,29 +98,35 @@ mod standard_vm; pub use micro_vm::LightMachine; use pci::{PciBus, PciDevOps, PciHost, RootPort}; pub use standard_vm::StdMachine; +use sysbus::{SysBus, SysBusDevOps}; use virtio::{ BlockState, RngState, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioState, VirtioNetState, }; +use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; -use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; +use address_space::{ + create_host_mmaps, set_host_memory_policy, AddressSpace, KvmMemoryListener, Region, +}; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; +use error_chain::bail; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::VcpuFd; use machine_manager::config::{ - get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, - parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, - parse_vsock, MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, - FAST_UNPLUG_ON, + check_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, + parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, + parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, MachineMemConfig, + NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, + VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; @@ -182,6 +180,8 @@ pub trait MachineOps { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) .chain_err(|| "Failed to mmap guest ram.")?; + set_host_memory_policy(&mem_mappings, &mem_config.mem_zones) + .chain_err(|| "Failed to set host memory NUMA policy.")?; } sys_mem @@ -471,6 +471,36 @@ pub trait MachineOps { bail!("No pci host found"); } + fn get_sys_bus(&mut self) -> &SysBus; + + fn get_fwcfg_dev(&mut self) -> Result>> { + bail!("No FwCfg deivce found"); + } + + fn get_boot_order_list(&self) -> Option>>> { + None + } + + fn reset_all_devices(&mut self) -> Result<()> { + let sysbus = self.get_sys_bus(); + for dev in sysbus.devices.iter() { + dev.lock() + .unwrap() + .reset() + .chain_err(|| "Fail to reset sysbus device")?; + } + + if let Ok(pci_host) = self.get_pci_host() { + pci_host + .lock() + .unwrap() + .reset() + .chain_err(|| "Fail to reset pci host")?; + } + + Ok(()) + } + fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { @@ -485,12 +515,88 @@ pub trait MachineOps { Ok(()) } + fn reset_fwcfg_boot_order(&mut self) -> Result<()> { + // unwrap is safe because stand machine always make sure it not return null. + let boot_order_vec = self.get_boot_order_list().unwrap(); + let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone(); + locked_boot_order_vec.sort_by(|x, y| x.boot_index.cmp(&y.boot_index)); + let mut fwcfg_boot_order_string = String::new(); + for item in &locked_boot_order_vec { + fwcfg_boot_order_string.push_str(&item.dev_path); + fwcfg_boot_order_string.push('\n'); + } + fwcfg_boot_order_string.push('\0'); + + let fwcfg = self.get_fwcfg_dev()?; + fwcfg + .lock() + .unwrap() + .modify_file_entry("bootorder", fwcfg_boot_order_string.as_bytes().to_vec()) + .chain_err(|| "Fail to add bootorder entry for standard VM.")?; + Ok(()) + } + + /// Add boot index of device. + /// + /// # Arguments + /// + /// * `bootindex` - The boot index of the device. + /// * `dev_path` - The firmware device path of the device. + /// * `dev_id` - The id of the device. + fn add_bootindex_devices( + &mut self, + boot_index: u8, + dev_path: &str, + dev_id: &str, + ) -> Result<()> { + // Unwrap is safe because StdMachine will overwrite this function, + // which ensure boot_order_list is not None. + let boot_order_list = self.get_boot_order_list().unwrap(); + if boot_order_list + .lock() + .unwrap() + .iter() + .any(|item| item.boot_index == boot_index) + { + bail!("Failed to add duplicated bootindex {}.", boot_index); + } + + boot_order_list.lock().unwrap().push(BootIndexInfo { + boot_index, + id: dev_id.to_string(), + dev_path: dev_path.to_string(), + }); + + Ok(()) + } + + /// Delete boot index of device. + /// + /// # Arguments + /// + /// * `dev_id` - The id of the device. + fn del_bootindex_devices(&self, dev_id: &str) { + // Unwrap is safe because StdMachine will overwrite this function, + // which ensure boot_order_list is not None. + let boot_order_list = self.get_boot_order_list().unwrap(); + let mut locked_boot_order_list = boot_order_list.lock().unwrap(); + locked_boot_order_list.retain(|item| item.id != dev_id); + } + fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_blk(vm_config, cfg_args)?; let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); - self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false)?; + let pci_dev = self + .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) + .chain_err(|| "Failed to add virtio pci device")?; + if let Some(bootindex) = device_cfg.boot_index { + if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { + self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id) + .chain_err(|| "Fail to add boot index")?; + } + } MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), device, @@ -605,7 +711,7 @@ pub trait MachineOps { device: Arc>, multi_func: bool, need_irqfd: bool, - ) -> Result<()> { + ) -> Result>> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; let sys_mem = self.get_sys_mem(); let mut pcidev = VirtioPciDevice::new( @@ -619,10 +725,11 @@ pub trait MachineOps { if need_irqfd { pcidev.enable_need_irqfd(); } + let clone_pcidev = Arc::new(Mutex::new(pcidev.clone())); pcidev .realize() .chain_err(|| "Failed to add virtio pci device")?; - Ok(()) + Ok(clone_pcidev) } /// Set the parent bus slot on when device attached @@ -684,6 +791,72 @@ pub trait MachineOps { Ok(()) } + /// Add numa nodes information to standard machine. + /// + /// # Arguments + /// + /// * `vm_config` - VM Configuration. + fn add_numa_nodes(&mut self, vm_config: &mut VmConfig) -> Result> { + if vm_config.numa_nodes.is_empty() { + return Ok(None); + } + + let mut numa_nodes: NumaNodes = BTreeMap::new(); + vm_config.numa_nodes.sort_by(|p, n| n.0.cmp(&p.0)); + for numa in vm_config.numa_nodes.iter() { + match numa.0.as_str() { + "node" => { + let numa_config: NumaConfig = parse_numa_mem(numa.1.as_str())?; + if numa_nodes.contains_key(&numa_config.numa_id) { + bail!("Numa node id is repeated {}", numa_config.numa_id); + } + let mut numa_node = NumaNode { + cpus: numa_config.cpus, + ..Default::default() + }; + + if let Some(object_cfg) = vm_config.object.remove(&numa_config.mem_dev) { + if let ObjConfig::Zone(zone_config) = object_cfg { + numa_node.size = zone_config.size; + } + } else { + bail!( + "Object for memory-backend-ram {} config not found", + numa_config.mem_dev + ); + } + numa_nodes.insert(numa_config.numa_id, numa_node); + } + "dist" => { + let dist: (u32, NumaDistance) = parse_numa_distance(numa.1.as_str())?; + if !numa_nodes.contains_key(&dist.0) { + bail!("Numa node id is not found {}", dist.0); + } + if !numa_nodes.contains_key(&dist.1.destination) { + bail!("Numa node id is not found {}", dist.1.destination); + } + + if let Some(n) = numa_nodes.get_mut(&dist.0) { + if n.distances.contains_key(&dist.1.destination) { + bail!( + "Numa destination info {} repeat settings", + dist.1.destination + ); + } + n.distances.insert(dist.1.destination, dist.1.distance); + } + } + _ => { + bail!("Unsupported args for NUMA node: {}", numa.0.as_str()); + } + } + } + + check_numa_node(&numa_nodes, vm_config)?; + + Ok(Some(numa_nodes)) + } + /// Add peripheral devices. /// /// # Arguments diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 189e8acea..d655af024 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -29,6 +29,8 @@ //! - `aarch64` pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); @@ -73,7 +75,7 @@ use devices::legacy::PL031; use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; #[cfg(target_arch = "aarch64")] -use devices::{InterruptController, InterruptControllerConfig}; +use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController}; use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] @@ -104,11 +106,15 @@ use virtio::{ }; use vmm_sys_util::eventfd::EventFd; +use self::errors::{ErrorKind, Result}; use super::{ errors::{ErrorKind as MachineErrorKind, Result as MachineResult}, MachineOps, }; -use errors::{ErrorKind, Result}; + +use error_chain::bail; +use log::error; +use machine_manager::event; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use syscall::syscall_whitelist; @@ -494,10 +500,7 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "aarch64")] fn init_interrupt_controller(&mut self, vcpu_count: u64) -> MachineResult<()> { // Interrupt Controller Chip init - let intc_conf = InterruptControllerConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - vcpu_count, - max_irq: 192, + let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], redist_region_ranges: vec![ @@ -506,6 +509,20 @@ impl MachineOps for LightMachine { ], its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), }; + let v2 = ICGICv2Config { + dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], + cpu_range: MEM_LAYOUT[LayoutEntryType::GicCpu as usize], + v2m_range: None, + sys_mem: None, + }; + // Passing both v2 and v3, leave GIC self to decide which one to use. + let intc_conf = ICGICConfig { + version: None, + vcpu_count, + max_irq: 192, + v3: Some(v3), + v2: Some(v2), + }; let irq_chip = InterruptController::new(&intc_conf)?; self.irq_chip = Some(Arc::new(irq_chip)); self.irq_chip.as_ref().unwrap().realize()?; @@ -608,6 +625,10 @@ impl MachineOps for LightMachine { &self.sys_mem } + fn get_sys_bus(&mut self) -> &SysBus { + &self.sysbus + } + #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { use crate::errors::ResultExt; @@ -934,7 +955,7 @@ impl DeviceInterface for LightMachine { current: true, qom_path: String::from("/machine/unattached/device[") + &cpu_index.to_string() - + &"]".to_string(), + + "]", halted: false, props: Some(cpu_instance), CPU: cpu_index as isize, @@ -1127,6 +1148,7 @@ impl DeviceInterface for LightMachine { iothread: None, iops: None, queues: 1, + boot_index: None, }; match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index e78700686..e96e49d96 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,18 +14,19 @@ mod pci_host_root; mod syscall; use std::fs::OpenOptions; +use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use acpi::{ - AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiTable, AmlBuilder, - AmlDevice, AmlInteger, AmlNameDecl, AmlScope, AmlScopeBuilder, AmlString, TableLoader, - ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, - ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, ACPI_GTDT_CAP_ALWAYS_ON, - ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, - ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, - ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, - INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, + AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, + AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, + AmlScopeBuilder, AmlString, TableLoader, ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, + ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, + ACPI_GTDT_CAP_ALWAYS_ON, ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX, ACPI_MADT_GENERIC_CPU_INTERFACE, + ACPI_MADT_GENERIC_DISTRIBUTOR, ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, + ARCH_GIC_MAINT_IRQ, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -33,10 +34,14 @@ use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; -use devices::{InterruptController, InterruptControllerConfig}; -use error_chain::ChainedError; +use devices::{ICGICConfig, ICGICv3Config, InterruptController}; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; -use machine_manager::config::{BootSource, PFlashConfig, SerialConfig, VmConfig}; +use log::error; +use machine_manager::config::{ + BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, +}; +use machine_manager::event; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, @@ -44,7 +49,7 @@ use machine_manager::machine::{ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -125,6 +130,12 @@ pub struct StdMachine { reset_req: EventFd, /// Device Tree Blob. dtb_vec: Vec, + /// List of guest NUMA nodes information. + numa_nodes: Option, + /// List contains the boot order of boot devices. + boot_order_list: Arc>>, + /// FwCfg device. + fwcfg_dev: Option>>, } impl StdMachine { @@ -170,13 +181,16 @@ impl StdMachine { reset_req: EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| ErrorKind::InitEventFdErr("reset_req".to_string()))?, dtb_vec: Vec::new(), + numa_nodes: None, + boot_order_list: Arc::new(Mutex::new(Vec::new())), + fwcfg_dev: None, }) } pub fn handle_reset_request(vm: &Arc>) -> Result<()> { use crate::errors::ResultExt; - let locked_vm = vm.lock().unwrap(); + let mut locked_vm = vm.lock().unwrap(); let mut fdt_addr: u64 = 0; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { @@ -201,18 +215,12 @@ impl StdMachine { ) .chain_err(|| "Fail to write dtb into sysmem")?; - for dev in locked_vm.sysbus.devices.iter() { - dev.lock() - .unwrap() - .reset() - .chain_err(|| "Fail to reset sysbus device")?; - } locked_vm - .pci_host - .lock() - .unwrap() - .reset() - .chain_err(|| "Fail to reset pci host")?; + .reset_all_devices() + .chain_err(|| "Fail to reset all devices")?; + locked_vm + .reset_fwcfg_boot_order() + .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.resume() @@ -286,6 +294,7 @@ impl StdMachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, ) .chain_err(|| "Failed to realize fwcfg device")?; + self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) } @@ -305,6 +314,10 @@ impl StdMachineOps for StdMachine { fn get_vm_config(&self) -> &Mutex { &self.vm_config } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } } impl MachineOps for StdMachine { @@ -313,10 +326,7 @@ impl MachineOps for StdMachine { } fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { - let intc_conf = InterruptControllerConfig { - version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - vcpu_count, - max_irq: 192, + let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], redist_region_ranges: vec![ @@ -325,6 +335,13 @@ impl MachineOps for StdMachine { ], its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), }; + let intc_conf = ICGICConfig { + version: None, + vcpu_count, + max_irq: 192, + v2: None, + v3: Some(v3), + }; let irq_chip = InterruptController::new(&intc_conf)?; self.irq_chip = Some(Arc::new(irq_chip)); self.irq_chip.as_ref().unwrap().realize()?; @@ -408,6 +425,7 @@ impl MachineOps for StdMachine { locked_vm .register_reset_event(&locked_vm.reset_req, clone_vm) .chain_err(|| "Fail to register reset event")?; + locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_mem, @@ -446,6 +464,10 @@ impl MachineOps for StdMachine { (None, None) }; + locked_vm + .reset_fwcfg_boot_order() + .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, @@ -529,6 +551,19 @@ impl MachineOps for StdMachine { fn get_pci_host(&mut self) -> StdResult<&Arc>> { Ok(&self.pci_host) } + + fn get_sys_bus(&mut self) -> &SysBus { + &self.sysbus + } + + fn get_fwcfg_dev(&mut self) -> Result>> { + // Unwrap is safe. Because after standard machine realize, this will not be None.F + Ok(self.fwcfg_dev.clone().unwrap()) + } + + fn get_boot_order_list(&self) -> Option>>> { + Some(self.boot_order_list.clone()) + } } impl AcpiBuilder for StdMachine { @@ -736,6 +771,71 @@ impl AcpiBuilder for StdMachine { .chain_err(|| "Fail to add MADT table to loader")?; Ok(madt_begin as u64) } + + fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { + for cpu in node.cpus.iter() { + srat.append_child( + &AcpiSratGiccAffinity { + type_id: 3_u8, + length: size_of::() as u8, + proximity_domain, + process_uid: *cpu as u32, + flags: 1, + clock_domain: 0_u32, + } + .aml_bytes(), + ); + } + } + + fn build_srat_mem( + &self, + base_addr: u64, + proximity_domain: u32, + node: &NumaNode, + srat: &mut AcpiTable, + ) -> u64 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr, + range_length: node.size, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + base_addr + node.size + } + + fn build_srat_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + if self.numa_nodes.is_none() { + return Ok(0); + } + + let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); + // Reserved + srat.append_child(&[1_u8; 4_usize]); + // Reserved + srat.append_child(&[0_u8; 8_usize]); + + let mut next_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { + self.build_srat_cpu(*id, node, &mut srat); + next_base = self.build_srat_mem(next_base, *id, node, &mut srat); + } + + let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) + .chain_err(|| "Fail to add SRAT table to loader")?; + Ok(srat_begin as u64) + } } impl MachineLifecycle for StdMachine { @@ -1047,6 +1147,8 @@ trait CompileFDTHelper { fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; /// Function that helps to generate the chosen node. fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + /// Function that helps to generate numa node distances. + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; } impl CompileFDTHelper for StdMachine { @@ -1131,14 +1233,31 @@ impl CompileFDTHelper for StdMachine { } fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { - let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.sys_mem.memory_end_address().raw_value() - - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let node = "memory"; - let memory_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; - fdt.end_node(memory_node_dep)?; + if self.numa_nodes.is_none() { + let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let mem_size = self.sys_mem.memory_end_address().raw_value() + - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let node = "memory"; + let memory_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.end_node(memory_node_dep)?; + + return Ok(()); + } + + // Set NUMA node information. + let mut mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + for (id, node) in self.numa_nodes.as_ref().unwrap().iter().enumerate() { + let mem_size = node.1.size; + let node = format!("memory@{:x}", mem_base); + let memory_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.set_property_u32("numa-node-id", id as u32)?; + fdt.end_node(memory_node_dep)?; + mem_base += mem_size; + } Ok(()) } @@ -1224,6 +1343,39 @@ impl CompileFDTHelper for StdMachine { Ok(()) } + + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + if self.numa_nodes.is_none() { + return Ok(()); + } + + let distance_node_dep = fdt.begin_node("distance-map")?; + fdt.set_property_string("compatible", "numa-distance-map-v1")?; + + let mut matrix = Vec::new(); + let numa_nodes = self.numa_nodes.as_ref().unwrap(); + let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); + for (id, node) in numa_nodes.iter().enumerate() { + let distances = &node.1.distances; + for i in existing_nodes.iter() { + matrix.push(id as u32); + matrix.push(*i as u32); + let dist: u32 = if id as u32 == *i { + 10 + } else if let Some(distance) = distances.get(i) { + *distance as u32 + } else { + 20 + }; + matrix.push(dist); + } + } + + fdt.set_property_array_u32("distance-matrix", matrix.as_ref())?; + fdt.end_node(distance_node_dep)?; + + Ok(()) + } } impl device_tree::CompileFDT for StdMachine { @@ -1240,7 +1392,7 @@ impl device_tree::CompileFDT for StdMachine { self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; - + self.generate_distance_node(fdt)?; fdt.end_node(node_dep)?; Ok(()) diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 36789f1de..1b5b5d439 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex, Weak}; +use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 897147930..80d16d2bc 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -18,6 +18,7 @@ mod x86_64; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; +use log::error; use machine_manager::event_loop::EventLoop; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; @@ -27,6 +28,8 @@ pub use x86_64::StdMachine; #[allow(clippy::upper_case_acronyms)] pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -72,11 +75,11 @@ use acpi::{ }; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use errors::{Result, ResultExt}; use machine_manager::config::{ get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, NetworkInterfaceConfig, - PciBdf, VmConfig, + NumaNode, NumaNodes, PciBdf, VmConfig, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -153,6 +156,17 @@ trait StdMachineOps: AcpiBuilder { .chain_err(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); + let srat_addr = self + .build_srat_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI SRAT table")?; + xsdt_entries.push(srat_addr); + + if let Some(numa_nodes) = self.get_numa_nodes() { + let slit_addr = Self::build_slit_table(numa_nodes, &acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI SLIT table")?; + xsdt_entries.push(slit_addr); + } + let xsdt_addr = Self::build_xsdt_table(&acpi_tables, &mut loader, xsdt_entries)?; let mut locked_fw_cfg = fw_cfg.lock().unwrap(); @@ -185,6 +199,8 @@ trait StdMachineOps: AcpiBuilder { fn get_vm_config(&self) -> &Mutex; + fn get_numa_nodes(&self) -> &Option; + /// Register event notifier for reset of standard machine. /// /// # Arguments @@ -558,6 +574,76 @@ trait AcpiBuilder { Ok(facs_begin as u64) } + /// Build ACPI SRAT CPU table. + /// # Arguments + /// + /// `proximity_domain` - The proximity domain. + /// `node` - The NUMA node. + /// `srat` - The SRAT table. + fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable); + + /// Build ACPI SRAT memory table. + /// # Arguments + /// + /// `base_addr` - The base address of the memory range. + /// `proximity_domain` - The proximity domain. + /// `node` - The NUMA node. + /// `srat` - The SRAT table. + fn build_srat_mem( + &self, + base_addr: u64, + proximity_domain: u32, + node: &NumaNode, + srat: &mut AcpiTable, + ) -> u64; + + /// Build ACPI SRAT table, returns the offset of ACPI SRAT table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + fn build_srat_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> Result; + + /// Build ACPI SLIT table, returns the offset of ACPI SLIT table in `acpi_data`. + /// + /// # Arguments + /// + /// `numa_nodes` - The information of NUMA nodes. + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + fn build_slit_table( + numa_nodes: &NumaNodes, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> Result { + let mut slit = AcpiTable::new(*b"SLIT", 1, *b"STRATO", *b"VIRTSLIT", 1); + slit.append_child((numa_nodes.len() as u64).as_bytes()); + + let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); + for (id, node) in numa_nodes.iter().enumerate() { + let distances = &node.1.distances; + for i in existing_nodes.iter() { + let dist: u8 = if id as u32 == *i { + 10 + } else if let Some(distance) = distances.get(i) { + *distance + } else { + 20 + }; + slit.append_child(dist.as_bytes()); + } + } + + let slit_begin = StdMachine::add_table_to_loader(acpi_data, loader, &slit) + .chain_err(|| "Fail to add SLIT table to loader")?; + Ok(slit_begin as u64) + } + /// Build ACPI XSDT table, returns the offset of ACPI XSDT table in `acpi_data`. /// /// # Arguments @@ -678,6 +764,7 @@ impl StdMachine { iothread: args.iothread.clone(), iops: conf.iops, queues: args.queues.unwrap_or(1), + boot_index: args.boot_index, }; dev.check()?; dev @@ -687,9 +774,17 @@ impl StdMachine { let blk_id = blk.id.clone(); let blk = Arc::new(Mutex::new(Block::new(blk))); - self.add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) + let pci_dev = self + .add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) .chain_err(|| "Failed to add virtio pci block device")?; + if let Some(bootindex) = args.boot_index { + if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { + self.add_bootindex_devices(bootindex, &dev_path, &args.id) + .chain_err(|| "Fail to add boot index")?; + } + } + MigrationManager::register_device_instance_mutex_with_id( BlockState::descriptor(), blk, @@ -744,6 +839,7 @@ impl StdMachine { &net_id, ); } + Ok(()) } @@ -949,7 +1045,13 @@ impl DeviceInterface for StdMachine { let locked_pci_host = pci_host.lock().unwrap(); if let Some((bus, dev)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &device_id) { match handle_unplug_request(&bus, &dev) { - Ok(()) => Response::create_empty_response(), + Ok(()) => { + let locked_dev = dev.lock().unwrap(); + let dev_id = locked_dev.name(); + drop(locked_pci_host); + self.del_bootindex_devices(&dev_id); + Response::create_empty_response() + } Err(e) => Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 24a8770d3..d6e2eb303 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -18,6 +18,8 @@ use std::sync::{ use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use error_chain::ChainedError; +use log::{debug, error}; +use pci::config::CLASS_CODE_ISA_BRIDGE; use pci::config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, @@ -29,7 +31,6 @@ use vmm_sys_util::eventfd::EventFd; use super::VENDOR_ID_INTEL; use crate::standard_vm::errors::Result; -use pci::config::CLASS_CODE_ISA_BRIDGE; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 2c93b28a4..05f87da36 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -13,7 +13,8 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{Region, RegionOps}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 0e6dc159d..9313035b6 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -21,42 +21,51 @@ use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use acpi::{ - AcpiIoApic, AcpiLocalApic, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, - AmlPackage, AmlScope, AmlScopeBuilder, AmlString, TableLoader, ACPI_TABLE_FILE, - IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, TABLE_CHECKSUM_OFFSET, + AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, + AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, + AmlString, TableLoader, IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; -use devices::legacy::{FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR}; -use error_chain::ChainedError; +use devices::legacy::{ + errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, + SERIAL_ADDR, +}; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; -use machine_manager::config::{BootSource, PFlashConfig, SerialConfig, VmConfig}; +use log::error; +use machine_manager::config::{ + BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, +}; +use machine_manager::event; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; +use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::{SysBus, SysBusDevOps}; +use sysbus::SysBus; +use syscall::syscall_whitelist; +use util::byte_code::ByteCode; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; use self::ich9_lpc::SLEEP_CTRL_OFFSET; - use super::errors::{ErrorKind, Result}; use super::{AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; use crate::MachineOps; -use mch::Mch; -use syscall::syscall_whitelist; -use util::byte_code::ByteCode; const VENDOR_ID_INTEL: u16 = 0x8086; +const HOLE_640K_START: u64 = 0x000A_0000; +const HOLE_640K_END: u64 = 0x0010_0000; /// The type of memory layout entry on x86_64 #[repr(usize)] @@ -105,6 +114,12 @@ pub struct StdMachine { /// VM power button, handle VM `Shutdown` event. power_button: EventFd, vm_config: Mutex, + /// List of guest NUMA nodes information. + numa_nodes: Option, + /// List contains the boot order of boot devices. + boot_order_list: Arc>>, + /// FwCfg device. + fwcfg_dev: Option>>, } impl StdMachine { @@ -152,13 +167,16 @@ impl StdMachine { power_button: EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?, vm_config: Mutex::new(vm_config.clone()), + numa_nodes: None, + boot_order_list: Arc::new(Mutex::new(Vec::new())), + fwcfg_dev: None, }) } pub fn handle_reset_request(vm: &Arc>) -> MachineResult<()> { use crate::errors::ResultExt as MachineResultExt; - let locked_vm = vm.lock().unwrap(); + let mut locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { MachineResultExt::chain_err(cpu.kick(), || { @@ -168,13 +186,11 @@ impl StdMachine { cpu.set_to_boot_state(); } - for dev in locked_vm.sysbus.devices.iter() { - MachineResultExt::chain_err(dev.lock().unwrap().reset(), || { - "Fail to reset sysbus device" - })?; - } - MachineResultExt::chain_err(locked_vm.pci_host.lock().unwrap().reset(), || { - "Fail to reset pci host" + MachineResultExt::chain_err(locked_vm.reset_all_devices(), || { + "Fail to reset all devices" + })?; + MachineResultExt::chain_err(locked_vm.reset_fwcfg_boot_order(), || { + "Fail to update boot order information to FwCfg device" })?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { @@ -295,8 +311,14 @@ impl StdMachineOps for StdMachine { fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, ncpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; + let boot_order = Vec::::new(); + fwcfg + .add_file_entry("bootorder", boot_order) + .chain_err(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; + let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) .chain_err(|| "Failed to realize fwcfg device")?; + self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) } @@ -316,6 +338,10 @@ impl StdMachineOps for StdMachine { fn get_vm_config(&self) -> &Mutex { &self.vm_config } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } } impl MachineOps for StdMachine { @@ -425,6 +451,7 @@ impl MachineOps for StdMachine { let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; + locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_io, @@ -477,6 +504,10 @@ impl MachineOps for StdMachine { StdMachine::arch_init()?; locked_vm.register_power_event(&locked_vm.power_button)?; + locked_vm + .reset_fwcfg_boot_order() + .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } @@ -561,6 +592,19 @@ impl MachineOps for StdMachine { fn get_pci_host(&mut self) -> Result<&Arc>> { Ok(&self.pci_host) } + + fn get_sys_bus(&mut self) -> &SysBus { + &self.sysbus + } + + fn get_fwcfg_dev(&mut self) -> MachineResult>> { + // Unwrap is safe. Because after standard machine realize, this will not be None.F + Ok(self.fwcfg_dev.clone().unwrap()) + } + + fn get_boot_order_list(&self) -> Option>>> { + Some(self.boot_order_list.clone()) + } } impl AcpiBuilder for StdMachine { @@ -569,6 +613,7 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> super::errors::Result { + use super::errors::ResultExt; let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -578,6 +623,7 @@ impl AcpiBuilder for StdMachine { let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0007".to_string()))); dev.append_child(AmlNameDecl::new("_UID", AmlInteger(cpu_id))); + dev.append_child(AmlNameDecl::new("_PXM", AmlInteger(0))); sb_scope.append_child(dev); } @@ -596,21 +642,8 @@ impl AcpiBuilder for StdMachine { package.append_child(AmlInteger(0)); dsdt.append_child(AmlNameDecl::new("_S5", package).aml_bytes().as_slice()); - let mut locked_acpi_data = acpi_data.lock().unwrap(); - let dsdt_begin = locked_acpi_data.len() as u32; - locked_acpi_data.extend(dsdt.aml_bytes()); - let dsdt_end = locked_acpi_data.len() as u32; - // Drop the lock of acpi_data to avoid dead-lock when adding entry to - // TableLoader, because TableLoader also needs to acquire this lock. - drop(locked_acpi_data); - - loader.add_cksum_entry( - ACPI_TABLE_FILE, - dsdt_begin + TABLE_CHECKSUM_OFFSET, - dsdt_begin, - dsdt_end - dsdt_begin, - )?; - + let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) + .chain_err(|| "Fail to add DSTD table to loader")?; Ok(dsdt_begin as u64) } @@ -619,6 +652,7 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> super::errors::Result { + use super::errors::ResultExt; let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.append_child(LAPIC_BASE_ADDR.as_bytes()); @@ -646,23 +680,130 @@ impl AcpiBuilder for StdMachine { madt.append_child(&lapic.aml_bytes()); }); - let mut locked_acpi_data = acpi_data.lock().unwrap(); - let madt_begin = locked_acpi_data.len() as u32; - locked_acpi_data.extend(madt.aml_bytes()); - let madt_end = locked_acpi_data.len() as u32; - // Drop the lock of acpi_data to avoid dead-lock when adding entry to - // TableLoader, because TableLoader also needs to acquire this lock. - drop(locked_acpi_data); - - loader.add_cksum_entry( - ACPI_TABLE_FILE, - madt_begin + TABLE_CHECKSUM_OFFSET, - madt_begin, - madt_end - madt_begin, - )?; - + let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) + .chain_err(|| "Fail to add DSTD table to loader")?; Ok(madt_begin as u64) } + + fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { + for cpu in node.cpus.iter() { + srat.append_child( + &AcpiSratProcessorAffinity { + length: size_of::() as u8, + proximity_lo: proximity_domain as u8, + local_apic_id: *cpu, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + } + + fn build_srat_mem( + &self, + base_addr: u64, + proximity_domain: u32, + node: &NumaNode, + srat: &mut AcpiTable, + ) -> u64 { + let mem_below_4g = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + let mem_above_4g = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; + + let mut mem_base = base_addr; + let mut mem_len = node.size; + let mut next_base = mem_base + mem_len; + // It contains the hole from 604Kb to 1Mb + if mem_base <= HOLE_640K_START && next_base > HOLE_640K_START { + mem_len -= next_base - HOLE_640K_START; + if mem_len > 0 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr: mem_base, + range_length: mem_len, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + + if next_base <= HOLE_640K_END { + next_base = HOLE_640K_END; + return next_base; + } + mem_base = HOLE_640K_END; + mem_len = next_base - HOLE_640K_END; + } + + // It contains the hole possibly from mem_below_4g(2G) to mem_below_4g(4G). + if mem_base <= mem_below_4g && next_base > mem_below_4g { + mem_len -= next_base - mem_below_4g; + if mem_len > 0 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr: mem_base, + range_length: mem_len, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + mem_base = mem_above_4g; + mem_len = next_base - mem_below_4g; + next_base = mem_base + mem_len; + } + + if mem_len > 0 { + srat.append_child( + &AcpiSratMemoryAffinity { + type_id: 1, + length: size_of::() as u8, + proximity_domain, + base_addr: mem_base, + range_length: mem_len, + flags: 1, + ..Default::default() + } + .aml_bytes(), + ); + } + + next_base + } + + fn build_srat_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + if self.numa_nodes.is_none() { + return Ok(0); + } + + let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); + srat.append_child(&[1_u8; 4_usize]); + srat.append_child(&[0_u8; 8_usize]); + + let mut next_base = 0_u64; + for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { + self.build_srat_cpu(*id, node, &mut srat); + next_base = self.build_srat_mem(next_base, *id, node, &mut srat); + } + + let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) + .chain_err(|| "Fail to add SRAT table to loader")?; + Ok(srat_begin as u64) + } } impl MachineLifecycle for StdMachine { diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 95adff176..7e61a40b8 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -372,6 +372,16 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true) .required(false), ) + .arg( + Arg::with_name("numa") + .multiple(true) + .long("numa") + .value_name("node,nodeid=0,cpus=0-1,memdev=mem0> \ + -numa \ + -object Result { add_args_to_config_multi!((args.values_of("chardev")), vm_cfg, add_chardev); add_args_to_config_multi!((args.values_of("device")), vm_cfg, add_device); add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); + add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); if let Some(s) = args.value_of("trace") { add_trace_events(&s)?; diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index ec19445f9..7a7d9376a 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 1567ed855..e525f5c5e 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 0386dc4e4..9850ba293 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -14,6 +14,8 @@ use std::fs::metadata; use std::os::linux::fs::MetadataExt; use std::path::Path; +use error_chain::bail; +use log::error; use serde::{Deserialize, Serialize}; use super::{ @@ -39,6 +41,14 @@ pub struct BlkDevConfig { pub iothread: Option, pub iops: Option, pub queues: u16, + pub boot_index: Option, +} + +#[derive(Debug, Clone)] +pub struct BootIndexInfo { + pub boot_index: u8, + pub id: String, + pub dev_path: String, } impl Default for BlkDevConfig { @@ -52,6 +62,7 @@ impl Default for BlkDevConfig { iothread: None, iops: None, queues: 1, + boot_index: None, } } } @@ -250,11 +261,11 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result("bootindex") { - bail!("Failed to parse \'bootindex\': {:?}", &e); + let mut blkdevcfg = BlkDevConfig::default(); + if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { + blkdevcfg.boot_index = Some(boot_index); } - let mut blkdevcfg = BlkDevConfig::default(); let blkdrive = if let Some(drive) = cmd_parser.get_value::("drive")? { drive } else { diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index fb428358c..a99b2fc38 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -12,10 +12,11 @@ use std::str::FromStr; +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result, ResultExt}; -use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig}; +use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_NODES, MAX_STRING_LENGTH}; const DEFAULT_CPUS: u8 = 1; const DEFAULT_THREADS: u8 = 1; @@ -54,6 +55,47 @@ impl FromStr for MachineType { } } +#[repr(u32)] +#[derive(PartialEq, Eq)] +pub enum HostMemPolicy { + Default = 0, + Preferred = 1, + Bind = 2, + Interleave = 3, + NotSupported = 4, +} + +impl From for HostMemPolicy { + fn from(str: String) -> HostMemPolicy { + match str.to_lowercase().as_str() { + "default" => HostMemPolicy::Default, + "preferred" => HostMemPolicy::Preferred, + "bind" => HostMemPolicy::Bind, + "interleave" => HostMemPolicy::Interleave, + _ => HostMemPolicy::NotSupported, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MemZoneConfig { + pub id: String, + pub size: u64, + pub host_numa_node: Option, + pub policy: String, +} + +impl Default for MemZoneConfig { + fn default() -> Self { + MemZoneConfig { + id: String::new(), + size: 0, + host_numa_node: None, + policy: String::from("bind"), + } + } +} + /// Config that contains machine's memory information config. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct MachineMemConfig { @@ -62,6 +104,7 @@ pub struct MachineMemConfig { pub dump_guest_core: bool, pub mem_share: bool, pub mem_prealloc: bool, + pub mem_zones: Option>, } impl Default for MachineMemConfig { @@ -72,6 +115,7 @@ impl Default for MachineMemConfig { dump_guest_core: true, mem_share: false, mem_prealloc: false, + mem_zones: None, } } } @@ -276,6 +320,66 @@ impl VmConfig { pub fn enable_mem_prealloc(&mut self) { self.machine_config.mem_config.mem_prealloc = true; } + + pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { + let mut cmd_parser = CmdParser::new("mem_zone"); + cmd_parser + .push("") + .push("id") + .push("size") + .push("host-nodes") + .push("policy"); + cmd_parser.parse(mem_zone)?; + + let mut zone_config = MemZoneConfig::default(); + if let Some(id) = cmd_parser.get_value::("id")? { + if id.len() > MAX_STRING_LENGTH { + return Err( + ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into(), + ); + } + zone_config.id = id; + } else { + return Err(ErrorKind::FieldIsMissing("id", "memory-backend-ram").into()); + } + if let Some(mem) = cmd_parser.get_value::("size")? { + zone_config.size = memory_unit_conversion(&mem)?; + } else { + return Err(ErrorKind::FieldIsMissing("size", "memory-backend-ram").into()); + } + if let Some(host_nodes) = cmd_parser.get_value::("host-nodes")? { + if host_nodes >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "host_nodes".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + zone_config.host_numa_node = Some(host_nodes); + } + if let Some(policy) = cmd_parser.get_value::("policy")? { + if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { + return Err(ErrorKind::InvalidParam("policy".to_string(), policy).into()); + } + zone_config.policy = policy; + } + + if self.machine_config.mem_config.mem_zones.is_some() { + self.machine_config + .mem_config + .mem_zones + .as_mut() + .unwrap() + .push(zone_config.clone()); + } else { + self.machine_config.mem_config.mem_zones = Some(vec![zone_config.clone()]); + } + + Ok(zone_config) + } } fn adjust_topology( @@ -316,7 +420,12 @@ fn adjust_topology( (max_cpus, sockets, cores, threads) } -fn memory_unit_conversion(origin_value: &str) -> Result { +/// Convert memory units from GiB, Mib to Byte. +/// +/// # Arguments +/// +/// * `origin_value` - The origin memory value from user. +pub fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) { @@ -374,6 +483,7 @@ mod tests { mem_share: false, dump_guest_core: false, mem_prealloc: false, + mem_zones: None, }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, @@ -700,4 +810,41 @@ mod tests { let cpu_cfg_ret = vm_config.add_cpu(cpu_cfg_str); assert!(cpu_cfg_ret.is_err()); } + + #[test] + fn test_add_mem_zone() { + let mut vm_config = VmConfig::default(); + let zone_config_1 = vm_config + .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind") + .unwrap(); + assert_eq!(zone_config_1.id, "mem1"); + assert_eq!(zone_config_1.size, 2147483648); + assert_eq!(zone_config_1.host_numa_node, Some(1)); + assert_eq!(zone_config_1.policy, "bind"); + + let zone_config_2 = vm_config + .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1") + .unwrap(); + assert_eq!(zone_config_2.host_numa_node, None); + assert_eq!(zone_config_2.policy, "bind"); + + assert!(vm_config + .add_mem_zone("-object memory-backend-ram,size=2G") + .is_err()); + assert!(vm_config + .add_mem_zone("-object memory-backend-ram,id=mem1") + .is_err()); + } + + #[test] + fn test_host_mem_policy() { + let policy = HostMemPolicy::from(String::from("default")); + assert!(policy == HostMemPolicy::Default); + + let policy = HostMemPolicy::from(String::from("interleave")); + assert!(policy == HostMemPolicy::Interleave); + + let policy = HostMemPolicy::from(String::from("error")); + assert!(policy == HostMemPolicy::NotSupported); + } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 4c4e2bcd3..554b42b33 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -11,6 +11,8 @@ // See the Mulan PSL v2 for more details. pub mod errors { + use error_chain::error_chain; + error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); @@ -85,6 +87,7 @@ pub use drive::*; pub use iothread::*; pub use machine_config::*; pub use network::*; +pub use numa::*; pub use pci::*; pub use rng::*; pub use vfio::*; @@ -97,6 +100,7 @@ mod drive; mod iothread; mod machine_config; mod network; +mod numa; mod pci; mod rng; mod vfio; @@ -105,6 +109,8 @@ use std::any::Any; use std::collections::HashMap; use std::str::FromStr; +use error_chain::bail; +use log::error; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::trace::enable_trace_events; @@ -114,10 +120,12 @@ pub const MAX_PATH_LENGTH: usize = 4096; pub const MAX_VIRTIO_QUEUE: usize = 1024; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; +pub const MAX_NODES: u32 = 128; #[derive(Debug, Clone)] pub enum ObjConfig { Rng(RngObjConfig), + Zone(MemZoneConfig), } fn parse_rng_obj(object_args: &str) -> Result { @@ -157,6 +165,7 @@ pub struct VmConfig { pub pflashs: Option>, pub dev_name: HashMap, pub global_config: HashMap, + pub numa_nodes: Vec<(String, String)>, } impl VmConfig { @@ -240,7 +249,17 @@ impl VmConfig { if self.object.get(&id).is_none() { self.object.insert(id, object_config); } else { - bail!("Object: {:?} has been added"); + bail!("Object: {} has been added", id); + } + } + "memory-backend-ram" => { + let zone_config = self.add_mem_zone(object_args)?; + let id = zone_config.id.clone(); + let object_config = ObjConfig::Zone(zone_config); + if self.object.get(&id).is_none() { + self.object.insert(id, object_config); + } else { + bail!("Object: {} has been added", id); } } _ => { @@ -490,6 +509,42 @@ pub fn add_trace_events(config: &str) -> Result<()> { bail!("trace: events file must be set."); } +pub struct IntegerList(pub Vec); + +impl FromStr for IntegerList { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + let mut integer_list = Vec::new(); + let lists: Vec<&str> = s.trim().split(',').collect(); + for list in lists.iter() { + let items: Vec<&str> = list.split('-').collect(); + if items.len() > 2 { + return Err(()); + } + + let start = items[0] + .parse::() + .map_err(|_| error!("Invalid value {}", items[0]))?; + integer_list.push(start); + if items.len() == 2 { + let end = items[1] + .parse::() + .map_err(|_| error!("Invalid value {}", items[1]))?; + if start >= end { + return Err(()); + } + + for i in start..end { + integer_list.push(i + 1); + } + } + } + + Ok(IntegerList(integer_list)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 820a5e56c..fa32d368b 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::{ diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs new file mode 100644 index 000000000..c97938817 --- /dev/null +++ b/machine_manager/src/config/numa.rs @@ -0,0 +1,310 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::BTreeMap; + +use error_chain::bail; + +use super::errors::{ErrorKind, Result}; +use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; + +const MIN_NUMA_DISTANCE: u8 = 10; + +#[derive(Default, Debug)] +pub struct NumaDistance { + pub destination: u32, + pub distance: u8, +} + +#[derive(Default, Debug)] +pub struct NumaConfig { + pub numa_id: u32, + pub cpus: Vec, + pub distances: Option>, + pub size: u64, + pub mem_dev: String, +} + +#[derive(Default)] +pub struct NumaNode { + pub cpus: Vec, + pub distances: BTreeMap, + pub size: u64, +} + +pub type NumaNodes = BTreeMap; + +pub fn check_numa_node(numa_nodes: &NumaNodes, vm_config: &mut VmConfig) -> Result<()> { + let mut total_ram_size = 0_u64; + let mut total_cpu_num = 0_u8; + let mut max_cpu_idx = 0_u8; + for node in numa_nodes.iter() { + total_ram_size += node.1.size; + total_cpu_num += node.1.cpus.len() as u8; + for idx in &node.1.cpus { + if max_cpu_idx <= *idx { + max_cpu_idx = *idx; + } + } + } + + if total_ram_size != vm_config.machine_config.mem_config.mem_size { + bail!( + "Total memory {} of NUMA nodes is not equals to memory size {}", + total_ram_size, + vm_config.machine_config.mem_config.mem_size, + ); + } + if total_cpu_num != vm_config.machine_config.nr_cpus { + bail!( + "Total cpu numbers {} of NUMA nodes is not equals to smp {}", + total_cpu_num, + vm_config.machine_config.nr_cpus, + ); + } + if max_cpu_idx != vm_config.machine_config.nr_cpus - 1 { + bail!("Error to configure CPU sets, please check you cmdline again"); + } + + Ok(()) +} + +pub fn parse_numa_mem(numa_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("numa"); + cmd_parser + .push("") + .push("nodeid") + .push("cpus") + .push("memdev"); + cmd_parser.parse(numa_config)?; + + let mut config: NumaConfig = NumaConfig::default(); + if let Some(node_id) = cmd_parser.get_value::("nodeid")? { + if node_id >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "nodeid".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + config.numa_id = node_id; + } else { + return Err(ErrorKind::FieldIsMissing("nodeid", "numa").into()); + } + if let Some(cpus) = cmd_parser + .get_value::("cpus") + .map_err(|_| ErrorKind::ConvertValueFailed(String::from("u64"), "cpus".to_string()))? + .map(|v| v.0.iter().map(|e| *e as u8).collect()) + { + config.cpus = cpus; + } else { + return Err(ErrorKind::FieldIsMissing("cpus", "numa").into()); + } + if let Some(mem_dev) = cmd_parser.get_value::("memdev")? { + config.mem_dev = mem_dev; + } else { + return Err(ErrorKind::FieldIsMissing("memdev", "numa").into()); + } + + Ok(config) +} + +pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { + let mut cmd_parser = CmdParser::new("numa"); + cmd_parser.push("").push("src").push("dst").push("val"); + cmd_parser.parse(numa_dist)?; + + let mut dist: NumaDistance = NumaDistance::default(); + let numa_id = if let Some(src) = cmd_parser.get_value::("src")? { + if src >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "src".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + src + } else { + return Err(ErrorKind::FieldIsMissing("src", "numa").into()); + }; + if let Some(dst) = cmd_parser.get_value::("dst")? { + if dst >= MAX_NODES { + return Err(ErrorKind::IllegalValue( + "dst".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ) + .into()); + } + dist.destination = dst; + } else { + return Err(ErrorKind::FieldIsMissing("dst", "numa").into()); + } + if let Some(val) = cmd_parser.get_value::("val")? { + if val < MIN_NUMA_DISTANCE { + bail!("NUMA distance shouldn't be less than 10"); + } + dist.distance = val; + } else { + return Err(ErrorKind::FieldIsMissing("val", "numa").into()); + } + + if numa_id == dist.destination && dist.distance != MIN_NUMA_DISTANCE { + bail!("Local distance of node {} should be 10.", numa_id); + } + + Ok((numa_id, dist)) +} + +impl VmConfig { + pub fn add_numa(&mut self, numa_config: &str) -> Result<()> { + let mut cmd_params = CmdParser::new("numa"); + cmd_params.push(""); + + cmd_params.get_parameters(numa_config)?; + if let Some(numa_type) = cmd_params.get_value::("")? { + self.numa_nodes.push((numa_type, numa_config.to_string())); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_numa_mem() { + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_numa("-numa node,nodeid=0,cpus=0-1,memdev=mem0") + .is_ok()); + assert!(vm_config + .add_numa("-numa node,nodeid=1,cpus=2-1,memdev=mem1") + .is_ok()); + assert!(vm_config + .add_numa("-numa node,nodeid=2,memdev=mem2") + .is_ok()); + assert!(vm_config.add_numa("-numa node,nodeid=3,cpus=3-4").is_ok()); + + let numa = vm_config.numa_nodes.get(0).unwrap(); + let numa_config = parse_numa_mem(numa.1.as_str()).unwrap(); + assert_eq!(numa_config.cpus, vec![0, 1]); + assert_eq!(numa_config.mem_dev, "mem0"); + + let numa = vm_config.numa_nodes.get(1).unwrap(); + assert!(parse_numa_mem(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(2).unwrap(); + assert!(parse_numa_mem(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(3).unwrap(); + assert!(parse_numa_mem(numa.1.as_str()).is_err()); + } + + #[test] + fn test_parse_numa_distance() { + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,src=0,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,src=0,dst=1").is_ok()); + + let numa = vm_config.numa_nodes.get(0).unwrap(); + let dist = parse_numa_distance(numa.1.as_str()).unwrap(); + assert_eq!(dist.0, 0); + assert_eq!(dist.1.destination, 1); + assert_eq!(dist.1.distance, 10); + + let numa = vm_config.numa_nodes.get(1).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(2).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(3).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); + } + + #[test] + fn test_check_numa_nodes() { + let mut vm_config = VmConfig::default(); + vm_config.machine_config.nr_cpus = 4; + vm_config.machine_config.mem_config.mem_size = 2147483648; + + let numa_node1 = NumaNode { + cpus: vec![0, 1], + distances: Default::default(), + size: 1073741824, + }; + let numa_node2 = NumaNode { + cpus: vec![2, 3], + distances: Default::default(), + size: 1073741824, + }; + + let mut numa_nodes = BTreeMap::new(); + numa_nodes.insert(0, numa_node1); + numa_nodes.insert(1, numa_node2); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_ok()); + + let numa_node3 = NumaNode { + cpus: vec![2], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(2, numa_node3); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node4 = NumaNode { + cpus: vec![2, 3, 4], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node4); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node5 = NumaNode { + cpus: vec![3, 4], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node5); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node6 = NumaNode { + cpus: vec![0, 1], + distances: Default::default(), + size: 1073741824, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node6); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + + let numa_node7 = NumaNode { + cpus: vec![2, 3], + distances: Default::default(), + size: 2147483648, + }; + numa_nodes.remove(&1); + numa_nodes.insert(1, numa_node7); + assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + } +} diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 9b6409280..9c6db3c48 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; + use super::errors::{ErrorKind, Result, ResultExt}; use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::ExBool; diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 349bf31cf..e1caff629 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; + use super::errors::{ErrorKind, Result}; use super::{pci_args_check, ObjConfig}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 72ef64f7f..bc4e177e7 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -14,10 +14,12 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::{process, thread}; +use super::config::IothreadConfig; use crate::machine::IOTHREADS; use crate::qmp::qmp_schema::IothreadInfo; -use super::config::IothreadConfig; +use error_chain::bail; +use log::info; use util::loop_context::{EventLoopContext, EventLoopManager, EventNotifier}; /// This struct used to manage all events occur during VM lifetime. diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index a544c609d..f76598a08 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -21,11 +21,6 @@ //! 2. The API interface over VM inside and outside. //! 3. Configuration for VM and its devices. -#[macro_use] -extern crate log; -#[macro_use] -extern crate error_chain; - pub mod cmdline; pub mod config; pub mod event_loop; @@ -36,6 +31,8 @@ pub mod socket; pub mod temp_cleaner; pub mod errors { + use error_chain::error_chain; + error_chain! { links { ConfigParser(crate::config::errors::Error, crate::config::errors::ErrorKind); diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 12e493ab9..4694e8d6a 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -37,6 +37,7 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; +use log::{info, warn}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 17dc7e540..95f8b9eaa 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -484,6 +484,7 @@ pub struct device_add { pub host: Option, #[serde(rename = "num-queues")] pub queues: Option, + pub boot_index: Option, } pub type DeviceAddArgument = device_add; @@ -1744,7 +1745,7 @@ mod tests { fn test_qmp_unexpected_arguments() { // qmp: quit. let json_msg = r#" - { + { "execute": "quit" } "#; @@ -1757,8 +1758,8 @@ mod tests { // unexpected arguments for quit. let json_msg = r#" - { - "execute": "quit" , + { + "execute": "quit" , "arguments": "isdf" } "#; @@ -1771,7 +1772,7 @@ mod tests { // qmp: stop. let json_msg = r#" - { + { "execute": "stop" } "#; @@ -1784,8 +1785,8 @@ mod tests { // unexpected arguments for stop. let json_msg = r#" - { - "execute": "stop" , + { + "execute": "stop" , "arguments": "isdf" } "#; @@ -1798,7 +1799,7 @@ mod tests { // qmp: cont. let json_msg = r#" - { + { "execute": "cont" } "#; @@ -1811,8 +1812,8 @@ mod tests { // unexpected arguments for count. let json_msg = r#" - { - "execute": "cont" , + { + "execute": "cont" , "arguments": "isdf" } "#; @@ -1825,7 +1826,7 @@ mod tests { // qmp: query-hotpluggable-cpus. let json_msg = r#" - { + { "execute": "query-hotpluggable-cpus" } "#; @@ -1838,8 +1839,8 @@ mod tests { // unexpected arguments for query-hotpluggable-cpus. let json_msg = r#" - { - "execute": "query-hotpluggable-cpus" , + { + "execute": "query-hotpluggable-cpus" , "arguments": "isdf" } "#; @@ -1852,7 +1853,7 @@ mod tests { // qmp: query-cpus. let json_msg = r#" - { + { "execute": "query-cpus" } "#; @@ -1865,8 +1866,8 @@ mod tests { // unexpected arguments for query-cpus. let json_msg = r#" - { - "execute": "query-cpus" , + { + "execute": "query-cpus" , "arguments": "isdf" } "#; @@ -1879,7 +1880,7 @@ mod tests { // qmp: query-ststus. let json_msg = r#" - { + { "execute": "query-status" } "#; @@ -1892,8 +1893,8 @@ mod tests { // unexpected arguments for query-status. let json_msg = r#" - { - "execute": "query-status" , + { + "execute": "query-status" , "arguments": "isdf" } "#; @@ -1909,11 +1910,11 @@ mod tests { fn test_wrong_qmp_arguments() { // right arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driver":"virtio-net-mmio", + "id":"net-0", + "driver":"virtio-net-mmio", "addr":"0x0" } } @@ -1927,11 +1928,11 @@ mod tests { // unknow arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driver":"virtio-net-mmio", + "id":"net-0", + "driver":"virtio-net-mmio", "addr":"0x0", "UnknowArg": "should go to error" } @@ -1946,11 +1947,11 @@ mod tests { // wrong spelling arguments for device_add. let json_msg = r#" - { - "execute": "device_add" , + { + "execute": "device_add" , "arguments": { - "id":"net-0", - "driv":"virtio-net-mmio", + "id":"net-0", + "driv":"virtio-net-mmio", "addr":"0x0" } } @@ -1964,8 +1965,8 @@ mod tests { // right arguments for device_del. let json_msg = r#" - { - "execute": "device_del" , + { + "execute": "device_del" , "arguments": { "id": "net-1" } @@ -1980,8 +1981,8 @@ mod tests { // wrong arguments for device_del. let json_msg = r#" - { - "execute": "device_del" , + { + "execute": "device_del" , "arguments": { "value": "h8i" } @@ -1998,7 +1999,7 @@ mod tests { // missing arguments for getfd. let json_msg = r#" - { + { "execute": "getfd" } "#; @@ -2011,8 +2012,8 @@ mod tests { // unexpected arguments for getfd. let json_msg = r#" - { - "execute": "getfd" , + { + "execute": "getfd" , "arguments": "isdf" } "#; @@ -2025,10 +2026,10 @@ mod tests { // right arguments for getfd. let json_msg = r#" - { + { "execute": "getfd", - "arguments": { - "fdname": "fd1" + "arguments": { + "fdname": "fd1" } } "#; @@ -2041,17 +2042,17 @@ mod tests { // right arguments for blockdev-add. let json_msg = r#" - { + { "execute": "blockdev-add", "arguments": { - "node-name": "drive-0", + "node-name": "drive-0", "file": { - "driver": "file", + "driver": "file", "filename": "/path/to/block" - }, + }, "cache": { "direct": true - }, + }, "read-only": false } } @@ -2065,11 +2066,11 @@ mod tests { // right arguments for device-add. let json_msg = r#" - { + { "execute": "device_add", "arguments": { - "id": "drive-0", - "driver": "virtio-blk-mmio", + "id": "drive-0", + "driver": "virtio-blk-mmio", "addr": "0x1" } } @@ -2083,10 +2084,10 @@ mod tests { // right arguments for netdev-add. let json_msg = r#" - { + { "execute": "netdev_add", "arguments": { - "id": "net-0", + "id": "net-0", "ifname":"tap0" } } @@ -2103,8 +2104,8 @@ mod tests { fn test_unsupported_commands() { // unsupported qmp command. let json_msg = r#" - { - "execute": "hello-world" , + { + "execute": "hello-world" , } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2116,8 +2117,8 @@ mod tests { // unsupported qmp command, and unknow field. let json_msg = r#" - { - "execute": "hello-world" , + { + "execute": "hello-world" , "arguments": { "msg": "hello", } @@ -2135,8 +2136,8 @@ mod tests { fn test_qmp_commands() { // query-version let json_msg = r#" - { - "execute": "query-version" + { + "execute": "query-version" } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2148,8 +2149,8 @@ mod tests { // query-target let json_msg = r#" - { - "execute": "query-target" + { + "execute": "query-target" } "#; let err_msg = match serde_json::from_str::(json_msg) { @@ -2161,8 +2162,8 @@ mod tests { // query-commands let json_msg = r#" - { - "execute": "query-commands" + { + "execute": "query-commands" } "#; let err_msg = match serde_json::from_str::(json_msg) { diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 0cd4524f5..138a576ed 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -9,7 +9,6 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -extern crate vmm_sys_util; use crate::temp_cleaner::TempCleaner; use std::io::Write; diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 594ef64a3..aa31717f6 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -16,6 +16,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::sync::{Arc, Mutex, RwLock}; +use error_chain::bail; +use log::{error, info}; use util::leak_bucket::LeakBucket; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use vmm_sys_util::epoll::EventSet; diff --git a/migration/src/device_state.rs b/migration/src/device_state.rs index de9c16cf8..e3f64d61c 100644 --- a/migration/src/device_state.rs +++ b/migration/src/device_state.rs @@ -12,6 +12,7 @@ use std::cmp::Ordering; +use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::Result; @@ -169,6 +170,7 @@ impl DeviceStateDesc { #[cfg(test)] pub mod tests { use super::{DeviceStateDesc, FieldDesc, StateTransfer, VersionCheck}; + use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; #[derive(Default)] diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 9ccab5f3f..491a8d01d 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -14,14 +14,6 @@ //! //! Offer snapshot and migration interface for VM. -#[macro_use] -extern crate error_chain; -#[cfg(test)] -#[macro_use] -extern crate migration_derive; -#[macro_use] -extern crate log; - mod device_state; mod header; mod manager; @@ -33,6 +25,8 @@ pub use manager::{MigrationHook, MigrationManager, MigrationRestoreOrder}; pub use status::MigrationStatus; pub mod errors { + use error_chain::error_chain; + use super::status::MigrationStatus; error_chain! { diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 1ea194e26..2ca89a258 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use log::{debug, info}; use std::cmp; use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index 30ee13c75..35e7c0e40 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -16,6 +16,8 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::path::PathBuf; +use error_chain::bail; +use log::info; use util::byte_code::ByteCode; use util::reader::BufferReader; use util::unix::host_page_size; diff --git a/migration_derive/src/lib.rs b/migration_derive/src/lib.rs index a71ab54f0..a219f1ec3 100644 --- a/migration_derive/src/lib.rs +++ b/migration_derive/src/lib.rs @@ -48,12 +48,9 @@ //! 2. The `ByteCode` derive to auto add `ByteCode` trait and its relying trait for //! struct, such as `Default`, `Sync`, `Send`. -#[macro_use] -extern crate syn; - use proc_macro::TokenStream; use quote::quote; -use syn::DeriveInput; +use syn::{parse_macro_input, DeriveInput}; mod attr_parser; mod field_parser; diff --git a/ozone/src/capability.rs b/ozone/src/capability.rs index e7eae704f..356748d47 100644 --- a/ozone/src/capability.rs +++ b/ozone/src/capability.rs @@ -13,6 +13,7 @@ //! Remove all capability for ozone when uid is 0, use -capability cap_* to add capability. use crate::{syscall, ErrorKind, Result, ResultExt}; +use error_chain::bail; use std::{collections::HashMap, io::Write}; const CAPS_V3: u32 = 0x20080522; diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index 8ce65394a..e06cf6e00 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::{ collections::HashMap, fs::{self, OpenOptions}, diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 4b27dd20f..4e37441bf 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -21,6 +21,7 @@ use std::{ process::Stdio, }; +use error_chain::bail; use util::arg_parser::ArgMatches; const BASE_OZONE_PATH: &str = "/srv/ozone"; diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 4cb998480..27507878b 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -10,11 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; +use error_chain::{error_chain, quick_main}; -use args::create_args_parser; -use handler::OzoneHandler; +use crate::args::create_args_parser; +use crate::handler::OzoneHandler; mod args; mod capability; diff --git a/pci/src/bus.rs b/pci/src/bus.rs index e88e9c2de..107239f01 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -14,6 +14,8 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use address_space::Region; +use error_chain::bail; +use log::debug; use super::config::{SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM}; use super::hotplug::HotplugOps; diff --git a/pci/src/host.rs b/pci/src/host.rs index c84a6becc..3baf26cee 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -12,6 +12,8 @@ use std::sync::{Arc, Mutex}; +#[cfg(target_arch = "aarch64")] +use acpi::AmlOne; use acpi::{ AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlByte, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, @@ -360,6 +362,12 @@ impl AmlBuilder for PciHost { pci_host_bridge.append_child(AmlNameDecl::new("_CID", AmlEisaId::new("PNP0A03"))); pci_host_bridge.append_child(AmlNameDecl::new("_ADR", AmlZero)); pci_host_bridge.append_child(AmlNameDecl::new("_UID", AmlZero)); + #[cfg(target_arch = "aarch64")] + { + // CCA: Cache Coherency Attribute, which determines whether + // guest supports DMA features in pci host on aarch64 platform. + pci_host_bridge.append_child(AmlNameDecl::new("_CCA", AmlOne)); + } build_osc_for_aml(&mut pci_host_bridge); diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index 9edd0fa29..5b80f40bf 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::sync::{Arc, Mutex}; use crate::{errors::Result, PciBus, PciDevOps}; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 87eb16373..ddb8a395e 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -10,16 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate migration_derive; -#[macro_use] -extern crate machine_manager; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -59,13 +52,14 @@ pub use root_port::RootPort; use std::{ mem::size_of, - sync::{Mutex, Weak}, + sync::{Arc, Mutex, Weak}, }; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; -use config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; -use errors::Result; +use crate::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use crate::errors::Result; const BDF_FUNC_SHIFT: u8 = 3; @@ -208,6 +202,51 @@ pub trait PciDevOps: Send { fn devfn(&self) -> Option { None } + + /// Get the path of the PCI bus where the device resides. + fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { + let locked_parent_bus = parent_bus.lock().unwrap(); + let parent_dev_path = if locked_parent_bus.name.eq("pcie.0") { + String::from("/pci@ffffffffffffffff") + } else { + // This else branch will not be executed currently, + // which is mainly to be compatible with new PCI bridge devices. + // unwrap is safe because pci bus under root port will not return null. + locked_parent_bus + .parent_bridge + .as_ref() + .unwrap() + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_dev_path() + .unwrap() + }; + parent_dev_path + } + + /// Fill the device patch accroding to parent device patch and device function. + fn populate_dev_path(&self, parent_dev_path: String, devfn: u8, dev_type: &str) -> String { + let mut dev_path = parent_dev_path; + dev_path.push_str(dev_type); + + let slot = pci_slot(devfn); + dev_path.push_str(&slot.to_string()); + + let function = pci_func(devfn); + if function != 0 { + dev_path.push(','); + dev_path.push_str(&function.to_string()); + } + + dev_path + } + + /// Get firmware device path. + fn get_dev_path(&self) -> Option { + None + } } /// Init multifunction for pci devices. @@ -220,7 +259,7 @@ pub trait PciDevOps: Send { /// * `parent_bus` - Parent bus of pci devices. pub fn init_multifunction( multifunction: bool, - config: &mut Vec, + config: &mut [u8], devfn: u8, parent_bus: Weak>, ) -> Result<()> { diff --git a/pci/src/msix.rs b/pci/src/msix.rs index cbf27ea1a..c3daa418c 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -14,8 +14,11 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{GuestAddress, Region, RegionOps}; +use error_chain::bail; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; use crate::config::{CapId, PciConfig, RegionType, SECONDARY_BUS_NUM}; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 3dc97dd4c..2e506fc7d 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -13,12 +13,14 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use once_cell::sync::OnceCell; - use address_space::Region; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{error, info}; +use machine_manager::event; use machine_manager::qmp::{qmp_schema as schema, QmpChannel}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use once_cell::sync::OnceCell; use util::byte_code::ByteCode; use super::config::{ @@ -425,6 +427,13 @@ impl PciDevOps for RootPort { Ok(()) } } + + fn get_dev_path(&self) -> Option { + let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_dev_path = self.get_parent_dev_path(parent_bus); + let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/pci-bridge@"); + Some(dev_path) + } } impl HotplugOps for RootPort { diff --git a/src/main.rs b/src/main.rs index 1b974ee82..d312ce165 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,15 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; - use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; +use error_chain::{bail, error_chain, quick_main}; +use log::{error, info}; use machine::{LightMachine, MachineOps, StdMachine}; use machine_manager::{ cmdline::{check_api_channel, create_args_parser, create_vmconfig}, diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 9b0999012..3dd0a4b52 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -10,10 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); @@ -29,10 +28,11 @@ use std::sync::{Arc, Mutex}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use error_chain::bail; use hypervisor::kvm::KVM_FDS; use vmm_sys_util::eventfd::EventFd; -use errors::{Result, ResultExt}; +use crate::errors::{Result, ResultExt}; pub struct SysBus { #[cfg(target_arch = "x86_64")] diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index eaac9059e..2e77901c6 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::Result; +use error_chain::bail; use kvm_bindings::__IncompleteArrayField; pub const IOCB_FLAG_RESFD: u32 = 1; @@ -104,7 +105,7 @@ impl LibaioContext { Ok(LibaioContext { ctx, max_size }) } - pub fn submit(&self, nr: i64, iocbp: &mut Vec<*mut IoCb>) -> Result<()> { + pub fn submit(&self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; if ret < 0 { bail!("Failed to submit aio, return {}.", ret); diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 27f6f0392..98422f9b0 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::Result; +use error_chain::bail; use libc::{c_void, fdatasync, pread, pwrite}; use std::os::unix::io::RawFd; diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 8b0336887..2b6ea5e75 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::cmp::Ord; use std::mem::size_of; diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 0c607b1ff..667d822d1 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -37,8 +37,9 @@ pub trait ByteCode: Default + Copy + Send + Sync { if data.len() != size_of::() { return None; } - let obj_array = unsafe { from_raw_parts::(data.as_ptr() as *const _, data.len()) }; - Some(&obj_array[0]) + + // SAFETY: The pointer is properly aligned and point to an initialized instance of T. + unsafe { data.as_ptr().cast::().as_ref() } } /// Creates an mutable object (impl trait `ByteCode`) from a mutable slice of bytes @@ -50,9 +51,9 @@ pub trait ByteCode: Default + Copy + Send + Sync { if data.len() != size_of::() { return None; } - let obj_array = - unsafe { from_raw_parts_mut::(data.as_mut_ptr() as *mut _, data.len()) }; - Some(&mut obj_array[0]) + + // SAFETY: The pointer is properly aligned and point to an initialized instance of T. + unsafe { data.as_mut_ptr().cast::().as_mut() } } } diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index d67d750a6..313e10997 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -14,6 +14,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::time::Instant; +use log::error; use vmm_sys_util::eventfd::EventFd; use crate::loop_context::EventLoopContext; diff --git a/util/src/lib.rs b/util/src/lib.rs index 2e2deae96..abbd7a211 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -10,14 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -extern crate libc; -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate vmm_sys_util; -#[macro_use] -extern crate log; - pub mod aio; pub mod arg_parser; pub mod bitmap; @@ -28,19 +20,20 @@ pub mod daemonize; pub mod device_tree; pub mod leak_bucket; mod link_list; +pub mod logger; pub mod loop_context; pub mod num_ops; +pub mod offsetof; pub mod reader; pub mod seccomp; +pub mod syscall; pub mod tap; -pub mod unix; -#[macro_use] -pub mod logger; -#[macro_use] -pub mod offsetof; pub mod trace; +pub mod unix; pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { KvmIoctl(kvm_ioctls::Error); @@ -156,6 +149,7 @@ pub mod errors { } use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; +use log::debug; use once_cell::sync::Lazy; use std::sync::Mutex; use vmm_sys_util::terminal::Terminal; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 2c9c8c0af..ce3aa5db4 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -10,14 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -extern crate vmm_sys_util; - use std::collections::BTreeMap; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; use libc::{c_void, read}; +use log::{error, warn}; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use crate::errors::{ErrorKind, Result, ResultExt}; diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index fd902eeed..2f09db691 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -10,7 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -//! This module implements some operations of Rust primitive types. +use log::error; +// This module implements some operations of Rust primitive types. /// Calculate the aligned-up u64 value. /// diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 40ab17eb9..2f1f0d19e 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -77,6 +77,8 @@ //! ``` //! This programe will be trapped. +use error_chain::bail; + use crate::errors::Result; use crate::offset_of; diff --git a/util/src/syscall.rs b/util/src/syscall.rs new file mode 100644 index 000000000..6294e09f9 --- /dev/null +++ b/util/src/syscall.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use error_chain::bail; +use libc::{c_void, syscall, SYS_mbind}; + +use super::errors::Result; + +/// This function set memory policy for host NUMA node memory range. +/// +/// * Arguments +/// +/// * `addr` - The memory range starting with addr. +/// * `len` - Length of the memory range. +/// * `mode` - Memory policy mode. +/// * `node_mask` - node_mask specifies physical node ID. +/// * `max_node` - The max node. +/// * `flags` - Mode flags. +pub fn mbind( + addr: u64, + len: u64, + mode: u32, + node_mask: Vec, + max_node: u64, + flags: u32, +) -> Result<()> { + let res = unsafe { + syscall( + SYS_mbind, + addr as *mut c_void, + len, + mode, + node_mask.as_ptr(), + max_node + 1, + flags, + ) + }; + if res < 0 { + bail!( + "Failed to apply host numa node policy, error is {}", + std::io::Error::last_os_error() + ); + } + + Ok(()) +} diff --git a/util/src/tap.rs b/util/src/tap.rs index 4154d522b..5bed417a7 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -10,11 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; +use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; use super::errors::{Result, ResultExt}; diff --git a/util/src/trace.rs b/util/src/trace.rs index 9a267d039..3f6748505 100644 --- a/util/src/trace.rs +++ b/util/src/trace.rs @@ -5,6 +5,7 @@ use std::ops::Deref; use std::sync::Arc; use arc_swap::ArcSwap; +use log::error; use once_cell::sync::Lazy; use crate::errors::{Result, ResultExt}; diff --git a/util/src/unix.rs b/util/src/unix.rs index c7d177282..7d2815296 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -16,10 +16,12 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; +use error_chain::bail; use libc::{ c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, CMSG_LEN, CMSG_SPACE, MSG_NOSIGNAL, MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, }; +use log::error; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 9787513ce..4ee431dbc 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -10,14 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate vmm_sys_util; - pub mod errors { + use error_chain::error_chain; + error_chain! { links { PciErr(pci::errors::Error, pci::errors::ErrorKind); @@ -53,6 +48,7 @@ use std::sync::{Arc, Mutex}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO}; use kvm_ioctls::DeviceFd; +use log::error; use once_cell::sync::Lazy; use vfio_dev::VfioGroup; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index db0c58df6..908d8a5f4 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -21,6 +21,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; use kvm_bindings::{ kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, }; @@ -28,6 +29,7 @@ use vfio_bindings::bindings::vfio; use vmm_sys_util::ioctl::{ ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, }; +use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index abc8f6c44..6680df29a 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -18,8 +18,9 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; #[cfg(target_arch = "aarch64")] use pci::config::SECONDARY_BUS_NUM; use pci::config::{ @@ -306,7 +307,7 @@ impl VfioPciDevice { Ok(vfio_bars) } - fn fixup_msix_region(&self, vfio_bars: &mut Vec) -> Result<()> { + fn fixup_msix_region(&self, vfio_bars: &mut [VfioBar]) -> Result<()> { let msix_info = self .msix_info .as_ref() diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index a2aa067c2..9379d4a39 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -22,9 +22,11 @@ use std::{ use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{error, warn}; use machine_manager::{ - config::BalloonConfig, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, qmp::QmpChannel, + config::BalloonConfig, event, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, + qmp::QmpChannel, }; use util::{ bitmap::Bitmap, diff --git a/virtio/src/block.rs b/virtio/src/block.rs index d65fd06c7..15c054d51 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,12 +21,14 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::error; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::aio::{Aio, AioCb, AioCompleteFunc, IoCmd, Iovec}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; diff --git a/virtio/src/console.rs b/virtio/src/console.rs index f758a84e9..b48a60b79 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -17,12 +17,14 @@ use std::{cmp, usize}; use address_space::AddressSpace; use devices::legacy::{Chardev, InputReceiver}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; +use log::{debug, error, warn}; use machine_manager::{ config::{ChardevType, VirtioConsole}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index cd06c832a..5af20f3f0 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -25,18 +25,9 @@ //! - `x86_64` //! - `aarch64` -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate machine_manager; -#[macro_use] -extern crate vmm_sys_util; -#[macro_use] -extern crate migration_derive; - pub mod errors { + use error_chain::error_chain; + error_chain! { foreign_links { Io(std::io::Error); @@ -131,6 +122,7 @@ pub use virtio_pci::VirtioPciDevice; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use error_chain::bail; use machine_manager::config::ConfigCheck; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index cbd843453..172528f93 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -18,11 +18,14 @@ use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; use address_space::AddressSpace; +use error_chain::bail; +use log::{error, warn}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 13409a875..0e5779c36 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -17,6 +17,8 @@ use std::sync::atomic::{fence, Ordering}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; +use error_chain::bail; +use log::{error, warn}; use util::byte_code::ByteCode; use super::errors::{ErrorKind, Result, ResultExt}; diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index ff87fc2d5..4b3770cc2 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -27,6 +27,9 @@ use util::loop_context::{ }; use util::num_ops::{read_u32, write_u32}; +use error_chain::bail; +use log::{error, warn}; +use migration_derive::{ByteCode, Desc}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index ccc85e957..39a7c2f23 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -24,6 +24,8 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; +use error_chain::ChainedError; +use log::{debug, error}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -31,11 +33,13 @@ use util::loop_context::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; +use vmm_sys_util::{ + ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, +}; use super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; use super::{VhostNotify, VhostOps}; -use crate::error_chain::ChainedError; /// Refer to VHOST_VIRTIO in /// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index ec5ee9f68..d1ceb7fac 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -17,6 +17,8 @@ use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use error_chain::bail; +use log::warn; use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 1489a97f3..f2dbeca8c 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -15,8 +15,11 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; +use log::warn; use machine_manager::{config::VsockConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index ea7a6223f..e76f0865f 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -17,6 +17,8 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; +use error_chain::bail; +use log::{info, warn}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 2e28dd1c6..9875af550 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -15,6 +15,7 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use log::warn; use machine_manager::config::NetworkInterfaceConfig; use util::byte_code::ByteCode; use util::num_ops::{read_u32, write_u32}; diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index 422e1f28a..9cbab183a 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -13,6 +13,7 @@ use std::mem::size_of; use std::os::unix::io::RawFd; +use error_chain::bail; use libc::{c_void, iovec}; use util::unix::UnixSock; diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index d85e889d6..6db9687bf 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -15,9 +15,12 @@ use std::sync::{Arc, Mutex}; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; +use error_chain::bail; +use log::{error, warn}; #[cfg(target_arch = "x86_64")] use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 3ffbd7b65..6e4f314e9 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -16,9 +16,11 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::ChainedError; +use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use pci::config::{ RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, @@ -1121,6 +1123,21 @@ impl PciDevOps for VirtioPciDevice { .reset() .chain_err(|| "Fail to reset virtio device") } + + fn get_dev_path(&self) -> Option { + let parent_bus = self.parent_bus.upgrade().unwrap(); + match self.device.lock().unwrap().device_type() { + VIRTIO_TYPE_BLOCK => { + // The virtio blk device is identified as a single-channel SCSI device, + // so add scsi controler indetification without channel, scsi-id and lun. + let parent_dev_path = self.get_parent_dev_path(parent_bus); + let mut dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); + dev_path.push_str("/disk@0,0"); + Some(dev_path) + } + _ => None, + } + } } impl StateTransfer for VirtioPciDevice { -- Gitee From 9f675d9c9d9b22877ceae3c481b784c90a7ef9d6 Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Thu, 16 Jun 2022 10:08:18 +0800 Subject: [PATCH 0066/2187] docs/config_guidebook: update the range of bootindex To match libvirt, the range of bootindex change to [0,255]. Update the doc file for better usage. Signed-off-by: Jiajie Li --- docs/config_guidebook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 5aa88e52c..014a95ef1 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -253,7 +253,7 @@ If not set, default is raw. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, the default block queue number is 1. * bootindex: the boot order of block device. (optional) If not set, the priority is lowest. -The number ranges from 1 to 255, the smaller the number, the higher the priority. +The number ranges from 0 to 255, the smaller the number, the higher the priority. It determines the order of bootable devices which firmware will use for booting the guest OS. For virtio-blk-pci, two more properties are required. -- Gitee From 267d061867ade99e9d279a68b53243ecc0577a9c Mon Sep 17 00:00:00 2001 From: uran0sH Date: Thu, 16 Jun 2022 20:45:33 +0800 Subject: [PATCH 0067/2187] Add ARM CPU Topology Support Users can use "-smp n,maxcpus=,sockets=,dies=,cores=,threads=" to configure the cpu topology on arm. Signed-off-by: uran0sH --- acpi/src/acpi_table.rs | 34 +++++ cpu/src/lib.rs | 36 +++++- docs/config_guidebook.md | 9 +- machine/src/micro_vm/mod.rs | 68 ++++------ machine/src/standard_vm/aarch64/mod.rs | 126 ++++++++++++------- machine/src/standard_vm/mod.rs | 26 ++++ machine/src/standard_vm/x86_64/mod.rs | 1 + machine_manager/src/config/machine_config.rs | 25 ++-- 8 files changed, 217 insertions(+), 108 deletions(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 308514fa1..c531da55f 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -178,6 +178,40 @@ impl AmlBuilder for AcpiTable { } } +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct ProcessorHierarchyNode { + pub r#type: u8, + pub length: u8, + pub reserved: u16, + pub flags: u32, + pub parent: u32, + pub acpi_processor_id: u32, + pub num_private_resources: u32, +} + +impl ByteCode for ProcessorHierarchyNode {} + +impl AmlBuilder for ProcessorHierarchyNode { + fn aml_bytes(&self) -> Vec { + self.as_bytes().to_vec() + } +} + +impl ProcessorHierarchyNode { + pub fn new(r#type: u8, flags: u32, parent: u32, acpi_processor_id: u32) -> Self { + Self { + r#type, + length: 20, + reserved: 0, + flags, + parent, + acpi_processor_id, + num_private_resources: 0, + } + } +} + /// ACPI RSDP structure. #[repr(C, packed)] #[derive(Default, Copy, Clone)] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index be310f7ec..d5d003bb9 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -706,6 +706,8 @@ pub struct CpuTopology { pub sockets: u8, /// Number of dies on one socket. pub dies: u8, + /// Number of clusters on one socket. + pub clusters: u8, /// Number of cores in VM. pub cores: u8, /// Number of threads in VM. @@ -728,6 +730,7 @@ impl CpuTopology { nr_threads: u8, nr_cores: u8, nr_dies: u8, + nr_clusters: u8, nr_sockets: u8, max_cpus: u8, ) -> Self { @@ -735,6 +738,7 @@ impl CpuTopology { Self { sockets: nr_sockets, dies: nr_dies, + clusters: nr_clusters, cores: nr_cores, threads: nr_threads, nrcpus: nr_cpus, @@ -765,7 +769,7 @@ impl CpuTopology { /// /// * `vcpu_id` - ID of vcpu. pub fn get_topo(&self, vcpu_id: usize) -> (u8, u8, u8) { - let socketid: u8 = vcpu_id as u8 / (self.dies * self.cores * self.threads); + let socketid: u8 = vcpu_id as u8 / (self.dies * self.clusters * self.cores * self.threads); let coreid: u8 = (vcpu_id as u8 / self.threads) % self.cores; let threadid: u8 = vcpu_id as u8 % self.threads; (socketid, coreid, threadid) @@ -941,6 +945,7 @@ mod tests { let microvm_cpu_topo = CpuTopology { sockets: test_nr_cpus, dies: 1, + clusters: 1, cores: 1, threads: 1, nrcpus: test_nr_cpus, @@ -954,9 +959,10 @@ mod tests { assert_eq!(microvm_cpu_topo.get_topo(15), (15, 0, 0)); let mask = Vec::with_capacity(test_nr_cpus as usize); - let microvm_cpu_topo1 = CpuTopology { + let microvm_cpu_topo_x86 = CpuTopology { sockets: 1, dies: 2, + clusters: 1, cores: 4, threads: 2, nrcpus: test_nr_cpus, @@ -964,16 +970,34 @@ mod tests { online_mask: Arc::new(Mutex::new(mask)), }; - assert_eq!(microvm_cpu_topo1.get_topo(0), (0, 0, 0)); - assert_eq!(microvm_cpu_topo1.get_topo(4), (0, 2, 0)); - assert_eq!(microvm_cpu_topo1.get_topo(8), (0, 0, 0)); - assert_eq!(microvm_cpu_topo1.get_topo(15), (0, 3, 1)); + assert_eq!(microvm_cpu_topo_x86.get_topo(0), (0, 0, 0)); + assert_eq!(microvm_cpu_topo_x86.get_topo(4), (0, 2, 0)); + assert_eq!(microvm_cpu_topo_x86.get_topo(8), (0, 0, 0)); + assert_eq!(microvm_cpu_topo_x86.get_topo(15), (0, 3, 1)); + + let mask = Vec::with_capacity(test_nr_cpus as usize); + let microvm_cpu_topo_arm = CpuTopology { + sockets: 1, + dies: 1, + clusters: 2, + cores: 4, + threads: 2, + nrcpus: test_nr_cpus, + max_cpus: test_nr_cpus, + online_mask: Arc::new(Mutex::new(mask)), + }; + + assert_eq!(microvm_cpu_topo_arm.get_topo(0), (0, 0, 0)); + assert_eq!(microvm_cpu_topo_arm.get_topo(4), (0, 2, 0)); + assert_eq!(microvm_cpu_topo_arm.get_topo(8), (0, 0, 0)); + assert_eq!(microvm_cpu_topo_arm.get_topo(15), (0, 3, 1)); let test_nr_cpus: u8 = 32; let mask = Vec::with_capacity(test_nr_cpus as usize); let test_cpu_topo = CpuTopology { sockets: 2, dies: 1, + clusters: 1, cores: 4, threads: 2, nrcpus: test_nr_cpus, diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 999e43e61..706f3242a 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -31,19 +31,20 @@ By default, after booted, VM will online all CPUs you set. Four properties are supported for `smp`. * cpus: the number of VCPUs. * maxcpus: the number of max VCPUs. -* sockets: the number of socket. (optional). If not set, its value depends on the value of `maxcpus`. +* sockets: the number of socket. (optional). If not set, its value depends on the value of `maxcpus`. On the arm machine, if you start a microvm, the value of socket must be one so far. * dies: the number of dies. (optional). If not set, default is one. +* clusters: the number of clusters. (optional). If not set, default is one. * cores: the number of core. (optional). If not set, its value depends on the value of `maxcpus`. * threads: the number of thread. (optional). If not set, its value depends on the value of `maxcpus`. -NB: the arguments of cpu topology is used to interconnect with libvirt. The x86 cpu topology has supported. But the ARM cpu topology of StratoVirt is not supported yet. Therefore, it is better to ignore these three arguments (sockets, cores, threads). +NB: the arguments of cpu topology is used to interconnect with libvirt. -If it is configured, sockets * dies * cores * threads must be equal to maxcpus, and maxcpus should be larger than cpus. +If it is configured, sockets * dies * clusters * cores * threads must be equal to maxcpus, and maxcpus should be larger than or equal to cpus. ```shell # cmdline --smp [cpus=]n[,maxcpus=,sockets=,dies=,cores=,threads=] +-smp [cpus=]n[,maxcpus=,sockets=,dies=,clusters=,cores=,threads=] ``` ### 1.3 Memory diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d655af024..c6cf8b633 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -235,6 +235,7 @@ impl LightMachine { vm_config.machine_config.nr_threads, vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_clusters, vm_config.machine_config.nr_sockets, vm_config.machine_config.max_cpus, ), @@ -1416,56 +1417,39 @@ impl CompileFDTHelper for LightMachine { fdt.set_property_u32("#size-cells", 0x0)?; // Generate CPU topology - if self.cpu_topo.max_cpus > 0 && self.cpu_topo.max_cpus % 8 == 0 { - let cpu_map_node_dep = fdt.begin_node("cpu-map")?; - - let sockets = self.cpu_topo.max_cpus / 8; - for cluster in 0..u32::from(sockets) { + let cpu_map_node_dep = fdt.begin_node("cpu-map")?; + for socket in 0..self.cpu_topo.sockets { + let sock_name = format!("cluster{}", socket); + let sock_node_dep = fdt.begin_node(&sock_name)?; + for cluster in 0..self.cpu_topo.clusters { let clster = format!("cluster{}", cluster); let cluster_node_dep = fdt.begin_node(&clster)?; - for i in 0..2_u32 { - let sub_cluster = format!("cluster{}", i); - let sub_cluster_node_dep = fdt.begin_node(&sub_cluster)?; - - let core0 = "core0".to_string(); - let core0_node_dep = fdt.begin_node(&core0)?; - - let thread0 = "thread0".to_string(); - let thread0_node_dep = fdt.begin_node(&thread0)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10)?; - fdt.end_node(thread0_node_dep)?; - - let thread1 = "thread1".to_string(); - let thread1_node_dep = fdt.begin_node(&thread1)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10 + 1)?; - fdt.end_node(thread1_node_dep)?; - - fdt.end_node(core0_node_dep)?; - - let core1 = "core1".to_string(); - let core1_node_dep = fdt.begin_node(&core1)?; - - let thread0 = "thread0".to_string(); - let thread0_node_dep = fdt.begin_node(&thread0)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10 + 2)?; - fdt.end_node(thread0_node_dep)?; - - let thread1 = "thread1".to_string(); - let thread1_node_dep = fdt.begin_node(&thread1)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10 + 3)?; - fdt.end_node(thread1_node_dep)?; - - fdt.end_node(core1_node_dep)?; - - fdt.end_node(sub_cluster_node_dep)?; + for core in 0..self.cpu_topo.cores { + let core_name = format!("core{}", core); + let core_node_dep = fdt.begin_node(&core_name)?; + + for thread in 0..self.cpu_topo.threads { + let thread_name = format!("thread{}", thread); + let thread_node_dep = fdt.begin_node(&thread_name)?; + let vcpuid = self.cpu_topo.threads * self.cpu_topo.cores * cluster + + self.cpu_topo.threads * core + + thread; + fdt.set_property_u32( + "cpu", + u32::from(vcpuid) + device_tree::CPU_PHANDLE_START, + )?; + fdt.end_node(thread_node_dep)?; + } + fdt.end_node(core_node_dep)?; } fdt.end_node(cluster_node_dep)?; } - fdt.end_node(cpu_map_node_dep)?; + fdt.end_node(sock_node_dep)?; } + fdt.end_node(cpu_map_node_dep)?; - for cpu_index in 0..self.cpu_topo.max_cpus { + for cpu_index in 0..self.cpu_topo.nrcpus { let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); let node = format!("cpu@{:x}", mpidr); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index e96e49d96..16aa56778 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -21,12 +21,13 @@ use std::sync::{Arc, Condvar, Mutex}; use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, - AmlScopeBuilder, AmlString, TableLoader, ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, - ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, - ACPI_GTDT_CAP_ALWAYS_ON, ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, - ACPI_IORT_NODE_PCI_ROOT_COMPLEX, ACPI_MADT_GENERIC_CPU_INTERFACE, - ACPI_MADT_GENERIC_DISTRIBUTOR, ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, - ARCH_GIC_MAINT_IRQ, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, + AmlScopeBuilder, AmlString, ProcessorHierarchyNode, TableLoader, + ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, + ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, ACPI_GTDT_CAP_ALWAYS_ON, + ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, + ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, + ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, + INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -147,6 +148,7 @@ impl StdMachine { vm_config.machine_config.nr_threads, vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_clusters, vm_config.machine_config.nr_sockets, vm_config.machine_config.max_cpus, ); @@ -836,6 +838,51 @@ impl AcpiBuilder for StdMachine { .chain_err(|| "Fail to add SRAT table to loader")?; Ok(srat_begin as u64) } + + fn build_pptt_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::errors::Result { + use super::errors::ResultExt; + let mut pptt = AcpiTable::new(*b"PPTT", 2, *b"STRATO", *b"VIRTPPTT", 1); + let pptt_start = 0; + let mut uid = 0; + for socket in 0..self.cpu_topo.sockets { + let socket_offset = pptt.table_len() - pptt_start; + let socket_hierarchy_node = ProcessorHierarchyNode::new(0, 0x2, 0, socket as u32); + pptt.append_child(&socket_hierarchy_node.aml_bytes()); + for cluster in 0..self.cpu_topo.clusters { + let cluster_offset = pptt.table_len() - pptt_start; + let cluster_hierarchy_node = + ProcessorHierarchyNode::new(0, 0x0, socket_offset as u32, cluster as u32); + pptt.append_child(&cluster_hierarchy_node.aml_bytes()); + for core in 0..self.cpu_topo.cores { + let core_offset = pptt.table_len() - pptt_start; + if self.cpu_topo.threads > 1 { + let core_hierarchy_node = + ProcessorHierarchyNode::new(0, 0x0, cluster_offset as u32, core as u32); + pptt.append_child(&core_hierarchy_node.aml_bytes()); + for _thread in 0..self.cpu_topo.threads { + let thread_hierarchy_node = + ProcessorHierarchyNode::new(0, 0xE, core_offset as u32, uid as u32); + pptt.append_child(&thread_hierarchy_node.aml_bytes()); + uid += 1; + } + } else { + let thread_hierarchy_node = + ProcessorHierarchyNode::new(0, 0xA, cluster_offset as u32, uid as u32); + pptt.append_child(&thread_hierarchy_node.aml_bytes()); + uid += 1; + } + } + } + } + pptt.set_table_len(pptt.table_len()); + let pptt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &pptt) + .chain_err(|| "Fail to add PPTT table to loader")?; + Ok(pptt_begin as u64) + } } impl MachineLifecycle for StdMachine { @@ -1160,56 +1207,39 @@ impl CompileFDTHelper for StdMachine { fdt.set_property_u32("#size-cells", 0x0)?; // Generate CPU topology - if self.cpu_topo.max_cpus > 0 && self.cpu_topo.max_cpus % 8 == 0 { - let cpu_map_node_dep = fdt.begin_node("cpu-map")?; - - let sockets = self.cpu_topo.max_cpus / 8; - for cluster in 0..u32::from(sockets) { + let cpu_map_node_dep = fdt.begin_node("cpu-map")?; + for socket in 0..self.cpu_topo.sockets { + let sock_name = format!("cluster{}", socket); + let sock_node_dep = fdt.begin_node(&sock_name)?; + for cluster in 0..self.cpu_topo.clusters { let clster = format!("cluster{}", cluster); let cluster_node_dep = fdt.begin_node(&clster)?; - for i in 0..2_u32 { - let sub_cluster = format!("cluster{}", i); - let sub_cluster_node_dep = fdt.begin_node(&sub_cluster)?; - - let core0 = "core0".to_string(); - let core0_node_dep = fdt.begin_node(&core0)?; - - let thread0 = "thread0".to_string(); - let thread0_node_dep = fdt.begin_node(&thread0)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10)?; - fdt.end_node(thread0_node_dep)?; - - let thread1 = "thread1".to_string(); - let thread1_node_dep = fdt.begin_node(&thread1)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10 + 1)?; - fdt.end_node(thread1_node_dep)?; - - fdt.end_node(core0_node_dep)?; - - let core1 = "core1".to_string(); - let core1_node_dep = fdt.begin_node(&core1)?; - - let thread0 = "thread0".to_string(); - let thread0_node_dep = fdt.begin_node(&thread0)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10 + 2)?; - fdt.end_node(thread0_node_dep)?; - - let thread1 = "thread1".to_string(); - let thread1_node_dep = fdt.begin_node(&thread1)?; - fdt.set_property_u32("cpu", cluster * 8 + i * 4 + 10 + 3)?; - fdt.end_node(thread1_node_dep)?; - - fdt.end_node(core1_node_dep)?; - - fdt.end_node(sub_cluster_node_dep)?; + for core in 0..self.cpu_topo.cores { + let core_name = format!("core{}", core); + let core_node_dep = fdt.begin_node(&core_name)?; + + for thread in 0..self.cpu_topo.threads { + let thread_name = format!("thread{}", thread); + let thread_node_dep = fdt.begin_node(&thread_name)?; + let vcpuid = self.cpu_topo.threads * self.cpu_topo.cores * cluster + + self.cpu_topo.threads * core + + thread; + fdt.set_property_u32( + "cpu", + u32::from(vcpuid) + device_tree::CPU_PHANDLE_START, + )?; + fdt.end_node(thread_node_dep)?; + } + fdt.end_node(core_node_dep)?; } fdt.end_node(cluster_node_dep)?; } - fdt.end_node(cpu_map_node_dep)?; + fdt.end_node(sock_node_dep)?; } + fdt.end_node(cpu_map_node_dep)?; - for cpu_index in 0..self.cpu_topo.max_cpus { + for cpu_index in 0..self.cpu_topo.nrcpus { let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); let node = format!("cpu@{:x}", mpidr); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 80d16d2bc..091fa94bf 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -167,6 +167,14 @@ trait StdMachineOps: AcpiBuilder { xsdt_entries.push(slit_addr); } + #[cfg(target_arch = "aarch64")] + { + let pptt_addr = self + .build_pptt_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI PPTT table")?; + xsdt_entries.push(pptt_addr); + } + let xsdt_addr = Self::build_xsdt_table(&acpi_tables, &mut loader, xsdt_entries)?; let mut locked_fw_cfg = fw_cfg.lock().unwrap(); @@ -388,6 +396,24 @@ trait AcpiBuilder { Ok(0) } + /// Build ACPI PPTT table, returns the offset of ACPI PPTT table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `Loader` - ACPI table loader. + #[cfg(target_arch = "aarch64")] + fn build_pptt_table( + &self, + _acpi_data: &Arc>>, + _loader: &mut TableLoader, + ) -> Result + where + Self: Sized, + { + Ok(0) + } + /// Build ACPI MCFG table, returns the offset of ACPI MCFG table in `acpi_data`. /// /// # Arguments diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 9313035b6..1940d6cca 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -131,6 +131,7 @@ impl StdMachine { vm_config.machine_config.nr_threads, vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_clusters, vm_config.machine_config.nr_sockets, vm_config.machine_config.max_cpus, ); diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index a99b2fc38..2382e230c 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -22,6 +22,7 @@ const DEFAULT_CPUS: u8 = 1; const DEFAULT_THREADS: u8 = 1; const DEFAULT_CORES: u8 = 1; const DEFAULT_DIES: u8 = 1; +const DEFAULT_CLUSTERS: u8 = 1; const DEFAULT_SOCKETS: u8 = 1; const DEFAULT_MAX_CPUS: u8 = 1; const DEFAULT_MEMSIZE: u64 = 256; @@ -129,6 +130,7 @@ pub struct MachineConfig { pub nr_threads: u8, pub nr_cores: u8, pub nr_dies: u8, + pub nr_clusters: u8, pub nr_sockets: u8, pub max_cpus: u8, pub mem_config: MachineMemConfig, @@ -143,6 +145,7 @@ impl Default for MachineConfig { nr_threads: DEFAULT_THREADS, nr_cores: DEFAULT_CORES, nr_dies: DEFAULT_DIES, + nr_clusters: DEFAULT_CLUSTERS, nr_sockets: DEFAULT_SOCKETS, max_cpus: DEFAULT_MAX_CPUS, mem_config: MachineMemConfig::default(), @@ -247,6 +250,7 @@ impl VmConfig { .push("maxcpus") .push("sockets") .push("dies") + .push("clusters") .push("cores") .push("threads") .push("cpus"); @@ -270,6 +274,8 @@ impl VmConfig { let dies = cmd_parser.get_value::("dies")?.unwrap_or(1); + let clusters = cmd_parser.get_value::("clusters")?.unwrap_or(1); + let cores = cmd_parser.get_value::("cores")?.unwrap_or_default(); let threads = cmd_parser.get_value::("threads")?.unwrap_or_default(); @@ -277,7 +283,7 @@ impl VmConfig { let max_cpus = cmd_parser.get_value::("maxcpus")?.unwrap_or_default(); let (max_cpus, sockets, cores, threads) = - adjust_topology(cpu, max_cpus, sockets, dies, cores, threads); + adjust_topology(cpu, max_cpus, sockets, dies, clusters, cores, threads); // limit cpu count if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&cpu) { @@ -291,12 +297,12 @@ impl VmConfig { .into()); } - if max_cpus < cpu || sockets * dies * cores * threads != max_cpus { + if max_cpus < cpu || sockets * dies * clusters * cores * threads != max_cpus { return Err(ErrorKind::IllegalValue( "maxcpus".to_string(), cpu as u64, true, - (sockets * dies * cores * threads) as u64, + (sockets * dies * clusters * cores * threads) as u64, true, ) .into()); @@ -306,6 +312,7 @@ impl VmConfig { self.machine_config.nr_threads = threads as u8; self.machine_config.nr_cores = cores as u8; self.machine_config.nr_dies = dies as u8; + self.machine_config.nr_clusters = clusters as u8; self.machine_config.nr_sockets = sockets as u8; self.machine_config.max_cpus = max_cpus as u8; @@ -387,12 +394,13 @@ fn adjust_topology( mut max_cpus: u64, mut sockets: u64, dies: u64, + clusters: u64, mut cores: u64, mut threads: u64, ) -> (u64, u64, u64, u64) { if max_cpus == 0 { - if sockets * dies * cores * threads > 0 { - max_cpus = sockets * dies * cores * threads; + if sockets * dies * clusters * cores * threads > 0 { + max_cpus = sockets * dies * clusters * cores * threads; } else { max_cpus = cpu; } @@ -405,16 +413,16 @@ fn adjust_topology( if threads == 0 { threads = 1; } - cores = max_cpus / (sockets * dies * threads); + cores = max_cpus / (sockets * dies * clusters * threads); } else if sockets == 0 { if threads == 0 { threads = 1; } - sockets = max_cpus / (dies * cores * threads); + sockets = max_cpus / (dies * clusters * cores * threads); } if threads == 0 { - threads = max_cpus / (sockets * dies * cores); + threads = max_cpus / (sockets * dies * clusters * cores); } (max_cpus, sockets, cores, threads) @@ -491,6 +499,7 @@ mod tests { nr_cores: 1, nr_threads: 1, nr_dies: 1, + nr_clusters: 1, nr_sockets: 1, max_cpus: MIN_NR_CPUS as u8, mem_config: memory_config, -- Gitee From 3d19fc51074df11de60ebe901ce128efba452421 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:10:13 +0800 Subject: [PATCH 0068/2187] guest NUMA: limit the max number of NuMA nodes to 8 Signed-off-by: Xinle.Guo --- machine/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index eaf5b00b3..5b90abf13 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -807,6 +807,13 @@ pub trait MachineOps { return Ok(None); } + if vm_config.numa_nodes.len() > 8 { + bail!( + "NUMA nodes should be less than or equal to 8, now is {}", + vm_config.numa_nodes.len() + ); + } + let mut numa_nodes: NumaNodes = BTreeMap::new(); vm_config.numa_nodes.sort_by(|p, n| n.0.cmp(&p.0)); for numa in vm_config.numa_nodes.iter() { -- Gitee From a4274ebc19b03f43cc7e9ed5255cc887fc8a4065 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:21:25 +0800 Subject: [PATCH 0069/2187] guest NUMA: limit the distance of remote node should be greater than 10 Signed-off-by: Xinle.Guo --- machine_manager/src/config/numa.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index c97938817..0a90f5ce1 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -161,15 +161,21 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { if val < MIN_NUMA_DISTANCE { bail!("NUMA distance shouldn't be less than 10"); } + if numa_id == dist.destination && val != MIN_NUMA_DISTANCE { + bail!("Local distance of node {} should be 10.", numa_id); + } + if numa_id != dist.destination && val == MIN_NUMA_DISTANCE { + bail!( + "Remote distance of node {} should be more than 10.", + numa_id + ); + } + dist.distance = val; } else { return Err(ErrorKind::FieldIsMissing("val", "numa").into()); } - if numa_id == dist.destination && dist.distance != MIN_NUMA_DISTANCE { - bail!("Local distance of node {} should be 10.", numa_id); - } - Ok((numa_id, dist)) } -- Gitee From d635ebecfdc6f1d2064c2eb2ac10c9e4033b01df Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Wed, 22 Jun 2022 21:26:37 +0800 Subject: [PATCH 0070/2187] standard_vm/aarch64: adjust the logic of build acpi table Acpi table need to be builded after fdt table generation, otherwise the multi queue attribute will be affected. Fix it by repalce the build_acpi_tables function. Signed-off-by: Jiajie Li --- machine/src/standard_vm/aarch64/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index cd6af51a8..09560c5e4 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -461,9 +461,6 @@ impl MachineOps for StdMachine { let fwcfg = locked_vm.add_fwcfg_device()?; let boot_config = if !is_migrate { - locked_vm - .build_acpi_tables(&fwcfg) - .chain_err(|| "Failed to create ACPI tables")?; Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { None @@ -498,6 +495,12 @@ impl MachineOps for StdMachine { .chain_err(|| ErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } + if !is_migrate { + locked_vm + .build_acpi_tables(&fwcfg) + .chain_err(|| "Failed to create ACPI tables")?; + } + locked_vm.register_power_event(&locked_vm.power_button)?; if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { -- Gitee From 56146c8cab0e2fab2a159ad79e65653a9696482a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:52:40 +0800 Subject: [PATCH 0071/2187] net: fix the problem that fails to hot plug net with multi queue It will be failed if hot plug a net with multi queue. The reason is incorrect parsing queue number from `String` type. Signed-off-by: Xinle.Guo --- machine_manager/src/config/network.rs | 2 +- machine_manager/src/qmp/qmp_schema.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index fa32d368b..46644d09d 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -325,7 +325,7 @@ pub fn get_netdev_config(args: Box) -> Result, pub downscript: Option, pub script: Option, - pub queues: Option, + pub queues: Option, } pub type NetDevAddArgument = netdev_add; -- Gitee From 063b32f490856c786d00cae5ecbb6de4c543f980 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 20:02:35 +0800 Subject: [PATCH 0072/2187] net/block: fix the problem that fails to unplug devices with multi queue. If we unplug a net/block device with multi queue, it will cause VM panic. The reason is eventfds is not cloned. After the first queue eventfd is freed from stratovirt, the system can not find eventfd resource, and it triggers panic. Signed-off-by: Xinle.Guo --- virtio/src/block.rs | 19 +++++++++++-------- virtio/src/net.rs | 24 +++++++++++------------- virtio/src/vhost/kernel/mod.rs | 6 +++--- virtio/src/vhost/kernel/net.rs | 2 +- virtio/src/vhost/kernel/vsock.rs | 4 ++-- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 15c054d51..65adc30ff 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -359,9 +359,9 @@ struct BlockIoHandler { /// The receiving half of Rust's channel to receive the image file. receiver: Receiver, /// Eventfd for config space update. - update_evt: RawFd, + update_evt: EventFd, /// Eventfd for device deactivate. - deactivate_evt: RawFd, + deactivate_evt: EventFd, /// Callback to trigger an interrupt. interrupt_cb: Arc, /// thread name of io handler @@ -634,14 +634,14 @@ impl BlockIoHandler { let mut notifiers = vec![ EventNotifier::new( NotifierOperation::Delete, - self.update_evt, + self.update_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), ), EventNotifier::new( NotifierOperation::Delete, - self.deactivate_evt, + self.deactivate_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), @@ -699,7 +699,7 @@ impl EventNotifierHelper for BlockIoHandler { h_clone.lock().unwrap().update_evt_handler(); None }); - notifiers.push(build_event_notifier(handler_raw.update_evt, h)); + notifiers.push(build_event_notifier(handler_raw.update_evt.as_raw_fd(), h)); // Register event notifier for deactivate_evt. let h_clone = handler.clone(); @@ -707,7 +707,10 @@ impl EventNotifierHelper for BlockIoHandler { read_fd(fd); Some(h_clone.lock().unwrap().deactivate_evt_handler()) }); - notifiers.push(build_event_notifier(handler_raw.deactivate_evt, h)); + notifiers.push(build_event_notifier( + handler_raw.deactivate_evt.as_raw_fd(), + h, + )); // Register event notifier for queue_evt. let h_clone = handler.clone(); @@ -1115,8 +1118,8 @@ impl VirtioDevice for Block { aio: None, driver_features: self.state.driver_features, receiver, - update_evt: self.update_evt.as_raw_fd(), - deactivate_evt: self.deactivate_evt.as_raw_fd(), + update_evt: self.update_evt.try_clone().unwrap(), + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 172528f93..57c2aff74 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -241,8 +241,8 @@ struct NetIoHandler { interrupt_cb: Arc, driver_features: u64, receiver: Receiver, - update_evt: RawFd, - deactivate_evt: RawFd, + update_evt: EventFd, + deactivate_evt: EventFd, is_listening: bool, } @@ -392,7 +392,7 @@ impl NetIoHandler { let mut notifiers = vec![ build_event_notifier( - locked_net_io.update_evt, + locked_net_io.update_evt.as_raw_fd(), None, NotifierOperation::Delete, EventSet::IN, @@ -428,14 +428,14 @@ impl NetIoHandler { let mut notifiers = vec![ EventNotifier::new( NotifierOperation::Delete, - self.update_evt, + self.update_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), ), EventNotifier::new( NotifierOperation::Delete, - self.deactivate_evt, + self.deactivate_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), @@ -492,14 +492,12 @@ impl EventNotifierHelper for NetIoHandler { read_fd(fd); Some(NetIoHandler::update_evt_handler(&cloned_net_io)) }); - let mut notifiers = Vec::new(); - let update_fd = locked_net_io.update_evt; - notifiers.push(build_event_notifier( - update_fd, + let mut notifiers = vec![build_event_notifier( + locked_net_io.update_evt.as_raw_fd(), Some(handler), NotifierOperation::AddShared, EventSet::IN, - )); + )]; // Register event notifier for deactivate_evt. let cloned_net_io = net_io.clone(); @@ -508,7 +506,7 @@ impl EventNotifierHelper for NetIoHandler { Some(cloned_net_io.lock().unwrap().deactivate_evt_handler()) }); notifiers.push(build_event_notifier( - locked_net_io.deactivate_evt, + locked_net_io.deactivate_evt.as_raw_fd(), Some(handler), NotifierOperation::AddShared, EventSet::IN, @@ -945,8 +943,8 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, receiver, - update_evt: self.update_evt.as_raw_fd(), - deactivate_evt: self.deactivate_evt.as_raw_fd(), + update_evt: self.update_evt.try_clone().unwrap(), + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), is_listening: true, }; if let Some(tap) = &handler.tap { diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 39a7c2f23..fa4a79851 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -423,7 +423,7 @@ impl VhostOps for VhostBackend { pub struct VhostIoHandler { interrupt_cb: Arc, host_notifies: Vec, - deactivate_evt: RawFd, + deactivate_evt: EventFd, } impl VhostIoHandler { @@ -441,7 +441,7 @@ impl VhostIoHandler { notifiers.push(EventNotifier::new( NotifierOperation::Delete, - self.deactivate_evt, + self.deactivate_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), @@ -496,7 +496,7 @@ impl EventNotifierHelper for VhostIoHandler { }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - vhost_handler.lock().unwrap().deactivate_evt, + vhost_handler.lock().unwrap().deactivate_evt.as_raw_fd(), None, EventSet::IN, vec![Arc::new(Mutex::new(handler))], diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index d1ceb7fac..49bb2d562 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -357,7 +357,7 @@ impl VirtioDevice for Net { let handler = VhostIoHandler { interrupt_cb: interrupt_cb.clone(), host_notifies, - deactivate_evt: self.deactivate_evt.as_raw_fd(), + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), }; EventLoop::update_event( diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index f2dbeca8c..23d1df332 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -299,7 +299,7 @@ impl VirtioDevice for Vsock { let handler = VhostIoHandler { interrupt_cb, host_notifies, - deactivate_evt: self.deactivate_evt.as_raw_fd(), + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), }; EventLoop::update_event( -- Gitee From cc5a43cdd065d166907f8d2c4250b772fa5c0067 Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 24 Jun 2022 21:36:53 +0800 Subject: [PATCH 0073/2187] Fix some cargo clippy warn 1. Reimplement the SocketStream structure using the newtype pattern 2. some dead code to clear Signed-off-by: Yan Wen --- cpu/src/x86_64/mod.rs | 1 - machine/src/micro_vm/mem_layout.rs | 2 -- machine/src/standard_vm/aarch64/mod.rs | 1 - machine/src/standard_vm/x86_64/mod.rs | 1 - machine_manager/src/config/rng.rs | 1 - machine_manager/src/machine.rs | 8 ++++---- machine_manager/src/socket.rs | 21 +++++++++------------ ozone/src/main.rs | 1 - util/src/aio/libaio.rs | 1 - util/src/arg_parser.rs | 16 +++++++++------- virtio/src/block.rs | 1 - 11 files changed, 22 insertions(+), 32 deletions(-) diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 173e566e9..fb38461e5 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -250,7 +250,6 @@ impl X86CPUState { Ok(()) } - #[allow(clippy::cast_ptr_alignment)] fn setup_lapic(&mut self, vcpu_fd: &Arc) -> Result<()> { // Disable nmi and external interrupt before enter protected mode // See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/apicdef.h diff --git a/machine/src/micro_vm/mem_layout.rs b/machine/src/micro_vm/mem_layout.rs index c9d6f56f5..b17310fc7 100644 --- a/machine/src/micro_vm/mem_layout.rs +++ b/machine/src/micro_vm/mem_layout.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. /// The type of memory layout entry on aarch64 -#[allow(dead_code)] #[cfg(target_arch = "aarch64")] #[repr(usize)] pub enum LayoutEntryType { @@ -41,7 +40,6 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ ]; /// The type of memory layout entry on x86_64 -#[allow(dead_code)] #[cfg(target_arch = "x86_64")] #[repr(usize)] pub enum LayoutEntryType { diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 09560c5e4..7c0bb781d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -65,7 +65,6 @@ use pci_host_root::PciHostRoot; use syscall::syscall_whitelist; /// The type of memory layout entry on aarch64 -#[allow(dead_code)] pub enum LayoutEntryType { Flash = 0, GicDist, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 4c5dc3925..e5302ee03 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -69,7 +69,6 @@ const HOLE_640K_END: u64 = 0x0010_0000; /// The type of memory layout entry on x86_64 #[repr(usize)] -#[allow(dead_code)] pub enum LayoutEntryType { MemBelow4g = 0_usize, PcieEcam, diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index e1caff629..60554e380 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -114,7 +114,6 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result Response { let mut vec_types = Vec::new(); // These devices are used to interconnect with libvirt, but not been implemented yet. - #[allow(unused_mut)] - let mut list_types: Vec<(&str, &str)> = vec![ + let list_types: Vec<(&str, &str)> = vec![ ("ioh3420", "pcie-root-port-base"), ("pcie-root-port", "pcie-root-port-base"), ("pcie-pci-bridge", "base-pci-bridge"), @@ -286,9 +285,10 @@ pub trait DeviceInterface { ("vfio-pci", "pci-device"), ("vhost-vsock-device", "virtio-device"), ("iothread", "object"), + #[cfg(target_arch = "aarch64")] + ("gpex-pcihost", "pcie-host-bridge"), ]; - #[cfg(target_arch = "aarch64")] - list_types.push(("gpex-pcihost", "pcie-host-bridge")); + for list in list_types { let re = TypeLists::new(String::from(list.0), String::from(list.1)); vec_types.push(re); diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index aa31717f6..169b1a302 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -137,7 +137,7 @@ impl Socket { /// Get socket fd from `Socket`, it a private function. pub fn get_stream_fd(&self) -> RawFd { if self.is_connected() { - self.stream.read().unwrap().as_ref().unwrap().socket_fd + self.stream.read().unwrap().as_ref().unwrap().as_raw_fd() } else { panic!("Failed to get socket fd!"); } @@ -295,20 +295,17 @@ pub enum SocketType { /// Wrapper over UnixSteam. #[derive(Debug)] -struct SocketStream { - /// `RawFd` for socket - socket_fd: RawFd, - /// Make `UnixStream` persistent without `drop` - #[allow(dead_code)] - persistent: Option, -} +struct SocketStream(UnixStream); impl SocketStream { fn from_unix_stream(stream: UnixStream) -> Self { - SocketStream { - socket_fd: stream.as_raw_fd(), - persistent: Some(stream), - } + SocketStream(stream) + } +} + +impl AsRawFd for SocketStream { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() } } diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 27507878b..71fad2a49 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -49,7 +49,6 @@ quick_main!(run); fn run() -> Result<()> { let args = create_args_parser().get_matches()?; - #[allow(unused_variables)] let handler = OzoneHandler::new(&args).chain_err(|| "Failed to parse cmdline args")?; if args.is_present("clean_resource") { diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 2e77901c6..8ee3e1987 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -114,7 +114,6 @@ impl LibaioContext { Ok(()) } - #[allow(clippy::zero_ptr)] pub fn get_events(&self) -> (&[IoEvent], u32, u32) { let ring = self.ctx as *mut AioRing; let head = unsafe { (*ring).head }; diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index 48ba49370..c8dd4f8b2 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::cmp::PartialEq; +use std::collections::btree_map::Entry; use std::collections::BTreeMap; use std::env; use std::io::Write; @@ -587,7 +588,6 @@ impl<'a> ArgMatches<'a> { } } -#[allow(clippy::map_entry)] fn parse_cmdline( cmd_args: &[String], allow_list: &[String], @@ -605,19 +605,21 @@ fn parse_cmdline( if cmd_arg.starts_with(PREFIX_CHARS_LONG) { let arg_str = split_arg(cmd_arg, PREFIX_CHARS_LONG); - if arg_map.contains_key(&arg_str) { - multi_vec.push(arg_str); + + if let Entry::Vacant(e) = arg_map.entry(arg_str.clone()) { + e.insert(Vec::new()); } else { - arg_map.insert(arg_str, Vec::new()); + multi_vec.push(arg_str); } i = (j, PREFIX_CHARS_LONG); } else if cmd_arg.starts_with(PREFIX_CHARS_SHORT) { let arg_str = split_arg(cmd_arg, PREFIX_CHARS_SHORT); - if arg_map.contains_key(&arg_str) { - multi_vec.push(arg_str); + + if let Entry::Vacant(e) = arg_map.entry(arg_str.clone()) { + e.insert(Vec::new()); } else { - arg_map.insert(arg_str, Vec::new()); + multi_vec.push(arg_str); } i = (j, PREFIX_CHARS_SHORT); } else { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 15c054d51..2a4b136c0 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -229,7 +229,6 @@ impl Request { } #[allow(clippy::too_many_arguments)] - #[allow(clippy::borrowed_box)] fn execute( &self, aio: &mut Box>, -- Gitee From 246a099a7e82886378f3bbc2067344788c957154 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 20 Jun 2022 19:32:11 +0800 Subject: [PATCH 0074/2187] guest NUMA: enhance the parameters for NUMA cpus and host nodes From this patch, it can set numa node cmd as `-numa node,nodeid=0,cpus=0-1:4-5,memdev=mem0`, cpus can be set to multiple groups. Host nodes can also be set as multiple groups like `host-nodes=1-2`. Signed-off-by: Xinle.Guo --- address_space/src/host_mmap.rs | 32 +++-- docs/config_guidebook.md | 10 +- machine/src/lib.rs | 16 ++- machine_manager/src/config/machine_config.rs | 99 ++++++++++----- machine_manager/src/config/mod.rs | 6 +- machine_manager/src/config/numa.rs | 119 ++++++++++++++----- 6 files changed, 195 insertions(+), 87 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index d2ef05690..f8e7af0e0 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -303,26 +303,34 @@ pub fn set_host_memory_policy( let mut host_addr_start = mem_mappings.get(0).map(|m| m.host_address()).unwrap(); for zone in mem_zones.as_ref().unwrap() { - let mut node_id = if let Some(id) = zone.host_numa_node { - id as usize - } else { - 0_usize - }; + if zone.host_numa_nodes.is_none() { + continue; + } + + let nodes = zone.host_numa_nodes.as_ref().unwrap(); + let mut max_node = nodes[nodes.len() - 1] as usize; + + let mut nmask: Vec = Vec::new(); + nmask.resize(max_node / 64 + 1, 0); + for node in nodes.iter() { + nmask[(*node / 64) as usize] |= 1_u64 << (*node % 64); + } + // We need to pass node_id + 1 as mbind() max_node argument. + // It is kind of linux bug or feature which will cut off the last node. + max_node += 1; - let mut nmask = vec![0_u64; node_id / 64 + 1]; - nmask[node_id / 64] |= 1_u64 << (node_id % 64); let policy = HostMemPolicy::from(zone.policy.clone()); - if policy != HostMemPolicy::Default { - // We need to pass node_id + 1 as mbind() max_node argument. - // It is kind of linux bug or feature which will cut off the last node. - node_id += 1; + if policy == HostMemPolicy::Default { + max_node = 0; + nmask = vec![0_u64; max_node]; } + mbind( host_addr_start, zone.size, policy as u32, nmask, - node_id as u64, + max_node as u64, MPOL_MF_STRICT | MPOL_MF_MOVE, ) .chain_err(|| "Failed to call mbind")?; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index d00a6d7ff..a56933121 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -125,15 +125,15 @@ The following command shows how to set NUMA node: ```shell # The number of cpu must be set to be the same as numa node cpu. --smp 4 +-smp 8 # The memory size must be set to be the same as numa node mem. -m 4G --object memory-backend-ram,size=2G,id=mem0,[host-nodes=0,policy=bind] --object memory-backend-ram,size=2G,id=mem1,[host-nodes=1,policy=bind] --numa node,nodeid=0,cpus=0-1,memdev=mem0 --numa node,nodeid=1,cpus=2-3,memdev=mem1 +-object memory-backend-ram,size=2G,id=mem0,[host-nodes=0-1,policy=bind] +-object memory-backend-ram,size=2G,id=mem1,[host-nodes=0-1,policy=bind] +-numa node,nodeid=0,cpus=0-1:4-5,memdev=mem0 +-numa node,nodeid=1,cpus=2-3:6-7,memdev=mem1 [-numa dist,src=0,dst=0,val=10] [-numa dist,src=0,dst=1,val=20] [-numa dist,src=1,dst=0,val=20] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5b90abf13..8b83547ab 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -122,7 +122,7 @@ use error_chain::bail; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::VcpuFd; use machine_manager::config::{ - check_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, + complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, MachineMemConfig, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, @@ -807,13 +807,6 @@ pub trait MachineOps { return Ok(None); } - if vm_config.numa_nodes.len() > 8 { - bail!( - "NUMA nodes should be less than or equal to 8, now is {}", - vm_config.numa_nodes.len() - ); - } - let mut numa_nodes: NumaNodes = BTreeMap::new(); vm_config.numa_nodes.sort_by(|p, n| n.0.cmp(&p.0)); for numa in vm_config.numa_nodes.iter() { @@ -865,7 +858,12 @@ pub trait MachineOps { } } - check_numa_node(&numa_nodes, vm_config)?; + // Complete user parameters if necessary. + complete_numa_node( + &mut numa_nodes, + vm_config.machine_config.nr_cpus, + vm_config.machine_config.mem_config.mem_size, + )?; Ok(Some(numa_nodes)) } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 2382e230c..07f223e6f 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -16,7 +16,9 @@ use error_chain::bail; use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result, ResultExt}; -use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_NODES, MAX_STRING_LENGTH}; +use crate::config::{ + CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES, MAX_STRING_LENGTH, +}; const DEFAULT_CPUS: u8 = 1; const DEFAULT_THREADS: u8 = 1; @@ -82,7 +84,7 @@ impl From for HostMemPolicy { pub struct MemZoneConfig { pub id: String, pub size: u64, - pub host_numa_node: Option, + pub host_numa_nodes: Option>, pub policy: String, } @@ -91,7 +93,7 @@ impl Default for MemZoneConfig { MemZoneConfig { id: String::new(), size: 0, - host_numa_node: None, + host_numa_nodes: None, policy: String::from("bind"), } } @@ -327,35 +329,41 @@ impl VmConfig { pub fn enable_mem_prealloc(&mut self) { self.machine_config.mem_config.mem_prealloc = true; } +} - pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { - let mut cmd_parser = CmdParser::new("mem_zone"); - cmd_parser - .push("") - .push("id") - .push("size") - .push("host-nodes") - .push("policy"); - cmd_parser.parse(mem_zone)?; - - let mut zone_config = MemZoneConfig::default(); +impl VmConfig { + fn get_mem_zone_id(&self, cmd_parser: &CmdParser) -> Result { if let Some(id) = cmd_parser.get_value::("id")? { if id.len() > MAX_STRING_LENGTH { return Err( ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into(), ); } - zone_config.id = id; + Ok(id) } else { - return Err(ErrorKind::FieldIsMissing("id", "memory-backend-ram").into()); + Err(ErrorKind::FieldIsMissing("id", "memory-backend-ram").into()) } + } + + fn get_mem_zone_size(&self, cmd_parser: &CmdParser) -> Result { if let Some(mem) = cmd_parser.get_value::("size")? { - zone_config.size = memory_unit_conversion(&mem)?; + let size = memory_unit_conversion(&mem)?; + Ok(size) } else { - return Err(ErrorKind::FieldIsMissing("size", "memory-backend-ram").into()); + Err(ErrorKind::FieldIsMissing("size", "memory-backend-ram").into()) } - if let Some(host_nodes) = cmd_parser.get_value::("host-nodes")? { - if host_nodes >= MAX_NODES { + } + + fn get_mem_zone_host_nodes(&self, cmd_parser: &CmdParser) -> Result>> { + if let Some(mut host_nodes) = cmd_parser + .get_value::("host-nodes") + .map_err(|_| { + ErrorKind::ConvertValueFailed(String::from("u32"), "host-nodes".to_string()) + })? + .map(|v| v.0.iter().map(|e| *e as u32).collect::>()) + { + host_nodes.sort_unstable(); + if host_nodes[host_nodes.len() - 1] >= MAX_NODES { return Err(ErrorKind::IllegalValue( "host_nodes".to_string(), 0, @@ -365,14 +373,44 @@ impl VmConfig { ) .into()); } - zone_config.host_numa_node = Some(host_nodes); + Ok(Some(host_nodes)) + } else { + Err(ErrorKind::FieldIsMissing("host-nodes", "memory-backend-ram").into()) } + } + + fn get_mem_zone_policy(&self, cmd_parser: &CmdParser) -> Result { if let Some(policy) = cmd_parser.get_value::("policy")? { if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { return Err(ErrorKind::InvalidParam("policy".to_string(), policy).into()); } - zone_config.policy = policy; + Ok(policy) + } else { + Err(ErrorKind::FieldIsMissing("policy", "memory-backend-ram").into()) } + } + + /// Convert memory zone cmdline to VM config + /// + /// # Arguments + /// + /// * `mem_zone` - The memory zone cmdline string. + pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { + let mut cmd_parser = CmdParser::new("mem_zone"); + cmd_parser + .push("") + .push("id") + .push("size") + .push("host-nodes") + .push("policy"); + cmd_parser.parse(mem_zone)?; + + let zone_config = MemZoneConfig { + id: self.get_mem_zone_id(&cmd_parser)?, + size: self.get_mem_zone_size(&cmd_parser)?, + host_numa_nodes: self.get_mem_zone_host_nodes(&cmd_parser)?, + policy: self.get_mem_zone_policy(&cmd_parser)?, + }; if self.machine_config.mem_config.mem_zones.is_some() { self.machine_config @@ -828,21 +866,26 @@ mod tests { .unwrap(); assert_eq!(zone_config_1.id, "mem1"); assert_eq!(zone_config_1.size, 2147483648); - assert_eq!(zone_config_1.host_numa_node, Some(1)); + assert_eq!(zone_config_1.host_numa_nodes, Some(vec![1])); assert_eq!(zone_config_1.policy, "bind"); - let zone_config_2 = vm_config + assert!(vm_config .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1") - .unwrap(); - assert_eq!(zone_config_2.host_numa_node, None); - assert_eq!(zone_config_2.policy, "bind"); - + .is_err()); assert!(vm_config .add_mem_zone("-object memory-backend-ram,size=2G") .is_err()); assert!(vm_config .add_mem_zone("-object memory-backend-ram,id=mem1") .is_err()); + + let mut vm_config = VmConfig::default(); + let zone_config_2 = vm_config + .add_mem_zone( + "-object memory-backend-ram,size=2G,id=mem1,host-nodes=1-2,policy=default", + ) + .unwrap(); + assert_eq!(zone_config_2.host_numa_nodes, Some(vec![1, 2])); } #[test] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 554b42b33..cda22e2e0 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -516,7 +516,11 @@ impl FromStr for IntegerList { fn from_str(s: &str) -> std::result::Result { let mut integer_list = Vec::new(); - let lists: Vec<&str> = s.trim().split(',').collect(); + let lists: Vec<&str> = s + .trim() + .trim_matches(|c| c == '[' || c == ']') + .split(':') + .collect(); for list in lists.iter() { let items: Vec<&str> = list.split('-').collect(); if items.len() > 2 { diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 0a90f5ce1..fb0ceda16 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -10,7 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::collections::BTreeMap; +use std::cmp::max; +use std::collections::{BTreeMap, HashSet}; use error_chain::bail; @@ -43,41 +44,75 @@ pub struct NumaNode { pub type NumaNodes = BTreeMap; -pub fn check_numa_node(numa_nodes: &NumaNodes, vm_config: &mut VmConfig) -> Result<()> { +/// Complete the NUMA node parameters from user. +/// +/// # Arguments +/// +/// * `numa_nodes` - The NUMA node information parsing from user. +/// * `nr_cpus` - The VM cpus number. +/// * `mem_size` - The VM memory size. +pub fn complete_numa_node(numa_nodes: &mut NumaNodes, nr_cpus: u8, mem_size: u64) -> Result<()> { + if numa_nodes.len() > 8 { + bail!( + "NUMA nodes should be less than or equal to 8, now is {}", + numa_nodes.len() + ); + } + let mut total_ram_size = 0_u64; - let mut total_cpu_num = 0_u8; - let mut max_cpu_idx = 0_u8; - for node in numa_nodes.iter() { - total_ram_size += node.1.size; - total_cpu_num += node.1.cpus.len() as u8; - for idx in &node.1.cpus { - if max_cpu_idx <= *idx { - max_cpu_idx = *idx; + let mut max_cpu_id = 0_u8; + let mut cpus_id = HashSet::::new(); + for (_, node) in numa_nodes.iter() { + total_ram_size += node.size; + for id in node.cpus.iter() { + if cpus_id.contains(id) { + bail!("CPU id {} is repeat, please check it again", *id); } + cpus_id.insert(*id); + max_cpu_id = max(max_cpu_id, *id); } } - if total_ram_size != vm_config.machine_config.mem_config.mem_size { + if cpus_id.len() < nr_cpus as usize { + if let Some(node_0) = numa_nodes.get_mut(&0) { + for id in 0..nr_cpus { + if !cpus_id.contains(&id) { + node_0.cpus.push(id); + } + } + } + } + + if total_ram_size != mem_size { bail!( "Total memory {} of NUMA nodes is not equals to memory size {}", total_ram_size, - vm_config.machine_config.mem_config.mem_size, + mem_size, ); } - if total_cpu_num != vm_config.machine_config.nr_cpus { + if max_cpu_id >= nr_cpus { bail!( - "Total cpu numbers {} of NUMA nodes is not equals to smp {}", - total_cpu_num, - vm_config.machine_config.nr_cpus, + "CPU index {} should be smaller than max cpu {}", + max_cpu_id, + nr_cpus ); } - if max_cpu_idx != vm_config.machine_config.nr_cpus - 1 { - bail!("Error to configure CPU sets, please check you cmdline again"); + if cpus_id.len() > nr_cpus as usize { + bail!( + "Total cpu numbers {} of NUMA nodes should be less than or equals to smp {}", + cpus_id.len(), + nr_cpus + ); } Ok(()) } +/// Parse the NUMA node memory parameters. +/// +/// # Arguments +/// +/// * `numa_config` - The NUMA node configuration. pub fn parse_numa_mem(numa_config: &str) -> Result { let mut cmd_parser = CmdParser::new("numa"); cmd_parser @@ -103,11 +138,12 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { } else { return Err(ErrorKind::FieldIsMissing("nodeid", "numa").into()); } - if let Some(cpus) = cmd_parser + if let Some(mut cpus) = cmd_parser .get_value::("cpus") - .map_err(|_| ErrorKind::ConvertValueFailed(String::from("u64"), "cpus".to_string()))? - .map(|v| v.0.iter().map(|e| *e as u8).collect()) + .map_err(|_| ErrorKind::ConvertValueFailed(String::from("u8"), "cpus".to_string()))? + .map(|v| v.0.iter().map(|e| *e as u8).collect::>()) { + cpus.sort_unstable(); config.cpus = cpus; } else { return Err(ErrorKind::FieldIsMissing("cpus", "numa").into()); @@ -121,6 +157,11 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { Ok(config) } +/// Parse the NUMA node distance parameters. +/// +/// # Arguments +/// +/// * `numa_dist` - The NUMA node distance configuration. pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { let mut cmd_parser = CmdParser::new("numa"); cmd_parser.push("").push("src").push("dst").push("val"); @@ -180,6 +221,11 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { } impl VmConfig { + /// Add the NUMA node config to vm config. + /// + /// # Arguments + /// + /// * `numa_config` - The NUMA node configuration. pub fn add_numa(&mut self, numa_config: &str) -> Result<()> { let mut cmd_params = CmdParser::new("numa"); cmd_params.push(""); @@ -210,6 +256,9 @@ mod tests { .add_numa("-numa node,nodeid=2,memdev=mem2") .is_ok()); assert!(vm_config.add_numa("-numa node,nodeid=3,cpus=3-4").is_ok()); + assert!(vm_config + .add_numa("-numa node,nodeid=0,cpus=[0-1:3-5],memdev=mem0") + .is_ok()); let numa = vm_config.numa_nodes.get(0).unwrap(); let numa_config = parse_numa_mem(numa.1.as_str()).unwrap(); @@ -222,21 +271,26 @@ mod tests { assert!(parse_numa_mem(numa.1.as_str()).is_err()); let numa = vm_config.numa_nodes.get(3).unwrap(); assert!(parse_numa_mem(numa.1.as_str()).is_err()); + + let numa = vm_config.numa_nodes.get(4).unwrap(); + let numa_config = parse_numa_mem(numa.1.as_str()).unwrap(); + assert_eq!(numa_config.cpus, vec![0, 1, 3, 4, 5]); } #[test] fn test_parse_numa_distance() { let mut vm_config = VmConfig::default(); - assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=15").is_ok()); assert!(vm_config.add_numa("-numa dist,dst=1,val=10").is_ok()); assert!(vm_config.add_numa("-numa dist,src=0,val=10").is_ok()); assert!(vm_config.add_numa("-numa dist,src=0,dst=1").is_ok()); + assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=10").is_ok()); let numa = vm_config.numa_nodes.get(0).unwrap(); let dist = parse_numa_distance(numa.1.as_str()).unwrap(); assert_eq!(dist.0, 0); assert_eq!(dist.1.destination, 1); - assert_eq!(dist.1.distance, 10); + assert_eq!(dist.1.distance, 15); let numa = vm_config.numa_nodes.get(1).unwrap(); assert!(parse_numa_distance(numa.1.as_str()).is_err()); @@ -244,13 +298,14 @@ mod tests { assert!(parse_numa_distance(numa.1.as_str()).is_err()); let numa = vm_config.numa_nodes.get(3).unwrap(); assert!(parse_numa_distance(numa.1.as_str()).is_err()); + let numa = vm_config.numa_nodes.get(4).unwrap(); + assert!(parse_numa_distance(numa.1.as_str()).is_err()); } #[test] fn test_check_numa_nodes() { - let mut vm_config = VmConfig::default(); - vm_config.machine_config.nr_cpus = 4; - vm_config.machine_config.mem_config.mem_size = 2147483648; + let nr_cpus = 4; + let mem_size = 2147483648; let numa_node1 = NumaNode { cpus: vec![0, 1], @@ -266,7 +321,7 @@ mod tests { let mut numa_nodes = BTreeMap::new(); numa_nodes.insert(0, numa_node1); numa_nodes.insert(1, numa_node2); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_ok()); + assert!(complete_numa_node(&mut numa_nodes, nr_cpus, mem_size).is_ok()); let numa_node3 = NumaNode { cpus: vec![2], @@ -275,7 +330,7 @@ mod tests { }; numa_nodes.remove(&1); numa_nodes.insert(2, numa_node3); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + assert!(complete_numa_node(&mut numa_nodes, nr_cpus, mem_size).is_ok()); let numa_node4 = NumaNode { cpus: vec![2, 3, 4], @@ -284,7 +339,7 @@ mod tests { }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node4); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + assert!(complete_numa_node(&mut numa_nodes, nr_cpus, mem_size).is_err()); let numa_node5 = NumaNode { cpus: vec![3, 4], @@ -293,7 +348,7 @@ mod tests { }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node5); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + assert!(complete_numa_node(&mut numa_nodes, nr_cpus, mem_size).is_err()); let numa_node6 = NumaNode { cpus: vec![0, 1], @@ -302,7 +357,7 @@ mod tests { }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node6); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + assert!(complete_numa_node(&mut numa_nodes, nr_cpus, mem_size).is_err()); let numa_node7 = NumaNode { cpus: vec![2, 3], @@ -311,6 +366,6 @@ mod tests { }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node7); - assert!(check_numa_node(&numa_nodes, &mut vm_config).is_err()); + assert!(complete_numa_node(&mut numa_nodes, nr_cpus, mem_size).is_err()); } } -- Gitee From 113b6b8a9127c30297b4f853cbeeef27b81b6588 Mon Sep 17 00:00:00 2001 From: louquuan18 <2710830093@qq.com> Date: Thu, 30 Jun 2022 15:07:18 +0800 Subject: [PATCH 0075/2187] docs:Add the missing '\' in the code Signed-off-by: louquuan18 <2710830093@qq.com> --- docs/quickstart.ch.md | 2 +- docs/quickstart.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.ch.md b/docs/quickstart.ch.md index 29e1496a2..7280d4309 100644 --- a/docs/quickstart.ch.md +++ b/docs/quickstart.ch.md @@ -77,7 +77,7 @@ rm -f ${socket_path} # 通过StratoVirt启动轻量化机型的Linux客户机。 /usr/bin/stratovirt \ - -machine microvm + -machine microvm \ -kernel ${kernel_path} \ -smp 1 \ -m 1024 \ diff --git a/docs/quickstart.md b/docs/quickstart.md index add534cb2..c207be288 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -80,7 +80,7 @@ rm -f ${socket_path} # Start linux VM with machine type "microvm" by StratoVirt. /usr/bin/stratovirt \ - -machine microvm + -machine microvm \ -kernel ${kernel_path} \ -smp 1 \ -m 1024 \ -- Gitee From 55ae6ca69898c67051b5cb4c89c07e8997237977 Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Thu, 30 Jun 2022 14:05:40 +0800 Subject: [PATCH 0076/2187] tests_microvm_cpu_features: fix_test_128vcpu_topo bug Due to the newly added CPU topology feature, the guest CPU topology information changes, and the test cases need to be modified accordingly. Signed-off-by: Zuo Xiaoxian --- .../microvm/functional/test_microvm_cpu_features.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py index 9be504329..ab80d50d8 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py @@ -136,10 +136,7 @@ def test_128vcpu_topo(microvm): test_vm.basic_config(vcpu_count=128) test_vm.launch() - if 'x86_64' in platform.machine(): - _check_cpu_topology(test_vm, 128, 1, 128, "0-127") - else: - _check_cpu_topology(test_vm, 128, 2, 2, "0-127") + _check_cpu_topology(test_vm, 128, 1, 128, "0-127") @pytest.mark.skipif("platform.machine().startswith('aarch64')") -- Gitee From 4cb546e062bede8f9d5e8136b20bc54ac321a123 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 12 Jul 2022 20:53:15 +0800 Subject: [PATCH 0077/2187] micro_vm: update seccomp rules When you run the query-cpus QMP command in the ARM of the micro vm, a bad system call is displayed. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 2 +- machine/src/micro_vm/syscall.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a56933121..26d446dd6 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -647,7 +647,7 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | -| microvm | 44 | 45 | +| microvm | 45 | 45 | | virt | 51 | 50 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 1e5c2bb19..9814444ed 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -50,7 +50,7 @@ const KVM_RUN: u32 = 0xae80; /// This allowlist limit syscall with: /// * x86_64-unknown-gnu: 43 syscalls /// * x86_64-unknown-musl: 43 syscalls -/// * aarch64-unknown-gnu: 41 syscalls +/// * aarch64-unknown-gnu: 42 syscalls /// * aarch64-unknown-musl: 42 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -108,7 +108,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_stat), #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] BpfRule::new(libc::SYS_newfstatat), - #[cfg(all(target_env = "musl", target_arch = "aarch64"))] + #[cfg(target_arch = "aarch64")] BpfRule::new(libc::SYS_newfstatat), #[cfg(target_arch = "x86_64")] BpfRule::new(libc::SYS_unlink), @@ -179,12 +179,12 @@ fn ioctl_arch_allow_list(bpf_rule: BpfRule) -> BpfRule { } fn madvise_rule() -> BpfRule { - #[cfg(all(target_env = "musl", target_arch = "x86_64"))] + #[cfg(target_env = "musl")] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32); - #[cfg(not(all(target_env = "musl", target_arch = "x86_64")))] + #[cfg(not(target_env = "musl"))] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32); -- Gitee From 32a952ff468bf3945006c35f653d4f5d3540fd0a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 5 Jul 2022 14:26:46 +0800 Subject: [PATCH 0078/2187] machine: fix the wrong arch type when query cpus The CPU information does not distinguish the x86 architecture from the ARM architecture. As a result, when the qmp command is executed in the ARM architecture, x86 is returned for the arch field. Signed-off-by: zhouli57 --- machine/src/micro_vm/mod.rs | 28 ++++++++--------- machine/src/standard_vm/mod.rs | 20 ++++++++++-- machine_manager/src/qmp/qmp_schema.rs | 44 ++++++++++++--------------- 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c6cf8b633..28b647144 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -935,17 +935,20 @@ impl DeviceInterface for LightMachine { core_id: Some(coreid as isize), thread_id: Some(threadid as isize), }; + let cpu_common = qmp_schema::CpuInfoCommon { + current: true, + qom_path: String::from("/machine/unattached/device[") + + &cpu_index.to_string() + + "]", + halted: false, + props: Some(cpu_instance), + CPU: cpu_index as isize, + thread_id: thread_id as isize, + }; #[cfg(target_arch = "x86_64")] { let cpu_info = qmp_schema::CpuInfo::x86 { - current: true, - qom_path: String::from("/machine/unattached/device[") - + &cpu_index.to_string() - + "]", - halted: false, - props: Some(cpu_instance), - CPU: cpu_index as isize, - thread_id: thread_id as isize, + common: cpu_common, x86: qmp_schema::CpuInfoX86 {}, }; cpu_vec.push(serde_json::to_value(cpu_info).unwrap()); @@ -953,14 +956,7 @@ impl DeviceInterface for LightMachine { #[cfg(target_arch = "aarch64")] { let cpu_info = qmp_schema::CpuInfo::Arm { - current: true, - qom_path: String::from("/machine/unattached/device[") - + &cpu_index.to_string() - + "]", - halted: false, - props: Some(cpu_instance), - CPU: cpu_index as isize, - thread_id: thread_id as isize, + common: cpu_common, arm: qmp_schema::CpuInfoArm {}, }; cpu_vec.push(serde_json::to_value(cpu_info).unwrap()); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 3b99327c2..e71822a71 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -930,7 +930,7 @@ impl DeviceInterface for StdMachine { core_id: Some(coreid as isize), thread_id: Some(threadid as isize), }; - let cpu_info = qmp_schema::CpuInfo::x86 { + let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") + &cpu_index.to_string() @@ -939,9 +939,23 @@ impl DeviceInterface for StdMachine { props: Some(cpu_instance), CPU: cpu_index as isize, thread_id: thread_id as isize, - x86: qmp_schema::CpuInfoX86 {}, }; - cpu_vec.push(serde_json::to_value(cpu_info).unwrap()); + #[cfg(target_arch = "x86_64")] + { + let cpu_info = qmp_schema::CpuInfo::x86 { + common: cpu_common, + x86: qmp_schema::CpuInfoX86 {}, + }; + cpu_vec.push(serde_json::to_value(cpu_info).unwrap()); + } + #[cfg(target_arch = "aarch64")] + { + let cpu_info = qmp_schema::CpuInfo::Arm { + common: cpu_common, + arm: qmp_schema::CpuInfoArm {}, + }; + cpu_vec.push(serde_json::to_value(cpu_info).unwrap()); + } } } Response::create_response(cpu_vec.into(), None) diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index bee2b7951..19b1a3513 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -804,41 +804,37 @@ impl Command for query_cpus { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CpuInfoCommon { + #[serde(rename = "current")] + pub current: bool, + #[serde(rename = "qom_path")] + pub qom_path: String, + #[serde(rename = "halted")] + pub halted: bool, + #[serde(rename = "props", default, skip_serializing_if = "Option::is_none")] + pub props: Option, + #[serde(rename = "CPU")] + pub CPU: isize, + #[serde(rename = "thread_id")] + pub thread_id: isize, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "arch")] pub enum CpuInfo { #[serde(rename = "x86")] x86 { - #[serde(rename = "current")] - current: bool, - #[serde(rename = "qom_path")] - qom_path: String, - #[serde(rename = "halted")] - halted: bool, - #[serde(rename = "props", default, skip_serializing_if = "Option::is_none")] - props: Option, - #[serde(rename = "CPU")] - CPU: isize, - #[serde(rename = "thread_id")] - thread_id: isize, + #[serde(flatten)] + common: CpuInfoCommon, #[serde(flatten)] #[serde(rename = "x86")] x86: CpuInfoX86, }, #[serde(rename = "arm")] Arm { - #[serde(rename = "current")] - current: bool, - #[serde(rename = "qom_path")] - qom_path: String, - #[serde(rename = "halted")] - halted: bool, - #[serde(rename = "props", default, skip_serializing_if = "Option::is_none")] - props: Option, - #[serde(rename = "CPU")] - CPU: isize, - #[serde(rename = "thread_id")] - thread_id: isize, + #[serde(flatten)] + common: CpuInfoCommon, #[serde(flatten)] #[serde(rename = "Arm")] arm: CpuInfoArm, -- Gitee From 65baa1ad633dbe83b672b4d0329707a4c8a240ea Mon Sep 17 00:00:00 2001 From: Jiajie Li Date: Wed, 13 Jul 2022 18:22:55 +0800 Subject: [PATCH 0079/2187] legacy/pflash: modify the flag of mmap syscall If you want to synchronize the memory modifications of the mmap map to a file, you need to enable the flag bit MAP_SHARED. This essentially depends on whether the file is writable or not. Signed-off-by: Jiajie Li --- devices/src/legacy/pflash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 6b12749c4..2e5f185df 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -214,8 +214,8 @@ impl PFlash { region_size, backend.map(FileBackend::new_common), false, - false, - false, + true, + self.read_only, )?); let dev = Arc::new(Mutex::new(self)); -- Gitee From a16e71f46a9ef295c955f0244456e820be3f6010 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Mon, 4 Jul 2022 10:52:40 +0800 Subject: [PATCH 0080/2187] Fix problems of the cpu topology. 1. The value of sockets, dies, clusters, cores, threads must be larger than 0. 2. The value of maxcpus should be less than the max value of cpus. 3. The vcpuid in fdt is calculated with sockets, clusters, cores, threads. 4. Fix the out-of-index of mask array when use 'query-cpus' command. Signed-off-by: uran0sH --- cpu/src/lib.rs | 5 ++- machine/src/micro_vm/mod.rs | 6 ++- machine_manager/src/config/machine_config.rs | 44 ++++++++++++++++---- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index d5d003bb9..dbc30fe0d 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -734,7 +734,10 @@ impl CpuTopology { nr_sockets: u8, max_cpus: u8, ) -> Self { - let mask: Vec = vec![1; nr_cpus as usize]; + let mut mask: Vec = vec![0; max_cpus as usize]; + (0..nr_cpus as usize).for_each(|index| { + mask[index] = 1; + }); Self { sockets: nr_sockets, dies: nr_dies, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c6cf8b633..2ed33a064 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1432,7 +1432,11 @@ impl CompileFDTHelper for LightMachine { for thread in 0..self.cpu_topo.threads { let thread_name = format!("thread{}", thread); let thread_node_dep = fdt.begin_node(&thread_name)?; - let vcpuid = self.cpu_topo.threads * self.cpu_topo.cores * cluster + let vcpuid = self.cpu_topo.threads + * self.cpu_topo.cores + * self.cpu_topo.clusters + * socket + + self.cpu_topo.threads * self.cpu_topo.cores * cluster + self.cpu_topo.threads * core + thread; fdt.set_property_u32( diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 07f223e6f..6dc75b2cc 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -264,7 +264,7 @@ impl VmConfig { } else if let Some(cpu) = cmd_parser.get_value::("cpus")? { if cpu == 0 { return Err( - ErrorKind::IllegalValue("cpu".to_string(), 1, true, u64::MAX, true).into(), + ErrorKind::IllegalValue("cpu".to_string(), 1, true, MAX_NR_CPUS, true).into(), ); } cpu @@ -272,15 +272,15 @@ impl VmConfig { return Err(ErrorKind::FieldIsMissing("cpus", "smp").into()); }; - let sockets = cmd_parser.get_value::("sockets")?.unwrap_or_default(); + let sockets = smp_read_and_check(&cmd_parser, "sockets", 0)?; - let dies = cmd_parser.get_value::("dies")?.unwrap_or(1); + let dies = smp_read_and_check(&cmd_parser, "dies", 1)?; - let clusters = cmd_parser.get_value::("clusters")?.unwrap_or(1); + let clusters = smp_read_and_check(&cmd_parser, "clusters", 1)?; - let cores = cmd_parser.get_value::("cores")?.unwrap_or_default(); + let cores = smp_read_and_check(&cmd_parser, "cores", 0)?; - let threads = cmd_parser.get_value::("threads")?.unwrap_or_default(); + let threads = smp_read_and_check(&cmd_parser, "threads", 0)?; let max_cpus = cmd_parser.get_value::("maxcpus")?.unwrap_or_default(); @@ -299,17 +299,32 @@ impl VmConfig { .into()); } - if max_cpus < cpu || sockets * dies * clusters * cores * threads != max_cpus { + if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&max_cpus) { + return Err(ErrorKind::IllegalValue( + "MAX CPU number".to_string(), + MIN_NR_CPUS, + true, + MAX_NR_CPUS, + true, + ) + .into()); + } + + if max_cpus < cpu { return Err(ErrorKind::IllegalValue( "maxcpus".to_string(), cpu as u64, true, - (sockets * dies * clusters * cores * threads) as u64, + MAX_NR_CPUS, true, ) .into()); } + if sockets * dies * clusters * cores * threads != max_cpus { + bail!("sockets * dies * clusters * cores * threads must be equal to max_cpus"); + } + self.machine_config.nr_cpus = cpu as u8; self.machine_config.nr_threads = threads as u8; self.machine_config.nr_cores = cores as u8; @@ -427,6 +442,19 @@ impl VmConfig { } } +fn smp_read_and_check(cmd_parser: &CmdParser, name: &str, default_val: u64) -> Result { + if let Some(values) = cmd_parser.get_value::(name)? { + if values == 0 { + return Err( + ErrorKind::IllegalValue(name.to_string(), 1, true, u8::MAX as u64, false).into(), + ); + } + Ok(values) + } else { + Ok(default_val) + } +} + fn adjust_topology( cpu: u64, mut max_cpus: u64, -- Gitee From fd137b970e3e1dbc4b9d16b3e769acce1d2c6953 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 14 Jul 2022 16:31:14 +0800 Subject: [PATCH 0081/2187] machine: update seccomp rules Fixed the issue that the VM fails to be started using the gnu version on x86. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 4 ++-- machine/src/micro_vm/syscall.rs | 4 +++- machine/src/standard_vm/x86_64/syscall.rs | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 26d446dd6..6af47e0f8 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -640,8 +640,8 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | -| microvm | 46 | 46 | -| q35 | 52 | 54 | +| microvm | 47 | 46 | +| q35 | 53 | 54 | * aarch64 diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 9814444ed..0290980af 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -48,7 +48,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 43 syscalls +/// * x86_64-unknown-gnu: 44 syscalls /// * x86_64-unknown-musl: 43 syscalls /// * aarch64-unknown-gnu: 42 syscalls /// * aarch64-unknown-musl: 42 syscalls @@ -118,6 +118,8 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_mkdir), #[cfg(target_arch = "aarch64")] BpfRule::new(libc::SYS_mkdirat), + #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] + BpfRule::new(libc::SYS_readlink), madvise_rule(), ] } diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 70a2e1ec2..81fdb6dbf 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,7 +55,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 49 syscalls +/// * x86_64-unknown-gnu: 50 syscalls /// * x86_64-unknown-musl: 51 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -118,7 +118,6 @@ pub fn syscall_whitelist() -> Vec { madvise_rule(), BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), - #[cfg(target_env = "musl")] BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_socket), BpfRule::new(libc::SYS_connect), -- Gitee From 47279cc7c548c42a817bba95c20527981e2aff1e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 12 Jul 2022 15:28:09 +0800 Subject: [PATCH 0082/2187] vhost-user-net: add multi-queue support Support multi-queue in vhost-user net to improve performance. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 11 +++-- pci/src/msix.rs | 22 ++++++++- virtio/src/vhost/user/net.rs | 74 +++++++++++++++++++++------- virtio/src/virtio_pci.rs | 93 ++++++++++++++++++++++++++++++++---- 4 files changed, 168 insertions(+), 32 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 26d446dd6..435fec186 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -332,13 +332,14 @@ given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatica ``` StratoVirt also supports vhost-user net to get a higher performance by ovs-dpdk. Currently, only -virtio pci net device support vhost-user net. +virtio pci net device support vhost-user net. It should open sharing memory('-mem-share=on') and +hugepages('-mem-path ...' ) when using vhost-user net. ```shell # virtio pci net device -chardev socket,id=chardevid,path=socket_path --netdev vhost-user,id=netdevid,chardev=chardevid --device virtio-net-pci,netdev=netdevid,id=netid,mac=12:34:56:78:9A:BC,bus=pci.0,addr=0x2.0x0 +-netdev vhost-user,id=netdevid,chardev=chardevid[,queues=N] +-device virtio-net-pci,netdev=netdevid,id=netid,mac=12:34:56:78:9A:BC,bus=pci.0,addr=0x2.0x0[,mq=on] ``` *How to set a tap device?* @@ -393,8 +394,8 @@ $ ovs-vsctl add-br ovs_br -- set bridge ovs_br datapath_type=netdev $ ovs-vsctl add-port ovs_br port1 -- set Interface port1 type=dpdkvhostuser $ ovs-vsctl add-port ovs_br port2 -- set Interface port2 type=dpdkvhostuser # Set num of rxq/txq -$ ovs-vsctl set Interface port1 options:n_rxq=1,n_txq=1 -$ ovs-vsctl set Interface port2 options:n_rxq=1,n_txq=1 +$ ovs-vsctl set Interface port1 options:n_rxq=num,n_txq=num +$ ovs-vsctl set Interface port2 options:n_rxq=num,n_txq=num ``` ### 2.4 Virtio-console diff --git a/pci/src/msix.rs b/pci/src/msix.rs index c3daa418c..34d6d79a3 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -45,6 +45,7 @@ pub const MSIX_CAP_TABLE: u8 = 0x04; pub const MSIX_CAP_PBA: u8 = 0x08; /// MSI-X message structure. +#[derive(Copy, Clone)] pub struct Message { /// Lower 32bit address of MSI-X address. pub address_lo: u32, @@ -54,6 +55,11 @@ pub struct Message { pub data: u32, } +/// Trait used to update the interrupt routing table. +pub trait MsixUpdate: Sync + Send { + fn update_irq_routing(&mut self, _vector: u16, _entry: &Message, _is_masked: bool) {} +} + /// The state of msix device. #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] @@ -78,6 +84,7 @@ pub struct Msix { pub enabled: bool, pub msix_cap_offset: u16, pub dev_id: Arc, + pub msix_update: Option>>, } impl Msix { @@ -97,6 +104,7 @@ impl Msix { enabled: true, msix_cap_offset, dev_id: Arc::new(AtomicU16::new(dev_id)), + msix_update: None, }; msix.mask_all_vectors(); msix @@ -186,7 +194,19 @@ impl Msix { locked_msix.table[offset..(offset + 4)].copy_from_slice(data); let is_masked: bool = locked_msix.is_vector_masked(vector); - if was_masked && !is_masked { + if was_masked != is_masked { + if let Some(msix_update) = &locked_msix.msix_update { + let entry = locked_msix.get_message(vector); + msix_update + .lock() + .unwrap() + .update_irq_routing(vector, &entry, is_masked); + } + } + + // Clear the pending vector just when it is pending. Otherwise, it + // will cause unknown error. + if was_masked && !is_masked && locked_msix.is_vector_pending(vector) { locked_msix.clear_pending_vector(vector); locked_msix.notify(vector, dev_id.load(Ordering::Acquire)); } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 9875af550..bea5fda27 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -16,18 +16,20 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use log::warn; -use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; +use util::loop_context::EventNotifierHelper; use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::eventfd::EventFd; use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::{ net::{build_device_config_space, VirtioNetConfig}, - Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_TYPE_NET, + CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; use super::super::VhostOps; use super::VhostUserClient; @@ -100,6 +102,16 @@ impl VirtioDevice for Net { | 1 << VIRTIO_F_RING_EVENT_IDX; self.device_features &= features; + let queue_pairs = self.net_cfg.queues / 2; + if self.net_cfg.mq + && queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN + && queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX + { + self.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; + self.device_features |= 1 << VIRTIO_NET_F_MQ; + self.device_config.max_virtqueue_pairs = queue_pairs; + } + self.client = Some(client); if let Some(mac) = &self.net_cfg.mac { @@ -116,7 +128,12 @@ impl VirtioDevice for Net { /// Get the count of virtio device queues. fn queue_num(&self) -> usize { - QUEUE_NUM_NET + if self.net_cfg.mq { + // If support multi-queue, it should add 1 control queue. + (self.net_cfg.queues + 1) as usize + } else { + QUEUE_NUM_NET + } } /// Get the queue size of virtio device. @@ -175,11 +192,30 @@ impl VirtioDevice for Net { /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, - _mem_space: Arc, - _interrupt_cb: Arc, + mem_space: Arc, + interrupt_cb: Arc, queues: &[Arc>], - queue_evts: Vec, + mut queue_evts: Vec, ) -> Result<()> { + let mut queue_num = queues.len(); + if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { + let ctrl_queue = queues[queue_num - 1].clone(); + let ctrl_queue_evt = queue_evts.remove(queue_num - 1); + queue_num -= 1; + + let ctrl_handler = NetCtrlHandler { + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt), + mem_space, + interrupt_cb: interrupt_cb.clone(), + driver_features: self.driver_features, + }; + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), + self.net_cfg.iothread.as_ref(), + )?; + } + let client = match &self.client { None => return Err("Failed to get client for vhost-user net".into()), Some(client_) => client_, @@ -198,17 +234,11 @@ impl VirtioDevice for Net { .set_mem_table() .chain_err(|| "Failed to set mem table for vhost-user net")?; - for (queue_index, queue_mutex) in queues.iter().enumerate() { + // Set all vring num to notify ovs/dpdk how many queues it needs to poll + // before setting vring info. + for (queue_index, queue_mutex) in queues.iter().enumerate().take(queue_num) { let queue = queue_mutex.lock().unwrap(); let actual_size = queue.vring.actual_size(); - let queue_config = queue.vring.get_queue_config(); - - client.set_vring_enable(queue_index, false).chain_err(|| { - format!( - "Failed to set vring enable for vhost-user net, index: {}, false", - queue_index, - ) - })?; client .set_vring_num(queue_index, actual_size) .chain_err(|| { @@ -217,6 +247,12 @@ impl VirtioDevice for Net { queue_index, actual_size, ) })?; + } + + for (queue_index, queue_mutex) in queues.iter().enumerate().take(queue_num) { + let queue = queue_mutex.lock().unwrap(); + let queue_config = queue.vring.get_queue_config(); + client .set_vring_addr(&queue_config, queue_index, 0) .chain_err(|| { @@ -248,7 +284,9 @@ impl VirtioDevice for Net { queue_index, ) })?; + } + for (queue_index, _) in queues.iter().enumerate().take(queue_num) { client.set_vring_enable(queue_index, true).chain_err(|| { format!( "Failed to set vring enable for vhost-user net, index: {}", diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 6e4f314e9..e03e42220 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::HashMap; use std::mem::size_of; use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; use std::sync::{Arc, Mutex, Weak}; @@ -27,7 +28,7 @@ use pci::config::{ VENDOR_ID, }; use pci::errors::{ErrorKind, Result as PciResult, ResultExt}; -use pci::msix::{update_dev_id, MsixState}; +use pci::msix::{update_dev_id, Message, MsixState, MsixUpdate}; use pci::{ config::PciConfig, init_msix, init_multifunction, le_write_u16, ranges_overlap, PciBus, PciDevOps, @@ -469,6 +470,7 @@ pub struct VirtioPciState { struct GsiMsiRoute { irq_fd: Option, gsi: i32, + msg: Message, } /// Virtio-PCI device structure @@ -503,7 +505,7 @@ pub struct VirtioPciDevice { /// If the device need to register irqfd to kvm. need_irqfd: bool, /// Maintains a list of GSI with irqfds that are registered to kvm. - gsi_msi_routes: Arc>>, + gsi_msi_routes: Arc>>, } impl VirtioPciDevice { @@ -535,7 +537,7 @@ impl VirtioPciDevice { queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))), multi_func, need_irqfd: false, - gsi_msi_routes: Arc::new(Mutex::new(Vec::new())), + gsi_msi_routes: Arc::new(Mutex::new(HashMap::new())), } } @@ -725,7 +727,12 @@ impl VirtioPciDevice { locked_queues.push(arc_queue.clone()); } - let queue_num = cloned_pci_device.device.lock().unwrap().queue_num(); + let mut queue_num = cloned_pci_device.device.lock().unwrap().queue_num(); + // No need to create call event for control queue. + // It will be polled in StratoVirt when activating the device. + if queue_num % 2 != 0 { + queue_num -= 1; + } let call_evts = NotifyEventFds::new(queue_num); let queue_evts = cloned_pci_device.notify_eventfds.clone().events; if let Some(cb) = cloned_pci_device.interrupt_cb.clone() { @@ -994,6 +1001,9 @@ impl PciDevOps for VirtioPciDevice { &self.name, )?; + if let Some(ref msix) = self.config.msix { + msix.lock().unwrap().msix_update = Some(Arc::new(Mutex::new(self.clone()))) + } self.assign_interrupt_cb(); let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) @@ -1283,9 +1293,71 @@ impl MigrationHook for VirtioPciDevice { } } +impl MsixUpdate for VirtioPciDevice { + fn update_irq_routing(&mut self, vector: u16, entry: &Message, is_masked: bool) { + if !self.need_irqfd { + return; + } + + let mut locked_gsi_routes = self.gsi_msi_routes.lock().unwrap(); + let route = if let Some(route) = locked_gsi_routes.get_mut(&vector) { + route + } else { + return; + }; + + update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); + let msix_vector = MsiVector { + msg_addr_lo: entry.address_lo, + msg_addr_hi: entry.address_hi, + msg_data: entry.data, + masked: false, + #[cfg(target_arch = "aarch64")] + dev_id: self.dev_id.load(Ordering::Acquire) as u32, + }; + + if is_masked { + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .unregister_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) + .unwrap_or_else(|e| error!("Failed to unregister irq, error is {}", e)); + } else { + let msg = &route.msg; + if msg.data != entry.data + || msg.address_lo != entry.address_lo + || msg.address_hi != entry.address_hi + { + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .update_msi_route(route.gsi as u32, msix_vector) + .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {}", e)); + KVM_FDS + .load() + .commit_irq_routing() + .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {}", e)); + route.msg = *entry; + } + + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .register_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) + .unwrap_or_else(|e| error!("Failed to register irq, error is {}", e)); + } + } +} + fn virtio_pci_register_irqfd( pci_device: &VirtioPciDevice, - gsi_routes: &Arc>>, + gsi_routes: &Arc>>, call_fds: &[EventFd], ) -> bool { let locked_msix = if let Some(msix) = &pci_device.config.msix { @@ -1298,6 +1370,10 @@ fn virtio_pci_register_irqfd( let locked_queues = pci_device.queues.lock().unwrap(); let mut locked_gsi_routes = gsi_routes.lock().unwrap(); for (queue_index, queue_mutex) in locked_queues.iter().enumerate() { + if queue_index + 1 == locked_queues.len() && locked_queues.len() % 2 != 0 { + break; + } + let vector = queue_mutex.lock().unwrap().vring.get_queue_config().vector; let entry = locked_msix.get_message(vector as u16); let msix_vector = MsiVector { @@ -1347,16 +1423,17 @@ fn virtio_pci_register_irqfd( let gsi_route = GsiMsiRoute { irq_fd: Some(call_fds[queue_index].try_clone().unwrap()), gsi, + msg: entry, }; - locked_gsi_routes.push(gsi_route); + locked_gsi_routes.insert(vector as u16, gsi_route); } true } -fn virtio_pci_unregister_irqfd(gsi_routes: Arc>>) { +fn virtio_pci_unregister_irqfd(gsi_routes: Arc>>) { let mut locked_gsi_routes = gsi_routes.lock().unwrap(); - for route in locked_gsi_routes.iter() { + for (_, route) in locked_gsi_routes.iter() { if let Some(fd) = &route.irq_fd.as_ref() { KVM_FDS .load() -- Gitee From 166d6553c351d60bbad441827851a3376252e79d Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 18 Jul 2022 21:20:03 +0800 Subject: [PATCH 0083/2187] virtio net/block: modify the max number of queues supported Set the max number of multi queues to no more than 32. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 5 +++-- machine_manager/src/config/mod.rs | 2 +- machine_manager/src/config/network.rs | 11 +++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 26d446dd6..a6a9d4922 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -254,7 +254,7 @@ Nine properties are supported for virtio block device. * format: the format of block image, default value `raw`. NB: currently only `raw` is supported. (optional) If not set, default is raw. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, -the default block queue number is 1. +the default block queue number is 1. The max queues number supported is no more than 32. * bootindex: the boot order of block device. (optional) If not set, the priority is lowest. The number ranges from 0 to 255, the smaller the number, the higher the priority. It determines the order of bootable devices which firmware will use for booting the guest OS. @@ -286,7 +286,8 @@ Six properties are supported for netdev. * ifname: name of tap device in host. * fd: the file descriptor of opened tap device. * fds: file descriptors of opened tap device. -* queues: the optional queues attribute controls the number of queues to be used for either multiple queue virtio-net or vhost-net device. +* queues: the optional queues attribute controls the number of queues to be used for either multiple queue virtio-net or + vhost-net device. The max queues number supported is no more than 16. NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, the tap device would be created according to `ifname`. diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index cda22e2e0..29c176d9d 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -117,7 +117,7 @@ use util::trace::enable_trace_events; pub const MAX_STRING_LENGTH: usize = 255; pub const MAX_PATH_LENGTH: usize = 4096; -pub const MAX_VIRTIO_QUEUE: usize = 1024; +pub const MAX_VIRTIO_QUEUE: usize = 32; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; pub const MAX_NODES: u32 = 128; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 46644d09d..7b6d142b4 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -136,10 +136,13 @@ impl ConfigCheck for NetworkInterfaceConfig { } } - if self.queues * 2 + 1 > MAX_VIRTIO_QUEUE as u16 { - return Err(ErrorKind::StringLengthTooLong( - "queues".to_string(), - (MAX_VIRTIO_QUEUE - 1) / 2, + if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u16 { + return Err(ErrorKind::IllegalValue( + "number queues of net device".to_string(), + 1, + true, + MAX_VIRTIO_QUEUE as u64 / 2, + true, ) .into()); } -- Gitee From 1c1e67be1cb11bb518bf147c7e32c17d10b89915 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 18 Jul 2022 20:56:45 +0800 Subject: [PATCH 0084/2187] virtio-net: fix leak file descriptor in multiqueue feature It needs to release control queue fd after unplug virtio net device. Add code to release it. Signed-off-by: Xinle.Guo --- virtio/src/net.rs | 39 +++++++++++++++++++++++++++++++++- virtio/src/vhost/kernel/net.rs | 1 + 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 57c2aff74..135e1cd63 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -93,8 +93,10 @@ pub struct NetCtrlHandler { pub mem_space: Arc, /// The interrupt call back function. pub interrupt_cb: Arc, - // Bit mask of features negotiated by the backend and the frontend. + /// Bit mask of features negotiated by the backend and the frontend. pub driver_features: u64, + /// Deactivate event to delete net control handler. + pub deactivate_evt: EventFd, } #[repr(C, packed)] @@ -175,6 +177,27 @@ impl NetCtrlHandler { Ok(()) } + + fn deactivate_evt_handler(&mut self) -> Vec { + let notifiers = vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.ctrl.queue_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.deactivate_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + ]; + + notifiers + } } impl EventNotifierHelper for NetCtrlHandler { @@ -199,6 +222,19 @@ impl EventNotifierHelper for NetCtrlHandler { EventSet::IN, )); + // Register event notifier for deactivate_evt. + let cloned_net_io = net_io.clone(); + let handler: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + Some(cloned_net_io.lock().unwrap().deactivate_evt_handler()) + }); + notifiers.push(build_event_notifier( + locked_net_io.deactivate_evt.as_raw_fd(), + Some(handler), + NotifierOperation::AddShared, + EventSet::IN, + )); + notifiers } } @@ -915,6 +951,7 @@ impl VirtioDevice for Net { mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), }; EventLoop::update_event( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 49bb2d562..5d2d9ba55 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -263,6 +263,7 @@ impl VirtioDevice for Net { mem_space, interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), }; EventLoop::update_event( -- Gitee From fa4aa34830580b7d9ffcd8ced91d590183dbe22c Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 25 Jul 2022 21:14:16 +0800 Subject: [PATCH 0085/2187] vhost-user-net: fix leak file descriptor in multiqueue feature When deactivate a vhost user net device, there is a control queue file descriptor leak problem. Using`de_ctrl_evt` eventfd to unregister control queue fd in time. Signed-off-by: Yan Wang Signed-off-by: Xinle.Guo --- virtio/src/vhost/user/net.rs | 7 +++++++ virtio/src/virtio_pci.rs | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index bea5fda27..6aae2e376 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -55,6 +55,8 @@ pub struct Net { client: Option, /// The notifier events from host. call_events: Vec, + /// EventFd for deactivate control Queue. + de_ctrl_evt: EventFd, } impl Net { @@ -67,6 +69,7 @@ impl Net { mem_space: mem_space.clone(), client: None, call_events: Vec::::new(), + de_ctrl_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } } } @@ -208,6 +211,7 @@ impl VirtioDevice for Net { mem_space, interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, + deactivate_evt: self.de_ctrl_evt.try_clone().unwrap(), }; EventLoop::update_event( @@ -325,6 +329,9 @@ impl VirtioDevice for Net { client .delete_event() .chain_err(|| "Failed to delete vhost-user net event")?; + self.de_ctrl_evt + .write(1) + .chain_err(|| ErrorKind::EventFdWrite)?; self.client = None; self.realize() diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index e03e42220..0edf12d58 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -679,7 +679,7 @@ impl VirtioPciDevice { .write_common_config(&cloned_pci_device.device.clone(), offset, value) { error!( - "Failed to read common config of virtio-pci device, error is {}", + "Failed to write common config of virtio-pci device, error is {}", e.display_chain(), ); return false; @@ -1002,7 +1002,7 @@ impl PciDevOps for VirtioPciDevice { )?; if let Some(ref msix) = self.config.msix { - msix.lock().unwrap().msix_update = Some(Arc::new(Mutex::new(self.clone()))) + msix.lock().unwrap().msix_update = Some(Arc::new(Mutex::new(self.clone()))); } self.assign_interrupt_cb(); @@ -1061,6 +1061,10 @@ impl PciDevOps for VirtioPciDevice { let bus = self.parent_bus.upgrade().unwrap(); self.config.unregister_bars(&bus)?; + if let Some(ref msix) = self.config.msix { + msix.lock().unwrap().msix_update = None; + } + MigrationManager::unregister_device_instance_mutex_by_id( MsixState::descriptor(), &self.name, -- Gitee From b4760061b37ee63a9650d238dc5cf1fa93c06a82 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 30 Jul 2022 20:39:52 +0800 Subject: [PATCH 0086/2187] Update version to 2.2.0 --- Cargo.lock | 34 +++++++++++++++++----------------- Cargo.toml | 2 +- acpi/Cargo.toml | 2 +- address_space/Cargo.toml | 2 +- boot_loader/Cargo.toml | 2 +- cpu/Cargo.toml | 2 +- devices/Cargo.toml | 2 +- hypervisor/Cargo.toml | 2 +- machine/Cargo.toml | 2 +- machine_manager/Cargo.toml | 2 +- migration/Cargo.toml | 2 +- migration_derive/Cargo.toml | 2 +- ozone/Cargo.toml | 2 +- pci/Cargo.toml | 2 +- sysbus/Cargo.toml | 2 +- util/Cargo.toml | 2 +- vfio/Cargo.toml | 2 +- virtio/Cargo.toml | 2 +- 18 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 215f1d557..6c72adeeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "StratoVirt" -version = "2.1.0" +version = "2.2.0" dependencies = [ "error-chain", "hypervisor", @@ -20,7 +20,7 @@ dependencies = [ [[package]] name = "acpi" -version = "2.1.0" +version = "2.2.0" dependencies = [ "address_space", "byteorder", @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "address_space" -version = "2.1.0" +version = "2.2.0" dependencies = [ "arc-swap", "error-chain", @@ -99,7 +99,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "boot_loader" -version = "2.1.0" +version = "2.2.0" dependencies = [ "address_space", "devices", @@ -132,7 +132,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpu" -version = "2.1.0" +version = "2.2.0" dependencies = [ "error-chain", "hypervisor", @@ -150,7 +150,7 @@ dependencies = [ [[package]] name = "devices" -version = "2.1.0" +version = "2.2.0" dependencies = [ "acpi", "address_space", @@ -198,7 +198,7 @@ dependencies = [ [[package]] name = "hypervisor" -version = "2.1.0" +version = "2.2.0" dependencies = [ "arc-swap", "error-chain", @@ -279,7 +279,7 @@ dependencies = [ [[package]] name = "machine" -version = "2.1.0" +version = "2.2.0" dependencies = [ "acpi", "address_space", @@ -307,7 +307,7 @@ dependencies = [ [[package]] name = "machine_manager" -version = "2.1.0" +version = "2.2.0" dependencies = [ "error-chain", "libc", @@ -323,7 +323,7 @@ dependencies = [ [[package]] name = "migration" -version = "2.1.0" +version = "2.2.0" dependencies = [ "error-chain", "kvm-ioctls", @@ -337,7 +337,7 @@ dependencies = [ [[package]] name = "migration_derive" -version = "2.1.0" +version = "2.2.0" dependencies = [ "migration", "proc-macro2", @@ -370,7 +370,7 @@ checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" [[package]] name = "ozone" -version = "2.1.0" +version = "2.2.0" dependencies = [ "error-chain", "libc", @@ -404,7 +404,7 @@ dependencies = [ [[package]] name = "pci" -version = "2.1.0" +version = "2.2.0" dependencies = [ "acpi", "address_space", @@ -559,7 +559,7 @@ dependencies = [ [[package]] name = "sysbus" -version = "2.1.0" +version = "2.2.0" dependencies = [ "acpi", "address_space", @@ -583,7 +583,7 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "util" -version = "2.1.0" +version = "2.2.0" dependencies = [ "arc-swap", "byteorder", @@ -604,7 +604,7 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "vfio" -version = "2.1.0" +version = "2.2.0" dependencies = [ "address_space", "byteorder", @@ -629,7 +629,7 @@ checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7" [[package]] name = "virtio" -version = "2.1.0" +version = "2.2.0" dependencies = [ "acpi", "address_space", diff --git a/Cargo.toml b/Cargo.toml index 46371cbff..2178458da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "StratoVirt" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" description = "a lightweight hypervisor with low memory overhead and fast booting speed" diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml index d7586ff37..201e8a85e 100644 --- a/acpi/Cargo.toml +++ b/acpi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "acpi" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index e1f4e0bef..cf8975b00 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "address_space" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index 933ff8643..d72b54778 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "boot_loader" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index e0e34ce2c..663d88cb4 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cpu" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 341d0cc6e..5e553a0d9 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "devices" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 65fe2ae9a..53e910609 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hypervisor" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/machine/Cargo.toml b/machine/Cargo.toml index b2674ba83..5f6006ebe 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "machine" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index ed573c5b6..9940c7397 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "machine_manager" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 699180494..1cdd91ca8 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "migration" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" diff --git a/migration_derive/Cargo.toml b/migration_derive/Cargo.toml index 4c115c12b..a2a0d80d1 100644 --- a/migration_derive/Cargo.toml +++ b/migration_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "migration_derive" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index 838d15852..6208f0c41 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ozone" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" description = "Provides protection for stratovirt" diff --git a/pci/Cargo.toml b/pci/Cargo.toml index e96649c85..4e919610f 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pci" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml index b7b798da7..fcbb6ef32 100644 --- a/sysbus/Cargo.toml +++ b/sysbus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sysbus" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/util/Cargo.toml b/util/Cargo.toml index 2bbc9e27f..7611418ad 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "util" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index b8fff9d81..ddb385a14 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vfio" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 41a5a5cb8..547948355 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "virtio" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2018" license = "Mulan PSL v2" -- Gitee From 8adec6a74b7e50cc4e018c46fee05b07f9af47c8 Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Mon, 8 Aug 2022 16:21:29 +0800 Subject: [PATCH 0087/2187] stratovirt: delete extra spaces of end of line Stratovirt remains some extra spaces of end of line, so delete it. Signed-off-by: Zhang Yang --- cpu/src/aarch64/mod.rs | 2 +- docs/boot.md | 4 +- docs/config_guidebook.md | 4 +- docs/design.md | 8 ++-- docs/interconnect_with_libvirt.ch.md | 2 +- docs/interconnect_with_libvirt.md | 8 ++-- docs/quickstart.ch.md | 4 +- docs/vfio.md | 6 +-- license/LICENSE | 44 +++++++++---------- machine_manager/src/config/machine_config.rs | 2 +- .../microvm/functional/test_microvm_isula.py | 2 +- .../standvm/functional/test_standvm_isula.py | 2 +- .../standvm/functional/test_standvm_vfio.py | 2 +- tests/hydropper/utils/utils_common.py | 2 +- 14 files changed, 46 insertions(+), 46 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index a85ca8231..e6d1b434c 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -194,7 +194,7 @@ impl ArmCPUState { /// /// # Arguments /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. + /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn reset_vcpu(&self, vcpu_fd: &Arc) -> Result<()> { set_core_regs(vcpu_fd, self.core_regs) .chain_err(|| format!("Failed to set core register for CPU {}", self.apic_id))?; diff --git a/docs/boot.md b/docs/boot.md index 66541fa06..39d5495e4 100644 --- a/docs/boot.md +++ b/docs/boot.md @@ -176,8 +176,8 @@ Kernel image can be built with: # on x86_64 platform, get bzImage format kernel image. $ make -j$(nproc) bzImage ``` -In addition to manually building the kernel image, you can also download the -[kernel image](https://repo.openeuler.org/openEuler-21.09/stratovirt_img/x86_64/std-vmlinuxz) +In addition to manually building the kernel image, you can also download the +[kernel image](https://repo.openeuler.org/openEuler-21.09/stratovirt_img/x86_64/std-vmlinuxz) from the openEuler official website. #### 2.2 Build rootfs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 63243624d..cb2e58a3e 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -110,7 +110,7 @@ $ cat /proc/meminfo The optional NUMA node element gives the opportunity to create a virtual machine with non-uniform memory accesses. The application of NUMA node is that one region of memory can be set as fast memory, another can be set as slow memory. -Each NUMA node is given a list of command lines option, there will be described in detail below. +Each NUMA node is given a list of command lines option, there will be described in detail below. 1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] It describes the size and id of each memory zone, the policy of binding to host memory node. you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. @@ -120,7 +120,7 @@ Each NUMA node is given a list of command lines option, there will be described 3. -numa dist,src=0,dst=0,val=10 It describes the distance between source and destination. The default of source to source is 10, source to destination is 20. And if you choose not to set these parameters, the VM will set the default values. - + The following command shows how to set NUMA node: ```shell diff --git a/docs/design.md b/docs/design.md index cd297fd84..391f82af1 100644 --- a/docs/design.md +++ b/docs/design.md @@ -2,8 +2,8 @@ ## Overview -StratoVirt is an open-source lightweight virtualization technology based on -Linux Kernel-based Virtual Machine(KVM), which reduces memory resource +StratoVirt is an open-source lightweight virtualization technology based on +Linux Kernel-based Virtual Machine(KVM), which reduces memory resource consumption and improves VM startup speed while retains isolation capability and security capability of traditional virtualization. StratoVirt can be applied to microservices or serverless scenarios such as function computing, and reserves @@ -22,7 +22,7 @@ in lightweight scenarios, and provide UEFI boot support for standard VM. - Emulated mainboard: - microvm: To improve performance as well as reduce the attack surface, StratoVirt minimizes the simulation of user-mode devices. KVM simulation - devices and paravirtualization devices, such as GIC, serial, RTC and + devices and paravirtualization devices, such as GIC, serial, RTC and virtio-mmio devices are implemented; - standard VM: realize UEFI boot with constructed ACPI tables. Virtio-pci and VFIO devices can be attached to greatly improve the I/O performance; @@ -32,7 +32,7 @@ VFIO devices can be attached to greatly improve the I/O performance; ## Features - High isolation ability based on hardware; -- Fast cold boot: Benefit from the minimalist design, microvm can be started +- Fast cold boot: Benefit from the minimalist design, microvm can be started within 50ms; - Low memory overhead: StratoVirt works with a memory footprint at 4MB; - IO enhancement: StratoVirt offers normal IO ability with minimalist IO device diff --git a/docs/interconnect_with_libvirt.ch.md b/docs/interconnect_with_libvirt.ch.md index d0323e2d0..2da7eeacb 100644 --- a/docs/interconnect_with_libvirt.ch.md +++ b/docs/interconnect_with_libvirt.ch.md @@ -1,5 +1,5 @@ # libvirt -Libvirt是StratoVirt的管理软件,它是通过创建命令行来运行StratoVirt和发送QMP命令来管理StratoVirt。目前,支持五个virsh命令来管理StratoVirt: +Libvirt是StratoVirt的管理软件,它是通过创建命令行来运行StratoVirt和发送QMP命令来管理StratoVirt。目前,支持五个virsh命令来管理StratoVirt: `virsh create`, `virsh destroy`, `virsh suspend`, `virsh resume` 和 `virsh console`. diff --git a/docs/interconnect_with_libvirt.md b/docs/interconnect_with_libvirt.md index 88ec77f39..494831900 100644 --- a/docs/interconnect_with_libvirt.md +++ b/docs/interconnect_with_libvirt.md @@ -1,6 +1,6 @@ # libvirt -Libvirt is one of manager for StratoVirt, it manages StratoVirt by creating cmdlines to launch StratoVirt -and giving commands via QMP. Currently, five virsh commands are supported to manage StratoVirt: +Libvirt is one of manager for StratoVirt, it manages StratoVirt by creating cmdlines to launch StratoVirt +and giving commands via QMP. Currently, five virsh commands are supported to manage StratoVirt: `virsh create`, `virsh destroy`, `virsh suspend`, `virsh resume` and `virsh console`. @@ -102,7 +102,7 @@ Pflash can be added by the following config. ``` -- net +- net ``` @@ -117,7 +117,7 @@ Pflash can be added by the following config. - console -To use `virsh console` command, the virtio console with redirect `pty` should be configured. +To use `virsh console` command, the virtio console with redirect `pty` should be configured. ``` diff --git a/docs/quickstart.ch.md b/docs/quickstart.ch.md index 7280d4309..d690fe6d3 100644 --- a/docs/quickstart.ch.md +++ b/docs/quickstart.ch.md @@ -3,9 +3,9 @@ ## 1. 准备工作 * 主机操作系统 - + StratoVirt可以运行在x86_64和aarch64平台。 - + 最重要的是StratoVirt是基于Linux内核的虚拟机(KVM)构建的,因此在运行的平台上需要保证有KVM内核模块的存在。 * 权限 diff --git a/docs/vfio.md b/docs/vfio.md index bd0aeead6..0e55c6521 100644 --- a/docs/vfio.md +++ b/docs/vfio.md @@ -2,13 +2,13 @@ ## Introduction -The VFIO driver is an IOMMU/device agnostic framework for exposing direct access to userspace, in a secure, -IOMMU protected environment. Virtual machine often makes use of direct device access when configured for the highest +The VFIO driver is an IOMMU/device agnostic framework for exposing direct access to userspace, in a secure, +IOMMU protected environment. Virtual machine often makes use of direct device access when configured for the highest possible I/O performance. ## Preparation -In order to successfully use VFIO device, it is mandatory that hardware supports virtualization and IOMMU groups. +In order to successfully use VFIO device, it is mandatory that hardware supports virtualization and IOMMU groups. Execute the following command on your host OS to check whether the IOMMU has been turned on. ```shell # dmesg | grep iommu diff --git a/license/LICENSE b/license/LICENSE index 9e32cdef1..a589e86a2 100644 --- a/license/LICENSE +++ b/license/LICENSE @@ -1,6 +1,6 @@ 木兰宽松许可证, 第2版 - 木兰宽松许可证, 第2版 + 木兰宽松许可证, 第2版 2020年1月 http://license.coscl.org.cn/MulanPSL2 @@ -36,15 +36,15 @@ 5. 免责声明与责任限制 - “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 6. 语言 “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 - 条款结束 + 条款结束 如何将木兰宽松许可证,第2版,应用到您的软件 - + 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; @@ -55,11 +55,11 @@ Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. Mulan Permissive Software License,Version 2 @@ -67,25 +67,25 @@ Mulan Permissive Software License,Version 2 (Mulan PSL v2) January 2020 http://license.coscl.org.cn/MulanPSL2 - Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: - + Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: + 0. Definition - - Software means the program and related documents which are licensed under this License and comprise all Contribution(s). - + + Software means the program and related documents which are licensed under this License and comprise all Contribution(s). + Contribution means the copyrightable work licensed by a particular Contributor under this License. - + Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. - + Legal Entity means the entity making a Contribution and all its Affiliates. - + Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 1. Grant of Copyright License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. - 2. Grant of Patent License + 2. Grant of Patent License Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. @@ -111,7 +111,7 @@ To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: - i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; + i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; @@ -120,8 +120,8 @@ Copyright (c) [Year] [name of copyright holder] [Software Name] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 6dc75b2cc..b7e90db05 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -158,7 +158,7 @@ impl Default for MachineConfig { impl ConfigCheck for MachineConfig { fn check(&self) -> Result<()> { if self.mem_config.mem_size < MIN_MEMSIZE || self.mem_config.mem_size > MAX_MEMSIZE { - bail!("Memory size must >= 256MiB and <= 512GiB, default unit: MiB, current memory size: {:?} bytes", + bail!("Memory size must >= 256MiB and <= 512GiB, default unit: MiB, current memory size: {:?} bytes", &self.mem_config.mem_size); } diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_isula.py b/tests/hydropper/testcases/microvm/functional/test_microvm_isula.py index 5d341a87f..b528f3984 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_isula.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_isula.py @@ -162,7 +162,7 @@ def test_microvm_isula_sandbox(container): name="sandbox1-hydropper", annotation="io.kubernetes.docker.type=podsandbox") LOG.info("podsandbox container id:%s", podsandbox_id) - + podsandbox_id = podsandbox_id.strip('\n') container_id = kata_container.run_isula(options="-tid", runtime="io.containerd.kata.v2", diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_isula.py b/tests/hydropper/testcases/standvm/functional/test_standvm_isula.py index 5e0168586..94eeee97e 100644 --- a/tests/hydropper/testcases/standvm/functional/test_standvm_isula.py +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_isula.py @@ -140,7 +140,7 @@ def test_standvm_isula_sandbox(container): name="sandbox1-hydropper-stand", annotation="io.kubernetes.docker.type=podsandbox") LOG.info("podsandbox container id:%s", podsandbox_id) - + podsandbox_id = podsandbox_id.strip('\n') container_id = kata_container.run_isula(options="-tid", runtime="io.containerd.kata.v2", diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_vfio.py b/tests/hydropper/testcases/standvm/functional/test_standvm_vfio.py index dc399a58a..602798469 100644 --- a/tests/hydropper/testcases/standvm/functional/test_standvm_vfio.py +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_vfio.py @@ -89,6 +89,6 @@ def test_standvm_vfio_ssd(standvm, bdf): assert ret == 0 ret, _ = testvm.serial_cmd("rm test") assert ret == 0 - + session.close() testvm.shutdown() diff --git a/tests/hydropper/utils/utils_common.py b/tests/hydropper/utils/utils_common.py index 7713bef7f..8618b642b 100644 --- a/tests/hydropper/utils/utils_common.py +++ b/tests/hydropper/utils/utils_common.py @@ -70,7 +70,7 @@ def config_host_vfio(net_type, number, bdf): def rebind_vfio_pci(bdf): """unbind old driver and bind a new one""" - run("echo %s > /sys/bus/pci/devices/%s/driver/unbind" % (bdf, bdf), shell=True, check=True) + run("echo %s > /sys/bus/pci/devices/%s/driver/unbind" % (bdf, bdf), shell=True, check=True) run("echo `lspci -ns %s | awk -F':| ' '{print $5\" \"$6}'` > /sys/bus/pci/drivers/vfio-pci/new_id"\ %bdf, shell=True, check=True) -- Gitee From 21f702825cb9c1872579b9c949f5e83e026ef40f Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Wed, 13 Jul 2022 18:10:15 +0800 Subject: [PATCH 0088/2187] docs: add explanation for free page reporting of balloon Add explanation for how to use free page reporting of balloon. Signed-off-by: Zhang Yang --- docs/config_guidebook.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 46a536384..2d0c08db2 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -490,9 +490,10 @@ Or you can simply use `-serial dev` to bind serial with character device. ### 2.7 Virtio-balloon Balloon is a virtio device, it offers a flex memory mechanism for VM. -Only one property is supported for virtio-balloon. +Two properties are supported for virtio-balloon. * deflate_on_oom: whether to deflate balloon when there is no enough memory in guest. This feature can prevent OOM occur in guest. +* free_page_reporting: whether to release free pages from kernel reporting. This feature can be used to reuse memory. For virtio-balloon-pci, two more properties are required. * bus: name of bus which to attach. @@ -501,9 +502,9 @@ of device and the second one represents function number of it. ```shell # virtio mmio balloon device --device virtio-balloon-device,deflate-on-oom=true +-device virtio-balloon-device[,deflate-on-oom=true|false][,free-page-reporting=true|false] # virtio pci balloon device --device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,deflate-on-oom=true,id=balloon-0[,multifunction=on] +-device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,id=balloon-0[,deflate-on-oom=true|false][,free-page-reporting=true|false][,multifunction=on|off] ``` ### 2.8 Virtio-rng -- Gitee From 866f06ac0ddca7852adfe2afa74dfa85c0b9ccaf Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Wed, 13 Jul 2022 18:11:09 +0800 Subject: [PATCH 0089/2187] virtio: implement free page reporting for balloon Implement free page reporting for virtio balloon device, which can release free pages from guest kernel. Signed-off-by: Zhang Yang --- machine_manager/src/config/balloon.rs | 64 ++++++++- virtio/src/balloon.rs | 195 ++++++++++++++++++++++---- virtio/src/lib.rs | 3 + 3 files changed, 231 insertions(+), 31 deletions(-) diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index 7a7d9376a..9836af3f0 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -23,6 +23,7 @@ use crate::config::{CmdParser, ExBool, VmConfig}; pub struct BalloonConfig { pub id: String, pub deflate_on_oom: bool, + pub free_page_reporting: bool, } impl ConfigCheck for BalloonConfig { @@ -50,7 +51,8 @@ pub fn parse_balloon(vm_config: &mut VmConfig, balloon_config: &str) -> Result Result("deflate-on-oom")? { balloon.deflate_on_oom = default.into(); } + if let Some(default) = cmd_parser.get_value::("free-page-reporting")? { + balloon.free_page_reporting = default.into(); + } if let Some(id) = cmd_parser.get_value::("id")? { balloon.id = id; } @@ -120,4 +125,61 @@ mod tests { ); assert!(bln_cfg_res2.is_err()); } + + #[test] + fn test_fpr_balloon_config_cmdline_parser() { + let mut vm_config1 = VmConfig::default(); + let bln_cfg_res1 = parse_balloon( + &mut vm_config1, + "virtio-balloon-device,free-page-reporting=true,id=balloon0", + ); + assert!(bln_cfg_res1.is_ok()); + let balloon_configs1 = bln_cfg_res1.unwrap(); + assert_eq!(balloon_configs1.id, "balloon0".to_string()); + assert_eq!(balloon_configs1.free_page_reporting, true); + + let mut vm_config2 = VmConfig::default(); + let bln_cfg_res2 = parse_balloon( + &mut vm_config2, + "virtio-balloon-device,free-page-reporting=false,id=balloon0", + ); + assert!(bln_cfg_res2.is_ok()); + let balloon_configs2 = bln_cfg_res2.unwrap(); + assert_eq!(balloon_configs2.id, "balloon0".to_string()); + assert_eq!(balloon_configs2.free_page_reporting, false); + + let mut vm_config3 = VmConfig::default(); + let bln_cfg_res3 = parse_balloon( + &mut vm_config3, + "virtio-balloon-pci,free-page-reporting=true,bus=pcie.0,addr=0x1.0x2,id=balloon0", + ); + assert!(bln_cfg_res3.is_ok()); + let balloon_configs3 = bln_cfg_res3.unwrap(); + assert_eq!(balloon_configs3.id, "balloon0".to_string()); + assert_eq!(balloon_configs3.free_page_reporting, true); + + let mut vm_config4 = VmConfig::default(); + let bln_cfg_res4 = parse_balloon( + &mut vm_config4, + "virtio-balloon-pci,free-page-reporting=false,bus=pcie.0,addr=0x1.0x2,id=balloon0", + ); + assert!(bln_cfg_res4.is_ok()); + let balloon_configs4 = bln_cfg_res4.unwrap(); + assert_eq!(balloon_configs4.id, "balloon0".to_string()); + assert_eq!(balloon_configs4.free_page_reporting, false); + + let mut vm_config5 = VmConfig::default(); + let bln_cfg_res5 = parse_balloon(&mut vm_config5, "virtio-balloon-device,id=balloon0"); + assert!(bln_cfg_res5.is_ok()); + let balloon_configs5 = bln_cfg_res5.unwrap(); + assert_eq!(balloon_configs5.id, "balloon0".to_string()); + assert_eq!(balloon_configs5.free_page_reporting, false); + + let mut vm_config6 = VmConfig::default(); + let bln_cfg_res6 = parse_balloon( + &mut vm_config6, + "virtio-balloon-device,free-page-reporting=2,id=balloon0", + ); + assert!(bln_cfg_res6.is_err()); + } } diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 9379d4a39..5a5b8a527 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -41,17 +41,20 @@ use util::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use super::{ - errors::*, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, + errors::*, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, + VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; +const VIRTIO_BALLOON_F_REPORTING: u32 = 5; const VIRTIO_BALLOON_PFN_SHIFT: u32 = 12; const QUEUE_SIZE_BALLOON: u16 = 256; const QUEUE_NUM_BALLOON: usize = 2; const BALLOON_PAGE_SIZE: u64 = 1 << VIRTIO_BALLOON_PFN_SHIFT; const BALLOON_INFLATE_EVENT: bool = true; const BALLOON_DEFLATE_EVENT: bool = false; +const IN_IOVEC: bool = true; +const OUT_IOVEC: bool = false; const BITS_OF_TYPE_U64: u64 = 64; static mut BALLOON_DEV: Option>> = None; @@ -167,21 +170,27 @@ struct Request { impl Request { /// Parse the request from virtio queue. - /// Return the request from qirtio queue. + /// Return the request from virtio queue. /// /// # Arguments /// /// * `elem` - Available ring. - fn parse(elem: &Element) -> Result { + /// * `elem_type` - The type of available ring. + fn parse(elem: &Element, elem_type: bool) -> Result { let mut request = Request { desc_index: elem.index, elem_cnt: 0u32, iovec: Vec::new(), }; - if elem.out_iovec.is_empty() { - return Err(ErrorKind::ElementEmpty.into()); + let iovec = if elem_type { + &elem.in_iovec } else { - let elem_iov = elem.out_iovec.get(0).unwrap(); + &elem.out_iovec + }; + if iovec.is_empty() { + return Err(ErrorKind::ElementEmpty.into()); + } + for elem_iov in iovec { request.iovec.push(Iovec { iov_base: elem_iov.addr, iov_len: elem_iov.len as u64, @@ -298,6 +307,24 @@ impl Request { } } } + + fn release_pages(&self, mem: &Arc>) { + for iov in self.iovec.iter() { + let gpa: GuestAddress = iov.iov_base; + let hva = match mem.lock().unwrap().get_host_address(gpa) { + Some(addr) => (addr), + None => { + error!("Can not get host address, gpa: {}", gpa.raw_value()); + continue; + } + }; + memory_advise( + hva as *const libc::c_void as *mut _, + iov.iov_len as usize, + libc::MADV_DONTNEED, + ); + } + } } #[derive(Debug, Copy, Clone, Default)] @@ -452,6 +479,10 @@ struct BalloonIoHandler { def_queue: Arc>, /// Deflate EventFd. def_evt: EventFd, + /// Reporting queue. + report_queue: Option>>, + /// Reporting EventFd. + report_evt: Option, /// EventFd for device deactivate deactivate_evt: RawFd, /// The interrupt call back function. @@ -483,24 +514,15 @@ impl BalloonIoHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { - match Request::parse(&elem) { - Ok(req) => { - if !self.mem_info.lock().unwrap().has_huge_page() { - req.mark_balloon_page(req_type, &self.mem_space, &self.mem_info); - } - locked_queue - .vring - .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) - .chain_err(|| "Failed to add balloon response into used queue")?; - } - Err(e) => { - error!( - "Fail to parse available descriptor chain, error is {}", - e.display_chain() - ); - break; - } + let req = Request::parse(&elem, OUT_IOVEC) + .chain_err(|| "Fail to parse available descriptor chain")?; + if !self.mem_info.lock().unwrap().has_huge_page() { + req.mark_balloon_page(req_type, &self.mem_space, &self.mem_info); } + locked_queue + .vring + .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) + .chain_err(|| "Failed to add balloon response into used queue")?; } (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)) @@ -508,6 +530,33 @@ impl BalloonIoHandler { Ok(()) } + fn reporting_evt_handler(&mut self) -> Result<()> { + let queue = &self.report_queue; + if queue.is_none() { + return Err(ErrorKind::VirtQueueIsNone.into()); + } + let unwraped_queue = queue.as_ref().unwrap(); + let mut locked_queue = unwraped_queue.lock().unwrap(); + while let Ok(elem) = locked_queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + { + let req = Request::parse(&elem, IN_IOVEC) + .chain_err(|| "Fail to parse available descriptor chain")?; + if !self.mem_info.lock().unwrap().has_huge_page() { + req.release_pages(&self.mem_info); + } + locked_queue + .vring + .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) + .chain_err(|| "Failed to add balloon response into used queue")?; + + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)) + .chain_err(|| ErrorKind::InterruptTrigger("balloon", VirtioInterruptType::Vring))?; + } + Ok(()) + } + /// Send balloon changed event. fn send_balloon_changed_event(&self) { let ram_size = self.mem_info.lock().unwrap().get_ram_size(); @@ -617,6 +666,19 @@ impl EventNotifierHelper for BalloonIoHandler { handler, )); + // register event notifier for free page reporting event. + if let Some(report_evt) = locked_balloon_io.report_evt.as_ref() { + let cloned_balloon_io = balloon_io.clone(); + let handler: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + if let Err(e) = cloned_balloon_io.lock().unwrap().reporting_evt_handler() { + error!("Failed to report free pages: {}", e.display_chain()); + } + None + }); + notifiers.push(build_event_notifier(report_evt.as_raw_fd(), handler)); + } + // register event notifier for reset event. let cloned_balloon_io = balloon_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { @@ -685,6 +747,9 @@ impl Balloon { if bln_cfg.deflate_on_oom { device_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM; } + if bln_cfg.free_page_reporting { + device_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING; + } Balloon { device_features, @@ -774,7 +839,11 @@ impl VirtioDevice for Balloon { /// Get the number of balloon-device queues. fn queue_num(&self) -> usize { - QUEUE_NUM_BALLOON + let mut queue_num = QUEUE_NUM_BALLOON; + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { + queue_num += 1; + } + queue_num } /// Get the zise of balloon queue. @@ -874,23 +943,47 @@ impl VirtioDevice for Balloon { queues: &[Arc>], mut queue_evts: Vec, ) -> Result<()> { - if queues.len() != QUEUE_NUM_BALLOON { + if queues.len() != self.queue_num() { return Err(ErrorKind::IncorrectQueueNum(QUEUE_NUM_BALLOON, queues.len()).into()); } let inf_queue = queues[0].clone(); - let inf_queue_evt = queue_evts.remove(0); + let inf_evt = queue_evts.remove(0); let def_queue = queues[1].clone(); - let def_queue_evt = queue_evts.remove(0); + let def_evt = queue_evts.remove(0); + + let mut current_queue_index = 1; + let (report_queue, report_evt) = + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) + && current_queue_index + 1 < self.queue_num() + && !queue_evts.is_empty() + { + current_queue_index += 1; + ( + Some(queues[current_queue_index].clone()), + Some(queue_evts.remove(0)), + ) + } else { + if current_queue_index + 1 >= self.queue_num() { + error!( + "Queue index: {} is invalid, correct index is from 0 to {}!", + current_queue_index + 1, + self.queue_num() + ) + } + (None, None) + }; self.interrupt_cb = Some(interrupt_cb.clone()); let handler = BalloonIoHandler { driver_features: self.driver_features, mem_space, inf_queue, - inf_evt: inf_queue_evt, + inf_evt, def_queue, - def_evt: def_queue_evt, + def_evt, + report_queue, + report_evt, deactivate_evt: self.deactivate_evt.as_raw_fd(), interrupt_cb, mem_info: self.mem_info.clone(), @@ -1017,6 +1110,7 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, + free_page_reporting: Default::default(), }; let mem_space = address_space_init(); @@ -1058,6 +1152,7 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, + free_page_reporting: Default::default(), }; let mem_space = address_space_init(); @@ -1076,6 +1171,7 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, + free_page_reporting: Default::default(), }; let mem_space = address_space_init(); @@ -1093,6 +1189,7 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, + free_page_reporting: Default::default(), }; let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); bln.realize().unwrap(); @@ -1165,6 +1262,8 @@ mod tests { inf_evt: event_inf.try_clone().unwrap(), def_queue: queue2, def_evt: event_def, + report_queue: None, + report_evt: None, deactivate_evt: event_deactivate.as_raw_fd(), interrupt_cb: cb.clone(), mem_info: bln.mem_info.clone(), @@ -1266,6 +1365,7 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, + free_page_reporting: Default::default(), }; let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); assert!(bln @@ -1323,4 +1423,39 @@ mod tests { // Out of range. assert!(!btp.is_full(65)); } + + #[test] + fn test_balloon_init_free_page_reporting() { + let bln_cfg = BalloonConfig { + id: "bln".to_string(), + deflate_on_oom: true, + free_page_reporting: true, + }; + let mem_space = address_space_init(); + let mut bln = Balloon::new(&bln_cfg, mem_space); + assert_eq!(bln.driver_features, 0); + assert_eq!(bln.actual.load(Ordering::Acquire), 0); + assert_eq!(bln.num_pages, 0); + assert!(bln.interrupt_cb.is_none()); + let feature = (1u64 << VIRTIO_F_VERSION_1) + | (1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM | 1u64 << VIRTIO_BALLOON_F_REPORTING); + assert_eq!(bln.device_features, feature); + + let fts = bln.get_device_features(0); + assert_eq!(fts, feature as u32); + let fts = bln.get_device_features(1); + assert_eq!(fts, (feature >> 32) as u32); + + // Test realize function. + bln.realize().unwrap(); + assert_eq!(bln.device_type(), 5); + assert_eq!(bln.queue_num(), 3); + assert_eq!(bln.queue_size(), QUEUE_SIZE); + // Test methods of balloon. + let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); + assert_eq!(ram_size, MEMORY_SIZE); + + assert!(bln.deactivate().is_ok()); + assert!(bln.update_config(None).is_err()); + } } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 5af20f3f0..afb53dd68 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -71,6 +71,9 @@ pub mod errors { ElementEmpty { display("Failed to get iovec from element!") } + VirtQueueIsNone { + display("Virt queue is none!") + } IncorrectQueueNum(expect: usize, actual: usize) { display("Cannot perform activate. Expected {} queue(s), got {}", expect, actual) } -- Gitee From 484e19985674da1bd1fea908497d6af672174668 Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Tue, 19 Jul 2022 11:01:30 +0800 Subject: [PATCH 0090/2187] virtio: add testcase for free page reporting Add testcase of free page reporting in hydropper. Signed-off-by: Zhang Yang --- .../functional/test_microvm_balloon.py | 21 ++++++++++++++++++- .../functional/test_standvm_balloon.py | 21 ++++++++++++++++++- tests/hydropper/virt/basevm.py | 16 ++++++++++++++ tests/hydropper/virt/microvm.py | 11 +++++++--- tests/hydropper/virt/standvm.py | 4 ++++ 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py b/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py index 2b6532e43..1afed0ebf 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py @@ -18,6 +18,26 @@ import pytest LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" logging.basicConfig(filename='/var/log/pytest.log', level=logging.DEBUG, format=LOG_FORMAT) +@pytest.mark.acceptance +def test_microvm_balloon_fpr(microvm): + """ + Test free page reporting of querying balloon + + steps: + 1) launch microvm with argument: "-balloon free-page-reporting=true". + 2) execute command "stress --vm 2 --vm-bytes 1G --vm-keep --timeout 20". + 3) compare rss between booted and fpr done. + """ + test_vm = microvm + test_vm.basic_config(mem_size=3072, balloon=True, free_page_reporting=True) + test_vm.launch() + + rss_booted = test_vm.get_rss_with_status_check() + test_vm.memory_stress() + rss_fpr_done = test_vm.get_rss_with_status_check() + assert rss_fpr_done - rss_booted < 20480 + test_vm.shutdown() + @pytest.mark.acceptance def test_microvm_balloon_query(microvm): """ @@ -49,7 +69,6 @@ def test_microvm_balloon(microvm): Note that balloon device may not inflate as many as the given argument, but it can deflate until no page left in balloon device. Therefore, memory in step 5 is less than 2524971008, while that in step 7 equals 2524971008. - """ test_vm = microvm test_vm.basic_config(balloon=True, deflate_on_oom=True) diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py b/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py index f007127c8..f40fea254 100644 --- a/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py @@ -18,6 +18,26 @@ import pytest LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" logging.basicConfig(filename='/var/log/pytest.log', level=logging.DEBUG, format=LOG_FORMAT) +@pytest.mark.acceptance +def test_standvm_balloon_fpr(standvm): + """ + Test free page reporting of querying balloon + + steps: + 1) launch standvm with argument: "-balloon free-page-reporting=true". + 2) execute command "stress --vm 2 --vm-bytes 1G --vm-keep --timeout 20". + 3) compare rss between booted and fpr done. + """ + test_vm = standvm + test_vm.basic_config(mem_size=3072, balloon=True, free_page_reporting=True) + test_vm.launch() + + rss_booted = test_vm.get_rss_with_status_check() + test_vm.memory_stress() + rss_fpr_done = test_vm.get_rss_with_status_check() + assert rss_fpr_done - rss_booted < 20480 + test_vm.shutdown() + @pytest.mark.acceptance def test_standvm_balloon_query(standvm): """ @@ -49,7 +69,6 @@ def test_standvm_balloon(standvm): Note that balloon device may not inflate as many as the given argument, but it can deflate until no page left in balloon device. Therefore, memory in step 5 is less than 2524971008, while that in step 7 equals 2524971008. - """ test_vm = standvm test_vm.basic_config(balloon=True, deflate_on_oom=True) diff --git a/tests/hydropper/virt/basevm.py b/tests/hydropper/virt/basevm.py index 8946bc130..44f44bcc3 100644 --- a/tests/hydropper/virt/basevm.py +++ b/tests/hydropper/virt/basevm.py @@ -34,6 +34,7 @@ from utils.exception import QMPCapabilitiesError from utils.exception import QMPTimeoutError from utils.exception import SSHError from utils.exception import LoginTimeoutError +from subprocess import getstatusoutput LOG = TestLog.get_global_log() LOGIN_TIMEOUT = 10 @@ -111,6 +112,7 @@ class BaseVM: self.withpid = False self.balloon = balloon self.deflate_on_oom = False + self.free_page_reporting = False self.quickstart_incoming = None def __enter__(self): @@ -135,6 +137,20 @@ class BaseVM: return None + def get_rss_with_status_check(self): + INVALID_VALUE = -1 + cmd = "ps -q %d -o rss=" % self.pid + status, output = getstatusoutput(cmd) + assert status == 0 + return int(output) + + def memory_stress(self, thread_num=2, vm_bytes='1G', timeout=20): + status, _ = self.serial_cmd("stress-ng --vm %d --vm-bytes %s --vm-keep --timeout %d" % (thread_num, vm_bytes, timeout)) + if status != 0: + logging.error("Cannot execute stress in stratovirt.") + assert status == 0 + time.sleep(20) + def _pre_shutdown(self): pass diff --git a/tests/hydropper/virt/microvm.py b/tests/hydropper/virt/microvm.py index 40f470478..7e4babf4f 100644 --- a/tests/hydropper/virt/microvm.py +++ b/tests/hydropper/virt/microvm.py @@ -195,11 +195,16 @@ class MicroVM(BaseVM): 'guest-cid=%s' % (sockcid, sockcid)]) if self.balloon: + _temp_balloon_args = 'virtio-balloon-device' if self.deflate_on_oom: - _temp_balloon_args = 'deflate-on-oom=true' + _temp_balloon_args += ',deflate-on-oom=true' else: - _temp_balloon_args = 'deflate-on-oom=false' - args.extend(['-device', 'virtio-balloon-device', _temp_balloon_args]) + _temp_balloon_args += ',deflate-on-oom=false' + if self.free_page_reporting: + _temp_balloon_args += ',free-page-reporting=true' + else: + _temp_balloon_args += ',free-page-reporting=false' + args.extend(['-device', _temp_balloon_args]) if self.iothreads > 0: args = self.make_iothread_cmd(args) diff --git a/tests/hydropper/virt/standvm.py b/tests/hydropper/virt/standvm.py index ae6cc43d7..0bda7a4ef 100644 --- a/tests/hydropper/virt/standvm.py +++ b/tests/hydropper/virt/standvm.py @@ -285,6 +285,10 @@ class StandVM(BaseVM): _temp_balloon_args += ',deflate-on-oom=true' else: _temp_balloon_args += ',deflate-on-oom=false' + if self.free_page_reporting: + _temp_balloon_args += ',free-page-reporting=true' + else: + _temp_balloon_args += ',free-page-reporting=false' if self.multifunction["balloon"]: _temp_balloon_args += ",multifunction=on" self.add_args('-device', _temp_balloon_args) -- Gitee From 9c576f7d8ade059d2760b247e66573b97cd714a6 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 12:17:20 +0800 Subject: [PATCH 0091/2187] vCPU/GIC pause: fix some error when pause vCPU and GIC 1.vCPU should wait pause state from kvm after notifying kvm exits. So, after executing`set_kvm_immediate_exit`, set `pause_signal` to be true. If not, the system call will fail to get the vCPU mutex lock. 2.Remove access GIC ITS tables operation to pause stage. It will flush tables into guest RAM. If not, it will cause live migration failure. Signed-off-by: Xinle.Guo --- cpu/src/lib.rs | 31 ++++++++++++++----- .../src/interrupt_controller/aarch64/gicv3.rs | 24 +++++++++----- .../src/interrupt_controller/aarch64/state.rs | 2 -- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index dbc30fe0d..86e1d3d74 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -107,8 +107,7 @@ pub use x86_64::X86CPUState as ArchCPU; pub use x86_64::X86CPUTopology as CPUTopology; use std::cell::RefCell; -use std::sync::atomic::fence; -use std::sync::atomic::Ordering; +use std::sync::atomic::{fence, AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; use std::time::Duration; @@ -214,6 +213,8 @@ pub struct CPU { caps: CPUCaps, /// The state backup of architecture CPU right before boot. boot_state: Arc>, + /// Sync the pause state of vCPU in kvm and userspace. + pause_signal: Arc, } impl CPU { @@ -241,6 +242,7 @@ impl CPU { vm: Arc::downgrade(&vm), caps: CPUCaps::init_capabilities(), boot_state: Arc::new(Mutex::new(ArchCPU::default())), + pause_signal: Arc::new(AtomicBool::new(false)), } } @@ -320,6 +322,7 @@ impl CPUInterface for CPU { } *cpu_state = CpuLifecycleState::Running; + self.pause_signal.store(false, Ordering::SeqCst); drop(cpu_state); cvar.notify_one(); Ok(()) @@ -390,15 +393,25 @@ impl CPUInterface for CPU { } match task.as_ref() { - Some(thread) => match thread.kill(VCPU_TASK_SIGNAL) { - Ok(_) => Ok(()), - Err(e) => Err(ErrorKind::StopVcpu(format!("{}", e)).into()), - }, + Some(thread) => { + if let Err(e) = thread.kill(VCPU_TASK_SIGNAL) { + return Err(ErrorKind::StopVcpu(format!("{}", e)).into()); + } + } None => { - warn!("VCPU thread not started, no need to stop"); - Ok(()) + warn!("vCPU thread not started, no need to stop"); + return Ok(()); } } + + // It shall wait for the vCPU pause state from kvm exits. + loop { + if self.pause_signal.load(Ordering::SeqCst) { + break; + } + } + + Ok(()) } fn destroy(&self) -> Result<()> { @@ -595,6 +608,8 @@ impl CPUThreadWorker { VCPU_TASK_SIGNAL => { let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { vcpu.fd().set_kvm_immediate_exit(1); + // Setting pause_signal to be `true` if kvm changes vCPU to pause state. + vcpu.pause_signal.store(true, Ordering::SeqCst); fence(Ordering::Release) }); } diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 7cc73e338..27754a00e 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -83,7 +83,7 @@ pub struct GICv3 { /// Number of vCPUs, determines the number of redistributor and CPU interface. pub(crate) vcpu_count: u64, /// GICv3 ITS device. - pub(crate) its_dev: Option>, + pub its_dev: Option>, /// Maximum irq number. pub(crate) nr_irqs: u32, /// GICv3 redistributor info, support multiple redistributor regions. @@ -249,15 +249,23 @@ impl MachineLifecycle for GICv3 { 0, true, ) - .is_ok() + .is_err() { - let mut state = self.state.lock().unwrap(); - *state = KvmVmState::Running; + return false; + } - true - } else { - false + // The ITS tables need to be flushed into guest RAM before VM pause. + if let Some(its_dev) = &self.its_dev { + if let Err(e) = its_dev.access_gic_its_tables(true) { + error!("Failed to access GIC ITS tables, error: {}", e); + return false; + } } + + let mut state = self.state.lock().unwrap(); + *state = KvmVmState::Running; + + true } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { @@ -431,7 +439,7 @@ impl GICDevice for GICv3 { } } -pub(crate) struct GICv3Its { +pub struct GICv3Its { /// The fd for the GICv3Its device fd: DeviceFd, diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 2f54c1b6f..0c20a7bc8 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -680,8 +680,6 @@ impl StateTransfer for GICv3Its { use migration::errors::ErrorKind; let mut state = GICv3ItsState::default(); - self.access_gic_its_tables(true) - .map_err(|e| ErrorKind::GetGicRegsError("Its table", e.to_string()))?; for i in 0..8 { self.access_gic_its(GITS_BASER + 8 * i as u32, &mut state.baser[i], false) .map_err(|e| ErrorKind::GetGicRegsError("Its baser", e.to_string()))?; -- Gitee From 87bbfdc38106d1a450fc0bf35ac4824c046e2175 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 15:18:59 +0800 Subject: [PATCH 0092/2187] migration: refactoring protocol for snapshot and migration 1. Move `MigrationStatus`, `MigrationHeader` to protocol.rs. 2. Update the live migration compatible version to 2.2 version. 3. Add `request` and `response` transport protocol structures for live migration. Signed-off-by: Xinle.Guo --- migration/src/header.rs | 188 ------ .../src/{device_state.rs => protocol.rs} | 558 +++++++++++++++++- migration/src/status.rs | 203 ------- 3 files changed, 550 insertions(+), 399 deletions(-) delete mode 100644 migration/src/header.rs rename migration/src/{device_state.rs => protocol.rs} (48%) delete mode 100644 migration/src/status.rs diff --git a/migration/src/header.rs b/migration/src/header.rs deleted file mode 100644 index 46833b40a..000000000 --- a/migration/src/header.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use kvm_ioctls::Kvm; - -use util::byte_code::ByteCode; - -use super::errors::{ErrorKind, Result}; - -/// Magic number for migration header. Those bytes represent "STRATOVIRT". -const MAGIC_NUMBER: [u8; 16] = [ - 0x53, 0x54, 0x52, 0x41, 0x54, 0x4f, 0x56, 0x49, 0x52, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -]; -const CURRENT_VERSION: u32 = 1; -const COMPAT_VERSION: u32 = 1; -#[cfg(target_arch = "x86_64")] -const EAX_VENDOR_INFO: u32 = 0x0; - -/// Format type for migration. -/// Different file format will have different file layout. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum FileFormat { - Device = 1, - MemoryFull = 2, -} - -/// The endianness of byte order. -#[derive(Debug, Copy, Clone, PartialEq)] -enum EndianType { - Little = 1, - Big = 2, -} - -impl EndianType { - fn get_endian_type() -> EndianType { - if cfg!(target_endian = "big") { - EndianType::Big - } else { - EndianType::Little - } - } -} - -/// Get host cpu model as bytes. -#[cfg(target_arch = "x86_64")] -fn cpu_model() -> [u8; 16] { - use core::arch::x86_64::__cpuid_count; - - // Safe because we only use cpuid for cpu info in x86_64. - let result = unsafe { __cpuid_count(EAX_VENDOR_INFO, 0) }; - let vendor_slice = [result.ebx, result.edx, result.ecx]; - - // Safe because we known those brand string length. - let vendor_array = unsafe { - let brand_string_start = vendor_slice.as_ptr() as *const u8; - std::slice::from_raw_parts(brand_string_start, 3 * 4) - }; - - let mut buffer = [0u8; 16]; - if vendor_array.len() > 16 { - buffer.copy_from_slice(&vendor_array[0..15]); - } else { - buffer[0..vendor_array.len()].copy_from_slice(vendor_array); - } - buffer -} - -/// Structure used to mark some message in migration. -#[derive(Copy, Clone, Debug)] -pub struct MigrationHeader { - /// Magic number for migration file/stream. - magic_num: [u8; 16], - /// Current version of migration. - #[allow(dead_code)] - current_version: u32, - /// Compatible version of migration. - compat_version: u32, - /// Arch identifier. - arch: [u8; 8], - /// Endianness of byte order. - byte_order: EndianType, - /// The type of hypervisor. - #[allow(dead_code)] - hypervisor_type: [u8; 8], - /// The version of hypervisor. - hypervisor_version: u32, - /// The type of Cpu model. - #[cfg(target_arch = "x86_64")] - cpu_model: [u8; 16], - /// Operation system type. - os_type: [u8; 8], - /// File format of migration file/stream. - pub format: FileFormat, - /// The length of `DeviceStateDesc`. - pub desc_len: usize, -} - -impl ByteCode for MigrationHeader {} - -impl Default for MigrationHeader { - fn default() -> Self { - MigrationHeader { - magic_num: MAGIC_NUMBER, - current_version: CURRENT_VERSION, - compat_version: COMPAT_VERSION, - format: FileFormat::Device, - byte_order: EndianType::Little, - hypervisor_type: [b'k', b'v', b'm', b'0', b'0', b'0', b'0', b'0'], - hypervisor_version: Kvm::new().unwrap().get_api_version() as u32, - #[cfg(target_arch = "x86_64")] - cpu_model: cpu_model(), - #[cfg(target_os = "linux")] - os_type: [b'l', b'i', b'n', b'u', b'x', b'0', b'0', b'0'], - #[cfg(target_arch = "x86_64")] - arch: [b'x', b'8', b'6', b'_', b'6', b'4', b'0', b'0'], - #[cfg(target_arch = "aarch64")] - arch: [b'a', b'a', b'r', b'c', b'h', b'6', b'4', b'0'], - desc_len: 0, - } - } -} - -impl MigrationHeader { - /// Check parsed `MigrationHeader` is illegal or not. - pub fn check_header(&self) -> Result<()> { - if self.magic_num != MAGIC_NUMBER { - return Err(ErrorKind::HeaderItemNotFit("Magic_number".to_string()).into()); - } - - if self.compat_version > CURRENT_VERSION { - return Err(ErrorKind::VersionNotFit(self.compat_version, CURRENT_VERSION).into()); - } - - #[cfg(target_arch = "x86_64")] - let current_arch = [b'x', b'8', b'6', b'_', b'6', b'4', b'0', b'0']; - #[cfg(target_arch = "aarch64")] - let current_arch = [b'a', b'a', b'r', b'c', b'h', b'6', b'4', b'0']; - if self.arch != current_arch { - return Err(ErrorKind::HeaderItemNotFit("Arch".to_string()).into()); - } - - if self.byte_order != EndianType::get_endian_type() { - return Err(ErrorKind::HeaderItemNotFit("Byte order".to_string()).into()); - } - - #[cfg(target_arch = "x86_64")] - if self.cpu_model != cpu_model() { - return Err(ErrorKind::HeaderItemNotFit("Cpu model".to_string()).into()); - } - - #[cfg(target_os = "linux")] - let current_os_type = [b'l', b'i', b'n', b'u', b'x', b'0', b'0', b'0']; - if self.os_type != current_os_type { - return Err(ErrorKind::HeaderItemNotFit("Os type".to_string()).into()); - } - - let current_kvm_version = Kvm::new().unwrap().get_api_version() as u32; - if current_kvm_version < self.hypervisor_version { - return Err(ErrorKind::HeaderItemNotFit("Hypervisor version".to_string()).into()); - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::{Kvm, MigrationHeader}; - - #[test] - fn test_check_header() { - if !Kvm::new().is_ok() { - return; - } - - let header = MigrationHeader::default(); - assert_eq!(header.check_header().is_ok(), true); - } -} diff --git a/migration/src/device_state.rs b/migration/src/protocol.rs similarity index 48% rename from migration/src/device_state.rs rename to migration/src/protocol.rs index e3f64d61c..38d808762 100644 --- a/migration/src/device_state.rs +++ b/migration/src/protocol.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -11,11 +11,426 @@ // See the Mulan PSL v2 for more details. use std::cmp::Ordering; +use std::io::{Read, Write}; +use std::mem::size_of; +use std::slice::{from_raw_parts, from_raw_parts_mut}; -use error_chain::bail; +use kvm_ioctls::Kvm; use serde::{Deserialize, Serialize}; -use super::errors::Result; +use crate::errors::{ErrorKind, Result, ResultExt}; +use util::byte_code::ByteCode; + +/// This status for migration in migration process. +/// +/// # Notes +/// +/// State transfer: +/// None -----------> Setup: set up migration resource. +/// Setup ----------> Active: migration is ready. +/// Active ---------> Completed: migration is successful. +/// Completed ------> Active: make migration become ready again. +/// Failed ---------> Setup: reset migration resource. +/// Any ------------> Failed: something wrong in migration. +/// Any ------------> Canceled: cancel migration. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum MigrationStatus { + /// Migration resource is not prepared all + None, + /// Migration resource(desc_db, device instance, ...) is setup. + Setup, + /// Migration is active. + Active, + /// Migration completed. + Completed, + /// Migration failed. + Failed, + /// Migration canceled. + Canceled, +} + +impl std::fmt::Display for MigrationStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + MigrationStatus::None => "none", + MigrationStatus::Setup => "setup", + MigrationStatus::Active => "active", + MigrationStatus::Completed => "completed", + MigrationStatus::Failed => "failed", + MigrationStatus::Canceled => "canceled", + } + ) + } +} + +impl MigrationStatus { + // Check and transfer migration status after convert migration operations. + pub fn transfer(self, new_status: MigrationStatus) -> Result { + match self { + MigrationStatus::None => match new_status { + MigrationStatus::Setup => Ok(new_status), + _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + }, + MigrationStatus::Setup => match new_status { + MigrationStatus::Active | MigrationStatus::Failed | MigrationStatus::Canceled => { + Ok(new_status) + } + _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + }, + MigrationStatus::Active => match new_status { + MigrationStatus::Completed + | MigrationStatus::Failed + | MigrationStatus::Canceled => Ok(new_status), + _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + }, + MigrationStatus::Completed => match new_status { + MigrationStatus::Active => Ok(new_status), + _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + }, + MigrationStatus::Failed => match new_status { + MigrationStatus::Setup | MigrationStatus::Active => Ok(new_status), + _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + }, + MigrationStatus::Canceled => Ok(new_status), + } + } +} + +/// Structure defines the transmission protocol between the source with destination VM. +#[repr(u16)] +#[derive(Copy, Clone, PartialEq)] +pub enum TransStatus { + /// Active migration. + Active, + /// Vm configuration. + VmConfig, + /// Processing memory data stage in migration. + Memory, + /// Processing device state stage in migration. + State, + /// Complete migration. + Complete, + /// Cancel migration. + Cancel, + /// Everything is ok in migration . + Ok, + /// Something error in migration . + Error, + /// Unknown status in migration . + Unknown, +} + +impl Default for TransStatus { + fn default() -> Self { + Self::Unknown + } +} + +impl std::fmt::Display for TransStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + TransStatus::Active => "Active", + TransStatus::VmConfig => "VmConfig", + TransStatus::Memory => "Memory", + TransStatus::State => "State", + TransStatus::Complete => "Complete", + TransStatus::Cancel => "Cancel", + TransStatus::Ok => "Ok", + TransStatus::Error => "Error", + TransStatus::Unknown => "Unknown", + } + ) + } +} + +/// Structure is used to save request protocol from source VM. +#[repr(C)] +#[derive(Default, Copy, Clone)] +pub struct Request { + /// Length of data to be sent. + pub length: u64, + /// The status need to sync to destination. + pub status: TransStatus, +} + +impl ByteCode for Request {} + +impl Request { + /// Send request message to socket file descriptor. + /// + /// # Arguments + /// + /// * `fd` - Socket file descriptor between source with destination. + /// * `status` - The transmission status of request. + /// * `length` - The length that data need to send. + /// + /// # Errors + /// + /// The socket file descriptor is broken. + pub fn send_msg(fd: &mut dyn Write, status: TransStatus, length: u64) -> Result<()> { + let request = Request { length, status }; + let data = + unsafe { from_raw_parts(&request as *const Self as *const u8, size_of::()) }; + fd.write_all(data) + .chain_err(|| format!("Failed to write request data {:?}", data))?; + + Ok(()) + } + + /// Receive request message from socket file descriptor. + /// + /// # Arguments + /// + /// * `fd` - Socket file descriptor between source with destination. + /// + /// # Errors + /// + /// The socket file descriptor is broken. + pub fn recv_msg(fd: &mut dyn Read) -> Result { + let mut request = Request::default(); + let data = unsafe { + from_raw_parts_mut(&mut request as *mut Request as *mut u8, size_of::()) + }; + fd.read_exact(data) + .chain_err(|| format!("Failed to read request data {:?}", data))?; + + Ok(request) + } +} + +/// Structure is used to save response protocol from destination VM. +#[repr(C)] +#[derive(Default, Copy, Clone)] +pub struct Response { + /// The status need to response to source. + pub status: TransStatus, +} + +impl ByteCode for Response {} + +impl Response { + /// Send response message to socket file descriptor. + /// + /// # Arguments + /// + /// * `fd` - Socket file descriptor between source and destination. + /// * `status` - The transmission status of response. + /// + /// # Errors + /// + /// The socket file descriptor is broken. + pub fn send_msg(fd: &mut dyn Write, status: TransStatus) -> Result<()> { + let response = Response { status }; + let data = + unsafe { from_raw_parts(&response as *const Self as *const u8, size_of::()) }; + fd.write_all(data) + .chain_err(|| format!("Failed to write response data {:?}", data))?; + + Ok(()) + } + + /// Receive response message from socket file descriptor. + /// + /// # Arguments + /// + /// * `fd` - Socket file descriptor between source and destination. + /// + /// # Errors + /// + /// The socket file descriptor is broken. + pub fn recv_msg(fd: &mut dyn Read) -> Result { + let mut response = Response::default(); + let data = unsafe { + from_raw_parts_mut(&mut response as *mut Response as *mut u8, size_of::()) + }; + fd.read_exact(data) + .chain_err(|| format!("Failed to read response data {:?}", data))?; + + Ok(response) + } + + /// Check the status from response is not OK. + pub fn is_err(&self) -> bool { + self.status != TransStatus::Ok + } +} + +/// Structure is used to save guest physical address and length of +/// memory block that needs to send. +#[repr(C)] +#[derive(Clone, Default)] +pub struct MemBlock { + /// Guest address. + pub gpa: u64, + /// Size of memory. + pub len: u64, +} + +/// Magic number for migration header. Those bytes represent "STRATOVIRT". +const MAGIC_NUMBER: [u8; 16] = [ + 0x53, 0x54, 0x52, 0x41, 0x54, 0x4f, 0x56, 0x49, 0x52, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +]; +const MAJOR_VERSION: u32 = 2; +const MINOR_VERSION: u32 = 2; +const CURRENT_VERSION: u32 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111; +const COMPAT_VERSION: u32 = CURRENT_VERSION; +#[cfg(target_arch = "x86_64")] +const EAX_VENDOR_INFO: u32 = 0x0; +/// The length of `MigrationHeader` part occupies bytes in snapshot file. +pub const HEADER_LENGTH: usize = 4096; + +/// Format type for migration. +/// Different file format will have different file layout. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum FileFormat { + Device, + MemoryFull, +} + +/// The endianness of byte order. +#[derive(Debug, Copy, Clone, PartialEq)] +enum EndianType { + Little = 1, + Big = 2, +} + +impl EndianType { + fn get_endian_type() -> EndianType { + if cfg!(target_endian = "big") { + EndianType::Big + } else { + EndianType::Little + } + } +} + +/// Get host cpu model as bytes. +#[cfg(target_arch = "x86_64")] +fn cpu_model() -> [u8; 16] { + use core::arch::x86_64::__cpuid_count; + + // Safe because we only use cpuid for cpu info in x86_64. + let result = unsafe { __cpuid_count(EAX_VENDOR_INFO, 0) }; + let vendor_slice = [result.ebx, result.edx, result.ecx]; + + // Safe because we known those brand string length. + let vendor_array = unsafe { + let brand_string_start = vendor_slice.as_ptr() as *const u8; + std::slice::from_raw_parts(brand_string_start, 3 * 4) + }; + + let mut buffer = [0u8; 16]; + if vendor_array.len() > 16 { + buffer.copy_from_slice(&vendor_array[0..15]); + } else { + buffer[0..vendor_array.len()].copy_from_slice(vendor_array); + } + buffer +} + +/// Structure used to mark some message in migration. +#[derive(Copy, Clone, Debug)] +pub struct MigrationHeader { + /// Magic number for migration file/stream. + magic_num: [u8; 16], + /// Current version of migration. + #[allow(dead_code)] + current_version: u32, + /// Compatible version of migration. + compat_version: u32, + /// Arch identifier. + arch: [u8; 8], + /// Endianness of byte order. + byte_order: EndianType, + /// The type of hypervisor. + #[allow(dead_code)] + hypervisor_type: [u8; 8], + /// The version of hypervisor. + hypervisor_version: u32, + /// The type of CPU model. + #[cfg(target_arch = "x86_64")] + cpu_model: [u8; 16], + /// Operation system type. + os_type: [u8; 8], + /// File format of migration file/stream. + pub format: FileFormat, + /// The length of `DeviceStateDesc`. + pub desc_len: usize, +} + +impl ByteCode for MigrationHeader {} + +impl Default for MigrationHeader { + fn default() -> Self { + MigrationHeader { + magic_num: MAGIC_NUMBER, + current_version: CURRENT_VERSION, + compat_version: COMPAT_VERSION, + format: FileFormat::Device, + byte_order: EndianType::Little, + hypervisor_type: [b'k', b'v', b'm', b'0', b'0', b'0', b'0', b'0'], + hypervisor_version: Kvm::new().unwrap().get_api_version() as u32, + #[cfg(target_arch = "x86_64")] + cpu_model: cpu_model(), + #[cfg(target_os = "linux")] + os_type: [b'l', b'i', b'n', b'u', b'x', b'0', b'0', b'0'], + #[cfg(target_arch = "x86_64")] + arch: [b'x', b'8', b'6', b'_', b'6', b'4', b'0', b'0'], + #[cfg(target_arch = "aarch64")] + arch: [b'a', b'a', b'r', b'c', b'h', b'6', b'4', b'0'], + desc_len: 0, + } + } +} + +impl MigrationHeader { + /// Check parsed `MigrationHeader` is illegal or not. + pub fn check_header(&self) -> Result<()> { + if self.magic_num != MAGIC_NUMBER { + return Err(ErrorKind::HeaderItemNotFit("Magic_number".to_string()).into()); + } + + if self.compat_version > CURRENT_VERSION { + return Err(ErrorKind::VersionNotFit(self.compat_version, CURRENT_VERSION).into()); + } + + #[cfg(target_arch = "x86_64")] + let current_arch = [b'x', b'8', b'6', b'_', b'6', b'4', b'0', b'0']; + #[cfg(target_arch = "aarch64")] + let current_arch = [b'a', b'a', b'r', b'c', b'h', b'6', b'4', b'0']; + if self.arch != current_arch { + return Err(ErrorKind::HeaderItemNotFit("Arch".to_string()).into()); + } + + if self.byte_order != EndianType::get_endian_type() { + return Err(ErrorKind::HeaderItemNotFit("Byte order".to_string()).into()); + } + + #[cfg(target_arch = "x86_64")] + if self.cpu_model != cpu_model() { + return Err(ErrorKind::HeaderItemNotFit("Cpu model".to_string()).into()); + } + + #[cfg(target_os = "linux")] + let current_os_type = [b'l', b'i', b'n', b'u', b'x', b'0', b'0', b'0']; + if self.os_type != current_os_type { + return Err(ErrorKind::HeaderItemNotFit("Os type".to_string()).into()); + } + + let current_kvm_version = Kvm::new().unwrap().get_api_version() as u32; + if current_kvm_version < self.hypervisor_version { + return Err(ErrorKind::HeaderItemNotFit("Hypervisor version".to_string()).into()); + } + + Ok(()) + } +} /// Version check result enum. #[derive(PartialEq, Debug)] @@ -60,10 +475,10 @@ pub trait StateTransfer { fn get_device_alias(&self) -> u64; } -/// The structure to describe `DeviceState` structure with version messege. +/// The structure to describe `DeviceState` structure with version message. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DeviceStateDesc { - /// Device type identfy. + /// Device type identify. pub name: String, /// Alias for device type. pub alias: u64, @@ -121,7 +536,7 @@ impl DeviceStateDesc { } /// Check padding from a device state descriptor to another version device state - /// descriptor. The padding will be added tinto current_slice for `DeviceState`. + /// descriptor. The padding will be added into current_slice for `DeviceState`. /// /// # Arguments /// @@ -150,7 +565,7 @@ impl DeviceStateDesc { Ok(()) } - /// Check device state version descriptor version messege. + /// Check device state version descriptor version message. /// If version is same, return enum `Same`. /// If version is not same but fit, return enum `Compat`. /// if version is not fit, return enum `Mismatch`. @@ -169,10 +584,127 @@ impl DeviceStateDesc { #[cfg(test)] pub mod tests { - use super::{DeviceStateDesc, FieldDesc, StateTransfer, VersionCheck}; + use super::*; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; + #[test] + fn test_normal_transfer() { + let mut status = MigrationStatus::None; + + // None to Setup. + assert!(status.transfer(MigrationStatus::Setup).is_ok()); + status = status.transfer(MigrationStatus::Setup).unwrap(); + + // Setup to Active. + assert!(status.transfer(MigrationStatus::Active).is_ok()); + status = status.transfer(MigrationStatus::Active).unwrap(); + + // Active to Completed. + assert!(status.transfer(MigrationStatus::Completed).is_ok()); + status = status.transfer(MigrationStatus::Completed).unwrap(); + + // Completed to Active. + assert!(status.transfer(MigrationStatus::Active).is_ok()); + status = status.transfer(MigrationStatus::Active).unwrap(); + + // Any to Failed. + assert!(status.transfer(MigrationStatus::Failed).is_ok()); + status = status.transfer(MigrationStatus::Failed).unwrap(); + + // Failed to Active. + assert!(status.transfer(MigrationStatus::Active).is_ok()); + status = status.transfer(MigrationStatus::Active).unwrap(); + + // Any to Failed. + assert!(status.transfer(MigrationStatus::Failed).is_ok()); + status = status.transfer(MigrationStatus::Failed).unwrap(); + + // Failed to Setup. + assert!(status.transfer(MigrationStatus::Setup).is_ok()); + status = status.transfer(MigrationStatus::Setup).unwrap(); + + assert_eq!(status, MigrationStatus::Setup); + } + + #[test] + fn test_abnormal_transfer_with_error() { + let mut status = MigrationStatus::None; + + // None to Active. + if let Err(e) = status.transfer(MigrationStatus::Active) { + assert_eq!( + e.to_string(), + format!( + "Failed to transfer migration status from {} to {}.", + MigrationStatus::None, + MigrationStatus::Active + ) + ); + } else { + assert!(false) + } + status = status.transfer(MigrationStatus::Setup).unwrap(); + + // Setup to Complete. + if let Err(e) = status.transfer(MigrationStatus::Completed) { + assert_eq!( + e.to_string(), + format!( + "Failed to transfer migration status from {} to {}.", + MigrationStatus::Setup, + MigrationStatus::Completed + ) + ); + } else { + assert!(false) + } + status = status.transfer(MigrationStatus::Active).unwrap(); + + // Active to Setup. + if let Err(e) = status.transfer(MigrationStatus::Setup) { + assert_eq!( + e.to_string(), + format!( + "Failed to transfer migration status from {} to {}.", + MigrationStatus::Active, + MigrationStatus::Setup + ) + ); + } else { + assert!(false) + } + status = status.transfer(MigrationStatus::Completed).unwrap(); + + // Completed to Setup. + if let Err(e) = status.transfer(MigrationStatus::Setup) { + assert_eq!( + e.to_string(), + format!( + "Failed to transfer migration status from {} to {}.", + MigrationStatus::Completed, + MigrationStatus::Setup + ) + ); + } else { + assert!(false) + } + + // Complete to failed. + if let Err(e) = status.transfer(MigrationStatus::Failed) { + assert_eq!( + e.to_string(), + format!( + "Failed to transfer migration status from {} to {}.", + MigrationStatus::Completed, + MigrationStatus::Failed + ) + ); + } else { + assert!(false) + } + } + #[derive(Default)] // A simple device version 1. pub struct DeviceV1 { @@ -556,4 +1088,14 @@ pub mod tests { assert_eq!(device_v5.state.rii, device_v2.state.iir as u64); } + + #[test] + fn test_check_header() { + if !Kvm::new().is_ok() { + return; + } + + let header = MigrationHeader::default(); + assert_eq!(header.check_header().is_ok(), true); + } } diff --git a/migration/src/status.rs b/migration/src/status.rs deleted file mode 100644 index 382622bd7..000000000 --- a/migration/src/status.rs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use crate::errors::{ErrorKind, Result}; - -/// This status for migration in migration process. -/// -/// # Notes -/// -/// State transfer: -/// None -----------> Setup: set up migration resource. -/// Setup ----------> Active: start to migrate. -/// Active ---------> Completed: migrate completed successfully. -/// Completed ------> Active: start to migrate again after a successfully migration. -/// Failed ---------> Setup: reset migration resource. -/// Any ------------> Failed: Something wrong in migration. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum MigrationStatus { - /// Migration resource is not prepared all - None = 0, - /// Migration resource(desc_db, device_instance, ...) is setup. - Setup = 1, - /// In migration or incoming migrating. - Active = 2, - /// Migration finished. - Completed = 3, - /// Migration failed. - Failed = 4, -} - -impl std::fmt::Display for MigrationStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - MigrationStatus::None => "none", - MigrationStatus::Setup => "setup", - MigrationStatus::Active => "active", - MigrationStatus::Completed => "completed", - MigrationStatus::Failed => "failed", - } - ) - } -} - -impl MigrationStatus { - pub fn transfer(self, new_status: MigrationStatus) -> Result { - match self { - MigrationStatus::None => match new_status { - MigrationStatus::Setup => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), - }, - MigrationStatus::Setup => match new_status { - MigrationStatus::Active | MigrationStatus::Failed => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), - }, - MigrationStatus::Active => match new_status { - MigrationStatus::Completed | MigrationStatus::Failed => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), - }, - MigrationStatus::Completed => match new_status { - MigrationStatus::Active => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), - }, - MigrationStatus::Failed => match new_status { - MigrationStatus::Setup | MigrationStatus::Active => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_normal_transfer() { - let mut status = MigrationStatus::None; - - // None to Setup. - assert!(status.transfer(MigrationStatus::Setup).is_ok()); - status = status.transfer(MigrationStatus::Setup).unwrap(); - - // Setup to Active. - assert!(status.transfer(MigrationStatus::Active).is_ok()); - status = status.transfer(MigrationStatus::Active).unwrap(); - - // Active to Completed. - assert!(status.transfer(MigrationStatus::Completed).is_ok()); - status = status.transfer(MigrationStatus::Completed).unwrap(); - - // Completed to Active. - assert!(status.transfer(MigrationStatus::Active).is_ok()); - status = status.transfer(MigrationStatus::Active).unwrap(); - - // Any to Failed. - assert!(status.transfer(MigrationStatus::Failed).is_ok()); - status = status.transfer(MigrationStatus::Failed).unwrap(); - - // Failed to Active. - assert!(status.transfer(MigrationStatus::Active).is_ok()); - status = status.transfer(MigrationStatus::Active).unwrap(); - - // Any to Failed. - assert!(status.transfer(MigrationStatus::Failed).is_ok()); - status = status.transfer(MigrationStatus::Failed).unwrap(); - - // Failed to Setup. - assert!(status.transfer(MigrationStatus::Setup).is_ok()); - status = status.transfer(MigrationStatus::Setup).unwrap(); - - assert_eq!(status, MigrationStatus::Setup); - } - - #[test] - fn test_abnormal_transfer_with_error() { - let mut status = MigrationStatus::None; - - // None to Active. - if let Err(e) = status.transfer(MigrationStatus::Active) { - assert_eq!( - e.to_string(), - format!( - "Failed to transfer migration status from {} to {}.", - MigrationStatus::None, - MigrationStatus::Active - ) - ); - } else { - assert!(false) - } - status = status.transfer(MigrationStatus::Setup).unwrap(); - - // Setup to Complete. - if let Err(e) = status.transfer(MigrationStatus::Completed) { - assert_eq!( - e.to_string(), - format!( - "Failed to transfer migration status from {} to {}.", - MigrationStatus::Setup, - MigrationStatus::Completed - ) - ); - } else { - assert!(false) - } - status = status.transfer(MigrationStatus::Active).unwrap(); - - // Active to Setup. - if let Err(e) = status.transfer(MigrationStatus::Setup) { - assert_eq!( - e.to_string(), - format!( - "Failed to transfer migration status from {} to {}.", - MigrationStatus::Active, - MigrationStatus::Setup - ) - ); - } else { - assert!(false) - } - status = status.transfer(MigrationStatus::Completed).unwrap(); - - // Completed to Setup. - if let Err(e) = status.transfer(MigrationStatus::Setup) { - assert_eq!( - e.to_string(), - format!( - "Failed to transfer migration status from {} to {}.", - MigrationStatus::Completed, - MigrationStatus::Setup - ) - ); - } else { - assert!(false) - } - - // Complete to failed. - if let Err(e) = status.transfer(MigrationStatus::Failed) { - assert_eq!( - e.to_string(), - format!( - "Failed to transfer migration status from {} to {}.", - MigrationStatus::Completed, - MigrationStatus::Failed - ) - ); - } else { - assert!(false) - } - } -} -- Gitee From 1de3b0b221f9562f8305de91bf6101b5de8d4355 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 3 Aug 2022 15:37:20 +0800 Subject: [PATCH 0093/2187] migration: Set serialization and deserialization for VmConfig It needs to send `VmConfig` data between source and destination vm during migration, then it can used to check virtual machine configuration. Signed-off-by: Xinle.Guo --- machine_manager/src/config/chardev.rs | 8 ++++---- machine_manager/src/config/iothread.rs | 4 +++- machine_manager/src/config/mod.rs | 6 ++++-- machine_manager/src/config/pci.rs | 3 ++- machine_manager/src/config/rng.rs | 3 ++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index e525f5c5e..a15ce4270 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -23,7 +23,7 @@ const MAX_GUEST_CID: u64 = 4_294_967_295; const MIN_GUEST_CID: u64 = 3; /// Charecter device options. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ChardevType { Stdio, Pty, @@ -43,7 +43,7 @@ pub struct VirtioConsole { } /// Config structure for character device. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChardevConfig { pub id: String, pub backend: ChardevType, @@ -223,7 +223,7 @@ impl VmConfig { } /// Config structure for serial. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SerialConfig { pub chardev: ChardevConfig, } @@ -322,7 +322,7 @@ pub fn parse_vsock(vsock_config: &str) -> Result { Ok(vsock) } -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct VirtioSerialInfo { pub id: String, pub pci_bdf: Option, diff --git a/machine_manager/src/config/iothread.rs b/machine_manager/src/config/iothread.rs index 4d615d229..db3c7966d 100644 --- a/machine_manager/src/config/iothread.rs +++ b/machine_manager/src/config/iothread.rs @@ -10,13 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use serde::{Deserialize, Serialize}; + use super::errors::{ErrorKind, Result}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH}; const MAX_IOTHREAD_NUM: usize = 8; /// Config structure for iothread. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct IothreadConfig { pub id: String, } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 29c176d9d..c0344cf31 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -109,6 +109,8 @@ use std::any::Any; use std::collections::HashMap; use std::str::FromStr; +use serde::{Deserialize, Serialize}; + use error_chain::bail; use log::error; #[cfg(target_arch = "aarch64")] @@ -122,7 +124,7 @@ pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; pub const MAX_NODES: u32 = 128; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ObjConfig { Rng(RngObjConfig), Zone(MemZoneConfig), @@ -149,7 +151,7 @@ fn parse_rng_obj(object_args: &str) -> Result { } /// This main config structure for Vm, contains Vm's basic configuration and devices. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct VmConfig { pub guest_name: String, pub machine_config: MachineConfig, diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 9c6db3c48..edfa7f257 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use error_chain::bail; +use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; @@ -18,7 +19,7 @@ use crate::config::ExBool; /// Basic information of pci devices such as bus number, /// slot number and function number. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct PciBdf { /// Bus number pub bus: String, diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index e1caff629..7f5d460c1 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use error_chain::bail; +use serde::{Deserialize, Serialize}; use super::errors::{ErrorKind, Result}; use super::{pci_args_check, ObjConfig}; @@ -19,7 +20,7 @@ use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; const MIN_BYTES_PER_SEC: u64 = 64; const MAX_BYTES_PER_SEC: u64 = 1_000_000_000; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct RngObjConfig { pub id: String, pub filename: String, -- Gitee From 7eb10199acab8b42b09187461ac2e00f7ab5cba9 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 15:31:39 +0800 Subject: [PATCH 0094/2187] migration: refactoring `MigrationManager` struct and functions 1. Move generic functions to `general.rs` file. 2. Replace `entry` element with `Vmm` element. The `Vmm` contains vCPU, memory, devices, GIC group, kvm instance. This reconstruction helps to reduce the complexity of finding migration instance. Signed-off-by: Xinle.Guo --- migration/Cargo.toml | 5 +- migration/src/general.rs | 244 +++++++++++++++++++ migration/src/lib.rs | 48 ++-- migration/src/manager.rs | 486 +++++++++++++++++--------------------- migration/src/snapshot.rs | 275 ++++++++++----------- 5 files changed, 614 insertions(+), 444 deletions(-) create mode 100644 migration/src/general.rs diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 1cdd91ca8..521da0015 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -5,13 +5,16 @@ authors = ["Huawei StratoVirt Team"] edition = "2018" [dependencies] -util = {path = "../util"} error-chain = "0.12.4" kvm-ioctls = "0.6.0" +kvm-bindings = ">=0.3.0" serde = { version = ">=1.0.114", features = ["derive"] } serde_json = "1.0.55" once_cell = "1.9.0" log = "0.4.8" +util = {path = "../util"} +hypervisor = { path = "../hypervisor" } +machine_manager = { path = "../machine_manager" } [dev-dependencies] migration_derive = { path = "../migration_derive" } diff --git a/migration/src/general.rs b/migration/src/general.rs new file mode 100644 index 000000000..56cda1305 --- /dev/null +++ b/migration/src/general.rs @@ -0,0 +1,244 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::{hash_map::DefaultHasher, HashMap}; +use std::hash::{Hash, Hasher}; +use std::io::{Read, Write}; +use std::mem::size_of; + +use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::manager::{Instance, MIGRATION_MANAGER}; +use crate::protocol::{ + DeviceStateDesc, FileFormat, MigrationHeader, MigrationStatus, VersionCheck, HEADER_LENGTH, +}; +use crate::MigrationManager; +use util::{byte_code::ByteCode, unix::host_page_size}; + +impl MigrationManager { + /// Write `MigrationHeader` to `Write` trait object as bytes. + /// `MigrationHeader` will occupy the first 4096 bytes in snapshot file. + /// + /// # Arguments + /// + /// * `file_format` - confirm snapshot file format. + /// * `fd` - The `Write` trait object to write header message. + pub fn save_header(file_format: Option, fd: &mut dyn Write) -> Result<()> { + let mut header = MigrationHeader::default(); + if let Some(format) = file_format { + header.format = format; + header.desc_len = match format { + FileFormat::Device => Self::desc_db_len()?, + FileFormat::MemoryFull => (host_page_size() as usize) * 2 - HEADER_LENGTH, + }; + } else { + header.desc_len = Self::desc_db_len()?; + } + + let mut input_slice = [0u8; HEADER_LENGTH]; + input_slice[0..size_of::()].copy_from_slice(header.as_bytes()); + fd.write(&input_slice) + .chain_err(|| "Failed to save migration header")?; + + Ok(()) + } + + /// Restore and parse `MigrationHeader` from `Read` object. + /// + /// # Arguments + /// + /// * `fd` - The `Read` trait object to read header message. + pub fn restore_header(fd: &mut dyn Read) -> Result { + let mut header_bytes = [0u8; size_of::()]; + fd.read_exact(&mut header_bytes)?; + + let mut place_holder = [0u8; HEADER_LENGTH - size_of::()]; + fd.read_exact(&mut place_holder)?; + + Ok(*MigrationHeader::from_bytes(&header_bytes) + .ok_or(ErrorKind::FromBytesError("HEADER"))?) + } + + /// Write all `DeviceStateDesc` in `desc_db` hashmap to `Write` trait object. + pub fn save_desc_db(fd: &mut dyn Write) -> Result<()> { + let length = Self::desc_db_len()?; + let mut buffer = Vec::new(); + buffer.resize(length, 0); + let mut start = 0; + + let desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); + for (_, desc) in desc_db.iter() { + let desc_str = serde_json::to_string(desc)?; + let desc_bytes = desc_str.as_bytes(); + buffer[start..start + desc_bytes.len()].copy_from_slice(desc_bytes); + start += desc_bytes.len(); + } + fd.write_all(&buffer) + .chain_err(|| "Failed to write descriptor message.")?; + + Ok(()) + } + + /// Load and parse device state descriptor from `Read` trait object. Save as a Hashmap. + pub fn restore_desc_db( + fd: &mut dyn Read, + desc_length: usize, + ) -> Result> { + let mut desc_buffer = Vec::new(); + desc_buffer.resize(desc_length, 0); + fd.read_exact(&mut desc_buffer)?; + let mut snapshot_desc_db = HashMap::::new(); + + let deserializer = serde_json::Deserializer::from_slice(&desc_buffer); + for desc in deserializer.into_iter::() { + let device_desc: DeviceStateDesc = match desc { + Ok(desc) => desc, + Err(_) => break, + }; + snapshot_desc_db.insert(device_desc.alias, device_desc); + } + + Ok(snapshot_desc_db) + } + + /// Get vm state and check its version can be match. + /// + /// # Arguments + /// + /// * fd - The `Read` trait object. + /// * snap_desc_db - snap_desc_db - snapshot state descriptor. + pub fn check_vm_state( + fd: &mut dyn Read, + desc_db: &HashMap, + ) -> Result<(Vec, u64)> { + let mut instance = Instance::default(); + fd.read_exact(unsafe { + std::slice::from_raw_parts_mut( + &mut instance as *mut Instance as *mut u8, + size_of::() as usize, + ) + }) + .chain_err(|| "Failed to read instance of object")?; + + let locked_desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); + let snap_desc = desc_db + .get(&instance.object) + .chain_err(|| "Failed to get instance object")?; + let current_desc = locked_desc_db + .get(&snap_desc.name) + .chain_err(|| "Failed to get snap_desc name")?; + + let mut state_data = Vec::new(); + state_data.resize(snap_desc.size as usize, 0); + fd.read_exact(&mut state_data)?; + + match current_desc.check_version(snap_desc) { + VersionCheck::Same => {} + VersionCheck::Compat => { + current_desc + .add_padding(snap_desc, &mut state_data) + .chain_err(|| "Failed to transform snapshot data version")?; + } + VersionCheck::Mismatch => { + return Err(ErrorKind::VersionNotFit( + current_desc.compat_version, + snap_desc.current_version, + ) + .into()) + } + } + + Ok((state_data, instance.name)) + } + + /// Get `Device`'s alias from device type string. + /// + /// # Argument + /// + /// * `device_type` - The type string of device instance. + pub fn get_desc_alias(device_type: &str) -> Option { + Some(translate_id(device_type)) + } + + /// Return `desc_db` value len(0 restored as `serde_json`) + pub fn desc_db_len() -> Result { + let mut db_data_len = 0; + let desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); + for (_, desc) in desc_db.iter() { + let desc_str = serde_json::to_string(desc)?; + db_data_len += desc_str.as_bytes().len(); + } + + Ok(db_data_len) + } + + /// Get current migration status for migration manager. + pub fn status() -> MigrationStatus { + *MIGRATION_MANAGER.status.read().unwrap() + } + + /// Set a new migration status for migration manager. + /// + /// # Arguments + /// + /// * `new_status`: new migration status, the transform must be illegal. + pub fn set_status(new_status: MigrationStatus) -> Result<()> { + let mut status = MIGRATION_MANAGER.status.write().unwrap(); + *status = status.transfer(new_status)?; + + Ok(()) + } + + /// Check whether current migration status is active. + pub fn is_active() -> bool { + Self::status() == MigrationStatus::Active + } + + /// Check whether current migration status is cancel. + pub fn is_canceled() -> bool { + Self::status() == MigrationStatus::Canceled + } +} + +pub trait Lifecycle { + /// Pause VM during migration. + fn pause() -> Result<()> { + if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { + locked_vm.lock().unwrap().pause(); + } + + Ok(()) + } + + /// Resume devices during migration. + fn resume() -> Result<()> { + let locked_devices = &MIGRATION_MANAGER.vmm.read().unwrap().devices; + for (_, device) in locked_devices.iter() { + device.lock().unwrap().resume()?; + } + + Ok(()) + } +} + +impl Lifecycle for MigrationManager {} + +/// Converting device instance to unique ID of u64 bit. +/// Because name of String type in `Instance` does not implement Copy trait. +/// +/// # Arguments +/// +/// * `dev_id` - The device id. +pub fn translate_id(dev_id: &str) -> u64 { + let mut hash = DefaultHasher::new(); + dev_id.hash(&mut hash); + hash.finish() +} diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 491a8d01d..ecbf9fec9 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -14,22 +14,27 @@ //! //! Offer snapshot and migration interface for VM. -mod device_state; -mod header; -mod manager; -mod snapshot; -mod status; +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; -pub use device_state::{DeviceStateDesc, FieldDesc, StateTransfer}; -pub use manager::{MigrationHook, MigrationManager, MigrationRestoreOrder}; -pub use status::MigrationStatus; +pub mod general; +pub mod manager; +pub mod protocol; +pub mod snapshot; -pub mod errors { - use error_chain::error_chain; +pub use manager::{MigrationHook, MigrationManager}; +pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; - use super::status::MigrationStatus; +pub mod errors { + use super::protocol::MigrationStatus; error_chain! { + links { + Util(util::errors::Error, util::errors::ErrorKind); + Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind); + } foreign_links { Io(std::io::Error); Ioctl(kvm_ioctls::Error); @@ -57,8 +62,23 @@ pub mod errors { SaveVmMemoryErr(e: String) { display("Failed to save vm memory: {}", e) } - RestoreVmMemoryErr { - display("Failed to restore vm memory.") + RestoreVmMemoryErr(e: String) { + display("Failed to restore vm memory: {}", e) + } + SendVmMemoryErr(e: String) { + display("Failed to send vm memory: {}", e) + } + RecvVmMemoryErr(e: String) { + display("Failed to receive vm memory: {}", e) + } + ResponseErr { + display("Response error") + } + MigrationStatusErr(source: String, destination: String) { + display("Migration status mismatch: source {}, destination {}.", source, destination) + } + MigrationConfigErr(config_type: String, source: String, destination: String) { + display("Migration config {} mismatch: source {}, destination {}.", config_type, source, destination) } InvalidSnapshotPath { display("Invalid snapshot path for restoring snapshot") diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 2ca89a258..c4644c3f9 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -10,44 +10,32 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::{debug, info}; -use std::cmp; -use std::collections::hash_map::DefaultHasher; use std::collections::HashMap; use std::fs::File; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; use std::io::{Read, Write}; use std::sync::{Arc, Mutex, RwLock}; +use std::time::Instant; -use super::device_state::{DeviceStateDesc, StateTransfer}; -use super::errors::{Result, ResultExt}; -use super::status::MigrationStatus; use once_cell::sync::Lazy; + +use crate::errors::{Result, ResultExt}; +use crate::general::translate_id; +use crate::migration::DirtyBitmap; +use crate::protocol::{DeviceStateDesc, MemBlock, MigrationStatus, StateTransfer}; +use machine_manager::config::VmConfig; +use machine_manager::machine::MachineLifecycle; use util::byte_code::ByteCode; -/// Glocal MigrationManager to manage all migration combined interface. +/// Global MigrationManager to manage all migration combined interface. pub(crate) static MIGRATION_MANAGER: Lazy = Lazy::new(|| MigrationManager { - entry: Arc::new(RwLock::new([ - Vec::<(String, MigrationEntry)>::new(), - Vec::<(String, MigrationEntry)>::new(), - Vec::<(String, MigrationEntry)>::new(), - ])), + vmm: Arc::new(RwLock::new(Vmm::default())), desc_db: Arc::new(RwLock::new(HashMap::::new())), status: Arc::new(RwLock::new(MigrationStatus::None)), + vmm_bitmaps: Arc::new(RwLock::new(HashMap::new())), + limit: Arc::new(RwLock::new(MigrationLimit::default())), }); -/// Used to map Device id from String to u64 only. -/// Because instance_id in InstanceId can't be String for it has no Copy trait. -/// -/// # Arguments -/// -/// * `dev_id` - The device id. -pub fn id_remap(dev_id: &str) -> u64 { - let mut hash = DefaultHasher::new(); - dev_id.hash(&mut hash); - hash.finish() -} - /// A hook for `Device` to save device state to `Write` object and load device /// from `[u8]` slice. /// @@ -56,135 +44,171 @@ pub fn id_remap(dev_id: &str) -> u64 { /// This trait is a symbol of device's migration capabilities. All /// migratable device must implement this trait. pub trait MigrationHook: StateTransfer { - /// Pre save device state as `[u8]` with device's `InstanceId` to a `Write` + /// Save device state as `[u8]` with device's `InstanceId` to a `Write` /// trait object. /// /// # Arguments /// /// * `id` - This unique id to represent a single device. It can be treated /// as `object_id` in `InstanceId`. - /// * `writer` - The `Write` trait object to store or receive data. - fn pre_save(&self, id: &str, writer: &mut dyn Write) -> Result<()> { + /// * `fd` - The `Write` trait object to save device data. + fn save_device(&self, id: u64, fd: &mut dyn Write) -> Result<()> { let state_data = self .get_state_vec() .chain_err(|| "Failed to get device state")?; - let device_alias = self.get_device_alias(); - let instance_id = InstanceId { - object_type: device_alias, - object_id: id_remap(id), - }; - - writer - .write_all(instance_id.as_bytes()) - .chain_err(|| "Failed to write instance id.")?; - writer - .write_all(&state_data) + fd.write_all( + Instance { + name: id, + object: self.get_device_alias(), + } + .as_bytes(), + ) + .chain_err(|| "Failed to write instance id.")?; + fd.write_all(&state_data) .chain_err(|| "Failed to write device state")?; Ok(()) } - /// Pre load device state from `[u8]` to `Device`. + /// Restore device state from `[u8]` to `Device`. /// /// # Arguments /// /// * `state` - The raw data which can be recovered to `DeviceState`. - /// * `memory` - The file of memory data, this parameter is optional. - fn pre_load(&self, state: &[u8], _memory: Option<&File>) -> Result<()> { + fn restore_device(&self, state: &[u8]) -> Result<()> { self.set_state(state) } - /// Pre load device state from `[u8]` to mutable `Device`. + /// Restore device state from `[u8]` to mutable `Device`. /// /// # Arguments /// /// * `state` - The raw data which can be recovered to `DeviceState`. - /// * `memory` - The file of memory data, this parameter is optional. - fn pre_load_mut(&mut self, state: &[u8], _memory: Option<&File>) -> Result<()> { + fn restore_mut_device(&mut self, state: &[u8]) -> Result<()> { self.set_state_mut(state) } + /// Save memory state to `Write` trait. + /// + /// # Arguments + /// + /// * _fd - The `Write` trait object to save memory data. + fn save_memory(&self, _fd: &mut dyn Write) -> Result<()> { + Ok(()) + } + + /// Restore memory state from memory. + /// + /// # Arguments + /// + /// * _memory - The file of memory data, this parameter is optional. + /// * _state - device state from memory. + fn restore_memory(&self, _memory: Option<&File>, _state: &[u8]) -> Result<()> { + Ok(()) + } + + /// Send memory data to `Write` trait. + /// + /// # Arguments + /// + /// * _fd - The `Write` trait object to send memory data. + /// * _range - the memory block range needs to send. + fn send_memory(&self, _fd: &mut dyn Write, _range: MemBlock) -> Result<()> { + Ok(()) + } + + /// Receive memory data from `Read`. + /// + /// # Arguments + /// + /// * _fd - The `Read` trait object to receive memory data. + /// * _range - the memory block range needs to send. + fn recv_memory(&self, _fd: &mut dyn Read, _range: MemBlock) -> Result<()> { + Ok(()) + } + /// Resume the recover device. /// /// # Notes /// - /// For some device, such as virtio-device or vhost-device, after recover - /// device state, it need a step to wake up device to running. + /// For some device, such as virtio-device or vhost-device, after restore + /// device state, it need a step to wake up device to run. fn resume(&mut self) -> Result<()> { Ok(()) } } -/// The instance id to represent a single object in VM. +/// The instance represents a single object in VM. /// /// # Notes /// -/// Instance_id contains two parts: One part is device type to describe the -/// type of a object, another is unique id for every object. -/// -/// ## object_type -/// The object_type for a object is the order which type is registered to -/// `desc_db`. It's associated with object name. -/// -/// ## object_id -/// -/// The object id should reflect the unique device or ram_region instance in -/// a VM. Is will be set delying on device create order. +/// Instance contains two parts: One part is name for every object. +/// another is the type of a object. #[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug, Default)] -pub struct InstanceId { - /// The type of object. - pub object_type: u64, - /// The unique id of object. - pub object_id: u64, +pub struct Instance { + /// The name reflects the unique device or ram_region instance in a VM. + pub name: u64, + /// The object is the type which is registered to `desc_db`. + pub object: u64, } -impl ByteCode for InstanceId {} - -/// A entry to every migratable device to call on migration interface. -pub enum MigrationEntry { - /// Safe device instance with migration interface. - Safe(Arc), - /// Mutex device instance with migration interface. - Mutex(Arc>), - /// Safe memory instance with migration interface. - Memory(Arc), +impl ByteCode for Instance {} + +/// Including all components of a Vmm. +#[derive(Default)] +pub struct Vmm { + /// Vm config + pub config: VmConfig, + /// Trait to represent a Vm. + pub vm: Option>>, + /// Trait to represent CPU devices. + pub cpus: HashMap>, + /// Trait to represent memory devices. + pub memory: Option>, + /// Trait to represent virtio-pci devices. + pub devices: HashMap>>, + #[cfg(target_arch = "aarch64")] + /// Trait to represent GIC devices(GICv3, GICv3 ITS). + pub gic_group: HashMap>, + #[cfg(target_arch = "x86_64")] + /// Trait to represent kvm device. + pub kvm: Option>, } -/// Ensure the recovery sequence of different devices based on priorities. -/// At present, we need to ensure that the state recovery of the gic device -/// must be after the cpu, so different priorities are defined. -#[derive(Debug)] -pub enum MigrationRestoreOrder { - Default = 0, - Gicv3 = 1, - Gicv3Its = 2, - Max = 3, +/// Limit of migration. +pub struct MigrationLimit { + /// Start time of each iteration. + pub iteration_start_time: Instant, + /// Virtual machine downtime. + pub limit_downtime: u64, + /// Max number of iterations during iteratively sending dirty memory. + pub max_dirty_iterations: u16, } -impl From for u16 { - fn from(order: MigrationRestoreOrder) -> u16 { - match order { - MigrationRestoreOrder::Default => 0, - MigrationRestoreOrder::Gicv3 => 1, - MigrationRestoreOrder::Gicv3Its => 2, - _ => 3, +impl Default for MigrationLimit { + fn default() -> Self { + Self { + iteration_start_time: Instant::now(), + limit_downtime: 50, + max_dirty_iterations: 30, } } } -/// The entry list size is the same as the MigrationRestoreOrder number -type MigrationEntryList = [Vec<(String, MigrationEntry)>; 3]; - /// This structure is to manage all resource during migration. /// It is also the only way to call on `MIGRATION_MANAGER`. pub struct MigrationManager { - /// The map offers the device_id and combined migratable device entry. - pub(crate) entry: Arc>, + /// The vmm can manage all VM related components + pub vmm: Arc>, /// The map offers the device type and its device state describe structure. - pub(crate) desc_db: Arc>>, + pub desc_db: Arc>>, /// The status of migration work. - status: Arc>, + pub status: Arc>, + /// vmm dirty bitmaps. + pub vmm_bitmaps: Arc>>, + /// Limiting elements of migration. + pub limit: Arc>, } impl MigrationManager { @@ -196,255 +220,171 @@ impl MigrationManager { fn register_device_desc(desc: DeviceStateDesc) { let mut desc_db = MIGRATION_MANAGER.desc_db.write().unwrap(); if !desc_db.contains_key(&desc.name) { + info!("Register device name: {}, desc: {:?}", desc.name, desc); desc_db.insert(desc.name.clone(), desc); } } - /// Register safe device instance to entry hashmap with instance id. + /// Register vm config to vmm. /// /// # Arguments /// - /// * `device_desc` - The `DeviceStateDesc` of device instance. - /// * `entry` - Device instance with migratable interface. - /// * `restore_order` - device restore order. - pub fn register_device_instance( - device_desc: DeviceStateDesc, - device_entry: Arc, - restore_order: MigrationRestoreOrder, - ) where - T: MigrationHook + Sync + Send + 'static, - { - let name = device_desc.name.clone(); - Self::register_device_desc(device_desc); + /// * `config` - The configuration from virtual machine. + pub fn register_vm_config(config: &mut VmConfig) { + MIGRATION_MANAGER.vmm.write().unwrap().config = config.clone(); + } - let entry = MigrationEntry::Safe(device_entry); - info!( - "Register device instance: id {} order {:?}", - &name, &restore_order - ); - MigrationManager::insert_entry(name, restore_order.into(), entry, true); + /// Register vm instance to vmm. + /// + /// # Arguments + /// + /// * `vm` - vm instance with MachineLifecycle trait. + pub fn register_vm_instance(vm: Arc>) + where + T: MachineLifecycle + Sync + Send + 'static, + { + MIGRATION_MANAGER.vmm.write().unwrap().vm = Some(vm); } - /// Register mutex device instance to entry hashmap with instance_id. + /// Register CPU instance to vmm. /// /// # Arguments /// - /// * `device_desc` - The `DeviceStateDesc` of device instance. - /// * `entry` - Device instance with migratable interface. - pub fn register_device_instance_mutex( - device_desc: DeviceStateDesc, - device_entry: Arc>, - ) where + /// * `cpu_desc` - The `DeviceStateDesc` of CPU instance. + /// * `cpu` - CPU device instance with MigrationHook trait. + /// * `id` - The unique id for CPU device. + pub fn register_cpu_instance(cpu_desc: DeviceStateDesc, cpu: Arc, id: u8) + where T: MigrationHook + Sync + Send + 'static, { - let name = device_desc.name.clone(); - let order = MigrationRestoreOrder::Default.into(); - Self::register_device_desc(device_desc); + let name = cpu_desc.name.clone() + "/" + &id.to_string(); + let mut copied_cpu_desc = cpu_desc.clone(); + copied_cpu_desc.name = name.clone(); + copied_cpu_desc.alias = cpu_desc.alias + id as u64; + Self::register_device_desc(copied_cpu_desc); + + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.cpus.insert(translate_id(&name), cpu); + } - let entry = MigrationEntry::Mutex(device_entry); - info!("Register device instance mutex: id {}", &name); - MigrationManager::insert_entry(name, order, entry, true); + /// Register memory instance to vmm. + /// + /// # Arguments + /// + /// * `memory` - The memory instance with MigrationHook trait. + pub fn register_memory_instance(memory: Arc) + where + T: MigrationHook + Sync + Send + 'static, + { + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.memory = Some(memory); } - pub fn register_device_instance_mutex_with_id( + /// Register device instance to vmm. + /// + /// # Arguments + /// + /// * `device_desc` - The `DeviceStateDesc` of device instance. + /// * `device` - The device instance with MigrationHook trait. + /// * `id` - The unique id for device. + pub fn register_device_instance( device_desc: DeviceStateDesc, - device_entry: Arc>, + device: Arc>, id: &str, ) where T: MigrationHook + Sync + Send + 'static, { let name = device_desc.name.clone() + "/" + id; - let order = MigrationRestoreOrder::Default.into(); Self::register_device_desc(device_desc); - let entry = MigrationEntry::Mutex(device_entry); - info!("Register device instance with id: id {}", &name); - MigrationManager::insert_entry(name, order, entry, false); - } - pub fn unregister_device_instance_mutex_by_id(device_desc: DeviceStateDesc, id: &str) { - let name = device_desc.name + "/" + id; - info!("Unregister device instance: id {}", &name); - MigrationManager::remove_entry(&name); + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.devices.insert(translate_id(&name), device); } - /// Register memory instance. + /// Register kvm instance to vmm. /// /// # Arguments /// - /// * `entry` - Memory instance with migratable interface. - pub fn register_memory_instance(entry: Arc) + /// * `kvm_desc` - The `DeviceStateDesc` of kvm instance. + /// * `kvm` - The kvm device instance with MigrationHook trait. + #[cfg(target_arch = "x86_64")] + pub fn register_kvm_instance(kvm_desc: DeviceStateDesc, kvm: Arc) where T: MigrationHook + Sync + Send + 'static, { - let entry = MigrationEntry::Memory(entry); - info!("Register memory instance"); - MigrationManager::insert_entry(String::from("MemoryState/Memory"), 0, entry, true); - } + Self::register_device_desc(kvm_desc); - /// Insert entry. If the name is duplicated, you should set gen_instance_id to true to - /// generated instance id to ensure that the id is unique. - /// - /// # Arguments - /// - /// * `name` - Entry name. - /// * `order` - Restore order. - /// * `entry` - Instance with migratable interface. - /// * `gen_instance_id` - If auto-generated instance id. - fn insert_entry(name: String, order: u16, entry: MigrationEntry, gen_instance_id: bool) { - let mut entrys = MIGRATION_MANAGER.entry.write().unwrap(); - let mut index = 0; - if gen_instance_id { - for (key, _) in &entrys[order as usize] { - if let Some(pos) = key.rfind(':') { - let (tmp_id, num_id) = key.split_at(pos); - if tmp_id == name { - let num = num_id.strip_prefix(':').unwrap(); - index = cmp::max(index, num.parse::().unwrap() + 1); - } - } - } - } - // ID is format as "{name}:{instance_id}" - let id = format!("{}:{}", name, index); - debug!("Insert entry: id {}", &id); - entrys[order as usize].push((id, entry)); + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.kvm = Some(kvm); } - /// Remove entry by the unique name. Not support to remove the entry with instance id. + /// Register GIC device instance to vmm. /// /// # Arguments /// - /// * `name` - Entry name. - fn remove_entry(name: &str) { - let eid = format!("{}:0", name); - let mut entrys = MIGRATION_MANAGER.entry.write().unwrap(); - for (i, item) in entrys.iter().enumerate() { - let pos = item.iter().position(|(key, _)| key == &eid); - if let Some(index) = pos { - debug!("Remove entry: eid {}", &eid); - entrys[i].remove(index); - return; - } - } - } - - /// Get `Device`'s alias from device type string. - /// - /// # Argument - /// - /// * `device_type` - The type string of device instance. - pub fn get_desc_alias(device_type: &str) -> Option { - Some(id_remap(device_type)) - } - - /// Return `desc_db` value len(0 restored as `serde_json`) - pub fn get_desc_db_len() -> Result { - let mut db_data_len = 0; - let desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); - for (_, desc) in desc_db.iter() { - let desc_str = serde_json::to_string(desc)?; - db_data_len += desc_str.as_bytes().len(); - } - - Ok(db_data_len) - } - - /// Write all `DeviceStateDesc` in `desc_db` hashmap to `Write` trait object. - pub fn save_descriptor_db(writer: &mut dyn Write) -> Result<()> { - let desc_length = Self::get_desc_db_len()?; - let mut desc_buffer = Vec::new(); - desc_buffer.resize(desc_length, 0); - let mut start = 0; - - let desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); - for (_, desc) in desc_db.iter() { - let desc_str = serde_json::to_string(desc)?; - let desc_bytes = desc_str.as_bytes(); - desc_buffer[start..start + desc_bytes.len()].copy_from_slice(desc_bytes); - start += desc_bytes.len(); - } - writer - .write_all(&desc_buffer) - .chain_err(|| "Failed to write descriptor message.")?; - - Ok(()) - } - - /// Load and parse device state descriptor from `Read` trait object. Save as a Hashmap. - pub fn load_descriptor_db( - reader: &mut dyn Read, - desc_length: usize, - ) -> Result> { - let mut desc_buffer = Vec::new(); - desc_buffer.resize(desc_length, 0); - reader.read_exact(&mut desc_buffer)?; - let mut snapshot_desc_db = HashMap::::new(); - - let deserializer = serde_json::Deserializer::from_slice(&desc_buffer); - for desc in deserializer.into_iter::() { - let device_desc: DeviceStateDesc = match desc { - Ok(desc) => desc, - Err(_) => break, - }; - snapshot_desc_db.insert(device_desc.alias, device_desc); - } + /// * `gic_desc` - The `DeviceStateDesc` of GIC instance. + /// * `gic` - The GIC device instance with MigrationHook trait. + #[cfg(target_arch = "aarch64")] + pub fn register_gic_instance(gic_desc: DeviceStateDesc, gic: Arc, id: &str) + where + T: MigrationHook + Sync + Send + 'static, + { + Self::register_device_desc(gic_desc); - Ok(snapshot_desc_db) + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.gic_group.insert(translate_id(id), gic); } - /// Set a new migration status for migration manager. + /// Unregister device instance from vmm. /// /// # Arguments /// - /// * `new_status`: new migration status, the transform must be illegal. - pub fn set_status(new_status: MigrationStatus) -> Result<()> { - let mut status = MIGRATION_MANAGER.status.write().unwrap(); - *status = status.transfer(new_status)?; - - Ok(()) - } - - /// Get current migration status for migration manager. - pub fn migration_get_status() -> MigrationStatus { - *MIGRATION_MANAGER.status.read().unwrap() + /// * `device_desc` - The `DeviceStateDesc` of device instance. + /// * `id` - The unique id for device. + pub fn unregister_device_instance(device_desc: DeviceStateDesc, id: &str) { + let name = device_desc.name + "/" + id; + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.devices.remove(&translate_id(&name)); } } #[cfg(test)] mod tests { - use super::*; - use crate::device_state::tests::{DeviceV1, DeviceV1State, DeviceV2, DeviceV2State}; use std::sync::{Arc, Mutex}; + use super::*; + use crate::protocol::tests::{DeviceV1, DeviceV1State, DeviceV2, DeviceV2State}; + impl MigrationHook for DeviceV1 {} impl MigrationHook for DeviceV2 {} #[test] fn test_register_device() { - let device_v1 = Arc::new(DeviceV1::default()); - let device_v2 = Arc::new(DeviceV2::default()); + let device_v1_mutex = Arc::new(Mutex::new(DeviceV1::default())); + let device_v2_arc = Arc::new(DeviceV2::default()); let device_v2_mutex = Arc::new(Mutex::new(DeviceV2::default())); MigrationManager::register_device_instance( DeviceV1State::descriptor(), - device_v1, - MigrationRestoreOrder::Default, + device_v1_mutex, + "device_v1", ); - MigrationManager::register_memory_instance(device_v2); - MigrationManager::register_device_instance_mutex( + MigrationManager::register_memory_instance(device_v2_arc); + MigrationManager::register_device_instance( DeviceV2State::descriptor(), device_v2_mutex, + "device_v2", ); assert!(MigrationManager::get_desc_alias("DeviceV1State").is_some()); assert_eq!( MigrationManager::get_desc_alias("DeviceV1State").unwrap(), - id_remap("DeviceV1State") + translate_id("DeviceV1State") ); assert!(MigrationManager::get_desc_alias("DeviceV2State").is_some()); assert_eq!( MigrationManager::get_desc_alias("DeviceV2State").unwrap(), - id_remap("DeviceV2State") + translate_id("DeviceV2State") ); } } diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index 35e7c0e40..9bfa94b2a 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -13,30 +13,28 @@ use std::collections::HashMap; use std::fs::{create_dir, File}; use std::io::{Read, Write}; -use std::mem::size_of; use std::path::PathBuf; -use error_chain::bail; -use log::info; -use util::byte_code::ByteCode; -use util::reader::BufferReader; +use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::general::{translate_id, Lifecycle}; +use crate::manager::{MigrationManager, MIGRATION_MANAGER}; +use crate::protocol::{DeviceStateDesc, FileFormat, MigrationStatus, HEADER_LENGTH}; use util::unix::host_page_size; -use crate::device_state::{DeviceStateDesc, VersionCheck}; -use crate::errors::{ErrorKind, Result, ResultExt}; -use crate::header::{FileFormat, MigrationHeader}; -use crate::manager::{id_remap, InstanceId, MigrationEntry, MigrationManager, MIGRATION_MANAGER}; -use crate::status::MigrationStatus; +pub const SERIAL_SNAPSHOT_ID: &str = "serial"; +pub const KVM_SNAPSHOT_ID: &str = "kvm"; +pub const GICV3_SNAPSHOT_ID: &str = "gicv3"; +pub const GICV3_ITS_SNAPSHOT_ID: &str = "gicv3_its"; +pub const PL011_SNAPSHOT_ID: &str = "pl011"; +pub const PL031_SNAPSHOT_ID: &str = "pl031"; -/// The length of `MigrationHeader` part occupies bytes in snapshot file. -const HEADER_LENGTH: usize = 4096; /// The suffix used for snapshot memory storage. const MEMORY_PATH_SUFFIX: &str = "memory"; /// The suffix used for snapshot device state storage. const DEVICE_PATH_SUFFIX: &str = "state"; impl MigrationManager { - /// Do snapshot for `VM`. + /// Save snapshot for `VM`. /// /// # Notes /// @@ -63,9 +61,7 @@ impl MigrationManager { vm_state_path.push(DEVICE_PATH_SUFFIX); match File::create(vm_state_path) { Ok(mut state_file) => { - Self::save_header(FileFormat::Device, &mut state_file)?; - Self::save_descriptor_db(&mut state_file)?; - Self::save_device_state(&mut state_file)?; + Self::save_vmstate(Some(FileFormat::Device), &mut state_file)?; } Err(e) => { bail!("Failed to create snapshot state file: {}", e); @@ -77,8 +73,7 @@ impl MigrationManager { vm_memory_path.push(MEMORY_PATH_SUFFIX); match File::create(vm_memory_path) { Ok(mut memory_file) => { - Self::save_header(FileFormat::MemoryFull, &mut memory_file)?; - Self::save_memory(&mut memory_file)?; + Self::save_memory(Some(FileFormat::MemoryFull), &mut memory_file)?; } Err(e) => { bail!("Failed to create snapshot memory file: {}", e); @@ -113,7 +108,7 @@ impl MigrationManager { snapshot_path.push(MEMORY_PATH_SUFFIX); let mut memory_file = File::open(&snapshot_path).chain_err(|| "Failed to open memory snapshot file")?; - let memory_header = Self::load_header(&mut memory_file)?; + let memory_header = Self::restore_header(&mut memory_file)?; memory_header.check_header()?; if memory_header.format != FileFormat::MemoryFull { bail!("Invalid memory snapshot file"); @@ -122,17 +117,17 @@ impl MigrationManager { snapshot_path.push(DEVICE_PATH_SUFFIX); let mut device_state_file = File::open(&snapshot_path).chain_err(|| "Failed to open device state snapshot file")?; - let device_state_header = Self::load_header(&mut device_state_file)?; + let device_state_header = Self::restore_header(&mut device_state_file)?; device_state_header.check_header()?; if device_state_header.format != FileFormat::Device { bail!("Invalid device state snapshot file"); } - Self::load_memory(&mut memory_file).chain_err(|| "Failed to load snapshot memory")?; + Self::restore_memory(&mut memory_file).chain_err(|| "Failed to load snapshot memory")?; let snapshot_desc_db = - Self::load_descriptor_db(&mut device_state_file, device_state_header.desc_len) + Self::restore_desc_db(&mut device_state_file, device_state_header.desc_len) .chain_err(|| "Failed to load device descriptor db")?; - Self::load_vmstate(snapshot_desc_db, &mut device_state_file) + Self::restore_vmstate(snapshot_desc_db, &mut device_state_file) .chain_err(|| "Failed to load snapshot device state")?; Self::resume()?; @@ -142,62 +137,16 @@ impl MigrationManager { Ok(()) } - /// Write `MigrationHeader` to `Write` trait object as bytes. - /// `MigrationHeader` will occupy the first 4096 bytes in snapshot file. - /// - /// # Arguments - /// - /// * `file_format` - confirm snapshot file format. - /// * `writer` - The `Write` trait object to write header message. - fn save_header(file_format: FileFormat, writer: &mut dyn Write) -> Result<()> { - let mut header = MigrationHeader::default(); - header.format = file_format; - header.desc_len = match file_format { - FileFormat::Device => Self::get_desc_db_len()?, - FileFormat::MemoryFull => (host_page_size() as usize) * 2 - HEADER_LENGTH, - }; - let header_bytes = header.as_bytes(); - let mut input_slice = [0u8; HEADER_LENGTH]; - - input_slice[0..size_of::()].copy_from_slice(header_bytes); - writer - .write(&input_slice) - .chain_err(|| "Failed to save migration header")?; - - Ok(()) - } - - /// Load and parse `MigrationHeader` from `Read` object. - /// - /// # Arguments - /// - /// * `reader` - The `Read` trait object. - fn load_header(reader: &mut dyn Read) -> Result { - let mut header_bytes = [0u8; size_of::()]; - reader.read_exact(&mut header_bytes)?; - - let mut place_holder = [0u8; HEADER_LENGTH - size_of::()]; - reader.read_exact(&mut place_holder)?; - - Ok(*MigrationHeader::from_bytes(&header_bytes) - .ok_or(ErrorKind::FromBytesError("HEADER"))?) - } - /// Save memory state and data to `Write` trait object. /// /// # Arguments /// - /// * `writer` - The `Write` trait object. - fn save_memory(writer: &mut dyn Write) -> Result<()> { - let entry = MIGRATION_MANAGER.entry.read().unwrap(); - for item in entry.iter() { - for (id, entry) in item.iter() { - if let MigrationEntry::Memory(i) = entry { - i.pre_save(id, writer) - .chain_err(|| "Failed to save vm memory")?; - } - } - } + /// * `fd` - The `Write` trait object to save memory data. + fn save_memory(file_format: Option, fd: &mut dyn Write) -> Result<()> { + Self::save_header(file_format, fd)?; + + let locked_vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + locked_vmm.memory.as_ref().unwrap().save_memory(fd)?; Ok(()) } @@ -207,115 +156,129 @@ impl MigrationManager { /// # Arguments /// /// * `file` - snapshot memory file. - fn load_memory(file: &mut File) -> Result<()> { + fn restore_memory(file: &mut File) -> Result<()> { let mut state_bytes = [0_u8].repeat((host_page_size() as usize) * 2 - HEADER_LENGTH); file.read_exact(&mut state_bytes)?; - let entry = MIGRATION_MANAGER.entry.read().unwrap(); - for item in entry.iter() { - for (_, entry) in item.iter() { - if let MigrationEntry::Memory(i) = entry { - i.pre_load(&state_bytes, Some(file)) - .chain_err(|| "Failed to load vm memory")?; - } - } - } + let locked_vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + locked_vmm + .memory + .as_ref() + .unwrap() + .restore_memory(Some(file), &state_bytes)?; Ok(()) } - /// Save device state to `Write` trait object. + /// Save vm state to `Write` trait object as bytes.. /// /// # Arguments /// - /// * `writer` - The `Write` trait object. - fn save_device_state(writer: &mut dyn Write) -> Result<()> { - let entry = MIGRATION_MANAGER.entry.read().unwrap(); - for item in entry.iter() { - for (id, entry) in item.iter() { - match entry { - MigrationEntry::Safe(i) => i.pre_save(id, writer)?, - MigrationEntry::Mutex(i) => i.lock().unwrap().pre_save(id, writer)?, - _ => {} - } + /// * fd - The `Write` trait object to save VM data. + pub fn save_vmstate(file_format: Option, fd: &mut dyn Write) -> Result<()> { + Self::save_header(file_format, fd)?; + Self::save_desc_db(fd)?; + + let locked_vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + // Save CPUs state. + for (id, cpu) in locked_vmm.cpus.iter() { + cpu.save_device(*id, fd) + .chain_err(|| "Failed to save cpu state")?; + } + + #[cfg(target_arch = "x86_64")] + { + // Save kvm device state. + locked_vmm + .kvm + .as_ref() + .unwrap() + .save_device(translate_id(KVM_SNAPSHOT_ID), fd) + .chain_err(|| "Failed to save kvm state")?; + } + + // Save devices state. + for (id, device) in locked_vmm.devices.iter() { + device + .lock() + .unwrap() + .save_device(*id, fd) + .chain_err(|| "Failed to save device state")?; + } + + #[cfg(target_arch = "aarch64")] + { + // Save GICv3 device state. + let gic_id = translate_id(GICV3_SNAPSHOT_ID); + if let Some(gic) = locked_vmm.gic_group.get(&gic_id) { + gic.save_device(gic_id, fd) + .chain_err(|| "Failed to save gic state")?; + } + + // Save GICv3 ITS device state. + let its_id = translate_id(GICV3_ITS_SNAPSHOT_ID); + if let Some(its) = locked_vmm.gic_group.get(&its_id) { + its.save_device(its_id, fd) + .chain_err(|| "Failed to save gic its state")?; } } Ok(()) } - /// Restore vm state from `Read` trait object. + /// Restore vm state from `Read` trait object as bytes.. /// /// # Arguments /// - /// * `snap_desc_db` - The snapshot descriptor hashmap read from snapshot file. - /// * `reader` - The `Read` trait object. - fn load_vmstate( + /// * snap_desc_db - snapshot state descriptor. + /// * fd - The `Read` trait object to restore VM data. + pub fn restore_vmstate( snap_desc_db: HashMap, - reader: &mut dyn Read, + fd: &mut dyn Read, ) -> Result<()> { - let desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); - let device_entry = MIGRATION_MANAGER.entry.read().unwrap(); - - let mut migration_file = BufferReader::new(reader); - migration_file.read_buffer()?; - - while let Some(data) = &migration_file.read_vectored(size_of::()) { - let instance_id = InstanceId::from_bytes(data.as_slice()).unwrap(); - let snap_desc = snap_desc_db.get(&instance_id.object_type).unwrap(); - let current_desc = desc_db.get(&snap_desc.name).unwrap(); - - let mut state_data = - if let Some(data) = migration_file.read_vectored(snap_desc.size as usize) { - data - } else { - bail!("Invalid snapshot device state data"); - }; - match current_desc.check_version(snap_desc) { - VersionCheck::Same => {} - VersionCheck::Compat => { - current_desc - .add_padding(snap_desc, &mut state_data) - .chain_err(|| "Failed to transform snapshot data version.")?; - } - VersionCheck::Mismatch => { - return Err(ErrorKind::VersionNotFit( - current_desc.compat_version, - snap_desc.current_version, - ) - .into()) - } + let locked_vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + // Restore CPUs state. + for _ in 0..locked_vmm.cpus.len() { + let (cpu_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; + if let Some(cpu) = locked_vmm.cpus.get(&id) { + cpu.restore_device(&cpu_data) + .chain_err(|| "Failed to restore cpu state")?; } + } - for item in device_entry.iter() { - for (key, state) in item { - if id_remap(key) == instance_id.object_id { - info!("Load VM state: key {}", key); - match state { - MigrationEntry::Safe(i) => i.pre_load(&state_data, None)?, - MigrationEntry::Mutex(i) => { - i.lock().unwrap().pre_load_mut(&state_data, None)? - } - _ => {} - } - } - } + #[cfg(target_arch = "x86_64")] + { + // Restore kvm device state. + if let Some(kvm) = &locked_vmm.kvm { + let (kvm_data, _) = Self::check_vm_state(fd, &snap_desc_db)?; + kvm.restore_device(&kvm_data) + .chain_err(|| "Failed to restore kvm state")?; } } - Ok(()) - } + // Restore devices state. + for _ in 0..locked_vmm.devices.len() { + let (device_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; + if let Some(device) = locked_vmm.devices.get(&id) { + device + .lock() + .unwrap() + .restore_mut_device(&device_data) + .chain_err(|| "Failed to restore device state")?; + } + } - /// Resume recovered device. - /// This function will be called after restore device state. - fn resume() -> Result<()> { - let entry = MIGRATION_MANAGER.entry.read().unwrap(); - for item in entry.iter() { - for (_, state) in item { - if let MigrationEntry::Mutex(i) = state { - i.lock().unwrap().resume()? + #[cfg(target_arch = "aarch64")] + { + // Restore GIC group state. + for _ in 0..locked_vmm.gic_group.len() { + let (gic_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; + if let Some(gic) = locked_vmm.gic_group.get(&id) { + gic.restore_device(&gic_data) + .chain_err(|| "Failed to restore gic state")?; } } } + Ok(()) } } -- Gitee From fc268647b8c7afaa5224e597a389ce0b8185b7b1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 15:55:41 +0800 Subject: [PATCH 0095/2187] migration: modify the registration migration instance functions. In order to use `Vmm` struct to save all device instances. It creates different registration functions for all instances. This patch is used to modify all the code related to the registered instance. Signed-off-by: Xinle.Guo --- Cargo.lock | 5 +- .../src/interrupt_controller/aarch64/gicv3.rs | 13 +++-- devices/src/legacy/pl011.rs | 11 +++- devices/src/legacy/pl031.rs | 11 +++- devices/src/legacy/serial.rs | 11 +++- hypervisor/Cargo.toml | 2 - .../kvm/state.rs => machine/src/vm_state.rs | 9 ++-- pci/src/msix.rs | 2 +- pci/src/root_port.rs | 6 +-- virtio/src/block.rs | 13 +++-- virtio/src/net.rs | 2 +- virtio/src/vhost/kernel/vsock.rs | 50 ++++++++++--------- virtio/src/virtio_pci.rs | 17 ++----- 13 files changed, 83 insertions(+), 69 deletions(-) rename hypervisor/src/kvm/state.rs => machine/src/vm_state.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index 6c72adeeb..64897c5a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,8 +205,6 @@ dependencies = [ "kvm-bindings", "kvm-ioctls", "log", - "migration", - "migration_derive", "once_cell", "util", "vmm-sys-util", @@ -326,8 +324,11 @@ name = "migration" version = "2.2.0" dependencies = [ "error-chain", + "hypervisor", + "kvm-bindings", "kvm-ioctls", "log", + "machine_manager", "migration_derive", "once_cell", "serde", diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 27754a00e..1b1c34787 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -22,7 +22,10 @@ use hypervisor::kvm::KVM_FDS; use kvm_ioctls::DeviceFd; use log::error; use machine_manager::machine::{KvmVmState, MachineLifecycle}; -use migration::{MigrationManager, MigrationRestoreOrder}; +use migration::{ + snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, + MigrationManager, +}; use util::device_tree::{self, FdtBuilder}; // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. @@ -370,16 +373,16 @@ impl GICDevice for GICv3 { ) -> Result> { let gicv3 = Arc::new(GICv3::new(gic_conf)?); if gicv3.its_dev.is_some() { - MigrationManager::register_device_instance( + MigrationManager::register_gic_instance( GICv3ItsState::descriptor(), gicv3.its_dev.as_ref().unwrap().clone(), - MigrationRestoreOrder::Gicv3Its, + GICV3_ITS_SNAPSHOT_ID, ); } - MigrationManager::register_device_instance( + MigrationManager::register_gic_instance( GICv3State::descriptor(), gicv3.clone(), - MigrationRestoreOrder::Gicv3, + GICV3_SNAPSHOT_ID, ); Ok(gicv3) diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 84c31bf31..d366ec64e 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -24,7 +24,10 @@ use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, }; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + snapshot::PL011_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, + StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; @@ -174,7 +177,11 @@ impl PL011 { param_type: "earlycon".to_string(), value: format!("pl011,mmio,0x{:08x}", region_base), }); - MigrationManager::register_device_instance_mutex(PL011State::descriptor(), dev.clone()); + MigrationManager::register_device_instance( + PL011State::descriptor(), + dev.clone(), + PL011_SNAPSHOT_ID, + ); let locked_dev = dev.lock().unwrap(); locked_dev.chardev.lock().unwrap().set_input_callback(&dev); EventLoop::update_event( diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 897c71919..8c22ad027 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -17,7 +17,10 @@ use acpi::AmlBuilder; use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; use log::error; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + snapshot::PL031_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, + StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; @@ -106,7 +109,11 @@ impl PL031 { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; - MigrationManager::register_device_instance_mutex(PL031State::descriptor(), dev); + MigrationManager::register_device_instance( + PL031State::descriptor(), + dev, + PL031_SNAPSHOT_ID, + ); Ok(()) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index cc2abd679..96ab47b24 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -25,7 +25,10 @@ use log::error; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + snapshot::SERIAL_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, + StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; @@ -151,7 +154,11 @@ impl Serial { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; - MigrationManager::register_device_instance_mutex(SerialState::descriptor(), dev.clone()); + MigrationManager::register_device_instance( + SerialState::descriptor(), + dev.clone(), + SERIAL_SNAPSHOT_ID, + ); #[cfg(target_arch = "aarch64")] bs.lock().unwrap().kernel_cmdline.push(Param { param_type: "earlycon".to_string(), diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 53e910609..ecb457c29 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -11,8 +11,6 @@ error-chain = "0.12.4" kvm-bindings = ">=0.3.0" kvm-ioctls = "0.6.0" log = "0.4.8" -migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } util = { path = "../util" } vmm-sys-util = ">=0.7.0" once_cell = "1.9.0" diff --git a/hypervisor/src/kvm/state.rs b/machine/src/vm_state.rs similarity index 93% rename from hypervisor/src/kvm/state.rs rename to machine/src/vm_state.rs index ec61cb23c..4254d8024 100644 --- a/hypervisor/src/kvm/state.rs +++ b/machine/src/vm_state.rs @@ -10,12 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use kvm_bindings::{ - kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_CLOCK_TSC_STABLE, KVM_IRQCHIP_IOAPIC, -}; +use kvm_bindings::{kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_IRQCHIP_IOAPIC}; use migration_derive::{ByteCode, Desc}; -use super::KVM_FDS; +use hypervisor::kvm::KVM_FDS; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use util::byte_code::ByteCode; @@ -43,7 +41,8 @@ impl StateTransfer for KvmDevice { // save kvm_clock let mut kvm_clock = vm_fd.get_clock()?; - kvm_clock.flags &= !KVM_CLOCK_TSC_STABLE; + // Reset kvm clock flag. + kvm_clock.flags = 0; // save ioapic let mut ioapic = kvm_irqchip { diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 34d6d79a3..ee1fbf7e9 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -463,7 +463,7 @@ pub fn init_msix( config.msix = Some(msix.clone()); #[cfg(not(test))] - MigrationManager::register_device_instance_mutex_with_id(MsixState::descriptor(), msix, _id); + MigrationManager::register_device_instance(MsixState::descriptor(), msix, _id); Ok(()) } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 2e506fc7d..25287059f 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -330,11 +330,7 @@ impl PciDevOps for RootPort { } // Need to drop locked_root_port in order to register root_port instance. drop(locked_root_port); - MigrationManager::register_device_instance_mutex_with_id( - RootPortState::descriptor(), - root_port, - &name, - ); + MigrationManager::register_device_instance(RootPortState::descriptor(), root_port, &name); Ok(()) } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 65adc30ff..5aca02d8d 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -27,7 +27,10 @@ use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, }; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + migration::Migratable, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, + StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use util::aio::{Aio, AioCb, AioCompleteFunc, IoCmd, Iovec}; use util::byte_code::ByteCode; @@ -276,6 +279,9 @@ impl Request { VIRTIO_BLK_T_IN => { aiocb.opcode = IoCmd::Preadv; if direct { + for iov in self.iovec.iter() { + MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); + } (*aio).as_mut().rw_aio(aiocb, SECTOR_SIZE).chain_err(|| { "Failed to process block request for reading asynchronously" })?; @@ -1026,10 +1032,7 @@ impl VirtioDevice for Block { } fn unrealize(&mut self) -> Result<()> { - MigrationManager::unregister_device_instance_mutex_by_id( - BlockState::descriptor(), - &self.blk_cfg.id, - ); + MigrationManager::unregister_device_instance(BlockState::descriptor(), &self.blk_cfg.id); Ok(()) } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 135e1cd63..1631f5aab 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -863,7 +863,7 @@ impl VirtioDevice for Net { } fn unrealize(&mut self) -> Result<()> { - MigrationManager::unregister_device_instance_mutex_by_id( + MigrationManager::unregister_device_instance( VirtioNetState::descriptor(), &self.net_cfg.id, ); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 23d1df332..8c58b49f5 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -117,30 +117,32 @@ impl Vsock { /// been interrupted. The driver shuts down established connections and the guest_cid /// configuration field is fetched again. fn transport_reset(&self) -> Result<()> { - let mut event_queue_locked = self.event_queue.as_ref().unwrap().lock().unwrap(); - let element = event_queue_locked - .vring - .pop_avail(&self.mem_space, self.state.driver_features) - .chain_err(|| "Failed to get avail ring element.")?; - - self.mem_space - .write_object( - &VIRTIO_VSOCK_EVENT_TRANSPORT_RESET, - element.in_iovec[0].addr, - ) - .chain_err(|| "Failed to write buf for virtio vsock event")?; - event_queue_locked - .vring - .add_used( - &self.mem_space, - element.index, - VIRTIO_VSOCK_EVENT_TRANSPORT_RESET.as_bytes().len() as u32, - ) - .chain_err(|| format!("Failed to add used ring {}", element.index))?; - - if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Vring, Some(&*event_queue_locked)) - .chain_err(|| ErrorKind::EventFdWrite)?; + if let Some(evt_queue) = self.event_queue.as_ref() { + let mut event_queue_locked = evt_queue.lock().unwrap(); + let element = event_queue_locked + .vring + .pop_avail(&self.mem_space, self.state.driver_features) + .chain_err(|| "Failed to get avail ring element.")?; + + self.mem_space + .write_object( + &VIRTIO_VSOCK_EVENT_TRANSPORT_RESET, + element.in_iovec[0].addr, + ) + .chain_err(|| "Failed to write buf for virtio vsock event")?; + event_queue_locked + .vring + .add_used( + &self.mem_space, + element.index, + VIRTIO_VSOCK_EVENT_TRANSPORT_RESET.as_bytes().len() as u32, + ) + .chain_err(|| format!("Failed to add used ring {}", element.index))?; + + if let Some(interrupt_cb) = &self.interrupt_cb { + interrupt_cb(&VirtioInterruptType::Vring, Some(&*event_queue_locked)) + .chain_err(|| ErrorKind::EventFdWrite)?; + } } Ok(()) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 0edf12d58..18a571a25 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1042,11 +1042,7 @@ impl PciDevOps for VirtioPciDevice { pci_device.unwrap().lock().unwrap().name() ); } - MigrationManager::register_device_instance_mutex_with_id( - VirtioPciState::descriptor(), - dev, - &name, - ); + MigrationManager::register_device_instance(VirtioPciState::descriptor(), dev, &name); Ok(()) } @@ -1065,14 +1061,9 @@ impl PciDevOps for VirtioPciDevice { msix.lock().unwrap().msix_update = None; } - MigrationManager::unregister_device_instance_mutex_by_id( - MsixState::descriptor(), - &self.name, - ); - MigrationManager::unregister_device_instance_mutex_by_id( - VirtioPciState::descriptor(), - &self.name, - ); + MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name); + MigrationManager::unregister_device_instance(VirtioPciState::descriptor(), &self.name); + Ok(()) } -- Gitee From abb935b5ff3ec142599b448b4cc921703640ead9 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 14:10:38 +0800 Subject: [PATCH 0096/2187] migration: add dirty page log tracking for live migration 1. Add `DirtyBitmap` structure to save vmm dirty bitmap. 2. Add kvm ioctls to start and stop dirty log in kvm. 3. Using Migratable trait to provide the methods for tracking dirty memory. Logging dirty page is started by `start_dirty_log()`, is stopped by `stop_dirty_log()`. Getting dirty memory from `get_dirty_log()`. Marking vmm dirty memory by `mark_dirty_log()`. Signed-off-by: Xinle.Guo --- address_space/src/listener.rs | 10 ++ address_space/src/state.rs | 40 ++++-- hypervisor/src/kvm/mod.rs | 114 ++++++++++++++--- migration/src/lib.rs | 1 + migration/src/migration.rs | 226 ++++++++++++++++++++++++++++++++++ 5 files changed, 361 insertions(+), 30 deletions(-) create mode 100644 migration/src/migration.rs diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index b9865adec..9bfa33a1c 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -261,6 +261,11 @@ impl KvmMemoryListener { flags, }; unsafe { + KVM_FDS + .load() + .add_mem_slot(kvm_region) + .chain_err(|| "Failed to add memory slot to kvm")?; + KVM_FDS .load() .vm_fd @@ -316,6 +321,11 @@ impl KvmMemoryListener { flags: 0, }; unsafe { + KVM_FDS + .load() + .remove_mem_slot(kvm_region) + .chain_err(|| "Failed to remove memory slot to kvm")?; + KVM_FDS .load() .vm_fd diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 901dc4457..f5274a5b8 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -11,18 +11,20 @@ // See the Mulan PSL v2 for more details. use std::fs::File; -use std::io::Write; +use std::io::{Read, Write}; use std::mem::size_of; use std::sync::Arc; -use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; - -use migration::errors::{ErrorKind, Result, ResultExt}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, StateTransfer}; +use migration::{ + errors::{ErrorKind, Result}, + DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::unix::host_page_size; +use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; + const MIGRATION_HEADER_LENGTH: usize = 4096; #[repr(C)] @@ -78,17 +80,17 @@ impl StateTransfer for AddressSpace { } impl MigrationHook for AddressSpace { - fn pre_save(&self, _id: &str, writer: &mut dyn Write) -> Result<()> { + fn save_memory(&self, fd: &mut dyn Write) -> Result<()> { let ram_state = self.get_state_vec()?; - writer.write_all(&ram_state)?; + fd.write_all(&ram_state)?; let padding_buffer = [0].repeat(memory_offset() - MIGRATION_HEADER_LENGTH - size_of::()); - writer.write_all(&padding_buffer)?; + fd.write_all(&padding_buffer)?; for region in self.root().subregions().iter() { if let Some(base_addr) = region.start_addr() { region - .read(writer, base_addr, 0, region.size()) + .read(fd, base_addr, 0, region.size()) .map_err(|e| ErrorKind::SaveVmMemoryErr(e.to_string()))?; } } @@ -96,7 +98,7 @@ impl MigrationHook for AddressSpace { Ok(()) } - fn pre_load(&self, state: &[u8], memory: Option<&File>) -> Result<()> { + fn restore_memory(&self, memory: Option<&File>, state: &[u8]) -> Result<()> { let address_space_state: &AddressSpaceState = AddressSpaceState::from_bytes(&state[0..size_of::()]) .ok_or(ErrorKind::FromBytesError("MEMORY"))?; @@ -121,16 +123,30 @@ impl MigrationHook for AddressSpace { false, false, ) - .chain_err(|| ErrorKind::RestoreVmMemoryErr)?, + .map_err(|e| ErrorKind::RestoreVmMemoryErr(e.to_string()))?, ); self.root() .add_subregion( Region::init_ram_region(host_mmap.clone()), host_mmap.start_address().raw_value(), ) - .chain_err(|| ErrorKind::RestoreVmMemoryErr)?; + .map_err(|e| ErrorKind::RestoreVmMemoryErr(e.to_string()))?; } Ok(()) } + + fn send_memory(&self, fd: &mut dyn Write, range: MemBlock) -> Result<()> { + self.read(fd, GuestAddress(range.gpa), range.len) + .map_err(|e| ErrorKind::SendVmMemoryErr(e.to_string()))?; + + Ok(()) + } + + fn recv_memory(&self, fd: &mut dyn Read, range: MemBlock) -> Result<()> { + self.write(fd, GuestAddress(range.gpa), range.len) + .map_err(|e| ErrorKind::RecvVmMemoryErr(e.to_string()))?; + + Ok(()) + } } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index d88e9f88c..1d20eed82 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -10,28 +10,25 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod interrupt; -#[cfg(target_arch = "x86_64")] -mod state; - -pub use interrupt::MsiVector; - +use std::collections::HashMap; use std::sync::{Arc, Mutex}; use arc_swap::ArcSwap; -use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; +use kvm_bindings::kvm_userspace_memory_region as MemorySlot; use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; use log::error; use once_cell::sync::Lazy; -use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::{ - ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, + eventfd::EventFd, ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, + ioctl_iowr_nr, }; use crate::errors::{Result, ResultExt}; -#[cfg(target_arch = "x86_64")] -use migration::{MigrationManager, MigrationRestoreOrder}; +pub use interrupt::MsiVector; +use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; + +mod interrupt; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; @@ -102,6 +99,7 @@ pub struct KVMFds { pub fd: Option, pub vm_fd: Option, pub irq_route_table: Mutex, + pub mem_slots: Arc>>, } impl KVMFds { @@ -120,6 +118,7 @@ impl KVMFds { fd: Some(fd), vm_fd: Some(vm_fd), irq_route_table, + mem_slots: Arc::new(Mutex::new(HashMap::new())), } } Err(e) => { @@ -128,13 +127,6 @@ impl KVMFds { } }; - #[cfg(target_arch = "x86_64")] - MigrationManager::register_device_instance( - state::KvmDeviceState::descriptor(), - Arc::new(state::KvmDevice {}), - MigrationRestoreOrder::Default, - ); - kvm_fds } @@ -173,6 +165,92 @@ impl KVMFds { .unregister_irqfd(fd, gsi) .chain_err(|| format!("Failed to unregister irqfd: gsi {}.", gsi)) } + + /// Start dirty page tracking in kvm. + pub fn start_dirty_log(&self) -> Result<()> { + for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { + region.flags = KVM_MEM_LOG_DIRTY_PAGES; + // Safe because region from `KVMFds` is reliable. + unsafe { + self.vm_fd + .as_ref() + .unwrap() + .set_user_memory_region(*region) + .chain_err(|| { + format!( + "Failed to start dirty log, error is {}", + std::io::Error::last_os_error() + ) + })?; + } + } + + Ok(()) + } + + /// Stop dirty page tracking in kvm. + pub fn stop_dirty_log(&self) -> Result<()> { + for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { + region.flags = 0; + // Safe because region from `KVMFds` is reliable. + unsafe { + self.vm_fd + .as_ref() + .unwrap() + .set_user_memory_region(*region) + .chain_err(|| { + format!( + "Failed to stop dirty log, error is {}", + std::io::Error::last_os_error() + ) + })?; + } + } + + Ok(()) + } + + /// Get dirty page bitmap in kvm. + pub fn get_dirty_log(&self, slot: u32, mem_size: u64) -> Result> { + let res = self + .vm_fd + .as_ref() + .unwrap() + .get_dirty_log(slot, mem_size as usize) + .chain_err(|| { + format!( + "Failed to get dirty log, error is {}", + std::io::Error::last_os_error() + ) + })?; + + Ok(res) + } + + /// Add ram memory region to `KVMFds` structure. + pub fn add_mem_slot(&self, mem_slot: MemorySlot) -> Result<()> { + if mem_slot.flags & KVM_MEM_READONLY != 0 { + return Ok(()); + } + + let mut locked_slots = self.mem_slots.as_ref().lock().unwrap(); + locked_slots.insert(mem_slot.slot, mem_slot); + + Ok(()) + } + + /// Remove ram memory region from `KVMFds` structure. + pub fn remove_mem_slot(&self, mem_slot: MemorySlot) -> Result<()> { + let mut locked_slots = self.mem_slots.as_ref().lock().unwrap(); + locked_slots.remove(&mem_slot.slot); + + Ok(()) + } + + /// Get ram memory region from `KVMFds` structure. + pub fn get_mem_slots(&self) -> Arc>> { + self.mem_slots.clone() + } } pub static KVM_FDS: Lazy> = Lazy::new(|| ArcSwap::from(Arc::new(KVMFds::new()))); diff --git a/migration/src/lib.rs b/migration/src/lib.rs index ecbf9fec9..9f40a3968 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -21,6 +21,7 @@ extern crate log; pub mod general; pub mod manager; +pub mod migration; pub mod protocol; pub mod snapshot; diff --git a/migration/src/migration.rs b/migration/src/migration.rs new file mode 100644 index 000000000..e0cf9dd4f --- /dev/null +++ b/migration/src/migration.rs @@ -0,0 +1,226 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; +use std::io::{Read, Write}; +use std::mem::size_of; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::Instant; + +use kvm_bindings::kvm_userspace_memory_region as MemorySlot; + +use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::general::Lifecycle; +use crate::manager::MIGRATION_MANAGER; +use crate::protocol::{MemBlock, MigrationStatus, Request, Response, TransStatus}; +use crate::MigrationManager; +use hypervisor::kvm::KVM_FDS; +use util::unix::host_page_size; + +/// Dirty bitmap information of vmm memory slot. +pub struct DirtyBitmap { + /// Guest address. + pub gpa: u64, + /// Host address. + pub hva: u64, + /// length of memory. + pub len: u64, + /// Bitmap for vmm memory slot. + pub map: Vec, + /// Host page size. + pub page_size: u64, +} + +impl DirtyBitmap { + /// Create a new dirty bitmap for vmm. + /// + /// # Arguments + /// + /// * `gpa` - Guest physical address of memory slot. + /// * `hva` - Host virtual address of memory slot. + /// * `len` - Length of memory slot. + pub fn new(gpa: u64, hva: u64, len: u64) -> Self { + let page_size = host_page_size(); + + let mut num_pages = len / page_size; + // Page alignment. + if len % page_size > 0 { + num_pages += 1; + } + let size = num_pages / 64 + 1; + let map: Vec = (0..size).map(|_| AtomicU64::new(0)).collect(); + + DirtyBitmap { + gpa, + hva, + len, + map, + page_size, + } + } + + /// Mark dirty bitmap for vmm. + /// + /// # Arguments + /// + /// * `addr` - Guest physical address of memory. + /// * `len` - Length of memory slot. + pub fn mark_bitmap(&self, addr: u64, len: u64) { + // Just return if len is 0. + if len == 0 { + return; + } + + let offset = addr - self.gpa; + let first_bit = offset / self.page_size; + let last_bit = (offset + len) / self.page_size; + for n in first_bit..last_bit { + // Ignore bit that is out of range. + if n >= self.len { + break; + } + self.map[(n as usize) >> 6].fetch_or(1 << (n & 63), Ordering::SeqCst); + } + } + + /// Get and clear dirty bitmap for vmm. + pub fn get_and_clear_dirty(&self) -> Vec { + self.map + .iter() + .map(|m| m.fetch_and(0, Ordering::SeqCst)) + .collect() + } +} + +pub trait Migratable { + /// Start the dirty log in the kvm and vmm. + fn start_dirty_log() -> Result<()> { + // Create dirty bitmaps for vmm. + let mut bitmaps = HashMap::::new(); + let mem_slots = KVM_FDS.load().get_mem_slots(); + for (_, slot) in mem_slots.lock().unwrap().iter() { + let bitmap = + DirtyBitmap::new(slot.guest_phys_addr, slot.userspace_addr, slot.memory_size); + bitmaps.insert(slot.slot, bitmap); + } + let mut vm_bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); + *vm_bitmaps = bitmaps; + + // Start logging dirty memory in kvm. + KVM_FDS.load().start_dirty_log()?; + + Ok(()) + } + + /// Stop the dirty log in the kvm and vmm. + fn stop_dirty_log() -> Result<()> { + // Clear dirty bitmaps from vmm. + let mut vm_bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); + *vm_bitmaps = HashMap::new(); + + // Stop logging dirty memory in kvm. + KVM_FDS.load().stop_dirty_log()?; + + Ok(()) + } + + /// Collect the dirty log from kvm and vmm. + /// + /// # Arguments + /// + /// * `slot` - The memory slot. + fn get_dirty_log(slot: &MemorySlot) -> Result> { + // Get dirty memory from vmm. + let mut vmm_dirty_bitmap = Vec::new(); + let bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); + for (_, map) in bitmaps.iter() { + if (slot.guest_phys_addr == map.gpa) && (slot.memory_size == map.len) { + vmm_dirty_bitmap = map.get_and_clear_dirty(); + } + } + + // Get dirty memory from kvm. + let vm_dirty_bitmap = KVM_FDS + .load() + .get_dirty_log(slot.slot, slot.memory_size) + .unwrap(); + + // Merge dirty bitmap. + let dirty_bitmap: Vec = vm_dirty_bitmap + .iter() + .zip(vmm_dirty_bitmap.iter()) + .map(|(x, y)| x | y) + .collect(); + + // Convert dirty bitmaps to memory blocks. + Ok(Self::sync_dirty_bitmap(dirty_bitmap, slot.guest_phys_addr)) + } + + /// mark the dirty log into vmm. + /// + /// # Arguments + /// + /// * `addr` - Start address of dirty memory. + /// * `len` - Length of dirty memory. + fn mark_dirty_log(addr: u64, len: u64) { + let bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); + for (_, map) in bitmaps.iter() { + if (addr >= map.hva) && ((addr + len) <= (map.hva + map.len)) { + map.mark_bitmap(addr - map.hva + map.gpa, len); + } + } + } + + /// sync the dirty log from kvm bitmaps. + /// + /// # Arguments + /// + /// * `bitmap` - dirty bitmap from kvm. + /// * `addr` - Start address of memory slot. + fn sync_dirty_bitmap(bitmap: Vec, addr: u64) -> Vec { + let page_size = host_page_size(); + let mut mem_blocks: Vec = Vec::new(); + let mut block: Option = None; + + for (idx, num) in bitmap.iter().enumerate() { + if *num == 0 { + continue; + } + + for bit in 0..64 { + if ((num >> bit) & 1_u64) == 0 { + if let Some(entry) = block.take() { + mem_blocks.push(entry); + } + continue; + } + + if let Some(e) = &mut block { + e.len += page_size; + } else { + let offset = ((idx * 64) + bit) as u64 * page_size; + block = Some(MemBlock { + gpa: addr + offset, + len: page_size, + }); + } + } + } + if let Some(entry) = block.take() { + mem_blocks.push(entry); + } + + mem_blocks + } +} + +impl Migratable for MigrationManager {} -- Gitee From e3c591cf2339255bb197bd983a4ceab4fbcb95c4 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 23 Jul 2022 22:55:25 +0800 Subject: [PATCH 0097/2187] Migration: implement live migration 1. The source VM sets the active status itself and sync the destination VM. 2. Dirty page log tracking is started. 3. Sending all source VM memory to the destination. 4. Up to send the dirty memory to the destination iteratively. 5. Pause virtual machine at the source. 6. The last set of dirty memory is sent to the destination. 7. The guest OS is snapshotted and sent to the destination. 8. Complete migration and the destination VM is resumed. Signed-off-by: Xinle.Guo --- migration/src/migration.rs | 503 ++++++++++++++++++++++++++++++++++++- 1 file changed, 502 insertions(+), 1 deletion(-) diff --git a/migration/src/migration.rs b/migration/src/migration.rs index e0cf9dd4f..480788216 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -14,7 +14,7 @@ use std::collections::HashMap; use std::io::{Read, Write}; use std::mem::size_of; use std::sync::atomic::{AtomicU64, Ordering}; -use std::time::Instant; +use std::time::{Duration, Instant}; use kvm_bindings::kvm_userspace_memory_region as MemorySlot; @@ -24,8 +24,509 @@ use crate::manager::MIGRATION_MANAGER; use crate::protocol::{MemBlock, MigrationStatus, Request, Response, TransStatus}; use crate::MigrationManager; use hypervisor::kvm::KVM_FDS; +use machine_manager::config::VmConfig; use util::unix::host_page_size; +impl MigrationManager { + /// Start VM live migration at source VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. it + /// will send source VM memory data and devices state to destination VM. + /// And, it will receive confirmation from destination VM. + pub fn send_migration(fd: &mut T) -> Result<()> + where + T: Read + Write, + { + // Activate the migration status of source and destination virtual machine. + Self::active_migration(fd).chain_err(|| "Failed to active migration")?; + + // Send source virtual machine configuration. + Self::send_vm_config(fd).chain_err(|| "Failed to send vm config")?; + + // Start logging dirty pages. + Self::start_dirty_log().chain_err(|| "Failed to start logging dirty page")?; + + // Send all memory of virtual machine itself to destination. + Self::send_vm_memory(fd).chain_err(|| "Failed to send VM memory")?; + + // Iteratively send virtual machine dirty memory. + let iterations = MIGRATION_MANAGER.limit.read().unwrap().max_dirty_iterations; + for _ in 0..iterations { + // Check the migration is active. + if !Self::is_active() { + break; + } + + if !Self::iteration_send(fd)? { + break; + } + } + + // Check whether the migration is canceled. + if Self::is_canceled() { + // Cancel the migration of source and destination. + Self::cancel_migration(fd).chain_err(|| "Failed to cancel migration")?; + return Ok(()); + } + + // Pause virtual machine. + Self::pause()?; + + // Send remaining virtual machine dirty memory. + Self::send_dirty_memory(fd).chain_err(|| "Failed to send dirty memory")?; + + // Stop logging dirty pages. + Self::stop_dirty_log().chain_err(|| "Failed to stop logging dirty page")?; + + // Get virtual machine state and send it to destination VM. + Self::send_vmstate(fd).chain_err(|| "Failed to send vm state")?; + + // Complete the migration. + Self::complete_migration(fd).chain_err(|| "Failed to completing migration")?; + + // Destroy virtual machine. + Self::clear_migration().chain_err(|| "Failed to clear migration")?; + + Ok(()) + } + + /// Start VM live migration at destination VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. it + /// will receive source VM memory data and devices state. And, + /// it will send confirmation to source VM. + pub fn recv_migration(fd: &mut T) -> Result<()> + where + T: Read + Write, + { + // Activate the migration status. + let request = Request::recv_msg(fd)?; + if request.status == TransStatus::Active { + info!("Active the migration"); + Self::set_status(MigrationStatus::Active)?; + Response::send_msg(fd, TransStatus::Ok)?; + } else { + Response::send_msg(fd, TransStatus::Error)?; + return Err(ErrorKind::MigrationStatusErr( + request.status.to_string(), + TransStatus::Active.to_string(), + ) + .into()); + } + + // Check source and destination virtual machine configuration. + let request = Request::recv_msg(fd)?; + if request.status == TransStatus::VmConfig { + info!("Receive VmConfig status"); + Self::check_vm_config(fd, request.length).chain_err(|| "Failed to check vm config")?; + } else { + Response::send_msg(fd, TransStatus::Error)?; + return Err(ErrorKind::MigrationStatusErr( + request.status.to_string(), + TransStatus::VmConfig.to_string(), + ) + .into()); + } + + loop { + let request = Request::recv_msg(fd)?; + match request.status { + TransStatus::Memory => { + info!("Receive Memory status"); + Self::recv_vm_memory(fd, request.length)?; + } + TransStatus::State => { + info!("Receive State status"); + Self::recv_vmstate(fd)?; + break; + } + TransStatus::Cancel => { + info!("Receive Cancel status"); + Self::set_status(MigrationStatus::Canceled)?; + Response::send_msg(fd, TransStatus::Ok)?; + + bail!("Cancel migration from source"); + } + _ => { + warn!("Unable to distinguish status"); + } + } + } + + Ok(()) + } + + /// Send Vm configuration from source virtual machine. + fn send_vm_config(fd: &mut T) -> Result<()> + where + T: Write + Read, + { + let vm_config = &MIGRATION_MANAGER.vmm.read().unwrap().config; + let config_data = serde_json::to_vec(vm_config)?; + Request::send_msg(fd, TransStatus::VmConfig, config_data.len() as u64)?; + fd.write_all(&config_data)?; + + let result = Response::recv_msg(fd)?; + if result.is_err() { + return Err(ErrorKind::ResponseErr.into()); + } + + Ok(()) + } + + fn check_vm_config(fd: &mut T, len: u64) -> Result<()> + where + T: Write + Read, + { + let mut data: Vec = Vec::new(); + data.resize_with(len as usize, Default::default); + fd.read_exact(&mut data)?; + let source_config: VmConfig = serde_json::from_slice(&data)?; + let dest_config = &MIGRATION_MANAGER.vmm.read().unwrap().config; + + // Check vCPU number. + let source_cpu = source_config.machine_config.nr_cpus; + let dest_cpu = dest_config.machine_config.nr_cpus; + if source_cpu != dest_cpu { + return Err(ErrorKind::MigrationConfigErr( + "vCPU number".to_string(), + source_cpu.to_string(), + dest_cpu.to_string(), + ) + .into()); + } + + // Check memory size + let source_mem = source_config.machine_config.mem_config.mem_size; + let dest_mem = dest_config.machine_config.mem_config.mem_size; + if source_mem != dest_mem { + return Err(ErrorKind::MigrationConfigErr( + "memory size".to_string(), + source_mem.to_string(), + dest_mem.to_string(), + ) + .into()); + } + + // Check devices number. + let source_dev = source_config.devices.len(); + let dest_dev = dest_config.devices.len(); + if source_dev != dest_dev { + return Err(ErrorKind::MigrationConfigErr( + "device number".to_string(), + source_dev.to_string(), + dest_dev.to_string(), + ) + .into()); + } + + Response::send_msg(fd, TransStatus::Ok)?; + + Ok(()) + } + + /// Start to send dirty memory page iteratively. Return true if it should + /// continue to the next iteration. Otherwise, return false. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn iteration_send(fd: &mut T) -> Result + where + T: Write + Read, + { + let mut state = Self::send_dirty_memory(fd).chain_err(|| "Failed to send dirty memory")?; + + // Check the virtual machine downtime. + if MIGRATION_MANAGER + .limit + .read() + .unwrap() + .iteration_start_time + .elapsed() + < Duration::from_millis(MIGRATION_MANAGER.limit.read().unwrap().limit_downtime) + { + state = false; + } + // Update iteration start time. + MIGRATION_MANAGER + .limit + .write() + .unwrap() + .iteration_start_time = Instant::now(); + + Ok(state) + } + + /// Receive memory data from source VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + /// * `len` - The length of Block data. + fn recv_vm_memory(fd: &mut T, len: u64) -> Result<()> + where + T: Write + Read, + { + let mut blocks = Vec::::new(); + blocks.resize_with(len as usize / (size_of::()), Default::default); + fd.read_exact(unsafe { + std::slice::from_raw_parts_mut( + blocks.as_ptr() as *mut MemBlock as *mut u8, + len as usize, + ) + })?; + + if let Some(locked_memory) = &MIGRATION_MANAGER.vmm.read().unwrap().memory { + for block in blocks.iter() { + locked_memory.recv_memory( + fd, + MemBlock { + gpa: block.gpa, + len: block.len, + }, + )?; + } + } + + Response::send_msg(fd, TransStatus::Ok)?; + + Ok(()) + } + + /// Send memory data to destination VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + /// * `blocks` - The memory blocks need to be sent. + fn send_memory(fd: &mut T, blocks: Vec) -> Result<()> + where + T: Read + Write, + { + let len = size_of::() * blocks.len(); + Request::send_msg(fd, TransStatus::Memory, len as u64)?; + fd.write_all(unsafe { + std::slice::from_raw_parts(blocks.as_ptr() as *const MemBlock as *const u8, len) + })?; + + if let Some(locked_memory) = &MIGRATION_MANAGER.vmm.read().unwrap().memory { + for block in blocks.iter() { + locked_memory.send_memory( + fd, + MemBlock { + gpa: block.gpa, + len: block.len, + }, + )?; + } + } + + let result = Response::recv_msg(fd)?; + if result.is_err() { + return Err(ErrorKind::ResponseErr.into()); + } + + Ok(()) + } + + /// Send entire VM memory data to destination VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn send_vm_memory(fd: &mut T) -> Result<()> + where + T: Read + Write, + { + let mut blocks: Vec = Vec::new(); + let slots = KVM_FDS.load().get_mem_slots(); + for (_, slot) in slots.lock().unwrap().iter() { + blocks.push(MemBlock { + gpa: slot.guest_phys_addr, + len: slot.memory_size, + }); + } + + Self::send_memory(fd, blocks)?; + + Ok(()) + } + + /// Send dirty memory data to destination VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn send_dirty_memory(fd: &mut T) -> Result + where + T: Read + Write, + { + let mut blocks: Vec = Vec::new(); + let mem_slots = KVM_FDS.load().get_mem_slots(); + for (_, slot) in mem_slots.lock().unwrap().iter() { + let sub_blocks: Vec = Self::get_dirty_log(slot)?; + blocks.extend(sub_blocks); + } + + if blocks.is_empty() { + return Ok(false); + } + + Self::send_memory(fd, blocks)?; + + Ok(true) + } + + /// Send VM state data to destination VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn send_vmstate(fd: &mut T) -> Result<()> + where + T: Read + Write, + { + Request::send_msg(fd, TransStatus::State, 0)?; + Self::save_vmstate(None, fd)?; + + let result = Response::recv_msg(fd)?; + if result.is_err() { + return Err(ErrorKind::ResponseErr.into()); + } + + Ok(()) + } + + /// Receive VM state data from source VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn recv_vmstate(fd: &mut T) -> Result<()> + where + T: Write + Read, + { + let header = Self::restore_header(fd)?; + header.check_header()?; + let desc_db = Self::restore_desc_db(fd, header.desc_len) + .chain_err(|| "Failed to load device descriptor db")?; + Self::restore_vmstate(desc_db, fd).chain_err(|| "Failed to load snapshot device")?; + Self::resume()?; + + Response::send_msg(fd, TransStatus::Ok)?; + + Ok(()) + } + + /// Active migration status and synchronize the state of destination VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn active_migration(fd: &mut T) -> Result<()> + where + T: Read + Write, + { + Self::set_status(MigrationStatus::Active)?; + Request::send_msg(fd, TransStatus::Active, 0)?; + let result = Response::recv_msg(fd)?; + if result.is_err() { + return Err(ErrorKind::ResponseErr.into()); + } + + Ok(()) + } + + /// Synchronize the `Completed` status of destination VM + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn complete_migration(fd: &mut T) -> Result<()> + where + T: Write + Read, + { + Self::set_status(MigrationStatus::Completed)?; + Request::send_msg(fd, TransStatus::Complete, 0)?; + let result = Response::recv_msg(fd)?; + if result.is_err() { + return Err(ErrorKind::ResponseErr.into()); + } + + Ok(()) + } + + /// Finish the migration of destination VM and notify the source VM. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + pub fn finish_migration(fd: &mut T) -> Result<()> + where + T: Write + Read, + { + // Receive complete status from source vm. + let request = Request::recv_msg(fd)?; + if request.status == TransStatus::Complete { + info!("Receive Complete status"); + Self::set_status(MigrationStatus::Completed)?; + Response::send_msg(fd, TransStatus::Ok)?; + } else { + return Err(ErrorKind::MigrationStatusErr( + request.status.to_string(), + TransStatus::Complete.to_string(), + ) + .into()); + } + + Ok(()) + } + + /// Cancel live migration. + /// + /// # Arguments + /// + /// * `fd` - The fd implements `Read` and `Write` trait object. + fn cancel_migration(fd: &mut T) -> Result<()> + where + T: Write + Read, + { + // Stop logging dirty pages. + Self::stop_dirty_log().chain_err(|| "Failed to stop logging dirty page")?; + + Request::send_msg(fd, TransStatus::Cancel, 0)?; + let result = Response::recv_msg(fd)?; + if result.is_err() { + return Err(ErrorKind::ResponseErr.into()); + } + + Ok(()) + } + + /// Clear live migration environment and shut down VM. + fn clear_migration() -> Result<()> { + if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { + locked_vm.lock().unwrap().destroy(); + } + + Ok(()) + } + + /// Recover the virtual machine if migration is failed. + pub fn recover_from_migration() -> Result<()> { + if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { + locked_vm.lock().unwrap().resume(); + } + + Ok(()) + } +} + /// Dirty bitmap information of vmm memory slot. pub struct DirtyBitmap { /// Guest address. -- Gitee From def02cb72682262e5d6c2476660627b30e9aeac4 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 14:21:38 +0800 Subject: [PATCH 0098/2187] migration: implement migration for x86/arm platform Add starting/cancel live migration interface for x86_64 and AArch64 platform. Signed-off-by: Xinle.Guo --- Cargo.lock | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 142 +++++++++++++++++-------- machine/src/micro_vm/mod.rs | 118 ++++++++++---------- machine/src/standard_vm/aarch64/mod.rs | 73 ++++++------- machine/src/standard_vm/mod.rs | 16 +-- machine/src/standard_vm/x86_64/mod.rs | 88 +++++++-------- migration/src/lib.rs | 142 +++++++++++++++++++++++++ 8 files changed, 379 insertions(+), 202 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64897c5a9..2b27275b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,7 @@ dependencies = [ "log", "machine_manager", "migration", + "migration_derive", "pci", "serde", "serde_json", diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 5f6006ebe..4a460e101 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -24,6 +24,7 @@ devices = { path = "../devices" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } +migration_derive = { path = "../migration_derive" } pci = { path = "../pci" } sysbus = { path = "../sysbus" } util = { path = "../util" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8b83547ab..2463a2cc8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -94,21 +94,22 @@ pub mod errors { mod micro_vm; mod standard_vm; - -pub use micro_vm::LightMachine; -use pci::{PciBus, PciDevOps, PciHost, RootPort}; -pub use standard_vm::StdMachine; -use sysbus::{SysBus, SysBusDevOps}; -use virtio::{ - BlockState, RngState, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioState, - VirtioNetState, -}; +#[cfg(target_arch = "x86_64")] +mod vm_state; use std::collections::BTreeMap; -use std::os::unix::io::AsRawFd; +use std::fs::remove_file; +use std::net::TcpListener; +use std::os::unix::{io::AsRawFd, net::UnixListener}; use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; +use error_chain::bail; +use kvm_ioctls::VcpuFd; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +pub use micro_vm::LightMachine; + #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{ @@ -118,28 +119,34 @@ use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; -use error_chain::bail; +use errors::{ErrorKind, Result, ResultExt}; use hypervisor::kvm::KVM_FDS; -use kvm_ioctls::VcpuFd; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, MachineMemConfig, - NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, - VfioConfig, VmConfig, FAST_UNPLUG_ON, + MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, + SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; -use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{KvmVmState, MachineInterface}; -use migration::{MigrationManager, MigrationRestoreOrder}; -use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; -use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; -use vfio::{VfioDevice, VfioPciDevice}; -use virtio::{balloon_allow_list, Balloon, Block, Console, Rng, VirtioMmioDevice, VirtioPciDevice}; -use vmm_sys_util::epoll::EventSet; -use vmm_sys_util::eventfd::EventFd; - -use errors::{ErrorKind, Result, ResultExt}; +use machine_manager::{ + event_loop::EventLoop, + machine::{KvmVmState, MachineInterface}, +}; +use migration::MigrationManager; +use pci::{PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::errors::Result as StdResult; +pub use standard_vm::StdMachine; +use sysbus::{SysBus, SysBusDevOps}; +use util::{ + loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, + seccomp::{BpfRule, SeccompOpt, SyscallFilter}, +}; +use vfio::{VfioDevice, VfioPciDevice}; +use virtio::{ + balloon_allow_list, Balloon, Block, BlockState, Console, Rng, RngState, VhostKern, VhostUser, + VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, + VirtioPciDevice, +}; pub trait MachineOps { /// Calculate the ranges of memory according to architecture. @@ -168,7 +175,6 @@ pub trait MachineOps { mem_config: &MachineMemConfig, #[cfg(target_arch = "x86_64")] sys_io: &Arc, sys_mem: &Arc, - is_migrate: bool, nr_cpus: u8, ) -> Result<()> { // KVM_CREATE_VM system call is invoked when KVM_FDS is used for the first time. The system @@ -176,7 +182,8 @@ pub trait MachineOps { // doing memory prealloc.To avoid affecting memory prealloc performance, create_host_mmaps // needs to be invoked first. let mut mem_mappings = Vec::new(); - if !is_migrate { + let migrate_mode = self.get_migrate_mode(); + if migrate_mode != MigrateMode::File { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) .chain_err(|| "Failed to mmap guest ram.")?; @@ -194,7 +201,7 @@ pub trait MachineOps { .register_listener(Arc::new(Mutex::new(KvmIoListener::default()))) .chain_err(|| "Failed to register KVM listener for I/O address space.")?; - if !is_migrate { + if migrate_mode != MigrateMode::File { for mmap in mem_mappings.iter() { let base = mmap.start_address().raw_value(); let size = mmap.size(); @@ -244,11 +251,7 @@ pub trait MachineOps { )); cpus.push(cpu.clone()); - MigrationManager::register_device_instance( - cpu::ArchCPU::descriptor(), - cpu, - MigrationRestoreOrder::Default, - ); + MigrationManager::register_cpu_instance(cpu::ArchCPU::descriptor(), cpu, vcpu_id); } if let Some(boot_config) = boot_cfg { @@ -305,17 +308,18 @@ pub trait MachineOps { let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); if cfg_args.contains("vhost-vsock-device") { let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); - MigrationManager::register_device_instance_mutex( + MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, + &device_cfg.id, ); } else { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let virtio_pci_device = VirtioPciDevice::new( - device_cfg.id, + device_cfg.id.clone(), devfn, sys_mem, vsock.clone(), @@ -326,9 +330,10 @@ pub trait MachineOps { .realize() .chain_err(|| "Failed to add virtio pci vsock device")?; } - MigrationManager::register_device_instance_mutex( + MigrationManager::register_device_instance( VhostKern::VsockState::descriptor(), vsock, + &device_cfg.id, ); Ok(()) @@ -343,6 +348,12 @@ pub trait MachineOps { fn get_sys_mem(&mut self) -> &Arc; + fn get_vm_config(&self) -> &Mutex; + + /// Get migration mode from VM config. There are four modes in total: + /// Tcp, Unix, File and Unknown. + fn get_migrate_mode(&self) -> MigrateMode; + /// Add net device. /// /// # Arguments @@ -390,13 +401,13 @@ pub trait MachineOps { if let Some(serial) = &vm_config.virtio_serial { if serial.pci_bdf.is_none() { let device = VirtioMmioDevice::new(sys_mem, console.clone()); - MigrationManager::register_device_instance_mutex( + MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, + &device_cfg.id, ); } else { - let name = device_cfg.id; let virtio_serial_info = if let Some(serial_info) = &vm_config.virtio_serial { serial_info } else { @@ -408,7 +419,7 @@ pub trait MachineOps { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); let virtio_pci_device = VirtioPciDevice::new( - name, + device_cfg.id.clone(), devfn, sys_mem, console.clone(), @@ -422,7 +433,11 @@ pub trait MachineOps { } else { bail!("No virtio-serial-bus specified"); } - MigrationManager::register_device_instance_mutex(VirtioConsoleState::descriptor(), console); + MigrationManager::register_device_instance( + VirtioConsoleState::descriptor(), + console, + &device_cfg.id, + ); Ok(()) } @@ -452,7 +467,7 @@ pub trait MachineOps { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); let vitio_pci_device = VirtioPciDevice::new( - device_cfg.id, + device_cfg.id.clone(), devfn, sys_mem, rng_dev.clone(), @@ -463,7 +478,7 @@ pub trait MachineOps { .realize() .chain_err(|| "Failed to add pci rng device")?; } - MigrationManager::register_device_instance_mutex(RngState::descriptor(), rng_dev); + MigrationManager::register_device_instance(RngState::descriptor(), rng_dev, &device_cfg.id); Ok(()) } @@ -603,7 +618,7 @@ pub trait MachineOps { self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); } } - MigrationManager::register_device_instance_mutex_with_id( + MigrationManager::register_device_instance( BlockState::descriptor(), device, &device_cfg.id, @@ -632,7 +647,7 @@ pub trait MachineOps { } } else { let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone()))); - MigrationManager::register_device_instance_mutex_with_id( + MigrationManager::register_device_instance( VirtioNetState::descriptor(), device.clone(), &device_cfg.id, @@ -995,7 +1010,7 @@ pub trait MachineOps { /// /// * `vm` - The machine structure. /// * `vm_config` - VM configuration. - fn realize(vm: &Arc>, vm_config: &mut VmConfig, is_migrate: bool) -> Result<()> + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> where Self: Sized; @@ -1158,4 +1173,41 @@ pub trait MachineOps { Ok(()) } + + fn start_incoming(&self) -> Result<()> { + let mut locked_vm_config = self.get_vm_config().lock().unwrap(); + if let Some((mode, path)) = locked_vm_config.incoming.as_ref() { + match mode { + MigrateMode::File => { + MigrationManager::restore_snapshot(path) + .chain_err(|| "Failed to restore snapshot")?; + } + MigrateMode::Unix => { + let listener = UnixListener::bind(path)?; + let (mut sock, _) = listener.accept()?; + remove_file(&path)?; + + MigrationManager::recv_migration(&mut sock) + .chain_err(|| "Failed to receive migration with unix mode")?; + } + MigrateMode::Tcp => { + let listener = TcpListener::bind(&path)?; + let mut sock = listener.accept().map(|(stream, _)| stream)?; + + MigrationManager::recv_migration(&mut sock) + .chain_err(|| "Failed to receive migration with tcp mode")?; + } + MigrateMode::Unknown => { + bail!("Unknown migration mode"); + } + } + } + + // End the migration and reset the mode. + if let Some((mode, _)) = locked_vm_config.incoming.as_mut() { + *mode = MigrateMode::Unknown; + } + + Ok(()) + } } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 0e9e99200..d2f4cfafa 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -66,6 +66,10 @@ use std::path::Path; use std::sync::{Arc, Condvar, Mutex}; use std::vec::Vec; +use error_chain::{bail, ChainedError}; +use log::error; +use vmm_sys_util::eventfd::EventFd; + use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState, CpuTopology, CPU}; @@ -76,13 +80,13 @@ use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; #[cfg(target_arch = "aarch64")] use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController}; -use error_chain::ChainedError; use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; -use machine_manager::config::parse_blk; -use machine_manager::config::parse_net; -use machine_manager::config::BlkDevConfig; +use machine_manager::config::{ + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, MigrateMode, +}; +use machine_manager::event; use machine_manager::machine::{ DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, @@ -91,32 +95,27 @@ use machine_manager::{ config::{BootSource, ConfigCheck, NetworkInterfaceConfig, SerialConfig, VmConfig}, qmp::{qmp_schema, QmpChannel, Response}, }; +use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use migration::{MigrationManager, MigrationStatus}; use sysbus::SysBus; #[cfg(target_arch = "aarch64")] use sysbus::{SysBusDevType, SysRes}; +use syscall::syscall_whitelist; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, CompileFDT, FdtBuilder}; -use util::loop_context::EventLoopManager; -use util::seccomp::BpfRule; -use util::set_termi_canon_mode; +use util::{loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode}; use virtio::{ create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, }; -use vmm_sys_util::eventfd::EventFd; use self::errors::{ErrorKind, Result}; use super::{ errors::{ErrorKind as MachineErrorKind, Result as MachineResult}, MachineOps, }; - -use error_chain::bail; -use log::error; -use machine_manager::event; -use mem_layout::{LayoutEntryType, MEM_LAYOUT}; -use syscall::syscall_whitelist; +#[cfg(target_arch = "x86_64")] +use crate::vm_state; // The replaceable block device maximum count. const MMIO_REPLACEABLE_BLK_NR: usize = 4; @@ -188,6 +187,8 @@ pub struct LightMachine { boot_source: Arc>, // VM power button, handle VM `Shutdown` event. power_button: EventFd, + // All configuration information of virtual machine. + vm_config: Mutex, } impl LightMachine { @@ -225,10 +226,6 @@ impl LightMachine { let power_button = EventFd::new(libc::EFD_NONBLOCK) .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?; - if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { - error!("{}", e); - } - Ok(LightMachine { cpu_topo: CpuTopology::new( vm_config.machine_config.nr_cpus, @@ -250,6 +247,7 @@ impl LightMachine { boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, power_button, + vm_config: Mutex::new(vm_config.clone()), }) } @@ -278,35 +276,43 @@ impl LightMachine { use errors::ResultExt; let mut rpl_devs: Vec = Vec::new(); - for _ in 0..MMIO_REPLACEABLE_BLK_NR { + for id in 0..MMIO_REPLACEABLE_BLK_NR { let block = Arc::new(Mutex::new(Block::default())); let virtio_mmio = VirtioMmioDevice::new(&self.sys_mem, block.clone()); rpl_devs.push(virtio_mmio); - MigrationManager::register_device_instance_mutex(BlockState::descriptor(), block); + MigrationManager::register_device_instance( + BlockState::descriptor(), + block, + &id.to_string(), + ); } - for _ in 0..MMIO_REPLACEABLE_NET_NR { + for id in 0..MMIO_REPLACEABLE_NET_NR { let net = Arc::new(Mutex::new(Net::default())); let virtio_mmio = VirtioMmioDevice::new(&self.sys_mem, net.clone()); rpl_devs.push(virtio_mmio); - MigrationManager::register_device_instance_mutex(VirtioNetState::descriptor(), net); + MigrationManager::register_device_instance( + VirtioNetState::descriptor(), + net, + &id.to_string(), + ); } let mut region_base = self.sysbus.min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; - for dev in rpl_devs { + for (id, dev) in rpl_devs.into_iter().enumerate() { self.replaceable_info .devices .lock() .unwrap() .push(MmioReplaceableDevInfo { device: dev.device.clone(), - id: "".to_string(), + id: id.to_string(), used: false, }); - MigrationManager::register_device_instance_mutex( + MigrationManager::register_device_instance( VirtioMmioState::descriptor(), VirtioMmioDevice::realize( dev, @@ -317,6 +323,7 @@ impl LightMachine { &self.boot_source, ) .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, + &id.to_string(), ); region_base += region_size; } @@ -626,6 +633,17 @@ impl MachineOps for LightMachine { &self.sys_mem } + fn get_vm_config(&self) -> &Mutex { + &self.vm_config + } + + fn get_migrate_mode(&self) -> MigrateMode { + if let Some(incoming) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return incoming.0; + } + MigrateMode::Unknown + } + fn get_sys_bus(&mut self) -> &SysBus { &self.sysbus } @@ -720,11 +738,7 @@ impl MachineOps for LightMachine { syscall_whitelist() } - fn realize( - vm: &Arc>, - vm_config: &mut VmConfig, - is_migrate: bool, - ) -> MachineResult<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { use crate::errors::ResultExt; let mut locked_vm = vm.lock().unwrap(); @@ -734,7 +748,6 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "x86_64")] &locked_vm.sys_io, &locked_vm.sys_mem, - is_migrate, vm_config.machine_config.nr_cpus, )?; @@ -763,7 +776,8 @@ impl MachineOps for LightMachine { .chain_err(|| "Failed to create replaceable devices.")?; locked_vm.add_devices(vm_config)?; - let boot_config = if !is_migrate { + let incoming = locked_vm.get_migrate_mode(); + let boot_config = if incoming == MigrateMode::Unknown { Some(locked_vm.load_boot_source(None)?) } else { None @@ -800,6 +814,17 @@ impl MachineOps for LightMachine { locked_vm .register_power_event(&locked_vm.power_button) .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?; + + MigrationManager::register_vm_instance(vm.clone()); + #[cfg(target_arch = "x86_64")] + MigrationManager::register_kvm_instance( + vm_state::KvmDeviceState::descriptor(), + Arc::new(vm_state::KvmDevice {}), + ); + if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { + bail!("Failed to set migration status {}", e); + } + Ok(()) } @@ -1254,24 +1279,8 @@ impl DeviceInterface for LightMachine { impl MigrateInterface for LightMachine { fn migrate(&self, uri: String) -> Response { - use util::unix::{parse_uri, UnixPath}; - - match parse_uri(&uri) { - Ok((UnixPath::File, path)) => { - if let Err(e) = MigrationManager::save_snapshot(&path) { - error!( - "Failed to migrate to path \'{:?}\': {}", - path, - e.display_chain() - ); - let _ = MigrationManager::set_status(MigrationStatus::Failed) - .map_err(|e| error!("{}", e)); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - } + match parse_incoming_uri(&uri) { + Ok((MigrateMode::File, path)) => migration::snapshot(path), _ => { return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), @@ -1279,17 +1288,10 @@ impl MigrateInterface for LightMachine { ); } } - - Response::create_empty_response() } fn query_migrate(&self) -> Response { - let status_str = MigrationManager::migration_get_status().to_string(); - let migration_info = qmp_schema::MigrationInfo { - status: Some(status_str), - }; - - Response::create_response(serde_json::to_value(migration_info).unwrap(), None) + migration::query_migrate() } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 09560c5e4..57339bb48 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -18,6 +18,9 @@ use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; +use error_chain::bail; +use vmm_sys_util::eventfd::EventFd; + use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, @@ -36,11 +39,10 @@ use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; use devices::{ICGICConfig, ICGICv3Config, InterruptController}; -use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; -use log::error; use machine_manager::config::{ - BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, MigrateMode, NumaNode, NumaNodes, PFlashConfig, + SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -50,19 +52,18 @@ use machine_manager::machine::{ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; +use pci_host_root::PciHostRoot; use sysbus::{SysBus, SysBusDevType, SysRes}; +use syscall::syscall_whitelist; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; -use vmm_sys_util::eventfd::EventFd; use super::{errors::Result as StdResult, AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind, Result}; use crate::MachineOps; -use pci_host_root::PciHostRoot; -use syscall::syscall_whitelist; /// The type of memory layout entry on aarch64 #[allow(dead_code)] @@ -126,6 +127,7 @@ pub struct StdMachine { boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. power_button: EventFd, + /// All configuration information of virtual machine. vm_config: Mutex, /// Reset request, handle VM `Reset` event. reset_req: EventFd, @@ -313,10 +315,6 @@ impl StdMachineOps for StdMachine { &self.cpus } - fn get_vm_config(&self) -> &Mutex { - &self.vm_config - } - fn get_numa_nodes(&self) -> &Option { &self.numa_nodes } @@ -417,7 +415,7 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn realize(vm: &Arc>, vm_config: &mut VmConfig, is_migrate: bool) -> Result<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { use super::errors::ErrorKind as StdErrorKind; use crate::errors::ResultExt; @@ -431,7 +429,6 @@ impl MachineOps for StdMachine { locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_mem, - is_migrate, vm_config.machine_config.nr_cpus, )?; @@ -460,7 +457,8 @@ impl MachineOps for StdMachine { .chain_err(|| "Failed to add devices")?; let fwcfg = locked_vm.add_fwcfg_device()?; - let boot_config = if !is_migrate { + let incoming = locked_vm.get_migrate_mode(); + let boot_config = if incoming == MigrateMode::Unknown { Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { None @@ -495,7 +493,7 @@ impl MachineOps for StdMachine { .chain_err(|| ErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } - if !is_migrate { + if incoming == MigrateMode::Unknown { locked_vm .build_acpi_tables(&fwcfg) .chain_err(|| "Failed to create ACPI tables")?; @@ -503,6 +501,8 @@ impl MachineOps for StdMachine { locked_vm.register_power_event(&locked_vm.power_button)?; + MigrationManager::register_vm_config(vm_config); + MigrationManager::register_vm_instance(vm.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } @@ -550,6 +550,18 @@ impl MachineOps for StdMachine { &self.sys_mem } + fn get_vm_config(&self) -> &Mutex { + &self.vm_config + } + + fn get_migrate_mode(&self) -> MigrateMode { + if let Some(incoming) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return incoming.0; + } + + MigrateMode::Unknown + } + fn get_pci_host(&mut self) -> StdResult<&Arc>> { Ok(&self.pci_host) } @@ -951,24 +963,10 @@ impl MachineAddressInterface for StdMachine { impl MigrateInterface for StdMachine { fn migrate(&self, uri: String) -> Response { - use util::unix::{parse_uri, UnixPath}; - - match parse_uri(&uri) { - Ok((UnixPath::File, path)) => { - if let Err(e) = MigrationManager::save_snapshot(&path) { - error!( - "Failed to migrate to path \'{:?}\': {}", - path, - e.display_chain() - ); - let _ = MigrationManager::set_status(MigrationStatus::Failed) - .map_err(|e| error!("{}", e)); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - } + match parse_incoming_uri(&uri) { + Ok((MigrateMode::File, path)) => migration::snapshot(path), + Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), + Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), _ => { return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), @@ -976,17 +974,14 @@ impl MigrateInterface for StdMachine { ); } } - - Response::create_empty_response() } fn query_migrate(&self) -> Response { - let status_str = MigrationManager::migration_get_status().to_string(); - let migration_info = qmp_schema::MigrationInfo { - status: Some(status_str), - }; + migration::query_migrate() + } - Response::create_response(serde_json::to_value(migration_info).unwrap(), None) + fn cancel_migrate(&self) -> Response { + migration::cancel_migrate() } } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index e71822a71..771b0a4ef 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -79,7 +79,7 @@ use error_chain::{bail, ChainedError}; use errors::{Result, ResultExt}; use machine_manager::config::{ get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, NetworkInterfaceConfig, - NumaNode, NumaNodes, PciBdf, VmConfig, + NumaNode, NumaNodes, PciBdf, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -205,8 +205,6 @@ trait StdMachineOps: AcpiBuilder { fn get_cpus(&self) -> &Vec>; - fn get_vm_config(&self) -> &Mutex; - fn get_numa_nodes(&self) -> &Option; /// Register event notifier for reset of standard machine. @@ -815,11 +813,7 @@ impl StdMachine { } } - MigrationManager::register_device_instance_mutex_with_id( - BlockState::descriptor(), - blk, - &blk_id, - ); + MigrationManager::register_device_instance(BlockState::descriptor(), blk, &blk_id); Ok(()) } @@ -863,11 +857,7 @@ impl StdMachine { let net = Arc::new(Mutex::new(virtio::Net::new(dev))); self.add_virtio_pci_device(&args.id, pci_bdf, net.clone(), multifunction, false) .chain_err(|| "Failed to add virtio net device")?; - MigrationManager::register_device_instance_mutex_with_id( - VirtioNetState::descriptor(), - net, - &net_id, - ); + MigrationManager::register_device_instance(VirtioNetState::descriptor(), net, &net_id); } Ok(()) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 4c5dc3925..bd22205fb 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -20,6 +20,10 @@ use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; +use error_chain::{bail, ChainedError}; +use log::error; +use vmm_sys_util::{eventfd::EventFd, ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; + use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, @@ -32,12 +36,11 @@ use devices::legacy::{ errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, }; -use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; -use log::error; use machine_manager::config::{ - BootIndexInfo, BootSource, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, MigrateMode, NumaNode, NumaNodes, PFlashConfig, + SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -50,18 +53,15 @@ use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; use sysbus::SysBus; use syscall::syscall_whitelist; -use util::byte_code::ByteCode; -use util::loop_context::EventLoopManager; -use util::seccomp::BpfRule; -use util::set_termi_canon_mode; -use vmm_sys_util::eventfd::EventFd; -use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; +use util::{ + byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, +}; use self::ich9_lpc::SLEEP_CTRL_OFFSET; use super::errors::{ErrorKind, Result}; use super::{AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; -use crate::MachineOps; +use crate::{vm_state, MachineOps}; const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -113,6 +113,7 @@ pub struct StdMachine { boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. power_button: EventFd, + /// All configuration information of virtual machine. vm_config: Mutex, /// List of guest NUMA nodes information. numa_nodes: Option, @@ -336,10 +337,6 @@ impl StdMachineOps for StdMachine { &self.cpus } - fn get_vm_config(&self) -> &Mutex { - &self.vm_config - } - fn get_numa_nodes(&self) -> &Option { &self.numa_nodes } @@ -442,11 +439,7 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn realize( - vm: &Arc>, - vm_config: &mut VmConfig, - is_migrate: bool, - ) -> MachineResult<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { use crate::errors::ResultExt; let clone_vm = vm.clone(); @@ -457,7 +450,6 @@ impl MachineOps for StdMachine { &vm_config.machine_config.mem_config, &locked_vm.sys_io, &locked_vm.sys_mem, - is_migrate, vm_config.machine_config.nr_cpus, )?; @@ -479,7 +471,8 @@ impl MachineOps for StdMachine { locked_vm.add_devices(vm_config)?; let fwcfg = locked_vm.add_fwcfg_device()?; - let boot_config = if !is_migrate { + let incoming = locked_vm.get_migrate_mode(); + let boot_config = if incoming == MigrateMode::Unknown { Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { None @@ -497,7 +490,7 @@ impl MachineOps for StdMachine { &boot_config, )?); - if !is_migrate { + if incoming == MigrateMode::Unknown { locked_vm .build_acpi_tables(&fwcfg) .chain_err(|| "Failed to create ACPI tables")?; @@ -510,6 +503,12 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + MigrationManager::register_vm_config(vm_config); + MigrationManager::register_vm_instance(vm.clone()); + MigrationManager::register_kvm_instance( + vm_state::KvmDeviceState::descriptor(), + Arc::new(vm_state::KvmDevice {}), + ); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } @@ -591,6 +590,18 @@ impl MachineOps for StdMachine { &self.sys_mem } + fn get_vm_config(&self) -> &Mutex { + &self.vm_config + } + + fn get_migrate_mode(&self) -> MigrateMode { + if let Some(incoming) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return incoming.0; + } + + MigrateMode::Unknown + } + fn get_pci_host(&mut self) -> Result<&Arc>> { Ok(&self.pci_host) } @@ -903,24 +914,10 @@ impl MachineAddressInterface for StdMachine { impl MigrateInterface for StdMachine { fn migrate(&self, uri: String) -> Response { - use util::unix::{parse_uri, UnixPath}; - - match parse_uri(&uri) { - Ok((UnixPath::File, path)) => { - if let Err(e) = MigrationManager::save_snapshot(&path) { - error!( - "Failed to migrate to path \'{:?}\': {}", - path, - e.display_chain() - ); - let _ = MigrationManager::set_status(MigrationStatus::Failed) - .map_err(|e| error!("{}", e)); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - } + match parse_incoming_uri(&uri) { + Ok((MigrateMode::File, path)) => migration::snapshot(path), + Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), + Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), _ => { return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), @@ -928,17 +925,14 @@ impl MigrateInterface for StdMachine { ); } } - - Response::create_empty_response() } fn query_migrate(&self) -> Response { - let status_str = MigrationManager::migration_get_status().to_string(); - let migration_info = qmp_schema::MigrationInfo { - status: Some(status_str), - }; + migration::query_migrate() + } - Response::create_response(serde_json::to_value(migration_info).unwrap(), None) + fn cancel_migrate(&self) -> Response { + migration::cancel_migrate() } } diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 9f40a3968..e0b75325c 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -25,8 +25,14 @@ pub mod migration; pub mod protocol; pub mod snapshot; +use std::{net::TcpStream, os::unix::net::UnixStream, thread}; + +use error_chain::ChainedError; + +use machine_manager::qmp::{qmp_schema, Response}; pub use manager::{MigrationHook, MigrationManager}; pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; +use std::time::Duration; pub mod errors { use super::protocol::MigrationStatus; @@ -87,3 +93,139 @@ pub mod errors { } } } + +/// Start to snapshot VM. +/// +/// # Arguments +/// +/// * `path` - snapshot dir path. If path dir not exists, will create it. +pub fn snapshot(path: String) -> Response { + if let Err(e) = MigrationManager::save_snapshot(&path) { + error!( + "Failed to migrate to path \'{:?}\': {}", + path, + e.display_chain() + ); + let _ = MigrationManager::set_status(MigrationStatus::Failed).map_err(|e| error!("{}", e)); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + + Response::create_empty_response() +} + +/// Start to migrate VM with unix mode. +/// +/// # Arguments +/// +/// * `path` - Unix socket path, as /tmp/migration.socket. +pub fn migration_unix_mode(path: String) -> Response { + let mut socket = match UnixStream::connect(path) { + Ok(_sock) => { + // Specify the tcp receiving or send timeout. + let time_out = Some(Duration::from_secs(30)); + _sock + .set_read_timeout(time_out) + .unwrap_or_else(|e| error!("{}", e)); + _sock + .set_write_timeout(time_out) + .unwrap_or_else(|e| error!("{}", e)); + _sock + } + Err(e) => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ) + } + }; + + if let Err(e) = thread::Builder::new() + .name("unix_migrate".to_string()) + .spawn(move || { + if let Err(e) = MigrationManager::send_migration(&mut socket) { + error!("Failed to send migration: {}", e.display_chain()); + let _ = MigrationManager::recover(); + let _ = MigrationManager::set_status(MigrationStatus::Failed) + .map_err(|e| error!("{}", e)); + } + }) + { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + + Response::create_empty_response() +} + +/// Start to migrate VM with tcp mode. +/// +/// # Arguments +/// +/// * `path` - Tcp ip and port, as 192.168.1.1:4446. +pub fn migration_tcp_mode(path: String) -> Response { + let mut socket = match TcpStream::connect(path) { + Ok(_sock) => { + // Specify the tcp receiving or send timeout. + let time_out = Some(Duration::from_secs(30)); + _sock + .set_read_timeout(time_out) + .unwrap_or_else(|e| error!("{}", e)); + _sock + .set_write_timeout(time_out) + .unwrap_or_else(|e| error!("{}", e)); + _sock + } + Err(e) => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ) + } + }; + + if let Err(e) = thread::Builder::new() + .name("tcp_migrate".to_string()) + .spawn(move || { + if let Err(e) = MigrationManager::send_migration(&mut socket) { + error!("Failed to send migration: {}", e.display_chain()); + let _ = MigrationManager::recover(); + let _ = MigrationManager::set_status(MigrationStatus::Failed) + .map_err(|e| error!("{}", e)); + } + }) + { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + }; + + Response::create_empty_response() +} + +/// Query the current migration status. +pub fn query_migrate() -> Response { + let status_str = MigrationManager::status().to_string(); + let migration_info = qmp_schema::MigrationInfo { + status: Some(status_str), + }; + + Response::create_response(serde_json::to_value(migration_info).unwrap(), None) +} + +/// Cancel the current migration. +pub fn cancel_migrate() -> Response { + if let Err(e) = MigrationManager::set_status(MigrationStatus::Canceled) { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + + Response::create_empty_response() +} -- Gitee From 93b3ee1428bfa1047ab3e43c47235d818db0aeb6 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 21 Jul 2022 14:22:13 +0800 Subject: [PATCH 0099/2187] migration: add command line and guidance doc for live migration 1. Add `-incoming xxx` command line to entry migration mode. 2. Add code to parse `migration` and `cancel_migration` qmp command lind. 3. Add guidance documents for start or cancel live migration. Signed-off-by: Xinle.Guo --- docs/migration.md | 111 +++++++++++++++++++++ machine/src/lib.rs | 110 +++++++++++++------- machine/src/micro_vm/mod.rs | 23 +++-- machine/src/standard_vm/aarch64/mod.rs | 28 +++--- machine/src/standard_vm/x86_64/mod.rs | 28 +++--- machine_manager/src/cmdline.rs | 6 +- machine_manager/src/config/incoming.rs | 133 +++++++++++++++++++++++++ machine_manager/src/config/mod.rs | 3 + machine_manager/src/machine.rs | 4 + machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 21 ++++ migration/src/lib.rs | 4 +- src/main.rs | 21 +--- util/src/unix.rs | 46 ++------- 14 files changed, 409 insertions(+), 130 deletions(-) create mode 100644 docs/migration.md create mode 100644 machine_manager/src/config/incoming.rs diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 000000000..2aefd00cc --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,111 @@ +# Live migration + +## Introduction + +Virtual machine live migration is the key feature provided by StratoVirt. It needs to execute virtual machine migration +when any of the following happens: +1. Server overload: when a source server is overloaded, a set of the VMs from this server is migrated to an underloaded + server using VM migration technique. +2. Server maintenance: if there is a need for server maintenance, VMs from the source server are migrated to another server. +3. Server fault: whenever there is server fault, VMs are migrated from the faulty server to the target server. + +## Transports + +The migration stream can be passed over any transport as following: +1. TCP mode migration: using tcp sockets to do the migration. +2. UNIX mode migration: using unix sockets to do the migration. + +Note: UNIX mode only supports migrate two VMs on the same host OS. TCP mode supports migrate both on the same or + different host OS. + +## Migration + +Launch the source VM: +```shell +./stratovirt \ + -machine q35 \ + -kernel ./vmlinux.bin \ + -append "console=ttyS0 pci=off reboot=k quiet panic=1 root=/dev/vda" \ + -drive file=path/to/rootfs,id=rootfs,readonly=off,direct=off \ + -device virtio-blk-pci,drive=rootfs,id=rootfs,bus=pcie.0,addr=0 \ + -qmp unix:path/to/socket1,server,nowait \ + -serial stdio \ +``` + +Launch the destination VM: +```shell +./stratovirt \ + -machine q35 \ + -kernel ./vmlinux.bin \ + -append "console=ttyS0 pci=off reboot=k quiet panic=1 root=/dev/vda" \ + -drive file=path/to/rootfs,id=rootfs,readonly=off,direct=off \ + -device virtio-blk-pci,drive=rootfs,id=rootfs,bus=pcie.0,addr=0 \ + -qmp unix:path/to/socket2,server,nowait \ + -serial stdio \ + -incoming tcp:192.168.0.1:4446 \ +``` + +Note: The destination VM command line parameter needs to be consistent with the source VM. If uses UNIX mode, the +parameter `-incoming tcp:192.168.0.1:4446` replace with `-incoming unix:/tmp/stratovirt-migrate.socket`. The following +are the same. + +Start to send migration for the source VM: +```shell +$ ncat -U path/to/socket1 +{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +{"execute":"migrate", "arguments":{"uri":"tcp:192.168.0.1:4446"}} +{"return":{}} +``` + +When finish executing the command line, the live migration is start. in a moment, the source VM should be successfully +migrated to the destination VM. + +## Cancel Migration + +If you want to cancel the live migration, executing the following command: +```shell +$ ncat -U path/to/socket1 +{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +{"execute":"migrate_cancel"} +{"return":{}} +``` + +## Query migration state + +Use QMP command `query-migrate` to check migration state: +```shell +$ ncat -U path/to/socket +{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +{"execute":"query-migrate"} +{"return":{"status":"completed"}} +``` + +Now there are 5 states during migration: +- `None`: Resource is not prepared all. +- `Setup`: Resource is setup, ready to migration. +- `Active`: In migration. +- `Completed`: Migration completed. +- `Failed`: Migration failed. +- `Canceled`: Migration canceled. + +## Limitations + +Migration supports machine type: +- `q35` (on x86_64 platform) +- `virt` (on aarch64 platform) + +Some devices and feature don't support to be migration yet: +- `vhost-net` +- `vhost-user-net` +- `vfio` devices +- `balloon` +- `mem-shared`,`backend file of memory` + +Some device attributes can't be changed: +- `virtio-net`: mac +- `virtio-blk`: file(only ordinary file or copy file), serial_num +- `device`: bus, addr +- `smp` +- `m` + +If hot plug device before migrate source vm, add newly replaced device command should be add to destination vm. \ No newline at end of file diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 2463a2cc8..834a3de4e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -124,7 +124,7 @@ use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, - parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, MachineMemConfig, + parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; @@ -138,6 +138,7 @@ use standard_vm::errors::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; use util::{ + arg_parser, loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; @@ -182,8 +183,8 @@ pub trait MachineOps { // doing memory prealloc.To avoid affecting memory prealloc performance, create_host_mmaps // needs to be invoked first. let mut mem_mappings = Vec::new(); - let migrate_mode = self.get_migrate_mode(); - if migrate_mode != MigrateMode::File { + let migrate_info = self.get_migrate_info(); + if migrate_info.0 != MigrateMode::File { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) .chain_err(|| "Failed to mmap guest ram.")?; @@ -201,7 +202,7 @@ pub trait MachineOps { .register_listener(Arc::new(Mutex::new(KvmIoListener::default()))) .chain_err(|| "Failed to register KVM listener for I/O address space.")?; - if migrate_mode != MigrateMode::File { + if migrate_info.0 != MigrateMode::File { for mmap in mem_mappings.iter() { let base = mmap.start_address().raw_value(); let size = mmap.size(); @@ -350,9 +351,9 @@ pub trait MachineOps { fn get_vm_config(&self) -> &Mutex; - /// Get migration mode from VM config. There are four modes in total: + /// Get migration mode and path from VM config. There are four modes in total: /// Tcp, Unix, File and Unknown. - fn get_migrate_mode(&self) -> MigrateMode; + fn get_migrate_info(&self) -> Incoming; /// Add net device. /// @@ -1173,41 +1174,80 @@ pub trait MachineOps { Ok(()) } +} - fn start_incoming(&self) -> Result<()> { - let mut locked_vm_config = self.get_vm_config().lock().unwrap(); - if let Some((mode, path)) = locked_vm_config.incoming.as_ref() { - match mode { - MigrateMode::File => { - MigrationManager::restore_snapshot(path) - .chain_err(|| "Failed to restore snapshot")?; - } - MigrateMode::Unix => { - let listener = UnixListener::bind(path)?; - let (mut sock, _) = listener.accept()?; - remove_file(&path)?; +/// Normal run or resume virtual machine from migration/snapshot . +/// +/// # Arguments +/// +/// * `vm` - virtual machine that implement `MachineOps`. +/// * `cmd_args` - Command arguments from user. +pub fn vm_run( + vm: &Arc>, + cmd_args: &arg_parser::ArgMatches, +) -> Result<()> { + let migrate = vm.lock().unwrap().get_migrate_info(); + if migrate.0 == MigrateMode::Unknown { + vm.lock() + .unwrap() + .run(cmd_args.is_present("freeze_cpu")) + .chain_err(|| "Failed to start VM.")?; + } else { + start_incoming_migration(vm).chain_err(|| "Failed to start migration.")?; + } - MigrationManager::recv_migration(&mut sock) - .chain_err(|| "Failed to receive migration with unix mode")?; - } - MigrateMode::Tcp => { - let listener = TcpListener::bind(&path)?; - let mut sock = listener.accept().map(|(stream, _)| stream)?; + Ok(()) +} - MigrationManager::recv_migration(&mut sock) - .chain_err(|| "Failed to receive migration with tcp mode")?; - } - MigrateMode::Unknown => { - bail!("Unknown migration mode"); - } - } +/// Start incoming migration from destination. +fn start_incoming_migration(vm: &Arc>) -> Result<()> { + let (mode, path) = vm.lock().unwrap().get_migrate_info(); + match mode { + MigrateMode::File => { + MigrationManager::restore_snapshot(&path).chain_err(|| "Failed to restore snapshot")?; + vm.lock() + .unwrap() + .run(false) + .chain_err(|| "Failed to start VM.")?; + } + MigrateMode::Unix => { + let listener = UnixListener::bind(&path)?; + let (mut sock, _) = listener.accept()?; + remove_file(&path)?; + + MigrationManager::recv_migration(&mut sock) + .chain_err(|| "Failed to receive migration with unix mode")?; + vm.lock() + .unwrap() + .run(false) + .chain_err(|| "Failed to start VM.")?; + MigrationManager::finish_migration(&mut sock) + .chain_err(|| "Failed to finish migraton.")?; } + MigrateMode::Tcp => { + let listener = TcpListener::bind(&path)?; + let mut sock = listener.accept().map(|(stream, _)| stream)?; - // End the migration and reset the mode. - if let Some((mode, _)) = locked_vm_config.incoming.as_mut() { - *mode = MigrateMode::Unknown; + MigrationManager::recv_migration(&mut sock) + .chain_err(|| "Failed to receive migration with tcp mode")?; + vm.lock() + .unwrap() + .run(false) + .chain_err(|| "Failed to start VM.")?; + MigrationManager::finish_migration(&mut sock) + .chain_err(|| "Failed to finish migraton.")?; } + MigrateMode::Unknown => { + bail!("Unknown migration mode"); + } + } - Ok(()) + // End the migration and reset the mode. + let locked_vm = vm.lock().unwrap(); + let vm_config = locked_vm.get_vm_config(); + if let Some((mode, _)) = vm_config.lock().unwrap().incoming.as_mut() { + *mode = MigrateMode::Unknown; } + + Ok(()) } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d2f4cfafa..b4b207e83 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -84,7 +84,7 @@ use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, MigrateMode, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, Incoming, MigrateMode, }; use machine_manager::event; use machine_manager::machine::{ @@ -637,11 +637,12 @@ impl MachineOps for LightMachine { &self.vm_config } - fn get_migrate_mode(&self) -> MigrateMode { - if let Some(incoming) = self.get_vm_config().lock().unwrap().incoming.as_ref() { - return incoming.0; + fn get_migrate_info(&self) -> Incoming { + if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return (*mode, path.to_string()); } - MigrateMode::Unknown + + (MigrateMode::Unknown, String::new()) } fn get_sys_bus(&mut self) -> &SysBus { @@ -776,8 +777,8 @@ impl MachineOps for LightMachine { .chain_err(|| "Failed to create replaceable devices.")?; locked_vm.add_devices(vm_config)?; - let incoming = locked_vm.get_migrate_mode(); - let boot_config = if incoming == MigrateMode::Unknown { + let migrate_info = locked_vm.get_migrate_info(); + let boot_config = if migrate_info.0 == MigrateMode::Unknown { Some(locked_vm.load_boot_source(None)?) } else { None @@ -1281,6 +1282,14 @@ impl MigrateInterface for LightMachine { fn migrate(&self, uri: String) -> Response { match parse_incoming_uri(&uri) { Ok((MigrateMode::File, path)) => migration::snapshot(path), + Ok((MigrateMode::Unix, _)) | Ok((MigrateMode::Tcp, _)) => { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "MicroVM does not support migration".to_string(), + ), + None, + ) + } _ => { return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 57339bb48..d8df7fbdc 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -41,8 +41,8 @@ use devices::legacy::{ use devices::{ICGICConfig, ICGICv3Config, InterruptController}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, MigrateMode, NumaNode, NumaNodes, PFlashConfig, - SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, Incoming, MigrateMode, NumaNode, NumaNodes, + PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -457,8 +457,8 @@ impl MachineOps for StdMachine { .chain_err(|| "Failed to add devices")?; let fwcfg = locked_vm.add_fwcfg_device()?; - let incoming = locked_vm.get_migrate_mode(); - let boot_config = if incoming == MigrateMode::Unknown { + let migrate = locked_vm.get_migrate_info(); + let boot_config = if migrate.0 == MigrateMode::Unknown { Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { None @@ -493,7 +493,7 @@ impl MachineOps for StdMachine { .chain_err(|| ErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } - if incoming == MigrateMode::Unknown { + if migrate.0 == MigrateMode::Unknown { locked_vm .build_acpi_tables(&fwcfg) .chain_err(|| "Failed to create ACPI tables")?; @@ -554,12 +554,12 @@ impl MachineOps for StdMachine { &self.vm_config } - fn get_migrate_mode(&self) -> MigrateMode { - if let Some(incoming) = self.get_vm_config().lock().unwrap().incoming.as_ref() { - return incoming.0; + fn get_migrate_info(&self) -> Incoming { + if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return (*mode, path.to_string()); } - MigrateMode::Unknown + (MigrateMode::Unknown, String::new()) } fn get_pci_host(&mut self) -> StdResult<&Arc>> { @@ -967,12 +967,10 @@ impl MigrateInterface for StdMachine { Ok((MigrateMode::File, path)) => migration::snapshot(path), Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), - _ => { - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), - None, - ); - } + _ => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), + None, + ), } } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index bd22205fb..77c51ede8 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -39,8 +39,8 @@ use devices::legacy::{ use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, MigrateMode, NumaNode, NumaNodes, PFlashConfig, - SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, Incoming, MigrateMode, NumaNode, NumaNodes, + PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -471,8 +471,8 @@ impl MachineOps for StdMachine { locked_vm.add_devices(vm_config)?; let fwcfg = locked_vm.add_fwcfg_device()?; - let incoming = locked_vm.get_migrate_mode(); - let boot_config = if incoming == MigrateMode::Unknown { + let migrate = locked_vm.get_migrate_info(); + let boot_config = if migrate.0 == MigrateMode::Unknown { Some(locked_vm.load_boot_source(Some(&fwcfg))?) } else { None @@ -490,7 +490,7 @@ impl MachineOps for StdMachine { &boot_config, )?); - if incoming == MigrateMode::Unknown { + if migrate.0 == MigrateMode::Unknown { locked_vm .build_acpi_tables(&fwcfg) .chain_err(|| "Failed to create ACPI tables")?; @@ -594,12 +594,12 @@ impl MachineOps for StdMachine { &self.vm_config } - fn get_migrate_mode(&self) -> MigrateMode { - if let Some(incoming) = self.get_vm_config().lock().unwrap().incoming.as_ref() { - return incoming.0; + fn get_migrate_info(&self) -> Incoming { + if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return (*mode, path.to_string()); } - MigrateMode::Unknown + (MigrateMode::Unknown, String::new()) } fn get_pci_host(&mut self) -> Result<&Arc>> { @@ -918,12 +918,10 @@ impl MigrateInterface for StdMachine { Ok((MigrateMode::File, path)) => migration::snapshot(path), Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), - _ => { - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), - None, - ); - } + _ => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), + None, + ), } } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 7e61a40b8..b69438c85 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -14,7 +14,7 @@ use std::os::unix::net::UnixListener; use error_chain::bail; use util::arg_parser::{Arg, ArgMatches, ArgParser}; -use util::unix::{limit_permission, parse_uri}; +use util::unix::{limit_permission, parse_unix_uri}; use crate::{ config::{add_trace_events, ChardevType, CmdParser, MachineType, VmConfig}, @@ -412,6 +412,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("kernel")), vm_cfg, add_kernel); add_args_to_config!((args.value_of("initrd-file")), vm_cfg, add_initrd); add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); + add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); add_args_to_config!( (args.is_present("mem-prealloc")), vm_cfg, @@ -462,8 +463,7 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< cmd_parser.parse(&qmp_config)?; if let Some(uri) = cmd_parser.get_value::("")? { - let (_api_type, api_path) = - parse_uri(&uri).chain_err(|| "Failed to parse qmp socket path")?; + let api_path = parse_unix_uri(&uri).chain_err(|| "Failed to parse qmp socket path")?; sock_paths.push(api_path); } else { bail!("No uri found for qmp"); diff --git a/machine_manager/src/config/incoming.rs b/machine_manager/src/config/incoming.rs new file mode 100644 index 000000000..eb1d85ad1 --- /dev/null +++ b/machine_manager/src/config/incoming.rs @@ -0,0 +1,133 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use error_chain::bail; +use serde::{Deserialize, Serialize}; + +use super::{errors::Result, VmConfig}; + +#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)] +pub enum MigrateMode { + File, + Unix, + Tcp, + Unknown, +} + +impl From<&str> for MigrateMode { + fn from(s: &str) -> Self { + match s { + "file" | "File" | "FILE" => MigrateMode::File, + "unix" | "Unix" | "UNIX" => MigrateMode::Unix, + "tcp" | "Tcp" | "TCP" => MigrateMode::Tcp, + _ => MigrateMode::Unknown, + } + } +} + +/// Parse `-incoming` cmdline to migrate mode and path. +pub fn parse_incoming_uri(uri: &str) -> Result<(MigrateMode, String)> { + let parse_vec: Vec<&str> = uri.split(':').collect(); + if parse_vec.len() == 2 { + match MigrateMode::from(parse_vec[0]) { + MigrateMode::File => Ok((MigrateMode::File, String::from(parse_vec[1]))), + MigrateMode::Unix => Ok((MigrateMode::Unix, String::from(parse_vec[1]))), + _ => bail!("Invalid incoming uri {}", uri), + } + } else if parse_vec.len() == 3 { + match MigrateMode::from(parse_vec[0]) { + MigrateMode::Tcp => Ok(( + MigrateMode::Tcp, + format!("{}:{}", parse_vec[1], parse_vec[2]), + )), + _ => bail!("Invalid incoming uri {}", uri), + } + } else { + bail!("Invalid incoming uri {}", uri) + } +} + +pub type Incoming = (MigrateMode, String); + +impl VmConfig { + /// Add incoming mode and path. + pub fn add_incoming(&mut self, config: &str) -> Result<()> { + let (mode, uri) = parse_incoming_uri(config)?; + let incoming = match mode { + MigrateMode::File => (MigrateMode::File, uri), + MigrateMode::Unix => (MigrateMode::Unix, uri), + MigrateMode::Tcp => (MigrateMode::Tcp, uri), + MigrateMode::Unknown => { + bail!("Unsupported incoming unix path type") + } + }; + + self.incoming = Some(incoming); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_migrate_mode() { + assert_eq!(MigrateMode::from("File"), MigrateMode::File); + assert_eq!(MigrateMode::from("UNIX"), MigrateMode::Unix); + assert_eq!(MigrateMode::from("tcp"), MigrateMode::Tcp); + assert_eq!(MigrateMode::from("fd"), MigrateMode::Unknown); + } + + #[test] + fn test_parse_incoming_uri() { + let incoming_case1 = "unix:/tmp/stratovirt.sock"; + let result = parse_incoming_uri(incoming_case1); + assert!(result.is_ok()); + let result_1 = result.unwrap(); + assert_eq!(result_1.0, MigrateMode::Unix); + assert_eq!(result_1.1, "/tmp/stratovirt.sock".to_string()); + + let incoming_case2 = "tcp:192.168.1.2:2022"; + let result = parse_incoming_uri(incoming_case2); + assert!(result.is_ok()); + let result_2 = result.unwrap(); + assert_eq!(result_2.0, MigrateMode::Tcp); + assert_eq!(result_2.1, "192.168.1.2:2022".to_string()); + + let incoming_case3 = "tcp:192.168.1.2:2:2"; + let result_3 = parse_incoming_uri(incoming_case3); + assert!(result_3.is_err()); + } + + #[test] + fn test_add_incoming() { + let mut vm_config_case1 = VmConfig::default(); + assert!(vm_config_case1.add_incoming("tcp:192.168.1.2:2022").is_ok()); + assert_eq!( + vm_config_case1.incoming.unwrap(), + (MigrateMode::Tcp, "192.168.1.2:2022".to_string()) + ); + + let mut vm_config_case2 = VmConfig::default(); + assert!(vm_config_case2 + .add_incoming("unix:/tmp/stratovirt.sock") + .is_ok()); + assert_eq!( + vm_config_case2.incoming.unwrap(), + (MigrateMode::Unix, "/tmp/stratovirt.sock".to_string()) + ); + + let mut vm_config_case2 = VmConfig::default(); + assert!(vm_config_case2.add_incoming("unkonw:/tmp/").is_err()); + } +} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index c0344cf31..2df85eb98 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -84,6 +84,7 @@ pub use boot_source::*; pub use chardev::*; pub use devices::*; pub use drive::*; +pub use incoming::*; pub use iothread::*; pub use machine_config::*; pub use network::*; @@ -97,6 +98,7 @@ mod boot_source; mod chardev; mod devices; mod drive; +mod incoming; mod iothread; mod machine_config; mod network; @@ -168,6 +170,7 @@ pub struct VmConfig { pub dev_name: HashMap, pub global_config: HashMap, pub numa_nodes: Vec<(String, String)>, + pub incoming: Option, } impl VmConfig { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 65fb547f6..d9e8e12ca 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -419,6 +419,10 @@ pub trait MigrateInterface { fn query_migrate(&self) -> Response { Response::create_empty_response() } + + fn cancel_migrate(&self) -> Response { + Response::create_empty_response() + } } /// Machine interface which is exposed to inner hypervisor. diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 4694e8d6a..e1d5d575f 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -446,6 +446,7 @@ fn qmp_command_exec( (query_gic_capabilities, query_gic_capabilities), (query_iothreads, query_iothreads), (query_migrate, query_migrate), + (cancel_migrate, cancel_migrate), (query_cpus, query_cpus), (query_balloon, query_balloon), (list_type, list_type), diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 19b1a3513..055276105 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -167,6 +167,13 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "migrate_cancel")] + cancel_migrate { + #[serde(default)] + arguments: cancel_migrate, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, #[serde(rename = "query-version")] query_version { #[serde(default)] @@ -964,6 +971,20 @@ impl Command for query_migrate { } } +/// cancel-migrate: +/// +/// Cancel migrate the current VM. +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct cancel_migrate {} + +impl Command for cancel_migrate { + type Res = MigrationInfo; + + fn back(self) -> MigrationInfo { + Default::default() + } +} + #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct MigrationInfo { #[serde(rename = "status", default, skip_serializing_if = "Option::is_none")] diff --git a/migration/src/lib.rs b/migration/src/lib.rs index e0b75325c..ded93f47d 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -147,7 +147,7 @@ pub fn migration_unix_mode(path: String) -> Response { .spawn(move || { if let Err(e) = MigrationManager::send_migration(&mut socket) { error!("Failed to send migration: {}", e.display_chain()); - let _ = MigrationManager::recover(); + let _ = MigrationManager::recover_from_migration(); let _ = MigrationManager::set_status(MigrationStatus::Failed) .map_err(|e| error!("{}", e)); } @@ -193,7 +193,7 @@ pub fn migration_tcp_mode(path: String) -> Response { .spawn(move || { if let Err(e) = MigrationManager::send_migration(&mut socket) { error!("Failed to send migration: {}", e.display_chain()); - let _ = MigrationManager::recover(); + let _ = MigrationManager::recover_from_migration(); let _ = MigrationManager::set_status(MigrationStatus::Failed) .map_err(|e| error!("{}", e)); } diff --git a/src/main.rs b/src/main.rs index d312ce165..8634878be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,6 @@ use machine_manager::{ temp_cleaner::TempCleaner, }; use util::loop_context::EventNotifierHelper; -use util::unix::{parse_uri, UnixPath}; use util::{arg_parser, daemonize::daemonize, logger, set_termi_canon_mode}; error_chain! { @@ -139,8 +138,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let vm = Arc::new(Mutex::new( LightMachine::new(vm_config).chain_err(|| "Failed to init MicroVM")?, )); - MachineOps::realize(&vm, vm_config, cmd_args.is_present("incoming")) - .chain_err(|| "Failed to realize micro VM.")?; + MachineOps::realize(&vm, vm_config).chain_err(|| "Failed to realize micro VM.")?; EventLoop::set_manager(vm.clone(), None); for listener in listeners { @@ -152,8 +150,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let vm = Arc::new(Mutex::new( StdMachine::new(vm_config).chain_err(|| "Failed to init StandardVM")?, )); - MachineOps::realize(&vm, vm_config, cmd_args.is_present("incoming")) - .chain_err(|| "Failed to realize standard VM.")?; + MachineOps::realize(&vm, vm_config).chain_err(|| "Failed to realize standard VM.")?; EventLoop::set_manager(vm.clone(), None); for listener in listeners { @@ -173,15 +170,6 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } }; - if let Some(uri) = cmd_args.value_of("incoming") { - if let (UnixPath::File, path) = parse_uri(&uri)? { - migration::MigrationManager::restore_snapshot(&path) - .chain_err(|| "Failed to start with incoming migration.")?; - } else { - bail!("Unsupported incoming unix path type.") - } - } - for socket in sockets { EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(socket))), @@ -190,10 +178,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res .chain_err(|| "Failed to add api event to MainLoop")?; } - vm.lock() - .unwrap() - .run(cmd_args.is_present("freeze_cpu")) - .chain_err(|| "Failed to start VM.")?; + machine::vm_run(&vm, cmd_args).chain_err(|| "Failed to start VM.")?; let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); if !cmd_args.is_present("disable-seccomp") { diff --git a/util/src/unix.rs b/util/src/unix.rs index 7d2815296..a7ce4ceee 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -48,39 +48,15 @@ pub fn host_page_size() -> u64 { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as u64 } } -#[derive(PartialEq, Debug)] -/// Three path type in unix. -pub enum UnixPath { - File = 0, - Unix = 1, - Tcp = 2, - Unknown = 3, -} - -impl From<&str> for UnixPath { - fn from(s: &str) -> Self { - match s { - "file" | "File" | "FILE" => UnixPath::File, - "unix" | "Unix" | "UNIX" => UnixPath::Unix, - "tcp" | "Tcp" | "TCP" => UnixPath::Tcp, - _ => UnixPath::Unknown, - } - } -} - /// Parse unix uri to unix path. /// /// # Notions /// -/// Unix uri is the string as `file:/xxx/xxx` or `unix:/xxx/xxx` or `tcp:xxx.xxx.xxx`. -pub fn parse_uri(uri: &str) -> Result<(UnixPath, String)> { +/// Unix uri is the string as `unix:/xxx/xxx`. +pub fn parse_unix_uri(uri: &str) -> Result { let parse_vec: Vec<&str> = uri.split(':').collect(); - if parse_vec.len() == 2 { - match UnixPath::from(parse_vec[0]) { - UnixPath::File => Ok((UnixPath::File, String::from(parse_vec[1]))), - UnixPath::Unix => Ok((UnixPath::Unix, String::from(parse_vec[1]))), - _ => bail!("Unsupported unix path type."), - } + if parse_vec.len() == 2 && parse_vec[0] == "unix" { + Ok(parse_vec[1].to_string()) } else { bail!("Invalid unix uri: {}", uri) } @@ -454,22 +430,22 @@ mod tests { use libc::{c_void, iovec}; - use super::{parse_uri, UnixPath, UnixSock}; + use super::{parse_unix_uri, UnixSock}; #[test] fn test_parse_uri() { - let test_uri_01 = "file:/tmp/test_file"; - assert!(parse_uri(test_uri_01).is_ok()); + let test_uri_01 = "unix:/tmp/test_file.sock"; + assert!(parse_unix_uri(test_uri_01).is_ok()); assert_eq!( - parse_uri(test_uri_01).unwrap(), - (UnixPath::File, String::from("/tmp/test_file")) + parse_unix_uri(test_uri_01).unwrap(), + String::from("/tmp/test_file.sock") ); let test_uri_02 = "file:/tmp/test_file:file"; - assert!(parse_uri(test_uri_02).is_err()); + assert!(parse_unix_uri(test_uri_02).is_err()); let test_uri_03 = "tcp:127.0.0.1"; - assert!(parse_uri(test_uri_03).is_err()); + assert!(parse_unix_uri(test_uri_03).is_err()); } #[test] -- Gitee From 9baa714745926484bf96768813b7092caabfd20b Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 1 Aug 2022 10:39:09 +0800 Subject: [PATCH 0100/2187] migration: add system call whitelist for migration Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 4 ++-- hypervisor/src/kvm/mod.rs | 1 + machine/src/standard_vm/aarch64/syscall.rs | 16 +++++++++++++--- machine/src/standard_vm/x86_64/syscall.rs | 20 +++++++++++++++++--- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 63243624d..b664bf8c6 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -643,14 +643,14 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 47 | 46 | -| q35 | 53 | 54 | +| q35 | 60 | 61 | * aarch64 | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 45 | 45 | -| virt | 51 | 50 | +| virt | 59 | 57 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 1d20eed82..78833c231 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -92,6 +92,7 @@ ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); +ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index ef3a9bf48..2b1d28b8d 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 48 syscalls -/// * aarch64-unknown-musl: 47 syscalls +/// * aarch64-unknown-gnu: 56 syscalls +/// * aarch64-unknown-musl: 54 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -88,7 +88,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_openat), BpfRule::new(libc::SYS_sigaltstack), BpfRule::new(libc::SYS_mmap), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_mprotect), BpfRule::new(libc::SYS_munmap), BpfRule::new(libc::SYS_accept4), @@ -116,6 +115,16 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_socket), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), + BpfRule::new(libc::SYS_clone), + BpfRule::new(libc::SYS_prctl), + BpfRule::new(libc::SYS_sendto), + BpfRule::new(libc::SYS_getrandom), + BpfRule::new(libc::SYS_rt_sigaction), + BpfRule::new(libc::SYS_setsockopt), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_set_robust_list), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_sched_getaffinity), ] } @@ -174,6 +183,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEVICE_ATTR() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) } fn madvise_rule() -> BpfRule { diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 81fdb6dbf..237128d4f 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 50 syscalls -/// * x86_64-unknown-musl: 51 syscalls +/// * x86_64-unknown-gnu: 58 syscalls +/// * x86_64-unknown-musl: 57 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -91,7 +91,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_openat), BpfRule::new(libc::SYS_sigaltstack), BpfRule::new(libc::SYS_mmap), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_mprotect), BpfRule::new(libc::SYS_munmap), BpfRule::new(libc::SYS_accept4), @@ -122,6 +121,20 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_socket), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), + #[cfg(target_env = "musl")] + BpfRule::new(libc::SYS_clone), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_clone3), + BpfRule::new(libc::SYS_prctl), + BpfRule::new(libc::SYS_sendto), + BpfRule::new(libc::SYS_getrandom), + BpfRule::new(libc::SYS_setsockopt), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rt_sigaction), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_set_robust_list), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_sched_getaffinity), ] } @@ -198,6 +211,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) } fn madvise_rule() -> BpfRule { -- Gitee From 649210d97b32be8d1d88890f858973ee796b3445 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 25 Jul 2022 09:02:14 +0800 Subject: [PATCH 0101/2187] chardev: support add/remove chardev by qmp Support add/remove chardev by qmp. --- docs/qmp.md | 43 +++++++++- machine/src/micro_vm/mod.rs | 18 +++++ machine/src/standard_vm/mod.rs | 43 +++++++++- machine_manager/src/config/chardev.rs | 63 +++++++++++++++ machine_manager/src/config/network.rs | 1 + machine_manager/src/machine.rs | 12 ++- machine_manager/src/qmp/mod.rs | 4 +- machine_manager/src/qmp/qmp_schema.rs | 111 ++++++++++++++++++++++++++ 8 files changed, 286 insertions(+), 9 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index ceee6fd81..89a2f9a98 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -138,9 +138,50 @@ Remove a network backend. -> {"return": {}} ``` +## Character device backend management + +Currently, It only supports Standard VM. + +### chardev-add + +Add a character device backend. + +#### Arguments + +* `id` : the character device's ID, must be unique. +* `backend` : the chardev backend info. + +#### Notes + +*Standard VM* + +* `id` in `chardev-add` should be same as `id` in `netdev_add`. + +#### Example + +```json +<- {"execute":"chardev-add", "arguments": {"id": "chardev_id", "backend": {"type": "socket", "data": {"addr": {"type": "unix", "data": {"path": "/path/to/socket"}}, "server": false}}}} +-> {"return": {}} +``` + +### chardev-remove + +Remove a character device backend. + +#### Arguments + +* `id` : the character device's ID. + +#### Example + +```json +<- {"execute": "chardev-remove", "arguments": {"id": "chardev_id"}} +-> {"return": {}} +``` + ## Hot plug management -StratoVirt supports hot-plug virtio-blk and virtio-net devices with QMP. Standard VM supports hot-plug vfio devices. +StratoVirt supports hot-plug virtio-blk and virtio-net devices with QMP. Standard VM supports hot-plug vfio and vhost-user net devices. ### device_add diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index b4b207e83..314488f24 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1266,6 +1266,24 @@ impl DeviceInterface for LightMachine { ) } + fn chardev_add(&mut self, _args: qmp_schema::CharDevAddArgument) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "chardev_add not supported yet for microVM".to_string(), + ), + None, + ) + } + + fn chardev_remove(&mut self, _id: String) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "chardev_remove not supported yet for microVM".to_string(), + ), + None, + ) + } + fn getfd(&self, fd_name: String, if_fd: Option) -> Response { if let Some(fd) = if_fd { QmpChannel::set_fd(fd_name, fd); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 771b0a4ef..2e3185cf7 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -78,8 +78,8 @@ use devices::legacy::FwCfgOps; use error_chain::{bail, ChainedError}; use errors::{Result, ResultExt}; use machine_manager::config::{ - get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, NetworkInterfaceConfig, - NumaNode, NumaNodes, PciBdf, + get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, + NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -1154,8 +1154,8 @@ impl DeviceInterface for StdMachine { } } - fn netdev_add(&mut self, args: Box) -> Response { - let config = match get_netdev_config(args) { + fn chardev_add(&mut self, args: qmp_schema::CharDevAddArgument) -> Response { + let config = match get_chardev_config(args) { Ok(conf) => conf, Err(e) => { return Response::create_error_response( @@ -1172,6 +1172,41 @@ impl DeviceInterface for StdMachine { ); } + match self + .get_vm_config() + .lock() + .unwrap() + .add_chardev_with_config(config) + { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + } + } + + fn chardev_remove(&mut self, id: String) -> Response { + match self.get_vm_config().lock().unwrap().del_chardev_by_id(&id) { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + } + } + + fn netdev_add(&mut self, args: Box) -> Response { + let config = match get_netdev_config(args) { + Ok(conf) => conf, + Err(e) => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + }; + match self .get_vm_config() .lock() diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index a15ce4270..34be0fd47 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use error_chain::bail; +use log::error; use serde::{Deserialize, Serialize}; use super::{ @@ -18,6 +19,7 @@ use super::{ get_pci_bdf, pci_args_check, PciBdf, }; use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH}; +use crate::qmp::qmp_schema; const MAX_GUEST_CID: u64 = 4_294_967_295; const MIN_GUEST_CID: u64 = 3; @@ -171,6 +173,34 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { }) } +pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result { + let backend = args.backend; + if backend.backend_type.as_str() != "socket" { + return Err(ErrorKind::InvalidParam("backend".to_string(), backend.backend_type).into()); + } + + let data = backend.backend_data; + if data.server { + error!("Not support chardev socket as server now."); + return Err(ErrorKind::InvalidParam("backend".to_string(), "server".to_string()).into()); + } + + let addr = data.addr; + if addr.addr_type.as_str() != "unix" { + error!("Just support \"unix\" addr type option now."); + return Err(ErrorKind::InvalidParam("backend".to_string(), "addr".to_string()).into()); + } + + Ok(ChardevConfig { + id: args.id, + backend: ChardevType::Socket { + path: addr.addr_data.path, + server: data.server, + nowait: false, + }, + }) +} + pub fn parse_virtconsole(vm_config: &mut VmConfig, config_args: &str) -> Result { let mut cmd_parser = CmdParser::new("virtconsole"); cmd_parser.push("").push("id").push("chardev"); @@ -220,6 +250,39 @@ impl VmConfig { } Ok(()) } + + /// Add chardev config to vm config. + /// + /// # Arguments + /// + /// * `conf` - The chardev config to be added to the vm. + pub fn add_chardev_with_config(&mut self, conf: ChardevConfig) -> Result<()> { + if let Err(e) = conf.check() { + bail!("Chardev config checking failed, {}", e.to_string()); + } + + let chardev_id = conf.id.clone(); + if self.chardev.get(&chardev_id).is_none() { + self.chardev.insert(chardev_id, conf); + } else { + bail!("Chardev {:?} has been added", chardev_id); + } + Ok(()) + } + + /// Delete chardev config from vm config. + /// + /// # Arguments + /// + /// * `id` - The chardev id which is used to delete chardev config. + pub fn del_chardev_by_id(&mut self, id: &str) -> Result<()> { + if self.chardev.get(id).is_some() { + self.chardev.remove(id); + } else { + bail!("Chardev {} not found", id); + } + Ok(()) + } } /// Config structure for serial. diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 7b6d142b4..182a50852 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -717,6 +717,7 @@ mod tests { downscript: None, script: None, queues: None, + chardev: None, }) } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index d9e8e12ca..77fd398d2 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -17,9 +17,9 @@ use once_cell::sync::Lazy; use strum::VariantNames; use crate::qmp::qmp_schema::{ - BlockDevAddArgument, ChardevInfo, Cmd, CmdLine, DeviceAddArgument, DeviceProps, Events, GicCap, - IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, NetDevAddArgument, PropList, - QmpCommand, QmpEvent, Target, TypeLists, + BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, DeviceAddArgument, + DeviceProps, Events, GicCap, IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, + NetDevAddArgument, PropList, QmpCommand, QmpEvent, Target, TypeLists, }; use crate::qmp::{Response, Version}; @@ -166,6 +166,12 @@ pub trait DeviceInterface { fn netdev_del(&mut self, id: String) -> Response; + /// Create a new chardev device. + fn chardev_add(&mut self, _args: CharDevAddArgument) -> Response; + + /// Remove a chardev device. + fn chardev_remove(&mut self, _id: String) -> Response; + /// Receive a file descriptor via SCM rights and assign it a name. fn getfd(&self, fd_name: String, if_fd: Option) -> Response; diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index e1d5d575f..195166f39 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -455,11 +455,13 @@ fn qmp_command_exec( (device_del, device_del, id), (blockdev_del, blockdev_del, node_name), (netdev_del, netdev_del, id), + (chardev_remove, chardev_remove, id), (balloon, balloon, value), (migrate, migrate, uri); (device_add, device_add), (blockdev_add, blockdev_add), - (netdev_add, netdev_add) + (netdev_add, netdev_add), + (chardev_add, chardev_add) ); // Handle the Qmp command which macro can't cover diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 055276105..c712c5e40 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -90,6 +90,18 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "chardev-add")] + chardev_add { + arguments: chardev_add, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, + #[serde(rename = "chardev-remove")] + chardev_remove { + arguments: chardev_remove, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, netdev_add { arguments: Box, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -602,6 +614,7 @@ pub struct netdev_add { pub downscript: Option, pub script: Option, pub queues: Option, + pub chardev: Option, } pub type NetDevAddArgument = netdev_add; @@ -614,6 +627,104 @@ impl Command for netdev_add { } } +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct AddrDataOptions { + pub path: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct AddrOptions { + #[serde(rename = "type")] + pub addr_type: String, + #[serde(rename = "data")] + pub addr_data: AddrDataOptions, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct BackendDataOptions { + pub addr: AddrOptions, + pub server: bool, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct BackendOptions { + #[serde(rename = "type")] + pub backend_type: String, + #[serde(rename = "data")] + pub backend_data: BackendDataOptions, +} + +/// chardev-add +/// +/// # Arguments +/// +/// * `id` - the character device's ID, must be unique. +/// * `backend` - the chardev backend info. +/// +/// Additional arguments depend on the type. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "chardev-add", +/// "arguments": { "id": "chardev_id", "backend": { "type": "socket", "data": { +/// "addr": { "type": "unix", "data": { "path": "/path/to/socket" } }, +/// "server": false }}}} +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct chardev_add { + pub id: String, + pub backend: BackendOptions, +} + +pub type CharDevAddArgument = chardev_add; + +impl Command for chardev_add { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + +/// chardev-remove +/// +/// Remove a chardev backend. +/// +/// # Arguments +/// +/// * `id` - The ID of the character device. +/// +/// # Errors +/// +/// If `id` is not a valid chardev backend, DeviceNotFound +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "chardev-remove", "arguments": { "id": "chardev_id" } } +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct chardev_remove { + pub id: String, +} + +impl Command for chardev_remove { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + /// device_del /// /// Remove a device from a guest -- Gitee From 3325665605124f0273b951e91b01ee9439deb607 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 6 Aug 2022 14:57:34 +0800 Subject: [PATCH 0102/2187] vhost-user: support hotplug vhost-user net Support hotplug vhost-user net. --- machine/src/standard_vm/mod.rs | 63 +++++++++++++++++++++++---- machine_manager/src/config/chardev.rs | 5 +++ machine_manager/src/config/network.rs | 24 ++++++++-- virtio/src/vhost/user/net.rs | 55 +++++++++++++++-------- 4 files changed, 119 insertions(+), 28 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 2e3185cf7..4eb8f1f45 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -78,8 +78,8 @@ use devices::legacy::FwCfgOps; use error_chain::{bail, ChainedError}; use errors::{Result, ResultExt}; use machine_manager::config::{ - get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ConfigCheck, DriveConfig, - NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, + get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, + DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, VmConfig, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -87,7 +87,10 @@ use migration::MigrationManager; use pci::hotplug::{handle_plug, handle_unplug_request}; use pci::PciBus; use util::byte_code::ByteCode; -use virtio::{qmp_balloon, qmp_query_balloon, Block, BlockState, VhostKern, VirtioNetState}; +use virtio::{ + qmp_balloon, qmp_query_balloon, Block, BlockState, VhostKern, VhostUser, VirtioDevice, + VirtioNetState, +}; #[cfg(target_arch = "aarch64")] use aarch64::{LayoutEntryType, MEM_LAYOUT}; @@ -817,6 +820,35 @@ impl StdMachine { Ok(()) } + fn get_socket_path(&self, vm_config: &VmConfig, chardev: String) -> Result> { + let char_dev = if let Some(char_dev) = vm_config.chardev.get(&chardev) { + char_dev + } else { + bail!("Chardev: {:?} not found for character device", &chardev); + }; + + let socket_path = match &char_dev.backend { + ChardevType::Socket { + path, + server, + nowait, + } => { + if *server || *nowait { + bail!( + "Argument \'server\' or \'nowait\' is not needed for chardev \'{}\'", + path + ); + } + Some(path.clone()) + } + _ => { + bail!("Chardev {:?} backend should be socket type.", &chardev); + } + }; + + Ok(socket_path) + } + fn plug_virtio_pci_net( &mut self, pci_bdf: &PciBdf, @@ -829,7 +861,14 @@ impl StdMachine { bail!("Netdev not set"); }; - let dev = if let Some(conf) = self.get_vm_config().lock().unwrap().netdevs.get(netdev) { + let vm_config = self.get_vm_config().lock().unwrap(); + let dev = if let Some(conf) = vm_config.netdevs.get(netdev) { + let mut socket_path: Option = None; + if let Some(chardev) = &conf.chardev { + socket_path = self + .get_socket_path(&vm_config, (&chardev).to_string()) + .chain_err(|| "Failed to get socket path")?; + } let dev = NetworkInterfaceConfig { id: args.id.clone(), host_dev_name: conf.ifname.clone(), @@ -840,18 +879,26 @@ impl StdMachine { iothread: args.iothread.clone(), queues: conf.queues, mq: conf.queues > 2, - socket_path: None, + socket_path, }; dev.check()?; dev } else { bail!("Netdev not found"); }; + drop(vm_config); if dev.vhost_type.is_some() { - let net = Arc::new(Mutex::new(VhostKern::Net::new(&dev, self.get_sys_mem()))); - self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, false) - .chain_err(|| "Failed to add virtio net device")?; + let mut need_irqfd = false; + let net: Arc> = + if dev.vhost_type == Some(String::from("vhost-kernel")) { + Arc::new(Mutex::new(VhostKern::Net::new(&dev, self.get_sys_mem()))) + } else { + need_irqfd = true; + Arc::new(Mutex::new(VhostUser::Net::new(&dev, self.get_sys_mem()))) + }; + self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, need_irqfd) + .chain_err(|| "Failed to add vhost-kernel/vhost-user net device")?; } else { let net_id = dev.id.clone(); let net = Arc::new(Mutex::new(virtio::Net::new(dev))); diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 34be0fd47..6af87f333 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -173,6 +173,11 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { }) } +/// Get chardev config from qmp arguments. +/// +/// # Arguments +/// +/// * `args` - The qmp arguments. pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result { let backend = args.backend; if backend.backend_type.as_str() != "socket" { diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 182a50852..fe85b4a39 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -63,7 +63,7 @@ impl ConfigCheck for NetDevcfg { } if let Some(vhost_type) = self.vhost_type.as_ref() { - if vhost_type != "vhost-kernel" { + if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" { return Err(ErrorKind::UnknownVhostType.into()); } } @@ -332,6 +332,11 @@ pub fn get_netdev_config(args: Box) -> Result = fds.split(':').collect(); @@ -355,18 +360,31 @@ pub fn get_netdev_config(args: Box) -> Result() { Ok(vhost) => { if vhost.into() { - config.vhost_type = Some(String::from("vhost-kernel")); + if netdev_type.ne("vhost-user") { + config.vhost_type = Some(String::from("vhost-kernel")); + } else { + bail!("vhost-user netdev does not support \"vhost\" option"); + } } } Err(_) => { bail!("Failed to get vhost type: {}", vhost); } }; + } else if netdev_type.eq("vhost-user") { + config.vhost_type = Some(netdev_type.clone()); } + if let Some(vhostfd) = args.vhostfds { match vhostfd.parse::() { Ok(fd) => config.vhost_fds = Some(vec![fd]), @@ -378,7 +396,7 @@ pub fn get_netdev_config(args: Box) -> Result Result<()> { + match &self.client { + Some(client) => { + client + .delete_event() + .chain_err(|| "Failed to delete vhost-user net event")?; + } + None => return Err("Failed to get client when stoping event".into()), + }; + if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq { + self.de_ctrl_evt + .write(1) + .chain_err(|| ErrorKind::EventFdWrite)?; + } + + Ok(()) + } + + fn clean_up(&mut self) -> Result<()> { + self.stop_event()?; + self.device_features = 0_u64; + self.driver_features = 0_u64; + self.device_config = VirtioNetConfig::default(); + self.client = None; + + Ok(()) + } } impl VirtioDevice for Net { @@ -221,8 +249,8 @@ impl VirtioDevice for Net { } let client = match &self.client { - None => return Err("Failed to get client for vhost-user net".into()), Some(client_) => client_, + None => return Err("Failed to get client for vhost-user net".into()), }; client @@ -279,7 +307,6 @@ impl VirtioDevice for Net { queue_index, ) })?; - client .set_vring_call(queue_index, &self.call_events[queue_index]) .chain_err(|| { @@ -314,26 +341,20 @@ impl VirtioDevice for Net { fn deactivate(&mut self) -> Result<()> { self.call_events.clear(); - Ok(()) + self.clean_up()?; + self.realize() } fn reset(&mut self) -> Result<()> { - self.device_features = 0_u64; - self.driver_features = 0_u64; - self.device_config = VirtioNetConfig::default(); + self.clean_up()?; + self.realize() + } - let client = match &self.client { - None => return Err("Failed to get client when reseting vhost-user net".into()), - Some(client_) => client_, - }; - client - .delete_event() - .chain_err(|| "Failed to delete vhost-user net event")?; - self.de_ctrl_evt - .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + fn unrealize(&mut self) -> Result<()> { + self.stop_event()?; + self.call_events.clear(); self.client = None; - self.realize() + Ok(()) } } -- Gitee From 08b49d12bef9c56a6a20165dcd2b306a00075b5d Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 12 Aug 2022 07:12:44 +0800 Subject: [PATCH 0103/2187] migrate to Rust 2021 edition and Update the crates to the latest version Signed-off-by: Yan Wen --- Cargo.lock | 157 +++++++++++++------------ Cargo.toml | 10 +- acpi/Cargo.toml | 6 +- address_space/Cargo.toml | 14 +-- address_space/src/address_space.rs | 4 +- boot_loader/Cargo.toml | 12 +- cpu/Cargo.toml | 12 +- cpu/src/aarch64/core_regs.rs | 119 +++++++++++-------- cpu/src/aarch64/mod.rs | 9 +- cpu/src/x86_64/caps.rs | 2 +- cpu/src/x86_64/mod.rs | 2 +- devices/Cargo.toml | 16 +-- hypervisor/Cargo.toml | 14 +-- hypervisor/src/kvm/interrupt.rs | 2 +- hypervisor/src/kvm/mod.rs | 9 +- machine/Cargo.toml | 18 +-- machine/src/micro_vm/mod.rs | 12 +- machine/src/standard_vm/aarch64/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 4 +- machine_manager/Cargo.toml | 16 +-- machine_manager/src/qmp/mod.rs | 5 +- migration/Cargo.toml | 14 +-- migration_derive/Cargo.toml | 4 +- migration_derive/src/field_parser.rs | 10 +- ozone/Cargo.toml | 4 +- pci/Cargo.toml | 16 +-- pci/src/msix.rs | 2 +- sysbus/Cargo.toml | 6 +- util/Cargo.toml | 18 +-- util/src/byte_code.rs | 2 + util/src/loop_context.rs | 5 +- util/src/tap.rs | 2 +- util/src/unix.rs | 4 +- vfio/Cargo.toml | 18 +-- vfio/src/vfio_dev.rs | 2 +- virtio/Cargo.toml | 14 +-- virtio/src/vhost/kernel/mod.rs | 4 +- 37 files changed, 296 insertions(+), 274 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b27275b9..bd2ec69d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "StratoVirt" version = "2.2.0" @@ -32,9 +34,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.15.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03345e98af8f3d786b6d9f656ccfa6ac316d954e92bc4841f0bba20789d5fb5a" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ "gimli", ] @@ -66,21 +68,21 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "arc-swap" -version = "0.4.8" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.59" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", @@ -93,9 +95,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "boot_loader" @@ -120,9 +122,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.67" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -183,9 +185,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.24.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "heck" @@ -212,33 +214,33 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "itoa" -version = "0.4.7" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "kvm-bindings" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9742810f4cb95388955853c032cd50415d18834357f67b7b299fc28217cd3d" +checksum = "a78c049190826fff959994b7c1d8a2930d0a348f1b8f3aa4f9bb34cd5d7f2952" dependencies = [ "vmm-sys-util", ] [[package]] name = "kvm-ioctls" -version = "0.6.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8002ac5e13e1f61da5b8c440fe4de5bae029b4538cd899ef2dd93e1245bf71" +checksum = "97422ba48d7ffb66fd4d18130f72ab66f9bbbf791fb7a87b9291cdcfec437593" dependencies = [ "kvm-bindings", "libc", @@ -253,24 +255,25 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.94" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -320,6 +323,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "migration" version = "2.2.0" @@ -350,25 +359,27 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", - "autocfg", ] [[package]] name = "object" -version = "0.24.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] [[package]] name = "once_cell" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "ozone" @@ -381,9 +392,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", @@ -392,9 +403,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ "cfg-if", "instant", @@ -428,42 +439,42 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.5" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "rustc-demangle" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410f7acf3cb3a44527c5d9546bad4bf4e6c460915d5f9f2fc524498bfe8f70ce" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "scopeguard" @@ -473,18 +484,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" dependencies = [ "proc-macro2", "quote", @@ -493,9 +504,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" dependencies = [ "itoa", "ryu", @@ -526,9 +537,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.6.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "strum" @@ -550,13 +561,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.72" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -572,16 +583,16 @@ dependencies = [ ] [[package]] -name = "unicode-segmentation" -version = "1.8.0" +name = "unicode-ident" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-segmentation" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "util" @@ -600,9 +611,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vfio" @@ -625,9 +636,9 @@ dependencies = [ [[package]] name = "vfio-bindings" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a21f546f2bda37f5a8cfb138c87f95b8e34d2d78d6a7a92ba3785f4e08604a7" +checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce" [[package]] name = "virtio" @@ -654,9 +665,9 @@ dependencies = [ [[package]] name = "vmm-sys-util" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cdd1d72e262bbfb014de65ada24c1ac50e10a2e3b1e8ec052df188c2ee5dfa" +checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62" dependencies = [ "bitflags", "libc", diff --git a/Cargo.toml b/Cargo.toml index 2178458da..81905af52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,16 +2,16 @@ name = "StratoVirt" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" description = "a lightweight hypervisor with low memory overhead and fast booting speed" license = "Mulan PSL v2" [dependencies] error-chain = "0.12.4" -kvm-ioctls = "0.6.0" -libc = ">=0.2.71" -log = "0.4.8" -vmm-sys-util = ">=0.7.0" +kvm-ioctls = ">=0.11.0" +libc = "0.2" +log = "0.4" +vmm-sys-util = ">=0.10.0" hypervisor = { path = "hypervisor" } machine = { path = "machine" } machine_manager = { path = "machine_manager" } diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml index 201e8a85e..b4abaf0b2 100644 --- a/acpi/Cargo.toml +++ b/acpi/Cargo.toml @@ -2,14 +2,14 @@ name = "acpi" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "acpi module" [dependencies] error-chain = "0.12.4" -log = "0.4.8" -byteorder = "1.3.4" +log = "0.4" +byteorder = "1.4.3" address_space = { path = "../address_space" } util = {path = "../util"} diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index cf8975b00..9bcc48c97 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -2,18 +2,18 @@ name = "address_space" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "provide memory management for VM" [dependencies] error-chain = "0.12.4" -libc = ">=0.2.71" -log = "0.4.8" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -vmm-sys-util = ">=0.7.0" -arc-swap = "0.4.8" +libc = "0.2" +log = "0.4" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +vmm-sys-util = ">=0.10.0" +arc-swap = ">=1.5.0" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 81774da55..0d757092b 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -64,7 +64,7 @@ pub struct AddressSpace { root: Region, /// Flat_view is the output of rendering all regions in parent address-space, /// every time the topology changed (add/delete region), flat_view would be updated. - flat_view: ArcSwap, + flat_view: Arc>, /// The triggered call-backs when flat_view changed. listeners: Arc>>, /// The current layout of ioeventfds, which is compared with new ones in topology-update stage. @@ -80,7 +80,7 @@ impl AddressSpace { pub fn new(root: Region) -> Result> { let space = Arc::new(AddressSpace { root: root.clone(), - flat_view: ArcSwap::new(Arc::new(FlatView::default())), + flat_view: Arc::new(ArcSwap::new(Arc::new(FlatView::default()))), listeners: Arc::new(Mutex::new(Vec::new())), ioeventfds: Arc::new(Mutex::new(Vec::new())), }); diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index d72b54778..e8de6690d 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -2,16 +2,16 @@ name = "boot_loader" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" [dependencies] error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -libc = ">=0.2.71" -log = "0.4.8" -vmm-sys-util = ">=0.7.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +libc = "0.2" +log = "0.4" +vmm-sys-util = ">=0.10.0" address_space = { path = "../address_space" } devices = { path = "../devices" } util = { path = "../util" } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 663d88cb4..a031ddb90 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -2,17 +2,17 @@ name = "cpu" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "CPU emulation" [dependencies] error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -log = "0.4.8" -libc = ">=0.2.71" -vmm-sys-util = ">=0.7.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +log = "0.4" +libc = "0.2" +vmm-sys-util = ">=0.10.0" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index 4f0726a32..708f4acca 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -51,56 +51,84 @@ pub enum Arm64CoreRegs { impl From for u64 { fn from(elem: Arm64CoreRegs) -> Self { - let register_size; - let regid; - match elem { - Arm64CoreRegs::KvmSpEl1 => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, sp_el1) - } - Arm64CoreRegs::KvmElrEl1 => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, elr_el1) - } + let (register_size, reg_offset) = match elem { + Arm64CoreRegs::KvmSpEl1 => (KVM_REG_SIZE_U64, offset_of!(kvm_regs, sp_el1)), + Arm64CoreRegs::KvmElrEl1 => (KVM_REG_SIZE_U64, offset_of!(kvm_regs, elr_el1)), Arm64CoreRegs::KvmSpsr(idx) if idx < KVM_NR_SPSR as usize => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, spsr) + idx * 8 - } - Arm64CoreRegs::UserPTRegRegs(idx) if idx < 31 => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, regs, user_pt_regs, regs) + idx * 8 - } - Arm64CoreRegs::UserPTRegSp => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, regs, user_pt_regs, sp) - } - Arm64CoreRegs::UserPTRegPc => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, regs, user_pt_regs, pc) - } - Arm64CoreRegs::UserPTRegPState => { - register_size = KVM_REG_SIZE_U64; - regid = offset_of!(kvm_regs, regs, user_pt_regs, pstate) - } - Arm64CoreRegs::UserFPSIMDStateVregs(idx) if idx < 32 => { - register_size = KVM_REG_SIZE_U128; - regid = offset_of!(kvm_regs, fp_regs, user_fpsimd_state, vregs) + idx * 16 - } - Arm64CoreRegs::UserFPSIMDStateFpsr => { - register_size = KVM_REG_SIZE_U32; - regid = offset_of!(kvm_regs, fp_regs, user_fpsimd_state, fpsr) - } - Arm64CoreRegs::UserFPSIMDStateFpcr => { - register_size = KVM_REG_SIZE_U32; - regid = offset_of!(kvm_regs, fp_regs, user_fpsimd_state, fpcr) + (KVM_REG_SIZE_U64, offset_of!(kvm_regs, spsr) + idx * 8) } + Arm64CoreRegs::UserPTRegRegs(idx) if idx < 31 => ( + KVM_REG_SIZE_U64, + offset_of!(kvm_regs, regs, user_pt_regs, regs) + idx * 8, + ), + Arm64CoreRegs::UserPTRegSp => ( + KVM_REG_SIZE_U64, + offset_of!(kvm_regs, regs, user_pt_regs, sp), + ), + Arm64CoreRegs::UserPTRegPc => ( + KVM_REG_SIZE_U64, + offset_of!(kvm_regs, regs, user_pt_regs, pc), + ), + Arm64CoreRegs::UserPTRegPState => ( + KVM_REG_SIZE_U64, + offset_of!(kvm_regs, regs, user_pt_regs, pstate), + ), + Arm64CoreRegs::UserFPSIMDStateVregs(idx) if idx < 32 => ( + KVM_REG_SIZE_U128, + offset_of!(kvm_regs, fp_regs, user_fpsimd_state, vregs) + idx * 16, + ), + Arm64CoreRegs::UserFPSIMDStateFpsr => ( + KVM_REG_SIZE_U32, + offset_of!(kvm_regs, fp_regs, user_fpsimd_state, fpsr), + ), + Arm64CoreRegs::UserFPSIMDStateFpcr => ( + KVM_REG_SIZE_U32, + offset_of!(kvm_regs, fp_regs, user_fpsimd_state, fpcr), + ), _ => panic!("No such Register"), }; + // The core registers of an arm64 machine are represented + // in kernel by the `kvm_regs` structure. This structure is a + // mix of 32, 64 and 128 bit fields, we index it as if it + // was a 32bit array. + // struct kvm_regs { + // struct user_pt_regs regs; + // __u64 sp_el1; + // __u64 elr_el1; + // __u64 spsr[KVM_NR_SPSR]; + // struct user_fpsimd_state fp_regs; + // }; + // struct user_pt_regs { + // __u64 regs[31]; + // __u64 sp; + // __u64 pc; + // __u64 pstate; + // }; + + // struct user_fpsimd_state { + // __uint128_t vregs[32]; + // __u32 fpsr; + // __u32 fpcr; + // __u32 __reserved[2]; + // }; + + // #define KVM_REG_ARM64 0x6000000000000000ULL + // #define KVM_REG_SIZE_U32 0x0020000000000000ULL + // #define KVM_REG_SIZE_U64 0x0030000000000000ULL + // #define KVM_REG_SIZE_U128 0x0040000000000000ULL + // #define KVM_REG_ARM_CORE 0x00100000ULL + + // The id of the register is encoded as specified for `KVM_GET_ONE_REG` in the kernel documentation. + // reg_id = KVM_REG_ARM64 | KVM_REG_SIZE_* | KVM_REG_ARM_CORE | reg_offset_index + // reg_offset_index = reg_offset / sizeof(u32) + // KVM_REG_SIZE_* => KVM_REG_SIZE_U32/KVM_REG_SIZE_U64/KVM_REG_SIZE_U128 + + // calculate reg_id KVM_REG_ARM64 as u64 | register_size as u64 | u64::from(KVM_REG_ARM_CORE) - | (regid / size_of::()) as u64 + | (reg_offset / size_of::()) as u64 } } @@ -196,8 +224,7 @@ pub fn get_core_regs(vcpu_fd: &VcpuFd) -> Result { for i in 0..KVM_NR_FP_REGS as usize { let register_value_vec = get_one_reg_vec(vcpu_fd, Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; - core_regs.fp_regs.vregs[i][0] = *u64::from_bytes(®ister_value_vec[0..8]).unwrap(); - core_regs.fp_regs.vregs[i][1] = *u64::from_bytes(®ister_value_vec[8..16]).unwrap(); + core_regs.fp_regs.vregs[i] = *u128::from_bytes(®ister_value_vec).unwrap(); } let register_value_vec = get_one_reg_vec(vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpsr.into())?; @@ -237,8 +264,8 @@ pub fn set_core_regs(vcpu_fd: &VcpuFd, core_regs: kvm_regs) -> Result<()> { for i in 0..KVM_NR_FP_REGS as usize { let mut data: Vec = Vec::new(); - data.append(&mut core_regs.fp_regs.vregs[i][0].as_bytes().to_vec()); - data.append(&mut core_regs.fp_regs.vregs[i][1].as_bytes().to_vec()); + data.append(&mut core_regs.fp_regs.vregs[i].as_bytes().to_vec()); + set_one_reg_vec( vcpu_fd, Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index a85ca8231..8d1ffdfd7 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -17,11 +17,10 @@ use std::sync::{Arc, Mutex}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{ - kvm_mp_state, kvm_reg_list, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, KVM_MP_STATE_RUNNABLE, + kvm_mp_state, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, RegList, KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_STOPPED, }; use kvm_ioctls::VcpuFd; -use vmm_sys_util::fam::FamStructWrapper; pub use self::caps::ArmCPUCaps; use self::caps::CpregListEntry; @@ -33,8 +32,6 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -type CpregList = FamStructWrapper; - // PSR (Processor State Register) bits. // See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/ptrace.h#L34 #[allow(non_upper_case_globals)] @@ -49,7 +46,7 @@ const UNINIT_MPIDR: u64 = 0xFFFF_FF00_0000_0000; // MPIDR - Multiprocessor Affinity Register. // See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h#L130 const SYS_MPIDR_EL1: u64 = 0x6030_0000_0013_c005; -const KVM_MAX_CPREG_ENTRIES: usize = 1024; +const KVM_MAX_CPREG_ENTRIES: usize = 500; /// AArch64 CPU booting configure information /// @@ -256,7 +253,7 @@ impl StateTransfer for CPU { cpu_state_locked.mp_state = mp_state; } - let mut cpreg_list = CpregList::new(KVM_MAX_CPREG_ENTRIES); + let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES).unwrap(); self.fd.get_reg_list(&mut cpreg_list)?; cpu_state_locked.cpreg_len = 0; for (index, cpreg) in cpreg_list.as_slice().iter().enumerate() { diff --git a/cpu/src/x86_64/caps.rs b/cpu/src/x86_64/caps.rs index 4bf75fb03..5f131389c 100644 --- a/cpu/src/x86_64/caps.rs +++ b/cpu/src/x86_64/caps.rs @@ -65,6 +65,6 @@ impl X86CPUCaps { } }) .collect(); - Msrs::from_entries(&entry_vec) + Msrs::from_entries(&entry_vec).unwrap() } } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 173e566e9..f2145cbff 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -241,7 +241,7 @@ impl X86CPUState { .set_lapic(&self.lapic) .chain_err(|| format!("Failed to set lapic for CPU {}", self.apic_id))?; vcpu_fd - .set_msrs(&Msrs::from_entries(&self.msr_list[0..self.msr_len])) + .set_msrs(&Msrs::from_entries(&self.msr_list[0..self.msr_len]).unwrap()) .chain_err(|| format!("Failed to set msrs for CPU {}", self.apic_id))?; vcpu_fd .set_vcpu_events(&self.cpu_events) diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 5e553a0d9..14b5d2390 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -2,16 +2,18 @@ name = "devices" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" [dependencies] error-chain = "0.12.4" -libc = ">=0.2.71" -log = "0.4.8" -kvm-ioctls = "0.6.0" -serde = { version = ">=1.0.114", features = ["derive"] } -vmm-sys-util = ">=0.7.0" +libc = "0.2" +log = "0.4" +kvm-ioctls = ">=0.11.0" +serde = { version = "1.0", features = ["derive"] } +vmm-sys-util = ">=0.10.0" +byteorder = "1.4.3" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } @@ -20,8 +22,6 @@ migration_derive = { path = "../migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } acpi = { path = "../acpi" } -byteorder = "1.3.4" -kvm-bindings = ">=0.3.0" [dev-dependencies] serial_test = "0.5.1" diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index ecb457c29..4b90f3bd4 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -2,15 +2,15 @@ name = "hypervisor" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" [dependencies] -arc-swap = "0.4.8" +arc-swap = ">=1.5.0" error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -log = "0.4.8" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +log = "0.4" +vmm-sys-util = ">=0.10.0" +once_cell = "1.13.0" util = { path = "../util" } -vmm-sys-util = ">=0.7.0" -once_cell = "1.9.0" diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 33596a1db..a1eae9be5 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -17,7 +17,7 @@ use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; use util::bitmap::Bitmap; use vmm_sys_util::ioctl::ioctl_with_val; -use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; +use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; use crate::errors::{Result, ResultExt}; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 78833c231..60990feeb 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -20,8 +20,7 @@ use kvm_ioctls::{Kvm, VmFd}; use log::error; use once_cell::sync::Lazy; use vmm_sys_util::{ - eventfd::EventFd, ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, - ioctl_iowr_nr, + eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, }; use crate::errors::{Result, ResultExt}; @@ -105,7 +104,7 @@ pub struct KVMFds { impl KVMFds { pub fn new() -> Self { - let kvm_fds = match Kvm::new() { + match Kvm::new() { Ok(fd) => { let vm_fd = match fd.create_vm() { Ok(vm_fd) => vm_fd, @@ -126,9 +125,7 @@ impl KVMFds { error!("Failed to open /dev/kvm: {}", e); KVMFds::default() } - }; - - kvm_fds + } } /// Sets the gsi routing table entries. It will overwrite previously set entries. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 4a460e101..8e14324a7 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -2,20 +2,20 @@ name = "machine" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "Emulation machines" [dependencies] error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -log = "0.4.8" -libc = ">=0.2.71" -serde = { version = ">=1.0.114", features = ["derive"] } -serde_json = "1.0.55" -vmm-sys-util = ">=0.7.0" -vfio-bindings = "0.2.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +log = "0.4" +libc = "0.2" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +vmm-sys-util = ">=0.10.0" +vfio-bindings = "0.3" acpi = { path = "../acpi" } address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index b4b207e83..68726d5cb 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -765,7 +765,7 @@ impl MachineOps for LightMachine { .vm_fd .as_ref() .unwrap() - .create_vcpu(vcpu_id)?, + .create_vcpu(vcpu_id as u64)?, )); } #[cfg(target_arch = "aarch64")] @@ -1290,12 +1290,10 @@ impl MigrateInterface for LightMachine { None, ) } - _ => { - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), - None, - ); - } + _ => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), + None, + ), } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index d8df7fbdc..9b1a7046c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -441,7 +441,7 @@ impl MachineOps for StdMachine { .vm_fd .as_ref() .unwrap() - .create_vcpu(vcpu_id)?, + .create_vcpu(vcpu_id as u64)?, )); } fds diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 77c51ede8..0aa5d1328 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -22,7 +22,7 @@ use std::sync::{Arc, Condvar, Mutex}; use error_chain::{bail, ChainedError}; use log::error; -use vmm_sys_util::{eventfd::EventFd, ioctl_expr, ioctl_ioc_nr, ioctl_iow_nr}; +use vmm_sys_util::{eventfd::EventFd, ioctl_ioc_nr, ioctl_iow_nr}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, @@ -459,7 +459,7 @@ impl MachineOps for StdMachine { let nr_cpus = vm_config.machine_config.nr_cpus; let mut vcpu_fds = vec![]; for cpu_id in 0..nr_cpus { - vcpu_fds.push(Arc::new(vm_fd.create_vcpu(cpu_id)?)); + vcpu_fds.push(Arc::new(vm_fd.create_vcpu(cpu_id as u64)?)); } locked_vm diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 9940c7397..3e797e6ba 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -2,22 +2,22 @@ name = "machine_manager" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] error-chain = "0.12.4" -log = "0.4.8" -libc = ">=0.2.71" -serde_json = "1.0.55" -vmm-sys-util = ">=0.7.0" -util = { path = "../util" } -serde = { version = ">=1.0.114", features = ["derive"] } +log = "0.4" +libc = "0.2" +serde_json = "1.0" +vmm-sys-util = ">=0.10.0" +serde = { version = "1.0", features = ["derive"] } strum = "0.20" strum_macros = "0.20" -once_cell = "1.9.0" +once_cell = "1.13.0" +util = { path = "../util" } [features] default = [] diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index e1d5d575f..befde9011 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -44,6 +44,7 @@ use serde_json::Value; use util::leak_bucket::LeakBucket; use util::set_termi_canon_mode; +use self::qmp_schema::{self as schema, QmpCommand}; use crate::event_loop::EventLoop; use crate::machine::MachineExternalInterface; use crate::socket::SocketRWHandler; @@ -51,8 +52,6 @@ use crate::{ errors::{Result, ResultExt}, temp_cleaner::TempCleaner, }; -use qmp_schema as schema; -use schema::QmpCommand; static mut QMP_CHANNEL: Option> = None; @@ -581,8 +580,8 @@ impl QmpChannel { #[cfg(test)] mod tests { - extern crate serde_json; use super::*; + use serde_json; use std::os::unix::net::{UnixListener, UnixStream}; #[test] diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 521da0015..a81486e8c 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -2,16 +2,16 @@ name = "migration" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" [dependencies] error-chain = "0.12.4" -kvm-ioctls = "0.6.0" -kvm-bindings = ">=0.3.0" -serde = { version = ">=1.0.114", features = ["derive"] } -serde_json = "1.0.55" -once_cell = "1.9.0" -log = "0.4.8" +kvm-ioctls = ">=0.11.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +once_cell = "1.13.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +log = "0.4" util = {path = "../util"} hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/migration_derive/Cargo.toml b/migration_derive/Cargo.toml index a2a0d80d1..5f889d136 100644 --- a/migration_derive/Cargo.toml +++ b/migration_derive/Cargo.toml @@ -2,12 +2,12 @@ name = "migration_derive" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" [dependencies] syn = { version = "1.0.72", features = ["full", "extra-traits"] } -quote = "1.0.7" +quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] diff --git a/migration_derive/src/field_parser.rs b/migration_derive/src/field_parser.rs index 24033997f..9df5aeb8f 100644 --- a/migration_derive/src/field_parser.rs +++ b/migration_derive/src/field_parser.rs @@ -76,14 +76,10 @@ fn parse_field( fn parse_ty(input: syn::Type) -> (syn::TypePath, usize, bool) { match input { syn::Type::Array(array) => { - let array_type_token; - - match *array.elem.clone() { - syn::Type::Path(token) => { - array_type_token = token; - } + let array_type_token = match *array.elem.clone() { + syn::Type::Path(token) => token, _ => panic!("Unsupported array type."), - } + }; match &array.len { syn::Expr::Lit(expr_lit) => match &expr_lit.lit { diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index 6208f0c41..bc0f23091 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -2,12 +2,12 @@ name = "ozone" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" description = "Provides protection for stratovirt" license = "Mulan PSL v2" [dependencies] error-chain = "0.12.4" -libc = ">=0.2.71" +libc = "0.2" util = { path = "../util" } diff --git a/pci/Cargo.toml b/pci/Cargo.toml index 4e919610f..abb22d92d 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -2,19 +2,19 @@ name = "pci" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "PCI" [dependencies] -byteorder = "1.3.4" +byteorder = "1.4.3" error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -libc = ">=0.2.71" -log = "0.4.8" -vmm-sys-util = ">=0.7.0" -once_cell = "1.9.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +libc = "0.2" +log = "0.4" +vmm-sys-util = ">=0.10.0" +once_cell = "1.13.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/pci/src/msix.rs b/pci/src/msix.rs index ee1fbf7e9..1ec78e547 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -352,7 +352,7 @@ impl MigrationHook for Msix { // update and commit irq routing { - let kvm_fds = KVM_FDS.load_signal_safe(); + let kvm_fds = KVM_FDS.load(); let mut locked_irq_table = kvm_fds.irq_route_table.lock().unwrap(); let allocated_gsi = match locked_irq_table.allocate_gsi() { Ok(gsi) => gsi, diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml index fcbb6ef32..ba4520d55 100644 --- a/sysbus/Cargo.toml +++ b/sysbus/Cargo.toml @@ -2,14 +2,14 @@ name = "sysbus" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "Emulate system bus" [dependencies] error-chain = "0.12.4" -kvm-ioctls = "0.6.0" -vmm-sys-util = ">=0.7.0" +kvm-ioctls = ">=0.11.0" +vmm-sys-util = ">=0.10.0" acpi = { path = "../acpi" } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/util/Cargo.toml b/util/Cargo.toml index 7611418ad..826066514 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -2,18 +2,18 @@ name = "util" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -arc-swap = "0.4.8" +arc-swap = ">=1.5.0" error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -libc = ">=0.2.71" -log = { version = "0.4.8", features = ["std"]} -vmm-sys-util = ">=0.7.0" -byteorder = "1.3.4" -once_cell = "1.9.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +libc = "0.2" +log = { version = "0.4", features = ["std"]} +vmm-sys-util = ">=0.10.0" +byteorder = "1.4.3" +once_cell = "1.13.0" diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 667d822d1..cd4604591 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -63,11 +63,13 @@ impl ByteCode for u8 {} impl ByteCode for u16 {} impl ByteCode for u32 {} impl ByteCode for u64 {} +impl ByteCode for u128 {} impl ByteCode for isize {} impl ByteCode for i8 {} impl ByteCode for i16 {} impl ByteCode for i32 {} impl ByteCode for i64 {} +impl ByteCode for i128 {} #[cfg(test)] mod test { diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ce3aa5db4..20dbc9ea4 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -431,10 +431,7 @@ impl EventLoopContext { } fn epoll_wait_manager(&mut self, time_out: i32) -> Result { - let ev_count = match self - .epoll - .wait(READY_EVENT_MAX, time_out, &mut self.ready_events[..]) - { + let ev_count = match self.epoll.wait(time_out, &mut self.ready_events[..]) { Ok(ev_count) => ev_count, Err(e) if e.raw_os_error() == Some(libc::EINTR) => 0, Err(e) => return Err(ErrorKind::EpollWait(e).into()), diff --git a/util/src/tap.rs b/util/src/tap.rs index 5bed417a7..a1bc805a6 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -16,7 +16,7 @@ use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; -use vmm_sys_util::{ioctl_expr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; use super::errors::{Result, ResultExt}; diff --git a/util/src/unix.rs b/util/src/unix.rs index a7ce4ceee..07644fc21 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -312,13 +312,13 @@ impl UnixSock { unsafe { sendmsg(self.sock.as_ref().unwrap().as_raw_fd(), &msg, MSG_NOSIGNAL) }; if write_count == -1 { - return Err(std::io::Error::new( + Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!( "Failed to send msg, err: {}", std::io::Error::last_os_error() ), - )); + )) } else { Ok(write_count as usize) } diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index ddb385a14..38f8bc8ff 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -2,20 +2,20 @@ name = "vfio" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "Virtual function I/O" [dependencies] -byteorder = "1.3.4" +byteorder = "1.4.3" error-chain = "0.12.4" -kvm-bindings = ">=0.3.0" -kvm-ioctls = "0.6.0" -libc = ">=0.2.71" -log = "0.4.8" -vmm-sys-util = ">=0.7.0" -vfio-bindings = "0.2.0" -once_cell = "1.9.0" +kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-ioctls = ">=0.11.0" +libc = "0.2" +log = "0.4" +vmm-sys-util = ">=0.10.0" +vfio-bindings = "0.3" +once_cell = "1.13.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } util = { path = "../util" } diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 908d8a5f4..51ed35a9a 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -29,7 +29,7 @@ use vfio_bindings::bindings::vfio; use vmm_sys_util::ioctl::{ ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, }; -use vmm_sys_util::{ioctl_expr, ioctl_io_nr, ioctl_ioc_nr}; +use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 547948355..beb38afb7 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -2,18 +2,18 @@ name = "virtio" version = "2.2.0" authors = ["Huawei StratoVirt Team"] -edition = "2018" +edition = "2021" license = "Mulan PSL v2" description = "Virtio devices emulation" [dependencies] -byteorder = "1.3.4" +byteorder = "1.4.3" error-chain = "0.12.4" -kvm-ioctls = "0.6.0" -libc = ">=0.2.71" -log = "0.4.8" -serde_json = "1.0.55" -vmm-sys-util = ">=0.7.0" +kvm-ioctls = ">=0.11.0" +libc = "0.2" +log = "0.4" +serde_json = "1.0" +vmm-sys-util = ">=0.10.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index fa4a79851..3deef2ad2 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -33,9 +33,7 @@ use util::loop_context::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; -use vmm_sys_util::{ - ioctl_expr, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, -}; +use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; use super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; -- Gitee From 58d54eb1a588ed97be57e59f29a6a1f81512313e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 11 Aug 2022 20:44:55 +0800 Subject: [PATCH 0104/2187] vhost-user: support vhost-user net reconnection Support vhost-user net reconnection. --- virtio/src/vhost/user/client.rs | 224 ++++++++++++++++++++++++++------ virtio/src/vhost/user/net.rs | 109 ++++------------ virtio/src/vhost/user/sock.rs | 2 + 3 files changed, 215 insertions(+), 120 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index e76f0865f..16be44937 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -18,7 +18,7 @@ use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; use error_chain::bail; -use log::{info, warn}; +use log::{error, info, warn}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -27,7 +27,7 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::super::super::{ errors::{ErrorKind, Result, ResultExt}, - QueueConfig, + Queue, QueueConfig, VIRTIO_NET_F_CTRL_VQ, }; use super::super::VhostOps; use super::message::{ @@ -41,8 +41,6 @@ struct ClientInternal { sock: VhostUserSock, // Maximum number of queues which is supported. max_queue_num: u64, - // EventFd for client reset. - delete_evt: EventFd, } #[allow(dead_code)] @@ -51,7 +49,6 @@ impl ClientInternal { ClientInternal { sock, max_queue_num, - delete_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } } @@ -76,45 +73,79 @@ impl ClientInternal { Ok(body) } +} - fn delete_evt_handler(&mut self) -> Vec { - vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.sock.domain.get_stream_raw_fd(), - None, - EventSet::HANG_UP, - vec![], - ), - EventNotifier::new( - NotifierOperation::Delete, - self.delete_evt.as_raw_fd(), - None, - EventSet::IN, - vec![], - ), - ] +pub fn vhost_user_reconnect(client: &Arc>) { + let cloned_client = client.clone(); + let func = Box::new(move || { + vhost_user_reconnect(&cloned_client); + }); + + info!("Try to reconnect vhost-user net."); + let cloned_client = client.clone(); + if let Err(_e) = client + .lock() + .unwrap() + .client + .lock() + .unwrap() + .sock + .domain + .connect() + { + if let Some(ctx) = EventLoop::get_ctx(None) { + // Default reconnecting time: 3s. + ctx.delay_call(func, 3 * 1_000_000_000); + } else { + error!("Failed to get ctx to delay vhost-user reconnecting"); + } + return; + } + + client.lock().unwrap().reconnecting = false; + if let Err(e) = + EventLoop::update_event(EventNotifierHelper::internal_notifiers(cloned_client), None) + { + error!("Failed to update event for client sock, {}", e); + } + + if let Err(e) = client.lock().unwrap().activate_vhost_user() { + error!("Failed to reactivate vhost-user net, {}", e); + } else { + info!("Reconnecting vhost-user net succeed."); } } -impl EventNotifierHelper for ClientInternal { +impl EventNotifierHelper for VhostUserClient { fn internal_notifiers(client_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); let mut handlers = Vec::new(); + let cloned_client = client_handler.clone(); let handler: Box Option>> = Box::new(move |event, _| { if event & EventSet::HANG_UP == EventSet::HANG_UP { - panic!("Receive the event of HANG_UP from vhost user backend"); + let mut locked_client = cloned_client.lock().unwrap(); + if let Err(e) = locked_client.delete_event() { + error!("Failed to delete vhost-user client event, {}", e); + } + if !locked_client.reconnecting { + locked_client.reconnecting = true; + drop(locked_client); + vhost_user_reconnect(&cloned_client); + } + None } else { None } }); handlers.push(Arc::new(Mutex::new(handler))); + let locked_client = client_handler.lock().unwrap(); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - client_handler + locked_client + .client .lock() .unwrap() .sock @@ -133,7 +164,7 @@ impl EventNotifierHelper for ClientInternal { }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - client_handler.lock().unwrap().delete_evt.as_raw_fd(), + locked_client.delete_evt.as_raw_fd(), None, EventSet::IN, vec![Arc::new(Mutex::new(handler))], @@ -266,10 +297,15 @@ impl Listener for VhostUserMemInfo { } /// Struct for communication with the vhost user backend in userspace -#[derive(Clone)] pub struct VhostUserClient { client: Arc>, mem_info: VhostUserMemInfo, + delete_evt: EventFd, + queues: Vec>>, + queue_evts: Vec, + call_events: Vec, + pub features: u64, + reconnecting: bool, } #[allow(dead_code)] @@ -289,28 +325,140 @@ impl VhostUserClient { .chain_err(|| "Failed to register memory for vhost user client")?; let client = Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))); - Ok(VhostUserClient { client, mem_info }) + let delete_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + Ok(VhostUserClient { + client, + mem_info, + delete_evt, + queues: Vec::new(), + queue_evts: Vec::new(), + call_events: Vec::new(), + features: 0, + reconnecting: false, + }) } - pub fn add_event_notifier(&self) -> Result<()> { - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(self.client.clone()), - None, - ) - .chain_err(|| "Failed to update event for client sock")?; + /// Save queue info used for reconnection. + pub fn set_queues(&mut self, queues: &[Arc>]) { + for (queue_index, _) in queues.iter().enumerate() { + self.queues.push(queues[queue_index].clone()); + } + } + + /// Save eventfd used for reconnection. + pub fn set_queue_evts(&mut self, queue_evts: &[EventFd]) { + for evt in queue_evts.iter() { + self.queue_evts.push(evt.try_clone().unwrap()); + } + } + + /// Save irqfd used for reconnection. + pub fn set_call_events(&mut self, call_events: &[EventFd]) { + for evt in call_events.iter() { + self.call_events.push(evt.try_clone().unwrap()); + } + } + + /// Activate device by vhost-user protocol. + pub fn activate_vhost_user(&mut self) -> Result<()> { + self.set_owner() + .chain_err(|| "Failed to set owner for vhost-user net")?; + + self.set_features(self.features) + .chain_err(|| "Failed to set features for vhost-user net")?; + + self.set_mem_table() + .chain_err(|| "Failed to set mem table for vhost-user net")?; + + let mut queue_num = self.queues.len(); + if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { + queue_num -= 1; + } + + // Set all vring num to notify ovs/dpdk how many queues it needs to poll + // before setting vring info. + for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { + let queue = queue_mutex.lock().unwrap(); + let actual_size = queue.vring.actual_size(); + self.set_vring_num(queue_index, actual_size).chain_err(|| { + format!( + "Failed to set vring num for vhost-user net, index: {}, size: {}", + queue_index, actual_size, + ) + })?; + } + + for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { + let queue = queue_mutex.lock().unwrap(); + let queue_config = queue.vring.get_queue_config(); + + self.set_vring_addr(&queue_config, queue_index, 0) + .chain_err(|| { + format!( + "Failed to set vring addr for vhost-user net, index: {}", + queue_index, + ) + })?; + self.set_vring_base(queue_index, 0).chain_err(|| { + format!( + "Failed to set vring base for vhost-user net, index: {}", + queue_index, + ) + })?; + self.set_vring_kick(queue_index, &self.queue_evts[queue_index]) + .chain_err(|| { + format!( + "Failed to set vring kick for vhost-user net, index: {}", + queue_index, + ) + })?; + self.set_vring_call(queue_index, &self.call_events[queue_index]) + .chain_err(|| { + format!( + "Failed to set vring call for vhost-user net, index: {}", + queue_index, + ) + })?; + } + + for (queue_index, _) in self.queues.iter().enumerate().take(queue_num) { + self.set_vring_enable(queue_index, true).chain_err(|| { + format!( + "Failed to set vring enable for vhost-user net, index: {}", + queue_index, + ) + })?; + } Ok(()) } + /// Delete the socket event in ClientInternal. pub fn delete_event(&self) -> Result<()> { - self.client - .lock() - .unwrap() - .delete_evt + self.delete_evt .write(1) .chain_err(|| ErrorKind::EventFdWrite)?; Ok(()) } + + fn delete_evt_handler(&mut self) -> Vec { + vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.client.lock().unwrap().sock.domain.get_stream_raw_fd(), + None, + EventSet::HANG_UP, + vec![], + ), + EventNotifier::new( + NotifierOperation::Delete, + self.delete_evt.as_raw_fd(), + None, + EventSet::IN, + vec![], + ), + ] + } } impl VhostOps for VhostUserClient { diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index e5a7b2608..8e5385e05 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -52,7 +52,7 @@ pub struct Net { /// System address space. mem_space: Arc, /// Vhost user client - client: Option, + client: Option>>, /// The notifier events from host. call_events: Vec, /// EventFd for deactivate control Queue. @@ -73,10 +73,12 @@ impl Net { } } - fn stop_event(&mut self) -> Result<()> { + fn delete_event(&mut self) -> Result<()> { match &self.client { Some(client) => { client + .lock() + .unwrap() .delete_event() .chain_err(|| "Failed to delete vhost-user net event")?; } @@ -92,7 +94,7 @@ impl Net { } fn clean_up(&mut self) -> Result<()> { - self.stop_event()?; + self.delete_event()?; self.device_features = 0_u64; self.driver_features = 0_u64; self.device_config = VirtioNetConfig::default(); @@ -115,11 +117,17 @@ impl VirtioDevice for Net { .chain_err(|| { "Failed to create the client which communicates with the server for vhost-user net" })?; - client - .add_event_notifier() - .chain_err(|| "Failed to add event nitifier for vhost-user net")?; + let client = Arc::new(Mutex::new(client)); + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(client.clone()), + None, + ) + .chain_err(|| "Failed to update event for client sock")?; self.device_features = client + .lock() + .unwrap() .get_features() .chain_err(|| "Failed to get features for vhost-user net")?; @@ -228,11 +236,10 @@ impl VirtioDevice for Net { queues: &[Arc>], mut queue_evts: Vec, ) -> Result<()> { - let mut queue_num = queues.len(); + let queue_num = queues.len(); if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts.remove(queue_num - 1); - queue_num -= 1; let ctrl_handler = NetCtrlHandler { ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt), @@ -248,83 +255,16 @@ impl VirtioDevice for Net { )?; } - let client = match &self.client { - Some(client_) => client_, + let mut client = match &self.client { + Some(client) => client.lock().unwrap(), None => return Err("Failed to get client for vhost-user net".into()), }; - client - .set_owner() - .chain_err(|| "Failed to set owner for vhost-user net")?; - let features = self.driver_features & !(1 << VIRTIO_NET_F_MAC); - client - .set_features(features) - .chain_err(|| "Failed to set features for vhost-user net")?; - - client - .set_mem_table() - .chain_err(|| "Failed to set mem table for vhost-user net")?; - - // Set all vring num to notify ovs/dpdk how many queues it needs to poll - // before setting vring info. - for (queue_index, queue_mutex) in queues.iter().enumerate().take(queue_num) { - let queue = queue_mutex.lock().unwrap(); - let actual_size = queue.vring.actual_size(); - client - .set_vring_num(queue_index, actual_size) - .chain_err(|| { - format!( - "Failed to set vring num for vhost-user net, index: {}, size: {}", - queue_index, actual_size, - ) - })?; - } - - for (queue_index, queue_mutex) in queues.iter().enumerate().take(queue_num) { - let queue = queue_mutex.lock().unwrap(); - let queue_config = queue.vring.get_queue_config(); - - client - .set_vring_addr(&queue_config, queue_index, 0) - .chain_err(|| { - format!( - "Failed to set vring addr for vhost-user net, index: {}", - queue_index, - ) - })?; - client.set_vring_base(queue_index, 0).chain_err(|| { - format!( - "Failed to set vring base for vhost-user net, index: {}", - queue_index, - ) - })?; - client - .set_vring_kick(queue_index, &queue_evts[queue_index]) - .chain_err(|| { - format!( - "Failed to set vring kick for vhost-user net, index: {}", - queue_index, - ) - })?; - client - .set_vring_call(queue_index, &self.call_events[queue_index]) - .chain_err(|| { - format!( - "Failed to set vring call for vhost-user net, index: {}", - queue_index, - ) - })?; - } - - for (queue_index, _) in queues.iter().enumerate().take(queue_num) { - client.set_vring_enable(queue_index, true).chain_err(|| { - format!( - "Failed to set vring enable for vhost-user net, index: {}", - queue_index, - ) - })?; - } + client.features = features; + client.set_queues(queues); + client.set_queue_evts(&queue_evts); + client.activate_vhost_user()?; Ok(()) } @@ -336,6 +276,11 @@ impl VirtioDevice for Net { self.call_events.push(cloned_evt_fd); } + match &self.client { + Some(client) => client.lock().unwrap().set_call_events(queue_evts), + None => return Err("Failed to get client for vhost-user net".into()), + }; + Ok(()) } @@ -351,7 +296,7 @@ impl VirtioDevice for Net { } fn unrealize(&mut self) -> Result<()> { - self.stop_event()?; + self.delete_event()?; self.call_events.clear(); self.client = None; diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index 9cbab183a..208bee5fc 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -23,12 +23,14 @@ use super::message::{MAX_ATTACHED_FD_ENTRIES, VHOST_USER_MSG_MAX_SIZE}; #[derive(Clone)] pub struct VhostUserSock { pub domain: UnixSock, + pub path: String, } impl VhostUserSock { pub fn new(path: &str) -> Self { VhostUserSock { domain: UnixSock::new(path), + path: path.to_string(), } } -- Gitee From b59111784be70d48c8b7f55309a5735519562396 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 13 Aug 2022 14:17:02 +0800 Subject: [PATCH 0105/2187] net config: fix error handle queues number of net config The args of `queues` indicates the number of queue pairs. And it already handle before, so delete error codes here. Signed-off-by: Xinle.Guo --- machine_manager/src/config/network.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index fe85b4a39..883d1f47d 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -329,14 +329,9 @@ pub fn get_netdev_config(args: Box) -> Result = fds.split(':').collect(); -- Gitee From 8398c5068756a2df37bd4d36aba1feb2f3cbb246 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Sun, 14 Aug 2022 06:43:20 +0000 Subject: [PATCH 0106/2187] util/loop_context: fix wrong index It is wrong to both use i as index in nested loop. So change the second index as j. Signed-off-by: wubinfeng --- util/src/loop_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ce3aa5db4..62e29c912 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -448,8 +448,8 @@ impl EventLoopContext { }; if let EventStatus::Alive = event.status { let mut notifiers = Vec::new(); - for i in 0..event.handlers.len() { - let handle = event.handlers[i].lock().unwrap(); + for j in 0..event.handlers.len() { + let handle = event.handlers[j].lock().unwrap(); match handle(self.ready_events[i].event_set(), event.raw_fd) { None => {} Some(mut notifier) => { -- Gitee From dc42cf0a253e47900ba0e427fd1cd24157023b87 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 13 Aug 2022 16:30:40 +0800 Subject: [PATCH 0107/2187] queue_number: Increase the max number of queue config state. Increase the max number of queue config state to 32. For snapshot multiqueue virtio device, the `queues_config` number in `VirtioPciState` struct should be equal to the max value of multiple queue number. If not ,it will case paninc because the array is out of range. refer: https://gitee.com/openeuler/stratovirt/pulls/479 Signed-off-by: Xinle.Guo --- machine_manager/src/config/mod.rs | 1 + virtio/src/virtio_pci.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 2df85eb98..cab588205 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -121,6 +121,7 @@ use util::trace::enable_trace_events; pub const MAX_STRING_LENGTH: usize = 255; pub const MAX_PATH_LENGTH: usize = 4096; +// FIXME: `queue_config` len in `VirtioPciState` struct needs to be modified together. pub const MAX_VIRTIO_QUEUE: usize = 32; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 18a571a25..1cdc4b3fe 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -461,8 +461,8 @@ pub struct VirtioPciState { config_generation: u32, queue_select: u16, msix_config: u16, - /// The configuration of queues. Max number of queues is 8. - queues_config: [QueueConfig; 8], + /// The configuration of queues. Max number of queues is 32(equals to MAX_VIRTIO_QUEUE). + queues_config: [QueueConfig; 32], /// The number of queues. queue_num: usize, } -- Gitee From 460d9124487cc3fc2c49afd15cbb095850bd0003 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 13 Aug 2022 16:44:04 +0800 Subject: [PATCH 0108/2187] migration: Enhance to check migrate device policy. In order to tolerate different device cmdline and adapt hot plug devices before migration, enhance check migrate device policy. Just check device type and its BDF is match. Signed-off-by: Xinle.Guo --- machine_manager/src/config/pci.rs | 2 +- migration/src/migration.rs | 79 ++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index edfa7f257..82a16e1fb 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -19,7 +19,7 @@ use crate::config::ExBool; /// Basic information of pci devices such as bus number, /// slot number and function number. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct PciBdf { /// Bus number pub bus: String, diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 480788216..80636df41 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -24,7 +24,7 @@ use crate::manager::MIGRATION_MANAGER; use crate::protocol::{MemBlock, MigrationStatus, Request, Response, TransStatus}; use crate::MigrationManager; use hypervisor::kvm::KVM_FDS; -use machine_manager::config::VmConfig; +use machine_manager::config::{get_pci_bdf, PciBdf, VmConfig}; use util::unix::host_page_size; impl MigrationManager { @@ -178,6 +178,7 @@ impl MigrationManager { Ok(()) } + /// Check source and destination virtual machine config. fn check_vm_config(fd: &mut T, len: u64) -> Result<()> where T: Write + Read, @@ -185,46 +186,80 @@ impl MigrationManager { let mut data: Vec = Vec::new(); data.resize_with(len as usize, Default::default); fd.read_exact(&mut data)?; - let source_config: VmConfig = serde_json::from_slice(&data)?; - let dest_config = &MIGRATION_MANAGER.vmm.read().unwrap().config; + let src_config: &VmConfig = &serde_json::from_slice(&data)?; + let dest_config: &VmConfig = &MIGRATION_MANAGER.vmm.read().unwrap().config; // Check vCPU number. - let source_cpu = source_config.machine_config.nr_cpus; + Self::check_vcpu(src_config, dest_config)?; + Self::check_memory(src_config, dest_config)?; + Self::check_devices(src_config, dest_config)?; + + Response::send_msg(fd, TransStatus::Ok)?; + + Ok(()) + } + + /// Check vcpu number config. + fn check_vcpu(src_config: &VmConfig, dest_config: &VmConfig) -> Result<()> { + let src_cpu = src_config.machine_config.nr_cpus; let dest_cpu = dest_config.machine_config.nr_cpus; - if source_cpu != dest_cpu { + if src_cpu != dest_cpu { return Err(ErrorKind::MigrationConfigErr( "vCPU number".to_string(), - source_cpu.to_string(), + src_cpu.to_string(), dest_cpu.to_string(), ) .into()); } - // Check memory size - let source_mem = source_config.machine_config.mem_config.mem_size; + Ok(()) + } + + /// Check memory size config. + fn check_memory(src_config: &VmConfig, dest_config: &VmConfig) -> Result<()> { + let src_mem = src_config.machine_config.mem_config.mem_size; let dest_mem = dest_config.machine_config.mem_config.mem_size; - if source_mem != dest_mem { + if src_mem != dest_mem { return Err(ErrorKind::MigrationConfigErr( "memory size".to_string(), - source_mem.to_string(), + src_mem.to_string(), dest_mem.to_string(), ) .into()); } - // Check devices number. - let source_dev = source_config.devices.len(); - let dest_dev = dest_config.devices.len(); - if source_dev != dest_dev { - return Err(ErrorKind::MigrationConfigErr( - "device number".to_string(), - source_dev.to_string(), - dest_dev.to_string(), - ) - .into()); - } + Ok(()) + } - Response::send_msg(fd, TransStatus::Ok)?; + /// Check devices type and BDF config. + fn check_devices(src_config: &VmConfig, dest_config: &VmConfig) -> Result<()> { + let mut dest_devices: HashMap = HashMap::new(); + for (dev_type, dev_info) in dest_config.devices.iter() { + if let Ok(dest_bdf) = get_pci_bdf(dev_info) { + dest_devices.insert(dest_bdf, dev_type.to_string()); + } + } + for (src_type, dev_info) in src_config.devices.iter() { + if let Ok(src_bdf) = get_pci_bdf(dev_info) { + match dest_devices.get(&src_bdf) { + Some(dest_type) => { + if !src_type.eq(dest_type) { + return Err(ErrorKind::MigrationConfigErr( + "device type".to_string(), + src_type.to_string(), + dest_type.to_string(), + ) + .into()); + } + } + None => bail!( + "Failed to get destination device bdf {:?}, type {}", + src_bdf, + src_type + ), + } + } + } Ok(()) } -- Gitee From 5ed6190f883e114e47831aab7253395a22c3cb98 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 11:24:27 +0800 Subject: [PATCH 0109/2187] msix: add table_offset and pba_offset in init_msix Expose table_offset and pba_offset in init_msix. Add parent_region which the MSI-X registered. Signed-off-by: zhouli57 --- pci/src/msix.rs | 82 +++++++++++++++++++++++++++++++++------- pci/src/root_port.rs | 10 ++++- virtio/src/virtio_pci.rs | 4 ++ 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 1ec78e547..fd043f1ca 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -166,6 +166,8 @@ impl Msix { msix: Arc>, region: &Region, dev_id: Arc, + table_offset: u64, + pba_offset: u64, ) -> Result<()> { let locked_msix = msix.lock().unwrap(); let table_size = locked_msix.table.len() as u64; @@ -219,7 +221,7 @@ impl Msix { }; let table_region = Region::init_io_region(table_size, table_region_ops); region - .add_subregion(table_region, 0) + .add_subregion(table_region, table_offset) .chain_err(|| "Failed to register MSI-X table region.")?; let cloned_msix = msix.clone(); @@ -243,7 +245,7 @@ impl Msix { }; let pba_region = Region::init_io_region(pba_size, pba_region_ops); region - .add_subregion(pba_region, table_size) + .add_subregion(pba_region, pba_offset) .chain_err(|| "Failed to register MSI-X PBA region.")?; Ok(()) @@ -423,12 +425,24 @@ fn send_msix(msg: Message, dev_id: u16) { } /// MSI-X initialization. +/// +/// # Arguments +/// +/// * `bar_id` - BAR id. +/// * `vector_nr` - The number of vector. +/// * `config` - The PCI config. +/// * `dev_id` - Dev id. +/// * `_id` - MSI-X id used in MigrationManager. +/// * `parent_region` - Parent region which the MSI-X region registered. If none, registered in BAR. +/// * `offset_opt` - Offset of table(table_offset) and Offset of pba(pba_offset). Set the table_offset and pba_offset together. pub fn init_msix( bar_id: usize, vector_nr: u32, config: &mut PciConfig, dev_id: Arc, _id: &str, + parent_region: Option<&Region>, + offset_opt: Option<(u32, u32)>, ) -> Result<()> { if vector_nr > MSIX_TABLE_SIZE_MAX as u32 + 1 { bail!("Too many msix vectors."); @@ -443,10 +457,15 @@ pub fn init_msix( MSIX_CAP_FUNC_MASK | MSIX_CAP_ENABLE, )?; offset = msix_cap_offset + MSIX_CAP_TABLE as usize; - le_write_u32(&mut config.config, offset, bar_id as u32)?; - offset = msix_cap_offset + MSIX_CAP_PBA as usize; let table_size = vector_nr * MSIX_TABLE_ENTRY_SIZE as u32; - le_write_u32(&mut config.config, offset, table_size | bar_id as u32)?; + let (table_offset, pba_offset) = if let Some((table, pba)) = offset_opt { + (table, pba) + } else { + (0, table_size) + }; + le_write_u32(&mut config.config, offset, table_offset | bar_id as u32)?; + offset = msix_cap_offset + MSIX_CAP_PBA as usize; + le_write_u32(&mut config.config, offset, pba_offset | bar_id as u32)?; let pba_size = ((round_up(vector_nr as u64, 64).unwrap() / 64) * 8) as u32; let msix = Arc::new(Mutex::new(Msix::new( @@ -455,11 +474,28 @@ pub fn init_msix( msix_cap_offset as u16, dev_id.load(Ordering::Acquire), ))); - let mut bar_size = ((table_size + pba_size) as u64).next_power_of_two(); - bar_size = round_up(bar_size, host_page_size()).unwrap(); - let region = Region::init_container_region(bar_size); - Msix::register_memory_region(msix.clone(), ®ion, dev_id)?; - config.register_bar(bar_id, region, RegionType::Mem32Bit, false, bar_size); + if let Some(region) = parent_region { + Msix::register_memory_region( + msix.clone(), + region, + dev_id, + table_offset as u64, + pba_offset as u64, + )?; + } else { + let mut bar_size = ((table_size + pba_size) as u64).next_power_of_two(); + bar_size = round_up(bar_size, host_page_size()).unwrap(); + let region = Region::init_container_region(bar_size); + Msix::register_memory_region( + msix.clone(), + ®ion, + dev_id, + table_offset as u64, + pba_offset as u64, + )?; + config.register_bar(bar_id, region, RegionType::Mem32Bit, false, bar_size); + } + config.msix = Some(msix.clone()); #[cfg(not(test))] @@ -494,11 +530,22 @@ mod tests { MSIX_TABLE_SIZE_MAX as u32 + 2, &mut pci_config, Arc::new(AtomicU16::new(0)), - "msix" + "msix", + None, + None, ) .is_err()); - init_msix(1, 2, &mut pci_config, Arc::new(AtomicU16::new(0)), "msix").unwrap(); + init_msix( + 1, + 2, + &mut pci_config, + Arc::new(AtomicU16::new(0)), + "msix", + None, + None, + ) + .unwrap(); let msix_cap_start = 64_u8; assert_eq!(pci_config.last_cap_end, 64 + MSIX_CAP_SIZE as u16); // Capabilities pointer @@ -563,7 +610,16 @@ mod tests { #[test] fn test_write_config() { let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2); - init_msix(0, 2, &mut pci_config, Arc::new(AtomicU16::new(0)), "msix").unwrap(); + init_msix( + 0, + 2, + &mut pci_config, + Arc::new(AtomicU16::new(0)), + "msix", + None, + None, + ) + .unwrap(); let msix = pci_config.msix.as_ref().unwrap(); let mut locked_msix = msix.lock().unwrap(); locked_msix.enabled = false; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 25287059f..a59d8fa27 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -291,7 +291,15 @@ impl PciDevOps for RootPort { .add_pcie_cap(self.devfn, self.port_num, PcieDevType::RootPort as u8)?; self.dev_id.store(self.devfn as u16, Ordering::SeqCst); - init_msix(0, 1, &mut self.config, self.dev_id.clone(), &self.name)?; + init_msix( + 0, + 1, + &mut self.config, + self.dev_id.clone(), + &self.name, + None, + None, + )?; let parent_bus = self.parent_bus.upgrade().unwrap(); let mut locked_parent_bus = parent_bus.lock().unwrap(); diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 1cdc4b3fe..db01fdbe7 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -999,6 +999,8 @@ impl PciDevOps for VirtioPciDevice { &mut self.config, self.dev_id.clone(), &self.name, + None, + None, )?; if let Some(ref msix) = self.config.msix { @@ -1738,6 +1740,8 @@ mod tests { &mut virtio_pci.config, virtio_pci.dev_id.clone(), &virtio_pci.name, + None, + None, ) .unwrap(); // Prepare valid queue config -- Gitee From 36d98dff76a05a88d7f40469c29c984935d45f77 Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Thu, 18 Aug 2022 10:45:36 +0800 Subject: [PATCH 0110/2187] docs: update arm microvm kernel config Update arm microvm kernel config, and changes 64K normal pages to 4K normal pages. Signed-off-by: Zhang Yang --- .../micro_vm/kernel_config_5.10_aarch64 | 107 +++++++++++++----- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 b/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 index 5ef0f9d6c..54e2c6e72 100644 --- a/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 +++ b/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 @@ -128,9 +128,11 @@ CONFIG_PAGE_COUNTER=y CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y CONFIG_MEMCG_KMEM=y +# CONFIG_MEMCG_MEMFS_INFO is not set CONFIG_BLK_CGROUP=y CONFIG_CGROUP_WRITEBACK=y CONFIG_CGROUP_SCHED=y +# CONFIG_QOS_SCHED is not set CONFIG_FAIR_GROUP_SCHED=y CONFIG_CFS_BANDWIDTH=y # CONFIG_RT_GROUP_SCHED is not set @@ -152,6 +154,7 @@ CONFIG_IPC_NS=y CONFIG_USER_NS=y CONFIG_PID_NS=y CONFIG_NET_NS=y +# CONFIG_SCHED_STEAL is not set # CONFIG_CHECKPOINT_RESTORE is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set @@ -220,17 +223,19 @@ CONFIG_SLAB_MERGE_DEFAULT=y # CONFIG_SLAB_FREELIST_HARDENED is not set # CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set # CONFIG_PROFILING is not set +CONFIG_KABI_RESERVE=y +CONFIG_KABI_SIZE_ALIGN_CHECKS=y # end of General setup CONFIG_ARM64=y CONFIG_64BIT=y CONFIG_MMU=y -CONFIG_ARM64_PAGE_SHIFT=16 -CONFIG_ARM64_CONT_PTE_SHIFT=5 -CONFIG_ARM64_CONT_PMD_SHIFT=5 -CONFIG_ARCH_MMAP_RND_BITS_MIN=14 -CONFIG_ARCH_MMAP_RND_BITS_MAX=29 -CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=7 +CONFIG_ARM64_PAGE_SHIFT=12 +CONFIG_ARM64_CONT_PTE_SHIFT=4 +CONFIG_ARM64_CONT_PMD_SHIFT=4 +CONFIG_ARCH_MMAP_RND_BITS_MIN=18 +CONFIG_ARCH_MMAP_RND_BITS_MAX=33 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 CONFIG_NO_IOPORT_MAP=y CONFIG_STACKTRACE_SUPPORT=y @@ -249,7 +254,7 @@ CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y CONFIG_SMP=y CONFIG_KERNEL_MODE_NEON=y CONFIG_FIX_EARLYCON_MEM=y -CONFIG_PGTABLE_LEVELS=3 +CONFIG_PGTABLE_LEVELS=4 CONFIG_ARCH_SUPPORTS_UPROBES=y CONFIG_ARCH_PROC_KCORE_TEXT=y CONFIG_ARCH_HAS_CPU_RELAX=y @@ -277,6 +282,7 @@ CONFIG_ARCH_HAS_CPU_RELAX=y # CONFIG_ARCH_MESON is not set # CONFIG_ARCH_MVEBU is not set # CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PHYTIUM is not set # CONFIG_ARCH_QCOM is not set # CONFIG_ARCH_REALTEK is not set # CONFIG_ARCH_RENESAS is not set @@ -297,6 +303,13 @@ CONFIG_ARCH_HAS_CPU_RELAX=y # CONFIG_ARCH_ZYNQMP is not set # end of Platform selection +CONFIG_HAVE_LIVEPATCH_WO_FTRACE=y + +# +# Enable Livepatch +# +# end of Enable Livepatch + # # Kernel Features # @@ -327,31 +340,33 @@ CONFIG_CAVIUM_ERRATUM_30115=y # CONFIG_CAVIUM_TX2_ERRATUM_219 is not set # CONFIG_FUJITSU_ERRATUM_010001 is not set CONFIG_HISILICON_ERRATUM_161600802=y +# CONFIG_HISILICON_ERRATUM_1980005 is not set CONFIG_QCOM_FALKOR_ERRATUM_1003=y CONFIG_QCOM_FALKOR_ERRATUM_1009=y CONFIG_QCOM_QDF2400_ERRATUM_0065=y CONFIG_QCOM_FALKOR_ERRATUM_E1041=y CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_HISILICON_ERRATUM_HIP08_RU_PREFETCH=y +# CONFIG_HISILICON_HIP08_RU_PREFETCH_DEFAULT_OFF is not set # end of ARM errata workarounds via the alternatives framework -# CONFIG_ARM64_4K_PAGES is not set +CONFIG_ARM64_4K_PAGES=y # CONFIG_ARM64_16K_PAGES is not set -CONFIG_ARM64_64K_PAGES=y -# CONFIG_ARM64_VA_BITS_42 is not set +# CONFIG_ARM64_64K_PAGES is not set +# CONFIG_ARM64_VA_BITS_39 is not set CONFIG_ARM64_VA_BITS_48=y -# CONFIG_ARM64_VA_BITS_52 is not set CONFIG_ARM64_VA_BITS=48 CONFIG_ARM64_PA_BITS_48=y -# CONFIG_ARM64_PA_BITS_52 is not set CONFIG_ARM64_PA_BITS=48 # CONFIG_CPU_BIG_ENDIAN is not set CONFIG_CPU_LITTLE_ENDIAN=y CONFIG_SCHED_MC=y +# CONFIG_SCHED_CLUSTER is not set CONFIG_SCHED_SMT=y CONFIG_NR_CPUS=255 CONFIG_HOTPLUG_CPU=y +# CONFIG_ARM64_BOOTPARAM_HOTPLUG_CPU0 is not set # CONFIG_NUMA is not set -CONFIG_HOLES_IN_ZONE=y # CONFIG_HZ_100 is not set # CONFIG_HZ_250 is not set # CONFIG_HZ_300 is not set @@ -365,7 +380,10 @@ CONFIG_ARCH_SELECT_MEMORY_MODEL=y CONFIG_ARCH_FLATMEM_ENABLE=y CONFIG_HAVE_ARCH_PFN_VALID=y CONFIG_SYS_SUPPORTS_HUGETLBFS=y +CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_ARCH_LLC_128_LINE_SIZE=y +CONFIG_ARCH_HAS_FILTER_PGPROT=y CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y CONFIG_PARAVIRT=y # CONFIG_PARAVIRT_SPINLOCKS is not set @@ -375,10 +393,12 @@ CONFIG_PARAVIRT=y # CONFIG_XEN is not set CONFIG_FORCE_MAX_ZONEORDER=11 CONFIG_UNMAP_KERNEL_AT_EL0=y +CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y # CONFIG_RODATA_FULL_DEFAULT_ENABLED is not set -# CONFIG_ARM64_PMEM_RESERVE is not set +# CONFIG_ARM64_PMEM_LEGACY is not set # CONFIG_ARM64_SW_TTBR0_PAN is not set # CONFIG_ARM64_TAGGED_ADDR_ABI is not set +# CONFIG_AARCH32_EL0 is not set # # ARMv8.1 architectural features @@ -392,7 +412,6 @@ CONFIG_ARM64_VHE=y # # ARMv8.2 architectural features # -CONFIG_ARM64_UAO=y # CONFIG_ARM64_PMEM is not set CONFIG_ARM64_RAS_EXTN=y CONFIG_ARM64_CNP=y @@ -426,11 +445,24 @@ CONFIG_ARCH_RANDOM=y CONFIG_ARM64_AS_HAS_MTE=y # end of ARMv8.5 architectural features +# +# ARMv8.6 architectural features +# +CONFIG_ARM64_TWED=y +# end of ARMv8.6 architectural features + +# +# ARMv8.7 architectural features +# +CONFIG_ARM64_EPAN=y +# end of ARMv8.7 architectural features + CONFIG_ARM64_SVE=y # CONFIG_ARM64_PSEUDO_NMI is not set CONFIG_RELOCATABLE=y # CONFIG_RANDOMIZE_BASE is not set CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y +# CONFIG_ASCEND_FEATURES is not set # end of Kernel Features # @@ -470,6 +502,7 @@ CONFIG_ARCH_SUSPEND_POSSIBLE=y # # Firmware Drivers # +# CONFIG_ARM_SCMI_PROTOCOL is not set # CONFIG_ARM_SDE_INTERFACE is not set # CONFIG_GOOGLE_FIRMWARE is not set @@ -477,6 +510,7 @@ CONFIG_ARCH_SUSPEND_POSSIBLE=y # EFI (Extensible Firmware Interface) Support # CONFIG_EFI_ESRT=y +# CONFIG_EFI_FAKE_MEMMAP is not set CONFIG_EFI_PARAMS_FROM_FDT=y CONFIG_EFI_RUNTIME_WRAPPERS=y CONFIG_EFI_GENERIC_STUB=y @@ -509,7 +543,6 @@ CONFIG_ARCH_SUPPORTS_ACPI=y # # General architecture-dependent options # -CONFIG_SET_FS=y # CONFIG_JUMP_LABEL is not set CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y CONFIG_HAVE_KPROBES=y @@ -543,19 +576,22 @@ CONFIG_HAVE_ARCH_SECCOMP=y CONFIG_HAVE_ARCH_SECCOMP_FILTER=y CONFIG_SECCOMP=y CONFIG_SECCOMP_FILTER=y +# CONFIG_SECCOMP_CACHE_DEBUG is not set CONFIG_HAVE_ARCH_STACKLEAK=y CONFIG_HAVE_STACKPROTECTOR=y # CONFIG_STACKPROTECTOR is not set CONFIG_HAVE_CONTEXT_TRACKING=y CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOVE_PUD=y CONFIG_HAVE_MOVE_PMD=y CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y CONFIG_HAVE_ARCH_HUGE_VMAP=y +CONFIG_HAVE_ARCH_HUGE_VMALLOC=y CONFIG_MODULES_USE_ELF_RELA=y CONFIG_ARCH_HAS_ELF_RANDOMIZE=y CONFIG_HAVE_ARCH_MMAP_RND_BITS=y -CONFIG_ARCH_MMAP_RND_BITS=14 +CONFIG_ARCH_MMAP_RND_BITS=18 CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y CONFIG_CLONE_BACKWARDS=y # CONFIG_COMPAT_32BIT_TIME is not set @@ -704,6 +740,7 @@ CONFIG_SPARSEMEM_EXTREME=y CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y CONFIG_SPARSEMEM_VMEMMAP=y CONFIG_HAVE_FAST_GUP=y +CONFIG_HOLES_IN_ZONE=y CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_MEMORY_ISOLATION=y CONFIG_MEMORY_HOTPLUG=y @@ -714,6 +751,7 @@ CONFIG_MEMORY_BALLOON=y # CONFIG_COMPACTION is not set CONFIG_PAGE_REPORTING=y # CONFIG_MIGRATION is not set +# CONFIG_HUGE_VMALLOC_DEFAULT_ENABLED is not set CONFIG_PHYS_ADDR_T_64BIT=y # CONFIG_BOUNCE is not set # CONFIG_KSM is not set @@ -723,6 +761,10 @@ CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y # CONFIG_TRANSPARENT_HUGEPAGE is not set # CONFIG_CLEANCACHE is not set # CONFIG_FRONTSWAP is not set +# CONFIG_SHRINK_PAGECACHE is not set +CONFIG_MEMCG_QOS=y +# CONFIG_ETMEM_SCAN is not set +# CONFIG_ETMEM_SWAP is not set # CONFIG_CMA is not set # CONFIG_ZPOOL is not set # CONFIG_ZBUD is not set @@ -735,6 +777,12 @@ CONFIG_ARCH_HAS_PTE_DEVMAP=y # CONFIG_GUP_BENCHMARK is not set CONFIG_ARCH_HAS_PTE_SPECIAL=y # CONFIG_PIN_MEMORY is not set + +# +# Data Access Monitoring +# +# CONFIG_DAMON is not set +# end of Data Access Monitoring # end of Memory Management options CONFIG_NET=y @@ -1279,7 +1327,6 @@ CONFIG_OF_IRQ=y CONFIG_OF_NET=y CONFIG_OF_RESERVED_MEM=y # CONFIG_OF_OVERLAY is not set -# CONFIG_OF_CONFIGFS is not set # CONFIG_PARPORT is not set CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_NULL_BLK is not set @@ -1296,6 +1343,7 @@ CONFIG_VIRTIO_BLK=y # NVME Support # # CONFIG_NVME_FC is not set +# CONFIG_NVME_TCP is not set # CONFIG_NVME_TARGET is not set # end of NVME Support @@ -1418,7 +1466,6 @@ CONFIG_VIRTIO_NET=y # CONFIG_WAN is not set CONFIG_NET_FAILOVER=y # CONFIG_ISDN is not set -# CONFIG_NVM is not set # # Input device support @@ -1459,9 +1506,6 @@ CONFIG_INPUT=y # # Character devices # -# CONFIG_BRCM_CHAR_DRIVERS is not set -CONFIG_BCM2835_DEVGPIOMEM=y -# CONFIG_RPIVID_MEM is not set CONFIG_TTY=y CONFIG_VT=y CONFIG_CONSOLE_TRANSLATIONS=y @@ -1749,7 +1793,6 @@ CONFIG_DMA_OF=y # DMABUF options # # CONFIG_SYNC_FILE is not set -# CONFIG_DMABUF_MOVE_NOTIFY is not set # CONFIG_DMABUF_HEAPS is not set # end of DMABUF options @@ -1783,7 +1826,6 @@ CONFIG_COMMON_CLK=y # CONFIG_CLK_QORIQ is not set # CONFIG_COMMON_CLK_XGENE is not set # CONFIG_COMMON_CLK_FIXED_MMIO is not set -# CONFIG_MCHP_CLK_PFSOC is not set # CONFIG_HWSPINLOCK is not set # @@ -1833,7 +1875,6 @@ CONFIG_ARM_ARCH_TIMER=y # # Broadcom SoC drivers # -# CONFIG_OPENEULER_RASPBERRYPI is not set # CONFIG_SOC_BRCMSTB is not set # end of Broadcom SoC drivers @@ -1920,12 +1961,15 @@ CONFIG_PARTITION_PERCPU=y # CONFIG_ANDROID is not set # end of Android +# +# Vendor Hooks +# +# end of Vendor Hooks + # CONFIG_LIBNVDIMM is not set -# CONFIG_PMEM_LEGACY is not set # CONFIG_DAX is not set CONFIG_NVMEM=y # CONFIG_NVMEM_SYSFS is not set -# CONFIG_NVMEM_RMEM is not set # # HW tracing support @@ -2008,6 +2052,7 @@ CONFIG_VIRTIO_FS=y # CONFIG_VFAT_FS is not set # CONFIG_EXFAT_FS is not set # CONFIG_NTFS_FS is not set +# CONFIG_NTFS3_FS is not set # end of DOS/FAT/EXFAT/NT Filesystems # @@ -2026,6 +2071,8 @@ CONFIG_TMPFS_XATTR=y # CONFIG_TMPFS_INODE64 is not set CONFIG_HUGETLBFS=y CONFIG_HUGETLB_PAGE=y +CONFIG_HUGETLB_PAGE_FREE_VMEMMAP=y +# CONFIG_HUGETLB_PAGE_FREE_VMEMMAP_DEFAULT_ON is not set CONFIG_MEMFD_CREATE=y CONFIG_ARCH_HAS_GIGANTIC_PAGE=y CONFIG_CONFIGFS_FS=y @@ -2124,6 +2171,7 @@ CONFIG_CRYPTO_ACOMP2=y # CONFIG_CRYPTO_RSA is not set # CONFIG_CRYPTO_DH is not set # CONFIG_CRYPTO_ECDH is not set +# CONFIG_CRYPTO_ECDSA is not set # CONFIG_CRYPTO_ECRDSA is not set # CONFIG_CRYPTO_SM2 is not set # CONFIG_CRYPTO_CURVE25519 is not set @@ -2319,8 +2367,6 @@ CONFIG_FONT_8x16=y CONFIG_FONT_AUTOSELECT=y CONFIG_ARCH_STACKWALK=y CONFIG_SBITMAP=y -# CONFIG_ETMEM_SCAN is not set -# CONFIG_ETMEM_SWAP is not set # CONFIG_STRING_SELFTEST is not set # end of Library routines @@ -2349,6 +2395,7 @@ CONFIG_DEBUG_BUGVERBOSE=y CONFIG_FRAME_WARN=2048 # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_HEADERS_INSTALL is not set +# CONFIG_OPTIMIZE_INLINING is not set # CONFIG_DEBUG_SECTION_MISMATCH is not set # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set CONFIG_ARCH_WANT_FRAME_POINTERS=y @@ -2386,6 +2433,8 @@ CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y CONFIG_CC_HAS_KASAN_GENERIC=y CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y # CONFIG_KASAN is not set +CONFIG_HAVE_ARCH_KFENCE=y +# CONFIG_KFENCE is not set # end of Memory Debugging # -- Gitee From 8bcf5ae638100da95b3b0f09592d383888ddf6cf Mon Sep 17 00:00:00 2001 From: ace-yan Date: Fri, 19 Aug 2022 22:14:06 +0800 Subject: [PATCH 0111/2187] Replace the ifconfig command with the ip command. ifconfig has been largely replaced by the ip command. The ip command has become the default network command tool for mainstream Linux systems. Signed-off-by: Yan Wen --- docs/config_guidebook.md | 10 ++++++---- tests/hydropper/README.cn.md | 7 ++++--- tests/hydropper/README.md | 7 ++++--- tests/hydropper/utils/resources.py | 2 +- tests/hydropper/virt/basevm.py | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2975fc15d..8a7dd84b4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -350,8 +350,9 @@ hugepages('-mem-path ...' ) when using vhost-user net. $ brctl addbr qbr0 $ ip tuntap add tap0 mode tap $ brctl addif qbr0 tap0 -$ ifconfig qbr0 up; ifconfig tap0 up -$ ifconfig qbr0 1.1.1.1 +$ ip link set qbr0 up +$ ip link set tap0 up +$ ip address add 1.1.1.1 dev qbr0 # Run StratoVirt ... -netdev tap,id=netdevid,ifname=tap0 ... @@ -370,8 +371,9 @@ note: If you want to use multiple queues, create a tap device as follows: $ brctl addbr qbr0 $ ip tuntap add tap1 mode tap multi_queue $ brctl addif qbr0 tap1 -$ ifconfig qbr0 up; ifconfig tap1 up -$ ifconfig qbr0 1.1.1.1 +$ ip link set qbr0 up +$ ip link set tap1 up +$ ip address add 1.1.1.1 dev qbr0 ``` *How to create port by ovs-dpdk?* diff --git a/tests/hydropper/README.cn.md b/tests/hydropper/README.cn.md index 5e359b3c2..2a8d872f0 100644 --- a/tests/hydropper/README.cn.md +++ b/tests/hydropper/README.cn.md @@ -22,14 +22,15 @@ $ pip3 install -r requirements.txt ```sh $ yum install nmap $ yum install iperf3 +$ yum install bridge-utils ``` 4. 网络配置(可参考以下模板): ```sh brctl addbr strato_br0 -ifconfig strato_br0 up -ifconfig strato_br0 1.1.1.1 +ip link set strato_br0 up +ip address add 1.1.1.1 dev strato_br0 ``` 5. 构建测试镜像请参考 docs/IMAGE_BUILD.md。 @@ -113,4 +114,4 @@ def test_microvm_xxx(microvm): ### 日志 - pytest默认日志路径:/var/log/pytest.log -- stratovirt默认日志路径:/var/log/stratovirt \ No newline at end of file +- stratovirt默认日志路径:/var/log/stratovirt diff --git a/tests/hydropper/README.md b/tests/hydropper/README.md index 5475ab289..47e453e73 100644 --- a/tests/hydropper/README.md +++ b/tests/hydropper/README.md @@ -22,14 +22,15 @@ $ pip3 install -r requirements.txt ```sh $ yum install nmap $ yum install iperf3 +$ yum install bridge-utils ``` 4. Network configuration(template) ```sh brctl addbr strato_br0 -ifconfig strato_br0 up -ifconfig strato_br0 1.1.1.1 +ip link set strato_br0 up +ip address add 1.1.1.1 dev strato_br0 ``` 5. For details about how to build a test image, see docs/IMAGE_BUILD.md. @@ -115,4 +116,4 @@ def test_microvm_xxx(microvm): ### Log - pytest default log path: /var/log/pytest.log -- stratovirt default log path: /var/log/stratovirt \ No newline at end of file +- stratovirt default log path: /var/log/stratovirt diff --git a/tests/hydropper/utils/resources.py b/tests/hydropper/utils/resources.py index b8e168030..77f5e502a 100644 --- a/tests/hydropper/utils/resources.py +++ b/tests/hydropper/utils/resources.py @@ -47,7 +47,7 @@ class NetworkResource(Singleton): # create bridge if it does not exist run("brctl show %s || brctl addbr %s" % (self.bridge, self.bridge), shell=True, check=True) - run("ifconfig %s up" % self.bridge, shell=True, check=True) + run("ip link set %s up" % self.bridge, shell=True, check=True) for index in range(self.nets_num): ipaddr = "%s.%s.1" % (self.ip_prefix, str(self.ip_3rd + index)) diff --git a/tests/hydropper/virt/basevm.py b/tests/hydropper/virt/basevm.py index 44f44bcc3..fb134fdbe 100644 --- a/tests/hydropper/virt/basevm.py +++ b/tests/hydropper/virt/basevm.py @@ -375,7 +375,7 @@ class BaseVM: self.serial_cmd("systemctl restart sshd") if 'dhcp' in model: self.serial_session.run_func("cmd_output", ("dhclient %s" % self.interfaces[index])) - _cmd = "ifconfig %s | awk '/inet/ {print $2}' | cut -f2 -d ':' | " \ + _cmd = "ip address show %s | awk '/inet/ {print $2}' | cut -f2 -d ':' | " \ "awk 'NR==1 {print $1}'" % self.interfaces[index] output = self.serial_session.run_func("cmd_output", _cmd) self.guest_ips.append(output) -- Gitee From 5bcc7c830198fe33d6d4e0a64cb7a92ec987f6b9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 14:21:31 +0800 Subject: [PATCH 0112/2187] usb: add usb and xhci controller basic structures Add xhci controller basic structures includes registers, slot context and so on. Add usb basic structures includes device and packet. Signed-off-by: zhouli57 --- Cargo.lock | 15 ++ machine/Cargo.toml | 1 + usb/Cargo.toml | 17 +++ usb/src/bus.rs | 43 ++++++ usb/src/config.rs | 226 +++++++++++++++++++++++++++++ usb/src/lib.rs | 35 +++++ usb/src/usb.rs | 249 ++++++++++++++++++++++++++++++++ usb/src/xhci/mod.rs | 15 ++ usb/src/xhci/xhci_controller.rs | 217 ++++++++++++++++++++++++++++ usb/src/xhci/xhci_regs.rs | 138 ++++++++++++++++++ usb/src/xhci/xhci_ring.rs | 213 +++++++++++++++++++++++++++ 11 files changed, 1169 insertions(+) create mode 100644 usb/Cargo.toml create mode 100644 usb/src/bus.rs create mode 100644 usb/src/config.rs create mode 100644 usb/src/lib.rs create mode 100644 usb/src/usb.rs create mode 100644 usb/src/xhci/mod.rs create mode 100644 usb/src/xhci/xhci_controller.rs create mode 100644 usb/src/xhci/xhci_regs.rs create mode 100644 usb/src/xhci/xhci_ring.rs diff --git a/Cargo.lock b/Cargo.lock index bd2ec69d7..6278b34a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,7 @@ dependencies = [ "serde", "serde_json", "sysbus", + "usb", "util", "vfio", "vfio-bindings", @@ -594,6 +595,20 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "usb" +version = "2.2.0" +dependencies = [ + "address_space", + "byteorder", + "error-chain", + "libc", + "log", + "once_cell", + "pci", + "util", +] + [[package]] name = "util" version = "2.2.0" diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 8e14324a7..8848a587f 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -30,6 +30,7 @@ sysbus = { path = "../sysbus" } util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } +usb = { path = "../usb" } [features] default = ["qmp"] diff --git a/usb/Cargo.toml b/usb/Cargo.toml new file mode 100644 index 000000000..fae115747 --- /dev/null +++ b/usb/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "usb" +version = "2.2.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "USB controller and device emulation" + +[dependencies] +byteorder = "1.3.4" +error-chain = "0.12.4" +libc = ">=0.2.71" +log = "0.4.8" +once_cell = "1.9.0" +address_space = { path = "../address_space" } +util = { path = "../util" } +pci = { path = "../pci" } diff --git a/usb/src/bus.rs b/usb/src/bus.rs new file mode 100644 index 000000000..b4d95348f --- /dev/null +++ b/usb/src/bus.rs @@ -0,0 +1,43 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::{HashMap, LinkedList}; +use std::sync::{Arc, Mutex}; + +use super::errors::Result; +use crate::usb::{UsbDeviceOps, UsbPort}; + +/// The key is bus name, the value is the device which can attach other devices. +pub type BusDeviceMap = Arc>>>>; + +/// USB bus used to manage USB ports. +#[derive(Default)] +pub struct UsbBus { + free_ports: LinkedList>>, + used_ports: LinkedList>>, +} + +impl UsbBus { + pub fn new() -> Self { + UsbBus { + free_ports: LinkedList::new(), + used_ports: LinkedList::new(), + } + } +} + +/// Bus device ops for USB controller to handle USB device attach/detach. +pub trait BusDeviceOps: Send + Sync { + fn attach_device(&mut self, dev: &Arc>) -> Result<()>; + + fn detach_device(&mut self, dev: &Arc>) -> Result<()>; +} diff --git a/usb/src/config.rs b/usb/src/config.rs new file mode 100644 index 000000000..6d47f74c1 --- /dev/null +++ b/usb/src/config.rs @@ -0,0 +1,226 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// USB Command +/// Run/Stop +pub const USB_CMD_RUN: u32 = 1 << 0; +/// Host Controller Reset +pub const USB_CMD_HCRST: u32 = 1 << 1; +/// Interrupter Enable +pub const USB_CMD_INTE: u32 = 1 << 2; +/// Host System Error Enable +pub const USB_CMD_HSEE: u32 = 1 << 3; +/// Light Host Controller Reset +pub const USB_CMD_LHCRST: u32 = 1 << 7; +/// Controller Save State +pub const USB_CMD_CSS: u32 = 1 << 8; +/// Controller Restore State +pub const USB_CMD_CRS: u32 = 1 << 9; +/// Enable Wrap Event +pub const USB_CMD_EWE: u32 = 1 << 10; +/// Enable U3 MFINDEX Stop +pub const USB_CMD_EU3S: u32 = 1 << 11; + +/// USB status +/// HC Halted +pub const USB_STS_HCH: u32 = 1 << 0; +/// Host System Error +pub const USB_STS_HSE: u32 = 1 << 2; +/// Event Interrupt +pub const USB_STS_EINT: u32 = 1 << 3; +/// Port Change Detect +pub const USB_STS_PCD: u32 = 1 << 4; +/// Save State Status +pub const USB_STS_SSS: u32 = 1 << 8; +/// Restore State Status +pub const USB_STS_RSS: u32 = 1 << 9; +/// Save/Restore Error +pub const USB_STS_SRE: u32 = 1 << 10; +/// Controller Not Ready +pub const USB_STS_CNR: u32 = 1 << 11; +/// Host Controller Error +pub const USB_STS_HCE: u32 = 1 << 12; + +/// Command Ring Control +/// Ring Cycle State +pub const CMD_RING_CTRL_RCS: u32 = 1 << 0; +/// Command Stop +pub const CMD_RING_CTRL_CS: u32 = 1 << 1; +/// Command Abort +pub const CMD_RING_CTRL_CA: u32 = 1 << 2; +/// Command Ring Running +pub const CMD_RING_CTRL_CRR: u32 = 1 << 3; +/// Interrupt Pending +pub const IMAN_IP: u32 = 1 << 0; +/// Interrupt Enable +pub const IMAN_IE: u32 = 1 << 1; +/// Event Handler Busy +pub const ERDP_EHB: u32 = 1 << 3; + +/// Port Status and Control Register +/// Current Connect Status +pub const PORTSC_CCS: u32 = 1 << 0; +/// Port Enabled/Disabled +pub const PORTSC_PED: u32 = 1 << 1; +/// Over-current Active +pub const PORTSC_OCA: u32 = 1 << 3; +/// Port Reset +pub const PORTSC_PR: u32 = 1 << 4; +/// Port Power +pub const PORTSC_PP: u32 = 1 << 9; +/// Port Speed +pub const PORTSC_SPEED_SHIFT: u32 = 10; +pub const PORTSC_SPEED_FULL: u32 = 1 << PORTSC_SPEED_SHIFT; +pub const PORTSC_SPEED_LOW: u32 = 2 << PORTSC_SPEED_SHIFT; +pub const PORTSC_SPEED_HIGH: u32 = 3 << PORTSC_SPEED_SHIFT; +pub const PORTSC_SPEED_SUPER: u32 = 4 << PORTSC_SPEED_SHIFT; +/// Port Indicator Control +pub const PORTSC_PLS_SHIFT: u32 = 5; +pub const PORTSC_PLS_MASK: u32 = 0xf; +/// Port Link State Write Strobe +pub const PORTSC_LWS: u32 = 1 << 16; +/// Connect Status Change +pub const PORTSC_CSC: u32 = 1 << 17; +/// Port Enabled/Disabled Change +pub const PORTSC_PEC: u32 = 1 << 18; +/// Warm Port Reset Change +pub const PORTSC_WRC: u32 = 1 << 19; +/// Over-current Change +pub const PORTSC_OCC: u32 = 1 << 20; +/// Port Reset Change +pub const PORTSC_PRC: u32 = 1 << 21; +/// Port Link State Change +pub const PORTSC_PLC: u32 = 1 << 22; +/// Port Config Error Change +pub const PORTSC_CEC: u32 = 1 << 23; +/// Cold Attach Status +pub const PORTSC_CAS: u32 = 1 << 24; +/// Wake on Connect Enable +pub const PORTSC_WCE: u32 = 1 << 25; +/// Wake on Disconnect Enable +pub const PORTSC_WDE: u32 = 1 << 26; +/// Wake on Over-current Enable +pub const PORTSC_WOE: u32 = 1 << 27; +/// Device Removable +pub const PORTSC_DR: u32 = 1 << 30; +/// Warm Port Reset +pub const PORTSC_WPR: u32 = 1 << 31; +/// Port Link State +pub const PLS_U0: u32 = 0; +pub const PLS_U1: u32 = 1; +pub const PLS_U2: u32 = 2; +pub const PLS_U3: u32 = 3; +pub const PLS_DISABLED: u32 = 4; +pub const PLS_RX_DETECT: u32 = 5; +pub const PLS_INACTIVE: u32 = 6; +pub const PLS_POLLING: u32 = 7; +pub const PLS_RECOVERY: u32 = 8; +pub const PLS_HOT_RESET: u32 = 9; +pub const PLS_COMPILANCE_MODE: u32 = 10; +pub const PLS_TEST_MODE: u32 = 11; +pub const PLS_RESUME: u32 = 15; + +/// USB speed +pub const USB_SPEED_LOW: u32 = 0; +pub const USB_SPEED_FULL: u32 = 1; +pub const USB_SPEED_HIGH: u32 = 2; +pub const USB_SPEED_SUPER: u32 = 3; +pub const USB_SPEED_MASK_LOW: u32 = 1 << USB_SPEED_LOW; +pub const USB_SPEED_MASK_FULL: u32 = 1 << USB_SPEED_FULL; +pub const USB_SPEED_MASK_HIGH: u32 = 1 << USB_SPEED_HIGH; +pub const USB_SPEED_MASK_SUPER: u32 = 1 << USB_SPEED_SUPER; + +/// See the spec section 8.3.1 Packet Identifier Field. +pub const USB_TOKEN_SETUP: u8 = 0x2d; +pub const USB_TOKEN_IN: u8 = 0x69; +pub const USB_TOKEN_OUT: u8 = 0xe1; + +/// See the spec section 9.3 USB Device Requests. Setup Data. +pub const USB_DIRECTION_HOST_TO_DEVICE: u8 = 0 << 7; +pub const USB_DIRECTION_DEVICE_TO_HOST: u8 = 0x80; +pub const USB_TYPE_STANDARD: u8 = 0x00 << 5; +pub const USB_TYPE_CLASS: u8 = 1 << 5; +pub const USB_TYPE_VENDOR: u8 = 2 << 5; +pub const USB_TYPE_RESERVED: u8 = 3 << 5; +pub const USB_RECIPIENT_DEVICE: u8 = 0; +pub const USB_RECIPIENT_INTERFACE: u8 = 1; +pub const USB_RECIPIENT_ENDPOINT: u8 = 2; +pub const USB_RECIPIENT_OTHER: u8 = 3; + +/// USB device request combination +pub const USB_DEVICE_IN_REQUEST: u8 = + USB_DIRECTION_DEVICE_TO_HOST | USB_TYPE_STANDARD | USB_RECIPIENT_DEVICE; +pub const USB_DEVICE_OUT_REQUEST: u8 = + USB_DIRECTION_HOST_TO_DEVICE | USB_TYPE_STANDARD | USB_RECIPIENT_DEVICE; +pub const USB_INTERFACE_IN_REQUEST: u8 = + USB_DIRECTION_DEVICE_TO_HOST | USB_TYPE_STANDARD | USB_RECIPIENT_INTERFACE; +pub const USB_INTERFACE_OUT_REQUEST: u8 = + USB_DIRECTION_HOST_TO_DEVICE | USB_TYPE_STANDARD | USB_RECIPIENT_INTERFACE; +pub const USB_INTERFACE_CLASS_IN_REQUEST: u8 = + USB_DIRECTION_DEVICE_TO_HOST | USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE; +pub const USB_INTERFACE_CLASS_OUT_REQUEST: u8 = + USB_DIRECTION_HOST_TO_DEVICE | USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE; + +/// USB Standard Request Code. 9.4 Standard Device Requests +pub const USB_REQUEST_GET_STATUS: u8 = 0; +pub const USB_REQUEST_CLEAR_FEATURE: u8 = 1; +pub const USB_REQUEST_SET_FEATURE: u8 = 3; +pub const USB_REQUEST_SET_ADDRESS: u8 = 5; +pub const USB_REQUEST_GET_DESCRIPTOR: u8 = 6; +pub const USB_REQUEST_SET_DESCRIPTOR: u8 = 7; +pub const USB_REQUEST_GET_CONFIGURATION: u8 = 8; +pub const USB_REQUEST_SET_CONFIGURATION: u8 = 9; +pub const USB_REQUEST_GET_INTERFACE: u8 = 10; +pub const USB_REQUEST_SET_INTERFACE: u8 = 11; +pub const USB_REQUEST_SYNCH_FRAME: u8 = 12; +pub const USB_REQUEST_SET_SEL: u8 = 48; +pub const USB_REQUEST_SET_ISOCH_DELAY: u8 = 49; + +/// See the spec section 9.4.5 Get Status +pub const USB_DEVICE_SELF_POWERED: u32 = 0; +pub const USB_DEVICE_REMOTE_WAKEUP: u32 = 1; + +/// USB Descriptor Type +pub const USB_DT_DEVICE: u8 = 1; +pub const USB_DT_CONFIGURATION: u8 = 2; +pub const USB_DT_STRING: u8 = 3; +pub const USB_DT_INTERFACE: u8 = 4; +pub const USB_DT_ENDPOINT: u8 = 5; +pub const USB_DT_INTERFACE_POWER: u8 = 8; +pub const USB_DT_OTG: u8 = 9; +pub const USB_DT_DEBUG: u8 = 10; +pub const USB_DT_INTERFACE_ASSOCIATION: u8 = 11; +pub const USB_DT_BOS: u8 = 15; +pub const USB_DT_DEVICE_CAPABILITY: u8 = 16; +pub const USB_DT_ENDPOINT_COMPANION: u8 = 48; + +/// USB Descriptor size +pub const USB_DT_DEVICE_SIZE: u8 = 18; +pub const USB_DT_CONFIG_SIZE: u8 = 9; +pub const USB_DT_INTERFACE_SIZE: u8 = 9; +pub const USB_DT_ENDPOINT_SIZE: u8 = 7; + +/// USB Endpoint Descriptor +pub const USB_ENDPOINT_ATTR_CONTROL: u8 = 0; +pub const USB_ENDPOINT_ATTR_ISOC: u8 = 1; +pub const USB_ENDPOINT_ATTR_BULK: u8 = 2; +pub const USB_ENDPOINT_ATTR_INT: u8 = 3; +pub const USB_ENDPOINT_ATTR_INVALID: u8 = 255; +pub const USB_INTERFACE_INVALID: u8 = 255; + +/// See the spec section 9.6.3 Configuration. Standard Configuration Descriptor. +pub const USB_CONFIGURATION_ATTR_ONE: u8 = 1 << 7; +pub const USB_CONFIGURATION_ATTR_SELF_POWER: u8 = 1 << 6; +pub const USB_CONFIGURATION_ATTR_REMOTE_WAKEUP: u8 = 1 << 5; + +// USB Class +pub const USB_CLASS_HID: u8 = 3; diff --git a/usb/src/lib.rs b/usb/src/lib.rs new file mode 100644 index 000000000..bd3732751 --- /dev/null +++ b/usb/src/lib.rs @@ -0,0 +1,35 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + +pub mod errors { + error_chain! { + links { + PciErr(pci::errors::Error, pci::errors::ErrorKind); + AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); + } + foreign_links { + Io(std::io::Error); + } + errors { + } + } +} + +pub mod bus; +pub mod config; +pub mod usb; +pub mod xhci; diff --git a/usb/src/usb.rs b/usb/src/usb.rs new file mode 100644 index 000000000..de8b3f9f1 --- /dev/null +++ b/usb/src/usb.rs @@ -0,0 +1,249 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + collections::LinkedList, + sync::{Arc, Mutex, Weak}, +}; + +use super::errors::Result; +use crate::xhci::xhci_controller::XhciDevice; + +/// USB packet return status. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum UsbPacketStatus { + Success, + NoDev, + Nak, + Stall, + Babble, + IoError, + Async, +} + +/// USB packet setup state. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SetupState { + Idle, + Setup, + Data, + Ack, + Parameter, +} + +/// USB device state. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum UsbDeviceState { + Removed, + Attached, + Powered, + Default, + Address, + Configured, + Suspended, +} + +/// USB request used to transfer to USB device. +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct UsbDeviceRequest { + pub request_type: u8, + pub request: u8, + pub value: u16, + pub index: u16, + pub length: u16, +} + +/// The data transmission channel. +#[derive(Default)] +pub struct UsbEndpoint { + pub nr: u8, + pub pid: u8, + pub usb_type: u8, + pub ifnum: u8, + pub max_packet_size: u32, + pub pipeline: bool, + pub halted: bool, + pub dev: Option>>, + pub queue: LinkedList, +} + +impl UsbEndpoint { + pub fn new(nr: u8, pid: u8, usb_type: u8, ifnum: u8, max_packet_size: u32) -> Self { + Self { + nr, + pid, + usb_type, + ifnum, + max_packet_size, + pipeline: false, + halted: false, + dev: None, + queue: LinkedList::new(), + } + } +} + +/// USB port which can attached device. +pub struct UsbPort { + pub dev: Option>>, + pub speed_mask: u32, + pub path: String, + pub index: u32, +} + +impl UsbPort { + pub fn new(index: u32) -> Self { + Self { + dev: None, + speed_mask: 0, + path: String::new(), + index, + } + } +} + +/// USB descriptor strings. +pub struct UsbDescString { + pub index: u32, + pub str: String, +} + +/// USB packet state. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum UsbPacketState { + Undefined = 0, + Setup, + Queued, + Async, + Complete, + Canceled, +} + +/// USB device common structure. +pub struct UsbDevice { + pub port: Option>>, + pub speed: u32, + pub speed_mask: u32, + pub addr: u8, + pub product_desc: String, + pub auto_attach: bool, + pub attached: bool, + pub state: UsbDeviceState, + pub setup_buf: Vec, + pub data_buf: Vec, + pub remote_wakeup: u32, + pub setup_state: SetupState, + pub setup_len: u32, + pub setup_index: u32, + pub ep_ctl: Arc>, + pub ep_in: Vec>>, + pub ep_out: Vec>>, +} + +/// UsbDeviceOps is the interface for USB device. +/// Include device handle attach/detach and the transfer between controller and device. +pub trait UsbDeviceOps: Send + Sync { + /// Handle the attach ops when attach device to controller. + fn handle_attach(&mut self) -> Result<()>; + + /// Reset the USB device. + fn reset(&mut self); + + /// Set the controller which the USB device attached. + /// USB deivce need to kick controller in some cases. + fn set_controller(&mut self, ctrl: Weak>); + + /// Set the attached USB port. + fn set_usb_port(&mut self, port: Option>>); + + /// Handle usb packet, used for controller to deliever packet to device. + fn handle_packet(&mut self, packet: &mut UsbPacket); + + /// Handle control pakcet. + fn handle_control( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ); + + /// Handle data pakcet. + fn handle_data(&mut self, packet: &mut UsbPacket); + + /// Unique device id. + fn device_id(&self) -> String; + + /// Get the UsbDevice. + fn get_usb_device(&self) -> Arc>; + + /// Get the mut UsbDevice. + fn get_mut_usb_device(&mut self) -> Arc>; + + /// Get the device speed. + fn speed(&self) -> u32; + + /// If USB device is attached. + fn attached(&self) -> bool; +} + +/// Io vector which save the hva. +#[derive(Debug, Copy, Clone)] +pub struct Iovec { + pub iov_base: u64, + pub iov_len: usize, +} + +impl Iovec { + pub fn new(base: u64, len: usize) -> Self { + Iovec { + iov_base: base, + iov_len: len, + } + } +} + +/// Usb packet used for device transfer data. +#[derive(Clone)] +pub struct UsbPacket { + /// USB packet id. + pub pid: u32, + pub id: u64, + pub ep: Option>>, + pub iovecs: Vec, + /// control transfer + pub parameter: u64, + pub short_not_ok: bool, + pub int_req: bool, + /// USB packet return status + pub status: UsbPacketStatus, + /// Actually transfer length + pub actual_length: u32, + pub state: UsbPacketState, +} + +impl Default for UsbPacket { + fn default() -> UsbPacket { + UsbPacket { + pid: 0, + id: 0, + ep: None, + iovecs: Vec::new(), + parameter: 0, + short_not_ok: false, + int_req: false, + status: UsbPacketStatus::NoDev, + actual_length: 0, + state: UsbPacketState::Undefined, + } + } +} diff --git a/usb/src/xhci/mod.rs b/usb/src/xhci/mod.rs new file mode 100644 index 000000000..62f752ac5 --- /dev/null +++ b/usb/src/xhci/mod.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod xhci_controller; +pub mod xhci_regs; +mod xhci_ring; diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs new file mode 100644 index 000000000..d657fdaa9 --- /dev/null +++ b/usb/src/xhci/xhci_controller.rs @@ -0,0 +1,217 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex, Weak}; + +use address_space::AddressSpace; + +use crate::bus::UsbBus; +use crate::config::*; +use crate::usb::{UsbPacket, UsbPort}; +use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter, XhciPort}; +use crate::xhci::xhci_ring::{TRBCCode, TRBType, XhciRing, XhciTRB}; + +pub const MAX_INTRS: u16 = 16; +pub const MAX_SLOTS: u32 = 64; + +type DmaAddr = u64; + +/// Transfer data between controller and device. +#[derive(Clone)] +pub struct XhciTransfer { + packet: UsbPacket, + status: TRBCCode, + trbs: Vec, + complete: bool, + slotid: u32, + epid: u32, + in_xfer: bool, + int_req: bool, + running_retry: bool, +} + +impl XhciTransfer { + fn new(len: u32) -> Self { + XhciTransfer { + packet: UsbPacket::default(), + status: TRBCCode::Invalid, + trbs: vec![XhciTRB::new(); len as usize], + complete: false, + slotid: 0, + epid: 0, + in_xfer: false, + int_req: false, + running_retry: false, + } + } +} + +/// Endpoint context which use the ring to transfer data. +#[derive(Clone)] +pub struct XhciEpContext { + epid: u32, + enabled: bool, + ring: XhciRing, + ep_type: EpType, + pctx: DmaAddr, + max_psize: u32, + state: u32, + /// Line Stream Array + lsa: bool, + interval: u32, + transfers: Vec, + retry: Option, +} + +impl XhciEpContext { + pub fn new(mem: &Arc, epid: u32) -> Self { + Self { + epid, + enabled: false, + ring: XhciRing::new(mem), + ep_type: EpType::Invalid, + pctx: 0, + max_psize: 0, + state: 0, + lsa: false, + interval: 0, + transfers: Vec::new(), + retry: None, + } + } +} + +/// Endpoint type, including control, bulk, interrupt and isochronous. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum EpType { + Invalid = 0, + IsoOut, + BulkOut, + IntrOut, + Control, + IsoIn, + BulkIn, + IntrIn, +} + +impl From for EpType { + fn from(t: u32) -> EpType { + match t { + 0 => EpType::Invalid, + 1 => EpType::IsoOut, + 2 => EpType::BulkOut, + 3 => EpType::IntrOut, + 4 => EpType::Control, + 5 => EpType::IsoIn, + 6 => EpType::BulkIn, + 7 => EpType::IntrIn, + _ => EpType::Invalid, + } + } +} + +/// Device slot, mainly including some endpoint. +#[derive(Clone)] +pub struct XhciSlot { + pub enabled: bool, + pub addressed: bool, + pub intr: u16, + pub ctx: u64, + pub usb_port: Option>>, + pub endpoints: Vec, +} + +impl XhciSlot { + pub fn new(mem: &Arc) -> Self { + XhciSlot { + enabled: false, + addressed: false, + intr: 0, + ctx: 0, + usb_port: None, + endpoints: vec![XhciEpContext::new(mem, 0); 31], + } + } +} + +/// Event usually send to drivers. +#[derive(Debug)] +pub struct XhciEvent { + pub trb_type: TRBType, + pub ccode: TRBCCode, + pub ptr: u64, + pub length: u32, + flags: u32, + slot_id: u8, + ep_id: u8, +} + +impl XhciEvent { + pub fn new(trb_type: TRBType, ccode: TRBCCode) -> Self { + Self { + trb_type, + ccode, + ptr: 0, + length: 0, + slot_id: 0, + flags: 0, + ep_id: 0, + } + } +} + +/// Controller ops registered in XhciDevice. Such as PCI device send MSIX. +pub trait XhciOps: Send + Sync { + fn trigger_intr(&mut self, n: u32, level: bool) -> bool; + + fn update_intr(&mut self, n: u32, enable: bool); +} + +/// Xhci controller device. +pub struct XhciDevice { + pub numports_2: u32, + pub numports_3: u32, + pub oper: XchiOperReg, + pub usb_ports: Vec>>, + pub ports: Vec>>, + pub port_num: u32, + pub slots: Vec, + pub intrs: Vec, + pub cmd_ring: XhciRing, + mem_space: Arc, + pub bus: Arc>, + pub ctrl_ops: Option>>, +} + +impl XhciDevice { + pub fn new(mem_space: &Arc) -> Arc> { + let xhci = XhciDevice { + oper: XchiOperReg::new(), + ctrl_ops: None, + usb_ports: Vec::new(), + numports_2: 2, + numports_3: 2, + port_num: 4, + ports: Vec::new(), + slots: vec![XhciSlot::new(mem_space); MAX_SLOTS as usize], + intrs: vec![XhciInterrupter::new(mem_space); 1], + cmd_ring: XhciRing::new(mem_space), + mem_space: mem_space.clone(), + bus: Arc::new(Mutex::new(UsbBus::new())), + }; + let xhci = Arc::new(Mutex::new(xhci)); + let clone_xhci = xhci.clone(); + let mut locked_xhci = clone_xhci.lock().unwrap(); + locked_xhci.oper.usb_status = USB_STS_HCH; + xhci + } +} diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs new file mode 100644 index 000000000..a282ab798 --- /dev/null +++ b/usb/src/xhci/xhci_regs.rs @@ -0,0 +1,138 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::config::*; +use std::sync::{Arc, Mutex, Weak}; + +use address_space::AddressSpace; + +use crate::usb::UsbPort; +use crate::xhci::xhci_controller::XhciDevice; + +/// Capability offset or size. +pub const XHCI_CAP_LENGTH: u32 = 0x40; +pub const XHCI_OFF_DOORBELL: u32 = 0x2000; +pub const XHCI_OFF_RUNTIME: u32 = 0x1000; + +/// XHCI Operation Registers +#[derive(Default, Copy, Clone)] +pub struct XchiOperReg { + /// USB Command + pub usb_cmd: u32, + /// USB Status + pub usb_status: u32, + /// Device Notify Control + pub dev_notify_ctrl: u32, + /// Command Ring Control + pub cmd_ring_ctrl: u64, + /// Device Context Base Address Array Pointer + pub dcbaap: u64, + /// Configure + pub config: u32, +} + +impl XchiOperReg { + pub fn new() -> Self { + Self { + usb_cmd: 0, + usb_status: 0, + dev_notify_ctrl: 0, + cmd_ring_ctrl: 0, + dcbaap: 0, + config: 0, + } + } + + pub fn reset(&mut self) { + self.usb_cmd = 0; + self.usb_status = USB_STS_HCH; + self.dev_notify_ctrl = 0; + self.cmd_ring_ctrl = 0; + self.dcbaap = 0; + self.config = 0; + } +} + +/// XHCI Interrupter +#[derive(Clone)] +pub struct XhciInterrupter { + mem: Arc, + /// Interrupter Management + pub iman: u32, + /// Interrupter Morderation + pub imod: u32, + /// Event Ring Segment Table Size + pub erstsz: u32, + /// Event Ring Segment Table Base Address + pub erstba: u64, + /// Event Ring Dequeue Pointer + pub erdp: u64, + /// Event Ring Producer Cycle State + pub er_pcs: bool, + pub er_start: u64, + pub er_size: u32, + pub er_ep_idx: u32, +} + +impl XhciInterrupter { + pub fn new(mem: &Arc) -> Self { + Self { + mem: mem.clone(), + iman: 0, + imod: 0, + erstsz: 0, + erstba: 0, + erdp: 0, + er_pcs: false, + er_start: 0, + er_size: 0, + er_ep_idx: 0, + } + } + + pub fn reset(&mut self) { + self.iman = 0; + self.imod = 0; + self.erstsz = 0; + self.erstba = 0; + self.erdp = 0; + self.er_pcs = false; + self.er_start = 0; + self.er_size = 0; + self.er_ep_idx = 0; + } +} + +/// XHCI port used to notify device. +pub struct XhciPort { + xhci: Weak>, + /// Port Status and Control + pub portsc: u32, + /// Port ID + pub port_idx: u32, + pub usb_port: Option>>, + pub speed_mask: u32, + pub name: String, +} + +impl XhciPort { + pub fn new(xhci: &Weak>, name: String, i: u32) -> Self { + Self { + xhci: xhci.clone(), + portsc: 0, + port_idx: i, + speed_mask: 0, + usb_port: None, + name, + } + } +} diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs new file mode 100644 index 000000000..5d374e347 --- /dev/null +++ b/usb/src/xhci/xhci_ring.rs @@ -0,0 +1,213 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::sync::Arc; + +use address_space::AddressSpace; + +/// TRB Type Definitions. See the spec 6.4.6 TRB types. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TRBType { + TrbReserved = 0, + TrNormal, + TrSetup, + TrData, + TrStatus, + TrIsoch, + TrLink, + TrEvdata, + TrNoop, + CrEnableSlot, + CrDisableSlot, + CrAddressDevice, + CrConfigureEndpoint, + CrEvaluateContext, + CrResetEndpoint, + CrStopEndpoint, + CrSetTrDequeue, + CrResetDevice, + CrForceEvent, + CrNegotiateBw, + CrSetLatencyTolerance, + CrGetPortBandwidth, + CrForceHeader, + CrNoop, + ErTransfer = 32, + ErCommandComplete, + ErPortStatusChange, + ErBandwidthRequest, + ErDoorbell, + ErHostController, + ErDeviceNotification, + ErMfindexWrap, + /* vendor specific bits */ + CrVendorNecFirmwareRevision = 49, + CrVendorNecChallengeResponse = 50, + Unknown, +} + +impl From for TRBType { + fn from(t: u32) -> TRBType { + match t { + 0 => TRBType::TrbReserved, + 1 => TRBType::TrNormal, + 2 => TRBType::TrSetup, + 3 => TRBType::TrData, + 4 => TRBType::TrStatus, + 5 => TRBType::TrIsoch, + 6 => TRBType::TrLink, + 7 => TRBType::TrEvdata, + 8 => TRBType::TrNoop, + 9 => TRBType::CrEnableSlot, + 10 => TRBType::CrDisableSlot, + 11 => TRBType::CrAddressDevice, + 12 => TRBType::CrConfigureEndpoint, + 13 => TRBType::CrEvaluateContext, + 14 => TRBType::CrResetEndpoint, + 15 => TRBType::CrStopEndpoint, + 16 => TRBType::CrSetTrDequeue, + 17 => TRBType::CrResetDevice, + 18 => TRBType::CrForceEvent, + 19 => TRBType::CrNegotiateBw, + 20 => TRBType::CrSetLatencyTolerance, + 21 => TRBType::CrGetPortBandwidth, + 22 => TRBType::CrForceHeader, + 23 => TRBType::CrNoop, + 32 => TRBType::ErTransfer, + 33 => TRBType::ErCommandComplete, + 34 => TRBType::ErPortStatusChange, + 35 => TRBType::ErBandwidthRequest, + 36 => TRBType::ErDoorbell, + 37 => TRBType::ErHostController, + 38 => TRBType::ErDeviceNotification, + 39 => TRBType::ErMfindexWrap, + 49 => TRBType::CrVendorNecFirmwareRevision, + 50 => TRBType::CrVendorNecChallengeResponse, + _ => TRBType::Unknown, + } + } +} + +/// TRB Completion Code. See the spec 6.4.5 TRB Completion Codes. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TRBCCode { + Invalid = 0, + Success, + DataBufferError, + BabbleDetected, + UsbTransactionError, + TrbError, + StallError, + ResourceError, + BandwidthError, + NoSlotsError, + InvalidStreamTypeError, + SlotNotEnabledError, + EpNotEnabledError, + ShortPacket, + RingUnderrun, + RingOverrun, + VfErFull, + ParameterError, + BandwidthOverrun, + ContextStateError, + NoPingResponseError, + EventRingFullError, + IncompatibleDeviceError, + MissedServiceError, + CommandRingStopped, + CommandAborted, + Stopped, + StoppedLengthInvalid, + MaxExitLatencyTooLargeError = 29, + IsochBufferOverrun = 31, + EventLostError, + UndefinedError, + InvalidStreamIdError, + SecondaryBandwidthError, + SplitTransactionError, +} + +type DmaAddr = u64; + +/// XHCI Transfer Request Block +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] +pub struct XhciTRB { + pub parameter: u64, + pub status: u32, + pub control: u32, + pub addr: DmaAddr, + pub ccs: bool, +} + +impl XhciTRB { + pub fn new() -> Self { + Self { + parameter: 0, + status: 0, + control: 0, + addr: 0, + ccs: true, + } + } +} + +/// XHCI Ring +#[derive(Clone)] +pub struct XhciRing { + mem: Arc, + pub dequeue: u64, + /// Consumer Cycle State + pub ccs: bool, +} + +impl XhciRing { + pub fn new(mem: &Arc) -> Self { + Self { + mem: mem.clone(), + dequeue: 0, + ccs: true, + } + } + + pub fn init(&mut self, addr: u64) { + self.dequeue = addr; + self.ccs = true; + } +} + +impl Display for XhciRing { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!(f, "XhciRing dequeue {:x} ccs {}", self.dequeue, self.ccs) + } +} + +/// XHCI event ring segment +#[derive(Clone)] +pub struct XhciEventRingSeg { + mem: Arc, + pub addr_lo: u32, + pub addr_hi: u32, + pub size: u32, + pub rsvd: u32, +} + +impl Display for XhciEventRingSeg { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!( + f, + "XhciEventRingSeg addr_lo {:x} addr_hi {:x} size {} rsvd {}", + self.addr_lo, self.addr_hi, self.size, self.rsvd + ) + } +} -- Gitee From ef1768c7ea1abb9090ee70b9df8028e93fe1c104 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 15:01:17 +0800 Subject: [PATCH 0113/2187] xhci: implement fetch trb in ring Now we support fetch trb from the ring. And get transfer trb len from the transfer ring. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 77 ++++++++++++++++- usb/src/xhci/xhci_ring.rs | 142 +++++++++++++++++++++++++++++++- 2 files changed, 216 insertions(+), 3 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index d657fdaa9..d5d9a7fe9 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -10,12 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::mem::size_of; use std::sync::{Arc, Mutex, Weak}; -use address_space::AddressSpace; +use address_space::{AddressSpace, GuestAddress}; +use byteorder::{ByteOrder, LittleEndian}; use crate::bus::UsbBus; use crate::config::*; +use crate::errors::{Result, ResultExt}; use crate::usb::{UsbPacket, UsbPort}; use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter, XhciPort}; use crate::xhci::xhci_ring::{TRBCCode, TRBType, XhciRing, XhciTRB}; @@ -215,3 +218,75 @@ impl XhciDevice { xhci } } + +// DMA read/write helpers. +pub(crate) fn dma_read_bytes( + addr_space: &Arc, + addr: GuestAddress, + mut buf: &mut [u8], + len: u64, +) -> Result<()> { + addr_space.read(&mut buf, addr, len).chain_err(|| { + format!( + "Failed to read dma memory at gpa=0x{:x} len=0x{:x}", + addr.0, len + ) + })?; + Ok(()) +} + +pub(crate) fn dma_write_bytes( + addr_space: &Arc, + addr: GuestAddress, + mut buf: &[u8], + len: u64, +) -> Result<()> { + addr_space.write(&mut buf, addr, len).chain_err(|| { + format!( + "Failed to write dma memory at gpa=0x{:x} len=0x{:x}", + addr.0, len + ) + })?; + Ok(()) +} + +pub(crate) fn dma_read_u64( + addr_space: &Arc, + addr: GuestAddress, + data: &mut u64, +) -> Result<()> { + let mut tmp = [0_u8; 8]; + dma_read_bytes(addr_space, addr, &mut tmp, 8)?; + *data = LittleEndian::read_u64(&tmp); + Ok(()) +} + +pub(crate) fn dma_read_u32( + addr_space: &Arc, + addr: GuestAddress, + buf: &mut [u32], +) -> Result<()> { + let vec_len = size_of::() * buf.len(); + let mut vec = vec![0_u8; vec_len]; + let tmp = vec.as_mut_slice(); + dma_read_bytes(addr_space, addr, tmp, vec_len as u64)?; + for i in 0..buf.len() { + buf[i] = LittleEndian::read_u32(&tmp[(size_of::() * i)..]); + } + Ok(()) +} + +pub(crate) fn dma_write_u32( + addr_space: &Arc, + addr: GuestAddress, + buf: &[u32], +) -> Result<()> { + let vec_len = size_of::() * buf.len(); + let mut vec = vec![0_u8; vec_len]; + let tmp = vec.as_mut_slice(); + for i in 0..buf.len() { + LittleEndian::write_u32(&mut tmp[(size_of::() * i)..], buf[i]); + } + dma_write_bytes(addr_space, addr, tmp, vec_len as u64)?; + Ok(()) +} diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 5d374e347..a298d08e1 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -13,7 +13,33 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use std::sync::Arc; -use address_space::AddressSpace; +use address_space::{AddressSpace, GuestAddress}; +use byteorder::{ByteOrder, LittleEndian}; + +use crate::errors::Result; +use crate::xhci::xhci_controller::dma_read_bytes; + +/// Transfer Request Block +pub const TRB_SIZE: u32 = 16; +pub const TRB_TYPE_SHIFT: u32 = 10; +pub const TRB_TYPE_MASK: u32 = 0x3f; +/// Cycle bit +pub const TRB_C: u32 = 1; +/// Event Data +pub const TRB_EV_ED: u32 = 1 << 2; +/// Toggle Cycle +pub const TRB_LK_TC: u32 = 1 << 1; +/// Interrupt-on Short Packet +pub const TRB_TR_ISP: u32 = 1 << 2; +/// Chain bit +pub const TRB_TR_CH: u32 = 1 << 4; +/// Interrupt On Completion +pub const TRB_TR_IOC: u32 = 1 << 5; +/// Immediate Data. +pub const TRB_TR_IDT: u32 = 1 << 6; + +const TRB_LINK_LIMIT: u32 = 32; +const RING_LEN_LIMIT: u32 = 32; /// TRB Type Definitions. See the spec 6.4.6 TRB types. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -160,6 +186,16 @@ impl XhciTRB { ccs: true, } } + + /// Get TRB type + pub fn get_type(&self) -> TRBType { + ((self.control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK).into() + } + + // Get Cycle bit + pub fn get_cycle_bit(&self) -> bool { + self.control & TRB_C == TRB_C + } } /// XHCI Ring @@ -184,6 +220,85 @@ impl XhciRing { self.dequeue = addr; self.ccs = true; } + + /// Fetch TRB from the ring. + pub fn fetch_trb(&mut self) -> Result { + let mut link_cnt = 0; + loop { + let mut trb = self.read_trb(self.dequeue)?; + trb.addr = self.dequeue; + trb.ccs = self.ccs; + if trb.get_cycle_bit() != self.ccs { + bail!("TRB cycle bit not matched"); + } + let trb_type = trb.get_type(); + debug!("Fetch TRB: type {:?} trb {:?}", trb_type, trb); + if trb_type == TRBType::TrLink { + link_cnt += 1; + if link_cnt > TRB_LINK_LIMIT { + bail!("TRB reach link limit"); + } + self.dequeue = trb.parameter; + if trb.control & TRB_LK_TC == TRB_LK_TC { + self.ccs = !self.ccs; + } + } else { + self.dequeue += TRB_SIZE as u64; + return Ok(trb); + } + } + } + + fn read_trb(&self, addr: u64) -> Result { + let mut buf = [0; TRB_SIZE as usize]; + dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf, TRB_SIZE as u64)?; + let trb = XhciTRB { + parameter: LittleEndian::read_u64(&buf), + status: LittleEndian::read_u32(&buf[8..]), + control: LittleEndian::read_u32(&buf[12..]), + addr: 0, + ccs: true, + }; + Ok(trb) + } + + /// Get the number of TRBs in the TD if success. + pub fn get_transfer_len(&self) -> Result { + let mut len = 0; + let mut dequeue = self.dequeue; + let mut ccs = self.ccs; + let mut ctrl_td = false; + let mut link_cnt = 0; + for _ in 0..RING_LEN_LIMIT { + let trb = self.read_trb(dequeue)?; + if trb.get_cycle_bit() != ccs { + bail!("TRB cycle bit not matched"); + } + let trb_type = trb.get_type(); + if trb_type == TRBType::TrLink { + link_cnt += 1; + if link_cnt > TRB_LINK_LIMIT { + bail!("TRB link over limit"); + } + dequeue = trb.parameter; + if trb.control & TRB_LK_TC == TRB_LK_TC { + ccs = !ccs; + } + } else { + len += 1; + dequeue += TRB_SIZE as u64; + if trb_type == TRBType::TrSetup { + ctrl_td = true; + } else if trb_type == TRBType::TrStatus { + ctrl_td = false; + } + if !ctrl_td && (trb.control & TRB_TR_CH != TRB_TR_CH) { + return Ok(len); + } + } + } + bail!("Transfer TRB length over limit"); + } } impl Display for XhciRing { @@ -192,7 +307,7 @@ impl Display for XhciRing { } } -/// XHCI event ring segment +/// Event Ring Segment Table Entry. See in the specs 6.5 Event Ring Segment Table. #[derive(Clone)] pub struct XhciEventRingSeg { mem: Arc, @@ -211,3 +326,26 @@ impl Display for XhciEventRingSeg { ) } } + +impl XhciEventRingSeg { + pub fn new(mem: &Arc) -> Self { + Self { + mem: mem.clone(), + addr_lo: 0, + addr_hi: 0, + size: 0, + rsvd: 0, + } + } + + /// Fetch the event ring segment. + pub fn fetch_event_ring_seg(&mut self, addr: u64) -> Result<()> { + let mut buf = [0_u8; TRB_SIZE as usize]; + dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf, TRB_SIZE as u64)?; + self.addr_lo = LittleEndian::read_u32(&buf); + self.addr_hi = LittleEndian::read_u32(&buf[4..]); + self.size = LittleEndian::read_u32(&buf[8..]); + self.rsvd = LittleEndian::read_u32(&buf[12..]); + Ok(()) + } +} -- Gitee From ef036842efd0964b6c0b13e338334b3ac88fe8ac Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 18:27:54 +0800 Subject: [PATCH 0114/2187] xhci: implement interrupter for xhci controller Implement interrupter for xhci to support send event. Signed-off-by: zhouli57 --- usb/src/bus.rs | 35 +++ usb/src/usb.rs | 10 + usb/src/xhci/xhci_controller.rs | 364 +++++++++++++++++++++++++++++++- usb/src/xhci/xhci_regs.rs | 37 +++- util/src/num_ops.rs | 66 ++++++ 5 files changed, 503 insertions(+), 9 deletions(-) diff --git a/usb/src/bus.rs b/usb/src/bus.rs index b4d95348f..76e5b5c0a 100644 --- a/usb/src/bus.rs +++ b/usb/src/bus.rs @@ -33,6 +33,41 @@ impl UsbBus { used_ports: LinkedList::new(), } } + + /// Register USB port to the bus. + pub fn register_usb_port(&mut self, port: &Arc>) { + let mut locked_port = port.lock().unwrap(); + locked_port.path = format!("{}", locked_port.index + 1); + self.free_ports.push_back(port.clone()); + } + + /// Assign USB port and attach the device. + pub fn assign_usb_port( + &mut self, + dev: &Arc>, + ) -> Result>> { + if let Some(port) = self.free_ports.pop_front() { + let mut locked_dev = dev.lock().unwrap(); + locked_dev.set_usb_port(Some(Arc::downgrade(&port))); + let mut locked_port = port.lock().unwrap(); + locked_port.dev = Some(dev.clone()); + drop(locked_port); + self.used_ports.push_back(port.clone()); + Ok(port) + } else { + bail!("No available usb port"); + } + } + + /// Find USB port by path. + pub fn find_usb_port(&self, path: String) -> Option>> { + for usb in &self.used_ports { + if usb.lock().unwrap().path == path { + return Some(usb.clone()); + } + } + None + } } /// Bus device ops for USB controller to handle USB device attach/detach. diff --git a/usb/src/usb.rs b/usb/src/usb.rs index de8b3f9f1..375bc1f53 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -110,6 +110,16 @@ impl UsbPort { index, } } + + /// If the USB port attached USB device. + pub fn is_attached(&self) -> bool { + if let Some(dev) = &self.dev { + let locked_dev = dev.lock().unwrap(); + locked_dev.attached() + } else { + false + } + } } /// USB descriptor strings. diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index d5d9a7fe9..37d03e307 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -11,20 +11,42 @@ // See the Mulan PSL v2 for more details. use std::mem::size_of; +use std::slice::from_raw_parts; +use std::slice::from_raw_parts_mut; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; +use util::num_ops::{read_u32, write_u64_low}; use crate::bus::UsbBus; use crate::config::*; use crate::errors::{Result, ResultExt}; use crate::usb::{UsbPacket, UsbPort}; use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter, XhciPort}; -use crate::xhci::xhci_ring::{TRBCCode, TRBType, XhciRing, XhciTRB}; +use crate::xhci::xhci_ring::{TRBCCode, TRBType, XhciRing, XhciTRB, TRB_SIZE, TRB_TYPE_SHIFT}; pub const MAX_INTRS: u16 = 16; pub const MAX_SLOTS: u32 = 64; +/// Endpoint state +const EP_STATE_MASK: u32 = 0x7; +const EP_DISABLED: u32 = 0; +const EP_RUNNING: u32 = 1; +const EP_HALTED: u32 = 2; +const EP_STOPPED: u32 = 3; +/// Endpoint type +const EP_TYPE_SHIFT: u32 = 3; +const EP_TYPE_MASK: u32 = 0x7; +#[allow(unused)] +const EP_ERROR: u32 = 4; +const EP_CTX_MAX_PACKET_SIZE_SHIFT: u32 = 16; +const EP_CTX_LSA_SHIFT: u32 = 15; +const EP_CTX_INTERVAL_SHIFT: u32 = 16; +const EP_CTX_INTERVAL_MASK: u32 = 0xff; +const EVENT_TRB_CCODE_SHIFT: u32 = 24; +const EVENT_TRB_SLOT_ID_SHIFT: u32 = 24; +const EVENT_TRB_EP_ID_SHIFT: u32 = 16; +const PORT_EVENT_ID_SHIFT: u32 = 24; type DmaAddr = u64; @@ -91,6 +113,30 @@ impl XhciEpContext { retry: None, } } + + pub fn init(&mut self, pctx: DmaAddr, ctx: &XhciEpCtx) { + let dequeue: DmaAddr = addr64_from_u32(ctx.deq_lo & !0xf, ctx.deq_hi); + self.ep_type = ((ctx.ep_info2 >> EP_TYPE_SHIFT) & EP_TYPE_MASK).into(); + self.pctx = pctx; + self.max_psize = ctx.ep_info2 >> EP_CTX_MAX_PACKET_SIZE_SHIFT; + self.max_psize *= 1 + ((ctx.ep_info2 >> 8) & 0xff); + self.lsa = (ctx.ep_info >> EP_CTX_LSA_SHIFT) & 1 == 1; + self.ring.init(dequeue); + self.ring.ccs = (ctx.deq_lo & 1) == 1; + self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); + } + + pub fn set_state(&mut self, mem: &Arc, state: u32) -> Result<()> { + let mut ep_ctx = XhciEpCtx::default(); + dma_read_u32(mem, GuestAddress(self.pctx), ep_ctx.as_mut_dwords())?; + ep_ctx.ep_info &= !EP_STATE_MASK; + ep_ctx.ep_info |= state; + ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; + ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; + dma_write_u32(mem, GuestAddress(self.pctx), ep_ctx.as_dwords())?; + self.state = state; + Ok(()) + } } /// Endpoint type, including control, bulk, interrupt and isochronous. @@ -170,6 +216,20 @@ impl XhciEvent { ep_id: 0, } } + + /// Convert event to trb. + pub fn to_trb(&self) -> XhciTRB { + XhciTRB { + parameter: self.ptr, + status: self.length | (self.ccode as u32) << EVENT_TRB_CCODE_SHIFT, + control: (self.slot_id as u32) << EVENT_TRB_SLOT_ID_SHIFT + | (self.ep_id as u32) << EVENT_TRB_EP_ID_SHIFT + | self.flags as u32 + | (self.trb_type as u32) << TRB_TYPE_SHIFT, + addr: 0, + ccs: false, + } + } } /// Controller ops registered in XhciDevice. Such as PCI device send MSIX. @@ -179,6 +239,51 @@ pub trait XhciOps: Send + Sync { fn update_intr(&mut self, n: u32, enable: bool); } +/// Input Control Context. See the spec 6.2.5 Input Control Context. +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct XhciInputCtrlCtx { + pub drop_flags: u32, + pub add_flags: u32, +} + +impl DwordOrder for XhciInputCtrlCtx {} + +/// Slot Context. See the spec 6.2.2 Slot Context. +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct XhciSlotCtx { + pub dev_info: u32, + pub dev_info2: u32, + pub tt_info: u32, + pub dev_state: u32, +} + +impl DwordOrder for XhciSlotCtx {} + +/// Endpoint Context. See the spec 6.2.3 Endpoint Context. +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct XhciEpCtx { + pub ep_info: u32, + pub ep_info2: u32, + pub deq_lo: u32, + pub deq_hi: u32, + pub tx_info: u32, +} + +impl DwordOrder for XhciEpCtx {} + +trait DwordOrder: Default + Copy + Send + Sync { + fn as_dwords(&self) -> &[u32] { + unsafe { from_raw_parts(self as *const Self as *const u32, size_of::() / 4) } + } + + fn as_mut_dwords(&mut self) -> &mut [u32] { + unsafe { from_raw_parts_mut(self as *mut Self as *mut u32, size_of::() / 4) } + } +} + /// Xhci controller device. pub struct XhciDevice { pub numports_2: u32, @@ -215,12 +320,244 @@ impl XhciDevice { let clone_xhci = xhci.clone(); let mut locked_xhci = clone_xhci.lock().unwrap(); locked_xhci.oper.usb_status = USB_STS_HCH; + for i in 0..locked_xhci.port_num { + locked_xhci.ports.push(Arc::new(Mutex::new(XhciPort::new( + &Arc::downgrade(&clone_xhci), + format!("xhci-port-{}", i), + i + 1, + )))); + } + for i in 0..locked_xhci.numports_2 { + let usb_port = Arc::new(Mutex::new(UsbPort::new(i))); + locked_xhci.usb_ports.push(usb_port.clone()); + locked_xhci.bus.lock().unwrap().register_usb_port(&usb_port); + let mut locked_port = locked_xhci.ports[i as usize].lock().unwrap(); + locked_port.name = format!("{}-usb2-{}", locked_port.name, i); + locked_port.speed_mask = USB_SPEED_LOW | USB_SPEED_HIGH | USB_SPEED_FULL; + locked_port.usb_port = Some(Arc::downgrade(&usb_port)); + } + for i in 0..locked_xhci.numports_3 { + let idx = i + locked_xhci.numports_2; + let usb_port = Arc::new(Mutex::new(UsbPort::new(idx))); + locked_xhci.usb_ports.push(usb_port.clone()); + locked_xhci.bus.lock().unwrap().register_usb_port(&usb_port); + let mut locked_port = locked_xhci.ports[idx as usize].lock().unwrap(); + locked_port.name = format!("{}-usb3-{}", locked_port.name, idx); + locked_port.speed_mask = USB_SPEED_SUPER; + locked_port.usb_port = Some(Arc::downgrade(&usb_port)); + } xhci } + + pub fn run(&mut self) { + self.oper.usb_status &= !USB_STS_HCH; + } + + pub fn stop(&mut self) { + self.oper.usb_status |= USB_STS_HCH; + let mut lo = read_u32(self.oper.cmd_ring_ctrl, 0); + lo &= !CMD_RING_CTRL_CRR; + write_u64_low(self.oper.cmd_ring_ctrl, lo); + } + + pub fn running(&self) -> bool { + self.oper.usb_status & USB_STS_HCH != USB_STS_HCH + } + + pub fn reset(&mut self) { + info!("xhci reset"); + self.oper.reset(); + for i in 0..self.slots.len() as u32 { + if let Err(e) = self.disable_slot(i + 1) { + error!("Failed to disable slot {}", e); + } + } + for i in 0..self.ports.len() { + let port = self.ports[i].clone(); + if let Err(e) = self.port_update(&port) { + error!("Failed to update port: {}", e); + } + } + for i in 0..self.intrs.len() { + self.intrs[i].reset(); + } + } + + /// Find xhci port by usb port. + pub fn lookup_xhci_port(&self, dev: &Arc>) -> Option>> { + let index = dev.lock().unwrap().index; + Some(self.ports[index as usize].clone()) + } + + /// Send PortStatusChange event to notify drivers. + pub fn port_notify(&mut self, port: &Arc>, flag: u32) -> Result<()> { + let mut locked_port = port.lock().unwrap(); + if locked_port.portsc & flag == flag { + return Ok(()); + } + locked_port.portsc |= flag; + if !self.running() { + return Ok(()); + } + let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); + evt.ptr = (locked_port.port_idx << PORT_EVENT_ID_SHIFT) as u64; + self.send_event(&evt, 0)?; + Ok(()) + } + + /// Update the xhci port status and then + pub fn port_update(&mut self, port: &Arc>) -> Result<()> { + let mut locked_port = port.lock().unwrap(); + locked_port.portsc = PORTSC_PP; + let mut pls = PLS_RX_DETECT; + if let Some(usb_port) = &locked_port.usb_port { + let usb_port = usb_port.upgrade().unwrap(); + let locked_usb_port = usb_port.lock().unwrap(); + if let Some(dev) = &locked_usb_port.dev { + let speed = dev.lock().unwrap().speed(); + locked_port.portsc |= PORTSC_CCS; + if speed == PORTSC_SPEED_SUPER { + locked_port.portsc |= PORTSC_SPEED_SUPER; + locked_port.portsc |= PORTSC_PED; + pls = PLS_U0; + } else { + locked_port.portsc |= speed; + pls = PLS_POLLING; + } + } + } + locked_port.portsc = set_field(locked_port.portsc, pls, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + debug!( + "xhci port update portsc {:x} pls {:x}", + locked_port.portsc, pls + ); + drop(locked_port); + self.port_notify(port, PORTSC_CSC)?; + Ok(()) + } + + fn disable_slot(&mut self, slot_id: u32) -> Result { + for i in 1..=self.slots[(slot_id - 1) as usize].endpoints.len() as u32 { + self.disable_endpoint(slot_id, i)?; + } + self.slots[(slot_id - 1) as usize].enabled = false; + self.slots[(slot_id - 1) as usize].addressed = false; + self.slots[(slot_id - 1) as usize].usb_port = None; + self.slots[(slot_id - 1) as usize].intr = 0; + Ok(TRBCCode::Success) + } + + fn disable_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { + let slot = &mut self.slots[(slot_id - 1) as usize]; + let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; + if !epctx.enabled { + info!("Endpoint already disabled"); + return Ok(TRBCCode::Success); + } + if self.oper.dcbaap != 0 { + epctx.set_state(&self.mem_space, EP_DISABLED)?; + } + epctx.enabled = false; + Ok(TRBCCode::Success) + } + + /// Send event TRB to driver, first write TRB and then send interrupt. + pub fn send_event(&mut self, evt: &XhciEvent, idx: u32) -> Result<()> { + if idx > self.intrs.len() as u32 { + bail!("Invalid index, out of range {}", idx); + } + let intr = &self.intrs[idx as usize]; + if intr.erdp < intr.er_start + || intr.erdp >= (intr.er_start + (TRB_SIZE * intr.er_size) as u64) + { + bail!( + "DMA out of range, erdp {} er_start {:x} er_size {}", + intr.erdp, + intr.er_start, + intr.er_size + ); + } + let dp_idx = (intr.erdp - intr.er_start) / TRB_SIZE as u64; + if ((intr.er_ep_idx + 2) % intr.er_size) as u64 == dp_idx { + error!("Event ring full error, idx {}", idx); + let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); + self.write_event(&event, idx)?; + } else if ((intr.er_ep_idx + 1) % intr.er_size) as u64 == dp_idx { + bail!("Event Ring full, drop Event."); + } else { + self.write_event(evt, idx)?; + } + self.send_intr(idx); + Ok(()) + } + + fn write_event(&mut self, evt: &XhciEvent, idx: u32) -> Result<()> { + let intr = &mut self.intrs[idx as usize]; + intr.write_event(evt)?; + Ok(()) + } + + pub fn send_intr(&mut self, idx: u32) { + let pending = read_u32(self.intrs[idx as usize].erdp, 0) & ERDP_EHB == ERDP_EHB; + let mut erdp_low = read_u32(self.intrs[idx as usize].erdp, 0); + erdp_low |= ERDP_EHB; + self.intrs[idx as usize].erdp = write_u64_low(self.intrs[idx as usize].erdp, erdp_low); + self.intrs[idx as usize].iman |= IMAN_IP; + self.oper.usb_status |= USB_STS_EINT; + if pending { + return; + } + if self.intrs[idx as usize].iman & IMAN_IE != IMAN_IE { + return; + } + if self.oper.usb_cmd & USB_CMD_INTE != USB_CMD_INTE { + return; + } + + if let Some(ops) = self.ctrl_ops.as_ref() { + ops.upgrade() + .unwrap() + .lock() + .unwrap() + .trigger_intr(idx, true); + self.intrs[idx as usize].iman &= !IMAN_IP; + } + } + + pub fn update_intr(&mut self, v: u32) { + let mut level = false; + if v == 0 { + if self.intrs[0].iman & IMAN_IP == IMAN_IP + && self.intrs[0].iman & IMAN_IE == IMAN_IE + && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE + { + level = true; + } + if let Some(ops) = &self.ctrl_ops { + if ops + .upgrade() + .unwrap() + .lock() + .unwrap() + .trigger_intr(0, level) + { + self.intrs[0].iman &= !IMAN_IP; + } + } + } + + if let Some(ops) = &self.ctrl_ops { + ops.upgrade() + .unwrap() + .lock() + .unwrap() + .update_intr(v, self.intrs[0].iman & IMAN_IE == IMAN_IE); + } + } } // DMA read/write helpers. -pub(crate) fn dma_read_bytes( +pub fn dma_read_bytes( addr_space: &Arc, addr: GuestAddress, mut buf: &mut [u8], @@ -235,7 +572,7 @@ pub(crate) fn dma_read_bytes( Ok(()) } -pub(crate) fn dma_write_bytes( +pub fn dma_write_bytes( addr_space: &Arc, addr: GuestAddress, mut buf: &[u8], @@ -250,7 +587,7 @@ pub(crate) fn dma_write_bytes( Ok(()) } -pub(crate) fn dma_read_u64( +pub fn dma_read_u64( addr_space: &Arc, addr: GuestAddress, data: &mut u64, @@ -261,7 +598,7 @@ pub(crate) fn dma_read_u64( Ok(()) } -pub(crate) fn dma_read_u32( +pub fn dma_read_u32( addr_space: &Arc, addr: GuestAddress, buf: &mut [u32], @@ -276,7 +613,7 @@ pub(crate) fn dma_read_u32( Ok(()) } -pub(crate) fn dma_write_u32( +pub fn dma_write_u32( addr_space: &Arc, addr: GuestAddress, buf: &[u32], @@ -290,3 +627,18 @@ pub(crate) fn dma_write_u32( dma_write_bytes(addr_space, addr, tmp, vec_len as u64)?; Ok(()) } + +fn addr64_from_u32(low: u32, high: u32) -> u64 { + (((high << 16) as u64) << 16) | low as u64 +} + +pub fn get_field(val: u32, mask: u32, shift: u32) -> u32 { + val >> shift & mask +} + +pub fn set_field(val: u32, new_val: u32, mask: u32, shift: u32) -> u32 { + let mut tmp = val; + tmp &= !(mask << shift); + tmp |= (new_val & mask) << shift; + tmp +} diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index a282ab798..8c5e850a9 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -10,13 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::config::*; +use crate::xhci::xhci_controller::dma_write_bytes; +use crate::xhci::xhci_ring::XhciTRB; use std::sync::{Arc, Mutex, Weak}; -use address_space::AddressSpace; +use address_space::{AddressSpace, GuestAddress}; +use byteorder::{ByteOrder, LittleEndian}; +use crate::config::*; +use crate::errors::Result; use crate::usb::UsbPort; -use crate::xhci::xhci_controller::XhciDevice; +use crate::xhci::xhci_controller::{XhciDevice, XhciEvent}; +use crate::xhci::xhci_ring::{TRB_C, TRB_SIZE}; /// Capability offset or size. pub const XHCI_CAP_LENGTH: u32 = 0x40; @@ -110,6 +115,32 @@ impl XhciInterrupter { self.er_size = 0; self.er_ep_idx = 0; } + + /// Write event to the ring and update index. + pub fn write_event(&mut self, evt: &XhciEvent) -> Result<()> { + let mut ev_trb = evt.to_trb(); + if self.er_pcs { + ev_trb.control |= TRB_C; + } + self.write_trb(&ev_trb)?; + // Update index + self.er_ep_idx += 1; + if self.er_ep_idx >= self.er_size { + self.er_ep_idx = 0; + self.er_pcs = !self.er_pcs; + } + Ok(()) + } + + fn write_trb(&mut self, trb: &XhciTRB) -> Result<()> { + let addr = self.er_start + (TRB_SIZE * self.er_ep_idx) as u64; + let mut buf = [0_u8; TRB_SIZE as usize]; + LittleEndian::write_u64(&mut buf, trb.parameter); + LittleEndian::write_u32(&mut buf[8..], trb.status); + LittleEndian::write_u32(&mut buf[12..], trb.control); + dma_write_bytes(&self.mem, GuestAddress(addr), &buf, TRB_SIZE as u64)?; + Ok(()) + } } /// XHCI port used to notify device. diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 2f09db691..c39b88a1f 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -108,6 +108,48 @@ pub fn write_u32(value: u32, page: u32) -> u64 { } } +/// Write the given u32 to the low bits in u64, keep the high bits, +/// returns the u64 value. +/// +/// # Arguments +/// +/// * `origin` - The origin u64 value. +/// * `value` - The set u32 value. +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::write_u64_low; +/// +/// let value = write_u64_low(0x1000_0000_0000_0000, 0x1000_0000); +/// assert!(value == 0x1000_0000_1000_0000); +/// ``` +pub fn write_u64_low(origin: u64, value: u32) -> u64 { + origin & 0xFFFF_FFFF_0000_0000_u64 | u64::from(value) +} + +/// Write the given u32 to the high bits in u64, keep the low bits, +/// returns the u64 value. +/// +/// # Arguments +/// +/// * `origin` - The origin u64 value. +/// * `value` - The set u32 value. +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::write_u64_high; +/// +/// let value = write_u64_high(0x0000_0000_1000_0000, 0x1000_0000); +/// assert!(value == 0x1000_0000_1000_0000); +/// ``` +pub fn write_u64_high(origin: u64, value: u32) -> u64 { + u64::from(value) << 32 | (origin & 0x0000_0000_FFFF_FFFF_u64) +} + /// Extract from the 32 bit input @value the bit field specified by the /// @start and @length parameters, and return it. The bit field must /// lie entirely within the 32 bit word. It is valid to request that @@ -240,6 +282,30 @@ mod test { assert_eq!(write_u32(0x1234_5678, 2), 0); } + #[test] + fn test_write_u64_low() { + assert_eq!( + write_u64_low(0x0000_0000_FFFF_FFFF_u64, 0x1234_5678), + 0x0000_0000_1234_5678_u64 + ); + assert_eq!( + write_u64_low(0xFFFF_FFFF_0000_0000_u64, 0x1234_5678), + 0xFFFF_FFFF_1234_5678_u64 + ); + } + + #[test] + fn test_write_u64_high() { + assert_eq!( + write_u64_high(0x0000_0000_FFFF_FFFF_u64, 0x1234_5678), + 0x1234_5678_FFFF_FFFF_u64 + ); + assert_eq!( + write_u64_high(0xFFFF_FFFF_0000_0000_u64, 0x1234_5678), + 0x1234_5678_0000_0000_u64 + ); + } + #[test] fn test_extract_u32() { assert_eq!(extract_u32(0xfefbfffa, 0, 33), None); -- Gitee From f6f8d711fdca3d92405c632a7ade381d942485f9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:23:55 +0800 Subject: [PATCH 0115/2187] xhci: add xhci pci device Add xhci pci device and as a pci device it can be attached to pci bus. But we not implement region ops in mem_region_init yet. Signed-off-by: zhouli57 --- pci/src/config.rs | 3 + usb/src/xhci/mod.rs | 1 + usb/src/xhci/xhci_pci.rs | 282 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 usb/src/xhci/xhci_pci.rs diff --git a/pci/src/config.rs b/pci/src/config.rs index c2b4c882c..4cca1138c 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -251,6 +251,9 @@ const PCIE_CAP_LINK_TLS_16GT: u16 = 0x0004; // PCIe type flag const PCI_EXP_FLAGS_TYPE_SHIFT: u16 = 4; const PCI_EXP_FLAGS_TYPE: u16 = 0x00f0; +// XHCI device id +pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; +pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; /// Type of bar region. #[derive(PartialEq, Debug, Copy, Clone)] diff --git a/usb/src/xhci/mod.rs b/usb/src/xhci/mod.rs index 62f752ac5..62151bb2a 100644 --- a/usb/src/xhci/mod.rs +++ b/usb/src/xhci/mod.rs @@ -11,5 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod xhci_controller; +pub mod xhci_pci; pub mod xhci_regs; mod xhci_ring; diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs new file mode 100644 index 000000000..a35c79753 --- /dev/null +++ b/usb/src/xhci/xhci_pci.rs @@ -0,0 +1,282 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::atomic::{AtomicU16, Ordering}; +use std::sync::{Arc, Mutex, Weak}; + +use address_space::{AddressSpace, Region}; +use pci::config::{ + PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, PCI_CLASS_SERIAL_USB, + PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, +}; +use pci::errors::Result as PciResult; +use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; +use util::num_ops::round_up; +use util::unix::host_page_size; + +use crate::bus::{BusDeviceMap, BusDeviceOps}; +use crate::errors::Result; +use crate::usb::UsbDeviceOps; +use crate::xhci::xhci_controller::{XhciDevice, XhciOps, MAX_INTRS, MAX_SLOTS}; +use crate::xhci::xhci_regs::{XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME}; + +const PCI_VENDOR_ID_NEC: u16 = 0x1033; +const PCU_DEVICE_ID_NEC_UPD720200: u16 = 0x0194; +/// 5.2 PCI Configuration Registers(USB) +const PCI_CLASS_PI: u16 = 0x09; +const PCI_INTERRUPT_PIN: u16 = 0x3d; +const PCI_CACHE_LINE_SIZE: u16 = 0x0c; +const PCI_SERIAL_BUS_RELEASE_NUMBER: u8 = 0x60; +const PCI_SERIAL_BUS_RELEASE_VERSION_3_0: u8 = 0x30; +/// PCI capability offset or size. +const XHCI_PCI_CONFIG_LENGTH: u32 = 0x4000; +const XHCI_PCI_CAP_OFFSET: u32 = 0x0; +const XHCI_PCI_CAP_LENGTH: u32 = XHCI_CAP_LENGTH; +const XHCI_PCI_OPER_OFFSET: u32 = XHCI_PCI_CAP_LENGTH; +const XHCI_PCI_OPER_LENGTH: u32 = 0x400; +const XHCI_PCI_RUNTIME_OFFSET: u32 = XHCI_OFF_RUNTIME; +const XHCI_PCI_RUNTIME_LENGTH: u32 = (MAX_INTRS as u32 + 1) * 0x20; +const XHCI_PCI_DOORBELL_OFFSET: u32 = XHCI_OFF_DOORBELL; +const XHCI_PCI_DOORBELL_LENGTH: u32 = (MAX_SLOTS as u32 + 1) * 0x20; +const XHCI_PCI_PORT_OFFSET: u32 = XHCI_PCI_OPER_OFFSET + XHCI_PCI_OPER_LENGTH; +const XHCI_PCI_PORT_LENGTH: u32 = 0x10; +const XHCI_MSIX_TABLE_OFFSET: u32 = 0x3000; +const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; + +/// XHCI pci device which can be attached to PCI bus. +pub struct XhciPciDevice { + pci_config: PciConfig, + devfn: u8, + xhci: Arc>, + dev_id: Arc, + name: String, + parent_bus: Weak>, + mem_region: Region, + bus_device: BusDeviceMap, +} + +impl XhciPciDevice { + pub fn new( + name: &str, + devfn: u8, + parent_bus: Weak>, + mem_space: &Arc, + bus_device: BusDeviceMap, + ) -> Self { + Self { + pci_config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), + devfn, + xhci: XhciDevice::new(mem_space), + dev_id: Arc::new(AtomicU16::new(0)), + name: name.to_string(), + parent_bus, + mem_region: Region::init_container_region(XHCI_PCI_CONFIG_LENGTH as u64), + bus_device, + } + } + + fn mem_region_init(&mut self) -> PciResult<()> { + Ok(()) + } +} + +impl PciDevOps for XhciPciDevice { + fn init_write_mask(&mut self) -> PciResult<()> { + self.pci_config.init_common_write_mask() + } + + fn init_write_clear_mask(&mut self) -> PciResult<()> { + self.pci_config.init_common_write_clear_mask() + } + + fn realize(mut self) -> PciResult<()> { + self.init_write_mask()?; + self.init_write_clear_mask()?; + le_write_u16( + &mut self.pci_config.config, + VENDOR_ID as usize, + PCI_VENDOR_ID_NEC, + )?; + le_write_u16( + &mut self.pci_config.config, + DEVICE_ID as usize, + PCU_DEVICE_ID_NEC_UPD720200 as u16, + )?; + le_write_u16(&mut self.pci_config.config, REVISION_ID as usize, 0x3_u16)?; + le_write_u16( + &mut self.pci_config.config, + SUB_CLASS_CODE as usize, + PCI_CLASS_SERIAL_USB, + )?; + self.pci_config.config[PCI_CLASS_PI as usize] = 0x30; + self.pci_config.config[PCI_INTERRUPT_PIN as usize] = 0x01; + self.pci_config.config[PCI_CACHE_LINE_SIZE as usize] = 0x10; + self.pci_config.config[PCI_SERIAL_BUS_RELEASE_NUMBER as usize] = + PCI_SERIAL_BUS_RELEASE_VERSION_3_0; + self.dev_id.store(self.devfn as u16, Ordering::SeqCst); + self.mem_region_init()?; + + let intrs_num = self.xhci.lock().unwrap().intrs.len() as u32; + init_msix( + 0_usize, + intrs_num, + &mut self.pci_config, + self.dev_id.clone(), + &self.name, + Some(&self.mem_region), + Some((XHCI_MSIX_TABLE_OFFSET, XHCI_MSIX_PBA_OFFSET)), + )?; + + let mut mem_region_size = (XHCI_PCI_CONFIG_LENGTH as u64).next_power_of_two(); + mem_region_size = round_up(mem_region_size, host_page_size()).unwrap(); + self.pci_config.register_bar( + 0_usize, + self.mem_region.clone(), + RegionType::Mem64Bit, + false, + mem_region_size, + ); + + let devfn = self.devfn; + let dev = Arc::new(Mutex::new(self)); + // Register xhci to bus device. + let cloned_dev = dev.clone(); + let locked_dev = dev.lock().unwrap(); + let mut locked_device = locked_dev.bus_device.lock().unwrap(); + locked_device.insert(String::from("usb.0"), cloned_dev); + drop(locked_device); + drop(locked_dev); + // Register xhci-pci to xhci-device for notify. + dev.lock().unwrap().xhci.lock().unwrap().ctrl_ops = + Some(Arc::downgrade(&dev) as Weak>); + // Attach to the PCI bus. + let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); + let mut locked_pci_bus = pci_bus.lock().unwrap(); + let pci_device = locked_pci_bus.devices.get(&devfn); + if pci_device.is_none() { + locked_pci_bus.devices.insert(devfn, dev.clone()); + } else { + bail!( + "Devfn {:?} has been used by {:?}", + &devfn, + pci_device.unwrap().lock().unwrap().name() + ); + } + Ok(()) + } + + fn unrealize(&mut self) -> PciResult<()> { + Ok(()) + } + + fn devfn(&self) -> Option { + Some(self.devfn) + } + + fn read_config(&self, offset: usize, data: &mut [u8]) { + let data_size = data.len(); + if offset + data_size > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { + error!( + "Failed to read pci config space at offset 0x{:x} with data size {}", + offset, data_size + ); + return; + } + self.pci_config.read(offset, data); + } + + fn write_config(&mut self, offset: usize, data: &[u8]) { + let data_size = data.len(); + let end = offset + data_size; + if end > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { + error!( + "Failed to write pci config space at offset 0x{:x} with data size {}", + offset, data_size + ); + return; + } + + self.pci_config + .write(offset, data, self.dev_id.clone().load(Ordering::Acquire)); + if ranges_overlap( + offset, + end, + BAR_0 as usize, + BAR_0 as usize + REG_SIZE as usize, + ) || ranges_overlap(offset, end, ROM_ADDRESS, ROM_ADDRESS + 4) + || ranges_overlap(offset, end, COMMAND as usize, COMMAND as usize + 1) + { + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + if let Err(e) = self.pci_config.update_bar_mapping( + #[cfg(target_arch = "x86_64")] + &locked_parent_bus.io_region, + &locked_parent_bus.mem_region, + ) { + error!("Failed to update bar, error is {}", e); + } + } + } + + fn name(&self) -> String { + self.name.clone() + } + + fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { + Ok(()) + } +} + +impl XhciOps for XhciPciDevice { + fn trigger_intr(&mut self, n: u32, _level: bool) -> bool { + if let Some(msix) = self.pci_config.msix.as_mut() { + msix.lock() + .unwrap() + .notify(n as u16, self.dev_id.load(Ordering::Acquire)); + true + } else { + error!("Failed to send interrupt: msix does not exist"); + false + } + } + + fn update_intr(&mut self, _n: u32, _enable: bool) { + warn!("XhciPciDevice update_intr not implemented"); + } +} + +impl BusDeviceOps for XhciPciDevice { + fn attach_device(&mut self, dev: &Arc>) -> Result<()> { + let mut locked_xhci = self.xhci.lock().unwrap(); + let usb_port = locked_xhci.bus.lock().unwrap().assign_usb_port(dev)?; + let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { + xhci_port + } else { + bail!("No xhci port found"); + }; + + locked_xhci.port_update(&xhci_port)?; + let mut locked_dev = dev.lock().unwrap(); + debug!( + "Attach usb device: xhci port name {} device id {}", + xhci_port.lock().unwrap().name, + locked_dev.device_id() + ); + locked_dev.handle_attach()?; + locked_dev.set_controller(Arc::downgrade(&self.xhci)); + Ok(()) + } + + fn detach_device(&mut self, _dev: &Arc>) -> Result<()> { + bail!("Detach usb device not implemented"); + } +} -- Gitee From c5ac034778280a8adace7433286411c5b4bfc142 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:28:20 +0800 Subject: [PATCH 0116/2187] xhci: implement region ops Now we implement region ops which registered in pci memory region. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 40 ++ usb/src/xhci/xhci_pci.rs | 53 ++- usb/src/xhci/xhci_regs.rs | 648 +++++++++++++++++++++++++++++++- 3 files changed, 733 insertions(+), 8 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 37d03e307..7b5655bdf 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::xhci::xhci_ring::XhciEventRingSeg; use std::mem::size_of; use std::slice::from_raw_parts; use std::slice::from_raw_parts_mut; @@ -436,6 +437,16 @@ impl XhciDevice { Ok(()) } + /// Control plane + pub fn handle_command(&mut self) -> Result<()> { + Ok(()) + } + + /// Data plane + pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { + Ok(()) + } + fn disable_slot(&mut self, slot_id: u32) -> Result { for i in 1..=self.slots[(slot_id - 1) as usize].endpoints.len() as u32 { self.disable_endpoint(slot_id, i)?; @@ -461,6 +472,35 @@ impl XhciDevice { Ok(TRBCCode::Success) } + /// Get microframe index + pub fn get_mf_index(&self) -> u64 { + warn!("get_mf_index not implemented"); + 0 + } + + pub fn update_mf(&self) { + warn!("update_mf not implemented"); + } + + pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { + let intr = &mut self.intrs[idx as usize]; + if intr.erstsz == 0 || intr.erstba == 0 { + intr.er_start = 0; + intr.er_size = 0; + return Ok(()); + } + let mut seg = XhciEventRingSeg::new(&self.mem_space); + seg.fetch_event_ring_seg(intr.erstba)?; + if seg.size < 16 || seg.size > 4096 { + bail!("Invalid segment size {}", seg.size); + } + intr.er_start = addr64_from_u32(seg.addr_lo, seg.addr_hi); + intr.er_size = seg.size; + intr.er_ep_idx = 0; + intr.er_pcs = true; + Ok(()) + } + /// Send event TRB to driver, first write TRB and then send interrupt. pub fn send_event(&mut self, evt: &XhciEvent, idx: u32) -> Result<()> { if idx > self.intrs.len() as u32 { diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index a35c79753..c77afa25c 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -18,7 +18,7 @@ use pci::config::{ PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, PCI_CLASS_SERIAL_USB, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::errors::Result as PciResult; +use pci::errors::{Result as PciResult, ResultExt as PciResultExt}; use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; use util::num_ops::round_up; use util::unix::host_page_size; @@ -27,7 +27,10 @@ use crate::bus::{BusDeviceMap, BusDeviceOps}; use crate::errors::Result; use crate::usb::UsbDeviceOps; use crate::xhci::xhci_controller::{XhciDevice, XhciOps, MAX_INTRS, MAX_SLOTS}; -use crate::xhci::xhci_regs::{XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME}; +use crate::xhci::xhci_regs::{ + build_cap_ops, build_doorbell_ops, build_oper_ops, build_port_ops, build_runtime_ops, + XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME, +}; const PCI_VENDOR_ID_NEC: u16 = 0x1033; const PCU_DEVICE_ID_NEC_UPD720200: u16 = 0x0194; @@ -85,6 +88,52 @@ impl XhciPciDevice { } fn mem_region_init(&mut self) -> PciResult<()> { + let cap_region = + Region::init_io_region(XHCI_PCI_CAP_LENGTH as u64, build_cap_ops(&self.xhci)); + PciResultExt::chain_err( + self.mem_region + .add_subregion(cap_region, XHCI_PCI_CAP_OFFSET as u64), + || "Failed to register cap region.", + )?; + + let oper_region = + Region::init_io_region(XHCI_PCI_OPER_LENGTH as u64, build_oper_ops(&self.xhci)); + PciResultExt::chain_err( + self.mem_region + .add_subregion(oper_region, XHCI_PCI_OPER_OFFSET as u64), + || "Failed to register oper region.", + )?; + + let runtime_region = Region::init_io_region( + XHCI_PCI_RUNTIME_LENGTH as u64, + build_runtime_ops(&self.xhci), + ); + PciResultExt::chain_err( + self.mem_region + .add_subregion(runtime_region, XHCI_PCI_RUNTIME_OFFSET as u64), + || "Failed to register runtime region.", + )?; + + let doorbell_region = Region::init_io_region( + XHCI_PCI_DOORBELL_LENGTH as u64, + build_doorbell_ops(&self.xhci), + ); + PciResultExt::chain_err( + self.mem_region + .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64), + || "Failed to register doorbell region.", + )?; + + let port_num = self.xhci.lock().unwrap().port_num; + for i in 0..port_num { + let port = &self.xhci.lock().unwrap().ports[i as usize]; + let port_region = + Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); + let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i) as u64; + PciResultExt::chain_err(self.mem_region.add_subregion(port_region, offset), || { + "Failed to register port region." + })?; + } Ok(()) } } diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 8c5e850a9..16df3d4f2 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -14,19 +14,91 @@ use crate::xhci::xhci_controller::dma_write_bytes; use crate::xhci::xhci_ring::XhciTRB; use std::sync::{Arc, Mutex, Weak}; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; +use util::num_ops::{read_u32, write_u64_high, write_u64_low}; use crate::config::*; use crate::errors::Result; use crate::usb::UsbPort; -use crate::xhci::xhci_controller::{XhciDevice, XhciEvent}; -use crate::xhci::xhci_ring::{TRB_C, TRB_SIZE}; +use crate::xhci::xhci_controller::{set_field, XhciDevice, XhciEvent}; +use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; /// Capability offset or size. -pub const XHCI_CAP_LENGTH: u32 = 0x40; -pub const XHCI_OFF_DOORBELL: u32 = 0x2000; -pub const XHCI_OFF_RUNTIME: u32 = 0x1000; +pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; +pub(crate) const XHCI_OFF_DOORBELL: u32 = 0x2000; +pub(crate) const XHCI_OFF_RUNTIME: u32 = 0x1000; +/// Capability Registers +/// Capability Register Length +const XHCI_CAP_REG_CAPLENGTH: u64 = 0x00; +/// Interface Version Number +const XHCI_CAP_REG_HCIVERSION: u64 = 0x02; +/// Structural Parameters 1 +const XHCI_CAP_REG_HCSPARAMS1: u64 = 0x04; +/// Structural Parameters 2 +const XHCI_CAP_REG_HCSPARAMS2: u64 = 0x08; +/// Structural Parameters 3 +const XHCI_CAP_REG_HCSPARAMS3: u64 = 0x0c; +/// Capability Parameters 1 +const XHCI_CAP_REG_HCCPARAMS1: u64 = 0x10; +/// Doorbell Offset +const XHCI_CAP_REG_DBOFF: u64 = 0x14; +/// Runtime Register Space Offset +const XHCI_CAP_REG_RTSOFF: u64 = 0x18; +const XHCI_VERSION: u32 = 0x100; +/// Number of Device Slots(MaxSlots) +const CAP_HCSP_NDS_SHIFT: u32 = 0; +/// Number of Interrupters(MaxIntrs) +const CAP_HCSP_NI_SHIFT: u32 = 8; +/// Number of Ports(MaxPorts) +const CAP_HCSP_NP_SHIFT: u32 = 24; +/// 64-bit Addressing Capability +const CAP_HCCP_AC64: u32 = 0x1; +/// xHCI Extended Capabilities Pointer +const CAP_HCCP_EXCP_SHIFT: u32 = 16; +/// Maximum Primary Stream Array Size +const CAP_HCCP_MPSAS_SHIFT: u32 = 12; +/// Extended Capability Code (Supported Protocol) +const CAP_EXT_CAP_ID_SUPPORT_PROTOCOL: u8 = 2; +/// xHCI Supported Protocol Capability (Name String) +const CAP_EXT_USB_NAME_STRING: u32 = 0x20425355; +/// Supported Protocol Capability (Major Revision and Minor Revision) +const CAP_EXT_REVISION_SHIFT: u32 = 16; +/// Next xHCI Extended Capability Pointer +const CAP_EXT_NEXT_CAP_POINTER_SHIFT: u32 = 8; +/// USB 2.0 +const CAP_EXT_USB_REVISION_2_0: u32 = 0x0200; +/// USB 3.0 +const CAP_EXT_USB_REVISION_3_0: u32 = 0x0300; +/// Operational Registers +const XHCI_OPER_REG_USBCMD: u64 = 0x00; +const XHCI_OPER_REG_USBSTS: u64 = 0x04; +const XHCI_OPER_REG_PAGESIZE: u64 = 0x08; +const XHCI_OPER_REG_DNCTRL: u64 = 0x14; +const XHCI_OPER_REG_CMD_RING_CTRL_LO: u64 = 0x18; +const XHCI_OPER_REG_CMD_RING_CTRL_HI: u64 = 0x1c; +const XHCI_OPER_REG_DCBAAP_LO: u64 = 0x30; +const XHCI_OPER_REG_DCBAAP_HI: u64 = 0x34; +const XHCI_OPER_REG_CONFIG: u64 = 0x38; +const XHCI_OPER_PAGESIZE: u32 = 1; +/// Interrupter Registers +const XHCI_INTR_REG_IMAN: u64 = 0x00; +const XHCI_INTR_REG_IMOD: u64 = 0x04; +const XHCI_INTR_REG_ERSTSZ: u64 = 0x08; +const XHCI_INTR_REG_ERSTBA_LO: u64 = 0x10; +const XHCI_INTR_REG_ERSTBA_HI: u64 = 0x14; +const XHCI_INTR_REG_ERDP_LO: u64 = 0x18; +const XHCI_INTR_REG_ERDP_HI: u64 = 0x1c; +const XHCI_INTR_REG_SIZE: u64 = 0x20; +/// Doorbell Register Bit Field +/// DB Target +const DB_TARGET_MASK: u32 = 0xff; +/// Port Registers +const XHCI_PORTSC: u64 = 0x0; +const XHCI_PORTPMSC: u64 = 0x4; +const XHCI_PORTLI: u64 = 0x8; +const XHCI_PORTHLPMC: u64 = 0xc; +const TRB_PORT_ID_SHIFT: u32 = 24; /// XHCI Operation Registers #[derive(Default, Copy, Clone)] @@ -166,4 +238,568 @@ impl XhciPort { name, } } + + pub fn reset(&mut self, warm_reset: bool) { + if let Some(usb_port) = self.usb_port.clone() { + let locked_port = usb_port.upgrade().unwrap(); + let speed = locked_port + .lock() + .unwrap() + .dev + .as_ref() + .unwrap() + .lock() + .unwrap() + .speed(); + if speed == USB_SPEED_SUPER && warm_reset { + self.portsc |= PORTSC_WRC; + } + match speed { + USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH => { + self.portsc = set_field(self.portsc, PLS_U0, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + self.portsc |= PORTSC_PED; + } + _ => { + error!("Invalid speed {}", speed); + } + } + self.portsc &= !PORTSC_PR; + if let Err(e) = self.notify(PORTSC_PRC) { + error!("Failed to notify {}", e); + } + } + } + + pub fn notify(&mut self, flag: u32) -> Result<()> { + if self.portsc & flag == flag { + return Ok(()); + } + let xhci = self.xhci.upgrade().unwrap(); + let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); + evt.ptr = (self.port_idx << TRB_PORT_ID_SHIFT) as u64; + xhci.lock().unwrap().send_event(&evt, 0)?; + Ok(()) + } +} + +/// Build capability region ops. +pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { + let xhci_dev = xhci_dev.clone(); + let cap_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + debug!("cap read {:x} {:x}", addr.0, offset); + let locked_dev = xhci_dev.lock().unwrap(); + let max_ports = locked_dev.numports_2 + locked_dev.numports_3; + let max_intrs = locked_dev.intrs.len() as u32; + let value = match offset { + XHCI_CAP_REG_CAPLENGTH => { + let hci_version_offset = XHCI_CAP_REG_HCIVERSION * 8; + XHCI_VERSION << hci_version_offset | XHCI_CAP_LENGTH + } + XHCI_CAP_REG_HCSPARAMS1 => { + max_ports << CAP_HCSP_NP_SHIFT + | max_intrs << CAP_HCSP_NI_SHIFT + | (locked_dev.slots.len() as u32) << CAP_HCSP_NDS_SHIFT + } + XHCI_CAP_REG_HCSPARAMS2 => { + // IST + 0xf + } + XHCI_CAP_REG_HCSPARAMS3 => 0x0, + XHCI_CAP_REG_HCCPARAMS1 => { + 0x8 << CAP_HCCP_EXCP_SHIFT | (0 << CAP_HCCP_MPSAS_SHIFT) | CAP_HCCP_AC64 + } + XHCI_CAP_REG_DBOFF => XHCI_OFF_DOORBELL, + XHCI_CAP_REG_RTSOFF => XHCI_OFF_RUNTIME, + // Extended capabilities (USB 2.0) + 0x20 => { + CAP_EXT_USB_REVISION_2_0 << CAP_EXT_REVISION_SHIFT + | 0x4 << CAP_EXT_NEXT_CAP_POINTER_SHIFT + | CAP_EXT_CAP_ID_SUPPORT_PROTOCOL as u32 + } + 0x24 => CAP_EXT_USB_NAME_STRING, + 0x28 => (locked_dev.numports_2 << 8) | 1, + 0x2c => 0x0, + // Extended capabilities (USB 3.0) + 0x30 => { + CAP_EXT_USB_REVISION_3_0 << CAP_EXT_REVISION_SHIFT + | CAP_EXT_CAP_ID_SUPPORT_PROTOCOL as u32 + } + 0x34 => CAP_EXT_USB_NAME_STRING, + 0x38 => (locked_dev.numports_3 << 8) | (locked_dev.numports_2 + 1), + 0x3c => 0x0, + _ => { + error!("Failed to read xhci cap: not implemented"); + 0 + } + }; + if let Err(e) = write_data(data, value) { + error!("Failed to write data when read oper registers: {}", e); + return false; + } + true + }; + + let cap_write = move |_data: &[u8], _addr: GuestAddress, offset: u64| -> bool { + error!( + "Failed to write cap register: addr {:?} offset {}", + _addr, offset + ); + true + }; + + RegionOps { + read: Arc::new(cap_read), + write: Arc::new(cap_write), + } +} + +/// Build operational region ops. +pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { + let xhci = xhci_dev.clone(); + let oper_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + debug!("oper read {:x} {:x}", addr.0, offset); + let locked_xhci = xhci.lock().unwrap(); + let value = match offset { + XHCI_OPER_REG_USBCMD => locked_xhci.oper.usb_cmd, + XHCI_OPER_REG_USBSTS => locked_xhci.oper.usb_status, + XHCI_OPER_REG_PAGESIZE => XHCI_OPER_PAGESIZE, + XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl, + XHCI_OPER_REG_CMD_RING_CTRL_LO => read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & !0xe, + XHCI_OPER_REG_CMD_RING_CTRL_HI => read_u32(locked_xhci.oper.cmd_ring_ctrl, 1), + XHCI_OPER_REG_DCBAAP_LO => read_u32(locked_xhci.oper.dcbaap, 0), + XHCI_OPER_REG_DCBAAP_HI => read_u32(locked_xhci.oper.dcbaap, 1), + XHCI_OPER_REG_CONFIG => locked_xhci.oper.config, + _ => { + error!( + "Invalid offset {:x} for reading operational registers.", + offset + ); + 0 + } + }; + if let Err(e) = write_data(data, value) { + error!("Failed to write data when read oper registers: {}", e); + return false; + } + true + }; + + let xhci = xhci_dev.clone(); + let oper_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + debug!("oper write {:x} {:x}", addr.0, offset); + let value = match read_data(data) { + Ok(v) => v, + Err(e) => { + error!("Failed to read data: offset 0x{:x}, {}", offset, e); + return false; + } + }; + let mut locked_xhci = xhci.lock().unwrap(); + match offset { + XHCI_OPER_REG_USBCMD => { + if (value & USB_CMD_RUN) == USB_CMD_RUN + && (locked_xhci.oper.usb_cmd & USB_CMD_RUN) != USB_CMD_RUN + { + locked_xhci.run(); + } else if (value & USB_CMD_RUN) != USB_CMD_RUN + && (locked_xhci.oper.usb_cmd & USB_CMD_RUN) == USB_CMD_RUN + { + locked_xhci.stop(); + } + if value & USB_CMD_CSS == USB_CMD_CSS { + locked_xhci.oper.usb_status &= !USB_STS_SRE; + } + if value & USB_CMD_CRS == USB_CMD_CRS { + locked_xhci.oper.usb_status |= USB_STS_SRE; + } + locked_xhci.oper.usb_cmd = value & 0xc0f; + locked_xhci.update_mf(); + if value & USB_CMD_HCRST == USB_CMD_HCRST { + locked_xhci.reset(); + } + locked_xhci.update_intr(0); + } + XHCI_OPER_REG_USBSTS => { + locked_xhci.oper.usb_status &= + !(value & (USB_STS_HSE | USB_STS_EINT | USB_STS_PCD | USB_STS_SRE)); + locked_xhci.update_intr(0); + } + XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl = value & 0xffff, + XHCI_OPER_REG_CMD_RING_CTRL_LO => { + let lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & CMD_RING_CTRL_CRR; + locked_xhci.oper.cmd_ring_ctrl = + write_u64_low(locked_xhci.oper.cmd_ring_ctrl, (value & 0xffffffcf) | lo); + } + XHCI_OPER_REG_CMD_RING_CTRL_HI => { + locked_xhci.oper.cmd_ring_ctrl = + write_u64_high(locked_xhci.oper.cmd_ring_ctrl, value); + let mut crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); + if crc_lo & (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) + == (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) + && (crc_lo & CMD_RING_CTRL_CRR) == CMD_RING_CTRL_CRR + { + let event = + XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::CommandRingStopped); + crc_lo &= !CMD_RING_CTRL_CRR; + locked_xhci.oper.cmd_ring_ctrl = + write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); + if let Err(e) = locked_xhci.send_event(&event, 0) { + error!("Failed to send event: {}", e); + } + } else { + let crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & !0x3f; + let crc_hi = ((value << 16) as u64) << 16; + let addr = crc_hi | crc_lo as u64; + locked_xhci.cmd_ring.init(addr); + } + crc_lo &= !(CMD_RING_CTRL_CA | CMD_RING_CTRL_CS); + locked_xhci.oper.cmd_ring_ctrl = + write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); + } + XHCI_OPER_REG_DCBAAP_LO => { + locked_xhci.oper.dcbaap = write_u64_low(locked_xhci.oper.dcbaap, value & 0xffffffc0) + } + XHCI_OPER_REG_DCBAAP_HI => { + locked_xhci.oper.dcbaap = write_u64_high(locked_xhci.oper.dcbaap, value) + } + XHCI_OPER_REG_CONFIG => locked_xhci.oper.config = value & 0xff, + _ => { + error!( + "Invalid offset {:x} for writing operational registers.", + offset + ); + return false; + } + }; + true + }; + + RegionOps { + read: Arc::new(oper_read), + write: Arc::new(oper_write), + } +} + +/// Build runtime region ops. +pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { + let xhci = xhci_dev.clone(); + let runtime_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + debug!("runtime read {:x} {:x}", addr.0, offset); + let mut value = 0; + if offset < 0x20 { + if offset == 0x0 { + value = (xhci.lock().unwrap().get_mf_index() & 0x3fff) as u32; + } else { + error!("Failed to read runtime registers, offset is {:x}", offset); + } + } else { + let idx = (offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE; + let mut xhci = xhci.lock().unwrap(); + let intr = &mut xhci.intrs[idx as usize]; + value = match offset & 0x1f { + XHCI_INTR_REG_IMAN => intr.iman, + XHCI_INTR_REG_IMOD => intr.imod, + XHCI_INTR_REG_ERSTSZ => intr.erstsz, + XHCI_INTR_REG_ERSTBA_LO => read_u32(intr.erstba, 0), + XHCI_INTR_REG_ERSTBA_HI => read_u32(intr.erstba, 1), + XHCI_INTR_REG_ERDP_LO => read_u32(intr.erdp, 0), + XHCI_INTR_REG_ERDP_HI => read_u32(intr.erdp, 1), + _ => { + error!( + "Invalid offset {:x} for reading interrupter registers.", + offset + ); + return false; + } + }; + } + if let Err(e) = write_data(data, value) { + error!("Failed to write data when read runtime registers: {}", e); + return false; + } + true + }; + + let xhci = xhci_dev.clone(); + let runtime_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + let value = match read_data(data) { + Ok(v) => v, + Err(e) => { + error!("Failed to read data: offset 0x{:x}, {}", offset, e); + return false; + } + }; + debug!("runtime write {:x} {:x} {:x}", addr.0, offset, value); + if offset < 0x20 { + error!("runtime write not implemented: offset {}", offset); + } + let mut xhci = xhci.lock().unwrap(); + let idx = ((offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE) as u32; + if idx > xhci.intrs.len() as u32 { + error!("Invalid interrupter index: {} idx {}", offset, idx); + return true; + } + let intr = &mut xhci.intrs[idx as usize]; + match offset & 0x1f { + XHCI_INTR_REG_IMAN => { + if value & IMAN_IP == IMAN_IP { + intr.iman &= !IMAN_IP; + } + intr.iman &= !IMAN_IE; + intr.iman |= value & IMAN_IE; + if idx == 0 { + xhci.update_intr(idx); + } + } + XHCI_INTR_REG_IMOD => intr.imod = value, + XHCI_INTR_REG_ERSTSZ => intr.erstsz = value & 0xffff, + XHCI_INTR_REG_ERSTBA_LO => { + intr.erstba = write_u64_low(intr.erstba, value & 0xfffffff0); + } + XHCI_INTR_REG_ERSTBA_HI => { + intr.erstba = write_u64_high(intr.erstba, value); + if let Err(e) = xhci.reset_event_ring(idx) { + error!("Failed to reset event ring: {}", e); + } + } + XHCI_INTR_REG_ERDP_LO => { + let mut erdp_lo = read_u32(intr.erdp, 0); + if value & ERDP_EHB == ERDP_EHB { + erdp_lo &= !ERDP_EHB; + } + erdp_lo = (value & !ERDP_EHB) | (erdp_lo & ERDP_EHB); + intr.erdp = write_u64_low(intr.erdp, erdp_lo); + if value & ERDP_EHB == ERDP_EHB { + let erdp = intr.erdp & 0x0000_FFFF_FFFF_FFFF_u64; + let dp_idx = (erdp - intr.er_start) / TRB_SIZE as u64; + if erdp >= intr.er_start + && erdp < intr.er_start + (TRB_SIZE * intr.er_size) as u64 + && dp_idx != intr.er_ep_idx as u64 + { + xhci.send_intr(idx); + } + } + } + XHCI_INTR_REG_ERDP_HI => { + intr.erdp = write_u64_high(intr.erdp, value); + } + _ => { + error!( + "Invalid offset {:x} for writing interrupter registers.", + offset + ); + } + }; + true + }; + + RegionOps { + read: Arc::new(runtime_read), + write: Arc::new(runtime_write), + } +} + +/// Build doorbeell region ops. +pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { + let doorbell_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + debug!("doorbell read addr {:x} offset {:x}", addr.0, offset); + if let Err(e) = write_data(data, 0) { + error!("Failed to write data: {}", e); + return false; + } + true + }; + let xhci = xhci_dev.clone(); + let doorbell_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + let value = match read_data(data) { + Ok(v) => v, + Err(e) => { + error!("Failed to read data: offset 0x{:x}, {}", offset, e); + return false; + } + }; + debug!("doorbell write {:x} {:x}", addr.0, offset); + if !xhci.lock().unwrap().running() { + error!("Failed to write doorbell, XHCI is not running"); + return true; + } + let mut xhci = xhci.lock().unwrap(); + let slot_id = (offset >> 2) as u32; + if slot_id == 0 { + if value == 0 { + if let Err(e) = xhci.handle_command() { + error!("Failed to process commands: {}", e); + } + } else { + error!("Invalid doorbell write: value {:x}", value) + } + } else { + let ep_id = value & DB_TARGET_MASK; + if slot_id > xhci.slots.len() as u32 { + error!("Invalid slot_id {}", slot_id); + } else if ep_id == 0 || ep_id > 31 { + error!("Invalid epid {}", ep_id,); + } else if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { + error!("Failed to kick endpoint: {}", e); + } + } + true + }; + + RegionOps { + read: Arc::new(doorbell_read), + write: Arc::new(doorbell_write), + } +} + +/// Build port region ops. +pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { + let port = xhci_port.clone(); + let port_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + debug!("port read {:x} {:x}", addr.0, offset); + let locked_port = port.lock().unwrap(); + let value = match offset { + XHCI_PORTSC => locked_port.portsc, + XHCI_PORTPMSC => 0, + XHCI_PORTLI => 0, + XHCI_PORTHLPMC => 0, + _ => { + error!("Faield to read port register: offset {:x}", offset); + 0 + } + }; + if let Err(e) = write_data(data, value) { + error!("Failed to write data: {}", e); + return false; + } + true + }; + + let port = xhci_port.clone(); + let port_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + let value = match read_data(data) { + Ok(v) => v, + Err(e) => { + error!("Failed to read data: offset 0x{:x}, {}", offset, e); + return false; + } + }; + let mut locked_port = port.lock().unwrap(); + debug!( + "port write {} {:x} {:x} {:x}", + locked_port.name, addr.0, offset, value + ); + + #[allow(clippy::never_loop)] + loop { + match offset { + XHCI_PORTSC => { + if value & PORTSC_WPR == PORTSC_WPR { + locked_port.reset(true); + break; + } + if value & PORTSC_PR == PORTSC_PR { + locked_port.reset(false); + break; + } + + let mut portsc = locked_port.portsc; + let mut notify = 0; + portsc &= !(value + & (PORTSC_CSC + | PORTSC_PEC + | PORTSC_WRC + | PORTSC_OCC + | PORTSC_PRC + | PORTSC_PLC + | PORTSC_CEC)); + if value & PORTSC_LWS == PORTSC_LWS { + let old_pls = (locked_port.portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + let new_pls = (value >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + match new_pls { + PLS_U0 => { + if old_pls != PLS_U0 { + portsc = set_field( + portsc, + new_pls, + PORTSC_PLS_MASK, + PORTSC_PLS_SHIFT, + ); + notify = PORTSC_PLC; + } + } + PLS_U3 => { + if old_pls < PLS_U3 { + portsc = set_field( + portsc, + new_pls, + PORTSC_PLS_MASK, + PORTSC_PLS_SHIFT, + ); + } + } + PLS_RESUME => {} + _ => { + error!("Invalid port link state, ignore the write."); + } + } + } + portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); + portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); + locked_port.portsc = portsc; + + if notify != 0 { + if let Err(e) = locked_port.notify(notify) { + error!("Failed to notify: {}", e); + } + } + } + XHCI_PORTPMSC => (), + XHCI_PORTLI => (), + XHCI_PORTHLPMC => (), + _ => { + error!("Invalid port link state offset {}", offset); + } + } + break; + } + true + }; + + RegionOps { + read: Arc::new(port_read), + write: Arc::new(port_write), + } +} + +fn write_data(data: &mut [u8], value: u32) -> Result<()> { + match data.len() { + 1 => data[0] = value as u8, + 2 => { + LittleEndian::write_u16(data, value as u16); + } + 4 => { + LittleEndian::write_u32(data, value); + } + _ => { + bail!( + "Invalid data length: value {}, data len {}", + value, + data.len() + ); + } + }; + Ok(()) +} + +fn read_data(data: &[u8]) -> Result { + let value = match data.len() { + 1 => data[0] as u32, + 2 => LittleEndian::read_u16(data) as u32, + 4 => LittleEndian::read_u32(data), + _ => { + bail!("Invalid data length: data len {}", data.len()); + } + }; + Ok(value) } -- Gitee From 1fc6789fe66933bef8d39136dd7b8f44a14cef51 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:43:57 +0800 Subject: [PATCH 0117/2187] usb: add descriptor Implement usb descriptor prepares the control plane and data plane for later. Signed-off-by: zhouli57 --- usb/src/descriptor.rs | 409 ++++++++++++++++++++++++++++++++++++++++++ usb/src/lib.rs | 1 + usb/src/usb.rs | 292 ++++++++++++++++++++++++++++++ 3 files changed, 702 insertions(+) create mode 100644 usb/src/descriptor.rs diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs new file mode 100644 index 000000000..55bec1af2 --- /dev/null +++ b/usb/src/descriptor.rs @@ -0,0 +1,409 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::Arc; + +use util::byte_code::ByteCode; + +use super::errors::Result; +use crate::config::*; +use crate::usb::{UsbDescConfig, UsbDescEndpoint, UsbDescIface, UsbDevice}; + +/// USB device descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbDeviceDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bcdUSB: u16, + pub bDeviceClass: u8, + pub bDeviceSubClass: u8, + pub bDeviceProtocol: u8, + pub bMaxPacketSize0: u8, + pub idVendor: u16, + pub idProduct: u16, + pub bcdDevice: u16, + pub iManufacturer: u8, + pub iProduct: u8, + pub iSerialNumber: u8, + pub bNumConfigurations: u8, +} + +impl ByteCode for UsbDeviceDescriptor {} + +/// USB config descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbConfigDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub wTotalLength: u16, + pub bNumInterfaces: u8, + pub bConfigurationValue: u8, + pub iConfiguration: u8, + pub bmAttributes: u8, + pub bMaxPower: u8, +} + +impl ByteCode for UsbConfigDescriptor {} + +/// USB interface descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbInterfaceDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bInterfaceNumber: u8, + pub bAlternateSetting: u8, + pub bNumEndpoints: u8, + pub bInterfaceClass: u8, + pub bInterfaceSubClass: u8, + pub bInterfaceProtocol: u8, + pub iInterface: u8, +} + +impl ByteCode for UsbInterfaceDescriptor {} + +/// USB endpoint descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbEndpointDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bEndpointAddress: u8, + pub bmAttributes: u8, + pub wMaxPacketSize: u16, + pub bInterval: u8, +} + +impl ByteCode for UsbEndpointDescriptor {} + +/// USB qualifier descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UsbQualifierDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bcdUSB: u16, + pub bDeviceClass: u8, + pub bDeviceSubClass: u8, + pub bDeviceProtocol: u8, + pub bMaxPacketSize0: u8, + pub bNumConfigurations: u8, + pub bRESERVED: u8, +} + +impl ByteCode for UsbQualifierDescriptor {} + +/// USB string descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UsbStringDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub wData: [u16; 1], +} + +impl ByteCode for UsbStringDescriptor {} + +/// USB descriptor ops including get/set descriptor. +pub trait UsbDescriptorOps { + fn get_descriptor(&self, value: u32) -> Result>; + + fn get_device_descriptor(&self) -> Result>; + + fn get_config_descriptor(&self, conf: &UsbDescConfig) -> Result>; + + fn get_interface_descriptor(&self, iface: &UsbDescIface) -> Result>; + + fn get_endpoint_descriptor(&self, ep: &UsbDescEndpoint) -> Result>; + + fn get_string_descriptor(&self, index: u32) -> Result>; + + fn set_config_descriptor(&mut self, v: u8) -> Result<()>; + + fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()>; + + fn find_interface(&self, nif: u32, alt: u32) -> Option>; + + fn init_endpoint(&mut self) -> Result<()>; + + fn set_default_descriptor(&mut self) -> Result<()>; + + fn init_descriptor(&mut self) -> Result<()>; +} + +impl UsbDescriptorOps for UsbDevice { + fn get_descriptor(&self, value: u32) -> Result> { + let desc_type = value >> 8; + let index = value & 0xff; + + let device_desc = if let Some(desc) = self.device_desc.as_ref() { + desc + } else { + bail!("Device descriptor not found"); + }; + + let conf = &device_desc.as_ref().confs; + let vec = match desc_type as u8 { + USB_DT_DEVICE => self.get_device_descriptor()?, + USB_DT_CONFIGURATION => self.get_config_descriptor(conf[index as usize].as_ref())?, + USB_DT_STRING => self.get_string_descriptor(index)?, + _ => { + bail!("Unknown descriptor type {}", desc_type); + } + }; + Ok(vec) + } + + fn get_device_descriptor(&self) -> Result> { + if let Some(desc) = self.device_desc.as_ref() { + Ok(desc.device_desc.as_bytes().to_vec()) + } else { + bail!("Device descriptor not found"); + } + } + + fn get_config_descriptor(&self, conf: &UsbDescConfig) -> Result> { + let mut config_desc = conf.config_desc; + let mut total = config_desc.bLength as u16; + let mut ifs = Vec::new(); + for i in 0..conf.ifs.len() { + let mut iface = self.get_interface_descriptor(conf.ifs[i].as_ref())?; + total += iface.len() as u16; + ifs.append(&mut iface); + } + config_desc.wTotalLength = total; + let mut buf = config_desc.as_bytes().to_vec(); + buf.append(&mut ifs); + Ok(buf) + } + + fn get_interface_descriptor(&self, iface: &UsbDescIface) -> Result> { + let desc = iface.interface_desc; + let mut buf = desc.as_bytes().to_vec(); + for i in 0..iface.other_desc.len() { + let desc = iface.other_desc[i].as_ref(); + for x in &desc.data { + buf.push(*x); + } + } + for i in 0..desc.bNumEndpoints as usize { + let mut ep = self.get_endpoint_descriptor(iface.eps[i].as_ref())?; + buf.append(&mut ep); + } + Ok(buf) + } + + fn get_endpoint_descriptor(&self, ep: &UsbDescEndpoint) -> Result> { + let desc = ep.endpoint_desc; + Ok(desc.as_bytes().to_vec()) + } + + fn get_string_descriptor(&self, index: u32) -> Result> { + if index == 0 { + // Language ID + let str: [u8; 4] = [4, 3, 9, 4]; + return Ok(str.to_vec()); + } + let mut found_str = String::new(); + for str in &self.strings { + if str.index == index { + found_str = str.str.clone(); + break; + } + } + if found_str.is_empty() { + found_str = if let Some(desc) = self.usb_desc.as_ref() { + desc.strings[index as usize].clone() + } else { + bail!("No usb desc found."); + } + } + let len = found_str.len() as u8 * 2 + 2; + let mut vec = vec![0_u8; len as usize]; + vec[0] = len; + vec[1] = USB_DT_STRING; + + let mut pos = 2; + for i in 0..found_str.len() { + vec[pos] = found_str.as_bytes()[i]; + vec[pos + 1] = 0; + pos += 2; + } + Ok(vec) + } + + fn set_config_descriptor(&mut self, v: u8) -> Result<()> { + if v == 0 { + self.configuration = 0; + self.ninterfaces = 0; + self.config = None; + } else { + let desc = if let Some(desc) = &self.device_desc { + desc + } else { + bail!("Device Desc is None."); + }; + let num = desc.device_desc.bNumConfigurations; + let desc = desc.as_ref(); + for i in 0..num as usize { + if desc.confs[i].config_desc.bConfigurationValue == v { + self.configuration = v as u32; + self.ninterfaces = desc.confs[i].config_desc.bNumInterfaces as u32; + self.config = Some(desc.confs[i].clone()); + } + } + } + for i in 0..self.ninterfaces { + self.set_interface_descriptor(i, 0)?; + } + for i in self.altsetting.iter_mut() { + *i = 0; + } + Ok(()) + } + + fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()> { + let iface = if let Some(face) = self.find_interface(index, v) { + face + } else { + bail!("Interface not found."); + }; + self.altsetting[index as usize] = v; + self.ifaces[index as usize] = Some(iface); + self.init_endpoint()?; + Ok(()) + } + + fn find_interface(&self, nif: u32, alt: u32) -> Option> { + self.config.as_ref()?; + let conf = if let Some(conf) = self.config.as_ref() { + conf + } else { + error!("No config descriptor found"); + return None; + }; + for group in conf.if_groups.iter() { + for iface in group.ifs.iter() { + if iface.interface_desc.bInterfaceNumber == nif as u8 + && iface.interface_desc.bAlternateSetting == alt as u8 + { + return Some(iface.clone()); + } + } + } + for i in 0..conf.ifs.len() { + let iface = conf.ifs[i].clone(); + if iface.interface_desc.bInterfaceNumber == nif as u8 + && iface.interface_desc.bAlternateSetting == alt as u8 + { + return Some(iface); + } + } + None + } + + fn init_endpoint(&mut self) -> Result<()> { + self.init_usb_endpoint(); + for i in 0..self.ninterfaces { + let iface = self.ifaces[i as usize].as_ref(); + if iface.is_none() { + continue; + } + let iface = if let Some(iface) = iface { + iface + } else { + bail!("No interface descriptor found."); + }; + for e in 0..iface.interface_desc.bNumEndpoints { + let pid = if iface.eps[e as usize].endpoint_desc.bEndpointAddress + & USB_DIRECTION_DEVICE_TO_HOST + == USB_DIRECTION_DEVICE_TO_HOST + { + USB_TOKEN_IN + } else { + USB_TOKEN_OUT + }; + + let ep = iface.eps[e as usize].endpoint_desc.bEndpointAddress & 0x0f; + let usb_ep = self.get_endpoint(pid as u32, ep as u32); + let mut locked_usb_ep = usb_ep.lock().unwrap(); + let usb_type = iface.eps[e as usize].endpoint_desc.bmAttributes & 0x03; + locked_usb_ep.usb_type = usb_type; + locked_usb_ep.ifnum = iface.interface_desc.bInterfaceNumber; + let raw = iface.eps[e as usize].endpoint_desc.wMaxPacketSize; + let size = raw & 0x7ff; + let v = (size >> 11) & 3; + let microframes = if v == 1 { + 2 + } else if v == 2 { + 3 + } else { + 1 + }; + locked_usb_ep.max_packet_size = size as u32 * microframes; + } + } + Ok(()) + } + + fn set_default_descriptor(&mut self) -> Result<()> { + if let Some(desc) = &self.usb_desc { + match self.speed { + USB_SPEED_LOW | USB_SPEED_FULL => { + self.device_desc = desc.full_dev.clone(); + } + USB_SPEED_HIGH => { + self.device_desc = desc.high_dev.clone(); + } + USB_SPEED_MASK_SUPER => { + self.device_desc = desc.super_dev.clone(); + } + _ => { + bail!("Unknown device speed."); + } + } + } + self.set_config_descriptor(0)?; + Ok(()) + } + + fn init_descriptor(&mut self) -> Result<()> { + let desc = if let Some(desc) = &self.usb_desc { + desc.clone() + } else { + bail!("Usb descriptor is None"); + }; + + self.speed = USB_SPEED_FULL; + self.speed_mask = 0; + + if desc.full_dev.is_some() { + self.speed_mask |= USB_SPEED_MASK_FULL; + } + if desc.high_dev.is_some() { + self.speed_mask |= USB_SPEED_MASK_HIGH; + } + if desc.super_dev.is_some() { + self.speed_mask |= USB_SPEED_MASK_SUPER; + } + self.set_default_descriptor()?; + Ok(()) + } +} diff --git a/usb/src/lib.rs b/usb/src/lib.rs index bd3732751..123d143b5 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -31,5 +31,6 @@ pub mod errors { pub mod bus; pub mod config; +mod descriptor; pub mod usb; pub mod xhci; diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 375bc1f53..6603f23f3 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -16,8 +16,16 @@ use std::{ }; use super::errors::Result; +use crate::config::*; +use crate::descriptor::{ + UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, + UsbInterfaceDescriptor, +}; use crate::xhci::xhci_controller::XhciDevice; +const USB_MAX_ENDPOINTS: u32 = 15; +const USB_MAX_INTERFACES: u32 = 16; + /// USB packet return status. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum UsbPacketStatus { @@ -139,6 +147,59 @@ pub enum UsbPacketState { Canceled, } +// USB descriptor +pub struct UsbDesc { + pub full_dev: Option>, + pub high_dev: Option>, + pub super_dev: Option>, + pub strings: Vec, +} + +// USB device descriptor +pub struct UsbDescDevice { + pub device_desc: UsbDeviceDescriptor, + pub confs: Vec>, +} + +// USB config descriptor +pub struct UsbDescConfig { + pub config_desc: UsbConfigDescriptor, + pub if_groups: Vec>, + pub ifs: Vec>, +} + +// USB interface descriptor +pub struct UsbDescIface { + pub interface_desc: UsbInterfaceDescriptor, + pub other_desc: Vec>, + pub eps: Vec>, +} + +/* conceptually an Interface Association Descriptor, and related interfaces */ +#[allow(non_snake_case)] +#[repr(C)] +pub struct UsbDescIfaceAssoc { + pub bFirstInterface: u8, + pub bInterfaceCount: u8, + pub bFunctionClass: u8, + pub bFunctionSubClass: u8, + pub bFunctionProtocol: u8, + pub iFunction: u8, + pub ifs: Vec>, +} + +// USB other descriptor +pub struct UsbDescOther { + pub length: u8, + pub data: Vec, +} + +// USB endpoint descriptor +pub struct UsbDescEndpoint { + pub endpoint_desc: UsbEndpointDescriptor, + pub extra: Option>, +} + /// USB device common structure. pub struct UsbDevice { pub port: Option>>, @@ -158,6 +219,237 @@ pub struct UsbDevice { pub ep_ctl: Arc>, pub ep_in: Vec>>, pub ep_out: Vec>>, + /// USB descriptor + pub strings: Vec, + pub usb_desc: Option>, + pub device_desc: Option>, + pub configuration: u32, + pub ninterfaces: u32, + pub altsetting: Vec, + pub config: Option>, + pub ifaces: Vec>>, +} + +impl UsbDevice { + pub fn new() -> Self { + let mut dev = UsbDevice { + port: None, + attached: false, + speed: 0, + speed_mask: 0, + addr: 0, + ep_ctl: Arc::new(Mutex::new(UsbEndpoint::new( + 0, + 0, + USB_ENDPOINT_ATTR_CONTROL, + 0, + 64, + ))), + ep_in: Vec::new(), + ep_out: Vec::new(), + product_desc: String::new(), + auto_attach: false, + strings: Vec::new(), + usb_desc: None, + device_desc: None, + configuration: 0, + ninterfaces: 0, + config: None, + altsetting: vec![0; USB_MAX_INTERFACES as usize], + state: UsbDeviceState::Removed, + setup_buf: vec![0_u8; 8], + data_buf: vec![0_u8; 4096], + ifaces: vec![None; USB_MAX_INTERFACES as usize], + remote_wakeup: 0, + setup_index: 0, + setup_len: 0, + setup_state: SetupState::Idle, + }; + + for i in 0..USB_MAX_ENDPOINTS as u8 { + dev.ep_in.push(Arc::new(Mutex::new(UsbEndpoint::new( + i + 1, + USB_TOKEN_IN, + USB_ENDPOINT_ATTR_INVALID, + USB_INTERFACE_INVALID, + 0, + )))); + dev.ep_out.push(Arc::new(Mutex::new(UsbEndpoint::new( + i + 1, + USB_TOKEN_OUT, + USB_ENDPOINT_ATTR_INVALID, + USB_INTERFACE_INVALID, + 0, + )))); + } + dev + } + + pub fn get_endpoint(&self, pid: u32, ep: u32) -> Arc> { + if ep == 0 { + return self.ep_ctl.clone(); + } + if pid as u8 == USB_TOKEN_IN { + self.ep_in[(ep - 1) as usize].clone() + } else { + self.ep_out[(ep - 1) as usize].clone() + } + } + + pub fn init_usb_endpoint(&mut self) { + self.reset_usb_endpoint(); + let mut ep_ctl = self.ep_ctl.lock().unwrap(); + ep_ctl.queue = LinkedList::new(); + for i in 0..USB_MAX_ENDPOINTS { + let mut ep_in = self.ep_in[i as usize].lock().unwrap(); + let mut ep_out = self.ep_out[i as usize].lock().unwrap(); + ep_in.queue = LinkedList::new(); + ep_out.queue = LinkedList::new(); + } + } + + pub fn reset_usb_endpoint(&mut self) { + let mut ep_ctl = self.ep_ctl.lock().unwrap(); + ep_ctl.nr = 0; + ep_ctl.usb_type = USB_ENDPOINT_ATTR_CONTROL; + ep_ctl.ifnum = 0; + ep_ctl.max_packet_size = 64; + ep_ctl.pipeline = false; + for i in 0..USB_MAX_ENDPOINTS { + let mut ep_in = self.ep_in[i as usize].lock().unwrap(); + let mut ep_out = self.ep_out[i as usize].lock().unwrap(); + ep_in.nr = (i + 1) as u8; + ep_out.nr = (i + 1) as u8; + ep_in.pid = USB_TOKEN_IN; + ep_out.pid = USB_TOKEN_OUT; + ep_in.usb_type = USB_ENDPOINT_ATTR_INVALID; + ep_out.usb_type = USB_ENDPOINT_ATTR_INVALID; + ep_in.ifnum = USB_INTERFACE_INVALID; + ep_out.ifnum = USB_INTERFACE_INVALID; + ep_in.max_packet_size = 0; + ep_out.max_packet_size = 0; + ep_in.pipeline = false; + ep_out.pipeline = false; + } + } + + pub fn handle_control_for_descriptor( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ) -> Result<()> { + let value = device_req.value as u32; + let index = device_req.index as u32; + let length = device_req.length as u32; + match device_req.request_type { + USB_DEVICE_IN_REQUEST => match device_req.request { + USB_REQUEST_GET_DESCRIPTOR => { + let res = self.get_descriptor(value)?; + let len = std::cmp::min(res.len() as u32, length); + data[..(len as usize)].clone_from_slice(&res[..(len as usize)]); + packet.actual_length = len; + } + USB_REQUEST_GET_CONFIGURATION => { + data[0] = if let Some(conf) = &self.config { + conf.config_desc.bConfigurationValue + } else { + 0 + }; + packet.actual_length = 1; + } + USB_REQUEST_GET_STATUS => { + let conf = if let Some(conf) = &self.config { + conf.clone() + } else { + let x = &self.device_desc.as_ref().unwrap().confs[0]; + x.clone() + }; + data[0] = 0; + if conf.config_desc.bmAttributes & USB_CONFIGURATION_ATTR_SELF_POWER + == USB_CONFIGURATION_ATTR_SELF_POWER + { + data[0] |= 1 << USB_DEVICE_SELF_POWERED; + } + + if self.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP { + data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; + } + data[1] = 0x00; + packet.actual_length = 2; + } + _ => { + bail!( + "Unhandled request: type {} request {}", + device_req.request_type, + device_req.request + ); + } + }, + USB_DEVICE_OUT_REQUEST => match device_req.request { + USB_REQUEST_SET_ADDRESS => { + self.addr = value as u8; + } + USB_REQUEST_SET_CONFIGURATION => { + return self.set_config_descriptor(value as u8); + } + USB_REQUEST_CLEAR_FEATURE => { + if value == USB_DEVICE_REMOTE_WAKEUP { + self.remote_wakeup = 0; + } + } + USB_REQUEST_SET_FEATURE => { + if value == USB_DEVICE_REMOTE_WAKEUP { + self.remote_wakeup = 1; + } + } + _ => { + bail!( + "Unhandled request: type {} request {}", + device_req.request_type, + device_req.request + ); + } + }, + USB_INTERFACE_IN_REQUEST => match device_req.request { + USB_REQUEST_GET_INTERFACE => { + if index < self.ninterfaces { + data[0] = self.altsetting[index as usize] as u8; + packet.actual_length = 1; + } + } + _ => { + bail!( + "Unhandled request: type {} request {}", + device_req.request_type, + device_req.request + ); + } + }, + USB_INTERFACE_OUT_REQUEST => match device_req.request { + USB_REQUEST_SET_INTERFACE => { + return self.set_interface_descriptor(index, value); + } + _ => { + bail!( + "Unhandled request: type {} request {}", + device_req.request_type, + device_req.request + ); + } + }, + _ => { + bail!("Unhandled request: type {}", device_req.request_type); + } + } + Ok(()) + } +} + +impl Default for UsbDevice { + fn default() -> Self { + Self::new() + } } /// UsbDeviceOps is the interface for USB device. -- Gitee From 0527795d4c36e4328046fe46c756e5317901c551 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 14 Aug 2022 22:22:04 +0800 Subject: [PATCH 0118/2187] xhci: implement control plane We implement xhci control plane ops. So the usb controller can handle commands from the drivers. Signed-off-by: zhouli57 --- usb/src/usb.rs | 61 ++++ usb/src/xhci/xhci_controller.rs | 488 +++++++++++++++++++++++++++++++- 2 files changed, 543 insertions(+), 6 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 6603f23f3..3a079f840 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -99,6 +99,36 @@ impl UsbEndpoint { queue: LinkedList::new(), } } + + pub fn get_ep_id(&self) -> u8 { + if self.nr == 0 { + // Control endpoint + 1 + } else if self.pid == USB_TOKEN_IN { + self.nr * 2 + 1 + } else { + self.nr * 2 + } + } +} + +/// Init USB endpoint, similar with init_usb_endpoint, but set dev in endpoint. +pub fn usb_endpoint_init(dev: &Arc>) { + let mut locked_dev = dev.lock().unwrap(); + let usb_dev = locked_dev.get_mut_usb_device(); + let mut locked_dev = usb_dev.lock().unwrap(); + locked_dev.reset_usb_endpoint(); + let mut ep_ctl = locked_dev.ep_ctl.lock().unwrap(); + ep_ctl.dev = Some(Arc::downgrade(dev)); + ep_ctl.queue = LinkedList::new(); + for i in 0..USB_MAX_ENDPOINTS { + let mut ep_in = locked_dev.ep_in[i as usize].lock().unwrap(); + let mut ep_out = locked_dev.ep_out[i as usize].lock().unwrap(); + ep_in.queue = LinkedList::new(); + ep_out.queue = LinkedList::new(); + ep_in.dev = Some(Arc::downgrade(dev)); + ep_out.dev = Some(Arc::downgrade(dev)); + } } /// USB port which can attached device. @@ -533,6 +563,37 @@ pub struct UsbPacket { pub state: UsbPacketState, } +impl std::fmt::Display for UsbPacket { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "pid {} id {} param {} status {:?} actual_length {}, state {:?}", + self.pid, self.id, self.parameter, self.status, self.actual_length, self.state + ) + } +} + +impl UsbPacket { + pub fn init( + &mut self, + pid: u32, + ep: Weak>, + id: u64, + short_or_ok: bool, + int_req: bool, + ) { + self.id = id; + self.pid = pid; + self.ep = Some(ep); + self.status = UsbPacketStatus::Success; + self.actual_length = 0; + self.parameter = 0; + self.short_not_ok = short_or_ok; + self.int_req = int_req; + self.state = UsbPacketState::Setup; + } +} + impl Default for UsbPacket { fn default() -> UsbPacket { UsbPacket { diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 7b5655bdf..1209aa57c 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::xhci::xhci_ring::XhciEventRingSeg; use std::mem::size_of; use std::slice::from_raw_parts; use std::slice::from_raw_parts_mut; @@ -23,9 +22,14 @@ use util::num_ops::{read_u32, write_u64_low}; use crate::bus::UsbBus; use crate::config::*; use crate::errors::{Result, ResultExt}; -use crate::usb::{UsbPacket, UsbPort}; +use crate::usb::{ + Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, UsbPort, +}; use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter, XhciPort}; -use crate::xhci::xhci_ring::{TRBCCode, TRBType, XhciRing, XhciTRB, TRB_SIZE, TRB_TYPE_SHIFT}; +use crate::xhci::xhci_ring::{ + TRBCCode, TRBType, XhciEventRingSeg, XhciRing, XhciTRB, TRB_EV_ED, TRB_SIZE, TRB_TR_IDT, + TRB_TR_IOC, TRB_TR_ISP, TRB_TYPE_SHIFT, +}; pub const MAX_INTRS: u16 = 16; pub const MAX_SLOTS: u32 = 64; @@ -40,6 +44,26 @@ const EP_TYPE_SHIFT: u32 = 3; const EP_TYPE_MASK: u32 = 0x7; #[allow(unused)] const EP_ERROR: u32 = 4; +/// Slot state +const SLOT_STATE_MASK: u32 = 0x1f; +const SLOT_STATE_SHIFT: u32 = 27; +#[allow(unused)] +const SLOT_ENABLED: u32 = 0; +const SLOT_DEFAULT: u32 = 1; +const SLOT_ADDRESSED: u32 = 2; +const SLOT_CONFIGURED: u32 = 3; +const SLOT_CONTEXT_ENTRIES_MASK: u32 = 0x1f; +const SLOT_CONTEXT_ENTRIES_SHIFT: u32 = 27; +/// TRB flags +const TRB_CR_BSR: u32 = 1 << 9; +const TRB_CR_EPID_SHIFT: u32 = 16; +const TRB_CR_EPID_MASK: u32 = 0x1f; +const TRB_INTR_SHIFT: u32 = 22; +const TRB_INTR_MASK: u32 = 0x3ff; +const TRB_CR_DC: u32 = 1 << 9; +const TRB_CR_SLOTID_SHIFT: u32 = 24; +const TRB_CR_SLOTID_MASK: u32 = 0xff; +const COMMAND_LIMIT: u32 = 256; const EP_CTX_MAX_PACKET_SIZE_SHIFT: u32 = 16; const EP_CTX_LSA_SHIFT: u32 = 15; const EP_CTX_INTERVAL_SHIFT: u32 = 16; @@ -48,6 +72,7 @@ const EVENT_TRB_CCODE_SHIFT: u32 = 24; const EVENT_TRB_SLOT_ID_SHIFT: u32 = 24; const EVENT_TRB_EP_ID_SHIFT: u32 = 16; const PORT_EVENT_ID_SHIFT: u32 = 24; +const SLOT_CTX_PORT_NUMBER_SHIFT: u32 = 16; type DmaAddr = u64; @@ -437,14 +462,130 @@ impl XhciDevice { Ok(()) } + fn get_slot_id(&self, evt: &mut XhciEvent, trb: &XhciTRB) -> u32 { + let slot_id = (trb.control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK; + if slot_id < 1 || slot_id > self.slots.len() as u32 { + error!("Failed to get slot id, slot {} out of range", slot_id); + evt.ccode = TRBCCode::TrbError; + return 0; + } else if !self.slots[(slot_id - 1) as usize].enabled { + error!("Failed to get slot id, slot {} is disabled", slot_id); + evt.ccode = TRBCCode::SlotNotEnabledError; + return 0; + } + slot_id + } + + fn lookup_usb_port(&mut self, slot_ctx: &XhciSlotCtx) -> Option>> { + let mut path = String::new(); + let mut port = slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff; + if port < 1 || port > self.port_num { + error!("Invalid port: {}", port); + return None; + } + let usb_port = &self.usb_ports[(port - 1) as usize]; + port = usb_port.lock().unwrap().index + 1; + path += &format!("{}", port); + for i in 0..5 { + port = (slot_ctx.dev_info >> (4 * i)) & 0x0f; + if port == 0 { + break; + } + path += &format!(".{}", port); + } + let locked_bus = self.bus.lock().unwrap(); + locked_bus.find_usb_port(path) + } + /// Control plane pub fn handle_command(&mut self) -> Result<()> { + if !self.running() { + bail!("Failed to process command: xhci is not running"); + } + let mut lo = read_u32(self.oper.cmd_ring_ctrl, 0); + lo |= CMD_RING_CTRL_CRR; + self.oper.cmd_ring_ctrl = write_u64_low(self.oper.cmd_ring_ctrl, lo); + let mut slot_id: u32; + let mut event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::Success); + for _ in 0..COMMAND_LIMIT { + match self.cmd_ring.fetch_trb() { + Ok(trb) => { + let trb_type = trb.get_type(); + event.ptr = trb.addr; + slot_id = self.get_slot_id(&mut event, &trb); + info!("handle_command {:?} {:?}", trb_type, trb); + match trb_type { + TRBType::CrEnableSlot => { + let mut found = 0; + for i in 0..self.slots.len() as u32 { + if !self.slots[i as usize].enabled { + found = i + 1; + break; + } + } + if found == 0 { + event.ccode = TRBCCode::NoSlotsError; + } else { + slot_id = found; + event.ccode = self.enable_slot(slot_id); + } + } + TRBType::CrDisableSlot => { + if slot_id != 0 { + event.ccode = self.disable_slot(slot_id)?; + } + } + TRBType::CrAddressDevice => { + if slot_id != 0 { + event.ccode = self.address_slot(slot_id, &trb)?; + } + } + TRBType::CrConfigureEndpoint => { + if slot_id != 0 { + event.ccode = self.config_slot(slot_id, &trb)?; + } + } + TRBType::CrEvaluateContext => { + if slot_id != 0 { + event.ccode = self.evaluate_slot(slot_id, &trb)?; + } + } + TRBType::CrStopEndpoint => { + let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; + event.ccode = self.stop_endpoint(slot_id, ep_id)?; + } + TRBType::CrResetEndpoint => { + let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; + event.ccode = self.reset_endpoint(slot_id, ep_id)?; + } + TRBType::CrSetTrDequeue => { + let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; + event.ccode = self.set_endpoint_dequeue(slot_id, ep_id, &trb)?; + } + TRBType::CrResetDevice => { + event.ccode = self.reset_slot(slot_id)?; + } + _ => { + error!("Invalid Command: type {:?}", trb_type); + event.ccode = TRBCCode::TrbError; + } + } + event.slot_id = slot_id as u8; + self.send_event(&event, 0)?; + } + Err(e) => { + error!("Failed to fetch ring: {}", e); + event.ccode = TRBCCode::TrbError; + break; + } + } + } Ok(()) } - /// Data plane - pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { - Ok(()) + fn enable_slot(&mut self, slot_id: u32) -> TRBCCode { + self.slots[(slot_id - 1) as usize].enabled = true; + TRBCCode::Success } fn disable_slot(&mut self, slot_id: u32) -> Result { @@ -458,6 +599,270 @@ impl XhciDevice { Ok(TRBCCode::Success) } + fn address_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + let bsr = trb.control & TRB_CR_BSR == TRB_CR_BSR; + let dcbaap = self.oper.dcbaap; + let ictx = trb.parameter; + let mut octx = 0; + dma_read_u64( + &self.mem_space, + GuestAddress(dcbaap + (8 * slot_id) as u64), + &mut octx, + )?; + let mut ictl_ctx = XhciInputCtrlCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx), + ictl_ctx.as_mut_dwords(), + )?; + if ictl_ctx.drop_flags != 0x0 || ictl_ctx.add_flags != 0x3 { + error!("Invalid input: {:?}", ictl_ctx); + return Ok(TRBCCode::TrbError); + } + let mut slot_ctx = XhciSlotCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx + 32), + slot_ctx.as_mut_dwords(), + )?; + if let Some(usb_port) = self.lookup_usb_port(&slot_ctx) { + let lock_port = usb_port.lock().unwrap(); + let dev = lock_port.dev.as_ref().unwrap(); + let mut locked_dev = dev.lock().unwrap(); + if !locked_dev.attached() { + error!("Failed to connect device"); + return Ok(TRBCCode::UsbTransactionError); + } + self.slots[(slot_id - 1) as usize].usb_port = Some(Arc::downgrade(&usb_port)); + self.slots[(slot_id - 1) as usize].ctx = octx; + self.slots[(slot_id - 1) as usize].intr = + ((slot_ctx.tt_info >> TRB_INTR_SHIFT) & TRB_INTR_MASK) as u16; + locked_dev.reset(); + drop(locked_dev); + if bsr { + slot_ctx.dev_state = SLOT_DEFAULT << SLOT_STATE_SHIFT; + } else { + slot_ctx.dev_state = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot_id; + self.address_device(dev, slot_id); + } + let mut ep0_ctx = XhciEpCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx + 64), + ep0_ctx.as_mut_dwords(), + )?; + self.enable_endpoint(slot_id, 1, octx + 32, &mut ep0_ctx)?; + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + dma_write_u32( + &self.mem_space, + GuestAddress(octx + 32), + ep0_ctx.as_dwords(), + )?; + self.slots[(slot_id - 1) as usize].addressed = true; + } else { + error!("Failed to found usb port"); + return Ok(TRBCCode::TrbError); + } + Ok(TRBCCode::Success) + } + + fn address_device(&mut self, dev: &Arc>, slot_id: u32) { + let mut p = UsbPacket::default(); + let mut locked_dev = dev.lock().unwrap(); + let usb_dev = locked_dev.get_mut_usb_device(); + let locked_usb = usb_dev.lock().unwrap(); + let ep = Arc::downgrade(&locked_usb.get_endpoint(USB_TOKEN_OUT as u32, 0)); + p.init(USB_TOKEN_OUT as u32, ep, 0, false, false); + drop(locked_usb); + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_OUT_REQUEST, + request: USB_REQUEST_SET_ADDRESS, + value: slot_id as u16, + index: 0, + length: 0, + }; + locked_dev.handle_control(&mut p, &device_req, &mut []); + } + + fn config_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + let ictx = trb.parameter; + let octx = self.slots[(slot_id - 1) as usize].ctx; + let mut slot_ctx = XhciSlotCtx::default(); + if trb.control & TRB_CR_DC == TRB_CR_DC { + for i in 2..32 { + self.disable_endpoint(slot_id, i)?; + } + dma_read_u32( + &self.mem_space, + GuestAddress(octx), + slot_ctx.as_mut_dwords(), + )?; + slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); + slot_ctx.dev_state |= SLOT_ADDRESSED << SLOT_STATE_SHIFT; + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + return Ok(TRBCCode::Success); + } + let mut ictl_ctx = XhciInputCtrlCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx), + ictl_ctx.as_mut_dwords(), + )?; + if ictl_ctx.drop_flags & 0x3 != 0x0 || ictl_ctx.add_flags & 0x3 != 0x1 { + error!("Invalid control context {:?}", ictl_ctx); + return Ok(TRBCCode::TrbError); + } + let mut islot_ctx = XhciSlotCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx + 32), + islot_ctx.as_mut_dwords(), + )?; + dma_read_u32( + &self.mem_space, + GuestAddress(octx), + slot_ctx.as_mut_dwords(), + )?; + if (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK < SLOT_ADDRESSED { + error!("Invalid slot state"); + return Ok(TRBCCode::TrbError); + } + self.config_slot_ep(slot_id, ictx, octx, ictl_ctx)?; + slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); + slot_ctx.dev_state |= SLOT_CONFIGURED << SLOT_STATE_SHIFT; + slot_ctx.dev_info &= !(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); + slot_ctx.dev_info |= + islot_ctx.dev_info & (SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + Ok(TRBCCode::Success) + } + + fn config_slot_ep( + &mut self, + slot_id: u32, + ictx: u64, + octx: u64, + ictl_ctx: XhciInputCtrlCtx, + ) -> Result { + for i in 2..32 { + if ictl_ctx.drop_flags & (1 << i) == 1 << i { + self.disable_endpoint(slot_id, i)?; + } + if ictl_ctx.add_flags & (1 << i) == 1 << i { + let offset = 32 + 32 * i as u64; + let mut ep_ctx = XhciEpCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx + offset), + ep_ctx.as_mut_dwords(), + )?; + self.disable_endpoint(slot_id, i)?; + let offset = 32 * i as u64; + let ret = self.enable_endpoint(slot_id, i, octx + offset, &mut ep_ctx)?; + if ret != TRBCCode::Success { + return Ok(ret); + } + dma_write_u32( + &self.mem_space, + GuestAddress(octx + offset), + ep_ctx.as_dwords(), + )?; + } + } + Ok(TRBCCode::Success) + } + + fn evaluate_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + let ictx = trb.parameter; + let octx = self.slots[(slot_id - 1) as usize].ctx; + let mut ictl_ctx = XhciInputCtrlCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx), + ictl_ctx.as_mut_dwords(), + )?; + if ictl_ctx.drop_flags != 0x0 || ictl_ctx.add_flags & !0x3 == !0x3 { + error!("Invalid input control"); + return Ok(TRBCCode::TrbError); + } + if ictl_ctx.add_flags & 0x1 == 0x1 { + let mut islot_ctx = XhciSlotCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx + 0x20), + islot_ctx.as_mut_dwords(), + )?; + let mut slot_ctx = XhciSlotCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(octx), + slot_ctx.as_mut_dwords(), + )?; + slot_ctx.dev_info2 &= !0xffff; + slot_ctx.dev_info2 |= islot_ctx.dev_info2 & 0xffff; + slot_ctx.tt_info &= !0xff00000; + slot_ctx.tt_info |= islot_ctx.tt_info & 0xff000000; + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + } + if ictl_ctx.add_flags & 0x2 == 0x2 { + let mut iep_ctx = XhciEpCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx + 0x40), + iep_ctx.as_mut_dwords(), + )?; + let mut ep_ctx = XhciEpCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(octx + 0x20), + ep_ctx.as_mut_dwords(), + )?; + ep_ctx.ep_info2 &= !0xffff0000; + ep_ctx.ep_info2 |= iep_ctx.ep_info2 & 0xffff0000; + dma_write_u32( + &self.mem_space, + GuestAddress(octx + 0x20), + ep_ctx.as_dwords(), + )?; + } + Ok(TRBCCode::Success) + } + + fn reset_slot(&mut self, slot_id: u32) -> Result { + let mut slot_ctx = XhciSlotCtx::default(); + let octx = self.slots[(slot_id - 1) as usize].ctx; + for i in 2..32 { + self.disable_endpoint(slot_id, i)?; + } + dma_read_u32( + &self.mem_space, + GuestAddress(octx), + slot_ctx.as_mut_dwords(), + )?; + slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); + slot_ctx.dev_state |= SLOT_DEFAULT << SLOT_STATE_SHIFT; + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + Ok(TRBCCode::Success) + } + + fn enable_endpoint( + &mut self, + slot_id: u32, + ep_id: u32, + pctx: DmaAddr, + ctx: &mut XhciEpCtx, + ) -> Result { + self.disable_endpoint(slot_id, ep_id)?; + let mut epctx = XhciEpContext::new(&self.mem_space, ep_id); + epctx.enabled = true; + epctx.init(pctx, ctx); + epctx.state = EP_RUNNING; + ctx.ep_info &= !EP_STATE_MASK; + ctx.ep_info |= EP_RUNNING; + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + Ok(TRBCCode::Success) + } + fn disable_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { let slot = &mut self.slots[(slot_id - 1) as usize]; let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; @@ -472,6 +877,77 @@ impl XhciDevice { Ok(TRBCCode::Success) } + fn stop_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { + if !(1..=31).contains(&ep_id) { + error!("Invalid endpoint id"); + return Ok(TRBCCode::TrbError); + } + if !self.slots[(slot_id - 1) as usize].enabled { + return Ok(TRBCCode::EpNotEnabledError); + } + if !self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].enabled { + error!("stop_endpoint ep is disabled"); + } + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] + .set_state(&self.mem_space, EP_STOPPED)?; + Ok(TRBCCode::Success) + } + + fn reset_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { + if !(1..=31).contains(&ep_id) { + error!("Invalid endpoint id {}", ep_id); + return Ok(TRBCCode::TrbError); + } + let slot = &mut self.slots[(slot_id - 1) as usize]; + let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; + if !epctx.enabled { + error!("reset_endpoint ep is disabled"); + return Ok(TRBCCode::EpNotEnabledError); + } + if epctx.state != EP_HALTED { + error!("Endpoint is not halted"); + return Ok(TRBCCode::ContextStateError); + } + if let Some(port) = &slot.usb_port { + if port.upgrade().unwrap().lock().unwrap().dev.is_some() { + epctx.set_state(&self.mem_space, EP_STOPPED)?; + } else { + error!("Failed to found usb device"); + return Ok(TRBCCode::UsbTransactionError); + } + } else { + error!("Failed to found port"); + return Ok(TRBCCode::UsbTransactionError); + } + Ok(TRBCCode::Success) + } + + fn set_endpoint_dequeue(&mut self, slotid: u32, epid: u32, trb: &XhciTRB) -> Result { + if !(1..=31).contains(&epid) { + error!("Invalid endpoint id {}", epid); + return Ok(TRBCCode::TrbError); + } + let dequeue = trb.parameter; + let mut epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize]; + if !epctx.enabled { + error!(" Endpoint is disabled slotid {} epid {}", slotid, epid); + return Ok(TRBCCode::EpNotEnabledError); + } + if epctx.state != EP_STOPPED { + error!("Endpoint is not stopped slotid {} epid {}", slotid, epid); + return Ok(TRBCCode::ContextStateError); + } + epctx.ring.init(dequeue & !0xf); + epctx.ring.ccs = (dequeue & 1) == 1; + epctx.set_state(&self.mem_space, EP_STOPPED)?; + Ok(TRBCCode::Success) + } + + /// Data plane + pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { + Ok(()) + } + /// Get microframe index pub fn get_mf_index(&self) -> u64 { warn!("get_mf_index not implemented"); -- Gitee From b3ae80f660c9208a4321351c78a99b8dc1684b78 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 14 Aug 2022 22:27:02 +0800 Subject: [PATCH 0119/2187] xhci: implement data plane Implement xhci data plane ops to support controller and device for data interaction. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 428 +++++++++++++++++++++++++++++++- 1 file changed, 426 insertions(+), 2 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 1209aa57c..a8db05159 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -91,11 +91,11 @@ pub struct XhciTransfer { } impl XhciTransfer { - fn new(len: u32) -> Self { + fn new(len: usize) -> Self { XhciTransfer { packet: UsbPacket::default(), status: TRBCCode::Invalid, - trbs: vec![XhciTRB::new(); len as usize], + trbs: vec![XhciTRB::new(); len], complete: false, slotid: 0, epid: 0, @@ -885,6 +885,9 @@ impl XhciDevice { if !self.slots[(slot_id - 1) as usize].enabled { return Ok(TRBCCode::EpNotEnabledError); } + if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Stopped)? > 0 { + warn!("endpoint stop when xfers running!"); + } if !self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].enabled { error!("stop_endpoint ep is disabled"); } @@ -945,6 +948,427 @@ impl XhciDevice { /// Data plane pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { + if !self.slots[(slot_id - 1) as usize].enabled { + bail!("Kick disbaled slot slotid {}", slot_id); + } + + let ep_ctx = &self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + debug!( + "kick_endpoint slotid {} epid {} dequeue {:x}", + slot_id, ep_id, ep_ctx.ring.dequeue + ); + if !ep_ctx.enabled { + bail!("Kick disabled endpoint slot id {} ep id {}", slot_id, ep_id); + } + let mut epctx = ep_ctx.clone(); + self.endpoint_retry_transfer(&mut epctx)?; + if epctx.state == EP_HALTED { + info!("xhci: endpoint halted"); + return Ok(()); + } + epctx.set_state(&self.mem_space, EP_RUNNING)?; + const KICK_LIMIT: u32 = 32; + let mut count = 0; + loop { + let len = match epctx.ring.get_transfer_len() { + Ok(len) => len, + Err(e) => { + error!("Failed to get transfer len: {}", e); + break; + } + }; + let mut xfer: XhciTransfer = XhciTransfer::new(len); + xfer.slotid = slot_id; + xfer.epid = ep_id; + for i in 0..len { + match epctx.ring.fetch_trb() { + Ok(trb) => { + debug!( + "fetch transfer trb {:?} ring dequeue {:x}", + trb, epctx.ring.dequeue, + ); + xfer.trbs[i] = trb; + } + Err(e) => { + self.oper.usb_status |= USB_STS_HCE; + // update the endpoint in slot + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + bail!("fetch ring failed {}", e); + } + } + } + if let Err(e) = self.endpoint_do_transfer(&mut xfer, &mut epctx) { + error!("Failed to transfer {}", e); + } + if xfer.complete { + epctx.set_state(&self.mem_space, epctx.state)?; + } else { + epctx.transfers.push(xfer.clone()); + } + if epctx.state == EP_HALTED { + break; + } + // retry + if !xfer.complete && xfer.running_retry { + epctx.retry = Some(xfer.clone()); + break; + } + count += 1; + if count > KICK_LIMIT { + warn!("kick endpoint over limit"); + break; + } + } + // Update the endpoint context in slot. + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + Ok(()) + } + + fn endpoint_retry_transfer(&mut self, epctx: &mut XhciEpContext) -> Result<()> { + if let Some(xfer) = &mut epctx.retry { + self.setup_usb_packet(xfer, epctx.epid)?; + self.device_handle_packet(&mut xfer.packet)?; + self.complete_packet(xfer)?; + if xfer.complete { + epctx.set_state(&self.mem_space, epctx.state)?; + } + epctx.retry = None; + } + Ok(()) + } + + fn device_handle_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { + if let Some(ep) = &packet.ep { + let ep = ep.upgrade().unwrap(); + let locked_ep = ep.lock().unwrap(); + let dev = if let Some(usb_dev) = &locked_ep.dev { + usb_dev.upgrade().unwrap() + } else { + bail!("No device found in endpoint"); + }; + drop(locked_ep); + let mut locked_dev = dev.lock().unwrap(); + locked_dev.handle_packet(packet); + } else { + bail!("No endpoint found"); + } + Ok(()) + } + + fn endpoint_do_transfer( + &mut self, + xfer: &mut XhciTransfer, + epctx: &mut XhciEpContext, + ) -> Result<()> { + if xfer.epid == 1 { + self.do_ctrl_transfer(xfer, epctx)?; + } else { + self.do_data_transfer(xfer, epctx)?; + } + Ok(()) + } + + /// Control Transfer, TRBs include Setup, Data(option), Status. + fn do_ctrl_transfer( + &mut self, + xfer: &mut XhciTransfer, + epctx: &mut XhciEpContext, + ) -> Result<()> { + let trb_setup = xfer.trbs[0]; + let mut trb_status = xfer.trbs[xfer.trbs.len() - 1]; + let setup_type = trb_setup.get_type(); + let status_type = trb_status.get_type(); + if status_type == TRBType::TrEvdata && xfer.trbs.len() > 2 { + trb_status = xfer.trbs[xfer.trbs.len() - 2]; + } + if setup_type != TRBType::TrSetup { + bail!("The first TRB is not Setup"); + } + if trb_status.get_type() != TRBType::TrStatus { + bail!("The last TRB is not Status"); + } + if trb_setup.control & TRB_TR_IDT != TRB_TR_IDT { + bail!("no IDT bit"); + } + if trb_setup.status & 0x1ffff != 8 { + bail!("Bad Setup TRB length {}", trb_setup.status & 0x1ffff); + } + + let bm_request_type = trb_setup.parameter as u8; + xfer.in_xfer = + bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + self.setup_usb_packet(xfer, epctx.epid)?; + xfer.packet.parameter = trb_setup.parameter; + self.device_handle_packet(&mut xfer.packet)?; + self.complete_packet(xfer)?; + Ok(()) + } + + fn do_data_transfer( + &mut self, + xfer: &mut XhciTransfer, + epctx: &mut XhciEpContext, + ) -> Result<()> { + xfer.in_xfer = epctx.ep_type == EpType::Control + || epctx.ep_type == EpType::IsoIn + || epctx.ep_type == EpType::BulkIn + || epctx.ep_type == EpType::IntrIn; + if epctx.ep_type != EpType::IntrOut && epctx.ep_type != EpType::IntrIn { + bail!("Unhandled ep_type {:?}", epctx.ep_type); + } + self.setup_usb_packet(xfer, epctx.epid)?; + self.device_handle_packet(&mut xfer.packet)?; + self.complete_packet(xfer)?; + Ok(()) + } + + // Setup USB packet, include mapping dma address to iovector. + fn setup_usb_packet(&mut self, xfer: &mut XhciTransfer, epid: u32) -> Result<()> { + let ep = if let Some(ep) = &xfer.packet.ep { + ep.clone() + } else { + let ep = self.get_usb_ep(xfer.slotid, epid)?; + Arc::downgrade(&ep) + }; + let dir = if xfer.in_xfer { + USB_TOKEN_IN + } else { + USB_TOKEN_OUT + }; + // Map dma address to iovec. + let mut vec = Vec::new(); + for trb in &xfer.trbs { + let trb_type = trb.get_type(); + if trb.control & TRB_TR_IOC == TRB_TR_IOC { + xfer.int_req = true; + } + if trb_type == TRBType::TrData + || trb_type == TRBType::TrNormal + || trb_type == TRBType::TrIsoch + { + let chunk = trb.status & 0x1ffff; + let dma_addr = if trb.control & TRB_TR_IDT == TRB_TR_IDT { + trb.addr + } else { + trb.parameter + }; + if let Some(hva) = self.mem_space.get_host_address(GuestAddress(dma_addr)) { + vec.push(Iovec::new(hva, chunk as usize)); + } else { + error!("HVA not existed {:x}", dma_addr); + } + } + } + xfer.packet.init(dir as u32, ep, 0, false, xfer.int_req); + for v in vec { + xfer.packet.iovecs.push(v); + } + Ok(()) + } + + fn get_usb_ep(&self, slotid: u32, epid: u32) -> Result>> { + let port = if let Some(port) = &self.slots[(slotid - 1) as usize].usb_port { + port.upgrade().unwrap() + } else { + bail!("USB port not found slotid {} epid {}", slotid, epid); + }; + let locked_port = port.lock().unwrap(); + let dev = locked_port.dev.as_ref().unwrap(); + let mut locked_dev = dev.lock().unwrap(); + let pid = if epid & 1 == 1 { + USB_TOKEN_IN + } else { + USB_TOKEN_OUT + }; + let usb_dev = locked_dev.get_mut_usb_device(); + let locked_usb = usb_dev.lock().unwrap(); + let ep = locked_usb.get_endpoint(pid as u32, epid >> 1); + Ok(ep) + } + + /// Update packet status and then submit transfer. + fn complete_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + if xfer.packet.status == UsbPacketStatus::Async { + xfer.complete = false; + xfer.running_retry = false; + return Ok(()); + } else if xfer.packet.status == UsbPacketStatus::Nak { + xfer.complete = false; + xfer.running_retry = true; + return Ok(()); + } else { + xfer.complete = true; + xfer.running_retry = false; + } + if xfer.packet.status == UsbPacketStatus::Success { + xfer.status = TRBCCode::Success; + self.submit_transfer(xfer)?; + return Ok(()); + } + // Handle packet error status + match xfer.packet.status { + v if v == UsbPacketStatus::NoDev || v == UsbPacketStatus::IoError => { + xfer.status = TRBCCode::UsbTransactionError; + self.submit_transfer(xfer)?; + } + UsbPacketStatus::Stall => { + xfer.status = TRBCCode::StallError; + self.submit_transfer(xfer)?; + } + UsbPacketStatus::Babble => { + xfer.status = TRBCCode::BabbleDetected; + self.submit_transfer(xfer)?; + } + _ => { + bail!("Unhandle status {:?}", xfer.packet.status); + } + } + Ok(()) + } + + /// Submit transfer TRBs. + fn submit_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + // Event Data Transfer Length Accumulator + let mut edtla = 0; + let mut left = xfer.packet.actual_length; + let mut reported = false; + let mut short_pkt = false; + for i in 0..xfer.trbs.len() { + let trb = &xfer.trbs[i]; + let trb_type = trb.get_type(); + let mut chunk = trb.status & 0x1ffff; + match trb_type { + TRBType::TrSetup => { + if chunk > 8 { + chunk = 8; + } + } + TRBType::TrData | TRBType::TrNormal | TRBType::TrIsoch => { + if chunk > left { + chunk = left; + if xfer.status == TRBCCode::Success { + short_pkt = true; + } + } + left -= chunk; + edtla += chunk; + } + TRBType::TrStatus => { + reported = false; + short_pkt = false; + } + _ => { + bail!("submit_transfer, unhandled trb type {:?}", trb.get_type()); + } + } + if !reported + && ((trb.control & TRB_TR_IOC == TRB_TR_IOC) + || (short_pkt && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) + || (xfer.status != TRBCCode::Success && left == 0)) + { + self.send_transfer_event(xfer, trb, chunk, short_pkt, &mut edtla)?; + reported = true; + if xfer.status != TRBCCode::Success { + // Send unSuccess event succeed,return directly. + info!("submit_transfer xfer status {:?}", xfer.status); + return Ok(()); + } + } + if trb_type == TRBType::TrSetup { + reported = false; + short_pkt = false; + } + } + Ok(()) + } + + fn send_transfer_event( + &mut self, + xfer: &XhciTransfer, + trb: &XhciTRB, + chunk: u32, + short_pkt: bool, + edtla: &mut u32, + ) -> Result<()> { + let trb_type = trb.get_type(); + let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); + evt.slot_id = xfer.slotid as u8; + evt.ep_id = xfer.epid as u8; + evt.length = (trb.status & 0x1ffff) - chunk; + evt.flags = 0; + evt.ptr = trb.addr; + evt.ccode = if xfer.status == TRBCCode::Success { + if short_pkt { + TRBCCode::ShortPacket + } else { + TRBCCode::Success + } + } else { + xfer.status + }; + if trb_type == TRBType::TrEvdata { + evt.ptr = trb.parameter; + evt.flags |= TRB_EV_ED; + evt.length = *edtla & 0xffffff; + *edtla = 0; + } + self.send_event(&evt, 0)?; + Ok(()) + } + + /// Flush transfer in endpoint in some case such as stop endpoint. + fn flush_ep_transfer(&mut self, slotid: u32, epid: u32, report: TRBCCode) -> Result { + info!("flush_ep_transfer slotid {} epid {}", slotid, epid); + let mut cnt = 0; + let mut report = report; + let xfers = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] + .transfers + .clone(); + for mut xfer in xfers { + cnt += self.do_ep_transfer(slotid, epid, &mut xfer, report)?; + if cnt != 0 { + // Only report once. + report = TRBCCode::Invalid; + } + } + self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] + .transfers + .clear(); + Ok(cnt) + } + + fn do_ep_transfer( + &mut self, + slotid: u32, + ep_id: u32, + xfer: &mut XhciTransfer, + report: TRBCCode, + ) -> Result { + let mut killed = 0; + if xfer.running_retry { + if report != TRBCCode::Invalid { + xfer.status = report; + self.submit_transfer(xfer)?; + } + let epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(ep_id - 1) as usize]; + if !epctx.enabled { + bail!("Endpoint is disabled"); + } + epctx.retry = None; + xfer.running_retry = false; + killed = 1; + } + xfer.trbs.clear(); + Ok(killed) + } + + /// Used for device to wakeup endpoint + pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &Arc>) -> Result<()> { + let locked_ep = ep.lock().unwrap(); + let ep_id = locked_ep.get_ep_id(); + // Kick endpoint may hold the lock, drop it. + drop(locked_ep); + self.kick_endpoint(slot_id as u32, ep_id as u32)?; Ok(()) } -- Gitee From 9d4e67fa81377553ac92478ee0a4b1547521a95b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 21:49:49 +0800 Subject: [PATCH 0120/2187] usb: implement handle packet for device Implement handle packet for usb device, and the deivce can process the packet from the controller. Signed-off-by: zhouli57 --- usb/src/usb.rs | 169 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 5 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 3a079f840..ca3cccaa4 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -486,7 +486,17 @@ impl Default for UsbDevice { /// Include device handle attach/detach and the transfer between controller and device. pub trait UsbDeviceOps: Send + Sync { /// Handle the attach ops when attach device to controller. - fn handle_attach(&mut self) -> Result<()>; + fn handle_attach(&mut self) -> Result<()> { + let usb_dev = self.get_mut_usb_device(); + let mut locked_dev = usb_dev.lock().unwrap(); + locked_dev.attached = true; + locked_dev.state = UsbDeviceState::Attached; + drop(locked_dev); + let usb_dev = self.get_mut_usb_device(); + let mut locked_dev = usb_dev.lock().unwrap(); + locked_dev.set_default_descriptor()?; + Ok(()) + } /// Reset the USB device. fn reset(&mut self); @@ -496,10 +506,25 @@ pub trait UsbDeviceOps: Send + Sync { fn set_controller(&mut self, ctrl: Weak>); /// Set the attached USB port. - fn set_usb_port(&mut self, port: Option>>); + fn set_usb_port(&mut self, port: Option>>) { + let usb_dev = self.get_mut_usb_device(); + let mut locked_dev = usb_dev.lock().unwrap(); + locked_dev.port = port; + } /// Handle usb packet, used for controller to deliever packet to device. - fn handle_packet(&mut self, packet: &mut UsbPacket); + fn handle_packet(&mut self, packet: &mut UsbPacket) { + if packet.state != UsbPacketState::Setup { + error!("The packet state is not Setup"); + return; + } + if let Err(e) = self.process_packet(packet) { + error!("Failed to process packet: {}", e); + } + if packet.status != UsbPacketStatus::Nak { + packet.state = UsbPacketState::Complete; + } + } /// Handle control pakcet. fn handle_control( @@ -522,10 +547,96 @@ pub trait UsbDeviceOps: Send + Sync { fn get_mut_usb_device(&mut self) -> Arc>; /// Get the device speed. - fn speed(&self) -> u32; + fn speed(&self) -> u32 { + let usb_dev = self.get_usb_device(); + let locked_dev = usb_dev.lock().unwrap(); + locked_dev.speed + } /// If USB device is attached. - fn attached(&self) -> bool; + fn attached(&self) -> bool { + let usb_dev = self.get_usb_device(); + let locked_dev = usb_dev.lock().unwrap(); + locked_dev.attached + } + + fn process_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { + packet.status = UsbPacketStatus::Success; + let ep = if let Some(ep) = &packet.ep { + ep.upgrade().unwrap() + } else { + bail!("Failed to find ep"); + }; + let locked_ep = ep.lock().unwrap(); + let nr = locked_ep.nr; + drop(locked_ep); + if nr == 0 { + if packet.parameter != 0 { + return self.do_parameter(packet); + } + match packet.pid as u8 { + USB_TOKEN_SETUP => { + warn!("process_packet USB_TOKEN_SETUP not implemented"); + } + USB_TOKEN_IN => { + warn!("process_packet USB_TOKEN_IN not implemented"); + } + USB_TOKEN_OUT => { + warn!("process_packet USB_TOKEN_OUT not implemented"); + } + _ => { + warn!("Unknown pid {}", packet.pid); + packet.status = UsbPacketStatus::Stall; + } + } + } else { + self.handle_data(packet); + } + Ok(()) + } + + fn do_parameter(&mut self, p: &mut UsbPacket) -> Result<()> { + let usb_dev = self.get_mut_usb_device(); + let mut locked_dev = usb_dev.lock().unwrap(); + for i in 0..8 { + locked_dev.setup_buf[i] = (p.parameter >> (i * 8)) as u8; + } + locked_dev.setup_state = SetupState::Parameter; + locked_dev.setup_index = 0; + let device_req = UsbDeviceRequest { + request_type: locked_dev.setup_buf[0], + request: locked_dev.setup_buf[1], + value: (locked_dev.setup_buf[3] as u16) << 8 | locked_dev.setup_buf[2] as u16, + index: (locked_dev.setup_buf[5] as u16) << 8 | locked_dev.setup_buf[4] as u16, + length: (locked_dev.setup_buf[7] as u16) << 8 | locked_dev.setup_buf[6] as u16, + }; + if device_req.length as usize > locked_dev.data_buf.len() { + bail!("data buffer small len {}", device_req.length); + } + locked_dev.setup_len = device_req.length as u32; + if p.pid as u8 == USB_TOKEN_OUT { + let len = locked_dev.data_buf.len(); + usb_packet_transfer(p, &mut locked_dev.data_buf, len); + } + // Drop locked for handle_control use it + drop(locked_dev); + let mut data_buf: [u8; 4096] = [0; 4096]; + self.handle_control(p, &device_req, &mut data_buf); + let mut locked_dev = usb_dev.lock().unwrap(); + locked_dev.data_buf = data_buf.to_vec(); + if p.status == UsbPacketStatus::Async { + return Ok(()); + } + if p.actual_length < locked_dev.setup_len { + locked_dev.setup_len = p.actual_length; + } + if p.pid as u8 == USB_TOKEN_IN { + p.actual_length = 0; + let len = locked_dev.data_buf.len(); + usb_packet_transfer(p, &mut locked_dev.data_buf, len); + } + Ok(()) + } } /// Io vector which save the hva. @@ -610,3 +721,51 @@ impl Default for UsbPacket { } } } + +fn read_mem(hva: u64, buf: &mut [u8]) { + let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; + buf.clone_from_slice(&slice[..buf.len()]); +} + +fn write_mem(hva: u64, buf: &[u8]) { + use std::io::Write; + let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; + if let Err(e) = (&mut slice).write(buf) { + error!("Failed to write mem {}", e); + } +} + +/// Transfer packet from host to device or from device to host. +pub fn usb_packet_transfer(packet: &mut UsbPacket, vec: &mut Vec, len: usize) { + let to_host = packet.pid as u8 & USB_TOKEN_IN == USB_TOKEN_IN; + + if to_host { + let mut copyed = 0; + let mut offset = 0; + for iov in &packet.iovecs { + let cnt = std::cmp::min(iov.iov_len, len - copyed); + let tmp = &vec[offset..(offset + cnt)]; + write_mem(iov.iov_base, tmp); + copyed += cnt; + offset += cnt; + if len - copyed == 0 { + break; + } + } + } else { + let mut copyed = 0; + let mut offset = 0; + for iov in &packet.iovecs { + let cnt = std::cmp::min(iov.iov_len, len - copyed); + let tmp = &mut vec[offset..(offset + cnt)]; + read_mem(iov.iov_base, tmp); + copyed += cnt; + offset += cnt; + if len - copyed == 0 { + break; + } + } + } + + packet.actual_length += len as u32; +} -- Gitee From 3b3e75509d6c9120acf83142f220c12cbc16e459 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 12 Aug 2022 22:03:07 +0800 Subject: [PATCH 0121/2187] xhci: add command line for xhci Now we support config xhci in command line. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 15 ++++++++ machine/src/lib.rs | 46 +++++++++++++++++++++-- machine/src/standard_vm/aarch64/mod.rs | 9 +++++ machine/src/standard_vm/x86_64/mod.rs | 9 +++++ machine_manager/src/config/mod.rs | 2 + machine_manager/src/config/usb.rs | 51 ++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 machine_manager/src/config/usb.rs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2975fc15d..4c05ef19d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -623,6 +623,21 @@ Five properties can be set for chardev. -chardev file,id=chardev_id,path=file_path ``` +### 2.13 USB controller +USB controller is a pci device which can be attached USB device. + +Three properties can be set for USB controller. + +* id: unique device id. +* bus: bus number of the device. +* addr: including slot number and function number. + +```shell +-device nec-usb-xhci,id=xhci,bus=pcie.0,addr=0xa.0x0 +``` + +Note: Only one USB controller can be configured, USB controller can only support USB keyboard and USB tablet. + ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 834a3de4e..30b0906c8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -124,9 +124,9 @@ use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, - parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, Incoming, MachineMemConfig, - MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, - SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, + MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, + PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::{ event_loop::EventLoop, @@ -137,6 +137,7 @@ use pci::{PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::errors::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; +use usb::{bus::BusDeviceMap, xhci::xhci_pci::XhciPciDevice}; use util::{ arg_parser, loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, @@ -355,6 +356,12 @@ pub trait MachineOps { /// Tcp, Unix, File and Unknown. fn get_migrate_info(&self) -> Incoming; + /// Get the bus device map. The map stores the mapping between bus name and bus device. + /// The bus device is the device which can attach other devices. + fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { + None + } + /// Add net device. /// /// # Arguments @@ -884,6 +891,36 @@ pub trait MachineOps { Ok(Some(numa_nodes)) } + /// Add usb xhci controller. + /// + /// # Arguments + /// + /// * `cfg_args` - XHCI Configuration. + fn add_usb_xhci(&mut self, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let device_cfg = parse_xhci(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + + let bus_device = if let Some(bus_device) = self.get_bus_device() { + bus_device.clone() + } else { + bail!("No bus device found"); + }; + + let pcidev = XhciPciDevice::new( + &device_cfg.id, + devfn, + parent_bus, + self.get_sys_mem(), + bus_device, + ); + + pcidev + .realize() + .chain_err(|| "Failed to realize usb xhci device")?; + Ok(()) + } + /// Add peripheral devices. /// /// # Arguments @@ -947,6 +984,9 @@ pub trait MachineOps { "vfio-pci" => { self.add_vfio_device(cfg_args)?; } + "nec-usb-xhci" => { + self.add_usb_xhci(cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 0b7bf6de5..b6379b37f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -13,6 +13,7 @@ mod pci_host_root; mod syscall; +use std::collections::HashMap; use std::fs::OpenOptions; use std::mem::size_of; use std::ops::Deref; @@ -55,6 +56,7 @@ use pci::{PciDevOps, PciHost}; use pci_host_root::PciHostRoot; use sysbus::{SysBus, SysBusDevType, SysRes}; use syscall::syscall_whitelist; +use usb::bus::BusDeviceMap; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -138,6 +140,8 @@ pub struct StdMachine { boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, + /// Bus device used to attach other devices. Only USB controller used now. + bus_device: BusDeviceMap, } impl StdMachine { @@ -187,6 +191,7 @@ impl StdMachine { numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, + bus_device: Arc::new(Mutex::new(HashMap::new())), }) } @@ -577,6 +582,10 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } + + fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { + Some(&self.bus_device) + } } impl AcpiBuilder for StdMachine { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index a6c368dfa..429b1812d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -14,6 +14,7 @@ pub(crate) mod ich9_lpc; mod mch; mod syscall; +use std::collections::HashMap; use std::fs::OpenOptions; use std::io::{Seek, SeekFrom}; use std::mem::size_of; @@ -53,6 +54,7 @@ use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; use sysbus::SysBus; use syscall::syscall_whitelist; +use usb::bus::BusDeviceMap; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, }; @@ -120,6 +122,8 @@ pub struct StdMachine { boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, + /// Bus device used to attach other devices. Only USB controller used now. + bus_device: BusDeviceMap, } impl StdMachine { @@ -171,6 +175,7 @@ impl StdMachine { numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, + bus_device: Arc::new(Mutex::new(HashMap::new())), }) } @@ -617,6 +622,10 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } + + fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { + Some(&self.bus_device) + } } impl AcpiBuilder for StdMachine { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index cab588205..257e97f1a 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -91,6 +91,7 @@ pub use network::*; pub use numa::*; pub use pci::*; pub use rng::*; +pub use usb::*; pub use vfio::*; mod balloon; @@ -105,6 +106,7 @@ mod network; mod numa; mod pci; mod rng; +mod usb; mod vfio; use std::any::Any; diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs new file mode 100644 index 000000000..897f9433a --- /dev/null +++ b/machine_manager/src/config/usb.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result}; + +use error_chain::bail; + +use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; + +/// XHCI contoller configuration. +pub struct XhciConfig { + pub id: String, +} + +impl XhciConfig { + fn new() -> Self { + XhciConfig { id: String::new() } + } +} + +impl ConfigCheck for XhciConfig { + fn check(&self) -> Result<()> { + if self.id.len() > MAX_STRING_LENGTH { + return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + } + Ok(()) + } +} + +pub fn parse_xhci(conf: &str) -> Result { + let mut cmd_parser = CmdParser::new("nec-usb-xhci"); + cmd_parser.push("").push("id").push("bus").push("addr"); + cmd_parser.parse(conf)?; + let mut dev = XhciConfig::new(); + if let Some(id) = cmd_parser.get_value::("id")? { + dev.id = id; + } else { + bail!("id is none for usb xhci"); + } + dev.check()?; + Ok(dev) +} -- Gitee From 21bff62154c4709a57344378f76470b7dfa166eb Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 Aug 2022 21:07:35 +0800 Subject: [PATCH 0122/2187] usb: add hid device Add hid device which handle hid operation such as key up/down or pointer move. Signed-off-by: zhouli57 --- usb/src/hid.rs | 552 +++++++++++++++++++++++++++++++++++++++++++++++++ usb/src/lib.rs | 1 + 2 files changed, 553 insertions(+) create mode 100644 usb/src/hid.rs diff --git a/usb/src/hid.rs b/usb/src/hid.rs new file mode 100644 index 000000000..32bf76931 --- /dev/null +++ b/usb/src/hid.rs @@ -0,0 +1,552 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::fmt::{Display, Formatter, Result as FmtResult}; + +use crate::config::*; +use crate::usb::{usb_packet_transfer, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; + +/// HID keycode +const HID_KEYBOARD_LEFT_CONTROL: u8 = 0xe0; +#[allow(unused)] +const HID_KEYBOARD_LEFT_SHIFT: u8 = 0xe1; +#[allow(unused)] +const HID_KEYBOARD_LEFT_ALT: u8 = 0xe2; +#[allow(unused)] +const HID_KEYBOARD_LEFT_GUI: u8 = 0xe3; +#[allow(unused)] +const HID_KEYBOARD_RIGHT_CONTROL: u8 = 0xe4; +#[allow(unused)] +const HID_KEYBOARD_RIGHT_SHIFT: u8 = 0xe5; +#[allow(unused)] +const HID_KEYBOARD_RIGHT_ALT: u8 = 0xe6; +const HID_KEYBOARD_RIGHT_GUI: u8 = 0xe7; + +/// See the spec section 7.2 Class-Specific Requests +const HID_GET_REPORT: u8 = 0x01; +const HID_GET_IDLE: u8 = 0x02; +const HID_GET_PROTOCOL: u8 = 0x03; +const HID_SET_REPORT: u8 = 0x09; +const HID_SET_IDLE: u8 = 0x0a; +const HID_SET_PROTOCOL: u8 = 0x0b; + +/// See the spec section 7.2.5 Get Protocol Request +#[allow(unused)] +const HID_PROTOCTL_BOOT: u8 = 0; +const HID_PROTOCOL_REPORT: u8 = 1; +const KEYCODE_UP: u32 = 0x80; +pub const QUEUE_LENGTH: u32 = 16; +pub const QUEUE_MASK: u32 = QUEUE_LENGTH - 1; +const HID_USAGE_ERROR_ROLLOVER: u8 = 0x1; + +/// String descriptor index +pub const STR_MANUFACTURER: u8 = 1; +pub const STR_PRODUCT_MOUSE: u8 = 2; +pub const STR_PRODUCT_TABLET: u8 = 3; +pub const STR_PRODUCT_KEYBOARD: u8 = 4; +pub const STR_SERIAL_COMPAT: u8 = 5; +pub const STR_CONFIG_MOUSE: u8 = 6; +pub const STR_CONFIG_TABLET: u8 = 7; +pub const STR_CONFIG_KEYBOARD: u8 = 8; +pub const STR_SERIAL_MOUSE: u8 = 9; +pub const STR_SERIAL_TABLET: u8 = 10; +pub const STR_SERIAL_KEYBOARD: u8 = 11; + +/// String descriptor +pub const DESC_STRINGS: [&str; 12] = [ + "", + "StratoVirt", + "StratoVirt USB Mouse", + "StratoVirt USB Tablet", + "StratoVirt USB Keyboard", + "42", + "HID Mouse", + "HID Tablet", + "HID Keyboard", + "89126", + "28754", + "68284", +]; +/// QKeyCode to HID code table +const HID_CODE: [u8; 0x100] = [ + 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, + 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, + 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, + 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, 0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, + 0x5a, 0x5b, 0x62, 0x63, 0x46, 0x00, 0x64, 0x44, 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x8b, 0x00, 0x89, 0xe7, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x48, 0x4a, 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, + 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x66, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; +/// Tablet report descriptor +const TABLET_REPORT_DESCRIPTOR: [u8; 74] = [ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xa1, 0x01, // Collection (Application) + 0x09, 0x01, // Usage (Pointer) + 0xa1, 0x00, // Collection (Physical) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x03, // Usage Maximum (3) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x03, // Report Count (3) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x95, 0x01, // Report Count (1) + 0x75, 0x05, // Report Size (5) + 0x81, 0x01, // Input (Constant) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x7f, // Logical Maximum (0x7fff) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xff, 0x7f, // Physical Maximum (0x7fff) + 0x75, 0x10, // Report Size (16) + 0x95, 0x02, // Report Count (2) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-0x7f) + 0x25, 0x7f, // Logical Maximum (0x7f) + 0x35, 0x00, // Physical Minimum (same as logical) + 0x45, 0x00, // Physical Maximum (same as logical) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x06, // Input (Data, Variable, Relative) + 0xc0, 0xc0, // End Collection +]; +/// Keyboard report descriptor +const KEYBOARD_REPORT_DESCRIPTOR: [u8; 63] = [ + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x06, // Usage (Keyboard) + 0xa1, 0x01, // Collection (Application) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x05, 0x07, // Usage Page (Key Codes) + 0x19, 0xe0, // Usage Minimum (224) + 0x29, 0xe7, // Usage Maximum (231) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) + 0x81, 0x01, // Input (Constant) + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x05, // Usage Maximum (5) + 0x91, 0x02, // Output (Data, Variable, Absolute) + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x91, 0x01, // Output (Constant) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0xff, // Logical Maximum (255) + 0x05, 0x07, // Usage Page (Key Codes) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0xff, // Usage Maximum (255) + 0x81, 0x00, // Input (Data, Array) + 0xc0, // End Collection +]; + +/// HID type +#[derive(Debug)] +pub enum HidType { + Mouse, + Tablet, + Keyboard, + UnKnown, +} + +/// HID keyboard including keycode and modifier. +pub struct HidKeyboard { + /// Recive keycode from VNC. + pub keycodes: [u32; QUEUE_LENGTH as usize], + pub modifiers: u16, + /// Send keycode to driver. + pub key_buf: [u8; QUEUE_LENGTH as usize], + pub key_num: u32, +} + +impl HidKeyboard { + fn new() -> HidKeyboard { + HidKeyboard { + keycodes: [0; QUEUE_LENGTH as usize], + modifiers: 0, + key_buf: [0; QUEUE_LENGTH as usize], + key_num: 0, + } + } +} + +/// HID pointer event including position and button state. +#[derive(Debug, Clone, Copy, Default)] +pub struct HidPointerEvent { + /// Direction: left to right. + pub pos_x: i32, + /// Direction: up to down. + pub pos_y: i32, + /// Wheel up or down. + pub pos_z: i32, + pub button_state: u32, +} + +/// HID pointer which include hid pointer event. +pub struct HidPointer { + pub queue: [HidPointerEvent; QUEUE_LENGTH as usize], +} + +impl HidPointer { + fn new() -> Self { + HidPointer { + queue: [HidPointerEvent::default(); QUEUE_LENGTH as usize], + } + } +} + +/// Human Interface Device. +pub struct Hid { + pub(crate) head: u32, + pub(crate) num: u32, + pub(crate) kind: HidType, + protocol: u8, + idle: u8, + pub(crate) keyboard: HidKeyboard, + pub(crate) pointer: HidPointer, +} + +impl Hid { + pub fn new() -> Self { + Hid { + head: 0, + num: 0, + kind: HidType::UnKnown, + protocol: 0, + idle: 0, + keyboard: HidKeyboard::new(), + pointer: HidPointer::new(), + } + } + + pub fn reset(&mut self) { + self.head = 0; + self.num = 0; + self.protocol = HID_PROTOCOL_REPORT; + self.idle = 0; + } + + fn convert_to_hid_code(&mut self) { + if self.num == 0 { + return; + } + let slot = self.head & QUEUE_MASK; + increase_queue(&mut self.head); + self.num -= 1; + let keycode = self.keyboard.keycodes[slot as usize]; + let key = keycode & 0x7f; + let index = key | ((self.keyboard.modifiers as u32 & (1 << 8)) >> 1); + let hid_code = HID_CODE[index as usize]; + self.keyboard.modifiers &= !(1 << 8); + debug!( + "convert_to_hid_code hid_code {} index {} key {}", + hid_code, index, key + ); + if hid_code == 0x0 { + return; + } + if hid_code == HID_KEYBOARD_LEFT_CONTROL && self.keyboard.modifiers & (1 << 9) == (1 << 9) { + self.keyboard.modifiers ^= (1 << 8) | (1 << 9); + return; + } + if (HID_KEYBOARD_LEFT_CONTROL..=HID_KEYBOARD_RIGHT_GUI).contains(&hid_code) + && keycode & KEYCODE_UP == KEYCODE_UP + { + self.keyboard.modifiers &= !(1 << (hid_code & 0x0f)); + return; + } + if (HID_KEYBOARD_LEFT_CONTROL..=0xe9).contains(&hid_code) { + self.keyboard.modifiers |= 1 << (hid_code & 0x0f); + return; + } + // Invalid code. + if (0xea..=0xef).contains(&hid_code) { + error!("Convert error, invalid code {}", hid_code); + return; + } + if keycode & KEYCODE_UP == KEYCODE_UP { + let mut i = self.keyboard.key_num as i32 - 1; + while i >= 0 { + if self.keyboard.key_buf[i as usize] == hid_code { + self.keyboard.key_num -= 1; + self.keyboard.key_buf[i as usize] = + self.keyboard.key_buf[self.keyboard.key_num as usize]; + self.keyboard.key_buf[self.keyboard.key_num as usize] = 0x0; + break; + } + i -= 1; + } + } else { + let mut i = self.keyboard.key_num as i32 - 1; + while i >= 0 { + if self.keyboard.key_buf[i as usize] == hid_code { + break; + } + i -= 1; + } + if i < 0 && self.keyboard.key_num < self.keyboard.key_buf.len() as u32 { + self.keyboard.key_buf[self.keyboard.key_num as usize] = hid_code; + self.keyboard.key_num += 1; + } + } + } + + fn keyboard_poll(&mut self) -> Vec { + let mut data = vec![0; 8]; + self.convert_to_hid_code(); + data[0] = self.keyboard.modifiers as u8; + data[1] = 0; + let len = (data.len() - 2) as usize; + if self.keyboard.key_num > 6 { + for i in 0..len { + data[i + 2] = HID_USAGE_ERROR_ROLLOVER; + } + } else { + data[2..(len + 2)].clone_from_slice(&self.keyboard.key_buf[..len]); + } + data + } + + fn pointer_poll(&mut self) -> Vec { + let index = if self.num > 0 { + self.head + } else { + self.head - 1 + }; + let evt = &mut self.pointer.queue[(index & QUEUE_MASK) as usize]; + let mut z = evt.pos_z; + evt.pos_z -= z; + if self.num != 0 && evt.pos_z == 0 { + increase_queue(&mut self.head); + self.num -= 1; + } + z = 0 - z; + let data = vec![ + evt.button_state as u8, + evt.pos_x as u8, + (evt.pos_x >> 8) as u8, + evt.pos_y as u8, + (evt.pos_y >> 8) as u8, + z as u8, + ]; + data + } + + /// USB HID device handle control packet. + pub fn handle_control_packet( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ) { + match device_req.request_type { + USB_INTERFACE_IN_REQUEST => { + self.do_interface_in_request(packet, device_req, data); + } + USB_INTERFACE_CLASS_IN_REQUEST => { + self.do_interface_class_in_request(packet, device_req, data); + } + USB_INTERFACE_CLASS_OUT_REQUEST => { + self.do_interface_class_out_request(packet, device_req); + } + _ => { + error!("Unhandled request {}", device_req.request); + packet.status = UsbPacketStatus::Stall; + } + } + } + + fn do_interface_in_request( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ) { + match device_req.request { + USB_REQUEST_GET_DESCRIPTOR => match device_req.value >> 8 { + 0x22 => match self.kind { + HidType::Tablet => { + data[..TABLET_REPORT_DESCRIPTOR.len()] + .clone_from_slice(&TABLET_REPORT_DESCRIPTOR[..]); + packet.actual_length = TABLET_REPORT_DESCRIPTOR.len() as u32; + } + HidType::Keyboard => { + data[..KEYBOARD_REPORT_DESCRIPTOR.len()] + .clone_from_slice(&KEYBOARD_REPORT_DESCRIPTOR[..]); + packet.actual_length = KEYBOARD_REPORT_DESCRIPTOR.len() as u32; + } + _ => { + error!("Unkown HID type"); + } + }, + _ => { + error!("Invalid value: {:?}", device_req); + } + }, + _ => { + packet.status = UsbPacketStatus::Stall; + error!("Unhandled request {}", device_req.request); + } + } + } + + fn do_interface_class_in_request( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ) { + match device_req.request { + HID_GET_REPORT => match self.kind { + HidType::Tablet => { + let buf = self.pointer_poll(); + data.copy_from_slice(buf.as_slice()); + packet.actual_length = buf.len() as u32; + } + HidType::Keyboard => { + let buf = self.keyboard_poll(); + data.copy_from_slice(buf.as_slice()); + packet.actual_length = buf.len() as u32; + } + _ => { + error!("Unsupported HID type for report"); + } + }, + HID_GET_PROTOCOL => { + data[0] = self.protocol; + packet.actual_length = 1; + } + HID_GET_IDLE => { + data[0] = self.idle; + packet.actual_length = 1; + } + _ => { + error!("Unhandled request {}", device_req.request); + packet.status = UsbPacketStatus::Stall; + } + } + } + + fn do_interface_class_out_request( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + ) { + match device_req.request { + HID_SET_REPORT => match self.kind { + HidType::Keyboard => { + error!("Keyboard set report not implemented"); + } + _ => { + error!("Unsupported to set report"); + } + }, + HID_SET_PROTOCOL => { + self.protocol = device_req.value as u8; + } + HID_SET_IDLE => { + self.idle = (device_req.value >> 8) as u8; + } + _ => { + error!("Unhandled request {}", device_req.request); + packet.status = UsbPacketStatus::Stall; + } + } + } + + /// USB HID device handle data packet. + pub fn handle_data_packet(&mut self, p: &mut UsbPacket) { + match p.pid as u8 { + USB_TOKEN_IN => { + self.handle_token_in(p); + } + _ => { + p.status = UsbPacketStatus::Stall; + } + }; + } + + fn handle_token_in(&mut self, p: &mut UsbPacket) { + let mut buf = Vec::new(); + if let Some(ep) = &p.ep { + let ep = ep.upgrade().unwrap(); + let locked_ep = ep.lock().unwrap(); + if locked_ep.nr == 1 { + if self.num == 0 { + debug!("No data in usb device."); + p.status = UsbPacketStatus::Nak; + return; + } + match self.kind { + HidType::Keyboard => { + buf = self.keyboard_poll(); + } + HidType::Tablet => { + buf = self.pointer_poll(); + } + _ => { + error!("Unsupported HID device"); + } + } + let len = buf.len(); + usb_packet_transfer(p, &mut buf, len); + } else { + p.status = UsbPacketStatus::Stall; + } + } else { + p.status = UsbPacketStatus::Stall; + } + } +} + +impl Default for Hid { + fn default() -> Self { + Self::new() + } +} + +impl Display for Hid { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!( + f, + "HID head {} num {} kind {:?} protocol {} idle {}", + self.head, self.num, self.kind, self.protocol, self.idle + ) + } +} + +fn increase_queue(head: &mut u32) { + let mut i = *head + 1; + i &= QUEUE_MASK; + *head = i; +} diff --git a/usb/src/lib.rs b/usb/src/lib.rs index 123d143b5..f92fe111d 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -32,5 +32,6 @@ pub mod errors { pub mod bus; pub mod config; mod descriptor; +pub mod hid; pub mod usb; pub mod xhci; -- Gitee From a0a3a32842a2e5d1d93efbc3032dd80363be7d9e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 Aug 2022 21:23:16 +0800 Subject: [PATCH 0123/2187] usb: add usb keyboard Support usb keyboard and you can send key event by keyboard_event. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 13 ++ machine/src/lib.rs | 40 ++++- machine_manager/src/config/usb.rs | 34 +++++ usb/src/keyboard.rs | 236 ++++++++++++++++++++++++++++++ usb/src/lib.rs | 1 + usb/src/usb.rs | 72 ++++++++- 6 files changed, 391 insertions(+), 5 deletions(-) create mode 100644 usb/src/keyboard.rs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 4c05ef19d..7a9e423f4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -638,6 +638,19 @@ Three properties can be set for USB controller. Note: Only one USB controller can be configured, USB controller can only support USB keyboard and USB tablet. +### 2.14 USB Keyboard +The USB keyboard is a keyboard that uses the USB protocol. It should be attached to USB controller. Keypad is not supported yet. + +One property can be set for USB Keyboard. + +* id: unique device id. + +```shell +-device usb-kbd,id=kbd +``` + +Note: Only one keyboard can be configured. + ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 30b0906c8..b45c0f646 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -106,6 +106,8 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; use error_chain::bail; use kvm_ioctls::VcpuFd; +use usb::keyboard::UsbKeyboard; +use usb::usb::UsbDeviceOps; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub use micro_vm::LightMachine; @@ -123,10 +125,11 @@ use errors::{ErrorKind, Result, ResultExt}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vfio, - parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, - MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, - PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, + parse_usb_keyboard, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, + parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, + NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, + FAST_UNPLUG_ON, }; use machine_manager::{ event_loop::EventLoop, @@ -921,6 +924,32 @@ pub trait MachineOps { Ok(()) } + /// Add usb keyboard. + /// + /// # Arguments + /// + /// * `cfg_args` - Keyboard Configuration. + fn add_usb_keyboard(&mut self, cfg_args: &str) -> Result<()> { + let device_cfg = parse_usb_keyboard(cfg_args)?; + let keyboard = UsbKeyboard::new(device_cfg.id); + let kbd = keyboard + .realize() + .chain_err(|| "Failed to realize usb keyboard device")?; + if let Some(bus_device) = self.get_bus_device() { + let locked_dev = bus_device.lock().unwrap(); + if let Some(ctrl) = locked_dev.get("usb.0") { + let mut locked_ctrl = ctrl.lock().unwrap(); + locked_ctrl + .attach_device(&(kbd as Arc>)) + .chain_err(|| "Failed to attach keyboard device")?; + } else { + bail!("No usb controller found"); + } + } else { + bail!("No bus device found"); + } + Ok(()) + } /// Add peripheral devices. /// /// # Arguments @@ -987,6 +1016,9 @@ pub trait MachineOps { "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } + "usb-kbd" => { + self.add_usb_keyboard(cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 897f9433a..dd2f478f2 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -46,6 +46,40 @@ pub fn parse_xhci(conf: &str) -> Result { } else { bail!("id is none for usb xhci"); } + + dev.check()?; + Ok(dev) +} + +pub struct UsbKeyboardConfig { + pub id: String, +} + +impl UsbKeyboardConfig { + fn new() -> Self { + UsbKeyboardConfig { id: String::new() } + } +} + +impl ConfigCheck for UsbKeyboardConfig { + fn check(&self) -> Result<()> { + if self.id.len() > MAX_STRING_LENGTH { + return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + } + Ok(()) + } +} + +pub fn parse_usb_keyboard(conf: &str) -> Result { + let mut cmd_parser = CmdParser::new("usb-kbd"); + cmd_parser.push("").push("id"); + cmd_parser.parse(conf)?; + let mut dev = UsbKeyboardConfig::new(); + if let Some(id) = cmd_parser.get_value::("id")? { + dev.id = id; + } else { + bail!("id is none for usb keyboard"); + } dev.check()?; Ok(dev) } diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs new file mode 100644 index 000000000..884c2fd33 --- /dev/null +++ b/usb/src/keyboard.rs @@ -0,0 +1,236 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex, Weak}; + +use once_cell::sync::Lazy; + +use super::errors::Result; +use crate::config::*; +use crate::descriptor::{ + UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, + UsbInterfaceDescriptor, +}; +use crate::hid::{ + Hid, HidType, DESC_STRINGS, QUEUE_MASK, STR_CONFIG_KEYBOARD, STR_MANUFACTURER, + STR_PRODUCT_KEYBOARD, STR_SERIAL_KEYBOARD, +}; +use crate::usb::{ + notify_controller, usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, + UsbDescIface, UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbDeviceState, + UsbEndpoint, UsbPacket, +}; +use crate::xhci::xhci_controller::XhciDevice; + +/// USB Keyboard Descriptor +static DESC_KEYBOARD: Lazy> = Lazy::new(|| { + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + Arc::new(UsbDesc { + full_dev: Some(DESC_DEVICE_KEYBOARD.clone()), + high_dev: None, + super_dev: None, + strings: s, + }) +}); +/// Keyboard device descriptor +static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDevice { + device_desc: UsbDeviceDescriptor { + bLength: USB_DT_DEVICE_SIZE, + bDescriptorType: USB_DT_DEVICE, + idVendor: 0x0627, + idProduct: 0x0001, + bcdDevice: 0, + iManufacturer: STR_MANUFACTURER, + iProduct: STR_PRODUCT_KEYBOARD, + iSerialNumber: STR_SERIAL_KEYBOARD, + bcdUSB: 0x0100, + bDeviceClass: 0, + bDeviceSubClass: 0, + bDeviceProtocol: 0, + bMaxPacketSize0: 8, + bNumConfigurations: 1, + }, + confs: vec![Arc::new(UsbDescConfig { + config_desc: UsbConfigDescriptor { + bLength: USB_DT_CONFIG_SIZE, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: 0, + bNumInterfaces: 1, + bConfigurationValue: 1, + iConfiguration: STR_CONFIG_KEYBOARD, + bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_REMOTE_WAKEUP, + bMaxPower: 50, + }, + if_groups: Vec::new(), + ifs: vec![DESC_IFACE_KEYBOARD.clone()], + })], + }) +}); +/// Keyboard interface descriptor +static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0, + bAlternateSetting: 0, + bNumEndpoints: 1, + bInterfaceClass: USB_CLASS_HID, + bInterfaceSubClass: 1, + bInterfaceProtocol: 1, + iInterface: 0, + }, + other_desc: vec![Arc::new(UsbDescOther { + length: 0, + /// HID descriptor + data: vec![0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0], + })], + eps: vec![Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST as u8 | 0x1, + bmAttributes: USB_ENDPOINT_ATTR_INT, + wMaxPacketSize: 8, + bInterval: 0xa, + }, + extra: None, + })], + }) +}); + +/// USB keyboard device. +pub struct UsbKeyboard { + id: String, + device: Arc>, + hid: Arc>, + /// USB controller used to notify controller to transfer data. + ctrl: Option>>, + endpoint: Option>>, +} + +impl UsbKeyboard { + pub fn new(id: String) -> Self { + Self { + id, + device: Arc::new(Mutex::new(UsbDevice::new())), + hid: Arc::new(Mutex::new(Hid::new())), + ctrl: None, + endpoint: None, + } + } + + pub fn realize(self) -> Result>> { + let mut locked_usb = self.device.lock().unwrap(); + locked_usb.product_desc = String::from("StratoVirt USB keyboard"); + locked_usb.auto_attach = true; + locked_usb.strings = Vec::new(); + drop(locked_usb); + let kbd = Arc::new(Mutex::new(self)); + let cloned_kbd = kbd.clone(); + usb_endpoint_init(&(kbd as Arc>)); + let mut locked_kbd = cloned_kbd.lock().unwrap(); + locked_kbd.init_hid()?; + drop(locked_kbd); + Ok(cloned_kbd) + } + + fn init_hid(&mut self) -> Result<()> { + let mut locked_usb = self.device.lock().unwrap(); + locked_usb.usb_desc = Some(DESC_KEYBOARD.clone()); + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.kind = HidType::Keyboard; + drop(locked_hid); + let ep = locked_usb.get_endpoint(USB_TOKEN_IN as u32, 1); + self.endpoint = Some(Arc::downgrade(&ep)); + locked_usb.init_descriptor()?; + Ok(()) + } +} + +// Used for VNC to send keyboard event. +pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Result<()> { + let locked_kbd = kbd.lock().unwrap(); + let mut locked_hid = locked_kbd.hid.lock().unwrap(); + for code in scan_codes { + let index = ((locked_hid.head + locked_hid.num) & QUEUE_MASK) as usize; + locked_hid.num += 1; + locked_hid.keyboard.keycodes[index] = *code; + } + drop(locked_hid); + drop(locked_kbd); + let clone_kbd = kbd.clone(); + notify_controller(&(clone_kbd as Arc>)) +} + +impl UsbDeviceOps for UsbKeyboard { + fn reset(&mut self) { + info!("Keyboard device reset"); + let mut locked_usb = self.device.lock().unwrap(); + locked_usb.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; + locked_usb.addr = 0; + locked_usb.state = UsbDeviceState::Default; + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.reset(); + } + + fn handle_control( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ) { + debug!("handle_control request {:?}", device_req); + let mut locked_dev = self.device.lock().unwrap(); + match locked_dev.handle_control_for_descriptor(packet, device_req, data) { + Ok(_) => { + debug!("Keyboard control handled by descriptor, return directly."); + return; + } + Err(e) => { + debug!("Keyboard not handled by descriptor, fallthrough {}", e); + } + } + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.handle_control_packet(packet, device_req, data); + } + + fn handle_data(&mut self, p: &mut UsbPacket) { + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.handle_data_packet(p); + } + + fn device_id(&self) -> String { + self.id.clone() + } + + fn get_usb_device(&self) -> Arc> { + self.device.clone() + } + + fn get_mut_usb_device(&mut self) -> Arc> { + self.device.clone() + } + + fn set_controller(&mut self, ctrl: Weak>) { + self.ctrl = Some(ctrl); + } + + fn get_controller(&self) -> Option>> { + self.ctrl.clone() + } + + fn get_endpoint(&self) -> Option>> { + self.endpoint.clone() + } +} diff --git a/usb/src/lib.rs b/usb/src/lib.rs index f92fe111d..737821800 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -33,5 +33,6 @@ pub mod bus; pub mod config; mod descriptor; pub mod hid; +pub mod keyboard; pub mod usb; pub mod xhci; diff --git a/usb/src/usb.rs b/usb/src/usb.rs index ca3cccaa4..6c7336ddc 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -16,12 +16,15 @@ use std::{ }; use super::errors::Result; -use crate::config::*; use crate::descriptor::{ UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use crate::xhci::xhci_controller::XhciDevice; +use crate::{ + config::*, + xhci::xhci_controller::{get_field, set_field}, +}; const USB_MAX_ENDPOINTS: u32 = 15; const USB_MAX_INTERFACES: u32 = 16; @@ -505,6 +508,12 @@ pub trait UsbDeviceOps: Send + Sync { /// USB deivce need to kick controller in some cases. fn set_controller(&mut self, ctrl: Weak>); + /// Set the controller which the USB device attached. + fn get_controller(&self) -> Option>>; + + /// Get the endpoint to wakeup. + fn get_endpoint(&self) -> Option>>; + /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { let usb_dev = self.get_mut_usb_device(); @@ -639,6 +648,67 @@ pub trait UsbDeviceOps: Send + Sync { } } +/// Notify controller to process data request. +pub fn notify_controller(dev: &Arc>) -> Result<()> { + let locked_dev = dev.lock().unwrap(); + let xhci = if let Some(ctrl) = &locked_dev.get_controller() { + ctrl.upgrade().unwrap() + } else { + bail!("USB controller not found"); + }; + drop(locked_dev); + // Lock controller before device to avoid dead lock. + let mut locked_xhci = xhci.lock().unwrap(); + let locked_dev = dev.lock().unwrap(); + let usb_dev = locked_dev.get_usb_device(); + drop(locked_dev); + let locked_usb_dev = usb_dev.lock().unwrap(); + let usb_port = if let Some(port) = &locked_usb_dev.port { + port.upgrade().unwrap() + } else { + bail!("No usb port found"); + }; + let slot_id = locked_usb_dev.addr; + let wakeup = + locked_usb_dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP; + drop(locked_usb_dev); + let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { + xhci_port + } else { + bail!("No xhci port found"); + }; + if wakeup { + let mut locked_port = xhci_port.lock().unwrap(); + let port_status = get_field(locked_port.portsc, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + if port_status == PLS_U3 { + locked_port.portsc = set_field( + locked_port.portsc, + PLS_RESUME, + PORTSC_PLS_MASK, + PORTSC_PLS_SHIFT, + ); + debug!( + "Update portsc when notify controller, port {} status {}", + locked_port.portsc, port_status + ); + drop(locked_port); + locked_xhci.port_notify(&xhci_port, PORTSC_PLC)?; + } + } + let locked_dev = dev.lock().unwrap(); + let intr = if let Some(intr) = locked_dev.get_endpoint() { + intr + } else { + bail!("No interrupter found"); + }; + drop(locked_dev); + let ep = intr.upgrade().unwrap(); + if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep) { + error!("Failed to wakeup endpoint {}", e); + } + Ok(()) +} + /// Io vector which save the hva. #[derive(Debug, Copy, Clone)] pub struct Iovec { -- Gitee From 269aaf6128881975b3d2fffed6291c345ec3b0a6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 Aug 2022 21:26:48 +0800 Subject: [PATCH 0124/2187] usb: add usb-tablet Support usb tablet and you can sent pointer event by pointer_event and pointer_sync. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 11 ++ machine/src/lib.rs | 46 ++++- machine_manager/src/config/usb.rs | 47 ++++- usb/src/lib.rs | 1 + usb/src/tablet.rs | 281 ++++++++++++++++++++++++++++++ 5 files changed, 371 insertions(+), 15 deletions(-) create mode 100644 usb/src/tablet.rs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 7a9e423f4..1d7c5ff37 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -650,7 +650,18 @@ One property can be set for USB Keyboard. ``` Note: Only one keyboard can be configured. +### 2.15 USB Tablet +Pointer Device which uses alsolute coordinates. It should be attached to USB controller. +One property can be set for USB Tablet. + +* id: unique device id. + +```shell +-device usb-tablet,id=tablet +``` + +Note: Only one tablet can be configured. ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index b45c0f646..17028090a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -106,8 +106,6 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; use error_chain::bail; use kvm_ioctls::VcpuFd; -use usb::keyboard::UsbKeyboard; -use usb::usb::UsbDeviceOps; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub use micro_vm::LightMachine; @@ -126,10 +124,10 @@ use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_usb_keyboard, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, - parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, - NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, - FAST_UNPLUG_ON, + parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_virtconsole, parse_virtio_serial, + parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, + NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, + VmConfig, FAST_UNPLUG_ON, }; use machine_manager::{ event_loop::EventLoop, @@ -140,7 +138,10 @@ use pci::{PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::errors::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; -use usb::{bus::BusDeviceMap, xhci::xhci_pci::XhciPciDevice}; +use usb::{ + bus::BusDeviceMap, keyboard::UsbKeyboard, tablet::UsbTablet, usb::UsbDeviceOps, + xhci::xhci_pci::XhciPciDevice, +}; use util::{ arg_parser, loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, @@ -950,6 +951,34 @@ pub trait MachineOps { } Ok(()) } + + /// Add usb tablet. + /// + /// # Arguments + /// + /// * `cfg_args` - Tablet Configuration. + fn add_usb_tablet(&mut self, cfg_args: &str) -> Result<()> { + let device_cfg = parse_usb_tablet(cfg_args)?; + let tablet = UsbTablet::new(device_cfg.id); + let tbt = tablet + .realize() + .chain_err(|| "Failed to realize usb tablet device")?; + if let Some(bus_device) = self.get_bus_device() { + let locked_dev = bus_device.lock().unwrap(); + if let Some(ctrl) = locked_dev.get("usb.0") { + let mut locked_ctrl = ctrl.lock().unwrap(); + locked_ctrl + .attach_device(&(tbt as Arc>)) + .chain_err(|| "Failed to attach tablet device")?; + } else { + bail!("No usb controller found"); + } + } else { + bail!("No bus device list found"); + } + Ok(()) + } + /// Add peripheral devices. /// /// # Arguments @@ -1019,6 +1048,9 @@ pub trait MachineOps { "usb-kbd" => { self.add_usb_keyboard(cfg_args)?; } + "usb-tablet" => { + self.add_usb_tablet(cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index dd2f478f2..8a8a85c77 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -29,10 +29,7 @@ impl XhciConfig { impl ConfigCheck for XhciConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); - } - Ok(()) + check_id(&self.id) } } @@ -63,10 +60,7 @@ impl UsbKeyboardConfig { impl ConfigCheck for UsbKeyboardConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); - } - Ok(()) + check_id(&self.id) } } @@ -83,3 +77,40 @@ pub fn parse_usb_keyboard(conf: &str) -> Result { dev.check()?; Ok(dev) } + +pub struct UsbTabletConfig { + pub id: String, +} + +impl UsbTabletConfig { + fn new() -> Self { + UsbTabletConfig { id: String::new() } + } +} + +impl ConfigCheck for UsbTabletConfig { + fn check(&self) -> Result<()> { + check_id(&self.id) + } +} + +pub fn parse_usb_tablet(conf: &str) -> Result { + let mut cmd_parser = CmdParser::new("usb-tablet"); + cmd_parser.push("").push("id"); + cmd_parser.parse(conf)?; + let mut dev = UsbTabletConfig::new(); + if let Some(id) = cmd_parser.get_value::("id")? { + dev.id = id; + } else { + bail!("id is none for usb tablet"); + } + dev.check()?; + Ok(dev) +} + +fn check_id(id: &str) -> Result<()> { + if id.len() > MAX_STRING_LENGTH { + return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + } + Ok(()) +} diff --git a/usb/src/lib.rs b/usb/src/lib.rs index 737821800..191a43d3e 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -34,5 +34,6 @@ pub mod config; mod descriptor; pub mod hid; pub mod keyboard; +pub mod tablet; pub mod usb; pub mod xhci; diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs new file mode 100644 index 000000000..58c4e23e4 --- /dev/null +++ b/usb/src/tablet.rs @@ -0,0 +1,281 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex, Weak}; + +use once_cell::sync::Lazy; + +use super::errors::Result; +use crate::config::*; +use crate::descriptor::{UsbConfigDescriptor, UsbDeviceDescriptor, UsbEndpointDescriptor}; +use crate::descriptor::{UsbDescriptorOps, UsbInterfaceDescriptor}; +use crate::hid::QUEUE_MASK; +use crate::hid::{HidType, QUEUE_LENGTH}; +use crate::usb::{notify_controller, UsbDeviceRequest}; +use crate::{ + hid::{ + Hid, DESC_STRINGS, STR_CONFIG_TABLET, STR_MANUFACTURER, STR_PRODUCT_TABLET, + STR_SERIAL_TABLET, + }, + usb::{ + usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, + UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceState, UsbEndpoint, UsbPacket, + }, + xhci::xhci_controller::XhciDevice, +}; + +const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; +const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; + +/// USB Tablet Descriptor +static DESC_TABLET: Lazy> = Lazy::new(|| { + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + Arc::new(UsbDesc { + full_dev: Some(DESC_DEVICE_TABLET.clone()), + high_dev: None, + super_dev: None, + strings: s, + }) +}); +/// Tablet device descriptor +static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDevice { + device_desc: UsbDeviceDescriptor { + bLength: USB_DT_DEVICE_SIZE, + bDescriptorType: USB_DT_DEVICE, + idVendor: 0x0627, + idProduct: 0x0001, + bcdDevice: 0, + iManufacturer: STR_MANUFACTURER, + iProduct: STR_PRODUCT_TABLET, + iSerialNumber: STR_SERIAL_TABLET, + bcdUSB: 0x0100, + bDeviceClass: 0, + bDeviceSubClass: 0, + bDeviceProtocol: 0, + bMaxPacketSize0: 8, + bNumConfigurations: 1, + }, + confs: vec![Arc::new(UsbDescConfig { + config_desc: UsbConfigDescriptor { + bLength: USB_DT_CONFIG_SIZE, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: 0, + bNumInterfaces: 1, + bConfigurationValue: 1, + iConfiguration: STR_CONFIG_TABLET, + bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_REMOTE_WAKEUP, + bMaxPower: 50, + }, + if_groups: Vec::new(), + ifs: vec![DESC_IFACE_TABLET.clone()], + })], + }) +}); +/// Tablet interface descriptor +static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0, + bAlternateSetting: 0, + bNumEndpoints: 1, + bInterfaceClass: USB_CLASS_HID, + bInterfaceSubClass: 0, + bInterfaceProtocol: 0, + iInterface: 0, + }, + other_desc: vec![Arc::new(UsbDescOther { + length: 0, + /// HID descriptor + data: vec![0x09, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0], + })], + eps: vec![Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST as u8 | 0x1, + bmAttributes: USB_ENDPOINT_ATTR_INT, + wMaxPacketSize: 8, + bInterval: 0xa, + }, + extra: None, + })], + }) +}); + +/// USB tablet device. +pub struct UsbTablet { + id: String, + device: Arc>, + hid: Arc>, + /// USB controller used to notify controller to transfer data. + ctrl: Option>>, + endpoint: Option>>, +} + +impl UsbTablet { + pub fn new(id: String) -> Self { + Self { + id, + device: Arc::new(Mutex::new(UsbDevice::new())), + hid: Arc::new(Mutex::new(Hid::new())), + ctrl: None, + endpoint: None, + } + } + + pub fn realize(self) -> Result>> { + let mut locked_usb = self.device.lock().unwrap(); + locked_usb.product_desc = String::from("StratoVirt USB Tablet"); + locked_usb.auto_attach = true; + locked_usb.strings = Vec::new(); + drop(locked_usb); + let tablet = Arc::new(Mutex::new(self)); + let cloned_tablet = tablet.clone(); + usb_endpoint_init(&(tablet as Arc>)); + let mut locked_tablet = cloned_tablet.lock().unwrap(); + locked_tablet.init_hid()?; + drop(locked_tablet); + Ok(cloned_tablet) + } + + fn init_hid(&mut self) -> Result<()> { + let mut locked_usb = self.device.lock().unwrap(); + locked_usb.usb_desc = Some(DESC_TABLET.clone()); + let mut hid = self.hid.lock().unwrap(); + hid.kind = HidType::Tablet; + drop(hid); + let ep = locked_usb.get_endpoint(USB_TOKEN_IN as u32, 1); + self.endpoint = Some(Arc::downgrade(&ep)); + locked_usb.init_descriptor()?; + Ok(()) + } +} + +// Used for VNC to send pointer event. +pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32) -> Result<()> { + let locked_tablet = tablet.lock().unwrap(); + let mut hid = locked_tablet.hid.lock().unwrap(); + let index = ((hid.head + hid.num) & QUEUE_MASK) as usize; + let mut evt = &mut hid.pointer.queue[index]; + if button == INPUT_BUTTON_WHEEL_UP { + evt.pos_z += 1; + } else if button == INPUT_BUTTON_WHEEL_DOWN { + evt.pos_z -= 1 + } + evt.button_state = button; + evt.pos_x = x; + evt.pos_y = y; + Ok(()) +} +/// Used for VNC to sync pointer event. +pub fn pointer_sync(tablet: &Arc>) -> Result<()> { + let locked_tablet = tablet.lock().unwrap(); + let mut locked_hid = locked_tablet.hid.lock().unwrap(); + if locked_hid.num == QUEUE_LENGTH - 1 { + info!("Pointer queue full"); + } + let cur_index = ((locked_hid.head + locked_hid.num) & QUEUE_MASK) as usize; + let pre_index = if cur_index == 0 { + QUEUE_MASK as usize + } else { + (((cur_index as u32) - 1) % QUEUE_MASK) as usize + }; + let nxt_index = (cur_index + 1) % QUEUE_MASK as usize; + let prev = locked_hid.pointer.queue[pre_index]; + let curr = locked_hid.pointer.queue[cur_index]; + let mut comp = false; + if locked_hid.num > 0 && curr.button_state == prev.button_state { + comp = true; + } + if comp { + locked_hid.pointer.queue[pre_index].pos_x = curr.pos_x; + locked_hid.pointer.queue[pre_index].pos_y = curr.pos_y; + locked_hid.pointer.queue[pre_index].pos_z += curr.pos_z; + locked_hid.pointer.queue[cur_index].pos_z = 0; + return Ok(()); + } else { + locked_hid.pointer.queue[nxt_index].pos_x = curr.pos_x; + locked_hid.pointer.queue[nxt_index].pos_y = curr.pos_y; + locked_hid.pointer.queue[nxt_index].pos_z = 0; + locked_hid.pointer.queue[nxt_index].button_state = curr.button_state; + locked_hid.num += 1; + } + drop(locked_hid); + drop(locked_tablet); + let clone_tablet = tablet.clone(); + notify_controller(&(clone_tablet as Arc>)) +} + +impl UsbDeviceOps for UsbTablet { + fn reset(&mut self) { + info!("Tablet device reset"); + let mut locked_usb = self.device.lock().unwrap(); + locked_usb.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; + locked_usb.addr = 0; + locked_usb.state = UsbDeviceState::Default; + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.reset(); + } + + fn handle_control( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + data: &mut [u8], + ) { + debug!("handle_control request {:?}", device_req); + let mut locked_dev = self.device.lock().unwrap(); + match locked_dev.handle_control_for_descriptor(packet, device_req, data) { + Ok(_) => { + debug!("Tablet Device control handled by descriptor, return directly."); + return; + } + Err(e) => { + debug!("Tablet not handled by descriptor, fallthrough {}", e); + } + } + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.handle_control_packet(packet, device_req, data); + } + + fn handle_data(&mut self, p: &mut UsbPacket) { + let mut locked_hid = self.hid.lock().unwrap(); + locked_hid.handle_data_packet(p); + } + + fn device_id(&self) -> String { + self.id.clone() + } + + fn get_usb_device(&self) -> Arc> { + self.device.clone() + } + + fn get_mut_usb_device(&mut self) -> Arc> { + self.device.clone() + } + + fn set_controller(&mut self, ctrl: Weak>) { + self.ctrl = Some(ctrl); + } + + fn get_controller(&self) -> Option>> { + self.ctrl.clone() + } + + fn get_endpoint(&self) -> Option>> { + self.endpoint.clone() + } +} -- Gitee From 9b782f5820754f1d129b821eeb50cef8c863b36f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 17:35:14 +0800 Subject: [PATCH 0125/2187] VNC: StratoVirt supports vnc 1. Parse parameters and generate the configuration of vnc during the initialization. 2. Create a vnc server to accept client's connections. Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- Cargo.toml | 2 + machine/Cargo.toml | 1 + machine/src/standard_vm/aarch64/mod.rs | 3 + machine/src/standard_vm/x86_64/mod.rs | 3 + machine_manager/src/cmdline.rs | 9 +++ machine_manager/src/config/mod.rs | 3 + machine_manager/src/config/vnc.rs | 106 +++++++++++++++++++++++++ vnc/Cargo.toml | 29 +++++++ vnc/src/client.rs | 13 +++ vnc/src/lib.rs | 61 ++++++++++++++ vnc/src/server.rs | 104 ++++++++++++++++++++++++ vnc/src/vnc.rs | 72 +++++++++++++++++ 12 files changed, 406 insertions(+) create mode 100644 machine_manager/src/config/vnc.rs create mode 100644 vnc/Cargo.toml create mode 100644 vnc/src/client.rs create mode 100644 vnc/src/lib.rs create mode 100644 vnc/src/server.rs create mode 100644 vnc/src/vnc.rs diff --git a/Cargo.toml b/Cargo.toml index 81905af52..ccf3a5310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ migration = { path = "migration" } util = { path = "util" } virtio = { path = "virtio" } vfio = { path = "vfio" } +vnc = { path = "vnc" } [workspace] members = [ @@ -38,6 +39,7 @@ members = [ "virtio", "ozone", "vfio", + "vnc", ] [[bin]] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 8848a587f..98bc22f6b 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -31,6 +31,7 @@ util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } usb = { path = "../usb" } +vnc = { path = "../vnc" } [features] default = ["qmp"] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index b6379b37f..c214e2bd9 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -62,6 +62,7 @@ use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; +use vnc::vnc; use super::{errors::Result as StdResult, AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind, Result}; @@ -459,6 +460,8 @@ impl MachineOps for StdMachine { locked_vm .add_devices(vm_config) .chain_err(|| "Failed to add devices")?; + vnc::vnc_init(&vm_config.vnc, &vm_config.object) + .chain_err(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device()?; let migrate = locked_vm.get_migrate_info(); diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 429b1812d..9f5621b49 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -64,6 +64,7 @@ use super::errors::{ErrorKind, Result}; use super::{AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; use crate::{vm_state, MachineOps}; +use vnc::vnc; const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -473,6 +474,8 @@ impl MachineOps for StdMachine { .init_ich9_lpc(clone_vm) .chain_err(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; + vnc::vnc_init(&vm_config.vnc, &vm_config.object) + .chain_err(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device()?; let migrate = locked_vm.get_migrate_info(); diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index b69438c85..613d8f704 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -382,6 +382,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("numa describes the memory read/write latency, bandwidth between Initiator Proximity Domains and Target Proximity Domains(Memory)") .takes_values(true), ) + .arg( + Arg::with_name("vnc") + .multiple(false) + .long("vnc") + .value_name("ip:port") + .help("specify the ip and port for vnc") + .takes_value(true), + ) } /// Create `VmConfig` from `ArgMatches`'s arg. @@ -413,6 +421,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("initrd-file")), vm_cfg, add_initrd); add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); + add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); add_args_to_config!( (args.is_present("mem-prealloc")), vm_cfg, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 257e97f1a..c0b0bce96 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -93,6 +93,7 @@ pub use pci::*; pub use rng::*; pub use usb::*; pub use vfio::*; +pub use vnc::*; mod balloon; mod boot_source; @@ -108,6 +109,7 @@ mod pci; mod rng; mod usb; mod vfio; +pub mod vnc; use std::any::Any; use std::collections::HashMap; @@ -174,6 +176,7 @@ pub struct VmConfig { pub global_config: HashMap, pub numa_nodes: Vec<(String, String)>, pub incoming: Option, + pub vnc: VncConfig, } impl VmConfig { diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs new file mode 100644 index 000000000..a9c22b045 --- /dev/null +++ b/machine_manager/src/config/vnc.rs @@ -0,0 +1,106 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use serde::{Deserialize, Serialize}; + +use super::errors::Result; +use crate::config::{ + errors::ErrorKind, + {CmdParser, VmConfig}, +}; + +/// Configuration of vnc. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct VncConfig { + /// Listening ip. + pub ip: String, + /// Listening port. + pub port: String, + /// Configuration of encryption. + pub tls_creds: String, + /// Authentication switch. + pub sasl: bool, + /// Configuration of authentication. + pub sasl_authz: String, +} + +impl VmConfig { + /// Make configuration for vnc: "chardev" -> "vnc". + pub fn add_vnc(&mut self, vnc_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("vnc"); + cmd_parser + .push("") + .push("tls-creds") + .push("sasl") + .push("sasl-authz"); + cmd_parser.parse(vnc_config)?; + + let mut vnc_config = VncConfig::default(); + if let Some(addr) = cmd_parser.get_value::("")? { + let v: Vec<&str> = addr.split(':').collect(); + if v.len() != 2 { + return Err(ErrorKind::FieldIsMissing("ip", "port").into()); + } + vnc_config.ip = v[0].to_string(); + vnc_config.port = v[1].to_string(); + } else { + return Err(ErrorKind::FieldIsMissing("ip", "port").into()); + } + + if let Some(tls_creds) = cmd_parser.get_value::("tls-creds")? { + vnc_config.tls_creds = tls_creds + } + if let Some(_sasl) = cmd_parser.get_value::("sasl")? { + vnc_config.sasl = true + } else { + vnc_config.sasl = false + } + if let Some(sasl_authz) = cmd_parser.get_value::("sasl-authz")? { + vnc_config.sasl_authz = sasl_authz; + } + + self.vnc = vnc_config; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_vnc() { + let mut vm_config = VmConfig::default(); + vm_config + .add_vnc("0.0.0.0:1,tls-creds=vnc-tls-creds0,sasl,sasl-authz=authz0") + .unwrap(); + assert_eq!(vm_config.vnc.ip, String::from("0.0.0.0")); + assert_eq!(vm_config.vnc.port, String::from("1")); + assert_eq!(vm_config.vnc.tls_creds, String::from("vnc-tls-creds0")); + assert_eq!(vm_config.vnc.sasl, true); + assert_eq!(vm_config.vnc.sasl_authz, String::from("authz0")); + + let mut vm_config = VmConfig::default(); + vm_config + .add_vnc("0.0.0.0:1,tls-creds=vnc-tls-creds0") + .unwrap(); + assert_eq!(vm_config.vnc.sasl, false); + + let mut vm_config = VmConfig::default(); + let res = vm_config.add_vnc("tls-creds=vnc-tls-creds0"); + assert!(res.is_err()); + + let mut vm_config = VmConfig::default(); + let _res = vm_config.add_vnc("0.0.0.0:1,sasl,sasl-authz=authz0"); + assert_eq!(vm_config.vnc.tls_creds, "".to_string()); + } +} diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml new file mode 100644 index 000000000..09e631227 --- /dev/null +++ b/vnc/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "vnc" +version = "2.1.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "Visual Network Computing" + +[dependencies] +byteorder = "1.3.4" +error-chain = "0.12.4" +kvm-ioctls = "0.6.0" +libc = ">=0.2.71" +log = "0.4.8" +serde_json = "1.0.55" +vmm-sys-util = ">=0.7.0" +once_cell = "1.9.0" +sscanf = "0.2.1" +bitintr = "0.2.0" +address_space = { path = "../address_space" } +hypervisor = { path = "../hypervisor" } +machine_manager = { path = "../machine_manager" } +migration = { path = "../migration" } +migration_derive = { path = "../migration_derive" } +sysbus = { path = "../sysbus" } +util = { path = "../util" } +pci = { path = "../pci" } +acpi = { path = "../acpi" } +devices = {path = "../devices"} \ No newline at end of file diff --git a/vnc/src/client.rs b/vnc/src/client.rs new file mode 100644 index 000000000..f427222d4 --- /dev/null +++ b/vnc/src/client.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights r&eserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub struct VncClient {} diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs new file mode 100644 index 000000000..015271d6b --- /dev/null +++ b/vnc/src/lib.rs @@ -0,0 +1,61 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +//#[macro_use] +extern crate sscanf; +//#[macro_use] +extern crate vmm_sys_util; + +pub mod errors { + error_chain! { + links { + Util(util::errors::Error, util::errors::ErrorKind); + } + errors { + UnsupportRFBProtocolVersion { + display("Unsupported RFB Protocol Version!") + } + InvalidImageSize { + display("Invalid Image Size!") + } + TcpBindFailed(reason: String) { + display("Tcp bind failed: {}", reason) + } + MakeTlsConnectionFailed(reason: String) { + display("Make tls connection failed: {}", reason) + } + ProtocolMessageFailed(reason: String) { + display("ProtocolMessage failed: {}", reason) + } + ReadMessageFailed(reason: String) { + display("Read buf form tcpstream failed: {}", reason) + } + AuthFailed(reason: String){ + display("Authentication failed: {}", reason) + } + ParseKeyBoardFailed(reason: String) { + display("ParseKeyBoardFailed: {}", reason) + } + } + } +} + +pub mod client; +pub mod server; +pub mod vnc; +pub use crate::vnc::*; +pub use client::*; +pub use server::*; diff --git a/vnc/src/server.rs b/vnc/src/server.rs new file mode 100644 index 000000000..94678c352 --- /dev/null +++ b/vnc/src/server.rs @@ -0,0 +1,104 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights r&eserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result}; +use machine_manager::{ + config::{ObjConfig, VncConfig}, + event_loop::EventLoop, +}; +use once_cell::sync::Lazy; +use std::{ + collections::HashMap, + net::TcpListener, + os::unix::prelude::{AsRawFd, RawFd}, + sync::{Arc, Mutex}, +}; +use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use vmm_sys_util::epoll::EventSet; + +use crate::{VncClient, VNC_SERVERS}; + +/// VncServer +#[derive(Clone)] +pub struct VncServer { + // Tcp connection listened by server. + listener: Arc>, + // Clients connected to vnc. + pub clients: HashMap>>, + // Connection limit. + conn_limits: usize, +} + +impl VncServer { + /// Create a new VncServer. + pub fn new(listener: Arc>) -> Self { + VncServer { + listener, + clients: HashMap::new(), + conn_limits: 1, + } + } + + /// Make configuration for VncServer. + pub fn make_config( + &mut self, + vnc_cfg: &VncConfig, + object: &HashMap, + ) -> Result<()> { + Ok(()) + } + + /// Listen to the port and accpet client's connection. + pub fn handle_connection(&mut self) -> Result<()> { + Ok(()) + } +} + +/// internal_notifiers for VncServer. +impl EventNotifierHelper for VncServer { + fn internal_notifiers(server_handler: Arc>) -> Vec { + let server = server_handler.clone(); + let handler: Box Option>> = + Box::new(move |event, fd: RawFd| { + read_fd(fd); + + if event & EventSet::HANG_UP == EventSet::HANG_UP { + info!("Client closed."); + } else if event == EventSet::IN { + let mut locked_handler = server.lock().unwrap(); + if let Err(e) = locked_handler.handle_connection() { + error!("Failed to handle vnc client connection, error is {}", e); + } + drop(locked_handler); + } + + None as Option> + }); + + let mut notifiers = Vec::new(); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + server_handler + .lock() + .unwrap() + .listener + .lock() + .unwrap() + .as_raw_fd(), + None, + EventSet::IN | EventSet::HANG_UP, + vec![Arc::new(Mutex::new(handler))], + )); + + notifiers + } +} diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs new file mode 100644 index 000000000..360e22e30 --- /dev/null +++ b/vnc/src/vnc.rs @@ -0,0 +1,72 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights r&eserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result}; +use machine_manager::{ + config::{ObjConfig, VncConfig}, + event_loop::EventLoop, +}; +use once_cell::sync::Lazy; +use std::{ + collections::HashMap, + net::TcpListener, + sync::{Arc, Mutex}, +}; +use util::loop_context::EventNotifierHelper; +use vmm_sys_util::epoll::EventSet; + +use crate::VncServer; + +pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Result<()> { + let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); + let listener: TcpListener; + match TcpListener::bind(&addr.as_str()) { + Ok(l) => listener = l, + Err(e) => { + let msg = format!("Bind {} failed {}", addr, e); + error!("{}", e); + return Err(ErrorKind::TcpBindFailed(msg).into()); + } + } + + listener + .set_nonblocking(true) + .expect("Set noblocking for vnc socket failed"); + + let mut server = VncServer::new(Arc::new(Mutex::new(listener))); + + // Parameter configuation for VncServeer. + if let Err(err) = server.make_config(vnc_cfg, object) { + return Err(err); + } + + // Add an VncServer. + add_vnc_server(server); + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(VNC_SERVERS.lock().unwrap()[0].clone()), + None, + )?; + + Ok(()) +} + +/// Add a vnc server during initialization. +fn add_vnc_server(server: VncServer) { + VNC_SERVERS + .lock() + .unwrap() + .push(Arc::new(Mutex::new(server))); +} + +pub static VNC_SERVERS: Lazy>>>> = + Lazy::new(|| Mutex::new(Vec::new())); -- Gitee From 960b2fb0b847a5c3c86d17c6abc4751251c54eb7 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 17:49:06 +0800 Subject: [PATCH 0126/2187] VNC: StratoVirt can add vnc client 1. Add VncClient struct to record the information of connnection whith client. 2. Listen for client's connection. 3. Exchange RFB protocol version with client. Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- vnc/src/client.rs | 190 +++++++++++++++++++++++++++++++++++++++++++++- vnc/src/server.rs | 32 +++++++- 2 files changed, 219 insertions(+), 3 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index f427222d4..e5bea7567 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -10,4 +10,192 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub struct VncClient {} +use super::errors::{ErrorKind, Result}; +use error_chain::ChainedError; +use machine_manager::event_loop::EventLoop; +use sscanf::scanf; +use std::{ + cmp, + io::{Read, Write}, + net::{Shutdown, TcpStream}, + os::unix::prelude::{AsRawFd, RawFd}, + sync::{Arc, Mutex}, +}; +use util::{ + bitmap::Bitmap, + loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}, +}; +use vmm_sys_util::epoll::EventSet; + +use crate::{VncServer, VNC_SERVERS}; + +/// RFB protocol version. +struct VncVersion { + major: u16, + minor: u16, +} + +impl VncVersion { + pub fn new(major: u16, minor: u16) -> Self { + VncVersion { major, minor } + } +} + +impl Default for VncVersion { + fn default() -> Self { + Self::new(0, 0) + } +} + +/// VncClient struct to record the information of connnection. +pub struct VncClient { + /// TcpStream connected with client. + pub stream: TcpStream, + /// Size of buff in next handle. + pub expect: usize, + /// Connection status. + pub dis_conn: bool, + /// RFB protocol version. + version: VncVersion, + /// The function handling the connection. + pub handlers: Vec>>>, + /// Pointer to VncServer. + pub server: Arc>, + /// Data storage type for client. + big_endian: bool, + /// Tcp listening address. + pub addr: String, + /// Image width. + width: i32, + /// Image height. + height: i32, + /// Image display feature. + feature: i32, + /// The pixel need to convert. + pixel_convert: bool, +} + +impl VncClient { + pub fn new(stream: TcpStream, addr: String, server: Arc>) -> Self { + VncClient { + stream, + expect: 12, + dis_conn: false, + version: VncVersion::default(), + handlers: Vec::new(), + server, + big_endian: false, + addr, + width: 0, + height: 0, + feature: 0, + pixel_convert: false, + } + } + + /// Modify event notifiers to event loop + /// + /// # Arguments + /// + /// * `op` - Notifier operation. + /// * `idx` - Idx of event in server.handlers + pub fn modify_event(&mut self, op: NotifierOperation, idx: usize) -> Result<()> { + let mut handlers = Vec::new(); + + if let NotifierOperation::Modify = op { + if self.handlers.len() <= idx { + return Ok(()); + } + handlers.push(self.handlers[idx].clone()); + } + + EventLoop::update_event( + vec![EventNotifier::new( + op, + self.stream.as_raw_fd(), + None, + EventSet::IN | EventSet::READ_HANG_UP, + handlers, + )], + None, + )?; + + Ok(()) + } + + /// Send plain txt. + pub fn write_plain_msg(&mut self, buf: &[u8]) { + let buf_size = buf.len(); + let mut offset = 0; + loop { + let tmp_buf = &buf[offset..]; + match self.stream.write(tmp_buf) { + Ok(ret) => { + offset += ret; + } + Err(e) => { + error!("write msg error: {:?}", e); + } + } + self.stream.flush().unwrap(); + if offset >= buf_size { + break; + } + } + } + + /// write buf to stream + /// Choose different channel according to whether or not to encrypt + /// + /// # Arguments + /// * `buf` - Data to be send. + pub fn write_msg(&mut self, buf: &[u8]) { + self.write_plain_msg(buf); + } + + /// Clear the data when disconnected from client. + pub fn disconnect(&mut self) { + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_server = server.lock().unwrap(); + locked_server.clients.remove(&self.addr); + + drop(locked_server); + + if let Err(e) = self.modify_event(NotifierOperation::Delete, 0) { + error!("Failed to delete event, error is {}", e.display_chain()); + } + + if let Err(e) = self.stream.shutdown(Shutdown::Both) { + info!("Shutdown stream failed: {}", e); + } + self.handlers.clear(); + } +} + +/// Internal_notifiers for VncClient. +impl EventNotifierHelper for VncClient { + fn internal_notifiers(client_handler: Arc>) -> Vec { + let client = client_handler.clone(); + let handler: Box Option>> = + Box::new(move |event, _| { + let mut dis_conn = false; + + if dis_conn { + client.lock().unwrap().disconnect(); + } + + None as Option> + }); + + let mut locked_client = client_handler.lock().unwrap(); + locked_client.handlers.push(Arc::new(Mutex::new(handler))); + + vec![EventNotifier::new( + NotifierOperation::AddShared, + locked_client.stream.as_raw_fd(), + None, + EventSet::IN | EventSet::READ_HANG_UP, + vec![locked_client.handlers[0].clone()], + )] + } +} diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 94678c352..5ed35a59b 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -18,7 +18,7 @@ use machine_manager::{ use once_cell::sync::Lazy; use std::{ collections::HashMap, - net::TcpListener, + net::{Shutdown, TcpListener}, os::unix::prelude::{AsRawFd, RawFd}, sync::{Arc, Mutex}, }; @@ -38,6 +38,8 @@ pub struct VncServer { conn_limits: usize, } +unsafe impl Send for VncServer {} + impl VncServer { /// Create a new VncServer. pub fn new(listener: Arc>) -> Self { @@ -59,11 +61,37 @@ impl VncServer { /// Listen to the port and accpet client's connection. pub fn handle_connection(&mut self) -> Result<()> { + match self.listener.lock().unwrap().accept() { + Ok((stream, addr)) => { + if self.clients.len() >= self.conn_limits { + stream.shutdown(Shutdown::Both).unwrap(); + return Ok(()); + } + info!("new client: {:?}", addr); + stream + .set_nonblocking(true) + .expect("set nonblocking failed"); + + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut client = VncClient::new(stream, addr.to_string(), server); + client.write_msg("RFB 003.008\n".to_string().as_bytes()); + info!("{:?}", client.stream); + + let tmp_client = Arc::new(Mutex::new(client)); + self.clients.insert(addr.to_string(), tmp_client.clone()); + + EventLoop::update_event(EventNotifierHelper::internal_notifiers(tmp_client), None)?; + } + Err(e) => { + info!("Connect failed: {:?}", e); + } + } + Ok(()) } } -/// internal_notifiers for VncServer. +/// Internal_notifiers for VncServer. impl EventNotifierHelper for VncServer { fn internal_notifiers(server_handler: Arc>) -> Vec { let server = server_handler.clone(); -- Gitee From 30088cc72b5069f2f8931d9c8031f00009cb43e4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 17:54:17 +0800 Subject: [PATCH 0127/2187] VNC: support bufferpool Implement a simple buffpool to cache the data received from tcpstream, in order to improve the read performance. Signed-off-by: Xiao Ye --- vnc/src/lib.rs | 2 ++ vnc/src/utils.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 vnc/src/utils.rs diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 015271d6b..61f11fef2 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -55,7 +55,9 @@ pub mod errors { pub mod client; pub mod server; +pub mod utils; pub mod vnc; pub use crate::vnc::*; pub use client::*; pub use server::*; +pub use utils::BuffPool; diff --git a/vnc/src/utils.rs b/vnc/src/utils.rs new file mode 100644 index 000000000..b99763dc4 --- /dev/null +++ b/vnc/src/utils.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cmp; + +/// Simple bufferpool can improve read performance of tcpstream. +pub struct BuffPool { + /// Cache received data. + buf: Vec, + /// Start Byte. + pos: usize, + /// Number of bytes in buff. + cap: usize, +} + +impl Default for BuffPool { + fn default() -> Self { + Self::new() + } +} + +/// The buffpool to improve read performance. +impl BuffPool { + pub fn new() -> Self { + BuffPool { + buf: Vec::new(), + pos: 0, + cap: 0, + } + } + + /// Read from the buff. + pub fn read(&mut self, buf: &mut Vec) { + self.buf.drain(..self.pos); + self.buf.append(buf); + self.pos = 0; + self.cap = self.buf.len(); + } + + /// Return the len of the buffpool. + pub fn len(&mut self) -> usize { + self.cap + } + + /// Is empty. + pub fn is_empty(&mut self) -> bool { + self.cap != 0 + } + + /// Read from front. + pub fn read_front(&mut self, len: usize) -> &[u8] { + let length = cmp::min(self.cap, len); + &self.buf[self.pos..self.pos + length] + } + + /// Remove front. + pub fn remov_front(&mut self, len: usize) { + self.pos = cmp::min(self.pos + len, self.buf.len()); + self.cap = cmp::max(0_usize, self.cap - len); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_buff_pool() { + let mut buffpool = BuffPool::new(); + buffpool.read(&mut (0x12345678 as u32).to_be_bytes().to_vec()); + assert!(buffpool.len() == 4 as usize); + buffpool.remov_front(1); + assert!(buffpool.read_front(3) == vec![52, 86, 120]); + } +} -- Gitee From cab1016aa29786c2929927e0f170e8c21368b5e5 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 18:14:37 +0800 Subject: [PATCH 0128/2187] VNC: Exchange information with clients 1. Protocol compression method. 2. Protocol authentication and encryption type. 3. Handle_protocol_msg: handle message with client. Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 40 +++++ vnc/src/client.rs | 439 +++++++++++++++++++++++++++++++++++++++++++++- vnc/src/input.rs | 55 ++++++ vnc/src/lib.rs | 4 + 4 files changed, 535 insertions(+), 3 deletions(-) create mode 100644 vnc/src/auth.rs create mode 100644 vnc/src/input.rs diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs new file mode 100644 index 000000000..a101aa7b9 --- /dev/null +++ b/vnc/src/auth.rs @@ -0,0 +1,40 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result}; +use crate::VncClient; + +/// Authentication type +#[derive(Clone, Copy)] +pub enum AuthState { + Invalid = 0, + No = 1, + Vnc = 2, + Vencrypt = 19, + Sasl = 20, +} + +/// Authentication and encryption method +#[derive(Clone, Copy)] +pub enum SubAuthState { + VncAuthVencryptPlain = 256, + VncAuthVencryptX509None = 260, + VncAuthVencryptX509Sasl = 263, + VncAuthVencryptTlssasl = 264, +} + +impl VncClient { + /// Send auth version + pub fn protocol_client_vencrypt_init(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/vnc/src/client.rs b/vnc/src/client.rs index e5bea7567..a3f9e506f 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -27,7 +27,73 @@ use util::{ }; use vmm_sys_util::epoll::EventSet; -use crate::{VncServer, VNC_SERVERS}; +use crate::{AuthState, BuffPool, SubAuthState, VncServer, VNC_SERVERS}; + +const MAX_RECVBUF_LEN: usize = 1024; + +// VNC encodings types +pub const ENCODING_RAW: i32 = 0; +const ENCODING_HEXTILE: i32 = 5; +const ENCODING_ZLIB: i32 = 6; +const ENCODING_TIGHT: i32 = 7; +const ENCODING_ZRLE: i32 = 16; +const ENCODING_ZYWRLE: i32 = 17; +const ENCODING_DESKTOPRESIZE: i32 = -223; +pub const ENCODING_RICH_CURSOR: i32 = -239; +const ENCODING_POINTER_TYPE_CHANGE: i32 = -257; +const ENCODING_LED_STATE: i32 = -261; +const ENCODING_DESKTOP_RESIZE_EXT: i32 = -308; +pub const ENCODING_ALPHA_CURSOR: i32 = -314; +const ENCODING_WMVI: i32 = 1464686185; + +/// Image display feature. +pub enum VncFeatures { + VncFeatureResize, + VncFeatureResizeExt, + VncFeatureHextile, + VncFeaturePointerTypeChange, + VncFeatureWmvi, + VncFeatureTight, + VncFeatureZlib, + VncFeatureRichCursor, + VncFeatureAlphaCursor, + VncFeatureTightPng, + VncFeatureZrle, + VncFeatureZywrle, + VncFeatureLedState, + VncFeatureXvp, + VncFeatureClipboardExt, +} + +// According to Remote Framebuffer Protocol +pub enum ClientMsg { + SetPixelFormat = 0, + SetEncodings = 2, + FramebufferUpdateRequest = 3, + KeyEvent = 4, + PointerEvent = 5, + ClientCutText = 6, + InvalidMsg, +} + +// Message id of server +pub enum ServerMsg { + FramebufferUpdate = 0, +} + +impl From for ClientMsg { + fn from(v: u8) -> Self { + match v { + 0 => ClientMsg::SetPixelFormat, + 2 => ClientMsg::SetEncodings, + 3 => ClientMsg::FramebufferUpdateRequest, + 4 => ClientMsg::KeyEvent, + 5 => ClientMsg::PointerEvent, + 6 => ClientMsg::ClientCutText, + _ => ClientMsg::InvalidMsg, + } + } +} /// RFB protocol version. struct VncVersion { @@ -47,28 +113,47 @@ impl Default for VncVersion { } } +#[derive(PartialEq)] +pub enum UpdateState { + No, + Incremental, + Force, +} + /// VncClient struct to record the information of connnection. pub struct VncClient { /// TcpStream connected with client. pub stream: TcpStream, + /// TcpStream receive buffer. + pub buffpool: BuffPool, /// Size of buff in next handle. pub expect: usize, /// Connection status. pub dis_conn: bool, /// RFB protocol version. version: VncVersion, + /// Auth type. + auth: AuthState, + /// SubAuth type. + pub subauth: SubAuthState, + /// Message handler. + pub handle_msg: fn(&mut VncClient) -> Result<()>, /// The function handling the connection. pub handlers: Vec>>>, /// Pointer to VncServer. pub server: Arc>, /// Data storage type for client. big_endian: bool, + /// State flags whether the image needs to be updated for the client. + state: UpdateState, /// Tcp listening address. pub addr: String, /// Image width. width: i32, /// Image height. height: i32, + /// Encoding type. + encoding: i32, /// Image display feature. feature: i32, /// The pixel need to convert. @@ -79,15 +164,21 @@ impl VncClient { pub fn new(stream: TcpStream, addr: String, server: Arc>) -> Self { VncClient { stream, + buffpool: BuffPool::new(), expect: 12, dis_conn: false, version: VncVersion::default(), + handle_msg: VncClient::handle_version, + auth: AuthState::No, + subauth: SubAuthState::VncAuthVencryptPlain, handlers: Vec::new(), server, big_endian: false, + state: UpdateState::No, addr, width: 0, height: 0, + encoding: 0, feature: 0, pixel_convert: false, } @@ -123,6 +214,22 @@ impl VncClient { Ok(()) } + /// Read plain txt + pub fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { + let mut len = 0 as usize; + buf.resize(MAX_RECVBUF_LEN, 0u8); + match self.stream.read(buf) { + Ok(ret) => { + len = ret; + } + Err(e) => { + error!("read msg error: {}", e); + } + } + + Ok(len) + } + /// Send plain txt. pub fn write_plain_msg(&mut self, buf: &[u8]) { let buf_size = buf.len(); @@ -144,7 +251,7 @@ impl VncClient { } } - /// write buf to stream + /// Write buf to stream /// Choose different channel according to whether or not to encrypt /// /// # Arguments @@ -153,7 +260,313 @@ impl VncClient { self.write_plain_msg(buf); } - /// Clear the data when disconnected from client. + /// Read buf from stream, return the size of buff. + pub fn read_msg(&mut self, buf: &mut Vec) -> Result { + self.read_plain_msg(buf) + } + + /// Read buf from tcpstream. + pub fn from_tcpstream_to_buff(&mut self) -> Result<()> { + let mut buf = Vec::new(); + match self.read_msg(&mut buf) { + Ok(len) => { + self.buffpool.read(&mut buf[0..len].to_vec()); + } + Err(e) => { + return Err(e); + } + } + + Ok(()) + } + + /// Exchange RFB protocol version with client. + fn handle_version(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + let res = String::from_utf8_lossy(&buf[..]); + let ver_str = &res[0..12].to_string(); + let ver; + match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { + Ok(v) => { + ver = v; + } + Err(e) => { + let msg = format!("Unsupport RFB version: {}", e); + error!("{}", msg); + return Err(ErrorKind::UnsupportRFBProtocolVersion.into()); + } + } + self.version.major = ver.0 as u16; + self.version.minor = ver.1 as u16; + if self.version.major != 3 || ![3, 4, 5, 7, 8].contains(&self.version.minor) { + let mut buf = Vec::new(); + buf.append(&mut (AuthState::Invalid as u32).to_be_bytes().to_vec()); + self.write_msg(&buf); + return Err(ErrorKind::UnsupportRFBProtocolVersion.into()); + } + + if [4, 5].contains(&self.version.minor) { + self.version.minor = 3; + } + + if self.version.minor == 3 { + error!("Waiting for handle minor=3 ..."); + match self.auth { + AuthState::No => { + let mut buf = Vec::new(); + buf.append(&mut (AuthState::No as u32).to_be_bytes().to_vec()); + self.write_msg(&buf); + self.update_event_handler(1, VncClient::handle_client_init); + } + _ => { + self.auth_failed("Unsupported auth method"); + return Err( + ErrorKind::AuthFailed(String::from("Unsupported auth method")).into(), + ); + } + } + } else { + let mut buf = [0u8; 2]; + buf[0] = 1; // Number of security types. + buf[1] = self.auth as u8; + self.write_msg(&buf); + self.update_event_handler(1, VncClient::handle_auth); + } + Ok(()) + } + + /// Invalid authentication, send 1 to reject. + fn auth_failed(&mut self, msg: &str) { + let auth_rej: u8 = 1; + let mut buf: Vec = vec![1u8]; + buf.append(&mut (auth_rej as u32).to_be_bytes().to_vec()); + if self.version.minor >= 8 { + let err_msg = msg; + buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); + buf.append(&mut err_msg.as_bytes().to_vec()); + } + self.write_msg(&buf); + } + + /// Authentication + fn handle_auth(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + + if buf[0] != self.auth as u8 { + self.auth_failed("Authentication failed"); + error!("handle_auth"); + return Err(ErrorKind::AuthFailed(String::from("handle_auth")).into()); + } + + match self.auth { + AuthState::No => { + if self.version.minor >= 8 { + let buf = [0u8; 4]; + self.write_msg(&buf); + } + self.update_event_handler(1, VncClient::handle_client_init); + } + AuthState::Vencrypt => { + // Send VeNCrypt version 0.2. + let mut buf = [0u8; 2]; + buf[0] = 0_u8; + buf[1] = 2_u8; + + self.write_msg(&buf); + self.update_event_handler(2, VncClient::protocol_client_vencrypt_init); + } + _ => { + self.auth_failed("Unhandled auth method"); + error!("handle_auth"); + return Err(ErrorKind::AuthFailed(String::from("handle_auth")).into()); + } + } + Ok(()) + } + + /// Initialize the connection of vnc client + pub fn handle_client_init(&mut self) -> Result<()> { + let mut buf = Vec::new(); + self.write_msg(&buf); + self.update_event_handler(1, VncClient::handle_protocol_msg); + Ok(()) + } + + /// Set image format + fn set_pixel_format(&mut self) -> Result<()> { + if self.expect == 1 { + self.expect = 20; + return Ok(()); + } + + let mut buf = self.buffpool.read_front(self.expect).to_vec(); + + self.update_event_handler(1, VncClient::handle_protocol_msg); + Ok(()) + } + + /// Update image for client + fn update_frame_buff(&mut self) { + if self.expect == 1 { + self.expect = 10; + return; + } + let buf = self.buffpool.read_front(self.expect); + + self.update_event_handler(1, VncClient::handle_protocol_msg); + } + + /// Set encoding + fn set_encodings(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + if self.expect == 1 { + self.expect = 4; + return Ok(()); + } + + let mut num_encoding: u16; + if self.expect == 4 { + num_encoding = u16::from_be_bytes([buf[2], buf[3]]); + if num_encoding > 0 { + self.expect = 4 + (num_encoding as usize) * 4; + return Ok(()); + } + } else { + num_encoding = u16::from_be_bytes([buf[2], buf[3]]); + } + + while num_encoding > 0 { + let offset = (4 * num_encoding) as usize; + let enc = i32::from_be_bytes([ + buf[offset], + buf[offset + 1], + buf[offset + 2], + buf[offset + 3], + ]); + match enc { + ENCODING_RAW => { + self.encoding = enc; + } + ENCODING_HEXTILE => { + self.feature |= 1 << VncFeatures::VncFeatureHextile as usize; + self.encoding = enc; + } + ENCODING_TIGHT => { + self.feature |= 1 << VncFeatures::VncFeatureTight as usize; + self.encoding = enc; + } + ENCODING_ZLIB => { + // ZRLE compress better than ZLIB, so prioritize ZRLE. + if self.feature & (1 << VncFeatures::VncFeatureZrle as usize) == 0 { + self.feature |= 1 << VncFeatures::VncFeatureZlib as usize; + self.encoding = enc; + } + } + ENCODING_ZRLE => { + self.feature |= 1 << VncFeatures::VncFeatureZrle as usize; + self.encoding = enc; + } + ENCODING_ZYWRLE => { + self.feature |= 1 << VncFeatures::VncFeatureZywrle as usize; + self.encoding = enc; + } + ENCODING_DESKTOPRESIZE => { + self.feature |= 1 << VncFeatures::VncFeatureResize as usize; + } + ENCODING_DESKTOP_RESIZE_EXT => { + self.feature |= 1 << VncFeatures::VncFeatureResizeExt as usize; + } + ENCODING_POINTER_TYPE_CHANGE => { + self.feature |= 1 << VncFeatures::VncFeaturePointerTypeChange as usize; + } + ENCODING_RICH_CURSOR => { + self.feature |= 1 << VncFeatures::VncFeatureRichCursor as usize; + } + ENCODING_ALPHA_CURSOR => { + self.feature |= 1 << VncFeatures::VncFeatureAlphaCursor as usize; + } + ENCODING_WMVI => { + self.feature |= 1 << VncFeatures::VncFeatureWmvi as usize; + } + ENCODING_LED_STATE => { + self.feature |= 1 << VncFeatures::VncFeatureLedState as usize; + } + // ENCODING_EXT_KEY_EVENT => {} + // ENCODING_AUDIO => {} + // VNC_ENCODING_XVP => {} + // ENCODING_CLIPBOARD_EXT => {} + // ENCODING_COMPRESSLEVEL0 ..= ENCODING_COMPRESSLEVEL0 + 9 => {} + // ENCODING_QUALITYLEVEL0 ..= ENCODING_QUALITYLEVEL0 + 9 => {} + _ => { + error!("Unknow encoding"); + } + } + + num_encoding -= 1; + } + + self.encoding = 0; + self.desktop_resize(); + + self.update_event_handler(1, VncClient::handle_protocol_msg); + Ok(()) + } + + fn has_feature(&mut self, feature: VncFeatures) -> bool { + self.feature & (1 << feature as usize) != 0 + } + + fn desktop_resize(&mut self) { + // If hash feature VNC_FEATURE_RESIZE + } + + /// Process the data sent by the client + pub fn handle_protocol_msg(&mut self) -> Result<()> { + // According to RFB protocol, first byte identifies the event type. + let buf = self.buffpool.read_front(self.expect); + match ClientMsg::from(buf[0]) { + ClientMsg::SetPixelFormat => { + return self.set_pixel_format(); + } + ClientMsg::SetEncodings => { + return self.set_encodings(); + } + ClientMsg::FramebufferUpdateRequest => { + self.update_frame_buff(); + } + ClientMsg::KeyEvent => { + self.key_envent(); + } + ClientMsg::PointerEvent => { + self.point_event(); + } + ClientMsg::ClientCutText => { + self.client_cut_event(); + } + _ => { + self.update_event_handler(1, VncClient::handle_protocol_msg); + } + } + Ok(()) + } + + /// Action token after the event. + /// + /// # Arguments + /// + /// * `expect` - the size of bytes of next callback function. + /// * `handle_msg` - callback function of the next event. + pub fn update_event_handler( + &mut self, + expect: usize, + handle_msg: fn(&mut VncClient) -> Result<()>, + ) { + self.buffpool.remov_front(self.expect); + self.expect = expect; + self.handle_msg = handle_msg; + } + + /// Clear the data when disconnected from client. pub fn disconnect(&mut self) { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_server = server.lock().unwrap(); @@ -179,6 +592,26 @@ impl EventNotifierHelper for VncClient { let handler: Box Option>> = Box::new(move |event, _| { let mut dis_conn = false; + if event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { + dis_conn = true; + } else if event == EventSet::IN { + let mut locked_client = client.lock().unwrap(); + if let Err(e) = locked_client.from_tcpstream_to_buff() { + error!("Failed to read_msg, error is {}", e.display_chain()); + dis_conn = true; + } + } + + if !dis_conn { + let mut locked_client = client.lock().unwrap(); + while locked_client.buffpool.len() >= locked_client.expect { + if let Err(e) = (locked_client.handle_msg)(&mut locked_client) { + error!("Failed to read_msg, error is {}", e.display_chain()); + dis_conn = true; + break; + } + } + } if dis_conn { client.lock().unwrap().disconnect(); diff --git a/vnc/src/input.rs b/vnc/src/input.rs new file mode 100644 index 000000000..39ffd45a0 --- /dev/null +++ b/vnc/src/input.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::VncClient; + +impl VncClient { + // Keyboard event. + pub fn key_envent(&mut self) { + if self.expect == 1 { + self.expect = 8; + return; + } + let buf = self.buffpool.read_front(self.expect); + + self.update_event_handler(1, VncClient::handle_protocol_msg); + } + + // Mouse event. + pub fn point_event(&mut self) { + if self.expect == 1 { + self.expect = 6; + return; + } + + self.update_event_handler(1, VncClient::handle_protocol_msg); + } + + // Client cut text. + pub fn client_cut_event(&mut self) { + let buf = self.buffpool.read_front(self.expect); + if self.expect == 1 { + self.expect = 8; + return; + } + if self.expect == 8 { + let buf = [buf[4], buf[5], buf[6], buf[7]]; + let len = u32::from_be_bytes(buf); + if len > 0 { + self.expect += len as usize; + return; + } + } + + self.update_event_handler(1, VncClient::handle_protocol_msg); + } +} diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 61f11fef2..e105ab589 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -53,11 +53,15 @@ pub mod errors { } } +pub mod auth; pub mod client; +pub mod input; pub mod server; pub mod utils; pub mod vnc; pub use crate::vnc::*; +pub use auth::*; pub use client::*; +pub use input::*; pub use server::*; pub use utils::BuffPool; -- Gitee From 5421a7de98f4a4dfc97c52d62633df538773aeca Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 18:24:24 +0800 Subject: [PATCH 0129/2187] VNC: Add library of pixman 1. Add Library of pixman. 2. Add functions of image operation. Signed-off-by: Xiao Ye --- .cargo/config | 6 + util/src/lib.rs | 1 + util/src/pixman.rs | 280 +++++++++++++++++++++++++++++++++++++++++++++ vnc/src/client.rs | 4 +- vnc/src/lib.rs | 2 + vnc/src/pixman.rs | 129 +++++++++++++++++++++ 6 files changed, 420 insertions(+), 2 deletions(-) create mode 100644 util/src/pixman.rs create mode 100644 vnc/src/pixman.rs diff --git a/.cargo/config b/.cargo/config index b66bb9cbd..78ea6eb91 100644 --- a/.cargo/config +++ b/.cargo/config @@ -15,5 +15,11 @@ [target.'cfg(any(target_arch="aarch64"))'] rustflags = [ "-C", "link-arg=-lgcc", + "-C", "link-arg=-lpixman-1", +] + +[target.'cfg(any(target_arch="x86_64"))'] +rustflags = [ + "-C", "link-arg=-lpixman-1", ] diff --git a/util/src/lib.rs b/util/src/lib.rs index abbd7a211..4e9a9c8b8 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -24,6 +24,7 @@ pub mod logger; pub mod loop_context; pub mod num_ops; pub mod offsetof; +pub mod pixman; pub mod reader; pub mod seccomp; pub mod syscall; diff --git a/util/src/pixman.rs b/util/src/pixman.rs new file mode 100644 index 000000000..8e3234bf5 --- /dev/null +++ b/util/src/pixman.rs @@ -0,0 +1,280 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +use std::ptr; + +pub type pixman_bool_t = libc::c_int; + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct pixman_box16 { + pub x1: i16, + pub y1: i16, + pub x2: i16, + pub y2: i16, +} +pub type pixman_box16_t = pixman_box16; + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct pixman_color { + pub red: u16, + pub green: u16, + pub blue: u16, + pub alpha: u16, +} +pub type pixman_color_t = pixman_color; + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum pixman_format_code_t { + PIXMAN_a8r8g8b8 = 537036936, + PIXMAN_x8r8g8b8 = 537004168, + PIXMAN_a8b8g8r8 = 537102472, + PIXMAN_x8b8g8r8 = 537069704, + PIXMAN_b8g8r8a8 = 537430152, + PIXMAN_b8g8r8x8 = 537397384, + PIXMAN_r8g8b8a8 = 537495688, + PIXMAN_r8g8b8x8 = 537462920, + PIXMAN_x14r6g6b6 = 537003622, + PIXMAN_x2r10g10b10 = 537004714, + PIXMAN_a2r10g10b10 = 537012906, + PIXMAN_x2b10g10r10 = 537070250, + PIXMAN_a2b10g10r10 = 537078442, + PIXMAN_a8r8g8b8_sRGB = 537561224, + PIXMAN_r8g8b8 = 402786440, + PIXMAN_b8g8r8 = 402851976, + PIXMAN_r5g6b5 = 268567909, + PIXMAN_b5g6r5 = 268633445, + PIXMAN_a1r5g5b5 = 268571989, + PIXMAN_x1r5g5b5 = 268567893, + PIXMAN_a1b5g5r5 = 268637525, + PIXMAN_x1b5g5r5 = 268633429, + PIXMAN_a4r4g4b4 = 268584004, + PIXMAN_x4r4g4b4 = 268567620, + PIXMAN_a4b4g4r4 = 268649540, + PIXMAN_x4b4g4r4 = 268633156, + PIXMAN_a8 = 134316032, + PIXMAN_r3g3b2 = 134349618, + PIXMAN_b2g3r3 = 134415154, + PIXMAN_a2r2g2b2 = 134357538, + PIXMAN_a2b2g2r2 = 134423074, + PIXMAN_c8 = 134479872, + PIXMAN_g8 = 134545408, + PIXMAN_x4a4 = 134299648, + PIXMAN_a4 = 67190784, + PIXMAN_r1g2b1 = 67240225, + PIXMAN_b1g2r1 = 67305761, + PIXMAN_a1r1g1b1 = 67244305, + PIXMAN_a1b1g1r1 = 67309841, + PIXMAN_c4 = 67371008, + PIXMAN_g4 = 67436544, + PIXMAN_a1 = 16846848, + PIXMAN_g1 = 17104896, + PIXMAN_yuy2 = 268828672, + PIXMAN_yv12 = 201785344, +} + +#[repr(C)] +#[derive(Default, Debug, Copy, Clone)] +pub struct pixman_image { + _unused: [u8; 0], +} +pub type pixman_image_t = pixman_image; + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct pixman_rectangle16 { + pub x: i16, + pub y: i16, + pub width: u16, + pub height: u16, +} +pub type pixman_rectangle16_t = pixman_rectangle16; + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct pixman_region16_data { + pub size: libc::c_long, + pub numRects: libc::c_long, +} +pub type pixman_region16_data_t = pixman_region16_data; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct pixman_region16 { + pub extents: pixman_box16_t, + pub data: *mut pixman_region16_data_t, +} +pub type pixman_region16_t = pixman_region16; +impl Default for pixman_region16 { + fn default() -> Self { + pixman_region16 { + extents: pixman_box16_t::default(), + data: ptr::null_mut(), + } + } +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum pixman_op_t { + PIXMAN_OP_CLEAR = 0, + PIXMAN_OP_SRC = 1, + PIXMAN_OP_DST = 2, + PIXMAN_OP_OVER = 3, + PIXMAN_OP_OVER_REVERSE = 4, + PIXMAN_OP_IN = 5, + PIXMAN_OP_IN_REVERSE = 6, + PIXMAN_OP_OUT = 7, + PIXMAN_OP_OUT_REVERSE = 8, + PIXMAN_OP_ATOP = 9, + PIXMAN_OP_ATOP_REVERSE = 10, + PIXMAN_OP_XOR = 11, + PIXMAN_OP_ADD = 12, + PIXMAN_OP_SATURATE = 13, + PIXMAN_OP_DISJOINT_CLEAR = 16, + PIXMAN_OP_DISJOINT_SRC = 17, + PIXMAN_OP_DISJOINT_DST = 18, + PIXMAN_OP_DISJOINT_OVER = 19, + PIXMAN_OP_DISJOINT_OVER_REVERSE = 20, + PIXMAN_OP_DISJOINT_IN = 21, + PIXMAN_OP_DISJOINT_IN_REVERSE = 22, + PIXMAN_OP_DISJOINT_OUT = 23, + PIXMAN_OP_DISJOINT_OUT_REVERSE = 24, + PIXMAN_OP_DISJOINT_ATOP = 25, + PIXMAN_OP_DISJOINT_ATOP_REVERSE = 26, + PIXMAN_OP_DISJOINT_XOR = 27, + PIXMAN_OP_CONJOINT_CLEAR = 32, + PIXMAN_OP_CONJOINT_SRC = 33, + PIXMAN_OP_CONJOINT_DST = 34, + PIXMAN_OP_CONJOINT_OVER = 35, + PIXMAN_OP_CONJOINT_OVER_REVERSE = 36, + PIXMAN_OP_CONJOINT_IN = 37, + PIXMAN_OP_CONJOINT_IN_REVERSE = 38, + PIXMAN_OP_CONJOINT_OUT = 39, + PIXMAN_OP_CONJOINT_OUT_REVERSE = 40, + PIXMAN_OP_CONJOINT_ATOP = 41, + PIXMAN_OP_CONJOINT_ATOP_REVERSE = 42, + PIXMAN_OP_CONJOINT_XOR = 43, + PIXMAN_OP_MULTIPLY = 48, + PIXMAN_OP_SCREEN = 49, + PIXMAN_OP_OVERLAY = 50, + PIXMAN_OP_DARKEN = 51, + PIXMAN_OP_LIGHTEN = 52, + PIXMAN_OP_COLOR_DODGE = 53, + PIXMAN_OP_COLOR_BURN = 54, + PIXMAN_OP_HARD_LIGHT = 55, + PIXMAN_OP_SOFT_LIGHT = 56, + PIXMAN_OP_DIFFERENCE = 57, + PIXMAN_OP_EXCLUSION = 58, + PIXMAN_OP_HSL_HUE = 59, + PIXMAN_OP_HSL_SATURATION = 60, + PIXMAN_OP_HSL_COLOR = 61, + PIXMAN_OP_HSL_LUMINOSITY = 62, +} + +pub type pixman_image_destroy_func_t = ::std::option::Option< + unsafe extern "C" fn(image: *mut pixman_image_t, data: *mut libc::c_void), +>; + +pub extern "C" fn virtio_gpu_unref_resource_callback( + _image: *mut pixman_image_t, + data: *mut libc::c_void, +) { + unsafe { pixman_image_unref(data.cast()) }; +} + +pub fn pixman_format_reshift(val: u32, ofs: u32, num: u32) -> u32 { + ((val >> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3) +} +pub fn pixman_format_bpp(val: u32) -> u8 { + pixman_format_reshift(val, 24, 8) as u8 +} + +pub fn pixman_format_a(val: u32) -> u8 { + pixman_format_reshift(val, 12, 4) as u8 +} +pub fn pixman_format_r(val: u32) -> u8 { + pixman_format_reshift(val, 8, 4) as u8 +} +pub fn pixman_format_g(val: u32) -> u8 { + pixman_format_reshift(val, 4, 4) as u8 +} +pub fn pixman_format_b(val: u32) -> u8 { + pixman_format_reshift(val, 0, 4) as u8 +} +pub fn pixman_format_depth(val: u32) -> u8 { + pixman_format_a(val) + pixman_format_r(val) + pixman_format_g(val) + pixman_format_b(val) +} + +extern "C" { + pub fn pixman_format_supported_source(format: pixman_format_code_t) -> pixman_bool_t; + pub fn pixman_image_composite( + op: pixman_op_t, + src: *mut pixman_image_t, + mask: *mut pixman_image_t, + dest: *mut pixman_image_t, + src_x: i16, + src_y: i16, + mask_x: i16, + mask_y: i16, + dest_x: i16, + dest_y: i16, + width: u16, + height: u16, + ); + pub fn pixman_image_create_bits( + format: pixman_format_code_t, + width: libc::c_int, + height: libc::c_int, + bits: *mut u32, + rowstride_bytes: libc::c_int, + ) -> *mut pixman_image_t; + pub fn pixman_image_create_solid_fill(color: *const pixman_color_t) -> *mut pixman_image_t; + pub fn pixman_image_fill_rectangles( + op: pixman_op_t, + image: *mut pixman_image_t, + color: *const pixman_color_t, + n_rects: libc::c_int, + rects: *const pixman_rectangle16_t, + ) -> pixman_bool_t; + pub fn pixman_image_get_data(image: *mut pixman_image_t) -> *mut u32; + pub fn pixman_image_get_format(image: *mut pixman_image_t) -> pixman_format_code_t; + pub fn pixman_image_get_height(image: *mut pixman_image_t) -> libc::c_int; + pub fn pixman_image_get_stride(image: *mut pixman_image_t) -> libc::c_int; + pub fn pixman_image_get_width(image: *mut pixman_image_t) -> libc::c_int; + pub fn pixman_image_ref(image: *mut pixman_image_t) -> *mut pixman_image_t; + pub fn pixman_image_set_destroy_function( + image: *mut pixman_image_t, + function: pixman_image_destroy_func_t, + data: *mut libc::c_void, + ); + pub fn pixman_image_unref(image: *mut pixman_image_t) -> pixman_bool_t; + pub fn pixman_region_extents(region: *mut pixman_region16_t) -> *mut pixman_box16_t; + pub fn pixman_region_fini(region: *mut pixman_region16_t); + pub fn pixman_region_init(region: *mut pixman_region16_t); + pub fn pixman_region_init_rect( + region: *mut pixman_region16_t, + x: libc::c_int, + y: libc::c_int, + width: libc::c_uint, + height: libc::c_uint, + ); + pub fn pixman_region_intersect( + new_reg: *mut pixman_region16_t, + reg1: *mut pixman_region16_t, + reg2: *mut pixman_region16_t, + ) -> pixman_bool_t; + pub fn pixman_region_translate(region: *mut pixman_region16_t, x: libc::c_int, y: libc::c_int); +} diff --git a/vnc/src/client.rs b/vnc/src/client.rs index a3f9e506f..8bf161583 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -216,7 +216,7 @@ impl VncClient { /// Read plain txt pub fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { - let mut len = 0 as usize; + let mut len = 0_usize; buf.resize(MAX_RECVBUF_LEN, 0u8); match self.stream.read(buf) { Ok(ret) => { @@ -283,7 +283,7 @@ impl VncClient { /// Exchange RFB protocol version with client. fn handle_version(&mut self) -> Result<()> { let buf = self.buffpool.read_front(self.expect); - let res = String::from_utf8_lossy(&buf[..]); + let res = String::from_utf8_lossy(buf); let ver_str = &res[0..12].to_string(); let ver; match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index e105ab589..582421906 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -56,6 +56,7 @@ pub mod errors { pub mod auth; pub mod client; pub mod input; +pub mod pixman; pub mod server; pub mod utils; pub mod vnc; @@ -63,5 +64,6 @@ pub use crate::vnc::*; pub use auth::*; pub use client::*; pub use input::*; +pub use pixman::*; pub use server::*; pub use utils::BuffPool; diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs new file mode 100644 index 000000000..37739175f --- /dev/null +++ b/vnc/src/pixman.rs @@ -0,0 +1,129 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use bitintr::Popcnt; +use util::pixman::{ + pixman_format_a, pixman_format_b, pixman_format_bpp, pixman_format_code_t, pixman_format_depth, + pixman_format_g, pixman_format_r, pixman_image_get_data, pixman_image_get_format, + pixman_image_get_height, pixman_image_get_stride, pixman_image_get_width, pixman_image_t, +}; + +#[derive(Clone, Default)] +pub struct ColorInfo { + /// Mask color. + pub mask: u32, + /// Shift to the lowest bit + pub shift: u8, + /// Max bits. + pub max: u8, + /// Color bits. + pub bits: u8, +} + +impl ColorInfo { + pub fn set_color_info(&mut self, shift: u8, max: u16) { + self.mask = (max as u32) << (shift as u32); + self.shift = shift; + self.max = if max == 0 { 0xFF } else { max as u8 }; + self.bits = max.popcnt() as u8; + } +} + +#[derive(Clone, Default)] +pub struct PixelFormat { + /// Bits per pixel. + pub pixel_bits: u8, + /// Bytes per pixel. + pub pixel_bytes: u8, + /// Color depth. + pub depth: u8, + /// Red info. + pub red: ColorInfo, + /// Green info. + pub green: ColorInfo, + /// Blue info. + pub blue: ColorInfo, + /// Alpha channel. + pub alpha_chl: ColorInfo, +} + +impl PixelFormat { + // Pixelformat_from_pixman. + pub fn init_pixelformat(&mut self) { + let fmt = pixman_format_code_t::PIXMAN_x8r8g8b8 as u32; + self.pixel_bits = pixman_format_bpp(fmt); + self.pixel_bytes = self.pixel_bits / 8; + self.depth = pixman_format_depth(fmt); + + self.alpha_chl.bits = pixman_format_a(fmt); + self.red.bits = pixman_format_r(fmt); + self.green.bits = pixman_format_g(fmt); + self.blue.bits = pixman_format_b(fmt); + + self.alpha_chl.shift = self.blue.bits + self.green.bits + self.red.bits; + self.red.shift = self.blue.bits + self.green.bits; + self.green.shift = self.blue.bits; + self.blue.shift = 0; + + self.alpha_chl.max = ((1 << self.alpha_chl.bits) - 1) as u8; + self.red.max = ((1 << self.red.bits) - 1) as u8; + self.green.max = ((1 << self.green.bits) - 1) as u8; + self.blue.max = ((1 << self.blue.bits) - 1) as u8; + + self.alpha_chl.mask = self.alpha_chl.max.wrapping_shl(self.alpha_chl.shift as u32) as u32; + self.red.mask = self.red.max.wrapping_shl(self.red.shift as u32) as u32; + self.green.mask = self.green.max.wrapping_shl(self.green.shift as u32) as u32; + self.blue.mask = self.blue.max.wrapping_shl(self.blue.shift as u32) as u32; + } + + pub fn is_default_pixel_format(&self) -> bool { + // Check if type is PIXMAN_TYPE_ARGB. + if self.red.shift <= self.green.shift + || self.green.shift <= self.blue.shift + || self.blue.shift != 0 + { + return false; + } + + // Check if format is PIXMAN_x8r8g8b8. + if self.pixel_bits != 32 + || self.alpha_chl.bits != 0 + || self.red.bits != 8 + || self.green.bits != 8 + || self.blue.bits != 8 + { + return false; + } + + true + } +} + +pub fn get_image_width(image: *mut pixman_image_t) -> i32 { + unsafe { pixman_image_get_width(image as *mut pixman_image_t) as i32 } +} + +pub fn get_image_height(image: *mut pixman_image_t) -> i32 { + unsafe { pixman_image_get_height(image as *mut pixman_image_t) as i32 } +} + +pub fn get_image_stride(image: *mut pixman_image_t) -> i32 { + unsafe { pixman_image_get_stride(image as *mut pixman_image_t) } +} + +pub fn get_image_data(image: *mut pixman_image_t) -> *mut u32 { + unsafe { pixman_image_get_data(image as *mut pixman_image_t) } +} + +pub fn get_image_format(image: *mut pixman_image_t) -> pixman_format_code_t { + unsafe { pixman_image_get_format(image as *mut pixman_image_t) } +} -- Gitee From ae3762f3fe304a31fe40f613b3a0ab97c0b27c93 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 19:03:25 +0800 Subject: [PATCH 0130/2187] VNC: Image refresh related interface 1. Guest image init. 2. Add system call whitelist: SYS_getsockname, SYS_getpeername, SYS_shutdown. 3. Add the bitmap operation:find_next_bit,find_next_zero,get_data. 4. Add set dirty. 5. Add handler of refresh after set dirty. Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/syscall.rs | 7 +- machine/src/standard_vm/x86_64/syscall.rs | 7 +- util/src/bitmap.rs | 59 ++++++++- vnc/src/client.rs | 74 ++++++++++- vnc/src/lib.rs | 8 ++ vnc/src/pixman.rs | 5 + vnc/src/server.rs | 91 ++++++++++--- vnc/src/vnc.rs | 146 ++++++++++++++++++++- 8 files changed, 364 insertions(+), 33 deletions(-) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 2b1d28b8d..13a399c0d 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 56 syscalls -/// * aarch64-unknown-musl: 54 syscalls +/// * aarch64-unknown-gnu: 59 syscalls +/// * aarch64-unknown-musl: 57 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -118,7 +118,10 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_prctl), BpfRule::new(libc::SYS_sendto), + BpfRule::new(libc::SYS_getsockname), + BpfRule::new(libc::SYS_getpeername), BpfRule::new(libc::SYS_getrandom), + BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_rt_sigaction), BpfRule::new(libc::SYS_setsockopt), #[cfg(target_env = "gnu")] diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 237128d4f..2189d0df3 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 58 syscalls -/// * x86_64-unknown-musl: 57 syscalls +/// * x86_64-unknown-gnu: 61 syscalls +/// * x86_64-unknown-musl: 60 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -127,6 +127,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_clone3), BpfRule::new(libc::SYS_prctl), BpfRule::new(libc::SYS_sendto), + BpfRule::new(libc::SYS_getsockname), + BpfRule::new(libc::SYS_getpeername), + BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_setsockopt), #[cfg(target_env = "gnu")] diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 2b6ea5e75..0b7767076 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::cmp::Ord; use std::mem::size_of; @@ -133,12 +132,62 @@ impl Bitmap { /// /// * `offset` - the input offset as the query's start. pub fn find_next_zero(&self, offset: usize) -> Result { - for i in offset + 1..self.vol() { - if !self.contain(i)? { - return Ok(i); + let size = self.size(); + let idx = offset / T::len(); + let mut offset = offset % T::len(); + for i in idx..size { + if self.data[i] == T::full() { + continue; } + for j in offset..T::len() { + if !self.contain(i * T::len() + j)? { + return Ok(i * T::len() + j); + } + } + offset = 0; + } + Ok(self.vol()) + } + + /// Return a new offset to get next nonzero bit from input offset. + /// + /// # Arguments + /// + /// * `offset` - the input offset as the query's start. + pub fn find_next_bit(&self, offset: usize) -> Result { + let size = self.size(); + let idx = offset / T::len(); + let mut offset = offset % T::len(); + for i in idx..size { + if self.data[i] == T::zero() { + continue; + } + for j in offset..T::len() { + if self.contain(i * T::len() + j)? { + return Ok(i * T::len() + j); + } + } + offset = 0; + } + + Ok(self.vol()) + } + + /// Get the inner data from bitmap. + /// + /// # Arguments + /// + /// * `buf` - the cache to receive the data. + pub fn get_data(&mut self, buf: &mut Vec) { + buf.clear(); + buf.append(&mut self.data); + } + + /// clear all the data in bitmap + pub fn clear_all(&mut self) { + for i in 0..self.size() { + self.data[i] = T::zero(); } - bail!("Failed to get new zero bit") } fn bit_index(&self, num: usize) -> usize { diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 8bf161583..fe961b1fe 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -24,10 +24,15 @@ use std::{ use util::{ bitmap::Bitmap, loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}, + pixman::pixman_image_t, }; use vmm_sys_util::epoll::EventSet; -use crate::{AuthState, BuffPool, SubAuthState, VncServer, VNC_SERVERS}; +use crate::{ + get_image_height, get_image_width, round_up_div, set_area_dirty, update_client_surface, + AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, + MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_SERVERS, +}; const MAX_RECVBUF_LEN: usize = 1024; @@ -146,6 +151,14 @@ pub struct VncClient { big_endian: bool, /// State flags whether the image needs to be updated for the client. state: UpdateState, + /// Identify the image update area. + dirty: Bitmap, + /// Number of dirty data. + dirty_num: i32, + /// Image pixel format in pixman. + pixel_format: PixelFormat, + /// Image pointer. + pub server_image: *mut pixman_image_t, /// Tcp listening address. pub addr: String, /// Image width. @@ -161,7 +174,12 @@ pub struct VncClient { } impl VncClient { - pub fn new(stream: TcpStream, addr: String, server: Arc>) -> Self { + pub fn new( + stream: TcpStream, + addr: String, + server: Arc>, + image: *mut pixman_image_t, + ) -> Self { VncClient { stream, buffpool: BuffPool::new(), @@ -175,6 +193,13 @@ impl VncClient { server, big_endian: false, state: UpdateState::No, + dirty: Bitmap::::new( + MAX_WINDOW_HEIGHT as usize + * round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) as usize, + ), + dirty_num: 0, + pixel_format: PixelFormat::default(), + server_image: image, addr, width: 0, height: 0, @@ -387,6 +412,49 @@ impl VncClient { /// Initialize the connection of vnc client pub fn handle_client_init(&mut self) -> Result<()> { let mut buf = Vec::new(); + // Send server framebuffer info + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let locked_server = server.lock().unwrap(); + let width = get_image_width(locked_server.server_image); + if width < 0 || width > MAX_WINDOW_WIDTH as i32 { + error!("Invalid Image Size!"); + return Err(ErrorKind::InvalidImageSize.into()); + } + self.width = width as i32; + buf.append(&mut (self.width as u16).to_be_bytes().to_vec()); + + let height = get_image_height(locked_server.server_image); + if height < 0 || height > MAX_WINDOW_HEIGHT as i32 { + error!("Invalid Image Size!"); + return Err(ErrorKind::InvalidImageSize.into()); + } + self.height = height as i32; + + buf.append(&mut (self.height as u16).to_be_bytes().to_vec()); + drop(locked_server); + + self.pixel_format.init_pixelformat(); + buf.push(self.pixel_format.pixel_bits); + buf.push(self.pixel_format.depth); + buf.push(0); // Big-endian flag. + buf.push(1); // True-color flag. + buf.push(0); + buf.push(self.pixel_format.red.max); + buf.push(0); + buf.push(self.pixel_format.green.max); + buf.push(0); + buf.push(self.pixel_format.blue.max); + buf.push(self.pixel_format.red.shift); + buf.push(self.pixel_format.green.shift); + buf.push(self.pixel_format.blue.shift); + buf.append(&mut [0; 3].to_vec()); + + buf.append( + &mut ("StratoVirt".to_string().len() as u32) + .to_be_bytes() + .to_vec(), + ); + buf.append(&mut "StratoVirt".to_string().as_bytes().to_vec()); self.write_msg(&buf); self.update_event_handler(1, VncClient::handle_protocol_msg); Ok(()) @@ -498,7 +566,7 @@ impl VncClient { // ENCODING_COMPRESSLEVEL0 ..= ENCODING_COMPRESSLEVEL0 + 9 => {} // ENCODING_QUALITYLEVEL0 ..= ENCODING_QUALITYLEVEL0 + 9 => {} _ => { - error!("Unknow encoding"); + info!("Unknow encoding"); } } diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 582421906..c1d1c289a 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -67,3 +67,11 @@ pub use input::*; pub use pixman::*; pub use server::*; pub use utils::BuffPool; + +pub const fn round_up_div(n: u64, d: u64) -> u64 { + (n + d - 1) / d +} + +pub const fn round_up(n: u64, d: u64) -> u64 { + round_up_div(n, d) * d +} diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs index 37739175f..93f4d4bfd 100644 --- a/vnc/src/pixman.rs +++ b/vnc/src/pixman.rs @@ -127,3 +127,8 @@ pub fn get_image_data(image: *mut pixman_image_t) -> *mut u32 { pub fn get_image_format(image: *mut pixman_image_t) -> pixman_format_code_t { unsafe { pixman_image_get_format(image as *mut pixman_image_t) } } + +/// Bpp: bit per pixel +pub fn bytes_per_pixel() -> usize { + ((pixman_format_bpp(pixman_format_code_t::PIXMAN_x8r8g8b8 as u32) + 7) / 8) as usize +} diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 5ed35a59b..57467768e 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -15,38 +15,71 @@ use machine_manager::{ config::{ObjConfig, VncConfig}, event_loop::EventLoop, }; -use once_cell::sync::Lazy; use std::{ + cmp, collections::HashMap, net::{Shutdown, TcpListener}, os::unix::prelude::{AsRawFd, RawFd}, + ptr, sync::{Arc, Mutex}, }; -use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::{ + bitmap::Bitmap, + loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}, + pixman::{ + pixman_format_bpp, pixman_format_code_t, pixman_image_composite, pixman_image_create_bits, + pixman_image_t, pixman_op_t, + }, +}; use vmm_sys_util::epoll::EventSet; -use crate::{VncClient, VNC_SERVERS}; +use crate::{ + bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, + get_image_width, round_up_div, update_client_surface, AuthState, SubAuthState, VncClient, + DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, + VNC_SERVERS, +}; /// VncServer -#[derive(Clone)] pub struct VncServer { // Tcp connection listened by server. listener: Arc>, // Clients connected to vnc. pub clients: HashMap>>, + /// Image refresh to VncClient. + pub server_image: *mut pixman_image_t, + /// Image from gpu. + pub guest_image: *mut pixman_image_t, + /// Identify the image update area for guest image. + pub guest_dirtymap: Bitmap, + /// Image format of pixman. + pub guest_format: pixman_format_code_t, // Connection limit. conn_limits: usize, + /// Width of current image. + pub true_width: i32, } unsafe impl Send for VncServer {} impl VncServer { /// Create a new VncServer. - pub fn new(listener: Arc>) -> Self { + pub fn new(listener: Arc>, guest_image: *mut pixman_image_t) -> Self { VncServer { listener, clients: HashMap::new(), + server_image: ptr::null_mut(), + guest_image, + guest_dirtymap: Bitmap::::new( + MAX_WINDOW_HEIGHT as usize + * round_up_div( + (MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM) as u64, + u64::BITS as u64, + ) as usize, + ), + guest_format: pixman_format_code_t::PIXMAN_x8r8g8b8, conn_limits: 1, + true_width: 0, } } @@ -73,7 +106,8 @@ impl VncServer { .expect("set nonblocking failed"); let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut client = VncClient::new(stream, addr.to_string(), server); + let mut client = + VncClient::new(stream, addr.to_string(), server, self.server_image); client.write_msg("RFB 003.008\n".to_string().as_bytes()); info!("{:?}", client.stream); @@ -87,6 +121,8 @@ impl VncServer { } } + update_client_surface(self); + Ok(()) } } @@ -100,7 +136,7 @@ impl EventNotifierHelper for VncServer { read_fd(fd); if event & EventSet::HANG_UP == EventSet::HANG_UP { - info!("Client closed."); + info!("Client Closed"); } else if event == EventSet::IN { let mut locked_handler = server.lock().unwrap(); if let Err(e) = locked_handler.handle_connection() { @@ -112,21 +148,42 @@ impl EventNotifierHelper for VncServer { None as Option> }); - let mut notifiers = Vec::new(); + let mut notifiers = vec![ + (EventNotifier::new( + NotifierOperation::AddShared, + server_handler + .lock() + .unwrap() + .listener + .lock() + .unwrap() + .as_raw_fd(), + None, + EventSet::IN | EventSet::HANG_UP, + vec![Arc::new(Mutex::new(handler))], + )), + ]; + + let handler: Box Option>> = + Box::new(move |_event, fd: RawFd| { + read_fd(fd); + vnc_refresh(); + None as Option> + }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - server_handler - .lock() - .unwrap() - .listener - .lock() - .unwrap() - .as_raw_fd(), + REFRESH_EVT.lock().unwrap().as_raw_fd(), None, - EventSet::IN | EventSet::HANG_UP, + EventSet::IN, vec![Arc::new(Mutex::new(handler))], )); - notifiers } } + +/// Refresh server_image to guest_image +fn vnc_refresh() { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } +} diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 360e22e30..ddbf20b0f 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -17,14 +17,40 @@ use machine_manager::{ }; use once_cell::sync::Lazy; use std::{ + cmp, collections::HashMap, net::TcpListener, + ptr, sync::{Arc, Mutex}, }; -use util::loop_context::EventNotifierHelper; -use vmm_sys_util::epoll::EventSet; +use util::{ + bitmap::Bitmap, + loop_context::EventNotifierHelper, + pixman::{ + pixman_format_code_t, pixman_image_create_bits, pixman_image_ref, pixman_image_t, + pixman_image_unref, + }, +}; +use vmm_sys_util::eventfd::EventFd; + +use crate::{ + bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, round_up, + round_up_div, PixelFormat, VncClient, VncFeatures, VncServer, ENCODING_ALPHA_CURSOR, + ENCODING_RAW, ENCODING_RICH_CURSOR, +}; + +/// The number of dirty pixels represented bt one bit in dirty bitmap. +pub const DIRTY_PIXELS_NUM: u16 = 16; +/// The default max window width. +pub const MAX_WINDOW_WIDTH: u16 = round_up(2560, DIRTY_PIXELS_NUM as u64) as u16; +/// The default max window height. +pub const MAX_WINDOW_HEIGHT: u16 = 2048; +pub const DIRTY_WIDTH_BITS: u16 = MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM; +pub const VNC_BITMAP_WIDTH: u64 = + round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) * u64::BITS as u64; -use crate::VncServer; +const DEFAULT_REFRESH_INTERVAL: u64 = 30; +const BIT_PER_BYTE: u32 = 8; pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Result<()> { let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); @@ -42,7 +68,7 @@ pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Res .set_nonblocking(true) .expect("Set noblocking for vnc socket failed"); - let mut server = VncServer::new(Arc::new(Mutex::new(listener))); + let mut server = VncServer::new(Arc::new(Mutex::new(listener)), get_client_image()); // Parameter configuation for VncServeer. if let Err(err) = server.make_config(vnc_cfg, object) { @@ -68,5 +94,117 @@ fn add_vnc_server(server: VncServer) { .push(Arc::new(Mutex::new(server))); } +/// Set dirty in bitmap. +pub fn set_area_dirty( + dirty: &mut Bitmap, + mut x: i32, + mut y: i32, + mut w: i32, + mut h: i32, + g_w: i32, + g_h: i32, +) { + let width: i32 = vnc_width(g_w); + let height: i32 = vnc_height(g_h); + + w += x % DIRTY_PIXELS_NUM as i32; + x -= x % DIRTY_PIXELS_NUM as i32; + + x = cmp::min(x, width); + y = cmp::min(y, height); + w = cmp::min(x + w, width) - x; + h = cmp::min(y + h, height); + while y < h { + let pos = y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32; + for i in 0..w / DIRTY_PIXELS_NUM as i32 { + dirty.set((pos + i) as usize).unwrap(); + } + y += 1; + } + REFRESH_EVT.lock().unwrap().write(1).unwrap(); +} + +/// Get the width of image. +pub fn vnc_width(width: i32) -> i32 { + cmp::min( + MAX_WINDOW_WIDTH as i32, + round_up(width as u64, DIRTY_PIXELS_NUM as u64) as i32, + ) +} + +/// Get the height of image. +fn vnc_height(height: i32) -> i32 { + cmp::min(MAX_WINDOW_HEIGHT as i32, height) +} + +/// Decrease the reference of image +/// # Arguments +/// +/// * `image` - the pointer to image in pixman +fn vnc_pixman_image_unref(image: *mut pixman_image_t) { + if image.is_null() { + return; + } + unsafe { pixman_image_unref(image) }; +} + +/// Update Client image +pub fn update_client_surface(server: &mut VncServer) { + vnc_pixman_image_unref(server.server_image); + server.server_image = ptr::null_mut(); + + if server.clients.is_empty() { + return; + } + + for client in server.clients.values_mut() { + client.lock().unwrap().server_image = ptr::null_mut(); + } + + let g_width = get_image_width(server.guest_image); + let g_height = get_image_height(server.guest_image); + let width = vnc_width(g_width); + let height = vnc_height(g_height); + server.true_width = cmp::min(MAX_WINDOW_WIDTH as i32, g_width); + server.server_image = unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + width, + height, + ptr::null_mut(), + 0, + ) + }; + for client in server.clients.values_mut() { + client.lock().unwrap().server_image = server.server_image; + } + server.guest_dirtymap.clear_all(); + set_area_dirty( + &mut server.guest_dirtymap, + 0, + 0, + width, + height, + g_width, + g_height, + ); +} + +/// Initialize a default image +/// Default: width is 640, height is 480, stride is 640 * 4 +fn get_client_image() -> *mut pixman_image_t { + unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + 640, + 480, + ptr::null_mut(), + 640 * 4, + ) + } +} + pub static VNC_SERVERS: Lazy>>>> = Lazy::new(|| Mutex::new(Vec::new())); +pub static REFRESH_EVT: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()))); -- Gitee From f3fc0171e572bdaa0bdcf7de1d199116eef11ab8 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 19:39:52 +0800 Subject: [PATCH 0131/2187] VNC: refresh image 1. Refresh dirty area depend on bitmap 2. Support to switch guest_image. 3. Encapsulate the sent data into a job and put it in send queue. 4. Change the callback for event Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- util/src/loop_context.rs | 22 +++- vnc/src/client.rs | 179 +++++++++++++++++++++++++++- vnc/src/pixman.rs | 12 ++ vnc/src/server.rs | 246 ++++++++++++++++++++++++++++++++++++++- vnc/src/vnc.rs | 199 ++++++++++++++++++++++++++++--- 5 files changed, 632 insertions(+), 26 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 83b8758f1..c499e2861 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -261,6 +261,22 @@ impl EventLoopContext { Ok(()) } + /// change the callback for event + fn modify_event(&mut self, event: EventNotifier) -> Result<()> { + let mut events_map = self.events.write().unwrap(); + match events_map.get_mut(&event.raw_fd) { + Some(notifier) => { + notifier.handlers.clear(); + let mut event = event; + notifier.handlers.append(&mut event.handlers); + } + _ => { + return Err(ErrorKind::NoRegisterFd(event.raw_fd).into()); + } + } + Ok(()) + } + fn park_event(&mut self, event: &EventNotifier) -> Result<()> { let mut events_map = self.events.write().unwrap(); match events_map.get_mut(&event.raw_fd) { @@ -314,6 +330,9 @@ impl EventLoopContext { NotifierOperation::AddExclusion | NotifierOperation::AddShared => { self.add_event(en)?; } + NotifierOperation::Modify => { + self.modify_event(en)?; + } NotifierOperation::Delete => { self.rm_event(&en)?; } @@ -323,9 +342,6 @@ impl EventLoopContext { NotifierOperation::Resume => { self.resume_event(&en)?; } - _ => { - return Err(ErrorKind::UnExpectedOperationType.into()); - } } } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index fe961b1fe..40659cf26 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -31,7 +31,7 @@ use vmm_sys_util::epoll::EventSet; use crate::{ get_image_height, get_image_width, round_up_div, set_area_dirty, update_client_surface, AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, - MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_SERVERS, + MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, }; const MAX_RECVBUF_LEN: usize = 1024; @@ -70,7 +70,7 @@ pub enum VncFeatures { VncFeatureClipboardExt, } -// According to Remote Framebuffer Protocol +/// Client to server message in Remote Framebuffer Protocol. pub enum ClientMsg { SetPixelFormat = 0, SetEncodings = 2, @@ -81,7 +81,7 @@ pub enum ClientMsg { InvalidMsg, } -// Message id of server +/// Server to client message in Remote Framebuffer Protocol. pub enum ServerMsg { FramebufferUpdate = 0, } @@ -125,6 +125,81 @@ pub enum UpdateState { Force, } +/// Dirty area of image +#[derive(Clone)] +pub struct Rectangle { + pub x: i32, + pub y: i32, + pub w: i32, + pub h: i32, +} + +impl Rectangle { + pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self { + Rectangle { x, y, w, h } + } +} + +unsafe impl Send for RectInfo {} +pub struct RectInfo { + /// TcpStream address + addr: String, + /// Dirty area of image + rects: Vec, + width: i32, + height: i32, + /// Encoding type + encoding: i32, + /// The pixel need to convert. + convert: bool, + /// Data storage type for client. + big_endian: bool, + /// Image pixel format in pixman. + pixel_format: PixelFormat, + /// Image + image: *mut pixman_image_t, +} + +impl RectInfo { + pub fn new(client: &VncClient, rects: Vec) -> Self { + RectInfo { + addr: client.addr.clone(), + rects, + width: client.width, + height: client.height, + encoding: client.encoding, + convert: client.pixel_convert, + big_endian: client.big_endian, + pixel_format: client.pixel_format.clone(), + image: client.server_image, + } + } +} + +impl Clone for RectInfo { + fn clone(&self) -> Self { + let mut rects = Vec::new(); + for rect in &self.rects { + rects.push(rect.clone()); + } + Self { + addr: self.addr.clone(), + rects, + width: self.width, + height: self.height, + encoding: self.encoding, + convert: self.convert, + big_endian: self.big_endian, + pixel_format: self.pixel_format.clone(), + image: self.image, + } + } + + fn clone_from(&mut self, source: &Self) { + *self = source.clone() + } +} + /// VncClient struct to record the information of connnection. pub struct VncClient { /// TcpStream connected with client. @@ -152,7 +227,7 @@ pub struct VncClient { /// State flags whether the image needs to be updated for the client. state: UpdateState, /// Identify the image update area. - dirty: Bitmap, + pub dirty_bitmap: Bitmap, /// Number of dirty data. dirty_num: i32, /// Image pixel format in pixman. @@ -193,7 +268,7 @@ impl VncClient { server, big_endian: false, state: UpdateState::No, - dirty: Bitmap::::new( + dirty_bitmap: Bitmap::::new( MAX_WINDOW_HEIGHT as usize * round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) as usize, ), @@ -209,6 +284,98 @@ impl VncClient { } } + /// Whether the client's image data needs to be updated + pub fn is_need_update(&self) -> bool { + match self.state { + UpdateState::No => false, + UpdateState::Incremental => { + // throttle_output_offset + true + } + UpdateState::Force => { + // force_update_offset + true + } + } + } + + /// Generate the data that needs to be sent + /// Add to send queue + pub fn get_rects(&mut self, dirty_num: i32) -> i32 { + self.dirty_num += dirty_num; + if !self.is_need_update() || (self.dirty_num == 0 && self.state != UpdateState::Force) { + return 0; + } + + let mut num_rects = 0; + let mut x: u64; + let mut y: u64 = 0; + let mut h: u64; + let mut x2: u64; + let mut rects = Vec::new(); + let bpl = self.dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; + + let height = get_image_height(self.server_image) as u64; + let width = get_image_width(self.server_image) as u64; + loop { + // Find the first non-zero bit in dirty bitmap. + let offset = self.dirty_bitmap.find_next_bit(y as usize * bpl).unwrap() as u64; + if offset >= height as u64 * bpl as u64 { + break; + } + + x = offset % bpl as u64; + y = offset / bpl as u64; + // Find value in one line to the end. + x2 = self.dirty_bitmap.find_next_zero(offset as usize).unwrap() as u64 % bpl as u64; + let mut i = y; + while i < height { + if !self + .dirty_bitmap + .contain((i * bpl as u64 + x) as usize) + .unwrap() + { + break; + } + for j in x..x2 { + self.dirty_bitmap + .clear((i * bpl as u64 + j) as usize) + .unwrap(); + } + i += 1; + } + + h = i - y; + x2 = cmp::min(x2, width / DIRTY_PIXELS_NUM as u64); + if x2 > x as u64 { + rects.push(Rectangle::new( + (x * DIRTY_PIXELS_NUM as u64) as i32, + y as i32, + ((x2 - x) * DIRTY_PIXELS_NUM as u64) as i32, + h as i32, + )); + num_rects += 1; + } + + if x == 0 && x2 == width / DIRTY_PIXELS_NUM as u64 { + y += h; + if y == height { + break; + } + } + } + + VNC_RECT_INFO + .lock() + .unwrap() + .push(RectInfo::new(self, rects)); + + self.state = UpdateState::No; + self.dirty_num = 0; + + num_rects + } + /// Modify event notifiers to event loop /// /// # Arguments @@ -584,7 +751,7 @@ impl VncClient { self.feature & (1 << feature as usize) != 0 } - fn desktop_resize(&mut self) { + pub fn desktop_resize(&mut self) { // If hash feature VNC_FEATURE_RESIZE } diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs index 93f4d4bfd..e0916a996 100644 --- a/vnc/src/pixman.rs +++ b/vnc/src/pixman.rs @@ -15,6 +15,7 @@ use util::pixman::{ pixman_format_a, pixman_format_b, pixman_format_bpp, pixman_format_code_t, pixman_format_depth, pixman_format_g, pixman_format_r, pixman_image_get_data, pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, pixman_image_get_width, pixman_image_t, + pixman_image_unref, }; #[derive(Clone, Default)] @@ -132,3 +133,14 @@ pub fn get_image_format(image: *mut pixman_image_t) -> pixman_format_code_t { pub fn bytes_per_pixel() -> usize { ((pixman_format_bpp(pixman_format_code_t::PIXMAN_x8r8g8b8 as u32) + 7) / 8) as usize } + +/// Decrease the reference of image +/// # Arguments +/// +/// * `image` - the pointer to image in pixman +pub fn unref_pixman_image(image: *mut pixman_image_t) { + if image.is_null() { + return; + } + unsafe { pixman_image_unref(image as *mut pixman_image_t) }; +} diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 57467768e..7cfe1c528 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -35,11 +35,48 @@ use vmm_sys_util::epoll::EventSet; use crate::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, - get_image_width, round_up_div, update_client_surface, AuthState, SubAuthState, VncClient, - DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, - VNC_SERVERS, + get_image_width, round_up_div, unref_pixman_image, update_client_surface, AuthState, + DisplayMouse, SubAuthState, VncClient, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, + REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, }; +/// Info of image +/// stride is not always equal to stride because of memory alignment. +pub struct ImageInfo { + /// The start pointer to image. + data: *mut u8, + /// The memory size of each line for image. + stride: i32, + /// The memory size of each line to store pixel for image + length: i32, + /// Middle pointer. + ptr: *mut u8, +} + +impl Default for ImageInfo { + fn default() -> Self { + ImageInfo { + data: ptr::null_mut(), + stride: 0, + length: 0, + ptr: ptr::null_mut(), + } + } +} + +impl ImageInfo { + fn new(image: *mut pixman_image_t) -> Self { + let bpp = pixman_format_bpp(get_image_format(image) as u32); + let length = get_image_width(image) * round_up_div(bpp as u64, 8) as i32; + ImageInfo { + data: get_image_data(image) as *mut u8, + stride: get_image_stride(image), + length, + ptr: ptr::null_mut(), + } + } +} + /// VncServer pub struct VncServer { // Tcp connection listened by server. @@ -54,6 +91,10 @@ pub struct VncServer { pub guest_dirtymap: Bitmap, /// Image format of pixman. pub guest_format: pixman_format_code_t, + /// Cursor property. + pub cursor: Option, + /// Identify the area need update for cursor. + pub mask: Option>, // Connection limit. conn_limits: usize, /// Width of current image. @@ -78,6 +119,8 @@ impl VncServer { ) as usize, ), guest_format: pixman_format_code_t::PIXMAN_x8r8g8b8, + cursor: None, + mask: None, conn_limits: 1, true_width: 0, } @@ -92,6 +135,193 @@ impl VncServer { Ok(()) } + /// Set diry for client + /// + /// # Arguments + /// + /// * `x` `y`- coordinates of dirty area. + fn set_dirty_for_clients(&mut self, x: usize, y: usize) { + for client in self.clients.values_mut() { + client + .lock() + .unwrap() + .dirty_bitmap + .set(x + y * VNC_BITMAP_WIDTH as usize) + .unwrap(); + } + } + + /// Transfer dirty data to buff in one line + /// + /// # Arguments + /// + /// * `s_info` - source image. + /// * `g_info` - dest image. + fn get_one_line_buf( + &self, + s_info: &mut ImageInfo, + g_info: &mut ImageInfo, + ) -> *mut pixman_image_t { + let mut line_buf = ptr::null_mut(); + if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { + line_buf = unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + get_image_width(self.server_image), + 1, + ptr::null_mut(), + 0, + ) + }; + g_info.stride = s_info.stride; + g_info.length = g_info.stride; + } + + line_buf + } + + /// Get min width + fn get_min_width(&self) -> i32 { + cmp::min( + get_image_width(self.server_image), + get_image_width(self.guest_image), + ) + } + + /// Get min height + fn get_min_height(&self) -> i32 { + cmp::min( + get_image_height(self.server_image), + get_image_height(self.guest_image), + ) + } + + /// Update each line + /// + /// # Arguments + /// + /// * `x` `y` - start coordinate in image to refresh + /// * `s_info` - source image. + /// * `g_info` - dest image. + fn update_one_line( + &mut self, + mut x: usize, + y: usize, + s_info: &mut ImageInfo, + g_info: &mut ImageInfo, + cmp_bytes: usize, + ) -> i32 { + let mut count = 0; + let width = self.get_min_width(); + let line_bytes = cmp::min(s_info.stride, g_info.length); + + while x < round_up_div(width as u64, DIRTY_PIXELS_NUM as u64) as usize { + if !self + .guest_dirtymap + .contain(x + y * VNC_BITMAP_WIDTH as usize) + .unwrap() + { + x += 1; + g_info.ptr = (g_info.ptr as usize + cmp_bytes) as *mut u8; + s_info.ptr = (s_info.ptr as usize + cmp_bytes) as *mut u8; + continue; + } + self.guest_dirtymap + .clear(x + y * VNC_BITMAP_WIDTH as usize) + .unwrap(); + let mut _cmp_bytes = cmp_bytes; + if (x + 1) * cmp_bytes > line_bytes as usize { + _cmp_bytes = line_bytes as usize - x * cmp_bytes; + } + + unsafe { + if libc::memcmp( + s_info.ptr as *mut libc::c_void, + g_info.ptr as *mut libc::c_void, + _cmp_bytes, + ) == 0 + { + x += 1; + g_info.ptr = (g_info.ptr as usize + cmp_bytes) as *mut u8; + s_info.ptr = (s_info.ptr as usize + cmp_bytes) as *mut u8; + continue; + } + + ptr::copy(g_info.ptr, s_info.ptr, _cmp_bytes); + }; + + self.set_dirty_for_clients(x, y); + count += 1; + + x += 1; + g_info.ptr = (g_info.ptr as usize + cmp_bytes) as *mut u8; + s_info.ptr = (s_info.ptr as usize + cmp_bytes) as *mut u8; + } + + count + } + + /// Update the dirty area of the image + /// Return dirty number + pub fn update_server_image(&mut self) -> i32 { + let mut dirty_num = 0; + let height = self.get_min_height(); + let g_bpl = self.guest_dirtymap.vol() / MAX_WINDOW_HEIGHT as usize; + + let mut offset = self.guest_dirtymap.find_next_bit(0).unwrap(); + if offset >= (height as usize) * g_bpl { + return dirty_num; + } + + let mut s_info = ImageInfo::new(self.server_image); + let mut g_info = ImageInfo::new(self.guest_image); + + let cmp_bytes = cmp::min( + DIRTY_PIXELS_NUM as usize * bytes_per_pixel(), + s_info.stride as usize, + ); + + let line_buf = self.get_one_line_buf(&mut s_info, &mut g_info); + loop { + let mut y = offset / g_bpl; + let x = offset % g_bpl; + s_info.ptr = + (s_info.data as usize + y * s_info.stride as usize + x * cmp_bytes) as *mut u8; + + if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + self.guest_image, + ptr::null_mut(), + line_buf, + 0, + y as i16, + 0, + 0, + 0, + 0, + self.get_min_width() as u16, + 1, + ); + }; + g_info.ptr = get_image_data(line_buf) as *mut u8; + } else { + g_info.ptr = (g_info.data as usize + y * g_info.stride as usize) as *mut u8; + } + g_info.ptr = (g_info.ptr as usize + x * cmp_bytes) as *mut u8; + dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes); + y += 1; + offset = self.guest_dirtymap.find_next_bit(y * g_bpl).unwrap(); + if offset >= (height as usize) * g_bpl { + break; + } + unref_pixman_image(line_buf); + } + + dirty_num + } + /// Listen to the port and accpet client's connection. pub fn handle_connection(&mut self) -> Result<()> { match self.listener.lock().unwrap().accept() { @@ -186,4 +416,14 @@ fn vnc_refresh() { if VNC_SERVERS.lock().unwrap().is_empty() { return; } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + if server.lock().unwrap().clients.is_empty() { + return; + } + + let dirty_num = server.lock().unwrap().update_server_image(); + let mut _rects: i32 = 0; + for client in server.lock().unwrap().clients.values_mut() { + _rects += client.lock().unwrap().get_rects(dirty_num); + } } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index ddbf20b0f..7b26c5ccb 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -35,8 +35,8 @@ use vmm_sys_util::eventfd::EventFd; use crate::{ bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, round_up, - round_up_div, PixelFormat, VncClient, VncFeatures, VncServer, ENCODING_ALPHA_CURSOR, - ENCODING_RAW, ENCODING_RICH_CURSOR, + round_up_div, unref_pixman_image, PixelFormat, RectInfo, Rectangle, VncClient, VncFeatures, + VncServer, ENCODING_ALPHA_CURSOR, ENCODING_RAW, ENCODING_RICH_CURSOR, }; /// The number of dirty pixels represented bt one bit in dirty bitmap. @@ -52,6 +52,34 @@ pub const VNC_BITMAP_WIDTH: u64 = const DEFAULT_REFRESH_INTERVAL: u64 = 30; const BIT_PER_BYTE: u32 = 8; +/// A struct to record image information +#[derive(Clone, Copy)] +pub struct DisplaySurface { + /// image format + pub format: pixman_format_code_t, + /// pointer to image + pub image: *mut pixman_image_t, +} + +impl Default for DisplaySurface { + fn default() -> Self { + DisplaySurface { + format: pixman_format_code_t::PIXMAN_a1, + image: ptr::null_mut(), + } + } +} + +/// Struct to record mouse information +#[derive(Clone, Default)] +pub struct DisplayMouse { + pub width: u32, + pub height: u32, + pub hot_x: u32, + pub hot_y: u32, + pub data: Vec, +} + pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Result<()> { let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); let listener: TcpListener; @@ -137,20 +165,9 @@ fn vnc_height(height: i32) -> i32 { cmp::min(MAX_WINDOW_HEIGHT as i32, height) } -/// Decrease the reference of image -/// # Arguments -/// -/// * `image` - the pointer to image in pixman -fn vnc_pixman_image_unref(image: *mut pixman_image_t) { - if image.is_null() { - return; - } - unsafe { pixman_image_unref(image) }; -} - /// Update Client image pub fn update_client_surface(server: &mut VncServer) { - vnc_pixman_image_unref(server.server_image); + unref_pixman_image(server.server_image); server.server_image = ptr::null_mut(); if server.clients.is_empty() { @@ -190,6 +207,110 @@ pub fn update_client_surface(server: &mut VncServer) { ); } +/// Check if the suface for VncClient is need update +fn check_surface(surface: &mut DisplaySurface) -> bool { + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let locked_server = server.lock().unwrap(); + if surface.image.is_null() + || locked_server.server_image.is_null() + || locked_server.guest_format != surface.format + || get_image_width(locked_server.server_image) != get_image_width(surface.image) + || get_image_height(locked_server.server_image) != get_image_height(surface.image) + { + return true; + } + + false +} + +/// Check if rectangle is in spec +pub fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { + if rect.x >= width { + return false; + } + + rect.w = cmp::min(width - rect.x, rect.w); + if rect.w == 0 { + return false; + } + + if rect.y >= height { + return false; + } + + rect.h = cmp::min(height - rect.y, rect.h); + if rect.h == 0 { + return false; + } + + true +} + +/// Send updated pixel information to client +/// +/// # Arguments +/// +/// * `x` `y` `w` `h` - coordinate, width, height +/// * `buf` - send buffer +pub fn framebuffer_upadate(x: i32, y: i32, w: i32, h: i32, encoding: i32, buf: &mut Vec) { + buf.append(&mut (x as u16).to_be_bytes().to_vec()); + buf.append(&mut (y as u16).to_be_bytes().to_vec()); + buf.append(&mut (w as u16).to_be_bytes().to_vec()); + buf.append(&mut (h as u16).to_be_bytes().to_vec()); + buf.append(&mut encoding.to_be_bytes().to_vec()); +} + +/// Convert the sent information to a format supported +/// by the client depend on byte arrangement +/// +/// # Arguments +/// +/// * `ptr` = pointer to the data need to be convert +/// * `big_endian` - byte arrangement +/// * `pf` - pixelformat +/// * `buf` - send buffer +/// * `size` - total number of bytes need to convert +fn convert_pixel(ptr: *mut u32, big_endian: bool, pf: &PixelFormat, buf: &mut Vec, size: u32) { + let num = size >> 2; + for i in 0..num { + let value = unsafe { *ptr.offset(i as isize) }; + let mut ret = [0u8; 4]; + let red = (((value >> 16) & 0xff) << pf.red.bits) >> 8; + let green = (((value >> 8) & 0xff) << pf.green.bits) >> 8; + let blue = ((value & 0xff) << pf.blue.bits) >> 8; + let v = (red << pf.red.shift) | (green << pf.green.shift) | (blue << pf.blue.shift); + match pf.pixel_bytes { + 1 => { + ret[0] = v as u8; + } + 2 => { + if big_endian { + ret[0] = (v >> 8) as u8; + ret[1] = v as u8; + } else { + ret[1] = (v >> 8) as u8; + ret[0] = v as u8; + } + } + 4 => { + if big_endian { + ret = (v as u32).to_be_bytes(); + } else { + ret = (v as u32).to_le_bytes(); + } + } + _ => { + if big_endian { + ret = (v as u32).to_be_bytes(); + } else { + ret = (v as u32).to_le_bytes(); + } + } + } + buf.append(&mut ret[..pf.pixel_bytes as usize].to_vec()); + } +} + /// Initialize a default image /// Default: width is 640, height is 480, stride is 640 * 4 fn get_client_image() -> *mut pixman_image_t { @@ -204,7 +325,57 @@ fn get_client_image() -> *mut pixman_image_t { } } +/// Update guest_image +/// Send a resize command to the client based on whether the image size has changed +pub fn vnc_display_switch(surface: &mut DisplaySurface) { + let need_resize = check_surface(surface); + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_server = server.lock().unwrap(); + unref_pixman_image(locked_server.guest_image); + + // Vnc_pixman_image_ref + locked_server.guest_image = unsafe { pixman_image_ref(surface.image) }; + locked_server.guest_format = surface.format; + + let guest_width: i32 = get_image_width(locked_server.guest_image); + let guest_height: i32 = get_image_height(locked_server.guest_image); + if !need_resize { + set_area_dirty( + &mut locked_server.guest_dirtymap, + 0, + 0, + guest_width, + guest_height, + guest_width, + guest_height, + ); + return; + } + update_client_surface(&mut locked_server); + + for client in locked_server.clients.values_mut() { + let width = vnc_width(guest_width); + let height = vnc_height(guest_height); + let mut locked_client = client.lock().unwrap(); + // Desktop_resize; + // Cursor + locked_client.desktop_resize(); + locked_client.dirty_bitmap.clear_all(); + set_area_dirty( + &mut locked_client.dirty_bitmap, + 0, + 0, + width, + height, + guest_width, + guest_height, + ); + } +} + pub static VNC_SERVERS: Lazy>>>> = Lazy::new(|| Mutex::new(Vec::new())); +pub static VNC_RECT_INFO: Lazy>>> = + Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); pub static REFRESH_EVT: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()))); -- Gitee From 2eb37c8ae0b3f54cefdab42f445756a548e3c679 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 22 Aug 2022 20:04:24 +0800 Subject: [PATCH 0132/2187] VNC: Add a send thread 1. Add a thread to send framebuffer. 2. Vnc_display_update: set area dirty. 3. Convert the format of pixel if needed. Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/syscall.rs | 5 +- machine/src/standard_vm/x86_64/syscall.rs | 5 +- vnc/src/auth.rs | 2 +- vnc/src/client.rs | 171 ++++++++----- vnc/src/input.rs | 1 - vnc/src/server.rs | 42 ++-- vnc/src/vnc.rs | 269 ++++++++++++++++++++- 7 files changed, 411 insertions(+), 84 deletions(-) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 13a399c0d..4ffc4e0e4 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 59 syscalls -/// * aarch64-unknown-musl: 57 syscalls +/// * aarch64-unknown-gnu: 60 syscalls +/// * aarch64-unknown-musl: 58 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -120,6 +120,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_sendto), BpfRule::new(libc::SYS_getsockname), BpfRule::new(libc::SYS_getpeername), + BpfRule::new(libc::SYS_nanosleep), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_rt_sigaction), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 2189d0df3..61c2c25fc 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 61 syscalls -/// * x86_64-unknown-musl: 60 syscalls +/// * x86_64-unknown-gnu: 62 syscalls +/// * x86_64-unknown-musl: 61 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -129,6 +129,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_sendto), BpfRule::new(libc::SYS_getsockname), BpfRule::new(libc::SYS_getpeername), + BpfRule::new(libc::SYS_nanosleep), BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_setsockopt), diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index a101aa7b9..d9098c8c0 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; +use super::errors::Result; use crate::VncClient; /// Authentication type diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 40659cf26..02a19212e 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -29,14 +29,15 @@ use util::{ use vmm_sys_util::epoll::EventSet; use crate::{ - get_image_height, get_image_width, round_up_div, set_area_dirty, update_client_surface, - AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, - MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, + framebuffer_upadate, get_image_height, get_image_width, round_up_div, set_area_dirty, + update_client_surface, AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, + DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, + VNC_SERVERS, }; const MAX_RECVBUF_LEN: usize = 1024; -// VNC encodings types +// VNC encodings types. pub const ENCODING_RAW: i32 = 0; const ENCODING_HEXTILE: i32 = 5; const ENCODING_ZLIB: i32 = 6; @@ -142,22 +143,22 @@ impl Rectangle { unsafe impl Send for RectInfo {} pub struct RectInfo { - /// TcpStream address - addr: String, - /// Dirty area of image - rects: Vec, - width: i32, - height: i32, - /// Encoding type - encoding: i32, + /// TcpStream address. + pub addr: String, + /// Dirty area of image. + pub rects: Vec, + pub width: i32, + pub height: i32, + /// Encoding type. + pub encoding: i32, /// The pixel need to convert. - convert: bool, + pub convert: bool, /// Data storage type for client. - big_endian: bool, + pub big_endian: bool, /// Image pixel format in pixman. - pixel_format: PixelFormat, + pub pixel_format: PixelFormat, /// Image - image: *mut pixman_image_t, + pub image: *mut pixman_image_t, } impl RectInfo { @@ -223,7 +224,7 @@ pub struct VncClient { /// Pointer to VncServer. pub server: Arc>, /// Data storage type for client. - big_endian: bool, + pub big_endian: bool, /// State flags whether the image needs to be updated for the client. state: UpdateState, /// Identify the image update area. @@ -231,7 +232,7 @@ pub struct VncClient { /// Number of dirty data. dirty_num: i32, /// Image pixel format in pixman. - pixel_format: PixelFormat, + pub pixel_format: PixelFormat, /// Image pointer. pub server_image: *mut pixman_image_t, /// Tcp listening address. @@ -252,6 +253,8 @@ impl VncClient { pub fn new( stream: TcpStream, addr: String, + auth: AuthState, + subauth: SubAuthState, server: Arc>, image: *mut pixman_image_t, ) -> Self { @@ -261,9 +264,9 @@ impl VncClient { expect: 12, dis_conn: false, version: VncVersion::default(), + auth, + subauth, handle_msg: VncClient::handle_version, - auth: AuthState::No, - subauth: SubAuthState::VncAuthVencryptPlain, handlers: Vec::new(), server, big_endian: false, @@ -284,7 +287,7 @@ impl VncClient { } } - /// Whether the client's image data needs to be updated + /// Whether the client's image data needs to be updated. pub fn is_need_update(&self) -> bool { match self.state { UpdateState::No => false, @@ -299,7 +302,7 @@ impl VncClient { } } - /// Generate the data that needs to be sent + /// Generate the data that needs to be sent. /// Add to send queue pub fn get_rects(&mut self, dirty_num: i32) -> i32 { self.dirty_num += dirty_num; @@ -406,7 +409,7 @@ impl VncClient { Ok(()) } - /// Read plain txt + /// Read plain txt. pub fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { let mut len = 0_usize; buf.resize(MAX_RECVBUF_LEN, 0u8); @@ -433,7 +436,13 @@ impl VncClient { offset += ret; } Err(e) => { - error!("write msg error: {:?}", e); + if e.kind() == std::io::ErrorKind::WouldBlock { + self.stream.flush().unwrap(); + continue; + } else { + error!("write msg error: {:?}", e); + return; + } } } self.stream.flush().unwrap(); @@ -576,30 +585,22 @@ impl VncClient { Ok(()) } - /// Initialize the connection of vnc client + /// Initialize the connection of vnc client. pub fn handle_client_init(&mut self) -> Result<()> { let mut buf = Vec::new(); - // Send server framebuffer info - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let locked_server = server.lock().unwrap(); - let width = get_image_width(locked_server.server_image); - if width < 0 || width > MAX_WINDOW_WIDTH as i32 { + // Send server framebuffer info. + self.width = get_image_width(self.server_image); + if self.width < 0 || self.width > MAX_WINDOW_WIDTH as i32 { error!("Invalid Image Size!"); return Err(ErrorKind::InvalidImageSize.into()); } - self.width = width as i32; buf.append(&mut (self.width as u16).to_be_bytes().to_vec()); - - let height = get_image_height(locked_server.server_image); - if height < 0 || height > MAX_WINDOW_HEIGHT as i32 { + self.height = get_image_height(self.server_image); + if self.height < 0 || self.height > MAX_WINDOW_HEIGHT as i32 { error!("Invalid Image Size!"); return Err(ErrorKind::InvalidImageSize.into()); } - self.height = height as i32; - buf.append(&mut (self.height as u16).to_be_bytes().to_vec()); - drop(locked_server); - self.pixel_format.init_pixelformat(); buf.push(self.pixel_format.pixel_bits); buf.push(self.pixel_format.depth); @@ -627,7 +628,7 @@ impl VncClient { Ok(()) } - /// Set image format + /// Set image format. fn set_pixel_format(&mut self) -> Result<()> { if self.expect == 1 { self.expect = 20; @@ -635,23 +636,82 @@ impl VncClient { } let mut buf = self.buffpool.read_front(self.expect).to_vec(); + if buf[7] == 0 { + // Set a default 256 color map. + // Bit per pixel. + buf[4] = 8; + // Max bit of red. + buf[8] = 7; + // Max bit of green. + buf[10] = 7; + // Max bit of blue. + buf[12] = 3; + // Red shift. + buf[14] = 0; + // Green shift. + buf[15] = 3; + // Blue shift. + buf[16] = 6; + } + if ![8, 16, 32].contains(&buf[4]) { + self.dis_conn = true; + error!("Worng format of bits_per_pixel"); + return Err(ErrorKind::ProtocolMessageFailed(String::from("set pixel format")).into()); + } + + self.pixel_format + .red + .set_color_info(buf[14], u16::from_be_bytes([buf[8], buf[9]])); + self.pixel_format + .green + .set_color_info(buf[15], u16::from_be_bytes([buf[10], buf[11]])); + self.pixel_format + .blue + .set_color_info(buf[16], u16::from_be_bytes([buf[12], buf[13]])); + self.pixel_format.pixel_bits = buf[4]; + self.pixel_format.pixel_bytes = buf[4] / 8; + self.pixel_format.depth = if buf[4] == 32 { 24 } else { buf[4] }; + self.big_endian = buf[6] != 0; + + if !self.pixel_format.is_default_pixel_format() { + self.pixel_convert = true; + } self.update_event_handler(1, VncClient::handle_protocol_msg); Ok(()) } - /// Update image for client + /// Update image for client. fn update_frame_buff(&mut self) { if self.expect == 1 { self.expect = 10; return; } let buf = self.buffpool.read_front(self.expect); - + if buf[1] != 0 { + if self.state != UpdateState::Force { + self.state = UpdateState::Incremental; + } + } else { + self.state = UpdateState::Force; + let x = u16::from_be_bytes([buf[2], buf[3]]) as i32; + let y = u16::from_be_bytes([buf[4], buf[5]]) as i32; + let w = u16::from_be_bytes([buf[6], buf[7]]) as i32; + let h = u16::from_be_bytes([buf[8], buf[9]]) as i32; + set_area_dirty( + &mut self.dirty_bitmap, + x, + y, + w, + h, + get_image_width(self.server_image), + get_image_height(self.server_image), + ); + } self.update_event_handler(1, VncClient::handle_protocol_msg); } - /// Set encoding + /// Set encoding. fn set_encodings(&mut self) -> Result<()> { let buf = self.buffpool.read_front(self.expect); if self.expect == 1 { @@ -726,15 +786,7 @@ impl VncClient { ENCODING_LED_STATE => { self.feature |= 1 << VncFeatures::VncFeatureLedState as usize; } - // ENCODING_EXT_KEY_EVENT => {} - // ENCODING_AUDIO => {} - // VNC_ENCODING_XVP => {} - // ENCODING_CLIPBOARD_EXT => {} - // ENCODING_COMPRESSLEVEL0 ..= ENCODING_COMPRESSLEVEL0 + 9 => {} - // ENCODING_QUALITYLEVEL0 ..= ENCODING_QUALITYLEVEL0 + 9 => {} - _ => { - info!("Unknow encoding"); - } + _ => {} } num_encoding -= 1; @@ -747,12 +799,21 @@ impl VncClient { Ok(()) } - fn has_feature(&mut self, feature: VncFeatures) -> bool { + pub fn has_feature(&mut self, feature: VncFeatures) -> bool { self.feature & (1 << feature as usize) != 0 } pub fn desktop_resize(&mut self) { - // If hash feature VNC_FEATURE_RESIZE + // If hash feature VNC_FEATURE_RESIZE. + let width = get_image_width(self.server_image); + let height = get_image_height(self.server_image); + let mut buf: Vec = Vec::new(); + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + buf.append(&mut (1_u16).to_be_bytes().to_vec()); + + framebuffer_upadate(0, 0, width, height, ENCODING_DESKTOPRESIZE, &mut buf); + self.write_msg(&buf); } /// Process the data sent by the client @@ -806,7 +867,9 @@ impl VncClient { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_server = server.lock().unwrap(); locked_server.clients.remove(&self.addr); - + if locked_server.clients.is_empty() { + update_client_surface(&mut locked_server); + } drop(locked_server); if let Err(e) = self.modify_event(NotifierOperation::Delete, 0) { diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 39ffd45a0..caf0ac216 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -19,7 +19,6 @@ impl VncClient { self.expect = 8; return; } - let buf = self.buffpool.read_front(self.expect); self.update_event_handler(1, VncClient::handle_protocol_msg); } diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 7cfe1c528..d89d28138 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; +use super::errors::Result; use machine_manager::{ config::{ObjConfig, VncConfig}, event_loop::EventLoop, @@ -40,7 +40,7 @@ use crate::{ REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, }; -/// Info of image +/// Info of image. /// stride is not always equal to stride because of memory alignment. pub struct ImageInfo { /// The start pointer to image. @@ -79,10 +79,14 @@ impl ImageInfo { /// VncServer pub struct VncServer { - // Tcp connection listened by server. + /// Tcp connection listened by server. listener: Arc>, - // Clients connected to vnc. + /// Clients connected to vnc. pub clients: HashMap>>, + /// Auth type. + pub auth: AuthState, + /// Subauth type. + pub subauth: SubAuthState, /// Image refresh to VncClient. pub server_image: *mut pixman_image_t, /// Image from gpu. @@ -95,7 +99,7 @@ pub struct VncServer { pub cursor: Option, /// Identify the area need update for cursor. pub mask: Option>, - // Connection limit. + /// Connection limit. conn_limits: usize, /// Width of current image. pub true_width: i32, @@ -109,6 +113,8 @@ impl VncServer { VncServer { listener, clients: HashMap::new(), + auth: AuthState::No, + subauth: SubAuthState::VncAuthVencryptPlain, server_image: ptr::null_mut(), guest_image, guest_dirtymap: Bitmap::::new( @@ -129,9 +135,11 @@ impl VncServer { /// Make configuration for VncServer. pub fn make_config( &mut self, - vnc_cfg: &VncConfig, - object: &HashMap, + _vnc_cfg: &VncConfig, + _object: &HashMap, ) -> Result<()> { + // tls configuration + Ok(()) } @@ -180,7 +188,7 @@ impl VncServer { line_buf } - /// Get min width + /// Get min width. fn get_min_width(&self) -> i32 { cmp::min( get_image_width(self.server_image), @@ -188,7 +196,7 @@ impl VncServer { ) } - /// Get min height + /// Get min height. fn get_min_height(&self) -> i32 { cmp::min( get_image_height(self.server_image), @@ -261,8 +269,8 @@ impl VncServer { count } - /// Update the dirty area of the image - /// Return dirty number + /// Flush dirty data from guest_image to server_image. + /// Return the number of dirty area. pub fn update_server_image(&mut self) -> i32 { let mut dirty_num = 0; let height = self.get_min_height(); @@ -336,8 +344,14 @@ impl VncServer { .expect("set nonblocking failed"); let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut client = - VncClient::new(stream, addr.to_string(), server, self.server_image); + let mut client = VncClient::new( + stream, + addr.to_string(), + self.auth, + self.subauth, + server, + self.server_image, + ); client.write_msg("RFB 003.008\n".to_string().as_bytes()); info!("{:?}", client.stream); @@ -411,7 +425,7 @@ impl EventNotifierHelper for VncServer { } } -/// Refresh server_image to guest_image +/// Refresh server_image to guest_image. fn vnc_refresh() { if VNC_SERVERS.lock().unwrap().is_empty() { return; diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 7b26c5ccb..a110c9fb2 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::errors::{ErrorKind, Result}; +use core::time; use machine_manager::{ config::{ObjConfig, VncConfig}, event_loop::EventLoop, @@ -22,21 +23,19 @@ use std::{ net::TcpListener, ptr, sync::{Arc, Mutex}, + thread, }; use util::{ bitmap::Bitmap, loop_context::EventNotifierHelper, - pixman::{ - pixman_format_code_t, pixman_image_create_bits, pixman_image_ref, pixman_image_t, - pixman_image_unref, - }, + pixman::{pixman_format_code_t, pixman_image_create_bits, pixman_image_ref, pixman_image_t}, }; use vmm_sys_util::eventfd::EventFd; use crate::{ bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, round_up, - round_up_div, unref_pixman_image, PixelFormat, RectInfo, Rectangle, VncClient, VncFeatures, - VncServer, ENCODING_ALPHA_CURSOR, ENCODING_RAW, ENCODING_RICH_CURSOR, + round_up_div, unref_pixman_image, PixelFormat, RectInfo, Rectangle, ServerMsg, VncClient, + VncFeatures, VncServer, ENCODING_ALPHA_CURSOR, ENCODING_RAW, ENCODING_RICH_CURSOR, }; /// The number of dirty pixels represented bt one bit in dirty bitmap. @@ -52,7 +51,7 @@ pub const VNC_BITMAP_WIDTH: u64 = const DEFAULT_REFRESH_INTERVAL: u64 = 30; const BIT_PER_BYTE: u32 = 8; -/// A struct to record image information +/// Struct to record image information #[derive(Clone, Copy)] pub struct DisplaySurface { /// image format @@ -80,6 +79,11 @@ pub struct DisplayMouse { pub data: Vec, } +/// Initizlization function of vnc +/// +/// # Arguments +/// +/// * `VncConfig` `object`- vnc related parameters pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Result<()> { let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); let listener: TcpListener; @@ -106,6 +110,11 @@ pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Res // Add an VncServer. add_vnc_server(server); + // Vnc_thread: a thread to send the framebuffer + if let Err(err) = start_vnc_thread() { + return Err(err); + } + EventLoop::update_event( EventNotifierHelper::internal_notifiers(VNC_SERVERS.lock().unwrap()[0].clone()), None, @@ -114,6 +123,65 @@ pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Res Ok(()) } +fn start_vnc_thread() -> Result<()> { + let interval = DEFAULT_REFRESH_INTERVAL; + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let _handle = thread::Builder::new() + .name("vnc_worker".to_string()) + .spawn(move || loop { + if VNC_RECT_INFO.lock().unwrap().is_empty() { + thread::sleep(time::Duration::from_millis(interval)); + continue; + } + + let mut rect_info; + match VNC_RECT_INFO.lock().unwrap().get_mut(0) { + Some(rect) => { + rect_info = rect.clone(); + } + None => { + thread::sleep(time::Duration::from_millis(interval)); + continue; + } + } + VNC_RECT_INFO.lock().unwrap().remove(0); + + let mut num_rects: i32 = 0; + let mut buf = Vec::new(); + buf.append(&mut [0_u8; 2].to_vec()); + + let locked_server = server.lock().unwrap(); + for rect in rect_info.rects.iter_mut() { + if check_rect(rect, rect_info.width, rect_info.height) { + let n = send_framebuffer_update( + rect_info.image, + rect_info.encoding, + rect, + rect_info.convert, + rect_info.big_endian, + &rect_info.pixel_format, + &mut buf, + ); + if n >= 0 { + num_rects += n; + } + } + } + buf.insert(2, num_rects as u8); + buf.insert(2, (num_rects >> 8) as u8); + + let client = if let Some(client) = locked_server.clients.get(&rect_info.addr) { + client.clone() + } else { + continue; + }; + drop(locked_server); + client.lock().unwrap().write_msg(&buf); + }) + .unwrap(); + Ok(()) +} + /// Add a vnc server during initialization. fn add_vnc_server(server: VncServer) { VNC_SERVERS @@ -152,6 +220,17 @@ pub fn set_area_dirty( REFRESH_EVT.lock().unwrap().write(1).unwrap(); } +pub fn vnc_display_update(x: i32, y: i32, w: i32, h: i32) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_server = server.lock().unwrap(); + let g_w = get_image_width(locked_server.guest_image); + let g_h = get_image_height(locked_server.guest_image); + set_area_dirty(&mut locked_server.guest_dirtymap, x, y, w, h, g_w, g_h); +} + /// Get the width of image. pub fn vnc_width(width: i32) -> i32 { cmp::min( @@ -224,7 +303,7 @@ fn check_surface(surface: &mut DisplaySurface) -> bool { } /// Check if rectangle is in spec -pub fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { +fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { if rect.x >= width { return false; } @@ -311,6 +390,87 @@ fn convert_pixel(ptr: *mut u32, big_endian: bool, pf: &PixelFormat, buf: &mut Ve } } +/// Send raw data directly without compression +/// +/// # Arguments +/// +/// * `image` = pointer to the data need to be send +/// * `rect` - dirty area of image +/// * `convert` - is need to be convert +/// * `big_endian` - send buffer +/// * `pixel_format` - pixelformat +/// * `buf` - send buffer +fn raw_send_framebuffer_update( + image: *mut pixman_image_t, + rect: &Rectangle, + convert: bool, + big_endian: bool, + pixel_format: &PixelFormat, + buf: &mut Vec, +) -> i32 { + let mut data_ptr = get_image_data(image) as *mut u8; + let stride = get_image_stride(image); + data_ptr = (data_ptr as usize + + (rect.y * stride) as usize + + rect.x as usize * bytes_per_pixel()) as *mut u8; + + let copy_bytes = rect.w as usize * bytes_per_pixel(); + + for _i in 0..rect.h { + if !convert { + let mut con = vec![0; copy_bytes]; + unsafe { + ptr::copy(data_ptr, con.as_mut_ptr(), copy_bytes); + } + buf.append(&mut con); + } else { + convert_pixel( + data_ptr as *mut u32, + big_endian, + pixel_format, + buf, + copy_bytes as u32, + ); + } + + data_ptr = (data_ptr as usize + stride as usize) as *mut u8; + } + + 1 +} + +/// Send data according to compression algorithm +/// +/// # Arguments +/// +/// * `image` = pointer to the data need to be send +/// * `rect` - dirty area of image +/// * `convert` - is need to be convert +/// * `big_endian` - send buffer +/// * `pixel_format` - pixelformat +/// * `buf` - send buffer +fn send_framebuffer_update( + image: *mut pixman_image_t, + encoding: i32, + rect: &Rectangle, + convert: bool, + big_endian: bool, + pixel_format: &PixelFormat, + buf: &mut Vec, +) -> i32 { + framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, encoding, buf); + /* + match encoding { + ENCODING_ZLIB => { /* ing... */ } + _ => { + VncServer::framebuffer_upadate(rect, encoding, buf); + n = VncServer::raw_send_framebuffer_update(image, rect, buf); + } + } + */ + raw_send_framebuffer_update(image, rect, convert, big_endian, pixel_format, buf) +} + /// Initialize a default image /// Default: width is 640, height is 480, stride is 640 * 4 fn get_client_image() -> *mut pixman_image_t { @@ -352,13 +512,24 @@ pub fn vnc_display_switch(surface: &mut DisplaySurface) { return; } update_client_surface(&mut locked_server); + // Cursor. + let mut cursor: DisplayMouse = DisplayMouse::default(); + let mut mask: Vec = Vec::new(); + if let Some(c) = &locked_server.cursor { + cursor = c.clone(); + } + if let Some(m) = &locked_server.mask { + mask = m.clone(); + } for client in locked_server.clients.values_mut() { let width = vnc_width(guest_width); let height = vnc_height(guest_height); let mut locked_client = client.lock().unwrap(); - // Desktop_resize; - // Cursor + // Desktop_resize. + if !mask.is_empty() { + display_cursor_define(&mut locked_client, &mut cursor, &mut mask); + } locked_client.desktop_resize(); locked_client.dirty_bitmap.clear_all(); set_area_dirty( @@ -373,6 +544,84 @@ pub fn vnc_display_switch(surface: &mut DisplaySurface) { } } +pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let width = cursor.width as u64; + let heigt = cursor.height as u64; + + let bpl = round_up_div(width as u64, BIT_PER_BYTE as u64); + let len = bpl * heigt as u64; + let mut mask = Bitmap::::new(len as usize); + for j in 0..heigt { + for i in 0..width { + let idx = i + j * width; + if let Some(n) = cursor.data.get(idx as usize) { + if *n == 0xff { + mask.set(idx as usize).unwrap(); + } + } + } + } + let mut data = Vec::new(); + mask.get_data(&mut data); + server.lock().unwrap().cursor = Some(cursor.clone()); + server.lock().unwrap().mask = Some(data.clone()); + + // Send the framebuff for each client. + for client in server.lock().unwrap().clients.values_mut() { + display_cursor_define(&mut client.lock().unwrap(), cursor, &mut data); + } +} + +/// Send framebuf of mouse to the client. +pub fn display_cursor_define( + client: &mut VncClient, + cursor: &mut DisplayMouse, + mask: &mut Vec, +) { + let mut buf = Vec::new(); + if client.has_feature(VncFeatures::VncFeatureAlphaCursor) { + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding + buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects + + framebuffer_upadate( + cursor.hot_x as i32, + cursor.hot_y as i32, + cursor.width as i32, + cursor.height as i32, + ENCODING_ALPHA_CURSOR as i32, + &mut buf, + ); + buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); + buf.append(&mut cursor.data); + client.write_msg(&buf); + return; + } + + if client.has_feature(VncFeatures::VncFeatureRichCursor) { + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding + buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects + + framebuffer_upadate( + cursor.hot_x as i32, + cursor.hot_y as i32, + cursor.width as i32, + cursor.height as i32, + ENCODING_RICH_CURSOR as i32, + &mut buf, + ); + let big_endian = client.big_endian; + let pixel_format = &client.pixel_format; + let size = cursor.width * cursor.height * pixel_format.pixel_bytes as u32; + let ptr = cursor.data.as_ptr() as *mut u32; + convert_pixel(ptr, big_endian, pixel_format, &mut buf, size); + buf.append(mask); + client.write_msg(&buf); + } +} + pub static VNC_SERVERS: Lazy>>>> = Lazy::new(|| Mutex::new(Vec::new())); pub static VNC_RECT_INFO: Lazy>>> = -- Gitee From 9955ad48b5d6b08b195c6039b321e0a343e41378 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 01:48:35 +0800 Subject: [PATCH 0133/2187] virtio-gpu: add edid lib Extended Display Identification Data (edid) is need by virtio-gpu to describe display attributes --- util/src/edid.rs | 505 +++++++++++++++++++++++++++++++++++++++++++++++ util/src/lib.rs | 1 + 2 files changed, 506 insertions(+) create mode 100644 util/src/edid.rs diff --git a/util/src/edid.rs b/util/src/edid.rs new file mode 100644 index 000000000..4f7b5aa39 --- /dev/null +++ b/util/src/edid.rs @@ -0,0 +1,505 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// We use Leaky Bucket Algorithm to limit iops of block device and qmp. +use byteorder::{BigEndian, ByteOrder, LittleEndian}; +use log::warn; + +#[derive(Debug, Default)] +struct EdidMode { + xres: u32, + yres: u32, + byte: u32, + xtra3: u32, + bit: u32, + dta: u32, +} + +#[derive(Debug, Default)] +pub struct EdidInfo { + vendor: Vec, + name: Vec, + serial: u32, + dpi: u32, + prefx: u32, + prefy: u32, + maxx: u32, + maxy: u32, +} + +impl EdidInfo { + pub fn new(vendor: &str, name: &str, dpi: u32, x: u32, y: u32) -> Self { + EdidInfo { + vendor: vendor.chars().collect(), + name: name.chars().collect(), + serial: 0, + dpi, + prefx: x, + prefy: y, + maxx: x, + maxy: y, + } + } + + pub fn edid_array_fulfill(&mut self, edid_array: &mut Vec) { + // The format follows VESA ENHANCED EXTENDED DISPLAY IDENTIFICATION DATA STANDARD + if self.vendor.len() != 3 { + // HWV for 'HUAWEI TECHNOLOGIES CO., INC.' + self.vendor = "HWV".chars().collect(); + } + if self.name.is_empty() { + self.name = "STRA Monitor".chars().collect(); + } + if self.dpi == 0 { + self.dpi = 100; + } + if self.prefx == 0 { + self.prefx = 1024; + } + if self.prefy == 0 { + self.prefy = 768; + } + + let mut offset: usize = 54; + let mut xtra3_offset: usize = 0; + let mut dta_offset: usize = 0; + if edid_array.len() >= 256 { + dta_offset = 128; + edid_array[126] += 1; + self.fullfill_ext_dta(edid_array, dta_offset); + } + + // Fixed header + let header: u64 = 0x00FF_FFFF_FFFF_FF00; + LittleEndian::write_u64(&mut edid_array[0..8], header); + // ID Manufacturer Name + let vendor_id: u16 = (((self.vendor[0] as u16 - '@' as u16) & 0x1f) << 10) + | (((self.vendor[1] as u16 - '@' as u16) & 0x1f) << 5) + | ((self.vendor[2] as u16 - '@' as u16) & 0x1f); + BigEndian::write_u16(&mut edid_array[8..10], vendor_id); + // ID Product Code + LittleEndian::write_u16(&mut edid_array[10..12], 0x1234); + // ID Serial Number + LittleEndian::write_u32(&mut edid_array[12..16], self.serial); + // Week of Manufacture + edid_array[16] = 42; + // Year of Manufacture or Model Year + edid_array[17] = (2022 - 1990) as u8; + // Version Number: defines EDID Structure Version 1, Revision 4. + edid_array[18] = 0x01; + // Revision Number + edid_array[19] = 0x04; + + // Video Input Definition: digital, 8bpc, displayport + edid_array[20] = 0xa5; + // Horizontal Screen Size or Aspect Ratio + edid_array[21] = (self.prefx * self.dpi / 2540) as u8; + // Vertical Screen Size or Aspect Ratio + edid_array[22] = (self.prefy * self.dpi / 2540) as u8; + // Display Transfer Characteristic: display gamma is 2.2 + edid_array[23] = 220 - 100; + // Feature Support: std sRGB, preferred timing + edid_array[24] = 0x06; + + let temp: [f32; 8] = [ + 0.6400, 0.3300, 0.3000, 0.6000, 0.1500, 0.0600, 0.3127, 0.3290, + ]; + // Color Characteristics: 10 bytes + self.fullfill_color_space(edid_array, temp); + + // 18 Byte Data Blocks: 72 bytes + self.fullfill_desc_timing(edid_array, offset); + offset += 18; + + self.fullfill_desc_range(edid_array, offset, 0xfd); + offset += 18; + + if !self.name.is_empty() { + self.fullfill_desc_text(edid_array, offset, 0xfc); + offset += 18; + } + + if self.serial != 0 { + self.fullfill_desc_text(edid_array, offset, 0xff); + offset += 18; + } + + if offset < 126 { + xtra3_offset = offset; + self.fullfill_desc_xtra3_std(edid_array, xtra3_offset); + offset += 18; + } + + while offset < 126 { + self.fullfill_desc_dummy(edid_array, offset); + offset += 18; + } + + // Established Timings: 3 bytes + // Standard Timings: 16 bytes + self.fullfill_modes(edid_array, xtra3_offset, dta_offset); + + // EXTENSION Flag and Checksum + self.fullfill_checksum(edid_array) + } + + fn fullfill_ext_dta(&mut self, edid_array: &mut [u8], offset: usize) { + edid_array[offset] = 0x02; + edid_array[offset + 1] = 0x03; + edid_array[offset + 2] = 0x05; + edid_array[offset + 3] = 0x00; + // video data block + edid_array[offset + 4] = 0x40; + } + + fn fullfill_color_space(&mut self, edid_array: &mut [u8], arr: [f32; 8]) { + let red_x: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let red_y: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let green_x: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let green_y: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let blue_x: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let blue_y: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let white_x: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + let white_y: u32 = (arr[0] * 1024_f32 + 0.5) as u32; + + edid_array[25] = (((red_x & 0x03) << 6) + | ((red_y & 0x03) << 4) + | ((green_x & 0x03) << 2) + | (green_y & 0x03)) as u8; + edid_array[26] = (((blue_x & 0x03) << 6) + | ((blue_y & 0x03) << 4) + | ((white_x & 0x03) << 2) + | (white_y & 0x03)) as u8; + edid_array[27] = (red_x >> 2) as u8; + edid_array[28] = (red_y >> 2) as u8; + edid_array[29] = (green_x >> 2) as u8; + edid_array[30] = (green_y >> 2) as u8; + edid_array[31] = (blue_x >> 2) as u8; + edid_array[32] = (blue_y >> 2) as u8; + edid_array[33] = (white_x >> 2) as u8; + edid_array[34] = (white_y >> 2) as u8; + } + + fn fullfill_desc_timing(&mut self, edid_array: &mut [u8], offset: usize) { + // physical display size + let xmm: u32 = self.prefx * self.dpi / 254; + let ymm: u32 = self.prefy * self.dpi / 254; + let xfront: u32 = self.prefx * 25 / 100; + let xsync: u32 = self.prefx * 3 / 100; + let xblank: u32 = self.prefx * 35 / 100; + let yfront: u32 = self.prefy * 5 / 1000; + let ysync: u32 = self.prefy * 5 / 1000; + let yblank: u32 = self.prefy * 35 / 1000; + let clock: u32 = 75 * (self.prefx + xblank) * (self.prefy + yblank); + + LittleEndian::write_u16(&mut edid_array[offset..offset + 2], clock as u16); + edid_array[offset + 2] = (self.prefx & 0xff) as u8; + edid_array[offset + 3] = (xblank & 0xff) as u8; + edid_array[offset + 4] = (((self.prefx & 0xf00) >> 4) | ((xblank & 0xf00) >> 8)) as u8; + edid_array[offset + 5] = (self.prefy & 0xff) as u8; + edid_array[offset + 6] = (yblank & 0xff) as u8; + edid_array[offset + 7] = (((self.prefy & 0xf00) >> 4) | ((yblank & 0xf00) >> 8)) as u8; + edid_array[offset + 8] = (xfront & 0xff) as u8; + edid_array[offset + 9] = (xsync & 0xff) as u8; + edid_array[offset + 10] = (((yfront & 0x00f) << 4) | (ysync & 0x00f)) as u8; + edid_array[offset + 11] = (((xfront & 0x300) >> 2) + | ((xsync & 0x300) >> 4) + | ((yfront & 0x030) >> 2) + | ((ysync & 0x030) >> 4)) as u8; + edid_array[offset + 12] = (xmm & 0xff) as u8; + edid_array[offset + 13] = (ymm & 0xff) as u8; + edid_array[offset + 14] = (((xmm & 0xf00) >> 4) | ((ymm & 0xf00) >> 8)) as u8; + edid_array[offset + 17] = 0x18; + } + + fn fullfill_desc_range(&mut self, edid_array: &mut [u8], offset: usize, desc_type: u8) { + self.fullfill_desc_type(edid_array, offset, desc_type); + // vertical (50 -> 125 Hz) + edid_array[offset + 5] = 50; + edid_array[offset + 6] = 125; + // horizontal (30 -> 160 kHz) + edid_array[offset + 7] = 30; + edid_array[offset + 8] = 160; + // max dot clock (1200 MHz) + edid_array[offset + 9] = (1200 / 10) as u8; + // no extended timing information + edid_array[offset + 10] = 0x01; + // padding + edid_array[offset + 11] = b'\n'; + for i in 12..18 { + edid_array[offset + i] = b' '; + } + } + + fn fullfill_desc_text(&mut self, edid_array: &mut [u8], offset: usize, desc_type: u8) { + self.fullfill_desc_type(edid_array, offset, desc_type); + for i in 5..18 { + edid_array[offset + i] = b' '; + } + if desc_type == 0xfc { + // name + for (index, c) in self.name.iter().enumerate() { + edid_array[offset + 5 + index] = (*c) as u8; + } + } else if desc_type == 0xff { + // serial + LittleEndian::write_u32(&mut edid_array[offset + 5..offset + 9], self.serial); + } else { + warn!("Unexpectd desc type"); + } + } + + fn fullfill_desc_xtra3_std(&mut self, edid_array: &mut [u8], offset: usize) { + // additional standard timings 3 + self.fullfill_desc_type(edid_array, offset, 0xf7); + edid_array[offset + 4] = 10; + } + + fn fullfill_desc_dummy(&mut self, edid_array: &mut [u8], offset: usize) { + self.fullfill_desc_type(edid_array, offset, 0x10); + } + + fn fullfill_desc_type(&mut self, edid_array: &mut [u8], offset: usize, desc_type: u8) { + edid_array[offset] = 0; + edid_array[offset + 1] = 0; + edid_array[offset + 2] = 0; + edid_array[offset + 3] = desc_type; + edid_array[offset + 4] = 0; + } + + fn fullfill_modes(&mut self, edid_array: &mut [u8], xtra3_offset: usize, dta_offset: usize) { + let edid_modes = vec![ + // dea/dta extension timings (all @ 50 Hz) + EdidMode { + xres: 5120, + yres: 2160, + dta: 125, + ..Default::default() + }, + EdidMode { + xres: 4096, + yres: 2160, + dta: 101, + ..Default::default() + }, + EdidMode { + xres: 3840, + yres: 2160, + dta: 96, + ..Default::default() + }, + EdidMode { + xres: 2560, + yres: 1080, + dta: 89, + ..Default::default() + }, + EdidMode { + xres: 2048, + yres: 1152, + ..Default::default() + }, + EdidMode { + xres: 1920, + yres: 1080, + dta: 31, + ..Default::default() + }, + // additional standard timings 3 (all @ 60Hz) + EdidMode { + xres: 1920, + yres: 1440, + xtra3: 11, + bit: 5, + ..Default::default() + }, + EdidMode { + xres: 1920, + yres: 1200, + xtra3: 10, + bit: 0, + ..Default::default() + }, + EdidMode { + xres: 1856, + yres: 1392, + xtra3: 10, + bit: 3, + ..Default::default() + }, + EdidMode { + xres: 1792, + yres: 1344, + xtra3: 10, + bit: 5, + ..Default::default() + }, + EdidMode { + xres: 1600, + yres: 1200, + xtra3: 9, + bit: 2, + ..Default::default() + }, + EdidMode { + xres: 1680, + yres: 1050, + xtra3: 9, + bit: 5, + ..Default::default() + }, + EdidMode { + xres: 1440, + yres: 1050, + xtra3: 8, + bit: 1, + ..Default::default() + }, + EdidMode { + xres: 1440, + yres: 900, + xtra3: 8, + bit: 5, + ..Default::default() + }, + EdidMode { + xres: 1360, + yres: 768, + xtra3: 8, + bit: 7, + ..Default::default() + }, + EdidMode { + xres: 1280, + yres: 1024, + xtra3: 7, + bit: 1, + ..Default::default() + }, + EdidMode { + xres: 1280, + yres: 960, + xtra3: 7, + bit: 3, + ..Default::default() + }, + EdidMode { + xres: 1280, + yres: 768, + xtra3: 7, + bit: 6, + ..Default::default() + }, + // established timings (all @ 60Hz) + EdidMode { + xres: 1024, + yres: 768, + byte: 36, + bit: 3, + ..Default::default() + }, + EdidMode { + xres: 800, + yres: 600, + byte: 35, + bit: 0, + ..Default::default() + }, + EdidMode { + xres: 640, + yres: 480, + byte: 35, + bit: 5, + ..Default::default() + }, + ]; + let mut std_offset: usize = 38; + + for mode in edid_modes { + if (self.maxx != 0 && mode.xres > self.maxx) + || (self.maxy != 0 && mode.yres > self.maxy) + { + continue; + } + + if mode.byte != 0 { + edid_array[mode.byte as usize] |= (1 << mode.bit) as u8; + } else if mode.xtra3 != 0 && xtra3_offset != 0 { + edid_array[xtra3_offset] |= (1 << mode.bit) as u8; + } else if std_offset < 54 + && self.fullfill_std_mode(edid_array, std_offset, mode.xres, mode.yres) == 0 + { + std_offset += 2; + } + + if dta_offset != 0 && mode.dta != 0 { + self.fullfill_ext_dta_mode(edid_array, dta_offset, mode.dta); + } + } + + while std_offset < 54 { + self.fullfill_std_mode(edid_array, std_offset, 0, 0); + std_offset += 2; + } + } + + fn fullfill_std_mode( + &mut self, + edid_array: &mut [u8], + std_offset: usize, + xres: u32, + yres: u32, + ) -> i32 { + let aspect: u32; + + if xres == 0 || yres == 0 { + edid_array[std_offset] = 0x01; + edid_array[std_offset + 1] = 0x01; + return 0; + } else if xres * 10 == yres * 16 { + aspect = 0; + } else if xres * 3 == yres * 4 { + aspect = 1; + } else if xres * 4 == yres * 5 { + aspect = 2; + } else if xres * 9 == yres * 16 { + aspect = 3; + } else { + return -1; + } + + if (xres / 8) - 31 > 255 { + return -1; + } + edid_array[std_offset] = ((xres / 8) - 31) as u8; + edid_array[std_offset + 1] = (aspect << 6) as u8; + 0 + } + + fn fullfill_ext_dta_mode(&mut self, edid_array: &mut [u8], dta_offset: usize, dta: u32) { + let index = edid_array[dta_offset + 2] as usize; + edid_array[index] = dta as u8; + edid_array[dta_offset + 2] += 1; + edid_array[dta_offset + 4] += 1; + } + + fn fullfill_checksum(&mut self, edid_array: &mut [u8]) { + let mut sum: u32 = 0; + for elem in edid_array.iter() { + sum += *elem as u32; + } + sum &= 0xff; + if sum != 0 { + edid_array[127] = (0x100 - sum) as u8; + } + } +} diff --git a/util/src/lib.rs b/util/src/lib.rs index 4e9a9c8b8..ee9bfd014 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -18,6 +18,7 @@ pub mod checksum; pub mod daemonize; #[cfg(target_arch = "aarch64")] pub mod device_tree; +pub mod edid; pub mod leak_bucket; mod link_list; pub mod logger; -- Gitee From a9634400e0b34c9834bc7a462e49fa2f8de18c01 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 05:06:55 +0800 Subject: [PATCH 0134/2187] virtio-gpu: support virtio-gpu configuration can parse virtio-gpu configuration to virtio-gpu device, but this device can not handle any cmd now. --- machine/src/lib.rs | 18 +- machine_manager/src/config/gpu.rs | 113 ++++++++++ machine_manager/src/config/machine_config.rs | 4 +- machine_manager/src/config/mod.rs | 2 + virtio/src/gpu.rs | 210 +++++++++++++++++++ virtio/src/lib.rs | 3 + 6 files changed, 345 insertions(+), 5 deletions(-) create mode 100644 machine_manager/src/config/gpu.rs create mode 100644 virtio/src/gpu.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 17028090a..1d8ea5ca4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -123,7 +123,7 @@ use errors::{ErrorKind, Result, ResultExt}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, + parse_gpu, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, @@ -149,8 +149,8 @@ use util::{ }; use vfio::{VfioDevice, VfioPciDevice}; use virtio::{ - balloon_allow_list, Balloon, Block, BlockState, Console, Rng, RngState, VhostKern, VhostUser, - VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, + balloon_allow_list, Balloon, Block, BlockState, Console, Gpu, Rng, RngState, VhostKern, + VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; @@ -703,6 +703,15 @@ pub trait MachineOps { Ok(()) } + fn add_virtio_pci_gpu(&mut self, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let device_cfg = parse_gpu(cfg_args)?; + let device = Arc::new(Mutex::new(Gpu::new(device_cfg.clone()))); + self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false)?; + Ok(()) + } + fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> StdResult<(u8, Weak>)> { let pci_host = self.get_pci_host()?; let bus = pci_host.lock().unwrap().root_bus.clone(); @@ -1042,6 +1051,9 @@ pub trait MachineOps { "vfio-pci" => { self.add_vfio_device(cfg_args)?; } + "virtio-gpu-pci" => { + self.add_virtio_pci_gpu(cfg_args)?; + } "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs new file mode 100644 index 000000000..dead90221 --- /dev/null +++ b/machine_manager/src/config/gpu.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::{ + errors::{ErrorKind, Result}, + M, +}; +use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; + +/// The maximum number of scanouts. +pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; + +#[derive(Clone)] +pub struct GpuConfig { + pub id: String, + pub max_outputs: u32, + pub edid: bool, + pub xres: u32, + pub yres: u32, + pub max_hostmem: u64, +} + +impl Default for GpuConfig { + fn default() -> Self { + GpuConfig { + id: "".to_string(), + max_outputs: 1, + edid: true, + xres: 1024, + yres: 768, + max_hostmem: 256 * M, + } + } +} + +impl ConfigCheck for GpuConfig { + fn check(&self) -> Result<()> { + if self.id.len() > MAX_STRING_LENGTH { + return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + } + + if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { + return Err(ErrorKind::IllegalValue( + "max_outputs".to_string(), + 0, + false, + VIRTIO_GPU_MAX_SCANOUTS as u64, + true, + ) + .into()); + } + + if self.max_hostmem < 256 * M { + return Err(ErrorKind::IllegalValue( + "max_hostmem".to_string(), + 0, + false, + 256 * M as u64, + true, + ) + .into()); + } + + Ok(()) + } +} + +pub fn parse_gpu(gpu_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("virtio-gpu-pci"); + cmd_parser + .push("") + .push("id") + .push("max_outputs") + .push("edid") + .push("xres") + .push("yres") + .push("max_hostmem") + .push("bus") + .push("addr"); + cmd_parser.parse(gpu_config)?; + + let mut gpu_cfg: GpuConfig = GpuConfig::default(); + if let Some(id) = cmd_parser.get_value::("id")? { + gpu_cfg.id = id; + } + if let Some(max_outputs) = cmd_parser.get_value::("max_outputs")? { + gpu_cfg.max_outputs = max_outputs; + } + if let Some(edid) = cmd_parser.get_value::("edid")? { + gpu_cfg.edid = edid; + } + if let Some(xres) = cmd_parser.get_value::("xres")? { + gpu_cfg.xres = xres; + } + if let Some(yres) = cmd_parser.get_value::("yres")? { + gpu_cfg.yres = yres; + } + if let Some(max_hostmem) = cmd_parser.get_value::("max_hostmem")? { + gpu_cfg.max_hostmem = max_hostmem; + } + gpu_cfg.check()?; + + Ok(gpu_cfg) +} diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index b7e90db05..102863659 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -32,8 +32,8 @@ const MAX_NR_CPUS: u64 = 254; const MIN_NR_CPUS: u64 = 1; const MAX_MEMSIZE: u64 = 549_755_813_888; const MIN_MEMSIZE: u64 = 268_435_456; -const M: u64 = 1024 * 1024; -const G: u64 = 1024 * 1024 * 1024; +pub const M: u64 = 1024 * 1024; +pub const G: u64 = 1024 * 1024 * 1024; #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum MachineType { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index c0b0bce96..bf1baa119 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -84,6 +84,7 @@ pub use boot_source::*; pub use chardev::*; pub use devices::*; pub use drive::*; +pub use gpu::*; pub use incoming::*; pub use iothread::*; pub use machine_config::*; @@ -100,6 +101,7 @@ mod boot_source; mod chardev; mod devices; mod drive; +mod gpu; mod incoming; mod iothread; mod machine_config; diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs new file mode 100644 index 000000000..41fb6917c --- /dev/null +++ b/virtio/src/gpu.rs @@ -0,0 +1,210 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result, ResultExt}; +use super::{Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU}; +use address_space::AddressSpace; +use error_chain::bail; +use log::warn; +use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; +use migration::{DeviceStateDesc, FieldDesc}; +use migration_derive::{ByteCode, Desc}; +use std::cmp; +use std::io::Write; +use std::sync::{Arc, Mutex}; +use util::byte_code::ByteCode; +use util::num_ops::{read_u32, write_u32}; +use vmm_sys_util::eventfd::EventFd; + +/// Number of virtqueues. +const QUEUE_NUM_GPU: usize = 2; +/// Size of each virtqueue. +const QUEUE_SIZE_GPU: u16 = 256; +/// Flags for virtio gpu base conf. +const VIRTIO_GPU_FLAG_VIRGL_ENABLED: u32 = 1; +//const VIRTIO_GPU_FLAG_STATS_ENABLED: u32 = 2; +const VIRTIO_GPU_FLAG_EDID_ENABLED: u32 = 3; + +#[derive(Clone, Copy, Debug, ByteCode)] +pub struct VirtioGpuConfig { + events_read: u32, + events_clear: u32, + num_scanouts: u32, + reserved: u32, +} + +#[derive(Clone, Copy, Debug, ByteCode)] +pub struct VirtioGpuBaseConf { + max_outputs: u32, + flags: u32, + xres: u32, + yres: u32, +} + +/// State of gpu device. +#[repr(C)] +#[derive(Clone, Copy, Desc, ByteCode)] +#[desc_version(compat_version = "0.1.0")] +pub struct GpuState { + /// Bitmask of features supported by the backend. + device_features: u64, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, + /// Config space of the GPU device. + config: VirtioGpuConfig, + /// Baisc Configure of GPU device. + base_conf: VirtioGpuBaseConf, +} + +/// GPU device structure. +pub struct Gpu { + /// Configuration of the GPU device. + gpu_conf: GpuConfig, + /// Status of the GPU device. + state: GpuState, + /// Callback to trigger interrupt. + interrupt_cb: Option>, + /// Eventfd for device deactivate. + deactivate_evt: EventFd, +} + +impl Default for Gpu { + fn default() -> Self { + Gpu { + gpu_conf: GpuConfig::default(), + state: GpuState::default(), + interrupt_cb: None, + deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + } + } +} + +impl Gpu { + pub fn new(gpu_conf: GpuConfig) -> Gpu { + let mut state = GpuState::default(); + state.base_conf.xres = gpu_conf.xres; + state.base_conf.yres = gpu_conf.yres; + if gpu_conf.edid { + state.base_conf.flags &= 1 << VIRTIO_GPU_FLAG_EDID_ENABLED; + } + state.base_conf.max_outputs = gpu_conf.max_outputs; + state.device_features = 1u64 << VIRTIO_F_VERSION_1; + Self { + gpu_conf, + state, + interrupt_cb: None, + deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + } + } +} + +impl VirtioDevice for Gpu { + /// Realize virtio gpu device. + fn realize(&mut self) -> Result<()> { + if self.gpu_conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { + bail!( + "Invalid max_outputs {} which is bigger than {}", + self.gpu_conf.max_outputs, + VIRTIO_GPU_MAX_SCANOUTS + ); + } + + // Virgl is not supported. + self.state.base_conf.flags &= !(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); + self.state.config.num_scanouts = self.state.base_conf.max_outputs; + self.state.config.reserved = 0; + Ok(()) + } + + /// Get the virtio device type, refer to Virtio Spec. + fn device_type(&self) -> u32 { + VIRTIO_TYPE_GPU + } + + /// Get the count of virtio gpu queues. + fn queue_num(&self) -> usize { + QUEUE_NUM_GPU + } + + /// Get the queue size of virtio gpu. + fn queue_size(&self) -> u16 { + QUEUE_SIZE_GPU + } + + /// Get device features from host. + fn get_device_features(&self, features_select: u32) -> u32 { + read_u32(self.state.device_features, features_select) + } + + /// Set driver features by guest. + fn set_driver_features(&mut self, page: u32, value: u32) { + let mut v = write_u32(value, page); + let unrequested_features = v & !self.state.device_features; + if unrequested_features != 0 { + warn!("Received acknowledge request with unknown feature: {:x}", v); + v &= !unrequested_features; + } + self.state.driver_features |= v; + } + + /// Read data of config from guest. + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + let config_slice = self.state.config.as_bytes(); + let config_len = config_slice.len() as u64; + if offset >= config_len { + return Err(ErrorKind::DevConfigOverflow(offset, config_len).into()); + } + if let Some(end) = offset.checked_add(data.len() as u64) { + data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; + } + Ok(()) + } + + /// Write data to config from guest. + fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + let data_len = data.len(); + let config_slice = self.state.config.as_mut_bytes(); + let config_len = config_slice.len(); + if offset as usize + data_len > config_len { + return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + } + + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + + if self.state.config.events_clear != 0 { + self.state.config.events_read &= !self.state.config.events_clear; + } + + Ok(()) + } + + fn activate( + &mut self, + _mem_space: Arc, + interrupt_cb: Arc, + queues: &[Arc>], + mut _queue_evts: Vec, + ) -> Result<()> { + if queues.len() != QUEUE_NUM_GPU { + return Err(ErrorKind::IncorrectQueueNum(QUEUE_NUM_GPU, queues.len()).into()); + } + self.interrupt_cb = Some(interrupt_cb.clone()); + + Ok(()) + } + + fn deactivate(&mut self) -> Result<()> { + self.deactivate_evt + .write(1) + .chain_err(|| ErrorKind::EventFdWrite) + } +} diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index afb53dd68..6d91a5d01 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -102,6 +102,7 @@ pub mod errors { mod balloon; mod block; mod console; +mod gpu; mod net; mod queue; mod rng; @@ -114,6 +115,7 @@ pub use balloon::*; pub use block::{Block, BlockState}; pub use console::{Console, VirtioConsoleState}; pub use errors::*; +pub use gpu::*; pub use net::*; pub use queue::*; pub use rng::{Rng, RngState}; @@ -140,6 +142,7 @@ pub const VIRTIO_TYPE_BLOCK: u32 = 2; pub const VIRTIO_TYPE_CONSOLE: u32 = 3; pub const VIRTIO_TYPE_RNG: u32 = 4; pub const VIRTIO_TYPE_BALLOON: u32 = 5; +pub const VIRTIO_TYPE_GPU: u32 = 16; pub const VIRTIO_TYPE_VSOCK: u32 = 19; pub const _VIRTIO_TYPE_FS: u32 = 26; -- Gitee From 5110bf5610fac8e5add4029170609ec668f151cc Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 06:02:44 +0800 Subject: [PATCH 0135/2187] virtio-gpu: create unready queue for virtio-device For virtio devices with multiple queues it is a good idea to alloc multiple queues for the device, even if the driver does not enable these queues. Because some devices like virtio-gpu distinguish specific queues, and we use index in vector to distinguish them. After this patch, check whether the queue is valid before using it. --- virtio/src/virtio_pci.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index db01fdbe7..2f6c56eac 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -19,7 +19,7 @@ use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEv use byteorder::{ByteOrder, LittleEndian}; use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; +use log::{error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use pci::config::{ @@ -707,19 +707,20 @@ impl VirtioPciDevice { let mut locked_queues = cloned_pci_device.queues.lock().unwrap(); for q_config in queues_config.iter_mut() { if !q_config.ready { - continue; + warn!("queue is not ready, please check your init process"); + } else { + q_config.addr_cache.desc_table_host = cloned_mem_space + .get_host_address(q_config.desc_table) + .unwrap_or(0); + q_config.addr_cache.avail_ring_host = cloned_mem_space + .get_host_address(q_config.avail_ring) + .unwrap_or(0); + q_config.addr_cache.used_ring_host = cloned_mem_space + .get_host_address(q_config.used_ring) + .unwrap_or(0); } - q_config.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(q_config.desc_table) - .unwrap_or(0); - q_config.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(q_config.avail_ring) - .unwrap_or(0); - q_config.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(q_config.used_ring) - .unwrap_or(0); let queue = Queue::new(*q_config, queue_type).unwrap(); - if !queue.is_valid(&cloned_pci_device.sys_mem) { + if q_config.ready && !queue.is_valid(&cloned_pci_device.sys_mem) { error!("Failed to activate device: Invalid queue"); return false; } -- Gitee From 2448908b9d4423a57e4c404023d4a6212f7b085b Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 10:08:34 +0800 Subject: [PATCH 0136/2187] virtio-gpu: add GpuIoHandler to take care event Add eventfds into EventLoop, which will be call in epoll and call deactivate_evt_handler or ctrl_queue_evt_handler or cursor_queue_evt_handler. All of them are realized in GpuIoHandler. --- virtio/Cargo.toml | 1 + virtio/src/gpu.rs | 315 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 311 insertions(+), 5 deletions(-) diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index beb38afb7..d84ec913a 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -24,3 +24,4 @@ util = { path = "../util" } pci = { path = "../pci" } acpi = { path = "../acpi" } devices = {path = "../devices"} +vnc = {path = "../vnc"} diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 41fb6917c..b7efcc00f 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -11,19 +11,30 @@ // See the Mulan PSL v2 for more details. use super::errors::{ErrorKind, Result, ResultExt}; -use super::{Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU}; +use super::{ + Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU, +}; use address_space::AddressSpace; use error_chain::bail; -use log::warn; +use log::{error, warn}; use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; +use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc}; use migration_derive::{ByteCode, Desc}; use std::cmp; use std::io::Write; +use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; +use std::{ptr, vec}; +use util::aio::Iovec; use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use util::num_ops::{read_u32, write_u32}; -use vmm_sys_util::eventfd::EventFd; +use util::pixman::pixman_image_t; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use vnc::{DisplayMouse, DisplaySurface}; /// Number of virtqueues. const QUEUE_NUM_GPU: usize = 2; @@ -50,6 +61,268 @@ pub struct VirtioGpuBaseConf { yres: u32, } +#[allow(unused)] +#[derive(Debug)] +struct GpuResource { + resource_id: u32, + width: u32, + height: u32, + format: u32, + iov: Vec, + scanouts_bitmask: u32, + host_mem: u64, + pixman_image: *mut pixman_image_t, +} +impl Default for GpuResource { + fn default() -> Self { + GpuResource { + resource_id: 0, + width: 0, + height: 0, + format: 0, + iov: Vec::new(), + scanouts_bitmask: 0, + host_mem: 0, + pixman_image: ptr::null_mut(), + } + } +} + +#[allow(unused)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuReqState { + width: u32, + height: u32, + x_coor: i32, + y_coor: i32, +} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuCtrlHdr { + hdr_type: u32, + flags: u32, + fence_id: u64, + ctx_id: u32, + padding: u32, +} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuCursorPos { + scanout_id: u32, + x_coord: u32, + y_coord: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuCursorPos {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuUpdateCursor { + header: VirtioGpuCtrlHdr, + pos: VirtioGpuCursorPos, + resource_id: u32, + hot_x: u32, + hot_y: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuUpdateCursor {} + +#[allow(unused)] +#[derive(Clone, Default)] +struct GpuScanout { + surface: Option, + mouse: Option, + width: u32, + height: u32, + x: u32, + y: u32, + invalidate: i32, + resource_id: u32, + cursor: VirtioGpuUpdateCursor, +} + +#[allow(unused)] +impl GpuScanout { + fn clear(&mut self) { + self.resource_id = 0; + self.surface = None; + self.width = 0; + self.height = 0; + } +} + +/// Control block of GPU IO. +#[allow(unused)] +struct GpuIoHandler { + /// The virtqueue for for sending control commands. + ctrl_queue: Arc>, + /// The virtqueue for sending cursor updates + cursor_queue: Arc>, + /// The address space to which the GPU device belongs. + mem_space: Arc, + /// Eventfd for contorl virtqueue. + ctrl_queue_evt: EventFd, + /// Eventfd for cursor virtqueue. + cursor_queue_evt: EventFd, + /// Eventfd for device deactivate. + deactivate_evt: RawFd, + /// Callback to trigger an interrupt. + interrupt_cb: Arc, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, + /// Vector for resources. + resources_list: Vec, + /// The bit mask of whether scanout is enabled or not. + enable_output_bitmask: u32, + /// Baisc Configure of GPU device. + base_conf: VirtioGpuBaseConf, + /// States of all request in scanout. + req_states: [VirtioGpuReqState; VIRTIO_GPU_MAX_SCANOUTS], + /// + scanouts: Vec, + /// Max host mem for resource. + max_hostmem: u64, + /// Current usage of host mem. + used_hostmem: u64, +} + +impl GpuIoHandler { + fn ctrl_queue_evt_handler(&mut self) -> Result<()> { + let mut queue = self.ctrl_queue.lock().unwrap(); + if !queue.is_valid(&self.mem_space) { + bail!("Failed to handle any request, the queue is not ready"); + } + while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .chain_err(|| "Failed to add used ring")?; + } + drop(queue); + (self.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&self.cursor_queue.lock().unwrap()), + ) + .chain_err(|| ErrorKind::InterruptTrigger("gpu", VirtioInterruptType::Vring))?; + + Ok(()) + } + + fn cursor_queue_evt_handler(&mut self) -> Result<()> { + let mut queue = self.cursor_queue.lock().unwrap(); + if !queue.is_valid(&self.mem_space) { + bail!("Failed to handle any request, the queue is not ready"); + } + + while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .chain_err(|| "Failed to add used ring")?; + + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) + .chain_err(|| ErrorKind::InterruptTrigger("gpu", VirtioInterruptType::Vring))?; + } + + Ok(()) + } + + fn deactivate_evt_handler(&mut self) -> Vec { + let notifiers = vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.ctrl_queue_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.cursor_queue_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.deactivate_evt, + None, + EventSet::IN, + Vec::new(), + ), + ]; + notifiers + } +} + +impl EventNotifierHelper for GpuIoHandler { + fn internal_notifiers(gpu_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + // Register event notifier for deactivate_evt. + let gpu_handler_clone = gpu_handler.clone(); + let handler: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + Some(gpu_handler_clone.lock().unwrap().deactivate_evt_handler()) + }); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + gpu_handler.lock().unwrap().deactivate_evt, + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + // Register event notifier for ctrl_queue_evt. + let gpu_handler_clone = gpu_handler.clone(); + let handler: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + if let Err(e) = gpu_handler_clone.lock().unwrap().ctrl_queue_evt_handler() { + error!( + "Failed to process queue for virtio gpu, err: {}", + error_chain::ChainedError::display_chain(&e), + ); + } + + None + }); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + gpu_handler.lock().unwrap().ctrl_queue_evt.as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + // Register event notifier for cursor_queue_evt. + let gpu_handler_clone = gpu_handler.clone(); + let handler: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + if let Err(e) = gpu_handler_clone.lock().unwrap().cursor_queue_evt_handler() { + error!( + "Failed to process queue for virtio gpu, err: {}", + error_chain::ChainedError::display_chain(&e), + ); + } + + None + }); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + gpu_handler.lock().unwrap().cursor_queue_evt.as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + notifiers + } +} + /// State of gpu device. #[repr(C)] #[derive(Clone, Copy, Desc, ByteCode)] @@ -189,15 +462,47 @@ impl VirtioDevice for Gpu { fn activate( &mut self, - _mem_space: Arc, + mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut _queue_evts: Vec, + mut queue_evts: Vec, ) -> Result<()> { if queues.len() != QUEUE_NUM_GPU { return Err(ErrorKind::IncorrectQueueNum(QUEUE_NUM_GPU, queues.len()).into()); } + self.interrupt_cb = Some(interrupt_cb.clone()); + let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; + let mut scanouts = vec![]; + for _i in 0..VIRTIO_GPU_MAX_SCANOUTS { + let scanout = GpuScanout::default(); + scanouts.push(scanout); + } + + let mut gpu_handler = GpuIoHandler { + ctrl_queue: queues[0].clone(), + cursor_queue: queues[1].clone(), + mem_space, + ctrl_queue_evt: queue_evts.remove(0), + cursor_queue_evt: queue_evts.remove(0), + deactivate_evt: self.deactivate_evt.as_raw_fd(), + interrupt_cb, + driver_features: self.state.driver_features, + resources_list: Vec::new(), + enable_output_bitmask: 1, + base_conf: self.state.base_conf, + scanouts, + req_states, + max_hostmem: self.gpu_conf.max_hostmem, + used_hostmem: 0, + }; + gpu_handler.req_states[0].width = self.state.base_conf.xres; + gpu_handler.req_states[0].height = self.state.base_conf.yres; + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(gpu_handler))), + None, + )?; Ok(()) } -- Gitee From 0fa9fa9ac1af9ffbf0223a32017bdec997be106c Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Sun, 21 Aug 2022 11:16:21 +0800 Subject: [PATCH 0137/2187] virtio-gpu: handle cursor event support cursor move and update --- virtio/src/gpu.rs | 210 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 204 insertions(+), 6 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index b7efcc00f..9f6b34f7e 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -12,9 +12,10 @@ use super::errors::{ErrorKind, Result, ResultExt}; use super::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU, + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_GPU, }; -use address_space::AddressSpace; +use address_space::{AddressSpace, GuestAddress}; use error_chain::bail; use log::{error, warn}; use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; @@ -23,6 +24,7 @@ use migration::{DeviceStateDesc, FieldDesc}; use migration_derive::{ByteCode, Desc}; use std::cmp; use std::io::Write; +use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; use std::{ptr, vec}; @@ -32,18 +34,28 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::num_ops::{read_u32, write_u32}; -use util::pixman::pixman_image_t; +use util::pixman::{ + pixman_image_get_data, pixman_image_get_height, pixman_image_get_width, pixman_image_t, +}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use vnc::{DisplayMouse, DisplaySurface}; +use vnc::{vnc_display_cursor, DisplayMouse, DisplaySurface}; /// Number of virtqueues. const QUEUE_NUM_GPU: usize = 2; /// Size of each virtqueue. const QUEUE_SIZE_GPU: u16 = 256; + /// Flags for virtio gpu base conf. const VIRTIO_GPU_FLAG_VIRGL_ENABLED: u32 = 1; -//const VIRTIO_GPU_FLAG_STATS_ENABLED: u32 = 2; +#[allow(unused)] +const VIRTIO_GPU_FLAG_STATS_ENABLED: u32 = 2; const VIRTIO_GPU_FLAG_EDID_ENABLED: u32 = 3; +/// flag used to distinguish the cmd type and format VirtioGpuRequest +const VIRTIO_GPU_CMD_CTRL: u32 = 0; +const VIRTIO_GPU_CMD_CURSOR: u32 = 1; +/// virtio_gpu_ctrl_type: cursor commands. +const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300; +const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301; #[derive(Clone, Copy, Debug, ByteCode)] pub struct VirtioGpuConfig { @@ -73,6 +85,7 @@ struct GpuResource { host_mem: u64, pixman_image: *mut pixman_image_t, } + impl Default for GpuResource { fn default() -> Self { GpuResource { @@ -107,6 +120,114 @@ pub struct VirtioGpuCtrlHdr { padding: u32, } +impl VirtioGpuCtrlHdr { + fn is_valid(&self) -> bool { + match self.hdr_type { + VIRTIO_GPU_CMD_UPDATE_CURSOR => true, + VIRTIO_GPU_CMD_MOVE_CURSOR => true, + _ => { + error!("request type {} is not supported for GPU", self.hdr_type); + false + } + } + } +} + +impl ByteCode for VirtioGpuCtrlHdr {} + +#[allow(unused)] +#[derive(Default, Clone)] +pub struct VirtioGpuRequest { + header: VirtioGpuCtrlHdr, + index: u16, + desc_num: u16, + out_iovec: Vec, + in_iovec: Vec, + in_header: GuestAddress, + out_header: GuestAddress, +} + +impl VirtioGpuRequest { + fn new(mem_space: &Arc, elem: &Element, cmd_type: u32) -> Result { + if cmd_type != VIRTIO_GPU_CMD_CTRL && cmd_type != VIRTIO_GPU_CMD_CURSOR { + bail!("unsupport GPU request: {} ", cmd_type); + } + + if elem.out_iovec.is_empty() + || (cmd_type == VIRTIO_GPU_CMD_CTRL && elem.in_iovec.is_empty()) + || (cmd_type == VIRTIO_GPU_CMD_CURSOR && !elem.in_iovec.is_empty()) + { + bail!( + "Missed header for GPU request: out {} in {} desc num {}", + elem.out_iovec.len(), + elem.in_iovec.len(), + elem.desc_num + ); + } + + let out_elem = elem.out_iovec.get(0).unwrap(); + if out_elem.len < size_of::() as u32 { + bail!( + "Invalid out header for GPU request: length {}", + out_elem.len + ); + } + + let out_header = mem_space + .read_object::(out_elem.addr) + .chain_err(|| ErrorKind::ReadObjectErr("the GPU's request header", out_elem.addr.0))?; + if !out_header.is_valid() { + bail!("Unsupported GPU request type"); + } + + let in_elem_addr = match cmd_type { + VIRTIO_GPU_CMD_CTRL => { + let in_elem = elem.in_iovec.last().unwrap(); + if in_elem.len < 1 { + bail!("Invalid in header for GPU request: length {}", in_elem.len) + } + in_elem.addr + } + VIRTIO_GPU_CMD_CURSOR => GuestAddress(0), + _ => { + bail!("unsupport GPU request: {}", cmd_type) + } + }; + + let mut request = VirtioGpuRequest { + header: out_header, + index: elem.index, + desc_num: elem.desc_num, + out_iovec: Vec::with_capacity(elem.out_iovec.len()), + in_iovec: Vec::with_capacity(elem.in_iovec.len()), + in_header: in_elem_addr, + out_header: out_elem.addr, + }; + + for (index, elem_iov) in elem.in_iovec.iter().enumerate() { + if index == elem.in_iovec.len() - 1 { + break; + } + if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { + let iov = Iovec { + iov_base: hva, + iov_len: u64::from(elem_iov.len), + }; + request.in_iovec.push(iov); + } + } + + for (_index, elem_iov) in elem.out_iovec.iter().enumerate() { + request.out_iovec.push(Iovec { + iov_base: elem_iov.addr.0, + iov_len: elem_iov.len as u64, + }); + } + + Ok(request) + } +} + #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuCursorPos { @@ -190,7 +311,71 @@ struct GpuIoHandler { used_hostmem: u64, } +fn display_define_mouse(mouse: &mut Option) { + if let Some(mouse) = mouse { + vnc_display_cursor(mouse); + } +} + impl GpuIoHandler { + fn gpu_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let info_cursor = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr("the GPU's cursor request header", req.out_header.0) + })?; + + let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; + if info_cursor.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { + scanout.cursor.pos.x_coord = info_cursor.hot_x; + scanout.cursor.pos.y_coord = info_cursor.hot_y; + } else if info_cursor.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { + if scanout.mouse.is_none() { + let tmp_mouse = DisplayMouse { + height: 64, + width: 64, + hot_x: info_cursor.hot_x, + hot_y: info_cursor.hot_y, + ..Default::default() + }; + scanout.mouse = Some(tmp_mouse); + } + if info_cursor.resource_id != 0 { + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == info_cursor.resource_id) + { + let res = &self.resources_list[res_index]; + unsafe { + let res_width = pixman_image_get_width(res.pixman_image); + let res_height = pixman_image_get_height(res.pixman_image); + + if res_width as u32 == scanout.mouse.as_ref().unwrap().width + && res_height as u32 == scanout.mouse.as_ref().unwrap().height + { + let pixels = scanout.mouse.as_ref().unwrap().width + * scanout.mouse.as_ref().unwrap().height; + let mouse_data_size = pixels * (size_of::() as u32); + let mut con = vec![0u8; 64 * 64 * 4]; + let res_data_ptr = pixman_image_get_data(res.pixman_image) as *mut u8; + ptr::copy(res_data_ptr, con.as_mut_ptr(), mouse_data_size as usize); + scanout.mouse.as_mut().unwrap().data.clear(); + scanout.mouse.as_mut().unwrap().data.append(&mut con); + } + } + } + } + display_define_mouse(&mut scanout.mouse); + scanout.cursor = info_cursor; + } else { + bail!("Wrong header type for cursor queue"); + } + + Ok(()) + } + fn ctrl_queue_evt_handler(&mut self) -> Result<()> { let mut queue = self.ctrl_queue.lock().unwrap(); if !queue.is_valid(&self.mem_space) { @@ -213,12 +398,25 @@ impl GpuIoHandler { } fn cursor_queue_evt_handler(&mut self) -> Result<()> { - let mut queue = self.cursor_queue.lock().unwrap(); + let cursor_queue = self.cursor_queue.clone(); + let mut queue = cursor_queue.lock().unwrap(); if !queue.is_valid(&self.mem_space) { bail!("Failed to handle any request, the queue is not ready"); } while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CURSOR) { + Ok(req) => { + self.gpu_update_cursor(&req) + .chain_err(|| "Fail to update cursor")?; + } + Err(e) => { + error!( + "failed to create GPU request, {}", + error_chain::ChainedError::display_chain(&e) + ); + } + } queue .vring .add_used(&self.mem_space, elem.index, 0) -- Gitee From a2a45943ac4f43bc51d27821401f5e6aa7bcaee0 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 22 Aug 2022 00:03:58 +0800 Subject: [PATCH 0138/2187] virtio-gpu: support simple ctrl event ctrl request like get display info and edid are supported. --- virtio/src/gpu.rs | 375 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 361 insertions(+), 14 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 9f6b34f7e..201942de0 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -28,7 +28,6 @@ use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; use std::{ptr, vec}; -use util::aio::Iovec; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -37,6 +36,7 @@ use util::num_ops::{read_u32, write_u32}; use util::pixman::{ pixman_image_get_data, pixman_image_get_height, pixman_image_get_width, pixman_image_t, }; +use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use vnc::{vnc_display_cursor, DisplayMouse, DisplaySurface}; @@ -50,12 +50,36 @@ const VIRTIO_GPU_FLAG_VIRGL_ENABLED: u32 = 1; #[allow(unused)] const VIRTIO_GPU_FLAG_STATS_ENABLED: u32 = 2; const VIRTIO_GPU_FLAG_EDID_ENABLED: u32 = 3; + +/// Features which virtio gpu cmd can support +const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; + /// flag used to distinguish the cmd type and format VirtioGpuRequest const VIRTIO_GPU_CMD_CTRL: u32 = 0; const VIRTIO_GPU_CMD_CURSOR: u32 = 1; + +/// virtio_gpu_ctrl_type: 2d commands. +const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; +const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101; +const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102; +const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103; +const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104; +const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105; +const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106; +const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107; +const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x0108; +const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x0109; +const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x010a; /// virtio_gpu_ctrl_type: cursor commands. const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300; const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301; +/// virtio_gpu_ctrl_type: success responses. +const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100; +const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101; +const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104; +/// virtio_gpu_ctrl_type: error responses. +const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200; +const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205; #[derive(Clone, Copy, Debug, ByteCode)] pub struct VirtioGpuConfig { @@ -123,8 +147,19 @@ pub struct VirtioGpuCtrlHdr { impl VirtioGpuCtrlHdr { fn is_valid(&self) -> bool { match self.hdr_type { - VIRTIO_GPU_CMD_UPDATE_CURSOR => true, - VIRTIO_GPU_CMD_MOVE_CURSOR => true, + VIRTIO_GPU_CMD_UPDATE_CURSOR + | VIRTIO_GPU_CMD_MOVE_CURSOR + | VIRTIO_GPU_CMD_GET_DISPLAY_INFO + | VIRTIO_GPU_CMD_RESOURCE_CREATE_2D + | VIRTIO_GPU_CMD_RESOURCE_UNREF + | VIRTIO_GPU_CMD_SET_SCANOUT + | VIRTIO_GPU_CMD_RESOURCE_FLUSH + | VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D + | VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING + | VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING + | VIRTIO_GPU_CMD_GET_CAPSET_INFO + | VIRTIO_GPU_CMD_GET_CAPSET + | VIRTIO_GPU_CMD_GET_EDID => true, _ => { error!("request type {} is not supported for GPU", self.hdr_type); false @@ -135,6 +170,65 @@ impl VirtioGpuCtrlHdr { impl ByteCode for VirtioGpuCtrlHdr {} +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuRect { + x_coord: u32, + y_coord: u32, + width: u32, + height: u32, +} + +impl ByteCode for VirtioGpuRect {} + +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuDisplayOne { + rect: VirtioGpuRect, + enabled: u32, + flags: u32, +} + +impl ByteCode for VirtioGpuDisplayOne {} + +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuDisplayInfo { + header: VirtioGpuCtrlHdr, + pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS], +} +impl ByteCode for VirtioGpuDisplayInfo {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuGetEdid { + header: VirtioGpuCtrlHdr, + scanouts: u32, + padding: u32, +} +impl ByteCode for VirtioGpuGetEdid {} + +#[allow(unused)] +// data which transfer to frontend need padding +#[derive(Clone, Copy)] +pub struct VirtioGpuRespEdid { + header: VirtioGpuCtrlHdr, + size: u32, + padding: u32, + edid: [u8; 1024], +} + +impl Default for VirtioGpuRespEdid { + fn default() -> Self { + VirtioGpuRespEdid { + header: VirtioGpuCtrlHdr::default(), + size: 0, + padding: 0, + edid: [0; 1024], + } + } +} + +impl ByteCode for VirtioGpuRespEdid {} + #[allow(unused)] #[derive(Default, Clone)] pub struct VirtioGpuRequest { @@ -376,25 +470,278 @@ impl GpuIoHandler { Ok(()) } + fn gpu_response_nodata( + &mut self, + need_interrupt: &mut bool, + resp_head_type: u32, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + let mut resp = VirtioGpuCtrlHdr { + hdr_type: resp_head_type, + ..Default::default() + }; + + if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { + resp.flags |= VIRTIO_GPU_FLAG_FENCE; + resp.fence_id = req.header.fence_id; + resp.ctx_id = req.header.ctx_id; + } + + self.mem_space + .write_object(&resp, req.in_header) + .chain_err(|| "Fail to write nodata response")?; + self.ctrl_queue + .lock() + .unwrap() + .vring + .add_used( + &self.mem_space, + req.index, + size_of::() as u32, + ) + .chain_err(|| "Fail to add used elem for control queue")?; + + Ok(()) + } + + fn gpu_cmd_get_display_info( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + let mut display_info = VirtioGpuDisplayInfo::default(); + display_info.header.hdr_type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO; + for i in 0..self.base_conf.max_outputs { + if (self.enable_output_bitmask & (1 << i)) != 0 { + let i = i as usize; + display_info.pmodes[i].enabled = 1; + display_info.pmodes[i].rect.width = self.req_states[i].width; + display_info.pmodes[i].rect.height = self.req_states[i].height; + display_info.pmodes[i].flags = 0; + } + } + + if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { + display_info.header.flags |= VIRTIO_GPU_FLAG_FENCE; + display_info.header.fence_id = req.header.fence_id; + display_info.header.ctx_id = req.header.ctx_id; + } + self.mem_space + .write_object(&display_info, req.in_header) + .chain_err(|| "Fail to write displayinfo")?; + self.ctrl_queue + .lock() + .unwrap() + .vring + .add_used( + &self.mem_space, + req.index, + size_of::() as u32, + ) + .chain_err(|| "Fail to add used elem for control queue")?; + + Ok(()) + } + + fn gpu_cmd_get_edid( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + let mut edid_resp = VirtioGpuRespEdid::default(); + edid_resp.header.hdr_type = VIRTIO_GPU_RESP_OK_EDID; + + let edid_req = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr("the GPU's edid request header", req.out_header.0) + })?; + + if edid_req.scanouts >= self.base_conf.max_outputs { + error!( + "The scanouts {} of request exceeds the max_outputs {}", + edid_req.scanouts, self.base_conf.max_outputs + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + req, + ); + } + + let mut edid_info = EdidInfo::new( + "HWV", + "STRA Monitor", + 100, + self.req_states[edid_req.scanouts as usize].width, + self.req_states[edid_req.scanouts as usize].height, + ); + edid_info.edid_array_fulfill(&mut edid_resp.edid.to_vec()); + edid_resp.size = edid_resp.edid.len() as u32; + + if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { + edid_resp.header.flags |= VIRTIO_GPU_FLAG_FENCE; + edid_resp.header.fence_id = req.header.fence_id; + edid_resp.header.ctx_id = req.header.ctx_id; + } + self.mem_space + .write_object(&edid_resp, req.in_header) + .chain_err(|| "Fail to write displayinfo")?; + self.ctrl_queue + .lock() + .unwrap() + .vring + .add_used( + &self.mem_space, + req.index, + size_of::() as u32, + ) + .chain_err(|| "Fail to add used elem for control queue")?; + + Ok(()) + } + + fn gpu_cmd_resource_create_2d( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + + fn gpu_cmd_resource_unref( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + + fn gpu_cmd_set_scanout( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + + fn gpu_cmd_resource_flush( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + + fn gpu_cmd_transfer_to_host_2d( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + fn gpu_cmd_resource_attach_backing( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + fn gpu_cmd_resource_detach_backing( + &mut self, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + *need_interrupt = true; + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } + fn process_control_queue(&mut self, mut req_queue: Vec) -> Result<()> { + for req in req_queue.iter_mut() { + let mut need_interrupt = true; + + if let Err(e) = match req.header.hdr_type { + VIRTIO_GPU_CMD_GET_DISPLAY_INFO => self + .gpu_cmd_get_display_info(&mut need_interrupt, req) + .chain_err(|| "Fail to get display info"), + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => self + .gpu_cmd_resource_create_2d(&mut need_interrupt, req) + .chain_err(|| "Fail to create 2d resource"), + VIRTIO_GPU_CMD_RESOURCE_UNREF => self + .gpu_cmd_resource_unref(&mut need_interrupt, req) + .chain_err(|| "Fail to unref resource"), + VIRTIO_GPU_CMD_SET_SCANOUT => self + .gpu_cmd_set_scanout(&mut need_interrupt, req) + .chain_err(|| "Fail to set scanout"), + VIRTIO_GPU_CMD_RESOURCE_FLUSH => self + .gpu_cmd_resource_flush(&mut need_interrupt, req) + .chain_err(|| "Fail to flush resource"), + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self + .gpu_cmd_transfer_to_host_2d(&mut need_interrupt, req) + .chain_err(|| "Fail to transfer fo host 2d resource"), + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self + .gpu_cmd_resource_attach_backing(&mut need_interrupt, req) + .chain_err(|| "Fail to attach backing"), + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self + .gpu_cmd_resource_detach_backing(&mut need_interrupt, req) + .chain_err(|| "Fail to detach backing"), + VIRTIO_GPU_CMD_GET_EDID => self + .gpu_cmd_get_edid(&mut need_interrupt, req) + .chain_err(|| "Fail to get edid info"), + _ => self + .gpu_response_nodata(&mut need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req) + .chain_err(|| "Fail to get nodata response"), + } { + error!("Fail to handle GPU request, {}", e); + } + + if need_interrupt { + (self.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&self.ctrl_queue.lock().unwrap()), + ) + .chain_err(|| ErrorKind::InterruptTrigger("gpu", VirtioInterruptType::Vring))?; + } + } + + Ok(()) + } + fn ctrl_queue_evt_handler(&mut self) -> Result<()> { let mut queue = self.ctrl_queue.lock().unwrap(); if !queue.is_valid(&self.mem_space) { bail!("Failed to handle any request, the queue is not ready"); } + + let mut req_queue = Vec::new(); while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .chain_err(|| "Failed to add used ring")?; + match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CTRL) { + Ok(req) => { + req_queue.push(req); + } + Err(e) => { + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .chain_err(|| "Failed to add used ring")?; + error!( + "failed to create GPU request, {}", + error_chain::ChainedError::display_chain(&e) + ); + } + } } drop(queue); - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.cursor_queue.lock().unwrap()), - ) - .chain_err(|| ErrorKind::InterruptTrigger("gpu", VirtioInterruptType::Vring))?; - - Ok(()) + self.process_control_queue(req_queue) } fn cursor_queue_evt_handler(&mut self) -> Result<()> { -- Gitee From 6d3d1fa7790c20360038605ec891e27917844d7d Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 22 Aug 2022 00:50:48 +0800 Subject: [PATCH 0139/2187] virtio-gpu: support all 2D ctrl event support all 2d commands --- virtio/src/gpu.rs | 783 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 770 insertions(+), 13 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 201942de0..76edef078 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -34,11 +34,18 @@ use util::loop_context::{ }; use util::num_ops::{read_u32, write_u32}; use util::pixman::{ - pixman_image_get_data, pixman_image_get_height, pixman_image_get_width, pixman_image_t, + pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits, pixman_image_get_data, + pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, + pixman_image_get_width, pixman_image_ref, pixman_image_set_destroy_function, pixman_image_t, + pixman_image_unref, pixman_region16_t, pixman_region_extents, pixman_region_fini, + pixman_region_init, pixman_region_init_rect, pixman_region_intersect, pixman_region_translate, + virtio_gpu_unref_resource_callback, }; use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use vnc::{vnc_display_cursor, DisplayMouse, DisplaySurface}; +use vnc::{ + vnc_display_cursor, vnc_display_switch, vnc_display_update, DisplayMouse, DisplaySurface, +}; /// Number of virtqueues. const QUEUE_NUM_GPU: usize = 2; @@ -79,8 +86,21 @@ const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101; const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104; /// virtio_gpu_ctrl_type: error responses. const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200; +const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201; +const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202; +const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203; const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205; +/// simple formats for fbcon/X use +const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; +const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; +const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3; +const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4; +const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; +const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; +const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; +const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; + #[derive(Clone, Copy, Debug, ByteCode)] pub struct VirtioGpuConfig { events_read: u32, @@ -97,7 +117,6 @@ pub struct VirtioGpuBaseConf { yres: u32, } -#[allow(unused)] #[derive(Debug)] struct GpuResource { resource_id: u32, @@ -229,6 +248,92 @@ impl Default for VirtioGpuRespEdid { impl ByteCode for VirtioGpuRespEdid {} +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceCreate2d { + header: VirtioGpuCtrlHdr, + resource_id: u32, + format: u32, + width: u32, + height: u32, +} + +impl ByteCode for VirtioGpuResourceCreate2d {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceUnref { + header: VirtioGpuCtrlHdr, + resource_id: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuResourceUnref {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuSetScanout { + header: VirtioGpuCtrlHdr, + rect: VirtioGpuRect, + scanout_id: u32, + resource_id: u32, +} + +impl ByteCode for VirtioGpuSetScanout {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceFlush { + header: VirtioGpuCtrlHdr, + rect: VirtioGpuRect, + resource_id: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuResourceFlush {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuTransferToHost2d { + header: VirtioGpuCtrlHdr, + rect: VirtioGpuRect, + offset: u64, + resource_id: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuTransferToHost2d {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceAttachBacking { + header: VirtioGpuCtrlHdr, + resource_id: u32, + nr_entries: u32, +} + +impl ByteCode for VirtioGpuResourceAttachBacking {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuMemEntry { + addr: u64, + length: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuMemEntry {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceDetachBacking { + header: VirtioGpuCtrlHdr, + resource_id: u32, + padding: u32, +} + +impl ByteCode for VirtioGpuResourceDetachBacking {} + #[allow(unused)] #[derive(Default, Clone)] pub struct VirtioGpuRequest { @@ -346,8 +451,7 @@ pub struct VirtioGpuUpdateCursor { impl ByteCode for VirtioGpuUpdateCursor {} -#[allow(unused)] -#[derive(Clone, Default)] +#[derive(Default)] struct GpuScanout { surface: Option, mouse: Option, @@ -355,12 +459,10 @@ struct GpuScanout { height: u32, x: u32, y: u32, - invalidate: i32, resource_id: u32, cursor: VirtioGpuUpdateCursor, } -#[allow(unused)] impl GpuScanout { fn clear(&mut self) { self.resource_id = 0; @@ -371,7 +473,6 @@ impl GpuScanout { } /// Control block of GPU IO. -#[allow(unused)] struct GpuIoHandler { /// The virtqueue for for sending control commands. ctrl_queue: Arc>, @@ -411,7 +512,86 @@ fn display_define_mouse(mouse: &mut Option) { } } +fn display_replace_surface(surface: Option) { + if let Some(mut surface) = surface { + vnc_display_switch(&mut surface); + } else { + println!("Surface is None, waiting complete the code!"); + } +} + +fn display_update(x: i32, y: i32, w: i32, h: i32) { + vnc_display_update(x, y, w, h); +} + +fn create_surface( + scanout: &mut GpuScanout, + info_set_scanout: VirtioGpuSetScanout, + res: &GpuResource, + pixman_format: pixman_format_code_t, + pixman_stride: libc::c_int, + res_data_offset: *mut u32, +) -> DisplaySurface { + let mut surface = DisplaySurface::default(); + unsafe { + let rect = pixman_image_create_bits( + pixman_format, + info_set_scanout.rect.width as i32, + info_set_scanout.rect.height as i32, + res_data_offset, + pixman_stride, + ); + pixman_image_ref(res.pixman_image); + pixman_image_set_destroy_function( + rect, + Some(virtio_gpu_unref_resource_callback), + res.pixman_image.cast(), + ); + surface.format = pixman_image_get_format(rect); + surface.image = pixman_image_ref(rect); + if !surface.image.is_null() { + // update surface in scanout. + scanout.surface = Some(surface); + pixman_image_unref(rect); + display_replace_surface(scanout.surface); + } + }; + surface +} + impl GpuIoHandler { + fn gpu_get_pixman_format(&mut self, format: u32) -> Result { + match format { + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8r8g8b8), + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8r8g8b8), + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM => Ok(pixman_format_code_t::PIXMAN_b8g8r8a8), + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM => Ok(pixman_format_code_t::PIXMAN_b8g8r8x8), + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8b8g8r8), + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8x8), + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8a8), + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8b8g8r8), + _ => { + bail!("Unsupport pixman format") + } + } + } + + fn gpu_get_image_hostmem( + &mut self, + format: pixman_format_code_t, + width: u32, + height: u32, + ) -> u64 { + let bpp = pixman_format_bpp(format as u32); + let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); + height as u64 * stride + } + + fn gpu_clear_resource_iovs(&mut self, res_index: usize) { + let res = &mut self.resources_list[res_index]; + res.iov.clear(); + } + fn gpu_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { let info_cursor = self .mem_space @@ -611,15 +791,139 @@ impl GpuIoHandler { req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; + let info_create_2d = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's resource create 2d request header", + req.out_header.0, + ) + })?; + if info_create_2d.resource_id == 0 { + error!("The 0 value for resource_id is illegal"); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + req, + ); + } + + if let Some(res) = self + .resources_list + .iter() + .find(|&x| x.resource_id == info_create_2d.resource_id) + { + error!("The resource_id {} is already existed", res.resource_id); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + req, + ); + } + + let mut res = GpuResource { + width: info_create_2d.width, + height: info_create_2d.height, + format: info_create_2d.format, + resource_id: info_create_2d.resource_id, + ..Default::default() + }; + let pixman_format = self + .gpu_get_pixman_format(res.format) + .chain_err(|| "Fail to parse guest format")?; + res.host_mem = + self.gpu_get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); + if res.host_mem + self.used_hostmem < self.max_hostmem { + res.pixman_image = unsafe { + pixman_image_create_bits( + pixman_format, + info_create_2d.width as i32, + info_create_2d.height as i32, + ptr::null_mut(), + 0, + ) + } + } + + if res.pixman_image.is_null() { + error!( + "Fail to create resource(id {}, width {}, height {}) on host", + res.resource_id, res.width, res.height + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + req, + ); + } + + self.used_hostmem += res.host_mem; + self.resources_list.push(res); self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) } + fn gpu_destroy_resoure( + &mut self, + res_id: u32, + need_interrupt: &mut bool, + req: &VirtioGpuRequest, + ) -> Result<()> { + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == res_id) + { + let res = &mut self.resources_list[res_index]; + if res.scanouts_bitmask != 0 { + for i in 0..self.base_conf.max_outputs { + if (res.scanouts_bitmask & (1 << i)) != 0 { + let scanout = &mut self.scanouts[i as usize]; + if scanout.resource_id != 0 { + // disable the scanout. + res.scanouts_bitmask &= !(1 << i); + display_replace_surface(None); + scanout.clear(); + } + } + } + } + unsafe { + pixman_image_unref(res.pixman_image); + } + self.used_hostmem -= res.host_mem; + self.gpu_clear_resource_iovs(res_index); + self.resources_list.remove(res_index); + } else { + error!("The resource_id {} is not existed", res_id); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + req, + ); + } + + Ok(()) + } + fn gpu_cmd_resource_unref( &mut self, need_interrupt: &mut bool, req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; + let info_resource_unref = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's resource unref request header", + req.out_header.0, + ) + })?; + + self.gpu_destroy_resoure(info_resource_unref.resource_id, need_interrupt, req) + .chain_err(|| "Fail to unref guest resource")?; self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) } @@ -629,7 +933,154 @@ impl GpuIoHandler { req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + let info_set_scanout = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr("the GPU's set scanout request header", req.out_header.0) + })?; + + if info_set_scanout.scanout_id >= self.base_conf.max_outputs { + error!( + "The scanout id {} is out of range", + info_set_scanout.scanout_id + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + req, + ); + } + + // TODO refactor to disable function + let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; + if info_set_scanout.resource_id == 0 { + // set resource_id to 0 means disable the scanout. + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == scanout.resource_id) + { + let res = &mut self.resources_list[res_index]; + res.scanouts_bitmask &= !(1 << info_set_scanout.scanout_id); + } + display_replace_surface(None); + scanout.clear(); + return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req); + } + + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == info_set_scanout.resource_id) + { + let res = &self.resources_list[res_index]; + if info_set_scanout.rect.x_coord > res.width + || info_set_scanout.rect.y_coord > res.height + || info_set_scanout.rect.width > res.width + || info_set_scanout.rect.height > res.height + || info_set_scanout.rect.width < 16 + || info_set_scanout.rect.height < 16 + || info_set_scanout.rect.width + info_set_scanout.rect.x_coord > res.width + || info_set_scanout.rect.height + info_set_scanout.rect.y_coord > res.height + { + error!( + "The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {})", + res.resource_id, + res.width, + res.height, + info_set_scanout.scanout_id, + info_set_scanout.rect.width, + info_set_scanout.rect.height, + info_set_scanout.rect.x_coord, + info_set_scanout.rect.y_coord, + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + req, + ); + } + + let pixman_format = unsafe { pixman_image_get_format(res.pixman_image) }; + let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + let pixman_stride = unsafe { pixman_image_get_stride(res.pixman_image) }; + let offset = info_set_scanout.rect.x_coord * bpp + + info_set_scanout.rect.y_coord * pixman_stride as u32; + let res_data = unsafe { pixman_image_get_data(res.pixman_image) }; + let res_data_offset = unsafe { res_data.offset(offset as isize) }; + + match scanout.surface { + None => { + if create_surface( + scanout, + info_set_scanout, + res, + pixman_format, + pixman_stride, + res_data_offset, + ) + .image + .is_null() + { + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_UNSPEC, + req, + ); + } + } + Some(sur) => { + let scanout_data = unsafe { pixman_image_get_data(sur.image) }; + if (res_data_offset != scanout_data + || scanout.width != info_set_scanout.rect.width + || scanout.height != info_set_scanout.rect.height) + && create_surface( + scanout, + info_set_scanout, + res, + pixman_format, + pixman_stride, + res_data_offset, + ) + .image + .is_null() + { + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_UNSPEC, + req, + ); + } + } + } + + if let Some(old_res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == scanout.resource_id) + { + // Update old resource scanout bitmask. + self.resources_list[old_res_index].scanouts_bitmask &= + !(1 << info_set_scanout.scanout_id); + } + // Update new resource scanout bitmask. + self.resources_list[res_index].scanouts_bitmask |= 1 << info_set_scanout.scanout_id; + // Update scanout configure. + scanout.resource_id = info_set_scanout.resource_id; + scanout.x = info_set_scanout.rect.x_coord; + scanout.y = info_set_scanout.rect.y_coord; + scanout.width = info_set_scanout.rect.width; + scanout.height = info_set_scanout.rect.height; + + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } else { + error!( + "The resource_id {} in set_scanout {} request is not existed", + info_set_scanout.resource_id, info_set_scanout.scanout_id + ); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + } } fn gpu_cmd_resource_flush( @@ -638,7 +1089,99 @@ impl GpuIoHandler { req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + let info_res_flush = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's resource flush request header", + req.out_header.0, + ) + })?; + + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == info_res_flush.resource_id) + { + let res = &self.resources_list[res_index]; + if info_res_flush.rect.x_coord > res.width + || info_res_flush.rect.y_coord > res.height + || info_res_flush.rect.width > res.width + || info_res_flush.rect.height > res.height + || info_res_flush.rect.width + info_res_flush.rect.x_coord > res.width + || info_res_flush.rect.height + info_res_flush.rect.y_coord > res.height + { + error!( + "The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {})", + res.resource_id, res.width, res.height, + info_res_flush.rect.width, info_res_flush.rect.height, + info_res_flush.rect.x_coord, info_res_flush.rect.y_coord, + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + req, + ); + } + + unsafe { + let mut flush_reg = pixman_region16_t::default(); + let flush_reg_ptr: *mut pixman_region16_t = + &mut flush_reg as *mut pixman_region16_t; + pixman_region_init_rect( + flush_reg_ptr, + info_res_flush.rect.x_coord as i32, + info_res_flush.rect.y_coord as i32, + info_res_flush.rect.width, + info_res_flush.rect.height, + ); + for i in 0..self.base_conf.max_outputs { + // Flushes any scanouts the resource is being used on. + if res.scanouts_bitmask & (1 << i) != 0 { + let scanout = &self.scanouts[i as usize]; + let mut rect_reg = pixman_region16_t::default(); + let mut final_reg = pixman_region16_t::default(); + let rect_reg_ptr: *mut pixman_region16_t = + &mut rect_reg as *mut pixman_region16_t; + let final_reg_ptr: *mut pixman_region16_t = + &mut final_reg as *mut pixman_region16_t; + + pixman_region_init(final_reg_ptr); + pixman_region_init_rect( + rect_reg_ptr, + scanout.x as i32, + scanout.y as i32, + scanout.width, + scanout.height, + ); + pixman_region_intersect(final_reg_ptr, flush_reg_ptr, rect_reg_ptr); + pixman_region_translate( + final_reg_ptr, + -(scanout.x as i32), + -(scanout.y as i32), + ); + let extents = pixman_region_extents(final_reg_ptr); + display_update( + (*extents).x1 as i32, + (*extents).y1 as i32, + ((*extents).x2 - (*extents).x1) as i32, + ((*extents).y2 - (*extents).y1) as i32, + ); + pixman_region_fini(rect_reg_ptr); + pixman_region_fini(final_reg_ptr); + } + } + pixman_region_fini(flush_reg_ptr); + } + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } else { + error!( + "The resource_id {} in resource flush request is not existed", + info_res_flush.resource_id + ); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + } } fn gpu_cmd_transfer_to_host_2d( @@ -647,7 +1190,111 @@ impl GpuIoHandler { req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + let info_transfer = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's transfer to host 2d request header", + req.out_header.0, + ) + })?; + + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == info_transfer.resource_id) + { + let res = &self.resources_list[res_index]; + if res.iov.is_empty() { + error!( + "The resource_id {} in transfer to host 2d request don't have iov", + info_transfer.resource_id + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + req, + ); + } + + if info_transfer.rect.x_coord > res.width + || info_transfer.rect.y_coord > res.height + || info_transfer.rect.width > res.width + || info_transfer.rect.height > res.height + || info_transfer.rect.width + info_transfer.rect.x_coord > res.width + || info_transfer.rect.height + info_transfer.rect.y_coord > res.height + { + error!( + "The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {})", + res.resource_id, + res.width, + res.height, + info_transfer.offset, + info_transfer.rect.width, + info_transfer.rect.height, + info_transfer.rect.x_coord, + info_transfer.rect.y_coord, + ); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + req, + ); + } + + unsafe { + let pixman_format = pixman_image_get_format(res.pixman_image); + let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + let stride = pixman_image_get_stride(res.pixman_image); + let height = pixman_image_get_height(res.pixman_image); + let width = pixman_image_get_width(res.pixman_image); + let data = pixman_image_get_data(res.pixman_image); + let data_cast: *mut u8 = data.cast(); + + let mut iovec_buf = Vec::new(); + for item in res.iov.iter() { + let mut dst = Vec::new(); + let src = std::slice::from_raw_parts( + item.iov_base as *const u8, + item.iov_len as usize, + ); + dst.resize(item.iov_len as usize, 0); + dst[0..item.iov_len as usize].copy_from_slice(src); + iovec_buf.append(&mut dst); + } + + if info_transfer.offset != 0 + || info_transfer.rect.x_coord != 0 + || info_transfer.rect.y_coord != 0 + || info_transfer.rect.width != width as u32 + { + for h in 0..info_transfer.rect.height { + let offset_iov = info_transfer.offset as u32 + stride as u32 * h; + let offset_data = (info_transfer.rect.y_coord + h) * stride as u32 + + (info_transfer.rect.x_coord * bpp); + ptr::copy( + iovec_buf.as_ptr().offset(offset_iov as isize), + data_cast.offset(offset_data as isize), + info_transfer.rect.width as usize * bpp as usize, + ); + } + } else { + ptr::copy( + iovec_buf.as_ptr(), + data_cast, + stride as usize * height as usize, + ); + } + } + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } else { + error!( + "The resource_id {} in transfer to host 2d request is not existed", + info_transfer.resource_id + ); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + } } fn gpu_cmd_resource_attach_backing( &mut self, @@ -655,7 +1302,86 @@ impl GpuIoHandler { req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + let info_attach_backing = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's resource attach backing request header", + req.out_header.0, + ) + })?; + + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == info_attach_backing.resource_id) + { + let res = &mut self.resources_list[res_index]; + if !res.iov.is_empty() { + error!( + "The resource_id {} in resource attach backing request allready has iov", + info_attach_backing.resource_id + ); + return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } + + if info_attach_backing.nr_entries > 16384 { + error!( + "The nr_entries in resource attach backing request is too large ( {} > 16384)", + info_attach_backing.nr_entries + ); + return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } + + let mut base_entry = req.out_iovec.get(0).unwrap(); + let mut offset = size_of::() as u64; + let esize = + size_of::() as u64 * info_attach_backing.nr_entries as u64; + if base_entry.iov_len < offset + esize { + if req.out_iovec.len() <= 1 || req.out_iovec.get(1).unwrap().iov_len < esize { + error!("The entries is not setted"); + return self.gpu_response_nodata( + need_interrupt, + VIRTIO_GPU_RESP_ERR_UNSPEC, + req, + ); + } + base_entry = req.out_iovec.get(1).unwrap(); + offset = 0; + } + + for i in 0..info_attach_backing.nr_entries { + let entry_addr = + base_entry.iov_base + offset + i as u64 * size_of::() as u64; + let info_gpu_mem_entry = self + .mem_space + .read_object::(GuestAddress(entry_addr)) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's resource attach backing request header", + req.out_header.0, + ) + })?; + let iov_base = self + .mem_space + .get_host_address(GuestAddress(info_gpu_mem_entry.addr)) + .chain_err(|| "Fail to get gpu mem entry host addr")?; + let iov_item = Iovec { + iov_base, + iov_len: info_gpu_mem_entry.length as u64, + }; + res.iov.push(iov_item); + } + + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } else { + error!( + "The resource_id {} in attach backing request request is not existed", + info_attach_backing.resource_id + ); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + } } fn gpu_cmd_resource_detach_backing( &mut self, @@ -663,7 +1389,38 @@ impl GpuIoHandler { req: &VirtioGpuRequest, ) -> Result<()> { *need_interrupt = true; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + let info_detach_backing = self + .mem_space + .read_object::(req.out_header) + .chain_err(|| { + ErrorKind::ReadObjectErr( + "the GPU's resource detach backing request header", + req.out_header.0, + ) + })?; + + if let Some(res_index) = self + .resources_list + .iter() + .position(|x| x.resource_id == info_detach_backing.resource_id) + { + let res = &mut self.resources_list[res_index]; + if res.iov.is_empty() { + error!( + "The resource_id {} in resource detach backing request don't have iov", + info_detach_backing.resource_id + ); + return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } + self.gpu_clear_resource_iovs(res_index); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + } else { + error!( + "The resource_id {} in detach backing request request is not existed", + info_detach_backing.resource_id + ); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + } } fn process_control_queue(&mut self, mut req_queue: Vec) -> Result<()> { for req in req_queue.iter_mut() { -- Gitee From f878bad62d57bcdcd1bc70e8433cb55022b7037b Mon Sep 17 00:00:00 2001 From: Zhang Yang Date: Fri, 19 Aug 2022 10:47:45 +0800 Subject: [PATCH 0140/2187] docs: add constraint about vfio Add explanation about constraint of vfio devices. Signed-off-by: Zhang Yang --- docs/config_guidebook.md | 2 ++ docs/vfio.md | 1 + 2 files changed, 3 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2975fc15d..0128bdccb 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -515,6 +515,8 @@ of device and the second one represents function number of it. -device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,id=balloon-0[,deflate-on-oom=true|false][,free-page-reporting=true|false][,multifunction=on|off] ``` +Note: avoid using balloon devices and vfio devices together. + ### 2.8 Virtio-rng Virtio rng is a paravirtualized random number generator device, it provides a hardware rng device to the guest. diff --git a/docs/vfio.md b/docs/vfio.md index 0e55c6521..227ca7159 100644 --- a/docs/vfio.md +++ b/docs/vfio.md @@ -49,6 +49,7 @@ Four properties are supported for VFIO device -device vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x03.0x0[,multifunction=on] ``` Note: the kernel must contain physical device drivers, otherwise it cannot be loaded normally. +Note: avoid using balloon devices and vfio devices together. ## Hot plug management -- Gitee From 993086f421bd62b8c581d98c272defddf324b138 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 24 Aug 2022 12:54:32 +0800 Subject: [PATCH 0141/2187] rust: rm unnecessary pub visibility prefix for functions Some private functions are marked as "pub", which is not recommended by rust. Such coding behavior could not be recognized by "cargo build", use shell cmd for assist: ''' for fun in `grep "^pub fn" . -r |awk:' '{print $1}' |sort |uniq |wc -l`; if test $num -eq 1; then echo $fun; fi; done ''' within the results, filter the macros and vnc/gpu related unused-yet functions(as they maybe used later, the desktop-vm feature is still in progress). --- boot_loader/src/x86_64/direct_boot/mod.rs | 4 ++-- machine_manager/src/config/drive.rs | 2 +- machine_manager/src/config/machine_config.rs | 2 +- machine_manager/src/config/network.rs | 2 +- ozone/src/capability.rs | 2 +- pci/src/lib.rs | 6 +++--- usb/src/xhci/xhci_controller.rs | 18 +++--------------- util/src/device_tree.rs | 7 ------- util/src/logger.rs | 2 +- util/src/pixman.rs | 2 +- virtio/src/net.rs | 2 +- virtio/src/vhost/user/client.rs | 2 +- vnc/src/vnc.rs | 6 +----- 13 files changed, 17 insertions(+), 40 deletions(-) diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 1435f7235..ae6241a09 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -52,7 +52,7 @@ use crate::errors::{ErrorKind, Result, ResultExt}; /// /// * Invalid BzImage header or version. /// * Failed to write bzImage linux kernel to guest memory. -pub fn load_bzimage(kernel_image: &mut File) -> Result { +fn load_bzimage(kernel_image: &mut File) -> Result { let mut boot_hdr = RealModeKernelHeader::new(); kernel_image.seek(SeekFrom::Start(BOOT_HDR_START))?; @@ -196,7 +196,7 @@ fn setup_boot_params( Ok(()) } -pub fn setup_kernel_cmdline( +fn setup_kernel_cmdline( config: &X86BootLoaderConfig, sys_mem: &Arc, boot_hdr: &mut RealModeKernelHeader, diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 9850ba293..ef15b7607 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -212,7 +212,7 @@ impl ConfigCheck for BlkDevConfig { } } -pub fn parse_drive(cmd_parser: CmdParser) -> Result { +fn parse_drive(cmd_parser: CmdParser) -> Result { let mut drive = DriveConfig::default(); if let Some(format) = cmd_parser.get_value::("format")? { diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 102863659..5a9c015e9 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -499,7 +499,7 @@ fn adjust_topology( /// # Arguments /// /// * `origin_value` - The origin memory value from user. -pub fn memory_unit_conversion(origin_value: &str) -> Result { +fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) { diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 883d1f47d..51608f10a 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -178,7 +178,7 @@ fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result>> { } } -pub fn parse_netdev(cmd_parser: CmdParser) -> Result { +fn parse_netdev(cmd_parser: CmdParser) -> Result { let mut net = NetDevcfg::default(); let netdev_type = if let Some(netdev_type) = cmd_parser.get_value::("")? { netdev_type diff --git a/ozone/src/capability.rs b/ozone/src/capability.rs index 356748d47..ced67b062 100644 --- a/ozone/src/capability.rs +++ b/ozone/src/capability.rs @@ -90,7 +90,7 @@ pub struct CapUserData { inheritable_s1: u32, } -pub fn has_cap(cap: u8) -> Result { +fn has_cap(cap: u8) -> Result { let mut hdr = CapUserHeader { version: CAPS_V3, pid: 0, diff --git a/pci/src/lib.rs b/pci/src/lib.rs index ddb8a395e..bd5d252b4 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -120,15 +120,15 @@ fn le_write_clear_value_u16(buf: &mut [u8], offset: usize, data: u16) -> Result< le_write_u16(buf, offset, val & !data) } -pub fn pci_devfn(slot: u8, func: u8) -> u8 { +fn pci_devfn(slot: u8, func: u8) -> u8 { ((slot & 0x1f) << 3) | (func & 0x07) } -pub fn pci_slot(devfn: u8) -> u8 { +fn pci_slot(devfn: u8) -> u8 { devfn >> 3 & 0x1f } -pub fn pci_func(devfn: u8) -> u8 { +fn pci_func(devfn: u8) -> u8 { devfn & 0x07 } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index a8db05159..61fa976ec 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1527,22 +1527,14 @@ pub fn dma_write_bytes( Ok(()) } -pub fn dma_read_u64( - addr_space: &Arc, - addr: GuestAddress, - data: &mut u64, -) -> Result<()> { +fn dma_read_u64(addr_space: &Arc, addr: GuestAddress, data: &mut u64) -> Result<()> { let mut tmp = [0_u8; 8]; dma_read_bytes(addr_space, addr, &mut tmp, 8)?; *data = LittleEndian::read_u64(&tmp); Ok(()) } -pub fn dma_read_u32( - addr_space: &Arc, - addr: GuestAddress, - buf: &mut [u32], -) -> Result<()> { +fn dma_read_u32(addr_space: &Arc, addr: GuestAddress, buf: &mut [u32]) -> Result<()> { let vec_len = size_of::() * buf.len(); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); @@ -1553,11 +1545,7 @@ pub fn dma_read_u32( Ok(()) } -pub fn dma_write_u32( - addr_space: &Arc, - addr: GuestAddress, - buf: &[u32], -) -> Result<()> { +fn dma_write_u32(addr_space: &Arc, addr: GuestAddress, buf: &[u32]) -> Result<()> { let vec_len = size_of::() * buf.len(); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index d47b7f5e7..3b5578a3f 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -292,13 +292,6 @@ pub trait CompileFDT { fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> Result<()>; } -pub fn dump_dtb(fdt: &[u8], file_path: &str) { - use std::fs::File; - use std::io::Write; - let mut f = File::create(file_path).unwrap(); - f.write_all(fdt).expect("Unable to write data"); -} - #[cfg(test)] mod tests { use super::*; diff --git a/util/src/logger.rs b/util/src/logger.rs index ae562cb72..b653b77e3 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -82,7 +82,7 @@ impl Log for VmLogger { fn flush(&self) {} } -pub fn init_vm_logger( +fn init_vm_logger( level: Option, logfile: Option>, ) -> Result<(), log::SetLoggerError> { diff --git a/util/src/pixman.rs b/util/src/pixman.rs index 8e3234bf5..d88b646e2 100644 --- a/util/src/pixman.rs +++ b/util/src/pixman.rs @@ -195,7 +195,7 @@ pub extern "C" fn virtio_gpu_unref_resource_callback( unsafe { pixman_image_unref(data.cast()) }; } -pub fn pixman_format_reshift(val: u32, ofs: u32, num: u32) -> u32 { +fn pixman_format_reshift(val: u32, ofs: u32, num: u32) -> u32 { ((val >> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3) } pub fn pixman_format_bpp(val: u32) -> u8 { diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 1631f5aab..1bb03b511 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -721,7 +721,7 @@ pub fn build_device_config_space(device_config: &mut VirtioNetConfig, mac: &str) /// /// * `dev_name` - The name of tap device on host. /// * `queue_pairs` - The number of virtio queue pairs. -pub fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> { +fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> { let path = format!("/sys/class/net/{}/tun_flags", dev_name); let tap_path = Path::new(&path); if !tap_path.exists() { diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 16be44937..17fe65bc9 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -75,7 +75,7 @@ impl ClientInternal { } } -pub fn vhost_user_reconnect(client: &Arc>) { +fn vhost_user_reconnect(client: &Arc>) { let cloned_client = client.clone(); let func = Box::new(move || { vhost_user_reconnect(&cloned_client); diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index a110c9fb2..88b6cca43 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -574,11 +574,7 @@ pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { } /// Send framebuf of mouse to the client. -pub fn display_cursor_define( - client: &mut VncClient, - cursor: &mut DisplayMouse, - mask: &mut Vec, -) { +fn display_cursor_define(client: &mut VncClient, cursor: &mut DisplayMouse, mask: &mut Vec) { let mut buf = Vec::new(); if client.has_feature(VncFeatures::VncFeatureAlphaCursor) { buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); -- Gitee From 9def08ff4ce39ab63095e9ac203c20a4b4de8499 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Wed, 24 Aug 2022 16:55:07 +0800 Subject: [PATCH 0142/2187] docs: resolve build error, use make to invoke cargo build Fix https://gitee.com/openeuler/stratovirt/issues/I5NWVG build error, use make to invoke cargo build and resolve dependency. Signed-off-by: Zhao Mengmeng --- Makefile | 7 +++++-- README.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c6016784c..86fe90507 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build -build: - cargo build +build: yum-deps + cargo build --release .PHONY: install install: @@ -9,3 +9,6 @@ install: .PHONY: clean clean: cargo clean + +yum-deps: + @yum install pixman-devel diff --git a/README.md b/README.md index 9778b6196..a41613ade 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ To build StratoVirt, clone the project and build it first: ```sh $ git clone https://gitee.com/openeuler/stratovirt.git $ cd stratovirt -$ cargo build --release +$ make build ``` Now you can find StratoVirt binary in `target/release/stratovirt`. -- Gitee From 52f857a034926fdd0d7cee035e80d0e75accfe57 Mon Sep 17 00:00:00 2001 From: YeXiao Date: Wed, 24 Aug 2022 10:01:00 +0800 Subject: [PATCH 0143/2187] Fix: Failed to init VNC server Fix a issue: virtual machine failed to start if VNC not configured. issue: https://gitee.com/openeuler/stratovirt/issues/I5NU6J Signed-off-by: Xiao Ye --- machine_manager/src/config/mod.rs | 2 +- machine_manager/src/config/vnc.rs | 19 +++++++++++-------- vnc/src/vnc.rs | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index bf1baa119..1fbd1b73b 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -178,7 +178,7 @@ pub struct VmConfig { pub global_config: HashMap, pub numa_nodes: Vec<(String, String)>, pub incoming: Option, - pub vnc: VncConfig, + pub vnc: Option, } impl VmConfig { diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index a9c22b045..85106670b 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -68,7 +68,7 @@ impl VmConfig { vnc_config.sasl_authz = sasl_authz; } - self.vnc = vnc_config; + self.vnc = Some(vnc_config); Ok(()) } } @@ -83,17 +83,19 @@ mod tests { vm_config .add_vnc("0.0.0.0:1,tls-creds=vnc-tls-creds0,sasl,sasl-authz=authz0") .unwrap(); - assert_eq!(vm_config.vnc.ip, String::from("0.0.0.0")); - assert_eq!(vm_config.vnc.port, String::from("1")); - assert_eq!(vm_config.vnc.tls_creds, String::from("vnc-tls-creds0")); - assert_eq!(vm_config.vnc.sasl, true); - assert_eq!(vm_config.vnc.sasl_authz, String::from("authz0")); + let vnc_config = vm_config.vnc.unwrap(); + assert_eq!(vnc_config.ip, String::from("0.0.0.0")); + assert_eq!(vnc_config.port, String::from("1")); + assert_eq!(vnc_config.tls_creds, String::from("vnc-tls-creds0")); + assert_eq!(vnc_config.sasl, true); + assert_eq!(vnc_config.sasl_authz, String::from("authz0")); let mut vm_config = VmConfig::default(); vm_config .add_vnc("0.0.0.0:1,tls-creds=vnc-tls-creds0") .unwrap(); - assert_eq!(vm_config.vnc.sasl, false); + let vnc_config = vm_config.vnc.unwrap(); + assert_eq!(vnc_config.sasl, false); let mut vm_config = VmConfig::default(); let res = vm_config.add_vnc("tls-creds=vnc-tls-creds0"); @@ -101,6 +103,7 @@ mod tests { let mut vm_config = VmConfig::default(); let _res = vm_config.add_vnc("0.0.0.0:1,sasl,sasl-authz=authz0"); - assert_eq!(vm_config.vnc.tls_creds, "".to_string()); + let vnc_config = vm_config.vnc.unwrap(); + assert_eq!(vnc_config.tls_creds, "".to_string()); } } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index a110c9fb2..5fff5b631 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -84,7 +84,14 @@ pub struct DisplayMouse { /// # Arguments /// /// * `VncConfig` `object`- vnc related parameters -pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Result<()> { +pub fn vnc_init(vnc: &Option, object: &HashMap) -> Result<()> { + let vnc_cfg; + if let Some(v) = vnc { + vnc_cfg = v; + } else { + return Ok(()); + } + let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); let listener: TcpListener; match TcpListener::bind(&addr.as_str()) { @@ -119,7 +126,6 @@ pub fn vnc_init(vnc_cfg: &VncConfig, object: &HashMap) -> Res EventNotifierHelper::internal_notifiers(VNC_SERVERS.lock().unwrap()[0].clone()), None, )?; - Ok(()) } @@ -488,6 +494,9 @@ fn get_client_image() -> *mut pixman_image_t { /// Update guest_image /// Send a resize command to the client based on whether the image size has changed pub fn vnc_display_switch(surface: &mut DisplaySurface) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } let need_resize = check_surface(surface); let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_server = server.lock().unwrap(); @@ -545,6 +554,9 @@ pub fn vnc_display_switch(surface: &mut DisplaySurface) { } pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } let server = VNC_SERVERS.lock().unwrap()[0].clone(); let width = cursor.width as u64; let heigt = cursor.height as u64; -- Gitee From 7e39e2e6551d6e6de39a0eedff333458cd5a848f Mon Sep 17 00:00:00 2001 From: XiaoyangXu Date: Wed, 24 Aug 2022 14:32:26 +0800 Subject: [PATCH 0144/2187] support for pass-through devices enable msix interrupts using discontinuous vectors --- vfio/src/vfio_dev.rs | 6 +++--- vfio/src/vfio_pci.rs | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 51ed35a9a..07713f3df 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -804,13 +804,14 @@ impl VfioDevice { /// # Arguments /// /// * `irq_fds` - Irq fds that will be registered to kvm. - pub fn enable_irqs(&mut self, irq_fds: Vec) -> Result<()> { + /// * `start` - The start of subindexes being specified. + pub fn enable_irqs(&mut self, irq_fds: Vec, start: u32) -> Result<()> { let mut irq_set = array_to_vec::(irq_fds.len()); irq_set[0].argsz = (size_of::() + irq_fds.len() * size_of::()) as u32; irq_set[0].flags = vfio::VFIO_IRQ_SET_DATA_EVENTFD | vfio::VFIO_IRQ_SET_ACTION_TRIGGER; irq_set[0].index = vfio::VFIO_PCI_MSIX_IRQ_INDEX; - irq_set[0].start = 0u32; + irq_set[0].start = start; irq_set[0].count = irq_fds.len() as u32; // It is safe as enough memory space to save irq_set data. @@ -829,7 +830,6 @@ impl VfioDevice { ) .into()); } - self.nr_vectors = irq_fds.len(); Ok(()) } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 6680df29a..266aa4d5c 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -76,6 +76,7 @@ struct VfioBar { struct GsiMsiRoute { irq_fd: Option, gsi: i32, + nr: u32, } /// VfioPciDevice is a VFIO PCI device. It implements PciDevOps trait for a PCI device. @@ -548,12 +549,26 @@ impl VfioPciDevice { } let mut locked_dev = cloned_dev.lock().unwrap(); - locked_dev - .disable_irqs() - .unwrap_or_else(|e| error!("Failed to disable irq, error is {}", e)); - locked_dev - .enable_irqs(get_irq_rawfds(&locked_gsi_routes)) - .unwrap_or_else(|e| error!("Failed to enable irq, error is {}", e)); + if (vector + 1) > (locked_dev.nr_vectors as u64) { + locked_dev + .disable_irqs() + .unwrap_or_else(|e| error!("Failed to disable irq, error is {}", e)); + + locked_dev + .enable_irqs( + get_irq_rawfds(&locked_gsi_routes, 0, (vector + 1) as u32), + 0, + ) + .unwrap_or_else(|e| error!("Failed to enable irq, error is {}", e)); + locked_dev.nr_vectors = (vector + 1) as usize; + } else { + locked_dev + .enable_irqs( + get_irq_rawfds(&locked_gsi_routes, vector as u32, 1), + vector as u32, + ) + .unwrap_or_else(|e| error!("Failed to enable irq, error is {}", e)); + } true }; @@ -697,14 +712,16 @@ impl VfioPciDevice { let gsi_route = GsiMsiRoute { irq_fd: Some(irq_fd), gsi: -1, + nr: 0, }; gsi_routes.push(gsi_route); let entries = self.msix_info.as_ref().unwrap().enteries; - for _ in 1..entries { + for i in 1..entries { let gsi_route = GsiMsiRoute { irq_fd: None, gsi: -1, + nr: i as u32, }; gsi_routes.push(gsi_route); } @@ -714,7 +731,7 @@ impl VfioPciDevice { self.vfio_device .lock() .unwrap() - .enable_irqs(get_irq_rawfds(&gsi_routes)) + .enable_irqs(get_irq_rawfds(&gsi_routes, 0, 1), 0) .chain_err(|| "Failed enable irqfds in kvm")?; Ok(()) @@ -1007,11 +1024,15 @@ impl PciDevOps for VfioPciDevice { } } -fn get_irq_rawfds(gsi_msi_routes: &[GsiMsiRoute]) -> Vec { +fn get_irq_rawfds(gsi_msi_routes: &[GsiMsiRoute], start: u32, count: u32) -> Vec { let mut rawfds: Vec = Vec::new(); for r in gsi_msi_routes.iter() { - if let Some(fd) = r.irq_fd.as_ref() { - rawfds.push(fd.as_raw_fd()); + if r.nr >= start && r.nr < start + count { + if let Some(fd) = r.irq_fd.as_ref() { + rawfds.push(fd.as_raw_fd()); + } else { + rawfds.push(-1); + } } } rawfds -- Gitee From bccb663d982d0c40f7c271581ff505fb46acd699 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Fri, 19 Aug 2022 14:47:03 +0800 Subject: [PATCH 0145/2187] cmdline: fix -smp help Signed-off-by: uran0sH --- machine_manager/src/cmdline.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index b69438c85..56e53a02b 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -94,8 +94,12 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("smp") .long("smp") - .value_name("[cpus=]n[,maxcpus=cpus][,dies=dies][,sockets=sockets][,cores=cores][,threads=threads]") - .help("set the number of CPUs to 'n' (default: 1)") + .value_name("[cpus=]n[,maxcpus=cpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]") + .help("set the number of CPUs to 'n' (default: 1). maxcpus=maximum number of total CPUs, including online and offline CPUs. \ + sockets is the number of sockets on the machine. \ + dies is the number of dies in one socket. \ + clusters is the number of clusters in one die. cores is the number of cores in one cluster. \ + threads is the number of threads in one core") .takes_value(true), ) .arg( -- Gitee From 7c301bcd5c653a6963d7ff1dac8466b2b2fcaa02 Mon Sep 17 00:00:00 2001 From: uran0sH Date: Thu, 21 Jul 2022 11:00:51 +0800 Subject: [PATCH 0146/2187] Fix query-cpus doesn't contain cluster-id in response. Fix the problem that the returned value of the query-cpus command does not contain cluster-id. Signed-off-by: uran0sH --- cpu/src/lib.rs | 63 +++++++++++++++++---------- machine/src/micro_vm/mod.rs | 24 ++-------- machine/src/standard_vm/mod.rs | 8 +--- machine_manager/src/qmp/qmp_schema.rs | 10 +++++ 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index dbc30fe0d..be04d3c91 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -97,6 +97,7 @@ pub use aarch64::ArmCPUCaps as CPUCaps; pub use aarch64::ArmCPUState as ArchCPU; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUTopology as CPUTopology; +use machine_manager::qmp::qmp_schema; #[cfg(target_arch = "x86_64")] use x86_64::caps::X86CPUCaps as CPUCaps; #[cfg(target_arch = "x86_64")] @@ -771,11 +772,27 @@ impl CpuTopology { /// # Arguments /// /// * `vcpu_id` - ID of vcpu. - pub fn get_topo(&self, vcpu_id: usize) -> (u8, u8, u8) { + fn get_topo_item(&self, vcpu_id: usize) -> (u8, u8, u8, u8, u8) { let socketid: u8 = vcpu_id as u8 / (self.dies * self.clusters * self.cores * self.threads); + let dieid: u8 = (vcpu_id as u8 / (self.clusters * self.cores * self.threads)) % self.dies; + let clusterid: u8 = (vcpu_id as u8 / (self.cores * self.threads)) % self.clusters; let coreid: u8 = (vcpu_id as u8 / self.threads) % self.cores; let threadid: u8 = vcpu_id as u8 % self.threads; - (socketid, coreid, threadid) + (socketid, dieid, clusterid, coreid, threadid) + } + + pub fn get_topo_instance_for_qmp(&self, cpu_index: usize) -> qmp_schema::CpuInstanceProperties { + let (socketid, _dieid, _clusterid, coreid, threadid) = self.get_topo_item(cpu_index); + qmp_schema::CpuInstanceProperties { + node_id: None, + socket_id: Some(socketid as isize), + #[cfg(target_arch = "x86_64")] + die_id: Some(_dieid as isize), + #[cfg(target_arch = "aarch64")] + cluster_id: Some(_clusterid as isize), + core_id: Some(coreid as isize), + thread_id: Some(threadid as isize), + } } } @@ -956,10 +973,10 @@ mod tests { online_mask: Arc::new(Mutex::new(mask)), }; - assert_eq!(microvm_cpu_topo.get_topo(0), (0, 0, 0)); - assert_eq!(microvm_cpu_topo.get_topo(4), (4, 0, 0)); - assert_eq!(microvm_cpu_topo.get_topo(8), (8, 0, 0)); - assert_eq!(microvm_cpu_topo.get_topo(15), (15, 0, 0)); + assert_eq!(microvm_cpu_topo.get_topo_item(0), (0, 0, 0, 0, 0)); + assert_eq!(microvm_cpu_topo.get_topo_item(4), (4, 0, 0, 0, 0)); + assert_eq!(microvm_cpu_topo.get_topo_item(8), (8, 0, 0, 0, 0)); + assert_eq!(microvm_cpu_topo.get_topo_item(15), (15, 0, 0, 0, 0)); let mask = Vec::with_capacity(test_nr_cpus as usize); let microvm_cpu_topo_x86 = CpuTopology { @@ -973,10 +990,10 @@ mod tests { online_mask: Arc::new(Mutex::new(mask)), }; - assert_eq!(microvm_cpu_topo_x86.get_topo(0), (0, 0, 0)); - assert_eq!(microvm_cpu_topo_x86.get_topo(4), (0, 2, 0)); - assert_eq!(microvm_cpu_topo_x86.get_topo(8), (0, 0, 0)); - assert_eq!(microvm_cpu_topo_x86.get_topo(15), (0, 3, 1)); + assert_eq!(microvm_cpu_topo_x86.get_topo_item(0), (0, 0, 0, 0, 0)); + assert_eq!(microvm_cpu_topo_x86.get_topo_item(4), (0, 0, 0, 2, 0)); + assert_eq!(microvm_cpu_topo_x86.get_topo_item(8), (0, 1, 0, 0, 0)); + assert_eq!(microvm_cpu_topo_x86.get_topo_item(15), (0, 1, 0, 3, 1)); let mask = Vec::with_capacity(test_nr_cpus as usize); let microvm_cpu_topo_arm = CpuTopology { @@ -990,10 +1007,10 @@ mod tests { online_mask: Arc::new(Mutex::new(mask)), }; - assert_eq!(microvm_cpu_topo_arm.get_topo(0), (0, 0, 0)); - assert_eq!(microvm_cpu_topo_arm.get_topo(4), (0, 2, 0)); - assert_eq!(microvm_cpu_topo_arm.get_topo(8), (0, 0, 0)); - assert_eq!(microvm_cpu_topo_arm.get_topo(15), (0, 3, 1)); + assert_eq!(microvm_cpu_topo_arm.get_topo_item(0), (0, 0, 0, 0, 0)); + assert_eq!(microvm_cpu_topo_arm.get_topo_item(4), (0, 0, 0, 2, 0)); + assert_eq!(microvm_cpu_topo_arm.get_topo_item(8), (0, 0, 1, 0, 0)); + assert_eq!(microvm_cpu_topo_arm.get_topo_item(15), (0, 0, 1, 3, 1)); let test_nr_cpus: u8 = 32; let mask = Vec::with_capacity(test_nr_cpus as usize); @@ -1008,14 +1025,14 @@ mod tests { online_mask: Arc::new(Mutex::new(mask)), }; - assert_eq!(test_cpu_topo.get_topo(0), (0, 0, 0)); - assert_eq!(test_cpu_topo.get_topo(4), (0, 2, 0)); - assert_eq!(test_cpu_topo.get_topo(7), (0, 3, 1)); - assert_eq!(test_cpu_topo.get_topo(11), (1, 1, 1)); - assert_eq!(test_cpu_topo.get_topo(15), (1, 3, 1)); - assert_eq!(test_cpu_topo.get_topo(17), (2, 0, 1)); - assert_eq!(test_cpu_topo.get_topo(23), (2, 3, 1)); - assert_eq!(test_cpu_topo.get_topo(29), (3, 2, 1)); - assert_eq!(test_cpu_topo.get_topo(31), (3, 3, 1)); + assert_eq!(test_cpu_topo.get_topo_item(0), (0, 0, 0, 0, 0)); + assert_eq!(test_cpu_topo.get_topo_item(4), (0, 0, 0, 2, 0)); + assert_eq!(test_cpu_topo.get_topo_item(7), (0, 0, 0, 3, 1)); + assert_eq!(test_cpu_topo.get_topo_item(11), (1, 0, 0, 1, 1)); + assert_eq!(test_cpu_topo.get_topo_item(15), (1, 0, 0, 3, 1)); + assert_eq!(test_cpu_topo.get_topo_item(17), (2, 0, 0, 0, 1)); + assert_eq!(test_cpu_topo.get_topo_item(23), (2, 0, 0, 3, 1)); + assert_eq!(test_cpu_topo.get_topo_item(29), (3, 0, 0, 2, 1)); + assert_eq!(test_cpu_topo.get_topo_item(31), (3, 0, 0, 3, 1)); } } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 0e9e99200..b5da44e0d 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -928,13 +928,7 @@ impl DeviceInterface for LightMachine { for cpu_index in 0..self.cpu_topo.max_cpus { if self.cpu_topo.get_mask(cpu_index as usize) == 1 { let thread_id = self.cpus[cpu_index as usize].tid(); - let (socketid, coreid, threadid) = self.cpu_topo.get_topo(cpu_index as usize); - let cpu_instance = qmp_schema::CpuInstanceProperties { - node_id: None, - socket_id: Some(socketid as isize), - core_id: Some(coreid as isize), - thread_id: Some(threadid as isize), - }; + let cpu_instance = self.cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") @@ -975,13 +969,7 @@ impl DeviceInterface for LightMachine { for cpu_index in 0..self.cpu_topo.max_cpus { if self.cpu_topo.get_mask(cpu_index as usize) == 0 { - let (socketid, coreid, threadid) = self.cpu_topo.get_topo(cpu_index as usize); - let cpu_instance = qmp_schema::CpuInstanceProperties { - node_id: None, - socket_id: Some(socketid as isize), - core_id: Some(coreid as isize), - thread_id: Some(threadid as isize), - }; + let cpu_instance = self.cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); let hotpluggable_cpu = qmp_schema::HotpluggableCPU { type_: cpu_type.clone(), vcpus_count: 1, @@ -990,13 +978,7 @@ impl DeviceInterface for LightMachine { }; hotplug_vec.push(serde_json::to_value(hotpluggable_cpu).unwrap()); } else { - let (socketid, coreid, threadid) = self.cpu_topo.get_topo(cpu_index as usize); - let cpu_instance = qmp_schema::CpuInstanceProperties { - node_id: None, - socket_id: Some(socketid as isize), - core_id: Some(coreid as isize), - thread_id: Some(threadid as isize), - }; + let cpu_instance = self.cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); let hotpluggable_cpu = qmp_schema::HotpluggableCPU { type_: cpu_type.clone(), vcpus_count: 1, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index e71822a71..14ae03747 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -923,13 +923,7 @@ impl DeviceInterface for StdMachine { for cpu_index in 0..cpu_topo.max_cpus { if cpu_topo.get_mask(cpu_index as usize) == 1 { let thread_id = cpus[cpu_index as usize].tid(); - let (socketid, coreid, threadid) = cpu_topo.get_topo(cpu_index as usize); - let cpu_instance = qmp_schema::CpuInstanceProperties { - node_id: None, - socket_id: Some(socketid as isize), - core_id: Some(coreid as isize), - thread_id: Some(threadid as isize), - }; + let cpu_instance = cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 19b1a3513..3f913a6b5 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -750,6 +750,16 @@ pub struct CpuInstanceProperties { pub node_id: Option, #[serde(rename = "socket-id", default, skip_serializing_if = "Option::is_none")] pub socket_id: Option, + #[cfg(target_arch = "x86_64")] + #[serde(rename = "die_id", default, skip_serializing_if = "Option::is_none")] + pub die_id: Option, + #[cfg(target_arch = "aarch64")] + #[serde( + rename = "cluster_id", + default, + skip_serializing_if = "Option::is_none" + )] + pub cluster_id: Option, #[serde(rename = "thread-id", default, skip_serializing_if = "Option::is_none")] pub thread_id: Option, #[serde(rename = "core-id", default, skip_serializing_if = "Option::is_none")] -- Gitee From 2ad31a7a089d4cbd5d37d238b03c88ec8a9ea4d8 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Wed, 31 Aug 2022 09:46:34 +0800 Subject: [PATCH 0147/2187] docs: fix the inconsistency of README.ch.md In https://gitee.com/openeuler/stratovirt/pulls/508, the build script was switched to `make build`, but only English version README.md was modified, so apply this change to README.ch.md Signed-off-by: Zhao Mengmeng --- README.ch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.ch.md b/README.ch.md index 93e5013c1..5c02ea369 100644 --- a/README.ch.md +++ b/README.ch.md @@ -15,7 +15,7 @@ https://www.rust-lang.org/tools/install ```sh $ git clone https://gitee.com/openeuler/stratovirt.git $ cd stratovirt -$ cargo build --release +$ make build ``` 可以在`target/release/stratovirt`路径下找到生成的二进制文件 -- Gitee From 5bc85cf7d96bf6f2d28e32e5efb7b7271b4ccc99 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Mon, 11 Apr 2022 15:46:00 +0800 Subject: [PATCH 0148/2187] vfio-mdev: add vfio-mdev support Add mdev pathname parsing during virtual machine startup and device hot-plug, and resolved bar0 read and write bugs when there is no mmap backend memory Signed-off-by: Xiaoyang Xu --- machine/src/lib.rs | 16 ++++++++++++++-- machine/src/standard_vm/mod.rs | 24 ++++++++++++++++++++++-- machine_manager/src/config/vfio.rs | 19 +++++++++++++++++-- machine_manager/src/qmp/qmp_schema.rs | 1 + vfio/src/vfio_dev.rs | 11 ++++++----- vfio/src/vfio_pci.rs | 9 ++++++--- 6 files changed, 66 insertions(+), 14 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 834a3de4e..46de03ab3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -665,10 +665,16 @@ pub trait MachineOps { id: &str, bdf: &PciBdf, host: &str, + sysfsdev: &str, multifunc: bool, ) -> Result<()> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; - let path = format!("/sys/bus/pci/devices/{}", host); + let path; + if !host.is_empty() { + path = format!("/sys/bus/pci/devices/{}", host); + } else { + path = sysfsdev.to_string(); + } let device = VfioDevice::new(Path::new(&path), self.get_sys_mem()) .chain_err(|| "Failed to create vfio device.")?; let vfio_pci = VfioPciDevice::new( @@ -687,7 +693,13 @@ pub trait MachineOps { let device_cfg: VfioConfig = parse_vfio(cfg_args)?; let bdf = get_pci_bdf(cfg_args)?; let multifunc = get_multi_function(cfg_args)?; - self.create_vfio_pci_device(&device_cfg.id, &bdf, &device_cfg.host, multifunc)?; + self.create_vfio_pci_device( + &device_cfg.id, + &bdf, + &device_cfg.host, + &device_cfg.sysfsdev, + multifunc, + )?; self.reset_bus(&device_cfg.id)?; Ok(()) } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 4eb8f1f45..1630ef2f5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -915,14 +915,34 @@ impl StdMachine { bdf: &PciBdf, args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { + let host; + let sysfsdev; + if args.host.is_none() { - bail!("Option \"host\" not provided."); + host = ""; + } else { + host = args.host.as_ref().unwrap(); + } + + if args.sysfsdev.is_none() { + sysfsdev = ""; + } else { + sysfsdev = args.sysfsdev.as_ref().unwrap(); + } + + if args.host.is_none() && args.sysfsdev.is_none() { + bail!("Neither option \"host\" nor \"sysfsdev\" was not provided."); + } + + if args.host.is_some() && args.sysfsdev.is_some() { + bail!("Both option \"host\" and \"sysfsdev\" was provided."); } if let Err(e) = self.create_vfio_pci_device( &args.id, bdf, - args.host.as_ref().unwrap(), + host, + sysfsdev, args.multifunction.map_or(false, |m| m), ) { error!("{}", e.display_chain()); diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs index 8acb4df6f..09e6363f1 100644 --- a/machine_manager/src/config/vfio.rs +++ b/machine_manager/src/config/vfio.rs @@ -15,6 +15,7 @@ use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; #[derive(Default)] pub struct VfioConfig { + pub sysfsdev: String, pub host: String, pub id: String, } @@ -40,6 +41,7 @@ pub fn parse_vfio(vfio_config: &str) -> Result { cmd_parser .push("") .push("host") + .push("sysfsdev") .push("id") .push("bus") .push("addr") @@ -49,9 +51,22 @@ pub fn parse_vfio(vfio_config: &str) -> Result { let mut vfio: VfioConfig = VfioConfig::default(); if let Some(host) = cmd_parser.get_value::("host")? { vfio.host = host; - } else { - return Err(ErrorKind::FieldIsMissing("host", "vfio").into()); } + + if let Some(sysfsdev) = cmd_parser.get_value::("sysfsdev")? { + vfio.sysfsdev = sysfsdev; + } + + if vfio.host.is_empty() && vfio.sysfsdev.is_empty() { + return Err(ErrorKind::FieldIsMissing("host nor sysfsdev", "vfio").into()); + } + + if !vfio.host.is_empty() && !vfio.sysfsdev.is_empty() { + return Err( + ErrorKind::InvalidParam("host and sysfsdev".to_string(), "vfio".to_string()).into(), + ); + } + if let Some(id) = cmd_parser.get_value::("id")? { vfio.id = id; } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index c712c5e40..770f57359 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -504,6 +504,7 @@ pub struct device_add { #[serde(rename = "num-queues")] pub queues: Option, pub boot_index: Option, + pub sysfsdev: Option, } pub type DeviceAddArgument = device_add; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 07713f3df..d1436001f 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use log::warn; use std::collections::HashMap; use std::ffi::CString; use std::fs::{File, OpenOptions}; @@ -751,11 +752,11 @@ impl VfioDevice { let ret = unsafe { ioctl_with_mut_ref(&self.fd, VFIO_DEVICE_GET_IRQ_INFO(), &mut info) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( - "VFIO_DEVICE_GET_IRQ_INFO".to_string(), - std::io::Error::last_os_error(), - ) - .into()); + warn!( + "VFIO_DEVICE_GET_IRQ_INFO return irq type{} not supported", + index + ); + continue; } let irq = VfioIrq { diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 266aa4d5c..99b521034 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -661,10 +661,13 @@ impl VfioPciDevice { .chain_err(|| "Failed to get bar info")?; let region = &mut bar.vfio_region; // If bar region already setups or does not support mapping, just process the nest. - if region.size == 0 || region.mmaps.is_empty() || region.guest_phys_addr == gpa { + if region.size == 0 || region.guest_phys_addr == gpa { + continue; + } + + region.guest_phys_addr = gpa; + if region.mmaps.is_empty() { continue; - } else { - region.guest_phys_addr = gpa; } let mut read_only = true; -- Gitee From be7742cb3f5a33bd031248d685b6ca5028cb9335 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 Aug 2022 17:19:01 +0800 Subject: [PATCH 0149/2187] VNC: Keyboard and mouse supported Add support for keyboard and mouse Signed-off-by: Yan Wang Signed-off-by: Xiao Ye --- machine/src/lib.rs | 9 +- vnc/Cargo.toml | 1 + vnc/src/client.rs | 4 +- vnc/src/data/keycode.rs | 188 ++++++++++++++++++++++++++++++++++++++++ vnc/src/data/mod.rs | 13 +++ vnc/src/input.rs | 112 +++++++++++++++++++++++- vnc/src/lib.rs | 1 + vnc/src/server.rs | 22 +++-- vnc/src/vnc.rs | 2 + 9 files changed, 341 insertions(+), 11 deletions(-) create mode 100644 vnc/src/data/keycode.rs create mode 100644 vnc/src/data/mod.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1d8ea5ca4..9f7db1b42 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -107,6 +107,7 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; use error_chain::bail; use kvm_ioctls::VcpuFd; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use vnc::INPUT; pub use micro_vm::LightMachine; @@ -950,7 +951,7 @@ pub trait MachineOps { if let Some(ctrl) = locked_dev.get("usb.0") { let mut locked_ctrl = ctrl.lock().unwrap(); locked_ctrl - .attach_device(&(kbd as Arc>)) + .attach_device(&(kbd.clone() as Arc>)) .chain_err(|| "Failed to attach keyboard device")?; } else { bail!("No usb controller found"); @@ -958,6 +959,8 @@ pub trait MachineOps { } else { bail!("No bus device found"); } + let mut locked_input = INPUT.lock().unwrap(); + locked_input.keyboard = Some(kbd); Ok(()) } @@ -977,7 +980,7 @@ pub trait MachineOps { if let Some(ctrl) = locked_dev.get("usb.0") { let mut locked_ctrl = ctrl.lock().unwrap(); locked_ctrl - .attach_device(&(tbt as Arc>)) + .attach_device(&(tbt.clone() as Arc>)) .chain_err(|| "Failed to attach tablet device")?; } else { bail!("No usb controller found"); @@ -985,6 +988,8 @@ pub trait MachineOps { } else { bail!("No bus device list found"); } + let mut locked_input = INPUT.lock().unwrap(); + locked_input.tablet = Some(tbt); Ok(()) } diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index 09e631227..7f77c4d3f 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -17,6 +17,7 @@ vmm-sys-util = ">=0.7.0" once_cell = "1.9.0" sscanf = "0.2.1" bitintr = "0.2.0" +usb = { path = "../usb" } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 02a19212e..c5e6a6311 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -238,9 +238,9 @@ pub struct VncClient { /// Tcp listening address. pub addr: String, /// Image width. - width: i32, + pub width: i32, /// Image height. - height: i32, + pub height: i32, /// Encoding type. encoding: i32, /// Image display feature. diff --git a/vnc/src/data/keycode.rs b/vnc/src/data/keycode.rs new file mode 100644 index 000000000..38778cff7 --- /dev/null +++ b/vnc/src/data/keycode.rs @@ -0,0 +1,188 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub const KEYSYM2KEYCODE: [(u16, u16); 173] = [ + // (Keysym , Keycode) + (0x0020, 0x0039), + (0x0021, 0x0102), + (0x0022, 0x0128), + (0x0023, 0x0104), + (0x0024, 0x0105), + (0x0025, 0x0106), + (0x0026, 0x0108), + (0x0027, 0x0028), + (0x0028, 0x010A), + (0x0029, 0x010B), + (0x002A, 0x0109), + (0x002B, 0x010D), + (0x002C, 0x0033), + (0x002D, 0x000C), + (0x002E, 0x0034), + (0x002F, 0x0035), + (0x0030, 0x000B), + (0x0031, 0x0002), + (0x0032, 0x0003), + (0x0033, 0x0004), + (0x0034, 0x0005), + (0x0035, 0x0006), + (0x0036, 0x0007), + (0x0037, 0x0008), + (0x0038, 0x0009), + (0x0039, 0x000A), + (0x003A, 0x0127), + (0x003B, 0x0027), + (0x003C, 0x0133), + (0x003D, 0x000D), + (0x003E, 0x0134), + (0x003F, 0x0135), + (0x0040, 0x0103), + (0x0041, 0x011E), + (0x0042, 0x0130), + (0x0043, 0x012E), + (0x0044, 0x0120), + (0x0045, 0x0112), + (0x0046, 0x0121), + (0x0047, 0x0122), + (0x0048, 0x0123), + (0x0049, 0x0117), + (0x004A, 0x0124), + (0x004B, 0x0125), + (0x004C, 0x0126), + (0x004D, 0x0132), + (0x004E, 0x0131), + (0x004F, 0x0118), + (0x0050, 0x0119), + (0x0051, 0x0110), + (0x0052, 0x0113), + (0x0053, 0x011F), + (0x0054, 0x0114), + (0x0055, 0x0116), + (0x0056, 0x012F), + (0x0057, 0x0111), + (0x0058, 0x012D), + (0x0059, 0x0115), + (0x005A, 0x012C), + (0x005B, 0x001A), + (0x005C, 0x002B), + (0x005D, 0x001B), + (0x005E, 0x0107), + (0x005F, 0x010C), + (0x0060, 0x0029), + (0x0061, 0x001E), + (0x0062, 0x0030), + (0x0063, 0x002E), + (0x0064, 0x0020), + (0x0065, 0x0012), + (0x0066, 0x0021), + (0x0067, 0x0022), + (0x0068, 0x0023), + (0x0069, 0x0017), + (0x006A, 0x0024), + (0x006B, 0x0025), + (0x006C, 0x0026), + (0x006D, 0x0032), + (0x006E, 0x0031), + (0x006F, 0x0018), + (0x0070, 0x0019), + (0x0071, 0x0010), + (0x0072, 0x0013), + (0x0073, 0x001F), + (0x0074, 0x0014), + (0x0075, 0x0016), + (0x0076, 0x002F), + (0x0077, 0x0011), + (0x0078, 0x002D), + (0x0079, 0x0015), + (0x007A, 0x002C), + (0x007B, 0x011A), + (0x007C, 0x012B), + (0x007D, 0x011B), + (0x007E, 0x0129), + (0x00A6, 0x0956), + (0xFE03, 0x00B8), + (0xFF08, 0x000E), + (0xFF09, 0x000F), + (0xFF0D, 0x001C), + (0xFF13, 0x00C6), + (0xFF14, 0x0046), + (0xFF15, 0x0054), + (0xFF1B, 0x0001), + (0xFF22, 0x007B), + (0xFF23, 0x0079), + (0xFF50, 0x00C7), + (0xFF51, 0x00CB), + (0xFF52, 0x00C8), + (0xFF53, 0x00CD), + (0xFF54, 0x00D0), + (0xFF55, 0x00C9), + (0xFF56, 0x00D1), + (0xFF57, 0x00CF), + (0xFF61, 0x0054), + (0xFF62, 0x0054), + (0xFF63, 0x00D2), + (0xFF67, 0x00DD), + (0xFF7E, 0x00B8), + (0xFF7F, 0x0045), + (0xFF8D, 0x009C), + (0xFF95, 0x0047), + (0xFF96, 0x004B), + (0xFF97, 0x0048), + (0xFF98, 0x004D), + (0xFF99, 0x0050), + (0xFF9A, 0x0049), + (0xFF9B, 0x0051), + (0xFF9C, 0x004F), + (0xFF9D, 0x004C), + (0xFF9E, 0x0052), + (0xFF9F, 0x0053), + (0xFFAA, 0x0037), + (0xFFAB, 0x004E), + (0xFFAC, 0x0053), + (0xFFAD, 0x004A), + (0xFFAE, 0x0053), + (0xFFAF, 0x00B5), + (0xFFB0, 0x0052), + (0xFFB1, 0x004F), + (0xFFB2, 0x0050), + (0xFFB3, 0x0051), + (0xFFB4, 0x004B), + (0xFFB5, 0x004C), + (0xFFB6, 0x004D), + (0xFFB7, 0x0047), + (0xFFB8, 0x0048), + (0xFFB9, 0x0049), + (0xFFBD, 0x0059), + (0xFFBE, 0x003B), + (0xFFBF, 0x003C), + (0xFFC0, 0x003D), + (0xFFC1, 0x003E), + (0xFFC2, 0x003F), + (0xFFC3, 0x0040), + (0xFFC4, 0x0041), + (0xFFC5, 0x0042), + (0xFFC6, 0x0043), + (0xFFC7, 0x0044), + (0xFFC8, 0x0057), + (0xFFC9, 0x0058), + (0xFFE1, 0x002A), + (0xFFE2, 0x0036), + (0xFFE3, 0x001D), + (0xFFE4, 0x009D), + (0xFFE5, 0x003A), + (0xFFE7, 0x0138), + (0xFFE8, 0x01B8), + (0xFFE9, 0x0038), + (0xFFEA, 0x00B8), + (0xFFEB, 0x00DB), + (0xFFEC, 0x00DC), + (0xFFFF, 0x00D3), +]; diff --git a/vnc/src/data/mod.rs b/vnc/src/data/mod.rs new file mode 100644 index 000000000..efc157923 --- /dev/null +++ b/vnc/src/data/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod keycode; diff --git a/vnc/src/input.rs b/vnc/src/input.rs index caf0ac216..fcdf14530 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -11,15 +11,59 @@ // See the Mulan PSL v2 for more details. use crate::VncClient; +use once_cell::sync::Lazy; +use std::sync::{Arc, Mutex}; +use usb::{ + keyboard::{keyboard_event, UsbKeyboard}, + tablet::{pointer_event, pointer_sync, UsbTablet}, +}; +// Logical window size for mouse. +const ABS_MAX: u64 = 0x7fff; +// Up flag. +const SCANCODE_UP: u16 = 0x80; +// Grey keys. +const SCANCODE_GREY: u16 = 0x80; +// Used to expand Grey keys. +const SCANCODE_EMUL0: u16 = 0xe0; +// Event type of Point. +const INPUT_POINT_LEFT: u8 = 0x01; +const INPUT_POINT_MIDDLE: u8 = 0x02; +const INPUT_POINT_RIGHT: u8 = 0x04; +// ASCII value. +const ASCII_A: i32 = 65; +const ASCII_Z: i32 = 90; +const UPPERCASE_TO_LOWERCASE: i32 = 32; impl VncClient { - // Keyboard event. + /// Keyboard event. pub fn key_envent(&mut self) { if self.expect == 1 { self.expect = 8; return; } + let buf = self.buffpool.read_front(self.expect); + let down = buf[1] as u8; + let mut keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); + + // Uppercase -> Lowercase. + if (ASCII_A..=ASCII_Z).contains(&keysym) { + keysym += UPPERCASE_TO_LOWERCASE; + } + let keycode: u16; + match self + .server + .lock() + .unwrap() + .keysym2keycode + .get(&(keysym as u16)) + { + Some(k) => keycode = *k, + None => { + keycode = 0; + } + } + self.do_key_event(down, keycode); self.update_event_handler(1, VncClient::handle_protocol_msg); } @@ -30,10 +74,63 @@ impl VncClient { return; } + let buf = self.buffpool.read_front(self.expect); + let mut x = ((buf[2] as u16) << 8) + buf[3] as u16; + let mut y = ((buf[4] as u16) << 8) + buf[5] as u16; + + // Window size alignment. + x = ((x as u64 * ABS_MAX) / self.width as u64) as u16; + y = ((y as u64 * ABS_MAX) / self.height as u64) as u16; + + // ASCII -> HidCode. + let button_mask: u8 = match buf[1] as u8 { + INPUT_POINT_LEFT => 0x01, + INPUT_POINT_MIDDLE => 0x04, + INPUT_POINT_RIGHT => 0x02, + _ => buf[1] as u8, + }; + + let locked_input = INPUT.lock().unwrap(); + if let Some(tablet) = &locked_input.tablet { + if let Err(e) = pointer_event(tablet, button_mask as u32, x as i32, y as i32) { + error!("Point event error: {}", e); + } + if let Err(e) = pointer_sync(tablet) { + error!("Point event sync error: {}", e); + } + } + self.update_event_handler(1, VncClient::handle_protocol_msg); } - // Client cut text. + /// Do keyboard event. + /// + /// # Arguments + /// + /// * `down` - press keyboard down or up. + /// * `keycode` - keycode. + pub fn do_key_event(&mut self, down: u8, keycode: u16) { + let mut scancode = Vec::new(); + let mut keycode = keycode; + if keycode & SCANCODE_GREY != 0 { + scancode.push(SCANCODE_EMUL0 as u32); + keycode &= !SCANCODE_GREY; + } + + if down == 0 { + keycode |= SCANCODE_UP; + } + scancode.push(keycode as u32); + // Send key event. + let locked_input = INPUT.lock().unwrap(); + if let Some(keyboard) = &locked_input.keyboard { + if let Err(e) = keyboard_event(keyboard, scancode.as_slice()) { + error!("Key event error: {}", e); + } + } + } + + /// Client cut text. pub fn client_cut_event(&mut self) { let buf = self.buffpool.read_front(self.expect); if self.expect == 1 { @@ -52,3 +149,14 @@ impl VncClient { self.update_event_handler(1, VncClient::handle_protocol_msg); } } + +pub struct Input { + pub keyboard: Option>>, + pub tablet: Option>>, +} +pub static INPUT: Lazy>> = Lazy::new(|| { + Arc::new(Mutex::new(Input { + keyboard: None, + tablet: None, + })) +}); diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index c1d1c289a..8b244e875 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -55,6 +55,7 @@ pub mod errors { pub mod auth; pub mod client; +mod data; pub mod input; pub mod pixman; pub mod server; diff --git a/vnc/src/server.rs b/vnc/src/server.rs index d89d28138..61dbbe802 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -34,10 +34,10 @@ use util::{ use vmm_sys_util::epoll::EventSet; use crate::{ - bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, - get_image_width, round_up_div, unref_pixman_image, update_client_surface, AuthState, - DisplayMouse, SubAuthState, VncClient, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, - REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, + bytes_per_pixel, data::keycode::KEYSYM2KEYCODE, get_image_data, get_image_format, + get_image_height, get_image_stride, get_image_width, round_up_div, unref_pixman_image, + update_client_surface, AuthState, DisplayMouse, SubAuthState, VncClient, DIRTY_PIXELS_NUM, + MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, }; /// Info of image. @@ -87,6 +87,8 @@ pub struct VncServer { pub auth: AuthState, /// Subauth type. pub subauth: SubAuthState, + /// Mapping ASCII to keycode. + pub keysym2keycode: HashMap, /// Image refresh to VncClient. pub server_image: *mut pixman_image_t, /// Image from gpu. @@ -115,6 +117,7 @@ impl VncServer { clients: HashMap::new(), auth: AuthState::No, subauth: SubAuthState::VncAuthVencryptPlain, + keysym2keycode: HashMap::new(), server_image: ptr::null_mut(), guest_image, guest_dirtymap: Bitmap::::new( @@ -132,7 +135,12 @@ impl VncServer { } } - /// Make configuration for VncServer. + /// make configuration for VncServer + /// + /// # Arguments + /// + /// * `vnc_cfg` - configure of vnc. + /// * `object` - configure of sasl and tls. pub fn make_config( &mut self, _vnc_cfg: &VncConfig, @@ -140,6 +148,10 @@ impl VncServer { ) -> Result<()> { // tls configuration + // Mapping ASCII to keycode. + for &(k, v) in KEYSYM2KEYCODE.iter() { + self.keysym2keycode.insert(k, v); + } Ok(()) } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 5fff5b631..e185fe54b 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -279,6 +279,8 @@ pub fn update_client_surface(server: &mut VncServer) { }; for client in server.clients.values_mut() { client.lock().unwrap().server_image = server.server_image; + client.lock().unwrap().width = width; + client.lock().unwrap().height = height; } server.guest_dirtymap.clear_all(); set_area_dirty( -- Gitee From 5a2dbe31af2bc988d467606692aa9e3f18433aa7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 1 Sep 2022 20:39:48 +0800 Subject: [PATCH 0150/2187] xhci: fixed the controller not work when start Windows VM Check the slot id when kick endpoint.And chekc the packet status in transfer retry. Signed-off-by: zhouli57 --- usb/src/tablet.rs | 3 +- usb/src/usb.rs | 2 +- usb/src/xhci/xhci_controller.rs | 62 +++++++++++++++++++++++---------- usb/src/xhci/xhci_pci.rs | 4 +-- usb/src/xhci/xhci_ring.rs | 4 ++- 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 58c4e23e4..b33f1a7e6 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -184,7 +184,8 @@ pub fn pointer_sync(tablet: &Arc>) -> Result<()> { let locked_tablet = tablet.lock().unwrap(); let mut locked_hid = locked_tablet.hid.lock().unwrap(); if locked_hid.num == QUEUE_LENGTH - 1 { - info!("Pointer queue full"); + debug!("Pointer queue is full!"); + return Ok(()); } let cur_index = ((locked_hid.head + locked_hid.num) & QUEUE_MASK) as usize; let pre_index = if cur_index == 0 { diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 6c7336ddc..fb75780ae 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -806,7 +806,7 @@ fn write_mem(hva: u64, buf: &[u8]) { } /// Transfer packet from host to device or from device to host. -pub fn usb_packet_transfer(packet: &mut UsbPacket, vec: &mut Vec, len: usize) { +pub fn usb_packet_transfer(packet: &mut UsbPacket, vec: &mut [u8], len: usize) { let to_host = packet.pid as u8 & USB_TOKEN_IN == USB_TOKEN_IN; if to_host { diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 61fa976ec..40b47b2d7 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -73,6 +73,8 @@ const EVENT_TRB_SLOT_ID_SHIFT: u32 = 24; const EVENT_TRB_EP_ID_SHIFT: u32 = 16; const PORT_EVENT_ID_SHIFT: u32 = 24; const SLOT_CTX_PORT_NUMBER_SHIFT: u32 = 16; +const ENDPOINT_ID_START: u32 = 1; +const MAX_ENDPOINTS: u32 = 31; type DmaAddr = u64; @@ -213,7 +215,7 @@ impl XhciSlot { intr: 0, ctx: 0, usb_port: None, - endpoints: vec![XhciEpContext::new(mem, 0); 31], + endpoints: vec![XhciEpContext::new(mem, 0); MAX_ENDPOINTS as usize], } } } @@ -867,7 +869,7 @@ impl XhciDevice { let slot = &mut self.slots[(slot_id - 1) as usize]; let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; if !epctx.enabled { - info!("Endpoint already disabled"); + debug!("Endpoint already disabled"); return Ok(TRBCCode::Success); } if self.oper.dcbaap != 0 { @@ -878,7 +880,7 @@ impl XhciDevice { } fn stop_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { - if !(1..=31).contains(&ep_id) { + if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&ep_id) { error!("Invalid endpoint id"); return Ok(TRBCCode::TrbError); } @@ -897,7 +899,7 @@ impl XhciDevice { } fn reset_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { - if !(1..=31).contains(&ep_id) { + if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&ep_id) { error!("Invalid endpoint id {}", ep_id); return Ok(TRBCCode::TrbError); } @@ -926,7 +928,7 @@ impl XhciDevice { } fn set_endpoint_dequeue(&mut self, slotid: u32, epid: u32, trb: &XhciTRB) -> Result { - if !(1..=31).contains(&epid) { + if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&epid) { error!("Invalid endpoint id {}", epid); return Ok(TRBCCode::TrbError); } @@ -948,20 +950,17 @@ impl XhciDevice { /// Data plane pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { - if !self.slots[(slot_id - 1) as usize].enabled { - bail!("Kick disbaled slot slotid {}", slot_id); - } - - let ep_ctx = &self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + let ep_ctx = self.get_endpoint(slot_id, ep_id)?; debug!( "kick_endpoint slotid {} epid {} dequeue {:x}", slot_id, ep_id, ep_ctx.ring.dequeue ); - if !ep_ctx.enabled { - bail!("Kick disabled endpoint slot id {} ep id {}", slot_id, ep_id); - } let mut epctx = ep_ctx.clone(); - self.endpoint_retry_transfer(&mut epctx)?; + if let Err(e) = self.endpoint_retry_transfer(&mut epctx) { + // Update the endpoint context in slot. + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + bail!("Failed to retry transfer {}", e); + } if epctx.state == EP_HALTED { info!("xhci: endpoint halted"); return Ok(()); @@ -977,6 +976,9 @@ impl XhciDevice { break; } }; + if len == 0 { + break; + } let mut xfer: XhciTransfer = XhciTransfer::new(len); xfer.slotid = slot_id; xfer.epid = ep_id; @@ -1024,10 +1026,35 @@ impl XhciDevice { Ok(()) } + fn check_slot_enabled(&self, slot_id: u32) -> Result<()> { + if slot_id == 0 || slot_id > self.slots.len() as u32 { + bail!("Invalid slot id {}", slot_id); + } + if !self.slots[(slot_id - 1) as usize].enabled { + bail!("Slot {} is disabled", slot_id); + } + Ok(()) + } + + fn get_endpoint(&self, slot_id: u32, ep_id: u32) -> Result<&XhciEpContext> { + self.check_slot_enabled(slot_id)?; + if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&ep_id) { + bail!("Invalid endpoint id {}", ep_id); + } + let ep_ctx = &self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + if !ep_ctx.enabled { + bail!("Endpoint is disabled, slot id {} ep id {}", slot_id, ep_id); + } + Ok(ep_ctx) + } + fn endpoint_retry_transfer(&mut self, epctx: &mut XhciEpContext) -> Result<()> { if let Some(xfer) = &mut epctx.retry { self.setup_usb_packet(xfer, epctx.epid)?; self.device_handle_packet(&mut xfer.packet)?; + if xfer.packet.status == UsbPacketStatus::Nak { + bail!("USB packet status is NAK"); + } self.complete_packet(xfer)?; if xfer.complete { epctx.set_state(&self.mem_space, epctx.state)?; @@ -1258,7 +1285,7 @@ impl XhciDevice { short_pkt = false; } _ => { - bail!("submit_transfer, unhandled trb type {:?}", trb.get_type()); + debug!("Ignore the TRB, unhandled trb type {:?}", trb.get_type()); } } if !reported @@ -1374,13 +1401,10 @@ impl XhciDevice { /// Get microframe index pub fn get_mf_index(&self) -> u64 { - warn!("get_mf_index not implemented"); 0 } - pub fn update_mf(&self) { - warn!("update_mf not implemented"); - } + pub fn update_mf(&self) {} pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { let intr = &mut self.intrs[idx as usize]; diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index c77afa25c..20dc7ae24 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -298,9 +298,7 @@ impl XhciOps for XhciPciDevice { } } - fn update_intr(&mut self, _n: u32, _enable: bool) { - warn!("XhciPciDevice update_intr not implemented"); - } + fn update_intr(&mut self, _n: u32, _enable: bool) {} } impl BusDeviceOps for XhciPciDevice { diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index a298d08e1..93330323b 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -272,7 +272,9 @@ impl XhciRing { for _ in 0..RING_LEN_LIMIT { let trb = self.read_trb(dequeue)?; if trb.get_cycle_bit() != ccs { - bail!("TRB cycle bit not matched"); + // TRB is not ready + debug!("TRB cycle bit not matched"); + return Ok(0); } let trb_type = trb.get_type(); if trb_type == TRBType::TrLink { -- Gitee From ed25dd88f2215db42f31bbc8e84590b1cf34c147 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 1 Sep 2022 20:44:13 +0800 Subject: [PATCH 0151/2187] xhci: fix potential deadlocks Do not hold xhci and port locks at the same time as possible. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 38 ++++++++++++++++++++ usb/src/xhci/xhci_regs.rs | 64 ++++++++------------------------- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 40b47b2d7..6237ca247 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -417,6 +417,44 @@ impl XhciDevice { Some(self.ports[index as usize].clone()) } + /// Reset xhci port. + pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { + let mut locked_port = xhci_port.lock().unwrap(); + if let Some(usb_port) = locked_port.usb_port.clone() { + let locked_usb_port = usb_port.upgrade().unwrap(); + let speed = locked_usb_port + .lock() + .unwrap() + .dev + .as_ref() + .unwrap() + .lock() + .unwrap() + .speed(); + if speed == USB_SPEED_SUPER && warm_reset { + locked_port.portsc |= PORTSC_WRC; + } + match speed { + USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH => { + locked_port.portsc = set_field( + locked_port.portsc, + PLS_U0, + PORTSC_PLS_MASK, + PORTSC_PLS_SHIFT, + ); + locked_port.portsc |= PORTSC_PED; + } + _ => { + error!("Invalid speed {}", speed); + } + } + locked_port.portsc &= !PORTSC_PR; + drop(locked_port); + self.port_notify(xhci_port, PORTSC_PRC)?; + } + Ok(()) + } + /// Send PortStatusChange event to notify drivers. pub fn port_notify(&mut self, port: &Arc>, flag: u32) -> Result<()> { let mut locked_port = port.lock().unwrap(); diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 16df3d4f2..565c8aab4 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -98,7 +98,6 @@ const XHCI_PORTSC: u64 = 0x0; const XHCI_PORTPMSC: u64 = 0x4; const XHCI_PORTLI: u64 = 0x8; const XHCI_PORTHLPMC: u64 = 0xc; -const TRB_PORT_ID_SHIFT: u32 = 24; /// XHCI Operation Registers #[derive(Default, Copy, Clone)] @@ -238,48 +237,6 @@ impl XhciPort { name, } } - - pub fn reset(&mut self, warm_reset: bool) { - if let Some(usb_port) = self.usb_port.clone() { - let locked_port = usb_port.upgrade().unwrap(); - let speed = locked_port - .lock() - .unwrap() - .dev - .as_ref() - .unwrap() - .lock() - .unwrap() - .speed(); - if speed == USB_SPEED_SUPER && warm_reset { - self.portsc |= PORTSC_WRC; - } - match speed { - USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH => { - self.portsc = set_field(self.portsc, PLS_U0, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); - self.portsc |= PORTSC_PED; - } - _ => { - error!("Invalid speed {}", speed); - } - } - self.portsc &= !PORTSC_PR; - if let Err(e) = self.notify(PORTSC_PRC) { - error!("Failed to notify {}", e); - } - } - } - - pub fn notify(&mut self, flag: u32) -> Result<()> { - if self.portsc & flag == flag { - return Ok(()); - } - let xhci = self.xhci.upgrade().unwrap(); - let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); - evt.ptr = (self.port_idx << TRB_PORT_ID_SHIFT) as u64; - xhci.lock().unwrap().send_event(&evt, 0)?; - Ok(()) - } } /// Build capability region ops. @@ -684,25 +641,32 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { return false; } }; - let mut locked_port = port.lock().unwrap(); + let locked_port = port.lock().unwrap(); debug!( "port write {} {:x} {:x} {:x}", locked_port.name, addr.0, offset, value ); - + let xhci = locked_port.xhci.upgrade().unwrap(); + drop(locked_port); + // Lock controller first. + let mut locked_xhci = xhci.lock().unwrap(); #[allow(clippy::never_loop)] loop { match offset { XHCI_PORTSC => { if value & PORTSC_WPR == PORTSC_WPR { - locked_port.reset(true); + if let Err(e) = locked_xhci.reset_port(&port, true) { + error!("Failed to warn reset port {}", e); + } break; } if value & PORTSC_PR == PORTSC_PR { - locked_port.reset(false); + if let Err(e) = locked_xhci.reset_port(&port, false) { + error!("Failed to reset port {}", e); + } break; } - + let mut locked_port = port.lock().unwrap(); let mut portsc = locked_port.portsc; let mut notify = 0; portsc &= !(value @@ -747,9 +711,9 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); locked_port.portsc = portsc; - + drop(locked_port); if notify != 0 { - if let Err(e) = locked_port.notify(notify) { + if let Err(e) = locked_xhci.port_notify(&port, notify) { error!("Failed to notify: {}", e); } } -- Gitee From 2dfb57c7f6d46cb62df776ae62131f0689e1337a Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 5 Sep 2022 16:10:55 +0800 Subject: [PATCH 0152/2187] Fix: Unable to compile musl Binary due to linking external dependencies Compile stratovirt twice, the gnu version and the musl version. Disable vnc and virtio-gpu in musl binaries. Signed-off-by: Xiao Ye Signed-off-by: Binfeng Wu --- .cargo/config | 4 +--- Cargo.toml | 3 ++- machine/Cargo.toml | 2 ++ machine/src/lib.rs | 21 +++++++++++++-------- machine/src/standard_vm/aarch64/mod.rs | 2 ++ machine/src/standard_vm/x86_64/mod.rs | 2 ++ usb/src/lib.rs | 16 ++++++++++++++++ util/src/lib.rs | 1 + virtio/Cargo.toml | 4 +++- virtio/src/lib.rs | 2 ++ vnc/Cargo.toml | 1 - vnc/src/input.rs | 18 +++--------------- 12 files changed, 47 insertions(+), 29 deletions(-) diff --git a/.cargo/config b/.cargo/config index 78ea6eb91..dd043fcba 100644 --- a/.cargo/config +++ b/.cargo/config @@ -15,11 +15,9 @@ [target.'cfg(any(target_arch="aarch64"))'] rustflags = [ "-C", "link-arg=-lgcc", - "-C", "link-arg=-lpixman-1", ] -[target.'cfg(any(target_arch="x86_64"))'] +[target.'cfg(not(target_env = "musl"))'] rustflags = [ "-C", "link-arg=-lpixman-1", ] - diff --git a/Cargo.toml b/Cargo.toml index ccf3a5310..4dcf49f07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ migration = { path = "migration" } util = { path = "util" } virtio = { path = "virtio" } vfio = { path = "vfio" } + +[target.'cfg(not(target_env = "musl"))'.dependencies] vnc = { path = "vnc" } [workspace] @@ -39,7 +41,6 @@ members = [ "virtio", "ozone", "vfio", - "vnc", ] [[bin]] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 98bc22f6b..46ea0bab4 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -31,6 +31,8 @@ util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } usb = { path = "../usb" } + +[target.'cfg(not(target_env = "musl"))'.dependencies] vnc = { path = "../vnc" } [features] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9f7db1b42..790cd1753 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -107,7 +107,6 @@ use std::sync::{Arc, Barrier, Mutex, Weak}; use error_chain::bail; use kvm_ioctls::VcpuFd; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use vnc::INPUT; pub use micro_vm::LightMachine; @@ -122,9 +121,11 @@ use devices::legacy::FwCfgOps; use devices::InterruptController; use errors::{ErrorKind, Result, ResultExt}; use hypervisor::kvm::KVM_FDS; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::parse_gpu; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_gpu, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, + parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, @@ -141,7 +142,7 @@ pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; use usb::{ bus::BusDeviceMap, keyboard::UsbKeyboard, tablet::UsbTablet, usb::UsbDeviceOps, - xhci::xhci_pci::XhciPciDevice, + xhci::xhci_pci::XhciPciDevice, INPUT, }; use util::{ arg_parser, @@ -149,9 +150,11 @@ use util::{ seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; use vfio::{VfioDevice, VfioPciDevice}; +#[cfg(not(target_env = "musl"))] +use virtio::Gpu; use virtio::{ - balloon_allow_list, Balloon, Block, BlockState, Console, Gpu, Rng, RngState, VhostKern, - VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, + balloon_allow_list, Balloon, Block, BlockState, Console, Rng, RngState, VhostKern, VhostUser, + VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; @@ -704,6 +707,7 @@ pub trait MachineOps { Ok(()) } + #[cfg(not(target_env = "musl"))] fn add_virtio_pci_gpu(&mut self, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -1056,9 +1060,6 @@ pub trait MachineOps { "vfio-pci" => { self.add_vfio_device(cfg_args)?; } - "virtio-gpu-pci" => { - self.add_virtio_pci_gpu(cfg_args)?; - } "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } @@ -1068,6 +1069,10 @@ pub trait MachineOps { "usb-tablet" => { self.add_usb_tablet(cfg_args)?; } + #[cfg(not(target_env = "musl"))] + "virtio-gpu-pci" => { + self.add_virtio_pci_gpu(cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c214e2bd9..0d86c2b4c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -62,6 +62,7 @@ use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; +#[cfg(not(target_env = "musl"))] use vnc::vnc; use super::{errors::Result as StdResult, AcpiBuilder, StdMachineOps}; @@ -460,6 +461,7 @@ impl MachineOps for StdMachine { locked_vm .add_devices(vm_config) .chain_err(|| "Failed to add devices")?; + #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) .chain_err(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device()?; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 9f5621b49..5c411ad56 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -64,6 +64,7 @@ use super::errors::{ErrorKind, Result}; use super::{AcpiBuilder, StdMachineOps}; use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; use crate::{vm_state, MachineOps}; +#[cfg(not(target_env = "musl"))] use vnc::vnc; const VENDOR_ID_INTEL: u16 = 0x8086; @@ -474,6 +475,7 @@ impl MachineOps for StdMachine { .init_ich9_lpc(clone_vm) .chain_err(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; + #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) .chain_err(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device()?; diff --git a/usb/src/lib.rs b/usb/src/lib.rs index 191a43d3e..f8ca73c09 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -37,3 +37,19 @@ pub mod keyboard; pub mod tablet; pub mod usb; pub mod xhci; + +use crate::keyboard::UsbKeyboard; +use crate::tablet::UsbTablet; +use once_cell::sync::Lazy; +use std::sync::{Arc, Mutex}; + +pub struct Input { + pub keyboard: Option>>, + pub tablet: Option>>, +} +pub static INPUT: Lazy>> = Lazy::new(|| { + Arc::new(Mutex::new(Input { + keyboard: None, + tablet: None, + })) +}); diff --git a/util/src/lib.rs b/util/src/lib.rs index ee9bfd014..a2ee22adf 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -25,6 +25,7 @@ pub mod logger; pub mod loop_context; pub mod num_ops; pub mod offsetof; +#[cfg(not(target_env = "musl"))] pub mod pixman; pub mod reader; pub mod seccomp; diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index d84ec913a..92105b037 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -24,4 +24,6 @@ util = { path = "../util" } pci = { path = "../pci" } acpi = { path = "../acpi" } devices = {path = "../devices"} -vnc = {path = "../vnc"} + +[target.'cfg(not(target_env = "musl"))'.dependencies] +vnc = { path = "../vnc" } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 6d91a5d01..cc93d43e9 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -102,6 +102,7 @@ pub mod errors { mod balloon; mod block; mod console; +#[cfg(not(target_env = "musl"))] mod gpu; mod net; mod queue; @@ -115,6 +116,7 @@ pub use balloon::*; pub use block::{Block, BlockState}; pub use console::{Console, VirtioConsoleState}; pub use errors::*; +#[cfg(not(target_env = "musl"))] pub use gpu::*; pub use net::*; pub use queue::*; diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index 7f77c4d3f..800fab2f5 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -9,7 +9,6 @@ description = "Visual Network Computing" [dependencies] byteorder = "1.3.4" error-chain = "0.12.4" -kvm-ioctls = "0.6.0" libc = ">=0.2.71" log = "0.4.8" serde_json = "1.0.55" diff --git a/vnc/src/input.rs b/vnc/src/input.rs index fcdf14530..0c4700585 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -11,11 +11,10 @@ // See the Mulan PSL v2 for more details. use crate::VncClient; -use once_cell::sync::Lazy; -use std::sync::{Arc, Mutex}; use usb::{ - keyboard::{keyboard_event, UsbKeyboard}, - tablet::{pointer_event, pointer_sync, UsbTablet}, + keyboard::keyboard_event, + tablet::{pointer_event, pointer_sync}, + INPUT, }; // Logical window size for mouse. const ABS_MAX: u64 = 0x7fff; @@ -149,14 +148,3 @@ impl VncClient { self.update_event_handler(1, VncClient::handle_protocol_msg); } } - -pub struct Input { - pub keyboard: Option>>, - pub tablet: Option>>, -} -pub static INPUT: Lazy>> = Lazy::new(|| { - Arc::new(Mutex::new(Input { - keyboard: None, - tablet: None, - })) -}); -- Gitee From 04265b9526aa502a13ae1ed985ccc97957371283 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Wed, 31 Aug 2022 09:54:30 +0800 Subject: [PATCH 0153/2187] cpu: re-arrange CPU topology options The list of CPU topology items in code are presented in a fairly arbitrary order currently. Re-arrange them so that they're ordered from largest to smallest unit, which is consistent with the -smp option. Signed-off-by: Zhao Mengmeng --- cpu/src/lib.rs | 29 ++++++++++++++------------ machine/src/micro_vm/mod.rs | 6 +++--- machine/src/standard_vm/aarch64/mod.rs | 6 +++--- machine/src/standard_vm/x86_64/mod.rs | 6 +++--- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 86e1d3d74..141cbae95 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -717,18 +717,18 @@ impl CPUThreadWorker { /// The wrapper for topology for VCPU. #[derive(Clone)] pub struct CpuTopology { + /// Number of vcpus in VM. + pub nrcpus: u8, /// Number of sockets in VM. pub sockets: u8, - /// Number of dies on one socket. + /// Number of dies in one socket. pub dies: u8, - /// Number of clusters on one socket. + /// Number of clusters in one die. pub clusters: u8, - /// Number of cores in VM. + /// Number of cores in one cluster. pub cores: u8, - /// Number of threads in VM. + /// Number of threads in one core. pub threads: u8, - /// Number of vcpus in VM. - pub nrcpus: u8, /// Number of online vcpus in VM. pub max_cpus: u8, /// Online mask number of all vcpus. @@ -737,16 +737,19 @@ pub struct CpuTopology { impl CpuTopology { /// * `nr_cpus`: Number of vcpus in one VM. - /// * `nr_threads`: Number of threads on one core. - /// * `nr_cores`: Number of cores on one socket - /// * `nr_sockets`: Number of sockets on in one VM. + /// * `nr_sockets`: Number of sockets in one VM. + /// * `nr_dies`: Number of dies in one socket. + /// * `nr_clusters`: Number of clusters in one die. + /// * `nr_cores`: Number of cores in one cluster. + /// * `nr_threads`: Number of threads in one core. + /// * `max_cpus`: Number of online vcpus in VM. pub fn new( nr_cpus: u8, - nr_threads: u8, - nr_cores: u8, + nr_sockets: u8, nr_dies: u8, nr_clusters: u8, - nr_sockets: u8, + nr_cores: u8, + nr_threads: u8, max_cpus: u8, ) -> Self { let mut mask: Vec = vec![0; max_cpus as usize]; @@ -754,12 +757,12 @@ impl CpuTopology { mask[index] = 1; }); Self { + nrcpus: nr_cpus, sockets: nr_sockets, dies: nr_dies, clusters: nr_clusters, cores: nr_cores, threads: nr_threads, - nrcpus: nr_cpus, max_cpus, online_mask: Arc::new(Mutex::new(mask)), } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c11f3c91d..37b3e8ea9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -229,11 +229,11 @@ impl LightMachine { Ok(LightMachine { cpu_topo: CpuTopology::new( vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_sockets, vm_config.machine_config.nr_dies, vm_config.machine_config.nr_clusters, - vm_config.machine_config.nr_sockets, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ), cpus: Vec::new(), diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c214e2bd9..64fb4dd98 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -151,11 +151,11 @@ impl StdMachine { let cpu_topo = CpuTopology::new( vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_sockets, vm_config.machine_config.nr_dies, vm_config.machine_config.nr_clusters, - vm_config.machine_config.nr_sockets, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 9f5621b49..8b61d958f 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -133,11 +133,11 @@ impl StdMachine { let cpu_topo = CpuTopology::new( vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_sockets, vm_config.machine_config.nr_dies, vm_config.machine_config.nr_clusters, - vm_config.machine_config.nr_sockets, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ); let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) -- Gitee From 51b0a2a9a8194f15f81fae5a0bf70df9835bef15 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 15 Sep 2022 19:14:51 +0800 Subject: [PATCH 0154/2187] usb: fix the wrong mouse wheel orientation The report value should increase as controls are rolled forward, and don't reverse it in pointer_poll. Signed-off-by: zhouli57 --- usb/src/hid.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 32bf76931..1065b5e08 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -344,22 +344,20 @@ impl Hid { self.head - 1 }; let evt = &mut self.pointer.queue[(index & QUEUE_MASK) as usize]; - let mut z = evt.pos_z; - evt.pos_z -= z; - if self.num != 0 && evt.pos_z == 0 { + let z = evt.pos_z; + evt.pos_z = 0; + if self.num != 0 { increase_queue(&mut self.head); self.num -= 1; } - z = 0 - z; - let data = vec![ + vec![ evt.button_state as u8, evt.pos_x as u8, (evt.pos_x >> 8) as u8, evt.pos_y as u8, (evt.pos_y >> 8) as u8, z as u8, - ]; - data + ] } /// USB HID device handle control packet. -- Gitee From e47a9c57be4f76dbbd1e0fa861ce8ceb775ef484 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 15 Sep 2022 19:22:05 +0800 Subject: [PATCH 0155/2187] usb: add description for keyboard The keyboard does not support led yet, the description is added in the docs. Signed-off-by: zhouli57 --- docs/config_guidebook.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index cdef72f03..2b9350b09 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -643,7 +643,7 @@ Three properties can be set for USB controller. Note: Only one USB controller can be configured, USB controller can only support USB keyboard and USB tablet. ### 2.14 USB Keyboard -The USB keyboard is a keyboard that uses the USB protocol. It should be attached to USB controller. Keypad is not supported yet. +The USB keyboard is a keyboard that uses the USB protocol. It should be attached to USB controller. Keypad and led are not supported yet. One property can be set for USB Keyboard. @@ -654,6 +654,7 @@ One property can be set for USB Keyboard. ``` Note: Only one keyboard can be configured. + ### 2.15 USB Tablet Pointer Device which uses alsolute coordinates. It should be attached to USB controller. @@ -666,6 +667,7 @@ One property can be set for USB Tablet. ``` Note: Only one tablet can be configured. + ## 3. Trace Users can specify the configuration file which lists events to trace. -- Gitee From c0c370777028a9fe63870206276cc64d813ea372 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Tue, 13 Sep 2022 15:18:51 +0800 Subject: [PATCH 0156/2187] gpu: change unrecoverable error to warn for checking of max_hostmem fix https://gitee.com/openeuler/stratovirt/issues/I5Q6J6, correct the check of virtio-gpu-pci cmdline config, add 2 tests to guard it. Signed-off-by: Zhao Mengmeng --- machine_manager/src/config/gpu.rs | 62 ++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index dead90221..180c46e5b 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use log::warn; + use super::{ errors::{ErrorKind, Result}, M, @@ -19,6 +21,8 @@ use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; /// The maximum number of scanouts. pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; +pub const VIRTIO_GPU_MAX_HOSTMEM: u64 = 256 * M; + #[derive(Clone)] pub struct GpuConfig { pub id: String, @@ -37,7 +41,7 @@ impl Default for GpuConfig { edid: true, xres: 1024, yres: 768, - max_hostmem: 256 * M, + max_hostmem: VIRTIO_GPU_MAX_HOSTMEM, } } } @@ -59,15 +63,12 @@ impl ConfigCheck for GpuConfig { .into()); } - if self.max_hostmem < 256 * M { - return Err(ErrorKind::IllegalValue( - "max_hostmem".to_string(), - 0, - false, - 256 * M as u64, - true, - ) - .into()); + if self.max_hostmem < VIRTIO_GPU_MAX_HOSTMEM { + warn!( + "max_hostmem must >= {}, allocating less than it may cause \ + the GPU to fail to start or refresh.", + VIRTIO_GPU_MAX_HOSTMEM + ); } Ok(()) @@ -111,3 +112,44 @@ pub fn parse_gpu(gpu_config: &str) -> Result { Ok(gpu_cfg) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_gpu_max_hostmem_greater_than_limit() { + let max_hostmem = VIRTIO_GPU_MAX_HOSTMEM + 1; + let gpu_cfg_cmdline = format!( + "{}{}", + "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ + max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=", + max_hostmem.to_string() + ); + + let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); + + assert!(gpu_cfg_.is_ok()); + + let gpu_cfg = gpu_cfg_.unwrap(); + assert_eq!(gpu_cfg.max_hostmem, max_hostmem); + } + + #[test] + fn test_parse_gpu_max_hostmem_less_than_limit() { + let max_hostmem = VIRTIO_GPU_MAX_HOSTMEM - 1; + let gpu_cfg_cmdline = format!( + "{}{}", + "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ + max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=", + max_hostmem.to_string() + ); + + let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); + + assert!(gpu_cfg_.is_ok()); + + let gpu_cfg = gpu_cfg_.unwrap(); + assert_eq!(gpu_cfg.max_hostmem, max_hostmem); + } +} -- Gitee From d01e9c9b48670288afb87aefc37270fefeadc48c Mon Sep 17 00:00:00 2001 From: louquan18 <2710830093@qq.com> Date: Sat, 10 Sep 2022 11:48:06 +0800 Subject: [PATCH 0157/2187] ftrace: Trace description at virtual machine startup. Added trace descriptions for some device startup parameters and added some debug traits. Description of the completed trace: trace_cpu_topo trace_sysbus trace_replaceable_info trace_vm_state trace_cpu_boot_config trace_eventnotifier trace_mmio_replaceable_config Signed-off-by: louquan18 <2710830093@qq.com> --- address_space/src/address_space.rs | 14 ++++++++-- address_space/src/host_mmap.rs | 3 +- address_space/src/region.rs | 30 ++++++++++++++++++-- cpu/src/aarch64/mod.rs | 9 +++++- cpu/src/lib.rs | 2 +- cpu/src/x86_64/mod.rs | 9 +++++- machine/src/lib.rs | 7 +++++ machine/src/micro_vm/mod.rs | 44 ++++++++++++++++++++++++++++++ machine_manager/src/config/gpu.rs | 2 +- machine_manager/src/config/mod.rs | 2 +- machine_manager/src/config/usb.rs | 3 ++ machine_manager/src/config/vfio.rs | 2 +- sysbus/src/lib.rs | 27 ++++++++++++++++++ util/src/loop_context.rs | 15 ++++++++++ 14 files changed, 158 insertions(+), 11 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 0d757092b..72ed8bae1 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -9,7 +9,7 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. - +use std::fmt; use std::io::Write; use std::sync::{Arc, Mutex}; @@ -23,7 +23,7 @@ use crate::{ }; /// Contain an array of `FlatRange`. -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub(crate) struct FlatView(pub Vec); impl FlatView { @@ -71,6 +71,16 @@ pub struct AddressSpace { ioeventfds: Arc>>, } +impl fmt::Debug for AddressSpace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AddressSpace") + .field("root", &self.root) + .field("flat_view", &self.flat_view) + .field("ioeventfds", &self.ioeventfds) + .finish() + } +} + impl AddressSpace { /// Create a new `AddressSpace` according to the given root region. /// diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index f8e7af0e0..dd0878714 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -34,7 +34,7 @@ const MPOL_MF_STRICT: u32 = 1; const MPOL_MF_MOVE: u32 = 2; /// FileBackend represents backend-file of `HostMemMapping`. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FileBackend { /// File we used to map memory. pub file: Arc, @@ -341,6 +341,7 @@ pub fn set_host_memory_policy( } /// Record information of memory mapping. +#[derive(Debug)] pub struct HostMemMapping { /// Record the range of one memory segment. address_range: AddressRange, diff --git a/address_space/src/region.rs b/address_space/src/region.rs index f90a88dc2..49d7dadd2 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -9,7 +9,7 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. - +use std::fmt; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; @@ -61,6 +61,22 @@ pub struct Region { rom_dev_romd: Arc, } +impl fmt::Debug for Region { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Region") + .field("region_type", &self.region_type) + .field("priority", &self.priority) + .field("size", &self.size) + .field("offset", &self.offset) + .field("mem_mapping", &self.mem_mapping) + .field("io_evtfds", &self.io_evtfds) + .field("space", &self.space) + .field("subregions", &self.subregions) + .field("rom_dev_romd", &self.rom_dev_romd) + .finish() + } +} + /// Used to trigger events. /// If `data_match` is enabled, the `EventFd` is triggered iff `data` is written /// to the specified address. @@ -77,6 +93,16 @@ pub struct RegionIoEventFd { pub data: u64, } +impl fmt::Debug for RegionIoEventFd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RegionIoEventFd") + .field("addr_range", &self.addr_range) + .field("data_match", &self.data_match) + .field("data_match", &self.data) + .finish() + } +} + impl RegionIoEventFd { /// Calculate if this `RegionIoEventFd` is located before the given one. /// @@ -113,7 +139,7 @@ impl RegionIoEventFd { } /// FlatRange is a piece of continuous memory address。 -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FlatRange { /// The address range. pub addr_range: AddressRange, diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 7011ce7ab..0f4d342e6 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -12,6 +12,7 @@ pub mod caps; mod core_regs; +extern crate util; use std::sync::{Arc, Mutex}; @@ -55,7 +56,7 @@ const KVM_MAX_CPREG_ENTRIES: usize = 500; /// tree blob (dtb) in system RAM. /// /// See: https://elixir.bootlin.com/linux/v5.6/source/Documentation/arm64/booting.rst -#[derive(Default, Copy, Clone)] +#[derive(Default, Copy, Clone, Debug)] pub struct ArmCPUBootConfig { pub fdt_addr: u64, pub boot_pc: u64, @@ -166,6 +167,7 @@ impl ArmCPUState { self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; } + trace_cpu_boot_config(boot_config); self.set_core_reg(boot_config); vcpu_fd @@ -294,3 +296,8 @@ impl StateTransfer for CPU { } impl MigrationHook for CPU {} + +/// The trace describes the configuration information when the cpu is booted. +fn trace_cpu_boot_config(cpu_boot_config: &ArmCPUBootConfig) { + util::ftrace!(trace_cpu_boot_config, "{:#?}", cpu_boot_config); +} diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 9e9a07014..44d24c5bf 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -716,7 +716,7 @@ impl CPUThreadWorker { } /// The wrapper for topology for VCPU. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CpuTopology { /// Number of vcpus in VM. pub nrcpus: u8, diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 6bd2d46fd..1cc2d5392 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -12,6 +12,7 @@ pub mod caps; mod cpuid; +extern crate util; use std::sync::{Arc, Mutex}; @@ -58,7 +59,7 @@ const ECX_DIE: u32 = 5u32 << 8; /// X86 CPU booting configure information #[allow(clippy::upper_case_acronyms)] -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct X86CPUBootConfig { pub prot64_mode: bool, /// Register %rip value @@ -181,6 +182,7 @@ impl X86CPUState { boot_config: &X86CPUBootConfig, ) -> Result<()> { self.setup_lapic(vcpu_fd)?; + trace_cpu_boot_config(boot_config); self.setup_regs(boot_config); self.setup_sregs(vcpu_fd, boot_config)?; self.setup_fpu(); @@ -577,6 +579,11 @@ impl StateTransfer for CPU { impl MigrationHook for CPU {} +/// The trace describes the configuration information when the cpu is booted. +fn trace_cpu_boot_config(cpu_boot_config: &X86CPUBootConfig) { + util::ftrace!(trace_cpu_boot_config, "{:#?}", cpu_boot_config); +} + #[cfg(test)] mod test { use super::*; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 057dc5d10..6259a8cf5 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -96,6 +96,7 @@ mod micro_vm; mod standard_vm; #[cfg(target_arch = "x86_64")] mod vm_state; +extern crate util; use std::collections::BTreeMap; use std::fs::remove_file; @@ -1138,6 +1139,7 @@ pub trait MachineOps { EventSet::IN, vec![power_button_handler], ); + trace_eventnotifier(¬ifier); EventLoop::update_event(vec![notifier], None).chain_err(|| ErrorKind::RegNotifierErr)?; Ok(()) @@ -1389,3 +1391,8 @@ fn start_incoming_migration(vm: &Arc>) -> Re Ok(()) } + +/// Description of the trace for eventnotifier. +fn trace_eventnotifier(eventnotifier: &EventNotifier) { + util::ftrace!(trace_eventnotifier, "{:#?}", eventnotifier); +} diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 0c3c1fadc..41d42f16f 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -57,7 +57,10 @@ pub mod errors { mod mem_layout; mod syscall; +extern crate util; +use std::fmt; +use std::fmt::Debug; use std::fs::metadata; use std::ops::Deref; use std::os::linux::fs::MetadataExt; @@ -123,6 +126,7 @@ const MMIO_REPLACEABLE_BLK_NR: usize = 4; const MMIO_REPLACEABLE_NET_NR: usize = 2; // The config of replaceable device. +#[derive(Debug)] struct MmioReplaceableConfig { // Device id. id: String, @@ -140,7 +144,18 @@ struct MmioReplaceableDevInfo { used: bool, } +impl fmt::Debug for MmioReplaceableDevInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MmioReplaceableDevInfo") + .field("device_type", &self.device.lock().unwrap().device_type()) + .field("id", &self.id) + .field("used", &self.used) + .finish() + } +} + // The gather of config, info and count of all replaceable devices. +#[derive(Debug)] struct MmioReplaceableInfo { // The arrays of all replaceable configs. configs: Arc>>, @@ -376,6 +391,8 @@ impl LightMachine { id: id.to_string(), dev_config, }; + + trace_mmio_replaceable_config(&config); configs_lock.push(config); Ok(()) } @@ -744,6 +761,11 @@ impl MachineOps for LightMachine { let mut locked_vm = vm.lock().unwrap(); + //trace for lightmachine + trace_cpu_topo(&locked_vm.cpu_topo); + trace_sysbus(&locked_vm.sysbus); + trace_vm_state(&locked_vm.vm_state); + locked_vm.init_memory( &vm_config.machine_config.mem_config, #[cfg(target_arch = "x86_64")] @@ -776,6 +798,7 @@ impl MachineOps for LightMachine { .create_replaceable_devices() .chain_err(|| "Failed to create replaceable devices.")?; locked_vm.add_devices(vm_config)?; + trace_replaceable_info(&locked_vm.replaceable_info); let migrate_info = locked_vm.get_migrate_info(); let boot_config = if migrate_info.0 == MigrateMode::Unknown { @@ -1582,3 +1605,24 @@ impl device_tree::CompileFDT for LightMachine { Ok(()) } } + +/// Trace descriptions for some devices at stratovirt startup. +fn trace_cpu_topo(cpu_topo: &CpuTopology) { + util::ftrace!(trace_cpu_topo, "{:#?}", cpu_topo); +} + +fn trace_sysbus(sysbus: &SysBus) { + util::ftrace!(trace_sysbus, "{:?}", sysbus); +} + +fn trace_replaceable_info(replaceable_info: &MmioReplaceableInfo) { + util::ftrace!(trace_replaceable_info, "{:?}", replaceable_info); +} + +fn trace_vm_state(vm_state: &Arc<(Mutex, Condvar)>) { + util::ftrace!(trace_vm_state, "{:#?}", vm_state); +} + +fn trace_mmio_replaceable_config(config: &MmioReplaceableConfig) { + util::ftrace!(trace_mmio_replaceable_config, "{:#?}", config); +} diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 180c46e5b..dc1897cea 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -23,7 +23,7 @@ pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; pub const VIRTIO_GPU_MAX_HOSTMEM: u64 = 256 * M; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct GpuConfig { pub id: String, pub max_outputs: u32, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 1fbd1b73b..65f528fd7 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -330,7 +330,7 @@ impl AsAny for T { } /// This trait is to check the legality of Config structure. -pub trait ConfigCheck: AsAny + Send + Sync { +pub trait ConfigCheck: AsAny + Send + Sync + std::fmt::Debug { /// To check the legality of Config structure. /// /// # Errors diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 8a8a85c77..543a449e5 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -17,6 +17,7 @@ use error_chain::bail; use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; /// XHCI contoller configuration. +#[derive(Debug)] pub struct XhciConfig { pub id: String, } @@ -48,6 +49,7 @@ pub fn parse_xhci(conf: &str) -> Result { Ok(dev) } +#[derive(Debug)] pub struct UsbKeyboardConfig { pub id: String, } @@ -78,6 +80,7 @@ pub fn parse_usb_keyboard(conf: &str) -> Result { Ok(dev) } +#[derive(Debug)] pub struct UsbTabletConfig { pub id: String, } diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs index 09e6363f1..757d5ac37 100644 --- a/machine_manager/src/config/vfio.rs +++ b/machine_manager/src/config/vfio.rs @@ -13,7 +13,7 @@ use super::errors::{ErrorKind, Result}; use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; -#[derive(Default)] +#[derive(Default, Debug)] pub struct VfioConfig { pub sysfsdev: String, pub host: String, diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 3dd0a4b52..2f6eea0fa 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -24,6 +24,7 @@ pub mod errors { } } +use std::fmt; use std::sync::{Arc, Mutex}; use acpi::{AmlBuilder, AmlScope}; @@ -45,6 +46,32 @@ pub struct SysBus { pub min_free_base: u64, } +#[cfg(target_arch = "x86_64")] +impl fmt::Debug for SysBus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SysBus") + .field("sys_io", &self.sys_io) + .field("sys_mem", &self.sys_mem) + .field("free_irqs", &self.free_irqs) + .field("min_free_irq", &self.min_free_irq) + .field("mmio_region", &self.mmio_region) + .field("min_free_base", &self.min_free_base) + .finish() + } +} +#[cfg(target_arch = "aarch64")] +impl fmt::Debug for SysBus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SysBus") + .field("sys_mem", &self.sys_mem) + .field("free_irqs", &self.free_irqs) + .field("min_free_irq", &self.min_free_irq) + .field("mmio_region", &self.mmio_region) + .field("min_free_base", &self.min_free_base) + .finish() + } +} + impl SysBus { pub fn new( #[cfg(target_arch = "x86_64")] sys_io: &Arc, diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index c499e2861..7f4cf4926 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::BTreeMap; +use std::fmt; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; @@ -43,6 +44,7 @@ pub enum NotifierOperation { Resume = 32, } +#[derive(Debug)] enum EventStatus { /// Event is currently monitored in epoll. Alive = 0, @@ -72,6 +74,19 @@ pub struct EventNotifier { pub io_poll: bool, } +impl fmt::Debug for EventNotifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EventNotifier") + .field("raw_fd", &self.raw_fd) + .field("op", &self.op) + .field("parked_fd", &self.parked_fd) + .field("event", &self.event) + .field("status", &self.status) + .field("io_poll", &self.io_poll) + .finish() + } +} + impl EventNotifier { /// Constructs a new `EventNotifier`. pub fn new( -- Gitee From 2c024d1e47e3c42aa4db0fea698beb8e434b941f Mon Sep 17 00:00:00 2001 From: louquan18 <2710830093@qq.com> Date: Sat, 10 Sep 2022 12:01:48 +0800 Subject: [PATCH 0158/2187] ftrace: Add trace description for virtio devices. Adds a description of the trace when the virtio device interacts with the front and back end, and abstracts a VirtioTrace trait. Signed-off-by: louquan18 <2710830093@qq.com> --- virtio/src/balloon.rs | 7 ++++++- virtio/src/block.rs | 14 +++++++++----- virtio/src/console.rs | 5 ++++- virtio/src/lib.rs | 21 +++++++++++++++++++++ virtio/src/net.rs | 8 +++++++- virtio/src/rng.rs | 8 ++++++-- 6 files changed, 53 insertions(+), 10 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 5a5b8a527..cb450e462 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -42,7 +42,7 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use super::{ errors::*, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, + VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; @@ -505,8 +505,10 @@ impl BalloonIoHandler { /// if `req_type` is `BALLOON_INFLATE_EVENT`, then inflate the balloon, otherwise, deflate the balloon. fn process_balloon_queue(&mut self, req_type: bool) -> Result<()> { let queue = if req_type { + self.trace_request("Balloon".to_string(), "to inflate".to_string()); &self.inf_queue } else { + self.trace_request("Balloon".to_string(), "to deflate".to_string()); &self.def_queue }; let mut locked_queue = queue.lock().unwrap(); @@ -527,6 +529,7 @@ impl BalloonIoHandler { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)) .chain_err(|| ErrorKind::InterruptTrigger("balloon", VirtioInterruptType::Vring))?; + self.trace_send_interrupt("Balloon".to_string()); Ok(()) } @@ -1055,6 +1058,8 @@ pub fn balloon_allow_list(syscall_allow_list: &mut Vec) { ]) } +impl VirtioTrace for BalloonIoHandler {} + #[cfg(test)] mod tests { pub use super::super::*; diff --git a/virtio/src/block.rs b/virtio/src/block.rs index dcbbb73f7..69eeed328 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -43,11 +43,11 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_BLK_F_FLUSH, - VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, - VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, - VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_BLOCK, + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, + VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; /// Number of virtqueues. @@ -413,6 +413,7 @@ impl BlockIoHandler { } fn process_queue(&mut self) -> Result { + self.trace_request("Block".to_string(), "to IO".to_string()); let mut req_queue = Vec::new(); let mut req_index = 0; let mut last_aio_req_index = 0; @@ -552,6 +553,7 @@ impl BlockIoHandler { Some(&self.queue.lock().unwrap()), ) .chain_err(|| ErrorKind::InterruptTrigger("block", VirtioInterruptType::Vring))?; + self.trace_send_interrupt("Block".to_string()); } Ok(done) @@ -1215,6 +1217,8 @@ impl StateTransfer for Block { impl MigrationHook for Block {} +impl VirtioTrace for BlockIoHandler {} + #[cfg(test)] mod tests { use super::super::*; diff --git a/virtio/src/console.rs b/virtio/src/console.rs index b48a60b79..ae12ecf28 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -33,7 +33,7 @@ use vmm_sys_util::eventfd::EventFd; use super::errors::{ErrorKind, Result, ResultExt}; use super::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_CONSOLE_F_SIZE, + Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; @@ -149,6 +149,7 @@ impl InputReceiver for ConsoleHandler { impl ConsoleHandler { fn output_handle(&mut self) { + self.trace_request("Console".to_string(), "to IO".to_string()); let mut queue_lock = self.output_queue.lock().unwrap(); let mut buffer = [0_u8; 4096]; @@ -464,6 +465,8 @@ impl StateTransfer for Console { impl MigrationHook for Console {} +impl VirtioTrace for ConsoleHandler {} + #[cfg(test)] mod tests { pub use super::super::*; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index cc93d43e9..b87b366d7 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -111,6 +111,7 @@ mod vhost; mod virtio_mmio; #[allow(dead_code)] mod virtio_pci; +extern crate util; pub use balloon::*; pub use block::{Block, BlockState}; @@ -339,3 +340,23 @@ pub trait VirtioDevice: Send { Ok(()) } } + +/// The trait for trace descriptions of virtio device interactions +/// on the front and back ends. +pub trait VirtioTrace { + fn trace_request(&self, device: String, behaviour: String) { + util::ftrace!( + trace_request, + "{} : Request received from Guest {}, ready to start processing.", + device, + behaviour + ); + } + fn trace_send_interrupt(&self, device: String) { + util::ftrace!( + trace_send_interrupt, + "{} : stratovirt processing complete, ready to send interrupt to guest.", + device + ); + } +} diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 1bb03b511..09c6045fd 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -36,7 +36,7 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::errors::{ErrorKind, Result, ResultExt}; use super::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, + Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_VQ, @@ -284,6 +284,7 @@ struct NetIoHandler { impl NetIoHandler { fn handle_rx(&mut self) -> Result<()> { + self.trace_request("Net".to_string(), "to rx".to_string()); let mut queue = self.rx.queue.lock().unwrap(); while let Some(tap) = self.tap.as_mut() { if queue.vring.avail_ring_len(&self.mem_space)? == 0 { @@ -353,12 +354,14 @@ impl NetIoHandler { self.rx.need_irqs = false; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) .chain_err(|| ErrorKind::InterruptTrigger("net", VirtioInterruptType::Vring))?; + self.trace_send_interrupt("Net".to_string()); } Ok(()) } fn handle_tx(&mut self) -> Result<()> { + self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); let mut need_irq = false; @@ -406,6 +409,7 @@ impl NetIoHandler { if need_irq { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) .chain_err(|| ErrorKind::InterruptTrigger("net", VirtioInterruptType::Vring))?; + self.trace_send_interrupt("Net".to_string()); } Ok(()) @@ -1072,6 +1076,8 @@ impl StateTransfer for Net { impl MigrationHook for Net {} +impl VirtioTrace for NetIoHandler {} + #[cfg(test)] mod tests { pub use super::super::*; diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 4b3770cc2..0a3226c50 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -35,8 +35,8 @@ use vmm_sys_util::eventfd::EventFd; use super::errors::{ErrorKind, Result, ResultExt}; use super::{ - ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_RNG, + ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; const QUEUE_NUM_RNG: usize = 1; @@ -79,6 +79,7 @@ impl RngHandler { } fn process_queue(&mut self) -> Result<()> { + self.trace_request("Rng".to_string(), "to IO".to_string()); let mut queue_lock = self.queue.lock().unwrap(); let mut need_interrupt = false; @@ -127,6 +128,7 @@ impl RngHandler { if need_interrupt { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock)) .chain_err(|| ErrorKind::InterruptTrigger("rng", VirtioInterruptType::Vring))?; + self.trace_send_interrupt("Rng".to_string()); } Ok(()) @@ -415,6 +417,8 @@ impl StateTransfer for Rng { impl MigrationHook for Rng {} +impl VirtioTrace for RngHandler {} + #[cfg(test)] mod tests { use super::super::*; -- Gitee From 9c363304f037bb328a0ab708ca6139cfb5fe843e Mon Sep 17 00:00:00 2001 From: louquan18 <2710830093@qq.com> Date: Sat, 17 Sep 2022 22:11:41 +0800 Subject: [PATCH 0159/2187] ftrace: Correct the location of trace Merge trace_cpu_boot_config for x86,arm. Change the location and content of trace_cpu_topo. Signed-off-by: louquan18 <2710830093@qq.com> --- address_space/src/region.rs | 2 +- cpu/src/aarch64/mod.rs | 9 +-------- cpu/src/lib.rs | 7 ++++++- cpu/src/x86_64/mod.rs | 9 +-------- machine/src/micro_vm/mod.rs | 4 ++-- sysbus/src/lib.rs | 19 +++++++++---------- 6 files changed, 20 insertions(+), 30 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 49d7dadd2..91d05c197 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -98,7 +98,7 @@ impl fmt::Debug for RegionIoEventFd { f.debug_struct("RegionIoEventFd") .field("addr_range", &self.addr_range) .field("data_match", &self.data_match) - .field("data_match", &self.data) + .field("data", &self.data) .finish() } } diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 0f4d342e6..7d74b8616 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -12,7 +12,6 @@ pub mod caps; mod core_regs; -extern crate util; use std::sync::{Arc, Mutex}; @@ -63,7 +62,7 @@ pub struct ArmCPUBootConfig { } #[allow(dead_code)] -#[derive(Default, Copy, Clone)] +#[derive(Default, Copy, Clone, Debug)] pub struct ArmCPUTopology { threads: u8, cores: u8, @@ -167,7 +166,6 @@ impl ArmCPUState { self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; } - trace_cpu_boot_config(boot_config); self.set_core_reg(boot_config); vcpu_fd @@ -296,8 +294,3 @@ impl StateTransfer for CPU { } impl MigrationHook for CPU {} - -/// The trace describes the configuration information when the cpu is booted. -fn trace_cpu_boot_config(cpu_boot_config: &ArmCPUBootConfig) { - util::ftrace!(trace_cpu_boot_config, "{:#?}", cpu_boot_config); -} diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 44d24c5bf..21a51a6a3 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -291,6 +291,7 @@ impl CPU { impl CPUInterface for CPU { fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()> { + trace_cpu_boot_config(boot); let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { return Err( @@ -716,7 +717,7 @@ impl CPUThreadWorker { } /// The wrapper for topology for VCPU. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct CpuTopology { /// Number of vcpus in VM. pub nrcpus: u8, @@ -814,6 +815,10 @@ impl CpuTopology { } } +fn trace_cpu_boot_config(cpu_boot_config: &CPUBootConfig) { + util::ftrace!(trace_CPU_boot_config, "{:#?}", cpu_boot_config); +} + #[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 1cc2d5392..bbd7c63a4 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -12,7 +12,6 @@ pub mod caps; mod cpuid; -extern crate util; use std::sync::{Arc, Mutex}; @@ -81,7 +80,7 @@ pub struct X86CPUBootConfig { } #[allow(clippy::upper_case_acronyms)] -#[derive(Default, Copy, Clone)] +#[derive(Default, Copy, Clone, Debug)] pub struct X86CPUTopology { threads: u8, cores: u8, @@ -182,7 +181,6 @@ impl X86CPUState { boot_config: &X86CPUBootConfig, ) -> Result<()> { self.setup_lapic(vcpu_fd)?; - trace_cpu_boot_config(boot_config); self.setup_regs(boot_config); self.setup_sregs(vcpu_fd, boot_config)?; self.setup_fpu(); @@ -579,11 +577,6 @@ impl StateTransfer for CPU { impl MigrationHook for CPU {} -/// The trace describes the configuration information when the cpu is booted. -fn trace_cpu_boot_config(cpu_boot_config: &X86CPUBootConfig) { - util::ftrace!(trace_cpu_boot_config, "{:#?}", cpu_boot_config); -} - #[cfg(test)] mod test { use super::*; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 41d42f16f..6437a1fbd 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -762,7 +762,6 @@ impl MachineOps for LightMachine { let mut locked_vm = vm.lock().unwrap(); //trace for lightmachine - trace_cpu_topo(&locked_vm.cpu_topo); trace_sysbus(&locked_vm.sysbus); trace_vm_state(&locked_vm.vm_state); @@ -811,6 +810,7 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, )); + trace_cpu_topo(&topology); locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, @@ -1607,7 +1607,7 @@ impl device_tree::CompileFDT for LightMachine { } /// Trace descriptions for some devices at stratovirt startup. -fn trace_cpu_topo(cpu_topo: &CpuTopology) { +fn trace_cpu_topo(cpu_topo: &CPUTopology) { util::ftrace!(trace_cpu_topo, "{:#?}", cpu_topo); } diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 2f6eea0fa..5d286f476 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -46,29 +46,28 @@ pub struct SysBus { pub min_free_base: u64, } -#[cfg(target_arch = "x86_64")] impl fmt::Debug for SysBus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SysBus") + #[cfg(target_arch = "x86_64")] + let debug = f + .debug_struct("SysBus") .field("sys_io", &self.sys_io) .field("sys_mem", &self.sys_mem) .field("free_irqs", &self.free_irqs) .field("min_free_irq", &self.min_free_irq) .field("mmio_region", &self.mmio_region) .field("min_free_base", &self.min_free_base) - .finish() - } -} -#[cfg(target_arch = "aarch64")] -impl fmt::Debug for SysBus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SysBus") + .finish(); + #[cfg(target_arch = "aarch64")] + let debug = f + .debug_struct("SysBus") .field("sys_mem", &self.sys_mem) .field("free_irqs", &self.free_irqs) .field("min_free_irq", &self.min_free_irq) .field("mmio_region", &self.mmio_region) .field("min_free_base", &self.min_free_base) - .finish() + .finish(); + debug } } -- Gitee From 7cc4d9595de453b60af6f02a9640c364ed467178 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Wed, 14 Sep 2022 02:01:24 +0000 Subject: [PATCH 0160/2187] update util/src/logger.rs. change default log level from error to info Signed-off-by: wubinfeng --- util/src/logger.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/util/src/logger.rs b/util/src/logger.rs index b653b77e3..a58c485fa 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -56,8 +56,8 @@ impl Log for VmLogger { let pid = unsafe { libc::getpid() }; let tid = gettid(); - self.handler.as_ref().map(|writer| match record.level() { - Level::Error => writer.lock().unwrap().write_fmt(format_args!( + self.handler.as_ref().map(|writer| { + writer.lock().unwrap().write_fmt(format_args!( "{:<5}: [{}][{}][{}: {}]:{}: {}\n", format_now(), pid, @@ -66,15 +66,7 @@ impl Log for VmLogger { record.line().unwrap_or(0), record.level(), record.args() - )), - _ => writer.lock().unwrap().write_fmt(format_args!( - "{:<5}: [{}][{}]:{}: {}\n", - format_now(), - pid, - tid, - record.level(), - record.args() - )), + )) }); } } @@ -98,13 +90,14 @@ fn init_vm_logger( pub fn init_logger_with_env(logfile: Option>) -> Result<(), SetLoggerError> { let level = match std::env::var("STRATOVIRT_LOG_LEVEL") { Ok(l) => match l.to_lowercase().as_str() { - "trace" => Level::Trace, - "debug" => Level::Debug, - "info" => Level::Info, + "error" => Level::Error, "warn" => Level::Warn, - _ => Level::Error, + "info" => Level::Info, + "debug" => Level::Debug, + "trace" => Level::Trace, + _ => Level::Info, }, - _ => Level::Error, + _ => Level::Info, }; init_vm_logger(Some(level), logfile)?; -- Gitee From 795fb66754c81f8d758096c940af1cb28fefa986 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:05:05 +0800 Subject: [PATCH 0161/2187] VNC: Modify Cargo.toml of vnc and add serveral methods for updating the vnc display Ramfb is one of the devices that depend on vnc. Delete devices dependency in Cargo.toml of vnc to avoid circular dependency. For some display devices like ramfb, it depends on the timer to update the vnc display. Add a method of using a timer to cyclically update the vnc display for display devices and a method of getting intervals to decide when to update the next frame image. Signed-off-by: Jinhao Gao --- vnc/Cargo.toml | 3 +-- vnc/src/server.rs | 21 +++++++++++++++++++-- vnc/src/vnc.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index 800fab2f5..fc270d433 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -25,5 +25,4 @@ migration_derive = { path = "../migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } pci = { path = "../pci" } -acpi = { path = "../acpi" } -devices = {path = "../devices"} \ No newline at end of file +acpi = { path = "../acpi" } \ No newline at end of file diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 61dbbe802..f0afe7faf 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -37,6 +37,7 @@ use crate::{ bytes_per_pixel, data::keycode::KEYSYM2KEYCODE, get_image_data, get_image_format, get_image_height, get_image_stride, get_image_width, round_up_div, unref_pixman_image, update_client_surface, AuthState, DisplayMouse, SubAuthState, VncClient, DIRTY_PIXELS_NUM, + DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, }; @@ -105,6 +106,8 @@ pub struct VncServer { conn_limits: usize, /// Width of current image. pub true_width: i32, + /// updating interval of display devices. + pub update_interval: u32, } unsafe impl Send for VncServer {} @@ -132,6 +135,7 @@ impl VncServer { mask: None, conn_limits: 1, true_width: 0, + update_interval: 0, } } @@ -447,9 +451,22 @@ fn vnc_refresh() { return; } - let dirty_num = server.lock().unwrap().update_server_image(); + let mut locked_server = server.lock().unwrap(); + let dirty_num = locked_server.update_server_image(); + if dirty_num != 0 { + locked_server.update_interval /= 2; + if locked_server.update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { + locked_server.update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT + } + } else { + locked_server.update_interval += DISPLAY_UPDATE_INTERVAL_INC; + if locked_server.update_interval > DISPLAY_UPDATE_INTERVAL_MAX { + locked_server.update_interval = DISPLAY_UPDATE_INTERVAL_MAX; + } + } + let mut _rects: i32 = 0; - for client in server.lock().unwrap().clients.values_mut() { + for client in locked_server.clients.values_mut() { _rects += client.lock().unwrap().get_rects(dirty_num); } } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index f24b21a27..38f7c77a2 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -50,6 +50,10 @@ pub const VNC_BITMAP_WIDTH: u64 = const DEFAULT_REFRESH_INTERVAL: u64 = 30; const BIT_PER_BYTE: u32 = 8; +const MILLI_PER_SEC: u64 = 1_000_000; +pub const DISPLAY_UPDATE_INTERVAL_DEFAULT: u32 = 30; +pub const DISPLAY_UPDATE_INTERVAL_INC: u32 = 50; +pub const DISPLAY_UPDATE_INTERVAL_MAX: u32 = 3_000; /// Struct to record image information #[derive(Clone, Copy)] @@ -237,6 +241,30 @@ pub fn vnc_display_update(x: i32, y: i32, w: i32, h: i32) { set_area_dirty(&mut locked_server.guest_dirtymap, x, y, w, h, g_w, g_h); } +fn vnc_get_display_update_interval() -> u32 { + if VNC_SERVERS.lock().unwrap().is_empty() { + return DISPLAY_UPDATE_INTERVAL_DEFAULT; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let locked_server = server.lock().unwrap(); + + locked_server.update_interval +} + +pub fn vnc_loop_update_display(x: i32, y: i32, width: i32, height: i32) { + let func = Box::new(move || { + vnc_display_update(x, y, width as i32, height as i32); + vnc_loop_update_display(x, y, width, height); + }); + + if let Some(ctx) = EventLoop::get_ctx(None) { + ctx.delay_call( + func, + vnc_get_display_update_interval() as u64 * MILLI_PER_SEC, + ); + } +} + /// Get the width of image. pub fn vnc_width(width: i32) -> i32 { cmp::min( -- Gitee From 9229498cab0f62142f78554679761404bf46c598 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:17:04 +0800 Subject: [PATCH 0162/2187] Sysbus: Add attaching dynamic devices method and the Ramfb SysBusDevType Some dynamic devices which attach to sysbus like ramfb don't have their own memory layout, which means that the attach_device() method doesn't support attach dynamic devices. Add a method to attach dynamic devices to sysbus. Add the Ramfb SysBusDevType. Signed-off-by: Jinhao Gao --- sysbus/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 3dd0a4b52..02b69068a 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -144,6 +144,14 @@ impl SysBus { self.devices.push(dev.clone()); Ok(()) } + + pub fn attach_dynamic_device( + &mut self, + dev: &Arc>, + ) -> Result<()> { + self.devices.push(dev.clone()); + Ok(()) + } } #[derive(Copy, Clone)] @@ -173,6 +181,7 @@ pub enum SysBusDevType { PL011, FwCfg, Flash, + Ramfb, Others, } -- Gitee From 429a1adbab140dd52f36b259f0cc1bd53eb477e4 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:42:27 +0800 Subject: [PATCH 0163/2187] FwCfg: Modify FwCfgWriteCallbackType and trait FwCfgWriteCallback For some FwCfgWriteCallbackType, they may need to be modified when write_callback() is called. Add that data as a new parameter to write_callback(). Signed-off-by: Jinhao Gao --- devices/src/legacy/fwcfg.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index a77f3047f..83b3c6caa 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -131,7 +131,7 @@ fn get_key_name(key: usize) -> &'static str { /// FwCfg select callback and write callback type definition type FwCfgCallbackType = Arc; -type FwCfgWriteCallbackType = Arc; +type FwCfgWriteCallbackType = Arc>; /// FwCfg select callback pub trait FwCfgCallback { @@ -140,7 +140,7 @@ pub trait FwCfgCallback { /// FwCfg write callback pub trait FwCfgWriteCallback { - fn write_callback(&self, start: u64, len: usize); + fn write_callback(&mut self, data: Vec, start: u64, len: usize); } /// The FwCfgEntry type which holds the firmware item @@ -646,7 +646,11 @@ impl FwCfgCommon { dma.control |= FW_CFG_DMA_CTL_ERROR; } else if true { if let Some(cb) = &entry.write_cb { - cb.write_callback(offset as u64, len as usize); + cb.lock().unwrap().write_callback( + data.to_vec(), + offset as u64, + len as usize, + ); } } } -- Gitee From 6edca21bbd82883f608ad5add8d7a108559b8d15 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 14 Sep 2022 10:54:24 +0800 Subject: [PATCH 0164/2187] Devices: Support ramfb display device Ramfb is a very simple framebuffer display device. It is intended to be configured by the firmware and used as boot framebuffer, until the guest OS loads a real GPU driver. The framebuffer memory is allocated from guest RAM and initialized via the firmware config interface (fw_cfg). Signed-off-by: Jinhao Gao --- devices/Cargo.toml | 2 + devices/src/legacy/mod.rs | 4 + devices/src/legacy/ramfb.rs | 234 +++++++++++++++++++++++++ machine/src/lib.rs | 8 + machine/src/standard_vm/aarch64/mod.rs | 15 +- 5 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 devices/src/legacy/ramfb.rs diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 14b5d2390..28c78f4f8 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -13,6 +13,7 @@ kvm-ioctls = ">=0.11.0" serde = { version = "1.0", features = ["derive"] } vmm-sys-util = ">=0.10.0" byteorder = "1.4.3" +drm-fourcc = ">=2.2.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } @@ -22,6 +23,7 @@ migration_derive = { path = "../migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } acpi = { path = "../acpi" } +vnc = { path = "../vnc" } [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index bd4d9474e..9072af0a9 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -94,6 +94,8 @@ mod pl011; #[cfg(target_arch = "aarch64")] mod pl031; #[allow(dead_code)] +mod ramfb; +#[allow(dead_code)] #[cfg(target_arch = "x86_64")] mod rtc; mod serial; @@ -111,4 +113,6 @@ pub use pflash::PFlash; pub use pl011::PL011; #[cfg(target_arch = "aarch64")] pub use pl031::PL031; +#[cfg(target_arch = "aarch64")] +pub use ramfb::Ramfb; pub use serial::{Serial, SERIAL_ADDR}; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs new file mode 100644 index 000000000..efcac7ad2 --- /dev/null +++ b/devices/src/legacy/ramfb.rs @@ -0,0 +1,234 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; +use crate::legacy::errors::{Result, ResultExt}; +use acpi::AmlBuilder; +use address_space::{AddressSpace, GuestAddress}; +use drm_fourcc::DrmFourcc; +use log::error; +use migration_derive::ByteCode; +use std::mem::size_of; +use std::sync::{Arc, Mutex}; +use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; +use util::byte_code::ByteCode; +use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; +use vnc::{vnc_display_switch, vnc_loop_update_display, DisplaySurface}; + +const BYTES_PER_PIXELS: u32 = 8; +const WIDTH_MAX: u32 = 16_000; +const HEIGHT_MAX: u32 = 12_000; + +#[repr(packed)] +#[derive(ByteCode, Clone, Copy)] +struct RamfbCfg { + addr: u64, + fourcc: u32, + flags: u32, + width: u32, + height: u32, + stride: u32, +} + +impl RamfbCfg { + pub fn new() -> Self { + Self { + ..Default::default() + } + } +} + +impl AmlBuilder for RamfbCfg { + fn aml_bytes(&self) -> Vec { + self.as_bytes().to_vec() + } +} + +#[derive(Clone)] +pub struct RamfbState { + pub surface: Option, + cfg: RamfbCfg, + sys_mem: Arc, +} + +unsafe impl Sync for RamfbState {} +unsafe impl Send for RamfbState {} + +impl RamfbState { + pub fn new(sys_mem: Arc) -> Self { + Self { + surface: None, + cfg: RamfbCfg::new(), + sys_mem, + } + } + + pub fn setup(&mut self, fw_cfg: &Arc>) -> Result<()> { + let mut locked_fw_cfg = fw_cfg.lock().unwrap(); + let ramfb_state_cb = self.clone(); + locked_fw_cfg + .add_file_callback_entry( + "etc/ramfb", + self.cfg.clone().aml_bytes(), + None, + Some(Arc::new(Mutex::new(ramfb_state_cb))), + true, + ) + .chain_err(|| "Failed to set fwcfg")?; + Ok(()) + } + + fn create_display_surface( + &mut self, + width: u32, + height: u32, + format: pixman_format_code_t, + mut stride: u32, + addr: u64, + ) { + if width < 16 || height < 16 || width > WIDTH_MAX || height > HEIGHT_MAX { + error!("The resolution: {}x{} is unsupported.", width, height); + } + + if stride == 0 { + let linesize = width * pixman_format_bpp(format as u32) as u32 / BYTES_PER_PIXELS; + stride = linesize; + } + + let fb_addr = match self.sys_mem.get_host_address(GuestAddress(addr)) { + Some(addr) => addr, + None => { + error!("Failed to get the host address of the framebuffer"); + return; + } + }; + + let mut ds = vnc::DisplaySurface { + format, + ..Default::default() + }; + // pixman_image_create_bits() is C function, it's an unsafe function. + unsafe { + ds.image = pixman_image_create_bits( + format, + width as i32, + height as i32, + fb_addr as *mut u32, + stride as i32, + ); + } + self.surface = Some(ds); + } + + fn reset_ramfb_state(&mut self) { + self.surface = None; + self.cfg = RamfbCfg::new(); + } +} + +impl FwCfgWriteCallback for RamfbState { + fn write_callback(&mut self, data: Vec, _start: u64, _len: usize) { + let addr = u64::from_be_bytes( + data.as_slice() + .split_at(size_of::()) + .0 + .try_into() + .unwrap(), + ); + let fourcc = u32::from_be_bytes( + data.as_slice()[8..] + .split_at(size_of::()) + .0 + .try_into() + .unwrap(), + ); + let width = u32::from_be_bytes( + data.as_slice()[16..] + .split_at(size_of::()) + .0 + .try_into() + .unwrap(), + ); + let height = u32::from_be_bytes( + data.as_slice()[20..] + .split_at(size_of::()) + .0 + .try_into() + .unwrap(), + ); + let stride = u32::from_be_bytes( + data.as_slice()[24..] + .split_at(size_of::()) + .0 + .try_into() + .unwrap(), + ); + + let format: pixman_format_code_t; + if fourcc == DrmFourcc::Xrgb8888 as u32 { + format = pixman_format_code_t::PIXMAN_x8r8g8b8; + } else { + error!("Unsupported drm format: {}", fourcc); + return; + } + + self.create_display_surface(width, height, format, stride, addr); + + vnc_display_switch(&mut self.surface.unwrap()); + vnc_loop_update_display(0, 0, width as i32, height as i32); + } +} + +pub struct Ramfb { + pub ramfb_state: RamfbState, +} + +impl Ramfb { + pub fn new(sys_mem: Arc) -> Self { + Ramfb { + ramfb_state: RamfbState::new(sys_mem), + } + } + + pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_dynamic_device(&dev)?; + Ok(()) + } +} + +impl SysBusDevOps for Ramfb { + fn read(&mut self, _data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool { + error!("Ramfb can not be read!"); + false + } + + fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { + error!("Ramfb can not be writed!"); + false + } + + fn get_type(&self) -> SysBusDevType { + SysBusDevType::Ramfb + } + + fn reset(&mut self) -> SysBusResult<()> { + self.ramfb_state.reset_ramfb_state(); + Ok(()) + } +} + +impl AmlBuilder for Ramfb { + fn aml_bytes(&self) -> Vec { + Vec::new() + } +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 057dc5d10..d22bb2ead 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1085,6 +1085,10 @@ pub trait MachineOps { "virtio-gpu-pci" => { self.add_virtio_pci_gpu(cfg_args)?; } + #[cfg(not(target_env = "musl"))] + "ramfb" => { + self.add_ramfb()?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } @@ -1098,6 +1102,10 @@ pub trait MachineOps { bail!("Pflash device is not supported!"); } + fn add_ramfb(&mut self) -> Result<()> { + bail!("ramfb device is not supported!"); + } + /// Return the syscall whitelist for seccomp. fn syscall_whitelist(&self) -> Vec; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 6460524d5..268b22a85 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -37,8 +37,10 @@ use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::{ - errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, + errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, Ramfb, PL011, + PL031, }; + use devices::{ICGICConfig, ICGICv3Config, InterruptController}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ @@ -458,13 +460,13 @@ impl MachineOps for StdMachine { locked_vm .init_pci_host() .chain_err(|| StdErrorKind::InitPCIeHostErr)?; + let fwcfg = locked_vm.add_fwcfg_device()?; locked_vm .add_devices(vm_config) .chain_err(|| "Failed to add devices")?; #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) .chain_err(|| "Failed to init VNC server!")?; - let fwcfg = locked_vm.add_fwcfg_device()?; let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { @@ -551,6 +553,15 @@ impl MachineOps for StdMachine { Ok(()) } + fn add_ramfb(&mut self) -> Result<()> { + let sys_mem = self.get_sys_mem(); + let mut ramfb = Ramfb::new(sys_mem.clone()); + let locked_fwcfg = self.get_fwcfg_dev()?; + ramfb.ramfb_state.setup(&locked_fwcfg)?; + ramfb.realize(&mut self.sysbus)?; + Ok(()) + } + fn run(&self, paused: bool) -> Result<()> { ::vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) } -- Gitee From 5cb78d4833f758f58d6c43d2f2bf6c728bdf4358 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Sep 2022 19:42:51 +0800 Subject: [PATCH 0165/2187] Cargo.lock: Add drm-fourcc dependency and some other package Ramfb depends on drm-fourcc package. Add that package and some other package which previous commits depend on. Signed-off-by: Jinhao Gao --- Cargo.lock | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6278b34a2..984e36da9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,7 @@ dependencies = [ "vfio", "virtio", "vmm-sys-util", + "vnc", ] [[package]] @@ -66,6 +67,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + [[package]] name = "arc-swap" version = "1.5.1" @@ -99,6 +109,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitintr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f80c754495ef07b4554134bbfb23fc1e31a775113b49acccbe35076d57cba145" + [[package]] name = "boot_loader" version = "2.2.0" @@ -132,6 +148,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "const_format" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "cpu" version = "2.2.0" @@ -157,6 +193,7 @@ dependencies = [ "acpi", "address_space", "byteorder", + "drm-fourcc", "error-chain", "hypervisor", "kvm-bindings", @@ -171,8 +208,15 @@ dependencies = [ "sysbus", "util", "vmm-sys-util", + "vnc", ] +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + [[package]] name = "error-chain" version = "0.12.4" @@ -306,6 +350,7 @@ dependencies = [ "vfio-bindings", "virtio", "vmm-sys-util", + "vnc", ] [[package]] @@ -465,6 +510,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -542,6 +604,30 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "sscanf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40f3891ecd1b4d716fd674bf238ee743d2cd948c9aeb3b42b1dd9d8f56d0154b" +dependencies = [ + "const_format", + "lazy_static", + "regex", + "sscanf_macro", +] + +[[package]] +name = "sscanf_macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95d18335301db6e21d35b955cfb58327f3ede99f831a84cad5a07a59c6b1ec5d" +dependencies = [ + "proc-macro2", + "quote", + "regex-syntax", + "syn", +] + [[package]] name = "strum" version = "0.20.0" @@ -595,6 +681,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "usb" version = "2.2.0" @@ -676,6 +768,7 @@ dependencies = [ "sysbus", "util", "vmm-sys-util", + "vnc", ] [[package]] @@ -688,6 +781,31 @@ dependencies = [ "libc", ] +[[package]] +name = "vnc" +version = "2.1.0" +dependencies = [ + "acpi", + "address_space", + "bitintr", + "byteorder", + "error-chain", + "hypervisor", + "libc", + "log", + "machine_manager", + "migration", + "migration_derive", + "once_cell", + "pci", + "serde_json", + "sscanf", + "sysbus", + "usb", + "util", + "vmm-sys-util", +] + [[package]] name = "winapi" version = "0.3.9" -- Gitee From 0d68f13943d5b4c4bde1790a21fad7458359236d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 20 Sep 2022 16:53:36 +0800 Subject: [PATCH 0166/2187] Fix: Disable ramfb in musl binaries Disable ramfb in musl binaries as vnc which ramfb depends on is disabled in musl binaries. Signed-off-by: Jinhao Gao --- devices/Cargo.toml | 1 + devices/src/legacy/mod.rs | 2 ++ machine/src/standard_vm/aarch64/mod.rs | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 28c78f4f8..a8599025d 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -23,6 +23,7 @@ migration_derive = { path = "../migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } acpi = { path = "../acpi" } +[target.'cfg(not(target_env = "musl"))'.dependencies] vnc = { path = "../vnc" } [dev-dependencies] diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 9072af0a9..db20ea7b5 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -94,6 +94,7 @@ mod pl011; #[cfg(target_arch = "aarch64")] mod pl031; #[allow(dead_code)] +#[cfg(not(target_env = "musl"))] mod ramfb; #[allow(dead_code)] #[cfg(target_arch = "x86_64")] @@ -114,5 +115,6 @@ pub use pl011::PL011; #[cfg(target_arch = "aarch64")] pub use pl031::PL031; #[cfg(target_arch = "aarch64")] +#[cfg(not(target_env = "musl"))] pub use ramfb::Ramfb; pub use serial::{Serial, SERIAL_ADDR}; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 268b22a85..5fcfa267f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -36,9 +36,10 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; +#[cfg(not(target_env = "musl"))] +use devices::legacy::Ramfb; use devices::legacy::{ - errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, Ramfb, PL011, - PL031, + errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, }; use devices::{ICGICConfig, ICGICv3Config, InterruptController}; @@ -553,6 +554,7 @@ impl MachineOps for StdMachine { Ok(()) } + #[cfg(not(target_env = "musl"))] fn add_ramfb(&mut self) -> Result<()> { let sys_mem = self.get_sys_mem(); let mut ramfb = Ramfb::new(sys_mem.clone()); -- Gitee From f106c83d540c4ff3f542e83480f30b17a05095ed Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 20 Sep 2022 20:43:11 +0800 Subject: [PATCH 0167/2187] update software version in 3rd license notice --- ...Third_Party_Open_Source_Software_Notice.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 4712cf535..69ec8eb4f 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -241,7 +241,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -Software: log 0.4.14 +Software: log 0.4.17 Copyright notice: Copyright (c) 2014 The Rust Project Developers Copyright 2014-2015 The Rust Project Developers @@ -282,14 +282,14 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to -Software: serde 1.0.125 +Software: serde 1.0.143 Copyright notice: Copyright (c) David Tolnay Copyright (c) Erick Tryzelaar License: MIT or Apache License Version 2.0 Please see above. -Software: serde_json 1.0.64 +Software: serde_json 1.0.143 Copyright notice: Copyright (c) David Tolnay Copyright (c) Erick Tryzelaar @@ -302,7 +302,7 @@ Copyright (c) 2017 The Error-Chain Project Developers License: MIT or Apache License Version 2.0 Please see above. -Software: vmm-sys-util 0.7.0 +Software: vmm-sys-util 0.10.0 Copyright notice: Copyright 2019 Intel Corporation. All Rights Reserved. Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -344,7 +344,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Software: kvm-ioctls 0.6.1 +Software: kvm-ioctls 0.11.0 Copyright notice: Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. Portions Copyright 2017 The Chromium OS Authors. All rights reserved. @@ -353,13 +353,13 @@ Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: MIT or Apache License Version 2.0 Please see above. -Software: kvm-bindings 0.3.1 +Software: kvm-bindings 0.5.0 Copyright notice: Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: The APACHE 2.0 License Please see above. -Software: arc-swap 0.4.8 +Software: arc-swap 1.5.1 Copyright notice: Copyright (c) 2017 arc-swap developers License: MIT or Apache License Version 2.0 @@ -371,44 +371,44 @@ Copyright (c) 2018 Tom Parker-Shemilt License: MIT Please see above. -Software: syn 1.0.72 +Software: syn 1.0.99 Copyright notice: Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: quote 1.0.7 +Software: quote 1.0.21 Copyright notice: Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: proc-macro2 1.0 +Software: proc-macro2 1.0.43 Copyright notice: Copyright (c) David Tolnay Copyright (c) Alex Crichton License: MIT or Apache License Version 2.0 Please see above. -Software: strum 0.20 +Software: strum 0.20.0 Copyright notice: Copyright (c) 2019 Peter Glotfelty License: MIT Please see above. -Software: strum_macros 0.20 +Software: strum_macros 0.20.1 Copyright notice: Copyright (c) 2019 Peter Glotfelty License: MIT Please see above. -Software: vfio-bindings 0.2.0 +Software: vfio-bindings 0.3.1 Copyright notice: Copyright (c) 2019 Intel Corporation. All Rights Reserved. License: Apache License Version 2.0 or BSD 3-Clause License Please see above. -Software: once_cell 1.9.0 +Software: once_cell 1.13.0 Copyright notice: Copyright (c) Aleksey Kladov License: MIT OR Apache-2.0 -- Gitee From 455009e16fc90519b7511e889dd883c7865a7e02 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:17:07 +0800 Subject: [PATCH 0168/2187] add new device: vhost-user-blk-pci support multi queue feature, hot-plug and reconnect is not supported for now. Signed-off-by: Xiaowei Xue --- machine/src/lib.rs | 36 ++- machine_manager/src/config/drive.rs | 49 +++- virtio/src/lib.rs | 13 ++ virtio/src/vhost/user/block.rs | 337 ++++++++++++++++++++++++++++ virtio/src/vhost/user/message.rs | 2 + 5 files changed, 432 insertions(+), 5 deletions(-) create mode 100644 virtio/src/vhost/user/block.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 057dc5d10..5f7d11eaa 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -126,10 +126,10 @@ use machine_manager::config::parse_gpu; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_virtconsole, parse_virtio_serial, - parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, - NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, - VmConfig, FAST_UNPLUG_ON, + parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, + parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, + MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, + SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::{ event_loop::EventLoop, @@ -675,6 +675,31 @@ pub trait MachineOps { Ok(()) } + fn add_vhost_user_blk_pci(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let device_cfg = parse_vhost_user_blk_pci(vm_config, cfg_args)?; + let device: Arc> = Arc::new(Mutex::new(VhostUser::Block::new( + &device_cfg, + self.get_sys_mem(), + ))); + let pci_dev = self + .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, true) + .chain_err(|| { + format!( + "Failed to add virtio pci device, device id: {}", + &device_cfg.id + ) + })?; + if let Some(bootindex) = device_cfg.boot_index { + if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { + self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); + } + } + self.reset_bus(&device_cfg.id)?; + Ok(()) + } + fn create_vfio_pci_device( &mut self, id: &str, @@ -1072,6 +1097,9 @@ pub trait MachineOps { "vfio-pci" => { self.add_vfio_device(cfg_args)?; } + "vhost-user-blk-pci" => { + self.add_vhost_user_blk_pci(vm_config, cfg_args)?; + } "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index ef15b7607..109d887be 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -23,7 +23,8 @@ use super::{ pci_args_check, }; use crate::config::{ - CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, + MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; const MAX_SERIAL_NUM: usize = 20; @@ -42,6 +43,8 @@ pub struct BlkDevConfig { pub iops: Option, pub queues: u16, pub boot_index: Option, + pub chardev: Option, + pub socket_path: Option, } #[derive(Debug, Clone)] @@ -63,6 +66,8 @@ impl Default for BlkDevConfig { iops: None, queues: 1, boot_index: None, + chardev: None, + socket_path: None, } } } @@ -302,6 +307,48 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result Result { + let mut cmd_parser = CmdParser::new("vhost-user-blk-pci"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("addr") + .push("num-queues") + .push("chardev"); + + cmd_parser.parse(drive_config)?; + + pci_args_check(&cmd_parser)?; + + let mut blkdevcfg = BlkDevConfig::default(); + + if let Some(chardev) = cmd_parser.get_value::("chardev")? { + blkdevcfg.chardev = Some(chardev); + } else { + return Err(ErrorKind::FieldIsMissing("chardev", "vhost-user-blk-pci").into()); + }; + + if let Some(id) = cmd_parser.get_value::("id")? { + blkdevcfg.id = id; + } else { + bail!("No id configured for blk device"); + } + + if let Some(queues) = cmd_parser.get_value::("num-queues")? { + blkdevcfg.queues = queues; + } + + if let Some(chardev) = &blkdevcfg.chardev { + blkdevcfg.socket_path = Some(get_chardev_socket_path(chardev, vm_config)?); + } + blkdevcfg.check()?; + Ok(blkdevcfg) +} + /// Config struct for `pflash`. /// Contains pflash device's attr. #[derive(Debug, Clone, Serialize, Deserialize, Default)] diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index cc93d43e9..f0a385622 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -195,10 +195,23 @@ pub const VIRTIO_CONSOLE_F_SIZE: u64 = 0; pub const VIRTIO_BLK_F_SIZE_MAX: u32 = 1; /// Maximum number of segments in a request is in seg_max. pub const VIRTIO_BLK_F_SEG_MAX: u32 = 2; +/// Legacy geometry available. +pub const VIRTIO_BLK_F_GEOMETRY: u32 = 4; /// Device is read-only. pub const VIRTIO_BLK_F_RO: u32 = 5; +/// Block size of disk is available. +pub const VIRTIO_BLK_F_BLK_SIZE: u32 = 6; /// Cache flush command support. pub const VIRTIO_BLK_F_FLUSH: u32 = 9; +/// Topology information is available. +pub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10; +/// Support more than one virtqueue. +pub const VIRTIO_BLK_F_MQ: u32 = 12; +/// DISCARD is supported. +pub const VIRTIO_BLK_F_DISCARD: u32 = 13; +/// WRITE ZEROES is supported. +pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; + /// The device sets MQ ok status values to driver. pub const VIRTIO_NET_OK: u8 = 0; /// Driver configure the class before enabling virtqueue. diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs new file mode 100644 index 000000000..87cb6125f --- /dev/null +++ b/virtio/src/vhost/user/block.rs @@ -0,0 +1,337 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use error_chain::bail; +use std::cmp; +use std::io::Write; +use std::sync::{Arc, Mutex}; + +use address_space::AddressSpace; +use log::warn; +use machine_manager::config::BlkDevConfig; +use machine_manager::event_loop::EventLoop; +use util::byte_code::ByteCode; +use util::loop_context::EventNotifierHelper; +use util::num_ops::{read_u32, write_u32}; +use vmm_sys_util::eventfd::EventFd; + +use super::client::VhostUserClient; +use crate::block::VirtioBlkConfig; +use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::vhost::VhostOps; +use crate::VhostUser::client::{VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ}; +use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; +use crate::{ + virtio_has_feature, BlockState, VirtioDevice, VirtioInterrupt, VIRTIO_BLK_F_BLK_SIZE, + VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, + VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, +}; + +/// Size of each virtqueue. +const QUEUE_SIZE_BLK: u16 = 256; + +#[allow(dead_code)] +pub struct Block { + /// Configuration of the block device. + blk_cfg: BlkDevConfig, + /// System address space. + mem_space: Arc, + /// Status of block device. + state: BlockState, + /// Vhost user client + client: Option>>, + /// The notifier events from host. + call_events: Vec, + /// Eventfd used to update the config space. + update_evt: EventFd, + /// Eventfd used to deactivate device. + deactivate_evt: EventFd, +} + +impl Block { + pub fn new(cfg: &BlkDevConfig, mem_space: &Arc) -> Self { + Block { + blk_cfg: cfg.clone(), + state: BlockState::default(), + update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + mem_space: mem_space.clone(), + client: None, + call_events: Vec::::new(), + } + } + + fn delete_event(&mut self) -> Result<()> { + match &self.client { + Some(client) => { + client + .lock() + .unwrap() + .delete_event() + .chain_err(|| "Failed to delete vhost-user blk event")?; + } + None => return Err("Failed to get client when stoping event".into()), + }; + + Ok(()) + } + + fn clean_up(&mut self) -> Result<()> { + self.delete_event()?; + self.state.config_space = VirtioBlkConfig::default(); + self.state.device_features = 0u64; + self.state.driver_features = 0u64; + self.client = None; + + Ok(()) + } + + /// Connect with spdk and register update event. + fn init_client(&mut self) -> Result<()> { + let socket_path = self + .blk_cfg + .socket_path + .as_ref() + .map(|path| path.to_string()) + .chain_err(|| "vhost-user: socket path is not found")?; + let client = VhostUserClient::new(&self.mem_space, &socket_path, self.queue_num() as u64) + .chain_err(|| { + "Failed to create the client which communicates with the server for vhost-user blk" + })?; + let client = Arc::new(Mutex::new(client)); + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(client.clone()), + None, + ) + .chain_err(|| "Failed to update event for client sock")?; + self.client = Some(client); + Ok(()) + } + + /// Get config from spdk and setup config space. + fn setup_device(&mut self) -> Result<()> { + let client = self.client.as_ref().unwrap(); + let feature = self.state.device_features; + + if virtio_has_feature(feature, VHOST_USER_F_PROTOCOL_FEATURES) { + let protocol_feature = client + .lock() + .unwrap() + .get_protocol_features() + .chain_err(|| "Failed to get protocol features for vhost-user blk")?; + + if virtio_has_feature(protocol_feature, VHOST_USER_PROTOCOL_F_CONFIG as u32) { + let config = client + .lock() + .unwrap() + .get_virtio_blk_config() + .chain_err(|| "Failed to get config for vhost-user blk")?; + self.state.config_space = config; + } else { + bail!( + "Failed to get config, spdk doesn't support, spdk protocol feature: {:#b}", + protocol_feature + ); + } + + if virtio_has_feature(protocol_feature, VHOST_USER_PROTOCOL_F_MQ as u32) { + let max_queue_num = client + .lock() + .unwrap() + .get_max_queue_num() + .chain_err(|| "Failed to get queue num for vhost-user blk")?; + if self.queue_num() > max_queue_num as usize { + bail!( + "Exceed the max queue num that spdk supported ({} queues)", + max_queue_num + ); + } + + if self.blk_cfg.queues > 1 { + self.state.config_space.num_queues = self.blk_cfg.queues; + } + } else if self.blk_cfg.queues > 1 { + bail!( + "spdk doesn't support multi queue, spdk protocol feature: {:#b}", + protocol_feature + ); + } + } + client + .lock() + .unwrap() + .set_virtio_blk_config(self.state.config_space) + .chain_err(|| "Failed to set config for vhost-user blk")?; + Ok(()) + } + + /// Negotiate feature with spdk. + fn negotiate_feature(&mut self) -> Result<()> { + let client = self.client.as_ref().unwrap(); + + let mut feature = client + .lock() + .unwrap() + .get_features() + .chain_err(|| "Failed to get features for vhost-user blk")?; + + feature |= 1_u64 << VIRTIO_F_VERSION_1; + feature |= 1_u64 << VIRTIO_BLK_F_SIZE_MAX; + feature |= 1_u64 << VIRTIO_BLK_F_TOPOLOGY; + feature |= 1_u64 << VIRTIO_BLK_F_BLK_SIZE; + feature |= 1_u64 << VIRTIO_BLK_F_FLUSH; + feature |= 1_u64 << VIRTIO_BLK_F_DISCARD; + feature |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; + feature |= 1_u64 << VIRTIO_BLK_F_SEG_MAX; + feature &= !(1_u64 << VIRTIO_F_RING_PACKED); + + if self.blk_cfg.read_only { + feature |= 1_u64 << VIRTIO_BLK_F_RO; + }; + if self.blk_cfg.queues > 1 { + feature |= 1_u64 << VIRTIO_BLK_F_MQ; + } + + self.state.device_features = feature; + + client + .lock() + .unwrap() + .set_features(feature) + .chain_err(|| "Failed to set features for vhost-user blk")?; + Ok(()) + } +} + +impl VirtioDevice for Block { + /// Realize vhost user blk pci device. + fn realize(&mut self) -> Result<()> { + self.init_client()?; + self.negotiate_feature()?; + self.setup_device()?; + Ok(()) + } + + /// Get the virtio device type, refer to Virtio Spec. + fn device_type(&self) -> u32 { + VIRTIO_TYPE_BLOCK + } + + /// Get the count of virtio device queues. + fn queue_num(&self) -> usize { + self.blk_cfg.queues as usize + } + + /// Get the queue size of virtio device. + fn queue_size(&self) -> u16 { + QUEUE_SIZE_BLK + } + + /// Get device features from host. + fn get_device_features(&self, features_select: u32) -> u32 { + read_u32(self.state.device_features, features_select) + } + + /// Set driver features by guest. + fn set_driver_features(&mut self, page: u32, value: u32) { + let mut features = write_u32(value, page); + let unsupported_features = features & !self.state.device_features; + if unsupported_features != 0 { + warn!( + "Received acknowledge request with unsupported feature for vhost-user blk: 0x{:x}", + features + ); + features &= !unsupported_features; + } + self.state.driver_features |= features; + } + + /// Read data of config from guest. + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + let offset = offset as usize; + let config_slice = self.state.config_space.as_bytes(); + let config_len = config_slice.len(); + if offset >= config_len { + return Err(ErrorKind::DevConfigOverflow(offset as u64, config_len as u64).into()); + } + if let Some(end) = offset.checked_add(data.len()) { + data.write_all(&config_slice[offset..cmp::min(end, config_len)])?; + } else { + bail!("Failed to read config from guest for vhost user blk pci, config space address overflow.") + } + + Ok(()) + } + + /// Write data to config from guest. + fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + let offset = offset as usize; + let config_slice = self.state.config_space.as_mut_bytes(); + let config_len = config_slice.len(); + if let Some(end) = offset.checked_add(data.len()) { + if end > config_len { + return Err(ErrorKind::DevConfigOverflow(offset as u64, config_len as u64).into()); + } + config_slice[offset..end].copy_from_slice(data); + } else { + bail!("Failed to write config to guest for vhost user blk pci, config space address overflow.") + } + + Ok(()) + } + + /// activate device + fn activate( + &mut self, + _mem_space: Arc, + _interrupt_cb: Arc, + queues: &[Arc>], + queue_evts: Vec, + ) -> Result<()> { + let mut client = match &self.client { + Some(client) => client.lock().unwrap(), + None => return Err("Failed to get client for vhost-user blk".into()), + }; + client.features = self.state.driver_features; + client.set_queues(queues); + client.set_queue_evts(&queue_evts); + client.activate_vhost_user()?; + Ok(()) + } + + fn deactivate(&mut self) -> Result<()> { + self.call_events.clear(); + self.clean_up()?; + self.realize() + } + + fn reset(&mut self) -> Result<()> { + self.clean_up()?; + self.realize() + } + + /// Set guest notifiers for notifying the guest. + fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { + for fd in queue_evts.iter() { + let cloned_evt_fd = fd.try_clone().unwrap(); + self.call_events.push(cloned_evt_fd); + } + + match &self.client { + Some(client) => client.lock().unwrap().set_call_events(queue_evts), + None => return Err("Failed to get client for vhost-user blk".into()), + }; + + Ok(()) + } +} diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index a921c7813..a91304f2a 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -14,6 +14,8 @@ pub const VHOST_USER_VERSION: u32 = 0x1; pub const VHOST_USER_MSG_MAX_SIZE: usize = 0x1000; pub const MAX_ATTACHED_FD_ENTRIES: usize = 32; +pub const VHOST_USER_F_PROTOCOL_FEATURES: u32 = 30; +pub const VHOST_USER_MAX_CONFIG_SIZE: u32 = 256; /// Type of requests sending from vhost user device to the userspace process. #[repr(u32)] -- Gitee From aacbb57f2cb7b825261c5e7efb47402c8a0d59f2 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:19:54 +0800 Subject: [PATCH 0169/2187] vhost-user: add support for vhost user blk pci 1. Add VhostUserConfig struct 2. Add set config rpc function 3. Add get/set protocol feature function Signed-off-by: Xiaowei Xue --- virtio/src/vhost/user/client.rs | 104 ++++++++++++++++++++++++++++++- virtio/src/vhost/user/message.rs | 31 +++++++++ virtio/src/vhost/user/mod.rs | 2 + 3 files changed, 136 insertions(+), 1 deletion(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 17fe65bc9..ca945a071 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -12,6 +12,7 @@ use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; +use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; use address_space::{ @@ -35,6 +36,13 @@ use super::message::{ VhostUserMsgReq, VhostUserVringAddr, VhostUserVringState, }; use super::sock::VhostUserSock; +use crate::block::VirtioBlkConfig; +use crate::VhostUser::message::VhostUserConfig; + +/// Vhost supports multiple queue +pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; +/// Vhost supports `VHOST_USER_GET_CONFIG` and `VHOST_USER_GET_CONFIG` msg. +pub const VHOST_USER_PROTOCOL_F_CONFIG: u8 = 9; struct ClientInternal { // Used to send requests to the vhost user backend in userspace. @@ -374,7 +382,6 @@ impl VhostUserClient { if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { queue_num -= 1; } - // Set all vring num to notify ovs/dpdk how many queues it needs to poll // before setting vring info. for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { @@ -459,6 +466,101 @@ impl VhostUserClient { ), ] } + + /// Send get protocol features request to vhost. + pub fn get_protocol_features(&self) -> Result { + let client = self.client.lock().unwrap(); + let request = VhostUserMsgReq::GetProtocolFeatures as u32; + let hdr = VhostUserMsgHdr::new(request, VhostUserHdrFlag::NeedReply as u32, 0); + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = None; + client + .sock + .send_msg(Some(&hdr), body_opt, payload_opt, &[]) + .chain_err(|| "Failed to send msg for getting features")?; + let features = client + .wait_ack_msg::(request) + .chain_err(|| "Failed to wait ack msg for getting protocols features")?; + + Ok(features) + } + + /// Send set protocol features request to vhost. + pub fn set_protocol_features(&self, features: u64) -> Result<()> { + let client = self.client.lock().unwrap(); + let hdr = VhostUserMsgHdr::new( + VhostUserMsgReq::SetProtocolFeatures as u32, + 0, + size_of::() as u32, + ); + let payload_opt: Option<&[u8]> = None; + client + .sock + .send_msg(Some(&hdr), Some(&features), payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting protocols features")?; + + Ok(()) + } + + /// Get virtio blk config from vhost. + pub fn get_virtio_blk_config(&self) -> Result { + let client = self.client.lock().unwrap(); + let request = VhostUserMsgReq::GetConfig as u32; + let config_len = size_of::>(); + let hdr = VhostUserMsgHdr::new( + request, + VhostUserHdrFlag::NeedReply as u32, + config_len as u32, + ); + let cnf = VhostUserConfig::new(0, 0, VirtioBlkConfig::default())?; + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = Some(unsafe { + from_raw_parts( + (&cnf as *const VhostUserConfig) as *const u8, + config_len, + ) + }); + client + .sock + .send_msg(Some(&hdr), body_opt, payload_opt, &[]) + .chain_err(|| "Failed to send msg for getting config")?; + let res = client + .wait_ack_msg::>(request) + .chain_err(|| "Failed to wait ack msg for getting virtio blk config")?; + Ok(res.config) + } + + /// Set virtio blk config to vhost. + pub fn set_virtio_blk_config(&self, cnf: VirtioBlkConfig) -> Result<()> { + let client = self.client.lock().unwrap(); + let request = VhostUserMsgReq::SetConfig as u32; + let config_len = size_of::>(); + let hdr = VhostUserMsgHdr::new(request, 0, config_len as u32); + let payload_opt: Option<&[u8]> = None; + let config = VhostUserConfig::new(0, 0, cnf)?; + client + .sock + .send_msg(Some(&hdr), Some(&config), payload_opt, &[]) + .chain_err(|| "Failed to send msg for getting virtio blk config")?; + Ok(()) + } + + /// Get max queues number that vhost supports. + pub fn get_max_queue_num(&self) -> Result { + let client = self.client.lock().unwrap(); + let request = VhostUserMsgReq::GetQueueNum as u32; + let hdr = VhostUserMsgHdr::new(request, VhostUserHdrFlag::NeedReply as u32, 0); + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = None; + client + .sock + .send_msg(Some(&hdr), body_opt, payload_opt, &[]) + .chain_err(|| "Failed to send msg for getting queue num")?; + let queue_num = client + .wait_ack_msg::(request) + .chain_err(|| "Failed to wait ack msg for getting queue num")?; + Ok(queue_num) + } } impl VhostOps for VhostUserClient { diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index a91304f2a..34d85c1cb 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -10,6 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use error_chain::bail; +use std::mem::size_of; + +use super::super::super::errors::Result; + /// The version of the protocol StratoVirt support. pub const VHOST_USER_VERSION: u32 = 0x1; pub const VHOST_USER_MSG_MAX_SIZE: usize = 0x1000; @@ -130,6 +135,32 @@ impl Default for VhostUserMsgHdr { } } +/// Struct for get and set config to vhost user. +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub struct VhostUserConfig { + offset: u32, + size: u32, + flags: u32, + pub config: T, +} + +impl VhostUserConfig { + /// Create a new instance of `VhostUserConfig`. + pub fn new(offset: u32, flags: u32, config: T) -> Result { + let size = size_of::() as u32; + if size > VHOST_USER_MAX_CONFIG_SIZE { + bail!("Failed to create VhostUserConfig: exceed max config size.") + } + Ok(VhostUserConfig { + offset, + size, + flags, + config, + }) + } +} + /// Memory region information for the message of memory table. #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq)] diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index fc6b8c7fa..12f0717d0 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -10,10 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod block; mod client; mod message; mod net; mod sock; +pub use block::Block; use client::VhostUserClient; pub use net::Net; -- Gitee From 29264d2e7dcd022c3006da75a356e1da2450af4c Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:24:45 +0800 Subject: [PATCH 0170/2187] virtio: add support for vhost user blk pci 1. Modify VirtioBlkConfig to support vhost user blk pci. 2. Add new function has_control_queue to VirtioDevice trait, virtio device can override this function to tell caller this device need control queue or not. Signed-off-by: Xiaowei Xue --- machine/src/micro_vm/mod.rs | 2 ++ machine/src/standard_vm/mod.rs | 2 ++ virtio/src/block.rs | 10 +++++----- virtio/src/lib.rs | 8 ++++++-- virtio/src/vhost/user/net.rs | 5 +++++ virtio/src/virtio_pci.rs | 9 +++++++-- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 961ca9bf2..e85315729 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1154,6 +1154,8 @@ impl DeviceInterface for LightMachine { iops: None, queues: 1, boot_index: None, + chardev: None, + socket_path: None, }; match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index df3f1f236..8c719f5cc 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -792,6 +792,8 @@ impl StdMachine { iops: conf.iops, queues: args.queues.unwrap_or(1), boot_index: args.boot_index, + chardev: None, + socket_path: None, }; dev.check()?; dev diff --git a/virtio/src/block.rs b/virtio/src/block.rs index dcbbb73f7..ef0dd5f4c 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -842,7 +842,7 @@ impl ByteCode for VirtioBlkGeometry {} #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] -struct VirtioBlkConfig { +pub struct VirtioBlkConfig { /// The capacity in 512 byte sectors. capacity: u64, /// The maximum segment size. @@ -866,7 +866,7 @@ struct VirtioBlkConfig { /// Reserved data. unused: u8, /// Number of virtio queues, only available when `VIRTIO_BLK_F_MQ` is set. - num_queues: u16, + pub num_queues: u16, /// The maximum discard sectors for one segment. max_discard_sectors: u32, /// The maximum number of discard segments in a discard command. @@ -891,11 +891,11 @@ impl ByteCode for VirtioBlkConfig {} #[desc_version(compat_version = "0.1.0")] pub struct BlockState { /// Bitmask of features supported by the backend. - device_features: u64, + pub device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. - driver_features: u64, + pub driver_features: u64, /// Config space of the block device. - config_space: VirtioBlkConfig, + pub config_space: VirtioBlkConfig, } /// Block device structure. diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index f0a385622..74e0d11b8 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -222,8 +222,6 @@ pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0; pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: u16 = 1; /// The maximum pairs of multiple queue. pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000; -/// Support more than one virtqueue. -pub const VIRTIO_BLK_F_MQ: u32 = 12; /// The IO type of virtio block, refer to Virtio Spec. /// Read. @@ -351,4 +349,10 @@ pub trait VirtioDevice: Send { fn set_guest_notifiers(&mut self, _queue_evts: &[EventFd]) -> Result<()> { Ok(()) } + + /// Get whether the virtio device has a control queue, + /// devices with a control queue should override this function. + fn has_control_queue(&mut self) -> bool { + false + } } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 8e5385e05..f1627dfa8 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -23,6 +23,7 @@ use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::eventfd::EventFd; use super::super::super::errors::{ErrorKind, Result, ResultExt}; +use super::super::super::virtio_has_feature; use super::super::super::{ net::{build_device_config_space, VirtioNetConfig}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, @@ -302,4 +303,8 @@ impl VirtioDevice for Net { Ok(()) } + + fn has_control_queue(&mut self) -> bool { + virtio_has_feature(self.device_features, VIRTIO_NET_F_CTRL_VQ) + } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 2f6c56eac..fef36446c 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -731,7 +731,9 @@ impl VirtioPciDevice { let mut queue_num = cloned_pci_device.device.lock().unwrap().queue_num(); // No need to create call event for control queue. // It will be polled in StratoVirt when activating the device. - if queue_num % 2 != 0 { + if cloned_pci_device.device.lock().unwrap().has_control_queue() + && queue_num % 2 != 0 + { queue_num -= 1; } let call_evts = NotifyEventFds::new(queue_num); @@ -1368,7 +1370,10 @@ fn virtio_pci_register_irqfd( let locked_queues = pci_device.queues.lock().unwrap(); let mut locked_gsi_routes = gsi_routes.lock().unwrap(); for (queue_index, queue_mutex) in locked_queues.iter().enumerate() { - if queue_index + 1 == locked_queues.len() && locked_queues.len() % 2 != 0 { + if pci_device.device.lock().unwrap().has_control_queue() + && queue_index + 1 == locked_queues.len() + && locked_queues.len() % 2 != 0 + { break; } -- Gitee From 9e46d97ca8a0a35a4ad3d8bdd471a05cd1104a3b Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:29:27 +0800 Subject: [PATCH 0171/2187] chardev: encapsulates common code Encapsulates the common code of blk device and net device to get_chardev_socket_path function Signed-off-by: Xiaowei Xue --- machine_manager/src/config/chardev.rs | 31 +++++++++++++++++++++++++++ machine_manager/src/config/network.rs | 27 +++-------------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 6af87f333..d040c66ea 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -206,6 +206,37 @@ pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result Result { + if let Some(char_dev) = vm_config.chardev.remove(chardev) { + match char_dev.backend.clone() { + ChardevType::Socket { + path, + server, + nowait, + } => { + if server || nowait { + bail!( + "Argument \'server\' or \'nowait\' is not need for chardev \'{}\'", + path + ); + } + Ok(path) + } + _ => { + bail!("Chardev {:?} backend should be socket type.", &char_dev.id); + } + } + } else { + bail!("Chardev: {:?} not found for character device", &chardev); + } +} + pub fn parse_virtconsole(vm_config: &mut VmConfig, config_args: &str) -> Result { let mut cmd_parser = CmdParser::new("virtconsole"); cmd_parser.push("").push("id").push("chardev"); diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 51608f10a..b6f532d0f 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -18,8 +18,8 @@ use super::{ pci_args_check, }; use crate::config::{ - ChardevType, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, - MAX_VIRTIO_QUEUE, + get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, + MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::{qmp_schema, QmpChannel}; @@ -290,28 +290,7 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result { - if *server || *nowait { - bail!( - "Argument \'server\' or \'nowait\' is not need for chardev \'{}\'", - path - ); - } - netdevinterfacecfg.socket_path = Some(path.clone()); - } - _ => { - bail!("Chardev {:?} backend should be socket type.", &chardev); - } - } - } else { - bail!("Chardev: {:?} not found for character device", &chardev); - } + netdevinterfacecfg.socket_path = Some(get_chardev_socket_path(chardev, vm_config)?); } } else { bail!("Netdev: {:?} not found for net device", &netdev); -- Gitee From e9904fdedeece638186b177c5e13afcc6ef73db3 Mon Sep 17 00:00:00 2001 From: xuebling Date: Thu, 15 Sep 2022 22:29:42 +0800 Subject: [PATCH 0172/2187] doc: add vhost-user-blk-pci usage to config_guidebook.md Signed-off-by: Xiaowei Xue --- docs/config_guidebook.md | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index cdef72f03..59862970e 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -276,6 +276,72 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo -device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N,bootindex=1] ``` +StratoVirt also supports vhost-user-blk-pci to get a higher performance in storage, but only standard vm supports it. + +You can use it by adding a new device, one more property is supported by vhost-user-blk-pci device than virtio-blk-pci. + +* chardev: id for char device, that means you need to add a chardev first, and use its id to index char device. + +```shell +# vhost user blk pci device +-chardev socket,id=chardevid,path=socket_path +-device vhost-user-blk-pci,id=blk1,chardev=chardevid,bus=pcie.0,addr=0x3[,num-queues=N,bootindex=1] +``` + +Note: More features to be supported. + +It should open sharing memory('-mem-share=on') and hugepages('-mem-path ...' ) when using vhost-user-blk-pci. + +Vhost-user-blk-pci use spdk as vhost-backend, so you need to start spdk before starting stratovirt. + +*How to start and configure spdk?* + +``` shell +# Get code and compile spdk +$ git clone https://github.com/spdk/spdk.git +$ cd spdk +$ git submodule update --init +$ ./scripts/pkgdep.sh +$ ./configure +$ make + +# Test spdk environment +$ ./test/unit/unittest.sh + +# Setup spdk +$ sudo HUGEMEM=2048 ./scripts/setup.sh +# Mount huge pages, you need to add -mem-path=/dev/hugepages in stratovirt config +$ sudo mount -t hugetlbfs hugetlbfs /dev/hugepages +$ sudo sysctl vm.nr_hugepages=1024 +# Start vhost, alloc 1024MB memory, default socket path is /var/tmp/spdk.sock, 0x3 means we use cpu cores 0 and 1 (cpumask 0x3) +$ sudo build/bin/vhost --logflag vhost_blk -S /var/tmp -s 1024 -m 0x3 & +# Attach nvme controller, you can find you nvme id by lspci command +$ sudo ./scripts/rpc.py bdev_nvme_attach_controller -b Nvme0 -t pcie -a you-nvme-id +# Create a bdev which size is 128MB, block size is 512B +$ sudo ./scripts/rpc.py bdev_malloc_create 128 512 -b Malloc0 +# Create a vhost-blk device exposing Malloc0 bdev, the I/O polling will be pinned to the CPU 0 (cpumask 0x1). +$ sudo ./scripts/rpc.py vhost_create_blk_controller --cpumask 0x1 spdk.sock Malloc0 +``` +A config template to start stratovirt with vhost-user-blk-pci as below: + +``` shell +stratovirt \ + -machine q35,mem-share=on \ + -smp 1 \ + -kernel /path-to/std-vmlinuxz \ + -mem-path /dev/hugepages \ + -m 1G \ + -append "console=ttyS0 reboot=k panic=1 root=/dev/vda rw" \ + -drive file=/path-to/OVMF_CODE.fd,if=pflash,unit=0,readonly=true \ + -drive file=/path-to/OVMF_VARS.fd,if=pflash,unit=1 \ + -drive file=/path-to/openEuler.img,id=rootfs,readonly=off,direct=off \ + -device virtio-blk-pci,drive=rootfs,id=blk0,bus=pcie.0,addr=0x2,bootindex=0 \ + -chardev socket,id=spdk_vhost_blk0,path=/var/tmp/spdk.sock \ + -device vhost-user-blk-pci,id=blk1,chardev=spdk_vhost_blk0,bus=pcie.0,addr=0x3\ + -qmp unix:/path-to/stratovirt.socket,server,nowait \ + -serial stdio +``` + ### 2.3 Virtio-net Virtio-net is a virtual Ethernet card in VM. It can enable the network capability of VM. -- Gitee From d3ee08e6fb0d7e46ab1f8460f9796cb0fef3914a Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 20 Sep 2022 16:50:56 +0800 Subject: [PATCH 0173/2187] VNC: plus listen port with offset 5900 1. Plus listen port with offset 5900. 2. Validity check for ip and port. Signed-off-by: Xiao Ye --- machine_manager/src/config/vnc.rs | 82 ++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index 85106670b..57102d938 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -12,11 +12,9 @@ use serde::{Deserialize, Serialize}; -use super::errors::Result; -use crate::config::{ - errors::ErrorKind, - {CmdParser, VmConfig}, -}; +use super::errors::{ErrorKind, Result}; +use crate::config::{CmdParser, VmConfig}; +use std::net::Ipv4Addr; /// Configuration of vnc. #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -33,6 +31,9 @@ pub struct VncConfig { pub sasl_authz: String, } +const VNC_MAX_PORT_NUM: i32 = 65535; +const VNC_PORT_OFFSET: i32 = 5900; + impl VmConfig { /// Make configuration for vnc: "chardev" -> "vnc". pub fn add_vnc(&mut self, vnc_config: &str) -> Result<()> { @@ -45,17 +46,16 @@ impl VmConfig { cmd_parser.parse(vnc_config)?; let mut vnc_config = VncConfig::default(); + // Parse Ip:Port. if let Some(addr) = cmd_parser.get_value::("")? { - let v: Vec<&str> = addr.split(':').collect(); - if v.len() != 2 { - return Err(ErrorKind::FieldIsMissing("ip", "port").into()); + if let Err(e) = parse_port(&mut vnc_config, addr) { + return Err(e); } - vnc_config.ip = v[0].to_string(); - vnc_config.port = v[1].to_string(); } else { return Err(ErrorKind::FieldIsMissing("ip", "port").into()); } + // VNC Security Type. if let Some(tls_creds) = cmd_parser.get_value::("tls-creds")? { vnc_config.tls_creds = tls_creds } @@ -73,6 +73,28 @@ impl VmConfig { } } +/// Parse Ip:port. +fn parse_port(vnc_config: &mut VncConfig, addr: String) -> Result<()> { + let v: Vec<&str> = addr.split(':').collect(); + if v.len() != 2 { + return Err(ErrorKind::FieldIsMissing("ip", "port").into()); + } + let ip = v[0] + .parse::() + .map_err(|_| "Invalid Ip param for vnc!")?; + let base_port = v[1] + .parse::() + .map_err(|_| "Invalid Port param for vnc!")?; + // Prevent the base_port out of bounds. + if !(0..=VNC_MAX_PORT_NUM - VNC_PORT_OFFSET).contains(&base_port) { + return Err(ErrorKind::InvalidParam(base_port.to_string(), "port".to_string()).into()); + } + vnc_config.ip = ip.to_string(); + vnc_config.port = ((base_port + VNC_PORT_OFFSET) as u16).to_string(); + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -80,30 +102,46 @@ mod tests { #[test] fn test_add_vnc() { let mut vm_config = VmConfig::default(); - vm_config - .add_vnc("0.0.0.0:1,tls-creds=vnc-tls-creds0,sasl,sasl-authz=authz0") - .unwrap(); + let config_line = "0.0.0.0:1,tls-creds=vnc-tls-creds0,sasl,sasl-authz=authz0"; + assert!(vm_config.add_vnc(config_line).is_ok()); let vnc_config = vm_config.vnc.unwrap(); assert_eq!(vnc_config.ip, String::from("0.0.0.0")); - assert_eq!(vnc_config.port, String::from("1")); + assert_eq!(vnc_config.port, String::from("5901")); assert_eq!(vnc_config.tls_creds, String::from("vnc-tls-creds0")); assert_eq!(vnc_config.sasl, true); assert_eq!(vnc_config.sasl_authz, String::from("authz0")); let mut vm_config = VmConfig::default(); - vm_config - .add_vnc("0.0.0.0:1,tls-creds=vnc-tls-creds0") - .unwrap(); + let config_line = "0.0.0.0:5900,tls-creds=vnc-tls-creds0"; + assert!(vm_config.add_vnc(config_line).is_ok()); let vnc_config = vm_config.vnc.unwrap(); assert_eq!(vnc_config.sasl, false); + assert_eq!(vnc_config.port, String::from("11800")); let mut vm_config = VmConfig::default(); - let res = vm_config.add_vnc("tls-creds=vnc-tls-creds0"); - assert!(res.is_err()); - - let mut vm_config = VmConfig::default(); - let _res = vm_config.add_vnc("0.0.0.0:1,sasl,sasl-authz=authz0"); + let config_line = "0.0.0.0:1,sasl,sasl-authz=authz0"; + assert!(vm_config.add_vnc(config_line).is_ok()); let vnc_config = vm_config.vnc.unwrap(); assert_eq!(vnc_config.tls_creds, "".to_string()); + + // Invalie format of ip:port. + let config_lines = [ + "tls-creds=vnc-tls-creds0", // No ip:port. + "127.0.0.1", // No port. + "1", // No ip. + "0.0.0.0:65536", // Invalid port. + "0.0.0.0:59636", // Invalid port. + "0.0.0.0:2147483647", // Invalie port. + "0.0.0.0:-1", // Invalid port. + "0.0.0.0:123ab", // Invalid port. + "127.257.0.1:0", // Invalid ip. + "127.0.0.0.1:0", // Invalid ip. + "127.12ab.0.1:0", // Invalid ip. + "127.0.1:0", // Invalid ip. + ]; + for config_line in config_lines { + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_vnc(config_line).is_err()); + } } } -- Gitee From f6cd152bc6d52c4ad50ebb8958b44279497a3ada Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 20 Sep 2022 17:06:20 +0800 Subject: [PATCH 0174/2187] VNC: Modify Some code format 1. Add set_color_depth. 2. Modify some variable names, transfer guest_dirtymap to guest_dirty_bitmap. 3. Transfer Vnc version from 2.1.0 to 2.2.0. 4. Add some logs in handshake interface of RFB protocol. Signed-off-by: Xiao Ye --- vnc/Cargo.toml | 2 +- vnc/src/client.rs | 97 ++++++++++++++++++++++++++++++++++------------- vnc/src/server.rs | 24 ++++++------ vnc/src/vnc.rs | 14 ++++--- 4 files changed, 91 insertions(+), 46 deletions(-) diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index fc270d433..db6fbeb4c 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vnc" -version = "2.1.0" +version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/vnc/src/client.rs b/vnc/src/client.rs index c5e6a6311..1ba203c86 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -585,6 +585,38 @@ impl VncClient { Ok(()) } + /// Set color depth for client. + pub fn set_color_depth(&mut self) { + if self.has_feature(VncFeatures::VncFeatureWmvi) { + let mut buf = Vec::new(); + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Padding. + buf.append(&mut (1_u16).to_be_bytes().to_vec()); // Number of pixel block. + framebuffer_upadate(0, 0, self.width, self.height, ENCODING_WMVI, &mut buf); + buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); + self.pixel_format_message(&mut buf); + self.write_msg(&buf); + } else if !self.pixel_format.is_default_pixel_format() { + self.pixel_convert = true; + } + } + + /// Set pixformat for client. + pub fn pixel_format_message(&mut self, buf: &mut Vec) { + self.pixel_format.init_pixelformat(); + buf.append(&mut (self.pixel_format.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. + buf.append(&mut (self.pixel_format.depth as u8).to_be_bytes().to_vec()); // Depth. + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Big-endian flag. + buf.append(&mut (1_u8).to_be_bytes().to_vec()); // True-color flag. + buf.append(&mut (self.pixel_format.red.max as u16).to_be_bytes().to_vec()); // Red max. + buf.append(&mut (self.pixel_format.green.max as u16).to_be_bytes().to_vec()); // Green max. + buf.append(&mut (self.pixel_format.blue.max as u16).to_be_bytes().to_vec()); // Blue max. + buf.append(&mut (self.pixel_format.red.shift as u8).to_be_bytes().to_vec()); // Red shift. + buf.append(&mut (self.pixel_format.green.shift as u8).to_be_bytes().to_vec()); // Green shift. + buf.append(&mut (self.pixel_format.blue.shift as u8).to_be_bytes().to_vec()); // Blue shift. + buf.append(&mut [0; 3].to_vec()); // Padding. + } + /// Initialize the connection of vnc client. pub fn handle_client_init(&mut self) -> Result<()> { let mut buf = Vec::new(); @@ -601,21 +633,7 @@ impl VncClient { return Err(ErrorKind::InvalidImageSize.into()); } buf.append(&mut (self.height as u16).to_be_bytes().to_vec()); - self.pixel_format.init_pixelformat(); - buf.push(self.pixel_format.pixel_bits); - buf.push(self.pixel_format.depth); - buf.push(0); // Big-endian flag. - buf.push(1); // True-color flag. - buf.push(0); - buf.push(self.pixel_format.red.max); - buf.push(0); - buf.push(self.pixel_format.green.max); - buf.push(0); - buf.push(self.pixel_format.blue.max); - buf.push(self.pixel_format.red.shift); - buf.push(self.pixel_format.green.shift); - buf.push(self.pixel_format.blue.shift); - buf.append(&mut [0; 3].to_vec()); + self.pixel_format_message(&mut buf); buf.append( &mut ("StratoVirt".to_string().len() as u32) @@ -635,6 +653,7 @@ impl VncClient { return Ok(()); } + info!("Set pixel format"); let mut buf = self.buffpool.read_front(self.expect).to_vec(); if buf[7] == 0 { // Set a default 256 color map. @@ -653,6 +672,7 @@ impl VncClient { // Blue shift. buf[16] = 6; } + if ![8, 16, 32].contains(&buf[4]) { self.dis_conn = true; error!("Worng format of bits_per_pixel"); @@ -730,6 +750,7 @@ impl VncClient { num_encoding = u16::from_be_bytes([buf[2], buf[3]]); } + info!("Set encoding"); while num_encoding > 0 { let offset = (4 * num_encoding) as usize; let enc = i32::from_be_bytes([ @@ -803,16 +824,37 @@ impl VncClient { self.feature & (1 << feature as usize) != 0 } + /// Set Desktop Size. pub fn desktop_resize(&mut self) { - // If hash feature VNC_FEATURE_RESIZE. - let width = get_image_width(self.server_image); - let height = get_image_height(self.server_image); + if !self.has_feature(VncFeatures::VncFeatureResizeExt) + && !self.has_feature(VncFeatures::VncFeatureResize) + { + return; + } + self.width = get_image_width(self.server_image); + self.height = get_image_height(self.server_image); + if self.width < 0 + || self.width > MAX_WINDOW_WIDTH as i32 + || self.height < 0 + || self.height > MAX_WINDOW_HEIGHT as i32 + { + error!("Invalid Image Size!"); + return; + } + let mut buf: Vec = Vec::new(); buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut (1_u16).to_be_bytes().to_vec()); + framebuffer_upadate( + 0, + 0, + self.width, + self.height, + ENCODING_DESKTOPRESIZE, + &mut buf, + ); - framebuffer_upadate(0, 0, width, height, ENCODING_DESKTOPRESIZE, &mut buf); self.write_msg(&buf); } @@ -864,14 +906,6 @@ impl VncClient { /// Clear the data when disconnected from client. pub fn disconnect(&mut self) { - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_server = server.lock().unwrap(); - locked_server.clients.remove(&self.addr); - if locked_server.clients.is_empty() { - update_client_surface(&mut locked_server); - } - drop(locked_server); - if let Err(e) = self.modify_event(NotifierOperation::Delete, 0) { error!("Failed to delete event, error is {}", e.display_chain()); } @@ -912,6 +946,15 @@ impl EventNotifierHelper for VncClient { } if dis_conn { + let addr = client.lock().unwrap().addr.clone(); + info!("Client disconnect : {:?}", addr); + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_server = server.lock().unwrap(); + locked_server.clients.remove(&addr); + if locked_server.clients.is_empty() { + update_client_surface(&mut locked_server); + } + drop(locked_server); client.lock().unwrap().disconnect(); } diff --git a/vnc/src/server.rs b/vnc/src/server.rs index f0afe7faf..d73b0500d 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -95,7 +95,7 @@ pub struct VncServer { /// Image from gpu. pub guest_image: *mut pixman_image_t, /// Identify the image update area for guest image. - pub guest_dirtymap: Bitmap, + pub guest_dirty_bitmap: Bitmap, /// Image format of pixman. pub guest_format: pixman_format_code_t, /// Cursor property. @@ -123,7 +123,7 @@ impl VncServer { keysym2keycode: HashMap::new(), server_image: ptr::null_mut(), guest_image, - guest_dirtymap: Bitmap::::new( + guest_dirty_bitmap: Bitmap::::new( MAX_WINDOW_HEIGHT as usize * round_up_div( (MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM) as u64, @@ -179,8 +179,8 @@ impl VncServer { /// /// # Arguments /// - /// * `s_info` - source image. - /// * `g_info` - dest image. + /// * `s_info` - Info of Server image. + /// * `g_info` - Info of Guest image. fn get_one_line_buf( &self, s_info: &mut ImageInfo, @@ -225,8 +225,8 @@ impl VncServer { /// # Arguments /// /// * `x` `y` - start coordinate in image to refresh - /// * `s_info` - source image. - /// * `g_info` - dest image. + /// * `s_info` - Info of Server image. + /// * `g_info` - Info of Guest image. fn update_one_line( &mut self, mut x: usize, @@ -241,7 +241,7 @@ impl VncServer { while x < round_up_div(width as u64, DIRTY_PIXELS_NUM as u64) as usize { if !self - .guest_dirtymap + .guest_dirty_bitmap .contain(x + y * VNC_BITMAP_WIDTH as usize) .unwrap() { @@ -250,7 +250,7 @@ impl VncServer { s_info.ptr = (s_info.ptr as usize + cmp_bytes) as *mut u8; continue; } - self.guest_dirtymap + self.guest_dirty_bitmap .clear(x + y * VNC_BITMAP_WIDTH as usize) .unwrap(); let mut _cmp_bytes = cmp_bytes; @@ -290,9 +290,9 @@ impl VncServer { pub fn update_server_image(&mut self) -> i32 { let mut dirty_num = 0; let height = self.get_min_height(); - let g_bpl = self.guest_dirtymap.vol() / MAX_WINDOW_HEIGHT as usize; + let g_bpl = self.guest_dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; - let mut offset = self.guest_dirtymap.find_next_bit(0).unwrap(); + let mut offset = self.guest_dirty_bitmap.find_next_bit(0).unwrap(); if offset >= (height as usize) * g_bpl { return dirty_num; } @@ -336,7 +336,7 @@ impl VncServer { g_info.ptr = (g_info.ptr as usize + x * cmp_bytes) as *mut u8; dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes); y += 1; - offset = self.guest_dirtymap.find_next_bit(y * g_bpl).unwrap(); + offset = self.guest_dirty_bitmap.find_next_bit(y * g_bpl).unwrap(); if offset >= (height as usize) * g_bpl { break; } @@ -354,7 +354,7 @@ impl VncServer { stream.shutdown(Shutdown::Both).unwrap(); return Ok(()); } - info!("new client: {:?}", addr); + info!("New Client: {:?}", addr); stream .set_nonblocking(true) .expect("set nonblocking failed"); diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 38f7c77a2..e70e1691f 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -238,7 +238,7 @@ pub fn vnc_display_update(x: i32, y: i32, w: i32, h: i32) { let mut locked_server = server.lock().unwrap(); let g_w = get_image_width(locked_server.guest_image); let g_h = get_image_height(locked_server.guest_image); - set_area_dirty(&mut locked_server.guest_dirtymap, x, y, w, h, g_w, g_h); + set_area_dirty(&mut locked_server.guest_dirty_bitmap, x, y, w, h, g_w, g_h); } fn vnc_get_display_update_interval() -> u32 { @@ -282,7 +282,6 @@ fn vnc_height(height: i32) -> i32 { pub fn update_client_surface(server: &mut VncServer) { unref_pixman_image(server.server_image); server.server_image = ptr::null_mut(); - if server.clients.is_empty() { return; } @@ -310,9 +309,9 @@ pub fn update_client_surface(server: &mut VncServer) { client.lock().unwrap().width = width; client.lock().unwrap().height = height; } - server.guest_dirtymap.clear_all(); + server.guest_dirty_bitmap.clear_all(); set_area_dirty( - &mut server.guest_dirtymap, + &mut server.guest_dirty_bitmap, 0, 0, width, @@ -540,7 +539,7 @@ pub fn vnc_display_switch(surface: &mut DisplaySurface) { let guest_height: i32 = get_image_height(locked_server.guest_image); if !need_resize { set_area_dirty( - &mut locked_server.guest_dirtymap, + &mut locked_server.guest_dirty_bitmap, 0, 0, guest_width, @@ -565,11 +564,14 @@ pub fn vnc_display_switch(surface: &mut DisplaySurface) { let width = vnc_width(guest_width); let height = vnc_height(guest_height); let mut locked_client = client.lock().unwrap(); + // Set Color depth. + locked_client.set_color_depth(); // Desktop_resize. + locked_client.desktop_resize(); + // Cursor define. if !mask.is_empty() { display_cursor_define(&mut locked_client, &mut cursor, &mut mask); } - locked_client.desktop_resize(); locked_client.dirty_bitmap.clear_all(); set_area_dirty( &mut locked_client.dirty_bitmap, -- Gitee From bb59ebb7bc1676d79835b9cd6d99e2bbd6a1b4cd Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 20 Sep 2022 17:23:44 +0800 Subject: [PATCH 0175/2187] VNC: Clear tasks in the send queue after switching images action. When switching the resolution of image or reconnectted by client, the server will clear the origin image. The image pointer in RectInfo of send queue will still point to the old image. So clear tasks in the send queue after switching images action. Signed-off-by: Xiao Ye --- vnc/src/vnc.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index e70e1691f..c8f8955bb 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -282,6 +282,8 @@ fn vnc_height(height: i32) -> i32 { pub fn update_client_surface(server: &mut VncServer) { unref_pixman_image(server.server_image); server.server_image = ptr::null_mut(); + // Server image changes, clear the task queue. + VNC_RECT_INFO.lock().unwrap().clear(); if server.clients.is_empty() { return; } -- Gitee From 24d2f8427063cbc2fed5efeb884ceee77bb75634 Mon Sep 17 00:00:00 2001 From: Zhao Mengmeng Date: Fri, 16 Sep 2022 16:45:19 +0800 Subject: [PATCH 0176/2187] network: return error when netdev queues not in expected range when -netdev type is vhost-user, queues's value should in range [1,16], otherwise raise an error. Signed-off-by: Zhao Mengmeng --- machine_manager/src/config/network.rs | 69 +++++++++++++++++++-------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 51608f10a..7525b8900 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -68,6 +68,17 @@ impl ConfigCheck for NetDevcfg { } } + if !is_netdev_queues_valid(self.queues) { + return Err(ErrorKind::IllegalValue( + "number queues of net device".to_string(), + 1, + true, + MAX_VIRTIO_QUEUE as u64 / 2, + true, + ) + .into()); + } + Ok(()) } } @@ -130,23 +141,6 @@ impl ConfigCheck for NetworkInterfaceConfig { return Err(ErrorKind::MacFormatError.into()); } - if let Some(vhost_type) = self.vhost_type.as_ref() { - if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" { - return Err(ErrorKind::UnknownVhostType.into()); - } - } - - if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u16 { - return Err(ErrorKind::IllegalValue( - "number queues of net device".to_string(), - 1, - true, - MAX_VIRTIO_QUEUE as u64 / 2, - true, - ) - .into()); - } - if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { return Err(ErrorKind::StringLengthTooLong( "iothread name".to_string(), @@ -198,9 +192,19 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { } if let Some(queue_pairs) = cmd_parser.get_value::("queues")? { let queues = queue_pairs * 2; - if queues > net.queues { - net.queues = queues; + + if !is_netdev_queues_valid(queues) { + return Err(ErrorKind::IllegalValue( + "number queues of net device".to_string(), + 1, + true, + MAX_VIRTIO_QUEUE as u64 / 2, + true, + ) + .into()); } + + net.queues = queues; } if let Some(tap_fd) = parse_fds(&cmd_parser, "fd")? { @@ -244,6 +248,8 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device"); } + net.check()?; + Ok(net) } @@ -467,6 +473,10 @@ fn check_mac_address(mac: &str) -> bool { true } +fn is_netdev_queues_valid(queues: u16) -> bool { + queues >= 1 && queues <= MAX_VIRTIO_QUEUE as u16 +} + #[cfg(test)] mod tests { use crate::config::get_pci_bdf; @@ -667,6 +677,27 @@ mod tests { assert!(netdev_conf.check().is_err()); } + #[test] + fn test_add_netdev_with_different_queues() { + let mut vm_config = VmConfig::default(); + + let set_queues = |q: u16| { + format!( + "vhost-user,id=netdevid{num},chardev=chardevid,queues={num}", + num = q.to_string() + ) + }; + + assert!(vm_config.add_netdev(&set_queues(0)).is_err()); + assert!(vm_config.add_netdev(&set_queues(1)).is_ok()); + assert!(vm_config + .add_netdev(&set_queues(MAX_VIRTIO_QUEUE as u16 / 2)) + .is_ok()); + assert!(vm_config + .add_netdev(&set_queues(MAX_VIRTIO_QUEUE as u16 / 2 + 1)) + .is_err()); + } + #[test] fn test_add_netdev_with_config() { let mut vm_config = VmConfig::default(); -- Gitee From 1712cb5aaacd94b70666447b7d7a5a177ae99ccd Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 30 Sep 2022 14:21:58 +0800 Subject: [PATCH 0177/2187] Fix: After the VM is stopped, the stratovirt process does not exit After the VM shutdown command is executed, the vcpu thread does not send power_button event to the main thread. As a result, the main thread still epoll wait. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 5fcfa267f..fb9de437d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -950,6 +950,8 @@ impl MachineLifecycle for StdMachine { if !self.notify_lifecycle(vmstate, KvmVmState::Shutdown) { return false; } + + self.power_button.write(1).unwrap(); true } -- Gitee From 61358f6cc586322bfb37f5d099c405dce67152ce Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 8 Oct 2022 16:10:34 +0800 Subject: [PATCH 0178/2187] Fix: the length of the Root Complex Node in iort table is error According to IO Remapping Table Platform Design Document, the length of the Root Complex Node is 36 + 20 * N. In this case, N = 1, so the length should be 56 instead of 52. In addition, the length and other offsets of the iort table need to be fixed. Signed-off-by: Mingwang Li --- acpi/src/acpi_table.rs | 3 +++ machine/src/standard_vm/aarch64/mod.rs | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index c531da55f..2f998876a 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -28,6 +28,9 @@ pub const ACPI_GTDT_CAP_ALWAYS_ON: u32 = 4; /// IORT node types, reference: ARM Document number: ARM DEN 0049B, October 2015. pub const ACPI_IORT_NODE_ITS_GROUP: u8 = 0x00; pub const ACPI_IORT_NODE_PCI_ROOT_COMPLEX: u8 = 0x02; +/// Root Complex Node in IORT +pub const ROOT_COMPLEX_ENTRY_SIZE: u16 = 36; +pub const ID_MAPPING_ENTRY_SIZE: u16 = 20; /// Interrupt controller structure types for MADT. pub const ACPI_MADT_GENERIC_CPU_INTERFACE: u8 = 11; pub const ACPI_MADT_GENERIC_DISTRIBUTOR: u8 = 12; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index fb9de437d..7f55f6d54 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -31,7 +31,7 @@ use acpi::{ ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, - INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, + ID_MAPPING_ENTRY_SIZE, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, ROOT_COMPLEX_ENTRY_SIZE, }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -648,7 +648,7 @@ impl AcpiBuilder for StdMachine { ) -> super::errors::Result { use super::errors::ResultExt; let mut iort = AcpiTable::new(*b"IORT", 2, *b"STRATO", *b"VIRTIORT", 1); - iort.set_table_len(124); + iort.set_table_len(128); // Number of IORT nodes is 2: ITS group node and Root Complex Node. iort.set_field(36, 2_u32); @@ -665,19 +665,20 @@ impl AcpiBuilder for StdMachine { // Root Complex Node iort.set_field(72, ACPI_IORT_NODE_PCI_ROOT_COMPLEX); // Length of Root Complex node - iort.set_field(73, 52_u16); + let len = ROOT_COMPLEX_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE; + iort.set_field(73, len); // Mapping counts of Root Complex Node iort.set_field(80, 1_u32); // Mapping offset of Root Complex Node - iort.set_field(84, 32_u32); + iort.set_field(84, ROOT_COMPLEX_ENTRY_SIZE as u32); // Cache of coherent device iort.set_field(88, 1_u32); // Memory flags of coherent device iort.set_field(95, 3_u8); // Identity RID mapping - iort.set_field(108, 0xffff_u32); + iort.set_field(112, 0xffff_u32); // Without SMMU, id mapping is the first node in ITS group node - iort.set_field(116, 48_u32); + iort.set_field(120, 48_u32); let iort_begin = StdMachine::add_table_to_loader(acpi_data, loader, &iort) .chain_err(|| "Fail to add IORT table to loader")?; -- Gitee From c46abf7174fdfa025cdd35965f343668e3ba31f8 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:38:53 +0800 Subject: [PATCH 0179/2187] virtiofs: add vhost_user_fs package add vhost_user_fs package for stratovirt Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- Cargo.lock | 23 ++++++++++++++++++++ Cargo.toml | 6 +++++ vhost_user_fs/Cargo.toml | 25 +++++++++++++++++++++ vhost_user_fs/src/lib.rs | 32 +++++++++++++++++++++++++++ vhost_user_fs/src/main.rs | 46 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 vhost_user_fs/Cargo.toml create mode 100644 vhost_user_fs/src/lib.rs create mode 100644 vhost_user_fs/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 984e36da9..42dbc861f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,7 @@ dependencies = [ "migration", "util", "vfio", + "vhost_user_fs", "virtio", "vmm-sys-util", "vnc", @@ -747,6 +748,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce" +[[package]] +name = "vhost_user_fs" +version = "2.2.0" +dependencies = [ + "acpi", + "address_space", + "devices", + "errno", + "error-chain", + "hypervisor", + "libc", + "log", + "machine_manager", + "migration", + "migration_derive", + "pci", + "sysbus", + "util", + "virtio", + "vmm-sys-util", +] + [[package]] name = "virtio" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index 4dcf49f07..61273ed5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ machine_manager = { path = "machine_manager" } migration = { path = "migration" } util = { path = "util" } virtio = { path = "virtio" } +vhost_user_fs = { path = "vhost_user_fs" } vfio = { path = "vfio" } [target.'cfg(not(target_env = "musl"))'.dependencies] @@ -39,6 +40,7 @@ members = [ "util", "acpi", "virtio", + "vhost_user_fs", "ozone", "vfio", ] @@ -51,6 +53,10 @@ path = "src/main.rs" name = "ozone" path = "ozone/src/main.rs" +[[bin]] +name = "vhost_user_fs" +path = "vhost_user_fs/src/main.rs" + [features] default = [] diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml new file mode 100644 index 000000000..7367b7c6b --- /dev/null +++ b/vhost_user_fs/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "vhost_user_fs" +version = "2.2.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "Provide virtio fs for VM" + +[dependencies] +errno = "0.2.7" +log = "0.4.8" +libc = ">=0.2.71" +error-chain = "0.12.4" +vmm-sys-util = ">=0.7.0" +address_space = { path = "../address_space" } +hypervisor = { path = "../hypervisor" } +machine_manager = { path = "../machine_manager" } +migration = { path = "../migration" } +migration_derive = { path = "../migration_derive" } +sysbus = { path = "../sysbus" } +util = { path = "../util" } +pci = { path = "../pci" } +acpi = { path = "../acpi" } +devices = {path = "../devices"} +virtio = {path = "../virtio"} diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs new file mode 100644 index 000000000..b6bda3446 --- /dev/null +++ b/vhost_user_fs/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[macro_use] +extern crate error_chain; + +extern crate address_space; +extern crate machine_manager; +extern crate util; +extern crate virtio; + +pub mod errors { + error_chain! { + links { + Util(util::errors::Error, util::errors::ErrorKind); + Virtio(virtio::errors::Error, virtio::errors::ErrorKind); + AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); + } + foreign_links { + Io(std::io::Error); + } + } +} diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs new file mode 100644 index 000000000..41cff8c1d --- /dev/null +++ b/vhost_user_fs/src/main.rs @@ -0,0 +1,46 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +extern crate vhost_user_fs; + +error_chain! { + links { + VhostUserFs(vhost_user_fs::errors::Error, vhost_user_fs::errors::ErrorKind); + Util(util::errors::Error, util::errors::ErrorKind); + } + foreign_links { + Io(std::io::Error); + } +} + +quick_main!(run); + +fn run() -> Result<()> { + set_panic_hook(); + Ok(()) +} + +fn set_panic_hook() { + std::panic::set_hook(Box::new(|panic_msg| { + let panic_file = panic_msg.location().map_or("", |loc| loc.file()); + let panic_line = panic_msg.location().map_or(0, |loc| loc.line()); + if let Some(msg) = panic_msg.payload().downcast_ref::<&str>() { + error!("Panic at [{}: {}]: {}.", panic_file, panic_line, msg); + } else { + error!("Panic at [{}: {}].", panic_file, panic_line); + } + })); +} -- Gitee From b868c1866f7b9000ff4090a2d866fa3d1de2a7a4 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 8 Oct 2022 19:43:48 +0800 Subject: [PATCH 0180/2187] Fix: bugfix the flags and refactoring build_pptt_table 1. According to ACPI Specification, the Processor Structure Flags of Physical package setting 1 if this node of the processor topology represents the boundary of a physical package, whether scoketed or surface mounted. 2. The build_pptt_table function is refactored because it is nested too deeply. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 73 +++++++++++++++----------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 7f55f6d54..6dc200a2a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -242,6 +242,46 @@ impl StdMachine { Ok(()) } + + fn build_pptt_cores(&self, pptt: &mut AcpiTable, cluster_offset: u32, uid: &mut u32) { + for core in 0..self.cpu_topo.cores { + if self.cpu_topo.threads > 1 { + let core_offset = pptt.table_len(); + let core_hierarchy_node = + ProcessorHierarchyNode::new(0, 0x0, cluster_offset, core as u32); + pptt.append_child(&core_hierarchy_node.aml_bytes()); + for _thread in 0..self.cpu_topo.threads { + let thread_hierarchy_node = + ProcessorHierarchyNode::new(0, 0xE, core_offset as u32, *uid); + pptt.append_child(&thread_hierarchy_node.aml_bytes()); + (*uid) += 1; + } + } else { + let core_hierarchy_node = ProcessorHierarchyNode::new(0, 0xA, cluster_offset, *uid); + pptt.append_child(&core_hierarchy_node.aml_bytes()); + (*uid) += 1; + } + } + } + + fn build_pptt_clusters(&self, pptt: &mut AcpiTable, socket_offset: u32, uid: &mut u32) { + for cluster in 0..self.cpu_topo.clusters { + let cluster_offset = pptt.table_len(); + let cluster_hierarchy_node = + ProcessorHierarchyNode::new(0, 0x0, socket_offset, cluster as u32); + pptt.append_child(&cluster_hierarchy_node.aml_bytes()); + self.build_pptt_cores(pptt, cluster_offset as u32, uid); + } + } + + fn build_pptt_sockets(&self, pptt: &mut AcpiTable, uid: &mut u32) { + for socket in 0..self.cpu_topo.sockets { + let socket_offset = pptt.table_len(); + let socket_hierarchy_node = ProcessorHierarchyNode::new(0, 0x1, 0, socket as u32); + pptt.append_child(&socket_hierarchy_node.aml_bytes()); + self.build_pptt_clusters(pptt, socket_offset as u32, uid); + } + } } impl StdMachineOps for StdMachine { @@ -885,39 +925,8 @@ impl AcpiBuilder for StdMachine { ) -> super::errors::Result { use super::errors::ResultExt; let mut pptt = AcpiTable::new(*b"PPTT", 2, *b"STRATO", *b"VIRTPPTT", 1); - let pptt_start = 0; let mut uid = 0; - for socket in 0..self.cpu_topo.sockets { - let socket_offset = pptt.table_len() - pptt_start; - let socket_hierarchy_node = ProcessorHierarchyNode::new(0, 0x2, 0, socket as u32); - pptt.append_child(&socket_hierarchy_node.aml_bytes()); - for cluster in 0..self.cpu_topo.clusters { - let cluster_offset = pptt.table_len() - pptt_start; - let cluster_hierarchy_node = - ProcessorHierarchyNode::new(0, 0x0, socket_offset as u32, cluster as u32); - pptt.append_child(&cluster_hierarchy_node.aml_bytes()); - for core in 0..self.cpu_topo.cores { - let core_offset = pptt.table_len() - pptt_start; - if self.cpu_topo.threads > 1 { - let core_hierarchy_node = - ProcessorHierarchyNode::new(0, 0x0, cluster_offset as u32, core as u32); - pptt.append_child(&core_hierarchy_node.aml_bytes()); - for _thread in 0..self.cpu_topo.threads { - let thread_hierarchy_node = - ProcessorHierarchyNode::new(0, 0xE, core_offset as u32, uid as u32); - pptt.append_child(&thread_hierarchy_node.aml_bytes()); - uid += 1; - } - } else { - let thread_hierarchy_node = - ProcessorHierarchyNode::new(0, 0xA, cluster_offset as u32, uid as u32); - pptt.append_child(&thread_hierarchy_node.aml_bytes()); - uid += 1; - } - } - } - } - pptt.set_table_len(pptt.table_len()); + self.build_pptt_sockets(&mut pptt, &mut uid); let pptt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &pptt) .chain_err(|| "Fail to add PPTT table to loader")?; Ok(pptt_begin as u64) -- Gitee From 4b28edee25166bce7b76e03888ea24f94ba32028 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:39:19 +0800 Subject: [PATCH 0181/2187] virtiofs: add cmdline.rs to parse cmline args support parsing command line parameter Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/cmdline.rs | 126 +++++++++++++++++++++++++++++++++++ vhost_user_fs/src/lib.rs | 1 + vhost_user_fs/src/main.rs | 29 +++++++- 3 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 vhost_user_fs/src/cmdline.rs diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs new file mode 100644 index 000000000..f969a4fa7 --- /dev/null +++ b/vhost_user_fs/src/cmdline.rs @@ -0,0 +1,126 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +// Read the programe version in `Cargo.toml`. +const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); +const MAX_STRING_LENGTH: usize = 255; + +use crate::errors::{Result, ResultExt}; +use std::path::PathBuf; +use util::arg_parser::{Arg, ArgMatches, ArgParser}; + +/// This function is to define all command line arguments. +pub fn create_args_parser<'a>() -> ArgParser<'a> { + ArgParser::new("VhostUserFs") + .version(VERSION.unwrap_or("unknown")) + .author("Huawei Technologies Co., Ltd") + .about("The process of Virtio fs for StratoVirt.") + .arg( + Arg::with_name("source dir") + .long("source") + .value_name("source directory in host") + .help("set source directory in host") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("socket path") + .long("socket-path") + .value_name("sock path which communicates with StratoVirt") + .help("sock path which communicates with StratoVirt") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("rlimit nofile") + .long("rlimit-nofile") + .value_name("file resource limits for the process") + .help("set file resource limits for the process") + .takes_value(true), + ) + .arg( + Arg::with_name("display log") + .long("D") + .value_name("log_path") + .help("output log to logfile") + .takes_value(true) + .can_no_value(true), + ) +} + +/// Filesystem configuration parsed from command line for the process. +#[derive(Clone, Default, Debug)] +pub struct FsConfig { + /// Source directory in host which can be accessed by guest. + pub source_dir: String, + /// The path of socket file which communicates with StratoVirt. + pub sock_path: String, + /// The limit of file resources which can be opened for the process. + pub rlimit_nofile: Option, +} + +impl FsConfig { + fn check_config(&self) -> Result<()> { + if self.source_dir.len() > MAX_STRING_LENGTH { + bail!( + "The length of source directory is too long {}", + self.source_dir.len() + ); + } + + if self.sock_path.len() > MAX_STRING_LENGTH { + bail!( + "The length of socket file path is too long {}", + self.sock_path.len() + ); + } + + let source_dir = PathBuf::from(&self.source_dir); + if !source_dir.is_dir() { + bail!( + "The source directory is not a directory {}", + self.source_dir + ); + } + + Ok(()) + } +} + +/// Construct a filesystem configuration parsed from command line. +/// +/// # Arguments +/// * `args` - The collection of information about the arguments from command line. +pub fn create_fs_config(args: &ArgMatches) -> Result { + let mut fs_config = FsConfig::default(); + + if let Some(source_dir) = args.value_of("source dir") { + fs_config.source_dir = source_dir; + } + + if let Some(sock_path) = args.value_of("socket path") { + fs_config.sock_path = sock_path; + } + + if let Some(rlimit_nofile) = args.value_of("rlimit nofile") { + let limit = rlimit_nofile + .parse::() + .chain_err(|| "Failed to parse rlimit nofile")?; + fs_config.rlimit_nofile = Some(limit); + } + + fs_config + .check_config() + .chain_err(|| "Precheck failed, Config is unhealthy, stop running")?; + + Ok(fs_config) +} diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index b6bda3446..ed7658d08 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -18,6 +18,7 @@ extern crate machine_manager; extern crate util; extern crate virtio; +pub mod cmdline; pub mod errors { error_chain! { links { diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 41cff8c1d..97464e0e2 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -15,7 +15,9 @@ extern crate error_chain; #[macro_use] extern crate log; extern crate vhost_user_fs; - +use std::os::unix::fs::OpenOptionsExt; +use util::logger; +use vhost_user_fs::cmdline::create_args_parser; error_chain! { links { VhostUserFs(vhost_user_fs::errors::Error, vhost_user_fs::errors::ErrorKind); @@ -29,10 +31,35 @@ error_chain! { quick_main!(run); fn run() -> Result<()> { + let cmd_args = create_args_parser().get_matches()?; + + if let Some(logfile_path) = cmd_args.value_of("display log") { + init_log(logfile_path)?; + } set_panic_hook(); Ok(()) } +fn init_log(logfile_path: String) -> Result<()> { + if logfile_path.is_empty() { + logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) + .chain_err(|| "Failed to init logger")?; + } else { + let logfile = std::fs::OpenOptions::new() + .read(false) + .write(true) + .append(true) + .create(true) + .mode(0o640) + .open(logfile_path.clone()) + .chain_err(|| format!("Failed to open log file {}", logfile_path))?; + logger::init_logger_with_env(Some(Box::new(logfile))) + .chain_err(|| format!("Failed to init logger {}", logfile_path))?; + } + + Ok(()) +} + fn set_panic_hook() { std::panic::set_hook(Box::new(|panic_msg| { let panic_file = panic_msg.location().map_or("", |loc| loc.file()); -- Gitee From 425331690900d5304632be9a8cce57636f6a7085 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:39:28 +0800 Subject: [PATCH 0182/2187] virtiofs: add fuse_msg.rs about fuse message implement FuseBuffer, FuseIovec, FuseAttr add fuse opcodes Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/fuse_msg.rs | 1011 +++++++++++++++++++++++++++++++++ 1 file changed, 1011 insertions(+) create mode 100644 vhost_user_fs/src/fuse_msg.rs diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs new file mode 100644 index 000000000..30bd93d4c --- /dev/null +++ b/vhost_user_fs/src/fuse_msg.rs @@ -0,0 +1,1011 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub const FUSE_LOOKUP: u32 = 1; +pub const FUSE_FORGET: u32 = 2; +pub const FUSE_GETATTR: u32 = 3; +pub const FUSE_SETATTR: u32 = 4; +pub const FUSE_READLINK: u32 = 5; +pub const FUSE_SYMLINK: u32 = 6; +pub const FUSE_MKNOD: u32 = 8; +pub const FUSE_MKDIR: u32 = 9; +pub const FUSE_UNLINK: u32 = 10; +pub const FUSE_RMDIR: u32 = 11; +pub const FUSE_RENAME: u32 = 12; +pub const FUSE_LINK: u32 = 13; +pub const FUSE_OPEN: u32 = 14; +pub const FUSE_READ: u32 = 15; +pub const FUSE_WRITE: u32 = 16; +pub const FUSE_STATFS: u32 = 17; +pub const FUSE_RELEASE: u32 = 18; +pub const FUSE_FSYNC: u32 = 20; +pub const FUSE_SETXATTR: u32 = 21; +pub const FUSE_GETXATTR: u32 = 22; +pub const FUSE_LISTXATTR: u32 = 23; +pub const FUSE_REMOVEXATTR: u32 = 24; +pub const FUSE_FLUSH: u32 = 25; +pub const FUSE_INIT: u32 = 26; +pub const FUSE_OPENDIR: u32 = 27; +pub const FUSE_READDIR: u32 = 28; +pub const FUSE_RELEASEDIR: u32 = 29; +pub const FUSE_FSYNCDIR: u32 = 30; +pub const FUSE_GETLK: u32 = 31; +pub const FUSE_SETLK: u32 = 32; +pub const FUSE_SETLKW: u32 = 33; +pub const FUSE_ACCESS: u32 = 34; +pub const FUSE_CREATE: u32 = 35; +pub const FUSE_INTERRUPT: u32 = 36; +pub const FUSE_BMAP: u32 = 37; +pub const FUSE_DESTROY: u32 = 38; +pub const FUSE_IOCTL: u32 = 39; +pub const FUSE_POLL: u32 = 40; +pub const FUSE_NOTIFY_REPLY: u32 = 41; +pub const FUSE_BATCH_FORGET: u32 = 42; +pub const FUSE_FALLOCATE: u32 = 43; +pub const FUSE_READDIRPLUS: u32 = 44; +pub const FUSE_RENAME2: u32 = 45; +pub const FUSE_LSEEK: u32 = 46; +pub const FUSE_COPY_FILE_RANGE: u32 = 47; +pub const FUSE_SETUPMAPPING: u32 = 48; +pub const FUSE_REMOVEMAPPING: u32 = 49; + +/// The kernel version which is supported by fuse messages. +pub const FUSE_KERNEL_VERSION: u32 = 7; +/// The minor version which is supported by fuse messages. +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; + +/// The capability bit supports asynchronous read requests. +pub const FUSE_CAP_ASYNC_READ: u32 = 1 << 0; +/// The capability bit supports posix file locks. +pub const FUSE_CAP_POSIX_LOCKS: u32 = 1 << 1; +/// The capability bit supports the O_TRUNC open flag. +pub const FUSE_CAP_ATOMIC_O_TRUNC: u32 = 1 << 3; +/// The capability bit supports lookups of "." and "..". +pub const FUSE_CAP_EXPORT_SUPPORT: u32 = 1 << 4; +/// The capability bit don't apply umask to file mode on create operation. +pub const FUSE_CAP_DONT_MASK: u32 = 1 << 6; +/// The capability bit supports BSD file locks. +pub const FUSE_CAP_FLOCK_LOCKS: u32 = 1 << 10; +/// The capability bit automatically checks invalid cached file. +pub const FUSE_CAP_AUTO_INVAL_DATA: u32 = 1 << 12; +/// The capability bit supports readdirplus. +pub const FUSE_CAP_READDIRPLUS: u32 = 1 << 13; +/// The capability bit supports adaptive readdirplus. +pub const FUSE_CAP_READDIRPLUS_AUTO: u32 = 1 << 14; +/// The capability bit supports asynchronous direct I/O submission. +pub const FUSE_CAP_ASYNC_DIO: u32 = 1 << 15; +/// The capability bit supports for parallel directory operations. +pub const FUSE_CAP_PARALLEL_DIROPS: u32 = 1 << 18; +/// The capability bit supports POSIX ACLs. +pub const FUSE_CAP_POSIX_ACL: u32 = 1 << 19; + +/// The supported bit that supports asynchronous read requests. +pub const FUSE_ASYNC_READ: u32 = 1 << 0; +/// The supported bit that supports posix file locks. +pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; +/// The supported bit that supports the O_TRUNC open flag. +pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; +/// The supported bit that supports lookups of "." and "..". +pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; +/// The supported bit that don't apply umask to file mode on create operation. +pub const FUSE_DONT_MASK: u32 = 1 << 6; +/// The supported bit that supports BSD file locks. +pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; +/// The supported bit that automatically checks invalid cached file. +pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; +/// The supported bit that supports readdirplus. +pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; +/// The supported bit that supports adaptive readdirplus. +pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; +/// The supported bit that supports asynchronous direct I/O submission. +pub const FUSE_ASYNC_DIO: u32 = 1 << 15; +/// The capability bit that supports for parallel directory operations. +pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; +/// The capability bit that supports POSIX ACLs. +pub const FUSE_POSIX_ACL: u32 = 1 << 20; +/// The capability bit that needs to reply the max number of pages in init fuse message. +pub const FUSE_MAX_PAGES: u32 = 1 << 22; + +pub const FATTR_MODE: u32 = 1 << 0; +pub const FATTR_UID: u32 = 1 << 1; +pub const FATTR_GID: u32 = 1 << 2; +pub const FATTR_SIZE: u32 = 1 << 3; +pub const FATTR_ATIME: u32 = 1 << 4; +pub const FATTR_MTIME: u32 = 1 << 5; +pub const FATTR_FH: u32 = 1 << 6; +pub const FATTR_ATIME_NOW: u32 = 1 << 7; +pub const FATTR_MTIME_NOW: u32 = 1 << 8; +pub const FATTR_LOCKOWNER: u32 = 1 << 9; +pub const FATTR_CTIME: u32 = 1 << 10; + +/// Successfully process the fuse message. +pub const FUSE_OK: i32 = 0; +pub const FUSE_SET_ATTR_MODE: u32 = 1 << 0; +pub const FUSE_SET_ATTR_UID: u32 = 1 << 1; +pub const FUSE_SET_ATTR_GID: u32 = 1 << 2; +pub const FUSE_SET_ATTR_SIZE: u32 = 1 << 3; +pub const FUSE_SET_ATTR_ATIME: u32 = 1 << 4; +pub const FUSE_SET_ATTR_MTIME: u32 = 1 << 5; +pub const FUSE_SET_ATTR_ATIME_NOW: u32 = 1 << 7; +pub const FUSE_SET_ATTR_MTIME_NOW: u32 = 1 << 8; +pub const FUSE_SET_ATTR_CTIME: u32 = 1 << 10; + +use std::cmp; +use std::collections::VecDeque; +use std::ffi::CString; +use std::mem::size_of; +use std::sync::Arc; + +use error_chain::ChainedError; + +use address_space::AddressSpace; +use util::byte_code::ByteCode; + +use crate::errors::{Result, ResultExt}; +use virtio::ElemIovec; + +/// Get the buffers from the element of virtio queue to parse fuse message or +/// reply fuse message. +pub struct FuseBuffer { + /// Get the buffers to read or write from the element of virtio queue. + pub bufs: VecDeque, + /// The total size of the buffers from the element of virtio queue. + bytes_total: usize, + /// The processed bytes to read or write fuse message. + bytes_processed: usize, +} + +impl FuseBuffer { + /// Construct a fuse buffer to process fuse message. + /// + /// # Arguments + /// + /// * `elem_iovec` - The vectors of IO vector element from virtio queue. + pub fn new(elem_iovec: &[ElemIovec]) -> Self { + let mut bytes_total = 0; + let mut bufs = VecDeque::new(); + + for iov in elem_iovec { + bytes_total += iov.len as usize; + bufs.push_back(*iov); + } + + FuseBuffer { + bufs, + bytes_total, + bytes_processed: 0_usize, + } + } + + /// Read the CString ending with '\0' from the fuse buffers. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + pub fn read_cstring(&mut self, sys_mem: &Arc) -> Result { + let bytes_remain = self.bytes_total - self.bytes_processed; + let mut buffer = vec![0; bytes_remain]; + + let mut offset = 0_usize; + for buf in &self.bufs { + let mut slice = &mut buffer[offset..]; + let read_count = cmp::min(slice.len(), buf.len as usize); + sys_mem + .read(&mut slice, buf.addr, read_count as u64) + .chain_err(|| "Failed to read buffer for fuse req")?; + offset += read_count; + } + + let pos = match buffer.iter().position(|c| *c == b'\0') { + Some(p) => p + 1, + None => bail!("It is not a string"), + }; + + let str_slice = buffer.as_slice(); + let cstring = unsafe { CString::from_vec_unchecked(str_slice[0..pos].to_vec()) }; + + // Remove the processed bytes in self.bufs. + let mut need_read_count = pos; + let bufs = self.bufs.clone(); + for buf in bufs { + let read_count = cmp::min(need_read_count, buf.len as usize); + self.bytes_processed += read_count; + + if let Some(buftmp) = self.bufs.pop_front() { + if read_count < buftmp.len as usize { + // Add the remain length to the head of self.bufs. + let len = buftmp.len - read_count as u32; + let addr = buftmp.addr.unchecked_add(read_count as u64); + let remain = ElemIovec { addr, len }; + self.bufs.push_front(remain); + break; + } else { + need_read_count -= buftmp.len as usize; + } + } + } + + Ok(cstring) + } + + fn read_slice( + &mut self, + sys_mem: &Arc, + dst: &mut [u8], + count: usize, + ) -> Result<()> { + if dst.len() != count { + bail!( + "The length {} of dst slice is not equal to the count {}", + dst.len(), + count + ); + } + + let read_end = match self.bytes_processed.checked_add(count) { + Some(end_) => end_, + None => bail!("The read count {} {} overflow", count, self.bytes_processed), + }; + + if read_end > self.bytes_total { + bail!( + "The read count {} exceeds maximum {}", + read_end, + self.bytes_total + ); + } + + let bufs = self.bufs.clone(); + let mut offset = 0_usize; + for buf in bufs { + let mut slice = &mut dst[offset..]; + let read_count = cmp::min(slice.len(), buf.len as usize); + + sys_mem + .read(&mut slice, buf.addr, read_count as u64) + .chain_err(|| "Failed to read buffer for fuse req")?; + self.bytes_processed += read_count; + offset += read_count; + + // Remove the processed bytes in self.bufs. + if let Some(buftmp) = self.bufs.pop_front() { + if read_count < buftmp.len as usize { + // Add the remain length to the head of self.bufs. + let len = buftmp.len - read_count as u32; + let addr = buftmp.addr.unchecked_add(read_count as u64); + let remain = ElemIovec { addr, len }; + self.bufs.push_front(remain); + break; + } + } + } + + Ok(()) + } + + /// read an object from the fuse buffers. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// + /// # Note + /// To use this method, it is necessary to implement `ByteCode` trait for your object. + pub fn read_obj(&mut self, sys_mem: &Arc) -> Result { + let mut obj = T::default(); + self.read_slice(sys_mem, &mut obj.as_mut_bytes(), size_of::())?; + Ok(obj) + } + + fn write_slice(&mut self, sys_mem: &Arc, src: &[u8], count: usize) -> Result<()> { + if src.len() != count { + bail!( + "The length {} of src slice is not equal to the count {}", + src.len(), + count + ); + } + + let write_end = match self.bytes_processed.checked_add(count) { + Some(end_) => end_, + None => bail!("The read count {} {} overflow", count, self.bytes_processed), + }; + + if write_end > self.bytes_total { + bail!( + "The read count {} exceeds maximum {}", + write_end, + self.bytes_total + ); + } + + let bufs = self.bufs.clone(); + let mut offset = 0_usize; + for buf in bufs { + let mut slice = &src[offset..]; + let write_count = cmp::min(slice.len(), buf.len as usize); + + sys_mem + .write(&mut slice, buf.addr, write_count as u64) + .chain_err(|| "Failed to read buffer for fuse req")?; + self.bytes_processed += write_count; + offset += write_count; + + // Remove the processed bytes in self.bufs. + if let Some(buftmp) = self.bufs.pop_front() { + if write_count < buftmp.len as usize { + // Add the remain length to the head of self.bufs. + let len = buftmp.len - write_count as u32; + let addr = buftmp.addr.unchecked_add(write_count as u64); + let remain = ElemIovec { addr, len }; + self.bufs.push_front(remain); + break; + } + } + } + + Ok(()) + } + + /// write an object to the fuse buffers. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// * `data` - The object the will be written to the fuse buffers. + /// + /// # Note + /// To use this method, it is necessary to implement `ByteCode` trait for your object. + pub fn write_obj(&mut self, sys_mem: &Arc, data: &T) -> Result<()> { + self.write_slice(sys_mem, data.as_bytes(), size_of::()) + } + + /// Process the data for host file. if is_read is true, writing the data which is read from host + /// file to the fuse buffers. if is_read is false, writing the data which is read from the fuse + /// buffers to host file. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// * `fd` - The file descriptor in host. + /// * `offset` - The offset which needs to be read and written in host file. + /// * `size` - The size which needs to be read and written in host file. + /// * `is_read` - If it is true, writing the data which is read from host file to the fuse buffers. + /// If it is false, writing the data which is read from the fuse buffers to host file. + pub fn access_file( + &mut self, + sys_mem: &Arc, + fd: i32, + offset: u64, + size: u32, + is_read: bool, + ) -> Result { + let mut remain_len = size; + let mut file_off = offset; + + let mut index = 0; + let mut bufs = self.bufs.clone(); + + loop { + if index >= bufs.len() { + bail!("{} out of bufs's index", index); + } + + let buf = if let Some(b) = bufs.get_mut(index) { + b + } else { + bail!("{} out of bufs's bound", index); + }; + + let len = if remain_len < buf.len { + remain_len + } else { + buf.len + }; + + let hva = if let Some(hva) = sys_mem.get_host_address(buf.addr) { + hva + } else { + bail!("read file error: get hva failed."); + }; + + let iov = vec![libc::iovec { + iov_base: hva as *mut libc::c_void, + iov_len: len as usize, + }]; + + let ret = unsafe { + if is_read { + libc::preadv(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) + } else { + libc::pwritev(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) + } + } as u32; + if ret == u32::MAX { + bail!("read file error"); + } + + remain_len -= ret; + file_off += ret as u64; + + self.bufs.pop_front(); + if ret < len { + buf.addr.0 += ret as u64; + buf.len -= ret; + + self.bufs.push_front(*buf); + } else { + index += 1; + } + + if ret == 0 || remain_len == 0 { + break; // finish. + } + } + + Ok(size - remain_len) + } +} + +/// Save the address and the length for replying fuse message. +pub struct FuseIovec<'a> { + body: &'a [u8], + len: usize, +} + +impl<'a> FuseIovec<'a> { + /// Convert an object to the struct of FuseIovec for replying fuse message. + /// + /// # Arguments + /// + /// * `obj` - The object the will be converted to the struct of FuseIovec. + /// + /// # Note + /// To use this method, it is necessary to implement `ByteCode` trait for your object. + pub fn from_obj(obj: &'a T) -> Self { + let body = obj.as_bytes(); + FuseIovec { + body, + len: body.len(), + } + } + + /// Convert a slice to the struct of FuseIovec for replying fuse message. + /// + /// # Arguments + /// + /// * `obj` - The slice the will be converted to the struct of FuseIovec. + pub fn from_slice(body: &'a [u8]) -> Self { + FuseIovec { + body, + len: body.len(), + } + } +} + +/// Reply the fuse messages by writing the data to the writable fuse buffers. +/// +/// # Arguments +/// +/// * `writer` - The writable fuse buffers. +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `in_header` - The in_header reading from the read-only fuse buffers. +/// * `err` - The error number for processing the fuse message. If it is ok, set +/// error number to 0. If it is false, set error number from linux. +/// * `body_opt` - The body for replying the fuse message needs to be written +/// to fuse buffers. +/// * `body_len` - The length fo body for replying the fuse message. if the body +/// is none, set the length to 0. +pub fn reply_fuse_msg( + writer: &mut FuseBuffer, + sys_mem: &Arc, + in_header: &FuseInHeader, + err: i32, + body_opt: Option>, + body_len: usize, +) -> u32 { + let len = size_of::() + body_len; + let mut written_len = len as u32; + + let fuse_out_header = FuseOutHeader { + len: len as u32, + error: -err, + unique: in_header.unique, + }; + + if let Err(e) = writer.write_obj(sys_mem, &fuse_out_header) { + error!( + "Failed to write out_header of fuse msg {}, {}", + in_header.opcode, + e.display_chain(), + ); + written_len = 0_u32; + }; + + //write the body of fuse message in address space + if let Some(body) = body_opt { + for fuse_iov in body.iter() { + if let Err(e) = writer.write_slice(sys_mem, fuse_iov.body, fuse_iov.len) { + error!( + "Failed to write the body of fuse msg {}, {}", + in_header.opcode, + e.display_chain(), + ); + written_len = 0_u32; + } + } + } + + written_len +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInHeader { + pub len: u32, + pub opcode: u32, + pub unique: u64, + pub nodeid: u64, + pub uid: u32, + pub gid: u32, + pub pid: u32, + pub padding: u32, +} + +impl ByteCode for FuseInHeader {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOutHeader { + pub len: u32, + pub error: i32, + pub unique: u64, +} + +impl ByteCode for FuseOutHeader {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseAttr { + pub ino: u64, + pub size: u64, + pub blocks: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub nlink: u32, + pub uid: u32, + pub gid: u32, + pub rdev: u32, + pub blksize: u32, + pub flags: u32, +} + +impl ByteCode for FuseAttr {} + +impl FuseAttr { + pub fn from_stat(stat: libc::stat) -> Self { + FuseAttr { + ino: stat.st_ino, + size: stat.st_size as u64, + blocks: stat.st_blocks as u64, + atime: stat.st_atime as u64, + mtime: stat.st_mtime as u64, + ctime: stat.st_ctime as u64, + atimensec: stat.st_atime_nsec as u32, + mtimensec: stat.st_mtime_nsec as u32, + ctimensec: stat.st_ctime_nsec as u32, + mode: stat.st_mode, + nlink: stat.st_nlink as u32, + uid: stat.st_uid, + gid: stat.st_gid, + rdev: stat.st_rdev as u32, + blksize: stat.st_blksize as u32, + flags: 0, + } + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseEntryOut { + pub nodeid: u64, + pub generation: u64, + pub entry_valid: u64, + pub attr_valid: u64, + pub entry_valid_nsec: u32, + pub attr_valid_nsec: u32, + pub attr: FuseAttr, +} + +impl ByteCode for FuseEntryOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetIn { + pub nlookup: u64, +} + +impl ByteCode for FuseForgetIn {} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseAttrOut { + pub attr_valid: u64, + pub attr_valid_nsec: u32, + pub dummy: u32, + pub attr: FuseAttr, +} + +impl ByteCode for FuseAttrOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseGetAttrIn { + pub getattr_flags: u32, + pub dummy: u32, + pub fh: u64, +} + +impl ByteCode for FuseGetAttrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseSetattrIn { + pub valid: u32, + pub padding: u32, + pub fh: u64, + pub size: u64, + pub lock_owner: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub unused4: u32, + pub uid: u32, + pub gid: u32, + pub unused5: u32, +} + +impl ByteCode for FuseSetattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseMknodIn { + pub mode: u32, + pub rdev: u32, + pub umask: u32, + pub padding: u32, +} + +impl ByteCode for FuseMknodIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseMkdirIn { + pub mode: u32, + pub umask: u32, +} + +impl ByteCode for FuseMkdirIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseRenameIn { + pub newdir: u64, +} + +impl ByteCode for FuseRenameIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLinkIn { + pub oldnodeid: u64, +} + +impl ByteCode for FuseLinkIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOpenIn { + pub flags: u32, + pub unused: u32, +} + +impl ByteCode for FuseOpenIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOpenOut { + pub fh: u64, + pub open_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseOpenOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseReadIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub read_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseReadIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseWriteIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub write_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseWriteIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseWriteOut { + pub size: u32, + pub padding: u32, +} + +impl ByteCode for FuseWriteOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseKstatfs { + pub blocks: u64, + pub bfree: u64, + pub bavail: u64, + pub files: u64, + pub ffree: u64, + pub bsize: u32, + pub namelen: u32, + pub frsize: u32, + pub padding: u32, + pub spare: [u32; 6], +} + +impl ByteCode for FuseKstatfs {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseStatfsOut { + pub st: FuseKstatfs, +} + +impl ByteCode for FuseStatfsOut {} + +impl FuseStatfsOut { + pub fn from_stat(stat: libc::statvfs) -> Self { + let st = FuseKstatfs { + blocks: stat.f_blocks, + bfree: stat.f_bfree, + bavail: stat.f_bavail, + files: stat.f_files, + ffree: stat.f_ffree, + bsize: stat.f_bsize as u32, + namelen: stat.f_namemax as u32, + frsize: stat.f_frsize as u32, + ..Default::default() + }; + + FuseStatfsOut { st } + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseReleaseIn { + pub fh: u64, + pub flags: u32, + pub release_flags: u32, + pub lock_owner: u64, +} + +impl ByteCode for FuseReleaseIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFsyncIn { + pub fh: u64, + pub fsync_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseFsyncIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseSetxattrIn { + pub size: u32, + pub flags: u32, +} + +impl ByteCode for FuseSetxattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseGetxattrIn { + pub size: u32, + pub padding: u32, +} + +impl ByteCode for FuseGetxattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInitIn { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, +} + +impl ByteCode for FuseInitIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInitOut { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, + pub max_background: u16, + pub congestion_threshold: u16, + pub max_write: u32, + pub time_gran: u32, + pub max_pages: u16, + pub map_alignment: u16, + pub unused: [u32; 8], +} + +impl ByteCode for FuseInitOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseDirent { + pub ino: u64, + pub off: u64, + pub namelen: u32, + pub type_: u32, + pub name: [u8; 0], +} + +impl ByteCode for FuseDirent {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseDirentplus { + pub entry_out: FuseEntryOut, + pub dirent: FuseDirent, +} + +impl ByteCode for FuseDirentplus {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFlushIn { + pub fh: u64, + pub unused: u32, + pub padding: u32, + pub lock_owner: u64, +} + +impl ByteCode for FuseFlushIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFileLock { + pub start: u64, + pub end: u64, + pub lock_type: u32, + pub pid: u32, +} + +impl ByteCode for FuseFileLock {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLkIn { + pub fh: u64, + pub owner: u64, + pub lk: FuseFileLock, + pub lk_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseLkIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLkOut { + pub lk: FuseFileLock, +} + +impl ByteCode for FuseLkOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseCreateIn { + pub flags: u32, + pub mode: u32, + pub umask: u32, + pub padding: u32, +} + +impl ByteCode for FuseCreateIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseBatchForgetIn { + pub count: u32, + pub dummy: u32, +} + +impl ByteCode for FuseBatchForgetIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetDataIn { + pub ino: u64, + pub nlookup: u64, +} + +impl ByteCode for FuseForgetDataIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFallocateIn { + pub fh: u64, + pub offset: u64, + pub length: u64, + pub mode: u32, + pub padding: u32, +} + +impl ByteCode for FuseFallocateIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLseekIn { + pub fh: u64, + pub offset: u64, + pub whence: u32, + pub padding: u32, +} + +impl ByteCode for FuseLseekIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLseekOut { + pub offset: u64, +} + +impl ByteCode for FuseLseekOut {} -- Gitee From 82523ea76e2682299582174f1fcf1721048ee0d3 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:41:36 +0800 Subject: [PATCH 0183/2187] virtiofs: add fs_ops.rs about operations on files add functions about operations on files Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/fs_ops.rs | 860 ++++++++++++++++++++++++++++++++++++ 1 file changed, 860 insertions(+) create mode 100644 vhost_user_fs/src/fs_ops.rs diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs new file mode 100644 index 000000000..83b4e9fcd --- /dev/null +++ b/vhost_user_fs/src/fs_ops.rs @@ -0,0 +1,860 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +const MAX_PATH_LEN: usize = 256; +const OFFSET_MAX: u64 = 0x7fffffffffffffff; + +use std::ffi::CString; +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use super::fuse_msg::*; + +/// The pointer to open a directory. +pub type DirPtr = *mut libc::DIR; +/// The pointer to a directory entry in the directory stream. +pub type DirentPtr = *mut libc::dirent; + +/// Get the information of a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `flags` - The flags used to get the information of the file. +pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { + let mut stat: libc::stat = unsafe { std::mem::zeroed() }; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fstatat(file.as_raw_fd(), name.as_ptr(), &mut stat, flags) } < 0 { + return (stat, errno::errno().0); + } + + (stat, FUSE_OK) +} + +/// Open a file with the path name. +/// +/// # Arguments +/// +/// * `name` - The path name in the host filesystem. +/// * `flags` - The flags used to open a file. +pub fn open(name: CString, flags: i32) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + let fd = unsafe { libc::open(name.as_ptr(), flags) }; + if fd < 0 { + return (None, errno::errno().0); + } + + let file = unsafe { File::from_raw_fd(fd) }; + + (Some(file), FUSE_OK) +} + +/// Open a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode used to open a file. +pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + + let fd = unsafe { libc::openat(file.as_raw_fd(), name.as_ptr(), flags, mode) }; + if fd < 0 { + return (None, errno::errno().0); + } + + let file = unsafe { File::from_raw_fd(fd) }; + + (Some(file), FUSE_OK) +} + +/// Change permissions of a file. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `mode` - The mode indicates the permissions of the file will be set. +pub fn fchmod(file: &File, mode: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fchmod(file.as_raw_fd(), mode) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Change permissions of a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode indicates the permissions of the file will be set. +pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fchmodat(file.as_raw_fd(), name.as_ptr(), mode, 0) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Change the owner and group of a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `uid` - The user id will be set. +/// * `gid` - The group id will be set. +/// * `flags` - The flags indicates the action of file will be set. +pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fchownat(file.as_raw_fd(), name.as_ptr(), uid, gid, flags) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Truncate file to specified length. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `size` - The size of truncating file. +pub fn ftruncate(file: &File, size: u64) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::ftruncate(file.as_raw_fd(), size as i64) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Update the timestamps of a file with nanosecond precision. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `a_sec` - The second of last access time. +/// * `a_nsec` - The nanosecond of last access time. +/// * `m_sec` - The second of last modification time. +/// * `m_nsec` - The nanosecond of last modification time. +pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) -> i32 { + let tv = vec![ + libc::timespec { + tv_sec: a_sec as i64, + tv_nsec: a_nsec, + }, + libc::timespec { + tv_sec: m_sec as i64, + tv_nsec: m_nsec, + }, + ]; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::futimens(file.as_raw_fd(), tv.as_ptr()) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Update the timestamps with nanosecond precision by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `a_sec` - The second of last access time. +/// * `a_nsec` - The nanosecond of last access time. +/// * `m_sec` - The second of last modification time. +/// * `m_nsec` - The nanosecond of last modification time. +pub fn utimensat( + file: &File, + name: CString, + a_sec: u64, + a_nsec: i64, + m_sec: u64, + m_nsec: i64, + flags: i32, +) -> i32 { + let tv = vec![ + libc::timespec { + tv_sec: a_sec as i64, + tv_nsec: a_nsec, + }, + libc::timespec { + tv_sec: m_sec as i64, + tv_nsec: m_nsec, + }, + ]; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::utimensat(file.as_raw_fd(), name.as_ptr(), tv.as_ptr(), flags) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Read value of a symbolic link by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { + let mut buf = vec![0; MAX_PATH_LEN + 1]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::readlinkat( + file.as_raw_fd(), + path.as_ptr(), + buf.as_mut_ptr() as *mut libc::c_char, + buf.len(), + ) + }; + + if ret == -1 { + return (None, errno::errno().0 as i32); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +/// Creates a symbolic link to the target path name. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `link_name` - The link name is new path name for the target path name. +pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { libc::symlinkat(link_name.as_ptr(), file.as_raw_fd(), name.as_ptr()) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +/// Change user id and group id in the process. +/// +/// # Arguments +/// +/// * `new_uid` - The user id will be changed to the current user id. +/// * `new_gid` - The group id will be changed to the current group id. +/// * `old_uid` - The old user id will be returned. +/// * `old_gid` - The old group id will be returned. +pub fn change_uid_gid(new_uid: u32, new_gid: u32, old_uid: &mut u32, old_gid: &mut u32) -> i32 { + let current_uid = unsafe { libc::geteuid() }; + let current_gid = unsafe { libc::getegid() }; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, new_gid, -1) }; + if ret == -1 { + return errno::errno().0; + } + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, new_uid, -1) }; + if ret == -1 { + unsafe { libc::syscall(libc::SYS_setresgid, -1, current_gid, -1) }; + + return errno::errno().0; + } + + *old_uid = current_uid; + *old_gid = current_gid; + FUSE_OK +} + +/// Recover user id and group id in the process. +/// +/// # Arguments +/// +/// * `old_uid` - The old user id will be recovered in the process. +/// * `old_gid` - The old group id will be recovered in the process. +pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { + let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, old_uid, -1) }; + if ret == -1 { + panic!("Failed to recover uid {} {}", old_uid, old_gid); + } + + let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, old_gid, -1) }; + if ret == -1 { + panic!("Failed to recover gid {} {}", old_uid, old_gid); + } + + FUSE_OK +} + +/// Create a special or ordinary file by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode indicates both the file mode to use and the type of node to be created. +/// * `rdev` - The rdev indicates the major and minor numbers of the special file. +pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { libc::mknodat(file.as_raw_fd(), name.as_ptr(), mode, rdev as u64) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +/// Create a directory by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode indicates the permissions of the new directory. +pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::mkdirat(file.as_raw_fd(), name.as_ptr(), mode) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Delete a name in host filesystem by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `flags` - The flags indicates the operation of deleting a name. +pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { libc::unlinkat(file.as_raw_fd(), name.as_ptr(), flags) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +/// Modify a name in host filesystem by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `olddir` - The directory file handler saves the file descriptor of starting directory to look up for the +/// old file. +/// * `name` - The name indicates the file path is relative to the starting of old directory. +/// * `newdir` - The directory file handler saves the file descriptor of starting directory to look up for the +/// new file. +/// * `newname` - The name indicates the file path is relative to the starting of new directory. +pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { + libc::renameat( + olddir.as_raw_fd(), + name.as_ptr(), + newdir.as_raw_fd(), + newname.as_ptr(), + ) + }; + + if ret != FUSE_OK { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +/// Change a name for file in host filesystem by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `old_file` - The file handler saves the file descriptor of starting old directory to look up for the +/// file. +/// * `old_name` - The name indicates the file path is relative to the starting of old directory. +/// * `new_file` - The file handler saves the file descriptor of starting new directory to look up for the +/// file. +/// * `new_name` - The name indicates the file path is relative to the starting of new directory. +/// * `flags` - The flags indicates the operation of change a name. +pub fn linkat( + old_file: &File, + old_name: CString, + new_file: &File, + new_name: CString, + flags: i32, +) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { + libc::linkat( + old_file.as_raw_fd(), + old_name.as_ptr(), + new_file.as_raw_fd(), + new_name.as_ptr(), + flags, + ) + }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +/// Get the information about a mounted filesystem. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { + let mut stat: libc::statvfs = unsafe { std::mem::zeroed() }; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fstatvfs(file.as_raw_fd(), &mut stat) } < 0 { + return (stat, errno::errno().0); + } + + (stat, FUSE_OK) +} + +/// Synchronize the data of file to storage device. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `datasync` - The datasync indicates whether to use the fdatasync +/// or fsync interface. +pub fn fsync(file: &File, datasync: bool) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = if datasync { + unsafe { libc::fdatasync(file.as_raw_fd()) } + } else { + unsafe { libc::fsync(file.as_raw_fd()) } + }; + + if ret != FUSE_OK { + return errno::errno().0; + } + + FUSE_OK +} + +/// Change current working directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open directory descriptor. +pub fn fchdir(file: &File) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::fchdir(file.as_raw_fd()) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Set an extended attribute value. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `name` - The name of extended attribute. +/// * `value` - The value of extended attribute. +/// * `size` - The size of the string of value. +/// * `flags` - The flags indicates the attribute will be set. +pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::setxattr( + path.as_ptr(), + name.as_ptr(), + value.as_ptr() as *const libc::c_void, + size as usize, + flags as i32, + ) + }; + + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Set an extended attribute value by the open file file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `name` - The name of extended attribute. +/// * `value` - The value of extended attribute. +/// * `size` - The size of the string of value. +/// * `flags` - The flags indicates the attribute will be set. +pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::fsetxattr( + file.as_raw_fd(), + name.as_ptr(), + value.as_ptr() as *const libc::c_void, + size as usize, + flags as i32, + ) + }; + + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Get an extended attribute value. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `name` - The name of extended attribute. +/// * `size` - The size of the extended attribute value that needs to be get. +pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::getxattr( + path.as_ptr(), + name.as_ptr(), + buf.as_mut_ptr() as *mut libc::c_void, + size, + ) + }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +/// Get an extended attribute value by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `name` - The name of extended attribute. +/// * `size` - The size of the extended attribute value that needs to be get. +pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::fgetxattr( + file.as_raw_fd(), + name.as_ptr(), + buf.as_mut_ptr() as *mut libc::c_void, + size, + ) + }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +/// List extended attribute names. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `size` - The size of the extended attribute names that needs to be get. +pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = + unsafe { libc::listxattr(path.as_ptr(), buf.as_mut_ptr() as *mut libc::c_char, size) }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +/// List extended attribute names by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `size` - The size of the extended attribute names that needs to be get. +pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::flistxattr( + file.as_raw_fd(), + buf.as_mut_ptr() as *mut libc::c_char, + size, + ) + }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +/// Remove an extended attribute value. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `name` - The name of the extended attribute value. +pub fn remove_xattr(path: CString, name: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Remove an extended attribute value by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `name` - The name of the extended attribute value. +pub fn fremove_xattr(file: &File, name: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::fremovexattr(file.as_raw_fd(), name.as_ptr()) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Set mask for file mode creation. +/// +/// # Arguments +/// +/// * `umask` - The umask indicates the permissions in the umask are turned off. +pub fn umask(umask: u32) { + unsafe { libc::umask(umask) }; +} + +/// Open a directory stream by the file descriptor. +/// +/// # Arguments +/// +/// * `fd` - The open file descriptor used to open a directory stream. +pub fn fdopen_dir(fd: RawFd) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + + let dirp = unsafe { libc::fdopendir(fd) }; + if errno::errno().0 != 0 { + return (None, errno::errno().0); + } + + (Some(dirp), FUSE_OK) +} + +/// Set the position of the next readdir() in the directory stream. +/// +/// # Arguments +/// +/// * `dirp` - The pointer to open a directory. +/// * `offset` - The position of the next readdir(). +pub fn seek_dir(dirp: &mut DirPtr, offset: u64) { + unsafe { + libc::seekdir(*dirp, offset as i64); + }; +} + +/// Read a directory entry in the directory stream. +/// +/// # Arguments +/// +/// * `dirp` - The pointer to open a directory. +pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + + let direntp = unsafe { libc::readdir(*dirp) }; + if errno::errno().0 != 0 { + return (None, errno::errno().0); + } + + (Some(direntp), FUSE_OK) +} + +/// Lock the file or unlock the file by BSD lock by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `operation` - The operation of lock type. +pub fn flock(file: &File, operation: i32) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +/// Lock the file or unlock the file by POSIX lock by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `cmd` - The command for creating, lock and unlock in POSIX lock. +/// * `file_lock` - The information of file lock will be set. +/// * `file_lock_out` - The information of file lock will be returned. +pub fn fcntl_flock( + file: &File, + cmd: i32, + file_lock: &FuseFileLock, + file_lock_out: &mut FuseFileLock, +) -> i32 { + let mut flock: libc::flock = unsafe { std::mem::zeroed() }; + + flock.l_type = file_lock.lock_type as i16; + flock.l_whence = libc::SEEK_SET as i16; + flock.l_start = file_lock.start as i64; + flock.l_pid = file_lock.pid as i32; + + if file_lock.end == OFFSET_MAX { + flock.l_len = 0; + } else { + flock.l_len = file_lock.end as i64 - file_lock.start as i64 + 1; + } + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &mut flock) }; + + if ret == -1 { + return errno::errno().0; + } + + file_lock_out.lock_type = flock.l_type as u32; + if flock.l_type != libc::F_ULOCK as i16 { + file_lock_out.start = flock.l_start as u64; + if flock.l_len == 0 { + file_lock_out.end = OFFSET_MAX; + } else { + file_lock_out.end = (flock.l_start + flock.l_len - 1) as u64; + } + } + + file_lock_out.pid = flock.l_pid as u32; + + FUSE_OK +} + +/// Allocate the disk space with the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `mode` - The mode determines the operation to be performed on the given range. +/// * `offset` - The offset in the file. +/// * `length` - The length that needs to be allocated. +pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = + unsafe { libc::fallocate(file.as_raw_fd(), mode as i32, offset as i64, length as i64) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +/// Reposition the file offset of the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `offset` - The offset in the file used together with the whence. +/// * `whence` - The length that needs to be allocated. +pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::lseek(file.as_raw_fd(), offset as i64, whence as i32) }; + + if ret == -1 { + return (0, errno::errno().0 as i32); + } + + (ret as u64, FUSE_OK) +} + +/// Set file resource limits. +/// +/// # Arguments +/// +/// * `rlim_cur` - The soft limit of the file resource. +/// * `rlim_max` - The hard limit of the file resource. +pub fn set_rlimit(rlim_cur: u64, rlim_max: u64) -> i32 { + let limit = libc::rlimit { rlim_cur, rlim_max }; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &limit) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} -- Gitee From aee9f7ec74976fb5762331907a452ac9b08a322b Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:41:49 +0800 Subject: [PATCH 0184/2187] virtiofs: create filesystem management create filesystem managenent and its relative functions Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/fs.rs | 1962 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1962 insertions(+) create mode 100644 vhost_user_fs/src/fs.rs diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs new file mode 100644 index 000000000..ca9c589b1 --- /dev/null +++ b/vhost_user_fs/src/fs.rs @@ -0,0 +1,1962 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// The map length used to extend the file/inode map. +const MAP_EXTEND_LENGTH: usize = 256; +const F_RDLCK: u32 = 0; +const F_WDLCK: u32 = 1; +const F_UNLCK: u32 = 2; +const RLIMIT_NOFILE_MIN: u64 = 20; +/// The inode 0 is reserved, 1 is root inode. +const ROOT_INODE: usize = 1; + +use super::fs_ops::*; +use super::fuse_msg::*; +use crate::errors::{Result, ResultExt}; +use std::collections::{BTreeMap, HashMap}; +use std::ffi::CString; +use std::fs::{read_to_string, File}; +use std::mem; +use std::os::unix::io::{AsRawFd, RawFd}; +use util::byte_code::ByteCode; +use util::num_ops::round_up; + +/// Used as the key of the inode. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] +struct StatKey { + /// Inode number. + ino: libc::ino64_t, + /// ID of device containing file. + dev: libc::dev_t, +} + +/// The entry of the inode/file. +struct Entry { + /// The value stored in the Entry. + value: Option, + /// If the entry is used or not. + used: bool, + /// The next free entry. + free_next: usize, +} + +/// The map used to store inodes/files. +struct Map { + /// The vector used to store Entry. + list: Vec>, + /// The first free entry in list. + free_head: usize, +} + +impl Map { + fn new() -> Self { + Map { + list: Vec::new(), + free_head: ROOT_INODE, + } + } + + fn destroy_map(&mut self) { + self.list = Vec::new(); + self.free_head = ROOT_INODE; + } + + /// Add MAP_EXTEND_LENGTH elems to the map. + fn extend_map(&mut self) { + let mut next = self.list.len(); + + for _ in 0..MAP_EXTEND_LENGTH { + next += 1; + self.list.push(Entry { + value: None, + used: false, + free_next: next, + }); + } + } + + /// Add entry to the map. + fn get_map(&mut self, value: T) -> usize { + let id = self.free_head; + if id == ROOT_INODE || id == self.list.len() { + self.extend_map(); + } + + match self.list.get_mut(id) { + Some(e) => { + e.value = Some(value); + e.used = true; + self.free_head = e.free_next; + + id + } + None => 0, + } + } + + /// Delete entry from the map. + fn put_map(&mut self, id: usize) { + if id >= self.list.len() { + return; + } + + if let Some(e) = self.list.get_mut(id) { + if !e.used { + return; + } + + e.value = None; + e.used = false; + e.free_next = self.free_head; + self.free_head = id + } + } + + fn get_value(&self, id: usize) -> Option<&T> { + if let Some(e) = self.list.get(id) { + e.value.as_ref().map(|v| v) + } else { + None + } + } + + fn get_value_mut(&mut self, id: usize) -> Option<&mut T> { + if let Some(e) = self.list.get_mut(id) { + e.value.as_mut().map(|v| v) + } else { + None + } + } +} + +/// Used to lock file. +struct FileLock { + /// The owner of the lock + lock_owner: u64, + /// The file which is locked + file: File, +} + +impl FileLock { + fn new(file: File, lock_owner: u64) -> Self { + FileLock { lock_owner, file } + } +} + +impl Clone for FileLock { + fn clone(&self) -> Self { + FileLock { + lock_owner: self.lock_owner, + file: self.file.try_clone().unwrap(), + } + } +} + +/// The inode info. +struct Inode { + /// The inode file. + file: File, + /// The refcount of the inode. + nlookup: u64, + /// Store the map index of the inode. + node_id: usize, + /// the file type. + file_type: u32, + /// The key of the inode. + key: StatKey, + /// The locks on the file of the Inode. + locks: HashMap, +} + +impl Inode { + fn new(file: File, nlookup: u64, node_id: usize, file_type: u32, key: StatKey) -> Self { + Inode { + file, + nlookup, + node_id, + file_type, + key, + locks: HashMap::new(), + } + } + + fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() + } +} + +impl Clone for Inode { + fn clone(&self) -> Self { + Inode { + file: self.file.try_clone().unwrap(), + nlookup: self.nlookup, + node_id: self.node_id, + file_type: self.file_type, + key: self.key, + locks: self.locks.clone(), + } + } +} + +fn array_to_cstring( + #[cfg(target_arch = "x86_64")] array: &[i8], + #[cfg(target_arch = "aarch64")] array: &[u8], +) -> Result<(usize, CString)> { + let mut vec = Vec::new(); + for item in array { + if *item == 0 { + break; + } + vec.push(*item as u8); + } + + let len = vec.len(); + if len == 0 { + bail!("convert array to CString failed") + } + + let cstring = match CString::new(vec) { + Ok(c) => c, + Err(_) => bail!("convert array to CString failed"), + }; + + Ok((len, cstring)) +} + +fn path_is_dot(path: &CString) -> bool { + let bytes = path.as_bytes(); + if bytes.len() == 1 && bytes[0] == b'.' { + return true; + } + + false +} + +fn path_is_dotdot(path: &CString) -> bool { + let bytes = path.as_bytes(); + if bytes.len() == 2 && bytes[0] == b'.' && bytes[1] == b'.' { + return true; + } + + false +} + +/// Set file resources limits for the process. The limit value +/// must be more than or equal to 20 to ensure that the process +/// can start normally. The limit value must be less than or equal +/// to the value of "/proc/sys/fs/file-max" and "/proc/sys/fs/nr_open". +/// +/// # Arguments +/// +/// * `limit` - The limit value which needs to be set. +pub fn set_rlimit_nofile(limit: u64) -> Result<()> { + if limit < RLIMIT_NOFILE_MIN { + bail!( + "The limit {} exceeds minimum of files {}", + limit, + RLIMIT_NOFILE_MIN + ); + } + + let max_file_str = + read_to_string("/proc/sys/fs/file-max").chain_err(|| "Failed to read file-max")?; + let max_file = max_file_str + .trim() + .parse::() + .chain_err(|| "Failed to convert the string of max files")?; + if limit > max_file { + bail!("The limit {} exceeds maximum of files {}", limit, max_file); + } + + let nr_open_str = + read_to_string("/proc/sys/fs/nr_open").chain_err(|| "Failed to read nr_open")?; + let max_file = nr_open_str + .trim() + .parse::() + .chain_err(|| "Failed to convert the string of nr_open")?; + if limit > max_file { + bail!( + "The limit {} exceeds maximum of nr_open {}", + limit, + max_file + ); + } + + let ret = set_rlimit(limit, limit); + if ret != FUSE_OK { + bail!("Failed to set rlimit, err: {}", ret); + } + + Ok(()) +} + +/// The management structure of filesystem that contains the management of inodes +/// and the information of files in host directory which needs to be shared. +pub struct FileSystem { + root_inode: Inode, + inodes: BTreeMap, + inode_key_map: Map, + file_map: Map, + proc_dir: File, +} + +impl FileSystem { + /// Create a filesystem management structure. + /// + /// # Arguments + /// + /// * `source_dir` - The path of the host directory which needs to be shared. + pub fn new(source_dir: &str) -> Result { + let (root_file_opt, ret) = open(CString::new(source_dir).unwrap(), libc::O_PATH); + if ret != FUSE_OK { + bail!("Failed to open root file {}", source_dir); + } + let root_file = root_file_opt.unwrap(); + let (stat, ret) = fstat_at( + &root_file, + CString::new("").unwrap(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + bail!("Failed to get stat of root file {}", source_dir); + } + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + let mut inode_key_map = Map::new(); + let root_id = inode_key_map.get_map(key); + // The default folder nlookup is 2 + let root_inode = Inode::new(root_file, 2, root_id, libc::S_IFDIR, key); + let mut inodes = BTreeMap::new(); + inodes.insert(key, root_inode.clone()); + let (proc_dir_opt, ret) = open(CString::new("/proc/self/fd").unwrap(), libc::O_PATH); + if ret != FUSE_OK { + bail!("Failed to open proc dir"); + } + Ok(FileSystem { + root_inode, + inodes, + inode_key_map, + file_map: Map::new(), + proc_dir: proc_dir_opt.unwrap(), + }) + } + + fn find_inode(&self, node_id: usize) -> Option<&Inode> { + match self.inode_key_map.get_value(node_id) { + Some(k) => self.inodes.get(k), + _ => None, + } + } + + fn find_mut_inode(&mut self, node_id: usize) -> Option<&mut Inode> { + match self.inode_key_map.get_value(node_id) { + Some(k) => self.inodes.get_mut(k), + _ => None, + } + } + + fn unref_inode(&mut self, inode: &mut Inode, count: u64) { + if count > inode.nlookup { + inode.nlookup = 0; + } else { + inode.nlookup -= count; + } + + if inode.nlookup == 0 { + self.inodes.remove(&inode.key); + self.inode_key_map.put_map(inode.node_id); + } else if let Some(inode_) = self.find_mut_inode(inode.node_id) { + inode_.nlookup = inode.nlookup; + } + } + + fn create_file_lock(&mut self, node_id: usize, owner: u64) -> (Option, i32) { + let proc_file = self.proc_dir.try_clone().unwrap(); + let inode = match self.find_mut_inode(node_id) { + Some(inode_) => inode_, + None => return (None, libc::EBADF as i32), + }; + + if let Some(lock) = inode.locks.get_mut(&owner) { + return (Some(lock.file.try_clone().unwrap()), FUSE_OK); + } + + if inode.file_type & libc::S_IFDIR == 0 && inode.file_type & libc::S_IFREG == 0 { + return (None, libc::EBADF as i32); + } + + let (file_opt, ret) = open_at( + &proc_file, + CString::new(format!("{}", inode.as_raw_fd())).unwrap(), + libc::O_RDWR, + 0, + ); + + if ret != FUSE_OK { + return (None, ret); + } + + let file = file_opt.unwrap().try_clone().unwrap(); + let file_lock = FileLock::new(file.try_clone().unwrap(), owner); + inode.locks.insert(owner, file_lock); + + (Some(file), FUSE_OK) + } + + fn delete_file_lock(&mut self, node_id: usize, owner: u64) -> i32 { + let inode = match self.find_mut_inode(node_id) { + Some(inode_) => inode_, + None => return libc::EBADF as i32, + }; + + inode.locks.remove(&owner); + + FUSE_OK + } + + fn internal_lookup( + &mut self, + parent_inode: &Inode, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let mut son_name = name.clone(); + if parent_inode.node_id == self.root_inode.node_id && path_is_dotdot(&name) { + son_name = CString::new(".").unwrap(); + } + + let (stat, ret) = fstat_at( + &parent_inode.file, + son_name.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + if let Some(inode) = self.inodes.get_mut(&key) { + inode.nlookup += 1; + *node_id = inode.node_id as u64; + } else { + let (file_opt, ret) = open_at( + &parent_inode.file, + son_name, + libc::O_PATH | libc::O_NOFOLLOW, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + let map_id = self.inode_key_map.get_map(key); + if let Some(file) = file_opt { + self.inodes.insert( + key, + Inode::new(file, 1, map_id, stat.st_mode & libc::S_IFMT, key), + ); + } + *node_id = map_id as u64; + }; + + *fuse_attr = FuseAttr::from_stat(stat); + + FUSE_OK + } + + /// Look up the directory or file information by name and reply attributes. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up. + /// * `name` - The name that needs to be looked up. + /// * `node_id` - The node id that needs to be looked up by name. + /// * `fuse_attr` - The attributes that needs to be looked up by name. + pub fn lookup( + &mut self, + parent_nodeid: usize, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + self.internal_lookup(&inode, name, node_id, fuse_attr) + } + + /// When the nlookup of inode is reduced to 0, delete the inode from the management structure. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `nlookup` - The number of nlookup for inode needs to be reduced. + pub fn forget(&mut self, node_id: usize, nlookup: u64) -> i32 { + let mut inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + self.unref_inode(&mut inode, nlookup); + FUSE_OK + } + + /// Get the attributes of a file or directory. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn getattr(&mut self, node_id: usize, fuse_attr: &mut FuseAttr) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + let (stat, ret) = fstat_at( + &inode.file, + CString::new("").unwrap(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + *fuse_attr = FuseAttr::from_stat(stat); + FUSE_OK + } + + /// Set the attributes of a file or directory. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `attr` - The attributes will be set to the found inode. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn setattr( + &mut self, + node_id: usize, + attr: &FuseSetattrIn, + fuse_attr: &mut FuseAttr, + ) -> i32 { + if attr.valid & FUSE_SET_ATTR_MODE != 0 { + if attr.valid & FATTR_FH != 0 { + match self.file_map.get_value(attr.fh as usize) { + Some(file) => { + let ret = fchmod(&file, attr.mode); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } else { + match self.find_inode(node_id) { + Some(i) => { + let ret = fchmod_at( + &self.proc_dir, + CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), + attr.mode, + ); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + } + + if attr.valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID) != 0 { + let uid = if attr.valid & FUSE_SET_ATTR_UID != 0 { + attr.uid + } else { + u32::MAX + }; + + let gid = if attr.valid & FUSE_SET_ATTR_GID != 0 { + attr.gid + } else { + u32::MAX + }; + + match self.find_inode(node_id) { + Some(i) => { + let ret = fchown_at( + &i.file, + CString::new("").unwrap(), + uid, + gid, + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + + if attr.valid & FUSE_SET_ATTR_SIZE != 0 { + if attr.valid & FATTR_FH != 0 { + match self.file_map.get_value(attr.fh as usize) { + Some(file) => { + let ret = ftruncate(&file, attr.size); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } else { + match self.find_inode(node_id) { + Some(i) => { + if i.file_type & libc::S_IFREG == 0 && i.file_type & libc::S_IFDIR == 0 { + return libc::EBADF as i32; + } + + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), + libc::O_RDWR, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let ret = ftruncate(&file, attr.size); + if ret != FUSE_OK { + return ret; + } + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + } + + if attr.valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME) != 0 { + let (a_sec, a_nsec) = if attr.valid & FUSE_SET_ATTR_ATIME_NOW != 0 { + (0, libc::UTIME_NOW) + } else if attr.valid & FUSE_SET_ATTR_ATIME != 0 { + (attr.atime, attr.atimensec as i64) + } else { + (0, libc::UTIME_OMIT) + }; + + let (m_sec, m_nsec) = if attr.valid & FUSE_SET_ATTR_MTIME_NOW != 0 { + (0, libc::UTIME_NOW) + } else if attr.valid & FUSE_SET_ATTR_MTIME != 0 { + (attr.mtime, attr.mtimensec as i64) + } else { + (0, libc::UTIME_OMIT) + }; + + if attr.valid & FATTR_FH != 0 { + match self.file_map.get_value(attr.fh as usize) { + Some(file) => { + let ret = futimens(&file, a_sec, a_nsec, m_sec, m_nsec); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } else { + match self.find_inode(node_id) { + Some(i) => { + let ret = utimensat( + &self.proc_dir, + CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), + a_sec, + a_nsec, + m_sec, + m_nsec, + 0, + ); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + } + + self.getattr(node_id, fuse_attr) + } + + /// Get the contexts of the symbolic link into the buffer. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `buff` - The buffer is saved by the contexts of the symbolic link. + pub fn readlink(&self, node_id: usize, buff: &mut Vec) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (buf_opt, ret) = readlinkat(&inode.file, CString::new("").unwrap()); + if ret != FUSE_OK { + return ret; + } + + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } + + /// Get the contexts of the symbolic link into the buffer. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `name` - The target link name used to create a symbolic link. + /// * `link_name` - The link name that will be created a symbolic link. + /// * `node_id` - The node id that is found by name. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn symlink( + &mut self, + in_header: &FuseInHeader, + name: CString, + link_name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_inode = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let ret = symlinkat(&parent_inode.file, name.clone(), link_name); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + self.internal_lookup(&parent_inode, name, node_id, fuse_attr) + } + + /// Create a file system node(file, device special file or named pipe) by the path name + /// with the mode and dev in the mknod information. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `mknod_in` - The information of mknod to get the permissions and dev. + /// * `name` - The path name used to create a file system node. + /// * `node_id` - The node id that is found by name. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn mknod( + &mut self, + in_header: &FuseInHeader, + mknod_in: &FuseMknodIn, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_inode = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let ret = mknodat( + &parent_inode.file, + name.clone(), + mknod_in.mode & !mknod_in.umask, + mknod_in.rdev, + ); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + self.internal_lookup(&parent_inode, name, node_id, fuse_attr) + } + + /// Create a directory by the name with the permissions in the mkdir information. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `mkdir_in` - The information of mkdir used to get permissions. + /// * `name` - The path name that will be created a directory. + /// * `node_id` - The node id that is found by the path name. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn mkdir( + &mut self, + in_header: &FuseInHeader, + mkdir_in: &FuseMkdirIn, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_dir = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let ret = mkdir_at( + &parent_dir.file, + name.clone(), + mkdir_in.mode & !mkdir_in.umask, + ); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + self.internal_lookup(&parent_dir, name, node_id, fuse_attr) + } + + /// Delete a name from the host filesystem. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up + /// in the management of filesystem. + /// * `name` - The name will be deleted. + pub fn unlink(&mut self, parent_nodeid: usize, name: CString) -> i32 { + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => return libc::EBADF as i32, + }; + + let (stat, ret) = fstat_at( + &parent_inode.file, + name.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + + match self.inodes.get(&key) { + Some(i) => i.clone(), + None => return libc::EIO as i32, + }; + + let ret = unlinkat(&parent_inode.file, name, 0); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + /// Delete a directory from the host filesystem by the path name. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up + /// in the management of filesystem. + /// * `name` - The path name of the directory will be deleted. + pub fn rmdir(&mut self, parent_nodeid: usize, name: CString) -> i32 { + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => return libc::EBADF as i32, + }; + + let (stat, ret) = fstat_at( + &parent_inode.file, + name.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + + match self.inodes.get(&key) { + Some(i) => i.clone(), + None => return libc::EIO as i32, + }; + + let ret = unlinkat(&parent_inode.file, name, libc::AT_REMOVEDIR); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + /// Rename the old path name to the new path name in the host filesystem.. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up + /// for old path name in the management of filesystem. + /// * `oldname` - The old path name that is relative to the directory of parent node. + /// * `newparent_nodeid` - The new parent node id that is the starting directory to + /// look up for new path name in the management of filesystem. + /// * `newname` - The new path name that is relative to the directory of new parent node. + pub fn rename( + &self, + parent_nodeid: usize, + oldname: CString, + newparent_nodeid: usize, + newname: CString, + ) -> i32 { + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let newparent_inode = match self.find_inode(newparent_nodeid) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (stat, ret) = fstat_at( + &parent_inode.file, + oldname.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + + match self.inodes.get(&key) { + Some(_) => {} + None => return libc::EIO as i32, + }; + + rename(&parent_inode.file, oldname, &newparent_inode.file, newname) + } + + /// Create a new link to an existing file for the host filesystem. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up. + /// * `old_nodeid` - The old node id in the management of filesystem. + /// * `name` - The path name that is relative to the directory of parent node. + /// * `node_id` - The node id that is found by the path name in the management of filesystem. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn link( + &mut self, + parent_nodeid: usize, + old_nodeid: usize, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let proc_file = self.proc_dir.try_clone().unwrap(); + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => return libc::EBADF as i32, + }; + + let inode = match self.find_mut_inode(old_nodeid) { + Some(inode_) => inode_, + None => return libc::EBADF as i32, + }; + + let ret = linkat( + &proc_file, + CString::new(format!("{}", inode.as_raw_fd())).unwrap(), + &parent_inode.file, + name, + libc::AT_SYMLINK_FOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let (stat, ret) = fstat_at( + &inode.file, + CString::new("").unwrap(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + + if ret != FUSE_OK { + return ret; + } + + *fuse_attr = FuseAttr::from_stat(stat); + *node_id = inode.node_id as u64; + inode.nlookup += 1; + + FUSE_OK + } + + /// Open the file with the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `flags` - The flags used to open the file. + /// * `fh` - The file handler is returned in the management of filesystem. + pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 { + let (inode_fd, file_type) = match self.find_inode(node_id) { + Some(i) => (i.as_raw_fd(), i.file_type), + None => { + return libc::EBADF as i32; + } + }; + + if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { + return libc::EBADF as i32; + } + + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", inode_fd)).unwrap(), + (flags as i32) & !libc::O_NOFOLLOW, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64; + } + + FUSE_OK + } + + /// Read the file descriptor by file hander in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `fd` - The file descriptor in the host filesystem. + pub fn read(&mut self, fh: usize, fd: &mut RawFd) -> i32 { + match self.file_map.get_value(fh) { + Some(file) => { + *fd = file.as_raw_fd(); + } + _ => { + return libc::EBADF as i32; + } + } + + FUSE_OK + } + + /// write the file descriptor by file hander in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `fd` - The file descriptor in the host filesystem. + pub fn write(&mut self, fh: usize, fd: &mut RawFd) -> i32 { + match self.file_map.get_value(fh) { + Some(file) => { + *fd = file.as_raw_fd(); + } + _ => { + return libc::EBADF as i32; + } + } + + FUSE_OK + } + + /// Get the information about a mounted filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `fuse_statfs` - The information about the mounted filesystem is + /// returned by the found inode. + pub fn statfs(&mut self, node_id: usize, fuse_statfs: &mut FuseStatfsOut) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (stat, ret) = fstat_vfs(&inode.file); + if ret != FUSE_OK { + return ret; + } + + *fuse_statfs = FuseStatfsOut::from_stat(stat); + + FUSE_OK + } + + /// Release the file with file handler in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + pub fn release(&mut self, fh: usize) -> i32 { + self.file_map.put_map(fh); + + FUSE_OK + } + + /// Transfer the file data to the storage device with file handler in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `datasync` - The datasync indicates whether to use the fdatasync + /// or fsync interface. + pub fn fsyncfile(&self, fh: usize, datasync: bool) -> i32 { + let mut ret = FUSE_OK; + + if fh == u64::max_value() as usize { + let (inode_fd, file_type) = match self.find_inode(fh) { + Some(i) => (i.as_raw_fd(), i.file_type), + None => { + return libc::EBADF as i32; + } + }; + + if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { + return libc::EBADF as i32; + } + + let (file_opt, ret_) = open_at( + &self.proc_dir, + CString::new(format!("{}", inode_fd)).unwrap(), + libc::O_RDWR, + 0, + ); + if ret_ != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + ret = fsync(&file, datasync); + } else { + return libc::EBADF as i32; + } + } else { + match self.file_map.get_value(fh) { + Some(file) => { + ret = fsync(file, datasync); + } + _ => { + return libc::EBADF as i32; + } + } + } + + ret + } + + /// Set an extended attribute identified by name and associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `name` - The name associated with inode. + /// * `value` - The value of the extended attribute. + /// * `size` - The size of the value string. + /// * `flags` - The flags used to set an extended attribute. + pub fn setxattr( + &self, + node_id: usize, + name: CString, + value: CString, + size: u32, + flags: u32, + ) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret_) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret_ != FUSE_OK { + return ret_; + } + + if let Some(file) = file_opt { + fset_xattr(&file, name, value, size, flags) + } else { + libc::EBADF as i32 + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("setxattr: failed to change process directoy"); + } + + let ret_ = set_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + name, + value, + size, + flags, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("setxattr: failed to change directoy of root inode"); + } + + ret_ + } + } + + /// Get an extended attribute identified by name and associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `name` - The name associated with inode. + /// * `size` - The size of the buffer. + /// * `buff` - The buffer of the extended attribute. + pub fn getxattr(&self, node_id: usize, name: CString, size: u32, buff: &mut Vec) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let (buf_opt, ret) = fget_xattr(&file, name, size as usize); + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } else { + return libc::EBADF as i32; + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("getxattr: failed to change process directoy"); + } + + let (buf_opt, ret) = get_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + name, + size as usize, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("getxattr: failed to change directoy of root inode"); + } + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } + + FUSE_OK + } + + /// List extended attribute names associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `size` - The size of the buffer. + /// * `buff` - The buffer of the extended attribute. + pub fn listxattr(&self, node_id: usize, size: u32, buff: &mut Vec) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let (buf_opt, ret) = flist_xattr(&file, size as usize); + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } else { + return libc::EBADF as i32; + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("listxattr: failed to change process directoy"); + } + + let (buf_opt, ret) = list_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + size as usize, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("listxattr: failed to change directoy of root inode"); + } + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } + + FUSE_OK + } + + /// Remove an extended attribute identified by name and associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `name` - The name associated with inode. + pub fn removexattr(&self, node_id: usize, name: CString) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let ret = fremove_xattr(&file, name); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("removexattr: failed to change process directoy"); + } + + let ret = remove_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + name, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("removexattr: failed to change directoy of root inode"); + } + if ret != FUSE_OK { + return ret; + } + } + + FUSE_OK + } + + /// Delete the file lock by the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `owner` - The name associated with inode. + pub fn flush(&mut self, node_id: usize, owner: u64) -> i32 { + self.delete_file_lock(node_id, owner) + } + + /// Initialize fuse message for getting supported features in the process. + /// + /// # Arguments + /// + /// * `flags` - The supported features in StratoVirt. + /// * `support_flags` - The supported features in the process. + pub fn init(&self, flags: u32, support_flags: &mut u32) { + if flags & FUSE_MAX_PAGES != 0 { + *support_flags |= FUSE_MAX_PAGES; + } + if flags & FUSE_CAP_ASYNC_READ != 0 { + *support_flags |= FUSE_ASYNC_READ; + } + if flags & FUSE_CAP_PARALLEL_DIROPS != 0 { + *support_flags |= FUSE_PARALLEL_DIROPS; + } + if flags & FUSE_CAP_POSIX_LOCKS != 0 { + *support_flags |= FUSE_POSIX_LOCKS; + } + if flags & FUSE_CAP_ATOMIC_O_TRUNC != 0 { + *support_flags |= FUSE_ATOMIC_O_TRUNC; + } + if flags & FUSE_CAP_EXPORT_SUPPORT != 0 { + *support_flags |= FUSE_EXPORT_SUPPORT; + } + if flags & FUSE_CAP_DONT_MASK != 0 { + *support_flags |= FUSE_DONT_MASK; + } + if flags & FUSE_CAP_FLOCK_LOCKS != 0 { + *support_flags |= FUSE_FLOCK_LOCKS; + } + if flags & FUSE_CAP_AUTO_INVAL_DATA != 0 { + *support_flags |= FUSE_AUTO_INVAL_DATA; + } + if flags & FUSE_CAP_READDIRPLUS != 0 { + *support_flags |= FUSE_DO_READDIRPLUS; + } + if flags & FUSE_CAP_READDIRPLUS_AUTO != 0 { + *support_flags |= FUSE_READDIRPLUS_AUTO; + } + if flags & FUSE_CAP_ASYNC_DIO != 0 { + *support_flags |= FUSE_ASYNC_DIO; + } + + if flags & FUSE_CAP_POSIX_ACL != 0 { + *support_flags |= FUSE_POSIX_ACL; + } + + umask(0o000); + } + + /// Open a directory with the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `dir_fh` - The directory handler is returned in the management of filesystem. + pub fn opendir(&mut self, node_id: usize, dir_fh: &mut u64) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (file_opt, ret) = open_at(&inode.file, CString::new(".").unwrap(), libc::O_RDONLY, 0); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + *dir_fh = self.file_map.get_map(file) as u64; + return FUSE_OK; + } + + libc::EBADF as i32 + } + + /// read a directory stream with the directory handler in the host filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `dh` - The directory handler in the management of filesystem. + /// * `size` - The size of the buffer. + /// * `offset` - The offset indicates it opens a directory stream with the offset. + /// * `plus` - The plus indicates it uses FuseDirentplus struct to the buffer. + /// * `buff` - The buffer of all FuseDirent structs or FuseDirentplus structs. + pub fn readdir( + &mut self, + node_id: usize, + dh: usize, + size: u32, + offset: u64, + plus: bool, + buff: &mut Vec, + ) -> i32 { + let dir_inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let mut dirp = match self.file_map.get_value_mut(dh) { + Some(file) => { + let (dirp_opt, ret) = fdopen_dir(file.as_raw_fd()); + if ret != FUSE_OK { + return libc::EBADF as i32; + } + dirp_opt.unwrap() + } + _ => { + return libc::EBADF as i32; + } + }; + + seek_dir(&mut dirp, offset); + + let mut remain = size; + let mut son_nodeid = 0_u64; + loop { + let (dirent_opt, ret) = read_dir(&mut dirp); + if ret != FUSE_OK { + return ret; + } + let direntp = dirent_opt.unwrap(); + if direntp.is_null() { + break; + } + + // The above code has checked the validity of direntp, so it is safe for *direntp. + let dirent = unsafe { *direntp }; + + let (name_len, son_name) = match array_to_cstring(&dirent.d_name[..]) { + Ok(v) => v, + Err(_) => { + continue; + } + }; + + let only_entry_size = if plus { + mem::size_of::() + } else { + mem::size_of::() + }; + + let (entry_size, gap) = match round_up((only_entry_size + name_len) as u64, 8) { + Some(v) => (v as u32, v as usize - (only_entry_size + name_len)), + _ => { + return libc::EINVAL as i32; + } + }; + if entry_size > remain { + if son_nodeid != 0 { + self.forget(son_nodeid as usize, 1); + } + break; + } + + let mut fuse_dirent = FuseDirent { + ino: dirent.d_ino, + off: dirent.d_off as u64, + namelen: name_len as u32, + type_: dirent.d_type as u32 & (libc::S_IFMT >> 12), + name: [0u8; 0], + }; + + if dir_inode.node_id == self.root_inode.node_id && path_is_dotdot(&son_name) { + fuse_dirent.ino = self.root_inode.key.ino; + fuse_dirent.type_ = libc::DT_DIR as u32 & (libc::S_IFMT >> 12); + } + + if plus { + son_nodeid = 0; + let mut son_attr = FuseAttr::default(); + if !path_is_dot(&son_name) && !path_is_dotdot(&son_name) { + let ret = self.internal_lookup( + &dir_inode, + son_name.clone(), + &mut son_nodeid, + &mut son_attr, + ); + if ret != FUSE_OK { + return ret; + } + } + + buff.extend_from_slice( + FuseDirentplus { + entry_out: FuseEntryOut { + nodeid: son_nodeid as u64, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: son_attr, + }, + dirent: fuse_dirent, + } + .as_bytes(), + ); + } else { + buff.extend_from_slice(fuse_dirent.as_bytes()); + }; + + buff.extend_from_slice(son_name.as_bytes()); + if gap > 0 { + buff.append(&mut vec![0u8; gap]); + } + + remain -= entry_size; + } + + FUSE_OK + } + + /// Release a directory in the management of filesystem. + /// + /// # Arguments + /// + /// * `dir_fh` - The directory handler in the management of filesystem. + pub fn releasedir(&mut self, dir_fh: usize) -> i32 { + self.file_map.put_map(dir_fh); + + FUSE_OK + } + + /// Transfer the directory data to the storage device with directory handler in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `dir_fh` - The directory handler in the management of filesystem. + /// * `datasync` - The datasync indicates whether to use the fdatasync + /// or fsync interface. + pub fn fsyncdir(&self, dir_fh: usize, datasync: bool) -> i32 { + if let Some(file) = self.file_map.get_value(dir_fh) { + let ret = fsync(file, datasync); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } + + /// Create the POSIX file lock with the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `owner` - The unique index for file lock in the inode. + /// * `file_lock_in` - The information of file lock will be set. + /// * `file_lock_out` - The information of file lock will be returned. + pub fn getlk( + &mut self, + node_id: usize, + owner: u64, + file_lock_in: &FuseFileLock, + file_lock_out: &mut FuseFileLock, + ) -> i32 { + let (file_opt, ret) = self.create_file_lock(node_id, owner); + if ret != FUSE_OK { + return ret; + } + + let ret = fcntl_flock( + &file_opt.unwrap(), + libc::F_GETLK, + file_lock_in, + file_lock_out, + ); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + /// Lock the file or unlock the file by POSIX lock with the node id in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `owner` - The unique index for file lock in the inode. + /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock. + /// * `file_lock_in` - The information of file lock will be set. + pub fn setlk( + &mut self, + node_id: usize, + owner: u64, + is_blocking: bool, + file_lock_in: &FuseFileLock, + ) -> i32 { + if is_blocking { + return libc::EOPNOTSUPP as i32; + } + + let (file_opt, ret) = self.create_file_lock(node_id, owner); + if ret != FUSE_OK { + return ret; + } + + let mut file_lock_out = FuseFileLock::default(); + let ret = fcntl_flock( + &file_opt.unwrap(), + libc::F_SETLK, + file_lock_in, + &mut file_lock_out, + ); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + /// Lock the file or unlock the file by BSD lock with file handler in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `lock_type` - The lock type contains the type of read lock, write lock and unlocking. + /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock. + pub fn flock(&self, fh: usize, lock_type: u32, is_blocking: bool) -> i32 { + let mut operation: i32 = 0; + + if lock_type == F_RDLCK { + operation = libc::LOCK_SH; + } else if lock_type == F_WDLCK { + operation = libc::LOCK_EX; + } else if lock_type == F_UNLCK { + operation = libc::LOCK_UN; + } + + if !is_blocking { + operation |= libc::LOCK_NB; + } + + if let Some(file) = self.file_map.get_value(fh) { + let ret = flock(file, operation); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } + + /// Create a file with name in the management of filesystem. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `create_in` - The information of creating a file contains the flags, mode and umask. + /// * `name` - The string of name used to create a file. + /// * `fh` - The file handler is returned in the management of filesystem. + /// * `node_id` - The node id that is found by the name in the management of filesystem. + /// * `fuse_attr` - The attributes will be returned by the found inode. + pub fn create( + &mut self, + in_header: &FuseInHeader, + create_in: &FuseCreateIn, + name: CString, + fh: &mut u64, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_dir = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let (file_opt, ret) = open_at( + &parent_dir.file, + name.clone(), + (create_in.flags as i32 | libc::O_CREAT) & !libc::O_NOFOLLOW, + create_in.mode & !(create_in.umask & 0o777), + ); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64; + } + + self.internal_lookup(&parent_dir, name, node_id, fuse_attr) + } + + /// Destroy the management of filesystem, except for the root inode. + pub fn destroy(&mut self) -> i32 { + let root_key = self.root_inode.key; + + self.inode_key_map.destroy_map(); + self.file_map.destroy_map(); + self.inodes = BTreeMap::new(); + + // Need to add root_inode back to the inode table and inode_key_map table for + // the filesystem function + let root_id = self.inode_key_map.get_map(root_key); + self.root_inode.node_id = root_id; + self.inodes.insert(root_key, self.root_inode.clone()); + + FUSE_OK + } + + /// Allocate the disk space with file handler in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `mode` - The mode determines the operation to be performed on the given range. + /// * `offset` - The offset in the file. + /// * `length` - The length that needs to be allocated. + pub fn fallocate(&self, fh: usize, mode: u32, offset: u64, length: u64) -> i32 { + if let Some(file) = self.file_map.get_value(fh) { + let ret = fallocate(file, mode, offset, length); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } + + /// Reposition the file offset of the open file with file handler + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `offset` - The offset in the file used together with the whence. + /// * `whence` - The whence determines the operation to be performed in the file. + /// * `outoffset` - The offset from the beginning of the file is returned. + pub fn lseek(&self, fh: usize, offset: u64, whence: u32, outoffset: &mut u64) -> i32 { + if let Some(file) = self.file_map.get_value(fh) { + let (offset_tmp, ret) = lseek(file, offset, whence); + if ret != FUSE_OK { + return ret; + } + *outoffset = offset_tmp; + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } +} -- Gitee From 440e677a61b56377b10722a9fd6a31ac831b5761 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:41:58 +0800 Subject: [PATCH 0185/2187] virtiofs: process fuseMsg of different opcodes add functions to process fuse message of different opcodes Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/fuse_proc.rs | 1751 ++++++++++++++++++++++++++++++++ 1 file changed, 1751 insertions(+) create mode 100644 vhost_user_fs/src/fuse_proc.rs diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs new file mode 100644 index 000000000..cb2289a11 --- /dev/null +++ b/vhost_user_fs/src/fuse_proc.rs @@ -0,0 +1,1751 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +const MAX_WRITE_SIZE: u32 = 1 << 20; + +use super::fs::FileSystem; +use super::fuse_msg::*; +use address_space::AddressSpace; +use error_chain::ChainedError; +use std::convert::TryInto; +use std::ffi::CString; +use std::mem; +use std::sync::{Arc, Mutex}; + +fn is_safe_path(path: CString) -> bool { + let path_str = match path.into_string() { + Ok(str_) => str_, + Err(_e) => return false, + }; + + if path_str.find('/').is_some() { + return false; + } + + // Check if the path is "." or ".." + let bytes = path_str.as_bytes(); + if bytes[0] == 0x2e && (bytes[1] == 0x0 || (bytes[1] == 0x2e && bytes[2] == 0x0)) { + return false; + } + + true +} + +/// Process the fuse message of FUSE_LOOKUP. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_lookup( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for lookup, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fuse_attr = FuseAttr::default(); + let mut node_id = 0_u64; + let ret = fs.lock().unwrap().lookup( + in_header.nodeid as usize, + name, + &mut node_id, + &mut fuse_attr, + ); + + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_FORGET. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_forget( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let forget_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for forget_in, {}", e.display_chain()); + return 0_u32; + } + }; + + fs.lock() + .unwrap() + .forget(in_header.nodeid as usize, forget_in.nlookup); + 0_u32 +} + +/// Process the fuse message of FUSE_GETATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_getattr( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .getattr(in_header.nodeid as usize, &mut fuse_attr); + if ret == 0 { + let attr_out = FuseAttrOut { + attr_valid: 1, + attr_valid_nsec: 0, + dummy: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&attr_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_SETATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_setattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let setattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for setattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .setattr(in_header.nodeid as usize, &setattr_in, &mut fuse_attr); + if ret == FUSE_OK { + let attr_out = FuseAttrOut { + attr_valid: 1, + attr_valid_nsec: 0, + dummy: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&attr_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_READLINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_readlink( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mut buff = Vec::new(); + let ret = fs + .lock() + .unwrap() + .readlink(in_header.nodeid as usize, &mut buff); + + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_SYMLINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_symlink( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for symlink, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let link_name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!( + "Failed to read link name for symlink, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(link_name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .symlink(in_header, name, link_name, &mut node_id, &mut fuse_attr); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_MKNOD. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_mknod( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mknod_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for mknod_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for mknod, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .mknod(in_header, &mknod_in, name, &mut node_id, &mut fuse_attr); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_MKDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_mkdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mkdir_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for mkdir_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for mkdir, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .mkdir(in_header, &mkdir_in, name, &mut node_id, &mut fuse_attr); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_UNLINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_unlink( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for unlink, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let ret = fs.lock().unwrap().unlink(in_header.nodeid as usize, name); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_RMDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_rmdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for rmdir, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let ret = fs.lock().unwrap().rmdir(in_header.nodeid as usize, name); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_RENAME. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_rename( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let rename_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for rename_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let oldname = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read old name for rename, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let newname = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read new name for rename, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(oldname.clone()) || !is_safe_path(newname.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let ret = fs.lock().unwrap().rename( + in_header.nodeid as usize, + oldname, + rename_in.newdir as usize, + newname, + ); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_LINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_link( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let link_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for link_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for link, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut fuse_attr = FuseAttr::default(); + let mut node_id = 0_u64; + let ret = fs.lock().unwrap().link( + in_header.nodeid as usize, + link_in.oldnodeid as usize, + name, + &mut node_id, + &mut fuse_attr, + ); + + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_OPEN. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_open( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let open_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for open_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fh = 0_u64; + let ret = fs + .lock() + .unwrap() + .open(in_header.nodeid as usize, open_in.flags, &mut fh); + if ret == FUSE_OK { + let open_out = FuseOpenOut { + fh, + open_flags: 0, + padding: 0, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&open_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_READ. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_read( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let read_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for read_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fd = 0; + let ret = fs.lock().unwrap().read(read_in.fh as usize, &mut fd); + if ret == FUSE_OK { + let buf_header = match writer.bufs.get(0) { + Some(b) => *b, + None => { + error!("Failed to get the address of out_header"); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut f_ret = reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize); + + match writer.access_file(sys_mem, fd, read_in.offset, read_in.size, true) { + Ok(size) => { + f_ret += size; + // write size to FuseOutHeader.len + sys_mem.write_object(&f_ret, buf_header.addr).unwrap(); + } + Err(e) => { + error!("Failed to access file for reading, {}", e.display_chain()); + } + }; + + f_ret + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_WRITE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_write( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let write_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for write_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fd = 0; + let ret = fs.lock().unwrap().write(write_in.fh as usize, &mut fd); + if ret == FUSE_OK { + match reader.access_file(sys_mem, fd, write_in.offset, write_in.size, false) { + Ok(size) => { + let write_out = FuseWriteOut { size, padding: 0 }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&write_out)]), + mem::size_of::(), + ) + } + Err(e) => { + error!("Failed to access file for writing, {}", e.display_chain()); + reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize) + } + } + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_STATFS. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_statfs( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mut statfs = FuseStatfsOut::default(); + let ret = fs + .lock() + .unwrap() + .statfs(in_header.nodeid as usize, &mut statfs); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&statfs)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_RELEASE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_release( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let release_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for release_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().release(release_in.fh as usize); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_FSYNC. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_fsync( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let fsync_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read name for fsync_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let datasync = fsync_in.fsync_flags & 0x1 == 0x1; + + let ret = fs.lock().unwrap().fsyncfile(fsync_in.fh as usize, datasync); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_SETXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_setxattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let setxattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for setxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for setxattr_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let value = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!( + "Failed to read value for setxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().setxattr( + in_header.nodeid as usize, + name, + value, + setxattr_in.size, + setxattr_in.flags, + ); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_GETXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_getxattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let getxattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for getxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for getxattr_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = + fs.lock() + .unwrap() + .getxattr(in_header.nodeid as usize, name, getxattr_in.size, &mut buff); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_LISTXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_listxattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let getxattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for listxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = fs + .lock() + .unwrap() + .listxattr(in_header.nodeid as usize, getxattr_in.size, &mut buff); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_REMOVEXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_removexattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!( + "Failed to read name for removexattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs + .lock() + .unwrap() + .removexattr(in_header.nodeid as usize, name); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_FLUSH. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_flush( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let flush_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for flush_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs + .lock() + .unwrap() + .flush(in_header.nodeid as usize, flush_in.lock_owner); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_INIT. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_init( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let init_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for init_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut support_flags = 0_u32; + fs.lock().unwrap().init(init_in.flags, &mut support_flags); + let pagesize: u32 = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }; + let init_out = FuseInitOut { + major: FUSE_KERNEL_VERSION, + minor: FUSE_KERNEL_MINOR_VERSION, + max_readahead: init_in.max_readahead, + flags: support_flags, + max_background: 0, + congestion_threshold: 0, + max_write: MAX_WRITE_SIZE, + /* Granularity of c/m/atime in ns (cannot be worse than a second), default 1 */ + time_gran: 1, + max_pages: ((MAX_WRITE_SIZE + pagesize - 1) / pagesize) as u16, + map_alignment: 0, + ..Default::default() + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&init_out)]), + mem::size_of::(), + ) +} + +/// Process the fuse message of FUSE_OPENDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_opendir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let _open_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for opendir_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut dir_fh = 0_u64; + let ret = fs + .lock() + .unwrap() + .opendir(in_header.nodeid as usize, &mut dir_fh); + if ret == FUSE_OK { + let open_out = FuseOpenOut { + fh: dir_fh, + open_flags: 0, + padding: 0, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&open_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_READDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_readdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let read_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for readdir_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = fs.lock().unwrap().readdir( + in_header.nodeid as usize, + read_in.fh as usize, + read_in.size, + read_in.offset, + false, + &mut buff, + ); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_RELEASEDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_releasedir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let release_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for releasedir_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().releasedir(release_in.fh as usize); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_FSYNCDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_fsyncdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let fsync_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for fsync_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let datasync = fsync_in.fsync_flags & 0x1 == 0x1; + let ret = fs.lock().unwrap().fsyncdir(fsync_in.fh as usize, datasync); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_GETLK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_getlk( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lk_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for get_lk_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut file_lock = FuseFileLock::default(); + let ret = fs.lock().unwrap().getlk( + in_header.nodeid as usize, + lk_in.owner, + &lk_in.lk, + &mut file_lock, + ); + + if ret == FUSE_OK { + let lk_out = FuseLkOut { lk: file_lock }; + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&lk_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +fn do_fuse_setlk_common( + sys_mem: &Arc, + fs: Arc>, + lk_in: &FuseLkIn, + is_blocking: bool, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let ret = fs.lock().unwrap().setlk( + in_header.nodeid as usize, + lk_in.owner, + is_blocking, + &lk_in.lk, + ); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +fn do_fuse_flock( + sys_mem: &Arc, + fs: Arc>, + lk_in: &FuseLkIn, + is_blocking: bool, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let ret = fs + .lock() + .unwrap() + .flock(lk_in.fh as usize, lk_in.lk.lock_type, is_blocking); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +const FUSE_LOCK_FLOCK: u32 = (1 << 0) as u32; + +/// Process the fuse message of FUSE_SETLK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_setlk( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lk_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for set_lk_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if lk_in.lk_flags & FUSE_LOCK_FLOCK == FUSE_LOCK_FLOCK { + do_fuse_flock(sys_mem, fs, &lk_in, false, writer, in_header) + } else { + do_fuse_setlk_common(sys_mem, fs, &lk_in, false, writer, in_header) + } +} + +/// Process the fuse message of FUSE_SETLKW. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_setlkw( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lk_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for setlkw_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if lk_in.lk_flags & FUSE_LOCK_FLOCK == FUSE_LOCK_FLOCK { + do_fuse_flock(sys_mem, fs, &lk_in, true, writer, in_header) + } else { + do_fuse_setlk_common(sys_mem, fs, &lk_in, true, writer, in_header) + } +} + +/// Process the fuse message of FUSE_CREATE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_create( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let create_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for create_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(string) => string, + Err(e) => { + error!( + "Failed to read name for creating file, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut fh = 0_u64; + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs.lock().unwrap().create( + in_header, + &create_in, + name, + &mut fh, + &mut node_id, + &mut fuse_attr, + ); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + let open_out = FuseOpenOut { + fh: fh as u64, + open_flags: 0, + padding: 0, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![ + FuseIovec::from_obj(&entry_out), + FuseIovec::from_obj(&open_out), + ]), + mem::size_of::() + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_DESTROY. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_destroy( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let ret = fs.lock().unwrap().destroy(); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_BATCH_FORGET. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +pub fn do_fuse_batch_forget( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, +) -> u32 { + let batch_forget_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for batch_forget_in, {}", + e.display_chain() + ); + return 0_u32; + } + }; + + for _i in 0..batch_forget_in.count as usize { + let forget_data_in = match reader.read_obj::(sys_mem) { + Ok(data) => data, + Err(e) => { + error!( + "Failed to read object for forget_date_in, {}", + e.display_chain() + ); + return 0; + } + }; + + fs.lock() + .unwrap() + .forget(forget_data_in.ino as usize, forget_data_in.nlookup); + } + + 0_u32 +} + +/// Process the fuse message of FUSE_FALLOCATE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_fallocate( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let fallocate_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for fallocate_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().fallocate( + fallocate_in.fh as usize, + fallocate_in.mode, + fallocate_in.offset, + fallocate_in.length, + ); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +/// Process the fuse message of FUSE_READDIRPLUS. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_readdirplus( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let read_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for readdirplus_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = fs.lock().unwrap().readdir( + in_header.nodeid as usize, + read_in.fh as usize, + read_in.size, + read_in.offset, + true, + &mut buff, + ); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +/// Process the fuse message of FUSE_LSEEK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. +pub fn do_fuse_lseek( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lseek_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for lseek_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut outoffset = 0_u64; + let ret = fs.lock().unwrap().lseek( + lseek_in.fh as usize, + lseek_in.offset, + lseek_in.whence, + &mut outoffset, + ); + + if ret == FUSE_OK { + let lseekout = FuseLseekOut { offset: outoffset }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&lseekout)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} -- Gitee From 565beb7abf5017beb72b4942b094a29624b5d080 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:07 +0800 Subject: [PATCH 0186/2187] virtiofs: process request of fuse message parsed from virtio queue process request of fuse message by calling functions in fuse_proc.rs Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/fuse_req.rs | 172 ++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 vhost_user_fs/src/fuse_req.rs diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs new file mode 100644 index 000000000..b35aa0e57 --- /dev/null +++ b/vhost_user_fs/src/fuse_req.rs @@ -0,0 +1,172 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::fs::FileSystem; +use super::fuse_msg::*; +use super::fuse_proc::*; +use address_space::AddressSpace; +use error_chain::ChainedError; +use std::sync::{Arc, Mutex}; +use virtio::Element; + +/// The request of fuse message parsed from virtio queue. +pub struct FuseReq { + desc_index: u16, + reader: FuseBuffer, + writer: FuseBuffer, +} + +impl FuseReq { + /// Construct a request of fuse message by the element from virtio queue. + /// + /// # Arguments + /// + /// * `elem` - The element parsed from virtio queue. + pub fn new(elem: &Element) -> Self { + FuseReq { + desc_index: elem.index, + reader: FuseBuffer::new(&elem.out_iovec), + writer: FuseBuffer::new(&elem.in_iovec), + } + } + + /// The function to deal with fuse message from the request. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// * `fs` - The management of userspace filesystem. + pub fn execute( + &mut self, + sys_mem: &Arc, + fs: Arc>, + ) -> (u16, u32) { + let in_header = match self.reader.read_obj::(sys_mem) { + Ok(data) => data, + Err(err) => { + error!( + "Failed to read the header of fuse msg, {}", + err.display_chain(), + ); + return (self.desc_index, 0); + } + }; + + let written_len = match in_header.opcode { + FUSE_LOOKUP => { + do_fuse_lookup(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_FORGET => do_fuse_forget(sys_mem, fs, &mut self.reader, &in_header), + FUSE_GETATTR => do_fuse_getattr(sys_mem, fs, &mut self.writer, &in_header), + FUSE_SETATTR => { + do_fuse_setattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_READLINK => do_fuse_readlink(sys_mem, fs, &mut self.writer, &in_header), + FUSE_SYMLINK => { + do_fuse_symlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_MKNOD => { + do_fuse_mknod(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_MKDIR => { + do_fuse_mkdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_UNLINK => { + do_fuse_unlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_RMDIR => { + do_fuse_rmdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_RENAME => { + do_fuse_rename(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_LINK => do_fuse_link(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_OPEN => do_fuse_open(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_READ => do_fuse_read(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_WRITE => { + do_fuse_write(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_STATFS => do_fuse_statfs(sys_mem, fs, &mut self.writer, &in_header), + FUSE_RELEASE => { + do_fuse_release(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_FSYNC => { + do_fuse_fsync(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_SETXATTR => { + do_fuse_setxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_GETXATTR => { + do_fuse_getxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_LISTXATTR => { + do_fuse_listxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_REMOVEXATTR => { + do_fuse_removexattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_FLUSH => { + do_fuse_flush(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_INIT => do_fuse_init(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_OPENDIR => { + do_fuse_opendir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_READDIR => { + do_fuse_readdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_RELEASEDIR => { + do_fuse_releasedir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_FSYNCDIR => { + do_fuse_fsyncdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_GETLK => { + do_fuse_getlk(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_SETLK => { + do_fuse_setlk(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_SETLKW => { + do_fuse_setlkw(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_CREATE => { + do_fuse_create(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_DESTROY => do_fuse_destroy(sys_mem, fs, &mut self.writer, &in_header), + FUSE_BATCH_FORGET => do_fuse_batch_forget(sys_mem, fs, &mut self.reader), + FUSE_FALLOCATE => { + do_fuse_fallocate(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_READDIRPLUS => { + do_fuse_readdirplus(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_LSEEK => { + do_fuse_lseek(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + _ => { + error!("The fuse msg {} is unsupported", in_header.opcode); + reply_fuse_msg( + &mut self.writer, + sys_mem, + &in_header, + libc::ENOSYS, + None, + 0_usize, + ) + } + }; + + // return the index of element and the length which is written + (self.desc_index, written_len) + } +} -- Gitee From b402e253592340941f06798d8b78335d5c308217 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:14 +0800 Subject: [PATCH 0187/2187] util: move funtions in sock.rs to unix.rs add server_connection_refuse() add vhostuser_msgrecv() add vhostuser_msgsend() Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- util/src/unix.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/util/src/unix.rs b/util/src/unix.rs index 07644fc21..5f871a297 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -167,7 +167,7 @@ impl UnixSock { } /// Bind assigns a unique listener for the socket. - fn bind(&mut self, unlink: bool) -> Result<()> { + pub fn bind(&mut self, unlink: bool) -> Result<()> { if unlink { std::fs::remove_file(self.path.as_str()) .chain_err(|| format!("Failed to remove socket file {}.", self.path.as_str()))?; @@ -180,7 +180,7 @@ impl UnixSock { } /// The listener accepts incoming client connections. - fn accept(&mut self) -> Result<()> { + pub fn accept(&mut self) -> Result<()> { let (sock, _addr) = self .listener .as_ref() @@ -192,10 +192,22 @@ impl UnixSock { Ok(()) } - fn is_accepted(&self) -> bool { + pub fn is_accepted(&self) -> bool { self.sock.is_some() } + pub fn server_connection_refuse(&mut self) -> Result<()> { + // Refuse connection by finishing life cycle of stream fd from listener fd. + self.listener.as_ref().unwrap().accept().chain_err(|| { + format!( + "Failed to accept the socket for refused connection {}", + self.path + ) + })?; + + Ok(()) + } + /// Unix socket stream create a connection for requests. pub fn connect(&mut self) -> Result<()> { let sock = UnixStream::connect(self.path.as_str()) @@ -418,7 +430,6 @@ impl UnixSock { cmsg_ptr = self.get_next_cmsg(&msg, &cmsg, cmsg_ptr); } - Ok((total_read as usize, in_fds_count as usize)) } } -- Gitee From cd3bf0b1c82829a81ffceaa89c1d8197b3c6731c Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:23 +0800 Subject: [PATCH 0188/2187] machine_manager: add fs mod add fs mod in machine manager package Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- machine_manager/src/config/fs.rs | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 machine_manager/src/config/fs.rs diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs new file mode 100644 index 000000000..bdc3c0e7f --- /dev/null +++ b/machine_manager/src/config/fs.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::errors::{ErrorKind, Result}; +use crate::config::{ + pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH, + MAX_STRING_LENGTH, MAX_TAG_LENGTH, +}; +use error_chain::bail; + +/// Config struct for `fs`. +/// Contains fs device's attr. +#[derive(Debug, Clone)] +pub struct FsConfig { + /// Device tag. + pub tag: String, + /// Device id. + pub id: String, + /// Char device sock path. + pub sock: String, +} + +impl Default for FsConfig { + fn default() -> Self { + FsConfig { + tag: "".to_string(), + id: "".to_string(), + sock: "".to_string(), + } + } +} + +impl ConfigCheck for FsConfig { + fn check(&self) -> Result<()> { + if self.tag.len() >= MAX_TAG_LENGTH { + return Err(ErrorKind::StringLengthTooLong( + "fs device tag".to_string(), + MAX_TAG_LENGTH - 1, + ) + .into()); + } + + if self.id.len() >= MAX_STRING_LENGTH { + return Err(ErrorKind::StringLengthTooLong( + "fs device id".to_string(), + MAX_STRING_LENGTH - 1, + ) + .into()); + } + + if self.sock.len() > MAX_PATH_LENGTH { + return Err(ErrorKind::StringLengthTooLong( + "fs sock path".to_string(), + MAX_PATH_LENGTH, + ) + .into()); + } + + Ok(()) + } +} + +pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("fs"); + cmd_parser + .push("") + .push("tag") + .push("id") + .push("chardev") + .push("bus") + .push("addr") + .push("multifunction"); + cmd_parser.parse(fs_config)?; + pci_args_check(&cmd_parser)?; + let mut fs_cfg = FsConfig::default(); + if let Some(tag) = cmd_parser.get_value::("tag")? { + fs_cfg.tag = tag; + } else { + return Err(ErrorKind::FieldIsMissing("tag", "virtio-fs").into()); + } + + if let Some(id) = cmd_parser.get_value::("id")? { + fs_cfg.id = id; + } else { + return Err(ErrorKind::FieldIsMissing("id", "virtio-fs").into()); + } + + if let Some(name) = cmd_parser.get_value::("chardev")? { + if let Some(char_dev) = vm_config.chardev.remove(&name) { + match &char_dev.backend { + ChardevType::Socket { path, .. } => { + fs_cfg.sock = path.clone(); + } + _ => { + bail!("Chardev {:?} backend should be socket type.", &name); + } + } + } else { + bail!("Chardev {:?} not found or is in use", &name); + } + } else { + return Err(ErrorKind::FieldIsMissing("chardev", "virtio-fs").into()); + } + fs_cfg.check()?; + + Ok(fs_cfg) +} -- Gitee From e451fb50cef81fb3e2d623f8fe941dd2095b2c6f Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:30 +0800 Subject: [PATCH 0189/2187] virtio: add fs mod add fs mod in virtio package Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- machine_manager/src/config/mod.rs | 3 + virtio/src/lib.rs | 7 +- virtio/src/vhost/user/client.rs | 6 +- virtio/src/vhost/user/fs.rs | 246 ++++++++++++++++++++++++++++++ virtio/src/vhost/user/mod.rs | 5 + 5 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 virtio/src/vhost/user/fs.rs diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 65f528fd7..de7860f07 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -84,6 +84,7 @@ pub use boot_source::*; pub use chardev::*; pub use devices::*; pub use drive::*; +pub use fs::*; pub use gpu::*; pub use incoming::*; pub use iothread::*; @@ -101,6 +102,7 @@ mod boot_source; mod chardev; mod devices; mod drive; +mod fs; mod gpu; mod incoming; mod iothread; @@ -131,6 +133,7 @@ pub const MAX_PATH_LENGTH: usize = 4096; pub const MAX_VIRTIO_QUEUE: usize = 32; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; +pub const MAX_TAG_LENGTH: usize = 36; pub const MAX_NODES: u32 = 128; #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index e60bdd43e..e6fd6c3a2 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -24,7 +24,8 @@ //! //! - `x86_64` //! - `aarch64` - +#[macro_use] +extern crate log; pub mod errors { use error_chain::error_chain; @@ -107,7 +108,7 @@ mod gpu; mod net; mod queue; mod rng; -mod vhost; +pub mod vhost; mod virtio_mmio; #[allow(dead_code)] mod virtio_pci; @@ -147,7 +148,7 @@ pub const VIRTIO_TYPE_RNG: u32 = 4; pub const VIRTIO_TYPE_BALLOON: u32 = 5; pub const VIRTIO_TYPE_GPU: u32 = 16; pub const VIRTIO_TYPE_VSOCK: u32 = 19; -pub const _VIRTIO_TYPE_FS: u32 = 26; +pub const VIRTIO_TYPE_FS: u32 = 26; // The Status of Virtio Device. const CONFIG_STATUS_ACKNOWLEDGE: u32 = 0x01; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index ca945a071..c1f41f9f3 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -683,14 +683,12 @@ impl VhostOps for VhostUserClient { queue.desc_table.0 )) })?; - let used_user_addr = self.mem_info.addr_to_host(queue.used_ring).ok_or_else(|| { ErrorKind::Msg(format!( "Failed to transform used ring address {}", queue.used_ring.0 )) })?; - let avail_user_addr = self .mem_info .addr_to_host(queue.avail_ring) @@ -700,7 +698,7 @@ impl VhostOps for VhostUserClient { queue.avail_ring.0 )) })?; - let vring_addr = VhostUserVringAddr { + let _vring_addr = VhostUserVringAddr { index: index as u32, flags, desc_user_addr, @@ -710,7 +708,7 @@ impl VhostOps for VhostUserClient { }; client .sock - .send_msg(Some(&hdr), Some(&vring_addr), payload_opt, &[]) + .send_msg(Some(&hdr), Some(&_vring_addr), payload_opt, &[]) .chain_err(|| "Failed to send msg for setting vring addr")?; Ok(()) diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs new file mode 100644 index 000000000..8b51e1ba9 --- /dev/null +++ b/virtio/src/vhost/user/fs.rs @@ -0,0 +1,246 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +// The num of high priority queue +const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: usize = 1; +// The num of request queue +const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; +// The size of queue for virtio fs +const VIRTIO_FS_QUEUE_SIZE: u16 = 1024; + +use std::cmp; +use std::io::Write; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::{Arc, Mutex}; + +use error_chain::ChainedError; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; + +use address_space::AddressSpace; +use machine_manager::{ + config::{FsConfig, MAX_TAG_LENGTH}, + event_loop::EventLoop, +}; +use util::byte_code::ByteCode; +use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::num_ops::{read_u32, write_u32}; + +use super::super::super::errors::{ErrorKind, Result, ResultExt}; +use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; +use super::super::{VhostNotify, VhostOps}; +use super::VhostUserClient; +use crate::{VirtioInterrupt, VirtioInterruptType}; + +#[derive(Copy, Clone)] +#[repr(C, packed)] +struct VirtioFsConfig { + tag: [u8; MAX_TAG_LENGTH], + num_request_queues: u32, +} + +impl Default for VirtioFsConfig { + fn default() -> Self { + VirtioFsConfig { + tag: [0; MAX_TAG_LENGTH], + num_request_queues: 0, + } + } +} + +impl ByteCode for VirtioFsConfig {} + +struct VhostUserFsHandler { + interrup_cb: Arc, + host_notifies: Vec, +} + +impl EventNotifierHelper for VhostUserFsHandler { + fn internal_notifiers(vhost_user_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let vhost_user = vhost_user_handler.clone(); + + let handler: Box Option>> = + Box::new(move |_, fd: RawFd| { + read_fd(fd); + + let locked_vhost_user = vhost_user.lock().unwrap(); + + for host_notify in locked_vhost_user.host_notifies.iter() { + if let Err(e) = (locked_vhost_user.interrup_cb)( + &VirtioInterruptType::Vring, + Some(&host_notify.queue.lock().unwrap()), + ) { + error!( + "Failed to trigger interrupt for vhost user device, error is {}", + e.display_chain() + ); + } + } + + None as Option> + }); + let h = Arc::new(Mutex::new(handler)); + + for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + host_notify.notify_evt.as_raw_fd(), + None, + EventSet::IN, + vec![h.clone()], + )); + } + + notifiers + } +} + +pub struct Fs { + fs_cfg: FsConfig, + config: VirtioFsConfig, + client: Option>>, + avail_features: u64, + acked_features: u64, + mem_space: Arc, + /// The notifier events from host. + call_events: Vec, +} + +impl Fs { + pub fn new(fs_cfg: FsConfig, mem_space: Arc) -> Self { + Fs { + fs_cfg, + config: VirtioFsConfig::default(), + client: None, + avail_features: 0_u64, + acked_features: 0_u64, + mem_space, + call_events: Vec::::new(), + } + } +} + +impl VirtioDevice for Fs { + fn realize(&mut self) -> Result<()> { + let tag_bytes_vec = self.fs_cfg.tag.clone().into_bytes(); + self.config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); + self.config.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; + + let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; + let client = VhostUserClient::new(&self.mem_space, &self.fs_cfg.sock, queues_num as u64) + .chain_err(|| { + "Failed to create the client which communicates with the server for virtio fs" + })?; + let client = Arc::new(Mutex::new(client)); + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(client.clone()), + None, + ) + .chain_err(|| "Failed to update event for client sock")?; + self.avail_features = client + .lock() + .unwrap() + .get_features() + .chain_err(|| "Failed to get features for virtio fs")?; + self.client = Some(client); + + Ok(()) + } + + fn device_type(&self) -> u32 { + VIRTIO_TYPE_FS as u32 + } + + fn queue_num(&self) -> usize { + VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM + } + + fn queue_size(&self) -> u16 { + VIRTIO_FS_QUEUE_SIZE + } + + fn get_device_features(&self, features_select: u32) -> u32 { + read_u32(self.avail_features, features_select) + } + + fn set_driver_features(&mut self, page: u32, value: u32) { + let mut features = write_u32(value, page); + let unsupported_features = features & !self.avail_features; + if unsupported_features != 0 { + warn!( + "Received acknowledge request with unsupported feature for virtio fs: 0x{:x}", + features + ); + features &= !unsupported_features; + } + self.acked_features |= features; + } + + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + let config_slice = self.config.as_bytes(); + let config_size = config_slice.len() as u64; + if offset >= config_size { + return Err(ErrorKind::DevConfigOverflow(offset, config_size).into()); + } + if let Some(end) = offset.checked_add(data.len() as u64) { + data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; + } + + Ok(()) + } + + fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + let data_len = data.len(); + let config_slice = self.config.as_mut_bytes(); + let config_len = config_slice.len(); + if offset as usize + data_len > config_len { + return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + } + + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + + Ok(()) + } + + fn activate( + &mut self, + _mem_space: Arc, + _interrup_cb: Arc, + queues: &[Arc>], + queue_evts: Vec, + ) -> Result<()> { + let mut client = match &self.client { + Some(client) => client.lock().unwrap(), + None => return Err("Failed to get client for virtio fs".into()), + }; + client.features = self.acked_features; + client.set_queues(queues); + client.set_queue_evts(&queue_evts); + client.activate_vhost_user()?; + Ok(()) + } + + /// Set guest notifiers for notifying the guest. + fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { + for fd in queue_evts.iter() { + let cloned_evt_fd = fd.try_clone().unwrap(); + self.call_events.push(cloned_evt_fd); + } + match &self.client { + Some(client) => client.lock().unwrap().set_call_events(queue_evts), + None => return Err("Failed to get client for vhost-user net".into()), + } + Ok(()) + } +} diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 12f0717d0..02f4c8af1 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -19,3 +19,8 @@ mod sock; pub use block::Block; use client::VhostUserClient; pub use net::Net; +pub mod fs; +pub use self::client::*; +pub use self::fs::*; +pub use self::message::*; +pub use self::sock::*; -- Gitee From 838a01caf148faeb3768d111f37a0b2823972ab6 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:36 +0800 Subject: [PATCH 0190/2187] virtiofs: add VhostUserFs(include VhostUserServer,virtio_fs) add VhostUserFs and its two important components:VhostUserServer,virtio_fs Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- vhost_user_fs/src/vhost_user_fs.rs | 174 +++++++++ vhost_user_fs/src/vhost_user_server.rs | 467 +++++++++++++++++++++++++ vhost_user_fs/src/virtio_fs.rs | 429 +++++++++++++++++++++++ 3 files changed, 1070 insertions(+) create mode 100644 vhost_user_fs/src/vhost_user_fs.rs create mode 100644 vhost_user_fs/src/vhost_user_server.rs create mode 100644 vhost_user_fs/src/virtio_fs.rs diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs new file mode 100644 index 000000000..dbb741e23 --- /dev/null +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -0,0 +1,174 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::unix::io::RawFd; +use std::sync::{Arc, Mutex}; + +use error_chain::ChainedError; +use vmm_sys_util::epoll::EventSet; + +use machine_manager::event_loop::EventLoop; +use util::loop_context::{ + EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; + +use super::cmdline::FsConfig; +use super::fs::set_rlimit_nofile; +use super::vhost_user_server::VhostUserServerHandler; +use super::virtio_fs::VirtioFs; + +use crate::errors::{Result, ResultExt}; + +/// The vhost-user filesystem device contains virtio fs device and the vhost-user +/// server which can be connected with the vhost-user client in StratoVirt. +#[derive(Clone)] +pub struct VhostUserFs { + /// Used to communicate with StratoVirt. + server_handler: VhostUserServerHandler, +} + +trait CreateEventNotifier { + fn create_event_notifier( + &mut self, + server_handler: Arc>, + ) -> Option>; +} + +impl CreateEventNotifier for VhostUserServerHandler { + fn create_event_notifier( + &mut self, + server_handler: Arc>, + ) -> Option> { + let mut notifiers = Vec::new(); + if self.sock.domain.is_accepted() { + if let Err(e) = self.sock.domain.server_connection_refuse() { + error!( + "Failed to refuse socket for vhost user server, {}", + e.display_chain() + ); + } + return None; + } else if let Err(e) = self.sock.domain.accept() { + error!( + "Failed to accept the socket for vhost user server, {}", + e.display_chain() + ); + return None; + } + + let mut handlers = Vec::new(); + let handler: Box = Box::new(move |event, _| { + if event == EventSet::IN { + let mut lock_server_handler = server_handler.lock().unwrap(); + if let Err(e) = lock_server_handler.handle_request() { + error!( + "Failed to handle request for vhost user server, {}", + e.display_chain() + ); + } + } + + if event & EventSet::HANG_UP == EventSet::HANG_UP { + panic!("Receive the event of HANG_UP from stratovirt"); + } else { + None + } + }); + + handlers.push(Arc::new(Mutex::new(handler))); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + self.sock.domain.get_stream_raw_fd(), + None, + EventSet::IN | EventSet::HANG_UP, + handlers, + ); + + notifiers.push(notifier); + Some(notifiers) + } +} + +impl EventNotifierHelper for VhostUserServerHandler { + fn internal_notifiers(server_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let mut handlers = Vec::new(); + let server_handler_clone = server_handler.clone(); + let handler: Box Option>> = + Box::new(move |_, _| { + server_handler_clone + .lock() + .unwrap() + .create_event_notifier(server_handler_clone.clone()) + }); + + handlers.push(Arc::new(Mutex::new(handler))); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + server_handler.lock().unwrap().sock.domain.get_listener_raw_fd(), + None, + EventSet::IN, + handlers, + ); + + notifiers.push(notifier); + + notifiers + } +} + +impl VhostUserFs { + /// Create a new vhost-user filesystem device. + /// + /// # Arguments + /// + /// * `fs_config` - Configuration of the vhost-user filesystem device. + pub fn new(fs_config: FsConfig) -> Result { + if let Some(limit) = fs_config.rlimit_nofile { + set_rlimit_nofile(limit) + .chain_err(|| format!("Failed to set rlimit nofile {}", limit))?; + } + + let virtio_fs = Arc::new(Mutex::new( + VirtioFs::new(&fs_config.source_dir) + .chain_err(|| format!("Failed to create virtio fs {}", fs_config.source_dir))?, + )); + + let server_handler = VhostUserServerHandler::new(&fs_config.sock_path, virtio_fs) + .chain_err(|| format!("Failed to create vhost user server {}", fs_config.sock_path))?; + Ok(VhostUserFs { server_handler }) + } + + /// Add events to epoll handler for the vhost-user filesystem device. + pub fn add_event_notifier(&self) -> Result<()> { + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new( + self.server_handler.clone(), + ))), + None, + )?; + + Ok(()) + } +} + +impl EventLoopManager for VhostUserFs { + fn loop_should_exit(&self) -> bool { + false + } + + fn loop_cleanup(&self) -> util::errors::Result<()> { + Ok(()) + } +} diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs new file mode 100644 index 000000000..ccd85b054 --- /dev/null +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -0,0 +1,467 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem::size_of; +use std::os::unix::io::RawFd; +use std::slice; +use std::sync::{Arc, Mutex}; +use util::unix::limit_permission; +use virtio::vhost::user::{ + VhostUserHdrFlag, VhostUserMemHdr, RegionMemInfo, + VhostUserMsgHdr, VhostUserMsgReq, VhostUserVringAddr, VhostUserVringState, MAX_ATTACHED_FD_ENTRIES, +}; +use virtio::VhostUser::VhostUserSock; + +use crate::errors::{Result, ResultExt}; + +/// The trait for dealing with vhost-user request in the server. +pub trait VhostUserReqHandler: Send + Sync { + /// Set the current process as the owner of this file descriptor. + fn set_owner(&mut self) -> Result<()>; + + /// Get a bitmask of supported virtio/vhost features. + fn get_features(&self) -> Result; + + /// Inform the vhost subsystem which features to enable. + /// + /// # Arguments + /// + /// * `features` - The features from the vhost-user client in StratoVirt. + fn set_features(&mut self, features: u64) -> Result<()>; + + /// Set the guest memory mappings for vhost to use. + /// + /// # Arguments + /// + /// * `regions` - The slice of memory region information for the message of memory table. + /// * `fds` - The files descriptors are used to map shared memory for the process and + /// StratoVirt. + fn set_mem_table(&mut self, regions: &[RegionMemInfo], fds: &[RawFd]) -> Result<()>; + + /// Set the size of descriptors in the virtio queue. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `num` - The total size of virtio queue. + fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>; + + /// Set the addresses for a given virtio queue. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `flags` - Option flags. + /// * `desc_table` - The start address of descriptor table. + /// * `used_ring` - The start address of used ring. + /// * `avail_ring` - The start address of avail ring. + /// * `log` - The start address of log. + fn set_vring_addr( + &mut self, + queue_index: usize, + flags: u32, + desc_table: u64, + used_ring: u64, + avail_ring: u64, + log: u64, + ) -> Result<()>; + + /// Set the first index to look for available descriptors. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `num` - the first index to look for available descriptors. + fn set_vring_base(&mut self, queue_index: usize, num: u16) -> Result<()>; + + /// Set the eventfd to trigger when buffers need to be processed + /// by the guest. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `fd` - The files descriptor used to notify the guest. + fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; + + /// Set the eventfd that will be signaled by the guest when buffers + /// need to be processed by the host. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `fd` - The files descriptor used to notify the host. + fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; + + /// set the status of virtio queue. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `status` - The status of virtio queue. + fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()>; +} + +/// The vhost-user server handler can communicate with StratoVirt and set the data of requests +/// to the backend. +#[derive(Clone)] +pub struct VhostUserServerHandler { + /// The information of socket used to communicate with StratoVirt. + pub sock: VhostUserSock, + /// The backend used to save the data of requests from StratoVirt. + backend: Arc>, +} + +fn close_fds(fds: Vec) { + for fd in fds { + let _ = unsafe { libc::close(fd) }; + } +} + +fn is_invalid_fds(hdr: &mut VhostUserMsgHdr, rfds: Option>) -> Result<()> { + match VhostUserMsgReq::from(hdr.request) { + VhostUserMsgReq::SetMemTable => Ok(()), + VhostUserMsgReq::SetVringCall => Ok(()), + VhostUserMsgReq::SetVringKick => Ok(()), + VhostUserMsgReq::SetSlaveReqFd => Ok(()), + _ => { + if rfds.is_some() { + if let Some(fds) = rfds { + close_fds(fds); + } + bail!("The fds is invalid, request: {}", hdr.request); + } else { + Ok(()) + } + } + } +} + +impl VhostUserServerHandler { + /// Construct a vhost-user server handler + /// + /// # Arguments + /// + /// * `path` - The path of unix socket file which communicates with StratoVirt. + /// * `backend` - The trait of backend used to save the data of requests from StratoVirt. + pub fn new(path: &str, backend: Arc>) -> Result { + let mut sock = VhostUserSock::new(path); + sock.domain + .bind(true) + .chain_err(|| format!("Failed to bind for vhost user server {}", path))?; + limit_permission(path).chain_err(|| format!("Failed to limit permission {}", path))?; + + Ok(VhostUserServerHandler { + sock, + backend, + }) + } + + fn recv_hdr_and_fds(&mut self) -> Result<(VhostUserMsgHdr, Option>)> { + let mut hdr = VhostUserMsgHdr::default(); + let body_opt: Option<&mut u32> = None; + let payload_opt: Option<&mut [u8]> = None; + let mut fds = vec![0; MAX_ATTACHED_FD_ENTRIES]; + + let (rcv_len, fds_num) = self + .sock + .recv_msg(Some(&mut hdr), body_opt, payload_opt, &mut fds) + .chain_err(|| "Failed to recv hdr and fds")?; + + if rcv_len != size_of::() { + bail!( + "The received length {} is invalid, expect {}", + rcv_len, + size_of::() + ); + } else if hdr.is_invalid() { + bail!( + "The header of vhost user msg is invalid, request: {}, size: {}, flags: {}", + hdr.request, + hdr.size, + hdr.flags + ); + } + + let rfds = match fds_num { + 0 => None, + n => { + let mut fds_temp = Vec::with_capacity(n); + fds_temp.extend_from_slice(&fds[0..n]); + Some(fds_temp) + } + }; + + is_invalid_fds(&mut hdr, rfds.clone())?; + + Ok((hdr, rfds)) + } + + fn recv_body(&mut self, len: usize) -> Result<(usize, Vec)> { + let mut rbuf = vec![0u8; len]; + let body_opt: Option<&mut u32> = None; + let hdr_opt: Option<&mut VhostUserMsgHdr> = None; + + let (rcv_len, _) = self + .sock + .recv_msg(hdr_opt, body_opt, Some(&mut rbuf), &mut []) + .chain_err(|| "Failed to recv msg body")?; + + if rcv_len != len { + bail!("The length of msg body {} is invalid, expected {}", rcv_len, len); + } + + Ok((rcv_len, rbuf)) + } + + fn get_msg_body<'a, D: Sized>( + &self, + hdr: &VhostUserMsgHdr, + buf: &'a [u8], + len: usize, + ) -> Result<&'a D> { + if size_of::() != len { + bail!( + "Failed to get msg body for request {}, len {}, payload size {}", + len, + size_of::() + ); + } + + let body = unsafe { &*(buf.as_ptr() as *const D) }; + Ok(body) + } + + fn send_ack_msg(&mut self, request: u32, res: D, fds: &[RawFd]) -> Result<()> { + let hdr = VhostUserMsgHdr::new( + request, + VhostUserHdrFlag::Reply as u32, + size_of::() as u32, + ); + let payload_opt: Option<&[u8]> = None; + + self.sock + .send_msg(Some(&hdr), Some(&res), payload_opt, fds) + .chain_err(|| "Failed to send ack msg")?; + + Ok(()) + } + + #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] + fn set_msg_mem_table( + &mut self, + hdr: &VhostUserMsgHdr, + buf: &[u8], + len: usize, + fds_opt: Option>, + ) -> Result<()> { + if len < size_of::() { + if let Some(fds) = fds_opt { + close_fds(fds); + } + bail!( + "The header length of mem table is invalid {}", + len, + ); + } + + let memhdrsize = size_of::(); + let memhdr = unsafe { &*(buf.as_ptr() as *const VhostUserMemHdr) }; + let total_size = + (memhdr.nregions as usize * size_of::()) + memhdrsize; + if (hdr.size as usize) != total_size { + if let Some(fds) = fds_opt { + close_fds(fds); + } + bail!( + "The body length of mem table is invalid {}, expected {}", + total_size, + hdr.size, + ); + } + + let regions = unsafe { + slice::from_raw_parts( + buf.as_ptr().add(memhdrsize) as *const RegionMemInfo, + memhdr.nregions as usize, + ) + }; + + if let Some(fds) = fds_opt { + let fds_len = fds.len(); + if fds_len != (memhdr.nregions as usize) { + close_fds(fds); + bail!( + "The length of fds {} for mem table is invalid, expected {}", + fds_len, + memhdr.nregions + ); + } + self.backend.lock().unwrap().set_mem_table(regions, &fds)?; + } else { + bail!("The fds of mem table is null"); + } + + Ok(()) + } + + fn process_request( + &mut self, + hdr: &VhostUserMsgHdr, + buf: &[u8], + len: usize, + rfds: Option>, + ) -> Result<()> { + match VhostUserMsgReq::from(hdr.request) { + VhostUserMsgReq::GetFeatures => { + if len != 0 { + bail!("The length {} of getting features is invalid", len); + } + + let features = self.backend.lock().unwrap().get_features()?; + if hdr.need_reply() { + self.send_ack_msg(VhostUserMsgReq::GetFeatures as u32, features, &[]) + .chain_err(|| "Failed to send ack msg for getting features")?; + } + } + VhostUserMsgReq::SetFeatures => { + let features = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting features")?; + self.backend.lock().unwrap().set_features(*features)?; + } + VhostUserMsgReq::SetOwner => { + if len != 0 { + bail!("The length {} of setting owner is invalid", len); + } + self.backend.lock().unwrap().set_owner()?; + } + VhostUserMsgReq::SetMemTable => { + let ret = match self.set_msg_mem_table(&hdr, buf, len, rfds) { + Err(ref e) => { + error!( + "Failed to set mem table {}", + error_chain::ChainedError::display_chain(e) + ); + 1u64 + } + Ok(_) => 0u64, + }; + if hdr.need_reply() { + self.send_ack_msg(VhostUserMsgReq::SetMemTable as u32, ret, &[]) + .chain_err(|| "Failed to send ack msg for setting mem table")?; + } + } + VhostUserMsgReq::SetVringNum => { + let vringstate = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring num")?; + self.backend + .lock() + .unwrap() + .set_vring_num(vringstate.index as usize, vringstate.value as u16)?; + } + VhostUserMsgReq::SetVringAddr => { + let vringaddr = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring addr")?; + self.backend.lock().unwrap().set_vring_addr( + vringaddr.index as usize, + vringaddr.flags, + vringaddr.desc_user_addr, + vringaddr.used_user_addr, + vringaddr.avail_user_addr, + vringaddr.log_guest_addr, + )?; + } + VhostUserMsgReq::SetVringBase => { + let vringstate = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring base")?; + self.backend + .lock() + .unwrap() + .set_vring_base(vringstate.index as usize, vringstate.value as u16)?; + } + VhostUserMsgReq::SetVringEnable => { + let vringstate = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring enable")?; + self.backend + .lock() + .unwrap() + .set_vring_enable(vringstate.index as usize, vringstate.value)?; + } + VhostUserMsgReq::SetVringKick => { + let index = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring kick")?; + if let Some(fds) = rfds { + let fds_len = fds.len(); + if fds_len != 1 { + close_fds(fds); + bail!("The length {} of fds for kicking is invalid", fds_len); + } + self.backend + .lock() + .unwrap() + .set_vring_kick(*index as usize, fds[0])?; + } else { + bail!("The length of fds for kicking is null"); + } + } + VhostUserMsgReq::SetVringCall => { + let index = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring call")?; + if let Some(fds) = rfds { + let fds_len = fds.len(); + if fds_len != 1 { + close_fds(fds); + bail!("The length {} of fds for calling is invalid", fds_len); + } + self.backend + .lock() + .unwrap() + .set_vring_call(*index as usize, fds[0])?; + } else { + bail!("The length of fds for calling is null"); + } + } + _ => { + bail!("The request {} is unknown", hdr.request); + } + }; + + Ok(()) + } + + /// The function used to process requests from StratoVirt. + pub fn handle_request(&mut self) -> Result<()> { + let (hdr, rfds) = self + .recv_hdr_and_fds() + .chain_err(|| "Failed to recv header and fds")?; + + let (len, buf) = match hdr.size { + 0 => (0, vec![0u8; 0]), + _ => { + let (rcv_len, rbuf) = self + .recv_body(hdr.size as usize) + .chain_err(|| "Failed to recv msg body")?; + (rcv_len, rbuf) + } + }; + + self.process_request(&hdr, &buf, len, rfds) + .chain_err(|| format!("Failed to process the request {}", hdr.request))?; + Ok(()) + } +} diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs new file mode 100644 index 000000000..1a083fbd2 --- /dev/null +++ b/vhost_user_fs/src/virtio_fs.rs @@ -0,0 +1,429 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// The num of high priority queue. +const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; +/// The num of request queue. +const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; +/// The next queue size. +const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; + +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::sync::{Arc, Mutex}; + +use error_chain::ChainedError; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; +use machine_manager::event_loop::EventLoop; +use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; + +use super::fs::FileSystem; +use super::fuse_req::FuseReq; +use super::vhost_user_server::VhostUserReqHandler; + +use crate::errors::{Result, ResultExt}; +use virtio::vhost::user::RegionMemInfo; +use virtio::{ + Queue, QueueConfig, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, +}; + +struct FsIoHandler { + queue: Queue, + kick_evt: EventFd, + call_evt: EventFd, + mem_space: Arc, + driver_features: u64, + fs: Arc>, +} + +impl FsIoHandler { + fn new( + queue_config: QueueConfig, + kick_evt: &EventFd, + call_evt: &EventFd, + mem_space: &Arc, + driver_features: u64, + fs: Arc>, + ) -> Result { + let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING) + .chain_err(|| "Failed to create virtual queue")?; + if !queue.is_valid(mem_space) { + bail!("Invalid queue for fs handler"); + } + + Ok(FsIoHandler { + queue, + kick_evt: kick_evt.try_clone().unwrap(), + call_evt: call_evt.try_clone().unwrap(), + mem_space: mem_space.clone(), + driver_features, + fs, + }) + } + + fn process_queue(&mut self) -> Result<()> { + while let Ok(elem) = self + .queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + { + let mut req = FuseReq::new(&elem); + let (index, len) = req.execute(&self.mem_space, self.fs.clone()); + self.queue.vring.add_used(&self.mem_space, index, len)?; + } + + if self + .queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + self.call_evt + .write(1) + .chain_err(|| "Failed to write call fd")?; + } + + Ok(()) + } + + fn delete_notifiers(&self) -> Vec { + let mut notifiers = Vec::new(); + notifiers.push(EventNotifier::new( + NotifierOperation::Delete, + self.kick_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + )); + + notifiers + } +} + +impl EventNotifierHelper for FsIoHandler { + fn internal_notifiers(fs_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let fs_handler_clone = fs_handler.clone(); + let handler = Box::new(move |_, fd: RawFd| { + read_fd(fd); + + if let Err(e) = fs_handler_clone.lock().unwrap().process_queue() { + error!("Failed to process fuse msg, {}", e.display_chain()); + } + + None + }); + + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + fs_handler.lock().unwrap().kick_evt.as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + notifiers + } +} + +struct QueueInfo { + config: QueueConfig, + kick_evt: Option, + call_evt: Option, +} + +impl QueueInfo { + fn new(queue_size: u16) -> Self { + QueueInfo { + config: QueueConfig::new(queue_size), + kick_evt: None, + call_evt: None, + } + } +} + +struct VirtioFsConfig { + device_features: u64, + driver_features: u64, + queues_info: Vec, + mem_regions: Vec, +} + +impl VirtioFsConfig { + fn new() -> Self { + let device_features = 1_u64 << VIRTIO_F_VERSION_1 + | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC + | 1_u64 << VIRTIO_F_RING_EVENT_IDX; + + let mut queues_info = Vec::new(); + for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { + queues_info.push(QueueInfo::new(VIRTIO_FS_MAX_QUEUE_SIZE)); + } + + VirtioFsConfig { + device_features, + driver_features: 0_u64, + queues_info, + mem_regions: Vec::new(), + } + } + + fn get_mut_queue_config(&mut self, queue_index: usize) -> Result<&mut QueueInfo> { + self.queues_info + .get_mut(queue_index) + .ok_or_else(|| format!("The select index of queue {} overflows", queue_index).into()) + } +} + +/// The virtio fs device contains the configuration of virtio fs, the management of +/// userspace filesystem and the handler used to process requests in virtio queue +/// from the guest. +pub struct VirtioFs { + /// The config of virtio-fs. + config: VirtioFsConfig, + /// Fs handlers of I/O request. + fs_handlers: Vec>>>, + /// Address space mapped witch StratoVirt. + sys_mem: Arc, + /// File system used to store inode and file information. + fs: Arc>, + /// The quest memory region information. + mem_info: Vec, +} + +impl VirtioFs { + /// Construct a virtio fs device by the path of source directory shared in host. + /// + /// # Arguments + /// + /// * `source_dir` - The path of source directory shared in host. + pub fn new(source_dir: &str) -> Result { + let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) + .chain_err(|| "Failed to create address space")?; + + let mut fs_handlers = Vec::new(); + for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { + fs_handlers.push(None); + } + + let fs = Arc::new(Mutex::new(FileSystem::new(source_dir).chain_err(|| { + format!("Failed to create file system, source dir: {}", source_dir) + })?)); + + Ok(VirtioFs { + config: VirtioFsConfig::new(), + fs_handlers, + sys_mem, + fs, + mem_info: Vec::new(), + }) + } + + fn get_guest_address(&self, addr: u64) -> Result { + for (_, info) in self.mem_info.iter().enumerate() { + if addr >= info.userspace_addr + && addr < info.userspace_addr + info.memory_size { + return Ok(info.guest_phys_addr + addr - info.userspace_addr); + } + } + + bail!("Failed to find the guest address for addr: 0x{:X}", addr); + } +} + +impl VhostUserReqHandler for VirtioFs { + fn set_owner(&mut self) -> Result<()> { + Ok(()) + } + + fn get_features(&self) -> Result { + Ok(self.config.device_features) + } + + fn set_features(&mut self, features: u64) -> Result<()> { + self.config.driver_features = features; + Ok(()) + } + + fn set_mem_table(&mut self, regions: &[RegionMemInfo], fds: &[RawFd]) -> Result<()> { + if !self.config.mem_regions.is_empty() { + for region in &self.config.mem_regions { + if let Err(e) = self.sys_mem.root().delete_subregion(region) { + error!( + "Failed to delete subregion for setting mem table, {}", + e.display_chain() + ); + } + } + self.config.mem_regions = Vec::new(); + } + + self.mem_info = regions.to_vec().clone(); + + for (index, region_config) in regions.iter().enumerate() { + let file = unsafe { File::from_raw_fd(fds[index]) }; + let fileback = FileBackend { + file: Arc::new(file), + offset: region_config.mmap_offset, + page_size: 0_u64, + }; + + let mmap = Arc::new( + HostMemMapping::new( + GuestAddress(region_config.guest_phys_addr), + None, + region_config.memory_size, + Some(fileback), + false, + true, + false, + ) + .chain_err(|| + format!("Failed to create the mapping of host memory for setting mem table, addr: 0x{:X}, size: {}, offset: {}", + region_config.guest_phys_addr, region_config.memory_size, region_config.mmap_offset, + ) + )? + ); + + let region = Region::init_ram_region(mmap.clone()); + self.sys_mem + .root() + .add_subregion(region.clone(), mmap.start_address().raw_value()) + .chain_err(|| "Failed to add subregion for setting mem table")?; + + self.config.mem_regions.push(region); + } + + Ok(()) + } + + fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()> { + self.config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + queue_info.config.size = num; + }) + .chain_err(|| { + format!( + "Failed to set vring num, index: {}, num: {}", + queue_index, num, + ) + })?; + Ok(()) + } + + fn set_vring_addr( + &mut self, + queue_index: usize, + _flags: u32, + desc_table: u64, + used_ring: u64, + avail_ring: u64, + _log: u64, + ) -> Result<()> { + let cloned_mem_space = self.sys_mem.clone(); + + let desc_addr = self.get_guest_address(desc_table)?; + let used_addr = self.get_guest_address(used_ring)?; + let avail_addr = self.get_guest_address(avail_ring)?; + + self.config + .get_mut_queue_config(queue_index as usize) + .map(|queue_info| { + queue_info.config.desc_table = GuestAddress(desc_addr); + queue_info.config.addr_cache.desc_table_host = cloned_mem_space + .get_host_address(GuestAddress(desc_addr)) + .unwrap_or(0); + + queue_info.config.avail_ring = GuestAddress(avail_addr); + queue_info.config.addr_cache.avail_ring_host = cloned_mem_space + .get_host_address(GuestAddress(avail_addr)) + .unwrap_or(0); + + queue_info.config.used_ring = GuestAddress(used_addr); + queue_info.config.addr_cache.used_ring_host = cloned_mem_space + .get_host_address(GuestAddress(used_addr)) + .unwrap_or(0); + }).chain_err(|| + format!("Failed to set vring addr, index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", + queue_index, desc_addr, avail_addr, used_addr, + ) + )?; + Ok(()) + } + + fn set_vring_base(&mut self, _queue_index: usize, _num: u16) -> Result<()> { + Ok(()) + } + + fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + self.config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + let call_evt = unsafe { EventFd::from_raw_fd(fd) }; + queue_info.call_evt = Some(call_evt); + }) + .chain_err(|| format!("Failed to set vring call, index: {}", queue_index))?; + Ok(()) + } + + fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + self.config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + let kick_evt = unsafe { EventFd::from_raw_fd(fd) }; + queue_info.kick_evt = Some(kick_evt); + }) + .chain_err(|| format!("Failed to set vring kick, index: {}", queue_index))?; + Ok(()) + } + + fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()> { + let driver_features = self.config.driver_features; + + let mut queue_info = self.config.get_mut_queue_config(queue_index)?; + queue_info.config.ready = status == 1; + + if status == 1 { + if queue_info.kick_evt.is_none() || queue_info.call_evt.is_none() { + bail!( + "The event for kicking {} or calling {} is none", + queue_info.kick_evt.is_none(), + queue_info.call_evt.is_none(), + ); + } + + let fs_handler = Arc::new(Mutex::new( + FsIoHandler::new( + queue_info.config, + queue_info.kick_evt.as_ref().unwrap(), + queue_info.call_evt.as_ref().unwrap(), + &self.sys_mem, + driver_features, + self.fs.clone(), + ) + .chain_err(|| "Failed to create fs handler")?, + )); + + self.fs_handlers[queue_index] = Some(fs_handler.clone()); + EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) + .chain_err(|| "Failed to update event for queue status which is ready")?; + } else if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { + EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) + .chain_err(|| "Failed to update event for queue status which is not ready")?; + } + Ok(()) + } +} -- Gitee From 4f86d81675e82293bae964f96479924ff68e049c Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Tue, 27 Sep 2022 17:42:44 +0800 Subject: [PATCH 0191/2187] virtiofs: complete main.rs complete main.rs in vhost_user_fs package fix format problems Signed-off-by: Fei Xu Signed-off-by: Xinle Guo Signed-off-by: YuJun Huang --- machine/src/lib.rs | 42 +++++++++++++++++++++++-- vhost_user_fs/src/fs.rs | 10 +++--- vhost_user_fs/src/fuse_msg.rs | 5 ++- vhost_user_fs/src/lib.rs | 12 ++++++- vhost_user_fs/src/main.rs | 36 +++++++++++++++++++-- vhost_user_fs/src/vhost_user_fs.rs | 7 ++++- vhost_user_fs/src/vhost_user_server.rs | 40 ++++++++++++------------ vhost_user_fs/src/virtio_fs.rs | 12 +++---- virtio/src/vhost/user/message.rs | 43 +++++++++++++++++++++++++- 9 files changed, 162 insertions(+), 45 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a8fd5b248..2ae995958 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -126,7 +126,7 @@ use hypervisor::kvm::KVM_FDS; use machine_manager::config::parse_gpu; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, + parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, @@ -154,8 +154,8 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, Balloon, Block, BlockState, Console, Rng, RngState, VhostKern, VhostUser, - VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, + balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, VhostKern, + VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; @@ -503,6 +503,39 @@ pub trait MachineOps { bail!("No pci host found"); } + /// Add virtioFs device. + /// + /// # Arguments + /// + /// * 'vm_config' - VM configuration. + /// * 'cfg_args' - Device configuration arguments. + fn add_virtio_fs(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let dev_cfg = parse_fs(vm_config, cfg_args)?; + let id_clone = dev_cfg.id.clone(); + let sys_mem = self.get_sys_mem().clone(); + let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); + if cfg_args.contains("vhost-user-fs-device") { + let device = VirtioMmioDevice::new(&sys_mem, device); + self.realize_virtio_mmio_device(device) + .chain_err(|| "Failed to add vhost user fs device")?; + } else if cfg_args.contains("vhost-user-fs-pci") { + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + + let mut vitio_pci_device = + VirtioPciDevice::new(id_clone, devfn, sys_mem, device, parent_bus, multi_func); + vitio_pci_device.enable_need_irqfd(); + vitio_pci_device + .realize() + .chain_err(|| "Failed to add pci fs device")?; + } else { + bail!("error device type"); + } + + Ok(()) + } + fn get_sys_bus(&mut self) -> &SysBus; fn get_fwcfg_dev(&mut self) -> Result>> { @@ -1101,6 +1134,9 @@ pub trait MachineOps { "vhost-user-blk-pci" => { self.add_vhost_user_blk_pci(vm_config, cfg_args)?; } + "vhost-user-fs-pci" | "vhost-user-fs-device" => { + self.add_virtio_fs(vm_config, cfg_args)?; + } "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index ca9c589b1..1b618176d 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -123,7 +123,7 @@ impl Map { fn get_value(&self, id: usize) -> Option<&T> { if let Some(e) = self.list.get(id) { - e.value.as_ref().map(|v| v) + e.value.as_ref() } else { None } @@ -131,7 +131,7 @@ impl Map { fn get_value_mut(&mut self, id: usize) -> Option<&mut T> { if let Some(e) = self.list.get_mut(id) { - e.value.as_mut().map(|v| v) + e.value.as_mut() } else { None } @@ -562,7 +562,7 @@ impl FileSystem { if attr.valid & FATTR_FH != 0 { match self.file_map.get_value(attr.fh as usize) { Some(file) => { - let ret = fchmod(&file, attr.mode); + let ret = fchmod(file, attr.mode); if ret != FUSE_OK { return ret; } @@ -626,7 +626,7 @@ impl FileSystem { if attr.valid & FATTR_FH != 0 { match self.file_map.get_value(attr.fh as usize) { Some(file) => { - let ret = ftruncate(&file, attr.size); + let ret = ftruncate(file, attr.size); if ret != FUSE_OK { return ret; } @@ -686,7 +686,7 @@ impl FileSystem { if attr.valid & FATTR_FH != 0 { match self.file_map.get_value(attr.fh as usize) { Some(file) => { - let ret = futimens(&file, a_sec, a_nsec, m_sec, m_nsec); + let ret = futimens(file, a_sec, a_nsec, m_sec, m_nsec); if ret != FUSE_OK { return ret; } diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 30bd93d4c..9d9830f05 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -145,9 +145,8 @@ use std::ffi::CString; use std::mem::size_of; use std::sync::Arc; -use error_chain::ChainedError; - use address_space::AddressSpace; +use error_chain::ChainedError; use util::byte_code::ByteCode; use crate::errors::{Result, ResultExt}; @@ -302,7 +301,7 @@ impl FuseBuffer { /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn read_obj(&mut self, sys_mem: &Arc) -> Result { let mut obj = T::default(); - self.read_slice(sys_mem, &mut obj.as_mut_bytes(), size_of::())?; + self.read_slice(sys_mem, obj.as_mut_bytes(), size_of::())?; Ok(obj) } diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index ed7658d08..005eaff7c 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -12,13 +12,23 @@ #[macro_use] extern crate error_chain; - +#[macro_use] +extern crate log; extern crate address_space; extern crate machine_manager; extern crate util; extern crate virtio; pub mod cmdline; +pub mod fs; +pub mod fs_ops; +pub mod fuse_msg; +pub mod fuse_proc; +pub mod fuse_req; +pub mod vhost_user_fs; +pub mod vhost_user_server; +pub mod virtio_fs; + pub mod errors { error_chain! { links { diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 97464e0e2..637213f00 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -15,9 +15,13 @@ extern crate error_chain; #[macro_use] extern crate log; extern crate vhost_user_fs; +use machine_manager::event_loop::EventLoop; use std::os::unix::fs::OpenOptionsExt; -use util::logger; -use vhost_user_fs::cmdline::create_args_parser; +use std::sync::{Arc, Mutex}; +use util::{arg_parser, logger}; +use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; +use vhost_user_fs::vhost_user_fs::VhostUserFs; + error_chain! { links { VhostUserFs(vhost_user_fs::errors::Error, vhost_user_fs::errors::ErrorKind); @@ -37,6 +41,34 @@ fn run() -> Result<()> { init_log(logfile_path)?; } set_panic_hook(); + match real_main(&cmd_args) { + Ok(()) => info!("EventLoop over, Vm exit"), + Err(ref e) => { + error!("{}", error_chain::ChainedError::display_chain(e)); + } + } + + Ok(()) +} + +fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { + let fsconfig: FsConfig = create_fs_config(cmd_args)?; + info!("FsConfig is {:?}", fsconfig); + + EventLoop::object_init(&None)?; + + let vhost_user_fs = Arc::new(Mutex::new( + VhostUserFs::new(fsconfig).chain_err(|| "Failed to create vhost use fs")?, + )); + EventLoop::set_manager(vhost_user_fs.clone(), None); + + vhost_user_fs + .lock() + .unwrap() + .add_event_notifier() + .chain_err(|| "Failed to add event")?; + + EventLoop::loop_run().chain_err(|| "EventLoop exits unexpectedly: error occurs")?; Ok(()) } diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index dbb741e23..eff69c188 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -116,7 +116,12 @@ impl EventNotifierHelper for VhostUserServerHandler { let notifier = EventNotifier::new( NotifierOperation::AddShared, - server_handler.lock().unwrap().sock.domain.get_listener_raw_fd(), + server_handler + .lock() + .unwrap() + .sock + .domain + .get_listener_raw_fd(), None, EventSet::IN, handlers, diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index ccd85b054..959f65870 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -16,8 +16,8 @@ use std::slice; use std::sync::{Arc, Mutex}; use util::unix::limit_permission; use virtio::vhost::user::{ - VhostUserHdrFlag, VhostUserMemHdr, RegionMemInfo, - VhostUserMsgHdr, VhostUserMsgReq, VhostUserVringAddr, VhostUserVringState, MAX_ATTACHED_FD_ENTRIES, + RegionMemInfo, VhostUserHdrFlag, VhostUserMemHdr, VhostUserMsgHdr, VhostUserMsgReq, + VhostUserVringAddr, VhostUserVringState, MAX_ATTACHED_FD_ENTRIES, }; use virtio::VhostUser::VhostUserSock; @@ -159,10 +159,7 @@ impl VhostUserServerHandler { .chain_err(|| format!("Failed to bind for vhost user server {}", path))?; limit_permission(path).chain_err(|| format!("Failed to limit permission {}", path))?; - Ok(VhostUserServerHandler { - sock, - backend, - }) + Ok(VhostUserServerHandler { sock, backend }) } fn recv_hdr_and_fds(&mut self) -> Result<(VhostUserMsgHdr, Option>)> { @@ -216,7 +213,11 @@ impl VhostUserServerHandler { .chain_err(|| "Failed to recv msg body")?; if rcv_len != len { - bail!("The length of msg body {} is invalid, expected {}", rcv_len, len); + bail!( + "The length of msg body {} is invalid, expected {}", + rcv_len, + len + ); } Ok((rcv_len, rbuf)) @@ -231,6 +232,7 @@ impl VhostUserServerHandler { if size_of::() != len { bail!( "Failed to get msg body for request {}, len {}, payload size {}", + hdr.request, len, size_of::() ); @@ -267,16 +269,12 @@ impl VhostUserServerHandler { if let Some(fds) = fds_opt { close_fds(fds); } - bail!( - "The header length of mem table is invalid {}", - len, - ); + bail!("The header length of mem table is invalid {}", len); } let memhdrsize = size_of::(); let memhdr = unsafe { &*(buf.as_ptr() as *const VhostUserMemHdr) }; - let total_size = - (memhdr.nregions as usize * size_of::()) + memhdrsize; + let total_size = (memhdr.nregions as usize * size_of::()) + memhdrsize; if (hdr.size as usize) != total_size { if let Some(fds) = fds_opt { close_fds(fds); @@ -334,7 +332,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetFeatures => { let features = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting features")?; self.backend.lock().unwrap().set_features(*features)?; } @@ -345,7 +343,7 @@ impl VhostUserServerHandler { self.backend.lock().unwrap().set_owner()?; } VhostUserMsgReq::SetMemTable => { - let ret = match self.set_msg_mem_table(&hdr, buf, len, rfds) { + let ret = match self.set_msg_mem_table(hdr, buf, len, rfds) { Err(ref e) => { error!( "Failed to set mem table {}", @@ -362,7 +360,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetVringNum => { let vringstate = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring num")?; self.backend .lock() @@ -371,7 +369,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetVringAddr => { let vringaddr = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring addr")?; self.backend.lock().unwrap().set_vring_addr( vringaddr.index as usize, @@ -384,7 +382,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetVringBase => { let vringstate = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring base")?; self.backend .lock() @@ -393,7 +391,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetVringEnable => { let vringstate = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring enable")?; self.backend .lock() @@ -402,7 +400,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetVringKick => { let index = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring kick")?; if let Some(fds) = rfds { let fds_len = fds.len(); @@ -420,7 +418,7 @@ impl VhostUserServerHandler { } VhostUserMsgReq::SetVringCall => { let index = self - .get_msg_body::(&hdr, buf, len) + .get_msg_body::(hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring call")?; if let Some(fds) = rfds { let fds_len = fds.len(); diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 1a083fbd2..551f7028f 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -98,16 +98,13 @@ impl FsIoHandler { } fn delete_notifiers(&self) -> Vec { - let mut notifiers = Vec::new(); - notifiers.push(EventNotifier::new( + vec![EventNotifier::new( NotifierOperation::Delete, self.kick_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), - )); - - notifiers + )] } } @@ -233,8 +230,7 @@ impl VirtioFs { fn get_guest_address(&self, addr: u64) -> Result { for (_, info) in self.mem_info.iter().enumerate() { - if addr >= info.userspace_addr - && addr < info.userspace_addr + info.memory_size { + if addr >= info.userspace_addr && addr < info.userspace_addr + info.memory_size { return Ok(info.guest_phys_addr + addr - info.userspace_addr); } } @@ -270,7 +266,7 @@ impl VhostUserReqHandler for VirtioFs { self.config.mem_regions = Vec::new(); } - self.mem_info = regions.to_vec().clone(); + self.mem_info = regions.to_vec(); for (index, region_config) in regions.iter().enumerate() { let file = unsafe { File::from_raw_fd(fds[index]) }; diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 34d85c1cb..1b4d66b2f 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -63,6 +63,47 @@ pub enum VhostUserMsgReq { MaxCmd = 33, } +impl From for VhostUserMsgReq { + fn from(t: u32) -> Self { + match t { + 0 => VhostUserMsgReq::None, + 1 => VhostUserMsgReq::GetFeatures, + 2 => VhostUserMsgReq::SetFeatures, + 3 => VhostUserMsgReq::SetOwner, + 4 => VhostUserMsgReq::ResetOwner, + 5 => VhostUserMsgReq::SetMemTable, + 6 => VhostUserMsgReq::SetLogBase, + 7 => VhostUserMsgReq::SetLogFd, + 8 => VhostUserMsgReq::SetVringNum, + 9 => VhostUserMsgReq::SetVringAddr, + 10 => VhostUserMsgReq::SetVringBase, + 11 => VhostUserMsgReq::GetVringBase, + 12 => VhostUserMsgReq::SetVringKick, + 13 => VhostUserMsgReq::SetVringCall, + 14 => VhostUserMsgReq::SetVringErr, + 15 => VhostUserMsgReq::GetProtocolFeatures, + 16 => VhostUserMsgReq::SetProtocolFeatures, + 17 => VhostUserMsgReq::GetQueueNum, + 18 => VhostUserMsgReq::SetVringEnable, + 19 => VhostUserMsgReq::SendRarp, + 20 => VhostUserMsgReq::NetSetMtu, + 21 => VhostUserMsgReq::SetSlaveReqFd, + 22 => VhostUserMsgReq::IotlbMsg, + 23 => VhostUserMsgReq::SetVringEndian, + 24 => VhostUserMsgReq::GetConfig, + 25 => VhostUserMsgReq::SetConfig, + 26 => VhostUserMsgReq::CreateCryptoSession, + 27 => VhostUserMsgReq::CloseCryptoSession, + 28 => VhostUserMsgReq::PostcopyAdvise, + 29 => VhostUserMsgReq::PostcopyListen, + 30 => VhostUserMsgReq::PostcopyEnd, + 31 => VhostUserMsgReq::GetInflightFd, + 32 => VhostUserMsgReq::SetInflightFd, + _ => VhostUserMsgReq::MaxCmd, + } + } +} + /// The meaning of flag bits for header of vhost user message. pub enum VhostUserHdrFlag { /// Bits[0..1] is message version number. @@ -107,7 +148,7 @@ impl VhostUserMsgHdr { } /// Check whether reply for this message is requested. - fn need_reply(&self) -> bool { + pub fn need_reply(&self) -> bool { (self.flags & VhostUserHdrFlag::NeedReply as u32) != 0 } -- Gitee From aa9329af3133ea9e4c8739b32240c13b647383fc Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 9 Oct 2022 19:28:11 +0800 Subject: [PATCH 0192/2187] Fix: don't add srat table when NUMA is not configured If NUMA node is not configured, an empty srat is added to the acpi table. This operation is unnecessary. Thereforce, the determination of whether to configure NUMA is placed outside build_srat_table. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 4 ---- machine/src/standard_vm/mod.rs | 10 +++++----- machine/src/standard_vm/x86_64/mod.rs | 4 ---- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 7f55f6d54..412c9813c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -857,10 +857,6 @@ impl AcpiBuilder for StdMachine { loader: &mut TableLoader, ) -> super::errors::Result { use super::errors::ResultExt; - if self.numa_nodes.is_none() { - return Ok(0); - } - let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); // Reserved srat.append_child(&[1_u8; 4_usize]); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 8c719f5cc..49c162e77 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -159,12 +159,12 @@ trait StdMachineOps: AcpiBuilder { .chain_err(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); - let srat_addr = self - .build_srat_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI SRAT table")?; - xsdt_entries.push(srat_addr); - if let Some(numa_nodes) = self.get_numa_nodes() { + let srat_addr = self + .build_srat_table(&acpi_tables, &mut loader) + .chain_err(|| "Failed to build ACPI SRAT table")?; + xsdt_entries.push(srat_addr); + let slit_addr = Self::build_slit_table(numa_nodes, &acpi_tables, &mut loader) .chain_err(|| "Failed to build ACPI SLIT table")?; xsdt_entries.push(slit_addr); diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index c8f85ab80..7dd9aa1cf 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -812,10 +812,6 @@ impl AcpiBuilder for StdMachine { loader: &mut TableLoader, ) -> super::errors::Result { use super::errors::ResultExt; - if self.numa_nodes.is_none() { - return Ok(0); - } - let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); srat.append_child(&[1_u8; 4_usize]); srat.append_child(&[0_u8; 8_usize]); -- Gitee From 27eed7441e340320612cac430207ec5cf535d7c2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Oct 2022 05:17:12 -0400 Subject: [PATCH 0193/2187] virtio-blk: Remove feature VIRTIO_BLK_F_SIZE_MAX In fact, virtio-blk don't has the restriction for segment size, so this feature is useless. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index fff028d0e..93838aa37 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -45,9 +45,9 @@ use super::errors::{ErrorKind, Result, ResultExt}; use super::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, - VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_BLOCK, }; /// Number of virtqueues. @@ -976,7 +976,6 @@ impl VirtioDevice for Block { self.state.device_features |= 1_u64 << VIRTIO_BLK_F_RO; }; self.state.device_features |= 1_u64 << VIRTIO_F_RING_INDIRECT_DESC; - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_SIZE_MAX; self.state.device_features |= 1_u64 << VIRTIO_BLK_F_SEG_MAX; self.state.device_features |= 1_u64 << VIRTIO_F_RING_EVENT_IDX; -- Gitee From 96227ad7863f36a7412b7551636072f6dd00ec3f Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 10 Oct 2022 10:03:32 +0800 Subject: [PATCH 0194/2187] Fix: when the vcpu stopped, don't kick it After the VM is shut down, one of the vcpus exits the user mode and this vcpu status changes to stopped. First, in the vcpu.destroy function, this vcpu wastes 32 ms waiting time. Second, this vcpu sends a signal to itself and may end the vcpu thread advance. As a result, other vcpu do not perform the kick operation and the VM is suspended. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 21a51a6a3..6fcf9be62 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -420,8 +420,9 @@ impl CPUInterface for CPU { let (cpu_state, cvar) = &*self.state; if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { *cpu_state.lock().unwrap() = CpuLifecycleState::Stopping; - } else { - *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; + } else if *cpu_state.lock().unwrap() == CpuLifecycleState::Stopped { + *cpu_state.lock().unwrap() = CpuLifecycleState::Nothing; + return Ok(()); } self.kick()?; -- Gitee From ecd42b68a8c16f0c73747a5a7b9427cef8112431 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 11 Oct 2022 14:30:29 +0800 Subject: [PATCH 0195/2187] Memory: Add verification content to the `delete_subregion` func Only Container Region has sub regions. So, before delete target subregion, it needs to judge whether it is Container-type Region. Signed-off-by: Xinle.Guo Signed-off-by: Jie Yang --- address_space/src/region.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 91d05c197..b08ff480d 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -639,6 +639,11 @@ impl Region { /// * The child-region does not exist in sub-regions array. /// * Failed to generate flat view (topology changed after removing sub-region). pub fn delete_subregion(&self, child: &Region) -> Result<()> { + // check parent Region's property, and check if child Region's offset is valid or not + if self.region_type() != RegionType::Container { + return Err(ErrorKind::RegionType(self.region_type()).into()); + } + let mut sub_regions = self.subregions.write().unwrap(); let mut removed = false; for (index, sub_r) in sub_regions.iter().enumerate() { -- Gitee From e5c8e9e577a65f73b9d20e163cc50bf0915c21c2 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 12 Oct 2022 16:34:55 +0800 Subject: [PATCH 0196/2187] virtio-net: use the same EventSet when parking an event notifier Use the same EventSet when parking an event notifier. Signed-off-by: Yan Wang --- virtio/src/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 09c6045fd..48a232b68 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -618,7 +618,7 @@ impl EventNotifierHelper for NetIoHandler { NotifierOperation::Park, tap.as_raw_fd(), None, - EventSet::IN, + EventSet::IN | EventSet::EDGE_TRIGGERED, Vec::new(), )]; locked_net_io.is_listening = false; -- Gitee From 4172e78aece36901a2c6010247f59ebad9862f21 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 12 Oct 2022 16:43:39 +0800 Subject: [PATCH 0197/2187] docs: add netmask length when adding ip address. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index d34a292c5..4e12d9c34 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -418,7 +418,7 @@ $ ip tuntap add tap0 mode tap $ brctl addif qbr0 tap0 $ ip link set qbr0 up $ ip link set tap0 up -$ ip address add 1.1.1.1 dev qbr0 +$ ip address add 1.1.1.1/24 dev qbr0 # Run StratoVirt ... -netdev tap,id=netdevid,ifname=tap0 ... @@ -439,7 +439,7 @@ $ ip tuntap add tap1 mode tap multi_queue $ brctl addif qbr0 tap1 $ ip link set qbr0 up $ ip link set tap1 up -$ ip address add 1.1.1.1 dev qbr0 +$ ip address add 1.1.1.1/24 dev qbr0 ``` *How to create port by ovs-dpdk?* -- Gitee From 1e42b33f48d43fd19cb47faf1d115d335c9491d3 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 12 Oct 2022 16:21:28 +0800 Subject: [PATCH 0198/2187] network device: VirtioNetConfig is read-only except mac. The virtio-spec(network device) required: The member in VirtioNetConfig is read-only except mac. So, Just allow driver to write mac in some conditions. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 2 ++ virtio/src/net.rs | 26 ++++++++++++++++---------- virtio/src/vhost/kernel/net.rs | 26 +++++++++++++++----------- virtio/src/vhost/user/net.rs | 21 ++++++++++++--------- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index e6fd6c3a2..9e206c73e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -191,6 +191,8 @@ pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15; pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17; /// Device supports multi queue with automatic receive steering. pub const VIRTIO_NET_F_MQ: u32 = 22; +/// Set Mac Address through control channel. +pub const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23; /// Configuration cols and rows are valid. pub const VIRTIO_CONSOLE_F_SIZE: u64 = 0; /// Maximum size of any single segment is in size_max. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 09c6045fd..c353fc8e0 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -39,16 +39,19 @@ use super::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; +use crate::virtio_has_feature; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; /// Size of each virtqueue. const QUEUE_SIZE_NET: u16 = 256; +/// The Mac Address length. +pub const MAC_ADDR_LEN: usize = 6; type SenderConfig = Option; @@ -57,7 +60,7 @@ type SenderConfig = Option; #[derive(Copy, Clone, Debug, Default)] pub struct VirtioNetConfig { /// Mac Address. - pub mac: [u8; 6], + pub mac: [u8; MAC_ADDR_LEN], /// Device status. pub status: u16, /// Maximum number of each of transmit and receive queues. @@ -926,12 +929,15 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let config_slice = self.state.config_space.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); - } - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + if !virtio_has_feature(self.state.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) + && !virtio_has_feature(self.state.driver_features, VIRTIO_F_VERSION_1) + && offset == 0 + && data_len == MAC_ADDR_LEN + && *data != config_slice[0..data_len] + { + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + } Ok(()) } @@ -1111,7 +1117,7 @@ mod tests { net.write_config(0x00, &write_data).unwrap(); net.read_config(0x00, &mut random_data).unwrap(); - assert_eq!(random_data, write_data); + assert_ne!(random_data, write_data); net.write_config(0x00, &origin_data).unwrap(); @@ -1131,7 +1137,7 @@ mod tests { let offset: u64 = len; let mut data: Vec = vec![0; 1]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), false); + assert_eq!(net.write_config(offset, &mut data).is_ok(), true); let offset: u64 = len - 1; let mut data: Vec = vec![0; 1]; diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 5d2d9ba55..85579403e 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -29,15 +29,16 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::{ - net::{build_device_config_space, create_tap, VirtioNetConfig}, + net::{build_device_config_space, create_tap, VirtioNetConfig, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, - VIRTIO_TYPE_NET, + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; +use crate::virtio_has_feature; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; @@ -234,12 +235,15 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let config_slice = self.device_config.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); - } - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + if !virtio_has_feature(self.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) + && !virtio_has_feature(self.driver_features, VIRTIO_F_VERSION_1) + && offset == 0 + && data_len == MAC_ADDR_LEN + && *data != config_slice[0..data_len] + { + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + } Ok(()) } @@ -498,11 +502,11 @@ mod tests { let mut read_data: Vec = vec![0; len as usize]; assert_eq!(vhost_net.read_config(offset, &mut read_data).is_ok(), true); - assert_eq!(read_data, data); + assert_ne!(read_data, data); let offset: u64 = 1; let data: Vec = vec![1; len as usize]; - assert_eq!(vhost_net.write_config(offset, &data).is_ok(), false); + assert_eq!(vhost_net.write_config(offset, &data).is_ok(), true); let offset: u64 = len + 1; let mut read_data: Vec = vec![0; len as usize]; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index f1627dfa8..5fa870d9e 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -25,12 +25,12 @@ use vmm_sys_util::eventfd::EventFd; use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::virtio_has_feature; use super::super::super::{ - net::{build_device_config_space, VirtioNetConfig}, + net::{build_device_config_space, VirtioNetConfig, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, + VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; use super::super::VhostOps; use super::VhostUserClient; @@ -218,12 +218,15 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let config_slice = self.device_config.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); - } - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + if !virtio_has_feature(self.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) + && !virtio_has_feature(self.driver_features, VIRTIO_F_VERSION_1) + && offset == 0 + && data_len == MAC_ADDR_LEN + && *data != config_slice[0..data_len] + { + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + } Ok(()) } -- Gitee From aa193144449025877c2fa6be30f8750641f9fa54 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 11 Oct 2022 21:56:13 +0800 Subject: [PATCH 0199/2187] Docs: fix the guidance document about NUMA node and memory prealloc For guest NUMA guidance documents, the `host-nodes` and `policy` parameters must be set with NUMA feature. Fix the error description in this patch. Add more advantage and note information about memory prealloc feature. Signed-off-by: Xinle.Guo Signed-off-by: Jie Yang --- docs/config_guidebook.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index d34a292c5..df88fdbe4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -67,8 +67,12 @@ Default VM memory size is 256M. The supported VM memory size is among [256M, 512 ``` #### 1.3.2 Memory Prealloc -Memory prealloc is supported by StratoVirt, users can use the following cmdline to configure -memory prealloc. +Memory Prealloc feature is used to preallocate VM physical memory in advance and create its page tables. +Using this feature, the number of page faults will decrease, and the memory access performance of the VM will improve. + +Note: This option will take effect the VM startup time. + +You can use the following cmdline to configure memory prealloc. ```shell -mem-prealloc @@ -130,8 +134,8 @@ The following command shows how to set NUMA node: # The memory size must be set to be the same as numa node mem. -m 4G --object memory-backend-ram,size=2G,id=mem0,[host-nodes=0-1,policy=bind] --object memory-backend-ram,size=2G,id=mem1,[host-nodes=0-1,policy=bind] +-object memory-backend-ram,size=2G,id=mem0,host-nodes=0-1,policy=bind +-object memory-backend-ram,size=2G,id=mem1,host-nodes=0-1,policy=bind -numa node,nodeid=0,cpus=0-1:4-5,memdev=mem0 -numa node,nodeid=1,cpus=2-3:6-7,memdev=mem1 [-numa dist,src=0,dst=0,val=10] -- Gitee From 60673fcc6ce29acdd645c9149ed6a08bcdba66c5 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 11 Oct 2022 22:11:20 +0800 Subject: [PATCH 0200/2187] Memory: Fix memory subsystem code comment errors 1. Memory subsystem add Ram device and Rom device type region, So, update note content here. 2. Fix code comment description unreasonable content. Signed-off-by: Xinle.Guo Signed-off-by: Jie Yang --- address_space/src/address_space.rs | 12 ++++++------ address_space/src/host_mmap.rs | 2 ++ address_space/src/region.rs | 12 +++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 72ed8bae1..f58da34b5 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -22,7 +22,7 @@ use crate::{ RegionType, }; -/// Contain an array of `FlatRange`. +/// Contains an array of `FlatRange`. #[derive(Default, Clone, Debug)] pub(crate) struct FlatView(pub Vec); @@ -62,8 +62,8 @@ type ListenerObj = Arc>; pub struct AddressSpace { /// Root Region of this AddressSpace. root: Region, - /// Flat_view is the output of rendering all regions in parent address-space, - /// every time the topology changed (add/delete region), flat_view would be updated. + /// `flat_view` is the output of rendering all regions in parent `address-space`, + /// every time the topology changed (add/delete region), `flat_view` would be updated. flat_view: Arc>, /// The triggered call-backs when flat_view changed. listeners: Arc>>, @@ -391,7 +391,7 @@ impl AddressSpace { None } - /// Return the end address of memory according to all Ram regions in AddressSpace. + /// Return the end address of memory according to all Ram regions in AddressSpace. pub fn memory_end_address(&self) -> GuestAddress { self.flat_view .load() @@ -491,7 +491,7 @@ impl AddressSpace { /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { let mut dst = unsafe { - std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::() as usize) + std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; dst.write_all(data.as_bytes()) .chain_err(|| "Failed to write object via host address") @@ -528,7 +528,7 @@ impl AddressSpace { let mut obj = T::default(); let mut dst = obj.as_mut_bytes(); let src = unsafe { - std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::() as usize) + std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; dst.write_all(src) .chain_err(|| "Failed to read object via host address")?; diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index dd0878714..0c42d2a5e 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -75,6 +75,8 @@ impl FileBackend { pub fn new_mem(file_path: &str, file_len: u64) -> Result { let path = std::path::Path::new(&file_path); let file = if path.is_dir() { + // The last six characters of template file must be "XXXXXX" for `mkstemp` + // function to create unique temporary file. let fs_path = format!("{}{}", file_path, "/stratovirt_backmem_XXXXXX"); let fs_cstr = std::ffi::CString::new(fs_path.clone()).unwrap().into_raw(); diff --git a/address_space/src/region.rs b/address_space/src/region.rs index b08ff480d..470ec9d5b 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -36,7 +36,7 @@ pub enum RegionType { RamDevice, } -/// Represents a memory region, used by mem-mapped IO or Ram. +/// Represents a memory region, used by mem-mapped IO, Ram or Rom. #[derive(Clone)] pub struct Region { /// Type of Region, won't be changed once initialized. @@ -47,7 +47,7 @@ pub struct Region { size: Arc, /// Offset in parent Container-type region. It won't be changed once initialized. offset: Arc>, - /// If not Ram-type Region, `mem_mapping` is None. It won't be changed once initialized. + /// If not Ram, RomDevice, RamDevice Region type, `mem_mapping` is None. It won't be changed once initialized. mem_mapping: Option>, /// `ops` provides read/write function. ops: Option, @@ -55,7 +55,7 @@ pub struct Region { io_evtfds: Arc>>, /// Weak pointer pointing to the father address-spaces. space: Arc>>, - /// Sub-regions array, keep sorted + /// Sub-regions array, keep sorted. subregions: Arc>>, /// This field is useful for RomDevice-type Region. If true, in read-only mode, otherwise in IO mode. rom_dev_romd: Arc, @@ -125,7 +125,7 @@ impl RegionIoEventFd { false } - /// Return the cloned IoEvent, + /// Return the cloned Region IoEventFd, /// return error if failed to clone EventFd. pub(crate) fn try_clone(&self) -> Result { let fd = self.fd.try_clone().or(Err(ErrorKind::IoEventFd))?; @@ -366,6 +366,7 @@ impl Region { self.mem_mapping.as_ref().and_then(|r| r.file_backend()) } + /// Get the region file backend page size. pub fn get_region_page_size(&self) -> Option { self.mem_mapping .as_ref() @@ -553,12 +554,13 @@ impl Region { Ok(()) } + /// Set the ioeventfds within this Region, /// Return the IoEvent of a `Region`. pub fn set_ioeventfds(&self, new_fds: &[RegionIoEventFd]) { *self.io_evtfds.lock().unwrap() = new_fds.iter().map(|e| e.try_clone().unwrap()).collect(); } - /// Set the ioeventfds within this Region, + /// Get the ioeventfds within this Region, /// these fds will be register to `KVM` and used for guest notifier. pub fn ioeventfds(&self) -> Vec { self.io_evtfds -- Gitee From 4587c13579f1d290d1ea2b0b5499c80ee03473a6 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 13 Oct 2022 11:42:51 +0800 Subject: [PATCH 0201/2187] Fix: Remove unnecessary memory fence When processing VCPU_TASK_SIGNAL, the fence is required to ensure that immediate_exit written to the memory. However, when processing VCPU_RESET_SIGNAL, the hence is not required. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 6fcf9be62..b0ab37d4d 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -625,7 +625,6 @@ impl CPUThreadWorker { ) { error!("Failed to reset vcpu state: {}", e.to_string()) } - fence(Ordering::Release) }); } _ => {} -- Gitee From a6f0a1058b9a231dd704e40326f78c41d78ef001 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 13 Oct 2022 14:14:35 +0800 Subject: [PATCH 0202/2187] Fix: don't modify bootorder file when boot_order_vec is empty If bootindex is not configured for the boot parameter, boot_order_vec is empty. In this case, there is no need to sort and modify the bootorder file. Signed-off-by: Mingwang Li --- machine/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 2ae995958..6a24b13fc 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -584,6 +584,9 @@ pub trait MachineOps { // unwrap is safe because stand machine always make sure it not return null. let boot_order_vec = self.get_boot_order_list().unwrap(); let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone(); + if locked_boot_order_vec.is_empty() { + return Ok(()); + } locked_boot_order_vec.sort_by(|x, y| x.boot_index.cmp(&y.boot_index)); let mut fwcfg_boot_order_string = String::new(); for item in &locked_boot_order_vec { -- Gitee From 48d115490d10ada4e7d75476f011dfbb79f34156 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 13 Oct 2022 15:06:13 +0800 Subject: [PATCH 0203/2187] docs: update the calculation of virtio-rng limited rate 'period' unit is millisecond, limited rate unit is bytes/sec, so let's multiply by 1000. Signed-off-by: yezengruan --- docs/config_guidebook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index aff178cd5..03e6336eb 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -611,7 +611,7 @@ single function device, the function number should be set to zero. * The limited rate will be transformed to bytes/sec. For instance: if period=100, max-bytes=100; the final result is that the max number of bytes generated by rng device is 1000. * Limited rate should between 64(include) and 1000000000(not include). In other words: - 64 <= max-bytes/period < 1000000000. + 64 <= max-bytes/period\*1000 < 1000000000. ```shell # virtio mmio rng device -- Gitee From 132e3490b75a3b432e9b957c465e0456af28249a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 13 Oct 2022 14:44:37 +0800 Subject: [PATCH 0204/2187] virtio-pci: add get_driver_features function to VirtioDevice trait In some conditions, driver may need to get the driver features. So, we need to add get_driver_features funciton. Signed-off-by: Yan Wang --- virtio/src/balloon.rs | 10 ++++++++++ virtio/src/block.rs | 12 ++++++++++++ virtio/src/console.rs | 11 +++++++++++ virtio/src/gpu.rs | 5 +++++ virtio/src/lib.rs | 3 +++ virtio/src/net.rs | 5 +++++ virtio/src/rng.rs | 11 +++++++++++ virtio/src/vhost/kernel/net.rs | 7 +++++++ virtio/src/vhost/kernel/vsock.rs | 7 +++++++ virtio/src/vhost/user/block.rs | 5 +++++ virtio/src/vhost/user/fs.rs | 5 +++++ virtio/src/vhost/user/net.rs | 5 +++++ virtio/src/virtio_mmio.rs | 4 ++++ virtio/src/virtio_pci.rs | 8 ++++++++ 14 files changed, 98 insertions(+) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index cb450e462..b58e4c254 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -875,6 +875,11 @@ impl VirtioDevice for Balloon { self.driver_features |= v; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.driver_features, features_select) + } + /// Read configuration. /// /// # Arguments @@ -1135,9 +1140,14 @@ mod tests { bln.device_features = 1; bln.set_driver_features(0, 1); assert_eq!(bln.driver_features, 1); + assert_eq!(bln.driver_features, bln.get_driver_features(0) as u64); bln.driver_features = 1 << 32; bln.set_driver_features(1, 1); assert_eq!(bln.driver_features, 1 << 32); + assert_eq!( + bln.driver_features, + (bln.get_driver_features(1) as u64) << 32 + ); // Test realize function. bln.realize().unwrap(); diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 93838aa37..e33280824 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1066,6 +1066,11 @@ impl VirtioDevice for Block { self.state.driver_features |= v; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config_space.as_bytes(); @@ -1325,12 +1330,14 @@ mod tests { let page = 0_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.state.driver_features, 0_u64); + assert_eq!(block.get_driver_features(page) as u64, 0_u64); assert_eq!(block.get_device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.state.driver_features, 0_u64); + assert_eq!(block.get_driver_features(page) as u64, 0_u64); assert_eq!(block.get_device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are @@ -1344,6 +1351,10 @@ mod tests { block.state.driver_features, (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) ); + assert_eq!( + block.get_driver_features(page) as u64, + (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) + ); assert_eq!( block.get_device_features(page), (1_u32 << VIRTIO_F_RING_INDIRECT_DESC) @@ -1355,6 +1366,7 @@ mod tests { let page = 0_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.state.driver_features, 0); + assert_eq!(block.get_driver_features(page), 0); assert_eq!(block.get_device_features(page), 0_u32); block.state.driver_features = 0; } diff --git a/virtio/src/console.rs b/virtio/src/console.rs index ae12ecf28..504bdf105 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -380,6 +380,11 @@ impl VirtioDevice for Console { self.state.driver_features |= v; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config_space.as_bytes(); @@ -495,11 +500,13 @@ mod tests { let page = 0_u32; console.set_driver_features(page, driver_feature); assert_eq!(console.state.driver_features, 0_u64); + assert_eq!(console.get_driver_features(page) as u64, 0_u64); let driver_feature: u32 = 0xFF; let page = 1_u32; console.set_driver_features(page, driver_feature); assert_eq!(console.state.driver_features, 0_u64); + assert_eq!(console.get_driver_features(page) as u64, 0_u64); //If both the device feature bit and the front-end driver feature bit are //supported at the same time, this driver feature bit is supported. @@ -512,6 +519,10 @@ mod tests { console.state.driver_features, (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); + assert_eq!( + console.get_driver_features(page) as u64, + (1_u64 << VIRTIO_CONSOLE_F_SIZE) + ); console.state.driver_features = 0; console.state.device_features = 1_u64 << VIRTIO_F_VERSION_1; diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 76edef078..82518d3a2 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -1731,6 +1731,11 @@ impl VirtioDevice for Gpu { self.state.driver_features |= v; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config.as_bytes(); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9e206c73e..8f4ea473f 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -297,6 +297,9 @@ pub trait VirtioDevice: Send { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32); + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32; + /// Read data of config from guest. fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()>; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 209a8205f..8ccf2ece3 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -912,6 +912,11 @@ impl VirtioDevice for Net { self.state.driver_features |= v; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config_space.as_bytes(); diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 0a3226c50..f5d86da7a 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -338,6 +338,11 @@ impl VirtioDevice for Rng { self.state.driver_features |= v; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, _data: &mut [u8]) -> Result<()> { bail!( @@ -505,12 +510,14 @@ mod tests { let page = 0_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.state.driver_features, 0_u64); + assert_eq!(rng.get_driver_features(page) as u64, 0_u64); assert_eq!(rng.get_device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.state.driver_features, 0_u64); + assert_eq!(rng.get_driver_features(page) as u64, 0_u64); assert_eq!(rng.get_device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are @@ -524,6 +531,10 @@ mod tests { rng.state.driver_features, (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) ); + assert_eq!( + rng.get_driver_features(page) as u64, + (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) + ); assert_eq!( rng.get_device_features(page), (1_u32 << VIRTIO_F_RING_INDIRECT_DESC) diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 85579403e..a5d290100 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -217,6 +217,11 @@ impl VirtioDevice for Net { self.driver_features |= features; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.device_config.as_bytes(); @@ -482,6 +487,7 @@ mod tests { let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); + assert_eq!(vhost_net.get_driver_features(page) as u64, 0_u64); let new_page = vhost_net.get_device_features(page); assert_eq!(new_page, page); @@ -489,6 +495,7 @@ mod tests { let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); + assert_eq!(vhost_net.get_driver_features(page) as u64, 0xff_u64); let new_page = vhost_net.get_device_features(page); assert_ne!(new_page, page); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 8c58b49f5..f7c042225 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -197,6 +197,11 @@ impl VirtioDevice for Vsock { self.state.driver_features |= features; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { match offset { @@ -428,9 +433,11 @@ mod tests { vsock.state.device_features = 0x0123_4567_89ab_cdef; // check for unsupported feature vsock.set_driver_features(0, 0x7000_0000); + assert_eq!(vsock.get_driver_features(0) as u64, 0_u64); assert_eq!(vsock.state.device_features, 0x0123_4567_89ab_cdef); // check for supported feature vsock.set_driver_features(0, 0x8000_0000); + assert_eq!(vsock.get_driver_features(0) as u64, 0x8000_0000_u64); assert_eq!(vsock.state.device_features, 0x0123_4567_89ab_cdef); // test vsock read_config diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 87cb6125f..aee91ada4 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -256,6 +256,11 @@ impl VirtioDevice for Block { self.state.driver_features |= features; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let offset = offset as usize; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 8b51e1ba9..cb5913dc2 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -187,6 +187,11 @@ impl VirtioDevice for Fs { self.acked_features |= features; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.acked_features, features_select) + } + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.config.as_bytes(); let config_size = config_slice.len() as u64; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 5fa870d9e..1e8a7e4bd 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -200,6 +200,11 @@ impl VirtioDevice for Net { self.driver_features |= features; } + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.driver_features, features_select) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.device_config.as_bytes(); diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 6db9687bf..ef75edf31 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -819,6 +819,10 @@ mod tests { self.driver_features |= v; } + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.driver_features, features_select) + } + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_len = self.config_space.len() as u64; if offset >= config_len { diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index fef36446c..d30c01847 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -208,6 +208,10 @@ impl VirtioPciCommonConfig { .unwrap() .get_device_features(self.features_select), COMMON_GFSELECT_REG => self.acked_features_select, + COMMON_GF_REG => device + .lock() + .unwrap() + .get_driver_features(self.acked_features_select), COMMON_MSIX_REG => self.msix_config.load(Ordering::SeqCst) as u32, COMMON_NUMQ_REG => self.queues_config.len() as u32, COMMON_STATUS_REG => self.device_status, @@ -1520,6 +1524,10 @@ mod tests { self.driver_features |= v; } + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.driver_features, features_select) + } + fn read_config(&self, _offset: u64, mut _data: &mut [u8]) -> VirtioResult<()> { Ok(()) } -- Gitee From c2944a6674bb16bea2c8f88634ff0a23ea65bd04 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 13 Oct 2022 15:37:46 +0800 Subject: [PATCH 0205/2187] virtio-pci: add check for features select num The valid features select num is 0 or 1, we shold check it before using it. Signed-off-by: Yan Wang --- pci/src/lib.rs | 3 +++ virtio/src/virtio_pci.rs | 34 ++++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/pci/src/lib.rs b/pci/src/lib.rs index bd5d252b4..cb3a4d66b 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -33,6 +33,9 @@ pub mod errors { PciRegister(offset: u64) { display("Unsupported pci register, 0x{:x}", offset) } + FeaturesSelect(select: u32) { + display("Invalid features select 0x{:x}", select) + } } } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index d30c01847..876f52a2c 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -110,6 +110,11 @@ const COMMON_Q_USEDLO_REG: u64 = 0x30; /// The high 32bit of queue's Used Ring address - Read Write. const COMMON_Q_USEDHI_REG: u64 = 0x34; +/// The max features select num, only 0 or 1 is valid: +/// 0: select feature bits 0 to 31. +/// 1: select feature bits 32 to 63. +const MAX_FEATURES_SELECT_NUM: u32 = 2; + /// Get class id according to device type. /// /// # Arguments @@ -203,15 +208,25 @@ impl VirtioPciCommonConfig { ) -> PciResult { let value = match offset { COMMON_DFSELECT_REG => self.features_select, - COMMON_DF_REG => device - .lock() - .unwrap() - .get_device_features(self.features_select), + COMMON_DF_REG => { + if self.features_select >= MAX_FEATURES_SELECT_NUM { + return Err(ErrorKind::FeaturesSelect(self.features_select).into()); + } + device + .lock() + .unwrap() + .get_device_features(self.features_select) + } COMMON_GFSELECT_REG => self.acked_features_select, - COMMON_GF_REG => device - .lock() - .unwrap() - .get_driver_features(self.acked_features_select), + COMMON_GF_REG => { + if self.acked_features_select >= MAX_FEATURES_SELECT_NUM { + return Err(ErrorKind::FeaturesSelect(self.acked_features_select).into()); + } + device + .lock() + .unwrap() + .get_driver_features(self.acked_features_select) + } COMMON_MSIX_REG => self.msix_config.load(Ordering::SeqCst) as u32, COMMON_NUMQ_REG => self.queues_config.len() as u32, COMMON_STATUS_REG => self.device_status, @@ -278,6 +293,9 @@ impl VirtioPciCommonConfig { self.acked_features_select = value; } COMMON_GF_REG => { + if self.acked_features_select >= MAX_FEATURES_SELECT_NUM { + return Err(ErrorKind::FeaturesSelect(self.acked_features_select).into()); + } device .lock() .unwrap() -- Gitee From b9ca6dff2ee265988746ff08353803baa8fb36bc Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 13 Oct 2022 15:55:31 +0800 Subject: [PATCH 0206/2187] virtio-pci: check if the driver support VIRTIO_F_VERSION_1 Due to the StratoVirt is modern only, it should check if the driver support VIRTIO_F_VERSION_1 when the driver setting FEATURES_OK. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 876f52a2c..0de7b78d4 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -42,7 +42,7 @@ use crate::{ use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, - VIRTIO_F_RING_PACKED, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, + VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -313,6 +313,15 @@ impl VirtioPciCommonConfig { self.interrupt_status.store(0_u32, Ordering::SeqCst); } COMMON_STATUS_REG => { + if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { + let features = (device.lock().unwrap().get_driver_features(1) as u64) << 32; + if !virtio_has_feature(features, VIRTIO_F_VERSION_1) { + error!( + "Device is modern only, but the driver not support VIRTIO_F_VERSION_1" + ); + return Ok(()); + } + } self.device_status = value; if self.device_status == 0 { self.queues_config.iter_mut().for_each(|q| { -- Gitee From 1fa227092b147a156af4518e7803ced9d56342ea Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Thu, 13 Oct 2022 16:40:09 +0800 Subject: [PATCH 0207/2187] vhost_vsock: modify comments and function names Because comments and function names do not accurately describe test cases,change virtio rng to vhost vsock. Signed-off-by: Zuo Xiaoxian --- .../microvm/functional/test_microvm_vhost_vsock.py | 8 ++++---- .../standvm/functional/test_standvm_vhost_vsock.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py b/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py index 96dfb0972..c7866e4d5 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py @@ -27,7 +27,7 @@ BLOB_SIZE = 2000 def _check_vsock_enable(microvm): - """Check virtio rng device in Guest""" + """Check vhost vsock device in Guest""" _cmd = "ls /dev/vsock" status, _ = microvm.serial_cmd(_cmd) if status != 0: @@ -77,13 +77,13 @@ def _get_recv_data_from_guest(microvm): ssh_session.close() @pytest.mark.acceptance -def test_microvm_virtio_vsock(microvm, nc_vsock_path, test_session_root_path): - """Test virtio-rng device""" +def test_microvm_vhost_vsock(microvm, nc_vsock_path, test_session_root_path): + """Test vhost vsock device""" test_vm = microvm test_vm.basic_config(vsocknums=1) test_vm.launch() - # check virtio-vsock device + # check vhost vsock device if not _check_vsock_enable(test_vm): pytest.skip("vhost-vsock init failed, skip this testcase") diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py b/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py index d96f3ff23..ba88e1ba2 100644 --- a/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py @@ -27,7 +27,7 @@ BLOB_SIZE = 2000 def _check_vsock_enable(standvm): - """Check virtio rng device in Guest""" + """Check vhost vsock device in Guest""" _cmd = "ls /dev/vsock" status, _ = standvm.serial_cmd(_cmd) if status != 0: @@ -77,13 +77,13 @@ def _get_recv_data_from_guest(standvm): ssh_session.close() @pytest.mark.acceptance -def test_standvm_virtio_vsock(standvm, nc_vsock_path, test_session_root_path): - """Test virtio-rng device""" +def test_standvm_vhost_vsock(standvm, nc_vsock_path, test_session_root_path): + """Test vhost vsock device""" test_vm = standvm test_vm.basic_config(vsocknums=1) test_vm.launch() - # check virtio-vsock device + # check vhost vsock device if not _check_vsock_enable(test_vm): pytest.skip("vhost-vsock init failed, skip this testcase") -- Gitee From 1e00d7daa0d2572c576ef7a07e1e31aea052ea6f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 13 Oct 2022 20:49:05 +0800 Subject: [PATCH 0208/2187] docs: update the maximum limited rate value of virtio-rng --- docs/config_guidebook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 03e6336eb..1c5716e28 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -610,8 +610,8 @@ single function device, the function number should be set to zero. NB: * The limited rate will be transformed to bytes/sec. For instance: if period=100, max-bytes=100; the final result is that the max number of bytes generated by rng device is 1000. - * Limited rate should between 64(include) and 1000000000(not include). In other words: - 64 <= max-bytes/period\*1000 < 1000000000. + * The limited rate should be between 64(included) and 1000000000(included), that is: + 64 <= max-bytes/period\*1000 <= 1000000000. ```shell # virtio mmio rng device -- Gitee From b09e95eca518b08592775c637e56b2639c739f86 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 13 Oct 2022 18:28:31 +0800 Subject: [PATCH 0209/2187] virtio-pci: return 0 when guest reads common configuration extra area return 0 when guest reads common configuration extra area, rather than a random value. Signed-off-by: Zhang Bo --- virtio/src/virtio_pci.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 0de7b78d4..c6ac46011 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -209,23 +209,25 @@ impl VirtioPciCommonConfig { let value = match offset { COMMON_DFSELECT_REG => self.features_select, COMMON_DF_REG => { - if self.features_select >= MAX_FEATURES_SELECT_NUM { - return Err(ErrorKind::FeaturesSelect(self.features_select).into()); + if self.features_select < MAX_FEATURES_SELECT_NUM { + device + .lock() + .unwrap() + .get_device_features(self.features_select) + } else { + 0 } - device - .lock() - .unwrap() - .get_device_features(self.features_select) } COMMON_GFSELECT_REG => self.acked_features_select, COMMON_GF_REG => { - if self.acked_features_select >= MAX_FEATURES_SELECT_NUM { - return Err(ErrorKind::FeaturesSelect(self.acked_features_select).into()); + if self.acked_features_select < MAX_FEATURES_SELECT_NUM { + device + .lock() + .unwrap() + .get_driver_features(self.acked_features_select) + } else { + 0 } - device - .lock() - .unwrap() - .get_driver_features(self.acked_features_select) } COMMON_MSIX_REG => self.msix_config.load(Ordering::SeqCst) as u32, COMMON_NUMQ_REG => self.queues_config.len() as u32, @@ -260,9 +262,7 @@ impl VirtioPciCommonConfig { COMMON_Q_USEDHI_REG => self .get_queue_config() .map(|config| (config.used_ring.0 >> 32) as u32)?, - _ => { - return Err(ErrorKind::PciRegister(offset).into()); - } + _ => 0, }; Ok(value) -- Gitee From 92560f95697588c816fbe5bc28b3097e32b9a705 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 17 Oct 2022 16:59:57 +0800 Subject: [PATCH 0210/2187] Fix: the numbers of vcpus verified by edk2 is incorrect The number of cpus in the fwcfg is set to 0 and the default maximum number of cpus in edk2 is 64. Therefore ,when the number of cpus in the boot parameter exceeds 64, edk2 verification fails. As a result, the VM fails to started. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 16 ++++++++-------- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 17 ++++++++--------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index a00668396..181be188f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -310,13 +310,12 @@ impl StdMachineOps for StdMachine { Ok(()) } - fn add_fwcfg_device(&mut self) -> StdResult>> { + fn add_fwcfg_device(&mut self, nr_cpus: u8) -> StdResult>> { use super::errors::ResultExt; let mut fwcfg = FwCfgMem::new(self.sys_mem.clone()); - let ncpus = self.cpus.len(); fwcfg - .add_data_entry(FwCfgEntryType::NbCpus, ncpus.as_bytes().to_vec()) + .add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec()) .chain_err(|| DevErrorKind::AddEntryErr("NbCpus".to_string()))?; let cmdline = self.boot_source.lock().unwrap().kernel_cmdline.to_string(); @@ -468,6 +467,7 @@ impl MachineOps for StdMachine { use super::errors::ErrorKind as StdErrorKind; use crate::errors::ResultExt; + let nr_cpus = vm_config.machine_config.nr_cpus; let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; @@ -478,12 +478,12 @@ impl MachineOps for StdMachine { locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.sys_mem, - vm_config.machine_config.nr_cpus, + nr_cpus, )?; let vcpu_fds = { let mut fds = vec![]; - for vcpu_id in 0..vm_config.machine_config.nr_cpus { + for vcpu_id in 0..nr_cpus { fds.push(Arc::new( KVM_FDS .load() @@ -497,11 +497,11 @@ impl MachineOps for StdMachine { }; // Interrupt Controller Chip init - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; locked_vm .init_pci_host() .chain_err(|| StdErrorKind::InitPCIeHostErr)?; - let fwcfg = locked_vm.add_fwcfg_device()?; + let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; locked_vm .add_devices(vm_config) .chain_err(|| "Failed to add devices")?; @@ -522,7 +522,7 @@ impl MachineOps for StdMachine { locked_vm.cpus.extend(::init_vcpu( vm.clone(), - vm_config.machine_config.nr_cpus, + nr_cpus, &CPUTopology::new(), &vcpu_fds, &boot_config, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 49c162e77..1803a5723 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -198,7 +198,7 @@ trait StdMachineOps: AcpiBuilder { Ok(()) } - fn add_fwcfg_device(&mut self) -> Result>> { + fn add_fwcfg_device(&mut self, _nr_cpus: u8) -> Result>> { bail!("Not implemented"); } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 7dd9aa1cf..4bac5e8be 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -310,13 +310,12 @@ impl StdMachineOps for StdMachine { Ok(()) } - fn add_fwcfg_device(&mut self) -> super::errors::Result>> { + fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::errors::Result>> { use super::errors::ResultExt; let mut fwcfg = FwCfgIO::new(self.sys_mem.clone()); - let ncpus = self.cpus.len(); - fwcfg.add_data_entry(FwCfgEntryType::NbCpus, ncpus.as_bytes().to_vec())?; - fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, ncpus.as_bytes().to_vec())?; + fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; + fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; let boot_order = Vec::::new(); @@ -448,6 +447,7 @@ impl MachineOps for StdMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { use crate::errors::ResultExt; + let nr_cpus = vm_config.machine_config.nr_cpus; let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; @@ -456,13 +456,12 @@ impl MachineOps for StdMachine { &vm_config.machine_config.mem_config, &locked_vm.sys_io, &locked_vm.sys_mem, - vm_config.machine_config.nr_cpus, + nr_cpus, )?; - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let nr_cpus = vm_config.machine_config.nr_cpus; let mut vcpu_fds = vec![]; for cpu_id in 0..nr_cpus { vcpu_fds.push(Arc::new(vm_fd.create_vcpu(cpu_id as u64)?)); @@ -478,7 +477,7 @@ impl MachineOps for StdMachine { #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) .chain_err(|| "Failed to init VNC server!")?; - let fwcfg = locked_vm.add_fwcfg_device()?; + let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { @@ -493,7 +492,7 @@ impl MachineOps for StdMachine { )); locked_vm.cpus.extend(::init_vcpu( vm.clone(), - vm_config.machine_config.nr_cpus, + nr_cpus, &topology, &vcpu_fds, &boot_config, -- Gitee From 50a5a79d377e4241fc786fe260b6b356cde7fe73 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 17 Oct 2022 20:46:07 +0800 Subject: [PATCH 0211/2187] virtio-blk: make seg_max virtqueue size dependent When queue_size > 128 (256 in stratovirt now), seg_max restrics guest's block request length which affects guest's performance making them issues more block request than needed. Modify seg_max to (virtqueue size - 2). Signed-off-by: liuxiangdong --- virtio/src/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index e33280824..62eb4272f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -953,8 +953,8 @@ impl Block { // capacity: 64bits let num_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; self.state.config_space.capacity = num_sectors; - // seg_max = 128-2: 32bits - self.state.config_space.seg_max = 126; + // seg_max = queue_size - 2: 32bits + self.state.config_space.seg_max = self.queue_size() as u32 - 2; } } -- Gitee From c53e62233ead5ab860c5e6a5d1c8f8cc3f20a9fe Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 19 Oct 2022 10:44:33 +0800 Subject: [PATCH 0212/2187] Fix: The VM can't be booted when ARM core exceeds 123 One GICD supports a maximum of 123 cpus. THe stratovirt has only one GICD in the madt table of ACPI. When the VM is booted, the cpus with more than 123 can't adapt to the GICR. Signed-off-by: Mingwang Li --- devices/src/interrupt_controller/aarch64/gicv3.rs | 4 ++++ devices/src/interrupt_controller/aarch64/mod.rs | 9 +++++++++ machine/src/standard_vm/aarch64/mod.rs | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 1b1c34787..9798c8278 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -440,6 +440,10 @@ impl GICDevice for GICv3 { Ok(()) } + + fn get_redist_count(&self) -> u8 { + self.redist_regions.len() as u8 + } } pub struct GICv3Its { diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index d45bd063b..4c0afa926 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -129,6 +129,11 @@ pub trait GICDevice: MachineLifecycle { /// /// * `fdt` - Device tree presented by bytes. fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()>; + + /// Get GIC redistributor number. + fn get_redist_count(&self) -> u8 { + 0 + } } /// A wrapper around creating and using a kvm-based interrupt controller. @@ -166,6 +171,10 @@ impl InterruptController { self.gic .notify_lifecycle(KvmVmState::Running, KvmVmState::Paused); } + + pub fn get_redist_count(&self) -> u8 { + self.gic.get_redist_count() + } } impl device_tree::CompileFDT for InterruptController { diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index a00668396..aa5bb271d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -840,6 +840,11 @@ impl AcpiBuilder for StdMachine { gic_redist.base_addr = MEM_LAYOUT[LayoutEntryType::GicRedist as usize].0; gic_redist.length = 16; madt.append_child(&gic_redist.aml_bytes()); + if self.irq_chip.as_ref().unwrap().get_redist_count() > 1 { + gic_redist.range_length = MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].1 as u32; + gic_redist.base_addr = MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].0; + madt.append_child(&gic_redist.aml_bytes()); + } // 4. GIC Its. let mut gic_its = AcpiGicIts::default(); -- Gitee From 5f42ac125a50e94362e70ca296f6d64e6949e041 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 19 Oct 2022 17:51:38 +0800 Subject: [PATCH 0213/2187] Fix: add 0x1f cpuid when host not support The Host does not support 0x1f cpuid. Thereforce, the configuration of the startup parameter dies not take effect. The 0x1f cpuid needs to be actively extended. Signed-off-by: Mingwang Li --- cpu/src/x86_64/mod.rs | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index bbd7c63a4..709a70740 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -17,9 +17,10 @@ use std::sync::{Arc, Mutex}; use error_chain::bail; use kvm_bindings::{ - kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_segment, - kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, Msrs, KVM_MAX_CPUID_ENTRIES, - KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_UNINITIALIZED, + kvm_cpuid_entry2, kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, + kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, CpuId, Msrs, + KVM_CPUID_FLAG_SIGNIFCANT_INDEX, KVM_MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE, + KVM_MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -390,6 +391,33 @@ impl X86CPUState { } } + fn adjust_cpuid(&self, cpuid: &mut CpuId) { + if self.nr_dies < 2 { + return; + } + + // Intel CPU topology with multi-dies support requies CPUID[0x1f]. + let entries = cpuid.as_mut_slice(); + for entry in entries.iter_mut() { + if entry.function == 0 { + if entry.eax >= 0x1f { + return; + } else { + entry.eax = 0x1f; + } + break; + } + } + (0..4).for_each(|index| { + let entry = kvm_cpuid_entry2 { + function: 0x1f, + index, + ..Default::default() + }; + cpuid.push(entry).unwrap(); + }); + } + fn setup_cpuid(&self, vcpu_fd: &Arc) -> Result<()> { let core_offset = 32u32 - (self.nr_threads - 1).leading_zeros(); let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset; @@ -401,6 +429,9 @@ impl X86CPUState { let mut cpuid = sys_fd .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) .chain_err(|| format!("Failed to get supported cpuid for CPU {}/KVM", self.apic_id))?; + + self.adjust_cpuid(&mut cpuid); + let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { @@ -483,6 +514,7 @@ impl X86CPUState { entry.edx = self.apic_id as u32; entry.ecx = entry.index & 0xff; + entry.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; match entry.index { 0 => { -- Gitee From 539a171f03a21d542a7688afee1768afcfe90c86 Mon Sep 17 00:00:00 2001 From: lin Date: Sun, 9 Oct 2022 21:53:27 +0800 Subject: [PATCH 0214/2187] vnc: update vnc version Signed-off-by: lin --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 42dbc861f..d8e1f8726 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -806,7 +806,7 @@ dependencies = [ [[package]] name = "vnc" -version = "2.1.0" +version = "2.2.0" dependencies = [ "acpi", "address_space", -- Gitee From dd6a3ef106540ae06c24c878f36d108005728d1f Mon Sep 17 00:00:00 2001 From: lin Date: Thu, 29 Sep 2022 19:04:50 +0800 Subject: [PATCH 0215/2187] virtio: add io-uring support Signed-off-by: lin --- Cargo.lock | 11 ++ ...Third_Party_Open_Source_Software_Notice.md | 6 + machine/src/micro_vm/mod.rs | 2 + machine/src/micro_vm/syscall.rs | 11 +- machine/src/standard_vm/aarch64/syscall.rs | 7 +- machine/src/standard_vm/mod.rs | 3 + machine/src/standard_vm/x86_64/syscall.rs | 7 +- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/drive.rs | 17 ++- util/Cargo.toml | 1 + util/src/aio/libaio.rs | 21 ++-- util/src/aio/mod.rs | 69 ++++++++-- util/src/aio/uring.rs | 119 ++++++++++++++++++ virtio/src/block.rs | 6 +- 14 files changed, 246 insertions(+), 36 deletions(-) create mode 100644 util/src/aio/uring.rs diff --git a/Cargo.lock b/Cargo.lock index d8e1f8726..83ed5eedb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,6 +266,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-uring" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d32c9c053ad47572e11da8bce622ed4c9ae9dedac5b7f678a2e876d1494d4c4" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "itoa" version = "1.0.3" @@ -709,6 +719,7 @@ dependencies = [ "arc-swap", "byteorder", "error-chain", + "io-uring", "kvm-bindings", "kvm-ioctls", "libc", diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 69ec8eb4f..1196ccc97 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -413,3 +413,9 @@ Copyright notice: Copyright (c) Aleksey Kladov License: MIT OR Apache-2.0 Please see above. + +Software: io-uring 0.5.7 +Copyright notice: +Copyright (c) tokio-rs +License: MIT OR Apache-2.0 +Please see above. diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 702f0b1ae..56497e5ea 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1179,6 +1179,8 @@ impl DeviceInterface for LightMachine { boot_index: None, chardev: None, socket_path: None, + // TODO Add aio option by qmp. + aio: None, }; match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 0290980af..b3a4aa5a2 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -48,10 +48,10 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 44 syscalls -/// * x86_64-unknown-musl: 43 syscalls -/// * aarch64-unknown-gnu: 42 syscalls -/// * aarch64-unknown-musl: 42 syscalls +/// * x86_64-unknown-gnu: 47 syscalls +/// * x86_64-unknown-musl: 46 syscalls +/// * aarch64-unknown-gnu: 45 syscalls +/// * aarch64-unknown-musl: 45 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -66,6 +66,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_wait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), + BpfRule::new(libc::SYS_io_uring_enter), + BpfRule::new(libc::SYS_io_uring_setup), + BpfRule::new(libc::SYS_io_uring_register), BpfRule::new(libc::SYS_dup), BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 4ffc4e0e4..ebcaa49bd 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 60 syscalls -/// * aarch64-unknown-musl: 58 syscalls +/// * aarch64-unknown-gnu: 63 syscalls +/// * aarch64-unknown-musl: 61 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -68,6 +68,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_pwait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), + BpfRule::new(libc::SYS_io_uring_setup), + BpfRule::new(libc::SYS_io_uring_register), + BpfRule::new(libc::SYS_io_uring_enter), BpfRule::new(libc::SYS_dup), BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 1803a5723..baae66d99 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -794,6 +794,8 @@ impl StdMachine { boot_index: args.boot_index, chardev: None, socket_path: None, + // TODO Add aio option by qmp. + aio: None, }; dev.check()?; dev @@ -1173,6 +1175,7 @@ impl DeviceInterface for StdMachine { read_only, direct, iops: args.iops, + aio: None, }; if let Err(e) = config.check() { diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 61c2c25fc..2e163a2f8 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 62 syscalls -/// * x86_64-unknown-musl: 61 syscalls +/// * x86_64-unknown-gnu: 65 syscalls +/// * x86_64-unknown-musl: 64 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -70,6 +70,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_wait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), + BpfRule::new(libc::SYS_io_uring_setup), + BpfRule::new(libc::SYS_io_uring_register), + BpfRule::new(libc::SYS_io_uring_enter), BpfRule::new(libc::SYS_dup), BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index c96083736..551a2123a 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -149,7 +149,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("drive") .multiple(true) .long("drive") - .value_name("file=path,id=str[,readonly=][,direct=][,serial=][,iothread=][iops=]") + .value_name("file=path,id=str[,readonly=][,direct=][,serial=][,iothread=][iops=][,aio=native|io_uring]") .help("use 'file' as a drive image") .takes_values(true), ) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 109d887be..b7c597da9 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -17,6 +17,7 @@ use std::path::Path; use error_chain::bail; use log::error; use serde::{Deserialize, Serialize}; +use util::aio::{AIO_IOURING, AIO_NATIVE}; use super::{ errors::{ErrorKind, Result}, @@ -45,6 +46,7 @@ pub struct BlkDevConfig { pub boot_index: Option, pub chardev: Option, pub socket_path: Option, + pub aio: Option, } #[derive(Debug, Clone)] @@ -68,6 +70,7 @@ impl Default for BlkDevConfig { boot_index: None, chardev: None, socket_path: None, + aio: None, } } } @@ -82,6 +85,7 @@ pub struct DriveConfig { pub read_only: bool, pub direct: bool, pub iops: Option, + pub aio: Option, } impl Default for DriveConfig { @@ -92,6 +96,7 @@ impl Default for DriveConfig { read_only: false, direct: true, iops: None, + aio: None, } } } @@ -245,6 +250,14 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { drive.direct = direct.into(); } drive.iops = cmd_parser.get_value::("throttling.iops-total")?; + drive.aio = if let Some(aio) = cmd_parser.get_value::("aio")? { + if aio != AIO_NATIVE && aio != AIO_IOURING { + bail!("Invalid aio configure") + } + Some(aio) + } else { + Some(AIO_NATIVE.to_string()) + }; Ok(drive) } @@ -300,6 +313,7 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result Result<()> { +/// Implements the AioContext for libaio. +impl AioContext for LibaioContext { + /// Submit requests. + fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; if ret < 0 { bail!("Failed to submit aio, return {}.", ret); @@ -114,7 +108,8 @@ impl LibaioContext { Ok(()) } - pub fn get_events(&self) -> (&[IoEvent], u32, u32) { + /// Get the IO events. + fn get_events(&mut self) -> (&[IoEvent], u32, u32) { let ring = self.ctx as *mut AioRing; let head = unsafe { (*ring).head }; let tail = unsafe { (*ring).tail }; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index d7dafdcb0..ba58a32e9 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -12,22 +12,47 @@ mod libaio; mod raw; +mod uring; use std::clone::Clone; use std::marker::{Send, Sync}; use std::os::unix::io::{AsRawFd, RawFd}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use vmm_sys_util::eventfd::EventFd; -use super::errors::Result; +use super::errors::{Result, ResultExt}; use super::link_list::{List, Node}; pub use libaio::*; pub use raw::*; +use uring::IoUringContext; type CbList = List>; type CbNode = Node>; +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Default)] +pub struct IoEvent { + pub data: u64, + pub obj: u64, + pub res: i64, + pub res2: i64, +} + +/// Io-uring aio type. +pub const AIO_IOURING: &str = "io_uring"; +/// Native aio type. +pub const AIO_NATIVE: &str = "native"; + +/// The trait for Asynchronous IO operation. +trait AioContext { + /// Submit IO requests to the OS. + fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()>; + /// Get the IO events of the requests sumbitted earlier. + fn get_events(&mut self) -> (&[IoEvent], u32, u32); +} + pub type AioCompleteFunc = Box, i64) + Sync + Send>; pub struct AioCb { @@ -57,7 +82,7 @@ impl AioCb { } pub struct Aio { - pub ctx: Arc, + ctx: Arc>, pub fd: EventFd, pub aio_in_queue: CbList, pub aio_in_flight: CbList, @@ -66,12 +91,28 @@ pub struct Aio { } impl Aio { - pub fn new(func: Arc>) -> Result { - let max_events = 128; + pub fn new(func: Arc>, engine: Option<&String>) -> Result { + let max_events: usize = 128; + let fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let aio = if let Some(engine) = engine { + engine + } else { + AIO_NATIVE + }; + + let ctx: Arc> = if aio == AIO_IOURING { + Arc::new(Mutex::new(IoUringContext::new( + max_events as u32, + &fd, + func.clone(), + )?)) + } else { + Arc::new(Mutex::new(LibaioContext::new(max_events as i32)?)) + }; Ok(Aio { - ctx: Arc::new(LibaioContext::new(max_events as i32)?), - fd: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + ctx, + fd, aio_in_queue: List::new(), aio_in_flight: List::new(), max_events, @@ -80,12 +121,14 @@ impl Aio { } pub fn handle(&mut self) -> Result { - let (evts, start, end) = self.ctx.get_events(); let mut done = false; + let mut ctx = self.ctx.lock().unwrap(); + let (evts, start, end) = ctx.get_events(); + for e in start..end { if evts[e as usize].res2 == 0 { + done = true; unsafe { - done = true; let node = evts[e as usize].data as *mut CbNode; (self.complete_func)(&(*node).value, evts[e as usize].res); @@ -100,6 +143,8 @@ impl Aio { } } } + // Drop reference of 'ctx', so below 'process_list' can work. + drop(ctx); self.process_list().map(|_v| Ok(done))? } @@ -118,7 +163,11 @@ impl Aio { } if !iocbs.is_empty() { - return self.ctx.submit(iocbs.len() as i64, &mut iocbs); + return self + .ctx + .lock() + .unwrap() + .submit(iocbs.len() as i64, &mut iocbs); } } diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs new file mode 100644 index 000000000..7ce2a5ff9 --- /dev/null +++ b/util/src/aio/uring.rs @@ -0,0 +1,119 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use libc; +use std::os::unix::io::AsRawFd; +use std::sync::Arc; + +use error_chain::bail; +use io_uring::{opcode, squeue, types, IoUring}; +use vmm_sys_util::eventfd::EventFd; + +use super::libaio::{IoCb, IoCmd}; +use super::{AioCb, AioCompleteFunc, AioContext, CbNode, IoEvent, Result, ResultExt}; + +/// The io-uring context. +pub(crate) struct IoUringContext { + ring: IoUring, + events: Vec, + + #[allow(dead_code)] + // Only used to refering type T. + func: Arc>, +} + +impl IoUringContext { + pub fn new(entries: u32, eventfd: &EventFd, func: Arc>) -> Result { + let tmp_entries = entries as i32; + // Ensure the power of 2. + if (tmp_entries & -tmp_entries) != tmp_entries || tmp_entries == 0 { + bail!("Entries must be the power of 2 and larger than 0"); + } + let ring = + IoUring::new(entries as u32).chain_err(|| "Failed to create io_uring instance")?; + + ring.submitter() + .register_eventfd(eventfd.as_raw_fd()) + .chain_err(|| "Failed to register event fd")?; + let events = Vec::with_capacity(entries as usize); + Ok(IoUringContext { ring, func, events }) + } +} + +impl AioContext for IoUringContext { + #[allow(clippy::zero_ptr)] + /// Submit requests to OS. + fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { + for iocb in iocbp.iter() { + let offset = unsafe { (*(*iocb)).aio_offset as libc::off_t }; + let node = unsafe { (*(*iocb)).data as *mut CbNode }; + let aiocb = unsafe { &mut (*node).value as *mut AioCb }; + let raw_fd = unsafe { (*(*iocb)).aio_fildes as i32 }; + let data = unsafe { (*(*iocb)).data }; + let code = unsafe { (*aiocb).opcode }; + let len = unsafe { (*(*iocb)).aio_nbytes }; + let iovs = unsafe { (*(*iocb)).aio_buf }; + let fd = types::Fd(raw_fd); + let entry = match code { + IoCmd::Preadv => opcode::Readv::new(fd, iovs as *const libc::iovec, len as u32) + .offset(offset) + .build() + .flags(squeue::Flags::ASYNC) + .user_data(data), + IoCmd::Pwritev => opcode::Writev::new(fd, iovs as *const libc::iovec, len as u32) + .offset(offset) + .build() + .flags(squeue::Flags::ASYNC) + .user_data(data), + IoCmd::Fdsync => opcode::Fsync::new(fd) + .build() + .flags(squeue::Flags::ASYNC) + .user_data(data), + _ => { + bail!("Invalid entry code"); + } + }; + unsafe { + self.ring + .submission() + .push(&entry) + .chain_err(|| "Failed to push entry")?; + } + } + self.ring + .submit_and_wait(nr as usize) + .chain_err(|| "Failed to submit sqe")?; + Ok(()) + } + + /// Get the events. + fn get_events(&mut self) -> (&[IoEvent], u32, u32) { + let mut queue = self.ring.completion(); + self.events.clear(); + let l = queue.len(); + for _i in 0..l { + match queue.next() { + None => break, + Some(cqe) => { + let event = IoEvent { + data: cqe.user_data(), + obj: 0, + res: cqe.result() as i64, + res2: 0, + }; + self.events.push(event); + } + } + } + (&self.events, 0, self.events.len() as u32) + } +} diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 62eb4272f..6eb98e58c 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -559,7 +559,7 @@ impl BlockIoHandler { Ok(done) } - fn build_aio(&self) -> Result>> { + fn build_aio(&self, engine: Option<&String>) -> Result>> { let complete_func = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { let status = if ret < 0 { ret @@ -610,7 +610,7 @@ impl BlockIoHandler { } }) as AioCompleteFunc); - Ok(Box::new(Aio::new(complete_func)?)) + Ok(Box::new(Aio::new(complete_func, engine)?)) } fn update_evt_handler(&mut self) { @@ -1133,7 +1133,7 @@ impl VirtioDevice for Block { leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), }; - handler.aio = Some(handler.build_aio()?); + handler.aio = Some(handler.build_aio(self.blk_cfg.aio.as_ref())?); EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), -- Gitee From 78dd5b6f29be66372b7c9cc617036db7141bed9d Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 14 Oct 2022 11:03:45 +0800 Subject: [PATCH 0216/2187] VNC: Send color map When the pixel format uses a colour map, it tells the clients that the specified pixel values should be mapped to the given RGB intensities. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 103 +++++++++++++++++++++++++++++++--------------- vnc/src/vnc.rs | 2 +- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 1ba203c86..a254ed69a 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -30,12 +30,13 @@ use vmm_sys_util::epoll::EventSet; use crate::{ framebuffer_upadate, get_image_height, get_image_width, round_up_div, set_area_dirty, - update_client_surface, AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, + update_client_surface, AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, }; const MAX_RECVBUF_LEN: usize = 1024; +const NUM_OF_COLORMAP: u16 = 256; // VNC encodings types. pub const ENCODING_RAW: i32 = 0; @@ -85,6 +86,7 @@ pub enum ClientMsg { /// Server to client message in Remote Framebuffer Protocol. pub enum ServerMsg { FramebufferUpdate = 0, + SetColourMapEntries = 1, } impl From for ClientMsg { @@ -646,6 +648,34 @@ impl VncClient { Ok(()) } + /// Tell the client that the specified pixel values should be + /// mapped to the given RGB intensities. + fn send_color_map(&mut self) { + let mut buf: Vec = Vec::new(); + buf.append( + &mut (ServerMsg::SetColourMapEntries as u8) + .to_be_bytes() + .to_vec(), + ); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + // First color. + buf.append(&mut (0_u16).to_be_bytes().to_vec()); + // Number of colors. + buf.append(&mut (NUM_OF_COLORMAP as u16).to_be_bytes().to_vec()); + + let pf = self.pixel_format.clone(); + for i in 0..NUM_OF_COLORMAP as u16 { + let r = ((i >> pf.red.shift) & pf.red.max as u16) << (16 - pf.red.bits); + let g = ((i >> pf.green.shift) & pf.green.max as u16) << (16 - pf.green.bits); + let b = ((i >> pf.blue.shift) & pf.blue.max as u16) << (16 - pf.blue.bits); + buf.append(&mut (r as u16).to_be_bytes().to_vec()); + buf.append(&mut (g as u16).to_be_bytes().to_vec()); + buf.append(&mut (b as u16).to_be_bytes().to_vec()); + } + + self.write_msg(&buf); + } + /// Set image format. fn set_pixel_format(&mut self) -> Result<()> { if self.expect == 1 { @@ -653,46 +683,51 @@ impl VncClient { return Ok(()); } - info!("Set pixel format"); - let mut buf = self.buffpool.read_front(self.expect).to_vec(); - if buf[7] == 0 { - // Set a default 256 color map. - // Bit per pixel. - buf[4] = 8; - // Max bit of red. - buf[8] = 7; - // Max bit of green. - buf[10] = 7; - // Max bit of blue. - buf[12] = 3; - // Red shift. - buf[14] = 0; - // Green shift. - buf[15] = 3; - // Blue shift. - buf[16] = 6; - } - - if ![8, 16, 32].contains(&buf[4]) { + let buf = self.buffpool.read_front(self.expect).to_vec(); + let mut bit_per_pixel: u8 = buf[4]; + let big_endian_flag = buf[6]; + let true_color_flag: u8 = buf[7]; + let mut red_max: u16 = u16::from_be_bytes([buf[8], buf[9]]); + let mut green_max: u16 = u16::from_be_bytes([buf[10], buf[11]]); + let mut blue_max: u16 = u16::from_be_bytes([buf[12], buf[13]]); + let mut red_shift: u8 = buf[14]; + let mut green_shift: u8 = buf[15]; + let mut blue_shift: u8 = buf[16]; + if true_color_flag == 0 { + bit_per_pixel = 8; + red_max = 7; + green_max = 7; + blue_max = 3; + red_shift = 0; + green_shift = 3; + blue_shift = 6; + } + + // Verify the validity of pixel format. + // bit_per_pixel: Bits occupied by each pixel. + if ![8, 16, 32].contains(&bit_per_pixel) { self.dis_conn = true; error!("Worng format of bits_per_pixel"); return Err(ErrorKind::ProtocolMessageFailed(String::from("set pixel format")).into()); } - self.pixel_format - .red - .set_color_info(buf[14], u16::from_be_bytes([buf[8], buf[9]])); + self.pixel_format.red.set_color_info(red_shift, red_max); self.pixel_format .green - .set_color_info(buf[15], u16::from_be_bytes([buf[10], buf[11]])); - self.pixel_format - .blue - .set_color_info(buf[16], u16::from_be_bytes([buf[12], buf[13]])); - self.pixel_format.pixel_bits = buf[4]; - self.pixel_format.pixel_bytes = buf[4] / 8; - self.pixel_format.depth = if buf[4] == 32 { 24 } else { buf[4] }; - self.big_endian = buf[6] != 0; - + .set_color_info(green_shift, green_max); + self.pixel_format.blue.set_color_info(blue_shift, blue_max); + self.pixel_format.pixel_bits = bit_per_pixel; + self.pixel_format.pixel_bytes = bit_per_pixel / BIT_PER_BYTE as u8; + // Standard pixel format, depth is equal to 24. + self.pixel_format.depth = if bit_per_pixel == 32 { + 24 + } else { + bit_per_pixel + }; + self.big_endian = big_endian_flag != 0; + if true_color_flag == 0 { + self.send_color_map(); + } if !self.pixel_format.is_default_pixel_format() { self.pixel_convert = true; } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index c8f8955bb..f0017658e 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -49,7 +49,7 @@ pub const VNC_BITMAP_WIDTH: u64 = round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) * u64::BITS as u64; const DEFAULT_REFRESH_INTERVAL: u64 = 30; -const BIT_PER_BYTE: u32 = 8; +pub const BIT_PER_BYTE: u32 = 8; const MILLI_PER_SEC: u64 = 1_000_000; pub const DISPLAY_UPDATE_INTERVAL_DEFAULT: u32 = 30; pub const DISPLAY_UPDATE_INTERVAL_INC: u32 = 50; -- Gitee From 2144997aa379b4b3a9a07a5ad0ee39ee781c1ab1 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 15:37:58 +0800 Subject: [PATCH 0217/2187] pci: bridge: fix bridge control register write masks it missed masking bits 7-11 for bridge control register, fix that. Signed-off-by: Zhang Bo --- pci/src/config.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 4cca1138c..9fc054c73 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -149,7 +149,11 @@ const BRIDGE_CTL_ISA_ENABLE: u16 = 0x0004; const BRIDGE_CTL_VGA_ENABLE: u16 = 0x0008; const BRIDGE_CTL_VGA_16BIT_DEC: u16 = 0x0010; const BRIDGE_CTL_SEC_BUS_RESET: u16 = 0x0040; +const BRIDGE_CTL_FAST_BACK: u16 = 0x0080; +const BRIDGE_CTL_DISCARD_TIMER: u16 = 0x0100; +const BRIDGE_CTL_SEC_DISCARD_TIMER: u16 = 0x0200; const BRIDGE_CTL_DISCARD_TIMER_STATUS: u16 = 0x0400; +const BRIDGE_CTL_DISCARD_TIMER_SERR_E: u16 = 0x0800; pub const COMMAND_BUS_MASTER: u16 = 0x0004; const COMMAND_SERR_ENABLE: u16 = 0x0100; @@ -406,7 +410,11 @@ impl PciConfig { | BRIDGE_CTL_ISA_ENABLE | BRIDGE_CTL_VGA_ENABLE | BRIDGE_CTL_VGA_16BIT_DEC - | BRIDGE_CTL_SEC_BUS_RESET, + | BRIDGE_CTL_SEC_BUS_RESET + | BRIDGE_CTL_FAST_BACK + | BRIDGE_CTL_DISCARD_TIMER + | BRIDGE_CTL_SEC_DISCARD_TIMER + | BRIDGE_CTL_DISCARD_TIMER_SERR_E, )?; Ok(()) } -- Gitee From 22cc2aef30c28ab685c0675bc1e3daa4c844dd28 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 17:35:09 +0800 Subject: [PATCH 0218/2187] pci: seperate slot cap and slot stat registers it confused about slot cap/control/stat registers inside pcie capability. make it more clear now. no function change. Signed-off-by: Zhang Bo --- pci/src/config.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 9fc054c73..bfb0c13fe 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -103,14 +103,20 @@ pub const PCI_EXP_SLTCTL_ABPE: u16 = 0x0001; pub const PCI_EXP_SLTCTL_PDCE: u16 = 0x0008; /// Command Completed Interrupt Enable pub const PCI_EXP_SLTCTL_CCIE: u16 = 0x0010; +/// Hot-Plug Interrupt Enable +pub const PCI_EXP_SLTCTL_HPIE: u16 = 0x0020; /// Power Indicator off pub const PCI_EXP_SLTCTL_PWR_IND_OFF: u16 = 0x0300; /// Power Indicator on pub const PCI_EXP_SLTCTL_PWR_IND_ON: u16 = 0x0100; /// Slot Status pub const PCI_EXP_SLTSTA: u16 = 26; +/// Attention Button Pressed +pub const PCI_EXP_SLTSTA_ABP: u16 = 0x0001; /// Presence Detect Changed pub const PCI_EXP_SLTSTA_PDC: u16 = 0x0008; +/// Command Completed +pub const PCI_EXP_SLTSTA_CC: u16 = 0x0010; /// Presence Detect State pub const PCI_EXP_SLTSTA_PDS: u16 = 0x0040; @@ -219,14 +225,7 @@ const PCIE_CAP_SLOT_AIC_OFF: u16 = 0x00c0; // Power Indicator Control. const PCIE_CAP_SLOT_PIC_MASK: u16 = 0x0300; const PCIE_CAP_SLOT_PIC_OFF: u16 = 0x0300; -// Attention button pressed enable. -const PCIE_CAP_SLOT_ABP: u16 = 0x0001; -// Presence detect changed enable. -const PCIE_CAP_SLOT_PDC: u16 = 0x0008; -// Command completed interrupt enable. -const PCIE_CAP_SLOT_CCI: u16 = 0x0010; -// Hot-Plug interrupt enable. -const PCIE_CAP_SLOT_HPI: u16 = 0x0020; + // Power controller control. const PCIE_CAP_SLOT_PCC: u16 = 0x0400; // Electromechanical interlock control. @@ -798,10 +797,10 @@ impl PciConfig { le_write_u16( &mut self.write_mask, offset, - PCIE_CAP_SLOT_ABP - | PCIE_CAP_SLOT_PDC - | PCIE_CAP_SLOT_CCI - | PCIE_CAP_SLOT_HPI + PCI_EXP_SLTCTL_ABPE + | PCI_EXP_SLTCTL_PDCE + | PCI_EXP_SLTCTL_CCIE + | PCI_EXP_SLTCTL_HPIE | PCIE_CAP_SLOT_AIC_MASK | PCIE_CAP_SLOT_PIC_MASK | PCIE_CAP_SLOT_PCC @@ -811,7 +810,7 @@ impl PciConfig { le_write_u16( &mut self.write_clear_mask, offset, - PCIE_CAP_SLOT_ABP | PCIE_CAP_SLOT_PDC | PCIE_CAP_SLOT_CCI, + PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC, )?; offset = cap_offset + PcieCap::RootCtl as usize; -- Gitee From 773cf08d9dcba84b375afa39c577c872b7a13e34 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 18:54:42 +0800 Subject: [PATCH 0219/2187] pci: set power controller control to 0 to indicate power on in pcie specification v6, chapter 7.5.3.10, power controller control in the slot control register with 0 meaning power on, while 1 meaning power off, the init function of pcie cap mistakenly set it, fix that. Signed-off-by: Zhang Bo --- pci/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index bfb0c13fe..b33bbdf49 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -792,7 +792,7 @@ impl PciConfig { le_write_u16( &mut self.config, offset, - PCIE_CAP_SLOT_AIC_OFF | PCIE_CAP_SLOT_PIC_OFF | PCIE_CAP_SLOT_PCC, + PCIE_CAP_SLOT_AIC_OFF | PCIE_CAP_SLOT_PIC_OFF, )?; le_write_u16( &mut self.write_mask, -- Gitee From 7ae052cf4799ea9bb41fe8c5451040453e1d4fa0 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 19:09:02 +0800 Subject: [PATCH 0220/2187] pcie: fix naming mismatch of link width and speed link width has value of 1,2,4,8,16,32, and link speed has value of 2.5, 5, 8, 16, 32, 64(GT/s), it mistakenly fused them. fix that. Signed-off-by: Zhang Bo --- pci/src/config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index b33bbdf49..b545c8113 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -201,9 +201,9 @@ const PCIE_CAP_LINK_LBNC: u32 = 0x0020_0000; const PCIE_CAP_LINK_DLLLARC: u32 = 0x0010_0000; const PCIE_CAP_PORT_NUM_SHIFT: u8 = 24; // Current link speed. -const PCIE_CAP_CLS_X1: u16 = 0x0001; +const PCIE_CAP_CLS_2_5G: u16 = 0x0001; // Negotiated link width. -const PCIE_CAP_NLW_2_5GT: u16 = 0x0010; +const PCIE_CAP_NLW_X1: u16 = 0x0010; // Attention button present. const PCIE_CAP_SLOTCAP_ABP: u32 = 0x0000_0001; // Power controller present. @@ -771,7 +771,7 @@ impl PciConfig { le_write_u16( &mut self.config, offset, - PCIE_CAP_CLS_X1 | PCIE_CAP_NLW_2_5GT, + PCIE_CAP_CLS_2_5G | PCIE_CAP_NLW_X1, )?; let slot: u8 = devfn >> BDF_FUNC_SHIFT; -- Gitee From d2834d1db02eb2f4f26f6425cc300a16ca9a4a2a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 21 Oct 2022 15:53:12 +0800 Subject: [PATCH 0221/2187] pci: fix wrong link band width value during hotplug it used 0x3f0 as bandwidth which is reserved and has no meaning, according to PCIe specification v6, table 7-26. In fact, the valid values are among 1,2,4,8,16, use 1 which is also used during initialization. Signed-off-by: Zhang Bo --- pci/src/config.rs | 2 +- pci/src/root_port.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index b545c8113..59311e735 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -203,7 +203,7 @@ const PCIE_CAP_PORT_NUM_SHIFT: u8 = 24; // Current link speed. const PCIE_CAP_CLS_2_5G: u16 = 0x0001; // Negotiated link width. -const PCIE_CAP_NLW_X1: u16 = 0x0010; +pub const PCIE_CAP_NLW_X1: u16 = 0x0010; // Attention button present. const PCIE_CAP_SLOTCAP_ABP: u32 = 0x0000_0001; // Power controller present. diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index a59d8fa27..8ab1736d8 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -26,8 +26,8 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, - PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, - PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, + PCIE_CAP_NLW_X1, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, + PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, @@ -458,7 +458,7 @@ impl HotplugOps for RootPort { le_write_set_value_u16( &mut self.config.config, (offset + PCI_EXP_LNKSTA) as usize, - PCI_EXP_LNKSTA_NLW | PCI_EXP_LNKSTA_DLLLA, + PCIE_CAP_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, )?; self.hotplug_event_notify(); } -- Gitee From 073a78a648db9d979c4ecac0f8978dfc13b0ba66 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 21 Oct 2022 15:58:51 +0800 Subject: [PATCH 0222/2187] pcie: set link speed during hotpluging link speed, just like bandwidth, should also be initialized during hotpluging, which is similar to the device initialization, although the codes in linux guest kernel only care about bandwidth. Signed-off-by: Zhang Bo --- pci/src/config.rs | 2 +- pci/src/root_port.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 59311e735..ed50762fc 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -201,7 +201,7 @@ const PCIE_CAP_LINK_LBNC: u32 = 0x0020_0000; const PCIE_CAP_LINK_DLLLARC: u32 = 0x0010_0000; const PCIE_CAP_PORT_NUM_SHIFT: u8 = 24; // Current link speed. -const PCIE_CAP_CLS_2_5G: u16 = 0x0001; +pub const PCIE_CAP_CLS_2_5G: u16 = 0x0001; // Negotiated link width. pub const PCIE_CAP_NLW_X1: u16 = 0x0010; // Attention button present. diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 8ab1736d8..1778162b6 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -26,11 +26,11 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, - PCIE_CAP_NLW_X1, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, - PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, - PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, - PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, - PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, + PCIE_CAP_CLS_2_5G, PCIE_CAP_NLW_X1, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, + PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, + PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; use crate::errors::{Result, ResultExt}; @@ -458,7 +458,7 @@ impl HotplugOps for RootPort { le_write_set_value_u16( &mut self.config.config, (offset + PCI_EXP_LNKSTA) as usize, - PCIE_CAP_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, + PCIE_CAP_CLS_2_5G | PCIE_CAP_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, )?; self.hotplug_event_notify(); } -- Gitee From 28782065d33f5a08e11bacde15cad6233f87b53d Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 20:17:02 +0800 Subject: [PATCH 0223/2187] pcie: avoid msix table and pending table overlapping when the upside module such as virtio devices set msix table incorrectly, they may overlap the msix vector table and the pending table, avoid that. Signed-off-by: Zhang Bo --- pci/src/msix.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index fd043f1ca..28ad7bc21 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -24,7 +24,8 @@ use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; use crate::config::{CapId, PciConfig, RegionType, SECONDARY_BUS_NUM}; use crate::errors::{Result, ResultExt}; use crate::{ - le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, PciBus, + le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, + ranges_overlap, PciBus, }; pub const MSIX_TABLE_ENTRY_SIZE: u16 = 16; @@ -458,16 +459,24 @@ pub fn init_msix( )?; offset = msix_cap_offset + MSIX_CAP_TABLE as usize; let table_size = vector_nr * MSIX_TABLE_ENTRY_SIZE as u32; + let pba_size = ((round_up(vector_nr as u64, 64).unwrap() / 64) * 8) as u32; let (table_offset, pba_offset) = if let Some((table, pba)) = offset_opt { (table, pba) } else { (0, table_size) }; + if ranges_overlap( + table_offset.try_into().unwrap(), + table_size.try_into().unwrap(), + pba_offset.try_into().unwrap(), + pba_size.try_into().unwrap(), + ) { + bail!("msix table and pba table overlapped."); + } le_write_u32(&mut config.config, offset, table_offset | bar_id as u32)?; offset = msix_cap_offset + MSIX_CAP_PBA as usize; le_write_u32(&mut config.config, offset, pba_offset | bar_id as u32)?; - let pba_size = ((round_up(vector_nr as u64, 64).unwrap() / 64) * 8) as u32; let msix = Arc::new(Mutex::new(Msix::new( table_size, pba_size, -- Gitee From cd45e9f6111af5a2374c24bb43de8e8bc315c953 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 20 Oct 2022 20:22:33 +0800 Subject: [PATCH 0224/2187] pcie: disable electronical interlock as we have not realize it electronical interlock is used to avoid hardware hotplug confiliction, which means that hotplugging the device again before it finishes last hotplugging. it's an optional function and we have not realize it yet. do not tell the guest that we support it to avoid confusing it. Signed-off-by: Zhang Bo --- pci/src/config.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index ed50762fc..71743b53f 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -216,8 +216,6 @@ const PCIE_CAP_SLOTCAP_PIP: u32 = 0x0000_0010; const PCIE_CAP_SLOTCAP_HPS: u32 = 0x0000_0020; // Hot-Plug capable. const PCIE_CAP_SLOTCAP_HPC: u32 = 0x0000_0040; -// Electromechanical interlock present. -const PCIE_CAP_SLOTCAP_EIP: u32 = 0x0002_0000; const PCIE_CAP_SLOT_NUM_SHIFT: u32 = 19; // Attention Indicator Control. const PCIE_CAP_SLOT_AIC_MASK: u16 = 0x00c0; @@ -785,7 +783,6 @@ impl PciConfig { | PCIE_CAP_SLOTCAP_PIP | PCIE_CAP_SLOTCAP_HPS | PCIE_CAP_SLOTCAP_HPC - | PCIE_CAP_SLOTCAP_EIP | ((slot as u32) << PCIE_CAP_SLOT_NUM_SHIFT), )?; offset = cap_offset + PcieCap::SlotCtl as usize; -- Gitee From 5befa2e75c85fd78483d8fe2e685c9226c2f2036 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 22 Oct 2022 11:07:18 +0800 Subject: [PATCH 0225/2187] VNC: Adjust Package Reference in VNC Signed-off-by: Xiao Ye --- devices/src/legacy/ramfb.rs | 4 ++-- virtio/src/gpu.rs | 2 +- vnc/src/auth.rs | 2 +- vnc/src/client.rs | 20 ++++++++++++-------- vnc/src/input.rs | 2 +- vnc/src/lib.rs | 7 ------- vnc/src/server.rs | 26 +++++++++++++++++--------- vnc/src/vnc.rs | 20 +++++++++++++------- 8 files changed, 47 insertions(+), 36 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index efcac7ad2..cb3f57ba6 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -22,7 +22,7 @@ use std::sync::{Arc, Mutex}; use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use util::byte_code::ByteCode; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; -use vnc::{vnc_display_switch, vnc_loop_update_display, DisplaySurface}; +use vnc::vnc::{vnc_display_switch, vnc_loop_update_display, DisplaySurface}; const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; @@ -112,7 +112,7 @@ impl RamfbState { } }; - let mut ds = vnc::DisplaySurface { + let mut ds = DisplaySurface { format, ..Default::default() }; diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 76edef078..d9ab72b53 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -43,7 +43,7 @@ use util::pixman::{ }; use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use vnc::{ +use vnc::vnc::{ vnc_display_cursor, vnc_display_switch, vnc_display_update, DisplayMouse, DisplaySurface, }; diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index d9098c8c0..03cfec6da 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use super::errors::Result; -use crate::VncClient; +use crate::client::VncClient; /// Authentication type #[derive(Clone, Copy)] diff --git a/vnc/src/client.rs b/vnc/src/client.rs index a254ed69a..8dc086ffd 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -10,7 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; +use crate::{ + auth::{AuthState, SubAuthState}, + errors::{ErrorKind, Result}, + pixman::{get_image_height, get_image_width, PixelFormat}, + round_up_div, + server::VncServer, + utils::BuffPool, + vnc::{ + framebuffer_upadate, set_area_dirty, update_client_surface, BIT_PER_BYTE, DIRTY_PIXELS_NUM, + DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, + }, +}; use error_chain::ChainedError; use machine_manager::event_loop::EventLoop; use sscanf::scanf; @@ -28,13 +39,6 @@ use util::{ }; use vmm_sys_util::epoll::EventSet; -use crate::{ - framebuffer_upadate, get_image_height, get_image_width, round_up_div, set_area_dirty, - update_client_surface, AuthState, BuffPool, PixelFormat, SubAuthState, VncServer, BIT_PER_BYTE, - DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, - VNC_SERVERS, -}; - const MAX_RECVBUF_LEN: usize = 1024; const NUM_OF_COLORMAP: u16 = 256; diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 0c4700585..5218d67c7 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::VncClient; +use crate::client::VncClient; use usb::{ keyboard::keyboard_event, tablet::{pointer_event, pointer_sync}, diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 8b244e875..937a59cfd 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -61,13 +61,6 @@ pub mod pixman; pub mod server; pub mod utils; pub mod vnc; -pub use crate::vnc::*; -pub use auth::*; -pub use client::*; -pub use input::*; -pub use pixman::*; -pub use server::*; -pub use utils::BuffPool; pub const fn round_up_div(n: u64, d: u64) -> u64 { (n + d - 1) / d diff --git a/vnc/src/server.rs b/vnc/src/server.rs index d73b0500d..7ac257e22 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -10,7 +10,23 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::Result; +use crate::{ + auth::{AuthState, SubAuthState}, + client::VncClient, + data::keycode::KEYSYM2KEYCODE, + errors::Result, + pixman::{ + bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, + get_image_width, unref_pixman_image, + }, + round_up_div, + vnc::{ + update_client_surface, DisplayMouse, DIRTY_PIXELS_NUM, DISPLAY_UPDATE_INTERVAL_DEFAULT, + DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, MAX_WINDOW_HEIGHT, + MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, + }, +}; + use machine_manager::{ config::{ObjConfig, VncConfig}, event_loop::EventLoop, @@ -33,14 +49,6 @@ use util::{ }; use vmm_sys_util::epoll::EventSet; -use crate::{ - bytes_per_pixel, data::keycode::KEYSYM2KEYCODE, get_image_data, get_image_format, - get_image_height, get_image_stride, get_image_width, round_up_div, unref_pixman_image, - update_client_surface, AuthState, DisplayMouse, SubAuthState, VncClient, DIRTY_PIXELS_NUM, - DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, - MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, -}; - /// Info of image. /// stride is not always equal to stride because of memory alignment. pub struct ImageInfo { diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index f0017658e..796d29bcc 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -10,7 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; +use crate::{ + client::{ + RectInfo, Rectangle, ServerMsg, VncClient, VncFeatures, ENCODING_ALPHA_CURSOR, + ENCODING_RAW, ENCODING_RICH_CURSOR, + }, + errors::{ErrorKind, Result}, + pixman::{ + bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, + unref_pixman_image, PixelFormat, + }, + round_up, round_up_div, + server::VncServer, +}; use core::time; use machine_manager::{ config::{ObjConfig, VncConfig}, @@ -32,12 +44,6 @@ use util::{ }; use vmm_sys_util::eventfd::EventFd; -use crate::{ - bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, round_up, - round_up_div, unref_pixman_image, PixelFormat, RectInfo, Rectangle, ServerMsg, VncClient, - VncFeatures, VncServer, ENCODING_ALPHA_CURSOR, ENCODING_RAW, ENCODING_RICH_CURSOR, -}; - /// The number of dirty pixels represented bt one bit in dirty bitmap. pub const DIRTY_PIXELS_NUM: u16 = 16; /// The default max window width. -- Gitee From cf3375c27c52b3871520a6d5bd8a4f19dc4b4067 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 09:41:46 +0800 Subject: [PATCH 0226/2187] memory: add const KVM_MEM_READONLY from `kvm_bindings` crate Replace custom constants `MEM_READ_ONLY` with const KVM_MEM_READONLY from `kvm_bindings` crate. Signed-off-by: Xinle.Guo --- address_space/src/listener.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 9bfa33a1c..ebea08d39 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; -use kvm_bindings::kvm_userspace_memory_region; +use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_READONLY}; use kvm_ioctls::{IoEventAddress, NoDatamatch}; use log::{debug, warn}; use util::{num_ops::round_down, unix::host_page_size}; @@ -23,8 +23,6 @@ use util::{num_ops::round_down, unix::host_page_size}; use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{AddressRange, FlatRange, RegionIoEventFd, RegionType}; -const MEM_READ_ONLY: u32 = 1 << 1; - /// Request type of listener. #[derive(Debug, Copy, Clone)] pub enum ListenerReqType { @@ -251,7 +249,7 @@ impl KvmMemoryListener { let mut flags = 0_u32; if flat_range.owner.get_rom_device_romd().unwrap_or(false) { - flags |= MEM_READ_ONLY; + flags |= KVM_MEM_READONLY; } let kvm_region = kvm_userspace_memory_region { slot: slot_idx | (self.as_id.load(Ordering::SeqCst) << 16), -- Gitee From 97f9eec64ddee7ab90d36eb4405076b9501e83fb Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 09:55:15 +0800 Subject: [PATCH 0227/2187] memory: do not implement default functions for `Listener` trait Every structure that using `Listener` trait, must implement `enabled`, `enable`, `disable` by itself. If not, after calling unregister listener by `AddressSpace`, the memory listner can not be remove. Becase the default `enabled` function always reture true. Signed-off-by: Xinle.Guo --- address_space/src/address_space.rs | 77 ++++++++++++++++++++++++++++-- address_space/src/listener.rs | 52 +++++++++++++++----- virtio/src/balloon.rs | 14 ++++++ virtio/src/vhost/kernel/mod.rs | 14 ++++++ virtio/src/vhost/user/client.rs | 14 ++++++ 5 files changed, 155 insertions(+), 16 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index f58da34b5..6aca711f4 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -568,6 +568,7 @@ mod test { #[derive(Default, Clone)] struct TestListener { reqs: Arc>>, + enabled: bool, } impl Listener for TestListener { @@ -575,6 +576,18 @@ mod test { 2 } + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + fn handle_request( &self, range: Option<&FlatRange>, @@ -603,29 +616,85 @@ mod test { #[test] fn test_listeners() { // define an array of listeners in order to check the priority order - struct ListenerPrior0; + struct ListenerPrior0 { + enabled: bool, + } impl Listener for ListenerPrior0 { fn priority(&self) -> i32 { 0 } + + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + } + struct ListenerPrior3 { + enabled: bool, } - struct ListenerPrior3; impl Listener for ListenerPrior3 { fn priority(&self) -> i32 { 3 } + + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + } + struct ListenerPrior4 { + enable: bool, } - struct ListenerPrior4; impl Listener for ListenerPrior4 { fn priority(&self) -> i32 { 4 } + + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + } + struct ListenerNeg { + enable: bool, } - struct ListenerNeg; impl Listener for ListenerNeg { fn priority(&self) -> i32 { -1 } + + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } } let root = Region::init_container_region(8000); diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index ebea08d39..c5d45d605 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -41,15 +41,13 @@ pub trait Listener: Send + Sync { fn priority(&self) -> i32; /// Is this listener enabled to call. - fn enabled(&self) -> bool { - true - } + fn enabled(&self) -> bool; /// Enable listener for address space. - fn enable(&mut self) {} + fn enable(&mut self); /// Disable listener for address space. - fn disable(&mut self) {} + fn disable(&mut self); /// Function that handle request according to request-type. /// @@ -92,6 +90,8 @@ pub struct KvmMemoryListener { as_id: Arc, /// Record all MemSlots. slots: Arc>>, + /// Whether enabled as a memory listener. + enabled: bool, } impl KvmMemoryListener { @@ -104,6 +104,7 @@ impl KvmMemoryListener { KvmMemoryListener { as_id: Arc::new(AtomicU32::new(0)), slots: Arc::new(Mutex::new(vec![MemSlot::default(); nr_slots as usize])), + enabled: false, } } @@ -426,6 +427,21 @@ impl Listener for KvmMemoryListener { 10_i32 } + /// Is this listener enabled to call. + fn enabled(&self) -> bool { + self.enabled + } + + /// Enable listener for address space. + fn enable(&mut self) { + self.enabled = true; + } + + /// Disable listener for address space. + fn disable(&mut self) { + self.enabled = false; + } + /// Deal with the request. /// /// # Arguments @@ -462,13 +478,10 @@ impl Listener for KvmMemoryListener { } #[cfg(target_arch = "x86_64")] -pub struct KvmIoListener; - -#[cfg(target_arch = "x86_64")] -impl Default for KvmIoListener { - fn default() -> Self { - Self - } +#[derive(Default)] +pub struct KvmIoListener { + /// Whether enabled as a IO listener. + enabled: bool, } #[cfg(target_arch = "x86_64")] @@ -560,6 +573,21 @@ impl Listener for KvmIoListener { 10_i32 } + /// Is this listener enabled to call. + fn enabled(&self) -> bool { + self.enabled + } + + /// Enable listener for address space. + fn enable(&mut self) { + self.enabled = true; + } + + /// Disable listener for address space. + fn disable(&mut self) { + self.enabled = false; + } + /// Deal with the request. /// /// # Arguments diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index b58e4c254..aded05c7a 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -343,12 +343,14 @@ struct BlnMemoryRegion { struct BlnMemInfo { regions: Mutex>, + enabled: bool, } impl BlnMemInfo { fn new() -> BlnMemInfo { BlnMemInfo { regions: Mutex::new(Vec::new()), + enabled: false, } } @@ -440,6 +442,18 @@ impl Listener for BlnMemInfo { 0 } + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + fn handle_request( &self, range: Option<&FlatRange>, diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 3deef2ad2..76434780a 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -129,12 +129,14 @@ impl ByteCode for VhostMemory {} #[derive(Clone)] struct VhostMemInfo { regions: Arc>>, + enabled: bool, } impl VhostMemInfo { fn new() -> VhostMemInfo { VhostMemInfo { regions: Arc::new(Mutex::new(Vec::new())), + enabled: false, } } @@ -194,6 +196,18 @@ impl Listener for VhostMemInfo { 0 } + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + fn handle_request( &self, range: Option<&FlatRange>, diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index c1f41f9f3..b641cedec 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -191,6 +191,7 @@ struct RegionInfo { #[derive(Clone)] struct VhostUserMemInfo { regions: Arc>>, + enabled: bool, } #[allow(dead_code)] @@ -198,6 +199,7 @@ impl VhostUserMemInfo { fn new() -> Self { VhostUserMemInfo { regions: Arc::new(Mutex::new(Vec::new())), + enabled: false, } } @@ -285,6 +287,18 @@ impl Listener for VhostUserMemInfo { 0 } + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + fn handle_request( &self, range: Option<&FlatRange>, -- Gitee From 8b9aaa10b393e241ccc4362532aa679e0a627f9a Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 10:04:07 +0800 Subject: [PATCH 0228/2187] memory: add testcase for register/unregister listener Signed-off-by: Xinle.Guo --- address_space/src/address_space.rs | 72 +++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 6aca711f4..60e945f0d 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -616,6 +616,7 @@ mod test { #[test] fn test_listeners() { // define an array of listeners in order to check the priority order + #[derive(Default)] struct ListenerPrior0 { enabled: bool, } @@ -636,6 +637,7 @@ mod test { self.enabled = false; } } + #[derive(Default)] struct ListenerPrior3 { enabled: bool, } @@ -656,8 +658,9 @@ mod test { self.enabled = false; } } + #[derive(Default)] struct ListenerPrior4 { - enable: bool, + enabled: bool, } impl Listener for ListenerPrior4 { fn priority(&self) -> i32 { @@ -676,8 +679,9 @@ mod test { self.enabled = false; } } + #[derive(Default)] struct ListenerNeg { - enable: bool, + enabled: bool, } impl Listener for ListenerNeg { fn priority(&self) -> i32 { @@ -699,16 +703,16 @@ mod test { let root = Region::init_container_region(8000); let space = AddressSpace::new(root).unwrap(); - let listener1 = Arc::new(Mutex::new(ListenerPrior0)); - let listener2 = Arc::new(Mutex::new(ListenerPrior0)); - let listener3 = Arc::new(Mutex::new(ListenerPrior3)); - let listener4 = Arc::new(Mutex::new(ListenerPrior4)); - let listener5 = Arc::new(Mutex::new(ListenerNeg)); - space.register_listener(listener1).unwrap(); - space.register_listener(listener3).unwrap(); - space.register_listener(listener5).unwrap(); - space.register_listener(listener2).unwrap(); - space.register_listener(listener4).unwrap(); + let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); + let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); + let listener3 = Arc::new(Mutex::new(ListenerPrior3::default())); + let listener4 = Arc::new(Mutex::new(ListenerPrior4::default())); + let listener5 = Arc::new(Mutex::new(ListenerNeg::default())); + space.register_listener(listener1.clone()).unwrap(); + space.register_listener(listener3.clone()).unwrap(); + space.register_listener(listener5.clone()).unwrap(); + space.register_listener(listener2.clone()).unwrap(); + space.register_listener(listener4.clone()).unwrap(); let mut pre_prior = std::i32::MIN; for listener in space.listeners.lock().unwrap().iter() { @@ -716,6 +720,50 @@ mod test { assert!(pre_prior <= curr); pre_prior = curr; } + + space.unregister_listener(listener4).unwrap(); + assert_eq!(space.listeners.lock().unwrap().len(), 4); + space.unregister_listener(listener3).unwrap(); + // It only contains listener1, listener5, listener2. + assert_eq!(space.listeners.lock().unwrap().len(), 3); + } + + #[test] + fn test_unregister_listener() { + #[derive(Default)] + struct ListenerPrior0 { + enabled: bool, + } + impl Listener for ListenerPrior0 { + fn priority(&self) -> i32 { + 0 + } + + fn enabled(&self) -> bool { + self.enabled + } + + fn enable(&mut self) { + self.enabled = true; + } + + fn disable(&mut self) { + self.enabled = false; + } + } + + let root = Region::init_container_region(8000); + let space = AddressSpace::new(root).unwrap(); + let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); + let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); + space.register_listener(listener1.clone()).unwrap(); + space.register_listener(listener2.clone()).unwrap(); + + space.unregister_listener(listener2).unwrap(); + assert_eq!(space.listeners.lock().unwrap().len(), 1); + for listener in space.listeners.lock().unwrap().iter() { + assert_eq!(listener.lock().unwrap().enabled(), true); + } } #[test] -- Gitee From 6bca3ca0d4f18c9433cf581313c364d2093950a0 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 25 Oct 2022 11:04:58 +0800 Subject: [PATCH 0229/2187] memory: add RamDevice and RomDevice region for `start_addr` function RamDevice and RomDevice region can also provide host virtual address. Two type of region should be considered by `start_addr` function. Signed-off-by: Xinle.Guo --- address_space/src/region.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 470ec9d5b..00c3aa406 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -305,9 +305,13 @@ impl Region { } /// Returns the minimum address managed by the region. - /// If this region is not `Ram` type, this function will return `None`. + /// If this region is not `Ram`, `RamDevice`, `RomDevice` type, + /// this function will return `None`. pub fn start_addr(&self) -> Option { - if self.region_type != RegionType::Ram { + if self.region_type != RegionType::Ram + && self.region_type != RegionType::RamDevice + && self.region_type != RegionType::RomDevice + { return None; } -- Gitee From ddd843c18fb0e803cae32784797f26b9cda1456d Mon Sep 17 00:00:00 2001 From: MJY-HUST Date: Wed, 26 Oct 2022 10:48:20 +0800 Subject: [PATCH 0230/2187] replace error-chain with thiserror+anyhow --- Cargo.lock | 204 +++++++++-------- Cargo.toml | 3 +- acpi/Cargo.toml | 3 +- acpi/src/error.rs | 27 +++ acpi/src/lib.rs | 26 +-- acpi/src/table_loader.rs | 40 +++- address_space/Cargo.toml | 3 +- address_space/src/address_space.rs | 63 +++--- address_space/src/error.rs | 53 +++++ address_space/src/host_mmap.rs | 22 +- address_space/src/lib.rs | 57 +---- address_space/src/listener.rs | 94 ++++---- address_space/src/region.rs | 96 ++++---- address_space/src/state.rs | 16 +- boot_loader/Cargo.toml | 3 +- boot_loader/src/aarch64/mod.rs | 44 ++-- boot_loader/src/error.rs | 58 +++++ boot_loader/src/lib.rs | 58 +---- boot_loader/src/x86_64/bootparam.rs | 9 +- boot_loader/src/x86_64/direct_boot/gdt.rs | 12 +- boot_loader/src/x86_64/direct_boot/mod.rs | 36 +-- boot_loader/src/x86_64/direct_boot/mptable.rs | 6 +- boot_loader/src/x86_64/mod.rs | 3 +- boot_loader/src/x86_64/standard_boot/elf.rs | 28 ++- boot_loader/src/x86_64/standard_boot/mod.rs | 46 ++-- cpu/Cargo.toml | 3 +- cpu/src/aarch64/mod.rs | 26 ++- cpu/src/error.rs | 51 +++++ cpu/src/lib.rs | 129 ++++------- cpu/src/x86_64/mod.rs | 47 ++-- devices/Cargo.toml | 3 +- .../src/interrupt_controller/aarch64/gicv2.rs | 29 +-- .../src/interrupt_controller/aarch64/gicv3.rs | 48 ++-- .../src/interrupt_controller/aarch64/mod.rs | 24 +- .../src/interrupt_controller/aarch64/state.rs | 120 ++++++---- devices/src/interrupt_controller/error.rs | 23 ++ devices/src/interrupt_controller/mod.rs | 20 +- devices/src/legacy/chardev.rs | 14 +- devices/src/legacy/error.rs | 60 +++++ devices/src/legacy/fwcfg.rs | 83 +++---- devices/src/legacy/mod.rs | 62 +----- devices/src/legacy/pflash.rs | 159 ++++++++------ devices/src/legacy/pl011.rs | 21 +- devices/src/legacy/pl031.rs | 12 +- devices/src/legacy/ramfb.rs | 7 +- devices/src/legacy/rtc.rs | 7 +- devices/src/legacy/serial.rs | 33 ++- devices/src/lib.rs | 4 +- hypervisor/Cargo.toml | 3 +- hypervisor/src/error.rs | 28 +++ hypervisor/src/kvm/interrupt.rs | 8 +- hypervisor/src/kvm/mod.rs | 14 +- hypervisor/src/lib.rs | 15 +- ...Third_Party_Open_Source_Software_Notice.md | 22 +- machine/Cargo.toml | 3 +- machine/src/error.rs | 112 ++++++++++ machine/src/lib.rs | 205 ++++++------------ machine/src/micro_vm/error.rs | 48 ++++ machine/src/micro_vm/mod.rs | 160 +++++--------- machine/src/standard_vm/aarch64/mod.rs | 174 +++++++-------- .../src/standard_vm/aarch64/pci_host_root.rs | 3 +- machine/src/standard_vm/error.rs | 61 ++++++ machine/src/standard_vm/mod.rs | 101 +++------ machine/src/standard_vm/x86_64/ich9_lpc.rs | 21 +- machine/src/standard_vm/x86_64/mch.rs | 8 +- machine/src/standard_vm/x86_64/mod.rs | 157 +++++--------- machine/src/vm_state.rs | 7 +- machine_manager/Cargo.toml | 3 +- machine_manager/src/cmdline.rs | 16 +- machine_manager/src/config/balloon.rs | 12 +- machine_manager/src/config/boot_source.rs | 29 +-- machine_manager/src/config/chardev.rs | 90 +++++--- machine_manager/src/config/devices.rs | 2 +- machine_manager/src/config/drive.rs | 102 +++++---- machine_manager/src/config/error.rs | 57 +++++ machine_manager/src/config/fs.rs | 25 +-- machine_manager/src/config/gpu.rs | 19 +- machine_manager/src/config/incoming.rs | 5 +- machine_manager/src/config/iothread.rs | 20 +- machine_manager/src/config/machine_config.rs | 106 +++++---- machine_manager/src/config/mod.rs | 146 ++++--------- machine_manager/src/config/network.rs | 69 +++--- machine_manager/src/config/numa.rs | 38 ++-- machine_manager/src/config/pci.rs | 21 +- machine_manager/src/config/rng.rs | 23 +- machine_manager/src/config/usb.rs | 10 +- machine_manager/src/config/vfio.rs | 28 ++- machine_manager/src/config/vnc.rs | 16 +- machine_manager/src/error.rs | 32 +++ machine_manager/src/event_loop.rs | 11 +- machine_manager/src/lib.rs | 16 +- machine_manager/src/qmp/mod.rs | 8 +- machine_manager/src/socket.rs | 5 +- migration/Cargo.toml | 3 +- migration/src/error.rs | 71 ++++++ migration/src/general.rs | 23 +- migration/src/lib.rs | 85 +------- migration/src/manager.rs | 8 +- migration/src/migration.rs | 78 ++++--- migration/src/protocol.rs | 61 ++++-- migration/src/snapshot.rs | 42 ++-- ozone/Cargo.toml | 3 +- ozone/src/capability.rs | 17 +- ozone/src/cgroup.rs | 37 ++-- ozone/src/error.rs | 36 +++ ozone/src/handler.rs | 74 +++---- ozone/src/main.rs | 52 +++-- ozone/src/namespace.rs | 32 +-- ozone/src/syscall.rs | 2 +- pci/Cargo.toml | 3 +- pci/src/bus.rs | 13 +- pci/src/config.rs | 19 +- pci/src/error.rs | 34 +++ pci/src/host.rs | 11 +- pci/src/hotplug.rs | 4 +- pci/src/lib.rs | 35 +-- pci/src/msix.rs | 21 +- pci/src/root_port.rs | 41 ++-- src/main.rs | 95 +++++--- sysbus/Cargo.toml | 3 +- sysbus/src/error.rs | 32 +++ sysbus/src/lib.rs | 28 +-- usb/Cargo.toml | 3 +- usb/src/bus.rs | 2 +- usb/src/descriptor.rs | 2 +- usb/src/error.rs | 32 +++ usb/src/keyboard.rs | 2 +- usb/src/lib.rs | 18 +- usb/src/tablet.rs | 2 +- usb/src/usb.rs | 4 +- usb/src/xhci/xhci_controller.rs | 16 +- usb/src/xhci/xhci_pci.rs | 27 ++- usb/src/xhci/xhci_regs.rs | 34 +-- usb/src/xhci/xhci_ring.rs | 2 +- util/Cargo.toml | 3 +- util/src/aio/libaio.rs | 2 +- util/src/aio/mod.rs | 2 +- util/src/aio/raw.rs | 2 +- util/src/aio/uring.rs | 12 +- util/src/arg_parser.rs | 37 ++-- util/src/bitmap.rs | 25 ++- util/src/daemonize.rs | 15 +- util/src/device_tree.rs | 28 ++- util/src/error.rs | 89 ++++++++ util/src/leak_bucket.rs | 2 +- util/src/lib.rs | 121 +---------- util/src/loop_context.rs | 30 +-- util/src/seccomp.rs | 4 +- util/src/syscall.rs | 4 +- util/src/tap.rs | 27 +-- util/src/trace.rs | 18 +- util/src/unix.rs | 19 +- vfio/Cargo.toml | 3 +- vfio/src/error.rs | 36 +++ vfio/src/lib.rs | 23 +- vfio/src/vfio_dev.rs | 132 +++++------ vfio/src/vfio_pci.rs | 88 ++++---- vhost_user_fs/Cargo.toml | 3 +- vhost_user_fs/src/cmdline.rs | 6 +- vhost_user_fs/src/error.rs | 32 +++ vhost_user_fs/src/fs.rs | 10 +- vhost_user_fs/src/fuse_msg.rs | 19 +- vhost_user_fs/src/fuse_proc.rs | 141 ++++-------- vhost_user_fs/src/fuse_req.rs | 6 +- vhost_user_fs/src/lib.rs | 16 +- vhost_user_fs/src/main.rs | 69 ++++-- vhost_user_fs/src/vhost_user_fs.rs | 28 +-- vhost_user_fs/src/vhost_user_server.rs | 41 ++-- vhost_user_fs/src/virtio_fs.rs | 42 ++-- virtio/Cargo.toml | 3 +- virtio/src/balloon.rs | 107 +++++---- virtio/src/block.rs | 162 +++++++------- virtio/src/console.rs | 53 ++--- virtio/src/error.rs | 75 +++++++ virtio/src/gpu.rs | 170 ++++++++------- virtio/src/lib.rs | 86 +------- virtio/src/net.rs | 118 +++++----- virtio/src/queue.rs | 138 +++++++----- virtio/src/rng.rs | 54 ++--- virtio/src/vhost/kernel/mod.rs | 64 ++++-- virtio/src/vhost/kernel/net.rs | 54 ++--- virtio/src/vhost/kernel/vsock.rs | 78 ++++--- virtio/src/vhost/mod.rs | 2 +- virtio/src/vhost/user/block.rs | 40 ++-- virtio/src/vhost/user/client.rs | 101 +++++---- virtio/src/vhost/user/fs.rs | 25 ++- virtio/src/vhost/user/message.rs | 6 +- virtio/src/vhost/user/net.rs | 25 ++- virtio/src/vhost/user/sock.rs | 4 +- virtio/src/virtio_mmio.rs | 63 +++--- virtio/src/virtio_pci.rs | 101 ++++----- vnc/Cargo.toml | 3 +- vnc/src/auth.rs | 2 +- vnc/src/client.rs | 34 +-- vnc/src/error.rs | 38 ++++ vnc/src/lib.rs | 38 +--- vnc/src/server.rs | 3 +- vnc/src/vnc.rs | 5 +- 198 files changed, 4226 insertions(+), 3722 deletions(-) create mode 100644 acpi/src/error.rs create mode 100644 address_space/src/error.rs create mode 100644 boot_loader/src/error.rs create mode 100644 cpu/src/error.rs create mode 100644 devices/src/interrupt_controller/error.rs create mode 100644 devices/src/legacy/error.rs create mode 100644 hypervisor/src/error.rs create mode 100644 machine/src/error.rs create mode 100644 machine/src/micro_vm/error.rs create mode 100644 machine/src/standard_vm/error.rs create mode 100644 machine_manager/src/config/error.rs create mode 100644 machine_manager/src/error.rs create mode 100644 migration/src/error.rs create mode 100644 ozone/src/error.rs create mode 100644 pci/src/error.rs create mode 100644 sysbus/src/error.rs create mode 100644 usb/src/error.rs create mode 100644 util/src/error.rs create mode 100644 vfio/src/error.rs create mode 100644 vhost_user_fs/src/error.rs create mode 100644 virtio/src/error.rs create mode 100644 vnc/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 83ed5eedb..f87996456 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,7 +6,7 @@ version = 3 name = "StratoVirt" version = "2.2.0" dependencies = [ - "error-chain", + "anyhow", "hypervisor", "kvm-ioctls", "libc", @@ -14,6 +14,7 @@ dependencies = [ "machine", "machine_manager", "migration", + "thiserror", "util", "vfio", "vhost_user_fs", @@ -27,28 +28,20 @@ name = "acpi" version = "2.2.0" dependencies = [ "address_space", + "anyhow", "byteorder", - "error-chain", "log", "machine_manager", + "thiserror", "util", ] -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - [[package]] name = "address_space" version = "2.2.0" dependencies = [ + "anyhow", "arc-swap", - "error-chain", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -58,16 +51,11 @@ dependencies = [ "migration", "migration_derive", "serial_test", + "thiserror", "util", "vmm-sys-util", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aho-corasick" version = "0.7.19" @@ -77,6 +65,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + [[package]] name = "arc-swap" version = "1.5.1" @@ -89,21 +83,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -121,12 +100,13 @@ name = "boot_loader" version = "2.2.0" dependencies = [ "address_space", + "anyhow", "devices", - "error-chain", "kvm-bindings", "kvm-ioctls", "libc", "log", + "thiserror", "util", "vmm-sys-util", ] @@ -173,7 +153,7 @@ dependencies = [ name = "cpu" version = "2.2.0" dependencies = [ - "error-chain", + "anyhow", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -183,6 +163,7 @@ dependencies = [ "migration", "migration_derive", "serial_test", + "thiserror", "util", "vmm-sys-util", ] @@ -193,9 +174,9 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "anyhow", "byteorder", "drm-fourcc", - "error-chain", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -207,6 +188,7 @@ dependencies = [ "serde", "serial_test", "sysbus", + "thiserror", "util", "vmm-sys-util", "vnc", @@ -219,20 +201,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" [[package]] -name = "error-chain" -version = "0.12.4" +name = "errno" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ - "backtrace", - "version_check", + "errno-dragonfly", + "libc", + "winapi", ] [[package]] -name = "gimli" -version = "0.26.2" +name = "errno-dragonfly" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] [[package]] name = "heck" @@ -247,12 +234,13 @@ dependencies = [ name = "hypervisor" version = "2.2.0" dependencies = [ + "anyhow", "arc-swap", - "error-chain", "kvm-bindings", "kvm-ioctls", "log", "once_cell", + "thiserror", "util", "vmm-sys-util", ] @@ -310,15 +298,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.129" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -339,10 +327,10 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "anyhow", "boot_loader", "cpu", "devices", - "error-chain", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -355,6 +343,7 @@ dependencies = [ "serde", "serde_json", "sysbus", + "thiserror", "usb", "util", "vfio", @@ -368,7 +357,7 @@ dependencies = [ name = "machine_manager" version = "2.2.0" dependencies = [ - "error-chain", + "anyhow", "libc", "log", "once_cell", @@ -376,6 +365,7 @@ dependencies = [ "serde_json", "strum", "strum_macros", + "thiserror", "util", "vmm-sys-util", ] @@ -390,7 +380,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" name = "migration" version = "2.2.0" dependencies = [ - "error-chain", + "anyhow", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -400,6 +390,7 @@ dependencies = [ "once_cell", "serde", "serde_json", + "thiserror", "util", ] @@ -414,36 +405,19 @@ dependencies = [ "util", ] -[[package]] -name = "miniz_oxide" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" -dependencies = [ - "adler", -] - -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "ozone" version = "2.2.0" dependencies = [ - "error-chain", + "anyhow", "libc", + "thiserror", "util", ] @@ -478,8 +452,8 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "anyhow", "byteorder", - "error-chain", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -490,15 +464,16 @@ dependencies = [ "migration_derive", "once_cell", "sysbus", + "thiserror", "util", "vmm-sys-util", ] [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ "unicode-ident", ] @@ -538,12 +513,6 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "ryu" version = "1.0.11" @@ -558,18 +527,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -578,9 +547,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -659,9 +628,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ "proc-macro2", "quote", @@ -674,23 +643,44 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", - "error-chain", + "anyhow", "hypervisor", "kvm-ioctls", + "thiserror", "vmm-sys-util", ] +[[package]] +name = "thiserror" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-xid" @@ -703,12 +693,13 @@ name = "usb" version = "2.2.0" dependencies = [ "address_space", + "anyhow", "byteorder", - "error-chain", "libc", "log", "once_cell", "pci", + "thiserror", "util", ] @@ -716,31 +707,26 @@ dependencies = [ name = "util" version = "2.2.0" dependencies = [ + "anyhow", "arc-swap", "byteorder", - "error-chain", "io-uring", "kvm-bindings", "kvm-ioctls", "libc", "log", "once_cell", + "thiserror", "vmm-sys-util", ] -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "vfio" version = "2.2.0" dependencies = [ "address_space", + "anyhow", "byteorder", - "error-chain", "hypervisor", "kvm-bindings", "kvm-ioctls", @@ -748,6 +734,7 @@ dependencies = [ "log", "once_cell", "pci", + "thiserror", "util", "vfio-bindings", "vmm-sys-util", @@ -765,9 +752,9 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "anyhow", "devices", "errno", - "error-chain", "hypervisor", "libc", "log", @@ -776,6 +763,7 @@ dependencies = [ "migration_derive", "pci", "sysbus", + "thiserror", "util", "virtio", "vmm-sys-util", @@ -787,9 +775,9 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "anyhow", "byteorder", "devices", - "error-chain", "hypervisor", "kvm-ioctls", "libc", @@ -800,6 +788,7 @@ dependencies = [ "pci", "serde_json", "sysbus", + "thiserror", "util", "vmm-sys-util", "vnc", @@ -821,9 +810,9 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "anyhow", "bitintr", "byteorder", - "error-chain", "hypervisor", "libc", "log", @@ -835,6 +824,7 @@ dependencies = [ "serde_json", "sscanf", "sysbus", + "thiserror", "usb", "util", "vmm-sys-util", diff --git a/Cargo.toml b/Cargo.toml index 61273ed5e..b7c3ddbf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,8 @@ description = "a lightweight hypervisor with low memory overhead and fast bootin license = "Mulan PSL v2" [dependencies] -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-ioctls = ">=0.11.0" libc = "0.2" log = "0.4" diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml index b4abaf0b2..faa035c07 100644 --- a/acpi/Cargo.toml +++ b/acpi/Cargo.toml @@ -7,9 +7,10 @@ license = "Mulan PSL v2" description = "acpi module" [dependencies] -error-chain = "0.12.4" log = "0.4" byteorder = "1.4.3" +thiserror = "1.0" +anyhow = "1.0" address_space = { path = "../address_space" } util = {path = "../util"} diff --git a/acpi/src/error.rs b/acpi/src/error.rs new file mode 100644 index 000000000..116234d6d --- /dev/null +++ b/acpi/src/error.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum AcpiError { + #[error("Failed to add AllocateEntry in TableLoader, file_blob {0} already exists.")] + FileEntryExist(String), + #[error("Failed to find matched file_blob in TableLoader, file name: {0}.")] + NoMatchedFile(String), + #[error("Invalid alignment {0}. Alignment is in bytes, and must be a power of 2.")] + Alignment(u32), + #[error("Address overflows, offset {0}, size {1}, max size {2}.")] + AddrOverflow(u32, u32, usize), + #[error("Failed to add pointer command: pointer length {0}, which is expected to be 1/2/4/8.")] + AddPointerLength(u8), +} diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index 82688bb81..efbe78bcc 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -15,6 +15,7 @@ mod acpi_device; pub mod acpi_table; #[allow(dead_code)] pub(crate) mod aml_compiler; +pub mod error; #[allow(dead_code)] mod table_loader; @@ -22,6 +23,7 @@ pub use acpi_device::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; pub use acpi_table::madt_subtable::*; pub use acpi_table::*; pub use aml_compiler::*; +pub use error::AcpiError; pub use table_loader::TableLoader; // The name of corresponding file-entry in FwCfg device that represents acpi table data. @@ -30,27 +32,3 @@ pub const ACPI_TABLE_FILE: &str = "etc/acpi/tables"; pub const ACPI_TABLE_LOADER_FILE: &str = "etc/table-loader"; // The name of corresponding file-entry in FwCfg device that represents acpi rsdp struct. pub const ACPI_RSDP_FILE: &str = "etc/acpi/rsdp"; - -pub mod errors { - use error_chain::error_chain; - - error_chain! { - errors { - FileEntryExist(name: String) { - display("Failed to add AllocateEntry in TableLoader, file_blob {} already exists.", name) - } - NoMatchedFile(name: String) { - display("Failed to find matched file_blob in TableLoader, file name: {}.", name) - } - Alignment(align: u32) { - display("Invalid alignment {}. Alignment is in bytes, and must be a power of 2.", align) - } - AddrOverflow(offset: u32, size: u32, blob_size: usize) { - display("Address overflows, offset {}, size {}, max size {}.", offset, size, blob_size) - } - AddPointerLength(size: u8) { - display("Failed to add pointer command: pointer length {}, which is expected to be 1/2/4/8.", size) - } - } - } -} diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 3703750e0..053a05de8 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -12,11 +12,11 @@ use std::sync::{Arc, Mutex}; -use error_chain::bail; use util::byte_code::ByteCode; -use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::AcpiError; use crate::AmlBuilder; +use anyhow::{anyhow, bail, Context, Result}; const TABLE_LOADER_FILE_NAME_SZ: usize = 56; const TABLE_LOADER_ENTRY_SZ: usize = 124; @@ -235,10 +235,10 @@ impl TableLoader { ) -> Result<()> { let file = file.to_string(); if align & (align - 1) != 0 { - return Err(ErrorKind::Alignment(align).into()); + return Err(anyhow!(AcpiError::Alignment(align))); } if self.find_matched_file(&file).is_some() { - return Err(ErrorKind::FileEntryExist(file).into()); + return Err(anyhow!(AcpiError::FileEntryExist(file))); } self.files.push(TableLoaderFileEntry { @@ -271,15 +271,23 @@ impl TableLoader { let file = file.to_string(); let file_entry = self .find_matched_file(&file) - .chain_err(|| ErrorKind::NoMatchedFile(file.clone()))?; + .with_context(|| anyhow!(AcpiError::NoMatchedFile(file.clone())))?; let file_entry_len = file_entry.file_blob.lock().unwrap().len(); if cksum_offset as usize + 1 > file_entry_len { - return Err(ErrorKind::AddrOverflow(cksum_offset, 1, file_entry_len).into()); + return Err(anyhow!(AcpiError::AddrOverflow( + cksum_offset, + 1, + file_entry_len + ))); } if start as usize >= file_entry_len || (start + length) as usize > file_entry_len { - return Err(ErrorKind::AddrOverflow(start, length, file_entry_len).into()); + return Err(anyhow!(AcpiError::AddrOverflow( + start, + length, + file_entry_len + ))); } if cksum_offset < start { bail!("The offset of checksum should larger offset of start of range in file blob"); @@ -322,23 +330,31 @@ impl TableLoader { let src_file = src_file.to_string(); let dst_file_entry = self .find_matched_file(&dst_file) - .chain_err(|| ErrorKind::NoMatchedFile(dst_file.clone()))?; + .with_context(|| anyhow!(AcpiError::NoMatchedFile(dst_file.clone())))?; let src_file_entry = self .find_matched_file(&src_file) - .chain_err(|| ErrorKind::NoMatchedFile(src_file.clone()))?; + .with_context(|| anyhow!(AcpiError::NoMatchedFile(src_file.clone())))?; let dst_file_len = dst_file_entry.file_blob.lock().unwrap().len(); let src_file_len = src_file_entry.file_blob.lock().unwrap().len(); if src_offset as usize >= src_file_len || (src_offset + u32::from(size)) as usize > src_file_len { - return Err(ErrorKind::AddrOverflow(src_offset, u32::from(size), src_file_len).into()); + return Err(anyhow!(AcpiError::AddrOverflow( + src_offset, + u32::from(size), + src_file_len + ))); } if offset as usize >= dst_file_len || (offset + u32::from(size)) as usize > dst_file_len { - return Err(ErrorKind::AddrOverflow(offset, u32::from(size), dst_file_len).into()); + return Err(anyhow!(AcpiError::AddrOverflow( + offset, + u32::from(size), + dst_file_len + ))); } if size != 1 && size != 2 && size != 4 && size != 8 { - return Err(ErrorKind::AddPointerLength(size).into()); + return Err(anyhow!(AcpiError::AddPointerLength(size))); } dst_file_entry.file_blob.lock().unwrap() diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 9bcc48c97..ed1148932 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -7,13 +7,14 @@ license = "Mulan PSL v2" description = "provide memory management for VM" [dependencies] -error-chain = "0.12.4" libc = "0.2" log = "0.4" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" vmm-sys-util = ">=0.10.0" arc-swap = ">=1.5.0" +thiserror = "1.0" +anyhow = "1.0" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index f58da34b5..d697f7701 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -9,18 +9,19 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + +use arc_swap::ArcSwap; use std::fmt; +use std::fmt::Debug; use std::io::Write; use std::sync::{Arc, Mutex}; - -use arc_swap::ArcSwap; use util::byte_code::ByteCode; -use crate::errors::{ErrorKind, Result, ResultExt}; use crate::{ - AddressRange, FlatRange, GuestAddress, Listener, ListenerReqType, Region, RegionIoEventFd, - RegionType, + AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, + RegionIoEventFd, RegionType, }; +use anyhow::{anyhow, Context, Result}; /// Contains an array of `FlatRange`. #[derive(Default, Clone, Debug)] @@ -99,7 +100,7 @@ impl AddressSpace { if !space.root.subregions().is_empty() { space .update_topology() - .chain_err(|| "Failed to update topology for address_space")?; + .with_context(|| "Failed to update topology for address_space")?; } Ok(space) @@ -240,12 +241,12 @@ impl AddressSpace { } else { if !is_add { self.call_listeners(Some(old_r), None, ListenerReqType::DeleteRegion) - .chain_err(|| { - ErrorKind::UpdateTopology( + .with_context(|| { + anyhow!(AddressSpaceError::UpdateTopology( old_r.addr_range.base.raw_value(), old_r.addr_range.size, old_r.owner.region_type(), - ) + )) })?; } old_idx += 1; @@ -256,12 +257,12 @@ impl AddressSpace { // current old_range is None, or current new_range is before old_range if is_add && new_range.is_some() { self.call_listeners(new_range, None, ListenerReqType::AddRegion) - .chain_err(|| { - ErrorKind::UpdateTopology( + .with_context(|| { + anyhow!(AddressSpaceError::UpdateTopology( new_range.unwrap().addr_range.base.raw_value(), new_range.unwrap().addr_range.size, new_range.unwrap().owner.region_type(), - ) + )) })?; } new_idx += 1; @@ -285,24 +286,24 @@ impl AddressSpace { let new_fd = new_evtfds.get(new_idx); if old_fd.is_some() && (new_fd.is_none() || old_fd.unwrap().before(new_fd.unwrap())) { self.call_listeners(None, old_fd, ListenerReqType::DeleteIoeventfd) - .chain_err(|| { - ErrorKind::UpdateTopology( + .with_context(|| { + anyhow!(AddressSpaceError::UpdateTopology( old_fd.unwrap().addr_range.base.raw_value(), old_fd.unwrap().addr_range.size, RegionType::IO, - ) + )) })?; old_idx += 1; } else if new_fd.is_some() && (old_fd.is_none() || new_fd.unwrap().before(old_fd.unwrap())) { self.call_listeners(None, new_fd, ListenerReqType::AddIoeventfd) - .chain_err(|| { - ErrorKind::UpdateTopology( + .with_context(|| { + anyhow!(AddressSpaceError::UpdateTopology( new_fd.unwrap().addr_range.base.raw_value(), new_fd.unwrap().addr_range.size, RegionType::IO, - ) + )) })?; new_idx += 1; } else { @@ -337,7 +338,7 @@ impl AddressSpace { } self.update_ioeventfds_pass(&ioeventfds) - .chain_err(|| "Failed to update ioeventfds")?; + .with_context(|| "Failed to update ioeventfds")?; *self.ioeventfds.lock().unwrap() = ioeventfds; Ok(()) } @@ -419,13 +420,13 @@ impl AddressSpace { let (fr, offset) = view .find_flatrange(addr) .map(|fr| (fr, addr.offset_from(fr.addr_range.base))) - .chain_err(|| ErrorKind::RegionNotFound(addr.raw_value()))?; + .with_context(|| anyhow!(AddressSpaceError::RegionNotFound(addr.raw_value())))?; let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let offset_in_region = fr.offset_in_region + offset; fr.owner .read(dst, region_base, offset_in_region, count) - .chain_err(|| { + .with_context(|| { format!( "Failed to read region, region base 0x{:X}, offset in region 0x{:X}, size 0x{:X}", region_base.raw_value(), @@ -451,13 +452,13 @@ impl AddressSpace { let (fr, offset) = view .find_flatrange(addr) .map(|fr| (fr, addr.offset_from(fr.addr_range.base))) - .chain_err(|| ErrorKind::RegionNotFound(addr.raw_value()))?; + .with_context(|| anyhow!(AddressSpaceError::RegionNotFound(addr.raw_value())))?; let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let offset_in_region = fr.offset_in_region + offset; fr.owner .write(src, region_base, offset_in_region, count) - .chain_err(|| + .with_context(|| format!( "Failed to write region, region base 0x{:X}, offset in region 0x{:X}, size 0x{:X}", region_base.raw_value(), @@ -477,7 +478,7 @@ impl AddressSpace { /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn write_object(&self, data: &T, addr: GuestAddress) -> Result<()> { self.write(&mut data.as_bytes(), addr, std::mem::size_of::() as u64) - .chain_err(|| "Failed to write object") + .with_context(|| "Failed to write object") } /// Write an object to memory via host address. @@ -494,7 +495,7 @@ impl AddressSpace { std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; dst.write_all(data.as_bytes()) - .chain_err(|| "Failed to write object via host address") + .with_context(|| "Failed to write object via host address") } /// Read some data from memory to form an object. @@ -512,7 +513,7 @@ impl AddressSpace { addr, std::mem::size_of::() as u64, ) - .chain_err(|| "Failed to read object")?; + .with_context(|| "Failed to read object")?; Ok(obj) } @@ -531,7 +532,7 @@ impl AddressSpace { std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; dst.write_all(src) - .chain_err(|| "Failed to read object via host address")?; + .with_context(|| "Failed to read object via host address")?; Ok(obj) } @@ -544,16 +545,16 @@ impl AddressSpace { let new_fv = self .root .generate_flatview(GuestAddress(0), addr_range) - .chain_err(|| "Failed to generate new topology")?; + .with_context(|| "Failed to generate new topology")?; self.update_topology_pass(&old_fv, &new_fv, false) - .chain_err(|| "Failed to update topology (first pass)")?; + .with_context(|| "Failed to update topology (first pass)")?; self.update_topology_pass(&old_fv, &new_fv, true) - .chain_err(|| "Failed to update topology (second pass)")?; + .with_context(|| "Failed to update topology (second pass)")?; self.flat_view.store(Arc::new(new_fv)); self.update_ioeventfds() - .chain_err(|| "Failed to generate and update ioeventfds")?; + .with_context(|| "Failed to generate and update ioeventfds")?; Ok(()) } } diff --git a/address_space/src/error.rs b/address_space/src/error.rs new file mode 100644 index 000000000..d42ebe61f --- /dev/null +++ b/address_space/src/error.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum AddressSpaceError { + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Failed to call listener, request type is {0:#?}")] + ListenerRequest(crate::listener::ListenerReqType), + #[error("Failed to update topology, base 0x{0:X}, size 0x{1:X}, region type is {2:#?}")] + UpdateTopology(u64, u64, crate::RegionType), + #[error("Failed to clone EventFd")] + IoEventFd, + #[error("Failed to align-up address, addr 0x{0:X}, align 0x{1:X}")] + AddrAlignUp(u64, u64), + #[error("Failed to find matched region, addr 0x{0:X}")] + RegionNotFound(u64), + #[error("Address overflows, addr is 0x{0:X}")] + Overflow(u64), + #[error("Failed to mmap")] + Mmap, + #[error("Failed to access IO-type region, region base 0x{0:X}, offset 0x{1:X}, size 0x{2:X}")] + IoAccess(u64, u64, u64), + #[error("Wrong region type, {0:#?}")] + RegionType(crate::RegionType), + #[error("No available kvm_mem_slot, total count is {0}")] + NoAvailKvmSlot(usize), + #[error("Failed to find matched kvm_mem_slot, addr 0x{0:X}, size 0x{1:X}")] + NoMatchedKvmSlot(u64, u64), + #[error("Added KVM mem range (0x{:X}, 0x{:X}) overlaps with exist one (0x{:X}, 0x{:X})", add.0, add.1, exist.0, exist.1)] + KvmSlotOverlap { add: (u64, u64), exist: (u64, u64) }, + #[error("Invalid offset: offset 0x{0:X}, data length 0x{1:X}, region size 0x{2:X}")] + InvalidOffset(u64, u64, u64), +} diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 0c42d2a5e..69c9ec623 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -16,7 +16,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; use std::thread; -use error_chain::bail; +use anyhow::{bail, Context, Result}; use log::{error, info}; use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; use util::{ @@ -24,7 +24,6 @@ use util::{ unix::{do_mmap, host_page_size}, }; -use crate::errors::{Result, ResultExt}; use crate::{AddressRange, GuestAddress}; const MAX_PREALLOC_THREAD: u8 = 16; @@ -82,8 +81,9 @@ impl FileBackend { let raw_fd = unsafe { libc::mkstemp(fs_cstr) }; if raw_fd < 0 { - return Err(std::io::Error::last_os_error()) - .chain_err(|| format!("Failed to create file in directory: {} ", file_path)); + return Err(std::io::Error::last_os_error()).with_context(|| { + format!("Failed to create file in directory: {} ", file_path) + }); } if unsafe { libc::unlink(fs_cstr) } != 0 { @@ -102,7 +102,7 @@ impl FileBackend { .write(true) .create(true) .open(path) - .chain_err(|| format!("Failed to open file: {}", file_path))?; + .with_context(|| format!("Failed to open file: {}", file_path))?; if existed && unsafe { libc::unlink(std::ffi::CString::new(file_path).unwrap().into_raw()) } @@ -130,7 +130,7 @@ impl FileBackend { let old_file_len = file.metadata().unwrap().len(); if old_file_len == 0 { file.set_len(file_len) - .chain_err(|| format!("Failed to set length of file: {}", file_path))?; + .with_context(|| format!("Failed to set length of file: {}", file_path))?; } else if old_file_len < file_len { bail!( "Backing file {} does not has sufficient resource for allocating RAM (size is 0x{:X})", @@ -210,7 +210,7 @@ fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { // join all threads to wait for pre-allocating. while let Some(thread) = threads_join.pop() { if let Err(ref e) = thread.join() { - error!("Failed to join thread: {:?}", e); + error!("{}", format!("Failed to join thread: {:?}", e)); } } } @@ -232,7 +232,7 @@ pub fn create_host_mmaps( let file_len = ranges.iter().fold(0, |acc, x| acc + x.1); f_back = Some( FileBackend::new_mem(path, file_len) - .chain_err(|| "Failed to create file that backs memory")?, + .with_context(|| "Failed to create file that backs memory")?, ); } else if mem_config.mem_share { let file_len = ranges.iter().fold(0, |acc, x| acc + x.1); @@ -241,13 +241,13 @@ pub fn create_host_mmaps( let anon_fd = unsafe { libc::syscall(libc::SYS_memfd_create, anon_mem_name.as_ptr(), 0) } as RawFd; if anon_fd < 0 { - return Err(std::io::Error::last_os_error()).chain_err(|| "Failed to create memfd"); + return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } let anon_file = unsafe { File::from_raw_fd(anon_fd) }; anon_file .set_len(file_len) - .chain_err(|| "Failed to set the length of anonymous file that backs memory")?; + .with_context(|| "Failed to set the length of anonymous file that backs memory")?; f_back = Some(FileBackend { file: Arc::new(anon_file), @@ -335,7 +335,7 @@ pub fn set_host_memory_policy( max_node as u64, MPOL_MF_STRICT | MPOL_MF_MOVE, ) - .chain_err(|| "Failed to call mbind")?; + .with_context(|| "Failed to call mbind")?; host_addr_start += zone.size; } diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 1077a445e..56402c930 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -79,6 +79,7 @@ mod address; mod address_space; +pub mod error; mod host_mmap; mod listener; mod region; @@ -86,6 +87,8 @@ mod state; pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; +pub use anyhow::Result; +pub use error::AddressSpaceError; pub use host_mmap::{create_host_mmaps, set_host_memory_policy, FileBackend, HostMemMapping}; #[cfg(target_arch = "x86_64")] pub use listener::KvmIoListener; @@ -93,60 +96,6 @@ pub use listener::KvmMemoryListener; pub use listener::{Listener, ListenerReqType}; pub use region::{FlatRange, Region, RegionIoEventFd, RegionType}; -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - } - errors { - ListenerRequest(req_type: crate::listener::ListenerReqType) { - display("Failed to call listener, request type is {:#?}", req_type) - } - UpdateTopology(base: u64, size: u64, reg_ty: crate::RegionType) { - display("Failed to update topology, base 0x{:X}, size 0x{:X}, region type is {:#?}", base, size, reg_ty) - } - IoEventFd { - display("Failed to clone EventFd") - } - AddrAlignUp(addr: u64, align: u64) { - display("Failed to align-up address, overflows: addr 0x{:X}, align 0x{:X}", addr, align) - } - RegionNotFound(addr: u64) { - display("Failed to find matched region, addr 0x{:X}", addr) - } - Overflow(addr: u64) { - display("Address overflows, addr is 0x{:X}", addr) - } - Mmap { - display("Failed to mmap") - } - IoAccess(base: u64, offset: u64, count: u64) { - display("Failed to access IO-type region, region base 0x{:X}, offset 0x{:X}, size 0x{:X}", base, offset, count) - } - RegionType(t: crate::RegionType) { - display("Wrong region type, {:#?}", t) - } - NoAvailKvmSlot(cnt: usize) { - display("No available kvm_mem_slot, total count is {}", cnt) - } - NoMatchedKvmSlot(addr: u64, sz: u64) { - display("Failed to find matched kvm_mem_slot, addr 0x{:X}, size 0x{:X}", addr, sz) - } - KvmSlotOverlap(add: (u64, u64), exist: (u64, u64)) { - display("Added KVM mem range (0x{:X}, 0x{:X}) overlaps with exist one (0x{:X}, 0x{:X})", add.0, add.1, exist.0, exist.1) - } - InvalidOffset(offset: u64, count: u64, region_size: u64) { - display("Invalid offset: offset 0x{:X}, data length 0x{:X}, region size 0x{:X}", offset, count, region_size) - } - } - } -} - /// Provide Some operations of `Region`, mainly used by Vm's devices. #[derive(Clone)] pub struct RegionOps { diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 9bfa33a1c..7521b52b3 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -13,15 +13,14 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; -use error_chain::{bail, ChainedError}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::kvm_userspace_memory_region; use kvm_ioctls::{IoEventAddress, NoDatamatch}; use log::{debug, warn}; use util::{num_ops::round_down, unix::host_page_size}; -use crate::errors::{ErrorKind, Result, ResultExt}; -use crate::{AddressRange, FlatRange, RegionIoEventFd, RegionType}; +use crate::{AddressRange, AddressSpaceError, FlatRange, RegionIoEventFd, RegionType}; +use anyhow::{anyhow, bail, Context, Result}; const MEM_READ_ONLY: u32 = 1 << 1; @@ -132,9 +131,10 @@ impl KvmMemoryListener { .find_intersection(range) .is_some() { - return Err( - ErrorKind::KvmSlotOverlap((guest_addr, size), (s.guest_addr, s.size)).into(), - ); + return Err(anyhow!(AddressSpaceError::KvmSlotOverlap { + add: (guest_addr, size), + exist: (s.guest_addr, s.size) + })); } Ok(()) })?; @@ -149,7 +149,7 @@ impl KvmMemoryListener { } } - Err(ErrorKind::NoAvailKvmSlot(slots.len()).into()) + Err(anyhow!(AddressSpaceError::NoAvailKvmSlot(slots.len()))) } /// Delete a slot after finding it according to the given arguments. @@ -172,7 +172,7 @@ impl KvmMemoryListener { return Ok(*slot); } } - Err(ErrorKind::NoMatchedKvmSlot(addr, size).into()) + Err(anyhow!(AddressSpaceError::NoMatchedKvmSlot(addr, size))) } /// Align a piece of memory segment according to `alignment`, @@ -187,17 +187,19 @@ impl KvmMemoryListener { /// /// Return Error if Memslot size is zero after aligned. fn align_mem_slot(range: AddressRange, alignment: u64) -> Result { - let aligned_addr = range - .base - .align_up(alignment) - .chain_err(|| ErrorKind::AddrAlignUp(range.base.raw_value(), alignment))?; + let aligned_addr = range.base.align_up(alignment).with_context(|| { + anyhow!(AddressSpaceError::AddrAlignUp( + range.base.raw_value(), + alignment + )) + })?; let aligned_size = range .size .checked_sub(aligned_addr.offset_from(range.base)) .and_then(|sz| round_down(sz, alignment)) .filter(|&sz| sz > 0_u64) - .chain_err(|| + .with_context(|| format!("Mem slot size is zero after aligned, addr 0x{:X}, size 0x{:X}, alignment 0x{:X}", range.base.raw_value(), range.size, alignment) )?; @@ -220,8 +222,8 @@ impl KvmMemoryListener { { if let Err(ref e) = self.delete_region(flat_range) { warn!( - "Rom-device Region changes to IO mode, Failed to delete region: {}", - e.display_chain() + "Rom-device Region changes to IO mode, Failed to delete region: {:?}", + e ); } return Ok(()); @@ -237,7 +239,7 @@ impl KvmMemoryListener { let (aligned_addr, aligned_size) = Self::align_mem_slot(flat_range.addr_range, host_page_size()) .map(|r| (r.base, r.size)) - .chain_err(|| "Failed to align mem slot")?; + .with_context(|| "Failed to align mem slot")?; let align_adjust = aligned_addr.raw_value() - flat_range.addr_range.base.raw_value(); // `unwrap()` won't fail because Ram-type Region definitely has hva @@ -247,7 +249,7 @@ impl KvmMemoryListener { let slot_idx = self .get_free_slot(aligned_addr.raw_value(), aligned_size, aligned_hva) - .chain_err(|| "Failed to get available KVM mem slot")?; + .with_context(|| "Failed to get available KVM mem slot")?; let mut flags = 0_u32; if flat_range.owner.get_rom_device_romd().unwrap_or(false) { @@ -264,7 +266,7 @@ impl KvmMemoryListener { KVM_FDS .load() .add_mem_slot(kvm_region) - .chain_err(|| "Failed to add memory slot to kvm")?; + .with_context(|| "Failed to add memory slot to kvm")?; KVM_FDS .load() @@ -274,8 +276,8 @@ impl KvmMemoryListener { .set_user_memory_region(kvm_region) .or_else(|e| { self.delete_slot(aligned_addr.raw_value(), aligned_size) - .chain_err(|| "Failed to delete Kvm mem slot")?; - Err(e).chain_err(|| { + .with_context(|| "Failed to delete Kvm mem slot")?; + Err(e).with_context(|| { format!( "KVM register memory region failed: addr 0x{:X}, size 0x{:X}", aligned_addr.raw_value(), @@ -303,7 +305,7 @@ impl KvmMemoryListener { let (aligned_addr, aligned_size) = Self::align_mem_slot(flat_range.addr_range, host_page_size()) .map(|r| (r.base, r.size)) - .chain_err(|| "Failed to align mem slot")?; + .with_context(|| "Failed to align mem slot")?; let mem_slot = match self.delete_slot(aligned_addr.raw_value(), aligned_size) { Ok(m) => m, @@ -324,7 +326,7 @@ impl KvmMemoryListener { KVM_FDS .load() .remove_mem_slot(kvm_region) - .chain_err(|| "Failed to remove memory slot to kvm")?; + .with_context(|| "Failed to remove memory slot to kvm")?; KVM_FDS .load() @@ -332,7 +334,7 @@ impl KvmMemoryListener { .as_ref() .unwrap() .set_user_memory_region(kvm_region) - .chain_err(|| { + .with_context(|| { format!( "KVM unregister memory region failed: addr 0x{:X}", aligned_addr.raw_value(), @@ -368,7 +370,7 @@ impl KvmMemoryListener { vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) }; - ioctl_ret.chain_err(|| { + ioctl_ret.with_context(|| { format!( "KVM register ioeventfd failed, mmio addr 0x{:X}, size 0x{:X}, data_match {}", ioevtfd.addr_range.base.raw_value(), @@ -405,7 +407,7 @@ impl KvmMemoryListener { vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) }; - ioctl_ret.chain_err(|| { + ioctl_ret.with_context(|| { format!( "KVM unregister ioeventfd failed: mmio addr 0x{:X}, size 0x{:X}, data_match {}", ioevtfd.addr_range.base.raw_value(), @@ -446,20 +448,21 @@ impl Listener for KvmMemoryListener { evtfd: Option<&RegionIoEventFd>, req_type: ListenerReqType, ) -> Result<()> { - let req_ret = match req_type { - ListenerReqType::AddRegion => { - self.add_region(flat_range.chain_err(|| "No FlatRange for AddRegion request")?) - } - ListenerReqType::DeleteRegion => self - .delete_region(flat_range.chain_err(|| "No FlatRange for DeleteRegion request")?), - ListenerReqType::AddIoeventfd => { - self.add_ioeventfd(evtfd.chain_err(|| "No IoEventFd for AddIoeventfd request")?) - } - ListenerReqType::DeleteIoeventfd => self - .delete_ioeventfd(evtfd.chain_err(|| "No IoEventFd for DeleteIoeventfd request")?), - }; - - req_ret.chain_err(|| ErrorKind::ListenerRequest(req_type)) + let req_ret = + match req_type { + ListenerReqType::AddRegion => self + .add_region(flat_range.with_context(|| "No FlatRange for AddRegion request")?), + ListenerReqType::DeleteRegion => self.delete_region( + flat_range.with_context(|| "No FlatRange for DeleteRegion request")?, + ), + ListenerReqType::AddIoeventfd => self + .add_ioeventfd(evtfd.with_context(|| "No IoEventFd for AddIoeventfd request")?), + ListenerReqType::DeleteIoeventfd => self.delete_ioeventfd( + evtfd.with_context(|| "No IoEventFd for DeleteIoeventfd request")?, + ), + }; + + req_ret.with_context(|| anyhow!(AddressSpaceError::ListenerRequest(req_type))) } } @@ -500,7 +503,7 @@ impl KvmIoListener { vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) }; - ioctl_ret.chain_err(|| { + ioctl_ret.with_context(|| { format!( "KVM register ioeventfd failed: io addr 0x{:X}, size 0x{:X}, data_match {}", ioevtfd.addr_range.base.raw_value(), @@ -537,7 +540,7 @@ impl KvmIoListener { vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) }; - ioctl_ret.chain_err(|| { + ioctl_ret.with_context(|| { format!( "KVM unregister ioeventfd failed: io addr 0x{:X}, size 0x{:X}, data_match {}", ioevtfd.addr_range.base.raw_value(), @@ -577,14 +580,15 @@ impl Listener for KvmIoListener { ) -> Result<()> { let handle_ret = match req_type { ListenerReqType::AddIoeventfd => { - self.add_ioeventfd(evtfd.chain_err(|| "No IoEventFd for AddIoeventfd request")?) + self.add_ioeventfd(evtfd.with_context(|| "No IoEventFd for AddIoeventfd request")?) } - ListenerReqType::DeleteIoeventfd => self - .delete_ioeventfd(evtfd.chain_err(|| "No IoEventFd for DeleteIoeventfd request")?), + ListenerReqType::DeleteIoeventfd => self.delete_ioeventfd( + evtfd.with_context(|| "No IoEventFd for DeleteIoeventfd request")?, + ), _ => return Ok(()), }; - handle_ret.chain_err(|| ErrorKind::ListenerRequest(req_type)) + handle_ret.with_context(|| anyhow!(AddressSpaceError::ListenerRequest(req_type))) } } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 470ec9d5b..b0c6e8976 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -9,17 +9,19 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::fmt; + use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; -use error_chain::bail; -use log::{debug, warn}; - use crate::address_space::FlatView; -use crate::errors::{ErrorKind, Result, ResultExt}; -use crate::{AddressRange, AddressSpace, FileBackend, GuestAddress, HostMemMapping, RegionOps}; - +use crate::{ + AddressRange, AddressSpace, AddressSpaceError, FileBackend, GuestAddress, HostMemMapping, + RegionOps, +}; +use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, warn}; +use std::fmt; +use std::fmt::Debug; /// Types of Region. #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -128,7 +130,10 @@ impl RegionIoEventFd { /// Return the cloned Region IoEventFd, /// return error if failed to clone EventFd. pub(crate) fn try_clone(&self) -> Result { - let fd = self.fd.try_clone().or(Err(ErrorKind::IoEventFd))?; + let fd = self + .fd + .try_clone() + .map_err(|_| anyhow!(AddressSpaceError::IoEventFd))?; Ok(RegionIoEventFd { fd, addr_range: self.addr_range, @@ -321,7 +326,7 @@ impl Region { /// * `read_only` - Set region to read-only mode or not. pub fn set_rom_device_romd(&self, read_only: bool) -> Result<()> { if self.region_type != RegionType::RomDevice { - return Err(ErrorKind::RegionType(self.region_type).into()); + return Err(anyhow!(AddressSpaceError::RegionType(self.region_type))); } let old_mode = self.rom_dev_romd.as_ref().load(Ordering::SeqCst); @@ -414,7 +419,7 @@ impl Region { .filter(|end| *end <= self.size()) .is_none() { - return Err(ErrorKind::Overflow(addr).into()); + return Err(anyhow!(AddressSpaceError::Overflow(addr))); } Ok(()) } @@ -443,18 +448,20 @@ impl Region { ) -> Result<()> { match self.region_type { RegionType::Ram | RegionType::RamDevice => { - self.check_valid_offset(offset, count) - .chain_err(|| ErrorKind::InvalidOffset(offset, count, self.size()))?; + self.check_valid_offset(offset, count).with_context(|| { + anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + })?; let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let slice = unsafe { std::slice::from_raw_parts((host_addr + offset) as *const u8, count as usize) }; dst.write_all(slice) - .chain_err(|| "Failed to write content of Ram to mutable buffer")?; + .with_context(|| "Failed to write content of Ram to mutable buffer")?; } RegionType::RomDevice => { - self.check_valid_offset(offset, count) - .chain_err(|| ErrorKind::InvalidOffset(offset, count, self.size()))?; + self.check_valid_offset(offset, count).with_context(|| { + anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + })?; if self.rom_dev_romd.as_ref().load(Ordering::SeqCst) { let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let read_ret = unsafe { @@ -469,25 +476,34 @@ impl Region { let read_ops = self.ops.as_ref().unwrap().read.as_ref(); if !read_ops(&mut read_ret, base, offset) { - return Err(ErrorKind::IoAccess(base.raw_value(), offset, count).into()); + return Err(anyhow!(AddressSpaceError::IoAccess( + base.raw_value(), + offset, + count + ))); } dst.write_all(&read_ret)?; } } RegionType::IO => { if count >= std::usize::MAX as u64 { - return Err(ErrorKind::Overflow(count).into()); + return Err(anyhow!(AddressSpaceError::Overflow(count))); } let mut slice = vec![0_u8; count as usize]; let read_ops = self.ops.as_ref().unwrap().read.as_ref(); if !read_ops(&mut slice, base, offset) { - return Err(ErrorKind::IoAccess(base.raw_value(), offset, count).into()); + return Err(anyhow!(AddressSpaceError::IoAccess( + base.raw_value(), + offset, + count + ))); } - dst.write_all(&slice) - .chain_err(|| "Failed to write slice provided by device to mutable buffer")?; + dst.write_all(&slice).with_context(|| { + "Failed to write slice provided by device to mutable buffer" + })?; } _ => { - return Err(ErrorKind::RegionType(self.region_type()).into()); + return Err(anyhow!(AddressSpaceError::RegionType(self.region_type()))); } } Ok(()) @@ -515,7 +531,7 @@ impl Region { offset: u64, count: u64, ) -> Result<()> { - self.check_valid_offset(offset, count).chain_err(|| { + self.check_valid_offset(offset, count).with_context(|| { format!( "Invalid offset: offset 0x{:X}, data length 0x{:X}, region size 0x{:X}", offset, @@ -531,24 +547,28 @@ impl Region { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, count as usize) }; src.read_exact(slice) - .chain_err(|| "Failed to write buffer to Ram")?; + .with_context(|| "Failed to write buffer to Ram")?; } RegionType::RomDevice | RegionType::IO => { if count >= std::usize::MAX as u64 { - return Err(ErrorKind::Overflow(count).into()); + return Err(anyhow!(AddressSpaceError::Overflow(count))); } let mut slice = vec![0_u8; count as usize]; - src.read_exact(&mut slice).chain_err(|| { + src.read_exact(&mut slice).with_context(|| { "Failed to write buffer to slice, which will be provided for device" })?; let write_ops = self.ops.as_ref().unwrap().write.as_ref(); if !write_ops(&slice, base, offset) { - return Err(ErrorKind::IoAccess(base.raw_value(), offset, count).into()); + return Err(anyhow!(AddressSpaceError::IoAccess( + base.raw_value(), + offset, + count + ))); } } _ => { - return Err(ErrorKind::RegionType(self.region_type()).into()); + return Err(anyhow!(AddressSpaceError::RegionType(self.region_type()))); } } Ok(()) @@ -588,10 +608,10 @@ impl Region { pub fn add_subregion(&self, child: Region, offset: u64) -> Result<()> { // check parent Region's property, and check if child Region's offset is valid or not if self.region_type() != RegionType::Container { - return Err(ErrorKind::RegionType(self.region_type()).into()); + return Err(anyhow!(AddressSpaceError::RegionType(self.region_type()))); } self.check_valid_offset(offset, child.size()) - .chain_err(|| { + .with_context(|| { format!( "Invalid offset: offset 0x{:X}, child length 0x{:X}, region size 0x{:X}", offset, @@ -621,7 +641,7 @@ impl Region { if let Some(space) = self.space.read().unwrap().upgrade() { space .update_topology() - .chain_err(|| "Failed to update topology for address_space")?; + .with_context(|| "Failed to update topology for address_space")?; } else { debug!("add subregion to container region, which has no belonged address-space"); } @@ -643,7 +663,7 @@ impl Region { pub fn delete_subregion(&self, child: &Region) -> Result<()> { // check parent Region's property, and check if child Region's offset is valid or not if self.region_type() != RegionType::Container { - return Err(ErrorKind::RegionType(self.region_type()).into()); + return Err(anyhow!(AddressSpaceError::RegionType(self.region_type()))); } let mut sub_regions = self.subregions.write().unwrap(); @@ -659,14 +679,16 @@ impl Region { if !removed { warn!("Failed to delete subregion from parent region: not found"); - return Err(ErrorKind::RegionNotFound(child.offset().raw_value()).into()); + return Err(anyhow!(AddressSpaceError::RegionNotFound( + child.offset().raw_value() + ))); } // get father address-space and update topology if let Some(space) = self.space.read().unwrap().upgrade() { space .update_topology() - .chain_err(|| "Failed to update topology for address_space")?; + .with_context(|| "Failed to update topology for address_space")?; } else { debug!("add subregion to container region, which has no belonged address-space"); } @@ -709,7 +731,7 @@ impl Region { for sub_r in self.subregions.read().unwrap().iter() { sub_r .render_region_pass(region_base, intersect, flat_view) - .chain_err(|| { + .with_context(|| { format!( "Failed to render subregion, base 0x{:X}, addr_range (0x{:X}, 0x{:X})", base.raw_value(), @@ -721,7 +743,7 @@ impl Region { } RegionType::Ram | RegionType::IO | RegionType::RomDevice | RegionType::RamDevice => { self.render_terminate_region(base, addr_range, flat_view) - .chain_err(|| + .with_context(|| format!( "Failed to render terminate region, base 0x{:X}, addr_range (0x{:X}, 0x{:X})", base.raw_value(), addr_range.base.raw_value(), @@ -832,7 +854,7 @@ impl Region { match self.region_type { RegionType::Container => { self.render_region_pass(base, addr_range, &mut flat_view) - .chain_err(|| { + .with_context(|| { format!( "Failed to render terminate region, base 0x{:X}, addr_range (0x{:X}, 0x{:X})", base.raw_value(), @@ -843,7 +865,7 @@ impl Region { } RegionType::Ram | RegionType::IO | RegionType::RomDevice | RegionType::RamDevice => { self.render_terminate_region(base, addr_range, &mut flat_view) - .chain_err(|| { + .with_context(|| { format!( "Failed to render terminate region, base 0x{:X}, addr_range (0x{:X}, 0x{:X})", base.raw_value(), diff --git a/address_space/src/state.rs b/address_space/src/state.rs index f5274a5b8..513ce82d9 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -15,9 +15,9 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::sync::Arc; +use anyhow::{anyhow, Result}; use migration::{ - errors::{ErrorKind, Result}, - DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, + error::MigrationError, DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -91,7 +91,7 @@ impl MigrationHook for AddressSpace { if let Some(base_addr) = region.start_addr() { region .read(fd, base_addr, 0, region.size()) - .map_err(|e| ErrorKind::SaveVmMemoryErr(e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SaveVmMemoryErr(e.to_string())))?; } } @@ -101,7 +101,7 @@ impl MigrationHook for AddressSpace { fn restore_memory(&self, memory: Option<&File>, state: &[u8]) -> Result<()> { let address_space_state: &AddressSpaceState = AddressSpaceState::from_bytes(&state[0..size_of::()]) - .ok_or(ErrorKind::FromBytesError("MEMORY"))?; + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("MEMORY")))?; let memfile_arc = Arc::new(memory.unwrap().try_clone().unwrap()); for ram_state in address_space_state.ram_region_state @@ -123,14 +123,14 @@ impl MigrationHook for AddressSpace { false, false, ) - .map_err(|e| ErrorKind::RestoreVmMemoryErr(e.to_string()))?, + .map_err(|e| anyhow!(MigrationError::RestoreVmMemoryErr(e.to_string())))?, ); self.root() .add_subregion( Region::init_ram_region(host_mmap.clone()), host_mmap.start_address().raw_value(), ) - .map_err(|e| ErrorKind::RestoreVmMemoryErr(e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::RestoreVmMemoryErr(e.to_string())))?; } Ok(()) @@ -138,14 +138,14 @@ impl MigrationHook for AddressSpace { fn send_memory(&self, fd: &mut dyn Write, range: MemBlock) -> Result<()> { self.read(fd, GuestAddress(range.gpa), range.len) - .map_err(|e| ErrorKind::SendVmMemoryErr(e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SendVmMemoryErr(e.to_string())))?; Ok(()) } fn recv_memory(&self, fd: &mut dyn Read, range: MemBlock) -> Result<()> { self.write(fd, GuestAddress(range.gpa), range.len) - .map_err(|e| ErrorKind::RecvVmMemoryErr(e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::RecvVmMemoryErr(e.to_string())))?; Ok(()) } diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index e8de6690d..d621c8aa5 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" license = "Mulan PSL v2" [dependencies] -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" libc = "0.2" diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index 1b5b3c4b0..8d6731217 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -15,13 +15,13 @@ use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use crate::error::BootLoaderError; use address_space::{AddressSpace, GuestAddress}; -use devices::legacy::{errors::ErrorKind as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; +use anyhow::{anyhow, Context, Result}; +use devices::legacy::{error::LegacyError as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; use log::info; use util::byte_code::ByteCode; -use crate::errors::{ErrorKind, Result, ResultExt}; - const AARCH64_KERNEL_OFFSET: u64 = 0x8_0000; /// Boot loader config used for aarch64. @@ -53,7 +53,8 @@ fn load_kernel( kernel_path: &Path, sys_mem: &Arc, ) -> Result { - let mut kernel_image = File::open(kernel_path).chain_err(|| ErrorKind::BootLoaderOpenKernel)?; + let mut kernel_image = + File::open(kernel_path).with_context(|| anyhow!(BootLoaderError::BootLoaderOpenKernel))?; let kernel_size = kernel_image.metadata().unwrap().len(); let kernel_end = kernel_start + kernel_size; @@ -66,10 +67,10 @@ fn load_kernel( FwCfgEntryType::KernelSize, (kernel_size as u32).as_bytes().to_vec(), ) - .chain_err(|| FwcfgErrorKind::AddEntryErr("KernelSize".to_string()))?; + .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("KernelSize".to_string())))?; lock_dev .add_data_entry(FwCfgEntryType::KernelData, kernel_data) - .chain_err(|| FwcfgErrorKind::AddEntryErr("KernelData".to_string()))?; + .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("KernelData".to_string())))?; } else { if sys_mem .memory_end_address() @@ -77,11 +78,14 @@ fn load_kernel( .checked_sub(kernel_end) .is_none() { - return Err(ErrorKind::KernelOverflow(kernel_start, kernel_size).into()); + return Err(anyhow!(BootLoaderError::KernelOverflow( + kernel_start, + kernel_size + ))); } sys_mem .write(&mut kernel_image, GuestAddress(kernel_start), kernel_size) - .chain_err(|| "Fail to write kernel to guest memory")?; + .with_context(|| "Fail to write kernel to guest memory")?; } Ok(kernel_end) } @@ -92,7 +96,8 @@ fn load_initrd( sys_mem: &Arc, kernel_end: u64, ) -> Result<(u64, u64)> { - let mut initrd_image = File::open(initrd_path).chain_err(|| ErrorKind::BootLoaderOpenInitrd)?; + let mut initrd_image = + File::open(initrd_path).with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; let initrd_size = initrd_image.metadata().unwrap().len(); let initrd_start = if let Some(addr) = sys_mem @@ -103,7 +108,10 @@ fn load_initrd( { addr } else { - return Err(ErrorKind::InitrdOverflow(kernel_end, initrd_size).into()); + return Err(anyhow!(BootLoaderError::InitrdOverflow( + kernel_end, + initrd_size + ))); }; if let Some(fw_cfg) = fwcfg { @@ -115,20 +123,20 @@ fn load_initrd( FwCfgEntryType::InitrdAddr, (initrd_start as u32).as_bytes().to_vec(), ) - .chain_err(|| FwcfgErrorKind::AddEntryErr("InitrdAddr".to_string()))?; + .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("InitrdAddr".to_string())))?; lock_dev .add_data_entry( FwCfgEntryType::InitrdSize, (initrd_size as u32).as_bytes().to_vec(), ) - .chain_err(|| FwcfgErrorKind::AddEntryErr("InitrdSize".to_string()))?; + .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("InitrdSize".to_string())))?; lock_dev .add_data_entry(FwCfgEntryType::InitrdData, initrd_data) - .chain_err(|| FwcfgErrorKind::AddEntryErr("InitrdData".to_string()))?; + .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("InitrdData".to_string())))?; } else { sys_mem .write(&mut initrd_image, GuestAddress(initrd_start), initrd_size) - .chain_err(|| "Fail to write initrd to guest memory")?; + .with_context(|| "Fail to write initrd to guest memory")?; } Ok((initrd_start, initrd_size)) @@ -168,7 +176,9 @@ pub fn load_linux( .filter(|addr| addr >= &config.mem_start) .is_none() { - return Err(ErrorKind::DTBOverflow(sys_mem.memory_end_address().raw_value()).into()); + return Err(anyhow!(BootLoaderError::DTBOverflow( + sys_mem.memory_end_address().raw_value() + ))); } let kernel_start = config.mem_start + AARCH64_KERNEL_OFFSET; @@ -189,13 +199,13 @@ pub fn load_linux( config.kernel.as_ref().unwrap(), sys_mem, ) - .chain_err(|| "Fail to load kernel")?; + .with_context(|| "Fail to load kernel")?; let mut initrd_start = 0_u64; let mut initrd_size = 0_u64; if config.initrd.is_some() { let initrd_tuple = load_initrd(fwcfg, config.initrd.as_ref().unwrap(), sys_mem, kernel_end) - .chain_err(|| "Fail to load initrd")?; + .with_context(|| "Fail to load initrd")?; initrd_start = initrd_tuple.0; initrd_size = initrd_tuple.1; } else { diff --git a/boot_loader/src/error.rs b/boot_loader/src/error.rs new file mode 100644 index 000000000..410f0fe6a --- /dev/null +++ b/boot_loader/src/error.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum BootLoaderError { + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("FwCfg")] + FwCfg { + #[from] + source: devices::legacy::error::LegacyError, + }, + #[allow(clippy::upper_case_acronyms)] + #[cfg(target_arch = "aarch64")] + #[error( + "guest memory size {0} should bigger than {}", + util::device_tree::FDT_MAX_SIZE + )] + DTBOverflow(u64), + #[error("Failed to load kernel image {0} to memory {1}.")] + KernelOverflow(u64, u64), + #[error("Failed to load initrd image {0} to memory {1}.")] + InitrdOverflow(u64, u64), + #[error("Failed to open kernel image")] + BootLoaderOpenKernel, + #[error("Failed to open initrd image")] + BootLoaderOpenInitrd, + #[error("Configure cpu number({0}) above supported max cpu numbers(254)")] + MaxCpus(u8), + #[error("Invalid bzImage kernel file")] + #[cfg(target_arch = "x86_64")] + InvalidBzImage, + #[error("Kernel version is too old.")] + #[cfg(target_arch = "x86_64")] + OldVersionKernel, + #[error("ELF-format kernel is not supported")] + #[cfg(target_arch = "x86_64")] + ElfKernel, +} diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index 66ed338bd..6dfb2e876 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -76,6 +76,7 @@ #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] mod aarch64; +pub mod error; #[cfg(target_arch = "x86_64")] mod x86_64; @@ -85,6 +86,7 @@ pub use aarch64::load_linux; pub use aarch64::AArch64BootLoader as BootLoader; #[cfg(target_arch = "aarch64")] pub use aarch64::AArch64BootLoaderConfig as BootLoaderConfig; +pub use error::BootLoaderError; #[cfg(target_arch = "x86_64")] pub use x86_64::load_linux; @@ -92,59 +94,3 @@ pub use x86_64::load_linux; pub use x86_64::X86BootLoader as BootLoader; #[cfg(target_arch = "x86_64")] pub use x86_64::X86BootLoaderConfig as BootLoaderConfig; - -pub mod errors { - use error_chain::error_chain; - - error_chain! { - foreign_links { - Io(std::io::Error); - } - links { - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - FwCfg(devices::legacy::errors::Error, devices::legacy::errors::ErrorKind); - } - errors { - #[allow(clippy::upper_case_acronyms)] - #[cfg(target_arch = "aarch64")] DTBOverflow(size: u64) { - display( - "guest memory size {} should bigger than {}", - size, - util::device_tree::FDT_MAX_SIZE - ) - } - KernelOverflow(addr: u64, size: u64) { - display( - "Failed to load kernel image {} to memory {}.", - size, - addr - ) - } - InitrdOverflow(addr: u64, size: u64) { - display( - "Failed to load initrd image {} to memory {}.", - size, - addr - ) - } - BootLoaderOpenKernel { - display("Failed to open kernel image") - } - BootLoaderOpenInitrd { - display("Failed to open initrd image") - } - MaxCpus(cpus: u8) { - display("Configure cpu number({}) above supported max cpu numbers(254)", cpus) - } - #[cfg(target_arch = "x86_64")] InvalidBzImage { - display("Invalid bzImage kernel file") - } - #[cfg(target_arch = "x86_64")] OldVersionKernel { - display("Kernel version is too old.") - } - #[cfg(target_arch = "x86_64")] ElfKernel { - display("ELF-format kernel is not supported") - } - } - } -} diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs index 8f1b35da8..45de66a05 100644 --- a/boot_loader/src/x86_64/bootparam.rs +++ b/boot_loader/src/x86_64/bootparam.rs @@ -19,7 +19,8 @@ use super::{ X86BootLoaderConfig, EBDA_START, MB_BIOS_BEGIN, REAL_MODE_IVT_BEGIN, VGA_RAM_BEGIN, VMLINUX_RAM_START, }; -use crate::errors::{ErrorKind, Result}; +use crate::error::BootLoaderError; +use anyhow::{anyhow, Result}; pub const E820_RAM: u32 = 1; pub const E820_RESERVED: u32 = 2; @@ -91,13 +92,13 @@ impl RealModeKernelHeader { pub fn check_valid_kernel(&self) -> Result<()> { if self.header != HDRS { - return Err(ErrorKind::ElfKernel.into()); + return Err(anyhow!(BootLoaderError::ElfKernel)); } if (self.version < BOOT_VERSION) || ((self.loadflags & 0x1) == 0x0) { - return Err(ErrorKind::InvalidBzImage.into()); + return Err(anyhow!(BootLoaderError::InvalidBzImage)); } if self.version < 0x202 { - return Err(ErrorKind::OldVersionKernel.into()); + return Err(anyhow!(BootLoaderError::OldVersionKernel)); } Ok(()) } diff --git a/boot_loader/src/x86_64/direct_boot/gdt.rs b/boot_loader/src/x86_64/direct_boot/gdt.rs index 79f7e2c81..2abea571a 100644 --- a/boot_loader/src/x86_64/direct_boot/gdt.rs +++ b/boot_loader/src/x86_64/direct_boot/gdt.rs @@ -12,15 +12,13 @@ use std::sync::Arc; -use address_space::{AddressSpace, GuestAddress}; -use kvm_bindings::kvm_segment; - use super::super::BootGdtSegment; use super::super::{ BOOT_GDT_MAX, BOOT_GDT_OFFSET, BOOT_IDT_OFFSET, GDT_ENTRY_BOOT_CS, GDT_ENTRY_BOOT_DS, }; -use crate::errors::{Result, ResultExt}; - +use address_space::{AddressSpace, GuestAddress}; +use anyhow::{Context, Result}; +use kvm_bindings::kvm_segment; // /* // * Constructor for a conventional segment GDT (or LDT) entry. // * This is a macro so it can be used in initializers. @@ -95,7 +93,7 @@ fn write_gdt_table(table: &[u64], guest_mem: &Arc) -> Result<()> { for (_, entry) in table.iter().enumerate() { guest_mem .write_object(entry, GuestAddress(boot_gdt_addr)) - .chain_err(|| format!("Failed to load gdt to 0x{:x}", boot_gdt_addr))?; + .with_context(|| format!("Failed to load gdt to 0x{:x}", boot_gdt_addr))?; boot_gdt_addr += 8; } Ok(()) @@ -105,7 +103,7 @@ fn write_idt_value(val: u64, guest_mem: &Arc) -> Result<()> { let boot_idt_addr = BOOT_IDT_OFFSET; guest_mem .write_object(&val, GuestAddress(boot_idt_addr)) - .chain_err(|| format!("Failed to load gdt to 0x{:x}", boot_idt_addr))?; + .with_context(|| format!("Failed to load gdt to 0x{:x}", boot_idt_addr))?; Ok(()) } diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index ae6241a09..056b64b73 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -18,7 +18,6 @@ use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; -use error_chain::bail; use log::info; use util::byte_code::ByteCode; @@ -30,8 +29,8 @@ use super::{ BOOT_HDR_START, BOOT_LOADER_SP, BZIMAGE_BOOT_OFFSET, CMDLINE_START, EBDA_START, INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; -use crate::errors::{ErrorKind, Result, ResultExt}; - +use crate::error::BootLoaderError; +use anyhow::{anyhow, bail, Context, Result}; /// Load bzImage linux kernel to Guest Memory. /// /// # Notes @@ -58,7 +57,7 @@ fn load_bzimage(kernel_image: &mut File) -> Result { kernel_image.seek(SeekFrom::Start(BOOT_HDR_START))?; kernel_image .read_exact(boot_hdr.as_mut_bytes()) - .chain_err(|| "Failed to read boot_hdr from bzImage kernel")?; + .with_context(|| "Failed to read boot_hdr from bzImage kernel")?; boot_hdr.type_of_loader = UNDEFINED_ID; if let Err(e) = boot_hdr.check_valid_kernel() { @@ -101,7 +100,8 @@ fn load_kernel_image( sys_mem: &Arc, boot_layout: &mut X86BootLoader, ) -> Result { - let mut kernel_image = File::open(kernel_path).chain_err(|| ErrorKind::BootLoaderOpenKernel)?; + let mut kernel_image = + File::open(kernel_path).with_context(|| anyhow!(BootLoaderError::BootLoaderOpenKernel))?; let (boot_hdr, kernel_start, vmlinux_start) = if let Ok(hdr) = load_bzimage(&mut kernel_image) { ( @@ -117,7 +117,8 @@ fn load_kernel_image( ) }; - load_image(&mut kernel_image, vmlinux_start, sys_mem).chain_err(|| "Failed to load image")?; + load_image(&mut kernel_image, vmlinux_start, sys_mem) + .with_context(|| "Failed to load image")?; boot_layout.boot_ip = kernel_start; @@ -140,11 +141,11 @@ fn load_initrd( }; let mut initrd_image = File::open(config.initrd.as_ref().unwrap()) - .chain_err(|| ErrorKind::BootLoaderOpenInitrd)?; + .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; let initrd_size = initrd_image.metadata().unwrap().len() as u64; let initrd_addr = (initrd_addr_max - initrd_size) & !0xfff_u64; - load_image(&mut initrd_image, initrd_addr, sys_mem).chain_err(|| "Failed to load image")?; + load_image(&mut initrd_image, initrd_addr, sys_mem).with_context(|| "Failed to load image")?; header.set_ramdisk(initrd_addr as u32, initrd_size as u32); @@ -162,13 +163,13 @@ fn setup_page_table(sys_mem: &Arc) -> Result { let pdpte = boot_pdpte_addr | 0x03; sys_mem .write_object(&pdpte, GuestAddress(boot_pml4_addr)) - .chain_err(|| format!("Failed to load PD PTE to 0x{:x}", boot_pml4_addr))?; + .with_context(|| format!("Failed to load PD PTE to 0x{:x}", boot_pml4_addr))?; // Entry covering VA [0..1GB) let pde = boot_pde_addr | 0x03; sys_mem .write_object(&pde, GuestAddress(boot_pdpte_addr)) - .chain_err(|| format!("Failed to load PDE to 0x{:x}", boot_pdpte_addr))?; + .with_context(|| format!("Failed to load PDE to 0x{:x}", boot_pdpte_addr))?; // 512 2MB entries together covering VA [0..1GB). Note we are assuming // CPU supports 2MB pages (/proc/cpuinfo has 'pse'). All modern CPUs do. @@ -176,7 +177,7 @@ fn setup_page_table(sys_mem: &Arc) -> Result { let pde = (i << 21) + 0x83u64; sys_mem .write_object(&pde, GuestAddress(boot_pde_addr + i * 8)) - .chain_err(|| format!("Failed to load PDE to 0x{:x}", boot_pde_addr + i * 8))?; + .with_context(|| format!("Failed to load PDE to 0x{:x}", boot_pde_addr + i * 8))?; } Ok(boot_pml4_addr) @@ -191,7 +192,7 @@ fn setup_boot_params( boot_params.setup_e820_entries(config, sys_mem); sys_mem .write_object(&boot_params, GuestAddress(ZERO_PAGE_START)) - .chain_err(|| format!("Failed to load zero page to 0x{:x}", ZERO_PAGE_START))?; + .with_context(|| format!("Failed to load zero page to 0x{:x}", ZERO_PAGE_START))?; Ok(()) } @@ -252,12 +253,13 @@ pub fn load_linux( )?; load_initrd(config, sys_mem, &mut boot_header) - .chain_err(|| "Failed to load initrd to vm memory")?; + .with_context(|| "Failed to load initrd to vm memory")?; setup_kernel_cmdline(config, sys_mem, &mut boot_header) - .chain_err(|| "Failed to setup kernel cmdline")?; + .with_context(|| "Failed to setup kernel cmdline")?; - setup_boot_params(config, sys_mem, &boot_header).chain_err(|| "Failed to setup boot params")?; + setup_boot_params(config, sys_mem, &boot_header) + .with_context(|| "Failed to setup boot params")?; setup_isa_mptable( sys_mem, @@ -268,8 +270,8 @@ pub fn load_linux( )?; boot_loader_layout.boot_pml4_addr = - setup_page_table(sys_mem).chain_err(|| "Failed to setup page table")?; - boot_loader_layout.segments = setup_gdt(sys_mem).chain_err(|| "Failed to setup gdt")?; + setup_page_table(sys_mem).with_context(|| "Failed to setup page table")?; + boot_loader_layout.segments = setup_gdt(sys_mem).with_context(|| "Failed to setup gdt")?; Ok(boot_loader_layout) } diff --git a/boot_loader/src/x86_64/direct_boot/mptable.rs b/boot_loader/src/x86_64/direct_boot/mptable.rs index 4adc3e3d2..b20c0c1a3 100644 --- a/boot_loader/src/x86_64/direct_boot/mptable.rs +++ b/boot_loader/src/x86_64/direct_boot/mptable.rs @@ -12,12 +12,12 @@ use std::sync::Arc; +use crate::error::BootLoaderError; use address_space::{AddressSpace, GuestAddress}; +use anyhow::{anyhow, Result}; use util::byte_code::ByteCode; use util::checksum::obj_checksum; -use crate::errors::{ErrorKind, Result}; - const SPEC_VERSION: u8 = 4; // version 1.4 const APIC_VERSION: u8 = 0x14; @@ -285,7 +285,7 @@ pub fn setup_isa_mptable( const MPTABLE_IOAPIC_NR: u8 = 16; if u32::from(num_cpus) > MPTABLE_MAX_CPUS { - return Err(ErrorKind::MaxCpus(num_cpus).into()); + return Err(anyhow!(BootLoaderError::MaxCpus(num_cpus))); } let ioapic_id: u8 = num_cpus + 1; diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs index 4cbe22c4f..2de509722 100644 --- a/boot_loader/src/x86_64/mod.rs +++ b/boot_loader/src/x86_64/mod.rs @@ -62,10 +62,9 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use devices::legacy::FwCfgOps; -use error_chain::bail; use kvm_bindings::kvm_segment; -use crate::errors::Result; +use anyhow::{bail, Result}; const ZERO_PAGE_START: u64 = 0x0000_7000; const PML4_START: u64 = 0x0000_9000; diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 17b0406e0..61abe3ad0 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -16,11 +16,10 @@ use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{FwCfgEntryType, FwCfgOps}; -use error_chain::bail; use util::byte_code::ByteCode; use util::num_ops::round_up; -use crate::errors::{Result, ResultExt}; +use anyhow::{anyhow, bail, Context, Result}; const EI_MAG0: usize = 0; const EI_MAG1: usize = 1; @@ -145,11 +144,11 @@ pub fn load_elf_kernel( kernel_image.read_exact(elf_header.as_mut_bytes())?; elf_header .is_valid() - .chain_err(|| "ELF header is invalid")?; + .with_context(|| "ELF header is invalid")?; let ep_hdrs = elf_header .parse_prog_hdrs(kernel_image) - .chain_err(|| "Failed to parse ELF program header")?; + .with_context(|| "Failed to parse ELF program header")?; let mut pvh_start_addr: Option = None; let mut addr_low = u64::MAX; @@ -185,10 +184,13 @@ pub fn load_elf_kernel( offset += note_size; let p_align = ph.p_align; - let aligned_namesz = round_up(note_hdr.namesz as u64, p_align).ok_or(format!( - "Overflows when align up: num 0x{:x}, alignment 0x{:x}", - note_hdr.namesz as u64, p_align, - ))?; + let aligned_namesz = + round_up(note_hdr.namesz as u64, p_align).ok_or_else(|| { + anyhow!(format!( + "Overflows when align up: num 0x{:x}, alignment 0x{:x}", + note_hdr.namesz as u64, p_align, + )) + })?; if note_hdr.type_ == XEN_ELFNOTE_PHYS32_ENTRY { kernel_image.seek(SeekFrom::Current(aligned_namesz as i64))?; @@ -198,10 +200,12 @@ pub fn load_elf_kernel( break; } else { let aligned_descsz = - round_up(note_hdr.descsz as u64, p_align).ok_or(format!( - "Overflows when align up, num 0x{:x}, alignment 0x{:x}", - note_hdr.descsz as u64, p_align, - ))?; + round_up(note_hdr.descsz as u64, p_align).ok_or_else(|| { + anyhow!(format!( + "Overflows when align up, num 0x{:x}, alignment 0x{:x}", + note_hdr.descsz as u64, p_align, + )) + })?; let tail_size = aligned_namesz + aligned_descsz; kernel_image.seek(SeekFrom::Current(tail_size as i64))?; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 705da226a..dac4052b1 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -19,7 +19,6 @@ use std::sync::Arc; use address_space::AddressSpace; use devices::legacy::{FwCfgEntryType, FwCfgOps}; -use error_chain::bail; use log::{error, info}; use util::byte_code::ByteCode; @@ -27,9 +26,10 @@ use self::elf::load_elf_kernel; use super::bootparam::RealModeKernelHeader; use super::X86BootLoaderConfig; use super::{BOOT_HDR_START, CMDLINE_START}; -use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::error::BootLoaderError; use crate::x86_64::bootparam::{E820Entry, E820_RAM, E820_RESERVED, UEFI_OVMF_ID}; use crate::x86_64::{INITRD_ADDR_MAX, SETUP_START}; +use anyhow::{anyhow, bail, Context, Result}; fn load_image( image: &mut File, @@ -71,18 +71,18 @@ fn load_kernel_image( let kernel_size = kernel_image.metadata().unwrap().len() - setup_size; load_image(kernel_image, setup_size, FwCfgEntryType::KernelData, fwcfg) - .chain_err(|| "Failed to load kernel image")?; + .with_context(|| "Failed to load kernel image")?; let kernel_start = header.code32_start; // boot_hdr.code32_start = 0x100000 fwcfg .add_data_entry(FwCfgEntryType::KernelAddr, kernel_start.as_bytes().to_vec()) - .chain_err(|| "Failed to add kernel-addr entry to FwCfg")?; + .with_context(|| "Failed to add kernel-addr entry to FwCfg")?; fwcfg .add_data_entry( FwCfgEntryType::KernelSize, (kernel_size as u32).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add kernel-size entry to FwCfg")?; + .with_context(|| "Failed to add kernel-size entry to FwCfg")?; Ok(setup_data) } @@ -103,24 +103,24 @@ fn load_initrd( }; let mut initrd_image = File::open(config.initrd.as_ref().unwrap()) - .chain_err(|| ErrorKind::BootLoaderOpenInitrd)?; + .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; let initrd_size = initrd_image.metadata().unwrap().len() as u64; let initrd_addr = (initrd_addr_max - initrd_size) & !0xfff_u64; load_image(&mut initrd_image, 0, FwCfgEntryType::InitrdData, fwcfg) - .chain_err(|| "Failed to load initrd")?; + .with_context(|| "Failed to load initrd")?; fwcfg .add_data_entry( FwCfgEntryType::InitrdAddr, (initrd_addr as u32).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add initrd-addr entry to FwCfg")?; + .with_context(|| "Failed to add initrd-addr entry to FwCfg")?; fwcfg .add_data_entry( FwCfgEntryType::InitrdSize, (initrd_size as u32).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add initrd-size to FwCfg")?; + .with_context(|| "Failed to add initrd-size to FwCfg")?; header.set_ramdisk(initrd_addr as u32, initrd_size as u32); Ok(()) @@ -158,7 +158,7 @@ fn setup_e820_table( }); fwcfg .add_file_entry("etc/e820", bytes) - .chain_err(|| "Failed to add e820 file entry to FwCfg")?; + .with_context(|| "Failed to add e820 file entry to FwCfg")?; Ok(()) } @@ -175,17 +175,17 @@ fn load_kernel_cmdline( FwCfgEntryType::CmdlineAddr, (CMDLINE_START as u32).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add cmdline-addr entry to FwCfg")?; + .with_context(|| "Failed to add cmdline-addr entry to FwCfg")?; // The length of cmdline should add the tailing `\0`. fwcfg .add_data_entry( FwCfgEntryType::CmdlineSize, (cmdline_len + 1).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add cmdline-size entry to FwCfg")?; + .with_context(|| "Failed to add cmdline-size entry to FwCfg")?; fwcfg .add_string_entry(FwCfgEntryType::CmdlineData, config.kernel_cmdline.as_ref()) - .chain_err(|| "Failed to add cmdline-data entry to FwCfg")?; + .with_context(|| "Failed to add cmdline-data entry to FwCfg")?; Ok(()) } @@ -208,7 +208,7 @@ pub fn load_linux( } let mut kernel_image = File::open(config.kernel.as_ref().unwrap().clone()) - .chain_err(|| ErrorKind::BootLoaderOpenKernel)?; + .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenKernel))?; let mut boot_header = RealModeKernelHeader::default(); kernel_image.seek(SeekFrom::Start(BOOT_HDR_START))?; @@ -219,12 +219,14 @@ pub fn load_linux( setup_e820_table(config, sys_mem, fwcfg)?; load_initrd(config, sys_mem, &mut boot_header, fwcfg)?; if let Err(e) = boot_header.check_valid_kernel() { - match e.kind() { - ErrorKind::ElfKernel => { - load_elf_kernel(&mut kernel_image, sys_mem, fwcfg)?; - return Ok(()); + if let Some(err) = e.downcast_ref::() { + match err { + BootLoaderError::ElfKernel => { + load_elf_kernel(&mut kernel_image, sys_mem, fwcfg)?; + return Ok(()); + } + _ => return Err(e), } - _ => return Err(e), } } @@ -241,16 +243,16 @@ pub fn load_linux( FwCfgEntryType::SetupAddr, (SETUP_START as u32).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add setup-addr to FwCfg")?; + .with_context(|| "Failed to add setup-addr to FwCfg")?; fwcfg .add_data_entry( FwCfgEntryType::SetupSize, (setup_data.len() as u32).as_bytes().to_vec(), ) - .chain_err(|| "Failed to add setup-size entry to FwCfg")?; + .with_context(|| "Failed to add setup-size entry to FwCfg")?; fwcfg .add_data_entry(FwCfgEntryType::SetupData, setup_data) - .chain_err(|| "Failed to add setup-data entry to FwCfg")?; + .with_context(|| "Failed to add setup-data entry to FwCfg")?; Ok(()) } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index a031ddb90..acc473857 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -7,7 +7,8 @@ license = "Mulan PSL v2" description = "CPU emulation" [dependencies] -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" log = "0.4" diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 7d74b8616..02040d2dc 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -25,10 +25,12 @@ use kvm_ioctls::VcpuFd; pub use self::caps::ArmCPUCaps; use self::caps::CpregListEntry; use self::core_regs::{get_core_regs, set_core_regs}; -use crate::errors::{Result, ResultExt}; use crate::CPU; +use anyhow::{anyhow, Context, Result}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -156,7 +158,7 @@ impl ArmCPUState { .as_ref() .unwrap() .get_preferred_target(&mut self.kvi) - .chain_err(|| "Failed to get kvm vcpu preferred target")?; + .with_context(|| "Failed to get kvm vcpu preferred target")?; // support PSCI 0.2 // We already checked that the capability is supported. @@ -170,10 +172,10 @@ impl ArmCPUState { vcpu_fd .vcpu_init(&self.kvi) - .chain_err(|| "Failed to init kvm vcpu")?; + .with_context(|| "Failed to init kvm vcpu")?; self.mpidr = vcpu_fd .get_one_reg(SYS_MPIDR_EL1) - .chain_err(|| "Failed to get mpidr")?; + .with_context(|| "Failed to get mpidr")?; Ok(()) } @@ -194,18 +196,18 @@ impl ArmCPUState { /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn reset_vcpu(&self, vcpu_fd: &Arc) -> Result<()> { set_core_regs(vcpu_fd, self.core_regs) - .chain_err(|| format!("Failed to set core register for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set core register for CPU {}", self.apic_id))?; vcpu_fd .set_mp_state(self.mp_state) - .chain_err(|| format!("Failed to set mpstate for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set mpstate for CPU {}", self.apic_id))?; for cpreg in self.cpreg_list[0..self.cpreg_len].iter() { cpreg .set_cpreg(&vcpu_fd.clone()) - .chain_err(|| format!("Failed to set cpreg for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set cpreg for CPU {}", self.apic_id))?; } vcpu_fd .set_vcpu_events(&self.cpu_events) - .chain_err(|| format!("Failed to set vcpu event for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set vcpu event for CPU {}", self.apic_id))?; Ok(()) } @@ -241,7 +243,7 @@ impl ArmCPUState { } impl StateTransfer for CPU { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); cpu_state_locked.core_regs = get_core_regs(&self.fd)?; @@ -272,9 +274,9 @@ impl StateTransfer for CPU { Ok(cpu_state_locked.as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state(&self, state: &[u8]) -> migration::Result<()> { let cpu_state = *ArmCPUState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("CPU"))?; + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("CPU")))?; let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); *cpu_state_locked = cpu_state; diff --git a/cpu/src/error.rs b/cpu/src/error.rs new file mode 100644 index 000000000..4e6517573 --- /dev/null +++ b/cpu/src/error.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CpuError { + #[error("Signal")] + Signal { + #[from] + source: vmm_sys_util::errno::Error, + }, + + #[error("Failed to create kvm vcpu: {0}!")] + CreateVcpu(String), + #[error("Failed to configure kvm vcpu: {0}!")] + RealizeVcpu(String), + #[error("Failed to starting kvm vcpu: {0}!")] + StartVcpu(String), + #[error("Failed to stopping kvm vcpu: {0}!")] + StopVcpu(String), + #[error("Failed to kick kvm vcpu: {0}!")] + KickVcpu(String), + #[error("Failed to destroy kvm vcpu: {0}!")] + DestroyVcpu(String), + #[error("CPU {0}/KVM halted!")] + VcpuHltEvent(u8), + #[error("CPU {0}/KVM received an unexpected exit reason: {1}!")] + VcpuExitReason(u8, String), + #[error("CPU {0}/KVM received an unhandled kvm exit event!")] + UnhandledKvmExit(u8), + #[error("Vcpu not present in local thread.")] + VcpuLocalThreadNotPresent, + #[error("No Machine Interface saved in CPU")] + NoMachineInterface, + #[cfg(target_arch = "aarch64")] + #[error("Failed to get system register: {0}!")] + GetSysRegister(String), + #[cfg(target_arch = "aarch64")] + #[error("Failed to Set system register: {0}!")] + SetSysRegister(String), +} diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index b0ab37d4d..281b891b8 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -34,60 +34,9 @@ mod aarch64; #[cfg(target_arch = "x86_64")] mod x86_64; -pub mod errors { - use error_chain::error_chain; - - error_chain! { - foreign_links { - Signal(vmm_sys_util::errno::Error); - } - errors { - CreateVcpu(err_info: String) { - display("Failed to create kvm vcpu: {}!", err_info) - } - RealizeVcpu(err_info: String) { - display("Failed to configure kvm vcpu: {}!", err_info) - } - StartVcpu(err_info: String) { - display("Failed to starting kvm vcpu: {}!", err_info) - } - StopVcpu(err_info: String) { - display("Failed to stopping kvm vcpu: {}!", err_info) - } - KickVcpu(err_info: String) { - display("Failed to kick kvm vcpu: {}!", err_info) - } - DestroyVcpu(err_info: String) { - display("Failed to destroy kvm vcpu: {}!", err_info) - } - VcpuHltEvent(cpu_id: u8) { - display("CPU {}/KVM halted!", cpu_id) - } - VcpuExitReason(cpu_id: u8, err_info: String) { - display("CPU {}/KVM received an unexpected exit reason: {}!", cpu_id, err_info) - } - UnhandledKvmExit(cpu_id: u8) { - display("CPU {}/KVM received an unhandled kvm exit event!", cpu_id) - } - VcpuLocalThreadNotPresent { - display("Vcpu not present in local thread.") - } - NoMachineInterface { - display("No Machine Interface saved in CPU") - } - #[cfg(target_arch = "aarch64")] - GetSysRegister(err_info: String) { - description("Get sys Register error") - display("Failed to get system register: {}!", err_info) - } - #[cfg(target_arch = "aarch64")] - SetSysRegister(err_info: String) { - description("Set sys Register error") - display("Failed to Set system register: {}!", err_info) - } - } - } -} +pub mod error; +use anyhow::{anyhow, Context, Result}; +pub use error::CpuError; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUBootConfig as CPUBootConfig; @@ -121,8 +70,6 @@ use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; use vmm_sys_util::signal::{register_signal_handler, Killable}; -use errors::{ErrorKind, Result, ResultExt}; - // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal // number should be assigned to SIGRTMIN + n, (n = 0...30). #[cfg(not(target_env = "musl"))] @@ -294,22 +241,23 @@ impl CPUInterface for CPU { trace_cpu_boot_config(boot); let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { - return Err( - ErrorKind::RealizeVcpu(format!("VCPU{} may has realized.", self.id())).into(), - ); + return Err(anyhow!(CpuError::RealizeVcpu(format!( + "VCPU{} may has realized.", + self.id() + )))); } self.arch_cpu .lock() .unwrap() .set_boot_config(&self.fd, boot) - .chain_err(|| "Failed to realize arch cpu")?; + .with_context(|| "Failed to realize arch cpu")?; self.arch_cpu .lock() .unwrap() .set_cpu_topology(topology) - .chain_err(|| "Failed to realize arch cpu")?; + .with_context(|| "Failed to realize arch cpu")?; self.boot_state.lock().unwrap().set(&self.arch_cpu); Ok(()) @@ -333,7 +281,9 @@ impl CPUInterface for CPU { fn start(cpu: Arc, thread_barrier: Arc, paused: bool) -> Result<()> { let (cpu_state, _) = &*cpu.state; if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { - return Err(ErrorKind::StartVcpu("Cpu is already running".to_string()).into()); + return Err(anyhow!(CpuError::StartVcpu( + "Cpu is already running".to_string() + ))); } if paused { *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; @@ -348,13 +298,15 @@ impl CPUInterface for CPU { .spawn(move || { if let Err(e) = cpu_thread_worker.handle(thread_barrier) { error!( - "Some error occurred in cpu{} thread: {}", - cpu_thread_worker.thread_cpu.id, - error_chain::ChainedError::display_chain(&e) + "{}", + format!( + "Some error occurred in cpu{} thread: {:?}", + cpu_thread_worker.thread_cpu.id, e + ) ); } }) - .chain_err(|| format!("Failed to create thread for CPU {}/KVM", local_cpu.id()))?; + .with_context(|| format!("Failed to create thread for CPU {}/KVM", local_cpu.id()))?; local_cpu.set_task(Some(handle)); Ok(()) } @@ -364,7 +316,7 @@ impl CPUInterface for CPU { match task.as_ref() { Some(thread) => thread .kill(VCPU_RESET_SIGNAL) - .chain_err(|| ErrorKind::KickVcpu("Fail to reset vcpu".to_string())), + .with_context(|| anyhow!(CpuError::KickVcpu("Fail to reset vcpu".to_string()))), None => { warn!("VCPU thread not started, no need to reset"); Ok(()) @@ -377,7 +329,7 @@ impl CPUInterface for CPU { match task.as_ref() { Some(thread) => thread .kill(VCPU_TASK_SIGNAL) - .chain_err(|| ErrorKind::KickVcpu("Fail to kick vcpu".to_string())), + .with_context(|| anyhow!(CpuError::KickVcpu("Fail to kick vcpu".to_string()))), None => { warn!("VCPU thread not started, no need to kick"); Ok(()) @@ -397,7 +349,7 @@ impl CPUInterface for CPU { match task.as_ref() { Some(thread) => { if let Err(e) = thread.kill(VCPU_TASK_SIGNAL) { - return Err(ErrorKind::StopVcpu(format!("{}", e)).into()); + return Err(anyhow!(CpuError::StopVcpu(format!("{:?}", e)))); } } None => { @@ -436,7 +388,10 @@ impl CPUInterface for CPU { *cpu_state = CpuLifecycleState::Nothing; Ok(()) } else { - Err(ErrorKind::DestroyVcpu(format!("VCPU still in {:?} state", *cpu_state)).into()) + Err(anyhow!(CpuError::DestroyVcpu(format!( + "VCPU still in {:?} state", + *cpu_state + )))) } } @@ -447,7 +402,7 @@ impl CPUInterface for CPU { if let Some(vm) = self.vm.upgrade() { vm.lock().unwrap().destroy(); } else { - return Err(ErrorKind::NoMachineInterface.into()); + return Err(anyhow!(CpuError::NoMachineInterface)); } if QmpChannel::is_connected() { @@ -465,7 +420,7 @@ impl CPUInterface for CPU { if let Some(vm) = self.vm.upgrade() { vm.lock().unwrap().reset(); } else { - return Err(ErrorKind::NoMachineInterface.into()); + return Err(anyhow!(CpuError::NoMachineInterface)); } if QmpChannel::is_connected() { @@ -480,7 +435,7 @@ impl CPUInterface for CPU { let vm = if let Some(vm) = self.vm.upgrade() { vm } else { - return Err(ErrorKind::NoMachineInterface.into()); + return Err(anyhow!(CpuError::NoMachineInterface)); }; match self.fd.run() { @@ -502,7 +457,7 @@ impl CPUInterface for CPU { #[cfg(target_arch = "x86_64")] VcpuExit::Hlt => { info!("Vcpu{} received KVM_EXIT_HLT signal", self.id()); - return Err(ErrorKind::VcpuHltEvent(self.id()).into()); + return Err(anyhow!(CpuError::VcpuHltEvent(self.id()))); } #[cfg(target_arch = "x86_64")] VcpuExit::Shutdown => { @@ -519,14 +474,14 @@ impl CPUInterface for CPU { self.id() ); self.guest_shutdown() - .chain_err(|| "Some error occurred in guest shutdown")?; + .with_context(|| "Some error occurred in guest shutdown")?; } else if event == kvm_bindings::KVM_SYSTEM_EVENT_RESET { info!( "Vcpu{} received an KVM_SYSTEM_EVENT_RESET signal", self.id() ); self.guest_reset() - .chain_err(|| "Some error occurred in guest reset")?; + .with_context(|| "Some error occurred in guest reset")?; return Ok(true); } else { error!( @@ -548,7 +503,10 @@ impl CPUInterface for CPU { return Ok(false); } r => { - return Err(ErrorKind::VcpuExitReason(self.id(), format!("{:?}", r)).into()); + return Err(anyhow!(CpuError::VcpuExitReason( + self.id(), + format!("{:?}", r) + ))); } }, Err(ref e) => { @@ -558,7 +516,7 @@ impl CPUInterface for CPU { self.fd.set_kvm_immediate_exit(0); } _ => { - return Err(ErrorKind::UnhandledKvmExit(self.id()).into()); + return Err(anyhow!(CpuError::UnhandledKvmExit(self.id()))); } }; } @@ -599,7 +557,7 @@ impl CPUThreadWorker { func(&local_thread_vcpu.thread_cpu); Ok(()) } else { - Err(ErrorKind::VcpuLocalThreadNotPresent.into()) + Err(anyhow!(CpuError::VcpuLocalThreadNotPresent)) } }) } @@ -632,9 +590,9 @@ impl CPUThreadWorker { } register_signal_handler(VCPU_TASK_SIGNAL, handle_signal) - .chain_err(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; + .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; register_signal_handler(VCPU_RESET_SIGNAL, handle_signal) - .chain_err(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; + .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; Ok(()) } @@ -673,7 +631,10 @@ impl CPUThreadWorker { fn handle(&self, thread_barrier: Arc) -> Result<()> { self.init_local_thread_vcpu(); if let Err(e) = Self::init_signals() { - error!("Failed to init cpu{} signal:{}", self.thread_cpu.id, e); + error!( + "{}", + format!("Failed to init cpu{} signal:{:?}", self.thread_cpu.id, e) + ); } self.thread_cpu.set_tid(); @@ -683,7 +644,7 @@ impl CPUThreadWorker { #[cfg(not(test))] self.thread_cpu .reset() - .chain_err(|| "Failed to reset for cpu register state")?; + .with_context(|| "Failed to reset for cpu register state")?; // Wait for all vcpu to complete the running // environment initialization. @@ -695,7 +656,7 @@ impl CPUThreadWorker { if !self .thread_cpu .kvm_vcpu_exec() - .chain_err(|| format!("VCPU {}/KVM emulate error!", self.thread_cpu.id()))? + .with_context(|| format!("VCPU {}/KVM emulate error!", self.thread_cpu.id()))? { break; } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 709a70740..5a8cb0746 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -15,7 +15,7 @@ mod cpuid; use std::sync::{Arc, Mutex}; -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use kvm_bindings::{ kvm_cpuid_entry2, kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, CpuId, Msrs, @@ -23,12 +23,13 @@ use kvm_bindings::{ KVM_MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use self::cpuid::host_cpuid; -use crate::errors::{Result, ResultExt}; use crate::CPU; const ECX_EPB_SHIFT: u32 = 3; @@ -210,43 +211,43 @@ impl X86CPUState { /// * `caps` - Vcpu capabilities in kvm. pub fn reset_vcpu(&self, vcpu_fd: &Arc, caps: &caps::X86CPUCaps) -> Result<()> { self.setup_cpuid(vcpu_fd) - .chain_err(|| format!("Failed to set cpuid for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set cpuid for CPU {}", self.apic_id))?; vcpu_fd .set_mp_state(self.mp_state) - .chain_err(|| format!("Failed to set mpstate for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set mpstate for CPU {}", self.apic_id))?; vcpu_fd .set_sregs(&self.sregs) - .chain_err(|| format!("Failed to set sregs for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set sregs for CPU {}", self.apic_id))?; vcpu_fd .set_regs(&self.regs) - .chain_err(|| format!("Failed to set regs for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set regs for CPU {}", self.apic_id))?; if caps.has_xsave { vcpu_fd .set_xsave(&self.xsave) - .chain_err(|| format!("Failed to set xsave for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set xsave for CPU {}", self.apic_id))?; } else { vcpu_fd .set_fpu(&self.fpu) - .chain_err(|| format!("Failed to set fpu for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set fpu for CPU {}", self.apic_id))?; } if caps.has_xcrs { vcpu_fd .set_xcrs(&self.xcrs) - .chain_err(|| format!("Failed to set xcrs for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set xcrs for CPU {}", self.apic_id))?; } vcpu_fd .set_debug_regs(&self.debugregs) - .chain_err(|| format!("Failed to set debug register for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set debug register for CPU {}", self.apic_id))?; vcpu_fd .set_lapic(&self.lapic) - .chain_err(|| format!("Failed to set lapic for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set lapic for CPU {}", self.apic_id))?; vcpu_fd .set_msrs(&Msrs::from_entries(&self.msr_list[0..self.msr_len]).unwrap()) - .chain_err(|| format!("Failed to set msrs for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set msrs for CPU {}", self.apic_id))?; vcpu_fd .set_vcpu_events(&self.cpu_events) - .chain_err(|| format!("Failed to set vcpu events for CPU {}", self.apic_id))?; + .with_context(|| format!("Failed to set vcpu events for CPU {}", self.apic_id))?; Ok(()) } @@ -262,7 +263,7 @@ impl X86CPUState { self.lapic = vcpu_fd .get_lapic() - .chain_err(|| format!("Failed to get lapic for CPU {}/KVM", self.apic_id))?; + .with_context(|| format!("Failed to get lapic for CPU {}/KVM", self.apic_id))?; // The member regs in struct kvm_lapic_state is a u8 array with 1024 entries, // so it's saft to cast u8 pointer to u32 at position APIC_LVT0 and APIC_LVT1. @@ -297,7 +298,7 @@ impl X86CPUState { fn setup_sregs(&mut self, vcpu_fd: &Arc, boot_config: &X86CPUBootConfig) -> Result<()> { self.sregs = vcpu_fd .get_sregs() - .chain_err(|| format!("Failed to get sregs for CPU {}/KVM", self.apic_id))?; + .with_context(|| format!("Failed to get sregs for CPU {}/KVM", self.apic_id))?; self.sregs.cs.base = (boot_config.boot_selector as u64) << 4; self.sregs.cs.selector = boot_config.boot_selector; @@ -428,10 +429,10 @@ impl X86CPUState { }; let mut cpuid = sys_fd .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) - .chain_err(|| format!("Failed to get supported cpuid for CPU {}/KVM", self.apic_id))?; - + .with_context(|| { + format!("Failed to get supported cpuid for CPU {}/KVM", self.apic_id) + })?; self.adjust_cpuid(&mut cpuid); - let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { @@ -556,13 +557,13 @@ impl X86CPUState { vcpu_fd .set_cpuid2(&cpuid) - .chain_err(|| format!("Failed to set cpuid for CPU {}/KVM", self.apic_id))?; + .with_context(|| format!("Failed to set cpuid for CPU {}/KVM", self.apic_id))?; Ok(()) } } impl StateTransfer for CPU { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> Result> { let mut msr_entries = self.caps.create_msr_entries(); let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); @@ -588,9 +589,9 @@ impl StateTransfer for CPU { Ok(cpu_state_locked.as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state(&self, state: &[u8]) -> migration::Result<()> { let cpu_state = *X86CPUState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("CPU"))?; + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("CPU")))?; let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); *cpu_state_locked = cpu_state; diff --git a/devices/Cargo.toml b/devices/Cargo.toml index a8599025d..27a079c3c 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" license = "Mulan PSL v2" [dependencies] -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" libc = "0.2" log = "0.4" kvm-ioctls = ">=0.11.0" diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index c15eb48b8..7037a045d 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -13,16 +13,15 @@ use std::marker::{Send, Sync}; use std::sync::{Arc, Mutex}; +use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; +use crate::interrupt_controller::InterruptError; use address_space::AddressSpace; +use anyhow::{anyhow, Context, Result}; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::DeviceFd; use log::error; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use util::device_tree::{self, FdtBuilder}; - -use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; -use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; - // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; @@ -87,7 +86,11 @@ impl GICv2 { fn new(config: &GICConfig) -> Result { let v2config = match config.v2.as_ref() { Some(v2) => v2, - None => return Err(ErrorKind::InvalidConfig("no v2 config found".to_string()).into()), + None => { + return Err(anyhow!(InterruptError::InvalidConfig( + "no v2 config found".to_string() + ))) + } }; let mut gic_device = kvm_bindings::kvm_create_device { @@ -102,7 +105,7 @@ impl GICv2 { .as_ref() .unwrap() .create_device(&mut gic_device) - .chain_err(|| "Failed to create GICv2 device")?; + .with_context(|| "Failed to create GICv2 device")?; let cpu_interface_region = GicCpuInterfaceRegion { base: v2config.dist_range.0 + KVM_VGIC_V2_DIST_SIZE, @@ -135,7 +138,7 @@ impl GICv2 { &self.nr_irqs as *const u32 as u64, true, ) - .chain_err(|| "Failed to set GICv2 attribute: irqs")?; + .with_context(|| "Failed to set GICv2 attribute: irqs")?; // Finalize the GIC. KvmDevice::kvm_device_access( @@ -145,7 +148,7 @@ impl GICv2 { 0, true, ) - .chain_err(|| "KVM failed to initialize GICv2")?; + .with_context(|| "KVM failed to initialize GICv2")?; KvmDevice::kvm_device_access( &self.fd, @@ -154,7 +157,7 @@ impl GICv2 { &self.dist_guest_region.base as *const u64 as u64, true, ) - .chain_err(|| "Failed to set GICv2 attribute: distributor address")?; + .with_context(|| "Failed to set GICv2 attribute: distributor address")?; KvmDevice::kvm_device_access( &self.fd, @@ -163,7 +166,7 @@ impl GICv2 { &self.cpu_interface_region.base as *const u64 as u64, true, ) - .chain_err(|| "Failed to set GICv2 attribute: cpu address")?; + .with_context(|| "Failed to set GICv2 attribute: cpu address")?; *self.state.lock().unwrap() = KvmVmState::Running; @@ -225,7 +228,7 @@ impl GICv2Access for GICv2 { gicd_value as *mut u32 as u64, write, ) - .chain_err(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) + .with_context(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) } fn access_gic_cpu( @@ -242,7 +245,7 @@ impl GICv2Access for GICv2 { gicc_value as *mut u64 as u64, write, ) - .chain_err(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) + .with_context(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) } } @@ -254,7 +257,7 @@ impl GICDevice for GICv2 { } fn realize(&self) -> Result<()> { - self.realize().chain_err(|| "Failed to realize GICv2")?; + self.realize().with_context(|| "Failed to realize GICv2")?; Ok(()) } diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 9798c8278..112a2c253 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -16,7 +16,8 @@ use super::{ state::{GICv3ItsState, GICv3State}, GICConfig, GICDevice, KvmDevice, UtilResult, }; -use crate::interrupt_controller::errors::{ErrorKind, Result, ResultExt}; +use crate::interrupt_controller::error::InterruptError; +use anyhow::{anyhow, Context, Result}; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::DeviceFd; @@ -104,7 +105,11 @@ impl GICv3 { fn new(config: &GICConfig) -> Result { let v3config = match config.v3.as_ref() { Some(v3) => v3, - None => return Err(ErrorKind::InvalidConfig("no v3 config found".to_string()).into()), + None => { + return Err(anyhow!(InterruptError::InvalidConfig( + "no v3 config found".to_string() + ))) + } }; let mut gic_device = kvm_bindings::kvm_create_device { type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, @@ -120,7 +125,7 @@ impl GICv3 { .create_device(&mut gic_device) { Ok(fd) => fd, - Err(e) => return Err(ErrorKind::CreateKvmDevice(e).into()), + Err(e) => return Err(anyhow!(InterruptError::CreateKvmDevice(e))), }; // Calculate GIC redistributor regions' address range according to vcpu count. @@ -159,7 +164,7 @@ impl GICv3 { if let Some(its_range) = v3config.its_range { gicv3.its_dev = Some(Arc::new( - GICv3Its::new(&its_range).chain_err(|| "Failed to create ITS")?, + GICv3Its::new(&its_range).with_context(|| "Failed to create ITS")?, )); } @@ -173,7 +178,7 @@ impl GICv3 { kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION as u64, ) - .chain_err(|| { + .with_context(|| { "Multiple redistributors are acquired while KVM does not provide support." })?; } @@ -186,7 +191,7 @@ impl GICv3 { &self.redist_regions.get(0).unwrap().base as *const u64 as u64, true, ) - .chain_err(|| "Failed to set GICv3 attribute: redistributor address")?; + .with_context(|| "Failed to set GICv3 attribute: redistributor address")?; } else { for redist in &self.redist_regions { KvmDevice::kvm_device_access( @@ -196,7 +201,7 @@ impl GICv3 { &redist.base_attr as *const u64 as u64, true, ) - .chain_err(|| "Failed to set GICv3 attribute: redistributor region address")?; + .with_context(|| "Failed to set GICv3 attribute: redistributor region address")?; } } @@ -207,7 +212,7 @@ impl GICv3 { &self.dist_base as *const u64 as u64, true, ) - .chain_err(|| "Failed to set GICv3 attribute: distributor address")?; + .with_context(|| "Failed to set GICv3 attribute: distributor address")?; KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; @@ -219,7 +224,7 @@ impl GICv3 { &self.nr_irqs as *const u32 as u64, true, ) - .chain_err(|| "Failed to set GICv3 attribute: irqs")?; + .with_context(|| "Failed to set GICv3 attribute: irqs")?; // Finalize the GIC. KvmDevice::kvm_device_access( @@ -229,7 +234,7 @@ impl GICv3 { 0, true, ) - .chain_err(|| "KVM failed to initialize GICv3")?; + .with_context(|| "KVM failed to initialize GICv3")?; let mut state = self.state.lock().unwrap(); *state = KvmVmState::Running; @@ -260,7 +265,10 @@ impl MachineLifecycle for GICv3 { // The ITS tables need to be flushed into guest RAM before VM pause. if let Some(its_dev) = &self.its_dev { if let Err(e) = its_dev.access_gic_its_tables(true) { - error!("Failed to access GIC ITS tables, error: {}", e); + error!( + "{}", + format!("Failed to access GIC ITS tables, error: {:?}", e) + ); return false; } } @@ -314,7 +322,7 @@ impl GICv3Access for GICv3 { gicd_value as *mut u32 as u64, write, ) - .chain_err(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) + .with_context(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) } fn access_gic_redistributor( @@ -331,7 +339,7 @@ impl GICv3Access for GICv3 { gicr_value as *mut u32 as u64, write, ) - .chain_err(|| { + .with_context(|| { format!( "Failed to access gic redistributor for offset 0x{:x}", offset @@ -353,7 +361,7 @@ impl GICv3Access for GICv3 { gicc_value as *mut u64 as u64, write, ) - .chain_err(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) + .with_context(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) } fn access_gic_line_level(&self, offset: u64, gicll_value: &mut u32, write: bool) -> Result<()> { @@ -389,10 +397,10 @@ impl GICDevice for GICv3 { } fn realize(&self) -> Result<()> { - self.realize().chain_err(|| "Failed to realize GICv3")?; + self.realize().with_context(|| "Failed to realize GICv3")?; if let Some(its) = &self.its_dev { - its.realize().chain_err(|| "Failed to realize ITS")?; + its.realize().with_context(|| "Failed to realize ITS")?; } Ok(()) @@ -474,7 +482,7 @@ impl GICv3Its { .create_device(&mut its_device) { Ok(fd) => fd, - Err(e) => return Err(ErrorKind::CreateKvmDevice(e).into()), + Err(e) => return Err(anyhow!(InterruptError::CreateKvmDevice(e))), }; Ok(GICv3Its { @@ -490,7 +498,7 @@ impl GICv3Its { kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), ) - .chain_err(|| "ITS address attribute is not supported for KVM")?; + .with_context(|| "ITS address attribute is not supported for KVM")?; KvmDevice::kvm_device_access( &self.fd, @@ -499,7 +507,7 @@ impl GICv3Its { &self.msi_base as *const u64 as u64, true, ) - .chain_err(|| "Failed to set ITS attribute: ITS address")?; + .with_context(|| "Failed to set ITS attribute: ITS address")?; // Finalize the GIC Its. KvmDevice::kvm_device_access( @@ -509,7 +517,7 @@ impl GICv3Its { &self.msi_base as *const u64 as u64, true, ) - .chain_err(|| "KVM failed to initialize ITS")?; + .with_context(|| "KVM failed to initialize ITS")?; Ok(()) } diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 4c0afa926..ffb8b52ab 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -22,14 +22,14 @@ pub use gicv3::{GICv3, GICv3Config}; use std::sync::Arc; +use crate::interrupt_controller::error::InterruptError; +use anyhow::{anyhow, Context, Result}; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use util::{ device_tree::{self, FdtBuilder}, - errors::Result as UtilResult, + Result as UtilResult, }; -use super::errors::{ErrorKind, Result, ResultExt}; - // First 32 are private to each CPU (SGIs and PPIs). pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; @@ -51,7 +51,7 @@ impl KvmDevice { flags: 0, }; fd.has_device_attr(&attr) - .chain_err(|| "Failed to check device attributes for GIC.")?; + .with_context(|| "Failed to check device attributes for GIC.")?; Ok(()) } @@ -71,11 +71,11 @@ impl KvmDevice { if write { fd.set_device_attr(&attr) - .chain_err(|| "Failed to set device attributes for GIC.")?; + .with_context(|| "Failed to set device attributes for GIC.")?; } else { let mut attr = attr; fd.get_device_attr(&mut attr) - .chain_err(|| "Failed to get device attributes for GIC.")?; + .with_context(|| "Failed to get device attributes for GIC.")?; }; Ok(()) @@ -98,9 +98,9 @@ pub struct GICConfig { impl GICConfig { fn check_sanity(&self) -> Result<()> { if self.max_irq <= GIC_IRQ_INTERNAL { - return Err( - ErrorKind::InvalidConfig("GIC irq numbers need above 32".to_string()).into(), - ); + return Err(anyhow!(InterruptError::InvalidConfig( + "GIC irq numbers need above 32".to_string() + ))); } Ok(()) } @@ -156,13 +156,15 @@ impl InterruptController { None => GICv3::create_device(gic_conf).or_else(|_| GICv2::create_device(gic_conf)), }; let intc = InterruptController { - gic: gic.chain_err(|| "Failed to realize GIC")?, + gic: gic.with_context(|| "Failed to realize GIC")?, }; Ok(intc) } pub fn realize(&self) -> Result<()> { - self.gic.realize().chain_err(|| "Failed to realize GIC")?; + self.gic + .realize() + .with_context(|| "Failed to realize GIC")?; Ok(()) } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 0c20a7bc8..b26e8c0e4 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -14,13 +14,13 @@ use std::mem::size_of; use super::gicv3::{GICv3, GICv3Access, GICv3Its}; use super::GIC_IRQ_INTERNAL; -use crate::interrupt_controller::errors::Result; +use crate::interrupt_controller::Result; +use anyhow::anyhow; use libc::c_uint; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; - /// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. const REGISTER_SIZE: u64 = size_of::() as u64; @@ -573,79 +573,109 @@ pub struct GICv3State { } impl StateTransfer for GICv3 { - fn get_state_vec(&self) -> migration::errors::Result> { - use migration::errors::ErrorKind; + fn get_state_vec(&self) -> migration::Result> { + use migration::MigrationError; let mut state = GICv3State::default(); self.access_gic_redistributor(GICR_TYPER, 0, &mut state.redist_typer_l, false) - .map_err(|e| ErrorKind::GetGicRegsError("redist_typer_l", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::GetGicRegsError( + "redist_typer_l", + e.to_string() + )) + })?; self.access_gic_redistributor(GICR_TYPER + 4, 0, &mut state.redist_typer_h, false) - .map_err(|e| ErrorKind::GetGicRegsError("redist_typer_h", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::GetGicRegsError( + "redist_typer_h", + e.to_string() + )) + })?; self.access_gic_distributor(GICD_CTLR, &mut state.gicd_ctlr, false) - .map_err(|e| ErrorKind::GetGicRegsError("gicd_ctlr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("gicd_ctlr", e.to_string())))?; let plpis = (state.redist_typer_l & 1) != 0; for cpu in 0..self.vcpu_count { state.vcpu_redist[state.redist_len] = self .get_redist(cpu as usize, plpis) - .map_err(|e| ErrorKind::GetGicRegsError("redist", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("redist", e.to_string())))?; state.redist_len += 1; } self.access_gic_distributor(GICD_STATUSR, &mut state.gicd_statusr, false) - .map_err(|e| ErrorKind::GetGicRegsError("gicd_statusr", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::GetGicRegsError( + "gicd_statusr", + e.to_string() + )) + })?; for irq in (GIC_IRQ_INTERNAL..self.nr_irqs).step_by(32) { state.irq_dist[state.dist_len] = self .get_dist(irq as u64) - .map_err(|e| ErrorKind::GetGicRegsError("dist", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("dist", e.to_string())))?; state.dist_len += 1; } for cpu in 0..self.vcpu_count { state.vcpu_iccr[state.iccr_len] = self .get_cpu(cpu as usize) - .map_err(|e| ErrorKind::GetGicRegsError("cpu", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("cpu", e.to_string())))?; state.iccr_len += 1; } Ok(state.as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::errors::Result<()> { - use migration::errors::ErrorKind; + fn set_state(&self, state: &[u8]) -> migration::Result<()> { + use migration::error::MigrationError; let state = GICv3State::from_bytes(state).unwrap(); let mut regu32 = state.redist_typer_l; self.access_gic_redistributor(GICR_TYPER, 0, &mut regu32, false) - .map_err(|e| ErrorKind::SetGicRegsError("gicr_typer_l", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::SetGicRegsError( + "gicr_typer_l", + e.to_string() + )) + })?; let plpis: bool = regu32 & 1 != 0; regu32 = state.redist_typer_h; self.access_gic_redistributor(GICR_TYPER + 4, 0, &mut regu32, false) - .map_err(|e| ErrorKind::SetGicRegsError("gicr_typer_h", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::SetGicRegsError( + "gicr_typer_h", + e.to_string() + )) + })?; regu32 = state.gicd_ctlr; self.access_gic_distributor(GICD_CTLR, &mut regu32, true) - .map_err(|e| ErrorKind::SetGicRegsError("gicd_ctlr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("gicd_ctlr", e.to_string())))?; for gicv3_redist in state.vcpu_redist[0..state.redist_len].iter() { self.set_redist(*gicv3_redist, plpis) - .map_err(|e| ErrorKind::SetGicRegsError("redist", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("redist", e.to_string())))?; } regu32 = state.gicd_statusr; self.access_gic_distributor(GICD_STATUSR, &mut regu32, true) - .map_err(|e| ErrorKind::SetGicRegsError("gicd_statusr", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::SetGicRegsError( + "gicd_statusr", + e.to_string() + )) + })?; for gicv3_dist in state.irq_dist[0..state.dist_len].iter() { self.set_dist(*gicv3_dist) - .map_err(|e| ErrorKind::SetGicRegsError("dist", e.to_string()))? + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("dist", e.to_string())))? } for gicv3_iccr in state.vcpu_iccr[0..state.iccr_len].iter() { self.set_cpu(*gicv3_iccr) - .map_err(|e| ErrorKind::SetGicRegsError("cpu", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("cpu", e.to_string())))?; } Ok(()) @@ -676,53 +706,67 @@ pub struct GICv3ItsState { } impl StateTransfer for GICv3Its { - fn get_state_vec(&self) -> migration::errors::Result> { - use migration::errors::ErrorKind; + fn get_state_vec(&self) -> migration::Result> { + use migration::MigrationError; let mut state = GICv3ItsState::default(); for i in 0..8 { self.access_gic_its(GITS_BASER + 8 * i as u32, &mut state.baser[i], false) - .map_err(|e| ErrorKind::GetGicRegsError("Its baser", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::GetGicRegsError("Its baser", e.to_string())) + })?; } self.access_gic_its(GITS_CTLR, &mut state.ctlr, false) - .map_err(|e| ErrorKind::GetGicRegsError("Its ctlr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its ctlr", e.to_string())))?; self.access_gic_its(GITS_CBASER, &mut state.cbaser, false) - .map_err(|e| ErrorKind::GetGicRegsError("Its cbaser", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its cbaser", e.to_string())))?; self.access_gic_its(GITS_CREADR, &mut state.creadr, false) - .map_err(|e| ErrorKind::GetGicRegsError("Its creadr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its creadr", e.to_string())))?; self.access_gic_its(GITS_CWRITER, &mut state.cwriter, false) - .map_err(|e| ErrorKind::GetGicRegsError("Its cwriter", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::GetGicRegsError( + "Its cwriter", + e.to_string() + )) + })?; self.access_gic_its(GITS_IIDR, &mut state.iidr, false) - .map_err(|e| ErrorKind::GetGicRegsError("Its iidr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its iidr", e.to_string())))?; Ok(state.as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::errors::Result<()> { - use migration::errors::ErrorKind; + fn set_state(&self, state: &[u8]) -> migration::Result<()> { + use migration::MigrationError; let mut its_state = *GICv3ItsState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("GICv3Its"))?; + .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("GICv3Its")))?; self.access_gic_its(GITS_IIDR, &mut its_state.iidr, true) - .map_err(|e| ErrorKind::SetGicRegsError("Its iidr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its iidr", e.to_string())))?; // It must be written before GITS_CREADR, because GITS_CBASER write access will reset // GITS_CREADR. self.access_gic_its(GITS_CBASER, &mut its_state.cbaser, true) - .map_err(|e| ErrorKind::SetGicRegsError("Its cbaser", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its cbaser", e.to_string())))?; self.access_gic_its(GITS_CREADR, &mut its_state.creadr, true) - .map_err(|e| ErrorKind::SetGicRegsError("Its readr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its readr", e.to_string())))?; self.access_gic_its(GITS_CWRITER, &mut its_state.cwriter, true) - .map_err(|e| ErrorKind::SetGicRegsError("Its cwriter", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::SetGicRegsError( + "Its cwriter", + e.to_string() + )) + })?; for i in 0..8 { self.access_gic_its(GITS_BASER + 8 * i as u32, &mut its_state.baser[i], true) - .map_err(|e| ErrorKind::SetGicRegsError("Its baser", e.to_string()))?; + .map_err(|e| { + anyhow!(MigrationError::SetGicRegsError("Its baser", e.to_string())) + })?; } self.access_gic_its_tables(false) - .map_err(|e| ErrorKind::SetGicRegsError("Its table", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its table", e.to_string())))?; self.access_gic_its(GITS_CTLR, &mut its_state.ctlr, true) - .map_err(|e| ErrorKind::SetGicRegsError("Its ctlr", e.to_string()))?; + .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its ctlr", e.to_string())))?; Ok(()) } diff --git a/devices/src/interrupt_controller/error.rs b/devices/src/interrupt_controller/error.rs new file mode 100644 index 000000000..cb2a0bbfa --- /dev/null +++ b/devices/src/interrupt_controller/error.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum InterruptError { + #[cfg(target_arch = "aarch64")] + #[error("Invalid GIC config: {0}")] + InvalidConfig(String), + #[cfg(target_arch = "aarch64")] + #[error("Failed to create KVM device: {0:#?}.")] + CreateKvmDevice(kvm_ioctls::Error), +} diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index dbaa81b25..0aee43a30 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -27,6 +27,7 @@ #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] mod aarch64; +mod error; #[cfg(target_arch = "aarch64")] pub use aarch64::GICConfig as ICGICConfig; @@ -36,20 +37,5 @@ pub use aarch64::GICv2Config as ICGICv2Config; pub use aarch64::GICv3Config as ICGICv3Config; #[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; - -pub mod errors { - use error_chain::error_chain; - - error_chain! { - errors { - #[cfg(target_arch = "aarch64")] - InvalidConfig(err_info: String) { - display("Invalid GIC config: {}.", err_info) - } - #[cfg(target_arch = "aarch64")] - CreateKvmDevice(err: kvm_ioctls::Error) { - display("Failed to create KVM device: {:#?}.", err) - } - } - } -} +pub use anyhow::Result; +pub use error::InterruptError; diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index f8a8e685b..4e75eef71 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -17,7 +17,7 @@ use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use error_chain::bail; +use anyhow::{bail, Context, Result}; use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; use log::{error, info}; use machine_manager::machine::{PathInfo, PTY_PATH}; @@ -30,8 +30,6 @@ use util::set_termi_raw_mode; use util::unix::limit_permission; use vmm_sys_util::epoll::EventSet; -use super::errors::{Result, ResultExt}; - /// Provide the trait that helps handle the input data. pub trait InputReceiver: Send { fn input_handle(&mut self, buffer: &[u8]); @@ -76,13 +74,13 @@ impl Chardev { pub fn realize(&mut self) -> Result<()> { match &self.backend { ChardevType::Stdio => { - set_termi_raw_mode().chain_err(|| "Failed to set terminal to raw mode")?; + set_termi_raw_mode().with_context(|| "Failed to set terminal to raw mode")?; self.input = Some(Arc::new(Mutex::new(std::io::stdin()))); self.output = Some(Arc::new(Mutex::new(std::io::stdout()))); } ChardevType::Pty => { let (master, path) = - set_pty_raw_mode().chain_err(|| "Failed to set pty to raw mode")?; + set_pty_raw_mode().with_context(|| "Failed to set pty to raw mode")?; info!("Pty path is: {:?}", path); let path_info = PathInfo { path: format!("pty:{:?}", &path), @@ -106,11 +104,11 @@ impl Chardev { ); } let sock = UnixListener::bind(path.clone()) - .chain_err(|| format!("Failed to bind socket for chardev, path:{}", path))?; + .with_context(|| format!("Failed to bind socket for chardev, path:{}", path))?; self.listener = Some(sock); // add file to temporary pool, so it could be cleaned when vm exit. TempCleaner::add_path(path.clone()); - limit_permission(path).chain_err(|| { + limit_permission(path).with_context(|| { format!( "Failed to change file permission for chardev, path:{}", path @@ -167,7 +165,7 @@ fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { ) } let proc_path = PathBuf::from(format!("/proc/self/fd/{}", slave)); - let path = read_link(proc_path).chain_err(|| "Failed to read slave pty link")?; + let path = read_link(proc_path).with_context(|| "Failed to read slave pty link")?; // Safe because this only set the `old_termios` struct to zero. let mut old_termios: termios = unsafe { std::mem::zeroed() }; // Safe because this only get the current mode of slave pty and save it. diff --git a/devices/src/legacy/error.rs b/devices/src/legacy/error.rs new file mode 100644 index 000000000..1a1d4b728 --- /dev/null +++ b/devices/src/legacy/error.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LegacyError { + #[error("SysBus")] + SysBus { + #[from] + source: sysbus::error::SysBusError, + }, + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Failed to allocate system bus resource.")] + SetSysResErr, + #[error("Failed to add FwCfg entry, key is {0}")] + AddEntryErr(String), + #[error("Failed to find FwCfg entry, key is {0}.")] + EntryNotFound(String), + #[error("Duplicate FwCfg file-entry, name is {0}")] + DuplicateFile(String), + #[error("No available FwCfg file-slot for this file entry with filename {0}")] + FileSlotsNotAvailable(String), + #[error("Failed to read DMA request, dma_addr=0x{0:x} size=0x{1:x}")] + ReadDmaRequest(u64, u64), + #[error("Invalid FwCfg entry key {0}")] + InvalidFwCfgEntry(u16), + #[error("Flash size is 0x{0:x}, offset 0x{1:x} and size 0x{2:x} in write request overflows")] + PFlashWriteOverflow(u64, u64, u64), + #[error("Flash size is 0x{0:x}, offset 0x{1:x} and size 0x{2:x} in read request overflows")] + PFlashReadOverflow(u64, u64, u64), + #[error("Failed to seek to offset 0x{0:x} of PFlash file")] + PFlashFileSeekErr(u64), + #[error("Flash CFI table len is 0x{0:x}, request 0x{1:x} overflows")] + PFlashIndexOverflow(u64, usize), + #[error("Unsupported device configuration: device width {0}, bank width {1}")] + PFlashDevConfigErr(u32, u32), + #[error("Failed to write to Flash ROM")] + WritePFlashRomErr, + #[error("Failed to register event notifier.")] + RegNotifierErr, +} diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 83b3c6caa..daedeba27 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex}; +use crate::legacy::error::LegacyError; use acpi::{ AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, AmlString, }; @@ -20,18 +21,15 @@ use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlMemory32Fixed, AmlReadAndWrite}; use address_space::{AddressSpace, GuestAddress}; +use anyhow::{anyhow, bail, Context, Result}; #[cfg(target_arch = "x86_64")] use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; -use error_chain::{bail, ChainedError}; use log::{error, warn}; -use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; use util::offset_of; - -use crate::legacy::errors::{ErrorKind, Result, ResultExt}; - #[cfg(target_arch = "x86_64")] const FW_CFG_IO_BASE: u64 = 0x510; // Size of ioports including control/data registers and DMA. @@ -261,7 +259,7 @@ fn write_dma_memory( mut buf: &[u8], len: u64, ) -> Result<()> { - addr_space.write(&mut buf, addr, len).chain_err(|| { + addr_space.write(&mut buf, addr, len).with_context(|| { format!( "Failed to write dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", addr.0, len @@ -278,7 +276,7 @@ fn read_dma_memory( mut buf: &mut [u8], len: u64, ) -> Result<()> { - addr_space.read(&mut buf, addr, len).chain_err(|| { + addr_space.read(&mut buf, addr, len).with_context(|| { format!( "Failed to read dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", addr.0, len @@ -297,7 +295,7 @@ fn write_dma_result(addr_space: &Arc, addr: GuestAddress, value: u &dma_buf, dma_buf.len() as u64, ) - .chain_err(|| { + .with_context(|| { format!( "Failed to write dma result of fwcfg at gpa=0x{:x} value=0x{:x}", addr.0, value @@ -362,9 +360,9 @@ impl FwCfgCommon { fn get_entry_mut(&mut self) -> Result<&mut FwCfgEntry> { let key = self.cur_entry & FW_CFG_ENTRY_MASK; if key >= self.max_entry() || self.cur_entry == FW_CFG_INVALID { - return Err( - ErrorKind::EntryNotFound(get_key_name(self.cur_entry as usize).to_owned()).into(), - ); + return Err(anyhow!(LegacyError::EntryNotFound( + get_key_name(self.cur_entry as usize).to_owned() + ))); }; // unwrap is safe because the count of arch_entries and entries is initialized @@ -404,7 +402,7 @@ impl FwCfgCommon { let key = (key as u16) & FW_CFG_ENTRY_MASK; if key >= self.max_entry() || data.len() >= std::u32::MAX as usize { - return Err(ErrorKind::InvalidFwCfgEntry(key).into()); + return Err(anyhow!(LegacyError::InvalidFwCfgEntry(key))); } let entry = if self.is_arch_local() { @@ -433,7 +431,7 @@ impl FwCfgCommon { // Update a FwCfgEntry fn update_entry_data(&mut self, key: u16, mut data: Vec) -> Result<()> { if key >= self.max_entry() || data.len() >= std::u32::MAX as usize { - return Err(ErrorKind::InvalidFwCfgEntry(key).into()); + return Err(anyhow!(LegacyError::InvalidFwCfgEntry(key))); } let entry = if self.is_arch_local() { @@ -447,7 +445,9 @@ impl FwCfgCommon { e.data.append(&mut data); Ok(()) } else { - Err(ErrorKind::EntryNotFound(get_key_name(key as usize).to_owned()).into()) + Err(anyhow!(LegacyError::EntryNotFound( + get_key_name(key as usize).to_owned() + ))) } } @@ -460,7 +460,9 @@ impl FwCfgCommon { allow_write: bool, ) -> Result<()> { if self.files.len() >= self.file_slots as usize { - return Err(ErrorKind::FileSlotsNotAvailable(filename.to_owned()).into()); + return Err(anyhow!(LegacyError::FileSlotsNotAvailable( + filename.to_owned() + ))); } let file_name_bytes = filename.to_string().as_bytes().to_vec(); @@ -470,7 +472,7 @@ impl FwCfgCommon { .iter() .any(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) { - return Err(ErrorKind::DuplicateFile(filename.to_owned()).into()); + return Err(anyhow!(LegacyError::DuplicateFile(filename.to_owned()))); } let mut index = self.files.len(); @@ -515,7 +517,9 @@ impl FwCfgCommon { allow_write: bool, ) -> Result<()> { if self.files.len() >= self.file_slots as usize { - return Err(ErrorKind::FileSlotsNotAvailable(filename.to_owned()).into()); + return Err(anyhow!(LegacyError::FileSlotsNotAvailable( + filename.to_owned() + ))); } let file_name_bytes = filename.to_string().as_bytes().to_vec(); @@ -524,7 +528,7 @@ impl FwCfgCommon { .files .iter() .position(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) - .ok_or_else(|| ErrorKind::EntryNotFound(filename.to_owned()))?; + .ok_or_else(|| anyhow!(LegacyError::EntryNotFound(filename.to_owned())))?; self.files[index].size = data.len() as u32; // Update FileDir entry @@ -557,7 +561,7 @@ impl FwCfgCommon { let size = std::mem::size_of::() as u64; if let Err(_e) = read_dma_memory(&self.mem_space, dma_addr, dma_request, size) { write_dma_result(&self.mem_space, dma_addr, FW_CFG_DMA_CTL_ERROR)?; - return Err(ErrorKind::ReadDmaRequest(dma_addr.0, size).into()); + return Err(anyhow!(LegacyError::ReadDmaRequest(dma_addr.0, size))); } // Build `FwCfgDmaAccess` object from dma_request here @@ -715,7 +719,7 @@ impl FwCfgCommon { ((8 - addr - size as u64) * 8) as u32, (size * 8) as u32, ) - .ok_or_else(|| ErrorKind::Msg("Failed to extract bits from u64".to_string()).into()) + .ok_or_else(|| anyhow!("Failed to extract bits from u64")) } /// Read data register @@ -864,12 +868,12 @@ impl FwCfgMem { ) -> Result>> { self.fwcfg.common_realize()?; self.set_sys_resource(sysbus, region_base, region_size) - .chain_err(|| "Failed to allocate system resource for FwCfg.")?; + .with_context(|| "Failed to allocate system resource for FwCfg.")?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev, region_base, region_size) - .chain_err(|| "Failed to attach FwCfg device to system bus.")?; + .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } } @@ -886,8 +890,8 @@ fn read_bytes( Ok(val) => val, Err(e) => { error!( - "Failed to read from FwCfg data register, error is {}", - e.display_chain() + "{}", + format!("Failed to read from FwCfg data register, error is {:?}", e) ); return false; } @@ -910,7 +914,10 @@ fn read_bytes( { Ok(val) => val, Err(e) => { - error!("Failed to handle FwCfg DMA-read, error is {}", e); + error!( + "{}", + format!("Failed to handle FwCfg DMA-read, error is {:?}", e) + ); return false; } } @@ -988,7 +995,7 @@ impl SysBusDevOps for FwCfgMem { _sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> SysBusResult<()> { + ) -> sysbus::Result<()> { let mut res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; @@ -1000,7 +1007,7 @@ impl SysBusDevOps for FwCfgMem { SysBusDevType::FwCfg } - fn reset(&mut self) -> SysBusResult<()> { + fn reset(&mut self) -> sysbus::Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } @@ -1032,12 +1039,12 @@ impl FwCfgIO { let region_base = self.res.region_base; let region_size = self.res.region_size; self.set_sys_resource(sysbus, region_base, region_size) - .chain_err(|| "Failed to allocate system resource for FwCfg.")?; + .with_context(|| "Failed to allocate system resource for FwCfg.")?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev, region_base, region_size) - .chain_err(|| "Failed to attach FwCfg device to system bus.")?; + .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } } @@ -1047,10 +1054,7 @@ fn read_bytes(fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, off let value: u64 = match offset { 0..=1 => match fwcfg_arch.fwcfg.read_data_reg(offset, data.len() as u32) { Err(e) => { - error!( - "Failed to read from FwCfg data register, error is {}", - e.display_chain() - ); + error!("Failed to read from FwCfg data register, error is {:?}", e); return false; } Ok(val) => val, @@ -1065,7 +1069,10 @@ fn read_bytes(fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, off } match fwcfg_arch.fwcfg.dma_mem_read(offset - 4, data.len() as u32) { Err(e) => { - error!("Failed to handle FwCfg DMA-read, error is {}", e); + error!( + "{}", + format!("Failed to handle FwCfg DMA-read, error is {:?}", e) + ); return false; } Ok(val) => val, @@ -1133,8 +1140,8 @@ impl SysBusDevOps for FwCfgIO { }; if let Err(e) = self.fwcfg.dma_mem_write(offset - 4, value, size) { error!( - "Failed to handle FwCfg DMA-write, error is {}", - e.display_chain() + "{}", + format!("Failed to handle FwCfg DMA-write, error is {:?}", e) ); return false; } @@ -1160,7 +1167,7 @@ impl SysBusDevOps for FwCfgIO { _sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> SysBusResult<()> { + ) -> sysbus::Result<()> { let mut res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; @@ -1171,7 +1178,7 @@ impl SysBusDevOps for FwCfgIO { SysBusDevType::FwCfg } - fn reset(&mut self) -> SysBusResult<()> { + fn reset(&mut self) -> sysbus::Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index db20ea7b5..ee3efdb8d 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -25,65 +25,8 @@ //! - `x86_64` //! - `aarch64` -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - SysBus(sysbus::errors::Error, sysbus::errors::ErrorKind); - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - } - errors { - SetSysResErr { - display("Failed to allocate system bus resource.") - } - AddEntryErr(key: String) { - display("Failed to add FwCfg entry, key is {}", key) - } - EntryNotFound(key: String) { - display("Failed to find FwCfg entry, key is {}.", key) - } - DuplicateFile(key: String) { - display("Duplicate FwCfg file-entry, name is {}", key) - } - FileSlotsNotAvailable(key: String) { - display("No available FwCfg file-slot for this file entry with filename {}", key) - } - ReadDmaRequest(addr: u64, size: u64) { - display("Failed to read DMA request, dma_addr=0x{:x} size=0x{:x}", addr, size) - } - InvalidFwCfgEntry(key: u16) { - display("Invalid FwCfg entry key {}", key) - } - PFlashWriteOverflow(size:u64, offset: u64, data_len: u64) { - display("Flash size is 0x{:x}, offset 0x{:x} and size 0x{:x} in write request overflows", size, offset, data_len) - } - PFlashReadOverflow(size:u64, offset: u64, data_len: u64) { - display("Flash size is 0x{:x}, offset 0x{:x} and size 0x{:x} in read request overflows", size, offset, data_len) - } - PFlashFileSeekErr(offset: u64) { - display("Failed to seek to offset 0x{:x} of PFlash file", offset) - } - PFlashIndexOverflow(index: u64, len: usize) { - display("Flash CFI table len is 0x{:x}, request 0x{:x} overflows", len, index) - } - PFlashDevConfigErr(dev_width: u32, bank_width: u32) { - display("Unsupported device configuration: device width {}, bank width {}", dev_width, bank_width) - } - WritePFlashRomErr { - display("Failed to write to Flash ROM") - } - RegNotifierErr { - display("Failed to register event notifier.") - } - } - } -} - mod chardev; +pub mod error; #[allow(dead_code)] mod fwcfg; #[allow(dead_code)] @@ -100,10 +43,11 @@ mod ramfb; #[cfg(target_arch = "x86_64")] mod rtc; mod serial; - #[cfg(target_arch = "x86_64")] pub use self::rtc::{RTC, RTC_IRQ, RTC_PORT_INDEX}; +pub use anyhow::Result; pub use chardev::{Chardev, InputReceiver}; +pub use error::LegacyError; #[cfg(target_arch = "x86_64")] pub use fwcfg::FwCfgIO; #[cfg(target_arch = "aarch64")] diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 2e5f185df..647e84460 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -14,16 +14,14 @@ use std::fs::File; use std::io::Write; use std::sync::{Arc, Mutex}; +use super::error::LegacyError; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::{bail, ChainedError}; use log::{debug, error, warn}; -use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::num_ops::{deposit_u32, extract_u32}; - -use super::errors::{ErrorKind, Result, ResultExt}; - pub struct PFlash { /// Has backend file or not. has_backend: bool, @@ -206,7 +204,7 @@ impl PFlash { backend: Option, ) -> Result<()> { self.set_sys_resource(sysbus, region_base, region_size) - .chain_err(|| "Failed to allocate system resource for PFlash.")?; + .with_context(|| "Failed to allocate system resource for PFlash.")?; let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(region_base), @@ -226,7 +224,7 @@ impl PFlash { .sys_mem .root() .add_subregion(rom_region, region_base) - .chain_err(|| "Failed to attach PFlash to system bus")?; + .with_context(|| "Failed to attach PFlash to system bus")?; sysbus.devices.push(dev); Ok(()) @@ -237,7 +235,7 @@ impl PFlash { .as_ref() .unwrap() .set_rom_device_romd(true) - .chain_err(|| "Failed to set to read array mode.")?; + .with_context(|| "Failed to set to read array mode.")?; self.write_cycle = 0; self.cmd = 0x00; @@ -270,7 +268,7 @@ impl PFlash { let mut i: u32 = self.device_width; while i < self.bank_width { resp = deposit_u32(resp, 8 * i, 8 * self.device_width, resp) - .ok_or("Failed to deposit bits to u32")?; + .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; i += self.device_width; } } @@ -285,20 +283,24 @@ impl PFlash { - self.device_width.trailing_zeros()); if index >= self.cfi_table.len() as u64 { - return Err(ErrorKind::PFlashIndexOverflow(index, self.cfi_table.len()).into()); + return Err(anyhow!(LegacyError::PFlashIndexOverflow( + index, + self.cfi_table.len() + ))); } let mut resp: u32 = self.cfi_table[index as usize].into(); if self.device_width != self.max_device_width { if self.device_width != 1 || self.bank_width > 4 { - return Err( - ErrorKind::PFlashDevConfigErr(self.device_width, self.bank_width).into(), - ); + return Err(anyhow!(LegacyError::PFlashDevConfigErr( + self.device_width, + self.bank_width + ))); } // Repeat data for PFlash device which supports x16-mode but works in x8-mode. for i in 1..self.max_device_width { resp = deposit_u32(resp, 8 * i as u32, 8, self.cfi_table[index as usize] as u32) - .ok_or("Failed to deposit bits to u32")?; + .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; } } // Responses are repeated for every device in bank. @@ -306,7 +308,7 @@ impl PFlash { let mut i: u32 = self.device_width; while i < self.bank_width { resp = deposit_u32(resp, 8 * i, 8 * self.device_width, resp) - .ok_or("Failed to deposit bits to u32")?; + .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; i += self.device_width; } } @@ -321,12 +323,16 @@ impl PFlash { // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); if offset + size as u64 > mr.size() as u64 { - return Err( - ErrorKind::PFlashWriteOverflow(mr.size() as u64, offset, size as u64).into(), - ); + return Err(anyhow!(LegacyError::PFlashWriteOverflow( + mr.size() as u64, + offset, + size as u64 + ))); } - let addr: u64 = mr.get_host_address().ok_or("Failed to get host address.")?; + let addr: u64 = mr + .get_host_address() + .ok_or_else(|| anyhow!("Failed to get host address."))?; let ret = unsafe { // Safe as addr and size are valid. libc::msync( @@ -346,7 +352,11 @@ impl PFlash { // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); if offset + data.len() as u64 > mr.size() as u64 { - return Err(ErrorKind::PFlashReadOverflow(mr.size(), offset, data.len() as u64).into()); + return Err(anyhow!(LegacyError::PFlashReadOverflow( + mr.size(), + offset, + data.len() as u64 + ))); } let host_addr = mr.get_host_address().unwrap(); // Safe because host_addr of the region is local allocated and sanity has been checked. @@ -355,7 +365,7 @@ impl PFlash { }; data.as_mut() .write_all(src) - .chain_err(|| "Failed to read data from PFlash Rom")?; + .with_context(|| "Failed to read data from PFlash Rom")?; Ok(()) } @@ -364,9 +374,11 @@ impl PFlash { // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); if offset + data.len() as u64 > mr.size() as u64 { - return Err( - ErrorKind::PFlashWriteOverflow(mr.size(), offset, data.len() as u64).into(), - ); + return Err(anyhow!(LegacyError::PFlashWriteOverflow( + mr.size(), + offset, + data.len() as u64 + ))); } let host_addr = mr.get_host_address().unwrap(); // Safe because host_addr of the region is local allocated and sanity has been checked. @@ -374,7 +386,7 @@ impl PFlash { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len() as usize) }; dst.write_all(data) - .chain_err(|| "Failed to write data to PFlash Rom")?; + .with_context(|| "Failed to write data to PFlash Rom")?; Ok(()) } @@ -385,9 +397,10 @@ impl PFlash { 0x00 | 0xf0 | 0xff => { if let Err(e) = self.set_read_array_mode(false) { error!( - "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", cmd, - e.display_chain() + e) ); return false; } @@ -401,13 +414,13 @@ impl PFlash { if !self.read_only { let all_one = vec![0xff_u8; self.block_len as usize]; if let Err(e) = self.write_data(all_one.as_slice(), offset_mask) { - error!("Failed to write PFlash device: {}.", e.display_chain()); + error!("{}", format!("Failed to write PFlash device: {:?}.", e)); } if let Err(e) = self.update_content(offset_mask, self.block_len) { error!( - "Failed to update content for PFlash device: {}.", - e.display_chain() + "{}", + format!("Failed to update content for PFlash device: {:?}.", e) ); } } else { @@ -421,9 +434,10 @@ impl PFlash { self.status = 0x0; if let Err(e) = self.set_read_array_mode(false) { error!( - "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", cmd, - e.display_chain() + e) ); return false; } @@ -446,9 +460,10 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", cmd, - e.display_chain() + e) ); return false; } @@ -472,12 +487,12 @@ impl PFlash { 0x10 | 0x40 => { if !self.read_only { if let Err(e) = self.write_data(data, offset) { - error!("Failed to write to PFlash device: {}.", e.display_chain()); + error!("Failed to write to PFlash device: {:?}.", e); } if let Err(e) = self.update_content(offset, data_len.into()) { error!( - "Failed to update content for PFlash device: {}.", - e.display_chain() + "{}", + format!("Failed to update content for PFlash device: {:?}.", e) ); } } else { @@ -493,9 +508,10 @@ impl PFlash { } else if cmd == 0xff { if let Err(e) = self.set_read_array_mode(false) { error!( - "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", cmd, - e.display_chain() + e) ); return false; } @@ -503,9 +519,10 @@ impl PFlash { } else { if let Err(e) = self.set_read_array_mode(true) { error!( - "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -536,9 +553,10 @@ impl PFlash { } else if cmd == 0xff { if let Err(e) = self.set_read_array_mode(false) { error!( - "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -546,9 +564,10 @@ impl PFlash { } else { if let Err(e) = self.set_read_array_mode(true) { error!( - "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -559,9 +578,10 @@ impl PFlash { if cmd == 0xff { if let Err(e) = self.set_read_array_mode(false) { error!( - "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -571,9 +591,10 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -588,7 +609,7 @@ impl PFlash { 0xe8 => { if !self.read_only { if let Err(e) = self.write_data(data, offset) { - error!("Failed to write to PFlash device: {}.", e.display_chain()); + error!("{}", format!("Failed to write to PFlash device: {:?}.", e)); } } else { self.status |= 0x10; @@ -600,8 +621,8 @@ impl PFlash { if !self.read_only { if let Err(e) = self.update_content(offset & mask, self.write_blk_size) { error!( - "Failed to update content for PFlash device: {}.", - e.display_chain() + "{}", + format!("Failed to update content for PFlash device: {:?}.", e) ); } } else { @@ -614,9 +635,10 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "Failed to set read array mode, write cycle 2, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 2, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -635,9 +657,10 @@ impl PFlash { } else { if let Err(e) = self.set_read_array_mode(false) { error!( - "Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -647,9 +670,10 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {}", + "{}", + format!("Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {:?}", self.cmd, - e.display_chain() + e) ); return false; } @@ -719,7 +743,7 @@ impl SysBusDevOps for PFlash { while i < data_len { match self.query_devid(offset + (i * self.bank_width) as u64) { Err(e) => { - error!("Failed to query devid {}.", e.display_chain()); + error!("{}", format!("Failed to query devid {:?}.", e)); break; } Ok(fieldval) => { @@ -759,7 +783,7 @@ impl SysBusDevOps for PFlash { while i < data_len { match self.query_cfi(offset + (i * self.bank_width) as u64) { Err(e) => { - error!("Failed to query devid, {}.", e.display_chain()); + error!("{}", format!("Failed to query devid, {:?}.", e)); break; } Ok(fieldval) => { @@ -786,7 +810,7 @@ impl SysBusDevOps for PFlash { self.write_cycle = 0; self.cmd = 0x00; if let Err(e) = self.read_data(data, offset) { - error!("Failed to read data from PFlash: {}.", e.display_chain()); + error!("{}", format!("Failed to read data from PFlash: {:?}.", e)); } } } @@ -844,10 +868,7 @@ impl SysBusDevOps for PFlash { self.write_cycle ); if let Err(e) = self.set_read_array_mode(false) { - error!( - "Failed to set PFlash to read array mode, error is {}", - e.display_chain() - ); + error!("Failed to set PFlash to read array mode, error is {:?}", e); } false } @@ -863,7 +884,7 @@ impl SysBusDevOps for PFlash { _sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> SysBusResult<()> { + ) -> sysbus::Result<()> { let mut res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; @@ -875,10 +896,8 @@ impl SysBusDevOps for PFlash { SysBusDevType::Flash } - fn reset(&mut self) -> SysBusResult<()> { - use sysbus::errors::ResultExt as SysBusResultExt; - - SysBusResultExt::chain_err(self.rom.as_ref().unwrap().set_rom_device_romd(true), || { + fn reset(&mut self) -> sysbus::Result<()> { + sysbus::Result::with_context(self.rom.as_ref().unwrap().set_rom_device_romd(true), || { "Fail to set PFlash rom region read only" })?; self.cmd = 0x00; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index d366ec64e..406dce4f2 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -12,12 +12,15 @@ use std::sync::{Arc, Mutex}; +use super::chardev::{Chardev, InputReceiver}; +use super::error::LegacyError; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlMemory32Fixed, AmlNameDecl, AmlReadAndWrite, AmlResTemplate, AmlResourceUsage, AmlScopeBuilder, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::GuestAddress; +use anyhow::{anyhow, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error}; use machine_manager::{ @@ -33,10 +36,6 @@ use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use vmm_sys_util::eventfd::EventFd; - -use super::chardev::{Chardev, InputReceiver}; -use super::errors::{ErrorKind, Result, ResultExt}; - const PL011_FLAG_TXFE: u8 = 0x80; const PL011_FLAG_RXFF: u8 = 0x40; const PL011_FLAG_RXFE: u8 = 0x10; @@ -164,14 +163,14 @@ impl PL011 { .lock() .unwrap() .realize() - .chain_err(|| "Failed to realize chardev")?; + .with_context(|| "Failed to realize chardev")?; self.set_sys_resource(sysbus, region_base, region_size) - .chain_err(|| "Failed to set system resource for PL011.")?; + .with_context(|| "Failed to set system resource for PL011.")?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev, region_base, region_size) - .chain_err(|| "Failed to attach PL011 to system bus.")?; + .with_context(|| "Failed to attach PL011 to system bus.")?; bs.lock().unwrap().kernel_cmdline.push(Param { param_type: "earlycon".to_string(), @@ -188,7 +187,7 @@ impl PL011 { EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, ) - .chain_err(|| ErrorKind::RegNotifierErr)?; + .with_context(|| anyhow!(LegacyError::RegNotifierErr))?; Ok(()) } } @@ -400,13 +399,13 @@ impl SysBusDevOps for PL011 { } impl StateTransfer for PL011 { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *PL011State::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("PL011"))?; + .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("PL011")))?; Ok(()) } diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 8c22ad027..ee5300dc6 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -13,8 +13,10 @@ use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use super::error::LegacyError; use acpi::AmlBuilder; use address_space::GuestAddress; +use anyhow::{anyhow, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::error; use migration::{ @@ -26,8 +28,6 @@ use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; -use super::errors::{ErrorKind, Result, ResultExt}; - /// Registers for pl031 from ARM PrimeCell Real Time Clock Technical Reference Manual. /// Data Register. const RTC_DR: u64 = 0x00; @@ -104,7 +104,7 @@ impl PL031 { ) -> Result<()> { self.interrupt_evt = Some(EventFd::new(libc::EFD_NONBLOCK)?); self.set_sys_resource(sysbus, region_base, region_size) - .chain_err(|| ErrorKind::SetSysResErr)?; + .with_context(|| anyhow!(LegacyError::SetSysResErr))?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; @@ -215,15 +215,15 @@ impl AmlBuilder for PL031 { } impl StateTransfer for PL031 { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let state = self.state; Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *PL031State::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("PL031"))?; + .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("PL031")))?; Ok(()) } diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index cb3f57ba6..a44d160cc 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -11,15 +11,16 @@ // See the Mulan PSL v2 for more details. use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; -use crate::legacy::errors::{Result, ResultExt}; +use crate::legacy::Result; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; +use anyhow::Context; use drm_fourcc::DrmFourcc; use log::error; use migration_derive::ByteCode; use std::mem::size_of; use std::sync::{Arc, Mutex}; -use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; +use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use util::byte_code::ByteCode; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; use vnc::vnc::{vnc_display_switch, vnc_loop_update_display, DisplaySurface}; @@ -83,7 +84,7 @@ impl RamfbState { Some(Arc::new(Mutex::new(ramfb_state_cb))), true, ) - .chain_err(|| "Failed to set fwcfg")?; + .with_context(|| "Failed to set fwcfg")?; Ok(()) } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index c87d7036c..93caeaaad 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -17,12 +17,11 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; +use anyhow::Result; use log::{debug, error, warn}; -use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; -use super::errors::Result; - /// IO port of RTC device to select Register to read/write. pub const RTC_PORT_INDEX: u64 = 0x70; /// IO port of RTC device to read/write data from selected register. @@ -277,7 +276,7 @@ impl SysBusDevOps for RTC { SysBusDevType::Rtc } - fn reset(&mut self) -> SysBusResult<()> { + fn reset(&mut self) -> sysbus::Result<()> { self.cmos_data.fill(0); self.cmos_data[RTC_REG_D as usize] = 0x80; self.set_memory(self.mem_size, self.gap_start); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 96ab47b24..50bb207b4 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -19,25 +19,24 @@ use acpi::{ AmlResourceUsage, AmlScopeBuilder, }; use address_space::GuestAddress; -use error_chain::bail; use hypervisor::kvm::KVM_FDS; use log::error; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; use migration::{ - snapshot::SERIAL_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, - StateTransfer, + snapshot::SERIAL_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, + MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{errors::Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use vmm_sys_util::eventfd::EventFd; use super::chardev::{Chardev, InputReceiver}; -use super::errors::{ErrorKind, Result}; - +use super::error::LegacyError; +use anyhow::{anyhow, bail, Context, Result}; pub const SERIAL_ADDR: u64 = 0x3f8; const UART_IER_RDI: u8 = 0x01; @@ -140,16 +139,14 @@ impl Serial { region_size: u64, #[cfg(target_arch = "aarch64")] bs: &Arc>, ) -> Result<()> { - use super::errors::ResultExt; - self.chardev .lock() .unwrap() .realize() - .chain_err(|| "Failed to realize chardev")?; + .with_context(|| "Failed to realize chardev")?; self.interrupt_evt = Some(EventFd::new(libc::EFD_NONBLOCK)?); self.set_sys_resource(sysbus, region_base, region_size) - .chain_err(|| ErrorKind::SetSysResErr)?; + .with_context(|| anyhow!(LegacyError::SetSysResErr))?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; @@ -170,7 +167,7 @@ impl Serial { EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, ) - .chain_err(|| ErrorKind::RegNotifierErr)?; + .with_context(|| anyhow!(LegacyError::RegNotifierErr))?; Ok(()) } @@ -278,8 +275,6 @@ impl Serial { // * fail to write serial. // * fail to flush serial. fn write_internal(&mut self, offset: u64, data: u8) -> Result<()> { - use super::errors::ResultExt; - match offset { 0 => { if self.state.lcr & UART_LCR_DLAB != 0 { @@ -308,10 +303,10 @@ impl Serial { let mut locked_output = output.as_ref().unwrap().lock().unwrap(); locked_output .write_all(&[data]) - .chain_err(|| "serial: failed to write.")?; + .with_context(|| "serial: failed to write.")?; locked_output .flush() - .chain_err(|| "serial: failed to flush.")?; + .with_context(|| "serial: failed to flush.")?; } self.update_iir(); @@ -382,7 +377,7 @@ impl SysBusDevOps for Serial { self.interrupt_evt.as_ref() } - fn set_irq(&mut self, _sysbus: &mut SysBus) -> SysBusResult { + fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { let mut irq: i32 = -1; if let Some(e) = self.interrupt_evt() { irq = 4; @@ -429,7 +424,7 @@ impl AmlBuilder for Serial { } impl StateTransfer for Serial { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let mut state = self.state; let (rbr_state, _) = self.rbr.as_slices(); state.rbr_len = rbr_state.len(); @@ -438,9 +433,9 @@ impl StateTransfer for Serial { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { let serial_state = *SerialState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("SERIAL"))?; + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("SERIAL")))?; let mut rbr = VecDeque::::default(); for i in 0..serial_state.rbr_len { rbr.push_back(serial_state.rbr_value[i]); diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 849ac5394..2bc76ee36 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -21,6 +21,6 @@ pub mod legacy; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ - errors as IntCtrlErrs, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, + ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, InterruptError as IntCtrlErrs, }; -pub use legacy::errors as LegacyErrs; +pub use legacy::error::LegacyError as LegacyErrs; diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 4b90f3bd4..10f69f3cc 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -7,7 +7,8 @@ license = "Mulan PSL v2" [dependencies] arc-swap = ">=1.5.0" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" log = "0.4" diff --git a/hypervisor/src/error.rs b/hypervisor/src/error.rs new file mode 100644 index 000000000..291a053c7 --- /dev/null +++ b/hypervisor/src/error.rs @@ -0,0 +1,28 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Error, Debug)] +pub enum HypervisorError { + #[error("Util error")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("KvmIoctl")] + KvmIoctl { + #[from] + source: kvm_ioctls::Error, + }, +} diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index a1eae9be5..8925a054a 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -19,7 +19,7 @@ use util::bitmap::Bitmap; use vmm_sys_util::ioctl::ioctl_with_val; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; -use crate::errors::{Result, ResultExt}; +use anyhow::{Context, Result}; pub(crate) type IrqRoute = kvm_bindings::kvm_irq_routing; pub(crate) type IrqRouteEntry = kvm_bindings::kvm_irq_routing_entry; @@ -177,7 +177,7 @@ impl IrqRouteTable { pub fn update_msi_route(&mut self, gsi: u32, msi_vector: MsiVector) -> Result<()> { self.remove_irq_route(gsi); self.add_msi_route(gsi, msi_vector) - .chain_err(|| "Failed to add msi route")?; + .with_context(|| "Failed to add msi route")?; Ok(()) } @@ -187,7 +187,7 @@ impl IrqRouteTable { let free_gsi = self .gsi_bitmap .find_next_zero(0) - .chain_err(|| "Failed to get new free gsi")?; + .with_context(|| "Failed to get new free gsi")?; self.gsi_bitmap.set(free_gsi)?; Ok(free_gsi as u32) } @@ -200,7 +200,7 @@ impl IrqRouteTable { pub fn release_gsi(&mut self, gsi: u32) -> Result<()> { self.gsi_bitmap .clear(gsi as usize) - .chain_err(|| "Failed to release gsi")?; + .with_context(|| "Failed to release gsi")?; self.remove_irq_route(gsi); Ok(()) } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 60990feeb..ffd12c6fc 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -23,7 +23,7 @@ use vmm_sys_util::{ eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, }; -use crate::errors::{Result, ResultExt}; +use anyhow::{Context, Result}; pub use interrupt::MsiVector; use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; @@ -144,7 +144,7 @@ impl KVMFds { .as_ref() .unwrap() .set_gsi_routing(&*irq_routing) - .chain_err(|| "Failed to set gsi routing") + .with_context(|| "Failed to set gsi routing") } } @@ -153,7 +153,7 @@ impl KVMFds { .as_ref() .unwrap() .register_irqfd(fd, gsi) - .chain_err(|| format!("Failed to register irqfd: gsi {}.", gsi)) + .with_context(|| format!("Failed to register irqfd: gsi {}.", gsi)) } pub fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { @@ -161,7 +161,7 @@ impl KVMFds { .as_ref() .unwrap() .unregister_irqfd(fd, gsi) - .chain_err(|| format!("Failed to unregister irqfd: gsi {}.", gsi)) + .with_context(|| format!("Failed to unregister irqfd: gsi {}.", gsi)) } /// Start dirty page tracking in kvm. @@ -174,7 +174,7 @@ impl KVMFds { .as_ref() .unwrap() .set_user_memory_region(*region) - .chain_err(|| { + .with_context(|| { format!( "Failed to start dirty log, error is {}", std::io::Error::last_os_error() @@ -196,7 +196,7 @@ impl KVMFds { .as_ref() .unwrap() .set_user_memory_region(*region) - .chain_err(|| { + .with_context(|| { format!( "Failed to stop dirty log, error is {}", std::io::Error::last_os_error() @@ -215,7 +215,7 @@ impl KVMFds { .as_ref() .unwrap() .get_dirty_log(slot, mem_size as usize) - .chain_err(|| { + .with_context(|| { format!( "Failed to get dirty log, error is {}", std::io::Error::last_os_error() diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index b9acc6904..16edaa1ab 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -12,18 +12,7 @@ //! This crate offers interfaces for different kinds of hypervisors, such as KVM. -#[allow(clippy::upper_case_acronyms)] -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - } - foreign_links { - KVMIoctl(kvm_ioctls::Error); - } - } -} +pub mod error; +pub use error::HypervisorError; pub mod kvm; diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 1196ccc97..1625bbc1c 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -5,7 +5,7 @@ Warranty Disclaimer THE OPEN SOURCE SOFTWARE IN THIS SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. Copyright Notice and License Texts -Software: libc 0.2.94 +Software: libc 0.2.133 Copyright notice: Copyright (c) 2014-2020 The Rust Project Developers License: MIT or Apache License Version 2.0 @@ -282,23 +282,29 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to -Software: serde 1.0.143 +Software: serde 1.0.145 Copyright notice: Copyright (c) David Tolnay Copyright (c) Erick Tryzelaar License: MIT or Apache License Version 2.0 Please see above. -Software: serde_json 1.0.143 +Software: serde_json 1.0.145 Copyright notice: Copyright (c) David Tolnay Copyright (c) Erick Tryzelaar License: MIT or Apache License Version 2.0 Please see above. -Software: error-chain 0.12.4 +Software: anyhow 1.0 Copyright notice: -Copyright (c) 2017 The Error-Chain Project Developers +Copyright (c) David Tolnay +License: MIT or Apache License Version 2.0 +Please see above. + +Software: thiserror 1.0 +Copyright notice: +Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. @@ -371,7 +377,7 @@ Copyright (c) 2018 Tom Parker-Shemilt License: MIT Please see above. -Software: syn 1.0.99 +Software: syn 1.0.100 Copyright notice: Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 @@ -383,7 +389,7 @@ Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: proc-macro2 1.0.43 +Software: proc-macro2 1.0.44 Copyright notice: Copyright (c) David Tolnay Copyright (c) Alex Crichton @@ -408,7 +414,7 @@ Copyright (c) 2019 Intel Corporation. All Rights Reserved. License: Apache License Version 2.0 or BSD 3-Clause License Please see above. -Software: once_cell 1.13.0 +Software: once_cell 1.15.0 Copyright notice: Copyright (c) Aleksey Kladov License: MIT OR Apache-2.0 diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 46ea0bab4..4f6465d6b 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -7,7 +7,6 @@ license = "Mulan PSL v2" description = "Emulation machines" [dependencies] -error-chain = "0.12.4" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" log = "0.4" @@ -16,6 +15,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" vmm-sys-util = ">=0.10.0" vfio-bindings = "0.3" +thiserror = "1.0" +anyhow = "1.0" acpi = { path = "../acpi" } address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } diff --git a/machine/src/error.rs b/machine/src/error.rs new file mode 100644 index 000000000..271cde945 --- /dev/null +++ b/machine/src/error.rs @@ -0,0 +1,112 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MachineError { + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("IntCtrl")] + #[cfg(target_arch = "aarch64")] + IntCtrl { + #[from] + source: devices::IntCtrlErrs, + }, + #[error("Legacy")] + Legacy { + #[from] + source: devices::legacy::error::LegacyError, + }, + #[error("MicroVm")] + MicroVm { + #[from] + source: super::micro_vm::error::MicroVmError, + }, + #[error("StdVm")] + StdVm { + #[from] + source: super::standard_vm::error::StandardVmError, + }, + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Virtio")] + Virtio { + #[from] + source: virtio::error::VirtioError, + }, + #[error("MachineManager")] + MachineManager { + #[from] + source: machine_manager::config::error::ConfigError, + }, + #[error("Hypervisor")] + Hypervisor { + #[from] + source: hypervisor::error::HypervisorError, + }, + #[error("KvmIoctl")] + KvmIoctl { + #[from] + source: kvm_ioctls::Error, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Failed to add {0} device.")] + AddDevErr(String), + #[error("Failed to load kernel.")] + LoadKernErr, + #[error("Failed to create memory address space")] + CrtMemSpaceErr, + #[error("Failed to create I/O address space")] + CrtIoSpaceErr, + #[error("Failed to register region in memory space: base={0},size={1}")] + RegMemRegionErr(u64, u64), + #[error("Failed to init eventfd {0}.")] + InitEventFdErr(String), + #[error("Failed to realize virtio mmio.")] + RlzVirtioMmioErr, + #[error("Failed to create irq chip.")] + #[cfg(target_arch = "x86_64")] + CrtIrqchipErr, + #[error("Failed to set tss address.")] + #[cfg(target_arch = "x86_64")] + SetTssErr, + #[error("Failed to create PIT.")] + #[cfg(target_arch = "x86_64")] + CrtPitErr, + #[error("Failed to generate FDT.")] + #[cfg(target_arch = "aarch64")] + GenFdtErr, + #[error("Failed to write FDT: addr={0}, size={1}")] + #[cfg(target_arch = "aarch64")] + WrtFdtErr(u64, usize), + #[error("Failed to register event notifier.")] + RegNotifierErr, + #[error("Failed to run vcpu{0}.")] + StartVcpuErr(u8), + #[error("Failed to pause vcpu{0}.")] + PauseVcpuErr(u8), + #[error("Failed to resume vcpu{0}")] + ResumeVcpuErr(u8), + #[error("Failed to destroy vcpu{0}.")] + DestroyVcpuErr(u8), +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6a24b13fc..275ddda1e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,94 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - IntCtrl(devices::IntCtrlErrs::Error, devices::IntCtrlErrs::ErrorKind) #[cfg(target_arch = "aarch64")]; - Legacy(devices::LegacyErrs::Error, devices::LegacyErrs::ErrorKind); - MicroVm(super::micro_vm::errors::Error, super::micro_vm::errors::ErrorKind); - StdVm(super::standard_vm::errors::Error, super::standard_vm::errors::ErrorKind); - Util(util::errors::Error, util::errors::ErrorKind); - Virtio(virtio::errors::Error, virtio::errors::ErrorKind); - MachineManager(machine_manager::config::errors::Error, machine_manager::config::errors::ErrorKind); - Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind); - } - - foreign_links { - KvmIoctl(kvm_ioctls::Error); - Io(std::io::Error); - } - - errors { - AddDevErr(dev: String) { - display("Failed to add {} device.", dev) - } - LoadKernErr { - display("Failed to load kernel.") - } - CrtMemSpaceErr { - display("Failed to create memory address space") - } - CrtIoSpaceErr { - display("Failed to create I/O address space") - } - RegMemRegionErr(base: u64, size: u64) { - display("Failed to register region in memory space: base={},size={}", base, size) - } - InitEventFdErr(fd: String) { - display("Failed to init eventfd {}.", fd) - } - RlzVirtioMmioErr { - display("Failed to realize virtio mmio.") - } - #[cfg(target_arch = "x86_64")] - CrtIrqchipErr { - display("Failed to create irq chip.") - } - #[cfg(target_arch = "x86_64")] - SetTssErr { - display("Failed to set tss address.") - } - #[cfg(target_arch = "x86_64")] - CrtPitErr { - display("Failed to create PIT.") - } - #[cfg(target_arch = "aarch64")] - GenFdtErr { - display("Failed to generate FDT.") - } - #[cfg(target_arch = "aarch64")] - WrtFdtErr(addr: u64, size: usize) { - display("Failed to write FDT: addr={}, size={}", addr, size) - } - RegNotifierErr { - display("Failed to register event notifier.") - } - StartVcpuErr(id: u8) { - display("Failed to run vcpu{}.", id) - } - PauseVcpuErr(id: u8) { - display("Failed to pause vcpu{}.", id) - } - ResumeVcpuErr(id: u8) { - display("Failed to resume vcpu{}.", id) - } - DestroyVcpuErr(id: u8) { - display("Failed to destroy vcpu{}.", id) - } - } - } -} - mod micro_vm; mod standard_vm; #[cfg(target_arch = "x86_64")] mod vm_state; extern crate util; +pub mod error; +pub use crate::error::MachineError; use std::collections::BTreeMap; use std::fs::remove_file; use std::net::TcpListener; @@ -105,7 +25,6 @@ use std::os::unix::{io::AsRawFd, net::UnixListener}; use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; -use error_chain::bail; use kvm_ioctls::VcpuFd; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -116,11 +35,13 @@ use address_space::KvmIoListener; use address_space::{ create_host_mmaps, set_host_memory_policy, AddressSpace, KvmMemoryListener, Region, }; +pub use anyhow::Result; +use anyhow::{anyhow, bail, Context}; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; -use errors::{ErrorKind, Result, ResultExt}; + use hypervisor::kvm::KVM_FDS; #[cfg(not(target_env = "musl"))] use machine_manager::config::parse_gpu; @@ -138,7 +59,7 @@ use machine_manager::{ }; use migration::MigrationManager; use pci::{PciBus, PciDevOps, PciHost, RootPort}; -use standard_vm::errors::Result as StdResult; +use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; use usb::{ @@ -197,20 +118,20 @@ pub trait MachineOps { if migrate_info.0 != MigrateMode::File { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) - .chain_err(|| "Failed to mmap guest ram.")?; + .with_context(|| "Failed to mmap guest ram.")?; set_host_memory_policy(&mem_mappings, &mem_config.mem_zones) - .chain_err(|| "Failed to set host memory NUMA policy.")?; + .with_context(|| "Failed to set host memory NUMA policy.")?; } sys_mem .register_listener(Arc::new(Mutex::new(KvmMemoryListener::new( KVM_FDS.load().fd.as_ref().unwrap().get_nr_memslots() as u32, )))) - .chain_err(|| "Failed to register KVM listener for memory space.")?; + .with_context(|| "Failed to register KVM listener for memory space.")?; #[cfg(target_arch = "x86_64")] sys_io .register_listener(Arc::new(Mutex::new(KvmIoListener::default()))) - .chain_err(|| "Failed to register KVM listener for I/O address space.")?; + .with_context(|| "Failed to register KVM listener for I/O address space.")?; if migrate_info.0 != MigrateMode::File { for mmap in mem_mappings.iter() { @@ -219,7 +140,7 @@ pub trait MachineOps { sys_mem .root() .add_subregion(Region::init_ram_region(mmap.clone()), base) - .chain_err(|| ErrorKind::RegMemRegionErr(base, size))?; + .with_context(|| anyhow!(MachineError::RegMemRegionErr(base, size)))?; } } @@ -269,7 +190,7 @@ pub trait MachineOps { for cpu_index in 0..nr_cpus as usize { cpus[cpu_index as usize] .realize(boot_config, topology) - .chain_err(|| { + .with_context(|| { format!( "Failed to realize arch cpu register for CPU {}/KVM", cpu_index @@ -322,7 +243,7 @@ pub trait MachineOps { MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) - .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, + .with_context(|| anyhow!(MachineError::RlzVirtioMmioErr))?, &device_cfg.id, ); } else { @@ -339,7 +260,7 @@ pub trait MachineOps { ); virtio_pci_device .realize() - .chain_err(|| "Failed to add virtio pci vsock device")?; + .with_context(|| "Failed to add virtio pci vsock device")?; } MigrationManager::register_device_instance( VhostKern::VsockState::descriptor(), @@ -399,7 +320,7 @@ pub trait MachineOps { VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func); virtio_pci_device .realize() - .chain_err(|| "Failed to add virtio pci balloon device")?; + .with_context(|| "Failed to add virtio pci balloon device")?; } Ok(()) @@ -421,7 +342,7 @@ pub trait MachineOps { MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) - .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, + .with_context(|| anyhow!(MachineError::RlzVirtioMmioErr))?, &device_cfg.id, ); } else { @@ -445,7 +366,7 @@ pub trait MachineOps { ); virtio_pci_device .realize() - .chain_err(|| "Failed to add virtio pci console device")?; + .with_context(|| "Failed to add virtio pci console device")?; } } else { bail!("No virtio-serial-bus specified"); @@ -477,7 +398,7 @@ pub trait MachineOps { if cfg_args.contains("virtio-rng-device") { let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); self.realize_virtio_mmio_device(device) - .chain_err(|| "Failed to add virtio mmio rng device")?; + .with_context(|| "Failed to add virtio mmio rng device")?; } else { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -493,7 +414,7 @@ pub trait MachineOps { ); vitio_pci_device .realize() - .chain_err(|| "Failed to add pci rng device")?; + .with_context(|| "Failed to add pci rng device")?; } MigrationManager::register_device_instance(RngState::descriptor(), rng_dev, &device_cfg.id); Ok(()) @@ -517,7 +438,7 @@ pub trait MachineOps { if cfg_args.contains("vhost-user-fs-device") { let device = VirtioMmioDevice::new(&sys_mem, device); self.realize_virtio_mmio_device(device) - .chain_err(|| "Failed to add vhost user fs device")?; + .with_context(|| "Failed to add vhost user fs device")?; } else if cfg_args.contains("vhost-user-fs-pci") { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -528,7 +449,7 @@ pub trait MachineOps { vitio_pci_device.enable_need_irqfd(); vitio_pci_device .realize() - .chain_err(|| "Failed to add pci fs device")?; + .with_context(|| "Failed to add pci fs device")?; } else { bail!("error device type"); } @@ -552,7 +473,7 @@ pub trait MachineOps { dev.lock() .unwrap() .reset() - .chain_err(|| "Fail to reset sysbus device")?; + .with_context(|| "Fail to reset sysbus device")?; } if let Ok(pci_host) = self.get_pci_host() { @@ -560,7 +481,7 @@ pub trait MachineOps { .lock() .unwrap() .reset() - .chain_err(|| "Fail to reset pci host")?; + .with_context(|| "Fail to reset pci host")?; } Ok(()) @@ -600,7 +521,7 @@ pub trait MachineOps { .lock() .unwrap() .modify_file_entry("bootorder", fwcfg_boot_order_string.as_bytes().to_vec()) - .chain_err(|| "Fail to add bootorder entry for standard VM.")?; + .with_context(|| "Fail to add bootorder entry for standard VM.")?; Ok(()) } @@ -660,12 +581,12 @@ pub trait MachineOps { let device_cfg = parse_blk(vm_config, cfg_args)?; if let Some(bootindex) = device_cfg.boot_index { self.check_bootindex(bootindex) - .chain_err(|| "Fail to add virtio pci blk device for invalid bootindex")?; + .with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?; } let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) - .chain_err(|| "Failed to add virtio pci device")?; + .with_context(|| "Failed to add virtio pci device")?; if let Some(bootindex) = device_cfg.boot_index { if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); @@ -722,7 +643,7 @@ pub trait MachineOps { ))); let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, true) - .chain_err(|| { + .with_context(|| { format!( "Failed to add virtio pci device, device id: {}", &device_cfg.id @@ -753,7 +674,7 @@ pub trait MachineOps { path = sysfsdev.to_string(); } let device = VfioDevice::new(Path::new(&path), self.get_sys_mem()) - .chain_err(|| "Failed to create vfio device.")?; + .with_context(|| "Failed to create vfio device.")?; let vfio_pci = VfioPciDevice::new( device, devfn, @@ -762,7 +683,7 @@ pub trait MachineOps { multifunc, self.get_sys_mem().clone(), ); - VfioPciDevice::realize(vfio_pci).chain_err(|| "Failed to realize vfio-pci device.")?; + VfioPciDevice::realize(vfio_pci).with_context(|| "Failed to realize vfio-pci device.")?; Ok(()) } @@ -810,7 +731,7 @@ pub trait MachineOps { let pci_host = self.get_pci_host()?; let bus = pci_host.lock().unwrap().root_bus.clone(); if PciBus::find_bus_by_name(&bus, &device_cfg.id).is_some() { - bail!("ID {} already exists."); + bail!("ID {} already exists.", &device_cfg.id); } let rootport = RootPort::new( device_cfg.id, @@ -821,7 +742,7 @@ pub trait MachineOps { ); rootport .realize() - .chain_err(|| "Failed to add pci root port")?; + .with_context(|| "Failed to add pci root port")?; Ok(()) } @@ -849,7 +770,7 @@ pub trait MachineOps { let clone_pcidev = Arc::new(Mutex::new(pcidev.clone())); pcidev .realize() - .chain_err(|| "Failed to add virtio pci device")?; + .with_context(|| "Failed to add virtio pci device")?; Ok(clone_pcidev) } @@ -892,7 +813,7 @@ pub trait MachineOps { .lock() .unwrap() .reset(false) - .chain_err(|| "Failed to reset bus"), + .with_context(|| "Failed to reset bus"), None => bail!("Failed to found device"), } } @@ -1009,7 +930,7 @@ pub trait MachineOps { pcidev .realize() - .chain_err(|| "Failed to realize usb xhci device")?; + .with_context(|| "Failed to realize usb xhci device")?; Ok(()) } @@ -1023,14 +944,14 @@ pub trait MachineOps { let keyboard = UsbKeyboard::new(device_cfg.id); let kbd = keyboard .realize() - .chain_err(|| "Failed to realize usb keyboard device")?; + .with_context(|| "Failed to realize usb keyboard device")?; if let Some(bus_device) = self.get_bus_device() { let locked_dev = bus_device.lock().unwrap(); if let Some(ctrl) = locked_dev.get("usb.0") { let mut locked_ctrl = ctrl.lock().unwrap(); locked_ctrl .attach_device(&(kbd.clone() as Arc>)) - .chain_err(|| "Failed to attach keyboard device")?; + .with_context(|| "Failed to attach keyboard device")?; } else { bail!("No usb controller found"); } @@ -1052,14 +973,14 @@ pub trait MachineOps { let tablet = UsbTablet::new(device_cfg.id); let tbt = tablet .realize() - .chain_err(|| "Failed to realize usb tablet device")?; + .with_context(|| "Failed to realize usb tablet device")?; if let Some(bus_device) = self.get_bus_device() { let locked_dev = bus_device.lock().unwrap(); if let Some(ctrl) = locked_dev.get("usb.0") { let mut locked_ctrl = ctrl.lock().unwrap(); locked_ctrl .attach_device(&(tbt.clone() as Arc>)) - .chain_err(|| "Failed to attach tablet device")?; + .with_context(|| "Failed to attach tablet device")?; } else { bail!("No usb controller found"); } @@ -1081,17 +1002,17 @@ pub trait MachineOps { #[cfg(target_arch = "x86_64")] vm_config.machine_config.mem_config.mem_size, ) - .chain_err(|| ErrorKind::AddDevErr("RTC".to_string()))?; + .with_context(|| anyhow!(MachineError::AddDevErr("RTC".to_string())))?; let cloned_vm_config = vm_config.clone(); if let Some(serial) = cloned_vm_config.serial.as_ref() { self.add_serial_device(serial) - .chain_err(|| ErrorKind::AddDevErr("serial".to_string()))?; + .with_context(|| anyhow!(MachineError::AddDevErr("serial".to_string())))?; } if let Some(pflashs) = cloned_vm_config.pflashs.as_ref() { self.add_pflash_device(pflashs) - .chain_err(|| ErrorKind::AddDevErr("pflash".to_string()))?; + .with_context(|| anyhow!(MachineError::AddDevErr("pflash".to_string())))?; } for dev in &cloned_vm_config.devices { @@ -1099,7 +1020,7 @@ pub trait MachineOps { // Check whether the device id exists to ensure device uniqueness. let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) - .chain_err(|| format!("Failed to check device id: config {}", cfg_args))?; + .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; match dev.0.as_str() { "virtio-blk-device" => { self.add_virtio_mmio_block(vm_config, cfg_args)?; @@ -1190,7 +1111,7 @@ pub trait MachineOps { } seccomp_filter .realize() - .chain_err(|| "Failed to init seccomp filter.")?; + .with_context(|| "Failed to init seccomp filter.")?; Ok(()) } @@ -1216,7 +1137,8 @@ pub trait MachineOps { ); trace_eventnotifier(¬ifier); - EventLoop::update_event(vec![notifier], None).chain_err(|| ErrorKind::RegNotifierErr)?; + EventLoop::update_event(vec![notifier], None) + .with_context(|| anyhow!(MachineError::RegNotifierErr))?; Ok(()) } @@ -1254,7 +1176,7 @@ pub trait MachineOps { let cpu_thread_barrier = cpus_thread_barrier.clone(); let cpu = cpus[cpu_index as usize].clone(); CPU::start(cpu, cpu_thread_barrier, paused) - .chain_err(|| format!("Failed to run vcpu{}", cpu_index))?; + .with_context(|| format!("Failed to run vcpu{}", cpu_index))?; } if paused { @@ -1283,7 +1205,7 @@ pub trait MachineOps { { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.pause() - .chain_err(|| format!("Failed to pause vcpu{}", cpu_index))?; + .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; } #[cfg(target_arch = "aarch64")] @@ -1306,7 +1228,7 @@ pub trait MachineOps { { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.resume() - .chain_err(|| format!("Failed to resume vcpu{}", cpu_index))?; + .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; } *vm_state = KvmVmState::Running; @@ -1326,7 +1248,7 @@ pub trait MachineOps { { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.destroy() - .chain_err(|| format!("Failed to destroy vcpu{}", cpu_index))?; + .with_context(|| format!("Failed to destroy vcpu{}", cpu_index))?; } *vm_state = KvmVmState::Shutdown; @@ -1360,19 +1282,19 @@ pub trait MachineOps { match (old_state, new_state) { (Created, Running) => ::vm_start(false, cpus, vm_state) - .chain_err(|| "Failed to start vm.")?, + .with_context(|| "Failed to start vm.")?, (Running, Paused) => ::vm_pause( cpus, #[cfg(target_arch = "aarch64")] irq_chip, vm_state, ) - .chain_err(|| "Failed to pause vm.")?, + .with_context(|| "Failed to pause vm.")?, (Paused, Running) => ::vm_resume(cpus, vm_state) - .chain_err(|| "Failed to resume vm.")?, + .with_context(|| "Failed to resume vm.")?, (_, Shutdown) => { ::vm_destroy(cpus, vm_state) - .chain_err(|| "Failed to destroy vm.")?; + .with_context(|| "Failed to destroy vm.")?; } (_, _) => { bail!("Vm lifecycle error: this transform is illegal."); @@ -1406,9 +1328,9 @@ pub fn vm_run( vm.lock() .unwrap() .run(cmd_args.is_present("freeze_cpu")) - .chain_err(|| "Failed to start VM.")?; + .with_context(|| "Failed to start VM.")?; } else { - start_incoming_migration(vm).chain_err(|| "Failed to start migration.")?; + start_incoming_migration(vm).with_context(|| "Failed to start migration.")?; } Ok(()) @@ -1419,11 +1341,12 @@ fn start_incoming_migration(vm: &Arc>) -> Re let (mode, path) = vm.lock().unwrap().get_migrate_info(); match mode { MigrateMode::File => { - MigrationManager::restore_snapshot(&path).chain_err(|| "Failed to restore snapshot")?; + MigrationManager::restore_snapshot(&path) + .with_context(|| "Failed to restore snapshot")?; vm.lock() .unwrap() .run(false) - .chain_err(|| "Failed to start VM.")?; + .with_context(|| "Failed to start VM.")?; } MigrateMode::Unix => { let listener = UnixListener::bind(&path)?; @@ -1431,26 +1354,26 @@ fn start_incoming_migration(vm: &Arc>) -> Re remove_file(&path)?; MigrationManager::recv_migration(&mut sock) - .chain_err(|| "Failed to receive migration with unix mode")?; + .with_context(|| "Failed to receive migration with unix mode")?; vm.lock() .unwrap() .run(false) - .chain_err(|| "Failed to start VM.")?; + .with_context(|| "Failed to start VM.")?; MigrationManager::finish_migration(&mut sock) - .chain_err(|| "Failed to finish migraton.")?; + .with_context(|| "Failed to finish migraton.")?; } MigrateMode::Tcp => { let listener = TcpListener::bind(&path)?; let mut sock = listener.accept().map(|(stream, _)| stream)?; MigrationManager::recv_migration(&mut sock) - .chain_err(|| "Failed to receive migration with tcp mode")?; + .with_context(|| "Failed to receive migration with tcp mode")?; vm.lock() .unwrap() .run(false) - .chain_err(|| "Failed to start VM.")?; + .with_context(|| "Failed to start VM.")?; MigrationManager::finish_migration(&mut sock) - .chain_err(|| "Failed to finish migraton.")?; + .with_context(|| "Failed to finish migraton.")?; } MigrateMode::Unknown => { bail!("Unknown migration mode"); diff --git a/machine/src/micro_vm/error.rs b/machine/src/micro_vm/error.rs new file mode 100644 index 000000000..150f3e240 --- /dev/null +++ b/machine/src/micro_vm/error.rs @@ -0,0 +1,48 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MicroVmError { + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Util")] + Virtio { + #[from] + source: virtio::error::VirtioError, + }, + #[error("Util")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Util")] + Kvm { + #[from] + source: kvm_ioctls::Error, + }, + #[error("Util")] + Nul { + #[from] + source: std::ffi::NulError, + }, + #[error("A maximum of {0} {1} replaceable devices are supported.")] + RplDevLmtErr(String, usize), + #[error("{0}: failed to update config.")] + UpdCfgErr(String), + #[error("Failed to realize virtio mmio.")] + RlzVirtioMmioErr, +} diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 56497e5ea..3a6a86033 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -28,37 +28,15 @@ //! - `x86_64` //! - `aarch64` -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - Virtio(virtio::errors::Error, virtio::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - Kvm(kvm_ioctls::Error); - Nul(std::ffi::NulError); - } - errors { - RplDevLmtErr(dev: String, nr: usize) { - display("A maximum of {} {} replaceable devices are supported.", nr, dev) - } - UpdCfgErr(id: String) { - display("{}: failed to update config.", id) - } - RlzVirtioMmioErr { - display("Failed to realize virtio mmio.") - } - } - } -} +pub mod error; +pub use error::MicroVmError; mod mem_layout; mod syscall; extern crate util; +use super::Result as MachineResult; +use log::error; use std::fmt; use std::fmt::Debug; use std::fs::metadata; @@ -68,9 +46,6 @@ use std::os::unix::io::RawFd; use std::path::Path; use std::sync::{Arc, Condvar, Mutex}; use std::vec::Vec; - -use error_chain::{bail, ChainedError}; -use log::error; use vmm_sys_util::eventfd::EventFd; use address_space::{AddressSpace, GuestAddress, Region}; @@ -112,13 +87,10 @@ use virtio::{ VirtioMmioDevice, VirtioMmioState, VirtioNetState, }; -use self::errors::{ErrorKind, Result}; -use super::{ - errors::{ErrorKind as MachineErrorKind, Result as MachineResult}, - MachineOps, -}; +use super::{error::MachineError, MachineOps}; #[cfg(target_arch = "x86_64")] use crate::vm_state; +use anyhow::{anyhow, bail, Context, Result}; // The replaceable block device maximum count. const MMIO_REPLACEABLE_BLK_NR: usize = 4; @@ -213,13 +185,11 @@ impl LightMachine { /// /// * `vm_config` - Represents the configuration for VM. pub fn new(vm_config: &VmConfig) -> MachineResult { - use crate::errors::ResultExt; - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .chain_err(|| MachineErrorKind::CrtMemSpaceErr)?; + .with_context(|| anyhow!(MachineError::CrtMemSpaceErr))?; #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) - .chain_err(|| MachineErrorKind::CrtIoSpaceErr)?; + .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; #[cfg(target_arch = "x86_64")] let free_irqs: (i32, i32) = (5, 15); #[cfg(target_arch = "aarch64")] @@ -239,7 +209,7 @@ impl LightMachine { // Machine state init let vm_state = Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())); let power_button = EventFd::new(libc::EFD_NONBLOCK) - .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?; + .with_context(|| anyhow!(MachineError::InitEventFdErr("power_button".to_string())))?; Ok(LightMachine { cpu_topo: CpuTopology::new( @@ -268,13 +238,11 @@ impl LightMachine { #[cfg(target_arch = "x86_64")] fn arch_init() -> MachineResult<()> { - use crate::errors::ResultExt; - let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); vm_fd .set_tss_address(0xfffb_d000_usize) - .chain_err(|| MachineErrorKind::SetTssErr)?; + .with_context(|| anyhow!(MachineError::SetTssErr))?; let pit_config = kvm_pit_config { flags: KVM_PIT_SPEAKER_DUMMY, @@ -282,14 +250,12 @@ impl LightMachine { }; vm_fd .create_pit2(pit_config) - .chain_err(|| MachineErrorKind::CrtPitErr)?; + .with_context(|| anyhow!(MachineError::CrtPitErr))?; Ok(()) } fn create_replaceable_devices(&mut self) -> Result<()> { - use errors::ResultExt; - let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { let block = Arc::new(Mutex::new(Block::default())); @@ -337,7 +303,7 @@ impl LightMachine { #[cfg(target_arch = "x86_64")] &self.boot_source, ) - .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, + .with_context(|| anyhow!(MicroVmError::RlzVirtioMmioErr))?, &id.to_string(), ); region_base += region_size; @@ -352,8 +318,6 @@ impl LightMachine { dev_config: Arc, index: usize, ) -> Result<()> { - use errors::ResultExt; - let mut replaceable_devices = self.replaceable_info.devices.lock().unwrap(); if let Some(device_info) = replaceable_devices.get_mut(index) { if device_info.used { @@ -367,7 +331,7 @@ impl LightMachine { .lock() .unwrap() .update_config(Some(dev_config.clone())) - .chain_err(|| ErrorKind::UpdCfgErr(id.to_string()))?; + .with_context(|| anyhow!(MicroVmError::UpdCfgErr(id.to_string())))?; } self.add_replaceable_config(id, dev_config)?; @@ -378,7 +342,7 @@ impl LightMachine { let mut configs_lock = self.replaceable_info.configs.lock().unwrap(); let limit = MMIO_REPLACEABLE_BLK_NR + MMIO_REPLACEABLE_NET_NR; if configs_lock.len() >= limit { - return Err(ErrorKind::RplDevLmtErr("".to_string(), limit).into()); + return Err(anyhow!(MicroVmError::RplDevLmtErr("".to_string(), limit))); } for config in configs_lock.iter() { @@ -398,20 +362,20 @@ impl LightMachine { } fn add_replaceable_device(&self, id: &str, driver: &str, slot: usize) -> Result<()> { - use errors::ResultExt; - let index = if driver.contains("net") { if slot >= MMIO_REPLACEABLE_NET_NR { - return Err( - ErrorKind::RplDevLmtErr("net".to_string(), MMIO_REPLACEABLE_NET_NR).into(), - ); + return Err(anyhow!(MicroVmError::RplDevLmtErr( + "net".to_string(), + MMIO_REPLACEABLE_NET_NR + ))); } slot + MMIO_REPLACEABLE_BLK_NR } else if driver.contains("blk") { if slot >= MMIO_REPLACEABLE_BLK_NR { - return Err( - ErrorKind::RplDevLmtErr("block".to_string(), MMIO_REPLACEABLE_BLK_NR).into(), - ); + return Err(anyhow!(MicroVmError::RplDevLmtErr( + "block".to_string(), + MMIO_REPLACEABLE_BLK_NR + ))); } slot } else { @@ -444,14 +408,12 @@ impl LightMachine { .lock() .unwrap() .update_config(dev_config) - .chain_err(|| ErrorKind::UpdCfgErr(id.to_string()))?; + .with_context(|| anyhow!(MicroVmError::UpdCfgErr(id.to_string())))?; } Ok(()) } fn del_replaceable_device(&self, id: &str) -> Result { - use errors::ResultExt; - // find the index of configuration by name and remove it let mut is_exist = false; let mut configs_lock = self.replaceable_info.configs.lock().unwrap(); @@ -474,7 +436,7 @@ impl LightMachine { .lock() .unwrap() .update_config(None) - .chain_err(|| ErrorKind::UpdCfgErr(id.to_string()))?; + .with_context(|| anyhow!(MicroVmError::UpdCfgErr(id.to_string())))?; } } @@ -510,15 +472,13 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "x86_64")] fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> MachineResult<()> { - use crate::errors::ResultExt; - KVM_FDS .load() .vm_fd .as_ref() .unwrap() .create_irq_chip() - .chain_err(|| MachineErrorKind::CrtIrqchipErr)?; + .with_context(|| anyhow!(MachineError::CrtIrqchipErr))?; Ok(()) } @@ -559,8 +519,6 @@ impl MachineOps for LightMachine { &self, fwcfg: Option<&Arc>>, ) -> MachineResult { - use crate::errors::ResultExt; - let boot_source = self.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); @@ -579,7 +537,7 @@ impl MachineOps for LightMachine { prot64_mode: true, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .chain_err(|| MachineErrorKind::LoadKernErr)?; + .with_context(|| anyhow!(MachineError::LoadKernErr))?; Ok(CPUBootConfig { prot64_mode: true, @@ -602,8 +560,6 @@ impl MachineOps for LightMachine { &self, fwcfg: Option<&Arc>>, ) -> MachineResult { - use crate::errors::ResultExt; - let mut boot_source = self.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); @@ -613,7 +569,7 @@ impl MachineOps for LightMachine { mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .chain_err(|| MachineErrorKind::LoadKernErr)?; + .with_context(|| anyhow!(MachineError::LoadKernErr))?; if let Some(rd) = &mut boot_source.initrd { rd.initrd_addr = layout.initrd_start; rd.initrd_size = layout.initrd_size; @@ -629,8 +585,6 @@ impl MachineOps for LightMachine { &mut self, dev: VirtioMmioDevice, ) -> MachineResult>> { - use errors::ResultExt; - let region_base = self.sysbus.min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; let realized_virtio_mmio_device = VirtioMmioDevice::realize( @@ -641,7 +595,7 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "x86_64")] &self.boot_source, ) - .chain_err(|| ErrorKind::RlzVirtioMmioErr)?; + .with_context(|| anyhow!(MicroVmError::RlzVirtioMmioErr))?; self.sysbus.min_free_base += region_size; Ok(realized_virtio_mmio_device) } @@ -668,15 +622,13 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { - use crate::errors::ResultExt; - PL031::realize( PL031::default(), &mut self.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, ) - .chain_err(|| "Failed to realize pl031.")?; + .with_context(|| "Failed to realize pl031.")?; Ok(()) } @@ -686,8 +638,6 @@ impl MachineOps for LightMachine { } fn add_serial_device(&mut self, config: &SerialConfig) -> MachineResult<()> { - use crate::errors::ResultExt; - #[cfg(target_arch = "x86_64")] let region_base: u64 = SERIAL_ADDR; #[cfg(target_arch = "aarch64")] @@ -706,7 +656,7 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "aarch64")] &self.boot_source, ) - .chain_err(|| "Failed to realize serial device.")?; + .with_context(|| "Failed to realize serial device.")?; Ok(()) } @@ -757,8 +707,6 @@ impl MachineOps for LightMachine { } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { - use crate::errors::ResultExt; - let mut locked_vm = vm.lock().unwrap(); //trace for lightmachine @@ -795,7 +743,7 @@ impl MachineOps for LightMachine { // Add mmio devices locked_vm .create_replaceable_devices() - .chain_err(|| "Failed to create replaceable devices.")?; + .with_context(|| "Failed to create replaceable devices.")?; locked_vm.add_devices(vm_config)?; trace_replaceable_info(&locked_vm.replaceable_info); @@ -824,7 +772,7 @@ impl MachineOps for LightMachine { let mut fdt_helper = FdtBuilder::new(); locked_vm .generate_fdt_node(&mut fdt_helper) - .chain_err(|| MachineErrorKind::GenFdtErr)?; + .with_context(|| anyhow!(MachineError::GenFdtErr))?; let fdt_vec = fdt_helper.finish()?; locked_vm .sys_mem @@ -833,11 +781,13 @@ impl MachineOps for LightMachine { GuestAddress(boot_cfg.fdt_addr as u64), fdt_vec.len() as u64, ) - .chain_err(|| MachineErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; + .with_context(|| { + anyhow!(MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len())) + })?; } locked_vm .register_power_event(&locked_vm.power_button) - .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?; + .with_context(|| anyhow!(MachineError::InitEventFdErr("power_button".to_string())))?; MigrationManager::register_vm_instance(vm.clone()); #[cfg(target_arch = "x86_64")] @@ -1083,7 +1033,7 @@ impl DeviceInterface for LightMachine { match self.add_replaceable_device(&args.id, &args.driver, slot) { Ok(()) => Response::create_empty_response(), Err(ref e) => { - error!("{}", e.display_chain()); + error!("{:?}", e); error!("Failed to add device: id {}, type {}", args.id, args.driver); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), @@ -1105,7 +1055,7 @@ impl DeviceInterface for LightMachine { Response::create_empty_response() } Err(ref e) => { - error!("Failed to delete device: {}", e.display_chain()); + error!("Failed to delete device: {:?}", e); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -1185,7 +1135,7 @@ impl DeviceInterface for LightMachine { match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), Err(ref e) => { - error!("{}", e.display_chain()); + error!("{:?}", e); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -1259,7 +1209,7 @@ impl DeviceInterface for LightMachine { match self.add_replaceable_config(&args.id, Arc::new(config)) { Ok(()) => Response::create_empty_response(), Err(ref e) => { - error!("{}", e.display_chain()); + error!("{:?}", e); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -1338,10 +1288,8 @@ impl EventLoopManager for LightMachine { *vmstate == KvmVmState::Shutdown } - fn loop_cleanup(&self) -> util::errors::Result<()> { - use util::errors::ResultExt; - - set_termi_canon_mode().chain_err(|| "Failed to set terminal to canonical mode")?; + fn loop_cleanup(&self) -> util::Result<()> { + set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; Ok(()) } } @@ -1353,7 +1301,7 @@ impl EventLoopManager for LightMachine { // * `dev_info` - Device resource info of serial device. // * `fdt` - Flatted device-tree blob where serial node will be filled into. #[cfg(target_arch = "aarch64")] -fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("uart@{:x}", res.region_base); let serial_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "ns16550a")?; @@ -1380,7 +1328,7 @@ fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::erro // * `dev_info` - Device resource info of RTC device. // * `fdt` - Flatted device-tree blob where RTC node will be filled into. #[cfg(target_arch = "aarch64")] -fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("pl031@{:x}", res.region_base); let rtc_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "arm,pl031\0arm,primecell\0")?; @@ -1407,7 +1355,7 @@ fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors: // * `dev_info` - Device resource info of Virtio-Mmio device. // * `fdt` - Flatted device-tree blob where node will be filled into. #[cfg(target_arch = "aarch64")] -fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("virtio_mmio@{:x}", res.region_base); let virtio_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "virtio,mmio")?; @@ -1430,18 +1378,18 @@ fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::err #[cfg(target_arch = "aarch64")] trait CompileFDTHelper { /// Function that helps to generate cpu nodes. - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate memory nodes. - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate Virtio-mmio devices' nodes. - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate the chosen node. - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; } #[cfg(target_arch = "aarch64")] impl CompileFDTHelper for LightMachine { - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "cpus"; let cpus_node_dep = fdt.begin_node(node)?; @@ -1508,7 +1456,7 @@ impl CompileFDTHelper for LightMachine { Ok(()) } - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let mem_size = self.sys_mem.memory_end_address().raw_value() - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; @@ -1521,7 +1469,7 @@ impl CompileFDTHelper for LightMachine { Ok(()) } - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { // timer let mut cells: Vec = Vec::new(); for &irq in [13, 14, 11, 10].iter() { @@ -1567,7 +1515,7 @@ impl CompileFDTHelper for LightMachine { Ok(()) } - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "chosen"; let boot_source = self.boot_source.lock().unwrap(); @@ -1590,7 +1538,7 @@ impl CompileFDTHelper for LightMachine { #[cfg(target_arch = "aarch64")] impl device_tree::CompileFDT for LightMachine { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node_dep = fdt.begin_node("")?; fdt.set_property_string("compatible", "linux,dummy-virt")?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 64078cdf5..99862e2dd 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -13,13 +13,12 @@ mod pci_host_root; mod syscall; +pub use crate::error::MachineError; use std::collections::HashMap; use std::fs::OpenOptions; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; - -use error_chain::bail; use vmm_sys_util::eventfd::EventFd; use acpi::{ @@ -39,7 +38,7 @@ use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; #[cfg(not(target_env = "musl"))] use devices::legacy::Ramfb; use devices::legacy::{ - errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgMem, FwCfgOps, PFlash, PL011, PL031, + FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; use devices::{ICGICConfig, ICGICv3Config, InterruptController}; @@ -68,9 +67,9 @@ use util::set_termi_canon_mode; #[cfg(not(target_env = "musl"))] use vnc::vnc; -use super::{errors::Result as StdResult, AcpiBuilder, StdMachineOps}; -use crate::errors::{ErrorKind, Result}; +use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; use crate::MachineOps; +use anyhow::{anyhow, bail, Context, Result}; /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { @@ -151,8 +150,6 @@ pub struct StdMachine { impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { - use crate::errors::ResultExt; - let cpu_topo = CpuTopology::new( vm_config.machine_config.nr_cpus, vm_config.machine_config.nr_sockets, @@ -163,7 +160,7 @@ impl StdMachine { vm_config.machine_config.max_cpus, ); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .chain_err(|| ErrorKind::CrtIoSpaceErr)?; + .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; let sysbus = SysBus::new( &sys_mem, (32, 192), @@ -187,11 +184,12 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), - power_button: EventFd::new(libc::EFD_NONBLOCK) - .chain_err(|| ErrorKind::InitEventFdErr("power_button".to_string()))?, + power_button: EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + anyhow!(MachineError::InitEventFdErr("power_button".to_string())) + })?, vm_config: Mutex::new(vm_config.clone()), reset_req: EventFd::new(libc::EFD_NONBLOCK) - .chain_err(|| ErrorKind::InitEventFdErr("reset_req".to_string()))?, + .with_context(|| anyhow!(MachineError::InitEventFdErr("reset_req".to_string())))?, dtb_vec: Vec::new(), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -201,14 +199,12 @@ impl StdMachine { } pub fn handle_reset_request(vm: &Arc>) -> Result<()> { - use crate::errors::ResultExt; - let mut locked_vm = vm.lock().unwrap(); let mut fdt_addr: u64 = 0; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.pause() - .chain_err(|| format!("Failed to pause vcpu{}", cpu_index))?; + .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; cpu.set_to_boot_state(); if cpu_index == 0 { @@ -216,7 +212,7 @@ impl StdMachine { } cpu.fd() .vcpu_init(&cpu.arch().lock().unwrap().kvi()) - .chain_err(|| "Failed to init vcpu fd")?; + .with_context(|| "Failed to init vcpu fd")?; } locked_vm @@ -226,18 +222,18 @@ impl StdMachine { GuestAddress(fdt_addr as u64), locked_vm.dtb_vec.len() as u64, ) - .chain_err(|| "Fail to write dtb into sysmem")?; + .with_context(|| "Fail to write dtb into sysmem")?; locked_vm .reset_all_devices() - .chain_err(|| "Fail to reset all devices")?; + .with_context(|| "Fail to reset all devices")?; locked_vm .reset_fwcfg_boot_order() - .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + .with_context(|| "Fail to update boot order imformation to FwCfg device")?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.resume() - .chain_err(|| format!("Failed to resume vcpu{}", cpu_index))?; + .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; } Ok(()) @@ -286,8 +282,6 @@ impl StdMachine { impl StdMachineOps for StdMachine { fn init_pci_host(&self) -> StdResult<()> { - use super::errors::ResultExt; - let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); let mmconfig_region = Region::init_io_region( @@ -300,23 +294,21 @@ impl StdMachineOps for StdMachine { mmconfig_region, MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0, ) - .chain_err(|| "Failed to register ECAM in memory space.")?; + .with_context(|| "Failed to register ECAM in memory space.")?; let pcihost_root = PciHostRoot::new(root_bus); pcihost_root .realize() - .chain_err(|| "Failed to realize pcihost root device.")?; + .with_context(|| "Failed to realize pcihost root device.")?; Ok(()) } fn add_fwcfg_device(&mut self, nr_cpus: u8) -> StdResult>> { - use super::errors::ResultExt; - let mut fwcfg = FwCfgMem::new(self.sys_mem.clone()); fwcfg .add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec()) - .chain_err(|| DevErrorKind::AddEntryErr("NbCpus".to_string()))?; + .with_context(|| anyhow!(DevErrorKind::AddEntryErr("NbCpus".to_string())))?; let cmdline = self.boot_source.lock().unwrap().kernel_cmdline.to_string(); fwcfg @@ -324,20 +316,20 @@ impl StdMachineOps for StdMachine { FwCfgEntryType::CmdlineSize, (cmdline.len() + 1).as_bytes().to_vec(), ) - .chain_err(|| DevErrorKind::AddEntryErr("CmdlineSize".to_string()))?; + .with_context(|| anyhow!(DevErrorKind::AddEntryErr("CmdlineSize".to_string())))?; fwcfg .add_string_entry(FwCfgEntryType::CmdlineData, cmdline.as_str()) - .chain_err(|| DevErrorKind::AddEntryErr("CmdlineData".to_string()))?; + .with_context(|| anyhow!(DevErrorKind::AddEntryErr("CmdlineData".to_string())))?; let boot_order = Vec::::new(); fwcfg .add_file_entry("bootorder", boot_order) - .chain_err(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; + .with_context(|| anyhow!(DevErrorKind::AddEntryErr("bootorder".to_string())))?; let bios_geometry = Vec::::new(); fwcfg .add_file_entry("bios-geometry", bios_geometry) - .chain_err(|| DevErrorKind::AddEntryErr("bios-geometry".to_string()))?; + .with_context(|| anyhow!(DevErrorKind::AddEntryErr("bios-geometry".to_string())))?; let fwcfg_dev = FwCfgMem::realize( fwcfg, @@ -345,7 +337,7 @@ impl StdMachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0, MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, ) - .chain_err(|| "Failed to realize fwcfg device")?; + .with_context(|| "Failed to realize fwcfg device")?; self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) @@ -404,8 +396,6 @@ impl MachineOps for StdMachine { } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { - use crate::errors::ResultExt; - let mut boot_source = self.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); @@ -415,7 +405,7 @@ impl MachineOps for StdMachine { mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .chain_err(|| ErrorKind::LoadKernErr)?; + .with_context(|| anyhow!(MachineError::LoadKernErr))?; if let Some(rd) = &mut boot_source.initrd { rd.initrd_addr = layout.initrd_start; rd.initrd_size = layout.initrd_size; @@ -428,8 +418,6 @@ impl MachineOps for StdMachine { } fn add_rtc_device(&mut self) -> Result<()> { - use crate::errors::ResultExt; - let rtc = PL031::default(); PL031::realize( rtc, @@ -437,17 +425,15 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, ) - .chain_err(|| "Failed to realize PL031")?; + .with_context(|| "Failed to realize PL031")?; Ok(()) } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { - use crate::errors::ResultExt; - let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; - let pl011 = PL011::new(config.clone()).chain_err(|| "Failed to create PL011")?; + let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; pl011 .realize( &mut self.sysbus, @@ -455,7 +441,7 @@ impl MachineOps for StdMachine { region_size, &self.boot_source, ) - .chain_err(|| "Failed to realize PL011")?; + .with_context(|| "Failed to realize PL011")?; Ok(()) } @@ -464,8 +450,7 @@ impl MachineOps for StdMachine { } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { - use super::errors::ErrorKind as StdErrorKind; - use crate::errors::ResultExt; + use super::error::StandardVmError as StdErrorKind; let nr_cpus = vm_config.machine_config.nr_cpus; let clone_vm = vm.clone(); @@ -473,7 +458,7 @@ impl MachineOps for StdMachine { locked_vm.init_global_config(vm_config)?; locked_vm .register_reset_event(&locked_vm.reset_req, clone_vm) - .chain_err(|| "Fail to register reset event")?; + .with_context(|| "Fail to register reset event")?; locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, @@ -500,14 +485,14 @@ impl MachineOps for StdMachine { locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; locked_vm .init_pci_host() - .chain_err(|| StdErrorKind::InitPCIeHostErr)?; + .with_context(|| anyhow!(StdErrorKind::InitPCIeHostErr))?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; locked_vm .add_devices(vm_config) - .chain_err(|| "Failed to add devices")?; + .with_context(|| "Failed to add devices")?; #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) - .chain_err(|| "Failed to init VNC server!")?; + .with_context(|| "Failed to init VNC server!")?; let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { @@ -518,7 +503,7 @@ impl MachineOps for StdMachine { locked_vm .reset_fwcfg_boot_order() - .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + .with_context(|| "Fail to update boot order imformation to FwCfg device")?; locked_vm.cpus.extend(::init_vcpu( vm.clone(), @@ -532,7 +517,7 @@ impl MachineOps for StdMachine { let mut fdt_helper = FdtBuilder::new(); locked_vm .generate_fdt_node(&mut fdt_helper) - .chain_err(|| ErrorKind::GenFdtErr)?; + .with_context(|| anyhow!(MachineError::GenFdtErr))?; let fdt_vec = fdt_helper.finish()?; locked_vm.dtb_vec = fdt_vec.clone(); locked_vm @@ -542,13 +527,15 @@ impl MachineOps for StdMachine { GuestAddress(boot_cfg.fdt_addr as u64), fdt_vec.len() as u64, ) - .chain_err(|| ErrorKind::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; + .with_context(|| { + anyhow!(MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len())) + })?; } if migrate.0 == MigrateMode::Unknown { locked_vm .build_acpi_tables(&fwcfg) - .chain_err(|| "Failed to create ACPI tables")?; + .with_context(|| "Failed to create ACPI tables")?; } locked_vm.register_power_event(&locked_vm.power_button)?; @@ -562,9 +549,7 @@ impl MachineOps for StdMachine { } fn add_pflash_device(&mut self, configs: &[PFlashConfig]) -> Result<()> { - use super::errors::ErrorKind as StdErrorKind; - use crate::errors::ResultExt; - + use super::error::StandardVmError as StdErrorKind; let mut configs_vec = configs.to_vec(); configs_vec.sort_by_key(|c| c.unit); let sector_len: u32 = 1024 * 256; @@ -578,16 +563,16 @@ impl MachineOps for StdMachine { .read(true) .write(!read_only) .open(path) - .chain_err(|| StdErrorKind::OpenFileErr(path.to_string()))?; + .with_context(|| anyhow!(StdErrorKind::OpenFileErr(path.to_string())))?; (Some(fd), read_only) } else { (None, false) }; let pflash = PFlash::new(flash_size, &fd, sector_len, 4, 2, read_only) - .chain_err(|| StdErrorKind::InitPflashErr)?; + .with_context(|| anyhow!(StdErrorKind::InitPflashErr))?; PFlash::realize(pflash, &mut self.sysbus, flash_base, flash_size, fd) - .chain_err(|| StdErrorKind::RlzPflashErr)?; + .with_context(|| anyhow!(StdErrorKind::RlzPflashErr))?; flash_base += flash_size; } @@ -651,8 +636,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut gtdt = AcpiTable::new(*b"GTDT", 2, *b"STRATO", *b"VIRTGTDT", 1); gtdt.set_table_len(96); @@ -677,7 +661,7 @@ impl AcpiBuilder for StdMachine { gtdt.set_field(76, ACPI_GTDT_INTERRUPT_MODE_LEVEL); let gtdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, >dt) - .chain_err(|| "Fail to add GTDT table to loader")?; + .with_context(|| "Fail to add GTDT table to loader")?; Ok(gtdt_begin as u64) } @@ -685,8 +669,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut iort = AcpiTable::new(*b"IORT", 2, *b"STRATO", *b"VIRTIORT", 1); iort.set_table_len(128); @@ -721,7 +704,7 @@ impl AcpiBuilder for StdMachine { iort.set_field(120, 48_u32); let iort_begin = StdMachine::add_table_to_loader(acpi_data, loader, &iort) - .chain_err(|| "Fail to add IORT table to loader")?; + .with_context(|| "Fail to add IORT table to loader")?; Ok(iort_begin as u64) } @@ -729,8 +712,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut spcr = AcpiTable::new(*b"SPCR", 2, *b"STRATO", *b"VIRTSPCR", 1); spcr.set_table_len(80); @@ -766,7 +748,7 @@ impl AcpiBuilder for StdMachine { spcr.set_field(66, 0xffff_u16); let spcr_begin = StdMachine::add_table_to_loader(acpi_data, loader, &spcr) - .chain_err(|| "Fail to add SPCR table to loader")?; + .with_context(|| "Fail to add SPCR table to loader")?; Ok(spcr_begin as u64) } @@ -774,8 +756,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -796,7 +777,7 @@ impl AcpiBuilder for StdMachine { dsdt.append_child(self.sysbus.aml_bytes().as_slice()); let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) - .chain_err(|| "Fail to add DSDT table to loader")?; + .with_context(|| "Fail to add DSDT table to loader")?; Ok(dsdt_begin as u64) } @@ -804,8 +785,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.set_table_len(44); @@ -854,7 +834,7 @@ impl AcpiBuilder for StdMachine { madt.append_child(&gic_its.aml_bytes()); let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) - .chain_err(|| "Fail to add MADT table to loader")?; + .with_context(|| "Fail to add MADT table to loader")?; Ok(madt_begin as u64) } @@ -900,8 +880,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); // Reserved srat.append_child(&[1_u8; 4_usize]); @@ -915,7 +894,7 @@ impl AcpiBuilder for StdMachine { } let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) - .chain_err(|| "Fail to add SRAT table to loader")?; + .with_context(|| "Fail to add SRAT table to loader")?; Ok(srat_begin as u64) } @@ -923,13 +902,12 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut pptt = AcpiTable::new(*b"PPTT", 2, *b"STRATO", *b"VIRTPPTT", 1); let mut uid = 0; self.build_pptt_sockets(&mut pptt, &mut uid); let pptt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &pptt) - .chain_err(|| "Fail to add PPTT table to loader")?; + .with_context(|| "Fail to add PPTT table to loader")?; Ok(pptt_begin as u64) } } @@ -1031,10 +1009,8 @@ impl EventLoopManager for StdMachine { *vmstate == KvmVmState::Shutdown } - fn loop_cleanup(&self) -> util::errors::Result<()> { - use util::errors::ResultExt; - - set_termi_canon_mode().chain_err(|| "Failed to set terminal to canonical mode")?; + fn loop_cleanup(&self) -> util::Result<()> { + set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; Ok(()) } } @@ -1044,7 +1020,7 @@ impl EventLoopManager for StdMachine { // # Arguments // // * `fdt` - Flatted device-tree blob where node will be filled into. -fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::errors::Result<()> { +fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::Result<()> { let pcie_ecam_base = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; let pcie_ecam_size = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1; let pcie_buses_num = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1 >> 20; @@ -1105,7 +1081,7 @@ fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::errors::Result<()> { // // * `dev_info` - Device resource info of Virtio-Mmio device. // * `fdt` - Flatted device-tree blob where node will be filled into. -fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("virtio_mmio@{:x}", res.region_base); let virtio_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "virtio,mmio")?; @@ -1129,7 +1105,7 @@ fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::err /// /// * `dev_info` - Device resource info of fw-cfg device. /// * `flash` - Flatted device-tree blob where fw-cfg node will be filled into. -fn generate_flash_device_node(fdt: &mut FdtBuilder) -> util::errors::Result<()> { +fn generate_flash_device_node(fdt: &mut FdtBuilder) -> util::Result<()> { let flash_base = MEM_LAYOUT[LayoutEntryType::Flash as usize].0; let flash_size = MEM_LAYOUT[LayoutEntryType::Flash as usize].1 / 2; let node = format!("flash@{:x}", flash_base); @@ -1150,7 +1126,7 @@ fn generate_flash_device_node(fdt: &mut FdtBuilder) -> util::errors::Result<()> /// /// * `dev_info` - Device resource info of fw-cfg device. /// * `fdt` - Flatted device-tree blob where fw-cfg node will be filled into. -fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("fw-cfg@{:x}", res.region_base); let fwcfg_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "qemu,fw-cfg-mmio")?; @@ -1166,7 +1142,7 @@ fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::error // // * `dev_info` - Device resource info of serial device. // * `fdt` - Flatted device-tree blob where serial node will be filled into. -fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("pl011@{:x}", res.region_base); let serial_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "arm,pl011\0arm,primecell")?; @@ -1195,7 +1171,7 @@ fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::erro // // * `dev_info` - Device resource info of RTC device. // * `fdt` - Flatted device-tree blob where RTC node will be filled into. -fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors::Result<()> { +fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { let node = format!("pl031@{:x}", res.region_base); let rtc_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "arm,pl031\0arm,primecell\0")?; @@ -1219,19 +1195,19 @@ fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::errors: #[allow(clippy::upper_case_acronyms)] trait CompileFDTHelper { /// Function that helps to generate cpu nodes. - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate memory nodes. - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate Virtio-mmio devices' nodes. - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate the chosen node. - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate numa node distances. - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()>; + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; } impl CompileFDTHelper for StdMachine { - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "cpus"; let cpus_node_dep = fdt.begin_node(node)?; @@ -1294,7 +1270,7 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { if self.numa_nodes.is_none() { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let mem_size = self.sys_mem.memory_end_address().raw_value() @@ -1324,7 +1300,7 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { // timer let mut cells: Vec = Vec::new(); for &irq in [13, 14, 11, 10].iter() { @@ -1381,7 +1357,7 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "chosen"; let boot_source = self.boot_source.lock().unwrap(); @@ -1406,7 +1382,7 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { if self.numa_nodes.is_none() { return Ok(()); } @@ -1441,7 +1417,7 @@ impl CompileFDTHelper for StdMachine { } impl device_tree::CompileFDT for StdMachine { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node_dep = fdt.begin_node("")?; fdt.set_property_string("compatible", "linux,dummy-virt")?; diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 1b5b5d439..8509fdc99 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -18,8 +18,7 @@ use pci::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }, - errors::Result as PciResult, - le_write_u16, PciBus, PciDevOps, + le_write_u16, PciBus, PciDevOps, Result as PciResult, }; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; diff --git a/machine/src/standard_vm/error.rs b/machine/src/standard_vm/error.rs new file mode 100644 index 000000000..539cd984a --- /dev/null +++ b/machine/src/standard_vm/error.rs @@ -0,0 +1,61 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Error, Debug)] +pub enum StandardVmError { + #[error("")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("")] + Cpu { + #[from] + source: cpu::error::CpuError, + }, + #[error("")] + Legacy { + #[from] + source: devices::legacy::error::LegacyError, + }, + #[error("")] + PciErr { + #[from] + source: pci::error::PciError, + }, + #[error("")] + Acpi { + #[from] + source: acpi::error::AcpiError, + }, + #[error("")] + MachineManager { + #[from] + source: machine_manager::config::error::ConfigError, + }, + #[error("")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Failed to init PCIe host.")] + InitPCIeHostErr, + #[error("Failed to open file: {0}.")] + OpenFileErr(String), + #[error("Failed to init pflash device.")] + InitPflashErr, + #[error("Failed to realize pflash device.")] + RlzPflashErr, +} diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index baae66d99..0078a5101 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -16,6 +16,9 @@ mod aarch64; #[cfg(target_arch = "x86_64")] mod x86_64; +pub mod error; +pub use error::StandardVmError; + #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; use log::error; @@ -26,46 +29,13 @@ use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; -#[allow(clippy::upper_case_acronyms)] -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - Cpu(cpu::errors::Error, cpu::errors::ErrorKind); - Legacy(devices::LegacyErrs::Error, devices::LegacyErrs::ErrorKind); - PciErr(pci::errors::Error, pci::errors::ErrorKind); - Acpi(acpi::errors::Error, acpi::errors::ErrorKind); - MachineManager(machine_manager::config::errors::Error, machine_manager::config::errors::ErrorKind); - } - foreign_links{ - Io(std::io::Error); - } - errors { - InitPCIeHostErr { - display("Failed to init PCIe host.") - } - OpenFileErr(path: String) { - display("Failed to open file: {}.", path) - } - InitPflashErr { - display("Failed to init pflash device.") - } - RlzPflashErr { - display("Failed to realize pflash device.") - } - } - } -} - use std::mem::size_of; use std::ops::Deref; use std::os::unix::io::RawFd; use std::os::unix::prelude::AsRawFd; use std::sync::{Arc, Condvar, Mutex}; -use crate::errors::Result as MachineResult; +use super::Result as MachineResult; use crate::MachineOps; #[cfg(target_arch = "x86_64")] use acpi::AcpiGenericAddress; @@ -73,10 +43,10 @@ use acpi::{ AcpiRsdp, AcpiTable, AmlBuilder, TableLoader, ACPI_RSDP_FILE, ACPI_TABLE_FILE, ACPI_TABLE_LOADER_FILE, TABLE_CHECKSUM_OFFSET, }; +pub use anyhow::Result; +use anyhow::{bail, Context}; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; -use error_chain::{bail, ChainedError}; -use errors::{Result, ResultExt}; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, VmConfig, @@ -121,52 +91,52 @@ trait StdMachineOps: AcpiBuilder { #[cfg(target_arch = "x86_64")] { let facs_addr = Self::build_facs_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI FACS table")?; + .with_context(|| "Failed to build ACPI FACS table")?; xsdt_entries.push(facs_addr); } let dsdt_addr = self .build_dsdt_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI DSDT table")?; + .with_context(|| "Failed to build ACPI DSDT table")?; let fadt_addr = Self::build_fadt_table(&acpi_tables, &mut loader, dsdt_addr) - .chain_err(|| "Failed to build ACPI FADT table")?; + .with_context(|| "Failed to build ACPI FADT table")?; xsdt_entries.push(fadt_addr); let madt_addr = self .build_madt_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI MADT table")?; + .with_context(|| "Failed to build ACPI MADT table")?; xsdt_entries.push(madt_addr); #[cfg(target_arch = "aarch64")] { let gtdt_addr = self .build_gtdt_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI GTDT table")?; + .with_context(|| "Failed to build ACPI GTDT table")?; xsdt_entries.push(gtdt_addr); let iort_addr = self .build_iort_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI IORT table")?; + .with_context(|| "Failed to build ACPI IORT table")?; xsdt_entries.push(iort_addr); let spcr_addr = self .build_spcr_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI SPCR table")?; + .with_context(|| "Failed to build ACPI SPCR table")?; xsdt_entries.push(spcr_addr); } let mcfg_addr = Self::build_mcfg_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI MCFG table")?; + .with_context(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); if let Some(numa_nodes) = self.get_numa_nodes() { let srat_addr = self .build_srat_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI SRAT table")?; + .with_context(|| "Failed to build ACPI SRAT table")?; xsdt_entries.push(srat_addr); let slit_addr = Self::build_slit_table(numa_nodes, &acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI SLIT table")?; + .with_context(|| "Failed to build ACPI SLIT table")?; xsdt_entries.push(slit_addr); } @@ -174,7 +144,7 @@ trait StdMachineOps: AcpiBuilder { { let pptt_addr = self .build_pptt_table(&acpi_tables, &mut loader) - .chain_err(|| "Failed to build ACPI PPTT table")?; + .with_context(|| "Failed to build ACPI PPTT table")?; xsdt_entries.push(pptt_addr); } @@ -186,14 +156,14 @@ trait StdMachineOps: AcpiBuilder { &mut *locked_fw_cfg as &mut dyn FwCfgOps, xsdt_addr, ) - .chain_err(|| "Failed to build ACPI RSDP")?; + .with_context(|| "Failed to build ACPI RSDP")?; locked_fw_cfg .add_file_entry(ACPI_TABLE_LOADER_FILE, loader.cmd_entries()) - .chain_err(|| "Failed to add ACPI table loader file entry")?; + .with_context(|| "Failed to add ACPI table loader file entry")?; locked_fw_cfg .add_file_entry(ACPI_TABLE_FILE, acpi_tables.lock().unwrap().to_vec()) - .chain_err(|| "Failed to add ACPI-tables file entry")?; + .with_context(|| "Failed to add ACPI-tables file entry")?; Ok(()) } @@ -227,10 +197,7 @@ trait StdMachineOps: AcpiBuilder { Arc::new(Mutex::new(Box::new(move |_, _| { let _ret = reset_req.read().unwrap(); if let Err(e) = StdMachine::handle_reset_request(&clone_vm) { - error!( - "Fail to reboot standard VM, {}", - error_chain::ChainedError::display_chain(&e) - ); + error!("Fail to reboot standard VM, {:?}", e); } None @@ -243,7 +210,7 @@ trait StdMachineOps: AcpiBuilder { vec![reset_req_handler], ); EventLoop::update_event(vec![notifier], None) - .chain_err(|| "Failed to register event notifier.")?; + .with_context(|| "Failed to register event notifier.")?; Ok(()) } @@ -276,7 +243,7 @@ trait StdMachineOps: AcpiBuilder { vec![shutdown_req_handler], ); EventLoop::update_event(vec![notifier], None) - .chain_err(|| "Failed to register event notifier.")?; + .with_context(|| "Failed to register event notifier.")?; Ok(()) } } @@ -667,7 +634,7 @@ trait AcpiBuilder { } let slit_begin = StdMachine::add_table_to_loader(acpi_data, loader, &slit) - .chain_err(|| "Fail to add SLIT table to loader")?; + .with_context(|| "Fail to add SLIT table to loader")?; Ok(slit_begin as u64) } @@ -764,7 +731,7 @@ fn get_device_bdf(bus: Option, addr: Option) -> Result { addr: (0, 0), }; let addr = addr.unwrap_or_else(|| String::from("0x0")); - pci_bdf.addr = get_pci_df(&addr).chain_err(|| "Failed to get device num or function num")?; + pci_bdf.addr = get_pci_df(&addr).with_context(|| "Failed to get device num or function num")?; Ok(pci_bdf) } @@ -805,14 +772,14 @@ impl StdMachine { if let Some(bootindex) = args.boot_index { self.check_bootindex(bootindex) - .chain_err(|| "Fail to add virtio pci blk device for invalid bootindex")?; + .with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?; } let blk_id = blk.id.clone(); let blk = Arc::new(Mutex::new(Block::new(blk))); let pci_dev = self .add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) - .chain_err(|| "Failed to add virtio pci block device")?; + .with_context(|| "Failed to add virtio pci block device")?; if let Some(bootindex) = args.boot_index { if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { @@ -871,7 +838,7 @@ impl StdMachine { if let Some(chardev) = &conf.chardev { socket_path = self .get_socket_path(&vm_config, (&chardev).to_string()) - .chain_err(|| "Failed to get socket path")?; + .with_context(|| "Failed to get socket path")?; } let dev = NetworkInterfaceConfig { id: args.id.clone(), @@ -902,12 +869,12 @@ impl StdMachine { Arc::new(Mutex::new(VhostUser::Net::new(&dev, self.get_sys_mem()))) }; self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, need_irqfd) - .chain_err(|| "Failed to add vhost-kernel/vhost-user net device")?; + .with_context(|| "Failed to add vhost-kernel/vhost-user net device")?; } else { let net_id = dev.id.clone(); let net = Arc::new(Mutex::new(virtio::Net::new(dev))); self.add_virtio_pci_device(&args.id, pci_bdf, net.clone(), multifunction, false) - .chain_err(|| "Failed to add virtio net device")?; + .with_context(|| "Failed to add virtio net device")?; MigrationManager::register_device_instance(VirtioNetState::descriptor(), net, &net_id); } @@ -949,7 +916,7 @@ impl StdMachine { sysfsdev, args.multifunction.map_or(false, |m| m), ) { - error!("{}", e.display_chain()); + error!("{:?}", e); bail!("Failed to plug vfio-pci device."); } Ok(()) @@ -1068,7 +1035,7 @@ impl DeviceInterface for StdMachine { match driver { "virtio-blk-pci" => { if let Err(e) = self.plug_virtio_pci_blk(&pci_bdf, args.as_ref()) { - error!("{}", e.display_chain()); + error!("{:?}", e); let err_str = format!("Failed to add virtio pci blk: {}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(err_str), @@ -1078,7 +1045,7 @@ impl DeviceInterface for StdMachine { } "virtio-net-pci" => { if let Err(e) = self.plug_virtio_pci_net(&pci_bdf, args.as_ref()) { - error!("{}", e.display_chain()); + error!("{:?}", e); let err_str = format!("Failed to add virtio pci net: {}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(err_str), @@ -1110,7 +1077,7 @@ impl DeviceInterface for StdMachine { Ok(()) => Response::create_empty_response(), Err(e) => { if let Err(e) = PciBus::detach_device(&bus, &dev) { - error!("{}", e.display_chain()); + error!("{:?}", e); error!("Failed to detach device"); } let err_str = format!("Failed to plug device: {}", e); diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index d6e2eb303..4faca0926 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -15,23 +15,22 @@ use std::sync::{ Arc, Mutex, Weak, }; +use super::VENDOR_ID_INTEL; +use crate::standard_vm::Result; use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; -use error_chain::ChainedError; +use anyhow::Context; use log::{debug, error}; use pci::config::CLASS_CODE_ISA_BRIDGE; use pci::config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::errors::Result as PciResult; +use pci::Result as PciResult; use pci::{le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevOps}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; -use super::VENDOR_ID_INTEL; -use crate::standard_vm::errors::Result; - const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; const PM_BASE_OFFSET: u8 = 0x40; @@ -221,8 +220,6 @@ impl PciDevOps for LPCBridge { } fn realize(mut self) -> PciResult<()> { - use pci::errors::ResultExt; - self.init_write_mask()?; self.init_write_clear_mask()?; @@ -245,15 +242,15 @@ impl PciDevOps for LPCBridge { )?; self.init_sleep_reg() - .chain_err(|| "Fail to init IO region for sleep control register")?; + .with_context(|| "Fail to init IO region for sleep control register")?; self.init_reset_ctrl_reg() - .chain_err(|| "Fail to init IO region for reset control register")?; + .with_context(|| "Fail to init IO region for reset control register")?; self.init_pm_evt_reg() - .chain_err(|| "Fail to init IO region for PM events register")?; + .with_context(|| "Fail to init IO region for PM events register")?; self.init_pm_ctrl_reg() - .chain_err(|| "Fail to init IO region for PM control register")?; + .with_context(|| "Fail to init IO region for PM control register")?; let parent_bus = self.parent_bus.clone(); parent_bus @@ -297,7 +294,7 @@ impl PciDevOps for LPCBridge { PM_BASE_OFFSET as usize + 4, ) { if let Err(e) = self.update_pm_base() { - error!("Failed to update PM base addr: {}", e.display_chain()); + error!("Failed to update PM base addr: {:?}", e); } } } diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 05f87da36..aca68db5c 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -13,19 +13,17 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{Region, RegionOps}; -use error_chain::{bail, ChainedError}; +use anyhow::{bail, Result}; use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }, - errors::Result as PciResult, - le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevOps, + le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevOps, Result as PciResult, }; use super::VENDOR_ID_INTEL; -use crate::standard_vm::errors::Result; const DEVICE_ID_INTEL_Q35_MCH: u16 = 0x29c0; @@ -180,7 +178,7 @@ impl PciDevOps for Mch { self.config.write(offset, data, 0); if ranges_overlap(offset, end, PCIEXBAR as usize, PCIEXBAR as usize + 8) { if let Err(e) = self.update_pciexbar_mapping() { - error!("{}", e.display_chain()); + error!("{:?}", e); } } } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 4bac5e8be..b501495ca 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -14,15 +14,14 @@ pub(crate) mod ich9_lpc; mod mch; mod syscall; +use crate::error::MachineError; +use log::error; use std::collections::HashMap; use std::fs::OpenOptions; use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; - -use error_chain::{bail, ChainedError}; -use log::error; use vmm_sys_util::{eventfd::EventFd, ioctl_ioc_nr, ioctl_iow_nr}; use acpi::{ @@ -34,7 +33,7 @@ use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::{ - errors::ErrorKind as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, + error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, }; use hypervisor::kvm::KVM_FDS; @@ -60,10 +59,10 @@ use util::{ }; use self::ich9_lpc::SLEEP_CTRL_OFFSET; -use super::errors::{ErrorKind, Result}; +use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; -use crate::errors::{ErrorKind as MachineErrorKind, Result as MachineResult}; use crate::{vm_state, MachineOps}; +use anyhow::{anyhow, bail, Context, Result}; #[cfg(not(target_env = "musl"))] use vnc::vnc; @@ -129,9 +128,7 @@ pub struct StdMachine { } impl StdMachine { - pub fn new(vm_config: &VmConfig) -> MachineResult { - use crate::errors::ResultExt; - + pub fn new(vm_config: &VmConfig) -> Result { let cpu_topo = CpuTopology::new( vm_config.machine_config.nr_cpus, vm_config.machine_config.nr_sockets, @@ -142,9 +139,9 @@ impl StdMachine { vm_config.machine_config.max_cpus, ); let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) - .chain_err(|| MachineErrorKind::CrtMemSpaceErr)?; + .with_context(|| anyhow!(MachineError::CrtMemSpaceErr))?; let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .chain_err(|| MachineErrorKind::CrtIoSpaceErr)?; + .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; let sysbus = SysBus::new( &sys_io, &sys_mem, @@ -171,8 +168,9 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, - power_button: EventFd::new(libc::EFD_NONBLOCK) - .chain_err(|| MachineErrorKind::InitEventFdErr("power_button".to_string()))?, + power_button: EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + anyhow!(MachineError::InitEventFdErr("power_button".to_string())) + })?, vm_config: Mutex::new(vm_config.clone()), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -181,30 +179,24 @@ impl StdMachine { }) } - pub fn handle_reset_request(vm: &Arc>) -> MachineResult<()> { - use crate::errors::ResultExt as MachineResultExt; - + pub fn handle_reset_request(vm: &Arc>) -> Result<()> { let mut locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { - MachineResultExt::chain_err(cpu.kick(), || { - format!("Failed to kick vcpu{}", cpu_index) - })?; + Result::with_context(cpu.kick(), || format!("Failed to kick vcpu{}", cpu_index))?; cpu.set_to_boot_state(); } - MachineResultExt::chain_err(locked_vm.reset_all_devices(), || { + Result::with_context(locked_vm.reset_all_devices(), || { "Fail to reset all devices" })?; - MachineResultExt::chain_err(locked_vm.reset_fwcfg_boot_order(), || { + Result::with_context(locked_vm.reset_fwcfg_boot_order(), || { "Fail to update boot order information to FwCfg device" })?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { - MachineResultExt::chain_err(cpu.reset(), || { - format!("Failed to reset vcpu{}", cpu_index) - })?; + Result::with_context(cpu.reset(), || format!("Failed to reset vcpu{}", cpu_index))?; } Ok(()) @@ -214,11 +206,7 @@ impl StdMachine { let locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { if let Err(e) = cpu.destroy() { - error!( - "Failed to destroy vcpu{}, error is {}", - cpu_index, - e.display_chain() - ); + error!("Failed to destroy vcpu{}, error is {:?}", cpu_index, e); } } @@ -227,9 +215,7 @@ impl StdMachine { true } - fn arch_init() -> MachineResult<()> { - use crate::errors::ResultExt; - + fn arch_init() -> Result<()> { let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); let identity_addr: u64 = MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0; @@ -247,7 +233,7 @@ impl StdMachine { // Page table takes 1 page, TSS takes the following 3 pages. vm_fd .set_tss_address((identity_addr + 0x1000) as usize) - .chain_err(|| MachineErrorKind::SetTssErr)?; + .with_context(|| anyhow!(MachineError::SetTssErr))?; let pit_config = kvm_pit_config { flags: KVM_PIT_SPEAKER_DUMMY, @@ -255,20 +241,18 @@ impl StdMachine { }; vm_fd .create_pit2(pit_config) - .chain_err(|| MachineErrorKind::CrtPitErr)?; + .with_context(|| anyhow!(MachineError::CrtPitErr))?; Ok(()) } fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { - use super::errors::ResultExt; - let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone()); self.register_reset_event(&ich.reset_req, vm) - .chain_err(|| "Fail to register reset event in LPC")?; + .with_context(|| "Fail to register reset event in LPC")?; self.register_acpi_shutdown_event(&ich.shutdown_req, clone_vm) - .chain_err(|| "Fail to register shutdown event in LPC")?; + .with_context(|| "Fail to register shutdown event in LPC")?; PciDevOps::realize(ich)?; Ok(()) } @@ -276,8 +260,6 @@ impl StdMachine { impl StdMachineOps for StdMachine { fn init_pci_host(&self) -> Result<()> { - use super::errors::ResultExt; - let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); let mmconfig_region = Region::init_io_region( @@ -290,29 +272,27 @@ impl StdMachineOps for StdMachine { mmconfig_region.clone(), MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].0, ) - .chain_err(|| "Failed to register ECAM in memory space.")?; + .with_context(|| "Failed to register ECAM in memory space.")?; let pio_addr_ops = PciHost::build_pio_addr_ops(self.pci_host.clone()); let pio_addr_region = Region::init_io_region(4, pio_addr_ops); self.sys_io .root() .add_subregion(pio_addr_region, 0xcf8) - .chain_err(|| "Failed to register CONFIG_ADDR port in I/O space.")?; + .with_context(|| "Failed to register CONFIG_ADDR port in I/O space.")?; let pio_data_ops = PciHost::build_pio_data_ops(self.pci_host.clone()); let pio_data_region = Region::init_io_region(4, pio_data_ops); self.sys_io .root() .add_subregion(pio_data_region, 0xcfc) - .chain_err(|| "Failed to register CONFIG_DATA port in I/O space.")?; + .with_context(|| "Failed to register CONFIG_DATA port in I/O space.")?; let mch = Mch::new(root_bus, mmconfig_region, mmconfig_region_ops); PciDevOps::realize(mch)?; Ok(()) } - fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::errors::Result>> { - use super::errors::ResultExt; - + fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::Result>> { let mut fwcfg = FwCfgIO::new(self.sys_mem.clone()); fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, nr_cpus.as_bytes().to_vec())?; @@ -321,10 +301,10 @@ impl StdMachineOps for StdMachine { let boot_order = Vec::::new(); fwcfg .add_file_entry("bootorder", boot_order) - .chain_err(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; + .with_context(|| anyhow!(DevErrorKind::AddEntryErr("bootorder".to_string())))?; let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) - .chain_err(|| "Failed to realize fwcfg device")?; + .with_context(|| "Failed to realize fwcfg device")?; self.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(fwcfg_dev) @@ -360,16 +340,14 @@ impl MachineOps for StdMachine { ranges } - fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> MachineResult<()> { - use crate::errors::ResultExt; - + fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { KVM_FDS .load() .vm_fd .as_ref() .unwrap() .create_irq_chip() - .chain_err(|| MachineErrorKind::CrtIrqchipErr)?; + .with_context(|| anyhow!(MachineError::CrtIrqchipErr))?; KVM_FDS .load() .irq_route_table @@ -380,12 +358,7 @@ impl MachineOps for StdMachine { Ok(()) } - fn load_boot_source( - &self, - fwcfg: Option<&Arc>>, - ) -> MachineResult { - use crate::errors::ResultExt; - + fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { let boot_source = self.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); @@ -404,7 +377,7 @@ impl MachineOps for StdMachine { prot64_mode: false, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .chain_err(|| MachineErrorKind::LoadKernErr)?; + .with_context(|| anyhow!(MachineError::LoadKernErr))?; Ok(CPUBootConfig { prot64_mode: false, @@ -415,28 +388,25 @@ impl MachineOps for StdMachine { }) } - fn add_rtc_device(&mut self, mem_size: u64) -> MachineResult<()> { - use crate::errors::ResultExt; - - let mut rtc = RTC::new().chain_err(|| "Failed to create RTC device")?; + fn add_rtc_device(&mut self, mem_size: u64) -> Result<()> { + let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; rtc.set_memory( mem_size, MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); - RTC::realize(rtc, &mut self.sysbus).chain_err(|| "Failed to realize RTC device")?; + RTC::realize(rtc, &mut self.sysbus).with_context(|| "Failed to realize RTC device")?; Ok(()) } - fn add_serial_device(&mut self, config: &SerialConfig) -> MachineResult<()> { - use crate::errors::ResultExt; + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; let serial = Serial::new(config.clone()); serial .realize(&mut self.sysbus, region_base, region_size) - .chain_err(|| "Failed to realize serial device.")?; + .with_context(|| "Failed to realize serial device.")?; Ok(()) } @@ -444,9 +414,7 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { - use crate::errors::ResultExt; - + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); @@ -469,14 +437,14 @@ impl MachineOps for StdMachine { locked_vm .init_pci_host() - .chain_err(|| ErrorKind::InitPCIeHostErr)?; + .with_context(|| anyhow!(StandardVmError::InitPCIeHostErr))?; locked_vm .init_ich9_lpc(clone_vm) - .chain_err(|| "Fail to init LPC bridge")?; + .with_context(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) - .chain_err(|| "Failed to init VNC server!")?; + .with_context(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; let migrate = locked_vm.get_migrate_info(); @@ -501,7 +469,7 @@ impl MachineOps for StdMachine { if migrate.0 == MigrateMode::Unknown { locked_vm .build_acpi_tables(&fwcfg) - .chain_err(|| "Failed to create ACPI tables")?; + .with_context(|| "Failed to create ACPI tables")?; } StdMachine::arch_init()?; @@ -509,7 +477,7 @@ impl MachineOps for StdMachine { locked_vm .reset_fwcfg_boot_order() - .chain_err(|| "Fail to update boot order imformation to FwCfg device")?; + .with_context(|| "Fail to update boot order imformation to FwCfg device")?; MigrationManager::register_vm_config(vm_config); MigrationManager::register_vm_instance(vm.clone()); @@ -524,9 +492,7 @@ impl MachineOps for StdMachine { Ok(()) } - fn add_pflash_device(&mut self, configs: &[PFlashConfig]) -> MachineResult<()> { - use super::errors::ResultExt; - + fn add_pflash_device(&mut self, configs: &[PFlashConfig]) -> Result<()> { let mut configs_vec = configs.to_vec(); configs_vec.sort_by_key(|c| c.unit); // The two PFlash devices locates below 4GB, this variable represents the end address @@ -537,7 +503,9 @@ impl MachineOps for StdMachine { .read(true) .write(!config.read_only) .open(&config.path_on_host) - .chain_err(|| ErrorKind::OpenFileErr(config.path_on_host.clone()))?; + .with_context(|| { + anyhow!(StandardVmError::OpenFileErr(config.path_on_host.clone())) + })?; let pfl_size = fd.metadata().unwrap().len(); if config.unit == 0 { @@ -575,7 +543,7 @@ impl MachineOps for StdMachine { 1_u32, config.read_only, ) - .chain_err(|| ErrorKind::InitPflashErr)?; + .with_context(|| anyhow!(StandardVmError::InitPflashErr))?; PFlash::realize( pflash, &mut self.sysbus, @@ -583,14 +551,14 @@ impl MachineOps for StdMachine { pfl_size, backend, ) - .chain_err(|| ErrorKind::RlzPflashErr)?; + .with_context(|| anyhow!(StandardVmError::RlzPflashErr))?; flash_end -= pfl_size; } Ok(()) } - fn run(&self, paused: bool) -> MachineResult<()> { + fn run(&self, paused: bool) -> Result<()> { ::vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) } @@ -618,7 +586,7 @@ impl MachineOps for StdMachine { &self.sysbus } - fn get_fwcfg_dev(&mut self) -> MachineResult>> { + fn get_fwcfg_dev(&mut self) -> Result>> { // Unwrap is safe. Because after standard machine realize, this will not be None.F Ok(self.fwcfg_dev.clone().unwrap()) } @@ -637,8 +605,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -668,7 +635,7 @@ impl AcpiBuilder for StdMachine { dsdt.append_child(AmlNameDecl::new("_S5", package).aml_bytes().as_slice()); let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) - .chain_err(|| "Fail to add DSTD table to loader")?; + .with_context(|| "Fail to add DSTD table to loader")?; Ok(dsdt_begin as u64) } @@ -676,8 +643,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.append_child(LAPIC_BASE_ADDR.as_bytes()); @@ -706,7 +672,7 @@ impl AcpiBuilder for StdMachine { }); let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) - .chain_err(|| "Fail to add DSTD table to loader")?; + .with_context(|| "Fail to add DSTD table to loader")?; Ok(madt_begin as u64) } @@ -809,8 +775,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::errors::Result { - use super::errors::ResultExt; + ) -> super::Result { let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); srat.append_child(&[1_u8; 4_usize]); srat.append_child(&[0_u8; 8_usize]); @@ -822,7 +787,7 @@ impl AcpiBuilder for StdMachine { } let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) - .chain_err(|| "Fail to add SRAT table to loader")?; + .with_context(|| "Fail to add SRAT table to loader")?; Ok(srat_begin as u64) } } @@ -897,7 +862,7 @@ impl MachineAddressInterface for StdMachine { let count = data.len() as u64; if addr == SLEEP_CTRL_OFFSET as u64 { if let Err(e) = self.cpus[0].pause() { - error!("Fail to pause bsp, {}", e.display_chain()); + error!("Fail to pause bsp, {:?}", e); } } self.sys_io @@ -951,10 +916,8 @@ impl EventLoopManager for StdMachine { *vmstate == KvmVmState::Shutdown } - fn loop_cleanup(&self) -> util::errors::Result<()> { - use util::errors::ResultExt; - - set_termi_canon_mode().chain_err(|| "Failed to set terminal to canonical mode")?; + fn loop_cleanup(&self) -> util::Result<()> { + set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; Ok(()) } } diff --git a/machine/src/vm_state.rs b/machine/src/vm_state.rs index 4254d8024..64b9e9719 100644 --- a/machine/src/vm_state.rs +++ b/machine/src/vm_state.rs @@ -13,6 +13,7 @@ use kvm_bindings::{kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_IRQCHIP_IOAPIC}; use migration_derive::{ByteCode, Desc}; +use anyhow::anyhow; use hypervisor::kvm::KVM_FDS; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use util::byte_code::ByteCode; @@ -32,7 +33,7 @@ pub struct KvmDeviceState { } impl StateTransfer for KvmDevice { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); @@ -60,12 +61,12 @@ impl StateTransfer for KvmDevice { .to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state(&self, state: &[u8]) -> migration::Result<()> { let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); let kvm_state = KvmDeviceState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("KVM_DEVICE"))?; + .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("KVM_DEVICE")))?; vm_fd.set_pit2(&kvm_state.pit_state)?; vm_fd.set_clock(&kvm_state.kvm_clock)?; diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 3e797e6ba..fcf59a285 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -8,7 +8,6 @@ license = "Mulan PSL v2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -error-chain = "0.12.4" log = "0.4" libc = "0.2" serde_json = "1.0" @@ -17,6 +16,8 @@ serde = { version = "1.0", features = ["derive"] } strum = "0.20" strum_macros = "0.20" once_cell = "1.13.0" +thiserror = "1.0" +anyhow = "1.0" util = { path = "../util" } [features] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 551a2123a..66b214191 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -12,13 +12,12 @@ use std::os::unix::net::UnixListener; -use error_chain::bail; +use anyhow::{bail, Context, Result}; use util::arg_parser::{Arg, ArgMatches, ArgParser}; use util::unix::{limit_permission, parse_unix_uri}; use crate::{ config::{add_trace_events, ChardevType, CmdParser, MachineType, VmConfig}, - errors::{Result, ResultExt}, temp_cleaner::TempCleaner, }; @@ -454,7 +453,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { if vm_cfg.machine_config.mach_type != MachineType::None { vm_cfg .check_vmconfig(args.is_present("daemonize")) - .chain_err(|| "Precheck failed, VmConfig is unhealthy, stop running")?; + .with_context(|| "Precheck failed, VmConfig is unhealthy, stop running")?; } Ok(vm_cfg) } @@ -476,7 +475,8 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< cmd_parser.parse(&qmp_config)?; if let Some(uri) = cmd_parser.get_value::("")? { - let api_path = parse_unix_uri(&uri).chain_err(|| "Failed to parse qmp socket path")?; + let api_path = + parse_unix_uri(&uri).with_context(|| "Failed to parse qmp socket path")?; sock_paths.push(api_path); } else { bail!("No uri found for qmp"); @@ -537,7 +537,7 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< for path in sock_paths { listeners.push( bind_socket(path.clone()) - .chain_err(|| format!("Failed to bind socket for path: {:?}", &path))?, + .with_context(|| format!("Failed to bind socket for path: {:?}", &path))?, ) } @@ -545,11 +545,11 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< } fn bind_socket(path: String) -> Result { - let listener = - UnixListener::bind(&path).chain_err(|| format!("Failed to bind socket file {}", &path))?; + let listener = UnixListener::bind(&path) + .with_context(|| format!("Failed to bind socket file {}", &path))?; // Add file to temporary pool, so it could be cleaned when vm exits. TempCleaner::add_path(path.clone()); limit_permission(&path) - .chain_err(|| format!("Failed to limit permission for socket file {}", &path))?; + .with_context(|| format!("Failed to limit permission for socket file {}", &path))?; Ok(listener) } diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index 9836af3f0..ff411f174 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -10,13 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::{anyhow, bail, Result}; use serde::{Deserialize, Serialize}; -use super::{ - errors::{ErrorKind, Result}, - pci_args_check, ConfigCheck, MAX_STRING_LENGTH, -}; +use super::{error::ConfigError, pci_args_check, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::{CmdParser, ExBool, VmConfig}; #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -29,11 +26,10 @@ pub struct BalloonConfig { impl ConfigCheck for BalloonConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "balloon id".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } Ok(()) diff --git a/machine_manager/src/config/boot_source.rs b/machine_manager/src/config/boot_source.rs index 81cc0f515..f582907d1 100644 --- a/machine_manager/src/config/boot_source.rs +++ b/machine_manager/src/config/boot_source.rs @@ -13,10 +13,10 @@ use std::fmt; use std::path::PathBuf; -use serde::{Deserialize, Serialize}; - -use super::errors::{ErrorKind, Result}; +use super::error::ConfigError; use crate::config::{ConfigCheck, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH}; +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; /// Config struct for boot-source. /// Contains `kernel_file`, `kernel_cmdline` and `initrd`. @@ -41,14 +41,15 @@ impl ConfigCheck for BootSource { fn check(&self) -> Result<()> { if let Some(kernel_file) = &self.kernel_file { if kernel_file.to_str().unwrap().len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "kernel_file path".to_string(), MAX_PATH_LENGTH, - ) - .into()); + ))); } if !kernel_file.is_file() { - return Err(ErrorKind::UnRegularFile("Input kernel_file".to_string()).into()); + return Err(anyhow!(ConfigError::UnRegularFile( + "Input kernel_file".to_string() + ))); } } @@ -82,15 +83,16 @@ impl InitrdConfig { impl ConfigCheck for InitrdConfig { fn check(&self) -> Result<()> { if self.initrd_file.to_str().unwrap().len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "initrd_file".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } if !self.initrd_file.is_file() { - return Err(ErrorKind::UnRegularFile("Input initrd_file".to_string()).into()); + return Err(anyhow!(ConfigError::UnRegularFile( + "Input initrd_file".to_string() + ))); } Ok(()) @@ -109,11 +111,10 @@ impl ConfigCheck for KernelParams { fn check(&self) -> Result<()> { for param in self.params.clone() { if param.value.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "kernel params".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } } diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index d040c66ea..6eb164fbf 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -10,14 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use log::error; use serde::{Deserialize, Serialize}; -use super::{ - errors::{ErrorKind, Result, ResultExt}, - get_pci_bdf, pci_args_check, PciBdf, -}; +use super::{error::ConfigError, get_pci_bdf, pci_args_check, PciBdf}; use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH}; use crate::qmp::qmp_schema; @@ -54,11 +51,10 @@ pub struct ChardevConfig { impl ConfigCheck for ChardevConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "chardev id".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } let len = match &self.backend { @@ -67,9 +63,10 @@ impl ConfigCheck for ChardevConfig { _ => 0, }; if len > MAX_PATH_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("socket path".to_string(), MAX_PATH_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "socket path".to_string(), + MAX_PATH_LENGTH + ))); } Ok(()) @@ -118,7 +115,7 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { let chardev_id = if let Some(chardev_id) = cmd_parser.get_value::("id")? { chardev_id } else { - return Err(ErrorKind::FieldIsMissing("id", "chardev").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "chardev"))); }; let backend = cmd_parser.get_value::("")?; let path = cmd_parser.get_value::("path")?; @@ -151,20 +148,31 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { nowait, } } else { - return Err(ErrorKind::FieldIsMissing("path", "socket-type chardev").into()); + return Err(anyhow!(ConfigError::FieldIsMissing( + "path", + "socket-type chardev" + ))); } } "file" => { if let Some(path) = path { ChardevType::File(path) } else { - return Err(ErrorKind::FieldIsMissing("path", "file-type chardev").into()); + return Err(anyhow!(ConfigError::FieldIsMissing( + "path", + "file-type chardev" + ))); } } - _ => return Err(ErrorKind::InvalidParam(backend, "chardev".to_string()).into()), + _ => { + return Err(anyhow!(ConfigError::InvalidParam( + backend, + "chardev".to_string() + ))) + } } } else { - return Err(ErrorKind::FieldIsMissing("backend", "chardev").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("backend", "chardev"))); }; Ok(ChardevConfig { @@ -181,19 +189,28 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result { let backend = args.backend; if backend.backend_type.as_str() != "socket" { - return Err(ErrorKind::InvalidParam("backend".to_string(), backend.backend_type).into()); + return Err(anyhow!(ConfigError::InvalidParam( + "backend".to_string(), + backend.backend_type + ))); } let data = backend.backend_data; if data.server { error!("Not support chardev socket as server now."); - return Err(ErrorKind::InvalidParam("backend".to_string(), "server".to_string()).into()); + return Err(anyhow!(ConfigError::InvalidParam( + "backend".to_string(), + "server".to_string() + ))); } let addr = data.addr; if addr.addr_type.as_str() != "unix" { error!("Just support \"unix\" addr type option now."); - return Err(ErrorKind::InvalidParam("backend".to_string(), "addr".to_string()).into()); + return Err(anyhow!(ConfigError::InvalidParam( + "backend".to_string(), + "addr".to_string() + ))); } Ok(ChardevConfig { @@ -245,13 +262,16 @@ pub fn parse_virtconsole(vm_config: &mut VmConfig, config_args: &str) -> Result< let chardev_name = if let Some(chardev) = cmd_parser.get_value::("chardev")? { chardev } else { - return Err(ErrorKind::FieldIsMissing("chardev", "virtconsole").into()); + return Err(anyhow!(ConfigError::FieldIsMissing( + "chardev", + "virtconsole" + ))); }; let id = if let Some(chardev_id) = cmd_parser.get_value::("id")? { chardev_id } else { - return Err(ErrorKind::FieldIsMissing("id", "virtconsole").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "virtconsole"))); }; if let Some(char_dev) = vm_config.chardev.remove(&chardev_name) { @@ -335,17 +355,16 @@ impl VmConfig { if parse_vec.len() == 2 { parse_vec[1] } else { - return Err(ErrorKind::InvalidParam( + return Err(anyhow!(ConfigError::InvalidParam( serial_config.to_string(), "serial".to_string(), - ) - .into()); + ))); } } _ => { let chardev_config = serial_config.to_string() + ",id=serial_chardev"; self.add_chardev(&chardev_config) - .chain_err(|| "Failed to add chardev")?; + .with_context(|| "Failed to add chardev")?; "serial_chardev" } }; @@ -368,20 +387,20 @@ pub struct VsockConfig { impl ConfigCheck for VsockConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("vsock id".to_string(), MAX_STRING_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "vsock id".to_string(), + MAX_STRING_LENGTH + ))); } if self.guest_cid < MIN_GUEST_CID || self.guest_cid >= MAX_GUEST_CID { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "Vsock guest-cid".to_string(), MIN_GUEST_CID, true, MAX_GUEST_CID, false, - ) - .into()); + ))); } Ok(()) @@ -403,13 +422,13 @@ pub fn parse_vsock(vsock_config: &str) -> Result { let id = if let Some(vsock_id) = cmd_parser.get_value::("id")? { vsock_id } else { - return Err(ErrorKind::FieldIsMissing("id", "vsock").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "vsock"))); }; let guest_cid = if let Some(cid) = cmd_parser.get_value::("guest-cid")? { cid } else { - return Err(ErrorKind::FieldIsMissing("guest-cid", "vsock").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("guest-cid", "vsock"))); }; let vhost_fd = cmd_parser.get_value::("vhostfd")?; @@ -431,11 +450,10 @@ pub struct VirtioSerialInfo { impl ConfigCheck for VirtioSerialInfo { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "virtio-serial id".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } Ok(()) diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index 08e558115..68b7959b0 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -10,8 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::Result; use super::{CmdParser, VmConfig}; +use anyhow::Result; impl VmConfig { pub fn add_device(&mut self, device_config: &str) -> Result<()> { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index b7c597da9..c8239c0fe 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -14,15 +14,12 @@ use std::fs::metadata; use std::os::linux::fs::MetadataExt; use std::path::Path; -use error_chain::bail; +use anyhow::{anyhow, bail, Result}; use log::error; use serde::{Deserialize, Serialize}; use util::aio::{AIO_IOURING, AIO_NATIVE}; -use super::{ - errors::{ErrorKind, Result}, - pci_args_check, -}; +use super::{error::ConfigError, pci_args_check}; use crate::config::{ get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, @@ -110,25 +107,30 @@ impl DriveConfig { if ((meta.st_mode() & libc::S_IFREG) != libc::S_IFREG) && ((meta.st_mode() & libc::S_IFBLK) != libc::S_IFBLK) { - return Err(ErrorKind::UnRegularFile("Drive File".to_string()).into()); + return Err(anyhow!(ConfigError::UnRegularFile( + "Drive File".to_string() + ))); } } Err(e) => { - error!("Failed to check the drive metadata: {}", e); - return Err(ErrorKind::UnRegularFile("Drive File".to_string()).into()); + error!("Failed to check the drive metadata: {:?}", e); + return Err(anyhow!(ConfigError::UnRegularFile( + "Drive File".to_string() + ))); } } if let Some(file_name) = blk.file_name() { if file_name.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "File name".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } } else { error!("Failed to check the drive file name"); - return Err(ErrorKind::UnRegularFile("Drive File".to_string()).into()); + return Err(anyhow!(ConfigError::UnRegularFile( + "Drive File".to_string() + ))); } Ok(()) } @@ -137,26 +139,25 @@ impl DriveConfig { impl ConfigCheck for DriveConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("Drive id".to_string(), MAX_STRING_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "Drive id".to_string(), + MAX_STRING_LENGTH + ))); } if self.path_on_host.len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "Drive device path".to_string(), MAX_PATH_LENGTH, - ) - .into()); + ))); } if self.iops.is_some() && self.iops.unwrap() > MAX_IOPS { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "iops of block device".to_string(), 0, true, MAX_IOPS, true, - ) - .into()); + ))); } Ok(()) } @@ -165,57 +166,51 @@ impl ConfigCheck for DriveConfig { impl ConfigCheck for BlkDevConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "drive device id".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } if self.path_on_host.len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "drive device path".to_string(), MAX_PATH_LENGTH, - ) - .into()); + ))); } if self.serial_num.is_some() && self.serial_num.as_ref().unwrap().len() > MAX_SERIAL_NUM { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "drive serial number".to_string(), MAX_SERIAL_NUM, - ) - .into()); + ))); } if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "iothread name".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } if self.iops.is_some() && self.iops.unwrap() > MAX_IOPS { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "iops of block device".to_string(), 0, true, MAX_IOPS, true, - ) - .into()); + ))); } if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u16 { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "number queues of block device".to_string(), 1, true, MAX_VIRTIO_QUEUE as u64, true, - ) - .into()); + ))); } Ok(()) @@ -234,13 +229,13 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { if let Some(id) = cmd_parser.get_value::("id")? { drive.id = id; } else { - return Err(ErrorKind::FieldIsMissing("id", "blk").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "blk"))); } if let Some(file) = cmd_parser.get_value::("file")? { drive.path_on_host = file; } else { - return Err(ErrorKind::FieldIsMissing("file", "blk").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("file", "blk"))); } if let Some(read_only) = cmd_parser.get_value::("readonly")? { @@ -287,7 +282,7 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result("drive")? { drive } else { - return Err(ErrorKind::FieldIsMissing("drive", "blk").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("drive", "blk"))); }; if let Some(iothread) = cmd_parser.get_value::("iothread")? { @@ -343,7 +338,10 @@ pub fn parse_vhost_user_blk_pci( if let Some(chardev) = cmd_parser.get_value::("chardev")? { blkdevcfg.chardev = Some(chardev); } else { - return Err(ErrorKind::FieldIsMissing("chardev", "vhost-user-blk-pci").into()); + return Err(anyhow!(ConfigError::FieldIsMissing( + "chardev", + "vhost-user-blk-pci" + ))); }; if let Some(id) = cmd_parser.get_value::("id")? { @@ -376,15 +374,14 @@ pub struct PFlashConfig { impl ConfigCheck for PFlashConfig { fn check(&self) -> Result<()> { if self.path_on_host.len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "drive device path".to_string(), MAX_PATH_LENGTH, - ) - .into()); + ))); } if self.unit >= MAX_UNIT_ID { - return Err(ErrorKind::UnitIdError(self.unit, MAX_UNIT_ID).into()); + return Err(anyhow!(ConfigError::UnitIdError(self.unit, MAX_UNIT_ID))); } Ok(()) } @@ -469,9 +466,10 @@ impl VmConfig { if self.pflashs.is_some() { for pf in self.pflashs.as_ref().unwrap() { if pf.unit == pflash.unit { - return Err( - ErrorKind::IdRepeat("pflash".to_string(), pf.unit.to_string()).into(), - ); + return Err(anyhow!(ConfigError::IdRepeat( + "pflash".to_string(), + pf.unit.to_string() + ))); } } self.pflashs.as_mut().unwrap().push(pflash); @@ -503,7 +501,7 @@ impl VmConfig { if let Some(drive_path) = cmd_parser.get_value::("file")? { pflash.path_on_host = drive_path; } else { - return Err(ErrorKind::FieldIsMissing("file", "pflash").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("file", "pflash"))); } if let Some(read_only) = cmd_parser.get_value::("readonly")? { @@ -513,7 +511,7 @@ impl VmConfig { if let Some(unit_id) = cmd_parser.get_value::("unit")? { pflash.unit = unit_id as usize; } else { - return Err(ErrorKind::FieldIsMissing("unit", "pflash").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("unit", "pflash"))); } pflash.check()?; diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs new file mode 100644 index 000000000..e823d991d --- /dev/null +++ b/machine_manager/src/config/error.rs @@ -0,0 +1,57 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("UtilError")] + UtilError { + #[from] + source: util::error::UtilError, + }, + #[error("JsonSerde")] + JsonSerde { + #[from] + source: serde_json::Error, + }, + #[error("Invalid json field \'{0}\'")] + InvalidJsonField(String), + #[error("Invalid parameter \'{0}\' for \'{1}\'")] + InvalidParam(String, String), + #[error("Unable to parse \'{0}\' for \'{1}\'")] + ConvertValueFailed(String, String), + #[error("Input {0} string's length must be no more than {1}.")] + StringLengthTooLong(String, usize), + #[error("Input field \'{0}\' in {1} is offered more than once.")] + FieldRepeat(String, String), + #[error("Input id \'{0}\' for {1} repeat.")] + IdRepeat(String, String), + #[error("Integer overflow occurred during parse {0}!")] + IntegerOverflow(String), + #[error("Unknown device type: {0}!")] + UnknownDeviceType(String), + #[error("\'{0}\' is missing for \'{1}\' device.")] + FieldIsMissing(&'static str, &'static str), + #[error("{0} must >{1} {2} and <{3} {4}.")] + IllegalValue(String, u64, bool, u64, bool), + #[error("Mac address is illegal.")] + MacFormatError, + #[error("Unknown vhost type.")] + UnknownVhostType, + #[error("{0} is not a regular File.")] + UnRegularFile(String), + #[error("Input value {0} is unaligned with {1} for {2}.")] + Unaligned(String, u64, u64), + #[error("PFlash unit id given {0} should not be more than {1}")] + UnitIdError(usize, usize), +} diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index bdc3c0e7f..d8f6977a5 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -10,12 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; +use super::error::ConfigError; use crate::config::{ pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_TAG_LENGTH, }; -use error_chain::bail; +use anyhow::{anyhow, bail, Result}; /// Config struct for `fs`. /// Contains fs device's attr. @@ -42,27 +42,24 @@ impl Default for FsConfig { impl ConfigCheck for FsConfig { fn check(&self) -> Result<()> { if self.tag.len() >= MAX_TAG_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "fs device tag".to_string(), MAX_TAG_LENGTH - 1, - ) - .into()); + ))); } if self.id.len() >= MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "fs device id".to_string(), MAX_STRING_LENGTH - 1, - ) - .into()); + ))); } if self.sock.len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "fs sock path".to_string(), MAX_PATH_LENGTH, - ) - .into()); + ))); } Ok(()) @@ -85,13 +82,13 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { if let Some(tag) = cmd_parser.get_value::("tag")? { fs_cfg.tag = tag; } else { - return Err(ErrorKind::FieldIsMissing("tag", "virtio-fs").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("tag", "virtio-fs"))); } if let Some(id) = cmd_parser.get_value::("id")? { fs_cfg.id = id; } else { - return Err(ErrorKind::FieldIsMissing("id", "virtio-fs").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "virtio-fs"))); } if let Some(name) = cmd_parser.get_value::("chardev")? { @@ -108,7 +105,7 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { bail!("Chardev {:?} not found or is in use", &name); } } else { - return Err(ErrorKind::FieldIsMissing("chardev", "virtio-fs").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("chardev", "virtio-fs"))); } fs_cfg.check()?; diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index dc1897cea..77b3d80ce 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -10,13 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::warn; - -use super::{ - errors::{ErrorKind, Result}, - M, -}; +use super::{error::ConfigError, M}; use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; +use anyhow::{anyhow, Result}; +use log::warn; /// The maximum number of scanouts. pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; @@ -49,18 +46,20 @@ impl Default for GpuConfig { impl ConfigCheck for GpuConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "id".to_string(), + MAX_STRING_LENGTH + ))); } if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "max_outputs".to_string(), 0, false, VIRTIO_GPU_MAX_SCANOUTS as u64, true, - ) - .into()); + ))); } if self.max_hostmem < VIRTIO_GPU_MAX_HOSTMEM { diff --git a/machine_manager/src/config/incoming.rs b/machine_manager/src/config/incoming.rs index eb1d85ad1..3fb91656d 100644 --- a/machine_manager/src/config/incoming.rs +++ b/machine_manager/src/config/incoming.rs @@ -10,11 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use super::VmConfig; +use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; -use super::{errors::Result, VmConfig}; - #[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)] pub enum MigrateMode { File, diff --git a/machine_manager/src/config/iothread.rs b/machine_manager/src/config/iothread.rs index db3c7966d..cc53992fd 100644 --- a/machine_manager/src/config/iothread.rs +++ b/machine_manager/src/config/iothread.rs @@ -12,8 +12,9 @@ use serde::{Deserialize, Serialize}; -use super::errors::{ErrorKind, Result}; +use super::error::ConfigError; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH}; +use anyhow::{anyhow, Result}; const MAX_IOTHREAD_NUM: usize = 8; @@ -26,11 +27,10 @@ pub struct IothreadConfig { impl ConfigCheck for IothreadConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "iothread id".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } Ok(()) @@ -52,21 +52,21 @@ impl VmConfig { if self.iothreads.is_some() { if self.iothreads.as_ref().unwrap().len() >= MAX_IOTHREAD_NUM { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "Iothread number".to_string(), 0, true, MAX_IOTHREAD_NUM as u64, true, - ) - .into()); + ))); } for t in self.iothreads.as_ref().unwrap() { if t.id == iothread.id { - return Err( - ErrorKind::IdRepeat("iothread".to_string(), t.id.to_string()).into(), - ); + return Err(anyhow!(ConfigError::IdRepeat( + "iothread".to_string(), + t.id.to_string() + ))); } } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 5a9c015e9..19639027c 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -12,10 +12,10 @@ use std::str::FromStr; -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use serde::{Deserialize, Serialize}; -use super::errors::{ErrorKind, Result, ResultExt}; +use super::error::ConfigError; use crate::config::{ CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES, MAX_STRING_LENGTH, }; @@ -204,13 +204,13 @@ impl VmConfig { } if let Some(mach_type) = cmd_parser .get_value::("") - .chain_err(|| "Unrecognized machine type")? + .with_context(|| "Unrecognized machine type")? { self.machine_config.mach_type = mach_type; } if let Some(mach_type) = cmd_parser .get_value::("type") - .chain_err(|| "Unrecognized machine type")? + .with_context(|| "Unrecognized machine type")? { self.machine_config.mach_type = mach_type; } @@ -236,7 +236,7 @@ impl VmConfig { } else if let Some(mem_size) = cmd_parser.get_value::("size")? { memory_unit_conversion(&mem_size)? } else { - return Err(ErrorKind::FieldIsMissing("size", "memory").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("size", "memory"))); }; self.machine_config.mem_config.mem_size = mem; @@ -263,13 +263,17 @@ impl VmConfig { cpu } else if let Some(cpu) = cmd_parser.get_value::("cpus")? { if cpu == 0 { - return Err( - ErrorKind::IllegalValue("cpu".to_string(), 1, true, MAX_NR_CPUS, true).into(), - ); + return Err(anyhow!(ConfigError::IllegalValue( + "cpu".to_string(), + 1, + true, + MAX_NR_CPUS, + true + ))); } cpu } else { - return Err(ErrorKind::FieldIsMissing("cpus", "smp").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("cpus", "smp"))); }; let sockets = smp_read_and_check(&cmd_parser, "sockets", 0)?; @@ -289,36 +293,33 @@ impl VmConfig { // limit cpu count if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&cpu) { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "CPU number".to_string(), MIN_NR_CPUS, true, MAX_NR_CPUS, true, - ) - .into()); + ))); } if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&max_cpus) { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "MAX CPU number".to_string(), MIN_NR_CPUS, true, MAX_NR_CPUS, true, - ) - .into()); + ))); } if max_cpus < cpu { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "maxcpus".to_string(), cpu as u64, true, MAX_NR_CPUS, true, - ) - .into()); + ))); } if sockets * dies * clusters * cores * threads != max_cpus { @@ -350,13 +351,17 @@ impl VmConfig { fn get_mem_zone_id(&self, cmd_parser: &CmdParser) -> Result { if let Some(id) = cmd_parser.get_value::("id")? { if id.len() > MAX_STRING_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "id".to_string(), + MAX_STRING_LENGTH + ))); } Ok(id) } else { - Err(ErrorKind::FieldIsMissing("id", "memory-backend-ram").into()) + Err(anyhow!(ConfigError::FieldIsMissing( + "id", + "memory-backend-ram" + ))) } } @@ -365,7 +370,10 @@ impl VmConfig { let size = memory_unit_conversion(&mem)?; Ok(size) } else { - Err(ErrorKind::FieldIsMissing("size", "memory-backend-ram").into()) + Err(anyhow!(ConfigError::FieldIsMissing( + "size", + "memory-backend-ram" + ))) } } @@ -373,35 +381,46 @@ impl VmConfig { if let Some(mut host_nodes) = cmd_parser .get_value::("host-nodes") .map_err(|_| { - ErrorKind::ConvertValueFailed(String::from("u32"), "host-nodes".to_string()) + anyhow!(ConfigError::ConvertValueFailed( + String::from("u32"), + "host-nodes".to_string() + )) })? .map(|v| v.0.iter().map(|e| *e as u32).collect::>()) { host_nodes.sort_unstable(); if host_nodes[host_nodes.len() - 1] >= MAX_NODES { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "host_nodes".to_string(), 0, true, MAX_NODES as u64, false, - ) - .into()); + ))); } Ok(Some(host_nodes)) } else { - Err(ErrorKind::FieldIsMissing("host-nodes", "memory-backend-ram").into()) + Err(anyhow!(ConfigError::FieldIsMissing( + "host-nodes", + "memory-backend-ram" + ))) } } fn get_mem_zone_policy(&self, cmd_parser: &CmdParser) -> Result { if let Some(policy) = cmd_parser.get_value::("policy")? { if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { - return Err(ErrorKind::InvalidParam("policy".to_string(), policy).into()); + return Err(anyhow!(ConfigError::InvalidParam( + "policy".to_string(), + policy + ))); } Ok(policy) } else { - Err(ErrorKind::FieldIsMissing("policy", "memory-backend-ram").into()) + Err(anyhow!(ConfigError::FieldIsMissing( + "policy", + "memory-backend-ram" + ))) } } @@ -445,9 +464,13 @@ impl VmConfig { fn smp_read_and_check(cmd_parser: &CmdParser, name: &str, default_val: u64) -> Result { if let Some(values) = cmd_parser.get_value::(name)? { if values == 0 { - return Err( - ErrorKind::IllegalValue(name.to_string(), 1, true, u8::MAX as u64, false).into(), - ); + return Err(anyhow!(ConfigError::IllegalValue( + name.to_string(), + 1, + true, + u8::MAX as u64, + false + ))); } Ok(values) } else { @@ -509,7 +532,10 @@ fn memory_unit_conversion(origin_value: &str) -> Result { value .parse::() .map_err(|_| { - ErrorKind::ConvertValueFailed(origin_value.to_string(), String::from("u64")) + anyhow!(ConfigError::ConvertValueFailed( + origin_value.to_string(), + String::from("u64") + )) })? .checked_mul(M), ) @@ -522,13 +548,19 @@ fn memory_unit_conversion(origin_value: &str) -> Result { value .parse::() .map_err(|_| { - ErrorKind::ConvertValueFailed(origin_value.to_string(), String::from("u64")) + anyhow!(ConfigError::ConvertValueFailed( + origin_value.to_string(), + String::from("u64") + )) })? .checked_mul(G), ) } else { let size = origin_value.parse::().map_err(|_| { - ErrorKind::ConvertValueFailed(origin_value.to_string(), String::from("u64")) + anyhow!(ConfigError::ConvertValueFailed( + origin_value.to_string(), + String::from("u64") + )) })?; let memory_size = size.checked_mul(M); @@ -541,7 +573,7 @@ fn get_inner(outer: Option) -> Result { if let Some(x) = outer { Ok(x) } else { - Err(ErrorKind::IntegerOverflow("-m".to_string()).into()) + Err(anyhow!(ConfigError::IntegerOverflow("-m".to_string()))) } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index de7860f07..e078764ef 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -10,80 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - } - foreign_links { - JsonSerde(serde_json::Error); - } - errors { - InvalidJsonField(field: String) { - display("Invalid json field \'{}\'", field) - } - InvalidParam(param: String, name: String) { - display("Invalid parameter \'{}\' for \'{}\'", param, name) - } - ConvertValueFailed(param: String, value: String) { - display("Unable to parse \'{}\' for \'{}\'", value, param) - } - StringLengthTooLong(t: String, len: usize) { - display("Input {} string's length must be no more than {}.", t, len) - } - FieldRepeat(param: String, field: String) { - display("Input field \'{}\' in {} is offered more than once.", field, param) - } - IdRepeat(param: String, id: String) { - display("Input id \'{}\' for {} repeat.", id, param) - } - IntegerOverflow(item: String) { - display("Integer overflow occurred during parse {}!", item) - } - UnknownDeviceType(item: String) { - display("Unknown device type: {}!", item) - } - FieldIsMissing(field: &'static str, device: &'static str) { - display("\'{}\' is missing for \'{}\' device.", field, device) - } - IllegalValue(name: String, min: u64, min_include: bool, max: u64, max_include: bool) { - display( - "{} must >{} {} and <{} {}.", - name, - if *min_include {"="} else {""}, - min, - if *max_include {"="} else {""}, - max - ) - } - MacFormatError { - display("Mac address is illegal.") - } - UnknownVhostType { - display("Unknown vhost type.") - } - UnRegularFile(t: String) { - display("{} is not a regular File.", t) - } - Unaligned(param: String, value: u64, align: u64) { - display("Input value {} is unaligned with {} for {}.", value, align, param) - } - UnitIdError(id: usize, max: usize){ - description("Check unit id of pflash device.") - display("PFlash unit id given {} should not be more than {}", id, max) - } - } - } -} - -pub use self::errors::{ErrorKind, Result, ResultExt}; pub use balloon::*; pub use boot_source::*; pub use chardev::*; pub use devices::*; pub use drive::*; +pub use error::ConfigError; pub use fs::*; pub use gpu::*; pub use incoming::*; @@ -102,6 +34,7 @@ mod boot_source; mod chardev; mod devices; mod drive; +pub mod error; mod fs; mod gpu; mod incoming; @@ -121,7 +54,7 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use log::error; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; @@ -150,12 +83,15 @@ fn parse_rng_obj(object_args: &str) -> Result { let id = if let Some(obj_id) = cmd_params.get_value::("id")? { obj_id } else { - return Err(ErrorKind::FieldIsMissing("id", "rng-object").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "rng-object"))); }; let filename = if let Some(name) = cmd_params.get_value::("filename")? { name } else { - return Err(ErrorKind::FieldIsMissing("filename", "rng-object").into()); + return Err(anyhow!(ConfigError::FieldIsMissing( + "filename", + "rng-object" + ))); }; let rng_obj_cfg = RngObjConfig { id, filename }; @@ -191,11 +127,10 @@ impl VmConfig { self.machine_config.check()?; if self.guest_name.len() > MAX_STRING_LENGTH { - return Err(self::errors::ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "name".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } if self.boot_source.kernel_file.is_none() && self.machine_config.mach_type == MachineType::MicroVm @@ -256,7 +191,7 @@ impl VmConfig { match device_type.as_str() { "iothread" => { self.add_iothread(object_args) - .chain_err(|| "Failed to add iothread")?; + .with_context(|| "Failed to add iothread")?; } "rng-random" => { let rng_cfg = parse_rng_obj(object_args)?; @@ -316,7 +251,7 @@ impl VmConfig { #[cfg(target_arch = "aarch64")] impl device_tree::CompileFDT for VmConfig { - fn generate_fdt_node(&self, _fdt: &mut FdtBuilder) -> util::errors::Result<()> { + fn generate_fdt_node(&self, _fdt: &mut FdtBuilder) -> util::Result<()> { Ok(()) } } @@ -380,14 +315,18 @@ impl CmdParser { /// * `cmd_param`: The whole cmdline parameter string. pub fn parse(&mut self, cmd_param: &str) -> Result<()> { if cmd_param.starts_with(',') || cmd_param.ends_with(',') { - return Err(ErrorKind::InvalidParam(cmd_param.to_string(), self.name.clone()).into()); + return Err(anyhow!(ConfigError::InvalidParam( + cmd_param.to_string(), + self.name.clone() + ))); } let param_items = cmd_param.split(',').collect::>(); for (i, param_item) in param_items.iter().enumerate() { if param_item.starts_with('=') || param_item.ends_with('=') { - return Err( - ErrorKind::InvalidParam(param_item.to_string(), self.name.clone()).into(), - ); + return Err(anyhow!(ConfigError::InvalidParam( + param_item.to_string(), + self.name.clone() + ))); } let param = param_item.splitn(2, '=').collect::>(); let (param_key, param_value) = match param.len() { @@ -400,9 +339,10 @@ impl CmdParser { } 2 => (param[0], param[1]), _ => { - return Err( - ErrorKind::InvalidParam(param_item.to_string(), self.name.clone()).into(), - ); + return Err(anyhow!(ConfigError::InvalidParam( + param_item.to_string(), + self.name.clone() + ))); } }; @@ -411,14 +351,16 @@ impl CmdParser { if field_value.is_none() { *field_value = Some(String::from(param_value)); } else { - return Err( - ErrorKind::FieldRepeat(self.name.clone(), param_key.to_string()).into(), - ); + return Err(anyhow!(ConfigError::FieldRepeat( + self.name.clone(), + param_key.to_string() + ))); } } else { - return Err( - ErrorKind::InvalidParam(param[0].to_string(), self.name.clone()).into(), - ); + return Err(anyhow!(ConfigError::InvalidParam( + param[0].to_string(), + self.name.clone() + ))); } } @@ -432,7 +374,10 @@ impl CmdParser { /// * `cmd_param`: The whole cmdline parameter string. fn get_parameters(&mut self, cmd_param: &str) -> Result<()> { if cmd_param.starts_with(',') || cmd_param.ends_with(',') { - return Err(ErrorKind::InvalidParam(cmd_param.to_string(), self.name.clone()).into()); + return Err(anyhow!(ConfigError::InvalidParam( + cmd_param.to_string(), + self.name.clone() + ))); } let param_items = cmd_param.split(',').collect::>(); for param_item in param_items { @@ -441,9 +386,10 @@ impl CmdParser { 1 => ("", param[0]), 2 => (param[0], param[1]), _ => { - return Err( - ErrorKind::InvalidParam(param_item.to_string(), self.name.clone()).into(), - ); + return Err(anyhow!(ConfigError::InvalidParam( + param_item.to_string(), + self.name.clone() + ))); } }; @@ -452,9 +398,10 @@ impl CmdParser { if field_value.is_none() { *field_value = Some(String::from(param_value)); } else { - return Err( - ErrorKind::FieldRepeat(self.name.clone(), param_key.to_string()).into(), - ); + return Err(anyhow!(ConfigError::FieldRepeat( + self.name.clone(), + param_key.to_string() + ))); } } } @@ -478,7 +425,10 @@ impl CmdParser { if let Some(raw_value) = value { Ok(Some(raw_value.parse().map_err(|_| { - ErrorKind::ConvertValueFailed(field_msg.to_string(), raw_value.clone()) + anyhow!(ConfigError::ConvertValueFailed( + field_msg.to_string(), + raw_value.clone() + )) })?)) } else { Ok(None) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index d47739a30..63538c9f5 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -10,16 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::{anyhow, bail, Result}; use serde::{Deserialize, Serialize}; -use super::{ - errors::{ErrorKind, Result}, - pci_args_check, -}; +use super::{error::ConfigError, pci_args_check}; +use crate::config::get_chardev_socket_path; use crate::config::{ - get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, - MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::{qmp_schema, QmpChannel}; @@ -53,30 +50,33 @@ impl Default for NetDevcfg { impl ConfigCheck for NetDevcfg { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "id".to_string(), + MAX_STRING_LENGTH + ))); } if self.ifname.len() > MAX_STRING_LENGTH { - return Err( - ErrorKind::StringLengthTooLong(self.ifname.clone(), MAX_STRING_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + self.ifname.clone(), + MAX_STRING_LENGTH + ))); } if let Some(vhost_type) = self.vhost_type.as_ref() { if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" { - return Err(ErrorKind::UnknownVhostType.into()); + return Err(anyhow!(ConfigError::UnknownVhostType)); } } if !is_netdev_queues_valid(self.queues) { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "number queues of net device".to_string(), 1, true, MAX_VIRTIO_QUEUE as u64 / 2, true, - ) - .into()); + ))); } Ok(()) @@ -126,34 +126,36 @@ impl Default for NetworkInterfaceConfig { impl ConfigCheck for NetworkInterfaceConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "id".to_string(), + MAX_STRING_LENGTH + ))); } if self.host_dev_name.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( self.host_dev_name.clone(), MAX_STRING_LENGTH, - ) - .into()); + ))); } if self.mac.is_some() && !check_mac_address(self.mac.as_ref().unwrap()) { - return Err(ErrorKind::MacFormatError.into()); + return Err(anyhow!(ConfigError::MacFormatError)); } if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "iothread name".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } if self.socket_path.is_some() && self.socket_path.as_ref().unwrap().len() > MAX_PATH_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("socket path".to_string(), MAX_PATH_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "socket path".to_string(), + MAX_PATH_LENGTH + ))); } Ok(()) @@ -164,7 +166,11 @@ fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result>> { if let Some(fds) = cmd_parser.get_value::(name)? { let mut raw_fds = Vec::new(); for fd in fds.split(':').collect::>().iter() { - raw_fds.push((*fd).parse::().map_err(|_| "Failed to parse fds")?); + raw_fds.push( + (*fd) + .parse::() + .map_err(|_| anyhow!("Failed to parse fds"))?, + ); } Ok(Some(raw_fds)) } else { @@ -185,7 +191,7 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { if let Some(net_id) = cmd_parser.get_value::("id")? { net.id = net_id; } else { - return Err(ErrorKind::FieldIsMissing("id", "netdev").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "netdev"))); } if let Some(ifname) = cmd_parser.get_value::("ifname")? { net.ifname = ifname; @@ -194,14 +200,13 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { let queues = queue_pairs * 2; if !is_netdev_queues_valid(queues) { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "number queues of net device".to_string(), 1, true, MAX_VIRTIO_QUEUE as u64 / 2, true, - ) - .into()); + ))); } net.queues = queues; @@ -274,7 +279,7 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result("netdev")? { devname } else { - return Err(ErrorKind::FieldIsMissing("netdev", "net").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("netdev", "net"))); }; let netid = if let Some(id) = cmd_parser.get_value::("id")? { id diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index fb0ceda16..485992d80 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -13,9 +13,9 @@ use std::cmp::max; use std::collections::{BTreeMap, HashSet}; -use error_chain::bail; +use anyhow::{anyhow, bail, Result}; -use super::errors::{ErrorKind, Result}; +use super::error::ConfigError; use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; const MIN_NUMA_DISTANCE: u8 = 10; @@ -125,33 +125,37 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { let mut config: NumaConfig = NumaConfig::default(); if let Some(node_id) = cmd_parser.get_value::("nodeid")? { if node_id >= MAX_NODES { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "nodeid".to_string(), 0, true, MAX_NODES as u64, false, - ) - .into()); + ))); } config.numa_id = node_id; } else { - return Err(ErrorKind::FieldIsMissing("nodeid", "numa").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("nodeid", "numa"))); } if let Some(mut cpus) = cmd_parser .get_value::("cpus") - .map_err(|_| ErrorKind::ConvertValueFailed(String::from("u8"), "cpus".to_string()))? + .map_err(|_| { + anyhow!(ConfigError::ConvertValueFailed( + String::from("u8"), + "cpus".to_string() + )) + })? .map(|v| v.0.iter().map(|e| *e as u8).collect::>()) { cpus.sort_unstable(); config.cpus = cpus; } else { - return Err(ErrorKind::FieldIsMissing("cpus", "numa").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("cpus", "numa"))); } if let Some(mem_dev) = cmd_parser.get_value::("memdev")? { config.mem_dev = mem_dev; } else { - return Err(ErrorKind::FieldIsMissing("memdev", "numa").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("memdev", "numa"))); } Ok(config) @@ -170,33 +174,31 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { let mut dist: NumaDistance = NumaDistance::default(); let numa_id = if let Some(src) = cmd_parser.get_value::("src")? { if src >= MAX_NODES { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "src".to_string(), 0, true, MAX_NODES as u64, false, - ) - .into()); + ))); } src } else { - return Err(ErrorKind::FieldIsMissing("src", "numa").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("src", "numa"))); }; if let Some(dst) = cmd_parser.get_value::("dst")? { if dst >= MAX_NODES { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "dst".to_string(), 0, true, MAX_NODES as u64, false, - ) - .into()); + ))); } dist.destination = dst; } else { - return Err(ErrorKind::FieldIsMissing("dst", "numa").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("dst", "numa"))); } if let Some(val) = cmd_parser.get_value::("val")? { if val < MIN_NUMA_DISTANCE { @@ -214,7 +216,7 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { dist.distance = val; } else { - return Err(ErrorKind::FieldIsMissing("val", "numa").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("val", "numa"))); } Ok((numa_id, dist)) diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 82a16e1fb..ffdd5c14d 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use serde::{Deserialize, Serialize}; -use super::errors::{ErrorKind, Result, ResultExt}; +use super::error::ConfigError; use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::ExBool; @@ -53,11 +53,10 @@ pub struct RootPortConfig { impl ConfigCheck for RootPortConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "root_port id".to_string(), MAX_STRING_LENGTH, - ) - .into()); + ))); } Ok(()) @@ -85,7 +84,7 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { let slot = addr_vec.get(0).unwrap(); let without_prefix = slot.trim_start_matches("0x"); let slot = u8::from_str_radix(without_prefix, 16) - .chain_err(|| format!("Invalid slot num: {}", slot))?; + .with_context(|| format!("Invalid slot num: {}", slot))?; if slot > 31 { bail!("Invalid slot num: {}", slot); } @@ -93,7 +92,7 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { let function = addr_vec.get(1).unwrap(); let without_prefix = function.trim_start_matches("0x"); u8::from_str_radix(without_prefix, 16) - .chain_err(|| format!("Invalid function num: {}", function))? + .with_context(|| format!("Invalid function num: {}", function))? } else { 0 }; @@ -115,7 +114,7 @@ pub fn get_pci_bdf(pci_cfg: &str) -> Result { bail!("Bus not specified for pci device"); } if let Some(addr) = cmd_parser.get_value::("addr")? { - pci_bdf.addr = get_pci_df(&addr).chain_err(|| "Failed to get addr")?; + pci_bdf.addr = get_pci_df(&addr).with_context(|| "Failed to get addr")?; } else { bail!("No addr found for pci device"); } @@ -129,7 +128,7 @@ pub fn get_multi_function(pci_cfg: &str) -> Result { if let Some(multi_func) = cmd_parser .get_value::("multifunction") - .chain_err(|| "Failed to get multifunction parameter, please set on or off (default).")? + .with_context(|| "Failed to get multifunction parameter, please set on or off (default).")? { return Ok(multi_func.inner); } @@ -154,14 +153,14 @@ pub fn parse_root_port(rootport_cfg: &str) -> Result { let without_prefix = port.trim_start_matches("0x"); root_port.port = u8::from_str_radix(without_prefix, 16).unwrap(); } else { - return Err(ErrorKind::FieldIsMissing("port", "rootport").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("port", "rootport"))); } let _ = cmd_parser.get_value::("chassis")?; if let Some(id) = cmd_parser.get_value::("id")? { root_port.id = id; } else { - return Err(ErrorKind::FieldIsMissing("id", "rootport").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("id", "rootport"))); } root_port.multifunction = if let Some(multi_func) = cmd_parser.get_value::("multifunction")? { diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 7f46e6db3..49aedfd5a 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::{anyhow, bail, Result}; use serde::{Deserialize, Serialize}; -use super::errors::{ErrorKind, Result}; +use super::error::ConfigError; use super::{pci_args_check, ObjConfig}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; @@ -37,29 +37,28 @@ pub struct RngConfig { impl ConfigCheck for RngConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_PATH_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("rng id".to_string(), MAX_PATH_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "rng id".to_string(), + MAX_PATH_LENGTH + ))); } if self.random_file.len() > MAX_PATH_LENGTH { - return Err(ErrorKind::StringLengthTooLong( + return Err(anyhow!(ConfigError::StringLengthTooLong( "rng random file".to_string(), MAX_PATH_LENGTH, - ) - .into()); + ))); } if let Some(bytes_per_sec) = self.bytes_per_sec { if !(MIN_BYTES_PER_SEC..=MAX_BYTES_PER_SEC).contains(&bytes_per_sec) { - return Err(ErrorKind::IllegalValue( + return Err(anyhow!(ConfigError::IllegalValue( "The bytes per second of rng device".to_string(), MIN_BYTES_PER_SEC, true, MAX_BYTES_PER_SEC, true, - ) - .into()); + ))); } } @@ -85,7 +84,7 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result("rng")? { rng_id } else { - return Err(ErrorKind::FieldIsMissing("rng", "rng").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("rng", "rng"))); }; rng_cfg.id = if let Some(rng_id) = cmd_parser.get_value::("id")? { diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 543a449e5..5d0d8ce0f 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -10,9 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; - -use error_chain::bail; +use super::error::ConfigError; +use anyhow::{anyhow, bail, Result}; use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; @@ -113,7 +112,10 @@ pub fn parse_usb_tablet(conf: &str) -> Result { fn check_id(id: &str) -> Result<()> { if id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "id".to_string(), + MAX_STRING_LENGTH + ))); } Ok(()) } diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs index 757d5ac37..5004a0aa0 100644 --- a/machine_manager/src/config/vfio.rs +++ b/machine_manager/src/config/vfio.rs @@ -10,9 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result}; +use super::error::ConfigError; use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; - +use anyhow::{anyhow, Result}; #[derive(Default, Debug)] pub struct VfioConfig { pub sysfsdev: String, @@ -23,13 +23,17 @@ pub struct VfioConfig { impl ConfigCheck for VfioConfig { fn check(&self) -> Result<()> { if self.host.len() > MAX_STRING_LENGTH { - return Err( - ErrorKind::StringLengthTooLong("host".to_string(), MAX_STRING_LENGTH).into(), - ); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "host".to_string(), + MAX_STRING_LENGTH + ))); } if self.id.len() > MAX_STRING_LENGTH { - return Err(ErrorKind::StringLengthTooLong("id".to_string(), MAX_STRING_LENGTH).into()); + return Err(anyhow!(ConfigError::StringLengthTooLong( + "id".to_string(), + MAX_STRING_LENGTH + ))); } Ok(()) @@ -58,13 +62,17 @@ pub fn parse_vfio(vfio_config: &str) -> Result { } if vfio.host.is_empty() && vfio.sysfsdev.is_empty() { - return Err(ErrorKind::FieldIsMissing("host nor sysfsdev", "vfio").into()); + return Err(anyhow!(ConfigError::FieldIsMissing( + "host nor sysfsdev", + "vfio" + ))); } if !vfio.host.is_empty() && !vfio.sysfsdev.is_empty() { - return Err( - ErrorKind::InvalidParam("host and sysfsdev".to_string(), "vfio".to_string()).into(), - ); + return Err(anyhow!(ConfigError::InvalidParam( + "host and sysfsdev".to_string(), + "vfio".to_string() + ))); } if let Some(id) = cmd_parser.get_value::("id")? { diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index 57102d938..3a065b910 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -12,8 +12,9 @@ use serde::{Deserialize, Serialize}; -use super::errors::{ErrorKind, Result}; +use crate::config::ConfigError; use crate::config::{CmdParser, VmConfig}; +use anyhow::{anyhow, Result}; use std::net::Ipv4Addr; /// Configuration of vnc. @@ -52,7 +53,7 @@ impl VmConfig { return Err(e); } } else { - return Err(ErrorKind::FieldIsMissing("ip", "port").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("ip", "port"))); } // VNC Security Type. @@ -77,17 +78,20 @@ impl VmConfig { fn parse_port(vnc_config: &mut VncConfig, addr: String) -> Result<()> { let v: Vec<&str> = addr.split(':').collect(); if v.len() != 2 { - return Err(ErrorKind::FieldIsMissing("ip", "port").into()); + return Err(anyhow!(ConfigError::FieldIsMissing("ip", "port"))); } let ip = v[0] .parse::() - .map_err(|_| "Invalid Ip param for vnc!")?; + .map_err(|_| anyhow!("Invalid Ip param for vnc!"))?; let base_port = v[1] .parse::() - .map_err(|_| "Invalid Port param for vnc!")?; + .map_err(|_| anyhow!("Invalid Port param for vnc!"))?; // Prevent the base_port out of bounds. if !(0..=VNC_MAX_PORT_NUM - VNC_PORT_OFFSET).contains(&base_port) { - return Err(ErrorKind::InvalidParam(base_port.to_string(), "port".to_string()).into()); + return Err(anyhow!(ConfigError::InvalidParam( + base_port.to_string(), + "port".to_string() + ))); } vnc_config.ip = ip.to_string(); vnc_config.port = ((base_port + VNC_PORT_OFFSET) as u16).to_string(); diff --git a/machine_manager/src/error.rs b/machine_manager/src/error.rs new file mode 100644 index 000000000..01bf61106 --- /dev/null +++ b/machine_manager/src/error.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MachineManagerError { + #[error("ConfigParser")] + ConfigParser { + #[from] + source: crate::config::error::ConfigError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Json")] + Json { + #[from] + source: serde_json::Error, + }, +} diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index bc4e177e7..e4c077132 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -18,7 +18,7 @@ use super::config::IothreadConfig; use crate::machine::IOTHREADS; use crate::qmp::qmp_schema::IothreadInfo; -use error_chain::bail; +use anyhow::bail; use log::info; use util::loop_context::{EventLoopContext, EventLoopManager, EventNotifier}; @@ -43,7 +43,7 @@ impl EventLoop { /// # Arguments /// /// * `iothreads` - refer to `-iothread` params - pub fn object_init(iothreads: &Option>) -> util::errors::Result<()> { + pub fn object_init(iothreads: &Option>) -> util::Result<()> { let mut io_threads = HashMap::new(); if let Some(thrs) = iothreads { for thr in thrs { @@ -122,10 +122,7 @@ impl EventLoop { /// /// * `notifiers` - The wrapper of events will be handled in the event loop specified by name. /// * `name` - specify which event loop to manage - pub fn update_event( - notifiers: Vec, - name: Option<&String>, - ) -> util::errors::Result<()> { + pub fn update_event(notifiers: Vec, name: Option<&String>) -> util::Result<()> { if let Some(ctx) = Self::get_ctx(name) { ctx.update_events(notifiers) } else { @@ -139,7 +136,7 @@ impl EventLoop { /// /// Once run main loop, `epoll` in `MainLoopContext` will execute /// `epoll_wait()` function to wait for events. - pub fn loop_run() -> util::errors::Result<()> { + pub fn loop_run() -> util::Result<()> { unsafe { if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { loop { diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index f76598a08..acce9e88a 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -23,23 +23,11 @@ pub mod cmdline; pub mod config; +pub mod error; pub mod event_loop; pub mod machine; pub mod qmp; pub mod signal_handler; pub mod socket; pub mod temp_cleaner; - -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - ConfigParser(crate::config::errors::Error, crate::config::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - Json(serde_json::Error); - } - } -} +pub use error::MachineManagerError; diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 2c22fe71f..eb03e8cf0 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -48,10 +48,8 @@ use self::qmp_schema::{self as schema, QmpCommand}; use crate::event_loop::EventLoop; use crate::machine::MachineExternalInterface; use crate::socket::SocketRWHandler; -use crate::{ - errors::{Result, ResultExt}, - temp_cleaner::TempCleaner, -}; +use crate::temp_cleaner::TempCleaner; +use anyhow::{Context, Result}; static mut QMP_CHANNEL: Option> = None; @@ -369,7 +367,7 @@ pub fn handle_qmp( .send_str(&serde_json::to_string(&Response::create_error_response( err_resp, None, ))?) - .chain_err(|| "Failed to send message to qmp client.")?; + .with_context(|| "Failed to send message to qmp client.")?; return Ok(()); } diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 169b1a302..7da0eba89 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -16,13 +16,12 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::sync::{Arc, Mutex, RwLock}; -use error_chain::bail; +use anyhow::{bail, Result}; use log::{error, info}; use util::leak_bucket::LeakBucket; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use vmm_sys_util::epoll::EventSet; -use super::errors::Result; use crate::machine::MachineExternalInterface; use crate::{ qmp::qmp_schema::QmpEvent, @@ -202,7 +201,7 @@ impl Socket { performer, &mut shared_leak_bucket.lock().unwrap(), ) { - error!("{}", e); + error!("{:?}", e); } } if event & EventSet::HANG_UP == EventSet::HANG_UP { diff --git a/migration/Cargo.toml b/migration/Cargo.toml index a81486e8c..6fc6401b0 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -5,13 +5,14 @@ authors = ["Huawei StratoVirt Team"] edition = "2021" [dependencies] -error-chain = "0.12.4" kvm-ioctls = ">=0.11.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" once_cell = "1.13.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } log = "0.4" +thiserror = "1.0" +anyhow = "1.0" util = {path = "../util"} hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/migration/src/error.rs b/migration/src/error.rs new file mode 100644 index 000000000..d7720cc7a --- /dev/null +++ b/migration/src/error.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::protocol::MigrationStatus; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MigrationError { + #[error("UtilError")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("HypervisorError")] + Hypervisor { + #[from] + source: hypervisor::error::HypervisorError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Ioctl")] + Ioctl { + #[from] + source: kvm_ioctls::Error, + }, + #[error("Json")] + Json { + #[from] + source: serde_json::Error, + }, + #[error("Migration compat_version {0} higher than current version {1}")] + VersionNotFit(u32, u32), + #[error("{0} for snapshot file / migration stream is not fit")] + HeaderItemNotFit(String), + #[error("Failed to transfer migration status from {0} to {1}.")] + InvalidStatusTransfer(MigrationStatus, MigrationStatus), + #[error("Can't restore structure from raw slice: {0}")] + FromBytesError(&'static str), + #[error("Failed to get GIC {0} register: {1}")] + GetGicRegsError(&'static str, String), + #[error("Failed to set GIC {0} register: {1}")] + SetGicRegsError(&'static str, String), + #[error("Failed to save vm memory: {0}")] + SaveVmMemoryErr(String), + #[error("Failed to restore vm memory: {0}")] + RestoreVmMemoryErr(String), + #[error("Failed to send vm memory: {0}")] + SendVmMemoryErr(String), + #[error("Failed to receive vm memory: {0}")] + RecvVmMemoryErr(String), + #[error("Response error")] + ResponseErr, + #[error("Migration status mismatch: source {0}, destination {1}.")] + MigrationStatusErr(String, String), + #[error("Migration config {0} mismatch: source {1}, destination {2}.")] + MigrationConfigErr(String, String, String), + #[error("Invalid snapshot path for restoring snapshot")] + InvalidSnapshotPath, +} diff --git a/migration/src/general.rs b/migration/src/general.rs index 56cda1305..25a346b5b 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -15,12 +15,12 @@ use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::mem::size_of; -use crate::errors::{ErrorKind, Result, ResultExt}; use crate::manager::{Instance, MIGRATION_MANAGER}; use crate::protocol::{ DeviceStateDesc, FileFormat, MigrationHeader, MigrationStatus, VersionCheck, HEADER_LENGTH, }; -use crate::MigrationManager; +use crate::{MigrationError, MigrationManager}; +use anyhow::{anyhow, Context, Result}; use util::{byte_code::ByteCode, unix::host_page_size}; impl MigrationManager { @@ -46,7 +46,7 @@ impl MigrationManager { let mut input_slice = [0u8; HEADER_LENGTH]; input_slice[0..size_of::()].copy_from_slice(header.as_bytes()); fd.write(&input_slice) - .chain_err(|| "Failed to save migration header")?; + .with_context(|| "Failed to save migration header")?; Ok(()) } @@ -64,7 +64,7 @@ impl MigrationManager { fd.read_exact(&mut place_holder)?; Ok(*MigrationHeader::from_bytes(&header_bytes) - .ok_or(ErrorKind::FromBytesError("HEADER"))?) + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("HEADER")))?) } /// Write all `DeviceStateDesc` in `desc_db` hashmap to `Write` trait object. @@ -82,7 +82,7 @@ impl MigrationManager { start += desc_bytes.len(); } fd.write_all(&buffer) - .chain_err(|| "Failed to write descriptor message.")?; + .with_context(|| "Failed to write descriptor message.")?; Ok(()) } @@ -126,15 +126,15 @@ impl MigrationManager { size_of::() as usize, ) }) - .chain_err(|| "Failed to read instance of object")?; + .with_context(|| "Failed to read instance of object")?; let locked_desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); let snap_desc = desc_db .get(&instance.object) - .chain_err(|| "Failed to get instance object")?; + .with_context(|| "Failed to get instance object")?; let current_desc = locked_desc_db .get(&snap_desc.name) - .chain_err(|| "Failed to get snap_desc name")?; + .with_context(|| "Failed to get snap_desc name")?; let mut state_data = Vec::new(); state_data.resize(snap_desc.size as usize, 0); @@ -145,14 +145,13 @@ impl MigrationManager { VersionCheck::Compat => { current_desc .add_padding(snap_desc, &mut state_data) - .chain_err(|| "Failed to transform snapshot data version")?; + .with_context(|| "Failed to transform snapshot data version")?; } VersionCheck::Mismatch => { - return Err(ErrorKind::VersionNotFit( + return Err(anyhow!(MigrationError::VersionNotFit( current_desc.compat_version, snap_desc.current_version, - ) - .into()) + ))) } } diff --git a/migration/src/lib.rs b/migration/src/lib.rs index ded93f47d..20bbcf5d9 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -14,11 +14,9 @@ //! //! Offer snapshot and migration interface for VM. -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; - +use anyhow::anyhow; pub mod general; pub mod manager; pub mod migration; @@ -27,72 +25,13 @@ pub mod snapshot; use std::{net::TcpStream, os::unix::net::UnixStream, thread}; -use error_chain::ChainedError; - +pub use anyhow::Result; use machine_manager::qmp::{qmp_schema, Response}; pub use manager::{MigrationHook, MigrationManager}; pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; use std::time::Duration; - -pub mod errors { - use super::protocol::MigrationStatus; - - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - Ioctl(kvm_ioctls::Error); - Json(serde_json::Error); - } - errors { - VersionNotFit(compat_version: u32, current_version: u32) { - display("Migration compat_version {} higher than current version {}", compat_version, current_version) - } - HeaderItemNotFit(item: String) { - display("{} for snapshot file / migration stream is not fit", item) - } - InvalidStatusTransfer(status1: MigrationStatus, status2: MigrationStatus) { - display("Failed to transfer migration status from {} to {}.", status1, status2) - } - FromBytesError(name: &'static str) { - display("Can't restore structure from raw slice: {}", name) - } - GetGicRegsError(reg: &'static str, ret: String) { - display("Failed to get GIC {} register: {}", reg, ret) - } - SetGicRegsError(reg: &'static str, ret: String) { - display("Failed to set GIC {} register: {}", reg, ret) - } - SaveVmMemoryErr(e: String) { - display("Failed to save vm memory: {}", e) - } - RestoreVmMemoryErr(e: String) { - display("Failed to restore vm memory: {}", e) - } - SendVmMemoryErr(e: String) { - display("Failed to send vm memory: {}", e) - } - RecvVmMemoryErr(e: String) { - display("Failed to receive vm memory: {}", e) - } - ResponseErr { - display("Response error") - } - MigrationStatusErr(source: String, destination: String) { - display("Migration status mismatch: source {}, destination {}.", source, destination) - } - MigrationConfigErr(config_type: String, source: String, destination: String) { - display("Migration config {} mismatch: source {}, destination {}.", config_type, source, destination) - } - InvalidSnapshotPath { - display("Invalid snapshot path for restoring snapshot") - } - } - } -} +pub mod error; +pub use error::MigrationError; /// Start to snapshot VM. /// @@ -101,12 +40,8 @@ pub mod errors { /// * `path` - snapshot dir path. If path dir not exists, will create it. pub fn snapshot(path: String) -> Response { if let Err(e) = MigrationManager::save_snapshot(&path) { - error!( - "Failed to migrate to path \'{:?}\': {}", - path, - e.display_chain() - ); - let _ = MigrationManager::set_status(MigrationStatus::Failed).map_err(|e| error!("{}", e)); + error!("Failed to migrate to path \'{:?}\': {:?}", path, e); + let _ = MigrationManager::set_status(MigrationStatus::Failed).map_err(|e| anyhow!("{}", e)); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -128,10 +63,10 @@ pub fn migration_unix_mode(path: String) -> Response { let time_out = Some(Duration::from_secs(30)); _sock .set_read_timeout(time_out) - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); _sock .set_write_timeout(time_out) - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); _sock } Err(e) => { @@ -146,7 +81,7 @@ pub fn migration_unix_mode(path: String) -> Response { .name("unix_migrate".to_string()) .spawn(move || { if let Err(e) = MigrationManager::send_migration(&mut socket) { - error!("Failed to send migration: {}", e.display_chain()); + error!("Failed to send migration: {:?}", e); let _ = MigrationManager::recover_from_migration(); let _ = MigrationManager::set_status(MigrationStatus::Failed) .map_err(|e| error!("{}", e)); @@ -192,7 +127,7 @@ pub fn migration_tcp_mode(path: String) -> Response { .name("tcp_migrate".to_string()) .spawn(move || { if let Err(e) = MigrationManager::send_migration(&mut socket) { - error!("Failed to send migration: {}", e.display_chain()); + error!("Failed to send migration: {:?}", e); let _ = MigrationManager::recover_from_migration(); let _ = MigrationManager::set_status(MigrationStatus::Failed) .map_err(|e| error!("{}", e)); diff --git a/migration/src/manager.rs b/migration/src/manager.rs index c4644c3f9..73e562ca3 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -19,10 +19,10 @@ use std::time::Instant; use once_cell::sync::Lazy; -use crate::errors::{Result, ResultExt}; use crate::general::translate_id; use crate::migration::DirtyBitmap; use crate::protocol::{DeviceStateDesc, MemBlock, MigrationStatus, StateTransfer}; +use anyhow::{Context, Result}; use machine_manager::config::VmConfig; use machine_manager::machine::MachineLifecycle; use util::byte_code::ByteCode; @@ -55,7 +55,7 @@ pub trait MigrationHook: StateTransfer { fn save_device(&self, id: u64, fd: &mut dyn Write) -> Result<()> { let state_data = self .get_state_vec() - .chain_err(|| "Failed to get device state")?; + .with_context(|| "Failed to get device state")?; fd.write_all( Instance { @@ -64,9 +64,9 @@ pub trait MigrationHook: StateTransfer { } .as_bytes(), ) - .chain_err(|| "Failed to write instance id.")?; + .with_context(|| "Failed to write instance id.")?; fd.write_all(&state_data) - .chain_err(|| "Failed to write device state")?; + .with_context(|| "Failed to write device state")?; Ok(()) } diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 80636df41..ac194952f 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -18,11 +18,11 @@ use std::time::{Duration, Instant}; use kvm_bindings::kvm_userspace_memory_region as MemorySlot; -use crate::errors::{ErrorKind, Result, ResultExt}; use crate::general::Lifecycle; use crate::manager::MIGRATION_MANAGER; use crate::protocol::{MemBlock, MigrationStatus, Request, Response, TransStatus}; -use crate::MigrationManager; +use crate::{MigrationError, MigrationManager}; +use anyhow::{anyhow, bail, Context, Result}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{get_pci_bdf, PciBdf, VmConfig}; use util::unix::host_page_size; @@ -40,16 +40,16 @@ impl MigrationManager { T: Read + Write, { // Activate the migration status of source and destination virtual machine. - Self::active_migration(fd).chain_err(|| "Failed to active migration")?; + Self::active_migration(fd).with_context(|| "Failed to active migration")?; // Send source virtual machine configuration. - Self::send_vm_config(fd).chain_err(|| "Failed to send vm config")?; + Self::send_vm_config(fd).with_context(|| "Failed to send vm config")?; // Start logging dirty pages. - Self::start_dirty_log().chain_err(|| "Failed to start logging dirty page")?; + Self::start_dirty_log().with_context(|| "Failed to start logging dirty page")?; // Send all memory of virtual machine itself to destination. - Self::send_vm_memory(fd).chain_err(|| "Failed to send VM memory")?; + Self::send_vm_memory(fd).with_context(|| "Failed to send VM memory")?; // Iteratively send virtual machine dirty memory. let iterations = MIGRATION_MANAGER.limit.read().unwrap().max_dirty_iterations; @@ -67,7 +67,7 @@ impl MigrationManager { // Check whether the migration is canceled. if Self::is_canceled() { // Cancel the migration of source and destination. - Self::cancel_migration(fd).chain_err(|| "Failed to cancel migration")?; + Self::cancel_migration(fd).with_context(|| "Failed to cancel migration")?; return Ok(()); } @@ -75,19 +75,19 @@ impl MigrationManager { Self::pause()?; // Send remaining virtual machine dirty memory. - Self::send_dirty_memory(fd).chain_err(|| "Failed to send dirty memory")?; + Self::send_dirty_memory(fd).with_context(|| "Failed to send dirty memory")?; // Stop logging dirty pages. - Self::stop_dirty_log().chain_err(|| "Failed to stop logging dirty page")?; + Self::stop_dirty_log().with_context(|| "Failed to stop logging dirty page")?; // Get virtual machine state and send it to destination VM. - Self::send_vmstate(fd).chain_err(|| "Failed to send vm state")?; + Self::send_vmstate(fd).with_context(|| "Failed to send vm state")?; // Complete the migration. - Self::complete_migration(fd).chain_err(|| "Failed to completing migration")?; + Self::complete_migration(fd).with_context(|| "Failed to completing migration")?; // Destroy virtual machine. - Self::clear_migration().chain_err(|| "Failed to clear migration")?; + Self::clear_migration().with_context(|| "Failed to clear migration")?; Ok(()) } @@ -111,25 +111,24 @@ impl MigrationManager { Response::send_msg(fd, TransStatus::Ok)?; } else { Response::send_msg(fd, TransStatus::Error)?; - return Err(ErrorKind::MigrationStatusErr( + return Err(anyhow!(MigrationError::MigrationStatusErr( request.status.to_string(), TransStatus::Active.to_string(), - ) - .into()); + ))); } // Check source and destination virtual machine configuration. let request = Request::recv_msg(fd)?; if request.status == TransStatus::VmConfig { info!("Receive VmConfig status"); - Self::check_vm_config(fd, request.length).chain_err(|| "Failed to check vm config")?; + Self::check_vm_config(fd, request.length) + .with_context(|| "Failed to check vm config")?; } else { Response::send_msg(fd, TransStatus::Error)?; - return Err(ErrorKind::MigrationStatusErr( + return Err(anyhow!(MigrationError::MigrationStatusErr( request.status.to_string(), TransStatus::VmConfig.to_string(), - ) - .into()); + ))); } loop { @@ -172,7 +171,7 @@ impl MigrationManager { let result = Response::recv_msg(fd)?; if result.is_err() { - return Err(ErrorKind::ResponseErr.into()); + return Err(anyhow!(MigrationError::ResponseErr)); } Ok(()) @@ -204,12 +203,11 @@ impl MigrationManager { let src_cpu = src_config.machine_config.nr_cpus; let dest_cpu = dest_config.machine_config.nr_cpus; if src_cpu != dest_cpu { - return Err(ErrorKind::MigrationConfigErr( + return Err(anyhow!(MigrationError::MigrationConfigErr( "vCPU number".to_string(), src_cpu.to_string(), dest_cpu.to_string(), - ) - .into()); + ))); } Ok(()) @@ -220,12 +218,11 @@ impl MigrationManager { let src_mem = src_config.machine_config.mem_config.mem_size; let dest_mem = dest_config.machine_config.mem_config.mem_size; if src_mem != dest_mem { - return Err(ErrorKind::MigrationConfigErr( + return Err(anyhow!(MigrationError::MigrationConfigErr( "memory size".to_string(), src_mem.to_string(), dest_mem.to_string(), - ) - .into()); + ))); } Ok(()) @@ -244,12 +241,11 @@ impl MigrationManager { match dest_devices.get(&src_bdf) { Some(dest_type) => { if !src_type.eq(dest_type) { - return Err(ErrorKind::MigrationConfigErr( + return Err(anyhow!(MigrationError::MigrationConfigErr( "device type".to_string(), src_type.to_string(), dest_type.to_string(), - ) - .into()); + ))); } } None => bail!( @@ -274,7 +270,8 @@ impl MigrationManager { where T: Write + Read, { - let mut state = Self::send_dirty_memory(fd).chain_err(|| "Failed to send dirty memory")?; + let mut state = + Self::send_dirty_memory(fd).with_context(|| "Failed to send dirty memory")?; // Check the virtual machine downtime. if MIGRATION_MANAGER @@ -363,7 +360,7 @@ impl MigrationManager { let result = Response::recv_msg(fd)?; if result.is_err() { - return Err(ErrorKind::ResponseErr.into()); + return Err(anyhow!(MigrationError::ResponseErr)); } Ok(()) @@ -431,7 +428,7 @@ impl MigrationManager { let result = Response::recv_msg(fd)?; if result.is_err() { - return Err(ErrorKind::ResponseErr.into()); + return Err(anyhow!(MigrationError::ResponseErr)); } Ok(()) @@ -449,8 +446,8 @@ impl MigrationManager { let header = Self::restore_header(fd)?; header.check_header()?; let desc_db = Self::restore_desc_db(fd, header.desc_len) - .chain_err(|| "Failed to load device descriptor db")?; - Self::restore_vmstate(desc_db, fd).chain_err(|| "Failed to load snapshot device")?; + .with_context(|| "Failed to load device descriptor db")?; + Self::restore_vmstate(desc_db, fd).with_context(|| "Failed to load snapshot device")?; Self::resume()?; Response::send_msg(fd, TransStatus::Ok)?; @@ -471,7 +468,7 @@ impl MigrationManager { Request::send_msg(fd, TransStatus::Active, 0)?; let result = Response::recv_msg(fd)?; if result.is_err() { - return Err(ErrorKind::ResponseErr.into()); + return Err(anyhow!(MigrationError::ResponseErr)); } Ok(()) @@ -490,7 +487,7 @@ impl MigrationManager { Request::send_msg(fd, TransStatus::Complete, 0)?; let result = Response::recv_msg(fd)?; if result.is_err() { - return Err(ErrorKind::ResponseErr.into()); + return Err(anyhow!(MigrationError::ResponseErr)); } Ok(()) @@ -512,11 +509,10 @@ impl MigrationManager { Self::set_status(MigrationStatus::Completed)?; Response::send_msg(fd, TransStatus::Ok)?; } else { - return Err(ErrorKind::MigrationStatusErr( + return Err(anyhow!(MigrationError::MigrationStatusErr( request.status.to_string(), TransStatus::Complete.to_string(), - ) - .into()); + ))); } Ok(()) @@ -532,12 +528,12 @@ impl MigrationManager { T: Write + Read, { // Stop logging dirty pages. - Self::stop_dirty_log().chain_err(|| "Failed to stop logging dirty page")?; + Self::stop_dirty_log().with_context(|| "Failed to stop logging dirty page")?; Request::send_msg(fd, TransStatus::Cancel, 0)?; let result = Response::recv_msg(fd)?; if result.is_err() { - return Err(ErrorKind::ResponseErr.into()); + return Err(anyhow!(MigrationError::ResponseErr)); } Ok(()) diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 38d808762..8888e1d13 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -18,9 +18,9 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; use kvm_ioctls::Kvm; use serde::{Deserialize, Serialize}; -use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::MigrationError; +use anyhow::{anyhow, bail, Context, Result}; use util::byte_code::ByteCode; - /// This status for migration in migration process. /// /// # Notes @@ -72,27 +72,37 @@ impl MigrationStatus { match self { MigrationStatus::None => match new_status { MigrationStatus::Setup => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + _ => Err(anyhow!(MigrationError::InvalidStatusTransfer( + self, new_status + ))), }, MigrationStatus::Setup => match new_status { MigrationStatus::Active | MigrationStatus::Failed | MigrationStatus::Canceled => { Ok(new_status) } - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + _ => Err(anyhow!(MigrationError::InvalidStatusTransfer( + self, new_status + ))), }, MigrationStatus::Active => match new_status { MigrationStatus::Completed | MigrationStatus::Failed | MigrationStatus::Canceled => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + _ => Err(anyhow!(MigrationError::InvalidStatusTransfer( + self, new_status + ))), }, MigrationStatus::Completed => match new_status { MigrationStatus::Active => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + _ => Err(anyhow!(MigrationError::InvalidStatusTransfer( + self, new_status + ))), }, MigrationStatus::Failed => match new_status { MigrationStatus::Setup | MigrationStatus::Active => Ok(new_status), - _ => Err(ErrorKind::InvalidStatusTransfer(self, new_status).into()), + _ => Err(anyhow!(MigrationError::InvalidStatusTransfer( + self, new_status + ))), }, MigrationStatus::Canceled => Ok(new_status), } @@ -178,7 +188,7 @@ impl Request { let data = unsafe { from_raw_parts(&request as *const Self as *const u8, size_of::()) }; fd.write_all(data) - .chain_err(|| format!("Failed to write request data {:?}", data))?; + .with_context(|| format!("Failed to write request data {:?}", data))?; Ok(()) } @@ -198,7 +208,7 @@ impl Request { from_raw_parts_mut(&mut request as *mut Request as *mut u8, size_of::()) }; fd.read_exact(data) - .chain_err(|| format!("Failed to read request data {:?}", data))?; + .with_context(|| format!("Failed to read request data {:?}", data))?; Ok(request) } @@ -230,7 +240,7 @@ impl Response { let data = unsafe { from_raw_parts(&response as *const Self as *const u8, size_of::()) }; fd.write_all(data) - .chain_err(|| format!("Failed to write response data {:?}", data))?; + .with_context(|| format!("Failed to write response data {:?}", data))?; Ok(()) } @@ -250,7 +260,7 @@ impl Response { from_raw_parts_mut(&mut response as *mut Response as *mut u8, size_of::()) }; fd.read_exact(data) - .chain_err(|| format!("Failed to read response data {:?}", data))?; + .with_context(|| format!("Failed to read response data {:?}", data))?; Ok(response) } @@ -393,11 +403,16 @@ impl MigrationHeader { /// Check parsed `MigrationHeader` is illegal or not. pub fn check_header(&self) -> Result<()> { if self.magic_num != MAGIC_NUMBER { - return Err(ErrorKind::HeaderItemNotFit("Magic_number".to_string()).into()); + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Magic_number".to_string() + ))); } if self.compat_version > CURRENT_VERSION { - return Err(ErrorKind::VersionNotFit(self.compat_version, CURRENT_VERSION).into()); + return Err(anyhow!(MigrationError::VersionNotFit( + self.compat_version, + CURRENT_VERSION + ))); } #[cfg(target_arch = "x86_64")] @@ -405,27 +420,37 @@ impl MigrationHeader { #[cfg(target_arch = "aarch64")] let current_arch = [b'a', b'a', b'r', b'c', b'h', b'6', b'4', b'0']; if self.arch != current_arch { - return Err(ErrorKind::HeaderItemNotFit("Arch".to_string()).into()); + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Arch".to_string() + ))); } if self.byte_order != EndianType::get_endian_type() { - return Err(ErrorKind::HeaderItemNotFit("Byte order".to_string()).into()); + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Byte order".to_string() + ))); } #[cfg(target_arch = "x86_64")] if self.cpu_model != cpu_model() { - return Err(ErrorKind::HeaderItemNotFit("Cpu model".to_string()).into()); + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Cpu model".to_string() + ))); } #[cfg(target_os = "linux")] let current_os_type = [b'l', b'i', b'n', b'u', b'x', b'0', b'0', b'0']; if self.os_type != current_os_type { - return Err(ErrorKind::HeaderItemNotFit("Os type".to_string()).into()); + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Os type".to_string() + ))); } let current_kvm_version = Kvm::new().unwrap().get_api_version() as u32; if current_kvm_version < self.hypervisor_version { - return Err(ErrorKind::HeaderItemNotFit("Hypervisor version".to_string()).into()); + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Hypervisor version".to_string() + ))); } Ok(()) diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index 9bfa94b2a..cc27d423d 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -10,15 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::general::{translate_id, Lifecycle}; +use crate::manager::{MigrationManager, MIGRATION_MANAGER}; +use crate::protocol::{DeviceStateDesc, FileFormat, MigrationStatus, HEADER_LENGTH}; +use crate::MigrationError; +use anyhow::{anyhow, bail, Context, Result}; use std::collections::HashMap; use std::fs::{create_dir, File}; use std::io::{Read, Write}; use std::path::PathBuf; - -use crate::errors::{ErrorKind, Result, ResultExt}; -use crate::general::{translate_id, Lifecycle}; -use crate::manager::{MigrationManager, MIGRATION_MANAGER}; -use crate::protocol::{DeviceStateDesc, FileFormat, MigrationStatus, HEADER_LENGTH}; use util::unix::host_page_size; pub const SERIAL_SNAPSHOT_ID: &str = "serial"; @@ -102,12 +102,12 @@ impl MigrationManager { let mut snapshot_path = PathBuf::from(path); if !snapshot_path.is_dir() { - return Err(ErrorKind::InvalidSnapshotPath.into()); + return Err(anyhow!(MigrationError::InvalidSnapshotPath)); } snapshot_path.push(MEMORY_PATH_SUFFIX); let mut memory_file = - File::open(&snapshot_path).chain_err(|| "Failed to open memory snapshot file")?; + File::open(&snapshot_path).with_context(|| "Failed to open memory snapshot file")?; let memory_header = Self::restore_header(&mut memory_file)?; memory_header.check_header()?; if memory_header.format != FileFormat::MemoryFull { @@ -115,20 +115,20 @@ impl MigrationManager { } snapshot_path.pop(); snapshot_path.push(DEVICE_PATH_SUFFIX); - let mut device_state_file = - File::open(&snapshot_path).chain_err(|| "Failed to open device state snapshot file")?; + let mut device_state_file = File::open(&snapshot_path) + .with_context(|| "Failed to open device state snapshot file")?; let device_state_header = Self::restore_header(&mut device_state_file)?; device_state_header.check_header()?; if device_state_header.format != FileFormat::Device { bail!("Invalid device state snapshot file"); } - Self::restore_memory(&mut memory_file).chain_err(|| "Failed to load snapshot memory")?; + Self::restore_memory(&mut memory_file).with_context(|| "Failed to load snapshot memory")?; let snapshot_desc_db = Self::restore_desc_db(&mut device_state_file, device_state_header.desc_len) - .chain_err(|| "Failed to load device descriptor db")?; + .with_context(|| "Failed to load device descriptor db")?; Self::restore_vmstate(snapshot_desc_db, &mut device_state_file) - .chain_err(|| "Failed to load snapshot device state")?; + .with_context(|| "Failed to load snapshot device state")?; Self::resume()?; // Set status to `Completed` @@ -182,7 +182,7 @@ impl MigrationManager { // Save CPUs state. for (id, cpu) in locked_vmm.cpus.iter() { cpu.save_device(*id, fd) - .chain_err(|| "Failed to save cpu state")?; + .with_context(|| "Failed to save cpu state")?; } #[cfg(target_arch = "x86_64")] @@ -193,7 +193,7 @@ impl MigrationManager { .as_ref() .unwrap() .save_device(translate_id(KVM_SNAPSHOT_ID), fd) - .chain_err(|| "Failed to save kvm state")?; + .with_context(|| "Failed to save kvm state")?; } // Save devices state. @@ -202,7 +202,7 @@ impl MigrationManager { .lock() .unwrap() .save_device(*id, fd) - .chain_err(|| "Failed to save device state")?; + .with_context(|| "Failed to save device state")?; } #[cfg(target_arch = "aarch64")] @@ -211,14 +211,14 @@ impl MigrationManager { let gic_id = translate_id(GICV3_SNAPSHOT_ID); if let Some(gic) = locked_vmm.gic_group.get(&gic_id) { gic.save_device(gic_id, fd) - .chain_err(|| "Failed to save gic state")?; + .with_context(|| "Failed to save gic state")?; } // Save GICv3 ITS device state. let its_id = translate_id(GICV3_ITS_SNAPSHOT_ID); if let Some(its) = locked_vmm.gic_group.get(&its_id) { its.save_device(its_id, fd) - .chain_err(|| "Failed to save gic its state")?; + .with_context(|| "Failed to save gic its state")?; } } @@ -241,7 +241,7 @@ impl MigrationManager { let (cpu_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; if let Some(cpu) = locked_vmm.cpus.get(&id) { cpu.restore_device(&cpu_data) - .chain_err(|| "Failed to restore cpu state")?; + .with_context(|| "Failed to restore cpu state")?; } } @@ -251,7 +251,7 @@ impl MigrationManager { if let Some(kvm) = &locked_vmm.kvm { let (kvm_data, _) = Self::check_vm_state(fd, &snap_desc_db)?; kvm.restore_device(&kvm_data) - .chain_err(|| "Failed to restore kvm state")?; + .with_context(|| "Failed to restore kvm state")?; } } @@ -263,7 +263,7 @@ impl MigrationManager { .lock() .unwrap() .restore_mut_device(&device_data) - .chain_err(|| "Failed to restore device state")?; + .with_context(|| "Failed to restore device state")?; } } @@ -274,7 +274,7 @@ impl MigrationManager { let (gic_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; if let Some(gic) = locked_vmm.gic_group.get(&id) { gic.restore_device(&gic_data) - .chain_err(|| "Failed to restore gic state")?; + .with_context(|| "Failed to restore gic state")?; } } } diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index bc0f23091..7d1280457 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -7,7 +7,8 @@ description = "Provides protection for stratovirt" license = "Mulan PSL v2" [dependencies] -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" libc = "0.2" util = { path = "../util" } diff --git a/ozone/src/capability.rs b/ozone/src/capability.rs index ced67b062..ca1c1ad6c 100644 --- a/ozone/src/capability.rs +++ b/ozone/src/capability.rs @@ -12,8 +12,9 @@ //! Remove all capability for ozone when uid is 0, use -capability cap_* to add capability. -use crate::{syscall, ErrorKind, Result, ResultExt}; -use error_chain::bail; +use crate::syscall; +use crate::OzoneError; +use anyhow::{anyhow, bail, Context, Result}; use std::{collections::HashMap, io::Write}; const CAPS_V3: u32 = 0x20080522; @@ -109,9 +110,9 @@ fn has_cap(cap: u8) -> Result { // so we set Bounding to limit child process. pub fn clear_all_capabilities() -> Result<()> { for cap in 0..NR_ALL_CAP { - if has_cap(cap).chain_err(|| ErrorKind::CapsError("CAPGET"))? { + if has_cap(cap).with_context(|| anyhow!(OzoneError::CapsError("CAPGET")))? { syscall::drop_bounding_caps(cap) - .chain_err(|| ErrorKind::CapsError("PR_CAPBSET_DROP"))?; + .with_context(|| anyhow!(OzoneError::CapsError("PR_CAPBSET_DROP")))?; } } @@ -130,10 +131,10 @@ pub fn set_capability_for_ozone(capability: &str) -> Result<()> { let warning = format!("Alert! Adding dangerous capability {:?} to ozone , it might cause risk of escape!\n", cap); std::io::stdout() .write(warning.as_bytes()) - .chain_err(|| "Failed to write warnings")?; + .with_context(|| "Failed to write warnings")?; std::io::stdout() .flush() - .chain_err(|| "Failed to flush stdout")?; + .with_context(|| "Failed to flush stdout")?; } } else { bail!("Invalid capability argument: {:?}", cap); @@ -144,9 +145,9 @@ pub fn set_capability_for_ozone(capability: &str) -> Result<()> { if cap_add_arr.contains(item.0) { continue; } - if has_cap(item.1 .0).chain_err(|| ErrorKind::CapsError("CAPGET"))? { + if has_cap(item.1 .0).with_context(|| anyhow!(OzoneError::CapsError("CAPGET")))? { syscall::drop_bounding_caps(item.1 .0) - .chain_err(|| ErrorKind::CapsError("PR_CAPBSET_DROP"))?; + .with_context(|| anyhow!(OzoneError::CapsError("PR_CAPBSET_DROP")))?; } } Ok(()) diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index e06cf6e00..23348f4cf 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::{ collections::HashMap, fs::{self, OpenOptions}, @@ -19,7 +18,8 @@ use std::{ process, }; -use crate::{ErrorKind, Result, ResultExt}; +use crate::OzoneError; +use anyhow::{bail, Context, Result}; const MOUNT_DIR: &str = "/proc/mounts"; const CGROUP_ALLOW_LIST: [&str; 2] = ["cpuset.cpus", "memory.limit_in_bytes"]; @@ -69,8 +69,9 @@ pub fn clean_cgroup(cmd_parser: &CgroupCfg, exec_file: String, name: String) -> let split: Vec<&str> = file.split('.').collect(); let base_path = get_base_location(split[0], &exec_file, &name)?; if base_path.exists() { - std::fs::remove_dir(&base_path) - .chain_err(|| format!("Failed to remove cgroup directory {:?}", &base_path))?; + std::fs::remove_dir(&base_path).with_context(|| { + format!("Failed to remove cgroup directory {:?}", &base_path) + })?; } } } @@ -82,7 +83,7 @@ pub fn clean_node(exec_file: String, name: String) -> Result<()> { let base_path = get_base_location("cpuset", &exec_file, &name)?; if base_path.exists() { std::fs::remove_dir(&base_path) - .chain_err(|| format!("Failed to remove cgroup directory {:?}", &base_path))?; + .with_context(|| format!("Failed to remove cgroup directory {:?}", &base_path))?; } Ok(()) @@ -94,10 +95,10 @@ fn get_base_location(controller: &str, exec_file: &str, name: &str) -> Result = dir.split(' ').collect(); target_path = PathBuf::from(split[1]); @@ -115,12 +116,12 @@ fn get_base_location(controller: &str, exec_file: &str, name: &str) -> Result Result<()> { let write_path = get_base_location("cpuset", exec_file, name)?; write_cgroup_value(&write_path, "cpuset.mems", node) - .chain_err(|| ErrorKind::WriteError("cpuset.mems".to_string(), node.to_string()))?; + .with_context(|| OzoneError::WriteError("cpuset.mems".to_string(), node.to_string()))?; let mut upper_path = write_path.clone(); upper_path.pop(); upper_path.push("cpuset.cpus"); - inherit_config(&write_path, "cpuset.cpus").chain_err(|| { + inherit_config(&write_path, "cpuset.cpus").with_context(|| { format!( "Failed to inherit configuration for path: {:?}", &write_path @@ -129,13 +130,13 @@ pub fn set_numa_node(node: &str, exec_file: &str, name: &str) -> Result<()> { let value = read_file_value(upper_path.clone()); if let Ok(val) = value { write_cgroup_value(&write_path, "cpuset.cpus", &val) - .chain_err(|| ErrorKind::WriteError("cpuset.cpus".to_string(), val.to_string()))?; + .with_context(|| OzoneError::WriteError("cpuset.cpus".to_string(), val.to_string()))?; } else { bail!("Can not read value from: {:?}", &upper_path); } let pid = process::id(); write_cgroup_value(&write_path, "tasks", &pid.to_string()) - .chain_err(|| "Failed to attach pid")?; + .with_context(|| "Failed to attach pid")?; Ok(()) } @@ -143,16 +144,16 @@ fn write_cgroup_value(path: &Path, file: &str, value: &str) -> Result<()> { if file != "tasks" { if !path.exists() { fs::create_dir_all(path) - .chain_err(|| format!("Failed to create directory: {:?}", path))?; + .with_context(|| format!("Failed to create directory: {:?}", path))?; } inherit_config(path, file) - .chain_err(|| format!("Failed to inherit configuration for path: {:?}", &path))?; + .with_context(|| format!("Failed to inherit configuration for path: {:?}", &path))?; } let mut path_to_write = path.to_path_buf(); path_to_write.push(&file); - fs::write(&path_to_write, format!("{}\n", value)).chain_err(|| { - ErrorKind::WriteError( + fs::write(&path_to_write, format!("{}\n", value)).with_context(|| { + OzoneError::WriteError( (&path_to_write.to_string_lossy()).to_string(), value.to_string(), ) @@ -163,7 +164,7 @@ fn write_cgroup_value(path: &Path, file: &str, value: &str) -> Result<()> { fn read_file_value(path: PathBuf) -> Result { let mut value = - fs::read_to_string(&path).chain_err(|| format!("Failed to read path: {:?}", &path))?; + fs::read_to_string(&path).with_context(|| format!("Failed to read path: {:?}", &path))?; value.pop(); Ok(value) } @@ -188,8 +189,8 @@ fn inherit_config(path: &Path, file: &str) -> Result<()> { if upper_value.is_empty() { bail!("File: {:?} is empty", &grand_parent_file); } - fs::write(upper_file.clone(), format!("{}\n", upper_value)).chain_err(|| { - ErrorKind::WriteError( + fs::write(upper_file.clone(), format!("{}\n", upper_value)).with_context(|| { + OzoneError::WriteError( (&upper_file.to_string_lossy()).to_string(), upper_value.to_string(), ) diff --git a/ozone/src/error.rs b/ozone/src/error.rs new file mode 100644 index 000000000..240fc12aa --- /dev/null +++ b/ozone/src/error.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Error, Debug)] +pub enum OzoneError { + #[error("Util error")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Failed to run binary file in ozone environment: {0}")] + ExecError(std::io::Error), + #[error("Failed to parse {0} to {1}")] + DigitalParseError(&'static str, String), + #[error("Failed to execute {0}")] + CapsError(&'static str), + #[error("Failed to write {0} to {1}")] + WriteError(String, String), +} diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index af6c2f860..8ed45cb79 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -11,7 +11,9 @@ // See the Mulan PSL v2 for more details. use crate::cgroup::{self, init_cgroup, parse_cgroup, CgroupCfg}; -use crate::{capability, namespace, syscall, ErrorKind, Result, ResultExt}; +use crate::OzoneError; +use crate::{capability, namespace, syscall}; +use anyhow::{anyhow, bail, Context, Result}; use std::process::Command; use std::{ @@ -21,7 +23,6 @@ use std::{ process::Stdio, }; -use error_chain::bail; use util::arg_parser::ArgMatches; const BASE_OZONE_PATH: &str = "/srv/ozone"; @@ -80,7 +81,7 @@ impl OzoneHandler { if let Some(uid) = args.value_of("uid") { let user_id = (&uid) .parse::() - .map_err(|_| ErrorKind::DigitalParseError("uid", uid))?; + .map_err(|_| anyhow!(OzoneError::DigitalParseError("uid", uid)))?; if user_id > MAX_ID_NUMBER { bail!("Input uid should be no more than 65535"); } @@ -89,7 +90,7 @@ impl OzoneHandler { if let Some(gid) = args.value_of("gid") { let group_id = (&gid) .parse::() - .map_err(|_| ErrorKind::DigitalParseError("gid", gid))?; + .map_err(|_| anyhow!(OzoneError::DigitalParseError("gid", gid)))?; if group_id > MAX_ID_NUMBER { bail!("Input gid should be no more than 65535"); } @@ -97,28 +98,28 @@ impl OzoneHandler { } if let Some(exec_file) = args.value_of("exec_file") { handler.exec_file_path = canonicalize(exec_file) - .chain_err(|| "Failed to parse exec file path to PathBuf")?; + .with_context(|| "Failed to parse exec file path to PathBuf")?; } if let Some(source_paths) = args.values_of("source_files") { for path in source_paths.iter() { - handler.source_file_paths.push( - canonicalize(path).chain_err(|| { + handler + .source_file_paths + .push(canonicalize(path).with_context(|| { format!("Failed to parse source path {:?} to PathBuf", &path) - })?, - ); + })?); } } if let Some(node) = args.value_of("numa") { handler.node = Some( (&node) .parse::() - .map_err(|_| ErrorKind::DigitalParseError("numa", node))?, + .map_err(|_| anyhow!(OzoneError::DigitalParseError("numa", node)))?, ); } if let Some(config) = args.values_of("cgroup") { let mut cgroup_cfg = init_cgroup(); for cfg in config { - parse_cgroup(&mut cgroup_cfg, &cfg).chain_err(|| "Failed to parse cgroup")? + parse_cgroup(&mut cgroup_cfg, &cfg).with_context(|| "Failed to parse cgroup")? } handler.cgroup = Some(cgroup_cfg); } @@ -142,7 +143,7 @@ impl OzoneHandler { ); } std::fs::create_dir_all(&self.chroot_dir) - .chain_err(|| format!("Failed to create folder {:?}", &self.chroot_dir))?; + .with_context(|| format!("Failed to create folder {:?}", &self.chroot_dir))?; Ok(()) } @@ -152,7 +153,7 @@ impl OzoneHandler { let mut chroot_dir = self.chroot_dir.clone(); chroot_dir.push(&exec_file_name); std::fs::copy(&self.exec_file_path, chroot_dir) - .chain_err(|| format!("Failed to copy {:?} to new chroot dir", exec_file_name))?; + .with_context(|| format!("Failed to copy {:?} to new chroot dir", exec_file_name))?; Ok(()) } @@ -171,10 +172,10 @@ impl OzoneHandler { new_root_dir.push(file_name); if file_path.is_dir() { std::fs::create_dir_all(&new_root_dir) - .chain_err(|| format!("Failed to create directory: {:?}", &new_root_dir))?; + .with_context(|| format!("Failed to create directory: {:?}", &new_root_dir))?; } else { std::fs::File::create(&new_root_dir) - .chain_err(|| format!("Failed to create file: {:?}", &new_root_dir))?; + .with_context(|| format!("Failed to create file: {:?}", &new_root_dir))?; } // new_root_dir.to_str().unwrap() is safe, because new_root_dir is not empty. syscall::mount( @@ -182,7 +183,7 @@ impl OzoneHandler { new_root_dir.to_str().unwrap(), libc::MS_BIND | libc::MS_SLAVE, ) - .chain_err(|| format!("Failed to mount file: {:?}", &file_path))?; + .with_context(|| format!("Failed to mount file: {:?}", &file_path))?; let data = std::fs::metadata(&new_root_dir)?; if !file_path.is_dir() && data.len() == 0 { @@ -190,7 +191,7 @@ impl OzoneHandler { } syscall::chown(new_root_dir.to_str().unwrap(), self.uid, self.gid) - .chain_err(|| format!("Failed to change owner for source: {:?}", &file_path))?; + .with_context(|| format!("Failed to change owner for source: {:?}", &file_path))?; Ok(()) } @@ -205,11 +206,11 @@ impl OzoneHandler { fn create_newroot_folder(&self, folder: &str) -> Result<()> { std::fs::create_dir_all(folder) - .chain_err(|| format!("Failed to create folder: {:?}", &folder))?; + .with_context(|| format!("Failed to create folder: {:?}", &folder))?; syscall::chmod(folder, 0o700) - .chain_err(|| format!("Failed to chmod to 0o700 for folder: {:?}", &folder))?; + .with_context(|| format!("Failed to chmod to 0o700 for folder: {:?}", &folder))?; syscall::chown(folder, self.uid, self.gid) - .chain_err(|| format!("Failed to change owner for folder: {:?}", &folder))?; + .with_context(|| format!("Failed to change owner for folder: {:?}", &folder))?; Ok(()) } @@ -222,11 +223,11 @@ impl OzoneHandler { ) -> Result<()> { let dev = syscall::makedev(dev_major, dev_minor)?; syscall::mknod(dev_path, libc::S_IFCHR | libc::S_IWUSR | libc::S_IRUSR, dev) - .chain_err(|| format!("Failed to call mknod for device: {:?}", &dev_path))?; + .with_context(|| format!("Failed to call mknod for device: {:?}", &dev_path))?; syscall::chmod(dev_path, mode) - .chain_err(|| format!("Failed to change mode for device: {:?}", &dev_path))?; + .with_context(|| format!("Failed to change mode for device: {:?}", &dev_path))?; syscall::chown(dev_path, self.uid, self.gid) - .chain_err(|| format!("Failed to change owner for device: {:?}", &dev_path))?; + .with_context(|| format!("Failed to change owner for device: {:?}", &dev_path))?; Ok(()) } @@ -234,7 +235,7 @@ impl OzoneHandler { /// Realize OzoneHandler. pub fn realize(&self) -> Result<()> { // First, disinfect the process. - disinfect_process().chain_err(|| "Failed to disinfect process")?; + disinfect_process().with_context(|| "Failed to disinfect process")?; self.create_chroot_dir()?; self.copy_exec_file()?; @@ -245,11 +246,11 @@ impl OzoneHandler { let exec_file = self.exec_file_name()?; if let Some(node) = self.node.clone() { cgroup::set_numa_node(&node, &exec_file, &self.name) - .chain_err(|| "Failed to set numa node")?; + .with_context(|| "Failed to set numa node")?; } if let Some(cgroup) = &self.cgroup { cgroup::realize_cgroup(cgroup, exec_file, self.name.clone()) - .chain_err(|| "Failed to realize cgroup")?; + .with_context(|| "Failed to realize cgroup")?; } namespace::set_uts_namespace("Ozone")?; @@ -273,15 +274,15 @@ impl OzoneHandler { } if let Some(capability) = &self.capability { capability::set_capability_for_ozone(capability) - .chain_err(|| "Failed to set capability for ozone.")?; + .with_context(|| "Failed to set capability for ozone.")?; } else { capability::clear_all_capabilities() - .chain_err(|| "Failed to clean all capability for ozone.")?; + .with_context(|| "Failed to clean all capability for ozone.")?; } let mut chroot_exec_file = PathBuf::from("/"); chroot_exec_file.push(self.exec_file_name()?); - Err(ErrorKind::ExecError( + Err(anyhow!(OzoneError::ExecError( Command::new(chroot_exec_file) .gid(self.gid) .uid(self.uid) @@ -290,8 +291,7 @@ impl OzoneHandler { .stderr(Stdio::inherit()) .args(&self.extra_args) .exec(), - ) - .into()) + ))) } /// Clean the environment. @@ -309,19 +309,19 @@ impl OzoneHandler { if chroot_path.exists() { syscall::umount(chroot_path.to_str().unwrap()) - .chain_err(|| format!("Failed to umount resource: {:?}", file_name))? + .with_context(|| format!("Failed to umount resource: {:?}", file_name))? } } std::fs::remove_dir_all(&self.chroot_dir) - .chain_err(|| "Failed to remove chroot dir path")?; + .with_context(|| "Failed to remove chroot dir path")?; if self.node.is_some() { cgroup::clean_node(self.exec_file_name()?, self.name.clone()) - .chain_err(|| "Failed to clean numa node")?; + .with_context(|| "Failed to clean numa node")?; } if let Some(cgroup) = &self.cgroup { cgroup::clean_cgroup(cgroup, self.exec_file_name()?, self.name.clone()) - .chain_err(|| "Failed to remove cgroup directory")?; + .with_context(|| "Failed to remove cgroup directory")?; } Ok(()) } @@ -329,7 +329,7 @@ impl OzoneHandler { /// Disinfect the process before launching the ozone process. fn disinfect_process() -> Result<()> { - let fd_entries = read_dir(SELF_FD).chain_err(|| "Failed to open process fd proc")?; + let fd_entries = read_dir(SELF_FD).with_context(|| "Failed to open process fd proc")?; for entry in fd_entries { if entry.is_err() { break; @@ -339,7 +339,7 @@ fn disinfect_process() -> Result<()> { let fd = file_name.parse::().unwrap_or(0); if fd > 2 { - syscall::close(fd).chain_err(|| format!("Failed to close fd: {}", fd))?; + syscall::close(fd).with_context(|| format!("Failed to close fd: {}", fd))?; } } Ok(()) diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 71fad2a49..649b9234a 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -10,7 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::{error_chain, quick_main}; +pub mod error; +use anyhow::{Context, Result}; +pub use error::OzoneError; use crate::args::create_args_parser; use crate::handler::OzoneHandler; @@ -22,34 +24,40 @@ mod handler; mod namespace; mod syscall; -error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); +pub trait ExitCode { + /// Returns the value to use as the exit status. + fn code(self) -> i32; +} + +impl ExitCode for i32 { + fn code(self) -> i32 { + self } - errors { - ExecError(e: std::io::Error) { - display("Failed to run binary file in ozone environment: {}", e) - } - DigitalParseError(column: &'static str, item: String) { - display("Failed to parse {} to {}", item, column) - } - CapsError(cap_option: &'static str) { - display("Failed to execute {}", cap_option) - } - WriteError(path: String, value: String) { - display("Failed to write {} to {}", value, path) - } +} + +impl ExitCode for () { + fn code(self) -> i32 { + 0 } } -quick_main!(run); +fn main() { + use std::io::Write; + + ::std::process::exit(match run() { + Ok(ret) => ExitCode::code(ret), + Err(ref e) => { + write!(&mut ::std::io::stderr(), "{}", format!("{:?}", e)) + .expect("Error writing to stderr"); + + 1 + } + }); +} fn run() -> Result<()> { let args = create_args_parser().get_matches()?; - let handler = OzoneHandler::new(&args).chain_err(|| "Failed to parse cmdline args")?; + let handler = OzoneHandler::new(&args).with_context(|| "Failed to parse cmdline args")?; if args.is_present("clean_resource") { handler.teardown()?; diff --git a/ozone/src/namespace.rs b/ozone/src/namespace.rs index e76379b43..b112dffdf 100644 --- a/ozone/src/namespace.rs +++ b/ozone/src/namespace.rs @@ -14,7 +14,7 @@ use std::fs::File; use std::os::unix::prelude::IntoRawFd; use crate::syscall; -use crate::{Result, ResultExt}; +use anyhow::{Context, Result}; const ROOT_DIR_NAME: &str = "/"; const OLD_ROOT_DIR_NAME: &str = "old_root"; @@ -26,15 +26,16 @@ const CURRENT_DIR_NAME: &str = "."; /// /// * `hostname` - Host name. pub fn set_uts_namespace(hostname: &str) -> Result<()> { - syscall::unshare(libc::CLONE_NEWUTS).chain_err(|| "Failed to unshare into a new namespace")?; - syscall::set_host_name(hostname).chain_err(|| "Failed to set new hostname")?; + syscall::unshare(libc::CLONE_NEWUTS) + .with_context(|| "Failed to unshare into a new namespace")?; + syscall::set_host_name(hostname).with_context(|| "Failed to set new hostname")?; Ok(()) } /// Set namespace for ipc. pub fn set_ipc_namespace() -> Result<()> { syscall::unshare(libc::CLONE_NEWIPC) - .chain_err(|| "Failed to share into a new ipc namespace")?; + .with_context(|| "Failed to share into a new ipc namespace")?; Ok(()) } @@ -45,10 +46,10 @@ pub fn set_ipc_namespace() -> Result<()> { /// * `path` - Path of network namespace. pub fn set_network_namespace(path: &str) -> Result<()> { let network_ns_fd = File::open(path) - .chain_err(|| format!("Failed to open netns path: {}", path))? + .with_context(|| format!("Failed to open netns path: {}", path))? .into_raw_fd(); syscall::setns(network_ns_fd, libc::CLONE_NEWNET) - .chain_err(|| "Failed to set network namespace")?; + .with_context(|| "Failed to set network namespace")?; syscall::close(network_ns_fd)?; Ok(()) } @@ -59,25 +60,26 @@ pub fn set_network_namespace(path: &str) -> Result<()> { /// /// * `mount_dir` - Path of mount directory . pub fn set_mount_namespace(mount_dir: &str) -> Result<()> { - syscall::unshare(libc::CLONE_NEWNS).chain_err(|| "Failed to unshare into a new namespace")?; + syscall::unshare(libc::CLONE_NEWNS) + .with_context(|| "Failed to unshare into a new namespace")?; syscall::mount(None, ROOT_DIR_NAME, libc::MS_SLAVE | libc::MS_REC) - .chain_err(|| "Failed to mount root path as slave and rec")?; + .with_context(|| "Failed to mount root path as slave and rec")?; syscall::mount(Some(mount_dir), mount_dir, libc::MS_BIND | libc::MS_REC) - .chain_err(|| "Failed to mount target path as bind and rec")?; + .with_context(|| "Failed to mount target path as bind and rec")?; std::env::set_current_dir(mount_dir) - .chain_err(|| "Failed to change current dir to mount dir path")?; + .with_context(|| "Failed to change current dir to mount dir path")?; - syscall::mkdir(OLD_ROOT_DIR_NAME).chain_err(|| "Failed to create old root dir")?; + syscall::mkdir(OLD_ROOT_DIR_NAME).with_context(|| "Failed to create old root dir")?; syscall::pivot_root(CURRENT_DIR_NAME, OLD_ROOT_DIR_NAME) - .chain_err(|| "Failed to call pivot_root")?; + .with_context(|| "Failed to call pivot_root")?; - syscall::chdir(ROOT_DIR_NAME).chain_err(|| "Failed to call chdir to change dir")?; + syscall::chdir(ROOT_DIR_NAME).with_context(|| "Failed to call chdir to change dir")?; - syscall::umount(OLD_ROOT_DIR_NAME).chain_err(|| "Failed to umount old root path dir")?; + syscall::umount(OLD_ROOT_DIR_NAME).with_context(|| "Failed to umount old root path dir")?; - std::fs::remove_dir(OLD_ROOT_DIR_NAME).chain_err(|| "Failed to remove old root path dir")?; + std::fs::remove_dir(OLD_ROOT_DIR_NAME).with_context(|| "Failed to remove old root path dir")?; Ok(()) } diff --git a/ozone/src/syscall.rs b/ozone/src/syscall.rs index 64b4ae1d2..8c5f658b9 100644 --- a/ozone/src/syscall.rs +++ b/ozone/src/syscall.rs @@ -215,7 +215,7 @@ pub fn chmod(file_path: &str, mode: libc::mode_t) -> Result<()> { /// * `major_id` - The major device number. /// * `minor_id` - The minor device number. pub fn makedev(major_id: u32, minor_id: u32) -> Result { - Ok(unsafe { libc::makedev(major_id, minor_id) }) + Ok(libc::makedev(major_id, minor_id)) } /// Create a special or ordinary file. diff --git a/pci/Cargo.toml b/pci/Cargo.toml index abb22d92d..1517daceb 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -8,7 +8,8 @@ description = "PCI" [dependencies] byteorder = "1.4.3" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" libc = "0.2" diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 107239f01..760af1b62 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -14,13 +14,12 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use address_space::Region; -use error_chain::bail; use log::debug; use super::config::{SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM}; use super::hotplug::HotplugOps; use super::PciDevOps; -use crate::errors::{Result, ResultExt}; +use anyhow::{bail, Context, Result}; type DeviceBusInfo = (Arc>, Arc>); @@ -187,11 +186,11 @@ impl PciBus { let mut dev_locked = dev.lock().unwrap(); dev_locked .unrealize() - .chain_err(|| format!("Failed to unrealize device {}", dev_locked.name()))?; + .with_context(|| format!("Failed to unrealize device {}", dev_locked.name()))?; let devfn = dev_locked .devfn() - .chain_err(|| format!("Failed to get devfn: device {}", dev_locked.name()))?; + .with_context(|| format!("Failed to get devfn: device {}", dev_locked.name()))?; let mut locked_bus = bus.lock().unwrap(); if locked_bus.devices.get(&devfn).is_some() { @@ -209,7 +208,7 @@ impl PciBus { .lock() .unwrap() .reset(false) - .chain_err(|| "Fail to reset pci dev")?; + .with_context(|| "Fail to reset pci dev")?; } for child_bus in self.child_buses.iter_mut() { @@ -217,7 +216,7 @@ impl PciBus { .lock() .unwrap() .reset() - .chain_err(|| "Fail to reset child bus")?; + .with_context(|| "Fail to reset child bus")?; } Ok(()) @@ -231,9 +230,9 @@ mod tests { use super::*; use crate::bus::PciBus; use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; - use crate::errors::Result; use crate::root_port::RootPort; use crate::PciHost; + use anyhow::Result; #[derive(Clone)] struct PciDevice { diff --git a/pci/src/config.rs b/pci/src/config.rs index 71743b53f..aa5199bbb 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -15,12 +15,13 @@ use std::sync::{Arc, Mutex}; use address_space::Region; -use crate::errors::{ErrorKind, Result, ResultExt}; use crate::msix::Msix; +use crate::PciError; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, }; +use anyhow::{anyhow, Context, Result}; /// Size in bytes of the configuration space of legacy PCI device. pub const PCI_CONFIG_SPACE_SIZE: usize = 256; @@ -588,7 +589,7 @@ impl PciConfig { locked_bus .io_region .delete_subregion(region) - .chain_err(|| "Failed to unregister io bar")?; + .with_context(|| "Failed to unregister io bar")?; } } _ => { @@ -596,7 +597,7 @@ impl PciConfig { locked_bus .mem_region .delete_subregion(region) - .chain_err(|| "Failed to unregister mem bar")?; + .with_context(|| "Failed to unregister mem bar")?; } } } @@ -631,11 +632,11 @@ impl PciConfig { #[cfg(target_arch = "x86_64")] io_region .delete_subregion(self.bars[id].region.as_ref().unwrap()) - .chain_err(|| format!("Failed to unmap BAR{} in I/O space.", id))?; + .with_context(|| format!("Failed to unmap BAR{} in I/O space.", id))?; } _ => mem_region .delete_subregion(self.bars[id].region.as_ref().unwrap()) - .chain_err(|| ErrorKind::UnregMemBar(id))?, + .with_context(|| anyhow!(PciError::UnregMemBar(id)))?, } self.bars[id].address = BAR_SPACE_UNMAPPED; } @@ -645,11 +646,11 @@ impl PciConfig { #[cfg(target_arch = "x86_64")] io_region .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) - .chain_err(|| format!("Failed to map BAR{} in I/O space.", id))?; + .with_context(|| format!("Failed to map BAR{} in I/O space.", id))?; } _ => mem_region .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) - .chain_err(|| ErrorKind::UnregMemBar(id))?, + .with_context(|| anyhow!(PciError::UnregMemBar(id)))?, } self.bars[id].address = new_addr; } @@ -666,7 +667,7 @@ impl PciConfig { pub fn add_pci_cap(&mut self, id: u8, size: usize) -> Result { let offset = self.last_cap_end as usize; if offset + size > PCI_CONFIG_SPACE_SIZE { - return Err(ErrorKind::AddPciCap(id, size).into()); + return Err(anyhow!(PciError::AddPciCap(id, size))); } self.config[offset] = id; @@ -697,7 +698,7 @@ impl PciConfig { pub fn add_pcie_ext_cap(&mut self, id: u16, size: usize, version: u32) -> Result { let offset = self.last_ext_cap_end as usize; if offset + size > PCIE_CONFIG_SPACE_SIZE { - return Err(ErrorKind::AddPcieExtCap(id, size).into()); + return Err(anyhow!(PciError::AddPcieExtCap(id, size))); } let regs_num = if size % REG_SIZE == 0 { diff --git a/pci/src/error.rs b/pci/src/error.rs new file mode 100644 index 000000000..f9efcd729 --- /dev/null +++ b/pci/src/error.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum PciError { + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("Failed to add PCI capability: id 0x{0:x}, size: 0x{1:x}.")] + AddPciCap(u8, usize), + #[error("Failed to add PCIe extended capability: id 0x{0:x}, size: 0x{1:x}.")] + AddPcieExtCap(u16, usize), + #[error("Failed to unmap BAR {0} in memory space.")] + UnregMemBar(usize), + #[error("Invalid device status 0x{0:x}")] + DeviceStatus(u32), + #[error("Unsupported pci register, 0x{0:x}")] + PciRegister(u64), + #[error("Invalid features select 0x{0:x}")] + FeaturesSelect(u32), +} diff --git a/pci/src/host.rs b/pci/src/host.rs index 3baf26cee..12db19009 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -24,7 +24,8 @@ use acpi::{ #[cfg(target_arch = "x86_64")] use acpi::{AmlIoDecode, AmlIoResource}; use address_space::{AddressSpace, GuestAddress, RegionOps}; -use sysbus::{errors::Result as SysBusResult, SysBusDevOps}; +use anyhow::Context; +use sysbus::SysBusDevOps; use crate::{bus::PciBus, PciDevOps}; #[cfg(target_arch = "x86_64")] @@ -250,11 +251,9 @@ impl SysBusDevOps for PciHost { } } - fn reset(&mut self) -> SysBusResult<()> { - use sysbus::errors::ResultExt as SysBusResultExt; - + fn reset(&mut self) -> sysbus::Result<()> { for (_id, pci_dev) in self.root_bus.lock().unwrap().devices.iter_mut() { - SysBusResultExt::chain_err(pci_dev.lock().unwrap().reset(true), || { + sysbus::Result::with_context(pci_dev.lock().unwrap().reset(true), || { "Fail to reset pci device under pci host" })?; } @@ -465,8 +464,8 @@ pub mod tests { use super::*; use crate::bus::PciBus; use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; - use crate::errors::Result; use crate::root_port::RootPort; + use crate::Result; struct PciDevice { devfn: u8, diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index 5b80f40bf..618567f6a 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; use std::sync::{Arc, Mutex}; -use crate::{errors::Result, PciBus, PciDevOps}; +use crate::{PciBus, PciDevOps}; +use anyhow::{bail, Result}; pub trait HotplugOps: Send { /// Plug device, usually called when hot plug device in device_add. diff --git a/pci/src/lib.rs b/pci/src/lib.rs index cb3a4d66b..d49c8d0c9 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -10,36 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - } - errors { - AddPciCap(id: u8, size: usize) { - display("Failed to add PCI capability: id 0x{:x}, size: 0x{:x}.", id, size) - } - AddPcieExtCap(id: u16, size: usize) { - display("Failed to add PCIe extended capability: id 0x{:x}, size: 0x{:x}.", id, size) - } - UnregMemBar(id: usize) { - display("Failed to unmap BAR {} in memory space.", id) - } - DeviceStatus(status: u32) { - display("Invalid device status 0x{:x}", status) - } - PciRegister(offset: u64) { - display("Unsupported pci register, 0x{:x}", offset) - } - FeaturesSelect(select: u32) { - display("Invalid features select 0x{:x}", select) - } - } - } -} - +pub mod error; +pub use error::PciError; pub mod config; pub mod hotplug; pub mod msix; @@ -58,11 +30,10 @@ use std::{ sync::{Arc, Mutex, Weak}, }; +pub use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; use crate::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; -use crate::errors::Result; const BDF_FUNC_SHIFT: u8 = 3; diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 28ad7bc21..0921a1ad6 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -14,19 +14,20 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{GuestAddress, Region, RegionOps}; -use error_chain::bail; use hypervisor::kvm::{MsiVector, KVM_FDS}; use log::error; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; use crate::config::{CapId, PciConfig, RegionType, SECONDARY_BUS_NUM}; -use crate::errors::{Result, ResultExt}; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, ranges_overlap, PciBus, }; +use anyhow::{anyhow, bail, Context, Result}; pub const MSIX_TABLE_ENTRY_SIZE: u16 = 16; pub const MSIX_TABLE_SIZE_MAX: u16 = 0x7ff; @@ -223,7 +224,7 @@ impl Msix { let table_region = Region::init_io_region(table_size, table_region_ops); region .add_subregion(table_region, table_offset) - .chain_err(|| "Failed to register MSI-X table region.")?; + .with_context(|| "Failed to register MSI-X table region.")?; let cloned_msix = msix.clone(); let pba_read = move |data: &mut [u8], _addr: GuestAddress, offset: u64| -> bool { @@ -247,7 +248,7 @@ impl Msix { let pba_region = Region::init_io_region(pba_size, pba_region_ops); region .add_subregion(pba_region, pba_offset) - .chain_err(|| "Failed to register MSI-X PBA region.")?; + .with_context(|| "Failed to register MSI-X PBA region.")?; Ok(()) } @@ -301,7 +302,7 @@ impl Msix { } impl StateTransfer for Msix { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let mut state = MsixState::default(); for (idx, table_byte) in self.table.iter().enumerate() { @@ -318,9 +319,9 @@ impl StateTransfer for Msix { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { let msix_state = *MsixState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("MSIX_DEVICE"))?; + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("MSIX_DEVICE")))?; let table_length = self.table.len(); let pba_length = self.pba.len(); @@ -344,7 +345,7 @@ impl StateTransfer for Msix { } impl MigrationHook for Msix { - fn resume(&mut self) -> migration::errors::Result<()> { + fn resume(&mut self) -> migration::Result<()> { if self.enabled && !self.func_masked { for vector in 0..self.table.len() as u16 / MSIX_TABLE_ENTRY_SIZE { if self.is_vector_masked(vector) { @@ -421,7 +422,7 @@ fn send_msix(msg: Message, dev_id: u16) { pad: [0; 12], }; if let Err(e) = KVM_FDS.load().vm_fd.as_ref().unwrap().signal_msi(kvm_msi) { - error!("Send msix error: {}", e); + error!("Send msix error: {:?}", e); }; } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 1778162b6..24fc197f9 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -14,11 +14,13 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::Region; -use error_chain::{bail, ChainedError}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, info}; use machine_manager::event; use machine_manager::qmp::{qmp_schema as schema, QmpChannel}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use once_cell::sync::OnceCell; use util::byte_code::ByteCode; @@ -33,7 +35,6 @@ use super::config::{ PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; -use crate::errors::{Result, ResultExt}; use crate::hotplug::HotplugOps; use crate::init_multifunction; use crate::msix::init_msix; @@ -122,7 +123,7 @@ impl RootPort { (self.config.ext_cap_offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_HP_EV_CCI, ) { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); error!("Failed to write command completed"); } } @@ -167,7 +168,7 @@ impl RootPort { for dev in devices.values() { let mut locked_dev = dev.lock().unwrap(); if let Err(e) = locked_dev.unrealize() { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); error!("Failed to unrealize device {}.", locked_dev.name()); } info!("Device {} unplug from {}", locked_dev.name(), self.name); @@ -196,9 +197,9 @@ impl RootPort { .unwrap() .io_region .add_subregion(self.io_region.clone(), 0) - .chain_err(|| "Failed to add IO container region.") + .with_context(|| "Failed to add IO container region.") { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); } } if command & COMMAND_MEMORY_SPACE != 0 { @@ -210,9 +211,9 @@ impl RootPort { .unwrap() .mem_region .add_subregion(self.mem_region.clone(), 0) - .chain_err(|| "Failed to add memory container region.") + .with_context(|| "Failed to add memory container region.") { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); } } } @@ -243,7 +244,7 @@ impl RootPort { self.remove_devices(); if let Err(e) = self.update_register_status() { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); error!("Failed to update register status"); } } @@ -307,11 +308,11 @@ impl PciDevOps for RootPort { locked_parent_bus .io_region .add_subregion(self.sec_bus.lock().unwrap().io_region.clone(), 0) - .chain_err(|| "Failed to register subregion in I/O space.")?; + .with_context(|| "Failed to register subregion in I/O space.")?; locked_parent_bus .mem_region .add_subregion(self.sec_bus.lock().unwrap().mem_region.clone(), 0) - .chain_err(|| "Failed to register subregion in memory space.")?; + .with_context(|| "Failed to register subregion in memory space.")?; let name = self.name.clone(); let root_port = Arc::new(Mutex::new(self)); @@ -381,7 +382,7 @@ impl PciDevOps for RootPort { &self.io_region, &self.mem_region, ) { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); } } if ranges_overlap(offset, end, COMMAND as usize, (COMMAND + 1) as usize) @@ -410,7 +411,7 @@ impl PciDevOps for RootPort { .lock() .unwrap() .reset() - .chain_err(|| "Fail to reset sec_bus in root port") + .with_context(|| "Fail to reset sec_bus in root port") } else { let cap_offset = self.config.ext_cap_offset; le_write_u16( @@ -446,7 +447,7 @@ impl HotplugOps for RootPort { .lock() .unwrap() .devfn() - .chain_err(|| "Failed to get devfn")?; + .with_context(|| "Failed to get devfn")?; // Only if devfn is equal to 0, hot plugging is supported. if devfn == 0 { let offset = self.config.ext_cap_offset; @@ -470,7 +471,7 @@ impl HotplugOps for RootPort { .lock() .unwrap() .devfn() - .chain_err(|| "Failed to get devfn")?; + .with_context(|| "Failed to get devfn")?; if devfn != 0 { return self.unplug(dev); } @@ -500,7 +501,7 @@ impl HotplugOps for RootPort { .lock() .unwrap() .devfn() - .chain_err(|| "Failed to get devfn")?; + .with_context(|| "Failed to get devfn")?; let mut locked_dev = dev.lock().unwrap(); locked_dev.unrealize()?; self.sec_bus.lock().unwrap().devices.remove(&devfn); @@ -509,7 +510,7 @@ impl HotplugOps for RootPort { } impl StateTransfer for RootPort { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> Result> { let mut state = RootPortState::default(); for idx in 0..self.config.config.len() { @@ -524,9 +525,9 @@ impl StateTransfer for RootPort { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { let root_port_state = *RootPortState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("ROOT_PORT"))?; + .ok_or_else(|| anyhow!(MigrationError::FromBytesError("ROOT_PORT")))?; let length = self.config.config.len(); self.config.config = root_port_state.config_space[..length].to_vec(); diff --git a/src/main.rs b/src/main.rs index 8634878be..eac476e0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; -use error_chain::{bail, error_chain, quick_main}; +use anyhow::{bail, Context, Result}; use log::{error, info}; use machine::{LightMachine, MachineOps, StdMachine}; use machine_manager::{ @@ -30,18 +30,60 @@ use machine_manager::{ use util::loop_context::EventNotifierHelper; use util::{arg_parser, daemonize::daemonize, logger, set_termi_canon_mode}; -error_chain! { - links { - Manager(machine_manager::errors::Error, machine_manager::errors::ErrorKind); - Util(util::errors::Error, util::errors::ErrorKind); - Machine(machine::errors::Error, machine::errors::ErrorKind); +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MainError { + #[error("Manager")] + Manager { + #[from] + source: machine_manager::error::MachineManagerError, + }, + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Machine")] + Machine { + #[from] + source: machine::error::MachineError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, +} + +pub trait ExitCode { + /// Returns the value to use as the exit status. + fn code(self) -> i32; +} + +impl ExitCode for i32 { + fn code(self) -> i32 { + self } - foreign_links { - Io(std::io::Error); +} + +impl ExitCode for () { + fn code(self) -> i32 { + 0 } } -quick_main!(run); +fn main() { + ::std::process::exit(match run() { + Ok(ret) => ExitCode::code(ret), + Err(ref e) => { + write!(&mut ::std::io::stderr(), "{}", format!("{:?}", e)) + .expect("Error writing to stderr"); + + 1 + } + }); +} fn run() -> Result<()> { let cmd_args = create_args_parser().get_matches()?; @@ -49,7 +91,7 @@ fn run() -> Result<()> { if let Some(logfile_path) = cmd_args.value_of("display log") { if logfile_path.is_empty() { logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) - .chain_err(|| "Failed to init logger.")?; + .with_context(|| "Failed to init logger.")?; } else { let logfile = std::fs::OpenOptions::new() .read(false) @@ -58,9 +100,9 @@ fn run() -> Result<()> { .create(true) .mode(0o640) .open(logfile_path) - .chain_err(|| "Failed to open log file")?; + .with_context(|| "Failed to open log file")?; logger::init_logger_with_env(Some(Box::new(logfile))) - .chain_err(|| "Failed to init logger.")?; + .with_context(|| "Failed to init logger.")?; } } @@ -92,14 +134,10 @@ fn run() -> Result<()> { Err(ref e) => { set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); if cmd_args.is_present("display log") { - error!("{}", error_chain::ChainedError::display_chain(e)); + error!("{}", format!("{:?}", e)); } else { - write!( - &mut std::io::stderr(), - "{}", - error_chain::ChainedError::display_chain(e) - ) - .expect("Failed to write to stderr"); + write!(&mut std::io::stderr(), "{}", format!("{:?}", e)) + .expect("Failed to write to stderr"); } // clean temporary file TempCleaner::clean(); @@ -136,9 +174,9 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let vm: Arc> = match vm_config.machine_config.mach_type { MachineType::MicroVm => { let vm = Arc::new(Mutex::new( - LightMachine::new(vm_config).chain_err(|| "Failed to init MicroVM")?, + LightMachine::new(vm_config).with_context(|| "Failed to init MicroVM")?, )); - MachineOps::realize(&vm, vm_config).chain_err(|| "Failed to realize micro VM.")?; + MachineOps::realize(&vm, vm_config).with_context(|| "Failed to realize micro VM.")?; EventLoop::set_manager(vm.clone(), None); for listener in listeners { @@ -148,9 +186,10 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } MachineType::StandardVm => { let vm = Arc::new(Mutex::new( - StdMachine::new(vm_config).chain_err(|| "Failed to init StandardVM")?, + StdMachine::new(vm_config).with_context(|| "Failed to init StandardVM")?, )); - MachineOps::realize(&vm, vm_config).chain_err(|| "Failed to realize standard VM.")?; + MachineOps::realize(&vm, vm_config) + .with_context(|| "Failed to realize standard VM.")?; EventLoop::set_manager(vm.clone(), None); for listener in listeners { @@ -160,7 +199,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } MachineType::None => { let vm = Arc::new(Mutex::new( - StdMachine::new(vm_config).chain_err(|| "Failed to init NoneVM")?, + StdMachine::new(vm_config).with_context(|| "Failed to init NoneVM")?, )); EventLoop::set_manager(vm.clone(), None); for listener in listeners { @@ -175,19 +214,19 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(socket))), None, ) - .chain_err(|| "Failed to add api event to MainLoop")?; + .with_context(|| "Failed to add api event to MainLoop")?; } - machine::vm_run(&vm, cmd_args).chain_err(|| "Failed to start VM.")?; + machine::vm_run(&vm, cmd_args).with_context(|| "Failed to start VM.")?; let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); if !cmd_args.is_present("disable-seccomp") { vm.lock() .unwrap() .register_seccomp(balloon_switch_on) - .chain_err(|| "Failed to register seccomp rules.")?; + .with_context(|| "Failed to register seccomp rules.")?; } - EventLoop::loop_run().chain_err(|| "MainLoop exits unexpectedly: error occurs")?; + EventLoop::loop_run().with_context(|| "MainLoop exits unexpectedly: error occurs")?; Ok(()) } diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml index ba4520d55..0e718ee37 100644 --- a/sysbus/Cargo.toml +++ b/sysbus/Cargo.toml @@ -7,7 +7,8 @@ license = "Mulan PSL v2" description = "Emulate system bus" [dependencies] -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-ioctls = ">=0.11.0" vmm-sys-util = ">=0.10.0" acpi = { path = "../acpi" } diff --git a/sysbus/src/error.rs b/sysbus/src/error.rs new file mode 100644 index 000000000..4ba9fd753 --- /dev/null +++ b/sysbus/src/error.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SysBusError { + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("Hypervisor")] + Hypervisor { + #[from] + source: hypervisor::error::HypervisorError, + }, + #[error("KvmIoctl")] + KvmIoctl { + #[from] + source: kvm_ioctls::Error, + }, +} diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 45fb02e31..f949caae7 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -10,31 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind); - } - foreign_links { - KvmIoctl(kvm_ioctls::Error); - } - } -} - +pub mod error; +pub use error::SysBusError; use std::fmt; use std::sync::{Arc, Mutex}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; -use error_chain::bail; +pub use anyhow::{bail, Context, Result}; use hypervisor::kvm::KVM_FDS; use vmm_sys_util::eventfd::EventFd; -use crate::errors::{Result, ResultExt}; - pub struct SysBus { #[cfg(target_arch = "x86_64")] pub sys_io: Arc, @@ -124,7 +110,7 @@ impl SysBus { self.sys_io .root() .add_subregion(region, region_base) - .chain_err(|| { + .with_context(|| { format!( "Failed to register region in I/O space: offset={},size={}", region_base, region_size @@ -136,7 +122,7 @@ impl SysBus { self.sys_io .root() .add_subregion(region, region_base) - .chain_err(|| { + .with_context(|| { format!( "Failed to register region in I/O space: offset 0x{:x}, size {}", region_base, region_size @@ -148,7 +134,7 @@ impl SysBus { self.sys_io .root() .add_subregion(region, region_base) - .chain_err(|| { + .with_context(|| { format!( "Failed to register region in I/O space: offset 0x{:x}, size {}", region_base, region_size @@ -159,7 +145,7 @@ impl SysBus { .sys_mem .root() .add_subregion(region, region_base) - .chain_err(|| { + .with_context(|| { format!( "Failed to register region in memory space: offset={},size={}", region_base, region_size diff --git a/usb/Cargo.toml b/usb/Cargo.toml index fae115747..d35f16a06 100644 --- a/usb/Cargo.toml +++ b/usb/Cargo.toml @@ -8,7 +8,8 @@ description = "USB controller and device emulation" [dependencies] byteorder = "1.3.4" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" libc = ">=0.2.71" log = "0.4.8" once_cell = "1.9.0" diff --git a/usb/src/bus.rs b/usb/src/bus.rs index 76e5b5c0a..8f69fcab4 100644 --- a/usb/src/bus.rs +++ b/usb/src/bus.rs @@ -13,8 +13,8 @@ use std::collections::{HashMap, LinkedList}; use std::sync::{Arc, Mutex}; -use super::errors::Result; use crate::usb::{UsbDeviceOps, UsbPort}; +use anyhow::{bail, Result}; /// The key is bus name, the value is the device which can attach other devices. pub type BusDeviceMap = Arc>>>>; diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index 55bec1af2..5d701eda0 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -14,9 +14,9 @@ use std::sync::Arc; use util::byte_code::ByteCode; -use super::errors::Result; use crate::config::*; use crate::usb::{UsbDescConfig, UsbDescEndpoint, UsbDescIface, UsbDevice}; +use anyhow::{bail, Result}; /// USB device descriptor for transfer #[allow(non_snake_case)] diff --git a/usb/src/error.rs b/usb/src/error.rs new file mode 100644 index 000000000..1d381e80e --- /dev/null +++ b/usb/src/error.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum UsbError { + #[error("PciErr")] + PciErr { + #[from] + source: pci::error::PciError, + }, + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, +} diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 884c2fd33..ef184e3d0 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -14,7 +14,6 @@ use std::sync::{Arc, Mutex, Weak}; use once_cell::sync::Lazy; -use super::errors::Result; use crate::config::*; use crate::descriptor::{ UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, @@ -30,6 +29,7 @@ use crate::usb::{ UsbEndpoint, UsbPacket, }; use crate::xhci::xhci_controller::XhciDevice; +use anyhow::Result; /// USB Keyboard Descriptor static DESC_KEYBOARD: Lazy> = Lazy::new(|| { diff --git a/usb/src/lib.rs b/usb/src/lib.rs index f8ca73c09..53493522c 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -10,24 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; -pub mod errors { - error_chain! { - links { - PciErr(pci::errors::Error, pci::errors::ErrorKind); - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - } - errors { - } - } -} +pub mod error; +pub use anyhow::Result; +pub use error::UsbError; pub mod bus; pub mod config; diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index b33f1a7e6..ee39c73a0 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -14,7 +14,6 @@ use std::sync::{Arc, Mutex, Weak}; use once_cell::sync::Lazy; -use super::errors::Result; use crate::config::*; use crate::descriptor::{UsbConfigDescriptor, UsbDeviceDescriptor, UsbEndpointDescriptor}; use crate::descriptor::{UsbDescriptorOps, UsbInterfaceDescriptor}; @@ -32,6 +31,7 @@ use crate::{ }, xhci::xhci_controller::XhciDevice, }; +use anyhow::Result; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; diff --git a/usb/src/usb.rs b/usb/src/usb.rs index fb75780ae..7d1fa2d0d 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -15,7 +15,6 @@ use std::{ sync::{Arc, Mutex, Weak}, }; -use super::errors::Result; use crate::descriptor::{ UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, @@ -25,6 +24,7 @@ use crate::{ config::*, xhci::xhci_controller::{get_field, set_field}, }; +use anyhow::{bail, Result}; const USB_MAX_ENDPOINTS: u32 = 15; const USB_MAX_INTERFACES: u32 = 16; @@ -801,7 +801,7 @@ fn write_mem(hva: u64, buf: &[u8]) { use std::io::Write; let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; if let Err(e) = (&mut slice).write(buf) { - error!("Failed to write mem {}", e); + error!("Failed to write mem {:?}", e); } } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 6237ca247..4660774dd 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -21,7 +21,6 @@ use util::num_ops::{read_u32, write_u64_low}; use crate::bus::UsbBus; use crate::config::*; -use crate::errors::{Result, ResultExt}; use crate::usb::{ Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, UsbPort, }; @@ -30,6 +29,7 @@ use crate::xhci::xhci_ring::{ TRBCCode, TRBType, XhciEventRingSeg, XhciRing, XhciTRB, TRB_EV_ED, TRB_SIZE, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TYPE_SHIFT, }; +use anyhow::{bail, Context, Result}; pub const MAX_INTRS: u16 = 16; pub const MAX_SLOTS: u32 = 64; @@ -397,13 +397,13 @@ impl XhciDevice { self.oper.reset(); for i in 0..self.slots.len() as u32 { if let Err(e) = self.disable_slot(i + 1) { - error!("Failed to disable slot {}", e); + error!("Failed to disable slot {:?}", e); } } for i in 0..self.ports.len() { let port = self.ports[i].clone(); if let Err(e) = self.port_update(&port) { - error!("Failed to update port: {}", e); + error!("Failed to update port: {:?}", e); } } for i in 0..self.intrs.len() { @@ -614,7 +614,7 @@ impl XhciDevice { self.send_event(&event, 0)?; } Err(e) => { - error!("Failed to fetch ring: {}", e); + error!("Failed to fetch ring: {:?}", e); event.ccode = TRBCCode::TrbError; break; } @@ -1010,7 +1010,7 @@ impl XhciDevice { let len = match epctx.ring.get_transfer_len() { Ok(len) => len, Err(e) => { - error!("Failed to get transfer len: {}", e); + error!("Failed to get transfer len: {:?}", e); break; } }; @@ -1038,7 +1038,7 @@ impl XhciDevice { } } if let Err(e) = self.endpoint_do_transfer(&mut xfer, &mut epctx) { - error!("Failed to transfer {}", e); + error!("Failed to transfer {:?}", e); } if xfer.complete { epctx.set_state(&self.mem_space, epctx.state)?; @@ -1565,7 +1565,7 @@ pub fn dma_read_bytes( mut buf: &mut [u8], len: u64, ) -> Result<()> { - addr_space.read(&mut buf, addr, len).chain_err(|| { + addr_space.read(&mut buf, addr, len).with_context(|| { format!( "Failed to read dma memory at gpa=0x{:x} len=0x{:x}", addr.0, len @@ -1580,7 +1580,7 @@ pub fn dma_write_bytes( mut buf: &[u8], len: u64, ) -> Result<()> { - addr_space.write(&mut buf, addr, len).chain_err(|| { + addr_space.write(&mut buf, addr, len).with_context(|| { format!( "Failed to write dma memory at gpa=0x{:x} len=0x{:x}", addr.0, len diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 20dc7ae24..be55b9481 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -18,19 +18,18 @@ use pci::config::{ PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, PCI_CLASS_SERIAL_USB, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::errors::{Result as PciResult, ResultExt as PciResultExt}; use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; use util::num_ops::round_up; use util::unix::host_page_size; use crate::bus::{BusDeviceMap, BusDeviceOps}; -use crate::errors::Result; use crate::usb::UsbDeviceOps; use crate::xhci::xhci_controller::{XhciDevice, XhciOps, MAX_INTRS, MAX_SLOTS}; use crate::xhci::xhci_regs::{ build_cap_ops, build_doorbell_ops, build_oper_ops, build_port_ops, build_runtime_ops, XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME, }; +use anyhow::{bail, Context, Result}; const PCI_VENDOR_ID_NEC: u16 = 0x1033; const PCU_DEVICE_ID_NEC_UPD720200: u16 = 0x0194; @@ -87,10 +86,10 @@ impl XhciPciDevice { } } - fn mem_region_init(&mut self) -> PciResult<()> { + fn mem_region_init(&mut self) -> pci::Result<()> { let cap_region = Region::init_io_region(XHCI_PCI_CAP_LENGTH as u64, build_cap_ops(&self.xhci)); - PciResultExt::chain_err( + pci::Result::with_context( self.mem_region .add_subregion(cap_region, XHCI_PCI_CAP_OFFSET as u64), || "Failed to register cap region.", @@ -98,7 +97,7 @@ impl XhciPciDevice { let oper_region = Region::init_io_region(XHCI_PCI_OPER_LENGTH as u64, build_oper_ops(&self.xhci)); - PciResultExt::chain_err( + pci::Result::with_context( self.mem_region .add_subregion(oper_region, XHCI_PCI_OPER_OFFSET as u64), || "Failed to register oper region.", @@ -108,7 +107,7 @@ impl XhciPciDevice { XHCI_PCI_RUNTIME_LENGTH as u64, build_runtime_ops(&self.xhci), ); - PciResultExt::chain_err( + pci::Result::with_context( self.mem_region .add_subregion(runtime_region, XHCI_PCI_RUNTIME_OFFSET as u64), || "Failed to register runtime region.", @@ -118,7 +117,7 @@ impl XhciPciDevice { XHCI_PCI_DOORBELL_LENGTH as u64, build_doorbell_ops(&self.xhci), ); - PciResultExt::chain_err( + pci::Result::with_context( self.mem_region .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64), || "Failed to register doorbell region.", @@ -130,7 +129,7 @@ impl XhciPciDevice { let port_region = Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i) as u64; - PciResultExt::chain_err(self.mem_region.add_subregion(port_region, offset), || { + pci::Result::with_context(self.mem_region.add_subregion(port_region, offset), || { "Failed to register port region." })?; } @@ -139,15 +138,15 @@ impl XhciPciDevice { } impl PciDevOps for XhciPciDevice { - fn init_write_mask(&mut self) -> PciResult<()> { + fn init_write_mask(&mut self) -> pci::Result<()> { self.pci_config.init_common_write_mask() } - fn init_write_clear_mask(&mut self) -> PciResult<()> { + fn init_write_clear_mask(&mut self) -> pci::Result<()> { self.pci_config.init_common_write_clear_mask() } - fn realize(mut self) -> PciResult<()> { + fn realize(mut self) -> pci::Result<()> { self.init_write_mask()?; self.init_write_clear_mask()?; le_write_u16( @@ -223,7 +222,7 @@ impl PciDevOps for XhciPciDevice { Ok(()) } - fn unrealize(&mut self) -> PciResult<()> { + fn unrealize(&mut self) -> pci::Result<()> { Ok(()) } @@ -271,7 +270,7 @@ impl PciDevOps for XhciPciDevice { &locked_parent_bus.io_region, &locked_parent_bus.mem_region, ) { - error!("Failed to update bar, error is {}", e); + error!("Failed to update bar, error is {:?}", e); } } } @@ -280,7 +279,7 @@ impl PciDevOps for XhciPciDevice { self.name.clone() } - fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { + fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { Ok(()) } } diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 565c8aab4..2f8459597 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -19,10 +19,10 @@ use byteorder::{ByteOrder, LittleEndian}; use util::num_ops::{read_u32, write_u64_high, write_u64_low}; use crate::config::*; -use crate::errors::Result; use crate::usb::UsbPort; use crate::xhci::xhci_controller::{set_field, XhciDevice, XhciEvent}; use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; +use anyhow::{bail, Result}; /// Capability offset or size. pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; @@ -290,7 +290,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { } }; if let Err(e) = write_data(data, value) { - error!("Failed to write data when read oper registers: {}", e); + error!("Failed to write data when read oper registers: {:?}", e); return false; } true @@ -335,7 +335,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { } }; if let Err(e) = write_data(data, value) { - error!("Failed to write data when read oper registers: {}", e); + error!("Failed to write data when read oper registers: {:?}", e); return false; } true @@ -347,7 +347,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { let value = match read_data(data) { Ok(v) => v, Err(e) => { - error!("Failed to read data: offset 0x{:x}, {}", offset, e); + error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); return false; } }; @@ -401,7 +401,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { locked_xhci.oper.cmd_ring_ctrl = write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); if let Err(e) = locked_xhci.send_event(&event, 0) { - error!("Failed to send event: {}", e); + error!("Failed to send event: {:?}", e); } } else { let crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & !0x3f; @@ -471,7 +471,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { }; } if let Err(e) = write_data(data, value) { - error!("Failed to write data when read runtime registers: {}", e); + error!("Failed to write data when read runtime registers: {:?}", e); return false; } true @@ -482,7 +482,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { let value = match read_data(data) { Ok(v) => v, Err(e) => { - error!("Failed to read data: offset 0x{:x}, {}", offset, e); + error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); return false; } }; @@ -516,7 +516,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_INTR_REG_ERSTBA_HI => { intr.erstba = write_u64_high(intr.erstba, value); if let Err(e) = xhci.reset_event_ring(idx) { - error!("Failed to reset event ring: {}", e); + error!("Failed to reset event ring: {:?}", e); } } XHCI_INTR_REG_ERDP_LO => { @@ -561,7 +561,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let doorbell_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { debug!("doorbell read addr {:x} offset {:x}", addr.0, offset); if let Err(e) = write_data(data, 0) { - error!("Failed to write data: {}", e); + error!("Failed to write data: {:?}", e); return false; } true @@ -571,7 +571,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let value = match read_data(data) { Ok(v) => v, Err(e) => { - error!("Failed to read data: offset 0x{:x}, {}", offset, e); + error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); return false; } }; @@ -585,7 +585,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { if slot_id == 0 { if value == 0 { if let Err(e) = xhci.handle_command() { - error!("Failed to process commands: {}", e); + error!("Failed to process commands: {:?}", e); } } else { error!("Invalid doorbell write: value {:x}", value) @@ -597,7 +597,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { } else if ep_id == 0 || ep_id > 31 { error!("Invalid epid {}", ep_id,); } else if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { - error!("Failed to kick endpoint: {}", e); + error!("Failed to kick endpoint: {:?}", e); } } true @@ -626,7 +626,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { } }; if let Err(e) = write_data(data, value) { - error!("Failed to write data: {}", e); + error!("Failed to write data: {:?}", e); return false; } true @@ -637,7 +637,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { let value = match read_data(data) { Ok(v) => v, Err(e) => { - error!("Failed to read data: offset 0x{:x}, {}", offset, e); + error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); return false; } }; @@ -656,13 +656,13 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { XHCI_PORTSC => { if value & PORTSC_WPR == PORTSC_WPR { if let Err(e) = locked_xhci.reset_port(&port, true) { - error!("Failed to warn reset port {}", e); + error!("Failed to warn reset port {:?}", e); } break; } if value & PORTSC_PR == PORTSC_PR { if let Err(e) = locked_xhci.reset_port(&port, false) { - error!("Failed to reset port {}", e); + error!("Failed to reset port {:?}", e); } break; } @@ -714,7 +714,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { drop(locked_port); if notify != 0 { if let Err(e) = locked_xhci.port_notify(&port, notify) { - error!("Failed to notify: {}", e); + error!("Failed to notify: {:?}", e); } } } diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 93330323b..d0c7159f1 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -16,8 +16,8 @@ use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; -use crate::errors::Result; use crate::xhci::xhci_controller::dma_read_bytes; +use anyhow::{bail, Result}; /// Transfer Request Block pub const TRB_SIZE: u32 = 16; diff --git a/util/Cargo.toml b/util/Cargo.toml index 7df9da4e6..8d82d639f 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -9,7 +9,8 @@ license = "Mulan PSL v2" [dependencies] arc-swap = ">=1.5.0" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" libc = "0.2" diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 6ed075ce6..a89295162 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use super::{AioContext, IoEvent, Result}; -use error_chain::bail; +use anyhow::bail; use kvm_bindings::__IncompleteArrayField; pub const IOCB_FLAG_RESFD: u32 = 1; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index ba58a32e9..b1bec28b1 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -21,8 +21,8 @@ use std::sync::{Arc, Mutex}; use vmm_sys_util::eventfd::EventFd; -use super::errors::{Result, ResultExt}; use super::link_list::{List, Node}; +use anyhow::Result; pub use libaio::*; pub use raw::*; use uring::IoUringContext; diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 98422f9b0..bd8effd3c 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use super::Result; -use error_chain::bail; +use anyhow::bail; use libc::{c_void, fdatasync, pread, pwrite}; use std::os::unix::io::RawFd; diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 7ce2a5ff9..da36e963c 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -14,12 +14,12 @@ use libc; use std::os::unix::io::AsRawFd; use std::sync::Arc; -use error_chain::bail; +use anyhow::{bail, Context}; use io_uring::{opcode, squeue, types, IoUring}; use vmm_sys_util::eventfd::EventFd; use super::libaio::{IoCb, IoCmd}; -use super::{AioCb, AioCompleteFunc, AioContext, CbNode, IoEvent, Result, ResultExt}; +use super::{AioCb, AioCompleteFunc, AioContext, CbNode, IoEvent, Result}; /// The io-uring context. pub(crate) struct IoUringContext { @@ -39,11 +39,11 @@ impl IoUringContext { bail!("Entries must be the power of 2 and larger than 0"); } let ring = - IoUring::new(entries as u32).chain_err(|| "Failed to create io_uring instance")?; + IoUring::new(entries as u32).with_context(|| "Failed to create io_uring instance")?; ring.submitter() .register_eventfd(eventfd.as_raw_fd()) - .chain_err(|| "Failed to register event fd")?; + .with_context(|| "Failed to register event fd")?; let events = Vec::with_capacity(entries as usize); Ok(IoUringContext { ring, func, events }) } @@ -86,12 +86,12 @@ impl AioContext for IoUringContext { self.ring .submission() .push(&entry) - .chain_err(|| "Failed to push entry")?; + .with_context(|| "Failed to push entry")?; } } self.ring .submit_and_wait(nr as usize) - .chain_err(|| "Failed to submit sqe")?; + .with_context(|| "Failed to submit sqe")?; Ok(()) } diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index c8dd4f8b2..a3469e871 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -17,7 +17,8 @@ use std::env; use std::io::Write; use std::process; -use crate::errors::{ErrorKind, Result}; +use crate::UtilError; +use anyhow::{anyhow, Result}; const PREFIX_CHARS_SHORT: &str = "-"; const PREFIX_CHARS_LONG: &str = "-"; @@ -376,11 +377,11 @@ impl<'a> Arg<'a> { if arg_hash.contains_key(&long_name) { if !self.multiple && multi_vec.contains(&long_name) { - return Err(ErrorKind::DuplicateArgument(long_name).into()); + return Err(anyhow!(UtilError::DuplicateArgument(long_name))); } if self.value.is_some() && (arg_hash[&long_name].len() > 1) && !self.multiple { - return Err(ErrorKind::DuplicateValue(long_name).into()); + return Err(anyhow!(UtilError::DuplicateValue(long_name))); } if (self.value.is_some() || self.values.is_some()) && (arg_hash[&long_name].is_empty()) @@ -390,44 +391,41 @@ impl<'a> Arg<'a> { self.presented = true; return Ok(()); } else { - return Err(ErrorKind::MissingValue(long_name).into()); + return Err(anyhow!(UtilError::MissingValue(long_name))); } } if (self.value.is_none() && self.values.is_none()) && (!arg_hash[&long_name].is_empty()) { - return Err(ErrorKind::IllegelValue( + return Err(anyhow!(UtilError::IllegelValue( arg_hash[&long_name][0].to_string(), long_name.to_string(), - ) - .into()); + ))); } if self.value.is_some() { if self.possible_value_check(&arg_hash[&long_name][0]) { self.value = Some(arg_hash[&long_name][0].clone()); } else { - return Err(ErrorKind::ValueOutOfPossible( + return Err(anyhow!(UtilError::ValueOutOfPossible( long_name, format!("{:?}", self.possible_values), - ) - .into()); + ))); } } else if self.values.is_some() { if self.possible_values_check(arg_hash[&long_name].clone()) { self.values = Some(arg_hash[&long_name].clone()); } else { - return Err(ErrorKind::ValueOutOfPossible( + return Err(anyhow!(UtilError::ValueOutOfPossible( long_name, format!("{:?}", self.possible_values), - ) - .into()); + ))); } } self.presented = true; } else if self.required { - return Err(ErrorKind::MissingArgument(long_name).into()); + return Err(anyhow!(UtilError::MissingArgument(long_name))); } if self.short.is_some() { @@ -436,16 +434,15 @@ impl<'a> Arg<'a> { if (self.value.is_none() && self.values.is_none()) && (!arg_hash[short_name].is_empty()) { - return Err(ErrorKind::IllegelValue( + return Err(anyhow!(UtilError::IllegelValue( arg_hash[short_name][0].to_string(), short_name.to_string(), - ) - .into()); + ))); } self.presented = true; } else if self.required { - return Err(ErrorKind::MissingArgument(short_name.to_string()).into()); + return Err(anyhow!(UtilError::MissingArgument(short_name.to_string()))); } } @@ -600,7 +597,7 @@ fn parse_cmdline( let mut j = 1; for cmd_arg in &cmd_args[1..] { if !allow_list.contains(cmd_arg) && cmd_arg.starts_with(PREFIX_CHARS_SHORT) { - return Err(ErrorKind::UnexpectedArguments(cmd_arg.to_string()).into()); + return Err(anyhow!(UtilError::UnexpectedArguments(cmd_arg.to_string()))); } if cmd_arg.starts_with(PREFIX_CHARS_LONG) { @@ -626,7 +623,7 @@ fn parse_cmdline( let arg_str = match i.1 { PREFIX_CHARS_LONG => split_arg(&cmd_args[i.0], PREFIX_CHARS_LONG), &_ => { - return Err(ErrorKind::UnexpectedArguments(cmd_arg.to_string()).into()); + return Err(anyhow!(UtilError::UnexpectedArguments(cmd_arg.to_string()))); } }; arg_map diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 0b7767076..42df5f4b1 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -13,7 +13,8 @@ use std::cmp::Ord; use std::mem::size_of; -use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::UtilError; +use anyhow::{anyhow, Context, Result}; /// This struct is used to offer bitmap. pub struct Bitmap { @@ -52,7 +53,10 @@ impl Bitmap { pub fn set(&mut self, num: usize) -> Result<()> { let index = self.bit_index(num); if index >= self.size() { - return Err(ErrorKind::OutOfBound(index as u64, self.vol() as u64).into()); + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); } self.data[index] = T::bit_or(self.data[index], T::one().rhs(self.bit_pos(num))); Ok(()) @@ -66,7 +70,10 @@ impl Bitmap { pub fn clear(&mut self, num: usize) -> Result<()> { let index = self.bit_index(num); if index >= self.size() { - return Err(ErrorKind::OutOfBound(index as u64, self.vol() as u64).into()); + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); } self.data[index] = T::bit_and( self.data[index], @@ -82,11 +89,10 @@ impl Bitmap { /// * `num` - the input number. pub fn contain(&self, num: usize) -> Result { if num > self.vol() { - return Err(ErrorKind::OutOfBound( + return Err(anyhow!(UtilError::OutOfBound( num as u64, (self.size() as u64 * T::len() as u64) as u64, - ) - .into()); + ))); } Ok(T::bit_and( self.data[self.bit_index(num)], @@ -102,13 +108,16 @@ impl Bitmap { /// * `offset` - the input offset as the query's start. pub fn count_front_bits(&self, offset: usize) -> Result { if offset > self.vol() { - return Err(ErrorKind::OutOfBound(offset as u64, self.size() as u64).into()); + return Err(anyhow!(UtilError::OutOfBound( + offset as u64, + self.size() as u64 + ))); } let mut num = 0; for i in 0..self.bit_index(offset) + 1 { if i == self.bit_index(offset) { for j in i * T::len()..offset { - let ret = self.contain(j).chain_err(|| "count front bits failed")?; + let ret = self.contain(j).with_context(|| "count front bits failed")?; if ret { num += 1; } diff --git a/util/src/daemonize.rs b/util/src/daemonize.rs index a057ebec4..e7671869e 100644 --- a/util/src/daemonize.rs +++ b/util/src/daemonize.rs @@ -39,7 +39,8 @@ use std::os::unix::io::RawFd; use std::path::Path; use std::process::exit; -use crate::errors::{ErrorKind, Result}; +use crate::UtilError; +use anyhow::{anyhow, Result}; /// Write process id to pid file. fn create_pid_file(path: &str) -> Result<()> { @@ -71,7 +72,7 @@ fn fork() -> Result<()> { let ret = unsafe { libc::fork() }; match ret.cmp(&0) { - Ordering::Less => Err(ErrorKind::DaemonFork.into()), + Ordering::Less => Err(anyhow!(UtilError::DaemonFork)), Ordering::Greater => exit(0), Ordering::Equal => Ok(()), } @@ -92,7 +93,7 @@ fn set_sid() -> Result<()> { let ret = unsafe { libc::setsid() }; if ret == -1 { - Err(ErrorKind::DaemonSetsid.into()) + Err(anyhow!(UtilError::DaemonSetsid)) } else { Ok(()) } @@ -115,15 +116,15 @@ fn redirect_stdio(fd: RawFd) -> Result<()> { let devnull_fd = libc::open(b"/dev/null\0" as *const [u8; 10] as _, libc::O_RDWR); if devnull_fd == -1 { - return Err(ErrorKind::DaemonRedirectStdio.into()); + return Err(anyhow!(UtilError::DaemonRedirectStdio)); } if libc::dup2(devnull_fd, fd) == -1 { - return Err(ErrorKind::DaemonRedirectStdio.into()); + return Err(anyhow!(UtilError::DaemonRedirectStdio)); } if libc::close(devnull_fd) == -1 { - return Err(ErrorKind::DaemonRedirectStdio.into()); + return Err(anyhow!(UtilError::DaemonRedirectStdio)); } } @@ -146,7 +147,7 @@ fn redirect_stdio(fd: RawFd) -> Result<()> { pub fn daemonize(pid_file: Option) -> Result<()> { if let Some(path) = pid_file.as_ref() { if Path::new(path).exists() { - return Err(ErrorKind::PidFileExist.into()); + return Err(anyhow!(UtilError::PidFileExist)); } } diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 3b5578a3f..20a55e5c4 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -12,7 +12,8 @@ use std::mem::size_of; -use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::UtilError; +use anyhow::{anyhow, Context, Result}; use byteorder::{BigEndian, ByteOrder}; pub const CLK_PHANDLE: u32 = 1; @@ -117,7 +118,7 @@ impl FdtBuilder { pub fn finish(mut self) -> Result> { if self.subnode_depth > 0 { - return Err(ErrorKind::NodeUnclosed(self.subnode_depth).into()); + return Err(anyhow!(UtilError::NodeUnclosed(self.subnode_depth))); } self.structure_blk .extend_from_slice(&FDT_END.to_be_bytes()[..]); @@ -158,7 +159,7 @@ impl FdtBuilder { pub fn add_mem_reserve(&mut self, mem_reservations: &[FdtReserveEntry]) -> Result<()> { if !check_mem_reserve_overlap(mem_reservations) { - return Err(ErrorKind::MemReserveOverlap.into()); + return Err(anyhow!(UtilError::MemReserveOverlap)); } for mem_reser in mem_reservations { @@ -174,7 +175,7 @@ impl FdtBuilder { pub fn begin_node(&mut self, node_name: &str) -> Result { if !check_string_legality(node_name) { - return Err(ErrorKind::IllegalString(node_name.to_string()).into()); + return Err(anyhow!(UtilError::IllegalString(node_name.to_string()))); } self.structure_blk @@ -196,7 +197,10 @@ impl FdtBuilder { pub fn end_node(&mut self, begin_node_depth: u32) -> Result<()> { if begin_node_depth != self.subnode_depth { - return Err(ErrorKind::NodeDepthMismatch(begin_node_depth, self.subnode_depth).into()); + return Err(anyhow!(UtilError::NodeDepthMismatch( + begin_node_depth, + self.subnode_depth + ))); } self.structure_blk @@ -215,17 +219,17 @@ impl FdtBuilder { // The string property should end with null('\0'). val_array.push(0x0_u8); self.set_property(prop, &val_array) - .chain_err(|| ErrorKind::SetPropertyErr("string".to_string())) + .with_context(|| anyhow!(UtilError::SetPropertyErr("string".to_string()))) } pub fn set_property_u32(&mut self, prop: &str, val: u32) -> Result<()> { self.set_property(prop, &val.to_be_bytes()[..]) - .chain_err(|| ErrorKind::SetPropertyErr("u32".to_string())) + .with_context(|| anyhow!(UtilError::SetPropertyErr("u32".to_string()))) } pub fn set_property_u64(&mut self, prop: &str, val: u64) -> Result<()> { self.set_property(prop, &val.to_be_bytes()[..]) - .chain_err(|| ErrorKind::SetPropertyErr("u64".to_string())) + .with_context(|| anyhow!(UtilError::SetPropertyErr("u64".to_string()))) } pub fn set_property_array_u32(&mut self, prop: &str, array: &[u32]) -> Result<()> { @@ -234,7 +238,7 @@ impl FdtBuilder { prop_array.extend_from_slice(&element.to_be_bytes()[..]); } self.set_property(prop, &prop_array) - .chain_err(|| ErrorKind::SetPropertyErr("u32 array".to_string())) + .with_context(|| anyhow!(UtilError::SetPropertyErr("u32 array".to_string()))) } pub fn set_property_array_u64(&mut self, prop: &str, array: &[u64]) -> Result<()> { @@ -243,16 +247,16 @@ impl FdtBuilder { prop_array.extend_from_slice(&element.to_be_bytes()[..]); } self.set_property(prop, &prop_array) - .chain_err(|| ErrorKind::SetPropertyErr("u64 array".to_string())) + .with_context(|| anyhow!(UtilError::SetPropertyErr("u64 array".to_string()))) } pub fn set_property(&mut self, property_name: &str, property_val: &[u8]) -> Result<()> { if !check_string_legality(property_name) { - return Err(ErrorKind::IllegalString(property_name.to_string()).into()); + return Err(anyhow!(UtilError::IllegalString(property_name.to_string()))); } if !self.begin_node { - return Err(ErrorKind::IllegelPropertyPos.into()); + return Err(anyhow!(UtilError::IllegelPropertyPos)); } let len = property_val.len() as u32; diff --git a/util/src/error.rs b/util/src/error.rs new file mode 100644 index 000000000..97293e3f8 --- /dev/null +++ b/util/src/error.rs @@ -0,0 +1,89 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum UtilError { + #[error("KvmIoctl")] + KvmIoctl { + #[from] + source: kvm_ioctls::Error, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Nul")] + Nul { + #[from] + source: std::ffi::NulError, + }, + // arg_parser submodule error + #[error("Argument '{0}' required, but not found. Use \'-h\' or \'-help\' to get usage.")] + MissingArgument(String), + #[error("The argument '{0}' requires a value, but none was supplied. Use \'-h\' or \'-help\' to get usage.")] + MissingValue(String), + #[error( + "The value '{0}' is illegel for argument '{1}'. Use \'-h\' or \'-help\' to get usage." + )] + IllegelValue(String, String), + #[error("The value of argument '{0}' must be in '{1}'. Use \'-h\' or \'-help\' to get usage.")] + ValueOutOfPossible(String, String), + #[error("Found argument '{0}' which wasn't expected, or isn't valid in the context. Use \'-h\' or \'-help\' to get usage.")] + UnexpectedArguments(String), + #[error( + "The argument '{0}' was provided more than once. Use \'-h\' or \'-help\' to get usage." + )] + DuplicateArgument(String), + #[error("The argument '{0}' only need one value. Use \'-h\' or \'-help\' to get usage.")] + DuplicateValue(String), + // daemonize submodule error + #[error("Unable to fork.")] + DaemonFork, + #[error("Unable to create new session.")] + DaemonSetsid, + #[error("Unable to redirect standard streams to /dev/null.")] + DaemonRedirectStdio, + #[error("Pidfile path is existed yet.")] + PidFileExist, + // epoll_context error + #[error("Found bad syscall, error is {0} .")] + BadSyscall(std::io::Error), + #[error("Unsupported Epoll notifier operation type.")] + UnExpectedOperationType, + #[error("Failed to execute epoll_wait syscall: {0} .")] + EpollWait(std::io::Error), + #[error("The fd {0} is not registered in epoll.")] + NoRegisterFd(i32), + #[error("Found no parked fd {0}.")] + NoParkedFd(i32), + #[error("Notifier Operation non allowed.")] + BadNotifierOperation, + #[error("Chmod command failed, os error {0}")] + ChmodFailed(i32), + #[error("Index :{0} out of bound :{1}")] + OutOfBound(u64, u64), + #[error("Desired node depth :{0}, current node depth :{1}")] + NodeDepthMismatch(u32, u32), + #[error("Still have {0} node open when terminating the fdt")] + NodeUnclosed(u32), + #[error("Failed to add property because there is no open node")] + IllegelPropertyPos, + #[error("Failed to add string to fdt because of null character inside \"{0}\"")] + IllegalString(String), + #[error("Failed to add overlapped mem reserve entries to fdt")] + MemReserveOverlap, + #[error("Failed to set {0} property")] + SetPropertyErr(String), +} diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 313e10997..10678562b 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -87,7 +87,7 @@ impl LeakBucket { let func = Box::new(move || { wakeup_clone .write(1) - .unwrap_or_else(|e| error!("LeakBucket send event to device failed {}", e)); + .unwrap_or_else(|e| error!("LeakBucket send event to device failed {:?}", e)); }); loop_context.delay_call( diff --git a/util/src/lib.rs b/util/src/lib.rs index a2ee22adf..bb098a468 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -19,6 +19,7 @@ pub mod daemonize; #[cfg(target_arch = "aarch64")] pub mod device_tree; pub mod edid; +pub mod error; pub mod leak_bucket; mod link_list; pub mod logger; @@ -33,124 +34,8 @@ pub mod syscall; pub mod tap; pub mod trace; pub mod unix; - -pub mod errors { - use error_chain::error_chain; - - error_chain! { - foreign_links { - KvmIoctl(kvm_ioctls::Error); - Io(std::io::Error); - Nul(std::ffi::NulError); - } - errors { - // arg_parser submodule error - MissingArgument(t: String) { - description("The required argument was not provided.") - display("Argument '{}' required, but not found. Use \'-h\' or \'-help\' to get usage.", t) - } - MissingValue(t: String) { - description("A value for args was not provided.") - display("The argument '{}' requires a value, but none was supplied. Use \'-h\' or \'-help\' to get usage.", t) - } - IllegelValue(t1: String, t2: String) { - description("A value is illegel for args.") - display("The value '{}' is illegel for argument '{}'. Use \'-h\' or \'-help\' to get usage.", t1, t2) - } - ValueOutOfPossible(t1: String, t2: String) { - description("A value for args is out of possile values.") - display("The value of argument '{}' must be in '{}'. Use \'-h\' or \'-help\' to get usage.", t1, t2) - } - UnexpectedArguments(t: String) { - description("The provided argument was not expected.") - display("Found argument '{}' which wasn't expected, or isn't valid in the context. Use \'-h\' or \'-help\' to get usage.", t) - } - DuplicateArgument(t: String) { - description("The argument was provided more than once.") - display("The argument '{}' was provided more than once. Use \'-h\' or \'-help\' to get usage.", t) - } - DuplicateValue(t: String) { - description("The argument value was provided more than once.") - display("The argument '{}' only need one value. Use \'-h\' or \'-help\' to get usage.", t) - } - // daemonize submodule error - DaemonFork { - description("Unable to fork.") - display("Unable to fork.") - } - DaemonSetsid { - description("Unable to create new session.") - display("Unable to create new session.") - } - DaemonRedirectStdio { - description("Unable to redirect standard streams to /dev/null.") - display("Unable to redirect standard streams to /dev/null.") - } - PidFileExist { - description("Pidfile path is existed yet.") - display("Pidfile path is existed yet.") - } - // epoll_context error - BadSyscall(err: std::io::Error) { - description("Return a bad syscall.") - display("Found bad syscall, error is {} .", err) - } - UnExpectedOperationType { - description("Unsupported notifier operation type.") - display("Unsupported Epoll notifier operation type.") - } - EpollWait(err: std::io::Error) { - description("Failed to execute epoll_wait syscall.") - display("Failed to execute epoll_wait syscall: {} .", err) - } - NoRegisterFd(t: i32) { - description("The fd is not registered in epoll.") - display("The fd {} is not registered in epoll.", t) - } - NoParkedFd(t: i32) { - description("Found no parked fd in registered.") - display("Found no parked fd {}.", t) - } - BadNotifierOperation { - description("Bad Notifier Operation.") - display("Notifier Operation non allowed.") - } - ChmodFailed(e: i32) { - description("Chmod command failed.") - display("Chmod command failed, os error {}", e) - } - OutOfBound(index: u64, bound: u64) { - description("Index out of bound of array") - display("Index :{} out of bound :{}", index, bound) - } - NodeDepthMismatch(target_dep: u32, real_dep: u32) { - description("Fdt structure nested node depth mismatch") - display("Desired node depth :{}, current node depth :{}", target_dep, real_dep) - } - NodeUnclosed(unclose: u32) { - description("Fdt structure block node unclose") - display("Still have {} node open when terminating the fdt", unclose) - } - IllegelPropertyPos { - description("Cann't add property outside the node") - display("Failed to add property because there is no open node") - } - IllegalString(s: String) { - description("The string for fdt should not contain null") - display("Failed to add string to fdt because of null character inside \"{}\"", s) - } - MemReserveOverlap { - description("The mem reserve entry should not overlap") - display("Failed to add overlapped mem reserve entries to fdt") - } - SetPropertyErr(s: String) { - description("Cann't set property for fdt node") - display("Failed to set {} property", s) - } - } - } -} - +pub use anyhow::Result; +pub use error::UtilError; use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; use log::debug; use once_cell::sync::Lazy; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 7f4cf4926..09376031c 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::collections::BTreeMap; -use std::fmt; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; @@ -20,7 +19,10 @@ use libc::{c_void, read}; use log::{error, warn}; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; -use crate::errors::{ErrorKind, Result, ResultExt}; +use crate::UtilError; +use anyhow::{anyhow, Context, Result}; +use std::fmt; +use std::fmt::Debug; const READY_EVENT_MAX: usize = 256; const AIO_PRFETCH_CYCLE_TIME: usize = 100; @@ -196,7 +198,7 @@ impl EventLoopContext { let mut events_map = self.events.write().unwrap(); if let Some(notifier) = events_map.get_mut(&event.raw_fd) { if let NotifierOperation::AddExclusion = event.op { - return Err(ErrorKind::BadNotifierOperation.into()); + return Err(anyhow!(UtilError::BadNotifierOperation)); } let mut event = event; @@ -222,7 +224,7 @@ impl EventLoopContext { .ctl(ControlOperation::Delete, parked_fd, EpollEvent::default())?; parked.status = EventStatus::Parked; } else { - return Err(ErrorKind::NoParkedFd(parked_fd).into()); + return Err(anyhow!(UtilError::NoParkedFd(parked_fd))); } } @@ -245,7 +247,7 @@ impl EventLoopContext { ) { let error_num = error.raw_os_error().unwrap(); if error_num != libc::EBADF && error_num != libc::ENOENT { - return Err(ErrorKind::BadSyscall(error).into()); + return Err(anyhow!(UtilError::BadSyscall(error))); } } } @@ -261,7 +263,7 @@ impl EventLoopContext { )?; parked.status = EventStatus::Alive; } else { - return Err(ErrorKind::NoParkedFd(parked_fd).into()); + return Err(anyhow!(UtilError::NoParkedFd(parked_fd))); } } @@ -269,7 +271,7 @@ impl EventLoopContext { self.gc.write().unwrap().push(event); } _ => { - return Err(ErrorKind::NoRegisterFd(event.raw_fd).into()); + return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); } } @@ -286,7 +288,7 @@ impl EventLoopContext { notifier.handlers.append(&mut event.handlers); } _ => { - return Err(ErrorKind::NoRegisterFd(event.raw_fd).into()); + return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); } } Ok(()) @@ -302,11 +304,13 @@ impl EventLoopContext { notifier.raw_fd, EpollEvent::default(), ) - .chain_err(|| format!("Failed to park event, event fd:{}", notifier.raw_fd))?; + .with_context(|| { + format!("Failed to park event, event fd:{}", notifier.raw_fd) + })?; notifier.status = EventStatus::Parked; } _ => { - return Err(ErrorKind::NoRegisterFd(event.raw_fd).into()); + return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); } } Ok(()) @@ -322,13 +326,13 @@ impl EventLoopContext { notifier.raw_fd, EpollEvent::new(notifier.event, &**notifier as *const _ as u64), ) - .chain_err(|| { + .with_context(|| { format!("Failed to resume event, event fd: {}", notifier.raw_fd) })?; notifier.status = EventStatus::Alive; } _ => { - return Err(ErrorKind::NoRegisterFd(event.raw_fd).into()); + return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); } } Ok(()) @@ -465,7 +469,7 @@ impl EventLoopContext { let ev_count = match self.epoll.wait(time_out, &mut self.ready_events[..]) { Ok(ev_count) => ev_count, Err(e) if e.raw_os_error() == Some(libc::EINTR) => 0, - Err(e) => return Err(ErrorKind::EpollWait(e).into()), + Err(e) => return Err(anyhow!(UtilError::EpollWait(e))), }; for i in 0..ev_count { diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 2f1f0d19e..5705ef722 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -77,10 +77,10 @@ //! ``` //! This programe will be trapped. -use error_chain::bail; +use anyhow::bail; -use crate::errors::Result; use crate::offset_of; +use anyhow::Result; // BPF Instruction classes /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/bpf_common.h#L7 diff --git a/util/src/syscall.rs b/util/src/syscall.rs index 6294e09f9..c2d7b16c1 100644 --- a/util/src/syscall.rs +++ b/util/src/syscall.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::bail; use libc::{c_void, syscall, SYS_mbind}; -use super::errors::Result; +use anyhow::Result; /// This function set memory policy for host NUMA node memory range. /// diff --git a/util/src/tap.rs b/util/src/tap.rs index a1bc805a6..369caaae6 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::{anyhow, bail, Context}; use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; @@ -18,7 +18,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; -use super::errors::{Result, ResultExt}; +use anyhow::Result; pub const TUN_F_CSUM: u32 = 1; pub const TUN_F_TSO4: u32 = 2; @@ -53,7 +53,7 @@ impl Tap { if let Some(name) = name { if name.len() > 15 { - return Err(format!("Open tap {} failed, name too long.", name).into()); + return Err(anyhow!("Open tap {} failed, name too long.", name)); } let mut ifr_name = [0_u8; 16]; @@ -74,15 +74,14 @@ impl Tap { .write(true) .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) .open(TUNTAP_PATH) - .chain_err(|| format!("Open {} failed.", TUNTAP_PATH))?; + .with_context(|| format!("Open {} failed.", TUNTAP_PATH))?; let ret = unsafe { ioctl_with_mut_ref(&file_, TUNSETIFF(), &mut if_req) }; if ret < 0 { - return Err(format!( + return Err(anyhow!( "Failed to set tap ifr flags, error is {}", std::io::Error::last_os_error() - ) - .into()); + )); } file = file_; @@ -92,21 +91,19 @@ impl Tap { File::from_raw_fd(fd) }; } else { - return Err(format!( + return Err(anyhow!( "Open tap failed, unsupported operation, error is {}", std::io::Error::last_os_error() - ) - .into()); + )); } let mut features = 0; let ret = unsafe { ioctl_with_mut_ref(&file, TUNGETFEATURES(), &mut features) }; if ret < 0 { - return Err(format!( + return Err(anyhow!( "Failed to get tap features, error is {}.", std::io::Error::last_os_error() - ) - .into()); + )); } if (features & IFF_MULTI_QUEUE == 0) && queue_pairs > 1 { @@ -122,7 +119,7 @@ impl Tap { pub fn set_offload(&self, flags: u32) -> Result<()> { let ret = unsafe { ioctl_with_val(&self.file, TUNSETOFFLOAD(), flags as libc::c_ulong) }; if ret < 0 { - return Err("ioctl TUNSETOFFLOAD failed.".to_string().into()); + return Err(anyhow!("ioctl TUNSETOFFLOAD failed.".to_string())); } Ok(()) @@ -131,7 +128,7 @@ impl Tap { pub fn set_hdr_size(&self, len: u32) -> Result<()> { let ret = unsafe { ioctl_with_ref(&self.file, TUNSETVNETHDRSZ(), &len) }; if ret < 0 { - return Err("ioctl TUNSETVNETHDRSZ failed.".to_string().into()); + return Err(anyhow!("ioctl TUNSETVNETHDRSZ failed.".to_string())); } Ok(()) diff --git a/util/src/trace.rs b/util/src/trace.rs index 3f6748505..043d85cb1 100644 --- a/util/src/trace.rs +++ b/util/src/trace.rs @@ -8,7 +8,7 @@ use arc_swap::ArcSwap; use log::error; use once_cell::sync::Lazy; -use crate::errors::{Result, ResultExt}; +use anyhow::{Context, Result}; static TRACE_MARKER_FD: Lazy> = Lazy::new(open_trace_marker); static TRACE_EVENTS: Lazy>> = @@ -19,7 +19,7 @@ fn open_trace_marker() -> Option { let proc_mounts_fd = match File::open(file) { Ok(fd) => fd, Err(e) => { - error!("Failed to open {}: {}", file, e); + error!("Failed to open {}: {:?}", file, e); return None; } }; @@ -34,7 +34,7 @@ fn open_trace_marker() -> Option { } } Err(e) => { - error!("Read {} error: {}.", &file, e); + error!("Read {} error: {:?}.", &file, e); return None; } } @@ -50,12 +50,12 @@ fn open_trace_marker() -> Option { let mut tracing_on_fd = match OpenOptions::new().write(true).open(&tracing_on) { Ok(fd) => fd, Err(e) => { - error!("Failed to open {}: {}", tracing_on, e); + error!("Failed to open {}: {:?}", tracing_on, e); return None; } }; if let Err(e) = tracing_on_fd.write(b"1") { - error!("Failed to enable tracing_on: {}", e); + error!("Failed to enable tracing_on: {:?}", e); return None; } @@ -63,7 +63,7 @@ fn open_trace_marker() -> Option { match OpenOptions::new().write(true).open(&trace_marker) { Ok(fd) => Some(fd), Err(e) => { - error!("Failed to open {}: {}", trace_marker, e); + error!("Failed to open {}: {:?}", trace_marker, e); None } } @@ -76,7 +76,7 @@ pub fn write_trace_marker(event: &str, msg: &str) { let msg = format!("[{}] {}", event, msg); if let Err(e) = TRACE_MARKER_FD.as_ref().unwrap().write(msg.as_bytes()) { - error!("Write trace_marker error: {}", e); + error!("Write trace_marker error: {:?}", e); } } @@ -95,14 +95,14 @@ macro_rules! ftrace { } pub fn enable_trace_events(file: &str) -> Result<()> { - let fd = File::open(file).chain_err(|| format!("Failed to open {}.", file))?; + let fd = File::open(file).with_context(|| format!("Failed to open {}.", file))?; let mut reader = BufReader::new(fd); loop { let mut buf = String::new(); let size = reader .read_line(&mut buf) - .chain_err(|| format!("Read {} error.", file))?; + .with_context(|| format!("Read {} error.", file))?; if size == 0 { return Ok(()); diff --git a/util/src/unix.rs b/util/src/unix.rs index 5f871a297..8e1e2eb52 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -10,20 +10,21 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::anyhow; use std::fs::File; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; -use error_chain::bail; use libc::{ c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, CMSG_LEN, CMSG_SPACE, MSG_NOSIGNAL, MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, }; use log::error; -use super::errors::{ErrorKind, Result, ResultExt}; +use crate::UtilError; +use anyhow::{bail, Context, Result}; /// This function returns the caller's thread ID(TID). pub fn gettid() -> u64 { @@ -39,7 +40,7 @@ pub fn limit_permission(path: &str) -> Result<()> { if ret == 0 { Ok(()) } else { - Err(ErrorKind::ChmodFailed(ret).into()) + Err(anyhow!(UtilError::ChmodFailed(ret))) } } @@ -115,7 +116,7 @@ pub fn do_mmap( ) }; if hva == libc::MAP_FAILED { - return Err(std::io::Error::last_os_error()).chain_err(|| "Mmap failed."); + return Err(std::io::Error::last_os_error()).with_context(|| "Mmap failed."); } if !dump_guest_core { set_memory_undumpable(hva, len); @@ -170,10 +171,10 @@ impl UnixSock { pub fn bind(&mut self, unlink: bool) -> Result<()> { if unlink { std::fs::remove_file(self.path.as_str()) - .chain_err(|| format!("Failed to remove socket file {}.", self.path.as_str()))?; + .with_context(|| format!("Failed to remove socket file {}.", self.path.as_str()))?; } let listener = UnixListener::bind(self.path.as_str()) - .chain_err(|| format!("Failed to bind the socket {}", self.path))?; + .with_context(|| format!("Failed to bind the socket {}", self.path))?; self.listener = Some(listener); Ok(()) @@ -186,7 +187,7 @@ impl UnixSock { .as_ref() .unwrap() .accept() - .chain_err(|| format!("Failed to accept the socket {}", self.path))?; + .with_context(|| format!("Failed to accept the socket {}", self.path))?; self.sock = Some(sock); Ok(()) @@ -198,7 +199,7 @@ impl UnixSock { pub fn server_connection_refuse(&mut self) -> Result<()> { // Refuse connection by finishing life cycle of stream fd from listener fd. - self.listener.as_ref().unwrap().accept().chain_err(|| { + self.listener.as_ref().unwrap().accept().with_context(|| { format!( "Failed to accept the socket for refused connection {}", self.path @@ -211,7 +212,7 @@ impl UnixSock { /// Unix socket stream create a connection for requests. pub fn connect(&mut self) -> Result<()> { let sock = UnixStream::connect(self.path.as_str()) - .chain_err(|| format!("Failed to connect the socket {}", self.path))?; + .with_context(|| format!("Failed to connect the socket {}", self.path))?; self.sock = Some(sock); Ok(()) diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index 38f8bc8ff..d6e7a5768 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -8,7 +8,8 @@ description = "Virtual function I/O" [dependencies] byteorder = "1.4.3" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.11.0" libc = "0.2" diff --git a/vfio/src/error.rs b/vfio/src/error.rs new file mode 100644 index 000000000..489218c32 --- /dev/null +++ b/vfio/src/error.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum VfioError { + #[error("PciErr")] + PciErr { + #[from] + source: pci::error::PciError, + }, + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("Hypervisor")] + Hypervisor { + #[from] + source: hypervisor::error::HypervisorError, + }, + #[error("Failed to add sub region at the BAR {0} in memory space.")] + AddRegBar(usize), + #[error("Vfio ioctl failed: {0}, error is: {1:?}")] + VfioIoctl(String, std::io::Error), +} diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 4ee431dbc..05d9a7ab8 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -10,25 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod errors { - use error_chain::error_chain; - - error_chain! { - links { - PciErr(pci::errors::Error, pci::errors::ErrorKind); - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind); - } - errors { - AddRegBar(id: usize) { - display("Failed to add sub region at the BAR {} in memory space.", id) - } - VfioIoctl(ioctl: String, error: std::io::Error) { - display("Vfio ioctl failed: {}, error is: {:?}", ioctl, error) - } - } - } -} +pub mod error; +pub use error::VfioError; mod vfio_dev; mod vfio_pci; @@ -73,7 +56,7 @@ fn create_kvm_vfio_device() -> Option { { Ok(fd) => Some(fd), Err(e) => { - error!("{}", e); + error!("{:?}", e); None } } diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index d1436001f..5bac11d56 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -22,7 +22,6 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; use kvm_bindings::{ kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, }; @@ -32,8 +31,9 @@ use vmm_sys_util::ioctl::{ }; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; -use super::errors::{ErrorKind, Result, ResultExt}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; +use crate::VfioError; +use anyhow::{anyhow, bail, Context, Result}; /// Refer to VFIO in https://github.com/torvalds/linux/blob/master/include/uapi/linux/vfio.h const IOMMU_GROUP: &str = "iommu_group"; @@ -119,27 +119,25 @@ impl VfioContainer { .read(true) .write(true) .open(CONTAINER_PATH) - .chain_err(|| format!("Failed to open {} for VFIO container.", CONTAINER_PATH))?; + .with_context(|| format!("Failed to open {} for VFIO container.", CONTAINER_PATH))?; // Ioctl is safe. Called file is `/dev/vfio/vfio` fd and we check the return. let v = unsafe { ioctl(&fd, VFIO_GET_API_VERSION()) }; if v as u32 != vfio::VFIO_API_VERSION { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_GET_API_VERSION".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); }; // Ioctl is safe. Called file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_val(&fd, VFIO_CHECK_EXTENSION(), vfio::VFIO_TYPE1v2_IOMMU.into()) }; if ret != 1 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_CHECK_EXTENSION".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(VfioContainer { @@ -162,11 +160,10 @@ impl VfioContainer { // Ioctl is safe. Called container file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_val(&self.fd, VFIO_SET_IOMMU(), val.into()) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_SET_IOMMU".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(()) } @@ -193,11 +190,10 @@ impl VfioContainer { // Ioctl is safe. Called container file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_IOMMU_MAP_DMA(), &map) }; if ret != 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_IOMMU_MAP_DMA".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(()) } @@ -222,16 +218,15 @@ impl VfioContainer { // Ioctl is safe. Called container file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_IOMMU_UNMAP_DMA(), &unmap) }; if ret != 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_IOMMU_UNMAP_DMA".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(()) } - fn add_listener_region(&self, fr: &FlatRange) -> address_space::errors::Result<()> { + fn add_listener_region(&self, fr: &FlatRange) -> address_space::Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } @@ -243,7 +238,7 @@ impl VfioContainer { None => bail!("Failed to get host address"), }; let userspace_addr = hva + fr.offset_in_region; - address_space::errors::ResultExt::chain_err( + address_space::Result::with_context( self.vfio_dma_map(guest_phys_addr, memory_size, userspace_addr), || { format!( @@ -255,22 +250,19 @@ impl VfioContainer { Ok(()) } - fn del_listener_region(&self, fr: &FlatRange) -> address_space::errors::Result<()> { + fn del_listener_region(&self, fr: &FlatRange) -> address_space::Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } let guest_phys_addr = fr.addr_range.base.raw_value(); let size = fr.addr_range.size; - address_space::errors::ResultExt::chain_err( - self.vfio_dma_unmap(guest_phys_addr, size), - || { - format!( - "Failed to do dma unmap: gpa 0x{:x}, size 0x{:x}.", - guest_phys_addr, size - ) - }, - )?; + address_space::Result::with_context(self.vfio_dma_unmap(guest_phys_addr, size), || { + format!( + "Failed to do dma unmap: gpa 0x{:x}, size 0x{:x}.", + guest_phys_addr, size + ) + })?; Ok(()) } } @@ -297,7 +289,7 @@ impl Listener for VfioContainer { range: Option<&FlatRange>, _evtfd: Option<&RegionIoEventFd>, req_type: ListenerReqType, - ) -> address_space::errors::Result<()> { + ) -> address_space::Result<()> { match req_type { ListenerReqType::AddRegion => { self.add_listener_region(range.unwrap())?; @@ -332,7 +324,7 @@ impl VfioGroup { .read(true) .write(true) .open(&group_path) - .chain_err(|| { + .with_context(|| { format!( "Failed to open {} for iommu_group.", group_path.to_str().unwrap() @@ -346,11 +338,10 @@ impl VfioGroup { // Safe as file is `iommu_group` fd, and we check the return. let ret = unsafe { ioctl_with_mut_ref(&file, VFIO_GROUP_GET_STATUS(), &mut status) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_GROUP_GET_STATUS".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } if status.flags != vfio::VFIO_GROUP_FLAGS_VIABLE { bail!( @@ -381,7 +372,7 @@ impl VfioGroup { match KVM_DEVICE_FD.as_ref() { Some(fd) => fd .set_device_attr(&attr) - .chain_err(|| "Failed to add group to kvm device.")?, + .with_context(|| "Failed to add group to kvm device.")?, None => bail!("Failed to create kvm device."), } Ok(()) @@ -401,7 +392,7 @@ impl VfioGroup { match KVM_DEVICE_FD.as_ref() { Some(fd) => fd .set_device_attr(&attr) - .chain_err(|| "Failed to delete group from kvm device.")?, + .with_context(|| "Failed to delete group from kvm device.")?, None => bail!("Kvm device hasn't been created."), } Ok(()) @@ -412,11 +403,10 @@ impl VfioGroup { // Safe as group is the owner of file, and we check the return. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_GROUP_SET_CONTAINER(), fd) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_GROUP_SET_CONTAINER".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } self.container = Arc::downgrade(container); Ok(()) @@ -452,7 +442,7 @@ impl VfioGroup { self.add_to_kvm_device()?; mem_as .register_listener(self.container.upgrade().unwrap()) - .chain_err(|| "Failed to register memory listener.")?; + .with_context(|| "Failed to register memory listener.")?; Ok(()) } } @@ -518,10 +508,11 @@ impl VfioDevice { bail!("No provided host PCI device, use -device vfio-pci,host=DDDD:BB:DD.F"); } - let group = Self::vfio_get_group(path, mem_as).chain_err(|| "Failed to get iommu group")?; + let group = + Self::vfio_get_group(path, mem_as).with_context(|| "Failed to get iommu group")?; let (name, fd) = - Self::vfio_get_device(&group, path).chain_err(|| "Failed to get vfio device")?; - let dev_info = Self::get_dev_info(&fd).chain_err(|| "Failed to get device info")?; + Self::vfio_get_device(&group, path).with_context(|| "Failed to get vfio device")?; + let dev_info = Self::get_dev_info(&fd).with_context(|| "Failed to get device info")?; let vfio_dev = Arc::new(Mutex::new(VfioDevice { fd, name, @@ -543,13 +534,13 @@ impl VfioDevice { .iter() .collect::() .read_link() - .chain_err(|| "Invalid iommu group path")?; + .with_context(|| "Invalid iommu group path")?; let group_name = iommu_group .file_name() - .chain_err(|| "Invalid iommu group name")?; + .with_context(|| "Invalid iommu group name")?; let mut group_id = 0; if let Some(n) = group_name.to_str() { - group_id = n.parse::().chain_err(|| "Invalid iommu group id")?; + group_id = n.parse::().with_context(|| "Invalid iommu group id")?; } if let Some(g) = GROUPS.lock().unwrap().get(&group_id) { @@ -578,7 +569,7 @@ impl VfioDevice { fn vfio_get_device(group: &VfioGroup, name: &Path) -> Result<(String, File)> { let mut dev_name: &str = ""; if let Some(n) = name.file_name() { - dev_name = n.to_str().chain_err(|| "Invalid device path")?; + dev_name = n.to_str().with_context(|| "Invalid device path")?; } for device in group.devices.lock().unwrap().iter() { @@ -588,16 +579,15 @@ impl VfioDevice { } let path: CString = CString::new(dev_name.as_bytes()) - .chain_err(|| "Failed to convert device name to CString type of data")?; + .with_context(|| "Failed to convert device name to CString type of data")?; let ptr = path.as_ptr(); // Safe as group is the owner of file and make sure ptr is valid. let fd = unsafe { ioctl_with_ptr(&group.fd, VFIO_GROUP_GET_DEVICE_FD(), ptr) }; if fd < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_GROUP_GET_DEVICE_FD".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } // Safe as we have verified that fd is a valid FD. @@ -620,11 +610,10 @@ impl VfioDevice { || dev_info.num_regions < vfio::VFIO_PCI_CONFIG_REGION_INDEX + 1 || dev_info.num_irqs < vfio::VFIO_PCI_MSIX_IRQ_INDEX + 1 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_DEVICE_GET_INFO".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(VfioDevInfo { @@ -655,11 +644,10 @@ impl VfioDevice { ) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_DEVICE_GET_REGION_INFO".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } // Safe as we make sure there is enough memory space to convert cap info into @@ -701,11 +689,10 @@ impl VfioDevice { // Safe as device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_mut_ref(&self.fd, VFIO_DEVICE_GET_REGION_INFO(), &mut info) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_DEVICE_GET_REGION_INFO".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(info) @@ -716,13 +703,13 @@ impl VfioDevice { for index in vfio::VFIO_PCI_BAR0_REGION_INDEX..vfio::VFIO_PCI_ROM_REGION_INDEX { let info = self .region_info(index) - .chain_err(|| "Fail to get region info")?; + .with_context(|| "Fail to get region info")?; let mut mmaps = Vec::new(); if info.size > 0 { mmaps = self .region_mmap_info(info) - .chain_err(|| "Fail to get region mmap info")?; + .with_context(|| "Fail to get region mmap info")?; } regions.push(VfioRegion { @@ -780,7 +767,7 @@ impl VfioDevice { pub fn read_region(&self, buf: &mut [u8], region_offset: u64, addr: u64) -> Result<()> { self.fd .read_exact_at(buf, region_offset + addr) - .chain_err(|| "Failed to read vfio region")?; + .with_context(|| "Failed to read vfio region")?; Ok(()) } @@ -795,7 +782,7 @@ impl VfioDevice { pub fn write_region(&self, buf: &[u8], region_offset: u64, addr: u64) -> Result<()> { self.fd .write_all_at(buf, region_offset + addr) - .chain_err(|| "Failed to write vfio region")?; + .with_context(|| "Failed to write vfio region")?; Ok(()) } @@ -825,11 +812,10 @@ impl VfioDevice { // Safe as device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_DEVICE_SET_IRQS".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } Ok(()) } @@ -854,11 +840,10 @@ impl VfioDevice { // Safe as device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_DEVICE_SET_IRQS".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } self.nr_vectors = 0; Ok(()) @@ -869,11 +854,10 @@ impl VfioDevice { if self.dev_info.flags & vfio::VFIO_DEVICE_FLAGS_RESET != 0 { let ret = unsafe { ioctl(&self.fd, VFIO_DEVICE_RESET()) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_DEVICE_RESET".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 99b521034..f9e188e75 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -16,9 +16,10 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; +use crate::VfioError; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; use log::error; #[cfg(target_arch = "aarch64")] @@ -29,7 +30,6 @@ use pci::config::{ HEADER_TYPE, IO_BASE_ADDR_MASK, MEM_BASE_ADDR_MASK, PCIE_CONFIG_SPACE_SIZE, PCI_CONFIG_SPACE_SIZE, REG_SIZE, }; -use pci::errors::Result as PciResult; use pci::msix::{ is_msix_enabled, update_dev_id, Msix, MSIX_CAP_CONTROL, MSIX_CAP_ENABLE, MSIX_CAP_FUNC_MASK, MSIX_CAP_ID, MSIX_CAP_SIZE, MSIX_CAP_TABLE, MSIX_TABLE_BIR, MSIX_TABLE_ENTRY_SIZE, @@ -44,7 +44,6 @@ use vfio_bindings::bindings::vfio; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_mut_ref; -use super::errors::{ErrorKind, Result, ResultExt}; use crate::vfio_dev::*; use crate::{CONTAINERS, GROUPS}; @@ -147,11 +146,10 @@ impl VfioPciDevice { let ret = unsafe { ioctl_with_mut_ref(&locked_dev.fd, VFIO_DEVICE_GET_REGION_INFO(), &mut info) }; if ret < 0 { - return Err(ErrorKind::VfioIoctl( + return Err(anyhow!(VfioError::VfioIoctl( "VFIO_GET_PCI_CONFIG_INFO".to_string(), std::io::Error::last_os_error(), - ) - .into()); + ))); } self.config_size = info.size; @@ -238,7 +236,7 @@ impl VfioPciDevice { let n = locked_dev.dev_info.num_irqs; let vfio_irq = locked_dev .get_irqs_info(n) - .chain_err(|| "Failed to get vfio irqs info")?; + .with_context(|| "Failed to get vfio irqs info")?; let cap_offset = self.pci_config.find_pci_cap(MSIX_CAP_ID); let table = le_read_u32( @@ -277,7 +275,7 @@ impl VfioPciDevice { let locked_dev = self.vfio_device.lock().unwrap(); let mut infos = locked_dev .get_regions_info() - .chain_err(|| "Failed get vfio device regions info")?; + .with_context(|| "Failed get vfio device regions info")?; for i in 0..PCI_ROM_SLOT { let mut data = vec![0_u8; 4]; @@ -312,11 +310,11 @@ impl VfioPciDevice { let msix_info = self .msix_info .as_ref() - .chain_err(|| "Failed to get MSIX info")?; + .with_context(|| "Failed to get MSIX info")?; let vfio_bar = vfio_bars .get_mut(msix_info.table.table_bar as usize) - .chain_err(|| "Failed to get vfio bar info")?; + .with_context(|| "Failed to get vfio bar info")?; let region = &mut vfio_bar.vfio_region; // If MSI-X area already setups or does not support mapping, we shall just return. if region.mmaps.len() != 1 @@ -359,14 +357,14 @@ impl VfioPciDevice { let msix_info = self .msix_info .as_ref() - .chain_err(|| "Failed to get MSIX info")?; + .with_context(|| "Failed to get MSIX info")?; let table_bar = msix_info.table.table_bar; let table_offset = msix_info.table.table_offset; let table_size = msix_info.table.table_size; // Create a separate region for MSI-X table, VFIO won't allow to map the MSI-X table area. let table_ops = self .get_table_region_ops() - .chain_err(|| "Failed to get table region ops")?; + .with_context(|| "Failed to get table region ops")?; let bar_ops = self.get_bar_region_ops(); for i in 0..PCI_ROM_SLOT { @@ -374,7 +372,7 @@ impl VfioPciDevice { let mut bars = self.vfio_bars.lock().unwrap(); let bar = bars .get_mut(i as usize) - .chain_err(|| "Failed to get bar info")?; + .with_context(|| "Failed to get bar info")?; // Skip unimplemented bar and the upper half of 64 bit bar. if bar.size == 0 { continue; @@ -384,7 +382,7 @@ impl VfioPciDevice { let mut vfio_bars = self.vfio_bars.lock().unwrap(); let vfio_bar = vfio_bars .get_mut(i as usize) - .chain_err(|| "Failed to get vfio bar info")?; + .with_context(|| "Failed to get vfio bar info")?; let size = vfio_bar.size; let region = Region::init_container_region(size); @@ -394,12 +392,12 @@ impl VfioPciDevice { Region::init_io_region(table_size as u64, table_ops.clone()), table_offset, ) - .chain_err(|| ErrorKind::AddRegBar(i as usize))?; + .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; if table_offset > 0 { region .add_subregion(Region::init_io_region(table_offset, bar_ops.clone()), 0) - .chain_err(|| ErrorKind::AddRegBar(i as usize))?; + .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; } if table_offset + table_size < size { @@ -411,13 +409,13 @@ impl VfioPciDevice { ), table_offset + table_size, ) - .chain_err(|| ErrorKind::AddRegBar(i as usize))?; + .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; } region } else { region .add_subregion(Region::init_io_region(size, bar_ops.clone()), 0) - .chain_err(|| ErrorKind::AddRegBar(i as usize))?; + .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; region }; @@ -439,7 +437,7 @@ impl VfioPciDevice { let msix_info = self .msix_info .as_ref() - .chain_err(|| "Failed to get MSIX info")?; + .with_context(|| "Failed to get MSIX info")?; let table_size = msix_info.table.table_size as u32; let cap_offset = self.pci_config.find_pci_cap(MSIX_CAP_ID); @@ -658,7 +656,7 @@ impl VfioPciDevice { let mut bars = self.vfio_bars.lock().unwrap(); let bar = bars .get_mut(i as usize) - .chain_err(|| "Failed to get bar info")?; + .with_context(|| "Failed to get bar info")?; let region = &mut bar.vfio_region; // If bar region already setups or does not support mapping, just process the nest. if region.size == 0 || region.guest_phys_addr == gpa { @@ -697,12 +695,12 @@ impl VfioPciDevice { .pci_config .bars .get_mut(i as usize) - .chain_err(|| "Failed to get pci bar info")?; + .with_context(|| "Failed to get pci bar info")?; bar.region .as_ref() .unwrap() .add_subregion(ram_device, mmap.offset) - .chain_err(|| ErrorKind::AddRegBar(i as usize))?; + .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; } } Ok(()) @@ -735,7 +733,7 @@ impl VfioPciDevice { .lock() .unwrap() .enable_irqs(get_irq_rawfds(&gsi_routes, 0, 1), 0) - .chain_err(|| "Failed enable irqfds in kvm")?; + .with_context(|| "Failed enable irqfds in kvm")?; Ok(()) } @@ -745,7 +743,7 @@ impl VfioPciDevice { .lock() .unwrap() .disable_irqs() - .chain_err(|| "Failed disable irqfds in kvm")?; + .with_context(|| "Failed disable irqfds in kvm")?; Ok(()) } @@ -800,30 +798,28 @@ impl VfioPciDevice { } impl PciDevOps for VfioPciDevice { - fn init_write_mask(&mut self) -> PciResult<()> { + fn init_write_mask(&mut self) -> pci::Result<()> { self.pci_config.init_common_write_mask() } - fn init_write_clear_mask(&mut self) -> PciResult<()> { + fn init_write_clear_mask(&mut self) -> pci::Result<()> { self.pci_config.init_common_write_clear_mask() } - fn realize(mut self) -> PciResult<()> { - use pci::errors::ResultExt as PciResultExt; - + fn realize(mut self) -> pci::Result<()> { self.init_write_mask()?; self.init_write_clear_mask()?; - PciResultExt::chain_err(self.vfio_device.lock().unwrap().reset(), || { + pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Failed to reset vfio device" })?; - PciResultExt::chain_err(self.get_pci_config(), || { + pci::Result::with_context(self.get_pci_config(), || { "Failed to get vfio device pci config space" })?; - PciResultExt::chain_err(self.pci_config_reset(), || { + pci::Result::with_context(self.pci_config_reset(), || { "Failed to reset vfio device pci config space" })?; - PciResultExt::chain_err( + pci::Result::with_context( init_multifunction( self.multi_func, &mut self.pci_config.config, @@ -845,14 +841,14 @@ impl PciDevOps for VfioPciDevice { self.dev_id = Arc::new(AtomicU16::new(self.set_dev_id(bus_num, self.devfn))); } - self.msix_info = Some(PciResultExt::chain_err(self.get_msix_info(), || { + self.msix_info = Some(pci::Result::with_context(self.get_msix_info(), || { "Failed to get MSI-X info" })?); - self.vfio_bars = Arc::new(Mutex::new(PciResultExt::chain_err( + self.vfio_bars = Arc::new(Mutex::new(pci::Result::with_context( self.bar_region_info(), || "Failed to get bar region info", )?)); - PciResultExt::chain_err(self.register_bars(), || "Failed to register bars")?; + pci::Result::with_context(self.register_bars(), || "Failed to register bars")?; let devfn = self.devfn; let dev = Arc::new(Mutex::new(self)); @@ -872,9 +868,9 @@ impl PciDevOps for VfioPciDevice { Ok(()) } - fn unrealize(&mut self) -> PciResult<()> { + fn unrealize(&mut self) -> pci::Result<()> { if let Err(e) = VfioPciDevice::unrealize(self) { - error!("{}", e.display_chain()); + error!("{}", format!("{:?}", e)); bail!("Failed to unrealize vfio-pci."); } Ok(()) @@ -969,13 +965,13 @@ impl PciDevOps for VfioPciDevice { &locked_parent_bus.io_region, &locked_parent_bus.mem_region, ) { - error!("Failed to update bar, error is {}", e.display_chain()); + error!("Failed to update bar, error is {}", format!("{:?}", e)); return; } drop(locked_parent_bus); if let Err(e) = self.setup_bars_mmap() { - error!("Failed to map bar regions, error is {}", e.display_chain()); + error!("Failed to map bar regions, error is {}", format!("{:?}", e)); } } } else if ranges_overlap(offset, end, BAR_0 as usize, (BAR_5 as usize) + REG_SIZE) { @@ -990,7 +986,7 @@ impl PciDevOps for VfioPciDevice { &locked_parent_bus.io_region, &locked_parent_bus.mem_region, ) { - error!("Failed to update bar, error is {}", e.display_chain()); + error!("Failed to update bar, error is {}", format!("{:?}", e)); } } } else if ranges_overlap(offset, end, cap_offset, cap_offset + MSIX_CAP_SIZE as usize) { @@ -1001,11 +997,11 @@ impl PciDevOps for VfioPciDevice { if !was_enable && is_enable { if let Err(e) = self.vfio_enable_msix() { - error!("{}\nFailed to enable MSI-X.", e.display_chain()); + error!("{}\nFailed to enable MSI-X.", format!("{:?}", e)); } } else if was_enable && !is_enable { if let Err(e) = self.vfio_disable_msix() { - error!("{}\nFailed to disable MSI-X.", e.display_chain()); + error!("{}\nFailed to disable MSI-X.", format!("{:?}", e)); } } } else { @@ -1018,10 +1014,8 @@ impl PciDevOps for VfioPciDevice { self.name.clone() } - fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { - use pci::errors::ResultExt as PciResultExt; - - PciResultExt::chain_err(self.vfio_device.lock().unwrap().reset(), || { + fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { + pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Fail to reset vfio dev" }) } diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 7367b7c6b..1016a5099 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -10,7 +10,8 @@ description = "Provide virtio fs for VM" errno = "0.2.7" log = "0.4.8" libc = ">=0.2.71" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" vmm-sys-util = ">=0.7.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index f969a4fa7..48fae02cf 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -14,7 +14,7 @@ const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const MAX_STRING_LENGTH: usize = 255; -use crate::errors::{Result, ResultExt}; +use anyhow::{bail, Context, Result}; use std::path::PathBuf; use util::arg_parser::{Arg, ArgMatches, ArgParser}; @@ -114,13 +114,13 @@ pub fn create_fs_config(args: &ArgMatches) -> Result { if let Some(rlimit_nofile) = args.value_of("rlimit nofile") { let limit = rlimit_nofile .parse::() - .chain_err(|| "Failed to parse rlimit nofile")?; + .with_context(|| "Failed to parse rlimit nofile")?; fs_config.rlimit_nofile = Some(limit); } fs_config .check_config() - .chain_err(|| "Precheck failed, Config is unhealthy, stop running")?; + .with_context(|| "Precheck failed, Config is unhealthy, stop running")?; Ok(fs_config) } diff --git a/vhost_user_fs/src/error.rs b/vhost_user_fs/src/error.rs new file mode 100644 index 000000000..b8913f199 --- /dev/null +++ b/vhost_user_fs/src/error.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum VhostUserFsError { + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Virtio")] + Virtio { + #[from] + source: virtio::error::VirtioError, + }, + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, +} diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 1b618176d..b7c437de7 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -21,7 +21,7 @@ const ROOT_INODE: usize = 1; use super::fs_ops::*; use super::fuse_msg::*; -use crate::errors::{Result, ResultExt}; +use anyhow::{bail, Context, Result}; use std::collections::{BTreeMap, HashMap}; use std::ffi::CString; use std::fs::{read_to_string, File}; @@ -268,21 +268,21 @@ pub fn set_rlimit_nofile(limit: u64) -> Result<()> { } let max_file_str = - read_to_string("/proc/sys/fs/file-max").chain_err(|| "Failed to read file-max")?; + read_to_string("/proc/sys/fs/file-max").with_context(|| "Failed to read file-max")?; let max_file = max_file_str .trim() .parse::() - .chain_err(|| "Failed to convert the string of max files")?; + .with_context(|| "Failed to convert the string of max files")?; if limit > max_file { bail!("The limit {} exceeds maximum of files {}", limit, max_file); } let nr_open_str = - read_to_string("/proc/sys/fs/nr_open").chain_err(|| "Failed to read nr_open")?; + read_to_string("/proc/sys/fs/nr_open").with_context(|| "Failed to read nr_open")?; let max_file = nr_open_str .trim() .parse::() - .chain_err(|| "Failed to convert the string of nr_open")?; + .with_context(|| "Failed to convert the string of nr_open")?; if limit > max_file { bail!( "The limit {} exceeds maximum of nr_open {}", diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 9d9830f05..40c88906d 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -146,10 +146,9 @@ use std::mem::size_of; use std::sync::Arc; use address_space::AddressSpace; -use error_chain::ChainedError; use util::byte_code::ByteCode; -use crate::errors::{Result, ResultExt}; +use anyhow::{bail, Context, Result}; use virtio::ElemIovec; /// Get the buffers from the element of virtio queue to parse fuse message or @@ -200,7 +199,7 @@ impl FuseBuffer { let read_count = cmp::min(slice.len(), buf.len as usize); sys_mem .read(&mut slice, buf.addr, read_count as u64) - .chain_err(|| "Failed to read buffer for fuse req")?; + .with_context(|| "Failed to read buffer for fuse req")?; offset += read_count; } @@ -271,7 +270,7 @@ impl FuseBuffer { sys_mem .read(&mut slice, buf.addr, read_count as u64) - .chain_err(|| "Failed to read buffer for fuse req")?; + .with_context(|| "Failed to read buffer for fuse req")?; self.bytes_processed += read_count; offset += read_count; @@ -335,7 +334,7 @@ impl FuseBuffer { sys_mem .write(&mut slice, buf.addr, write_count as u64) - .chain_err(|| "Failed to read buffer for fuse req")?; + .with_context(|| "Failed to read buffer for fuse req")?; self.bytes_processed += write_count; offset += write_count; @@ -523,9 +522,8 @@ pub fn reply_fuse_msg( if let Err(e) = writer.write_obj(sys_mem, &fuse_out_header) { error!( - "Failed to write out_header of fuse msg {}, {}", - in_header.opcode, - e.display_chain(), + "Failed to write out_header of fuse msg {}, {:?}", + in_header.opcode, e, ); written_len = 0_u32; }; @@ -535,9 +533,8 @@ pub fn reply_fuse_msg( for fuse_iov in body.iter() { if let Err(e) = writer.write_slice(sys_mem, fuse_iov.body, fuse_iov.len) { error!( - "Failed to write the body of fuse msg {}, {}", - in_header.opcode, - e.display_chain(), + "Failed to write the body of fuse msg {}, {:?}", + in_header.opcode, e, ); written_len = 0_u32; } diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index cb2289a11..28da4336b 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -15,7 +15,6 @@ const MAX_WRITE_SIZE: u32 = 1 << 20; use super::fs::FileSystem; use super::fuse_msg::*; use address_space::AddressSpace; -use error_chain::ChainedError; use std::convert::TryInto; use std::ffi::CString; use std::mem; @@ -59,7 +58,7 @@ pub fn do_fuse_lookup( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for lookup, {}", e.display_chain()); + error!("Failed to read name for lookup, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -113,7 +112,7 @@ pub fn do_fuse_forget( let forget_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for forget_in, {}", e.display_chain()); + error!("Failed to read object for forget_in, {:?}", e); return 0_u32; } }; @@ -183,10 +182,7 @@ pub fn do_fuse_setattr( let setattr_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for setattr_in, {}", - e.display_chain() - ); + error!("Failed to read object for setattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -270,7 +266,7 @@ pub fn do_fuse_symlink( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for symlink, {}", e.display_chain()); + error!("Failed to read name for symlink, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -278,10 +274,7 @@ pub fn do_fuse_symlink( let link_name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!( - "Failed to read link name for symlink, {}", - e.display_chain() - ); + error!("Failed to read link name for symlink, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -339,7 +332,7 @@ pub fn do_fuse_mknod( let mknod_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for mknod_in, {}", e.display_chain()); + error!("Failed to read object for mknod_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -347,7 +340,7 @@ pub fn do_fuse_mknod( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for mknod, {}", e.display_chain()); + error!("Failed to read name for mknod, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -405,7 +398,7 @@ pub fn do_fuse_mkdir( let mkdir_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for mkdir_in, {}", e.display_chain()); + error!("Failed to read object for mkdir_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -413,7 +406,7 @@ pub fn do_fuse_mkdir( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for mkdir, {}", e.display_chain()); + error!("Failed to read name for mkdir, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -471,7 +464,7 @@ pub fn do_fuse_unlink( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for unlink, {}", e.display_chain()); + error!("Failed to read name for unlink, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -504,7 +497,7 @@ pub fn do_fuse_rmdir( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for rmdir, {}", e.display_chain()); + error!("Failed to read name for rmdir, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -537,7 +530,7 @@ pub fn do_fuse_rename( let rename_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for rename_in, {}", e.display_chain()); + error!("Failed to read object for rename_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -545,7 +538,7 @@ pub fn do_fuse_rename( let oldname = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read old name for rename, {}", e.display_chain()); + error!("Failed to read old name for rename, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -553,7 +546,7 @@ pub fn do_fuse_rename( let newname = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read new name for rename, {}", e.display_chain()); + error!("Failed to read new name for rename, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -590,7 +583,7 @@ pub fn do_fuse_link( let link_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for link_in, {}", e.display_chain()); + error!("Failed to read object for link_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -598,7 +591,7 @@ pub fn do_fuse_link( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for link, {}", e.display_chain()); + error!("Failed to read name for link, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -659,7 +652,7 @@ pub fn do_fuse_open( let open_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for open_in, {}", e.display_chain()); + error!("Failed to read object for open_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -708,7 +701,7 @@ pub fn do_fuse_read( let read_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for read_in, {}", e.display_chain()); + error!("Failed to read object for read_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -733,7 +726,7 @@ pub fn do_fuse_read( sys_mem.write_object(&f_ret, buf_header.addr).unwrap(); } Err(e) => { - error!("Failed to access file for reading, {}", e.display_chain()); + error!("Failed to access file for reading, {:?}", e); } }; @@ -762,7 +755,7 @@ pub fn do_fuse_write( let write_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for write_in, {}", e.display_chain()); + error!("Failed to read object for write_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -784,7 +777,7 @@ pub fn do_fuse_write( ) } Err(e) => { - error!("Failed to access file for writing, {}", e.display_chain()); + error!("Failed to access file for writing, {:?}", e); reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize) } } @@ -845,10 +838,7 @@ pub fn do_fuse_release( let release_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for release_in, {}", - e.display_chain() - ); + error!("Failed to read object for release_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -876,7 +866,7 @@ pub fn do_fuse_fsync( let fsync_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read name for fsync_in, {}", e.display_chain()); + error!("Failed to read name for fsync_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -906,10 +896,7 @@ pub fn do_fuse_setxattr( let setxattr_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for setxattr_in, {}", - e.display_chain() - ); + error!("Failed to read object for setxattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -917,7 +904,7 @@ pub fn do_fuse_setxattr( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for setxattr_in, {}", e.display_chain()); + error!("Failed to read name for setxattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -925,10 +912,7 @@ pub fn do_fuse_setxattr( let value = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!( - "Failed to read value for setxattr_in, {}", - e.display_chain() - ); + error!("Failed to read value for setxattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -963,10 +947,7 @@ pub fn do_fuse_getxattr( let getxattr_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for getxattr_in, {}", - e.display_chain() - ); + error!("Failed to read object for getxattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -974,7 +955,7 @@ pub fn do_fuse_getxattr( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!("Failed to read name for getxattr_in, {}", e.display_chain()); + error!("Failed to read name for getxattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1017,10 +998,7 @@ pub fn do_fuse_listxattr( let getxattr_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for listxattr_in, {}", - e.display_chain() - ); + error!("Failed to read object for listxattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1063,10 +1041,7 @@ pub fn do_fuse_removexattr( let name = match reader.read_cstring(sys_mem) { Ok(s) => s, Err(e) => { - error!( - "Failed to read name for removexattr_in, {}", - e.display_chain() - ); + error!("Failed to read name for removexattr_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1098,7 +1073,7 @@ pub fn do_fuse_flush( let flush_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for flush_in, {}", e.display_chain()); + error!("Failed to read object for flush_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1130,7 +1105,7 @@ pub fn do_fuse_init( let init_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for init_in, {}", e.display_chain()); + error!("Failed to read object for init_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1182,10 +1157,7 @@ pub fn do_fuse_opendir( let _open_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for opendir_in, {}", - e.display_chain() - ); + error!("Failed to read object for opendir_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1234,10 +1206,7 @@ pub fn do_fuse_readdir( let read_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for readdir_in, {}", - e.display_chain() - ); + error!("Failed to read object for readdir_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1284,10 +1253,7 @@ pub fn do_fuse_releasedir( let release_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for releasedir_in, {}", - e.display_chain() - ); + error!("Failed to read object for releasedir_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1315,7 +1281,7 @@ pub fn do_fuse_fsyncdir( let fsync_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for fsync_in, {}", e.display_chain()); + error!("Failed to read object for fsync_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1344,7 +1310,7 @@ pub fn do_fuse_getlk( let lk_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for get_lk_in, {}", e.display_chain()); + error!("Failed to read object for get_lk_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1427,7 +1393,7 @@ pub fn do_fuse_setlk( let lk_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for set_lk_in, {}", e.display_chain()); + error!("Failed to read object for set_lk_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1458,7 +1424,7 @@ pub fn do_fuse_setlkw( let lk_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for setlkw_in, {}", e.display_chain()); + error!("Failed to read object for setlkw_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1489,7 +1455,7 @@ pub fn do_fuse_create( let create_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for create_in, {}", e.display_chain()); + error!("Failed to read object for create_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1497,10 +1463,7 @@ pub fn do_fuse_create( let name = match reader.read_cstring(sys_mem) { Ok(string) => string, Err(e) => { - error!( - "Failed to read name for creating file, {}", - e.display_chain() - ); + error!("Failed to read name for creating file, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1587,10 +1550,7 @@ pub fn do_fuse_batch_forget( let batch_forget_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for batch_forget_in, {}", - e.display_chain() - ); + error!("Failed to read object for batch_forget_in, {:?}", e); return 0_u32; } }; @@ -1599,10 +1559,7 @@ pub fn do_fuse_batch_forget( let forget_data_in = match reader.read_obj::(sys_mem) { Ok(data) => data, Err(e) => { - error!( - "Failed to read object for forget_date_in, {}", - e.display_chain() - ); + error!("Failed to read object for forget_date_in, {:?}", e); return 0; } }; @@ -1634,10 +1591,7 @@ pub fn do_fuse_fallocate( let fallocate_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for fallocate_in, {}", - e.display_chain() - ); + error!("Failed to read object for fallocate_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1671,10 +1625,7 @@ pub fn do_fuse_readdirplus( let read_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!( - "Failed to read object for readdirplus_in, {}", - e.display_chain() - ); + error!("Failed to read object for readdirplus_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; @@ -1721,7 +1672,7 @@ pub fn do_fuse_lseek( let lseek_in = match reader.read_obj::(sys_mem) { Ok(d) => d, Err(e) => { - error!("Failed to read object for lseek_in, {}", e.display_chain()); + error!("Failed to read object for lseek_in, {:?}", e); return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } }; diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index b35aa0e57..1d5f98393 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -14,7 +14,6 @@ use super::fs::FileSystem; use super::fuse_msg::*; use super::fuse_proc::*; use address_space::AddressSpace; -use error_chain::ChainedError; use std::sync::{Arc, Mutex}; use virtio::Element; @@ -53,10 +52,7 @@ impl FuseReq { let in_header = match self.reader.read_obj::(sys_mem) { Ok(data) => data, Err(err) => { - error!( - "Failed to read the header of fuse msg, {}", - err.display_chain(), - ); + error!("Failed to read the header of fuse msg, {:?}", err,); return (self.desc_index, 0); } }; diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index 005eaff7c..a26ce8cbd 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; extern crate address_space; @@ -29,15 +27,5 @@ pub mod vhost_user_fs; pub mod vhost_user_server; pub mod virtio_fs; -pub mod errors { - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - Virtio(virtio::errors::Error, virtio::errors::ErrorKind); - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - } - foreign_links { - Io(std::io::Error); - } - } -} +pub mod error; +pub use error::VhostUserFsError; diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 637213f00..7c45e797d 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -10,29 +10,66 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; extern crate vhost_user_fs; +use anyhow::{Context, Result}; use machine_manager::event_loop::EventLoop; +use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; +use thiserror::Error; use util::{arg_parser, logger}; use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; use vhost_user_fs::vhost_user_fs::VhostUserFs; -error_chain! { - links { - VhostUserFs(vhost_user_fs::errors::Error, vhost_user_fs::errors::ErrorKind); - Util(util::errors::Error, util::errors::ErrorKind); +#[derive(Error, Debug)] +pub enum MainError { + #[error("VhostUserFs")] + VhostUserFs { + #[from] + source: vhost_user_fs::error::VhostUserFsError, + }, + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, +} + +pub trait ExitCode { + /// Returns the value to use as the exit status. + fn code(self) -> i32; +} + +impl ExitCode for i32 { + fn code(self) -> i32 { + self } - foreign_links { - Io(std::io::Error); +} + +impl ExitCode for () { + fn code(self) -> i32 { + 0 } } -quick_main!(run); +fn main() { + ::std::process::exit(match run() { + Ok(ret) => ExitCode::code(ret), + Err(ref e) => { + write!(&mut ::std::io::stderr(), "{}", format!("{:?}", e)) + .expect("Error writing to stderr"); + + 1 + } + }); +} fn run() -> Result<()> { let cmd_args = create_args_parser().get_matches()?; @@ -44,7 +81,7 @@ fn run() -> Result<()> { match real_main(&cmd_args) { Ok(()) => info!("EventLoop over, Vm exit"), Err(ref e) => { - error!("{}", error_chain::ChainedError::display_chain(e)); + error!("{:?}", e); } } @@ -58,7 +95,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { EventLoop::object_init(&None)?; let vhost_user_fs = Arc::new(Mutex::new( - VhostUserFs::new(fsconfig).chain_err(|| "Failed to create vhost use fs")?, + VhostUserFs::new(fsconfig).with_context(|| "Failed to create vhost use fs")?, )); EventLoop::set_manager(vhost_user_fs.clone(), None); @@ -66,16 +103,16 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { .lock() .unwrap() .add_event_notifier() - .chain_err(|| "Failed to add event")?; + .with_context(|| "Failed to add event")?; - EventLoop::loop_run().chain_err(|| "EventLoop exits unexpectedly: error occurs")?; + EventLoop::loop_run().with_context(|| "EventLoop exits unexpectedly: error occurs")?; Ok(()) } fn init_log(logfile_path: String) -> Result<()> { if logfile_path.is_empty() { logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) - .chain_err(|| "Failed to init logger")?; + .with_context(|| "Failed to init logger")?; } else { let logfile = std::fs::OpenOptions::new() .read(false) @@ -84,9 +121,9 @@ fn init_log(logfile_path: String) -> Result<()> { .create(true) .mode(0o640) .open(logfile_path.clone()) - .chain_err(|| format!("Failed to open log file {}", logfile_path))?; + .with_context(|| format!("Failed to open log file {}", logfile_path))?; logger::init_logger_with_env(Some(Box::new(logfile))) - .chain_err(|| format!("Failed to init logger {}", logfile_path))?; + .with_context(|| format!("Failed to init logger {}", logfile_path))?; } Ok(()) diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index eff69c188..15d19ff0d 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -13,7 +13,6 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; -use error_chain::ChainedError; use vmm_sys_util::epoll::EventSet; use machine_manager::event_loop::EventLoop; @@ -26,7 +25,7 @@ use super::fs::set_rlimit_nofile; use super::vhost_user_server::VhostUserServerHandler; use super::virtio_fs::VirtioFs; -use crate::errors::{Result, ResultExt}; +use anyhow::{Context, Result}; /// The vhost-user filesystem device contains virtio fs device and the vhost-user /// server which can be connected with the vhost-user client in StratoVirt. @@ -51,17 +50,11 @@ impl CreateEventNotifier for VhostUserServerHandler { let mut notifiers = Vec::new(); if self.sock.domain.is_accepted() { if let Err(e) = self.sock.domain.server_connection_refuse() { - error!( - "Failed to refuse socket for vhost user server, {}", - e.display_chain() - ); + error!("Failed to refuse socket for vhost user server, {:?}", e); } return None; } else if let Err(e) = self.sock.domain.accept() { - error!( - "Failed to accept the socket for vhost user server, {}", - e.display_chain() - ); + error!("Failed to accept the socket for vhost user server, {:?}", e); return None; } @@ -70,10 +63,7 @@ impl CreateEventNotifier for VhostUserServerHandler { if event == EventSet::IN { let mut lock_server_handler = server_handler.lock().unwrap(); if let Err(e) = lock_server_handler.handle_request() { - error!( - "Failed to handle request for vhost user server, {}", - e.display_chain() - ); + error!("Failed to handle request for vhost user server, {:?}", e); } } @@ -142,16 +132,18 @@ impl VhostUserFs { pub fn new(fs_config: FsConfig) -> Result { if let Some(limit) = fs_config.rlimit_nofile { set_rlimit_nofile(limit) - .chain_err(|| format!("Failed to set rlimit nofile {}", limit))?; + .with_context(|| format!("Failed to set rlimit nofile {}", limit))?; } let virtio_fs = Arc::new(Mutex::new( VirtioFs::new(&fs_config.source_dir) - .chain_err(|| format!("Failed to create virtio fs {}", fs_config.source_dir))?, + .with_context(|| format!("Failed to create virtio fs {}", fs_config.source_dir))?, )); let server_handler = VhostUserServerHandler::new(&fs_config.sock_path, virtio_fs) - .chain_err(|| format!("Failed to create vhost user server {}", fs_config.sock_path))?; + .with_context(|| { + format!("Failed to create vhost user server {}", fs_config.sock_path) + })?; Ok(VhostUserFs { server_handler }) } @@ -173,7 +165,7 @@ impl EventLoopManager for VhostUserFs { false } - fn loop_cleanup(&self) -> util::errors::Result<()> { + fn loop_cleanup(&self) -> util::Result<()> { Ok(()) } } diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 959f65870..55672f6f1 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -21,7 +21,7 @@ use virtio::vhost::user::{ }; use virtio::VhostUser::VhostUserSock; -use crate::errors::{Result, ResultExt}; +use anyhow::{bail, Context, Result}; /// The trait for dealing with vhost-user request in the server. pub trait VhostUserReqHandler: Send + Sync { @@ -156,8 +156,8 @@ impl VhostUserServerHandler { let mut sock = VhostUserSock::new(path); sock.domain .bind(true) - .chain_err(|| format!("Failed to bind for vhost user server {}", path))?; - limit_permission(path).chain_err(|| format!("Failed to limit permission {}", path))?; + .with_context(|| format!("Failed to bind for vhost user server {}", path))?; + limit_permission(path).with_context(|| format!("Failed to limit permission {}", path))?; Ok(VhostUserServerHandler { sock, backend }) } @@ -171,7 +171,7 @@ impl VhostUserServerHandler { let (rcv_len, fds_num) = self .sock .recv_msg(Some(&mut hdr), body_opt, payload_opt, &mut fds) - .chain_err(|| "Failed to recv hdr and fds")?; + .with_context(|| "Failed to recv hdr and fds")?; if rcv_len != size_of::() { bail!( @@ -210,7 +210,7 @@ impl VhostUserServerHandler { let (rcv_len, _) = self .sock .recv_msg(hdr_opt, body_opt, Some(&mut rbuf), &mut []) - .chain_err(|| "Failed to recv msg body")?; + .with_context(|| "Failed to recv msg body")?; if rcv_len != len { bail!( @@ -252,7 +252,7 @@ impl VhostUserServerHandler { self.sock .send_msg(Some(&hdr), Some(&res), payload_opt, fds) - .chain_err(|| "Failed to send ack msg")?; + .with_context(|| "Failed to send ack msg")?; Ok(()) } @@ -327,13 +327,13 @@ impl VhostUserServerHandler { let features = self.backend.lock().unwrap().get_features()?; if hdr.need_reply() { self.send_ack_msg(VhostUserMsgReq::GetFeatures as u32, features, &[]) - .chain_err(|| "Failed to send ack msg for getting features")?; + .with_context(|| "Failed to send ack msg for getting features")?; } } VhostUserMsgReq::SetFeatures => { let features = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting features")?; + .with_context(|| "Failed to get msg body for setting features")?; self.backend.lock().unwrap().set_features(*features)?; } VhostUserMsgReq::SetOwner => { @@ -345,23 +345,20 @@ impl VhostUserServerHandler { VhostUserMsgReq::SetMemTable => { let ret = match self.set_msg_mem_table(hdr, buf, len, rfds) { Err(ref e) => { - error!( - "Failed to set mem table {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to set mem table {:?}", e); 1u64 } Ok(_) => 0u64, }; if hdr.need_reply() { self.send_ack_msg(VhostUserMsgReq::SetMemTable as u32, ret, &[]) - .chain_err(|| "Failed to send ack msg for setting mem table")?; + .with_context(|| "Failed to send ack msg for setting mem table")?; } } VhostUserMsgReq::SetVringNum => { let vringstate = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting vring num")?; + .with_context(|| "Failed to get msg body for setting vring num")?; self.backend .lock() .unwrap() @@ -370,7 +367,7 @@ impl VhostUserServerHandler { VhostUserMsgReq::SetVringAddr => { let vringaddr = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting vring addr")?; + .with_context(|| "Failed to get msg body for setting vring addr")?; self.backend.lock().unwrap().set_vring_addr( vringaddr.index as usize, vringaddr.flags, @@ -383,7 +380,7 @@ impl VhostUserServerHandler { VhostUserMsgReq::SetVringBase => { let vringstate = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting vring base")?; + .with_context(|| "Failed to get msg body for setting vring base")?; self.backend .lock() .unwrap() @@ -392,7 +389,7 @@ impl VhostUserServerHandler { VhostUserMsgReq::SetVringEnable => { let vringstate = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting vring enable")?; + .with_context(|| "Failed to get msg body for setting vring enable")?; self.backend .lock() .unwrap() @@ -401,7 +398,7 @@ impl VhostUserServerHandler { VhostUserMsgReq::SetVringKick => { let index = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting vring kick")?; + .with_context(|| "Failed to get msg body for setting vring kick")?; if let Some(fds) = rfds { let fds_len = fds.len(); if fds_len != 1 { @@ -419,7 +416,7 @@ impl VhostUserServerHandler { VhostUserMsgReq::SetVringCall => { let index = self .get_msg_body::(hdr, buf, len) - .chain_err(|| "Failed to get msg body for setting vring call")?; + .with_context(|| "Failed to get msg body for setting vring call")?; if let Some(fds) = rfds { let fds_len = fds.len(); if fds_len != 1 { @@ -446,20 +443,20 @@ impl VhostUserServerHandler { pub fn handle_request(&mut self) -> Result<()> { let (hdr, rfds) = self .recv_hdr_and_fds() - .chain_err(|| "Failed to recv header and fds")?; + .with_context(|| "Failed to recv header and fds")?; let (len, buf) = match hdr.size { 0 => (0, vec![0u8; 0]), _ => { let (rcv_len, rbuf) = self .recv_body(hdr.size as usize) - .chain_err(|| "Failed to recv msg body")?; + .with_context(|| "Failed to recv msg body")?; (rcv_len, rbuf) } }; self.process_request(&hdr, &buf, len, rfds) - .chain_err(|| format!("Failed to process the request {}", hdr.request))?; + .with_context(|| format!("Failed to process the request {}", hdr.request))?; Ok(()) } } diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 551f7028f..b55b0514c 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -21,7 +21,6 @@ use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::{Arc, Mutex}; -use error_chain::ChainedError; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; @@ -32,7 +31,7 @@ use super::fs::FileSystem; use super::fuse_req::FuseReq; use super::vhost_user_server::VhostUserReqHandler; -use crate::errors::{Result, ResultExt}; +use anyhow::{anyhow, bail, Context, Result}; use virtio::vhost::user::RegionMemInfo; use virtio::{ Queue, QueueConfig, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, @@ -58,7 +57,7 @@ impl FsIoHandler { fs: Arc>, ) -> Result { let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING) - .chain_err(|| "Failed to create virtual queue")?; + .with_context(|| "Failed to create virtual queue")?; if !queue.is_valid(mem_space) { bail!("Invalid queue for fs handler"); } @@ -91,7 +90,7 @@ impl FsIoHandler { { self.call_evt .write(1) - .chain_err(|| "Failed to write call fd")?; + .with_context(|| "Failed to write call fd")?; } Ok(()) @@ -117,7 +116,7 @@ impl EventNotifierHelper for FsIoHandler { read_fd(fd); if let Err(e) = fs_handler_clone.lock().unwrap().process_queue() { - error!("Failed to process fuse msg, {}", e.display_chain()); + error!("Failed to process fuse msg, {:?}", e); } None @@ -180,7 +179,7 @@ impl VirtioFsConfig { fn get_mut_queue_config(&mut self, queue_index: usize) -> Result<&mut QueueInfo> { self.queues_info .get_mut(queue_index) - .ok_or_else(|| format!("The select index of queue {} overflows", queue_index).into()) + .ok_or_else(|| anyhow!("The select index of queue {} overflows", queue_index)) } } @@ -208,16 +207,16 @@ impl VirtioFs { /// * `source_dir` - The path of source directory shared in host. pub fn new(source_dir: &str) -> Result { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .chain_err(|| "Failed to create address space")?; + .with_context(|| "Failed to create address space")?; let mut fs_handlers = Vec::new(); for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { fs_handlers.push(None); } - let fs = Arc::new(Mutex::new(FileSystem::new(source_dir).chain_err(|| { - format!("Failed to create file system, source dir: {}", source_dir) - })?)); + let fs = Arc::new(Mutex::new(FileSystem::new(source_dir).with_context( + || format!("Failed to create file system, source dir: {}", source_dir), + )?)); Ok(VirtioFs { config: VirtioFsConfig::new(), @@ -257,10 +256,7 @@ impl VhostUserReqHandler for VirtioFs { if !self.config.mem_regions.is_empty() { for region in &self.config.mem_regions { if let Err(e) = self.sys_mem.root().delete_subregion(region) { - error!( - "Failed to delete subregion for setting mem table, {}", - e.display_chain() - ); + error!("Failed to delete subregion for setting mem table, {:?}", e); } } self.config.mem_regions = Vec::new(); @@ -286,7 +282,7 @@ impl VhostUserReqHandler for VirtioFs { true, false, ) - .chain_err(|| + .with_context(|| format!("Failed to create the mapping of host memory for setting mem table, addr: 0x{:X}, size: {}, offset: {}", region_config.guest_phys_addr, region_config.memory_size, region_config.mmap_offset, ) @@ -297,7 +293,7 @@ impl VhostUserReqHandler for VirtioFs { self.sys_mem .root() .add_subregion(region.clone(), mmap.start_address().raw_value()) - .chain_err(|| "Failed to add subregion for setting mem table")?; + .with_context(|| "Failed to add subregion for setting mem table")?; self.config.mem_regions.push(region); } @@ -311,7 +307,7 @@ impl VhostUserReqHandler for VirtioFs { .map(|queue_info| { queue_info.config.size = num; }) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring num, index: {}, num: {}", queue_index, num, @@ -352,7 +348,7 @@ impl VhostUserReqHandler for VirtioFs { queue_info.config.addr_cache.used_ring_host = cloned_mem_space .get_host_address(GuestAddress(used_addr)) .unwrap_or(0); - }).chain_err(|| + }).with_context(|| format!("Failed to set vring addr, index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", queue_index, desc_addr, avail_addr, used_addr, ) @@ -371,7 +367,7 @@ impl VhostUserReqHandler for VirtioFs { let call_evt = unsafe { EventFd::from_raw_fd(fd) }; queue_info.call_evt = Some(call_evt); }) - .chain_err(|| format!("Failed to set vring call, index: {}", queue_index))?; + .with_context(|| format!("Failed to set vring call, index: {}", queue_index))?; Ok(()) } @@ -382,7 +378,7 @@ impl VhostUserReqHandler for VirtioFs { let kick_evt = unsafe { EventFd::from_raw_fd(fd) }; queue_info.kick_evt = Some(kick_evt); }) - .chain_err(|| format!("Failed to set vring kick, index: {}", queue_index))?; + .with_context(|| format!("Failed to set vring kick, index: {}", queue_index))?; Ok(()) } @@ -410,15 +406,15 @@ impl VhostUserReqHandler for VirtioFs { driver_features, self.fs.clone(), ) - .chain_err(|| "Failed to create fs handler")?, + .with_context(|| "Failed to create fs handler")?, )); self.fs_handlers[queue_index] = Some(fs_handler.clone()); EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) - .chain_err(|| "Failed to update event for queue status which is ready")?; + .with_context(|| "Failed to update event for queue status which is ready")?; } else if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) - .chain_err(|| "Failed to update event for queue status which is not ready")?; + .with_context(|| "Failed to update event for queue status which is not ready")?; } Ok(()) } diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 92105b037..19c8cfa39 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -8,7 +8,8 @@ description = "Virtio devices emulation" [dependencies] byteorder = "1.4.3" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" kvm-ioctls = ">=0.11.0" libc = "0.2" log = "0.4" diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index b58e4c254..8d4bb1468 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -22,7 +22,7 @@ use std::{ use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use error_chain::{bail, ChainedError}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::{ config::BalloonConfig, event, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, @@ -41,7 +41,7 @@ use util::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use super::{ - errors::*, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, + error::*, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; @@ -107,7 +107,7 @@ impl BalloonedPageBitmap { match self.bitmap.count_front_bits(bits as usize) { Ok(nr) => nr == bits as usize, Err(ref e) => { - error!("Failed to count bits: {}", e); + error!("Failed to count bits: {:?}", e); false } } @@ -135,10 +135,7 @@ fn iov_to_buf( match address_space.read_object::(GuestAddress(iov.iov_base.raw_value() + offset)) { Ok(dat) => Some(dat), Err(ref e) => { - error!( - "Read virtioqueue failed: {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Read virtioqueue failed: {:?}", e); None } } @@ -154,7 +151,7 @@ fn memory_advise(addr: *mut libc::c_void, len: libc::size_t, advice: libc::c_int }; let e = std::io::Error::last_os_error(); error!( - "Mark memory address: {} to {} failed: {}", + "Mark memory address: {} to {} failed: {:?}", addr as u64, evt_type, e ); } @@ -188,7 +185,7 @@ impl Request { &elem.out_iovec }; if iovec.is_empty() { - return Err(ErrorKind::ElementEmpty.into()); + return Err(anyhow!(VirtioError::ElementEmpty)); } for elem_iov in iovec { request.iovec.push(Iovec { @@ -289,7 +286,7 @@ impl Request { host_page_bitmap.set_bit((hva % host_page_size) / BALLOON_PAGE_SIZE) { error!( - "Failed to set bit with index: {} :{}", + "Failed to set bit with index: {} :{:?}", (hva % host_page_size) / BALLOON_PAGE_SIZE, e ); @@ -312,7 +309,7 @@ impl Request { for iov in self.iovec.iter() { let gpa: GuestAddress = iov.iov_base; let hva = match mem.lock().unwrap().get_host_address(gpa) { - Some(addr) => (addr), + Some(addr) => addr, None => { error!("Can not get host address, gpa: {}", gpa.raw_value()); continue; @@ -445,7 +442,7 @@ impl Listener for BlnMemInfo { range: Option<&FlatRange>, _evtfd: Option<&RegionIoEventFd>, req_type: ListenerReqType, - ) -> std::result::Result<(), address_space::errors::Error> { + ) -> Result<(), anyhow::Error> { match req_type { ListenerReqType::AddRegion => { let fr = range.unwrap(); @@ -517,26 +514,31 @@ impl BalloonIoHandler { .pop_avail(&self.mem_space, self.driver_features) { let req = Request::parse(&elem, OUT_IOVEC) - .chain_err(|| "Fail to parse available descriptor chain")?; + .with_context(|| "Fail to parse available descriptor chain")?; if !self.mem_info.lock().unwrap().has_huge_page() { req.mark_balloon_page(req_type, &self.mem_space, &self.mem_info); } locked_queue .vring .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) - .chain_err(|| "Failed to add balloon response into used queue")?; + .with_context(|| "Failed to add balloon response into used queue")?; } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)) - .chain_err(|| ErrorKind::InterruptTrigger("balloon", VirtioInterruptType::Vring))?; - self.trace_send_interrupt("Balloon".to_string()); + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + }, + )?; Ok(()) } fn reporting_evt_handler(&mut self) -> Result<()> { let queue = &self.report_queue; if queue.is_none() { - return Err(ErrorKind::VirtQueueIsNone.into()); + return Err(anyhow!(VirtioError::VirtQueueIsNone)); } let unwraped_queue = queue.as_ref().unwrap(); let mut locked_queue = unwraped_queue.lock().unwrap(); @@ -545,17 +547,23 @@ impl BalloonIoHandler { .pop_avail(&self.mem_space, self.driver_features) { let req = Request::parse(&elem, IN_IOVEC) - .chain_err(|| "Fail to parse available descriptor chain")?; + .with_context(|| "Fail to parse available descriptor chain")?; if !self.mem_info.lock().unwrap().has_huge_page() { req.release_pages(&self.mem_info); } locked_queue .vring .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) - .chain_err(|| "Failed to add balloon response into used queue")?; - - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)) - .chain_err(|| ErrorKind::InterruptTrigger("balloon", VirtioInterruptType::Vring))?; + .with_context(|| "Failed to add balloon response into used queue")?; + + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + }, + )?; } Ok(()) } @@ -642,7 +650,7 @@ impl EventNotifierHelper for BalloonIoHandler { .unwrap() .process_balloon_queue(BALLOON_INFLATE_EVENT) { - error!("Failed to inflate balloon: {}", e.display_chain()); + error!("Failed to inflate balloon: {:?}", e); }; None }); @@ -660,7 +668,7 @@ impl EventNotifierHelper for BalloonIoHandler { .unwrap() .process_balloon_queue(BALLOON_DEFLATE_EVENT) { - error!("Failed to deflate balloon: {}", e.display_chain()); + error!("Failed to deflate balloon: {:?}", e); }; None }); @@ -675,7 +683,7 @@ impl EventNotifierHelper for BalloonIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = cloned_balloon_io.lock().unwrap().reporting_evt_handler() { - error!("Failed to report free pages: {}", e.display_chain()); + error!("Failed to report free pages: {:?}", e); } None }); @@ -781,10 +789,16 @@ impl Balloon { /// Notify configuration changes to VM. fn signal_config_change(&self) -> Result<()> { if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Config, None) - .chain_err(|| ErrorKind::InterruptTrigger("balloon", VirtioInterruptType::Vring)) + interrupt_cb(&VirtioInterruptType::Config, None).with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + }) } else { - Err(ErrorKind::DeviceNotActivated("balloon".to_string()).into()) + Err(anyhow!(VirtioError::DeviceNotActivated( + "balloon".to_string() + ))) } } @@ -804,7 +818,7 @@ impl Balloon { (self.mem_info.lock().unwrap().get_ram_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32; let vm_target = cmp::min(target, current_ram_size); self.num_pages = current_ram_size - vm_target; - self.signal_config_change().chain_err(|| { + self.signal_config_change().with_context(|| { "Failed to notify about configuration change after setting balloon memory" })?; let msg = BalloonInfo { @@ -831,7 +845,7 @@ impl VirtioDevice for Balloon { self.mem_info = Arc::new(Mutex::new(BlnMemInfo::new())); self.mem_space .register_listener(self.mem_info.clone()) - .chain_err(|| "Failed to register memory listener defined by balloon device.")?; + .with_context(|| "Failed to register memory listener defined by balloon device.")?; Ok(()) } @@ -892,7 +906,7 @@ impl VirtioDevice for Balloon { actual: self.actual.load(Ordering::Acquire), }; if offset != 0 { - return Err(ErrorKind::IncorrectOffset(0, offset).into()); + return Err(anyhow!(VirtioError::IncorrectOffset(0, offset))); } data.write_all( &new_config.as_bytes()[offset as usize @@ -901,7 +915,7 @@ impl VirtioDevice for Balloon { size_of::(), )], ) - .chain_err(|| "Failed to write data to 'data' while reading balloon config")?; + .with_context(|| "Failed to write data to 'data' while reading balloon config")?; Ok(()) } @@ -917,7 +931,7 @@ impl VirtioDevice for Balloon { let new_actual = match unsafe { data.align_to::() } { (_, [new_config], _) => *new_config, _ => { - return Err(ErrorKind::FailedToWriteConfig.into()); + return Err(anyhow!(VirtioError::FailedToWriteConfig)); } }; if old_actual != new_actual { @@ -926,7 +940,7 @@ impl VirtioDevice for Balloon { if !ret { timer .reset(Duration::new(1, 0), None) - .chain_err(|| "Failed to reset timer for qmp event during ballooning")?; + .with_context(|| "Failed to reset timer for qmp event during ballooning")?; } } } @@ -952,7 +966,10 @@ impl VirtioDevice for Balloon { mut queue_evts: Vec, ) -> Result<()> { if queues.len() != self.queue_num() { - return Err(ErrorKind::IncorrectQueueNum(QUEUE_NUM_BALLOON, queues.len()).into()); + return Err(anyhow!(VirtioError::IncorrectQueueNum( + QUEUE_NUM_BALLOON, + queues.len() + ))); } let inf_queue = queues[0].clone(); @@ -1003,7 +1020,7 @@ impl VirtioDevice for Balloon { EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), None, ) - .chain_err(|| "Failed to register balloon event notifier to MainLoop")?; + .with_context(|| "Failed to register balloon event notifier to MainLoop")?; Ok(()) } @@ -1011,7 +1028,7 @@ impl VirtioDevice for Balloon { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } fn update_config( @@ -1031,11 +1048,7 @@ pub fn qmp_balloon(target: u64) -> bool { return true; } Err(ref e) => { - error!( - "Failed to set balloon memory size: {}, :{}", - target, - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to set balloon memory size: {}, :{:?}", target, e); return false; } } @@ -1224,7 +1237,9 @@ mod tests { VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status, Ordering::SeqCst); - interrupt_evt.write(1).chain_err(|| ErrorKind::EventFdWrite) + interrupt_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) }, ) as VirtioInterrupt); @@ -1360,7 +1375,9 @@ mod tests { VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status, Ordering::SeqCst); - interrupt_evt.write(1).chain_err(|| ErrorKind::EventFdWrite) + interrupt_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) }, ) as VirtioInterrupt); diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 6eb98e58c..9fee358a5 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -20,8 +20,16 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; +use super::{ + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_BLOCK, +}; +use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; -use error_chain::{bail, ChainedError}; +use anyhow::{anyhow, bail, Context, Result}; use log::error; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, @@ -40,16 +48,6 @@ use util::loop_context::{ }; use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; - -use super::errors::{ErrorKind, Result, ResultExt}; -use super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, - VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_BLOCK, -}; - /// Number of virtqueues. const QUEUE_NUM_BLK: usize = 1; /// Size of each virtqueue. @@ -76,7 +74,7 @@ fn write_buf_mem(buf: &[u8], hva: u64) -> Result<()> { let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; (&mut slice) .write(buf) - .chain_err(|| format!("Failed to write buf(hva:{})", hva))?; + .with_context(|| format!("Failed to write buf(hva:{})", hva))?; Ok(()) } @@ -169,8 +167,11 @@ impl Request { let out_header = mem_space .read_object::(out_iov_elem.addr) - .chain_err(|| { - ErrorKind::ReadObjectErr("the block's request header", out_iov_elem.addr.0) + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "the block's request header", + out_iov_elem.addr.0 + )) })?; if !out_header.is_valid() { @@ -248,7 +249,7 @@ impl Request { } top.checked_add(self.out_header.sector) .filter(|off| off <= &disk_sectors) - .chain_err(|| { + .with_context(|| { format!( "offset {} invalid, disk sector {}", self.out_header.sector, disk_sectors @@ -281,11 +282,14 @@ impl Request { for iov in self.iovec.iter() { MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); } - (*aio).as_mut().rw_aio(aiocb, SECTOR_SIZE).chain_err(|| { - "Failed to process block request for reading asynchronously" - })?; + (*aio) + .as_mut() + .rw_aio(aiocb, SECTOR_SIZE) + .with_context(|| { + "Failed to process block request for reading asynchronously" + })?; } else { - (*aio).as_mut().rw_sync(aiocb).chain_err(|| { + (*aio).as_mut().rw_sync(aiocb).with_context(|| { "Failed to process block request for reading synchronously" })?; } @@ -293,11 +297,14 @@ impl Request { VIRTIO_BLK_T_OUT => { aiocb.opcode = IoCmd::Pwritev; if direct { - (*aio).as_mut().rw_aio(aiocb, SECTOR_SIZE).chain_err(|| { - "Failed to process block request for writing asynchronously" - })?; + (*aio) + .as_mut() + .rw_aio(aiocb, SECTOR_SIZE) + .with_context(|| { + "Failed to process block request for writing asynchronously" + })?; } else { - (*aio).as_mut().rw_sync(aiocb).chain_err(|| { + (*aio).as_mut().rw_sync(aiocb).with_context(|| { "Failed to process block request for writing synchronously" })?; } @@ -307,7 +314,7 @@ impl Request { (*aio) .as_mut() .rw_sync(aiocb) - .chain_err(|| "Failed to process block request for flushing")?; + .with_context(|| "Failed to process block request for flushing")?; } VIRTIO_BLK_T_GET_ID => { if let Some(serial) = serial_num { @@ -322,7 +329,7 @@ impl Request { ); } write_buf_mem(&serial_vec, iov.iov_base) - .chain_err(|| "Failed to write buf for virtio block id")?; + .with_context(|| "Failed to write buf for virtio block id")?; } } @@ -455,13 +462,10 @@ impl BlockIoHandler { queue .vring .add_used(&self.mem_space, elem.index, 0) - .chain_err(|| "Failed to add used ring")?; + .with_context(|| "Failed to add used ring")?; need_interrupt = true; - error!( - "failed to create block request, {}", - error_chain::ChainedError::display_chain(e) - ); + error!("failed to create block request, {:?}", e); } }; } @@ -477,7 +481,7 @@ impl BlockIoHandler { if let Some(ref mut aio) = self.aio { let rw_len = match req.out_header.request_type { VIRTIO_BLK_T_IN => u32::try_from(req.data_len) - .chain_err(|| "Convert block request len to u32 with overflow.")?, + .with_context(|| "Convert block request len to u32 with overflow.")?, _ => 0u32, }; @@ -505,12 +509,12 @@ impl BlockIoHandler { // get device id self.mem_space .write_object(&VIRTIO_BLK_S_OK, req.in_header) - .chain_err(|| "Failed to write result for the request for block with device id")?; + .with_context(|| "Failed to write result for the request for block with device id")?; self.queue.lock().unwrap().vring.add_used( &self.mem_space, req.desc_index, 1, - ).chain_err(|| "Failed to add the request for block with device id to used ring")?; + ).with_context(|| "Failed to add the request for block with device id to used ring")?; if self .queue @@ -524,10 +528,7 @@ impl BlockIoHandler { } } Err(ref e) => { - error!( - "Failed to execute block request, {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to execute block request, {:?}", e); } } req_index += 1; @@ -540,7 +541,7 @@ impl BlockIoHandler { .unwrap() .vring .add_used(&self.mem_space, req.desc_index, 1) - .chain_err(|| { + .with_context(|| { "Failed to add used ring, when block request queue isn't empty" })?; } @@ -552,7 +553,12 @@ impl BlockIoHandler { &VirtioInterruptType::Vring, Some(&self.queue.lock().unwrap()), ) - .chain_err(|| ErrorKind::InterruptTrigger("block", VirtioInterruptType::Vring))?; + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "block", + VirtioInterruptType::Vring + )) + })?; self.trace_send_interrupt("Block".to_string()); } @@ -572,10 +578,7 @@ impl BlockIoHandler { .mem_space .write_object(&status, complete_cb.req_status_addr) { - error!( - "Failed to write the status (aio completion) {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to write the status (aio completion) {:?}", e); return; } @@ -586,10 +589,8 @@ impl BlockIoHandler { complete_cb.rw_len, ) { error!( - "Failed to add used ring(aio completion), index {}, len {} {}", - complete_cb.desc_index, - complete_cb.rw_len, - error_chain::ChainedError::display_chain(e), + "Failed to add used ring(aio completion), index {}, len {} {:?}", + complete_cb.desc_index, complete_cb.rw_len, e, ); return; } @@ -603,8 +604,8 @@ impl BlockIoHandler { Some(&queue_lock), ) { error!( - "Failed to trigger interrupt(aio completion) for block device, error is {}", - e.display_chain() + "Failed to trigger interrupt(aio completion) for block device, error is {:?}", + e ); } } @@ -630,10 +631,7 @@ impl BlockIoHandler { }; if let Err(ref e) = self.process_queue() { - error!( - "Failed to handle block IO for updating handler {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to handle block IO for updating handler {:?}", e); } } @@ -725,10 +723,7 @@ impl EventNotifierHelper for BlockIoHandler { read_fd(fd); if let Err(ref e) = h_clone.lock().unwrap().process_queue() { - error!( - "Failed to handle block IO {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to handle block IO {:?}", e); } None }); @@ -739,7 +734,7 @@ impl EventNotifierHelper for BlockIoHandler { .lock() .unwrap() .process_queue() - .chain_err(|| "Failed to handle block IO") + .with_context(|| "Failed to handle block IO") .ok()?; if done { Some(Vec::new()) @@ -773,10 +768,7 @@ impl EventNotifierHelper for BlockIoHandler { } if let Err(ref e) = h_clone.lock().unwrap().process_queue() { - error!( - "Failed to handle block IO {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to handle block IO {:?}", e); } None }); @@ -791,10 +783,7 @@ impl EventNotifierHelper for BlockIoHandler { if let Some(aio) = &mut h_clone.lock().unwrap().aio { if let Err(ref e) = aio.handle() { - error!( - "Failed to handle aio, {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to handle aio, {:?}", e); } } None @@ -804,7 +793,7 @@ impl EventNotifierHelper for BlockIoHandler { let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { let mut done = false; if let Some(aio) = &mut h_clone.lock().unwrap().aio { - done = aio.handle().chain_err(|| "Failed to handle aio").ok()?; + done = aio.handle().with_context(|| "Failed to handle aio").ok()?; } if done { Some(Vec::new()) @@ -997,7 +986,7 @@ impl VirtioDevice for Block { .write(!self.blk_cfg.read_only) .custom_flags(libc::O_DIRECT) .open(&self.blk_cfg.path_on_host) - .chain_err(|| { + .with_context(|| { format!( "failed to open the file by O_DIRECT for block {}", self.blk_cfg.path_on_host @@ -1008,7 +997,7 @@ impl VirtioDevice for Block { .read(true) .write(!self.blk_cfg.read_only) .open(&self.blk_cfg.path_on_host) - .chain_err(|| { + .with_context(|| { format!( "failed to open the file for block {}", self.blk_cfg.path_on_host @@ -1016,9 +1005,9 @@ impl VirtioDevice for Block { })? }; - disk_size = file - .seek(SeekFrom::End(0)) - .chain_err(|| "Failed to seek the end for block")? as u64; + disk_size = + file.seek(SeekFrom::End(0)) + .with_context(|| "Failed to seek the end for block")? as u64; self.disk_image = Some(Arc::new(file)); } else { @@ -1076,7 +1065,7 @@ impl VirtioDevice for Block { let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } if let Some(end) = offset.checked_add(data.len() as u64) { data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; @@ -1091,7 +1080,10 @@ impl VirtioDevice for Block { let config_slice = self.state.config_space.as_mut_bytes(); let config_len = config_slice.len(); if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset, + config_len as u64 + ))); } config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); @@ -1149,7 +1141,7 @@ impl VirtioDevice for Block { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } fn update_config(&mut self, dev_config: Option>) -> Result<()> { @@ -1176,17 +1168,21 @@ impl VirtioDevice for Block { self.blk_cfg.serial_num.clone(), self.blk_cfg.direct, )) - .chain_err(|| ErrorKind::ChannelSend("image fd".to_string()))?; + .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; } self.update_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Config, None) - .chain_err(|| ErrorKind::InterruptTrigger("block", VirtioInterruptType::Config))?; + interrupt_cb(&VirtioInterruptType::Config, None).with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "block", + VirtioInterruptType::Config + )) + })?; } Ok(()) @@ -1199,13 +1195,13 @@ impl VirtioDevice for Block { unsafe impl Sync for Block {} impl StateTransfer for Block { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *BlockState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("BLOCK"))?; + .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("BLOCK")))?; Ok(()) } @@ -1425,7 +1421,7 @@ mod tests { interrupt_status.fetch_or(status as u32, Ordering::SeqCst); interrupt_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) }, diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 504bdf105..128efcb48 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -15,9 +15,14 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; use std::{cmp, usize}; +use super::{ + Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_SIZE, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, +}; +use crate::VirtioError; use address_space::AddressSpace; +use anyhow::{anyhow, bail, Context, Result}; use devices::legacy::{Chardev, InputReceiver}; -use error_chain::{bail, ChainedError}; use log::{debug, error, warn}; use machine_manager::{ config::{ChardevType, VirtioConsole}, @@ -31,12 +36,6 @@ use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use super::errors::{ErrorKind, Result, ResultExt}; -use super::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_SIZE, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, -}; - /// Number of virtqueues. const QUEUE_NUM_CONSOLE: usize = 2; /// Size of virtqueue. @@ -104,10 +103,10 @@ impl InputReceiver for ConsoleHandler { } Err(ref e) => { error!( - "Failed to write slice for input console: addr {:X} len {} {}", + "Failed to write slice for input console: addr {:X} len {} {:?}", elem_iov.addr.0, source_slice.len(), - e.display_chain() + e ); break; } @@ -120,10 +119,8 @@ impl InputReceiver for ConsoleHandler { .add_used(&self.mem_space, elem.index, write_count as u32) { error!( - "Failed to add used ring for input console, index: {} len: {} {}", - elem.index, - write_count, - e.display_chain() + "Failed to add used ring for input console, index: {} len: {} {:?}", + elem.index, write_count, e ); break; } @@ -135,9 +132,9 @@ impl InputReceiver for ConsoleHandler { if let Err(ref e) = (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock)) { error!( - "Failed to trigger interrupt for console, int-type {:?} {} ", + "Failed to trigger interrupt for console, int-type {:?} {:?} ", VirtioInterruptType::Vring, - e.display_chain() + e ) } } @@ -173,10 +170,10 @@ impl ConsoleHandler { } Err(ref e) => { error!( - "Failed to read buffer for output console: addr: {:X}, len: {} {}", + "Failed to read buffer for output console: addr: {:X}, len: {} {:?}", elem_iov.addr.0, allow_read_count - read_count, - e.display_chain() + e ); break; } @@ -185,10 +182,10 @@ impl ConsoleHandler { if let Some(output) = &mut self.chardev.lock().unwrap().output { let mut locked_output = output.lock().unwrap(); if let Err(e) = locked_output.write_all(&buffer[..read_count as usize]) { - error!("Failed to write to console output: {}", e); + error!("Failed to write to console output: {:?}", e); } if let Err(e) = locked_output.flush() { - error!("Failed to flush console output: {}", e); + error!("Failed to flush console output: {:?}", e); } } else { debug!("Failed to get output fd"); @@ -196,10 +193,8 @@ impl ConsoleHandler { if let Err(ref e) = queue_lock.vring.add_used(&self.mem_space, elem.index, 0) { error!( - "Failed to add used ring for output console, index: {} len: {} {}", - elem.index, - 0, - e.display_chain() + "Failed to add used ring for output console, index: {} len: {} {:?}", + elem.index, 0, e ); break; } @@ -345,7 +340,7 @@ impl VirtioDevice for Console { .lock() .unwrap() .realize() - .chain_err(|| "Failed to realize chardev")?; + .with_context(|| "Failed to realize chardev")?; Ok(()) } @@ -390,7 +385,7 @@ impl VirtioDevice for Console { let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } if let Some(end) = offset.checked_add(data.len() as u64) { @@ -441,18 +436,18 @@ impl VirtioDevice for Console { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } } impl StateTransfer for Console { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *VirtioConsoleState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("CONSOLE"))?; + .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("CONSOLE")))?; Ok(()) } diff --git a/virtio/src/error.rs b/virtio/src/error.rs new file mode 100644 index 000000000..13ceb8aae --- /dev/null +++ b/virtio/src/error.rs @@ -0,0 +1,75 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum VirtioError { + #[error("Io")] + Io { + #[from] + source: std::io::Error, + }, + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("AddressSpace")] + AddressSpace { + #[from] + source: address_space::error::AddressSpaceError, + }, + #[error("SysBus")] + SysBus { + #[from] + source: sysbus::error::SysBusError, + }, + #[error("Failed to create eventfd.")] + EventFdCreate, + #[error("Failed to write eventfd.")] + EventFdWrite, + #[error("Failed to create {0} thread")] + ThreadCreate(String), + #[error("Failed to send {0} on the channel")] + ChannelSend(String), + #[error("Queue index {0} invalid, queue size is {1}")] + QueueIndex(u16, u16), + #[error("Vring descriptor is invalid")] + QueueDescInvalid, + #[error("Address overflows for {0}, address: 0x{1:x}, offset: {2}")] + AddressOverflow(&'static str, u64, u64), + #[error("Failed to r/w dev config space: overflows, offset {0}, space size {1}")] + DevConfigOverflow(u64, u64), + #[error("Failed to trigger interrupt for {0}, int-type {1:#?}")] + InterruptTrigger(&'static str, super::VirtioInterruptType), + #[error("Vhost ioctl failed: {0}")] + VhostIoctl(String), + #[error("Failed to get iovec from element!")] + ElementEmpty, + #[error("IVirt queue is none!o")] + VirtQueueIsNone, + #[error("Cannot perform activate. Expected {0} queue(s), got {1}")] + IncorrectQueueNum(usize, usize), + #[error("Incorrect offset, expected {0}, got {1}")] + IncorrectOffset(u64, u64), + #[error("Device {0} not activated")] + DeviceNotActivated(String), + #[error("Failed to write config")] + FailedToWriteConfig, + #[error("Failed to read object for {0}, address: 0x{1:x}")] + ReadObjectErr(&'static str, u64), + #[error("Invalid device status: 0x{0:x}.")] + DevStatErr(u32), + #[error("Unsupported mmio register at offset 0x{0:x}.")] + MmioRegErr(u64), +} diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index c4021f477..b9e96f131 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -10,13 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::{ErrorKind, Result, ResultExt}; use super::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU, }; +use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::EventLoop; @@ -46,7 +46,6 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use vnc::vnc::{ vnc_display_cursor, vnc_display_switch, vnc_display_update, DisplayMouse, DisplaySurface, }; - /// Number of virtqueues. const QUEUE_NUM_GPU: usize = 2; /// Size of each virtqueue. @@ -374,7 +373,12 @@ impl VirtioGpuRequest { let out_header = mem_space .read_object::(out_elem.addr) - .chain_err(|| ErrorKind::ReadObjectErr("the GPU's request header", out_elem.addr.0))?; + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "the GPU's request header", + out_elem.addr.0 + )) + })?; if !out_header.is_valid() { bail!("Unsupported GPU request type"); } @@ -596,8 +600,11 @@ impl GpuIoHandler { let info_cursor = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr("the GPU's cursor request header", req.out_header.0) + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "the GPU's cursor request header", + req.out_header.0 + )) })?; let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; @@ -670,7 +677,7 @@ impl GpuIoHandler { self.mem_space .write_object(&resp, req.in_header) - .chain_err(|| "Fail to write nodata response")?; + .with_context(|| "Fail to write nodata response")?; self.ctrl_queue .lock() .unwrap() @@ -680,7 +687,7 @@ impl GpuIoHandler { req.index, size_of::() as u32, ) - .chain_err(|| "Fail to add used elem for control queue")?; + .with_context(|| "Fail to add used elem for control queue")?; Ok(()) } @@ -710,7 +717,7 @@ impl GpuIoHandler { } self.mem_space .write_object(&display_info, req.in_header) - .chain_err(|| "Fail to write displayinfo")?; + .with_context(|| "Fail to write displayinfo")?; self.ctrl_queue .lock() .unwrap() @@ -720,7 +727,7 @@ impl GpuIoHandler { req.index, size_of::() as u32, ) - .chain_err(|| "Fail to add used elem for control queue")?; + .with_context(|| "Fail to add used elem for control queue")?; Ok(()) } @@ -737,8 +744,11 @@ impl GpuIoHandler { let edid_req = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr("the GPU's edid request header", req.out_header.0) + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "the GPU's edid request header", + req.out_header.0 + )) })?; if edid_req.scanouts >= self.base_conf.max_outputs { @@ -770,7 +780,7 @@ impl GpuIoHandler { } self.mem_space .write_object(&edid_resp, req.in_header) - .chain_err(|| "Fail to write displayinfo")?; + .with_context(|| "Fail to write displayinfo")?; self.ctrl_queue .lock() .unwrap() @@ -780,7 +790,7 @@ impl GpuIoHandler { req.index, size_of::() as u32, ) - .chain_err(|| "Fail to add used elem for control queue")?; + .with_context(|| "Fail to add used elem for control queue")?; Ok(()) } @@ -794,11 +804,11 @@ impl GpuIoHandler { let info_create_2d = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's resource create 2d request header", req.out_header.0, - ) + )) })?; if info_create_2d.resource_id == 0 { error!("The 0 value for resource_id is illegal"); @@ -831,7 +841,7 @@ impl GpuIoHandler { }; let pixman_format = self .gpu_get_pixman_format(res.format) - .chain_err(|| "Fail to parse guest format")?; + .with_context(|| "Fail to parse guest format")?; res.host_mem = self.gpu_get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); if res.host_mem + self.used_hostmem < self.max_hostmem { @@ -915,15 +925,15 @@ impl GpuIoHandler { let info_resource_unref = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's resource unref request header", req.out_header.0, - ) + )) })?; self.gpu_destroy_resoure(info_resource_unref.resource_id, need_interrupt, req) - .chain_err(|| "Fail to unref guest resource")?; + .with_context(|| "Fail to unref guest resource")?; self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) } @@ -936,8 +946,11 @@ impl GpuIoHandler { let info_set_scanout = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr("the GPU's set scanout request header", req.out_header.0) + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "the GPU's set scanout request header", + req.out_header.0 + )) })?; if info_set_scanout.scanout_id >= self.base_conf.max_outputs { @@ -1092,11 +1105,11 @@ impl GpuIoHandler { let info_res_flush = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's resource flush request header", req.out_header.0, - ) + )) })?; if let Some(res_index) = self @@ -1193,11 +1206,11 @@ impl GpuIoHandler { let info_transfer = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's transfer to host 2d request header", req.out_header.0, - ) + )) })?; if let Some(res_index) = self @@ -1305,11 +1318,11 @@ impl GpuIoHandler { let info_attach_backing = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's resource attach backing request header", req.out_header.0, - ) + )) })?; if let Some(res_index) = self @@ -1357,16 +1370,16 @@ impl GpuIoHandler { let info_gpu_mem_entry = self .mem_space .read_object::(GuestAddress(entry_addr)) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's resource attach backing request header", req.out_header.0, - ) + )) })?; let iov_base = self .mem_space .get_host_address(GuestAddress(info_gpu_mem_entry.addr)) - .chain_err(|| "Fail to get gpu mem entry host addr")?; + .with_context(|| "Fail to get gpu mem entry host addr")?; let iov_item = Iovec { iov_base, iov_len: info_gpu_mem_entry.length as u64, @@ -1392,11 +1405,11 @@ impl GpuIoHandler { let info_detach_backing = self .mem_space .read_object::(req.out_header) - .chain_err(|| { - ErrorKind::ReadObjectErr( + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( "the GPU's resource detach backing request header", req.out_header.0, - ) + )) })?; if let Some(res_index) = self @@ -1429,36 +1442,36 @@ impl GpuIoHandler { if let Err(e) = match req.header.hdr_type { VIRTIO_GPU_CMD_GET_DISPLAY_INFO => self .gpu_cmd_get_display_info(&mut need_interrupt, req) - .chain_err(|| "Fail to get display info"), + .with_context(|| "Fail to get display info"), VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => self .gpu_cmd_resource_create_2d(&mut need_interrupt, req) - .chain_err(|| "Fail to create 2d resource"), + .with_context(|| "Fail to create 2d resource"), VIRTIO_GPU_CMD_RESOURCE_UNREF => self .gpu_cmd_resource_unref(&mut need_interrupt, req) - .chain_err(|| "Fail to unref resource"), + .with_context(|| "Fail to unref resource"), VIRTIO_GPU_CMD_SET_SCANOUT => self .gpu_cmd_set_scanout(&mut need_interrupt, req) - .chain_err(|| "Fail to set scanout"), + .with_context(|| "Fail to set scanout"), VIRTIO_GPU_CMD_RESOURCE_FLUSH => self .gpu_cmd_resource_flush(&mut need_interrupt, req) - .chain_err(|| "Fail to flush resource"), + .with_context(|| "Fail to flush resource"), VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self .gpu_cmd_transfer_to_host_2d(&mut need_interrupt, req) - .chain_err(|| "Fail to transfer fo host 2d resource"), + .with_context(|| "Fail to transfer fo host 2d resource"), VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self .gpu_cmd_resource_attach_backing(&mut need_interrupt, req) - .chain_err(|| "Fail to attach backing"), + .with_context(|| "Fail to attach backing"), VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self .gpu_cmd_resource_detach_backing(&mut need_interrupt, req) - .chain_err(|| "Fail to detach backing"), + .with_context(|| "Fail to detach backing"), VIRTIO_GPU_CMD_GET_EDID => self .gpu_cmd_get_edid(&mut need_interrupt, req) - .chain_err(|| "Fail to get edid info"), + .with_context(|| "Fail to get edid info"), _ => self .gpu_response_nodata(&mut need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req) - .chain_err(|| "Fail to get nodata response"), + .with_context(|| "Fail to get nodata response"), } { - error!("Fail to handle GPU request, {}", e); + error!("Fail to handle GPU request, {:?}", e); } if need_interrupt { @@ -1466,7 +1479,12 @@ impl GpuIoHandler { &VirtioInterruptType::Vring, Some(&self.ctrl_queue.lock().unwrap()), ) - .chain_err(|| ErrorKind::InterruptTrigger("gpu", VirtioInterruptType::Vring))?; + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "gpu", + VirtioInterruptType::Vring + )) + })?; } } @@ -1489,11 +1507,8 @@ impl GpuIoHandler { queue .vring .add_used(&self.mem_space, elem.index, 0) - .chain_err(|| "Failed to add used ring")?; - error!( - "failed to create GPU request, {}", - error_chain::ChainedError::display_chain(&e) - ); + .with_context(|| "Failed to add used ring")?; + error!("failed to create GPU request, {:?}", e); } } } @@ -1512,22 +1527,23 @@ impl GpuIoHandler { match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CURSOR) { Ok(req) => { self.gpu_update_cursor(&req) - .chain_err(|| "Fail to update cursor")?; + .with_context(|| "Fail to update cursor")?; } Err(e) => { - error!( - "failed to create GPU request, {}", - error_chain::ChainedError::display_chain(&e) - ); + error!("failed to create GPU request, {:?}", e); } } queue .vring .add_used(&self.mem_space, elem.index, 0) - .chain_err(|| "Failed to add used ring")?; + .with_context(|| "Failed to add used ring")?; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) - .chain_err(|| ErrorKind::InterruptTrigger("gpu", VirtioInterruptType::Vring))?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "gpu", + VirtioInterruptType::Vring + )) + })?; } Ok(()) @@ -1584,10 +1600,7 @@ impl EventNotifierHelper for GpuIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = gpu_handler_clone.lock().unwrap().ctrl_queue_evt_handler() { - error!( - "Failed to process queue for virtio gpu, err: {}", - error_chain::ChainedError::display_chain(&e), - ); + error!("Failed to process queue for virtio gpu, err: {:?}", e,); } None @@ -1605,10 +1618,7 @@ impl EventNotifierHelper for GpuIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = gpu_handler_clone.lock().unwrap().cursor_queue_evt_handler() { - error!( - "Failed to process queue for virtio gpu, err: {}", - error_chain::ChainedError::display_chain(&e), - ); + error!("Failed to process queue for virtio gpu, err: {:?}", e,); } None @@ -1741,7 +1751,7 @@ impl VirtioDevice for Gpu { let config_slice = self.state.config.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } if let Some(end) = offset.checked_add(data.len() as u64) { data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; @@ -1755,7 +1765,10 @@ impl VirtioDevice for Gpu { let config_slice = self.state.config.as_mut_bytes(); let config_len = config_slice.len(); if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset, + config_len as u64 + ))); } config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); @@ -1775,7 +1788,10 @@ impl VirtioDevice for Gpu { mut queue_evts: Vec, ) -> Result<()> { if queues.len() != QUEUE_NUM_GPU { - return Err(ErrorKind::IncorrectQueueNum(QUEUE_NUM_GPU, queues.len()).into()); + return Err(anyhow!(VirtioError::IncorrectQueueNum( + QUEUE_NUM_GPU, + queues.len() + ))); } self.interrupt_cb = Some(interrupt_cb.clone()); @@ -1817,6 +1833,6 @@ impl VirtioDevice for Gpu { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 8f4ea473f..a3190aa0d 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -26,80 +26,6 @@ //! - `aarch64` #[macro_use] extern crate log; -pub mod errors { - use error_chain::error_chain; - - error_chain! { - foreign_links { - Io(std::io::Error); - } - links { - Util(util::errors::Error, util::errors::ErrorKind); - AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); - SysBus(sysbus::errors::Error, sysbus::errors::ErrorKind); - } - errors { - EventFdCreate { - display("Failed to create eventfd.") - } - EventFdWrite { - display("Failed to write eventfd.") - } - ThreadCreate(name: String) { - display("Failed to create {} thread", name) - } - ChannelSend(value: String) { - display("Failed to send {} on the channel", value) - } - QueueIndex(index: u16, size: u16) { - display("Queue index {} invalid, queue size is {}", index, size) - } - QueueDescInvalid { - display("Vring descriptor is invalid") - } - AddressOverflow(value: &'static str, address: u64, offset: u64) { - display("Address overflows for {}, address: 0x{:x}, offset: {}", value, address, offset) - } - DevConfigOverflow(offset: u64, size: u64) { - display("Failed to r/w dev config space: overflows, offset {}, space size {}", offset, size) - } - InterruptTrigger(dev_ty: &'static str, int_type: super::VirtioInterruptType) { - display("Failed to trigger interrupt for {}, int-type {:#?}", dev_ty, int_type) - } - VhostIoctl(ioctl: String) { - display("Vhost ioctl failed: {}", ioctl) - } - ElementEmpty { - display("Failed to get iovec from element!") - } - VirtQueueIsNone { - display("Virt queue is none!") - } - IncorrectQueueNum(expect: usize, actual: usize) { - display("Cannot perform activate. Expected {} queue(s), got {}", expect, actual) - } - IncorrectOffset(expect: u64, actual: u64) { - display("Incorrect offset, expected {}, got {}", expect, actual) - } - DeviceNotActivated(devname: String) { - display("Device {} not activated", devname) - } - FailedToWriteConfig { - display("Failed to write config") - } - ReadObjectErr(object: &'static str, address: u64) { - display("Failed to read object for {}, address: 0x{:x}", object, address) - } - DevStatErr(status: u32) { - display("Invalid device status: 0x{:x}.", status) - } - MmioRegErr(offset: u64) { - display("Unsupported mmio register at offset 0x{:x}.", offset) - } - } - } -} - mod balloon; mod block; mod console; @@ -113,11 +39,13 @@ mod virtio_mmio; #[allow(dead_code)] mod virtio_pci; extern crate util; - +pub mod error; +pub use anyhow::Result; pub use balloon::*; pub use block::{Block, BlockState}; pub use console::{Console, VirtioConsoleState}; -pub use errors::*; +pub use error::VirtioError; +pub use error::*; #[cfg(not(target_env = "musl"))] pub use gpu::*; pub use net::*; @@ -131,7 +59,7 @@ pub use virtio_pci::VirtioPciDevice; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use error_chain::bail; +use anyhow::bail; use machine_manager::config::ConfigCheck; use vmm_sys_util::eventfd::EventFd; @@ -209,8 +137,6 @@ pub const VIRTIO_BLK_F_BLK_SIZE: u32 = 6; pub const VIRTIO_BLK_F_FLUSH: u32 = 9; /// Topology information is available. pub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10; -/// Support more than one virtqueue. -pub const VIRTIO_BLK_F_MQ: u32 = 12; /// DISCARD is supported. pub const VIRTIO_BLK_F_DISCARD: u32 = 13; /// WRITE ZEROES is supported. @@ -226,6 +152,8 @@ pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0; pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: u16 = 1; /// The maximum pairs of multiple queue. pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000; +/// Support more than one virtqueue. +pub const VIRTIO_BLK_F_MQ: u32 = 12; /// The IO type of virtio block, refer to Virtio Spec. /// Read. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 8ccf2ece3..0d333c48f 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -17,8 +17,19 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; +use super::{ + Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_OK, VIRTIO_TYPE_NET, +}; +use crate::virtio_has_feature; +use crate::VirtioError; use address_space::AddressSpace; -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, @@ -33,19 +44,6 @@ use util::loop_context::{ use util::num_ops::{read_u32, write_u32}; use util::tap::{Tap, IFF_MULTI_QUEUE, TUN_F_VIRTIO}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; - -use super::errors::{ErrorKind, Result, ResultExt}; -use super::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, - VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_OK, VIRTIO_TYPE_NET, -}; -use crate::virtio_has_feature; - /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; /// Size of each virtqueue. @@ -120,7 +118,7 @@ impl NetCtrlHandler { .unwrap() .vring .pop_avail(&self.mem_space, self.driver_features) - .chain_err(|| "Failed to pop avail ring for net control queue")?; + .with_context(|| "Failed to pop avail ring for net control queue")?; let mut used_len = 0; if let Some(ctrl_desc) = elem.out_iovec.get(0) { @@ -128,7 +126,7 @@ impl NetCtrlHandler { let ctrl_hdr = self .mem_space .read_object::(ctrl_desc.addr) - .chain_err(|| "Failed to get control queue descriptor")?; + .with_context(|| "Failed to get control queue descriptor")?; match ctrl_hdr.class as u16 { VIRTIO_NET_CTRL_MQ => { if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { @@ -142,7 +140,7 @@ impl NetCtrlHandler { let queue_pairs = self .mem_space .read_object::(mq_desc.addr) - .chain_err(|| "Failed to read multi queue descriptor")?; + .with_context(|| "Failed to read multi queue descriptor")?; if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { @@ -170,13 +168,18 @@ impl NetCtrlHandler { .unwrap() .vring .add_used(&self.mem_space, elem.index, used_len) - .chain_err(|| format!("Failed to add used ring {}", elem.index))?; + .with_context(|| format!("Failed to add used ring {}", elem.index))?; (self.interrupt_cb)( &VirtioInterruptType::Vring, Some(&self.ctrl.queue.lock().unwrap()), ) - .chain_err(|| ErrorKind::InterruptTrigger("ctrl", VirtioInterruptType::Vring))?; + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "ctrl", + VirtioInterruptType::Vring + )) + })?; Ok(()) } @@ -297,7 +300,7 @@ impl NetIoHandler { let elem = queue .vring .pop_avail(&self.mem_space, self.driver_features) - .chain_err(|| "Failed to pop avail ring for net rx")?; + .with_context(|| "Failed to pop avail ring for net rx")?; let mut iovecs = Vec::new(); for elem_iov in elem.in_iovec.iter() { let host_addr = queue @@ -344,7 +347,7 @@ impl NetIoHandler { queue .vring .add_used(&self.mem_space, elem.index, write_count as u32) - .chain_err(|| { + .with_context(|| { format!( "Failed to add used ring for net rx, index: {}, len: {}", elem.index, write_count @@ -355,8 +358,12 @@ impl NetIoHandler { if self.rx.need_irqs { self.rx.need_irqs = false; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) - .chain_err(|| ErrorKind::InterruptTrigger("net", VirtioInterruptType::Vring))?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "net", + VirtioInterruptType::Vring + )) + })?; self.trace_send_interrupt("Net".to_string()); } @@ -404,14 +411,18 @@ impl NetIoHandler { queue .vring .add_used(&self.mem_space, elem.index, 0) - .chain_err(|| format!("Net tx: Failed to add used ring {}", elem.index))?; + .with_context(|| format!("Net tx: Failed to add used ring {}", elem.index))?; need_irq = true; } if need_irq { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) - .chain_err(|| ErrorKind::InterruptTrigger("net", VirtioInterruptType::Vring))?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "net", + VirtioInterruptType::Vring + )) + })?; self.trace_send_interrupt("Net".to_string()); } @@ -588,10 +599,7 @@ impl EventNotifierHelper for NetIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); if let Err(ref e) = cloned_net_io.lock().unwrap().handle_tx() { - error!( - "Failed to handle tx(tx event) for net, {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to handle tx(tx event) for net, {:?}", e); } None }); @@ -609,10 +617,7 @@ impl EventNotifierHelper for NetIoHandler { let handler: Box = Box::new(move |_, _| { let mut locked_net_io = cloned_net_io.lock().unwrap(); if let Err(ref e) = locked_net_io.handle_rx() { - error!( - "Failed to handle rx(tap event), {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to handle rx(tap event), {:?}", e); } if let Some(tap) = locked_net_io.tap.as_ref() { @@ -736,10 +741,10 @@ fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> { } let is_mq = queue_pair > 1; - let ifr_flag = - fs::read_to_string(tap_path).chain_err(|| "Failed to read content from tun_flags file")?; + let ifr_flag = fs::read_to_string(tap_path) + .with_context(|| "Failed to read content from tun_flags file")?; let flags = u16::from_str_radix(ifr_flag.trim().trim_start_matches("0x"), 16) - .chain_err(|| "Failed to parse tap ifr flag")?; + .with_context(|| "Failed to parse tap ifr flag")?; if (flags & IFF_MULTI_QUEUE != 0) && !is_mq { bail!(format!( "Tap device supports mq, but command set queue pairs {}.", @@ -779,14 +784,14 @@ pub fn create_tap( let tap = if let Some(fds) = net_fds { let fd = fds .get(index as usize) - .chain_err(|| format!("Failed to get fd from index {}", index))?; + .with_context(|| format!("Failed to get fd from index {}", index))?; Tap::new(None, Some(*fd), queue_pairs) - .chain_err(|| format!("Failed to create tap, index is {}", index))? + .with_context(|| format!("Failed to create tap, index is {}", index))? } else { // `unwrap()` won't fail because the arguments have been checked let dev_name = host_dev_name.unwrap(); check_mq(dev_name, queue_pairs)?; - Tap::new(Some(dev_name), None, queue_pairs).chain_err(|| { + Tap::new(Some(dev_name), None, queue_pairs).with_context(|| { format!( "Failed to create tap with name {}, index is {}", dev_name, index @@ -795,11 +800,11 @@ pub fn create_tap( }; tap.set_offload(TUN_F_VIRTIO) - .chain_err(|| "Failed to set tap offload")?; + .with_context(|| "Failed to set tap offload")?; let vnet_hdr_size = mem::size_of::() as u32; tap.set_hdr_size(vnet_hdr_size) - .chain_err(|| "Failed to set tap hdr size")?; + .with_context(|| "Failed to set tap hdr size")?; taps.push(tap); } @@ -842,7 +847,7 @@ impl VirtioDevice for Net { if !self.net_cfg.host_dev_name.is_empty() { self.taps = None; self.taps = create_tap(None, Some(&self.net_cfg.host_dev_name), queue_pairs) - .chain_err(|| "Failed to open tap with file path")?; + .with_context(|| "Failed to open tap with file path")?; } else if let Some(fds) = self.net_cfg.tap_fds.as_mut() { let mut created_fds = 0; if let Some(taps) = &self.taps { @@ -854,8 +859,8 @@ impl VirtioDevice for Net { } if created_fds != fds.len() { - self.taps = - create_tap(Some(fds), None, queue_pairs).chain_err(|| "Failed to open tap")?; + self.taps = create_tap(Some(fds), None, queue_pairs) + .with_context(|| "Failed to open tap")?; } } else { self.taps = None; @@ -922,7 +927,7 @@ impl VirtioDevice for Net { let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } if let Some(end) = offset.checked_add(data.len() as u64) { data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; @@ -934,7 +939,6 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let config_slice = self.state.config_space.as_mut_bytes(); - if !virtio_has_feature(self.state.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) && !virtio_has_feature(self.state.driver_features, VIRTIO_F_VERSION_1) && offset == 0 @@ -1033,20 +1037,20 @@ impl VirtioDevice for Net { let tap = taps .get(index) .cloned() - .chain_err(|| format!("Failed to get index {} tap", index))?; - sender - .send(Some(tap)) - .chain_err(|| ErrorKind::ChannelSend("tap fd".to_string()))?; + .with_context(|| format!("Failed to get index {} tap", index))?; + sender.send(Some(tap)).with_context(|| { + anyhow!(VirtioError::ChannelSend("tap fd".to_string())) + })?; } None => sender .send(None) - .chain_err(|| "Failed to send status of None to channel".to_string())?, + .with_context(|| "Failed to send status of None to channel".to_string())?, } } self.update_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } Ok(()) @@ -1055,7 +1059,7 @@ impl VirtioDevice for Net { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } } @@ -1065,13 +1069,13 @@ impl VirtioDevice for Net { unsafe impl Sync for Net {} impl StateTransfer for Net { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *VirtioNetState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("NET"))?; + .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("NET")))?; Ok(()) } diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 0e5779c36..4d15f8cea 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -17,12 +17,12 @@ use std::sync::atomic::{fence, Ordering}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; -use error_chain::bail; use log::{error, warn}; use util::byte_code::ByteCode; -use super::errors::{ErrorKind, Result, ResultExt}; use super::{virtio_has_feature, VIRTIO_F_RING_EVENT_IDX}; +use crate::VirtioError; +use anyhow::{anyhow, bail, Context, Result}; /// When host consumes a buffer, don't interrupt the guest. const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; @@ -43,8 +43,13 @@ fn checked_offset_mem( offset ); } - base.checked_add(offset) - .ok_or_else(|| ErrorKind::AddressOverflow("queue", base.raw_value(), offset).into()) + base.checked_add(offset).ok_or_else(|| { + anyhow!(VirtioError::AddressOverflow( + "queue", + base.raw_value(), + offset + )) + }) } #[derive(Default, Clone, Copy)] @@ -270,26 +275,26 @@ impl SplitVringDesc { cache: &mut Option, ) -> Result { if index >= queue_size { - return Err(ErrorKind::QueueIndex(index, queue_size).into()); + return Err(anyhow!(VirtioError::QueueIndex(index, queue_size))); } let desc_addr = desc_table_host .checked_add(u64::from(index) * DESCRIPTOR_LEN) - .chain_err(|| { - ErrorKind::AddressOverflow( + .with_context(|| { + anyhow!(VirtioError::AddressOverflow( "creating a descriptor", desc_table_host, u64::from(index) * DESCRIPTOR_LEN, - ) + )) })?; let desc = sys_mem .read_object_direct::(desc_addr) - .chain_err(|| ErrorKind::ReadObjectErr("a descriptor", desc_addr))?; + .with_context(|| anyhow!(VirtioError::ReadObjectErr("a descriptor", desc_addr)))?; if desc.is_valid(sys_mem, queue_size, cache) { Ok(desc) } else { - Err(ErrorKind::QueueDescInvalid.into()) + Err(anyhow!(VirtioError::QueueDescInvalid)) } } @@ -319,10 +324,7 @@ impl SplitVringDesc { if miss_cached { if let Err(ref e) = checked_offset_mem(sys_mem, self.addr, u64::from(self.len)) { - error!( - "The memory of descriptor is invalid, {} ", - error_chain::ChainedError::display_chain(e), - ); + error!("The memory of descriptor is invalid, {:?} ", e); return false; } } @@ -352,7 +354,7 @@ impl SplitVringDesc { cache: &mut Option, ) -> Result { SplitVringDesc::new(sys_mem, desc_table_host, queue_size, index, cache) - .chain_err(|| format!("Failed to find next descriptor {}", index)) + .with_context(|| format!("Failed to find next descriptor {}", index)) } /// Check whether this descriptor is write-only or read-only. @@ -430,7 +432,7 @@ impl SplitVringDesc { elem: &mut Element, ) -> Result<()> { if !self.is_valid_indirect_desc() { - return Err(ErrorKind::QueueDescInvalid.into()); + return Err(anyhow!(VirtioError::QueueDescInvalid)); } let desc_num = self.get_desc_num(); @@ -440,7 +442,7 @@ impl SplitVringDesc { }; let desc = Self::next_desc(sys_mem, desc_hva, desc_num, 0, cache)?; Self::get_element(sys_mem, desc_hva, desc_num, index, desc, cache, elem) - .chain_err(|| + .with_context(|| format!("Failed to get element from indirect descriptor chain {}, table entry addr: 0x{:X}, size: {}", index, self.addr.0, desc_num) ) @@ -456,7 +458,7 @@ impl SplitVringDesc { cache: &mut Option, elem: &mut Element, ) -> Result<()> { - Self::get_element(sys_mem, desc_table_host, queue_size, index, *self, cache, elem).chain_err(|| { + Self::get_element(sys_mem, desc_table_host, queue_size, index, *self, cache, elem).with_context(|| { format!( "Failed to get element from normal descriptor chain {}, table addr: 0x{:X}, size: {}", index, desc_table_host, queue_size @@ -541,7 +543,12 @@ impl SplitVring { fn get_avail_idx(&self, sys_mem: &Arc) -> Result { let avail_flags_idx = sys_mem .read_object_direct::(self.addr_cache.avail_ring_host) - .chain_err(|| ErrorKind::ReadObjectErr("avail id", self.avail_ring.raw_value()))?; + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "avail id", + self.avail_ring.raw_value() + )) + })?; Ok(avail_flags_idx.idx) } @@ -549,7 +556,12 @@ impl SplitVring { fn get_avail_flags(&self, sys_mem: &Arc) -> Result { let avail_flags_idx: SplitVringFlagsIdx = sys_mem .read_object_direct::(self.addr_cache.avail_ring_host) - .chain_err(|| ErrorKind::ReadObjectErr("avail flags", self.avail_ring.raw_value()))?; + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "avail flags", + self.avail_ring.raw_value() + )) + })?; Ok(avail_flags_idx.flags) } @@ -559,7 +571,12 @@ impl SplitVring { fence(Ordering::SeqCst); let used_flag_idx: SplitVringFlagsIdx = sys_mem .read_object_direct::(self.addr_cache.used_ring_host) - .chain_err(|| ErrorKind::ReadObjectErr("used id", self.used_ring.raw_value()))?; + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "used id", + self.used_ring.raw_value() + )) + })?; Ok(used_flag_idx.idx) } @@ -574,7 +591,7 @@ impl SplitVring { &event_idx, self.addr_cache.used_ring_host + avail_event_offset, ) - .chain_err(|| { + .with_context(|| { format!( "Failed to set avail event idx, used_ring: 0x{:X}, offset: {}", self.used_ring.raw_value(), @@ -596,16 +613,18 @@ impl SplitVring { .addr_cache .avail_ring_host .checked_add(used_event_offset) - .chain_err(|| { - ErrorKind::AddressOverflow( + .with_context(|| { + anyhow!(VirtioError::AddressOverflow( "getting used event idx", self.avail_ring.raw_value(), used_event_offset, - ) + )) })?; let used_event = sys_mem .read_object_direct::(used_event_addr) - .chain_err(|| ErrorKind::ReadObjectErr("used event id", used_event_addr))?; + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr("used event id", used_event_addr)) + })?; Ok(used_event) } @@ -616,8 +635,8 @@ impl SplitVring { Ok(avail_flags) => (avail_flags & VRING_AVAIL_F_NO_INTERRUPT) != 0, Err(ref e) => { warn!( - "Failed to get the status for VRING_AVAIL_F_NO_INTERRUPT {}", - error_chain::ChainedError::display_chain(e) + "Failed to get the status for VRING_AVAIL_F_NO_INTERRUPT {:?}", + e ); false } @@ -630,10 +649,7 @@ impl SplitVring { let new = match self.get_used_idx(sys_mem) { Ok(used_idx) => Wrapping(used_idx), Err(ref e) => { - error!( - "Failed to get the status for notifying used vring {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to get the status for notifying used vring {:?}", e); return false; } }; @@ -641,10 +657,7 @@ impl SplitVring { let used_event_idx = match self.get_used_event(sys_mem) { Ok(idx) => Wrapping(idx), Err(ref e) => { - error!( - "Failed to get the status for notifying used vring {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to get the status for notifying used vring {:?}", e); return false; } }; @@ -659,10 +672,10 @@ impl SplitVring { Ok(addr) => addr, Err(ref e) => { error!( - "descriptor table is out of bounds: start:0x{:X} size:{} {}", + "descriptor table is out of bounds: start:0x{:X} size:{} {:?}", self.desc_table.raw_value(), DESCRIPTOR_LEN * actual_size, - error_chain::ChainedError::display_chain(e), + e ); return true; } @@ -676,10 +689,10 @@ impl SplitVring { Ok(addr) => addr, Err(ref e) => { error!( - "avail ring is out of bounds: start:0x{:X} size:{} {}", + "avail ring is out of bounds: start:0x{:X} size:{} {:?}", self.avail_ring.raw_value(), VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * actual_size, - error_chain::ChainedError::display_chain(e), + e ); return true; } @@ -691,10 +704,10 @@ impl SplitVring { VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * actual_size, ) { error!( - "used ring is out of bounds: start:0x{:X} size:{} {}", + "used ring is out of bounds: start:0x{:X} size:{} {:?}", self.used_ring.raw_value(), VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * actual_size, - error_chain::ChainedError::display_chain(e), + e ); return true; } @@ -744,16 +757,21 @@ impl SplitVring { .addr_cache .avail_ring_host .checked_add(index_offset) - .chain_err(|| { - ErrorKind::AddressOverflow( + .with_context(|| { + anyhow!(VirtioError::AddressOverflow( "popping avail ring", self.avail_ring.raw_value(), index_offset, - ) + )) })?; let desc_index = sys_mem .read_object_direct::(desc_index_addr) - .chain_err(|| ErrorKind::ReadObjectErr("the index of descriptor", desc_index_addr))?; + .with_context(|| { + anyhow!(VirtioError::ReadObjectErr( + "the index of descriptor", + desc_index_addr + )) + })?; let desc = SplitVringDesc::new( sys_mem, @@ -772,7 +790,7 @@ impl SplitVring { self.next_avail += Wrapping(1); elem }) - .chain_err(|| "Failed to get indirect desc for popping avail ring")? + .with_context(|| "Failed to get indirect desc for popping avail ring")? } else { desc.get_nonindirect_desc( sys_mem, @@ -790,7 +808,7 @@ impl SplitVring { if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { self.set_avail_event(sys_mem) - .chain_err(|| "Failed to set avail event for popping avail ring")?; + .with_context(|| "Failed to set avail event for popping avail ring")?; } Ok(()) @@ -823,7 +841,7 @@ impl VringOps for SplitVring { let mut element = Element::new(0); self.get_vring_element(sys_mem, features, &mut element) - .chain_err(|| "Failed to get vring element")?; + .with_context(|| "Failed to get vring element")?; Ok(element) } @@ -834,7 +852,7 @@ impl VringOps for SplitVring { fn add_used(&mut self, sys_mem: &Arc, index: u16, len: u32) -> Result<()> { if index >= self.size { - return Err(ErrorKind::QueueIndex(index, self.size).into()); + return Err(anyhow!(VirtioError::QueueIndex(index, self.size))); } let next_used = u64::from(self.next_used.0 % self.actual_size()); @@ -846,7 +864,7 @@ impl VringOps for SplitVring { }; sys_mem .write_object_direct::(&used_elem, used_elem_addr) - .chain_err(|| "Failed to write object for used element")?; + .with_context(|| "Failed to write object for used element")?; self.next_used += Wrapping(1); @@ -857,7 +875,7 @@ impl VringOps for SplitVring { &(self.next_used.0 as u16), self.addr_cache.used_ring_host + VRING_IDX_POSITION, ) - .chain_err(|| "Failed to write next used idx")?; + .with_context(|| "Failed to write next used idx")?; Ok(()) } @@ -1024,7 +1042,7 @@ mod tests { next: u16, ) -> Result<()> { if index >= self.actual_size() { - return Err(ErrorKind::QueueIndex(index, self.size).into()); + return Err(anyhow!(VirtioError::QueueIndex(index, self.size))); } let desc_addr_offset = DESCRIPTOR_LEN * index as u64; @@ -1707,10 +1725,16 @@ mod tests { assert_eq!(vring.is_valid(&sys_space), true); // it is false when the index is more than the size of queue - let err = vring.add_used(&sys_space, QUEUE_SIZE, 100).unwrap_err(); - if let ErrorKind::QueueIndex(offset, size) = err.kind() { - assert_eq!(*offset, 256); - assert_eq!(*size, 256); + if let Err(err) = vring.add_used(&sys_space, QUEUE_SIZE, 100) { + if let Some(e) = err.downcast_ref::() { + match e { + VirtioError::QueueIndex(offset, size) => { + assert_eq!(*offset, 256); + assert_eq!(*size, 256); + } + _ => (), + } + } } assert!(vring.add_used(&sys_space, 10, 100).is_ok()); diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index f5d86da7a..f18ab9c82 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -27,17 +27,17 @@ use util::loop_context::{ }; use util::num_ops::{read_u32, write_u32}; -use error_chain::bail; use log::{error, warn}; use migration_derive::{ByteCode, Desc}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use super::errors::{ErrorKind, Result, ResultExt}; use super::{ ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; +use crate::error::VirtioError; +use anyhow::{anyhow, bail, Context, Result}; const QUEUE_NUM_RNG: usize = 1; const QUEUE_SIZE_RNG: u16 = 256; @@ -71,7 +71,7 @@ impl RngHandler { for iov in in_iov { self.mem_space .write(&mut buffer[offset..].as_ref(), iov.addr, iov.len as u64) - .chain_err(|| "Failed to write request data for virtio rng")?; + .with_context(|| "Failed to write request data for virtio rng")?; offset += iov.len as usize; } @@ -88,7 +88,7 @@ impl RngHandler { .pop_avail(&self.mem_space, self.driver_features) { let size = - get_req_data_size(&elem.in_iovec).chain_err(|| "Failed to get request size")?; + get_req_data_size(&elem.in_iovec).with_context(|| "Failed to get request size")?; if let Some(leak_bucket) = self.leak_bucket.as_mut() { if let Some(ctx) = EventLoop::get_ctx(None) { @@ -108,14 +108,14 @@ impl RngHandler { size as usize, 0, ) - .chain_err(|| format!("Failed to read random file, size: {}", size))?; + .with_context(|| format!("Failed to read random file, size: {}", size))?; self.write_req_data(&elem.in_iovec, &mut buffer)?; queue_lock .vring .add_used(&self.mem_space, elem.index, size) - .chain_err(|| { + .with_context(|| { format!( "Failed to add used ring, index: {}, size: {}", elem.index, size @@ -126,8 +126,14 @@ impl RngHandler { } if need_interrupt { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock)) - .chain_err(|| ErrorKind::InterruptTrigger("rng", VirtioInterruptType::Vring))?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock)).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "rng", + VirtioInterruptType::Vring + )) + }, + )?; self.trace_send_interrupt("Rng".to_string()); } @@ -175,10 +181,7 @@ impl EventNotifierHelper for RngHandler { read_fd(fd); if let Err(ref e) = rng_handler_clone.lock().unwrap().process_queue() { - error!( - "Failed to process queue for virtio rng, err: {}", - error_chain::ChainedError::display_chain(e), - ); + error!("Failed to process queue for virtio rng, err: {:?}", e,); } None @@ -216,10 +219,7 @@ impl EventNotifierHelper for RngHandler { } if let Err(ref e) = rng_handler_clone.lock().unwrap().process_queue() { - error!( - "Failed to process queue for virtio rng, err: {}", - error_chain::ChainedError::display_chain(e), - ); + error!("Failed to process queue for virtio rng, err: {:?}", e,); } None @@ -298,9 +298,9 @@ impl VirtioDevice for Rng { /// Realize virtio rng device. fn realize(&mut self) -> Result<()> { self.check_random_file() - .chain_err(|| "Failed to check random file")?; + .with_context(|| "Failed to check random file")?; let file = File::open(&self.rng_cfg.random_file) - .chain_err(|| "Failed to open file of random number generator")?; + .with_context(|| "Failed to open file of random number generator")?; self.random_file = Some(file); self.state.device_features = 1 << VIRTIO_F_VERSION_1 as u64; @@ -380,7 +380,7 @@ impl VirtioDevice for Rng { .as_ref() .unwrap() .try_clone() - .chain_err(|| "Failed to clone random file for virtio rng")?, + .with_context(|| "Failed to clone random file for virtio rng")?, leak_bucket: self.rng_cfg.bytes_per_sec.map(LeakBucket::new), }; @@ -395,18 +395,18 @@ impl VirtioDevice for Rng { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } } impl StateTransfer for Rng { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *RngState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("RNG"))?; + .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("RNG")))?; Ok(()) } @@ -597,7 +597,9 @@ mod tests { VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status, Ordering::SeqCst); - interrupt_evt.write(1).chain_err(|| ErrorKind::EventFdWrite) + interrupt_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) }, ) as VirtioInterrupt); @@ -680,7 +682,9 @@ mod tests { VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status, Ordering::SeqCst); - interrupt_evt.write(1).chain_err(|| ErrorKind::EventFdWrite) + interrupt_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) }, ) as VirtioInterrupt); diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 3deef2ad2..5ae3178f8 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -24,7 +24,6 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use error_chain::ChainedError; use log::{debug, error}; use util::byte_code::ByteCode; use util::loop_context::{ @@ -35,9 +34,10 @@ use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; -use super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; use super::{VhostNotify, VhostOps}; +use crate::VirtioError; +use anyhow::{anyhow, Context, Result}; /// Refer to VHOST_VIRTIO in /// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. @@ -199,7 +199,7 @@ impl Listener for VhostMemInfo { range: Option<&FlatRange>, _evtfd: Option<&RegionIoEventFd>, req_type: ListenerReqType, - ) -> std::result::Result<(), address_space::errors::Error> { + ) -> std::result::Result<(), anyhow::Error> { match req_type { ListenerReqType::AddRegion => { if Self::check_vhost_mem_range(range.unwrap()) { @@ -236,7 +236,7 @@ impl VhostBackend { .write(true) .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) .open(path) - .chain_err(|| format!("Failed to open {} for vhost backend.", path))?, + .with_context(|| format!("Failed to open {} for vhost backend.", path))?, }; let mem_info = Arc::new(Mutex::new(VhostMemInfo::new())); mem_space.register_listener(mem_info.clone())?; @@ -255,7 +255,9 @@ impl VhostOps for VhostBackend { fn set_owner(&self) -> Result<()> { let ret = unsafe { ioctl(self, VHOST_SET_OWNER()) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_OWNER".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_OWNER".to_string() + ))); } Ok(()) } @@ -263,7 +265,9 @@ impl VhostOps for VhostBackend { fn reset_owner(&self) -> Result<()> { let ret = unsafe { ioctl(self, VHOST_RESET_OWNER()) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_RESET_OWNER".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_RESET_OWNER".to_string() + ))); } Ok(()) } @@ -272,7 +276,9 @@ impl VhostOps for VhostBackend { let mut avail_features: u64 = 0; let ret = unsafe { ioctl_with_mut_ref(self, VHOST_GET_FEATURES(), &mut avail_features) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_GET_FEATURES".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_GET_FEATURES".to_string() + ))); } Ok(avail_features) } @@ -280,7 +286,9 @@ impl VhostOps for VhostBackend { fn set_features(&self, features: u64) -> Result<()> { let ret = unsafe { ioctl_with_ref(self, VHOST_SET_FEATURES(), &features) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_FEATURES".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_FEATURES".to_string() + ))); } Ok(()) } @@ -308,7 +316,9 @@ impl VhostOps for VhostBackend { let ret = unsafe { ioctl_with_ptr(self, VHOST_SET_MEM_TABLE(), bytes.as_ptr()) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_MEM_TABLE".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_MEM_TABLE".to_string() + ))); } Ok(()) } @@ -320,7 +330,9 @@ impl VhostOps for VhostBackend { }; let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_NUM(), &vring_state) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_VRING_NUM".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_VRING_NUM".to_string() + ))); } Ok(()) } @@ -330,7 +342,7 @@ impl VhostOps for VhostBackend { let desc_user_addr = locked_mem_info .addr_to_host(queue_config.desc_table) .ok_or_else(|| { - ErrorKind::Msg(format!( + anyhow!(anyhow!( "Failed to transform desc-table address {}", queue_config.desc_table.0 )) @@ -338,7 +350,7 @@ impl VhostOps for VhostBackend { let used_user_addr = locked_mem_info .addr_to_host(queue_config.used_ring) .ok_or_else(|| { - ErrorKind::Msg(format!( + anyhow!(anyhow!( "Failed to transform used ring address {}", queue_config.used_ring.0 )) @@ -346,10 +358,10 @@ impl VhostOps for VhostBackend { let avail_user_addr = locked_mem_info .addr_to_host(queue_config.avail_ring) .ok_or_else(|| { - ErrorKind::Msg(format!( + anyhow!( "Failed to transform avail ring address {}", queue_config.avail_ring.0 - )) + ) })?; let vring_addr = VhostVringAddr { @@ -363,7 +375,9 @@ impl VhostOps for VhostBackend { let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_VRING_ADDR".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_VRING_ADDR".to_string() + ))); } Ok(()) } @@ -375,7 +389,9 @@ impl VhostOps for VhostBackend { }; let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_BASE(), &vring_state) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_VRING_BASE".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_VRING_BASE".to_string() + ))); } Ok(()) } @@ -388,7 +404,9 @@ impl VhostOps for VhostBackend { let ret = unsafe { ioctl_with_ref(self, VHOST_GET_VRING_BASE(), &vring_state) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_GET_VRING_BASE".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_GET_VRING_BASE".to_string() + ))); } Ok(vring_state.num as u16) } @@ -400,7 +418,9 @@ impl VhostOps for VhostBackend { }; let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_CALL(), &vring_file) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_VRING_CALL".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_VRING_CALL".to_string() + ))); } Ok(()) } @@ -412,7 +432,9 @@ impl VhostOps for VhostBackend { }; let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_KICK(), &vring_file) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_SET_VRING_KICK".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_SET_VRING_KICK".to_string() + ))); } Ok(()) } @@ -466,8 +488,8 @@ impl EventNotifierHelper for VhostIoHandler { Some(&host_notify.queue.lock().unwrap()), ) { error!( - "Failed to trigger interrupt for vhost device, error is {}", - e.display_chain() + "Failed to trigger interrupt for vhost device, error is {:?}", + e ); } } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index a5d290100..66da16804 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -16,8 +16,9 @@ use std::io::Write; use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; +use crate::error::VirtioError; use address_space::AddressSpace; -use error_chain::bail; +use anyhow::{anyhow, bail, Context, Result}; use log::warn; use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; @@ -27,7 +28,6 @@ use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::{ net::{build_device_config_space, create_tap, VirtioNetConfig, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_ACCESS_PLATFORM, @@ -69,7 +69,9 @@ impl VhostNetBackend for VhostBackend { let ret = unsafe { ioctl_with_ref(self, VHOST_NET_SET_BACKEND(), &vring_file) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_NET_SET_BACKEND".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_NET_SET_BACKEND".to_string() + ))); } Ok(()) } @@ -126,16 +128,16 @@ impl VirtioDevice for Net { }; let backend = VhostBackend::new(&self.mem_space, "/dev/vhost-net", fd) - .chain_err(|| "Failed to create backend for vhost net")?; + .with_context(|| "Failed to create backend for vhost net")?; backend .set_owner() - .chain_err(|| "Failed to set owner for vhost net")?; + .with_context(|| "Failed to set owner for vhost net")?; backends.push(backend); } let mut vhost_features = backends[0] .get_features() - .chain_err(|| "Failed to get features for vhost net")?; + .with_context(|| "Failed to get features for vhost net")?; vhost_features &= !(1_u64 << VHOST_NET_F_VIRTIO_NET_HDR); vhost_features &= !(1_u64 << VIRTIO_F_ACCESS_PLATFORM); @@ -167,7 +169,7 @@ impl VirtioDevice for Net { }; self.taps = create_tap(self.net_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) - .chain_err(|| "Failed to create tap for vhost net")?; + .with_context(|| "Failed to create tap for vhost net")?; self.backends = Some(backends); self.device_features = device_features; self.vhost_features = vhost_features; @@ -227,7 +229,7 @@ impl VirtioDevice for Net { let config_slice = self.device_config.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { - return Err(ErrorKind::DevConfigOverflow(offset, config_size).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); } if let Some(end) = offset.checked_add(data.len() as u64) { data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; @@ -285,18 +287,18 @@ impl VirtioDevice for Net { for index in 0..queue_pairs { let mut host_notifies = Vec::new(); let backend = match &self.backends { - None => return Err("Failed to get backend for vhost net".into()), + None => return Err(anyhow!("Failed to get backend for vhost net")), Some(backends) => backends .get(index) - .chain_err(|| format!("Failed to get index {} vhost backend", index))?, + .with_context(|| format!("Failed to get index {} vhost backend", index))?, }; backend .set_features(self.vhost_features) - .chain_err(|| "Failed to set features for vhost net")?; + .with_context(|| "Failed to set features for vhost net")?; backend .set_mem_table() - .chain_err(|| "Failed to set mem table for vhost net")?; + .with_context(|| "Failed to set mem table for vhost net")?; for queue_index in 0..2 { let queue_mutex = queues[index * 2 + queue_index].clone(); @@ -306,7 +308,7 @@ impl VirtioDevice for Net { backend .set_vring_num(queue_index, actual_size) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring num for vhost net, index: {} size: {}", queue_index, actual_size, @@ -314,13 +316,13 @@ impl VirtioDevice for Net { })?; backend .set_vring_addr(&queue_config, queue_index, 0) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring addr for vhost net, index: {}", queue_index, ) })?; - backend.set_vring_base(queue_index, 0).chain_err(|| { + backend.set_vring_base(queue_index, 0).with_context(|| { format!( "Failed to set vring base for vhost net, index: {}", queue_index, @@ -328,7 +330,7 @@ impl VirtioDevice for Net { })?; backend .set_vring_kick(queue_index, &queue_evts[index * 2 + queue_index]) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring kick for vhost net, index: {}", index * 2 + queue_index, @@ -339,12 +341,12 @@ impl VirtioDevice for Net { let host_notify = VhostNotify { notify_evt: EventFd::new(libc::EFD_NONBLOCK) - .chain_err(|| ErrorKind::EventFdCreate)?, + .with_context(|| anyhow!(VirtioError::EventFdCreate))?, queue: queue_mutex.clone(), }; backend .set_vring_call(queue_index, &host_notify.notify_evt) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring call for vhost net, index: {}", queue_index, @@ -356,12 +358,14 @@ impl VirtioDevice for Net { None => bail!("Failed to get tap for vhost net"), Some(taps) => taps[index].clone(), }; - backend.set_backend(queue_index, &tap.file).chain_err(|| { - format!( - "Failed to set tap device for vhost net, index: {}", - queue_index, - ) - })?; + backend + .set_backend(queue_index, &tap.file) + .with_context(|| { + format!( + "Failed to set tap device for vhost net, index: {}", + queue_index, + ) + })?; } let handler = VhostIoHandler { @@ -381,7 +385,7 @@ impl VirtioDevice for Net { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index f7c042225..e8d6afac8 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -15,7 +15,6 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; use log::warn; use machine_manager::{config::VsockConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -26,7 +25,9 @@ use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::super::errors::{ErrorKind, Result, ResultExt}; +use crate::VirtioError; +use anyhow::{anyhow, bail, Context, Result}; + use super::super::super::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_VSOCK, }; @@ -53,7 +54,9 @@ impl VhostVsockBackend for VhostBackend { fn set_guest_cid(&self, cid: u64) -> Result<()> { let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_GUEST_CID(), &cid) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_VSOCK_SET_GUEST_CID".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_VSOCK_SET_GUEST_CID".to_string() + ))); } Ok(()) } @@ -62,7 +65,9 @@ impl VhostVsockBackend for VhostBackend { let on: u32 = if start { 1 } else { 0 }; let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_RUNNING(), &on) }; if ret < 0 { - return Err(ErrorKind::VhostIoctl("VHOST_VSOCK_SET_RUNNING".to_string()).into()); + return Err(anyhow!(VirtioError::VhostIoctl( + "VHOST_VSOCK_SET_RUNNING".to_string() + ))); } Ok(()) } @@ -122,14 +127,14 @@ impl Vsock { let element = event_queue_locked .vring .pop_avail(&self.mem_space, self.state.driver_features) - .chain_err(|| "Failed to get avail ring element.")?; + .with_context(|| "Failed to get avail ring element.")?; self.mem_space .write_object( &VIRTIO_VSOCK_EVENT_TRANSPORT_RESET, element.in_iovec[0].addr, ) - .chain_err(|| "Failed to write buf for virtio vsock event")?; + .with_context(|| "Failed to write buf for virtio vsock event")?; event_queue_locked .vring .add_used( @@ -137,11 +142,11 @@ impl Vsock { element.index, VIRTIO_VSOCK_EVENT_TRANSPORT_RESET.as_bytes().len() as u32, ) - .chain_err(|| format!("Failed to add used ring {}", element.index))?; + .with_context(|| format!("Failed to add used ring {}", element.index))?; if let Some(interrupt_cb) = &self.interrupt_cb { interrupt_cb(&VirtioInterruptType::Vring, Some(&*event_queue_locked)) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } } @@ -154,13 +159,13 @@ impl VirtioDevice for Vsock { fn realize(&mut self) -> Result<()> { let vhost_fd: Option = self.vsock_cfg.vhost_fd; let backend = VhostBackend::new(&self.mem_space, VHOST_PATH, vhost_fd) - .chain_err(|| "Failed to create backend for vsock")?; + .with_context(|| "Failed to create backend for vsock")?; backend .set_owner() - .chain_err(|| "Failed to set owner for vsock")?; + .with_context(|| "Failed to set owner for vsock")?; self.state.device_features = backend .get_features() - .chain_err(|| "Failed to get features for vsock")?; + .with_context(|| "Failed to get features for vsock")?; self.backend = Some(backend); Ok(()) @@ -223,7 +228,10 @@ impl VirtioDevice for Vsock { let data_len = data.len(); let config_len = self.state.config_space.len(); if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset, + config_len as u64 + ))); } self.state.config_space[(offset as usize)..(offset as usize + data_len)] @@ -250,15 +258,15 @@ impl VirtioDevice for Vsock { // Preliminary setup for vhost net. let backend = match &self.backend { - None => return Err("Failed to get backend for vsock".into()), + None => return Err(anyhow!("Failed to get backend for vsock")), Some(backend_) => backend_, }; backend .set_features(self.state.driver_features) - .chain_err(|| "Failed to set features for vsock")?; + .with_context(|| "Failed to set features for vsock")?; backend .set_mem_table() - .chain_err(|| "Failed to set mem table for vsock")?; + .with_context(|| "Failed to set mem table for vsock")?; for (queue_index, queue_mutex) in vhost_queues.iter().enumerate() { let queue = queue_mutex.lock().unwrap(); @@ -267,34 +275,34 @@ impl VirtioDevice for Vsock { backend .set_vring_num(queue_index, actual_size) - .chain_err(|| { + .with_context(|| { format!("Failed to set vring num for vsock, index: {}", queue_index) })?; backend .set_vring_addr(&queue_config, queue_index, 0) - .chain_err(|| { + .with_context(|| { format!("Failed to set vring addr for vsock, index: {}", queue_index) })?; backend .set_vring_base(queue_index, self.state.last_avail_idx[queue_index]) - .chain_err(|| { + .with_context(|| { format!("Failed to set vring base for vsock, index: {}", queue_index) })?; backend .set_vring_kick(queue_index, &queue_evts[queue_index]) - .chain_err(|| { + .with_context(|| { format!("Failed to set vring kick for vsock, index: {}", queue_index) })?; drop(queue); let host_notify = VhostNotify { notify_evt: EventFd::new(libc::EFD_NONBLOCK) - .chain_err(|| ErrorKind::EventFdCreate)?, + .with_context(|| anyhow!(VirtioError::EventFdCreate))?, queue: queue_mutex.clone(), }; backend .set_vring_call(queue_index, &host_notify.notify_evt) - .chain_err(|| { + .with_context(|| { format!("Failed to set vring call for vsock, index: {}", queue_index) })?; host_notifies.push(host_notify); @@ -320,7 +328,7 @@ impl VirtioDevice for Vsock { fn deactivate(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) } @@ -338,28 +346,26 @@ impl VirtioDevice for Vsock { } impl StateTransfer for Vsock { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let mut state = self.state; - migration::errors::ResultExt::chain_err( - self.backend.as_ref().unwrap().set_running(false), - || "Failed to set vsock backend stopping", - )?; + migration::Result::with_context(self.backend.as_ref().unwrap().set_running(false), || { + "Failed to set vsock backend stopping" + })?; state.last_avail_idx[0] = self.backend.as_ref().unwrap().get_vring_base(0).unwrap(); state.last_avail_idx[1] = self.backend.as_ref().unwrap().get_vring_base(1).unwrap(); - migration::errors::ResultExt::chain_err( - self.backend.as_ref().unwrap().set_running(true), - || "Failed to set vsock backend running", - )?; - migration::errors::ResultExt::chain_err(self.transport_reset(), || { + migration::Result::with_context(self.backend.as_ref().unwrap().set_running(true), || { + "Failed to set vsock backend running" + })?; + migration::Result::with_context(self.transport_reset(), || { "Failed to send vsock transport reset event" })?; Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *VsockState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("VSOCK"))?; + .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("VSOCK")))?; Ok(()) } @@ -375,8 +381,8 @@ impl StateTransfer for Vsock { impl MigrationHook for Vsock { #[cfg(target_arch = "aarch64")] - fn resume(&mut self) -> migration::errors::Result<()> { - migration::errors::ResultExt::chain_err(self.transport_reset(), || { + fn resume(&mut self) -> migration::Result<()> { + migration::Result::with_context(self.transport_reset(), || { "Failed to resume virtio vsock device" })?; diff --git a/virtio/src/vhost/mod.rs b/virtio/src/vhost/mod.rs index 08ef95caf..db1a66e9a 100644 --- a/virtio/src/vhost/mod.rs +++ b/virtio/src/vhost/mod.rs @@ -17,8 +17,8 @@ use std::sync::{Arc, Mutex}; use vmm_sys_util::eventfd::EventFd; -use super::errors::Result; use super::{Queue, QueueConfig}; +use anyhow::Result; /// Vhost vring call notify structure. pub struct VhostNotify { diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index aee91ada4..80a69d2c9 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -10,7 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use crate::VirtioError; +use anyhow::{anyhow, bail, Context, Result}; use std::cmp; use std::io::Write; use std::sync::{Arc, Mutex}; @@ -26,7 +27,6 @@ use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; use crate::block::VirtioBlkConfig; -use crate::errors::{ErrorKind, Result, ResultExt}; use crate::vhost::VhostOps; use crate::VhostUser::client::{VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ}; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; @@ -78,9 +78,9 @@ impl Block { .lock() .unwrap() .delete_event() - .chain_err(|| "Failed to delete vhost-user blk event")?; + .with_context(|| "Failed to delete vhost-user blk event")?; } - None => return Err("Failed to get client when stoping event".into()), + None => return Err(anyhow!("Failed to get client when stoping event")), }; Ok(()) @@ -103,9 +103,9 @@ impl Block { .socket_path .as_ref() .map(|path| path.to_string()) - .chain_err(|| "vhost-user: socket path is not found")?; + .with_context(|| "vhost-user: socket path is not found")?; let client = VhostUserClient::new(&self.mem_space, &socket_path, self.queue_num() as u64) - .chain_err(|| { + .with_context(|| { "Failed to create the client which communicates with the server for vhost-user blk" })?; let client = Arc::new(Mutex::new(client)); @@ -113,7 +113,7 @@ impl Block { EventNotifierHelper::internal_notifiers(client.clone()), None, ) - .chain_err(|| "Failed to update event for client sock")?; + .with_context(|| "Failed to update event for client sock")?; self.client = Some(client); Ok(()) } @@ -128,14 +128,14 @@ impl Block { .lock() .unwrap() .get_protocol_features() - .chain_err(|| "Failed to get protocol features for vhost-user blk")?; + .with_context(|| "Failed to get protocol features for vhost-user blk")?; if virtio_has_feature(protocol_feature, VHOST_USER_PROTOCOL_F_CONFIG as u32) { let config = client .lock() .unwrap() .get_virtio_blk_config() - .chain_err(|| "Failed to get config for vhost-user blk")?; + .with_context(|| "Failed to get config for vhost-user blk")?; self.state.config_space = config; } else { bail!( @@ -149,7 +149,7 @@ impl Block { .lock() .unwrap() .get_max_queue_num() - .chain_err(|| "Failed to get queue num for vhost-user blk")?; + .with_context(|| "Failed to get queue num for vhost-user blk")?; if self.queue_num() > max_queue_num as usize { bail!( "Exceed the max queue num that spdk supported ({} queues)", @@ -171,7 +171,7 @@ impl Block { .lock() .unwrap() .set_virtio_blk_config(self.state.config_space) - .chain_err(|| "Failed to set config for vhost-user blk")?; + .with_context(|| "Failed to set config for vhost-user blk")?; Ok(()) } @@ -183,7 +183,7 @@ impl Block { .lock() .unwrap() .get_features() - .chain_err(|| "Failed to get features for vhost-user blk")?; + .with_context(|| "Failed to get features for vhost-user blk")?; feature |= 1_u64 << VIRTIO_F_VERSION_1; feature |= 1_u64 << VIRTIO_BLK_F_SIZE_MAX; @@ -208,7 +208,7 @@ impl Block { .lock() .unwrap() .set_features(feature) - .chain_err(|| "Failed to set features for vhost-user blk")?; + .with_context(|| "Failed to set features for vhost-user blk")?; Ok(()) } } @@ -267,7 +267,10 @@ impl VirtioDevice for Block { let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len(); if offset >= config_len { - return Err(ErrorKind::DevConfigOverflow(offset as u64, config_len as u64).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset as u64, + config_len as u64 + ))); } if let Some(end) = offset.checked_add(data.len()) { data.write_all(&config_slice[offset..cmp::min(end, config_len)])?; @@ -285,7 +288,10 @@ impl VirtioDevice for Block { let config_len = config_slice.len(); if let Some(end) = offset.checked_add(data.len()) { if end > config_len { - return Err(ErrorKind::DevConfigOverflow(offset as u64, config_len as u64).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset as u64, + config_len as u64 + ))); } config_slice[offset..end].copy_from_slice(data); } else { @@ -305,7 +311,7 @@ impl VirtioDevice for Block { ) -> Result<()> { let mut client = match &self.client { Some(client) => client.lock().unwrap(), - None => return Err("Failed to get client for vhost-user blk".into()), + None => return Err(anyhow!("Failed to get client for vhost-user blk")), }; client.features = self.state.driver_features; client.set_queues(queues); @@ -334,7 +340,7 @@ impl VirtioDevice for Block { match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), - None => return Err("Failed to get client for vhost-user blk".into()), + None => return Err(anyhow!("Failed to get client for vhost-user blk")), }; Ok(()) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index c1f41f9f3..12f9cabc9 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -18,7 +18,6 @@ use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; -use error_chain::bail; use log::{error, info, warn}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ @@ -26,10 +25,7 @@ use util::loop_context::{ }; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use super::super::super::{ - errors::{ErrorKind, Result, ResultExt}, - Queue, QueueConfig, VIRTIO_NET_F_CTRL_VQ, -}; +use super::super::super::{Queue, QueueConfig, VIRTIO_NET_F_CTRL_VQ}; use super::super::VhostOps; use super::message::{ RegionMemInfo, VhostUserHdrFlag, VhostUserMemContext, VhostUserMemHdr, VhostUserMsgHdr, @@ -38,6 +34,8 @@ use super::message::{ use super::sock::VhostUserSock; use crate::block::VirtioBlkConfig; use crate::VhostUser::message::VhostUserConfig; +use crate::VirtioError; +use anyhow::{anyhow, bail, Context, Result}; /// Vhost supports multiple queue pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; @@ -68,7 +66,7 @@ impl ClientInternal { let (recv_len, _fds_num) = self .sock .recv_msg(Some(&mut hdr), Some(&mut body), payload_opt, &mut []) - .chain_err(|| "Failed to recv ack msg")?; + .with_context(|| "Failed to recv ack msg")?; if request != hdr.request || recv_len != (size_of::() + size_of::()) @@ -114,11 +112,11 @@ fn vhost_user_reconnect(client: &Arc>) { if let Err(e) = EventLoop::update_event(EventNotifierHelper::internal_notifiers(cloned_client), None) { - error!("Failed to update event for client sock, {}", e); + error!("Failed to update event for client sock, {:?}", e); } if let Err(e) = client.lock().unwrap().activate_vhost_user() { - error!("Failed to reactivate vhost-user net, {}", e); + error!("Failed to reactivate vhost-user net, {:?}", e); } else { info!("Reconnecting vhost-user net succeed."); } @@ -135,7 +133,7 @@ impl EventNotifierHelper for VhostUserClient { if event & EventSet::HANG_UP == EventSet::HANG_UP { let mut locked_client = cloned_client.lock().unwrap(); if let Err(e) = locked_client.delete_event() { - error!("Failed to delete vhost-user client event, {}", e); + error!("Failed to delete vhost-user client event, {:?}", e); } if !locked_client.reconnecting { locked_client.reconnecting = true; @@ -214,7 +212,7 @@ impl VhostUserMemInfo { None } - fn add_mem_range(&self, fr: &FlatRange) -> address_space::errors::Result<()> { + fn add_mem_range(&self, fr: &FlatRange) -> address_space::Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } @@ -245,7 +243,7 @@ impl VhostUserMemInfo { Ok(()) } - fn delete_mem_range(&self, fr: &FlatRange) -> address_space::errors::Result<()> { + fn delete_mem_range(&self, fr: &FlatRange) -> address_space::Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } @@ -290,7 +288,7 @@ impl Listener for VhostUserMemInfo { range: Option<&FlatRange>, _evtfd: Option<&RegionIoEventFd>, req_type: ListenerReqType, - ) -> std::result::Result<(), address_space::errors::Error> { + ) -> std::result::Result<(), anyhow::Error> { match req_type { ListenerReqType::AddRegion => { self.add_mem_range(range.unwrap())?; @@ -320,7 +318,7 @@ pub struct VhostUserClient { impl VhostUserClient { pub fn new(mem_space: &Arc, path: &str, max_queue_num: u64) -> Result { let mut sock = VhostUserSock::new(path); - sock.domain.connect().chain_err(|| { + sock.domain.connect().with_context(|| { format!( "Failed to connect the socket {} for vhost user client", path @@ -330,7 +328,7 @@ impl VhostUserClient { let mem_info = VhostUserMemInfo::new(); mem_space .register_listener(Arc::new(Mutex::new(mem_info.clone()))) - .chain_err(|| "Failed to register memory for vhost user client")?; + .with_context(|| "Failed to register memory for vhost user client")?; let client = Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))); let delete_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); @@ -370,13 +368,13 @@ impl VhostUserClient { /// Activate device by vhost-user protocol. pub fn activate_vhost_user(&mut self) -> Result<()> { self.set_owner() - .chain_err(|| "Failed to set owner for vhost-user net")?; + .with_context(|| "Failed to set owner for vhost-user net")?; self.set_features(self.features) - .chain_err(|| "Failed to set features for vhost-user net")?; + .with_context(|| "Failed to set features for vhost-user net")?; self.set_mem_table() - .chain_err(|| "Failed to set mem table for vhost-user net")?; + .with_context(|| "Failed to set mem table for vhost-user net")?; let mut queue_num = self.queues.len(); if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { @@ -387,12 +385,13 @@ impl VhostUserClient { for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { let queue = queue_mutex.lock().unwrap(); let actual_size = queue.vring.actual_size(); - self.set_vring_num(queue_index, actual_size).chain_err(|| { - format!( - "Failed to set vring num for vhost-user net, index: {}, size: {}", - queue_index, actual_size, - ) - })?; + self.set_vring_num(queue_index, actual_size) + .with_context(|| { + format!( + "Failed to set vring num for vhost-user net, index: {}, size: {}", + queue_index, actual_size, + ) + })?; } for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { @@ -400,27 +399,27 @@ impl VhostUserClient { let queue_config = queue.vring.get_queue_config(); self.set_vring_addr(&queue_config, queue_index, 0) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring addr for vhost-user net, index: {}", queue_index, ) })?; - self.set_vring_base(queue_index, 0).chain_err(|| { + self.set_vring_base(queue_index, 0).with_context(|| { format!( "Failed to set vring base for vhost-user net, index: {}", queue_index, ) })?; self.set_vring_kick(queue_index, &self.queue_evts[queue_index]) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring kick for vhost-user net, index: {}", queue_index, ) })?; self.set_vring_call(queue_index, &self.call_events[queue_index]) - .chain_err(|| { + .with_context(|| { format!( "Failed to set vring call for vhost-user net, index: {}", queue_index, @@ -429,7 +428,7 @@ impl VhostUserClient { } for (queue_index, _) in self.queues.iter().enumerate().take(queue_num) { - self.set_vring_enable(queue_index, true).chain_err(|| { + self.set_vring_enable(queue_index, true).with_context(|| { format!( "Failed to set vring enable for vhost-user net, index: {}", queue_index, @@ -444,7 +443,7 @@ impl VhostUserClient { pub fn delete_event(&self) -> Result<()> { self.delete_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) } @@ -477,10 +476,10 @@ impl VhostUserClient { client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) - .chain_err(|| "Failed to send msg for getting features")?; + .with_context(|| "Failed to send msg for getting features")?; let features = client .wait_ack_msg::(request) - .chain_err(|| "Failed to wait ack msg for getting protocols features")?; + .with_context(|| "Failed to wait ack msg for getting protocols features")?; Ok(features) } @@ -497,7 +496,7 @@ impl VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&features), payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting protocols features")?; + .with_context(|| "Failed to send msg for setting protocols features")?; Ok(()) } @@ -523,10 +522,10 @@ impl VhostUserClient { client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) - .chain_err(|| "Failed to send msg for getting config")?; + .with_context(|| "Failed to send msg for getting config")?; let res = client .wait_ack_msg::>(request) - .chain_err(|| "Failed to wait ack msg for getting virtio blk config")?; + .with_context(|| "Failed to wait ack msg for getting virtio blk config")?; Ok(res.config) } @@ -541,7 +540,7 @@ impl VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&config), payload_opt, &[]) - .chain_err(|| "Failed to send msg for getting virtio blk config")?; + .with_context(|| "Failed to send msg for getting virtio blk config")?; Ok(()) } @@ -555,10 +554,10 @@ impl VhostUserClient { client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) - .chain_err(|| "Failed to send msg for getting queue num")?; + .with_context(|| "Failed to send msg for getting queue num")?; let queue_num = client .wait_ack_msg::(request) - .chain_err(|| "Failed to wait ack msg for getting queue num")?; + .with_context(|| "Failed to wait ack msg for getting queue num")?; Ok(queue_num) } } @@ -572,7 +571,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting owner")?; + .with_context(|| "Failed to send msg for setting owner")?; Ok(()) } @@ -586,10 +585,10 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) - .chain_err(|| "Failed to send msg for getting features")?; + .with_context(|| "Failed to send msg for getting features")?; let features = client .wait_ack_msg::(request) - .chain_err(|| "Failed to wait ack msg for getting features")?; + .with_context(|| "Failed to wait ack msg for getting features")?; Ok(features) } @@ -605,7 +604,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&features), payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting features")?; + .with_context(|| "Failed to send msg for setting features")?; Ok(()) } @@ -636,7 +635,7 @@ impl VhostOps for VhostUserClient { Some(memcontext.regions.as_slice()), &fds, ) - .chain_err(|| "Failed to send msg for setting mem table")?; + .with_context(|| "Failed to send msg for setting mem table")?; Ok(()) } @@ -661,7 +660,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting vring num")?; + .with_context(|| "Failed to send msg for setting vring num")?; Ok(()) } @@ -678,13 +677,13 @@ impl VhostOps for VhostUserClient { .mem_info .addr_to_host(queue.desc_table) .ok_or_else(|| { - ErrorKind::Msg(format!( + anyhow!(format!( "Failed to transform desc-table address {}", queue.desc_table.0 )) })?; let used_user_addr = self.mem_info.addr_to_host(queue.used_ring).ok_or_else(|| { - ErrorKind::Msg(format!( + anyhow!(format!( "Failed to transform used ring address {}", queue.used_ring.0 )) @@ -693,7 +692,7 @@ impl VhostOps for VhostUserClient { .mem_info .addr_to_host(queue.avail_ring) .ok_or_else(|| { - ErrorKind::Msg(format!( + anyhow!(format!( "Failed to transform avail ring address {}", queue.avail_ring.0 )) @@ -709,7 +708,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&_vring_addr), payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting vring addr")?; + .with_context(|| "Failed to send msg for setting vring addr")?; Ok(()) } @@ -734,7 +733,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting vring base")?; + .with_context(|| "Failed to send msg for setting vring base")?; Ok(()) } @@ -758,7 +757,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&queue_idx), payload_opt, &[fd.as_raw_fd()]) - .chain_err(|| "Failed to send msg for setting vring call")?; + .with_context(|| "Failed to send msg for setting vring call")?; Ok(()) } @@ -782,7 +781,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&queue_idx), payload_opt, &[fd.as_raw_fd()]) - .chain_err(|| "Failed to send msg for setting vring kick")?; + .with_context(|| "Failed to send msg for setting vring kick")?; Ok(()) } @@ -807,7 +806,7 @@ impl VhostOps for VhostUserClient { client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) - .chain_err(|| "Failed to send msg for setting vring enable")?; + .with_context(|| "Failed to send msg for setting vring enable")?; Ok(()) } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index cb5913dc2..8f0cc08d5 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -17,12 +17,12 @@ const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; // The size of queue for virtio fs const VIRTIO_FS_QUEUE_SIZE: u16 = 1024; +use crate::VirtioError; use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; -use error_chain::ChainedError; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -35,11 +35,11 @@ use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use util::num_ops::{read_u32, write_u32}; -use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; use super::super::{VhostNotify, VhostOps}; use super::VhostUserClient; use crate::{VirtioInterrupt, VirtioInterruptType}; +use anyhow::{anyhow, Context, Result}; #[derive(Copy, Clone)] #[repr(C, packed)] @@ -81,8 +81,8 @@ impl EventNotifierHelper for VhostUserFsHandler { Some(&host_notify.queue.lock().unwrap()), ) { error!( - "Failed to trigger interrupt for vhost user device, error is {}", - e.display_chain() + "Failed to trigger interrupt for vhost user device, error is {:?}", + e ); } } @@ -138,7 +138,7 @@ impl VirtioDevice for Fs { let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; let client = VhostUserClient::new(&self.mem_space, &self.fs_cfg.sock, queues_num as u64) - .chain_err(|| { + .with_context(|| { "Failed to create the client which communicates with the server for virtio fs" })?; let client = Arc::new(Mutex::new(client)); @@ -147,12 +147,12 @@ impl VirtioDevice for Fs { EventNotifierHelper::internal_notifiers(client.clone()), None, ) - .chain_err(|| "Failed to update event for client sock")?; + .with_context(|| "Failed to update event for client sock")?; self.avail_features = client .lock() .unwrap() .get_features() - .chain_err(|| "Failed to get features for virtio fs")?; + .with_context(|| "Failed to get features for virtio fs")?; self.client = Some(client); Ok(()) @@ -196,7 +196,7 @@ impl VirtioDevice for Fs { let config_slice = self.config.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { - return Err(ErrorKind::DevConfigOverflow(offset, config_size).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); } if let Some(end) = offset.checked_add(data.len() as u64) { data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; @@ -210,7 +210,10 @@ impl VirtioDevice for Fs { let config_slice = self.config.as_mut_bytes(); let config_len = config_slice.len(); if offset as usize + data_len > config_len { - return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset, + config_len as u64 + ))); } config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); @@ -227,7 +230,7 @@ impl VirtioDevice for Fs { ) -> Result<()> { let mut client = match &self.client { Some(client) => client.lock().unwrap(), - None => return Err("Failed to get client for virtio fs".into()), + None => return Err(anyhow!("Failed to get client for virtio fs")), }; client.features = self.acked_features; client.set_queues(queues); @@ -244,7 +247,7 @@ impl VirtioDevice for Fs { } match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), - None => return Err("Failed to get client for vhost-user net".into()), + None => return Err(anyhow!("Failed to get client for vhost-user net")), } Ok(()) } diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 1b4d66b2f..96f81e9fe 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -10,17 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use error_chain::bail; +use anyhow::bail; use std::mem::size_of; - -use super::super::super::errors::Result; - /// The version of the protocol StratoVirt support. pub const VHOST_USER_VERSION: u32 = 0x1; pub const VHOST_USER_MSG_MAX_SIZE: usize = 0x1000; pub const MAX_ATTACHED_FD_ENTRIES: usize = 32; pub const VHOST_USER_F_PROTOCOL_FEATURES: u32 = 30; pub const VHOST_USER_MAX_CONFIG_SIZE: u32 = 256; +use anyhow::Result; /// Type of requests sending from vhost user device to the userspace process. #[repr(u32)] diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 1e8a7e4bd..e164017e9 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::virtio_has_feature; use std::cmp; use std::io::Write; use std::sync::{Arc, Mutex}; @@ -22,8 +23,6 @@ use util::loop_context::EventNotifierHelper; use util::num_ops::{read_u32, write_u32}; use vmm_sys_util::eventfd::EventFd; -use super::super::super::errors::{ErrorKind, Result, ResultExt}; -use super::super::super::virtio_has_feature; use super::super::super::{ net::{build_device_config_space, VirtioNetConfig, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, @@ -34,6 +33,8 @@ use super::super::super::{ }; use super::super::VhostOps; use super::VhostUserClient; +use crate::error::VirtioError; +use anyhow::{anyhow, Context, Result}; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; @@ -81,14 +82,14 @@ impl Net { .lock() .unwrap() .delete_event() - .chain_err(|| "Failed to delete vhost-user net event")?; + .with_context(|| "Failed to delete vhost-user net event")?; } - None => return Err("Failed to get client when stoping event".into()), + None => return Err(anyhow!("Failed to get client when stoping event")), }; if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq { self.de_ctrl_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } Ok(()) @@ -113,9 +114,9 @@ impl VirtioDevice for Net { .socket_path .as_ref() .map(|path| path.to_string()) - .chain_err(|| "vhost-user: socket path is not found")?; + .with_context(|| "vhost-user: socket path is not found")?; let client = VhostUserClient::new(&self.mem_space, &socket_path, self.queue_num() as u64) - .chain_err(|| { + .with_context(|| { "Failed to create the client which communicates with the server for vhost-user net" })?; let client = Arc::new(Mutex::new(client)); @@ -124,13 +125,13 @@ impl VirtioDevice for Net { EventNotifierHelper::internal_notifiers(client.clone()), None, ) - .chain_err(|| "Failed to update event for client sock")?; + .with_context(|| "Failed to update event for client sock")?; self.device_features = client .lock() .unwrap() .get_features() - .chain_err(|| "Failed to get features for vhost-user net")?; + .with_context(|| "Failed to get features for vhost-user net")?; let features = 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_GUEST_CSUM @@ -210,7 +211,7 @@ impl VirtioDevice for Net { let config_slice = self.device_config.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { - return Err(ErrorKind::DevConfigOverflow(offset, config_size).into()); + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); } if let Some(end) = offset.checked_add(data.len() as u64) { data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; @@ -266,7 +267,7 @@ impl VirtioDevice for Net { let mut client = match &self.client { Some(client) => client.lock().unwrap(), - None => return Err("Failed to get client for vhost-user net".into()), + None => return Err(anyhow!("Failed to get client for vhost-user net")), }; let features = self.driver_features & !(1 << VIRTIO_NET_F_MAC); @@ -287,7 +288,7 @@ impl VirtioDevice for Net { match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), - None => return Err("Failed to get client for vhost-user net".into()), + None => return Err(anyhow!("Failed to get client for vhost-user net")), }; Ok(()) diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index 208bee5fc..5e1d25442 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -13,12 +13,12 @@ use std::mem::size_of; use std::os::unix::io::RawFd; -use error_chain::bail; use libc::{c_void, iovec}; use util::unix::UnixSock; -use super::super::super::errors::Result; use super::message::{MAX_ATTACHED_FD_ENTRIES, VHOST_USER_MSG_MAX_SIZE}; +use anyhow::bail; +use anyhow::Result; #[derive(Clone)] pub struct VhostUserSock { diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index ef75edf31..c1b1992d0 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -13,9 +13,9 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; +use crate::error::VirtioError; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::bail; use log::{error, warn}; #[cfg(target_arch = "x86_64")] use machine_manager::config::{BootSource, Param}; @@ -31,7 +31,7 @@ use super::{ CONFIG_STATUS_FEATURES_OK, NOTIFY_REG_OFFSET, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, }; -use crate::errors::{ErrorKind, Result, ResultExt}; +use anyhow::{anyhow, bail, Context, Result}; /// Registers of virtio-mmio device refer to Virtio Spec. /// Magic value - Read Only. @@ -175,14 +175,13 @@ impl VirtioMmioCommonConfig { self.queues_config .get_mut(queue_select as usize) .ok_or_else(|| { - format!( + anyhow!( "Mmio-reg queue_select {} overflows for mutable queue config", queue_select, ) - .into() }) } else { - Err(ErrorKind::DevStatErr(self.device_status).into()) + Err(anyhow!(VirtioError::DevStatErr(self.device_status))) } } @@ -192,11 +191,10 @@ impl VirtioMmioCommonConfig { self.queues_config .get(queue_select as usize) .ok_or_else(|| { - format!( + anyhow!( "Mmio-reg queue_select overflows {} for immutable queue config", queue_select, ) - .into() }) } @@ -238,7 +236,7 @@ impl VirtioMmioCommonConfig { STATUS_REG => self.device_status, CONFIG_GENERATION_REG => self.config_generation, _ => { - return Err(ErrorKind::MmioRegErr(offset).into()); + return Err(anyhow!(VirtioError::MmioRegErr(offset))); } }; @@ -280,7 +278,7 @@ impl VirtioMmioCommonConfig { self.queue_type = QUEUE_TYPE_PACKED_VRING; } } else { - return Err(ErrorKind::DevStatErr(self.device_status).into()); + return Err(anyhow!(VirtioError::DevStatErr(self.device_status))); } } DRIVER_FEATURES_SEL_REG => self.acked_features_select = value, @@ -316,7 +314,7 @@ impl VirtioMmioCommonConfig { config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); })?, _ => { - return Err(ErrorKind::MmioRegErr(offset).into()); + return Err(anyhow!(VirtioError::MmioRegErr(offset))); } }; Ok(()) @@ -374,7 +372,7 @@ impl VirtioMmioDevice { .lock() .unwrap() .realize() - .chain_err(|| "Failed to realize virtio.")?; + .with_context(|| "Failed to realize virtio.")?; if region_base >= sysbus.mmio_region.1 { bail!("Mmio region space exhausted."); @@ -424,7 +422,7 @@ impl VirtioMmioDevice { let evt_fd_clone = match fd.try_clone() { Ok(fd) => fd, Err(e) => { - error!("Failed to clone IoEventFd, {}", e); + error!("Failed to clone IoEventFd, {:?}", e); continue; } }; @@ -442,7 +440,7 @@ impl VirtioMmioDevice { interrupt_status.fetch_or(status as u32, Ordering::SeqCst); interrupt_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) }, @@ -472,10 +470,10 @@ impl SysBusDevOps for VirtioMmioDevice { Ok(v) => v, Err(ref e) => { error!( - "Failed to read mmio register {}, type: {}, {}", + "Failed to read mmio register {}, type: {}, {:?}", offset, self.device.lock().unwrap().device_type(), - error_chain::ChainedError::display_chain(e), + e, ); return false; } @@ -490,10 +488,10 @@ impl SysBusDevOps for VirtioMmioDevice { .read_config(offset as u64 - 0x100, data) { error!( - "Failed to read virtio-dev config space {} type: {} {}", + "Failed to read virtio-dev config space {} type: {} {:?}", offset as u64 - 0x100, self.device.lock().unwrap().device_type(), - error_chain::ChainedError::display_chain(e), + e, ); return false; } @@ -521,10 +519,10 @@ impl SysBusDevOps for VirtioMmioDevice { value, ) { error!( - "Failed to write mmio register {}, type: {}, {}", + "Failed to write mmio register {}, type: {}, {:?}", offset, self.device.lock().unwrap().device_type(), - error_chain::ChainedError::display_chain(e), + e, ); return false; } @@ -540,9 +538,9 @@ impl SysBusDevOps for VirtioMmioDevice { let ret = self.activate().map(|_| self.state.activated = true); if let Err(ref e) = ret { error!( - "Failed to activate dev, type: {}, {}", + "Failed to activate dev, type: {}, {:?}", self.device.lock().unwrap().device_type(), - error_chain::ChainedError::display_chain(e), + e, ); } } @@ -560,10 +558,10 @@ impl SysBusDevOps for VirtioMmioDevice { .write_config(offset as u64 - 0x100, data) { error!( - "Failed to write virtio-dev config space {}, type: {}, {}", + "Failed to write virtio-dev config space {}, type: {}, {:?}", offset as u64 - 0x100, self.device.lock().unwrap().device_type(), - error_chain::ChainedError::display_chain(e), + e, ); return false; } @@ -593,7 +591,7 @@ impl SysBusDevOps for VirtioMmioDevice { let addr = u64::from(NOTIFY_REG_OFFSET); let eventfd_clone = match eventfd.try_clone() { Err(e) => { - error!("Failed to clone ioeventfd, error is {}", e); + error!("Failed to clone ioeventfd, error is {:?}", e); continue; } Ok(fd) => fd, @@ -628,7 +626,7 @@ impl acpi::AmlBuilder for VirtioMmioDevice { } impl StateTransfer for VirtioMmioDevice { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let mut state = self.state; for (index, queue) in self.queues.iter().enumerate() { @@ -640,9 +638,12 @@ impl StateTransfer for VirtioMmioDevice { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { - self.state = *VirtioMmioState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("MMIO_DEVICE"))?; + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + self.state = *VirtioMmioState::from_bytes(state).ok_or_else(|| { + anyhow!(migration::error::MigrationError::FromBytesError( + "MMIO_DEVICE" + )) + })?; let cloned_mem_space = self.mem_space.clone(); let mut queue_states = self.state.config_space.queues_config[0..self.state.config_space.queue_num].to_vec(); @@ -678,14 +679,14 @@ impl StateTransfer for VirtioMmioDevice { } impl MigrationHook for VirtioMmioDevice { - fn resume(&mut self) -> migration::errors::Result<()> { + fn resume(&mut self) -> migration::Result<()> { if self.state.activated { let mut queue_evts = Vec::::new(); for fd in self.host_notify_info.events.iter() { let evt_fd_clone = match fd.try_clone() { Ok(fd) => fd, Err(e) => { - error!("Failed to clone IoEventFd, {}", e); + error!("Failed to clone IoEventFd, {:?}", e); continue; } }; @@ -703,7 +704,7 @@ impl MigrationHook for VirtioMmioDevice { interrupt_status.fetch_or(status as u32, Ordering::SeqCst); interrupt_evt .write(1) - .chain_err(|| ErrorKind::EventFdWrite)?; + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; Ok(()) }, diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index c6ac46011..d5d797c50 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -16,8 +16,8 @@ use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use anyhow::{anyhow, bail, Context}; use byteorder::{ByteOrder, LittleEndian}; -use error_chain::{bail, ChainedError}; use hypervisor::kvm::{MsiVector, KVM_FDS}; use log::{error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -27,11 +27,11 @@ use pci::config::{ ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::errors::{ErrorKind, Result as PciResult, ResultExt}; use pci::msix::{update_dev_id, Message, MsixState, MsixUpdate}; +use pci::Result as PciResult; use pci::{ config::PciConfig, init_msix, init_multifunction, le_write_u16, ranges_overlap, PciBus, - PciDevOps, + PciDevOps, PciError, }; use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; use vmm_sys_util::eventfd::EventFd; @@ -182,16 +182,16 @@ impl VirtioPciCommonConfig { ) { self.queues_config .get_mut(self.queue_select as usize) - .ok_or_else(|| "pci-reg queue_select overflows".into()) + .ok_or_else(|| anyhow!("pci-reg queue_select overflows")) } else { - Err(ErrorKind::DeviceStatus(self.device_status).into()) + Err(anyhow!(PciError::DeviceStatus(self.device_status))) } } fn get_queue_config(&self) -> PciResult<&QueueConfig> { self.queues_config .get(self.queue_select as usize) - .ok_or_else(|| "pci-reg queue_select overflows".into()) + .ok_or_else(|| anyhow!("pci-reg queue_select overflows")) } /// Read data from the common config of virtio device. @@ -294,7 +294,9 @@ impl VirtioPciCommonConfig { } COMMON_GF_REG => { if self.acked_features_select >= MAX_FEATURES_SELECT_NUM { - return Err(ErrorKind::FeaturesSelect(self.acked_features_select).into()); + return Err(anyhow!(PciError::FeaturesSelect( + self.acked_features_select + ))); } device .lock() @@ -367,7 +369,7 @@ impl VirtioPciCommonConfig { config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); })?, _ => { - return Err(ErrorKind::PciRegister(offset).into()); + return Err(anyhow!(PciError::PciRegister(offset))); } }; @@ -654,8 +656,8 @@ impl VirtioPciDevice { Ok(v) => v, Err(e) => { error!( - "Failed to read common config of virtio-pci device, error is {}", - e.display_chain(), + "Failed to read common config of virtio-pci device, error is {:?}", + e, ); return false; } @@ -710,8 +712,8 @@ impl VirtioPciDevice { .write_common_config(&cloned_pci_device.device.clone(), offset, value) { error!( - "Failed to write common config of virtio-pci device, error is {}", - e.display_chain(), + "Failed to write common config of virtio-pci device, error is {:?}", + e, ); return false; } @@ -777,10 +779,7 @@ impl VirtioPciDevice { .unwrap() .set_guest_notifiers(&call_evts.events) { - error!( - "Failed to set guest notifiers, error is {}", - e.display_chain() - ); + error!("Failed to set guest notifiers, error is {:?}", e); } } if let Err(e) = cloned_pci_device.device.lock().unwrap().activate( @@ -789,7 +788,7 @@ impl VirtioPciDevice { &locked_queues, queue_evts, ) { - error!("Failed to activate device, error is {}", e.display_chain()); + error!("Failed to activate device, error is {:?}", e); } } else { error!("Failed to activate device: No interrupt callback"); @@ -838,10 +837,7 @@ impl VirtioPciDevice { let cloned_msix = cloned_pci_device.config.msix.as_ref().unwrap().clone(); cloned_msix.lock().unwrap().reset(); if let Err(e) = cloned_pci_device.device.lock().unwrap().deactivate() { - error!( - "Failed to deactivate virtio device, error is {}", - e.display_chain() - ); + error!("Failed to deactivate virtio device, error is {:?}", e); } } update_dev_id( @@ -867,7 +863,7 @@ impl VirtioPciDevice { Region::init_io_region(u64::from(VIRTIO_PCI_CAP_COMMON_LENGTH), common_region_ops); modern_mem_region .add_subregion(common_region, u64::from(VIRTIO_PCI_CAP_COMMON_OFFSET)) - .chain_err(|| "Failed to register pci-common-cap region.")?; + .with_context(|| "Failed to register pci-common-cap region.")?; // 2. PCI ISR cap sub-region. let cloned_common_cfg = self.common_config.clone(); @@ -890,16 +886,13 @@ impl VirtioPciDevice { Region::init_io_region(u64::from(VIRTIO_PCI_CAP_ISR_LENGTH), isr_region_ops); modern_mem_region .add_subregion(isr_region, u64::from(VIRTIO_PCI_CAP_ISR_OFFSET)) - .chain_err(|| "Failed to register pci-isr-cap region.")?; + .with_context(|| "Failed to register pci-isr-cap region.")?; // 3. PCI dev cap sub-region. let cloned_virtio_dev = self.device.clone(); let device_read = move |data: &mut [u8], _addr: GuestAddress, offset: u64| -> bool { if let Err(e) = cloned_virtio_dev.lock().unwrap().read_config(offset, data) { - error!( - "Failed to read virtio-dev config space, error is {}", - e.display_chain() - ); + error!("Failed to read virtio-dev config space, error is {:?}", e); return false; } true @@ -908,10 +901,7 @@ impl VirtioPciDevice { let cloned_virtio_dev = self.device.clone(); let device_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { if let Err(e) = cloned_virtio_dev.lock().unwrap().write_config(offset, data) { - error!( - "Failed to write virtio-dev config space, error is {}", - e.display_chain() - ); + error!("Failed to write virtio-dev config space, error is {:?}", e); return false; } true @@ -924,7 +914,7 @@ impl VirtioPciDevice { Region::init_io_region(u64::from(VIRTIO_PCI_CAP_DEVICE_LENGTH), device_region_ops); modern_mem_region .add_subregion(device_region, u64::from(VIRTIO_PCI_CAP_DEVICE_OFFSET)) - .chain_err(|| "Failed to register pci-dev-cap region.")?; + .with_context(|| "Failed to register pci-dev-cap region.")?; // 4. PCI notify cap sub-region. let notify_read = move |_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }; @@ -938,7 +928,7 @@ impl VirtioPciDevice { notify_region.set_ioeventfds(&self.ioeventfds()); modern_mem_region .add_subregion(notify_region, u64::from(VIRTIO_PCI_CAP_NOTIFY_OFFSET)) - .chain_err(|| "Failed to register pci-notify-cap region.")?; + .with_context(|| "Failed to register pci-notify-cap region.")?; Ok(()) } @@ -1061,7 +1051,7 @@ impl PciDevOps for VirtioPciDevice { .lock() .unwrap() .realize() - .chain_err(|| "Failed to realize virtio device")?; + .with_context(|| "Failed to realize virtio device")?; let name = self.name.clone(); let devfn = self.devfn; @@ -1088,7 +1078,7 @@ impl PciDevOps for VirtioPciDevice { .lock() .unwrap() .unrealize() - .chain_err(|| "Failed to unrealize the virtio device")?; + .with_context(|| "Failed to unrealize the virtio device")?; let bus = self.parent_bus.upgrade().unwrap(); self.config.unregister_bars(&bus)?; @@ -1144,7 +1134,7 @@ impl PciDevOps for VirtioPciDevice { &locked_parent_bus.io_region, &locked_parent_bus.mem_region, ) { - error!("Failed to update bar, error is {}", e.display_chain()); + error!("Failed to update bar, error is {:?}", e); } } } @@ -1162,7 +1152,7 @@ impl PciDevOps for VirtioPciDevice { .lock() .unwrap() .reset() - .chain_err(|| "Fail to reset virtio device") + .with_context(|| "Fail to reset virtio device") } fn get_dev_path(&self) -> Option { @@ -1182,7 +1172,7 @@ impl PciDevOps for VirtioPciDevice { } impl StateTransfer for VirtioPciDevice { - fn get_state_vec(&self) -> migration::errors::Result> { + fn get_state_vec(&self) -> migration::Result> { let mut state = VirtioPciState::default(); // Save virtio pci config state. @@ -1226,9 +1216,12 @@ impl StateTransfer for VirtioPciDevice { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::errors::Result<()> { - let mut pci_state = *VirtioPciState::from_bytes(state) - .ok_or(migration::errors::ErrorKind::FromBytesError("PCI_DEVICE"))?; + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + let mut pci_state = *VirtioPciState::from_bytes(state).ok_or_else(|| { + anyhow!(migration::error::MigrationError::FromBytesError( + "PCI_DEVICE" + ),) + })?; // Set virtio pci config state. let config_length = self.config.config.len(); @@ -1292,7 +1285,7 @@ impl StateTransfer for VirtioPciDevice { } impl MigrationHook for VirtioPciDevice { - fn resume(&mut self) -> migration::errors::Result<()> { + fn resume(&mut self) -> migration::Result<()> { if self.device_activated.load(Ordering::Relaxed) { // Reregister ioevents for notifies. let parent_bus = self.parent_bus.upgrade().unwrap(); @@ -1302,7 +1295,7 @@ impl MigrationHook for VirtioPciDevice { &locked_parent_bus.io_region, &locked_parent_bus.mem_region, ) { - bail!("Failed to update bar, error is {}", e.display_chain()); + bail!("Failed to update bar, error is {:?}", e); } let queue_evts = self.notify_eventfds.clone().events; @@ -1313,7 +1306,7 @@ impl MigrationHook for VirtioPciDevice { &self.queues.lock().unwrap(), queue_evts, ) { - error!("Failed to resume device, error is {}", e.display_chain()); + error!("Failed to resume device, error is {}", e); } } else { error!("Failed to resume device: No interrupt callback"); @@ -1354,7 +1347,7 @@ impl MsixUpdate for VirtioPciDevice { .as_ref() .unwrap() .unregister_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to unregister irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to unregister irq, error is {:?}", e)); } else { let msg = &route.msg; if msg.data != entry.data @@ -1367,11 +1360,11 @@ impl MsixUpdate for VirtioPciDevice { .lock() .unwrap() .update_msi_route(route.gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {:?}", e)); KVM_FDS .load() .commit_irq_routing() - .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {:?}", e)); route.msg = *entry; } @@ -1381,7 +1374,7 @@ impl MsixUpdate for VirtioPciDevice { .as_ref() .unwrap() .register_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to register irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to register irq, error is {:?}", e)); } } } @@ -1428,7 +1421,7 @@ fn virtio_pci_register_irqfd( { Ok(g) => g as i32, Err(e) => { - error!("Failed to allocate gsi, error is {}", e); + error!("Failed to allocate gsi, error is {:?}", e); return false; } }; @@ -1439,12 +1432,12 @@ fn virtio_pci_register_irqfd( .lock() .unwrap() .add_msi_route(gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {:?}", e)); KVM_FDS .load() .commit_irq_routing() - .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {:?}", e)); KVM_FDS .load() @@ -1452,7 +1445,7 @@ fn virtio_pci_register_irqfd( .as_ref() .unwrap() .register_irqfd(&call_fds[queue_index], gsi as u32) - .unwrap_or_else(|e| error!("Failed to register irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to register irq, error is {:?}", e)); let gsi_route = GsiMsiRoute { irq_fd: Some(call_fds[queue_index].try_clone().unwrap()), @@ -1472,7 +1465,7 @@ fn virtio_pci_unregister_irqfd(gsi_routes: Arc>> KVM_FDS .load() .unregister_irqfd(fd, route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to unregister irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to unregister irq, error is {:?}", e)); KVM_FDS .load() @@ -1480,7 +1473,7 @@ fn virtio_pci_unregister_irqfd(gsi_routes: Arc>> .lock() .unwrap() .release_gsi(route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to release gsi, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to release gsi, error is {:?}", e)); } } locked_gsi_routes.clear(); diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index db6fbeb4c..d52343745 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -8,7 +8,8 @@ description = "Visual Network Computing" [dependencies] byteorder = "1.3.4" -error-chain = "0.12.4" +thiserror = "1.0" +anyhow = "1.0" libc = ">=0.2.71" log = "0.4.8" serde_json = "1.0.55" diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 03cfec6da..fefe020c2 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -10,8 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::errors::Result; use crate::client::VncClient; +use anyhow::Result; /// Authentication type #[derive(Clone, Copy)] diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 8dc086ffd..565a36846 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -10,9 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::VncError; use crate::{ auth::{AuthState, SubAuthState}, - errors::{ErrorKind, Result}, pixman::{get_image_height, get_image_width, PixelFormat}, round_up_div, server::VncServer, @@ -22,7 +22,7 @@ use crate::{ DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, }, }; -use error_chain::ChainedError; +use anyhow::{anyhow, Result}; use machine_manager::event_loop::EventLoop; use sscanf::scanf; use std::{ @@ -424,7 +424,7 @@ impl VncClient { len = ret; } Err(e) => { - error!("read msg error: {}", e); + error!("read msg error: {:?}", e); } } @@ -500,7 +500,7 @@ impl VncClient { Err(e) => { let msg = format!("Unsupport RFB version: {}", e); error!("{}", msg); - return Err(ErrorKind::UnsupportRFBProtocolVersion.into()); + return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } } self.version.major = ver.0 as u16; @@ -509,7 +509,7 @@ impl VncClient { let mut buf = Vec::new(); buf.append(&mut (AuthState::Invalid as u32).to_be_bytes().to_vec()); self.write_msg(&buf); - return Err(ErrorKind::UnsupportRFBProtocolVersion.into()); + return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } if [4, 5].contains(&self.version.minor) { @@ -527,9 +527,9 @@ impl VncClient { } _ => { self.auth_failed("Unsupported auth method"); - return Err( - ErrorKind::AuthFailed(String::from("Unsupported auth method")).into(), - ); + return Err(anyhow!(VncError::AuthFailed(String::from( + "Unsupported auth method" + )))); } } } else { @@ -562,7 +562,7 @@ impl VncClient { if buf[0] != self.auth as u8 { self.auth_failed("Authentication failed"); error!("handle_auth"); - return Err(ErrorKind::AuthFailed(String::from("handle_auth")).into()); + return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); } match self.auth { @@ -585,7 +585,7 @@ impl VncClient { _ => { self.auth_failed("Unhandled auth method"); error!("handle_auth"); - return Err(ErrorKind::AuthFailed(String::from("handle_auth")).into()); + return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); } } Ok(()) @@ -630,13 +630,13 @@ impl VncClient { self.width = get_image_width(self.server_image); if self.width < 0 || self.width > MAX_WINDOW_WIDTH as i32 { error!("Invalid Image Size!"); - return Err(ErrorKind::InvalidImageSize.into()); + return Err(anyhow!(VncError::InvalidImageSize)); } buf.append(&mut (self.width as u16).to_be_bytes().to_vec()); self.height = get_image_height(self.server_image); if self.height < 0 || self.height > MAX_WINDOW_HEIGHT as i32 { error!("Invalid Image Size!"); - return Err(ErrorKind::InvalidImageSize.into()); + return Err(anyhow!(VncError::InvalidImageSize)); } buf.append(&mut (self.height as u16).to_be_bytes().to_vec()); self.pixel_format_message(&mut buf); @@ -712,7 +712,9 @@ impl VncClient { if ![8, 16, 32].contains(&bit_per_pixel) { self.dis_conn = true; error!("Worng format of bits_per_pixel"); - return Err(ErrorKind::ProtocolMessageFailed(String::from("set pixel format")).into()); + return Err(anyhow!(VncError::ProtocolMessageFailed(String::from( + "set pixel format" + )))); } self.pixel_format.red.set_color_info(red_shift, red_max); @@ -946,7 +948,7 @@ impl VncClient { /// Clear the data when disconnected from client. pub fn disconnect(&mut self) { if let Err(e) = self.modify_event(NotifierOperation::Delete, 0) { - error!("Failed to delete event, error is {}", e.display_chain()); + error!("Failed to delete event, error is {}", format!("{:?}", e)); } if let Err(e) = self.stream.shutdown(Shutdown::Both) { @@ -968,7 +970,7 @@ impl EventNotifierHelper for VncClient { } else if event == EventSet::IN { let mut locked_client = client.lock().unwrap(); if let Err(e) = locked_client.from_tcpstream_to_buff() { - error!("Failed to read_msg, error is {}", e.display_chain()); + error!("Failed to read_msg, error is {}", format!("{:?}", e)); dis_conn = true; } } @@ -977,7 +979,7 @@ impl EventNotifierHelper for VncClient { let mut locked_client = client.lock().unwrap(); while locked_client.buffpool.len() >= locked_client.expect { if let Err(e) = (locked_client.handle_msg)(&mut locked_client) { - error!("Failed to read_msg, error is {}", e.display_chain()); + error!("Failed to read_msg, error is {}", format!("{:?}", e)); dis_conn = true; break; } diff --git a/vnc/src/error.rs b/vnc/src/error.rs new file mode 100644 index 000000000..70f8ec91b --- /dev/null +++ b/vnc/src/error.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum VncError { + #[error("Util")] + Util { + #[from] + source: util::error::UtilError, + }, + #[error("Unsupported RFB Protocol Version!")] + UnsupportRFBProtocolVersion, + #[error("Invalid Image Size!")] + InvalidImageSize, + #[error("Tcp bind failed: {0}")] + TcpBindFailed(String), + #[error("Make tls connection failed: {0}")] + MakeTlsConnectionFailed(String), + #[error("ProtocolMessage failed: {0}")] + ProtocolMessageFailed(String), + #[error("Read buf form tcpstream failed: {0}")] + ReadMessageFailed(String), + #[error("Authentication failed: {0}")] + AuthFailed(String), + #[error("ParseKeyBoardFailed: {0}")] + ParseKeyBoardFailed(String), +} diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 937a59cfd..a920f3bdc 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; //#[macro_use] @@ -19,39 +17,9 @@ extern crate sscanf; //#[macro_use] extern crate vmm_sys_util; -pub mod errors { - error_chain! { - links { - Util(util::errors::Error, util::errors::ErrorKind); - } - errors { - UnsupportRFBProtocolVersion { - display("Unsupported RFB Protocol Version!") - } - InvalidImageSize { - display("Invalid Image Size!") - } - TcpBindFailed(reason: String) { - display("Tcp bind failed: {}", reason) - } - MakeTlsConnectionFailed(reason: String) { - display("Make tls connection failed: {}", reason) - } - ProtocolMessageFailed(reason: String) { - display("ProtocolMessage failed: {}", reason) - } - ReadMessageFailed(reason: String) { - display("Read buf form tcpstream failed: {}", reason) - } - AuthFailed(reason: String){ - display("Authentication failed: {}", reason) - } - ParseKeyBoardFailed(reason: String) { - display("ParseKeyBoardFailed: {}", reason) - } - } - } -} +pub mod error; +pub use anyhow::Result; +pub use error::VncError; pub mod auth; pub mod client; diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 7ac257e22..ed20b3474 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -14,7 +14,6 @@ use crate::{ auth::{AuthState, SubAuthState}, client::VncClient, data::keycode::KEYSYM2KEYCODE, - errors::Result, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, get_image_width, unref_pixman_image, @@ -26,7 +25,7 @@ use crate::{ MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, }, }; - +use anyhow::Result; use machine_manager::{ config::{ObjConfig, VncConfig}, event_loop::EventLoop, diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 796d29bcc..3c38271e8 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -10,12 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::VncError; use crate::{ client::{ RectInfo, Rectangle, ServerMsg, VncClient, VncFeatures, ENCODING_ALPHA_CURSOR, ENCODING_RAW, ENCODING_RICH_CURSOR, }, - errors::{ErrorKind, Result}, pixman::{ bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, unref_pixman_image, PixelFormat, @@ -23,6 +23,7 @@ use crate::{ round_up, round_up_div, server::VncServer, }; +use anyhow::{anyhow, Result}; use core::time; use machine_manager::{ config::{ObjConfig, VncConfig}, @@ -109,7 +110,7 @@ pub fn vnc_init(vnc: &Option, object: &HashMap) -> Err(e) => { let msg = format!("Bind {} failed {}", addr, e); error!("{}", e); - return Err(ErrorKind::TcpBindFailed(msg).into()); + return Err(anyhow!(VncError::TcpBindFailed(msg))); } } -- Gitee From aa0022cfac90edb422d0cb3ccbefc82836c00bf3 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:17:27 +0800 Subject: [PATCH 0231/2187] Balloon/fpr: fix bugs when reporting_evt_handler sends interrupsts During reporting_evt_handler processing, the interrupt should be sent after all the data has been processed. Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 45d386741..b217e8ad4 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -569,16 +569,16 @@ impl BalloonIoHandler { .vring .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) .with_context(|| "Failed to add balloon response into used queue")?; - - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Vring - )) - }, - )?; } + + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + }, + )?; Ok(()) } -- Gitee From 991cced3d71c86aad79301cef0c222421a4226f3 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:34:30 +0800 Subject: [PATCH 0232/2187] Balloon/balloon: Modified the config_guidebook document regarding Balloon Signed-off-by: Li HuaChao --- docs/config_guidebook.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1c5716e28..7264c06f3 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -571,9 +571,8 @@ Or you can simply use `-serial dev` to bind serial with character device. Balloon is a virtio device, it offers a flex memory mechanism for VM. Two properties are supported for virtio-balloon. -* deflate_on_oom: whether to deflate balloon when there is no enough memory in guest. -This feature can prevent OOM occur in guest. -* free_page_reporting: whether to release free pages from kernel reporting. This feature can be used to reuse memory. +* deflate_on_oom: Deflate balloon on guest out of memory condition. If deflate_on_oom has not been negotiated, the driver MUST NOT use pages from the balloon when num_pages is less than or equal to the actual number of pages in the balloon. If deflate_on_oom has been negotiated, the driver MAY use pages from the balloon when num_pages is less than or equal to the actual number of pages in the balloon if this is required for system stability (e.g. if memory is required by applications running within the guest). This feature may prevent OOM occur in guest. +* free_page_reporting: whether to release free guest pages. This feature can be used to reuse memory. For virtio-balloon-pci, two more properties are required. * bus: name of bus which to attach. -- Gitee From 0360afd65f88f1c806c069f44cb0caf203076891 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:40:35 +0800 Subject: [PATCH 0233/2187] Balloon/balloon: Fix the bug when the balloon device is deflated When the balloon device is deflated and the advice is MADV_WILLNEED. We just hint the whole host page it lives on, since we can't do anything smaller. Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index b217e8ad4..d003e81f9 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -236,7 +236,11 @@ impl Request { } hvaset.sort_by_key(|&b| Reverse(b)); let host_page_size = host_page_size(); - if host_page_size == BALLOON_PAGE_SIZE { + // If host_page_size equals BALLOON_PAGE_SIZE, we can directly call the + // madvise function without any problem. And if the advice is MADV_WILLNEED, + // we just hint the whole host page it lives on, since we can't do anything + // smaller. + if host_page_size == BALLOON_PAGE_SIZE || advice == libc::MADV_WILLNEED { while let Some(hva) = hvaset.pop() { if last_addr == 0 { free_len += 1; -- Gitee From 859aeffac5c49748863fda5310309464b5791e0f Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 25 Oct 2022 14:46:26 +0800 Subject: [PATCH 0234/2187] Balloon/balloon: Modify some comments and variable names The main body of the balloon design is the balloon device, and the relevant description should be from the perspective of the device. Comments and variables are modified here to better describe the device. Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index d003e81f9..42199da2b 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -71,10 +71,10 @@ struct Iovec { /// Balloon configuration, which would be used to transport data between `Guest` and `Host`. #[derive(Copy, Clone, Default)] struct VirtioBalloonConfig { - /// Number of pages host wants Guest to give up. + /// The target page numbers of balloon device. #[allow(dead_code)] pub num_pages: u32, - /// Number of pages we've actually got in balloon. + /// Number of pages we've actually got in balloon device. #[allow(dead_code)] pub actual: u32, } @@ -749,9 +749,9 @@ pub struct Balloon { device_features: u64, /// Driver features. driver_features: u64, - /// Actual memory pages. + /// Actual memory pages of balloon device. actual: Arc, - /// Target memory pages. + /// Target memory pages of balloon device. num_pages: u32, /// Interrupt callback function. interrupt_cb: Option>, @@ -832,10 +832,10 @@ impl Balloon { warn!("Balloon used with backing page size > 4kiB, this may not be reliable"); } let target = (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32; - let current_ram_size = + let address_space_ram_size = (self.mem_info.lock().unwrap().get_ram_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32; - let vm_target = cmp::min(target, current_ram_size); - self.num_pages = current_ram_size - vm_target; + let vm_target = cmp::min(target, address_space_ram_size); + self.num_pages = address_space_ram_size - vm_target; self.signal_config_change().with_context(|| { "Failed to notify about configuration change after setting balloon memory" })?; -- Gitee From f865adc149387c938b18828ae5c200d9beb13e39 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 20 Oct 2022 11:16:10 +0800 Subject: [PATCH 0235/2187] define the constant NANOSECONDS_PER_SECOND Nanoseconds per second is used in many places, so let's define the constant variable for it. Signed-off-by: yezengruan --- acpi/src/acpi_device.rs | 5 +++-- machine_manager/src/qmp/mod.rs | 4 +++- util/src/leak_bucket.rs | 9 ++++----- util/src/lib.rs | 1 + util/src/time.rs | 13 +++++++++++++ virtio/src/vhost/user/client.rs | 3 ++- 6 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 util/src/time.rs diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index b907aa675..49c0ef912 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -16,9 +16,10 @@ use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; use log::error; +use util::time::NANOSECONDS_PER_SECOND; + // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; -const NANOSECONDS_PER_SECOND: u128 = 1_000_000_000; pub const ACPI_BITMASK_SLEEP_ENABLE: u16 = 0x2000; /// ACPI Power Management Timer @@ -49,7 +50,7 @@ impl AcpiPMTimer { } let now = Instant::now(); let time_nanos = now.duration_since(self.start).as_nanos(); - let counter: u128 = (time_nanos * PM_TIMER_FREQUENCY) / NANOSECONDS_PER_SECOND; + let counter: u128 = (time_nanos * PM_TIMER_FREQUENCY) / (NANOSECONDS_PER_SECOND as u128); data.copy_from_slice(&((counter & 0xFFFF_FFFF) as u32).to_le_bytes()); true diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index eb03e8cf0..16b35a2b0 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -43,6 +43,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use util::leak_bucket::LeakBucket; use util::set_termi_canon_mode; +use util::time::NANOSECONDS_PER_SECOND; use self::qmp_schema::{self as schema, QmpCommand}; use crate::event_loop::EventLoop; @@ -333,7 +334,8 @@ pub fn create_timestamp() -> TimeStamp { .duration_since(UNIX_EPOCH) .expect("Time went backwards"); let seconds = u128::from(since_the_epoch.as_secs()); - let microseconds = (since_the_epoch.as_nanos() - seconds * 1_000_000_000) / (1_000_u128); + let microseconds = + (since_the_epoch.as_nanos() - seconds * (NANOSECONDS_PER_SECOND as u128)) / (1_000_u128); TimeStamp { seconds: seconds as u64, microseconds: microseconds as u64, diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 10678562b..c17d04144 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -18,11 +18,10 @@ use log::error; use vmm_sys_util::eventfd::EventFd; use crate::loop_context::EventLoopContext; +use crate::time::NANOSECONDS_PER_SECOND; /// Used to improve the accuracy of bucket level. const ACCURACY_SCALE: u64 = 1000; -/// Nanoseconds per second. -const NANOS_PER_SEC: u64 = 1_000_000_000; /// Structure used to describe a Leaky Bucket. pub struct LeakBucket { @@ -73,10 +72,10 @@ impl LeakBucket { // update the water level let now = Instant::now(); let nanos = (now - self.prev_time).as_nanos(); - if nanos > (self.level * NANOS_PER_SEC / self.capacity) as u128 { + if nanos > (self.level * NANOSECONDS_PER_SECOND / self.capacity) as u128 { self.level = 0; } else { - self.level -= nanos as u64 * self.capacity / NANOS_PER_SEC; + self.level -= nanos as u64 * self.capacity / NANOSECONDS_PER_SECOND; } self.prev_time = now; @@ -92,7 +91,7 @@ impl LeakBucket { loop_context.delay_call( func, - (self.level - self.capacity) * NANOS_PER_SEC / self.capacity, + (self.level - self.capacity) * NANOSECONDS_PER_SECOND / self.capacity, ); self.timer_started = true; diff --git a/util/src/lib.rs b/util/src/lib.rs index bb098a468..de9b1ff34 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -32,6 +32,7 @@ pub mod reader; pub mod seccomp; pub mod syscall; pub mod tap; +pub mod time; pub mod trace; pub mod unix; pub use anyhow::Result; diff --git a/util/src/time.rs b/util/src/time.rs new file mode 100644 index 000000000..8fdd98222 --- /dev/null +++ b/util/src/time.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 658395950..ab059be9d 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -23,6 +23,7 @@ use machine_manager::event_loop::EventLoop; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; +use util::time::NANOSECONDS_PER_SECOND; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::super::super::{Queue, QueueConfig, VIRTIO_NET_F_CTRL_VQ}; @@ -101,7 +102,7 @@ fn vhost_user_reconnect(client: &Arc>) { { if let Some(ctx) = EventLoop::get_ctx(None) { // Default reconnecting time: 3s. - ctx.delay_call(func, 3 * 1_000_000_000); + ctx.delay_call(func, 3 * NANOSECONDS_PER_SECOND); } else { error!("Failed to get ctx to delay vhost-user reconnecting"); } -- Gitee From 5f8d872785ae7652ebc11148fda438228b7c7d05 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 20 Oct 2022 11:43:09 +0800 Subject: [PATCH 0236/2187] set CMOS RTC irq number for guest The CMOS RTC irq number must be set, otherwise, guest will assign the RTC irq number. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 93caeaaad..95a710615 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -18,6 +18,7 @@ use acpi::{ }; use address_space::GuestAddress; use anyhow::Result; +use hypervisor::kvm::KVM_FDS; use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; @@ -98,7 +99,7 @@ pub struct RTC { /// Index of Selected register. cur_index: u8, /// Interrupt eventfd. - interrupt_evt: EventFd, + interrupt_evt: Option, /// Resource of RTC. res: SysRes, /// Guest memory size. @@ -113,7 +114,7 @@ impl RTC { let mut rtc = RTC { cmos_data: [0_u8; 128], cur_index: 0_u8, - interrupt_evt: EventFd::new(libc::EFD_NONBLOCK)?, + interrupt_evt: Some(EventFd::new(libc::EFD_NONBLOCK)?), res: SysRes { region_base: RTC_PORT_INDEX, region_size: 8, @@ -239,6 +240,16 @@ impl RTC { sysbus.attach_device(&dev, region_base, region_size)?; Ok(()) } + + fn inject_interrupt(&self) { + if let Some(evt_fd) = self.interrupt_evt() { + if let Err(e) = evt_fd.write(1) { + error!("cmos rtc: failed to write interrupt eventfd ({}).", e); + } + return; + } + error!("cmos rtc: failed to get interrupt event fd."); + } } impl SysBusDevOps for RTC { @@ -265,7 +276,16 @@ impl SysBusDevOps for RTC { } fn interrupt_evt(&self) -> Option<&EventFd> { - Some(&self.interrupt_evt) + self.interrupt_evt.as_ref() + } + + fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { + let mut irq: i32 = -1; + if let Some(e) = self.interrupt_evt() { + irq = RTC_IRQ as i32; + KVM_FDS.load().register_irqfd(e, irq as u32)?; + } + Ok(irq) } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { -- Gitee From 027babdd5e1901bb29c0ff2347d4af57ab82f8db Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 15 Sep 2022 17:00:25 +0800 Subject: [PATCH 0237/2187] virtio-scsi-pci: add basic virtio-scsi-pci frame Add basic virtio scsi pci frame. We can add a virtio scsi controller by using cmdline just like: -device virtio-scsi-pci,bus=pcie.2,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1] Signed-off-by: liuxiangdong --- machine/src/lib.rs | 28 +++-- machine_manager/src/config/mod.rs | 2 + machine_manager/src/config/scsi.rs | 100 ++++++++++++++++ virtio/src/lib.rs | 14 +++ virtio/src/scsi/controller.rs | 184 +++++++++++++++++++++++++++++ virtio/src/scsi/mod.rs | 13 ++ virtio/src/virtio_pci.rs | 3 +- 7 files changed, 336 insertions(+), 8 deletions(-) create mode 100644 machine_manager/src/config/scsi.rs create mode 100644 virtio/src/scsi/controller.rs create mode 100644 virtio/src/scsi/mod.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 275ddda1e..dfd997734 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -48,10 +48,10 @@ use machine_manager::config::parse_gpu; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, - parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, - MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, - SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + parse_scsi_controller, parse_usb_keyboard, parse_usb_tablet, parse_vfio, + parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, + BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, + NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::{ event_loop::EventLoop, @@ -75,9 +75,9 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, VhostKern, - VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, - VirtioPciDevice, + balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiCntlr, + VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, + VirtioNetState, VirtioPciDevice, }; pub trait MachineOps { @@ -601,6 +601,17 @@ pub trait MachineOps { Ok(()) } + fn add_virtio_pci_scsi(&mut self, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let device_cfg = parse_scsi_controller(cfg_args)?; + let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(device_cfg.clone()))); + self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false) + .with_context(|| "Failed to add virtio scsi controller")?; + self.reset_bus(&device_cfg.id)?; + Ok(()) + } + fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -1028,6 +1039,9 @@ pub trait MachineOps { "virtio-blk-pci" => { self.add_virtio_pci_blk(vm_config, cfg_args)?; } + "virtio-scsi-pci" => { + self.add_virtio_pci_scsi(cfg_args)?; + } "virtio-net-device" => { self.add_virtio_mmio_net(vm_config, cfg_args)?; } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index e078764ef..cb0162d09 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -25,6 +25,7 @@ pub use network::*; pub use numa::*; pub use pci::*; pub use rng::*; +pub use scsi::*; pub use usb::*; pub use vfio::*; pub use vnc::*; @@ -44,6 +45,7 @@ mod network; mod numa; mod pci; mod rng; +mod scsi; mod usb; mod vfio; pub mod vnc; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs new file mode 100644 index 000000000..4e3e528b7 --- /dev/null +++ b/machine_manager/src/config/scsi.rs @@ -0,0 +1,100 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{anyhow, Result}; + +use super::{error::ConfigError, pci_args_check}; +use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; + +#[derive(Debug, Clone)] +pub struct ScsiCntlrConfig { + /// Virtio-scsi-pci device id. + pub id: String, + /// Thread name of io handler. + pub iothread: Option, + /// Number of scsi cmd queues. + pub queues: u32, +} + +impl Default for ScsiCntlrConfig { + fn default() -> Self { + ScsiCntlrConfig { + id: "".to_string(), + iothread: None, + //At least 1 cmd queue. + queues: 1, + } + } +} + +impl ConfigCheck for ScsiCntlrConfig { + fn check(&self) -> Result<()> { + if self.id.len() > MAX_STRING_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "virtio-scsi-pci device id".to_string(), + MAX_STRING_LENGTH, + ))); + } + + if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "iothread name".to_string(), + MAX_STRING_LENGTH, + ))); + } + + if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u32 { + return Err(anyhow!(ConfigError::IllegalValue( + "queues number of scsi controller".to_string(), + 1, + true, + MAX_VIRTIO_QUEUE as u64, + true, + ))); + } + + Ok(()) + } +} + +pub fn parse_scsi_controller(drive_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("virtio-scsi-pci"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("addr") + .push("multifunction") + .push("iothread"); + + cmd_parser.parse(drive_config)?; + + pci_args_check(&cmd_parser)?; + + let mut cntlr_cfg = ScsiCntlrConfig::default(); + + if let Some(iothread) = cmd_parser.get_value::("iothread")? { + cntlr_cfg.iothread = Some(iothread); + } + + if let Some(id) = cmd_parser.get_value::("id")? { + cntlr_cfg.id = id; + } else { + return Err(anyhow!(ConfigError::FieldIsMissing( + "id", + "virtio scsi pci" + ))); + } + + cntlr_cfg.check()?; + Ok(cntlr_cfg) +} diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index a3190aa0d..c129f52bc 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -34,6 +34,7 @@ mod gpu; mod net; mod queue; mod rng; +mod scsi; pub mod vhost; mod virtio_mmio; #[allow(dead_code)] @@ -51,6 +52,7 @@ pub use gpu::*; pub use net::*; pub use queue::*; pub use rng::{Rng, RngState}; +pub use scsi::controller as ScsiCntlr; pub use vhost::kernel as VhostKern; pub use vhost::user as VhostUser; pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; @@ -74,6 +76,7 @@ pub const VIRTIO_TYPE_BLOCK: u32 = 2; pub const VIRTIO_TYPE_CONSOLE: u32 = 3; pub const VIRTIO_TYPE_RNG: u32 = 4; pub const VIRTIO_TYPE_BALLOON: u32 = 5; +pub const VIRTIO_TYPE_SCSI: u32 = 8; pub const VIRTIO_TYPE_GPU: u32 = 16; pub const VIRTIO_TYPE_VSOCK: u32 = 19; pub const VIRTIO_TYPE_FS: u32 = 26; @@ -155,6 +158,17 @@ pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000; /// Support more than one virtqueue. pub const VIRTIO_BLK_F_MQ: u32 = 12; +/// A single request can include both device-readable and device-writable data buffers. +pub const VIRTIO_SCSI_F_INOUT: u32 = 0; +/// The host SHOULD enable reporting of hot-plug and hot-unplug events for LUNs and targets on the SCSI bus. +/// The guest SHOULD handle hot-plug and hot-unplug events. +pub const VIRTIO_SCSI_F_HOTPLUG: u32 = 1; +/// The host will report changes to LUN parameters via a VIRTIO_SCSI_T_PARAM_CHANGE event. +/// The guest SHOULD handle them. +pub const VIRTIO_SCSI_F_CHANGE: u32 = 2; +/// The extended fields for T10 protection information (DIF/DIX) are included in the SCSI request header. +pub const VIRTIO_SCSI_F_T10_PI: u32 = 3; + /// The IO type of virtio block, refer to Virtio Spec. /// Read. pub const VIRTIO_BLK_T_IN: u32 = 0; diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs new file mode 100644 index 000000000..f4d2b6e34 --- /dev/null +++ b/virtio/src/scsi/controller.rs @@ -0,0 +1,184 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cmp; +use std::io::Write; +use std::sync::{Arc, Mutex}; + +use anyhow::{anyhow, bail, Result}; + +use super::super::{Queue, VirtioDevice, VirtioInterrupt, VIRTIO_TYPE_SCSI}; +use crate::VirtioError; +use address_space::AddressSpace; +use log::warn; +use machine_manager::config::{ConfigCheck, ScsiCntlrConfig}; +use util::byte_code::ByteCode; +use util::num_ops::{read_u32, write_u32}; +use vmm_sys_util::eventfd::EventFd; + +/// Virtio Scsi Controller has 1 ctrl queue, 1 event queue and at least 1 cmd queue. +const SCSI_CTRL_QUEUE_NUM: usize = 1; +const SCSI_EVENT_QUEUE_NUM: usize = 1; +const SCSI_MIN_QUEUE_NUM: usize = 3; +/// Size of each virtqueue. +const QUEUE_SIZE_SCSI: u16 = 256; + +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioScsiConfig { + num_queues: u32, + seg_max: u32, + max_sectors: u32, + cmd_per_lun: u32, + event_info_size: u32, + sense_size: u32, + cdb_size: u32, + max_channel: u32, + max_target: u32, + max_lun: u32, +} + +impl ByteCode for VirtioScsiConfig {} + +/// State of virtio scsi controller. +#[derive(Clone, Copy, Default)] +pub struct ScsiCntlrState { + /// Bitmask of features supported by the backend. + device_features: u64, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, + /// Config space of the virtio scsi controller. + config_space: VirtioScsiConfig, +} + +/// Virtio Scsi Controller device structure. +#[derive(Default)] +pub struct ScsiCntlr { + /// Configuration of the virtio scsi controller. + config: ScsiCntlrConfig, + /// Status of virtio scsi controller. + state: ScsiCntlrState, +} + +impl ScsiCntlr { + pub fn new(config: ScsiCntlrConfig) -> ScsiCntlr { + Self { + config, + state: ScsiCntlrState::default(), + } + } +} + +impl VirtioDevice for ScsiCntlr { + /// Realize virtio scsi controller, which is a pci device. + fn realize(&mut self) -> Result<()> { + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } + + /// Get the virtio device type, refer to Virtio Spec. + fn device_type(&self) -> u32 { + VIRTIO_TYPE_SCSI + } + + /// Get the count of virtio device queues. + fn queue_num(&self) -> usize { + self.config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM + } + + /// Get the queue size of virtio device. + fn queue_size(&self) -> u16 { + QUEUE_SIZE_SCSI + } + + /// Get device features from host. + fn get_device_features(&self, features_select: u32) -> u32 { + read_u32(self.state.device_features, features_select) + } + + /// Set driver features by guest. + fn set_driver_features(&mut self, page: u32, value: u32) { + let mut features = write_u32(value, page); + let unrequested_features = features & !self.state.device_features; + if unrequested_features != 0 { + warn!( + "Received acknowledge request with unsupported feature for virtio scsi: 0x{:x}", + features + ); + features &= !unrequested_features; + } + self.state.driver_features |= features; + } + + /// Get driver features by guest. + fn get_driver_features(&self, features_select: u32) -> u32 { + read_u32(self.state.driver_features, features_select) + } + + /// Read data of config from guest. + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + let config_slice = self.state.config_space.as_bytes(); + let config_len = config_slice.len() as u64; + if offset >= config_len { + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); + } + if let Some(end) = offset.checked_add(data.len() as u64) { + data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; + } + + Ok(()) + } + + /// Write data to config from guest. + fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + let data_len = data.len(); + let config_slice = self.state.config_space.as_mut_bytes(); + let config_len = config_slice.len(); + if offset as usize + data_len > config_len { + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset, + config_len as u64 + ))); + } + + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + + Ok(()) + } + + /// Activate the virtio device, this function is called by vcpu thread when frontend + /// virtio driver is ready and write `DRIVER_OK` to backend. + fn activate( + &mut self, + _mem_space: Arc, + _interrupt_cb: Arc, + queues: &[Arc>], + _queue_evts: Vec, + ) -> Result<()> { + let queue_num = queues.len(); + if queue_num < SCSI_MIN_QUEUE_NUM { + bail!("virtio scsi controller queues num can not be less than 3!"); + } + Ok(()) + } + + fn deactivate(&mut self) -> Result<()> { + Ok(()) + } + + fn update_config(&mut self, _dev_config: Option>) -> Result<()> { + Ok(()) + } +} diff --git a/virtio/src/scsi/mod.rs b/virtio/src/scsi/mod.rs new file mode 100644 index 000000000..859ab442b --- /dev/null +++ b/virtio/src/scsi/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod controller; diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index d5d797c50..6ae7630b5 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -42,7 +42,7 @@ use crate::{ use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, - VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, + VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -123,6 +123,7 @@ const MAX_FEATURES_SELECT_NUM: u32 = 2; fn get_virtio_class_id(device_type: u32) -> u16 { match device_type { VIRTIO_TYPE_BLOCK => VIRTIO_PCI_CLASS_ID_BLOCK, + VIRTIO_TYPE_SCSI => VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_NET => VIRTIO_PCI_CLASS_ID_NET, _ => VIRTIO_PCI_CLASS_ID_OTHERS, } -- Gitee From 34d8392242442c21e07d03054bd1fa821e974d89 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 17 Sep 2022 15:41:39 +0800 Subject: [PATCH 0238/2187] virtio-scsi: add scsi bus frame The scsi bus named "$scsi_controller_id.0" will be created and attached to the virtio scsi controller when creating controller. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 22 ++++++++++++-- machine/src/standard_vm/aarch64/mod.rs | 8 ++++++ machine/src/standard_vm/x86_64/mod.rs | 8 ++++++ virtio/src/lib.rs | 1 + virtio/src/scsi/bus.rs | 40 ++++++++++++++++++++++++++ virtio/src/scsi/controller.rs | 8 ++++++ virtio/src/scsi/mod.rs | 1 + 7 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 virtio/src/scsi/bus.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index dfd997734..c4b4d4b96 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -75,10 +75,11 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiCntlr, - VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, - VirtioNetState, VirtioPciDevice, + balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiBus, + ScsiCntlr, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, + VirtioMmioState, VirtioNetState, VirtioPciDevice, }; +use ScsiCntlr::ScsiCntlrMap; pub trait MachineOps { /// Calculate the ranges of memory according to architecture. @@ -292,6 +293,11 @@ pub trait MachineOps { None } + /// Get the Scsi Controller list. The map stores the mapping between scsi bus name and scsi controller. + fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { + None + } + /// Add net device. /// /// # Arguments @@ -606,6 +612,16 @@ pub trait MachineOps { let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_scsi_controller(cfg_args)?; let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(device_cfg.clone()))); + + let bus_name = format!("{}.0", device_cfg.id); + ScsiBus::create_scsi_bus(&bus_name, &device)?; + if let Some(cntlr_list) = self.get_scsi_cntlr_list() { + let mut lock_cntlr_list = cntlr_list.lock().unwrap(); + lock_cntlr_list.insert(bus_name, device.clone()); + } else { + bail!("No scsi controller list found!"); + } + self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false) .with_context(|| "Failed to add virtio scsi controller")?; self.reset_bus(&device_cfg.id)?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 99862e2dd..ddeab8b9b 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -70,6 +70,7 @@ use vnc::vnc; use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; use crate::MachineOps; use anyhow::{anyhow, bail, Context, Result}; +use virtio::ScsiCntlr::ScsiCntlrMap; /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { @@ -146,6 +147,8 @@ pub struct StdMachine { fwcfg_dev: Option>>, /// Bus device used to attach other devices. Only USB controller used now. bus_device: BusDeviceMap, + /// Scsi Controller List. + scsi_cntlr_list: ScsiCntlrMap, } impl StdMachine { @@ -195,6 +198,7 @@ impl StdMachine { boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, bus_device: Arc::new(Mutex::new(HashMap::new())), + scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), }) } @@ -629,6 +633,10 @@ impl MachineOps for StdMachine { fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { Some(&self.bus_device) } + + fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { + Some(&self.scsi_cntlr_list) + } } impl AcpiBuilder for StdMachine { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index b501495ca..6c100f8ed 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -63,6 +63,7 @@ use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; use crate::{vm_state, MachineOps}; use anyhow::{anyhow, bail, Context, Result}; +use virtio::ScsiCntlr::ScsiCntlrMap; #[cfg(not(target_env = "musl"))] use vnc::vnc; @@ -125,6 +126,8 @@ pub struct StdMachine { fwcfg_dev: Option>>, /// Bus device used to attach other devices. Only USB controller used now. bus_device: BusDeviceMap, + /// Scsi Controller List. + scsi_cntlr_list: ScsiCntlrMap, } impl StdMachine { @@ -176,6 +179,7 @@ impl StdMachine { boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, bus_device: Arc::new(Mutex::new(HashMap::new())), + scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), }) } @@ -598,6 +602,10 @@ impl MachineOps for StdMachine { fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { Some(&self.bus_device) } + + fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { + Some(&self.scsi_cntlr_list) + } } impl AcpiBuilder for StdMachine { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index c129f52bc..3dd4fdf13 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -52,6 +52,7 @@ pub use gpu::*; pub use net::*; pub use queue::*; pub use rng::{Rng, RngState}; +pub use scsi::bus as ScsiBus; pub use scsi::controller as ScsiCntlr; pub use vhost::kernel as VhostKern; pub use vhost::user as VhostUser; diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs new file mode 100644 index 000000000..f88ab791b --- /dev/null +++ b/virtio/src/scsi/bus.rs @@ -0,0 +1,40 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex, Weak}; + +use anyhow::Result; + +use crate::ScsiCntlr::ScsiCntlr; + +pub struct ScsiBus { + /// Bus name. + pub name: String, + /// Scsi Controller which the bus orignates from. + pub parent_cntlr: Weak>, +} + +impl ScsiBus { + pub fn new(bus_name: String, parent_cntlr: Weak>) -> ScsiBus { + ScsiBus { + name: bus_name, + parent_cntlr, + } + } +} + +pub fn create_scsi_bus(bus_name: &str, scsi_cntlr: &Arc>) -> Result<()> { + let mut locked_scsi_cntlr = scsi_cntlr.lock().unwrap(); + let bus = ScsiBus::new(bus_name.to_string(), Arc::downgrade(scsi_cntlr)); + locked_scsi_cntlr.bus = Some(Arc::new(Mutex::new(bus))); + Ok(()) +} diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index f4d2b6e34..38710d7e7 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -11,12 +11,14 @@ // See the Mulan PSL v2 for more details. use std::cmp; +use std::collections::HashMap; use std::io::Write; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Result}; use super::super::{Queue, VirtioDevice, VirtioInterrupt, VIRTIO_TYPE_SCSI}; +use crate::ScsiBus::ScsiBus; use crate::VirtioError; use address_space::AddressSpace; use log::warn; @@ -32,6 +34,9 @@ const SCSI_MIN_QUEUE_NUM: usize = 3; /// Size of each virtqueue. const QUEUE_SIZE_SCSI: u16 = 256; +/// The key is bus name, the value is the attached Scsi Controller. +pub type ScsiCntlrMap = Arc>>>>; + #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] struct VirtioScsiConfig { @@ -67,6 +72,8 @@ pub struct ScsiCntlr { config: ScsiCntlrConfig, /// Status of virtio scsi controller. state: ScsiCntlrState, + /// Scsi bus. + pub bus: Option>>, } impl ScsiCntlr { @@ -74,6 +81,7 @@ impl ScsiCntlr { Self { config, state: ScsiCntlrState::default(), + bus: None, } } } diff --git a/virtio/src/scsi/mod.rs b/virtio/src/scsi/mod.rs index 859ab442b..50fdf8df0 100644 --- a/virtio/src/scsi/mod.rs +++ b/virtio/src/scsi/mod.rs @@ -10,4 +10,5 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod bus; pub mod controller; -- Gitee From 10f5f78b100f6707cd04cd8151216dd3be793ec9 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 17 Sep 2022 16:54:48 +0800 Subject: [PATCH 0239/2187] scsi-hd: add basic scsi harddisk frame Add basic scsi hardsisk frame. We can add a virtio scsi disk by using cmdline just like: -drive file=$image_file,id=$drive_id \ -device scsi-hd,bus=$scsi_controller_name.0,scsi-id=0,lun=0,drive=$drive_id,id=$disk_id Only support scsi-id=0 now. Lun number should start from 0. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 54 +++++++++- machine_manager/src/config/scsi.rs | 88 +++++++++++++++- virtio/src/lib.rs | 1 + virtio/src/scsi/bus.rs | 5 + virtio/src/scsi/disk.rs | 162 +++++++++++++++++++++++++++++ virtio/src/scsi/mod.rs | 1 + 6 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 virtio/src/scsi/disk.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c4b4d4b96..49cbeed16 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -48,7 +48,7 @@ use machine_manager::config::parse_gpu; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_scsi_controller, parse_usb_keyboard, parse_usb_tablet, parse_vfio, + parse_scsi_controller, parse_scsi_device, parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, @@ -76,10 +76,11 @@ use vfio::{VfioDevice, VfioPciDevice}; use virtio::Gpu; use virtio::{ balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiBus, - ScsiCntlr, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, + ScsiCntlr, ScsiDisk, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; use ScsiCntlr::ScsiCntlrMap; +use ScsiDisk::SCSI_TYPE_DISK; pub trait MachineOps { /// Calculate the ranges of memory according to architecture. @@ -628,6 +629,52 @@ pub trait MachineOps { Ok(()) } + fn add_scsi_device( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + scsi_type: u32, + ) -> Result<()> { + let device_cfg = parse_scsi_device(vm_config, cfg_args)?; + let device = Arc::new(Mutex::new(ScsiDisk::ScsiDevice::new( + device_cfg.clone(), + scsi_type, + ))); + + let lock_cntlr_list = if let Some(cntlr_list) = self.get_scsi_cntlr_list() { + cntlr_list.lock().unwrap() + } else { + bail!("Wrong! No scsi controller list found") + }; + + let lock_cntlr = if let Some(cntlr) = lock_cntlr_list.get(&device_cfg.bus) { + cntlr.lock().unwrap() + } else { + bail!("Wrong! Bus {} not found in list", &device_cfg.bus) + }; + + if let Some(bus) = &lock_cntlr.bus { + if bus + .lock() + .unwrap() + .devices + .contains_key(&(device_cfg.target, device_cfg.lun)) + { + bail!("Wrong! Two scsi devices have the same scsi-id and lun"); + } + bus.lock() + .unwrap() + .devices + .insert((device_cfg.target, device_cfg.lun), device.clone()); + device.lock().unwrap().parent_bus = Arc::downgrade(bus); + } else { + bail!("Wrong! Controller has no bus {} !", &device_cfg.bus); + } + + device.lock().unwrap().realize()?; + Ok(()) + } + fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -1058,6 +1105,9 @@ pub trait MachineOps { "virtio-scsi-pci" => { self.add_virtio_pci_scsi(cfg_args)?; } + "scsi-hd" => { + self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_DISK)?; + } "virtio-net-device" => { self.add_virtio_mmio_net(vm_config, cfg_args)?; } diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 4e3e528b7..34048cdc7 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -13,7 +13,7 @@ use anyhow::{anyhow, Result}; use super::{error::ConfigError, pci_args_check}; -use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; +use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; #[derive(Debug, Clone)] pub struct ScsiCntlrConfig { @@ -98,3 +98,89 @@ pub fn parse_scsi_controller(drive_config: &str) -> Result { cntlr_cfg.check()?; Ok(cntlr_cfg) } + +#[derive(Clone, Default)] +pub struct ScsiDevConfig { + /// Scsi Device id. + pub id: String, + /// The image file path. + pub path_on_host: String, + /// Serial number of the scsi device. + pub serial: Option, + /// Scsi bus which the scsi device attaches to. + pub bus: String, + /// Scsi four level hierarchical address(host, channel, target, lun). + pub channel: u8, + pub target: u8, + pub lun: u16, +} + +pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("scsi-device"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("scsi-id") + .push("lun") + .push("serial") + .push("drive"); + + cmd_parser.parse(drive_config)?; + + let mut scsi_dev_cfg = ScsiDevConfig::default(); + + let scsi_drive = if let Some(drive) = cmd_parser.get_value::("drive")? { + drive + } else { + return Err(anyhow!(ConfigError::FieldIsMissing("drive", "scsi device"))); + }; + + if let Some(serial) = cmd_parser.get_value::("serial")? { + scsi_dev_cfg.serial = Some(serial); + } + + if let Some(id) = cmd_parser.get_value::("id")? { + scsi_dev_cfg.id = id; + } else { + return Err(anyhow!(ConfigError::FieldIsMissing("id", "scsi device"))); + } + + if let Some(bus) = cmd_parser.get_value::("bus")? { + scsi_dev_cfg.bus = bus; + } else { + return Err(anyhow!(ConfigError::FieldIsMissing("bus", "scsi device"))); + } + + if let Some(target) = cmd_parser.get_value::("scsi-id")? { + if target != 0 { + return Err(anyhow!(ConfigError::IllegalValue( + "scsi-id of scsi device".to_string(), + 0, + true, + 0, + true, + ))); + } + scsi_dev_cfg.target = target; + } + + if let Some(lun) = cmd_parser.get_value::("lun")? { + if lun > 255 { + return Err(anyhow!(ConfigError::IllegalValue( + "lun of scsi device".to_string(), + 0, + true, + 255, + true, + ))); + } + scsi_dev_cfg.lun = lun; + } + + if let Some(drive_arg) = &vm_config.drives.remove(&scsi_drive) { + scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); + } + + Ok(scsi_dev_cfg) +} diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 3dd4fdf13..0e0d710a3 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -54,6 +54,7 @@ pub use queue::*; pub use rng::{Rng, RngState}; pub use scsi::bus as ScsiBus; pub use scsi::controller as ScsiCntlr; +pub use scsi::disk as ScsiDisk; pub use vhost::kernel as VhostKern; pub use vhost::user as VhostUser; pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index f88ab791b..1d5b5078e 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -10,15 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use crate::ScsiCntlr::ScsiCntlr; +use crate::ScsiDisk::ScsiDevice; pub struct ScsiBus { /// Bus name. pub name: String, + /// Scsi Devices attached to the bus. + pub devices: HashMap<(u8, u16), Arc>>, /// Scsi Controller which the bus orignates from. pub parent_cntlr: Weak>, } @@ -27,6 +31,7 @@ impl ScsiBus { pub fn new(bus_name: String, parent_cntlr: Weak>) -> ScsiBus { ScsiBus { name: bus_name, + devices: HashMap::new(), parent_cntlr, } } diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs new file mode 100644 index 000000000..83218c3e3 --- /dev/null +++ b/virtio/src/scsi/disk.rs @@ -0,0 +1,162 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::fs::{File, OpenOptions}; +use std::io::{Seek, SeekFrom}; +use std::os::unix::fs::OpenOptionsExt; +use std::sync::{Arc, Mutex, Weak}; + +use anyhow::{bail, Context, Result}; + +use crate::ScsiBus::ScsiBus; +use machine_manager::config::ScsiDevConfig; + +/// SCSI DEVICE TYPES. +pub const SCSI_TYPE_DISK: u32 = 0x00; +pub const SCSI_TYPE_TAPE: u32 = 0x01; +pub const SCSI_TYPE_PRINTER: u32 = 0x02; +pub const SCSI_TYPE_PROCESSOR: u32 = 0x03; +pub const SCSI_TYPE_WORM: u32 = 0x04; +pub const SCSI_TYPE_ROM: u32 = 0x05; +pub const SCSI_TYPE_SCANNER: u32 = 0x06; +pub const SCSI_TYPE_MOD: u32 = 0x07; +pub const SCSI_TYPE_MEDIUM_CHANGER: u32 = 0x08; +pub const SCSI_TYPE_STORAGE_ARRAY: u32 = 0x0c; +pub const SCSI_TYPE_ENCLOSURE: u32 = 0x0d; +pub const SCSI_TYPE_RBC: u32 = 0x0e; +pub const SCSI_TYPE_OSD: u32 = 0x11; +pub const SCSI_TYPE_ZBC: u32 = 0x14; +pub const SCSI_TYPE_WLUN: u32 = 0x1e; +pub const SCSI_TYPE_NOT_PRESENT: u32 = 0x1f; +pub const SCSI_TYPE_INACTIVE: u32 = 0x20; +pub const SCSI_TYPE_NO_LUN: u32 = 0x7f; + +/// Used to compute the number of sectors. +const SECTOR_SHIFT: u8 = 9; +/// Size of the dummy block device. +const DUMMY_IMG_SIZE: u64 = 0; + +#[derive(Clone, Default)] +pub struct ScsiDevState { + /// Features which the scsi device supports. + pub features: u32, + /// Scsi device vendor identification. + pub vendor: String, + /// Scsi device product identification. + pub product: String, + /// Scsi device id. + pub device_id: String, + /// The standard version which the scsi device complies to. + pub version: String, + /// Scsi device serial number. + pub serial: String, +} + +impl ScsiDevState { + fn new() -> Self { + ScsiDevState { + features: 0, + vendor: "STRA".to_string(), + product: "".to_string(), + device_id: "".to_string(), + version: "".to_string(), + serial: "".to_string(), + } + } +} + +#[derive(Clone)] +pub struct ScsiDevice { + /// Configuration of the scsi device. + pub config: ScsiDevConfig, + /// State of the scsi device. + pub state: ScsiDevState, + /// Image file opened. + pub disk_image: Option>, + /// Number of sectors of the image file. + pub disk_sectors: u64, + /// Scsi device type. + pub scsi_type: u32, + /// Scsi Bus attached to. + pub parent_bus: Weak>, +} + +impl Default for ScsiDevice { + fn default() -> Self { + ScsiDevice { + config: Default::default(), + state: Default::default(), + disk_image: None, + disk_sectors: 0, + scsi_type: SCSI_TYPE_DISK, + parent_bus: Weak::new(), + } + } +} + +impl ScsiDevice { + pub fn new(config: ScsiDevConfig, scsi_type: u32) -> ScsiDevice { + ScsiDevice { + config, + state: ScsiDevState::new(), + disk_image: None, + disk_sectors: 0, + scsi_type, + parent_bus: Weak::new(), + } + } + + pub fn realize(&mut self) -> Result<()> { + match self.scsi_type { + SCSI_TYPE_DISK => { + self.state.product = "STRA HARDDISK".to_string(); + } + _ => { + bail!("Scsi type {} does not support now", self.scsi_type); + } + } + + if let Some(serial) = &self.config.serial { + self.state.serial = serial.clone(); + } + let mut disk_size = DUMMY_IMG_SIZE; + + if !self.config.path_on_host.is_empty() { + self.disk_image = None; + + let mut file = OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_DIRECT) + .open(&self.config.path_on_host) + .with_context(|| { + format!( + "Failed to open the file {} for scsi device", + self.config.path_on_host + ) + })?; + + disk_size = file + .seek(SeekFrom::End(0)) + .with_context(|| "Failed to seek the end for scsi device")? + as u64; + + self.disk_image = Some(Arc::new(file)); + } else { + self.disk_image = None; + } + + self.disk_sectors = disk_size >> SECTOR_SHIFT; + + Ok(()) + } +} diff --git a/virtio/src/scsi/mod.rs b/virtio/src/scsi/mod.rs index 50fdf8df0..b05f8ea5e 100644 --- a/virtio/src/scsi/mod.rs +++ b/virtio/src/scsi/mod.rs @@ -12,3 +12,4 @@ pub mod bus; pub mod controller; +pub mod disk; -- Gitee From f02551721e321b0f959eb8c91e1988f72481c47e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 20 Sep 2022 15:31:22 +0800 Subject: [PATCH 0240/2187] virtio-scsi: Implement the basic structure for controller queue All virtio-scsi devices share virtio-scsi controller's queues. The controller has 1 Controller queue, 1 Event queue, and N Command queues. Implement the basic structure for Controller queue. Controller queue is used for task management functions(TMFs) such as starting up, shutting down, resetting, etc. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 391 +++++++++++++++++++++++++++++++++- 1 file changed, 382 insertions(+), 9 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 38710d7e7..1481802ac 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -13,19 +13,30 @@ use std::cmp; use std::collections::HashMap; use std::io::Write; +use std::mem::size_of; +use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; -use super::super::{Queue, VirtioDevice, VirtioInterrupt, VIRTIO_TYPE_SCSI}; +use super::super::{ + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_SCSI, +}; use crate::ScsiBus::ScsiBus; use crate::VirtioError; -use address_space::AddressSpace; -use log::warn; -use machine_manager::config::{ConfigCheck, ScsiCntlrConfig}; +use address_space::{AddressSpace, GuestAddress}; +use log::{error, info, warn}; +use machine_manager::{ + config::{ConfigCheck, ScsiCntlrConfig}, + event_loop::EventLoop, +}; +use util::aio::Iovec; use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use util::num_ops::{read_u32, write_u32}; -use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Virtio Scsi Controller has 1 ctrl queue, 1 event queue and at least 1 cmd queue. const SCSI_CTRL_QUEUE_NUM: usize = 1; @@ -34,9 +45,58 @@ const SCSI_MIN_QUEUE_NUM: usize = 3; /// Size of each virtqueue. const QUEUE_SIZE_SCSI: u16 = 256; +/// Default values of the cdb and sense data size configuration fields. Cannot change cdb size +/// and sense data size Now. +/// To do: support Override CDB/sense data size.(Guest controlled) +pub const VIRTIO_SCSI_CDB_DEFAULT_SIZE: usize = 32; +pub const VIRTIO_SCSI_SENSE_DEFAULT_SIZE: usize = 96; + /// The key is bus name, the value is the attached Scsi Controller. pub type ScsiCntlrMap = Arc>>>>; +/// Control type codes. +/// Task Management Function. +pub const VIRTIO_SCSI_T_TMF: u32 = 0; +/// Asynchronous notification query. +pub const VIRTIO_SCSI_T_AN_QUERY: u32 = 1; +/// Asynchronous notification subscription. +pub const VIRTIO_SCSI_T_AN_SUBSCRIBE: u32 = 2; + +/// Valid TMF Subtypes. +pub const VIRTIO_SCSI_T_TMF_ABORT_TASK: u32 = 0; +pub const VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: u32 = 1; +pub const VIRTIO_SCSI_T_TMF_CLEAR_ACA: u32 = 2; +pub const VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: u32 = 3; +pub const VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: u32 = 4; +pub const VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: u32 = 5; +pub const VIRTIO_SCSI_T_TMF_QUERY_TASK: u32 = 6; +pub const VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: u32 = 7; + +/// Response codes. +pub const VIRTIO_SCSI_S_OK: u8 = 0; +pub const VIRTIO_SCSI_S_OVERRUN: u8 = 1; +pub const VIRTIO_SCSI_S_ABORTED: u8 = 2; +pub const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; +pub const VIRTIO_SCSI_S_RESET: u8 = 4; +pub const VIRTIO_SCSI_S_BUSY: u8 = 5; +pub const VIRTIO_SCSI_S_TRANSPORT_FAILURE: u8 = 6; +pub const VIRTIO_SCSI_S_TARGET_FAILURE: u8 = 7; +pub const VIRTIO_SCSI_S_NEXUS_FAILURE: u8 = 8; +pub const VIRTIO_SCSI_S_FAILURE: u8 = 9; +pub const VIRTIO_SCSI_S_FUNCTION_SUCCEEDED: u8 = 10; +pub const VIRTIO_SCSI_S_FUNCTION_REJECTED: u8 = 11; +pub const VIRTIO_SCSI_S_INCORRECT_LUN: u8 = 12; + +#[derive(Clone)] +pub enum ScsiXferMode { + /// TEST_UNIT_READY, ... + ScsiXferNone, + /// READ, INQUIRY, MODE_SENSE, ... + ScsiXferFromDev, + /// WRITE, MODE_SELECT, ... + ScsiXferToDev, +} + #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] struct VirtioScsiConfig { @@ -170,15 +230,30 @@ impl VirtioDevice for ScsiCntlr { /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, - _mem_space: Arc, - _interrupt_cb: Arc, + mem_space: Arc, + interrupt_cb: Arc, queues: &[Arc>], - _queue_evts: Vec, + mut queue_evts: Vec, ) -> Result<()> { let queue_num = queues.len(); if queue_num < SCSI_MIN_QUEUE_NUM { bail!("virtio scsi controller queues num can not be less than 3!"); } + + let ctrl_queue = queues[0].clone(); + let ctrl_queue_evt = queue_evts.remove(0); + let ctrl_handler = ScsiCtrlHandler { + queue: ctrl_queue, + queue_evt: ctrl_queue_evt, + mem_space, + interrupt_cb: interrupt_cb.clone(), + driver_features: self.state.driver_features, + }; + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), + self.config.iothread.as_ref(), + )?; + Ok(()) } @@ -190,3 +265,301 @@ impl VirtioDevice for ScsiCntlr { Ok(()) } } + +fn build_event_notifier(fd: RawFd, handler: Box) -> EventNotifier { + EventNotifier::new( + NotifierOperation::AddShared, + fd, + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + ) +} + +/// Task Managememt Request +#[derive(Copy, Clone, Debug, Default)] +pub struct VirtioScsiCtrlTmfReq { + pub ctrltype: u32, + pub subtype: u32, + pub lun: [u8; 8], + pub tag: u64, +} + +impl ByteCode for VirtioScsiCtrlTmfReq {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct VirtioScsiCtrlTmfResp { + pub response: u8, +} + +impl ByteCode for VirtioScsiCtrlTmfResp {} + +/// Asynchronous notification query/subscription +#[derive(Copy, Clone, Debug, Default)] +pub struct VirtioScsiCtrlAnReq { + pub ctrltype: u32, + pub lun: [u8; 8], + pub event_requested: u32, +} + +impl ByteCode for VirtioScsiCtrlAnReq {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct VirtioScsiCtrlAnResp { + pub evnet_actual: u32, + pub response: u8, +} + +impl ByteCode for VirtioScsiCtrlAnResp {} + +/// T: request; U: response +pub struct VirtioScsiRequest { + queue: Arc>, + desc_index: u16, + /// Read or Write data, HVA, except resp + iovec: Vec, + data_len: u32, + _cdb_size: u32, + _sense_size: u32, + mode: ScsiXferMode, + _interrupt_cb: Option>, + _driver_features: u64, + /// resp GPA + resp_addr: GuestAddress, + req: T, + resp: U, +} + +/// T: request; U:response +impl VirtioScsiRequest { + fn new( + mem_space: &Arc, + queue: Arc>, + interrupt_cb: Option>, + driver_features: u64, + elem: &Element, + ) -> Result { + let out_iov_elem = elem.out_iovec.get(0).unwrap(); + if out_iov_elem.len < size_of::() as u32 { + bail!( + "Invalid virtio scsi request: get length {}, expected length {}", + out_iov_elem.len, + size_of::(), + ); + } + + let scsi_req = mem_space + .read_object::(out_iov_elem.addr) + .with_context(|| VirtioError::ReadObjectErr("the scsi request", out_iov_elem.addr.0))?; + + let in_iov_elem = elem.in_iovec.get(0).unwrap(); + if in_iov_elem.len < size_of::() as u32 { + bail!( + "Invalid virtio scsi response: get length {}, expected length {}", + in_iov_elem.len, + size_of::() + ); + } + let scsi_resp = mem_space + .read_object::(in_iov_elem.addr) + .with_context(|| VirtioError::ReadObjectErr("the scsi response", in_iov_elem.addr.0))?; + + let mut request = VirtioScsiRequest { + queue, + desc_index: elem.index, + iovec: Vec::with_capacity(elem.desc_num as usize), + data_len: 0, + _cdb_size: VIRTIO_SCSI_CDB_DEFAULT_SIZE as u32, + _sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE as u32, + mode: ScsiXferMode::ScsiXferNone, + _interrupt_cb: interrupt_cb, + _driver_features: driver_features, + resp_addr: in_iov_elem.addr, + req: scsi_req, + resp: scsi_resp, + }; + + let mut out_len: u32 = 0; + let mut skip_out_size: u32 = size_of::() as u32; + for (_index, elem_iov) in elem.out_iovec.iter().enumerate() { + if skip_out_size >= elem_iov.len { + skip_out_size -= elem_iov.len; + } else if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { + let len = elem_iov.len - skip_out_size; + let iov = Iovec { + iov_base: hva + skip_out_size as u64, + iov_len: u64::from(len), + }; + out_len += len; + skip_out_size = 0; + request.iovec.push(iov); + } + } + + let mut in_len: u32 = 0; + let mut skip_in_size: u32 = size_of::() as u32; + for (_index, elem_iov) in elem.in_iovec.iter().enumerate() { + if skip_in_size >= elem_iov.len { + skip_in_size -= elem_iov.len; + } else { + if out_len > 0 { + bail!("Wrong scsi request!"); + } + if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { + let len = elem_iov.len - skip_in_size; + let iov = Iovec { + iov_base: hva + skip_in_size as u64, + iov_len: u64::from(len), + }; + in_len += len; + skip_in_size = 0; + request.iovec.push(iov); + } + } + } + + if out_len > 0 { + request.mode = ScsiXferMode::ScsiXferToDev; + request.data_len = out_len; + } else if in_len > 0 { + request.mode = ScsiXferMode::ScsiXferFromDev; + request.data_len = in_len; + } + + Ok(request) + } + + fn complete(&self, mem_space: &Arc) -> bool { + if let Err(ref e) = mem_space.write_object(&self.resp, self.resp_addr) { + error!("Failed to write the scsi response {:?}", e); + return false; + } + let mut queue_lock = self.queue.lock().unwrap(); + if let Err(ref e) = queue_lock.vring.add_used( + mem_space, + self.desc_index, + self.data_len + (size_of::() as u32), + ) { + error!( + "Failed to add used ring(scsi completion), index {}, len {} {:?}", + self.desc_index, self.data_len, e + ); + return false; + } + true + } +} + +pub struct ScsiCtrlHandler { + /// The ctrl virtqueue + queue: Arc>, + /// EventFd for the ctrl virtqueue + queue_evt: EventFd, + /// The address space to which the scsi HBA belongs. + mem_space: Arc, + /// The interrupt callback function + interrupt_cb: Arc, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, +} + +impl ScsiCtrlHandler { + fn handle_ctrl(&mut self) -> Result<()> { + let mut queue = self.queue.lock().unwrap(); + + while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + drop(queue); + let ctrl_desc = elem.out_iovec.get(0).unwrap(); + let ctrl_type = self + .mem_space + .read_object::(ctrl_desc.addr) + .with_context(|| "Failed to get control queue descriptor")?; + match ctrl_type { + VIRTIO_SCSI_T_TMF => { + match VirtioScsiRequest::::new( + &self.mem_space, + self.queue.clone(), + Some(self.interrupt_cb.clone()), + self.driver_features, + &elem, + ) { + Ok(mut tmf) => { + info!("incomplete tmf req, subtype {}!", tmf.req.subtype); + + tmf.resp.response = VIRTIO_SCSI_S_OK; + tmf.complete(&self.mem_space); + } + Err(ref e) => { + let mut queue = self.queue.lock().unwrap(); + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .with_context(|| "Failed to add used ring")?; + drop(queue); + + error!("Failed to create VIRTIO_SCSI_T_TMF request, {:?}", e); + } + } + } + + VIRTIO_SCSI_T_AN_QUERY | VIRTIO_SCSI_T_AN_SUBSCRIBE => { + match VirtioScsiRequest::::new( + &self.mem_space, + self.queue.clone(), + Some(self.interrupt_cb.clone()), + self.driver_features, + &elem, + ) { + Ok(mut an) => { + info!("incomplete An req!"); + an.resp.evnet_actual = 0; + an.resp.response = VIRTIO_SCSI_S_OK; + an.complete(&self.mem_space); + } + Err(ref e) => { + let mut queue = self.queue.lock().unwrap(); + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .with_context(|| "Failed to add used ring")?; + drop(queue); + + error!("Failed to create scsi ctrl an req, {:?}", e) + } + } + } + _ => { + bail!("Control queue type doesn't support {}", ctrl_type); + } + } + queue = self.queue.lock().unwrap(); + } + + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { + VirtioError::InterruptTrigger("scsi ctrl", VirtioInterruptType::Vring) + })?; + Ok(()) + } +} + +impl EventNotifierHelper for ScsiCtrlHandler { + fn internal_notifiers(handler: Arc>) -> Vec { + let h_locked = handler.lock().unwrap(); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + h_clone + .lock() + .unwrap() + .handle_ctrl() + .unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {}.", e)); + None + }); + + let mut notifiers = Vec::new(); + let ctrl_fd = h_locked.queue_evt.as_raw_fd(); + notifiers.push(build_event_notifier(ctrl_fd, h)); + + notifiers + } +} -- Gitee From a7de63f353dbb6995d196f07d155ae3ec8e16529 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 20 Sep 2022 16:43:47 +0800 Subject: [PATCH 0241/2187] virtio-scsi: Implement the basic structure for Event queue All virtio-scsi devices share virtio-scsi controller's queues. The controller has 1 Controller queue, 1 Event queue, and N Command queues. Implement the basic structure for Event queue. The Event queue is used for reporting information (events) from the host on logical units attached to virtio-scsi. These events include transport events (e.g device resets rescans, hot-plug, hot-unplug, etc.) asynchronous nofifications, and logical unit number(LUN) paramter changes. We will not realize event queue completely until supporting hot-plug/hot-unplug. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 57 ++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 1481802ac..2f75411be 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -245,7 +245,7 @@ impl VirtioDevice for ScsiCntlr { let ctrl_handler = ScsiCtrlHandler { queue: ctrl_queue, queue_evt: ctrl_queue_evt, - mem_space, + mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, }; @@ -254,6 +254,20 @@ impl VirtioDevice for ScsiCntlr { self.config.iothread.as_ref(), )?; + let event_queue = queues[1].clone(); + let event_queue_evt = queue_evts.remove(0); + let event_handler = ScsiEventHandler { + _queue: event_queue, + queue_evt: event_queue_evt, + _mem_space: mem_space, + _interrupt_cb: interrupt_cb.clone(), + _driver_features: self.state.driver_features, + }; + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))), + self.config.iothread.as_ref(), + )?; + Ok(()) } @@ -563,3 +577,44 @@ impl EventNotifierHelper for ScsiCtrlHandler { notifiers } } + +pub struct ScsiEventHandler { + /// The Event virtqueue + _queue: Arc>, + /// EventFd for the Event virtqueue + queue_evt: EventFd, + /// The address space to which the scsi HBA belongs. + _mem_space: Arc, + /// The interrupt callback function + _interrupt_cb: Arc, + /// Bit mask of features negotiated by the backend and the frontend. + _driver_features: u64, +} + +impl EventNotifierHelper for ScsiEventHandler { + fn internal_notifiers(handler: Arc>) -> Vec { + let h_locked = handler.lock().unwrap(); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + h_clone + .lock() + .unwrap() + .handle_event() + .unwrap_or_else(|e| error!("Failed to handle event queue, err is {}", e)); + None + }); + + let mut notifiers = Vec::new(); + let event_fd = h_locked.queue_evt.as_raw_fd(); + notifiers.push(build_event_notifier(event_fd, h)); + + notifiers + } +} + +impl ScsiEventHandler { + fn handle_event(&mut self) -> Result<()> { + Ok(()) + } +} -- Gitee From e0b54c7b0465f2e2b275edc20ad221b8f2435ac1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 21 Sep 2022 15:01:04 +0800 Subject: [PATCH 0242/2187] virtio-scsi: Implement the basic structure for Command queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All virtio-scsi devices share virtio-scsi controller's queues. The controller has 1 Controller queue, 1 Event queue, and N Command queues. Implement the basic structure for Command queue. The command VirtQueues are used for typical SCSI transport commands (e.g.  reading and writing to and from files). The scsi command processing process is implemented in the next patch. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 157 ++++++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 5 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 2f75411be..87ee244fc 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -259,7 +259,7 @@ impl VirtioDevice for ScsiCntlr { let event_handler = ScsiEventHandler { _queue: event_queue, queue_evt: event_queue_evt, - _mem_space: mem_space, + _mem_space: mem_space.clone(), _interrupt_cb: interrupt_cb.clone(), _driver_features: self.state.driver_features, }; @@ -268,6 +268,27 @@ impl VirtioDevice for ScsiCntlr { self.config.iothread.as_ref(), )?; + let queues_num = queues.len(); + for cmd_queue in queues.iter().take(queues_num).skip(2) { + if let Some(bus) = &self.bus { + let cmd_handler = ScsiCmdHandler { + _scsibus: bus.clone(), + queue: cmd_queue.clone(), + queue_evt: queue_evts.remove(0), + mem_space: mem_space.clone(), + interrupt_cb: interrupt_cb.clone(), + driver_features: self.state.driver_features, + }; + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))), + self.config.iothread.as_ref(), + )?; + } else { + bail!("Scsi controller has no bus!"); + } + } + Ok(()) } @@ -326,11 +347,60 @@ pub struct VirtioScsiCtrlAnResp { impl ByteCode for VirtioScsiCtrlAnResp {} -/// T: request; U: response +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +pub struct VirtioScsiCmdReq { + /// Logical Unit Number + lun: [u8; 8], + /// Command identifier + tag: u64, + /// Task attribute + task_attr: u8, + /// SAM command priority field + prio: u8, + crn: u8, + cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], +} + +impl ByteCode for VirtioScsiCmdReq {} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct VirtioScsiCmdResp { + /// Sense data length + sense_len: u32, + /// Resudual bytes in data buffer + resid: u32, + /// Status qualifier + status_qualifier: u16, + /// Command completion status + status: u8, + /// Respinse value + response: u8, + /// sense buffer data + sense: [u8; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], +} + +impl Default for VirtioScsiCmdResp { + fn default() -> Self { + VirtioScsiCmdResp { + sense_len: 0, + resid: 0, + status_qualifier: 0, + status: 0, + response: 0, + sense: [0; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], + } + } +} + +impl ByteCode for VirtioScsiCmdResp {} + +/// T: request; U: response. pub struct VirtioScsiRequest { queue: Arc>, desc_index: u16, - /// Read or Write data, HVA, except resp + /// Read or Write data, HVA, except resp. iovec: Vec, data_len: u32, _cdb_size: u32, @@ -338,13 +408,13 @@ pub struct VirtioScsiRequest { mode: ScsiXferMode, _interrupt_cb: Option>, _driver_features: u64, - /// resp GPA + /// resp GPA. resp_addr: GuestAddress, req: T, resp: U, } -/// T: request; U:response +/// T: request; U:response. impl VirtioScsiRequest { fn new( mem_space: &Arc, @@ -618,3 +688,80 @@ impl ScsiEventHandler { Ok(()) } } + +pub struct ScsiCmdHandler { + /// The scsi controller + _scsibus: Arc>, + /// The Cmd virtqueue + queue: Arc>, + /// EventFd for the Cmd virtqueue + queue_evt: EventFd, + /// The address space to which the scsi HBA belongs. + mem_space: Arc, + /// The interrupt callback function + interrupt_cb: Arc, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, +} + +impl EventNotifierHelper for ScsiCmdHandler { + fn internal_notifiers(handler: Arc>) -> Vec { + let h_locked = handler.lock().unwrap(); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + h_clone + .lock() + .unwrap() + .handle_cmd() + .unwrap_or_else(|e| error!("Failed to handle cmd queue, err is {}", e)); + + None + }); + + let mut notifiers = Vec::new(); + let event_fd = h_locked.queue_evt.as_raw_fd(); + notifiers.push(build_event_notifier(event_fd, h)); + + notifiers + } +} + +impl ScsiCmdHandler { + fn handle_cmd(&mut self) -> Result<()> { + let mut queue = self.queue.lock().unwrap(); + + while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + match VirtioScsiRequest::::new( + &self.mem_space, + self.queue.clone(), + Some(self.interrupt_cb.clone()), + self.driver_features, + &elem, + ) { + Ok(_cmd) => {} + Err(ref e) => { + // If it fails, also need to free descriptor table entry + let mut queue = self.queue.lock().unwrap(); + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .chain_err(|| "Failed to add used ring")?; + drop(queue); + + error!( + "Failed to create cmd request, {}", + error_chain::ChainedError::display_chain(e) + ); + } + } + + queue = self.queue.lock().unwrap(); + } + + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) + .chain_err(|| ErrorKind::InterruptTrigger("scsi cmd", VirtioInterruptType::Vring))?; + + Ok(()) + } +} -- Gitee From 949f65ff57b6ff44288c312e11d128c9661c7f2e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 23 Sep 2022 14:25:03 +0800 Subject: [PATCH 0243/2187] virtio-scsi: realize scsi command queue Realize scsi command queue. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 605 +++++++++++++++++++++++++++++++++- virtio/src/scsi/controller.rs | 183 +++++++--- 2 files changed, 740 insertions(+), 48 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 1d5b5078e..d559ec255 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -11,12 +11,243 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; +use std::fs::File; +use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex, Weak}; -use anyhow::Result; +use anyhow::{bail, Context, Result}; -use crate::ScsiCntlr::ScsiCntlr; +use crate::ScsiCntlr::{ + ScsiCntlr, ScsiCompleteCb, ScsiXferMode, VirtioScsiCmdReq, VirtioScsiCmdResp, + VirtioScsiRequest, VIRTIO_SCSI_CDB_DEFAULT_SIZE, VIRTIO_SCSI_S_OK, +}; use crate::ScsiDisk::ScsiDevice; +use byteorder::{BigEndian, ByteOrder}; +use log::{debug, info}; +use util::aio::{Aio, AioCb, IoCmd, Iovec}; + +/// Scsi Operation code. +pub const TEST_UNIT_READY: u8 = 0x00; +pub const REWIND: u8 = 0x01; +pub const REQUEST_SENSE: u8 = 0x03; +pub const FORMAT_UNIT: u8 = 0x04; +pub const READ_BLOCK_LIMITS: u8 = 0x05; +pub const INITIALIZE_ELEMENT_STATUS: u8 = 0x07; +pub const REASSIGN_BLOCKS: u8 = 0x07; +pub const READ_6: u8 = 0x08; +pub const WRITE_6: u8 = 0x0a; +pub const SET_CAPACITY: u8 = 0x0b; +pub const READ_REVERSE: u8 = 0x0f; +pub const WRITE_FILEMARKS: u8 = 0x10; +pub const SPACE: u8 = 0x11; +pub const INQUIRY: u8 = 0x12; +pub const RECOVER_BUFFERED_DATA: u8 = 0x14; +pub const MODE_SELECT: u8 = 0x15; +pub const RESERVE: u8 = 0x16; +pub const RELEASE: u8 = 0x17; +pub const COPY: u8 = 0x18; +pub const ERASE: u8 = 0x19; +pub const MODE_SENSE: u8 = 0x1a; +pub const LOAD_UNLOAD: u8 = 0x1b; +pub const SCAN: u8 = 0x1b; +pub const START_STOP: u8 = 0x1b; +pub const RECEIVE_DIAGNOSTIC: u8 = 0x1c; +pub const SEND_DIAGNOSTIC: u8 = 0x1d; +pub const ALLOW_MEDIUM_REMOVAL: u8 = 0x1e; +pub const SET_WINDOW: u8 = 0x24; +pub const READ_CAPACITY_10: u8 = 0x25; +pub const GET_WINDOW: u8 = 0x25; +pub const READ_10: u8 = 0x28; +pub const WRITE_10: u8 = 0x2a; +pub const SEND: u8 = 0x2a; +pub const SEEK_10: u8 = 0x2b; +pub const LOCATE_10: u8 = 0x2b; +pub const POSITION_TO_ELEMENT: u8 = 0x2b; +pub const WRITE_VERIFY_10: u8 = 0x2e; +pub const VERIFY_10: u8 = 0x2f; +pub const SEARCH_HIGH: u8 = 0x30; +pub const SEARCH_EQUAL: u8 = 0x31; +pub const OBJECT_POSITION: u8 = 0x31; +pub const SEARCH_LOW: u8 = 0x32; +pub const SET_LIMITS: u8 = 0x33; +pub const PRE_FETCH: u8 = 0x34; +pub const READ_POSITION: u8 = 0x34; +pub const GET_DATA_BUFFER_STATUS: u8 = 0x34; +pub const SYNCHRONIZE_CACHE: u8 = 0x35; +pub const LOCK_UNLOCK_CACHE: u8 = 0x36; +pub const INITIALIZE_ELEMENT_STATUS_WITH_RANGE: u8 = 0x37; +pub const READ_DEFECT_DATA: u8 = 0x37; +pub const MEDIUM_SCAN: u8 = 0x38; +pub const COMPARE: u8 = 0x39; +pub const COPY_VERIFY: u8 = 0x3a; +pub const WRITE_BUFFER: u8 = 0x3b; +pub const READ_BUFFER: u8 = 0x3c; +pub const UPDATE_BLOCK: u8 = 0x3d; +pub const READ_LONG_10: u8 = 0x3e; +pub const WRITE_LONG_10: u8 = 0x3f; +pub const CHANGE_DEFINITION: u8 = 0x40; +pub const WRITE_SAME_10: u8 = 0x41; +pub const UNMAP: u8 = 0x42; +pub const READ_TOC: u8 = 0x43; +pub const REPORT_DENSITY_SUPPORT: u8 = 0x44; +pub const GET_CONFIGURATION: u8 = 0x46; +pub const SANITIZE: u8 = 0x48; +pub const GET_EVENT_STATUS_NOTIFICATION: u8 = 0x4a; +pub const LOG_SELECT: u8 = 0x4c; +pub const LOG_SENSE: u8 = 0x4d; +pub const READ_DISC_INFORMATION: u8 = 0x51; +pub const RESERVE_TRACK: u8 = 0x53; +pub const MODE_SELECT_10: u8 = 0x55; +pub const RESERVE_10: u8 = 0x56; +pub const RELEASE_10: u8 = 0x57; +pub const MODE_SENSE_10: u8 = 0x5a; +pub const SEND_CUE_SHEET: u8 = 0x5d; +pub const PERSISTENT_RESERVE_IN: u8 = 0x5e; +pub const PERSISTENT_RESERVE_OUT: u8 = 0x5f; +pub const VARLENGTH_CDB: u8 = 0x7f; +pub const WRITE_FILEMARKS_16: u8 = 0x80; +pub const READ_REVERSE_16: u8 = 0x81; +pub const ALLOW_OVERWRITE: u8 = 0x82; +pub const EXTENDED_COPY: u8 = 0x83; +pub const ATA_PASSTHROUGH_16: u8 = 0x85; +pub const ACCESS_CONTROL_IN: u8 = 0x86; +pub const ACCESS_CONTROL_OUT: u8 = 0x87; +pub const READ_16: u8 = 0x88; +pub const COMPARE_AND_WRITE: u8 = 0x89; +pub const WRITE_16: u8 = 0x8a; +pub const WRITE_VERIFY_16: u8 = 0x8e; +pub const VERIFY_16: u8 = 0x8f; +pub const PRE_FETCH_16: u8 = 0x90; +pub const SPACE_16: u8 = 0x91; +pub const SYNCHRONIZE_CACHE_16: u8 = 0x91; +pub const LOCATE_16: u8 = 0x92; +pub const WRITE_SAME_16: u8 = 0x93; +pub const ERASE_16: u8 = 0x93; +pub const SERVICE_ACTION_IN_16: u8 = 0x9e; +pub const WRITE_LONG_16: u8 = 0x9f; +pub const REPORT_LUNS: u8 = 0xa0; +pub const ATA_PASSTHROUGH_12: u8 = 0xa1; +pub const MAINTENANCE_IN: u8 = 0xa3; +pub const MAINTENANCE_OUT: u8 = 0xa4; +pub const MOVE_MEDIUM: u8 = 0xa5; +pub const EXCHANGE_MEDIUM: u8 = 0xa6; +pub const SET_READ_AHEAD: u8 = 0xa7; +pub const READ_12: u8 = 0xa8; +pub const WRITE_12: u8 = 0xaa; +pub const SERVICE_ACTION_IN_12: u8 = 0xab; +pub const ERASE_12: u8 = 0xac; +pub const READ_DVD_STRUCTURE: u8 = 0xad; +pub const WRITE_VERIFY_12: u8 = 0xae; +pub const VERIFY_12: u8 = 0xaf; +pub const SEARCH_HIGH_12: u8 = 0xb0; +pub const SEARCH_EQUAL_12: u8 = 0xb1; +pub const SEARCH_LOW_12: u8 = 0xb2; +pub const READ_ELEMENT_STATUS: u8 = 0xb8; +pub const SEND_VOLUME_TAG: u8 = 0xb6; +pub const READ_DEFECT_DATA_12: u8 = 0xb7; +pub const SET_CD_SPEED: u8 = 0xbb; +pub const MECHANISM_STATUS: u8 = 0xbd; +pub const READ_CD: u8 = 0xbe; +pub const SEND_DVD_STRUCTURE: u8 = 0xbf; + +/// SAM Status codes. +pub const GOOD: u8 = 0x00; +pub const CHECK_CONDITION: u8 = 0x02; +pub const CONDITION_GOOD: u8 = 0x04; +pub const BUSY: u8 = 0x08; +pub const INTERMEDIATE_GOOD: u8 = 0x10; +pub const INTERMEDIATE_C_GOOD: u8 = 0x14; +pub const RESERVATION_CONFLICT: u8 = 0x18; +pub const COMMAND_TERMINATED: u8 = 0x22; +pub const TASK_SET_FULL: u8 = 0x28; +pub const ACA_ACTIVE: u8 = 0x30; +pub const TASK_ABORTED: u8 = 0x40; + +pub const STATUS_MASK: u8 = 0x3e; + +pub const SCSI_CMD_BUF_SIZE: usize = 16; +pub const SCSI_SENSE_BUF_SIZE: usize = 252; + +/// SERVICE ACTION IN subcodes. +pub const SAI_READ_CAPACITY_16: u8 = 0x10; + +/// Used to compute the number of sectors. +const SECTOR_SHIFT: u8 = 9; +/// Size of a sector of the block device. +const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; + +/// Sense Keys. +pub const NO_SENSE: u8 = 0x00; +pub const RECOVERED_ERROR: u8 = 0x01; +pub const NOT_READY: u8 = 0x02; +pub const MEDIUM_ERROR: u8 = 0x03; +pub const HARDWARE_ERROR: u8 = 0x04; +pub const ILLEGAL_REQUEST: u8 = 0x05; +pub const UNIT_ATTENTION: u8 = 0x06; +pub const DATA_PROTECT: u8 = 0x07; +pub const BLANK_CHECK: u8 = 0x08; +pub const COPY_ABORTED: u8 = 0x0a; +pub const ABORTED_COMMAND: u8 = 0x0b; +pub const VOLUME_OVERFLOW: u8 = 0x0d; +pub const MISCOMPARE: u8 = 0x0e; + +macro_rules! scsisense { + ( $key:expr, $asc: expr, $ascq:expr) => { + ScsiSense { + key: $key, + asc: $asc, + ascq: $ascq, + } + }; +} + +/// Sense Code. +pub const SCSI_SENSE_NO_SENSE: ScsiSense = scsisense!(NO_SENSE, 0x00, 0x00); +pub const SCSI_SENSE_LUN_NOT_READY: ScsiSense = scsisense!(NOT_READY, 0x04, 0x03); +pub const SCSI_SENSE_NO_MEDIUM: ScsiSense = scsisense!(NOT_READY, 0x3a, 0x00); +pub const SCSI_SENSE_NOT_READY_REMOVAL_PREVENTED: ScsiSense = scsisense!(NOT_READY, 0x53, 0x02); +pub const SCSI_SENSE_TARGET_FAILURE: ScsiSense = scsisense!(HARDWARE_ERROR, 0x44, 0x00); +pub const SCSI_SENSE_INVALID_OPCODE: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x20, 0x00); +pub const SCSI_SENSE_LBA_OUT_OF_RANGE: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x21, 0x00); +pub const SCSI_SENSE_INVALID_FIELD: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x24, 0x00); +pub const SCSI_SENSE_INVALID_PARAM: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x26, 0x00); +pub const SCSI_SENSE_INVALID_PARAM_VALUE: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x26, 0x01); +pub const SCSI_SENSE_INVALID_PARAM_LEN: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x1a, 0x00); +pub const SCSI_SENSE_LUN_NOT_SUPPORTED: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x25, 0x00); +pub const SCSI_SENSE_SAVING_PARAMS_NOT_SUPPORTED: ScsiSense = + scsisense!(ILLEGAL_REQUEST, 0x39, 0x00); +pub const SCSI_SENSE_INCOMPATIBLE_FORMAT: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x30, 0x00); +pub const SCSI_SENSE_ILLEGAL_REQ_REMOVAL_PREVENTED: ScsiSense = + scsisense!(ILLEGAL_REQUEST, 0x53, 0x02); +pub const SCSI_SENSE_INVALID_TAG: ScsiSense = scsisense!(ILLEGAL_REQUEST, 0x4b, 0x01); +pub const SCSI_SENSE_IO_ERROR: ScsiSense = scsisense!(ABORTED_COMMAND, 0x00, 0x06); +pub const SCSI_SENSE_I_T_NEXUS_LOSS: ScsiSense = scsisense!(ABORTED_COMMAND, 0x29, 0x07); +pub const SCSI_SENSE_LUN_FAILURE: ScsiSense = scsisense!(ABORTED_COMMAND, 0x3e, 0x01); +pub const SCSI_SENSE_OVERLAPPED_COMMANDS: ScsiSense = scsisense!(ABORTED_COMMAND, 0x4e, 0x00); +pub const SCSI_SENSE_LUN_COMM_FAILURE: ScsiSense = scsisense!(ABORTED_COMMAND, 0x08, 0x00); +pub const SCSI_SENSE_LUN_NOT_RESPONDING: ScsiSense = scsisense!(ABORTED_COMMAND, 0x05, 0x00); +pub const SCSI_SENSE_COMMAND_TIMEOUT: ScsiSense = scsisense!(ABORTED_COMMAND, 0x2e, 0x02); +pub const SCSI_SENSE_COMMAND_ABORTED: ScsiSense = scsisense!(ABORTED_COMMAND, 0x2f, 0x02); +pub const SCSI_SENSE_READ_ERROR: ScsiSense = scsisense!(MEDIUM_ERROR, 0x11, 0x00); +pub const SCSI_SENSE_NOT_READY: ScsiSense = scsisense!(NOT_READY, 0x04, 0x00); +pub const SCSI_SENSE_CAPACITY_CHANGED: ScsiSense = scsisense!(UNIT_ATTENTION, 0x2a, 0x09); +pub const SCSI_SENSE_RESET: ScsiSense = scsisense!(UNIT_ATTENTION, 0x29, 0x00); +pub const SCSI_SENSE_SCSI_BUS_RESET: ScsiSense = scsisense!(UNIT_ATTENTION, 0x29, 0x02); +pub const SCSI_SENSE_UNIT_ATTENTION_NO_MEDIUM: ScsiSense = scsisense!(UNIT_ATTENTION, 0x3a, 0x00); +pub const SCSI_SENSE_MEDIUM_CHANGED: ScsiSense = scsisense!(UNIT_ATTENTION, 0x28, 0x00); +pub const SCSI_SENSE_REPORTED_LUNS_CHANGED: ScsiSense = scsisense!(UNIT_ATTENTION, 0x3f, 0x0e); +pub const SCSI_SENSE_DEVICE_INTERNAL_RESET: ScsiSense = scsisense!(UNIT_ATTENTION, 0x29, 0x04); +pub const SCSI_SENSE_WRITE_PROTECTED: ScsiSense = scsisense!(DATA_PROTECT, 0x27, 0x00); +pub const SCSI_SENSE_SPACE_ALLOC_FAILED: ScsiSense = scsisense!(DATA_PROTECT, 0x27, 0x07); + +#[derive(Default)] +pub struct ScsiSense { + key: u8, + asc: u8, + ascq: u8, +} + +pub const SCSI_SENSE_LEN: u32 = 18; pub struct ScsiBus { /// Bus name. @@ -35,6 +266,48 @@ impl ScsiBus { parent_cntlr, } } + + /// Get device by the target number and the lun number. + pub fn get_device(&self, target: u8, lun: u16) -> Option>> { + if let Some(dev) = self.devices.get(&(target, lun)) { + return Some((*dev).clone()); + } + debug!("Can't find scsi device target {} lun {}", target, lun); + None + } + + pub fn scsi_bus_parse_req_cdb( + &self, + cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], + ) -> Option { + let buf: [u8; SCSI_CMD_BUF_SIZE] = (cdb[0..SCSI_CMD_BUF_SIZE]) + .try_into() + .expect("incorrect length"); + let command = cdb[0]; + let len = scsi_cdb_length(&cdb); + if len < 0 { + return None; + } + + let xfer = scsi_cdb_xfer(&cdb); + if xfer < 0 { + return None; + } + + let lba = scsi_cdb_lba(&cdb); + if lba < 0 { + return None; + } + + Some(ScsiCommand { + buf, + command, + len: len as u32, + xfer: xfer as u32, + lba: lba as u64, + mode: scsi_cdb_xfer_mode(&cdb), + }) + } } pub fn create_scsi_bus(bus_name: &str, scsi_cntlr: &Arc>) -> Result<()> { @@ -43,3 +316,331 @@ pub fn create_scsi_bus(bus_name: &str, scsi_cntlr: &Arc>) -> Re locked_scsi_cntlr.bus = Some(Arc::new(Mutex::new(bus))); Ok(()) } + +#[derive(Clone)] +pub struct ScsiCommand { + /// The Command Descriptor Block(CDB). + pub buf: [u8; SCSI_CMD_BUF_SIZE], + /// Scsi Operation Code. + pub command: u8, + /// Length of CDB. + pub len: u32, + /// Transfer length. + pub xfer: u32, + /// Logical Block Address. + pub lba: u64, + /// Transfer direction. + mode: ScsiXferMode, +} + +#[derive(Clone)] +pub struct ScsiRequest { + cmd: ScsiCommand, + _sense: [u8; SCSI_SENSE_BUF_SIZE], + _sense_size: u32, + _resid: u32, + pub opstype: u32, + pub virtioscsireq: Arc>>, + _dev: Arc>, +} + +impl ScsiRequest { + pub fn new( + req: Arc>>, + scsibus: Arc>, + scsidevice: Arc>, + ) -> Result { + if let Some(cmd) = scsibus + .lock() + .unwrap() + .scsi_bus_parse_req_cdb(req.lock().unwrap().req.cdb) + { + let ops = cmd.command; + let opstype = scsi_operation_type(ops); + let _resid = cmd.xfer; + + Ok(ScsiRequest { + cmd, + _sense: [0; SCSI_SENSE_BUF_SIZE], + _sense_size: 0, + _resid, + opstype, + virtioscsireq: req.clone(), + _dev: scsidevice, + }) + } else { + bail!("Error CDB!"); + } + } + + pub fn execute( + &self, + aio: &mut Box>, + disk: &File, + direct: bool, + last_aio: bool, + iocompletecb: ScsiCompleteCb, + ) -> Result { + let mut aiocb = AioCb { + last_aio, + file_fd: disk.as_raw_fd(), + opcode: IoCmd::Noop, + iovec: Vec::new(), + offset: (self.cmd.lba << 9) as usize, + process: true, + iocb: None, + iocompletecb, + }; + + for iov in self.virtioscsireq.lock().unwrap().iovec.iter() { + let iovec = Iovec { + iov_base: iov.iov_base, + iov_len: iov.iov_len, + }; + aiocb.iovec.push(iovec); + } + + match self.cmd.mode { + ScsiXferMode::ScsiXferFromDev => { + aiocb.opcode = IoCmd::Preadv; + if direct { + (*aio) + .as_mut() + .rw_aio(aiocb, SECTOR_SIZE) + .with_context(|| { + "Failed to process scsi request for reading asynchronously" + })?; + } else { + (*aio).as_mut().rw_sync(aiocb).with_context(|| { + "Failed to process scsi request for reading synchronously" + })?; + } + } + ScsiXferMode::ScsiXferToDev => { + aiocb.opcode = IoCmd::Pwritev; + if direct { + (*aio) + .as_mut() + .rw_aio(aiocb, SECTOR_SIZE) + .with_context(|| { + "Failed to process block request for writing asynchronously" + })?; + } else { + (*aio).as_mut().rw_sync(aiocb).with_context(|| { + "Failed to process block request for writing synchronously" + })?; + } + } + _ => { + info!("xfer none"); + } + } + Ok(0) + } + + pub fn emulate_execute(&self, iocompletecb: ScsiCompleteCb) -> Result { + debug!("scsi command is {:#x}", self.cmd.command); + match self.cmd.command { + _ => { + info!( + "emulation scsi command {:#x} is not supported", + self.cmd.command + ); + self.set_scsi_sense(SCSI_SENSE_INVALID_OPCODE); + + let mut req = self.virtioscsireq.lock().unwrap(); + req.resp.response = VIRTIO_SCSI_S_OK; + req.resp.status = CHECK_CONDITION; + req.resp.resid = 0; + + req.complete(&iocompletecb.mem_space); + } + } + + Ok(0) + } + + fn set_scsi_sense(&self, sense: ScsiSense) { + let mut req = self.virtioscsireq.lock().unwrap(); + req.resp.sense[0] = 0x70; // Response code: current errors(0x70). + req.resp.sense[2] = sense.key; + req.resp.sense[7] = 10; // Additional sense length: sense len - 8. + req.resp.sense[12] = sense.asc; + req.resp.sense[13] = sense.ascq; + req.resp.sense_len = SCSI_SENSE_LEN; + } +} + +pub const EMULATE_SCSI_OPS: u32 = 0; +pub const DMA_SCSI_OPS: u32 = 1; + +fn scsi_operation_type(op: u8) -> u32 { + match op { + READ_6 | READ_10 | READ_12 | READ_16 | WRITE_6 | WRITE_10 | WRITE_12 | WRITE_16 + | WRITE_VERIFY_10 | WRITE_VERIFY_12 | WRITE_VERIFY_16 => DMA_SCSI_OPS, + _ => EMULATE_SCSI_OPS, + } +} + +// lun: [u8, 8] +// | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | +// | 1 | target | lun | 0 | +pub fn virtio_scsi_get_lun(lun: [u8; 8]) -> u16 { + (((lun[2] as u16) << 8) | (lun[3] as u16)) & 0x3FFF +} + +fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { + match cdb[0] >> 5 { + 0 => 6, + 1 | 2 => 10, + 4 => 16, + 5 => 12, + _ => -1, + } +} + +fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { + let mut xfer = match cdb[0] >> 5 { + 0 => cdb[4] as i32, + 1 | 2 => BigEndian::read_u16(&cdb[7..]) as i32, + 4 => BigEndian::read_u32(&cdb[10..]) as i32, + 5 => BigEndian::read_u32(&cdb[6..]) as i32, + _ => -1, + }; + + match cdb[0] { + TEST_UNIT_READY | REWIND | START_STOP | SET_CAPACITY | WRITE_FILEMARKS + | WRITE_FILEMARKS_16 | SPACE | RESERVE | RELEASE | ERASE | ALLOW_MEDIUM_REMOVAL + | SEEK_10 | SYNCHRONIZE_CACHE | SYNCHRONIZE_CACHE_16 | LOCATE_16 | LOCK_UNLOCK_CACHE + | SET_CD_SPEED | SET_LIMITS | WRITE_LONG_10 | UPDATE_BLOCK | RESERVE_TRACK + | SET_READ_AHEAD | PRE_FETCH | PRE_FETCH_16 | ALLOW_OVERWRITE => { + xfer = 0; + } + VERIFY_10 | VERIFY_12 | VERIFY_16 => { + if cdb[1] & 2 == 0 { + xfer = 0; + } else if cdb[1] & 4 != 0 { + xfer = 1; + } + // 512 : blocksize + xfer *= 512; + } + WRITE_SAME_10 | WRITE_SAME_16 => { + if cdb[1] & 1 == 0 { + xfer = 0; + } else { + xfer = 512; + } + } + READ_CAPACITY_10 => { + xfer = 8; + } + READ_BLOCK_LIMITS => { + xfer = 6; + } + SEND_VOLUME_TAG => { + xfer = i32::from(cdb[9]) | i32::from(cdb[8]) << 8; + } + WRITE_6 => { + if xfer == 0 { + xfer = 256 * 512; + } + } + WRITE_10 | WRITE_VERIFY_10 | WRITE_12 | WRITE_VERIFY_12 | WRITE_16 | WRITE_VERIFY_16 => { + xfer *= 512; + } + READ_6 | READ_REVERSE => { + if xfer == 0 { + xfer = 256 * 512; + } + } + READ_10 | READ_12 | READ_16 => { + xfer *= 512; + } + FORMAT_UNIT => { + xfer = match cdb[1] & 16 { + 0 => 0, + _ => match cdb[1] & 32 { + 0 => 4, + _ => 8, + }, + }; + } + INQUIRY | RECEIVE_DIAGNOSTIC | SEND_DIAGNOSTIC => { + xfer = i32::from(cdb[4]) | i32::from(cdb[3]) << 8; + } + READ_CD | READ_BUFFER | WRITE_BUFFER | SEND_CUE_SHEET => { + xfer = i32::from(cdb[8]) | i32::from(cdb[7]) << 8 | (u32::from(cdb[6]) << 16) as i32; + } + PERSISTENT_RESERVE_OUT => { + xfer = BigEndian::read_i32(&cdb[5..]); + } + ERASE_12 | MECHANISM_STATUS | READ_DVD_STRUCTURE | SEND_DVD_STRUCTURE | MAINTENANCE_OUT + | MAINTENANCE_IN => {} + ATA_PASSTHROUGH_12 => {} + ATA_PASSTHROUGH_16 => {} + _ => {} + } + xfer +} + +fn scsi_cdb_lba(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i64 { + match cdb[0] >> 5 { + 0 => (BigEndian::read_u32(&cdb[0..]) & 0x1fffff) as i64, + 1 | 2 | 5 => BigEndian::read_u32(&cdb[2..]) as i64, + 4 => BigEndian::read_u64(&cdb[2..]) as i64, + _ => -1, + } +} + +fn scsi_cdb_xfer_mode(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> ScsiXferMode { + match cdb[0] { + WRITE_6 + | WRITE_10 + | WRITE_VERIFY_10 + | WRITE_12 + | WRITE_VERIFY_12 + | WRITE_16 + | WRITE_VERIFY_16 + | VERIFY_10 + | VERIFY_12 + | VERIFY_16 + | COPY + | COPY_VERIFY + | COMPARE + | CHANGE_DEFINITION + | LOG_SELECT + | MODE_SELECT + | MODE_SELECT_10 + | SEND_DIAGNOSTIC + | WRITE_BUFFER + | FORMAT_UNIT + | REASSIGN_BLOCKS + | SEARCH_EQUAL + | SEARCH_HIGH + | SEARCH_LOW + | UPDATE_BLOCK + | WRITE_LONG_10 + | WRITE_SAME_10 + | WRITE_SAME_16 + | UNMAP + | SEARCH_HIGH_12 + | SEARCH_EQUAL_12 + | SEARCH_LOW_12 + | MEDIUM_SCAN + | SEND_VOLUME_TAG + | SEND_CUE_SHEET + | SEND_DVD_STRUCTURE + | PERSISTENT_RESERVE_OUT + | MAINTENANCE_OUT + | SET_WINDOW + | SCAN => ScsiXferMode::ScsiXferToDev, + + ATA_PASSTHROUGH_12 | ATA_PASSTHROUGH_16 => match cdb[2] & 0x8 { + 0 => ScsiXferMode::ScsiXferToDev, + _ => ScsiXferMode::ScsiXferFromDev, + }, + + _ => ScsiXferMode::ScsiXferFromDev, + } +} diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 87ee244fc..5b98d7c17 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -22,7 +22,7 @@ use anyhow::{anyhow, bail, Context, Result}; use super::super::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_SCSI, }; -use crate::ScsiBus::ScsiBus; +use crate::ScsiBus::{virtio_scsi_get_lun, ScsiBus, ScsiRequest, EMULATE_SCSI_OPS, GOOD}; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use log::{error, info, warn}; @@ -30,7 +30,7 @@ use machine_manager::{ config::{ConfigCheck, ScsiCntlrConfig}, event_loop::EventLoop, }; -use util::aio::Iovec; +use util::aio::{Aio, AioCb, AioCompleteFunc, Iovec}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -271,8 +271,9 @@ impl VirtioDevice for ScsiCntlr { let queues_num = queues.len(); for cmd_queue in queues.iter().take(queues_num).skip(2) { if let Some(bus) = &self.bus { - let cmd_handler = ScsiCmdHandler { - _scsibus: bus.clone(), + let mut cmd_handler = ScsiCmdHandler { + aio: None, + scsibus: bus.clone(), queue: cmd_queue.clone(), queue_evt: queue_evts.remove(0), mem_space: mem_space.clone(), @@ -280,6 +281,8 @@ impl VirtioDevice for ScsiCntlr { driver_features: self.state.driver_features, }; + cmd_handler.aio = Some(cmd_handler.build_aio()?); + EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))), self.config.iothread.as_ref(), @@ -311,7 +314,7 @@ fn build_event_notifier(fd: RawFd, handler: Box) -> EventNotif ) } -/// Task Managememt Request +/// Task Managememt Request. #[derive(Copy, Clone, Debug, Default)] pub struct VirtioScsiCtrlTmfReq { pub ctrltype: u32, @@ -329,7 +332,7 @@ pub struct VirtioScsiCtrlTmfResp { impl ByteCode for VirtioScsiCtrlTmfResp {} -/// Asynchronous notification query/subscription +/// Asynchronous notification query/subscription. #[derive(Copy, Clone, Debug, Default)] pub struct VirtioScsiCtrlAnReq { pub ctrltype: u32, @@ -350,16 +353,16 @@ impl ByteCode for VirtioScsiCtrlAnResp {} #[repr(C, packed)] #[derive(Default, Clone, Copy)] pub struct VirtioScsiCmdReq { - /// Logical Unit Number + /// Logical Unit Number. lun: [u8; 8], - /// Command identifier + /// Command identifier. tag: u64, - /// Task attribute + /// Task attribute. task_attr: u8, - /// SAM command priority field + /// SAM command priority field. prio: u8, crn: u8, - cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], + pub cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], } impl ByteCode for VirtioScsiCmdReq {} @@ -367,18 +370,18 @@ impl ByteCode for VirtioScsiCmdReq {} #[repr(C)] #[derive(Clone, Copy)] pub struct VirtioScsiCmdResp { - /// Sense data length - sense_len: u32, - /// Resudual bytes in data buffer - resid: u32, - /// Status qualifier + /// Sense data length. + pub sense_len: u32, + /// Resudual bytes in data buffer. + pub resid: u32, + /// Status qualifier. status_qualifier: u16, - /// Command completion status - status: u8, - /// Respinse value - response: u8, - /// sense buffer data - sense: [u8; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], + /// Command completion status. + pub status: u8, + /// Respinse value. + pub response: u8, + /// Sense buffer data. + pub sense: [u8; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], } impl Default for VirtioScsiCmdResp { @@ -401,7 +404,7 @@ pub struct VirtioScsiRequest { queue: Arc>, desc_index: u16, /// Read or Write data, HVA, except resp. - iovec: Vec, + pub iovec: Vec, data_len: u32, _cdb_size: u32, _sense_size: u32, @@ -410,8 +413,8 @@ pub struct VirtioScsiRequest { _driver_features: u64, /// resp GPA. resp_addr: GuestAddress, - req: T, - resp: U, + pub req: T, + pub resp: U, } /// T: request; U:response. @@ -513,7 +516,7 @@ impl VirtioScsiRequest { Ok(request) } - fn complete(&self, mem_space: &Arc) -> bool { + pub fn complete(&self, mem_space: &Arc) -> bool { if let Err(ref e) = mem_space.write_object(&self.resp, self.resp_addr) { error!("Failed to write the scsi response {:?}", e); return false; @@ -535,13 +538,13 @@ impl VirtioScsiRequest { } pub struct ScsiCtrlHandler { - /// The ctrl virtqueue + /// The ctrl virtqueue. queue: Arc>, - /// EventFd for the ctrl virtqueue + /// EventFd for the ctrl virtqueue. queue_evt: EventFd, /// The address space to which the scsi HBA belongs. mem_space: Arc, - /// The interrupt callback function + /// The interrupt callback function. interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, @@ -649,13 +652,13 @@ impl EventNotifierHelper for ScsiCtrlHandler { } pub struct ScsiEventHandler { - /// The Event virtqueue + /// The Event virtqueue. _queue: Arc>, - /// EventFd for the Event virtqueue + /// EventFd for the Event virtqueue. queue_evt: EventFd, /// The address space to which the scsi HBA belongs. _mem_space: Arc, - /// The interrupt callback function + /// The interrupt callback function. _interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. _driver_features: u64, @@ -690,18 +693,20 @@ impl ScsiEventHandler { } pub struct ScsiCmdHandler { - /// The scsi controller - _scsibus: Arc>, - /// The Cmd virtqueue + /// The scsi controller. + scsibus: Arc>, + /// The Cmd virtqueue. queue: Arc>, - /// EventFd for the Cmd virtqueue + /// EventFd for the Cmd virtqueue. queue_evt: EventFd, /// The address space to which the scsi HBA belongs. mem_space: Arc, - /// The interrupt callback function + /// The interrupt callback function. interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, + /// Aio context. + aio: Option>>, } impl EventNotifierHelper for ScsiCmdHandler { @@ -739,29 +744,115 @@ impl ScsiCmdHandler { self.driver_features, &elem, ) { - Ok(_cmd) => {} + Ok(mut cmd) => { + drop(queue); + let lun: [u8; 8] = cmd.req.lun; + let scsibus = self.scsibus.lock().unwrap(); + + if let Some(scsidevice) = scsibus.get_device(lun[1], virtio_scsi_get_lun(lun)) { + drop(scsibus); + let cmd_req = Arc::new(Mutex::new(cmd)); + let req = if let Ok(scsireq) = + ScsiRequest::new(cmd_req, self.scsibus.clone(), scsidevice.clone()) + { + Some(scsireq) + } else { + let mut queue = self.queue.lock().unwrap(); + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .with_context(|| "Failed to add used ring")?; + drop(queue); + error!("Failed to create scsi request"); + + None + }; + + if let Some(scsireq) = req { + if scsireq.opstype == EMULATE_SCSI_OPS { + let scsicompletecb = ScsiCompleteCb::new( + self.mem_space.clone(), + Arc::new(Mutex::new(scsireq.clone())), + ); + scsireq.emulate_execute(scsicompletecb)?; + } else if let Some(disk_img) = + scsidevice.lock().unwrap().disk_image.as_mut() + { + let scsicompletecb = ScsiCompleteCb::new( + self.mem_space.clone(), + Arc::new(Mutex::new(scsireq.clone())), + ); + if let Some(ref mut aio) = self.aio { + scsireq.execute(aio, disk_img, false, true, scsicompletecb)?; + } + } + } + } else { + cmd.resp.response = VIRTIO_SCSI_S_BAD_TARGET; + cmd.complete(&self.mem_space); + error!( + "no such scsi device target {}, lun {}", + lun[1], + virtio_scsi_get_lun(lun) + ); + }; + } Err(ref e) => { - // If it fails, also need to free descriptor table entry + // If it fails, also need to free descriptor table entry. let mut queue = self.queue.lock().unwrap(); queue .vring .add_used(&self.mem_space, elem.index, 0) - .chain_err(|| "Failed to add used ring")?; + .with_context(|| "Failed to add used ring")?; drop(queue); - error!( - "Failed to create cmd request, {}", - error_chain::ChainedError::display_chain(e) - ); + error!("Failed to create cmd request, {:?}", e); } } queue = self.queue.lock().unwrap(); } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) - .chain_err(|| ErrorKind::InterruptTrigger("scsi cmd", VirtioInterruptType::Vring))?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "scsi cmd", + VirtioInterruptType::Vring + )) + })?; Ok(()) } + + fn build_aio(&self) -> Result>> { + let complete_fun = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { + let complete_cb = &aiocb.iocompletecb; + let request = &aiocb.iocompletecb.req.lock().unwrap(); + let mut virtio_scsi_req = request.virtioscsireq.lock().unwrap(); + + virtio_scsi_req.resp.response = if ret < 0 { + VIRTIO_SCSI_S_FAILURE + } else { + VIRTIO_SCSI_S_OK + }; + + virtio_scsi_req.resp.status = GOOD; + virtio_scsi_req.resp.resid = 0; + virtio_scsi_req.resp.sense_len = 0; + virtio_scsi_req.complete(&complete_cb.mem_space); + }) as AioCompleteFunc); + + Ok(Box::new(Aio::new(complete_fun, None)?)) + } +} + +#[derive(Clone)] +pub struct ScsiCompleteCb { + pub mem_space: Arc, + req: Arc>, +} + +impl ScsiCompleteCb { + fn new(mem_space: Arc, req: Arc>) -> Self { + ScsiCompleteCb { mem_space, req } + } } -- Gitee From f117e5e629920da7a9669b157d394701715bfaa9 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 9 Oct 2022 09:30:43 +0800 Subject: [PATCH 0244/2187] virtio-scsi: Add emulation scsi command We need to emulate some scsi command in stratovirt when using VRDM virtio-scsi. 1. RESUEST SENSE. The REQUEST SENSE command requests that the device server transfer patameter data containing sense data to the application client. 2. TEST UNIT READY The TEST UNIT READY command provides a means to check if the logical unit is ready. 3. INQUIRY The INQUIRY command requests that information regarding the logical unit and scsi target device be sent to the application client. 4. READ_CAPACITY_10 (SCSI BLOCK COMMANDS) The READ CAPACITY(10) command requests that the device server transfer eight bytes of parameter data describing the capacity and medium format of the direct access block device to the Data-In Buffer. 5. MODE_SENSE / MODE_SENSE_10 The MODE SENSE command provides a means for a device server to report parameters to an application client. 6. REPORT_LUNS The REPORT LUNS command requests that the peripheral device logical unit inventory accessible to the I_T nexus be sent to the application client. 7. SERVICE_ACTION_IN_16 8. WRITE_SAME_10 / WRITE_SAME_16 9. SYNCHRONIZE_CACHE Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 688 ++++++++++++++++++++++++++++++++++++++-- virtio/src/scsi/disk.rs | 4 + 2 files changed, 667 insertions(+), 25 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index d559ec255..095eb9087 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -10,8 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp; use std::collections::HashMap; use std::fs::File; +use std::io::Write; use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex, Weak}; @@ -21,9 +23,13 @@ use crate::ScsiCntlr::{ ScsiCntlr, ScsiCompleteCb, ScsiXferMode, VirtioScsiCmdReq, VirtioScsiCmdResp, VirtioScsiRequest, VIRTIO_SCSI_CDB_DEFAULT_SIZE, VIRTIO_SCSI_S_OK, }; -use crate::ScsiDisk::ScsiDevice; +use crate::ScsiDisk::{ + ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, + SCSI_TYPE_ROM, +}; +use address_space::AddressSpace; use byteorder::{BigEndian, ByteOrder}; -use log::{debug, info}; +use log::{debug, error, info}; use util::aio::{Aio, AioCb, IoCmd, Iovec}; /// Scsi Operation code. @@ -169,13 +175,15 @@ pub const SCSI_CMD_BUF_SIZE: usize = 16; pub const SCSI_SENSE_BUF_SIZE: usize = 252; /// SERVICE ACTION IN subcodes. -pub const SAI_READ_CAPACITY_16: u8 = 0x10; +pub const SUBCODE_READ_CAPACITY_16: u8 = 0x10; /// Used to compute the number of sectors. const SECTOR_SHIFT: u8 = 9; /// Size of a sector of the block device. const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; +const SCSI_DEFAULT_BLOCK_SIZE: i32 = 512; + /// Sense Keys. pub const NO_SENSE: u8 = 0x00; pub const RECOVERED_ERROR: u8 = 0x01; @@ -249,6 +257,24 @@ pub struct ScsiSense { pub const SCSI_SENSE_LEN: u32 = 18; +/// Mode page codes for mode sense/set. +pub const MODE_PAGE_R_W_ERROR: u8 = 0x01; +pub const MODE_PAGE_HD_GEOMETRY: u8 = 0x04; +pub const MODE_PAGE_FLEXIBLE_DISK_GEOMETRY: u8 = 0x05; +pub const MODE_PAGE_CACHING: u8 = 0x08; +pub const MODE_PAGE_AUDIO_CTL: u8 = 0x0e; +pub const MODE_PAGE_POWER: u8 = 0x1a; +pub const MODE_PAGE_FAULT_FAIL: u8 = 0x1c; +pub const MODE_PAGE_TO_PROTECT: u8 = 0x1d; +pub const MODE_PAGE_CAPABILITIES: u8 = 0x2a; +pub const MODE_PAGE_ALLS: u8 = 0x3f; + +pub const SCSI_MAX_INQUIRY_LEN: u32 = 256; +pub const SCSI_INQUIRY_PRODUCT_MAX_LEN: usize = 16; +pub const SCSI_INQUIRY_VENDOR_MAX_LEN: usize = 8; +pub const SCSI_INQUIRY_VERSION_MAX_LEN: usize = 4; +pub const SCSI_INQUIRY_VPD_SERIAL_NUMBER_MAX_LEN: usize = 32; + pub struct ScsiBus { /// Bus name. pub name: String, @@ -341,7 +367,7 @@ pub struct ScsiRequest { _resid: u32, pub opstype: u32, pub virtioscsireq: Arc>>, - _dev: Arc>, + dev: Arc>, } impl ScsiRequest { @@ -366,7 +392,7 @@ impl ScsiRequest { _resid, opstype, virtioscsireq: req.clone(), - _dev: scsidevice, + dev: scsidevice, }) } else { bail!("Error CDB!"); @@ -438,9 +464,155 @@ impl ScsiRequest { Ok(0) } - pub fn emulate_execute(&self, iocompletecb: ScsiCompleteCb) -> Result { + pub fn emulate_execute(&self, iocompletecb: ScsiCompleteCb) -> Result<()> { debug!("scsi command is {:#x}", self.cmd.command); match self.cmd.command { + REQUEST_SENSE => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + Some(SCSI_SENSE_NO_SENSE), + &Vec::new(), + )?; + } + TEST_UNIT_READY => { + let dev_lock = self.dev.lock().unwrap(); + if dev_lock.disk_image.is_none() { + error!("error in processing scsi command TEST_UNIT_READY, no scsi backend"); + } + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &Vec::new(), + )?; + } + INQUIRY => match scsi_command_emulate_inquiry(&self.cmd, &self.dev) { + Ok(outbuf) => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &outbuf, + )?; + } + Err(ref e) => { + error!("error in Processing scsi command INQUIRY: {:?}", e); + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + CHECK_CONDITION, + Some(SCSI_SENSE_INVALID_FIELD), + &Vec::new(), + )?; + } + }, + READ_CAPACITY_10 => match scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev) { + Ok(outbuf) => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &outbuf, + )?; + } + Err(ref e) => { + error!("error in Processing scsi command READ_CAPACITY_10: {:?}", e); + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + CHECK_CONDITION, + Some(SCSI_SENSE_INVALID_FIELD), + &Vec::new(), + )?; + } + }, + MODE_SENSE | MODE_SENSE_10 => { + match scsi_command_emulate_mode_sense(&self.cmd, &self.dev) { + Ok(outbuf) => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &outbuf, + )?; + } + Err(ref e) => { + error!( + "error in processing scsi command MODE_SENSE / MODE_SENSE_10: {:?}", + e + ); + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + CHECK_CONDITION, + Some(SCSI_SENSE_INVALID_FIELD), + &Vec::new(), + )?; + } + } + } + REPORT_LUNS => match scsi_command_emulate_report_luns(&self.cmd, &self.dev) { + Ok(outbuf) => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &outbuf, + )?; + } + Err(ref e) => { + error!("error in processing scsi command REPORT_LUNS: {:?}", e); + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + CHECK_CONDITION, + Some(SCSI_SENSE_INVALID_FIELD), + &Vec::new(), + )?; + } + }, + SERVICE_ACTION_IN_16 => { + match scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev) { + Ok(outbuf) => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &outbuf, + )?; + } + Err(ref e) => { + error!( + "error in processing scsi command SERVICE_ACTION_IN(16): {:?}", + e + ); + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + CHECK_CONDITION, + Some(SCSI_SENSE_INVALID_FIELD), + &Vec::new(), + )?; + } + } + } + WRITE_SAME_10 | WRITE_SAME_16 | SYNCHRONIZE_CACHE => { + self.cmd_complete( + &iocompletecb.mem_space, + VIRTIO_SCSI_S_OK, + GOOD, + None, + &Vec::new(), + )?; + } _ => { info!( "emulation scsi command {:#x} is not supported", @@ -457,18 +629,69 @@ impl ScsiRequest { } } - Ok(0) + Ok(()) } fn set_scsi_sense(&self, sense: ScsiSense) { let mut req = self.virtioscsireq.lock().unwrap(); - req.resp.sense[0] = 0x70; // Response code: current errors(0x70). + // Response code: current errors(0x70). + req.resp.sense[0] = 0x70; req.resp.sense[2] = sense.key; - req.resp.sense[7] = 10; // Additional sense length: sense len - 8. + // Additional sense length: sense len - 8. + req.resp.sense[7] = SCSI_SENSE_LEN as u8 - 8; req.resp.sense[12] = sense.asc; req.resp.sense[13] = sense.ascq; req.resp.sense_len = SCSI_SENSE_LEN; } + + fn cmd_complete( + &self, + mem_space: &Arc, + response: u8, + status: u8, + scsisense: Option, + outbuf: &[u8], + ) -> Result<()> { + if let Some(sense) = scsisense { + self.set_scsi_sense(sense); + } + let mut req = self.virtioscsireq.lock().unwrap(); + req.resp.response = response; + req.resp.status = status; + req.resp.resid = 0; + + if !outbuf.is_empty() { + for (idx, iov) in req.iovec.iter().enumerate() { + if outbuf.len() as u64 > iov.iov_len as u64 { + debug!( + "cmd is {:x}, outbuf len is {}, iov_len is {}, idx is {}, iovec size is {}", + self.cmd.command, + outbuf.len(), + iov.iov_len, + idx, + req.iovec.len() + ); + } + + write_buf_mem(outbuf, iov.iov_len, iov.iov_base) + .with_context(|| "Failed to write buf for virtio scsi iov")?; + } + } + + req.complete(mem_space); + Ok(()) + } +} + +fn write_buf_mem(buf: &[u8], max: u64, hva: u64) -> Result<()> { + let mut slice = unsafe { + std::slice::from_raw_parts_mut(hva as *mut u8, cmp::min(buf.len(), max as usize)) + }; + (&mut slice) + .write(buf) + .with_context(|| format!("Failed to write buf(hva:{})", hva))?; + + Ok(()) } pub const EMULATE_SCSI_OPS: u32 = 0; @@ -491,6 +714,16 @@ pub fn virtio_scsi_get_lun(lun: [u8; 8]) -> u16 { fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { match cdb[0] >> 5 { + // CDB[0]: Operation Code Byte. Bits[0-4]: Command Code. Bits[5-7]: Group Code. + // Group Code | Meaning | + // 000b | 6 bytes commands. | + // 001b | 10 bytes commands. | + // 010b | 10 bytes commands. | + // 011b | reserved. | + // 100b | 16 bytes commands. | + // 101b | 12 bytes commands. | + // 110b | vendor specific. | + // 111b | vendor specific. | 0 => 6, 1 | 2 => 10, 4 => 16, @@ -501,6 +734,12 @@ fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { let mut xfer = match cdb[0] >> 5 { + // Group Code | Transfer length. | + // 000b | Byte[4]. | + // 001b | Bytes[7-8]. | + // 010b | Bytes[7-8]. | + // 100b | Bytes[10-13]. | + // 101b | Bytes[6-9]. | 0 => cdb[4] as i32, 1 | 2 => BigEndian::read_u16(&cdb[7..]) as i32, 4 => BigEndian::read_u32(&cdb[10..]) as i32, @@ -522,14 +761,13 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { } else if cdb[1] & 4 != 0 { xfer = 1; } - // 512 : blocksize - xfer *= 512; + xfer *= SCSI_DEFAULT_BLOCK_SIZE; } WRITE_SAME_10 | WRITE_SAME_16 => { - if cdb[1] & 1 == 0 { + if cdb[1] & 1 != 0 { xfer = 0; } else { - xfer = 512; + xfer = SCSI_DEFAULT_BLOCK_SIZE; } } READ_CAPACITY_10 => { @@ -541,21 +779,15 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { SEND_VOLUME_TAG => { xfer = i32::from(cdb[9]) | i32::from(cdb[8]) << 8; } - WRITE_6 => { - if xfer == 0 { - xfer = 256 * 512; - } - } - WRITE_10 | WRITE_VERIFY_10 | WRITE_12 | WRITE_VERIFY_12 | WRITE_16 | WRITE_VERIFY_16 => { - xfer *= 512; - } - READ_6 | READ_REVERSE => { + WRITE_6 | READ_6 | READ_REVERSE => { + // length 0 means 256 blocks. if xfer == 0 { - xfer = 256 * 512; + xfer = 256 * SCSI_DEFAULT_BLOCK_SIZE; } } - READ_10 | READ_12 | READ_16 => { - xfer *= 512; + WRITE_10 | WRITE_VERIFY_10 | WRITE_12 | WRITE_VERIFY_12 | WRITE_16 | WRITE_VERIFY_16 + | READ_10 | READ_12 | READ_16 => { + xfer *= SCSI_DEFAULT_BLOCK_SIZE; } FORMAT_UNIT => { xfer = match cdb[1] & 16 { @@ -586,6 +818,12 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { fn scsi_cdb_lba(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i64 { match cdb[0] >> 5 { + // Group Code | Logical Block Address. | + // 000b | Byte[1].bits[0-4]~Byte[3]. | + // 001b | Bytes[2-5]. | + // 010b | Bytes[2-5]. | + // 100b | Bytes[2-9]. | + // 101b | Bytes[2-5]. | 0 => (BigEndian::read_u32(&cdb[0..]) & 0x1fffff) as i64, 1 | 2 | 5 => BigEndian::read_u32(&cdb[2..]) as i64, 4 => BigEndian::read_u64(&cdb[2..]) as i64, @@ -644,3 +882,403 @@ fn scsi_cdb_xfer_mode(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> ScsiXferMode _ => ScsiXferMode::ScsiXferFromDev, } } + +/// VPD: Virtual Product Data. +fn scsi_command_emulate_vpd_page( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + let buflen: usize; + let mut outbuf: Vec = vec![0; 4]; + + let dev_lock = dev.lock().unwrap(); + let page_code = cmd.buf[2]; + + outbuf[0] = dev_lock.scsi_type as u8 & 0x1f; + outbuf[1] = page_code; + + match page_code { + 0x00 => { + // Supported VPD Pages. + outbuf.push(0_u8); + if !dev_lock.state.serial.is_empty() { + // 0x80: Unit Serial Number. + outbuf.push(0x80); + } + // 0x83: Device Identification. + outbuf.push(0x83); + if dev_lock.scsi_type == SCSI_TYPE_DISK { + // 0xb0: Block Limits. + outbuf.push(0xb0); + // 0xb1: Block Device Characteristics. + outbuf.push(0xb1); + // 0xb2: Logical Block Provisioning. + outbuf.push(0xb2); + } + buflen = outbuf.len(); + } + 0x80 => { + // Unit Serial Number. + let len = dev_lock.state.serial.len(); + if len == 0 { + bail!("Missed serial number!"); + } + + let l = cmp::min(SCSI_INQUIRY_VPD_SERIAL_NUMBER_MAX_LEN, len); + let mut serial_vec = dev_lock.state.serial.as_bytes().to_vec(); + serial_vec.truncate(l); + outbuf.append(&mut serial_vec); + buflen = outbuf.len(); + } + 0x83 => { + // Device Identification. + let mut len: u8 = dev_lock.state.device_id.len() as u8; + if len > (255 - 8) { + len = 255 - 8; + } + + if len > 0 { + // 0x2: Code Set: ASCII, Protocol Identifier: FCP-4. + // 0: Identifier Type, Association, Reserved, Piv. + // 0: Reserved. + // len: identifier length. + outbuf.append(&mut [0x2_u8, 0_u8, 0_u8, len].to_vec()); + + let mut device_id_vec = dev_lock.state.device_id.as_bytes().to_vec(); + device_id_vec.truncate(len as usize); + outbuf.append(&mut device_id_vec); + } + buflen = outbuf.len(); + } + 0xb0 => { + // Block Limits. + if dev_lock.scsi_type == SCSI_TYPE_ROM { + bail!("Invalid scsi type: SCSI_TYPE_ROM !"); + } + outbuf.resize(64, 0); + + // Byte[4]: bit 0: wsnz: Write Same Non-zero. + // Byte[5] = 0: Maximum compare and write length (COMPARE_AND_WRITE not supported). + // Byte[6-7] = 0: Optimal transfer length granularity. + // Byte[8-11]: Maximum transfer length. + // Byte[12-15] = 0: Optimal Transfer Length. + // Byte[16-19] = 0: Maxium Prefetch Length. + // Byte[20-23]: Maximum unmap lba count. + // Byte[24-27]: Maximum unmap block descriptor count. + // Byte[28-31]: Optimal unmap granulatity. + // Byte[32-35] = 0: Unmap Granularity alignment. + // Byte[36-43]: Maximum write same length. + // Bytes[44-47] = 0: Maximum atomic Transfer length. + // Bytes[48-51] = 0: Atomic Alignment. + // Bytes[52-55] = 0: Atomic Transfer Length Granularity. + // Bytes[56-59] = 0: Maximum Atomic Transfer Length With Atomic Boundary. + // Bytes[60-63] = 0: Maximum Atomic Boundary Size. + outbuf[4] = 1; + let max_xfer_length: u32 = u32::MAX / 512; + BigEndian::write_u32(&mut outbuf[8..12], max_xfer_length); + let max_unmap_sectors: u32 = (1_u32 << 30) / 512; + BigEndian::write_u32(&mut outbuf[20..24], max_unmap_sectors); + let max_unmap_block_desc: u32 = 255; + BigEndian::write_u32(&mut outbuf[24..28], max_unmap_block_desc); + let opt_unmap_granulatity: u32 = (1_u32 << 12) / 512; + BigEndian::write_u32(&mut outbuf[28..32], opt_unmap_granulatity); + BigEndian::write_u64(&mut outbuf[36..44], max_xfer_length as u64); + buflen = outbuf.len(); + } + 0xb1 => { + // Block Device Characteristics. + // 0: Medium Rotation Rate: 2Bytes. + // 0: Medium Rotation Rate: 2Bytes. + // 0: Product Type. + // 0: Nominal Form Factor, Wacereq, Wabereq. + // 0: Vbuls, Fuab, Bocs, Reserved, Zoned, Reserved. + outbuf.append(&mut [0_u8, 0_u8, 0_u8, 0_u8, 0_u8].to_vec()); + buflen = 0x40; + } + 0xb2 => { + // Logical Block Provisioning. + // 0: Threshold exponent. + // 0xe0: LBPU | LBPWS | LBPWS10 | LBPRZ | ANC_SUP | DP. + // 0: Threshold percentage | Provisioning Type. + // 0: Threshold percentage. + outbuf.append(&mut [0_u8, 0xe0_u8, 1_u8, 0_u8].to_vec()); + buflen = 8; + } + _ => { + bail!("Invalid INQUIRY pagecode {}", page_code); + } + } + + // It's OK for just using outbuf bit 3, because all page_code's buflen in stratovirt is less than 255 now. + outbuf[3] = buflen as u8 - 4; + Ok(outbuf) +} + +fn scsi_command_emulate_inquiry( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + if cmd.buf[1] == 0x1 { + return scsi_command_emulate_vpd_page(cmd, dev); + } + + if cmd.buf[2] != 0 { + bail!("Invalid INQUIRY!"); + } + + let buflen = cmp::min(cmd.xfer, SCSI_MAX_INQUIRY_LEN); + let mut outbuf: Vec = vec![0; buflen as usize]; + + let dev_lock = dev.lock().unwrap(); + + outbuf[0] = (dev_lock.scsi_type & 0x1f) as u8; + outbuf[1] = match dev_lock.state.features & SCSI_DISK_F_REMOVABLE { + 1 => 0x80, + _ => 0, + }; + + let product_bytes = dev_lock.state.product.as_bytes(); + let product_len = cmp::min(product_bytes.len(), SCSI_INQUIRY_PRODUCT_MAX_LEN); + let vendor_bytes = dev_lock.state.vendor.as_bytes(); + let vendor_len = cmp::min(vendor_bytes.len(), SCSI_INQUIRY_VENDOR_MAX_LEN); + let version_bytes = dev_lock.state.version.as_bytes(); + let vension_len = cmp::min(version_bytes.len(), SCSI_INQUIRY_VERSION_MAX_LEN); + + outbuf[16..16 + product_len].copy_from_slice(product_bytes); + outbuf[8..8 + vendor_len].copy_from_slice(vendor_bytes); + outbuf[32..32 + vension_len].copy_from_slice(version_bytes); + + drop(dev_lock); + + // scsi version: 5. + outbuf[2] = 5; + outbuf[3] = (2 | 0x10) as u8; + + if buflen > 36 { + outbuf[4] = (buflen - 5) as u8; + } else { + outbuf[4] = 36 - 5; + } + + // TCQ. + outbuf[7] = 0x12; + + Ok(outbuf) +} + +fn scsi_command_emulate_read_capacity_10( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + if cmd.buf[8] & 1 == 0 && cmd.lba != 0 { + // PMI(Partial Medium Indicator) + bail!("Invalid scsi cmd READ_CAPACITY_10!"); + } + + let dev_lock = dev.lock().unwrap(); + let mut outbuf: Vec = vec![0; 8]; + let nb_sectors = cmp::min(dev_lock.disk_sectors as u32, u32::MAX); + + // Bytes[0-3]: Returned Logical Block Address. + // Bytes[4-7]: Logical Block Length In Bytes. + BigEndian::write_u32(&mut outbuf[0..4], nb_sectors); + BigEndian::write_u32(&mut outbuf[4..8], DEFAULT_SECTOR_SIZE); + + Ok(outbuf) +} + +fn scsi_command_emulate_mode_sense( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + // disable block descriptors(DBD) bit. + let mut dbd: bool = cmd.buf[1] & 0x8 != 0; + let page_code = cmd.buf[2] & 0x3f; + let page_control = (cmd.buf[2] & 0xc0) >> 6; + let mut outbuf: Vec = vec![0]; + let dev_lock = dev.lock().unwrap(); + let mut dev_specific_parameter: u8 = 0; + let nb_sectors = dev_lock.disk_sectors; + + debug!( + "MODE SENSE page_code {:x}, page_control {:x}, subpage {:x}, dbd bit {:x}, Allocation length {}", + page_code, + page_control, + cmd.buf[3], + cmd.buf[1] & 0x8, + cmd.buf[4] + ); + + if dev_lock.scsi_type == SCSI_TYPE_DISK { + if dev_lock.state.features & (1 << SCSI_DISK_F_DPOFUA) != 0 { + dev_specific_parameter = 0x10; + } + } else { + dbd = true; + } + drop(dev_lock); + + if cmd.command == MODE_SENSE { + outbuf.resize(4, 0); + // Device Specific Parameter. + outbuf[2] = dev_specific_parameter; + } else { + // MODE_SENSE_10. + outbuf.resize(8, 0); + // Device Specific Parameter. + outbuf[3] = dev_specific_parameter; + } + + if !dbd && nb_sectors > 0 { + if cmd.command == MODE_SENSE { + // Block Descriptor Length. + outbuf[3] = 8; + } else { + // Block Descriptor Length. + outbuf[7] = 8; + } + + // Block descriptors. + // Byte[0]: density code. + // Bytes[1-3]: number of blocks. + // Byte[4]: Reserved. + // Byte[5-7]: Block Length. + let mut block_desc: Vec = vec![0; 8]; + BigEndian::write_u32(&mut block_desc[0..4], nb_sectors as u32 & 0xffffff); + BigEndian::write_u32(&mut block_desc[4..8], DEFAULT_SECTOR_SIZE); + outbuf.append(&mut block_desc); + } + + if page_control == 3 { + bail!("Invalid Mode Sense command, Page control 0x11 is not supported!"); + } + + if page_code == 0x3f { + // 3Fh Return all pages not including subpages. + for pg in 0..page_code { + let _ = scsi_command_emulate_mode_sense_page(pg, page_control, &mut outbuf); + } + } else { + scsi_command_emulate_mode_sense_page(page_code, page_control, &mut outbuf)?; + } + + // The Mode Data Length field indicates the length in bytes of the following data + // that is available to be transferred. The Mode data length does not include the + // number of bytes in the Mode Data Length field. + let buflen = outbuf.len(); + if cmd.command == MODE_SENSE { + outbuf[0] = (buflen - 1) as u8; + } else { + outbuf[0] = (((buflen - 2) >> 8) & 0xff) as u8; + outbuf[1] = ((buflen - 2) & 0xff) as u8; + } + + Ok(outbuf) +} + +fn scsi_command_emulate_mode_sense_page( + page: u8, + page_control: u8, + outbuf: &mut Vec, +) -> Result> { + let buflen = outbuf.len(); + match page { + MODE_PAGE_CACHING => { + // Caching Mode Page. + outbuf.resize(buflen + 20, 0); + outbuf[buflen] = page; + outbuf[buflen + 1] = 18; + // 0x4: WCE(Write Cache Enable). + outbuf[buflen + 2] = 0x4; + } + MODE_PAGE_R_W_ERROR => { + // Read-Write Error Recovery mode page. + outbuf.resize(buflen + 12, 0); + outbuf[buflen] = page; + outbuf[buflen + 1] = 10; + + if page_control != 1 { + // 0x80: AWRE(Automatic Write Reallocation Enabled). + outbuf[buflen + 2] = 0x80; + } + } + _ => { + bail!( + "Invalid Mode Sense command, page control ({:x}), page ({:x})", + page_control, + page + ); + } + } + + Ok(outbuf.to_vec()) +} + +fn scsi_command_emulate_report_luns( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + let dev_lock = dev.lock().unwrap(); + // Byte 0-3: Lun List Length. Byte 4-7: Reserved. + let mut outbuf: Vec = vec![0; 8]; + + if cmd.xfer < 16 { + bail!("scsi REPORT LUNS xfer {} too short!", cmd.xfer); + } + + if cmd.buf[2] > 2 { + bail!("Invalid REPORT LUNS cmd, buf[2] is {}", cmd.buf[2]); + } + + let scsi_bus = dev_lock.parent_bus.upgrade().unwrap(); + let scsi_bus_clone = scsi_bus.lock().unwrap(); + + drop(dev_lock); + + for (_pos, device) in scsi_bus_clone.devices.iter() { + let device_lock = device.lock().unwrap(); + let len = outbuf.len(); + if device_lock.config.lun < 256 { + outbuf.push(0); + outbuf.push(device_lock.config.lun as u8); + } else { + outbuf.push(0x40 | ((device_lock.config.lun >> 8) & 0xff) as u8); + outbuf.push((device_lock.config.lun & 0xff) as u8); + } + outbuf.resize(len + 8, 0); + drop(device_lock); + } + + let len: u32 = outbuf.len() as u32 - 8; + BigEndian::write_u32(&mut outbuf[0..4], len); + Ok(outbuf) +} + +fn scsi_command_emulate_service_action_in_16( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + // Read Capacity(16) Command. + // Byte 0: Operation Code(0x9e) + // Byte 1: bit0 - bit4: Service Action(0x10), bit 5 - bit 7: Reserved. + if cmd.buf[1] & 0x1f == SUBCODE_READ_CAPACITY_16 { + let dev_lock = dev.lock().unwrap(); + let mut outbuf: Vec = vec![0; 32]; + let nb_sectors = dev_lock.disk_sectors; + + drop(dev_lock); + + // Byte[0-7]: Returned Logical BLock Address. + // Byte[8-11]: Logical Block Length in Bytes. + BigEndian::write_u64(&mut outbuf[0..8], nb_sectors); + BigEndian::write_u32(&mut outbuf[8..12], DEFAULT_SECTOR_SIZE); + + return Ok(outbuf); + } + + bail!( + "Invalid combination Scsi Command, operation code ({:x}), service action ({:x})", + SERVICE_ACTION_IN_16, + cmd.buf[1] & 31 + ); +} diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 83218c3e3..8b9862540 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -40,10 +40,14 @@ pub const SCSI_TYPE_NOT_PRESENT: u32 = 0x1f; pub const SCSI_TYPE_INACTIVE: u32 = 0x20; pub const SCSI_TYPE_NO_LUN: u32 = 0x7f; +pub const SCSI_DISK_F_REMOVABLE: u32 = 0; +pub const SCSI_DISK_F_DPOFUA: u32 = 1; + /// Used to compute the number of sectors. const SECTOR_SHIFT: u8 = 9; /// Size of the dummy block device. const DUMMY_IMG_SIZE: u64 = 0; +pub const DEFAULT_SECTOR_SIZE: u32 = 1_u32 << SECTOR_SHIFT; #[derive(Clone, Default)] pub struct ScsiDevState { -- Gitee From f3b971f5eaa158feef0d97dc87e49ae934648236 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 26 Sep 2022 18:13:31 +0800 Subject: [PATCH 0245/2187] virtio-scsi: Realize virtio-scsi-pci Realize virtio-scsi-pci to enable using it. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 5b98d7c17..addc3c52c 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -20,7 +20,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use super::super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_SCSI, + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, + VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, }; use crate::ScsiBus::{virtio_scsi_get_lun, ScsiBus, ScsiRequest, EMULATE_SCSI_OPS, GOOD}; use crate::VirtioError; @@ -149,6 +150,27 @@ impl ScsiCntlr { impl VirtioDevice for ScsiCntlr { /// Realize virtio scsi controller, which is a pci device. fn realize(&mut self) -> Result<()> { + // If iothread not found, return err. + if self.config.iothread.is_some() + && EventLoop::get_ctx(self.config.iothread.as_ref()).is_none() + { + bail!( + "IOThread {:?} of virtio scsi is not configured in params.", + self.config.iothread, + ); + } + + self.state.config_space.num_queues = self.config.queues; + + self.state.config_space.max_sectors = 0xFFFF_u32; + // cmd_per_lun: maximum nuber of linked commands can be sent to one LUN. 32bit. + self.state.config_space.cmd_per_lun = 128; + // seg_max: queue size - 2, 32 bit. + self.state.config_space.seg_max = self.queue_size() as u32 - 2; + + self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) + | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) + | (1_u64 << VIRTIO_SCSI_F_CHANGE); Ok(()) } -- Gitee From ba462e52bb1a96fe08a88b4366362807e487ac87 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 9 Oct 2022 14:18:41 +0800 Subject: [PATCH 0246/2187] doc: add virtio-scsi usage to config_guidebook.md Add virtio-scsi usage to config_guidebook.md. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 7264c06f3..4780994df 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -737,6 +737,40 @@ One property can be set for USB Tablet. Note: Only one tablet can be configured. +### 2.16 Virtio Scsi Controller +Virtio Scsi controller is a pci device which can be attached scsi device. + +Four properties can be set for Virtio-Scsi controller. + +* id: unique device id. +* bus: bus number of the device. +* addr: including slot number and function number. +* iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) + +```shell +-device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1] +``` +### 2.17 Virtio Scsi HardDisk +Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. + +Note: Only support using raw image file as backend now. + +Six properties can be set for virtio-scsi hd. + +* file: the path of backend image file. +* id: unique device id. +* bus: scsi bus name, only support $scsi_controller_name + ".0" +* scsi-id: id number (target) of scsi four level hierarchical address (host, channel, target, lun). Only support 0 now. +* lun: lun number (lun) of scsi four level hierarchical address (host, channel, target, lun). Only support starting from 0 now. +* serial: serial number of virtio scsi device.(optional) + +```shell +-device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1] +-drive file=path_on_host,id=drive-scsi0-0-0-0 +-device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 +``` +Note: Only support scsi-id=0 and lun number should start from 0 Now. + ## 3. Trace Users can specify the configuration file which lists events to trace. -- Gitee From 259ad24313a63b8819047aa0a8fa3256da214183 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 27 Oct 2022 16:00:44 +0800 Subject: [PATCH 0247/2187] Fix: delete redundant target_arch configurations machine/src/standard_vm/aarch64/mod.rs is compliled only in the ARM architecture. It don't need to set the same cnditions in the file. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 99862e2dd..581fc24d0 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -118,7 +118,6 @@ pub struct StdMachine { /// `vCPU` devices. cpus: Vec>, // Interrupt controller device. - #[cfg(target_arch = "aarch64")] irq_chip: Option>, /// Memory address space. pub sys_mem: Arc, @@ -952,7 +951,6 @@ impl MachineLifecycle for StdMachine { fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { ::vm_state_transfer( &self.cpus, - #[cfg(target_arch = "aarch64")] &self.irq_chip, &mut self.vm_state.0.lock().unwrap(), old, -- Gitee From d71a036d4b919867363e137cafcecd0815f6ba13 Mon Sep 17 00:00:00 2001 From: Darui Zhuo Date: Sat, 6 Aug 2022 13:20:16 +0800 Subject: [PATCH 0248/2187] Add PMU support for aarch64 VMs. Add PMU support for `microvm` and `virt` models. Add config options and PMU config will be preserved on migration. Signed-off-by: Darui Zhuo zhuodarui@outlook.com --- cpu/src/aarch64/caps.rs | 17 +++++ cpu/src/aarch64/mod.rs | 69 ++++++++++++++++++-- cpu/src/lib.rs | 27 +++++++- machine/src/lib.rs | 17 ++++- machine/src/micro_vm/mod.rs | 45 +++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 36 +++++++++- machine_manager/src/cmdline.rs | 11 ++-- machine_manager/src/config/machine_config.rs | 53 +++++++++++++++ util/src/device_tree.rs | 1 + 9 files changed, 261 insertions(+), 15 deletions(-) diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index 9da3f1530..199b8cdff 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -17,6 +17,7 @@ use kvm_bindings::{ KVM_REG_SIZE_U64, }; use kvm_ioctls::{Cap, Kvm, VcpuFd}; +use machine_manager::config::{CpuConfig, PmuConfig}; use super::core_regs::{get_one_reg_vec, set_one_reg_vec, Result}; @@ -47,6 +48,22 @@ impl ArmCPUCaps { } } +#[derive(Copy, Clone, Debug, Default)] +pub struct ArmCPUFeatures { + pub pmu: bool, +} + +impl From<&CpuConfig> for ArmCPUFeatures { + fn from(conf: &CpuConfig) -> Self { + Self { + pmu: match &conf.pmu { + PmuConfig::On => true, + PmuConfig::Off => false, + }, + } + } +} + /// Entry to cpreg list. #[derive(Default, Clone, Copy)] pub struct CpregListEntry { diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 02040d2dc..c84890a56 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -13,17 +13,20 @@ pub mod caps; mod core_regs; +use std::mem::forget; +use std::os::unix::prelude::{AsRawFd, FromRawFd}; use std::sync::{Arc, Mutex}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{ - kvm_mp_state, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, RegList, KVM_MP_STATE_RUNNABLE, - KVM_MP_STATE_STOPPED, + kvm_device_attr, kvm_mp_state, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, RegList, + KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, KVM_ARM_VCPU_PMU_V3_IRQ, + KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_STOPPED, }; -use kvm_ioctls::VcpuFd; +use kvm_ioctls::{DeviceFd, VcpuFd}; -pub use self::caps::ArmCPUCaps; use self::caps::CpregListEntry; +pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; use self::core_regs::{get_core_regs, set_core_regs}; use crate::CPU; use anyhow::{anyhow, Context, Result}; @@ -50,6 +53,12 @@ const UNINIT_MPIDR: u64 = 0xFFFF_FF00_0000_0000; const SYS_MPIDR_EL1: u64 = 0x6030_0000_0013_c005; const KVM_MAX_CPREG_ENTRIES: usize = 500; +/// Interrupt ID for pmu. +/// See: https://developer.arm.com/documentation/den0094/b/ +/// And: https://developer.arm.com/documentation/dai0492/b/ +pub const PPI_BASE: u32 = 16; +pub const PMU_INTR: u32 = 7; + /// AArch64 CPU booting configure information /// /// Before jumping into the kernel, primary CPU general-purpose @@ -104,6 +113,8 @@ pub struct ArmCPUState { cpreg_len: usize, /// The list of Cpreg. cpreg_list: [CpregListEntry; 512], + /// Vcpu features + features: ArmCPUFeatures, } impl ArmCPUState { @@ -139,6 +150,7 @@ impl ArmCPUState { self.mp_state = locked_cpu_state.mp_state; self.cpreg_len = locked_cpu_state.cpreg_len; self.cpreg_list = locked_cpu_state.cpreg_list; + self.features = locked_cpu_state.features; } /// Set register value in `ArmCPUState` according to `boot_config`. @@ -151,6 +163,7 @@ impl ArmCPUState { &mut self, vcpu_fd: &Arc, boot_config: &ArmCPUBootConfig, + vcpu_config: &ArmCPUFeatures, ) -> Result<()> { KVM_FDS .load() @@ -168,6 +181,11 @@ impl ArmCPUState { self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; } + // Enable PMU from config. + if vcpu_config.pmu { + self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; + } + self.set_core_reg(boot_config); vcpu_fd @@ -177,6 +195,9 @@ impl ArmCPUState { .get_one_reg(SYS_MPIDR_EL1) .with_context(|| "Failed to get mpidr")?; + ArmCPUState::set_cpu_feature(vcpu_config, vcpu_fd)?; + self.features = *vcpu_config; + Ok(()) } @@ -240,6 +261,43 @@ impl ArmCPUState { self.core_regs.regs.pc = boot_config.boot_pc; } } + + fn set_cpu_feature(features: &ArmCPUFeatures, vcpu_fd: &Arc) -> Result<()> { + if !features.pmu { + return Ok(()); + } + + let pmu_attr = kvm_device_attr { + group: KVM_ARM_VCPU_PMU_V3_CTRL, + attr: KVM_ARM_VCPU_PMU_V3_INIT as u64, + addr: 0, + flags: 0, + }; + let vcpu_device = unsafe { DeviceFd::from_raw_fd(vcpu_fd.as_raw_fd()) }; + vcpu_device + .has_device_attr(&pmu_attr) + .with_context(|| "Kernel does not support PMU for vCPU")?; + // Set IRQ 23, PPI 7 for PMU. + let irq = PMU_INTR + PPI_BASE; + let pmu_irq_attr = kvm_device_attr { + group: KVM_ARM_VCPU_PMU_V3_CTRL, + attr: KVM_ARM_VCPU_PMU_V3_IRQ as u64, + addr: &irq as *const u32 as u64, + flags: 0, + }; + + vcpu_device + .set_device_attr(&pmu_irq_attr) + .with_context(|| "Failed to set IRQ for PMU")?; + // Init PMU after setting IRQ. + vcpu_device + .set_device_attr(&pmu_attr) + .with_context(|| "Failed to enable PMU for vCPU")?; + // forget `vcpu_device` to avoid fd close on exit, as DeviceFd is backed by File. + forget(vcpu_device); + + Ok(()) + } } impl StateTransfer for CPU { @@ -283,6 +341,9 @@ impl StateTransfer for CPU { self.fd.vcpu_init(&cpu_state.kvi)?; + //Init CPU features. + ArmCPUState::set_cpu_feature(&cpu_state.features, &self.fd) + .map_err(|_| migration::MigrationError::FromBytesError("failed to set features."))?; Ok(()) } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 281b891b8..c7f3ac8cc 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -43,9 +43,15 @@ pub use aarch64::ArmCPUBootConfig as CPUBootConfig; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUCaps as CPUCaps; #[cfg(target_arch = "aarch64")] +pub use aarch64::ArmCPUFeatures as CPUFeatures; +#[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUState as ArchCPU; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUTopology as CPUTopology; +#[cfg(target_arch = "aarch64")] +pub use aarch64::PMU_INTR; +#[cfg(target_arch = "aarch64")] +pub use aarch64::PPI_BASE; use machine_manager::qmp::qmp_schema; #[cfg(target_arch = "x86_64")] use x86_64::caps::X86CPUCaps as CPUCaps; @@ -102,7 +108,12 @@ pub enum CpuLifecycleState { #[allow(clippy::upper_case_acronyms)] pub trait CPUInterface { /// Realize `CPU` structure, set registers value for `CPU`. - fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()>; + fn realize( + &self, + boot: &CPUBootConfig, + topology: &CPUTopology, + #[cfg(target_arch = "aarch64")] features: &CPUFeatures, + ) -> Result<()>; /// Start `CPU` thread and run virtual CPU in kvm. /// @@ -237,7 +248,12 @@ impl CPU { } impl CPUInterface for CPU { - fn realize(&self, boot: &CPUBootConfig, topology: &CPUTopology) -> Result<()> { + fn realize( + &self, + boot: &CPUBootConfig, + topology: &CPUTopology, + #[cfg(target_arch = "aarch64")] config: &CPUFeatures, + ) -> Result<()> { trace_cpu_boot_config(boot); let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { @@ -250,7 +266,12 @@ impl CPUInterface for CPU { self.arch_cpu .lock() .unwrap() - .set_boot_config(&self.fd, boot) + .set_boot_config( + &self.fd, + boot, + #[cfg(target_arch = "aarch64")] + config, + ) .with_context(|| "Failed to realize arch cpu")?; self.arch_cpu diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 275ddda1e..c3a8e1528 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -37,6 +37,8 @@ use address_space::{ }; pub use anyhow::Result; use anyhow::{anyhow, bail, Context}; +#[cfg(target_arch = "aarch64")] +use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] @@ -95,6 +97,11 @@ pub trait MachineOps { fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result; + #[cfg(target_arch = "aarch64")] + fn load_cpu_features(&self, vmcfg: &VmConfig) -> Result { + Ok((&vmcfg.machine_config.cpu_config).into()) + } + /// Init I/O & memory address space and mmap guest memory. /// /// # Arguments @@ -163,6 +170,7 @@ pub trait MachineOps { topology: &CPUTopology, fds: &[Arc], boot_cfg: &Option, + #[cfg(target_arch = "aarch64")] vcpu_cfg: &Option, ) -> Result>> where Self: Sized, @@ -189,10 +197,15 @@ pub trait MachineOps { if let Some(boot_config) = boot_cfg { for cpu_index in 0..nr_cpus as usize { cpus[cpu_index as usize] - .realize(boot_config, topology) + .realize( + boot_config, + topology, + #[cfg(target_arch = "aarch64")] + &vcpu_cfg.unwrap_or_default(), + ) .with_context(|| { format!( - "Failed to realize arch cpu register for CPU {}/KVM", + "Failed to realize arch cpu register/features for CPU {}/KVM", cpu_index ) })?; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 3a6a86033..6e05d3a70 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -50,6 +50,10 @@ use vmm_sys_util::eventfd::EventFd; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; +#[cfg(target_arch = "aarch64")] +use cpu::CPUFeatures; +#[cfg(target_arch = "aarch64")] +use cpu::PMU_INTR; use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState, CpuTopology, CPU}; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; @@ -154,6 +158,9 @@ impl MmioReplaceableInfo { pub struct LightMachine { // `vCPU` topology, support sockets, cores, threads. cpu_topo: CpuTopology, + // `vCPU` family and feature configuration. Only supports aarch64 currently. + #[cfg(target_arch = "aarch64")] + cpu_feature: CPUFeatures, // `vCPU` devices. cpus: Vec>, // Interrupt controller device. @@ -223,6 +230,8 @@ impl LightMachine { ), cpus: Vec::new(), #[cfg(target_arch = "aarch64")] + cpu_feature: (&vm_config.machine_config.cpu_config).into(), + #[cfg(target_arch = "aarch64")] irq_chip: None, sys_mem, #[cfg(target_arch = "x86_64")] @@ -759,12 +768,23 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_dies, )); trace_cpu_topo(&topology); + + #[cfg(target_arch = "aarch64")] + let cpu_config = if migrate_info.0 == MigrateMode::Unknown { + Some(locked_vm.load_cpu_features(vm_config)?) + } else { + None + }; + + // vCPUs init,and apply CPU features (for aarch64) locked_vm.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, &topology, &vcpu_fds, &boot_config, + #[cfg(target_arch = "aarch64")] + &cpu_config, )?); #[cfg(target_arch = "aarch64")] @@ -1373,6 +1393,25 @@ fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Res Ok(()) } +#[cfg(target_arch = "aarch64")] +fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { + let node = "pmu"; + let pmu_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("compatible", "arm,armv8-pmuv3")?; + fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; + fdt.set_property_array_u32( + "interrupts", + &[ + device_tree::GIC_FDT_IRQ_TYPE_PPI, + PMU_INTR, + device_tree::IRQ_TYPE_LEVEL_HIGH, + ], + )?; + + fdt.end_node(pmu_node_dep)?; + Ok(()) +} + /// Trait that helps to generate all nodes in device-tree. #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] @@ -1453,6 +1492,11 @@ impl CompileFDTHelper for LightMachine { fdt.end_node(cpus_node_dep)?; + // CPU Features : PMU + if self.cpu_feature.pmu { + generate_pmu_node(fdt)?; + } + Ok(()) } @@ -1512,6 +1556,7 @@ impl CompileFDTHelper for LightMachine { _ => (), } } + Ok(()) } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 99862e2dd..79e555775 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,6 +14,7 @@ mod pci_host_root; mod syscall; pub use crate::error::MachineError; +use std::borrow::Borrow; use std::collections::HashMap; use std::fs::OpenOptions; use std::mem::size_of; @@ -34,7 +35,9 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; +use cpu::{ + CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuTopology, CPU, PMU_INTR, PPI_BASE, +}; #[cfg(not(target_env = "musl"))] use devices::legacy::Ramfb; use devices::legacy::{ @@ -117,6 +120,7 @@ pub struct StdMachine { cpu_topo: CpuTopology, /// `vCPU` devices. cpus: Vec>, + cpu_features: CPUFeatures, // Interrupt controller device. #[cfg(target_arch = "aarch64")] irq_chip: Option>, @@ -173,6 +177,7 @@ impl StdMachine { Ok(StdMachine { cpu_topo, cpus: Vec::new(), + cpu_features: vm_config.machine_config.cpu_config.borrow().into(), irq_chip: None, sys_mem: sys_mem.clone(), sysbus, @@ -500,6 +505,11 @@ impl MachineOps for StdMachine { } else { None }; + let cpu_config = if migrate.0 == MigrateMode::Unknown { + Some(locked_vm.load_cpu_features(vm_config)?) + } else { + None + }; locked_vm .reset_fwcfg_boot_order() @@ -511,6 +521,7 @@ impl MachineOps for StdMachine { &CPUTopology::new(), &vcpu_fds, &boot_config, + &cpu_config, )?); if let Some(boot_cfg) = boot_config { @@ -810,6 +821,7 @@ impl AcpiBuilder for StdMachine { gic_cpu.flags = 5; gic_cpu.mpidr = mpidr & mpidr_mask; gic_cpu.vgic_interrupt = ARCH_GIC_MAINT_IRQ + INTERRUPT_PPIS_COUNT; + gic_cpu.perf_interrupt = PMU_INTR + PPI_BASE; madt.append_child(&gic_cpu.aml_bytes()); } @@ -1191,6 +1203,24 @@ fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result< Ok(()) } +fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { + let node = "pmu"; + let pmu_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("compatible", "arm,armv8-pmuv3")?; + fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; + fdt.set_property_array_u32( + "interrupts", + &[ + device_tree::GIC_FDT_IRQ_TYPE_PPI, + PMU_INTR, + device_tree::IRQ_TYPE_LEVEL_HIGH, + ], + )?; + fdt.end_node(pmu_node_dep)?; + + Ok(()) +} + /// Trait that helps to generate all nodes in device-tree. #[allow(clippy::upper_case_acronyms)] trait CompileFDTHelper { @@ -1267,6 +1297,10 @@ impl CompileFDTHelper for StdMachine { fdt.end_node(cpus_node_dep)?; + if self.cpu_features.pmu { + generate_pmu_node(fdt)?; + } + Ok(()) } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 66b214191..442eaf073 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -244,13 +244,13 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("-mon is another way to create qmp channel. To use it, the chardev should be specified") .takes_value(true), ) - .arg( + .arg( Arg::with_name("cpu") .long("cpu") - .value_name("host") - .hidden(true) - .can_no_value(true) - .takes_value(true), + .value_name("[model][,pmu=on|off]") + .help("Set CPU model and features.") + .can_no_value(false) + .takes_value(true) ) .arg( Arg::with_name("overcommit") @@ -420,6 +420,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("memory")), vm_cfg, add_memory); add_args_to_config!((args.value_of("mem-path")), vm_cfg, add_mem_path); add_args_to_config!((args.value_of("smp")), vm_cfg, add_cpu); + add_args_to_config!((args.value_of("cpu")), vm_cfg, add_cpu_feature); add_args_to_config!((args.value_of("kernel")), vm_cfg, add_kernel); add_args_to_config!((args.value_of("initrd-file")), vm_cfg, add_initrd); add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 19639027c..2d1ce01ae 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -123,6 +123,23 @@ impl Default for MachineMemConfig { } } +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +pub struct CpuConfig { + pub pmu: PmuConfig, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum PmuConfig { + On, + Off, +} + +impl Default for PmuConfig { + fn default() -> Self { + PmuConfig::Off + } +} + /// Config struct for machine-config. /// Contains some basic Vm config about cpu, memory, name. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -136,6 +153,7 @@ pub struct MachineConfig { pub nr_sockets: u8, pub max_cpus: u8, pub mem_config: MachineMemConfig, + pub cpu_config: CpuConfig, } impl Default for MachineConfig { @@ -151,6 +169,7 @@ impl Default for MachineConfig { nr_sockets: DEFAULT_SOCKETS, max_cpus: DEFAULT_MAX_CPUS, mem_config: MachineMemConfig::default(), + cpu_config: CpuConfig::default(), } } } @@ -337,6 +356,22 @@ impl VmConfig { Ok(()) } + pub fn add_cpu_feature(&mut self, features: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("cpu"); + cmd_parser.push(""); + cmd_parser.push("pmu"); + cmd_parser.parse(features)?; + //Check PMU when actually enabling PMU. + if let Some(k) = cmd_parser.get_value::("pmu")? { + self.machine_config.cpu_config.pmu = match k.as_ref() { + "on" => PmuConfig::On, + "off" => PmuConfig::Off, + _ => bail!("Invalid PMU option,must be one of \'on\" or \"off\"."), + } + } + Ok(()) + } + pub fn add_mem_path(&mut self, mem_path: &str) -> Result<()> { self.machine_config.mem_config.mem_path = Some(mem_path.replace('\"', "")); Ok(()) @@ -601,6 +636,7 @@ mod tests { nr_sockets: 1, max_cpus: MIN_NR_CPUS as u8, mem_config: memory_config, + cpu_config: CpuConfig::default(), }; assert!(machine_config.check().is_ok()); @@ -959,4 +995,21 @@ mod tests { let policy = HostMemPolicy::from(String::from("error")); assert!(policy == HostMemPolicy::NotSupported); } + + #[cfg(target_arch = "aarch64")] + #[test] + fn test_cpu_features() { + //Test PMU flags + let mut vm_config = VmConfig::default(); + vm_config.add_cpu_feature("host").unwrap(); + assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); + vm_config.add_cpu_feature("host,pmu=off").unwrap(); + assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); + vm_config.add_cpu_feature("pmu=off").unwrap(); + assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); + vm_config.add_cpu_feature("host,pmu=on").unwrap(); + assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::On); + vm_config.add_cpu_feature("pmu=on").unwrap(); + assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::On); + } } diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 20a55e5c4..87c69e0c5 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -19,6 +19,7 @@ use byteorder::{BigEndian, ByteOrder}; pub const CLK_PHANDLE: u32 = 1; pub const GIC_PHANDLE: u32 = 2; pub const GIC_ITS_PHANDLE: u32 = 3; +pub const PPI_CLUSTER_PHANDLE: u32 = 4; pub const CPU_PHANDLE_START: u32 = 10; pub const GIC_FDT_IRQ_TYPE_SPI: u32 = 0; -- Gitee From dc738d41014e010154f1de4f5706e650bc06b525 Mon Sep 17 00:00:00 2001 From: Darui Zhuo Date: Sat, 17 Sep 2022 20:19:31 +0800 Subject: [PATCH 0249/2187] Add docs for PMU. Add description of PMU config in config handbook and enable PMU option in kernal config file. Signed-off-by: Darui Zhuo --- docs/config_guidebook.md | 18 +++++++++++++++++- .../micro_vm/kernel_config_4.19_aarch64 | 2 +- .../micro_vm/kernel_config_5.10_aarch64 | 2 +- docs/migration.md | 3 ++- docs/snapshot.md | 3 ++- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1c5716e28..e0a0aade7 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -21,7 +21,9 @@ NB: machine type "none" is used to get the capabilities of stratovirt. -machine [type=]name[,dump-guest-core=on|off,mem-share=on|off] ``` -### 1.2 Cpu Number +### 1.2 CPU Config + +#### 1.2.1 CPU Number StratoVirt supports to set the number of VCPUs(**nr_vcpus**). @@ -47,6 +49,20 @@ If it is configured, sockets * dies * clusters * cores * threads must be equal t -smp [cpus=]n[,maxcpus=,sockets=,dies=,clusters=,cores=,threads=] ``` +#### 1.2.2 CPU Features + +StratoVirt allows the configuration of CPU features. + +Currently, these options are supported. + +* CPU Family: Set the CPU family for VM, default to `host`, and this is the only supported variant currently. +* pmu: This enables armv8 PMU for VM. Should be `off` or `on`, default to `off`. (Currently only supported on aarch64) + +```shell +# cmdline +-cpu [host,][pmu=on|off] +``` + ### 1.3 Memory #### 1.3.1 Memory Size diff --git a/docs/kernel_config/micro_vm/kernel_config_4.19_aarch64 b/docs/kernel_config/micro_vm/kernel_config_4.19_aarch64 index 073641783..804b86c1f 100644 --- a/docs/kernel_config/micro_vm/kernel_config_4.19_aarch64 +++ b/docs/kernel_config/micro_vm/kernel_config_4.19_aarch64 @@ -1761,7 +1761,7 @@ CONFIG_PARTITION_PERCPU=y # # CONFIG_ARM_CCI_PMU is not set # CONFIG_ARM_CCN is not set -# CONFIG_ARM_PMU is not set +CONFIG_ARM_PMU=y # CONFIG_ARM_DSU_PMU is not set # CONFIG_HISI_PMU is not set # CONFIG_ARM_SPE_PMU is not set diff --git a/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 b/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 index 54e2c6e72..1ef0b5024 100644 --- a/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 +++ b/docs/kernel_config/micro_vm/kernel_config_5.10_aarch64 @@ -1948,7 +1948,7 @@ CONFIG_PARTITION_PERCPU=y # CONFIG_ARM_CCI_PMU is not set # CONFIG_ARM_CCN is not set # CONFIG_ARM_CMN is not set -# CONFIG_ARM_PMU is not set +CONFIG_ARM_PMU=y # CONFIG_ARM_DSU_PMU is not set # CONFIG_ARM_SPE_PMU is not set # end of Performance monitor support diff --git a/docs/migration.md b/docs/migration.md index 2aefd00cc..fbd3a00cc 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -100,6 +100,7 @@ Some devices and feature don't support to be migration yet: - `vfio` devices - `balloon` - `mem-shared`,`backend file of memory` +- `pmu`: ongoing PMU measurements Some device attributes can't be changed: - `virtio-net`: mac @@ -108,4 +109,4 @@ Some device attributes can't be changed: - `smp` - `m` -If hot plug device before migrate source vm, add newly replaced device command should be add to destination vm. \ No newline at end of file +If hot plug device before migrate source vm, add newly replaced device command should be add to destination vm. diff --git a/docs/snapshot.md b/docs/snapshot.md index a8ebffd2c..d28dffaf5 100644 --- a/docs/snapshot.md +++ b/docs/snapshot.md @@ -86,6 +86,7 @@ Some devices and feature don't support to be snapshot yet: - `vfio` devices - `balloon` - `hugepage`,`mem-shared`,`backend file of memory` +- `pmu`: ongoing PMU measurements Some device attributes can't be changed: - `virtio-net`: mac @@ -94,4 +95,4 @@ Some device attributes can't be changed: - `smp` - `m` -For machine type `microvm`, if use `hot-replace` before snapshot, add newly replaced device to restore command. \ No newline at end of file +For machine type `microvm`, if use `hot-replace` before snapshot, add newly replaced device to restore command. -- Gitee From 45dd42a251ded8da91a85dea5a54d039d1b17fcf Mon Sep 17 00:00:00 2001 From: Darui Zhuo Date: Wed, 21 Sep 2022 08:43:49 +0800 Subject: [PATCH 0250/2187] Add hydropper test for aarch64 PMU. Add tests to test PMU feature on microvm and standvm. Add instruction to configure perf tool. Signed-off-by: Darui Zhuo --- tests/hydropper/docs/IMAGE_BUILD.md | 2 + .../functional/test_microvm_cpu_features.py | 22 ++++++++++ .../functional/test_standvm_cpu_feature.py | 42 +++++++++++++++++++ tests/hydropper/virt/microvm.py | 7 ++++ tests/hydropper/virt/standvm.py | 6 +++ 5 files changed, 79 insertions(+) create mode 100644 tests/hydropper/testcases/standvm/functional/test_standvm_cpu_feature.py diff --git a/tests/hydropper/docs/IMAGE_BUILD.md b/tests/hydropper/docs/IMAGE_BUILD.md index 4485cfa21..57506c848 100644 --- a/tests/hydropper/docs/IMAGE_BUILD.md +++ b/tests/hydropper/docs/IMAGE_BUILD.md @@ -45,6 +45,8 @@ chroot . echo "set enable-bracketed-paste off" > /root/.inputrc yum -y install openssh + # For PMU tests + yum -y install perf ``` - 离开当前目录后,使用umount命令卸载镜像。 diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py index ab80d50d8..768ec74fc 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py @@ -177,3 +177,25 @@ def test_brand_string(microvm): assert guest_brand_string == expected_guest_brand_string + +@pytest.mark.skipif("platform.machine().startswith('x86_64')") +@pytest.mark.acceptance +def test_pmu(microvm): + '''Test for PMU events and interrupt. + ''' + test_vm = microvm + test_vm.basic_config(vcpu_count=1,cpu_features="pmu=on") + test_vm.launch() + + #PMU events available? + guest_cmd = "perf list | grep cache-misses" + status, output = test_vm.serial_cmd(guest_cmd) + assert status == 0 + + #PMU interrupt available? + guest_cmd = "cat /proc/interrupts | grep -i 'pmu' | head -1" + status, output = test_vm.serial_cmd(guest_cmd) + assert status == 0 + + + diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_cpu_feature.py b/tests/hydropper/testcases/standvm/functional/test_standvm_cpu_feature.py new file mode 100644 index 000000000..bbff08285 --- /dev/null +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_cpu_feature.py @@ -0,0 +1,42 @@ +# Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +# +# StratoVirt is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan +# PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http:#license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +"""Test standvm PMU""" +import time +import logging +import pytest + +LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" +logging.basicConfig(filename='/var/log/pytest.log', level=logging.DEBUG, format=LOG_FORMAT) + +@pytest.mark.skipif("platform.machine().startswith('x86_64')") +@pytest.mark.acceptance +def test_standvm_pmu(standvm): + """ + Test PMU feature for standvm. + + steps: + 1) launch standvm with argument: "-cpu pmu=on". + 2) Check PMU presence. + """ + test_vm = standvm + test_vm.basic_config(cpu_features="pmu=on") + test_vm.launch() + #PMU events available? + guest_cmd = "perf list | grep cache-misses" + status, output = test_vm.serial_cmd(guest_cmd) + assert status == 0 + + #PMU interrupt available? + guest_cmd = "cat /proc/interrupts | grep -i 'pmu' | head -1" + status, output = test_vm.serial_cmd(guest_cmd) + assert status == 0 diff --git a/tests/hydropper/virt/microvm.py b/tests/hydropper/virt/microvm.py index 7e4babf4f..f11ee3916 100644 --- a/tests/hydropper/virt/microvm.py +++ b/tests/hydropper/virt/microvm.py @@ -225,6 +225,9 @@ class MicroVM(BaseVM): if "vhost_type" in kwargs: self.vhost_type = kwargs.get("vhost_type") del kwargs["vhost_type"] + if "cpu_features" in kwargs: + self.configdict["machine-config"]["cpu_features"] = kwargs.get("cpu_features") + del kwargs["cpu_features"] for key, value in kwargs.items(): if hasattr(self, key): @@ -259,6 +262,10 @@ class MicroVM(BaseVM): self.add_args('-m', _temp_mem_args) if "mem_path" in configdict["machine-config"]: self.add_args('-mem-path', configdict["machine-config"]["mem_path"]) + # make CPU feature cmdline + if "cpu_features" in configdict["machine-config"]: + self.add_args('-cpu', configdict["machine-config"]["cpu_features"]) + # make block cmdline for block in configdict.get("block", []): diff --git a/tests/hydropper/virt/standvm.py b/tests/hydropper/virt/standvm.py index 0bda7a4ef..45f3263c9 100644 --- a/tests/hydropper/virt/standvm.py +++ b/tests/hydropper/virt/standvm.py @@ -358,6 +358,9 @@ class StandVM(BaseVM): if "vhost_type" in kwargs: self.vhost_type = kwargs.get("vhost_type") del kwargs["vhost_type"] + if "cpu_features" in kwargs: + self.configdict["machine-config"]["cpu_features"] = kwargs.get("cpu_features") + del kwargs["cpu_features"] for key, value in kwargs.items(): if hasattr(self, key): @@ -392,6 +395,9 @@ class StandVM(BaseVM): self.add_args('-m', _temp_mem_args) if "mem_path" in configdict["machine-config"]: self.add_args('-mem-path', configdict["machine-config"]["mem_path"]) + # make CPU feature cmdline + if "cpu_features" in configdict["machine-config"]: + self.add_args('-cpu', configdict["machine-config"]["cpu_features"]) # make block cmdline for block in configdict.get("block", []): -- Gitee From dfdcff5aaf3f9df8db9df42f2efe147123bed7bc Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 28 Oct 2022 10:16:39 +0800 Subject: [PATCH 0251/2187] hotplug: avoid potential deadlocks Drop the bus lock when do unplug request, if the device function is not zero, call unplug directly causes deadlock. Signed-off-by: zhouli57 --- pci/src/hotplug.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index 618567f6a..cb536a774 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -68,12 +68,15 @@ pub fn handle_unplug_request( dev: &Arc>, ) -> Result<()> { let locked_bus = bus.lock().unwrap(); - if let Some(hpc) = locked_bus.hotplug_controller.as_ref() { - hpc.upgrade().unwrap().lock().unwrap().unplug_request(dev) + let hpc = if let Some(hpc) = locked_bus.hotplug_controller.as_ref() { + hpc.clone() } else { bail!( "No hot plug controller found for bus {} when unplug request", locked_bus.name ); - } + }; + // No need to hold the lock. + drop(locked_bus); + hpc.upgrade().unwrap().lock().unwrap().unplug_request(dev) } -- Gitee From 4db3d4813b4597d0134295e22b7598675c54143d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 28 Oct 2022 11:49:09 +0800 Subject: [PATCH 0252/2187] gpu: update cursor coordinates Update the cursor hot_x and hot_y when update cursor. Signed-off-by: zhouli57 --- virtio/src/gpu.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index b9e96f131..da7aae94f 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -621,6 +621,10 @@ impl GpuIoHandler { ..Default::default() }; scanout.mouse = Some(tmp_mouse); + } else { + let mut mse = scanout.mouse.as_mut().unwrap(); + mse.hot_x = info_cursor.hot_x; + mse.hot_y = info_cursor.hot_y; } if info_cursor.resource_id != 0 { if let Some(res_index) = self -- Gitee From ea22dbe685f94d63c19dc527e39bc5664601f93f Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Fri, 28 Oct 2022 11:42:05 +0800 Subject: [PATCH 0253/2187] virtiofs: modify unlink function calling unlink function will fail when sock file not exist, so modify it to check the sock file exists. Signed-off-by: YuJun Huang --- util/src/unix.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/src/unix.rs b/util/src/unix.rs index 8e1e2eb52..e2f1659a9 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -15,6 +15,7 @@ use std::fs::File; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; +use std::path::Path; use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; use libc::{ @@ -169,7 +170,7 @@ impl UnixSock { /// Bind assigns a unique listener for the socket. pub fn bind(&mut self, unlink: bool) -> Result<()> { - if unlink { + if unlink && Path::new(self.path.as_str()).exists() { std::fs::remove_file(self.path.as_str()) .with_context(|| format!("Failed to remove socket file {}.", self.path.as_str()))?; } -- Gitee From 6e48e430b6755ac3117ecd688f1e37c1fe06d7c4 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 28 Oct 2022 03:19:43 -0400 Subject: [PATCH 0254/2187] Balloon: Introduce MADV_REMOVE to remove shared memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can create shared anonymous memory via "-machine virt,mem-share=on,..." In this case, MADV_DONTNEED will only remove/zap all relevant page table entries of the current process, the backend storage will not get removed. At this point we can use MADV_REMOVE. Shared anonymous memory is internally really just shmem. You can use “cat /proc/meminfo | grep Shmem” to see the total amount of Shmem. One obvious difference between using parameters MADV_DONTNEED and MADV_REMOVE is that MADV_REMOVE immediately decreases the total amount of Shmem, while MADV_DONTNEED does not. MADV_DONTNEED will only unmap the page table, not immediately remove it from the LRU list, and Shmem will not decrease. Signed-off-by: Li HuaChao --- machine/src/lib.rs | 6 ++- machine/src/micro_vm/syscall.rs | 6 ++- machine/src/standard_vm/aarch64/syscall.rs | 6 ++- machine/src/standard_vm/x86_64/syscall.rs | 6 ++- virtio/src/balloon.rs | 44 ++++++++++++++-------- 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4de35e183..bd4a1dd64 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -325,7 +325,11 @@ pub trait MachineOps { fn add_virtio_balloon(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_balloon(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); - let balloon = Arc::new(Mutex::new(Balloon::new(&device_cfg, sys_mem.clone()))); + let balloon = Arc::new(Mutex::new(Balloon::new( + &device_cfg, + sys_mem.clone(), + vm_config.machine_config.mem_config.mem_share, + ))); Balloon::object_init(balloon.clone()); if cfg_args.contains("virtio-balloon-device") { let device = VirtioMmioDevice::new(sys_mem, balloon); diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index b3a4aa5a2..f439ce83d 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -188,11 +188,13 @@ fn madvise_rule() -> BpfRule { return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32); + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); #[cfg(not(target_env = "musl"))] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32); + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); } fn futex_rule() -> BpfRule { diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index ebcaa49bd..eb08fdb16 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -199,12 +199,14 @@ fn madvise_rule() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32); + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); #[cfg(target_env = "gnu")] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32); + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); } fn futex_rule() -> BpfRule { diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 2e163a2f8..60ee1ee57 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -227,12 +227,14 @@ fn madvise_rule() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32); + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); #[cfg(target_env = "gnu")] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32); + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); } fn futex_rule() -> BpfRule { diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 42199da2b..9cf683bea 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -209,8 +209,11 @@ impl Request { address_space: &Arc, mem: &Arc>, ) { - let advice = if req_type { + let mem_share = mem.lock().unwrap().mem_share(); + let advice = if req_type && !mem_share { libc::MADV_DONTNEED + } else if req_type { + libc::MADV_REMOVE } else { libc::MADV_WILLNEED }; @@ -311,6 +314,11 @@ impl Request { fn release_pages(&self, mem: &Arc>) { for iov in self.iovec.iter() { + let advice = if mem.lock().unwrap().mem_share() { + libc::MADV_REMOVE + } else { + libc::MADV_DONTNEED + }; let gpa: GuestAddress = iov.iov_base; let hva = match mem.lock().unwrap().get_host_address(gpa) { Some(addr) => addr, @@ -322,7 +330,7 @@ impl Request { memory_advise( hva as *const libc::c_void as *mut _, iov.iov_len as usize, - libc::MADV_DONTNEED, + advice, ); } } @@ -345,13 +353,15 @@ struct BlnMemoryRegion { struct BlnMemInfo { regions: Mutex>, enabled: bool, + mem_share: bool, } impl BlnMemInfo { - fn new() -> BlnMemInfo { + fn new(mem_share: bool) -> BlnMemInfo { BlnMemInfo { regions: Mutex::new(Vec::new()), enabled: false, + mem_share, } } @@ -436,6 +446,11 @@ impl BlnMemInfo { } size } + + /// Get Balloon memory type, shared or private. + fn mem_share(&self) -> bool { + self.mem_share + } } impl Listener for BlnMemInfo { @@ -771,7 +786,7 @@ impl Balloon { /// # Arguments /// /// * `bln_cfg` - Balloon configuration. - pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc) -> Balloon { + pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc, mem_share: bool) -> Balloon { let mut device_features = 1u64 << VIRTIO_F_VERSION_1; if bln_cfg.deflate_on_oom { device_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM; @@ -786,7 +801,7 @@ impl Balloon { actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, interrupt_cb: None, - mem_info: Arc::new(Mutex::new(BlnMemInfo::new())), + mem_info: Arc::new(Mutex::new(BlnMemInfo::new(mem_share))), mem_space, event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), @@ -860,7 +875,6 @@ impl Balloon { impl VirtioDevice for Balloon { /// Realize a balloon device. fn realize(&mut self) -> Result<()> { - self.mem_info = Arc::new(Mutex::new(BlnMemInfo::new())); self.mem_space .register_listener(self.mem_info.clone()) .with_context(|| "Failed to register memory listener defined by balloon device.")?; @@ -1155,7 +1169,7 @@ mod tests { }; let mem_space = address_space_init(); - let mut bln = Balloon::new(&bln_cfg, mem_space); + let mut bln = Balloon::new(&bln_cfg, mem_space, false); assert_eq!(bln.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); @@ -1202,7 +1216,7 @@ mod tests { }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space); + let balloon = Balloon::new(&bln_cfg, mem_space, false); let write_data = [0, 0, 0, 0, 1, 0, 0, 0]; let mut random_data: Vec = vec![0; 8]; let addr = 0x00; @@ -1221,7 +1235,7 @@ mod tests { }; let mem_space = address_space_init(); - let mut balloon = Balloon::new(&bln_cfg, mem_space); + let mut balloon = Balloon::new(&bln_cfg, mem_space, false); let write_data = [1, 0, 0, 0]; let addr = 0x00; assert_eq!(balloon.get_balloon_memory_size(), 0); @@ -1237,10 +1251,10 @@ mod tests { deflate_on_oom: true, free_page_reporting: Default::default(), }; - let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); + let mut bln = Balloon::new(&bln_cfg, mem_space.clone(), false); bln.realize().unwrap(); let ram_fr1 = create_flat_range(0, MEMORY_SIZE, 0); - let blninfo = BlnMemInfo::new(); + let blninfo = BlnMemInfo::new(false); assert!(blninfo .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) .is_ok()); @@ -1417,7 +1431,7 @@ mod tests { deflate_on_oom: true, free_page_reporting: Default::default(), }; - let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); + let mut bln = Balloon::new(&bln_cfg, mem_space.clone(), false); assert!(bln .activate(mem_space, interrupt_cb, &queues, queue_evts) .is_err()); @@ -1431,7 +1445,7 @@ mod tests { blndef.memory_size = 0x8000; blndef.userspace_addr = 0; - let blninfo = BlnMemInfo::new(); + let blninfo = BlnMemInfo::new(false); assert_eq!(blninfo.priority(), 0); blninfo.regions.lock().unwrap().push(blndef); @@ -1440,7 +1454,7 @@ mod tests { let ram_size = 0x800; let ram_fr1 = create_flat_range(0, ram_size, 0); - let blninfo = BlnMemInfo::new(); + let blninfo = BlnMemInfo::new(false); assert!(blninfo .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) .is_ok()); @@ -1482,7 +1496,7 @@ mod tests { free_page_reporting: true, }; let mem_space = address_space_init(); - let mut bln = Balloon::new(&bln_cfg, mem_space); + let mut bln = Balloon::new(&bln_cfg, mem_space, false); assert_eq!(bln.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); -- Gitee From cbd50ac6ae08e6cb28c6fb9bc57969e88d09569d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 6 Sep 2022 20:56:59 +0800 Subject: [PATCH 0255/2187] rtc: fix CMOS initialization failure 1. time_val is always initialized to 0, so rtc_cmos set system clock to 1970-01-01T00:00:00 UTC (0) all the time. 2. UIP bit should be set at last 244us of every second. 3. rtc needs to use monotone time. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 68 ++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 95a710615..3e2b1023f 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::sync::{Arc, Mutex}; +use std::time::{Instant, SystemTime, UNIX_EPOCH}; use acpi::{ AmlBuilder, AmlDevice, AmlEisaId, AmlIoDecode, AmlIoResource, AmlIrqNoFlags, AmlNameDecl, @@ -23,6 +24,8 @@ use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; +use util::time::NANOSECONDS_PER_SECOND; + /// IO port of RTC device to select Register to read/write. pub const RTC_PORT_INDEX: u64 = 0x70; /// IO port of RTC device to read/write data from selected register. @@ -36,7 +39,7 @@ const RTC_SECONDS_ALARM: u8 = 0x01; const RTC_MINUTES: u8 = 0x02; const RTC_MINUTES_ALARM: u8 = 0x03; const RTC_HOURS: u8 = 0x04; -const RTC_HOURS_ARARM: u8 = 0x05; +const RTC_HOURS_ALARM: u8 = 0x05; const RTC_DAY_OF_WEEK: u8 = 0x06; const RTC_DAY_OF_MONTH: u8 = 0x07; const RTC_MONTH: u8 = 0x08; @@ -47,6 +50,11 @@ const RTC_REG_C: u8 = 0x0C; const RTC_REG_D: u8 = 0x0D; const RTC_CENTURY_BCD: u8 = 0x32; +// Update in progress (UIP) bit. +const REG_A_UIP: u8 = 0x80; +// UIP bit held for last 244 us of every second. +const UIP_HOLD_LENGTH: u64 = 8 * NANOSECONDS_PER_SECOND / 32768; + // Index of memory data in RTC static RAM. // 0x15/0x16 stores low/high byte below 1MB, range is [0, 640KB]. const CMOS_BASE_MEM: (u8, u8) = (0x15, 0x16); @@ -59,12 +67,7 @@ const CMOS_MEM_BELOW_4GB: (u8, u8) = (0x34, 0x35); // 0x5B/0x5C/0x5D stores low/middle/high byte of memory above 4GB, unit is 64KB. const CMOS_MEM_ABOVE_4GB: (u8, u8, u8) = (0x5B, 0x5C, 0x5D); -fn get_utc_time() -> libc::tm { - let time_val = 0_i64; - - // Safe because `libc::time` only get time. - unsafe { libc::time(time_val as *mut i64) }; - +fn rtc_time_to_tm(time_val: i64) -> libc::tm { let mut dest_tm = libc::tm { tm_sec: 0, tm_min: 0, @@ -91,6 +94,16 @@ fn bin_to_bcd(src: u8) -> u8 { ((src / 10) << 4) + (src % 10) } +/// Transfer BCD coded decimal to binary coded decimal. +fn bcd_to_bin(src: u8) -> u64 { + if (src >> 4) > 9 || (src & 0x0f) > 9 { + error!("RTC: The BCD coded format is wrong."); + return 0_u64; + } + + (((src >> 4) * 10) + (src & 0x0f)) as u64 +} + #[allow(clippy::upper_case_acronyms)] /// RTC device. pub struct RTC { @@ -106,6 +119,10 @@ pub struct RTC { mem_size: u64, /// The start address of gap. gap_start: u64, + /// The tick offset. + tick_offset: u64, + /// Record the real time. + base_time: Instant, } impl RTC { @@ -122,8 +139,22 @@ impl RTC { }, mem_size: 0, gap_start: 0, + // Since 1970-01-01 00:00:00, it never cause overflow. + tick_offset: SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("time wrong") + .as_secs() as u64, + base_time: Instant::now(), }; + // Set Time frequency divider and Rate selection frequency in Register-A. + // Bits 6-4 = Time frequency divider (010 = 32.768KHz). + // Bits 3-0 = Rate selection frequency (110 = 1.024KHz, 976.562s). + rtc.cmos_data[RTC_REG_A as usize] = 0x26; + + // Set 24 hour mode in Register-B. + rtc.cmos_data[RTC_REG_B as usize] = 0x02; + // Set VRT bit in Register-D, indicates that RAM and time are valid. rtc.cmos_data[RTC_REG_D as usize] = 0x80; @@ -175,7 +206,7 @@ impl RTC { return false; } - let tm = get_utc_time(); + let tm = rtc_time_to_tm(self.get_current_value() as i64); match self.cur_index { RTC_SECONDS => { data[0] = bin_to_bcd(tm.tm_sec as u8); @@ -202,6 +233,18 @@ impl RTC { RTC_CENTURY_BCD => { data[0] = bin_to_bcd(((tm.tm_year + 1900) % 100) as u8); } + RTC_REG_A => { + data[0] = self.cmos_data[RTC_REG_A as usize]; + // UIP(update in progress) bit will be set at last 244us of every second. + if self.update_in_progress() { + data[0] |= REG_A_UIP; + self.inject_interrupt(); + } + } + RTC_REG_C => { + // The interrupt request flag (IRQF), alarm interrupt flag (AF). + data[0] = 1 << 7 | 1 << 5; + } _ => { data[0] = self.cmos_data[self.cur_index as usize]; } @@ -250,6 +293,15 @@ impl RTC { } error!("cmos rtc: failed to get interrupt event fd."); } + + /// Get current clock value. + fn get_current_value(&self) -> u64 { + self.base_time.elapsed().as_secs() + self.tick_offset + } + + fn update_in_progress(&self) -> bool { + self.base_time.elapsed().subsec_nanos() >= (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH) as u32 + } } impl SysBusDevOps for RTC { -- Gitee From 1d7d1c470dac295c17cf899aacb839822a6e0b68 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 20 Oct 2022 15:48:31 +0800 Subject: [PATCH 0256/2187] update rtc base time when guest set rtc Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 3e2b1023f..2fe94354b 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -260,6 +260,11 @@ impl RTC { } match self.cur_index { + RTC_SECONDS | RTC_MINUTES | RTC_HOURS | RTC_DAY_OF_WEEK | RTC_DAY_OF_MONTH + | RTC_MONTH | RTC_YEAR => { + self.cmos_data[self.cur_index as usize] = data[0]; + self.update_rtc_time(); + } RTC_REG_C | RTC_REG_D => { warn!( "Failed to write: read-only register, index {}, data {}", @@ -299,6 +304,35 @@ impl RTC { self.base_time.elapsed().as_secs() + self.tick_offset } + fn update_rtc_time(&mut self) { + let sec = bcd_to_bin(self.cmos_data[RTC_SECONDS as usize]); + let min = bcd_to_bin(self.cmos_data[RTC_MINUTES as usize]); + let hour = bcd_to_bin(self.cmos_data[RTC_HOURS as usize]); + let day = bcd_to_bin(self.cmos_data[RTC_DAY_OF_MONTH as usize]); + let mut mon = bcd_to_bin(self.cmos_data[RTC_MONTH as usize]); + let mut year = bcd_to_bin(self.cmos_data[RTC_YEAR as usize]) + + bcd_to_bin(self.cmos_data[RTC_CENTURY_BCD as usize]) * 100; + + // Converts date to seconds since 1970-01-01 00:00:00. + if mon <= 2 { + mon += 10; + year -= 1; + } else { + mon -= 2; + } + + self.tick_offset = + ((((year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) + year * 365 - 719499) + * 24 + + hour) + * 60 + + min) + * 60 + + sec; + + self.base_time = Instant::now(); + } + fn update_in_progress(&self) -> bool { self.base_time.elapsed().subsec_nanos() >= (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH) as u32 } -- Gitee From 6063be03c126bec2878b841aa32b874e91843c51 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 29 Oct 2022 12:30:58 +0800 Subject: [PATCH 0257/2187] Documation: Modify the description of PMU in live-migration doc Remove the incorrect description of PMU in live-migration doc. Signed-off-by: Xinle.Guo --- docs/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration.md b/docs/migration.md index fbd3a00cc..abcef3a8e 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -100,7 +100,7 @@ Some devices and feature don't support to be migration yet: - `vfio` devices - `balloon` - `mem-shared`,`backend file of memory` -- `pmu`: ongoing PMU measurements +- `pmu` Some device attributes can't be changed: - `virtio-net`: mac -- Gitee From 770e853455bc0c3b2d69b8020a1a7e4ce9f7a7e7 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Sat, 29 Oct 2022 12:35:48 +0800 Subject: [PATCH 0258/2187] memory: modify startup the VM with minimum memory limit. Currently, the minimum memory limit for starting VM is 256Mb. The minimum memory is unsuitable for light virtual platform. So, modify it to 128Mb for this sence. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 2 +- machine_manager/src/config/machine_config.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 4f012a641..1f9b7ab89 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -72,7 +72,7 @@ StratoVirt supports to set the size of VM's memory in cmdline. This allows you to set the size of memory that VM will support. You can choose `G` as unit (default unit is `M`). -Default VM memory size is 256M. The supported VM memory size is among [256M, 512G]. +Default VM memory size is 256M. The supported VM memory size is among [128M, 512G]. ```shell # cmdline diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 2d1ce01ae..eb7065993 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -31,7 +31,7 @@ const DEFAULT_MEMSIZE: u64 = 256; const MAX_NR_CPUS: u64 = 254; const MIN_NR_CPUS: u64 = 1; const MAX_MEMSIZE: u64 = 549_755_813_888; -const MIN_MEMSIZE: u64 = 268_435_456; +const MIN_MEMSIZE: u64 = 134_217_728; pub const M: u64 = 1024 * 1024; pub const G: u64 = 1024 * 1024 * 1024; @@ -177,7 +177,7 @@ impl Default for MachineConfig { impl ConfigCheck for MachineConfig { fn check(&self) -> Result<()> { if self.mem_config.mem_size < MIN_MEMSIZE || self.mem_config.mem_size > MAX_MEMSIZE { - bail!("Memory size must >= 256MiB and <= 512GiB, default unit: MiB, current memory size: {:?} bytes", + bail!("Memory size must >= 128MiB and <= 512GiB, default unit: MiB, current memory size: {:?} bytes", &self.mem_config.mem_size); } -- Gitee From 00b4aed415be25c560fea85d667c8d18aca1c2f0 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 31 Oct 2022 01:06:03 -0400 Subject: [PATCH 0259/2187] pci: return err when plug dev with devfn other than 0 because now we only support hotplugging device with devfn of 0, return error so that the backend notice that the device has not been plugged, which is the same to the frontend. Signed-off-by: Zhang Bo --- pci/src/error.rs | 2 ++ pci/src/root_port.rs | 31 +++++++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/pci/src/error.rs b/pci/src/error.rs index f9efcd729..06a897a83 100644 --- a/pci/src/error.rs +++ b/pci/src/error.rs @@ -31,4 +31,6 @@ pub enum PciError { PciRegister(u64), #[error("Invalid features select 0x{0:x}")] FeaturesSelect(u32), + #[error("HotPlug is not supported for device with devfn {0}")] + HotplugUnsupported(u8), } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 924778e9f..7cf99e49c 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -37,8 +37,8 @@ use super::config::{ }; use crate::bus::PciBus; use crate::hotplug::HotplugOps; -use crate::init_multifunction; use crate::msix::init_msix; +use crate::{init_multifunction, PciError}; use crate::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, PciDevOps, @@ -450,20 +450,23 @@ impl HotplugOps for RootPort { .devfn() .with_context(|| "Failed to get devfn")?; // Only if devfn is equal to 0, hot plugging is supported. - if devfn == 0 { - let offset = self.config.ext_cap_offset; - le_write_set_value_u16( - &mut self.config.config, - (offset + PCI_EXP_SLTSTA) as usize, - PCI_EXP_SLTSTA_PDS | PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP, - )?; - le_write_set_value_u16( - &mut self.config.config, - (offset + PCI_EXP_LNKSTA) as usize, - PCIE_CAP_CLS_2_5G | PCIE_CAP_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, - )?; - self.hotplug_event_notify(); + if devfn != 0 { + return Err(anyhow!(PciError::HotplugUnsupported(devfn))); } + + let offset = self.config.ext_cap_offset; + le_write_set_value_u16( + &mut self.config.config, + (offset + PCI_EXP_SLTSTA) as usize, + PCI_EXP_SLTSTA_PDS | PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP, + )?; + le_write_set_value_u16( + &mut self.config.config, + (offset + PCI_EXP_LNKSTA) as usize, + PCIE_CAP_CLS_2_5G | PCIE_CAP_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, + )?; + self.hotplug_event_notify(); + Ok(()) } -- Gitee From 23ac82e8c6c799e2f24d2ec4d76113f80abbabc1 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 31 Oct 2022 02:18:14 -0400 Subject: [PATCH 0260/2187] virtio-pci: do not set features after being negotiated it's not allowed to set features after having been negoiated, validate that at the backend. Signed-off-by: Zhang Bo --- virtio/src/virtio_pci.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index e4852816a..66467547f 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -307,6 +307,10 @@ impl VirtioPciCommonConfig { self.acked_features_select = value; } COMMON_GF_REG => { + if self.device_status & CONFIG_STATUS_FEATURES_OK != 0 { + error!("it's not allowed to set features after having been negoiated"); + return Ok(()); + } if self.acked_features_select >= MAX_FEATURES_SELECT_NUM { return Err(anyhow!(PciError::FeaturesSelect( self.acked_features_select -- Gitee From 9059aad11d8b6942d7796b85a6ee7e3f6ec69fa2 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 31 Oct 2022 14:30:06 +0800 Subject: [PATCH 0261/2187] machine_manager: Add a qmp command Add a qmp command: query_block_jobs. Signed-off-by: Jinhao Gao --- machine_manager/src/machine.rs | 6 ++++++ machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 73d32569f..a73a85133 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -395,6 +395,12 @@ pub trait DeviceInterface { Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) } + fn query_block_jobs(&self) -> Response { + // Fix me: qmp command call, return none temporarily. + let vec_cmd: Vec = Vec::new(); + Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + } + fn query_gic_capabilities(&self) -> Response { let vec_gic: Vec = Vec::new(); Response::create_response(serde_json::to_value(&vec_gic).unwrap(), None) diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index eb03e8cf0..a8832892f 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -440,6 +440,7 @@ fn qmp_command_exec( (query_block, query_block), (query_named_block_nodes, query_named_block_nodes), (query_blockstats, query_blockstats), + (query_block_jobs, query_block_jobs), (query_gic_capabilities, query_gic_capabilities), (query_iothreads, query_iothreads), (query_migrate, query_migrate), diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 2029f8eb8..c56b1bbea 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -341,6 +341,14 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "query-block-jobs")] + #[strum(serialize = "query-block-jobs")] + query_block_jobs { + #[serde(default)] + arguments: query_block_jobs, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, #[serde(rename = "query-gic-capabilities")] #[strum(serialize = "query-gic-capabilities")] query_gic_capabilities { @@ -1818,6 +1826,25 @@ impl Command for query_blockstats { } } +/// Query jobs of blocks. +/// +/// # Example +/// +/// ```text +/// -> { "execute": "query-block_jobs" } +/// <- {"return":[]} +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct query_block_jobs {} + +impl Command for query_block_jobs { + type Res = Vec; + + fn back(self) -> Vec { + Default::default() + } +} + /// Query capabilities of gic. /// /// # Example -- Gitee From 0e104dd77b5fa36bc47752f5f5c9de627760b9b8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 18 Oct 2022 13:52:55 -0400 Subject: [PATCH 0262/2187] virtio_pci: Set queue_type depends on final driver features The final filtered driver features are subset of device features. We should set queue_type depends on final driver features. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 6ae7630b5..ddbf91181 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -304,11 +304,13 @@ impl VirtioPciCommonConfig { .unwrap() .set_driver_features(self.acked_features_select, value); - if self.acked_features_select == 1 - && virtio_has_feature(u64::from(value) << 32, VIRTIO_F_RING_PACKED) - { - error!("Set packed virtqueue, which is not supported"); - self.queue_type = QUEUE_TYPE_PACKED_VRING; + if self.acked_features_select == 1 { + let features = (device.lock().unwrap().get_driver_features(1) as u64) << 32; + if virtio_has_feature(features, VIRTIO_F_RING_PACKED) { + self.queue_type = QUEUE_TYPE_PACKED_VRING; + } else { + self.queue_type = QUEUE_TYPE_SPLIT_VRING; + } } } COMMON_MSIX_REG => { -- Gitee From d75732ce34e228c3db86e696c51af4884f9a897b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 18 Oct 2022 03:32:36 -0400 Subject: [PATCH 0263/2187] virtio_pci: Fix the value when guest read Q_SIZE field The Q_SIZE field is rd/wr, when guest read this field, device should return the queue size changed by guest before. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index ddbf91181..cf881904e 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -237,7 +237,7 @@ impl VirtioPciCommonConfig { COMMON_Q_SELECT_REG => self.queue_select as u32, COMMON_Q_SIZE_REG => self .get_queue_config() - .map(|config| u32::from(config.max_size))?, + .map(|config| u32::from(config.size))?, COMMON_Q_MSIX_REG => self .get_queue_config() .map(|config| u32::from(config.vector))?, -- Gitee From 6a12fa2dcc0aea1deaecbd5e36a4eb680e9fc9c8 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 31 Oct 2022 14:22:55 +0800 Subject: [PATCH 0264/2187] virtio: optimize some funtions about get_element() 1. Reduce some arguments of get_element(); 2. Distinguish the normal/indirect descriptor in get_element(); 3. Add a test for the optimized code. Signed-off-by: Yan Wang --- virtio/src/queue.rs | 299 ++++++++++++++++++++++++++++++++------------ 1 file changed, 222 insertions(+), 77 deletions(-) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 4d15f8cea..59505c64c 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -241,6 +241,17 @@ const VIRTQ_DESC_F_WRITE: u16 = 0x2; /// This means the buffer contains a list of buffer descriptors. const VIRTQ_DESC_F_INDIRECT: u16 = 0x4; +struct DescInfo { + /// The host virtual address of the descriptor table. + table_host: u64, + /// The size of the descriptor table. + size: u16, + /// The index of the current descriptor table. + index: u16, + /// The descriptor table. + desc: SplitVringDesc, +} + /// Descriptor of split vring. #[repr(C)] #[derive(Default, Clone, Copy)] @@ -371,12 +382,15 @@ impl SplitVringDesc { /// Return true if the indirect descriptor is valid. /// The len can be divided evenly by the size of descriptor and can not be zero. fn is_valid_indirect_desc(&self) -> bool { - if u64::from(self.len) % DESCRIPTOR_LEN == 0 && self.len != 0 { - true - } else { + if self.len == 0 || u64::from(self.len) % DESCRIPTOR_LEN != 0 { error!("The indirect descriptor is invalid, len: {}", self.len); - false + return false; } + if self.write_only() { + error!("Unexpected descriptor for writing only for popping avail ring"); + return false; + } + true } /// Get the num of descriptor in the table of indirect descriptor. @@ -387,20 +401,40 @@ impl SplitVringDesc { /// Get element from descriptor chain. fn get_element( sys_mem: &Arc, - desc_table_host: u64, - queue_size: u16, - index: u16, - mut desc: SplitVringDesc, + desc_info: &DescInfo, cache: &mut Option, elem: &mut Element, ) -> Result<()> { - elem.index = index; + let mut desc_table_host = desc_info.table_host; + let mut desc_size = desc_info.size; + let mut desc = desc_info.desc; + elem.index = desc_info.index; + let mut queue_size = desc_size; + let mut indirect: bool = false; loop { - if elem.desc_num >= queue_size { + if elem.desc_num >= desc_size { break; } + if desc.is_indirect_desc() { + if !desc.is_valid_indirect_desc() { + return Err(anyhow!(VirtioError::QueueDescInvalid)); + } + if !indirect { + indirect = true; + } else { + bail!("Found two indirect descriptor elem in one request"); + } + desc_table_host = match sys_mem.get_host_address(desc.addr) { + Some(addr) => addr, + None => bail!("Failed to get descriptor table entry host address"), + }; + queue_size = desc.get_desc_num(); + desc = Self::next_desc(sys_mem, desc_table_host, queue_size, 0, cache)?; + desc_size = elem.desc_num + queue_size; + } + let iovec = ElemIovec { addr: desc.addr, len: desc.len, @@ -422,49 +456,6 @@ impl SplitVringDesc { Ok(()) } - - /// Get element from indirect descriptor chain. - fn get_indirect_desc( - &self, - sys_mem: &Arc, - index: u16, - cache: &mut Option, - elem: &mut Element, - ) -> Result<()> { - if !self.is_valid_indirect_desc() { - return Err(anyhow!(VirtioError::QueueDescInvalid)); - } - - let desc_num = self.get_desc_num(); - let desc_hva = match sys_mem.get_host_address(self.addr) { - Some(addr) => addr, - None => bail!("Failed to get descriptor table entry host address"), - }; - let desc = Self::next_desc(sys_mem, desc_hva, desc_num, 0, cache)?; - Self::get_element(sys_mem, desc_hva, desc_num, index, desc, cache, elem) - .with_context(|| - format!("Failed to get element from indirect descriptor chain {}, table entry addr: 0x{:X}, size: {}", - index, self.addr.0, desc_num) - ) - } - - /// Get element from normal descriptor chain. - fn get_nonindirect_desc( - &self, - sys_mem: &Arc, - desc_table_host: u64, - queue_size: u16, - index: u16, - cache: &mut Option, - elem: &mut Element, - ) -> Result<()> { - Self::get_element(sys_mem, desc_table_host, queue_size, index, *self, cache, elem).with_context(|| { - format!( - "Failed to get element from normal descriptor chain {}, table addr: 0x{:X}, size: {}", - index, desc_table_host, queue_size - ) - }) - } } impl ByteCode for SplitVringDesc {} @@ -780,31 +771,21 @@ impl SplitVring { desc_index, &mut self.cache, )?; - if desc.is_indirect_desc() { - if desc.write_only() { - bail!("Unexpected descriptor for writing only for popping avail ring"); - } - - desc.get_indirect_desc(sys_mem, desc_index, &mut self.cache, elem) - .map(|elem| { - self.next_avail += Wrapping(1); - elem - }) - .with_context(|| "Failed to get indirect desc for popping avail ring")? - } else { - desc.get_nonindirect_desc( - sys_mem, - self.addr_cache.desc_table_host, - self.actual_size(), - desc_index, - &mut self.cache, - elem, - ) - .map(|elem| { - self.next_avail += Wrapping(1); - elem - })? + let desc_info = DescInfo { + table_host: self.addr_cache.desc_table_host, + size: self.actual_size(), + index: desc_index, + desc, }; + SplitVringDesc::get_element(sys_mem, &desc_info, &mut self.cache, elem).with_context( + || { + format!( + "Failed to get element from descriptor chain {}, table addr: 0x{:X}, size: {}", + desc_info.index, desc_info.table_host, desc_info.size, + ) + }, + )?; + self.next_avail += Wrapping(1); if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { self.set_avail_event(sys_mem) @@ -1700,6 +1681,170 @@ mod tests { } } + #[test] + fn test_pop_avail_04() { + let sys_space = address_space_init(); + + let mut queue_config = QueueConfig::new(QUEUE_SIZE); + queue_config.desc_table = GuestAddress(0); + queue_config.addr_cache.desc_table_host = + sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.addr_cache.avail_ring_host = + sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.used_ring = GuestAddress(align( + (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + + AVAILELEM_LEN * (QUEUE_SIZE as u64), + 4096, + )); + queue_config.addr_cache.used_ring_host = + sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.ready = true; + queue_config.size = QUEUE_SIZE; + let mut vring = SplitVring::new(queue_config); + assert_eq!(vring.is_valid(&sys_space), true); + + // Set the information of index 0 for normal descriptor. + vring + .set_desc(&sys_space, 0, GuestAddress(0x111), 16, VIRTQ_DESC_F_NEXT, 1) + .unwrap(); + + // Set the information of index 1 for normal descriptor. + vring + .set_desc(&sys_space, 1, GuestAddress(0x222), 32, VIRTQ_DESC_F_NEXT, 2) + .unwrap(); + + // Set the incorrect information of index 2 for normal descriptor. + // The VIRTQ_DESC_F_INDIRECT and VIRTQ_DESC_F_NEXT flag can not be + // used together. + vring + .set_desc( + &sys_space, + 2, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + 32, + VIRTQ_DESC_F_INDIRECT | VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT, + 0, + ) + .unwrap(); + + // Set the information of index 0 for indirect descriptor. + set_indirect_desc( + &sys_space, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + GuestAddress(0x444), + 100, + VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, + 1, + ) + .unwrap(); + + // Set the information of index 1 for indirect descriptor. + set_indirect_desc( + &sys_space, + GuestAddress(SYSTEM_SPACE_SIZE / 2 + DESCRIPTOR_LEN), + GuestAddress(0x555), + 200, + VIRTQ_DESC_F_WRITE, + 2, + ) + .unwrap(); + + // Set the index 0 of descriptor to the position 0 for the element of avail ring. + vring.set_avail_ring_elem(&sys_space, 0, 0).unwrap(); + // Set 1 to the idx of avail ring. + vring.set_avail_ring_idx(&sys_space, 1).unwrap(); + + let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; + if let Err(err) = vring.pop_avail(&sys_space, features) { + assert_eq!(err.to_string(), "Failed to get vring element"); + } else { + assert!(false); + } + + // Set the correct information of index 2 for normal descriptor. + vring + .set_desc( + &sys_space, + 2, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + 32, + VIRTQ_DESC_F_INDIRECT, + 0, + ) + .unwrap(); + + // Set the incorrect information of index 1 for indirect descriptor. + // The VIRTQ_DESC_F_INDIRECT flag can not be used in indirect descriptor + // table. + set_indirect_desc( + &sys_space, + GuestAddress(SYSTEM_SPACE_SIZE / 2 + DESCRIPTOR_LEN), + GuestAddress(0x555), + 208, + VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_INDIRECT, + 2, + ) + .unwrap(); + + if let Err(err) = vring.pop_avail(&sys_space, features) { + assert_eq!(err.to_string(), "Failed to get vring element"); + } else { + assert!(false); + } + + // Set the correct information of index 1 for indirect descriptor. + set_indirect_desc( + &sys_space, + GuestAddress(SYSTEM_SPACE_SIZE / 2 + DESCRIPTOR_LEN), + GuestAddress(0x555), + 200, + VIRTQ_DESC_F_WRITE, + 2, + ) + .unwrap(); + + // Check the result of pop_avail(), which has normal and indirect + // descriptor elem. + let elem = match vring.pop_avail(&sys_space, features) { + Ok(ret) => ret, + Err(_) => Element { + index: 1, + desc_num: 0, + out_iovec: Vec::new(), + in_iovec: Vec::new(), + }, + }; + + assert_eq!(elem.index, 0); + assert_eq!(elem.desc_num, 4); + + // Two elem for reading. + assert_eq!(elem.out_iovec.len(), 2); + let elem_iov = elem.out_iovec.get(0).unwrap(); + assert_eq!(elem_iov.addr, GuestAddress(0x111)); + assert_eq!(elem_iov.len, 16); + let elem_iov = elem.out_iovec.get(1).unwrap(); + assert_eq!(elem_iov.addr, GuestAddress(0x222)); + assert_eq!(elem_iov.len, 32); + + // Two elem for writing. + assert_eq!(elem.in_iovec.len(), 2); + let elem_iov = elem.in_iovec.get(0).unwrap(); + assert_eq!(elem_iov.addr, GuestAddress(0x444)); + assert_eq!(elem_iov.len, 100); + let elem_iov = elem.in_iovec.get(1).unwrap(); + assert_eq!(elem_iov.addr, GuestAddress(0x555)); + assert_eq!(elem_iov.len, 200); + + // The event idx of avail ring is equal to get_avail_event. + let event_idx = vring.get_avail_event(&sys_space).unwrap(); + assert_eq!(event_idx, 1); + let avail_idx = vring.get_avail_idx(&sys_space).unwrap(); + assert_eq!(avail_idx, 1); + } + #[test] fn test_add_used() { let sys_space = address_space_init(); -- Gitee From 9def9275c7a370aff622bff8c2d4d6c669f287fe Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 31 Oct 2022 14:26:07 +0800 Subject: [PATCH 0265/2187] virtio: INDIRECT and NEXT flag should not be used together Signed-off-by: Yan Wang --- virtio/src/queue.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 59505c64c..c12278678 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -390,6 +390,10 @@ impl SplitVringDesc { error!("Unexpected descriptor for writing only for popping avail ring"); return false; } + if self.has_next() { + error!("INDIRECT and NEXT flag should not be used together"); + return false; + } true } @@ -1679,6 +1683,23 @@ mod tests { } else { assert!(false); } + + // The INDIRECT and NEXT flag should not be used together. + vring + .set_desc( + &sys_space, + 0, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + 48, + VIRTQ_DESC_F_INDIRECT | VIRTQ_DESC_F_NEXT, + 0, + ) + .unwrap(); + if let Err(err) = vring.pop_avail(&sys_space, features) { + assert_eq!(err.to_string(), "Failed to get vring element"); + } else { + assert!(false); + } } #[test] -- Gitee From 7561543aad91602a24a825864752a6b8bfacfcb5 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 31 Oct 2022 14:32:46 +0800 Subject: [PATCH 0266/2187] virtio: the device-writable descriptor elems must behind the device-readable descriptor elem Signed-off-by: Yan Wang --- virtio/src/queue.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index c12278678..e19c27ef8 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -415,6 +415,7 @@ impl SplitVringDesc { elem.index = desc_info.index; let mut queue_size = desc_size; let mut indirect: bool = false; + let mut write_elem_count: u32 = 0; loop { if elem.desc_num >= desc_size { @@ -446,7 +447,11 @@ impl SplitVringDesc { if desc.write_only() { elem.in_iovec.push(iovec); + write_elem_count += 1; } else { + if write_elem_count > 0 { + bail!("Invalid order of the descriptor elem"); + } elem.out_iovec.push(iovec); } elem.desc_num += 1; @@ -1700,6 +1705,34 @@ mod tests { } else { assert!(false); } + + // The device-writable desc elems must behind the device-readable desc elems. + vring + .set_desc( + &sys_space, + 0, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + 48, + VIRTQ_DESC_F_INDIRECT, + 0, + ) + .unwrap(); + + // Set the information of index 0 for indirect descriptor. + set_indirect_desc( + &sys_space, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + GuestAddress(0x444), + 100, + VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, + 1, + ) + .unwrap(); + if let Err(err) = vring.pop_avail(&sys_space, features) { + assert_eq!(err.to_string(), "Failed to get vring element"); + } else { + assert!(false); + } } #[test] -- Gitee From 533ea7cdf47c871a144dadf13684be0f765f0d6e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 26 Oct 2022 14:56:16 +0800 Subject: [PATCH 0267/2187] virtio-net: set the tap offload after feature negotiated It needs to use the negotiated features to decide which TUN feature needs to set offload. Signed-off-by: Yan Wang --- util/src/tap.rs | 1 + virtio/src/lib.rs | 4 ++++ virtio/src/net.rs | 58 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/util/src/tap.rs b/util/src/tap.rs index 369caaae6..4db3c98ba 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -23,6 +23,7 @@ use anyhow::Result; pub const TUN_F_CSUM: u32 = 1; pub const TUN_F_TSO4: u32 = 2; pub const TUN_F_TSO6: u32 = 4; +pub const TUN_F_TSO_ECN: u32 = 8; pub const TUN_F_UFO: u32 = 16; pub const TUN_F_VIRTIO: u32 = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_UFO; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 0e0d710a3..fa0f4df5e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -112,6 +112,10 @@ pub const VIRTIO_NET_F_GUEST_CSUM: u32 = 1; pub const VIRTIO_NET_F_MAC: u32 = 5; /// Driver can receive TSOv4. pub const VIRTIO_NET_F_GUEST_TSO4: u32 = 7; +/// Driver can receive TSOv6. +pub const VIRTIO_NET_F_GUEST_TSO6: u32 = 8; +/// Driver can receive TSO with ECN. +pub const VIRTIO_NET_F_GUEST_ECN: u32 = 9; /// Driver can receive UFO. pub const VIRTIO_NET_F_GUEST_UFO: u32 = 10; /// Device can receive TSOv4. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 0d333c48f..4ccff423e 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -22,9 +22,9 @@ use super::{ VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, - VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::virtio_has_feature; use crate::VirtioError; @@ -42,7 +42,9 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::num_ops::{read_u32, write_u32}; -use util::tap::{Tap, IFF_MULTI_QUEUE, TUN_F_VIRTIO}; +use util::tap::{ + Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, +}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; @@ -799,9 +801,6 @@ pub fn create_tap( })? }; - tap.set_offload(TUN_F_VIRTIO) - .with_context(|| "Failed to set tap offload")?; - let vnet_hdr_size = mem::size_of::() as u32; tap.set_hdr_size(vnet_hdr_size) .with_context(|| "Failed to set tap hdr size")?; @@ -812,6 +811,31 @@ pub fn create_tap( Ok(Some(taps)) } +/// Get the tap offload flags from driver features. +/// +/// # Arguments +/// +/// * `features` - The driver features. +fn get_tap_offload_flags(features: u64) -> u32 { + let mut flags: u32 = 0; + if virtio_has_feature(features, VIRTIO_NET_F_GUEST_CSUM) { + flags |= TUN_F_CSUM; + } + if virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4) { + flags |= TUN_F_TSO4; + } + if virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6) { + flags |= TUN_F_TSO6; + } + if virtio_has_feature(features, VIRTIO_NET_F_GUEST_ECN) { + flags |= TUN_F_TSO_ECN; + } + if virtio_has_feature(features, VIRTIO_NET_F_GUEST_UFO) { + flags |= TUN_F_UFO; + } + flags +} + impl VirtioDevice for Net { /// Realize virtio network device. fn realize(&mut self) -> Result<()> { @@ -979,6 +1003,10 @@ impl VirtioDevice for Net { )?; } + // The features about offload is included in bits 0 to 31. + let features = self.get_driver_features(0_u32); + let flags = get_tap_offload_flags(features as u64); + let mut senders = Vec::new(); let queue_pairs = queue_num / 2; for index in 0..queue_pairs { @@ -990,6 +1018,11 @@ impl VirtioDevice for Net { let (sender, receiver) = channel(); senders.push(sender); + if let Some(tap) = self.taps.as_ref().map(|t| t[index].clone()) { + tap.set_offload(flags) + .with_context(|| "Failed to set tap offload")?; + } + let mut handler = NetIoHandler { rx: RxVirtio::new(rx_queue, rx_queue_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), @@ -1024,6 +1057,17 @@ impl VirtioDevice for Net { .downcast_ref::() .unwrap() .clone(); + + // Set tap offload. + // The features about offload is included in bits 0 to 31. + let features = self.get_driver_features(0_u32); + let flags = get_tap_offload_flags(features as u64); + if let Some(taps) = &self.taps { + for (_, tap) in taps.iter().enumerate() { + tap.set_offload(flags) + .with_context(|| "Failed to set tap offload")?; + } + } } else { self.net_cfg = Default::default(); } -- Gitee From 64e62bb7ec7315760b348f74a153691b2c688410 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 15:42:05 -0400 Subject: [PATCH 0268/2187] virtio-pci: Reset common_config completely when reset device Some fields of VirtioPciCommonConfig are not reset when guest resets device, let's do it in a separate method. Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 4 ++++ virtio/src/virtio_pci.rs | 21 +++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index e19c27ef8..f7e64fc1a 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -111,6 +111,10 @@ impl QueueConfig { last_signal_used: 0, } } + + pub fn reset(&mut self) { + *self = Self::new(self.max_size); + } } /// IO vector element which contains the information of a descriptor. diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index cf881904e..69c085d31 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -172,6 +172,18 @@ impl VirtioPciCommonConfig { } } + fn reset(&mut self) { + self.features_select = 0; + self.acked_features_select = 0; + self.interrupt_status.store(0_u32, Ordering::SeqCst); + self.device_status = 0; + self.config_generation = 0; + self.queue_select = 0; + self.msix_config.store(0_u16, Ordering::SeqCst); + self.queue_type = QUEUE_TYPE_SPLIT_VRING; + self.queues_config.iter_mut().for_each(|q| q.reset()); + } + fn check_device_status(&self, set: u32, clr: u32) -> bool { self.device_status & (set | clr) == set } @@ -329,14 +341,7 @@ impl VirtioPciCommonConfig { } self.device_status = value; if self.device_status == 0 { - self.queues_config.iter_mut().for_each(|q| { - q.ready = false; - q.vector = 0; - q.avail_ring = GuestAddress(0); - q.desc_table = GuestAddress(0); - q.used_ring = GuestAddress(0); - }); - self.msix_config.store(0_u16, Ordering::SeqCst) + self.reset(); } } COMMON_Q_SELECT_REG => { -- Gitee From 3d42d6df01f1afb33bd0433ba5d3dd1583f0e1fd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 17:04:01 -0400 Subject: [PATCH 0269/2187] virtio-pci: Set isr bit 1 before send config change notification Should always set isr bit 1 before send config change notification regardless MSI-X is enabled or not. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 69c085d31..6c9bfb786 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -42,7 +42,8 @@ use crate::{ use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, - VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, + VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_MMIO_INT_CONFIG, VIRTIO_TYPE_BLOCK, + VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -593,11 +594,18 @@ impl VirtioPciDevice { let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, queue: Option<&Queue>| { let vector = match int_type { - VirtioInterruptType::Config => cloned_common_cfg - .lock() - .unwrap() - .msix_config - .load(Ordering::SeqCst), + VirtioInterruptType::Config => { + cloned_common_cfg + .lock() + .unwrap() + .interrupt_status + .fetch_or(VIRTIO_MMIO_INT_CONFIG, Ordering::SeqCst); + cloned_common_cfg + .lock() + .unwrap() + .msix_config + .load(Ordering::SeqCst) + } VirtioInterruptType::Vring => { queue.map_or(0, |q| q.vring.get_queue_config().vector) } -- Gitee From a7d9c96c1aaa37972f354f0ea116b48b9d564045 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 31 Oct 2022 03:20:01 -0400 Subject: [PATCH 0270/2187] virtio-pci: Prevent driver set illegal value to queue_enable The only legal value is 1. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 6c9bfb786..364a20426 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -353,9 +353,14 @@ impl VirtioPciCommonConfig { COMMON_Q_SIZE_REG => self .get_mut_queue_config() .map(|config| config.size = value as u16)?, - COMMON_Q_ENABLE_REG => self - .get_mut_queue_config() - .map(|config| config.ready = value == 1)?, + COMMON_Q_ENABLE_REG => { + if value != 1 { + error!("Driver set illegal value for queue_enable {}", value); + return Ok(()); + } + self.get_mut_queue_config() + .map(|config| config.ready = true)?; + } COMMON_Q_MSIX_REG => self .get_mut_queue_config() .map(|config| config.vector = value as u16)?, -- Gitee From 068289c1e96f64b6f79cee9184f89009806bbd43 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 31 Oct 2022 03:46:51 -0400 Subject: [PATCH 0271/2187] virtio-pci: Prevent driver clear a device status bit .. except device reset operation. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 364a20426..492aa02ff 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -340,6 +340,11 @@ impl VirtioPciCommonConfig { return Ok(()); } } + if value != 0 && (self.device_status & !value) != 0 { + error!("Driver must not clear a device status bit"); + return Ok(()); + } + self.device_status = value; if self.device_status == 0 { self.reset(); -- Gitee From be4806ca8f6930fbbba3f80d7e13639144e5a63a Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Mon, 31 Oct 2022 20:43:45 +0800 Subject: [PATCH 0272/2187] test_microvm_balloon: Fix the value of the setting balloon Since the value of balloon must be an integer multiple of the size of the guest memory page, set the balloon value in the testcase to be an integer multiple of 64KB to meet the guest requirements of 4KB, 16KB, and 64KB. Signed-off-by: Zuo Xiaoxian --- .../testcases/microvm/functional/test_microvm_balloon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py b/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py index 1afed0ebf..eca35c54f 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py @@ -76,7 +76,7 @@ def test_microvm_balloon(microvm): resp = test_vm.query_balloon() ori = int(resp["return"]["actual"]) - resp = test_vm.balloon_set(value=814748368) + resp = test_vm.balloon_set(value=814743552) time.sleep(5) test_vm.event_wait(name='BALLOON_CHANGED', timeout=2.0) resp = test_vm.query_balloon() -- Gitee From 82df9e85a95f759860653cd61db2b172092bc0ae Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 11:32:25 -0400 Subject: [PATCH 0273/2187] virtio: Use override semantics when set driver features The set_driver_features should have an override semantics, which means new value should override old value. Extract helper func into trait to drop duplicated code. Signed-off-by: Keqian Zhu --- virtio/src/balloon.rs | 12 +++--------- virtio/src/block.rs | 9 ++------- virtio/src/console.rs | 12 +++--------- virtio/src/gpu.rs | 12 +++--------- virtio/src/lib.rs | 19 +++++++++++++++++++ virtio/src/net.rs | 12 +++--------- virtio/src/rng.rs | 12 +++--------- virtio/src/scsi/controller.rs | 15 +++------------ virtio/src/vhost/kernel/net.rs | 14 ++------------ virtio/src/vhost/kernel/vsock.rs | 11 ++--------- virtio/src/vhost/user/block.rs | 14 ++------------ virtio/src/vhost/user/fs.rs | 13 ++----------- virtio/src/vhost/user/net.rs | 14 ++------------ virtio/src/virtio_mmio.rs | 9 ++------- virtio/src/virtio_pci.rs | 9 ++------- 15 files changed, 53 insertions(+), 134 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 9cf683bea..d23a66fd4 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -34,7 +34,7 @@ use util::{ loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }, - num_ops::{read_u32, round_down, write_u32}, + num_ops::{read_u32, round_down}, seccomp::BpfRule, unix::host_page_size, }; @@ -912,13 +912,7 @@ impl VirtioDevice for Balloon { /// * `page` - Selector of feature. /// * `value` - Value to be set. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.device_features; - if unrequested_features != 0 { - warn!("Received acknowledge request for unknown feature: {:x}", v); - v &= !unrequested_features; - } - self.driver_features |= v; + self.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. @@ -1182,7 +1176,7 @@ mod tests { let fts = bln.get_device_features(1); assert_eq!(fts, (feature >> 32) as u32); bln.driver_features = 0; - bln.device_features = 1; + bln.device_features = 1 | 1 << 32; bln.set_driver_features(0, 1); assert_eq!(bln.driver_features, 1); assert_eq!(bln.driver_features, bln.get_driver_features(0) as u64); diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 9fee358a5..b9ba91991 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -46,7 +46,7 @@ use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Number of virtqueues. const QUEUE_NUM_BLK: usize = 1; @@ -1047,12 +1047,7 @@ impl VirtioDevice for Block { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.state.device_features; - if unrequested_features != 0 { - v &= !unrequested_features; - } - self.state.driver_features |= v; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 128efcb48..9f4a902b8 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -23,7 +23,7 @@ use crate::VirtioError; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use devices::legacy::{Chardev, InputReceiver}; -use log::{debug, error, warn}; +use log::{debug, error}; use machine_manager::{ config::{ChardevType, VirtioConsole}, event_loop::EventLoop, @@ -32,7 +32,7 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -366,13 +366,7 @@ impl VirtioDevice for Console { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.state.device_features; - if unrequested_features != 0 { - warn!("Received acknowledge request with unknown feature for console."); - v &= !unrequested_features; - } - self.state.driver_features |= v; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index da7aae94f..bdc8b9888 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -17,7 +17,7 @@ use super::{ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; -use log::{error, warn}; +use log::error; use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc}; @@ -32,7 +32,7 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use util::pixman::{ pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits, pixman_image_get_data, pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, @@ -1736,13 +1736,7 @@ impl VirtioDevice for Gpu { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.state.device_features; - if unrequested_features != 0 { - warn!("Received acknowledge request with unknown feature: {:x}", v); - v &= !unrequested_features; - } - self.state.driver_features |= v; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index fa0f4df5e..eedbffa06 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -65,6 +65,7 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use anyhow::bail; use machine_manager::config::ConfigCheck; +use util::num_ops::write_u32; use vmm_sys_util::eventfd::EventFd; /// Check if the bit of features is configured. @@ -242,6 +243,24 @@ pub trait VirtioDevice: Send { /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32; + /// Get checked driver features before set the value at the page. + fn checked_driver_features(&mut self, page: u32, value: u32) -> u64 { + let mut v = value; + let unsupported_features = value & !self.get_device_features(page); + if unsupported_features != 0 { + warn!( + "Receive acknowlege request with unknown feature: {:x}", + write_u32(value, page) + ); + v &= !unsupported_features; + } + if page == 0 { + (self.get_driver_features(1) as u64) << 32 | (v as u64) + } else { + (v as u64) << 32 | (self.get_driver_features(0) as u64) + } + } + /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32); diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 4ccff423e..80bc2f553 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -30,7 +30,7 @@ use crate::virtio_has_feature; use crate::VirtioError; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; -use log::{error, warn}; +use log::error; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, @@ -41,7 +41,7 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use util::tap::{ Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, }; @@ -932,13 +932,7 @@ impl VirtioDevice for Net { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.state.device_features; - if unrequested_features != 0 { - warn!("Received acknowledge request with unknown feature: {:x}", v); - v &= !unrequested_features; - } - self.state.driver_features |= v; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index f18ab9c82..3c0309f18 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -25,9 +25,9 @@ use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; -use log::{error, warn}; +use log::error; use migration_derive::{ByteCode, Desc}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -329,13 +329,7 @@ impl VirtioDevice for Rng { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.state.device_features; - if unrequested_features != 0 { - warn!("Received acknowledge request with unknown feature: {:x}", v); - v &= !unrequested_features; - } - self.state.driver_features |= v; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index addc3c52c..1b97ac9af 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -26,7 +26,7 @@ use super::super::{ use crate::ScsiBus::{virtio_scsi_get_lun, ScsiBus, ScsiRequest, EMULATE_SCSI_OPS, GOOD}; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; -use log::{error, info, warn}; +use log::{error, info}; use machine_manager::{ config::{ConfigCheck, ScsiCntlrConfig}, event_loop::EventLoop, @@ -36,7 +36,7 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Virtio Scsi Controller has 1 ctrl queue, 1 event queue and at least 1 cmd queue. @@ -200,16 +200,7 @@ impl VirtioDevice for ScsiCntlr { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut features = write_u32(value, page); - let unrequested_features = features & !self.state.device_features; - if unrequested_features != 0 { - warn!( - "Received acknowledge request with unsupported feature for virtio scsi: 0x{:x}", - features - ); - features &= !unrequested_features; - } - self.state.driver_features |= features; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 66da16804..7b5bb323c 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -19,11 +19,10 @@ use std::sync::{Arc, Mutex}; use crate::error::VirtioError; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; -use log::warn; use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; @@ -207,16 +206,7 @@ impl VirtioDevice for Net { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut features = write_u32(value, page); - let unsupported_features = features & !self.device_features; - if unsupported_features != 0 { - warn!( - "Received acknowledge request with unsupported feature for vhost net: 0x{:x}", - features - ); - features &= !unsupported_features; - } - self.driver_features |= features; + self.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index e8d6afac8..59048878e 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -15,13 +15,12 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; -use log::warn; use machine_manager::{config::VsockConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; @@ -193,13 +192,7 @@ impl VirtioDevice for Vsock { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut features = write_u32(value, page); - let unsupported_features = features & !self.state.device_features; - if unsupported_features != 0 { - warn!("Unsupported feature ack (Vsock): {:x}", features); - features &= !unsupported_features; - } - self.state.driver_features |= features; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 80a69d2c9..8f9939fb7 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -17,12 +17,11 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use log::warn; use machine_manager::config::BlkDevConfig; use machine_manager::event_loop::EventLoop; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; @@ -244,16 +243,7 @@ impl VirtioDevice for Block { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut features = write_u32(value, page); - let unsupported_features = features & !self.state.device_features; - if unsupported_features != 0 { - warn!( - "Received acknowledge request with unsupported feature for vhost-user blk: 0x{:x}", - features - ); - features &= !unsupported_features; - } - self.state.driver_features |= features; + self.state.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 8f0cc08d5..698f4be02 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -33,7 +33,7 @@ use machine_manager::{ }; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; use super::super::{VhostNotify, VhostOps}; @@ -175,16 +175,7 @@ impl VirtioDevice for Fs { } fn set_driver_features(&mut self, page: u32, value: u32) { - let mut features = write_u32(value, page); - let unsupported_features = features & !self.avail_features; - if unsupported_features != 0 { - warn!( - "Received acknowledge request with unsupported feature for virtio fs: 0x{:x}", - features - ); - features &= !unsupported_features; - } - self.acked_features |= features; + self.acked_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index e164017e9..d42770235 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -16,11 +16,10 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use log::warn; use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::{read_u32, write_u32}; +use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::super::super::{ @@ -189,16 +188,7 @@ impl VirtioDevice for Net { /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - let mut features = write_u32(value, page); - let unsupported_features = features & !self.device_features; - if unsupported_features != 0 { - warn!( - "Received acknowledge request with unsupported feature for vhost net: 0x{:x}", - features - ); - features &= !unsupported_features; - } - self.driver_features |= features; + self.driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index c1b1992d0..b4c441a13 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -729,7 +729,7 @@ mod tests { use std::io::Write; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use util::num_ops::{read_u32, write_u32}; + use util::num_ops::read_u32; use super::*; use crate::VIRTIO_TYPE_BLOCK; @@ -812,12 +812,7 @@ mod tests { } fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.device_features; - if unrequested_features != 0 { - v &= !unrequested_features; - } - self.driver_features |= v; + self.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 492aa02ff..e4852816a 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1514,7 +1514,7 @@ mod tests { config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC}, le_read_u16, }; - use util::num_ops::{read_u32, write_u32}; + use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::*; @@ -1562,12 +1562,7 @@ mod tests { } fn set_driver_features(&mut self, page: u32, value: u32) { - let mut v = write_u32(value, page); - let unrequested_features = v & !self.device_features; - if unrequested_features != 0 { - v &= !unrequested_features; - } - self.driver_features |= v; + self.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { -- Gitee From dc48efbfe47cad6839b57be556dacb88007d2769 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 20:40:29 -0400 Subject: [PATCH 0274/2187] virtio: Fix vring areas overlap check There is no specific order of vring areas, so just check whether they are overlapped. Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 51 ++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index f7e64fc1a..7cbe2177e 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -670,6 +670,15 @@ impl SplitVring { (new - used_event_idx - Wrapping(1)) < (new - old) } + fn is_overlap( + start1: GuestAddress, + end1: GuestAddress, + start2: GuestAddress, + end2: GuestAddress, + ) -> bool { + !(start1 >= end2 || start2 >= end1) + } + fn is_invalid_memory(&self, sys_mem: &Arc, actual_size: u64) -> bool { let desc_table_end = match checked_offset_mem(sys_mem, self.desc_table, DESCRIPTOR_LEN * actual_size) { @@ -702,25 +711,39 @@ impl SplitVring { } }; - if let Err(ref e) = checked_offset_mem( + let desc_used_end = match checked_offset_mem( sys_mem, self.used_ring, VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * actual_size, ) { - error!( - "used ring is out of bounds: start:0x{:X} size:{} {:?}", - self.used_ring.raw_value(), - VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * actual_size, - e - ); - return true; - } + Ok(addr) => addr, + Err(ref e) => { + error!( + "used ring is out of bounds: start:0x{:X} size:{} {:?}", + self.used_ring.raw_value(), + VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * actual_size, + e, + ); + return true; + } + }; - if self.desc_table >= self.avail_ring - || self.avail_ring >= self.used_ring - || desc_table_end > self.avail_ring - || desc_avail_end > self.used_ring - { + if SplitVring::is_overlap( + self.desc_table, + desc_table_end, + self.avail_ring, + desc_avail_end, + ) || SplitVring::is_overlap( + self.avail_ring, + desc_avail_end, + self.used_ring, + desc_used_end, + ) || SplitVring::is_overlap( + self.desc_table, + desc_table_end, + self.used_ring, + desc_used_end, + ) { error!("The memory of descriptor table: 0x{:X}, avail ring: 0x{:X} or used ring: 0x{:X} is overlapped. queue size:{}", self.desc_table.raw_value(), self.avail_ring.raw_value(), self.used_ring.raw_value(), actual_size); return true; -- Gitee From ef929b343eba142e4a19c636cfaf8ca7dd7a628f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 20:46:42 -0400 Subject: [PATCH 0275/2187] virtio: Report err when chained desc contains too many ... Which means there are desc loop, we can't handle that. Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 7cbe2177e..374bcf2aa 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -423,7 +423,7 @@ impl SplitVringDesc { loop { if elem.desc_num >= desc_size { - break; + bail!("The element desc number exceeds max allowed"); } if desc.is_indirect_desc() { -- Gitee From 40536748986e4f045d3ef01f8bc765fd1876fdc9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 20:50:29 -0400 Subject: [PATCH 0276/2187] virtio: Ignore write flag when desc has indirect flag Refer to virtio 1.2 spec at chapter 2.7.5.3.2 Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 374bcf2aa..1e0b0db1d 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -390,10 +390,6 @@ impl SplitVringDesc { error!("The indirect descriptor is invalid, len: {}", self.len); return false; } - if self.write_only() { - error!("Unexpected descriptor for writing only for popping avail ring"); - return false; - } if self.has_next() { error!("INDIRECT and NEXT flag should not be used together"); return false; -- Gitee From a1b470f713d091bbed2be11d239e8b97a6903b4d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 01:19:02 -0400 Subject: [PATCH 0277/2187] virtio: Place a read memory barrier after avail index read There may has processor prefetching situation, we must prevent that so as to make sure the descriptor is complete. Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 1e0b0db1d..e42499e4f 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -852,6 +852,9 @@ impl VringOps for SplitVring { bail!("failed to pop avail: empty!"); } + // Make sure descriptor read does not bypass avail index read. + fence(Ordering::Acquire); + let mut element = Element::new(0); self.get_vring_element(sys_mem, features, &mut element) .with_context(|| "Failed to get vring element")?; -- Gitee From 45a9d7ed1e9d1ec5d5c96abad673026337301f7f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 03:43:26 -0400 Subject: [PATCH 0278/2187] virtio: Place a memory barrier after update used index Make sure used index is exposed before notifying guest. Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index e42499e4f..a4e27316f 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -893,6 +893,9 @@ impl VringOps for SplitVring { ) .with_context(|| "Failed to write next used idx")?; + // Make sure used index is exposed before notifying guest. + fence(Ordering::SeqCst); + Ok(()) } -- Gitee From fdd8b07bcc9596493ad4b94b28643ad31653b144 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 29 Oct 2022 22:22:44 +0800 Subject: [PATCH 0279/2187] virtio: Add some check for desc syntax 1. Zero sized desc buffers are not allowed. 2. Max total len of a desc chain is 4GB. Signed-off-by: Keqian Zhu --- virtio/src/queue.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index a4e27316f..6dcbe6066 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -30,6 +30,8 @@ const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; /// Packed Virtqueue. pub const QUEUE_TYPE_PACKED_VRING: u16 = 2; +/// Max total len of a descriptor chain. +const DESC_CHAIN_MAX_TOTAL_LEN: u64 = 1u64 << 32; fn checked_offset_mem( mmio_space: &Arc, @@ -320,6 +322,10 @@ impl SplitVringDesc { queue_size: u16, cache: &mut Option, ) -> bool { + if self.len == 0 { + error!("Zero sized buffers are not allowed"); + return false; + } let mut miss_cached = true; if let Some(reg_cache) = cache { let base = self.addr.0; @@ -416,6 +422,7 @@ impl SplitVringDesc { let mut queue_size = desc_size; let mut indirect: bool = false; let mut write_elem_count: u32 = 0; + let mut desc_total_len: u64 = 0; loop { if elem.desc_num >= desc_size { @@ -455,6 +462,7 @@ impl SplitVringDesc { elem.out_iovec.push(iovec); } elem.desc_num += 1; + desc_total_len += iovec.len as u64; if desc.has_next() { desc = Self::next_desc(sys_mem, desc_table_host, queue_size, desc.next, cache)?; @@ -463,6 +471,10 @@ impl SplitVringDesc { } } + if desc_total_len > DESC_CHAIN_MAX_TOTAL_LEN { + bail!("Find a descriptor chain longer than 4GB in total"); + } + Ok(()) } } -- Gitee From 2abb33b0d51419bc5a684a6cc535201fb274f2e5 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 31 Oct 2022 10:22:48 +0800 Subject: [PATCH 0280/2187] boot time: add new function to log boot time 1. Add `MAGIC_SIGNAL_GUEST_BOOT = 0x3ff` for capture IO port. The guest kernel writes 0x3ff IO region before and after start kernel. And it will trap to StratVirt to record kernel boot timestamp. 2. Add `MAGIC_SIGNAL_GUEST_BOOT = 0x9000f00` for capture MMIO region trap. 3. The other two constants are used to verify that the kernel starts or complete. Signed-off-by: Xinle.Guo --- Cargo.toml | 1 + cpu/Cargo.toml | 4 ++++ cpu/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ docs/config_guidebook.md | 7 +++++++ machine/Cargo.toml | 1 + 5 files changed, 45 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b7c3ddbf0..37d98cb13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ path = "vhost_user_fs/src/main.rs" [features] default = [] +boot_time = ["machine/boot_time"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index acc473857..6edb9ec56 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -22,3 +22,7 @@ util = { path = "../util" } [dev-dependencies] serial_test = "0.5.1" + +[features] +default = [] +boot_time = [] \ No newline at end of file diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index c7f3ac8cc..8aa37fa46 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -87,6 +87,19 @@ const VCPU_RESET_SIGNAL: i32 = 35; #[cfg(target_env = "musl")] const VCPU_RESET_SIGNAL: i32 = 36; +/// Watch `0x3ff` IO port to record the magic value trapped from guest kernel. +#[cfg(all(target_arch = "x86_64", feature = "boot_time"))] +const MAGIC_SIGNAL_GUEST_BOOT: u64 = 0x3ff; +/// Watch Uart MMIO region to record the magic value trapped from guest kernel. +#[cfg(all(target_arch = "aarch64", feature = "boot_time"))] +const MAGIC_SIGNAL_GUEST_BOOT: u64 = 0x9000f00; +/// The boot start value can be verified before kernel start. +#[cfg(feature = "boot_time")] +const MAGIC_VALUE_SIGNAL_GUEST_BOOT_START: u8 = 0x01; +/// The boot complete value can be verified before init guest userspace. +#[cfg(feature = "boot_time")] +const MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE: u8 = 0x02; + /// State for `CPU` lifecycle. #[derive(Copy, Clone, Debug, PartialEq)] pub enum CpuLifecycleState { @@ -467,12 +480,18 @@ impl CPUInterface for CPU { } #[cfg(target_arch = "x86_64")] VcpuExit::IoOut(addr, data) => { + #[cfg(feature = "boot_time")] + capture_boot_signal(addr as u64, data); + vm.lock().unwrap().pio_out(u64::from(addr), data); } VcpuExit::MmioRead(addr, data) => { vm.lock().unwrap().mmio_read(addr, data); } VcpuExit::MmioWrite(addr, data) => { + #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] + capture_boot_signal(addr, data); + vm.lock().unwrap().mmio_write(addr, data); } #[cfg(target_arch = "x86_64")] @@ -801,6 +820,19 @@ fn trace_cpu_boot_config(cpu_boot_config: &CPUBootConfig) { util::ftrace!(trace_CPU_boot_config, "{:#?}", cpu_boot_config); } +/// Capture the boot signal that trap from guest kernel, and then record +/// kernel boot timestamp. +#[cfg(feature = "boot_time")] +fn capture_boot_signal(addr: u64, data: &[u8]) { + if addr == MAGIC_SIGNAL_GUEST_BOOT { + if data[0] == MAGIC_VALUE_SIGNAL_GUEST_BOOT_START { + info!("Kernel starts to boot!"); + } else if data[0] == MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE { + info!("Kernel boot complete!"); + } + } +} + #[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1f9b7ab89..41034ac62 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -936,3 +936,10 @@ white list. However, these cmdlines never function. Apart from the above commands, some arguments are playing the same roles. Like 'format' and 'bootindex' for virtio-blk; 'chassis' for pcie-root-port; 'sockets', 'cores' and 'threads' for smp; 'accel' and 'usb' for machine; "format" for pflash device. + +## 8. Debug boot time +Currently, measurement of guest boot up time is supported. The guest kernel writes different +values to specific IO/MMIO regions, and it will trap to `stratovirt`, we can record the timestamp +of kernel start or kernel boot complete. + +See [Debug_Boot_Time](https://gitee.com/openeuler/stratovirt/wikis/%E6%B5%8B%E8%AF%95%E6%96%87%E6%A1%A3/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95-%E5%86%B7%E5%90%AF%E5%8A%A8%E6%97%B6%E9%97%B4) for more details. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 4f6465d6b..25cb197c8 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -39,3 +39,4 @@ vnc = { path = "../vnc" } [features] default = ["qmp"] qmp = [] +boot_time = ["cpu/boot_time"] -- Gitee From 1f660ad9f43b088a3958760d3da76aee22711652 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 1 Nov 2022 12:51:48 +0800 Subject: [PATCH 0281/2187] pcie: hot unplug: skip unplugging while power indicator is blinking when power indicator is blinking, it means that the guest is still doing pluging or unpluging job, do not unplug the device then. Signed-off-by: Zhang Bo --- pci/src/config.rs | 3 ++- pci/src/root_port.rs | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index aa5199bbb..c3c87de1c 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -222,7 +222,8 @@ const PCIE_CAP_SLOT_NUM_SHIFT: u32 = 19; const PCIE_CAP_SLOT_AIC_MASK: u16 = 0x00c0; const PCIE_CAP_SLOT_AIC_OFF: u16 = 0x00c0; // Power Indicator Control. -const PCIE_CAP_SLOT_PIC_MASK: u16 = 0x0300; +pub(crate) const PCIE_CAP_SLOT_PIC_MASK: u16 = 0x0300; +pub(crate) const PCIE_CAP_SLOT_PIC_BLINK: u16 = 0x200; const PCIE_CAP_SLOT_PIC_OFF: u16 = 0x0300; // Power controller control. diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 24fc197f9..924778e9f 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -28,11 +28,12 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, - PCIE_CAP_CLS_2_5G, PCIE_CAP_NLW_X1, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, - PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, - PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, + PCIE_CAP_CLS_2_5G, PCIE_CAP_NLW_X1, PCIE_CAP_SLOT_PIC_BLINK, PCIE_CAP_SLOT_PIC_MASK, + PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, + PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, + PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, + PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, + PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; use crate::hotplug::HotplugOps; @@ -467,6 +468,17 @@ impl HotplugOps for RootPort { } fn unplug_request(&mut self, dev: &Arc>) -> Result<()> { + let pcie_cap_offset = self.config.ext_cap_offset; + let sltctl = le_read_u16( + &self.config.config, + (pcie_cap_offset + PCI_EXP_SLTCTL) as usize, + ) + .unwrap(); + + if (sltctl & PCIE_CAP_SLOT_PIC_MASK) == PCIE_CAP_SLOT_PIC_BLINK { + bail!("Guest is still on the fly of another (un)pluging"); + } + let devfn = dev .lock() .unwrap() -- Gitee From 099473eeb2f2266efc552922af1927957eac5f18 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 26 Oct 2022 14:56:31 +0800 Subject: [PATCH 0282/2187] address_space: add access size supported by the device The xhci supports 64-bit address. When some drivers access a 64-bit address, it may be accessed once, or it may be divided into two 32-bit address accesses. Split the 64-bit access into two 32-bit accesses to keep the device processing simple. Signed-off-by: zhouli57 --- address_space/src/region.rs | 85 ++++++++++++++++++++++++++++++++++++- usb/src/xhci/xhci_pci.rs | 6 ++- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index b1bfa1db7..4107e8315 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -61,6 +61,8 @@ pub struct Region { subregions: Arc>>, /// This field is useful for RomDevice-type Region. If true, in read-only mode, otherwise in IO mode. rom_dev_romd: Arc, + /// Max access size supported by the device. + max_access_size: Option, } impl fmt::Debug for Region { @@ -75,6 +77,7 @@ impl fmt::Debug for Region { .field("space", &self.space) .field("subregions", &self.subregions) .field("rom_dev_romd", &self.rom_dev_romd) + .field("max_access_size", &self.max_access_size) .finish() } } @@ -180,6 +183,58 @@ impl PartialEq for Region { impl Eq for Region {} +/// Used for read/write for multi times. +struct MultiOpsArgs { + /// The base address of the read/write ops. + base: GuestAddress, + /// The offset of the read/write ops. + offset: u64, + /// the number of the read/write ops in bytes. + count: u64, + /// The access size for one read/write in bytes. + access_size: u64, +} + +/// Read/Write for multi times. +macro_rules! rw_multi_ops { + ( $ops: ident, $slice: expr, $args: ident ) => { + // The data size is larger than the max access size, we split to read/write for multiple times. + let base = $args.base; + let offset = $args.offset; + let cnt = $args.count; + let access_size = $args.access_size; + let mut pos = 0; + for _ in 0..(cnt / access_size) { + if !$ops( + &mut $slice[pos as usize..(pos + access_size) as usize], + base, + offset + pos, + ) { + return Err(anyhow!(AddressSpaceError::IoAccess( + base.raw_value(), + offset + pos, + access_size, + ))); + } + pos += access_size; + } + // Unaligned memory access. + if cnt % access_size > 0 + && !$ops( + &mut $slice[pos as usize..cnt as usize], + base, + offset + pos, + ) + { + return Err(anyhow!(AddressSpaceError::IoAccess( + base.raw_value(), + offset + pos, + cnt - pos + ))); + } + }; +} + impl Region { /// The core function of initialization. /// @@ -206,6 +261,7 @@ impl Region { space: Arc::new(RwLock::new(Weak::new())), subregions: Arc::new(RwLock::new(Vec::new())), rom_dev_romd: Arc::new(AtomicBool::new(false)), + max_access_size: None, } } @@ -228,6 +284,15 @@ impl Region { Region::init_region_internal(size, RegionType::IO, None, Some(ops)) } + /// Set the access size limit of the IO region. + /// + /// # Arguments + /// + /// * `access_size` - Max access size supported in bytes. + pub fn set_access_size(&mut self, access_size: u64) { + self.max_access_size = Some(access_size); + } + /// Initialize Container-type region. /// /// # Arguments @@ -495,7 +560,15 @@ impl Region { } let mut slice = vec![0_u8; count as usize]; let read_ops = self.ops.as_ref().unwrap().read.as_ref(); - if !read_ops(&mut slice, base, offset) { + if matches!(self.max_access_size, Some(access_size) if count > access_size) { + let args = MultiOpsArgs { + base, + offset, + count, + access_size: self.max_access_size.unwrap(), + }; + rw_multi_ops!(read_ops, slice, args); + } else if !read_ops(&mut slice, base, offset) { return Err(anyhow!(AddressSpaceError::IoAccess( base.raw_value(), offset, @@ -563,7 +636,15 @@ impl Region { })?; let write_ops = self.ops.as_ref().unwrap().write.as_ref(); - if !write_ops(&slice, base, offset) { + if matches!(self.max_access_size, Some(access_size) if count > access_size) { + let args = MultiOpsArgs { + base, + offset, + count, + access_size: self.max_access_size.unwrap(), + }; + rw_multi_ops!(write_ops, slice, args); + } else if !write_ops(&slice, base, offset) { return Err(anyhow!(AddressSpaceError::IoAccess( base.raw_value(), offset, diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index be55b9481..9453d078f 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -95,18 +95,20 @@ impl XhciPciDevice { || "Failed to register cap region.", )?; - let oper_region = + let mut oper_region = Region::init_io_region(XHCI_PCI_OPER_LENGTH as u64, build_oper_ops(&self.xhci)); + oper_region.set_access_size(4); pci::Result::with_context( self.mem_region .add_subregion(oper_region, XHCI_PCI_OPER_OFFSET as u64), || "Failed to register oper region.", )?; - let runtime_region = Region::init_io_region( + let mut runtime_region = Region::init_io_region( XHCI_PCI_RUNTIME_LENGTH as u64, build_runtime_ops(&self.xhci), ); + runtime_region.set_access_size(4); pci::Result::with_context( self.mem_region .add_subregion(runtime_region, XHCI_PCI_RUNTIME_OFFSET as u64), -- Gitee From c6ff94c56b9b331357e48dcfd063d9a2dc0d3c17 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 31 Oct 2022 19:27:20 +0800 Subject: [PATCH 0283/2187] rtc: fix cmos update_rtc_time overflow After the rtc is initialized, the rtc time needs to be written ro the cmos register, otherwise an overflow may occur. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 53 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 2fe94354b..446fbc327 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -97,7 +97,7 @@ fn bin_to_bcd(src: u8) -> u8 { /// Transfer BCD coded decimal to binary coded decimal. fn bcd_to_bin(src: u8) -> u64 { if (src >> 4) > 9 || (src & 0x0f) > 9 { - error!("RTC: The BCD coded format is wrong."); + warn!("RTC: The BCD coded format is wrong."); return 0_u64; } @@ -147,6 +147,9 @@ impl RTC { base_time: Instant::now(), }; + let tm = rtc_time_to_tm(rtc.get_current_value() as i64); + rtc.set_rtc_cmos(tm); + // Set Time frequency divider and Rate selection frequency in Register-A. // Bits 6-4 = Time frequency divider (010 = 32.768KHz). // Bits 3-0 = Rate selection frequency (110 = 1.024KHz, 976.562s). @@ -200,39 +203,15 @@ impl RTC { } } - fn read_data(&self, data: &mut [u8]) -> bool { + fn read_data(&mut self, data: &mut [u8]) -> bool { if data.len() != 1 { error!("RTC only supports reading data byte by byte."); return false; } let tm = rtc_time_to_tm(self.get_current_value() as i64); + self.set_rtc_cmos(tm); match self.cur_index { - RTC_SECONDS => { - data[0] = bin_to_bcd(tm.tm_sec as u8); - } - RTC_MINUTES => { - data[0] = bin_to_bcd(tm.tm_min as u8); - } - RTC_HOURS => { - data[0] = bin_to_bcd(tm.tm_hour as u8); - } - RTC_DAY_OF_WEEK => { - data[0] = bin_to_bcd((tm.tm_wday + 1) as u8); - } - RTC_DAY_OF_MONTH => { - data[0] = bin_to_bcd(tm.tm_mday as u8); - } - RTC_MONTH => { - data[0] = bin_to_bcd((tm.tm_mon + 1) as u8); - } - RTC_YEAR => { - let year = tm.tm_year + 1900; - data[0] = bin_to_bcd((year % 100) as u8); - } - RTC_CENTURY_BCD => { - data[0] = bin_to_bcd(((tm.tm_year + 1900) % 100) as u8); - } RTC_REG_A => { data[0] = self.cmos_data[RTC_REG_A as usize]; // UIP(update in progress) bit will be set at last 244us of every second. @@ -304,6 +283,17 @@ impl RTC { self.base_time.elapsed().as_secs() + self.tick_offset } + fn set_rtc_cmos(&mut self, tm: libc::tm) { + self.cmos_data[RTC_SECONDS as usize] = bin_to_bcd(tm.tm_sec as u8); + self.cmos_data[RTC_MINUTES as usize] = bin_to_bcd(tm.tm_min as u8); + self.cmos_data[RTC_HOURS as usize] = bin_to_bcd(tm.tm_hour as u8); + self.cmos_data[RTC_DAY_OF_WEEK as usize] = bin_to_bcd((tm.tm_wday + 1) as u8); + self.cmos_data[RTC_DAY_OF_MONTH as usize] = bin_to_bcd(tm.tm_mday as u8); + self.cmos_data[RTC_MONTH as usize] = bin_to_bcd((tm.tm_mon + 1) as u8); + self.cmos_data[RTC_YEAR as usize] = bin_to_bcd(((tm.tm_year + 1900) % 100) as u8); + self.cmos_data[RTC_CENTURY_BCD as usize] = bin_to_bcd(((tm.tm_year + 1900) / 100) as u8); + } + fn update_rtc_time(&mut self) { let sec = bcd_to_bin(self.cmos_data[RTC_SECONDS as usize]); let min = bcd_to_bin(self.cmos_data[RTC_MINUTES as usize]); @@ -313,6 +303,15 @@ impl RTC { let mut year = bcd_to_bin(self.cmos_data[RTC_YEAR as usize]) + bcd_to_bin(self.cmos_data[RTC_CENTURY_BCD as usize]) * 100; + // Check rtc time is valid to prevent tick_offset overflow. + if year < 1970 || mon > 12 || mon < 1 || day > 31 || day < 1 { + warn!( + "RTC: the updated rtc time {}-{}-{} may be invalid.", + year, mon, day + ); + return; + } + // Converts date to seconds since 1970-01-01 00:00:00. if mon <= 2 { mon += 10; -- Gitee From ae8e29e9395fe16435ec88658ab92b16f21eab79 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 12:04:14 -0400 Subject: [PATCH 0284/2187] virtio-blk: Make config_len depends on device feature VIRTIO_BLK_F_DISCARD is not supported for now, so related config does not exist. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index b9ba91991..3add147df 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -47,6 +47,7 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::num_ops::read_u32; +use util::offset_of; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Number of virtqueues. const QUEUE_NUM_BLK: usize = 1; @@ -1057,31 +1058,38 @@ impl VirtioDevice for Block { /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.state.config_space.as_bytes(); - let config_len = config_slice.len() as u64; - if offset >= config_len { + // F_DISCARD is not supported for now, so related config does not exist. + let config_len = offset_of!(VirtioBlkConfig, max_discard_sectors) as u64; + let read_end = offset as usize + data.len(); + if offset + .checked_add(data.len() as u64) + .filter(|&end| end <= config_len) + .is_none() + { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; - } + + let config_slice = self.state.config_space.as_bytes(); + data.write_all(&config_slice[(offset as usize)..read_end])?; Ok(()) } /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let data_len = data.len(); - let config_slice = self.state.config_space.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset, - config_len as u64 - ))); + // F_DISCARD is not supported for now, so related config does not exist. + let config_len = offset_of!(VirtioBlkConfig, max_discard_sectors) as u64; + let write_end = offset as usize + data.len(); + if offset + .checked_add(data.len() as u64) + .filter(|&end| end <= config_len) + .is_none() + { + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + let config_slice = self.state.config_space.as_mut_bytes(); + config_slice[(offset as usize)..write_end].copy_from_slice(data); Ok(()) } -- Gitee From a96b1e3fbc070db10ad47081263599411b34621d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 13:46:32 -0400 Subject: [PATCH 0285/2187] virtio-blk: Refuse guest writing device config Currently the virtio-blk has no writable field in device config space. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 3add147df..298d23491 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1079,7 +1079,6 @@ impl VirtioDevice for Block { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { // F_DISCARD is not supported for now, so related config does not exist. let config_len = offset_of!(VirtioBlkConfig, max_discard_sectors) as u64; - let write_end = offset as usize + data.len(); if offset .checked_add(data.len() as u64) .filter(|&end| end <= config_len) @@ -1087,9 +1086,8 @@ impl VirtioDevice for Block { { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - - let config_slice = self.state.config_space.as_mut_bytes(); - config_slice[(offset as usize)..write_end].copy_from_slice(data); + // The only writable field is "writeback", but it's not supported for now, + // so do nothing here. Ok(()) } @@ -1290,7 +1288,7 @@ mod tests { } // Test `write_config` and `read_config`. The main contests include: compare expect data and - // read date are same; Input invalid offset or date length, it will failed. + // read data are not same; Input invalid offset or data length, it will failed. #[test] fn test_read_write_config() { let mut block = Block::default(); @@ -1300,7 +1298,7 @@ mod tests { let mut read_config_space = [0u8; 8]; block.write_config(0, &expect_config_space).unwrap(); block.read_config(0, &mut read_config_space).unwrap(); - assert_eq!(read_config_space, expect_config_space); + assert_ne!(read_config_space, expect_config_space); // Invalid write assert!(block -- Gitee From e66455e9f76abf41b0e55f985117ccbb03a8f509 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 24 Oct 2022 18:40:38 -0400 Subject: [PATCH 0286/2187] virtio-blk: Don't process virtqueue if it is not enabled A virtqueue maybe not enabled by driver but ioeventfd is still installed for it (see virtio_pci.rs). If so, return early when process the virtqueue to avoid accidental err. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 4 ++++ virtio/src/queue.rs | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 298d23491..f7d941b1c 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -429,6 +429,10 @@ impl BlockIoHandler { let mut done = false; let mut queue = self.queue.lock().unwrap(); + if !queue.is_enabled() { + done = true; + return Ok(done); + } while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { // limit io operations if iops is configured diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 6dcbe6066..b101e91f3 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -158,6 +158,9 @@ impl Element { /// Vring operations. pub trait VringOps { + /// Return true if the vring is enable by driver. + fn is_enabled(&self) -> bool; + /// Return true if the configuration of vring is valid. /// /// # Arguments @@ -841,6 +844,10 @@ impl SplitVring { } impl VringOps for SplitVring { + fn is_enabled(&self) -> bool { + self.ready + } + fn is_valid(&self, sys_mem: &Arc) -> bool { let size = u64::from(self.actual_size()); if !self.ready { @@ -989,6 +996,11 @@ impl Queue { Ok(Queue { vring }) } + /// Return true if the virtqueue is enabled by driver. + pub fn is_enabled(&self) -> bool { + self.vring.is_enabled() + } + /// Return true if the memory layout of the virqueue is valid. /// /// # Arguments -- Gitee From 94b78232c1d82d4a4db743bf96b6f1755926951e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 05:53:40 -0400 Subject: [PATCH 0287/2187] virtio-blk: Flush data if VIRTIO_BLK_F_FLUSH is not negotiated As the VIRTIO_BLK_F_FLUSH feature is offered by device, if this feature is not negotiated, the backend device cache should be in writethrough mode (see virtio 1.2 spec at chapter 5.2.6.2). Signed-off-by: Keqian Zhu --- util/src/aio/libaio.rs | 2 +- virtio/src/block.rs | 25 +++++++++++++++++++------ virtio/src/lib.rs | 2 ++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index a89295162..3a9267d49 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -43,7 +43,7 @@ pub struct IoCb { #[repr(C)] #[allow(non_camel_case_types)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] pub enum IoCmd { Pread = 0, Pwrite = 1, diff --git a/virtio/src/block.rs b/virtio/src/block.rs index f7d941b1c..69655a553 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,11 +21,11 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, - VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_BLOCK, + virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, + VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; @@ -40,6 +40,7 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; +use util::aio::raw_datasync; use util::aio::{Aio, AioCb, AioCompleteFunc, IoCmd, Iovec}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; @@ -572,13 +573,25 @@ impl BlockIoHandler { fn build_aio(&self, engine: Option<&String>) -> Result>> { let complete_func = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { - let status = if ret < 0 { + let mut status = if ret < 0 { ret } else { i64::from(VIRTIO_BLK_S_OK) }; let complete_cb = &aiocb.iocompletecb; + // When driver does not accept FLUSH feature, the device must be of + // writethrough cache type, so flush data before updating used ring. + if !virtio_has_feature(complete_cb.driver_features, VIRTIO_BLK_F_FLUSH) + && aiocb.opcode == IoCmd::Pwritev + && ret >= 0 + { + if let Err(ref e) = raw_datasync(aiocb.file_fd) { + error!("Failed to flush data before send response to guest {:?}", e); + status = i64::from(VIRTIO_BLK_S_IOERR); + } + } + if let Err(ref e) = complete_cb .mem_space .write_object(&status, complete_cb.req_status_addr) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index eedbffa06..6d8f5ab97 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -189,6 +189,8 @@ pub const VIRTIO_BLK_T_GET_ID: u32 = 8; pub const VIRTIO_BLK_ID_BYTES: u32 = 20; /// Success pub const VIRTIO_BLK_S_OK: u32 = 0; +/// IO Error. +pub const VIRTIO_BLK_S_IOERR: u32 = 1; /// Interrupt status: Used Buffer Notification pub const VIRTIO_MMIO_INT_VRING: u32 = 0x01; -- Gitee From c2d4efc24277151d9a4d108bfbf3bcc2a862df8b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 06:21:13 -0400 Subject: [PATCH 0288/2187] virtio-blk: Set status as UNSUPP when command is unknown For a request unsupported by device, its status field should be set as VIRTIO_BLK_S_UNSUPP. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 23 ++++++++++++++++------- virtio/src/lib.rs | 2 ++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 69655a553..d7d6a3f48 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -23,9 +23,9 @@ use std::sync::{Arc, Mutex}; use super::{ virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_FLUSH, - VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, + VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; @@ -176,10 +176,6 @@ impl Request { )) })?; - if !out_header.is_valid() { - bail!("Unsupported block request type"); - } - let pos = elem.in_iovec.len() - 1; let in_iov_elem = elem.in_iovec.get(pos).unwrap(); if in_iov_elem.len < 1 { @@ -453,6 +449,19 @@ impl BlockIoHandler { match Request::new(&self.mem_space, &elem) { Ok(req) => { + if !req.out_header.is_valid() { + self.mem_space + .write_object(&VIRTIO_BLK_S_UNSUPP, req.in_header) + .with_context(|| { + "Failed to write result for the unsupport request for block" + })?; + queue + .vring + .add_used(&self.mem_space, req.desc_index, 1) + .with_context(|| "Failed to add used ring")?; + need_interrupt = true; + continue; + } match req.out_header.request_type { VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT => { last_aio_req_index = req_index; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 6d8f5ab97..bd38bb9d4 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -191,6 +191,8 @@ pub const VIRTIO_BLK_ID_BYTES: u32 = 20; pub const VIRTIO_BLK_S_OK: u32 = 0; /// IO Error. pub const VIRTIO_BLK_S_IOERR: u32 = 1; +/// Unsupport. +pub const VIRTIO_BLK_S_UNSUPP: u32 = 2; /// Interrupt status: Used Buffer Notification pub const VIRTIO_MMIO_INT_VRING: u32 = 0x01; -- Gitee From 63f62f5384389ece7e6dd6792e097bf8f6058084 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 19:37:23 -0400 Subject: [PATCH 0289/2187] virtio-blk: Set status as IOERR when request failed Refer to virtio 1.2 spec at chapter 5.2.6 Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index d7d6a3f48..f6dbe6b46 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -544,6 +544,20 @@ impl BlockIoHandler { } Err(ref e) => { error!("Failed to execute block request, {:?}", e); + self.mem_space + .write_object(&VIRTIO_BLK_S_IOERR, req.in_header) + .with_context(|| { + "Failed to write result, when block request execute failed" + })?; + self.queue + .lock() + .unwrap() + .vring + .add_used(&self.mem_space, req.desc_index, 1) + .with_context(|| { + "Failed to add used ring, when block request execute failed" + })?; + need_interrupt = true; } } req_index += 1; @@ -551,6 +565,11 @@ impl BlockIoHandler { } } else if !merge_req_queue.is_empty() { for req in merge_req_queue.iter() { + self.mem_space + .write_object(&VIRTIO_BLK_S_IOERR, req.in_header) + .with_context(|| { + "Failed to write result, when block request queue isn't empty" + })?; self.queue .lock() .unwrap() @@ -583,7 +602,7 @@ impl BlockIoHandler { fn build_aio(&self, engine: Option<&String>) -> Result>> { let complete_func = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { let mut status = if ret < 0 { - ret + i64::from(VIRTIO_BLK_S_IOERR) } else { i64::from(VIRTIO_BLK_S_OK) }; -- Gitee From f3da7aa3699df5d82f3b7ab619417c5d9bd146dc Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 20:29:19 -0400 Subject: [PATCH 0290/2187] virtio-blk: Fix the size of written status too large The size of status should be one byte. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 6 +++--- virtio/src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index f6dbe6b46..0ee027717 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -602,9 +602,9 @@ impl BlockIoHandler { fn build_aio(&self, engine: Option<&String>) -> Result>> { let complete_func = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { let mut status = if ret < 0 { - i64::from(VIRTIO_BLK_S_IOERR) + VIRTIO_BLK_S_IOERR } else { - i64::from(VIRTIO_BLK_S_OK) + VIRTIO_BLK_S_OK }; let complete_cb = &aiocb.iocompletecb; @@ -616,7 +616,7 @@ impl BlockIoHandler { { if let Err(ref e) = raw_datasync(aiocb.file_fd) { error!("Failed to flush data before send response to guest {:?}", e); - status = i64::from(VIRTIO_BLK_S_IOERR); + status = VIRTIO_BLK_S_IOERR; } } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index bd38bb9d4..9eeb13119 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -188,11 +188,11 @@ pub const VIRTIO_BLK_T_GET_ID: u32 = 8; /// Device id length pub const VIRTIO_BLK_ID_BYTES: u32 = 20; /// Success -pub const VIRTIO_BLK_S_OK: u32 = 0; +pub const VIRTIO_BLK_S_OK: u8 = 0; /// IO Error. -pub const VIRTIO_BLK_S_IOERR: u32 = 1; +pub const VIRTIO_BLK_S_IOERR: u8 = 1; /// Unsupport. -pub const VIRTIO_BLK_S_UNSUPP: u32 = 2; +pub const VIRTIO_BLK_S_UNSUPP: u8 = 2; /// Interrupt status: Used Buffer Notification pub const VIRTIO_MMIO_INT_VRING: u32 = 0x01; -- Gitee From ed54e33f56766bdb1061548b7aa50725e6ddde63 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 06:21:13 -0400 Subject: [PATCH 0291/2187] virtio-blk: Set len field of used element correctly We always touch the last status byte, so the actual used len should cover all in_iovs. Signed-off-by: Keqian Zhu Signed-off-by: zhukeqian --- virtio/src/block.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 0ee027717..99c216f3d 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::cmp; -use std::convert::TryFrom; use std::fs::{File, OpenOptions}; use std::io::{Seek, SeekFrom, Write}; use std::mem::size_of; @@ -145,6 +144,7 @@ struct Request { out_header: RequestOutHeader, iovec: Vec, data_len: u64, + in_len: u32, in_header: GuestAddress, } @@ -190,6 +190,7 @@ impl Request { out_header, iovec: Vec::with_capacity(elem.desc_num as usize), data_len: 0, + in_len: 0, in_header: in_iov_elem.addr, }; @@ -227,6 +228,11 @@ impl Request { _ => (), } + // We always write the last status byte, so count all in_iovs. + for in_iov in elem.in_iovec.iter() { + request.in_len += in_iov.len; + } + Ok(request) } @@ -457,7 +463,7 @@ impl BlockIoHandler { })?; queue .vring - .add_used(&self.mem_space, req.desc_index, 1) + .add_used(&self.mem_space, req.desc_index, req.in_len) .with_context(|| "Failed to add used ring")?; need_interrupt = true; continue; @@ -494,17 +500,11 @@ impl BlockIoHandler { req_index = 0; for req in merge_req_queue.iter() { if let Some(ref mut aio) = self.aio { - let rw_len = match req.out_header.request_type { - VIRTIO_BLK_T_IN => u32::try_from(req.data_len) - .with_context(|| "Convert block request len to u32 with overflow.")?, - _ => 0u32, - }; - let aiocompletecb = AioCompleteCb::new( self.queue.clone(), self.mem_space.clone(), req.desc_index, - rw_len, + req.in_len, req.in_header, Some(self.interrupt_cb.clone()), self.driver_features, @@ -528,7 +528,7 @@ impl BlockIoHandler { self.queue.lock().unwrap().vring.add_used( &self.mem_space, req.desc_index, - 1, + req.in_len, ).with_context(|| "Failed to add the request for block with device id to used ring")?; if self @@ -553,7 +553,7 @@ impl BlockIoHandler { .lock() .unwrap() .vring - .add_used(&self.mem_space, req.desc_index, 1) + .add_used(&self.mem_space, req.desc_index, req.in_len) .with_context(|| { "Failed to add used ring, when block request execute failed" })?; @@ -574,7 +574,7 @@ impl BlockIoHandler { .lock() .unwrap() .vring - .add_used(&self.mem_space, req.desc_index, 1) + .add_used(&self.mem_space, req.desc_index, req.in_len) .with_context(|| { "Failed to add used ring, when block request queue isn't empty" })?; -- Gitee From f25eef1243db3382e7c912fa8a873789a605f7ef Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 25 Oct 2022 20:17:17 -0400 Subject: [PATCH 0292/2187] virtio-blk: Fix buffer filling for GET_ID command If an individual in_iov is not large enough, we should fill the following in_iov one by one. And even though serial num is not configured, we must also write the buffer offered by guest. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 99c216f3d..aa8080cd3 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -321,20 +321,20 @@ impl Request { .with_context(|| "Failed to process block request for flushing")?; } VIRTIO_BLK_T_GET_ID => { - if let Some(serial) = serial_num { - let serial_vec = get_serial_num_config(serial); + let serial = serial_num.clone().unwrap_or_else(|| String::from("")); + let serial_vec = get_serial_num_config(&serial); + let mut start: usize = 0; + let mut end: usize; - for iov in self.iovec.iter() { - if (iov.iov_len as usize) < serial_vec.len() { - bail!( - "The buffer length {} is less than the length {} of serial num", - iov.iov_len, - serial_vec.len() - ); - } - write_buf_mem(&serial_vec, iov.iov_base) - .with_context(|| "Failed to write buf for virtio block id")?; + for iov in self.iovec.iter() { + end = cmp::min(start + iov.iov_len as usize, serial_vec.len()); + write_buf_mem(&serial_vec[start..end], iov.iov_base) + .with_context(|| "Failed to write buf for virtio block id")?; + + if end >= serial_vec.len() { + break; } + start = end; } return Ok(1); -- Gitee From 93d2bdb89f2893456a813e2e07d290ace6042cfe Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 29 Oct 2022 22:10:22 +0800 Subject: [PATCH 0293/2187] virtio-blk: Fix data_len verification for T_IN and T_OUT The data_len must be multiply of 512 byte for T_IN and T_OUT. And put this verification before merging request. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 49 +++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index aa8080cd3..393afa825 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -241,25 +241,11 @@ impl Request { &self, aio: &mut Box>, disk: &File, - disk_sectors: u64, serial_num: &Option, direct: bool, last_aio: bool, iocompletecb: AioCompleteCb, ) -> Result { - let mut top: u64 = self.data_len / SECTOR_SIZE; - if self.data_len % SECTOR_SIZE != 0 { - top += 1; - } - top.checked_add(self.out_header.sector) - .filter(|off| off <= &disk_sectors) - .with_context(|| { - format!( - "offset {} invalid, disk sector {}", - self.out_header.sector, disk_sectors - ) - })?; - let mut aiocb = AioCb { last_aio, file_fd: disk.as_raw_fd(), @@ -347,6 +333,31 @@ impl Request { Ok(0) } + fn io_range_valid(&self, disk_sectors: u64) -> bool { + match self.out_header.request_type { + VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT => { + if self.data_len % SECTOR_SIZE != 0 { + error!("Failed to process block request with size not aligned to 512B"); + return false; + } + if self + .get_req_sector_num() + .checked_add(self.out_header.sector) + .filter(|&off| off <= disk_sectors) + .is_none() + { + error!( + "offset {} invalid, disk sector {}", + self.out_header.sector, disk_sectors + ); + return false; + } + true + } + _ => true, + } + } + fn get_req_sector_num(&self) -> u64 { self.data_len / SECTOR_SIZE } @@ -455,9 +466,14 @@ impl BlockIoHandler { match Request::new(&self.mem_space, &elem) { Ok(req) => { - if !req.out_header.is_valid() { + if !req.out_header.is_valid() || !req.io_range_valid(self.disk_sectors) { + let status = if !req.out_header.is_valid() { + VIRTIO_BLK_S_UNSUPP + } else { + VIRTIO_BLK_S_IOERR + }; self.mem_space - .write_object(&VIRTIO_BLK_S_UNSUPP, req.in_header) + .write_object(&status, req.in_header) .with_context(|| { "Failed to write result for the unsupport request for block" })?; @@ -513,7 +529,6 @@ impl BlockIoHandler { match req.execute( aio, disk_img, - self.disk_sectors, &self.serial_num, self.direct, last_aio_req_index == req_index, -- Gitee From f9be4d5bf56389ea0416ba3bf7df02ab4f599c71 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 30 Oct 2022 10:53:10 +0800 Subject: [PATCH 0294/2187] virtio-blk: Fix delayed queue used notification We should send queue used notification according to guest hint immediately. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 86 +++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 393afa825..c696bee52 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -439,7 +439,6 @@ impl BlockIoHandler { let mut req_queue = Vec::new(); let mut req_index = 0; let mut last_aio_req_index = 0; - let mut need_interrupt = false; let mut done = false; let mut queue = self.queue.lock().unwrap(); @@ -481,7 +480,19 @@ impl BlockIoHandler { .vring .add_used(&self.mem_space, req.desc_index, req.in_len) .with_context(|| "Failed to add used ring")?; - need_interrupt = true; + if queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "block", + VirtioInterruptType::Vring + )) + })?; + self.trace_send_interrupt("Block".to_string()); + } continue; } match req.out_header.request_type { @@ -500,8 +511,6 @@ impl BlockIoHandler { .vring .add_used(&self.mem_space, elem.index, 0) .with_context(|| "Failed to add used ring")?; - need_interrupt = true; - error!("failed to create block request, {:?}", e); } }; @@ -512,7 +521,7 @@ impl BlockIoHandler { let merge_req_queue = self.merge_req_queue(req_queue); - if let Some(disk_img) = self.disk_image.as_mut() { + if let Some(disk_img) = self.disk_image.as_ref() { req_index = 0; for req in merge_req_queue.iter() { if let Some(ref mut aio) = self.aio { @@ -545,7 +554,6 @@ impl BlockIoHandler { req.desc_index, req.in_len, ).with_context(|| "Failed to add the request for block with device id to used ring")?; - if self .queue .lock() @@ -553,7 +561,17 @@ impl BlockIoHandler { .vring .should_notify(&self.mem_space, self.driver_features) { - need_interrupt = true; + (self.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&self.queue.lock().unwrap()), + ) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "block", + VirtioInterruptType::Vring + )) + })?; + self.trace_send_interrupt("Block".to_string()); } } } @@ -572,7 +590,25 @@ impl BlockIoHandler { .with_context(|| { "Failed to add used ring, when block request execute failed" })?; - need_interrupt = true; + if self + .queue + .lock() + .unwrap() + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&self.queue.lock().unwrap()), + ) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "block", + VirtioInterruptType::Vring + )) + })?; + self.trace_send_interrupt("Block".to_string()); + } } } req_index += 1; @@ -593,22 +629,26 @@ impl BlockIoHandler { .with_context(|| { "Failed to add used ring, when block request queue isn't empty" })?; + if self + .queue + .lock() + .unwrap() + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&self.queue.lock().unwrap()), + ) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "block", + VirtioInterruptType::Vring + )) + })?; + self.trace_send_interrupt("Block".to_string()); + } } - need_interrupt = true - } - - if need_interrupt { - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.queue.lock().unwrap()), - ) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "block", - VirtioInterruptType::Vring - )) - })?; - self.trace_send_interrupt("Block".to_string()); } Ok(done) -- Gitee From 6ff9bd2d1145040c07afa19bcd9e9183d3677e9b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 30 Oct 2022 14:35:55 +0800 Subject: [PATCH 0295/2187] virtio-blk: Extract complete_request func to simplify code There are many duplicate code, refactor them. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 264 +++++++++++++------------------------------- 1 file changed, 77 insertions(+), 187 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index c696bee52..505c1edea 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -136,6 +136,42 @@ impl AioCompleteCb { driver_features, } } + + fn complete_request(&self, status: u8) { + if let Err(ref e) = self.mem_space.write_object(&status, self.req_status_addr) { + error!("Failed to write the status (aio completion) {:?}", e); + return; + } + + let mut queue_lock = self.queue.lock().unwrap(); + if let Err(ref e) = queue_lock + .vring + .add_used(&self.mem_space, self.desc_index, self.rw_len) + { + error!( + "Failed to add used ring(aio completion), index {}, len {} {:?}", + self.desc_index, self.rw_len, e, + ); + return; + } + + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + if let Err(e) = (*self.interrupt_cb.as_ref().unwrap())( + &VirtioInterruptType::Vring, + Some(&queue_lock), + ) { + error!( + "Failed to trigger interrupt(aio completion) for block device, error is {:?}", + e + ); + return; + } + self.trace_send_interrupt("Block".to_string()); + } + } } #[derive(Clone)] @@ -245,7 +281,7 @@ impl Request { direct: bool, last_aio: bool, iocompletecb: AioCompleteCb, - ) -> Result { + ) -> Result<()> { let mut aiocb = AioCb { last_aio, file_fd: disk.as_raw_fd(), @@ -322,15 +358,14 @@ impl Request { } start = end; } - - return Ok(1); + aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_OK); } _ => bail!( "The type {} of block request is not supported", self.out_header.request_type ), }; - Ok(0) + Ok(()) } fn io_range_valid(&self, disk_sectors: u64) -> bool { @@ -471,28 +506,16 @@ impl BlockIoHandler { } else { VIRTIO_BLK_S_IOERR }; - self.mem_space - .write_object(&status, req.in_header) - .with_context(|| { - "Failed to write result for the unsupport request for block" - })?; - queue - .vring - .add_used(&self.mem_space, req.desc_index, req.in_len) - .with_context(|| "Failed to add used ring")?; - if queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "block", - VirtioInterruptType::Vring - )) - })?; - self.trace_send_interrupt("Block".to_string()); - } + let aiocompletecb = AioCompleteCb::new( + self.queue.clone(), + self.mem_space.clone(), + req.desc_index, + req.in_len, + req.in_header, + Some(self.interrupt_cb.clone()), + self.driver_features, + ); + aiocompletecb.complete_request(status); continue; } match req.out_header.request_type { @@ -521,134 +544,34 @@ impl BlockIoHandler { let merge_req_queue = self.merge_req_queue(req_queue); - if let Some(disk_img) = self.disk_image.as_ref() { - req_index = 0; - for req in merge_req_queue.iter() { - if let Some(ref mut aio) = self.aio { - let aiocompletecb = AioCompleteCb::new( - self.queue.clone(), - self.mem_space.clone(), - req.desc_index, - req.in_len, - req.in_header, - Some(self.interrupt_cb.clone()), - self.driver_features, - ); - - match req.execute( - aio, - disk_img, - &self.serial_num, - self.direct, - last_aio_req_index == req_index, - aiocompletecb, - ) { - Ok(v) => { - if v == 1 { - // get device id - self.mem_space - .write_object(&VIRTIO_BLK_S_OK, req.in_header) - .with_context(|| "Failed to write result for the request for block with device id")?; - self.queue.lock().unwrap().vring.add_used( - &self.mem_space, - req.desc_index, - req.in_len, - ).with_context(|| "Failed to add the request for block with device id to used ring")?; - if self - .queue - .lock() - .unwrap() - .vring - .should_notify(&self.mem_space, self.driver_features) - { - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.queue.lock().unwrap()), - ) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "block", - VirtioInterruptType::Vring - )) - })?; - self.trace_send_interrupt("Block".to_string()); - } - } - } - Err(ref e) => { - error!("Failed to execute block request, {:?}", e); - self.mem_space - .write_object(&VIRTIO_BLK_S_IOERR, req.in_header) - .with_context(|| { - "Failed to write result, when block request execute failed" - })?; - self.queue - .lock() - .unwrap() - .vring - .add_used(&self.mem_space, req.desc_index, req.in_len) - .with_context(|| { - "Failed to add used ring, when block request execute failed" - })?; - if self - .queue - .lock() - .unwrap() - .vring - .should_notify(&self.mem_space, self.driver_features) - { - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.queue.lock().unwrap()), - ) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "block", - VirtioInterruptType::Vring - )) - })?; - self.trace_send_interrupt("Block".to_string()); - } - } - } - req_index += 1; - } - } - } else if !merge_req_queue.is_empty() { - for req in merge_req_queue.iter() { - self.mem_space - .write_object(&VIRTIO_BLK_S_IOERR, req.in_header) - .with_context(|| { - "Failed to write result, when block request queue isn't empty" - })?; - self.queue - .lock() - .unwrap() - .vring - .add_used(&self.mem_space, req.desc_index, req.in_len) - .with_context(|| { - "Failed to add used ring, when block request queue isn't empty" - })?; - if self - .queue - .lock() - .unwrap() - .vring - .should_notify(&self.mem_space, self.driver_features) - { - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.queue.lock().unwrap()), - ) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "block", - VirtioInterruptType::Vring - )) - })?; - self.trace_send_interrupt("Block".to_string()); + req_index = 0; + for req in merge_req_queue.iter() { + let aiocompletecb = AioCompleteCb::new( + self.queue.clone(), + self.mem_space.clone(), + req.desc_index, + req.in_len, + req.in_header, + Some(self.interrupt_cb.clone()), + self.driver_features, + ); + if let Some(disk_img) = self.disk_image.as_ref() { + if let Err(ref e) = req.execute( + self.aio.as_mut().unwrap(), + disk_img, + &self.serial_num, + self.direct, + req_index == last_aio_req_index, + aiocompletecb.clone(), + ) { + error!("Failed to execute block request, {:?}", e); + aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } + } else { + error!("Failed to execute block request, disk_img not specified"); + aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } + req_index += 1; } Ok(done) @@ -675,41 +598,7 @@ impl BlockIoHandler { } } - if let Err(ref e) = complete_cb - .mem_space - .write_object(&status, complete_cb.req_status_addr) - { - error!("Failed to write the status (aio completion) {:?}", e); - return; - } - - let mut queue_lock = complete_cb.queue.lock().unwrap(); - if let Err(ref e) = queue_lock.vring.add_used( - &complete_cb.mem_space, - complete_cb.desc_index, - complete_cb.rw_len, - ) { - error!( - "Failed to add used ring(aio completion), index {}, len {} {:?}", - complete_cb.desc_index, complete_cb.rw_len, e, - ); - return; - } - - if queue_lock - .vring - .should_notify(&complete_cb.mem_space, complete_cb.driver_features) - { - if let Err(e) = (*complete_cb.interrupt_cb.as_ref().unwrap())( - &VirtioInterruptType::Vring, - Some(&queue_lock), - ) { - error!( - "Failed to trigger interrupt(aio completion) for block device, error is {:?}", - e - ); - } - } + complete_cb.complete_request(status); }) as AioCompleteFunc); Ok(Box::new(Aio::new(complete_func, engine)?)) @@ -1319,6 +1208,7 @@ impl StateTransfer for Block { impl MigrationHook for Block {} impl VirtioTrace for BlockIoHandler {} +impl VirtioTrace for AioCompleteCb {} #[cfg(test)] mod tests { -- Gitee From e88fcc1fab29630e5ca1e053dbecc33241925c76 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 30 Oct 2022 15:43:38 +0800 Subject: [PATCH 0296/2187] virtio-blk: Fix IO merging The IO merging is not working actually, as the "continue_merge" variable is inited as false, so the merging code is not executed at all. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 133 +++++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 64 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 505c1edea..6a36e56fd 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -16,6 +16,7 @@ use std::io::{Seek, SeekFrom, Write}; use std::mem::size_of; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; @@ -109,9 +110,8 @@ impl ByteCode for RequestOutHeader {} pub struct AioCompleteCb { queue: Arc>, mem_space: Arc, - desc_index: u16, - rw_len: u32, - req_status_addr: GuestAddress, + /// The head of merged Request list. + req: Rc, interrupt_cb: Option>, driver_features: u64, } @@ -120,25 +120,29 @@ impl AioCompleteCb { fn new( queue: Arc>, mem_space: Arc, - desc_index: u16, - rw_len: u32, - req_status_addr: GuestAddress, + req: Rc, interrupt_cb: Option>, driver_features: u64, ) -> Self { AioCompleteCb { queue, mem_space, - desc_index, - rw_len, - req_status_addr, + req, interrupt_cb, driver_features, } } fn complete_request(&self, status: u8) { - if let Err(ref e) = self.mem_space.write_object(&status, self.req_status_addr) { + let mut req = Some(self.req.as_ref()); + while let Some(req_raw) = req { + self.complete_one_request(req_raw, status); + req = req_raw.next.as_ref().as_ref(); + } + } + + fn complete_one_request(&self, req: &Request, status: u8) { + if let Err(ref e) = self.mem_space.write_object(&status, req.in_header) { error!("Failed to write the status (aio completion) {:?}", e); return; } @@ -146,11 +150,11 @@ impl AioCompleteCb { let mut queue_lock = self.queue.lock().unwrap(); if let Err(ref e) = queue_lock .vring - .add_used(&self.mem_space, self.desc_index, self.rw_len) + .add_used(&self.mem_space, req.desc_index, req.in_len) { error!( "Failed to add used ring(aio completion), index {}, len {} {:?}", - self.desc_index, self.rw_len, e, + req.desc_index, req.in_len, e, ); return; } @@ -182,6 +186,8 @@ struct Request { data_len: u64, in_len: u32, in_header: GuestAddress, + /// Point to the next merged Request. + next: Box>, } impl Request { @@ -228,6 +234,7 @@ impl Request { data_len: 0, in_len: 0, in_header: in_iov_elem.addr, + next: Box::new(None), }; match out_header.request_type { @@ -293,19 +300,23 @@ impl Request { iocompletecb, }; - for iov in self.iovec.iter() { - let iovec = Iovec { - iov_base: iov.iov_base, - iov_len: iov.iov_len, - }; - aiocb.iovec.push(iovec); + let mut req = Some(self); + while let Some(req_raw) = req { + for iov in req_raw.iovec.iter() { + let iovec = Iovec { + iov_base: iov.iov_base, + iov_len: iov.iov_len, + }; + aiocb.iovec.push(iovec); + } + req = req_raw.next.as_ref().as_ref(); } match self.out_header.request_type { VIRTIO_BLK_T_IN => { aiocb.opcode = IoCmd::Preadv; if direct { - for iov in self.iovec.iter() { + for iov in aiocb.iovec.iter() { MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); } (*aio) @@ -433,36 +444,39 @@ struct BlockIoHandler { } impl BlockIoHandler { - fn merge_req_queue(&self, mut req_queue: Vec) -> Vec { - if req_queue.len() == 1 { - return req_queue; - } - + fn merge_req_queue( + &self, + mut req_queue: Vec, + last_aio_index: &mut usize, + ) -> Vec { req_queue.sort_by(|a, b| a.out_header.sector.cmp(&b.out_header.sector)); + let mut merge_req_queue = Vec::::new(); - let mut continue_merge: bool = false; - - for req in &req_queue { - if continue_merge { - if let Some(last_req) = merge_req_queue.last_mut() { - if last_req.out_header.sector + last_req.get_req_sector_num() - != req.out_header.sector - { - continue_merge = false; - merge_req_queue.push(req.clone()); - } else { - for iov in req.iovec.iter() { - let iovec = Iovec { - iov_base: iov.iov_base, - iov_len: iov.iov_len, - }; - last_req.data_len += iovec.iov_len; - last_req.iovec.push(iovec); - } - } + let mut last_req: Option<&mut Request> = None; + + *last_aio_index = 0; + for req in req_queue { + let io = req.out_header.request_type == VIRTIO_BLK_T_IN + || req.out_header.request_type == VIRTIO_BLK_T_OUT; + let can_merge = match last_req { + Some(ref req_ref) => { + io && req_ref.out_header.request_type == req.out_header.request_type + && (req_ref.out_header.sector + req_ref.get_req_sector_num() + == req.out_header.sector) } + None => false, + }; + + if can_merge { + let last_req_raw = last_req.unwrap(); + last_req_raw.next = Box::new(Some(req)); + last_req = last_req_raw.next.as_mut().as_mut(); } else { - merge_req_queue.push(req.clone()); + if io { + *last_aio_index = merge_req_queue.len(); + } + merge_req_queue.push(req); + last_req = merge_req_queue.last_mut(); } } @@ -472,7 +486,6 @@ impl BlockIoHandler { fn process_queue(&mut self) -> Result { self.trace_request("Block".to_string(), "to IO".to_string()); let mut req_queue = Vec::new(); - let mut req_index = 0; let mut last_aio_req_index = 0; let mut done = false; @@ -509,23 +522,14 @@ impl BlockIoHandler { let aiocompletecb = AioCompleteCb::new( self.queue.clone(), self.mem_space.clone(), - req.desc_index, - req.in_len, - req.in_header, + Rc::new(req), Some(self.interrupt_cb.clone()), self.driver_features, ); aiocompletecb.complete_request(status); continue; } - match req.out_header.request_type { - VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT => { - last_aio_req_index = req_index; - } - _ => {} - } req_queue.push(req); - req_index += 1; done = true; } Err(ref e) => { @@ -542,21 +546,23 @@ impl BlockIoHandler { // unlock queue, because it will be hold below. drop(queue); - let merge_req_queue = self.merge_req_queue(req_queue); + if req_queue.is_empty() { + return Ok(done); + } + + let merge_req_queue = self.merge_req_queue(req_queue, &mut last_aio_req_index); - req_index = 0; - for req in merge_req_queue.iter() { + for (req_index, req) in merge_req_queue.into_iter().enumerate() { + let req_rc = Rc::new(req); let aiocompletecb = AioCompleteCb::new( self.queue.clone(), self.mem_space.clone(), - req.desc_index, - req.in_len, - req.in_header, + req_rc.clone(), Some(self.interrupt_cb.clone()), self.driver_features, ); if let Some(disk_img) = self.disk_image.as_ref() { - if let Err(ref e) = req.execute( + if let Err(ref e) = req_rc.execute( self.aio.as_mut().unwrap(), disk_img, &self.serial_num, @@ -571,7 +577,6 @@ impl BlockIoHandler { error!("Failed to execute block request, disk_img not specified"); aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } - req_index += 1; } Ok(done) -- Gitee From 5df0ae3bb17a38c503166fdbecaa9e113fdb448d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 29 Oct 2022 22:19:14 +0800 Subject: [PATCH 0297/2187] VNC: Fix mouse display problem Clients that support the VncFeatureRichCursor feature will receive a mask which can indicate the mouse image area. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 20 ++++++++++++++++++-- vnc/src/vnc.rs | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 565a36846..ee9c08991 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -18,8 +18,9 @@ use crate::{ server::VncServer, utils::BuffPool, vnc::{ - framebuffer_upadate, set_area_dirty, update_client_surface, BIT_PER_BYTE, DIRTY_PIXELS_NUM, - DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, + display_cursor_define, framebuffer_upadate, set_area_dirty, update_client_surface, + DisplayMouse, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, + MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, }, }; use anyhow::{anyhow, Result}; @@ -856,6 +857,21 @@ impl VncClient { self.encoding = 0; self.desktop_resize(); + // VNC display cursor define. + let mut cursor: DisplayMouse = DisplayMouse::default(); + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let locked_server = server.lock().unwrap(); + let mut mask: Vec = Vec::new(); + if let Some(c) = &locked_server.cursor { + cursor = c.clone(); + } + if let Some(m) = &locked_server.mask { + mask = m.clone(); + } + drop(locked_server); + if !cursor.data.is_empty() { + display_cursor_define(self, &mut cursor, &mut mask); + } self.update_event_handler(1, VncClient::handle_protocol_msg); Ok(()) diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 3c38271e8..8f4c81a46 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -578,7 +578,7 @@ pub fn vnc_display_switch(surface: &mut DisplaySurface) { // Desktop_resize. locked_client.desktop_resize(); // Cursor define. - if !mask.is_empty() { + if !cursor.data.is_empty() { display_cursor_define(&mut locked_client, &mut cursor, &mut mask); } locked_client.dirty_bitmap.clear_all(); @@ -600,34 +600,49 @@ pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { } let server = VNC_SERVERS.lock().unwrap()[0].clone(); let width = cursor.width as u64; - let heigt = cursor.height as u64; - + let height = cursor.height as u64; let bpl = round_up_div(width as u64, BIT_PER_BYTE as u64); - let len = bpl * heigt as u64; - let mut mask = Bitmap::::new(len as usize); - for j in 0..heigt { + // Set the bit for mask. + let bit_mask: u8 = 0x80; + + let mut mask: Vec = vec![0; (bpl * height) as usize]; + let first_bit = if cfg!(target_endian = "big") { + 0_usize + } else { + (bytes_per_pixel() - 1) as usize + }; + + for j in 0..height { + let mut bit = bit_mask; for i in 0..width { - let idx = i + j * width; + let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; if let Some(n) = cursor.data.get(idx as usize) { if *n == 0xff { - mask.set(idx as usize).unwrap(); + mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; } } + bit >>= 1; + if bit == 0 { + bit = bit_mask; + } } } - let mut data = Vec::new(); - mask.get_data(&mut data); + server.lock().unwrap().cursor = Some(cursor.clone()); - server.lock().unwrap().mask = Some(data.clone()); + server.lock().unwrap().mask = Some(mask.clone()); // Send the framebuff for each client. for client in server.lock().unwrap().clients.values_mut() { - display_cursor_define(&mut client.lock().unwrap(), cursor, &mut data); + display_cursor_define(&mut client.lock().unwrap(), cursor, &mut mask); } } /// Send framebuf of mouse to the client. -fn display_cursor_define(client: &mut VncClient, cursor: &mut DisplayMouse, mask: &mut Vec) { +pub fn display_cursor_define( + client: &mut VncClient, + cursor: &mut DisplayMouse, + mask: &mut Vec, +) { let mut buf = Vec::new(); if client.has_feature(VncFeatures::VncFeatureAlphaCursor) { buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); -- Gitee From 5a02e98c25ccc36e0c878052d9344c7db0837337 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 1 Nov 2022 20:20:07 +0800 Subject: [PATCH 0298/2187] VNC: fix incomplete image display In the operation of set area dirty, The remainder operation causes some pixel loss, so replace it by the round_up_div operation. Signed-off-by: Xiao Ye --- vnc/src/vnc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 8f4c81a46..03dd96ac9 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -229,7 +229,7 @@ pub fn set_area_dirty( h = cmp::min(y + h, height); while y < h { let pos = y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32; - for i in 0..w / DIRTY_PIXELS_NUM as i32 { + for i in 0..round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as i32 { dirty.set((pos + i) as usize).unwrap(); } y += 1; -- Gitee From 9ac0a508c67979cc8769037b7b8044c297ccdfb1 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 2 Nov 2022 11:31:47 +0800 Subject: [PATCH 0299/2187] memory: fix error to get host cpu number before prealloc The function `sysconf` may call failure before prealloc. To deal with such situation, just return one thread to touch pages. Signed-off-by: Xinle.Guo --- address_space/src/host_mmap.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 69c9ec623..5486eb36e 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -153,8 +153,12 @@ impl FileBackend { /// /// * `nr_vcpus` - Number of vcpus. fn max_nr_threads(nr_vcpus: u8) -> u8 { - let nr_host_cpu = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as u8 }; - min(min(nr_host_cpu, MAX_PREALLOC_THREAD), nr_vcpus) + let nr_host_cpu = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) }; + if nr_host_cpu > 0 { + return min(min(nr_host_cpu as u8, MAX_PREALLOC_THREAD), nr_vcpus); + } + // If fails to call `sysconf` function, just use a single thread to touch pages. + 1 } /// Touch pages to pre-alloc memory for VM. @@ -184,7 +188,7 @@ fn touch_pages(start: u64, page_size: u64, nr_pages: u64) { /// /// # Arguments /// -/// * `host_addr` - The start host address of memory of the virtual machine. +/// * `host_addr` - The start host address to pre allocate. /// * `size` - Size of memory. /// * `nr_vcpus` - Number of vcpus. fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { -- Gitee From 8f5a9e22df0eda69194a1e414c99baec9476774b Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 2 Nov 2022 21:02:14 +0800 Subject: [PATCH 0300/2187] Virtio-gpu: the default image data of cursor is null When switching the image, the vnc server will reflush the cursor data to client.As the default image data of cursor is null, the number of bytes received by the client is not as expected. Signed-off-by: Xiao Ye --- virtio/src/gpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index da7aae94f..7f37d95d1 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -618,7 +618,7 @@ impl GpuIoHandler { width: 64, hot_x: info_cursor.hot_x, hot_y: info_cursor.hot_y, - ..Default::default() + data: vec![0_u8; 64 * 64 * size_of::() as usize], }; scanout.mouse = Some(tmp_mouse); } else { -- Gitee From 5fa26fc76179332ac80fa0453d44ab03a50ebe9e Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 2 Nov 2022 15:03:18 +0800 Subject: [PATCH 0301/2187] memory: add testcase for memory prealloc Signed-off-by: Xinle.Guo --- address_space/src/host_mmap.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 5486eb36e..8005d3c05 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -585,4 +585,30 @@ mod test { assert_eq!(total_mem_size, total_file_size); assert_eq!(total_mem_size, total_mmaps_size); } + + #[test] + fn test_memory_prealloc() { + // Mmap and prealloc with anonymous memory. + let host_addr = do_mmap(&None, 0x20_0000, 0, false, false, false).unwrap(); + // Check the thread number equals to minimum value. + assert_eq!(max_nr_threads(1), 1); + // The max threads limit is 16, or the number of host CPUs, it will never be 20. + assert_ne!(max_nr_threads(20), 20); + mem_prealloc(host_addr, 0x20_0000, 20); + + // Mmap and prealloc with file backend. + let file_path = String::from("back_mem_test"); + let file_size = 0x10_0000; + let f_back = FileBackend::new_mem(&file_path, file_size).unwrap(); + let host_addr = do_mmap( + &Some(f_back.file.as_ref()), + 0x10_0000, + f_back.offset, + false, + true, + false, + ) + .unwrap(); + mem_prealloc(host_addr, 0x10_0000, 2); + } } -- Gitee From fc162be1be8c2dba3315ab50e4760e4c0ae84384 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 2 Nov 2022 16:01:15 +0800 Subject: [PATCH 0302/2187] memory: add specifications for memory features 1. The numa node is no more than 8. 2. StratoVirt supports 2M or 1G hugepages. 3. virtio-balloon is invalid for hugepages. And the balloon value must be an integer multiple of guest page size. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 41034ac62..98b93c461 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -10,7 +10,7 @@ General configuration of machine, including * type: The type of machine, three types of machine are available: "none", "microvm", "q35"(x86_64 platform) and "virt" (aarch64 platform). * dump-guest-core: Including guest memory in coredump file or not, default value is true. -* mem-share: Guest memory is sharable with other processes or not. +* mem-share: Guest memory is sharable with other processes or not. By default this option is turned off. * accel: accelerate module, supported value `kvm`. (optional). If not set, default is KVM. * usb: whether use usb. supported value `off`. (optional). If not set, default is off. @@ -70,7 +70,7 @@ Currently, these options are supported. StratoVirt supports to set the size of VM's memory in cmdline. This allows you to set the size of memory that VM will support. -You can choose `G` as unit (default unit is `M`). +You can choose `G` as unit (default unit is `M`). And the memory size needs to be an integer. Default VM memory size is 256M. The supported VM memory size is among [128M, 512G]. @@ -109,7 +109,7 @@ The path has to be absolute path. ### 1.4.1 hugepages -Memory backend file can be used to let guest use hugetlbfs on host. +Memory backend file can be used to let guest use hugetlbfs on host. It supports 2M or 1G hugepages memory. The following steps show how to use hugepages: ```shell @@ -141,6 +141,8 @@ Each NUMA node is given a list of command lines option, there will be described It describes the distance between source and destination. The default of source to source is 10, source to destination is 20. And if you choose not to set these parameters, the VM will set the default values. +Note: The maximum number of numa nodes is not more than 8. + The following command shows how to set NUMA node: ```shell @@ -602,7 +604,8 @@ of device and the second one represents function number of it. -device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,id=balloon-0[,deflate-on-oom=true|false][,free-page-reporting=true|false][,multifunction=on|off] ``` -Note: avoid using balloon devices and vfio devices together. +Note: avoid using balloon devices and vfio devices together, balloon device is invalid when memory is hugepages. +The balloon memory size must be an integer multiple of guest page size. ### 2.8 Virtio-rng Virtio rng is a paravirtualized random number generator device, it provides a hardware rng device to the guest. -- Gitee From f34902cee7d5f0c38566a30480678fed368dabed Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 Aug 2022 20:42:34 +0800 Subject: [PATCH 0303/2187] VNC: Add tls encryption 1. Parse encryption configuration during initialization. 2. Load private and certificate during initialization. 3. Tls channel init. Signed-off-by: Xiao Ye --- machine_manager/src/config/error.rs | 2 + machine_manager/src/config/mod.rs | 6 + machine_manager/src/config/tls_creds.rs | 116 ++++++++ vnc/Cargo.toml | 2 + vnc/src/auth.rs | 20 +- vnc/src/client.rs | 155 +++++++++-- vnc/src/lib.rs | 1 + vnc/src/server.rs | 70 ++++- vnc/src/vencrypt.rs | 347 ++++++++++++++++++++++++ 9 files changed, 693 insertions(+), 26 deletions(-) create mode 100644 machine_manager/src/config/tls_creds.rs create mode 100644 vnc/src/vencrypt.rs diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index e823d991d..9b8f2e0fc 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -54,4 +54,6 @@ pub enum ConfigError { Unaligned(String, u64, u64), #[error("PFlash unit id given {0} should not be more than {1}")] UnitIdError(usize, usize), + #[error("Directory {0} does not exist")] + DirNotExist(String), } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index cb0162d09..77bc79e6f 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -26,6 +26,7 @@ pub use numa::*; pub use pci::*; pub use rng::*; pub use scsi::*; +pub use tls_creds::*; pub use usb::*; pub use vfio::*; pub use vnc::*; @@ -46,6 +47,7 @@ mod numa; mod pci; mod rng; mod scsi; +mod tls_creds; mod usb; mod vfio; pub mod vnc; @@ -75,6 +77,7 @@ pub const MAX_NODES: u32 = 128; pub enum ObjConfig { Rng(RngObjConfig), Zone(MemZoneConfig), + Tls(TlsCredObjConfig), } fn parse_rng_obj(object_args: &str) -> Result { @@ -215,6 +218,9 @@ impl VmConfig { bail!("Object: {} has been added", id); } } + "tls-creds-x509" => { + self.add_tlscred(object_args)?; + } _ => { bail!("Unknow object type: {:?}", &device_type); } diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs new file mode 100644 index 000000000..4e30c03ba --- /dev/null +++ b/machine_manager/src/config/tls_creds.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::config::{ + ConfigError, ObjConfig, {CmdParser, VmConfig}, +}; +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; +use std::path::Path; + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct TlsCredObjConfig { + pub id: String, + pub dir: String, + pub cred_type: String, + pub endpoint: Option, + pub verifypeer: bool, +} + +impl VmConfig { + pub fn add_tlscred(&mut self, tlscred_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("tls-creds-x509"); + cmd_parser + .push("") + .push("id") + .push("dir") + .push("endpoint") + .push("verify-peer"); + cmd_parser.parse(tlscred_config)?; + let mut tlscred = TlsCredObjConfig::default(); + if let Some(id) = cmd_parser.get_value::("id")? { + tlscred.id = id; + } else { + return Err(anyhow!(ConfigError::FieldIsMissing("id", "vnc tls_creds"))); + } + + if let Some(dir) = cmd_parser.get_value::("dir")? { + if Path::new(&dir).is_dir() { + tlscred.dir = dir; + } else { + return Err(anyhow!(ConfigError::DirNotExist(dir))); + } + } + if let Some(endpoint) = cmd_parser.get_value::("endpoint")? { + tlscred.endpoint = Some(endpoint); + } + if let Some(verifypeer) = cmd_parser.get_value::("verify-peer")? { + if verifypeer == *"true" { + tlscred.verifypeer = true; + } else { + tlscred.verifypeer = false; + } + } + tlscred.cred_type = "x509".to_string(); + + let id = tlscred.id.clone(); + if self.object.get(&id).is_none() { + let tlscred_config = ObjConfig::Tls(tlscred); + self.object.insert(id, tlscred_config); + } else { + return Err(anyhow!(ConfigError::IdRepeat("tlscred".to_string(), id))); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{env, fs}; + + #[test] + fn test_add_tlscred() { + let mut dir = env::current_dir().unwrap(); + dir.push("test_pki"); + // Create file. + if !dir.is_dir() { + fs::create_dir(dir.clone()).unwrap(); + } + assert_eq!(dir.is_dir(), true); + + // Certificate directory is exist. + let tls_config: String = format!( + "tls-creds-x509,id=vnc-tls-creds0,dir={},endpoint=server,verify-peer=false", + dir.to_str().unwrap() + ); + let id = String::from("vnc-tls-creds0"); + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_object(tls_config.as_str()).is_ok()); + assert!(vm_config.object.get(&id).is_some()); + if let Some(obj_cfg) = vm_config.object.get(&id) { + if let ObjConfig::Tls(tls_cred) = obj_cfg { + assert_eq!(tls_cred.dir, dir.to_str().unwrap()); + assert_eq!(tls_cred.endpoint, Some("server".to_string())); + assert_eq!(tls_cred.verifypeer, false); + } + } + + // Delete file. + fs::remove_dir(dir.clone()).unwrap(); + assert_eq!(dir.is_dir(), false); + // Certificate directory does not exist. + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_object(tls_config.as_str()).is_err()); + } +} diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index d52343745..6de86ec56 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -16,6 +16,8 @@ serde_json = "1.0.55" vmm-sys-util = ">=0.7.0" once_cell = "1.9.0" sscanf = "0.2.1" +rustls = "0.20.6" +rustls-pemfile = "1.0.0" bitintr = "0.2.0" usb = { path = "../usb" } address_space = { path = "../address_space" } diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index fefe020c2..15f3123fe 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -32,9 +32,25 @@ pub enum SubAuthState { VncAuthVencryptTlssasl = 264, } +/// Configuration for authentication +/// Identity: authentication user +#[derive(Debug, Clone, Default)] +pub struct SaslAuth { + pub identity: String, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum SaslStage { + SaslServerStart, + SaslServerStep, +} + impl VncClient { - /// Send auth version - pub fn protocol_client_vencrypt_init(&mut self) -> Result<()> { + pub fn get_mechname_length(&mut self) -> Result<()> { + Ok(()) + } + + pub fn start_sasl_auth(&mut self) -> Result<()> { Ok(()) } } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index ee9c08991..779631357 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -25,6 +25,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use machine_manager::event_loop::EventLoop; +use rustls::ServerConnection; use sscanf::scanf; use std::{ cmp, @@ -41,6 +42,7 @@ use util::{ use vmm_sys_util::epoll::EventSet; const MAX_RECVBUF_LEN: usize = 1024; +const MAX_SEND_LEN: usize = 64 * 1024; const NUM_OF_COLORMAP: u16 = 256; // VNC encodings types. @@ -109,9 +111,9 @@ impl From for ClientMsg { } /// RFB protocol version. -struct VncVersion { - major: u16, - minor: u16, +pub struct VncVersion { + pub major: u16, + pub minor: u16, } impl VncVersion { @@ -219,7 +221,7 @@ pub struct VncClient { /// Connection status. pub dis_conn: bool, /// RFB protocol version. - version: VncVersion, + pub version: VncVersion, /// Auth type. auth: AuthState, /// SubAuth type. @@ -230,6 +232,8 @@ pub struct VncClient { pub handlers: Vec>>>, /// Pointer to VncServer. pub server: Arc>, + /// Tls server connection. + pub tls_conn: Option, /// Data storage type for client. pub big_endian: bool, /// State flags whether the image needs to be updated for the client. @@ -276,6 +280,7 @@ impl VncClient { handle_msg: VncClient::handle_version, handlers: Vec::new(), server, + tls_conn: None, big_endian: false, state: UpdateState::No, dirty_bitmap: Bitmap::::new( @@ -416,6 +421,43 @@ impl VncClient { Ok(()) } + // Read from vencrypt channel. + pub fn read_tls_msg(&mut self, buf: &mut Vec) -> Result { + let mut len = 0_usize; + if self.tls_conn.is_none() { + return Ok(0_usize); + } + let tc: &mut ServerConnection; + match &mut self.tls_conn { + Some(sc) => tc = sc, + None => return Ok(0_usize), + } + + if let Err(e) = tc.read_tls(&mut self.stream) { + error!("tls_conn read error {:?}", e); + return Err(anyhow!(VncError::ReadMessageFailed(format!( + "tls_conn read error {:?}", + e + )))); + } + + if let Ok(io_state) = tc.process_new_packets() { + if io_state.plaintext_bytes_to_read() > 0 { + len = io_state.plaintext_bytes_to_read(); + buf.resize(len, 0u8); + if let Err(e) = tc.reader().read_exact(buf) { + error!("tls_conn read error {:?}", e); + buf.clear(); + return Err(anyhow!(VncError::ReadMessageFailed(format!( + "tls_conn read error {:?}", + e + )))); + } + } + } + Ok(len) + } + /// Read plain txt. pub fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { let mut len = 0_usize; @@ -432,8 +474,33 @@ impl VncClient { Ok(len) } + // Send vencrypt message. + fn write_tls_msg(&mut self, buf: &[u8]) { + let buf_size = buf.len(); + let mut offset = 0; + + let tc: &mut ServerConnection; + match &mut self.tls_conn { + Some(ts) => tc = ts, + None => { + return; + } + } + + while offset < buf_size { + let next = cmp::min(buf_size, offset + MAX_SEND_LEN); + let tmp_buf = &buf[offset..next].to_vec(); + if let Err(e) = tc.writer().write_all(tmp_buf) { + error!("write msg error: {:?}", e); + return; + } + vnc_write_tls_message(tc, &mut self.stream); + offset = next; + } + } + /// Send plain txt. - pub fn write_plain_msg(&mut self, buf: &[u8]) { + fn write_plain_msg(&mut self, buf: &[u8]) { let buf_size = buf.len(); let mut offset = 0; loop { @@ -465,12 +532,20 @@ impl VncClient { /// # Arguments /// * `buf` - Data to be send. pub fn write_msg(&mut self, buf: &[u8]) { - self.write_plain_msg(buf); + if self.tls_conn.is_none() { + self.write_plain_msg(buf) + } else { + self.write_tls_msg(buf) + } } - /// Read buf from stream, return the size of buff. + /// Read buf from stream, return the size. pub fn read_msg(&mut self, buf: &mut Vec) -> Result { - self.read_plain_msg(buf) + if self.tls_conn.is_none() { + self.read_plain_msg(buf) + } else { + self.read_tls_msg(buf) + } } /// Read buf from tcpstream. @@ -581,7 +656,7 @@ impl VncClient { buf[1] = 2_u8; self.write_msg(&buf); - self.update_event_handler(2, VncClient::protocol_client_vencrypt_init); + self.update_event_handler(2, VncClient::client_vencrypt_init); } _ => { self.auth_failed("Unhandled auth method"); @@ -1003,16 +1078,7 @@ impl EventNotifierHelper for VncClient { } if dis_conn { - let addr = client.lock().unwrap().addr.clone(); - info!("Client disconnect : {:?}", addr); - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_server = server.lock().unwrap(); - locked_server.clients.remove(&addr); - if locked_server.clients.is_empty() { - update_client_surface(&mut locked_server); - } - drop(locked_server); - client.lock().unwrap().disconnect(); + connection_cleanup(client.clone()); } None as Option> @@ -1021,6 +1087,28 @@ impl EventNotifierHelper for VncClient { let mut locked_client = client_handler.lock().unwrap(); locked_client.handlers.push(Arc::new(Mutex::new(handler))); + let client = client_handler.clone(); + let handler: Box Option>> = + Box::new(move |event, _| { + let mut dis_conn = false; + if event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { + dis_conn = true; + } else if event == EventSet::IN { + let mut locked_client = client.lock().unwrap(); + if locked_client.tls_handshake().is_err() { + dis_conn = true; + } + } + + if dis_conn { + connection_cleanup(client.clone()); + } + + None as Option> + }); + + locked_client.handlers.push(Arc::new(Mutex::new(handler))); + vec![EventNotifier::new( NotifierOperation::AddShared, locked_client.stream.as_raw_fd(), @@ -1030,3 +1118,32 @@ impl EventNotifierHelper for VncClient { )] } } + +fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) { + while tc.wants_write() { + match tc.write_tls(stream) { + Ok(_) => {} + Err(e) => { + if e.kind() == std::io::ErrorKind::WouldBlock { + stream.flush().unwrap(); + continue; + } else { + error!("write msg error: {:?}", e); + return; + } + } + } + } +} + +fn connection_cleanup(client: Arc>) { + let addr = client.lock().unwrap().addr.clone(); + info!("Client disconnect : {:?}", addr); + client.lock().unwrap().disconnect(); + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_server = server.lock().unwrap(); + locked_server.clients.remove(&addr); + if locked_server.clients.is_empty() { + update_client_surface(&mut locked_server); + } +} diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index a920f3bdc..e7c77b1d4 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -28,6 +28,7 @@ pub mod input; pub mod pixman; pub mod server; pub mod utils; +pub mod vencrypt; pub mod vnc; pub const fn round_up_div(n: u64, d: u64) -> u64 { diff --git a/vnc/src/server.rs b/vnc/src/server.rs index ed20b3474..1bff117e9 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -19,13 +19,15 @@ use crate::{ get_image_width, unref_pixman_image, }, round_up_div, + vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, vnc::{ update_client_surface, DisplayMouse, DIRTY_PIXELS_NUM, DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, }, + VncError, }; -use anyhow::Result; +use anyhow::{anyhow, Result}; use machine_manager::{ config::{ObjConfig, VncConfig}, event_loop::EventLoop, @@ -47,7 +49,6 @@ use util::{ }, }; use vmm_sys_util::epoll::EventSet; - /// Info of image. /// stride is not always equal to stride because of memory alignment. pub struct ImageInfo { @@ -91,6 +92,12 @@ pub struct VncServer { listener: Arc>, /// Clients connected to vnc. pub clients: HashMap>>, + /// Configuration for tls connection. + pub tlscreds: Option, + /// Configuration for sasl Authentication. + pub saslauth: Option, + /// Configuration to make tls channel. + pub tls_config: Option>, /// Auth type. pub auth: AuthState, /// Subauth type. @@ -125,6 +132,9 @@ impl VncServer { VncServer { listener, clients: HashMap::new(), + tlscreds: None, + saslauth: None, + tls_config: None, auth: AuthState::No, subauth: SubAuthState::VncAuthVencryptPlain, keysym2keycode: HashMap::new(), @@ -154,10 +164,33 @@ impl VncServer { /// * `object` - configure of sasl and tls. pub fn make_config( &mut self, - _vnc_cfg: &VncConfig, - _object: &HashMap, + vnc_cfg: &VncConfig, + object: &HashMap, ) -> Result<()> { - // tls configuration + // Tls configuration. + if let Some(ObjConfig::Tls(tls_cred)) = object.get(&vnc_cfg.tls_creds) { + let tlscred = TlsCreds { + cred_type: tls_cred.cred_type.clone(), + dir: tls_cred.dir.clone(), + endpoint: tls_cred.endpoint.clone(), + verifypeer: tls_cred.verifypeer, + }; + + match make_vencrypt_config(&tlscred) { + Ok(tls_config) => { + self.tls_config = Some(tls_config); + } + Err(e) => { + return Err(e); + } + } + self.tlscreds = Some(tlscred); + } + + // Server.auth. + if let Err(err) = self.setup_auth() { + return Err(err); + } // Mapping ASCII to keycode. for &(k, v) in KEYSYM2KEYCODE.iter() { @@ -166,6 +199,33 @@ impl VncServer { Ok(()) } + /// Encryption configuration. + pub fn setup_auth(&mut self) -> Result<()> { + if let Some(tlscred) = &self.tlscreds { + self.auth = AuthState::Vencrypt; + if tlscred.cred_type != *X509_CERT && tlscred.cred_type != *ANON_CERT { + error!("Unsupported tls cred type"); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Unsupported tls cred type", + )))); + } + if self.saslauth.is_some() { + if tlscred.cred_type == *"x509" { + self.subauth = SubAuthState::VncAuthVencryptX509Sasl; + } else { + self.subauth = SubAuthState::VncAuthVencryptTlssasl; + } + } else { + self.subauth = SubAuthState::VncAuthVencryptX509None; + } + } else { + self.auth = AuthState::No; + self.subauth = SubAuthState::VncAuthVencryptPlain; + } + + Ok(()) + } + /// Set diry for client /// /// # Arguments diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs new file mode 100644 index 000000000..9c219575a --- /dev/null +++ b/vnc/src/vencrypt.rs @@ -0,0 +1,347 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::{auth::SubAuthState, client::VncClient, VncError}; +use anyhow::{anyhow, Result}; +use rustls::{ + self, + cipher_suite::{ + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + }, + kx_group::{SECP256R1, SECP384R1, X25519}, + server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth}, + version::{TLS12, TLS13}, + RootCertStore, SupportedCipherSuite, SupportedKxGroup, SupportedProtocolVersion, +}; +use std::{fs::File, io::BufReader, sync::Arc}; +use util::loop_context::NotifierOperation; + +const TLS_CREDS_SERVER_CACERT: &str = "cacert.pem"; +const TLS_CREDS_SERVERCERT: &str = "servercert.pem"; +const TLS_CREDS_SERVERKEY: &str = "serverkey.pem"; +pub const X509_CERT: &str = "x509"; +pub const ANON_CERT: &str = "anon"; +const CLIENT_REQUIRE_AUTH: bool = true; +/// Number of stored sessions. +const MAXIMUM_SESSION_STORAGE: usize = 256; + +/// Cipher suites supported by server. +pub static TLS_CIPHER_SUITES: &[SupportedCipherSuite] = &[ + TLS13_AES_128_GCM_SHA256, + TLS13_AES_256_GCM_SHA384, + TLS13_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, +]; +/// Tls version supported by server. +pub static TLS_VERSIONS: &[&SupportedProtocolVersion] = &[&TLS13, &TLS12]; +/// Key exchange groups supported by server. +pub static TLS_KX_GROUPS: [&SupportedKxGroup; 3] = [&X25519, &SECP256R1, &SECP384R1]; + +/// Configuration for tls. +#[derive(Debug, Clone, Default)] +pub struct TlsCreds { + /// X509 or anon. + pub cred_type: String, + /// Path of cred file. + pub dir: String, + /// Server of client. + pub endpoint: Option, + /// Verify peer. + pub verifypeer: bool, +} + +impl VncClient { + /// Exchange auth version with client + pub fn client_vencrypt_init(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + + // VeNCrypt version 0.2. + if buf[0] != 0 || buf[1] != 2 { + let mut buf = Vec::new(); + // Reject version. + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + self.write_msg(&buf); + return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); + } else { + let mut buf = Vec::new(); + // Accept version. + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + // Number of sub-auths. + buf.append(&mut (1_u8).to_be_bytes().to_vec()); + // The supported auth. + buf.append(&mut (self.subauth as u32).to_be_bytes().to_vec()); + self.write_msg(&buf); + } + + self.update_event_handler(4, VncClient::client_vencrypt_auth); + Ok(()) + } + + /// Encrypted Channel Initialize. + pub fn client_vencrypt_auth(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + let buf = [buf[0], buf[1], buf[2], buf[3]]; + let auth = u32::from_be_bytes(buf); + + if auth != self.subauth as u32 { + let mut buf = Vec::new(); + // Reject auth. + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + self.write_msg(&buf); + error!("Authentication failed"); + return Err(anyhow!(VncError::AuthFailed(String::from( + "Authentication failed" + )))); + } + + let mut buf = Vec::new(); + // Accept auth. + buf.append(&mut (1_u8).to_be_bytes().to_vec()); + self.write_msg(&buf); + + if let Some(tls_config) = &self.server.lock().unwrap().tls_config { + match rustls::ServerConnection::new(Arc::clone(tls_config)) { + Ok(tls_conn) => { + self.tls_conn = Some(tls_conn); + } + Err(e) => { + error!("Can't make ServerConnection: {}", e); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Can't make ServerConnection", + )))); + } + } + } else { + error!("There is no ventrypt configuration!"); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "There is no ventrypt configuration!", + )))); + } + + self.update_event_handler(1, VncClient::tls_handshake); + self.modify_event(NotifierOperation::Modify, 1)?; + Ok(()) + } + + /// Tls handshake. + pub fn tls_handshake(&mut self) -> Result<()> { + if let Some(tc) = &mut self.tls_conn { + info!("tls_handshake"); + match tc.read_tls(&mut self.stream) { + Err(err) => { + error!("{:?}", err); + return Err(anyhow!(VncError::AuthFailed(format!("{:?}", err)))); + } + Ok(0) => { + error!("EOF"); + return Err(anyhow!(VncError::AuthFailed(String::from("EOF")))); + } + Ok(_) => {} + } + + if let Err(err) = tc.process_new_packets() { + error!("Cannot process packet: {:?}", err); + let rc = tc.write_tls(&mut self.stream); + if rc.is_err() { + return Err(anyhow!(VncError::AuthFailed(format!("{:?}", rc)))); + } + return Err(anyhow!(VncError::AuthFailed(format!("{:?}", err)))); + } + + if tc.wants_write() { + if let Err(err) = tc.write_tls(&mut self.stream) { + return Err(anyhow!(VncError::AuthFailed(format!("{:?}", err)))); + } + } + + if tc.is_handshaking() { + // Tls handshake continue. + self.handle_msg = VncClient::tls_handshake; + } else { + info!("Finished tls handshaking"); + // Tls handshake finished. + self.modify_event(NotifierOperation::Modify, 0)?; + if let Err(e) = self.handle_vencrypt_subauth() { + return Err(e); + } + } + } else { + return Err(anyhow!(VncError::AuthFailed(String::from( + "Handshake failed" + )))); + } + Ok(()) + } + + fn handle_vencrypt_subauth(&mut self) -> Result<()> { + match self.subauth { + SubAuthState::VncAuthVencryptX509Sasl => { + self.expect = 4; + self.handle_msg = VncClient::get_mechname_length; + if let Err(e) = self.start_sasl_auth() { + return Err(e); + } + } + SubAuthState::VncAuthVencryptX509None => { + let buf = [0u8; 4]; + self.write_msg(&buf); + self.expect = 1; + self.handle_msg = VncClient::handle_client_init; + } + _ => { + let mut buf: Vec = Vec::new(); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + if self.version.minor >= 8 { + let err_msg: String = "Unsupported subauth type".to_string(); + buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); + buf.append(&mut err_msg.as_bytes().to_vec()); + self.write_msg(&buf); + } + error!("Unsupported subauth type"); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Unsupported subauth type", + )))); + } + } + Ok(()) + } +} + +/// Config encrypted channel. +/// +/// # Arguments +/// +/// * `args` - tls configuration. +pub fn make_vencrypt_config(args: &TlsCreds) -> Result> { + let server_cacert = args.dir.clone() + "/" + TLS_CREDS_SERVER_CACERT; + let server_cert = args.dir.clone() + "/" + TLS_CREDS_SERVERCERT; + let server_key = args.dir.clone() + "/" + TLS_CREDS_SERVERKEY; + + // Load cacert.pem and provide verification for certificate chain + let client_auth = if args.verifypeer { + let roots; + match load_certs(server_cacert.as_str()) { + Ok(r) => roots = r, + Err(e) => return Err(e), + } + let mut client_auth_roots = RootCertStore::empty(); + for root in roots { + client_auth_roots.add(&root).unwrap(); + } + if CLIENT_REQUIRE_AUTH { + AllowAnyAuthenticatedClient::new(client_auth_roots) + } else { + AllowAnyAnonymousOrAuthenticatedClient::new(client_auth_roots) + } + } else { + NoClientAuth::new() + }; + + // Cipher suiter. + let suites = TLS_CIPHER_SUITES.to_vec(); + // Tls protocol version supported by server. + let versions = TLS_VERSIONS.to_vec(); + let certs: Vec; + let privkey: rustls::PrivateKey; + // Server certificate. + match load_certs(server_cert.as_str()) { + Ok(c) => certs = c, + Err(e) => return Err(e), + }; + // Server private key. + match load_private_key(server_key.as_str()) { + Ok(key) => privkey = key, + Err(e) => return Err(e), + } + + let mut config = rustls::ServerConfig::builder() + .with_cipher_suites(&suites) + .with_kx_groups(&TLS_KX_GROUPS) + .with_protocol_versions(&versions) + .expect("Unsupported cipher-suite/version") + .with_client_cert_verifier(client_auth) + .with_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![]) + .expect("Invalid Certificate format"); + + // SSLKEYLOGFILE=path configure key log path. + config.key_log = Arc::new(rustls::KeyLogFile::new()); + // Limit data size in one time. + config.session_storage = rustls::server::ServerSessionMemoryCache::new(MAXIMUM_SESSION_STORAGE); + // Tickets. + config.ticketer = rustls::Ticketer::new().unwrap(); + config.alpn_protocols = Vec::new(); + + Ok(Arc::new(config)) +} + +/// load private key +/// +/// # Arguments +/// +/// * `filepath` - the path private key. +fn load_private_key(filepath: &str) -> Result { + let keyfile; + match File::open(filepath) { + Ok(file) => keyfile = file, + Err(e) => { + error!("Private key file is no exit!: {}", e); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Private key file is no exit!", + )))); + } + } + + let mut reader = BufReader::new(keyfile); + loop { + match rustls_pemfile::read_one(&mut reader).expect("Cannot parse private key .pem file") { + Some(rustls_pemfile::Item::RSAKey(key)) => return Ok(rustls::PrivateKey(key)), + Some(rustls_pemfile::Item::PKCS8Key(key)) => return Ok(rustls::PrivateKey(key)), + Some(rustls_pemfile::Item::ECKey(key)) => return Ok(rustls::PrivateKey(key)), + None => break, + _ => {} + } + } + + error!("Load private key failed!"); + Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Load private key failed!" + )))) +} + +/// Load certificate. +/// +/// # Arguments +/// +/// * `filepath` - the file path of certificate. +fn load_certs(filepath: &str) -> Result> { + let certfile; + match File::open(filepath) { + Ok(file) => certfile = file, + Err(e) => { + error!("Cannot open certificate file: {}", e); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Cannot open certificate file", + )))); + } + } + let mut reader = BufReader::new(certfile); + let certs = rustls_pemfile::certs(&mut reader) + .unwrap() + .iter() + .map(|v| rustls::Certificate(v.clone())) + .collect(); + Ok(certs) +} -- Gitee From ce544ec6cdede1c54bf78b7fa3fdc22301935dfc Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Aug 2022 17:33:34 +0800 Subject: [PATCH 0304/2187] VNC: Add sasl authentication 1. Parse authentication configuration during initialization. 2. Add sasl authentication. Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/syscall.rs | 20 +- machine/src/standard_vm/x86_64/syscall.rs | 20 +- machine_manager/src/config/mod.rs | 6 + machine_manager/src/config/sasl_auth.rs | 83 ++++ vnc/Cargo.toml | 1 + vnc/src/auth.rs | 449 ++++++++++++++++++++- vnc/src/client.rs | 4 + vnc/src/server.rs | 12 + 8 files changed, 586 insertions(+), 9 deletions(-) create mode 100644 machine_manager/src/config/sasl_auth.rs diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index eb08fdb16..f93db530f 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -47,6 +47,7 @@ const F_DUPFD_CLOEXEC: u32 = F_LINUX_SPECIFIC_BASE + 6; const TCGETS: u32 = 0x5401; const TCSETS: u32 = 0x5402; const TIOCGWINSZ: u32 = 0x5413; +const FIONREAD: u32 = 0x541B; const FIOCLEX: u32 = 0x5451; const FIONBIO: u32 = 0x5421; const KVM_RUN: u32 = 0xae80; @@ -55,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 63 syscalls -/// * aarch64-unknown-musl: 61 syscalls +/// * aarch64-unknown-gnu: 77 syscalls +/// * aarch64-unknown-musl: 75 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -75,9 +76,11 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), BpfRule::new(libc::SYS_epoll_ctl), + BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + BpfRule::new(libc::SYS_sendmmsg), BpfRule::new(libc::SYS_recvfrom), BpfRule::new(libc::SYS_mremap), BpfRule::new(libc::SYS_io_setup), @@ -116,6 +119,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), BpfRule::new(libc::SYS_socket), + BpfRule::new(libc::SYS_bind), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), BpfRule::new(libc::SYS_clone), @@ -124,6 +128,17 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_getsockname), BpfRule::new(libc::SYS_getpeername), BpfRule::new(libc::SYS_nanosleep), + BpfRule::new(libc::SYS_getuid), + BpfRule::new(libc::SYS_geteuid), + BpfRule::new(libc::SYS_getgid), + BpfRule::new(libc::SYS_getegid), + BpfRule::new(libc::SYS_gettid), + BpfRule::new(libc::SYS_getdents64), + BpfRule::new(libc::SYS_clock_gettime), + BpfRule::new(libc::SYS_getsockopt), + BpfRule::new(libc::SYS_uname), + BpfRule::new(libc::SYS_sysinfo), + BpfRule::new(libc::SYS_faccessat), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_rt_sigaction), @@ -148,6 +163,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_USER_MEMORY_REGION) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IOEVENTFD) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SIGNAL_MSI) + .add_constraint(SeccompCmpOpt::Eq, 1, FIONREAD) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_VSOCK_SET_GUEST_CID() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_VSOCK_SET_RUNNING() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_CALL() as u32) diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 60ee1ee57..f69dfca45 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -47,6 +47,7 @@ const F_DUPFD_CLOEXEC: u32 = F_LINUX_SPECIFIC_BASE + 6; const TCGETS: u32 = 0x5401; const TCSETS: u32 = 0x5402; const TIOCGWINSZ: u32 = 0x5413; +const FIONREAD: u32 = 0x541B; const FIOCLEX: u32 = 0x5451; const FIONBIO: u32 = 0x5421; const KVM_RUN: u32 = 0xae80; @@ -55,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 65 syscalls -/// * x86_64-unknown-musl: 64 syscalls +/// * x86_64-unknown-gnu: 79 syscalls +/// * x86_64-unknown-musl: 78 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -77,9 +78,11 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), BpfRule::new(libc::SYS_epoll_ctl), + BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + BpfRule::new(libc::SYS_sendmmsg), BpfRule::new(libc::SYS_recvfrom), BpfRule::new(libc::SYS_mremap), BpfRule::new(libc::SYS_io_setup), @@ -122,6 +125,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_readlinkat), BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_socket), + BpfRule::new(libc::SYS_bind), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), #[cfg(target_env = "musl")] @@ -133,6 +137,17 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_getsockname), BpfRule::new(libc::SYS_getpeername), BpfRule::new(libc::SYS_nanosleep), + BpfRule::new(libc::SYS_getuid), + BpfRule::new(libc::SYS_geteuid), + BpfRule::new(libc::SYS_getgid), + BpfRule::new(libc::SYS_getegid), + BpfRule::new(libc::SYS_gettid), + BpfRule::new(libc::SYS_getdents64), + BpfRule::new(libc::SYS_clock_gettime), + BpfRule::new(libc::SYS_getsockopt), + BpfRule::new(libc::SYS_uname), + BpfRule::new(libc::SYS_sysinfo), + BpfRule::new(libc::SYS_faccessat), BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_setsockopt), @@ -158,6 +173,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_USER_MEMORY_REGION) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IOEVENTFD) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SIGNAL_MSI) + .add_constraint(SeccompCmpOpt::Eq, 1, FIONREAD) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_VSOCK_SET_GUEST_CID() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_VSOCK_SET_RUNNING() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_CALL() as u32) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 77bc79e6f..041b7e714 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -25,6 +25,7 @@ pub use network::*; pub use numa::*; pub use pci::*; pub use rng::*; +pub use sasl_auth::*; pub use scsi::*; pub use tls_creds::*; pub use usb::*; @@ -46,6 +47,7 @@ mod network; mod numa; mod pci; mod rng; +mod sasl_auth; mod scsi; mod tls_creds; mod usb; @@ -78,6 +80,7 @@ pub enum ObjConfig { Rng(RngObjConfig), Zone(MemZoneConfig), Tls(TlsCredObjConfig), + Sasl(SaslAuthObjConfig), } fn parse_rng_obj(object_args: &str) -> Result { @@ -221,6 +224,9 @@ impl VmConfig { "tls-creds-x509" => { self.add_tlscred(object_args)?; } + "authz-simple" => { + self.add_saslauth(object_args)?; + } _ => { bail!("Unknow object type: {:?}", &device_type); } diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs new file mode 100644 index 000000000..495aa5926 --- /dev/null +++ b/machine_manager/src/config/sasl_auth.rs @@ -0,0 +1,83 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::config::{ + ConfigError, ObjConfig, {CmdParser, VmConfig}, +}; +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct SaslAuthObjConfig { + /// Object Id. + pub id: String, + /// Authentication User Name. + pub identity: String, +} + +impl VmConfig { + pub fn add_saslauth(&mut self, saslauth_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("authz-simple"); + cmd_parser.push("").push("id").push("identity"); + cmd_parser.parse(saslauth_config)?; + + let mut saslauth = SaslAuthObjConfig::default(); + if let Some(id) = cmd_parser.get_value::("id")? { + saslauth.id = id; + } else { + return Err(anyhow!(ConfigError::FieldIsMissing("id", "vnc sasl_auth"))); + } + + if let Some(identity) = cmd_parser.get_value::("identity")? { + saslauth.identity = identity; + } + + let id = saslauth.id.clone(); + if self.object.get(&id).is_none() { + let saslauth_config = ObjConfig::Sasl(saslauth); + self.object.insert(id, saslauth_config); + } else { + return Err(anyhow!(ConfigError::IdRepeat("saslauth".to_string(), id))); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_saslauth() { + let id = String::from("authz0"); + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_object("authz-simple,id=authz0,identity=test") + .is_ok()); + assert!(vm_config.object.get(&id).is_some()); + if let Some(obj_cfg) = vm_config.object.get(&id) { + if let ObjConfig::Sasl(saslauth) = obj_cfg { + assert_eq!(saslauth.identity, "test".to_string()); + } + } + + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_object("authz-simple,id=authz0").is_ok()); + assert!(vm_config.object.get(&id).is_some()); + if let Some(obj_cfg) = vm_config.object.get(&id) { + if let ObjConfig::Sasl(saslauth) = obj_cfg { + assert!(saslauth.identity == "".to_string()); + } + } + } +} diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index 6de86ec56..e42cd0b61 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -18,6 +18,7 @@ once_cell = "1.9.0" sscanf = "0.2.1" rustls = "0.20.6" rustls-pemfile = "1.0.0" +sasl2-sys = "0.1.20" bitintr = "0.2.0" usb = { path = "../usb" } address_space = { path = "../address_space" } diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 15f3123fe..d3cf3370a 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -10,8 +10,29 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::client::VncClient; -use anyhow::Result; +use crate::{client::VncClient, VncError}; +use anyhow::{anyhow, Result}; +use libc::{c_char, c_int, c_uint, c_void}; +use sasl2_sys::prelude::{ + sasl_conn_t, sasl_dispose, sasl_getprop, sasl_listmech, sasl_security_properties_t, + sasl_server_init, sasl_server_new, sasl_server_start, sasl_server_step, sasl_setprop, + sasl_ssf_t, SASL_CONTINUE, SASL_OK, SASL_SEC_PROPS, SASL_SSF, SASL_SSF_EXTERNAL, + SASL_SUCCESS_DATA, +}; +use sasl2_sys::sasl::SASL_USERNAME; +use std::ffi::{CStr, CString}; +use std::ptr; +use util::byte_code::ByteCode; + +/// Vnc Service. +const SERVICE: &str = "vnc"; +/// Saslauthd service can fetch the configuration in the /etc/sasl2/${APP_NAME}.conf. +const APP_NAME: &str = "stratovirt"; +const MECHNAME_MAX_LEN: u32 = 100; +const MECHNAME_MIN_LEN: u32 = 1; +const SASL_DATA_MAX_LEN: u32 = 1024 * 1024; +/// Minimum supported encryption length of ssf layer in sasl. +const MIN_SSF_LENGTH: usize = 56; /// Authentication type #[derive(Clone, Copy)] @@ -23,22 +44,60 @@ pub enum AuthState { Sasl = 20, } -/// Authentication and encryption method +/// Authentication and encryption method. #[derive(Clone, Copy)] pub enum SubAuthState { + /// Send plain Message + no auth. VncAuthVencryptPlain = 256, + /// Tls vencrypt with x509 + no auth. VncAuthVencryptX509None = 260, + /// Tls vencrypt with x509 + sasl. VncAuthVencryptX509Sasl = 263, + /// Tls vencrypt + sasl. VncAuthVencryptTlssasl = 264, } -/// Configuration for authentication -/// Identity: authentication user +/// Struct of sasl authentiation. +#[derive(Debug, Clone)] +pub struct Sasl { + /// State of sasl connection . + pub sasl_conn: *mut sasl_conn_t, + /// Identity user. + pub identity: String, + /// Mech list server support. + pub mech_list: String, + /// Authentication mechanism currently in use. + pub mech_name: String, + /// State of auth. + pub sasl_stage: SaslStage, + /// Security layer in sasl. + pub want_ssf: bool, + /// Strength of ssf. + pub run_ssf: u32, +} + +impl Sasl { + pub fn default() -> Self { + Sasl { + sasl_conn: ptr::null_mut() as *mut sasl_conn_t, + identity: String::new(), + mech_list: String::new(), + mech_name: String::new(), + sasl_stage: SaslStage::SaslServerStart, + want_ssf: false, + run_ssf: 0, + } + } +} + +/// Configuration for authentication. +/// Identity: authentication user. #[derive(Debug, Clone, Default)] pub struct SaslAuth { pub identity: String, } +/// Authentication stage. #[derive(Clone, Copy, PartialEq, Debug)] pub enum SaslStage { SaslServerStart, @@ -46,11 +105,391 @@ pub enum SaslStage { } impl VncClient { + /// Get length of mechname send form client. pub fn get_mechname_length(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + let len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]); + + if len > MECHNAME_MAX_LEN { + error!("SASL mechname too long"); + return Err(anyhow!(VncError::AuthFailed(String::from( + "SASL mechname too long" + )))); + } + if len < MECHNAME_MIN_LEN { + error!("SASL mechname too short"); + return Err(anyhow!(VncError::AuthFailed(String::from( + "SASL mechname too short" + )))); + } + + self.update_event_handler(len as usize, VncClient::get_sasl_mechname); Ok(()) } + /// Start sasl authentication. + /// 1. Sals server init. + /// 2. Get the mechlist support by Sasl server. + /// 3. Send the mechlist to client. pub fn start_sasl_auth(&mut self) -> Result<()> { + if let Err(e) = self.sasl_server_init() { + return Err(e); + } + + if let Err(e) = self.set_ssf_for_sasl() { + return Err(e); + } + + if let Err(e) = self.send_mech_list() { + return Err(e); + } + + Ok(()) + } + + /// Get authentication mechanism supported by client. + pub fn get_sasl_mechname(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + let mech_name = String::from_utf8_lossy(buf).to_string(); + + let mech_list: Vec<&str> = self.sasl.mech_list.split(',').collect(); + for mech in mech_list { + if mech_name == *mech { + self.sasl.mech_name = mech_name; + break; + } + } + // Unsupported mechanism. + if self.sasl.mech_name.is_empty() { + return Err(anyhow!(VncError::AuthFailed( + "Unsupported mechanism".to_string() + ))); + } + + self.update_event_handler(4, VncClient::get_authmessage_length); + Ok(()) + } + + /// Length of client authentication message. + pub fn get_authmessage_length(&mut self) -> Result<()> { + let buf = self.buffpool.read_front(self.expect); + let buf = [buf[0], buf[1], buf[2], buf[3]]; + let len = u32::from_be_bytes(buf); + + if len > SASL_DATA_MAX_LEN { + error!("SASL start len too large"); + return Err(anyhow!(VncError::AuthFailed( + "SASL start len too large".to_string() + ))); + } + + if len == 0 { + return self.client_sasl_auth(); + } + self.update_event_handler(len as usize, VncClient::client_sasl_auth); + Ok(()) + } + + /// Receive the authentication information from client and return the result. + pub fn client_sasl_auth(&mut self) -> Result<()> { + info!("Sasl Authentication"); + let buf = self.buffpool.read_front(self.expect); + + let mut client_data = buf.to_vec(); + let mut client_len: c_uint = 0; + if self.expect > 0 { + client_len = (self.expect - 1) as c_uint; + client_data[self.expect - 1] = 0_u8; + } + + let err: c_int; + let mut serverout: *const c_char = ptr::null_mut(); + let mut serverout_len: c_uint = 0; + let mech_name = CString::new(self.sasl.mech_name.as_str()).unwrap(); + + // Start authentication. + if self.sasl.sasl_stage == SaslStage::SaslServerStart { + unsafe { + err = sasl_server_start( + self.sasl.sasl_conn, + mech_name.as_ptr(), + client_data.as_ptr() as *const c_char, + client_len, + &mut serverout, + &mut serverout_len, + ) + } + } else { + unsafe { + err = sasl_server_step( + self.sasl.sasl_conn, + client_data.as_ptr() as *const c_char, + client_len, + &mut serverout, + &mut serverout_len, + ) + } + } + + if err != SASL_OK && err != SASL_CONTINUE { + unsafe { sasl_dispose(&mut self.sasl.sasl_conn) } + error!("Auth failed!"); + return Err(anyhow!(VncError::AuthFailed("Auth failed!".to_string()))); + } + if serverout_len > SASL_DATA_MAX_LEN { + unsafe { sasl_dispose(&mut self.sasl.sasl_conn) } + error!("SASL data too long"); + return Err(anyhow!(VncError::AuthFailed( + "SASL data too long".to_string() + ))); + } + + let mut buf = Vec::new(); + if serverout_len > 0 { + // Authentication related information. + let serverout = unsafe { CStr::from_ptr(serverout as *const c_char) }; + let auth_message = String::from(serverout.to_str().unwrap()); + buf.append(&mut ((serverout_len + 1) as u32).to_be_bytes().to_vec()); + buf.append(&mut auth_message.as_bytes().to_vec()); + } else { + buf.append(&mut (0_u32).to_be_bytes().to_vec()); + } + + if err == SASL_OK { + buf.append(&mut (1_u8).as_bytes().to_vec()); + } else if err == SASL_CONTINUE { + buf.append(&mut (0_u8).as_bytes().to_vec()); + } + + if err == SASL_CONTINUE { + // Authentication continue. + self.sasl.sasl_stage = SaslStage::SaslServerStep; + self.update_event_handler(4, VncClient::get_authmessage_length); + return Ok(()); + } else { + if let Err(err) = self.sasl_check_ssf() { + // Reject auth: the strength of ssf is too weak. + auth_reject(&mut buf); + self.write_msg(&buf); + return Err(err); + } + + if let Err(err) = self.sasl_check_authz() { + // Reject auth: wrong sasl username. + auth_reject(&mut buf); + self.write_msg(&buf); + return Err(err); + } + // Accpet auth. + buf.append(&mut (0_u32).as_bytes().to_vec()); + } + + self.write_msg(&buf); + self.update_event_handler(1, VncClient::handle_client_init); + Ok(()) + } + + /// Sasl server init. + fn sasl_server_init(&mut self) -> Result<()> { + let mut err: c_int; + let service = CString::new(SERVICE).unwrap(); + let appname = CString::new(APP_NAME).unwrap(); + let local_addr = self + .stream + .local_addr() + .unwrap() + .to_string() + .replace(":", ";"); + let remote_addr = self + .stream + .peer_addr() + .unwrap() + .to_string() + .replace(":", ";"); + info!("local_addr: {} remote_addr: {}", local_addr, remote_addr); + let local_addr = CString::new(local_addr).unwrap(); + let remote_addr = CString::new(remote_addr).unwrap(); + // Sasl server init. + unsafe { + err = sasl_server_init(ptr::null_mut(), appname.as_ptr()); + } + if err != SASL_OK { + error!("SASL_FAIL error code {}", err); + return Err(anyhow!(VncError::AuthFailed(format!( + "SASL_FAIL error code {}", + err + )))); + } + unsafe { + err = sasl_server_new( + service.as_ptr(), + ptr::null_mut(), + ptr::null_mut(), + local_addr.as_ptr(), + remote_addr.as_ptr(), + ptr::null_mut(), + SASL_SUCCESS_DATA, + &mut self.sasl.sasl_conn, + ); + } + if err != SASL_OK { + error!("SASL_FAIL error code {}", err); + return Err(anyhow!(VncError::AuthFailed(format!( + "SASL_FAIL error code {}", + err + )))); + } + + Ok(()) + } + + /// Set properties for sasl. + fn set_ssf_for_sasl(&mut self) -> Result<()> { + // Set the relevant properties of sasl. + let mut err: c_int; + let ssf: sasl_ssf_t = 256; + let ssf = &ssf as *const sasl_ssf_t; + unsafe { + err = sasl_setprop( + self.sasl.sasl_conn, + SASL_SSF_EXTERNAL as i32, + ssf as *const c_void, + ); + } + if err != SASL_OK { + error!("SASL_FAIL error code {}", err); + return Err(anyhow!(VncError::AuthFailed(format!( + "SASL_FAIL error code {}", + err + )))); + } + + // Already using tls, disable ssf in sasl. + let props_name = ptr::null_mut() as *mut *const c_char; + let props_value = ptr::null_mut() as *mut *const c_char; + let saslprops = sasl_security_properties_t { + min_ssf: 0, + max_ssf: 0, + maxbufsize: 8192, + security_flags: 0, + property_names: props_name, + property_values: props_value, + }; + + let props = &saslprops as *const sasl_security_properties_t; + unsafe { + err = sasl_setprop( + self.sasl.sasl_conn, + SASL_SEC_PROPS.try_into().unwrap(), + props as *const c_void, + ); + } + if err != SASL_OK { + error!("SASL_FAIL error code {}", err); + return Err(anyhow!(VncError::AuthFailed(format!( + "SASL_FAIL error code {}", + err + )))); + } + + Ok(()) + } + + /// Get the mechlist support by Sasl server. + /// Send the mechlist to client. + fn send_mech_list(&mut self) -> Result<()> { + let err: c_int; + let prefix = CString::new("").unwrap(); + let sep = CString::new(",").unwrap(); + let suffix = CString::new("").unwrap(); + let mut mechlist: *const c_char = ptr::null_mut(); + unsafe { + err = sasl_listmech( + self.sasl.sasl_conn, + ptr::null_mut(), + prefix.as_ptr(), + sep.as_ptr(), + suffix.as_ptr(), + &mut mechlist, + ptr::null_mut(), + ptr::null_mut(), + ); + } + if err != SASL_OK || mechlist.is_null() { + error!("SASL_FAIL: no support sasl mechlist"); + return Err(anyhow!(VncError::AuthFailed( + "SASL_FAIL: no support sasl mechlist".to_string() + ))); + } + let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) }; + self.sasl.mech_list = String::from(mech_list.to_str().unwrap()); + let mut buf = Vec::new(); + let len = self.sasl.mech_list.len(); + buf.append(&mut (len as u32).to_be_bytes().to_vec()); + buf.append(&mut self.sasl.mech_list.as_bytes().to_vec()); + self.write_msg(&buf); + + Ok(()) + } + + /// Check whether the ssf layer of sasl meets the strength requirements. + fn sasl_check_ssf(&mut self) -> Result<()> { + if !self.sasl.want_ssf { + return Ok(()); + } + let err: c_int; + let mut val: *const c_void = ptr::null_mut(); + unsafe { err = sasl_getprop(self.sasl.sasl_conn, SASL_SSF as c_int, &mut val) } + if err != SASL_OK { + error!("sasl_getprop: internal error"); + return Err(anyhow!(VncError::AuthFailed(String::from( + "sasl_getprop: internal error" + )))); + } + + let ssf: usize = unsafe { *(val as *const usize) }; + if ssf < MIN_SSF_LENGTH { + error!("SASL SSF too weak"); + return Err(anyhow!(VncError::AuthFailed(String::from( + "SASL SSF too weak" + )))); + } + + self.sasl.run_ssf = 1; Ok(()) } + + /// Check username. + fn sasl_check_authz(&mut self) -> Result<()> { + let mut val: *const c_void = ptr::null_mut(); + let err = unsafe { sasl_getprop(self.sasl.sasl_conn, SASL_USERNAME as c_int, &mut val) }; + if err != SASL_OK { + return Err(anyhow!(VncError::AuthFailed(String::from( + "Cannot fetch SASL username" + )))); + } + if val.is_null() { + return Err(anyhow!(VncError::AuthFailed(String::from( + "No SASL username set" + )))); + } + let username = unsafe { CStr::from_ptr(val as *const c_char) }; + let username = String::from(username.to_str().unwrap()); + + if self.sasl.identity != username { + return Err(anyhow!(VncError::AuthFailed(String::from( + "No SASL username set" + )))); + } + + Ok(()) + } +} + +/// Auth reject. +fn auth_reject(buf: &mut Vec) { + let reason = String::from("Authentication failed"); + buf.append(&mut (1_u32).to_be_bytes().to_vec()); + buf.append(&mut (reason.len() as u32).to_be_bytes().to_vec()); + buf.append(&mut reason.as_bytes().to_vec()); } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 779631357..1d33c6fa1 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -12,6 +12,7 @@ use crate::VncError; use crate::{ + auth::Sasl, auth::{AuthState, SubAuthState}, pixman::{get_image_height, get_image_width, PixelFormat}, round_up_div, @@ -234,6 +235,8 @@ pub struct VncClient { pub server: Arc>, /// Tls server connection. pub tls_conn: Option, + /// Configuration for sasl authentication. + pub sasl: Sasl, /// Data storage type for client. pub big_endian: bool, /// State flags whether the image needs to be updated for the client. @@ -281,6 +284,7 @@ impl VncClient { handlers: Vec::new(), server, tls_conn: None, + sasl: Sasl::default(), big_endian: false, state: UpdateState::No, dirty_bitmap: Bitmap::::new( diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 1bff117e9..d57125390 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::{ + auth::SaslAuth, auth::{AuthState, SubAuthState}, client::VncClient, data::keycode::KEYSYM2KEYCODE, @@ -187,6 +188,14 @@ impl VncServer { self.tlscreds = Some(tlscred); } + // Sasl configuration. + if let Some(ObjConfig::Sasl(sasl_auth)) = object.get(&vnc_cfg.sasl_authz) { + let saslauth = SaslAuth { + identity: sasl_auth.identity.clone(), + }; + self.saslauth = Some(saslauth); + } + // Server.auth. if let Err(err) = self.setup_auth() { return Err(err); @@ -435,6 +444,9 @@ impl VncServer { server, self.server_image, ); + if let Some(saslauth) = &self.saslauth { + client.sasl.identity = saslauth.identity.clone(); + } client.write_msg("RFB 003.008\n".to_string().as_bytes()); info!("{:?}", client.stream); -- Gitee From 99e005cba8af1bcbe6b7920986bf64addb05846c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 29 Oct 2022 10:16:07 +0800 Subject: [PATCH 0305/2187] VNC: Add docs for VNC Add the document description for VNC configuration, including the listening port, encryption and authentication. Signed-off-by: Xiao Ye --- docs/config_guidebook.md | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 98b93c461..c93ae8615 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -789,6 +789,54 @@ Six properties can be set for virtio-scsi hd. -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 ``` Note: Only support scsi-id=0 and lun number should start from 0 Now. +### 2.18 VNC +VNC can provide the users with way to login virtual machines remotely. + +In order to use VNC, the ip and port value must be configured. The IP address can be set to a specified value or `0.0.0.0`, which means that all IP addresses on the host network card are monitored + +```shell +-vnc 0.0.0.0:0 +``` + +Tls encryption is an optional configuration.Three properties can be set for encrypted transmission: + +* certificate type. +* id: unique object id. +* dir: certificate directory. You should place a legal institutional certificate, a server certificate, and a private key for certificate encryption in this directory. + +```shell +-object tls-creds-x509,id=vnc-tls-creds0,dir=/etc/pki/vnc +``` + +Authentication is an optional configuration, it depends on the saslauth service . To use this function, you must ensure that the saslauthd service is running normally, and configure the supported authentication mechanism in `/etc/sasl2/stratovirt. conf` + +Sample configuration for file `/etc/sasl2/stratovirt.conf` +```shell +# Using the saslauthd service +pwcheck_method: saslauthd +# Authentication mechanism +mech_list: plain +``` + +Three properties can be set for Authentication: + +- authz-simple +- id: unique object id. +- identity: specify the username that can log in. + +```shell +-object authz-simple,id=authz0,identity=username +``` + +Sample Configuration: + +```shell +-object authz-simple,id=authz0,identity=username +-object tls-creds-x509,id=vnc-tls-creds0,dir=/etc/pki/vnc +-vnc 0.0.0.0:0,tls-creds=vnc-tls-creds0,sasl=on,sasl-authz=authz0 +``` + +Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. ## 3. Trace -- Gitee From a219c359a446fda9d6c5bbd14daf6a0d04a2884c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 3 Nov 2022 15:26:58 +0800 Subject: [PATCH 0306/2187] Fix: don't realize vsock device when VM reboot The fd is configured for the vsock device. Therefore, the fd of the devices is not released when the VM is restarted. If the reset operation is performed on the device, the fd.set_owner command is executed repeatedly. The kernle don't allow the set_owner command to be executed repeatedly. As a result, the VM fails to restarted. Therefor, when the VM is restarted, it only need to suspend the device. Fix me: The device queue may need to perform corresponding operations. Signed-off-by: Mingwang Li --- virtio/src/vhost/kernel/vsock.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 59048878e..ab13c4ec8 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -327,14 +327,7 @@ impl VirtioDevice for Vsock { } fn reset(&mut self) -> Result<()> { - // No need to close fd manually, because rust will - // automatically cleans up variables at the end of the lifecycle. - self.backend = None; - self.state = VsockState::default(); - self.event_queue = None; - self.interrupt_cb = None; - - self.realize() + self.backend.as_ref().unwrap().set_running(false) } } -- Gitee From 8e1562ca3c2a2f89497bfd92505e3660e756f09c Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 3 Nov 2022 17:01:01 +0800 Subject: [PATCH 0307/2187] VNC: Check before updating the mouse data Check before updating the mouse data. The image length of cursor should be equal to cursor.length * cursor.width * bytes_per_pixel. Signed-off-by: Xiao Ye --- vnc/src/vnc.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 03dd96ac9..aaeccbece 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -643,6 +643,9 @@ pub fn display_cursor_define( cursor: &mut DisplayMouse, mask: &mut Vec, ) { + if cursor.data.len() != ((cursor.width * cursor.height) as usize) * bytes_per_pixel() { + return; + } let mut buf = Vec::new(); if client.has_feature(VncFeatures::VncFeatureAlphaCursor) { buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); -- Gitee From 8a5a44dce013a4fefeb111bd9eb779bc1a89ae4c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 3 Nov 2022 17:40:30 +0800 Subject: [PATCH 0308/2187] virtio-console: Add two configuration fields of the VirtioConsoleConfig Add two configuration fields of the VirtioConsoleConfig according the virtio specification. Signed-off-by: Jinhao Gao --- virtio/src/console.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 9f4a902b8..49a92d609 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -46,6 +46,8 @@ const BUFF_SIZE: usize = 4096; #[derive(Copy, Clone, Debug, Default)] #[repr(C)] struct VirtioConsoleConfig { + cols: u16, + rows: u16, max_nr_ports: u32, emerg_wr: u32, } @@ -56,6 +58,8 @@ impl VirtioConsoleConfig { /// Create configuration of virtio-console devices. pub fn new() -> Self { VirtioConsoleConfig { + cols: 0_u16, + rows: 0_u16, max_nr_ports: 1_u32, emerg_wr: 0_u32, } -- Gitee From 7bdeac03fa1aa2380cde0d2881a6cfe2fab43d63 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 3 Nov 2022 17:08:39 +0800 Subject: [PATCH 0309/2187] virtio-console: Fix some problems of using virtio-console The console which users use the socket file or the "virsh console" command to log in will be stuck after reboot. It is due to the fact that the listening event of UnixStream connection between guest and virtio-console was deleted improperly. It also causes that the VM will be killed after reboot twice and the VM will fail to boot if users enter any character in console immediately after reboot. Add a console_connected status to indicate to the connectability between guest and console. Signed-off-by: Jinhao Gao --- devices/src/legacy/chardev.rs | 10 ++++++- virtio/src/console.rs | 50 ++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 4e75eef71..53bb4b227 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -284,7 +284,15 @@ impl EventNotifierHelper for Chardev { } } ChardevType::Socket { .. } => { - if let Some(listener) = chardev.lock().unwrap().listener.as_ref() { + if chardev.lock().unwrap().stream_fd.is_some() { + notifiers.push(EventNotifier::new( + NotifierOperation::Resume, + chardev.lock().unwrap().stream_fd.unwrap(), + None, + EventSet::IN | EventSet::HANG_UP, + Vec::new(), + )); + } else if let Some(listener) = chardev.lock().unwrap().listener.as_ref() { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, listener.as_raw_fd(), diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 49a92d609..7516b4d72 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -235,25 +235,27 @@ impl ConsoleHandler { )); } } - ChardevType::Socket { .. } => { - if let Some(stream_fd) = locked_chardev.stream_fd { + ChardevType::Socket { .. } => match locked_chardev.stream_fd { + Some(stream_fd) => { notifiers.push(EventNotifier::new( - NotifierOperation::Delete, + NotifierOperation::Park, stream_fd, None, EventSet::IN | EventSet::HANG_UP, Vec::new(), )); } - let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - listener_fd, - None, - EventSet::IN, - Vec::new(), - )); - } + None => { + let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); + notifiers.push(EventNotifier::new( + NotifierOperation::Delete, + listener_fd, + None, + EventSet::IN, + Vec::new(), + )); + } + }, _ => (), } notifiers @@ -315,6 +317,8 @@ pub struct Console { deactivate_evt: EventFd, /// Character device for redirection. chardev: Arc>, + /// Connectability status between guest and console. + console_connected: bool, } impl Console { @@ -332,6 +336,7 @@ impl Console { }, deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), chardev: Arc::new(Mutex::new(Chardev::new(console_cfg.chardev))), + console_connected: false, } } } @@ -428,13 +433,26 @@ impl VirtioDevice for Console { EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, )?; + self.console_connected = true; Ok(()) } fn deactivate(&mut self) -> Result<()> { + if self.console_connected { + self.deactivate_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) + } else { + Ok(()) + } + } + + fn reset(&mut self) -> Result<()> { self.deactivate_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + self.console_connected = false; + Ok(()) } } @@ -565,12 +583,12 @@ mod tests { //Check the configuration that needs to be read let offset = 0_u64; - let mut read_data: Vec = vec![0; 8]; - let expect_data: Vec = vec![1, 0, 0, 0, 0, 0, 0, 0]; + let mut read_data: Vec = vec![0; 12]; + let expect_data: Vec = vec![0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]; assert_eq!(console.read_config(offset, &mut read_data).is_ok(), true); assert_eq!(read_data, expect_data); - let offset = 0_u64; + let offset = 4_u64; let mut read_data: Vec = vec![0; 1]; let expect_data: Vec = vec![1]; assert_eq!(console.read_config(offset, &mut read_data).is_ok(), true); -- Gitee From 97c2632da0c15f84fbb5af0eb7452ce7afb46593 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 4 Nov 2022 15:18:46 +0800 Subject: [PATCH 0310/2187] virtio-rng: Don't process virtqueue if it is not enabled Signed-off-by: yezengruan --- virtio/src/rng.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 3c0309f18..f80db589a 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -83,6 +83,10 @@ impl RngHandler { let mut queue_lock = self.queue.lock().unwrap(); let mut need_interrupt = false; + if !queue_lock.is_enabled() { + return Ok(()); + } + while let Ok(elem) = queue_lock .vring .pop_avail(&self.mem_space, self.driver_features) -- Gitee From 14bf799f5f19da252f927d6731503b48a21ae3dd Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 12:25:18 +0800 Subject: [PATCH 0311/2187] VmConfig/object: Modify `object` element in VmConfig For all about VM's features command line `-object xxx`, the parameters are saved in VmConfig struct object element. If the features ID are the same, the parameter will be overwritten. For example: 1. For NUMA node: -object memory-backend-ram,id=tmp,size=xxx; 2. For RND device: -object rng-random,id=tmp,xxx; One of the parameters will be overwritten as above. How to slove: add new struct `ObjectConfig`, and add an element for each type of parameter. ``` pub struct ObjectConfig { pub rng_object: ..., pub mem_object: ..., ... } ``` Signed-off-by: Xinle.Guo --- machine/src/lib.rs | 9 +++-- machine_manager/src/config/mod.rs | 47 ++++++------------------- machine_manager/src/config/rng.rs | 31 +++++++++++++--- machine_manager/src/config/sasl_auth.rs | 23 +++++------- machine_manager/src/config/tls_creds.rs | 19 +++++----- vnc/src/server.rs | 12 +++---- vnc/src/vnc.rs | 5 ++- 7 files changed, 64 insertions(+), 82 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index bd4a1dd64..69aa0aeb0 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -53,7 +53,7 @@ use machine_manager::config::{ parse_scsi_controller, parse_scsi_device, parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, - NumaNodes, ObjConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::{ event_loop::EventLoop, @@ -948,10 +948,9 @@ pub trait MachineOps { ..Default::default() }; - if let Some(object_cfg) = vm_config.object.remove(&numa_config.mem_dev) { - if let ObjConfig::Zone(zone_config) = object_cfg { - numa_node.size = zone_config.size; - } + if let Some(mem_cfg) = vm_config.object.mem_object.remove(&numa_config.mem_dev) + { + numa_node.size = mem_cfg.size; } else { bail!( "Object for memory-backend-ram {} config not found", diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 041b7e714..5a40da99f 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -75,35 +75,12 @@ pub const FAST_UNPLUG_OFF: &str = "0"; pub const MAX_TAG_LENGTH: usize = 36; pub const MAX_NODES: u32 = 128; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum ObjConfig { - Rng(RngObjConfig), - Zone(MemZoneConfig), - Tls(TlsCredObjConfig), - Sasl(SaslAuthObjConfig), -} - -fn parse_rng_obj(object_args: &str) -> Result { - let mut cmd_params = CmdParser::new("rng-object"); - cmd_params.push("").push("id").push("filename"); - - cmd_params.parse(object_args)?; - let id = if let Some(obj_id) = cmd_params.get_value::("id")? { - obj_id - } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "rng-object"))); - }; - let filename = if let Some(name) = cmd_params.get_value::("filename")? { - name - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "filename", - "rng-object" - ))); - }; - let rng_obj_cfg = RngObjConfig { id, filename }; - - Ok(rng_obj_cfg) +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct ObjectConfig { + pub rng_object: HashMap, + pub mem_object: HashMap, + pub tls_object: HashMap, + pub sasl_object: HashMap, } /// This main config structure for Vm, contains Vm's basic configuration and devices. @@ -119,7 +96,7 @@ pub struct VmConfig { pub devices: Vec<(String, String)>, pub serial: Option, pub iothreads: Option>, - pub object: HashMap, + pub object: ObjectConfig, pub pflashs: Option>, pub dev_name: HashMap, pub global_config: HashMap, @@ -204,9 +181,8 @@ impl VmConfig { "rng-random" => { let rng_cfg = parse_rng_obj(object_args)?; let id = rng_cfg.id.clone(); - let object_config = ObjConfig::Rng(rng_cfg); - if self.object.get(&id).is_none() { - self.object.insert(id, object_config); + if self.object.rng_object.get(&id).is_none() { + self.object.rng_object.insert(id, rng_cfg); } else { bail!("Object: {} has been added", id); } @@ -214,9 +190,8 @@ impl VmConfig { "memory-backend-ram" => { let zone_config = self.add_mem_zone(object_args)?; let id = zone_config.id.clone(); - let object_config = ObjConfig::Zone(zone_config); - if self.object.get(&id).is_none() { - self.object.insert(id, object_config); + if self.object.mem_object.get(&id).is_none() { + self.object.mem_object.insert(id, zone_config); } else { bail!("Object: {} has been added", id); } diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 49aedfd5a..09e238120 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -14,7 +14,7 @@ use anyhow::{anyhow, bail, Result}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; -use super::{pci_args_check, ObjConfig}; +use super::pci_args_check; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; const MIN_BYTES_PER_SEC: u64 = 64; @@ -113,10 +113,8 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result Result Result { + let mut cmd_params = CmdParser::new("rng-object"); + cmd_params.push("").push("id").push("filename"); + + cmd_params.parse(object_args)?; + let id = if let Some(obj_id) = cmd_params.get_value::("id")? { + obj_id + } else { + return Err(anyhow!(ConfigError::FieldIsMissing("id", "rng-object"))); + }; + let filename = if let Some(name) = cmd_params.get_value::("filename")? { + name + } else { + return Err(anyhow!(ConfigError::FieldIsMissing( + "filename", + "rng-object" + ))); + }; + let rng_obj_cfg = RngObjConfig { id, filename }; + + Ok(rng_obj_cfg) +} + #[cfg(test)] mod tests { use crate::config::get_pci_bdf; diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs index 495aa5926..00e056c0c 100644 --- a/machine_manager/src/config/sasl_auth.rs +++ b/machine_manager/src/config/sasl_auth.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::config::{ - ConfigError, ObjConfig, {CmdParser, VmConfig}, + ConfigError, {CmdParser, VmConfig}, }; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; @@ -42,9 +42,8 @@ impl VmConfig { } let id = saslauth.id.clone(); - if self.object.get(&id).is_none() { - let saslauth_config = ObjConfig::Sasl(saslauth); - self.object.insert(id, saslauth_config); + if self.object.sasl_object.get(&id).is_none() { + self.object.sasl_object.insert(id, saslauth); } else { return Err(anyhow!(ConfigError::IdRepeat("saslauth".to_string(), id))); } @@ -64,20 +63,16 @@ mod tests { assert!(vm_config .add_object("authz-simple,id=authz0,identity=test") .is_ok()); - assert!(vm_config.object.get(&id).is_some()); - if let Some(obj_cfg) = vm_config.object.get(&id) { - if let ObjConfig::Sasl(saslauth) = obj_cfg { - assert_eq!(saslauth.identity, "test".to_string()); - } + assert!(vm_config.object.sasl_object.get(&id).is_some()); + if let Some(obj_cfg) = vm_config.object.sasl_object.get(&id) { + assert_eq!(obj_cfg.identity, "test".to_string()); } let mut vm_config = VmConfig::default(); assert!(vm_config.add_object("authz-simple,id=authz0").is_ok()); - assert!(vm_config.object.get(&id).is_some()); - if let Some(obj_cfg) = vm_config.object.get(&id) { - if let ObjConfig::Sasl(saslauth) = obj_cfg { - assert!(saslauth.identity == "".to_string()); - } + assert!(vm_config.object.sasl_object.get(&id).is_some()); + if let Some(obj_cfg) = vm_config.object.sasl_object.get(&id) { + assert!(obj_cfg.identity == "".to_string()); } } } diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index 4e30c03ba..6a5d409d1 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::config::{ - ConfigError, ObjConfig, {CmdParser, VmConfig}, + ConfigError, {CmdParser, VmConfig}, }; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; @@ -63,9 +63,8 @@ impl VmConfig { tlscred.cred_type = "x509".to_string(); let id = tlscred.id.clone(); - if self.object.get(&id).is_none() { - let tlscred_config = ObjConfig::Tls(tlscred); - self.object.insert(id, tlscred_config); + if self.object.tls_object.get(&id).is_none() { + self.object.tls_object.insert(id, tlscred); } else { return Err(anyhow!(ConfigError::IdRepeat("tlscred".to_string(), id))); } @@ -97,13 +96,11 @@ mod tests { let id = String::from("vnc-tls-creds0"); let mut vm_config = VmConfig::default(); assert!(vm_config.add_object(tls_config.as_str()).is_ok()); - assert!(vm_config.object.get(&id).is_some()); - if let Some(obj_cfg) = vm_config.object.get(&id) { - if let ObjConfig::Tls(tls_cred) = obj_cfg { - assert_eq!(tls_cred.dir, dir.to_str().unwrap()); - assert_eq!(tls_cred.endpoint, Some("server".to_string())); - assert_eq!(tls_cred.verifypeer, false); - } + assert!(vm_config.object.tls_object.get(&id).is_some()); + if let Some(tls_cred_cfg) = vm_config.object.tls_object.get(&id) { + assert_eq!(tls_cred_cfg.dir, dir.to_str().unwrap()); + assert_eq!(tls_cred_cfg.endpoint, Some("server".to_string())); + assert_eq!(tls_cred_cfg.verifypeer, false); } // Delete file. diff --git a/vnc/src/server.rs b/vnc/src/server.rs index d57125390..7d01684e1 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -30,7 +30,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use machine_manager::{ - config::{ObjConfig, VncConfig}, + config::{ObjectConfig, VncConfig}, event_loop::EventLoop, }; use std::{ @@ -163,13 +163,9 @@ impl VncServer { /// /// * `vnc_cfg` - configure of vnc. /// * `object` - configure of sasl and tls. - pub fn make_config( - &mut self, - vnc_cfg: &VncConfig, - object: &HashMap, - ) -> Result<()> { + pub fn make_config(&mut self, vnc_cfg: &VncConfig, object: &ObjectConfig) -> Result<()> { // Tls configuration. - if let Some(ObjConfig::Tls(tls_cred)) = object.get(&vnc_cfg.tls_creds) { + if let Some(tls_cred) = object.tls_object.get(&vnc_cfg.tls_creds) { let tlscred = TlsCreds { cred_type: tls_cred.cred_type.clone(), dir: tls_cred.dir.clone(), @@ -189,7 +185,7 @@ impl VncServer { } // Sasl configuration. - if let Some(ObjConfig::Sasl(sasl_auth)) = object.get(&vnc_cfg.sasl_authz) { + if let Some(sasl_auth) = object.sasl_object.get(&vnc_cfg.sasl_authz) { let saslauth = SaslAuth { identity: sasl_auth.identity.clone(), }; diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index aaeccbece..241a933d8 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -26,13 +26,12 @@ use crate::{ use anyhow::{anyhow, Result}; use core::time; use machine_manager::{ - config::{ObjConfig, VncConfig}, + config::{ObjectConfig, VncConfig}, event_loop::EventLoop, }; use once_cell::sync::Lazy; use std::{ cmp, - collections::HashMap, net::TcpListener, ptr, sync::{Arc, Mutex}, @@ -95,7 +94,7 @@ pub struct DisplayMouse { /// # Arguments /// /// * `VncConfig` `object`- vnc related parameters -pub fn vnc_init(vnc: &Option, object: &HashMap) -> Result<()> { +pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { let vnc_cfg; if let Some(v) = vnc { vnc_cfg = v; -- Gitee From ac0fbde4891c2252774df0986b82d3ce0378a61f Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 12:53:29 +0800 Subject: [PATCH 0312/2187] numa: add cpu numa information for FDT table Signed-off-by: Xinle.Guo --- machine/src/standard_vm/aarch64/mod.rs | 11 +++++++++++ util/src/device_tree.rs | 1 + 2 files changed, 12 insertions(+) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c64f97bdf..c7e730f46 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -1298,6 +1298,17 @@ impl CompileFDTHelper for StdMachine { fdt.set_property_string("enable-method", "psci")?; } fdt.set_property_u64("reg", mpidr & 0x007F_FFFF)?; + fdt.set_property_u32("phandle", device_tree::FIRST_VCPU_PHANDLE)?; + + if let Some(numa_nodes) = &self.numa_nodes { + for numa_index in 0..numa_nodes.len() { + let numa_node = numa_nodes.get(&(numa_index as u32)); + if numa_node.unwrap().cpus.contains(&(cpu_index as u8)) { + fdt.set_property_u32("numa-node-id", numa_index as u32)?; + } + } + } + fdt.end_node(mpidr_node_dep)?; } diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 87c69e0c5..b2a48ec8f 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -20,6 +20,7 @@ pub const CLK_PHANDLE: u32 = 1; pub const GIC_PHANDLE: u32 = 2; pub const GIC_ITS_PHANDLE: u32 = 3; pub const PPI_CLUSTER_PHANDLE: u32 = 4; +pub const FIRST_VCPU_PHANDLE: u32 = 6; pub const CPU_PHANDLE_START: u32 = 10; pub const GIC_FDT_IRQ_TYPE_SPI: u32 = 0; -- Gitee From e11c4054065e37394f29553580dd49a4e0c2bd14 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 15:20:46 +0800 Subject: [PATCH 0313/2187] memory/high mmio: add high mmio pcie region for FDT table Some devices need to use high pcie mmio region. For example, a VFIO device containing 32G mmio bar space. This patch is arm to add high pcie mmio reion for FDT table. Signed-off-by: Xinle.Guo --- machine/src/standard_vm/aarch64/mod.rs | 19 +++++++++++++++++-- util/src/device_tree.rs | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c64f97bdf..50f037b44 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -1052,9 +1052,17 @@ fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::Result<()> { fdt.set_property_u32("#address-cells", 3)?; fdt.set_property_u32("#size-cells", 2)?; + let high_pcie_mmio_base = MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize].0; + let high_pcie_mmio_size = MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize].1; + let fdt_pci_mmio_type_64bit: u32 = device_tree::FDT_PCI_RANGE_MMIO_64BIT; + let high_mmio_base_hi: u32 = (high_pcie_mmio_base >> 32) as u32; + let high_mmio_base_lo: u32 = (high_pcie_mmio_base & 0xffff_ffff) as u32; + let high_mmio_size_hi: u32 = (high_pcie_mmio_size >> 32) as u32; + let high_mmio_size_lo: u32 = (high_pcie_mmio_size & 0xffff_ffff) as u32; + let pcie_mmio_base = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; let pcie_mmio_size = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; - let fdt_pci_mmio_type: u32 = 0x0200_0000; + let fdt_pci_mmio_type: u32 = device_tree::FDT_PCI_RANGE_MMIO; let mmio_base_hi: u32 = (pcie_mmio_base >> 32) as u32; let mmio_base_lo: u32 = (pcie_mmio_base & 0xffff_ffff) as u32; let mmio_size_hi: u32 = (pcie_mmio_size >> 32) as u32; @@ -1062,7 +1070,7 @@ fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::Result<()> { let pcie_pio_base = MEM_LAYOUT[LayoutEntryType::PciePio as usize].0; let pcie_pio_size = MEM_LAYOUT[LayoutEntryType::PciePio as usize].1; - let fdt_pci_pio_type: u32 = 0x0100_0000; + let fdt_pci_pio_type: u32 = device_tree::FDT_PCI_RANGE_IOPORT; let pio_base_hi: u32 = (pcie_pio_base >> 32) as u32; let pio_base_lo: u32 = (pcie_pio_base & 0xffff_ffff) as u32; let pio_size_hi: u32 = (pcie_pio_size >> 32) as u32; @@ -1085,6 +1093,13 @@ fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::Result<()> { mmio_base_lo, mmio_size_hi, mmio_size_lo, + fdt_pci_mmio_type_64bit, + high_mmio_base_hi, + high_mmio_base_lo, + high_mmio_base_hi, + high_mmio_base_lo, + high_mmio_size_hi, + high_mmio_size_lo, ], )?; diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 87c69e0c5..eceee7002 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -45,6 +45,10 @@ const MEM_RESERVE_ALIGNMENT: usize = 8; // Structure block alignment. const STRUCTURE_BLOCK_ALIGNMENT: usize = 4; +pub const FDT_PCI_RANGE_IOPORT: u32 = 0x0100_0000; +pub const FDT_PCI_RANGE_MMIO: u32 = 0x0200_0000; +pub const FDT_PCI_RANGE_MMIO_64BIT: u32 = 0x0300_0000; + /// FdtBuilder structure. pub struct FdtBuilder { /// The header of flattened device tree. -- Gitee From 34c651a6195d6756becc0acb7e0637d7843cc8df Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 23:55:45 -0400 Subject: [PATCH 0314/2187] pci: avoid mixing CCI event with other slot events during unplugging According to the PCIe specification, CCI events is different from others. To avoid mixing them together, do a notify for each. Signed-off-by: Zhang Bo --- pci/src/root_port.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 924778e9f..d6ec1a44d 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -250,6 +250,9 @@ impl RootPort { } } + // According to the PCIe specification 6.7.3, CCI events is different from others. + // To avoid mixing them together, trigger a notify for each. + self.hotplug_event_notify(); self.hotplug_command_completed(); self.hotplug_event_notify(); } -- Gitee From 407aea3add6422d0007490b3c435c1cf9a49a940 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 7 Nov 2022 19:39:18 +0800 Subject: [PATCH 0315/2187] Configuration/Cargo.lock: update Cargo.lock and .gitignore file 1. For StratoVirt users, they don't need to care about the Cargo.lock, so add it to gitignore file. In addition, IDEA configuration files can also be ignored. 2. Update the Cargo.lock for Developers. Signed-off-by: Xinle.Guo --- .gitignore | 9 +++ Cargo.lock | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) diff --git a/.gitignore b/.gitignore index eb5a316cb..c58a49ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ target +# Remove Cargo.lock from gitignore. +# Ref: https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +**/*.rs.bk + +# Ignore configuration directory generated by IDEA. +.idea +.vscode diff --git a/Cargo.lock b/Cargo.lock index f87996456..9b3d2bd42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bitflags" version = "1.3.2" @@ -111,6 +117,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + [[package]] name = "byteorder" version = "1.4.3" @@ -200,6 +212,18 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" +[[package]] +name = "duct" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc6a0a59ed0888e0041cf708e66357b7ae1a82f1c67247e1f93b5e0818f7d8d" +dependencies = [ + "libc", + "once_cell", + "os_pipe", + "shared_child", +] + [[package]] name = "errno" version = "0.2.8" @@ -270,6 +294,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "kvm-bindings" version = "0.5.0" @@ -411,6 +444,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "os_pipe" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "ozone" version = "2.2.0" @@ -469,6 +512,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + [[package]] name = "proc-macro2" version = "1.0.44" @@ -513,18 +562,76 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + [[package]] name = "ryu" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +[[package]] +name = "sasl2-sys" +version = "0.1.20+2.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e645bd98535fc8fd251c43ba7c7c1f9be1e0369c99b6a5ea719052a773e655c" +dependencies = [ + "cc", + "duct", + "libc", + "pkg-config", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.145" @@ -578,12 +685,28 @@ dependencies = [ "syn", ] +[[package]] +name = "shared_child" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6be9f7d5565b1483af3e72975e2dee33879b3b86bd48c0929fccf6585d79e65a" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "smallvec" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "sscanf" version = "0.2.2" @@ -688,6 +811,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "usb" version = "2.2.0" @@ -821,6 +950,9 @@ dependencies = [ "migration_derive", "once_cell", "pci", + "rustls", + "rustls-pemfile", + "sasl2-sys", "serde_json", "sscanf", "sysbus", @@ -830,6 +962,80 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "winapi" version = "0.3.9" -- Gitee From cd3f14bf3cf9ce562703e30adee31836938e9667 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 7 Nov 2022 14:43:48 +0800 Subject: [PATCH 0316/2187] Fix: add high pcie mmio range for PCI host And high pcie mmio range in DSDT table. To prevent the high address space from overlapping with the memory address space, change the high address space to 512G. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 7 ++++--- pci/src/bus.rs | 2 ++ pci/src/host.rs | 26 ++++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c64f97bdf..ee497fea8 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -110,9 +110,9 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0x3EFF_0000, 0x0001_0000), // PciePio (0x3F00_0000, 0x0100_0000), // PcieEcam (0x4000_0000, 0x80_0000_0000), // Mem - (256 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) - (257 << 30, 0x1000_0000), // HighPcieEcam - (258 << 30, 512 << 30), // HighPcieMmio + (512 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) + (513 << 30, 0x1000_0000), // HighPcieEcam + (514 << 30, 512 << 30), // HighPcieMmio ]; /// Standard machine structure. @@ -188,6 +188,7 @@ impl StdMachine { MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize], MEM_LAYOUT[LayoutEntryType::PcieMmio as usize], MEM_LAYOUT[LayoutEntryType::PciePio as usize], + MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize], ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 760af1b62..7e48416ef 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -302,6 +302,8 @@ mod tests { (0xC000_0000, 0x3000_0000), #[cfg(target_arch = "aarch64")] (0xF000_0000, 0x1000_0000), + #[cfg(target_arch = "aarch64")] + (512 << 30, 512 << 30), ))) } diff --git a/pci/src/host.rs b/pci/src/host.rs index 12db19009..3400cb4a2 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -12,8 +12,6 @@ use std::sync::{Arc, Mutex}; -#[cfg(target_arch = "aarch64")] -use acpi::AmlOne; use acpi::{ AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlByte, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, @@ -23,6 +21,8 @@ use acpi::{ }; #[cfg(target_arch = "x86_64")] use acpi::{AmlIoDecode, AmlIoResource}; +#[cfg(target_arch = "aarch64")] +use acpi::{AmlOne, AmlQWordDesc}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use anyhow::Context; use sysbus::SysBusDevOps; @@ -60,6 +60,8 @@ pub struct PciHost { pcie_mmio_range: (u64, u64), #[cfg(target_arch = "aarch64")] pcie_pio_range: (u64, u64), + #[cfg(target_arch = "aarch64")] + high_pcie_mmio_range: (u64, u64), } impl PciHost { @@ -69,12 +71,17 @@ impl PciHost { /// /// * `sys_io` - IO space which the host bridge maps (only on x86_64). /// * `sys_mem`- Memory space which the host bridge maps. + /// * `pcie_ecam_range` - PCIe ECAM base address and length. + /// * `pcie_mmio_range` - PCIe MMIO base address and length. + /// * `pcie_pio_range` - PCIe PIO base addreass and length (only on aarch64). + /// * `high_pcie_mmio_range` - PCIe high MMIO base address and length (only on aarch64). pub fn new( #[cfg(target_arch = "x86_64")] sys_io: &Arc, sys_mem: &Arc, pcie_ecam_range: (u64, u64), pcie_mmio_range: (u64, u64), #[cfg(target_arch = "aarch64")] pcie_pio_range: (u64, u64), + #[cfg(target_arch = "aarch64")] high_pcie_mmio_range: (u64, u64), ) -> Self { #[cfg(target_arch = "x86_64")] let io_region = sys_io.root().clone(); @@ -94,6 +101,8 @@ impl PciHost { pcie_mmio_range, #[cfg(target_arch = "aarch64")] pcie_pio_range, + #[cfg(target_arch = "aarch64")] + high_pcie_mmio_range, } } @@ -423,6 +432,17 @@ impl AmlBuilder for PciHost { 0, pcie_pio.1 as u32, )); + let high_pcie_mmio = self.high_pcie_mmio_range; + crs.append_child(AmlQWordDesc::new_memory( + AmlAddressSpaceDecode::Positive, + AmlCacheable::NonCacheable, + AmlReadAndWrite::ReadWrite, + 0, + high_pcie_mmio.0 as u64, + (high_pcie_mmio.0 + high_pcie_mmio.1) as u64 - 1, + 0, + high_pcie_mmio.1 as u64, + )); } crs.append_child(AmlDWordDesc::new_memory( AmlAddressSpaceDecode::Positive, @@ -533,6 +553,8 @@ pub mod tests { (0xC000_0000, 0x3000_0000), #[cfg(target_arch = "aarch64")] (0xF000_0000, 0x1000_0000), + #[cfg(target_arch = "aarch64")] + (512 << 30, 512 << 30), ))) } -- Gitee From 383a307c972e9ebff575822f3a3d5a2ceb977c4b Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 21:49:29 -0400 Subject: [PATCH 0317/2187] PCI: validate bar id range before registering it there are 6/2 bars for endpoints(type 0 device)/bridges(type 1 device), make sure that the caller won't go over that boundary. Signed-off-by: Zhang Bo --- pci/src/config.rs | 108 +++++++++++++++++++++++++++++---------- pci/src/error.rs | 2 + pci/src/msix.rs | 2 +- usb/src/xhci/xhci_pci.rs | 2 +- vfio/src/vfio_pci.rs | 9 +++- virtio/src/virtio_pci.rs | 2 +- 6 files changed, 93 insertions(+), 32 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index c3c87de1c..f7a38fc97 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -78,12 +78,12 @@ pub const CLASS_CODE_HOST_BRIDGE: u16 = 0x0600; pub const CLASS_CODE_ISA_BRIDGE: u16 = 0x0601; /// Class code of PCI-to-PCI bridge. pub const CLASS_CODE_PCI_BRIDGE: u16 = 0x0604; - +/// Type 0 configuration Space Header Layout. +pub const HEADER_TYPE_ENDPOINT: u8 = 0x0; /// Type 1 configuration Space Header Layout. pub const HEADER_TYPE_BRIDGE: u8 = 0x01; /// Multi-function device. pub const HEADER_TYPE_MULTIFUNC: u8 = 0x80; - /// The vendor ID for PCI devices other than virtio. pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; @@ -179,6 +179,10 @@ pub const MEM_BASE_ADDR_MASK: u64 = 0xffff_ffff_ffff_fff0; pub const BAR_MEM_64BIT: u8 = 0x04; const BAR_PREFETCH: u8 = 0x08; pub const BAR_SPACE_UNMAPPED: u64 = 0xffff_ffff_ffff_ffff; +/// The maximum Bar ID numbers of a Type 0 device +pub const BAR_NUM_MAX_FOR_ENDPOINT: u8 = 6; +/// The maximum Bar ID numbers of a Type 1 device +pub const BAR_NUM_MAX_FOR_BRIDGE: u8 = 2; // Role-Based error reporting. const PCIE_CAP_RBER: u32 = 0x8000; @@ -543,7 +547,8 @@ impl PciConfig { region_type: RegionType, prefetchable: bool, size: u64, - ) { + ) -> Result<()> { + self.validate_bar_id(id)?; let offset: usize = BAR_0 as usize + id * REG_SIZE; match region_type { RegionType::Io => { @@ -569,6 +574,7 @@ impl PciConfig { self.bars[id].address = BAR_SPACE_UNMAPPED; self.bars[id].size = size; self.bars[id].region = Some(region); + Ok(()) } /// Unregister region in PciConfig::bars. @@ -866,6 +872,20 @@ impl PciConfig { end_pos - pos } + + fn validate_bar_id(&self, id: usize) -> Result<()> { + if (self.config[HEADER_TYPE as usize] == HEADER_TYPE_ENDPOINT + && id >= BAR_NUM_MAX_FOR_ENDPOINT as usize) + || (self.config[HEADER_TYPE as usize] == HEADER_TYPE_BRIDGE + && id >= BAR_NUM_MAX_FOR_BRIDGE as usize) + { + return Err(anyhow!(PciError::InvalidConf( + "Bar id".to_string(), + id as u32 + ))); + } + Ok(()) + } } #[cfg(test)] @@ -912,21 +932,37 @@ mod tests { let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); #[cfg(target_arch = "x86_64")] - pci_config.register_bar( - 0, - region.clone(), - RegionType::Io, - false, - IO_BASE_ADDR_MASK as u64, - ); - pci_config.register_bar( - 1, - region.clone(), - RegionType::Mem32Bit, - false, - (MEM_BASE_ADDR_MASK as u32) as u64, - ); - pci_config.register_bar(2, region, RegionType::Mem64Bit, true, MEM_BASE_ADDR_MASK); + assert!(pci_config + .register_bar( + 0, + region.clone(), + RegionType::Io, + false, + IO_BASE_ADDR_MASK as u64, + ) + .is_ok()); + assert!(pci_config + .register_bar( + 1, + region.clone(), + RegionType::Mem32Bit, + false, + (MEM_BASE_ADDR_MASK as u32) as u64, + ) + .is_ok()); + assert!(pci_config + .register_bar( + 2, + region.clone(), + RegionType::Mem64Bit, + true, + MEM_BASE_ADDR_MASK + ) + .is_ok()); + // test when bar id is not valid + assert!(pci_config + .register_bar(7, region, RegionType::Mem64Bit, true, MEM_BASE_ADDR_MASK) + .is_err()); #[cfg(target_arch = "x86_64")] le_write_u32( @@ -986,9 +1022,15 @@ mod tests { let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 6); #[cfg(target_arch = "x86_64")] - pci_config.register_bar(0, region.clone(), RegionType::Io, false, 2048); - pci_config.register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048); - pci_config.register_bar(2, region, RegionType::Mem64Bit, true, 2048); + assert!(pci_config + .register_bar(0, region.clone(), RegionType::Io, false, 2048) + .is_ok()); + assert!(pci_config + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048) + .is_ok()); + assert!(pci_config + .register_bar(2, region, RegionType::Mem64Bit, true, 2048) + .is_ok()); #[cfg(target_arch = "x86_64")] le_write_u32( @@ -1126,9 +1168,15 @@ mod tests { // bar is unmapped #[cfg(target_arch = "x86_64")] - pci_config.register_bar(0, region.clone(), RegionType::Io, false, 2048); - pci_config.register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048); - pci_config.register_bar(2, region.clone(), RegionType::Mem64Bit, true, 2048); + assert!(pci_config + .register_bar(0, region.clone(), RegionType::Io, false, 2048) + .is_ok()); + assert!(pci_config + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048) + .is_ok()); + assert!(pci_config + .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 2048) + .is_ok()); #[cfg(target_arch = "x86_64")] let io_region = Region::init_container_region(1 << 16); @@ -1144,9 +1192,15 @@ mod tests { // bar is mapped #[cfg(target_arch = "x86_64")] - pci_config.register_bar(0, region.clone(), RegionType::Io, false, 2048); - pci_config.register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048); - pci_config.register_bar(2, region.clone(), RegionType::Mem64Bit, true, 2048); + assert!(pci_config + .register_bar(0, region.clone(), RegionType::Io, false, 2048) + .is_ok()); + assert!(pci_config + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048) + .is_ok()); + assert!(pci_config + .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 2048) + .is_ok()); #[cfg(target_arch = "x86_64")] le_write_u32( diff --git a/pci/src/error.rs b/pci/src/error.rs index 06a897a83..123101cad 100644 --- a/pci/src/error.rs +++ b/pci/src/error.rs @@ -33,4 +33,6 @@ pub enum PciError { FeaturesSelect(u32), #[error("HotPlug is not supported for device with devfn {0}")] HotplugUnsupported(u8), + #[error("Invalid PCI configuration, key:{0}, value:{1}")] + InvalidConf(String, u32), } diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 0921a1ad6..184fa8706 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -503,7 +503,7 @@ pub fn init_msix( table_offset as u64, pba_offset as u64, )?; - config.register_bar(bar_id, region, RegionType::Mem32Bit, false, bar_size); + config.register_bar(bar_id, region, RegionType::Mem32Bit, false, bar_size)?; } config.msix = Some(msix.clone()); diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 9453d078f..432f2bd12 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -194,7 +194,7 @@ impl PciDevOps for XhciPciDevice { RegionType::Mem64Bit, false, mem_region_size, - ); + )?; let devfn = self.devfn; let dev = Arc::new(Mutex::new(self)); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index f9e188e75..928100d5a 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -419,8 +419,13 @@ impl VfioPciDevice { region }; - self.pci_config - .register_bar(i as usize, bar_region, vfio_bar.region_type, false, size); + self.pci_config.register_bar( + i as usize, + bar_region, + vfio_bar.region_type, + false, + size, + )?; } Ok(()) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index e4852816a..e53ffb579 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1071,7 +1071,7 @@ impl PciDevOps for VirtioPciDevice { RegionType::Mem32Bit, false, mem_region_size, - ); + )?; self.device .lock() -- Gitee From 4cc4ed24edc050e01f989755f47eab8b6fb1477d Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 21:57:10 -0400 Subject: [PATCH 0318/2187] pci: leave the write mask lower bits untouched MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the caller has already made the bar size above 4KB for mmio and 4B for pio,according to the PCIe specification, no need to do the double-check in register_bar(). Signed-off-by: Zhang Bo --- pci/src/config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index f7a38fc97..529550089 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -552,16 +552,16 @@ impl PciConfig { let offset: usize = BAR_0 as usize + id * REG_SIZE; match region_type { RegionType::Io => { - let write_mask = (!(size - 1) as u32) & 0xffff_fffc; + let write_mask = !(size - 1) as u32; le_write_u32(&mut self.write_mask, offset, write_mask).unwrap(); self.config[offset] = BAR_IO_SPACE; } RegionType::Mem32Bit => { - let write_mask = (!(size - 1) as u32) & 0xffff_fff0; + let write_mask = !(size - 1) as u32; le_write_u32(&mut self.write_mask, offset, write_mask).unwrap(); } RegionType::Mem64Bit => { - let write_mask = !(size - 1) & 0xffff_ffff_ffff_fff0; + let write_mask = !(size - 1); le_write_u64(&mut self.write_mask, offset, write_mask).unwrap(); self.config[offset] = BAR_MEM_64BIT; } -- Gitee From fb72b4f80491ec9c18557491537c14c79a04feeb Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 30 Oct 2022 23:12:37 -0400 Subject: [PATCH 0319/2187] pci: correct the minimum size of a bar the bar size shall be at least 4KB for the mem type and 4B for the pio type, according to PCIe specification 7.7.2 and 7.5.1.2.1, rather than the host page size, or, the devices would cost too much memory and lead to less supported device numbers for a VM when hugepage is set on the host. Signed-off-by: Zhang Bo --- pci/src/config.rs | 4 ++++ pci/src/msix.rs | 7 ++++--- usb/src/xhci/xhci_pci.rs | 10 +++++----- virtio/src/virtio_pci.rs | 11 ++++++----- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 529550089..0e1d5a117 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -183,6 +183,10 @@ pub const BAR_SPACE_UNMAPPED: u64 = 0xffff_ffff_ffff_ffff; pub const BAR_NUM_MAX_FOR_ENDPOINT: u8 = 6; /// The maximum Bar ID numbers of a Type 1 device pub const BAR_NUM_MAX_FOR_BRIDGE: u8 = 2; +/// mmio bar's minimum size shall be 4KB +pub const MINMUM_BAR_SIZE_FOR_MMIO: usize = 0x1000; +/// pio bar's minimum size shall be 4B +pub const MINMUM_BAR_SIZE_FOR_PIO: usize = 0x4; // Role-Based error reporting. const PCIE_CAP_RBER: u32 = 0x8000; diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 184fa8706..2090be7c1 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::max; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; @@ -20,9 +21,9 @@ use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; +use util::{byte_code::ByteCode, num_ops::round_up}; -use crate::config::{CapId, PciConfig, RegionType, SECONDARY_BUS_NUM}; +use crate::config::{CapId, PciConfig, RegionType, MINMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, ranges_overlap, PciBus, @@ -494,7 +495,7 @@ pub fn init_msix( )?; } else { let mut bar_size = ((table_size + pba_size) as u64).next_power_of_two(); - bar_size = round_up(bar_size, host_page_size()).unwrap(); + bar_size = max(bar_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); let region = Region::init_container_region(bar_size); Msix::register_memory_region( msix.clone(), diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 432f2bd12..5fa4d0e72 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -10,17 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::max; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; use pci::config::{ - PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, PCI_CLASS_SERIAL_USB, - PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, + PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, + PCIE_CONFIG_SPACE_SIZE, PCI_CLASS_SERIAL_USB, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, + ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, }; use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; -use util::num_ops::round_up; -use util::unix::host_page_size; use crate::bus::{BusDeviceMap, BusDeviceOps}; use crate::usb::UsbDeviceOps; @@ -187,7 +187,7 @@ impl PciDevOps for XhciPciDevice { )?; let mut mem_region_size = (XHCI_PCI_CONFIG_LENGTH as u64).next_power_of_two(); - mem_region_size = round_up(mem_region_size, host_page_size()).unwrap(); + mem_region_size = max(mem_region_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); self.pci_config.register_bar( 0_usize, self.mem_region.clone(), diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index e53ffb579..e7c8cef24 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::max; use std::collections::HashMap; use std::mem::size_of; use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; @@ -23,9 +24,9 @@ use log::{error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use pci::config::{ - RegionType, BAR_0, COMMAND, DEVICE_ID, PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, - ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, - VENDOR_ID, + RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, + REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, + SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::{update_dev_id, Message, MsixState, MsixUpdate}; use pci::Result as PciResult; @@ -33,7 +34,7 @@ use pci::{ config::PciConfig, init_msix, init_multifunction, le_write_u16, ranges_overlap, PciBus, PciDevOps, PciError, }; -use util::{byte_code::ByteCode, num_ops::round_up, unix::host_page_size}; +use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; use crate::{ @@ -1061,7 +1062,7 @@ impl PciDevOps for VirtioPciDevice { let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) as u64) .next_power_of_two(); - mem_region_size = round_up(mem_region_size, host_page_size()).unwrap(); + mem_region_size = max(mem_region_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); let modern_mem_region = Region::init_container_region(mem_region_size); self.modern_mem_region_init(&modern_mem_region)?; -- Gitee From 68b2f2e573ce30bfb5c19e08148f4127c4b61581 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 7 Nov 2022 10:24:34 -0500 Subject: [PATCH 0320/2187] pci: validate bar size beore registering bar size shall be power of 2 and has minimum size constraint, although the callers nowadays all have followed this rule, make a validation in the callee function for future insurance. Signed-off-by: Zhang Bo --- pci/src/config.rs | 76 +++++++++++++++++++++++++---------------------- pci/src/error.rs | 2 +- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0e1d5a117..0b7bc317f 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -553,6 +553,7 @@ impl PciConfig { size: u64, ) -> Result<()> { self.validate_bar_id(id)?; + self.validate_bar_size(region_type, size)?; let offset: usize = BAR_0 as usize + id * REG_SIZE; match region_type { RegionType::Io => { @@ -885,7 +886,21 @@ impl PciConfig { { return Err(anyhow!(PciError::InvalidConf( "Bar id".to_string(), - id as u32 + id.to_string(), + ))); + } + Ok(()) + } + + fn validate_bar_size(&self, bar_type: RegionType, size: u64) -> Result<()> { + if !size.is_power_of_two() + || ((bar_type == RegionType::Mem32Bit || bar_type == RegionType::Mem64Bit) + && size < MINMUM_BAR_SIZE_FOR_MMIO.try_into().unwrap()) + || (bar_type == RegionType::Io && size < MINMUM_BAR_SIZE_FOR_PIO.try_into().unwrap()) + { + return Err(anyhow!(PciError::InvalidConf( + "Bar size".to_string(), + size.to_string(), ))); } Ok(()) @@ -932,40 +947,31 @@ mod tests { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let region = Region::init_io_region(2048, region_ops); + let region = Region::init_io_region(8192, region_ops.clone()); let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); #[cfg(target_arch = "x86_64")] assert!(pci_config - .register_bar( - 0, - region.clone(), - RegionType::Io, - false, - IO_BASE_ADDR_MASK as u64, - ) + .register_bar(0, region.clone(), RegionType::Io, false, 8192) .is_ok()); assert!(pci_config - .register_bar( - 1, - region.clone(), - RegionType::Mem32Bit, - false, - (MEM_BASE_ADDR_MASK as u32) as u64, - ) + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 8192) .is_ok()); assert!(pci_config - .register_bar( - 2, - region.clone(), - RegionType::Mem64Bit, - true, - MEM_BASE_ADDR_MASK - ) + .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 8192) .is_ok()); // test when bar id is not valid assert!(pci_config - .register_bar(7, region, RegionType::Mem64Bit, true, MEM_BASE_ADDR_MASK) + .register_bar(7, region, RegionType::Mem64Bit, true, 8192) + .is_err()); + // test when bar size is incorrect(below 4KB, or not power of 2) + let region_size_too_small = Region::init_io_region(2048, region_ops.clone()); + assert!(pci_config + .register_bar(3, region_size_too_small, RegionType::Mem64Bit, true, 2048) + .is_err()); + let region_size_not_pow_2 = Region::init_io_region(4238, region_ops); + assert!(pci_config + .register_bar(4, region_size_not_pow_2, RegionType::Mem64Bit, true, 4238) .is_err()); #[cfg(target_arch = "x86_64")] @@ -1022,18 +1028,18 @@ mod tests { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let region = Region::init_io_region(2048, region_ops); + let region = Region::init_io_region(8192, region_ops); let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 6); #[cfg(target_arch = "x86_64")] assert!(pci_config - .register_bar(0, region.clone(), RegionType::Io, false, 2048) + .register_bar(0, region.clone(), RegionType::Io, false, 8192) .is_ok()); assert!(pci_config - .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048) + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 8192) .is_ok()); assert!(pci_config - .register_bar(2, region, RegionType::Mem64Bit, true, 2048) + .register_bar(2, region, RegionType::Mem64Bit, true, 8192) .is_ok()); #[cfg(target_arch = "x86_64")] @@ -1167,19 +1173,19 @@ mod tests { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let region = Region::init_io_region(2048, region_ops); + let region = Region::init_io_region(4096, region_ops); let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); // bar is unmapped #[cfg(target_arch = "x86_64")] assert!(pci_config - .register_bar(0, region.clone(), RegionType::Io, false, 2048) + .register_bar(0, region.clone(), RegionType::Io, false, 4096) .is_ok()); assert!(pci_config - .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048) + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 4096) .is_ok()); assert!(pci_config - .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 2048) + .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 4096) .is_ok()); #[cfg(target_arch = "x86_64")] @@ -1197,13 +1203,13 @@ mod tests { // bar is mapped #[cfg(target_arch = "x86_64")] assert!(pci_config - .register_bar(0, region.clone(), RegionType::Io, false, 2048) + .register_bar(0, region.clone(), RegionType::Io, false, 4096) .is_ok()); assert!(pci_config - .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 2048) + .register_bar(1, region.clone(), RegionType::Mem32Bit, false, 4096) .is_ok()); assert!(pci_config - .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 2048) + .register_bar(2, region.clone(), RegionType::Mem64Bit, true, 4096) .is_ok()); #[cfg(target_arch = "x86_64")] diff --git a/pci/src/error.rs b/pci/src/error.rs index 123101cad..ce9e0bcde 100644 --- a/pci/src/error.rs +++ b/pci/src/error.rs @@ -34,5 +34,5 @@ pub enum PciError { #[error("HotPlug is not supported for device with devfn {0}")] HotplugUnsupported(u8), #[error("Invalid PCI configuration, key:{0}, value:{1}")] - InvalidConf(String, u32), + InvalidConf(String, String), } -- Gitee From 5c42503e0f5ba3b66adc86ae98b4431b96461511 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 31 Oct 2022 01:47:59 -0400 Subject: [PATCH 0321/2187] pci: avoid overrun when write msix table check whether it overrun the table boundary before writing msix table. Signed-off-by: Zhang Bo --- pci/src/msix.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 0921a1ad6..6e6b97f7b 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -179,9 +179,10 @@ impl Msix { let table_read = move |data: &mut [u8], _addr: GuestAddress, offset: u64| -> bool { if offset as usize + data.len() > cloned_msix.lock().unwrap().table.len() { error!( - "Fail to read msi table, illegal data length {}, offset {}", - data.len(), - offset + "It's forbidden to read out of the msix table(size: {}), with offset of {} and size of {}", + cloned_msix.lock().unwrap().table.len(), + offset, + data.len() ); return false; } @@ -191,6 +192,15 @@ impl Msix { }; let cloned_msix = msix.clone(); let table_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { + if offset as usize + data.len() > cloned_msix.lock().unwrap().table.len() { + error!( + "It's forbidden to write out of the msix table(size: {}), with offset of {} and size of {}", + cloned_msix.lock().unwrap().table.len(), + offset, + data.len() + ); + return false; + } let mut locked_msix = cloned_msix.lock().unwrap(); let vector: u16 = offset as u16 / MSIX_TABLE_ENTRY_SIZE; let was_masked: bool = locked_msix.is_vector_masked(vector); -- Gitee From 4e8032a29591819e812425f460fc8c58df6f8990 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 2 Nov 2022 09:57:04 +0800 Subject: [PATCH 0322/2187] usb: check the usb address The usb address is not greater than 127, check the address when set address. Signed-off-by: zhouli57 --- usb/src/keyboard.rs | 11 +++++---- usb/src/tablet.rs | 11 +++++---- usb/src/usb.rs | 54 +++++++++++++++++++++++---------------------- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index ef184e3d0..4b435b2c7 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -193,12 +193,15 @@ impl UsbDeviceOps for UsbKeyboard { debug!("handle_control request {:?}", device_req); let mut locked_dev = self.device.lock().unwrap(); match locked_dev.handle_control_for_descriptor(packet, device_req, data) { - Ok(_) => { - debug!("Keyboard control handled by descriptor, return directly."); - return; + Ok(handled) => { + if handled { + debug!("Keyboard control handled by descriptor, return directly."); + return; + } } Err(e) => { - debug!("Keyboard not handled by descriptor, fallthrough {}", e); + error!("Keyboard descriptor error {}", e); + return; } } let mut locked_hid = self.hid.lock().unwrap(); diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index ee39c73a0..b52d9c676 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -239,12 +239,15 @@ impl UsbDeviceOps for UsbTablet { debug!("handle_control request {:?}", device_req); let mut locked_dev = self.device.lock().unwrap(); match locked_dev.handle_control_for_descriptor(packet, device_req, data) { - Ok(_) => { - debug!("Tablet Device control handled by descriptor, return directly."); - return; + Ok(handled) => { + if handled { + debug!("Tablet control handled by descriptor, return directly."); + return; + } } Err(e) => { - debug!("Tablet not handled by descriptor, fallthrough {}", e); + error!("Tablet descriptor error {}", e); + return; } } let mut locked_hid = self.hid.lock().unwrap(); diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 7d1fa2d0d..ac65f32cf 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -28,6 +28,8 @@ use anyhow::{bail, Result}; const USB_MAX_ENDPOINTS: u32 = 15; const USB_MAX_INTERFACES: u32 = 16; +/// USB max address. +const USB_MAX_ADDRESS: u8 = 127; /// USB packet return status. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -366,12 +368,23 @@ impl UsbDevice { } } + /// Handle USB control request which is for descriptor. + /// + /// # Arguments + /// + /// * `packet` - USB packet. + /// * `device_req` - USB device request. + /// * `data` - USB control transfer data. + /// + /// # Returns + /// + /// Return true if request is handled, false is unhandled. pub fn handle_control_for_descriptor( &mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest, data: &mut [u8], - ) -> Result<()> { + ) -> Result { let value = device_req.value as u32; let index = device_req.index as u32; let length = device_req.length as u32; @@ -412,19 +425,20 @@ impl UsbDevice { packet.actual_length = 2; } _ => { - bail!( - "Unhandled request: type {} request {}", - device_req.request_type, - device_req.request - ); + return Ok(false); } }, USB_DEVICE_OUT_REQUEST => match device_req.request { USB_REQUEST_SET_ADDRESS => { - self.addr = value as u8; + if value as u8 > USB_MAX_ADDRESS { + packet.status = UsbPacketStatus::Stall; + bail!("The address is invalid {}", value); + } else { + self.addr = value as u8; + } } USB_REQUEST_SET_CONFIGURATION => { - return self.set_config_descriptor(value as u8); + self.set_config_descriptor(value as u8)?; } USB_REQUEST_CLEAR_FEATURE => { if value == USB_DEVICE_REMOTE_WAKEUP { @@ -437,11 +451,7 @@ impl UsbDevice { } } _ => { - bail!( - "Unhandled request: type {} request {}", - device_req.request_type, - device_req.request - ); + return Ok(false); } }, USB_INTERFACE_IN_REQUEST => match device_req.request { @@ -452,30 +462,22 @@ impl UsbDevice { } } _ => { - bail!( - "Unhandled request: type {} request {}", - device_req.request_type, - device_req.request - ); + return Ok(false); } }, USB_INTERFACE_OUT_REQUEST => match device_req.request { USB_REQUEST_SET_INTERFACE => { - return self.set_interface_descriptor(index, value); + self.set_interface_descriptor(index, value)?; } _ => { - bail!( - "Unhandled request: type {} request {}", - device_req.request_type, - device_req.request - ); + return Ok(false); } }, _ => { - bail!("Unhandled request: type {}", device_req.request_type); + return Ok(false); } } - Ok(()) + Ok(true) } } -- Gitee From 4a1835c77d80d8ccb46fb6e8d01d447426d9ec68 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 3 Nov 2022 17:25:57 +0800 Subject: [PATCH 0323/2187] add mktime64 to convert date to seconds Refactor the function mktime64 that converts date to seconds, which will be used in the unit tests of pl031. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 23 ++++------------------- util/src/time.rs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 446fbc327..9a71bcd62 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -24,7 +24,7 @@ use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; -use util::time::NANOSECONDS_PER_SECOND; +use util::time::{mktime64, NANOSECONDS_PER_SECOND}; /// IO port of RTC device to select Register to read/write. pub const RTC_PORT_INDEX: u64 = 0x70; @@ -299,8 +299,8 @@ impl RTC { let min = bcd_to_bin(self.cmos_data[RTC_MINUTES as usize]); let hour = bcd_to_bin(self.cmos_data[RTC_HOURS as usize]); let day = bcd_to_bin(self.cmos_data[RTC_DAY_OF_MONTH as usize]); - let mut mon = bcd_to_bin(self.cmos_data[RTC_MONTH as usize]); - let mut year = bcd_to_bin(self.cmos_data[RTC_YEAR as usize]) + let mon = bcd_to_bin(self.cmos_data[RTC_MONTH as usize]); + let year = bcd_to_bin(self.cmos_data[RTC_YEAR as usize]) + bcd_to_bin(self.cmos_data[RTC_CENTURY_BCD as usize]) * 100; // Check rtc time is valid to prevent tick_offset overflow. @@ -312,22 +312,7 @@ impl RTC { return; } - // Converts date to seconds since 1970-01-01 00:00:00. - if mon <= 2 { - mon += 10; - year -= 1; - } else { - mon -= 2; - } - - self.tick_offset = - ((((year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) + year * 365 - 719499) - * 24 - + hour) - * 60 - + min) - * 60 - + sec; + self.tick_offset = mktime64(year, mon, day, hour, min, sec); self.base_time = Instant::now(); } diff --git a/util/src/time.rs b/util/src/time.rs index 8fdd98222..ad391ab44 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -11,3 +11,20 @@ // See the Mulan PSL v2 for more details. pub const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000; + +/// Converts date to seconds since 1970-01-01 00:00:00. +pub fn mktime64(year: u64, mon: u64, day: u64, hour: u64, min: u64, sec: u64) -> u64 { + let mut y = year; + let mut m = mon; + + if m <= 2 { + m += 10; + y -= 1; + } else { + m -= 2; + } + + ((((y / 4 - y / 100 + y / 400 + 367 * m / 12 + day) + y * 365 - 719499) * 24 + hour) * 60 + min) + * 60 + + sec +} -- Gitee From e1504801e99247c38f460ec3b4cbd80e8ce10d59 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Nov 2022 20:08:45 +0800 Subject: [PATCH 0324/2187] rtc: check the time before writing to the register Before the RTC register is written, it is necessary to check the time format to ensure the correctness of the RTC time. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 43 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 9a71bcd62..fae3d3218 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -240,9 +240,16 @@ impl RTC { match self.cur_index { RTC_SECONDS | RTC_MINUTES | RTC_HOURS | RTC_DAY_OF_WEEK | RTC_DAY_OF_MONTH - | RTC_MONTH | RTC_YEAR => { - self.cmos_data[self.cur_index as usize] = data[0]; - self.update_rtc_time(); + | RTC_MONTH | RTC_YEAR | RTC_CENTURY_BCD => { + if self.rtc_valid_check(data[0]) { + self.cmos_data[self.cur_index as usize] = data[0]; + self.update_rtc_time(); + } else { + warn!( + "Set invalid RTC time, index {}, data {}", + self.cur_index, data[0] + ); + } } RTC_REG_C | RTC_REG_D => { warn!( @@ -294,6 +301,36 @@ impl RTC { self.cmos_data[RTC_CENTURY_BCD as usize] = bin_to_bcd(((tm.tm_year + 1900) / 100) as u8); } + fn rtc_valid_check(&self, val: u8) -> bool { + let range = [ + [0, 59], // Seconds + [0, 59], // Seconds Alarm + [0, 59], // Minutes + [0, 59], // Minutes Alarm + [0, 23], // Hours + [0, 23], // Hours Alarm + [1, 7], // Day of the Week + [1, 31], // Day of the Month + [1, 12], // Month + [0, 99], // Year + ]; + + if (val >> 4) > 9 || (val & 0x0f) > 9 { + return false; + } + + let value = bcd_to_bin(val); + + if self.cur_index <= 9 + && (value < range[self.cur_index as usize][0] + || value > range[self.cur_index as usize][1]) + { + return false; + } + + true + } + fn update_rtc_time(&mut self) { let sec = bcd_to_bin(self.cmos_data[RTC_SECONDS as usize]); let min = bcd_to_bin(self.cmos_data[RTC_MINUTES as usize]); -- Gitee From 1a4c0cf2d4bb21431f440fac0c432225c5ff8969 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 3 Nov 2022 15:38:20 +0800 Subject: [PATCH 0325/2187] rtc: add cmos rtc unit test cases cmos rtc unit test cases: 1. test_set_year_20xx 2. test_set_year_1970 3. test_invalid_rtc_time Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index fae3d3218..e079f5f30 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -430,3 +430,105 @@ impl AmlBuilder for RTC { acpi_dev.aml_bytes() } } + +#[cfg(test)] +mod test { + use super::*; + use address_space::GuestAddress; + use anyhow::Context; + + const WIGGLE: u8 = 2; + + fn cmos_read(rtc: &mut RTC, index: u8) -> u8 { + let mut data: [u8; 1] = [index; 1]; + RTC::write(rtc, &mut data, GuestAddress(0), 0); + RTC::read(rtc, &mut data, GuestAddress(0), 1); + data[0] + } + + fn cmos_write(rtc: &mut RTC, index: u8, val: u8) { + let mut data: [u8; 1] = [index; 1]; + RTC::write(rtc, &mut data, GuestAddress(0), 0); + data[0] = val; + RTC::write(rtc, &mut data, GuestAddress(0), 1); + } + + #[test] + fn test_set_year_20xx() -> Result<()> { + let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + // Set rtc time: 2013-11-13 02:04:56 + cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x20); + cmos_write(&mut rtc, RTC_YEAR, 0x13); + cmos_write(&mut rtc, RTC_MONTH, 0x11); + cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x13); + cmos_write(&mut rtc, RTC_HOURS, 0x02); + cmos_write(&mut rtc, RTC_MINUTES, 0x04); + cmos_write(&mut rtc, RTC_SECONDS, 0x56); + + let seconds_check = (cmos_read(&mut rtc, RTC_SECONDS) - 0x56) <= WIGGLE; + assert_eq!(seconds_check, true); + assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x04); + assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x02); + assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x13); + assert_eq!(cmos_read(&mut rtc, RTC_MONTH), 0x11); + assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x13); + assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x20); + + Ok(()) + } + + #[test] + fn test_set_year_1970() -> Result<()> { + let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + // Set rtc time (min): 1970-01-01 00:00:00 + cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); + cmos_write(&mut rtc, RTC_YEAR, 0x70); + cmos_write(&mut rtc, RTC_MONTH, 0x01); + cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x01); + cmos_write(&mut rtc, RTC_HOURS, 0x00); + cmos_write(&mut rtc, RTC_MINUTES, 0x00); + cmos_write(&mut rtc, RTC_SECONDS, 0x00); + + let seconds_check = (cmos_read(&mut rtc, RTC_SECONDS) - 0x00) <= WIGGLE; + assert_eq!(seconds_check, true); + assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x00); + assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x00); + assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x01); + assert_eq!(cmos_read(&mut rtc, RTC_MONTH), 0x01); + assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x70); + assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x19); + + Ok(()) + } + + #[test] + fn test_invalid_rtc_time() -> Result<()> { + let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + // Set rtc year: 1969 + cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); + cmos_write(&mut rtc, RTC_YEAR, 0x69); + assert_ne!(cmos_read(&mut rtc, RTC_YEAR), 0x69); + + // Set rtc month: 13 + cmos_write(&mut rtc, RTC_MONTH, 0x13); + assert_ne!(cmos_read(&mut rtc, RTC_HOURS), 0x13); + + // Set rtc day: 32 + cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x32); + assert_ne!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x32); + + // Set rtc hour: 25 + cmos_write(&mut rtc, RTC_HOURS, 0x25); + assert_ne!(cmos_read(&mut rtc, RTC_HOURS), 0x25); + + // Set rtc minute: 60 + cmos_write(&mut rtc, RTC_MINUTES, 0x60); + assert_ne!(cmos_read(&mut rtc, RTC_MINUTES), 0x60); + + // Set rtc second: 60 + cmos_write(&mut rtc, RTC_SECONDS, 0x60); + assert_ne!(cmos_read(&mut rtc, RTC_SECONDS), 0x60); + + Ok(()) + } +} -- Gitee From af1ebf7507418fe4f94f7eae3eabc4f7100eb27c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 3 Nov 2022 17:39:37 +0800 Subject: [PATCH 0326/2187] rtc: add pl031 rtc unit test cases pl031 rtc unit test cases: 1. test_set_year_20xx 2. test_set_year_1970 Signed-off-by: yezengruan --- devices/src/legacy/pl031.rs | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index ee5300dc6..658b835d2 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -238,3 +238,43 @@ impl StateTransfer for PL031 { } impl MigrationHook for PL031 {} + +#[cfg(test)] +mod test { + use super::*; + use util::time::mktime64; + + const WIGGLE: u32 = 2; + + #[test] + fn test_set_year_20xx() { + let mut rtc = PL031::default(); + // Set rtc time: 2013-11-13 02:04:56. + let wtick = mktime64(2013, 11, 13, 2, 4, 56) as u32; + let mut data = [0; 4]; + LittleEndian::write_u32(&mut data, wtick); + PL031::write(&mut rtc, &mut data, GuestAddress(0), RTC_LR); + + PL031::read(&mut rtc, &mut data, GuestAddress(0), RTC_DR); + let rtick = LittleEndian::read_u32(&data); + + let rtc_check = (rtick - wtick) <= WIGGLE; + assert_eq!(rtc_check, true); + } + + #[test] + fn test_set_year_1970() { + let mut rtc = PL031::default(); + // Set rtc time (min): 1970-01-01 00:00:00. + let wtick = mktime64(1970, 1, 1, 0, 0, 0) as u32; + let mut data = [0; 4]; + LittleEndian::write_u32(&mut data, wtick); + PL031::write(&mut rtc, &mut data, GuestAddress(0), RTC_LR); + + PL031::read(&mut rtc, &mut data, GuestAddress(0), RTC_DR); + let rtick = LittleEndian::read_u32(&data); + + let rtc_check = (rtick - wtick) <= WIGGLE; + assert_eq!(rtc_check, true); + } +} -- Gitee From 22076fce21c147916d16010f9efe417a55776662 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 9 Nov 2022 09:13:21 +0800 Subject: [PATCH 0327/2187] rtc/test: fix RTC_MONTH invalid value check Write to the RTC_MONTH register, should read the RTC_MONTH register to test. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index e079f5f30..ad7d1bba6 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -511,7 +511,7 @@ mod test { // Set rtc month: 13 cmos_write(&mut rtc, RTC_MONTH, 0x13); - assert_ne!(cmos_read(&mut rtc, RTC_HOURS), 0x13); + assert_ne!(cmos_read(&mut rtc, RTC_MONTH), 0x13); // Set rtc day: 32 cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x32); -- Gitee From 4faab545ca346a1f32cc390fb9b8dbb5f447ecaa Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 8 Nov 2022 21:04:05 +0800 Subject: [PATCH 0328/2187] virtio-pci: modify the type of config_generation to u8 In virtio spec, the type of config_generation is u8, change it to u8. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 00891e72d..a6dbe740d 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -143,7 +143,7 @@ struct VirtioPciCommonConfig { /// Device status. device_status: u32, /// Configuration atomicity value. - config_generation: u32, + config_generation: u8, /// Queue selector. queue_select: u16, /// The configuration vector for MSI-X. @@ -247,7 +247,7 @@ impl VirtioPciCommonConfig { COMMON_MSIX_REG => self.msix_config.load(Ordering::SeqCst) as u32, COMMON_NUMQ_REG => self.queues_config.len() as u32, COMMON_STATUS_REG => self.device_status, - COMMON_CFGGENERATION_REG => self.config_generation, + COMMON_CFGGENERATION_REG => self.config_generation as u32, COMMON_Q_SELECT_REG => self.queue_select as u32, COMMON_Q_SIZE_REG => self .get_queue_config() @@ -515,7 +515,7 @@ pub struct VirtioPciState { acked_features_select: u32, interrupt_status: u32, device_status: u32, - config_generation: u32, + config_generation: u8, queue_select: u16, msix_config: u16, /// The configuration of queues. Max number of queues is 32(equals to MAX_VIRTIO_QUEUE). -- Gitee From 5d8eb482cc574e1b06c3b3b36fc149fb55b85342 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 3 Nov 2022 21:52:18 +0800 Subject: [PATCH 0329/2187] virtio: add report_virtio_error() It should report error when meet some virtio error. Signed-off-by: Yan Wang --- pci/src/error.rs | 2 + virtio/src/balloon.rs | 16 +- virtio/src/block.rs | 8 +- virtio/src/console.rs | 10 +- virtio/src/gpu.rs | 21 +- virtio/src/lib.rs | 25 +- virtio/src/net.rs | 38 ++- virtio/src/queue.rs | 13 +- virtio/src/rng.rs | 14 +- virtio/src/scsi/controller.rs | 26 +- virtio/src/vhost/kernel/mod.rs | 1 + virtio/src/vhost/kernel/vsock.rs | 11 +- virtio/src/vhost/user/fs.rs | 1 + virtio/src/virtio_mmio.rs | 422 +++++++++++++++++++++---------- virtio/src/virtio_pci.rs | 62 +++-- 15 files changed, 471 insertions(+), 199 deletions(-) diff --git a/pci/src/error.rs b/pci/src/error.rs index ce9e0bcde..fa3c140b0 100644 --- a/pci/src/error.rs +++ b/pci/src/error.rs @@ -35,4 +35,6 @@ pub enum PciError { HotplugUnsupported(u8), #[error("Invalid PCI configuration, key:{0}, value:{1}")] InvalidConf(String, String), + #[error("Failed to enable queue, value is 0x{0:x}")] + QueueEnable(u32), } diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index d23a66fd4..ed321d2a8 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -546,6 +546,9 @@ impl BalloonIoHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } let req = Request::parse(&elem, OUT_IOVEC) .with_context(|| "Fail to parse available descriptor chain")?; if !self.mem_info.lock().unwrap().has_huge_page() { @@ -557,7 +560,7 @@ impl BalloonIoHandler { .with_context(|| "Failed to add balloon response into used queue")?; } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)).with_context( + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false).with_context( || { anyhow!(VirtioError::InterruptTrigger( "balloon", @@ -579,6 +582,9 @@ impl BalloonIoHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } let req = Request::parse(&elem, IN_IOVEC) .with_context(|| "Fail to parse available descriptor chain")?; if !self.mem_info.lock().unwrap().has_huge_page() { @@ -590,7 +596,7 @@ impl BalloonIoHandler { .with_context(|| "Failed to add balloon response into used queue")?; } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue)).with_context( + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false).with_context( || { anyhow!(VirtioError::InterruptTrigger( "balloon", @@ -822,7 +828,7 @@ impl Balloon { /// Notify configuration changes to VM. fn signal_config_change(&self) -> Result<()> { if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Config, None).with_context(|| { + interrupt_cb(&VirtioInterruptType::Config, None, false).with_context(|| { anyhow!(VirtioError::InterruptTrigger( "balloon", VirtioInterruptType::Vring @@ -1257,7 +1263,7 @@ mod tests { let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); let cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { let status = match int_type { VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, @@ -1395,7 +1401,7 @@ mod tests { let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); let interrupt_cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { let status = match int_type { VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 6a36e56fd..87eb868e1 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -166,6 +166,7 @@ impl AioCompleteCb { if let Err(e) = (*self.interrupt_cb.as_ref().unwrap())( &VirtioInterruptType::Vring, Some(&queue_lock), + false, ) { error!( "Failed to trigger interrupt(aio completion) for block device, error is {:?}", @@ -496,6 +497,9 @@ impl BlockIoHandler { } while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } // limit io operations if iops is configured if let Some(lb) = self.leak_bucket.as_mut() { if let Some(ctx) = EventLoop::get_ctx(self.iothread.as_ref()) { @@ -1172,7 +1176,7 @@ impl VirtioDevice for Block { } if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Config, None).with_context(|| { + interrupt_cb(&VirtioInterruptType::Config, None, false).with_context(|| { anyhow!(VirtioError::InterruptTrigger( "block", VirtioInterruptType::Config @@ -1409,7 +1413,7 @@ mod tests { let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); let interrupt_cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { let status = match int_type { VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 7516b4d72..938bd318c 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -91,6 +91,9 @@ impl InputReceiver for ConsoleHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } let mut write_count = 0_usize; for elem_iov in elem.in_iovec.iter() { let allow_write_count = cmp::min(write_count + elem_iov.len as usize, count); @@ -134,7 +137,9 @@ impl InputReceiver for ConsoleHandler { } } - if let Err(ref e) = (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock)) { + if let Err(ref e) = + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + { error!( "Failed to trigger interrupt for console, int-type {:?} {:?} ", VirtioInterruptType::Vring, @@ -158,6 +163,9 @@ impl ConsoleHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } let mut read_count = 0_usize; for elem_iov in elem.out_iovec.iter() { let allow_read_count = cmp::min(read_count + elem_iov.len as usize, buffer.len()); diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 5ef9d21f6..bdc74ddca 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -1482,6 +1482,7 @@ impl GpuIoHandler { (self.interrupt_cb)( &VirtioInterruptType::Vring, Some(&self.ctrl_queue.lock().unwrap()), + false, ) .with_context(|| { anyhow!(VirtioError::InterruptTrigger( @@ -1503,6 +1504,9 @@ impl GpuIoHandler { let mut req_queue = Vec::new(); while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CTRL) { Ok(req) => { req_queue.push(req); @@ -1528,6 +1532,9 @@ impl GpuIoHandler { } while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CURSOR) { Ok(req) => { self.gpu_update_cursor(&req) @@ -1542,12 +1549,14 @@ impl GpuIoHandler { .add_used(&self.mem_space, elem.index, 0) .with_context(|| "Failed to add used ring")?; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "gpu", - VirtioInterruptType::Vring - )) - })?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "gpu", + VirtioInterruptType::Vring + )) + }, + )?; } Ok(()) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9eeb13119..f126277af 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -89,6 +89,7 @@ const CONFIG_STATUS_ACKNOWLEDGE: u32 = 0x01; const CONFIG_STATUS_DRIVER: u32 = 0x02; const CONFIG_STATUS_DRIVER_OK: u32 = 0x04; const CONFIG_STATUS_FEATURES_OK: u32 = 0x08; +const CONFIG_STATUS_NEEDS_RESET: u32 = 0x40; const CONFIG_STATUS_FAILED: u32 = 0x80; /// Feature Bits, refer to Virtio Spec. @@ -223,7 +224,7 @@ pub enum VirtioInterruptType { } pub type VirtioInterrupt = - Box) -> Result<()> + Send + Sync>; + Box, bool) -> Result<()> + Send + Sync>; /// The trait for virtio device operations. pub trait VirtioDevice: Send { @@ -354,3 +355,25 @@ pub trait VirtioTrace { ); } } + +/// The function used to inject interrupt to guest when encounter an virtio error. +pub fn report_virtio_error( + interrupt_cb: Arc, + features: u64, + deactivate_evt: Option<&EventFd>, +) { + if virtio_has_feature(features, VIRTIO_F_VERSION_1) { + interrupt_cb(&VirtioInterruptType::Config, None, true).unwrap_or_else(|e| { + error!( + "Failed to trigger interrupt for virtio error, error is {}", + e + ) + }); + } + // The queue should not work when meeting virtio error. + // So, using deactivate evt to disable the queue. + if let Some(evt) = deactivate_evt { + evt.write(1) + .unwrap_or_else(|e| error!("Failed to deactivate event, error is {}", e)); + } +} diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 80bc2f553..f3e58e1ff 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -121,6 +121,9 @@ impl NetCtrlHandler { .vring .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net control queue")?; + if elem.desc_num == 0 { + return Ok(()); + } let mut used_len = 0; if let Some(ctrl_desc) = elem.out_iovec.get(0) { @@ -175,6 +178,7 @@ impl NetCtrlHandler { (self.interrupt_cb)( &VirtioInterruptType::Vring, Some(&self.ctrl.queue.lock().unwrap()), + false, ) .with_context(|| { anyhow!(VirtioError::InterruptTrigger( @@ -303,6 +307,9 @@ impl NetIoHandler { .vring .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net rx")?; + if elem.desc_num == 0 { + break; + } let mut iovecs = Vec::new(); for elem_iov in elem.in_iovec.iter() { let host_addr = queue @@ -360,12 +367,14 @@ impl NetIoHandler { if self.rx.need_irqs { self.rx.need_irqs = false; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "net", - VirtioInterruptType::Vring - )) - })?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "net", + VirtioInterruptType::Vring + )) + }, + )?; self.trace_send_interrupt("Net".to_string()); } @@ -378,6 +387,9 @@ impl NetIoHandler { let mut need_irq = false; while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } let mut iovecs = Vec::new(); for elem_iov in elem.out_iovec.iter() { let host_addr = queue @@ -419,12 +431,14 @@ impl NetIoHandler { } if need_irq { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "net", - VirtioInterruptType::Vring - )) - })?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "net", + VirtioInterruptType::Vring + )) + }, + )?; self.trace_send_interrupt("Net".to_string()); } diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index b101e91f3..2c3f641d0 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -866,15 +866,14 @@ impl VringOps for SplitVring { } fn pop_avail(&mut self, sys_mem: &Arc, features: u64) -> Result { - let avail_len = self.avail_ring_len(sys_mem)?; - if avail_len == 0 { - bail!("failed to pop avail: empty!"); + let mut element = Element::new(0); + if self.avail_ring_len(sys_mem)? == 0 { + return Ok(element); } // Make sure descriptor read does not bypass avail index read. fence(Ordering::Acquire); - let mut element = Element::new(0); self.get_vring_element(sys_mem, features, &mut element) .with_context(|| "Failed to get vring element")?; @@ -1650,8 +1649,10 @@ mod tests { // set 0 to the idx of avail ring which is equal to next_avail vring.set_avail_ring_idx(&sys_space, 0).unwrap(); let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; - if let Ok(_) = vring.pop_avail(&sys_space, features) { - assert!(false); + if let Ok(elem) = vring.pop_avail(&sys_space, features) { + if elem.desc_num != 0 { + assert!(false); + } } // it is error when the indirect descriptor is written diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index f80db589a..26f480324 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -91,6 +91,9 @@ impl RngHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } let size = get_req_data_size(&elem.in_iovec).with_context(|| "Failed to get request size")?; @@ -130,14 +133,13 @@ impl RngHandler { } if need_interrupt { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock)).with_context( - || { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { anyhow!(VirtioError::InterruptTrigger( "rng", VirtioInterruptType::Vring )) - }, - )?; + })?; self.trace_send_interrupt("Rng".to_string()); } @@ -589,7 +591,7 @@ mod tests { let cloned_interrupt_evt = interrupt_evt.try_clone().unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); let interrupt_cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { let status = match int_type { VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, @@ -674,7 +676,7 @@ mod tests { let cloned_interrupt_evt = interrupt_evt.try_clone().unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); let interrupt_cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { let status = match int_type { VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 1b97ac9af..52d0c9b27 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -568,6 +568,9 @@ impl ScsiCtrlHandler { let mut queue = self.queue.lock().unwrap(); while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } drop(queue); let ctrl_desc = elem.out_iovec.get(0).unwrap(); let ctrl_type = self @@ -635,9 +638,9 @@ impl ScsiCtrlHandler { queue = self.queue.lock().unwrap(); } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { - VirtioError::InterruptTrigger("scsi ctrl", VirtioInterruptType::Vring) - })?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( + || VirtioError::InterruptTrigger("scsi ctrl", VirtioInterruptType::Vring), + )?; Ok(()) } } @@ -750,6 +753,9 @@ impl ScsiCmdHandler { let mut queue = self.queue.lock().unwrap(); while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + if elem.desc_num == 0 { + break; + } match VirtioScsiRequest::::new( &self.mem_space, self.queue.clone(), @@ -826,12 +832,14 @@ impl ScsiCmdHandler { queue = self.queue.lock().unwrap(); } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue)).with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "scsi cmd", - VirtioInterruptType::Vring - )) - })?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "scsi cmd", + VirtioInterruptType::Vring + )) + }, + )?; Ok(()) } diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 12900424c..c2cea3dd5 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -500,6 +500,7 @@ impl EventNotifierHelper for VhostIoHandler { if let Err(e) = (locked_vhost_handler.interrupt_cb)( &VirtioInterruptType::Vring, Some(&host_notify.queue.lock().unwrap()), + false, ) { error!( "Failed to trigger interrupt for vhost device, error is {:?}", diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index ab13c4ec8..ba8b51443 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -127,6 +127,9 @@ impl Vsock { .vring .pop_avail(&self.mem_space, self.state.driver_features) .with_context(|| "Failed to get avail ring element.")?; + if element.desc_num == 0 { + return Ok(()); + } self.mem_space .write_object( @@ -144,8 +147,12 @@ impl Vsock { .with_context(|| format!("Failed to add used ring {}", element.index))?; if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Vring, Some(&*event_queue_locked)) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + interrupt_cb( + &VirtioInterruptType::Vring, + Some(&*event_queue_locked), + false, + ) + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 698f4be02..56c5c2030 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -79,6 +79,7 @@ impl EventNotifierHelper for VhostUserFsHandler { if let Err(e) = (locked_vhost_user.interrup_cb)( &VirtioInterruptType::Vring, Some(&host_notify.queue.lock().unwrap()), + false, ) { error!( "Failed to trigger interrupt for vhost user device, error is {:?}", diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index b4c441a13..a5e768c28 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -28,8 +28,9 @@ use vmm_sys_util::eventfd::EventFd; use super::{ virtio_has_feature, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, VirtioInterruptType, CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, - CONFIG_STATUS_FEATURES_OK, NOTIFY_REG_OFFSET, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, - VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, + CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, NOTIFY_REG_OFFSET, + QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, + VIRTIO_MMIO_INT_VRING, }; use anyhow::{anyhow, bail, Context, Result}; @@ -332,13 +333,15 @@ pub struct VirtioMmioDevice { // HostNotifyInfo used for guest notifier host_notify_info: HostNotifyInfo, // The state of virtio mmio device. - state: VirtioMmioState, + state: Arc>, // System address space. mem_space: Arc, // Virtio queues. queues: Vec>>, // System Resource of device. res: SysRes, + /// The function for interrupt triggering. + interrupt_cb: Option>, } impl VirtioMmioDevice { @@ -351,13 +354,14 @@ impl VirtioMmioDevice { interrupt_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), interrupt_status: Arc::new(AtomicU32::new(0)), host_notify_info: HostNotifyInfo::new(queue_num), - state: VirtioMmioState { + state: Arc::new(Mutex::new(VirtioMmioState { activated: false, config_space: VirtioMmioCommonConfig::new(&device_clone), - }, + })), mem_space: mem_space.clone(), queues: Vec::new(), res: SysRes::default(), + interrupt_cb: None, } } @@ -368,6 +372,7 @@ impl VirtioMmioDevice { region_size: u64, #[cfg(target_arch = "x86_64")] bs: &Arc>, ) -> Result>> { + self.assign_interrupt_cb(); self.device .lock() .unwrap() @@ -397,8 +402,10 @@ impl VirtioMmioDevice { /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate(&mut self) -> Result<()> { - let queues_config = - &mut self.state.config_space.queues_config[0..self.state.config_space.queue_num]; + let mut locked_state = self.state.lock().unwrap(); + let queue_num = locked_state.config_space.queue_num; + let queue_type = locked_state.config_space.queue_type; + let queues_config = &mut locked_state.config_space.queues_config[0..queue_num]; let cloned_mem_space = self.mem_space.clone(); for q_config in queues_config.iter_mut() { q_config.addr_cache.desc_table_host = cloned_mem_space @@ -410,12 +417,13 @@ impl VirtioMmioDevice { q_config.addr_cache.used_ring_host = cloned_mem_space .get_host_address(q_config.used_ring) .unwrap_or(0); - let queue = Queue::new(*q_config, self.state.config_space.queue_type)?; + let queue = Queue::new(*q_config, queue_type)?; if !queue.is_valid(&self.mem_space) { bail!("Invalid queue"); } self.queues.push(Arc::new(Mutex::new(queue))); } + drop(locked_state); let mut queue_evts = Vec::::new(); for fd in self.host_notify_info.events.iter() { @@ -429,12 +437,42 @@ impl VirtioMmioDevice { queue_evts.push(evt_fd_clone); } + if let Some(cb) = self.interrupt_cb.clone() { + self.device.lock().unwrap().activate( + self.mem_space.clone(), + cb, + &self.queues, + queue_evts, + )?; + } else { + bail!("Failed to activate device: No interrupt callback"); + } + + Ok(()) + } + + fn assign_interrupt_cb(&mut self) { let interrupt_status = self.interrupt_status.clone(); let interrupt_evt = self.interrupt_evt.try_clone().unwrap(); + let cloned_state = self.state.clone(); let cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, needs_reset: bool| { + let mut locked_state = cloned_state.lock().unwrap(); let status = match int_type { - VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, + VirtioInterruptType::Config => { + if needs_reset { + locked_state.config_space.device_status |= CONFIG_STATUS_NEEDS_RESET; + if locked_state.config_space.device_status & CONFIG_STATUS_DRIVER_OK + == 0 + { + return Ok(()); + } + } + locked_state.config_space.config_generation += 1; + // Use (CONFIG | VRING) instead of CONFIG, it can be used to solve the + // IO stuck problem by change the device configure. + VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING + } VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status as u32, Ordering::SeqCst); @@ -446,14 +484,7 @@ impl VirtioMmioDevice { }, ) as VirtioInterrupt); - self.device.lock().unwrap().activate( - self.mem_space.clone(), - cb, - &self.queues, - queue_evts, - )?; - - Ok(()) + self.interrupt_cb = Some(cb); } } @@ -462,7 +493,7 @@ impl SysBusDevOps for VirtioMmioDevice { fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { match offset { 0x00..=0xff if data.len() == 4 => { - let value = match self.state.config_space.read_common_config( + let value = match self.state.lock().unwrap().config_space.read_common_config( &self.device, &self.interrupt_status, offset, @@ -509,10 +540,11 @@ impl SysBusDevOps for VirtioMmioDevice { /// Write data by virtio driver from VM. fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { + let mut locked_state = self.state.lock().unwrap(); match offset { 0x00..=0xff if data.len() == 4 => { let value = LittleEndian::read_u32(data); - if let Err(ref e) = self.state.config_space.write_common_config( + if let Err(ref e) = locked_state.config_space.write_common_config( &self.device, &self.interrupt_status, offset, @@ -527,27 +559,28 @@ impl SysBusDevOps for VirtioMmioDevice { return false; } - if self.state.config_space.check_device_status( + if locked_state.config_space.check_device_status( CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER | CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_FAILED, - ) && !self.state.activated + ) && !locked_state.activated { - let ret = self.activate().map(|_| self.state.activated = true); - if let Err(ref e) = ret { + drop(locked_state); + if let Err(ref e) = self.activate() { error!( "Failed to activate dev, type: {}, {:?}", self.device.lock().unwrap().device_type(), e, ); + return false; } + self.state.lock().unwrap().activated = true; } } 0x100..=0xfff => { - if self - .state + if locked_state .config_space .check_device_status(CONFIG_STATUS_DRIVER, CONFIG_STATUS_FAILED) { @@ -567,7 +600,7 @@ impl SysBusDevOps for VirtioMmioDevice { } } else { error!("Failed to write virtio-dev config space: driver is not ready 0x{:X}, type: {}", - self.state.config_space.get_device_status(), + locked_state.config_space.get_device_status(), self.device.lock().unwrap().device_type(), ); return false; @@ -627,7 +660,7 @@ impl acpi::AmlBuilder for VirtioMmioDevice { impl StateTransfer for VirtioMmioDevice { fn get_state_vec(&self) -> migration::Result> { - let mut state = self.state; + let mut state = self.state.lock().unwrap(); for (index, queue) in self.queues.iter().enumerate() { state.config_space.queues_config[index] = @@ -639,14 +672,18 @@ impl StateTransfer for VirtioMmioDevice { } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *VirtioMmioState::from_bytes(state).ok_or_else(|| { - anyhow!(migration::error::MigrationError::FromBytesError( - "MMIO_DEVICE" - )) - })?; + self.state = Arc::new(Mutex::new(*VirtioMmioState::from_bytes(state).ok_or_else( + || { + anyhow!(migration::error::MigrationError::FromBytesError( + "MMIO_DEVICE" + )) + }, + )?)); let cloned_mem_space = self.mem_space.clone(); - let mut queue_states = - self.state.config_space.queues_config[0..self.state.config_space.queue_num].to_vec(); + let locked_state = self.state.lock().unwrap(); + let mut queue_states = locked_state.config_space.queues_config + [0..locked_state.config_space.queue_num] + .to_vec(); self.queues = queue_states .iter_mut() .map(|queue_state| { @@ -660,11 +697,12 @@ impl StateTransfer for VirtioMmioDevice { .get_host_address(queue_state.used_ring) .unwrap_or(0); Arc::new(Mutex::new( - Queue::new(*queue_state, self.state.config_space.queue_type).unwrap(), + Queue::new(*queue_state, locked_state.config_space.queue_type).unwrap(), )) }) .collect(); - self.interrupt_status = Arc::new(AtomicU32::new(self.state.config_space.interrupt_status)); + self.interrupt_status = + Arc::new(AtomicU32::new(locked_state.config_space.interrupt_status)); Ok(()) } @@ -680,7 +718,7 @@ impl StateTransfer for VirtioMmioDevice { impl MigrationHook for VirtioMmioDevice { fn resume(&mut self) -> migration::Result<()> { - if self.state.activated { + if self.state.lock().unwrap().activated { let mut queue_evts = Vec::::new(); for fd in self.host_notify_info.events.iter() { let evt_fd_clone = match fd.try_clone() { @@ -693,30 +731,17 @@ impl MigrationHook for VirtioMmioDevice { queue_evts.push(evt_fd_clone); } - let interrupt_status = self.interrupt_status.clone(); - let interrupt_evt = self.interrupt_evt.try_clone().unwrap(); - let cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, _queue: Option<&Queue>| { - let status = match int_type { - VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, - VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, - }; - interrupt_status.fetch_or(status as u32, Ordering::SeqCst); - interrupt_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - - Ok(()) - }, - ) as VirtioInterrupt); - - if let Err(e) = self.device.lock().unwrap().activate( - self.mem_space.clone(), - cb, - &self.queues, - queue_evts, - ) { - bail!("Failed to resume virtio mmio device: {}", e); + if let Some(cb) = self.interrupt_cb.clone() { + if let Err(e) = self.device.lock().unwrap().activate( + self.mem_space.clone(), + cb, + &self.queues, + queue_evts, + ) { + bail!("Failed to resume virtio mmio device: {}", e); + } + } else { + bail!("Failed to resume device: No interrupt callback"); } } @@ -874,27 +899,22 @@ mod tests { let sys_space = address_space_init(); let virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); - assert_eq!(virtio_mmio_device.state.activated, false); + assert_eq!(virtio_mmio_device.state.lock().unwrap().activated, false); assert_eq!( virtio_mmio_device.host_notify_info.events.len(), virtio_device_clone.lock().unwrap().queue_num() ); - assert_eq!(virtio_mmio_device.state.config_space.features_select, 0); + let config_space = virtio_mmio_device.state.lock().unwrap().config_space; + assert_eq!(config_space.features_select, 0); + assert_eq!(config_space.acked_features_select, 0); + assert_eq!(config_space.device_status, 0); + assert_eq!(config_space.config_generation, 0); + assert_eq!(config_space.queue_select, 0); assert_eq!( - virtio_mmio_device.state.config_space.acked_features_select, - 0 - ); - assert_eq!(virtio_mmio_device.state.config_space.device_status, 0); - assert_eq!(virtio_mmio_device.state.config_space.config_generation, 0); - assert_eq!(virtio_mmio_device.state.config_space.queue_select, 0); - assert_eq!( - virtio_mmio_device.state.config_space.queue_num, + config_space.queue_num, virtio_device_clone.lock().unwrap().queue_num() ); - assert_eq!( - virtio_mmio_device.state.config_space.queue_type, - QUEUE_TYPE_SPLIT_VRING - ); + assert_eq!(config_space.queue_type, QUEUE_TYPE_SPLIT_VRING); } #[test] @@ -940,7 +960,12 @@ mod tests { // read the register of the features // get low 32bit of the features let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.features_select = 0; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .features_select = 0; virtio_device_clone.lock().unwrap().device_features = 0x0000_00f8_0000_00fe; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), @@ -949,7 +974,12 @@ mod tests { assert_eq!(LittleEndian::read_u32(&buf[..]), 0x0000_00fe); // get high 32bit of the features for device which supports VirtIO Version 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.features_select = 1; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .features_select = 1; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), true @@ -967,7 +997,12 @@ mod tests { // read the register representing max size of the queue // for queue_select as 0 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 0; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .queue_select = 0; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), true @@ -975,7 +1010,12 @@ mod tests { assert_eq!(LittleEndian::read_u32(&buf[..]), QUEUE_SIZE as u32); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 1; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .queue_select = 1; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), true @@ -985,8 +1025,10 @@ mod tests { // read the register representing the status of queue // for queue_select as 0 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); LittleEndian::write_u32(&mut buf[..], 1); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), @@ -1000,8 +1042,10 @@ mod tests { assert_eq!(LittleEndian::read_u32(&data[..]), 1); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 1; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 1; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, QUEUE_READY_REG), true @@ -1027,14 +1071,24 @@ mod tests { // read the register representing the status of device let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.device_status = 0; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .device_status = 0; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG), true ); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.device_status = 5; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .device_status = 5; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG), true @@ -1058,7 +1112,12 @@ mod tests { ); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.config_generation = 10; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .config_generation = 10; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, CONFIG_GENERATION_REG), true @@ -1106,30 +1165,55 @@ mod tests { virtio_mmio_device.write(&buf[..], addr, DEVICE_FEATURES_SEL_REG), true ); - assert_eq!(virtio_mmio_device.state.config_space.features_select, 2); + assert_eq!( + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .features_select, + 2 + ); // write the device features // false when the device status is CONFIG_STATUS_FEATURES_OK or CONFIG_STATUS_FAILED isn't CONFIG_STATUS_DRIVER - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .device_status = CONFIG_STATUS_FEATURES_OK; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), false ); - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FAILED; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .device_status = CONFIG_STATUS_FAILED; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), false ); - virtio_mmio_device.state.config_space.device_status = + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .device_status = CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_FAILED | CONFIG_STATUS_DRIVER; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), false ); // it is ok to write the low 32bit of device features - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_DRIVER; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.device_status = CONFIG_STATUS_DRIVER; let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.acked_features_select = 0; + locked_state.config_space.acked_features_select = 0; + drop(locked_state); LittleEndian::write_u32(&mut buf[..], 0x0000_00fe); virtio_device_clone.lock().unwrap().device_features = 0x0000_00fe; assert_eq!( @@ -1142,7 +1226,12 @@ mod tests { ); // it is ok to write the high 32bit of device features let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.acked_features_select = 1; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .acked_features_select = 1; LittleEndian::write_u32(&mut buf[..], 0x0000_00ff); virtio_device_clone.lock().unwrap().device_features = 0x0000_00ff_0000_0000; assert_eq!( @@ -1150,7 +1239,12 @@ mod tests { true ); assert_eq!( - virtio_mmio_device.state.config_space.queue_type, + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .queue_type, QUEUE_TYPE_PACKED_VRING ); assert_eq!( @@ -1166,7 +1260,12 @@ mod tests { true ); assert_eq!( - virtio_mmio_device.state.config_space.acked_features_select, + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .acked_features_select, 0x00ff_0000 ); @@ -1178,20 +1277,28 @@ mod tests { true ); assert_eq!( - virtio_mmio_device.state.config_space.queue_select, + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .queue_select, 0x0000_ff00 ); // write the size of queue let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); LittleEndian::write_u32(&mut buf[..], 128); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_NUM_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + let locked_state = virtio_mmio_device.state.lock().unwrap(); + if let Ok(config) = locked_state.config_space.get_queue_config() { assert_eq!(config.size, 128); } else { assert!(false); @@ -1207,8 +1314,10 @@ mod tests { // write the ready status of queue let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); LittleEndian::write_u32(&mut buf[..], 1); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), @@ -1222,8 +1331,10 @@ mod tests { assert_eq!(LittleEndian::read_u32(&data[..]), 1); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); LittleEndian::write_u32(&mut buf[..], 2); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), @@ -1238,7 +1349,12 @@ mod tests { // write the interrupt status let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_DRIVER_OK; + virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .device_status = CONFIG_STATUS_DRIVER_OK; virtio_mmio_device .interrupt_status .store(0b10_1111, Ordering::Relaxed); @@ -1263,90 +1379,133 @@ mod tests { let addr = GuestAddress(0); // write the low 32bit of queue's descriptor table address - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xffff_fefe); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_LOW_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + if let Ok(config) = virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .get_queue_config() + { assert_eq!(config.desc_table.0 as u32, 0xffff_fefe) } else { assert!(false); } // write the high 32bit of queue's descriptor table address - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xfcfc_ffff); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_HIGH_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + if let Ok(config) = virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .get_queue_config() + { assert_eq!((config.desc_table.0 >> 32) as u32, 0xfcfc_ffff) } else { assert!(false); } // write the low 32bit of queue's available ring address - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xfcfc_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_LOW_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + if let Ok(config) = virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .get_queue_config() + { assert_eq!(config.avail_ring.0 as u32, 0xfcfc_fafa) } else { assert!(false); } // write the high 32bit of queue's available ring address - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xecec_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_HIGH_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + if let Ok(config) = virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .get_queue_config() + { assert_eq!((config.avail_ring.0 >> 32) as u32, 0xecec_fafa) } else { assert!(false); } // write the low 32bit of queue's used ring address - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xacac_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_LOW_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + if let Ok(config) = virtio_mmio_device + .state + .lock() + .unwrap() + .config_space + .get_queue_config() + { assert_eq!(config.used_ring.0 as u32, 0xacac_fafa) } else { assert!(false); } // write the high 32bit of queue's used ring address - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + drop(locked_state); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xcccc_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_HIGH_REG), true ); - if let Ok(config) = virtio_mmio_device.state.config_space.get_queue_config() { + let locked_state = virtio_mmio_device.state.lock().unwrap(); + if let Ok(config) = locked_state.config_space.get_queue_config() { assert_eq!((config.used_ring.0 >> 32) as u32, 0xcccc_fafa) } else { assert!(false); @@ -1370,9 +1529,11 @@ mod tests { let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); let addr = GuestAddress(0); - virtio_mmio_device.state.config_space.queue_select = 0; - virtio_mmio_device.state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - if let Ok(config) = virtio_mmio_device.state.config_space.get_mut_queue_config() { + virtio_mmio_device.assign_interrupt_cb(); + let mut locked_state = virtio_mmio_device.state.lock().unwrap(); + locked_state.config_space.queue_select = 0; + locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; + if let Ok(config) = locked_state.config_space.get_mut_queue_config() { config.desc_table = GuestAddress(0); config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * 16); config.used_ring = GuestAddress(align( @@ -1382,8 +1543,8 @@ mod tests { config.size = QUEUE_SIZE; config.ready = true; } - virtio_mmio_device.state.config_space.queue_select = 1; - if let Ok(config) = virtio_mmio_device.state.config_space.get_mut_queue_config() { + locked_state.config_space.queue_select = 1; + if let Ok(config) = locked_state.config_space.get_mut_queue_config() { config.desc_table = GuestAddress(0); config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * 16); config.used_ring = GuestAddress(align( @@ -1393,12 +1554,13 @@ mod tests { config.size = QUEUE_SIZE / 2; config.ready = true; } + drop(locked_state); // write the device status let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], CONFIG_STATUS_ACKNOWLEDGE); assert_eq!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG), true); - assert_eq!(virtio_mmio_device.state.activated, false); + assert_eq!(virtio_mmio_device.state.lock().unwrap().activated, false); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; assert_eq!( virtio_mmio_device.read(&mut data[..], addr, STATUS_REG), @@ -1416,7 +1578,7 @@ mod tests { ); assert_eq!(virtio_device_clone.lock().unwrap().b_active, false); assert_eq!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG), true); - assert_eq!(virtio_mmio_device.state.activated, true); + assert_eq!(virtio_mmio_device.state.lock().unwrap().activated, true); assert_eq!(virtio_device_clone.lock().unwrap().b_active, true); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; assert_eq!( diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index a6dbe740d..e18d16a4d 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -38,13 +38,14 @@ use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; use crate::{ - virtio_has_feature, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + report_virtio_error, virtio_has_feature, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, + VirtioInterruptType, }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, - CONFIG_STATUS_FEATURES_OK, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, - VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_MMIO_INT_CONFIG, VIRTIO_TYPE_BLOCK, - VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, + CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, QUEUE_TYPE_PACKED_VRING, + QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_MMIO_INT_CONFIG, + VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -366,7 +367,7 @@ impl VirtioPciCommonConfig { COMMON_Q_ENABLE_REG => { if value != 1 { error!("Driver set illegal value for queue_enable {}", value); - return Ok(()); + return Err(anyhow!(PciError::QueueEnable(value))); } self.get_mut_queue_config() .map(|config| config.ready = true)?; @@ -607,19 +608,24 @@ impl VirtioPciDevice { let cloned_msix = self.config.msix.clone(); let dev_id = self.dev_id.clone(); let cb = Arc::new(Box::new( - move |int_type: &VirtioInterruptType, queue: Option<&Queue>| { + move |int_type: &VirtioInterruptType, queue: Option<&Queue>, needs_reset: bool| { + let mut locked_common_cfg = cloned_common_cfg.lock().unwrap(); let vector = match int_type { VirtioInterruptType::Config => { - cloned_common_cfg - .lock() - .unwrap() - .interrupt_status - .fetch_or(VIRTIO_MMIO_INT_CONFIG, Ordering::SeqCst); - cloned_common_cfg - .lock() - .unwrap() - .msix_config - .load(Ordering::SeqCst) + if needs_reset { + locked_common_cfg.device_status |= CONFIG_STATUS_NEEDS_RESET; + if locked_common_cfg.device_status & CONFIG_STATUS_DRIVER_OK == 0 { + return Ok(()); + } + } + // Use (CONFIG | VRING) instead of CONFIG, it can be used to solve the + // IO stuck problem by change the device configure. + locked_common_cfg.interrupt_status.fetch_or( + VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING, + Ordering::SeqCst, + ); + locked_common_cfg.config_generation += 1; + locked_common_cfg.msix_config.load(Ordering::SeqCst) } VirtioInterruptType::Vring => { queue.map_or(0, |q| q.vring.get_queue_config().vector) @@ -736,16 +742,34 @@ impl VirtioPciDevice { .unwrap() .device_status; - if let Err(e) = cloned_pci_device + // In report_virtio_error(), it will lock cloned_pci_device.common_config. + // So, avoid deadlock by getting the returned value firstly. + let err = cloned_pci_device .common_config .lock() .unwrap() - .write_common_config(&cloned_pci_device.device.clone(), offset, value) - { + .write_common_config(&cloned_pci_device.device.clone(), offset, value); + if let Err(e) = err { error!( "Failed to write common config of virtio-pci device, error is {:?}", e, ); + if let Some(cb) = cloned_pci_device.interrupt_cb.clone() { + if let Some(PciError::QueueEnable(_)) = e.downcast_ref::() { + let features = (cloned_pci_device + .device + .lock() + .unwrap() + .get_driver_features(1) as u64) + << 32; + report_virtio_error(cb, features, None); + if let Err(e) = cloned_pci_device.device.lock().unwrap().deactivate() { + error!("Failed to deactivate virtio device, error is {:?}", e); + } + } + } else { + error!("Failed to get interrupt callback"); + } return false; } -- Gitee From b2374fd2b7c292d96d68427124643b151067a6b0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 3 Nov 2022 21:58:49 +0800 Subject: [PATCH 0330/2187] virtio-net: report virtio error in some error case Signed-off-by: Yan Wang --- virtio/src/net.rs | 53 +++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index f3e58e1ff..ddf6f1359 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -26,8 +26,7 @@ use super::{ VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; -use crate::virtio_has_feature; -use crate::VirtioError; +use crate::{report_virtio_error, virtio_has_feature, VirtioError}; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use log::error; @@ -113,11 +112,8 @@ impl ByteCode for CrtlHdr {} impl NetCtrlHandler { fn handle_ctrl(&mut self) -> Result<()> { - let elem = self - .ctrl - .queue - .lock() - .unwrap() + let mut locked_queue = self.ctrl.queue.lock().unwrap(); + let elem = locked_queue .vring .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net control queue")?; @@ -167,25 +163,19 @@ impl NetCtrlHandler { self.mem_space.write_object::(&data, status.addr)?; } - self.ctrl - .queue - .lock() - .unwrap() + locked_queue .vring .add_used(&self.mem_space, elem.index, used_len) .with_context(|| format!("Failed to add used ring {}", elem.index))?; - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.ctrl.queue.lock().unwrap()), - false, - ) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "ctrl", - VirtioInterruptType::Vring - )) - })?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false).with_context( + || { + anyhow!(VirtioError::InterruptTrigger( + "ctrl", + VirtioInterruptType::Vring + )) + }, + )?; Ok(()) } @@ -218,11 +208,15 @@ impl EventNotifierHelper for NetCtrlHandler { let cloned_net_io = net_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - cloned_net_io - .lock() - .unwrap() - .handle_ctrl() - .unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {}.", e)); + let mut locked_net_io = cloned_net_io.lock().unwrap(); + locked_net_io.handle_ctrl().unwrap_or_else(|e| { + error!("Failed to handle ctrl queue, error is {}.", e); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + Some(&locked_net_io.deactivate_evt), + ); + }); None }); let mut notifiers = Vec::new(); @@ -634,6 +628,11 @@ impl EventNotifierHelper for NetIoHandler { let mut locked_net_io = cloned_net_io.lock().unwrap(); if let Err(ref e) = locked_net_io.handle_rx() { error!("Failed to handle rx(tap event), {:?}", e); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + Some(&locked_net_io.deactivate_evt), + ); } if let Some(tap) = locked_net_io.tap.as_ref() { -- Gitee From 90b480c5dc2aa872dbc078ad0fca43bf0ba75833 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 4 Nov 2022 10:02:41 +0800 Subject: [PATCH 0331/2187] virtio-net: Support VIRTIO_NET_F_GUEST/HOST_TOS6 Signed-off-by: Yan Wang --- virtio/src/lib.rs | 2 ++ virtio/src/net.rs | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index f126277af..dc01bef2a 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -122,6 +122,8 @@ pub const VIRTIO_NET_F_GUEST_ECN: u32 = 9; pub const VIRTIO_NET_F_GUEST_UFO: u32 = 10; /// Device can receive TSOv4. pub const VIRTIO_NET_F_HOST_TSO4: u32 = 11; +/// Device can receive TSOv6. +pub const VIRTIO_NET_F_HOST_TSO6: u32 = 12; /// Device can receive UFO. pub const VIRTIO_NET_F_HOST_UFO: u32 = 14; /// Device can merge receive buffers. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index ddf6f1359..1573794de 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -23,8 +23,9 @@ use super::{ VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::{report_virtio_error, virtio_has_feature, VirtioError}; use address_space::AddressSpace; @@ -866,8 +867,10 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GUEST_CSUM | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_TSO6 | 1 << VIRTIO_NET_F_GUEST_UFO | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_TSO6 | 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_F_RING_EVENT_IDX; -- Gitee From 0d880601b987b3626289683af1f67be37849ecf9 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 8 Nov 2022 19:59:42 +0800 Subject: [PATCH 0332/2187] virtio-net: interrupt the guest even though reading the tap device failed It should interrupt the Guest even though reading the tap device failed. Otherwise, the receiving queue will not work any more. Signed-off-by: Yan Wang --- virtio/src/net.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 1573794de..7e9b5a491 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -345,7 +345,8 @@ impl NetIoHandler { error!("Failed to read tap: {}", e); } } - bail!("Failed to call readv for net handle_rx: {}", e); + error!("Failed to call readv for net handle_rx: {}", e); + break; } queue -- Gitee From 172d884c2a5bdc2a08dc8cd357d5bcbe5c8fe969 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 16:21:18 +0800 Subject: [PATCH 0333/2187] pci: fix link speed registers According to PCIe spec, 8.2.1, link speed shall be at least 2.5G, and all the cap bits from 2.5G to the highest speed shall all be set. It wrongly set wmask bits instead, fix that. Signed-off-by: Zhang Bo --- pci/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0b7bc317f..6625bd0f7 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -845,7 +845,7 @@ impl PciConfig { offset = cap_offset + PcieCap::LinkCap2 as usize; le_write_u32( - &mut self.write_mask, + &mut self.config, offset, PCIE_CAP_LINK_SLSV_2_5GT | PCIE_CAP_LINK_SLSV_5GT @@ -853,7 +853,7 @@ impl PciConfig { | PCIE_CAP_LINK_SLSV_16GT, )?; offset = cap_offset + PcieCap::LinkCtl2 as usize; - le_write_u16(&mut self.write_mask, offset, PCIE_CAP_LINK_TLS_16GT)?; + le_write_u16(&mut self.config, offset, PCIE_CAP_LINK_TLS_16GT)?; Ok(cap_offset) } -- Gitee From 557e0e2f83ea72ac9f6680c5d568b9fe680e6ffe Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 17:26:34 +0800 Subject: [PATCH 0334/2187] pci: set PCC to power off at the begining during guest startup With patch 773cf0, when root port has no downstream devices, the guest would wait for several seconds to boot up, it seems that we set power on later would work around this issue, as we already clear PCC(set power on)later when devices are avaible under root port, so revert 773cf0 to leave power state to off anyway. Signed-off-by: Zhang Bo --- pci/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0b7bc317f..e76637a2a 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -802,7 +802,7 @@ impl PciConfig { le_write_u16( &mut self.config, offset, - PCIE_CAP_SLOT_AIC_OFF | PCIE_CAP_SLOT_PIC_OFF, + PCIE_CAP_SLOT_AIC_OFF | PCIE_CAP_SLOT_PIC_OFF | PCIE_CAP_SLOT_PCC, )?; le_write_u16( &mut self.write_mask, -- Gitee From e3bcce1ba235044a32e545c7fe189e4089934366 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 9 Nov 2022 19:15:12 +0800 Subject: [PATCH 0335/2187] rtc/test: replace assert_eq with assert Refactor rtc unit test with assert. Signed-off-by: yezengruan --- devices/src/legacy/pl031.rs | 6 ++---- devices/src/legacy/rtc.rs | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 658b835d2..f34ed2bb2 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -258,8 +258,7 @@ mod test { PL031::read(&mut rtc, &mut data, GuestAddress(0), RTC_DR); let rtick = LittleEndian::read_u32(&data); - let rtc_check = (rtick - wtick) <= WIGGLE; - assert_eq!(rtc_check, true); + assert!((rtick - wtick) <= WIGGLE); } #[test] @@ -274,7 +273,6 @@ mod test { PL031::read(&mut rtc, &mut data, GuestAddress(0), RTC_DR); let rtick = LittleEndian::read_u32(&data); - let rtc_check = (rtick - wtick) <= WIGGLE; - assert_eq!(rtc_check, true); + assert!((rtick - wtick) <= WIGGLE); } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index ad7d1bba6..07286d713 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -465,8 +465,7 @@ mod test { cmos_write(&mut rtc, RTC_MINUTES, 0x04); cmos_write(&mut rtc, RTC_SECONDS, 0x56); - let seconds_check = (cmos_read(&mut rtc, RTC_SECONDS) - 0x56) <= WIGGLE; - assert_eq!(seconds_check, true); + assert!((cmos_read(&mut rtc, RTC_SECONDS) - 0x56) <= WIGGLE); assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x04); assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x02); assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x13); @@ -489,8 +488,7 @@ mod test { cmos_write(&mut rtc, RTC_MINUTES, 0x00); cmos_write(&mut rtc, RTC_SECONDS, 0x00); - let seconds_check = (cmos_read(&mut rtc, RTC_SECONDS) - 0x00) <= WIGGLE; - assert_eq!(seconds_check, true); + assert!((cmos_read(&mut rtc, RTC_SECONDS) - 0x00) <= WIGGLE); assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x00); assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x00); assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x01); -- Gitee From 52e0dfa8f39b98eebf6537b9321b2f50e29444e7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 2 Nov 2022 10:26:17 +0800 Subject: [PATCH 0336/2187] xhci: register bus device at the end Register bus device at the end to avoid the resource leaks due to realize failure. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_pci.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 5fa4d0e72..d658a35c7 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -198,13 +198,7 @@ impl PciDevOps for XhciPciDevice { let devfn = self.devfn; let dev = Arc::new(Mutex::new(self)); - // Register xhci to bus device. let cloned_dev = dev.clone(); - let locked_dev = dev.lock().unwrap(); - let mut locked_device = locked_dev.bus_device.lock().unwrap(); - locked_device.insert(String::from("usb.0"), cloned_dev); - drop(locked_device); - drop(locked_dev); // Register xhci-pci to xhci-device for notify. dev.lock().unwrap().xhci.lock().unwrap().ctrl_ops = Some(Arc::downgrade(&dev) as Weak>); @@ -221,6 +215,10 @@ impl PciDevOps for XhciPciDevice { pci_device.unwrap().lock().unwrap().name() ); } + // Register xhci to bus device. + let locked_dev = dev.lock().unwrap(); + let mut locked_device = locked_dev.bus_device.lock().unwrap(); + locked_device.insert(String::from("usb.0"), cloned_dev); Ok(()) } -- Gitee From 1316b473897e3cdbcab41b6da57e98a315b9c4fd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 2 Nov 2022 15:57:41 +0800 Subject: [PATCH 0337/2187] xhci: adjust the ring segment limit The ring segments max size in bytes is 64k, adjust the limit. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_ring.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index d0c7159f1..0d82832c3 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -39,7 +39,10 @@ pub const TRB_TR_IOC: u32 = 1 << 5; pub const TRB_TR_IDT: u32 = 1 << 6; const TRB_LINK_LIMIT: u32 = 32; -const RING_LEN_LIMIT: u32 = 32; +/// The max size of a ring segment in bytes is 64k. +const RING_SEGMENT_LIMIT: u32 = 0x1_0000; +/// The max size of ring. +const RING_LEN_LIMIT: u32 = TRB_LINK_LIMIT * RING_SEGMENT_LIMIT / TRB_SIZE; /// TRB Type Definitions. See the spec 6.4.6 TRB types. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -- Gitee From 1202c46e3f5af238794c0556ce98c39470dda74f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 3 Nov 2022 10:42:34 +0800 Subject: [PATCH 0338/2187] xhci: add registers offset note Add registers offset in note, and adjust the port register order. Signed-off-by: zhouli57 --- pci/src/config.rs | 3 --- usb/src/xhci/xhci_pci.rs | 31 ++++++++++++++++++------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0b7bc317f..8087f95f9 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -262,9 +262,6 @@ const PCIE_CAP_LINK_TLS_16GT: u16 = 0x0004; // PCIe type flag const PCI_EXP_FLAGS_TYPE_SHIFT: u16 = 4; const PCI_EXP_FLAGS_TYPE: u16 = 0x00f0; -// XHCI device id -pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; -pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; /// Type of bar region. #[derive(PartialEq, Debug, Copy, Clone)] diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index d658a35c7..5ffc48175 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -17,8 +17,8 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; use pci::config::{ PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, - PCIE_CONFIG_SPACE_SIZE, PCI_CLASS_SERIAL_USB, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, - ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, + PCIE_CONFIG_SPACE_SIZE, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, + SUB_CLASS_CODE, VENDOR_ID, }; use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; @@ -39,6 +39,7 @@ const PCI_INTERRUPT_PIN: u16 = 0x3d; const PCI_CACHE_LINE_SIZE: u16 = 0x0c; const PCI_SERIAL_BUS_RELEASE_NUMBER: u8 = 0x60; const PCI_SERIAL_BUS_RELEASE_VERSION_3_0: u8 = 0x30; +const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; /// PCI capability offset or size. const XHCI_PCI_CONFIG_LENGTH: u32 = 0x4000; const XHCI_PCI_CAP_OFFSET: u32 = 0x0; @@ -54,6 +55,10 @@ const XHCI_PCI_PORT_LENGTH: u32 = 0x10; const XHCI_MSIX_TABLE_OFFSET: u32 = 0x3000; const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; +/// Registers offset. +/// 0x0 0x40 0x440 0x1000 0x2000 0x3000 0x4000 +/// | cap | oper | port | runtime | doorbell | MSIX | + /// XHCI pci device which can be attached to PCI bus. pub struct XhciPciDevice { pci_config: PciConfig, @@ -104,6 +109,17 @@ impl XhciPciDevice { || "Failed to register oper region.", )?; + let port_num = self.xhci.lock().unwrap().port_num; + for i in 0..port_num { + let port = &self.xhci.lock().unwrap().ports[i as usize]; + let port_region = + Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); + let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i) as u64; + pci::Result::with_context(self.mem_region.add_subregion(port_region, offset), || { + "Failed to register port region." + })?; + } + let mut runtime_region = Region::init_io_region( XHCI_PCI_RUNTIME_LENGTH as u64, build_runtime_ops(&self.xhci), @@ -124,17 +140,6 @@ impl XhciPciDevice { .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64), || "Failed to register doorbell region.", )?; - - let port_num = self.xhci.lock().unwrap().port_num; - for i in 0..port_num { - let port = &self.xhci.lock().unwrap().ports[i as usize]; - let port_region = - Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); - let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i) as u64; - pci::Result::with_context(self.mem_region.add_subregion(port_region, offset), || { - "Failed to register port region." - })?; - } Ok(()) } } -- Gitee From 260e67ce7205977c1b6135700ecb889577428d55 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 3 Nov 2022 10:52:29 +0800 Subject: [PATCH 0339/2187] xhci: remove the useless port_num The port_num in xhci is replaced by the sum of USB2.0 port and USB3.0 port. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 13 +++++++------ usb/src/xhci/xhci_pci.rs | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 4660774dd..0f024051d 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -75,6 +75,9 @@ const PORT_EVENT_ID_SHIFT: u32 = 24; const SLOT_CTX_PORT_NUMBER_SHIFT: u32 = 16; const ENDPOINT_ID_START: u32 = 1; const MAX_ENDPOINTS: u32 = 31; +/// XHCI config +const XHCI_PORT2_NUM: u32 = 2; +const XHCI_PORT3_NUM: u32 = 2; type DmaAddr = u64; @@ -319,7 +322,6 @@ pub struct XhciDevice { pub oper: XchiOperReg, pub usb_ports: Vec>>, pub ports: Vec>>, - pub port_num: u32, pub slots: Vec, pub intrs: Vec, pub cmd_ring: XhciRing, @@ -334,9 +336,8 @@ impl XhciDevice { oper: XchiOperReg::new(), ctrl_ops: None, usb_ports: Vec::new(), - numports_2: 2, - numports_3: 2, - port_num: 4, + numports_2: XHCI_PORT2_NUM, + numports_3: XHCI_PORT3_NUM, ports: Vec::new(), slots: vec![XhciSlot::new(mem_space); MAX_SLOTS as usize], intrs: vec![XhciInterrupter::new(mem_space); 1], @@ -348,7 +349,7 @@ impl XhciDevice { let clone_xhci = xhci.clone(); let mut locked_xhci = clone_xhci.lock().unwrap(); locked_xhci.oper.usb_status = USB_STS_HCH; - for i in 0..locked_xhci.port_num { + for i in 0..(XHCI_PORT2_NUM + XHCI_PORT3_NUM) { locked_xhci.ports.push(Arc::new(Mutex::new(XhciPort::new( &Arc::downgrade(&clone_xhci), format!("xhci-port-{}", i), @@ -519,7 +520,7 @@ impl XhciDevice { fn lookup_usb_port(&mut self, slot_ctx: &XhciSlotCtx) -> Option>> { let mut path = String::new(); let mut port = slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff; - if port < 1 || port > self.port_num { + if port < 1 || port > self.ports.len() as u32 { error!("Invalid port: {}", port); return None; } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 5ffc48175..c46e3cb8d 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -109,12 +109,12 @@ impl XhciPciDevice { || "Failed to register oper region.", )?; - let port_num = self.xhci.lock().unwrap().port_num; + let port_num = self.xhci.lock().unwrap().ports.len(); for i in 0..port_num { - let port = &self.xhci.lock().unwrap().ports[i as usize]; + let port = &self.xhci.lock().unwrap().ports[i]; let port_region = Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); - let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i) as u64; + let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i as u32) as u64; pci::Result::with_context(self.mem_region.add_subregion(port_region, offset), || { "Failed to register port region." })?; -- Gitee From 42421b598752d7909b94f06230de2ef72a482868 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 3 Nov 2022 11:43:56 +0800 Subject: [PATCH 0340/2187] xhci: send the interrupt only when needed Signed-off-by: zhouli57 --- usb/src/xhci/xhci_pci.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index c46e3cb8d..f50d6ccfe 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -290,16 +290,18 @@ impl PciDevOps for XhciPciDevice { } impl XhciOps for XhciPciDevice { - fn trigger_intr(&mut self, n: u32, _level: bool) -> bool { + fn trigger_intr(&mut self, n: u32, trigger: bool) -> bool { if let Some(msix) = self.pci_config.msix.as_mut() { - msix.lock() - .unwrap() - .notify(n as u16, self.dev_id.load(Ordering::Acquire)); - true + if trigger { + msix.lock() + .unwrap() + .notify(n as u16, self.dev_id.load(Ordering::Acquire)); + return true; + } } else { error!("Failed to send interrupt: msix does not exist"); - false } + false } fn update_intr(&mut self, _n: u32, _enable: bool) {} -- Gitee From 0930766be1ac4c40c3eacba2a1a077ff8d82039b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 5 Nov 2022 10:23:27 +0800 Subject: [PATCH 0341/2187] xhci : fix some bugs for oper registers according to the spec 1. Set PCD when port has a change bit. 2. Add the HCCPARAMS2 register. 3. Update the CRCR register read operation according to the spec 5.4.5 Command Ring Control Register. 4. Optimized runtime register implementation. 5. Set RCS when write to Command Ring Control Register. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 5 +- usb/src/xhci/xhci_regs.rs | 145 +++++++++++++++++++------------- usb/src/xhci/xhci_ring.rs | 4 + 3 files changed, 92 insertions(+), 62 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 0f024051d..4729c650e 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -472,7 +472,7 @@ impl XhciDevice { Ok(()) } - /// Update the xhci port status and then + /// Update the xhci port status and then notify the driver. pub fn port_update(&mut self, port: &Arc>) -> Result<()> { let mut locked_port = port.lock().unwrap(); locked_port.portsc = PORTSC_PP; @@ -499,6 +499,7 @@ impl XhciDevice { locked_port.portsc, pls ); drop(locked_port); + self.oper.usb_status |= USB_STS_PCD; self.port_notify(port, PORTSC_CSC)?; Ok(()) } @@ -1443,8 +1444,6 @@ impl XhciDevice { 0 } - pub fn update_mf(&self) {} - pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { let intr = &mut self.intrs[idx as usize]; if intr.erstsz == 0 || intr.erstba == 0 { diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 2f8459597..bc455c5fe 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -28,49 +28,51 @@ use anyhow::{bail, Result}; pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; pub(crate) const XHCI_OFF_DOORBELL: u32 = 0x2000; pub(crate) const XHCI_OFF_RUNTIME: u32 = 0x1000; -/// Capability Registers -/// Capability Register Length +/// Capability Registers. +/// Capability Register Length. const XHCI_CAP_REG_CAPLENGTH: u64 = 0x00; -/// Interface Version Number +/// Interface Version Number. const XHCI_CAP_REG_HCIVERSION: u64 = 0x02; -/// Structural Parameters 1 +/// Structural Parameters 1. const XHCI_CAP_REG_HCSPARAMS1: u64 = 0x04; -/// Structural Parameters 2 +/// Structural Parameters 2. const XHCI_CAP_REG_HCSPARAMS2: u64 = 0x08; -/// Structural Parameters 3 +/// Structural Parameters 3. const XHCI_CAP_REG_HCSPARAMS3: u64 = 0x0c; -/// Capability Parameters 1 +/// Capability Parameters 1. const XHCI_CAP_REG_HCCPARAMS1: u64 = 0x10; -/// Doorbell Offset +/// Doorbell Offset. const XHCI_CAP_REG_DBOFF: u64 = 0x14; -/// Runtime Register Space Offset +/// Runtime Register Space Offset. const XHCI_CAP_REG_RTSOFF: u64 = 0x18; +/// Capability Parameters 2. +const XHCI_CAP_REG_HCCPARAMS2: u64 = 0x1c; const XHCI_VERSION: u32 = 0x100; -/// Number of Device Slots(MaxSlots) +/// Number of Device Slots(MaxSlots). const CAP_HCSP_NDS_SHIFT: u32 = 0; -/// Number of Interrupters(MaxIntrs) +/// Number of Interrupters(MaxIntrs). const CAP_HCSP_NI_SHIFT: u32 = 8; -/// Number of Ports(MaxPorts) +/// Number of Ports(MaxPorts). const CAP_HCSP_NP_SHIFT: u32 = 24; -/// 64-bit Addressing Capability +/// 64-bit Addressing Capability. const CAP_HCCP_AC64: u32 = 0x1; -/// xHCI Extended Capabilities Pointer +/// xHCI Extended Capabilities Pointer. const CAP_HCCP_EXCP_SHIFT: u32 = 16; -/// Maximum Primary Stream Array Size +/// Maximum Primary Stream Array Size. const CAP_HCCP_MPSAS_SHIFT: u32 = 12; -/// Extended Capability Code (Supported Protocol) +/// Extended Capability Code (Supported Protocol). const CAP_EXT_CAP_ID_SUPPORT_PROTOCOL: u8 = 2; -/// xHCI Supported Protocol Capability (Name String) +/// xHCI Supported Protocol Capability (Name String). const CAP_EXT_USB_NAME_STRING: u32 = 0x20425355; -/// Supported Protocol Capability (Major Revision and Minor Revision) +/// Supported Protocol Capability (Major Revision and Minor Revision). const CAP_EXT_REVISION_SHIFT: u32 = 16; -/// Next xHCI Extended Capability Pointer +/// Next xHCI Extended Capability Pointer. const CAP_EXT_NEXT_CAP_POINTER_SHIFT: u32 = 8; -/// USB 2.0 +/// USB 2.0. const CAP_EXT_USB_REVISION_2_0: u32 = 0x0200; -/// USB 3.0 +/// USB 3.0. const CAP_EXT_USB_REVISION_3_0: u32 = 0x0300; -/// Operational Registers +/// Operational Registers. const XHCI_OPER_REG_USBCMD: u64 = 0x00; const XHCI_OPER_REG_USBSTS: u64 = 0x04; const XHCI_OPER_REG_PAGESIZE: u64 = 0x08; @@ -81,7 +83,13 @@ const XHCI_OPER_REG_DCBAAP_LO: u64 = 0x30; const XHCI_OPER_REG_DCBAAP_HI: u64 = 0x34; const XHCI_OPER_REG_CONFIG: u64 = 0x38; const XHCI_OPER_PAGESIZE: u32 = 1; -/// Interrupter Registers +/// Command Ring Control Register RCS/CS/CA/CRR mask. +const XHCI_CRCR_CTRL_LO_MASK: u32 = 0xffffffc7; +/// Command Ring Pointer Mask. +const XHCI_CRCR_CRP_MASK: u64 = !0x3f; +/// Notification Enable. +const XHCI_OPER_NE_MASK: u32 = 0xffff; +/// Interrupter Registers. const XHCI_INTR_REG_IMAN: u64 = 0x00; const XHCI_INTR_REG_IMOD: u64 = 0x04; const XHCI_INTR_REG_ERSTSZ: u64 = 0x08; @@ -90,10 +98,10 @@ const XHCI_INTR_REG_ERSTBA_HI: u64 = 0x14; const XHCI_INTR_REG_ERDP_LO: u64 = 0x18; const XHCI_INTR_REG_ERDP_HI: u64 = 0x1c; const XHCI_INTR_REG_SIZE: u64 = 0x20; -/// Doorbell Register Bit Field -/// DB Target +/// Doorbell Register Bit Field. +/// DB Target. const DB_TARGET_MASK: u32 = 0xff; -/// Port Registers +/// Port Registers. const XHCI_PORTSC: u64 = 0x0; const XHCI_PORTPMSC: u64 = 0x4; const XHCI_PORTLI: u64 = 0x8; @@ -267,6 +275,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { } XHCI_CAP_REG_DBOFF => XHCI_OFF_DOORBELL, XHCI_CAP_REG_RTSOFF => XHCI_OFF_RUNTIME, + XHCI_CAP_REG_HCCPARAMS2 => 0, // Extended capabilities (USB 2.0) 0x20 => { CAP_EXT_USB_REVISION_2_0 << CAP_EXT_REVISION_SHIFT @@ -321,8 +330,16 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_OPER_REG_USBSTS => locked_xhci.oper.usb_status, XHCI_OPER_REG_PAGESIZE => XHCI_OPER_PAGESIZE, XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl, - XHCI_OPER_REG_CMD_RING_CTRL_LO => read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & !0xe, - XHCI_OPER_REG_CMD_RING_CTRL_HI => read_u32(locked_xhci.oper.cmd_ring_ctrl, 1), + XHCI_OPER_REG_CMD_RING_CTRL_LO => { + // 5.4.5 Command Ring Control Register + // Table 5-24 shows read RCS CS CA always returns 0. + read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & CMD_RING_CTRL_CRR + } + XHCI_OPER_REG_CMD_RING_CTRL_HI => { + // 5.4.5 Command Ring Control Register + // Table 5-24 shows read CRP always returns 0. + 0 + } XHCI_OPER_REG_DCBAAP_LO => read_u32(locked_xhci.oper.dcbaap, 0), XHCI_OPER_REG_DCBAAP_HI => read_u32(locked_xhci.oper.dcbaap, 1), XHCI_OPER_REG_CONFIG => locked_xhci.oper.config, @@ -363,6 +380,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { { locked_xhci.stop(); } + // Not support Save/Restore, report the Save/Restore Error. if value & USB_CMD_CSS == USB_CMD_CSS { locked_xhci.oper.usb_status &= !USB_STS_SRE; } @@ -370,30 +388,28 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { locked_xhci.oper.usb_status |= USB_STS_SRE; } locked_xhci.oper.usb_cmd = value & 0xc0f; - locked_xhci.update_mf(); if value & USB_CMD_HCRST == USB_CMD_HCRST { locked_xhci.reset(); } locked_xhci.update_intr(0); } XHCI_OPER_REG_USBSTS => { + // Write 1 to clear. locked_xhci.oper.usb_status &= !(value & (USB_STS_HSE | USB_STS_EINT | USB_STS_PCD | USB_STS_SRE)); locked_xhci.update_intr(0); } - XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl = value & 0xffff, + XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl = value & XHCI_OPER_NE_MASK, XHCI_OPER_REG_CMD_RING_CTRL_LO => { - let lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & CMD_RING_CTRL_CRR; - locked_xhci.oper.cmd_ring_ctrl = - write_u64_low(locked_xhci.oper.cmd_ring_ctrl, (value & 0xffffffcf) | lo); - } - XHCI_OPER_REG_CMD_RING_CTRL_HI => { - locked_xhci.oper.cmd_ring_ctrl = - write_u64_high(locked_xhci.oper.cmd_ring_ctrl, value); - let mut crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); + let mut crc_lo = value; + if crc_lo & CMD_RING_CTRL_CRR != CMD_RING_CTRL_CRR { + locked_xhci + .cmd_ring + .set_cycle_bit(crc_lo & CMD_RING_CTRL_RCS == CMD_RING_CTRL_RCS); + } if crc_lo & (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) == (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) - && (crc_lo & CMD_RING_CTRL_CRR) == CMD_RING_CTRL_CRR + && (value & CMD_RING_CTRL_CRR) == CMD_RING_CTRL_CRR { let event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::CommandRingStopped); @@ -403,15 +419,22 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { if let Err(e) = locked_xhci.send_event(&event, 0) { error!("Failed to send event: {:?}", e); } - } else { - let crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0) & !0x3f; - let crc_hi = ((value << 16) as u64) << 16; - let addr = crc_hi | crc_lo as u64; - locked_xhci.cmd_ring.init(addr); } crc_lo &= !(CMD_RING_CTRL_CA | CMD_RING_CTRL_CS); - locked_xhci.oper.cmd_ring_ctrl = - write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); + locked_xhci.oper.cmd_ring_ctrl = write_u64_low( + locked_xhci.oper.cmd_ring_ctrl, + crc_lo & XHCI_CRCR_CTRL_LO_MASK, + ); + } + XHCI_OPER_REG_CMD_RING_CTRL_HI => { + let crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); + if (crc_lo & CMD_RING_CTRL_CRR) != CMD_RING_CTRL_CRR { + let crc_hi = (value as u64) << 32; + let addr = (crc_hi | crc_lo as u64) & XHCI_CRCR_CRP_MASK; + locked_xhci.cmd_ring.init(addr); + locked_xhci.oper.cmd_ring_ctrl = + write_u64_high(locked_xhci.oper.cmd_ring_ctrl, value); + } } XHCI_OPER_REG_DCBAAP_LO => { locked_xhci.oper.dcbaap = write_u64_low(locked_xhci.oper.dcbaap, value & 0xffffffc0) @@ -450,9 +473,13 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { error!("Failed to read runtime registers, offset is {:x}", offset); } } else { - let idx = (offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE; + let idx = ((offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE) as usize; let mut xhci = xhci.lock().unwrap(); - let intr = &mut xhci.intrs[idx as usize]; + if idx >= xhci.intrs.len() { + error!("Invalid interrupter index: {} idx {}", offset, idx); + return false; + } + let intr = &mut xhci.intrs[idx]; value = match offset & 0x1f { XHCI_INTR_REG_IMAN => intr.iman, XHCI_INTR_REG_IMOD => intr.imod, @@ -488,13 +515,14 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { }; debug!("runtime write {:x} {:x} {:x}", addr.0, offset, value); if offset < 0x20 { - error!("runtime write not implemented: offset {}", offset); + error!("Runtime write not implemented: offset {}", offset); + return false; } let mut xhci = xhci.lock().unwrap(); let idx = ((offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE) as u32; - if idx > xhci.intrs.len() as u32 { + if idx >= xhci.intrs.len() as u32 { error!("Invalid interrupter index: {} idx {}", offset, idx); - return true; + return false; } let intr = &mut xhci.intrs[idx as usize]; match offset & 0x1f { @@ -504,9 +532,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } intr.iman &= !IMAN_IE; intr.iman |= value & IMAN_IE; - if idx == 0 { - xhci.update_intr(idx); - } + xhci.update_intr(idx); } XHCI_INTR_REG_IMOD => intr.imod = value, XHCI_INTR_REG_ERSTSZ => intr.erstsz = value & 0xffff, @@ -520,14 +546,15 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } } XHCI_INTR_REG_ERDP_LO => { - let mut erdp_lo = read_u32(intr.erdp, 0); - if value & ERDP_EHB == ERDP_EHB { - erdp_lo &= !ERDP_EHB; + // ERDP_EHB is write 1 clear. + let mut erdp_lo = value & !ERDP_EHB; + if value & ERDP_EHB != ERDP_EHB { + let erdp_old = read_u32(intr.erdp, 0); + erdp_lo |= erdp_old & ERDP_EHB; } - erdp_lo = (value & !ERDP_EHB) | (erdp_lo & ERDP_EHB); intr.erdp = write_u64_low(intr.erdp, erdp_lo); if value & ERDP_EHB == ERDP_EHB { - let erdp = intr.erdp & 0x0000_FFFF_FFFF_FFFF_u64; + let erdp = intr.erdp; let dp_idx = (erdp - intr.er_start) / TRB_SIZE as u64; if erdp >= intr.er_start && erdp < intr.er_start + (TRB_SIZE * intr.er_size) as u64 diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 0d82832c3..8da70e0ff 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -224,6 +224,10 @@ impl XhciRing { self.ccs = true; } + pub fn set_cycle_bit(&mut self, v: bool) { + self.ccs = v; + } + /// Fetch TRB from the ring. pub fn fetch_trb(&mut self) -> Result { let mut link_cnt = 0; -- Gitee From 92c9a565bc573bdac26c1068be472605431716d9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 5 Nov 2022 11:43:43 +0800 Subject: [PATCH 0342/2187] xhci: refactor the port register write ops Refactor the port register write ops to split port link state write. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 2 +- usb/src/xhci/xhci_regs.rs | 161 +++++++++++++++----------------- 2 files changed, 78 insertions(+), 85 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 4729c650e..eecb92613 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -436,7 +436,7 @@ impl XhciDevice { locked_port.portsc |= PORTSC_WRC; } match speed { - USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH => { + USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH | USB_SPEED_SUPER => { locked_port.portsc = set_field( locked_port.portsc, PLS_U0, diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index bc455c5fe..60d2b5b2c 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -649,7 +649,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { XHCI_PORTHLPMC => 0, _ => { error!("Faield to read port register: offset {:x}", offset); - 0 + return false; } }; if let Err(e) = write_data(data, value) { @@ -668,91 +668,21 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { return false; } }; - let locked_port = port.lock().unwrap(); - debug!( - "port write {} {:x} {:x} {:x}", - locked_port.name, addr.0, offset, value - ); - let xhci = locked_port.xhci.upgrade().unwrap(); - drop(locked_port); - // Lock controller first. - let mut locked_xhci = xhci.lock().unwrap(); - #[allow(clippy::never_loop)] - loop { - match offset { - XHCI_PORTSC => { - if value & PORTSC_WPR == PORTSC_WPR { - if let Err(e) = locked_xhci.reset_port(&port, true) { - error!("Failed to warn reset port {:?}", e); - } - break; - } - if value & PORTSC_PR == PORTSC_PR { - if let Err(e) = locked_xhci.reset_port(&port, false) { - error!("Failed to reset port {:?}", e); - } - break; - } - let mut locked_port = port.lock().unwrap(); - let mut portsc = locked_port.portsc; - let mut notify = 0; - portsc &= !(value - & (PORTSC_CSC - | PORTSC_PEC - | PORTSC_WRC - | PORTSC_OCC - | PORTSC_PRC - | PORTSC_PLC - | PORTSC_CEC)); - if value & PORTSC_LWS == PORTSC_LWS { - let old_pls = (locked_port.portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; - let new_pls = (value >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; - match new_pls { - PLS_U0 => { - if old_pls != PLS_U0 { - portsc = set_field( - portsc, - new_pls, - PORTSC_PLS_MASK, - PORTSC_PLS_SHIFT, - ); - notify = PORTSC_PLC; - } - } - PLS_U3 => { - if old_pls < PLS_U3 { - portsc = set_field( - portsc, - new_pls, - PORTSC_PLS_MASK, - PORTSC_PLS_SHIFT, - ); - } - } - PLS_RESUME => {} - _ => { - error!("Invalid port link state, ignore the write."); - } - } - } - portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); - portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); - locked_port.portsc = portsc; - drop(locked_port); - if notify != 0 { - if let Err(e) = locked_xhci.port_notify(&port, notify) { - error!("Failed to notify: {:?}", e); - } - } - } - XHCI_PORTPMSC => (), - XHCI_PORTLI => (), - XHCI_PORTHLPMC => (), - _ => { - error!("Invalid port link state offset {}", offset); + debug!("port write {:x} {:x} {:x}", addr.0, offset, value); + match offset { + XHCI_PORTSC => { + if let Err(e) = xhci_portsc_write(&port, value) { + error!("Failed to write portsc register, {:?}", e); + return false; } } - break; + XHCI_PORTPMSC => (), + XHCI_PORTLI => (), + XHCI_PORTHLPMC => (), + _ => { + error!("Invalid port link state offset {}", offset); + return false; + } } true }; @@ -763,6 +693,69 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { } } +fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { + let locked_port = port.lock().unwrap(); + let xhci = locked_port.xhci.upgrade().unwrap(); + drop(locked_port); + // Lock controller first. + let mut locked_xhci = xhci.lock().unwrap(); + if value & PORTSC_WPR == PORTSC_WPR { + return locked_xhci.reset_port(port, true); + } + if value & PORTSC_PR == PORTSC_PR { + return locked_xhci.reset_port(port, false); + } + let mut locked_port = port.lock().unwrap(); + let mut portsc = locked_port.portsc; + let mut notify = 0; + // Write 1 to clear. + portsc &= !(value + & (PORTSC_CSC + | PORTSC_PEC + | PORTSC_WRC + | PORTSC_OCC + | PORTSC_PRC + | PORTSC_PLC + | PORTSC_CEC)); + if value & PORTSC_LWS == PORTSC_LWS { + let old_pls = (locked_port.portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + let new_pls = (value >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + notify = xhci_portsc_ls_write(&mut portsc, old_pls, new_pls); + } + portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); + portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); + locked_port.portsc = portsc; + drop(locked_port); + if notify != 0 { + locked_xhci.port_notify(port, notify)?; + } + Ok(()) +} + +fn xhci_portsc_ls_write(portsc: &mut u32, old_pls: u32, new_pls: u32) -> u32 { + match new_pls { + PLS_U0 => { + if old_pls != PLS_U0 { + *portsc = set_field(*portsc, new_pls, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + return PORTSC_PLC; + } + } + PLS_U3 => { + if old_pls < PLS_U3 { + *portsc = set_field(*portsc, new_pls, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + } + } + PLS_RESUME => {} + _ => { + error!( + "Unhandled port link state, ignore the write. old {:x} new {:x}", + old_pls, new_pls + ); + } + } + 0 +} + fn write_data(data: &mut [u8], value: u32) -> Result<()> { match data.len() { 1 => data[0] = value as u8, -- Gitee From ab4416bea20f2986f3095d25e0a755062f9af202 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 7 Nov 2022 20:25:25 +0800 Subject: [PATCH 0343/2187] xhci: set NFC=1 because it is not supported Set NFC=1 for the Frame Length Timing Value is not supported according to the spec 5.2.4. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_pci.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index f50d6ccfe..4b0fccb56 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -38,8 +38,10 @@ const PCI_CLASS_PI: u16 = 0x09; const PCI_INTERRUPT_PIN: u16 = 0x3d; const PCI_CACHE_LINE_SIZE: u16 = 0x0c; const PCI_SERIAL_BUS_RELEASE_NUMBER: u8 = 0x60; +const PCI_FRAME_LENGTH_ADJUSTMENT: u8 = 0x61; const PCI_SERIAL_BUS_RELEASE_VERSION_3_0: u8 = 0x30; const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; +const PCI_NO_FRAME_LENGTH_TIMING_CAP: u8 = 0x40; /// PCI capability offset or size. const XHCI_PCI_CONFIG_LENGTH: u32 = 0x4000; const XHCI_PCI_CAP_OFFSET: u32 = 0x0; @@ -177,6 +179,8 @@ impl PciDevOps for XhciPciDevice { self.pci_config.config[PCI_CACHE_LINE_SIZE as usize] = 0x10; self.pci_config.config[PCI_SERIAL_BUS_RELEASE_NUMBER as usize] = PCI_SERIAL_BUS_RELEASE_VERSION_3_0; + self.pci_config.config[PCI_FRAME_LENGTH_ADJUSTMENT as usize] = + PCI_NO_FRAME_LENGTH_TIMING_CAP; self.dev_id.store(self.devfn as u16, Ordering::SeqCst); self.mem_region_init()?; -- Gitee From 75edf05b3f387b41c02abf47e4addbdd7d3969e6 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:00:23 -0500 Subject: [PATCH 0344/2187] virtiofs: Limit the length of sock path to 108 Unix platforms limit socket path characters to 108 instead of 255. Signed-off-by: Li HuaChao --- vhost_user_fs/src/cmdline.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 48fae02cf..891c85b17 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -13,6 +13,7 @@ // Read the programe version in `Cargo.toml`. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const MAX_STRING_LENGTH: usize = 255; +const MAX_SOCK_PATH_LENGTH: usize = 108; use anyhow::{bail, Context, Result}; use std::path::PathBuf; @@ -77,7 +78,7 @@ impl FsConfig { ); } - if self.sock_path.len() > MAX_STRING_LENGTH { + if self.sock_path.len() > MAX_SOCK_PATH_LENGTH { bail!( "The length of socket file path is too long {}", self.sock_path.len() -- Gitee From 0f8dd4fc3ba214a05ced78982bf5d80652cd6b0a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:26:01 -0500 Subject: [PATCH 0345/2187] virtiofs: Add a newline character to vhost_user_fs process stderr Signed-off-by: Li HuaChao --- vhost_user_fs/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 7c45e797d..cb0d6f158 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -63,7 +63,7 @@ fn main() { ::std::process::exit(match run() { Ok(ret) => ExitCode::code(ret), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format!("{:?}", e)) + write!(&mut ::std::io::stderr(), "{}", format!("{:?}\r\n", e)) .expect("Error writing to stderr"); 1 -- Gitee From a0bb71af6d048731b7eadbf285b5b252db549658 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:45:57 -0500 Subject: [PATCH 0346/2187] virtiofs: Add more verification to the shared directory Add more verification to the shared directory. We need to verify presence and permissions. Signed-off-by: Li HuaChao --- vhost_user_fs/src/cmdline.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 891c85b17..47ec60618 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -16,7 +16,7 @@ const MAX_STRING_LENGTH: usize = 255; const MAX_SOCK_PATH_LENGTH: usize = 108; use anyhow::{bail, Context, Result}; -use std::path::PathBuf; +use std::{fs, path::PathBuf}; use util::arg_parser::{Arg, ArgMatches, ArgParser}; /// This function is to define all command line arguments. @@ -85,10 +85,13 @@ impl FsConfig { ); } + if fs::metadata(&self.source_dir).is_err() { + bail!("Failed to stat source directory {}", self.source_dir); + } let source_dir = PathBuf::from(&self.source_dir); if !source_dir.is_dir() { bail!( - "The source directory is not a directory {}", + "The source directory {} is not a directory", self.source_dir ); } -- Gitee From 4bf6fb59f691869bfa70334d7f1ac5e17f7aeed9 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 02:58:38 -0500 Subject: [PATCH 0347/2187] virtiofs: Add verification to the socket path Signed-off-by: Li HuaChao --- vhost_user_fs/src/cmdline.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 47ec60618..2d09dfdb4 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -13,6 +13,7 @@ // Read the programe version in `Cargo.toml`. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const MAX_STRING_LENGTH: usize = 255; +// Maximum length of the socket path is restricted by linux. const MAX_SOCK_PATH_LENGTH: usize = 108; use anyhow::{bail, Context, Result}; @@ -96,6 +97,11 @@ impl FsConfig { ); } + let sock_path = PathBuf::from(&self.sock_path); + if !sock_path.is_file() { + bail!("The socket path {} is not a file", self.sock_path); + } + Ok(()) } } -- Gitee From c19a908f2b8c4970df17e8fce42e843684f4f03f Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 8 Nov 2022 03:04:22 -0500 Subject: [PATCH 0348/2187] virtiofs: Change the character limit of a shared directory to 4096 characters Signed-off-by: Li HuaChao --- vhost_user_fs/src/cmdline.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 2d09dfdb4..4d58735ab 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -12,7 +12,7 @@ // Read the programe version in `Cargo.toml`. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); -const MAX_STRING_LENGTH: usize = 255; +const MAX_PATH_LENGTH: usize = 4096; // Maximum length of the socket path is restricted by linux. const MAX_SOCK_PATH_LENGTH: usize = 108; @@ -72,7 +72,7 @@ pub struct FsConfig { impl FsConfig { fn check_config(&self) -> Result<()> { - if self.source_dir.len() > MAX_STRING_LENGTH { + if self.source_dir.len() > MAX_PATH_LENGTH { bail!( "The length of source directory is too long {}", self.source_dir.len() -- Gitee From a676580270a2977945ee5ebb01127e54a5792e26 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 11 Nov 2022 15:55:10 +0800 Subject: [PATCH 0349/2187] machine_manager: add usb device in list type Add usb device in list type, which is used by libvirt. Signed-off-by: zhouli57 --- machine_manager/src/machine.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index a73a85133..865edb70e 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -293,6 +293,9 @@ pub trait DeviceInterface { ("iothread", "object"), #[cfg(target_arch = "aarch64")] ("gpex-pcihost", "pcie-host-bridge"), + ("nec-usb-xhci", "base-xhci"), + ("usb-tablet", "usb-hid"), + ("usb-kbd", "usb-hid"), ]; for list in list_types { -- Gitee From 7bb3440028e8a13582f251f85f8faf59085b42ff Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 5 Nov 2022 16:56:19 +0800 Subject: [PATCH 0350/2187] virtio_pci: Add pci configuration access capability As described by virtio 1.2 spec: This pci capability creates an alternative (and likely suboptimal) access method to the common configuration, notification, ISR and device-specific configuration regions. Signed-off-by: Keqian Zhu --- .../src/standard_vm/aarch64/pci_host_root.rs | 2 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 2 +- machine/src/standard_vm/x86_64/mch.rs | 2 +- pci/src/bus.rs | 2 +- pci/src/host.rs | 2 +- pci/src/lib.rs | 4 +- pci/src/root_port.rs | 2 +- usb/src/xhci/xhci_pci.rs | 2 +- vfio/src/vfio_pci.rs | 2 +- virtio/src/virtio_pci.rs | 122 ++++++++++++++++-- 10 files changed, 123 insertions(+), 19 deletions(-) diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 8509fdc99..4d118c62a 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -79,7 +79,7 @@ impl PciDevOps for PciHostRoot { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); if size > 4 { error!( diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 4faca0926..ae5fcb4c7 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -263,7 +263,7 @@ impl PciDevOps for LPCBridge { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); if offset + size > PCI_CONFIG_SPACE_SIZE || size > 4 { debug!( diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index aca68db5c..db69c0c6c 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -138,7 +138,7 @@ impl PciDevOps for Mch { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); if size > 4 { error!( diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 7e48416ef..2e7872fb0 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -251,7 +251,7 @@ mod tests { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { self.config.read(offset, data); } diff --git a/pci/src/host.rs b/pci/src/host.rs index 3400cb4a2..f146f8734 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -510,7 +510,7 @@ pub mod tests { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { self.config.read(offset, data); } diff --git a/pci/src/lib.rs b/pci/src/lib.rs index d49c8d0c9..744a5f623 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -139,7 +139,7 @@ pub trait PciDevOps: Send { /// /// * `offset` - Offset in configuration space. /// * `data` - Data buffer for reading. - fn read_config(&self, offset: usize, data: &mut [u8]); + fn read_config(&mut self, offset: usize, data: &mut [u8]); /// Configuration space write. /// @@ -376,7 +376,7 @@ mod tests { Ok(()) } - fn read_config(&self, _offset: usize, _data: &mut [u8]) {} + fn read_config(&mut self, _offset: usize, _data: &mut [u8]) {} fn write_config(&mut self, _offset: usize, _data: &[u8]) {} diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 6363c3a38..6948e3749 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -348,7 +348,7 @@ impl PciDevOps for RootPort { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); if offset + size > PCIE_CONFIG_SPACE_SIZE || size > 4 { error!( diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 4b0fccb56..9239c8f97 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -239,7 +239,7 @@ impl PciDevOps for XhciPciDevice { Some(self.devfn) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let data_size = data.len(); if offset + data_size > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { error!( diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 928100d5a..8c70569dc 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -886,7 +886,7 @@ impl PciDevOps for VfioPciDevice { } /// Read pci data from pci config if it emulate, otherwise read from vfio device. - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); let end = offset + size; if end > (self.config_size as usize) || size > 4 { diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index e18d16a4d..9d56d2101 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -24,17 +24,18 @@ use log::{error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use pci::config::{ - RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, - REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, - SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, + RegionType, BAR_0, BAR_SPACE_UNMAPPED, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, + PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, + SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::{update_dev_id, Message, MsixState, MsixUpdate}; use pci::Result as PciResult; use pci::{ - config::PciConfig, init_msix, init_multifunction, le_write_u16, ranges_overlap, PciBus, - PciDevOps, PciError, + config::PciConfig, init_msix, init_multifunction, le_write_u16, le_write_u32, ranges_overlap, + PciBus, PciDevOps, PciError, }; use util::byte_code::ByteCode; +use util::offset_of; use vmm_sys_util::eventfd::EventFd; use crate::{ @@ -409,6 +410,7 @@ enum VirtioPciCapType { Notify = 2, ISR = 3, Device = 4, + CfgAccess = 5, } /// Virtio PCI Capability @@ -444,6 +446,27 @@ impl VirtioPciCap { } } +/// The struct of virtio pci capability for accessing BAR regions. +#[repr(C, packed)] +#[derive(Debug, Copy, Clone, Default)] +struct VirtioPciCfgAccessCap { + /// The struct of virtio pci capability. + cap: VirtioPciCap, + /// Data for BAR regions access. + pci_cfg_data: [u8; 4], +} + +impl ByteCode for VirtioPciCfgAccessCap {} + +impl VirtioPciCfgAccessCap { + fn new(cap_len: u8, cfg_type: u8) -> Self { + VirtioPciCfgAccessCap { + cap: VirtioPciCap::new(cap_len, cfg_type, 0, 0, 0), + pci_cfg_data: [0; 4], + } + } +} + /// The struct of virtio pci capability for notifying the host #[repr(C, packed)] #[derive(Debug, Copy, Clone, Default)] @@ -548,6 +571,8 @@ pub struct VirtioPciDevice { sys_mem: Arc, /// Pci config space. config: PciConfig, + /// Offset of VirtioPciCfgAccessCap in Pci config space. + cfg_cap_offset: usize, /// Virtio common config refer to Virtio Spec. common_config: Arc>, /// Primary Bus @@ -586,6 +611,7 @@ impl VirtioPciDevice { device_activated: Arc::new(AtomicBool::new(false)), sys_mem, config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), + cfg_cap_offset: 0, common_config: Arc::new(Mutex::new(VirtioPciCommonConfig::new( queue_size, queue_num, ))), @@ -668,7 +694,7 @@ impl VirtioPciDevice { ret } - fn modern_mem_region_map(&mut self, data: T) -> PciResult<()> { + fn modern_mem_region_map(&mut self, data: T) -> PciResult { let cap_offset = self.config.add_pci_cap( PCI_CAP_ID_VNDR, size_of::() + PCI_CAP_VNDR_AND_NEXT_SIZE as usize, @@ -678,7 +704,7 @@ impl VirtioPciDevice { self.config.config[write_start..(write_start + size_of::())] .copy_from_slice(data.as_bytes()); - Ok(()) + Ok(write_start) } fn build_common_cfg_ops(&mut self) -> RegionOps { @@ -987,6 +1013,62 @@ impl VirtioPciDevice { Ok(()) } + + // Access virtio configuration through VirtioPciCfgAccessCap. + fn do_cfg_access(&mut self, start: usize, end: usize, is_write: bool) { + let pci_cfg_data_offset = + self.cfg_cap_offset + offset_of!(VirtioPciCfgAccessCap, pci_cfg_data); + let pci_cfg_data_end = self.cfg_cap_offset + size_of::(); + if !ranges_overlap(start, end, pci_cfg_data_offset, pci_cfg_data_end) { + return; + } + + let config = &self.config.config[self.cfg_cap_offset..]; + let bar = config[offset_of!(VirtioPciCap, bar_id)]; + let off = LittleEndian::read_u32(&config[offset_of!(VirtioPciCap, offset)..]); + let len = LittleEndian::read_u32(&config[offset_of!(VirtioPciCap, length)..]); + if bar >= VIRTIO_PCI_BAR_MAX { + warn!("The bar_id {} of VirtioPciCfgAccessCap exceeds max", bar); + return; + } + let bar_base = self.config.get_bar_address(bar as usize); + if bar_base == BAR_SPACE_UNMAPPED { + warn!("The bar {} of VirtioPciCfgAccessCap is not mapped", bar); + return; + } + if ![1, 2, 4].contains(&len) { + warn!("The length {} of VirtioPciCfgAccessCap is illegal", len); + return; + } + if off & (len - 1) != 0 { + warn!("The offset {} of VirtioPciCfgAccessCap is not aligned", off); + return; + } + if (off as u64) + .checked_add(len as u64) + .filter(|&end| end <= self.config.bars[bar as usize].size) + .is_none() + { + warn!("The access range of VirtioPciCfgAccessCap exceeds bar size"); + return; + } + + let result = if is_write { + let mut data = self.config.config[pci_cfg_data_offset..].as_ref(); + self.sys_mem + .write(&mut data, GuestAddress(bar_base + off as u64), len as u64) + } else { + let mut data = self.config.config[pci_cfg_data_offset..].as_mut(); + self.sys_mem + .read(&mut data, GuestAddress(bar_base + off as u64), len as u64) + }; + if let Err(e) = result { + error!( + "Failed to access virtio configuration through VirtioPciCfgAccessCap. {:?}", + e + ); + } + } } impl PciDevOps for VirtioPciDevice { @@ -1070,6 +1152,23 @@ impl PciDevOps for VirtioPciDevice { ); self.modern_mem_region_map(notify_cap)?; + let cfg_cap = VirtioPciCfgAccessCap::new( + size_of::() as u8 + PCI_CAP_VNDR_AND_NEXT_SIZE, + VirtioPciCapType::CfgAccess as u8, + ); + self.cfg_cap_offset = self.modern_mem_region_map(cfg_cap)?; + + // Make related fields of PCI config writable for VirtioPciCfgAccessCap. + let write_mask = &mut self.config.write_mask[self.cfg_cap_offset..]; + write_mask[offset_of!(VirtioPciCap, bar_id)] = !0; + le_write_u32(write_mask, offset_of!(VirtioPciCap, offset), !0)?; + le_write_u32(write_mask, offset_of!(VirtioPciCap, length), !0)?; + le_write_u32( + write_mask, + offset_of!(VirtioPciCfgAccessCap, pci_cfg_data), + !0, + )?; + let nvectors = self.device.lock().unwrap().queue_num() + 1; init_msix( @@ -1148,9 +1247,10 @@ impl PciDevOps for VirtioPciDevice { Ok(()) } - fn read_config(&self, offset: usize, data: &mut [u8]) { + fn read_config(&mut self, offset: usize, data: &mut [u8]) { let data_size = data.len(); - if offset + data_size > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { + let end = offset + data_size; + if end > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { error!( "Failed to read pcie config space at offset 0x{:x} with data size {}", offset, data_size @@ -1158,6 +1258,7 @@ impl PciDevOps for VirtioPciDevice { return; } + self.do_cfg_access(offset, end, false); self.config.read(offset, data); } @@ -1190,8 +1291,11 @@ impl PciDevOps for VirtioPciDevice { &locked_parent_bus.mem_region, ) { error!("Failed to update bar, error is {:?}", e); + return; } } + + self.do_cfg_access(offset, end, true); } fn name(&self) -> String { -- Gitee From 9068b783d50b46229e83e8977ff9627bc0d1ebd7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 6 Nov 2022 05:39:08 +0800 Subject: [PATCH 0351/2187] virtio-blk: Support report virtio error During process queue, if the syntax of request is incorrect or some other issues, device will report virtio error to driver and will not go on unless driver reset device. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 121 ++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 87eb868e1..037c0b1ad 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,11 +21,12 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use super::{ - virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, - VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, + VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, + VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_BLOCK, }; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; @@ -89,21 +90,6 @@ struct RequestOutHeader { sector: u64, } -impl RequestOutHeader { - fn is_valid(&self) -> bool { - match self.request_type { - VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_FLUSH | VIRTIO_BLK_T_GET_ID => true, - _ => { - error!( - "request type {} is not supported for block", - self.request_type - ); - false - } - } - } -} - impl ByteCode for RequestOutHeader {} #[derive(Clone)] @@ -192,7 +178,7 @@ struct Request { } impl Request { - fn new(mem_space: &Arc, elem: &Element) -> Result { + fn new(handler: &BlockIoHandler, elem: &Element, status: &mut u8) -> Result { if elem.out_iovec.is_empty() || elem.in_iovec.is_empty() || elem.desc_num < 2 { bail!( "Missed header for block request: out {} in {} desc num {}", @@ -210,7 +196,8 @@ impl Request { ); } - let out_header = mem_space + let out_header = handler + .mem_space .read_object::(out_iov_elem.addr) .with_context(|| { anyhow!(VirtioError::ReadObjectErr( @@ -223,7 +210,7 @@ impl Request { let in_iov_elem = elem.in_iovec.get(pos).unwrap(); if in_iov_elem.len < 1 { bail!( - "Invalid out header for block request: length {}", + "Invalid in header for block request: length {}", in_iov_elem.len ); } @@ -244,13 +231,15 @@ impl Request { if index == elem.in_iovec.len() - 1 { break; } - if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { + if let Some(hva) = handler.mem_space.get_host_address(elem_iov.addr) { let iov = Iovec { iov_base: hva, iov_len: u64::from(elem_iov.len), }; request.iovec.push(iov); request.data_len += u64::from(elem_iov.len); + } else { + bail!("Map desc base {:?} failed", elem_iov.addr); } } } @@ -259,17 +248,27 @@ impl Request { if index == 0 { continue; } - if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { + if let Some(hva) = handler.mem_space.get_host_address(elem_iov.addr) { let iov = Iovec { iov_base: hva, iov_len: u64::from(elem_iov.len), }; request.iovec.push(iov); request.data_len += u64::from(elem_iov.len); + } else { + bail!("Map desc base {:?} failed", elem_iov.addr); } } } - _ => (), + VIRTIO_BLK_T_FLUSH => (), + others => { + error!("Request type {} is not supported for block", others); + *status = VIRTIO_BLK_S_UNSUPP; + } + } + + if !request.io_range_valid(handler.disk_sectors) { + *status = VIRTIO_BLK_S_IOERR; } // We always write the last status byte, so count all in_iovs. @@ -484,8 +483,7 @@ impl BlockIoHandler { merge_req_queue } - fn process_queue(&mut self) -> Result { - self.trace_request("Block".to_string(), "to IO".to_string()); + fn process_queue_internal(&mut self) -> Result { let mut req_queue = Vec::new(); let mut last_aio_req_index = 0; let mut done = false; @@ -496,10 +494,14 @@ impl BlockIoHandler { return Ok(done); } - while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + loop { + let elem = queue + .vring + .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { break; } + // limit io operations if iops is configured if let Some(lb) = self.leak_bucket.as_mut() { if let Some(ctx) = EventLoop::get_ctx(self.iothread.as_ref()) { @@ -515,47 +517,31 @@ impl BlockIoHandler { }; } - match Request::new(&self.mem_space, &elem) { - Ok(req) => { - if !req.out_header.is_valid() || !req.io_range_valid(self.disk_sectors) { - let status = if !req.out_header.is_valid() { - VIRTIO_BLK_S_UNSUPP - } else { - VIRTIO_BLK_S_IOERR - }; - let aiocompletecb = AioCompleteCb::new( - self.queue.clone(), - self.mem_space.clone(), - Rc::new(req), - Some(self.interrupt_cb.clone()), - self.driver_features, - ); - aiocompletecb.complete_request(status); - continue; - } - req_queue.push(req); - done = true; - } - Err(ref e) => { - // If it fails, also need to free descriptor table entry. - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; - error!("failed to create block request, {:?}", e); - } - }; + // Init and put valid request into request queue. + let mut status = VIRTIO_BLK_S_OK; + let req = Request::new(self, &elem, &mut status)?; + if status != VIRTIO_BLK_S_OK { + let aiocompletecb = AioCompleteCb::new( + self.queue.clone(), + self.mem_space.clone(), + Rc::new(req), + Some(self.interrupt_cb.clone()), + self.driver_features, + ); + aiocompletecb.complete_request(status); + continue; + } + req_queue.push(req); + done = true; } // unlock queue, because it will be hold below. drop(queue); - if req_queue.is_empty() { return Ok(done); } let merge_req_queue = self.merge_req_queue(req_queue, &mut last_aio_req_index); - for (req_index, req) in merge_req_queue.into_iter().enumerate() { let req_rc = Rc::new(req); let aiocompletecb = AioCompleteCb::new( @@ -586,6 +572,19 @@ impl BlockIoHandler { Ok(done) } + fn process_queue(&mut self) -> Result { + self.trace_request("Block".to_string(), "to IO".to_string()); + let result = self.process_queue_internal(); + if result.is_err() { + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + Some(&self.deactivate_evt), + ); + } + result + } + fn build_aio(&self, engine: Option<&String>) -> Result>> { let complete_func = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { let mut status = if ret < 0 { -- Gitee From 0678fd46eaa7ca14bb8e95f2c2445da8f763af8e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 5 Nov 2022 18:26:50 +0800 Subject: [PATCH 0352/2187] virtio-blk: Suppress notification when process queue ... which can reduce unnecessary notification overhead. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 38 ++++++++++++--- virtio/src/queue.rs | 112 +++++++++++++++++++++++++++++++++----------- 2 files changed, 116 insertions(+), 34 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 037c0b1ad..064ff7d7b 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -487,12 +487,7 @@ impl BlockIoHandler { let mut req_queue = Vec::new(); let mut last_aio_req_index = 0; let mut done = false; - let mut queue = self.queue.lock().unwrap(); - if !queue.is_enabled() { - done = true; - return Ok(done); - } loop { let elem = queue @@ -572,9 +567,40 @@ impl BlockIoHandler { Ok(done) } + fn process_queue_suppress_notify(&mut self) -> Result { + let mut done = false; + if !self.queue.lock().unwrap().is_enabled() { + done = true; + return Ok(done); + } + while self + .queue + .lock() + .unwrap() + .vring + .avail_ring_len(&self.mem_space)? + != 0 + { + self.queue.lock().unwrap().vring.suppress_queue_notify( + &self.mem_space, + self.driver_features, + true, + )?; + + done = self.process_queue_internal()?; + + self.queue.lock().unwrap().vring.suppress_queue_notify( + &self.mem_space, + self.driver_features, + false, + )?; + } + Ok(done) + } + fn process_queue(&mut self) -> Result { self.trace_request("Block".to_string(), "to IO".to_string()); - let result = self.process_queue_internal(); + let result = self.process_queue_suppress_notify(); if result.is_err() { report_virtio_error( self.interrupt_cb.clone(), diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 2c3f641d0..27c8658d2 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -26,6 +26,8 @@ use anyhow::{anyhow, bail, Context, Result}; /// When host consumes a buffer, don't interrupt the guest. const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; +/// When guest produces a buffer, don't notify the host. +const VRING_USED_F_NO_NOTIFY: u16 = 1; /// Split Virtqueue. pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; /// Packed Virtqueue. @@ -194,7 +196,21 @@ pub trait VringOps { /// /// * `sys_mem` - Address space to which the vring belongs. /// * `features` - Bit mask of features negotiated by the backend and the frontend. - fn should_notify(&mut self, system_space: &Arc, features: u64) -> bool; + fn should_notify(&mut self, sys_mem: &Arc, features: u64) -> bool; + + /// Give guest a hint to suppress virtqueue notification. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + /// * `features` - Bit mask of features negotiated by the backend and the frontend. + /// * `suppress` - Suppress virtqueue notification or not. + fn suppress_queue_notify( + &mut self, + sys_mem: &Arc, + features: u64, + suppress: bool, + ) -> Result<()>; /// Get the actual size of the vring. fn actual_size(&self) -> u16; @@ -554,52 +570,76 @@ impl SplitVring { min(self.size, self.max_size) } - /// Get the index of the available ring from guest memory. - fn get_avail_idx(&self, sys_mem: &Arc) -> Result { - let avail_flags_idx = sys_mem + /// Get the flags and idx of the available ring from guest memory. + fn get_avail_flags_idx(&self, sys_mem: &Arc) -> Result { + sys_mem .read_object_direct::(self.addr_cache.avail_ring_host) .with_context(|| { anyhow!(VirtioError::ReadObjectErr( - "avail id", + "avail flags idx", self.avail_ring.raw_value() )) - })?; - Ok(avail_flags_idx.idx) + }) + } + + /// Get the idx of the available ring from guest memory. + fn get_avail_idx(&self, sys_mem: &Arc) -> Result { + let flags_idx = self.get_avail_flags_idx(sys_mem)?; + Ok(flags_idx.idx) } /// Get the flags of the available ring from guest memory. fn get_avail_flags(&self, sys_mem: &Arc) -> Result { - let avail_flags_idx: SplitVringFlagsIdx = sys_mem - .read_object_direct::(self.addr_cache.avail_ring_host) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "avail flags", - self.avail_ring.raw_value() - )) - })?; - Ok(avail_flags_idx.flags) + let flags_idx = self.get_avail_flags_idx(sys_mem)?; + Ok(flags_idx.flags) } - /// Get the index of the used ring from guest memory. - fn get_used_idx(&self, sys_mem: &Arc) -> Result { + /// Get the flags and idx of the used ring from guest memory. + fn get_used_flags_idx(&self, sys_mem: &Arc) -> Result { // Make sure the idx read from sys_mem is new. fence(Ordering::SeqCst); - let used_flag_idx: SplitVringFlagsIdx = sys_mem + sys_mem .read_object_direct::(self.addr_cache.used_ring_host) .with_context(|| { anyhow!(VirtioError::ReadObjectErr( - "used id", + "used flags idx", self.used_ring.raw_value() )) + }) + } + + /// Get the index of the used ring from guest memory. + fn get_used_idx(&self, sys_mem: &Arc) -> Result { + let flag_idx = self.get_used_flags_idx(sys_mem)?; + Ok(flag_idx.idx) + } + + /// Set the used flags to suppress virtqueue notification or not + fn set_used_flags(&self, sys_mem: &Arc, suppress: bool) -> Result<()> { + let mut flags_idx = self.get_used_flags_idx(sys_mem)?; + + if suppress { + flags_idx.flags |= VRING_USED_F_NO_NOTIFY; + } else { + flags_idx.flags &= !VRING_USED_F_NO_NOTIFY; + } + sys_mem + .write_object_direct::(&flags_idx, self.addr_cache.used_ring_host) + .with_context(|| { + format!( + "Failed to set used flags, used_ring: 0x{:X}", + self.used_ring.raw_value() + ) })?; - Ok(used_flag_idx.idx) + // Make sure the data has been set. + fence(Ordering::SeqCst); + Ok(()) } /// Set the avail idx to the field of the event index for the available ring. - fn set_avail_event(&self, sys_mem: &Arc) -> Result<()> { + fn set_avail_event(&self, sys_mem: &Arc, event_idx: u16) -> Result<()> { let avail_event_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(self.actual_size()); - let event_idx = self.next_avail.0; sys_mem .write_object_direct( @@ -818,6 +858,13 @@ impl SplitVring { desc_index, &mut self.cache, )?; + + // Suppress queue notification related to current processing desc chain. + if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { + self.set_avail_event(sys_mem, (self.next_avail + Wrapping(1)).0) + .with_context(|| "Failed to set avail event for popping avail ring")?; + } + let desc_info = DescInfo { table_host: self.addr_cache.desc_table_host, size: self.actual_size(), @@ -834,11 +881,6 @@ impl SplitVring { )?; self.next_avail += Wrapping(1); - if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { - self.set_avail_event(sys_mem) - .with_context(|| "Failed to set avail event for popping avail ring")?; - } - Ok(()) } } @@ -925,6 +967,20 @@ impl VringOps for SplitVring { } } + fn suppress_queue_notify( + &mut self, + sys_mem: &Arc, + features: u64, + suppress: bool, + ) -> Result<()> { + if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { + self.set_avail_event(sys_mem, self.get_avail_idx(sys_mem)?)?; + } else { + self.set_used_flags(sys_mem, suppress)?; + } + Ok(()) + } + fn actual_size(&self) -> u16 { self.actual_size() } -- Gitee From 728956bc33867a6672e5ab7a142b87c16903c701 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 07:37:46 +0800 Subject: [PATCH 0353/2187] virtio-blk: Fix deadlock when request check failed complete_request() will try to hold queue lock, so we must drop it before call it. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 064ff7d7b..05a556d71 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -487,9 +487,9 @@ impl BlockIoHandler { let mut req_queue = Vec::new(); let mut last_aio_req_index = 0; let mut done = false; - let mut queue = self.queue.lock().unwrap(); loop { + let mut queue = self.queue.lock().unwrap(); let elem = queue .vring .pop_avail(&self.mem_space, self.driver_features)?; @@ -523,6 +523,8 @@ impl BlockIoHandler { Some(self.interrupt_cb.clone()), self.driver_features, ); + // unlock queue, because it will be hold below. + drop(queue); aiocompletecb.complete_request(status); continue; } @@ -530,8 +532,6 @@ impl BlockIoHandler { done = true; } - // unlock queue, because it will be hold below. - drop(queue); if req_queue.is_empty() { return Ok(done); } -- Gitee From 8928995fd21935856ecca4efd12a3e72b47e7ddc Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 9 Nov 2022 23:28:29 +0800 Subject: [PATCH 0354/2187] virtio-blk: Support any layout for message framing As described by virtio 1.2 spec: The framing of messages with descriptors is independent of the contents of the buffers. Also rename Iovec in virtio-balloon as its name conflicts with that in util/aio. Signed-off-by: Keqian Zhu --- virtio/src/balloon.rs | 18 +++---- virtio/src/block.rs | 122 ++++++++++++++++-------------------------- virtio/src/lib.rs | 82 ++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 86 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index ed321d2a8..2d53fb7e5 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -61,7 +61,7 @@ static mut BALLOON_DEV: Option>> = None; /// IO vector, used to find memory segments. #[derive(Clone, Copy, Default)] -struct Iovec { +struct GuestIovec { /// Base address of memory. iov_base: GuestAddress, /// Length of memory segments. @@ -79,7 +79,7 @@ struct VirtioBalloonConfig { pub actual: u32, } -impl ByteCode for Iovec {} +impl ByteCode for GuestIovec {} impl ByteCode for VirtioBalloonConfig {} /// Bitmap for balloon. It is used if the host page size is bigger than 4k. @@ -124,7 +124,7 @@ impl BalloonedPageBitmap { /// * `offset` - Offset. fn iov_to_buf( address_space: &Arc, - &iov: &Iovec, + &iov: &GuestIovec, offset: u64, ) -> Option { let obj_len = std::mem::size_of::() as u64; @@ -162,7 +162,7 @@ struct Request { /// Count of elements. elem_cnt: u32, /// The data which is both readable and writable. - iovec: Vec, + iovec: Vec, } impl Request { @@ -188,7 +188,7 @@ impl Request { return Err(anyhow!(VirtioError::ElementEmpty)); } for elem_iov in iovec { - request.iovec.push(Iovec { + request.iovec.push(GuestIovec { iov_base: elem_iov.addr, iov_len: elem_iov.len as u64, }); @@ -1352,12 +1352,12 @@ mod tests { .write_object::(&desc, GuestAddress(queue_config_inf.desc_table.0)) .unwrap(); - let ele = Iovec { + let ele = GuestIovec { iov_base: GuestAddress(0xff), - iov_len: std::mem::size_of::() as u64, + iov_len: std::mem::size_of::() as u64, }; mem_space - .write_object::(&ele, GuestAddress(0x2000)) + .write_object::(&ele, GuestAddress(0x2000)) .unwrap(); mem_space .write_object::(&0, GuestAddress(queue_config_inf.avail_ring.0 + 4 as u64)) @@ -1383,7 +1383,7 @@ mod tests { .unwrap(); mem_space - .write_object::(&ele, GuestAddress(0x3000)) + .write_object::(&ele, GuestAddress(0x3000)) .unwrap(); mem_space .write_object::(&0, GuestAddress(queue_config_def.avail_ring.0 + 4 as u64)) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 05a556d71..9b8b9222f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,16 +21,17 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use super::{ - report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, - VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, - VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, - VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_BLOCK, + iov_discard_back, iov_discard_front, iov_from_buf_direct, iov_to_buf, report_virtio_error, + virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, + VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; +use byteorder::{ByteOrder, LittleEndian}; use log::error; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, @@ -73,15 +74,6 @@ fn get_serial_num_config(serial_num: &str) -> Vec { id_bytes } -fn write_buf_mem(buf: &[u8], hva: u64) -> Result<()> { - let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; - (&mut slice) - .write(buf) - .with_context(|| format!("Failed to write buf(hva:{})", hva))?; - - Ok(()) -} - #[repr(C)] #[derive(Default, Clone, Copy)] struct RequestOutHeader { @@ -178,8 +170,8 @@ struct Request { } impl Request { - fn new(handler: &BlockIoHandler, elem: &Element, status: &mut u8) -> Result { - if elem.out_iovec.is_empty() || elem.in_iovec.is_empty() || elem.desc_num < 2 { + fn new(handler: &BlockIoHandler, elem: &mut Element, status: &mut u8) -> Result { + if elem.out_iovec.is_empty() || elem.in_iovec.is_empty() { bail!( "Missed header for block request: out {} in {} desc num {}", elem.out_iovec.len(), @@ -188,32 +180,29 @@ impl Request { ); } - let out_iov_elem = elem.out_iovec.get(0).unwrap(); - if out_iov_elem.len < size_of::() as u32 { - bail!( - "Invalid out header for block request: length {}", - out_iov_elem.len - ); - } - - let out_header = handler - .mem_space - .read_object::(out_iov_elem.addr) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the block's request header", - out_iov_elem.addr.0 - )) - })?; + let mut out_header = RequestOutHeader::default(); + iov_to_buf( + &handler.mem_space, + &elem.out_iovec, + out_header.as_mut_bytes(), + ) + .and_then(|size| { + if size < size_of::() { + bail!("Invalid out header for block request: length {}", size); + } + Ok(()) + })?; + out_header.request_type = LittleEndian::read_u32(out_header.request_type.as_bytes()); + out_header.sector = LittleEndian::read_u64(out_header.sector.as_bytes()); - let pos = elem.in_iovec.len() - 1; - let in_iov_elem = elem.in_iovec.get(pos).unwrap(); + let in_iov_elem = elem.in_iovec.last().unwrap(); if in_iov_elem.len < 1 { bail!( "Invalid in header for block request: length {}", in_iov_elem.len ); } + let in_header = GuestAddress(in_iov_elem.addr.0 + in_iov_elem.len as u64 - 1); let mut request = Request { desc_index: elem.index, @@ -221,33 +210,29 @@ impl Request { iovec: Vec::with_capacity(elem.desc_num as usize), data_len: 0, in_len: 0, - in_header: in_iov_elem.addr, + in_header, next: Box::new(None), }; + // Count in_len before discard iovec. + // We always write the last status byte, so count all in_iovs. + for in_iov in elem.in_iovec.iter() { + request.in_len += in_iov.len; + } + match out_header.request_type { - VIRTIO_BLK_T_IN | VIRTIO_BLK_T_GET_ID => { - for (index, elem_iov) in elem.in_iovec.iter().enumerate() { - if index == elem.in_iovec.len() - 1 { - break; - } - if let Some(hva) = handler.mem_space.get_host_address(elem_iov.addr) { - let iov = Iovec { - iov_base: hva, - iov_len: u64::from(elem_iov.len), - }; - request.iovec.push(iov); - request.data_len += u64::from(elem_iov.len); - } else { - bail!("Map desc base {:?} failed", elem_iov.addr); + VIRTIO_BLK_T_IN | VIRTIO_BLK_T_GET_ID | VIRTIO_BLK_T_OUT => { + let data_iovec = match out_header.request_type { + VIRTIO_BLK_T_OUT => { + iov_discard_front(&mut elem.out_iovec, size_of::() as u64) } + // Otherwise discard the last "status" byte. + _ => iov_discard_back(&mut elem.in_iovec, 1), + }; + if data_iovec.is_none() { + bail!("Empty data for block request"); } - } - VIRTIO_BLK_T_OUT => { - for (index, elem_iov) in elem.out_iovec.iter().enumerate() { - if index == 0 { - continue; - } + for elem_iov in data_iovec.unwrap() { if let Some(hva) = handler.mem_space.get_host_address(elem_iov.addr) { let iov = Iovec { iov_base: hva, @@ -271,11 +256,6 @@ impl Request { *status = VIRTIO_BLK_S_IOERR; } - // We always write the last status byte, so count all in_iovs. - for in_iov in elem.in_iovec.iter() { - request.in_len += in_iov.len; - } - Ok(request) } @@ -356,19 +336,7 @@ impl Request { VIRTIO_BLK_T_GET_ID => { let serial = serial_num.clone().unwrap_or_else(|| String::from("")); let serial_vec = get_serial_num_config(&serial); - let mut start: usize = 0; - let mut end: usize; - - for iov in self.iovec.iter() { - end = cmp::min(start + iov.iov_len as usize, serial_vec.len()); - write_buf_mem(&serial_vec[start..end], iov.iov_base) - .with_context(|| "Failed to write buf for virtio block id")?; - - if end >= serial_vec.len() { - break; - } - start = end; - } + iov_from_buf_direct(&self.iovec, &serial_vec)?; aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_OK); } _ => bail!( @@ -490,7 +458,7 @@ impl BlockIoHandler { loop { let mut queue = self.queue.lock().unwrap(); - let elem = queue + let mut elem = queue .vring .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { @@ -514,7 +482,7 @@ impl BlockIoHandler { // Init and put valid request into request queue. let mut status = VIRTIO_BLK_S_OK; - let req = Request::new(self, &elem, &mut status)?; + let req = Request::new(self, &mut elem, &mut status)?; if status != VIRTIO_BLK_S_OK { let aiocompletecb = AioCompleteCb::new( self.queue.clone(), diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index dc01bef2a..486f15b25 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -60,11 +60,16 @@ pub use vhost::user as VhostUser; pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; pub use virtio_pci::VirtioPciDevice; +use std::cmp; +use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use anyhow::anyhow; use anyhow::bail; +use anyhow::Context; use machine_manager::config::ConfigCheck; +use util::aio::Iovec; use util::num_ops::write_u32; use vmm_sys_util::eventfd::EventFd; @@ -379,3 +384,80 @@ pub fn report_virtio_error( .unwrap_or_else(|e| error!("Failed to deactivate event, error is {}", e)); } } + +fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { + let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; + (&mut slice) + .write(buf) + .with_context(|| format!("Failed to write buf to hva:{})", hva))?; + Ok(()) +} + +/// Write buf to iovec and return the writed number of bytes. +pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { + let mut start: usize = 0; + let mut end: usize = 0; + + for iov in iovec.iter() { + end = cmp::min(start + iov.iov_len as usize, buf.len()); + mem_from_buf(&buf[start..end], iov.iov_base)?; + if end >= buf.len() { + break; + } + start = end; + } + Ok(end) +} + +fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { + let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; + buf.write(slice) + .with_context(|| format!("Failed to read buf from hva:{})", hva))?; + Ok(()) +} + +/// Read iovec to buf and return the readed number of bytes. +pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) -> Result { + let mut start: usize = 0; + let mut end: usize = 0; + let mut hva; + + for iov in iovec { + end = cmp::min(start + iov.len as usize, buf.len()); + hva = mem_space + .get_host_address(iov.addr) + .ok_or_else(|| anyhow!("Map iov base failed"))?; + mem_to_buf(&mut buf[start..end], hva)?; + if end >= buf.len() { + break; + } + start = end; + } + Ok(end) +} + +/// Discard "size" bytes of the front of iovec. +pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&[ElemIovec]> { + for (index, iov) in iovec.iter_mut().enumerate() { + if iov.len as u64 > size { + iov.addr.0 += size; + iov.len -= size as u32; + return Some(&iovec[index..]); + } + size -= iov.len as u64; + } + None +} + +/// Discard "size" bytes of the back of iovec. +pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&[ElemIovec]> { + let len = iovec.len(); + for (index, iov) in iovec.iter_mut().rev().enumerate() { + if iov.len as u64 > size { + iov.len -= size as u32; + return Some(&iovec[..(len - index)]); + } + size -= iov.len as u64; + } + None +} -- Gitee From 0e805cb10718119319f67d91627bc4d740c8707c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 6 Nov 2022 15:23:41 +0800 Subject: [PATCH 0355/2187] virtio-blk: Add auto queue num support Give each vcpu a vq, allow the vCPU that submit request can handle its own request completion. i.e, If the vq is not enough, vcpu A will receive completion of request that submitted by vcpu B, then A needs to IPI B. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 15 +++++++++++++-- machine/src/micro_vm/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 9 ++++++--- machine_manager/src/config/drive.rs | 19 +++++++++++++++---- virtio/src/virtio_pci.rs | 10 +++++++++- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 69aa0aeb0..94f796f13 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -54,6 +54,7 @@ use machine_manager::config::{ parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + MAX_VIRTIO_QUEUE, }; use machine_manager::{ event_loop::EventLoop, @@ -602,7 +603,12 @@ pub trait MachineOps { fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - let device_cfg = parse_blk(vm_config, cfg_args)?; + let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( + 0, + vm_config.machine_config.nr_cpus, + MAX_VIRTIO_QUEUE, + )); + let device_cfg = parse_blk(vm_config, cfg_args, queues_auto)?; if let Some(bootindex) = device_cfg.boot_index { self.check_bootindex(bootindex) .with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?; @@ -727,7 +733,12 @@ pub trait MachineOps { fn add_vhost_user_blk_pci(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - let device_cfg = parse_vhost_user_blk_pci(vm_config, cfg_args)?; + let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( + 0, + vm_config.machine_config.nr_cpus, + MAX_VIRTIO_QUEUE, + )); + let device_cfg = parse_vhost_user_blk_pci(vm_config, cfg_args, queues_auto)?; let device: Arc> = Arc::new(Mutex::new(VhostUser::Block::new( &device_cfg, self.get_sys_mem(), diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 6e05d3a70..9478d3185 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -698,7 +698,7 @@ impl MachineOps for LightMachine { vm_config: &mut VmConfig, cfg_args: &str, ) -> MachineResult<()> { - let device_cfg = parse_blk(vm_config, cfg_args)?; + let device_cfg = parse_blk(vm_config, cfg_args, None)?; if self.replaceable_info.block_count >= MMIO_REPLACEABLE_BLK_NR { bail!( "A maximum of {} block replaceable devices are supported.", diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 0078a5101..6d0c122f8 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -49,7 +49,7 @@ use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, - DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, VmConfig, + DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, VmConfig, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -59,7 +59,7 @@ use pci::PciBus; use util::byte_code::ByteCode; use virtio::{ qmp_balloon, qmp_query_balloon, Block, BlockState, VhostKern, VhostUser, VirtioDevice, - VirtioNetState, + VirtioNetState, VirtioPciDevice, }; #[cfg(target_arch = "aarch64")] @@ -748,6 +748,7 @@ impl StdMachine { bail!("Drive not set"); }; + let nr_cpus = self.get_vm_config().lock().unwrap().machine_config.nr_cpus; let blk = if let Some(conf) = self.get_vm_config().lock().unwrap().drives.get(drive) { let dev = BlkDevConfig { id: args.id.clone(), @@ -757,7 +758,9 @@ impl StdMachine { serial_num: args.serial_num.clone(), iothread: args.iothread.clone(), iops: conf.iops, - queues: args.queues.unwrap_or(1), + queues: args.queues.unwrap_or_else(|| { + VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) + }), boot_index: args.boot_index, chardev: None, socket_path: None, diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index c8239c0fe..bb5d6799d 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -256,7 +256,11 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { Ok(drive) } -pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result { +pub fn parse_blk( + vm_config: &mut VmConfig, + drive_config: &str, + queues_auto: Option, +) -> Result { let mut cmd_parser = CmdParser::new("virtio-blk"); cmd_parser .push("") @@ -301,6 +305,8 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result("num-queues")? { blkdevcfg.queues = queues; + } else if let Some(queues) = queues_auto { + blkdevcfg.queues = queues; } if let Some(drive_arg) = &vm_config.drives.remove(&blkdrive) { @@ -319,6 +325,7 @@ pub fn parse_blk(vm_config: &mut VmConfig, drive_config: &str) -> Result, ) -> Result { let mut cmd_parser = CmdParser::new("vhost-user-blk-pci"); cmd_parser @@ -352,6 +359,8 @@ pub fn parse_vhost_user_blk_pci( if let Some(queues) = cmd_parser.get_value::("num-queues")? { blkdevcfg.queues = queues; + } else if let Some(queues) = queues_auto { + blkdevcfg.queues = queues; } if let Some(chardev) = &blkdevcfg.chardev { @@ -536,6 +545,7 @@ mod tests { let blk_cfg_res = parse_blk( &mut vm_config, "virtio-blk-device,drive=rootfs,id=rootfs,iothread=iothread1,serial=111111,num-queues=4", + None, ); assert!(blk_cfg_res.is_ok()); let blk_device_config = blk_cfg_res.unwrap(); @@ -553,6 +563,7 @@ mod tests { let blk_cfg_res = parse_blk( &mut vm_config, "virtio-blk-device,drive=rootfs1,id=rootfs1,iothread=iothread1,iops=200,serial=111111", + None, ); assert!(blk_cfg_res.is_err()); // Can not find drive named "rootfs1". } @@ -564,7 +575,7 @@ mod tests { .add_drive("id=rootfs,file=/path/to/rootfs,readonly=off,direct=on") .is_ok()); let blk_cfg = "virtio-blk-pci,id=rootfs,bus=pcie.0,addr=0x1.0x2,drive=rootfs,serial=111111,num-queues=4"; - let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg); + let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg, None); assert!(blk_cfg_res.is_ok()); let drive_configs = blk_cfg_res.unwrap(); assert_eq!(drive_configs.id, "rootfs"); @@ -581,7 +592,7 @@ mod tests { assert_eq!(pci.addr, (1, 2)); // drive "rootfs" has been removed. - let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg); + let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg, None); assert!(blk_cfg_res.is_err()); let mut vm_config = VmConfig::default(); @@ -590,7 +601,7 @@ mod tests { .is_ok()); let blk_cfg = "virtio-blk-pci,id=blk1,bus=pcie.0,addr=0x1.0x2,drive=rootfs,multifunction=on"; - assert!(parse_blk(&mut vm_config, blk_cfg).is_ok()); + assert!(parse_blk(&mut vm_config, blk_cfg, None).is_ok()); } #[test] diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 9d56d2101..72d5a6c78 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::cmp::max; +use std::cmp::{max, min}; use std::collections::HashMap; use std::mem::size_of; use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; @@ -1069,6 +1069,14 @@ impl VirtioPciDevice { ); } } + + pub fn virtio_pci_auto_queues_num(queues_fixed: u16, nr_cpus: u8, queues_max: usize) -> u16 { + // Give each vcpu a vq, allow the vCPU that submit request can handle + // its own request completion. i.e, If the vq is not enough, vcpu A will + // receive completion of request that submitted by vcpu B, then A needs + // to IPI B. + min(queues_max as u16 - queues_fixed, nr_cpus as u16) + } } impl PciDevOps for VirtioPciDevice { -- Gitee From 78bb0d3eb938fff251d0a92b516b37970828c715 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sat, 12 Nov 2022 07:07:38 -0500 Subject: [PATCH 0356/2187] pci: acpi: fix the hotplog bits in _OSC control field We now support PCIe native hotplug with the PCIe capability structure, but not SHPC, PME or AER, so the value shall be 0x11. Signed-off-by: Zhang Bo --- pci/src/host.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pci/src/host.rs b/pci/src/host.rs index 3400cb4a2..507a31c76 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -323,8 +323,12 @@ fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { AmlName("CDW3".to_string()), AmlName("CTRL".to_string()), )); + /* + * Hotplug: We now support PCIe native hotplug(bit 0) with PCI Express Capability Structure(bit 4) + * other bits: bit1: SHPC; bit2: PME; bit3: AER; + */ if_obj_0.append_child(AmlStore::new( - AmlAnd::new(AmlName("CTRL".to_string()), AmlInteger(0x1d), AmlLocal(0)), + AmlAnd::new(AmlName("CTRL".to_string()), AmlInteger(0x11), AmlLocal(0)), AmlName("CTRL".to_string()), )); let mut if_obj_1 = AmlIf::new(AmlLNot::new(AmlEqual::new(AmlArg(1), AmlInteger(1)))); -- Gitee From a18a591832783f2ec05acc0f733ea5b5d60e4817 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sat, 12 Nov 2022 07:23:15 -0500 Subject: [PATCH 0357/2187] pci: acpi: x86: fix _OSC hotpulg control field same to ARM acpi _OSC table, make the hotplug field in _OSC 0x11, as we now support PCIe native hotplug with PCIe capability, others not supported yet. Signed-off-by: Zhang Bo --- pci/src/host.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pci/src/host.rs b/pci/src/host.rs index 3baf26cee..6c5451488 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -275,7 +275,11 @@ fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(8), "CDW3")); let cdw3 = AmlName("CDW3".to_string()); if_obj_0.append_child(AmlStore::new(cdw3.clone(), AmlLocal(0))); - if_obj_0.append_child(AmlAnd::new(AmlLocal(0), AmlInteger(0x1f), AmlLocal(0))); + /* + * Hotplug: We now support PCIe native hotplug(bit 0) with PCI Express Capability Structure(bit 4) + * other bits: bit1: SHPC; bit2: PME; bit3: AER; + */ + if_obj_0.append_child(AmlAnd::new(AmlLocal(0), AmlInteger(0x11), AmlLocal(0))); let mut if_obj_1 = AmlIf::new(AmlLNot::new(AmlEqual::new(AmlArg(1), AmlInteger(1)))); let cdw1 = AmlName("CDW1".to_string()); if_obj_1.append_child(AmlOr::new(cdw1.clone(), AmlInteger(0x08), cdw1.clone())); -- Gitee From 82c95575c247c2858efebea35ce7663028503843 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 21:31:51 +0800 Subject: [PATCH 0358/2187] blk-config: Remove duplicate check code 1. Reuse check code of DriveConfig in blockdev_add check of micro_vm. 2. Reuse check code of DriveConfig in BlkDevConfig check. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/mod.rs | 49 +++++------------------------ machine_manager/src/config/drive.rs | 38 +++++++++------------- machine_manager/src/config/error.rs | 4 +++ 3 files changed, 27 insertions(+), 64 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 434354c5b..fbd16148c 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -39,11 +39,8 @@ use super::Result as MachineResult; use log::error; use std::fmt; use std::fmt::Debug; -use std::fs::metadata; use std::ops::Deref; -use std::os::linux::fs::MetadataExt; use std::os::unix::io::RawFd; -use std::path::Path; use std::sync::{Arc, Condvar, Mutex}; use std::vec::Vec; use vmm_sys_util::eventfd::EventFd; @@ -66,7 +63,7 @@ use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, Incoming, MigrateMode, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, DriveConfig, Incoming, MigrateMode, }; use machine_manager::event; use machine_manager::machine::{ @@ -1082,9 +1079,7 @@ impl DeviceInterface for LightMachine { } fn blockdev_add(&self, args: Box) -> Response { - const MAX_STRING_LENGTH: usize = 255; let read_only = args.read_only.unwrap_or(false); - let direct = if let Some(cache) = args.cache { match cache.direct { Some(direct) => direct, @@ -1094,42 +1089,14 @@ impl DeviceInterface for LightMachine { true }; - let blk = Path::new(&args.file.filename); - match metadata(blk) { - Ok(meta) => { - if (meta.st_mode() & libc::S_IFREG != libc::S_IFREG) - && (meta.st_mode() & libc::S_IFBLK != libc::S_IFBLK) - { - error!("File {:?} is not a regular file or block device", blk); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError( - "File is not a regular file or block device".to_string(), - ), - None, - ); - } - } - Err(ref e) => { - error!("Blockdev_add failed: {}", e); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - } - - if let Some(file_name) = blk.file_name() { - if file_name.len() > MAX_STRING_LENGTH { - error!("File name {:?} is illegal", file_name); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError("Illegal block name".to_string()), - None, - ); - } - } else { - error!("Path: {:?} is not valid", blk); + let fake_drive = DriveConfig { + path_on_host: args.file.filename.clone(), + ..Default::default() + }; + if let Err(e) = fake_drive.check_path() { + error!("{:?}", e); return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError("Invalid block path".to_string()), + qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, ); } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index bb5d6799d..6b48cbb28 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -107,15 +107,16 @@ impl DriveConfig { if ((meta.st_mode() & libc::S_IFREG) != libc::S_IFREG) && ((meta.st_mode() & libc::S_IFBLK) != libc::S_IFBLK) { - return Err(anyhow!(ConfigError::UnRegularFile( - "Drive File".to_string() + return Err(anyhow!(ConfigError::UnRegularFileOrBlk( + self.path_on_host.clone() ))); } } Err(e) => { error!("Failed to check the drive metadata: {:?}", e); - return Err(anyhow!(ConfigError::UnRegularFile( - "Drive File".to_string() + return Err(anyhow!(ConfigError::NoMetadata( + self.path_on_host.clone(), + e.to_string(), ))); } } @@ -128,8 +129,9 @@ impl DriveConfig { } } else { error!("Failed to check the drive file name"); - return Err(anyhow!(ConfigError::UnRegularFile( - "Drive File".to_string() + return Err(anyhow!(ConfigError::InvalidParam( + self.path_on_host.clone(), + "file".to_string(), ))); } Ok(()) @@ -172,13 +174,6 @@ impl ConfigCheck for BlkDevConfig { ))); } - if self.path_on_host.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "drive device path".to_string(), - MAX_PATH_LENGTH, - ))); - } - if self.serial_num.is_some() && self.serial_num.as_ref().unwrap().len() > MAX_SERIAL_NUM { return Err(anyhow!(ConfigError::StringLengthTooLong( "drive serial number".to_string(), @@ -193,16 +188,6 @@ impl ConfigCheck for BlkDevConfig { ))); } - if self.iops.is_some() && self.iops.unwrap() > MAX_IOPS { - return Err(anyhow!(ConfigError::IllegalValue( - "iops of block device".to_string(), - 0, - true, - MAX_IOPS, - true, - ))); - } - if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u16 { return Err(anyhow!(ConfigError::IllegalValue( "number queues of block device".to_string(), @@ -213,6 +198,13 @@ impl ConfigCheck for BlkDevConfig { ))); } + let fake_drive = DriveConfig { + path_on_host: self.path_on_host.clone(), + iops: self.iops, + ..Default::default() + }; + fake_drive.check()?; + Ok(()) } } diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index 9b8f2e0fc..589aab688 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -50,6 +50,10 @@ pub enum ConfigError { UnknownVhostType, #[error("{0} is not a regular File.")] UnRegularFile(String), + #[error("{0} is not a regular file or block device.")] + UnRegularFileOrBlk(String), + #[error("Failed to get metadata of file {0}: {1}.")] + NoMetadata(String, String), #[error("Input value {0} is unaligned with {1} for {2}.")] Unaligned(String, u64, u64), #[error("PFlash unit id given {0} should not be more than {1}")] -- Gitee From 1393d63ba646b6f2fa4a0632ebed15423fa20f14 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 22:37:06 +0800 Subject: [PATCH 0359/2187] blk-config: Add missing check for DriveConfig and BlkDevConfig Add check_path in blkdevconfig check, so the blockdev_add in lightmachine can be deleted. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/mod.rs | 21 ++++++++------------- machine_manager/src/config/drive.rs | 5 +++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index fbd16148c..32b8f8bb7 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -63,7 +63,7 @@ use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, DriveConfig, Incoming, MigrateMode, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, Incoming, MigrateMode, }; use machine_manager::event; use machine_manager::machine::{ @@ -1089,18 +1089,6 @@ impl DeviceInterface for LightMachine { true }; - let fake_drive = DriveConfig { - path_on_host: args.file.filename.clone(), - ..Default::default() - }; - if let Err(e) = fake_drive.check_path() { - error!("{:?}", e); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - let config = BlkDevConfig { id: args.node_name.clone(), path_on_host: args.file.filename, @@ -1116,6 +1104,13 @@ impl DeviceInterface for LightMachine { // TODO Add aio option by qmp. aio: None, }; + if let Err(e) = config.check() { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), Err(ref e) => { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 6b48cbb28..cf31be9cc 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -204,6 +204,8 @@ impl ConfigCheck for BlkDevConfig { ..Default::default() }; fake_drive.check()?; + #[cfg(not(test))] + fake_drive.check_path()?; Ok(()) } @@ -245,6 +247,9 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { } else { Some(AIO_NATIVE.to_string()) }; + drive.check()?; + #[cfg(not(test))] + drive.check_path()?; Ok(drive) } -- Gitee From e4c0f413f1a646dc2a19ae08d0c6c360fbaa3cbe Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 13 Nov 2022 01:31:35 +0800 Subject: [PATCH 0360/2187] blk-config: Remove "serial" option of DriveConfig ... which is actually unused. Signed-off-by: Keqian Zhu --- machine_manager/src/config/drive.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index cf31be9cc..58b8282fd 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -430,7 +430,6 @@ impl VmConfig { .push("format") .push("if") .push("throttling.iops-total") - .push("serial") .push("aio"); cmd_parser.parse(block_config)?; @@ -594,7 +593,7 @@ mod tests { let mut vm_config = VmConfig::default(); assert!(vm_config - .add_drive("id=rootfs,file=/path/to/rootfs,serial=111111,readonly=off,direct=on") + .add_drive("id=rootfs,file=/path/to/rootfs,readonly=off,direct=on") .is_ok()); let blk_cfg = "virtio-blk-pci,id=blk1,bus=pcie.0,addr=0x1.0x2,drive=rootfs,multifunction=on"; -- Gitee From 4afb09fdbaad76d169f609300a609b87e36d9ad9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 13:38:01 +0800 Subject: [PATCH 0361/2187] blk-config: Decouple direct and aio option for drive The direct option controls the cache mode of fd. And the aio option controls the async IO type. Note: for direct IO, the buffer must be aligned to sector size. Signed-off-by: Keqian Zhu --- docs/config_guidebook.md | 3 ++- machine/src/micro_vm/mod.rs | 9 ++++++-- machine/src/standard_vm/mod.rs | 11 ++++++--- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/drive.rs | 36 +++++++++++++++++++++++++---- util/src/aio/mod.rs | 14 ++++++----- virtio/src/block.rs | 25 ++++++++++++-------- virtio/src/scsi/bus.rs | 9 ++++---- virtio/src/scsi/controller.rs | 9 +++++++- 9 files changed, 86 insertions(+), 32 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index eee2fdb9d..7e580177a 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -263,7 +263,7 @@ There is only one argument for iothread: Virtio block device is a virtual block device, which process read and write requests in virtio queue from guest. -Nine properties are supported for virtio block device. +Ten properties are supported for virtio block device. * drive_id: unique device-id in StratoVirt. * path_on_host: the path of block device in host. @@ -280,6 +280,7 @@ the default block queue number is 1. The max queues number supported is no more * bootindex: the boot order of block device. (optional) If not set, the priority is lowest. The number ranges from 0 to 255, the smaller the number, the higher the priority. It determines the order of bootable devices which firmware will use for booting the guest OS. +* aio: the aio type of block device (optional). Possible values are `native`, `io_uring`, or `off`. If not set, default is `native` if `direct` is true, otherwise default is `off`. For virtio-blk-pci, two more properties are required. * bus: name of bus which to attach. diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 32b8f8bb7..aee05d69c 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -30,6 +30,7 @@ pub mod error; pub use error::MicroVmError; +use util::aio::AIO_NATIVE; mod mem_layout; mod syscall; @@ -1101,8 +1102,12 @@ impl DeviceInterface for LightMachine { boot_index: None, chardev: None, socket_path: None, - // TODO Add aio option by qmp. - aio: None, + // TODO Add aio option by qmp, now we set it based on "direct". + aio: if direct { + Some(String::from(AIO_NATIVE)) + } else { + None + }, }; if let Err(e) = config.check() { error!("{:?}", e); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index b7cd867dc..f5d504a7e 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -23,6 +23,7 @@ pub use error::StandardVmError; pub use aarch64::StdMachine; use log::error; use machine_manager::event_loop::EventLoop; +use util::aio::AIO_NATIVE; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -764,8 +765,7 @@ impl StdMachine { boot_index: args.boot_index, chardev: None, socket_path: None, - // TODO Add aio option by qmp. - aio: None, + aio: conf.aio.clone(), }; dev.check()?; dev @@ -1145,7 +1145,12 @@ impl DeviceInterface for StdMachine { read_only, direct, iops: args.iops, - aio: None, + // TODO Add aio option by qmp, now we set it based on "direct". + aio: if direct { + Some(String::from(AIO_NATIVE)) + } else { + None + }, }; if let Err(e) = config.check() { diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 442eaf073..d81ad79e5 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -148,7 +148,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("drive") .multiple(true) .long("drive") - .value_name("file=path,id=str[,readonly=][,direct=][,serial=][,iothread=][iops=][,aio=native|io_uring]") + .value_name("file=path,id=str[,readonly=][,direct=][,serial=][,iothread=][iops=][,aio=native|io_uring|off]") .help("use 'file' as a drive image") .takes_values(true), ) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 58b8282fd..becc6a684 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -93,7 +93,7 @@ impl Default for DriveConfig { read_only: false, direct: true, iops: None, - aio: None, + aio: Some(String::from(AIO_NATIVE)), } } } @@ -161,6 +161,18 @@ impl ConfigCheck for DriveConfig { true, ))); } + if self.aio == Some(String::from(AIO_NATIVE)) && !self.direct { + return Err(anyhow!(ConfigError::InvalidParam( + "aio".to_string(), + "native aio type should be used with \"direct\" on".to_string(), + ))); + } + if self.aio.is_none() && self.direct { + return Err(anyhow!(ConfigError::InvalidParam( + "aio".to_string(), + "low performance expected when use sync io with \"direct\" on".to_string(), + ))); + } Ok(()) } } @@ -200,7 +212,9 @@ impl ConfigCheck for BlkDevConfig { let fake_drive = DriveConfig { path_on_host: self.path_on_host.clone(), + direct: self.direct, iops: self.iops, + aio: self.aio.clone(), ..Default::default() }; fake_drive.check()?; @@ -240,12 +254,24 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { } drive.iops = cmd_parser.get_value::("throttling.iops-total")?; drive.aio = if let Some(aio) = cmd_parser.get_value::("aio")? { - if aio != AIO_NATIVE && aio != AIO_IOURING { - bail!("Invalid aio configure") + let aio_off = "off"; + if aio != AIO_NATIVE && aio != AIO_IOURING && aio != aio_off { + bail!( + "Invalid aio configure, should be one of {}|{}|{}", + AIO_NATIVE, + AIO_IOURING, + aio_off + ); } - Some(aio) - } else { + if aio != aio_off { + Some(aio) + } else { + None + } + } else if drive.direct { Some(AIO_NATIVE.to_string()) + } else { + None }; drive.check()?; #[cfg(not(test))] diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index b1bec28b1..3d8c0d2bf 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -174,15 +174,17 @@ impl Aio { Ok(()) } - pub fn rw_aio(&mut self, cb: AioCb, sector_size: u64) -> Result<()> { + pub fn rw_aio(&mut self, cb: AioCb, sector_size: u64, direct: bool) -> Result<()> { let mut misaligned = false; - for iov in cb.iovec.iter() { - if iov.iov_base % sector_size != 0 || iov.iov_len % sector_size != 0 { - misaligned = true; - break; + if direct { + for iov in cb.iovec.iter() { + if iov.iov_base % sector_size != 0 || iov.iov_len % sector_size != 0 { + misaligned = true; + break; + } } } - if misaligned { + if direct && misaligned { return self.handle_misaligned_aio(cb); } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 9b8b9222f..83d30cf1c 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -42,8 +42,7 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::aio::raw_datasync; -use util::aio::{Aio, AioCb, AioCompleteFunc, IoCmd, Iovec}; +use util::aio::{raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ @@ -63,7 +62,7 @@ const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; /// Size of the dummy block device. const DUMMY_IMG_SIZE: u64 = 0; -type SenderConfig = (Option>, u64, Option, bool); +type SenderConfig = (Option>, u64, Option, bool, Option); fn get_serial_num_config(serial_num: &str) -> Vec { let mut id_bytes = vec![0; VIRTIO_BLK_ID_BYTES as usize]; @@ -266,6 +265,7 @@ impl Request { disk: &File, serial_num: &Option, direct: bool, + aio_type: &Option, last_aio: bool, iocompletecb: AioCompleteCb, ) -> Result<()> { @@ -295,13 +295,13 @@ impl Request { match self.out_header.request_type { VIRTIO_BLK_T_IN => { aiocb.opcode = IoCmd::Preadv; - if direct { + if aio_type.is_some() { for iov in aiocb.iovec.iter() { MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); } (*aio) .as_mut() - .rw_aio(aiocb, SECTOR_SIZE) + .rw_aio(aiocb, SECTOR_SIZE, direct) .with_context(|| { "Failed to process block request for reading asynchronously" })?; @@ -313,10 +313,10 @@ impl Request { } VIRTIO_BLK_T_OUT => { aiocb.opcode = IoCmd::Pwritev; - if direct { + if aio_type.is_some() { (*aio) .as_mut() - .rw_aio(aiocb, SECTOR_SIZE) + .rw_aio(aiocb, SECTOR_SIZE, direct) .with_context(|| { "Failed to process block request for writing asynchronously" })?; @@ -391,8 +391,10 @@ struct BlockIoHandler { disk_sectors: u64, /// Serial number of the block device. serial_num: Option, - /// if use direct access io. + /// If use direct access io. direct: bool, + /// Async IO type. + aio_type: Option, /// Aio context. aio: Option>>, /// Bit mask of features negotiated by the backend and the frontend. @@ -520,6 +522,7 @@ impl BlockIoHandler { disk_img, &self.serial_num, self.direct, + &self.aio_type, req_index == last_aio_req_index, aiocompletecb.clone(), ) { @@ -608,17 +611,19 @@ impl BlockIoHandler { fn update_evt_handler(&mut self) { match self.receiver.recv() { - Ok((image, disk_sectors, serial_num, direct)) => { + Ok((image, disk_sectors, serial_num, direct, aio_type)) => { self.disk_sectors = disk_sectors; self.disk_image = image; self.serial_num = serial_num; self.direct = direct; + self.aio_type = aio_type; } Err(_) => { self.disk_sectors = 0; self.disk_image = None; self.serial_num = None; self.direct = true; + self.aio_type = Some(String::from(AIO_NATIVE)); } }; @@ -1106,6 +1111,7 @@ impl VirtioDevice for Block { disk_image: self.disk_image.clone(), disk_sectors: self.disk_sectors, direct: self.blk_cfg.direct, + aio_type: self.blk_cfg.aio.clone(), serial_num: self.blk_cfg.serial_num.clone(), aio: None, driver_features: self.state.driver_features, @@ -1159,6 +1165,7 @@ impl VirtioDevice for Block { self.disk_sectors, self.blk_cfg.serial_num.clone(), self.blk_cfg.direct, + self.blk_cfg.aio.clone(), )) .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; } diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 307f7f32c..e3e3aeca0 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -438,6 +438,7 @@ impl ScsiRequest { aio: &mut Box>, disk: &File, direct: bool, + aio_type: Option, last_aio: bool, iocompletecb: ScsiCompleteCb, ) -> Result { @@ -472,10 +473,10 @@ impl ScsiRequest { match self.cmd.mode { ScsiXferMode::ScsiXferFromDev => { aiocb.opcode = IoCmd::Preadv; - if direct { + if aio_type.is_some() { (*aio) .as_mut() - .rw_aio(aiocb, SECTOR_SIZE) + .rw_aio(aiocb, SECTOR_SIZE, direct) .with_context(|| { "Failed to process scsi request for reading asynchronously" })?; @@ -487,10 +488,10 @@ impl ScsiRequest { } ScsiXferMode::ScsiXferToDev => { aiocb.opcode = IoCmd::Pwritev; - if direct { + if aio_type.is_some() { (*aio) .as_mut() - .rw_aio(aiocb, SECTOR_SIZE) + .rw_aio(aiocb, SECTOR_SIZE, direct) .with_context(|| { "Failed to process block request for writing asynchronously" })?; diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 5a258f4c5..56cca5184 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -813,7 +813,14 @@ impl ScsiCmdHandler { Arc::new(Mutex::new(scsireq.clone())), ); if let Some(ref mut aio) = self.aio { - scsireq.execute(aio, disk_img, false, true, scsicompletecb)?; + scsireq.execute( + aio, + disk_img, + false, + None, + true, + scsicompletecb, + )?; } } } -- Gitee From e6d6e5d1d5e5bd3041743ba59af3ccad347db506 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 13:52:38 +0800 Subject: [PATCH 0362/2187] blk-config: Update docs related to virtio-blk configuration There are some errors and some descriptions are out of date. Signed-off-by: Keqian Zhu --- docs/config_guidebook.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 7e580177a..4720a2b29 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -263,29 +263,28 @@ There is only one argument for iothread: Virtio block device is a virtual block device, which process read and write requests in virtio queue from guest. -Ten properties are supported for virtio block device. - -* drive_id: unique device-id in StratoVirt. -* path_on_host: the path of block device in host. -* serial_num: serial number of virtio block. (optional) -* read_only: whether virtio block device is read-only. If not set, default is false. -* direct: open block device with `O_DIRECT` mode. If not set, default is true. -* iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) +10 properties are supported for virtio block device. + +* id: unique device-id in StratoVirt. +* file: the path of backend file on host. +* serial: serial number of virtio block. (optional) +* readonly: whether virtio block device is read-only. (optional) If not set, default is false. +* direct: open block device with `O_DIRECT` mode. (optional) If not set, default is true. +* iothread: indicate which iothread will be used. (optional) if not set, the main thread will be used. * throttling.iops-total: used to limit IO operations for block device. (optional) -* if: drive type, for block drive, it should be `none`. If not set, default is `none` (optional) -* format: the format of block image, default value `raw`. NB: currently only `raw` is supported. (optional) -If not set, default is raw. -* num-queues: the optional num-queues attribute controls the number of queues to be used for block device. If not set, -the default block queue number is 1. The max queues number supported is no more than 32. +* if: drive type, for block drive, it should be `none`. (optional) If not set, default is `none`. +* format: the format of block image. (optional) If not set, default is `raw`. NB: currently only `raw` is supported. +* num-queues: the optional num-queues attribute controls the number of queues to be used for block device. (optional) The max queues number supported is 32. If not set, the default block queue number is the smaller one of vCPU count and the max queues number (e.g, min(vcpu_count, 32)). * bootindex: the boot order of block device. (optional) If not set, the priority is lowest. The number ranges from 0 to 255, the smaller the number, the higher the priority. It determines the order of bootable devices which firmware will use for booting the guest OS. * aio: the aio type of block device (optional). Possible values are `native`, `io_uring`, or `off`. If not set, default is `native` if `direct` is true, otherwise default is `off`. -For virtio-blk-pci, two more properties are required. +For virtio-blk-pci, three more properties are required. * bus: name of bus which to attach. * addr: including slot number and function number. The first number represents slot number of device and the second one represents function number of it. +* multifunction: whether to open multi-function for device. (optional) If not set, default is false. If you want to boot VM with a virtio block device as rootfs, you should add `root=DEVICE_NAME_IN_GUESTOS` in Kernel Parameters. `DEVICE_NAME_IN_GUESTOS` will from `vda` to `vdz` in order. -- Gitee From efcf7be42404d708a3a38f372ab5de5c9ccbd738 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 13 Nov 2022 08:03:11 +0800 Subject: [PATCH 0363/2187] aio: Fix memory leak of aio request list node 1. The iocb don't need to be raw pointer. 2. Construct node from raw pointer to free memory automatically. 3. The list node is of type NonNull which should be freed by hand. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 18 +++++++----------- util/src/link_list.rs | 6 ++++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 3d8c0d2bf..058d2009b 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -62,7 +62,7 @@ pub struct AioCb { pub iovec: Vec, pub offset: usize, pub process: bool, - pub iocb: Option>, + pub iocb: Option>, pub iocompletecb: T, } @@ -133,13 +133,8 @@ impl Aio { (self.complete_func)(&(*node).value, evts[e as usize].res); self.aio_in_flight.unlink(&(*node)); - - // free mem - if let Some(i) = (*node).value.iocb { - libc::free((*node).value.iovec.as_ptr() as *mut libc::c_void); - libc::free(i.as_ptr() as *mut libc::c_void); - }; - libc::free(node as *mut libc::c_void); + // Construct Box to free mem automatically. + Box::from_raw(node); } } } @@ -154,8 +149,9 @@ impl Aio { for _ in self.aio_in_flight.len..self.max_events { match self.aio_in_queue.pop_tail() { - Some(node) => { - iocbs.push(node.value.iocb.unwrap().as_ptr()); + Some(mut node) => { + let iocb = node.value.iocb.as_mut().unwrap(); + iocbs.push(&mut **iocb as *mut IoCb); self.aio_in_flight.add_head(node); } None => break, @@ -207,7 +203,7 @@ impl Aio { data: (&mut (*node) as *mut CbNode) as u64, ..Default::default() }; - node.value.iocb = std::ptr::NonNull::new(Box::into_raw(Box::new(iocb))); + node.value.iocb = Some(Box::new(iocb)); self.aio_in_queue.add_head(node); if last_aio || self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { diff --git a/util/src/link_list.rs b/util/src/link_list.rs index 982fe3828..1565985e5 100644 --- a/util/src/link_list.rs +++ b/util/src/link_list.rs @@ -27,6 +27,12 @@ pub struct List { marker: PhantomData>>, } +impl Drop for List { + fn drop(&mut self) { + while self.pop_head().is_some() {} + } +} + impl Node { pub fn new(value: T) -> Self { Node { -- Gitee From 56b6be52e9dff857d0aa4954719c70b74cc29e6c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Nov 2022 12:42:33 +0800 Subject: [PATCH 0364/2187] virtio-scsi: modify wrong virtioscsiconfig size max_channel and max_target are 16 bytes. Modify it. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 52d0c9b27..0ae1d1746 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -108,8 +108,8 @@ struct VirtioScsiConfig { event_info_size: u32, sense_size: u32, cdb_size: u32, - max_channel: u32, - max_target: u32, + max_channel: u16, + max_target: u16, max_lun: u32, } -- Gitee From 4d4cd26c908cdc431a6bfb04ab8efbb73ab748c6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 15:31:07 +0800 Subject: [PATCH 0365/2187] aio: Handle incomplete read and write 1. Sync io: try again if raw rd/wr is interrupted by signal and report err if returned bytes is not equal to size desired. 2. Async io: return -1 if returned bytes is not equal to size desired or res2 is not 0. Signed-off-by: Keqian Zhu --- Cargo.lock | 1 + util/Cargo.toml | 1 + util/src/aio/libaio.rs | 4 ++-- util/src/aio/mod.rs | 33 +++++++++++++++++++++------------ util/src/aio/raw.rs | 20 ++++++++++++++++---- util/src/aio/uring.rs | 4 ++-- virtio/src/block.rs | 2 ++ virtio/src/scsi/bus.rs | 2 ++ 8 files changed, 47 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41ecc506a..8f4f747de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -840,6 +840,7 @@ dependencies = [ "anyhow", "arc-swap", "byteorder", + "errno", "io-uring", "kvm-bindings", "kvm-ioctls", diff --git a/util/Cargo.toml b/util/Cargo.toml index 8d82d639f..4ae17cf22 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -19,3 +19,4 @@ vmm-sys-util = ">=0.10.0" byteorder = "1.4.3" once_cell = "1.13.0" io-uring = "0.5.7" +errno = "0.2.7" diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 3a9267d49..4dfce7168 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -109,7 +109,7 @@ impl AioContext for LibaioContext { } /// Get the IO events. - fn get_events(&mut self) -> (&[IoEvent], u32, u32) { + fn get_events(&mut self) -> (&[IoEvent], usize, usize) { let ring = self.ctx as *mut AioRing; let head = unsafe { (*ring).head }; let tail = unsafe { (*ring).tail }; @@ -123,6 +123,6 @@ impl AioContext for LibaioContext { let io_events: &[IoEvent] = unsafe { (*ring).io_events.as_slice(ring_nr as usize) }; - (io_events, head, head + nr) + (io_events, head as usize, (head + nr) as usize) } } diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 058d2009b..87f876783 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -50,7 +50,7 @@ trait AioContext { /// Submit IO requests to the OS. fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()>; /// Get the IO events of the requests sumbitted earlier. - fn get_events(&mut self) -> (&[IoEvent], u32, u32); + fn get_events(&mut self) -> (&[IoEvent], usize, usize); } pub type AioCompleteFunc = Box, i64) + Sync + Send>; @@ -61,6 +61,7 @@ pub struct AioCb { pub opcode: IoCmd, pub iovec: Vec, pub offset: usize, + pub nbytes: u64, pub process: bool, pub iocb: Option>, pub iocompletecb: T, @@ -74,6 +75,7 @@ impl AioCb { opcode: IoCmd::Noop, iovec: Vec::new(), offset: 0, + nbytes: 0, process: false, iocb: None, iocompletecb: cb, @@ -125,17 +127,24 @@ impl Aio { let mut ctx = self.ctx.lock().unwrap(); let (evts, start, end) = ctx.get_events(); - for e in start..end { - if evts[e as usize].res2 == 0 { - done = true; - unsafe { - let node = evts[e as usize].data as *mut CbNode; - - (self.complete_func)(&(*node).value, evts[e as usize].res); - self.aio_in_flight.unlink(&(*node)); - // Construct Box to free mem automatically. - Box::from_raw(node); - } + for index in start..end { + unsafe { + let node = evts[index].data as *mut CbNode; + + let res = if evts[index].res2 == 0 + && evts[index].res > 0 + && evts[index].res as u64 == (*node).value.nbytes + { + done = true; + evts[index].res + } else { + -1 + }; + + (self.complete_func)(&(*node).value, res); + self.aio_in_flight.unlink(&(*node)); + // Construct Box to free mem automatically. + Box::from_raw(node); } } // Drop reference of 'ctx', so below 'process_list' can work. diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index bd8effd3c..816d76eb6 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -16,8 +16,14 @@ use libc::{c_void, fdatasync, pread, pwrite}; use std::os::unix::io::RawFd; pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result { - let ret = unsafe { pread(fd, buf as *mut c_void, size, offset as i64) as i64 }; - if ret < 0 { + let mut ret; + loop { + ret = unsafe { pread(fd, buf as *mut c_void, size, offset as i64) as i64 }; + if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + break; + } + } + if ret < 0 || ret as usize != size { bail!("Failed to pread for {}, return {}.", fd, ret); } @@ -25,8 +31,14 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result } pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result { - let ret = unsafe { pwrite(fd, buf as *mut c_void, size, offset as i64) as i64 }; - if ret < 0 { + let mut ret; + loop { + ret = unsafe { pwrite(fd, buf as *mut c_void, size, offset as i64) as i64 }; + if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + break; + } + } + if ret < 0 || ret as usize != size { bail!("Failed to pwrite for {}, return {}.", fd, ret); } diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index da36e963c..6e84ee9ca 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -96,7 +96,7 @@ impl AioContext for IoUringContext { } /// Get the events. - fn get_events(&mut self) -> (&[IoEvent], u32, u32) { + fn get_events(&mut self) -> (&[IoEvent], usize, usize) { let mut queue = self.ring.completion(); self.events.clear(); let l = queue.len(); @@ -114,6 +114,6 @@ impl AioContext for IoUringContext { } } } - (&self.events, 0, self.events.len() as u32) + (&self.events, 0, self.events.len()) } } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 83d30cf1c..617cf8848 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -275,6 +275,7 @@ impl Request { opcode: IoCmd::Noop, iovec: Vec::new(), offset: (self.out_header.sector << SECTOR_SHIFT) as usize, + nbytes: 0, process: true, iocb: None, iocompletecb, @@ -288,6 +289,7 @@ impl Request { iov_len: iov.iov_len, }; aiocb.iovec.push(iovec); + aiocb.nbytes += iov.iov_len; } req = req_raw.next.as_ref().as_ref(); } diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index e3e3aeca0..4bb192395 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -448,6 +448,7 @@ impl ScsiRequest { opcode: IoCmd::Noop, iovec: Vec::new(), offset: (self.cmd.lba << 9) as usize, + nbytes: 0, process: true, iocb: None, iocompletecb, @@ -459,6 +460,7 @@ impl ScsiRequest { iov_len: iov.iov_len, }; aiocb.iovec.push(iovec); + aiocb.nbytes += iov.iov_len; } if self.cmd.command == SYNCHRONIZE_CACHE { -- Gitee From 87b3e5c66aa3fc73062eb1c63bf9c2b0f444bc9b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 16:41:02 +0800 Subject: [PATCH 0366/2187] aio/native: Implement drop for native aio .. to destroy the aio ctx. Signed-off-by: Keqian Zhu --- util/src/aio/libaio.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 4dfce7168..9ae5642d7 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -67,6 +67,14 @@ pub struct LibaioContext { pub max_size: i32, } +impl Drop for LibaioContext { + fn drop(&mut self) { + if !self.ctx.is_null() { + unsafe { libc::syscall(libc::SYS_io_destroy, self.ctx) }; + } + } +} + #[repr(C)] #[derive(Default)] pub struct AioRing { -- Gitee From 4b86bb2f2a6eba8844d509cadf8ace2c4cf0fc4c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 22:54:21 +0800 Subject: [PATCH 0367/2187] aio/native: Enhance exception handling for io_submit 1. Push back unsubmitted requests to aio_queue. 2. If failed to submit, failed the first request and retry rest. 3. If no err but no request submitted, exit the loop. Signed-off-by: Keqian Zhu --- util/src/aio/libaio.rs | 10 +++++--- util/src/aio/mod.rs | 55 +++++++++++++++++++++++++++++++----------- util/src/aio/raw.rs | 4 +-- util/src/aio/uring.rs | 5 ++-- 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 9ae5642d7..aab42fce9 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -107,13 +107,15 @@ impl LibaioContext { /// Implements the AioContext for libaio. impl AioContext for LibaioContext { /// Submit requests. - fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { + fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result { let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; - if ret < 0 { + if ret >= 0 { + return Ok(ret as usize); + } + if errno::errno().0 != libc::EAGAIN { bail!("Failed to submit aio, return {}.", ret); } - - Ok(()) + Ok(0) } /// Get the IO events. diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 87f876783..ff0ae9979 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -19,6 +19,7 @@ use std::marker::{Send, Sync}; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; +use log::error; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; @@ -47,8 +48,8 @@ pub const AIO_NATIVE: &str = "native"; /// The trait for Asynchronous IO operation. trait AioContext { - /// Submit IO requests to the OS. - fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()>; + /// Submit IO requests to the OS, the nr submitted is returned. + fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result; /// Get the IO events of the requests sumbitted earlier. fn get_events(&mut self) -> (&[IoEvent], usize, usize); } @@ -149,11 +150,12 @@ impl Aio { } // Drop reference of 'ctx', so below 'process_list' can work. drop(ctx); - self.process_list().map(|_v| Ok(done))? + self.process_list(); + Ok(done) } - fn process_list(&mut self) -> Result<()> { - if self.aio_in_queue.len > 0 && self.aio_in_flight.len < self.max_events { + fn process_list(&mut self) { + while self.aio_in_queue.len > 0 && self.aio_in_flight.len < self.max_events { let mut iocbs = Vec::new(); for _ in self.aio_in_flight.len..self.max_events { @@ -167,16 +169,41 @@ impl Aio { } } - if !iocbs.is_empty() { - return self - .ctx - .lock() - .unwrap() - .submit(iocbs.len() as i64, &mut iocbs); + // The iocbs must not be empty. + let (nr, is_err) = match self + .ctx + .lock() + .unwrap() + .submit(iocbs.len() as i64, &mut iocbs) + .map_err(|e| { + error!("{}", e); + e + }) { + Ok(nr) => (nr, false), + Err(_) => (0, true), + }; + + // Push back unsubmitted requests. This should rarely happen, so the + // trade off is acceptable. + let mut index = nr; + while index < iocbs.len() { + if let Some(node) = self.aio_in_flight.pop_head() { + self.aio_in_queue.add_tail(node); + } + index += 1; } - } - Ok(()) + if is_err { + // Fail one request, retry the rest. + if let Some(node) = self.aio_in_queue.pop_tail() { + (self.complete_func)(&(*node).value, -1); + } + } else if nr == 0 { + // If can't submit any request, break the loop + // and the method handle() will try again. + break; + } + } } pub fn rw_aio(&mut self, cb: AioCb, sector_size: u64, direct: bool) -> Result<()> { @@ -216,7 +243,7 @@ impl Aio { self.aio_in_queue.add_head(node); if last_aio || self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { - return self.process_list(); + self.process_list(); } Ok(()) diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 816d76eb6..3fa8d5af6 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -24,7 +24,7 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result } } if ret < 0 || ret as usize != size { - bail!("Failed to pread for {}, return {}.", fd, ret); + bail!("Failed to pread for {}, size {} return {}.", fd, size, ret); } Ok(ret) @@ -39,7 +39,7 @@ pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result } } if ret < 0 || ret as usize != size { - bail!("Failed to pwrite for {}, return {}.", fd, ret); + bail!("Failed to pwrite for {}, size {} return {}.", fd, size, ret); } Ok(ret) diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 6e84ee9ca..d63c2df53 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -52,7 +52,7 @@ impl IoUringContext { impl AioContext for IoUringContext { #[allow(clippy::zero_ptr)] /// Submit requests to OS. - fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result<()> { + fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result { for iocb in iocbp.iter() { let offset = unsafe { (*(*iocb)).aio_offset as libc::off_t }; let node = unsafe { (*(*iocb)).data as *mut CbNode }; @@ -91,8 +91,7 @@ impl AioContext for IoUringContext { } self.ring .submit_and_wait(nr as usize) - .with_context(|| "Failed to submit sqe")?; - Ok(()) + .with_context(|| "Failed to submit sqe") } /// Get the events. -- Gitee From 9a448b1d263f271cba130f091c291b8de3a7805a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 21:05:37 +0800 Subject: [PATCH 0368/2187] aio/native: Retrieve all avail aio events 1. We must retrieve all avial aio events even if the events tail wrapped, otherwise guest may hang up as some aio requests are not handled. 2. We should copy events, because after we changed the aio_ring head, the events may will be modified by kernel. Signed-off-by: Keqian Zhu --- util/src/aio/libaio.rs | 29 +++++++++++++++++++++++------ util/src/aio/mod.rs | 18 +++++++----------- util/src/aio/uring.rs | 4 ++-- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index aab42fce9..9790697c7 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::atomic::{fence, Ordering}; + use super::{AioContext, IoEvent, Result}; use anyhow::bail; use kvm_bindings::__IncompleteArrayField; @@ -64,6 +66,7 @@ pub struct EventResult { pub struct LibaioContext { pub ctx: *mut IoContext, + pub events: Vec, pub max_size: i32, } @@ -100,7 +103,11 @@ impl LibaioContext { bail!("Failed to setup aio context, return {}.", ret); } - Ok(LibaioContext { ctx, max_size }) + Ok(LibaioContext { + ctx, + events: Vec::with_capacity(max_size as usize), + max_size, + }) } } @@ -119,20 +126,30 @@ impl AioContext for LibaioContext { } /// Get the IO events. - fn get_events(&mut self) -> (&[IoEvent], usize, usize) { + fn get_events(&mut self) -> &[IoEvent] { let ring = self.ctx as *mut AioRing; let head = unsafe { (*ring).head }; let tail = unsafe { (*ring).tail }; let ring_nr = unsafe { (*ring).nr }; + let io_events: &[IoEvent] = unsafe { (*ring).io_events.as_slice(ring_nr as usize) }; + let nr = if tail >= head { tail - head } else { - ring_nr - head + ring_nr - head + tail }; - unsafe { (*ring).head = (head + nr) % ring_nr }; - let io_events: &[IoEvent] = unsafe { (*ring).io_events.as_slice(ring_nr as usize) }; + // Avoid speculatively loading ring.io_events before observing tail. + fence(Ordering::Acquire); + self.events.clear(); + for i in head..(head + nr) { + self.events.push(io_events[(i % ring_nr) as usize].clone()); + } + + // Avoid head is updated before we consume all io_events. + fence(Ordering::Release); + unsafe { (*ring).head = tail }; - (io_events, head as usize, (head + nr) as usize) + &self.events } } diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index ff0ae9979..47f713e14 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -33,7 +33,7 @@ type CbNode = Node>; #[repr(C)] #[allow(non_camel_case_types)] -#[derive(Default)] +#[derive(Default, Clone)] pub struct IoEvent { pub data: u64, pub obj: u64, @@ -51,7 +51,7 @@ trait AioContext { /// Submit IO requests to the OS, the nr submitted is returned. fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result; /// Get the IO events of the requests sumbitted earlier. - fn get_events(&mut self) -> (&[IoEvent], usize, usize); + fn get_events(&mut self) -> &[IoEvent]; } pub type AioCompleteFunc = Box, i64) + Sync + Send>; @@ -126,19 +126,15 @@ impl Aio { pub fn handle(&mut self) -> Result { let mut done = false; let mut ctx = self.ctx.lock().unwrap(); - let (evts, start, end) = ctx.get_events(); - for index in start..end { + for evt in ctx.get_events() { unsafe { - let node = evts[index].data as *mut CbNode; - - let res = if evts[index].res2 == 0 - && evts[index].res > 0 - && evts[index].res as u64 == (*node).value.nbytes - { + let node = evt.data as *mut CbNode; + let res = if (evt.res2 == 0) && (evt.res == (*node).value.nbytes as i64) { done = true; - evts[index].res + evt.res } else { + error!("Async IO request failed, res2 {} res {}", evt.res2, evt.res); -1 }; diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index d63c2df53..621aa9e77 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -95,7 +95,7 @@ impl AioContext for IoUringContext { } /// Get the events. - fn get_events(&mut self) -> (&[IoEvent], usize, usize) { + fn get_events(&mut self) -> &[IoEvent] { let mut queue = self.ring.completion(); self.events.clear(); let l = queue.len(); @@ -113,6 +113,6 @@ impl AioContext for IoUringContext { } } } - (&self.events, 0, self.events.len()) + &self.events } } -- Gitee From d928d263624b7d21d33266d67af452cc88b4cc73 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 20 Nov 2022 13:50:48 +0800 Subject: [PATCH 0369/2187] aio/misalign: Fix misalign rw handler 1. The allocated buffer is not checked whether is null. 2. There is memory leak if raw_read/raw_write failed. 3. Handle iov one by one has two problems as below, fix that by do rd/wr in one pass. 1) The individual iov may still misaligned. 2) It's inefficient. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 148 +++++++++++++++++++++++++++++++------------- virtio/src/block.rs | 10 +-- virtio/src/lib.rs | 35 +---------- 3 files changed, 113 insertions(+), 80 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 47f713e14..0b285eea9 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -15,6 +15,8 @@ mod raw; mod uring; use std::clone::Clone; +use std::cmp; +use std::io::Write; use std::marker::{Send, Sync}; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; @@ -23,7 +25,7 @@ use log::error; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; -use anyhow::Result; +use anyhow::{bail, Context, Result}; pub use libaio::*; pub use raw::*; use uring::IoUringContext; @@ -280,52 +282,67 @@ impl Aio { match cb.opcode { IoCmd::Preadv => { - let mut off = cb.offset; - for iov in cb.iovec.iter() { - // Safe because we allocate aligned memory and free it later. - // Alignment is set to host page size to decrease the count of allocated pages. - let aligned_buffer = - unsafe { libc::memalign(host_page_size as usize, iov.iov_len as usize) }; - ret = raw_read(cb.file_fd, aligned_buffer as u64, iov.iov_len as usize, off)?; - off += iov.iov_len as usize; - - let dst = unsafe { - std::slice::from_raw_parts_mut( - iov.iov_base as *mut u8, - iov.iov_len as usize, - ) - }; - let src = unsafe { - std::slice::from_raw_parts( - aligned_buffer as *const u8, - iov.iov_len as usize, - ) - }; - dst.copy_from_slice(src); + // Safe because we allocate aligned memory and free it later. + // Alignment is set to host page size to decrease the count of allocated pages. + let aligned_buffer = + unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; + if aligned_buffer.is_null() { + bail!("Failed to alloc memory for misaligned read"); + } + ret = raw_read( + cb.file_fd, + aligned_buffer as u64, + cb.nbytes as usize, + cb.offset, + ) + .map_err(|e| { // Safe because the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; - } + e + })?; + + let src = unsafe { + std::slice::from_raw_parts(aligned_buffer as *const u8, cb.nbytes as usize) + }; + iov_from_buf_direct(&cb.iovec, src).and_then(|v| { + if v == cb.nbytes as usize { + Ok(()) + } else { + unsafe { libc::free(aligned_buffer) }; + bail!("Failed to copy buff to iovs") + } + })?; + unsafe { libc::free(aligned_buffer) }; } IoCmd::Pwritev => { - let mut off = cb.offset; - for iov in cb.iovec.iter() { - let aligned_buffer = - unsafe { libc::memalign(host_page_size as usize, iov.iov_len as usize) }; - let dst = unsafe { - std::slice::from_raw_parts_mut( - aligned_buffer as *mut u8, - iov.iov_len as usize, - ) - }; - let src = unsafe { - std::slice::from_raw_parts(iov.iov_base as *const u8, iov.iov_len as usize) - }; - dst.copy_from_slice(src); - - ret = raw_write(cb.file_fd, aligned_buffer as u64, iov.iov_len as usize, off)?; - off += iov.iov_len as usize; - unsafe { libc::free(aligned_buffer) }; + let aligned_buffer = + unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; + if aligned_buffer.is_null() { + bail!("Failed to alloc memory for misaligned write"); } + let dst = unsafe { + std::slice::from_raw_parts_mut(aligned_buffer as *mut u8, cb.nbytes as usize) + }; + iov_to_buf_direct(&cb.iovec, dst).and_then(|v| { + if v == cb.nbytes as usize { + Ok(()) + } else { + unsafe { libc::free(aligned_buffer) }; + bail!("Failed to copy iovs to buff") + } + })?; + + ret = raw_write( + cb.file_fd, + aligned_buffer as u64, + cb.nbytes as usize, + cb.offset, + ) + .map_err(|e| { + unsafe { libc::free(aligned_buffer) }; + e + })?; + unsafe { libc::free(aligned_buffer) }; } IoCmd::Fdsync => ret = raw_datasync(cb.file_fd)?, _ => {} @@ -335,3 +352,50 @@ impl Aio { Ok(()) } } + +fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { + let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; + (&mut slice) + .write(buf) + .with_context(|| format!("Failed to write buf to hva:{})", hva))?; + Ok(()) +} + +/// Write buf to iovec and return the writed number of bytes. +pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { + let mut start: usize = 0; + let mut end: usize = 0; + + for iov in iovec.iter() { + end = cmp::min(start + iov.iov_len as usize, buf.len()); + mem_from_buf(&buf[start..end], iov.iov_base)?; + if end >= buf.len() { + break; + } + start = end; + } + Ok(end) +} + +pub fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { + let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; + buf.write(slice) + .with_context(|| format!("Failed to read buf from hva:{})", hva))?; + Ok(()) +} + +/// Read iovec to buf and return the readed number of bytes. +pub fn iov_to_buf_direct(iovec: &[Iovec], buf: &mut [u8]) -> Result { + let mut start: usize = 0; + let mut end: usize = 0; + + for iov in iovec { + end = cmp::min(start + iov.iov_len as usize, buf.len()); + mem_to_buf(&mut buf[start..end], iov.iov_base)?; + if end >= buf.len() { + break; + } + start = end; + } + Ok(end) +} diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 617cf8848..7d243f24d 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -21,9 +21,9 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use super::{ - iov_discard_back, iov_discard_front, iov_from_buf_direct, iov_to_buf, report_virtio_error, - virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, virtio_has_feature, + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, @@ -42,7 +42,9 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::aio::{raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE}; +use util::aio::{ + iov_from_buf_direct, raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE, +}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 486f15b25..ce39cd45e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -61,15 +61,13 @@ pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; pub use virtio_pci::VirtioPciDevice; use std::cmp; -use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use anyhow::anyhow; use anyhow::bail; -use anyhow::Context; use machine_manager::config::ConfigCheck; -use util::aio::Iovec; +use util::aio::mem_to_buf; use util::num_ops::write_u32; use vmm_sys_util::eventfd::EventFd; @@ -385,37 +383,6 @@ pub fn report_virtio_error( } } -fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { - let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; - (&mut slice) - .write(buf) - .with_context(|| format!("Failed to write buf to hva:{})", hva))?; - Ok(()) -} - -/// Write buf to iovec and return the writed number of bytes. -pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { - let mut start: usize = 0; - let mut end: usize = 0; - - for iov in iovec.iter() { - end = cmp::min(start + iov.iov_len as usize, buf.len()); - mem_from_buf(&buf[start..end], iov.iov_base)?; - if end >= buf.len() { - break; - } - start = end; - } - Ok(end) -} - -fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { - let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; - buf.write(slice) - .with_context(|| format!("Failed to read buf from hva:{})", hva))?; - Ok(()) -} - /// Read iovec to buf and return the readed number of bytes. pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) -> Result { let mut start: usize = 0; -- Gitee From c3faeffb23e2421825ff66667c3bc93ea6a3ce68 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 15:05:35 +0800 Subject: [PATCH 0370/2187] aio: Do some code factor for aio 1. Omit unnecessary code when handle misaligned IO and init iocb. 2. Factor out flush op from rw methods to make code logic clear. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 43 ++++++++++++++++++------------------------- virtio/src/block.rs | 2 +- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 0b285eea9..ddc018f61 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -205,38 +205,27 @@ impl Aio { } pub fn rw_aio(&mut self, cb: AioCb, sector_size: u64, direct: bool) -> Result<()> { - let mut misaligned = false; if direct { for iov in cb.iovec.iter() { if iov.iov_base % sector_size != 0 || iov.iov_len % sector_size != 0 { - misaligned = true; - break; + return self.handle_misaligned_rw(cb); } } } - if direct && misaligned { - return self.handle_misaligned_aio(cb); - } - - let last_aio = cb.last_aio; - let opcode = cb.opcode; - let file_fd = cb.file_fd; - let iovec = (&*cb.iovec).as_ptr() as u64; - let sg_size = cb.iovec.len(); - let offset = cb.offset; - let mut node = Box::new(Node::new(cb)); - let iocb = IoCb { - aio_lio_opcode: opcode as u16, - aio_fildes: file_fd as u32, - aio_buf: iovec, - aio_nbytes: sg_size as u64, - aio_offset: offset as u64, + let mut iocb = IoCb { + aio_lio_opcode: cb.opcode as u16, + aio_fildes: cb.file_fd as u32, + aio_buf: (&*cb.iovec).as_ptr() as u64, + aio_nbytes: cb.iovec.len() as u64, + aio_offset: cb.offset as u64, aio_flags: IOCB_FLAG_RESFD, aio_resfd: self.fd.as_raw_fd() as u32, - data: (&mut (*node) as *mut CbNode) as u64, ..Default::default() }; + let last_aio = cb.last_aio; + let mut node = Box::new(Node::new(cb)); + iocb.data = (&mut (*node) as *mut CbNode) as u64; node.value.iocb = Some(Box::new(iocb)); self.aio_in_queue.add_head(node); @@ -267,7 +256,6 @@ impl Aio { } r } - IoCmd::Fdsync => raw_datasync(cb.file_fd)?, _ => -1, }; (self.complete_func)(&cb, ret); @@ -275,10 +263,10 @@ impl Aio { Ok(()) } - fn handle_misaligned_aio(&mut self, cb: AioCb) -> Result<()> { + fn handle_misaligned_rw(&mut self, cb: AioCb) -> Result<()> { // Safe because we only get the host page size. let host_page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u64; - let mut ret = 0_i64; + let mut ret = -1; match cb.opcode { IoCmd::Preadv => { @@ -344,13 +332,18 @@ impl Aio { })?; unsafe { libc::free(aligned_buffer) }; } - IoCmd::Fdsync => ret = raw_datasync(cb.file_fd)?, _ => {} }; (self.complete_func)(&cb, ret); Ok(()) } + + pub fn flush_sync(&mut self, cb: AioCb) -> Result<()> { + let ret = raw_datasync(cb.file_fd)?; + (self.complete_func)(&cb, ret); + Ok(()) + } } fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 7d243f24d..d647bf666 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -334,7 +334,7 @@ impl Request { aiocb.opcode = IoCmd::Fdsync; (*aio) .as_mut() - .rw_sync(aiocb) + .flush_sync(aiocb) .with_context(|| "Failed to process block request for flushing")?; } VIRTIO_BLK_T_GET_ID => { -- Gitee From fb707ff92eebbc238d9420a76fe587433285e287 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 18:25:05 +0800 Subject: [PATCH 0371/2187] virtio-blk: Change log level for disk img not specified We preallocate 4 virtio-blk devices for micro VM, some of them may be hotplugged after VM started, which means the disk img is not specified until device is hotplugged. Before one blk device hotplugged, guest may try to send request to it to see whether it exists. So let's changed the log level to warn as it will occur under common case. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index d647bf666..f119370ab 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -534,7 +534,7 @@ impl BlockIoHandler { aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } } else { - error!("Failed to execute block request, disk_img not specified"); + warn!("Failed to execute block request, disk_img not specified"); aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } } -- Gitee From 70f2d185066c06cdb1276fa8b41c2b73a1c0a779 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 23:57:31 +0800 Subject: [PATCH 0372/2187] virtio-blk: Avoid file opened writable more than once A file should has only one writer, and if has writer, no reader is allowed. Signed-off-by: Keqian Zhu --- util/src/file.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ util/src/lib.rs | 1 + virtio/src/block.rs | 45 ++++++++++----------------------------------- 3 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 util/src/file.rs diff --git a/util/src/file.rs b/util/src/file.rs new file mode 100644 index 000000000..2834ed7ac --- /dev/null +++ b/util/src/file.rs @@ -0,0 +1,43 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::fs::{File, OpenOptions}; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::io::AsRawFd; + +use anyhow::{bail, Context, Result}; + +pub fn open_disk_file(path: &str, read_only: bool, direct: bool) -> Result { + let mut options = OpenOptions::new(); + options.read(true).write(!read_only); + if direct { + options.custom_flags(libc::O_DIRECT); + } + let file = options + .open(path) + .with_context(|| format!("failed to open the file for block {}", path))?; + + let (lockop, lockname) = if read_only { + (libc::LOCK_SH | libc::LOCK_NB, "read lock") + } else { + (libc::LOCK_EX | libc::LOCK_NB, "write lock") + }; + let ret = unsafe { libc::flock(file.as_raw_fd(), lockop) }; + if ret < 0 { + bail!( + "Failed to get {} on file: {}. Maybe it's used more than once.", + lockname, + path + ); + } + Ok(file) +} diff --git a/util/src/lib.rs b/util/src/lib.rs index de9b1ff34..bf0ee9f96 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -20,6 +20,7 @@ pub mod daemonize; pub mod device_tree; pub mod edid; pub mod error; +pub mod file; pub mod leak_bucket; mod link_list; pub mod logger; diff --git a/virtio/src/block.rs b/virtio/src/block.rs index f119370ab..55005145e 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -11,10 +11,9 @@ // See the Mulan PSL v2 for more details. use std::cmp; -use std::fs::{File, OpenOptions}; +use std::fs::File; use std::io::{Seek, SeekFrom, Write}; use std::mem::size_of; -use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::mpsc::{channel, Receiver, Sender}; @@ -46,6 +45,7 @@ use util::aio::{ iov_from_buf_direct, raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE, }; use util::byte_code::ByteCode; +use util::file::open_disk_file; use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -976,45 +976,20 @@ impl VirtioDevice for Block { self.state.config_space.num_queues = self.blk_cfg.queues; } + let mut disk_image = None; let mut disk_size = DUMMY_IMG_SIZE; - if !self.blk_cfg.path_on_host.is_empty() { - self.disk_image = None; - - let mut file = if self.blk_cfg.direct { - OpenOptions::new() - .read(true) - .write(!self.blk_cfg.read_only) - .custom_flags(libc::O_DIRECT) - .open(&self.blk_cfg.path_on_host) - .with_context(|| { - format!( - "failed to open the file by O_DIRECT for block {}", - self.blk_cfg.path_on_host - ) - })? - } else { - OpenOptions::new() - .read(true) - .write(!self.blk_cfg.read_only) - .open(&self.blk_cfg.path_on_host) - .with_context(|| { - format!( - "failed to open the file for block {}", - self.blk_cfg.path_on_host - ) - })? - }; - + let mut file = open_disk_file( + &self.blk_cfg.path_on_host, + self.blk_cfg.read_only, + self.blk_cfg.direct, + )?; disk_size = file.seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for block")? as u64; - - self.disk_image = Some(Arc::new(file)); - } else { - self.disk_image = None; + disk_image = Some(Arc::new(file)); } - + self.disk_image = disk_image; self.disk_sectors = disk_size >> SECTOR_SHIFT; self.state.config_space.capacity = self.disk_sectors; -- Gitee From 31804932669d3a51fa5535f44d460123d19d8d8a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 14:34:24 +0800 Subject: [PATCH 0373/2187] virtio-blk: Add limit to number of reqs can be merged Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 55005145e..50816d562 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -63,6 +63,8 @@ const SECTOR_SHIFT: u8 = 9; const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; /// Size of the dummy block device. const DUMMY_IMG_SIZE: u64 = 0; +/// Number of max merged requests. +const MAX_NUM_MERGE_REQS: u16 = 32; type SenderConfig = (Option>, u64, Option, bool, Option); @@ -427,6 +429,7 @@ impl BlockIoHandler { let mut merge_req_queue = Vec::::new(); let mut last_req: Option<&mut Request> = None; + let mut merged_reqs = 1; *last_aio_index = 0; for req in req_queue { @@ -434,7 +437,8 @@ impl BlockIoHandler { || req.out_header.request_type == VIRTIO_BLK_T_OUT; let can_merge = match last_req { Some(ref req_ref) => { - io && req_ref.out_header.request_type == req.out_header.request_type + io && merged_reqs < MAX_NUM_MERGE_REQS + && req_ref.out_header.request_type == req.out_header.request_type && (req_ref.out_header.sector + req_ref.get_req_sector_num() == req.out_header.sector) } @@ -445,12 +449,14 @@ impl BlockIoHandler { let last_req_raw = last_req.unwrap(); last_req_raw.next = Box::new(Some(req)); last_req = last_req_raw.next.as_mut().as_mut(); + merged_reqs += 1; } else { if io { *last_aio_index = merge_req_queue.len(); } merge_req_queue.push(req); last_req = merge_req_queue.last_mut(); + merged_reqs = 1; } } -- Gitee From 87c21692ff1793ffb2aad519aed44e46a0f11400 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 12 Nov 2022 18:36:44 +0800 Subject: [PATCH 0374/2187] virtio-blk: Fix infinite process loop if IO throttled Do not try to process all avail request if IO throttled, otherwise it causes the IO thread stuck. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 50816d562..a168e73f2 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -575,6 +575,15 @@ impl BlockIoHandler { self.driver_features, false, )?; + + // See whether we have been throttled. + if let Some(lb) = self.leak_bucket.as_mut() { + if let Some(ctx) = EventLoop::get_ctx(self.iothread.as_ref()) { + if lb.throttled(ctx, 0) { + break; + } + } + } } Ok(done) } -- Gitee From d389998ace03112541b25a7567a3ec7eda81491f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 19 Nov 2022 23:32:52 +0800 Subject: [PATCH 0375/2187] virtio-blk: Avoid stuck IO thread when process queue 1. Make sure avail requests not exceeds queue size. 2. Limit the time for every round of process queue. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index a168e73f2..97f619aba 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -18,6 +18,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; +use std::time::Instant; use super::{ iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, virtio_has_feature, @@ -65,6 +66,8 @@ const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; const DUMMY_IMG_SIZE: u64 = 0; /// Number of max merged requests. const MAX_NUM_MERGE_REQS: u16 = 32; +/// Max time for every round of process queue. +const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; type SenderConfig = (Option>, u64, Option, bool, Option); @@ -508,6 +511,10 @@ impl BlockIoHandler { aiocompletecb.complete_request(status); continue; } + // Avoid bogus guest stuck IO thread. + if req_queue.len() >= queue.vring.actual_size() as usize { + bail!("The front driver may be damaged, avail requests more than queue size"); + } req_queue.push(req); done = true; } @@ -550,6 +557,8 @@ impl BlockIoHandler { fn process_queue_suppress_notify(&mut self) -> Result { let mut done = false; + let start_time = Instant::now(); + if !self.queue.lock().unwrap().is_enabled() { done = true; return Ok(done); @@ -562,6 +571,14 @@ impl BlockIoHandler { .avail_ring_len(&self.mem_space)? != 0 { + // Do not stuck IO thread. + let now = Instant::now(); + if (now - start_time).as_millis() > MAX_MILLIS_TIME_PROCESS_QUEUE as u128 { + // Make sure we can come back. + self.queue_evt.try_clone()?.write(1)?; + break; + } + self.queue.lock().unwrap().vring.suppress_queue_notify( &self.mem_space, self.driver_features, -- Gitee From 8722bb909d9055df78b8187c62d8032d939ecbad Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Nov 2022 13:06:49 +0800 Subject: [PATCH 0376/2187] virtio-scsi: set max_target to 255 according to virtio spec Virtio Spec: max_target should be less than or equal to 255. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- machine_manager/src/config/scsi.rs | 8 ++++++-- virtio/src/scsi/controller.rs | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index c93ae8615..4246215e6 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -779,7 +779,7 @@ Six properties can be set for virtio-scsi hd. * file: the path of backend image file. * id: unique device id. * bus: scsi bus name, only support $scsi_controller_name + ".0" -* scsi-id: id number (target) of scsi four level hierarchical address (host, channel, target, lun). Only support 0 now. +* scsi-id: id number (target) of scsi four level hierarchical address (host, channel, target, lun). Configuration range is [0, 255]. Boot scsi disk configuration range is [0, 31]. * lun: lun number (lun) of scsi four level hierarchical address (host, channel, target, lun). Only support starting from 0 now. * serial: serial number of virtio scsi device.(optional) diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 34048cdc7..094ba3cbf 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -15,6 +15,10 @@ use anyhow::{anyhow, Result}; use super::{error::ConfigError, pci_args_check}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; +/// According to Virtio Spec. +/// Max_target should be less than or equal to 255. +pub const VIRTIO_SCSI_MAX_TARGET: u16 = 255; + #[derive(Debug, Clone)] pub struct ScsiCntlrConfig { /// Virtio-scsi-pci device id. @@ -153,12 +157,12 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result } if let Some(target) = cmd_parser.get_value::("scsi-id")? { - if target != 0 { + if target > VIRTIO_SCSI_MAX_TARGET as u8 { return Err(anyhow!(ConfigError::IllegalValue( "scsi-id of scsi device".to_string(), 0, true, - 0, + VIRTIO_SCSI_MAX_TARGET as u64, true, ))); } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 0ae1d1746..dd5f8c612 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -28,7 +28,7 @@ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use log::{error, info}; use machine_manager::{ - config::{ConfigCheck, ScsiCntlrConfig}, + config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; use util::aio::{Aio, AioCb, AioCompleteFunc, Iovec}; @@ -167,6 +167,7 @@ impl VirtioDevice for ScsiCntlr { self.state.config_space.cmd_per_lun = 128; // seg_max: queue size - 2, 32 bit. self.state.config_space.seg_max = self.queue_size() as u32 - 2; + self.state.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) -- Gitee From 93fdb6ff0f376481e5f81b6756b06b93f814b59e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:15:28 +0800 Subject: [PATCH 0377/2187] virtio-net: correct the index which used to get the queue_pairs For VIRIOT_NET_CTRL_MQ: elem.out_iovec[0] means the ctrl header; elem.out_iovec[1] means the queue_pairs; In the Code, it gets the queue_pairs from elem.out_iovec[0]. So, correct it. Signed-off-by: Yan Wang --- virtio/src/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 7e9b5a491..fb7305eed 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -137,7 +137,7 @@ impl NetCtrlHandler { VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET ); } - if let Some(mq_desc) = elem.out_iovec.get(0) { + if let Some(mq_desc) = elem.out_iovec.get(1) { used_len += mq_desc.len; let queue_pairs = self .mem_space -- Gitee From 3895b502107fdf846959b937853ff14b46bfc396 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:25:11 +0800 Subject: [PATCH 0378/2187] virtio-net: correct the notification mechanism It should judge if notifing the Guest is needed when a request has been handled. Signed-off-by: Yan Wang --- virtio/src/net.rs | 75 ++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index fb7305eed..0caa8bc8d 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -169,14 +169,18 @@ impl NetCtrlHandler { .add_used(&self.mem_space, elem.index, used_len) .with_context(|| format!("Failed to add used ring {}", elem.index))?; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "ctrl", - VirtioInterruptType::Vring - )) - }, - )?; + if locked_queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "ctrl", + VirtioInterruptType::Vring + )) + })?; + } Ok(()) } @@ -259,7 +263,6 @@ impl TxVirtio { struct RxVirtio { queue_full: bool, - need_irqs: bool, queue: Arc>, queue_evt: EventFd, } @@ -268,7 +271,6 @@ impl RxVirtio { fn new(queue: Arc>, queue_evt: EventFd) -> Self { RxVirtio { queue_full: false, - need_irqs: false, queue, queue_evt, } @@ -358,20 +360,20 @@ impl NetIoHandler { elem.index, write_count ) })?; - self.rx.need_irqs = true; - } - if self.rx.need_irqs { - self.rx.need_irqs = false; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "net", - VirtioInterruptType::Vring - )) - }, - )?; - self.trace_send_interrupt("Net".to_string()); + if queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "net", + VirtioInterruptType::Vring + )) + })?; + self.trace_send_interrupt("Net".to_string()); + } } Ok(()) @@ -380,7 +382,6 @@ impl NetIoHandler { fn handle_tx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); - let mut need_irq = false; while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { if elem.desc_num == 0 { @@ -423,19 +424,19 @@ impl NetIoHandler { .add_used(&self.mem_space, elem.index, 0) .with_context(|| format!("Net tx: Failed to add used ring {}", elem.index))?; - need_irq = true; - } - - if need_irq { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "net", - VirtioInterruptType::Vring - )) - }, - )?; - self.trace_send_interrupt("Net".to_string()); + if queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "net", + VirtioInterruptType::Vring + )) + })?; + self.trace_send_interrupt("Net".to_string()); + } } Ok(()) -- Gitee From a4496b045bc571823b87e235f852a252ceead3b0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:30:21 +0800 Subject: [PATCH 0379/2187] virtio-net: output the error message when pop_avail() failed Currently, it uses "while let Ok(elem) = queue.vring.pop_avail.." to get the avail element, but it will lose the error message. Signed-off-by: Yan Wang --- virtio/src/net.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 0caa8bc8d..1e82eac77 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -383,7 +383,11 @@ impl NetIoHandler { self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); - while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + loop { + let elem = queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + .with_context(|| "Failed to pop avail ring for net tx")?; if elem.desc_num == 0 { break; } -- Gitee From 5951fbd035ad1b53ccd1054b59e13939716679f4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:40:36 +0800 Subject: [PATCH 0380/2187] virtio-net: handle all the avail element in the control queue Use loop to handle all the avail element in the control queue. Otherwise, it may leave some avail elements unhandled. Signed-off-by: Yan Wang --- virtio/src/net.rs | 118 +++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 1e82eac77..6d5ea7261 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -114,72 +114,74 @@ impl ByteCode for CrtlHdr {} impl NetCtrlHandler { fn handle_ctrl(&mut self) -> Result<()> { let mut locked_queue = self.ctrl.queue.lock().unwrap(); - let elem = locked_queue - .vring - .pop_avail(&self.mem_space, self.driver_features) - .with_context(|| "Failed to pop avail ring for net control queue")?; - if elem.desc_num == 0 { - return Ok(()); - } + loop { + let elem = locked_queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + .with_context(|| "Failed to pop avail ring for net control queue")?; + if elem.desc_num == 0 { + break; + } - let mut used_len = 0; - if let Some(ctrl_desc) = elem.out_iovec.get(0) { - used_len += ctrl_desc.len; - let ctrl_hdr = self - .mem_space - .read_object::(ctrl_desc.addr) - .with_context(|| "Failed to get control queue descriptor")?; - match ctrl_hdr.class as u16 { - VIRTIO_NET_CTRL_MQ => { - if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { + let mut used_len = 0; + if let Some(ctrl_desc) = elem.out_iovec.get(0) { + used_len += ctrl_desc.len; + let ctrl_hdr = self + .mem_space + .read_object::(ctrl_desc.addr) + .with_context(|| "Failed to get control queue descriptor")?; + match ctrl_hdr.class as u16 { + VIRTIO_NET_CTRL_MQ => { + if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { + bail!( + "Control queue header command can't match {}", + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + ); + } + if let Some(mq_desc) = elem.out_iovec.get(1) { + used_len += mq_desc.len; + let queue_pairs = self + .mem_space + .read_object::(mq_desc.addr) + .with_context(|| "Failed to read multi queue descriptor")?; + if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + .contains(&queue_pairs) + { + bail!("Invalid queue pairs {}", queue_pairs); + } + } + } + _ => { bail!( - "Control queue header command can't match {}", - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + "Control queue header class can't match {}", + VIRTIO_NET_CTRL_MQ ); } - if let Some(mq_desc) = elem.out_iovec.get(1) { - used_len += mq_desc.len; - let queue_pairs = self - .mem_space - .read_object::(mq_desc.addr) - .with_context(|| "Failed to read multi queue descriptor")?; - if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) - .contains(&queue_pairs) - { - bail!("Invalid queue pairs {}", queue_pairs); - } - } - } - _ => { - bail!( - "Control queue header class can't match {}", - VIRTIO_NET_CTRL_MQ - ); } } - } - if let Some(status) = elem.in_iovec.get(0) { - used_len += status.len; - let data = VIRTIO_NET_OK; - self.mem_space.write_object::(&data, status.addr)?; - } + if let Some(status) = elem.in_iovec.get(0) { + used_len += status.len; + let data = VIRTIO_NET_OK; + self.mem_space.write_object::(&data, status.addr)?; + } - locked_queue - .vring - .add_used(&self.mem_space, elem.index, used_len) - .with_context(|| format!("Failed to add used ring {}", elem.index))?; + locked_queue + .vring + .add_used(&self.mem_space, elem.index, used_len) + .with_context(|| format!("Failed to add used ring {}", elem.index))?; - if locked_queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "ctrl", - VirtioInterruptType::Vring - )) - })?; + if locked_queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "ctrl", + VirtioInterruptType::Vring + )) + })?; + } } Ok(()) -- Gitee From 95ffeb0472fd75157e21613597527ff45f490432 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:45:08 +0800 Subject: [PATCH 0381/2187] virtio-net: correct the length in Used elem when hanlde control queue The length in used element is the number of bytes written into the device writable portion of the buffer described by the descriptor chain. But, it also adds the bytes of device readable portion. Signed-off-by: Yan Wang --- virtio/src/net.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 6d5ea7261..2854dd74e 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -115,6 +115,7 @@ impl NetCtrlHandler { fn handle_ctrl(&mut self) -> Result<()> { let mut locked_queue = self.ctrl.queue.lock().unwrap(); loop { + let ack = VIRTIO_NET_OK; let elem = locked_queue .vring .pop_avail(&self.mem_space, self.driver_features) @@ -123,9 +124,7 @@ impl NetCtrlHandler { break; } - let mut used_len = 0; if let Some(ctrl_desc) = elem.out_iovec.get(0) { - used_len += ctrl_desc.len; let ctrl_hdr = self .mem_space .read_object::(ctrl_desc.addr) @@ -139,7 +138,6 @@ impl NetCtrlHandler { ); } if let Some(mq_desc) = elem.out_iovec.get(1) { - used_len += mq_desc.len; let queue_pairs = self .mem_space .read_object::(mq_desc.addr) @@ -160,14 +158,12 @@ impl NetCtrlHandler { } } if let Some(status) = elem.in_iovec.get(0) { - used_len += status.len; - let data = VIRTIO_NET_OK; - self.mem_space.write_object::(&data, status.addr)?; + self.mem_space.write_object::(&ack, status.addr)?; } locked_queue .vring - .add_used(&self.mem_space, elem.index, used_len) + .add_used(&self.mem_space, elem.index, mem::size_of_val(&ack) as u32) .with_context(|| format!("Failed to add used ring {}", elem.index))?; if locked_queue -- Gitee From 53dae8d127ce0e5d7b340c9b413552f0defc5e74 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:46:34 +0800 Subject: [PATCH 0382/2187] virtio-net: use the same EventSet when resuming an event notifier In the code, it adds an event notifier with event set "IN | EDGE_TRIGGERED". So, it also should use the same event set when parking the event notifier. Signed-off-by: Yan Wang --- virtio/src/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 2854dd74e..ea1a0791b 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -592,7 +592,7 @@ impl EventNotifierHelper for NetIoHandler { NotifierOperation::Resume, tap.as_raw_fd(), None, - EventSet::IN, + EventSet::IN | EventSet::EDGE_TRIGGERED, Vec::new(), )]; locked_net_io.is_listening = true; -- Gitee From 56e6f13aaa592d90faed676d8b210cc8ae676da8 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 14 Nov 2022 14:47:59 +0800 Subject: [PATCH 0383/2187] virtio-net: report error when failed to convert gpa to hva When failed to convert the address in rx queue element, it should report virtio error. Signed-off-by: Yan Wang --- virtio/src/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index ea1a0791b..815212319 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -317,7 +317,7 @@ impl NetIoHandler { }; iovecs.push(iovec); } else { - error!("Failed to get host address for {}", elem_iov.addr.0); + bail!("Failed to get host address for {}", elem_iov.addr.0); } } let write_count = unsafe { -- Gitee From 1a0186921773e13cf4a716fc5990e0fc012c1947 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 14 Nov 2022 20:04:03 +0800 Subject: [PATCH 0384/2187] aio: Some fixes for compile and syscall whitelist 1. Add io_destroy to syscall whitelist. 2. Explicitly drop Box. 3. Use errno version 0.2.8. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/syscall.rs | 1 + machine/src/standard_vm/aarch64/syscall.rs | 1 + machine/src/standard_vm/x86_64/syscall.rs | 1 + util/Cargo.toml | 2 +- util/src/aio/mod.rs | 2 +- vhost_user_fs/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 3 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index f439ce83d..8b41f52ad 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -66,6 +66,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_wait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), + BpfRule::new(libc::SYS_io_destroy), BpfRule::new(libc::SYS_io_uring_enter), BpfRule::new(libc::SYS_io_uring_setup), BpfRule::new(libc::SYS_io_uring_register), diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index f93db530f..8e5752c75 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -69,6 +69,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_pwait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), + BpfRule::new(libc::SYS_io_destroy), BpfRule::new(libc::SYS_io_uring_setup), BpfRule::new(libc::SYS_io_uring_register), BpfRule::new(libc::SYS_io_uring_enter), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index f69dfca45..acf3be4b0 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -71,6 +71,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_wait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), + BpfRule::new(libc::SYS_io_destroy), BpfRule::new(libc::SYS_io_uring_setup), BpfRule::new(libc::SYS_io_uring_register), BpfRule::new(libc::SYS_io_uring_enter), diff --git a/util/Cargo.toml b/util/Cargo.toml index 4ae17cf22..4cd2617ca 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -19,4 +19,4 @@ vmm-sys-util = ">=0.10.0" byteorder = "1.4.3" once_cell = "1.13.0" io-uring = "0.5.7" -errno = "0.2.7" +errno = "0.2.8" diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index ddc018f61..083f15805 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -143,7 +143,7 @@ impl Aio { (self.complete_func)(&(*node).value, res); self.aio_in_flight.unlink(&(*node)); // Construct Box to free mem automatically. - Box::from_raw(node); + drop(Box::from_raw(node)); } } // Drop reference of 'ctx', so below 'process_list' can work. diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 68b0ed6eb..18aa273d0 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -8,7 +8,7 @@ description = "Provide virtio fs for VM" [dependencies] capng = "0.2.2" -errno = "0.2.7" +errno = "0.2.8" log = "0.4.8" libc = ">=0.2.71" thiserror = "1.0" -- Gitee From 2c8f2b6580eb5aa6dbf7f963135d45aa44a2c1c9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 15 Nov 2022 01:11:18 +0800 Subject: [PATCH 0385/2187] syscall: Add flock syscall to whitelist flock is used in commit "70f2d185"(virtio-blk: Avoid file opened writable more than once). Signed-off-by: Keqian Zhu --- machine/src/micro_vm/syscall.rs | 1 + machine/src/standard_vm/aarch64/syscall.rs | 1 + machine/src/standard_vm/x86_64/syscall.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 8b41f52ad..ed124dab2 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -85,6 +85,7 @@ pub fn syscall_whitelist() -> Vec { .add_constraint(SeccompCmpOpt::Eq, 1, F_DUPFD_CLOEXEC) .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFD) .add_constraint(SeccompCmpOpt::Eq, 1, F_GETFD), + BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), #[cfg(target_arch = "x86_64")] BpfRule::new(libc::SYS_open), diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 8e5752c75..8802d19de 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -91,6 +91,7 @@ pub fn syscall_whitelist() -> Vec { .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFD) .add_constraint(SeccompCmpOpt::Eq, 1, F_GETFD) .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFL), + BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), BpfRule::new(libc::SYS_openat), BpfRule::new(libc::SYS_sigaltstack), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index acf3be4b0..2a76519a6 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -93,6 +93,7 @@ pub fn syscall_whitelist() -> Vec { .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFD) .add_constraint(SeccompCmpOpt::Eq, 1, F_GETFD) .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFL), + BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), BpfRule::new(libc::SYS_open), BpfRule::new(libc::SYS_openat), -- Gitee From beab9f8aa8e99c4228002aee05956ccf228dfebe Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 14 Nov 2022 20:23:26 +0800 Subject: [PATCH 0386/2187] direct boot: boot directly from kernel and reduce startup time This patch add a way to boot directly from kernel for ARM architecture. It can bypass UEFI boot to reduce bootup time. Signed-off-by: Xinle.Guo --- machine/src/lib.rs | 12 +++++++--- machine/src/standard_vm/aarch64/mod.rs | 31 +++++++++++++++++--------- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 18 ++++++++------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 69aa0aeb0..25abaf570 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -26,6 +26,7 @@ use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; use kvm_ioctls::VcpuFd; +use log::warn; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub use micro_vm::LightMachine; @@ -483,8 +484,8 @@ pub trait MachineOps { fn get_sys_bus(&mut self) -> &SysBus; - fn get_fwcfg_dev(&mut self) -> Result>> { - bail!("No FwCfg deivce found"); + fn get_fwcfg_dev(&mut self) -> Option>> { + None } fn get_boot_order_list(&self) -> Option>>> { @@ -540,8 +541,13 @@ pub trait MachineOps { } fwcfg_boot_order_string.push('\0'); - let fwcfg = self.get_fwcfg_dev()?; + let fwcfg = self.get_fwcfg_dev(); + if fwcfg.is_none() { + warn!("Direct kernel boot mode don't support set boot order"); + return Ok(()); + } fwcfg + .unwrap() .lock() .unwrap() .modify_file_entry("bootorder", fwcfg_boot_order_string.as_bytes().to_vec()) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index ea8472556..d2cad0af7 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -313,7 +313,11 @@ impl StdMachineOps for StdMachine { Ok(()) } - fn add_fwcfg_device(&mut self, nr_cpus: u8) -> StdResult>> { + fn add_fwcfg_device(&mut self, nr_cpus: u8) -> StdResult>>> { + if self.vm_config.lock().unwrap().pflashs.is_none() { + return Ok(None); + } + let mut fwcfg = FwCfgMem::new(self.sys_mem.clone()); fwcfg .add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec()) @@ -349,7 +353,7 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to realize fwcfg device")?; self.fwcfg_dev = Some(fwcfg_dev.clone()); - Ok(fwcfg_dev) + Ok(Some(fwcfg_dev)) } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { @@ -505,7 +509,7 @@ impl MachineOps for StdMachine { let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(Some(&fwcfg))?) + Some(locked_vm.load_boot_source(fwcfg.as_ref())?) } else { None }; @@ -547,9 +551,10 @@ impl MachineOps for StdMachine { })?; } - if migrate.0 == MigrateMode::Unknown { + // If it is direct kernel boot mode, the ACPI can not be enabled. + if migrate.0 == MigrateMode::Unknown && fwcfg.is_some() { locked_vm - .build_acpi_tables(&fwcfg) + .build_acpi_tables(&fwcfg.unwrap()) .with_context(|| "Failed to create ACPI tables")?; } @@ -596,10 +601,14 @@ impl MachineOps for StdMachine { #[cfg(not(target_env = "musl"))] fn add_ramfb(&mut self) -> Result<()> { + let fwcfg_dev = self.get_fwcfg_dev(); + if fwcfg_dev.is_none() { + bail!("Ramfb device must be used UEFI to boot, please add pflash devices"); + } + let sys_mem = self.get_sys_mem(); let mut ramfb = Ramfb::new(sys_mem.clone()); - let locked_fwcfg = self.get_fwcfg_dev()?; - ramfb.ramfb_state.setup(&locked_fwcfg)?; + ramfb.ramfb_state.setup(&fwcfg_dev.unwrap())?; ramfb.realize(&mut self.sysbus)?; Ok(()) } @@ -632,9 +641,11 @@ impl MachineOps for StdMachine { &self.sysbus } - fn get_fwcfg_dev(&mut self) -> Result>> { - // Unwrap is safe. Because after standard machine realize, this will not be None.F - Ok(self.fwcfg_dev.clone().unwrap()) + fn get_fwcfg_dev(&mut self) -> Option>> { + if let Some(fwcfg_dev) = &self.fwcfg_dev { + return Some(fwcfg_dev.clone()); + } + None } fn get_boot_order_list(&self) -> Option>>> { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 0078a5101..05d6fd196 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -168,7 +168,7 @@ trait StdMachineOps: AcpiBuilder { Ok(()) } - fn add_fwcfg_device(&mut self, _nr_cpus: u8) -> Result>> { + fn add_fwcfg_device(&mut self, _nr_cpus: u8) -> Result>>> { bail!("Not implemented"); } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 6c100f8ed..1c370ea14 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -296,7 +296,7 @@ impl StdMachineOps for StdMachine { Ok(()) } - fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::Result>> { + fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::Result>>> { let mut fwcfg = FwCfgIO::new(self.sys_mem.clone()); fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, nr_cpus.as_bytes().to_vec())?; @@ -311,7 +311,7 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to realize fwcfg device")?; self.fwcfg_dev = Some(fwcfg_dev.clone()); - Ok(fwcfg_dev) + Ok(Some(fwcfg_dev)) } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { @@ -453,7 +453,7 @@ impl MachineOps for StdMachine { let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(Some(&fwcfg))?) + Some(locked_vm.load_boot_source(fwcfg.as_ref())?) } else { None }; @@ -470,9 +470,9 @@ impl MachineOps for StdMachine { &boot_config, )?); - if migrate.0 == MigrateMode::Unknown { + if migrate.0 == MigrateMode::Unknown && fwcfg.is_some() { locked_vm - .build_acpi_tables(&fwcfg) + .build_acpi_tables(&fwcfg.unwrap()) .with_context(|| "Failed to create ACPI tables")?; } @@ -590,9 +590,11 @@ impl MachineOps for StdMachine { &self.sysbus } - fn get_fwcfg_dev(&mut self) -> Result>> { - // Unwrap is safe. Because after standard machine realize, this will not be None.F - Ok(self.fwcfg_dev.clone().unwrap()) + fn get_fwcfg_dev(&mut self) -> Option>> { + if let Some(fwcfg_dev) = &self.fwcfg_dev { + return Some(fwcfg_dev.clone()); + } + None } fn get_boot_order_list(&self) -> Option>>> { -- Gitee From c725baef44f1f8a0bf1d0ae76d9c58e61c474d35 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 14 Nov 2022 21:00:56 +0800 Subject: [PATCH 0387/2187] direct boot: add documents for using direct boot By using direct kernel boot, it can provides a faster way to startup virtual machine. The UEFI and ACPI table will not be used. Signed-off-by: Xinle.Guo --- docs/boot.ch.md | 24 +++++++++++++++++++++++- docs/boot.md | 24 +++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/boot.ch.md b/docs/boot.ch.md index 774e4a81c..5b95bb099 100644 --- a/docs/boot.ch.md +++ b/docs/boot.ch.md @@ -197,7 +197,29 @@ $ qemu-img convert -f qcow2 -O raw openEuler-21.03-x86_64.qcow2 openEuler-21.03- 至此就获得了可以使用的 raw 格式镜像。 -### 4. 启动命令行样例 +### 4. 以 direct kernel boot 方式启动标准虚拟机 + +为virt虚机主板提供直接从kernel启动的模式。在该模式下,不需要UEFI和APCI表, +虚拟机将跳过UEFI启动阶段,直接从kernel启动,从而加快启动速度。 + +启动命令行如下: + +```shell +/usr/bin/stratovirt \ + -machine virt \ + -kernel /path/to/kernel \ + -smp 1 \ + -m 2G \ + -append "console=${con} reboot=k panic=1 root=/dev/vda rw" \ + -drive file=/path/to/rootfs,id=rootfs,readonly=off,direct=off \ + -device virtio-blk-pci,drive=rootfs,id=blk1,bus=pcie.0,addr=0x2 \ + -qmp unix:/path/to/socket,server,nowait \ + -serial stdio +``` + +说明:当前只支持ARM架构下virt虚机主板快速启动标准虚拟机。 + +### 5. 启动命令行样例 请注意,标准虚拟机需要两个PFlash设备,它们将使用来自与EDK2二进制的两个固件文件。 如果你不需要保持启动信息,单元序列为1的数据存储文件可以被省略。但是单元序号为0的 diff --git a/docs/boot.md b/docs/boot.md index 39d5495e4..ac7dcd715 100644 --- a/docs/boot.md +++ b/docs/boot.md @@ -202,7 +202,29 @@ $ qemu-img convert -f qcow2 -O raw openEuler-21.03-x86_64.qcow2 openEuler-21.03- Now the available raw image is obtained. -### 4. Boot command line sample +### 4. Boot with kernel directly + +It can directly boot from kernel. In this mode, UEFI and ACPI will not be used. And VM will skip the UEFI, directly start the kernel to reduce boot +up time. + +Run the following commands to direct boot VM from kernel: + +```shell +/usr/bin/stratovirt \ + -machine virt \ + -kernel /path/to/kernel \ + -smp 1 \ + -m 2G \ + -append "console=${con} reboot=k panic=1 root=/dev/vda rw" \ + -drive file=/path/to/rootfs,id=rootfs,readonly=off,direct=off \ + -device virtio-blk-pci,drive=rootfs,id=blk1,bus=pcie.0,addr=0x2 \ + -qmp unix:/path/to/socket,server,nowait \ + -serial stdio +``` + +Note: This mode currently only supports arm architecture. + +### 5. Boot command line sample Note that standard need two PFlash devices which will use two firmware files from EDK II binary. If you don't need to store boot information, data storage file can -- Gitee From 6eb41b7bc17fb3bd48ce6fe7e31160a2fefea4e0 Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Tue, 15 Nov 2022 16:37:23 +0800 Subject: [PATCH 0388/2187] test_microvm_balloon: Modify comments in functions Because the annotation does not accurately describe the test case 814748368 is changed to 814743552. Signed-off-by: Zuo Xiaoxian --- .../testcases/microvm/functional/test_microvm_balloon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py b/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py index eca35c54f..4602a8996 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_balloon.py @@ -61,7 +61,7 @@ def test_microvm_balloon(microvm): steps: 1) launch microvm with argument: "-balloon deflate-on-oom=true". 2) query memory size, and save. - 3) set memory size through balloon device to 814748368. + 3) set memory size through balloon device to 814743552. 4) wait 5 seconds for ballooning. 5) check if the memory size is less than 2524971008. 6) set memory size through balloon device to 2524971008, and wait. -- Gitee From 867c34fb8b226adb760f4c2e7c5b037c0043f6d2 Mon Sep 17 00:00:00 2001 From: Zuo Xiaoxian Date: Tue, 15 Nov 2022 17:08:46 +0800 Subject: [PATCH 0389/2187] test_standvm_balloon: Fix the value of the setting balloon Since the value of balloon must be an integer multiple of the size of the guest memory page, set the balloon value in the testcase to be an integer multiple of 64KB to meet the guest requirements of 4KB, 16KB and 64KB. Signed-off-by: chenhuiying --- .../testcases/standvm/functional/test_standvm_balloon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py b/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py index f40fea254..a1a2b5829 100644 --- a/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_balloon.py @@ -61,7 +61,7 @@ def test_standvm_balloon(standvm): steps: 1) launch standvm with argument: "-balloon deflate-on-oom=true". 2) query memory size, and save. - 3) set memory size through balloon device to 814748368. + 3) set memory size through balloon device to 814743552. 4) wait 5 seconds for ballooning. 5) check if the memory size is less than 2524971008. 6) set memory size through balloon device to 2524971008, and wait. @@ -76,7 +76,7 @@ def test_standvm_balloon(standvm): resp = test_vm.query_balloon() ori = int(resp["return"]["actual"]) - resp = test_vm.balloon_set(value=814748368) + resp = test_vm.balloon_set(value=814743552) time.sleep(5) test_vm.event_wait(name='BALLOON_CHANGED', timeout=2.0) resp = test_vm.query_balloon() -- Gitee From 63d22c58520940703dac4cf5e60ad961594e12a2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 18:49:41 +0800 Subject: [PATCH 0390/2187] usb: the bus and port parameter is added to adapt to libvirt The bus and port parameter is added for usb-kbd and usb-tablet to adapt to libvirt. Signed-off-by: zhouli57 --- machine_manager/src/config/usb.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 5d0d8ce0f..902223126 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -67,7 +67,7 @@ impl ConfigCheck for UsbKeyboardConfig { pub fn parse_usb_keyboard(conf: &str) -> Result { let mut cmd_parser = CmdParser::new("usb-kbd"); - cmd_parser.push("").push("id"); + cmd_parser.push("").push("id").push("bus").push("port"); cmd_parser.parse(conf)?; let mut dev = UsbKeyboardConfig::new(); if let Some(id) = cmd_parser.get_value::("id")? { @@ -98,7 +98,7 @@ impl ConfigCheck for UsbTabletConfig { pub fn parse_usb_tablet(conf: &str) -> Result { let mut cmd_parser = CmdParser::new("usb-tablet"); - cmd_parser.push("").push("id"); + cmd_parser.push("").push("id").push("bus").push("port"); cmd_parser.parse(conf)?; let mut dev = UsbTabletConfig::new(); if let Some(id) = cmd_parser.get_value::("id")? { -- Gitee From f97681e562c5143ea14d28b8a3da094cfe919314 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 19:16:56 +0800 Subject: [PATCH 0391/2187] scsi-bus: refactoring emulate_execute of scsi bus Refactoring emulate_execute of scsi bus. No functional change. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 173 +++++++++-------------------------------- 1 file changed, 38 insertions(+), 135 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 095eb9087..a80ccb1f4 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -17,7 +17,7 @@ use std::io::Write; use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use crate::ScsiCntlr::{ ScsiCntlr, ScsiCompleteCb, ScsiXferMode, VirtioScsiCmdReq, VirtioScsiCmdResp, @@ -466,109 +466,61 @@ impl ScsiRequest { pub fn emulate_execute(&self, iocompletecb: ScsiCompleteCb) -> Result<()> { debug!("scsi command is {:#x}", self.cmd.command); - match self.cmd.command { + let mut not_supported_flag = false; + let mut sense = None; + let result = match self.cmd.command { REQUEST_SENSE => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - Some(SCSI_SENSE_NO_SENSE), - &Vec::new(), - )?; + sense = Some(SCSI_SENSE_NO_SENSE); + Ok(Vec::new()) } + WRITE_SAME_10 | WRITE_SAME_16 | SYNCHRONIZE_CACHE => Ok(Vec::new()), TEST_UNIT_READY => { let dev_lock = self.dev.lock().unwrap(); if dev_lock.disk_image.is_none() { - error!("error in processing scsi command TEST_UNIT_READY, no scsi backend"); + Err(anyhow!("No scsi backend!")) + } else { + Ok(Vec::new()) } + } + INQUIRY => scsi_command_emulate_inquiry(&self.cmd, &self.dev), + READ_CAPACITY_10 => scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev), + MODE_SENSE | MODE_SENSE_10 => scsi_command_emulate_mode_sense(&self.cmd, &self.dev), + REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), + SERVICE_ACTION_IN_16 => scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev), + _ => { + not_supported_flag = true; + Err(anyhow!("Emulation scsi command is not supported now!")) + } + }; + + match result { + Ok(outbuf) => { self.cmd_complete( &iocompletecb.mem_space, VIRTIO_SCSI_S_OK, GOOD, - None, - &Vec::new(), + sense, + &outbuf, )?; } - INQUIRY => match scsi_command_emulate_inquiry(&self.cmd, &self.dev) { - Ok(outbuf) => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - None, - &outbuf, - )?; - } - Err(ref e) => { - error!("error in Processing scsi command INQUIRY: {:?}", e); - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - CHECK_CONDITION, - Some(SCSI_SENSE_INVALID_FIELD), - &Vec::new(), - )?; - } - }, - READ_CAPACITY_10 => match scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev) { - Ok(outbuf) => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - None, - &outbuf, - )?; - } - Err(ref e) => { - error!("error in Processing scsi command READ_CAPACITY_10: {:?}", e); + Err(ref e) => { + if not_supported_flag { + info!( + "emulation scsi command {:#x} is no supported", + self.cmd.command + ); self.cmd_complete( &iocompletecb.mem_space, VIRTIO_SCSI_S_OK, CHECK_CONDITION, - Some(SCSI_SENSE_INVALID_FIELD), + Some(SCSI_SENSE_INVALID_OPCODE), &Vec::new(), )?; - } - }, - MODE_SENSE | MODE_SENSE_10 => { - match scsi_command_emulate_mode_sense(&self.cmd, &self.dev) { - Ok(outbuf) => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - None, - &outbuf, - )?; - } - Err(ref e) => { - error!( - "error in processing scsi command MODE_SENSE / MODE_SENSE_10: {:?}", - e - ); - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - CHECK_CONDITION, - Some(SCSI_SENSE_INVALID_FIELD), - &Vec::new(), - )?; - } - } - } - REPORT_LUNS => match scsi_command_emulate_report_luns(&self.cmd, &self.dev) { - Ok(outbuf) => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - None, - &outbuf, - )?; - } - Err(ref e) => { - error!("error in processing scsi command REPORT_LUNS: {:?}", e); + } else { + error!( + "Error in processing scsi command 0x{:#x}, err is {:?}", + self.cmd.command, e + ); self.cmd_complete( &iocompletecb.mem_space, VIRTIO_SCSI_S_OK, @@ -577,55 +529,6 @@ impl ScsiRequest { &Vec::new(), )?; } - }, - SERVICE_ACTION_IN_16 => { - match scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev) { - Ok(outbuf) => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - None, - &outbuf, - )?; - } - Err(ref e) => { - error!( - "error in processing scsi command SERVICE_ACTION_IN(16): {:?}", - e - ); - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - CHECK_CONDITION, - Some(SCSI_SENSE_INVALID_FIELD), - &Vec::new(), - )?; - } - } - } - WRITE_SAME_10 | WRITE_SAME_16 | SYNCHRONIZE_CACHE => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - None, - &Vec::new(), - )?; - } - _ => { - info!( - "emulation scsi command {:#x} is not supported", - self.cmd.command - ); - self.set_scsi_sense(SCSI_SENSE_INVALID_OPCODE); - - let mut req = self.virtioscsireq.lock().unwrap(); - req.resp.response = VIRTIO_SCSI_S_OK; - req.resp.status = CHECK_CONDITION; - req.resp.resid = 0; - - req.complete(&iocompletecb.mem_space); } } -- Gitee From 599ad8ee366c43f0a66576c0ff0bf140d5bef40e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 21:21:41 +0800 Subject: [PATCH 0392/2187] scsi-bus: add emulation for target request According to scsi spec, if there is any lun in this scsi target, it will response some scsi commands. And, if there is no lun found in scsi target, it means such target is non-existen. So, add emulation for such target request. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 170 +++++++++++++++++++++++++++++----- virtio/src/scsi/controller.rs | 13 ++- 2 files changed, 158 insertions(+), 25 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index a80ccb1f4..a725f4805 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -275,6 +275,17 @@ pub const SCSI_INQUIRY_VENDOR_MAX_LEN: usize = 8; pub const SCSI_INQUIRY_VERSION_MAX_LEN: usize = 4; pub const SCSI_INQUIRY_VPD_SERIAL_NUMBER_MAX_LEN: usize = 32; +const SCSI_TARGET_INQUIRY_LEN: u32 = 36; + +/// | bit7 - bit 5 | bit 4 - bit 0 | +/// | Peripheral Qualifier | Peripheral Device Type | +/// Unknown or no device type. +const TYPE_UNKNOWN: u8 = 0x1f; +/// A peripheral device having the specified peripheral device type is not connected to this logical unit. +const TYPE_INACTIVE: u8 = 0x20; +/// Scsi target device is not capable of supporting a peripheral device connected to this logical unit. +const TYPE_NO_LUN: u8 = 0x7f; + pub struct ScsiBus { /// Bus name. pub name: String, @@ -294,10 +305,33 @@ impl ScsiBus { } /// Get device by the target number and the lun number. + /// If the device requested by the target number and the lun number is non-existen, + /// return the first device in ScsiBus's devices list. It's OK because we will not + /// use this "random" device, we will just use it to prove that the target is existen. pub fn get_device(&self, target: u8, lun: u16) -> Option>> { if let Some(dev) = self.devices.get(&(target, lun)) { return Some((*dev).clone()); } + + // If lun device requested in CDB's LUNS bytes is not found, it may be a target request. + // Target request means if there is any lun in this scsi target, it will response some + // scsi commands. And, if there is no lun found in this scsi target, it means such target + // is non-existent. So, we should find if there exists a lun which has the same id with + // target id in CBD's LUNS bytes. And, if there exist two or more luns which have the same + // target id, just return the first one is OK enough. + for (id, device) in self.devices.iter() { + let (target_id, lun_id) = id; + if *target_id == target { + debug!( + "Target request, target {}, requested lun {}, found lun {}", + target_id, lun, lun_id + ); + return Some((*device).clone()); + } + } + + // No lun found in requested target. It seems there is no such target requested in + // CDB's LUNS bytes. debug!("Can't find scsi device target {} lun {}", target, lun); None } @@ -464,34 +498,65 @@ impl ScsiRequest { Ok(0) } - pub fn emulate_execute(&self, iocompletecb: ScsiCompleteCb) -> Result<()> { + pub fn emulate_execute( + &self, + iocompletecb: ScsiCompleteCb, + req_lun_id: u16, + found_lun_id: u16, + ) -> Result<()> { debug!("scsi command is {:#x}", self.cmd.command); let mut not_supported_flag = false; let mut sense = None; - let result = match self.cmd.command { - REQUEST_SENSE => { - sense = Some(SCSI_SENSE_NO_SENSE); - Ok(Vec::new()) - } - WRITE_SAME_10 | WRITE_SAME_16 | SYNCHRONIZE_CACHE => Ok(Vec::new()), - TEST_UNIT_READY => { - let dev_lock = self.dev.lock().unwrap(); - if dev_lock.disk_image.is_none() { - Err(anyhow!("No scsi backend!")) - } else { + let result; + + // Requested lun id is not equal to found device id means it may be a target request. + // REPORT LUNS is also a target request command. + if req_lun_id != found_lun_id || self.cmd.command == REPORT_LUNS { + result = match self.cmd.command { + REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), + INQUIRY => scsi_command_emulate_target_inquiry(req_lun_id, &self.cmd), + REQUEST_SENSE => { + if req_lun_id != 0 { + sense = Some(SCSI_SENSE_LUN_NOT_SUPPORTED); + } + // Scsi Device does not realize sense buffer now, so just return. Ok(Vec::new()) } - } - INQUIRY => scsi_command_emulate_inquiry(&self.cmd, &self.dev), - READ_CAPACITY_10 => scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev), - MODE_SENSE | MODE_SENSE_10 => scsi_command_emulate_mode_sense(&self.cmd, &self.dev), - REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), - SERVICE_ACTION_IN_16 => scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev), - _ => { - not_supported_flag = true; - Err(anyhow!("Emulation scsi command is not supported now!")) - } - }; + TEST_UNIT_READY => Ok(Vec::new()), + _ => { + not_supported_flag = true; + sense = Some(SCSI_SENSE_INVALID_OPCODE); + Err(anyhow!("Invalid emulation target scsi command")) + } + }; + } else { + // It's not a target request. + result = match self.cmd.command { + REQUEST_SENSE => { + sense = Some(SCSI_SENSE_NO_SENSE); + Ok(Vec::new()) + } + WRITE_SAME_10 | WRITE_SAME_16 | SYNCHRONIZE_CACHE => Ok(Vec::new()), + TEST_UNIT_READY => { + let dev_lock = self.dev.lock().unwrap(); + if dev_lock.disk_image.is_none() { + Err(anyhow!("No scsi backend!")) + } else { + Ok(Vec::new()) + } + } + INQUIRY => scsi_command_emulate_inquiry(&self.cmd, &self.dev), + READ_CAPACITY_10 => scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev), + MODE_SENSE | MODE_SENSE_10 => scsi_command_emulate_mode_sense(&self.cmd, &self.dev), + SERVICE_ACTION_IN_16 => { + scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev) + } + _ => { + not_supported_flag = true; + Err(anyhow!("Emulation scsi command is not supported now!")) + } + }; + } match result { Ok(outbuf) => { @@ -917,10 +982,69 @@ fn scsi_command_emulate_vpd_page( Ok(outbuf) } +fn scsi_command_emulate_target_inquiry(lun: u16, cmd: &ScsiCommand) -> Result> { + let mut outbuf: Vec = vec![0; 4]; + + // Byte1: bit0: EVPD (Enable Vital product bit). + if cmd.buf[1] == 0x1 { + // Vital Product Data. + // Byte2: Page Code. + let page_code = cmd.buf[2]; + outbuf[1] = page_code; + match page_code { + 0x00 => { + // Supported page codes. + // Page Length: outbuf.len() - 4. Supported VPD page list only has 0x00 item. + outbuf[3] = 0x1; + // Supported VPD page list. Only support this page. + outbuf.push(0x00); + } + _ => { + bail!("Emulate target inquiry invalid page code {:x}", page_code); + } + } + return Ok(outbuf); + } + + // EVPD = 0 means it's a Standard INQUIRY command. + // Byte2: page code. + if cmd.buf[2] != 0 { + bail!("Invalid standatd inquiry command!"); + } + + outbuf.resize(SCSI_TARGET_INQUIRY_LEN as usize, 0); + let len = cmp::min(cmd.xfer, SCSI_TARGET_INQUIRY_LEN); + + // outbuf. + // Byte0: Peripheral Qualifier | peripheral device type. + // Byte1:RMB. + // Byte2: VERSION. + // Byte3: NORMACA | HISUP | Response Data Format. + // Byte4: Additional length(outbuf.len() - 5). + // Byte5: SCCS | ACC | TPGS | 3PC | RESERVED | PROTECT. + // Byte6: ENCSERV | VS | MULTIP | ADDR16. + // Byte7: WBUS16 | SYNC | CMDQUE | VS. + if lun != 0 { + outbuf[0] = TYPE_NO_LUN; + } else { + outbuf[0] = TYPE_UNKNOWN | TYPE_INACTIVE; + // scsi version. + outbuf[2] = 5; + // HISUP(hierarchical support). Response Data Format(must be 2). + outbuf[3] = 0x12; + outbuf[4] = len as u8 - 5; + // SYNC, CMDQUE(the logical unit supports the task management model). + outbuf[7] = 0x12; + } + + Ok(outbuf) +} + fn scsi_command_emulate_inquiry( cmd: &ScsiCommand, dev: &Arc>, ) -> Result> { + // Vital product data. if cmd.buf[1] == 0x1 { return scsi_command_emulate_vpd_page(cmd, dev); } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index dd5f8c612..abda92fca 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -768,8 +768,9 @@ impl ScsiCmdHandler { drop(queue); let lun: [u8; 8] = cmd.req.lun; let scsibus = self.scsibus.lock().unwrap(); + let req_lun_id = virtio_scsi_get_lun(lun); - if let Some(scsidevice) = scsibus.get_device(lun[1], virtio_scsi_get_lun(lun)) { + if let Some(scsidevice) = scsibus.get_device(lun[1], req_lun_id) { drop(scsibus); let cmd_req = Arc::new(Mutex::new(cmd)); let req = if let Ok(scsireq) = @@ -788,13 +789,21 @@ impl ScsiCmdHandler { None }; + let scsi_dev_lock = scsidevice.lock().unwrap(); + let found_lun_id = scsi_dev_lock.config.lun; + drop(scsi_dev_lock); + if let Some(scsireq) = req { if scsireq.opstype == EMULATE_SCSI_OPS { let scsicompletecb = ScsiCompleteCb::new( self.mem_space.clone(), Arc::new(Mutex::new(scsireq.clone())), ); - scsireq.emulate_execute(scsicompletecb)?; + scsireq.emulate_execute( + scsicompletecb, + req_lun_id, + found_lun_id, + )?; } else if let Some(disk_img) = scsidevice.lock().unwrap().disk_image.as_mut() { -- Gitee From b9de96caff4e1a67c993001ced799ff9be30aad6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 21:35:47 +0800 Subject: [PATCH 0393/2187] virtio-scsi: set max_lun to 255 Set max_lun to 255, and need not start from 0. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- machine_manager/src/config/scsi.rs | 14 ++++++++++++-- virtio/src/scsi/controller.rs | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 4246215e6..eee2fdb9d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -780,7 +780,7 @@ Six properties can be set for virtio-scsi hd. * id: unique device id. * bus: scsi bus name, only support $scsi_controller_name + ".0" * scsi-id: id number (target) of scsi four level hierarchical address (host, channel, target, lun). Configuration range is [0, 255]. Boot scsi disk configuration range is [0, 31]. -* lun: lun number (lun) of scsi four level hierarchical address (host, channel, target, lun). Only support starting from 0 now. +* lun: lun number (lun) of scsi four level hierarchical address (host, channel, target, lun). Configuration rage is [0, 255]. Boot scsi disk configuration range is [0, 7]. * serial: serial number of virtio scsi device.(optional) ```shell diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 094ba3cbf..b79e51c26 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -16,8 +16,15 @@ use super::{error::ConfigError, pci_args_check}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; /// According to Virtio Spec. +/// Max_channel should be 0. /// Max_target should be less than or equal to 255. pub const VIRTIO_SCSI_MAX_TARGET: u16 = 255; +/// Max_lun should be less than or equal to 16383 (2^14 - 1). +pub const VIRTIO_SCSI_MAX_LUN: u16 = 16383; + +/// Only support peripheral device addressing format(8 bits for lun) in stratovirt now. +/// So, max lun id supported is 255 (2^8 - 1). +const SUPPORT_SCSI_MAX_LUN: u16 = 255; #[derive(Debug, Clone)] pub struct ScsiCntlrConfig { @@ -170,12 +177,15 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result } if let Some(lun) = cmd_parser.get_value::("lun")? { - if lun > 255 { + // Do not support Flat space addressing format(14 bits for lun) in stratovirt now. + // We now support peripheral device addressing format(8 bits for lun). + // So, MAX_LUN should be less than 255(2^8 - 1) temporarily. + if lun > SUPPORT_SCSI_MAX_LUN { return Err(anyhow!(ConfigError::IllegalValue( "lun of scsi device".to_string(), 0, true, - 255, + SUPPORT_SCSI_MAX_LUN as u64, true, ))); } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index abda92fca..5a258f4c5 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -28,7 +28,7 @@ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use log::{error, info}; use machine_manager::{ - config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_TARGET}, + config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; use util::aio::{Aio, AioCb, AioCompleteFunc, Iovec}; @@ -168,6 +168,7 @@ impl VirtioDevice for ScsiCntlr { // seg_max: queue size - 2, 32 bit. self.state.config_space.seg_max = self.queue_size() as u32 - 2; self.state.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; + self.state.config_space.max_lun = VIRTIO_SCSI_MAX_LUN as u32; self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) -- Gitee From b593b09298c06103ff2bc4cdf150f0d032f17938 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 6 Nov 2022 13:19:12 +0800 Subject: [PATCH 0394/2187] virtio-scsi: REPORT LUNS should only report the same target's luns REPORT LUNS should only report the same target's luns. Add check when reporting. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index a725f4805..b815df22e 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -1248,13 +1248,18 @@ fn scsi_command_emulate_report_luns( let dev_lock = dev.lock().unwrap(); // Byte 0-3: Lun List Length. Byte 4-7: Reserved. let mut outbuf: Vec = vec![0; 8]; + let target = dev_lock.config.target; if cmd.xfer < 16 { bail!("scsi REPORT LUNS xfer {} too short!", cmd.xfer); } + //Byte2: SELECT REPORT:00h/01h/02h. 03h to FFh is reserved. if cmd.buf[2] > 2 { - bail!("Invalid REPORT LUNS cmd, buf[2] is {}", cmd.buf[2]); + bail!( + "Invalid REPORT LUNS cmd, SELECT REPORT Byte is {}", + cmd.buf[2] + ); } let scsi_bus = dev_lock.parent_bus.upgrade().unwrap(); @@ -1264,6 +1269,10 @@ fn scsi_command_emulate_report_luns( for (_pos, device) in scsi_bus_clone.devices.iter() { let device_lock = device.lock().unwrap(); + if device_lock.config.target != target { + drop(device_lock); + continue; + } let len = outbuf.len(); if device_lock.config.lun < 256 { outbuf.push(0); -- Gitee From deeb8ee745c27a100f23583800c738437b713b04 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Nov 2022 23:48:26 +0800 Subject: [PATCH 0395/2187] virtio-scsi: modify INQUIRY command notes Modify some incorrect notes in INQUIRY command. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index b815df22e..9cbcad6dd 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -851,7 +851,7 @@ fn scsi_cdb_xfer_mode(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> ScsiXferMode } } -/// VPD: Virtual Product Data. +/// VPD: Vital Product Data. fn scsi_command_emulate_vpd_page( cmd: &ScsiCommand, dev: &Arc>, @@ -906,7 +906,7 @@ fn scsi_command_emulate_vpd_page( } if len > 0 { - // 0x2: Code Set: ASCII, Protocol Identifier: FCP-4. + // 0x2: Code Set: ASCII, Protocol Identifier: reserved. // 0: Identifier Type, Association, Reserved, Piv. // 0: Reserved. // len: identifier length. @@ -956,7 +956,6 @@ fn scsi_command_emulate_vpd_page( 0xb1 => { // Block Device Characteristics. // 0: Medium Rotation Rate: 2Bytes. - // 0: Medium Rotation Rate: 2Bytes. // 0: Product Type. // 0: Nominal Form Factor, Wacereq, Wabereq. // 0: Vbuls, Fuab, Bocs, Reserved, Zoned, Reserved. @@ -977,7 +976,7 @@ fn scsi_command_emulate_vpd_page( } } - // It's OK for just using outbuf bit 3, because all page_code's buflen in stratovirt is less than 255 now. + // It's OK for just using outbuf byte 3, because all page_code's buflen in stratovirt is less than 255 now. outbuf[3] = buflen as u8 - 4; Ok(outbuf) } @@ -1044,7 +1043,7 @@ fn scsi_command_emulate_inquiry( cmd: &ScsiCommand, dev: &Arc>, ) -> Result> { - // Vital product data. + // Byte1 bit0: EVPD(enable vital product data). if cmd.buf[1] == 0x1 { return scsi_command_emulate_vpd_page(cmd, dev); } @@ -1077,7 +1076,11 @@ fn scsi_command_emulate_inquiry( drop(dev_lock); - // scsi version: 5. + // outbuf: + // Byte2: Version. + // Byte3: bits[0-3]: Response Data Format; bit 4:Hisup. + // Byte4: Additional Length(outbuf.len()-5). + // Byte7: bit2: Cmdque; bit4: SYNC. outbuf[2] = 5; outbuf[3] = (2 | 0x10) as u8; @@ -1087,7 +1090,6 @@ fn scsi_command_emulate_inquiry( outbuf[4] = 36 - 5; } - // TCQ. outbuf[7] = 0x12; Ok(outbuf) -- Gitee From 5fdc49e4efd0ac73ee1d7ba30fb7256f0dd82724 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 14:56:52 +0800 Subject: [PATCH 0396/2187] pci: notify only when the enabled/masked bits are written Only when the masked/enabled bits in msix cap configure space is written, it's necessary to check whether to notify the guest. Otherwise, let the msix/pba table written callback functions to deal with that, or just leave it alone, as that such as writing cap next register should not affect the interruption behavior. Signed-off-by: Zhang Bo --- pci/src/config.rs | 5 ++++- pci/src/msix.rs | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0b7bc317f..3ee56c208 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -469,6 +469,7 @@ impl PciConfig { /// * `dev_id` - Device id to send MSI/MSI-X. pub fn write(&mut self, mut offset: usize, data: &[u8], dev_id: u16) { let cloned_data = data.to_vec(); + let old_offset = offset; for data in &cloned_data { self.config[offset] = (self.config[offset] & (!self.write_mask[offset])) | (data & self.write_mask[offset]); @@ -476,7 +477,9 @@ impl PciConfig { offset += 1; } if let Some(msix) = &mut self.msix { - msix.lock().unwrap().write_config(&self.config, dev_id); + msix.lock() + .unwrap() + .write_config(&self.config, dev_id, old_offset, data); } } diff --git a/pci/src/msix.rs b/pci/src/msix.rs index d62c673bb..385a249a6 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -294,7 +294,19 @@ impl Msix { send_msix(self.get_message(vector), dev_id); } - pub fn write_config(&mut self, config: &[u8], dev_id: u16) { + pub fn write_config(&mut self, config: &[u8], dev_id: u16, offset: usize, data: &[u8]) { + let len = data.len(); + let msix_cap_control_off: usize = self.msix_cap_offset as usize + MSIX_CAP_CONTROL as usize; + // Only care about the bits Masked(14) & Enabled(15) in msix control register. + if !ranges_overlap( + offset, + offset + len, + msix_cap_control_off + 1, + msix_cap_control_off + 2, + ) { + return; + } + let func_masked: bool = is_msix_func_masked(self.msix_cap_offset as usize, config); let enabled: bool = is_msix_enabled(self.msix_cap_offset as usize, config); @@ -648,7 +660,12 @@ mod tests { let val = le_read_u16(&pci_config.config, offset).unwrap(); le_write_u16(&mut pci_config.config, offset, val | MSIX_CAP_ENABLE).unwrap(); locked_msix.set_pending_vector(0); - locked_msix.write_config(&pci_config.config, 0); + locked_msix.write_config( + &pci_config.config, + 0, + offset, + &[0, val as u8 | MSIX_CAP_ENABLE as u8], + ); assert!(!locked_msix.func_masked); assert!(locked_msix.enabled); -- Gitee From 47073f0b7547f42e021904f42ace4924bb00ba90 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 14:16:09 +0800 Subject: [PATCH 0397/2187] pci: correct the name of pci express capability Pci express capability means the capability that's in the range of [64B, 256B] of pcie configure space, which includes device/slot/link's cap, control and status registers. Pcie extended capabilities are the capabilities that's in the range of [256B, 4096B] of pcie configuration space, takes more bits for its cap ID and cap next registers. The slot status, etc. things belong to pci express capability, in fact. Thus correct the name. Signed-off-by: Zhang Bo --- pci/src/config.rs | 8 ++++---- pci/src/root_port.rs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0b7bc317f..10cd63216 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -339,8 +339,8 @@ pub struct PciConfig { pub last_ext_cap_end: u16, /// MSI-X information. pub msix: Option>>, - /// Offset of the PCIe extended capability. - pub ext_cap_offset: u16, + /// Offset of the PCI express capability. + pub pci_express_cap_offset: u16, } impl PciConfig { @@ -370,7 +370,7 @@ impl PciConfig { last_ext_cap_offset: 0, last_ext_cap_end: PCI_CONFIG_SPACE_SIZE as u16, msix: None, - ext_cap_offset: PCI_CONFIG_HEAD_END as u16, + pci_express_cap_offset: PCI_CONFIG_HEAD_END as u16, } } @@ -750,7 +750,7 @@ impl PciConfig { /// * `dev_type` - Device type. pub fn add_pcie_cap(&mut self, devfn: u8, port_num: u8, dev_type: u8) -> Result { let cap_offset: usize = self.add_pci_cap(CapId::Pcie as u8, PCIE_CAP_SIZE as usize)?; - self.ext_cap_offset = cap_offset as u16; + self.pci_express_cap_offset = cap_offset as u16; let mut offset: usize = cap_offset + PcieCap::CapReg as usize; let pci_type = (dev_type << PCI_EXP_FLAGS_TYPE_SHIFT) as u16 & PCI_EXP_FLAGS_TYPE; le_write_u16( diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 6363c3a38..bbdd9a32b 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -121,7 +121,7 @@ impl RootPort { fn hotplug_command_completed(&mut self) { if let Err(e) = le_write_set_value_u16( &mut self.config.config, - (self.config.ext_cap_offset + PCI_EXP_SLTSTA) as usize, + (self.config.pci_express_cap_offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_HP_EV_CCI, ) { error!("{}", format!("{:?}", e)); @@ -141,7 +141,7 @@ impl RootPort { /// Update register when the guest OS trigger the removal of the device. fn update_register_status(&mut self) -> Result<()> { - let cap_offset = self.config.ext_cap_offset; + let cap_offset = self.config.pci_express_cap_offset; le_write_clear_value_u16( &mut self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, @@ -220,7 +220,7 @@ impl RootPort { } fn do_unplug(&mut self, offset: usize, end: usize, old_ctl: u16) { - let cap_offset = self.config.ext_cap_offset; + let cap_offset = self.config.pci_express_cap_offset; // Only care the write config about slot control if !ranges_overlap( offset, @@ -372,7 +372,7 @@ impl PciDevOps for RootPort { return; } - let cap_offset = self.config.ext_cap_offset; + let cap_offset = self.config.pci_express_cap_offset; let old_ctl = le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize).unwrap(); @@ -417,7 +417,7 @@ impl PciDevOps for RootPort { .reset() .with_context(|| "Fail to reset sec_bus in root port") } else { - let cap_offset = self.config.ext_cap_offset; + let cap_offset = self.config.pci_express_cap_offset; le_write_u16( &mut self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, @@ -457,7 +457,7 @@ impl HotplugOps for RootPort { return Err(anyhow!(PciError::HotplugUnsupported(devfn))); } - let offset = self.config.ext_cap_offset; + let offset = self.config.pci_express_cap_offset; le_write_set_value_u16( &mut self.config.config, (offset + PCI_EXP_SLTSTA) as usize, @@ -474,7 +474,7 @@ impl HotplugOps for RootPort { } fn unplug_request(&mut self, dev: &Arc>) -> Result<()> { - let pcie_cap_offset = self.config.ext_cap_offset; + let pcie_cap_offset = self.config.pci_express_cap_offset; let sltctl = le_read_u16( &self.config.config, (pcie_cap_offset + PCI_EXP_SLTCTL) as usize, @@ -494,7 +494,7 @@ impl HotplugOps for RootPort { return self.unplug(dev); } - let offset = self.config.ext_cap_offset; + let offset = self.config.pci_express_cap_offset; le_write_clear_value_u16( &mut self.config.config, (offset + PCI_EXP_LNKSTA) as usize, -- Gitee From bcf7899b686da126d33e6061cc8245fa7f556fa4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 15 Nov 2022 15:32:33 +0800 Subject: [PATCH 0398/2187] vhost-net: using set_backend() to reset device In guest, execute "reboot" command, it will call reset() in StratoVirt. The taps will be assigned None value, which will close the tap's fd later. When it call realize() later, it will use the fd which has been closed. So, it cause the reboot process gets stuck. Instead of setting taps to None, we use set_backend() with fd(-1) to reset device. Signed-off-by: Yan Wang --- virtio/src/vhost/kernel/net.rs | 37 +++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 7b5bb323c..818ec0903 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -11,9 +11,8 @@ // See the Mulan PSL v2 for more details. use std::cmp; -use std::fs::File; use std::io::Write; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; use crate::error::VirtioError; @@ -55,15 +54,15 @@ trait VhostNetBackend { /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. - fn set_backend(&self, queue_index: usize, tap_file: &File) -> Result<()>; + fn set_backend(&self, queue_index: usize, fd: RawFd) -> Result<()>; } impl VhostNetBackend for VhostBackend { /// Attach virtio net ring to a raw socket, or tap device. - fn set_backend(&self, queue_index: usize, tap_file: &File) -> Result<()> { + fn set_backend(&self, queue_index: usize, fd: RawFd) -> Result<()> { let vring_file = VhostVringFile { index: queue_index as u32, - fd: tap_file.as_raw_fd(), + fd, }; let ret = unsafe { ioctl_with_ref(self, VHOST_NET_SET_BACKEND(), &vring_file) }; @@ -349,7 +348,7 @@ impl VirtioDevice for Net { Some(taps) => taps[index].clone(), }; backend - .set_backend(queue_index, &tap.file) + .set_backend(queue_index, tap.file.as_raw_fd()) .with_context(|| { format!( "Failed to set tap device for vhost net, index: {}", @@ -381,16 +380,22 @@ impl VirtioDevice for Net { } fn reset(&mut self) -> Result<()> { - // No need to close fd manually, because rust will - // automatically cleans up variables at the end of the lifecycle. - self.backends = None; - self.taps = None; - self.device_features = 0_u64; - self.driver_features = 0_u64; - self.vhost_features = 0_u64; - self.device_config = VirtioNetConfig::default(); - - self.realize() + let queue_pairs = self.net_cfg.queues / 2; + for index in 0..queue_pairs as usize { + let backend = match &self.backends { + None => return Err(anyhow!("Failed to get backend for vhost net")), + Some(backends) => backends + .get(index) + .with_context(|| format!("Failed to get index {} vhost backend", index))?, + }; + + // 2 queues: rx and tx. + for queue_index in 0..2 { + backend.set_backend(queue_index, -1)?; + } + } + + Ok(()) } } -- Gitee From de90f0631570537f269a0cdb40693dfcc57b4ac3 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 15 Nov 2022 15:40:59 +0800 Subject: [PATCH 0399/2187] virtio-net: check if the tap has ufo when using GUEST_UFO/HOST_UFO flag It should check if the tap has ufo before using flags VIRTIO_NET_F_GUEST_UFO and VIRTIO_NET_F_HOST_UFO. Signed-off-by: Yan Wang --- util/src/tap.rs | 5 +++++ virtio/src/net.rs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/util/src/tap.rs b/util/src/tap.rs index 4db3c98ba..1d24520d1 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -135,6 +135,11 @@ impl Tap { Ok(()) } + pub fn has_ufo(&self) -> bool { + let flags = TUN_F_CSUM | TUN_F_UFO; + (unsafe { ioctl_with_val(&self.file, TUNSETOFFLOAD(), flags as libc::c_ulong) }) >= 0 + } + pub fn read(&mut self, buf: &mut [u8]) -> IoResult { self.file.read(buf) } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 815212319..07add93ed 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -910,6 +910,14 @@ impl VirtioDevice for Net { self.taps = None; } + // Using the first tap to test if all the taps have ufo. + if let Some(tap) = self.taps.as_ref().map(|t| &t[0]) { + if !tap.has_ufo() { + self.state.device_features &= + !(1 << VIRTIO_NET_F_GUEST_UFO | 1 << VIRTIO_NET_F_HOST_UFO); + } + } + if let Some(mac) = &self.net_cfg.mac { self.state.device_features |= build_device_config_space(&mut self.state.config_space, mac); -- Gitee From d452692ca7e8b5daff37a595125179b1c5c48a9c Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 7 Nov 2022 20:01:06 +0800 Subject: [PATCH 0400/2187] pci: avoid creating a msix with no vector Msix makes sense only when it has vectors. Check whether the vector number is not 0 before initializing it. Signed-off-by: Zhang Bo --- pci/src/msix.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 0921a1ad6..519ea2c12 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -446,8 +446,11 @@ pub fn init_msix( parent_region: Option<&Region>, offset_opt: Option<(u32, u32)>, ) -> Result<()> { - if vector_nr > MSIX_TABLE_SIZE_MAX as u32 + 1 { - bail!("Too many msix vectors."); + if vector_nr == 0 || vector_nr > MSIX_TABLE_SIZE_MAX as u32 + 1 { + bail!( + "invalid msix vectors, which should be in [1, {}]", + MSIX_TABLE_SIZE_MAX + 1 + ); } let msix_cap_offset: usize = config.add_pci_cap(CapId::Msix as u8, MSIX_CAP_SIZE as usize)?; @@ -546,6 +549,18 @@ mod tests { ) .is_err()); + // No vector. + assert!(init_msix( + 0, + 0, + &mut pci_config, + Arc::new(AtomicU16::new(0)), + "msix", + None, + None, + ) + .is_err()); + init_msix( 1, 2, -- Gitee From 1a35a0d7782c70d78080259734e80b8af7c04638 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 9 Nov 2022 14:10:43 +0800 Subject: [PATCH 0401/2187] pci: avoid bar size overflow pio and 32 bit mmio bars shall have the bar size within 2^32, if the caller pass a size greater than that, we shall take it as an error. Signed-off-by: Zhang Bo --- pci/src/config.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0b7bc317f..19834b563 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -274,6 +274,20 @@ pub enum RegionType { Mem64Bit, } +impl std::fmt::Display for RegionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + RegionType::Io => "PIO", + RegionType::Mem32Bit => "32 bits MMIO", + RegionType::Mem64Bit => "64 bits MMIO", + } + ) + } +} + /// Registered bar. #[derive(Clone)] pub struct Bar { @@ -897,9 +911,11 @@ impl PciConfig { || ((bar_type == RegionType::Mem32Bit || bar_type == RegionType::Mem64Bit) && size < MINMUM_BAR_SIZE_FOR_MMIO.try_into().unwrap()) || (bar_type == RegionType::Io && size < MINMUM_BAR_SIZE_FOR_PIO.try_into().unwrap()) + || (bar_type == RegionType::Mem32Bit && size > u32::MAX as u64) + || (bar_type == RegionType::Io && size > u16::MAX as u64) { return Err(anyhow!(PciError::InvalidConf( - "Bar size".to_string(), + "Bar size of type ".to_string() + &bar_type.to_string(), size.to_string(), ))); } -- Gitee From 3afefcb7cd8f95e69aee1ab0ea7540b330e52ed9 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 13 Nov 2022 03:08:59 -0500 Subject: [PATCH 0402/2187] root port: reset bridge configs during reset reset bridge configs during root port reset. Signed-off-by: Zhang Bo --- pci/src/config.rs | 6 +++--- pci/src/root_port.rs | 29 +++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 630bf0dee..ac5570d1c 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -143,9 +143,9 @@ pub const STATUS: u8 = 0x06; /// PCI Interrupt Status. pub const STATUS_INTERRUPT: u8 = 0x08; const CACHE_LINE_SIZE: u8 = 0x0c; -const PRIMARY_BUS_NUM: u8 = 0x18; -const IO_LIMIT: u8 = 0x1d; -const PREF_MEM_BASE_UPPER: u8 = 0x28; +pub const PRIMARY_BUS_NUM: u8 = 0x18; +pub const IO_LIMIT: u8 = 0x1d; +pub const PREF_MEM_BASE_UPPER: u8 = 0x28; const CAP_LIST: u8 = 0x34; const INTERRUPT_LINE: u8 = 0x3c; const BRIDGE_CONTROL: u8 = 0x3e; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 6363c3a38..efa0dd828 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -27,18 +27,19 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, - COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, - PCIE_CAP_CLS_2_5G, PCIE_CAP_NLW_X1, PCIE_CAP_SLOT_PIC_BLINK, PCIE_CAP_SLOT_PIC_MASK, - PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, - PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, + COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, IO_LIMIT, + MEMORY_BASE, PCIE_CAP_CLS_2_5G, PCIE_CAP_NLW_X1, PCIE_CAP_SLOT_PIC_BLINK, + PCIE_CAP_SLOT_PIC_MASK, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, + PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, - PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, + PREF_MEM_BASE_UPPER, PREF_MEM_RANGE_64BIT, PRIMARY_BUS_NUM, REG_SIZE, SUB_CLASS_CODE, + VENDOR_ID, }; use crate::bus::PciBus; use crate::hotplug::HotplugOps; use crate::msix::init_msix; -use crate::{init_multifunction, PciError}; +use crate::{init_multifunction, le_write_u32, le_write_u64, PciError}; use crate::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, PciDevOps, @@ -262,6 +263,18 @@ impl RootPort { error!("Failed to set fast unplug feature: {}", v); } } + + fn reset_bridge_regs(&mut self) -> Result<()> { + le_write_u32(&mut self.config.config, PRIMARY_BUS_NUM as usize, 0)?; + + self.config.config[IO_BASE as usize] = 0xff; + self.config.config[IO_LIMIT as usize] = 0; + // set memory/pref memory's base to 0xFFFF and limit to 0. + le_write_u32(&mut self.config.config, MEMORY_BASE as usize, 0xffff)?; + le_write_u32(&mut self.config.config, PREF_MEMORY_BASE as usize, 0xffff)?; + le_write_u64(&mut self.config.config, PREF_MEM_BASE_UPPER as usize, 0)?; + Ok(()) + } } impl PciDevOps for RootPort { @@ -415,7 +428,7 @@ impl PciDevOps for RootPort { .lock() .unwrap() .reset() - .with_context(|| "Fail to reset sec_bus in root port") + .with_context(|| "Fail to reset sec_bus in root port")?; } else { let cap_offset = self.config.ext_cap_offset; le_write_u16( @@ -433,8 +446,8 @@ impl PciDevOps for RootPort { (cap_offset + PCI_EXP_LNKSTA) as usize, PCI_EXP_LNKSTA_DLLLA, )?; - Ok(()) } + self.reset_bridge_regs() } fn get_dev_path(&self) -> Option { -- Gitee From 3979b89d3fa31a798c2f721394b04237b0725532 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 13 Nov 2022 08:36:36 -0500 Subject: [PATCH 0403/2187] root port: skip checking device in range during reset skip checking whether a bdf number is within the downstream ports' bus numbers of a bridge(root port here) when it's reseting. Signed-off-by: Zhang Bo --- pci/src/bus.rs | 46 +++++++++++++++++++++++++++++++++------------- pci/src/config.rs | 4 ++-- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 7e48416ef..e40554820 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -16,7 +16,9 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::Region; use log::debug; -use super::config::{SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM}; +use super::config::{ + BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET, SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM, +}; use super::hotplug::HotplugOps; use super::PciDevOps; use anyhow::{bail, Context, Result}; @@ -74,19 +76,9 @@ impl PciBus { /// /// * `offset` - Offset of bus number register. pub fn number(&self, offset: usize) -> u8 { - if self.parent_bridge.is_none() { - return 0; - } - let mut data = vec![0_u8; 1]; - self.parent_bridge - .as_ref() - .unwrap() - .upgrade() - .unwrap() - .lock() - .unwrap() - .read_config(offset, &mut data); + self.get_bridge_control_reg(offset, &mut data); + data[0] } @@ -105,6 +97,10 @@ impl PciBus { } fn in_range(&self, bus_num: u8) -> bool { + if self.is_during_reset() { + return false; + } + let secondary_bus_num: u8 = self.number(SECONDARY_BUS_NUM as usize); let subordinate_bus_num: u8 = self.number(SUBORDINATE_BUS_NUM as usize); if bus_num > secondary_bus_num && bus_num <= subordinate_bus_num { @@ -221,6 +217,30 @@ impl PciBus { Ok(()) } + + fn is_during_reset(&self) -> bool { + let mut data = vec![0_u8; 2]; + self.get_bridge_control_reg(BRIDGE_CONTROL as usize + 1, &mut data); + if data[1] & ((BRIDGE_CTL_SEC_BUS_RESET >> 8) as u8) != 0 { + return true; + } + false + } + + fn get_bridge_control_reg(&self, offset: usize, data: &mut [u8]) { + if self.parent_bridge.is_none() { + return; + } + + self.parent_bridge + .as_ref() + .unwrap() + .upgrade() + .unwrap() + .lock() + .unwrap() + .read_config(offset, data); + } } #[cfg(test)] diff --git a/pci/src/config.rs b/pci/src/config.rs index ac5570d1c..f034b3bc8 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -148,14 +148,14 @@ pub const IO_LIMIT: u8 = 0x1d; pub const PREF_MEM_BASE_UPPER: u8 = 0x28; const CAP_LIST: u8 = 0x34; const INTERRUPT_LINE: u8 = 0x3c; -const BRIDGE_CONTROL: u8 = 0x3e; +pub const BRIDGE_CONTROL: u8 = 0x3e; const BRIDGE_CTL_PARITY_ENABLE: u16 = 0x0001; const BRIDGE_CTL_SERR_ENABLE: u16 = 0x0002; const BRIDGE_CTL_ISA_ENABLE: u16 = 0x0004; const BRIDGE_CTL_VGA_ENABLE: u16 = 0x0008; const BRIDGE_CTL_VGA_16BIT_DEC: u16 = 0x0010; -const BRIDGE_CTL_SEC_BUS_RESET: u16 = 0x0040; +pub const BRIDGE_CTL_SEC_BUS_RESET: u16 = 0x0040; const BRIDGE_CTL_FAST_BACK: u16 = 0x0080; const BRIDGE_CTL_DISCARD_TIMER: u16 = 0x0100; const BRIDGE_CTL_SEC_DISCARD_TIMER: u16 = 0x0200; -- Gitee From 515211c56c2e86b37fc07652e820b0bf84f6e62e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 15 Nov 2022 05:38:40 +0800 Subject: [PATCH 0404/2187] virtio: Factor out splitqueue from queue ... to make code looks clean and easy to extend. Signed-off-by: Keqian Zhu --- virtio/src/lib.rs | 4 +- virtio/src/virtqueue/mod.rs | 206 ++++++++++++++++ virtio/src/{queue.rs => virtqueue/split.rs} | 248 +++----------------- 3 files changed, 242 insertions(+), 216 deletions(-) create mode 100644 virtio/src/virtqueue/mod.rs rename virtio/src/{queue.rs => virtqueue/split.rs} (92%) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 56272a812..5ebdaaba9 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -32,13 +32,13 @@ pub mod error; #[cfg(not(target_env = "musl"))] mod gpu; mod net; -mod queue; mod rng; mod scsi; pub mod vhost; mod virtio_mmio; #[allow(dead_code)] mod virtio_pci; +mod virtqueue; pub use anyhow::Result; pub use balloon::*; pub use block::{Block, BlockState}; @@ -49,7 +49,6 @@ pub use error::*; pub use gpu::*; use log::{error, warn}; pub use net::*; -pub use queue::*; pub use rng::{Rng, RngState}; pub use scsi::bus as ScsiBus; pub use scsi::controller as ScsiCntlr; @@ -58,6 +57,7 @@ pub use vhost::kernel as VhostKern; pub use vhost::user as VhostUser; pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; pub use virtio_pci::VirtioPciDevice; +pub use virtqueue::*; use std::cmp; use std::sync::{Arc, Mutex}; diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs new file mode 100644 index 000000000..0e7981661 --- /dev/null +++ b/virtio/src/virtqueue/mod.rs @@ -0,0 +1,206 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod split; + +use address_space::{AddressSpace, GuestAddress}; +use anyhow::{anyhow, bail, Result}; +use std::sync::Arc; + +use crate::VirtioError; +pub use split::*; + +/// Split Virtqueue. +pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; +/// Packed Virtqueue. +pub const QUEUE_TYPE_PACKED_VRING: u16 = 2; +/// Invalid queue vector num. +pub const INVALID_VECTOR_NUM: u16 = 0xFFFF; + +/// This marks a buffer as continuing via the next field. +const VIRTQ_DESC_F_NEXT: u16 = 0x1; +/// This marks a buffer as write-only (otherwise read-only). +const VIRTQ_DESC_F_WRITE: u16 = 0x2; +/// This means the buffer contains a list of buffer descriptors. +const VIRTQ_DESC_F_INDIRECT: u16 = 0x4; + +fn checked_offset_mem( + mmio_space: &Arc, + base: GuestAddress, + offset: u64, +) -> Result { + if !mmio_space.address_in_memory(base, offset) { + bail!( + "Invalid Address for queue: base 0x{:X}, size {}", + base.raw_value(), + offset + ); + } + base.checked_add(offset).ok_or_else(|| { + anyhow!(VirtioError::AddressOverflow( + "queue", + base.raw_value(), + offset + )) + }) +} + +/// IO vector element which contains the information of a descriptor. +#[derive(Debug, Clone, Copy)] +pub struct ElemIovec { + /// Guest address of descriptor. + pub addr: GuestAddress, + /// Length of descriptor. + pub len: u32, +} + +/// IO request element. +pub struct Element { + /// Index of the descriptor in the table. + pub index: u16, + /// Number of descriptors. + pub desc_num: u16, + /// Vector to put host readable descriptors. + pub out_iovec: Vec, + /// Vector to put host writable descriptors. + pub in_iovec: Vec, +} + +impl Element { + /// Create an IO request element. + /// + /// # Arguments + /// + /// * `index` - The index of descriptor in the virqueue descriptor table. + fn new(index: u16) -> Self { + Element { + index, + desc_num: 0, + out_iovec: Vec::new(), + in_iovec: Vec::new(), + } + } + + pub fn iovec_size(iovec: &[ElemIovec]) -> u32 { + let mut size: u32 = 0; + for elem in iovec.iter() { + size += elem.len; + } + size + } +} + +/// Vring operations. +pub trait VringOps { + /// Return true if the vring is enable by driver. + fn is_enabled(&self) -> bool; + + /// Return true if the configuration of vring is valid. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + fn is_valid(&self, sys_mem: &Arc) -> bool; + + /// Assemble an IO request element with descriptors from the available vring. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + /// * `features` - Bit mask of features negotiated by the backend and the frontend. + fn pop_avail(&mut self, sys_mem: &Arc, features: u64) -> Result; + + /// Rollback the entry which is pop from available queue by `pop_avail`. + fn push_back(&mut self); + + /// Fill the used vring after processing the IO request. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + /// * `index` - Index of descriptor in the virqueue descriptor table. + /// * `len` - Total length of the descriptor chain which was used (written to). + fn add_used(&mut self, sys_mem: &Arc, index: u16, len: u32) -> Result<()>; + + /// Return true if guest needed to be notified. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + /// * `features` - Bit mask of features negotiated by the backend and the frontend. + fn should_notify(&mut self, sys_mem: &Arc, features: u64) -> bool; + + /// Give guest a hint to suppress virtqueue notification. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + /// * `features` - Bit mask of features negotiated by the backend and the frontend. + /// * `suppress` - Suppress virtqueue notification or not. + fn suppress_queue_notify( + &mut self, + sys_mem: &Arc, + features: u64, + suppress: bool, + ) -> Result<()>; + + /// Get the actual size of the vring. + fn actual_size(&self) -> u16; + + /// Get the configuration of the vring. + fn get_queue_config(&self) -> QueueConfig; + + /// The number of descriptor chains in the available ring. + fn avail_ring_len(&mut self, sys_mem: &Arc) -> Result; + + fn get_host_address_from_cache(&self, addr: GuestAddress, mem_space: &Arc) + -> u64; +} + +/// Virtio queue. +pub struct Queue { + /// Vring structure. + pub vring: Box, +} + +impl Queue { + /// Create a virtqueue. + /// + /// # Arguments + /// + /// * `queue_config` - Configuration of the vring. + /// * `queue_type` - Type of virtqueue. + pub fn new(queue_config: QueueConfig, queue_type: u16) -> Result { + let vring: Box = match queue_type { + QUEUE_TYPE_SPLIT_VRING => Box::new(SplitVring::new(queue_config)), + _ => { + bail!("Unsupported queue type {}", queue_type); + } + }; + + Ok(Queue { vring }) + } + + /// Return true if the virtqueue is enabled by driver. + pub fn is_enabled(&self) -> bool { + self.vring.is_enabled() + } + + /// Return true if the memory layout of the virqueue is valid. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space to which the vring belongs. + pub fn is_valid(&self, sys_mem: &Arc) -> bool { + self.vring.is_valid(sys_mem) + } +} diff --git a/virtio/src/queue.rs b/virtio/src/virtqueue/split.rs similarity index 92% rename from virtio/src/queue.rs rename to virtio/src/virtqueue/split.rs index 9e44a4b38..5fa733953 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/virtqueue/split.rs @@ -17,46 +17,37 @@ use std::sync::atomic::{fence, Ordering}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use util::byte_code::ByteCode; -use super::{virtio_has_feature, VIRTIO_F_RING_EVENT_IDX}; -use crate::VirtioError; -use anyhow::{anyhow, bail, Context, Result}; +use super::{ + checked_offset_mem, ElemIovec, Element, VringOps, INVALID_VECTOR_NUM, VIRTQ_DESC_F_INDIRECT, + VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE, +}; +use crate::{virtio_has_feature, VirtioError, VIRTIO_F_RING_EVENT_IDX}; /// When host consumes a buffer, don't interrupt the guest. const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; /// When guest produces a buffer, don't notify the host. const VRING_USED_F_NO_NOTIFY: u16 = 1; -/// Split Virtqueue. -pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; -/// Packed Virtqueue. -pub const QUEUE_TYPE_PACKED_VRING: u16 = 2; + /// Max total len of a descriptor chain. const DESC_CHAIN_MAX_TOTAL_LEN: u64 = 1u64 << 32; -/// Invalid queue vector num -pub const INVALID_VECTOR_NUM: u16 = 0xFFFF; - -fn checked_offset_mem( - mmio_space: &Arc, - base: GuestAddress, - offset: u64, -) -> Result { - if !mmio_space.address_in_memory(base, offset) { - bail!( - "Invalid Address for queue: base 0x{:X}, size {}", - base.raw_value(), - offset - ); - } - base.checked_add(offset).ok_or_else(|| { - anyhow!(VirtioError::AddressOverflow( - "queue", - base.raw_value(), - offset - )) - }) -} +/// The length of used element. +const USEDELEM_LEN: u64 = size_of::() as u64; +/// The length of avail element. +const AVAILELEM_LEN: u64 = size_of::() as u64; +/// The length of available ring except array of avail element(flags: u16 idx: u16 used_event: u16). +const VRING_AVAIL_LEN_EXCEPT_AVAILELEM: u64 = (size_of::() * 3) as u64; +/// The length of used ring except array of used element(flags: u16 idx: u16 avail_event: u16). +const VRING_USED_LEN_EXCEPT_USEDELEM: u64 = (size_of::() * 3) as u64; +/// The length of flags(u16) and idx(u16). +const VRING_FLAGS_AND_IDX_LEN: u64 = size_of::() as u64; +/// The position of idx in the available ring and the used ring. +const VRING_IDX_POSITION: u64 = size_of::() as u64; +/// The length of virtio descriptor. +const DESCRIPTOR_LEN: u64 = size_of::() as u64; #[derive(Default, Clone, Copy)] pub struct VirtioAddrCache { @@ -67,6 +58,7 @@ pub struct VirtioAddrCache { /// Host virtual address of the used ring. pub used_ring_host: u64, } + /// The configuration of virtqueue. #[derive(Default, Clone, Copy)] pub struct QueueConfig { @@ -123,118 +115,6 @@ impl QueueConfig { } } -/// IO vector element which contains the information of a descriptor. -#[derive(Debug, Clone, Copy)] -pub struct ElemIovec { - /// Guest address of descriptor. - pub addr: GuestAddress, - /// Length of descriptor. - pub len: u32, -} - -/// IO request element. -pub struct Element { - /// Index of the descriptor in the table. - pub index: u16, - /// Number of descriptors. - pub desc_num: u16, - /// Vector to put host readable descriptors. - pub out_iovec: Vec, - /// Vector to put host writable descriptors. - pub in_iovec: Vec, -} - -impl Element { - /// Create an IO request element. - /// - /// # Arguments - /// - /// * `index` - The index of descriptor in the virqueue descriptor table. - pub fn new(index: u16) -> Self { - Element { - index, - desc_num: 0, - out_iovec: Vec::new(), - in_iovec: Vec::new(), - } - } - - pub fn iovec_size(iovec: &[ElemIovec]) -> u32 { - let mut size: u32 = 0; - for elem in iovec.iter() { - size += elem.len; - } - size - } -} - -/// Vring operations. -pub trait VringOps { - /// Return true if the vring is enable by driver. - fn is_enabled(&self) -> bool; - - /// Return true if the configuration of vring is valid. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space to which the vring belongs. - fn is_valid(&self, sys_mem: &Arc) -> bool; - - /// Assemble an IO request element with descriptors from the available vring. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space to which the vring belongs. - /// * `features` - Bit mask of features negotiated by the backend and the frontend. - fn pop_avail(&mut self, sys_mem: &Arc, features: u64) -> Result; - - /// Rollback the entry which is pop from available queue by `pop_avail`. - fn push_back(&mut self); - - /// Fill the used vring after processing the IO request. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space to which the vring belongs. - /// * `index` - Index of descriptor in the virqueue descriptor table. - /// * `len` - Total length of the descriptor chain which was used (written to). - fn add_used(&mut self, sys_mem: &Arc, index: u16, len: u32) -> Result<()>; - - /// Return true if guest needed to be notified. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space to which the vring belongs. - /// * `features` - Bit mask of features negotiated by the backend and the frontend. - fn should_notify(&mut self, sys_mem: &Arc, features: u64) -> bool; - - /// Give guest a hint to suppress virtqueue notification. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space to which the vring belongs. - /// * `features` - Bit mask of features negotiated by the backend and the frontend. - /// * `suppress` - Suppress virtqueue notification or not. - fn suppress_queue_notify( - &mut self, - sys_mem: &Arc, - features: u64, - suppress: bool, - ) -> Result<()>; - - /// Get the actual size of the vring. - fn actual_size(&self) -> u16; - - /// Get the configuration of the vring. - fn get_queue_config(&self) -> QueueConfig; - - /// The number of descriptor chains in the available ring. - fn avail_ring_len(&mut self, sys_mem: &Arc) -> Result; - - fn get_host_address_from_cache(&self, addr: GuestAddress, mem_space: &Arc) - -> u64; -} - /// Virtio used element. #[repr(C)] #[derive(Default, Clone, Copy)] @@ -257,25 +137,6 @@ struct SplitVringFlagsIdx { impl ByteCode for SplitVringFlagsIdx {} -/// The length of used element. -const USEDELEM_LEN: u64 = size_of::() as u64; -/// The length of avail element. -const AVAILELEM_LEN: u64 = size_of::() as u64; -/// The length of available ring except array of avail element(flags: u16 idx: u16 used_event: u16). -const VRING_AVAIL_LEN_EXCEPT_AVAILELEM: u64 = (size_of::() * 3) as u64; -/// The length of used ring except array of used element(flags: u16 idx: u16 avail_event: u16). -const VRING_USED_LEN_EXCEPT_USEDELEM: u64 = (size_of::() * 3) as u64; -/// The length of flags(u16) and idx(u16). -const VRING_FLAGS_AND_IDX_LEN: u64 = size_of::() as u64; -/// The position of idx in the available ring and the used ring. -const VRING_IDX_POSITION: u64 = size_of::() as u64; -/// This marks a buffer as continuing via the next field. -const VIRTQ_DESC_F_NEXT: u16 = 0x1; -/// This marks a buffer as write-only (otherwise read-only). -const VIRTQ_DESC_F_WRITE: u16 = 0x2; -/// This means the buffer contains a list of buffer descriptors. -const VIRTQ_DESC_F_INDIRECT: u16 = 0x4; - struct DescInfo { /// The host virtual address of the descriptor table. table_host: u64, @@ -301,9 +162,6 @@ pub struct SplitVringDesc { pub next: u16, } -/// The length of virtio descriptor. -const DESCRIPTOR_LEN: u64 = size_of::() as u64; - impl SplitVringDesc { /// Create a descriptor of split vring. /// @@ -313,7 +171,7 @@ impl SplitVringDesc { /// * `desc_table` - Guest address of virtqueue descriptor table. /// * `queue_size` - Size of virtqueue. /// * `index` - Index of descriptor in the virqueue descriptor table. - pub fn new( + fn new( sys_mem: &Arc, desc_table_host: u64, queue_size: u16, @@ -514,33 +372,33 @@ impl ByteCode for SplitVringDesc {} #[derive(Default, Clone, Copy)] pub struct SplitVring { /// Region cache information. - pub cache: Option, + cache: Option, /// Guest physical address of the descriptor table. /// The table is composed of descriptors(SplitVringDesc). - pub desc_table: GuestAddress, + desc_table: GuestAddress, /// Guest physical address of the available ring. /// The ring is composed of flags(u16), idx(u16), ring[size](u16) and used_event(u16). - pub avail_ring: GuestAddress, + avail_ring: GuestAddress, /// Guest physical address of the used ring. /// The ring is composed of flags(u16), idx(u16), used_ring[size](UsedElem) and avail_event(u16). - pub used_ring: GuestAddress, + used_ring: GuestAddress, /// Host address cache. - pub addr_cache: VirtioAddrCache, + addr_cache: VirtioAddrCache, /// Indicate whether the queue configuration is finished. - pub ready: bool, + ready: bool, /// The maximal size in elements offered by the device. - pub max_size: u16, + max_size: u16, /// The queue size set by frontend. - pub size: u16, + size: u16, /// Interrupt vector index of the queue for msix - pub vector: u16, + vector: u16, /// The next index which can be popped in the available vring. next_avail: Wrapping, @@ -1037,48 +895,10 @@ impl VringOps for SplitVring { } } -/// Virtio queue. -pub struct Queue { - /// Vring structure. - pub vring: Box, -} - -impl Queue { - /// Create a virtqueue. - /// - /// # Arguments - /// - /// * `queue_config` - Configuration of the vring. - /// * `queue_type` - Type of virtqueue. - pub fn new(queue_config: QueueConfig, queue_type: u16) -> Result { - let vring: Box = match queue_type { - QUEUE_TYPE_SPLIT_VRING => Box::new(SplitVring::new(queue_config)), - _ => { - bail!("Unsupported queue type {}", queue_type); - } - }; - - Ok(Queue { vring }) - } - - /// Return true if the virtqueue is enabled by driver. - pub fn is_enabled(&self) -> bool { - self.vring.is_enabled() - } - - /// Return true if the memory layout of the virqueue is valid. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space to which the vring belongs. - pub fn is_valid(&self, sys_mem: &Arc) -> bool { - self.vring.is_valid(sys_mem) - } -} - #[cfg(test)] mod tests { - pub use super::*; + use super::*; + use crate::{Queue, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING}; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; fn address_space_init() -> Arc { -- Gitee From d20a38cd38071d2e9e752c7c055e5c8f948b9fcd Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 10 Nov 2022 11:28:18 +0800 Subject: [PATCH 0405/2187] pcie: rename and reorder the pcie cap registers The naming and placement of the registers of pci express capability is a little bit free-style, sometimes confusing and misleading, so rename them according to the linux kernel header, and reorder them to make them easier to locate and maintain. Signed-off-by: Zhang Bo --- pci/src/config.rs | 291 +++++++++++++++++++++++-------------------- pci/src/root_port.rs | 10 +- 2 files changed, 160 insertions(+), 141 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 10d12214c..72ac8a519 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -87,56 +87,9 @@ pub const HEADER_TYPE_MULTIFUNC: u8 = 0x80; /// The vendor ID for PCI devices other than virtio. pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; -/// PCI Express capability registers, same as kernel defines -/// Link Training -pub const PCI_EXP_LNKSTA: u16 = 18; -/// Data Link Layer Link Active -pub const PCI_EXP_LNKSTA_DLLLA: u16 = 0x2000; -/// Negotiated Link Width -pub const PCI_EXP_LNKSTA_NLW: u16 = 0x03f0; -/// Slot Control -pub const PCI_EXP_SLTCTL: u16 = 24; -/// Power Controller Control -pub const PCI_EXP_SLTCTL_PCC: u16 = 0x0400; -/// Attention Button Pressed Enable -pub const PCI_EXP_SLTCTL_ABPE: u16 = 0x0001; -/// Presence Detect Changed Enable -pub const PCI_EXP_SLTCTL_PDCE: u16 = 0x0008; -/// Command Completed Interrupt Enable -pub const PCI_EXP_SLTCTL_CCIE: u16 = 0x0010; -/// Hot-Plug Interrupt Enable -pub const PCI_EXP_SLTCTL_HPIE: u16 = 0x0020; -/// Power Indicator off -pub const PCI_EXP_SLTCTL_PWR_IND_OFF: u16 = 0x0300; -/// Power Indicator on -pub const PCI_EXP_SLTCTL_PWR_IND_ON: u16 = 0x0100; -/// Slot Status -pub const PCI_EXP_SLTSTA: u16 = 26; -/// Attention Button Pressed -pub const PCI_EXP_SLTSTA_ABP: u16 = 0x0001; -/// Presence Detect Changed -pub const PCI_EXP_SLTSTA_PDC: u16 = 0x0008; -/// Command Completed -pub const PCI_EXP_SLTSTA_CC: u16 = 0x0010; -/// Presence Detect State -pub const PCI_EXP_SLTSTA_PDS: u16 = 0x0040; - -/// Hot plug event -/// Presence detect changed -pub const PCI_EXP_HP_EV_PDC: u16 = PCI_EXP_SLTCTL_PDCE; -/// Attention button pressed -pub const PCI_EXP_HP_EV_ABP: u16 = PCI_EXP_SLTCTL_ABPE; -/// Command completed -pub const PCI_EXP_HP_EV_CCI: u16 = PCI_EXP_SLTCTL_CCIE; - const PCI_CONFIG_HEAD_END: u8 = 64; const NEXT_CAP_OFFSET: u8 = 0x01; const STATUS_CAP_LIST: u16 = 0x0010; -const PCIE_CAP_VERSION_SHIFT: u8 = 16; -const PCIE_CAP_NEXT_OFFSET_SHIFT: u8 = 20; -const PCIE_CAP_SIZE: u8 = 0x3c; -const PCIE_CAP_VERSION_2: u16 = 0x0002; -const PCIE_CAP_SLOT_IMPLEMENTED: u16 = 0x0100; /// 16 bits PCI Status. pub const STATUS: u8 = 0x06; @@ -188,80 +141,144 @@ pub const MINMUM_BAR_SIZE_FOR_MMIO: usize = 0x1000; /// pio bar's minimum size shall be 4B pub const MINMUM_BAR_SIZE_FOR_PIO: usize = 0x4; +/// PCI Express capability registers, same as kernel defines + +const PCI_EXT_CAP_VER_SHIFT: u8 = 16; +const PCI_EXT_CAP_NEXT_SHIFT: u8 = 20; +const PCI_EXP_VER2_SIZEOF: u8 = 0x3c; +const PCI_EXP_FLAGS_VER2: u16 = 0x0002; +const PCI_EXP_FLAGS_SLOT: u16 = 0x0100; +// PCIe type flag +const PCI_EXP_FLAGS_TYPE_SHIFT: u16 = 4; +const PCI_EXP_FLAGS_TYPE: u16 = 0x00f0; + // Role-Based error reporting. -const PCIE_CAP_RBER: u32 = 0x8000; -// Correctable error reporting. -const PCIE_CAP_DEV_CER: u16 = 0x01; -// Non-Fatal error reporting. -const PCIE_CAP_DEV_NFER: u16 = 0x02; -// Fatal error reporting. -const PCIE_CAP_DEV_FER: u16 = 0x04; -// Unsupported request reporting. -const PCIE_CAP_DEV_URR: u16 = 0x08; -// Max link speed. -const PCIE_CAP_MLS_16GT: u32 = 0x0000_0004; -// Maximum link width. -const PCIE_CAP_MLW_X32: u32 = 0x0000_0200; +const PCI_EXP_DEVCAP_RBER: u32 = 0x8000; + +// Correctable error reporting enable. +const PCI_EXP_DEVCTL_CERE: u16 = 0x01; +// Non-Fatal error reporting enable. +const PCI_EXP_DEVCTL_NFERE: u16 = 0x02; +// Fatal error reporting enable. +const PCI_EXP_DEVCTL_FERE: u16 = 0x04; +// Unsupported request reporting enable. +const PCI_EXP_DEVCTL_URRE: u16 = 0x08; + +// Supported max link speed, 16GT for default. +const PCI_EXP_LNKCAP_MLS_16GT: u32 = 0x0000_0004; +// Supported maximum link width, X32 for default. +const PCI_EXP_LNKCAP_MLW_X32: u32 = 0x0000_0200; // Active state power management support. -const PCIE_CAP_ASPM_L0S: u32 = 0x0000_0400; -// Link bandwidth notification capability -const PCIE_CAP_LINK_LBNC: u32 = 0x0020_0000; -// Data link layer link active reporting capable -const PCIE_CAP_LINK_DLLLARC: u32 = 0x0010_0000; -const PCIE_CAP_PORT_NUM_SHIFT: u8 = 24; -// Current link speed. -pub const PCIE_CAP_CLS_2_5G: u16 = 0x0001; -// Negotiated link width. -pub const PCIE_CAP_NLW_X1: u16 = 0x0010; +const PCI_EXP_LNKCAP_ASPMS_0S: u32 = 0x0000_0400; +// Link bandwidth notification capability. +const PCI_EXP_LNKCAP_LBNC: u32 = 0x0020_0000; +// Data link layer link active reporting capable. +const PCI_EXP_LNKCAP_DLLLARC: u32 = 0x0010_0000; +// Port number reg's shift. +const PCI_EXP_LNKCAP_PN_SHIFT: u8 = 24; + +/// Link Training +pub const PCI_EXP_LNKSTA: u16 = 18; +// Current link speed, 2.5GB for default. +pub const PCI_EXP_LNKSTA_CLS_2_5GB: u16 = 0x0001; +// Negotiated link width, X1 for default. +pub const PCI_EXP_LNKSTA_NLW_X1: u16 = 0x0010; +/// Data Link Layer Link Active +pub const PCI_EXP_LNKSTA_DLLLA: u16 = 0x2000; +/// Negotiated Link Width +pub const PCI_EXP_LNKSTA_NLW: u16 = 0x03f0; + // Attention button present. -const PCIE_CAP_SLOTCAP_ABP: u32 = 0x0000_0001; +const PCI_EXP_SLTCAP_ABP: u32 = 0x0000_0001; // Power controller present. -const PCIE_CAP_SLOTCAP_PCP: u32 = 0x0000_0002; +const PCI_EXP_SLTCAP_PCP: u32 = 0x0000_0002; // Attention indicator present. -const PCIE_CAP_SLOTCAP_AIP: u32 = 0x0000_0008; +const PCI_EXP_SLTCAP_AIP: u32 = 0x0000_0008; // Power indicator present. -const PCIE_CAP_SLOTCAP_PIP: u32 = 0x0000_0010; +const PCI_EXP_SLTCAP_PIP: u32 = 0x0000_0010; // Hot-Plug surprise. -const PCIE_CAP_SLOTCAP_HPS: u32 = 0x0000_0020; +const PCI_EXP_SLTCAP_HPS: u32 = 0x0000_0020; // Hot-Plug capable. -const PCIE_CAP_SLOTCAP_HPC: u32 = 0x0000_0040; -const PCIE_CAP_SLOT_NUM_SHIFT: u32 = 19; +const PCI_EXP_SLTCAP_HPC: u32 = 0x0000_0040; +// Physical slot number reg's shift. +const PCI_EXP_SLTCAP_PSN_SHIFT: u32 = 19; + +/// Slot Control +pub const PCI_EXP_SLTCTL: u16 = 24; +/// Attention Button Pressed Enable +pub const PCI_EXP_SLTCTL_ABPE: u16 = 0x0001; +/// Presence Detect Changed Enable +pub const PCI_EXP_SLTCTL_PDCE: u16 = 0x0008; +/// Command Completed Interrupt Enable +pub const PCI_EXP_SLTCTL_CCIE: u16 = 0x0010; +/// Hot-Plug Interrupt Enable +pub const PCI_EXP_SLTCTL_HPIE: u16 = 0x0020; // Attention Indicator Control. -const PCIE_CAP_SLOT_AIC_MASK: u16 = 0x00c0; -const PCIE_CAP_SLOT_AIC_OFF: u16 = 0x00c0; +const PCI_EXP_SLTCTL_AIC: u16 = 0x00c0; +// Attention Indicator off. +const PCI_EXP_SLTCTL_ATTN_IND_OFF: u16 = 0x00c0; // Power Indicator Control. -pub(crate) const PCIE_CAP_SLOT_PIC_MASK: u16 = 0x0300; -pub(crate) const PCIE_CAP_SLOT_PIC_BLINK: u16 = 0x200; -const PCIE_CAP_SLOT_PIC_OFF: u16 = 0x0300; - -// Power controller control. -const PCIE_CAP_SLOT_PCC: u16 = 0x0400; +pub(crate) const PCI_EXP_SLTCTL_PIC: u16 = 0x0300; +// Power Indicator blinking. +pub(crate) const PCI_EXP_SLTCTL_PWR_IND_BLINK: u16 = 0x200; +/// Power Indicator on +pub const PCI_EXP_SLTCTL_PWR_IND_ON: u16 = 0x0100; +// Power Indicator off. +pub const PCI_EXP_SLTCTL_PWR_IND_OFF: u16 = 0x0300; +/// Power Controller Control +pub const PCI_EXP_SLTCTL_PCC: u16 = 0x0400; // Electromechanical interlock control. -const PCIE_CAP_SLOT_EIC: u16 = 0x0800; +const PCI_EXP_SLTCTL_EIC: u16 = 0x0800; + +/// Slot Status +pub const PCI_EXP_SLTSTA: u16 = 26; +/// Attention Button Pressed +pub const PCI_EXP_SLTSTA_ABP: u16 = 0x0001; +/// Presence Detect Changed +pub const PCI_EXP_SLTSTA_PDC: u16 = 0x0008; +/// Command Completed +pub const PCI_EXP_SLTSTA_CC: u16 = 0x0010; +/// Presence Detect State +pub const PCI_EXP_SLTSTA_PDS: u16 = 0x0040; + // System error on correctable error enable. -const PCIE_CAP_ROOT_SECEE: u16 = 0x01; +const PCI_EXP_RTCTL_SECEE: u16 = 0x01; // System error on non-fatal error enable. -const PCIE_CAP_ROOT_SENFEE: u16 = 0x02; +const PCI_EXP_RTCTL_SENFEE: u16 = 0x02; // System error on fatal error enable. -const PCIE_CAP_ROOT_SEFEE: u16 = 0x04; -const PCIE_CAP_ARI: u32 = 0x0000_0020; +const PCI_EXP_RTCTL_SEFEE: u16 = 0x04; + +// Alternative Routing-ID. +const PCI_EXP_DEVCAP2_ARI: u32 = 0x0000_0020; // Extended Fmt Field Supported. -const PCIE_CAP_DEV_EFFS: u32 = 0x0010_0000; +const PCI_EXP_DEVCAP2_EFF: u32 = 0x0010_0000; // End-End TLP Prefix Supported. -const PCIE_CAP_DEV_EETPS: u32 = 0x0020_0000; -const PCIE_CAP_ARI_ENABLE: u16 = 0x0020; +const PCI_EXP_DEVCAP2_EETLPP: u32 = 0x0020_0000; +// Alternative Routing-ID. +const PCI_EXP_DEVCTL2_ARI: u16 = 0x0020; // End-End TLP Prefix Blocking -const PCIE_CAP_DEV_EETPB: u16 = 0x8000; +const PCI_EXP_DEVCTL2_EETLPPB: u16 = 0x8000; + // Supported Link Speeds Vector. -const PCIE_CAP_LINK_SLSV_2_5GT: u32 = 0x02; -const PCIE_CAP_LINK_SLSV_5GT: u32 = 0x04; -const PCIE_CAP_LINK_SLSV_8GT: u32 = 0x08; -const PCIE_CAP_LINK_SLSV_16GT: u32 = 0x10; -// Target Link Speed. -const PCIE_CAP_LINK_TLS_16GT: u16 = 0x0004; -// PCIe type flag -const PCI_EXP_FLAGS_TYPE_SHIFT: u16 = 4; -const PCI_EXP_FLAGS_TYPE: u16 = 0x00f0; +const PCI_EXP_LNKCAP2_SLS_2_5GB: u32 = 0x02; +const PCI_EXP_LNKCAP2_SLS_5_0GB: u32 = 0x04; +const PCI_EXP_LNKCAP2_SLS_8_0GB: u32 = 0x08; +const PCI_EXP_LNKCAP2_SLS_16_0GB: u32 = 0x10; + +// Target Link Speed, 16GT for default. +const PCI_EXP_LNKCTL2_TLS_16_0GT: u16 = 0x0004; + +/// Hot plug event +/// Presence detect changed +pub const PCI_EXP_HP_EV_PDC: u16 = PCI_EXP_SLTCTL_PDCE; +/// Attention button pressed +pub const PCI_EXP_HP_EV_ABP: u16 = PCI_EXP_SLTCTL_ABPE; +/// Command completed +pub const PCI_EXP_HP_EV_CCI: u16 = PCI_EXP_SLTCTL_CCIE; + +// XHCI device id +pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; +pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; /// Type of bar region. #[derive(PartialEq, Debug, Copy, Clone)] @@ -740,14 +757,14 @@ impl PciConfig { le_write_u32( &mut self.config, offset, - id as u32 | (version << PCIE_CAP_VERSION_SHIFT), + id as u32 | (version << PCI_EXT_CAP_VER_SHIFT), )?; if self.last_ext_cap_offset != 0 { let old_value = le_read_u32(&self.config, self.last_ext_cap_offset as usize)?; le_write_u32( &mut self.config, self.last_ext_cap_offset as usize, - old_value | ((offset as u32) << PCIE_CAP_NEXT_OFFSET_SHIFT), + old_value | ((offset as u32) << PCI_EXT_CAP_NEXT_SHIFT), )?; } self.last_ext_cap_offset = offset as u16; @@ -763,20 +780,22 @@ impl PciConfig { /// * `port_num` - Port number. /// * `dev_type` - Device type. pub fn add_pcie_cap(&mut self, devfn: u8, port_num: u8, dev_type: u8) -> Result { - let cap_offset: usize = self.add_pci_cap(CapId::Pcie as u8, PCIE_CAP_SIZE as usize)?; + let cap_offset: usize = + self.add_pci_cap(CapId::Pcie as u8, PCI_EXP_VER2_SIZEOF as usize)?; self.pci_express_cap_offset = cap_offset as u16; let mut offset: usize = cap_offset + PcieCap::CapReg as usize; let pci_type = (dev_type << PCI_EXP_FLAGS_TYPE_SHIFT) as u16 & PCI_EXP_FLAGS_TYPE; le_write_u16( &mut self.config, offset, - pci_type | PCIE_CAP_VERSION_2 | PCIE_CAP_SLOT_IMPLEMENTED, + pci_type | PCI_EXP_FLAGS_VER2 | PCI_EXP_FLAGS_SLOT, )?; offset = cap_offset + PcieCap::DevCap as usize; - le_write_u32(&mut self.config, offset, PCIE_CAP_RBER)?; + le_write_u32(&mut self.config, offset, PCI_EXP_DEVCAP_RBER)?; offset = cap_offset + PcieCap::DevCtl as usize; - let mask = PCIE_CAP_DEV_CER | PCIE_CAP_DEV_NFER | PCIE_CAP_DEV_FER | PCIE_CAP_DEV_URR; + let mask = + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE; le_write_u16(&mut self.write_mask, offset, mask)?; offset = cap_offset + PcieCap::DevStat as usize; le_write_u16(&mut self.write_clear_mask, offset, mask)?; @@ -785,18 +804,18 @@ impl PciConfig { le_write_u32( &mut self.config, offset, - PCIE_CAP_MLS_16GT - | PCIE_CAP_MLW_X32 - | PCIE_CAP_ASPM_L0S - | PCIE_CAP_LINK_LBNC - | PCIE_CAP_LINK_DLLLARC - | ((port_num as u32) << PCIE_CAP_PORT_NUM_SHIFT), + PCI_EXP_LNKCAP_MLS_16GT + | PCI_EXP_LNKCAP_MLW_X32 + | PCI_EXP_LNKCAP_ASPMS_0S + | PCI_EXP_LNKCAP_LBNC + | PCI_EXP_LNKCAP_DLLLARC + | ((port_num as u32) << PCI_EXP_LNKCAP_PN_SHIFT), )?; offset = cap_offset + PcieCap::LinkStat as usize; le_write_u16( &mut self.config, offset, - PCIE_CAP_CLS_2_5G | PCIE_CAP_NLW_X1, + PCI_EXP_LNKSTA_CLS_2_5GB | PCI_EXP_LNKSTA_NLW_X1, )?; let slot: u8 = devfn >> BDF_FUNC_SHIFT; @@ -804,19 +823,19 @@ impl PciConfig { le_write_u32( &mut self.config, offset, - PCIE_CAP_SLOTCAP_ABP - | PCIE_CAP_SLOTCAP_PCP - | PCIE_CAP_SLOTCAP_AIP - | PCIE_CAP_SLOTCAP_PIP - | PCIE_CAP_SLOTCAP_HPS - | PCIE_CAP_SLOTCAP_HPC - | ((slot as u32) << PCIE_CAP_SLOT_NUM_SHIFT), + PCI_EXP_SLTCAP_ABP + | PCI_EXP_SLTCAP_PCP + | PCI_EXP_SLTCAP_AIP + | PCI_EXP_SLTCAP_PIP + | PCI_EXP_SLTCAP_HPS + | PCI_EXP_SLTCAP_HPC + | ((slot as u32) << PCI_EXP_SLTCAP_PSN_SHIFT), )?; offset = cap_offset + PcieCap::SlotCtl as usize; le_write_u16( &mut self.config, offset, - PCIE_CAP_SLOT_AIC_OFF | PCIE_CAP_SLOT_PIC_OFF | PCIE_CAP_SLOT_PCC, + PCI_EXP_SLTCTL_ATTN_IND_OFF | PCI_EXP_SLTCTL_PWR_IND_OFF | PCI_EXP_SLTCTL_PCC, )?; le_write_u16( &mut self.write_mask, @@ -825,10 +844,10 @@ impl PciConfig { | PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE - | PCIE_CAP_SLOT_AIC_MASK - | PCIE_CAP_SLOT_PIC_MASK - | PCIE_CAP_SLOT_PCC - | PCIE_CAP_SLOT_EIC, + | PCI_EXP_SLTCTL_AIC + | PCI_EXP_SLTCTL_PIC + | PCI_EXP_SLTCTL_PCC + | PCI_EXP_SLTCTL_EIC, )?; offset = cap_offset + PcieCap::SlotStat as usize; le_write_u16( @@ -841,33 +860,33 @@ impl PciConfig { le_write_u16( &mut self.write_mask, offset, - PCIE_CAP_ROOT_SECEE | PCIE_CAP_ROOT_SENFEE | PCIE_CAP_ROOT_SEFEE, + PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | PCI_EXP_RTCTL_SEFEE, )?; offset = cap_offset + PcieCap::DevCap2 as usize; le_write_u32( &mut self.config, offset, - PCIE_CAP_ARI | PCIE_CAP_DEV_EFFS | PCIE_CAP_DEV_EETPS, + PCI_EXP_DEVCAP2_ARI | PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP, )?; offset = cap_offset + PcieCap::DevCtl2 as usize; le_write_u16( &mut self.write_mask, offset, - PCIE_CAP_ARI_ENABLE | PCIE_CAP_DEV_EETPB, + PCI_EXP_DEVCTL2_ARI | PCI_EXP_DEVCTL2_EETLPPB, )?; offset = cap_offset + PcieCap::LinkCap2 as usize; le_write_u32( &mut self.config, offset, - PCIE_CAP_LINK_SLSV_2_5GT - | PCIE_CAP_LINK_SLSV_5GT - | PCIE_CAP_LINK_SLSV_8GT - | PCIE_CAP_LINK_SLSV_16GT, + PCI_EXP_LNKCAP2_SLS_2_5GB + | PCI_EXP_LNKCAP2_SLS_5_0GB + | PCI_EXP_LNKCAP2_SLS_8_0GB + | PCI_EXP_LNKCAP2_SLS_16_0GB, )?; offset = cap_offset + PcieCap::LinkCtl2 as usize; - le_write_u16(&mut self.config, offset, PCIE_CAP_LINK_TLS_16GT)?; + le_write_u16(&mut self.config, offset, PCI_EXP_LNKCTL2_TLS_16_0GT)?; Ok(cap_offset) } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index fe4a2888e..6168cb0bf 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -28,9 +28,9 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, IO_LIMIT, - MEMORY_BASE, PCIE_CAP_CLS_2_5G, PCIE_CAP_NLW_X1, PCIE_CAP_SLOT_PIC_BLINK, - PCIE_CAP_SLOT_PIC_MASK, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, - PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, + MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, + PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, PREF_MEM_BASE_UPPER, PREF_MEM_RANGE_64BIT, PRIMARY_BUS_NUM, REG_SIZE, SUB_CLASS_CODE, @@ -479,7 +479,7 @@ impl HotplugOps for RootPort { le_write_set_value_u16( &mut self.config.config, (offset + PCI_EXP_LNKSTA) as usize, - PCIE_CAP_CLS_2_5G | PCIE_CAP_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, + PCI_EXP_LNKSTA_CLS_2_5GB | PCI_EXP_LNKSTA_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, )?; self.hotplug_event_notify(); @@ -494,7 +494,7 @@ impl HotplugOps for RootPort { ) .unwrap(); - if (sltctl & PCIE_CAP_SLOT_PIC_MASK) == PCIE_CAP_SLOT_PIC_BLINK { + if (sltctl & PCI_EXP_SLTCTL_PIC) == PCI_EXP_SLTCTL_PWR_IND_BLINK { bail!("Guest is still on the fly of another (un)pluging"); } -- Gitee From 00ab293056be9578d8402303f76c65793f83da09 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 16:02:50 +0800 Subject: [PATCH 0406/2187] modify the serial IRQ number as a constant Modify the serial IRQ number to a constant instead of the magic number. Signed-off-by: yezengruan --- devices/src/legacy/serial.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 50bb207b4..3ca5951c3 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -59,6 +59,9 @@ const UART_MSR_CTS: u8 = 0x10; const UART_MSR_DSR: u8 = 0x20; const UART_MSR_DCD: u8 = 0x80; +/// IRQ number of serial device. +const UART_IRQ: i32 = 4; + const RECEIVER_BUFF_SIZE: usize = 1024; /// Contain register status of serial device. @@ -380,7 +383,7 @@ impl SysBusDevOps for Serial { fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { let mut irq: i32 = -1; if let Some(e) = self.interrupt_evt() { - irq = 4; + irq = UART_IRQ; KVM_FDS.load().register_irqfd(e, irq as u32)?; } Ok(irq) -- Gitee From 22c3d59e26487bdd00691680ea84c7588c703e34 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 16 Nov 2022 21:03:59 +0800 Subject: [PATCH 0407/2187] virtio-mmio: recreate interrupt_status cause snapshot getting stuck Fix commit 5d8eb48 "virtio: add report_virtio_error()". After commit 5d8eb48, the interrupt_cb in VirtioMmioDevice::activate and VirtioMmioDevice::resume is unified to the same one in VirtioMmioDevice::realize. This will cause the vm getting stucked when restoring it from the snapshot. The stucking process: realize() ->assign_interrupt_cb ->let interrupt_status = self.interrupt_status.clone(); ->cb = Arc::new(Box::new(..)); // In cb, it will use interrupt_status. ->self.interrupt_cb = Some(cb); set_state_mut() ->self.state = Arc::new(..); ->self.interrupt_status = Arc::new(...); // This will cause the old clone meaningless. resume() ->activate(.., self.interrupt_cb,..) ... ->self.interrupt_cb() ->interrput_status.fetch_or(..); // It is old clone, "fetch_or" is meaningless. ->interrupt_evt.write(1)...; // The interrupt doing nothing due to the old cloned interrupt_status. So, the interrupt for vm is not processed correctly, the vm will be stucked. We should correct the set_state_mut(), not release the reference on old value for "state" and "interrupt_stats, just modify its value. Signed-off-by: Yan Wang --- virtio/src/virtio_mmio.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index a5e768c28..3c4357d38 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -672,15 +672,13 @@ impl StateTransfer for VirtioMmioDevice { } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = Arc::new(Mutex::new(*VirtioMmioState::from_bytes(state).ok_or_else( - || { - anyhow!(migration::error::MigrationError::FromBytesError( - "MMIO_DEVICE" - )) - }, - )?)); + let s_len = std::mem::size_of::(); + if state.len() != s_len { + bail!("Invalid state length {}, expected {}", state.len(), s_len); + } + let mut locked_state = self.state.lock().unwrap(); + locked_state.as_mut_bytes().copy_from_slice(state); let cloned_mem_space = self.mem_space.clone(); - let locked_state = self.state.lock().unwrap(); let mut queue_states = locked_state.config_space.queues_config [0..locked_state.config_space.queue_num] .to_vec(); @@ -701,8 +699,8 @@ impl StateTransfer for VirtioMmioDevice { )) }) .collect(); - self.interrupt_status = - Arc::new(AtomicU32::new(locked_state.config_space.interrupt_status)); + self.interrupt_status + .store(locked_state.config_space.interrupt_status, Ordering::SeqCst); Ok(()) } -- Gitee From 0dc78141c2b60bfb6c16e53f96b66d4a55772a55 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 17 Nov 2022 14:43:25 +0800 Subject: [PATCH 0408/2187] virtio-mmio/pci: narrowing the scope of the variable Narrowing the scope of the variable to optimize the code. Signed-off-by: Yan Wang --- virtio/src/virtio_mmio.rs | 2 +- virtio/src/virtio_pci.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 3c4357d38..8878ec934 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -457,9 +457,9 @@ impl VirtioMmioDevice { let cloned_state = self.state.clone(); let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, needs_reset: bool| { - let mut locked_state = cloned_state.lock().unwrap(); let status = match int_type { VirtioInterruptType::Config => { + let mut locked_state = cloned_state.lock().unwrap(); if needs_reset { locked_state.config_space.device_status |= CONFIG_STATUS_NEEDS_RESET; if locked_state.config_space.device_status & CONFIG_STATUS_DRIVER_OK diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index e18d16a4d..948f07474 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -609,9 +609,9 @@ impl VirtioPciDevice { let dev_id = self.dev_id.clone(); let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, queue: Option<&Queue>, needs_reset: bool| { - let mut locked_common_cfg = cloned_common_cfg.lock().unwrap(); let vector = match int_type { VirtioInterruptType::Config => { + let mut locked_common_cfg = cloned_common_cfg.lock().unwrap(); if needs_reset { locked_common_cfg.device_status |= CONFIG_STATUS_NEEDS_RESET; if locked_common_cfg.device_status & CONFIG_STATUS_DRIVER_OK == 0 { -- Gitee From 06351d6187b945051c15413dfc665ad4d58592df Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 17:05:15 +0800 Subject: [PATCH 0409/2187] define the free_irqs range of as a constant Avoid free_irqs range using magic numbers. Signed-off-by: yezengruan --- devices/src/legacy/fwcfg.rs | 6 ++---- devices/src/legacy/pflash.rs | 6 ++---- machine/src/micro_vm/mod.rs | 7 ++----- machine/src/standard_vm/aarch64/mod.rs | 4 ++-- machine/src/standard_vm/x86_64/mod.rs | 4 ++-- sysbus/src/lib.rs | 13 +++++++++++++ 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index daedeba27..d783ac436 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1300,15 +1300,13 @@ impl AmlBuilder for FwCfgIO { mod test { use super::*; use address_space::{AddressSpace, HostMemMapping, Region}; + use sysbus::{IRQ_BASE, IRQ_MAX}; fn sysbus_init() -> SysBus { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); - #[cfg(target_arch = "x86_64")] - let free_irqs: (i32, i32) = (5, 15); - #[cfg(target_arch = "aarch64")] - let free_irqs: (i32, i32) = (32, 191); + let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( #[cfg(target_arch = "x86_64")] diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 647e84460..ce6f0341c 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -919,15 +919,13 @@ mod test { use address_space::AddressSpace; use std::fs; pub use std::fs::File; + use sysbus::{IRQ_BASE, IRQ_MAX}; fn sysbus_init() -> SysBus { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); - #[cfg(target_arch = "x86_64")] - let free_irqs: (i32, i32) = (5, 15); - #[cfg(target_arch = "aarch64")] - let free_irqs: (i32, i32) = (32, 191); + let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( #[cfg(target_arch = "x86_64")] diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 9478d3185..7933ea500 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -79,7 +79,7 @@ use machine_manager::{ }; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use migration::{MigrationManager, MigrationStatus}; -use sysbus::SysBus; +use sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; #[cfg(target_arch = "aarch64")] use sysbus::{SysBusDevType, SysRes}; use syscall::syscall_whitelist; @@ -197,10 +197,7 @@ impl LightMachine { #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; - #[cfg(target_arch = "x86_64")] - let free_irqs: (i32, i32) = (5, 15); - #[cfg(target_arch = "aarch64")] - let free_irqs: (i32, i32) = (32, 191); + let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index ea8472556..c18a8a23c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -59,7 +59,7 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; use pci_host_root::PciHostRoot; -use sysbus::{SysBus, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevType, SysRes, IRQ_BASE, IRQ_MAX}; use syscall::syscall_whitelist; use usb::bus::BusDeviceMap; use util::byte_code::ByteCode; @@ -169,7 +169,7 @@ impl StdMachine { .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; let sysbus = SysBus::new( &sys_mem, - (32, 192), + (IRQ_BASE, IRQ_MAX), ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 6c100f8ed..7ca63736e 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -51,7 +51,7 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::SysBus; +use sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; use syscall::syscall_whitelist; use usb::bus::BusDeviceMap; use util::{ @@ -148,7 +148,7 @@ impl StdMachine { let sysbus = SysBus::new( &sys_io, &sys_mem, - (5, 15), + (IRQ_BASE, IRQ_MAX), ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index f949caae7..0882282fe 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -21,6 +21,19 @@ pub use anyhow::{bail, Context, Result}; use hypervisor::kvm::KVM_FDS; use vmm_sys_util::eventfd::EventFd; +// Now that the serial device use a hardcoded IRQ number (4), and the starting +// free IRQ number can be 5. +#[cfg(target_arch = "x86_64")] +pub const IRQ_BASE: i32 = 5; +#[cfg(target_arch = "x86_64")] +pub const IRQ_MAX: i32 = 15; + +// 0-31 is private to each CPU (SGIs and PPIs). +#[cfg(target_arch = "aarch64")] +pub const IRQ_BASE: i32 = 32; +#[cfg(target_arch = "aarch64")] +pub const IRQ_MAX: i32 = 191; + pub struct SysBus { #[cfg(target_arch = "x86_64")] pub sys_io: Arc, -- Gitee From a032d41322d7d009adfc7090dd5abaaae908d70e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 17:20:29 +0800 Subject: [PATCH 0410/2187] define GIC max_irq as a constant Avoid using magic numbers. Signed-off-by: yezengruan --- devices/src/interrupt_controller/aarch64/gicv2.rs | 4 +++- devices/src/interrupt_controller/aarch64/gicv3.rs | 8 +++++--- devices/src/interrupt_controller/aarch64/mod.rs | 4 +++- devices/src/interrupt_controller/mod.rs | 2 ++ devices/src/lib.rs | 1 + machine/src/micro_vm/mod.rs | 4 ++-- machine/src/standard_vm/aarch64/mod.rs | 4 ++-- 7 files changed, 18 insertions(+), 9 deletions(-) diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index 7037a045d..bf44049f4 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -302,6 +302,8 @@ mod tests { use super::super::GICv2Config; use super::*; + use crate::GIC_IRQ_MAX; + #[test] #[serial] fn test_create_gicv2() { @@ -314,7 +316,7 @@ mod tests { let gic_conf = GICConfig { version: Some(GICVersion::GICv2), vcpu_count: 4, - max_irq: 192, + max_irq: GIC_IRQ_MAX, v2: Some(GICv2Config { dist_range: (0x0800_0000, 0x0001_0000), cpu_range: (0x080A_0000, 0x00F6_0000), diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 112a2c253..6db4a2ce4 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -557,6 +557,8 @@ mod tests { use super::super::GICv3Config; use super::*; + use crate::GIC_IRQ_MAX; + #[test] #[serial] fn test_create_gicv3() { @@ -569,7 +571,7 @@ mod tests { let gic_conf = GICConfig { version: Some(GICVersion::GICv3), vcpu_count: 4, - max_irq: 192, + max_irq: GIC_IRQ_MAX, v2: None, v3: Some(GICv3Config { msi: false, @@ -593,7 +595,7 @@ mod tests { let gic_config = GICConfig { version: Some(GICVersion::GICv3), vcpu_count: 4_u64, - max_irq: 192_u32, + max_irq: GIC_IRQ_MAX, v2: None, v3: Some(GICv3Config { msi: false, @@ -619,7 +621,7 @@ mod tests { let gic_config = GICConfig { version: Some(GICVersion::GICv3), vcpu_count: 210_u64, - max_irq: 192_u32, + max_irq: GIC_IRQ_MAX, v3: Some(GICv3Config { msi: true, dist_range: (0x0800_0000, 0x0001_0000), diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index ffb8b52ab..f267bd8df 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -32,6 +32,8 @@ use util::{ // First 32 are private to each CPU (SGIs and PPIs). pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; +// Last usable IRQ on aarch64. +pub const GIC_IRQ_MAX: u32 = 192; /// GIC version type. pub enum GICVersion { @@ -195,7 +197,7 @@ mod tests { let mut gic_conf = GICConfig { version: Some(GICVersion::GICv3), vcpu_count: 4, - max_irq: 192, + max_irq: GIC_IRQ_MAX, v2: None, v3: None, }; diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 0aee43a30..7eb7dd4d2 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -37,5 +37,7 @@ pub use aarch64::GICv2Config as ICGICv2Config; pub use aarch64::GICv3Config as ICGICv3Config; #[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; +#[cfg(target_arch = "aarch64")] +pub use aarch64::GIC_IRQ_MAX; pub use anyhow::Result; pub use error::InterruptError; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 2bc76ee36..14e32d2a4 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -22,5 +22,6 @@ pub mod legacy; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, InterruptError as IntCtrlErrs, + GIC_IRQ_MAX, }; pub use legacy::error::LegacyError as LegacyErrs; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 7933ea500..434354c5b 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -61,7 +61,7 @@ use devices::legacy::PL031; use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; #[cfg(target_arch = "aarch64")] -use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController}; +use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; @@ -510,7 +510,7 @@ impl MachineOps for LightMachine { let intc_conf = ICGICConfig { version: None, vcpu_count, - max_irq: 192, + max_irq: GIC_IRQ_MAX, v3: Some(v3), v2: Some(v2), }; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c18a8a23c..a8f5f69e9 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -44,7 +44,7 @@ use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; -use devices::{ICGICConfig, ICGICv3Config, InterruptController}; +use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, BootSource, Incoming, MigrateMode, NumaNode, NumaNodes, @@ -387,7 +387,7 @@ impl MachineOps for StdMachine { let intc_conf = ICGICConfig { version: None, vcpu_count, - max_irq: 192, + max_irq: GIC_IRQ_MAX, v2: None, v3: Some(v3), }; -- Gitee From db2daa06eb2a7ef26d82b69bbe650ca0b71ff2c5 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Nov 2022 17:44:05 +0800 Subject: [PATCH 0411/2187] modify the RTC IRQ to be allocated by the sysbus The range of sysbus free_irqs is 5 to 15. If the RTC IRQ is assigned as 8, it may conflict with the IRQ number automatically assigned by sysbus. The RTC device now use the IRQ number assigned by sysbus. Signed-off-by: yezengruan --- devices/src/legacy/mod.rs | 2 +- devices/src/legacy/rtc.rs | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index ee3efdb8d..f893a21f4 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -44,7 +44,7 @@ mod ramfb; mod rtc; mod serial; #[cfg(target_arch = "x86_64")] -pub use self::rtc::{RTC, RTC_IRQ, RTC_PORT_INDEX}; +pub use self::rtc::{RTC, RTC_PORT_INDEX}; pub use anyhow::Result; pub use chardev::{Chardev, InputReceiver}; pub use error::LegacyError; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 07286d713..415b4972e 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -19,7 +19,6 @@ use acpi::{ }; use address_space::GuestAddress; use anyhow::Result; -use hypervisor::kvm::KVM_FDS; use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; @@ -30,8 +29,6 @@ use util::time::{mktime64, NANOSECONDS_PER_SECOND}; pub const RTC_PORT_INDEX: u64 = 0x70; /// IO port of RTC device to read/write data from selected register. pub const RTC_PORT_DATA: u64 = 0x71; -/// IRQ number of RTC device. -pub const RTC_IRQ: u32 = 8; /// Index of register of time in RTC static RAM. const RTC_SECONDS: u8 = 0x00; @@ -135,7 +132,7 @@ impl RTC { res: SysRes { region_base: RTC_PORT_INDEX, region_size: 8, - irq: RTC_IRQ as i32, + irq: -1, }, mem_size: 0, gap_start: 0, @@ -386,15 +383,6 @@ impl SysBusDevOps for RTC { self.interrupt_evt.as_ref() } - fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { - let mut irq: i32 = -1; - if let Some(e) = self.interrupt_evt() { - irq = RTC_IRQ as i32; - KVM_FDS.load().register_irqfd(e, irq as u32)?; - } - Ok(irq) - } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.res) } -- Gitee From 39f5c4b0d775ed37d300c3bb6911aaf84ed75f24 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 16 Nov 2022 19:19:34 +0800 Subject: [PATCH 0412/2187] pci: unplug: skip notifing guest when the slot has been powered off When the slot has been powered off, it means that the guest has no need to do the front-end unpluging work anymore, thus, skip notifying the guest if so. Signed-off-by: Zhang Bo --- pci/src/config.rs | 2 ++ pci/src/root_port.rs | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 72ac8a519..9706d8fda 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -227,6 +227,8 @@ pub const PCI_EXP_SLTCTL_PWR_IND_ON: u16 = 0x0100; pub const PCI_EXP_SLTCTL_PWR_IND_OFF: u16 = 0x0300; /// Power Controller Control pub const PCI_EXP_SLTCTL_PCC: u16 = 0x0400; +/// Power Off +pub const PCI_EXP_SLTCTL_PWR_OFF: u16 = 0x0400; // Electromechanical interlock control. const PCI_EXP_SLTCTL_EIC: u16 = 0x0800; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 6168cb0bf..578087feb 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -31,10 +31,10 @@ use super::config::{ MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, - PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, - PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, - PREF_MEM_BASE_UPPER, PREF_MEM_RANGE_64BIT, PRIMARY_BUS_NUM, REG_SIZE, SUB_CLASS_CODE, - VENDOR_ID, + PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, + PREF_MEMORY_LIMIT, PREF_MEM_BASE_UPPER, PREF_MEM_RANGE_64BIT, PRIMARY_BUS_NUM, REG_SIZE, + SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; use crate::hotplug::HotplugOps; @@ -514,7 +514,7 @@ impl HotplugOps for RootPort { PCI_EXP_LNKSTA_DLLLA, )?; - let mut slot_status = PCI_EXP_HP_EV_ABP; + let mut slot_status = 0; if let Some(&true) = FAST_UNPLUG_FEATURE.get() { slot_status |= PCI_EXP_HP_EV_PDC; } @@ -523,6 +523,19 @@ impl HotplugOps for RootPort { (offset + PCI_EXP_SLTSTA) as usize, slot_status, )?; + + if ((sltctl & PCI_EXP_SLTCTL_PIC) == PCI_EXP_SLTCTL_PWR_IND_OFF) + && ((sltctl & PCI_EXP_SLTCTL_PCC) == PCI_EXP_SLTCTL_PWR_OFF) + { + // if the slot has already been unpluged, skip notifing the guest. + return Ok(()); + } + + le_write_set_value_u16( + &mut self.config.config, + (offset + PCI_EXP_SLTSTA) as usize, + slot_status | PCI_EXP_HP_EV_ABP, + )?; self.hotplug_event_notify(); Ok(()) } -- Gitee From 929b6916e51a0c71c07ff3793bb5141f9e94cb63 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 17 Nov 2022 04:55:28 -0500 Subject: [PATCH 0413/2187] virtio-pci: set msi vector num to FFFF when guest sets invalid msix vector When the guest sets an invalid value of config_msix_vector/queue_msix_vector, rewrite the number to 0xFFFF to let the guest know that something went wrong. Meanwhile, set the initial value of msix_config to 0xFFFF as well. Signed-off-by: Zhang Bo --- virtio/src/queue.rs | 4 +- virtio/src/virtio_pci.rs | 101 +++++++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 20 deletions(-) diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index 27c8658d2..cbfac1a2e 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -34,6 +34,8 @@ pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; pub const QUEUE_TYPE_PACKED_VRING: u16 = 2; /// Max total len of a descriptor chain. const DESC_CHAIN_MAX_TOTAL_LEN: u64 = 1u64 << 32; +/// Invalid queue vector num +pub const INVALID_VECTOR_NUM: u16 = 0xFFFF; fn checked_offset_mem( mmio_space: &Arc, @@ -109,7 +111,7 @@ impl QueueConfig { max_size, size: max_size, ready: false, - vector: 0, + vector: INVALID_VECTOR_NUM, next_avail: 0, next_used: 0, last_signal_used: 0, diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 72d5a6c78..52481222f 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -44,9 +44,10 @@ use crate::{ }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, - CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, QUEUE_TYPE_PACKED_VRING, - QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_MMIO_INT_CONFIG, - VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, + CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, + QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, + VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, + VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -170,7 +171,7 @@ impl VirtioPciCommonConfig { device_status: 0, config_generation: 0, queue_select: 0, - msix_config: Arc::new(AtomicU16::new(0)), + msix_config: Arc::new(AtomicU16::new(INVALID_VECTOR_NUM)), queues_config, queue_type: QUEUE_TYPE_SPLIT_VRING, } @@ -183,7 +184,7 @@ impl VirtioPciCommonConfig { self.device_status = 0; self.config_generation = 0; self.queue_select = 0; - self.msix_config.store(0_u16, Ordering::SeqCst); + self.msix_config.store(INVALID_VECTOR_NUM, Ordering::SeqCst); self.queue_type = QUEUE_TYPE_SPLIT_VRING; self.queues_config.iter_mut().for_each(|q| q.reset()); } @@ -211,6 +212,19 @@ impl VirtioPciCommonConfig { .ok_or_else(|| anyhow!("pci-reg queue_select overflows")) } + fn revise_queue_vector(&self, vector_nr: u32, virtio_pci_dev: &VirtioPciDevice) -> u32 { + let msix = &virtio_pci_dev.config.msix; + if msix.is_none() { + return INVALID_VECTOR_NUM as u32; + } + let max_vector = msix.as_ref().unwrap().lock().unwrap().table.len(); + if vector_nr >= max_vector as u32 { + INVALID_VECTOR_NUM as u32 + } else { + vector_nr + } + } + /// Read data from the common config of virtio device. /// Return the config value in u32. /// @@ -298,10 +312,11 @@ impl VirtioPciCommonConfig { /// Returns Error if the offset is out of bound. fn write_common_config( &mut self, - device: &Arc>, + virtio_pci_dev: &VirtioPciDevice, offset: u64, value: u32, ) -> PciResult<()> { + let device = virtio_pci_dev.device.clone(); match offset { COMMON_DFSELECT_REG => { self.features_select = value; @@ -334,7 +349,8 @@ impl VirtioPciCommonConfig { } } COMMON_MSIX_REG => { - self.msix_config.store(value as u16, Ordering::SeqCst); + let val = self.revise_queue_vector(value, virtio_pci_dev); + self.msix_config.store(val as u16, Ordering::SeqCst); self.interrupt_status.store(0_u32, Ordering::SeqCst); } COMMON_STATUS_REG => { @@ -373,9 +389,11 @@ impl VirtioPciCommonConfig { self.get_mut_queue_config() .map(|config| config.ready = true)?; } - COMMON_Q_MSIX_REG => self - .get_mut_queue_config() - .map(|config| config.vector = value as u16)?, + COMMON_Q_MSIX_REG => { + let val = self.revise_queue_vector(value, virtio_pci_dev); + self.get_mut_queue_config() + .map(|config| config.vector = val as u16)?; + } COMMON_Q_DESCLO_REG => self.get_mut_queue_config().map(|config| { config.desc_table = GuestAddress(config.desc_table.0 | u64::from(value)); })?, @@ -774,7 +792,7 @@ impl VirtioPciDevice { .common_config .lock() .unwrap() - .write_common_config(&cloned_pci_device.device.clone(), offset, value); + .write_common_config(&cloned_pci_device, offset, value); if let Err(e) = err { error!( "Failed to write common config of virtio-pci device, error is {:?}", @@ -1745,8 +1763,25 @@ mod tests { fn test_common_config_dev_feature() { let dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); let virtio_dev = dev.clone() as Arc>; - let queue_size = virtio_dev.lock().unwrap().queue_size(); - let queue_num = virtio_dev.lock().unwrap().queue_num(); + let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let parent_bus = Arc::new(Mutex::new(PciBus::new( + String::from("test bus"), + #[cfg(target_arch = "x86_64")] + Region::init_container_region(1 << 16), + sys_mem.root().clone(), + ))); + let cloned_virtio_dev = virtio_dev.clone(); + let virtio_pci = VirtioPciDevice::new( + String::from("test device"), + 0, + sys_mem, + cloned_virtio_dev, + Arc::downgrade(&parent_bus), + false, + ); + + let queue_size = dev.lock().unwrap().queue_size(); + let queue_num = dev.lock().unwrap().queue_num(); let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); @@ -1758,11 +1793,12 @@ mod tests { // Write virtio device features cmn_cfg.acked_features_select = 1_u32; - com_cfg_write_test!(cmn_cfg, virtio_dev, COMMON_GF_REG, 0xFF); + com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, 0xFF); // The feature is not supported by this virtio device, and is masked assert_eq!(dev.lock().unwrap().driver_features, 0_u64); + cmn_cfg.acked_features_select = 0_u32; - com_cfg_write_test!(cmn_cfg, virtio_dev, COMMON_GF_REG, 0xCF); + com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, 0xCF); // The feature is partially supported by this virtio device, and is partially masked assert_eq!(dev.lock().unwrap().driver_features, 0xC0_u64); @@ -1771,7 +1807,7 @@ mod tests { dev.lock().unwrap().driver_features = 0_u64; dev.lock().unwrap().device_features = 0xFFFF_FFFF_0000_0000_u64; let driver_features = 1_u32 << (VIRTIO_F_RING_PACKED - 32); - com_cfg_write_test!(cmn_cfg, virtio_dev, COMMON_GF_REG, driver_features); + com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, driver_features); assert_eq!(cmn_cfg.queue_type, QUEUE_TYPE_PACKED_VRING); assert_eq!( dev.lock().unwrap().driver_features, @@ -1814,6 +1850,33 @@ mod tests { let queue_size = virtio_dev.lock().unwrap().queue_size(); let queue_num = virtio_dev.lock().unwrap().queue_num(); let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); + let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let parent_bus = Arc::new(Mutex::new(PciBus::new( + String::from("test bus"), + #[cfg(target_arch = "x86_64")] + Region::init_container_region(1 << 16), + sys_mem.root().clone(), + ))); + let cloned_virtio_dev = virtio_dev.clone(); + let mut virtio_pci = VirtioPciDevice::new( + String::from("test device"), + 0, + sys_mem, + cloned_virtio_dev, + Arc::downgrade(&parent_bus), + false, + ); + + assert!(init_msix( + VIRTIO_PCI_MSIX_BAR_IDX as usize, + (queue_num + 1) as u32, + &mut virtio_pci.config, + virtio_pci.dev_id.clone(), + &virtio_pci.name, + None, + None, + ) + .is_ok()); // Error occurs when queue selector exceeds queue num cmn_cfg.queue_select = VIRTIO_DEVICE_QUEUE_NUM as u16; @@ -1821,20 +1884,20 @@ mod tests { .read_common_config(&virtio_dev, COMMON_Q_SIZE_REG) .is_err()); assert!(cmn_cfg - .write_common_config(&virtio_dev, COMMON_Q_SIZE_REG, 128) + .write_common_config(&virtio_pci, COMMON_Q_SIZE_REG, 128) .is_err()); // Test Queue ready register cmn_cfg.device_status = CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER; cmn_cfg.queue_select = 0; - com_cfg_write_test!(cmn_cfg, virtio_dev, COMMON_Q_ENABLE_REG, 0x1_u32); + com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_Q_ENABLE_REG, 0x1_u32); assert!(cmn_cfg.queues_config.get(0).unwrap().ready); // Failed to set Queue relevant register if device is no ready cmn_cfg.device_status = CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER_OK; cmn_cfg.queue_select = 1; assert!(cmn_cfg - .write_common_config(&virtio_dev, COMMON_Q_MSIX_REG, 0x4_u32) + .write_common_config(&virtio_pci, COMMON_Q_MSIX_REG, 0x4_u32) .is_err()); } -- Gitee From 0d85c8418a3ca9de6978937c4ed2cb9a6b6bcf81 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 21:57:27 -0500 Subject: [PATCH 0414/2187] virtiofs: Fix some print formatting issues Add a print newline for the error log, issues are: https://gitee.com/openeuler/stratovirt/issues/I5ZU6S https://gitee.com/openeuler/stratovirt/issues/I5ZUJ6 Signed-off-by: Li HuaChao --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index eac476e0f..1eb7c0636 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,7 +77,7 @@ fn main() { ::std::process::exit(match run() { Ok(ret) => ExitCode::code(ret), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format!("{:?}", e)) + write!(&mut ::std::io::stderr(), "{}", format!("{:?}\r\n", e)) .expect("Error writing to stderr"); 1 -- Gitee From 3fe907894d6e88f8380efe2ce8d7b6c97aa121e5 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 22:21:15 -0500 Subject: [PATCH 0415/2187] virtiofs: Change the length of the socket path to 108 The length of the socket path on linux is limited to 108. issue: https://gitee.com/openeuler/stratovirt/issues/I5ZUMB Signed-off-by: Li HuaChao --- machine_manager/src/config/fs.rs | 6 +++--- machine_manager/src/config/mod.rs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index d8f6977a5..eae8da3ca 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -12,7 +12,7 @@ use super::error::ConfigError; use crate::config::{ - pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH, + pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_SOCK_PATH_LENGTH, MAX_STRING_LENGTH, MAX_TAG_LENGTH, }; use anyhow::{anyhow, bail, Result}; @@ -55,10 +55,10 @@ impl ConfigCheck for FsConfig { ))); } - if self.sock.len() > MAX_PATH_LENGTH { + if self.sock.len() > MAX_SOCK_PATH_LENGTH { return Err(anyhow!(ConfigError::StringLengthTooLong( "fs sock path".to_string(), - MAX_PATH_LENGTH, + MAX_SOCK_PATH_LENGTH, ))); } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 5a40da99f..c47a360b4 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -68,6 +68,8 @@ use util::trace::enable_trace_events; pub const MAX_STRING_LENGTH: usize = 255; pub const MAX_PATH_LENGTH: usize = 4096; +// Maximum length of the socket path is restricted by linux. +pub const MAX_SOCK_PATH_LENGTH: usize = 108; // FIXME: `queue_config` len in `VirtioPciState` struct needs to be modified together. pub const MAX_VIRTIO_QUEUE: usize = 32; pub const FAST_UNPLUG_ON: &str = "1"; -- Gitee From f12f7724b51b16e00e3f780f91b8224a7c6fe984 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 22:23:16 -0500 Subject: [PATCH 0416/2187] virtiofs: Change the default queue size to 128 1.Excessively long queue lengths waste memory 2.The guest needs to apply for contiguous physical memory (GPA) based on queue length. In legacy mode, initialization will fail if contiguous physical memory is not allocated. issue: https://gitee.com/openeuler/stratovirt/issues/I60ATP Signed-off-by: Li HuaChao --- vhost_user_fs/src/virtio_fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index b55b0514c..29fb791ee 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -15,7 +15,7 @@ const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; /// The num of request queue. const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; /// The next queue size. -const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; +const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 128; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -- Gitee From 7463e0ee4f748d0a817c140f8cd7c6ce5ee0cea4 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 10 Nov 2022 20:32:28 -0500 Subject: [PATCH 0417/2187] Balloon: Send interrupts for every element The interrupt is sent only after the element has been processed, and is not required for empty queues. issue: https://gitee.com/openeuler/stratovirt/issues/I5ZFAR Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 2d53fb7e5..144999d33 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -558,16 +558,15 @@ impl BalloonIoHandler { .vring .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) .with_context(|| "Failed to add balloon response into used queue")?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + })?; } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Vring - )) - }, - )?; Ok(()) } @@ -594,16 +593,15 @@ impl BalloonIoHandler { .vring .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) .with_context(|| "Failed to add balloon response into used queue")?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + })?; } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Vring - )) - }, - )?; Ok(()) } -- Gitee From 1fb44eb8cf0ba245e5f599e922450b01f951c79a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 10 Nov 2022 21:24:43 -0500 Subject: [PATCH 0418/2187] Balloon: report virtio error in some error case issue: https://gitee.com/openeuler/stratovirt/issues/I5ZFAR Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 144999d33..6410ed486 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -19,6 +19,7 @@ use std::{ time::Duration, }; +use crate::report_virtio_error; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; @@ -514,7 +515,7 @@ struct BalloonIoHandler { /// Reporting EventFd. report_evt: Option, /// EventFd for device deactivate - deactivate_evt: RawFd, + deactivate_evt: EventFd, /// The interrupt call back function. interrupt_cb: Arc, /// Balloon Memory information. @@ -624,7 +625,7 @@ impl BalloonIoHandler { let notifiers = vec![ EventNotifier::new( NotifierOperation::Delete, - self.deactivate_evt, + self.deactivate_evt.as_raw_fd(), None, EventSet::IN, Vec::new(), @@ -682,12 +683,14 @@ impl EventNotifierHelper for BalloonIoHandler { let cloned_balloon_io = balloon_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(e) = cloned_balloon_io - .lock() - .unwrap() - .process_balloon_queue(BALLOON_INFLATE_EVENT) - { + let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if let Err(e) = locked_balloon_io.process_balloon_queue(BALLOON_INFLATE_EVENT) { error!("Failed to inflate balloon: {:?}", e); + report_virtio_error( + locked_balloon_io.interrupt_cb.clone(), + locked_balloon_io.driver_features, + Some(&locked_balloon_io.deactivate_evt), + ); }; None }); @@ -700,12 +703,14 @@ impl EventNotifierHelper for BalloonIoHandler { let cloned_balloon_io = balloon_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(e) = cloned_balloon_io - .lock() - .unwrap() - .process_balloon_queue(BALLOON_DEFLATE_EVENT) - { + let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if let Err(e) = locked_balloon_io.process_balloon_queue(BALLOON_DEFLATE_EVENT) { error!("Failed to deflate balloon: {:?}", e); + report_virtio_error( + locked_balloon_io.interrupt_cb.clone(), + locked_balloon_io.driver_features, + Some(&locked_balloon_io.deactivate_evt), + ); }; None }); @@ -719,8 +724,14 @@ impl EventNotifierHelper for BalloonIoHandler { let cloned_balloon_io = balloon_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(e) = cloned_balloon_io.lock().unwrap().reporting_evt_handler() { + let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if let Err(e) = locked_balloon_io.reporting_evt_handler() { error!("Failed to report free pages: {:?}", e); + report_virtio_error( + locked_balloon_io.interrupt_cb.clone(), + locked_balloon_io.driver_features, + Some(&locked_balloon_io.deactivate_evt), + ); } None }); @@ -734,7 +745,7 @@ impl EventNotifierHelper for BalloonIoHandler { Some(cloned_balloon_io.lock().unwrap().deactivate_evt_handler()) }); notifiers.push(build_event_notifier( - locked_balloon_io.deactivate_evt, + locked_balloon_io.deactivate_evt.as_raw_fd(), handler, )); @@ -1039,7 +1050,7 @@ impl VirtioDevice for Balloon { def_evt, report_queue, report_evt, - deactivate_evt: self.deactivate_evt.as_raw_fd(), + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), interrupt_cb, mem_info: self.mem_info.clone(), event_timer: self.event_timer.clone(), @@ -1324,7 +1335,7 @@ mod tests { def_evt: event_def, report_queue: None, report_evt: None, - deactivate_evt: event_deactivate.as_raw_fd(), + deactivate_evt: event_deactivate.try_clone().unwrap(), interrupt_cb: cb.clone(), mem_info: bln.mem_info.clone(), event_timer: bln.event_timer.clone(), -- Gitee From ad65d259baa66d9195b98f2d7a34c553edc4ff66 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 16 Nov 2022 23:30:52 -0500 Subject: [PATCH 0419/2187] balloon: Handling pop elem errors When a pop elem error occurs, an error is reported. issue: https://gitee.com/openeuler/stratovirt/issues/I61MVD Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 6410ed486..56aa99954 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -543,10 +543,12 @@ impl BalloonIoHandler { &self.def_queue }; let mut locked_queue = queue.lock().unwrap(); - while let Ok(elem) = locked_queue - .vring - .pop_avail(&self.mem_space, self.driver_features) - { + loop { + let elem = locked_queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + .with_context(|| "Failed to pop avail ring for process baloon queue")?; + if elem.desc_num == 0 { break; } @@ -565,7 +567,7 @@ impl BalloonIoHandler { "balloon", VirtioInterruptType::Vring )) - })?; + })? } Ok(()) @@ -578,10 +580,12 @@ impl BalloonIoHandler { } let unwraped_queue = queue.as_ref().unwrap(); let mut locked_queue = unwraped_queue.lock().unwrap(); - while let Ok(elem) = locked_queue - .vring - .pop_avail(&self.mem_space, self.driver_features) - { + loop { + let elem = locked_queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + .with_context(|| "Failed to pop avail ring for reporting free pages")?; + if elem.desc_num == 0 { break; } -- Gitee From f1c29beba1e1d462b72eb1658486ea32238bdb32 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 12 Nov 2022 09:37:57 +0800 Subject: [PATCH 0420/2187] xhci: no need to return an error when trb is not ready When the TRB is fetched from the ring, if the TRB is not ready, None is returned instead of an error. And fetch the td in the transfer ring without prefetch the length. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 77 +++++++++++++++++---------------- usb/src/xhci/xhci_ring.rs | 31 ++++++++----- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index eecb92613..2fb9d2921 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -86,7 +86,7 @@ type DmaAddr = u64; pub struct XhciTransfer { packet: UsbPacket, status: TRBCCode, - trbs: Vec, + td: Vec, complete: bool, slotid: u32, epid: u32, @@ -96,11 +96,11 @@ pub struct XhciTransfer { } impl XhciTransfer { - fn new(len: usize) -> Self { + fn new() -> Self { XhciTransfer { packet: UsbPacket::default(), status: TRBCCode::Invalid, - trbs: vec![XhciTRB::new(); len], + td: Vec::new(), complete: false, slotid: 0, epid: 0, @@ -393,6 +393,11 @@ impl XhciDevice { self.oper.usb_status & USB_STS_HCH != USB_STS_HCH } + fn internal_error(&mut self) { + error!("Xhci host controller error!"); + self.oper.usb_status |= USB_STS_HCE; + } + pub fn reset(&mut self) { info!("xhci reset"); self.oper.reset(); @@ -551,7 +556,7 @@ impl XhciDevice { let mut event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::Success); for _ in 0..COMMAND_LIMIT { match self.cmd_ring.fetch_trb() { - Ok(trb) => { + Ok(Some(trb)) => { let trb_type = trb.get_type(); event.ptr = trb.addr; slot_id = self.get_slot_id(&mut event, &trb); @@ -615,6 +620,10 @@ impl XhciDevice { event.slot_id = slot_id as u8; self.send_event(&event, 0)?; } + Ok(None) => { + debug!("No TRB in the cmd ring."); + break; + } Err(e) => { error!("Failed to fetch ring: {:?}", e); event.ccode = TRBCCode::TrbError; @@ -1009,34 +1018,26 @@ impl XhciDevice { const KICK_LIMIT: u32 = 32; let mut count = 0; loop { - let len = match epctx.ring.get_transfer_len() { - Ok(len) => len, - Err(e) => { - error!("Failed to get transfer len: {:?}", e); - break; - } - }; - if len == 0 { - break; - } - let mut xfer: XhciTransfer = XhciTransfer::new(len); + let mut xfer: XhciTransfer = XhciTransfer::new(); xfer.slotid = slot_id; xfer.epid = ep_id; - for i in 0..len { - match epctx.ring.fetch_trb() { - Ok(trb) => { - debug!( - "fetch transfer trb {:?} ring dequeue {:x}", - trb, epctx.ring.dequeue, - ); - xfer.trbs[i] = trb; - } - Err(e) => { - self.oper.usb_status |= USB_STS_HCE; - // update the endpoint in slot - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; - bail!("fetch ring failed {}", e); - } + match epctx.ring.fetch_td() { + Ok(Some(td)) => { + debug!( + "fetch transfer trb {:?} ring dequeue {:?}", + td, epctx.ring.dequeue, + ); + xfer.td = td; + } + Ok(None) => { + debug!("No TD in the transfer ring."); + break; + } + Err(e) => { + self.internal_error(); + // update the endpoint in slot + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + bail!("fetch ring failed {}", e); } } if let Err(e) = self.endpoint_do_transfer(&mut xfer, &mut epctx) { @@ -1141,12 +1142,12 @@ impl XhciDevice { xfer: &mut XhciTransfer, epctx: &mut XhciEpContext, ) -> Result<()> { - let trb_setup = xfer.trbs[0]; - let mut trb_status = xfer.trbs[xfer.trbs.len() - 1]; + let trb_setup = xfer.td[0]; + let mut trb_status = xfer.td[xfer.td.len() - 1]; let setup_type = trb_setup.get_type(); let status_type = trb_status.get_type(); - if status_type == TRBType::TrEvdata && xfer.trbs.len() > 2 { - trb_status = xfer.trbs[xfer.trbs.len() - 2]; + if status_type == TRBType::TrEvdata && xfer.td.len() > 2 { + trb_status = xfer.td[xfer.td.len() - 2]; } if setup_type != TRBType::TrSetup { bail!("The first TRB is not Setup"); @@ -1204,7 +1205,7 @@ impl XhciDevice { }; // Map dma address to iovec. let mut vec = Vec::new(); - for trb in &xfer.trbs { + for trb in &xfer.td { let trb_type = trb.get_type(); if trb.control & TRB_TR_IOC == TRB_TR_IOC { xfer.int_req = true; @@ -1300,8 +1301,8 @@ impl XhciDevice { let mut left = xfer.packet.actual_length; let mut reported = false; let mut short_pkt = false; - for i in 0..xfer.trbs.len() { - let trb = &xfer.trbs[i]; + for i in 0..xfer.td.len() { + let trb = &xfer.td[i]; let trb_type = trb.get_type(); let mut chunk = trb.status & 0x1ffff; match trb_type { @@ -1425,7 +1426,7 @@ impl XhciDevice { xfer.running_retry = false; killed = 1; } - xfer.trbs.clear(); + xfer.td.clear(); Ok(killed) } diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 8da70e0ff..30e45c57b 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -229,14 +229,15 @@ impl XhciRing { } /// Fetch TRB from the ring. - pub fn fetch_trb(&mut self) -> Result { + pub fn fetch_trb(&mut self) -> Result> { let mut link_cnt = 0; loop { let mut trb = self.read_trb(self.dequeue)?; trb.addr = self.dequeue; trb.ccs = self.ccs; if trb.get_cycle_bit() != self.ccs { - bail!("TRB cycle bit not matched"); + debug!("TRB cycle bit not matched"); + return Ok(None); } let trb_type = trb.get_type(); debug!("Fetch TRB: type {:?} trb {:?}", trb_type, trb); @@ -251,7 +252,7 @@ impl XhciRing { } } else { self.dequeue += TRB_SIZE as u64; - return Ok(trb); + return Ok(Some(trb)); } } } @@ -269,19 +270,24 @@ impl XhciRing { Ok(trb) } - /// Get the number of TRBs in the TD if success. - pub fn get_transfer_len(&self) -> Result { - let mut len = 0; + /// Get the transfer descriptor which includes one or more TRBs. + /// Return None if the td is not ready. + /// Return Vec if the td is ok. + /// Return Error if read trb failed. + pub fn fetch_td(&mut self) -> Result>> { let mut dequeue = self.dequeue; let mut ccs = self.ccs; let mut ctrl_td = false; let mut link_cnt = 0; + let mut td = Vec::new(); for _ in 0..RING_LEN_LIMIT { - let trb = self.read_trb(dequeue)?; + let mut trb = self.read_trb(dequeue)?; + trb.addr = dequeue; + trb.ccs = ccs; if trb.get_cycle_bit() != ccs { - // TRB is not ready + // TRB is not ready. debug!("TRB cycle bit not matched"); - return Ok(0); + return Ok(None); } let trb_type = trb.get_type(); if trb_type == TRBType::TrLink { @@ -294,7 +300,7 @@ impl XhciRing { ccs = !ccs; } } else { - len += 1; + td.push(trb); dequeue += TRB_SIZE as u64; if trb_type == TRBType::TrSetup { ctrl_td = true; @@ -302,7 +308,10 @@ impl XhciRing { ctrl_td = false; } if !ctrl_td && (trb.control & TRB_TR_CH != TRB_TR_CH) { - return Ok(len); + // Update the dequeue pointer and ccs flag. + self.dequeue = dequeue; + self.ccs = ccs; + return Ok(Some(td)); } } } -- Gitee From 863c9a0ebe249ddf32f5dffb45aeb2f94c8e7fbe Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 12 Nov 2022 09:55:04 +0800 Subject: [PATCH 0421/2187] xhci: delete the duplicate verification The check of slot id and ep id already done in the kick_endpoint, delete it in doorbell_write. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_regs.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 60d2b5b2c..64711ac54 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -612,19 +612,18 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { if slot_id == 0 { if value == 0 { if let Err(e) = xhci.handle_command() { - error!("Failed to process commands: {:?}", e); + error!("Failed to handle command: {:?}", e); + return false; } } else { - error!("Invalid doorbell write: value {:x}", value) + error!("Invalid doorbell write: value {:x}", value); + return false; } } else { let ep_id = value & DB_TARGET_MASK; - if slot_id > xhci.slots.len() as u32 { - error!("Invalid slot_id {}", slot_id); - } else if ep_id == 0 || ep_id > 31 { - error!("Invalid epid {}", ep_id,); - } else if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { + if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { error!("Failed to kick endpoint: {:?}", e); + return false; } } true -- Gitee From 2d131e6bf430e98d6f63d95a074965fcee6b05e1 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 12 Nov 2022 22:05:31 +0800 Subject: [PATCH 0422/2187] usb: check the device existed before using it Check the device existed in the usb port, and remove the useless code of UsbPort. Signed-off-by: zhouli57 --- usb/src/keyboard.rs | 1 - usb/src/tablet.rs | 1 - usb/src/usb.rs | 22 ---------------------- usb/src/xhci/xhci_controller.rs | 11 ++++++----- 4 files changed, 6 insertions(+), 29 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 4b435b2c7..9e76a30fc 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -133,7 +133,6 @@ impl UsbKeyboard { pub fn realize(self) -> Result>> { let mut locked_usb = self.device.lock().unwrap(); locked_usb.product_desc = String::from("StratoVirt USB keyboard"); - locked_usb.auto_attach = true; locked_usb.strings = Vec::new(); drop(locked_usb); let kbd = Arc::new(Mutex::new(self)); diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index b52d9c676..2371e669b 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -138,7 +138,6 @@ impl UsbTablet { pub fn realize(self) -> Result>> { let mut locked_usb = self.device.lock().unwrap(); locked_usb.product_desc = String::from("StratoVirt USB Tablet"); - locked_usb.auto_attach = true; locked_usb.strings = Vec::new(); drop(locked_usb); let tablet = Arc::new(Mutex::new(self)); diff --git a/usb/src/usb.rs b/usb/src/usb.rs index ac65f32cf..ba9af6882 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -153,16 +153,6 @@ impl UsbPort { index, } } - - /// If the USB port attached USB device. - pub fn is_attached(&self) -> bool { - if let Some(dev) = &self.dev { - let locked_dev = dev.lock().unwrap(); - locked_dev.attached() - } else { - false - } - } } /// USB descriptor strings. @@ -242,8 +232,6 @@ pub struct UsbDevice { pub speed_mask: u32, pub addr: u8, pub product_desc: String, - pub auto_attach: bool, - pub attached: bool, pub state: UsbDeviceState, pub setup_buf: Vec, pub data_buf: Vec, @@ -269,7 +257,6 @@ impl UsbDevice { pub fn new() -> Self { let mut dev = UsbDevice { port: None, - attached: false, speed: 0, speed_mask: 0, addr: 0, @@ -283,7 +270,6 @@ impl UsbDevice { ep_in: Vec::new(), ep_out: Vec::new(), product_desc: String::new(), - auto_attach: false, strings: Vec::new(), usb_desc: None, device_desc: None, @@ -494,7 +480,6 @@ pub trait UsbDeviceOps: Send + Sync { fn handle_attach(&mut self) -> Result<()> { let usb_dev = self.get_mut_usb_device(); let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.attached = true; locked_dev.state = UsbDeviceState::Attached; drop(locked_dev); let usb_dev = self.get_mut_usb_device(); @@ -564,13 +549,6 @@ pub trait UsbDeviceOps: Send + Sync { locked_dev.speed } - /// If USB device is attached. - fn attached(&self) -> bool { - let usb_dev = self.get_usb_device(); - let locked_dev = usb_dev.lock().unwrap(); - locked_dev.attached - } - fn process_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { packet.status = UsbPacketStatus::Success; let ep = if let Some(ep) = &packet.ep { diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 2fb9d2921..22ea28b45 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -678,12 +678,13 @@ impl XhciDevice { )?; if let Some(usb_port) = self.lookup_usb_port(&slot_ctx) { let lock_port = usb_port.lock().unwrap(); - let dev = lock_port.dev.as_ref().unwrap(); - let mut locked_dev = dev.lock().unwrap(); - if !locked_dev.attached() { - error!("Failed to connect device"); + let dev = if let Some(dev) = lock_port.dev.as_ref() { + dev + } else { + error!("No device found in usb port."); return Ok(TRBCCode::UsbTransactionError); - } + }; + let mut locked_dev = dev.lock().unwrap(); self.slots[(slot_id - 1) as usize].usb_port = Some(Arc::downgrade(&usb_port)); self.slots[(slot_id - 1) as usize].ctx = octx; self.slots[(slot_id - 1) as usize].intr = -- Gitee From 83ed99e47ec94d68a40ccdd896364bfc722b9b98 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 14 Nov 2022 21:41:01 +0800 Subject: [PATCH 0423/2187] xhci: move dma read/write to enable_endpoint Move the dma read/write to enable_endpoint to reduce duplicate codes. And rename some filed to improve readability. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 81 ++++++++++++++++----------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 22ea28b45..ed04bb834 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -78,6 +78,10 @@ const MAX_ENDPOINTS: u32 = 31; /// XHCI config const XHCI_PORT2_NUM: u32 = 2; const XHCI_PORT3_NUM: u32 = 2; +/// Endpoint Context. +const EP_INPUT_CTX_ENTRY_SIZE: u64 = 0x20; +const EP_INPUT_CTX_OFFSET: u64 = 0x40; +const EP_CTX_OFFSET: u64 = 0x20; type DmaAddr = u64; @@ -118,7 +122,7 @@ pub struct XhciEpContext { enabled: bool, ring: XhciRing, ep_type: EpType, - pctx: DmaAddr, + output_ctx: DmaAddr, max_psize: u32, state: u32, /// Line Stream Array @@ -129,13 +133,13 @@ pub struct XhciEpContext { } impl XhciEpContext { - pub fn new(mem: &Arc, epid: u32) -> Self { + pub fn new(mem: &Arc) -> Self { Self { - epid, + epid: 0, enabled: false, ring: XhciRing::new(mem), ep_type: EpType::Invalid, - pctx: 0, + output_ctx: 0, max_psize: 0, state: 0, lsa: false, @@ -145,10 +149,11 @@ impl XhciEpContext { } } - pub fn init(&mut self, pctx: DmaAddr, ctx: &XhciEpCtx) { + /// Init the endpoint context used the context read from memory. + fn init_ctx(&mut self, output_ctx: DmaAddr, ctx: &XhciEpCtx) { let dequeue: DmaAddr = addr64_from_u32(ctx.deq_lo & !0xf, ctx.deq_hi); self.ep_type = ((ctx.ep_info2 >> EP_TYPE_SHIFT) & EP_TYPE_MASK).into(); - self.pctx = pctx; + self.output_ctx = output_ctx; self.max_psize = ctx.ep_info2 >> EP_CTX_MAX_PACKET_SIZE_SHIFT; self.max_psize *= 1 + ((ctx.ep_info2 >> 8) & 0xff); self.lsa = (ctx.ep_info >> EP_CTX_LSA_SHIFT) & 1 == 1; @@ -157,14 +162,15 @@ impl XhciEpContext { self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); } - pub fn set_state(&mut self, mem: &Arc, state: u32) -> Result<()> { + /// Update the endpoint state and write the state to memory. + fn set_state(&mut self, mem: &Arc, state: u32) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32(mem, GuestAddress(self.pctx), ep_ctx.as_mut_dwords())?; + dma_read_u32(mem, GuestAddress(self.output_ctx), ep_ctx.as_mut_dwords())?; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= state; ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; - dma_write_u32(mem, GuestAddress(self.pctx), ep_ctx.as_dwords())?; + dma_write_u32(mem, GuestAddress(self.output_ctx), ep_ctx.as_dwords())?; self.state = state; Ok(()) } @@ -218,7 +224,7 @@ impl XhciSlot { intr: 0, ctx: 0, usb_port: None, - endpoints: vec![XhciEpContext::new(mem, 0); MAX_ENDPOINTS as usize], + endpoints: vec![XhciEpContext::new(mem); MAX_ENDPOINTS as usize], } } } @@ -697,19 +703,9 @@ impl XhciDevice { slot_ctx.dev_state = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot_id; self.address_device(dev, slot_id); } - let mut ep0_ctx = XhciEpCtx::default(); - dma_read_u32( - &self.mem_space, - GuestAddress(ictx + 64), - ep0_ctx.as_mut_dwords(), - )?; - self.enable_endpoint(slot_id, 1, octx + 32, &mut ep0_ctx)?; + // Enable control endpoint. + self.enable_endpoint(slot_id, 1, ictx, octx)?; dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; - dma_write_u32( - &self.mem_space, - GuestAddress(octx + 32), - ep0_ctx.as_dwords(), - )?; self.slots[(slot_id - 1) as usize].addressed = true; } else { error!("Failed to found usb port"); @@ -801,24 +797,11 @@ impl XhciDevice { self.disable_endpoint(slot_id, i)?; } if ictl_ctx.add_flags & (1 << i) == 1 << i { - let offset = 32 + 32 * i as u64; - let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32( - &self.mem_space, - GuestAddress(ictx + offset), - ep_ctx.as_mut_dwords(), - )?; self.disable_endpoint(slot_id, i)?; - let offset = 32 * i as u64; - let ret = self.enable_endpoint(slot_id, i, octx + offset, &mut ep_ctx)?; + let ret = self.enable_endpoint(slot_id, i, ictx, octx)?; if ret != TRBCCode::Success { return Ok(ret); } - dma_write_u32( - &self.mem_space, - GuestAddress(octx + offset), - ep_ctx.as_dwords(), - )?; } } Ok(TRBCCode::Success) @@ -901,17 +884,29 @@ impl XhciDevice { &mut self, slot_id: u32, ep_id: u32, - pctx: DmaAddr, - ctx: &mut XhciEpCtx, + input_ctx: DmaAddr, + output_ctx: DmaAddr, ) -> Result { + let entry_offset = (ep_id - 1) as u64 * EP_INPUT_CTX_ENTRY_SIZE; + let mut ep_ctx = XhciEpCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(input_ctx + EP_INPUT_CTX_OFFSET + entry_offset), + ep_ctx.as_mut_dwords(), + )?; self.disable_endpoint(slot_id, ep_id)?; - let mut epctx = XhciEpContext::new(&self.mem_space, ep_id); + let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + epctx.epid = ep_id; epctx.enabled = true; - epctx.init(pctx, ctx); + epctx.init_ctx(output_ctx + EP_CTX_OFFSET + entry_offset, &ep_ctx); epctx.state = EP_RUNNING; - ctx.ep_info &= !EP_STATE_MASK; - ctx.ep_info |= EP_RUNNING; - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + ep_ctx.ep_info &= !EP_STATE_MASK; + ep_ctx.ep_info |= EP_RUNNING; + dma_write_u32( + &self.mem_space, + GuestAddress(output_ctx + EP_CTX_OFFSET + entry_offset), + ep_ctx.as_dwords(), + )?; Ok(TRBCCode::Success) } -- Gitee From 638fd388de7a0b6792a81714f709c7dc6992ed91 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 14 Nov 2022 21:58:00 +0800 Subject: [PATCH 0424/2187] xhci: check device existed when address device 1. Check device existed before address device. 2. Check slot state when address device, if bsr=1 the slot state should be enabled and if bsr=0 the slot state should be enabled or default. 3. Remove the drop_flags verification for xHC should ignore it. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 125 +++++++++++++++++++------------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index ed04bb834..dc7073248 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -78,6 +78,8 @@ const MAX_ENDPOINTS: u32 = 31; /// XHCI config const XHCI_PORT2_NUM: u32 = 2; const XHCI_PORT3_NUM: u32 = 2; +/// Slot Context. +const SLOT_INPUT_CTX_OFFSET: u64 = 0x20; /// Endpoint Context. const EP_INPUT_CTX_ENTRY_SIZE: u64 = 0x20; const EP_INPUT_CTX_OFFSET: u64 = 0x40; @@ -210,7 +212,7 @@ impl From for EpType { pub struct XhciSlot { pub enabled: bool, pub addressed: bool, - pub intr: u16, + pub intr_target: u16, pub ctx: u64, pub usb_port: Option>>, pub endpoints: Vec, @@ -221,7 +223,7 @@ impl XhciSlot { XhciSlot { enabled: false, addressed: false, - intr: 0, + intr_target: 0, ctx: 0, usb_port: None, endpoints: vec![XhciEpContext::new(mem); MAX_ENDPOINTS as usize], @@ -590,7 +592,7 @@ impl XhciDevice { } TRBType::CrAddressDevice => { if slot_id != 0 { - event.ccode = self.address_slot(slot_id, &trb)?; + event.ccode = self.address_device(slot_id, &trb)?; } } TRBType::CrConfigureEndpoint => { @@ -652,69 +654,88 @@ impl XhciDevice { self.slots[(slot_id - 1) as usize].enabled = false; self.slots[(slot_id - 1) as usize].addressed = false; self.slots[(slot_id - 1) as usize].usb_port = None; - self.slots[(slot_id - 1) as usize].intr = 0; + self.slots[(slot_id - 1) as usize].intr_target = 0; Ok(TRBCCode::Success) } - fn address_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { - let bsr = trb.control & TRB_CR_BSR == TRB_CR_BSR; - let dcbaap = self.oper.dcbaap; + fn address_device(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; - let mut octx = 0; - dma_read_u64( - &self.mem_space, - GuestAddress(dcbaap + (8 * slot_id) as u64), - &mut octx, - )?; - let mut ictl_ctx = XhciInputCtrlCtx::default(); - dma_read_u32( - &self.mem_space, - GuestAddress(ictx), - ictl_ctx.as_mut_dwords(), - )?; - if ictl_ctx.drop_flags != 0x0 || ictl_ctx.add_flags != 0x3 { - error!("Invalid input: {:?}", ictl_ctx); - return Ok(TRBCCode::TrbError); + let ccode = self.check_input_ctx(ictx)?; + if ccode != TRBCCode::Success { + return Ok(ccode); } let mut slot_ctx = XhciSlotCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(ictx + 32), + GuestAddress(ictx + SLOT_INPUT_CTX_OFFSET), slot_ctx.as_mut_dwords(), )?; - if let Some(usb_port) = self.lookup_usb_port(&slot_ctx) { - let lock_port = usb_port.lock().unwrap(); - let dev = if let Some(dev) = lock_port.dev.as_ref() { - dev - } else { - error!("No device found in usb port."); - return Ok(TRBCCode::UsbTransactionError); - }; - let mut locked_dev = dev.lock().unwrap(); - self.slots[(slot_id - 1) as usize].usb_port = Some(Arc::downgrade(&usb_port)); - self.slots[(slot_id - 1) as usize].ctx = octx; - self.slots[(slot_id - 1) as usize].intr = - ((slot_ctx.tt_info >> TRB_INTR_SHIFT) & TRB_INTR_MASK) as u16; - locked_dev.reset(); - drop(locked_dev); - if bsr { - slot_ctx.dev_state = SLOT_DEFAULT << SLOT_STATE_SHIFT; - } else { - slot_ctx.dev_state = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot_id; - self.address_device(dev, slot_id); - } - // Enable control endpoint. - self.enable_endpoint(slot_id, 1, ictx, octx)?; - dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; - self.slots[(slot_id - 1) as usize].addressed = true; + let bsr = trb.control & TRB_CR_BSR == TRB_CR_BSR; + let ccode = self.check_slot_state(&slot_ctx, bsr)?; + if ccode != TRBCCode::Success { + return Ok(ccode); + } + let usb_port = if let Some(usb_port) = self.lookup_usb_port(&slot_ctx) { + usb_port } else { error!("Failed to found usb port"); return Ok(TRBCCode::TrbError); + }; + let lock_port = usb_port.lock().unwrap(); + let dev = if let Some(dev) = lock_port.dev.as_ref() { + dev + } else { + error!("No device found in usb port."); + return Ok(TRBCCode::UsbTransactionError); + }; + let ctx_addr = self.get_device_context_addr(slot_id); + let mut octx = 0; + dma_read_u64(&self.mem_space, GuestAddress(ctx_addr), &mut octx)?; + self.slots[(slot_id - 1) as usize].usb_port = Some(Arc::downgrade(&usb_port)); + self.slots[(slot_id - 1) as usize].ctx = octx; + self.slots[(slot_id - 1) as usize].intr_target = + ((slot_ctx.tt_info >> TRB_INTR_SHIFT) & TRB_INTR_MASK) as u16; + dev.lock().unwrap().reset(); + if bsr { + slot_ctx.dev_state = SLOT_DEFAULT << SLOT_STATE_SHIFT; + } else { + slot_ctx.dev_state = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot_id; + self.set_device_address(dev, slot_id); + } + // Enable control endpoint. + self.enable_endpoint(slot_id, 1, ictx, octx)?; + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + self.slots[(slot_id - 1) as usize].addressed = true; + Ok(TRBCCode::Success) + } + + fn check_input_ctx(&self, ictx: u64) -> Result { + let mut ictl_ctx = XhciInputCtrlCtx::default(); + dma_read_u32( + &self.mem_space, + GuestAddress(ictx), + ictl_ctx.as_mut_dwords(), + )?; + if ictl_ctx.add_flags & 0x3 != 0x3 { + // The Slot Context(Add Context flag0 (A0)) and Default Endpoint Control + // (Add Context flag1 (A1)) shall be valid. Others shall be ignored. + error!("Invalid input context: {:?}", ictl_ctx); + return Ok(TRBCCode::ParameterError); } Ok(TRBCCode::Success) } - fn address_device(&mut self, dev: &Arc>, slot_id: u32) { + fn check_slot_state(&self, slot_ctx: &XhciSlotCtx, bsr: bool) -> Result { + let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; + if !(slot_state == SLOT_ENABLED || !bsr && slot_state == SLOT_DEFAULT) { + error!("Invalid slot state: {:?}", slot_ctx); + return Ok(TRBCCode::ContextStateError); + } + Ok(TRBCCode::Success) + } + + /// Send SET_ADDRESS request to usb device. + fn set_device_address(&mut self, dev: &Arc>, addr: u32) { let mut p = UsbPacket::default(); let mut locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_mut_usb_device(); @@ -725,13 +746,17 @@ impl XhciDevice { let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, request: USB_REQUEST_SET_ADDRESS, - value: slot_id as u16, + value: addr as u16, index: 0, length: 0, }; locked_dev.handle_control(&mut p, &device_req, &mut []); } + fn get_device_context_addr(&self, slot_id: u32) -> u64 { + self.oper.dcbaap + (8 * slot_id) as u64 + } + fn config_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; let octx = self.slots[(slot_id - 1) as usize].ctx; -- Gitee From db10f4751f63828e0821ec55387eb1e45bf66c32 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 09:15:25 +0800 Subject: [PATCH 0425/2187] xhci: allow Error state when set TR dequeue pointer From the section 4.6.10 Set TR Dequeue Pointer of the spec, the xHC shall reject the command if the endpoint is not in the Error or Stopped state. And remove the unused filed in XhciEpContext. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 112 ++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index dc7073248..ef880956d 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -39,11 +39,10 @@ const EP_DISABLED: u32 = 0; const EP_RUNNING: u32 = 1; const EP_HALTED: u32 = 2; const EP_STOPPED: u32 = 3; +const EP_ERROR: u32 = 4; /// Endpoint type const EP_TYPE_SHIFT: u32 = 3; const EP_TYPE_MASK: u32 = 0x7; -#[allow(unused)] -const EP_ERROR: u32 = 4; /// Slot state const SLOT_STATE_MASK: u32 = 0x1f; const SLOT_STATE_SHIFT: u32 = 27; @@ -64,8 +63,6 @@ const TRB_CR_DC: u32 = 1 << 9; const TRB_CR_SLOTID_SHIFT: u32 = 24; const TRB_CR_SLOTID_MASK: u32 = 0xff; const COMMAND_LIMIT: u32 = 256; -const EP_CTX_MAX_PACKET_SIZE_SHIFT: u32 = 16; -const EP_CTX_LSA_SHIFT: u32 = 15; const EP_CTX_INTERVAL_SHIFT: u32 = 16; const EP_CTX_INTERVAL_MASK: u32 = 0xff; const EVENT_TRB_CCODE_SHIFT: u32 = 24; @@ -84,6 +81,8 @@ const SLOT_INPUT_CTX_OFFSET: u64 = 0x20; const EP_INPUT_CTX_ENTRY_SIZE: u64 = 0x20; const EP_INPUT_CTX_OFFSET: u64 = 0x40; const EP_CTX_OFFSET: u64 = 0x20; +const EP_CTX_TR_DEQUEUE_POINTER_MASK: u64 = !0xf; +const EP_CTX_DCS: u64 = 1; type DmaAddr = u64; @@ -124,11 +123,8 @@ pub struct XhciEpContext { enabled: bool, ring: XhciRing, ep_type: EpType, - output_ctx: DmaAddr, - max_psize: u32, + output_ctx_addr: DmaAddr, state: u32, - /// Line Stream Array - lsa: bool, interval: u32, transfers: Vec, retry: Option, @@ -141,10 +137,8 @@ impl XhciEpContext { enabled: false, ring: XhciRing::new(mem), ep_type: EpType::Invalid, - output_ctx: 0, - max_psize: 0, + output_ctx_addr: 0, state: 0, - lsa: false, interval: 0, transfers: Vec::new(), retry: None, @@ -155,10 +149,7 @@ impl XhciEpContext { fn init_ctx(&mut self, output_ctx: DmaAddr, ctx: &XhciEpCtx) { let dequeue: DmaAddr = addr64_from_u32(ctx.deq_lo & !0xf, ctx.deq_hi); self.ep_type = ((ctx.ep_info2 >> EP_TYPE_SHIFT) & EP_TYPE_MASK).into(); - self.output_ctx = output_ctx; - self.max_psize = ctx.ep_info2 >> EP_CTX_MAX_PACKET_SIZE_SHIFT; - self.max_psize *= 1 + ((ctx.ep_info2 >> 8) & 0xff); - self.lsa = (ctx.ep_info >> EP_CTX_LSA_SHIFT) & 1 == 1; + self.output_ctx_addr = output_ctx; self.ring.init(dequeue); self.ring.ccs = (ctx.deq_lo & 1) == 1; self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); @@ -167,15 +158,35 @@ impl XhciEpContext { /// Update the endpoint state and write the state to memory. fn set_state(&mut self, mem: &Arc, state: u32) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32(mem, GuestAddress(self.output_ctx), ep_ctx.as_mut_dwords())?; + dma_read_u32( + mem, + GuestAddress(self.output_ctx_addr), + ep_ctx.as_mut_dwords(), + )?; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= state; ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; - dma_write_u32(mem, GuestAddress(self.output_ctx), ep_ctx.as_dwords())?; + dma_write_u32(mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_dwords())?; self.state = state; Ok(()) } + + /// Update the dequeue pointer in memory. + fn update_dequeue(&mut self, mem: &Arc, dequeue: u64) -> Result<()> { + let mut ep_ctx = XhciEpCtx::default(); + dma_read_u32( + mem, + GuestAddress(self.output_ctx_addr), + ep_ctx.as_mut_dwords(), + )?; + self.ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); + self.ring.ccs = (dequeue & EP_CTX_DCS) == EP_CTX_DCS; + ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; + ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; + dma_write_u32(mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_dwords())?; + Ok(()) + } } /// Endpoint type, including control, bulk, interrupt and isochronous. @@ -213,22 +224,42 @@ pub struct XhciSlot { pub enabled: bool, pub addressed: bool, pub intr_target: u16, - pub ctx: u64, + pub slot_ctx_addr: u64, pub usb_port: Option>>, pub endpoints: Vec, } impl XhciSlot { - pub fn new(mem: &Arc) -> Self { + fn new(mem: &Arc) -> Self { XhciSlot { enabled: false, addressed: false, intr_target: 0, - ctx: 0, + slot_ctx_addr: 0, usb_port: None, endpoints: vec![XhciEpContext::new(mem); MAX_ENDPOINTS as usize], } } + + /// Get the slot state from the memory. + fn get_slot_state(&self, mem: &Arc) -> Result { + let mut slot_ctx = XhciSlotCtx::default(); + dma_read_u32( + mem, + GuestAddress(self.slot_ctx_addr), + slot_ctx.as_mut_dwords(), + )?; + let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; + Ok(slot_state) + } + + fn slot_state_is_valid(&self, mem: &Arc) -> Result { + let slot_state = self.get_slot_state(mem)?; + let valid = slot_state == SLOT_DEFAULT + || slot_state == SLOT_ADDRESSED + || slot_state == SLOT_CONFIGURED; + Ok(valid) + } } /// Event usually send to drivers. @@ -614,8 +645,10 @@ impl XhciDevice { event.ccode = self.reset_endpoint(slot_id, ep_id)?; } TRBType::CrSetTrDequeue => { - let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; - event.ccode = self.set_endpoint_dequeue(slot_id, ep_id, &trb)?; + if slot_id != 0 { + let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; + event.ccode = self.set_tr_dequeue_pointer(slot_id, ep_id, &trb)?; + } } TRBType::CrResetDevice => { event.ccode = self.reset_slot(slot_id)?; @@ -692,7 +725,7 @@ impl XhciDevice { let mut octx = 0; dma_read_u64(&self.mem_space, GuestAddress(ctx_addr), &mut octx)?; self.slots[(slot_id - 1) as usize].usb_port = Some(Arc::downgrade(&usb_port)); - self.slots[(slot_id - 1) as usize].ctx = octx; + self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; self.slots[(slot_id - 1) as usize].intr_target = ((slot_ctx.tt_info >> TRB_INTR_SHIFT) & TRB_INTR_MASK) as u16; dev.lock().unwrap().reset(); @@ -759,7 +792,7 @@ impl XhciDevice { fn config_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; - let octx = self.slots[(slot_id - 1) as usize].ctx; + let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; let mut slot_ctx = XhciSlotCtx::default(); if trb.control & TRB_CR_DC == TRB_CR_DC { for i in 2..32 { @@ -834,7 +867,7 @@ impl XhciDevice { fn evaluate_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; - let octx = self.slots[(slot_id - 1) as usize].ctx; + let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; let mut ictl_ctx = XhciInputCtrlCtx::default(); dma_read_u32( &self.mem_space, @@ -890,7 +923,7 @@ impl XhciDevice { fn reset_slot(&mut self, slot_id: u32) -> Result { let mut slot_ctx = XhciSlotCtx::default(); - let octx = self.slots[(slot_id - 1) as usize].ctx; + let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; for i in 2..32 { self.disable_endpoint(slot_id, i)?; } @@ -997,24 +1030,33 @@ impl XhciDevice { Ok(TRBCCode::Success) } - fn set_endpoint_dequeue(&mut self, slotid: u32, epid: u32, trb: &XhciTRB) -> Result { + fn set_tr_dequeue_pointer( + &mut self, + slotid: u32, + epid: u32, + trb: &XhciTRB, + ) -> Result { if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&epid) { error!("Invalid endpoint id {}", epid); return Ok(TRBCCode::TrbError); } - let dequeue = trb.parameter; - let mut epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize]; + if !self.slots[(slotid - 1) as usize].slot_state_is_valid(&self.mem_space)? { + error!("Invalid slot state, slotid {}", slotid); + return Ok(TRBCCode::ContextStateError); + } + let epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize]; if !epctx.enabled { - error!(" Endpoint is disabled slotid {} epid {}", slotid, epid); + error!("Endpoint is disabled, slotid {} epid {}", slotid, epid); return Ok(TRBCCode::EpNotEnabledError); } - if epctx.state != EP_STOPPED { - error!("Endpoint is not stopped slotid {} epid {}", slotid, epid); + if epctx.state != EP_STOPPED && epctx.state != EP_ERROR { + error!( + "Endpoint invalid state, slotid {} epid {} state {}", + slotid, epid, epctx.state + ); return Ok(TRBCCode::ContextStateError); } - epctx.ring.init(dequeue & !0xf); - epctx.ring.ccs = (dequeue & 1) == 1; - epctx.set_state(&self.mem_space, EP_STOPPED)?; + epctx.update_dequeue(&self.mem_space, trb.parameter)?; Ok(TRBCCode::Success) } -- Gitee From 3d71ce0542df78d9622b89a3b96e64cd04ad60fc Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 09:58:43 +0800 Subject: [PATCH 0426/2187] xhci: the slot state should be addressed or configured when reset From section 4.6.11 Reset Device of the spec, if the device slot is not in the addressed or configured state, the xHC should report context state error. And if state is valid, xHC need to Set Context Entries field to 1 and set USB Device Address to 0. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index ef880956d..a84ce1e05 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -53,6 +53,7 @@ const SLOT_ADDRESSED: u32 = 2; const SLOT_CONFIGURED: u32 = 3; const SLOT_CONTEXT_ENTRIES_MASK: u32 = 0x1f; const SLOT_CONTEXT_ENTRIES_SHIFT: u32 = 27; +const SLOT_CONTEXT_DEVICE_ADDRESS_MASK: u32 = 0xff; /// TRB flags const TRB_CR_BSR: u32 = 1 << 9; const TRB_CR_EPID_SHIFT: u32 = 16; @@ -651,7 +652,9 @@ impl XhciDevice { } } TRBType::CrResetDevice => { - event.ccode = self.reset_slot(slot_id)?; + if slot_id != 0 { + event.ccode = self.reset_device(slot_id)?; + } } _ => { error!("Invalid Command: type {:?}", trb_type); @@ -921,19 +924,27 @@ impl XhciDevice { Ok(TRBCCode::Success) } - fn reset_slot(&mut self, slot_id: u32) -> Result { + fn reset_device(&mut self, slot_id: u32) -> Result { let mut slot_ctx = XhciSlotCtx::default(); let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; - for i in 2..32 { - self.disable_endpoint(slot_id, i)?; - } dma_read_u32( &self.mem_space, GuestAddress(octx), slot_ctx.as_mut_dwords(), )?; + let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; + if slot_state != SLOT_ADDRESSED && slot_state != SLOT_CONFIGURED { + error!("Invalid slot state: {:?}", slot_state); + return Ok(TRBCCode::ContextStateError); + } + for i in 2..32 { + self.disable_endpoint(slot_id, i)?; + } slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx.dev_state |= SLOT_DEFAULT << SLOT_STATE_SHIFT; + slot_ctx.dev_info &= !(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); + slot_ctx.dev_info |= 1 << SLOT_CONTEXT_ENTRIES_SHIFT; + slot_ctx.dev_state &= !SLOT_CONTEXT_DEVICE_ADDRESS_MASK; dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; Ok(TRBCCode::Success) } -- Gitee From 30576eecc149dfc5c90b79a9921fd568a7d2ecb7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 10:09:12 +0800 Subject: [PATCH 0427/2187] xhci: check slot state when stop or reset endpoint Check the slot state when stop or reset endpoint. Also check the endpoint state when stop endpoint according to the spec 4.6.9 Stop Endpoint. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 34 +++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index a84ce1e05..7f18604ed 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -638,12 +638,16 @@ impl XhciDevice { } } TRBType::CrStopEndpoint => { - let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; - event.ccode = self.stop_endpoint(slot_id, ep_id)?; + if slot_id != 0 { + let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; + event.ccode = self.stop_endpoint(slot_id, ep_id)?; + } } TRBType::CrResetEndpoint => { - let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; - event.ccode = self.reset_endpoint(slot_id, ep_id)?; + if slot_id != 0 { + let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; + event.ccode = self.reset_endpoint(slot_id, ep_id)?; + } } TRBType::CrSetTrDequeue => { if slot_id != 0 { @@ -998,15 +1002,25 @@ impl XhciDevice { error!("Invalid endpoint id"); return Ok(TRBCCode::TrbError); } - if !self.slots[(slot_id - 1) as usize].enabled { + if !self.slots[(slot_id - 1) as usize].slot_state_is_valid(&self.mem_space)? { + error!("Invalid slot state, slotid {}", slot_id); + return Ok(TRBCCode::ContextStateError); + } + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + if !epctx.enabled { + error!(" Endpoint is disabled, slotid {} epid {}", slot_id, ep_id); return Ok(TRBCCode::EpNotEnabledError); } + if epctx.state != EP_RUNNING { + error!( + "Endpoint invalid state, slotid {} epid {} state {}", + slot_id, ep_id, epctx.state + ); + return Ok(TRBCCode::ContextStateError); + } if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Stopped)? > 0 { warn!("endpoint stop when xfers running!"); } - if !self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].enabled { - error!("stop_endpoint ep is disabled"); - } self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] .set_state(&self.mem_space, EP_STOPPED)?; Ok(TRBCCode::Success) @@ -1017,6 +1031,10 @@ impl XhciDevice { error!("Invalid endpoint id {}", ep_id); return Ok(TRBCCode::TrbError); } + if !self.slots[(slot_id - 1) as usize].slot_state_is_valid(&self.mem_space)? { + error!("Invalid slot state, slotid {}", slot_id); + return Ok(TRBCCode::ContextStateError); + } let slot = &mut self.slots[(slot_id - 1) as usize]; let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; if !epctx.enabled { -- Gitee From 271bef5231ccc70f78f2b561f98dc8b8c7f22c73 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 10:33:10 +0800 Subject: [PATCH 0428/2187] xhci: check slot state when evaluate context Check the slot state when evaluate context and use the macros to improve readability. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 167 +++++++++++++++++++------------- 1 file changed, 97 insertions(+), 70 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 7f18604ed..3b15cfd93 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -78,12 +78,15 @@ const XHCI_PORT2_NUM: u32 = 2; const XHCI_PORT3_NUM: u32 = 2; /// Slot Context. const SLOT_INPUT_CTX_OFFSET: u64 = 0x20; +const SLOT_CTX_MAX_EXIT_LATENCY_MASK: u32 = 0xffff; +const SLOT_CTX_INTERRUPTER_TARGET_MASK: u32 = 0xffc00000; /// Endpoint Context. const EP_INPUT_CTX_ENTRY_SIZE: u64 = 0x20; const EP_INPUT_CTX_OFFSET: u64 = 0x40; const EP_CTX_OFFSET: u64 = 0x20; const EP_CTX_TR_DEQUEUE_POINTER_MASK: u64 = !0xf; const EP_CTX_DCS: u64 = 1; +const EP_CTX_MAX_PACKET_SIZE_MASK: u32 = 0xffff0000; type DmaAddr = u64; @@ -242,14 +245,20 @@ impl XhciSlot { } } - /// Get the slot state from the memory. - fn get_slot_state(&self, mem: &Arc) -> Result { + /// Get the slot context from the memory. + fn get_slot_ctx(&self, mem: &Arc) -> Result { let mut slot_ctx = XhciSlotCtx::default(); dma_read_u32( mem, GuestAddress(self.slot_ctx_addr), slot_ctx.as_mut_dwords(), )?; + Ok(slot_ctx) + } + + /// Get the slot state from the memory. + fn get_slot_state(&self, mem: &Arc) -> Result { + let slot_ctx = self.get_slot_ctx(mem)?; let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; Ok(slot_state) } @@ -330,6 +339,18 @@ pub struct XhciSlotCtx { pub dev_state: u32, } +impl XhciSlotCtx { + fn set_slot_state(&mut self, state: u32) { + self.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); + self.dev_state |= (state & SLOT_STATE_MASK) << SLOT_STATE_SHIFT; + } + + fn set_context_entry(&mut self, num: u32) { + self.dev_info &= !(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); + self.dev_info |= (num & SLOT_CONTEXT_ENTRIES_MASK) << SLOT_CONTEXT_ENTRIES_SHIFT; + } +} + impl DwordOrder for XhciSlotCtx {} /// Endpoint Context. See the spec 6.2.3 Endpoint Context. @@ -629,12 +650,12 @@ impl XhciDevice { } TRBType::CrConfigureEndpoint => { if slot_id != 0 { - event.ccode = self.config_slot(slot_id, &trb)?; + event.ccode = self.configure_endpoint(slot_id, &trb)?; } } TRBType::CrEvaluateContext => { if slot_id != 0 { - event.ccode = self.evaluate_slot(slot_id, &trb)?; + event.ccode = self.evaluate_context(slot_id, &trb)?; } } TRBType::CrStopEndpoint => { @@ -797,24 +818,38 @@ impl XhciDevice { self.oper.dcbaap + (8 * slot_id) as u64 } - fn config_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { - let ictx = trb.parameter; - let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; - let mut slot_ctx = XhciSlotCtx::default(); + fn configure_endpoint(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + let slot_state = self.slots[(slot_id - 1) as usize].get_slot_state(&self.mem_space)?; if trb.control & TRB_CR_DC == TRB_CR_DC { - for i in 2..32 { - self.disable_endpoint(slot_id, i)?; + if slot_state != SLOT_CONFIGURED { + error!("Invalid slot state: {:?}", slot_state); + return Ok(TRBCCode::ContextStateError); } - dma_read_u32( - &self.mem_space, - GuestAddress(octx), - slot_ctx.as_mut_dwords(), - )?; - slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx.dev_state |= SLOT_ADDRESSED << SLOT_STATE_SHIFT; - dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; - return Ok(TRBCCode::Success); + return self.deconfigure_endpoint(slot_id); + } + if slot_state != SLOT_CONFIGURED && slot_state != SLOT_ADDRESSED { + error!("Invalid slot state: {:?}", slot_state); + return Ok(TRBCCode::ContextStateError); } + self.config_slot_ep(slot_id, trb.parameter)?; + Ok(TRBCCode::Success) + } + + fn deconfigure_endpoint(&mut self, slot_id: u32) -> Result { + for i in 2..32 { + self.disable_endpoint(slot_id, i)?; + } + let mut slot_ctx = self.slots[(slot_id - 1) as usize].get_slot_ctx(&self.mem_space)?; + slot_ctx.set_slot_state(SLOT_ADDRESSED); + dma_write_u32( + &self.mem_space, + GuestAddress(self.slots[(slot_id - 1) as usize].slot_ctx_addr), + slot_ctx.as_dwords(), + )?; + Ok(TRBCCode::Success) + } + + fn config_slot_ep(&mut self, slot_id: u32, ictx: u64) -> Result { let mut ictl_ctx = XhciInputCtrlCtx::default(); dma_read_u32( &self.mem_space, @@ -825,54 +860,47 @@ impl XhciDevice { error!("Invalid control context {:?}", ictl_ctx); return Ok(TRBCCode::TrbError); } - let mut islot_ctx = XhciSlotCtx::default(); - dma_read_u32( - &self.mem_space, - GuestAddress(ictx + 32), - islot_ctx.as_mut_dwords(), - )?; - dma_read_u32( - &self.mem_space, - GuestAddress(octx), - slot_ctx.as_mut_dwords(), - )?; - if (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK < SLOT_ADDRESSED { - error!("Invalid slot state"); - return Ok(TRBCCode::TrbError); - } - self.config_slot_ep(slot_id, ictx, octx, ictl_ctx)?; - slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx.dev_state |= SLOT_CONFIGURED << SLOT_STATE_SHIFT; - slot_ctx.dev_info &= !(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); - slot_ctx.dev_info |= - islot_ctx.dev_info & (SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); - dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; - Ok(TRBCCode::Success) - } - - fn config_slot_ep( - &mut self, - slot_id: u32, - ictx: u64, - octx: u64, - ictl_ctx: XhciInputCtrlCtx, - ) -> Result { + let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; for i in 2..32 { if ictl_ctx.drop_flags & (1 << i) == 1 << i { self.disable_endpoint(slot_id, i)?; } if ictl_ctx.add_flags & (1 << i) == 1 << i { self.disable_endpoint(slot_id, i)?; - let ret = self.enable_endpoint(slot_id, i, ictx, octx)?; - if ret != TRBCCode::Success { - return Ok(ret); - } + self.enable_endpoint(slot_id, i, ictx, octx)?; + } + } + // From section 4.6.6 Configure Endpoint of the spec: + // If all Endpoints are Disabled: + // Set the Slot State in the Output Slot Context to Addresed. + // else (An Endpoint is Enabled): + // Set the Slot State in the Output Slot Context to Configured. + // Set the Context Entries field in the Output Slot Context to the index of + // the last valid Endpoint Context in its Output Device Context structure. + let mut enabled_ep_idx = 0; + for i in (2..32).rev() { + if self.slots[(slot_id - 1) as usize].endpoints[(i - 1) as usize].enabled { + enabled_ep_idx = i; + break; } } + let mut slot_ctx = self.slots[(slot_id - 1) as usize].get_slot_ctx(&self.mem_space)?; + if enabled_ep_idx == 0 { + slot_ctx.set_slot_state(SLOT_ADDRESSED); + slot_ctx.set_context_entry(1); + } else { + slot_ctx.set_slot_state(SLOT_CONFIGURED); + slot_ctx.set_context_entry(enabled_ep_idx); + } + dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; Ok(TRBCCode::Success) } - fn evaluate_slot(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + fn evaluate_context(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + if !self.slots[(slot_id - 1) as usize].slot_state_is_valid(&self.mem_space)? { + error!("Invalid slot state, slot id {}", slot_id); + return Ok(TRBCCode::ContextStateError); + } let ictx = trb.parameter; let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; let mut ictl_ctx = XhciInputCtrlCtx::default(); @@ -889,7 +917,7 @@ impl XhciDevice { let mut islot_ctx = XhciSlotCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(ictx + 0x20), + GuestAddress(ictx + SLOT_INPUT_CTX_OFFSET), islot_ctx.as_mut_dwords(), )?; let mut slot_ctx = XhciSlotCtx::default(); @@ -898,30 +926,31 @@ impl XhciDevice { GuestAddress(octx), slot_ctx.as_mut_dwords(), )?; - slot_ctx.dev_info2 &= !0xffff; - slot_ctx.dev_info2 |= islot_ctx.dev_info2 & 0xffff; - slot_ctx.tt_info &= !0xff00000; - slot_ctx.tt_info |= islot_ctx.tt_info & 0xff000000; + slot_ctx.dev_info2 &= !SLOT_CTX_MAX_EXIT_LATENCY_MASK; + slot_ctx.dev_info2 |= islot_ctx.dev_info2 & SLOT_CTX_MAX_EXIT_LATENCY_MASK; + slot_ctx.tt_info &= !SLOT_CTX_INTERRUPTER_TARGET_MASK; + slot_ctx.tt_info |= islot_ctx.tt_info & SLOT_CTX_INTERRUPTER_TARGET_MASK; dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; } if ictl_ctx.add_flags & 0x2 == 0x2 { + // Default control endpoint context. let mut iep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(ictx + 0x40), + GuestAddress(ictx + EP_INPUT_CTX_OFFSET), iep_ctx.as_mut_dwords(), )?; let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(octx + 0x20), + GuestAddress(octx + EP_CTX_OFFSET), ep_ctx.as_mut_dwords(), )?; - ep_ctx.ep_info2 &= !0xffff0000; - ep_ctx.ep_info2 |= iep_ctx.ep_info2 & 0xffff0000; + ep_ctx.ep_info2 &= !EP_CTX_MAX_PACKET_SIZE_MASK; + ep_ctx.ep_info2 |= iep_ctx.ep_info2 & EP_CTX_MAX_PACKET_SIZE_MASK; dma_write_u32( &self.mem_space, - GuestAddress(octx + 0x20), + GuestAddress(octx + EP_CTX_OFFSET), ep_ctx.as_dwords(), )?; } @@ -944,10 +973,8 @@ impl XhciDevice { for i in 2..32 { self.disable_endpoint(slot_id, i)?; } - slot_ctx.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx.dev_state |= SLOT_DEFAULT << SLOT_STATE_SHIFT; - slot_ctx.dev_info &= !(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); - slot_ctx.dev_info |= 1 << SLOT_CONTEXT_ENTRIES_SHIFT; + slot_ctx.set_slot_state(SLOT_DEFAULT); + slot_ctx.set_context_entry(1); slot_ctx.dev_state &= !SLOT_CONTEXT_DEVICE_ADDRESS_MASK; dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; Ok(TRBCCode::Success) -- Gitee From 05b0d62bfbbbcf7662e9039a8c30d236b9fff17d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 14:44:33 +0800 Subject: [PATCH 0429/2187] xhci: set host controller error when process failed 1. Set host controller error when handle command or kick endpoint failed, the most common error is the dma read/write error. 2. Move the command ring start to the start_cmd_ring. Remove the duplicate check which called in the doorbell_write. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 10 ++-------- usb/src/xhci/xhci_regs.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 3b15cfd93..a255c74ac 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -454,7 +454,7 @@ impl XhciDevice { self.oper.usb_status & USB_STS_HCH != USB_STS_HCH } - fn internal_error(&mut self) { + pub fn host_controller_error(&mut self) { error!("Xhci host controller error!"); self.oper.usb_status |= USB_STS_HCE; } @@ -607,12 +607,7 @@ impl XhciDevice { /// Control plane pub fn handle_command(&mut self) -> Result<()> { - if !self.running() { - bail!("Failed to process command: xhci is not running"); - } - let mut lo = read_u32(self.oper.cmd_ring_ctrl, 0); - lo |= CMD_RING_CTRL_CRR; - self.oper.cmd_ring_ctrl = write_u64_low(self.oper.cmd_ring_ctrl, lo); + self.oper.start_cmd_ring(); let mut slot_id: u32; let mut event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::Success); for _ in 0..COMMAND_LIMIT { @@ -1153,7 +1148,6 @@ impl XhciDevice { break; } Err(e) => { - self.internal_error(); // update the endpoint in slot self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; bail!("fetch ring failed {}", e); diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 64711ac54..fcabb7517 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -144,6 +144,11 @@ impl XchiOperReg { self.dcbaap = 0; self.config = 0; } + + /// Run the command ring. + pub fn start_cmd_ring(&mut self) { + self.cmd_ring_ctrl |= CMD_RING_CTRL_CRR as u64; + } } /// XHCI Interrupter @@ -605,7 +610,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { debug!("doorbell write {:x} {:x}", addr.0, offset); if !xhci.lock().unwrap().running() { error!("Failed to write doorbell, XHCI is not running"); - return true; + return false; } let mut xhci = xhci.lock().unwrap(); let slot_id = (offset >> 2) as u32; @@ -613,6 +618,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { if value == 0 { if let Err(e) = xhci.handle_command() { error!("Failed to handle command: {:?}", e); + xhci.host_controller_error(); return false; } } else { @@ -623,6 +629,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let ep_id = value & DB_TARGET_MASK; if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { error!("Failed to kick endpoint: {:?}", e); + xhci.host_controller_error(); return false; } } -- Gitee From b47930ec4f2136423e3cee810080f964b601062f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Nov 2022 14:39:01 +0800 Subject: [PATCH 0430/2187] xhci: support No Op command Support No Op command to let xHC report the command ring dequeue pointer. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index a255c74ac..764b7c0b6 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -676,6 +676,9 @@ impl XhciDevice { event.ccode = self.reset_device(slot_id)?; } } + TRBType::CrNoop => { + event.ccode = TRBCCode::Success; + } _ => { error!("Invalid Command: type {:?}", trb_type); event.ccode = TRBCCode::TrbError; -- Gitee From 7a94583f4ca0bb94e1defb54a08b7f58f81f3671 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 18 Nov 2022 02:03:17 -0500 Subject: [PATCH 0431/2187] Balloon: Fix the test case test_balloon_process Change the value of SplitVringDesc's flag field to 0, which indicates that SplitVringDest is the last element. Otherwise, the search for the next desc continues, resulting in the following error: test balloon::tests::test_balloon_process ... thread 'main' panicked at 'assertion failed: handler.process_balloon_queue(BALLOON_INFLATE_EVENT).is_ok()', virtio/src/balloon.rs:1382:9 stack backtrace: 0: rust_begin_unwind 1: core::panicking::panic_fmt 2: core::panicking::panic 3: virtio::balloon::tests::test_balloon_process at ./src/balloon.rs:1382:9 4: virtio::balloon::tests::test_balloon_process::{{closure}} at ./src/balloon.rs:1260:5 5: core::ops::function::FnOnce::call_once at /home/abuild/rpmbuild/BUILD/rustc-1.57.0-src/library/core/src/ops/function.rs:227:5 Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 56aa99954..a86d9a7c4 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -1356,7 +1356,7 @@ mod tests { let desc = SplitVringDesc { addr: GuestAddress(0x2000), len: 4, - flags: 1, + flags: 0, next: 1, }; @@ -1387,7 +1387,7 @@ mod tests { let desc = SplitVringDesc { addr: GuestAddress(0x2000), len: 4, - flags: 1, + flags: 0, next: 1, }; -- Gitee From 11c60e6b062acf2df2aafaf8ce70f772e3856d9e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 15:02:56 +0800 Subject: [PATCH 0432/2187] virtio-scsi: flush when receive cmd SYNCHRONIZE_CACHE Rename Scsi command type. SYNCHRONIZE_CACHE should be a non-emulation scsi command. virtio-scsi should execute flushing operation when receice SYNCHRONIZE_CACHE Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 9cbcad6dd..307f7f32c 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -460,6 +460,15 @@ impl ScsiRequest { aiocb.iovec.push(iovec); } + if self.cmd.command == SYNCHRONIZE_CACHE { + aiocb.opcode = IoCmd::Fdsync; + (*aio) + .as_mut() + .rw_sync(aiocb) + .with_context(|| "Failed to process scsi request for flushing")?; + return Ok(0); + } + match self.cmd.mode { ScsiXferMode::ScsiXferFromDev => { aiocb.opcode = IoCmd::Preadv; @@ -536,7 +545,7 @@ impl ScsiRequest { sense = Some(SCSI_SENSE_NO_SENSE); Ok(Vec::new()) } - WRITE_SAME_10 | WRITE_SAME_16 | SYNCHRONIZE_CACHE => Ok(Vec::new()), + WRITE_SAME_10 | WRITE_SAME_16 => Ok(Vec::new()), TEST_UNIT_READY => { let dev_lock = self.dev.lock().unwrap(); if dev_lock.disk_image.is_none() { @@ -662,13 +671,17 @@ fn write_buf_mem(buf: &[u8], max: u64, hva: u64) -> Result<()> { Ok(()) } +// Scsi Commands which are emulated in stratovirt and do noting to the backend. pub const EMULATE_SCSI_OPS: u32 = 0; -pub const DMA_SCSI_OPS: u32 = 1; +// Scsi Commands which will do something(eg: read and write) to the backend. +pub const NON_EMULATE_SCSI_OPS: u32 = 1; fn scsi_operation_type(op: u8) -> u32 { match op { READ_6 | READ_10 | READ_12 | READ_16 | WRITE_6 | WRITE_10 | WRITE_12 | WRITE_16 - | WRITE_VERIFY_10 | WRITE_VERIFY_12 | WRITE_VERIFY_16 => DMA_SCSI_OPS, + | WRITE_VERIFY_10 | WRITE_VERIFY_12 | WRITE_VERIFY_16 | SYNCHRONIZE_CACHE => { + NON_EMULATE_SCSI_OPS + } _ => EMULATE_SCSI_OPS, } } @@ -1256,7 +1269,7 @@ fn scsi_command_emulate_report_luns( bail!("scsi REPORT LUNS xfer {} too short!", cmd.xfer); } - //Byte2: SELECT REPORT:00h/01h/02h. 03h to FFh is reserved. + // Byte2: SELECT REPORT:00h/01h/02h. 03h to FFh is reserved. if cmd.buf[2] > 2 { bail!( "Invalid REPORT LUNS cmd, SELECT REPORT Byte is {}", -- Gitee From 55f69086149b4c44d9756683a424181f391fd11b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 6 Nov 2022 14:27:23 +0800 Subject: [PATCH 0433/2187] virtio-scsi: Realize deactivate function VirtioDevice function will be called during vm starts as such order: First activate() -> deactivate() -> Second activate(). Now, deactivate function is not implement. As a result of that, EventFd requested in the first activate() will not be freed, and read it will report error after requesting a new EventFd in the second activate(). So, realize deactivate() function to delete the old EventFd. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 123 +++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 5a258f4c5..06489af05 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -127,7 +127,6 @@ pub struct ScsiCntlrState { } /// Virtio Scsi Controller device structure. -#[derive(Default)] pub struct ScsiCntlr { /// Configuration of the virtio scsi controller. config: ScsiCntlrConfig, @@ -135,6 +134,8 @@ pub struct ScsiCntlr { state: ScsiCntlrState, /// Scsi bus. pub bus: Option>>, + /// Eventfd for Scsi Controller deactivates. + deactivate_evt: EventFd, } impl ScsiCntlr { @@ -143,6 +144,7 @@ impl ScsiCntlr { config, state: ScsiCntlrState::default(), bus: None, + deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), } } } @@ -260,6 +262,7 @@ impl VirtioDevice for ScsiCntlr { let ctrl_handler = ScsiCtrlHandler { queue: ctrl_queue, queue_evt: ctrl_queue_evt, + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, @@ -274,6 +277,7 @@ impl VirtioDevice for ScsiCntlr { let event_handler = ScsiEventHandler { _queue: event_queue, queue_evt: event_queue_evt, + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), _mem_space: mem_space.clone(), _interrupt_cb: interrupt_cb.clone(), _driver_features: self.state.driver_features, @@ -291,6 +295,7 @@ impl VirtioDevice for ScsiCntlr { scsibus: bus.clone(), queue: cmd_queue.clone(), queue_evt: queue_evts.remove(0), + deactivate_evt: self.deactivate_evt.try_clone().unwrap(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, @@ -311,7 +316,9 @@ impl VirtioDevice for ScsiCntlr { } fn deactivate(&mut self) -> Result<()> { - Ok(()) + self.deactivate_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite)) } fn update_config(&mut self, _dev_config: Option>) -> Result<()> { @@ -557,6 +564,8 @@ pub struct ScsiCtrlHandler { queue: Arc>, /// EventFd for the ctrl virtqueue. queue_evt: EventFd, + /// EventFd for deleting ctrl handler. + deactivate_evt: EventFd, /// The address space to which the scsi HBA belongs. mem_space: Arc, /// The interrupt callback function. @@ -645,6 +654,25 @@ impl ScsiCtrlHandler { )?; Ok(()) } + + fn deactivate_evt_handler(&mut self) -> Vec { + vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.deactivate_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.queue_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + ] + } } impl EventNotifierHelper for ScsiCtrlHandler { @@ -665,6 +693,13 @@ impl EventNotifierHelper for ScsiCtrlHandler { let ctrl_fd = h_locked.queue_evt.as_raw_fd(); notifiers.push(build_event_notifier(ctrl_fd, h)); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + Some(h_clone.lock().unwrap().deactivate_evt_handler()) + }); + notifiers.push(build_event_notifier(h_locked.deactivate_evt.as_raw_fd(), h)); + notifiers } } @@ -674,6 +709,8 @@ pub struct ScsiEventHandler { _queue: Arc>, /// EventFd for the Event virtqueue. queue_evt: EventFd, + /// EventFd for deleting event handler. + deactivate_evt: EventFd, /// The address space to which the scsi HBA belongs. _mem_space: Arc, /// The interrupt callback function. @@ -700,6 +737,13 @@ impl EventNotifierHelper for ScsiEventHandler { let event_fd = h_locked.queue_evt.as_raw_fd(); notifiers.push(build_event_notifier(event_fd, h)); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + Some(h_clone.lock().unwrap().deactivate_evt_handler()) + }); + notifiers.push(build_event_notifier(h_locked.deactivate_evt.as_raw_fd(), h)); + notifiers } } @@ -708,6 +752,25 @@ impl ScsiEventHandler { fn handle_event(&mut self) -> Result<()> { Ok(()) } + + fn deactivate_evt_handler(&mut self) -> Vec { + vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.deactivate_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.queue_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + ] + } } pub struct ScsiCmdHandler { @@ -717,6 +780,8 @@ pub struct ScsiCmdHandler { queue: Arc>, /// EventFd for the Cmd virtqueue. queue_evt: EventFd, + /// EventFd for deleting cmd handler. + deactivate_evt: EventFd, /// The address space to which the scsi HBA belongs. mem_space: Arc, /// The interrupt callback function. @@ -746,6 +811,30 @@ impl EventNotifierHelper for ScsiCmdHandler { let event_fd = h_locked.queue_evt.as_raw_fd(); notifiers.push(build_event_notifier(event_fd, h)); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + Some(h_clone.lock().unwrap().deactivate_evt_handler()) + }); + notifiers.push(build_event_notifier(h_locked.deactivate_evt.as_raw_fd(), h)); + + // Register event notifier for aio. + if let Some(ref aio) = h_locked.aio { + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + + if let Some(aio) = &mut h_clone.lock().unwrap().aio { + if let Err(ref e) = aio.handle() { + error!("Failed to handle aio, {:?}", e); + } + } + None + }); + + notifiers.push(build_event_notifier(aio.fd.as_raw_fd(), h)); + } + notifiers } } @@ -875,6 +964,36 @@ impl ScsiCmdHandler { Ok(Box::new(Aio::new(complete_fun, None)?)) } + + fn deactivate_evt_handler(&mut self) -> Vec { + let mut notifiers = vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.deactivate_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.queue_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + ]; + if let Some(aio) = &self.aio { + notifiers.push(EventNotifier::new( + NotifierOperation::Delete, + aio.fd.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + )); + } + + notifiers + } } #[derive(Clone)] -- Gitee From 45b1f3e41c8362cae0366ce7a17d82898277c7f0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 18:57:41 +0800 Subject: [PATCH 0434/2187] virtio-scsi: cannot change cdb/sense size in write_config() Guest can only set sense_size and cdb_size, which are fixed default value (VIRTIO_SCSI_CDB_DEFAULT_SIZE; VIRTIO_SCSI_SENSE_DEFAULT_SIZE) and cannot be changed in stratovirt now. So, do nothing when guest writes config. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 06489af05..6ab4301c4 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -238,8 +238,9 @@ impl VirtioDevice for ScsiCntlr { ))); } - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); - + // Guest can only set sense_size and cdb_size, which are fixed default values + // (VIRTIO_SCSI_CDB_DEFAULT_SIZE; VIRTIO_SCSI_SENSE_DEFAULT_SIZE) and cannot be + // changed in stratovirt now. So, do nothing when guest writes config. Ok(()) } -- Gitee From 96a845c826d555c1598380ad0b907b271f379683 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 19:32:05 +0800 Subject: [PATCH 0435/2187] virtio-scsi: don't report error when cmd_handler don't find target/lun requested in cdb Guest will scan all the target and lun numbers in starting to identify all the scsi device. As a result, it is possible that many target/lun numbers have no corresponding scsi device. So, it's useless to report this error. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index c5fe06c12..f2cc0214c 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -917,7 +917,7 @@ impl ScsiCmdHandler { } else { cmd.resp.response = VIRTIO_SCSI_S_BAD_TARGET; cmd.complete(&self.mem_space); - error!( + debug!( "no such scsi device target {}, lun {}", lun[1], virtio_scsi_get_lun(lun) -- Gitee From 8c855c93bb0020834b04f787bc3856767270d8d1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 20:59:30 +0800 Subject: [PATCH 0436/2187] virtio-scsi: add feature VIRTIO_F_RING_EVENT_IDX Virtio-scsi adds feature VIRTIO_F_RING_EVENT_IDX. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index f2cc0214c..65471fca9 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -20,8 +20,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use super::super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, - VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, }; use crate::ScsiBus::{virtio_scsi_get_lun, ScsiBus, ScsiRequest, EMULATE_SCSI_OPS, GOOD}; use crate::VirtioError; @@ -174,7 +174,9 @@ impl VirtioDevice for ScsiCntlr { self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) - | (1_u64 << VIRTIO_SCSI_F_CHANGE); + | (1_u64 << VIRTIO_SCSI_F_CHANGE) + | (1_u64 << VIRTIO_F_RING_EVENT_IDX); + Ok(()) } -- Gitee From ea95f2d222c42905cc42adaac57d70910d1a24fc Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 21:01:21 +0800 Subject: [PATCH 0437/2187] virtio-scsi: add feature VIRTIO_F_RING_INDIRECT_DESC Virtio-scsi adds feature VIRTIO_F_RING_INDIRECT_DESC. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 65471fca9..35a8e4d7b 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -21,7 +21,8 @@ use anyhow::{anyhow, bail, Context, Result}; use super::super::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, + VIRTIO_TYPE_SCSI, }; use crate::ScsiBus::{virtio_scsi_get_lun, ScsiBus, ScsiRequest, EMULATE_SCSI_OPS, GOOD}; use crate::VirtioError; @@ -175,7 +176,8 @@ impl VirtioDevice for ScsiCntlr { self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) | (1_u64 << VIRTIO_SCSI_F_CHANGE) - | (1_u64 << VIRTIO_F_RING_EVENT_IDX); + | (1_u64 << VIRTIO_F_RING_EVENT_IDX) + | (1_u64 << VIRTIO_F_RING_INDIRECT_DESC); Ok(()) } -- Gitee From 216eaf8cbd422adc0eee936429c5c1347bba0cc8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 6 Nov 2022 14:48:12 +0800 Subject: [PATCH 0438/2187] virtio-scsi: notify guest after vring add used Notify guest after vring add used. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 39 +++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 35a8e4d7b..a5d4e1683 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -436,8 +436,8 @@ pub struct VirtioScsiRequest { _cdb_size: u32, _sense_size: u32, mode: ScsiXferMode, - _interrupt_cb: Option>, - _driver_features: u64, + interrupt_cb: Option>, + driver_features: u64, /// resp GPA. resp_addr: GuestAddress, pub req: T, @@ -486,8 +486,8 @@ impl VirtioScsiRequest { _cdb_size: VIRTIO_SCSI_CDB_DEFAULT_SIZE as u32, _sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE as u32, mode: ScsiXferMode::ScsiXferNone, - _interrupt_cb: interrupt_cb, - _driver_features: driver_features, + interrupt_cb, + driver_features, resp_addr: in_iov_elem.addr, req: scsi_req, resp: scsi_resp, @@ -548,6 +548,7 @@ impl VirtioScsiRequest { error!("Failed to write the scsi response {:?}", e); return false; } + let mut queue_lock = self.queue.lock().unwrap(); if let Err(ref e) = queue_lock.vring.add_used( mem_space, @@ -560,6 +561,24 @@ impl VirtioScsiRequest { ); return false; } + + if queue_lock + .vring + .should_notify(mem_space, self.driver_features) + { + if let Err(e) = (*self.interrupt_cb.as_ref().unwrap())( + &VirtioInterruptType::Vring, + Some(&queue_lock), + false, + ) { + error!( + "Failed to trigger interrupt(aio completion) for scsi controller, error is {:?}", + e + ); + return false; + } + } + true } } @@ -654,9 +673,6 @@ impl ScsiCtrlHandler { queue = self.queue.lock().unwrap(); } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( - || VirtioError::InterruptTrigger("scsi ctrl", VirtioInterruptType::Vring), - )?; Ok(()) } @@ -944,15 +960,6 @@ impl ScsiCmdHandler { queue = self.queue.lock().unwrap(); } - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "scsi cmd", - VirtioInterruptType::Vring - )) - }, - )?; - Ok(()) } -- Gitee From 1a2877f13ba352a5a88d9349dade96d9b331f9c1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 19 Nov 2022 22:55:16 +0800 Subject: [PATCH 0439/2187] virtio-scsi: add num-queues option for scsi controller Num-queues attribute controls the number of request queues to be used for the scsi controller. If not set, the default block queue number is 1. The max queues number supported is no more than 32. We can now enable mq by using cmd just like: -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0,multifunction=on,iothread=iothread1,num-queues=4 Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 6 +++--- machine/src/lib.rs | 11 ++++++++--- machine_manager/src/config/scsi.rs | 14 ++++++++++++-- virtio/src/scsi/controller.rs | 2 ++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 9e0401a0c..1cade7370 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -759,15 +759,15 @@ Note: Only one tablet can be configured. ### 2.16 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. -Four properties can be set for Virtio-Scsi controller. +Five properties can be set for Virtio-Scsi controller. * id: unique device id. * bus: bus number of the device. * addr: including slot number and function number. * iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) - +* num-queues: the optional num-queues attribute controls the number of request queues to be used for the scsi controller. If not set, the default block queue number is 1. The max queues number supported is no more than 32. (optional) ```shell --device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1] +-device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] ``` ### 2.17 Virtio Scsi HardDisk Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4850ee401..d2861afd5 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -637,10 +637,15 @@ pub trait MachineOps { Ok(()) } - fn add_virtio_pci_scsi(&mut self, cfg_args: &str) -> Result<()> { + fn add_virtio_pci_scsi(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - let device_cfg = parse_scsi_controller(cfg_args)?; + let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( + 0, + vm_config.machine_config.nr_cpus, + MAX_VIRTIO_QUEUE, + )); + let device_cfg = parse_scsi_controller(cfg_args, queues_auto)?; let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(device_cfg.clone()))); let bus_name = format!("{}.0", device_cfg.id); @@ -1136,7 +1141,7 @@ pub trait MachineOps { self.add_virtio_pci_blk(vm_config, cfg_args)?; } "virtio-scsi-pci" => { - self.add_virtio_pci_scsi(cfg_args)?; + self.add_virtio_pci_scsi(vm_config, cfg_args)?; } "scsi-hd" => { self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_DISK)?; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index b79e51c26..0d13c3f13 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -77,7 +77,10 @@ impl ConfigCheck for ScsiCntlrConfig { } } -pub fn parse_scsi_controller(drive_config: &str) -> Result { +pub fn parse_scsi_controller( + drive_config: &str, + queues_auto: Option, +) -> Result { let mut cmd_parser = CmdParser::new("virtio-scsi-pci"); cmd_parser .push("") @@ -85,7 +88,8 @@ pub fn parse_scsi_controller(drive_config: &str) -> Result { .push("bus") .push("addr") .push("multifunction") - .push("iothread"); + .push("iothread") + .push("num-queues"); cmd_parser.parse(drive_config)?; @@ -106,6 +110,12 @@ pub fn parse_scsi_controller(drive_config: &str) -> Result { ))); } + if let Some(queues) = cmd_parser.get_value::("num-queues")? { + cntlr_cfg.queues = queues; + } else if let Some(queues) = queues_auto { + cntlr_cfg.queues = queues as u32; + } + cntlr_cfg.check()?; Ok(cntlr_cfg) } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index a5d4e1683..1ef15fef7 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -172,6 +172,8 @@ impl VirtioDevice for ScsiCntlr { self.state.config_space.seg_max = self.queue_size() as u32 - 2; self.state.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; self.state.config_space.max_lun = VIRTIO_SCSI_MAX_LUN as u32; + // num_queues: request queues number. + self.state.config_space.num_queues = self.config.queues; self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) -- Gitee From 54ef3377ed0cc0a26e3fc13824a3ace2e380e7d0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 00:05:00 +0800 Subject: [PATCH 0440/2187] scsi-disk: add readonly config parameter Readonly parameter is used to indicate whether scsi device is read-only or not. Default option is false. We can use it just like: -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 9 +++++---- machine_manager/src/config/scsi.rs | 3 +++ virtio/src/scsi/bus.rs | 6 ++++++ virtio/src/scsi/disk.rs | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1cade7370..8fb32bf00 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -774,18 +774,19 @@ Virtio Scsi HardDisk is a virtual block device, which process read and write req Note: Only support using raw image file as backend now. -Six properties can be set for virtio-scsi hd. +Seven properties can be set for virtio-scsi hd. * file: the path of backend image file. * id: unique device id. * bus: scsi bus name, only support $scsi_controller_name + ".0" * scsi-id: id number (target) of scsi four level hierarchical address (host, channel, target, lun). Configuration range is [0, 255]. Boot scsi disk configuration range is [0, 31]. * lun: lun number (lun) of scsi four level hierarchical address (host, channel, target, lun). Configuration rage is [0, 255]. Boot scsi disk configuration range is [0, 7]. -* serial: serial number of virtio scsi device.(optional) +* serial: serial number of virtio scsi device. (optional) +* readonly: whether scsi device is read-only or not. Default option is false. (optional) ```shell --device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1] --drive file=path_on_host,id=drive-scsi0-0-0-0 +-device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] +-drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 ``` Note: Only support scsi-id=0 and lun number should start from 0 Now. diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 0d13c3f13..995f13da4 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -130,6 +130,8 @@ pub struct ScsiDevConfig { pub serial: Option, /// Scsi bus which the scsi device attaches to. pub bus: String, + /// Scsi device can not do write operation. + pub read_only: bool, /// Scsi four level hierarchical address(host, channel, target, lun). pub channel: u8, pub target: u8, @@ -204,6 +206,7 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result if let Some(drive_arg) = &vm_config.drives.remove(&scsi_drive) { scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); + scsi_dev_cfg.read_only = drive_arg.read_only; } Ok(scsi_dev_cfg) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 4bb192395..0731c0933 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -1154,10 +1154,16 @@ fn scsi_command_emulate_mode_sense( cmd.buf[4] ); + // Device specific paramteter field for direct access block devices: + // Bit 7: WP(Write Protect); bit 4: DPOFUA; if dev_lock.scsi_type == SCSI_TYPE_DISK { if dev_lock.state.features & (1 << SCSI_DISK_F_DPOFUA) != 0 { dev_specific_parameter = 0x10; } + if dev_lock.config.read_only { + // Readonly. + dev_specific_parameter |= 0x80; + } } else { dbd = true; } diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 8b9862540..993ca157a 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -139,7 +139,7 @@ impl ScsiDevice { let mut file = OpenOptions::new() .read(true) - .write(true) + .write(!self.config.read_only) .custom_flags(libc::O_DIRECT) .open(&self.config.path_on_host) .with_context(|| { -- Gitee From f85390ab7b387303c09984f40490acfba0147e54 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 04:45:31 +0800 Subject: [PATCH 0441/2187] virtio-scsi: support hot-plugging virtio scsi controller Support hotplugging virtio scsi controller by using qmp command such as: {"execute":"device_add","arguments":{"id":"scsi0","driver":"virtio-scsi-pci","bus":"pcie.1","addr":"0x0"}} Signed-off-by: liuxiangdong --- machine/src/standard_vm/mod.rs | 58 ++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 377696f96..165cc4cad 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -50,7 +50,8 @@ use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, - DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, VmConfig, MAX_VIRTIO_QUEUE, + DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, + MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -59,8 +60,8 @@ use pci::hotplug::{handle_plug, handle_unplug_request}; use pci::PciBus; use util::byte_code::ByteCode; use virtio::{ - qmp_balloon, qmp_query_balloon, Block, BlockState, VhostKern, VhostUser, VirtioDevice, - VirtioNetState, VirtioPciDevice, + qmp_balloon, qmp_query_balloon, Block, BlockState, ScsiBus, ScsiCntlr, VhostKern, VhostUser, + VirtioDevice, VirtioNetState, VirtioPciDevice, }; #[cfg(target_arch = "aarch64")] @@ -794,6 +795,46 @@ impl StdMachine { Ok(()) } + fn plug_virtio_pci_scsi( + &mut self, + pci_bdf: &PciBdf, + args: &qmp_schema::DeviceAddArgument, + ) -> Result<()> { + let multifunction = args.multifunction.unwrap_or(false); + let nr_cpus = self.get_vm_config().lock().unwrap().machine_config.nr_cpus; + let dev_cfg = ScsiCntlrConfig { + id: args.id.clone(), + iothread: args.iothread.clone(), + queues: args.queues.unwrap_or_else(|| { + VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) + }) as u32, + }; + dev_cfg.check()?; + + let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(dev_cfg.clone()))); + + let bus_name = format!("{}.0", dev_cfg.id); + ScsiBus::create_scsi_bus(&bus_name, &device)?; + if let Some(cntlr_list) = self.get_scsi_cntlr_list() { + let mut lock_cntlr_list = cntlr_list.lock().unwrap(); + lock_cntlr_list.insert(bus_name.clone(), device.clone()); + } else { + bail!("No scsi controller list found"); + } + + let result = self.add_virtio_pci_device(&args.id, pci_bdf, device, multifunction, false); + if let Err(ref e) = result { + self.get_scsi_cntlr_list() + .unwrap() + .lock() + .unwrap() + .remove(&bus_name); + bail!("Failed to add virtio scsi controller, error is {:?}", e); + } + + Ok(()) + } + fn get_socket_path(&self, vm_config: &VmConfig, chardev: String) -> Result> { let char_dev = if let Some(char_dev) = vm_config.chardev.get(&chardev) { char_dev @@ -1046,6 +1087,17 @@ impl DeviceInterface for StdMachine { ); } } + "virtio-scsi-pci" => { + if let Err(e) = self.plug_virtio_pci_scsi(&pci_bdf, args.as_ref()) { + error!("{:?}", e); + let err_str = format!("Failed to add virtio scsi controller: {}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(err_str), + None, + ); + } + } + "virtio-net-pci" => { if let Err(e) = self.plug_virtio_pci_net(&pci_bdf, args.as_ref()) { error!("{:?}", e); -- Gitee From 32867bad2076abc935e8526f41f09d854720bcb0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 08:34:11 +0800 Subject: [PATCH 0442/2187] scsi-bus: don't set unmap parameters in scsi command UNMAP is not supported in stratovirt now. Don't set unmap parameters in logical block provisioning and block limits. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 0731c0933..fe2a4c6e3 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -960,12 +960,6 @@ fn scsi_command_emulate_vpd_page( outbuf[4] = 1; let max_xfer_length: u32 = u32::MAX / 512; BigEndian::write_u32(&mut outbuf[8..12], max_xfer_length); - let max_unmap_sectors: u32 = (1_u32 << 30) / 512; - BigEndian::write_u32(&mut outbuf[20..24], max_unmap_sectors); - let max_unmap_block_desc: u32 = 255; - BigEndian::write_u32(&mut outbuf[24..28], max_unmap_block_desc); - let opt_unmap_granulatity: u32 = (1_u32 << 12) / 512; - BigEndian::write_u32(&mut outbuf[28..32], opt_unmap_granulatity); BigEndian::write_u64(&mut outbuf[36..44], max_xfer_length as u64); buflen = outbuf.len(); } @@ -981,10 +975,10 @@ fn scsi_command_emulate_vpd_page( 0xb2 => { // Logical Block Provisioning. // 0: Threshold exponent. - // 0xe0: LBPU | LBPWS | LBPWS10 | LBPRZ | ANC_SUP | DP. + // 0xe0: LBPU(bit 7) | LBPWS | LBPWS10 | LBPRZ | ANC_SUP | DP. // 0: Threshold percentage | Provisioning Type. // 0: Threshold percentage. - outbuf.append(&mut [0_u8, 0xe0_u8, 1_u8, 0_u8].to_vec()); + outbuf.append(&mut [0_u8, 0x60_u8, 1_u8, 0_u8].to_vec()); buflen = 8; } _ => { -- Gitee From 2da397bc740571f3a366ccadf0ebb5dca61a29ad Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 09:41:18 +0800 Subject: [PATCH 0443/2187] virtio-scsi: report error when fatal errors occur Refactor virtio scsi IO/TMF processing code. Report virtio error when fatal errors occur. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 34 ++-- virtio/src/scsi/controller.rs | 357 +++++++++++++++++++--------------- 2 files changed, 209 insertions(+), 182 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index fe2a4c6e3..4c1b2e6ee 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -250,13 +250,14 @@ pub const SCSI_SENSE_SPACE_ALLOC_FAILED: ScsiSense = scsisense!(DATA_PROTECT, 0x #[derive(Default)] pub struct ScsiSense { - key: u8, - asc: u8, - ascq: u8, + /// Sense key. + pub key: u8, + /// Additional sense code. + pub asc: u8, + /// Additional sense code qualifier. + pub ascq: u8, } -pub const SCSI_SENSE_LEN: u32 = 18; - /// Mode page codes for mode sense/set. pub const MODE_PAGE_R_W_ERROR: u8 = 0x01; pub const MODE_PAGE_HD_GEOMETRY: u8 = 0x04; @@ -467,7 +468,7 @@ impl ScsiRequest { aiocb.opcode = IoCmd::Fdsync; (*aio) .as_mut() - .rw_sync(aiocb) + .flush_sync(aiocb) .with_context(|| "Failed to process scsi request for flushing")?; return Ok(0); } @@ -595,7 +596,7 @@ impl ScsiRequest { )?; } else { error!( - "Error in processing scsi command 0x{:#x}, err is {:?}", + "Error in processing scsi command {:#x}, err is {:?}", self.cmd.command, e ); self.cmd_complete( @@ -612,18 +613,6 @@ impl ScsiRequest { Ok(()) } - fn set_scsi_sense(&self, sense: ScsiSense) { - let mut req = self.virtioscsireq.lock().unwrap(); - // Response code: current errors(0x70). - req.resp.sense[0] = 0x70; - req.resp.sense[2] = sense.key; - // Additional sense length: sense len - 8. - req.resp.sense[7] = SCSI_SENSE_LEN as u8 - 8; - req.resp.sense[12] = sense.asc; - req.resp.sense[13] = sense.ascq; - req.resp.sense_len = SCSI_SENSE_LEN; - } - fn cmd_complete( &self, mem_space: &Arc, @@ -632,10 +621,11 @@ impl ScsiRequest { scsisense: Option, outbuf: &[u8], ) -> Result<()> { + let mut req = self.virtioscsireq.lock().unwrap(); + if let Some(sense) = scsisense { - self.set_scsi_sense(sense); + req.resp.set_scsi_sense(sense); } - let mut req = self.virtioscsireq.lock().unwrap(); req.resp.response = response; req.resp.status = status; req.resp.resid = 0; @@ -658,7 +648,7 @@ impl ScsiRequest { } } - req.complete(mem_space); + req.complete(mem_space)?; Ok(()) } } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 1ef15fef7..1348e693f 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -20,11 +20,14 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use super::super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, - VIRTIO_TYPE_SCSI, + report_virtio_error, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, + VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, +}; +use crate::ScsiBus::{ + virtio_scsi_get_lun, ScsiBus, ScsiRequest, ScsiSense, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, + SCSI_SENSE_INVALID_OPCODE, }; -use crate::ScsiBus::{virtio_scsi_get_lun, ScsiBus, ScsiRequest, EMULATE_SCSI_OPS, GOOD}; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use log::{error, info}; @@ -53,6 +56,9 @@ const QUEUE_SIZE_SCSI: u16 = 256; pub const VIRTIO_SCSI_CDB_DEFAULT_SIZE: usize = 32; pub const VIRTIO_SCSI_SENSE_DEFAULT_SIZE: usize = 96; +/// Basic length of fixed format sense data. +pub const SCSI_SENSE_LEN: u32 = 18; + /// The key is bus name, the value is the attached Scsi Controller. pub type ScsiCntlrMap = Arc>>>>; @@ -426,6 +432,19 @@ impl Default for VirtioScsiCmdResp { } } +impl VirtioScsiCmdResp { + pub fn set_scsi_sense(&mut self, sense: ScsiSense) { + // Response code: current errors(0x70). + self.sense[0] = 0x70; + self.sense[2] = sense.key; + // Additional sense length: sense len - 8. + self.sense[7] = SCSI_SENSE_LEN as u8 - 8; + self.sense[12] = sense.asc; + self.sense[13] = sense.ascq; + self.sense_len = SCSI_SENSE_LEN; + } +} + impl ByteCode for VirtioScsiCmdResp {} /// T: request; U: response. @@ -438,7 +457,10 @@ pub struct VirtioScsiRequest { _cdb_size: u32, _sense_size: u32, mode: ScsiXferMode, - interrupt_cb: Option>, + interrupt_cb: Arc, + /// Virtio device should report virtio error when fatal errors occur. + /// And then, deactivate eventfd is used to disable virtio queues. + deactivate_evt: EventFd, driver_features: u64, /// resp GPA. resp_addr: GuestAddress, @@ -451,7 +473,8 @@ impl VirtioScsiRequest { fn new( mem_space: &Arc, queue: Arc>, - interrupt_cb: Option>, + interrupt_cb: Arc, + deactivate_evt: EventFd, driver_features: u64, elem: &Element, ) -> Result { @@ -489,6 +512,7 @@ impl VirtioScsiRequest { _sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE as u32, mode: ScsiXferMode::ScsiXferNone, interrupt_cb, + deactivate_evt, driver_features, resp_addr: in_iov_elem.addr, req: scsi_req, @@ -545,10 +569,9 @@ impl VirtioScsiRequest { Ok(request) } - pub fn complete(&self, mem_space: &Arc) -> bool { + pub fn complete(&self, mem_space: &Arc) -> Result<()> { if let Err(ref e) = mem_space.write_object(&self.resp, self.resp_addr) { - error!("Failed to write the scsi response {:?}", e); - return false; + bail!("Failed to write the scsi response {:?}", e); } let mut queue_lock = self.queue.lock().unwrap(); @@ -557,31 +580,29 @@ impl VirtioScsiRequest { self.desc_index, self.data_len + (size_of::() as u32), ) { - error!( + bail!( "Failed to add used ring(scsi completion), index {}, len {} {:?}", - self.desc_index, self.data_len, e + self.desc_index, + self.data_len, + e ); - return false; } if queue_lock .vring .should_notify(mem_space, self.driver_features) { - if let Err(e) = (*self.interrupt_cb.as_ref().unwrap())( - &VirtioInterruptType::Vring, - Some(&queue_lock), - false, - ) { - error!( + if let Err(e) = + (*self.interrupt_cb.as_ref())(&VirtioInterruptType::Vring, Some(&queue_lock), false) + { + bail!( "Failed to trigger interrupt(aio completion) for scsi controller, error is {:?}", e ); - return false; } } - true + Ok(()) } } @@ -602,77 +623,74 @@ pub struct ScsiCtrlHandler { impl ScsiCtrlHandler { fn handle_ctrl(&mut self) -> Result<()> { - let mut queue = self.queue.lock().unwrap(); + let result = self.handle_ctrl_request(); + if result.is_err() { + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + Some(&self.deactivate_evt), + ); + } - while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + result + } + + fn handle_ctrl_request(&mut self) -> Result<()> { + if !self.queue.lock().unwrap().is_enabled() { + return Ok(()); + } + + loop { + let mut queue = self.queue.lock().unwrap(); + let elem = queue + .vring + .pop_avail(&self.mem_space, self.driver_features)?; + drop(queue); if elem.desc_num == 0 { break; } - drop(queue); + let ctrl_desc = elem.out_iovec.get(0).unwrap(); let ctrl_type = self .mem_space .read_object::(ctrl_desc.addr) .with_context(|| "Failed to get control queue descriptor")?; + match ctrl_type { VIRTIO_SCSI_T_TMF => { - match VirtioScsiRequest::::new( - &self.mem_space, - self.queue.clone(), - Some(self.interrupt_cb.clone()), - self.driver_features, - &elem, - ) { - Ok(mut tmf) => { - info!("incomplete tmf req, subtype {}!", tmf.req.subtype); - - tmf.resp.response = VIRTIO_SCSI_S_OK; - tmf.complete(&self.mem_space); - } - Err(ref e) => { - let mut queue = self.queue.lock().unwrap(); - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; - drop(queue); - - error!("Failed to create VIRTIO_SCSI_T_TMF request, {:?}", e); - } - } + let mut tmf = + VirtioScsiRequest::::new( + &self.mem_space, + self.queue.clone(), + self.interrupt_cb.clone(), + self.deactivate_evt.try_clone().unwrap(), + self.driver_features, + &elem, + )?; + info!("incomplete tmf req, subtype {}!", tmf.req.subtype); + // Scsi Task Management Function is not supported. + // So, do nothing when stratovirt receives TMF request except responsing guest scsi drivers. + tmf.resp.response = VIRTIO_SCSI_S_OK; + tmf.complete(&self.mem_space)?; } - VIRTIO_SCSI_T_AN_QUERY | VIRTIO_SCSI_T_AN_SUBSCRIBE => { - match VirtioScsiRequest::::new( - &self.mem_space, - self.queue.clone(), - Some(self.interrupt_cb.clone()), - self.driver_features, - &elem, - ) { - Ok(mut an) => { - info!("incomplete An req!"); - an.resp.evnet_actual = 0; - an.resp.response = VIRTIO_SCSI_S_OK; - an.complete(&self.mem_space); - } - Err(ref e) => { - let mut queue = self.queue.lock().unwrap(); - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; - drop(queue); - - error!("Failed to create scsi ctrl an req, {:?}", e) - } - } + let mut an = + VirtioScsiRequest::::new( + &self.mem_space, + self.queue.clone(), + self.interrupt_cb.clone(), + self.deactivate_evt.try_clone().unwrap(), + self.driver_features, + &elem, + )?; + an.resp.evnet_actual = 0; + an.resp.response = VIRTIO_SCSI_S_OK; + an.complete(&self.mem_space)?; } _ => { - bail!("Control queue type doesn't support {}", ctrl_type); + bail!("Invalid ctrl type {}", ctrl_type); } } - queue = self.queue.lock().unwrap(); } Ok(()) @@ -864,102 +882,113 @@ impl EventNotifierHelper for ScsiCmdHandler { impl ScsiCmdHandler { fn handle_cmd(&mut self) -> Result<()> { - let mut queue = self.queue.lock().unwrap(); + let result = self.handle_cmd_request(); + if result.is_err() { + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + Some(&self.deactivate_evt), + ); + } + + result + } - while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + fn handle_cmd_request(&mut self) -> Result<()> { + if !self.queue.lock().unwrap().is_enabled() { + return Ok(()); + } + + loop { + let mut queue = self.queue.lock().unwrap(); + let elem = queue + .vring + .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { break; } - match VirtioScsiRequest::::new( + drop(queue); + + let mut cmd = VirtioScsiRequest::::new( &self.mem_space, self.queue.clone(), - Some(self.interrupt_cb.clone()), + self.interrupt_cb.clone(), + self.deactivate_evt.try_clone().unwrap(), self.driver_features, &elem, - ) { - Ok(mut cmd) => { - drop(queue); - let lun: [u8; 8] = cmd.req.lun; - let scsibus = self.scsibus.lock().unwrap(); - let req_lun_id = virtio_scsi_get_lun(lun); - - if let Some(scsidevice) = scsibus.get_device(lun[1], req_lun_id) { - drop(scsibus); - let cmd_req = Arc::new(Mutex::new(cmd)); - let req = if let Ok(scsireq) = - ScsiRequest::new(cmd_req, self.scsibus.clone(), scsidevice.clone()) - { - Some(scsireq) - } else { - let mut queue = self.queue.lock().unwrap(); - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; - drop(queue); - error!("Failed to create scsi request"); - - None - }; - - let scsi_dev_lock = scsidevice.lock().unwrap(); - let found_lun_id = scsi_dev_lock.config.lun; - drop(scsi_dev_lock); - - if let Some(scsireq) = req { - if scsireq.opstype == EMULATE_SCSI_OPS { - let scsicompletecb = ScsiCompleteCb::new( - self.mem_space.clone(), - Arc::new(Mutex::new(scsireq.clone())), - ); - scsireq.emulate_execute( - scsicompletecb, - req_lun_id, - found_lun_id, - )?; - } else if let Some(disk_img) = - scsidevice.lock().unwrap().disk_image.as_mut() - { - let scsicompletecb = ScsiCompleteCb::new( - self.mem_space.clone(), - Arc::new(Mutex::new(scsireq.clone())), - ); - if let Some(ref mut aio) = self.aio { - scsireq.execute( - aio, - disk_img, - false, - None, - true, - scsicompletecb, - )?; - } - } - } - } else { - cmd.resp.response = VIRTIO_SCSI_S_BAD_TARGET; - cmd.complete(&self.mem_space); - debug!( - "no such scsi device target {}, lun {}", - lun[1], - virtio_scsi_get_lun(lun) + )?; + + let lun = cmd.req.lun; + let scsibus = self.scsibus.lock().unwrap(); + let req_lun_id = virtio_scsi_get_lun(lun); + + let scsidevice = if let Some(scsi_device) = scsibus.get_device(lun[1], req_lun_id) { + scsi_device + } else { + // No such target. Response VIRTIO_SCSI_S_BAD_TARGET to guest scsi drivers. + // It's not an error! + cmd.resp.response = VIRTIO_SCSI_S_BAD_TARGET; + cmd.complete(&self.mem_space)?; + debug!( + "no such scsi device target {}, lun {}", + lun[1], + virtio_scsi_get_lun(lun) + ); + continue; + }; + drop(scsibus); + + let cmd_h = Arc::new(Mutex::new(cmd)); + let scsi_req = if let Ok(req) = + ScsiRequest::new(cmd_h.clone(), self.scsibus.clone(), scsidevice.clone()) + { + req + } else { + // Wrong scsi cdb. Response CHECK_CONDITION / SCSI_SENSE_INVALID_OPCODE to guest scsi drivers. + let mut cmd_lock = cmd_h.lock().unwrap(); + cmd_lock.resp.set_scsi_sense(SCSI_SENSE_INVALID_OPCODE); + cmd_lock.resp.status = CHECK_CONDITION; + cmd_lock.complete(&self.mem_space)?; + drop(cmd_lock); + + error!("Failed to create scsi request"); + continue; + }; + + // If found lun id is not equal to request lun id, this request is a target request. + let scsi_dev_lock = scsidevice.lock().unwrap(); + let found_lun_id = scsi_dev_lock.config.lun; + drop(scsi_dev_lock); + + if scsi_req.opstype == EMULATE_SCSI_OPS { + let scsicompletecb = ScsiCompleteCb::new( + self.mem_space.clone(), + Arc::new(Mutex::new(scsi_req.clone())), + ); + scsi_req.emulate_execute(scsicompletecb, req_lun_id, found_lun_id)?; + } else if let Some(disk_img) = scsidevice.lock().unwrap().disk_image.as_mut() { + let scsicompletecb = ScsiCompleteCb::new( + self.mem_space.clone(), + Arc::new(Mutex::new(scsi_req.clone())), + ); + if let Some(ref mut aio) = self.aio { + if let Err(ref e) = + scsi_req.execute(aio, disk_img, false, None, true, scsicompletecb) + { + // If read/write operation by rw_sync or flush operation fails, this request will not + // call complete_func(). So we should process this error here. Otherwise, stratovirt + // will not add used index and notify guest, and then guest will be stuck. + error!( + "Failed to execute scsi device non emulation request, {:?}", + e ); - }; - } - Err(ref e) => { - // If it fails, also need to free descriptor table entry. - let mut queue = self.queue.lock().unwrap(); - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; - drop(queue); - - error!("Failed to create cmd request, {:?}", e); + let mut cmd_lock = cmd_h.lock().unwrap(); + cmd_lock.resp.response = VIRTIO_SCSI_S_FAILURE; + cmd_lock.complete(&self.mem_space)?; + drop(cmd_lock); + } } } - - queue = self.queue.lock().unwrap(); } Ok(()) @@ -980,7 +1009,15 @@ impl ScsiCmdHandler { virtio_scsi_req.resp.status = GOOD; virtio_scsi_req.resp.resid = 0; virtio_scsi_req.resp.sense_len = 0; - virtio_scsi_req.complete(&complete_cb.mem_space); + let result = virtio_scsi_req.complete(&complete_cb.mem_space); + if result.is_err() { + let deactivate_evt = &virtio_scsi_req.deactivate_evt; + report_virtio_error( + virtio_scsi_req.interrupt_cb.clone(), + virtio_scsi_req.driver_features, + Some(deactivate_evt), + ); + } }) as AioCompleteFunc); Ok(Box::new(Aio::new(complete_fun, None)?)) -- Gitee From d18d997c77e9ad9d962449828283a2d220975545 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 11:11:16 +0800 Subject: [PATCH 0444/2187] scsi-bus: fix errors in SERVICE_ACTION_IN_16 and READ_CAPACITY_10 scsi cmd Nb_sectors is the logical block address of the last logical block in SERVICE_ACTION_IN_16 and READ_CAPACITY_10 scsi command. So, it should be total logical block numbers - 1. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 4c1b2e6ee..ccb070f40 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -1106,9 +1106,10 @@ fn scsi_command_emulate_read_capacity_10( let dev_lock = dev.lock().unwrap(); let mut outbuf: Vec = vec![0; 8]; - let nb_sectors = cmp::min(dev_lock.disk_sectors as u32, u32::MAX); + let mut nb_sectors = cmp::min(dev_lock.disk_sectors as u32, u32::MAX); + nb_sectors -= 1; - // Bytes[0-3]: Returned Logical Block Address. + // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical block). // Bytes[4-7]: Logical Block Length In Bytes. BigEndian::write_u32(&mut outbuf[0..4], nb_sectors); BigEndian::write_u32(&mut outbuf[4..8], DEFAULT_SECTOR_SIZE); @@ -1308,11 +1309,12 @@ fn scsi_command_emulate_service_action_in_16( if cmd.buf[1] & 0x1f == SUBCODE_READ_CAPACITY_16 { let dev_lock = dev.lock().unwrap(); let mut outbuf: Vec = vec![0; 32]; - let nb_sectors = dev_lock.disk_sectors; + let mut nb_sectors = dev_lock.disk_sectors; + nb_sectors -= 1; drop(dev_lock); - // Byte[0-7]: Returned Logical BLock Address. + // Byte[0-7]: Returned Logical BLock Address(the logical block address of the last logical block). // Byte[8-11]: Logical Block Length in Bytes. BigEndian::write_u64(&mut outbuf[0..8], nb_sectors); BigEndian::write_u32(&mut outbuf[8..12], DEFAULT_SECTOR_SIZE); -- Gitee From ac75845e1764e931807673d25b980f9e3eadf43f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 16:31:13 +0800 Subject: [PATCH 0445/2187] virtio-scsi: support "direct" and "aio" parameters We can now support "direct" and "aio" parameters by using cmd just like: -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 direct: open block device with `O_DIRECT` mode. (optional) If not set, default is true. aio: the aio type of block device (optional). Possible values are `native`, `io_uring`, or `off`. If not set, default is `native` if `direct` is true, otherwise default is `off`. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 4 +++- machine_manager/src/config/scsi.rs | 6 ++++++ virtio/src/scsi/controller.rs | 20 ++++++++++++-------- virtio/src/scsi/disk.rs | 20 +++++++------------- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 407819fde..be760720f 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -783,10 +783,12 @@ Seven properties can be set for virtio-scsi hd. * lun: lun number (lun) of scsi four level hierarchical address (host, channel, target, lun). Configuration rage is [0, 255]. Boot scsi disk configuration range is [0, 7]. * serial: serial number of virtio scsi device. (optional) * readonly: whether scsi device is read-only or not. Default option is false. (optional) +* direct: open block device with `O_DIRECT` mode. (optional) If not set, default is true. +* aio: the aio type of block device (optional). Possible values are `native`, `io_uring`, or `off`. If not set, default is `native` if `direct` is true, otherwise default is `off`. ```shell -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] --drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true] +-drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 ``` Note: Only support scsi-id=0 and lun number should start from 0 Now. diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 995f13da4..085ed1050 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -132,6 +132,10 @@ pub struct ScsiDevConfig { pub bus: String, /// Scsi device can not do write operation. pub read_only: bool, + /// If true, use direct access io. + pub direct: bool, + /// Async IO type. + pub aio_type: Option, /// Scsi four level hierarchical address(host, channel, target, lun). pub channel: u8, pub target: u8, @@ -207,6 +211,8 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result if let Some(drive_arg) = &vm_config.drives.remove(&scsi_drive) { scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); scsi_dev_cfg.read_only = drive_arg.read_only; + scsi_dev_cfg.direct = drive_arg.direct; + scsi_dev_cfg.aio_type = drive_arg.aio.clone(); } Ok(scsi_dev_cfg) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 1348e693f..216fc0ab7 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -955,25 +955,29 @@ impl ScsiCmdHandler { continue; }; - // If found lun id is not equal to request lun id, this request is a target request. - let scsi_dev_lock = scsidevice.lock().unwrap(); - let found_lun_id = scsi_dev_lock.config.lun; - drop(scsi_dev_lock); - + let scsi_device_lock = scsidevice.lock().unwrap(); if scsi_req.opstype == EMULATE_SCSI_OPS { + let lun = scsi_device_lock.config.lun; + drop(scsi_device_lock); let scsicompletecb = ScsiCompleteCb::new( self.mem_space.clone(), Arc::new(Mutex::new(scsi_req.clone())), ); - scsi_req.emulate_execute(scsicompletecb, req_lun_id, found_lun_id)?; - } else if let Some(disk_img) = scsidevice.lock().unwrap().disk_image.as_mut() { + // If found device's lun id is not equal to request lun id, this request is a target request. + scsi_req.emulate_execute(scsicompletecb, req_lun_id, lun)?; + } else { + let aio_type = scsi_device_lock.config.aio_type.clone(); + let direct = scsi_device_lock.config.direct; + let disk_img = scsi_device_lock.disk_image.as_ref().unwrap().clone(); + drop(scsi_device_lock); + let scsicompletecb = ScsiCompleteCb::new( self.mem_space.clone(), Arc::new(Mutex::new(scsi_req.clone())), ); if let Some(ref mut aio) = self.aio { if let Err(ref e) = - scsi_req.execute(aio, disk_img, false, None, true, scsicompletecb) + scsi_req.execute(aio, &disk_img, direct, aio_type, true, scsicompletecb) { // If read/write operation by rw_sync or flush operation fails, this request will not // call complete_func(). So we should process this error here. Otherwise, stratovirt diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 993ca157a..dd6943de1 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -10,15 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::fs::{File, OpenOptions}; +use std::fs::File; use std::io::{Seek, SeekFrom}; -use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; use crate::ScsiBus::ScsiBus; use machine_manager::config::ScsiDevConfig; +use util::file::open_disk_file; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -137,17 +137,11 @@ impl ScsiDevice { if !self.config.path_on_host.is_empty() { self.disk_image = None; - let mut file = OpenOptions::new() - .read(true) - .write(!self.config.read_only) - .custom_flags(libc::O_DIRECT) - .open(&self.config.path_on_host) - .with_context(|| { - format!( - "Failed to open the file {} for scsi device", - self.config.path_on_host - ) - })?; + let mut file = open_disk_file( + &self.config.path_on_host, + self.config.read_only, + self.config.direct, + )?; disk_size = file .seek(SeekFrom::End(0)) -- Gitee From 776b6756753316fc040cdc35912d342f7890bf81 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 20 Nov 2022 21:00:36 +0800 Subject: [PATCH 0446/2187] loop_context: report warning when reading fd failure In some situation, the same fd is shared by more than one objects. E.g, the deactivate fd of virtio-blk/virtio-scsi is shared by all the virtqueues. The deactivate function write the fd to broadcast notification of all queues. If the first queue read fd, other queues can not read fd successfully again. So, it's confusing to report "Failed to read fd" error. Change log level from error to warn. Signed-off-by: liuxiangdong --- util/src/loop_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 09376031c..04761b05e 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; use libc::{c_void, read}; -use log::{error, warn}; +use log::warn; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use crate::UtilError; @@ -517,7 +517,7 @@ pub fn read_fd(fd: RawFd) -> u64 { }; if ret == -1 { - error!("Failed to read fd"); + warn!("Failed to read fd"); } value -- Gitee From 4d68049fc28d474aec6e6b7641906bdd66b2fc6a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 14 Nov 2022 22:33:55 -0500 Subject: [PATCH 0447/2187] Balloon: Modify the read_config offset verification logic The value of the config offset non-zero field should be allowed to be read. issue: https://gitee.com/openeuler/stratovirt/issues/I60JO3 Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 2d53fb7e5..c5b9766ed 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -937,17 +937,19 @@ impl VirtioDevice for Balloon { num_pages: self.num_pages, actual: self.actual.load(Ordering::Acquire), }; - if offset != 0 { - return Err(anyhow!(VirtioError::IncorrectOffset(0, offset))); + + let config_len = size_of::() as u64; + let data_len = data.len() as u64; + if offset + data_len > config_len { + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - data.write_all( - &new_config.as_bytes()[offset as usize - ..cmp::min( - offset as usize + data.len(), - size_of::(), - )], - ) - .with_context(|| "Failed to write data to 'data' while reading balloon config")?; + + if let Some(end) = offset.checked_add(data_len) { + data.write_all( + &new_config.as_bytes()[offset as usize..cmp::min(end, config_len) as usize], + )?; + } + Ok(()) } -- Gitee From e68114e016f474c039c4f1a62d47ee94ce927a70 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 21 Nov 2022 17:28:55 +0800 Subject: [PATCH 0448/2187] snapshot/gci_v2: add document description that does not support the GIC_v2 version The current code does not implement the feature that snapshots and restores of GIC_v2. So, it can not snapshot and live migration on Raspberry Pi. Signed-off-by: Xinle.Guo --- docs/migration.md | 1 + docs/snapshot.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/migration.md b/docs/migration.md index abcef3a8e..c9ab509e5 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -101,6 +101,7 @@ Some devices and feature don't support to be migration yet: - `balloon` - `mem-shared`,`backend file of memory` - `pmu` +- `gic-version=2` Some device attributes can't be changed: - `virtio-net`: mac diff --git a/docs/snapshot.md b/docs/snapshot.md index d28dffaf5..515f40c57 100644 --- a/docs/snapshot.md +++ b/docs/snapshot.md @@ -86,7 +86,8 @@ Some devices and feature don't support to be snapshot yet: - `vfio` devices - `balloon` - `hugepage`,`mem-shared`,`backend file of memory` -- `pmu`: ongoing PMU measurements +- `pmu` +- `gic-version=2` Some device attributes can't be changed: - `virtio-net`: mac -- Gitee From 1b51fbadf46a272357c983c568cab5a4433e4238 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 18 Nov 2022 09:51:06 +0800 Subject: [PATCH 0449/2187] Fix: Set the maximum interrupt ID supported by the USB to 16 Currently, there is only one interrupt ID, whitch restricts subsequent device expansion. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 764b7c0b6..9358c4f99 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -401,7 +401,7 @@ impl XhciDevice { numports_3: XHCI_PORT3_NUM, ports: Vec::new(), slots: vec![XhciSlot::new(mem_space); MAX_SLOTS as usize], - intrs: vec![XhciInterrupter::new(mem_space); 1], + intrs: vec![XhciInterrupter::new(mem_space); MAX_INTRS as usize], cmd_ring: XhciRing::new(mem_space), mem_space: mem_space.clone(), bus: Arc::new(Mutex::new(UsbBus::new())), @@ -1496,7 +1496,8 @@ impl XhciDevice { evt.length = *edtla & 0xffffff; *edtla = 0; } - self.send_event(&evt, 0)?; + let idx = (trb.status >> TRB_INTR_SHIFT) & TRB_INTR_MASK; + self.send_event(&evt, idx)?; Ok(()) } -- Gitee From 35a51c9e79a7a29665aed86640302199ec371d05 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 18 Nov 2022 10:10:52 +0800 Subject: [PATCH 0450/2187] Fix: Simplified clear Command CRR bit The Command Running Control is 64 bits, to modify the lower 32 bits, the read and then write operations are complex. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 9358c4f99..2440a7f9b 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -445,9 +445,7 @@ impl XhciDevice { pub fn stop(&mut self) { self.oper.usb_status |= USB_STS_HCH; - let mut lo = read_u32(self.oper.cmd_ring_ctrl, 0); - lo &= !CMD_RING_CTRL_CRR; - write_u64_low(self.oper.cmd_ring_ctrl, lo); + self.oper.cmd_ring_ctrl &= !(CMD_RING_CTRL_CRR as u64); } pub fn running(&self) -> bool { -- Gitee From 36b69102d9359447f44b850bde5eccd06c869569 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 18 Nov 2022 15:50:24 +0800 Subject: [PATCH 0451/2187] Fix: The number of ports on the XHCI is configured through boot parameters. The XHCI has only two ports, whitch limits the number of USB devices. Now it is configred by boot parameter. The maximum number cannot exceed 15. If this parameter is not set, the default value is 4. Signed-off-by: Mingwang Li --- Cargo.lock | 1 + machine/src/lib.rs | 2 +- machine_manager/src/config/usb.rs | 51 +++++++++++++++++++++++++++++-- usb/Cargo.toml | 2 ++ usb/src/usb.rs | 4 +-- usb/src/xhci/xhci_controller.rs | 40 ++++++++++++++++-------- usb/src/xhci/xhci_pci.rs | 7 +++-- usb/src/xhci/xhci_regs.rs | 10 +++--- 8 files changed, 91 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b3d2bd42..41ecc506a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -826,6 +826,7 @@ dependencies = [ "byteorder", "libc", "log", + "machine_manager", "once_cell", "pci", "thiserror", diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5baff5336..4850ee401 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1028,7 +1028,7 @@ pub trait MachineOps { }; let pcidev = XhciPciDevice::new( - &device_cfg.id, + &device_cfg, devfn, parent_bus, self.get_sys_mem(), diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 902223126..a02ae5f5e 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -19,23 +19,60 @@ use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; #[derive(Debug)] pub struct XhciConfig { pub id: String, + // number of usb2.0 ports + pub p2: Option, + // number of usb3.0 ports + pub p3: Option, } impl XhciConfig { fn new() -> Self { - XhciConfig { id: String::new() } + XhciConfig { + id: String::new(), + p2: None, + p3: None, + } + } + + fn check_ports(&self) -> Result<()> { + if self.p2.is_some() && self.p2.unwrap() == 0 { + return Err(anyhow!(ConfigError::IllegalValue( + "usb port2 number".to_string(), + 0, + true, + u8::MAX as u64, + false, + ))); + } + if self.p3.is_some() && self.p3.unwrap() == 0 { + return Err(anyhow!(ConfigError::IllegalValue( + "usb port3 number".to_string(), + 0, + true, + u8::MAX as u64, + false + ))); + } + Ok(()) } } impl ConfigCheck for XhciConfig { fn check(&self) -> Result<()> { - check_id(&self.id) + check_id(&self.id)?; + self.check_ports() } } pub fn parse_xhci(conf: &str) -> Result { let mut cmd_parser = CmdParser::new("nec-usb-xhci"); - cmd_parser.push("").push("id").push("bus").push("addr"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("addr") + .push("p2") + .push("p3"); cmd_parser.parse(conf)?; let mut dev = XhciConfig::new(); if let Some(id) = cmd_parser.get_value::("id")? { @@ -44,6 +81,14 @@ pub fn parse_xhci(conf: &str) -> Result { bail!("id is none for usb xhci"); } + if let Some(p2) = cmd_parser.get_value::("p2")? { + dev.p2 = Some(p2); + } + + if let Some(p3) = cmd_parser.get_value::("p3")? { + dev.p3 = Some(p3); + } + dev.check()?; Ok(dev) } diff --git a/usb/Cargo.toml b/usb/Cargo.toml index d35f16a06..a338b2c71 100644 --- a/usb/Cargo.toml +++ b/usb/Cargo.toml @@ -16,3 +16,5 @@ once_cell = "1.9.0" address_space = { path = "../address_space" } util = { path = "../util" } pci = { path = "../pci" } +machine_manager = { path = "../machine_manager" } + diff --git a/usb/src/usb.rs b/usb/src/usb.rs index ba9af6882..9755d7cf4 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -141,11 +141,11 @@ pub struct UsbPort { pub dev: Option>>, pub speed_mask: u32, pub path: String, - pub index: u32, + pub index: u8, } impl UsbPort { - pub fn new(index: u32) -> Self { + pub fn new(index: u8) -> Self { Self { dev: None, speed_mask: 0, diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 2440a7f9b..bc0405b4d 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -17,6 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; +use machine_manager::config::XhciConfig; use util::num_ops::{read_u32, write_u64_low}; use crate::bus::UsbBus; @@ -74,8 +75,9 @@ const SLOT_CTX_PORT_NUMBER_SHIFT: u32 = 16; const ENDPOINT_ID_START: u32 = 1; const MAX_ENDPOINTS: u32 = 31; /// XHCI config -const XHCI_PORT2_NUM: u32 = 2; -const XHCI_PORT3_NUM: u32 = 2; +const XHCI_MAX_PORT2: u8 = 15; +const XHCI_MAX_PORT3: u8 = 15; +const XHCI_DEFAULT_PORT: u8 = 4; /// Slot Context. const SLOT_INPUT_CTX_OFFSET: u64 = 0x20; const SLOT_CTX_MAX_EXIT_LATENCY_MASK: u32 = 0xffff; @@ -378,8 +380,8 @@ trait DwordOrder: Default + Copy + Send + Sync { /// Xhci controller device. pub struct XhciDevice { - pub numports_2: u32, - pub numports_3: u32, + pub numports_2: u8, + pub numports_3: u8, pub oper: XchiOperReg, pub usb_ports: Vec>>, pub ports: Vec>>, @@ -392,13 +394,27 @@ pub struct XhciDevice { } impl XhciDevice { - pub fn new(mem_space: &Arc) -> Arc> { + pub fn new(mem_space: &Arc, config: &XhciConfig) -> Arc> { + let mut p2 = XHCI_DEFAULT_PORT; + let mut p3 = XHCI_DEFAULT_PORT; + if config.p2.is_some() { + p2 = config.p2.unwrap(); + if p2 > XHCI_MAX_PORT2 { + p2 = XHCI_MAX_PORT2 + } + } + if config.p3.is_some() { + p3 = config.p3.unwrap(); + if p3 > XHCI_MAX_PORT3 { + p3 = XHCI_MAX_PORT3; + } + } let xhci = XhciDevice { oper: XchiOperReg::new(), ctrl_ops: None, usb_ports: Vec::new(), - numports_2: XHCI_PORT2_NUM, - numports_3: XHCI_PORT3_NUM, + numports_3: p3, + numports_2: p2, ports: Vec::new(), slots: vec![XhciSlot::new(mem_space); MAX_SLOTS as usize], intrs: vec![XhciInterrupter::new(mem_space); MAX_INTRS as usize], @@ -410,7 +426,7 @@ impl XhciDevice { let clone_xhci = xhci.clone(); let mut locked_xhci = clone_xhci.lock().unwrap(); locked_xhci.oper.usb_status = USB_STS_HCH; - for i in 0..(XHCI_PORT2_NUM + XHCI_PORT3_NUM) { + for i in 0..(p2 + p3) { locked_xhci.ports.push(Arc::new(Mutex::new(XhciPort::new( &Arc::downgrade(&clone_xhci), format!("xhci-port-{}", i), @@ -531,7 +547,7 @@ impl XhciDevice { return Ok(()); } let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); - evt.ptr = (locked_port.port_idx << PORT_EVENT_ID_SHIFT) as u64; + evt.ptr = ((locked_port.port_idx as u32) << PORT_EVENT_ID_SHIFT) as u64; self.send_event(&evt, 0)?; Ok(()) } @@ -584,8 +600,8 @@ impl XhciDevice { fn lookup_usb_port(&mut self, slot_ctx: &XhciSlotCtx) -> Option>> { let mut path = String::new(); - let mut port = slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff; - if port < 1 || port > self.ports.len() as u32 { + let mut port = (slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff) as u8; + if port < 1 || port > self.ports.len() as u8 { error!("Invalid port: {}", port); return None; } @@ -593,7 +609,7 @@ impl XhciDevice { port = usb_port.lock().unwrap().index + 1; path += &format!("{}", port); for i in 0..5 { - port = (slot_ctx.dev_info >> (4 * i)) & 0x0f; + port = ((slot_ctx.dev_info >> (4 * i)) & 0x0f) as u8; if port == 0 { break; } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 9239c8f97..aabbe3a8b 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -15,6 +15,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; +use machine_manager::config::XhciConfig; use pci::config::{ PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, @@ -75,7 +76,7 @@ pub struct XhciPciDevice { impl XhciPciDevice { pub fn new( - name: &str, + config: &XhciConfig, devfn: u8, parent_bus: Weak>, mem_space: &Arc, @@ -84,9 +85,9 @@ impl XhciPciDevice { Self { pci_config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, - xhci: XhciDevice::new(mem_space), + xhci: XhciDevice::new(mem_space, config), dev_id: Arc::new(AtomicU16::new(0)), - name: name.to_string(), + name: config.id.to_string(), parent_bus, mem_region: Region::init_container_region(XHCI_PCI_CONFIG_LENGTH as u64), bus_device, diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index fcabb7517..0e4a90981 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -233,14 +233,14 @@ pub struct XhciPort { /// Port Status and Control pub portsc: u32, /// Port ID - pub port_idx: u32, + pub port_idx: u8, pub usb_port: Option>>, pub speed_mask: u32, pub name: String, } impl XhciPort { - pub fn new(xhci: &Weak>, name: String, i: u32) -> Self { + pub fn new(xhci: &Weak>, name: String, i: u8) -> Self { Self { xhci: xhci.clone(), portsc: 0, @@ -266,7 +266,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_VERSION << hci_version_offset | XHCI_CAP_LENGTH } XHCI_CAP_REG_HCSPARAMS1 => { - max_ports << CAP_HCSP_NP_SHIFT + (max_ports as u32) << CAP_HCSP_NP_SHIFT | max_intrs << CAP_HCSP_NI_SHIFT | (locked_dev.slots.len() as u32) << CAP_HCSP_NDS_SHIFT } @@ -288,7 +288,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { | CAP_EXT_CAP_ID_SUPPORT_PROTOCOL as u32 } 0x24 => CAP_EXT_USB_NAME_STRING, - 0x28 => (locked_dev.numports_2 << 8) | 1, + 0x28 => ((locked_dev.numports_2 as u32) << 8) | 1, 0x2c => 0x0, // Extended capabilities (USB 3.0) 0x30 => { @@ -296,7 +296,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { | CAP_EXT_CAP_ID_SUPPORT_PROTOCOL as u32 } 0x34 => CAP_EXT_USB_NAME_STRING, - 0x38 => (locked_dev.numports_3 << 8) | (locked_dev.numports_2 + 1), + 0x38 => ((locked_dev.numports_3 as u32) << 8) | (locked_dev.numports_2 + 1) as u32, 0x3c => 0x0, _ => { error!("Failed to read xhci cap: not implemented"); -- Gitee From d739053024a34a204441aae000eeb341a92e55c8 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 16 Nov 2022 22:09:40 +0800 Subject: [PATCH 0452/2187] xhci: impl reset for xhci pci device Impl reset for xhci pci device. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_pci.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index aabbe3a8b..217c166cc 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -290,6 +290,7 @@ impl PciDevOps for XhciPciDevice { } fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { + self.xhci.lock().unwrap().reset(); Ok(()) } } -- Gitee From b267668a3164ac353729080ca7179dc207f0dd9d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 11:18:21 +0800 Subject: [PATCH 0453/2187] xhci: ensure device existed before reset port Add usb device reset in port reset, and ensure device existed before reset. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index bc0405b4d..cdba816f9 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -501,17 +501,17 @@ impl XhciDevice { /// Reset xhci port. pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { let mut locked_port = xhci_port.lock().unwrap(); - if let Some(usb_port) = locked_port.usb_port.clone() { - let locked_usb_port = usb_port.upgrade().unwrap(); - let speed = locked_usb_port - .lock() - .unwrap() - .dev - .as_ref() - .unwrap() - .lock() - .unwrap() - .speed(); + if let Some(usb_port) = locked_port.usb_port.as_ref() { + let upg_usb_port = usb_port.upgrade().unwrap(); + let locked_usb_port = upg_usb_port.lock().unwrap(); + let usb_dev = if let Some(dev) = locked_usb_port.dev.as_ref() { + dev + } else { + // No device, no need to reset. + return Ok(()); + }; + usb_dev.lock().unwrap().reset(); + let speed = usb_dev.lock().unwrap().speed(); if speed == USB_SPEED_SUPER && warm_reset { locked_port.portsc |= PORTSC_WRC; } -- Gitee From fa9abf0c6d4eefde2a476219f3571e5e8002d186 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 11:35:17 +0800 Subject: [PATCH 0454/2187] xhci: use get/set method for the port link state The get/set PLS(port link state) method is provided to replace the get/set field. Signed-off-by: zhouli57 --- usb/src/usb.rs | 14 +++----------- usb/src/xhci/xhci_controller.rs | 20 ++------------------ usb/src/xhci/xhci_regs.rs | 21 ++++++++++++++++----- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 9755d7cf4..eca2f2aee 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -15,15 +15,12 @@ use std::{ sync::{Arc, Mutex, Weak}, }; +use crate::config::*; use crate::descriptor::{ UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use crate::xhci::xhci_controller::XhciDevice; -use crate::{ - config::*, - xhci::xhci_controller::{get_field, set_field}, -}; use anyhow::{bail, Result}; const USB_MAX_ENDPOINTS: u32 = 15; @@ -659,14 +656,9 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { }; if wakeup { let mut locked_port = xhci_port.lock().unwrap(); - let port_status = get_field(locked_port.portsc, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + let port_status = locked_port.get_port_link_state(); if port_status == PLS_U3 { - locked_port.portsc = set_field( - locked_port.portsc, - PLS_RESUME, - PORTSC_PLS_MASK, - PORTSC_PLS_SHIFT, - ); + locked_port.set_port_link_state(PLS_RESUME); debug!( "Update portsc when notify controller, port {} status {}", locked_port.portsc, port_status diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index cdba816f9..4662f5114 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -517,12 +517,7 @@ impl XhciDevice { } match speed { USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH | USB_SPEED_SUPER => { - locked_port.portsc = set_field( - locked_port.portsc, - PLS_U0, - PORTSC_PLS_MASK, - PORTSC_PLS_SHIFT, - ); + locked_port.set_port_link_state(PLS_U0); locked_port.portsc |= PORTSC_PED; } _ => { @@ -573,7 +568,7 @@ impl XhciDevice { } } } - locked_port.portsc = set_field(locked_port.portsc, pls, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + locked_port.set_port_link_state(pls); debug!( "xhci port update portsc {:x} pls {:x}", locked_port.portsc, pls @@ -1753,14 +1748,3 @@ fn dma_write_u32(addr_space: &Arc, addr: GuestAddress, buf: &[u32] fn addr64_from_u32(low: u32, high: u32) -> u64 { (((high << 16) as u64) << 16) | low as u64 } - -pub fn get_field(val: u32, mask: u32, shift: u32) -> u32 { - val >> shift & mask -} - -pub fn set_field(val: u32, new_val: u32, mask: u32, shift: u32) -> u32 { - let mut tmp = val; - tmp &= !(mask << shift); - tmp |= (new_val & mask) << shift; - tmp -} diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 0e4a90981..8b7abd046 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -20,7 +20,7 @@ use util::num_ops::{read_u32, write_u64_high, write_u64_low}; use crate::config::*; use crate::usb::UsbPort; -use crate::xhci::xhci_controller::{set_field, XhciDevice, XhciEvent}; +use crate::xhci::xhci_controller::{XhciDevice, XhciEvent}; use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; use anyhow::{bail, Result}; @@ -250,6 +250,17 @@ impl XhciPort { name, } } + + /// Get port link state from port status and control register. + pub fn get_port_link_state(&self) -> u32 { + self.portsc >> PORTSC_PLS_SHIFT & PORTSC_PLS_MASK + } + + /// Set port link state in port status and control register. + pub fn set_port_link_state(&mut self, pls: u32) { + self.portsc &= !(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); + self.portsc |= (pls & PORTSC_PLS_MASK) << PORTSC_PLS_SHIFT; + } } /// Build capability region ops. @@ -726,7 +737,7 @@ fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { if value & PORTSC_LWS == PORTSC_LWS { let old_pls = (locked_port.portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; let new_pls = (value >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; - notify = xhci_portsc_ls_write(&mut portsc, old_pls, new_pls); + notify = xhci_portsc_ls_write(&mut locked_port, old_pls, new_pls); } portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); @@ -738,17 +749,17 @@ fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { Ok(()) } -fn xhci_portsc_ls_write(portsc: &mut u32, old_pls: u32, new_pls: u32) -> u32 { +fn xhci_portsc_ls_write(port: &mut XhciPort, old_pls: u32, new_pls: u32) -> u32 { match new_pls { PLS_U0 => { if old_pls != PLS_U0 { - *portsc = set_field(*portsc, new_pls, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + port.set_port_link_state(new_pls); return PORTSC_PLC; } } PLS_U3 => { if old_pls < PLS_U3 { - *portsc = set_field(*portsc, new_pls, PORTSC_PLS_MASK, PORTSC_PLS_SHIFT); + port.set_port_link_state(new_pls); } } PLS_RESUME => {} -- Gitee From 2f98b57520adaf91fa409087d40f2c6a30338214 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 17:16:43 +0800 Subject: [PATCH 0455/2187] usb: reset the keyboard and pointer in hid reset Reset keyboard and pointer in hid reset. And set the hid kind when new hid device. Move the increate queue head function into hid. Signed-off-by: zhouli57 --- usb/src/hid.rs | 49 +++++++++++++++++++++++++++------------------ usb/src/keyboard.rs | 5 +---- usb/src/tablet.rs | 5 +---- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 1065b5e08..3b6a08047 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -198,6 +198,13 @@ impl HidKeyboard { key_num: 0, } } + + fn reset(&mut self) { + self.keycodes.iter_mut().for_each(|x| *x = 0); + self.modifiers = 0; + self.key_buf.iter_mut().for_each(|x| *x = 0); + self.key_num = 0; + } } /// HID pointer event including position and button state. @@ -223,6 +230,12 @@ impl HidPointer { queue: [HidPointerEvent::default(); QUEUE_LENGTH as usize], } } + + fn reset(&mut self) { + self.queue + .iter_mut() + .for_each(|x| *x = HidPointerEvent::default()); + } } /// Human Interface Device. @@ -237,11 +250,11 @@ pub struct Hid { } impl Hid { - pub fn new() -> Self { + pub fn new(kind: HidType) -> Self { Hid { head: 0, num: 0, - kind: HidType::UnKnown, + kind, protocol: 0, idle: 0, keyboard: HidKeyboard::new(), @@ -254,6 +267,8 @@ impl Hid { self.num = 0; self.protocol = HID_PROTOCOL_REPORT; self.idle = 0; + self.keyboard.reset(); + self.pointer.reset(); } fn convert_to_hid_code(&mut self) { @@ -261,7 +276,7 @@ impl Hid { return; } let slot = self.head & QUEUE_MASK; - increase_queue(&mut self.head); + self.increase_head(); self.num -= 1; let keycode = self.keyboard.keycodes[slot as usize]; let key = keycode & 0x7f; @@ -343,13 +358,13 @@ impl Hid { } else { self.head - 1 }; - let evt = &mut self.pointer.queue[(index & QUEUE_MASK) as usize]; - let z = evt.pos_z; - evt.pos_z = 0; if self.num != 0 { - increase_queue(&mut self.head); + self.increase_head(); self.num -= 1; } + let evt = &mut self.pointer.queue[(index & QUEUE_MASK) as usize]; + let z = evt.pos_z; + evt.pos_z = 0; vec![ evt.button_state as u8, evt.pos_x as u8, @@ -360,6 +375,14 @@ impl Hid { ] } + fn increase_head(&mut self) { + if self.head + 1 >= QUEUE_LENGTH { + self.head = 0; + } else { + self.head += 1; + } + } + /// USB HID device handle control packet. pub fn handle_control_packet( &mut self, @@ -527,12 +550,6 @@ impl Hid { } } -impl Default for Hid { - fn default() -> Self { - Self::new() - } -} - impl Display for Hid { fn fmt(&self, f: &mut Formatter) -> FmtResult { write!( @@ -542,9 +559,3 @@ impl Display for Hid { ) } } - -fn increase_queue(head: &mut u32) { - let mut i = *head + 1; - i &= QUEUE_MASK; - *head = i; -} diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 9e76a30fc..7de29e783 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -124,7 +124,7 @@ impl UsbKeyboard { Self { id, device: Arc::new(Mutex::new(UsbDevice::new())), - hid: Arc::new(Mutex::new(Hid::new())), + hid: Arc::new(Mutex::new(Hid::new(HidType::Keyboard))), ctrl: None, endpoint: None, } @@ -147,9 +147,6 @@ impl UsbKeyboard { fn init_hid(&mut self) -> Result<()> { let mut locked_usb = self.device.lock().unwrap(); locked_usb.usb_desc = Some(DESC_KEYBOARD.clone()); - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.kind = HidType::Keyboard; - drop(locked_hid); let ep = locked_usb.get_endpoint(USB_TOKEN_IN as u32, 1); self.endpoint = Some(Arc::downgrade(&ep)); locked_usb.init_descriptor()?; diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 2371e669b..dbfaddaa0 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -129,7 +129,7 @@ impl UsbTablet { Self { id, device: Arc::new(Mutex::new(UsbDevice::new())), - hid: Arc::new(Mutex::new(Hid::new())), + hid: Arc::new(Mutex::new(Hid::new(HidType::Tablet))), ctrl: None, endpoint: None, } @@ -152,9 +152,6 @@ impl UsbTablet { fn init_hid(&mut self) -> Result<()> { let mut locked_usb = self.device.lock().unwrap(); locked_usb.usb_desc = Some(DESC_TABLET.clone()); - let mut hid = self.hid.lock().unwrap(); - hid.kind = HidType::Tablet; - drop(hid); let ep = locked_usb.get_endpoint(USB_TOKEN_IN as u32, 1); self.endpoint = Some(Arc::downgrade(&ep)); locked_usb.init_descriptor()?; -- Gitee From 34a1c816c6ef5d62bb55b89aad56c4d216803480 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 16 Nov 2022 18:27:47 +0800 Subject: [PATCH 0456/2187] pci: reset common regs for root port reset common regs during root port reset. Signed-off-by: Zhang Bo --- pci/src/config.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++ pci/src/root_port.rs | 24 ++++++--------------- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 9706d8fda..d8b718b6e 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -117,6 +117,8 @@ const BRIDGE_CTL_DISCARD_TIMER_SERR_E: u16 = 0x0800; pub const COMMAND_BUS_MASTER: u16 = 0x0004; const COMMAND_SERR_ENABLE: u16 = 0x0100; +#[cfg(test)] +const COMMAND_FAST_BACK: u16 = 0x0200; pub const COMMAND_INTERRUPT_DISABLE: u16 = 0x0400; const STATUS_PARITY_ERROR: u16 = 0x0100; @@ -513,6 +515,37 @@ impl PciConfig { } } + /// Reset type1 specific configuration space. + pub fn reset_bridge_regs(&mut self) -> Result<()> { + le_write_u32(&mut self.config, PRIMARY_BUS_NUM as usize, 0)?; + + self.config[IO_BASE as usize] = 0xff; + self.config[IO_LIMIT as usize] = 0; + // set memory/pref memory's base to 0xFFFF and limit to 0. + le_write_u32(&mut self.config, MEMORY_BASE as usize, 0xffff)?; + le_write_u32(&mut self.config, PREF_MEMORY_BASE as usize, 0xffff)?; + le_write_u64(&mut self.config, PREF_MEM_BASE_UPPER as usize, 0)?; + Ok(()) + } + + fn reset_single_writable_reg(&mut self, offset: usize) -> Result<()> { + let writable_command = le_read_u16(&self.write_mask, offset).unwrap() + | le_read_u16(&self.write_clear_mask, offset).unwrap(); + let old_command = le_read_u16(&self.config, offset).unwrap(); + + le_write_u16(&mut self.config, offset, old_command & !writable_command) + } + + /// Reset bits that's writable in the common configuration fields for both type0 and type1 devices. + pub fn reset_common_regs(&mut self) -> Result<()> { + self.reset_single_writable_reg(COMMAND as usize)?; + self.reset_single_writable_reg(STATUS as usize)?; + self.reset_single_writable_reg(INTERRUPT_LINE as usize)?; + self.config[CACHE_LINE_SIZE as usize] = 0; + + Ok(()) + } + /// Get base offset of the capability in PCIe/PCI configuration space. /// /// # Arguments @@ -1202,6 +1235,24 @@ mod tests { assert_eq!(size2, 0x40); } + #[test] + fn test_reset_common_regs() { + let mut pcie_config = PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 3); + pcie_config.init_common_write_mask().unwrap(); + pcie_config.init_common_write_clear_mask().unwrap(); + + le_write_u16( + &mut pcie_config.config, + COMMAND as usize, + COMMAND_MEMORY_SPACE | COMMAND_FAST_BACK, + ) + .unwrap(); + assert!(pcie_config.reset_common_regs().is_ok()); + + let res = le_read_u16(&mut pcie_config.config, COMMAND as usize).unwrap(); + assert_eq!(res, COMMAND_FAST_BACK); + } + #[test] fn test_unregister_bars() { let read_ops = move |_data: &mut [u8], _addr: GuestAddress, _offset: u64| -> bool { true }; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 578087feb..75608081b 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -27,19 +27,18 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, - COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, IO_LIMIT, - MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, + COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, + PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, - PREF_MEMORY_LIMIT, PREF_MEM_BASE_UPPER, PREF_MEM_RANGE_64BIT, PRIMARY_BUS_NUM, REG_SIZE, - SUB_CLASS_CODE, VENDOR_ID, + PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; use crate::hotplug::HotplugOps; use crate::msix::init_msix; -use crate::{init_multifunction, le_write_u32, le_write_u64, PciError}; +use crate::{init_multifunction, PciError}; use crate::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, PciDevOps, @@ -263,18 +262,6 @@ impl RootPort { error!("Failed to set fast unplug feature: {}", v); } } - - fn reset_bridge_regs(&mut self) -> Result<()> { - le_write_u32(&mut self.config.config, PRIMARY_BUS_NUM as usize, 0)?; - - self.config.config[IO_BASE as usize] = 0xff; - self.config.config[IO_LIMIT as usize] = 0; - // set memory/pref memory's base to 0xFFFF and limit to 0. - le_write_u32(&mut self.config.config, MEMORY_BASE as usize, 0xffff)?; - le_write_u32(&mut self.config.config, PREF_MEMORY_BASE as usize, 0xffff)?; - le_write_u64(&mut self.config.config, PREF_MEM_BASE_UPPER as usize, 0)?; - Ok(()) - } } impl PciDevOps for RootPort { @@ -447,7 +434,8 @@ impl PciDevOps for RootPort { PCI_EXP_LNKSTA_DLLLA, )?; } - self.reset_bridge_regs() + self.config.reset_bridge_regs()?; + self.config.reset_common_regs() } fn get_dev_path(&self) -> Option { -- Gitee From ceb3ccd8d100bd8e88d5ea7b3f0ae20f99fd423f Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 09:27:45 +0800 Subject: [PATCH 0457/2187] Fix: Add reasonable comments when handling the restore save command Currently, a flexible way is used to restore and save commands. Only the error flag is cleared when the save command is processed. When processing the restore command, an error is reported, causing the guest OS to reinitialize the XHCI. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_regs.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 0e4a90981..056a5bfbf 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -385,10 +385,11 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { { locked_xhci.stop(); } - // Not support Save/Restore, report the Save/Restore Error. if value & USB_CMD_CSS == USB_CMD_CSS { locked_xhci.oper.usb_status &= !USB_STS_SRE; } + // When the restore command is issued, an error is reported and then + // guest OS performs a complete initialization. if value & USB_CMD_CRS == USB_CMD_CRS { locked_xhci.oper.usb_status |= USB_STS_SRE; } -- Gitee From f0879d873ca470caf4ff01b25e3bca1bbc4c61bd Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 10:21:57 +0800 Subject: [PATCH 0458/2187] XHCI: change divisioon to right shift Shift right is better than division, so change division to the power of 2 to shift right. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_regs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 056a5bfbf..1d23ec2a8 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -98,6 +98,7 @@ const XHCI_INTR_REG_ERSTBA_HI: u64 = 0x14; const XHCI_INTR_REG_ERDP_LO: u64 = 0x18; const XHCI_INTR_REG_ERDP_HI: u64 = 0x1c; const XHCI_INTR_REG_SIZE: u64 = 0x20; +const XHCI_INTR_REG_SHIFT: u64 = 5; /// Doorbell Register Bit Field. /// DB Target. const DB_TARGET_MASK: u32 = 0xff; @@ -479,7 +480,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { error!("Failed to read runtime registers, offset is {:x}", offset); } } else { - let idx = ((offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE) as usize; + let idx = ((offset - XHCI_INTR_REG_SIZE) >> XHCI_INTR_REG_SHIFT) as usize; let mut xhci = xhci.lock().unwrap(); if idx >= xhci.intrs.len() { error!("Invalid interrupter index: {} idx {}", offset, idx); @@ -525,7 +526,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { return false; } let mut xhci = xhci.lock().unwrap(); - let idx = ((offset - XHCI_INTR_REG_SIZE) / XHCI_INTR_REG_SIZE) as u32; + let idx = ((offset - XHCI_INTR_REG_SIZE) >> XHCI_INTR_REG_SHIFT) as u32; if idx >= xhci.intrs.len() as u32 { error!("Invalid interrupter index: {} idx {}", offset, idx); return false; -- Gitee From 83e71f20e5f75036042204f84cffea83789f9d7c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 11:29:49 +0800 Subject: [PATCH 0459/2187] XHCI: Obtain the slot id only when necessary. The slot id is not required for each cmd process. Therefore, the slot id is obtained when necessary to avoid overhead on other cmd process. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index bc0405b4d..126792759 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -622,14 +622,13 @@ impl XhciDevice { /// Control plane pub fn handle_command(&mut self) -> Result<()> { self.oper.start_cmd_ring(); - let mut slot_id: u32; + let mut slot_id: u32 = 0; let mut event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::Success); for _ in 0..COMMAND_LIMIT { match self.cmd_ring.fetch_trb() { Ok(Some(trb)) => { let trb_type = trb.get_type(); event.ptr = trb.addr; - slot_id = self.get_slot_id(&mut event, &trb); info!("handle_command {:?} {:?}", trb_type, trb); match trb_type { TRBType::CrEnableSlot => { @@ -648,44 +647,52 @@ impl XhciDevice { } } TRBType::CrDisableSlot => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { event.ccode = self.disable_slot(slot_id)?; } } TRBType::CrAddressDevice => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { event.ccode = self.address_device(slot_id, &trb)?; } } TRBType::CrConfigureEndpoint => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { event.ccode = self.configure_endpoint(slot_id, &trb)?; } } TRBType::CrEvaluateContext => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { event.ccode = self.evaluate_context(slot_id, &trb)?; } } TRBType::CrStopEndpoint => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; event.ccode = self.stop_endpoint(slot_id, ep_id)?; } } TRBType::CrResetEndpoint => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; event.ccode = self.reset_endpoint(slot_id, ep_id)?; } } TRBType::CrSetTrDequeue => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; event.ccode = self.set_tr_dequeue_pointer(slot_id, ep_id, &trb)?; } } TRBType::CrResetDevice => { + slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { event.ccode = self.reset_device(slot_id)?; } -- Gitee From 9a563f6a5b194c89bbbaddb84d9224ba5740d060 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 14:25:49 +0800 Subject: [PATCH 0460/2187] XHCI: Eliminate the Devil's numbers The length of the Setup Stage TRB is always 8, and this field is in bits 0-15 of 08-0BH. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 16 +++++++++++----- usb/src/xhci/xhci_ring.rs | 4 ++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 126792759..61cddccb6 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -32,6 +32,9 @@ use crate::xhci::xhci_ring::{ }; use anyhow::{bail, Context, Result}; +use super::xhci_ring::SETUP_TRB_TR_LEN; +use super::xhci_ring::TRB_TR_LEN_MASK; + pub const MAX_INTRS: u16 = 16; pub const MAX_SLOTS: u32 = 64; /// Endpoint state @@ -1295,8 +1298,11 @@ impl XhciDevice { if trb_setup.control & TRB_TR_IDT != TRB_TR_IDT { bail!("no IDT bit"); } - if trb_setup.status & 0x1ffff != 8 { - bail!("Bad Setup TRB length {}", trb_setup.status & 0x1ffff); + if trb_setup.status & TRB_TR_LEN_MASK != SETUP_TRB_TR_LEN { + bail!( + "Bad Setup TRB length {}", + trb_setup.status & TRB_TR_LEN_MASK + ); } let bm_request_type = trb_setup.parameter as u8; @@ -1351,7 +1357,7 @@ impl XhciDevice { || trb_type == TRBType::TrNormal || trb_type == TRBType::TrIsoch { - let chunk = trb.status & 0x1ffff; + let chunk = trb.status & TRB_TR_LEN_MASK; let dma_addr = if trb.control & TRB_TR_IDT == TRB_TR_IDT { trb.addr } else { @@ -1441,7 +1447,7 @@ impl XhciDevice { for i in 0..xfer.td.len() { let trb = &xfer.td[i]; let trb_type = trb.get_type(); - let mut chunk = trb.status & 0x1ffff; + let mut chunk = trb.status & TRB_TR_LEN_MASK; match trb_type { TRBType::TrSetup => { if chunk > 8 { @@ -1499,7 +1505,7 @@ impl XhciDevice { let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); evt.slot_id = xfer.slotid as u8; evt.ep_id = xfer.epid as u8; - evt.length = (trb.status & 0x1ffff) - chunk; + evt.length = (trb.status & TRB_TR_LEN_MASK) - chunk; evt.flags = 0; evt.ptr = trb.addr; evt.ccode = if xfer.status == TRBCCode::Success { diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 30e45c57b..3eb6c62c8 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -37,6 +37,10 @@ pub const TRB_TR_CH: u32 = 1 << 4; pub const TRB_TR_IOC: u32 = 1 << 5; /// Immediate Data. pub const TRB_TR_IDT: u32 = 1 << 6; +/// TRB Transfer Length Mask +pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; +/// Setup Stage TRB Length always 8 +pub const SETUP_TRB_TR_LEN: u32 = 8; const TRB_LINK_LIMIT: u32 = 32; /// The max size of a ring segment in bytes is 64k. -- Gitee From 95d9100846ba746bb414783d8f3b542477634a0b Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 14:56:45 +0800 Subject: [PATCH 0461/2187] XHCI: Avoid the xfer packet iovecs is not released. There is no need to push the new vec loop to the old queue, and the ownership can be transferred directly. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 61cddccb6..6dc729e5f 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1371,9 +1371,7 @@ impl XhciDevice { } } xfer.packet.init(dir as u32, ep, 0, false, xfer.int_req); - for v in vec { - xfer.packet.iovecs.push(v); - } + xfer.packet.iovecs = vec; Ok(()) } -- Gitee From 0bf419510963b4004250bfd9c68c7ec9eb359577 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 17 Nov 2022 20:33:35 +0800 Subject: [PATCH 0462/2187] usb: check if the queue is full in keyboard Check if the queue is full before sending event in keyboard. Signed-off-by: zhouli57 --- usb/src/keyboard.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 7de29e783..3f226267b 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -20,7 +20,7 @@ use crate::descriptor::{ UsbInterfaceDescriptor, }; use crate::hid::{ - Hid, HidType, DESC_STRINGS, QUEUE_MASK, STR_CONFIG_KEYBOARD, STR_MANUFACTURER, + Hid, HidType, DESC_STRINGS, QUEUE_LENGTH, QUEUE_MASK, STR_CONFIG_KEYBOARD, STR_MANUFACTURER, STR_PRODUCT_KEYBOARD, STR_SERIAL_KEYBOARD, }; use crate::usb::{ @@ -158,6 +158,10 @@ impl UsbKeyboard { pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Result<()> { let locked_kbd = kbd.lock().unwrap(); let mut locked_hid = locked_kbd.hid.lock().unwrap(); + if scan_codes.len() as u32 + locked_hid.num > QUEUE_LENGTH { + debug!("Keyboard queue is full!"); + return Ok(()); + } for code in scan_codes { let index = ((locked_hid.head + locked_hid.num) & QUEUE_MASK) as usize; locked_hid.num += 1; -- Gitee From 239c3d52bc06889cd4eb9cfa53bcfd34cb898651 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 18 Nov 2022 09:40:01 +0800 Subject: [PATCH 0463/2187] usb check head when get index Check the head when get index in pointer_poll to avoid overflow. Signed-off-by: zhouli57 --- usb/src/hid.rs | 4 +++- usb/src/keyboard.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 3b6a08047..2eab64879 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -355,8 +355,10 @@ impl Hid { fn pointer_poll(&mut self) -> Vec { let index = if self.num > 0 { self.head - } else { + } else if self.head > 0 { self.head - 1 + } else { + QUEUE_LENGTH - 1 }; if self.num != 0 { self.increase_head(); diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 3f226267b..dc01f2288 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -160,6 +160,7 @@ pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Resu let mut locked_hid = locked_kbd.hid.lock().unwrap(); if scan_codes.len() as u32 + locked_hid.num > QUEUE_LENGTH { debug!("Keyboard queue is full!"); + // Return ok to ignore the request. return Ok(()); } for code in scan_codes { -- Gitee From 3eaf25eb16cf363fac1ca466049c8edf0caa8295 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 19 Nov 2022 18:16:14 +0800 Subject: [PATCH 0464/2187] usb: fix the confusing usb speed Fix the issue that the usb speed and port port speed are confused. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 4662f5114..b64e0d247 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -558,12 +558,18 @@ impl XhciDevice { if let Some(dev) = &locked_usb_port.dev { let speed = dev.lock().unwrap().speed(); locked_port.portsc |= PORTSC_CCS; - if speed == PORTSC_SPEED_SUPER { + if speed == USB_SPEED_SUPER { locked_port.portsc |= PORTSC_SPEED_SUPER; locked_port.portsc |= PORTSC_PED; pls = PLS_U0; - } else { - locked_port.portsc |= speed; + } else if speed == USB_SPEED_FULL { + locked_port.portsc |= PORTSC_SPEED_FULL; + pls = PLS_POLLING; + } else if speed == USB_SPEED_HIGH { + locked_port.portsc |= PORTSC_SPEED_HIGH; + pls = PLS_POLLING; + } else if speed == USB_SPEED_LOW { + locked_port.portsc |= PORTSC_SPEED_LOW; pls = PLS_POLLING; } } -- Gitee From 780efd2f1f7f5b963a31144864a0c7806b4e3ee4 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 02:07:36 -0500 Subject: [PATCH 0465/2187] virtiofsd: Resolved the bug of mounting the shared directory An endless loop occurs when mount the shared directory.We need to break out of the loop at elem.desc_num==0. Signed-off-by: Li HuaChao --- vhost_user_fs/src/virtio_fs.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 29fb791ee..f0a14495e 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -73,24 +73,30 @@ impl FsIoHandler { } fn process_queue(&mut self) -> Result<()> { - while let Ok(elem) = self - .queue - .vring - .pop_avail(&self.mem_space, self.driver_features) - { + loop { + let elem = self + .queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + .with_context(|| "Failed to pop avail ring for process virtiofs queue")?; + + if elem.desc_num == 0 { + break; + } + let mut req = FuseReq::new(&elem); let (index, len) = req.execute(&self.mem_space, self.fs.clone()); self.queue.vring.add_used(&self.mem_space, index, len)?; - } - if self - .queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { - self.call_evt - .write(1) - .with_context(|| "Failed to write call fd")?; + if self + .queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + self.call_evt + .write(1) + .with_context(|| "Failed to write call fd")?; + } } Ok(()) -- Gitee From 93cb64013919dcd55d655e5df9b80d9d1b4c0430 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 02:30:33 -0500 Subject: [PATCH 0466/2187] virtiofs: Set the default length of the queue on the device to 128 Set the default queue length on the device to 128 and the maximum queue length on the virtiofsd to 1024. Signed-off-by: Li HuaChao --- vhost_user_fs/src/virtio_fs.rs | 4 ++-- virtio/src/vhost/user/fs.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index f0a14495e..dbeae0d46 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -14,8 +14,8 @@ const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; /// The num of request queue. const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; -/// The next queue size. -const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 128; +/// The max queue size. +const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 56c5c2030..db0a21fd2 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -15,7 +15,7 @@ const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: usize = 1; // The num of request queue const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; // The size of queue for virtio fs -const VIRTIO_FS_QUEUE_SIZE: u16 = 1024; +const VIRTIO_FS_QUEUE_SIZE: u16 = 128; use crate::VirtioError; use std::cmp; -- Gitee From 27859d6a91081287059568200c9b08ea5718c936 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 22 Nov 2022 07:58:07 +0800 Subject: [PATCH 0467/2187] pci: validate offset and len before write to config Some callers forgot to check boundary when writing to config, add the validation at only one place that in the basic function in pci::config, to make sure that do not write accross over the config space's boundary, either write no more than 4B, according PCIe spec. Signed-off-by: Zhang Bo --- .../src/standard_vm/aarch64/pci_host_root.rs | 15 ------------ machine/src/standard_vm/x86_64/ich9_lpc.rs | 10 +------- machine/src/standard_vm/x86_64/mch.rs | 18 +------------- pci/src/config.rs | 24 +++++++++++++++++++ usb/src/xhci/xhci_pci.rs | 11 +-------- 5 files changed, 27 insertions(+), 51 deletions(-) diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 4d118c62a..3513562e4 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -99,21 +99,6 @@ impl PciDevOps for PciHostRoot { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let size = data.len(); - if size > 4 { - error!( - "Failed to write PciHostRoot config space: Invalid data size {}", - size - ); - return; - } - if offset + size > PCI_CONFIG_SPACE_SIZE { - debug!( - "Failed to write PciHostRoot config space: offset {}, size {}, config space size {}", - offset, size, PCI_CONFIG_SPACE_SIZE - ); - return; - } self.config.write(offset, data, 0); } diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index ae5fcb4c7..3dcd49cee 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -276,15 +276,7 @@ impl PciDevOps for LPCBridge { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let size = data.len(); - let end = offset + size; - if end > PCI_CONFIG_SPACE_SIZE || size > 4 { - debug!( - "Failed to write LPC bridge's pci config space: offset {}, data size {}", - offset, size - ); - return; - } + let end = offset + data.len(); self.config.write(offset, data, 0); if ranges_overlap( diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index db69c0c6c..6ee7f18c2 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -158,23 +158,7 @@ impl PciDevOps for Mch { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let size = data.len(); - let end = offset + size; - if size > 4 { - error!( - "Failed to write MCH config space: Invalid data size {}", - size - ); - return; - } - if offset + size > PCI_CONFIG_SPACE_SIZE { - debug!( - "Failed to write MCH config space: offset {}, size {}, config space size {}", - offset, size, PCI_CONFIG_SPACE_SIZE - ); - return; - } - + let end = offset + data.len(); self.config.write(offset, data, 0); if ranges_overlap(offset, end, PCIEXBAR as usize, PCIEXBAR as usize + 8) { if let Err(e) = self.update_pciexbar_mapping() { diff --git a/pci/src/config.rs b/pci/src/config.rs index 72ac8a519..7c5e02c48 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -488,6 +488,25 @@ impl PciConfig { buf[..].copy_from_slice(&self.config[offset..(offset + size)]); } + fn validate_config_boundary(&mut self, offset: usize, data: &[u8]) -> Result<()> { + if offset + data.len() > self.config.len() { + return Err(anyhow!(PciError::InvalidConf( + "config size".to_string(), + format!("offset {} with len {}", offset, data.len()) + ))); + } + + // According to pcie specification 7.2.2.2 PCI Express Device Requirements: + if data.len() > 4 { + return Err(anyhow!(PciError::InvalidConf( + "data size".to_string(), + format!("{}", data.len()) + ))); + } + + Ok(()) + } + /// Common writing to configuration space. /// /// # Arguments @@ -496,6 +515,10 @@ impl PciConfig { /// * `data` - Data to write. /// * `dev_id` - Device id to send MSI/MSI-X. pub fn write(&mut self, mut offset: usize, data: &[u8], dev_id: u16) { + if self.validate_config_boundary(offset, data).is_err() { + return; + } + let cloned_data = data.to_vec(); let old_offset = offset; for data in &cloned_data { @@ -504,6 +527,7 @@ impl PciConfig { self.config[offset] &= !(data & self.write_clear_mask[offset]); offset += 1; } + if let Some(msix) = &mut self.msix { msix.lock() .unwrap() diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 9239c8f97..61e73fb72 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -252,16 +252,7 @@ impl PciDevOps for XhciPciDevice { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let data_size = data.len(); - let end = offset + data_size; - if end > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { - error!( - "Failed to write pci config space at offset 0x{:x} with data size {}", - offset, data_size - ); - return; - } - + let end = offset + data.len(); self.pci_config .write(offset, data, self.dev_id.clone().load(Ordering::Acquire)); if ranges_overlap( -- Gitee From c47e782264881c5393114c4f8aeb6200e6483232 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 11:46:15 +0800 Subject: [PATCH 0468/2187] xhci: simplify the epctx in the kick endpoint Simplify the epctx in the kick_endpoint, replace the clone of epctx by the mut reference. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 81 +++++++++++++++------------------ 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 286a55446..bb3e73bf5 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1141,17 +1141,15 @@ impl XhciDevice { /// Data plane pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { - let ep_ctx = self.get_endpoint(slot_id, ep_id)?; + let epctx = self.get_endpoint(slot_id, ep_id)?; debug!( "kick_endpoint slotid {} epid {} dequeue {:x}", - slot_id, ep_id, ep_ctx.ring.dequeue + slot_id, ep_id, epctx.ring.dequeue ); - let mut epctx = ep_ctx.clone(); - if let Err(e) = self.endpoint_retry_transfer(&mut epctx) { - // Update the endpoint context in slot. - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; + if let Err(e) = self.endpoint_retry_transfer(slot_id, ep_id) { bail!("Failed to retry transfer {}", e); } + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if epctx.state == EP_HALTED { info!("xhci: endpoint halted"); return Ok(()); @@ -1163,6 +1161,7 @@ impl XhciDevice { let mut xfer: XhciTransfer = XhciTransfer::new(); xfer.slotid = slot_id; xfer.epid = ep_id; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; match epctx.ring.fetch_td() { Ok(Some(td)) => { debug!( @@ -1176,14 +1175,13 @@ impl XhciDevice { break; } Err(e) => { - // update the endpoint in slot - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; bail!("fetch ring failed {}", e); } } - if let Err(e) = self.endpoint_do_transfer(&mut xfer, &mut epctx) { + if let Err(e) = self.endpoint_do_transfer(&mut xfer) { error!("Failed to transfer {:?}", e); } + let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if xfer.complete { epctx.set_state(&self.mem_space, epctx.state)?; } else { @@ -1194,7 +1192,7 @@ impl XhciDevice { } // retry if !xfer.complete && xfer.running_retry { - epctx.retry = Some(xfer.clone()); + epctx.retry = Some(xfer); break; } count += 1; @@ -1203,8 +1201,6 @@ impl XhciDevice { break; } } - // Update the endpoint context in slot. - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] = epctx; Ok(()) } @@ -1230,19 +1226,25 @@ impl XhciDevice { Ok(ep_ctx) } - fn endpoint_retry_transfer(&mut self, epctx: &mut XhciEpContext) -> Result<()> { - if let Some(xfer) = &mut epctx.retry { - self.setup_usb_packet(xfer, epctx.epid)?; - self.device_handle_packet(&mut xfer.packet)?; - if xfer.packet.status == UsbPacketStatus::Nak { - bail!("USB packet status is NAK"); - } - self.complete_packet(xfer)?; - if xfer.complete { - epctx.set_state(&self.mem_space, epctx.state)?; - } - epctx.retry = None; + fn endpoint_retry_transfer(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { + let slot = &mut self.slots[(slot_id - 1) as usize]; + let mut xfer = if let Some(xfer) = &mut slot.endpoints[(ep_id - 1) as usize].retry { + xfer.clone() + } else { + // No need to retry. + return Ok(()); + }; + self.setup_usb_packet(&mut xfer)?; + self.device_handle_packet(&mut xfer.packet)?; + if xfer.packet.status == UsbPacketStatus::Nak { + bail!("USB packet status is NAK"); } + self.complete_packet(&mut xfer)?; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + if xfer.complete { + epctx.set_state(&self.mem_space, epctx.state)?; + } + epctx.retry = None; Ok(()) } @@ -1264,25 +1266,17 @@ impl XhciDevice { Ok(()) } - fn endpoint_do_transfer( - &mut self, - xfer: &mut XhciTransfer, - epctx: &mut XhciEpContext, - ) -> Result<()> { + fn endpoint_do_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { if xfer.epid == 1 { - self.do_ctrl_transfer(xfer, epctx)?; + self.do_ctrl_transfer(xfer)?; } else { - self.do_data_transfer(xfer, epctx)?; + self.do_data_transfer(xfer)?; } Ok(()) } /// Control Transfer, TRBs include Setup, Data(option), Status. - fn do_ctrl_transfer( - &mut self, - xfer: &mut XhciTransfer, - epctx: &mut XhciEpContext, - ) -> Result<()> { + fn do_ctrl_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { let trb_setup = xfer.td[0]; let mut trb_status = xfer.td[xfer.td.len() - 1]; let setup_type = trb_setup.get_type(); @@ -1309,18 +1303,15 @@ impl XhciDevice { let bm_request_type = trb_setup.parameter as u8; xfer.in_xfer = bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; - self.setup_usb_packet(xfer, epctx.epid)?; + self.setup_usb_packet(xfer)?; xfer.packet.parameter = trb_setup.parameter; self.device_handle_packet(&mut xfer.packet)?; self.complete_packet(xfer)?; Ok(()) } - fn do_data_transfer( - &mut self, - xfer: &mut XhciTransfer, - epctx: &mut XhciEpContext, - ) -> Result<()> { + fn do_data_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; xfer.in_xfer = epctx.ep_type == EpType::Control || epctx.ep_type == EpType::IsoIn || epctx.ep_type == EpType::BulkIn @@ -1328,18 +1319,18 @@ impl XhciDevice { if epctx.ep_type != EpType::IntrOut && epctx.ep_type != EpType::IntrIn { bail!("Unhandled ep_type {:?}", epctx.ep_type); } - self.setup_usb_packet(xfer, epctx.epid)?; + self.setup_usb_packet(xfer)?; self.device_handle_packet(&mut xfer.packet)?; self.complete_packet(xfer)?; Ok(()) } // Setup USB packet, include mapping dma address to iovector. - fn setup_usb_packet(&mut self, xfer: &mut XhciTransfer, epid: u32) -> Result<()> { + fn setup_usb_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { let ep = if let Some(ep) = &xfer.packet.ep { ep.clone() } else { - let ep = self.get_usb_ep(xfer.slotid, epid)?; + let ep = self.get_usb_ep(xfer.slotid, xfer.epid)?; Arc::downgrade(&ep) }; let dir = if xfer.in_xfer { -- Gitee From 3a1716f1b26be70bafbf82a4ceb168b4784abfde Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 20:22:41 +0800 Subject: [PATCH 0469/2187] xhci: don't handle error when fetch trb failed Generally, the fetch TRB fails in two cases. one is that DMA read/write fails, and the other is that the Link TRB exceeds the limit. If an error occurs during handle command processing, the error is returned to the upper layer for processing. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index bb3e73bf5..18975fd98 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -629,8 +629,8 @@ impl XhciDevice { let mut slot_id: u32 = 0; let mut event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::Success); for _ in 0..COMMAND_LIMIT { - match self.cmd_ring.fetch_trb() { - Ok(Some(trb)) => { + match self.cmd_ring.fetch_trb()? { + Some(trb) => { let trb_type = trb.get_type(); event.ptr = trb.addr; info!("handle_command {:?} {:?}", trb_type, trb); @@ -712,15 +712,10 @@ impl XhciDevice { event.slot_id = slot_id as u8; self.send_event(&event, 0)?; } - Ok(None) => { + None => { debug!("No TRB in the cmd ring."); break; } - Err(e) => { - error!("Failed to fetch ring: {:?}", e); - event.ccode = TRBCCode::TrbError; - break; - } } } Ok(()) -- Gitee From 8d200637b66c6eab0621a970f3e6b96d90c2b749 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 20:37:41 +0800 Subject: [PATCH 0470/2187] xhci: retry again if the packet is NAK Retry again if the packet is NAK instead of returning an error directly. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 18975fd98..21588effe 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1141,8 +1141,9 @@ impl XhciDevice { "kick_endpoint slotid {} epid {} dequeue {:x}", slot_id, ep_id, epctx.ring.dequeue ); - if let Err(e) = self.endpoint_retry_transfer(slot_id, ep_id) { - bail!("Failed to retry transfer {}", e); + if !self.endpoint_retry_transfer(slot_id, ep_id)? { + // Return directly to retry again at the next kick. + return Ok(()); } let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if epctx.state == EP_HALTED { @@ -1221,18 +1222,23 @@ impl XhciDevice { Ok(ep_ctx) } - fn endpoint_retry_transfer(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { + /// Return true if retry is done. + /// Return false if packet is need to retry again. + /// Return error if retry failed. + fn endpoint_retry_transfer(&mut self, slot_id: u32, ep_id: u32) -> Result { let slot = &mut self.slots[(slot_id - 1) as usize]; let mut xfer = if let Some(xfer) = &mut slot.endpoints[(ep_id - 1) as usize].retry { xfer.clone() } else { // No need to retry. - return Ok(()); + return Ok(true); }; self.setup_usb_packet(&mut xfer)?; self.device_handle_packet(&mut xfer.packet)?; if xfer.packet.status == UsbPacketStatus::Nak { - bail!("USB packet status is NAK"); + debug!("USB packet status is NAK"); + // NAK need to retry again. + return Ok(false); } self.complete_packet(&mut xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; @@ -1240,7 +1246,7 @@ impl XhciDevice { epctx.set_state(&self.mem_space, epctx.state)?; } epctx.retry = None; - Ok(()) + Ok(true) } fn device_handle_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { -- Gitee From 0c710dba86fe773a52f55b97d173bc454783b741 Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Wed, 12 Oct 2022 15:34:09 +0800 Subject: [PATCH 0471/2187] virtiofsd: add seccomp, sandbox mechanism to vhost_user_fs parse -seccomp from cmdline args enable seccomp mechanism to limit syscall parse -sandbox from cmdline args enable sandbox chroot/namespace to ensure security of vhost_user_fs process Signed-off-by: YuJun Huang --- Cargo.lock | 18 + ...Third_Party_Open_Source_Software_Notice.md | 12 + vhost_user_fs/Cargo.toml | 3 + vhost_user_fs/src/cmdline.rs | 49 ++- vhost_user_fs/src/fs.rs | 16 +- vhost_user_fs/src/lib.rs | 2 + vhost_user_fs/src/main.rs | 27 +- vhost_user_fs/src/sandbox.rs | 343 ++++++++++++++++++ vhost_user_fs/src/securecomputing.rs | 185 ++++++++++ vhost_user_fs/src/vhost_user_fs.rs | 10 +- vhost_user_fs/src/virtio_fs.rs | 9 +- 11 files changed, 653 insertions(+), 21 deletions(-) create mode 100644 vhost_user_fs/src/sandbox.rs create mode 100644 vhost_user_fs/src/securecomputing.rs diff --git a/Cargo.lock b/Cargo.lock index 41ecc506a..782d947f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,6 +129,16 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "capng" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f8e9448233603643e42606121d95f5f8d4e015b3e7619a51593864dd902575" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "cc" version = "1.0.73" @@ -335,6 +345,12 @@ version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +[[package]] +name = "libseccomp-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7cbbd4ad467251987c6e5b47d53b11a5a05add08f2447a9e2d70aef1e0d138" + [[package]] name = "lock_api" version = "0.4.9" @@ -883,10 +899,12 @@ dependencies = [ "acpi", "address_space", "anyhow", + "capng", "devices", "errno", "hypervisor", "libc", + "libseccomp-sys", "log", "machine_manager", "migration", diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 1625bbc1c..f4b0ab4b8 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -425,3 +425,15 @@ Copyright notice: Copyright (c) tokio-rs License: MIT OR Apache-2.0 Please see above. + +Software: libseccomp-sys 0.2.1 +Copyright notice: +Copyright (c) 2021 Sony Group Corporation +License: MIT OR Apache-2.0 +Please see above. + +Software: capng 0.2.2 +Copyright notice: +Copyright (C) 2020 Red Hat, Inc. All rights reserved. +License: Apache License Version 2.0 or BSD 3-Clause License +Please see above. diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 1016a5099..68b0ed6eb 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -7,11 +7,14 @@ license = "Mulan PSL v2" description = "Provide virtio fs for VM" [dependencies] +capng = "0.2.2" errno = "0.2.7" log = "0.4.8" libc = ">=0.2.71" thiserror = "1.0" anyhow = "1.0" +libseccomp-sys = "0.2.1" +error-chain = "0.12.4" vmm-sys-util = ">=0.7.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 4d58735ab..0259487f9 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -16,7 +16,11 @@ const MAX_PATH_LENGTH: usize = 4096; // Maximum length of the socket path is restricted by linux. const MAX_SOCK_PATH_LENGTH: usize = 108; +use crate::fs_ops::open; +use crate::fuse_msg::FUSE_OK; use anyhow::{bail, Context, Result}; +use std::ffi::CString; +use std::fs::File; use std::{fs, path::PathBuf}; use util::arg_parser::{Arg, ArgMatches, ArgParser}; @@ -57,10 +61,26 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_value(true) .can_no_value(true), ) + .arg( + Arg::with_name("seccomp") + .long("seccomp") + .value_name("limit syscall(allow, kill, log, trap)") + .help("-seccomp kill") + .takes_value(true) + .possible_values(vec!["allow", "kill", "log", "trap"]), + ) + .arg( + Arg::with_name("sandbox") + .long("sandbox") + .value_name("isolate the daemon process(chroot, namespace)") + .help("-sandbox namespace") + .takes_value(true) + .possible_values(vec!["namespace", "chroot"]), + ) } /// Filesystem configuration parsed from command line for the process. -#[derive(Clone, Default, Debug)] +#[derive(Debug)] pub struct FsConfig { /// Source directory in host which can be accessed by guest. pub source_dir: String, @@ -68,6 +88,22 @@ pub struct FsConfig { pub sock_path: String, /// The limit of file resources which can be opened for the process. pub rlimit_nofile: Option, + /// The path of root directory. + pub root_dir: String, + /// File object for /proc/self/fd. + pub proc_dir_opt: Option, +} + +impl Default for FsConfig { + fn default() -> Self { + FsConfig { + source_dir: String::from(""), + sock_path: String::from(""), + rlimit_nofile: None, + root_dir: String::from(""), + proc_dir_opt: None, + } + } } impl FsConfig { @@ -128,6 +164,17 @@ pub fn create_fs_config(args: &ArgMatches) -> Result { fs_config.rlimit_nofile = Some(limit); } + let (proc_dir_opt, ret) = open(CString::new("/proc/self/fd").unwrap(), libc::O_PATH); + if ret != FUSE_OK { + bail!("Failed to open proc dir"); + } + fs_config.proc_dir_opt = proc_dir_opt; + + fs_config.root_dir = fs_config.source_dir.clone(); + if args.value_of("sandbox").is_some() { + fs_config.root_dir = "/".to_string(); + } + fs_config .check_config() .with_context(|| "Precheck failed, Config is unhealthy, stop running")?; diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index b7c437de7..f0dc47307 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -21,6 +21,7 @@ const ROOT_INODE: usize = 1; use super::fs_ops::*; use super::fuse_msg::*; +use crate::cmdline::FsConfig; use anyhow::{bail, Context, Result}; use std::collections::{BTreeMap, HashMap}; use std::ffi::CString; @@ -315,10 +316,11 @@ impl FileSystem { /// # Arguments /// /// * `source_dir` - The path of the host directory which needs to be shared. - pub fn new(source_dir: &str) -> Result { - let (root_file_opt, ret) = open(CString::new(source_dir).unwrap(), libc::O_PATH); + pub fn new(fs_config: FsConfig) -> Result { + let root_dir = fs_config.root_dir.clone(); + let (root_file_opt, ret) = open(CString::new(root_dir).unwrap(), libc::O_PATH); if ret != FUSE_OK { - bail!("Failed to open root file {}", source_dir); + bail!("Failed to open root file {}", fs_config.root_dir); } let root_file = root_file_opt.unwrap(); let (stat, ret) = fstat_at( @@ -327,7 +329,7 @@ impl FileSystem { libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, ); if ret != FUSE_OK { - bail!("Failed to get stat of root file {}", source_dir); + bail!("Failed to get stat of root file {}", fs_config.root_dir); } let key = StatKey { ino: stat.st_ino, @@ -339,16 +341,12 @@ impl FileSystem { let root_inode = Inode::new(root_file, 2, root_id, libc::S_IFDIR, key); let mut inodes = BTreeMap::new(); inodes.insert(key, root_inode.clone()); - let (proc_dir_opt, ret) = open(CString::new("/proc/self/fd").unwrap(), libc::O_PATH); - if ret != FUSE_OK { - bail!("Failed to open proc dir"); - } Ok(FileSystem { root_inode, inodes, inode_key_map, file_map: Map::new(), - proc_dir: proc_dir_opt.unwrap(), + proc_dir: fs_config.proc_dir_opt.unwrap(), }) } diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index a26ce8cbd..e8ec9f670 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -23,6 +23,8 @@ pub mod fs_ops; pub mod fuse_msg; pub mod fuse_proc; pub mod fuse_req; +pub mod sandbox; +pub mod securecomputing; pub mod vhost_user_fs; pub mod vhost_user_server; pub mod virtio_fs; diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index cb0d6f158..a7cbc167d 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -15,12 +15,15 @@ extern crate log; extern crate vhost_user_fs; use anyhow::{Context, Result}; use machine_manager::event_loop::EventLoop; +use machine_manager::signal_handler; use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; use thiserror::Error; use util::{arg_parser, logger}; use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; +use vhost_user_fs::sandbox::Sandbox; +use vhost_user_fs::securecomputing::{seccomp_filter, string_to_seccompopt, SeccompOpt}; use vhost_user_fs::vhost_user_fs::VhostUserFs; #[derive(Error, Debug)] @@ -77,6 +80,7 @@ fn run() -> Result<()> { if let Some(logfile_path) = cmd_args.value_of("display log") { init_log(logfile_path)?; } + signal_handler::register_kill_signal(); set_panic_hook(); match real_main(&cmd_args) { Ok(()) => info!("EventLoop over, Vm exit"), @@ -89,9 +93,30 @@ fn run() -> Result<()> { } fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { - let fsconfig: FsConfig = create_fs_config(cmd_args)?; + let mut fsconfig: FsConfig = create_fs_config(cmd_args)?; info!("FsConfig is {:?}", fsconfig); + let source_dir = cmd_args.value_of("source dir").unwrap(); + let mut sandbox = Sandbox::new(source_dir); + if let Some(sandbox_value) = cmd_args.value_of("sandbox") { + match sandbox_value.as_str() { + "chroot" => sandbox.enable_chroot(), + "namespace" => sandbox.enable_namespace(), + _ => Ok(()), + }?; + }; + if sandbox.proc_self_fd.is_some() { + fsconfig.proc_dir_opt = sandbox.proc_self_fd; + } + + if let Some(seccomp) = cmd_args.value_of("seccomp") { + let seccomp_opt = string_to_seccompopt(seccomp); + match seccomp_opt { + SeccompOpt::Allow => {} + _ => seccomp_filter(seccomp_opt).unwrap(), + } + } + EventLoop::object_init(&None)?; let vhost_user_fs = Arc::new(Mutex::new( diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs new file mode 100644 index 000000000..8b5bf09fc --- /dev/null +++ b/vhost_user_fs/src/sandbox.rs @@ -0,0 +1,343 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Result}; +use std::ffi::CString; +use std::fs; +use std::fs::File; +use std::os::unix::io::FromRawFd; + +/// Sandbox mechanism to isolate process. +pub struct Sandbox { + /// Source directory in host which can be accessed by guest. + pub source_dir: String, + /// File object for /proc/self/fd. + pub proc_self_fd: Option, +} + +impl Sandbox { + pub fn new(source_dir: String) -> Self { + Sandbox { + source_dir, + proc_self_fd: None, + } + } + + /// In "chroot" sandbox mode. + /// The program invokes chroot(2) to make the shared directory tree its root. + pub fn enable_chroot(&mut self) -> Result<()> { + if unsafe { libc::geteuid() } != 0 { + bail!("chroot/setgroups must be privileged user"); + } + + let cstr = CString::new("/proc/self/fd").unwrap(); + let open_ans = unsafe { libc::open(cstr.as_ptr(), libc::O_PATH) }; + if open_ans == -1 { + bail!("open /proc/self/fd failed"); + } + self.proc_self_fd = Some(unsafe { File::from_raw_fd(open_ans) }); + + drop_groups()?; + + let source_dir = CString::new(self.source_dir.clone()).unwrap(); + if unsafe { libc::chroot(source_dir.as_ptr()) } == -1 { + bail!("change root fail"); + } + let root_dir = CString::new("/").unwrap(); + if unsafe { libc::chdir(root_dir.as_ptr()) } == -1 { + bail!("change root directory fail"); + } + Ok(()) + } + + /// In "namespace" sandbox mode. + /// The program switches into a new file system namespace and invokes pivot_root(2) to make the shared directory tree its root. + pub fn enable_namespace(&mut self) -> Result<()> { + let mut flags = libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::CLONE_NEWNET; + let euid = unsafe { libc::geteuid() }; + let egid = unsafe { libc::getegid() }; + if euid == 0 { + // An unprivileged user do not have permission to call setgroups. + drop_groups()?; + } else { + flags |= libc::CLONE_NEWUSER; + } + + if unsafe { libc::unshare(flags) } == -1 { + bail!("unshare fail"); + } + let pid = unsafe { libc::getpid() }; + // Get parent's pid and wrap it in file Object to ensure that is auto-closed. + let pidfd_open = unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) as libc::c_int }; + if pidfd_open == -1 { + bail!("pidfd_open fail"); + } + struct PidFd(File); + let _pidfd = unsafe { PidFd(File::from_raw_fd(pidfd_open)) }; + + let fork_ans = unsafe { libc::fork() }; + match fork_ans { + -1 => bail!("fork fail"), + 0 => self.do_namespace_in_child_process(euid, egid, pidfd_open)?, + _ => self.parent_process_wait_child(fork_ans)?, + } + Ok(()) + } + + fn do_namespace_in_child_process( + &mut self, + euid: u32, + egid: u32, + pidfd_open: i32, + ) -> Result<()> { + // If vhost_user_fs/src/set_signal_handlers do not register SIGTERM. + // Child process became orphan process when parent process died. + // Beacuse child process can not receive signal notification from parent process. + // This is the signal that the calling process will get when its parent died. + if unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) } == -1 { + bail!("prctl fail"); + } + // The parent maybe died before before prctl. + let mut pollfd = libc::pollfd { + fd: pidfd_open, + events: libc::POLLIN, + revents: 0, + }; + let poll_ans = unsafe { libc::poll(&mut pollfd, 1, 0) }; + if poll_ans == -1 { + bail!("pollfd fail"); + } else if poll_ans != 0 { + bail!("original parent process died"); + } + // An unprivileged user set uid/gid mapping in user namespace. + if euid != 0 { + self.id_mapping(euid, egid); + } + // Open fd to '/proc/self' so we can later open '/proc/self/mountinfo'. + let cstr = CString::new("/proc/self").unwrap(); + let open_fd = unsafe { libc::open(cstr.as_ptr(), libc::O_PATH) }; + if open_fd < 0 { + bail!("open /proc/self fail"); + } + // Changing into file object to ensure it will closed when this function returns. + let _open_fd_file = unsafe { File::from_raw_fd(open_fd) }; + // Ensure changes in child mount namespace do not affect parent mount namespace. + self.change_propagation()?; + // mount /proc in this context. + self.mount_proc()?; + // Bind-mount '/proc/self/fd' onto '/proc' to preventing access to ancestor directories. + self.proc_self_fd_bind_proc()?; + // Bind-mount 'source_dir' on itself so we can use as new root on 'pivot_root'. + self.bind_source_dir()?; + // Get a fd to old root. + let cstr = CString::new("/").unwrap(); + let root_dir_fd = unsafe { + libc::open( + cstr.as_ptr(), + libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC, + ) + }; + if root_dir_fd < 0 { + bail!("open root_dir fail"); + } + // Get a fd to new root. + let cstr = CString::new(self.source_dir.as_str()).unwrap(); + let source_dir_fd = unsafe { + libc::open( + cstr.as_ptr(), + libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC, + ) + }; + if source_dir_fd < 0 { + bail!("open source_dir fail"); + } + // Switch to new root then call pivot_root. + if unsafe { libc::fchdir(source_dir_fd) } == -1 { + bail!("fchdir fail"); + } + // Use '.' as both old and new root. + let cstr = CString::new(".").unwrap(); + if unsafe { libc::syscall(libc::SYS_pivot_root, cstr.as_ptr(), cstr.as_ptr()) } == -1 { + bail!("pivot_root fail"); + } + // Switch to old root then umount it. + if unsafe { libc::fchdir(root_dir_fd) } == -1 { + bail!("change to root_dir fail"); + } + // Clean up old root to avoid mount namespace propagation. + self.clean_old_root()?; + // Umount old root. + let cstr = CString::new(".").unwrap(); + if unsafe { libc::umount2(cstr.as_ptr(), libc::MNT_DETACH) } == -1 { + bail!("umount2 old root fail"); + } + if unsafe { libc::fchdir(source_dir_fd) } == -1 { + bail!("change to root_dir fail"); + } + if unsafe { libc::close(source_dir_fd) } == -1 { + bail!("close source_dir fail"); + } + if unsafe { libc::close(root_dir_fd) } == -1 { + bail!("close root_dir fail"); + } + Ok(()) + } + + pub fn parent_process_wait_child(&self, fork_ans: i32) -> Result<()> { + capng::clear(capng::Set::BOTH); + if let Err(err) = capng::apply(capng::Set::BOTH) { + error!("apply fail {}", err); + } + + let mut wstatus = 0; + if fork_ans != unsafe { libc::waitpid(fork_ans, &mut wstatus, 0) } { + bail!("waitpid fail"); + } + let exit_code = if libc::WIFEXITED(wstatus) { + // The child terminated normally return true. + libc::WEXITSTATUS(wstatus) + } else if libc::WIFSIGNALED(wstatus) { + // Child process was terminated by a signal return true. + let signal = libc::WTERMSIG(wstatus); + error!("Child process was terminated by a signal: {}", signal); + -signal + } else { + error!("exit failed: {:#X}", wstatus); + libc::EXIT_FAILURE + }; + bail!("exit_code {}", exit_code); + } + + pub fn clean_old_root(&self) -> Result<()> { + let cstr = CString::new("").unwrap(); + let cstr2 = CString::new(".").unwrap(); + if unsafe { + libc::mount( + cstr.as_ptr(), + cstr2.as_ptr(), + cstr.as_ptr(), + libc::MS_SLAVE | libc::MS_REC, + std::ptr::null(), + ) + } == -1 + { + bail!("changing the propagation type of mounts in the new namespace fail"); + } + Ok(()) + } + + pub fn bind_source_dir(&self) -> Result<()> { + let cstr = CString::new(self.source_dir.as_str()).unwrap(); + let cstr2 = CString::new("").unwrap(); + if unsafe { + libc::mount( + cstr.as_ptr(), + cstr.as_ptr(), + cstr2.as_ptr(), + libc::MS_BIND | libc::MS_REC, + std::ptr::null(), + ) + } == -1 + { + bail!("mount --bind source_dir source_dir fail"); + } + Ok(()) + } + + fn proc_self_fd_bind_proc(&mut self) -> Result<()> { + let cstr = CString::new("/proc/self/fd").unwrap(); + let cstr2 = CString::new("/proc").unwrap(); + let cstr3 = CString::new("").unwrap(); + if unsafe { + libc::mount( + cstr.as_ptr(), + cstr2.as_ptr(), + cstr3.as_ptr(), + libc::MS_BIND, + std::ptr::null(), + ) + } == -1 + { + bail!("mount --bind /proc/self/fd /proc fail"); + } + + let cstr = CString::new("/proc").unwrap(); + let open_ans = unsafe { libc::open(cstr.as_ptr(), libc::O_PATH) }; + if open_ans == -1 { + bail!("open /proc failed"); + } + self.proc_self_fd = Some(unsafe { File::from_raw_fd(open_ans) }); + Ok(()) + } + + fn mount_proc(&self) -> Result<()> { + let cstr = CString::new("proc").unwrap(); + let cstr2 = CString::new("/proc").unwrap(); + let cstr3 = CString::new("proc").unwrap(); + if unsafe { + libc::mount( + cstr.as_ptr(), + cstr2.as_ptr(), + cstr3.as_ptr(), + libc::MS_NODEV | libc::MS_NOEXEC | libc::MS_NOSUID | libc::MS_RELATIME, + std::ptr::null(), + ) + } == -1 + { + bail!("mount /proc fail"); + } + Ok(()) + } + + fn change_propagation(&self) -> Result<()> { + let cstr = CString::new("").unwrap(); + let cstr2 = CString::new("/").unwrap(); + if unsafe { + libc::mount( + cstr.as_ptr(), + cstr2.as_ptr(), + cstr.as_ptr(), + libc::MS_SLAVE | libc::MS_REC, + std::ptr::null(), + ) + } == -1 + { + bail!("changing the propagation type of mounts in the new namespace fail"); + } + Ok(()) + } + + fn id_mapping(&self, euid: u32, egid: u32) { + // The setgroups file can only be written to before the group-ID mapping has been set. + let _result1 = fs::write("/proc/self/setgroups", "deny"); + // Format: id_in_namespace id_out_namespace length. + let uid_map_string = format!("{} {} {}", euid, euid, 1); + let gid_map_string = format!("{} {} {}", egid, egid, 1); + let _result2 = fs::write("/proc/self/uid_map", uid_map_string); + let _result3 = fs::write("/proc/self/gid_map", gid_map_string); + } +} + +pub fn drop_groups() -> Result<()> { + // The total number of supplementary group IDs for the process is returned. + let group_num = unsafe { libc::getgroups(0, std::ptr::null_mut()) }; + if group_num == -1 { + bail!("getgroups fail"); + } else if group_num > 0 { + // Sets the supplementary group IDs for the calling process. Appropriate privileges are required. + // A process can drop all of its supplementary groups with the call:setgroups(0, NULL). + if unsafe { libc::setgroups(0, std::ptr::null()) } == -1 { + bail!("setgroups fail"); + } + } + Ok(()) +} diff --git a/vhost_user_fs/src/securecomputing.rs b/vhost_user_fs/src/securecomputing.rs new file mode 100644 index 000000000..859479ca7 --- /dev/null +++ b/vhost_user_fs/src/securecomputing.rs @@ -0,0 +1,185 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Result}; +use libc::c_int; +use libseccomp_sys::{ + seccomp_init, seccomp_load, seccomp_release, seccomp_rule_add, SCMP_ACT_ALLOW, + SCMP_ACT_KILL_PROCESS, SCMP_ACT_LOG, SCMP_ACT_TRAP, +}; + +/// Seccomp option parsed from command line. +#[derive(Copy, Clone, Debug)] +pub enum SeccompOpt { + /// The seccomp filter will have no effect. + Allow, + /// Same as 'allow', but the syscall will be logged. + Log, + /// Kill the task immediately. + Kill, + /// Disallow and force a SIGSYS. + Trap, +} + +impl From for u32 { + fn from(action: SeccompOpt) -> u32 { + match action { + SeccompOpt::Allow => SCMP_ACT_ALLOW, + SeccompOpt::Kill => SCMP_ACT_KILL_PROCESS, + SeccompOpt::Log => SCMP_ACT_LOG, + SeccompOpt::Trap => SCMP_ACT_TRAP, + } + } +} + +pub fn add_syscall() -> Vec { + let mut v = vec![libc::SYS_accept4]; + v.push(libc::SYS_brk); + v.push(libc::SYS_bind); + v.push(libc::SYS_capget); + v.push(libc::SYS_capset); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_chmod); + v.push(libc::SYS_clock_gettime); + v.push(libc::SYS_clone); + v.push(libc::SYS_clone3); + v.push(libc::SYS_close); + v.push(libc::SYS_copy_file_range); + v.push(libc::SYS_dup); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_epoll_create); + v.push(libc::SYS_epoll_create1); + v.push(libc::SYS_epoll_ctl); + v.push(libc::SYS_epoll_pwait); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_epoll_wait); + v.push(libc::SYS_eventfd2); + v.push(libc::SYS_exit); + v.push(libc::SYS_exit_group); + v.push(libc::SYS_fallocate); + v.push(libc::SYS_fchdir); + v.push(libc::SYS_fchmod); + v.push(libc::SYS_fchmodat); + v.push(libc::SYS_fchownat); + v.push(libc::SYS_fcntl); + v.push(libc::SYS_fdatasync); + v.push(libc::SYS_fgetxattr); + v.push(libc::SYS_flistxattr); + v.push(libc::SYS_flock); + v.push(libc::SYS_fremovexattr); + v.push(libc::SYS_fsetxattr); + v.push(libc::SYS_fstat); + v.push(libc::SYS_fstatfs); + v.push(libc::SYS_fsync); + v.push(libc::SYS_ftruncate); + v.push(libc::SYS_futex); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_getdents); + v.push(libc::SYS_getdents64); + v.push(libc::SYS_getegid); + v.push(libc::SYS_geteuid); + v.push(libc::SYS_getpid); + v.push(libc::SYS_getrandom); + v.push(libc::SYS_gettid); + v.push(libc::SYS_gettimeofday); + v.push(libc::SYS_getxattr); + v.push(libc::SYS_linkat); + v.push(libc::SYS_listen); + v.push(libc::SYS_listxattr); + v.push(libc::SYS_lseek); + v.push(libc::SYS_madvise); + v.push(libc::SYS_mkdirat); + v.push(libc::SYS_mknodat); + v.push(libc::SYS_mmap); + v.push(libc::SYS_mprotect); + v.push(libc::SYS_mremap); + v.push(libc::SYS_munmap); + v.push(libc::SYS_name_to_handle_at); + v.push(libc::SYS_newfstatat); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_open); + v.push(libc::SYS_openat); + v.push(libc::SYS_open_by_handle_at); + v.push(libc::SYS_prctl); + v.push(libc::SYS_preadv); + v.push(libc::SYS_pread64); + v.push(libc::SYS_pwritev); + v.push(libc::SYS_pwrite64); + v.push(libc::SYS_read); + v.push(libc::SYS_readlinkat); + v.push(libc::SYS_recvmsg); + v.push(libc::SYS_renameat); + v.push(libc::SYS_renameat2); + v.push(libc::SYS_removexattr); + v.push(libc::SYS_rt_sigaction); + v.push(libc::SYS_rt_sigprocmask); + v.push(libc::SYS_rt_sigreturn); + v.push(libc::SYS_sched_getaffinity); + v.push(libc::SYS_sendmsg); + v.push(libc::SYS_setresgid); + v.push(libc::SYS_setresuid); + v.push(libc::SYS_set_robust_list); + v.push(libc::SYS_setxattr); + v.push(libc::SYS_sigaltstack); + v.push(libc::SYS_socket); + v.push(libc::SYS_statx); + v.push(libc::SYS_symlinkat); + v.push(libc::SYS_syncfs); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_time); + v.push(libc::SYS_tgkill); + v.push(libc::SYS_umask); + #[cfg(target_arch = "x86_64")] + v.push(libc::SYS_unlink); + v.push(libc::SYS_unlinkat); + v.push(libc::SYS_unshare); + v.push(libc::SYS_utimensat); + v.push(libc::SYS_write); + v.push(libc::SYS_writev); + v +} + +/// Enable seccomp to limit syscall. +/// +/// # Arguments +/// +/// * `action` - The default action. +pub fn seccomp_filter(action: SeccompOpt) -> Result<()> { + let action_value = action.into(); + let scmp_filter_ctx = unsafe { seccomp_init(action_value) }; + if scmp_filter_ctx.is_null() { + bail!("seccomp_init fail"); + } + + let allowed_syscalls = add_syscall(); + for i in allowed_syscalls { + if unsafe { seccomp_rule_add(scmp_filter_ctx, SCMP_ACT_ALLOW, i as c_int, 0) } != 0 { + bail!("seccomp rule add fail {}", i); + } + } + if unsafe { seccomp_load(scmp_filter_ctx) } != 0 { + bail!("seccomp_load fail"); + } + unsafe { seccomp_release(scmp_filter_ctx) }; + Ok(()) +} + +pub fn string_to_seccompopt(string: String) -> SeccompOpt { + let str = string.as_str(); + match str { + "kill" => SeccompOpt::Kill, + "log" => SeccompOpt::Log, + "trap" => SeccompOpt::Trap, + "allow" => SeccompOpt::Allow, + _ => SeccompOpt::Kill, + } +} diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index 15d19ff0d..bfe54e526 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -135,15 +135,13 @@ impl VhostUserFs { .with_context(|| format!("Failed to set rlimit nofile {}", limit))?; } + let sock_path = fs_config.sock_path.clone(); let virtio_fs = Arc::new(Mutex::new( - VirtioFs::new(&fs_config.source_dir) - .with_context(|| format!("Failed to create virtio fs {}", fs_config.source_dir))?, + VirtioFs::new(fs_config).with_context(|| "Failed to create virtio fs")?, )); - let server_handler = VhostUserServerHandler::new(&fs_config.sock_path, virtio_fs) - .with_context(|| { - format!("Failed to create vhost user server {}", fs_config.sock_path) - })?; + let server_handler = VhostUserServerHandler::new(sock_path.as_str(), virtio_fs) + .with_context(|| "Failed to create vhost user server")?; Ok(VhostUserFs { server_handler }) } diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index dbeae0d46..d259777f7 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -17,6 +17,7 @@ const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; /// The max queue size. const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; +use crate::cmdline::FsConfig; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::{Arc, Mutex}; @@ -211,7 +212,7 @@ impl VirtioFs { /// # Arguments /// /// * `source_dir` - The path of source directory shared in host. - pub fn new(source_dir: &str) -> Result { + pub fn new(fs_config: FsConfig) -> Result { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) .with_context(|| "Failed to create address space")?; @@ -220,9 +221,9 @@ impl VirtioFs { fs_handlers.push(None); } - let fs = Arc::new(Mutex::new(FileSystem::new(source_dir).with_context( - || format!("Failed to create file system, source dir: {}", source_dir), - )?)); + let fs = Arc::new(Mutex::new( + FileSystem::new(fs_config).with_context(|| "Failed to create file system")?, + )); Ok(VirtioFs { config: VirtioFsConfig::new(), -- Gitee From 78735b1ce397420b1a8ea8f6cb0d6b6018e587cb Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Wed, 12 Oct 2022 15:41:54 +0800 Subject: [PATCH 0472/2187] virtiofsd: parse modcaps from cmdline and support modify capability list 1 add prefix '--' and parse '--modcaps' from cmdline 2 modify the list of capabilities allowed Signed-off-by: YuJun Huang --- Cargo.lock | 2 + Cargo.toml | 2 + util/src/arg_parser.rs | 84 ++++++++++++++++++++++++++---------- vhost_user_fs/src/cmdline.rs | 7 +++ vhost_user_fs/src/main.rs | 61 +++++++++++++++++++++++++- 5 files changed, 131 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 782d947f0..c90ce7f0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,8 @@ name = "StratoVirt" version = "2.2.0" dependencies = [ "anyhow", + "capng", + "error-chain", "hypervisor", "kvm-ioctls", "libc", diff --git a/Cargo.toml b/Cargo.toml index 37d98cb13..161f8950e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ license = "Mulan PSL v2" [dependencies] thiserror = "1.0" anyhow = "1.0" +capng = "0.2.2" +error-chain = "0.12.4" kvm-ioctls = ">=0.11.0" libc = "0.2" log = "0.4" diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index a3469e871..e57bdd96e 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -18,10 +18,11 @@ use std::io::Write; use std::process; use crate::UtilError; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; const PREFIX_CHARS_SHORT: &str = "-"; const PREFIX_CHARS_LONG: &str = "-"; +const PREFIX_OPT_LONG: &str = "--"; const ARG_SEPARATOR: &str = "--"; const HELP_SHORT: &str = "h"; const HELP_LONG: &str = "help"; @@ -99,6 +100,7 @@ pub struct Arg<'a> { name: &'a str, long: Option<&'a str>, short: Option<&'a str>, + opt_long: Option<&'a str>, help: Option<&'a str>, value_name: Option<&'a str>, value: Option, @@ -175,6 +177,10 @@ impl<'a> ArgParser<'a> { self.allow_list .push(format!("{}{}", PREFIX_CHARS_SHORT, arg.short.unwrap())); } + if arg.opt_long.is_some() { + self.allow_list + .push(format!("{}{}", PREFIX_OPT_LONG, arg.opt_long.unwrap())); + } self.args.insert(arg.name, arg); self } @@ -298,6 +304,12 @@ impl<'a> Arg<'a> { self } + /// Set opt long argument for arg. + pub fn opt_long(mut self, opt_long: &'a str) -> Self { + self.opt_long = Some(opt_long); + self + } + /// Set help message for arg. pub fn help(mut self, help: &'a str) -> Self { self.help = Some(help); @@ -373,51 +385,55 @@ impl<'a> Arg<'a> { /// Parse argument from a hashset. fn parse_from_hash(&mut self, arg_hash: &ArgsMap, multi_vec: &[String]) -> Result<()> { - let long_name = self.long.unwrap().to_string(); + let name = if let Some(long) = self.long { + long.to_string() + } else if let Some(opt_long) = self.opt_long { + opt_long.to_string() + } else { + bail!("Invalid argument, long and opt_long are None") + }; - if arg_hash.contains_key(&long_name) { - if !self.multiple && multi_vec.contains(&long_name) { - return Err(anyhow!(UtilError::DuplicateArgument(long_name))); + if arg_hash.contains_key(&name) { + if !self.multiple && multi_vec.contains(&name) { + return Err(anyhow!(UtilError::DuplicateArgument(name))); } - if self.value.is_some() && (arg_hash[&long_name].len() > 1) && !self.multiple { - return Err(anyhow!(UtilError::DuplicateValue(long_name))); + if self.value.is_some() && (arg_hash[&name].len() > 1) && !self.multiple { + return Err(anyhow!(UtilError::DuplicateValue(name))); } - if (self.value.is_some() || self.values.is_some()) && (arg_hash[&long_name].is_empty()) - { + if (self.value.is_some() || self.values.is_some()) && (arg_hash[&name].is_empty()) { if self.can_no_value { self.value = Some(Default::default()); self.presented = true; return Ok(()); } else { - return Err(anyhow!(UtilError::MissingValue(long_name))); + return Err(anyhow!(UtilError::MissingValue(name))); } } - if (self.value.is_none() && self.values.is_none()) && (!arg_hash[&long_name].is_empty()) - { + if (self.value.is_none() && self.values.is_none()) && (!arg_hash[&name].is_empty()) { return Err(anyhow!(UtilError::IllegelValue( - arg_hash[&long_name][0].to_string(), - long_name.to_string(), + arg_hash[&name][0].to_string(), + name.to_string(), ))); } if self.value.is_some() { - if self.possible_value_check(&arg_hash[&long_name][0]) { - self.value = Some(arg_hash[&long_name][0].clone()); + if self.possible_value_check(&arg_hash[&name][0]) { + self.value = Some(arg_hash[&name][0].clone()); } else { return Err(anyhow!(UtilError::ValueOutOfPossible( - long_name, + name, format!("{:?}", self.possible_values), ))); } } else if self.values.is_some() { - if self.possible_values_check(arg_hash[&long_name].clone()) { - self.values = Some(arg_hash[&long_name].clone()); + if self.possible_values_check(arg_hash[&name].clone()) { + self.values = Some(arg_hash[&name].clone()); } else { return Err(anyhow!(UtilError::ValueOutOfPossible( - long_name, + name, format!("{:?}", self.possible_values), ))); } @@ -425,7 +441,7 @@ impl<'a> Arg<'a> { self.presented = true; } else if self.required { - return Err(anyhow!(UtilError::MissingArgument(long_name))); + return Err(anyhow!(UtilError::MissingArgument(name))); } if self.short.is_some() { @@ -596,11 +612,33 @@ fn parse_cmdline( let mut i = (0, ""); let mut j = 1; for cmd_arg in &cmd_args[1..] { - if !allow_list.contains(cmd_arg) && cmd_arg.starts_with(PREFIX_CHARS_SHORT) { + if !allow_list.contains(cmd_arg) + && cmd_arg.starts_with(PREFIX_CHARS_SHORT) + && !cmd_arg.starts_with(PREFIX_OPT_LONG) + { return Err(anyhow!(UtilError::UnexpectedArguments(cmd_arg.to_string()))); } - if cmd_arg.starts_with(PREFIX_CHARS_LONG) { + if cmd_arg.starts_with(PREFIX_OPT_LONG) { + let splits = cmd_arg.split('=').collect::>(); + // It has two arguments. e.g. "--modcaps=+sys_admin". + if splits.len() != 2 { + return Err(anyhow!(UtilError::UnexpectedArguments(cmd_arg.to_string()))); + } + if !allow_list.contains(&splits[0].to_string()) { + return Err(anyhow!(UtilError::UnexpectedArguments(cmd_arg.to_string()))); + } + let arg_str = split_arg(splits[0], PREFIX_OPT_LONG); + if let Entry::Vacant(e) = arg_map.entry(arg_str.to_string()) { + e.insert(Vec::new()); + } else { + multi_vec.push(arg_str.to_string()); + } + arg_map + .get_mut(arg_str.as_str()) + .unwrap() + .push(splits[1].to_string()); + } else if cmd_arg.starts_with(PREFIX_CHARS_LONG) { let arg_str = split_arg(cmd_arg, PREFIX_CHARS_LONG); if let Entry::Vacant(e) = arg_map.entry(arg_str.clone()) { diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 0259487f9..cc56f42ab 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -77,6 +77,13 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_value(true) .possible_values(vec!["namespace", "chroot"]), ) + .arg( + Arg::with_name("modcaps") + .opt_long("modcaps") + .value_name("add or delete modcaps") + .help("--modcaps=-LEASE,+KILL") + .takes_value(true), + ) } /// Filesystem configuration parsed from command line for the process. diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index a7cbc167d..0a12b44e1 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -13,13 +13,15 @@ #[macro_use] extern crate log; extern crate vhost_user_fs; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use machine_manager::event_loop::EventLoop; use machine_manager::signal_handler; +use std::collections::HashSet; use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; use thiserror::Error; +use util::arg_parser::ArgMatches; use util::{arg_parser, logger}; use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; use vhost_user_fs::sandbox::Sandbox; @@ -74,6 +76,45 @@ fn main() { }); } +fn parse_capabilities(cmd_args: &arg_parser::ArgMatches) -> Result> { + let mut add_caps = HashSet::new(); + + add_caps.insert("CHOWN".to_string()); + add_caps.insert("DAC_OVERRIDE".to_string()); + add_caps.insert("FOWNER".to_string()); + add_caps.insert("FSETID".to_string()); + add_caps.insert("SETGID".to_string()); + add_caps.insert("SETUID".to_string()); + add_caps.insert("MKNOD".to_string()); + add_caps.insert("SETFCAP".to_string()); + + if let Some(capabilities_str) = cmd_args.value_of("modcaps") { + let cut = &capabilities_str; + for s in cut.split(',').map(str::to_string) { + if s.is_empty() { + bail!("empty capability"); + } + let (addorsub, capability_literal) = s.split_at(1); + let capability = capability_literal.to_uppercase().to_string(); + if capng::name_to_capability(capability.as_str()).is_err() { + bail!("invalid capability {}", s); + } + match addorsub { + "+" => { + info!("add capability:{}", &capability); + add_caps.insert(capability); + } + "-" => { + info!("del capability:{}", &capability); + add_caps.remove(&capability); + } + _ => bail!("The first char before capability name must be + or - "), + } + } + } + Ok(add_caps) +} + fn run() -> Result<()> { let cmd_args = create_args_parser().get_matches()?; @@ -116,7 +157,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { _ => seccomp_filter(seccomp_opt).unwrap(), } } - + update_capabilities(cmd_args)?; EventLoop::object_init(&None)?; let vhost_user_fs = Arc::new(Mutex::new( @@ -134,6 +175,22 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { Ok(()) } +fn update_capabilities(cmd_args: &ArgMatches) -> Result<()> { + let add = parse_capabilities(cmd_args)?; + capng::clear(capng::Set::BOTH); + if let Err(e) = capng::updatev( + capng::Action::ADD, + capng::Type::PERMITTED | capng::Type::EFFECTIVE, + add.iter().map(String::as_str).collect(), + ) { + bail!("can't set up the child capabilities: {}", e); + } + if let Err(e) = capng::apply(capng::Set::BOTH) { + bail!("can't apply the child capabilities: {}", e); + } + Ok(()) +} + fn init_log(logfile_path: String) -> Result<()> { if logfile_path.is_empty() { logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) -- Gitee From 8b80b1107bb398f6fe66296a37c7d403c27e213e Mon Sep 17 00:00:00 2001 From: YuJun Huang Date: Mon, 24 Oct 2022 20:02:55 +0800 Subject: [PATCH 0473/2187] virtiofsd: add vhost_user_fs usage to config_guidebook.md 1 add vhost_user_fs basic usage. 2 add vhost_user_fs's sandbox usage. Signed-off-by: YuJun Huang --- docs/config_guidebook.md | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index eee2fdb9d..d13f0060c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -838,6 +838,59 @@ Sample Configuration: Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. +### 2.18 Virtio-fs +Virtio-fs is a shared file system that lets virtual machines access a directory tree on the host. Unlike existing approaches, it is designed to offer local file system semantics and performance. + +### 2.18.1 virtio fs device +Three properties can be set for virtio fs device. +* chardevid: id for char device +* device_id: the unique id for device +* mount_tag: the mount tag of the shared directory which can be mounted in the guest + +```shell +-chardev socket,id=chardevid,path=socket_path +-device vhost-user-fs-pci,id=device_id,chardev=chardevid,tag=mount_tag +``` + +### 2.18.2 vhost_user_fs +The vhost-user filesystem device contains virtio fs device and the vhost-user server which can be connected with the vhost-user client in StratoVirt through socket. + +Seven properties are supported for vhost_user_fs. +* source: Shared directory path. +* socket-path: vhost user socket path. +* rlimit-nofile: Set maxinum number of file descriptors, The limit of file resources which can be opened for the process. +* D: log file path. +* seccomp: Action to take when seccomp finds a not allowed syscall (allow, kill, log, trap). + - **allow**: The seccomp filter will have no effect on the thread calling the syscall if it matches the filter rule. + - **kill**: The process will be killed by the kernel when it calls a syscall that matches the filter rule. + - **log**: The seccomp filter will have no effect on the thread calling the syscall if it matches the filter rule but the syscall will be logged. + - **trap**: The thread will throw a SIGSYS signal when it calls a syscall that matches the filter rule. +* sandbox: Sandbox mechanism to isolate the daemon process (chroot, namespace). + - **chroot**: The program invokes `chroot(2)` to make the shared directory tree its root when it does not have permission to create namespaces itself. + - **namespace**: The program invodes `pivot_root(2)` to make the shared directory tree its root. +* modcaps: Add/delete capabilities, For example, `--modcaps=-LEASE,+KILL` stands for delete CAP_LEASE, add CAP_KILL. Capabilityes list do not need prefix `CAP_`. + +*How to start vhost_user_fs process?* + +```shell +host# ./path/to/vhost_user_fs -source /tmp/shared -socket-path /tmp/shared/virtio_fs.sock -D + +host# stratovirt \ + -machine type=q35,dump-guest-core=off,mem-share=on \ + -smp 1 \ + -m 1024 \ + -kernel \ + -append root=/dev/vda console=ttyS0 reboot=k panic=1 random.trust_cpu=on rw \ + -drive file=,if=pflash,unit=0 \ + -qmp unix:/tmp/qmp2.socket,server,nowait \ + -drive id=drive_id,file=,direct=on \ + -device virtio-blk-pci,drive=drive_id,bug=pcie.0,addr=1,id=blk -serial stdio -disable-seccomp \ + -chardev socket,id=virtio_fs,path=/tmp/shared/virtio_fs.sock,server,nowait \ + -device vhost-user-fs-pci,id=device_id,chardev=virtio_fs,tag=myfs,bus=pcie.0,addr=0x7 + +guest# mount -t virtiofs myfs /mnt +``` + ## 3. Trace Users can specify the configuration file which lists events to trace. -- Gitee From ce750e91d890bc05c1ef0dcb6528cc3677db6623 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 23 Nov 2022 10:51:39 +0800 Subject: [PATCH 0474/2187] fix qmp command 'query-status' The excepted return value by qmp command 'query-status' is false when the domain status is paused, but now is true. Signed-off-by: mayunlong --- machine/src/micro_vm/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 434354c5b..3ed316e42 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -930,7 +930,7 @@ impl DeviceInterface for LightMachine { }, KvmVmState::Paused => qmp_schema::StatusInfo { singlestep: false, - running: true, + running: false, status: qmp_schema::RunState::paused, }, _ => Default::default(), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index b7cd867dc..ace8409e5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -938,7 +938,7 @@ impl DeviceInterface for StdMachine { }, KvmVmState::Paused => qmp_schema::StatusInfo { singlestep: false, - running: true, + running: false, status: qmp_schema::RunState::paused, }, _ => Default::default(), -- Gitee From 91de4a0b46e0ef3cd8c6854d90e8fb2ca1217a75 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 01:34:30 -0500 Subject: [PATCH 0475/2187] virtiofsd: Report error when the client queue request was in polling mode For VHOST_USER_SET_VRING_KICK and VHOST_USER_SET_VRING_CALL and VHOST_USER_SET_VRING_ERR, Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag is set when there is no file descriptor in the ancillary data. This signals that polling should be used instead of waiting for the kick. We check this flag, and report error when the client queue request was in polling mode. Signed-off-by: Li HuaChao --- vhost_user_fs/src/virtio_fs.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index dbeae0d46..cef987aca 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -16,6 +16,12 @@ const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; /// The max queue size. const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; +const VIRTIO_FS_VRING_IDX_MASK: usize = 0xff; +/// For VHOST_USER_SET_VRING_KICK and VHOST_USER_SET_VRING_CALL and VHOST_USER_SET_ +/// VRING_ERR, Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid +/// FD flag. This flag is set when there is no file descriptor in the ancillary data. +/// This signals that polling should be used instead of waiting for the kick. +const VIRTIO_FS_VRING_NO_FD_MASK: usize = 0x1 << 8; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -367,24 +373,32 @@ impl VhostUserReqHandler for VirtioFs { } fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + if (queue_index & VIRTIO_FS_VRING_NO_FD_MASK) != 0 { + bail!("The polling mode is not supported"); + } + let index = queue_index & VIRTIO_FS_VRING_IDX_MASK; self.config - .get_mut_queue_config(queue_index) + .get_mut_queue_config(index) .map(|queue_info| { let call_evt = unsafe { EventFd::from_raw_fd(fd) }; queue_info.call_evt = Some(call_evt); }) - .with_context(|| format!("Failed to set vring call, index: {}", queue_index))?; + .with_context(|| format!("Failed to set vring call, index: {}", index))?; Ok(()) } fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + if (queue_index & VIRTIO_FS_VRING_NO_FD_MASK) != 0 { + bail!("The polling mode is not supported"); + } + let index = queue_index & VIRTIO_FS_VRING_IDX_MASK; self.config - .get_mut_queue_config(queue_index) + .get_mut_queue_config(index) .map(|queue_info| { let kick_evt = unsafe { EventFd::from_raw_fd(fd) }; queue_info.kick_evt = Some(kick_evt); }) - .with_context(|| format!("Failed to set vring kick, index: {}", queue_index))?; + .with_context(|| format!("Failed to set vring kick, index: {}", index))?; Ok(()) } -- Gitee From c543d9f70e4fc544ef9227c6e3ae7ed6ef0db34c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 23 Nov 2022 10:04:11 +0800 Subject: [PATCH 0476/2187] Fix: Duplicate data writing code is optimized. Data write back to array operations occur in multiple files. Unify this operation to reduce code duplication. Signed-off-by: Mingwang Li --- acpi/src/acpi_device.rs | 39 ++------------ devices/src/legacy/pflash.rs | 14 +---- devices/src/legacy/pl031.rs | 18 ++----- usb/src/xhci/xhci_regs.rs | 52 +++---------------- util/src/num_ops.rs | 99 ++++++++++++++++++++++++++++++++++++ virtio/src/virtio_pci.rs | 20 +------- 6 files changed, 117 insertions(+), 125 deletions(-) diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index 49c0ef912..558d57bc0 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -16,7 +16,7 @@ use address_space::GuestAddress; use byteorder::{ByteOrder, LittleEndian}; use log::error; -use util::time::NANOSECONDS_PER_SECOND; +use util::{num_ops::write_data_u16, time::NANOSECONDS_PER_SECOND}; // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; @@ -75,34 +75,13 @@ impl AcpiPmEvent { pub fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { match offset { - 0 => match data.len() { - 1 => data[0] = self.status as u8, - 2 => LittleEndian::write_u16(data, self.status), - n => { - error!( - "Invalid data length {} for reading PM status register, offset is {}", - n, offset - ); - return false; - } - }, - 2 => match data.len() { - 1 => data[0] = self.enable as u8, - 2 => LittleEndian::write_u16(data, self.enable), - n => { - error!( - "Invalid data length {} for reading PM enable register, offset is {}", - n, offset - ); - return false; - } - }, + 0 => write_data_u16(data, self.status), + 2 => write_data_u16(data, self.enable), _ => { error!("Invalid offset"); - return false; + false } } - true } pub fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { @@ -155,15 +134,7 @@ impl AcpiPmCtrl { } pub fn read(&mut self, data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool { - match data.len() { - 1 => data[0] = self.control as u8, - 2 => LittleEndian::write_u16(data, self.control), - n => { - error!("Invalid data length {} for reading PM control register", n); - return false; - } - } - true + write_data_u16(data, self.control) } // Return true when guest want poweroff. diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index ce6f0341c..13487eb3b 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -21,7 +21,7 @@ use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; -use util::num_ops::{deposit_u32, extract_u32}; +use util::num_ops::{deposit_u32, extract_u32, write_data_u32}; pub struct PFlash { /// Has backend file or not. has_backend: bool, @@ -815,17 +815,7 @@ impl SysBusDevOps for PFlash { } } - match data.len() { - 1 => data[0] = ret as u8, - 2 => LittleEndian::write_u16(data, ret as u16), - 4 => LittleEndian::write_u32(data, ret), - n => { - error!("Invalid data length {}", n); - return false; - } - } - - true + write_data_u32(data, ret) } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index f34ed2bb2..3ba4a3996 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -26,6 +26,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; +use util::num_ops::write_data_u32; use vmm_sys_util::eventfd::EventFd; /// Registers for pl031 from ARM PrimeCell Real Time Clock Technical Reference Manual. @@ -139,13 +140,7 @@ impl SysBusDevOps for PL031 { fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if (0xFE0..0x1000).contains(&offset) { let value = u32::from(RTC_PERIPHERAL_ID[((offset - 0xFE0) >> 2) as usize]); - match data.len() { - 1 => data[0] = value as u8, - 2 => LittleEndian::write_u16(data, value as u16), - 4 => LittleEndian::write_u32(data, value as u32), - _ => {} - } - return true; + return write_data_u32(data, value); } let mut value: u32 = 0; @@ -160,14 +155,7 @@ impl SysBusDevOps for PL031 { _ => {} } - match data.len() { - 1 => data[0] = value as u8, - 2 => LittleEndian::write_u16(data, value as u16), - 4 => LittleEndian::write_u32(data, value as u32), - _ => {} - } - - true + write_data_u32(data, value) } /// Write data to registers by guest. diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 2bb79b97b..a2323850a 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use util::num_ops::{read_u32, write_u64_high, write_u64_low}; +use util::num_ops::{read_u32, write_data_u32, write_u64_high, write_u64_low}; use crate::config::*; use crate::usb::UsbPort; @@ -315,11 +315,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { 0 } }; - if let Err(e) = write_data(data, value) { - error!("Failed to write data when read oper registers: {:?}", e); - return false; - } - true + write_data_u32(data, value) }; let cap_write = move |_data: &[u8], _addr: GuestAddress, offset: u64| -> bool { @@ -368,11 +364,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { 0 } }; - if let Err(e) = write_data(data, value) { - error!("Failed to write data when read oper registers: {:?}", e); - return false; - } - true + write_data_u32(data, value) }; let xhci = xhci_dev.clone(); @@ -515,11 +507,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } }; } - if let Err(e) = write_data(data, value) { - error!("Failed to write data when read runtime registers: {:?}", e); - return false; - } - true + write_data_u32(data, value) }; let xhci = xhci_dev.clone(); @@ -605,11 +593,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let doorbell_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { debug!("doorbell read addr {:x} offset {:x}", addr.0, offset); - if let Err(e) = write_data(data, 0) { - error!("Failed to write data: {:?}", e); - return false; - } - true + write_data_u32(data, 0) }; let xhci = xhci_dev.clone(); let doorbell_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { @@ -671,11 +655,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { return false; } }; - if let Err(e) = write_data(data, value) { - error!("Failed to write data: {:?}", e); - return false; - } - true + write_data_u32(data, value) }; let port = xhci_port.clone(); @@ -775,26 +755,6 @@ fn xhci_portsc_ls_write(port: &mut XhciPort, old_pls: u32, new_pls: u32) -> u32 0 } -fn write_data(data: &mut [u8], value: u32) -> Result<()> { - match data.len() { - 1 => data[0] = value as u8, - 2 => { - LittleEndian::write_u16(data, value as u16); - } - 4 => { - LittleEndian::write_u32(data, value); - } - _ => { - bail!( - "Invalid data length: value {}, data len {}", - value, - data.len() - ); - } - }; - Ok(()) -} - fn read_data(data: &[u8]) -> Result { let value = match data.len() { 1 => data[0] as u32, diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index c39b88a1f..96fb1cfce 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use byteorder::{ByteOrder, LittleEndian}; use log::error; // This module implements some operations of Rust primitive types. @@ -251,6 +252,75 @@ pub fn deposit_u32(value: u32, start: u32, length: u32, fieldval: u32) -> Option Some((value & !mask) | ((fieldval << start) & mask)) } +/// Write the given u16 to an array, returns the bool. +/// +/// # Arguments +/// +/// * `data` - The array of u8. +/// * `value` - The u16 value +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::write_data_u16; +/// +/// let mut data: [u8; 2] = [0; 2]; +/// let ret = write_data_u16(&mut data, 0x1234); +/// assert!(ret && data[0] == 0x34 && data[1] == 0x12); +/// ``` +pub fn write_data_u16(data: &mut [u8], value: u16) -> bool { + match data.len() { + 1 => data[0] = value as u8, + 2 => { + LittleEndian::write_u16(data, value as u16); + } + n => { + error!("Invalid data length {} for reading value {}", n, value); + return false; + } + }; + true +} + +/// Write the given u32 to an array, returns the bool. +/// +/// # Arguments +/// +/// * `data` - The array of u8. +/// * `value` - The u32 value +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::write_data_u32; +/// +/// let mut data: [u8; 4] = [0; 4]; +/// let ret = write_data_u32(&mut data, 0x12345678); +/// assert!(ret && data[0] == 0x78 && data[1] == 0x56 && data[2] == 0x34 && data[3] == 0x12); +/// ``` +pub fn write_data_u32(data: &mut [u8], value: u32) -> bool { + match data.len() { + 1 => data[0] = value as u8, + 2 => { + LittleEndian::write_u16(data, value as u16); + } + 4 => { + LittleEndian::write_u32(data, value); + } + _ => { + error!( + "Invalid data length: value {}, data len {}", + value, + data.len() + ); + return false; + } + }; + true +} + #[cfg(test)] mod test { use super::*; @@ -350,4 +420,33 @@ mod test { assert_eq!(deposit_u32(0xfdfcfbfa, 8, 24, 0xbdbcbbba), Some(0xbcbbbafa)); assert_eq!(deposit_u32(0xfdfcfbfa, 0, 32, 0xbdbcbbba), Some(0xbdbcbbba)); } + + #[test] + fn test_write_data_u16() { + let mut data: [u8; 1] = [0; 1]; + let ret = write_data_u16(&mut data, 0x11); + assert!(ret && data[0] == 0x11); + let mut data: [u8; 2] = [0; 2]; + let ret = write_data_u16(&mut data, 0x1122); + assert!(ret && data[0] == 0x22 && data[1] == 0x11); + let mut data: [u8; 3] = [0; 3]; + let ret = write_data_u16(&mut data, 0x1122); + assert!(!ret); + } + + #[test] + fn test_write_data_u32() { + let mut data: [u8; 1] = [0; 1]; + let ret = write_data_u32(&mut data, 0x11); + assert!(ret && data[0] == 0x11); + let mut data: [u8; 2] = [0; 2]; + let ret = write_data_u32(&mut data, 0x1122); + assert!(ret && data[0] == 0x22 && data[1] == 0x11); + let mut data: [u8; 3] = [0; 3]; + let ret = write_data_u32(&mut data, 0x112233); + assert!(!ret); + let mut data: [u8; 4] = [0; 4]; + let ret = write_data_u32(&mut data, 0x11223344); + assert!(ret && data[0] == 0x44 && data[1] == 0x33 && data[2] == 0x22 && data[3] == 0x11); + } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index dcc481b8e..3ac51d3e9 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -35,6 +35,7 @@ use pci::{ PciBus, PciDevOps, PciError, }; use util::byte_code::ByteCode; +use util::num_ops::write_data_u32; use util::offset_of; use vmm_sys_util::eventfd::EventFd; @@ -744,24 +745,7 @@ impl VirtioPciDevice { } }; - match data.len() { - 1 => data[0] = value as u8, - 2 => { - LittleEndian::write_u16(data, value as u16); - } - 4 => { - LittleEndian::write_u32(data, value); - } - _ => { - error!( - "invalid data length for reading pci common config: offset 0x{:x}, data len {}", - offset, data.len() - ); - return false; - } - }; - - true + write_data_u32(data, value) }; let cloned_pci_device = self.clone(); -- Gitee From d0e612d9c60df6b1c3a18e7cabf30ab3e11cd8ab Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 23 Nov 2022 11:29:41 +0800 Subject: [PATCH 0477/2187] Fix: Duplicate data reading code is optimized. Data read from array operations occur in multiple files. Unify this operation to reduce code duplication. Signed-off-by: Mingwang Li --- acpi/src/acpi_device.rs | 48 +++++++-------------- devices/src/legacy/pflash.rs | 16 +++---- devices/src/legacy/pl011.rs | 12 +++--- usb/src/xhci/xhci_regs.rs | 60 ++++++++------------------ util/src/num_ops.rs | 83 ++++++++++++++++++++++++++++++++++++ virtio/src/virtio_pci.rs | 18 +++----- 6 files changed, 132 insertions(+), 105 deletions(-) diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index 558d57bc0..9c6c2674f 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -13,10 +13,12 @@ use std::time::Instant; use address_space::GuestAddress; -use byteorder::{ByteOrder, LittleEndian}; use log::error; -use util::{num_ops::write_data_u16, time::NANOSECONDS_PER_SECOND}; +use util::{ + num_ops::{read_data_u16, write_data_u16}, + time::NANOSECONDS_PER_SECOND, +}; // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; @@ -87,31 +89,17 @@ impl AcpiPmEvent { pub fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { match offset { 0 => { - let value: u16 = match data.len() { - 1 => data[0] as u16, - 2 => LittleEndian::read_u16(data), - n => { - error!( - "Invalid data length {} for writing PM status register, offset is {}", - n, offset - ); - return false; - } - }; + let mut value = 0; + if !read_data_u16(data, &mut value) { + return false; + } self.status &= !value; } 2 => { - let value: u16 = match data.len() { - 1 => data[0] as u16, - 2 => LittleEndian::read_u16(data), - n => { - error!( - "Invalid data length {} for writing PM enable register, offset is {}", - n, offset - ); - return false; - } - }; + let mut value = 0; + if !read_data_u16(data, &mut value) { + return false; + } self.enable = value; } _ => { @@ -139,14 +127,10 @@ impl AcpiPmCtrl { // Return true when guest want poweroff. pub fn write(&mut self, data: &[u8], _base: GuestAddress, _offset: u64) -> bool { - let value: u16 = match data.len() { - 1 => data[0] as u16, - 2 => LittleEndian::read_u16(data), - n => { - error!("Invalid data length {} for writing PM control register", n); - return false; - } - }; + let mut value = 0; + if !read_data_u16(data, &mut value) { + return false; + } self.control = value & !ACPI_BITMASK_SLEEP_ENABLE; value & ACPI_BITMASK_SLEEP_ENABLE != 0 } diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 13487eb3b..d0ded2a27 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -18,10 +18,9 @@ use super::error::LegacyError; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use anyhow::{anyhow, bail, Context, Result}; -use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; -use util::num_ops::{deposit_u32, extract_u32, write_data_u32}; +use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; pub struct PFlash { /// Has backend file or not. has_backend: bool, @@ -819,15 +818,10 @@ impl SysBusDevOps for PFlash { } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { - let value: u32 = match data.len() { - 1 => data[0] as u32, - 2 => LittleEndian::read_u16(data).into(), - 4 => LittleEndian::read_u32(data), - n => { - error!("Invalid data length {}", n); - return false; - } - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } let cmd: u8 = data[0]; let data_len: u8 = data.len() as u8; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 406dce4f2..23617d1fb 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -21,7 +21,6 @@ use acpi::{ }; use address_space::GuestAddress; use anyhow::{anyhow, Context, Result}; -use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, @@ -35,6 +34,7 @@ use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; +use util::num_ops::read_data_u32; use vmm_sys_util::eventfd::EventFd; const PL011_FLAG_TXFE: u8 = 0x80; const PL011_FLAG_RXFF: u8 = 0x40; @@ -305,12 +305,10 @@ impl SysBusDevOps for PL011 { } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { - let value = match data.len() { - 1 => data[0] as u32, - 2 => LittleEndian::read_u16(data) as u32, - 4 => LittleEndian::read_u32(data) as u32, - _ => return false, - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } match offset >> 2 { 0 => { diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index a2323850a..fddb72d15 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -16,13 +16,13 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; -use util::num_ops::{read_u32, write_data_u32, write_u64_high, write_u64_low}; +use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; use crate::config::*; use crate::usb::UsbPort; use crate::xhci::xhci_controller::{XhciDevice, XhciEvent}; use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; -use anyhow::{bail, Result}; +use anyhow::Result; /// Capability offset or size. pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; @@ -370,13 +370,10 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { let xhci = xhci_dev.clone(); let oper_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { debug!("oper write {:x} {:x}", addr.0, offset); - let value = match read_data(data) { - Ok(v) => v, - Err(e) => { - error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); - return false; - } - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } let mut locked_xhci = xhci.lock().unwrap(); match offset { XHCI_OPER_REG_USBCMD => { @@ -512,13 +509,10 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { let xhci = xhci_dev.clone(); let runtime_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { - let value = match read_data(data) { - Ok(v) => v, - Err(e) => { - error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); - return false; - } - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } debug!("runtime write {:x} {:x} {:x}", addr.0, offset, value); if offset < 0x20 { error!("Runtime write not implemented: offset {}", offset); @@ -597,13 +591,10 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { }; let xhci = xhci_dev.clone(); let doorbell_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { - let value = match read_data(data) { - Ok(v) => v, - Err(e) => { - error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); - return false; - } - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } debug!("doorbell write {:x} {:x}", addr.0, offset); if !xhci.lock().unwrap().running() { error!("Failed to write doorbell, XHCI is not running"); @@ -660,13 +651,10 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { let port = xhci_port.clone(); let port_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { - let value = match read_data(data) { - Ok(v) => v, - Err(e) => { - error!("Failed to read data: offset 0x{:x}, {:?}", offset, e); - return false; - } - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } debug!("port write {:x} {:x} {:x}", addr.0, offset, value); match offset { XHCI_PORTSC => { @@ -754,15 +742,3 @@ fn xhci_portsc_ls_write(port: &mut XhciPort, old_pls: u32, new_pls: u32) -> u32 } 0 } - -fn read_data(data: &[u8]) -> Result { - let value = match data.len() { - 1 => data[0] as u32, - 2 => LittleEndian::read_u16(data) as u32, - 4 => LittleEndian::read_u32(data), - _ => { - bail!("Invalid data length: data len {}", data.len()); - } - }; - Ok(value) -} diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 96fb1cfce..11f4a59f2 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -321,6 +321,65 @@ pub fn write_data_u32(data: &mut [u8], value: u32) -> bool { true } +/// Read the given array to an u32, returns the bool. +/// +/// # Arguments +/// +/// * `data` - The array of u8. +/// * `value` - The u32 value +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::read_data_u32; +/// +/// let mut value = 0; +/// let ret = read_data_u32(&[0x11, 0x22, 0x33, 0x44], &mut value); +/// assert!(ret && value == 0x44332211); +/// ``` +pub fn read_data_u32(data: &[u8], value: &mut u32) -> bool { + *value = match data.len() { + 1 => data[0] as u32, + 2 => LittleEndian::read_u16(data) as u32, + 4 => LittleEndian::read_u32(data), + _ => { + error!("Invalid data length: data len {}", data.len()); + return false; + } + }; + true +} + +/// Read the given array to an u16, returns the bool. +/// +/// # Arguments +/// +/// * `data` - The array of u8. +/// * `value` - The u16 value +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::read_data_u16; +/// +/// let mut value = 0; +/// let ret = read_data_u16(&[0x11, 0x22], &mut value); +/// assert!(ret && value == 0x2211); +/// ``` +pub fn read_data_u16(data: &[u8], value: &mut u16) -> bool { + *value = match data.len() { + 1 => data[0] as u16, + 2 => LittleEndian::read_u16(data), + _ => { + error!("Invalid data length: data len {}", data.len()); + return false; + } + }; + true +} + #[cfg(test)] mod test { use super::*; @@ -449,4 +508,28 @@ mod test { let ret = write_data_u32(&mut data, 0x11223344); assert!(ret && data[0] == 0x44 && data[1] == 0x33 && data[2] == 0x22 && data[3] == 0x11); } + + #[test] + fn test_read_data_u16() { + let mut value = 0; + let ret = read_data_u16(&[0x11], &mut value); + assert!(ret && value == 0x11); + let ret = read_data_u16(&[0x11, 0x22], &mut value); + assert!(ret && value == 0x2211); + let ret = read_data_u16(&[0x11, 0x22, 0x33], &mut value); + assert!(!ret); + } + + #[test] + fn test_read_data_u32() { + let mut value = 0; + let ret = read_data_u32(&[0x11], &mut value); + assert!(ret && value == 0x11); + let ret = read_data_u32(&[0x11, 0x22], &mut value); + assert!(ret && value == 0x2211); + let ret = read_data_u32(&[0x11, 0x22, 0x33], &mut value); + assert!(!ret); + let ret = read_data_u32(&[0x11, 0x22, 0x33, 0x44], &mut value); + assert!(ret && value == 0x44332211); + } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 3ac51d3e9..6b78a2e26 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -35,7 +35,7 @@ use pci::{ PciBus, PciDevOps, PciError, }; use util::byte_code::ByteCode; -use util::num_ops::write_data_u32; +use util::num_ops::{read_data_u32, write_data_u32}; use util::offset_of; use vmm_sys_util::eventfd::EventFd; @@ -752,18 +752,10 @@ impl VirtioPciDevice { let cloned_mem_space = self.sys_mem.clone(); let cloned_gsi_routes = self.gsi_msi_routes.clone(); let common_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { - let value = match data.len() { - 1 => data[0] as u32, - 2 => LittleEndian::read_u16(data) as u32, - 4 => LittleEndian::read_u32(data), - _ => { - error!( - "Invalid data length for writing pci common config: offset 0x{:x}, data len {}", - offset, data.len() - ); - return false; - } - }; + let mut value = 0; + if !read_data_u32(data, &mut value) { + return false; + } let old_dev_status = cloned_pci_device .common_config .lock() -- Gitee From ad6a130e92a5266cd6b5f5d10ffa73420758d388 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 21:51:50 -0500 Subject: [PATCH 0478/2187] virtiofs: Enhanced the verification of vhost message Increase or enhance the size check of requset and buf before Vhost message processing. Signed-off-by: Li HuaChao --- vhost_user_fs/src/vhost_user_server.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 55672f6f1..3d0ff1a97 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -229,12 +229,13 @@ impl VhostUserServerHandler { buf: &'a [u8], len: usize, ) -> Result<&'a D> { - if size_of::() != len { + if !self.is_valid_request(hdr, len, size_of::()) { bail!( - "Failed to get msg body for request {}, len {}, payload size {}", + "Failed to get msg body for request {}, len {}, payload size {}, hdr.size {}", hdr.request, len, - size_of::() + size_of::(), + hdr.size ); } @@ -311,6 +312,10 @@ impl VhostUserServerHandler { Ok(()) } + fn is_valid_request(&self, hdr: &VhostUserMsgHdr, size: usize, expected: usize) -> bool { + (hdr.size as usize == expected) && (size == expected) && !hdr.is_reply() + } + fn process_request( &mut self, hdr: &VhostUserMsgHdr, @@ -320,8 +325,8 @@ impl VhostUserServerHandler { ) -> Result<()> { match VhostUserMsgReq::from(hdr.request) { VhostUserMsgReq::GetFeatures => { - if len != 0 { - bail!("The length {} of getting features is invalid", len); + if !self.is_valid_request(hdr, len, 0) { + bail!("Invalid request size of GetFeatures"); } let features = self.backend.lock().unwrap().get_features()?; @@ -337,9 +342,10 @@ impl VhostUserServerHandler { self.backend.lock().unwrap().set_features(*features)?; } VhostUserMsgReq::SetOwner => { - if len != 0 { - bail!("The length {} of setting owner is invalid", len); + if !self.is_valid_request(hdr, len, 0) { + bail!("Invalid request size of SetOwner"); } + self.backend.lock().unwrap().set_owner()?; } VhostUserMsgReq::SetMemTable => { -- Gitee From b68a386eba48fd2a674575e8b4a8cb4c3f4f66e8 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 22 Nov 2022 23:26:10 -0500 Subject: [PATCH 0479/2187] virtiofsd: Added address verification for set_vring_addr To add the address verification for queue_info.config.desc, ensure that the values of desc_table_host,avail_ring_host,and used_ring_host are valid values. Signed-off-by: Li HuaChao --- vhost_user_fs/src/virtio_fs.rs | 60 +++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index dbeae0d46..16655b5a8 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -337,28 +337,44 @@ impl VhostUserReqHandler for VirtioFs { let used_addr = self.get_guest_address(used_ring)?; let avail_addr = self.get_guest_address(avail_ring)?; - self.config - .get_mut_queue_config(queue_index as usize) - .map(|queue_info| { - queue_info.config.desc_table = GuestAddress(desc_addr); - queue_info.config.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(GuestAddress(desc_addr)) - .unwrap_or(0); - - queue_info.config.avail_ring = GuestAddress(avail_addr); - queue_info.config.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(GuestAddress(avail_addr)) - .unwrap_or(0); - - queue_info.config.used_ring = GuestAddress(used_addr); - queue_info.config.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(GuestAddress(used_addr)) - .unwrap_or(0); - }).with_context(|| - format!("Failed to set vring addr, index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", - queue_index, desc_addr, avail_addr, used_addr, - ) - )?; + if let Err(_ret) = + self.config + .get_mut_queue_config(queue_index as usize) + .map(|queue_info| { + queue_info.config.desc_table = GuestAddress(desc_addr); + queue_info.config.addr_cache.desc_table_host = cloned_mem_space + .get_host_address(GuestAddress(desc_addr)) + .unwrap_or(0); + + queue_info.config.avail_ring = GuestAddress(avail_addr); + queue_info.config.addr_cache.avail_ring_host = cloned_mem_space + .get_host_address(GuestAddress(avail_addr)) + .unwrap_or(0); + + queue_info.config.used_ring = GuestAddress(used_addr); + queue_info.config.addr_cache.used_ring_host = cloned_mem_space + .get_host_address(GuestAddress(used_addr)) + .unwrap_or(0); + + if queue_info.config.addr_cache.desc_table_host == 0 + || queue_info.config.addr_cache.avail_ring_host == 0 + || queue_info.config.addr_cache.used_ring_host == 0 + { + return Err(()); + } + + Ok(()) + }) + { + bail!( + "Failed to set vring addr, got host address failed. Index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", + queue_index, + desc_addr, + avail_addr, + used_addr + ); + } + Ok(()) } -- Gitee From 0ead8ba457566e851164003cfead6e3d4d514fd6 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 22 Nov 2022 20:10:26 +0800 Subject: [PATCH 0480/2187] Fix: When either CA or CS is set, an event is sent. According to spec 5.4.5, when the command is running, CS or CA is set to 1, the command is stoped and an event is reported. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_regs.rs | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index fddb72d15..4988e1337 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -83,7 +83,7 @@ const XHCI_OPER_REG_DCBAAP_LO: u64 = 0x30; const XHCI_OPER_REG_DCBAAP_HI: u64 = 0x34; const XHCI_OPER_REG_CONFIG: u64 = 0x38; const XHCI_OPER_PAGESIZE: u32 = 1; -/// Command Ring Control Register RCS/CS/CA/CRR mask. +/// Command Ring Control Register RCS/CS/CA mask. const XHCI_CRCR_CTRL_LO_MASK: u32 = 0xffffffc7; /// Command Ring Pointer Mask. const XHCI_CRCR_CRP_MASK: u64 = !0x3f; @@ -408,40 +408,29 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { } XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl = value & XHCI_OPER_NE_MASK, XHCI_OPER_REG_CMD_RING_CTRL_LO => { - let mut crc_lo = value; - if crc_lo & CMD_RING_CTRL_CRR != CMD_RING_CTRL_CRR { - locked_xhci - .cmd_ring - .set_cycle_bit(crc_lo & CMD_RING_CTRL_RCS == CMD_RING_CTRL_RCS); - } - if crc_lo & (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) - == (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) - && (value & CMD_RING_CTRL_CRR) == CMD_RING_CTRL_CRR + let mut crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); + crc_lo = (value & XHCI_CRCR_CTRL_LO_MASK) | (crc_lo & CMD_RING_CTRL_CRR); + locked_xhci.oper.cmd_ring_ctrl = + write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); + } + XHCI_OPER_REG_CMD_RING_CTRL_HI => { + let crc_hi = (value as u64) << 32; + let mut crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); + if crc_lo & (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) != 0 + && (crc_lo & CMD_RING_CTRL_CRR) == CMD_RING_CTRL_CRR { let event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::CommandRingStopped); crc_lo &= !CMD_RING_CTRL_CRR; - locked_xhci.oper.cmd_ring_ctrl = - write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); if let Err(e) = locked_xhci.send_event(&event, 0) { error!("Failed to send event: {:?}", e); } - } - crc_lo &= !(CMD_RING_CTRL_CA | CMD_RING_CTRL_CS); - locked_xhci.oper.cmd_ring_ctrl = write_u64_low( - locked_xhci.oper.cmd_ring_ctrl, - crc_lo & XHCI_CRCR_CTRL_LO_MASK, - ); - } - XHCI_OPER_REG_CMD_RING_CTRL_HI => { - let crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); - if (crc_lo & CMD_RING_CTRL_CRR) != CMD_RING_CTRL_CRR { - let crc_hi = (value as u64) << 32; + } else { let addr = (crc_hi | crc_lo as u64) & XHCI_CRCR_CRP_MASK; locked_xhci.cmd_ring.init(addr); - locked_xhci.oper.cmd_ring_ctrl = - write_u64_high(locked_xhci.oper.cmd_ring_ctrl, value); } + crc_lo &= !(CMD_RING_CTRL_CA | CMD_RING_CTRL_CS); + locked_xhci.oper.cmd_ring_ctrl = write_u64_low(crc_hi, crc_lo); } XHCI_OPER_REG_DCBAAP_LO => { locked_xhci.oper.dcbaap = write_u64_low(locked_xhci.oper.dcbaap, value & 0xffffffc0) -- Gitee From ef0e6bc326898ea97ef650d9a02f52cdafe5fd0d Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 24 Nov 2022 15:35:49 +0800 Subject: [PATCH 0481/2187] net/tap: fix the problem that if tap interface is not exist. 1. If `/sys/class/net/tap_name/tun_flags` path is not exist, we are allowed to not verify the tap flags. For example, tap device is created in a namespace, the path can't be find in the current environment. It needs to be find by `ip netns exec ns_id ls /sys/class/net/tap_name/tun_flags`. 2. Modify the error message that check tap features does not contain `IFF_MULTI_QUEUE` flag. Signed-off-by: Xinle.Guo --- util/src/tap.rs | 5 +---- virtio/src/net.rs | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/util/src/tap.rs b/util/src/tap.rs index 1d24520d1..dbe5808a6 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -108,10 +108,7 @@ impl Tap { } if (features & IFF_MULTI_QUEUE == 0) && queue_pairs > 1 { - bail!( - "Tap device doesn't support mq, but command set queue pairs {}.", - queue_pairs - ); + bail!("Needs multiqueue, but no kernel support for IFF_MULTI_QUEUE available"); } Ok(Tap { file }) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 07add93ed..1fef570b8 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -757,7 +757,8 @@ fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> { let path = format!("/sys/class/net/{}/tun_flags", dev_name); let tap_path = Path::new(&path); if !tap_path.exists() { - bail!("Tap path doesn't exist"); + warn!("Tap interface does not exist"); + return Ok(()); } let is_mq = queue_pair > 1; -- Gitee From 07ee8cd61691b5583d8625920a9821d8f43c8c30 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 22 Nov 2022 12:54:29 +0800 Subject: [PATCH 0482/2187] pci: validate offset and len before reading config Some callers forgot to check boundary when reading config, add the validation at only one place that in the basic function in pci::config, to make sure that do not read accross over the config space's boundary, either read no more than 4B, according PCIe spec. Signed-off-by: Zhang Bo --- .../src/standard_vm/aarch64/pci_host_root.rs | 16 ---------------- machine/src/standard_vm/x86_64/ich9_lpc.rs | 10 +--------- machine/src/standard_vm/x86_64/mch.rs | 17 +---------------- pci/src/config.rs | 10 ++++++++-- pci/src/root_port.rs | 9 --------- usb/src/xhci/xhci_pci.rs | 11 +---------- virtio/src/virtio_pci.rs | 12 +----------- 7 files changed, 12 insertions(+), 73 deletions(-) diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 3513562e4..77e47f476 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -12,7 +12,6 @@ use std::sync::{Arc, Mutex, Weak}; -use log::{debug, error}; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, @@ -80,21 +79,6 @@ impl PciDevOps for PciHostRoot { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - let size = data.len(); - if size > 4 { - error!( - "Failed to read PciHostRoot config space: Invalid data size {}", - size - ); - return; - } - if offset + size > PCI_CONFIG_SPACE_SIZE { - debug!( - "Failed to read PciHostRoot config space: offset {}, size {}, config space size {}", - offset, size, PCI_CONFIG_SPACE_SIZE - ); - return; - } self.config.read(offset, data); } diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 3dcd49cee..b42650642 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -20,7 +20,7 @@ use crate::standard_vm::Result; use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use anyhow::Context; -use log::{debug, error}; +use log::error; use pci::config::CLASS_CODE_ISA_BRIDGE; use pci::config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, @@ -264,14 +264,6 @@ impl PciDevOps for LPCBridge { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - let size = data.len(); - if offset + size > PCI_CONFIG_SPACE_SIZE || size > 4 { - debug!( - "Failed to read LPC bridge's pci config space: offset {}, data size {}", - offset, size - ); - return; - } self.config.read(offset, data); } diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 6ee7f18c2..491075bb4 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -14,7 +14,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{Region, RegionOps}; use anyhow::{bail, Result}; -use log::{debug, error}; +use log::error; use pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, @@ -139,21 +139,6 @@ impl PciDevOps for Mch { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - let size = data.len(); - if size > 4 { - error!( - "Failed to read MCH config space: Invalid data size {}", - size - ); - return; - } - if offset + size > PCI_CONFIG_SPACE_SIZE { - debug!( - "Failed to read MCH config space: offset {}, size {}, config space size {}", - offset, size, PCI_CONFIG_SPACE_SIZE - ); - return; - } self.config.read(offset, data); } diff --git a/pci/src/config.rs b/pci/src/config.rs index 7c5e02c48..7c341b83e 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -14,6 +14,7 @@ use std::collections::HashSet; use std::sync::{Arc, Mutex}; use address_space::Region; +use log::error; use crate::msix::Msix; use crate::PciError; @@ -484,11 +485,15 @@ impl PciConfig { /// * `offset` - Offset in the configuration space from which to read. /// * `data` - Buffer to put read data. pub fn read(&self, offset: usize, buf: &mut [u8]) { + if let Err(err) = self.validate_config_boundary(offset, buf) { + error!("invalid read: {:?}", err); + return; + } let size = buf.len(); buf[..].copy_from_slice(&self.config[offset..(offset + size)]); } - fn validate_config_boundary(&mut self, offset: usize, data: &[u8]) -> Result<()> { + fn validate_config_boundary(&self, offset: usize, data: &[u8]) -> Result<()> { if offset + data.len() > self.config.len() { return Err(anyhow!(PciError::InvalidConf( "config size".to_string(), @@ -515,7 +520,8 @@ impl PciConfig { /// * `data` - Data to write. /// * `dev_id` - Device id to send MSI/MSI-X. pub fn write(&mut self, mut offset: usize, data: &[u8], dev_id: u16) { - if self.validate_config_boundary(offset, data).is_err() { + if let Err(err) = self.validate_config_boundary(offset, data) { + error!("invalid write: {:?}", err); return; } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 6168cb0bf..4d5fbb5c3 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -362,15 +362,6 @@ impl PciDevOps for RootPort { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - let size = data.len(); - if offset + size > PCIE_CONFIG_SPACE_SIZE || size > 4 { - error!( - "Failed to read pcie config space at offset {} with data size {}", - offset, size - ); - return; - } - self.config.read(offset, data); } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 61e73fb72..d14538a4b 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -17,8 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; use pci::config::{ PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, - PCIE_CONFIG_SPACE_SIZE, PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, - SUB_CLASS_CODE, VENDOR_ID, + PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, }; use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; @@ -240,14 +239,6 @@ impl PciDevOps for XhciPciDevice { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - let data_size = data.len(); - if offset + data_size > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { - error!( - "Failed to read pci config space at offset 0x{:x} with data size {}", - offset, data_size - ); - return; - } self.pci_config.read(offset, data); } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index dcc481b8e..1fed3ba39 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1274,17 +1274,7 @@ impl PciDevOps for VirtioPciDevice { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - let data_size = data.len(); - let end = offset + data_size; - if end > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { - error!( - "Failed to read pcie config space at offset 0x{:x} with data size {}", - offset, data_size - ); - return; - } - - self.do_cfg_access(offset, end, false); + self.do_cfg_access(offset, offset + data.len(), false); self.config.read(offset, data); } -- Gitee From 870d8b0961084e57bf352645624ac9b6ca6b6c8f Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 5 Sep 2022 21:38:57 +0800 Subject: [PATCH 0483/2187] migration: modify migration operation and help document This patch modify the QMP command format, add operation for socket mode of live-migration. In addition, add more detailed help content. Signed-off-by: Xinle.Guo --- docs/migration.md | 40 +++++++++++++++++++--------------- machine_manager/src/cmdline.rs | 5 +++-- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/docs/migration.md b/docs/migration.md index c9ab509e5..51b655b1d 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -4,16 +4,16 @@ Virtual machine live migration is the key feature provided by StratoVirt. It needs to execute virtual machine migration when any of the following happens: -1. Server overload: when a source server is overloaded, a set of the VMs from this server is migrated to an underloaded +- Server overload: when a source server is overloaded, a set of the VMs from this server is migrated to an underloaded server using VM migration technique. -2. Server maintenance: if there is a need for server maintenance, VMs from the source server are migrated to another server. -3. Server fault: whenever there is server fault, VMs are migrated from the faulty server to the target server. +- Server maintenance: if there is a need for server maintenance, VMs from the source server are migrated to another server. +- Server fault: whenever there is server fault, VMs are migrated from the faulty server to the target server. ## Transports The migration stream can be passed over any transport as following: -1. TCP mode migration: using tcp sockets to do the migration. -2. UNIX mode migration: using unix sockets to do the migration. +- TCP mode migration: using tcp sockets to do the migration. +- UNIX mode migration: using unix sockets to do the migration. Note: UNIX mode only supports migrate two VMs on the same host OS. TCP mode supports migrate both on the same or different host OS. @@ -45,18 +45,24 @@ Launch the destination VM: -incoming tcp:192.168.0.1:4446 \ ``` -Note: The destination VM command line parameter needs to be consistent with the source VM. If uses UNIX mode, the -parameter `-incoming tcp:192.168.0.1:4446` replace with `-incoming unix:/tmp/stratovirt-migrate.socket`. The following -are the same. +Note: +- The destination VM command line parameter needs to be consistent with the source VM. +- If it is necessary to change the data transmission from tcp network protocol to unix socket, + the parameter `-incoming tcp:192.168.0.1:4446` needs to be replaced with `-incoming unix:/tmp/stratovirt-migrate.socket`. +- Unix socket protocol only supports migrate two VMs on the same host OS. Start to send migration for the source VM: ```shell $ ncat -U path/to/socket1 -{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -{"execute":"migrate", "arguments":{"uri":"tcp:192.168.0.1:4446"}} -{"return":{}} +-> {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +<- {"execute":"migrate", "arguments":{"uri":"tcp:192.168.0.1:4446"}} +-> {"return":{}} ``` +Note: +- If using unix socket protocol to migrate vm, you need to modify QMP command of `"uri":"tcp:192.168.0.1:4446"` to + `"uri":"unix:/tmp/stratovirt-migrate.socket"`. + When finish executing the command line, the live migration is start. in a moment, the source VM should be successfully migrated to the destination VM. @@ -65,9 +71,9 @@ migrated to the destination VM. If you want to cancel the live migration, executing the following command: ```shell $ ncat -U path/to/socket1 -{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -{"execute":"migrate_cancel"} -{"return":{}} +-> {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +<- {"execute":"migrate_cancel"} +-> {"return":{}} ``` ## Query migration state @@ -75,9 +81,9 @@ $ ncat -U path/to/socket1 Use QMP command `query-migrate` to check migration state: ```shell $ ncat -U path/to/socket -{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -{"execute":"query-migrate"} -{"return":{"status":"completed"}} +-> {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +<- {"execute":"query-migrate"} +-> {"return":{"status":"completed"}} ``` Now there are 5 states during migration: diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 442eaf073..319724e7d 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -225,8 +225,9 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("incoming") .long("incoming") - .help("wait for the URI to be specified via migrate_incoming") - .value_name("incoming") + .value_name("tcp:ip:port> or -incoming or -incoming Date: Mon, 5 Sep 2022 21:41:54 +0800 Subject: [PATCH 0484/2187] migration: verify the validity of the IP address and port number for `incoming` command If the IP address and port arn not verified, VM will coredump during live migration. So, before using IP address to bind the server, we should check each digit of the IP and port. Signed-off-by: Xinle.Guo --- machine_manager/src/config/incoming.rs | 31 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/machine_manager/src/config/incoming.rs b/machine_manager/src/config/incoming.rs index 3fb91656d..290040807 100644 --- a/machine_manager/src/config/incoming.rs +++ b/machine_manager/src/config/incoming.rs @@ -10,10 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::VmConfig; +use std::net::Ipv4Addr; + use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; +use super::VmConfig; + #[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)] pub enum MigrateMode { File, @@ -44,10 +47,20 @@ pub fn parse_incoming_uri(uri: &str) -> Result<(MigrateMode, String)> { } } else if parse_vec.len() == 3 { match MigrateMode::from(parse_vec[0]) { - MigrateMode::Tcp => Ok(( - MigrateMode::Tcp, - format!("{}:{}", parse_vec[1], parse_vec[2]), - )), + MigrateMode::Tcp => { + if parse_vec[1].parse::().is_err() { + bail!("Invalid ip address {}", parse_vec[1]); + } + if parse_vec[2].parse::().is_err() { + bail!("Invalid ip port {}", parse_vec[2]); + } + + return Ok(( + MigrateMode::Tcp, + format!("{}:{}", parse_vec[1], parse_vec[2]), + )); + } + _ => bail!("Invalid incoming uri {}", uri), } } else { @@ -106,6 +119,14 @@ mod tests { let incoming_case3 = "tcp:192.168.1.2:2:2"; let result_3 = parse_incoming_uri(incoming_case3); assert!(result_3.is_err()); + + let incoming_case4 = "tcp:300.168.1.2:22"; + let result_4 = parse_incoming_uri(incoming_case4); + assert!(result_4.is_err()); + + let incoming_case5 = "tcp:192.168.1.2:65568"; + let result_5 = parse_incoming_uri(incoming_case5); + assert!(result_5.is_err()); } #[test] -- Gitee From 21a012f7436c96424380dbcd4178ed31ffc82fc9 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Thu, 24 Nov 2022 20:39:37 +0800 Subject: [PATCH 0485/2187] Cargo.lock: remove `Cargo.lock` file from .gitignore It is suggested from the rust community that the `Cargo.lock` should be managed. If the dependent crates are updated, the `Cargo.lock` also needs to be updated. Signed-off-by: Xinle.Guo --- .gitignore | 5 ++-- Cargo.lock | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index c58a49ac6..68d13feb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -target -# Remove Cargo.lock from gitignore. +# Don't add Cargo.lock file to gitignore. # Ref: https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock +target **/*.rs.bk # Ignore configuration directory generated by IDEA. diff --git a/Cargo.lock b/Cargo.lock index ccc9f91c3..5b4a92b41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,15 @@ dependencies = [ "util", ] +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + [[package]] name = "address_space" version = "2.2.0" @@ -58,6 +67,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.19" @@ -85,6 +100,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -257,6 +287,22 @@ dependencies = [ "libc", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "backtrace", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "heck" version = "0.3.3" @@ -456,6 +502,24 @@ dependencies = [ "util", ] +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.15.0" @@ -595,6 +659,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustls" version = "0.20.7" @@ -869,6 +939,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "vfio" version = "2.2.0" @@ -905,6 +981,7 @@ dependencies = [ "capng", "devices", "errno", + "error-chain", "hypervisor", "libc", "libseccomp-sys", -- Gitee From 5dcf99c8047e371a851e36c0662514ff9b979918 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 03:46:04 -0500 Subject: [PATCH 0486/2187] virtiofsd: fix MAX_PATH_LEN to 4096 The maximum length of the file name is 255 bytes. The maximum length of the combination of file name and path name is 4096 bytes. So, change MAX_PATH_LEN to 4096.(Linux platform) Signed-off-by: Li HuaChao --- vhost_user_fs/src/fs_ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 83b4e9fcd..d7836e8d4 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -const MAX_PATH_LEN: usize = 256; +const MAX_PATH_LEN: usize = 4096; const OFFSET_MAX: u64 = 0x7fffffffffffffff; use std::ffi::CString; -- Gitee From f3addd45663e53ad82a87755ee1e0518b12a3077 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 07:54:50 -0500 Subject: [PATCH 0487/2187] virtiofs: add path name empty check for some fuse opration For some file operations, you first need to check if the name is empty. Signed-off-by: Li HuaChao --- vhost_user_fs/src/fuse_proc.rs | 58 ++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 28da4336b..b5558a8b7 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -20,8 +20,8 @@ use std::ffi::CString; use std::mem; use std::sync::{Arc, Mutex}; -fn is_safe_path(path: CString) -> bool { - let path_str = match path.into_string() { +fn is_safe_path(path: &CString) -> bool { + let path_str = match path.clone().into_string() { Ok(str_) => str_, Err(_e) => return false, }; @@ -39,6 +39,12 @@ fn is_safe_path(path: CString) -> bool { true } +fn is_empty_path(path: &CString) -> bool { + let bytes = path.clone().into_bytes(); + + bytes[0] == 0x0 +} + /// Process the fuse message of FUSE_LOOKUP. /// /// # Arguments @@ -279,7 +285,11 @@ pub fn do_fuse_symlink( } }; - if !is_safe_path(link_name.clone()) { + if is_empty_path(&link_name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&link_name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -345,7 +355,11 @@ pub fn do_fuse_mknod( } }; - if !is_safe_path(name.clone()) { + if is_empty_path(&name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -411,7 +425,11 @@ pub fn do_fuse_mkdir( } }; - if !is_safe_path(name.clone()) { + if is_empty_path(&name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -469,7 +487,11 @@ pub fn do_fuse_unlink( } }; - if !is_safe_path(name.clone()) { + if is_empty_path(&name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -502,7 +524,11 @@ pub fn do_fuse_rmdir( } }; - if !is_safe_path(name.clone()) { + if is_empty_path(&name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -551,7 +577,11 @@ pub fn do_fuse_rename( } }; - if !is_safe_path(oldname.clone()) || !is_safe_path(newname.clone()) { + if is_empty_path(&oldname) || is_empty_path(&newname) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&oldname) || !is_safe_path(&newname) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -596,7 +626,11 @@ pub fn do_fuse_link( } }; - if !is_safe_path(name.clone()) { + if is_empty_path(&name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } @@ -1468,7 +1502,11 @@ pub fn do_fuse_create( } }; - if !is_safe_path(name.clone()) { + if is_empty_path(&name) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); + } + + if !is_safe_path(&name) { return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); } -- Gitee From 7dec3c7f03961391ad1dae1c5c4221bacea90348 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 23 Nov 2022 20:58:27 -0500 Subject: [PATCH 0488/2187] virtiofsd: Don't allow file creation with FUSE_OPEN The FUSE filesystem doesn't currently support O_CREAT and O_TMPFILE. Because these two flags need to be used with mode. And FUSE did not implement support for mode parameter. Signed-off-by: Li HuaChao --- vhost_user_fs/src/fs.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index f0dc47307..21cf8c767 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -1080,6 +1080,11 @@ impl FileSystem { /// * `flags` - The flags used to open the file. /// * `fh` - The file handler is returned in the management of filesystem. pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 { + // File creation should be done with create and mknod fuse messages. + if (flags & (libc::O_CREAT as u32 | libc::O_TMPFILE as u32)) != 0 { + return libc::EINVAL; + } + let (inode_fd, file_type) = match self.find_inode(node_id) { Some(i) => (i.as_raw_fd(), i.file_type), None => { -- Gitee From 78de5ef9c3d5a0c250946b1c444df3c19d168f53 Mon Sep 17 00:00:00 2001 From: xuexiaowei Date: Thu, 24 Nov 2022 16:24:37 +0800 Subject: [PATCH 0489/2187] vhost-user-blk-pci: Adapt the BlkDevConfig check function. Should not check path_on_host when creating vhost-user-blk-pci device. Signed-off-by: Xiaowei Xue --- machine_manager/src/config/drive.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index becc6a684..2715d23ac 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -67,7 +67,7 @@ impl Default for BlkDevConfig { boot_index: None, chardev: None, socket_path: None, - aio: None, + aio: Some(AIO_NATIVE.to_string()), } } } @@ -219,7 +219,9 @@ impl ConfigCheck for BlkDevConfig { }; fake_drive.check()?; #[cfg(not(test))] - fake_drive.check_path()?; + if self.chardev.is_none() { + fake_drive.check_path()?; + } Ok(()) } -- Gitee From c6ef6f122beb49b148f3d587ce4a70be71e03294 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 25 Nov 2022 10:13:58 +0800 Subject: [PATCH 0490/2187] migration/seccomp: add `SYS_sreq` to system call whitelist Add `libc::SYS_sreq` to system call whitelist on aarch64 architecture. For live migration, if the whitelist does not contain the system call, it will cause `bad systemcall` error. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 8 ++++---- machine/src/micro_vm/syscall.rs | 8 ++++---- machine/src/standard_vm/aarch64/syscall.rs | 6 ++++-- machine/src/standard_vm/x86_64/syscall.rs | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 33bc0deea..9e0401a0c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -911,15 +911,15 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | -| microvm | 47 | 46 | -| q35 | 60 | 61 | +| microvm | 51 | 50 | +| q35 | 83 | 82 | * aarch64 | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | -| microvm | 45 | 45 | -| virt | 59 | 57 | +| microvm | 49 | 49 | +| virt | 82 | 79 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 8b41f52ad..ee5003054 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -48,10 +48,10 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 47 syscalls -/// * x86_64-unknown-musl: 46 syscalls -/// * aarch64-unknown-gnu: 45 syscalls -/// * aarch64-unknown-musl: 45 syscalls +/// * x86_64-unknown-gnu: 48 syscalls +/// * x86_64-unknown-musl: 47 syscalls +/// * aarch64-unknown-gnu: 46 syscalls +/// * aarch64-unknown-musl: 46 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 8e5752c75..7810128c2 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 77 syscalls -/// * aarch64-unknown-musl: 75 syscalls +/// * aarch64-unknown-gnu: 79 syscalls +/// * aarch64-unknown-musl: 76 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -148,6 +148,8 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_set_robust_list), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sched_getaffinity), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rseq), ] } diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index acf3be4b0..da84bb8ed 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 79 syscalls -/// * x86_64-unknown-musl: 78 syscalls +/// * x86_64-unknown-gnu: 80 syscalls +/// * x86_64-unknown-musl: 79 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ -- Gitee From 44e26f7c00b99bf03a1a15d4a345f71033f7ea89 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 25 Nov 2022 10:43:50 +0800 Subject: [PATCH 0491/2187] migration: add more limitations for live migration The limiting conditions are clearly stated at different stages of live migration in this patch. Users need to understand these limitations before do live migration operations. Signed-off-by: Xinle.Guo --- docs/migration.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/migration.md b/docs/migration.md index c9ab509e5..0c8778d1d 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -111,3 +111,17 @@ Some device attributes can't be changed: - `m` If hot plug device before migrate source vm, add newly replaced device command should be add to destination vm. + +Before live migration: +- source and destination host CPU needs to be the same architecture. +- the VMs image needs to be shared by source and destination. +- live migration may fail if the VM is performing lifecycle operations, such as reboot, shutdown. +- the command to startup the VM needs to be consistent on source and destination host. + +During live migration: +- source and destination networks cannot be disconnected. +- it is banned to operate VM lifecycle, inclues using the QMP command and executing in the VM. +- live migration time is affected by network performance, total memory of VM and applications. + +After live migration: +- it needs to wait for the source VM to release resources before fetching back the live migration operation. \ No newline at end of file -- Gitee From 9b022a990b3cf4c7d4c496607f83f6231cec1ec4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 22 Nov 2022 20:51:21 +0800 Subject: [PATCH 0492/2187] xhci: no need to return an error if slot or endpoint is invalid There is no need to return an error if slot or endpoint is invalid, just ignore it. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 21588effe..f843c50ca 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1136,7 +1136,14 @@ impl XhciDevice { /// Data plane pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { - let epctx = self.get_endpoint(slot_id, ep_id)?; + let epctx = match self.get_endpoint_ctx(slot_id, ep_id) { + Ok(epctx) => epctx, + Err(e) => { + error!("Kick endpoint error: {}", e); + // No need to return the error, just ignore it. + return Ok(()); + } + }; debug!( "kick_endpoint slotid {} epid {} dequeue {:x}", slot_id, ep_id, epctx.ring.dequeue @@ -1210,7 +1217,7 @@ impl XhciDevice { Ok(()) } - fn get_endpoint(&self, slot_id: u32, ep_id: u32) -> Result<&XhciEpContext> { + fn get_endpoint_ctx(&self, slot_id: u32, ep_id: u32) -> Result<&XhciEpContext> { self.check_slot_enabled(slot_id)?; if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&ep_id) { bail!("Invalid endpoint id {}", ep_id); -- Gitee From ca294950f1bbf44df567a82f4ec26f563a877221 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 23 Nov 2022 08:58:22 +0800 Subject: [PATCH 0493/2187] xhci: flush the transfers when disable the endpoint Flush the transfers when disable the endpoint to clean the resource. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index f843c50ca..b2eff4000 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1029,12 +1029,13 @@ impl XhciDevice { } fn disable_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { - let slot = &mut self.slots[(slot_id - 1) as usize]; - let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if !epctx.enabled { debug!("Endpoint already disabled"); return Ok(TRBCCode::Success); } + self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Invalid)?; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if self.oper.dcbaap != 0 { epctx.set_state(&self.mem_space, EP_DISABLED)?; } @@ -1528,7 +1529,7 @@ impl XhciDevice { /// Flush transfer in endpoint in some case such as stop endpoint. fn flush_ep_transfer(&mut self, slotid: u32, epid: u32, report: TRBCCode) -> Result { - info!("flush_ep_transfer slotid {} epid {}", slotid, epid); + debug!("flush_ep_transfer slotid {} epid {}", slotid, epid); let mut cnt = 0; let mut report = report; let xfers = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] -- Gitee From 834d41db533fa1c37fe0367a5377e936f4f73ffc Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 11 Nov 2022 11:01:26 +0800 Subject: [PATCH 0494/2187] pci: update bar mapping during writing config Bar is a part of PCI config, so, add the bar write callback funs into pci write config function. Right now each device checks whether bar space is written and deals with its update mapping callbacks by themselves, some one missed some checking branches, further more such behavior makes maintaince difficult. Nowadays ich9/mch seems have no bars related functions, just do a workaround to add a fake region to let write config ignore updating bar mapping. Thus no function changes for them now. Signed-off-by: Zhang Bo --- .../src/standard_vm/aarch64/pci_host_root.rs | 2 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 2 +- machine/src/standard_vm/x86_64/mch.rs | 2 +- pci/src/bus.rs | 10 ++- pci/src/config.rs | 85 ++++++++++++++++--- pci/src/host.rs | 10 ++- pci/src/root_port.rs | 33 +++---- usb/src/xhci/xhci_pci.rs | 36 +++----- vfio/src/vfio_pci.rs | 46 +++------- virtio/src/virtio_pci.rs | 40 +++------ 10 files changed, 146 insertions(+), 120 deletions(-) diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 77e47f476..5be4da5ed 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -83,7 +83,7 @@ impl PciDevOps for PciHostRoot { } fn write_config(&mut self, offset: usize, data: &[u8]) { - self.config.write(offset, data, 0); + self.config.write(offset, data, 0, None); } fn name(&self) -> String { diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index b42650642..a4d47307a 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -270,7 +270,7 @@ impl PciDevOps for LPCBridge { fn write_config(&mut self, offset: usize, data: &[u8]) { let end = offset + data.len(); - self.config.write(offset, data, 0); + self.config.write(offset, data, 0, None, None); if ranges_overlap( offset, end, diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 491075bb4..bb009bea2 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -144,7 +144,7 @@ impl PciDevOps for Mch { fn write_config(&mut self, offset: usize, data: &[u8]) { let end = offset + data.len(); - self.config.write(offset, data, 0); + self.config.write(offset, data, 0, None, None); if ranges_overlap(offset, end, PCIEXBAR as usize, PCIEXBAR as usize + 8) { if let Err(e) = self.update_pciexbar_mapping() { error!("{:?}", e); diff --git a/pci/src/bus.rs b/pci/src/bus.rs index c72d0b4e9..749e961f5 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -276,7 +276,15 @@ mod tests { } fn write_config(&mut self, offset: usize, data: &[u8]) { - self.config.write(offset, data, 0); + #[allow(unused_variables)] + self.config.write( + offset, + data, + 0, + #[cfg(target_arch = "x86_64")] + None, + None, + ); } fn name(&self) -> String { diff --git a/pci/src/config.rs b/pci/src/config.rs index c273c5635..b849d5138 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -17,11 +17,11 @@ use address_space::Region; use log::error; use crate::msix::Msix; -use crate::PciError; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, }; +use crate::{ranges_overlap, PciError}; use anyhow::{anyhow, Context, Result}; /// Size in bytes of the configuration space of legacy PCI device. @@ -523,7 +523,14 @@ impl PciConfig { /// * `offset` - Offset in the configuration space from which to write. /// * `data` - Data to write. /// * `dev_id` - Device id to send MSI/MSI-X. - pub fn write(&mut self, mut offset: usize, data: &[u8], dev_id: u16) { + pub fn write( + &mut self, + mut offset: usize, + data: &[u8], + dev_id: u16, + #[cfg(target_arch = "x86_64")] io_region: Option<&Region>, + mem_region: Option<&Region>, + ) { if let Err(err) = self.validate_config_boundary(offset, data) { error!("invalid write: {:?}", err); return; @@ -531,6 +538,7 @@ impl PciConfig { let cloned_data = data.to_vec(); let old_offset = offset; + let end = offset + data.len(); for data in &cloned_data { self.config[offset] = (self.config[offset] & (!self.write_mask[offset])) | (data & self.write_mask[offset]); @@ -538,6 +546,28 @@ impl PciConfig { offset += 1; } + let mut bar_num = BAR_NUM_MAX_FOR_ENDPOINT; + if self.config[HEADER_TYPE as usize] == HEADER_TYPE_BRIDGE { + bar_num = BAR_NUM_MAX_FOR_ENDPOINT; + } + if ranges_overlap(old_offset, end, COMMAND as usize, (COMMAND + 1) as usize) + || ranges_overlap( + old_offset, + end, + BAR_0 as usize, + BAR_0 as usize + REG_SIZE * bar_num as usize, + ) + || ranges_overlap(old_offset, end, ROM_ADDRESS, ROM_ADDRESS + 4) + { + if let Err(e) = self.update_bar_mapping( + #[cfg(target_arch = "x86_64")] + io_region, + mem_region, + ) { + error!("{:?}", e); + } + } + if let Some(msix) = &mut self.msix { msix.lock() .unwrap() @@ -714,6 +744,23 @@ impl PciConfig { Ok(()) } + fn is_bar_region_empty( + &mut self, + id: usize, + #[cfg(target_arch = "x86_64")] io_region: Option<&Region>, + mem_region: Option<&Region>, + ) -> bool { + if self.bars[id].region_type == RegionType::Io { + #[cfg(target_arch = "x86_64")] + if io_region.is_none() { + return true; + } + } else if mem_region.is_none() { + return true; + } + false + } + /// Update bar space mapping once the base address is updated by the guest. /// /// # Arguments @@ -722,8 +769,8 @@ impl PciConfig { /// * `mem_region`: Memory space region which the parent bridge manages. pub fn update_bar_mapping( &mut self, - #[cfg(target_arch = "x86_64")] io_region: &Region, - mem_region: &Region, + #[cfg(target_arch = "x86_64")] io_region: Option<&Region>, + mem_region: Option<&Region>, ) -> Result<()> { for id in 0..self.bars.len() { if self.bars[id].size == 0 { @@ -734,15 +781,27 @@ impl PciConfig { if self.bars[id].address == new_addr { continue; } + + if self.is_bar_region_empty( + id, + #[cfg(target_arch = "x86_64")] + io_region, + mem_region, + ) { + return Ok(()); + } + if self.bars[id].address != BAR_SPACE_UNMAPPED { match self.bars[id].region_type { RegionType::Io => { #[cfg(target_arch = "x86_64")] io_region + .unwrap() .delete_subregion(self.bars[id].region.as_ref().unwrap()) .with_context(|| format!("Failed to unmap BAR{} in I/O space.", id))?; } _ => mem_region + .unwrap() .delete_subregion(self.bars[id].region.as_ref().unwrap()) .with_context(|| anyhow!(PciError::UnregMemBar(id)))?, } @@ -753,10 +812,12 @@ impl PciConfig { RegionType::Io => { #[cfg(target_arch = "x86_64")] io_region + .unwrap() .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) .with_context(|| format!("Failed to map BAR{} in I/O space.", id))?; } _ => mem_region + .unwrap() .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) .with_context(|| anyhow!(PciError::UnregMemBar(id)))?, } @@ -1171,8 +1232,8 @@ mod tests { pci_config .update_bar_mapping( #[cfg(target_arch = "x86_64")] - sys_io.root(), - sys_mem.root(), + Some(sys_io.root()), + Some(sys_mem.root()), ) .unwrap(); assert_eq!(pci_config.bars[1].address, 2048); @@ -1182,8 +1243,8 @@ mod tests { pci_config .update_bar_mapping( #[cfg(target_arch = "x86_64")] - sys_io.root(), - sys_mem.root(), + Some(sys_io.root()), + Some(sys_mem.root()), ) .unwrap(); assert_eq!(pci_config.bars[1].address, 2048); @@ -1206,8 +1267,8 @@ mod tests { pci_config .update_bar_mapping( #[cfg(target_arch = "x86_64")] - sys_io.root(), - sys_mem.root(), + Some(sys_io.root()), + Some(sys_mem.root()), ) .unwrap(); assert_eq!(pci_config.bars[1].address, pci_config.get_bar_address(1)); @@ -1353,8 +1414,8 @@ mod tests { pci_config .update_bar_mapping( #[cfg(target_arch = "x86_64")] - &io_region, - &mem_region, + Some(&io_region), + Some(&mem_region), ) .unwrap(); diff --git a/pci/src/host.rs b/pci/src/host.rs index 96cc278f6..4612f220d 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -523,7 +523,15 @@ pub mod tests { } fn write_config(&mut self, offset: usize, data: &[u8]) { - self.config.write(offset, data, 0); + #[allow(unused_variables)] + self.config.write( + offset, + data, + 0, + #[cfg(target_arch = "x86_64")] + None, + None, + ); } fn name(&self) -> String { diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index b855dd89a..2d219e816 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -26,14 +26,14 @@ use once_cell::sync::OnceCell; use util::byte_code::ByteCode; use super::config::{ - PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, - COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, - PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, - PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, - PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, + PciConfig, PcieDevType, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, + DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, + PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, - PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID, + PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; use crate::hotplug::HotplugOps; @@ -367,19 +367,14 @@ impl PciDevOps for RootPort { let old_ctl = le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize).unwrap(); - self.config - .write(offset, data, self.dev_id.load(Ordering::Acquire)); - if ranges_overlap(offset, end, COMMAND as usize, (COMMAND + 1) as usize) - || ranges_overlap(offset, end, BAR_0 as usize, BAR_0 as usize + REG_SIZE * 2) - { - if let Err(e) = self.config.update_bar_mapping( - #[cfg(target_arch = "x86_64")] - &self.io_region, - &self.mem_region, - ) { - error!("{}", format!("{:?}", e)); - } - } + self.config.write( + offset, + data, + self.dev_id.load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + Some(&self.io_region), + Some(&self.mem_region), + ); if ranges_overlap(offset, end, COMMAND as usize, (COMMAND + 1) as usize) || ranges_overlap(offset, end, IO_BASE as usize, (IO_BASE + 2) as usize) || ranges_overlap( diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 36d22a470..b16aa3eb5 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -17,10 +17,10 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; use machine_manager::config::XhciConfig; use pci::config::{ - PciConfig, RegionType, BAR_0, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, - PCI_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, SUB_CLASS_CODE, VENDOR_ID, + PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, REVISION_ID, + SUB_CLASS_CODE, VENDOR_ID, }; -use pci::{init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps}; +use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; use crate::bus::{BusDeviceMap, BusDeviceOps}; use crate::usb::UsbDeviceOps; @@ -244,27 +244,17 @@ impl PciDevOps for XhciPciDevice { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let end = offset + data.len(); - self.pci_config - .write(offset, data, self.dev_id.clone().load(Ordering::Acquire)); - if ranges_overlap( + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + + self.pci_config.write( offset, - end, - BAR_0 as usize, - BAR_0 as usize + REG_SIZE as usize, - ) || ranges_overlap(offset, end, ROM_ADDRESS, ROM_ADDRESS + 4) - || ranges_overlap(offset, end, COMMAND as usize, COMMAND as usize + 1) - { - let parent_bus = self.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - if let Err(e) = self.pci_config.update_bar_mapping( - #[cfg(target_arch = "x86_64")] - &locked_parent_bus.io_region, - &locked_parent_bus.mem_region, - ) { - error!("Failed to update bar, error is {:?}", e); - } - } + data, + self.dev_id.clone().load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), + ); } fn name(&self) -> String { diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 8c70569dc..6c53c0d39 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -956,48 +956,27 @@ impl PciDevOps for VfioPciDevice { .msix .as_ref() .map_or(0, |m| m.lock().unwrap().msix_cap_offset as usize); - if ranges_overlap(offset, end, COMMAND as usize, COMMAND as usize + REG_SIZE) { - self.pci_config - .write(offset, data, self.dev_id.load(Ordering::Acquire)); + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + self.pci_config.write( + offset, + data, + self.dev_id.load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), + ); + if ranges_overlap(offset, end, COMMAND as usize, COMMAND as usize + REG_SIZE) { if le_read_u32(&self.pci_config.config, offset).unwrap() & COMMAND_MEMORY_SPACE as u32 != 0 { - let parent_bus = self.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - if let Err(e) = self.pci_config.update_bar_mapping( - #[cfg(target_arch = "x86_64")] - &locked_parent_bus.io_region, - &locked_parent_bus.mem_region, - ) { - error!("Failed to update bar, error is {}", format!("{:?}", e)); - return; - } - drop(locked_parent_bus); - if let Err(e) = self.setup_bars_mmap() { error!("Failed to map bar regions, error is {}", format!("{:?}", e)); } } - } else if ranges_overlap(offset, end, BAR_0 as usize, (BAR_5 as usize) + REG_SIZE) { - self.pci_config - .write(offset, data, self.dev_id.load(Ordering::Acquire)); - - if size == 4 && LittleEndian::read_u32(data) != 0xffff_ffff { - let parent_bus = self.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - if let Err(e) = self.pci_config.update_bar_mapping( - #[cfg(target_arch = "x86_64")] - &locked_parent_bus.io_region, - &locked_parent_bus.mem_region, - ) { - error!("Failed to update bar, error is {}", format!("{:?}", e)); - } - } } else if ranges_overlap(offset, end, cap_offset, cap_offset + MSIX_CAP_SIZE as usize) { let was_enable = is_msix_enabled(cap_offset, &self.pci_config.config); - self.pci_config - .write(offset, data, self.dev_id.load(Ordering::Acquire)); let is_enable = is_msix_enabled(cap_offset, &self.pci_config.config); if !was_enable && is_enable { @@ -1009,9 +988,6 @@ impl PciDevOps for VfioPciDevice { error!("{}\nFailed to disable MSI-X.", format!("{:?}", e)); } } - } else { - self.pci_config - .write(offset, data, self.dev_id.load(Ordering::Acquire)); } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index c0596e71d..b45006677 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -24,9 +24,9 @@ use log::{error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use pci::config::{ - RegionType, BAR_0, BAR_SPACE_UNMAPPED, COMMAND, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, - PCIE_CONFIG_SPACE_SIZE, REG_SIZE, REVISION_ID, ROM_ADDRESS, STATUS, STATUS_INTERRUPT, - SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, + RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, + REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, + SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::{update_dev_id, Message, MsixState, MsixUpdate}; use pci::Result as PciResult; @@ -1265,28 +1265,16 @@ impl PciDevOps for VirtioPciDevice { return; } - self.config - .write(offset, data, self.dev_id.clone().load(Ordering::Acquire)); - if ranges_overlap( + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + self.config.write( offset, - end, - BAR_0 as usize, - BAR_0 as usize + REG_SIZE as usize * VIRTIO_PCI_BAR_MAX as usize, - ) || ranges_overlap(offset, end, ROM_ADDRESS, ROM_ADDRESS + 4) - || ranges_overlap(offset, end, COMMAND as usize, COMMAND as usize + 1) - { - let parent_bus = self.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - if let Err(e) = self.config.update_bar_mapping( - #[cfg(target_arch = "x86_64")] - &locked_parent_bus.io_region, - &locked_parent_bus.mem_region, - ) { - error!("Failed to update bar, error is {:?}", e); - return; - } - } - + data, + self.dev_id.clone().load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), + ); self.do_cfg_access(offset, end, true); } @@ -1443,8 +1431,8 @@ impl MigrationHook for VirtioPciDevice { let locked_parent_bus = parent_bus.lock().unwrap(); if let Err(e) = self.config.update_bar_mapping( #[cfg(target_arch = "x86_64")] - &locked_parent_bus.io_region, - &locked_parent_bus.mem_region, + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), ) { bail!("Failed to update bar, error is {:?}", e); } -- Gitee From a851ec96f8408b7f65fc473261bdc52d654047ef Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 25 Nov 2022 19:03:41 +0800 Subject: [PATCH 0495/2187] pci: do not report error in some scenarios Because linux kernel tries to read offset 256B of the configuration space of the host bridge, although it's a PCI device that has only 256B meaningful space(that's to say, the max offset is 255B). Meanwhile, virtio-net driver doesn't set the interrupt vector for the control queue, thus, we would use the default 0xFFFF vector to notify the guest, which is invalid, and an error message occurs. These abnormal behaviors are triggered by the guest, thus tolerate them by changing loglevel from error to warn. Signed-off-by: Zhang Bo --- pci/src/config.rs | 4 ++-- pci/src/msix.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index b849d5138..805221e65 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; use std::sync::{Arc, Mutex}; use address_space::Region; -use log::error; +use log::{error, warn}; use crate::msix::Msix; use crate::{ @@ -490,7 +490,7 @@ impl PciConfig { /// * `data` - Buffer to put read data. pub fn read(&self, offset: usize, buf: &mut [u8]) { if let Err(err) = self.validate_config_boundary(offset, buf) { - error!("invalid read: {:?}", err); + warn!("invalid read: {:?}", err); return; } let size = buf.len(); diff --git a/pci/src/msix.rs b/pci/src/msix.rs index fa686e2b7..2f9b18b04 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{GuestAddress, Region, RegionOps}; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; +use log::{error, warn}; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; @@ -282,7 +282,7 @@ impl Msix { pub fn notify(&mut self, vector: u16, dev_id: u16) { if vector >= self.table.len() as u16 / MSIX_TABLE_ENTRY_SIZE { - error!("Invaild msix vector {}.", vector); + warn!("Invalid msix vector {}.", vector); return; } -- Gitee From 60597f6c57dde17dae1999164348b52d254eaa93 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 6 Nov 2022 11:26:19 +0800 Subject: [PATCH 0496/2187] VNC: Configure the system calls used by vnc to gnu in whitelist. Since the feature of VNC is only compiled in the Gnu version, set the system calls required by VNC to gnu. Signed-off-by: Xiao Ye --- docs/config_guidebook.md | 4 ++-- machine/src/standard_vm/aarch64/syscall.rs | 22 ++++++++++++++++++++-- machine/src/standard_vm/x86_64/syscall.rs | 22 ++++++++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 9e0401a0c..519a7c383 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -912,14 +912,14 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 51 | 50 | -| q35 | 83 | 82 | +| q35 | 84 | 65 | * aarch64 | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 49 | 49 | -| virt | 82 | 79 | +| virt | 83 | 62 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 315e44485..09d75a625 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 79 syscalls -/// * aarch64-unknown-musl: 76 syscalls +/// * aarch64-unknown-gnu: 80 syscalls +/// * aarch64-unknown-musl: 59 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -77,10 +77,12 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), BpfRule::new(libc::SYS_epoll_ctl), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sendmmsg), BpfRule::new(libc::SYS_recvfrom), BpfRule::new(libc::SYS_mremap), @@ -121,27 +123,43 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), BpfRule::new(libc::SYS_socket), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_bind), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_prctl), BpfRule::new(libc::SYS_sendto), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockname), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getpeername), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_nanosleep), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getuid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_geteuid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getgid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getegid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_gettid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getdents64), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_clock_gettime), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockopt), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_uname), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sysinfo), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_faccessat), BpfRule::new(libc::SYS_getrandom), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_rt_sigaction), BpfRule::new(libc::SYS_setsockopt), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 630173448..18c5704d1 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 80 syscalls -/// * x86_64-unknown-musl: 79 syscalls +/// * x86_64-unknown-gnu: 81 syscalls +/// * x86_64-unknown-musl: 62 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -79,10 +79,12 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), BpfRule::new(libc::SYS_epoll_ctl), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sendmmsg), BpfRule::new(libc::SYS_recvfrom), BpfRule::new(libc::SYS_mremap), @@ -127,6 +129,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_readlinkat), BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_socket), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_bind), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), @@ -136,20 +139,35 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_clone3), BpfRule::new(libc::SYS_prctl), BpfRule::new(libc::SYS_sendto), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockname), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getpeername), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_nanosleep), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getuid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_geteuid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getgid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getegid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_gettid), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getdents64), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_clock_gettime), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockopt), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_uname), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sysinfo), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_faccessat), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_shutdown), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_setsockopt), -- Gitee From 993e2452de449ac807f69752e4168c3942007b2b Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 24 Nov 2022 17:38:03 +0800 Subject: [PATCH 0497/2187] VNC: Add system call to whitelist. Add system call of SYS_ clock_ nanosleep to the whitelist, as it will be used in the function of sleep. Signed-off-by: Xiao Ye --- docs/config_guidebook.md | 4 ++-- machine/src/standard_vm/aarch64/syscall.rs | 4 +++- machine/src/standard_vm/x86_64/syscall.rs | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 519a7c383..9168ad013 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -912,14 +912,14 @@ in StratoVirt process by default. It will make a slight influence on performance | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 51 | 50 | -| q35 | 84 | 65 | +| q35 | 85 | 65 | * aarch64 | Number of Syscalls | GNU Toolchain | MUSL Toolchain | | :----------------: | :-----------: | :------------: | | microvm | 49 | 49 | -| virt | 83 | 62 | +| virt | 84 | 62 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 09d75a625..c50adae73 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 80 syscalls +/// * aarch64-unknown-gnu: 81 syscalls /// * aarch64-unknown-musl: 59 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -137,6 +137,8 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_nanosleep), #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_clock_nanosleep), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getuid), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_geteuid), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 18c5704d1..5362ae2ca 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 81 syscalls +/// * x86_64-unknown-gnu: 82 syscalls /// * x86_64-unknown-musl: 62 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -146,6 +146,8 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_nanosleep), #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_clock_nanosleep), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getuid), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_geteuid), -- Gitee From 0fcfb4ccb3b20ebc9746bf65abfd3b429e51d23f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 25 Nov 2022 15:39:14 +0800 Subject: [PATCH 0498/2187] xhci: no error needs to be reported in some cases In some cases, for example, the setup packet fails or the handle packet fails, no error needs to be reported. However, an error needs to be reported when fetch td fails because dma fails. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 114 +++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index b2eff4000..2dbf3fb29 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1149,10 +1149,15 @@ impl XhciDevice { "kick_endpoint slotid {} epid {} dequeue {:x}", slot_id, ep_id, epctx.ring.dequeue ); - if !self.endpoint_retry_transfer(slot_id, ep_id)? { + if self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] + .retry + .is_some() + && !self.endpoint_retry_transfer(slot_id, ep_id)? + { // Return directly to retry again at the next kick. return Ok(()); } + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if epctx.state == EP_HALTED { info!("xhci: endpoint halted"); @@ -1166,25 +1171,20 @@ impl XhciDevice { xfer.slotid = slot_id; xfer.epid = ep_id; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - match epctx.ring.fetch_td() { - Ok(Some(td)) => { + match epctx.ring.fetch_td()? { + Some(td) => { debug!( "fetch transfer trb {:?} ring dequeue {:?}", td, epctx.ring.dequeue, ); xfer.td = td; } - Ok(None) => { + None => { debug!("No TD in the transfer ring."); break; } - Err(e) => { - bail!("fetch ring failed {}", e); - } - } - if let Err(e) = self.endpoint_do_transfer(&mut xfer) { - error!("Failed to transfer {:?}", e); } + self.endpoint_do_transfer(&mut xfer)?; let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if xfer.complete { epctx.set_state(&self.mem_space, epctx.state)?; @@ -1230,25 +1230,29 @@ impl XhciDevice { Ok(ep_ctx) } - /// Return true if retry is done. - /// Return false if packet is need to retry again. - /// Return error if retry failed. + /// Return Ok(true) if retry is done. + /// Return Ok(false) if packet is need to retry again. + /// Return Err() if retry failed. fn endpoint_retry_transfer(&mut self, slot_id: u32, ep_id: u32) -> Result { let slot = &mut self.slots[(slot_id - 1) as usize]; - let mut xfer = if let Some(xfer) = &mut slot.endpoints[(ep_id - 1) as usize].retry { - xfer.clone() - } else { - // No need to retry. + // Safe because the retry is checked in the outer function call. + let xfer = &mut slot.endpoints[(ep_id - 1) as usize] + .retry + .as_ref() + .unwrap() + .clone(); + if let Err(e) = self.setup_usb_packet(xfer) { + error!("Failed to setup packet when retry {}", e); + self.report_transfer_error(xfer)?; return Ok(true); - }; - self.setup_usb_packet(&mut xfer)?; - self.device_handle_packet(&mut xfer.packet)?; + } + self.device_handle_packet(&mut xfer.packet); if xfer.packet.status == UsbPacketStatus::Nak { debug!("USB packet status is NAK"); // NAK need to retry again. return Ok(false); } - self.complete_packet(&mut xfer)?; + self.complete_packet(xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if xfer.complete { epctx.set_state(&self.mem_space, epctx.state)?; @@ -1257,22 +1261,24 @@ impl XhciDevice { Ok(true) } - fn device_handle_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { + fn device_handle_packet(&mut self, packet: &mut UsbPacket) { if let Some(ep) = &packet.ep { let ep = ep.upgrade().unwrap(); let locked_ep = ep.lock().unwrap(); let dev = if let Some(usb_dev) = &locked_ep.dev { usb_dev.upgrade().unwrap() } else { - bail!("No device found in endpoint"); + packet.status = UsbPacketStatus::NoDev; + error!("Failed to handle packet, No device found in endpoint"); + return; }; drop(locked_ep); let mut locked_dev = dev.lock().unwrap(); locked_dev.handle_packet(packet); } else { - bail!("No endpoint found"); + packet.status = UsbPacketStatus::NoDev; + error!("Failed to handle packet, No endpoint found"); } - Ok(()) } fn endpoint_do_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { @@ -1288,11 +1294,30 @@ impl XhciDevice { fn do_ctrl_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { let trb_setup = xfer.td[0]; let mut trb_status = xfer.td[xfer.td.len() - 1]; - let setup_type = trb_setup.get_type(); let status_type = trb_status.get_type(); if status_type == TRBType::TrEvdata && xfer.td.len() > 2 { trb_status = xfer.td[xfer.td.len() - 2]; } + if let Err(e) = self.check_ctrl_transfer(&trb_setup, &trb_status) { + error!("Failed to check control transfer {}", e); + return self.report_transfer_error(xfer); + } + + let bm_request_type = trb_setup.parameter as u8; + xfer.in_xfer = + bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + if let Err(e) = self.setup_usb_packet(xfer) { + error!("Failed to setup packet when transfer control {}", e); + return self.report_transfer_error(xfer); + } + xfer.packet.parameter = trb_setup.parameter; + self.device_handle_packet(&mut xfer.packet); + self.complete_packet(xfer)?; + Ok(()) + } + + fn check_ctrl_transfer(&self, trb_setup: &XhciTRB, trb_status: &XhciTRB) -> Result<()> { + let setup_type = trb_setup.get_type(); if setup_type != TRBType::TrSetup { bail!("The first TRB is not Setup"); } @@ -1308,14 +1333,6 @@ impl XhciDevice { trb_setup.status & TRB_TR_LEN_MASK ); } - - let bm_request_type = trb_setup.parameter as u8; - xfer.in_xfer = - bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; - self.setup_usb_packet(xfer)?; - xfer.packet.parameter = trb_setup.parameter; - self.device_handle_packet(&mut xfer.packet)?; - self.complete_packet(xfer)?; Ok(()) } @@ -1326,10 +1343,13 @@ impl XhciDevice { || epctx.ep_type == EpType::BulkIn || epctx.ep_type == EpType::IntrIn; if epctx.ep_type != EpType::IntrOut && epctx.ep_type != EpType::IntrIn { - bail!("Unhandled ep_type {:?}", epctx.ep_type); + warn!("Unhandled ep_type {:?}", epctx.ep_type); + } + if let Err(e) = self.setup_usb_packet(xfer) { + error!("Failed to setup packet when transfer data {}", e); + return self.report_transfer_error(xfer); } - self.setup_usb_packet(xfer)?; - self.device_handle_packet(&mut xfer.packet)?; + self.device_handle_packet(&mut xfer.packet); self.complete_packet(xfer)?; Ok(()) } @@ -1367,7 +1387,7 @@ impl XhciDevice { if let Some(hva) = self.mem_space.get_host_address(GuestAddress(dma_addr)) { vec.push(Iovec::new(hva, chunk as usize)); } else { - error!("HVA not existed {:x}", dma_addr); + bail!("HVA not existed {:x}", dma_addr); } } } @@ -1430,7 +1450,8 @@ impl XhciDevice { self.submit_transfer(xfer)?; } _ => { - bail!("Unhandle status {:?}", xfer.packet.status); + error!("Unhandle status {:?}", xfer.packet.status); + self.report_transfer_error(xfer)?; } } Ok(()) @@ -1492,6 +1513,21 @@ impl XhciDevice { Ok(()) } + fn report_transfer_error(&mut self, xfer: &XhciTransfer) -> Result<()> { + let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); + evt.slot_id = xfer.slotid as u8; + evt.ep_id = xfer.epid as u8; + let mut idx = 0; + // According to 4.10.1 Transfer TRBs, the TRB pointer field in a Transfer TRB not + // only references the TRB that generated the event, but it also provides system software + // with thr latest value of the xHC Dequeue Pointer for the Transfer Ring. + if let Some(trb) = xfer.td.last() { + evt.ptr = trb.addr; + idx = (trb.status >> TRB_INTR_SHIFT) & TRB_INTR_MASK; + } + self.send_event(&evt, idx) + } + fn send_transfer_event( &mut self, xfer: &XhciTransfer, -- Gitee From de5b31d0008eff094e4f70550a961c6812156c79 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 17 Nov 2022 15:36:30 +0800 Subject: [PATCH 0499/2187] virtio-net: optimizing the handle_rx() Extract the read tap device code to read_from_tap(). Signed-off-by: Yan Wang --- virtio/src/net.rs | 64 +++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 1fef570b8..662f6b75d 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -290,6 +290,38 @@ struct NetIoHandler { } impl NetIoHandler { + fn read_from_tap(queue: &mut Queue, iovecs: &[libc::iovec], tap: &mut Tap) -> i32 { + let size = unsafe { + libc::readv( + tap.as_raw_fd() as libc::c_int, + iovecs.as_ptr() as *const libc::iovec, + iovecs.len() as libc::c_int, + ) + } as i32; + if size < 0 { + let e = std::io::Error::last_os_error(); + queue.vring.push_back(); + if e.kind() == std::io::ErrorKind::WouldBlock { + return size; + } + + // If the backend tap device is removed, readv returns less than 0. + // At this time, the content in the tap needs to be cleaned up. + // Here, read is called to process, otherwise handle_rx may be triggered all the time. + let mut buf = [0; 1024]; + match tap.read(&mut buf) { + Ok(cnt) => error!("Failed to call readv but tap read is ok: cnt {}", cnt), + Err(e) => { + // When the backend tap device is abnormally removed, read return EBADFD. + error!("Failed to read tap: {}", e); + } + } + error!("Failed to call readv for net handle_rx: {}", e); + } + + size + } + fn handle_rx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to rx".to_string()); let mut queue = self.rx.queue.lock().unwrap(); @@ -320,42 +352,20 @@ impl NetIoHandler { bail!("Failed to get host address for {}", elem_iov.addr.0); } } - let write_count = unsafe { - libc::readv( - tap.as_raw_fd() as libc::c_int, - iovecs.as_ptr() as *const libc::iovec, - iovecs.len() as libc::c_int, - ) - }; - if write_count < 0 { - let e = std::io::Error::last_os_error(); - queue.vring.push_back(); - if e.kind() == std::io::ErrorKind::WouldBlock { - break; - } - // If the backend tap device is removed, readv returns less than 0. - // At this time, the content in the tap needs to be cleaned up. - // Here, read is called to process, otherwise handle_rx may be triggered all the time. - let mut buf = [0; 1024]; - match tap.read(&mut buf) { - Ok(cnt) => error!("Failed to call readv but tap read is ok: cnt {}", cnt), - Err(e) => { - // When the backend tap device is abnormally removed, read return EBADFD. - error!("Failed to read tap: {}", e); - } - } - error!("Failed to call readv for net handle_rx: {}", e); + // Read the data from the tap device. + let size = NetIoHandler::read_from_tap(&mut queue, &iovecs, tap); + if size < 0 { break; } queue .vring - .add_used(&self.mem_space, elem.index, write_count as u32) + .add_used(&self.mem_space, elem.index, size as u32) .with_context(|| { format!( "Failed to add used ring for net rx, index: {}, len: {}", - elem.index, write_count + elem.index, size ) })?; -- Gitee From 97e876fafcd44dc50be1936fe33f57d39d471fbf Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 16:06:23 +0800 Subject: [PATCH 0500/2187] virtio-net: reutrn VIRITO_NET_ERR for unsupported control command It should not return error Result for unsupported control command, which will cause the guest getting stucked. So, just return VIRITO_NET_ERR to the guest in this scene. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 4 +++- virtio/src/net.rs | 10 ++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index ce39cd45e..f641ec839 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -158,8 +158,10 @@ pub const VIRTIO_BLK_F_DISCARD: u32 = 13; /// WRITE ZEROES is supported. pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; -/// The device sets MQ ok status values to driver. +/// The device sets control ok status to driver. pub const VIRTIO_NET_OK: u8 = 0; +/// The device sets control err status to driver. +pub const VIRTIO_NET_ERR: u8 = 1; /// Driver configure the class before enabling virtqueue. pub const VIRTIO_NET_CTRL_MQ: u16 = 4; /// Driver configure the command before enabling virtqueue. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 662f6b75d..9b7a92f38 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -21,7 +21,7 @@ use super::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, @@ -115,7 +115,7 @@ impl NetCtrlHandler { fn handle_ctrl(&mut self) -> Result<()> { let mut locked_queue = self.ctrl.queue.lock().unwrap(); loop { - let ack = VIRTIO_NET_OK; + let mut ack = VIRTIO_NET_OK; let elem = locked_queue .vring .pop_avail(&self.mem_space, self.driver_features) @@ -150,10 +150,8 @@ impl NetCtrlHandler { } } _ => { - bail!( - "Control queue header class can't match {}", - VIRTIO_NET_CTRL_MQ - ); + error!("Control queue header class can't match {}", ctrl_hdr.class); + ack = VIRTIO_NET_ERR; } } } -- Gitee From f98d4377195995027598af6c3afda8707d072904 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Sat, 26 Nov 2022 01:45:48 -0500 Subject: [PATCH 0501/2187] viritofs: delete unused VhostUserFsHandler The VhostUserFsHandler struct is unused in fact, just delete it. Signed-off-by: Li HuaChao --- virtio/src/vhost/user/fs.rs | 55 ++----------------------------------- 1 file changed, 3 insertions(+), 52 deletions(-) diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index db0a21fd2..c2137e698 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -20,10 +20,8 @@ const VIRTIO_FS_QUEUE_SIZE: u16 = 128; use crate::VirtioError; use std::cmp; use std::io::Write; -use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; -use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use address_space::AddressSpace; @@ -32,13 +30,13 @@ use machine_manager::{ event_loop::EventLoop, }; use util::byte_code::ByteCode; -use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; -use super::super::{VhostNotify, VhostOps}; +use super::super::VhostOps; use super::VhostUserClient; -use crate::{VirtioInterrupt, VirtioInterruptType}; +use crate::VirtioInterrupt; use anyhow::{anyhow, Context, Result}; #[derive(Copy, Clone)] @@ -59,53 +57,6 @@ impl Default for VirtioFsConfig { impl ByteCode for VirtioFsConfig {} -struct VhostUserFsHandler { - interrup_cb: Arc, - host_notifies: Vec, -} - -impl EventNotifierHelper for VhostUserFsHandler { - fn internal_notifiers(vhost_user_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - let vhost_user = vhost_user_handler.clone(); - - let handler: Box Option>> = - Box::new(move |_, fd: RawFd| { - read_fd(fd); - - let locked_vhost_user = vhost_user.lock().unwrap(); - - for host_notify in locked_vhost_user.host_notifies.iter() { - if let Err(e) = (locked_vhost_user.interrup_cb)( - &VirtioInterruptType::Vring, - Some(&host_notify.queue.lock().unwrap()), - false, - ) { - error!( - "Failed to trigger interrupt for vhost user device, error is {:?}", - e - ); - } - } - - None as Option> - }); - let h = Arc::new(Mutex::new(handler)); - - for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - host_notify.notify_evt.as_raw_fd(), - None, - EventSet::IN, - vec![h.clone()], - )); - } - - notifiers - } -} - pub struct Fs { fs_cfg: FsConfig, config: VirtioFsConfig, -- Gitee From 8fba391ed208567649d3543669a313bf8e9266e4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 23 Nov 2022 11:08:48 +0800 Subject: [PATCH 0502/2187] xhci: modify the vendor id and device id The Nec HC has some vendor command which we not implemented. And modify the vendor id and device id in pci config to avoid driver write the vendor command. To be compatible with libvirt, the command line parameters are not modified. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_pci.rs | 10 ++++------ usb/src/xhci/xhci_ring.rs | 5 ----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index b16aa3eb5..99594dd71 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -17,8 +17,8 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; use machine_manager::config::XhciConfig; use pci::config::{ - PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, REVISION_ID, - SUB_CLASS_CODE, VENDOR_ID, + PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, + PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; @@ -31,8 +31,6 @@ use crate::xhci::xhci_regs::{ }; use anyhow::{bail, Context, Result}; -const PCI_VENDOR_ID_NEC: u16 = 0x1033; -const PCU_DEVICE_ID_NEC_UPD720200: u16 = 0x0194; /// 5.2 PCI Configuration Registers(USB) const PCI_CLASS_PI: u16 = 0x09; const PCI_INTERRUPT_PIN: u16 = 0x3d; @@ -161,12 +159,12 @@ impl PciDevOps for XhciPciDevice { le_write_u16( &mut self.pci_config.config, VENDOR_ID as usize, - PCI_VENDOR_ID_NEC, + PCI_VENDOR_ID_REDHAT, )?; le_write_u16( &mut self.pci_config.config, DEVICE_ID as usize, - PCU_DEVICE_ID_NEC_UPD720200 as u16, + PCI_DEVICE_ID_REDHAT_XHCI, )?; le_write_u16(&mut self.pci_config.config, REVISION_ID as usize, 0x3_u16)?; le_write_u16( diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 3eb6c62c8..77710d512 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -83,9 +83,6 @@ pub enum TRBType { ErHostController, ErDeviceNotification, ErMfindexWrap, - /* vendor specific bits */ - CrVendorNecFirmwareRevision = 49, - CrVendorNecChallengeResponse = 50, Unknown, } @@ -124,8 +121,6 @@ impl From for TRBType { 37 => TRBType::ErHostController, 38 => TRBType::ErDeviceNotification, 39 => TRBType::ErMfindexWrap, - 49 => TRBType::CrVendorNecFirmwareRevision, - 50 => TRBType::CrVendorNecChallengeResponse, _ => TRBType::Unknown, } } -- Gitee From 47c593701fcab74e23a30ebcfc91f8852666ea5a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 23 Nov 2022 11:18:19 +0800 Subject: [PATCH 0503/2187] xhci: allow reset device in the default state The linux driver resets the device in the default state. Compatible with this situation that does not comply with the spec. --- usb/src/xhci/xhci_controller.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 2dbf3fb29..e47a927e8 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -984,7 +984,10 @@ impl XhciDevice { slot_ctx.as_mut_dwords(), )?; let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; - if slot_state != SLOT_ADDRESSED && slot_state != SLOT_CONFIGURED { + if slot_state != SLOT_ADDRESSED + && slot_state != SLOT_CONFIGURED + && slot_state != SLOT_DEFAULT + { error!("Invalid slot state: {:?}", slot_state); return Ok(TRBCCode::ContextStateError); } -- Gitee From 58cb543e480685b8679ded4e2ed4482cf7ff7ba9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 25 Nov 2022 12:46:20 +0800 Subject: [PATCH 0504/2187] hid: set the usb packet status to stall when an error occurs Set the usb packet status to stall when an eror occurs. The stall indicates that a function is unable to transmit or reveive data, or that a control pipe request is not supported. --- usb/src/hid.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 2eab64879..aaf94fb76 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -429,16 +429,18 @@ impl Hid { packet.actual_length = KEYBOARD_REPORT_DESCRIPTOR.len() as u32; } _ => { - error!("Unkown HID type"); + error!("Unknown HID type"); + packet.status = UsbPacketStatus::Stall; } }, _ => { error!("Invalid value: {:?}", device_req); + packet.status = UsbPacketStatus::Stall; } }, _ => { - packet.status = UsbPacketStatus::Stall; error!("Unhandled request {}", device_req.request); + packet.status = UsbPacketStatus::Stall; } } } @@ -463,6 +465,7 @@ impl Hid { } _ => { error!("Unsupported HID type for report"); + packet.status = UsbPacketStatus::Stall; } }, HID_GET_PROTOCOL => { @@ -488,10 +491,11 @@ impl Hid { match device_req.request { HID_SET_REPORT => match self.kind { HidType::Keyboard => { - error!("Keyboard set report not implemented"); + warn!("Keyboard set report not implemented"); } _ => { error!("Unsupported to set report"); + packet.status = UsbPacketStatus::Stall; } }, HID_SET_PROTOCOL => { @@ -539,6 +543,7 @@ impl Hid { } _ => { error!("Unsupported HID device"); + p.status = UsbPacketStatus::Stall; } } let len = buf.len(); -- Gitee From e474fdd8146b76a86a9e155db7a42a6ecfd6659d Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 17:44:12 +0800 Subject: [PATCH 0505/2187] virtio-net: support feature VIRTIO_NET_F_CTRL_RX and VIRTIO_NET_F_CTRL_RX_EXTRA Add new RX_MODE control virtqueue class with PROMISC/ALLMULTI/ALLUNI/NOMULTI/NOUNI/NOBCAST which will be used to drop packets the guest doesn't want to see. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 30 ++- virtio/src/net.rs | 389 +++++++++++++++++++++++++++------ virtio/src/queue.rs | 8 + virtio/src/vhost/kernel/net.rs | 2 +- virtio/src/vhost/user/net.rs | 2 +- 5 files changed, 362 insertions(+), 69 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index f641ec839..44d7c8759 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -133,6 +133,10 @@ pub const VIRTIO_NET_F_HOST_UFO: u32 = 14; pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15; /// Control channel is available. pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17; +/// Control channel RX mode support. +pub const VIRTIO_NET_F_CTRL_RX: u32 = 18; +/// Extra RX mode control support. +pub const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20; /// Device supports multi queue with automatic receive steering. pub const VIRTIO_NET_F_MQ: u32 = 22; /// Set Mac Address through control channel. @@ -162,8 +166,24 @@ pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; pub const VIRTIO_NET_OK: u8 = 0; /// The device sets control err status to driver. pub const VIRTIO_NET_ERR: u8 = 1; + +/// Driver can send control commands. +pub const VIRTIO_NET_CTRL_RX: u8 = 0; +/// Control commands for promiscuous mode. +pub const VIRTIO_NET_CTRL_RX_PROMISC: u8 = 0; +/// Control commands for all-multicast receive. +pub const VIRTIO_NET_CTRL_RX_ALLMULTI: u8 = 1; +/// Control commands for all-unicast receive. +pub const VIRTIO_NET_CTRL_RX_ALLUNI: u8 = 2; +/// Control commands for suppressing multicast receive. +pub const VIRTIO_NET_CTRL_RX_NOMULTI: u8 = 3; +/// Control commands for suppressing unicast receive. +pub const VIRTIO_NET_CTRL_RX_NOUNI: u8 = 4; +/// Control commands for suppressing broadcast receive. +pub const VIRTIO_NET_CTRL_RX_NOBCAST: u8 = 5; + /// Driver configure the class before enabling virtqueue. -pub const VIRTIO_NET_CTRL_MQ: u16 = 4; +pub const VIRTIO_NET_CTRL_MQ: u8 = 4; /// Driver configure the command before enabling virtqueue. pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0; /// The minimum pairs of multiple queue. @@ -406,12 +426,12 @@ pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) } /// Discard "size" bytes of the front of iovec. -pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&[ElemIovec]> { +pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> { for (index, iov) in iovec.iter_mut().enumerate() { if iov.len as u64 > size { iov.addr.0 += size; iov.len -= size as u32; - return Some(&iovec[index..]); + return Some(&mut iovec[index..]); } size -= iov.len as u64; } @@ -419,12 +439,12 @@ pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&[Ele } /// Discard "size" bytes of the back of iovec. -pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&[ElemIovec]> { +pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> { let len = iovec.len(); for (index, iov) in iovec.iter_mut().rev().enumerate() { if iov.len as u64 > size { iov.len -= size as u32; - return Some(&iovec[..(len - index)]); + return Some(&mut iovec[..(len - index)]); } size -= iov.len as u64; } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 9b7a92f38..13bd482b3 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -21,13 +21,19 @@ use super::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, + VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, + VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; -use crate::{report_virtio_error, virtio_has_feature, VirtioError}; +use crate::{ + iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, + Element, VirtioError, +}; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use log::error; @@ -52,6 +58,8 @@ const QUEUE_NUM_NET: usize = 2; const QUEUE_SIZE_NET: u16 = 256; /// The Mac Address length. pub const MAC_ADDR_LEN: usize = 6; +/// The length of ethernet header. +const ETHERNET_HDR_LENGTH: usize = 14; type SenderConfig = Option; @@ -76,15 +84,162 @@ pub struct VirtioNetConfig { impl ByteCode for VirtioNetConfig {} +/// The control mode used for packet receive filtering. +pub struct CtrlRxMode { + /// If the device should receive all incoming packets. + promisc: bool, + /// If the device should allow all incoming multicast packets. + all_multi: bool, + /// If the device should allow all incoming unicast packets. + all_uni: bool, + /// Used to suppress multicast receive. + no_multi: bool, + /// Used to suppress unicast receive. + no_uni: bool, + /// Used to suppresses broadcast receive. + no_bcast: bool, +} + +impl Default for CtrlRxMode { + fn default() -> Self { + Self { + // For compatibility with older guest drivers, it + // needs to default to promiscuous. + promisc: true, + all_multi: false, + all_uni: false, + no_multi: false, + no_uni: false, + no_bcast: false, + } + } +} + +pub struct CtrlInfo { + /// The control rx mode for packet receive filtering. + rx_mode: CtrlRxMode, + /// The net device status. + state: Arc>, +} + +impl CtrlInfo { + fn new(state: Arc>) -> Self { + CtrlInfo { + rx_mode: CtrlRxMode::default(), + state, + } + } + + fn handle_rx_mode( + &mut self, + mem_space: &AddressSpace, + cmd: u8, + data_iovec: &mut Vec, + ) -> Result { + // Get the command specific data, one byte containing 0(off) or 1(on). + let mut status: u8 = 0; + get_buf_and_discard(mem_space, data_iovec, status.as_mut_bytes()) + .with_context(|| "Failed to get control data")?; + // 0: off, 1: on. + if ![0, 1].contains(&status) { + return Ok(VIRTIO_NET_ERR); + } + let mut on_off = false; + if status == 1 { + on_off = true; + } + let mut ack = VIRTIO_NET_OK; + match cmd { + VIRTIO_NET_CTRL_RX_PROMISC => self.rx_mode.promisc = on_off, + VIRTIO_NET_CTRL_RX_ALLMULTI => self.rx_mode.all_multi = on_off, + VIRTIO_NET_CTRL_RX_ALLUNI => self.rx_mode.all_uni = on_off, + VIRTIO_NET_CTRL_RX_NOMULTI => self.rx_mode.no_multi = on_off, + VIRTIO_NET_CTRL_RX_NOUNI => self.rx_mode.no_uni = on_off, + VIRTIO_NET_CTRL_RX_NOBCAST => self.rx_mode.no_bcast = on_off, + _ => { + error!("Invalid command {} for control rx mode", cmd); + ack = VIRTIO_NET_ERR; + } + } + Ok(ack) + } + + fn filter_packets(&mut self, buf: &[u8]) -> bool { + // Broadcast address: 0xff:0xff:0xff:0xff:0xff:0xff. + let bcast = [0xff; MAC_ADDR_LEN]; + + if self.rx_mode.promisc { + return false; + } + + // The bit 0 in byte[0] means unicast(0) or multicast(1). + if buf[0] & 0x01 > 0 { + if buf[..MAC_ADDR_LEN] == bcast { + return self.rx_mode.no_bcast; + } + if self.rx_mode.no_multi { + return true; + } + if self.rx_mode.all_multi { + return false; + } + } else { + if self.rx_mode.no_uni { + return true; + } + + if self.rx_mode.all_uni + || buf[..MAC_ADDR_LEN] == self.state.lock().unwrap().config_space.mac + { + return false; + } + } + + true + } +} + +fn get_buf_and_discard( + mem_space: &AddressSpace, + iovec: &mut Vec, + buf: &mut [u8], +) -> Result> { + iov_to_buf(mem_space, iovec, buf).and_then(|size| { + if size < buf.len() { + error!("Invalid length {}, expected length {}", size, buf.len()); + bail!("Invalid length {}, expected length {}", size, buf.len()); + } + Ok(()) + })?; + + if let Some(data_iovec) = iov_discard_front(iovec, buf.len() as u64) { + Ok(data_iovec.to_vec()) + } else { + Ok(Vec::new()) + } +} + /// The control queue is used to verify the multi queue feature. pub struct CtrlVirtio { + /// The control queue. queue: Arc>, + /// The eventfd used to notify the control queue event. queue_evt: EventFd, + /// The information about control command. + ctrl_info: Option>>, } impl CtrlVirtio { - pub fn new(queue: Arc>, queue_evt: EventFd) -> Self { - Self { queue, queue_evt } + pub fn new( + queue: Arc>, + queue_evt: EventFd, + ctrl_info: Option>>, + ) -> Self { + Self { + queue, + queue_evt, + ctrl_info, + } } } @@ -116,7 +271,7 @@ impl NetCtrlHandler { let mut locked_queue = self.ctrl.queue.lock().unwrap(); loop { let mut ack = VIRTIO_NET_OK; - let elem = locked_queue + let mut elem = locked_queue .vring .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net control queue")?; @@ -124,41 +279,81 @@ impl NetCtrlHandler { break; } - if let Some(ctrl_desc) = elem.out_iovec.get(0) { - let ctrl_hdr = self - .mem_space - .read_object::(ctrl_desc.addr) - .with_context(|| "Failed to get control queue descriptor")?; - match ctrl_hdr.class as u16 { - VIRTIO_NET_CTRL_MQ => { - if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { - bail!( - "Control queue header command can't match {}", - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET - ); - } - if let Some(mq_desc) = elem.out_iovec.get(1) { - let queue_pairs = self - .mem_space - .read_object::(mq_desc.addr) - .with_context(|| "Failed to read multi queue descriptor")?; - if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) - .contains(&queue_pairs) - { - bail!("Invalid queue pairs {}", queue_pairs); - } - } + // Validate the control request. + let in_size = Element::iovec_size(&elem.in_iovec); + let out_size = Element::iovec_size(&elem.out_iovec); + if in_size < mem::size_of_val(&ack) as u32 + || out_size < mem::size_of::() as u32 + { + bail!( + "Invalid length, in_iovec size is {}, out_iovec size is {}", + in_size, + out_size + ); + } + + // Get the control information first. + let ctrl_info = if let Some(ctrl_info) = &self.ctrl.ctrl_info { + ctrl_info + } else { + bail!("Control information is None"); + }; + + // Get the control request header. + let mut ctrl_hdr = CrtlHdr::default(); + let mut data_iovec = get_buf_and_discard( + &self.mem_space, + &mut elem.out_iovec, + ctrl_hdr.as_mut_bytes(), + ) + .with_context(|| "Failed to get control header")?; + + match ctrl_hdr.class { + VIRTIO_NET_CTRL_RX => { + ack = ctrl_info + .lock() + .unwrap() + .handle_rx_mode(&self.mem_space, ctrl_hdr.cmd, &mut data_iovec) + .unwrap_or_else(|e| { + error!("Failed to handle rx mode, error is {}", e); + VIRTIO_NET_ERR + }); + } + VIRTIO_NET_CTRL_MQ => { + if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { + bail!( + "Control queue header command can't match {}", + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + ); } - _ => { - error!("Control queue header class can't match {}", ctrl_hdr.class); - ack = VIRTIO_NET_ERR; + if let Some(mq_desc) = elem.out_iovec.get(1) { + let queue_pairs = self + .mem_space + .read_object::(mq_desc.addr) + .with_context(|| "Failed to read multi queue descriptor")?; + if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + .contains(&queue_pairs) + { + bail!("Invalid queue pairs {}", queue_pairs); + } } } - } - if let Some(status) = elem.in_iovec.get(0) { - self.mem_space.write_object::(&ack, status.addr)?; + _ => { + error!( + "Control queue header class {} not supported", + ctrl_hdr.class + ); + ack = VIRTIO_NET_ERR; + } } + // Write result to the device writable iovec. + let status = elem + .in_iovec + .get(0) + .with_context(|| "Failed to get device writable iovec")?; + self.mem_space.write_object::(&ack, status.addr)?; + locked_queue .vring .add_used(&self.mem_space, elem.index, mem::size_of_val(&ack) as u32) @@ -285,6 +480,7 @@ struct NetIoHandler { update_evt: EventFd, deactivate_evt: EventFd, is_listening: bool, + ctrl_info: Arc>, } impl NetIoHandler { @@ -357,6 +553,28 @@ impl NetIoHandler { break; } + let net_hdr_len = mem::size_of::(); + let mut buf = vec![0_u8; net_hdr_len + ETHERNET_HDR_LENGTH]; + get_net_header(&iovecs, &mut buf).and_then(|size| { + if size != buf.len() { + bail!( + "Invalid header length {}, expected length {}", + size, + buf.len() + ); + } + Ok(()) + })?; + if self + .ctrl_info + .lock() + .unwrap() + .filter_packets(&buf[net_hdr_len..]) + { + queue.vring.push_back(); + continue; + } + queue .vring .add_used(&self.mem_space, elem.index, size as u32) @@ -547,6 +765,21 @@ impl NetIoHandler { } } +fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { + let mut start: usize = 0; + let mut end: usize = 0; + + for elem in iovec { + end = cmp::min(start + elem.iov_len as usize, buf.len()); + mem_to_buf(&mut buf[start..end], elem.iov_base as u64)?; + if end >= buf.len() { + break; + } + start = end; + } + Ok(end) +} + fn build_event_notifier( fd: RawFd, handler: Option>, @@ -697,13 +930,15 @@ pub struct Net { /// Tap device opened. taps: Option>, /// The status of net device. - state: VirtioNetState, + state: Arc>, /// The send half of Rust's channel to send tap information. senders: Option>>, /// Eventfd for config space update. update_evt: EventFd, /// Eventfd for device deactivate. deactivate_evt: EventFd, + /// The information about control command. + ctrl_info: Option>>, } impl Default for Net { @@ -711,10 +946,11 @@ impl Default for Net { Self { net_cfg: Default::default(), taps: None, - state: VirtioNetState::default(), + state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + ctrl_info: None, } } } @@ -724,10 +960,11 @@ impl Net { Self { net_cfg, taps: None, - state: VirtioNetState::default(), + state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + ctrl_info: None, } } } @@ -876,7 +1113,8 @@ impl VirtioDevice for Net { ); } - self.state.device_features = 1 << VIRTIO_F_VERSION_1 + let mut locked_state = self.state.lock().unwrap(); + locked_state.device_features = 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GUEST_CSUM | 1 << VIRTIO_NET_F_GUEST_TSO4 @@ -885,6 +1123,8 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_TSO4 | 1 << VIRTIO_NET_F_HOST_TSO6 | 1 << VIRTIO_NET_F_HOST_UFO + | 1 << VIRTIO_NET_F_CTRL_RX + | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA | 1 << VIRTIO_F_RING_EVENT_IDX; let queue_pairs = self.net_cfg.queues / 2; @@ -892,9 +1132,9 @@ impl VirtioDevice for Net { && queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN && queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX { - self.state.device_features |= 1 << VIRTIO_NET_F_MQ; - self.state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; - self.state.config_space.max_virtqueue_pairs = queue_pairs; + locked_state.device_features |= 1 << VIRTIO_NET_F_MQ; + locked_state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; + locked_state.config_space.max_virtqueue_pairs = queue_pairs; } if !self.net_cfg.host_dev_name.is_empty() { @@ -922,14 +1162,14 @@ impl VirtioDevice for Net { // Using the first tap to test if all the taps have ufo. if let Some(tap) = self.taps.as_ref().map(|t| &t[0]) { if !tap.has_ufo() { - self.state.device_features &= + locked_state.device_features &= !(1 << VIRTIO_NET_F_GUEST_UFO | 1 << VIRTIO_NET_F_HOST_UFO); } } if let Some(mac) = &self.net_cfg.mac { - self.state.device_features |= - build_device_config_space(&mut self.state.config_space, mac); + locked_state.device_features |= + build_device_config_space(&mut locked_state.config_space, mac); } Ok(()) @@ -964,22 +1204,23 @@ impl VirtioDevice for Net { /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.state.lock().unwrap().device_features, features_select) } /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.state.lock().unwrap().driver_features, features_select) } /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.state.config_space.as_bytes(); + let locked_state = self.state.lock().unwrap(); + let config_slice = locked_state.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); @@ -993,9 +1234,11 @@ impl VirtioDevice for Net { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let config_slice = self.state.config_space.as_mut_bytes(); - if !virtio_has_feature(self.state.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) - && !virtio_has_feature(self.state.driver_features, VIRTIO_F_VERSION_1) + let mut locked_state = self.state.lock().unwrap(); + let driver_features = locked_state.driver_features; + let config_slice = locked_state.config_space.as_mut_bytes(); + if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) + && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) && offset == 0 && data_len == MAC_ADDR_LEN && *data != config_slice[0..data_len] @@ -1016,15 +1259,19 @@ impl VirtioDevice for Net { mut queue_evts: Vec, ) -> Result<()> { let queue_num = queues.len(); - if (self.state.driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { + let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); + self.ctrl_info = Some(ctrl_info.clone()); + if (self.state.lock().unwrap().driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) + && (queue_num % 2 != 0) + { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts.remove(queue_num - 1); let ctrl_handler = NetCtrlHandler { - ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt), + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, Some(ctrl_info.clone())), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, + driver_features: self.state.lock().unwrap().driver_features, deactivate_evt: self.deactivate_evt.try_clone().unwrap(), }; @@ -1061,11 +1308,12 @@ impl VirtioDevice for Net { tap_fd: -1, mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, + driver_features: self.state.lock().unwrap().driver_features, receiver, update_evt: self.update_evt.try_clone().unwrap(), deactivate_evt: self.deactivate_evt.try_clone().unwrap(), is_listening: true, + ctrl_info: ctrl_info.clone(), }; if let Some(tap) = &handler.tap { handler.tap_fd = tap.as_raw_fd(); @@ -1136,6 +1384,17 @@ impl VirtioDevice for Net { .write(1) .with_context(|| anyhow!(VirtioError::EventFdWrite)) } + + fn reset(&mut self) -> Result<()> { + if let Some(ctrl_info) = &self.ctrl_info { + let mut locked_ctrl = ctrl_info.lock().unwrap(); + locked_ctrl.rx_mode = Default::default(); + } else { + bail!("Control information is None"); + } + + Ok(()) + } } // Send and Sync is not auto-implemented for `Sender` type. @@ -1145,12 +1404,16 @@ unsafe impl Sync for Net {} impl StateTransfer for Net { fn get_state_vec(&self) -> migration::Result> { - Ok(self.state.as_bytes().to_vec()) + Ok(self.state.lock().unwrap().as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *VirtioNetState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("NET")))?; + let s_len = std::mem::size_of::(); + if state.len() != s_len { + bail!("Invalid state length {}, expected {}", state.len(), s_len); + } + let mut locked_state = self.state.lock().unwrap(); + locked_state.as_mut_bytes().copy_from_slice(state); Ok(()) } @@ -1177,8 +1440,8 @@ mod tests { fn test_net_init() { // test net new method let mut net = Net::default(); - assert_eq!(net.state.device_features, 0); - assert_eq!(net.state.driver_features, 0); + assert_eq!(net.state.lock().unwrap().device_features, 0); + assert_eq!(net.state.lock().unwrap().driver_features, 0); assert_eq!(net.taps.is_none(), true); assert_eq!(net.senders.is_none(), true); @@ -1206,8 +1469,10 @@ mod tests { net.write_config(0x00, &origin_data).unwrap(); // test boundary condition of offset and data parameters - let device_config = net.state.config_space.as_bytes(); + let locked_state = net.state.lock().unwrap(); + let device_config = locked_state.config_space.as_bytes(); let len = device_config.len() as u64; + drop(locked_state); let mut data: Vec = vec![0; 10]; let offset: u64 = len + 1; diff --git a/virtio/src/queue.rs b/virtio/src/queue.rs index cbfac1a2e..9e44a4b38 100644 --- a/virtio/src/queue.rs +++ b/virtio/src/queue.rs @@ -158,6 +158,14 @@ impl Element { in_iovec: Vec::new(), } } + + pub fn iovec_size(iovec: &[ElemIovec]) -> u32 { + let mut size: u32 = 0; + for elem in iovec.iter() { + size += elem.len; + } + size + } } /// Vring operations. diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 818ec0903..dac20739b 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -259,7 +259,7 @@ impl VirtioDevice for Net { let ctrl_queue_evt = queue_evts.remove(queue_num - 1); let ctrl_handler = NetCtrlHandler { - ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt), + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, None), mem_space, interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index d42770235..1638d8204 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -242,7 +242,7 @@ impl VirtioDevice for Net { let ctrl_queue_evt = queue_evts.remove(queue_num - 1); let ctrl_handler = NetCtrlHandler { - ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt), + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, None), mem_space, interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, -- Gitee From 50400e79703584442cb5b1c7b1146c1b1f79ce32 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 17:47:16 +0800 Subject: [PATCH 0506/2187] virtio-net: correct the abbreviation name "CrtlHdr" to "CtrlHdr" Signed-off-by: Yan Wang --- virtio/src/net.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 13bd482b3..9b6e061d3 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -259,12 +259,12 @@ pub struct NetCtrlHandler { #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] -struct CrtlHdr { +struct CtrlHdr { class: u8, cmd: u8, } -impl ByteCode for CrtlHdr {} +impl ByteCode for CtrlHdr {} impl NetCtrlHandler { fn handle_ctrl(&mut self) -> Result<()> { @@ -283,7 +283,7 @@ impl NetCtrlHandler { let in_size = Element::iovec_size(&elem.in_iovec); let out_size = Element::iovec_size(&elem.out_iovec); if in_size < mem::size_of_val(&ack) as u32 - || out_size < mem::size_of::() as u32 + || out_size < mem::size_of::() as u32 { bail!( "Invalid length, in_iovec size is {}, out_iovec size is {}", @@ -300,7 +300,7 @@ impl NetCtrlHandler { }; // Get the control request header. - let mut ctrl_hdr = CrtlHdr::default(); + let mut ctrl_hdr = CtrlHdr::default(); let mut data_iovec = get_buf_and_discard( &self.mem_space, &mut elem.out_iovec, -- Gitee From 7bcb0e570338941f864038bf305278a839103ff0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 26 Nov 2022 14:28:32 +0800 Subject: [PATCH 0507/2187] virtio-net: support feature VIRTIO_NET_F_CTRL_MAC_ADDR Enable filtering based on mac address. Drop the packets the guest doesn't want to see. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 7 +++ virtio/src/net.rs | 157 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 152 insertions(+), 12 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 44d7c8759..c87fcf506 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -182,6 +182,13 @@ pub const VIRTIO_NET_CTRL_RX_NOUNI: u8 = 4; /// Control commands for suppressing broadcast receive. pub const VIRTIO_NET_CTRL_RX_NOBCAST: u8 = 5; +/// The driver can send control commands for MAC address filtering. +pub const VIRTIO_NET_CTRL_MAC: u8 = 1; +/// The driver sets the unicast/multicast addresse table. +pub const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0; +/// The driver sets the default MAC address which rx filtering accepts. +pub const VIRTIO_NET_CTRL_MAC_ADDR_SET: u8 = 1; + /// Driver configure the class before enabling virtqueue. pub const VIRTIO_NET_CTRL_MQ: u8 = 4; /// Driver configure the command before enabling virtqueue. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 9b6e061d3..9b89ab411 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -19,16 +19,16 @@ use std::{cmp, fs, mem}; use super::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, - VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, - VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, - VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, + VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, + VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, + VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, + VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, @@ -60,6 +60,8 @@ const QUEUE_SIZE_NET: u16 = 256; pub const MAC_ADDR_LEN: usize = 6; /// The length of ethernet header. const ETHERNET_HDR_LENGTH: usize = 14; +/// The max "multicast + unicast" mac address table length. +const CTRL_MAC_TABLE_LEN: usize = 64; type SenderConfig = Option; @@ -115,9 +117,29 @@ impl Default for CtrlRxMode { } } +#[derive(Default, Clone)] +struct MacAddress { + pub address: [u8; MAC_ADDR_LEN], +} + +/// The Mac information used to filter incoming packet. +#[derive(Default)] +struct CtrlMacInfo { + /// Unicast mac address table. + uni_mac_table: Vec, + /// Unicast mac address overflow. + uni_mac_of: bool, + /// Multicast mac address table. + multi_mac_table: Vec, + /// Multicast mac address overflow. + multi_mac_of: bool, +} + pub struct CtrlInfo { /// The control rx mode for packet receive filtering. rx_mode: CtrlRxMode, + /// The mac address information for packet receive filtering. + mac_info: CtrlMacInfo, /// The net device status. state: Arc>, } @@ -126,6 +148,7 @@ impl CtrlInfo { fn new(state: Arc>) -> Self { CtrlInfo { rx_mode: CtrlRxMode::default(), + mac_info: CtrlMacInfo::default(), state, } } @@ -164,6 +187,97 @@ impl CtrlInfo { Ok(ack) } + fn set_mac_table( + &mut self, + mem_space: &AddressSpace, + data_iovec: &mut Vec, + ) -> Result { + let ack = VIRTIO_NET_OK; + let mut mac_table_len = 0; + // Default for unicast. + let mut overflow = &mut self.mac_info.uni_mac_of; + let mut mac_table = &mut self.mac_info.uni_mac_table; + + // 0 for unicast, 1 for multicast. + for i in 0..2 { + if i == 1 { + overflow = &mut self.mac_info.multi_mac_of; + mac_table_len = self.mac_info.uni_mac_table.len(); + mac_table = &mut self.mac_info.multi_mac_table; + } + + let mut entries: u32 = 0; + *data_iovec = get_buf_and_discard(mem_space, data_iovec, entries.as_mut_bytes()) + .with_context(|| "Failed to get unicast MAC entries".to_string())?; + if entries == 0 { + mac_table.clear(); + continue; + } + + let mut macs = vec![0_u8; entries as usize * MAC_ADDR_LEN]; + *data_iovec = get_buf_and_discard(mem_space, data_iovec, &mut macs) + .with_context(|| "Failed to get multicast MAC entries".to_string())?; + if entries as usize > CTRL_MAC_TABLE_LEN - mac_table_len { + *overflow = true; + mac_table.clear(); + continue; + } + + mac_table.clear(); + for i in 0..entries { + let offset = i as usize * MAC_ADDR_LEN; + let mut mac: MacAddress = Default::default(); + mac.address + .copy_from_slice(&macs[offset..offset + MAC_ADDR_LEN]); + mac_table.push(mac); + } + } + Ok(ack) + } + + fn handle_mac( + &mut self, + mem_space: &AddressSpace, + cmd: u8, + data_iovec: &mut Vec, + ) -> u8 { + let mut ack = VIRTIO_NET_OK; + match cmd { + VIRTIO_NET_CTRL_MAC_ADDR_SET => { + let mut mac = [0; MAC_ADDR_LEN]; + *data_iovec = + get_buf_and_discard(mem_space, data_iovec, &mut mac).unwrap_or_else(|e| { + error!("Failed to get MAC address, error is {}", e); + ack = VIRTIO_NET_ERR; + Vec::new() + }); + if ack == VIRTIO_NET_ERR { + return VIRTIO_NET_ERR; + } + self.state + .lock() + .unwrap() + .config_space + .mac + .copy_from_slice(&mac); + } + VIRTIO_NET_CTRL_MAC_TABLE_SET => { + ack = self + .set_mac_table(mem_space, data_iovec) + .unwrap_or_else(|e| { + error!("Failed to get Unicast Mac address, error is {}", e); + VIRTIO_NET_ERR + }); + } + _ => { + error!("Invalid cmd {} when handling control mac", cmd); + return VIRTIO_NET_ERR; + } + } + + ack + } + fn filter_packets(&mut self, buf: &[u8]) -> bool { // Broadcast address: 0xff:0xff:0xff:0xff:0xff:0xff. let bcast = [0xff; MAC_ADDR_LEN]; @@ -180,19 +294,29 @@ impl CtrlInfo { if self.rx_mode.no_multi { return true; } - if self.rx_mode.all_multi { + if self.rx_mode.all_multi || self.mac_info.multi_mac_of { return false; } + for mac in self.mac_info.multi_mac_table.iter() { + if buf[..MAC_ADDR_LEN] == mac.address { + return false; + } + } } else { if self.rx_mode.no_uni { return true; } - if self.rx_mode.all_uni + || self.mac_info.uni_mac_of || buf[..MAC_ADDR_LEN] == self.state.lock().unwrap().config_space.mac { return false; } + for mac in self.mac_info.uni_mac_table.iter() { + if buf[..MAC_ADDR_LEN] == mac.address { + return false; + } + } } true @@ -319,6 +443,13 @@ impl NetCtrlHandler { VIRTIO_NET_ERR }); } + VIRTIO_NET_CTRL_MAC => { + ack = ctrl_info.lock().unwrap().handle_mac( + &self.mem_space, + ctrl_hdr.cmd, + &mut data_iovec, + ); + } VIRTIO_NET_CTRL_MQ => { if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { bail!( @@ -1125,6 +1256,7 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_NET_F_CTRL_RX | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA + | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR | 1 << VIRTIO_F_RING_EVENT_IDX; let queue_pairs = self.net_cfg.queues / 2; @@ -1389,6 +1521,7 @@ impl VirtioDevice for Net { if let Some(ctrl_info) = &self.ctrl_info { let mut locked_ctrl = ctrl_info.lock().unwrap(); locked_ctrl.rx_mode = Default::default(); + locked_ctrl.mac_info = Default::default(); } else { bail!("Control information is None"); } -- Gitee From cd809c8bab57867e5419fb7bb0c08d12897faf35 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 24 Nov 2022 18:16:40 +0800 Subject: [PATCH 0508/2187] virtio-net: support feature VIRTIO_NET_F_CTRL_VLAN Enable filtering based on vlan. Drop the packets the guest doesn't want to see. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 9 +++++ virtio/src/net.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index c87fcf506..f2169b1bf 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -135,6 +135,8 @@ pub const VIRTIO_NET_F_MRG_RXBUF: u32 = 15; pub const VIRTIO_NET_F_CTRL_VQ: u32 = 17; /// Control channel RX mode support. pub const VIRTIO_NET_F_CTRL_RX: u32 = 18; +/// Control channel VLAN filtering. +pub const VIRTIO_NET_F_CTRL_VLAN: u32 = 19; /// Extra RX mode control support. pub const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20; /// Device supports multi queue with automatic receive steering. @@ -189,6 +191,13 @@ pub const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0; /// The driver sets the default MAC address which rx filtering accepts. pub const VIRTIO_NET_CTRL_MAC_ADDR_SET: u8 = 1; +/// The driver can send control commands for vlan filtering. +pub const VIRTIO_NET_CTRL_VLAN: u8 = 2; +/// The driver adds a vlan id to the vlan filtering table. +pub const VIRTIO_NET_CTRL_VLAN_ADD: u8 = 0; +/// The driver adds a vlan id from the vlan filtering table. +pub const VIRTIO_NET_CTRL_VLAN_DEL: u8 = 1; + /// Driver configure the class before enabling virtqueue. pub const VIRTIO_NET_CTRL_MQ: u8 = 4; /// Driver configure the command before enabling virtqueue. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 9b89ab411..2e7c541a0 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::HashMap; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; @@ -24,11 +25,13 @@ use super::{ VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, - VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, - VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, @@ -62,6 +65,8 @@ pub const MAC_ADDR_LEN: usize = 6; const ETHERNET_HDR_LENGTH: usize = 14; /// The max "multicast + unicast" mac address table length. const CTRL_MAC_TABLE_LEN: usize = 64; +/// From 802.1Q definition, the max vlan ID. +const CTRL_MAX_VLAN: u16 = 1 << 12; type SenderConfig = Option; @@ -140,6 +145,8 @@ pub struct CtrlInfo { rx_mode: CtrlRxMode, /// The mac address information for packet receive filtering. mac_info: CtrlMacInfo, + /// The map of all the vlan ids. + vlan_map: HashMap, /// The net device status. state: Arc>, } @@ -149,6 +156,7 @@ impl CtrlInfo { CtrlInfo { rx_mode: CtrlRxMode::default(), mac_info: CtrlMacInfo::default(), + vlan_map: HashMap::new(), state, } } @@ -278,14 +286,72 @@ impl CtrlInfo { ack } + fn handle_vlan_table( + &mut self, + mem_space: &AddressSpace, + cmd: u8, + data_iovec: &mut Vec, + ) -> u8 { + let mut ack = VIRTIO_NET_OK; + let mut vid: u16 = 0; + + *data_iovec = get_buf_and_discard(mem_space, data_iovec, vid.as_mut_bytes()) + .unwrap_or_else(|e| { + error!("Failed to get vlan id, error is {}", e); + ack = VIRTIO_NET_ERR; + Vec::new() + }); + if ack == VIRTIO_NET_ERR { + return ack; + } + if vid >= CTRL_MAX_VLAN { + return VIRTIO_NET_ERR; + } + + match cmd { + VIRTIO_NET_CTRL_VLAN_ADD => { + if let Some(value) = self.vlan_map.get_mut(&(vid >> 5)) { + *value |= 1 << (vid & 0x1f); + } else { + self.vlan_map.insert(vid >> 5, 1 << (vid & 0x1f)); + } + } + VIRTIO_NET_CTRL_VLAN_DEL => { + if let Some(value) = self.vlan_map.get_mut(&(vid >> 5)) { + *value &= !(1 << (vid & 0x1f)); + } + } + _ => { + error!("Invalid cmd {} when handling control vlan", cmd); + ack = VIRTIO_NET_ERR; + } + } + ack + } + fn filter_packets(&mut self, buf: &[u8]) -> bool { // Broadcast address: 0xff:0xff:0xff:0xff:0xff:0xff. let bcast = [0xff; MAC_ADDR_LEN]; + // TPID of the vlan tag, defined in IEEE 802.1Q, is 0x8100. + let vlan = [0x81, 0x00]; if self.rx_mode.promisc { return false; } + if buf[..vlan.len()] == vlan { + let vid = u16::from_be_bytes([buf[14], buf[15]]); + let value = if let Some(value) = self.vlan_map.get(&(vid >> 5)) { + *value + } else { + 0 + }; + + if value & (1 << (vid & 0x1f)) == 0 { + return true; + } + } + // The bit 0 in byte[0] means unicast(0) or multicast(1). if buf[0] & 0x01 > 0 { if buf[..MAC_ADDR_LEN] == bcast { @@ -450,6 +516,13 @@ impl NetCtrlHandler { &mut data_iovec, ); } + VIRTIO_NET_CTRL_VLAN => { + ack = ctrl_info.lock().unwrap().handle_vlan_table( + &self.mem_space, + ctrl_hdr.cmd, + &mut data_iovec, + ); + } VIRTIO_NET_CTRL_MQ => { if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { bail!( @@ -1255,6 +1328,7 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_TSO6 | 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_NET_F_CTRL_RX + | 1 << VIRTIO_NET_F_CTRL_VLAN | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR | 1 << VIRTIO_F_RING_EVENT_IDX; @@ -1522,6 +1596,7 @@ impl VirtioDevice for Net { let mut locked_ctrl = ctrl_info.lock().unwrap(); locked_ctrl.rx_mode = Default::default(); locked_ctrl.mac_info = Default::default(); + locked_ctrl.vlan_map = HashMap::new(); } else { bail!("Control information is None"); } -- Gitee From b99e7eb3a83c001f71bf89e20bfbe3a769b0ecfc Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 26 Nov 2022 09:40:42 +0800 Subject: [PATCH 0509/2187] virtio-net: just return error for unsupported control command It should not use "bail" in handle_ctrl() which will cause reporting virtio error. Just return error status to the guest. Signed-off-by: Yan Wang --- virtio/src/net.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 2e7c541a0..617e8feeb 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -525,10 +525,11 @@ impl NetCtrlHandler { } VIRTIO_NET_CTRL_MQ => { if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { - bail!( + error!( "Control queue header command can't match {}", VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET ); + ack = VIRTIO_NET_ERR; } if let Some(mq_desc) = elem.out_iovec.get(1) { let queue_pairs = self @@ -538,7 +539,8 @@ impl NetCtrlHandler { if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { - bail!("Invalid queue pairs {}", queue_pairs); + error!("Invalid queue pairs {}", queue_pairs); + ack = VIRTIO_NET_ERR; } } } -- Gitee From d052a782f13a6f6696bbe0873497bc7b54567ae3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 25 Nov 2022 12:52:10 +0800 Subject: [PATCH 0510/2187] xhci: update dev_id before send msix On aarch64 platform, dev_id is necessary for pci device to send msix. Update dev_id before send msix. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_pci.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 99594dd71..865256a32 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -20,6 +20,7 @@ use pci::config::{ PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; +use pci::msix::update_dev_id; use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; use crate::bus::{BusDeviceMap, BusDeviceOps}; @@ -242,6 +243,7 @@ impl PciDevOps for XhciPciDevice { } fn write_config(&mut self, offset: usize, data: &[u8]) { + update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); let parent_bus = self.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); -- Gitee From 8944670fefc15f012882826274dfe1007cb718d8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 27 Nov 2022 13:48:45 +0800 Subject: [PATCH 0511/2187] Fix: reported error when TRB Direction is mismatch The error shall be reported if the TRB data transmission direction is wrong. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 11 +++++++++++ usb/src/xhci/xhci_ring.rs | 2 ++ 2 files changed, 13 insertions(+) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index e47a927e8..39979d7bb 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -33,6 +33,7 @@ use crate::xhci::xhci_ring::{ use anyhow::{bail, Context, Result}; use super::xhci_ring::SETUP_TRB_TR_LEN; +use super::xhci_ring::TRB_TR_DIR; use super::xhci_ring::TRB_TR_LEN_MASK; pub const MAX_INTRS: u16 = 16; @@ -1370,6 +1371,8 @@ impl XhciDevice { } else { USB_TOKEN_OUT }; + let in_xfer = dir == USB_TOKEN_IN; + // Map dma address to iovec. let mut vec = Vec::new(); for trb in &xfer.td { @@ -1377,12 +1380,20 @@ impl XhciDevice { if trb.control & TRB_TR_IOC == TRB_TR_IOC { xfer.int_req = true; } + + if trb_type == TRBType::TrData && (trb.control & TRB_TR_DIR == 0) == in_xfer { + bail!("Direction of data transfer is mismatch"); + } + if trb_type == TRBType::TrData || trb_type == TRBType::TrNormal || trb_type == TRBType::TrIsoch { let chunk = trb.status & TRB_TR_LEN_MASK; let dma_addr = if trb.control & TRB_TR_IDT == TRB_TR_IDT { + if chunk > 8 && in_xfer { + bail!("Invalid immediate data TRB"); + } trb.addr } else { trb.parameter diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 77710d512..1e2679d22 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -37,6 +37,8 @@ pub const TRB_TR_CH: u32 = 1 << 4; pub const TRB_TR_IOC: u32 = 1 << 5; /// Immediate Data. pub const TRB_TR_IDT: u32 = 1 << 6; +/// Direction of the data transfer. +pub const TRB_TR_DIR: u32 = 1 << 16; /// TRB Transfer Length Mask pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; /// Setup Stage TRB Length always 8 -- Gitee From 1872d4d95fd441e0894ca1fb1dffcebda06a0bc1 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 27 Nov 2022 14:13:23 +0800 Subject: [PATCH 0512/2187] Fix: Control transfer verification is processed uniformly. The trb_status will not be used in the do_ctrl_transfer except in the check_ctrl_transfer, so it will be only obtained in the check_ctrl_transfer. Signed-off-by: Mingwang Li --- usb/src/xhci/xhci_controller.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 39979d7bb..b8fb3862b 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1296,17 +1296,11 @@ impl XhciDevice { /// Control Transfer, TRBs include Setup, Data(option), Status. fn do_ctrl_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - let trb_setup = xfer.td[0]; - let mut trb_status = xfer.td[xfer.td.len() - 1]; - let status_type = trb_status.get_type(); - if status_type == TRBType::TrEvdata && xfer.td.len() > 2 { - trb_status = xfer.td[xfer.td.len() - 2]; - } - if let Err(e) = self.check_ctrl_transfer(&trb_setup, &trb_status) { + if let Err(e) = self.check_ctrl_transfer(xfer) { error!("Failed to check control transfer {}", e); return self.report_transfer_error(xfer); } - + let trb_setup = xfer.td[0]; let bm_request_type = trb_setup.parameter as u8; xfer.in_xfer = bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; @@ -1320,7 +1314,15 @@ impl XhciDevice { Ok(()) } - fn check_ctrl_transfer(&self, trb_setup: &XhciTRB, trb_status: &XhciTRB) -> Result<()> { + fn check_ctrl_transfer(&self, xfer: &mut XhciTransfer) -> Result<()> { + let trb_setup = xfer.td[0]; + let mut trb_status = xfer.td[xfer.td.len() - 1]; + let status_type = trb_status.get_type(); + + if status_type == TRBType::TrEvdata && xfer.td.len() > 2 { + trb_status = xfer.td[xfer.td.len() - 2]; + } + let setup_type = trb_setup.get_type(); if setup_type != TRBType::TrSetup { bail!("The first TRB is not Setup"); -- Gitee From de897851e338ac94dca4f7022383783789eaea6c Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 26 Nov 2022 16:28:47 +0800 Subject: [PATCH 0513/2187] Rename the index field of CpregListEntry to reg_id The index field of CpregListEntry represents the ID of the register, and renaming it to reg_id makes the meaning of this field more clear. Signed-off-by: Yan Wen --- cpu/src/aarch64/caps.rs | 18 +++++++++--------- cpu/src/aarch64/mod.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index 199b8cdff..85c19abaf 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -67,13 +67,13 @@ impl From<&CpuConfig> for ArmCPUFeatures { /// Entry to cpreg list. #[derive(Default, Clone, Copy)] pub struct CpregListEntry { - pub index: u64, + pub reg_id: u64, pub value: u64, } impl CpregListEntry { fn cpreg_tuples_entry(&self) -> bool { - self.index & KVM_REG_ARM_COPROC_MASK as u64 == KVM_REG_ARM_CORE as u64 + self.reg_id & KVM_REG_ARM_COPROC_MASK as u64 == KVM_REG_ARM_CORE as u64 } fn normal_cpreg_entry(&self) -> bool { @@ -81,8 +81,8 @@ impl CpregListEntry { return false; } - ((self.index & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) - || ((self.index & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) + ((self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) + || ((self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) } /// Validate cpreg_list's tuples entry and normal entry. @@ -101,8 +101,8 @@ impl CpregListEntry { /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn get_cpreg(&mut self, vcpu_fd: &VcpuFd) -> Result<()> { if self.normal_cpreg_entry() { - let val = get_one_reg_vec(vcpu_fd, self.index)?; - if (self.index & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32 { + let val = get_one_reg_vec(vcpu_fd, self.reg_id)?; + if (self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32 { self.value = u32::from_be_bytes( val.as_slice() .split_at(size_of::()) @@ -110,7 +110,7 @@ impl CpregListEntry { .try_into() .unwrap(), ) as u64; - } else if (self.index & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64 { + } else if (self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64 { self.value = u64::from_be_bytes( val.as_slice() .split_at(size_of::()) @@ -132,13 +132,13 @@ impl CpregListEntry { pub fn set_cpreg(&self, vcpu_fd: &VcpuFd) -> Result<()> { if self.normal_cpreg_entry() { let mut value: Vec = self.value.to_be_bytes().to_vec(); - let data = if (self.index & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32 { + let data = if (self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32 { value.split_off(size_of::() / size_of::()) } else { value }; - set_one_reg_vec(vcpu_fd, self.index, &data)?; + set_one_reg_vec(vcpu_fd, self.reg_id, &data)?; } Ok(()) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index c84890a56..cf6e43292 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -318,7 +318,7 @@ impl StateTransfer for CPU { cpu_state_locked.cpreg_len = 0; for (index, cpreg) in cpreg_list.as_slice().iter().enumerate() { let mut cpreg_entry = CpregListEntry { - index: *cpreg, + reg_id: *cpreg, value: 0, }; if cpreg_entry.validate() { -- Gitee From 17c462db8da6ec7a73ebdd616ee52fb711970220 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Mon, 28 Nov 2022 10:17:52 +0800 Subject: [PATCH 0514/2187] Unified coding style, remove macro-use-extern-crate code the use of extern crate is being phased out in the 2018 edition. To bring macros from extern crates into scope, it is recommended to use a use import. Signed-off-by: Yan Wen --- machine/src/lib.rs | 3 +-- machine/src/micro_vm/mod.rs | 1 - migration/src/lib.rs | 7 +++---- migration/src/manager.rs | 1 + migration/src/migration.rs | 1 + usb/src/descriptor.rs | 3 ++- usb/src/hid.rs | 2 ++ usb/src/keyboard.rs | 1 + usb/src/lib.rs | 3 --- usb/src/tablet.rs | 1 + usb/src/usb.rs | 1 + usb/src/xhci/xhci_controller.rs | 1 + usb/src/xhci/xhci_pci.rs | 1 + usb/src/xhci/xhci_regs.rs | 1 + usb/src/xhci/xhci_ring.rs | 1 + vhost_user_fs/src/fuse_msg.rs | 1 + vhost_user_fs/src/fuse_proc.rs | 1 + vhost_user_fs/src/fuse_req.rs | 1 + vhost_user_fs/src/lib.rs | 7 ------- vhost_user_fs/src/main.rs | 4 +--- vhost_user_fs/src/sandbox.rs | 1 + vhost_user_fs/src/vhost_user_fs.rs | 1 + vhost_user_fs/src/vhost_user_server.rs | 1 + vhost_user_fs/src/virtio_fs.rs | 1 + virtio/src/block.rs | 2 +- virtio/src/lib.rs | 7 +++---- virtio/src/net.rs | 2 +- virtio/src/scsi/controller.rs | 2 +- vnc/src/auth.rs | 1 + vnc/src/client.rs | 1 + vnc/src/input.rs | 1 + vnc/src/lib.rs | 7 ------- vnc/src/server.rs | 1 + vnc/src/vencrypt.rs | 1 + vnc/src/vnc.rs | 1 + 35 files changed, 37 insertions(+), 35 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d2861afd5..a7e2be545 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,12 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod error; mod micro_vm; mod standard_vm; #[cfg(target_arch = "x86_64")] mod vm_state; -extern crate util; -pub mod error; pub use crate::error::MachineError; use std::collections::BTreeMap; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index b80b21c33..0cddd0178 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -34,7 +34,6 @@ use util::aio::AIO_NATIVE; mod mem_layout; mod syscall; -extern crate util; use super::Result as MachineResult; use log::error; diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 20bbcf5d9..b469af499 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -14,22 +14,21 @@ //! //! Offer snapshot and migration interface for VM. -#[macro_use] -extern crate log; -use anyhow::anyhow; pub mod general; pub mod manager; pub mod migration; pub mod protocol; pub mod snapshot; +use std::time::Duration; use std::{net::TcpStream, os::unix::net::UnixStream, thread}; +use anyhow::anyhow; pub use anyhow::Result; +use log::error; use machine_manager::qmp::{qmp_schema, Response}; pub use manager::{MigrationHook, MigrationManager}; pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; -use std::time::Duration; pub mod error; pub use error::MigrationError; diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 73e562ca3..d28d6c4bf 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -17,6 +17,7 @@ use std::io::{Read, Write}; use std::sync::{Arc, Mutex, RwLock}; use std::time::Instant; +use log::info; use once_cell::sync::Lazy; use crate::general::translate_id; diff --git a/migration/src/migration.rs b/migration/src/migration.rs index ac194952f..35a81bee2 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; use kvm_bindings::kvm_userspace_memory_region as MemorySlot; +use log::{info, warn}; use crate::general::Lifecycle; use crate::manager::MIGRATION_MANAGER; diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index 5d701eda0..d1e4c6ec1 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -12,11 +12,12 @@ use std::sync::Arc; +use anyhow::{bail, Result}; +use log::error; use util::byte_code::ByteCode; use crate::config::*; use crate::usb::{UsbDescConfig, UsbDescEndpoint, UsbDescIface, UsbDevice}; -use anyhow::{bail, Result}; /// USB device descriptor for transfer #[allow(non_snake_case)] diff --git a/usb/src/hid.rs b/usb/src/hid.rs index aaf94fb76..ec38eb87c 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -12,6 +12,8 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; +use log::{debug, error, warn}; + use crate::config::*; use crate::usb::{usb_packet_transfer, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index dc01f2288..5a943dd94 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex, Weak}; +use log::{debug, error, info}; use once_cell::sync::Lazy; use crate::config::*; diff --git a/usb/src/lib.rs b/usb/src/lib.rs index 53493522c..c3b25759e 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -10,9 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate log; - pub mod error; pub use anyhow::Result; pub use error::UsbError; diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index dbfaddaa0..2189311b7 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex, Weak}; +use log::{debug, error, info}; use once_cell::sync::Lazy; use crate::config::*; diff --git a/usb/src/usb.rs b/usb/src/usb.rs index eca2f2aee..eedf40754 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -22,6 +22,7 @@ use crate::descriptor::{ }; use crate::xhci::xhci_controller::XhciDevice; use anyhow::{bail, Result}; +use log::{debug, error, warn}; const USB_MAX_ENDPOINTS: u32 = 15; const USB_MAX_INTERFACES: u32 = 16; diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index b8fb3862b..a934bd718 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -17,6 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; +use log::{debug, error, info, warn}; use machine_manager::config::XhciConfig; use util::num_ops::{read_u32, write_u64_low}; diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 865256a32..93d5cac00 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -15,6 +15,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; +use log::{debug, error}; use machine_manager::config::XhciConfig; use pci::config::{ PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 4988e1337..d210d53d6 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -16,6 +16,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; +use log::{debug, error}; use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; use crate::config::*; diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 1e2679d22..c29ea04c1 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -15,6 +15,7 @@ use std::sync::Arc; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; +use log::debug; use crate::xhci::xhci_controller::dma_read_bytes; use anyhow::{bail, Result}; diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 40c88906d..9b9a8581e 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -146,6 +146,7 @@ use std::mem::size_of; use std::sync::Arc; use address_space::AddressSpace; +use log::error; use util::byte_code::ByteCode; use anyhow::{bail, Context, Result}; diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index b5558a8b7..d13389f70 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -15,6 +15,7 @@ const MAX_WRITE_SIZE: u32 = 1 << 20; use super::fs::FileSystem; use super::fuse_msg::*; use address_space::AddressSpace; +use log::error; use std::convert::TryInto; use std::ffi::CString; use std::mem; diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 1d5f98393..8543a5cf7 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -14,6 +14,7 @@ use super::fs::FileSystem; use super::fuse_msg::*; use super::fuse_proc::*; use address_space::AddressSpace; +use log::error; use std::sync::{Arc, Mutex}; use virtio::Element; diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index e8ec9f670..8e7686428 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -10,13 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate log; -extern crate address_space; -extern crate machine_manager; -extern crate util; -extern crate virtio; - pub mod cmdline; pub mod fs; pub mod fs_ops; diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 0a12b44e1..cacad4e3e 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -10,10 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate log; -extern crate vhost_user_fs; use anyhow::{bail, Context, Result}; +use log::{error, info}; use machine_manager::event_loop::EventLoop; use machine_manager::signal_handler; use std::collections::HashSet; diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs index 8b5bf09fc..dc44c0f54 100644 --- a/vhost_user_fs/src/sandbox.rs +++ b/vhost_user_fs/src/sandbox.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use anyhow::{bail, Result}; +use log::error; use std::ffi::CString; use std::fs; use std::fs::File; diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index bfe54e526..d20901283 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -13,6 +13,7 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; +use log::error; use vmm_sys_util::epoll::EventSet; use machine_manager::event_loop::EventLoop; diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 3d0ff1a97..d873ac6f7 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use log::error; use std::mem::size_of; use std::os::unix::io::RawFd; use std::slice; diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index fbf5ff842..276dab6dc 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -28,6 +28,7 @@ use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::{Arc, Mutex}; +use log::error; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 97f619aba..cc84ae08e 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -32,7 +32,7 @@ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::error; +use log::{error, warn}; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index f2169b1bf..560af1e4a 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -24,11 +24,11 @@ //! //! - `x86_64` //! - `aarch64` -#[macro_use] -extern crate log; + mod balloon; mod block; mod console; +pub mod error; #[cfg(not(target_env = "musl"))] mod gpu; mod net; @@ -39,8 +39,6 @@ pub mod vhost; mod virtio_mmio; #[allow(dead_code)] mod virtio_pci; -extern crate util; -pub mod error; pub use anyhow::Result; pub use balloon::*; pub use block::{Block, BlockState}; @@ -49,6 +47,7 @@ pub use error::VirtioError; pub use error::*; #[cfg(not(target_env = "musl"))] pub use gpu::*; +use log::{error, warn}; pub use net::*; pub use queue::*; pub use rng::{Rng, RngState}; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 617e8feeb..749170ffb 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -39,7 +39,7 @@ use crate::{ }; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; -use log::error; +use log::{error, warn}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 1348e693f..ea9ebcd53 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -30,7 +30,7 @@ use crate::ScsiBus::{ }; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; -use log::{error, info}; +use log::{debug, error, info}; use machine_manager::{ config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index d3cf3370a..28a11ea77 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -13,6 +13,7 @@ use crate::{client::VncClient, VncError}; use anyhow::{anyhow, Result}; use libc::{c_char, c_int, c_uint, c_void}; +use log::{error, info}; use sasl2_sys::prelude::{ sasl_conn_t, sasl_dispose, sasl_getprop, sasl_listmech, sasl_security_properties_t, sasl_server_init, sasl_server_new, sasl_server_start, sasl_server_step, sasl_setprop, diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 1d33c6fa1..2be074458 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -25,6 +25,7 @@ use crate::{ }, }; use anyhow::{anyhow, Result}; +use log::{error, info}; use machine_manager::event_loop::EventLoop; use rustls::ServerConnection; use sscanf::scanf; diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 5218d67c7..9c524938c 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::client::VncClient; +use log::error; use usb::{ keyboard::keyboard_event, tablet::{pointer_event, pointer_sync}, diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index e7c77b1d4..227e177cc 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -10,13 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[macro_use] -extern crate log; -//#[macro_use] -extern crate sscanf; -//#[macro_use] -extern crate vmm_sys_util; - pub mod error; pub use anyhow::Result; pub use error::VncError; diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 7d01684e1..eb6b05b63 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -29,6 +29,7 @@ use crate::{ VncError, }; use anyhow::{anyhow, Result}; +use log::{error, info}; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 9c219575a..ca2ca958b 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -12,6 +12,7 @@ use crate::{auth::SubAuthState, client::VncClient, VncError}; use anyhow::{anyhow, Result}; +use log::{error, info}; use rustls::{ self, cipher_suite::{ diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 241a933d8..ca00c977a 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -25,6 +25,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use core::time; +use log::error; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, -- Gitee From 4bd668ea579136e0e6f53706f91d589f91a76a21 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Sun, 20 Nov 2022 11:31:00 +0000 Subject: [PATCH 0515/2187] virtio-gpu: add message display before the real picture is presented "Display is not active" wiil be show in scanout before a real meanful picture be presented. May be more different msg will be added if it's necessary. --- devices/src/legacy/ramfb.rs | 2 +- virtio/src/gpu.rs | 71 +++++- vnc/src/pixman.rs | 469 +++++++++++++++++++++++++++++++++++- vnc/src/vnc.rs | 4 +- 4 files changed, 535 insertions(+), 11 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index a44d160cc..cbc569db4 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -184,7 +184,7 @@ impl FwCfgWriteCallback for RamfbState { self.create_display_surface(width, height, format, stride, addr); - vnc_display_switch(&mut self.surface.unwrap()); + vnc_display_switch(&self.surface.unwrap()); vnc_loop_update_display(0, 0, width as i32, height as i32); } } diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index bdc74ddca..f8cbf8ace 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -43,6 +43,10 @@ use util::pixman::{ }; use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use vnc::pixman::{ + pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, QemuColorNames, + COLOR_TABLE_RGB, +}; use vnc::vnc::{ vnc_display_cursor, vnc_display_switch, vnc_display_update, DisplayMouse, DisplaySurface, }; @@ -517,10 +521,20 @@ fn display_define_mouse(mouse: &mut Option) { } fn display_replace_surface(surface: Option) { - if let Some(mut surface) = surface { - vnc_display_switch(&mut surface); - } else { - println!("Surface is None, waiting complete the code!"); + match surface { + Some(surface) => { + vnc_display_switch(&surface); + } + None => unsafe { + match MSG_SURFACE { + Some(surface) => { + vnc_display_switch(&surface); + } + None => { + error!("Default msg surface is none, check is needed") + } + } + }, } } @@ -563,6 +577,51 @@ fn create_surface( surface } +const FONT_HEIGHT: i32 = 16; +const FONT_WIDTH: i32 = 8; +const MSG_SURFACE_HEIGHT: i32 = 480; +const MSG_SURFACE_WIDTH: i32 = 640; + +fn create_msg_surface() -> Option { + let mut surface = DisplaySurface::default(); + unsafe { + surface.format = pixman_format_code_t::PIXMAN_x8r8g8b8; + surface.image = pixman_image_create_bits( + surface.format, + MSG_SURFACE_WIDTH, + MSG_SURFACE_HEIGHT, + ptr::null_mut(), + MSG_SURFACE_WIDTH * 4, + ); + if surface.image.is_null() { + error!("create default surface failed!"); + return None; + } + } + let msg = String::from("Display is not active now"); + let fg = COLOR_TABLE_RGB[0][QemuColorNames::QemuColorWhite as usize]; + let bg = COLOR_TABLE_RGB[0][QemuColorNames::QemuColorBlack as usize]; + let x = (MSG_SURFACE_WIDTH / FONT_WIDTH - msg.len() as i32) / 2; + let y = (MSG_SURFACE_HEIGHT / FONT_HEIGHT - 1) / 2; + + for (index, ch) in msg.chars().enumerate() { + let glyph = pixman_glyph_from_vgafont(FONT_HEIGHT, ch as u32); + pixman_glyph_render( + glyph, + surface.image, + &fg, + &bg, + (x + index as i32, y), + FONT_WIDTH, + FONT_HEIGHT, + ); + unref_pixman_image(glyph); + } + Some(surface) +} + +static mut MSG_SURFACE: Option = None; + impl GpuIoHandler { fn gpu_get_pixman_format(&mut self, format: u32) -> Result { match format { @@ -1801,6 +1860,10 @@ impl VirtioDevice for Gpu { ))); } + unsafe { + MSG_SURFACE = create_msg_surface(); + } + self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; let mut scanouts = vec![]; diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs index e0916a996..3456d797f 100644 --- a/vnc/src/pixman.rs +++ b/vnc/src/pixman.rs @@ -11,11 +11,13 @@ // See the Mulan PSL v2 for more details. use bitintr::Popcnt; +use std::ptr; use util::pixman::{ - pixman_format_a, pixman_format_b, pixman_format_bpp, pixman_format_code_t, pixman_format_depth, - pixman_format_g, pixman_format_r, pixman_image_get_data, pixman_image_get_format, - pixman_image_get_height, pixman_image_get_stride, pixman_image_get_width, pixman_image_t, - pixman_image_unref, + pixman_color_t, pixman_format_a, pixman_format_b, pixman_format_bpp, pixman_format_code_t, + pixman_format_depth, pixman_format_g, pixman_format_r, pixman_image_composite, + pixman_image_create_bits, pixman_image_create_solid_fill, pixman_image_get_data, + pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, + pixman_image_get_width, pixman_image_t, pixman_image_unref, pixman_op_t, }; #[derive(Clone, Default)] @@ -144,3 +146,462 @@ pub fn unref_pixman_image(image: *mut pixman_image_t) { } unsafe { pixman_image_unref(image as *mut pixman_image_t) }; } + +pub enum QemuColorNames { + QemuColorBlack = 0, + QemuColorBlue = 1, + QemuColorGreen = 2, + QemuColorCyan = 3, + QemuColorRed = 4, + QemuColorMagenta = 5, + QemuColorYellow = 6, + QemuColorWhite = 7, +} + +pub const COLOR_TABLE_RGB: [[pixman_color_t; 8]; 2] = [ + [ + pixman_color_t { + red: 0x00 << 8, + green: 0x00 << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* black */ + pixman_color_t { + red: 0x00 << 8, + green: 0x00 << 8, + blue: 0xaa << 8, + alpha: 0xffff, + }, /* blue */ + pixman_color_t { + red: 0x00 << 8, + green: 0xaa << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* green */ + pixman_color_t { + red: 0x00 << 8, + green: 0xaa << 8, + blue: 0xaa << 8, + alpha: 0xffff, + }, /* cyan */ + pixman_color_t { + red: 0xaa << 8, + green: 0x00 << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* red */ + pixman_color_t { + red: 0xaa << 8, + green: 0x00 << 8, + blue: 0xaa << 8, + alpha: 0xffff, + }, /* magenta */ + pixman_color_t { + red: 0xaa << 8, + green: 0xaa << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* yellow */ + pixman_color_t { + red: 0xaa << 8, + green: 0xaa << 8, + blue: 0xaa << 8, + alpha: 0xffff, + }, /* white */ + ], + [ + pixman_color_t { + red: 0x00 << 8, + green: 0x00 << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* black */ + pixman_color_t { + red: 0x00 << 8, + green: 0x00 << 8, + blue: 0xff << 8, + alpha: 0xffff, + }, /* blue */ + pixman_color_t { + red: 0x00 << 8, + green: 0xff << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* green */ + pixman_color_t { + red: 0x00 << 8, + green: 0xff << 8, + blue: 0xff << 8, + alpha: 0xffff, + }, /* cyan */ + pixman_color_t { + red: 0xff << 8, + green: 0x00 << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* red */ + pixman_color_t { + red: 0xff << 8, + green: 0x00 << 8, + blue: 0xff << 8, + alpha: 0xffff, + }, /* magenta */ + pixman_color_t { + red: 0xff << 8, + green: 0xff << 8, + blue: 0x00 << 8, + alpha: 0xffff, + }, /* yellow */ + pixman_color_t { + red: 0xff << 8, + green: 0xff << 8, + blue: 0xff << 8, + alpha: 0xffff, + }, /* white */ + ], +]; + +const VGA_FONTS: [u16; 256 * 16] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +pub fn pixman_glyph_from_vgafont(height: i32, ch: u32) -> *mut pixman_image_t { + let glyph; + + unsafe { + glyph = pixman_image_create_bits( + pixman_format_code_t::PIXMAN_a8, + 8, + height, + ptr::null_mut(), + 0, + ); + let data = pixman_image_get_data(glyph) as *mut u8; + let mut data_index: usize = 0; + let mut font_index: usize = (height * ch as i32).try_into().unwrap(); + let slice = std::slice::from_raw_parts_mut(data, (height * 8).try_into().unwrap()); + + for _y in 0..height { + for _x in 0..8 { + if VGA_FONTS[font_index] & (1 << (7 - _x)) > 0 { + slice[data_index] = 0xff; + } else { + slice[data_index] = 0x00; + }; + + data_index += 1; + } + font_index += 1; + } + } + glyph +} + +pub fn pixman_glyph_render( + glyph: *mut pixman_image_t, + surface: *mut pixman_image_t, + fgcolor: *const pixman_color_t, + bgcolor: *const pixman_color_t, + rec: (i32, i32), + cw: i32, + ch: i32, +) { + unsafe { + // for not_unsafe_ptr_arg_deref check, need declare here + let glyph = glyph as *mut pixman_image_t; + let surface = surface as *mut pixman_image_t; + let fgcolor = fgcolor as *const pixman_color_t; + let bgcolor = bgcolor as *const pixman_color_t; + let (x, y) = rec; + + let ifg = pixman_image_create_solid_fill(fgcolor); + let ibg = pixman_image_create_solid_fill(bgcolor); + + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + ibg, + ptr::null_mut(), + surface, + 0, + 0, + 0, + 0, + (cw * x) as i16, + (ch * y) as i16, + cw as u16, + ch as u16, + ); + + pixman_image_composite( + pixman_op_t::PIXMAN_OP_OVER, + ifg, + glyph, + surface, + 0, + 0, + 0, + 0, + (cw * x) as i16, + (ch * y) as i16, + cw as u16, + ch as u16, + ); + unref_pixman_image(ifg); + unref_pixman_image(ibg); + } +} diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 241a933d8..4b65823ba 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -330,7 +330,7 @@ pub fn update_client_surface(server: &mut VncServer) { } /// Check if the suface for VncClient is need update -fn check_surface(surface: &mut DisplaySurface) -> bool { +fn check_surface(surface: &DisplaySurface) -> bool { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let locked_server = server.lock().unwrap(); if surface.image.is_null() @@ -530,7 +530,7 @@ fn get_client_image() -> *mut pixman_image_t { /// Update guest_image /// Send a resize command to the client based on whether the image size has changed -pub fn vnc_display_switch(surface: &mut DisplaySurface) { +pub fn vnc_display_switch(surface: &DisplaySurface) { if VNC_SERVERS.lock().unwrap().is_empty() { return; } -- Gitee From 637ab760de40289996d465be1b25ec8074ee2333 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 28 Nov 2022 17:45:08 +0800 Subject: [PATCH 0516/2187] virtio-net: add syscall 'getrandom' for virtio net The 'vlan_map: HashMap::new()' in virtio net needs 'getrandom' syscall. Signed-off-by: Yan Wang --- machine/src/micro_vm/syscall.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index ed124dab2..75f4ab3ed 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -48,10 +48,10 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 47 syscalls -/// * x86_64-unknown-musl: 46 syscalls -/// * aarch64-unknown-gnu: 45 syscalls -/// * aarch64-unknown-musl: 45 syscalls +/// * x86_64-unknown-gnu: 48 syscalls +/// * x86_64-unknown-musl: 47 syscalls +/// * aarch64-unknown-gnu: 46 syscalls +/// * aarch64-unknown-musl: 46 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -125,6 +125,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_mkdirat), #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] BpfRule::new(libc::SYS_readlink), + BpfRule::new(libc::SYS_getrandom), madvise_rule(), ] } -- Gitee From 66059e5dcc210e89c30048856ba27e71bf74cf37 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 28 Nov 2022 17:49:08 +0800 Subject: [PATCH 0517/2187] virtio-net: disable the filter features The filter packet features rely on mac address which should be provided by device. But it doesn't support default mac address by now. So, we disable these filter packet features until the default mac address supported. Signed-off-by: Yan Wang --- virtio/src/net.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 617e8feeb..352cfd052 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -26,12 +26,10 @@ use super::{ VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, - VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, - VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, @@ -1329,10 +1327,6 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_TSO4 | 1 << VIRTIO_NET_F_HOST_TSO6 | 1 << VIRTIO_NET_F_HOST_UFO - | 1 << VIRTIO_NET_F_CTRL_RX - | 1 << VIRTIO_NET_F_CTRL_VLAN - | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA - | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR | 1 << VIRTIO_F_RING_EVENT_IDX; let queue_pairs = self.net_cfg.queues / 2; -- Gitee From bef1c2b87750da3a33c5e4cedde3f32285f94e60 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Tue, 29 Nov 2022 10:32:04 +0800 Subject: [PATCH 0518/2187] help: modify StratoVirt help information 1. Modify the format of help content. `` information can be changed by users. `[xxx]` information optional to be set. 2. Add more detailed help content. For example, `-device `, add help information of various devices to use the parameters. Signed-off-by: Xinle.Guo --- docs/config_guidebook.md | 2 +- machine_manager/src/cmdline.rs | 167 ++++++++++++++++++++------------- util/src/arg_parser.rs | 4 +- 3 files changed, 103 insertions(+), 70 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 407819fde..930622de4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -60,7 +60,7 @@ Currently, these options are supported. ```shell # cmdline --cpu [host,][pmu=on|off] +-cpu host[,pmu=on|off] ``` ### 1.3 Memory diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index b91c551ac..13f23aab1 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -74,51 +74,86 @@ macro_rules! add_args_to_config_multi { pub fn create_args_parser<'a>() -> ArgParser<'a> { ArgParser::new("StratoVirt") .version(VERSION.unwrap_or("unknown")) - .author("Huawei Technologies Co., Ltd") + .author("The StratoVirt Project Developers") .about("A light kvm-based hypervisor.") .arg( Arg::with_name("name") .long("name") - .value_name("vm_name") + .value_name("[vm_name]") .help("set the name of the guest.") .takes_value(true), ) .arg( Arg::with_name("machine") .long("machine") - .value_name("[type=]name[,dump_guest_core=on|off][,mem-share=on|off]") - .help("selects emulated machine and set properties") + .value_name("[type=][,dump_guest_core=on|off][,mem-share=on|off]") + .help("'type' selects emulated machine type and set properties. \ + 'dump_guest_core' includes guest memory in a core dump. \ + 'mem-share' sets guest memory is shareable.") .takes_value(true), ) .arg( Arg::with_name("smp") .long("smp") - .value_name("[cpus=]n[,maxcpus=cpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]") - .help("set the number of CPUs to 'n' (default: 1). maxcpus=maximum number of total CPUs, including online and offline CPUs. \ - sockets is the number of sockets on the machine. \ - dies is the number of dies in one socket. \ - clusters is the number of clusters in one die. cores is the number of cores in one cluster. \ - threads is the number of threads in one core") + .value_name("[cpus=][,maxcpus=][,sockets=][,dies=][,clusters=][,cores=][,threads=]") + .help("'cpus' sets the number of CPUs to 'n' (default: 1). 'maxcpus' sets number of total CPUs, including online and offline CPUs. \ + 'sockets' is the number of sockets on the machine. \ + 'dies' is the number of dies in one socket. \ + 'clusters' is the number of clusters in one die. \ + 'cores' is the number of cores in one cluster. \ + 'threads' is the number of threads in one core") .takes_value(true), ) + .arg( + Arg::with_name("cpu") + .long("cpu") + .value_name("host[,pmu=on|off]") + .help("set CPU model and features.") + .can_no_value(false) + .takes_value(true) + ) + .arg( + Arg::with_name("freeze_cpu") + .short("S") + .long("freeze") + .help("freeze CPU at startup") + .takes_value(false) + .required(false), + ) .arg( Arg::with_name("memory") .long("m") - .value_name("[size=]megs[m|M|g|G]") - .help("configure guest RAM") + .value_name("[size=][m|M|g|G]") + .help("configure guest RAM(default unit: MiB).") .takes_value(true), ) .arg( Arg::with_name("mem-path") .long("mem-path") - .value_name("filebackend file path") + .value_name("") .help("configure file path that backs guest memory.") .takes_value(true), ) + .arg( + Arg::with_name("mem-prealloc") + .long("mem-prealloc") + .help("Prealloc memory for VM") + .takes_value(false) + .required(false), + ) + .arg( + Arg::with_name("numa") + .multiple(true) + .long("numa") + .value_name("") + .help("\n\t\tset numa node: -numa node,nodeid=<0>,cpus=<0-1>,memdev=; \ + \n\t\tset numa distance: -numa dist,src=<0>,dst=<1>,val=<20> ") + .takes_values(true), + ) .arg( Arg::with_name("kernel") .long("kernel") - .value_name("kernel_path") + .value_name("") .help("use uncompressed kernel image") .takes_value(true), ) @@ -126,30 +161,32 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("kernel-cmdline") .multiple(true) .long("append") - .value_name("kernel cmdline parameters") + .value_name("") .help("use 'cmdline' as kernel command line") .takes_values(true), ) .arg( Arg::with_name("initrd-file") .long("initrd") - .value_name("initrd_path") + .value_name("") .help("use 'initrd-file' as initial ram disk") .takes_value(true), ) .arg( Arg::with_name("qmp") .long("qmp") - .value_name("unix:socket_path") - .help("set qmp's unixsocket path") + .value_name("unix:") + .help("set QMP's unix socket path") .takes_value(true) ) .arg( Arg::with_name("drive") .multiple(true) .long("drive") - .value_name("file=path,id=str[,readonly=][,direct=][,serial=][,iothread=][iops=][,aio=native|io_uring|off]") - .help("use 'file' as a drive image") + .value_name("") + .help("\n\t\tset block drive image: -drive id=,file=[,readonly=on|off][,direct=on|off][,throttling.iops-total=<200>]; \ + \n\t\tset pflash drive image: -drive file=,if=pflash,unit=0|1[,readonly=true|false]; \ + \n\t\tset scsi drive image: -drive id=,file=[,readonly=true|false]") .takes_values(true), ) .arg( @@ -157,7 +194,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .multiple(true) .long("netdev") .value_name( - "id=str,netdev=str[,mac=][,fds=][,vhost=on|off][,vhostfd=][,iothread=]", + "tap,id=,ifname=[,queue=]", ) .help("configure a host TAP network with ID 'str'") .takes_values(true), @@ -166,7 +203,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("chardev") .multiple(true) .long("chardev") - .value_name("id=str,path=socket_path") + .value_name("socket,id=,path=") .help("set char device virtio console for vm") .takes_values(true), ) @@ -174,21 +211,43 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("device") .multiple(true) .long("device") - .value_name("vsock,id=str,guest-cid=u32[,vhostfd=]") - .help("add virtio vsock device and sets properties") + .value_name("") + .help("\n\t\tadd virtio mmio block: -device virtio-blk-device,id=,drive=[,iothread=][,serial=]; \ + \n\t\tadd virtio pci block: -device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction=on|off][,iothread=][,serial=][,num-queues=][,bootindex=]; \ + \n\t\tadd vhost user pci block: -device vhost-user-blk-pci,id=,chardev=,bus=,addr=<0x3>[,num-queues=][,bootindex=]; \ + \n\t\tadd virtio mmio net: -device virtio-net-device,id=,netdev=[,iothread=][,mac=<12:34:56:78:9A:BC>]; \ + \n\t\tadd virtio pci net: -device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction=on|off][,iothread=][,mac=<12:34:56:78:9A:BC>][,mq=on|off]; \ + \n\t\tadd vhost mmio net: -device virtio-net-device,id=,netdev=[,iothread=][,mac=<12:34:56:78:9A:BC>]; \ + \n\t\tadd vhost pci net: -device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction=on|off][,iothread=][,mac=<12:34:56:78:9A:BC>][,mq=on|off]; \ + \n\t\tadd virtio mmio console: -device virtio-serial-device[,id=] -device virtconsole,id=console_id,chardev=; \ + \n\t\tadd virtio pci console: -device virtio-serial-pci,id=,bus=,addr=<0x3>[,multifunction=on|off] -device virtconsole,id=,chardev=; \ + \n\t\tadd vhost mmio vsock: -device vhost-vsock-device,id=,guest-cid=; \ + \n\t\tadd vhost pci vsock: -device vhost-vsock-pci,id=,guest-cid=,bus=,addr=<0x3>[,multifunction=on|off]; \ + \n\t\tadd virtio mmio balloon: -device virtio-balloon-device[,deflate-on-oom=true|false][,free-page-reporting=true|false]; \ + \n\t\tadd virtio pci balloon: -device virtio-balloon-pci,id=,bus=,addr=<0x4>[,deflate-on-oom=true|false][,free-page-reporting=true|false][,multifunction=on|off]; \ + \n\t\tadd virtio mmio rng: -device virtio-rng-device,rng=,max-bytes=<1234>,period=<1000>; \ + \n\t\tadd virtio pci rng: -device virtio-rng-pci,id=,rng=,max-bytes=<1234>,period=<1000>,bus=,addr=<0x1>[,multifunction=on|off]; \ + \n\t\tadd pcie root port: -device pcie-root-port,id=,port=<0x1>,bus=,addr=<0x1>[,multifunction=on|off]; \ + \n\t\tadd vfio pci: -device vfio-pci,id=,host=<0000:1a:00.3>,bus=,addr=<0x03>[,multifunction=on|off]; \ + \n\t\tadd usb controller: -device nec-usb-xhci,id=,bus=,addr=<0xa>; \ + \n\t\tadd usb keyboard: -device usb-kbd,id=; \ + \n\t\tadd usb tablet-device usb-tablet,id=; \ + \n\t\tadd scsi controller: -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction=on|off][,iothread=][,num-queues=]; \ + \n\t\tadd scsi hard disk: -device scsi-hd,scsi-id=<0>,bus=,lun=<0>,drive=,id=; \ + \n\t\tadd vhost user fs: -device vhost-user-fs-pci,id=,chardev=,tag=") .takes_values(true), ) .arg( Arg::with_name("serial") .long("serial") - .value_name("backend[,path=,server,nowait] or chardev:char_id") + .value_name("backend[,path=,server,nowait] or chardev:") .help("add serial and set chardev for it") .takes_value(true), ) .arg( Arg::with_name("display log") .long("D") - .value_name("log path") + .value_name("[log path]") .help("output log to logfile (default stderr)") .takes_value(true) .can_no_value(true), @@ -196,13 +255,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("pidfile") .long("pidfile") - .value_name("pidfile path") + .value_name("") .help("write PID to 'file'") .takes_value(true), ) .arg( Arg::with_name("daemonize") .long("daemonize") + .value_name("") .help("daemonize StratoVirt after initializing") .takes_value(false) .required(false), @@ -210,49 +270,39 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("disable-seccomp") .long("disable-seccomp") + .value_name("") .help("not use seccomp sandbox for StratoVirt") .takes_value(false) .required(false), ) - .arg( - Arg::with_name("freeze_cpu") - .short("S") - .long("freeze") - .help("Freeze CPU at startup") - .takes_value(false) - .required(false), - ) .arg( Arg::with_name("incoming") .long("incoming") - .value_name("tcp:ip:port> or -incoming or -incoming ") + .help("\n\t\tdo the migration using tcp socket: -incoming tcp::; \ + \n\t\tdo the migration using unix socket: -incoming unix:; \ + \n\t\tdo the virtual machine snapshot: -incoming file:") .takes_value(true), ) .arg( Arg::with_name("object") .multiple(true) .long("object") - .value_name("-object virtio-rng-device,rng=rng_name,max-bytes=1234,period=1000") - .help("add object") + .value_name("") + .help("\n\t\tadd memory backend ram object: -object memory-backend-ram,id=,size=<2G>,host-nodes=<0-1>,policy=; \ + \n\t\tadd iothread object: -object iothread,id=; \ + \n\t\tadd rng object: -object rng-random,id=,filename=; \ + \n\t\tadd vnc tls object: -object tls-creds-x509,id=,dir=; \ + \n\t\tadd authz object: -object authz-simple,id=,identity=") .takes_values(true), ) .arg( Arg::with_name("mon") .long("mon") - .value_name("chardev=chardev_id,id=mon_id[,mode=control]") + .value_name("chardev=,id=[,mode=control]") .help("-mon is another way to create qmp channel. To use it, the chardev should be specified") .takes_value(true), ) - .arg( - Arg::with_name("cpu") - .long("cpu") - .value_name("[model][,pmu=on|off]") - .help("Set CPU model and features.") - .can_no_value(false) - .takes_value(true) - ) .arg( Arg::with_name("overcommit") .long("overcommit") @@ -352,13 +402,6 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .can_no_value(true) .takes_value(true), ) - .arg( - Arg::with_name("mem-prealloc") - .long("mem-prealloc") - .help("Prealloc memory for VM") - .takes_value(false) - .required(false), - ) .arg( Arg::with_name("trace") .multiple(false) @@ -371,21 +414,11 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("global") .multiple(true) .long("global") - .value_name("[key=value]") + .value_name("[key=]") .help("set global config") .takes_values(true) .required(false), ) - .arg( - Arg::with_name("numa") - .multiple(true) - .long("numa") - .value_name("node,nodeid=0,cpus=0-1,memdev=mem0> \ - -numa \ - -object Arg<'a> { } else { let font_str = if self.values.is_some() { format!( - "{}{}{} <{}>...", + "{}{}{} {}...", EIGHT_BLANK, PREFIX_CHARS_LONG, self.long.unwrap(), @@ -493,7 +493,7 @@ impl<'a> Arg<'a> { ) } else { format!( - "{}{}{} <{}>", + "{}{}{} {}", EIGHT_BLANK, PREFIX_CHARS_LONG, self.long.unwrap(), -- Gitee From fc00ecfafb6c15df533ed550c890995f208acd2c Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 29 Sep 2022 19:39:37 +0800 Subject: [PATCH 0519/2187] VNC: Add Hextiles algorithm to compresses images in order to improve the performance of VNC Hextile is image compression algorithm. Rectangles are split up into 16x16 tiles, allowing the dimensions of the subrectangles to be specified in 4 bits each, 16 bits in total. The rectangle is split into tiles starting at the top left going in left-to-right, top-to-bottom order. The encoded contents of the tiles simply follow one another in the predetermined order. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 130 ++- vnc/src/encoding/enc_hextile.rs | 472 ++++++++ vnc/src/encoding/mod.rs | 15 + vnc/src/encoding/test_hextile_image_data.rs | 1160 +++++++++++++++++++ vnc/src/lib.rs | 1 + vnc/src/vnc.rs | 197 ++-- 6 files changed, 1817 insertions(+), 158 deletions(-) create mode 100644 vnc/src/encoding/enc_hextile.rs create mode 100644 vnc/src/encoding/mod.rs create mode 100644 vnc/src/encoding/test_hextile_image_data.rs diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 2be074458..3ea8028a9 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -49,7 +49,7 @@ const NUM_OF_COLORMAP: u16 = 256; // VNC encodings types. pub const ENCODING_RAW: i32 = 0; -const ENCODING_HEXTILE: i32 = 5; +pub const ENCODING_HEXTILE: i32 = 5; const ENCODING_ZLIB: i32 = 6; const ENCODING_TIGHT: i32 = 7; const ENCODING_ZRLE: i32 = 16; @@ -152,22 +152,48 @@ impl Rectangle { } } +/// Display Output mode information of client. +#[derive(Clone)] +pub struct DisplayMode { + /// Encoding type. + pub enc: i32, + /// Data storage type for client. + pub client_be: bool, + /// The pixel need to convert. + pub convert: bool, + /// Image pixel format in pixman. + pub pf: PixelFormat, +} + +impl DisplayMode { + pub fn new(enc: i32, client_be: bool, convert: bool, pf: PixelFormat) -> Self { + DisplayMode { + enc, + client_be, + convert, + pf, + } + } +} + +impl Default for DisplayMode { + fn default() -> Self { + Self::new(0, false, false, PixelFormat::default()) + } +} + unsafe impl Send for RectInfo {} pub struct RectInfo { /// TcpStream address. pub addr: String, /// Dirty area of image. pub rects: Vec, + /// Width of Client image. pub width: i32, + /// Height of Client image. pub height: i32, - /// Encoding type. - pub encoding: i32, - /// The pixel need to convert. - pub convert: bool, - /// Data storage type for client. - pub big_endian: bool, - /// Image pixel format in pixman. - pub pixel_format: PixelFormat, + /// Output mod information of client display. + pub dpm: DisplayMode, /// Image pub image: *mut pixman_image_t, } @@ -179,10 +205,7 @@ impl RectInfo { rects, width: client.width, height: client.height, - encoding: client.encoding, - convert: client.pixel_convert, - big_endian: client.big_endian, - pixel_format: client.pixel_format.clone(), + dpm: client.client_dpm.clone(), image: client.server_image, } } @@ -199,10 +222,7 @@ impl Clone for RectInfo { rects, width: self.width, height: self.height, - encoding: self.encoding, - convert: self.convert, - big_endian: self.big_endian, - pixel_format: self.pixel_format.clone(), + dpm: self.dpm.clone(), image: self.image, } } @@ -238,16 +258,12 @@ pub struct VncClient { pub tls_conn: Option, /// Configuration for sasl authentication. pub sasl: Sasl, - /// Data storage type for client. - pub big_endian: bool, /// State flags whether the image needs to be updated for the client. state: UpdateState, /// Identify the image update area. pub dirty_bitmap: Bitmap, /// Number of dirty data. dirty_num: i32, - /// Image pixel format in pixman. - pub pixel_format: PixelFormat, /// Image pointer. pub server_image: *mut pixman_image_t, /// Tcp listening address. @@ -256,12 +272,10 @@ pub struct VncClient { pub width: i32, /// Image height. pub height: i32, - /// Encoding type. - encoding: i32, + /// Display output mod information of client. + pub client_dpm: DisplayMode, /// Image display feature. feature: i32, - /// The pixel need to convert. - pixel_convert: bool, } impl VncClient { @@ -286,21 +300,18 @@ impl VncClient { server, tls_conn: None, sasl: Sasl::default(), - big_endian: false, state: UpdateState::No, dirty_bitmap: Bitmap::::new( MAX_WINDOW_HEIGHT as usize * round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) as usize, ), dirty_num: 0, - pixel_format: PixelFormat::default(), server_image: image, addr, width: 0, height: 0, - encoding: 0, + client_dpm: DisplayMode::default(), feature: 0, - pixel_convert: false, } } @@ -683,24 +694,28 @@ impl VncClient { buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); self.pixel_format_message(&mut buf); self.write_msg(&buf); - } else if !self.pixel_format.is_default_pixel_format() { - self.pixel_convert = true; + } else if !self.client_dpm.pf.is_default_pixel_format() { + self.client_dpm.convert = true; } } /// Set pixformat for client. pub fn pixel_format_message(&mut self, buf: &mut Vec) { - self.pixel_format.init_pixelformat(); - buf.append(&mut (self.pixel_format.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. - buf.append(&mut (self.pixel_format.depth as u8).to_be_bytes().to_vec()); // Depth. + self.client_dpm.pf.init_pixelformat(); + buf.append(&mut (self.client_dpm.pf.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. + buf.append(&mut (self.client_dpm.pf.depth as u8).to_be_bytes().to_vec()); // Depth. buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Big-endian flag. buf.append(&mut (1_u8).to_be_bytes().to_vec()); // True-color flag. - buf.append(&mut (self.pixel_format.red.max as u16).to_be_bytes().to_vec()); // Red max. - buf.append(&mut (self.pixel_format.green.max as u16).to_be_bytes().to_vec()); // Green max. - buf.append(&mut (self.pixel_format.blue.max as u16).to_be_bytes().to_vec()); // Blue max. - buf.append(&mut (self.pixel_format.red.shift as u8).to_be_bytes().to_vec()); // Red shift. - buf.append(&mut (self.pixel_format.green.shift as u8).to_be_bytes().to_vec()); // Green shift. - buf.append(&mut (self.pixel_format.blue.shift as u8).to_be_bytes().to_vec()); // Blue shift. + buf.append(&mut (self.client_dpm.pf.red.max as u16).to_be_bytes().to_vec()); // Red max. + buf.append(&mut (self.client_dpm.pf.green.max as u16).to_be_bytes().to_vec()); // Green max. + buf.append(&mut (self.client_dpm.pf.blue.max as u16).to_be_bytes().to_vec()); // Blue max. + buf.append(&mut (self.client_dpm.pf.red.shift as u8).to_be_bytes().to_vec()); // Red shift. + buf.append( + &mut (self.client_dpm.pf.green.shift as u8) + .to_be_bytes() + .to_vec(), + ); // Green shift. + buf.append(&mut (self.client_dpm.pf.blue.shift as u8).to_be_bytes().to_vec()); // Blue shift. buf.append(&mut [0; 3].to_vec()); // Padding. } @@ -748,7 +763,7 @@ impl VncClient { // Number of colors. buf.append(&mut (NUM_OF_COLORMAP as u16).to_be_bytes().to_vec()); - let pf = self.pixel_format.clone(); + let pf = self.client_dpm.pf.clone(); for i in 0..NUM_OF_COLORMAP as u16 { let r = ((i >> pf.red.shift) & pf.red.max as u16) << (16 - pf.red.bits); let g = ((i >> pf.green.shift) & pf.green.max as u16) << (16 - pf.green.bits); @@ -798,27 +813,29 @@ impl VncClient { )))); } - self.pixel_format.red.set_color_info(red_shift, red_max); - self.pixel_format + self.client_dpm.pf.red.set_color_info(red_shift, red_max); + self.client_dpm + .pf .green .set_color_info(green_shift, green_max); - self.pixel_format.blue.set_color_info(blue_shift, blue_max); - self.pixel_format.pixel_bits = bit_per_pixel; - self.pixel_format.pixel_bytes = bit_per_pixel / BIT_PER_BYTE as u8; + self.client_dpm.pf.blue.set_color_info(blue_shift, blue_max); + self.client_dpm.pf.pixel_bits = bit_per_pixel; + self.client_dpm.pf.pixel_bytes = bit_per_pixel / BIT_PER_BYTE as u8; // Standard pixel format, depth is equal to 24. - self.pixel_format.depth = if bit_per_pixel == 32 { + self.client_dpm.pf.depth = if bit_per_pixel == 32 { 24 } else { bit_per_pixel }; - self.big_endian = big_endian_flag != 0; + self.client_dpm.client_be = big_endian_flag != 0; if true_color_flag == 0 { self.send_color_map(); } - if !self.pixel_format.is_default_pixel_format() { - self.pixel_convert = true; + if !self.client_dpm.pf.is_default_pixel_format() { + self.client_dpm.convert = true; } + VNC_RECT_INFO.lock().unwrap().clear(); self.update_event_handler(1, VncClient::handle_protocol_msg); Ok(()) } @@ -883,30 +900,30 @@ impl VncClient { ]); match enc { ENCODING_RAW => { - self.encoding = enc; + self.client_dpm.enc = enc; } ENCODING_HEXTILE => { self.feature |= 1 << VncFeatures::VncFeatureHextile as usize; - self.encoding = enc; + self.client_dpm.enc = enc; } ENCODING_TIGHT => { self.feature |= 1 << VncFeatures::VncFeatureTight as usize; - self.encoding = enc; + self.client_dpm.enc = enc; } ENCODING_ZLIB => { // ZRLE compress better than ZLIB, so prioritize ZRLE. if self.feature & (1 << VncFeatures::VncFeatureZrle as usize) == 0 { self.feature |= 1 << VncFeatures::VncFeatureZlib as usize; - self.encoding = enc; + self.client_dpm.enc = enc; } } ENCODING_ZRLE => { self.feature |= 1 << VncFeatures::VncFeatureZrle as usize; - self.encoding = enc; + self.client_dpm.enc = enc; } ENCODING_ZYWRLE => { self.feature |= 1 << VncFeatures::VncFeatureZywrle as usize; - self.encoding = enc; + self.client_dpm.enc = enc; } ENCODING_DESKTOPRESIZE => { self.feature |= 1 << VncFeatures::VncFeatureResize as usize; @@ -935,7 +952,6 @@ impl VncClient { num_encoding -= 1; } - self.encoding = 0; self.desktop_resize(); // VNC display cursor define. let mut cursor: DisplayMouse = DisplayMouse::default(); diff --git a/vnc/src/encoding/enc_hextile.rs b/vnc/src/encoding/enc_hextile.rs new file mode 100644 index 000000000..cc50a2a3b --- /dev/null +++ b/vnc/src/encoding/enc_hextile.rs @@ -0,0 +1,472 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::client::{DisplayMode, Rectangle}; +use crate::pixman::{bytes_per_pixel, get_image_data, get_image_stride}; +use crate::vnc::write_pixel; +use std::{cmp, mem}; +use util::pixman::pixman_image_t; + +/// Size of subrectangle. +const HEXTILE_BLOCK_SIZE: usize = 16; +/// SubEncoding type of hextile. +const RAW: u8 = 0x01; +const BACKGROUND_SPECIFIC: u8 = 0x02; +const FOREGROUND_SPECIFIC: u8 = 0x04; +const ANY_SUBRECTS: u8 = 0x08; +const SUBRECTS_COLOURED: u8 = 0x10; + +/// Compress data by hextile algorithm before sending. +/// Rectangles are split up into 16 * 16 tiles. +/// +/// # Arguments +/// +/// * `image` - pointer to the data need to be send. +/// * `rect` - dirty area of image. +/// * `client_dpm` - Output mode information of client display. +/// * `buf` - send buffer. +pub fn hextile_send_framebuffer_update( + image: *mut pixman_image_t, + rect: &Rectangle, + client_dpm: &DisplayMode, + buf: &mut Vec, +) -> i32 { + let mut last_bg: Option = None; + let mut last_fg: Option = None; + for j in (0..rect.h).step_by(HEXTILE_BLOCK_SIZE) { + for i in (0..rect.w).step_by(HEXTILE_BLOCK_SIZE) { + let sub_rect = Rectangle::new( + rect.x + i, + rect.y + j, + cmp::min(HEXTILE_BLOCK_SIZE as i32, rect.w - i), + cmp::min(HEXTILE_BLOCK_SIZE as i32, rect.h - j), + ); + compress_each_tile( + image, + &sub_rect, + client_dpm, + buf, + &mut last_bg, + &mut last_fg, + ); + } + } + 1 +} + +/// Compress each tiles by hextile algorithm. +/// +/// # Arguments +/// +/// * `image` - pointer to the data need to be send. +/// * `sub_rect` - area of tile. +/// * `client_dpm` - Output mode information of client display. +/// * `buf` - send buffer. +/// * `last_bg` - background of last tile. +/// * `last_fg` - foreground of last tile. +fn compress_each_tile<'a>( + image: *mut pixman_image_t, + sub_rect: &Rectangle, + client_dpm: &DisplayMode, + buf: &mut Vec, + last_bg: &'a mut Option, + last_fg: &'a mut Option, +) { + let stride = get_image_stride(image); + let mut data_ptr = get_image_data(image) as *mut u8; + data_ptr = (data_ptr as usize + + (sub_rect.y * stride) as usize + + sub_rect.x as usize * bytes_per_pixel()) as *mut u8; + let mut flag: u8 = 0; // Subencoding mask. + let mut bg: u32 = 0; // Pixel value of background. + let mut fg: u32 = 0; // Pixel value of foreground. + let n_colors = pixel_statistical(data_ptr, stride, sub_rect, &mut bg, &mut fg); + let mut n_subtiles = 0; // Number of subrectangle. + let mut tmp_buf: Vec = Vec::new(); + + if *last_bg == None || Some(bg) != *last_bg { + flag |= BACKGROUND_SPECIFIC; + *last_bg = Some(bg); + } + if n_colors < 3 && (*last_fg == None || Some(fg) != *last_fg) { + flag |= FOREGROUND_SPECIFIC; + *last_fg = Some(fg); + } + + match n_colors { + 2 => { + flag |= ANY_SUBRECTS; + n_subtiles = + subrectangle_of_foreground(sub_rect, data_ptr, bg, fg, stride, &mut tmp_buf); + } + 3 => { + flag |= ANY_SUBRECTS | SUBRECTS_COLOURED; + if *last_bg == None || Some(bg) != *last_bg { + flag |= BACKGROUND_SPECIFIC; + } + n_subtiles = subrectangle_with_pixel_value( + sub_rect, + data_ptr, + bg, + stride, + client_dpm, + &mut tmp_buf, + ); + //If the length becomes longer after compression, give up compression. + if tmp_buf.len() > (sub_rect.h * sub_rect.w * client_dpm.pf.pixel_bytes as i32) as usize + { + flag = RAW; + *last_bg = None; + } + *last_fg = None; + } + _ => {} + } + + buf.append(&mut (flag as u8).to_be_bytes().to_vec()); // SubEncoding-mask. + if flag & RAW == 0 { + if flag & BACKGROUND_SPECIFIC != 0 { + write_pixel( + bg.to_ne_bytes().as_ptr() as *mut u8, + bytes_per_pixel(), + client_dpm, + buf, + ); + } + if flag & FOREGROUND_SPECIFIC != 0 { + write_pixel( + fg.to_ne_bytes().as_ptr() as *mut u8, + bytes_per_pixel(), + client_dpm, + buf, + ); + } + if n_subtiles != 0 { + buf.append(&mut (n_subtiles as u8).to_be_bytes().to_vec()); // Num of SubRectanges. + buf.append(&mut tmp_buf); // SubrectsColoured. + } + } else { + // Send data directly without compression. + for j in 0..sub_rect.h { + let ptr = (data_ptr as usize + (j * stride) as usize) as *mut u8; + write_pixel(ptr, (sub_rect.w * 4) as usize, client_dpm, buf); + } + } +} + +/// Specifies all subrectangles of foreground colour in this tile. +/// +/// # Arguments +/// +/// * `sub_rect` - area of tile. +/// * `data_ptr` - pointer to the data of image. +/// * `bg` - background of current tile. +/// * `fg` - foreground of current tile. +/// * `stride` - stride of image. +/// * `buf` - send buffer. +fn subrectangle_of_foreground( + sub_rect: &Rectangle, + data_ptr: *mut u8, + bg: u32, + fg: u32, + stride: i32, + buf: &mut Vec, +) -> i32 { + let mut n_subtiles = 0; + for j in 0..sub_rect.h { + let ptr = (data_ptr as usize + (j * stride) as usize) as *mut u32; + let mut x_begin = -1; + for i in 0..sub_rect.w { + let value = unsafe { *ptr.add(i as usize) }; + if value == fg && x_begin == -1 { + x_begin = i; + } else if value == bg && x_begin != -1 { + hextile_enc_sub_coloured(buf, x_begin, j, i - x_begin, 1); + n_subtiles += 1; + x_begin = -1; + } + } + if x_begin != -1 { + hextile_enc_sub_coloured(buf, x_begin, j, sub_rect.w - x_begin, 1); + n_subtiles += 1; + } + } + n_subtiles +} + +/// Specifies all subrectangles with pixel value. +/// +/// # Arguments +/// +/// * `sub_rect` - area of tile. +/// * `data_ptr` - pointer to the data of image. +/// * `bg` - background of current tile. +/// * `stride` - stride of image. +/// * `client_dpm` - Output mode information of client display. +/// * `buf` - send buffer. +fn subrectangle_with_pixel_value( + sub_rect: &Rectangle, + data_ptr: *mut u8, + bg: u32, + stride: i32, + client_dpm: &DisplayMode, + buf: &mut Vec, +) -> i32 { + let mut n_subtiles = 0; + for j in 0..sub_rect.h { + let mut x_begin = -1; + let mut last_color: Option = None; + let ptr = (data_ptr as usize + (j * stride) as usize) as *mut u32; + for i in 0..sub_rect.w { + let value = unsafe { *ptr.offset(i as isize) }; + match last_color { + Some(color) => { + if color != value { + last_color = None; + write_pixel( + color.to_ne_bytes().as_ptr() as *mut u8, + bytes_per_pixel(), + client_dpm, + buf, + ); + hextile_enc_sub_coloured(buf, x_begin, j, i - x_begin, 1); + n_subtiles += 1; + x_begin = -1; + if value != bg { + last_color = Some(value); + x_begin = i; + } + } + } + None => { + if value == bg { + continue; + } + last_color = Some(value); + x_begin = i; + } + } + } + if let Some(color) = last_color { + write_pixel( + color.to_ne_bytes().as_ptr() as *mut u8, + bytes_per_pixel(), + client_dpm, + buf, + ); + n_subtiles += 1; + hextile_enc_sub_coloured(buf, x_begin, j, sub_rect.w - x_begin, 1) + } + } + + n_subtiles +} + +/// Encode SubrectsColoured. +/// First Byte: x-and-y-position +/// Second Byte: width-and-height-position. +fn hextile_enc_sub_coloured(buf: &mut Vec, x: i32, y: i32, w: i32, h: i32) { + buf.append( + &mut (((x & 0x0f) << 4 | (y & 0x0f)) as u8) + .to_be_bytes() + .to_vec(), + ); + buf.append( + &mut ((((w - 1) & 0x0f) << 4 | ((h - 1) & 0x0f)) as u8) + .to_be_bytes() + .to_vec(), + ); +} + +/// Count the total number of different pixels in rectangle. +/// +/// # Arguments +/// +/// * `data_ptr` - pointer to the data. +/// * `stride` - number of bytes for one line of image data. +/// * `sub_rect` - subrectangle. +/// * `bg` - background. +/// * `fg` - foreground. +fn pixel_statistical<'a>( + data_ptr: *mut u8, + stride: i32, + sub_rect: &Rectangle, + bg: &'a mut u32, + fg: &'a mut u32, +) -> usize { + let mut n_colors = 0; + let mut bg_count = 0; // Number of background. + let mut fg_count = 0; // Number of foreground. + + for j in 0..sub_rect.h { + let ptr = (data_ptr as usize + (j * stride) as usize) as *mut u32; + for i in 0..sub_rect.w { + let value = unsafe { *ptr.offset(i as isize) }; + match n_colors { + 0 => { + *bg = value; + n_colors = 1; + } + 1 => { + if *bg != value { + *fg = value; + n_colors = 2; + } + } + 2 => { + if value == *bg { + bg_count += 1; + } else if value == *fg { + fg_count += 1; + } else { + n_colors = 3; + } + } + _ => { + break; + } + } + } + if n_colors > 2 { + break; + } + } + + if n_colors > 1 && fg_count > bg_count { + mem::swap(bg, fg); + } + + n_colors +} + +#[cfg(test)] +mod tests { + use super::hextile_send_framebuffer_update; + use crate::{ + client::{DisplayMode, Rectangle, ENCODING_HEXTILE}, + encoding::test_hextile_image_data::{ + IMAGE_DATA_MULTI_PIXELS, IMAGE_DATA_SINGLE_PIXEL, IMAGE_DATA_TWO_PIXEL, + TARGET_DATA_MULTI_PIXELS, TARGET_DATA_SINGLE_PIXEL, TARGET_DATA_TWO_PIXEL, + }, + pixman::PixelFormat, + }; + use util::pixman::{pixman_format_code_t, pixman_image_create_bits}; + fn color_init() -> PixelFormat { + let mut pf = PixelFormat::default(); + pf.red.set_color_info(16, 255); + pf.green.set_color_info(8, 255); + pf.blue.set_color_info(0, 255); + pf.pixel_bits = 32; + pf.pixel_bytes = 4; + pf.depth = 24; + pf + } + + #[test] + fn test_hextile_send_framebuffer_single_pixel() { + let pf = color_init(); + let convert = false; + let client_be = false; + let enc = ENCODING_HEXTILE; + let client_dpm = DisplayMode::new(enc, client_be, convert, pf); + let image_data = IMAGE_DATA_SINGLE_PIXEL; + let target_data = TARGET_DATA_SINGLE_PIXEL; + let image_width: i32 = 32; + let image_height: i32 = 32; + let image_stride: i32 = 128; + + let image = unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + image_width as i32, + image_height as i32, + image_data.as_ptr() as *mut u32, + image_stride, + ) + }; + let mut buf: Vec = Vec::new(); + let rect = Rectangle { + x: 0, + y: 0, + w: image_width, + h: image_height, + }; + hextile_send_framebuffer_update(image, &rect, &client_dpm, &mut buf); + assert_eq!(buf, target_data); + } + + #[test] + fn test_hextile_send_framebuffer_two_pixels() { + let pf = color_init(); + let convert = false; + let client_be = false; + let enc = ENCODING_HEXTILE; + let client_dpm = DisplayMode::new(enc, client_be, convert, pf); + let image_data = IMAGE_DATA_TWO_PIXEL; + let target_data = TARGET_DATA_TWO_PIXEL; + let image_width: i32 = 40; + let image_height: i32 = 40; + let image_stride: i32 = 160; + + let image = unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + image_width as i32, + image_height as i32, + image_data.as_ptr() as *mut u32, + image_stride, + ) + }; + let mut buf: Vec = Vec::new(); + let rect = Rectangle { + x: 0, + y: 0, + w: image_width, + h: image_height, + }; + hextile_send_framebuffer_update(image, &rect, &client_dpm, &mut buf); + assert_eq!(buf, target_data); + } + + #[test] + fn test_hextile_send_framebuffer_multi_pixels() { + let pf = color_init(); + let convert = false; + let client_be = false; + let enc = ENCODING_HEXTILE; + let client_dpm = DisplayMode::new(enc, client_be, convert, pf); + let image_data = IMAGE_DATA_MULTI_PIXELS; + let target_data = TARGET_DATA_MULTI_PIXELS; + let image_width: i32 = 40; + let image_height: i32 = 40; + let image_stride: i32 = 160; + + let image = unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + image_width as i32, + image_height as i32, + image_data.as_ptr() as *mut u32, + image_stride, + ) + }; + let mut buf: Vec = Vec::new(); + let rect = Rectangle { + x: 0, + y: 0, + w: image_width, + h: image_height, + }; + hextile_send_framebuffer_update(image, &rect, &client_dpm, &mut buf); + assert_eq!(buf, target_data); + } +} diff --git a/vnc/src/encoding/mod.rs b/vnc/src/encoding/mod.rs new file mode 100644 index 000000000..6cabbc97c --- /dev/null +++ b/vnc/src/encoding/mod.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod enc_hextile; +#[cfg(test)] +mod test_hextile_image_data; diff --git a/vnc/src/encoding/test_hextile_image_data.rs b/vnc/src/encoding/test_hextile_image_data.rs new file mode 100644 index 000000000..bfedd8720 --- /dev/null +++ b/vnc/src/encoding/test_hextile_image_data.rs @@ -0,0 +1,1160 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// Image data: Each tile contains only one pixel. +/// Width of image = 32 +/// Height of image = 32 +/// Stride of image = 128 +/// Total length is 4096 Byte. +pub const IMAGE_DATA_SINGLE_PIXEL: [u8; 4096] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// The data stream obtained afetr the IMAGE_DATA_2 is compressed using the Hextile algorithm. +/// Total length is equal to 12. +pub const TARGET_DATA_SINGLE_PIXEL: [u8; 12] = [ + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Image data: Each tile contains two different pixels. +/// Width of image = 40 +/// Height of image = 40 +/// Stride of image = 160 +/// Total length is 6400 Byte. +pub const IMAGE_DATA_TWO_PIXEL: [u8; 6400] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, +]; + +pub const TARGET_DATA_TWO_PIXEL: [u8; 348] = [ + 0x0e, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x19, 0x02, 0x70, 0x03, 0x10, 0x33, 0x10, + 0x63, 0x10, 0x04, 0x00, 0x34, 0x10, 0x74, 0x00, 0x35, 0x10, 0x95, 0x30, 0x36, 0x10, 0xc6, 0x10, + 0x37, 0x10, 0x97, 0x40, 0x38, 0x10, 0x88, 0x10, 0xc8, 0x10, 0x39, 0x10, 0x89, 0x10, 0xc9, 0x10, + 0x3a, 0x10, 0x8a, 0x10, 0xca, 0x10, 0x2b, 0x30, 0x9b, 0x20, 0xdb, 0x10, 0x08, 0x18, 0x82, 0x20, + 0x93, 0x10, 0x94, 0x10, 0x15, 0x40, 0x95, 0x10, 0xd5, 0x10, 0x06, 0x10, 0x56, 0x10, 0x96, 0x10, + 0xc6, 0x10, 0x17, 0x10, 0x97, 0x30, 0x28, 0x20, 0x98, 0x30, 0x49, 0x10, 0x99, 0x10, 0xc9, 0x10, + 0x0a, 0x10, 0x5a, 0x10, 0x9a, 0x10, 0xda, 0x10, 0x1b, 0x40, 0x8b, 0x20, 0xdb, 0x10, 0x08, 0x09, + 0x15, 0x40, 0x06, 0x10, 0x56, 0x10, 0x17, 0x10, 0x28, 0x20, 0x49, 0x10, 0x0a, 0x10, 0x5a, 0x10, + 0x1b, 0x40, 0x08, 0x1a, 0xa2, 0x30, 0x93, 0x10, 0xd3, 0x10, 0x04, 0x10, 0x64, 0x00, 0x84, 0x10, + 0xe4, 0x00, 0x05, 0x10, 0x55, 0x10, 0x85, 0x10, 0x46, 0x10, 0x86, 0x10, 0x37, 0x10, 0x87, 0x10, + 0x28, 0x10, 0x88, 0x10, 0x19, 0x10, 0x89, 0x10, 0xe9, 0x00, 0x0a, 0x10, 0x5a, 0x10, 0x9a, 0x10, + 0xda, 0x10, 0x0b, 0x00, 0x5b, 0x10, 0xab, 0x30, 0x08, 0x1e, 0x05, 0x10, 0x35, 0x20, 0x85, 0x10, + 0xc5, 0x10, 0x16, 0x10, 0x56, 0x10, 0x86, 0x10, 0xc6, 0x10, 0x17, 0x10, 0x57, 0x10, 0x87, 0x10, + 0xc7, 0x10, 0x18, 0x10, 0x58, 0x10, 0x88, 0x10, 0xc8, 0x10, 0x19, 0x10, 0x59, 0x10, 0x89, 0x10, + 0xc9, 0x10, 0x1a, 0x10, 0x5a, 0x10, 0x8a, 0x10, 0xca, 0x10, 0x1b, 0x40, 0x9b, 0x20, 0xdb, 0x10, + 0x1c, 0x10, 0x1d, 0x10, 0x0e, 0x30, 0x08, 0x0a, 0x42, 0x10, 0x33, 0x10, 0x24, 0x10, 0x25, 0x10, + 0x26, 0x10, 0x27, 0x10, 0x28, 0x10, 0x29, 0x10, 0x3a, 0x10, 0x4b, 0x10, 0x08, 0x10, 0x02, 0x10, + 0x62, 0x10, 0xb2, 0x10, 0x03, 0x20, 0x53, 0x20, 0xb3, 0x10, 0x04, 0x70, 0x05, 0x70, 0xa5, 0x20, + 0x06, 0x10, 0x36, 0x10, 0x66, 0x10, 0xb6, 0x10, 0x07, 0x10, 0x67, 0x10, 0xb7, 0x10, 0x08, 0x0a, + 0x02, 0x50, 0x13, 0x10, 0x53, 0x10, 0x14, 0x10, 0x54, 0x10, 0x15, 0x10, 0x55, 0x10, 0x16, 0x40, + 0x17, 0x10, 0x57, 0x10, 0x0e, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x70, + 0x01, 0x70, 0x22, 0x30, 0x33, 0x10, 0x26, 0x00, 0x56, 0x00, 0x27, 0x30, +]; + +/// Image data: Each tile contains multi pixels. +/// Width of image = 40 +/// Height of image = 40 +/// Stride of image = 160 +/// Total length is 6400 Byte. +pub const IMAGE_DATA_MULTI_PIXELS: [u8; 6400] = [ + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, + 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, +]; + +pub const TARGET_DATA_MULTI_PIXELS: [u8; 557] = [ + 0x1a, 0x8a, 0x71, 0x7b, 0xff, 0x20, 0x8a, 0x72, 0x7b, 0xff, 0x00, 0x50, 0x8a, 0x71, 0x7a, 0xff, + 0xe0, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x01, 0x50, 0x8a, 0x71, 0x7a, 0xff, 0xe1, 0x10, 0x8a, 0x72, + 0x7b, 0xff, 0x02, 0x50, 0x8a, 0x71, 0x7a, 0xff, 0xe2, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x03, 0x50, + 0x8a, 0x71, 0x7a, 0xff, 0xe3, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x04, 0x50, 0x8a, 0x71, 0x7a, 0xff, + 0xe4, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x05, 0x50, 0x8a, 0x71, 0x7a, 0xff, 0xe5, 0x10, 0x8a, 0x72, + 0x7b, 0xff, 0x06, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe6, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x07, 0x10, + 0x8a, 0x71, 0x7a, 0xff, 0xe7, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x08, 0x10, 0x8a, 0x71, 0x7a, 0xff, + 0xe8, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x09, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe9, 0x10, 0x8a, 0x72, + 0x7b, 0xff, 0x0a, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xea, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x0b, 0x10, + 0x8a, 0x71, 0x7a, 0xff, 0xeb, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x0c, 0x10, 0x8a, 0x71, 0x7a, 0xff, + 0xec, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x0d, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xed, 0x10, 0x8a, 0x72, + 0x7b, 0xff, 0x0e, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xee, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x0f, 0x10, + 0x8a, 0x71, 0x7a, 0xff, 0xef, 0x10, 0x06, 0x8a, 0x71, 0x7a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x10, 0x00, 0x10, 0x01, 0x10, 0x02, 0x10, 0x03, + 0x10, 0x04, 0x10, 0x05, 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, 0x09, 0x10, 0x0a, 0x10, 0x0b, + 0x10, 0x0c, 0x10, 0x0d, 0x10, 0x0e, 0x10, 0x0f, 0x10, 0x1a, 0x8a, 0x71, 0x7b, 0xff, 0x20, 0x8a, + 0x72, 0x7b, 0xff, 0x00, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe0, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x01, + 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe1, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x02, 0x10, 0x8a, 0x71, 0x7a, + 0xff, 0xe2, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x03, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe3, 0x10, 0x8a, + 0x72, 0x7b, 0xff, 0x04, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe4, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x05, + 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe5, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x06, 0x10, 0x8a, 0x71, 0x7a, + 0xff, 0xe6, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x07, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe7, 0x10, 0x8a, + 0x72, 0x7b, 0xff, 0x08, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe8, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x09, + 0x10, 0x8a, 0x71, 0x7a, 0xff, 0xe9, 0x10, 0x8a, 0x72, 0x7b, 0xff, 0x0a, 0x10, 0x8a, 0x71, 0x7a, + 0xff, 0x2a, 0xd0, 0x8a, 0x72, 0x7b, 0xff, 0x0b, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0x2b, 0xd0, 0x8a, + 0x72, 0x7b, 0xff, 0x0c, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0x2c, 0xd0, 0x8a, 0x72, 0x7b, 0xff, 0x0d, + 0x10, 0x8a, 0x71, 0x7a, 0xff, 0x2d, 0xd0, 0x8a, 0x72, 0x7b, 0xff, 0x0e, 0x10, 0x8a, 0x71, 0x7a, + 0xff, 0x2e, 0xd0, 0x8a, 0x72, 0x7b, 0xff, 0x0f, 0x10, 0x8a, 0x71, 0x7a, 0xff, 0x2f, 0xd0, 0x0e, + 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x70, 0x7a, 0xff, 0x0a, 0xe6, 0x10, 0xe7, 0x10, 0xe8, 0x10, 0xe9, + 0x10, 0xea, 0x10, 0xeb, 0x10, 0xec, 0x10, 0xed, 0x10, 0xee, 0x10, 0xef, 0x10, 0x0e, 0x8a, 0x70, + 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x06, 0x00, 0x10, 0x01, 0x10, 0x02, 0x10, 0x03, 0x10, 0x04, + 0x10, 0x05, 0x10, 0x0e, 0x8a, 0x71, 0x7a, 0xff, 0x8a, 0x72, 0x7b, 0xff, 0x08, 0x00, 0x10, 0x01, + 0x10, 0x02, 0x10, 0x03, 0x10, 0x04, 0x10, 0x05, 0x10, 0x06, 0x10, 0x07, 0x10, 0x0c, 0x8a, 0x70, + 0x7a, 0xff, 0x06, 0xe0, 0x10, 0xe1, 0x10, 0xe2, 0x10, 0xe3, 0x10, 0xe4, 0x10, 0xe5, 0x10, 0x0e, + 0x8a, 0x70, 0x7a, 0xff, 0x8a, 0x71, 0x7a, 0xff, 0x02, 0x06, 0x10, 0x07, 0x10, +]; diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 227e177cc..0455bf9bf 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -17,6 +17,7 @@ pub use error::VncError; pub mod auth; pub mod client; mod data; +pub mod encoding; pub mod input; pub mod pixman; pub mod server; diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 456836934..eafbb510e 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -13,12 +13,13 @@ use crate::VncError; use crate::{ client::{ - RectInfo, Rectangle, ServerMsg, VncClient, VncFeatures, ENCODING_ALPHA_CURSOR, - ENCODING_RAW, ENCODING_RICH_CURSOR, + DisplayMode, RectInfo, Rectangle, ServerMsg, VncClient, VncFeatures, ENCODING_ALPHA_CURSOR, + ENCODING_HEXTILE, ENCODING_RAW, ENCODING_RICH_CURSOR, }, + encoding::enc_hextile::hextile_send_framebuffer_update, pixman::{ bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, - unref_pixman_image, PixelFormat, + unref_pixman_image, }, round_up, round_up_div, server::VncServer, @@ -165,27 +166,22 @@ fn start_vnc_thread() -> Result<()> { let mut num_rects: i32 = 0; let mut buf = Vec::new(); + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut [0_u8; 2].to_vec()); let locked_server = server.lock().unwrap(); for rect in rect_info.rects.iter_mut() { if check_rect(rect, rect_info.width, rect_info.height) { - let n = send_framebuffer_update( - rect_info.image, - rect_info.encoding, - rect, - rect_info.convert, - rect_info.big_endian, - &rect_info.pixel_format, - &mut buf, - ); + let n = + send_framebuffer_update(rect_info.image, rect, &rect_info.dpm, &mut buf); if n >= 0 { num_rects += n; } } } - buf.insert(2, num_rects as u8); - buf.insert(2, (num_rects >> 8) as u8); + buf[2] = (num_rects >> 8) as u8; + buf[3] = num_rects as u8; let client = if let Some(client) = locked_server.clients.get(&rect_info.addr) { client.clone() @@ -383,73 +379,95 @@ pub fn framebuffer_upadate(x: i32, y: i32, w: i32, h: i32, encoding: i32, buf: & buf.append(&mut encoding.to_be_bytes().to_vec()); } +/// Write pixel to client. +/// +/// # Arguments +/// +/// * `data_ptr` - pointer to the data need. +/// * `copy_bytes` - total pixel to write. +/// * `client_dpm` - Output mod of client display. +/// * `buf` - send buffer. +pub fn write_pixel( + data_ptr: *mut u8, + copy_bytes: usize, + client_dpm: &DisplayMode, + buf: &mut Vec, +) { + if !client_dpm.convert { + let mut con = vec![0; copy_bytes]; + unsafe { + ptr::copy(data_ptr as *mut u8, con.as_mut_ptr(), copy_bytes); + } + buf.append(&mut con); + } else if client_dpm.convert && bytes_per_pixel() == 4 { + let num = copy_bytes >> 2; + let ptr = data_ptr as *mut u32; + for i in 0..num { + let color = unsafe { *ptr.add(i) }; + convert_pixel(client_dpm, buf, color); + } + } +} + /// Convert the sent information to a format supported /// by the client depend on byte arrangement /// /// # Arguments /// -/// * `ptr` = pointer to the data need to be convert -/// * `big_endian` - byte arrangement -/// * `pf` - pixelformat -/// * `buf` - send buffer -/// * `size` - total number of bytes need to convert -fn convert_pixel(ptr: *mut u32, big_endian: bool, pf: &PixelFormat, buf: &mut Vec, size: u32) { - let num = size >> 2; - for i in 0..num { - let value = unsafe { *ptr.offset(i as isize) }; - let mut ret = [0u8; 4]; - let red = (((value >> 16) & 0xff) << pf.red.bits) >> 8; - let green = (((value >> 8) & 0xff) << pf.green.bits) >> 8; - let blue = ((value & 0xff) << pf.blue.bits) >> 8; - let v = (red << pf.red.shift) | (green << pf.green.shift) | (blue << pf.blue.shift); - match pf.pixel_bytes { - 1 => { +/// * `client_dpm` - Output mod of client display. +/// * `buf` - send buffer. +/// * `color` - the pixel value need to be convert. +pub fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { + let mut ret = [0u8; 4]; + let r = ((color & 0x00ff0000) >> 16) << client_dpm.pf.red.bits >> 8; + let g = ((color & 0x0000ff00) >> 8) << client_dpm.pf.green.bits >> 8; + let b = (color & 0x000000ff) << client_dpm.pf.blue.bits >> 8; + let v = (r << client_dpm.pf.red.shift) + | (g << client_dpm.pf.green.shift) + | (b << client_dpm.pf.blue.shift); + match client_dpm.pf.pixel_bytes { + 1 => { + ret[0] = v as u8; + } + 2 => { + if client_dpm.client_be { + ret[0] = (v >> 8) as u8; + ret[1] = v as u8; + } else { + ret[1] = (v >> 8) as u8; ret[0] = v as u8; } - 2 => { - if big_endian { - ret[0] = (v >> 8) as u8; - ret[1] = v as u8; - } else { - ret[1] = (v >> 8) as u8; - ret[0] = v as u8; - } - } - 4 => { - if big_endian { - ret = (v as u32).to_be_bytes(); - } else { - ret = (v as u32).to_le_bytes(); - } + } + 4 => { + if client_dpm.client_be { + ret = (v as u32).to_be_bytes(); + } else { + ret = (v as u32).to_le_bytes(); } - _ => { - if big_endian { - ret = (v as u32).to_be_bytes(); - } else { - ret = (v as u32).to_le_bytes(); - } + } + _ => { + if client_dpm.client_be { + ret = (v as u32).to_be_bytes(); + } else { + ret = (v as u32).to_le_bytes(); } } - buf.append(&mut ret[..pf.pixel_bytes as usize].to_vec()); } + buf.append(&mut ret[..client_dpm.pf.pixel_bytes as usize].to_vec()); } /// Send raw data directly without compression /// /// # Arguments /// -/// * `image` = pointer to the data need to be send -/// * `rect` - dirty area of image -/// * `convert` - is need to be convert -/// * `big_endian` - send buffer -/// * `pixel_format` - pixelformat -/// * `buf` - send buffer -fn raw_send_framebuffer_update( +/// * `image` - pointer to the data need to be send. +/// * `rect` - dirty area of image. +/// * `client_dpm` - Output mod information of client display. +/// * `buf` - send buffer. +pub fn raw_send_framebuffer_update( image: *mut pixman_image_t, rect: &Rectangle, - convert: bool, - big_endian: bool, - pixel_format: &PixelFormat, + client_dpm: &DisplayMode, buf: &mut Vec, ) -> i32 { let mut data_ptr = get_image_data(image) as *mut u8; @@ -461,22 +479,7 @@ fn raw_send_framebuffer_update( let copy_bytes = rect.w as usize * bytes_per_pixel(); for _i in 0..rect.h { - if !convert { - let mut con = vec![0; copy_bytes]; - unsafe { - ptr::copy(data_ptr, con.as_mut_ptr(), copy_bytes); - } - buf.append(&mut con); - } else { - convert_pixel( - data_ptr as *mut u32, - big_endian, - pixel_format, - buf, - copy_bytes as u32, - ); - } - + write_pixel(data_ptr, copy_bytes as usize, client_dpm, buf); data_ptr = (data_ptr as usize + stride as usize) as *mut u8; } @@ -487,32 +490,26 @@ fn raw_send_framebuffer_update( /// /// # Arguments /// -/// * `image` = pointer to the data need to be send -/// * `rect` - dirty area of image -/// * `convert` - is need to be convert -/// * `big_endian` - send buffer -/// * `pixel_format` - pixelformat -/// * `buf` - send buffer +/// * `image` = pointer to the data need to be send. +/// * `rect` - dirty area of image. +/// * `client_dpm` - Output mod information of client display. +/// * `buf` - send buffer. fn send_framebuffer_update( image: *mut pixman_image_t, - encoding: i32, rect: &Rectangle, - convert: bool, - big_endian: bool, - pixel_format: &PixelFormat, + client_dpm: &DisplayMode, buf: &mut Vec, ) -> i32 { - framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, encoding, buf); - /* - match encoding { - ENCODING_ZLIB => { /* ing... */ } + match client_dpm.enc { + ENCODING_HEXTILE => { + framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_HEXTILE, buf); + hextile_send_framebuffer_update(image, rect, client_dpm, buf) + } _ => { - VncServer::framebuffer_upadate(rect, encoding, buf); - n = VncServer::raw_send_framebuffer_update(image, rect, buf); + framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_RAW, buf); + raw_send_framebuffer_update(image, rect, client_dpm, buf) } } - */ - raw_send_framebuffer_update(image, rect, convert, big_endian, pixel_format, buf) } /// Initialize a default image @@ -679,11 +676,9 @@ pub fn display_cursor_define( ENCODING_RICH_CURSOR as i32, &mut buf, ); - let big_endian = client.big_endian; - let pixel_format = &client.pixel_format; - let size = cursor.width * cursor.height * pixel_format.pixel_bytes as u32; - let ptr = cursor.data.as_ptr() as *mut u32; - convert_pixel(ptr, big_endian, pixel_format, &mut buf, size); + let data_size = cursor.width * cursor.height * client.client_dpm.pf.pixel_bytes as u32; + let data_ptr = cursor.data.as_ptr() as *mut u8; + write_pixel(data_ptr, data_size as usize, &client.client_dpm, &mut buf); buf.append(mask); client.write_msg(&buf); } -- Gitee From 3ac35b691202cb49bf794e20331e97a7d2312359 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 6 Nov 2022 22:55:01 +0800 Subject: [PATCH 0520/2187] VNC: Split the VncServer struct. Split the data and event from VncServer struct and add vnciohandler to handle callback events. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 13 +- vnc/src/client.rs | 37 +-- vnc/src/error.rs | 2 + vnc/src/input.rs | 2 +- vnc/src/pixman.rs | 15 ++ vnc/src/server.rs | 642 +++++++++++++++++++++++++------------------- vnc/src/vencrypt.rs | 4 +- vnc/src/vnc.rs | 191 ++++++------- 8 files changed, 493 insertions(+), 413 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 28a11ea77..8050ba4fc 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -60,7 +60,7 @@ pub enum SubAuthState { /// Struct of sasl authentiation. #[derive(Debug, Clone)] -pub struct Sasl { +pub struct SaslAuth { /// State of sasl connection . pub sasl_conn: *mut sasl_conn_t, /// Identity user. @@ -77,9 +77,9 @@ pub struct Sasl { pub run_ssf: u32, } -impl Sasl { +impl SaslAuth { pub fn default() -> Self { - Sasl { + SaslAuth { sasl_conn: ptr::null_mut() as *mut sasl_conn_t, identity: String::new(), mech_list: String::new(), @@ -91,13 +91,6 @@ impl Sasl { } } -/// Configuration for authentication. -/// Identity: authentication user. -#[derive(Debug, Clone, Default)] -pub struct SaslAuth { - pub identity: String, -} - /// Authentication stage. #[derive(Clone, Copy, PartialEq, Debug)] pub enum SaslStage { diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 3ea8028a9..6d8b23053 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -12,16 +12,15 @@ use crate::VncError; use crate::{ - auth::Sasl, + auth::SaslAuth, auth::{AuthState, SubAuthState}, pixman::{get_image_height, get_image_width, PixelFormat}, round_up_div, server::VncServer, utils::BuffPool, vnc::{ - display_cursor_define, framebuffer_upadate, set_area_dirty, update_client_surface, - DisplayMouse, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, - MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, + display_cursor_define, framebuffer_upadate, set_area_dirty, BIT_PER_BYTE, DIRTY_PIXELS_NUM, + DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, }, }; use anyhow::{anyhow, Result}; @@ -253,11 +252,11 @@ pub struct VncClient { /// The function handling the connection. pub handlers: Vec>>>, /// Pointer to VncServer. - pub server: Arc>, + pub server: Arc, /// Tls server connection. pub tls_conn: Option, /// Configuration for sasl authentication. - pub sasl: Sasl, + pub sasl: SaslAuth, /// State flags whether the image needs to be updated for the client. state: UpdateState, /// Identify the image update area. @@ -284,7 +283,7 @@ impl VncClient { addr: String, auth: AuthState, subauth: SubAuthState, - server: Arc>, + server: Arc, image: *mut pixman_image_t, ) -> Self { VncClient { @@ -299,7 +298,7 @@ impl VncClient { handlers: Vec::new(), server, tls_conn: None, - sasl: Sasl::default(), + sasl: SaslAuth::default(), state: UpdateState::No, dirty_bitmap: Bitmap::::new( MAX_WINDOW_HEIGHT as usize @@ -954,21 +953,8 @@ impl VncClient { self.desktop_resize(); // VNC display cursor define. - let mut cursor: DisplayMouse = DisplayMouse::default(); let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let locked_server = server.lock().unwrap(); - let mut mask: Vec = Vec::new(); - if let Some(c) = &locked_server.cursor { - cursor = c.clone(); - } - if let Some(m) = &locked_server.mask { - mask = m.clone(); - } - drop(locked_server); - if !cursor.data.is_empty() { - display_cursor_define(self, &mut cursor, &mut mask); - } - + display_cursor_define(self, &server); self.update_event_handler(1, VncClient::handle_protocol_msg); Ok(()) } @@ -1162,9 +1148,6 @@ fn connection_cleanup(client: Arc>) { info!("Client disconnect : {:?}", addr); client.lock().unwrap().disconnect(); let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_server = server.lock().unwrap(); - locked_server.clients.remove(&addr); - if locked_server.clients.is_empty() { - update_client_surface(&mut locked_server); - } + let mut locked_clients = server.clients.lock().unwrap(); + locked_clients.remove(&addr); } diff --git a/vnc/src/error.rs b/vnc/src/error.rs index 70f8ec91b..7d2c52d21 100644 --- a/vnc/src/error.rs +++ b/vnc/src/error.rs @@ -25,6 +25,8 @@ pub enum VncError { InvalidImageSize, #[error("Tcp bind failed: {0}")] TcpBindFailed(String), + #[error("Make connection failed: {0}")] + MakeConnectionFailed(String), #[error("Make tls connection failed: {0}")] MakeTlsConnectionFailed(String), #[error("ProtocolMessage failed: {0}")] diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 9c524938c..1634d1e07 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -53,9 +53,9 @@ impl VncClient { let keycode: u16; match self .server + .keysym2keycode .lock() .unwrap() - .keysym2keycode .get(&(keysym as u16)) { Some(k) => keycode = *k, diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs index 3456d797f..b4da1a3ae 100644 --- a/vnc/src/pixman.rs +++ b/vnc/src/pixman.rs @@ -112,22 +112,37 @@ impl PixelFormat { } pub fn get_image_width(image: *mut pixman_image_t) -> i32 { + if image.is_null() { + return 0; + } unsafe { pixman_image_get_width(image as *mut pixman_image_t) as i32 } } pub fn get_image_height(image: *mut pixman_image_t) -> i32 { + if image.is_null() { + return 0; + } unsafe { pixman_image_get_height(image as *mut pixman_image_t) as i32 } } pub fn get_image_stride(image: *mut pixman_image_t) -> i32 { + if image.is_null() { + return 0; + } unsafe { pixman_image_get_stride(image as *mut pixman_image_t) } } pub fn get_image_data(image: *mut pixman_image_t) -> *mut u32 { + if image.is_null() { + return ptr::null_mut() as *mut u32; + } unsafe { pixman_image_get_data(image as *mut pixman_image_t) } } pub fn get_image_format(image: *mut pixman_image_t) -> pixman_format_code_t { + if image.is_null() { + return pixman_format_code_t::PIXMAN_x8r8g8b8; + } unsafe { pixman_image_get_format(image as *mut pixman_image_t) } } diff --git a/vnc/src/server.rs b/vnc/src/server.rs index eb6b05b63..1eb2d7ad1 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -22,9 +22,9 @@ use crate::{ round_up_div, vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, vnc::{ - update_client_surface, DisplayMouse, DIRTY_PIXELS_NUM, DISPLAY_UPDATE_INTERVAL_DEFAULT, + update_server_surface, DisplayMouse, DIRTY_PIXELS_NUM, DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, MAX_WINDOW_HEIGHT, - MAX_WINDOW_WIDTH, REFRESH_EVT, VNC_BITMAP_WIDTH, VNC_SERVERS, + MAX_WINDOW_WIDTH, VNC_BITMAP_WIDTH, VNC_SERVERS, }, VncError, }; @@ -37,7 +37,7 @@ use machine_manager::{ use std::{ cmp, collections::HashMap, - net::{Shutdown, TcpListener}, + net::{Shutdown, SocketAddr, TcpListener, TcpStream}, os::unix::prelude::{AsRawFd, RawFd}, ptr, sync::{Arc, Mutex}, @@ -50,7 +50,114 @@ use util::{ pixman_image_t, pixman_op_t, }, }; -use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +const CONNECTION_LIMIT: usize = 1; + +/// Information of VncServer. +pub struct VncServer { + /// Event fd for vnc refresh. + pub refresh_fd: Arc>, + /// Clients connected to vnc. + pub clients: Arc>>>>, + /// Security Type for connection. + pub security_type: Arc>, + /// Mapping ASCII to keycode. + pub keysym2keycode: Arc>>, + /// Image data of surface. + pub vnc_surface: Arc>, + /// Data for cursor image. + pub vnc_cursor: Arc>, + /// Connection limit. + conn_limits: usize, + /// Updating interval of display devices. + pub update_interval: Arc>, +} + +unsafe impl Send for VncServer {} +unsafe impl Sync for VncServer {} + +impl VncServer { + /// Create a new VncServer. + pub fn new(refresh_fd: Arc>, guest_image: *mut pixman_image_t) -> Self { + VncServer { + refresh_fd, + clients: Arc::new(Mutex::new(HashMap::new())), + security_type: Arc::new(Mutex::new(SecurityType::default())), + keysym2keycode: Arc::new(Mutex::new(HashMap::new())), + vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), + vnc_cursor: Arc::new(Mutex::new(VncCursor::default())), + conn_limits: CONNECTION_LIMIT, + update_interval: Arc::new(Mutex::new(0_u32)), + } + } +} + +pub struct VncConnHandler { + /// Tcp connection listened by server. + listener: TcpListener, + /// VncServer. + server: Arc, +} + +impl VncConnHandler { + pub fn new(listener: TcpListener, server: Arc) -> Self { + VncConnHandler { listener, server } + } +} + +/// Internal_notifiers for VncServer. +impl EventNotifierHelper for VncConnHandler { + fn internal_notifiers(vnc_io: Arc>) -> Vec { + let vnc_io_clone = vnc_io.clone(); + let server = vnc_io.lock().unwrap().server.clone(); + // Register event notifier for connection. + let handler: Box Option>> = + Box::new(move |_event, fd: RawFd| { + read_fd(fd); + match vnc_io_clone.clone().lock().unwrap().listener.accept() { + Ok((stream, addr)) => { + if let Err(e) = handle_connection(&server, stream, addr) { + error!("{:?}", e); + } + } + Err(e) => { + error!("Connect failed: {:?}", e); + } + } + + None as Option> + }); + let mut notifiers = vec![ + (EventNotifier::new( + NotifierOperation::AddShared, + vnc_io.lock().unwrap().listener.as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )), + ]; + + // Register event notifier to refresh + // the image from guest_imag to server image. + let server = vnc_io.lock().unwrap().server.clone(); + let handler: Box Option>> = + Box::new(move |_event, fd: RawFd| { + read_fd(fd); + vnc_refresh(); + None as Option> + }); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + server.refresh_fd.lock().unwrap().as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + notifiers + } +} + /// Info of image. /// stride is not always equal to stride because of memory alignment. pub struct ImageInfo { @@ -88,12 +195,8 @@ impl ImageInfo { } } -/// VncServer -pub struct VncServer { - /// Tcp connection listened by server. - listener: Arc>, - /// Clients connected to vnc. - pub clients: HashMap>>, +/// Security type for connection and transport. +pub struct SecurityType { /// Configuration for tls connection. pub tlscreds: Option, /// Configuration for sasl Authentication. @@ -104,67 +207,23 @@ pub struct VncServer { pub auth: AuthState, /// Subauth type. pub subauth: SubAuthState, - /// Mapping ASCII to keycode. - pub keysym2keycode: HashMap, - /// Image refresh to VncClient. - pub server_image: *mut pixman_image_t, - /// Image from gpu. - pub guest_image: *mut pixman_image_t, - /// Identify the image update area for guest image. - pub guest_dirty_bitmap: Bitmap, - /// Image format of pixman. - pub guest_format: pixman_format_code_t, - /// Cursor property. - pub cursor: Option, - /// Identify the area need update for cursor. - pub mask: Option>, - /// Connection limit. - conn_limits: usize, - /// Width of current image. - pub true_width: i32, - /// updating interval of display devices. - pub update_interval: u32, } -unsafe impl Send for VncServer {} - -impl VncServer { - /// Create a new VncServer. - pub fn new(listener: Arc>, guest_image: *mut pixman_image_t) -> Self { - VncServer { - listener, - clients: HashMap::new(), +impl Default for SecurityType { + fn default() -> Self { + SecurityType { tlscreds: None, saslauth: None, tls_config: None, auth: AuthState::No, subauth: SubAuthState::VncAuthVencryptPlain, - keysym2keycode: HashMap::new(), - server_image: ptr::null_mut(), - guest_image, - guest_dirty_bitmap: Bitmap::::new( - MAX_WINDOW_HEIGHT as usize - * round_up_div( - (MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM) as u64, - u64::BITS as u64, - ) as usize, - ), - guest_format: pixman_format_code_t::PIXMAN_x8r8g8b8, - cursor: None, - mask: None, - conn_limits: 1, - true_width: 0, - update_interval: 0, } } +} - /// make configuration for VncServer - /// - /// # Arguments - /// - /// * `vnc_cfg` - configure of vnc. - /// * `object` - configure of sasl and tls. - pub fn make_config(&mut self, vnc_cfg: &VncConfig, object: &ObjectConfig) -> Result<()> { +impl SecurityType { + // Set security config. + fn set_security_config(&mut self, vnc_cfg: &VncConfig, object: &ObjectConfig) -> Result<()> { // Tls configuration. if let Some(tls_cred) = object.tls_object.get(&vnc_cfg.tls_creds) { let tlscred = TlsCreds { @@ -186,28 +245,16 @@ impl VncServer { } // Sasl configuration. - if let Some(sasl_auth) = object.sasl_object.get(&vnc_cfg.sasl_authz) { - let saslauth = SaslAuth { - identity: sasl_auth.identity.clone(), - }; - self.saslauth = Some(saslauth); - } - - // Server.auth. - if let Err(err) = self.setup_auth() { - return Err(err); + if let Some(_sasl_auth) = object.sasl_object.get(&vnc_cfg.sasl_authz) { + self.saslauth = Some(SaslAuth::default()); } - // Mapping ASCII to keycode. - for &(k, v) in KEYSYM2KEYCODE.iter() { - self.keysym2keycode.insert(k, v); - } Ok(()) } /// Encryption configuration. - pub fn setup_auth(&mut self) -> Result<()> { - if let Some(tlscred) = &self.tlscreds { + fn set_auth(&mut self) -> Result<()> { + if let Some(tlscred) = self.tlscreds.clone() { self.auth = AuthState::Vencrypt; if tlscred.cred_type != *X509_CERT && tlscred.cred_type != *ANON_CERT { error!("Unsupported tls cred type"); @@ -228,53 +275,45 @@ impl VncServer { self.auth = AuthState::No; self.subauth = SubAuthState::VncAuthVencryptPlain; } - Ok(()) } +} - /// Set diry for client - /// - /// # Arguments - /// - /// * `x` `y`- coordinates of dirty area. - fn set_dirty_for_clients(&mut self, x: usize, y: usize) { - for client in self.clients.values_mut() { - client - .lock() - .unwrap() - .dirty_bitmap - .set(x + y * VNC_BITMAP_WIDTH as usize) - .unwrap(); - } - } +/// Image date of cursor. +#[derive(Default)] +pub struct VncCursor { + /// Cursor property. + pub cursor: Option, + /// Identify the area need update for cursor. + pub mask: Option>, +} - /// Transfer dirty data to buff in one line - /// - /// # Arguments - /// - /// * `s_info` - Info of Server image. - /// * `g_info` - Info of Guest image. - fn get_one_line_buf( - &self, - s_info: &mut ImageInfo, - g_info: &mut ImageInfo, - ) -> *mut pixman_image_t { - let mut line_buf = ptr::null_mut(); - if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { - line_buf = unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - get_image_width(self.server_image), - 1, - ptr::null_mut(), - 0, - ) - }; - g_info.stride = s_info.stride; - g_info.length = g_info.stride; - } +/// The image data for vnc display surface. +pub struct VncSurface { + /// Image from display device. + pub guest_image: *mut pixman_image_t, + /// Identify the image update area for guest image. + pub guest_dirty_bitmap: Bitmap, + /// Image refresh to vnc client. + pub server_image: *mut pixman_image_t, + /// Image format of pixman. + pub guest_format: pixman_format_code_t, +} - line_buf +impl VncSurface { + fn new(guest_image: *mut pixman_image_t) -> Self { + VncSurface { + guest_image, + guest_dirty_bitmap: Bitmap::::new( + MAX_WINDOW_HEIGHT as usize + * round_up_div( + (MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM) as u64, + u64::BITS as u64, + ) as usize, + ), + server_image: ptr::null_mut(), + guest_format: pixman_format_code_t::PIXMAN_x8r8g8b8, + } } /// Get min width. @@ -293,6 +332,69 @@ impl VncServer { ) } + /// Flush dirty data from guest_image to server_image. + /// Return the number of dirty area. + pub fn update_server_image(&mut self) -> i32 { + let mut dirty_num = 0; + let height = self.get_min_height(); + let g_bpl = self.guest_dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; + + let mut offset = self.guest_dirty_bitmap.find_next_bit(0).unwrap(); + if offset >= (height as usize) * g_bpl { + return dirty_num; + } + + let mut s_info = ImageInfo::new(self.server_image); + let mut g_info = ImageInfo::new(self.guest_image); + + // The guset image is not changed, so there is no + // need to update the server image. + let cmp_bytes = cmp::min( + DIRTY_PIXELS_NUM as usize * bytes_per_pixel(), + s_info.stride as usize, + ); + + let line_buf = self.get_one_line_buf(&mut s_info, &mut g_info); + loop { + let mut y = offset / g_bpl; + let x = offset % g_bpl; + s_info.ptr = + (s_info.data as usize + y * s_info.stride as usize + x * cmp_bytes) as *mut u8; + + if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + self.guest_image, + ptr::null_mut(), + line_buf, + 0, + y as i16, + 0, + 0, + 0, + 0, + self.get_min_width() as u16, + 1, + ); + }; + g_info.ptr = get_image_data(line_buf) as *mut u8; + } else { + g_info.ptr = (g_info.data as usize + y * g_info.stride as usize) as *mut u8; + } + g_info.ptr = (g_info.ptr as usize + x * cmp_bytes) as *mut u8; + dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes); + y += 1; + offset = self.guest_dirty_bitmap.find_next_bit(y * g_bpl).unwrap(); + if offset >= (height as usize) * g_bpl { + break; + } + } + + unref_pixman_image(line_buf); + dirty_num + } + /// Update each line /// /// # Arguments @@ -347,7 +449,7 @@ impl VncServer { ptr::copy(g_info.ptr, s_info.ptr, _cmp_bytes); }; - self.set_dirty_for_clients(x, y); + set_dirty_for_each_clients(x, y); count += 1; x += 1; @@ -358,163 +460,105 @@ impl VncServer { count } - /// Flush dirty data from guest_image to server_image. - /// Return the number of dirty area. - pub fn update_server_image(&mut self) -> i32 { - let mut dirty_num = 0; - let height = self.get_min_height(); - let g_bpl = self.guest_dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; - - let mut offset = self.guest_dirty_bitmap.find_next_bit(0).unwrap(); - if offset >= (height as usize) * g_bpl { - return dirty_num; - } - - let mut s_info = ImageInfo::new(self.server_image); - let mut g_info = ImageInfo::new(self.guest_image); - - let cmp_bytes = cmp::min( - DIRTY_PIXELS_NUM as usize * bytes_per_pixel(), - s_info.stride as usize, - ); - - let line_buf = self.get_one_line_buf(&mut s_info, &mut g_info); - loop { - let mut y = offset / g_bpl; - let x = offset % g_bpl; - s_info.ptr = - (s_info.data as usize + y * s_info.stride as usize + x * cmp_bytes) as *mut u8; - - if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { - unsafe { - pixman_image_composite( - pixman_op_t::PIXMAN_OP_SRC, - self.guest_image, - ptr::null_mut(), - line_buf, - 0, - y as i16, - 0, - 0, - 0, - 0, - self.get_min_width() as u16, - 1, - ); - }; - g_info.ptr = get_image_data(line_buf) as *mut u8; - } else { - g_info.ptr = (g_info.data as usize + y * g_info.stride as usize) as *mut u8; - } - g_info.ptr = (g_info.ptr as usize + x * cmp_bytes) as *mut u8; - dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes); - y += 1; - offset = self.guest_dirty_bitmap.find_next_bit(y * g_bpl).unwrap(); - if offset >= (height as usize) * g_bpl { - break; - } - unref_pixman_image(line_buf); + /// Transfer dirty data to buff in one line + /// + /// # Arguments + /// + /// * `s_info` - Info of Server image. + /// * `g_info` - Info of Guest image. + fn get_one_line_buf( + &self, + s_info: &mut ImageInfo, + g_info: &mut ImageInfo, + ) -> *mut pixman_image_t { + let mut line_buf = ptr::null_mut(); + if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { + line_buf = unsafe { + pixman_image_create_bits( + pixman_format_code_t::PIXMAN_x8r8g8b8, + get_image_width(self.server_image), + 1, + ptr::null_mut(), + 0, + ) + }; + g_info.stride = s_info.stride; + g_info.length = g_info.stride; } - dirty_num + line_buf } +} - /// Listen to the port and accpet client's connection. - pub fn handle_connection(&mut self) -> Result<()> { - match self.listener.lock().unwrap().accept() { - Ok((stream, addr)) => { - if self.clients.len() >= self.conn_limits { - stream.shutdown(Shutdown::Both).unwrap(); - return Ok(()); - } - info!("New Client: {:?}", addr); - stream - .set_nonblocking(true) - .expect("set nonblocking failed"); - - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut client = VncClient::new( - stream, - addr.to_string(), - self.auth, - self.subauth, - server, - self.server_image, - ); - if let Some(saslauth) = &self.saslauth { - client.sasl.identity = saslauth.identity.clone(); - } - client.write_msg("RFB 003.008\n".to_string().as_bytes()); - info!("{:?}", client.stream); - - let tmp_client = Arc::new(Mutex::new(client)); - self.clients.insert(addr.to_string(), tmp_client.clone()); - - EventLoop::update_event(EventNotifierHelper::internal_notifiers(tmp_client), None)?; - } - Err(e) => { - info!("Connect failed: {:?}", e); - } - } - - update_client_surface(self); - - Ok(()) +/// Set diry for each client. +/// +/// # Arguments +/// +/// * `x` `y`- coordinates of dirty area. +fn set_dirty_for_each_clients(x: usize, y: usize) { + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_clients = server.clients.lock().unwrap(); + for client in locked_clients.values_mut() { + client + .lock() + .unwrap() + .dirty_bitmap + .set(x + y * VNC_BITMAP_WIDTH as usize) + .unwrap(); } } -/// Internal_notifiers for VncServer. -impl EventNotifierHelper for VncServer { - fn internal_notifiers(server_handler: Arc>) -> Vec { - let server = server_handler.clone(); - let handler: Box Option>> = - Box::new(move |event, fd: RawFd| { - read_fd(fd); - - if event & EventSet::HANG_UP == EventSet::HANG_UP { - info!("Client Closed"); - } else if event == EventSet::IN { - let mut locked_handler = server.lock().unwrap(); - if let Err(e) = locked_handler.handle_connection() { - error!("Failed to handle vnc client connection, error is {}", e); - } - drop(locked_handler); - } - - None as Option> - }); - - let mut notifiers = vec![ - (EventNotifier::new( - NotifierOperation::AddShared, - server_handler - .lock() - .unwrap() - .listener - .lock() - .unwrap() - .as_raw_fd(), - None, - EventSet::IN | EventSet::HANG_UP, - vec![Arc::new(Mutex::new(handler))], - )), - ]; - - let handler: Box Option>> = - Box::new(move |_event, fd: RawFd| { - read_fd(fd); - vnc_refresh(); - None as Option> - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - REFRESH_EVT.lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - vec![Arc::new(Mutex::new(handler))], - )); - notifiers +/// Accpet client's connection. +/// +/// # Arguments +/// +/// * `stream` - TcpStream. +/// * `addr`- SocketAddr. +pub fn handle_connection( + server: &Arc, + stream: TcpStream, + addr: SocketAddr, +) -> Result<()> { + if server.clients.lock().unwrap().len() >= server.conn_limits { + stream.shutdown(Shutdown::Both).unwrap(); + return Err(anyhow!(VncError::MakeConnectionFailed(String::from( + "Total connection is exceeding to limit." + )))); + } + info!("New Client: {:?}", addr); + stream + .set_nonblocking(true) + .expect("set nonblocking failed"); + + let locked_security_type = server.security_type.lock().unwrap(); + let mut client = VncClient::new( + stream, + addr.to_string(), + locked_security_type.auth, + locked_security_type.subauth, + server.clone(), + server.vnc_surface.lock().unwrap().server_image, + ); + + if let Some(saslauth) = &locked_security_type.saslauth { + client.sasl.identity = saslauth.identity.clone(); } + drop(locked_security_type); + client.write_msg("RFB 003.008\n".to_string().as_bytes()); + info!("{:?}", client.stream); + + let tmp_client = Arc::new(Mutex::new(client)); + server + .clients + .lock() + .unwrap() + .insert(addr.to_string(), tmp_client.clone()); + + EventLoop::update_event(EventNotifierHelper::internal_notifiers(tmp_client), None)?; + + update_server_surface(server); + vnc_refresh_notify(server); + Ok(()) } /// Refresh server_image to guest_image. @@ -523,26 +567,66 @@ fn vnc_refresh() { return; } let server = VNC_SERVERS.lock().unwrap()[0].clone(); - if server.lock().unwrap().clients.is_empty() { + if server.clients.lock().unwrap().is_empty() { return; } - let mut locked_server = server.lock().unwrap(); - let dirty_num = locked_server.update_server_image(); + let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); + let mut locked_update_interval = server.update_interval.lock().unwrap(); if dirty_num != 0 { - locked_server.update_interval /= 2; - if locked_server.update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { - locked_server.update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT + *locked_update_interval /= 2; + if *locked_update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { + *locked_update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT } } else { - locked_server.update_interval += DISPLAY_UPDATE_INTERVAL_INC; - if locked_server.update_interval > DISPLAY_UPDATE_INTERVAL_MAX { - locked_server.update_interval = DISPLAY_UPDATE_INTERVAL_MAX; + *locked_update_interval += DISPLAY_UPDATE_INTERVAL_INC; + if *locked_update_interval > DISPLAY_UPDATE_INTERVAL_MAX { + *locked_update_interval = DISPLAY_UPDATE_INTERVAL_MAX; } } let mut _rects: i32 = 0; - for client in locked_server.clients.values_mut() { + let mut clients = server.clients.lock().unwrap(); + for client in clients.values_mut() { _rects += client.lock().unwrap().get_rects(dirty_num); } } + +/// Refresh event. +pub fn vnc_refresh_notify(server: &Arc) { + server.refresh_fd.lock().unwrap().write(1).unwrap(); +} + +/// make configuration for VncServer +/// +/// # Arguments +/// +/// * `vnc_cfg` - configure of vnc. +/// * `object` - configure of sasl and tls. +pub fn make_server_config( + server: &Arc, + vnc_cfg: &VncConfig, + object: &ObjectConfig, +) -> Result<()> { + // Set security config. + if let Err(e) = server + .security_type + .lock() + .unwrap() + .set_security_config(vnc_cfg, object) + { + return Err(e); + } + // Set auth type. + if let Err(e) = server.security_type.lock().unwrap().set_auth() { + return Err(e); + } + let mut locked_keysym2keycode = server.keysym2keycode.lock().unwrap(); + // Mapping ASCII to keycode. + for &(k, v) in KEYSYM2KEYCODE.iter() { + locked_keysym2keycode.insert(k, v); + } + drop(locked_keysym2keycode); + + Ok(()) +} diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index ca2ca958b..10aa42d96 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -113,8 +113,8 @@ impl VncClient { buf.append(&mut (1_u8).to_be_bytes().to_vec()); self.write_msg(&buf); - if let Some(tls_config) = &self.server.lock().unwrap().tls_config { - match rustls::ServerConnection::new(Arc::clone(tls_config)) { + if let Some(tls_config) = self.server.security_type.lock().unwrap().tls_config.clone() { + match rustls::ServerConnection::new(tls_config) { Ok(tls_conn) => { self.tls_conn = Some(tls_conn); } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index eafbb510e..200364d52 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -22,7 +22,7 @@ use crate::{ unref_pixman_image, }, round_up, round_up_div, - server::VncServer, + server::{make_server_config, vnc_refresh_notify, VncConnHandler, VncServer, VncSurface}, }; use anyhow::{anyhow, Result}; use core::time; @@ -119,25 +119,23 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { .set_nonblocking(true) .expect("Set noblocking for vnc socket failed"); - let mut server = VncServer::new(Arc::new(Mutex::new(listener)), get_client_image()); - + let refresh_fd = Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); + let server = Arc::new(VncServer::new(refresh_fd, get_client_image())); // Parameter configuation for VncServeer. - if let Err(err) = server.make_config(vnc_cfg, object) { + if let Err(err) = make_server_config(&server, vnc_cfg, object) { return Err(err); } // Add an VncServer. - add_vnc_server(server); - + add_vnc_server(server.clone()); + // Register the event to listen for client's connection. + let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server))); // Vnc_thread: a thread to send the framebuffer if let Err(err) = start_vnc_thread() { return Err(err); } - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(VNC_SERVERS.lock().unwrap()[0].clone()), - None, - )?; + EventLoop::update_event(EventNotifierHelper::internal_notifiers(vnc_io), None)?; Ok(()) } @@ -170,7 +168,7 @@ fn start_vnc_thread() -> Result<()> { buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut [0_u8; 2].to_vec()); - let locked_server = server.lock().unwrap(); + let locked_surface = server.vnc_surface.lock().unwrap(); for rect in rect_info.rects.iter_mut() { if check_rect(rect, rect_info.width, rect_info.height) { let n = @@ -182,25 +180,21 @@ fn start_vnc_thread() -> Result<()> { } buf[2] = (num_rects >> 8) as u8; buf[3] = num_rects as u8; + drop(locked_surface); - let client = if let Some(client) = locked_server.clients.get(&rect_info.addr) { - client.clone() - } else { - continue; - }; - drop(locked_server); - client.lock().unwrap().write_msg(&buf); + let locked_clients = server.clients.lock().unwrap(); + if let Some(client) = locked_clients.get(&rect_info.addr) { + client.lock().unwrap().write_msg(&buf); + } + drop(locked_clients); }) .unwrap(); Ok(()) } /// Add a vnc server during initialization. -fn add_vnc_server(server: VncServer) { - VNC_SERVERS - .lock() - .unwrap() - .push(Arc::new(Mutex::new(server))); +fn add_vnc_server(server: Arc) { + VNC_SERVERS.lock().unwrap().push(server); } /// Set dirty in bitmap. @@ -230,7 +224,6 @@ pub fn set_area_dirty( } y += 1; } - REFRESH_EVT.lock().unwrap().write(1).unwrap(); } pub fn vnc_display_update(x: i32, y: i32, w: i32, h: i32) { @@ -238,10 +231,20 @@ pub fn vnc_display_update(x: i32, y: i32, w: i32, h: i32) { return; } let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_server = server.lock().unwrap(); - let g_w = get_image_width(locked_server.guest_image); - let g_h = get_image_height(locked_server.guest_image); - set_area_dirty(&mut locked_server.guest_dirty_bitmap, x, y, w, h, g_w, g_h); + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + let g_w = get_image_width(locked_vnc_surface.guest_image); + let g_h = get_image_height(locked_vnc_surface.guest_image); + set_area_dirty( + &mut locked_vnc_surface.guest_dirty_bitmap, + x, + y, + w, + h, + g_w, + g_h, + ); + drop(locked_vnc_surface); + vnc_refresh_notify(&server); } fn vnc_get_display_update_interval() -> u32 { @@ -249,9 +252,9 @@ fn vnc_get_display_update_interval() -> u32 { return DISPLAY_UPDATE_INTERVAL_DEFAULT; } let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let locked_server = server.lock().unwrap(); + let update_interval = *server.update_interval.lock().unwrap(); - locked_server.update_interval + update_interval } pub fn vnc_loop_update_display(x: i32, y: i32, width: i32, height: i32) { @@ -281,26 +284,22 @@ fn vnc_height(height: i32) -> i32 { cmp::min(MAX_WINDOW_HEIGHT as i32, height) } -/// Update Client image -pub fn update_client_surface(server: &mut VncServer) { - unref_pixman_image(server.server_image); - server.server_image = ptr::null_mut(); +/// Update server image +pub fn update_server_surface(server: &Arc) { + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + unref_pixman_image(locked_vnc_surface.server_image); + locked_vnc_surface.server_image = ptr::null_mut(); // Server image changes, clear the task queue. VNC_RECT_INFO.lock().unwrap().clear(); - if server.clients.is_empty() { + if server.clients.lock().unwrap().is_empty() { return; } - for client in server.clients.values_mut() { - client.lock().unwrap().server_image = ptr::null_mut(); - } - - let g_width = get_image_width(server.guest_image); - let g_height = get_image_height(server.guest_image); + let g_width = get_image_width(locked_vnc_surface.guest_image); + let g_height = get_image_height(locked_vnc_surface.guest_image); let width = vnc_width(g_width); let height = vnc_height(g_height); - server.true_width = cmp::min(MAX_WINDOW_WIDTH as i32, g_width); - server.server_image = unsafe { + locked_vnc_surface.server_image = unsafe { pixman_image_create_bits( pixman_format_code_t::PIXMAN_x8r8g8b8, width, @@ -309,14 +308,10 @@ pub fn update_client_surface(server: &mut VncServer) { 0, ) }; - for client in server.clients.values_mut() { - client.lock().unwrap().server_image = server.server_image; - client.lock().unwrap().width = width; - client.lock().unwrap().height = height; - } - server.guest_dirty_bitmap.clear_all(); + + locked_vnc_surface.guest_dirty_bitmap.clear_all(); set_area_dirty( - &mut server.guest_dirty_bitmap, + &mut locked_vnc_surface.guest_dirty_bitmap, 0, 0, width, @@ -327,14 +322,12 @@ pub fn update_client_surface(server: &mut VncServer) { } /// Check if the suface for VncClient is need update -fn check_surface(surface: &DisplaySurface) -> bool { - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let locked_server = server.lock().unwrap(); +fn check_surface(locked_vnc_surface: &mut VncSurface, surface: &DisplaySurface) -> bool { if surface.image.is_null() - || locked_server.server_image.is_null() - || locked_server.guest_format != surface.format - || get_image_width(locked_server.server_image) != get_image_width(surface.image) - || get_image_height(locked_server.server_image) != get_image_height(surface.image) + || locked_vnc_surface.server_image.is_null() + || locked_vnc_surface.guest_format != surface.format + || get_image_width(locked_vnc_surface.server_image) != get_image_width(surface.image) + || get_image_height(locked_vnc_surface.server_image) != get_image_height(surface.image) { return true; } @@ -532,20 +525,20 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { if VNC_SERVERS.lock().unwrap().is_empty() { return; } - let need_resize = check_surface(surface); let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_server = server.lock().unwrap(); - unref_pixman_image(locked_server.guest_image); + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + let need_resize = check_surface(&mut locked_vnc_surface, surface); + unref_pixman_image(locked_vnc_surface.guest_image); // Vnc_pixman_image_ref - locked_server.guest_image = unsafe { pixman_image_ref(surface.image) }; - locked_server.guest_format = surface.format; + locked_vnc_surface.guest_image = unsafe { pixman_image_ref(surface.image) }; + locked_vnc_surface.guest_format = surface.format; - let guest_width: i32 = get_image_width(locked_server.guest_image); - let guest_height: i32 = get_image_height(locked_server.guest_image); + let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); + let guest_height: i32 = get_image_height(locked_vnc_surface.guest_image); if !need_resize { set_area_dirty( - &mut locked_server.guest_dirty_bitmap, + &mut locked_vnc_surface.guest_dirty_bitmap, 0, 0, guest_width, @@ -553,20 +546,14 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { guest_width, guest_height, ); + vnc_refresh_notify(&server); return; } - update_client_surface(&mut locked_server); - // Cursor. - let mut cursor: DisplayMouse = DisplayMouse::default(); - let mut mask: Vec = Vec::new(); - if let Some(c) = &locked_server.cursor { - cursor = c.clone(); - } - if let Some(m) = &locked_server.mask { - mask = m.clone(); - } + drop(locked_vnc_surface); + update_server_surface(&server); - for client in locked_server.clients.values_mut() { + let mut locked_clients = server.clients.lock().unwrap(); + for client in locked_clients.values_mut() { let width = vnc_width(guest_width); let height = vnc_height(guest_height); let mut locked_client = client.lock().unwrap(); @@ -575,9 +562,7 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { // Desktop_resize. locked_client.desktop_resize(); // Cursor define. - if !cursor.data.is_empty() { - display_cursor_define(&mut locked_client, &mut cursor, &mut mask); - } + display_cursor_define(&mut locked_client, &server); locked_client.dirty_bitmap.clear_all(); set_area_dirty( &mut locked_client.dirty_bitmap, @@ -589,6 +574,7 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { guest_height, ); } + vnc_refresh_notify(&server); } pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { @@ -625,22 +611,42 @@ pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { } } - server.lock().unwrap().cursor = Some(cursor.clone()); - server.lock().unwrap().mask = Some(mask.clone()); + server.vnc_cursor.lock().unwrap().cursor = Some(cursor.clone()); + server.vnc_cursor.lock().unwrap().mask = Some(mask.clone()); + let mut locked_clients = server.clients.lock().unwrap(); // Send the framebuff for each client. - for client in server.lock().unwrap().clients.values_mut() { - display_cursor_define(&mut client.lock().unwrap(), cursor, &mut mask); + for client in locked_clients.values_mut() { + let mut locked_client = client.lock().unwrap(); + display_cursor_define(&mut locked_client, &server); } } /// Send framebuf of mouse to the client. -pub fn display_cursor_define( - client: &mut VncClient, - cursor: &mut DisplayMouse, - mask: &mut Vec, -) { - if cursor.data.len() != ((cursor.width * cursor.height) as usize) * bytes_per_pixel() { +pub fn display_cursor_define(client: &mut VncClient, server: &Arc) { + let mut cursor: DisplayMouse; + let mut mask: Vec; + let locked_cursor = server.vnc_cursor.lock().unwrap(); + match &locked_cursor.cursor { + Some(c) => { + cursor = c.clone(); + } + None => { + return; + } + } + match &locked_cursor.mask { + Some(m) => { + mask = m.clone(); + } + None => { + return; + } + } + drop(locked_cursor); + if cursor.data.is_empty() + || cursor.data.len() != ((cursor.width * cursor.height) as usize) * bytes_per_pixel() + { return; } let mut buf = Vec::new(); @@ -679,14 +685,11 @@ pub fn display_cursor_define( let data_size = cursor.width * cursor.height * client.client_dpm.pf.pixel_bytes as u32; let data_ptr = cursor.data.as_ptr() as *mut u8; write_pixel(data_ptr, data_size as usize, &client.client_dpm, &mut buf); - buf.append(mask); + buf.append(&mut mask); client.write_msg(&buf); } } -pub static VNC_SERVERS: Lazy>>>> = - Lazy::new(|| Mutex::new(Vec::new())); +pub static VNC_SERVERS: Lazy>>> = Lazy::new(|| Mutex::new(Vec::new())); pub static VNC_RECT_INFO: Lazy>>> = Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); -pub static REFRESH_EVT: Lazy>> = - Lazy::new(|| Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()))); -- Gitee From 04002c677ad24c2f568da9e06be4c74678e09408 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 9 Nov 2022 16:17:41 +0800 Subject: [PATCH 0521/2187] VNC: Split the VncClient struct. Split the data and event from VncClient struct and add ClientIoHandler to handle callback events. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 135 +++-- vnc/src/client.rs | 1248 ++++++++++++++++++++++++------------------- vnc/src/error.rs | 2 + vnc/src/input.rs | 27 +- vnc/src/server.rs | 70 +-- vnc/src/vencrypt.rs | 40 +- vnc/src/vnc.rs | 122 ++--- 7 files changed, 912 insertions(+), 732 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 8050ba4fc..0886b5b92 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -10,7 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{client::VncClient, VncError}; +use crate::{ + client::{ClientIoHandler, APP_NAME}, + VncError, +}; use anyhow::{anyhow, Result}; use libc::{c_char, c_int, c_uint, c_void}; use log::{error, info}; @@ -27,8 +30,6 @@ use util::byte_code::ByteCode; /// Vnc Service. const SERVICE: &str = "vnc"; -/// Saslauthd service can fetch the configuration in the /etc/sasl2/${APP_NAME}.conf. -const APP_NAME: &str = "stratovirt"; const MECHNAME_MAX_LEN: u32 = 100; const MECHNAME_MIN_LEN: u32 = 1; const SASL_DATA_MAX_LEN: u32 = 1024 * 1024; @@ -58,13 +59,24 @@ pub enum SubAuthState { VncAuthVencryptTlssasl = 264, } -/// Struct of sasl authentiation. +/// Configuration for authentication. +/// Identity: authentication user. #[derive(Debug, Clone)] pub struct SaslAuth { + pub identity: String, +} + +impl SaslAuth { + pub fn new(identity: String) -> Self { + SaslAuth { identity } + } +} + +/// Struct of sasl authentiation. +#[derive(Debug, Clone)] +pub struct SaslConfig { /// State of sasl connection . pub sasl_conn: *mut sasl_conn_t, - /// Identity user. - pub identity: String, /// Mech list server support. pub mech_list: String, /// Authentication mechanism currently in use. @@ -77,11 +89,10 @@ pub struct SaslAuth { pub run_ssf: u32, } -impl SaslAuth { - pub fn default() -> Self { - SaslAuth { +impl Default for SaslConfig { + fn default() -> Self { + SaslConfig { sasl_conn: ptr::null_mut() as *mut sasl_conn_t, - identity: String::new(), mech_list: String::new(), mech_name: String::new(), sasl_stage: SaslStage::SaslServerStart, @@ -98,10 +109,10 @@ pub enum SaslStage { SaslServerStep, } -impl VncClient { +impl ClientIoHandler { /// Get length of mechname send form client. pub fn get_mechname_length(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); let len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]); if len > MECHNAME_MAX_LEN { @@ -117,7 +128,7 @@ impl VncClient { )))); } - self.update_event_handler(len as usize, VncClient::get_sasl_mechname); + self.update_event_handler(len as usize, ClientIoHandler::get_sasl_mechname); Ok(()) } @@ -143,30 +154,32 @@ impl VncClient { /// Get authentication mechanism supported by client. pub fn get_sasl_mechname(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); - let mech_name = String::from_utf8_lossy(buf).to_string(); + let buf = self.read_incoming_msg(); + let mech_name = String::from_utf8_lossy(&buf).to_string(); - let mech_list: Vec<&str> = self.sasl.mech_list.split(',').collect(); + let mut locked_security = self.server.security_type.lock().unwrap(); + let mech_list: Vec<&str> = locked_security.saslconfig.mech_list.split(',').collect(); for mech in mech_list { if mech_name == *mech { - self.sasl.mech_name = mech_name; + locked_security.saslconfig.mech_name = mech_name; break; } } // Unsupported mechanism. - if self.sasl.mech_name.is_empty() { + if locked_security.saslconfig.mech_name.is_empty() { return Err(anyhow!(VncError::AuthFailed( "Unsupported mechanism".to_string() ))); } + drop(locked_security); - self.update_event_handler(4, VncClient::get_authmessage_length); + self.update_event_handler(4, ClientIoHandler::get_authmessage_length); Ok(()) } /// Length of client authentication message. pub fn get_authmessage_length(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); let buf = [buf[0], buf[1], buf[2], buf[3]]; let len = u32::from_be_bytes(buf); @@ -180,14 +193,14 @@ impl VncClient { if len == 0 { return self.client_sasl_auth(); } - self.update_event_handler(len as usize, VncClient::client_sasl_auth); + self.update_event_handler(len as usize, ClientIoHandler::client_sasl_auth); Ok(()) } /// Receive the authentication information from client and return the result. pub fn client_sasl_auth(&mut self) -> Result<()> { info!("Sasl Authentication"); - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); let mut client_data = buf.to_vec(); let mut client_len: c_uint = 0; @@ -196,16 +209,18 @@ impl VncClient { client_data[self.expect - 1] = 0_u8; } + let server = self.server.clone(); + let mut locked_security = server.security_type.lock().unwrap(); let err: c_int; let mut serverout: *const c_char = ptr::null_mut(); let mut serverout_len: c_uint = 0; - let mech_name = CString::new(self.sasl.mech_name.as_str()).unwrap(); + let mech_name = CString::new(locked_security.saslconfig.mech_name.as_str()).unwrap(); // Start authentication. - if self.sasl.sasl_stage == SaslStage::SaslServerStart { + if locked_security.saslconfig.sasl_stage == SaslStage::SaslServerStart { unsafe { err = sasl_server_start( - self.sasl.sasl_conn, + locked_security.saslconfig.sasl_conn, mech_name.as_ptr(), client_data.as_ptr() as *const c_char, client_len, @@ -216,7 +231,7 @@ impl VncClient { } else { unsafe { err = sasl_server_step( - self.sasl.sasl_conn, + locked_security.saslconfig.sasl_conn, client_data.as_ptr() as *const c_char, client_len, &mut serverout, @@ -226,12 +241,12 @@ impl VncClient { } if err != SASL_OK && err != SASL_CONTINUE { - unsafe { sasl_dispose(&mut self.sasl.sasl_conn) } + unsafe { sasl_dispose(&mut locked_security.saslconfig.sasl_conn) } error!("Auth failed!"); return Err(anyhow!(VncError::AuthFailed("Auth failed!".to_string()))); } if serverout_len > SASL_DATA_MAX_LEN { - unsafe { sasl_dispose(&mut self.sasl.sasl_conn) } + unsafe { sasl_dispose(&mut locked_security.saslconfig.sasl_conn) } error!("SASL data too long"); return Err(anyhow!(VncError::AuthFailed( "SASL data too long".to_string() @@ -254,11 +269,14 @@ impl VncClient { } else if err == SASL_CONTINUE { buf.append(&mut (0_u8).as_bytes().to_vec()); } + drop(locked_security); if err == SASL_CONTINUE { // Authentication continue. - self.sasl.sasl_stage = SaslStage::SaslServerStep; - self.update_event_handler(4, VncClient::get_authmessage_length); + let mut locked_security = server.security_type.lock().unwrap(); + locked_security.saslconfig.sasl_stage = SaslStage::SaslServerStep; + self.update_event_handler(4, ClientIoHandler::get_authmessage_length); + drop(locked_security); return Ok(()); } else { if let Err(err) = self.sasl_check_ssf() { @@ -279,7 +297,7 @@ impl VncClient { } self.write_msg(&buf); - self.update_event_handler(1, VncClient::handle_client_init); + self.update_event_handler(1, ClientIoHandler::handle_client_init); Ok(()) } @@ -314,6 +332,7 @@ impl VncClient { err )))); } + let mut saslconfig = SaslConfig::default(); unsafe { err = sasl_server_new( service.as_ptr(), @@ -323,7 +342,7 @@ impl VncClient { remote_addr.as_ptr(), ptr::null_mut(), SASL_SUCCESS_DATA, - &mut self.sasl.sasl_conn, + &mut saslconfig.sasl_conn, ); } if err != SASL_OK { @@ -333,6 +352,7 @@ impl VncClient { err )))); } + self.server.security_type.lock().unwrap().saslconfig = saslconfig; Ok(()) } @@ -343,9 +363,10 @@ impl VncClient { let mut err: c_int; let ssf: sasl_ssf_t = 256; let ssf = &ssf as *const sasl_ssf_t; + let locked_security = self.server.security_type.lock().unwrap(); unsafe { err = sasl_setprop( - self.sasl.sasl_conn, + locked_security.saslconfig.sasl_conn, SASL_SSF_EXTERNAL as i32, ssf as *const c_void, ); @@ -373,7 +394,7 @@ impl VncClient { let props = &saslprops as *const sasl_security_properties_t; unsafe { err = sasl_setprop( - self.sasl.sasl_conn, + locked_security.saslconfig.sasl_conn, SASL_SEC_PROPS.try_into().unwrap(), props as *const c_void, ); @@ -397,9 +418,10 @@ impl VncClient { let sep = CString::new(",").unwrap(); let suffix = CString::new("").unwrap(); let mut mechlist: *const c_char = ptr::null_mut(); + let mut locked_security = self.server.security_type.lock().unwrap(); unsafe { err = sasl_listmech( - self.sasl.sasl_conn, + locked_security.saslconfig.sasl_conn, ptr::null_mut(), prefix.as_ptr(), sep.as_ptr(), @@ -416,11 +438,12 @@ impl VncClient { ))); } let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) }; - self.sasl.mech_list = String::from(mech_list.to_str().unwrap()); + locked_security.saslconfig.mech_list = String::from(mech_list.to_str().unwrap()); let mut buf = Vec::new(); - let len = self.sasl.mech_list.len(); + let len = locked_security.saslconfig.mech_list.len(); buf.append(&mut (len as u32).to_be_bytes().to_vec()); - buf.append(&mut self.sasl.mech_list.as_bytes().to_vec()); + buf.append(&mut locked_security.saslconfig.mech_list.as_bytes().to_vec()); + drop(locked_security); self.write_msg(&buf); Ok(()) @@ -428,12 +451,20 @@ impl VncClient { /// Check whether the ssf layer of sasl meets the strength requirements. fn sasl_check_ssf(&mut self) -> Result<()> { - if !self.sasl.want_ssf { + let server = self.server.clone(); + let mut locked_security = server.security_type.lock().unwrap(); + if !locked_security.saslconfig.want_ssf { return Ok(()); } let err: c_int; let mut val: *const c_void = ptr::null_mut(); - unsafe { err = sasl_getprop(self.sasl.sasl_conn, SASL_SSF as c_int, &mut val) } + unsafe { + err = sasl_getprop( + locked_security.saslconfig.sasl_conn, + SASL_SSF as c_int, + &mut val, + ) + } if err != SASL_OK { error!("sasl_getprop: internal error"); return Err(anyhow!(VncError::AuthFailed(String::from( @@ -449,14 +480,23 @@ impl VncClient { )))); } - self.sasl.run_ssf = 1; + locked_security.saslconfig.run_ssf = 1; + drop(locked_security); Ok(()) } /// Check username. fn sasl_check_authz(&mut self) -> Result<()> { + let locked_security = self.server.security_type.lock().unwrap(); let mut val: *const c_void = ptr::null_mut(); - let err = unsafe { sasl_getprop(self.sasl.sasl_conn, SASL_USERNAME as c_int, &mut val) }; + let err = unsafe { + sasl_getprop( + locked_security.saslconfig.sasl_conn, + SASL_USERNAME as c_int, + &mut val, + ) + }; + drop(locked_security); if err != SASL_OK { return Err(anyhow!(VncError::AuthFailed(String::from( "Cannot fetch SASL username" @@ -470,11 +510,20 @@ impl VncClient { let username = unsafe { CStr::from_ptr(val as *const c_char) }; let username = String::from(username.to_str().unwrap()); - if self.sasl.identity != username { + let server = self.server.clone(); + let locked_security = server.security_type.lock().unwrap(); + if let Some(saslauth) = &locked_security.saslauth { + if saslauth.identity != username { + return Err(anyhow!(VncError::AuthFailed(String::from( + "No SASL username set" + )))); + } + } else { return Err(anyhow!(VncError::AuthFailed(String::from( "No SASL username set" )))); } + drop(locked_security); Ok(()) } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 6d8b23053..94b7dc8d6 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -10,22 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::VncError; use crate::{ - auth::SaslAuth, - auth::{AuthState, SubAuthState}, - pixman::{get_image_height, get_image_width, PixelFormat}, + auth::AuthState, + pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, round_up_div, - server::VncServer, + server::{vnc_refresh_notify, VncServer}, utils::BuffPool, vnc::{ - display_cursor_define, framebuffer_upadate, set_area_dirty, BIT_PER_BYTE, DIRTY_PIXELS_NUM, - DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, VNC_SERVERS, + framebuffer_upadate, set_area_dirty, write_pixel, DisplayMouse, BIT_PER_BYTE, + DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, }, + VncError, }; use anyhow::{anyhow, Result}; -use log::{error, info}; -use machine_manager::event_loop::EventLoop; +use log::error; use rustls::ServerConnection; use sscanf::scanf; use std::{ @@ -37,11 +35,12 @@ use std::{ }; use util::{ bitmap::Bitmap, - loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}, - pixman::pixman_image_t, + loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}, }; use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; +pub const APP_NAME: &str = "stratovirt"; const MAX_RECVBUF_LEN: usize = 1024; const MAX_SEND_LEN: usize = 64 * 1024; const NUM_OF_COLORMAP: u16 = 256; @@ -112,6 +111,7 @@ impl From for ClientMsg { } /// RFB protocol version. +#[derive(Clone)] pub struct VncVersion { pub major: u16, pub minor: u16, @@ -183,29 +183,17 @@ impl Default for DisplayMode { unsafe impl Send for RectInfo {} pub struct RectInfo { - /// TcpStream address. - pub addr: String, + /// Vnc client state. + pub client: Arc, /// Dirty area of image. pub rects: Vec, - /// Width of Client image. - pub width: i32, - /// Height of Client image. - pub height: i32, - /// Output mod information of client display. - pub dpm: DisplayMode, - /// Image - pub image: *mut pixman_image_t, } impl RectInfo { - pub fn new(client: &VncClient, rects: Vec) -> Self { + pub fn new(client: &Arc, rects: Vec) -> Self { RectInfo { - addr: client.addr.clone(), + client: client.clone(), rects, - width: client.width, - height: client.height, - dpm: client.client_dpm.clone(), - image: client.server_image, } } } @@ -217,12 +205,8 @@ impl Clone for RectInfo { rects.push(rect.clone()); } Self { - addr: self.addr.clone(), + client: self.client.clone(), rects, - width: self.width, - height: self.height, - dpm: self.dpm.clone(), - image: self.image, } } @@ -231,213 +215,178 @@ impl Clone for RectInfo { } } -/// VncClient struct to record the information of connnection. -pub struct VncClient { - /// TcpStream connected with client. - pub stream: TcpStream, - /// TcpStream receive buffer. - pub buffpool: BuffPool, - /// Size of buff in next handle. - pub expect: usize, +/// The connection state of vnc client. +pub struct ConnState { /// Connection status. pub dis_conn: bool, + /// State flags whether the image needs to be updated for the client. + update_state: UpdateState, /// RFB protocol version. pub version: VncVersion, - /// Auth type. - auth: AuthState, - /// SubAuth type. - pub subauth: SubAuthState, - /// Message handler. - pub handle_msg: fn(&mut VncClient) -> Result<()>, - /// The function handling the connection. - pub handlers: Vec>>>, - /// Pointer to VncServer. - pub server: Arc, + /// Vnc display feature. + feature: i32, +} + +impl Default for ConnState { + fn default() -> Self { + ConnState { + dis_conn: false, + update_state: UpdateState::No, + version: VncVersion::default(), + feature: 0, + } + } +} + +impl ConnState { + pub fn is_disconnect(&mut self) -> bool { + self.dis_conn + } + + pub fn has_feature(&self, feature: VncFeatures) -> bool { + self.feature & (1 << feature as usize) != 0 + } +} + +/// Struct to record the state with the vnc client. +pub struct ClientState { + /// Write event fd. + write_fd: Arc>, + /// TcpStream receive buffer. + pub in_buffer: Arc>, + /// TcpStream write buffer. + pub out_buffer: Arc>, + /// Output mode information of client display. + pub client_dpm: Arc>, + /// The connection state of vnc client. + pub conn_state: Arc>, + /// Identify the image update area. + pub dirty_bitmap: Arc>>, +} + +impl Default for ClientState { + fn default() -> Self { + ClientState { + write_fd: Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), + in_buffer: Arc::new(Mutex::new(BuffPool::new())), + out_buffer: Arc::new(Mutex::new(BuffPool::new())), + client_dpm: Arc::new(Mutex::new(DisplayMode::default())), + conn_state: Arc::new(Mutex::new(ConnState::default())), + dirty_bitmap: Arc::new(Mutex::new(Bitmap::::new( + MAX_WINDOW_HEIGHT as usize + * round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) as usize, + ))), + } + } +} + +/// Handle the message with vnc client. +pub struct ClientIoHandler { + /// TcpStream connected with client. + pub stream: TcpStream, /// Tls server connection. pub tls_conn: Option, - /// Configuration for sasl authentication. - pub sasl: SaslAuth, - /// State flags whether the image needs to be updated for the client. - state: UpdateState, - /// Identify the image update area. - pub dirty_bitmap: Bitmap, - /// Number of dirty data. - dirty_num: i32, - /// Image pointer. - pub server_image: *mut pixman_image_t, + /// Message handler. + pub msg_handler: fn(&mut ClientIoHandler) -> Result<()>, + /// Size of buff in next handle. + pub expect: usize, + /// State with vnc client. + pub client: Arc, + /// Configure for vnc server. + pub server: Arc, + /// Disconnect event fd. + pub disconn_evt: EventFd, /// Tcp listening address. pub addr: String, - /// Image width. - pub width: i32, - /// Image height. - pub height: i32, - /// Display output mod information of client. - pub client_dpm: DisplayMode, - /// Image display feature. - feature: i32, } -impl VncClient { +impl ClientIoHandler { pub fn new( stream: TcpStream, - addr: String, - auth: AuthState, - subauth: SubAuthState, + client: Arc, server: Arc, - image: *mut pixman_image_t, + addr: String, ) -> Self { - VncClient { + ClientIoHandler { stream, - buffpool: BuffPool::new(), + tls_conn: None, + msg_handler: ClientIoHandler::handle_version, expect: 12, - dis_conn: false, - version: VncVersion::default(), - auth, - subauth, - handle_msg: VncClient::handle_version, - handlers: Vec::new(), + client, server, - tls_conn: None, - sasl: SaslAuth::default(), - state: UpdateState::No, - dirty_bitmap: Bitmap::::new( - MAX_WINDOW_HEIGHT as usize - * round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) as usize, - ), - dirty_num: 0, - server_image: image, + disconn_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), addr, - width: 0, - height: 0, - client_dpm: DisplayMode::default(), - feature: 0, - } - } - - /// Whether the client's image data needs to be updated. - pub fn is_need_update(&self) -> bool { - match self.state { - UpdateState::No => false, - UpdateState::Incremental => { - // throttle_output_offset - true - } - UpdateState::Force => { - // force_update_offset - true - } } } +} - /// Generate the data that needs to be sent. - /// Add to send queue - pub fn get_rects(&mut self, dirty_num: i32) -> i32 { - self.dirty_num += dirty_num; - if !self.is_need_update() || (self.dirty_num == 0 && self.state != UpdateState::Force) { - return 0; +impl ClientIoHandler { + fn client_handle_read(&mut self) -> Result<(), anyhow::Error> { + // Read message from tcpstream. + if let Err(e) = self.read_msg() { + return Err(e); } - let mut num_rects = 0; - let mut x: u64; - let mut y: u64 = 0; - let mut h: u64; - let mut x2: u64; - let mut rects = Vec::new(); - let bpl = self.dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; - - let height = get_image_height(self.server_image) as u64; - let width = get_image_width(self.server_image) as u64; - loop { - // Find the first non-zero bit in dirty bitmap. - let offset = self.dirty_bitmap.find_next_bit(y as usize * bpl).unwrap() as u64; - if offset >= height as u64 * bpl as u64 { - break; - } - - x = offset % bpl as u64; - y = offset / bpl as u64; - // Find value in one line to the end. - x2 = self.dirty_bitmap.find_next_zero(offset as usize).unwrap() as u64 % bpl as u64; - let mut i = y; - while i < height { - if !self - .dirty_bitmap - .contain((i * bpl as u64 + x) as usize) - .unwrap() - { - break; - } - for j in x..x2 { - self.dirty_bitmap - .clear((i * bpl as u64 + j) as usize) - .unwrap(); - } - i += 1; + let client = self.client.clone(); + while client.in_buffer.lock().unwrap().len() >= self.expect { + if let Err(e) = (self.msg_handler)(self) { + return Err(e); } - h = i - y; - x2 = cmp::min(x2, width / DIRTY_PIXELS_NUM as u64); - if x2 > x as u64 { - rects.push(Rectangle::new( - (x * DIRTY_PIXELS_NUM as u64) as i32, - y as i32, - ((x2 - x) * DIRTY_PIXELS_NUM as u64) as i32, - h as i32, - )); - num_rects += 1; + if self.client.conn_state.lock().unwrap().dis_conn { + return Err(anyhow!(VncError::Disconnection)); } - if x == 0 && x2 == width / DIRTY_PIXELS_NUM as u64 { - y += h; - if y == height { - break; - } + if self.expect == 0 { + break; } } - VNC_RECT_INFO - .lock() - .unwrap() - .push(RectInfo::new(self, rects)); - - self.state = UpdateState::No; - self.dirty_num = 0; - - num_rects + Ok(()) } - /// Modify event notifiers to event loop - /// - /// # Arguments - /// - /// * `op` - Notifier operation. - /// * `idx` - Idx of event in server.handlers - pub fn modify_event(&mut self, op: NotifierOperation, idx: usize) -> Result<()> { - let mut handlers = Vec::new(); + fn client_handle_write(&mut self) { + let client = self.client.clone(); + let mut locked_buffer = client.out_buffer.lock().unwrap(); + let len = locked_buffer.len(); + let buf = locked_buffer.read_front(len); + self.write_msg(buf); + locked_buffer.remov_front(len); + drop(locked_buffer); + } - if let NotifierOperation::Modify = op { - if self.handlers.len() <= idx { - return Ok(()); + /// Read buf from stream, return the size. + fn read_msg(&mut self) -> Result { + let mut buf = Vec::new(); + let mut len: usize = 0; + if self.tls_conn.is_none() { + match self.read_plain_msg(&mut buf) { + Ok(n) => len = n, + Err(e) => return Err(e), } - handlers.push(self.handlers[idx].clone()); } + if let Some(tc) = &self.tls_conn { + if tc.is_handshaking() { + return Ok(0_usize); + } + } + if self.tls_conn.is_some() { + match self.read_tls_msg(&mut buf) { + Ok(n) => len = n, + Err(e) => return Err(e), + } + } + self.client + .in_buffer + .lock() + .unwrap() + .read(&mut buf[..len].to_vec()); - EventLoop::update_event( - vec![EventNotifier::new( - op, - self.stream.as_raw_fd(), - None, - EventSet::IN | EventSet::READ_HANG_UP, - handlers, - )], - None, - )?; - - Ok(()) + Ok(len) } // Read from vencrypt channel. - pub fn read_tls_msg(&mut self, buf: &mut Vec) -> Result { + fn read_tls_msg(&mut self, buf: &mut Vec) -> Result { let mut len = 0_usize; if self.tls_conn.is_none() { return Ok(0_usize); @@ -474,21 +423,27 @@ impl VncClient { } /// Read plain txt. - pub fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { + fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { let mut len = 0_usize; buf.resize(MAX_RECVBUF_LEN, 0u8); match self.stream.read(buf) { - Ok(ret) => { - len = ret; - } - Err(e) => { - error!("read msg error: {:?}", e); - } + Ok(ret) => len = ret, + Err(e) => error!("read msg error: {:?}", e), } Ok(len) } + /// Write buf to stream + /// Choose different channel according to whether or not to encrypt + pub fn write_msg(&mut self, buf: &[u8]) { + if self.tls_conn.is_none() { + self.write_plain_msg(buf) + } else { + self.write_tls_msg(buf) + } + } + // Send vencrypt message. fn write_tls_msg(&mut self, buf: &[u8]) { let buf_size = buf.len(); @@ -509,7 +464,10 @@ impl VncClient { error!("write msg error: {:?}", e); return; } - vnc_write_tls_message(tc, &mut self.stream); + if let Err(_e) = vnc_write_tls_message(tc, &mut self.stream) { + self.client.conn_state.lock().unwrap().dis_conn = true; + return; + } offset = next; } } @@ -530,6 +488,7 @@ impl VncClient { continue; } else { error!("write msg error: {:?}", e); + self.client.conn_state.lock().unwrap().dis_conn = true; return; } } @@ -541,47 +500,10 @@ impl VncClient { } } - /// Write buf to stream - /// Choose different channel according to whether or not to encrypt - /// - /// # Arguments - /// * `buf` - Data to be send. - pub fn write_msg(&mut self, buf: &[u8]) { - if self.tls_conn.is_none() { - self.write_plain_msg(buf) - } else { - self.write_tls_msg(buf) - } - } - - /// Read buf from stream, return the size. - pub fn read_msg(&mut self, buf: &mut Vec) -> Result { - if self.tls_conn.is_none() { - self.read_plain_msg(buf) - } else { - self.read_tls_msg(buf) - } - } - - /// Read buf from tcpstream. - pub fn from_tcpstream_to_buff(&mut self) -> Result<()> { - let mut buf = Vec::new(); - match self.read_msg(&mut buf) { - Ok(len) => { - self.buffpool.read(&mut buf[0..len].to_vec()); - } - Err(e) => { - return Err(e); - } - } - - Ok(()) - } - /// Exchange RFB protocol version with client. fn handle_version(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); - let res = String::from_utf8_lossy(buf); + let buf = self.read_incoming_msg(); + let res = String::from_utf8_lossy(&buf); let ver_str = &res[0..12].to_string(); let ver; match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { @@ -594,27 +516,29 @@ impl VncClient { return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } } - self.version.major = ver.0 as u16; - self.version.minor = ver.1 as u16; - if self.version.major != 3 || ![3, 4, 5, 7, 8].contains(&self.version.minor) { + + let mut version = VncVersion::new(ver.0 as u16, ver.1 as u16); + if version.major != 3 || ![3, 4, 5, 7, 8].contains(&version.minor) { let mut buf = Vec::new(); buf.append(&mut (AuthState::Invalid as u32).to_be_bytes().to_vec()); self.write_msg(&buf); return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } - if [4, 5].contains(&self.version.minor) { - self.version.minor = 3; + if [4, 5].contains(&version.minor) { + version.minor = 3; } + self.client.conn_state.lock().unwrap().version = version; + let auth = self.server.security_type.lock().unwrap().auth; - if self.version.minor == 3 { + if self.client.conn_state.lock().unwrap().version.minor == 3 { error!("Waiting for handle minor=3 ..."); - match self.auth { + match auth { AuthState::No => { let mut buf = Vec::new(); buf.append(&mut (AuthState::No as u32).to_be_bytes().to_vec()); self.write_msg(&buf); - self.update_event_handler(1, VncClient::handle_client_init); + self.update_event_handler(1, ClientIoHandler::handle_client_init); } _ => { self.auth_failed("Unsupported auth method"); @@ -626,43 +550,58 @@ impl VncClient { } else { let mut buf = [0u8; 2]; buf[0] = 1; // Number of security types. - buf[1] = self.auth as u8; + buf[1] = auth as u8; self.write_msg(&buf); - self.update_event_handler(1, VncClient::handle_auth); + self.update_event_handler(1, ClientIoHandler::handle_auth); } Ok(()) } - /// Invalid authentication, send 1 to reject. - fn auth_failed(&mut self, msg: &str) { - let auth_rej: u8 = 1; - let mut buf: Vec = vec![1u8]; - buf.append(&mut (auth_rej as u32).to_be_bytes().to_vec()); - if self.version.minor >= 8 { - let err_msg = msg; - buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); - buf.append(&mut err_msg.as_bytes().to_vec()); + /// Initialize the connection of vnc client. + pub fn handle_client_init(&mut self) -> Result<()> { + let mut buf = Vec::new(); + // Send server framebuffer info. + let locked_surface = self.server.vnc_surface.lock().unwrap(); + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); + drop(locked_surface); + if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) + || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) + { + error!("Invalid Image Size!"); + return Err(anyhow!(VncError::InvalidImageSize)); } + buf.append(&mut (width as u16).to_be_bytes().to_vec()); + buf.append(&mut (height as u16).to_be_bytes().to_vec()); + let client = self.client.clone(); + pixel_format_message(&client, &mut buf); + + buf.append(&mut (APP_NAME.to_string().len() as u32).to_be_bytes().to_vec()); + buf.append(&mut APP_NAME.to_string().as_bytes().to_vec()); self.write_msg(&buf); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + Ok(()) } /// Authentication fn handle_auth(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); + let auth = self.server.security_type.lock().unwrap().auth; + let version = self.client.conn_state.lock().unwrap().version.clone(); - if buf[0] != self.auth as u8 { + if buf[0] != auth as u8 { self.auth_failed("Authentication failed"); error!("handle_auth"); return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); } - match self.auth { + match auth { AuthState::No => { - if self.version.minor >= 8 { + if version.minor >= 8 { let buf = [0u8; 4]; self.write_msg(&buf); } - self.update_event_handler(1, VncClient::handle_client_init); + self.update_event_handler(1, ClientIoHandler::handle_client_init); } AuthState::Vencrypt => { // Send VeNCrypt version 0.2. @@ -671,7 +610,7 @@ impl VncClient { buf[1] = 2_u8; self.write_msg(&buf); - self.update_event_handler(2, VncClient::client_vencrypt_init); + self.update_event_handler(2, ClientIoHandler::client_vencrypt_init); } _ => { self.auth_failed("Unhandled auth method"); @@ -682,68 +621,33 @@ impl VncClient { Ok(()) } - /// Set color depth for client. - pub fn set_color_depth(&mut self) { - if self.has_feature(VncFeatures::VncFeatureWmvi) { - let mut buf = Vec::new(); - buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); - buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Padding. - buf.append(&mut (1_u16).to_be_bytes().to_vec()); // Number of pixel block. - framebuffer_upadate(0, 0, self.width, self.height, ENCODING_WMVI, &mut buf); - buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); - self.pixel_format_message(&mut buf); - self.write_msg(&buf); - } else if !self.client_dpm.pf.is_default_pixel_format() { - self.client_dpm.convert = true; - } - } - - /// Set pixformat for client. - pub fn pixel_format_message(&mut self, buf: &mut Vec) { - self.client_dpm.pf.init_pixelformat(); - buf.append(&mut (self.client_dpm.pf.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. - buf.append(&mut (self.client_dpm.pf.depth as u8).to_be_bytes().to_vec()); // Depth. - buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Big-endian flag. - buf.append(&mut (1_u8).to_be_bytes().to_vec()); // True-color flag. - buf.append(&mut (self.client_dpm.pf.red.max as u16).to_be_bytes().to_vec()); // Red max. - buf.append(&mut (self.client_dpm.pf.green.max as u16).to_be_bytes().to_vec()); // Green max. - buf.append(&mut (self.client_dpm.pf.blue.max as u16).to_be_bytes().to_vec()); // Blue max. - buf.append(&mut (self.client_dpm.pf.red.shift as u8).to_be_bytes().to_vec()); // Red shift. - buf.append( - &mut (self.client_dpm.pf.green.shift as u8) - .to_be_bytes() - .to_vec(), - ); // Green shift. - buf.append(&mut (self.client_dpm.pf.blue.shift as u8).to_be_bytes().to_vec()); // Blue shift. - buf.append(&mut [0; 3].to_vec()); // Padding. - } - - /// Initialize the connection of vnc client. - pub fn handle_client_init(&mut self) -> Result<()> { - let mut buf = Vec::new(); - // Send server framebuffer info. - self.width = get_image_width(self.server_image); - if self.width < 0 || self.width > MAX_WINDOW_WIDTH as i32 { - error!("Invalid Image Size!"); - return Err(anyhow!(VncError::InvalidImageSize)); - } - buf.append(&mut (self.width as u16).to_be_bytes().to_vec()); - self.height = get_image_height(self.server_image); - if self.height < 0 || self.height > MAX_WINDOW_HEIGHT as i32 { - error!("Invalid Image Size!"); - return Err(anyhow!(VncError::InvalidImageSize)); + /// Process the data sent by the client + pub fn handle_protocol_msg(&mut self) -> Result<()> { + // According to RFB protocol, first byte identifies the event type. + let buf = self.read_incoming_msg(); + match ClientMsg::from(buf[0]) { + ClientMsg::SetPixelFormat => { + return self.set_pixel_format(); + } + ClientMsg::SetEncodings => { + return self.set_encodings(); + } + ClientMsg::FramebufferUpdateRequest => { + self.update_frame_buff(); + } + ClientMsg::KeyEvent => { + self.key_envent(); + } + ClientMsg::PointerEvent => { + self.point_event(); + } + ClientMsg::ClientCutText => { + self.client_cut_event(); + } + _ => { + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + } } - buf.append(&mut (self.height as u16).to_be_bytes().to_vec()); - self.pixel_format_message(&mut buf); - - buf.append( - &mut ("StratoVirt".to_string().len() as u32) - .to_be_bytes() - .to_vec(), - ); - buf.append(&mut "StratoVirt".to_string().as_bytes().to_vec()); - self.write_msg(&buf); - self.update_event_handler(1, VncClient::handle_protocol_msg); Ok(()) } @@ -762,7 +666,7 @@ impl VncClient { // Number of colors. buf.append(&mut (NUM_OF_COLORMAP as u16).to_be_bytes().to_vec()); - let pf = self.client_dpm.pf.clone(); + let pf = self.client.client_dpm.lock().unwrap().pf.clone(); for i in 0..NUM_OF_COLORMAP as u16 { let r = ((i >> pf.red.shift) & pf.red.max as u16) << (16 - pf.red.bits); let g = ((i >> pf.green.shift) & pf.green.max as u16) << (16 - pf.green.bits); @@ -782,7 +686,7 @@ impl VncClient { return Ok(()); } - let buf = self.buffpool.read_front(self.expect).to_vec(); + let buf = self.read_incoming_msg(); let mut bit_per_pixel: u8 = buf[4]; let big_endian_flag = buf[6]; let true_color_flag: u8 = buf[7]; @@ -805,73 +709,45 @@ impl VncClient { // Verify the validity of pixel format. // bit_per_pixel: Bits occupied by each pixel. if ![8, 16, 32].contains(&bit_per_pixel) { - self.dis_conn = true; + self.client.conn_state.lock().unwrap().dis_conn = true; error!("Worng format of bits_per_pixel"); return Err(anyhow!(VncError::ProtocolMessageFailed(String::from( "set pixel format" )))); } - self.client_dpm.pf.red.set_color_info(red_shift, red_max); - self.client_dpm - .pf - .green - .set_color_info(green_shift, green_max); - self.client_dpm.pf.blue.set_color_info(blue_shift, blue_max); - self.client_dpm.pf.pixel_bits = bit_per_pixel; - self.client_dpm.pf.pixel_bytes = bit_per_pixel / BIT_PER_BYTE as u8; + let mut locked_dpm = self.client.client_dpm.lock().unwrap(); + locked_dpm.pf.red.set_color_info(red_shift, red_max); + locked_dpm.pf.green.set_color_info(green_shift, green_max); + locked_dpm.pf.blue.set_color_info(blue_shift, blue_max); + locked_dpm.pf.pixel_bits = bit_per_pixel; + locked_dpm.pf.pixel_bytes = bit_per_pixel / BIT_PER_BYTE as u8; // Standard pixel format, depth is equal to 24. - self.client_dpm.pf.depth = if bit_per_pixel == 32 { + locked_dpm.pf.depth = if bit_per_pixel == 32 { 24 } else { bit_per_pixel }; - self.client_dpm.client_be = big_endian_flag != 0; + locked_dpm.client_be = big_endian_flag != 0; + + if !locked_dpm.pf.is_default_pixel_format() { + locked_dpm.convert = true; + } + drop(locked_dpm); if true_color_flag == 0 { self.send_color_map(); } - if !self.client_dpm.pf.is_default_pixel_format() { - self.client_dpm.convert = true; - } VNC_RECT_INFO.lock().unwrap().clear(); - self.update_event_handler(1, VncClient::handle_protocol_msg); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } - /// Update image for client. - fn update_frame_buff(&mut self) { - if self.expect == 1 { - self.expect = 10; - return; - } - let buf = self.buffpool.read_front(self.expect); - if buf[1] != 0 { - if self.state != UpdateState::Force { - self.state = UpdateState::Incremental; - } - } else { - self.state = UpdateState::Force; - let x = u16::from_be_bytes([buf[2], buf[3]]) as i32; - let y = u16::from_be_bytes([buf[4], buf[5]]) as i32; - let w = u16::from_be_bytes([buf[6], buf[7]]) as i32; - let h = u16::from_be_bytes([buf[8], buf[9]]) as i32; - set_area_dirty( - &mut self.dirty_bitmap, - x, - y, - w, - h, - get_image_width(self.server_image), - get_image_height(self.server_image), - ); - } - self.update_event_handler(1, VncClient::handle_protocol_msg); - } - /// Set encoding. fn set_encodings(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); + let client = self.client.clone(); + let server = self.server.clone(); + let buf = self.read_incoming_msg(); if self.expect == 1 { self.expect = 4; return Ok(()); @@ -888,7 +764,8 @@ impl VncClient { num_encoding = u16::from_be_bytes([buf[2], buf[3]]); } - info!("Set encoding"); + let mut locked_dpm = self.client.client_dpm.lock().unwrap(); + let mut locked_state = self.client.conn_state.lock().unwrap(); while num_encoding > 0 { let offset = (4 * num_encoding) as usize; let enc = i32::from_be_bytes([ @@ -899,51 +776,51 @@ impl VncClient { ]); match enc { ENCODING_RAW => { - self.client_dpm.enc = enc; + locked_dpm.enc = enc; } ENCODING_HEXTILE => { - self.feature |= 1 << VncFeatures::VncFeatureHextile as usize; - self.client_dpm.enc = enc; + locked_state.feature |= 1 << VncFeatures::VncFeatureHextile as usize; + locked_dpm.enc = enc; } ENCODING_TIGHT => { - self.feature |= 1 << VncFeatures::VncFeatureTight as usize; - self.client_dpm.enc = enc; + locked_state.feature |= 1 << VncFeatures::VncFeatureTight as usize; + locked_dpm.enc = enc; } ENCODING_ZLIB => { // ZRLE compress better than ZLIB, so prioritize ZRLE. - if self.feature & (1 << VncFeatures::VncFeatureZrle as usize) == 0 { - self.feature |= 1 << VncFeatures::VncFeatureZlib as usize; - self.client_dpm.enc = enc; + if locked_state.feature & (1 << VncFeatures::VncFeatureZrle as usize) == 0 { + locked_state.feature |= 1 << VncFeatures::VncFeatureZlib as usize; + locked_dpm.enc = enc; } } ENCODING_ZRLE => { - self.feature |= 1 << VncFeatures::VncFeatureZrle as usize; - self.client_dpm.enc = enc; + locked_state.feature |= 1 << VncFeatures::VncFeatureZrle as usize; + locked_dpm.enc = enc; } ENCODING_ZYWRLE => { - self.feature |= 1 << VncFeatures::VncFeatureZywrle as usize; - self.client_dpm.enc = enc; + locked_state.feature |= 1 << VncFeatures::VncFeatureZywrle as usize; + locked_dpm.enc = enc; } ENCODING_DESKTOPRESIZE => { - self.feature |= 1 << VncFeatures::VncFeatureResize as usize; + locked_state.feature |= 1 << VncFeatures::VncFeatureResize as usize; } ENCODING_DESKTOP_RESIZE_EXT => { - self.feature |= 1 << VncFeatures::VncFeatureResizeExt as usize; + locked_state.feature |= 1 << VncFeatures::VncFeatureResizeExt as usize; } ENCODING_POINTER_TYPE_CHANGE => { - self.feature |= 1 << VncFeatures::VncFeaturePointerTypeChange as usize; + locked_state.feature |= 1 << VncFeatures::VncFeaturePointerTypeChange as usize; } ENCODING_RICH_CURSOR => { - self.feature |= 1 << VncFeatures::VncFeatureRichCursor as usize; + locked_state.feature |= 1 << VncFeatures::VncFeatureRichCursor as usize; } ENCODING_ALPHA_CURSOR => { - self.feature |= 1 << VncFeatures::VncFeatureAlphaCursor as usize; + locked_state.feature |= 1 << VncFeatures::VncFeatureAlphaCursor as usize; } ENCODING_WMVI => { - self.feature |= 1 << VncFeatures::VncFeatureWmvi as usize; + locked_state.feature |= 1 << VncFeatures::VncFeatureWmvi as usize; } ENCODING_LED_STATE => { - self.feature |= 1 << VncFeatures::VncFeatureLedState as usize; + locked_state.feature |= 1 << VncFeatures::VncFeatureLedState as usize; } _ => {} } @@ -951,80 +828,77 @@ impl VncClient { num_encoding -= 1; } - self.desktop_resize(); + drop(locked_dpm); + drop(locked_state); + let mut buf: Vec = Vec::new(); + // VNC desktop resize. + desktop_resize(&client, &server, &mut buf); // VNC display cursor define. - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - display_cursor_define(self, &server); - self.update_event_handler(1, VncClient::handle_protocol_msg); + display_cursor_define(&client, &server, &mut buf); + client.out_buffer.lock().unwrap().read(&mut buf); + vnc_flush_notify(&client); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } - pub fn has_feature(&mut self, feature: VncFeatures) -> bool { - self.feature & (1 << feature as usize) != 0 - } - - /// Set Desktop Size. - pub fn desktop_resize(&mut self) { - if !self.has_feature(VncFeatures::VncFeatureResizeExt) - && !self.has_feature(VncFeatures::VncFeatureResize) - { + /// Update image for client. + fn update_frame_buff(&mut self) { + if self.expect == 1 { + self.expect = 10; return; } - self.width = get_image_width(self.server_image); - self.height = get_image_height(self.server_image); - if self.width < 0 - || self.width > MAX_WINDOW_WIDTH as i32 - || self.height < 0 - || self.height > MAX_WINDOW_HEIGHT as i32 - { - error!("Invalid Image Size!"); - return; + let buf = self.read_incoming_msg(); + let locked_surface = self.server.vnc_surface.lock().unwrap(); + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); + drop(locked_surface); + let client = self.client.clone(); + let mut locked_state = client.conn_state.lock().unwrap(); + if buf[1] != 0 { + if locked_state.update_state != UpdateState::Force { + locked_state.update_state = UpdateState::Incremental; + } + } else { + locked_state.update_state = UpdateState::Force; + let x = u16::from_be_bytes([buf[2], buf[3]]) as i32; + let y = u16::from_be_bytes([buf[4], buf[5]]) as i32; + let w = u16::from_be_bytes([buf[6], buf[7]]) as i32; + let h = u16::from_be_bytes([buf[8], buf[9]]) as i32; + set_area_dirty( + &mut client.dirty_bitmap.lock().unwrap(), + x, + y, + w, + h, + width, + height, + ); } + drop(locked_state); + let server = self.server.clone(); + vnc_refresh_notify(&server); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + } - let mut buf: Vec = Vec::new(); - buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); - buf.append(&mut (0_u8).to_be_bytes().to_vec()); - buf.append(&mut (1_u16).to_be_bytes().to_vec()); - framebuffer_upadate( - 0, - 0, - self.width, - self.height, - ENCODING_DESKTOPRESIZE, - &mut buf, - ); - + /// Invalid authentication, send 1 to reject. + fn auth_failed(&mut self, msg: &str) { + let auth_rej: u8 = 1; + let mut buf: Vec = vec![1u8]; + buf.append(&mut (auth_rej as u32).to_be_bytes().to_vec()); + // If the RFB protocol version is above 3.8, an error reason will be returned. + if self.client.conn_state.lock().unwrap().version.minor >= 8 { + let err_msg = msg; + buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); + buf.append(&mut err_msg.as_bytes().to_vec()); + } self.write_msg(&buf); } - /// Process the data sent by the client - pub fn handle_protocol_msg(&mut self) -> Result<()> { - // According to RFB protocol, first byte identifies the event type. - let buf = self.buffpool.read_front(self.expect); - match ClientMsg::from(buf[0]) { - ClientMsg::SetPixelFormat => { - return self.set_pixel_format(); - } - ClientMsg::SetEncodings => { - return self.set_encodings(); - } - ClientMsg::FramebufferUpdateRequest => { - self.update_frame_buff(); - } - ClientMsg::KeyEvent => { - self.key_envent(); - } - ClientMsg::PointerEvent => { - self.point_event(); - } - ClientMsg::ClientCutText => { - self.client_cut_event(); - } - _ => { - self.update_event_handler(1, VncClient::handle_protocol_msg); - } - } - Ok(()) + /// Read the data from the receiver buffer. + pub fn read_incoming_msg(&mut self) -> Vec { + let mut locked_in_buffer = self.client.in_buffer.lock().unwrap(); + let buf = locked_in_buffer.read_front(self.expect).to_vec(); + buf } /// Action token after the event. @@ -1032,101 +906,228 @@ impl VncClient { /// # Arguments /// /// * `expect` - the size of bytes of next callback function. - /// * `handle_msg` - callback function of the next event. + /// * `msg_handler` - callback function of the next event. pub fn update_event_handler( &mut self, expect: usize, - handle_msg: fn(&mut VncClient) -> Result<()>, + msg_handler: fn(&mut ClientIoHandler) -> Result<()>, ) { - self.buffpool.remov_front(self.expect); + self.client + .in_buffer + .lock() + .unwrap() + .remov_front(self.expect); self.expect = expect; - self.handle_msg = handle_msg; + self.msg_handler = msg_handler; } - /// Clear the data when disconnected from client. - pub fn disconnect(&mut self) { - if let Err(e) = self.modify_event(NotifierOperation::Delete, 0) { - error!("Failed to delete event, error is {}", format!("{:?}", e)); - } + fn disconn_evt_handler(&mut self) -> Vec { + let notifiers = vec![ + EventNotifier::new( + NotifierOperation::Delete, + self.stream.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.client.write_fd.lock().unwrap().as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + self.disconn_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + ), + ]; - if let Err(e) = self.stream.shutdown(Shutdown::Both) { - info!("Shutdown stream failed: {}", e); - } - self.handlers.clear(); + notifiers } } -/// Internal_notifiers for VncClient. -impl EventNotifierHelper for VncClient { - fn internal_notifiers(client_handler: Arc>) -> Vec { - let client = client_handler.clone(); - let handler: Box Option>> = - Box::new(move |event, _| { - let mut dis_conn = false; - if event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { - dis_conn = true; - } else if event == EventSet::IN { - let mut locked_client = client.lock().unwrap(); - if let Err(e) = locked_client.from_tcpstream_to_buff() { - error!("Failed to read_msg, error is {}", format!("{:?}", e)); - dis_conn = true; - } - } +/// Internal notifiers for Client message. +impl EventNotifierHelper for ClientIoHandler { + fn internal_notifiers(client_io_handler: Arc>) -> Vec { + let mut notifiers: Vec = Vec::new(); - if !dis_conn { - let mut locked_client = client.lock().unwrap(); - while locked_client.buffpool.len() >= locked_client.expect { - if let Err(e) = (locked_client.handle_msg)(&mut locked_client) { - error!("Failed to read_msg, error is {}", format!("{:?}", e)); - dis_conn = true; - break; - } + // Register event notifier for read. + let client_io = client_io_handler.clone(); + let handler: Box Option>> = + Box::new(move |event, _fd: RawFd| { + let mut locked_client_io = client_io.lock().unwrap(); + if event & EventSet::ERROR == EventSet::ERROR + || event & EventSet::HANG_UP == EventSet::HANG_UP + || event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP + { + locked_client_io.client.conn_state.lock().unwrap().dis_conn = true; + } else if event & EventSet::IN == EventSet::IN { + if let Err(_e) = locked_client_io.client_handle_read() { + locked_client_io.client.conn_state.lock().unwrap().dis_conn = true; } } - if dis_conn { - connection_cleanup(client.clone()); + // Do disconnection event. + if locked_client_io + .client + .conn_state + .lock() + .unwrap() + .is_disconnect() + { + locked_client_io.disconn_evt.write(1).unwrap(); } + drop(locked_client_io); - None as Option> + None }); + let client_io = client_io_handler.clone(); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + client_io.lock().unwrap().stream.as_raw_fd(), + None, + EventSet::IN | EventSet::READ_HANG_UP, + vec![Arc::new(Mutex::new(handler))], + )); - let mut locked_client = client_handler.lock().unwrap(); - locked_client.handlers.push(Arc::new(Mutex::new(handler))); - - let client = client_handler.clone(); + // Register event notifier for write. + let client_io = client_io_handler.clone(); + let client = client_io.lock().unwrap().client.clone(); let handler: Box Option>> = - Box::new(move |event, _| { - let mut dis_conn = false; - if event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { - dis_conn = true; - } else if event == EventSet::IN { - let mut locked_client = client.lock().unwrap(); - if locked_client.tls_handshake().is_err() { - dis_conn = true; - } + Box::new(move |_event, fd| { + read_fd(fd); + let mut locked_client_io = client_io.lock().unwrap(); + locked_client_io.client_handle_write(); + + // do disconnection event. + if locked_client_io.client.conn_state.lock().unwrap().dis_conn { + locked_client_io.disconn_evt.write(1).unwrap(); } + drop(locked_client_io); - if dis_conn { - connection_cleanup(client.clone()); + None + }); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + client.write_fd.lock().unwrap().as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + // Register event for disconnect. + let client_io = client_io_handler.clone(); + let handler: Box Option>> = + Box::new(move |_event, fd| { + read_fd(fd); + // Drop client info from vnc server. + let mut locked_client_io = client_io.lock().unwrap(); + let addr = locked_client_io.addr.clone(); + let server = locked_client_io.server.clone(); + let notifiers = locked_client_io.disconn_evt_handler(); + // Shutdown stream. + if let Err(e) = locked_client_io.stream.shutdown(Shutdown::Both) { + error!("Shutdown stream failed: {}", e); } + drop(locked_client_io); + server.client_handlers.lock().unwrap().remove(&addr); - None as Option> + Some(notifiers) }); - locked_client.handlers.push(Arc::new(Mutex::new(handler))); - - vec![EventNotifier::new( + notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - locked_client.stream.as_raw_fd(), + client_io_handler.lock().unwrap().disconn_evt.as_raw_fd(), None, - EventSet::IN | EventSet::READ_HANG_UP, - vec![locked_client.handlers[0].clone()], - )] + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + notifiers } } -fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) { +/// Generate the data that needs to be sent. +/// Add to send queue +pub fn get_rects(client: &Arc, server: &Arc, dirty_num: i32) -> i32 { + if !is_need_update(client) || dirty_num == 0 { + return 0; + } + + let mut num_rects = 0; + let mut x: u64; + let mut y: u64 = 0; + let mut h: u64; + let mut x2: u64; + let mut rects = Vec::new(); + let bpl = client.dirty_bitmap.lock().unwrap().vol() / MAX_WINDOW_HEIGHT as usize; + let locked_surface = server.vnc_surface.lock().unwrap(); + let mut locked_dirty = client.dirty_bitmap.lock().unwrap(); + + let height = get_image_height(locked_surface.server_image) as u64; + let width = get_image_width(locked_surface.server_image) as u64; + loop { + // Find the first non-zero bit in dirty bitmap. + let offset = locked_dirty.find_next_bit(y as usize * bpl).unwrap() as u64; + if offset >= height as u64 * bpl as u64 { + break; + } + + x = offset % bpl as u64; + y = offset / bpl as u64; + // Find value in one line to the end. + x2 = locked_dirty.find_next_zero(offset as usize).unwrap() as u64 % bpl as u64; + let mut i = y; + while i < height { + if !locked_dirty.contain((i * bpl as u64 + x) as usize).unwrap() { + break; + } + for j in x..x2 { + locked_dirty.clear((i * bpl as u64 + j) as usize).unwrap(); + } + i += 1; + } + + h = i - y; + x2 = cmp::min(x2, width / DIRTY_PIXELS_NUM as u64); + if x2 > x as u64 { + rects.push(Rectangle::new( + (x * DIRTY_PIXELS_NUM as u64) as i32, + y as i32, + ((x2 - x) * DIRTY_PIXELS_NUM as u64) as i32, + h as i32, + )); + num_rects += 1; + } + + if x == 0 && x2 == width / DIRTY_PIXELS_NUM as u64 { + y += h; + if y == height { + break; + } + } + } + + VNC_RECT_INFO + .lock() + .unwrap() + .push(RectInfo::new(client, rects)); + + drop(locked_dirty); + drop(locked_surface); + + let mut locked_state = client.conn_state.lock().unwrap(); + locked_state.update_state = UpdateState::No; + drop(locked_state); + + num_rects +} + +fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> Result<()> { while tc.wants_write() { match tc.write_tls(stream) { Ok(_) => {} @@ -1136,18 +1137,193 @@ fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) { continue; } else { error!("write msg error: {:?}", e); - return; + return Err(anyhow!(VncError::Disconnection)); } } } } + + Ok(()) +} + +/// Whether the client's image data needs to be updated. +pub fn is_need_update(client: &Arc) -> bool { + match client.conn_state.lock().unwrap().update_state { + UpdateState::No => false, + UpdateState::Incremental => { + // throttle_output_offset + true + } + UpdateState::Force => { + // force_update_offset + true + } + } +} + +/// Set pixformat for client. +pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { + let mut locked_dpm = client.client_dpm.lock().unwrap(); + locked_dpm.pf.init_pixelformat(); + let big_endian: u8 = if cfg!(target_endian = "big") { + 1_u8 + } else { + 0_u8 + }; + buf.append(&mut (locked_dpm.pf.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. + buf.append(&mut (locked_dpm.pf.depth as u8).to_be_bytes().to_vec()); // Depth. + buf.append(&mut (big_endian as u8).to_be_bytes().to_vec()); // Big-endian flag. + buf.append(&mut (1_u8).to_be_bytes().to_vec()); // True-color flag. + buf.append(&mut (locked_dpm.pf.red.max as u16).to_be_bytes().to_vec()); // Red max. + buf.append(&mut (locked_dpm.pf.green.max as u16).to_be_bytes().to_vec()); // Green max. + buf.append(&mut (locked_dpm.pf.blue.max as u16).to_be_bytes().to_vec()); // Blue max. + buf.append(&mut (locked_dpm.pf.red.shift as u8).to_be_bytes().to_vec()); // Red shift. + buf.append(&mut (locked_dpm.pf.green.shift as u8).to_be_bytes().to_vec()); // Green shift. + buf.append(&mut (locked_dpm.pf.blue.shift as u8).to_be_bytes().to_vec()); // Blue shift. + buf.append(&mut [0; 3].to_vec()); // Padding. + drop(locked_dpm); +} + +/// Set Desktop Size. +pub fn desktop_resize(client: &Arc, server: &Arc, buf: &mut Vec) { + let locked_state = client.conn_state.lock().unwrap(); + let locked_surface = server.vnc_surface.lock().unwrap(); + if !locked_state.has_feature(VncFeatures::VncFeatureResizeExt) + && !locked_state.has_feature(VncFeatures::VncFeatureResize) + { + return; + } + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); + drop(locked_state); + drop(locked_surface); + + if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) + || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) + { + error!("Invalid Image Size!"); + return; + } + + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + buf.append(&mut (1_u16).to_be_bytes().to_vec()); + framebuffer_upadate(0, 0, width, height, ENCODING_DESKTOPRESIZE, buf); +} + +/// Set color depth for client. +pub fn set_color_depth(client: &Arc, server: &Arc, buf: &mut Vec) { + let locked_state = client.conn_state.lock().unwrap(); + if locked_state.has_feature(VncFeatures::VncFeatureWmvi) { + let locked_surface = server.vnc_surface.lock().unwrap(); + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); + drop(locked_surface); + + if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) + || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) + { + error!("Invalid Image Size!"); + return; + } + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Padding. + buf.append(&mut (1_u16).to_be_bytes().to_vec()); // Number of pixel block. + framebuffer_upadate(0, 0, width, height, ENCODING_WMVI, buf); + buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); + pixel_format_message(client, buf); + } else if !client + .client_dpm + .lock() + .unwrap() + .pf + .is_default_pixel_format() + { + client.client_dpm.lock().unwrap().convert = true; + } +} + +/// Send framebuf of mouse to the client. +pub fn display_cursor_define( + client: &Arc, + server: &Arc, + buf: &mut Vec, +) { + let mut cursor: DisplayMouse; + let mut mask: Vec; + let locked_cursor = server.vnc_cursor.lock().unwrap(); + match &locked_cursor.cursor { + Some(c) => { + cursor = c.clone(); + } + None => { + return; + } + } + match &locked_cursor.mask { + Some(m) => { + mask = m.clone(); + } + None => { + return; + } + } + drop(locked_cursor); + if cursor.data.is_empty() + || cursor.data.len() != ((cursor.width * cursor.height) as usize) * bytes_per_pixel() + { + return; + } + if client + .conn_state + .lock() + .unwrap() + .has_feature(VncFeatures::VncFeatureAlphaCursor) + { + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding + buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects + + framebuffer_upadate( + cursor.hot_x as i32, + cursor.hot_y as i32, + cursor.width as i32, + cursor.height as i32, + ENCODING_ALPHA_CURSOR as i32, + buf, + ); + buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); + buf.append(&mut cursor.data); + return; + } + + if client + .conn_state + .lock() + .unwrap() + .has_feature(VncFeatures::VncFeatureRichCursor) + { + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding + buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects + + framebuffer_upadate( + cursor.hot_x as i32, + cursor.hot_y as i32, + cursor.width as i32, + cursor.height as i32, + ENCODING_RICH_CURSOR as i32, + buf, + ); + let dpm = client.client_dpm.lock().unwrap().clone(); + let data_size = cursor.width * cursor.height * dpm.pf.pixel_bytes as u32; + let data_ptr = cursor.data.as_ptr() as *mut u8; + write_pixel(data_ptr, data_size as usize, &dpm, buf); + buf.append(&mut mask); + } } -fn connection_cleanup(client: Arc>) { - let addr = client.lock().unwrap().addr.clone(); - info!("Client disconnect : {:?}", addr); - client.lock().unwrap().disconnect(); - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_clients = server.clients.lock().unwrap(); - locked_clients.remove(&addr); +/// Consume the output buffer. +pub fn vnc_flush_notify(client: &Arc) { + client.write_fd.lock().unwrap().write(1).unwrap(); } diff --git a/vnc/src/error.rs b/vnc/src/error.rs index 7d2c52d21..08cf8d91b 100644 --- a/vnc/src/error.rs +++ b/vnc/src/error.rs @@ -37,4 +37,6 @@ pub enum VncError { AuthFailed(String), #[error("ParseKeyBoardFailed: {0}")] ParseKeyBoardFailed(String), + #[error("Disconnection")] + Disconnection, } diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 1634d1e07..586fd7aae 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -10,7 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::client::VncClient; +use crate::{ + client::ClientIoHandler, + pixman::{get_image_height, get_image_width}, +}; use log::error; use usb::{ keyboard::keyboard_event, @@ -34,14 +37,14 @@ const ASCII_A: i32 = 65; const ASCII_Z: i32 = 90; const UPPERCASE_TO_LOWERCASE: i32 = 32; -impl VncClient { +impl ClientIoHandler { /// Keyboard event. pub fn key_envent(&mut self) { if self.expect == 1 { self.expect = 8; return; } - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); let down = buf[1] as u8; let mut keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); @@ -64,7 +67,7 @@ impl VncClient { } } self.do_key_event(down, keycode); - self.update_event_handler(1, VncClient::handle_protocol_msg); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } // Mouse event. @@ -74,13 +77,17 @@ impl VncClient { return; } - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); let mut x = ((buf[2] as u16) << 8) + buf[3] as u16; let mut y = ((buf[4] as u16) << 8) + buf[5] as u16; // Window size alignment. - x = ((x as u64 * ABS_MAX) / self.width as u64) as u16; - y = ((y as u64 * ABS_MAX) / self.height as u64) as u16; + let locked_surface = self.server.vnc_surface.lock().unwrap(); + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); + drop(locked_surface); + x = ((x as u64 * ABS_MAX) / width as u64) as u16; + y = ((y as u64 * ABS_MAX) / height as u64) as u16; // ASCII -> HidCode. let button_mask: u8 = match buf[1] as u8 { @@ -100,7 +107,7 @@ impl VncClient { } } - self.update_event_handler(1, VncClient::handle_protocol_msg); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } /// Do keyboard event. @@ -132,7 +139,7 @@ impl VncClient { /// Client cut text. pub fn client_cut_event(&mut self) { - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); if self.expect == 1 { self.expect = 8; return; @@ -146,6 +153,6 @@ impl VncClient { } } - self.update_event_handler(1, VncClient::handle_protocol_msg); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } } diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 1eb2d7ad1..35625de56 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -12,8 +12,8 @@ use crate::{ auth::SaslAuth, - auth::{AuthState, SubAuthState}, - client::VncClient, + auth::{AuthState, SaslConfig, SubAuthState}, + client::{get_rects, ClientIoHandler, ClientState}, data::keycode::KEYSYM2KEYCODE, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, @@ -58,8 +58,8 @@ const CONNECTION_LIMIT: usize = 1; pub struct VncServer { /// Event fd for vnc refresh. pub refresh_fd: Arc>, - /// Clients connected to vnc. - pub clients: Arc>>>>, + /// Client io handler. + pub client_handlers: Arc>>>>, /// Security Type for connection. pub security_type: Arc>, /// Mapping ASCII to keycode. @@ -82,7 +82,7 @@ impl VncServer { pub fn new(refresh_fd: Arc>, guest_image: *mut pixman_image_t) -> Self { VncServer { refresh_fd, - clients: Arc::new(Mutex::new(HashMap::new())), + client_handlers: Arc::new(Mutex::new(HashMap::new())), security_type: Arc::new(Mutex::new(SecurityType::default())), keysym2keycode: Arc::new(Mutex::new(HashMap::new())), vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), @@ -199,8 +199,10 @@ impl ImageInfo { pub struct SecurityType { /// Configuration for tls connection. pub tlscreds: Option, - /// Configuration for sasl Authentication. + /// Authentication for connection pub saslauth: Option, + /// Configuration for sasl Authentication. + pub saslconfig: SaslConfig, /// Configuration to make tls channel. pub tls_config: Option>, /// Auth type. @@ -214,6 +216,7 @@ impl Default for SecurityType { SecurityType { tlscreds: None, saslauth: None, + saslconfig: SaslConfig::default(), tls_config: None, auth: AuthState::No, subauth: SubAuthState::VncAuthVencryptPlain, @@ -245,8 +248,8 @@ impl SecurityType { } // Sasl configuration. - if let Some(_sasl_auth) = object.sasl_object.get(&vnc_cfg.sasl_authz) { - self.saslauth = Some(SaslAuth::default()); + if let Some(sasl_auth) = object.sasl_object.get(&vnc_cfg.sasl_authz) { + self.saslauth = Some(SaslAuth::new(sasl_auth.identity.clone())); } Ok(()) @@ -497,12 +500,13 @@ impl VncSurface { /// * `x` `y`- coordinates of dirty area. fn set_dirty_for_each_clients(x: usize, y: usize) { let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_clients = server.clients.lock().unwrap(); - for client in locked_clients.values_mut() { + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client_io in locked_handlers.values_mut() { + let client = client_io.lock().unwrap().client.clone(); client + .dirty_bitmap .lock() .unwrap() - .dirty_bitmap .set(x + y * VNC_BITMAP_WIDTH as usize) .unwrap(); } @@ -519,42 +523,37 @@ pub fn handle_connection( stream: TcpStream, addr: SocketAddr, ) -> Result<()> { - if server.clients.lock().unwrap().len() >= server.conn_limits { + if server.client_handlers.lock().unwrap().len() >= server.conn_limits { stream.shutdown(Shutdown::Both).unwrap(); return Err(anyhow!(VncError::MakeConnectionFailed(String::from( "Total connection is exceeding to limit." )))); } - info!("New Client: {:?}", addr); stream .set_nonblocking(true) .expect("set nonblocking failed"); + info!("New Connection: {:?}", stream); - let locked_security_type = server.security_type.lock().unwrap(); - let mut client = VncClient::new( + // Register event notifier for vnc client. + let client = Arc::new(ClientState::default()); + let client_io = Arc::new(Mutex::new(ClientIoHandler::new( stream, - addr.to_string(), - locked_security_type.auth, - locked_security_type.subauth, + client, server.clone(), - server.vnc_surface.lock().unwrap().server_image, - ); - - if let Some(saslauth) = &locked_security_type.saslauth { - client.sasl.identity = saslauth.identity.clone(); - } - drop(locked_security_type); - client.write_msg("RFB 003.008\n".to_string().as_bytes()); - info!("{:?}", client.stream); + addr.to_string(), + ))); + client_io + .lock() + .unwrap() + .write_msg("RFB 003.008\n".to_string().as_bytes()); - let tmp_client = Arc::new(Mutex::new(client)); server - .clients + .client_handlers .lock() .unwrap() - .insert(addr.to_string(), tmp_client.clone()); + .insert(addr.to_string(), client_io.clone()); - EventLoop::update_event(EventNotifierHelper::internal_notifiers(tmp_client), None)?; + EventLoop::update_event(EventNotifierHelper::internal_notifiers(client_io), None)?; update_server_surface(server); vnc_refresh_notify(server); @@ -567,7 +566,7 @@ fn vnc_refresh() { return; } let server = VNC_SERVERS.lock().unwrap()[0].clone(); - if server.clients.lock().unwrap().is_empty() { + if server.client_handlers.lock().unwrap().is_empty() { return; } @@ -586,9 +585,10 @@ fn vnc_refresh() { } let mut _rects: i32 = 0; - let mut clients = server.clients.lock().unwrap(); - for client in clients.values_mut() { - _rects += client.lock().unwrap().get_rects(dirty_num); + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client_io in locked_handlers.values_mut() { + let client = client_io.lock().unwrap().client.clone(); + _rects += get_rects(&client, &server, dirty_num); } } diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 10aa42d96..739129a9c 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{auth::SubAuthState, client::VncClient, VncError}; +use crate::{auth::SubAuthState, client::ClientIoHandler, VncError}; use anyhow::{anyhow, Result}; use log::{error, info}; use rustls::{ @@ -26,7 +26,6 @@ use rustls::{ RootCertStore, SupportedCipherSuite, SupportedKxGroup, SupportedProtocolVersion, }; use std::{fs::File, io::BufReader, sync::Arc}; -use util::loop_context::NotifierOperation; const TLS_CREDS_SERVER_CACERT: &str = "cacert.pem"; const TLS_CREDS_SERVERCERT: &str = "servercert.pem"; @@ -64,11 +63,11 @@ pub struct TlsCreds { pub verifypeer: bool, } -impl VncClient { +impl ClientIoHandler { /// Exchange auth version with client pub fn client_vencrypt_init(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); - + let buf = self.read_incoming_msg(); + let subauth = self.server.security_type.lock().unwrap().subauth; // VeNCrypt version 0.2. if buf[0] != 0 || buf[1] != 2 { let mut buf = Vec::new(); @@ -83,21 +82,22 @@ impl VncClient { // Number of sub-auths. buf.append(&mut (1_u8).to_be_bytes().to_vec()); // The supported auth. - buf.append(&mut (self.subauth as u32).to_be_bytes().to_vec()); + buf.append(&mut (subauth as u32).to_be_bytes().to_vec()); self.write_msg(&buf); } - self.update_event_handler(4, VncClient::client_vencrypt_auth); + self.update_event_handler(4, ClientIoHandler::client_vencrypt_auth); Ok(()) } /// Encrypted Channel Initialize. pub fn client_vencrypt_auth(&mut self) -> Result<()> { - let buf = self.buffpool.read_front(self.expect); + let buf = self.read_incoming_msg(); let buf = [buf[0], buf[1], buf[2], buf[3]]; let auth = u32::from_be_bytes(buf); + let subauth = self.server.security_type.lock().unwrap().subauth; - if auth != self.subauth as u32 { + if auth != subauth as u32 { let mut buf = Vec::new(); // Reject auth. buf.append(&mut (0_u8).to_be_bytes().to_vec()); @@ -132,8 +132,13 @@ impl VncClient { )))); } - self.update_event_handler(1, VncClient::tls_handshake); - self.modify_event(NotifierOperation::Modify, 1)?; + self.client + .in_buffer + .lock() + .unwrap() + .remov_front(self.expect); + self.expect = 0; + self.msg_handler = ClientIoHandler::tls_handshake; Ok(()) } @@ -170,11 +175,10 @@ impl VncClient { if tc.is_handshaking() { // Tls handshake continue. - self.handle_msg = VncClient::tls_handshake; + self.msg_handler = ClientIoHandler::tls_handshake; } else { info!("Finished tls handshaking"); // Tls handshake finished. - self.modify_event(NotifierOperation::Modify, 0)?; if let Err(e) = self.handle_vencrypt_subauth() { return Err(e); } @@ -188,10 +192,11 @@ impl VncClient { } fn handle_vencrypt_subauth(&mut self) -> Result<()> { - match self.subauth { + let subauth = self.server.security_type.lock().unwrap().subauth; + match subauth { SubAuthState::VncAuthVencryptX509Sasl => { self.expect = 4; - self.handle_msg = VncClient::get_mechname_length; + self.msg_handler = ClientIoHandler::get_mechname_length; if let Err(e) = self.start_sasl_auth() { return Err(e); } @@ -200,12 +205,13 @@ impl VncClient { let buf = [0u8; 4]; self.write_msg(&buf); self.expect = 1; - self.handle_msg = VncClient::handle_client_init; + self.msg_handler = ClientIoHandler::handle_client_init; } _ => { let mut buf: Vec = Vec::new(); buf.append(&mut (0_u8).to_be_bytes().to_vec()); - if self.version.minor >= 8 { + let version = self.client.conn_state.lock().unwrap().version.clone(); + if version.minor >= 8 { let err_msg: String = "Unsupported subauth type".to_string(); buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); buf.append(&mut err_msg.as_bytes().to_vec()); diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 200364d52..6506c09f0 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -10,11 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::VncError; use crate::{ client::{ - DisplayMode, RectInfo, Rectangle, ServerMsg, VncClient, VncFeatures, ENCODING_ALPHA_CURSOR, - ENCODING_HEXTILE, ENCODING_RAW, ENCODING_RICH_CURSOR, + desktop_resize, display_cursor_define, set_color_depth, vnc_flush_notify, DisplayMode, + RectInfo, Rectangle, ServerMsg, ENCODING_HEXTILE, ENCODING_RAW, }, encoding::enc_hextile::hextile_send_framebuffer_update, pixman::{ @@ -23,6 +22,7 @@ use crate::{ }, round_up, round_up_div, server::{make_server_config, vnc_refresh_notify, VncConnHandler, VncServer, VncSurface}, + VncError, }; use anyhow::{anyhow, Result}; use core::time; @@ -169,10 +169,13 @@ fn start_vnc_thread() -> Result<()> { buf.append(&mut [0_u8; 2].to_vec()); let locked_surface = server.vnc_surface.lock().unwrap(); + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); for rect in rect_info.rects.iter_mut() { - if check_rect(rect, rect_info.width, rect_info.height) { + let dpm = rect_info.client.client_dpm.lock().unwrap().clone(); + if check_rect(rect, width, height) { let n = - send_framebuffer_update(rect_info.image, rect, &rect_info.dpm, &mut buf); + send_framebuffer_update(locked_surface.server_image, rect, &dpm, &mut buf); if n >= 0 { num_rects += n; } @@ -182,11 +185,9 @@ fn start_vnc_thread() -> Result<()> { buf[3] = num_rects as u8; drop(locked_surface); - let locked_clients = server.clients.lock().unwrap(); - if let Some(client) = locked_clients.get(&rect_info.addr) { - client.lock().unwrap().write_msg(&buf); - } - drop(locked_clients); + let client = rect_info.client; + client.out_buffer.lock().unwrap().read(&mut buf); + vnc_flush_notify(&client); }) .unwrap(); Ok(()) @@ -291,7 +292,7 @@ pub fn update_server_surface(server: &Arc) { locked_vnc_surface.server_image = ptr::null_mut(); // Server image changes, clear the task queue. VNC_RECT_INFO.lock().unwrap().clear(); - if server.clients.lock().unwrap().is_empty() { + if server.client_handlers.lock().unwrap().is_empty() { return; } @@ -319,6 +320,7 @@ pub fn update_server_surface(server: &Arc) { g_width, g_height, ); + vnc_refresh_notify(server); } /// Check if the suface for VncClient is need update @@ -552,20 +554,23 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { drop(locked_vnc_surface); update_server_surface(&server); - let mut locked_clients = server.clients.lock().unwrap(); - for client in locked_clients.values_mut() { + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client_io in locked_handlers.values_mut() { + let client = client_io.lock().unwrap().client.clone(); let width = vnc_width(guest_width); let height = vnc_height(guest_height); - let mut locked_client = client.lock().unwrap(); + let mut buf: Vec = Vec::new(); // Set Color depth. - locked_client.set_color_depth(); + set_color_depth(&client, &server, &mut buf); // Desktop_resize. - locked_client.desktop_resize(); + desktop_resize(&client, &server, &mut buf); // Cursor define. - display_cursor_define(&mut locked_client, &server); - locked_client.dirty_bitmap.clear_all(); + display_cursor_define(&client, &server, &mut buf); + client.out_buffer.lock().unwrap().read(&mut buf); + vnc_flush_notify(&client); + client.dirty_bitmap.lock().unwrap().clear_all(); set_area_dirty( - &mut locked_client.dirty_bitmap, + &mut client.dirty_bitmap.lock().unwrap(), 0, 0, width, @@ -614,79 +619,14 @@ pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { server.vnc_cursor.lock().unwrap().cursor = Some(cursor.clone()); server.vnc_cursor.lock().unwrap().mask = Some(mask.clone()); - let mut locked_clients = server.clients.lock().unwrap(); + let mut locked_handler = server.client_handlers.lock().unwrap(); // Send the framebuff for each client. - for client in locked_clients.values_mut() { - let mut locked_client = client.lock().unwrap(); - display_cursor_define(&mut locked_client, &server); - } -} - -/// Send framebuf of mouse to the client. -pub fn display_cursor_define(client: &mut VncClient, server: &Arc) { - let mut cursor: DisplayMouse; - let mut mask: Vec; - let locked_cursor = server.vnc_cursor.lock().unwrap(); - match &locked_cursor.cursor { - Some(c) => { - cursor = c.clone(); - } - None => { - return; - } - } - match &locked_cursor.mask { - Some(m) => { - mask = m.clone(); - } - None => { - return; - } - } - drop(locked_cursor); - if cursor.data.is_empty() - || cursor.data.len() != ((cursor.width * cursor.height) as usize) * bytes_per_pixel() - { - return; - } - let mut buf = Vec::new(); - if client.has_feature(VncFeatures::VncFeatureAlphaCursor) { - buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); - buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding - buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects - - framebuffer_upadate( - cursor.hot_x as i32, - cursor.hot_y as i32, - cursor.width as i32, - cursor.height as i32, - ENCODING_ALPHA_CURSOR as i32, - &mut buf, - ); - buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); - buf.append(&mut cursor.data); - client.write_msg(&buf); - return; - } - - if client.has_feature(VncFeatures::VncFeatureRichCursor) { - buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); - buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding - buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects - - framebuffer_upadate( - cursor.hot_x as i32, - cursor.hot_y as i32, - cursor.width as i32, - cursor.height as i32, - ENCODING_RICH_CURSOR as i32, - &mut buf, - ); - let data_size = cursor.width * cursor.height * client.client_dpm.pf.pixel_bytes as u32; - let data_ptr = cursor.data.as_ptr() as *mut u8; - write_pixel(data_ptr, data_size as usize, &client.client_dpm, &mut buf); - buf.append(&mut mask); - client.write_msg(&buf); + for client_io in locked_handler.values_mut() { + let client = client_io.lock().unwrap().client.clone(); + let mut buf: Vec = Vec::new(); + display_cursor_define(&client, &server, &mut buf); + client.out_buffer.lock().unwrap().read(&mut buf); + vnc_flush_notify(&client); } } -- Gitee From 31b7a5bf9c697d3d15adb6159460989fd5d75726 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 19:53:40 +0800 Subject: [PATCH 0522/2187] xhci: remove the completed transfer Remove the completed transfer in the epctx when transfer is done. And use Arc for the completed field, because the xfer may have multiple copies, for example, in retry and epctx transfer queues, ensure that the same object is accessed. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 45 +++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index a934bd718..28e6f8f01 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -10,9 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::LinkedList; use std::mem::size_of; use std::slice::from_raw_parts; use std::slice::from_raw_parts_mut; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, GuestAddress}; @@ -103,7 +105,7 @@ pub struct XhciTransfer { packet: UsbPacket, status: TRBCCode, td: Vec, - complete: bool, + complete: Arc, slotid: u32, epid: u32, in_xfer: bool, @@ -117,7 +119,7 @@ impl XhciTransfer { packet: UsbPacket::default(), status: TRBCCode::Invalid, td: Vec::new(), - complete: false, + complete: Arc::new(AtomicBool::new(false)), slotid: 0, epid: 0, in_xfer: false, @@ -125,6 +127,14 @@ impl XhciTransfer { running_retry: false, } } + + fn is_completed(&self) -> bool { + self.complete.load(Ordering::Acquire) + } + + fn set_comleted(&mut self, v: bool) { + self.complete.store(v, Ordering::SeqCst) + } } /// Endpoint context which use the ring to transfer data. @@ -137,7 +147,7 @@ pub struct XhciEpContext { output_ctx_addr: DmaAddr, state: u32, interval: u32, - transfers: Vec, + transfers: LinkedList, retry: Option, } @@ -151,7 +161,7 @@ impl XhciEpContext { output_ctx_addr: 0, state: 0, interval: 0, - transfers: Vec::new(), + transfers: LinkedList::new(), retry: None, } } @@ -198,6 +208,17 @@ impl XhciEpContext { dma_write_u32(mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_dwords())?; Ok(()) } + + /// Flush the transfer list, remove the transfer which is completed. + fn flush_transfer(&mut self) { + let mut undo = LinkedList::new(); + while let Some(head) = self.transfers.pop_front() { + if !head.is_completed() { + undo.push_back(head); + } + } + self.transfers = undo; + } } /// Endpoint type, including control, bulk, interrupt and isochronous. @@ -1191,16 +1212,17 @@ impl XhciDevice { } self.endpoint_do_transfer(&mut xfer)?; let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - if xfer.complete { + if xfer.is_completed() { epctx.set_state(&self.mem_space, epctx.state)?; + epctx.flush_transfer(); } else { - epctx.transfers.push(xfer.clone()); + epctx.transfers.push_back(xfer.clone()); } if epctx.state == EP_HALTED { break; } // retry - if !xfer.complete && xfer.running_retry { + if !xfer.is_completed() && xfer.running_retry { epctx.retry = Some(xfer); break; } @@ -1259,8 +1281,9 @@ impl XhciDevice { } self.complete_packet(xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - if xfer.complete { + if xfer.is_completed() { epctx.set_state(&self.mem_space, epctx.state)?; + epctx.flush_transfer(); } epctx.retry = None; Ok(true) @@ -1436,15 +1459,15 @@ impl XhciDevice { /// Update packet status and then submit transfer. fn complete_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { if xfer.packet.status == UsbPacketStatus::Async { - xfer.complete = false; + xfer.set_comleted(false); xfer.running_retry = false; return Ok(()); } else if xfer.packet.status == UsbPacketStatus::Nak { - xfer.complete = false; + xfer.set_comleted(false); xfer.running_retry = true; return Ok(()); } else { - xfer.complete = true; + xfer.set_comleted(true); xfer.running_retry = false; } if xfer.packet.status == UsbPacketStatus::Success { -- Gitee From 7831bc82b90426a4760ab939ecca9657f108f9b3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:11:50 +0800 Subject: [PATCH 0523/2187] xhci: no need to clone transfer when flushing the queue There is no need to clone transfer when flushing the queue. In addition, you do not need to check the endpoint enabled state. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 28e6f8f01..89858b61c 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1608,10 +1608,10 @@ impl XhciDevice { debug!("flush_ep_transfer slotid {} epid {}", slotid, epid); let mut cnt = 0; let mut report = report; - let xfers = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] + while let Some(mut xfer) = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] .transfers - .clone(); - for mut xfer in xfers { + .pop_front() + { cnt += self.do_ep_transfer(slotid, epid, &mut xfer, report)?; if cnt != 0 { // Only report once. @@ -1638,9 +1638,6 @@ impl XhciDevice { self.submit_transfer(xfer)?; } let epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(ep_id - 1) as usize]; - if !epctx.enabled { - bail!("Endpoint is disabled"); - } epctx.retry = None; xfer.running_retry = false; killed = 1; -- Gitee From 673509e7c047f75129861c38d51afdabb851acdb Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:24:21 +0800 Subject: [PATCH 0524/2187] xhci: flush the transfer when the endpoint is reset. Flush the transfer when the endpoint is reset. --- usb/src/xhci/xhci_controller.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 89858b61c..48d4293b0 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1117,6 +1117,11 @@ impl XhciDevice { error!("Endpoint is not halted"); return Ok(TRBCCode::ContextStateError); } + if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Invalid)? > 0 { + warn!("endpoint reset when xfers running!"); + } + let slot = &mut self.slots[(slot_id - 1) as usize]; + let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; if let Some(port) = &slot.usb_port { if port.upgrade().unwrap().lock().unwrap().dev.is_some() { epctx.set_state(&self.mem_space, EP_STOPPED)?; -- Gitee From 5e19e78ae8788390c49c81e85f42230fc5f0c29c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:28:15 +0800 Subject: [PATCH 0525/2187] xhci: no need to setup packet when doing retry There is no need to setup packet when doring retry. Becuase it's been done. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 48d4293b0..ebc0eb49c 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1273,11 +1273,6 @@ impl XhciDevice { .as_ref() .unwrap() .clone(); - if let Err(e) = self.setup_usb_packet(xfer) { - error!("Failed to setup packet when retry {}", e); - self.report_transfer_error(xfer)?; - return Ok(true); - } self.device_handle_packet(&mut xfer.packet); if xfer.packet.status == UsbPacketStatus::Nak { debug!("USB packet status is NAK"); -- Gitee From 91de37396b6f19d89124529da77d628b1fb93532 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 20:45:32 +0800 Subject: [PATCH 0526/2187] usb: remove unused field Remove unused field in usb packet and xhci transfer. Signed-off-by: zhouli57 --- usb/src/usb.rs | 28 ++++++---------------------- usb/src/xhci/xhci_controller.rs | 10 ++-------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index eedf40754..9d0ee3652 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -703,16 +703,13 @@ impl Iovec { pub struct UsbPacket { /// USB packet id. pub pid: u32, - pub id: u64, pub ep: Option>>, pub iovecs: Vec, - /// control transfer + /// control transfer parameter. pub parameter: u64, - pub short_not_ok: bool, - pub int_req: bool, - /// USB packet return status + /// USB packet return status. pub status: UsbPacketStatus, - /// Actually transfer length + /// Actually transfer length. pub actual_length: u32, pub state: UsbPacketState, } @@ -721,29 +718,19 @@ impl std::fmt::Display for UsbPacket { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "pid {} id {} param {} status {:?} actual_length {}, state {:?}", - self.pid, self.id, self.parameter, self.status, self.actual_length, self.state + "pid {} param {} status {:?} actual_length {}, state {:?}", + self.pid, self.parameter, self.status, self.actual_length, self.state ) } } impl UsbPacket { - pub fn init( - &mut self, - pid: u32, - ep: Weak>, - id: u64, - short_or_ok: bool, - int_req: bool, - ) { - self.id = id; + pub fn init(&mut self, pid: u32, ep: Weak>) { self.pid = pid; self.ep = Some(ep); self.status = UsbPacketStatus::Success; self.actual_length = 0; self.parameter = 0; - self.short_not_ok = short_or_ok; - self.int_req = int_req; self.state = UsbPacketState::Setup; } } @@ -752,12 +739,9 @@ impl Default for UsbPacket { fn default() -> UsbPacket { UsbPacket { pid: 0, - id: 0, ep: None, iovecs: Vec::new(), parameter: 0, - short_not_ok: false, - int_req: false, status: UsbPacketStatus::NoDev, actual_length: 0, state: UsbPacketState::Undefined, diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index ebc0eb49c..d6fc6e35b 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -109,7 +109,6 @@ pub struct XhciTransfer { slotid: u32, epid: u32, in_xfer: bool, - int_req: bool, running_retry: bool, } @@ -123,7 +122,6 @@ impl XhciTransfer { slotid: 0, epid: 0, in_xfer: false, - int_req: false, running_retry: false, } } @@ -843,7 +841,7 @@ impl XhciDevice { let usb_dev = locked_dev.get_mut_usb_device(); let locked_usb = usb_dev.lock().unwrap(); let ep = Arc::downgrade(&locked_usb.get_endpoint(USB_TOKEN_OUT as u32, 0)); - p.init(USB_TOKEN_OUT as u32, ep, 0, false, false); + p.init(USB_TOKEN_OUT as u32, ep); drop(locked_usb); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, @@ -1403,10 +1401,6 @@ impl XhciDevice { let mut vec = Vec::new(); for trb in &xfer.td { let trb_type = trb.get_type(); - if trb.control & TRB_TR_IOC == TRB_TR_IOC { - xfer.int_req = true; - } - if trb_type == TRBType::TrData && (trb.control & TRB_TR_DIR == 0) == in_xfer { bail!("Direction of data transfer is mismatch"); } @@ -1431,7 +1425,7 @@ impl XhciDevice { } } } - xfer.packet.init(dir as u32, ep, 0, false, xfer.int_req); + xfer.packet.init(dir as u32, ep); xfer.packet.iovecs = vec; Ok(()) } -- Gitee From dacf0e7760962fce5f938eed6f040cdbd7e0ae86 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 28 Nov 2022 21:05:41 +0800 Subject: [PATCH 0527/2187] usb: check whether the device exists when getting the usb endpoint Check whether the device exists when getting the usb endpoint. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index d6fc6e35b..8a5bb4570 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1437,7 +1437,11 @@ impl XhciDevice { bail!("USB port not found slotid {} epid {}", slotid, epid); }; let locked_port = port.lock().unwrap(); - let dev = locked_port.dev.as_ref().unwrap(); + let dev = if let Some(dev) = locked_port.dev.as_ref() { + dev + } else { + bail!("No device found in USB port."); + }; let mut locked_dev = dev.lock().unwrap(); let pid = if epid & 1 == 1 { USB_TOKEN_IN -- Gitee From a6ab4b81866f5d5711aeea00eaca4f6e2af3718a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 29 Nov 2022 11:36:44 +0800 Subject: [PATCH 0528/2187] xhci: optimize the event reporting code Optimize the event reporting code. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 8a5bb4570..05b2f1cdd 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -81,6 +81,7 @@ const PORT_EVENT_ID_SHIFT: u32 = 24; const SLOT_CTX_PORT_NUMBER_SHIFT: u32 = 16; const ENDPOINT_ID_START: u32 = 1; const MAX_ENDPOINTS: u32 = 31; +const TRANSFER_LEN_MASK: u32 = 0xffffff; /// XHCI config const XHCI_MAX_PORT2: u8 = 15; const XHCI_MAX_PORT3: u8 = 15; @@ -1538,11 +1539,15 @@ impl XhciDevice { self.send_transfer_event(xfer, trb, chunk, short_pkt, &mut edtla)?; reported = true; if xfer.status != TRBCCode::Success { - // Send unSuccess event succeed,return directly. - info!("submit_transfer xfer status {:?}", xfer.status); + // Send unSuccess event succeed, return directly. + warn!( + "A warning is generated when the transfer is submitted, xfer status {:?}", + xfer.status + ); return Ok(()); } } + // Allow reporting events after IOC bit is set in setup TRB. if trb_type == TRBType::TrSetup { reported = false; short_pkt = false; @@ -1570,7 +1575,7 @@ impl XhciDevice { &mut self, xfer: &XhciTransfer, trb: &XhciTRB, - chunk: u32, + transfered: u32, short_pkt: bool, edtla: &mut u32, ) -> Result<()> { @@ -1578,22 +1583,18 @@ impl XhciDevice { let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); evt.slot_id = xfer.slotid as u8; evt.ep_id = xfer.epid as u8; - evt.length = (trb.status & TRB_TR_LEN_MASK) - chunk; + evt.length = (trb.status & TRB_TR_LEN_MASK) - transfered; evt.flags = 0; evt.ptr = trb.addr; - evt.ccode = if xfer.status == TRBCCode::Success { - if short_pkt { - TRBCCode::ShortPacket - } else { - TRBCCode::Success - } + evt.ccode = if short_pkt && xfer.status == TRBCCode::Success { + TRBCCode::ShortPacket } else { xfer.status }; if trb_type == TRBType::TrEvdata { evt.ptr = trb.parameter; evt.flags |= TRB_EV_ED; - evt.length = *edtla & 0xffffff; + evt.length = *edtla & TRANSFER_LEN_MASK; *edtla = 0; } let idx = (trb.status >> TRB_INTR_SHIFT) & TRB_INTR_MASK; -- Gitee From c11b4c8f5bac08c3824759053c0f159424c8fa18 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 29 Nov 2022 15:28:42 +0800 Subject: [PATCH 0529/2187] pci: vfio: fix msix enable/disable change In commit 834d41db5 it did't get was_enabled before writing config, thus it seems that the msix always keeps enabled/disabled unchanged. Fix that by getting was_enabled before writing config. Signed-off-by: Zhang Bo --- vfio/src/vfio_pci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 6c53c0d39..5bbfa606f 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -956,6 +956,7 @@ impl PciDevOps for VfioPciDevice { .msix .as_ref() .map_or(0, |m| m.lock().unwrap().msix_cap_offset as usize); + let was_enable = is_msix_enabled(cap_offset, &self.pci_config.config); let parent_bus = self.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); self.pci_config.write( @@ -976,7 +977,6 @@ impl PciDevOps for VfioPciDevice { } } } else if ranges_overlap(offset, end, cap_offset, cap_offset + MSIX_CAP_SIZE as usize) { - let was_enable = is_msix_enabled(cap_offset, &self.pci_config.config); let is_enable = is_msix_enabled(cap_offset, &self.pci_config.config); if !was_enable && is_enable { -- Gitee From 1254ca8685d190e98d8e5415813cabdc5b5064f4 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 26 Nov 2022 17:45:53 +0800 Subject: [PATCH 0530/2187] kvm-bindings, kvm-ioctls, vmm-sys-util crate upgrade kvm-ioctl v0.12.0 introduces some broken api changes and expose some new api. There are some code in the Repositories that overlap with the new API functions, so upgrade to newest kvm-ioctl crate. Signed-off-by: Yan Wen --- Cargo.lock | 12 +- Cargo.toml | 4 +- address_space/Cargo.toml | 8 +- boot_loader/Cargo.toml | 6 +- cpu/Cargo.toml | 6 +- cpu/src/aarch64/caps.rs | 36 +---- cpu/src/aarch64/core_regs.rs | 141 ++++-------------- cpu/src/aarch64/mod.rs | 2 +- cpu/src/lib.rs | 7 +- devices/Cargo.toml | 6 +- hypervisor/Cargo.toml | 8 +- hypervisor/src/kvm/interrupt.rs | 13 +- ...Third_Party_Open_Source_Software_Notice.md | 6 +- machine/Cargo.toml | 6 +- machine_manager/Cargo.toml | 2 +- migration/Cargo.toml | 4 +- pci/Cargo.toml | 6 +- sysbus/Cargo.toml | 4 +- usb/Cargo.toml | 2 +- util/Cargo.toml | 8 +- vfio/Cargo.toml | 6 +- vhost_user_fs/Cargo.toml | 4 +- virtio/Cargo.toml | 4 +- vnc/Cargo.toml | 4 +- 24 files changed, 96 insertions(+), 209 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b4a92b41..61ce029a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,18 +363,18 @@ dependencies = [ [[package]] name = "kvm-bindings" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78c049190826fff959994b7c1d8a2930d0a348f1b8f3aa4f9bb34cd5d7f2952" +checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" dependencies = [ "vmm-sys-util", ] [[package]] name = "kvm-ioctls" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97422ba48d7ffb66fd4d18130f72ab66f9bbbf791fb7a87b9291cdcfec437593" +checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4" dependencies = [ "kvm-bindings", "libc", @@ -1024,9 +1024,9 @@ dependencies = [ [[package]] name = "vmm-sys-util" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08604d7be03eb26e33b3cee3ed4aef2bf550b305d1cca60e84da5d28d3790b62" +checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24" dependencies = [ "bitflags", "libc", diff --git a/Cargo.toml b/Cargo.toml index 161f8950e..18d3cf187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ thiserror = "1.0" anyhow = "1.0" capng = "0.2.2" error-chain = "0.12.4" -kvm-ioctls = ">=0.11.0" +kvm-ioctls = "0.12.0" libc = "0.2" log = "0.4" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" hypervisor = { path = "hypervisor" } machine = { path = "machine" } machine_manager = { path = "machine_manager" } diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index ed1148932..465d546f4 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -9,10 +9,10 @@ description = "provide memory management for VM" [dependencies] libc = "0.2" log = "0.4" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" -vmm-sys-util = ">=0.10.0" -arc-swap = ">=1.5.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" +vmm-sys-util = "0.11.0" +arc-swap = "1.5.0" thiserror = "1.0" anyhow = "1.0" hypervisor = { path = "../hypervisor" } diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index d621c8aa5..0665e51c0 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -8,11 +8,11 @@ license = "Mulan PSL v2" [dependencies] thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" libc = "0.2" log = "0.4" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" address_space = { path = "../address_space" } devices = { path = "../devices" } util = { path = "../util" } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 6edb9ec56..7aa86f7c3 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -9,11 +9,11 @@ description = "CPU emulation" [dependencies] thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" log = "0.4" libc = "0.2" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index 85c19abaf..df46d07f0 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{convert::TryInto, mem::size_of}; - use kvm_bindings::{ KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64, @@ -19,7 +17,7 @@ use kvm_bindings::{ use kvm_ioctls::{Cap, Kvm, VcpuFd}; use machine_manager::config::{CpuConfig, PmuConfig}; -use super::core_regs::{get_one_reg_vec, set_one_reg_vec, Result}; +use super::core_regs::Result; // Capabilities for ARM cpu. #[derive(Debug, Clone)] @@ -68,12 +66,12 @@ impl From<&CpuConfig> for ArmCPUFeatures { #[derive(Default, Clone, Copy)] pub struct CpregListEntry { pub reg_id: u64, - pub value: u64, + pub value: u128, } impl CpregListEntry { fn cpreg_tuples_entry(&self) -> bool { - self.reg_id & KVM_REG_ARM_COPROC_MASK as u64 == KVM_REG_ARM_CORE as u64 + (self.reg_id & KVM_REG_ARM_COPROC_MASK as u64) == (KVM_REG_ARM_CORE as u64) } fn normal_cpreg_entry(&self) -> bool { @@ -101,24 +99,7 @@ impl CpregListEntry { /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn get_cpreg(&mut self, vcpu_fd: &VcpuFd) -> Result<()> { if self.normal_cpreg_entry() { - let val = get_one_reg_vec(vcpu_fd, self.reg_id)?; - if (self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32 { - self.value = u32::from_be_bytes( - val.as_slice() - .split_at(size_of::()) - .0 - .try_into() - .unwrap(), - ) as u64; - } else if (self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64 { - self.value = u64::from_be_bytes( - val.as_slice() - .split_at(size_of::()) - .0 - .try_into() - .unwrap(), - ) - } + self.value = vcpu_fd.get_one_reg(self.reg_id)?; } Ok(()) @@ -131,14 +112,7 @@ impl CpregListEntry { /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn set_cpreg(&self, vcpu_fd: &VcpuFd) -> Result<()> { if self.normal_cpreg_entry() { - let mut value: Vec = self.value.to_be_bytes().to_vec(); - let data = if (self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32 { - value.split_off(size_of::() / size_of::()) - } else { - value - }; - - set_one_reg_vec(vcpu_fd, self.reg_id, &data)?; + vcpu_fd.set_one_reg(self.reg_id, self.value)?; } Ok(()) diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index 708f4acca..8ca13cbd4 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -12,23 +12,16 @@ use std::mem::size_of; -use hypervisor::kvm::{KVM_GET_ONE_REG, KVM_SET_ONE_REG}; use kvm_bindings::{ - kvm_one_reg, kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, - KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK, KVM_REG_SIZE_SHIFT, KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, - KVM_REG_SIZE_U64, + kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM_CORE, + KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64, }; use kvm_ioctls::VcpuFd; -use util::byte_code::ByteCode; use util::offset_of; -use vmm_sys_util::{ - errno, - ioctl::{ioctl_with_mut_ref, ioctl_with_ref}, -}; +use vmm_sys_util::errno; pub type Result = std::result::Result; -const KVM_REG_MAX_SIZE: u64 = 256; const KVM_NR_REGS: u64 = 31; const KVM_NR_FP_REGS: u64 = 32; @@ -132,71 +125,6 @@ impl From for u64 { } } -/// Returns the 128 bits value of the specified vCPU register. -/// -/// The id of the register is encoded as specified in the kernel documentation -/// for `KVM_GET_ONE_REG`. -/// -/// Max register size is 256 Bytes. -/// -/// # Arguments -/// -/// * `vcpu_fd` - The file descriptor of kvm_based vcpu. -/// * `reg_id` - ID of register. -pub fn get_one_reg_vec(vcpu_fd: &VcpuFd, reg_id: u64) -> Result> { - let reg_size = 1_u64 << ((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT); - if reg_size > KVM_REG_MAX_SIZE { - return Err(errno::Error::new(libc::EINVAL)); - } - let mut reg_value: Vec = vec![0; reg_size as usize]; - reg_value.resize(reg_size as usize, 0); - let mut onereg = kvm_one_reg { - id: reg_id, - addr: reg_value.as_mut_ptr() as *mut u8 as u64, - }; - - // This is safe because we allocated the struct and we know the kernel will read - // exactly the size of the struct. - let ret = unsafe { ioctl_with_mut_ref(vcpu_fd, KVM_GET_ONE_REG(), &mut onereg) }; - if ret < 0 { - return Err(errno::Error::last()); - } - - Ok(reg_value) -} - -/// Sets the value of one register for this vCPU. -/// -/// The id of the register is encoded as specified in the kernel documentation -/// for `KVM_SET_ONE_REG`. -/// -/// Max register size is 256 Bytes. -/// -/// # Arguments -/// -/// * `reg_id` - ID of the register for which we are setting the value. -/// * `data` - value for the specified register. -pub fn set_one_reg_vec(vcpu_fd: &VcpuFd, reg_id: u64, data: &[u8]) -> Result<()> { - let reg_size = 1u64 << ((reg_id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT); - if reg_size > KVM_REG_MAX_SIZE || reg_size as usize > data.len() { - return Err(errno::Error::new(libc::EINVAL)); - }; - let data_ref = data.as_ptr() as *const u8; - let onereg = kvm_one_reg { - id: reg_id, - addr: data_ref as u64, - }; - - // This is safe because we allocated the struct and we know the kernel will read - // exactly the size of the struct. - let ret = unsafe { ioctl_with_ref(vcpu_fd, KVM_SET_ONE_REG(), &onereg) }; - if ret < 0 { - return Err(errno::Error::last()); - } - - Ok(()) -} - /// Returns the vcpu's current `core_register`. /// /// The register state is gotten from `KVM_GET_ONE_REG` api in KVM. @@ -207,31 +135,28 @@ pub fn set_one_reg_vec(vcpu_fd: &VcpuFd, reg_id: u64, data: &[u8]) -> Result<()> pub fn get_core_regs(vcpu_fd: &VcpuFd) -> Result { let mut core_regs = kvm_regs::default(); - core_regs.regs.sp = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())?; - core_regs.sp_el1 = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())?; - core_regs.regs.pstate = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())?; - core_regs.regs.pc = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())?; - core_regs.elr_el1 = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())?; + core_regs.regs.sp = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; + core_regs.sp_el1 = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())? as u64; + core_regs.regs.pstate = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())? as u64; + core_regs.regs.pc = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())? as u64; + core_regs.elr_el1 = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())? as u64; for i in 0..KVM_NR_REGS as usize { - core_regs.regs.regs[i] = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())?; + core_regs.regs.regs[i] = + vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())? as u64; } for i in 0..KVM_NR_SPSR as usize { - core_regs.spsr[i] = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())?; + core_regs.spsr[i] = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; } for i in 0..KVM_NR_FP_REGS as usize { - let register_value_vec = - get_one_reg_vec(vcpu_fd, Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; - core_regs.fp_regs.vregs[i] = *u128::from_bytes(®ister_value_vec).unwrap(); + core_regs.fp_regs.vregs[i] = + vcpu_fd.get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; } - let register_value_vec = get_one_reg_vec(vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpsr.into())?; - core_regs.fp_regs.fpsr = *u32::from_bytes(®ister_value_vec[0..4]).unwrap(); - - let register_value_vec = get_one_reg_vec(vcpu_fd, Arm64CoreRegs::UserFPSIMDStateFpcr.into())?; - core_regs.fp_regs.fpcr = *u32::from_bytes(®ister_value_vec[0..4]).unwrap(); + core_regs.fp_regs.fpsr = vcpu_fd.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())? as u32; + core_regs.fp_regs.fpcr = vcpu_fd.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())? as u32; Ok(core_regs) } @@ -245,44 +170,40 @@ pub fn get_core_regs(vcpu_fd: &VcpuFd) -> Result { /// * `vcpu_fd` - the VcpuFd in KVM mod. /// * `core_regs` - kvm_regs state to be written. pub fn set_core_regs(vcpu_fd: &VcpuFd, core_regs: kvm_regs) -> Result<()> { - vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp)?; - vcpu_fd.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1)?; - vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegPState.into(), core_regs.regs.pstate)?; - vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc)?; - vcpu_fd.set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1)?; + vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; + vcpu_fd.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; + vcpu_fd.set_one_reg( + Arm64CoreRegs::UserPTRegPState.into(), + core_regs.regs.pstate as u128, + )?; + vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc as u128)?; + vcpu_fd.set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1 as u128)?; for i in 0..KVM_NR_REGS as usize { vcpu_fd.set_one_reg( Arm64CoreRegs::UserPTRegRegs(i).into(), - core_regs.regs.regs[i] as u64, + core_regs.regs.regs[i] as u128, )?; } for i in 0..KVM_NR_SPSR as usize { - vcpu_fd.set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i])?; + vcpu_fd.set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; } for i in 0..KVM_NR_FP_REGS as usize { - let mut data: Vec = Vec::new(); - data.append(&mut core_regs.fp_regs.vregs[i].as_bytes().to_vec()); - - set_one_reg_vec( - vcpu_fd, + vcpu_fd.set_one_reg( Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), - &data, + core_regs.fp_regs.vregs[i], )?; } - set_one_reg_vec( - vcpu_fd, + vcpu_fd.set_one_reg( Arm64CoreRegs::UserFPSIMDStateFpsr.into(), - core_regs.fp_regs.fpsr.as_bytes(), + core_regs.fp_regs.fpsr as u128, )?; - - set_one_reg_vec( - vcpu_fd, + vcpu_fd.set_one_reg( Arm64CoreRegs::UserFPSIMDStateFpcr.into(), - core_regs.fp_regs.fpcr.as_bytes(), + core_regs.fp_regs.fpcr as u128, )?; Ok(()) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index cf6e43292..678732b15 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -193,7 +193,7 @@ impl ArmCPUState { .with_context(|| "Failed to init kvm vcpu")?; self.mpidr = vcpu_fd .get_one_reg(SYS_MPIDR_EL1) - .with_context(|| "Failed to get mpidr")?; + .with_context(|| "Failed to get mpidr")? as u64; ArmCPUState::set_cpu_feature(vcpu_config, vcpu_fd)?; self.features = *vcpu_config; diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 8aa37fa46..7aa1354b2 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -534,8 +534,11 @@ impl CPUInterface for CPU { return Ok(false); } - VcpuExit::FailEntry => { - info!("Vcpu{} received KVM_EXIT_FAIL_ENTRY signal", self.id()); + VcpuExit::FailEntry(reason, cpuid) => { + info!( + "Vcpu{} received KVM_EXIT_FAIL_ENTRY signal. the vcpu could not be run due to unknown reasons({})", + cpuid, reason + ); return Ok(false); } VcpuExit::InternalError => { diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 27a079c3c..982dcc4cc 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -10,12 +10,12 @@ thiserror = "1.0" anyhow = "1.0" libc = "0.2" log = "0.4" -kvm-ioctls = ">=0.11.0" +kvm-ioctls = "0.12.0" serde = { version = "1.0", features = ["derive"] } -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 10f69f3cc..89c79d1c7 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -6,12 +6,12 @@ edition = "2021" license = "Mulan PSL v2" [dependencies] -arc-swap = ">=1.5.0" +arc-swap = "1.5.0" thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" log = "0.4" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" once_cell = "1.13.0" util = { path = "../util" } diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 8925a054a..0d0e3ec0f 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -11,12 +11,10 @@ // See the Mulan PSL v2 for more details. use std::mem::{align_of, size_of}; -use std::os::raw::c_ulong; use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; use util::bitmap::Bitmap; -use vmm_sys_util::ioctl::ioctl_with_val; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; use anyhow::{Context, Result}; @@ -39,18 +37,9 @@ const IOCHIP_NUM_PINS: u32 = 192; #[cfg(target_arch = "aarch64")] const KVM_IRQCHIP: u32 = 0; -/// Wrapper over `KVM_CHECK_EXTENSION`. -/// -/// Returns 0 if the capability is not available and a positive integer otherwise. -fn check_extension_int(kvmfd: &Kvm, c: Cap) -> i32 { - // Safe because we know that our file is a KVM fd and that the extension is one of the ones - // defined by kernel. - unsafe { ioctl_with_val(kvmfd, KVM_CHECK_EXTENSION(), c as c_ulong) } -} - /// Return the max number kvm supports. fn get_maximum_gsi_cnt(kvmfd: &Kvm) -> u32 { - let mut gsi_count = check_extension_int(kvmfd, Cap::IrqRouting); + let mut gsi_count = kvmfd.check_extension_int(Cap::IrqRouting); if gsi_count < 0 { gsi_count = 0; } diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index f4b0ab4b8..8857e13d0 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -308,7 +308,7 @@ Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: vmm-sys-util 0.10.0 +Software: vmm-sys-util 0.11.0 Copyright notice: Copyright 2019 Intel Corporation. All Rights Reserved. Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -350,7 +350,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Software: kvm-ioctls 0.11.0 +Software: kvm-ioctls 0.12.0 Copyright notice: Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. Portions Copyright 2017 The Chromium OS Authors. All rights reserved. @@ -359,7 +359,7 @@ Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: MIT or Apache License Version 2.0 Please see above. -Software: kvm-bindings 0.5.0 +Software: kvm-bindings 0.6.0 Copyright notice: Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: The APACHE 2.0 License diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 25cb197c8..d22d8e218 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -7,13 +7,13 @@ license = "Mulan PSL v2" description = "Emulation machines" [dependencies] -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" log = "0.4" libc = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" vfio-bindings = "0.3" thiserror = "1.0" anyhow = "1.0" diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index fcf59a285..21d2791d2 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -11,7 +11,7 @@ license = "Mulan PSL v2" log = "0.4" libc = "0.2" serde_json = "1.0" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" serde = { version = "1.0", features = ["derive"] } strum = "0.20" strum_macros = "0.20" diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 6fc6401b0..103994a64 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -5,11 +5,11 @@ authors = ["Huawei StratoVirt Team"] edition = "2021" [dependencies] -kvm-ioctls = ">=0.11.0" +kvm-ioctls = "0.12.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" once_cell = "1.13.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } log = "0.4" thiserror = "1.0" anyhow = "1.0" diff --git a/pci/Cargo.toml b/pci/Cargo.toml index 1517daceb..e2e248fd0 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -10,11 +10,11 @@ description = "PCI" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" libc = "0.2" log = "0.4" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" once_cell = "1.13.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml index 0e718ee37..59e97288d 100644 --- a/sysbus/Cargo.toml +++ b/sysbus/Cargo.toml @@ -9,8 +9,8 @@ description = "Emulate system bus" [dependencies] thiserror = "1.0" anyhow = "1.0" -kvm-ioctls = ">=0.11.0" -vmm-sys-util = ">=0.10.0" +kvm-ioctls = "0.12.0" +vmm-sys-util = "0.11.0" acpi = { path = "../acpi" } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/usb/Cargo.toml b/usb/Cargo.toml index a338b2c71..368eaaef2 100644 --- a/usb/Cargo.toml +++ b/usb/Cargo.toml @@ -10,7 +10,7 @@ description = "USB controller and device emulation" byteorder = "1.3.4" thiserror = "1.0" anyhow = "1.0" -libc = ">=0.2.71" +libc = "0.2" log = "0.4.8" once_cell = "1.9.0" address_space = { path = "../address_space" } diff --git a/util/Cargo.toml b/util/Cargo.toml index 4cd2617ca..6d1eca527 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -8,14 +8,14 @@ license = "Mulan PSL v2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -arc-swap = ">=1.5.0" +arc-swap = "1.5.0" thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" libc = "0.2" log = { version = "0.4", features = ["std"]} -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" byteorder = "1.4.3" once_cell = "1.13.0" io-uring = "0.5.7" diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index d6e7a5768..705d98772 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -10,11 +10,11 @@ description = "Virtual function I/O" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = ">=0.5.0", features = ["fam-wrappers"] } -kvm-ioctls = ">=0.11.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.12.0" libc = "0.2" log = "0.4" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" vfio-bindings = "0.3" once_cell = "1.13.0" address_space = { path = "../address_space" } diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 18aa273d0..a2294b796 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -10,12 +10,12 @@ description = "Provide virtio fs for VM" capng = "0.2.2" errno = "0.2.8" log = "0.4.8" -libc = ">=0.2.71" +libc = "0.2" thiserror = "1.0" anyhow = "1.0" libseccomp-sys = "0.2.1" error-chain = "0.12.4" -vmm-sys-util = ">=0.7.0" +vmm-sys-util = "0.11.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 19c8cfa39..78d33fdd5 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -10,11 +10,11 @@ description = "Virtio devices emulation" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-ioctls = ">=0.11.0" +kvm-ioctls = "0.12.0" libc = "0.2" log = "0.4" serde_json = "1.0" -vmm-sys-util = ">=0.10.0" +vmm-sys-util = "0.11.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index e42cd0b61..73b355cae 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -10,10 +10,10 @@ description = "Visual Network Computing" byteorder = "1.3.4" thiserror = "1.0" anyhow = "1.0" -libc = ">=0.2.71" +libc = "0.2" log = "0.4.8" serde_json = "1.0.55" -vmm-sys-util = ">=0.7.0" +vmm-sys-util = "0.11.0" once_cell = "1.9.0" sscanf = "0.2.1" rustls = "0.20.6" -- Gitee From 7e29ffbdac45339b44adbe215d092346085709d5 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 30 Nov 2022 21:11:47 +0800 Subject: [PATCH 0531/2187] Fix: Adapt to the new version of rust Currently, the Stratovirt reports errors when command(cargo clippy -- -D warnins) is executed in versions such as rust 1.64. The Stratovirt needs to be adapt to the new rust version. Signed-off-by: Mingwang Li --- acpi/src/table_loader.rs | 9 ++--- address_space/src/host_mmap.rs | 4 +- address_space/src/lib.rs | 40 ++++++++++--------- cpu/src/lib.rs | 2 +- devices/src/legacy/chardev.rs | 4 +- devices/src/legacy/ramfb.rs | 7 ++-- devices/src/legacy/rtc.rs | 2 +- machine/src/lib.rs | 9 ++--- machine/src/standard_vm/mod.rs | 19 ++++----- machine_manager/src/config/incoming.rs | 6 +-- machine_manager/src/config/pci.rs | 2 +- machine_manager/src/config/vnc.rs | 4 +- machine_manager/src/machine.rs | 2 +- machine_manager/src/qmp/mod.rs | 6 +-- machine_manager/src/socket.rs | 2 +- migration/src/protocol.rs | 10 ++--- ozone/src/cgroup.rs | 6 +-- ozone/src/handler.rs | 8 ++-- ozone/src/main.rs | 2 +- pci/src/config.rs | 2 +- src/main.rs | 4 +- util/src/aio/libaio.rs | 2 +- util/src/aio/mod.rs | 4 +- util/src/arg_parser.rs | 2 +- util/src/seccomp.rs | 6 +-- vhost_user_fs/src/main.rs | 2 +- virtio/src/lib.rs | 2 +- virtio/src/net.rs | 8 ++-- virtio/src/scsi/bus.rs | 13 +++--- virtio/src/vhost/kernel/net.rs | 4 +- virtio/src/vhost/kernel/vsock.rs | 2 +- virtio/src/vhost/user/message.rs | 2 +- virtio/src/vhost/user/net.rs | 4 +- vnc/src/auth.rs | 18 +++------ vnc/src/client.rs | 39 ++++++------------ vnc/src/encoding/enc_hextile.rs | 6 +-- vnc/src/input.rs | 11 ++---- vnc/src/server.rs | 11 ++---- vnc/src/vencrypt.rs | 55 +++++++++++--------------- vnc/src/vnc.rs | 15 +++---- 40 files changed, 155 insertions(+), 201 deletions(-) diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 053a05de8..21bf56f63 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -209,12 +209,9 @@ impl TableLoader { /// /// * `file_name` - The name of file to find. fn find_matched_file(&self, file_name: &str) -> Option<&TableLoaderFileEntry> { - for file_entry in &self.files { - if file_entry.file_name == file_name { - return Some(file_entry); - } - } - None + self.files + .iter() + .find(|&file_entry| file_entry.file_name == file_name) } /// Add loader entry of type `Allocate`. diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 8005d3c05..df4694c90 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -260,7 +260,7 @@ pub fn create_host_mmaps( }); } - let backend = (&f_back).as_ref(); + let backend = f_back.as_ref(); let mut host_addr = do_mmap( &backend.map(|fb| fb.file.as_ref()), mem_config.mem_size, @@ -387,7 +387,7 @@ impl HostMemMapping { let host_addr = if let Some(addr) = host_addr { addr } else { - let fb = (&file_back).as_ref(); + let fb = file_back.as_ref(); do_mmap( &fb.map(|f| f.file.as_ref()), size, diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 56402c930..047f86934 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -96,25 +96,29 @@ pub use listener::KvmMemoryListener; pub use listener::{Listener, ListenerReqType}; pub use region::{FlatRange, Region, RegionIoEventFd, RegionType}; +/// Read data from Region to argument `data`, +/// return `true` if read successfully, or return `false`. +/// +/// # Arguments +/// +/// * `data` - A u8-type array. +/// * `base` - Base address. +/// * `offset` - Offset from base address. +type ReadFn = std::sync::Arc bool + Send + Sync>; + +/// Write `data` to memory, +/// return `true` if write successfully, or return `false`. +/// +/// # Arguments +/// +/// * `data` - A u8-type array. +/// * `base` - Base address. +/// * `offset` - Offset from base address. +type WriteFn = std::sync::Arc bool + Send + Sync>; + /// Provide Some operations of `Region`, mainly used by Vm's devices. #[derive(Clone)] pub struct RegionOps { - /// Read data from Region to argument `data`, - /// return `true` if read successfully, or return `false`. - /// - /// # Arguments - /// - /// * `data` - A u8-type array. - /// * `base` - Base address. - /// * `offset` - Offset from base address. - pub read: std::sync::Arc bool + Send + Sync>, - /// Write `data` to memory, - /// return `true` if write successfully, or return `false`. - /// - /// # Arguments - /// - /// * `data` - A u8-type array. - /// * `base` - Base address. - /// * `offset` - Offset from base address. - pub write: std::sync::Arc bool + Send + Sync>, + pub read: ReadFn, + pub write: WriteFn, } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 8aa37fa46..d4a8c8f37 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -101,7 +101,7 @@ const MAGIC_VALUE_SIGNAL_GUEST_BOOT_START: u8 = 0x01; const MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE: u8 = 0x02; /// State for `CPU` lifecycle. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CpuLifecycleState { /// `CPU` structure is only be initialized, but nothing set. Nothing = 0, diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 53bb4b227..f718cc774 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -37,6 +37,8 @@ pub trait InputReceiver: Send { fn get_remain_space_size(&mut self) -> usize; } +type ReceFn = Option>; + /// Character device structure. pub struct Chardev { /// Id of chardev. @@ -52,7 +54,7 @@ pub struct Chardev { /// Fd of socket stream. pub stream_fd: Option, /// Handle the input data and trigger interrupt if necessary. - receive: Option>, + receive: ReceFn, /// Return the remain space size of receiver buffer. get_remain_space_size: Option usize + Send + Sync>>, } diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index cbc569db4..edcf1fb13 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -174,13 +174,12 @@ impl FwCfgWriteCallback for RamfbState { .unwrap(), ); - let format: pixman_format_code_t; - if fourcc == DrmFourcc::Xrgb8888 as u32 { - format = pixman_format_code_t::PIXMAN_x8r8g8b8; + let format: pixman_format_code_t = if fourcc == DrmFourcc::Xrgb8888 as u32 { + pixman_format_code_t::PIXMAN_x8r8g8b8 } else { error!("Unsupported drm format: {}", fourcc); return; - } + }; self.create_display_surface(width, height, format, stride, addr); diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 415b4972e..b6c75d7ac 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -338,7 +338,7 @@ impl RTC { + bcd_to_bin(self.cmos_data[RTC_CENTURY_BCD as usize]) * 100; // Check rtc time is valid to prevent tick_offset overflow. - if year < 1970 || mon > 12 || mon < 1 || day > 31 || day < 1 { + if year < 1970 || !(1..=12).contains(&mon) || !(1..=31).contains(&day) { warn!( "RTC: the updated rtc time {}-{}-{} may be invalid.", year, mon, day diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a7e2be545..9c26906ba 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -779,12 +779,11 @@ pub trait MachineOps { multifunc: bool, ) -> Result<()> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; - let path; - if !host.is_empty() { - path = format!("/sys/bus/pci/devices/{}", host); + let path = if !host.is_empty() { + format!("/sys/bus/pci/devices/{}", host) } else { - path = sysfsdev.to_string(); - } + sysfsdev.to_string() + }; let device = VfioDevice::new(Path::new(&path), self.get_sys_mem()) .with_context(|| "Failed to create vfio device.")?; let vfio_pci = VfioPciDevice::new( diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 165cc4cad..943276094 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -930,20 +930,17 @@ impl StdMachine { bdf: &PciBdf, args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { - let host; - let sysfsdev; - - if args.host.is_none() { - host = ""; + let host = if args.host.is_none() { + "" } else { - host = args.host.as_ref().unwrap(); - } + args.host.as_ref().unwrap() + }; - if args.sysfsdev.is_none() { - sysfsdev = ""; + let sysfsdev = if args.sysfsdev.is_none() { + "" } else { - sysfsdev = args.sysfsdev.as_ref().unwrap(); - } + args.sysfsdev.as_ref().unwrap() + }; if args.host.is_none() && args.sysfsdev.is_none() { bail!("Neither option \"host\" nor \"sysfsdev\" was not provided."); diff --git a/machine_manager/src/config/incoming.rs b/machine_manager/src/config/incoming.rs index 290040807..e967617b8 100644 --- a/machine_manager/src/config/incoming.rs +++ b/machine_manager/src/config/incoming.rs @@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize}; use super::VmConfig; -#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)] pub enum MigrateMode { File, Unix, @@ -55,10 +55,10 @@ pub fn parse_incoming_uri(uri: &str) -> Result<(MigrateMode, String)> { bail!("Invalid ip port {}", parse_vec[2]); } - return Ok(( + Ok(( MigrateMode::Tcp, format!("{}:{}", parse_vec[1], parse_vec[2]), - )); + )) } _ => bail!("Invalid incoming uri {}", uri), diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index ffdd5c14d..5df10e670 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -81,7 +81,7 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { addr_vec.len() ); } - let slot = addr_vec.get(0).unwrap(); + let slot = addr_vec.first().unwrap(); let without_prefix = slot.trim_start_matches("0x"); let slot = u8::from_str_radix(without_prefix, 16) .with_context(|| format!("Invalid slot num: {}", slot))?; diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index 3a065b910..61a40adc8 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -49,9 +49,7 @@ impl VmConfig { let mut vnc_config = VncConfig::default(); // Parse Ip:Port. if let Some(addr) = cmd_parser.get_value::("")? { - if let Err(e) = parse_port(&mut vnc_config, addr) { - return Err(e); - } + parse_port(&mut vnc_config, addr)?; } else { return Err(anyhow!(ConfigError::FieldIsMissing("ip", "port"))); } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 865edb70e..ea468621d 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -30,7 +30,7 @@ pub struct PathInfo { } /// State for KVM VM. -#[derive(PartialEq, Copy, Clone, Debug)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum KvmVmState { Created = 1, Running = 2, diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 6615ed38c..305b82b5e 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -221,7 +221,7 @@ impl QmpGreeting { /// /// It contains two kind response: `BadResponse` and `GoodResponse`. This two /// kind response are fit by executing qmp command by success and failure. -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct Response { #[serde(rename = "return", default, skip_serializing_if = "Option::is_none")] return_: Option, @@ -289,7 +289,7 @@ impl From for Response { } /// `ErrorMessage` for Qmp Response. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct ErrorMessage { #[serde(rename = "class")] errorkind: String, @@ -311,7 +311,7 @@ impl ErrorMessage { } /// Empty message for QMP. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct Empty {} /// Command trait for Deserialize and find back Response. diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 7da0eba89..7c9f439de 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -287,7 +287,7 @@ impl EventNotifierHelper for Socket { } /// Type for api socket. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SocketType { Unix = 1, } diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 8888e1d13..5d435825e 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -33,7 +33,7 @@ use util::byte_code::ByteCode; /// Failed ---------> Setup: reset migration resource. /// Any ------------> Failed: something wrong in migration. /// Any ------------> Canceled: cancel migration. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum MigrationStatus { /// Migration resource is not prepared all None, @@ -111,7 +111,7 @@ impl MigrationStatus { /// Structure defines the transmission protocol between the source with destination VM. #[repr(u16)] -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum TransStatus { /// Active migration. Active, @@ -297,7 +297,7 @@ pub const HEADER_LENGTH: usize = 4096; /// Format type for migration. /// Different file format will have different file layout. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FileFormat { Device, MemoryFull, @@ -458,7 +458,7 @@ impl MigrationHeader { } /// Version check result enum. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Eq, Debug)] pub enum VersionCheck { /// Version is completely same. Same, @@ -518,7 +518,7 @@ pub struct DeviceStateDesc { } /// The structure to describe struct field in `DeviceState` structure. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct FieldDesc { /// Field var name. pub var_name: String, diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index 23348f4cf..94fe01a4b 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -151,10 +151,10 @@ fn write_cgroup_value(path: &Path, file: &str, value: &str) -> Result<()> { } let mut path_to_write = path.to_path_buf(); - path_to_write.push(&file); + path_to_write.push(file); fs::write(&path_to_write, format!("{}\n", value)).with_context(|| { OzoneError::WriteError( - (&path_to_write.to_string_lossy()).to_string(), + path_to_write.to_string_lossy().to_string(), value.to_string(), ) })?; @@ -191,7 +191,7 @@ fn inherit_config(path: &Path, file: &str) -> Result<()> { } fs::write(upper_file.clone(), format!("{}\n", upper_value)).with_context(|| { OzoneError::WriteError( - (&upper_file.to_string_lossy()).to_string(), + upper_file.to_string_lossy().to_string(), upper_value.to_string(), ) })?; diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 8ed45cb79..e5300f6da 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -79,7 +79,7 @@ impl OzoneHandler { handler.name = name; } if let Some(uid) = args.value_of("uid") { - let user_id = (&uid) + let user_id = (uid) .parse::() .map_err(|_| anyhow!(OzoneError::DigitalParseError("uid", uid)))?; if user_id > MAX_ID_NUMBER { @@ -88,7 +88,7 @@ impl OzoneHandler { handler.uid = user_id; } if let Some(gid) = args.value_of("gid") { - let group_id = (&gid) + let group_id = (gid) .parse::() .map_err(|_| anyhow!(OzoneError::DigitalParseError("gid", gid)))?; if group_id > MAX_ID_NUMBER { @@ -111,7 +111,7 @@ impl OzoneHandler { } if let Some(node) = args.value_of("numa") { handler.node = Some( - (&node) + (node) .parse::() .map_err(|_| anyhow!(OzoneError::DigitalParseError("numa", node)))?, ); @@ -261,7 +261,7 @@ impl OzoneHandler { namespace::set_mount_namespace(self.chroot_dir.to_str().unwrap())?; for folder in NEWROOT_FOLDERS.iter() { - self.create_newroot_folder(*folder)?; + self.create_newroot_folder(folder)?; } for index in 0..NEWROOT_DEVICE_NR { diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 649b9234a..3be2a5dbe 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -47,7 +47,7 @@ fn main() { ::std::process::exit(match run() { Ok(ret) => ExitCode::code(ret), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format!("{:?}", e)) + write!(&mut ::std::io::stderr(), "{}", format_args!("{:?}", e)) .expect("Error writing to stderr"); 1 diff --git a/pci/src/config.rs b/pci/src/config.rs index 805221e65..475c57851 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -286,7 +286,7 @@ pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; /// Type of bar region. -#[derive(PartialEq, Debug, Copy, Clone)] +#[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum RegionType { Io, Mem32Bit, diff --git a/src/main.rs b/src/main.rs index 1eb7c0636..9cc02e10b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,7 +77,7 @@ fn main() { ::std::process::exit(match run() { Ok(ret) => ExitCode::code(ret), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format!("{:?}\r\n", e)) + write!(&mut ::std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) .expect("Error writing to stderr"); 1 @@ -136,7 +136,7 @@ fn run() -> Result<()> { if cmd_args.is_present("display log") { error!("{}", format!("{:?}", e)); } else { - write!(&mut std::io::stderr(), "{}", format!("{:?}", e)) + write!(&mut std::io::stderr(), "{}", format_args!("{:?}", e)) .expect("Failed to write to stderr"); } // clean temporary file diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 9790697c7..6ea1bc58a 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -45,7 +45,7 @@ pub struct IoCb { #[repr(C)] #[allow(non_camel_case_types)] -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum IoCmd { Pread = 0, Pwrite = 1, diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 083f15805..71dd9e570 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -194,7 +194,7 @@ impl Aio { if is_err { // Fail one request, retry the rest. if let Some(node) = self.aio_in_queue.pop_tail() { - (self.complete_func)(&(*node).value, -1); + (self.complete_func)(&(node).value, -1); } } else if nr == 0 { // If can't submit any request, break the loop @@ -216,7 +216,7 @@ impl Aio { let mut iocb = IoCb { aio_lio_opcode: cb.opcode as u16, aio_fildes: cb.file_fd as u32, - aio_buf: (&*cb.iovec).as_ptr() as u64, + aio_buf: (*cb.iovec).as_ptr() as u64, aio_nbytes: cb.iovec.len() as u64, aio_offset: cb.offset as u64, aio_flags: IOCB_FLAG_RESFD, diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index dec8061a5..424f8bc87 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -35,7 +35,7 @@ const TWENTY_FOUT_BLANK: &str = " "; type ArgsMap = BTreeMap>; /// Format help type. -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Eq, Debug)] pub enum HelpType { /// Argument as a Flag. Flags, diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 5705ef722..03c84d097 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -136,7 +136,7 @@ const AUDIT_ARCH_X86_64: u32 = EM_X86_64 | __AUDIT_ATCH_64BIT | __AUDIT_ARCH_LE; const AUDIT_ARCH_AARCH64: u32 = EM_AARCH64 | __AUDIT_ATCH_64BIT | __AUDIT_ARCH_LE; /// Compared operator in bpf filter rule. -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum SeccompCmpOpt { /// Equal. Eq, @@ -158,7 +158,7 @@ pub enum SeccompCmpOpt { /// These operation one-to-one correspondence with BPF-filter return value: /// `SECCOMP_RET_KILL_PROCESS`, `SECCOMP_RET_KILL_THREAD`, `SECCOMP_RET_TRAP`, /// `SECCOMP_RET_ERRNO`, `SECCOMP_RET_TRACE`, `SECCOMP_RET_ALLOW`, `SECCOMP_RET_LOG`. -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum SeccompOpt { /// Kill the task immediately. Kill, @@ -220,7 +220,7 @@ impl SeccompData { /// /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/filter.h#L24 #[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct SockFilter { /// Actual filter code code: u16, diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index cacad4e3e..b8549d1d7 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -66,7 +66,7 @@ fn main() { ::std::process::exit(match run() { Ok(ret) => ExitCode::code(ret), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format!("{:?}\r\n", e)) + write!(&mut ::std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) .expect("Error writing to stderr"); 1 diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 560af1e4a..56272a812 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -248,7 +248,7 @@ pub const NOTIFY_REG_OFFSET: u32 = 0x50; /// Packet header, refer to Virtio Spec. #[repr(C)] -#[derive(Debug, Default, Copy, Clone, PartialEq)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub struct VirtioNetHdr { pub flags: u8, pub gso_type: u8, diff --git a/virtio/src/net.rs b/virtio/src/net.rs index f051cfdc3..50987e6ff 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -163,7 +163,7 @@ impl CtrlInfo { &mut self, mem_space: &AddressSpace, cmd: u8, - data_iovec: &mut Vec, + data_iovec: &mut [ElemIovec], ) -> Result { // Get the command specific data, one byte containing 0(off) or 1(on). let mut status: u8 = 0; @@ -389,7 +389,7 @@ impl CtrlInfo { fn get_buf_and_discard( mem_space: &AddressSpace, - iovec: &mut Vec, + iovec: &mut [ElemIovec], buf: &mut [u8], ) -> Result> { iov_to_buf(mem_space, iovec, buf).and_then(|size| { @@ -1331,8 +1331,8 @@ impl VirtioDevice for Net { let queue_pairs = self.net_cfg.queues / 2; if self.net_cfg.mq - && queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN - && queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX + && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + .contains(&queue_pairs) { locked_state.device_features |= 1 << VIRTIO_NET_F_MQ; locked_state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index ccb070f40..ce5158457 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -520,12 +520,11 @@ impl ScsiRequest { debug!("scsi command is {:#x}", self.cmd.command); let mut not_supported_flag = false; let mut sense = None; - let result; // Requested lun id is not equal to found device id means it may be a target request. // REPORT LUNS is also a target request command. - if req_lun_id != found_lun_id || self.cmd.command == REPORT_LUNS { - result = match self.cmd.command { + let result = if req_lun_id != found_lun_id || self.cmd.command == REPORT_LUNS { + match self.cmd.command { REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), INQUIRY => scsi_command_emulate_target_inquiry(req_lun_id, &self.cmd), REQUEST_SENSE => { @@ -541,10 +540,10 @@ impl ScsiRequest { sense = Some(SCSI_SENSE_INVALID_OPCODE); Err(anyhow!("Invalid emulation target scsi command")) } - }; + } } else { // It's not a target request. - result = match self.cmd.command { + match self.cmd.command { REQUEST_SENSE => { sense = Some(SCSI_SENSE_NO_SENSE); Ok(Vec::new()) @@ -568,8 +567,8 @@ impl ScsiRequest { not_supported_flag = true; Err(anyhow!("Emulation scsi command is not supported now!")) } - }; - } + } + }; match result { Ok(outbuf) => { diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index dac20739b..d782c4597 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -149,8 +149,8 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_UFO; if self.net_cfg.mq - && queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN - && queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX + && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + .contains(&queue_pairs) { device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; device_features |= 1 << VIRTIO_NET_F_MQ; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index ba8b51443..61e7cfbad 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -61,7 +61,7 @@ impl VhostVsockBackend for VhostBackend { } fn set_running(&self, start: bool) -> Result<()> { - let on: u32 = if start { 1 } else { 0 }; + let on: u32 = u32::from(start); let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_RUNNING(), &on) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 96f81e9fe..4c807f8d4 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -202,7 +202,7 @@ impl VhostUserConfig { /// Memory region information for the message of memory table. #[repr(C)] -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct RegionMemInfo { /// Guest physical address of the memory region. pub guest_phys_addr: u64, diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 1638d8204..dfee7ae71 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -144,8 +144,8 @@ impl VirtioDevice for Net { let queue_pairs = self.net_cfg.queues / 2; if self.net_cfg.mq - && queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN - && queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX + && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + .contains(&queue_pairs) { self.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; self.device_features |= 1 << VIRTIO_NET_F_MQ; diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 0886b5b92..f1d7b517f 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -103,7 +103,7 @@ impl Default for SaslConfig { } /// Authentication stage. -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum SaslStage { SaslServerStart, SaslServerStep, @@ -137,17 +137,11 @@ impl ClientIoHandler { /// 2. Get the mechlist support by Sasl server. /// 3. Send the mechlist to client. pub fn start_sasl_auth(&mut self) -> Result<()> { - if let Err(e) = self.sasl_server_init() { - return Err(e); - } + self.sasl_server_init()?; - if let Err(e) = self.set_ssf_for_sasl() { - return Err(e); - } + self.set_ssf_for_sasl()?; - if let Err(e) = self.send_mech_list() { - return Err(e); - } + self.send_mech_list()?; Ok(()) } @@ -311,13 +305,13 @@ impl ClientIoHandler { .local_addr() .unwrap() .to_string() - .replace(":", ";"); + .replace(':', ";"); let remote_addr = self .stream .peer_addr() .unwrap() .to_string() - .replace(":", ";"); + .replace(':', ";"); info!("local_addr: {} remote_addr: {}", local_addr, remote_addr); let local_addr = CString::new(local_addr).unwrap(); let remote_addr = CString::new(remote_addr).unwrap(); diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 94b7dc8d6..9abba6846 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -129,7 +129,7 @@ impl Default for VncVersion { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum UpdateState { No, Incremental, @@ -323,15 +323,11 @@ impl ClientIoHandler { impl ClientIoHandler { fn client_handle_read(&mut self) -> Result<(), anyhow::Error> { // Read message from tcpstream. - if let Err(e) = self.read_msg() { - return Err(e); - } + self.read_msg()?; let client = self.client.clone(); while client.in_buffer.lock().unwrap().len() >= self.expect { - if let Err(e) = (self.msg_handler)(self) { - return Err(e); - } + (self.msg_handler)(self)?; if self.client.conn_state.lock().unwrap().dis_conn { return Err(anyhow!(VncError::Disconnection)); @@ -391,11 +387,10 @@ impl ClientIoHandler { if self.tls_conn.is_none() { return Ok(0_usize); } - let tc: &mut ServerConnection; - match &mut self.tls_conn { - Some(sc) => tc = sc, + let tc: &mut ServerConnection = match &mut self.tls_conn { + Some(sc) => sc, None => return Ok(0_usize), - } + }; if let Err(e) = tc.read_tls(&mut self.stream) { error!("tls_conn read error {:?}", e); @@ -449,13 +444,12 @@ impl ClientIoHandler { let buf_size = buf.len(); let mut offset = 0; - let tc: &mut ServerConnection; - match &mut self.tls_conn { - Some(ts) => tc = ts, + let tc: &mut ServerConnection = match &mut self.tls_conn { + Some(ts) => ts, None => { return; } - } + }; while offset < buf_size { let next = cmp::min(buf_size, offset + MAX_SEND_LEN); @@ -505,17 +499,14 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); let res = String::from_utf8_lossy(&buf); let ver_str = &res[0..12].to_string(); - let ver; - match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { - Ok(v) => { - ver = v; - } + let ver = match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { + Ok(v) => v, Err(e) => { let msg = format!("Unsupport RFB version: {}", e); error!("{}", msg); return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } - } + }; let mut version = VncVersion::new(ver.0 as u16, ver.1 as u16); if version.major != 3 || ![3, 4, 5, 7, 8].contains(&version.minor) { @@ -1165,11 +1156,7 @@ pub fn is_need_update(client: &Arc) -> bool { pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { let mut locked_dpm = client.client_dpm.lock().unwrap(); locked_dpm.pf.init_pixelformat(); - let big_endian: u8 = if cfg!(target_endian = "big") { - 1_u8 - } else { - 0_u8 - }; + let big_endian: u8 = u8::from(cfg!(target_endian = "big")); buf.append(&mut (locked_dpm.pf.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. buf.append(&mut (locked_dpm.pf.depth as u8).to_be_bytes().to_vec()); // Depth. buf.append(&mut (big_endian as u8).to_be_bytes().to_vec()); // Big-endian flag. diff --git a/vnc/src/encoding/enc_hextile.rs b/vnc/src/encoding/enc_hextile.rs index cc50a2a3b..5bdc30eab 100644 --- a/vnc/src/encoding/enc_hextile.rs +++ b/vnc/src/encoding/enc_hextile.rs @@ -93,11 +93,11 @@ fn compress_each_tile<'a>( let mut n_subtiles = 0; // Number of subrectangle. let mut tmp_buf: Vec = Vec::new(); - if *last_bg == None || Some(bg) != *last_bg { + if last_bg.is_none() || Some(bg) != *last_bg { flag |= BACKGROUND_SPECIFIC; *last_bg = Some(bg); } - if n_colors < 3 && (*last_fg == None || Some(fg) != *last_fg) { + if n_colors < 3 && (last_fg.is_none() || Some(fg) != *last_fg) { flag |= FOREGROUND_SPECIFIC; *last_fg = Some(fg); } @@ -110,7 +110,7 @@ fn compress_each_tile<'a>( } 3 => { flag |= ANY_SUBRECTS | SUBRECTS_COLOURED; - if *last_bg == None || Some(bg) != *last_bg { + if last_bg.is_none() || Some(bg) != *last_bg { flag |= BACKGROUND_SPECIFIC; } n_subtiles = subrectangle_with_pixel_value( diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 586fd7aae..b742cc5e5 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -53,19 +53,16 @@ impl ClientIoHandler { keysym += UPPERCASE_TO_LOWERCASE; } - let keycode: u16; - match self + let keycode: u16 = match self .server .keysym2keycode .lock() .unwrap() .get(&(keysym as u16)) { - Some(k) => keycode = *k, - None => { - keycode = 0; - } - } + Some(k) => *k, + None => 0, + }; self.do_key_event(down, keycode); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 35625de56..4cb1e2533 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -609,18 +609,13 @@ pub fn make_server_config( object: &ObjectConfig, ) -> Result<()> { // Set security config. - if let Err(e) = server + server .security_type .lock() .unwrap() - .set_security_config(vnc_cfg, object) - { - return Err(e); - } + .set_security_config(vnc_cfg, object)?; // Set auth type. - if let Err(e) = server.security_type.lock().unwrap().set_auth() { - return Err(e); - } + server.security_type.lock().unwrap().set_auth()?; let mut locked_keysym2keycode = server.keysym2keycode.lock().unwrap(); // Mapping ASCII to keycode. for &(k, v) in KEYSYM2KEYCODE.iter() { diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 739129a9c..2d0070302 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -179,9 +179,7 @@ impl ClientIoHandler { } else { info!("Finished tls handshaking"); // Tls handshake finished. - if let Err(e) = self.handle_vencrypt_subauth() { - return Err(e); - } + self.handle_vencrypt_subauth()?; } } else { return Err(anyhow!(VncError::AuthFailed(String::from( @@ -197,9 +195,7 @@ impl ClientIoHandler { SubAuthState::VncAuthVencryptX509Sasl => { self.expect = 4; self.msg_handler = ClientIoHandler::get_mechname_length; - if let Err(e) = self.start_sasl_auth() { - return Err(e); - } + self.start_sasl_auth()?; } SubAuthState::VncAuthVencryptX509None => { let buf = [0u8; 4]; @@ -239,11 +235,10 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result // Load cacert.pem and provide verification for certificate chain let client_auth = if args.verifypeer { - let roots; - match load_certs(server_cacert.as_str()) { - Ok(r) => roots = r, + let roots = match load_certs(server_cacert.as_str()) { + Ok(r) => r, Err(e) => return Err(e), - } + }; let mut client_auth_roots = RootCertStore::empty(); for root in roots { client_auth_roots.add(&root).unwrap(); @@ -261,18 +256,16 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result let suites = TLS_CIPHER_SUITES.to_vec(); // Tls protocol version supported by server. let versions = TLS_VERSIONS.to_vec(); - let certs: Vec; - let privkey: rustls::PrivateKey; // Server certificate. - match load_certs(server_cert.as_str()) { - Ok(c) => certs = c, + let certs: Vec = match load_certs(server_cert.as_str()) { + Ok(c) => c, Err(e) => return Err(e), }; // Server private key. - match load_private_key(server_key.as_str()) { - Ok(key) => privkey = key, + let privkey: rustls::PrivateKey = match load_private_key(server_key.as_str()) { + Ok(key) => key, Err(e) => return Err(e), - } + }; let mut config = rustls::ServerConfig::builder() .with_cipher_suites(&suites) @@ -300,23 +293,22 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result /// /// * `filepath` - the path private key. fn load_private_key(filepath: &str) -> Result { - let keyfile; - match File::open(filepath) { - Ok(file) => keyfile = file, + let file = match File::open(filepath) { + Ok(file) => file, Err(e) => { - error!("Private key file is no exit!: {}", e); + error!("Can not open file of the private key!: {}", e); return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "Private key file is no exit!", + "File of the private key is no exit!", )))); } - } + }; - let mut reader = BufReader::new(keyfile); + let mut reader = BufReader::new(file); loop { - match rustls_pemfile::read_one(&mut reader).expect("Cannot parse private key .pem file") { - Some(rustls_pemfile::Item::RSAKey(key)) => return Ok(rustls::PrivateKey(key)), - Some(rustls_pemfile::Item::PKCS8Key(key)) => return Ok(rustls::PrivateKey(key)), - Some(rustls_pemfile::Item::ECKey(key)) => return Ok(rustls::PrivateKey(key)), + match rustls_pemfile::read_one(&mut reader).expect("Cannot parse .pem file") { + Some(rustls_pemfile::Item::RSAKey(ras)) => return Ok(rustls::PrivateKey(ras)), + Some(rustls_pemfile::Item::PKCS8Key(pkcs8)) => return Ok(rustls::PrivateKey(pkcs8)), + Some(rustls_pemfile::Item::ECKey(ec)) => return Ok(rustls::PrivateKey(ec)), None => break, _ => {} } @@ -334,16 +326,15 @@ fn load_private_key(filepath: &str) -> Result { /// /// * `filepath` - the file path of certificate. fn load_certs(filepath: &str) -> Result> { - let certfile; - match File::open(filepath) { - Ok(file) => certfile = file, + let certfile = match File::open(filepath) { + Ok(file) => file, Err(e) => { error!("Cannot open certificate file: {}", e); return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( "Cannot open certificate file", )))); } - } + }; let mut reader = BufReader::new(certfile); let certs = rustls_pemfile::certs(&mut reader) .unwrap() diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 6506c09f0..924b8da86 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -105,15 +105,14 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { } let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); - let listener: TcpListener; - match TcpListener::bind(&addr.as_str()) { - Ok(l) => listener = l, + let listener: TcpListener = match TcpListener::bind(addr.as_str()) { + Ok(l) => l, Err(e) => { let msg = format!("Bind {} failed {}", addr, e); error!("{}", e); return Err(anyhow!(VncError::TcpBindFailed(msg))); } - } + }; listener .set_nonblocking(true) @@ -122,18 +121,14 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { let refresh_fd = Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); let server = Arc::new(VncServer::new(refresh_fd, get_client_image())); // Parameter configuation for VncServeer. - if let Err(err) = make_server_config(&server, vnc_cfg, object) { - return Err(err); - } + make_server_config(&server, vnc_cfg, object)?; // Add an VncServer. add_vnc_server(server.clone()); // Register the event to listen for client's connection. let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server))); // Vnc_thread: a thread to send the framebuffer - if let Err(err) = start_vnc_thread() { - return Err(err); - } + start_vnc_thread()?; EventLoop::update_event(EventNotifierHelper::internal_notifiers(vnc_io), None)?; Ok(()) -- Gitee From 5425a25765010c5617d979ebd23ce158b8e9a4ca Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 1 Dec 2022 16:09:03 +0800 Subject: [PATCH 0532/2187] hydropper: Remove useless code StratoVirt directly passes the name of the host cpu model to guest, and the test case does not need to distinguish cpu vendor. Signed-off-by: Gan Qixin --- .../functional/test_microvm_cpu_features.py | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py index 768ec74fc..6874c7aa6 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py @@ -21,31 +21,6 @@ LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" logging.basicConfig(filename='/var/log/pytest.log', level=logging.DEBUG, format=LOG_FORMAT) -class CpuVendor(Enum): - """CPU vendors enum.""" - - AMD = auto() - INTEL = auto() - - -def _get_cpu_vendor(): - cif = open('/proc/cpuinfo', 'r') - host_vendor_id = None - while True: - line = cif.readline() - if line == '': - break - matchoutput = re.search("^vendor_id\\s+:\\s+(.+)$", line) - if matchoutput: - host_vendor_id = matchoutput.group(1) - cif.close() - assert host_vendor_id is not None - - if host_vendor_id == "AuthenticAMD": - return CpuVendor.AMD - return CpuVendor.INTEL - - def _check_guest_cmd_output(microvm, guest_cmd, expected_header, expected_separator, expected_key_value_store): @@ -143,9 +118,6 @@ def test_128vcpu_topo(microvm): @pytest.mark.acceptance def test_brand_string(microvm): """Ensure the guest band string is correct. - In x86_64 platform, the guest brand string is: - - Intel(R) Xeon(R) Processor @ {host frequency} """ branch_string_format = "^model name\\s+:\\s+(.+)$" host_brand_string = None @@ -169,13 +141,7 @@ def test_brand_string(microvm): assert matchoutput guest_brand_string = matchoutput.group(1) assert guest_brand_string - - cpu_vendor = _get_cpu_vendor() - expected_guest_brand_string = "" - if cpu_vendor == CpuVendor.INTEL: - expected_guest_brand_string = host_brand_string - - assert guest_brand_string == expected_guest_brand_string + assert guest_brand_string == host_brand_string @pytest.mark.skipif("platform.machine().startswith('x86_64')") -- Gitee From a87d5ba0e03774aa945d2420f7010ebe82123cfe Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 1 Dec 2022 18:01:43 +0800 Subject: [PATCH 0533/2187] hydropper: Use lscpu -J to get cpu info Obtain the information in json format through "lscpu -J", which makes it easier to parse. Signed-off-by: Gan Qixin --- .../functional/test_microvm_cpu_features.py | 80 +++++++------------ 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py index 6874c7aa6..b955b559a 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cpu_features.py @@ -14,6 +14,7 @@ import platform import logging import re +import json from enum import Enum from enum import auto import pytest @@ -21,67 +22,42 @@ LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" logging.basicConfig(filename='/var/log/pytest.log', level=logging.DEBUG, format=LOG_FORMAT) -def _check_guest_cmd_output(microvm, guest_cmd, expected_header, - expected_separator, - expected_key_value_store): - status, output = microvm.serial_cmd(guest_cmd) +def _parse_output(output): - assert status == 0 - for line in output.splitlines(): - line = line.strip() - if line != '': - # all the keys have been matched. Stop. - if not expected_key_value_store: - break - - # try to match the header if needed. - if expected_header not in (None, ''): - if line.strip() == expected_header: - expected_header = None - continue - - # see if any key matches. - # we use a try-catch block here since line.split() may fail. - try: - [key, value] = list( - map(lambda x: x.strip(), line.split(expected_separator))) - except ValueError: - continue - - if key in expected_key_value_store.keys(): - assert value == expected_key_value_store[key], \ - "%s does not have the expected value" % key - del expected_key_value_store[key] - - else: - break - - assert not expected_key_value_store, \ - "some keys in dictionary have not been found in the output: %s" \ - % expected_key_value_store + cpu_info = {} + for item in output: + cpu_info.update({item['field']: item['data']}) + cpu_info.update(_parse_output(item.get('children', []))) + return cpu_info + +def _get_cpu_info(test_microvm): + output = json.loads(test_microvm.ssh_session.cmd_output("lscpu -J")) + return _parse_output(output.get("lscpu", [])) def _check_cpu_topology(test_microvm, expected_cpu_count, expected_threads_per_core, expected_cores_per_socket, expected_cpus_list): + expected_cpu_topology = { - "CPU(s)": str(expected_cpu_count), - "On-line CPU(s) list": expected_cpus_list, - "Thread(s) per core": str(expected_threads_per_core), - "Core(s) per socket": str(expected_cores_per_socket), - "Socket(s)": str(int(expected_cpu_count / expected_cores_per_socket / expected_threads_per_core)), + "CPU(s):": str(expected_cpu_count), + "On-line CPU(s) list:": expected_cpus_list, + "Thread(s) per core:": str(expected_threads_per_core), + "Core(s) per socket:": str(expected_cores_per_socket), + "Socket(s):": str(int(expected_cpu_count / expected_cores_per_socket / expected_threads_per_core)), } - status, output = test_microvm.serial_cmd("lscpu") - assert status == 0, str(output) - if "Core(s) per cluster" in output and "aarch64" in platform.machine(): - expected_cpu_topology["Core(s) per cluster"] = expected_cpu_topology["Core(s) per socket"] - del expected_cpu_topology["Core(s) per socket"] - expected_cpu_topology["Cluster(s)"] = expected_cpu_topology["Socket(s)"] - del expected_cpu_topology["Socket(s)"] - - _check_guest_cmd_output(test_microvm, "lscpu", None, ':', - expected_cpu_topology) + + cpu_info = _get_cpu_info(test_microvm) + if "Core(s) per cluster:" in cpu_info.keys(): + expected_cpu_topology["Core(s) per cluster:"] = expected_cpu_topology["Core(s) per socket:"] + del expected_cpu_topology["Core(s) per socket:"] + if "Cluster(s):" in cpu_info.keys(): + expected_cpu_topology["Cluster(s):"] = expected_cpu_topology["Socket(s):"] + del expected_cpu_topology["Socket(s):"] + + for key, expect_value in expected_cpu_topology.items(): + assert cpu_info[key] == expect_value @pytest.mark.acceptance -- Gitee From 0e527b31ef5be8c3ca278fdd43fa7b283cf57904 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 29 Nov 2022 18:58:04 +0800 Subject: [PATCH 0534/2187] VNC: Init the variable of update_interval. Before initialization, the update_interval of timer is 0, result in the waste of computing resource. Signed-off-by: Xiao Ye --- vnc/src/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 4cb1e2533..8bb799da7 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -88,7 +88,7 @@ impl VncServer { vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), vnc_cursor: Arc::new(Mutex::new(VncCursor::default())), conn_limits: CONNECTION_LIMIT, - update_interval: Arc::new(Mutex::new(0_u32)), + update_interval: Arc::new(Mutex::new(DISPLAY_UPDATE_INTERVAL_DEFAULT as u32)), } } } -- Gitee From f098245b0c6af9d7005d38307cec027aba2fa872 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 29 Nov 2022 19:30:59 +0800 Subject: [PATCH 0535/2187] VNC: Add the subauth of VncAuthVencryptTlNone Add the subauth of VncAuthVencryptTlNone. In which encrypt with anon certificate and do not use sasl authentication. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 2 ++ vnc/src/server.rs | 40 +++++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index f1d7b517f..11395e4e9 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -51,6 +51,8 @@ pub enum AuthState { pub enum SubAuthState { /// Send plain Message + no auth. VncAuthVencryptPlain = 256, + /// Tls vencry with anon + no auth. + VncAuthVencryptTlNone = 257, /// Tls vencrypt with x509 + no auth. VncAuthVencryptX509None = 260, /// Tls vencrypt with x509 + sasl. diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 8bb799da7..6e1d6c76f 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -257,26 +257,36 @@ impl SecurityType { /// Encryption configuration. fn set_auth(&mut self) -> Result<()> { + let is_x509: bool; + let is_anon: bool; + let is_sasl: bool = self.saslauth.is_some(); + if let Some(tlscred) = self.tlscreds.clone() { + is_x509 = tlscred.cred_type == *X509_CERT; + is_anon = tlscred.cred_type == *ANON_CERT; self.auth = AuthState::Vencrypt; - if tlscred.cred_type != *X509_CERT && tlscred.cred_type != *ANON_CERT { - error!("Unsupported tls cred type"); - return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "Unsupported tls cred type", - )))); - } - if self.saslauth.is_some() { - if tlscred.cred_type == *"x509" { - self.subauth = SubAuthState::VncAuthVencryptX509Sasl; - } else { - self.subauth = SubAuthState::VncAuthVencryptTlssasl; - } - } else { - self.subauth = SubAuthState::VncAuthVencryptX509None; - } } else { self.auth = AuthState::No; self.subauth = SubAuthState::VncAuthVencryptPlain; + return Ok(()); + } + + if !is_x509 && !is_anon { + error!("Unsupported tls cred type"); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( + "Unsupported tls cred type", + )))); + } + if is_sasl { + if is_x509 { + self.subauth = SubAuthState::VncAuthVencryptX509Sasl; + } else { + self.subauth = SubAuthState::VncAuthVencryptTlssasl; + } + } else if is_x509 { + self.subauth = SubAuthState::VncAuthVencryptX509None; + } else { + self.subauth = SubAuthState::VncAuthVencryptTlNone; } Ok(()) } -- Gitee From 20cc03b91e8f3a9947d088dc28525fcb52c9a4a3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 21 Nov 2022 01:45:33 +0800 Subject: [PATCH 0536/2187] scsi-hd: support config "bootindex" parameter bootindex is the boot order of the scsi device. (optional) If not set, the priority is lowest. The number ranges from 0 to 255, the smaller the number, the higher the priority. It determines the order of bootable devices which firmware will use for booting the guest OS. We can now set bootindex by using command line just like: -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,serial=123456,bootindex=1] Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 7 +++-- machine/src/lib.rs | 43 +++++++++++++++++++++--------- machine/src/standard_vm/mod.rs | 12 ++++++--- machine_manager/src/config/scsi.rs | 10 +++++++ virtio/src/scsi/controller.rs | 2 +- virtio/src/virtio_pci.rs | 9 +++++++ 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 774a1899d..c025659f4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -774,7 +774,7 @@ Virtio Scsi HardDisk is a virtual block device, which process read and write req Note: Only support using raw image file as backend now. -Seven properties can be set for virtio-scsi hd. +Ten properties can be set for virtio-scsi hd. * file: the path of backend image file. * id: unique device id. @@ -785,11 +785,14 @@ Seven properties can be set for virtio-scsi hd. * readonly: whether scsi device is read-only or not. Default option is false. (optional) * direct: open block device with `O_DIRECT` mode. (optional) If not set, default is true. * aio: the aio type of block device (optional). Possible values are `native`, `io_uring`, or `off`. If not set, default is `native` if `direct` is true, otherwise default is `off`. +* bootindex: the boot order of the scsi device. (optional) If not set, the priority is lowest. +The number ranges from 0 to 255, the smaller the number, the higher the priority. +It determines the order of bootable devices which firmware will use for booting the guest OS. ```shell -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] --device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0 +-device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,serial=123456,bootindex=1] ``` Note: Only support scsi-id=0 and lun number should start from 0 Now. ### 2.18 VNC diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9c26906ba..ed5aab921 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -656,9 +656,11 @@ pub trait MachineOps { bail!("No scsi controller list found!"); } - self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false) + let pci_dev = self + .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) .with_context(|| "Failed to add virtio scsi controller")?; self.reset_bus(&device_cfg.id)?; + device.lock().unwrap().config.boot_prefix = pci_dev.lock().unwrap().get_dev_path(); Ok(()) } @@ -669,24 +671,26 @@ pub trait MachineOps { scsi_type: u32, ) -> Result<()> { let device_cfg = parse_scsi_device(vm_config, cfg_args)?; + if let Some(bootindex) = device_cfg.boot_index { + self.check_bootindex(bootindex) + .with_context(|| "Failed to add scsi device for invalid bootindex")?; + } let device = Arc::new(Mutex::new(ScsiDisk::ScsiDevice::new( device_cfg.clone(), scsi_type, ))); - let lock_cntlr_list = if let Some(cntlr_list) = self.get_scsi_cntlr_list() { - cntlr_list.lock().unwrap() - } else { - bail!("Wrong! No scsi controller list found") - }; + let cntlr_list = self + .get_scsi_cntlr_list() + .ok_or_else(|| anyhow!("Wrong! No scsi controller list found!"))?; + let cntlr_list_clone = cntlr_list.clone(); + let cntlr_list_lock = cntlr_list_clone.lock().unwrap(); - let lock_cntlr = if let Some(cntlr) = lock_cntlr_list.get(&device_cfg.bus) { - cntlr.lock().unwrap() - } else { - bail!("Wrong! Bus {} not found in list", &device_cfg.bus) - }; + let cntlr = cntlr_list_lock + .get(&device_cfg.bus) + .ok_or_else(|| anyhow!("Wrong! Bus {} not found in list", &device_cfg.bus))?; - if let Some(bus) = &lock_cntlr.bus { + if let Some(bus) = &cntlr.lock().unwrap().bus { if bus .lock() .unwrap() @@ -705,6 +709,21 @@ pub trait MachineOps { } device.lock().unwrap().realize()?; + + if let Some(bootindex) = device_cfg.boot_index { + let mut cntlr_locked = cntlr.lock().unwrap(); + // Eg: OpenFirmware device path(virtio-scsi disk): + // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3 + // | | | | | | + // | | | | target,lun. + // | | | channel(unused, fixed 0). + // | PCI slot,[function] holding SCSI controller. + // PCI root as system bus port. + let dev_path = cntlr_locked.config.boot_prefix.as_mut().unwrap(); + let str = format! {"/channel@0/disk@{:x},{:x}", device_cfg.target, device_cfg.lun}; + dev_path.push_str(&str); + self.add_bootindex_devices(bootindex, dev_path, &device_cfg.id); + } Ok(()) } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 943276094..adb71759d 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -808,6 +808,7 @@ impl StdMachine { queues: args.queues.unwrap_or_else(|| { VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) }) as u32, + boot_prefix: None, }; dev_cfg.check()?; @@ -822,15 +823,20 @@ impl StdMachine { bail!("No scsi controller list found"); } - let result = self.add_virtio_pci_device(&args.id, pci_bdf, device, multifunction, false); - if let Err(ref e) = result { + let result = + self.add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false); + let pci_dev = if let Err(ref e) = result { self.get_scsi_cntlr_list() .unwrap() .lock() .unwrap() .remove(&bus_name); bail!("Failed to add virtio scsi controller, error is {:?}", e); - } + } else { + result.unwrap() + }; + + device.lock().unwrap().config.boot_prefix = pci_dev.lock().unwrap().get_dev_path(); Ok(()) } diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 085ed1050..6d4a77ba1 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -34,6 +34,8 @@ pub struct ScsiCntlrConfig { pub iothread: Option, /// Number of scsi cmd queues. pub queues: u32, + /// Boot path of this scsi controller. It's prefix of scsi device's boot path. + pub boot_prefix: Option, } impl Default for ScsiCntlrConfig { @@ -43,6 +45,7 @@ impl Default for ScsiCntlrConfig { iothread: None, //At least 1 cmd queue. queues: 1, + boot_prefix: None, } } } @@ -136,6 +139,8 @@ pub struct ScsiDevConfig { pub direct: bool, /// Async IO type. pub aio_type: Option, + /// Boot order. + pub boot_index: Option, /// Scsi four level hierarchical address(host, channel, target, lun). pub channel: u8, pub target: u8, @@ -151,6 +156,7 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result .push("scsi-id") .push("lun") .push("serial") + .push("bootindex") .push("drive"); cmd_parser.parse(drive_config)?; @@ -163,6 +169,10 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result return Err(anyhow!(ConfigError::FieldIsMissing("drive", "scsi device"))); }; + if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { + scsi_dev_cfg.boot_index = Some(boot_index); + } + if let Some(serial) = cmd_parser.get_value::("serial")? { scsi_dev_cfg.serial = Some(serial); } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index e0f63a121..5a3df5115 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -136,7 +136,7 @@ pub struct ScsiCntlrState { /// Virtio Scsi Controller device structure. pub struct ScsiCntlr { /// Configuration of the virtio scsi controller. - config: ScsiCntlrConfig, + pub config: ScsiCntlrConfig, /// Status of virtio scsi controller. state: ScsiCntlrState, /// Scsi bus. diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index c8b84f1c3..2cc41d38f 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1305,6 +1305,15 @@ impl PciDevOps for VirtioPciDevice { dev_path.push_str("/disk@0,0"); Some(dev_path) } + VIRTIO_TYPE_SCSI => { + // The virtio scsi controller can not set boot order, which is set for scsi device. + // All the scsi devices in the same scsi controller have the same boot path prefix + // (eg: /pci@XXXXX/scsi@$slot_id[,function_id]). And every scsi device has it's + // own boot path("/channel@0/disk@$target_id,$lun_id"); + let parent_dev_path = self.get_parent_dev_path(parent_bus); + let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); + Some(dev_path) + } _ => None, } } -- Gitee From 897916edac52dcd41ee782d806bd2292d7b67127 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 21 Nov 2022 04:21:43 +0800 Subject: [PATCH 0537/2187] bootindex: fix bootindex device path string's error stratovirt will generate bootable device's path by bootindex order. And then EDK2 will parse this path string. EDK2 parses this string's number in hexadecimal, but stratovirt generate this string by using number in decimal. This different action will lead to error when boot device's(eg virtio-blk) bdf number(eg: slot or function) is greater than 9. Fix this unmatched behavior. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 7 +++++++ pci/src/lib.rs | 19 ++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index ed5aab921..ff3977f17 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -623,6 +623,13 @@ pub trait MachineOps { .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) .with_context(|| "Failed to add virtio pci device")?; if let Some(bootindex) = device_cfg.boot_index { + // Eg: OpenFirmware device path(virtio-blk disk): + // /pci@i0cf8/scsi@6[,3]/disk@0,0 + // | | | | | + // | | | | | + // | | | fixed 0. + // | PCI slot,[function] holding disk. + // PCI root as system bus port. if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); } diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 744a5f623..d27a15ca2 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -200,21 +200,18 @@ pub trait PciDevOps: Send { parent_dev_path } - /// Fill the device patch accroding to parent device patch and device function. + /// Fill the device path accroding to parent device path and device function. fn populate_dev_path(&self, parent_dev_path: String, devfn: u8, dev_type: &str) -> String { - let mut dev_path = parent_dev_path; - dev_path.push_str(dev_type); - let slot = pci_slot(devfn); - dev_path.push_str(&slot.to_string()); - let function = pci_func(devfn); - if function != 0 { - dev_path.push(','); - dev_path.push_str(&function.to_string()); - } - dev_path + let slot_function = if function != 0 { + format!("{:x},{:x}", slot, function) + } else { + format!("{:x}", slot) + }; + + format!("{}{}{}", parent_dev_path, dev_type, slot_function) } /// Get firmware device path. -- Gitee From 90bb3e00b398d2b055ded08629194b8f34367e21 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 4 Dec 2022 10:54:53 +0800 Subject: [PATCH 0538/2187] docs: delete scsi-id=0 and lun starting from 0 limits for scsi devices Since we implement scsi target commands and fix virtio-scsi controller common config error, scsi-id configuration range is [0, 255](boot scsi disk is [0, 31]) and lun configuration is [0, 255](boot disk is [0, 7]). So, delete limits in docs. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index c025659f4..cd662c53b 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -794,7 +794,6 @@ It determines the order of bootable devices which firmware will use for booting -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,serial=123456,bootindex=1] ``` -Note: Only support scsi-id=0 and lun number should start from 0 Now. ### 2.18 VNC VNC can provide the users with way to login virtual machines remotely. -- Gitee From c1f2530128a8c3d8db037375aa2e9e5b02864ddb Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Sun, 4 Dec 2022 17:30:44 +0800 Subject: [PATCH 0539/2187] bugfix: make devices support the 64-bit bar address to meet the requirements of some devices for high mmio address space, the configuration of device's bar address from guestOS in 64-bit form needs to be supported. Signed-off-by: Binfeng Wu --- virtio/src/virtio_pci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index b45006677..c8b84f1c3 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1198,7 +1198,7 @@ impl PciDevOps for VirtioPciDevice { self.config.register_bar( VIRTIO_PCI_MEM_BAR_IDX as usize, modern_mem_region, - RegionType::Mem32Bit, + RegionType::Mem64Bit, false, mem_region_size, )?; -- Gitee From 37571008a88adbea2347b79e7d6100daee780e45 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 4 Dec 2022 20:08:37 +0800 Subject: [PATCH 0540/2187] Chardev: don't lock chardev all the time in get_notifier_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dead lock will happen between PL011 and chardev. thread 1 lock by order: lock 1) chardev: chardev.lock().unwrap(). receive.as_ref().unwrap(). // chardev.rs: get_notifier_handler. lock 2) PL011: self.receive = Some(Arc::new(move |data: &[u8]| { cloned_dev.lock().unwrap().input_handle(data) })); // chardev.rs: set_input_callback. thread 2 lock by order: lock 1) PL011; let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { cloned_dev.lock().unwrap().write(data, addr, offset) }; // sysbus/src/lib.rs: build_region_ops. lock 2) chardev: if let Some(output) = &mut self.chardev.lock().unwrap().output // pl011.rs: write. So, don't lock chardev all the time in get_notifier_handler to avoid this dead lock. Signed-off-by: liuxiangdong --- devices/src/legacy/chardev.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index f718cc774..7bb4798c9 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -201,9 +201,12 @@ fn get_notifier_handler( let locked_chardev = chardev.lock().unwrap(); let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); let mut buffer = vec![0_u8; buff_size]; - if let Some(input) = locked_chardev.input.clone() { + let input_h = locked_chardev.input.clone(); + let receive = locked_chardev.receive.clone(); + drop(locked_chardev); + if let Some(input) = input_h { if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { - locked_chardev.receive.as_ref().unwrap()(&mut buffer[..index]); + receive.as_ref().unwrap()(&mut buffer[..index]); } else { error!("Failed to read input data"); } -- Gitee From 0716e6a9b587aac14bdaba39172fac8ee7d1df21 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 3 Dec 2022 15:11:44 +0800 Subject: [PATCH 0541/2187] scsi-device: add block_size field because it's inconsistent in different scsi devices Scsi Device block size is inconsistent in different scsi devices. E.g: scsi harddisk's block size is 512bytes, and scsi multi-media device's block size is 2048bytes. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 35 ++++++++++++++++++++++------------- virtio/src/scsi/disk.rs | 13 +++++++++++++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index ce5158457..76ecb7501 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -182,8 +182,6 @@ const SECTOR_SHIFT: u8 = 9; /// Size of a sector of the block device. const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; -const SCSI_DEFAULT_BLOCK_SIZE: i32 = 512; - /// Sense Keys. pub const NO_SENSE: u8 = 0x00; pub const RECOVERED_ERROR: u8 = 0x01; @@ -340,6 +338,7 @@ impl ScsiBus { pub fn scsi_bus_parse_req_cdb( &self, cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], + dev: Arc>, ) -> Option { let buf: [u8; SCSI_CMD_BUF_SIZE] = (cdb[0..SCSI_CMD_BUF_SIZE]) .try_into() @@ -350,7 +349,7 @@ impl ScsiBus { return None; } - let xfer = scsi_cdb_xfer(&cdb); + let xfer = scsi_cdb_xfer(&cdb, dev); if xfer < 0 { return None; } @@ -414,7 +413,7 @@ impl ScsiRequest { if let Some(cmd) = scsibus .lock() .unwrap() - .scsi_bus_parse_req_cdb(req.lock().unwrap().req.cdb) + .scsi_bus_parse_req_cdb(req.lock().unwrap().req.cdb, scsidevice.clone()) { let ops = cmd.command; let opstype = scsi_operation_type(ops); @@ -705,7 +704,11 @@ fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { } } -fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { +fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc>) -> i32 { + let dev_lock = dev.lock().unwrap(); + let block_size = dev_lock.block_size as i32; + drop(dev_lock); + let mut xfer = match cdb[0] >> 5 { // Group Code | Transfer length. | // 000b | Byte[4]. | @@ -734,13 +737,13 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { } else if cdb[1] & 4 != 0 { xfer = 1; } - xfer *= SCSI_DEFAULT_BLOCK_SIZE; + xfer *= block_size; } WRITE_SAME_10 | WRITE_SAME_16 => { if cdb[1] & 1 != 0 { xfer = 0; } else { - xfer = SCSI_DEFAULT_BLOCK_SIZE; + xfer = block_size; } } READ_CAPACITY_10 => { @@ -755,12 +758,12 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { WRITE_6 | READ_6 | READ_REVERSE => { // length 0 means 256 blocks. if xfer == 0 { - xfer = 256 * SCSI_DEFAULT_BLOCK_SIZE; + xfer = 256 * block_size; } } WRITE_10 | WRITE_VERIFY_10 | WRITE_12 | WRITE_VERIFY_12 | WRITE_16 | WRITE_VERIFY_16 | READ_10 | READ_12 | READ_16 => { - xfer *= SCSI_DEFAULT_BLOCK_SIZE; + xfer *= block_size; } FORMAT_UNIT => { xfer = match cdb[1] & 16 { @@ -1104,14 +1107,16 @@ fn scsi_command_emulate_read_capacity_10( } let dev_lock = dev.lock().unwrap(); + let block_size = dev_lock.block_size; let mut outbuf: Vec = vec![0; 8]; let mut nb_sectors = cmp::min(dev_lock.disk_sectors as u32, u32::MAX); + nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; nb_sectors -= 1; // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical block). // Bytes[4-7]: Logical Block Length In Bytes. BigEndian::write_u32(&mut outbuf[0..4], nb_sectors); - BigEndian::write_u32(&mut outbuf[4..8], DEFAULT_SECTOR_SIZE); + BigEndian::write_u32(&mut outbuf[4..8], block_size); Ok(outbuf) } @@ -1127,7 +1132,9 @@ fn scsi_command_emulate_mode_sense( let mut outbuf: Vec = vec![0]; let dev_lock = dev.lock().unwrap(); let mut dev_specific_parameter: u8 = 0; - let nb_sectors = dev_lock.disk_sectors; + let mut nb_sectors = dev_lock.disk_sectors as u32; + let block_size = dev_lock.block_size as u32; + nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; debug!( "MODE SENSE page_code {:x}, page_control {:x}, subpage {:x}, dbd bit {:x}, Allocation length {}", @@ -1180,7 +1187,7 @@ fn scsi_command_emulate_mode_sense( // Byte[5-7]: Block Length. let mut block_desc: Vec = vec![0; 8]; BigEndian::write_u32(&mut block_desc[0..4], nb_sectors as u32 & 0xffffff); - BigEndian::write_u32(&mut block_desc[4..8], DEFAULT_SECTOR_SIZE); + BigEndian::write_u32(&mut block_desc[4..8], block_size); outbuf.append(&mut block_desc); } @@ -1307,8 +1314,10 @@ fn scsi_command_emulate_service_action_in_16( // Byte 1: bit0 - bit4: Service Action(0x10), bit 5 - bit 7: Reserved. if cmd.buf[1] & 0x1f == SUBCODE_READ_CAPACITY_16 { let dev_lock = dev.lock().unwrap(); + let block_size = dev_lock.block_size; let mut outbuf: Vec = vec![0; 32]; let mut nb_sectors = dev_lock.disk_sectors; + nb_sectors /= (block_size / DEFAULT_SECTOR_SIZE) as u64; nb_sectors -= 1; drop(dev_lock); @@ -1316,7 +1325,7 @@ fn scsi_command_emulate_service_action_in_16( // Byte[0-7]: Returned Logical BLock Address(the logical block address of the last logical block). // Byte[8-11]: Logical Block Length in Bytes. BigEndian::write_u64(&mut outbuf[0..8], nb_sectors); - BigEndian::write_u32(&mut outbuf[8..12], DEFAULT_SECTOR_SIZE); + BigEndian::write_u32(&mut outbuf[8..12], block_size); return Ok(outbuf); } diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 145dcf74d..4096d278d 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -49,6 +49,11 @@ const SECTOR_SHIFT: u8 = 9; const DUMMY_IMG_SIZE: u64 = 0; pub const DEFAULT_SECTOR_SIZE: u32 = 1_u32 << SECTOR_SHIFT; +/// Scsi disk's block size is 512 Bytes. +pub const SCSI_DISK_DEFAULT_BLOCK_SIZE: u32 = 512; +/// Scsi media device's block size is 2048 Bytes. +pub const SCSI_CDROM_DEFAULT_BLOCK_SIZE: u32 = 2048; + #[derive(Clone, Default)] pub struct ScsiDevState { /// Features which the scsi device supports. @@ -88,6 +93,8 @@ pub struct ScsiDevice { pub disk_image: Option>, /// Number of sectors of the image file. pub disk_sectors: u64, + /// Scsi Device block size. + pub block_size: u32, /// Scsi device type. pub scsi_type: u32, /// Scsi Bus attached to. @@ -103,6 +110,7 @@ impl Default for ScsiDevice { state: Default::default(), disk_image: None, disk_sectors: 0, + block_size: SCSI_DISK_DEFAULT_BLOCK_SIZE, scsi_type: SCSI_TYPE_DISK, parent_bus: Weak::new(), drive_files: Arc::new(Mutex::new(HashMap::new())), @@ -121,6 +129,7 @@ impl ScsiDevice { state: ScsiDevState::new(), disk_image: None, disk_sectors: 0, + block_size: 0, scsi_type, parent_bus: Weak::new(), drive_files, @@ -130,8 +139,12 @@ impl ScsiDevice { pub fn realize(&mut self) -> Result<()> { match self.scsi_type { SCSI_TYPE_DISK => { + self.block_size = SCSI_DISK_DEFAULT_BLOCK_SIZE; self.state.product = "STRA HARDDISK".to_string(); } + SCSI_TYPE_ROM => { + self.block_size = SCSI_CDROM_DEFAULT_BLOCK_SIZE; + } _ => { bail!("Scsi type {} does not support now", self.scsi_type); } -- Gitee From ca6bf5654bae77678b2ff2112a6c12a538ec7682 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 3 Dec 2022 16:01:52 +0800 Subject: [PATCH 0542/2187] scsi-bus: emulate some scsi commands for scsi cd 1) READ_DISC_INFORMATION: Allow the client to request information about the currently mounted Multi-media disc. 2) READ_TOC: Request that the logical unit transfer data from the Table of Contens(TOC). 3) READ_DISC_INFORMATION: Provide information about all discs. 4) GET_CONFIGURATION: Provide information to the initiator about the overall capabilities of the device and, specifically, the current capabilities of the device. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 484 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 472 insertions(+), 12 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 76ecb7501..bebf1c513 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -94,6 +94,7 @@ pub const WRITE_LONG_10: u8 = 0x3f; pub const CHANGE_DEFINITION: u8 = 0x40; pub const WRITE_SAME_10: u8 = 0x41; pub const UNMAP: u8 = 0x42; +/// The Read TOC command requests that the Drive read data from a table of contets. pub const READ_TOC: u8 = 0x43; pub const REPORT_DENSITY_SUPPORT: u8 = 0x44; pub const GET_CONFIGURATION: u8 = 0x46; @@ -285,6 +286,82 @@ const TYPE_INACTIVE: u8 = 0x20; /// Scsi target device is not capable of supporting a peripheral device connected to this logical unit. const TYPE_NO_LUN: u8 = 0x7f; +/// Notification Classes for GET EVENT STATUS NOTIFICATION. +/// 000b: No requested Event Clases are supported. +pub const GESN_NO_REQUESTED_EVENT: u8 = 0; +/// 001b: Operational Change Request/Notification. +pub const GESN_OPERATIONAL_CHANGE: u8 = 1; +/// 010b: Power Management. +pub const GESN_POWER_MANAGEMENT: u8 = 2; +/// 011b: External Request. +pub const GESN_EXTERNAL_REQUEST: u8 = 3; +/// 100b: Media. +pub const GESN_MEDIA: u8 = 4; +/// 101b: Multiple Hosts. +pub const GESN_MULTIPLE_HOSTS: u8 = 5; +/// 110b: Device Busy. +/// 111b: Reserved. +pub const GESN_DEVICE_BUSY: u8 = 6; + +/// Media Status in Get Event Status Notification. +/// If the Media Present bit is set to zero, no media is present in the Drive. +/// If the Media Present bit is set to one, media is present in the Drive. +pub const GESN_MS_DOOR_OR_TRAY_OPEN_BIT: u8 = 0; +/// If the Door or Tray Open bit is set to zero, the Tray or Door mechanism is in the closed state. +/// If the Door or Tray Open bit is set to one, the Tray or Door mechanism is in the open state. +/// If the Drive does not have either a tray or a door, this bit shall be set to zero. +pub const GESN_MS_MEDIA_PRESENT_BIT: u8 = 1; + +/// Event Code in Get Event Status Notification. +/// Media status is unchanged. +pub const GESN_EC_NOCHG: u8 = 0; +/// The Drive has received a request from the user(usually through a mechanical switch on the Drive) +/// to eject the specified slot or media. +pub const GESN_EC_EJECTREQUEST: u8 = 1; +/// The specified slot(or the Drive) has received new media, and is ready to access it. +pub const GESN_EC_NEWMEDIA: u8 = 2; +/// The media has been removed from the specified slot, and the Drive is unable to access the media +/// without user intervention. This applies to media changers only. +pub const GESN_EC_MEDIAREMOVAL: u8 = 3; +/// The user has requested that the media in the specified slot be loaded. This applies to media changers only. +pub const GESN_EC_MEDIACHANGED: u8 = 4; +/// A DVD+RW background format has completed. Since DVD+RW Drives are capable of generationg multiple +/// media events concurrently, such Drives shall be capable of queuing media events. +pub const GESN_EC_BGFORMATCOMPLETED: u8 = 5; +/// A DVD+RW background format has been automatically restarted by the Drive. Since DVD+RW Drives are +/// capable of generationg multiple media events concurrently, such Drives shall be capable of queuing +/// media event. +pub const GESN_EC_BGFORMATRESTARTED: u8 = 6; + +/// Some generally useful CD-ROM information. From +/// Max. minutes per CD. +pub const CD_MINS: u32 = 74; +/// Seconds per minute. +pub const CD_SECS: u32 = 60; +/// Frames per second. +pub const CD_FRAMES: u32 = 75; +/// Bytes per frame, "cooked" mode. +pub const CD_FRAME_SIZE: u32 = 2048; +/// MSF numbering offset of the first frame. +pub const CD_MSF_OFFSET: u32 = 150; +/// Max bytes supported for CD in stratovirt now. +pub const CD_MAX_BYTES: u32 = CD_MINS * CD_SECS * CD_FRAMES * CD_FRAME_SIZE; +pub const CD_MAX_SECTORS: u32 = CD_MAX_BYTES / DEFAULT_SECTOR_SIZE; + +/// Profile Number for GET CONFIGURATION command in MMC-6. +/// Read only Compact Disc capable. +const GC_PROFILE_CD_ROM: u16 = 0x0008; +/// Read only DVD. +const GC_PROFILE_DVD_ROM: u16 = 0x0010; + +/// Features Codes for GET CONFIGURATION command in MMC-6. +/// A list of all Profiles supported by the Drive. +const GC_FC_PROFILE_LIST: u16 = 0x0000; +/// Mandatory behavior for all devices. +const GC_FC_CORE: u16 = 0x0001; +/// The medium may be removed from the device. +const GC_FC_REMOVEABLE_MEDIUM: u16 = 0x0003; + pub struct ScsiBus { /// Bus name. pub name: String, @@ -562,6 +639,14 @@ impl ScsiRequest { SERVICE_ACTION_IN_16 => { scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev) } + READ_DISC_INFORMATION => { + scsi_command_emulate_read_disc_information(&self.cmd, &self.dev) + } + GET_EVENT_STATUS_NOTIFICATION => { + scsi_command_emulate_get_event_status_notification(&self.cmd, &self.dev) + } + READ_TOC => scsi_command_emulate_read_toc(&self.cmd, &self.dev), + GET_CONFIGURATION => scsi_command_emulate_get_configuration(&self.cmd, &self.dev), _ => { not_supported_flag = true; Err(anyhow!("Emulation scsi command is not supported now!")) @@ -707,6 +792,7 @@ fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc>) -> i32 { let dev_lock = dev.lock().unwrap(); let block_size = dev_lock.block_size as i32; + let scsi_type = dev_lock.scsi_type; drop(dev_lock); let mut xfer = match cdb[0] >> 5 { @@ -753,7 +839,10 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc { - xfer = i32::from(cdb[9]) | i32::from(cdb[8]) << 8; + xfer = match scsi_type { + SCSI_TYPE_DISK => i32::from(cdb[9]) | i32::from(cdb[8]) << 8, + _ => i32::from(cdb[10]) | i32::from(cdb[8]) << 8, + }; } WRITE_6 | READ_6 | READ_REVERSE => { // length 0 means 256 blocks. @@ -766,12 +855,14 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc { - xfer = match cdb[1] & 16 { - 0 => 0, - _ => match cdb[1] & 32 { - 0 => 4, - _ => 8, - }, + xfer = if (scsi_type == SCSI_TYPE_ROM) && (cdb[1] & 16 != 0) { + 12 + } else if cdb[1] & 16 == 0 { + 0 + } else if cdb[1] & 32 == 0 { + 4 + } else { + 8 }; } INQUIRY | RECEIVE_DIAGNOSTIC | SEND_DIAGNOSTIC => { @@ -783,8 +874,13 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc { xfer = BigEndian::read_i32(&cdb[5..]); } - ERASE_12 | MECHANISM_STATUS | READ_DVD_STRUCTURE | SEND_DVD_STRUCTURE | MAINTENANCE_OUT - | MAINTENANCE_IN => {} + ERASE_12 => {} + MECHANISM_STATUS | READ_DVD_STRUCTURE | SEND_DVD_STRUCTURE | MAINTENANCE_OUT + | MAINTENANCE_IN => { + if scsi_type == SCSI_TYPE_ROM { + xfer = i32::from(cdb[9]) | i32::from(cdb[8]) << 8; + } + } ATA_PASSTHROUGH_12 => {} ATA_PASSTHROUGH_16 => {} _ => {} @@ -1133,6 +1229,7 @@ fn scsi_command_emulate_mode_sense( let dev_lock = dev.lock().unwrap(); let mut dev_specific_parameter: u8 = 0; let mut nb_sectors = dev_lock.disk_sectors as u32; + let scsi_type = dev_lock.scsi_type; let block_size = dev_lock.block_size as u32; nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; @@ -1147,7 +1244,7 @@ fn scsi_command_emulate_mode_sense( // Device specific paramteter field for direct access block devices: // Bit 7: WP(Write Protect); bit 4: DPOFUA; - if dev_lock.scsi_type == SCSI_TYPE_DISK { + if scsi_type == SCSI_TYPE_DISK { if dev_lock.state.features & (1 << SCSI_DISK_F_DPOFUA) != 0 { dev_specific_parameter = 0x10; } @@ -1198,10 +1295,10 @@ fn scsi_command_emulate_mode_sense( if page_code == 0x3f { // 3Fh Return all pages not including subpages. for pg in 0..page_code { - let _ = scsi_command_emulate_mode_sense_page(pg, page_control, &mut outbuf); + let _ = scsi_command_emulate_mode_sense_page(pg, page_control, &mut outbuf, scsi_type); } } else { - scsi_command_emulate_mode_sense_page(page_code, page_control, &mut outbuf)?; + scsi_command_emulate_mode_sense_page(page_code, page_control, &mut outbuf, scsi_type)?; } // The Mode Data Length field indicates the length in bytes of the following data @@ -1222,7 +1319,32 @@ fn scsi_command_emulate_mode_sense_page( page: u8, page_control: u8, outbuf: &mut Vec, + scsi_type: u32, ) -> Result> { + if scsi_type == SCSI_TYPE_DISK + && ![ + MODE_PAGE_HD_GEOMETRY, + MODE_PAGE_FLEXIBLE_DISK_GEOMETRY, + MODE_PAGE_CACHING, + MODE_PAGE_R_W_ERROR, + ] + .contains(&page) + || scsi_type == SCSI_TYPE_ROM + && ![ + MODE_PAGE_CACHING, + MODE_PAGE_R_W_ERROR, + MODE_PAGE_AUDIO_CTL, + MODE_PAGE_CAPABILITIES, + ] + .contains(&page) + { + bail!( + "Invalid Mode Sense command, page control ({:x}), page ({:x}), scsi device type ({})", + page_control, + page, + scsi_type + ); + } let buflen = outbuf.len(); match page { MODE_PAGE_CACHING => { @@ -1244,6 +1366,53 @@ fn scsi_command_emulate_mode_sense_page( outbuf[buflen + 2] = 0x80; } } + MODE_PAGE_CAPABILITIES => { + // MM Capabilities and Mechanical Status Page(Page Code 0x2A). + // This mode page is legacy and was most recently defined in MMC-3. + // Outbuf in CD/DVD Capabilities and Mechanical Status Page: + // Byte[buflen + 0]: PS | Reserved | Bits[0-5]: Page Code(0x2A). + // Byte[buflen + 1]: Page Length(28 + 4 * (maximum number of n)). + // Byte[buflen + 2]: Bits[6-7]: Reserved | DVD-RAW Read(1) | DVD-R READ(1) | + // DVD-ROM READ(1) | Method 2 | CD-RW Read(1) | CD-R Read(1). + // Byte[buflen + 3]: Bits[6-7]: Reserved | DVD-RAW WRITE | DVD-R WRITE | + // Reserved | Test Write | CD-R/RW Write | CD-R Write. + // Byte[buflen + 4]: BUF | Multi Session(1) | Mode 2 Form 2(1) | Mode 2 Form 1(1) | + // Digital Port 2(1) | Digital Port 1(1) | Composite(1) | Audio Play(1). + // Byte[buflen + 5]: Read Bar Code(1) | UPC(1) | ISRC(1) | C2 Pointers supported(1) | + // R-W Deinterleaved & corrected(1) | R-W supported(1) | + // CD-DA Stream is Accurate(1) | CD-DA Cmds supported(1). + // Byte[buflen + 6]: Bits[5-7]: Loading Mechanism Type(1) | Reserved | Eject(1) | Prevent Jumper(1) | + // Lock State | Lock(1). + // Byte[buflen + 7]: Bits[6-7]: Reserved | R-W in Lead-in | Side Change Capable | SSS | + // Changer Supports Disc Present | Separate Channel Mute | Separate volume levels + // Bytes[buflen + 8 - buflen + 9]: Obsolete. + // Bytes[buflen + 10 - buflen + 11]: Number of Volume Levels Supported. + // Bytes[buflen + 12 - buflen + 13]: Buffer Size Supported. + // Bytes[buflen + 14 - buflen + 15]: Obsolete. + // Byte[buflen + 16]: Reserved. + // Byte[buflen + 17]: Bits[6-7]: Reserved | Bits[4-5]: Length | LSBF | RCK | BCKF | Reserved. + // Bytes[buflen + 18 - buflen + 21]: Obsolete. + // Bytes[buflen + 22 - buflen + 23]: Copy Management Revision Supported. + // Bytes[buflen + 24 - buflen + 26]: Reserved. + // Byte[buflen + 27]: Bits[2-7]: Reserved. Bits[0-1]: Rotation Control Selected. + // Bytes[buflen + 28 - buflen + 29]: Current Write Speed Selected. + // Bytes[buflen + 31]: Number of Logical Unit Write Speed Performance Descriptor Tables(n). + outbuf.resize(buflen + 32, 0); + outbuf[buflen] = page; + outbuf[buflen + 1] = 28; + + if page_control == 1 { + bail!("Not supported page control"); + } + + outbuf[buflen + 2] = 0x3b; + outbuf[buflen + 4] = 0x7f; + outbuf[buflen + 5] = 0xff; + // Stratovirt does not implement tray for CD, so set "Lock State" to 0. + outbuf[buflen + 6] = 0x2d; + BigEndian::write_u16(&mut outbuf[(buflen + 10)..(buflen + 12)], 2); + BigEndian::write_u16(&mut outbuf[(buflen + 12)..(buflen + 14)], 2048); + } _ => { bail!( "Invalid Mode Sense command, page control ({:x}), page ({:x})", @@ -1336,3 +1505,294 @@ fn scsi_command_emulate_service_action_in_16( cmd.buf[1] & 31 ); } + +fn scsi_command_emulate_read_disc_information( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + // Byte1: Bits[0-2]: Data type. + // Data Type | Returned Data. | + // 000b | Standard Disc Information. | + // 001b | Track Resources Information. | + // 010b | POW Resources Information. | + // 011b-111b | Reserved | + let data_type = cmd.buf[1] & 7; + + // Types 001b/010b are only defined for Blu-Ray. + if data_type != 0 { + bail!("Unsupported read disc information data type {}!", data_type); + } + if dev.lock().unwrap().scsi_type != SCSI_TYPE_ROM { + bail!("Read disc information command is only for scsi multi-media device!"); + } + + // Outbuf: + // Bytes[0-1]: Disc Information Length(32). + // Byte2: Disc Information Data Type(000b) | Erasable(0) | State of last Session(01b) | Disc Status(11b). + // Byte3: Number of First Track on Disc. + // Byte4: Number of Sessions. + // Byte5: First Track Number in Last Session(Least Significant Byte). + // Byte6: Last Track Number in Last Session(Last Significant Byte). + // Byte7: DID_V | DBC_V | URU:Unrestricted Use Disc(1) | DAC_V | Reserved | Legacy | BG Format Status. + // Byte8: Disc Type(00h: CD-DA or CD-ROM Disc). + // Byte9: Number of sessions(Most Significant Byte). + // Byte10: First Trace Number in Last Session(Most Significant Byte). + // Byte11: Last Trace Number in Last Session(Most Significant Byte). + // Bytes12-15: Disc Identification. + // Bytes16-19: Last Session Lead-in Start Address. + // Bytes20-23: Last Possible Lead-Out Start Address. + // Bytes24-31: Disc Bar Code. + // Byte32: Disc Application Code. + // Byte33: Number of OPC Tables.(0) + let mut outbuf: Vec = vec![0; 34]; + outbuf[1] = 32; + outbuf[2] = 0xe; + outbuf[3] = 1; + outbuf[4] = 1; + outbuf[5] = 1; + outbuf[6] = 1; + outbuf[7] = 0x20; + + Ok(outbuf) +} + +/// Format field for READ TOC command. +/// The Track/Session Number field specifies starting track number for which the data is returned. +/// For multi-session discs, TOC data is returned for all sessions. Track number Aah is reported +/// only for the Lead-out area of the last complete session. +const RT_FORMATTED_TOC: u8 = 0x0000; +/// This format returns the first complete session number, last complete session number and last +/// complete session starting address. +const RT_MULTI_SESSION_INFORMATION: u8 = 0x0001; +/// This format returns all Q sub-code data in the Lead-IN(TOC) areas starting from a session number +/// as specified in the Track/Session Number field. +const RT_RAW_TOC: u8 = 0x0010; + +fn scsi_command_emulate_read_toc( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + // Byte1: Bit1: MSF.(MSF: Minute, Second, Frame) + // MSF = 1: the address fields in some returned data formats shall be in MSF form. + // MSF = 0: the address fields in some returned data formats shall be in LBA form. + let msf = cmd.buf[1] & 2; + // Byte2: Bits[0-3]: Format(Select specific returned data format)(CD: 0,1,2). + let format = cmd.buf[2] & 0xf; + // Byte6: Track/Session Number. + let track_number = cmd.buf[6]; + let mut outbuf: Vec = vec![0; 0]; + + match format { + RT_FORMATTED_TOC => { + let nb_sectors = dev.lock().unwrap().disk_sectors as u32; + let mut buf = cdrom_read_formatted_toc(nb_sectors, msf, track_number)?; + outbuf.append(&mut buf); + } + RT_MULTI_SESSION_INFORMATION => { + outbuf.resize(12, 0); + outbuf[1] = 0x0a; + outbuf[2] = 0x01; + outbuf[3] = 0x01; + } + RT_RAW_TOC => {} + _ => { + bail!("Invalid read toc format {}", format); + } + } + + Ok(outbuf) +} + +fn scsi_command_emulate_get_configuration( + _cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + let dev_lock = dev.lock().unwrap(); + if dev_lock.scsi_type != SCSI_TYPE_ROM { + bail!("Invalid scsi type {}", dev_lock.scsi_type); + } + + // 8 bytes(Feature Header) + 12 bytes(Profile List Feature) + + // 12bytes(Core Feature) + 8bytes(Removable media feature) = 40 bytes. + let mut outbuf = vec![0; 40]; + + // Outbuf: + // Bytes[0-7]: Feature Header. + // Bytes[0-3]: Data Length(36 = 40 - 4). + // Bytes[4-5]: Reserved. + // Bytes[6-7]: Current Profile. + BigEndian::write_u32(&mut outbuf[0..4], 36); + let current = if dev_lock.disk_sectors > CD_MAX_SECTORS as u64 { + GC_PROFILE_DVD_ROM + } else { + GC_PROFILE_CD_ROM + }; + BigEndian::write_u16(&mut outbuf[6..8], current); + + // Bytes[8-n]: Feature Descriptor(s): + // Bytes[8-19]: Feature 0: Profile List Feature: + // Bytes[8-9]: Feature code(0000h). + // Byte[10]: Bits[6-7]: Reserved. Bits[2-5]: Version. Bit 1: Persistent. Bit 0: Current(1). + // Byte[11]: Additional Length. + // Byte[12-19]: Profile Descriptors.(2 descriptors: CD and DVD) + // Byte[12-13]: Profile Number(CD). + // Byte[14]: Bits[1-7]: Reserved. Bit 0: CurrentP. + // Byte[15]: Reserved. + // Byte[16-17]: Profile Number(DVD). + // Byte[18]: Bits[1-7]: Reserved. Bit 0: CurrentP. + // Byte[19]: Reserved. + BigEndian::write_u16(&mut outbuf[8..10], GC_FC_PROFILE_LIST); + outbuf[10] = 0x03; + outbuf[11] = 8; + BigEndian::write_u16(&mut outbuf[12..14], GC_PROFILE_CD_ROM); + outbuf[14] |= (current == GC_PROFILE_CD_ROM) as u8; + BigEndian::write_u16(&mut outbuf[16..18], GC_PROFILE_DVD_ROM); + outbuf[18] |= (current == GC_PROFILE_DVD_ROM) as u8; + + // Bytes[8-n]: Feature Descriptor(s): + // Bytes[20-31]: Feature 1: Core Feature: + // Bytes[20-21]: Feature Code(0001h). + // Byte[22]: Bits[6-7]: Reserved. Bits[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + // Byte[23]: Additional Length(8). + // Bytes[24-27]: Physical Interface Standard. (Scsi Family: 00000001h) + // Byte[28]: Bits[2-7]: Reserved. Bit 1: INQ2. Bit 0: DBE(1). + // Bytes[29-31]: Reserved. + BigEndian::write_u16(&mut outbuf[20..22], GC_FC_CORE); + outbuf[22] = 0x0b; + outbuf[23] = 8; + BigEndian::write_u32(&mut outbuf[24..28], 1); + outbuf[28] = 1; + + // Bytes[8-n]: Feature Descriptor(s): + // Bytes[32-40]: Feature 2: Removable media feature: + // Bytes[32-33]: Feature Code(0003h). + // Byte[34]: Bits[6-7]: Reserved. Bit[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + // Byte[35]: Additional Length(4). + // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). Bit 2: Pvnt Jmpr. + // Bit 1: DBML. Bit 0: Lock(1). + // Byte[37-39]: Reserved. + BigEndian::write_u16(&mut outbuf[32..34], GC_FC_REMOVEABLE_MEDIUM); + outbuf[34] = 0x0b; + outbuf[35] = 4; + outbuf[36] = 0x39; + + Ok(outbuf) +} + +fn scsi_command_emulate_get_event_status_notification( + cmd: &ScsiCommand, + dev: &Arc>, +) -> Result> { + // Byte4: Notification Class Request. + let notification_class_request = cmd.buf[4]; + let dev_lock = dev.lock().unwrap(); + + if dev_lock.scsi_type != SCSI_TYPE_ROM { + bail!("Invalid scsi tye {}", dev_lock.scsi_type); + } + + // Byte1: Bit0: Polled. + // Polled = 1: the Host is requesting polled operation. + // Polled = 0: the Host is requesting asynchronous operation. + if cmd.buf[1] & 1 == 0 { + bail!("Asynchronous. Do not support."); + } + + // Outbuf: + // Bytes[0-3]: Event Header. + // Bytes[4-n]: Event Descriptor. + // Bytes[0-1]: Event Descriptor Length. + // Byte2: Bit7: NEC(No Event Available). Bits[0-2]: Notification Class. + // NEC = 1: The Drive supports none of the requested notification classes. + // NEC = 0: At least one of the requested notification classes is supported. + // Byte3: Supported Event Class. + let mut outbuf: Vec = vec![0; 4]; + + outbuf[3] = 1 << GESN_MEDIA; + if notification_class_request & (1 << GESN_MEDIA) != 0 { + // NCE = 0, notification class = media. + outbuf[2] = GESN_MEDIA; + outbuf.resize(8, 0); + // Bytes[4-7]: Media Event Descriptor. + // Byte4: Bits[4-7]: reserved. Bits[0-3]: Event Code. + // Byte5: Media Status. Bits[2-7] reserved. Bit 1: Media Present. Bit 0: Door or Tray open. + // Byte6: Start Slot. + // Byte7: End Slot. + + // Do not support hot-plug/hot-unplug scsi cd which will be present all the time once vm starts. + // To do: this outbuf event code and media status should be changed after allowing hot-plug. + outbuf[4] = GESN_EC_NOCHG; + outbuf[5] = 1 << GESN_MS_MEDIA_PRESENT_BIT; + } else { + // NCE = 1. + outbuf[2] = 0x80; + } + + let len = outbuf.len() as u16 - 2; + BigEndian::write_u16(&mut outbuf[0..2], len); + + Ok(outbuf) +} + +/// LBA to MSF translation is defined in MMC6 Table 647. +/// MSF values are converted to LBA values via such formula: +/// lba = ((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET. +fn lba_to_msf(lba: u32) -> Vec { + let minute = ((lba + CD_MSF_OFFSET) / CD_FRAMES / CD_SECS) as u8; + let second = ((lba + CD_MSF_OFFSET) / CD_FRAMES % CD_SECS) as u8; + let frame = ((lba + CD_MSF_OFFSET) % CD_FRAMES) as u8; + + vec![minute, second, frame] +} + +fn cdrom_read_formatted_toc(nb_sectors: u32, msf: u8, track_number: u8) -> Result> { + // Track number 0xaa is reported only for the Lead-out area of the last complete session. + if track_number > 1 && track_number != 0xaa { + bail!("Invalid track number!"); + } + + let mut outbuf: Vec = vec![0; 4]; + + // Outbuf: + // Bytes[0-1]: TOC Data Length. + // Byte[2]: First Track Number(1). + // Byte[3]: Last Track Number(1). + outbuf[2] = 1; + outbuf[3] = 1; + if track_number <= 1 { + // Byte[4]: Reserved. + // Byte[5]: Bits[5-7]: ADR, Bits[0-4]: CONTROL. + // Byte[6]: Track Number. + // Byte[7]: Reserved. + // Bytes[8-11]: Track Start Address(LBA form = 000000h, MSF form = 00:00:02:00). + outbuf.append(&mut [0, 0x14, 1, 0].to_vec()); + if msf != 0 { + // MSF form. + outbuf.push(0); + outbuf.append(&mut lba_to_msf(0)); + } else { + outbuf.append(&mut [0, 0, 0, 0].to_vec()); + } + } + + // Lead Out Track. + // Byte[temporary buflen]: Reserved. + // Byte[temporary buflen + 1]: Bits[5-7]: ADR, Bits[0-4]: CONTROL. + // Byte[temporary buflen + 2]: Track Number. + // Byte[temporary buflen + 3]: Reserved. + // Bytes[temporary buflen + 4 - temporary buflen + 7]: Track Start Address. + outbuf.append(&mut [0, 0x14, 0xaa, 0].to_vec()); + if msf != 0 { + outbuf.push(0); + outbuf.append(&mut lba_to_msf(nb_sectors)); + } else { + let pos = outbuf.len(); + outbuf.resize(pos + 4, 0); + BigEndian::write_u32(&mut outbuf[pos..pos + 4], nb_sectors); + } + + let len = outbuf.len() as u16; + BigEndian::write_u16(&mut outbuf[0..2], len - 2); + + Ok(outbuf) +} -- Gitee From cdf843ca70a3ac762abb2509f375634e31de0129 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 3 Dec 2022 18:13:53 +0800 Subject: [PATCH 0543/2187] scsi-cd: implement scsi cd-rom We can now use scsi-cd by using command just like: -device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0 -drive file=/path/iso,id=drive-scsi0-0-0-0,readonly=true -device scsi-cd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,bootindex=1] --- machine/src/lib.rs | 5 ++++- virtio/src/scsi/bus.rs | 10 ++++++++-- virtio/src/scsi/disk.rs | 8 ++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9119e9f57..c62ab7171 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -85,7 +85,7 @@ use virtio::{ VirtioMmioState, VirtioNetState, VirtioPciDevice, }; use ScsiCntlr::ScsiCntlrMap; -use ScsiDisk::SCSI_TYPE_DISK; +use ScsiDisk::{SCSI_TYPE_DISK, SCSI_TYPE_ROM}; pub trait MachineOps { /// Calculate the ranges of memory according to architecture. @@ -1183,6 +1183,9 @@ pub trait MachineOps { "scsi-hd" => { self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_DISK)?; } + "scsi-cd" => { + self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_ROM)?; + } "virtio-net-device" => { self.add_virtio_mmio_net(vm_config, cfg_args)?; } diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index bebf1c513..589223988 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -24,7 +24,8 @@ use crate::ScsiCntlr::{ VirtioScsiRequest, VIRTIO_SCSI_CDB_DEFAULT_SIZE, VIRTIO_SCSI_S_OK, }; use crate::ScsiDisk::{ - ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, + ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, + SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, }; use address_space::AddressSpace; @@ -519,12 +520,17 @@ impl ScsiRequest { last_aio: bool, iocompletecb: ScsiCompleteCb, ) -> Result { + let dev_lock = self.dev.lock().unwrap(); + let offset = match dev_lock.scsi_type { + SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, + _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, + }; let mut aiocb = AioCb { last_aio, file_fd: disk.as_raw_fd(), opcode: IoCmd::Noop, iovec: Vec::new(), - offset: (self.cmd.lba << 9) as usize, + offset: (self.cmd.lba << offset) as usize, nbytes: 0, process: true, iocb: None, diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 4096d278d..92cfb9471 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -50,9 +50,12 @@ const DUMMY_IMG_SIZE: u64 = 0; pub const DEFAULT_SECTOR_SIZE: u32 = 1_u32 << SECTOR_SHIFT; /// Scsi disk's block size is 512 Bytes. -pub const SCSI_DISK_DEFAULT_BLOCK_SIZE: u32 = 512; +pub const SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT: u32 = 9; +pub const SCSI_DISK_DEFAULT_BLOCK_SIZE: u32 = 1 << SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT; + /// Scsi media device's block size is 2048 Bytes. -pub const SCSI_CDROM_DEFAULT_BLOCK_SIZE: u32 = 2048; +pub const SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT: u32 = 11; +pub const SCSI_CDROM_DEFAULT_BLOCK_SIZE: u32 = 1 << SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT; #[derive(Clone, Default)] pub struct ScsiDevState { @@ -144,6 +147,7 @@ impl ScsiDevice { } SCSI_TYPE_ROM => { self.block_size = SCSI_CDROM_DEFAULT_BLOCK_SIZE; + self.state.product = "STRA CDROM".to_string(); } _ => { bail!("Scsi type {} does not support now", self.scsi_type); -- Gitee From 4842070a4cf01cb2638a6ff1969d24d27aa34ab2 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 28 Nov 2022 14:15:07 +0800 Subject: [PATCH 0544/2187] virtiofsd: introduce TempCleaner to viritofsd When the virtiofsd process ends, we need to clear resources(socket files). Signed-off-by: Li HuaChao --- vhost_user_fs/src/main.rs | 4 ++++ vhost_user_fs/src/vhost_user_server.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index b8549d1d7..20705d4e6 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -14,6 +14,7 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use machine_manager::event_loop::EventLoop; use machine_manager::signal_handler; +use machine_manager::temp_cleaner::TempCleaner; use std::collections::HashSet; use std::io::Write; use std::os::unix::fs::OpenOptionsExt; @@ -132,6 +133,8 @@ fn run() -> Result<()> { } fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { + TempCleaner::object_init(); + let mut fsconfig: FsConfig = create_fs_config(cmd_args)?; info!("FsConfig is {:?}", fsconfig); @@ -211,6 +214,7 @@ fn init_log(logfile_path: String) -> Result<()> { fn set_panic_hook() { std::panic::set_hook(Box::new(|panic_msg| { + TempCleaner::clean(); let panic_file = panic_msg.location().map_or("", |loc| loc.file()); let panic_line = panic_msg.location().map_or(0, |loc| loc.line()); if let Some(msg) = panic_msg.payload().downcast_ref::<&str>() { diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index d873ac6f7..ad725a7ab 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use log::error; +use machine_manager::temp_cleaner::TempCleaner; use std::mem::size_of; use std::os::unix::io::RawFd; use std::slice; @@ -158,6 +159,7 @@ impl VhostUserServerHandler { sock.domain .bind(true) .with_context(|| format!("Failed to bind for vhost user server {}", path))?; + TempCleaner::add_path(path.to_string()); limit_permission(path).with_context(|| format!("Failed to limit permission {}", path))?; Ok(VhostUserServerHandler { sock, backend }) -- Gitee From b166bee4af246a368b292499de9a233aec9bdcf7 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 28 Nov 2022 14:28:15 +0800 Subject: [PATCH 0545/2187] virtiofsd: delete the check for socket path when create fs config Previously, when creating fs config, we checked to see if the file was a regular file, and if it was not, we reported an error. In fact, socket file is not a regular file type, and an error is also reported. Delete the check logic. Signed-off-by: Li HuaChao --- vhost_user_fs/src/cmdline.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index cc56f42ab..fae61458a 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -140,11 +140,6 @@ impl FsConfig { ); } - let sock_path = PathBuf::from(&self.sock_path); - if !sock_path.is_file() { - bail!("The socket path {} is not a file", self.sock_path); - } - Ok(()) } } -- Gitee From 53d56c6cf278e7a9c5ba3252d8ccca9e9d88e22b Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 5 Dec 2022 11:05:40 +0800 Subject: [PATCH 0546/2187] virtiofsd: not to delete socket file when virtiofsd starts We should not delete socket file when it exists. In this case, UnixListener::bind will handle it, and an error "Address already in use (os error 98)" will be reported. Signed-off-by: Li HuaChao --- vhost_user_fs/src/vhost_user_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index ad725a7ab..713dede48 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -157,7 +157,7 @@ impl VhostUserServerHandler { pub fn new(path: &str, backend: Arc>) -> Result { let mut sock = VhostUserSock::new(path); sock.domain - .bind(true) + .bind(false) .with_context(|| format!("Failed to bind for vhost user server {}", path))?; TempCleaner::add_path(path.to_string()); limit_permission(path).with_context(|| format!("Failed to limit permission {}", path))?; -- Gitee From 1511fd5cfe6bf6ac2bc2eec4c0faf2a00fb2d078 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 04:35:32 +0800 Subject: [PATCH 0547/2187] machine: Move get_vm_state from std_machine to machine As get_vm_state should be a common method for machine. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 4 +++- machine/src/micro_vm/mod.rs | 6 +++++- machine/src/standard_vm/aarch64/mod.rs | 8 ++++---- machine/src/standard_vm/mod.rs | 4 +--- machine/src/standard_vm/x86_64/mod.rs | 8 ++++---- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a8d21f624..0fa66e201 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -22,7 +22,7 @@ use std::fs::remove_file; use std::net::TcpListener; use std::os::unix::{io::AsRawFd, net::UnixListener}; use std::path::Path; -use std::sync::{Arc, Barrier, Mutex, Weak}; +use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use kvm_ioctls::VcpuFd; use log::warn; @@ -298,6 +298,8 @@ pub trait MachineOps { fn get_vm_config(&self) -> &Mutex; + fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)>; + /// Get migration mode and path from VM config. There are four modes in total: /// Tcp, Unix, File and Unknown. fn get_migrate_info(&self) -> Incoming; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 0cddd0178..dfa44cc57 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -611,6 +611,10 @@ impl MachineOps for LightMachine { &self.vm_config } + fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { + &self.vm_state + } + fn get_migrate_info(&self) -> Incoming { if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { return (*mode, path.to_string()); @@ -918,7 +922,7 @@ impl MachineAddressInterface for LightMachine { impl DeviceInterface for LightMachine { fn query_status(&self) -> Response { - let vmstate = self.vm_state.deref().0.lock().unwrap(); + let vmstate = self.get_vm_state().deref().0.lock().unwrap(); let qmp_state = match *vmstate { KvmVmState::Running => qmp_schema::StatusInfo { singlestep: false, diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 88a6aa538..ca8511ade 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -356,10 +356,6 @@ impl StdMachineOps for StdMachine { Ok(Some(fwcfg_dev)) } - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.vm_state - } - fn get_cpu_topo(&self) -> &CpuTopology { &self.cpu_topo } @@ -625,6 +621,10 @@ impl MachineOps for StdMachine { &self.vm_config } + fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { + &self.vm_state + } + fn get_migrate_info(&self) -> Incoming { if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { return (*mode, path.to_string()); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 943276094..c6233bcdb 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -34,7 +34,7 @@ use std::mem::size_of; use std::ops::Deref; use std::os::unix::io::RawFd; use std::os::unix::prelude::AsRawFd; -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Mutex}; use super::Result as MachineResult; use crate::MachineOps; @@ -174,8 +174,6 @@ trait StdMachineOps: AcpiBuilder { bail!("Not implemented"); } - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)>; - fn get_cpu_topo(&self) -> &CpuTopology; fn get_cpus(&self) -> &Vec>; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index a449bf7f8..438b009c2 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -314,10 +314,6 @@ impl StdMachineOps for StdMachine { Ok(Some(fwcfg_dev)) } - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.vm_state - } - fn get_cpu_topo(&self) -> &CpuTopology { &self.cpu_topo } @@ -574,6 +570,10 @@ impl MachineOps for StdMachine { &self.vm_config } + fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { + &self.vm_state + } + fn get_migrate_info(&self) -> Incoming { if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { return (*mode, path.to_string()); -- Gitee From a316d3b6c892a4c0e1c318f249901b31fefe4b8a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 05:33:12 +0800 Subject: [PATCH 0548/2187] machine: Convert lifecycle related function to method We will invoke methods in these functions. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 55 +++++++++++--------------- machine/src/micro_vm/mod.rs | 4 +- machine/src/standard_vm/aarch64/mod.rs | 4 +- machine/src/standard_vm/x86_64/mod.rs | 11 ++---- 4 files changed, 29 insertions(+), 45 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 0fa66e201..790b0a137 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1289,10 +1289,7 @@ pub trait MachineOps { /// * `paused` - After started, paused all vcpu or not. /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. - fn vm_start(paused: bool, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> - where - Self: Sized, - { + fn vm_start(&self, paused: bool, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { let nr_vcpus = cpus.len(); let cpus_thread_barrier = Arc::new(Barrier::new((nr_vcpus + 1) as usize)); for cpu_index in 0..nr_vcpus { @@ -1319,13 +1316,11 @@ pub trait MachineOps { /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_pause( + &self, cpus: &[Arc], #[cfg(target_arch = "aarch64")] irq_chip: &Option>, vm_state: &mut KvmVmState, - ) -> Result<()> - where - Self: Sized, - { + ) -> Result<()> { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.pause() .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; @@ -1345,10 +1340,7 @@ pub trait MachineOps { /// /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. - fn vm_resume(cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> - where - Self: Sized, - { + fn vm_resume(&self, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.resume() .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; @@ -1365,10 +1357,7 @@ pub trait MachineOps { /// /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. - fn vm_destroy(cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> - where - Self: Sized, - { + fn vm_destroy(&self, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.destroy() .with_context(|| format!("Failed to destroy vcpu{}", cpu_index))?; @@ -1388,15 +1377,13 @@ pub trait MachineOps { /// * `old_state` - Old vm state want to leave. /// * `new_state` - New vm state want to transfer to. fn vm_state_transfer( + &self, cpus: &[Arc], #[cfg(target_arch = "aarch64")] irq_chip: &Option>, vm_state: &mut KvmVmState, old_state: KvmVmState, new_state: KvmVmState, - ) -> Result<()> - where - Self: Sized, - { + ) -> Result<()> { use KvmVmState::*; if *vm_state != old_state { @@ -1404,21 +1391,23 @@ pub trait MachineOps { } match (old_state, new_state) { - (Created, Running) => ::vm_start(false, cpus, vm_state) + (Created, Running) => self + .vm_start(false, cpus, vm_state) .with_context(|| "Failed to start vm.")?, - (Running, Paused) => ::vm_pause( - cpus, - #[cfg(target_arch = "aarch64")] - irq_chip, - vm_state, - ) - .with_context(|| "Failed to pause vm.")?, - (Paused, Running) => ::vm_resume(cpus, vm_state) + (Running, Paused) => self + .vm_pause( + cpus, + #[cfg(target_arch = "aarch64")] + irq_chip, + vm_state, + ) + .with_context(|| "Failed to pause vm.")?, + (Paused, Running) => self + .vm_resume(cpus, vm_state) .with_context(|| "Failed to resume vm.")?, - (_, Shutdown) => { - ::vm_destroy(cpus, vm_state) - .with_context(|| "Failed to destroy vm.")?; - } + (_, Shutdown) => self + .vm_destroy(cpus, vm_state) + .with_context(|| "Failed to destroy vm.")?, (_, _) => { bail!("Vm lifecycle error: this transform is illegal."); } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index dfa44cc57..3c5ece562 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -821,7 +821,7 @@ impl MachineOps for LightMachine { } fn run(&self, paused: bool) -> MachineResult<()> { - ::vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) + self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) } } @@ -869,7 +869,7 @@ impl MachineLifecycle for LightMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - ::vm_state_transfer( + self.vm_state_transfer( &self.cpus, #[cfg(target_arch = "aarch64")] &self.irq_chip, diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index ca8511ade..a020adf10 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -610,7 +610,7 @@ impl MachineOps for StdMachine { } fn run(&self, paused: bool) -> Result<()> { - ::vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) + self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) } fn get_sys_mem(&mut self) -> &Arc { @@ -981,7 +981,7 @@ impl MachineLifecycle for StdMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - ::vm_state_transfer( + self.vm_state_transfer( &self.cpus, &self.irq_chip, &mut self.vm_state.0.lock().unwrap(), diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 438b009c2..e8fb7a30b 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -559,7 +559,7 @@ impl MachineOps for StdMachine { } fn run(&self, paused: bool) -> Result<()> { - ::vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) + self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) } fn get_sys_mem(&mut self) -> &Arc { @@ -835,13 +835,8 @@ impl MachineLifecycle for StdMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - ::vm_state_transfer( - &self.cpus, - &mut self.vm_state.0.lock().unwrap(), - old, - new, - ) - .is_ok() + self.vm_state_transfer(&self.cpus, &mut self.vm_state.0.lock().unwrap(), old, new) + .is_ok() } } -- Gitee From 5b2a51ac9464d15ae6a5e58e9eedb822b7f05c6c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 05:50:50 +0800 Subject: [PATCH 0549/2187] machine: Add drive backend files store This will be used to store opened files related to all drive device. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 104 ++++++++++++++++++++++--- machine/src/micro_vm/mod.rs | 10 ++- machine/src/standard_vm/aarch64/mod.rs | 7 ++ machine/src/standard_vm/x86_64/mod.rs | 11 ++- machine_manager/src/config/drive.rs | 14 +++- machine_manager/src/config/mod.rs | 54 ++++++++++++- util/src/file.rs | 37 +++++++-- virtio/src/block.rs | 4 +- virtio/src/scsi/disk.rs | 4 +- 9 files changed, 219 insertions(+), 26 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 790b0a137..b5dea42b2 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -17,15 +17,17 @@ mod standard_vm; mod vm_state; pub use crate::error::MachineError; -use std::collections::BTreeMap; -use std::fs::remove_file; +use std::collections::{BTreeMap, HashMap}; +use std::fs::{remove_file, File}; use std::net::TcpListener; +use std::ops::Deref; use std::os::unix::{io::AsRawFd, net::UnixListener}; use std::path::Path; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use kvm_ioctls::VcpuFd; use log::warn; +use util::file::{lock_file, unlock_file}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub use micro_vm::LightMachine; @@ -52,8 +54,8 @@ use machine_manager::config::{ parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_scsi_controller, parse_scsi_device, parse_usb_keyboard, parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, - BootIndexInfo, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, - NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, + NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::{ @@ -1265,6 +1267,74 @@ pub trait MachineOps { Ok(()) } + /// Get the drive backend files. + fn get_drive_files(&self) -> Arc>>; + + /// Fetch a cloned file from drive backend files. + fn fetch_drive_file(&self, path: &str) -> Result { + let files = self.get_drive_files(); + let drive_files = files.lock().unwrap(); + match drive_files.get(path) { + Some(drive_file) => drive_file + .file + .try_clone() + .with_context(|| format!("Failed to clone drive backend file {}", path)), + None => Err(anyhow!("The file {} is not in drive backend", path)), + } + } + + /// Register a new drive backend file. + fn register_drive_file(&self, path: &str, read_only: bool, direct: bool) -> Result<()> { + let files = self.get_drive_files(); + let mut drive_files = files.lock().unwrap(); + VmConfig::add_drive_file(&mut drive_files, path, read_only, direct)?; + + // Lock the added file if VM is running. + let drive_file = drive_files.get_mut(path).unwrap(); + let vm_state = self.get_vm_state().deref().0.lock().unwrap(); + if *vm_state == KvmVmState::Running { + if let Err(e) = lock_file(&drive_file.file, path, read_only) { + drive_files.remove(path); + return Err(e); + } + drive_file.locked = true; + } + Ok(()) + } + + /// Unregister a drive backend file. + fn unregister_drive_file(&self, path: &str) -> Result { + self.get_drive_files() + .lock() + .unwrap() + .remove(path) + .with_context(|| "Failed to unregister drive file") + } + + /// Active drive backend files. i.e., Apply lock. + fn active_drive_files(&self) -> Result<()> { + for drive_file in self.get_drive_files().lock().unwrap().values_mut() { + if drive_file.locked { + continue; + } + lock_file(&drive_file.file, &drive_file.path, drive_file.read_only)?; + drive_file.locked = true; + } + Ok(()) + } + + /// Deactive drive backend files. i.e., Release lock. + fn deactive_drive_files(&self) -> Result<()> { + for drive_file in self.get_drive_files().lock().unwrap().values_mut() { + if !drive_file.locked { + continue; + } + unlock_file(&drive_file.file, &drive_file.path)?; + drive_file.locked = false; + } + Ok(()) + } + /// Realize the machine. /// /// # Arguments @@ -1290,13 +1360,19 @@ pub trait MachineOps { /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_start(&self, paused: bool, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { + if !paused { + self.active_drive_files()?; + } + let nr_vcpus = cpus.len(); let cpus_thread_barrier = Arc::new(Barrier::new((nr_vcpus + 1) as usize)); for cpu_index in 0..nr_vcpus { let cpu_thread_barrier = cpus_thread_barrier.clone(); let cpu = cpus[cpu_index as usize].clone(); - CPU::start(cpu, cpu_thread_barrier, paused) - .with_context(|| format!("Failed to run vcpu{}", cpu_index))?; + if let Err(e) = CPU::start(cpu, cpu_thread_barrier, paused) { + self.deactive_drive_files()?; + return Err(anyhow!("Failed to run vcpu{}, {:?}", cpu_index, e)); + } } if paused { @@ -1321,9 +1397,13 @@ pub trait MachineOps { #[cfg(target_arch = "aarch64")] irq_chip: &Option>, vm_state: &mut KvmVmState, ) -> Result<()> { + self.deactive_drive_files()?; + for (cpu_index, cpu) in cpus.iter().enumerate() { - cpu.pause() - .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; + if let Err(e) = cpu.pause() { + self.active_drive_files()?; + return Err(anyhow!("Failed to pause vcpu{}, {:?}", cpu_index, e)); + } } #[cfg(target_arch = "aarch64")] @@ -1341,9 +1421,13 @@ pub trait MachineOps { /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_resume(&self, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { + self.active_drive_files()?; + for (cpu_index, cpu) in cpus.iter().enumerate() { - cpu.resume() - .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; + if let Err(e) = cpu.resume() { + self.deactive_drive_files()?; + return Err(anyhow!("Failed to resume vcpu{}, {:?}", cpu_index, e)); + } } *vm_state = KvmVmState::Running; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 3c5ece562..7e55328a5 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -37,6 +37,7 @@ mod syscall; use super::Result as MachineResult; use log::error; +use std::collections::HashMap; use std::fmt; use std::fmt::Debug; use std::ops::Deref; @@ -63,7 +64,7 @@ use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, Incoming, MigrateMode, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, DriveFile, Incoming, MigrateMode, }; use machine_manager::event; use machine_manager::machine::{ @@ -180,6 +181,8 @@ pub struct LightMachine { power_button: EventFd, // All configuration information of virtual machine. vm_config: Mutex, + // Drive backend files. + drive_files: Arc>>, } impl LightMachine { @@ -236,6 +239,7 @@ impl LightMachine { vm_state, power_button, vm_config: Mutex::new(vm_config.clone()), + drive_files: Arc::new(Mutex::new(HashMap::new())), }) } @@ -713,6 +717,10 @@ impl MachineOps for LightMachine { syscall_whitelist() } + fn get_drive_files(&self) -> Arc>> { + self.drive_files.clone() + } + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { let mut locked_vm = vm.lock().unwrap(); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index a020adf10..8d755a765 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -152,6 +152,8 @@ pub struct StdMachine { bus_device: BusDeviceMap, /// Scsi Controller List. scsi_cntlr_list: ScsiCntlrMap, + /// Drive backend files. + drive_files: Arc>>, } impl StdMachine { @@ -204,6 +206,7 @@ impl StdMachine { fwcfg_dev: None, bus_device: Arc::new(Mutex::new(HashMap::new())), scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), + drive_files: Arc::new(Mutex::new(HashMap::new())), }) } @@ -458,6 +461,10 @@ impl MachineOps for StdMachine { syscall_whitelist() } + fn get_drive_files(&self) -> Arc>> { + self.drive_files.clone() + } + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { use super::error::StandardVmError as StdErrorKind; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index e8fb7a30b..c26f3f943 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -39,8 +39,8 @@ use devices::legacy::{ use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, Incoming, MigrateMode, NumaNode, NumaNodes, - PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, + NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -128,6 +128,8 @@ pub struct StdMachine { bus_device: BusDeviceMap, /// Scsi Controller List. scsi_cntlr_list: ScsiCntlrMap, + /// Drive backend files. + drive_files: Arc>>, } impl StdMachine { @@ -180,6 +182,7 @@ impl StdMachine { fwcfg_dev: None, bus_device: Arc::new(Mutex::new(HashMap::new())), scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), + drive_files: Arc::new(Mutex::new(HashMap::new())), }) } @@ -414,6 +417,10 @@ impl MachineOps for StdMachine { syscall_whitelist() } + fn get_drive_files(&self) -> Arc>> { + self.drive_files.clone() + } + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let clone_vm = vm.clone(); diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 2715d23ac..332423037 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::fs::metadata; +use std::fs::{metadata, File}; use std::os::linux::fs::MetadataExt; use std::path::Path; @@ -29,6 +29,18 @@ const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; +/// Represent a single drive backend file. +pub struct DriveFile { + /// The opened file. + pub file: File, + /// File path. + pub path: String, + /// File is read only or not. + pub read_only: bool, + /// File lock status. + pub locked: bool, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct BlkDevConfig { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index c47a360b4..9aae29f10 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -64,7 +64,7 @@ use anyhow::{anyhow, bail, Context, Result}; use log::error; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; -use util::trace::enable_trace_events; +use util::{file::open_file, trace::enable_trace_events}; pub const MAX_STRING_LENGTH: usize = 255; pub const MAX_PATH_LENGTH: usize = 4096; @@ -238,6 +238,58 @@ impl VmConfig { } Ok(()) } + + /// Add a file to drive file store. + pub fn add_drive_file( + drive_files: &mut HashMap, + path: &str, + read_only: bool, + direct: bool, + ) -> Result<()> { + if let Some(drive_file) = drive_files.get(path) { + if drive_file.read_only && read_only { + // File can be shared with read_only. + return Ok(()); + } else { + return Err(anyhow!( + "Failed to add drive {}, file can only be shared with read_only", + path + )); + } + } + let drive_file = DriveFile { + file: open_file(path, read_only, direct)?, + read_only, + path: path.to_string(), + locked: false, + }; + drive_files.insert(path.to_string(), drive_file); + Ok(()) + } + + /// Create initial drive file store from cmdline drive. + pub fn init_drive_files(&self) -> Result> { + let mut drive_files: HashMap = HashMap::new(); + for drive in self.drives.values() { + Self::add_drive_file( + &mut drive_files, + &drive.path_on_host, + drive.read_only, + drive.direct, + )?; + } + if let Some(pflashs) = self.pflashs.as_ref() { + for pflash in pflashs { + Self::add_drive_file( + &mut drive_files, + &pflash.path_on_host, + pflash.read_only, + false, + )?; + } + } + Ok(drive_files) + } } #[cfg(target_arch = "aarch64")] diff --git a/util/src/file.rs b/util/src/file.rs index 2834ed7ac..0ee56702b 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -16,16 +16,24 @@ use std::os::unix::io::AsRawFd; use anyhow::{bail, Context, Result}; -pub fn open_disk_file(path: &str, read_only: bool, direct: bool) -> Result { +pub fn open_file(path: &str, read_only: bool, direct: bool) -> Result { let mut options = OpenOptions::new(); options.read(true).write(!read_only); if direct { options.custom_flags(libc::O_DIRECT); } - let file = options - .open(path) - .with_context(|| format!("failed to open the file for block {}", path))?; + let file = options.open(path).with_context(|| { + format!( + "failed to open the file for block {}. Error: {}", + path, + std::io::Error::last_os_error(), + ) + })?; + Ok(file) +} + +pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { let (lockop, lockname) = if read_only { (libc::LOCK_SH | libc::LOCK_NB, "read lock") } else { @@ -34,10 +42,25 @@ pub fn open_disk_file(path: &str, read_only: bool, direct: bool) -> Result let ret = unsafe { libc::flock(file.as_raw_fd(), lockop) }; if ret < 0 { bail!( - "Failed to get {} on file: {}. Maybe it's used more than once.", + "Failed to get {} on file: {}. Maybe it's used more than once. Error: {}", lockname, - path + path, + std::io::Error::last_os_error(), ); } - Ok(file) + + Ok(()) +} + +pub fn unlock_file(file: &File, path: &str) -> Result<()> { + let ret = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_UN) }; + if ret < 0 { + bail!( + "Failed to release lock on file: {}. Error: {}", + path, + std::io::Error::last_os_error(), + ); + } + + Ok(()) } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index cc84ae08e..a45b71de1 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -46,7 +46,7 @@ use util::aio::{ iov_from_buf_direct, raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE, }; use util::byte_code::ByteCode; -use util::file::open_disk_file; +use util::file::open_file; use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -1011,7 +1011,7 @@ impl VirtioDevice for Block { let mut disk_image = None; let mut disk_size = DUMMY_IMG_SIZE; if !self.blk_cfg.path_on_host.is_empty() { - let mut file = open_disk_file( + let mut file = open_file( &self.blk_cfg.path_on_host, self.blk_cfg.read_only, self.blk_cfg.direct, diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index dd6943de1..89fdad519 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -18,7 +18,7 @@ use anyhow::{bail, Context, Result}; use crate::ScsiBus::ScsiBus; use machine_manager::config::ScsiDevConfig; -use util::file::open_disk_file; +use util::file::open_file; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -137,7 +137,7 @@ impl ScsiDevice { if !self.config.path_on_host.is_empty() { self.disk_image = None; - let mut file = open_disk_file( + let mut file = open_file( &self.config.path_on_host, self.config.read_only, self.config.direct, -- Gitee From 49b05e5df91e770b3df4de9fb25999e140768067 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 09:17:26 +0800 Subject: [PATCH 0550/2187] drive: Manage drive files lock and unlock During migration, the src VM should unlock drive files before dest VM lock them. 1. For cmdline drives, push them to backend store by vm_comfig init_drive_files() at once. 2. For qmp added drives, push them to backend store individually. The pflahs and block devices should fetch drive file from file store. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 6 +++- machine/src/micro_vm/mod.rs | 13 +++++++-- machine/src/standard_vm/aarch64/mod.rs | 13 +++------ machine/src/standard_vm/mod.rs | 31 ++++++++++++++++----- machine/src/standard_vm/x86_64/mod.rs | 11 ++------ machine_manager/src/config/drive.rs | 5 ++-- virtio/src/block.rs | 38 +++++++++++++++++++++----- virtio/src/scsi/disk.rs | 23 ++++++++++------ 8 files changed, 94 insertions(+), 46 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index b5dea42b2..5ce3b0c16 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -627,7 +627,10 @@ pub trait MachineOps { self.check_bootindex(bootindex) .with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?; } - let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); + let device = Arc::new(Mutex::new(Block::new( + device_cfg.clone(), + self.get_drive_files(), + ))); let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) .with_context(|| "Failed to add virtio pci device")?; @@ -681,6 +684,7 @@ pub trait MachineOps { let device = Arc::new(Mutex::new(ScsiDisk::ScsiDevice::new( device_cfg.clone(), scsi_type, + self.get_drive_files(), ))); let lock_cntlr_list = if let Some(cntlr_list) = self.get_scsi_cntlr_list() { diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 7e55328a5..6388c319f 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -239,7 +239,7 @@ impl LightMachine { vm_state, power_button, vm_config: Mutex::new(vm_config.clone()), - drive_files: Arc::new(Mutex::new(HashMap::new())), + drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) } @@ -1103,7 +1103,7 @@ impl DeviceInterface for LightMachine { let config = BlkDevConfig { id: args.node_name.clone(), - path_on_host: args.file.filename, + path_on_host: args.file.filename.clone(), read_only, direct, serial_num: None, @@ -1127,10 +1127,19 @@ impl DeviceInterface for LightMachine { None, ); } + // Register drive backend file for hotplugged drive. + if let Err(e) = self.register_drive_file(&args.file.filename, read_only, direct) { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } match self.add_replaceable_config(&args.node_name, Arc::new(config)) { Ok(()) => Response::create_empty_response(), Err(ref e) => { error!("{:?}", e); + self.unregister_drive_file(&args.file.filename).unwrap(); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 8d755a765..1eaedfc21 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -16,7 +16,6 @@ mod syscall; pub use crate::error::MachineError; use std::borrow::Borrow; use std::collections::HashMap; -use std::fs::OpenOptions; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; @@ -47,8 +46,8 @@ use devices::legacy::{ use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, Incoming, MigrateMode, NumaNode, NumaNodes, - PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, + NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -206,7 +205,7 @@ impl StdMachine { fwcfg_dev: None, bus_device: Arc::new(Mutex::new(HashMap::new())), scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), - drive_files: Arc::new(Mutex::new(HashMap::new())), + drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) } @@ -582,11 +581,7 @@ impl MachineOps for StdMachine { let (fd, read_only) = if i < configs_vec.len() { let path = &configs_vec[i].path_on_host; let read_only = configs_vec[i].read_only; - let fd = OpenOptions::new() - .read(true) - .write(!read_only) - .open(path) - .with_context(|| anyhow!(StdErrorKind::OpenFileErr(path.to_string())))?; + let fd = self.fetch_drive_file(path)?; (Some(fd), read_only) } else { (None, false) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index c6233bcdb..792deb268 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -778,7 +778,7 @@ impl StdMachine { } let blk_id = blk.id.clone(); - let blk = Arc::new(Mutex::new(Block::new(blk))); + let blk = Arc::new(Mutex::new(Block::new(blk, self.get_drive_files()))); let pci_dev = self .add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) .with_context(|| "Failed to add virtio pci block device")?; @@ -1188,7 +1188,7 @@ impl DeviceInterface for StdMachine { }; let config = DriveConfig { id: args.node_name, - path_on_host: args.file.filename, + path_on_host: args.file.filename.clone(), read_only, direct, iops: args.iops, @@ -1201,6 +1201,7 @@ impl DeviceInterface for StdMachine { }; if let Err(e) = config.check() { + error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -1208,6 +1209,15 @@ impl DeviceInterface for StdMachine { } // Check whether path is valid after configuration check if let Err(e) = config.check_path() { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + // Register drive backend file for hotplug drive. + if let Err(e) = self.register_drive_file(&args.file.filename, read_only, direct) { + error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -1220,10 +1230,14 @@ impl DeviceInterface for StdMachine { .add_drive_with_config(config) { Ok(()) => Response::create_empty_response(), - Err(e) => Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ), + Err(e) => { + error!("{:?}", e); + self.unregister_drive_file(&args.file.filename).unwrap(); + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ) + } } } @@ -1234,7 +1248,10 @@ impl DeviceInterface for StdMachine { .unwrap() .del_drive_by_id(&node_name) { - Ok(()) => Response::create_empty_response(), + Ok(path) => { + self.unregister_drive_file(&path).unwrap(); + Response::create_empty_response() + } Err(e) => Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index c26f3f943..725f33450 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -17,7 +17,6 @@ mod syscall; use crate::error::MachineError; use log::error; use std::collections::HashMap; -use std::fs::OpenOptions; use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; @@ -182,7 +181,7 @@ impl StdMachine { fwcfg_dev: None, bus_device: Arc::new(Mutex::new(HashMap::new())), scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), - drive_files: Arc::new(Mutex::new(HashMap::new())), + drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) } @@ -506,13 +505,7 @@ impl MachineOps for StdMachine { // of current PFlash device. let mut flash_end: u64 = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; for config in configs_vec { - let mut fd = OpenOptions::new() - .read(true) - .write(!config.read_only) - .open(&config.path_on_host) - .with_context(|| { - anyhow!(StandardVmError::OpenFileErr(config.path_on_host.clone())) - })?; + let mut fd = self.fetch_drive_file(&config.path_on_host)?; let pfl_size = fd.metadata().unwrap().len(); if config.unit == 0 { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 332423037..e009b3aed 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -497,13 +497,12 @@ impl VmConfig { /// # Arguments /// /// * `drive_id` - Drive id. - pub fn del_drive_by_id(&mut self, drive_id: &str) -> Result<()> { + pub fn del_drive_by_id(&mut self, drive_id: &str) -> Result { if self.drives.get(drive_id).is_some() { - self.drives.remove(drive_id); + Ok(self.drives.remove(drive_id).unwrap().path_on_host) } else { bail!("Drive {} not found", drive_id); } - Ok(()) } /// Add new flash device to `VmConfig`. diff --git a/virtio/src/block.rs b/virtio/src/block.rs index a45b71de1..6b721b01f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::cmp; +use std::collections::HashMap; use std::fs::File; use std::io::{Seek, SeekFrom, Write}; use std::mem::size_of; @@ -33,6 +34,7 @@ use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, warn}; +use machine_manager::config::DriveFile; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, @@ -46,7 +48,6 @@ use util::aio::{ iov_from_buf_direct, raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE, }; use util::byte_code::ByteCode; -use util::file::open_file; use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -940,6 +941,8 @@ pub struct Block { update_evt: EventFd, /// Eventfd for device deactivate. deactivate_evt: EventFd, + /// Drive backend files. + drive_files: Arc>>, } impl Default for Block { @@ -953,12 +956,16 @@ impl Default for Block { senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + drive_files: Arc::new(Mutex::new(HashMap::new())), } } } impl Block { - pub fn new(blk_cfg: BlkDevConfig) -> Block { + pub fn new( + blk_cfg: BlkDevConfig, + drive_files: Arc>>, + ) -> Block { Self { blk_cfg, disk_image: None, @@ -968,6 +975,7 @@ impl Block { senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + drive_files, } } @@ -1011,11 +1019,10 @@ impl VirtioDevice for Block { let mut disk_image = None; let mut disk_size = DUMMY_IMG_SIZE; if !self.blk_cfg.path_on_host.is_empty() { - let mut file = open_file( - &self.blk_cfg.path_on_host, - self.blk_cfg.read_only, - self.blk_cfg.direct, - )?; + let drive_files = self.drive_files.lock().unwrap(); + // It's safe to unwrap as the path has been registered. + let drive_file = drive_files.get(&self.blk_cfg.path_on_host).unwrap(); + let mut file = drive_file.file.try_clone()?; disk_size = file.seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for block")? as u64; @@ -1238,6 +1245,7 @@ mod tests { use machine_manager::config::IothreadConfig; use std::sync::atomic::{AtomicU32, Ordering}; use std::{thread, time::Duration}; + use util::file::open_file; use vmm_sys_util::tempfile::TempFile; const QUEUE_NUM_BLK: usize = 1; @@ -1290,6 +1298,22 @@ mod tests { block.blk_cfg.direct = false; let f = TempFile::new().unwrap(); block.blk_cfg.path_on_host = f.as_path().to_str().unwrap().to_string(); + let drive_file = DriveFile { + file: open_file( + &block.blk_cfg.path_on_host, + block.blk_cfg.read_only, + block.blk_cfg.direct, + ) + .unwrap(), + path: block.blk_cfg.path_on_host.clone(), + read_only: block.blk_cfg.read_only, + locked: false, + }; + block + .drive_files + .lock() + .unwrap() + .insert(block.blk_cfg.path_on_host.clone(), drive_file); assert!(block.realize().is_ok()); assert_eq!(block.device_type(), VIRTIO_TYPE_BLOCK); diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 89fdad519..cdf515020 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::HashMap; use std::fs::File; use std::io::{Seek, SeekFrom}; use std::sync::{Arc, Mutex, Weak}; @@ -17,8 +18,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; use crate::ScsiBus::ScsiBus; -use machine_manager::config::ScsiDevConfig; -use util::file::open_file; +use machine_manager::config::{DriveFile, ScsiDevConfig}; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -92,6 +92,8 @@ pub struct ScsiDevice { pub scsi_type: u32, /// Scsi Bus attached to. pub parent_bus: Weak>, + /// Drive backend files. + drive_files: Arc>>, } impl Default for ScsiDevice { @@ -103,12 +105,17 @@ impl Default for ScsiDevice { disk_sectors: 0, scsi_type: SCSI_TYPE_DISK, parent_bus: Weak::new(), + drive_files: Arc::new(Mutex::new(HashMap::new())), } } } impl ScsiDevice { - pub fn new(config: ScsiDevConfig, scsi_type: u32) -> ScsiDevice { + pub fn new( + config: ScsiDevConfig, + scsi_type: u32, + drive_files: Arc>>, + ) -> ScsiDevice { ScsiDevice { config, state: ScsiDevState::new(), @@ -116,6 +123,7 @@ impl ScsiDevice { disk_sectors: 0, scsi_type, parent_bus: Weak::new(), + drive_files, } } @@ -137,11 +145,10 @@ impl ScsiDevice { if !self.config.path_on_host.is_empty() { self.disk_image = None; - let mut file = open_file( - &self.config.path_on_host, - self.config.read_only, - self.config.direct, - )?; + let drive_files = self.drive_files.lock().unwrap(); + // It's safe to unwrap as the path has been registered. + let drive_file = drive_files.get(&self.config.path_on_host).unwrap(); + let mut file = drive_file.file.try_clone()?; disk_size = file .seek(SeekFrom::End(0)) -- Gitee From 60fe184c6bfa86e260d464c288580424cfad4081 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 16 Nov 2022 20:00:07 +0800 Subject: [PATCH 0551/2187] pci: avoid unplug race during guest boot up The guest may clear events related slot status registers in pcie capability configuration field, if at the meanwhile we are doing the unplug work, it may fail. Take this senario into considration by igoring such clearing work. Signed-off-by: Zhang Bo --- pci/src/config.rs | 9 +++++++++ pci/src/root_port.rs | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 475c57851..0f72a5e1d 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -239,12 +239,21 @@ const PCI_EXP_SLTCTL_EIC: u16 = 0x0800; pub const PCI_EXP_SLTSTA: u16 = 26; /// Attention Button Pressed pub const PCI_EXP_SLTSTA_ABP: u16 = 0x0001; +/// Power Fault Detected +pub const PCI_EXP_SLTSTA_PFD: u16 = 0x0002; +/// MRL Sensor Changed +pub const PCI_EXP_SLTSTA_MRLSC: u16 = 0x0004; /// Presence Detect Changed pub const PCI_EXP_SLTSTA_PDC: u16 = 0x0008; /// Command Completed pub const PCI_EXP_SLTSTA_CC: u16 = 0x0010; /// Presence Detect State pub const PCI_EXP_SLTSTA_PDS: u16 = 0x0040; +pub const PCI_EXP_SLOTSTA_EVENTS: u16 = PCI_EXP_SLTSTA_ABP + | PCI_EXP_SLTSTA_PFD + | PCI_EXP_SLTSTA_MRLSC + | PCI_EXP_SLTSTA_PDC + | PCI_EXP_SLTSTA_CC; // System error on correctable error enable. const PCI_EXP_RTCTL_SECEE: u16 = 0x01; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 2d219e816..fa22c879d 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -29,8 +29,8 @@ use super::config::{ PciConfig, PcieDevType, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, - PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, + PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, PCI_EXP_SLOTSTA_EVENTS, + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, SUB_CLASS_CODE, VENDOR_ID, @@ -219,7 +219,37 @@ impl RootPort { } } - fn do_unplug(&mut self, offset: usize, end: usize, old_ctl: u16) { + fn correct_race_unplug(&mut self, offset: usize, data: &[u8], old_status: u16) { + let end = offset + data.len(); + let cap_offset = self.config.pci_express_cap_offset; + if !ranges_overlap( + offset, + end, + (cap_offset + PCI_EXP_SLTSTA) as usize, + (cap_offset + PCI_EXP_SLTSTA + 2) as usize, + ) { + return; + } + + let status = + le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let val: u16 = data[0] as u16 + ((data[1] as u16) << 8); + if (val & !old_status & PCI_EXP_SLOTSTA_EVENTS) != 0 { + let tmpstat = + (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); + le_write_u16( + &mut self.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + tmpstat, + ) + .unwrap(); + } + } + + fn do_unplug(&mut self, offset: usize, data: &[u8], old_ctl: u16, old_status: u16) { + self.correct_race_unplug(offset, data, old_status); + + let end = offset + data.len(); let cap_offset = self.config.pci_express_cap_offset; // Only care the write config about slot control if !ranges_overlap( @@ -366,6 +396,8 @@ impl PciDevOps for RootPort { let cap_offset = self.config.pci_express_cap_offset; let old_ctl = le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize).unwrap(); + let old_status = + le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); self.config.write( offset, @@ -387,7 +419,7 @@ impl PciDevOps for RootPort { self.register_region(); } - self.do_unplug(offset, end, old_ctl); + self.do_unplug(offset, data, old_ctl, old_status); } fn name(&self) -> String { -- Gitee From 3bcd6b19655fbe9a95b2b1d393d2a49a02cc64fa Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 3 Dec 2022 15:50:35 +0800 Subject: [PATCH 0552/2187] VNC: Add two variables to the VncState struct. Client_width and client_height indicate the length and width of the display in VncClient. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 45 +++++++++++++++++++++++++++------------------ vnc/src/vnc.rs | 2 +- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 9abba6846..b87f0e9f7 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -154,6 +154,10 @@ impl Rectangle { /// Display Output mode information of client. #[derive(Clone)] pub struct DisplayMode { + /// Width of client display. + pub client_width: i32, + /// Height of client display. + pub client_height: i32, /// Encoding type. pub enc: i32, /// Data storage type for client. @@ -167,6 +171,8 @@ pub struct DisplayMode { impl DisplayMode { pub fn new(enc: i32, client_be: bool, convert: bool, pf: PixelFormat) -> Self { DisplayMode { + client_width: 0, + client_height: 0, enc, client_be, convert, @@ -552,19 +558,24 @@ impl ClientIoHandler { pub fn handle_client_init(&mut self) -> Result<()> { let mut buf = Vec::new(); // Send server framebuffer info. + let client = self.client.clone(); let locked_surface = self.server.vnc_surface.lock().unwrap(); + let mut locked_dpm = client.client_dpm.lock().unwrap(); let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); - drop(locked_surface); if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) { error!("Invalid Image Size!"); return Err(anyhow!(VncError::InvalidImageSize)); } + locked_dpm.client_width = width; + locked_dpm.client_height = height; + drop(locked_dpm); + drop(locked_surface); + buf.append(&mut (width as u16).to_be_bytes().to_vec()); buf.append(&mut (height as u16).to_be_bytes().to_vec()); - let client = self.client.clone(); pixel_format_message(&client, &mut buf); buf.append(&mut (APP_NAME.to_string().len() as u32).to_be_bytes().to_vec()); @@ -1173,8 +1184,9 @@ pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { /// Set Desktop Size. pub fn desktop_resize(client: &Arc, server: &Arc, buf: &mut Vec) { - let locked_state = client.conn_state.lock().unwrap(); let locked_surface = server.vnc_surface.lock().unwrap(); + let locked_state = client.conn_state.lock().unwrap(); + let mut locked_dpm = client.client_dpm.lock().unwrap(); if !locked_state.has_feature(VncFeatures::VncFeatureResizeExt) && !locked_state.has_feature(VncFeatures::VncFeatureResize) { @@ -1182,9 +1194,6 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & } let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); - drop(locked_state); - drop(locked_surface); - if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) { @@ -1192,6 +1201,15 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & return; } + if locked_dpm.client_width == width && locked_dpm.client_height == height { + return; + } + locked_dpm.client_width = width; + locked_dpm.client_height = height; + drop(locked_dpm); + drop(locked_state); + drop(locked_surface); + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut (1_u16).to_be_bytes().to_vec()); @@ -1199,20 +1217,11 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & } /// Set color depth for client. -pub fn set_color_depth(client: &Arc, server: &Arc, buf: &mut Vec) { +pub fn set_color_depth(client: &Arc, buf: &mut Vec) { let locked_state = client.conn_state.lock().unwrap(); if locked_state.has_feature(VncFeatures::VncFeatureWmvi) { - let locked_surface = server.vnc_surface.lock().unwrap(); - let width = get_image_width(locked_surface.server_image); - let height = get_image_height(locked_surface.server_image); - drop(locked_surface); - - if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) - || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) - { - error!("Invalid Image Size!"); - return; - } + let width = client.client_dpm.lock().unwrap().client_width; + let height = client.client_dpm.lock().unwrap().client_height; buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Padding. buf.append(&mut (1_u16).to_be_bytes().to_vec()); // Number of pixel block. diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 924b8da86..fb4c14d9e 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -556,7 +556,7 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { let height = vnc_height(guest_height); let mut buf: Vec = Vec::new(); // Set Color depth. - set_color_depth(&client, &server, &mut buf); + set_color_depth(&client, &mut buf); // Desktop_resize. desktop_resize(&client, &server, &mut buf); // Cursor define. -- Gitee From 81090dae3cb82d0af85ac18bb79ab6926122e8ad Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 29 Nov 2022 21:52:53 +0800 Subject: [PATCH 0553/2187] VNC: Add function set_range. In the process of marking dirty for pixels, the efficiency of marking the contrast bits dirty one by one is relatively low. So change to mark dirty within a range. Signed-off-by: Xiao Ye --- util/src/bitmap.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++ vnc/src/vnc.rs | 8 +++-- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 42df5f4b1..cd22192ac 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -62,6 +62,62 @@ impl Bitmap { Ok(()) } + /// Set the range of bitmap. + /// + /// # Arguments + /// + /// * `start` - the begin bit. + /// * `len` - the end bit. + /// + /// # Example + /// + /// ```rust + /// use util::bitmap::Bitmap; + /// let mut bitmap = Bitmap::::new(4); + /// assert!(bitmap.set_range(65, 10).is_ok()); + /// assert_eq!(bitmap.contain(64).unwrap(), false); + /// assert_eq!(bitmap.contain(65).unwrap(), true); + /// assert_eq!(bitmap.contain(70).unwrap(), true); + /// assert_eq!(bitmap.contain(74).unwrap(), true); + /// assert_eq!(bitmap.contain(75).unwrap(), false); + /// ``` + pub fn set_range(&mut self, start: usize, len: usize) -> Result<()> { + if len == 0 { + return Ok(()); + } + + let mut index = self.bit_index(start); + let mut bits_to_set: usize = T::len() - self.bit_pos(start); + let mut mask_to_set: T = T::full().rhs(self.bit_pos(start)); + let mut length: usize = len; + while length >= bits_to_set { + if index >= self.size() { + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); + } + length -= bits_to_set; + self.data[index] = T::bit_or(self.data[index], mask_to_set); + bits_to_set = T::len(); + mask_to_set = T::full(); + index += 1; + } + if length > 0 { + if index >= self.size() { + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); + } + bits_to_set = T::len() - self.bit_pos(start + len); + let mask_to_set_end: T = T::full().lhs(self.bit_pos(bits_to_set)); + mask_to_set = T::bit_and(mask_to_set, mask_to_set_end); + self.data[index] = T::bit_or(self.data[index], mask_to_set); + } + Ok(()) + } + /// Clear the bit of bitmap. /// /// # Arguments @@ -295,4 +351,35 @@ mod tests { assert_eq!(bitmap.count_front_bits(16).unwrap(), 1); assert_eq!(bitmap.count_front_bits(15).unwrap(), 0); } + + #[test] + fn test_bitmap_set_range() { + let mut bitmap = Bitmap::::new(4); + assert!(bitmap.set_range(256, 1).is_err()); + assert!(bitmap.set_range(0, 257).is_err()); + assert!(bitmap.set_range(0, 256).is_ok()); + bitmap.clear_all(); + + assert!(bitmap.set_range(65, 10).is_ok()); + assert_eq!(bitmap.contain(64).unwrap(), false); + assert_eq!(bitmap.contain(65).unwrap(), true); + assert_eq!(bitmap.contain(70).unwrap(), true); + assert_eq!(bitmap.contain(74).unwrap(), true); + assert_eq!(bitmap.contain(75).unwrap(), false); + bitmap.clear_all(); + + assert!(bitmap.set_range(63, 1).is_ok()); + assert_eq!(bitmap.contain(62).unwrap(), false); + assert_eq!(bitmap.contain(63).unwrap(), true); + assert_eq!(bitmap.contain(64).unwrap(), false); + bitmap.clear_all(); + + assert!(bitmap.set_range(63, 66).is_ok()); + assert_eq!(bitmap.contain(62).unwrap(), false); + assert_eq!(bitmap.contain(63).unwrap(), true); + assert_eq!(bitmap.contain(67).unwrap(), true); + assert_eq!(bitmap.contain(128).unwrap(), true); + assert_eq!(bitmap.contain(129).unwrap(), false); + bitmap.clear_all(); + } } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 924b8da86..fe0fe7ad5 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -214,9 +214,11 @@ pub fn set_area_dirty( w = cmp::min(x + w, width) - x; h = cmp::min(y + h, height); while y < h { - let pos = y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32; - for i in 0..round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as i32 { - dirty.set((pos + i) as usize).unwrap(); + let pos = (y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32) as usize; + let len = round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as usize; + if let Err(e) = dirty.set_range(pos, len) { + error!("set bitmap error: {:?}", e); + return; } y += 1; } -- Gitee From 2dc7c6b9ac5424872b3b272e35571cfdca2c5a64 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 30 Nov 2022 14:38:13 +0800 Subject: [PATCH 0554/2187] VNC: Add function clear_range for bitmap. In the process of cleaning dirty bitmap for pixels, the efficiency of clearing the contrast bits one by one is relatively low, so change to clear dirty within a range. Signed-off-by: Xiao Ye Bitmap { Ok(()) } + /// Clear the range of bitmap. + /// + /// # Arguments + /// + /// * `start` - the begin bit. + /// * `len` - the end bit. + /// + /// # Example + /// + /// ```rust + /// use util::bitmap::Bitmap; + /// let mut bitmap = Bitmap::::new(4); + /// assert!(bitmap.set_range(0, 256).is_ok()); + /// assert!(bitmap.clear_range(65, 10).is_ok()); + /// + /// assert_eq!(bitmap.contain(64).unwrap(), true); + /// assert_eq!(bitmap.contain(65).unwrap(), false); + /// assert_eq!(bitmap.contain(70).unwrap(), false); + /// assert_eq!(bitmap.contain(74).unwrap(), false); + /// assert_eq!(bitmap.contain(75).unwrap(), true); + /// ``` + pub fn clear_range(&mut self, start: usize, len: usize) -> Result<()> { + if len == 0 { + return Ok(()); + } + + let mut index = self.bit_index(start); + let mut bits_to_clear: usize = T::len() - self.bit_pos(start); + let mut mask_to_clear: T = T::bit_not(T::full().rhs(self.bit_pos(start))); + let mut length: usize = len; + while length >= bits_to_clear { + if index >= self.size() { + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); + } + length -= bits_to_clear; + self.data[index] = T::bit_and(self.data[index], mask_to_clear); + bits_to_clear = T::len(); + mask_to_clear = T::zero(); + index += 1; + } + if length > 0 { + if index >= self.size() { + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); + } + bits_to_clear = T::len() - self.bit_pos(start + len); + let mask_to_clear_end: T = T::bit_not(T::full().lhs(self.bit_pos(bits_to_clear))); + mask_to_clear = T::bit_or(mask_to_clear, mask_to_clear_end); + self.data[index] = T::bit_and(self.data[index], mask_to_clear); + } + Ok(()) + } + /// Query bitmap if contains input number or not. /// /// # Arguments @@ -382,4 +440,37 @@ mod tests { assert_eq!(bitmap.contain(129).unwrap(), false); bitmap.clear_all(); } + + #[test] + fn test_bitmap_clear_range() { + let mut bitmap = Bitmap::::new(4); + assert!(bitmap.set_range(0, 256).is_ok()); + assert!(bitmap.clear_range(256, 1).is_err()); + assert!(bitmap.clear_range(0, 0).is_ok()); + assert!(bitmap.clear_range(0, 257).is_err()); + + assert!(bitmap.set_range(0, 256).is_ok()); + assert!(bitmap.clear_range(65, 10).is_ok()); + assert_eq!(bitmap.contain(64).unwrap(), true); + assert_eq!(bitmap.contain(65).unwrap(), false); + assert_eq!(bitmap.contain(70).unwrap(), false); + assert_eq!(bitmap.contain(74).unwrap(), false); + assert_eq!(bitmap.contain(75).unwrap(), true); + + assert!(bitmap.set_range(0, 256).is_ok()); + assert!(bitmap.clear_range(63, 1).is_ok()); + assert_eq!(bitmap.contain(62).unwrap(), true); + assert_eq!(bitmap.contain(63).unwrap(), false); + assert_eq!(bitmap.contain(64).unwrap(), true); + + assert!(bitmap.set_range(0, 256).is_ok()); + assert!(bitmap.clear_range(63, 66).is_ok()); + assert_eq!(bitmap.contain(62).unwrap(), true); + assert_eq!(bitmap.contain(63).unwrap(), false); + assert_eq!(bitmap.contain(67).unwrap(), false); + assert_eq!(bitmap.contain(128).unwrap(), false); + assert_eq!(bitmap.contain(129).unwrap(), true); + + assert!(bitmap.clear_range(0, 256).is_ok()); + } } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 9abba6846..d324c0243 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -1077,8 +1077,11 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: if !locked_dirty.contain((i * bpl as u64 + x) as usize).unwrap() { break; } - for j in x..x2 { - locked_dirty.clear((i * bpl as u64 + j) as usize).unwrap(); + let start = (i * bpl as u64 + x) as usize; + let len = (x2 - x) as usize; + if let Err(e) = locked_dirty.clear_range(start, len) { + error!("clear bitmap error: {:?}", e); + return num_rects; } i += 1; } -- Gitee From b2594dc460211e305cd9fdf917718fb1da542a4b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 3 Dec 2022 15:22:06 +0800 Subject: [PATCH 0555/2187] VNC: Add testcase in bitmap operation. Add testcase for find_next_zero and find_next_bit. Signed-off-by: Xiao Ye ::new(4); + assert!(bitmap.set_range(0, 256).is_ok()); + assert!(bitmap.clear(0).is_ok()); + assert!(bitmap.clear(32).is_ok()); + assert!(bitmap.clear(64).is_ok()); + assert!(bitmap.clear(128).is_ok()); + + let mut offset = 0; + offset = bitmap.find_next_zero(offset).unwrap(); + assert_eq!(offset, 0); + offset = bitmap.find_next_zero(offset + 1).unwrap(); + assert_eq!(offset, 32); + offset = bitmap.find_next_zero(offset + 1).unwrap(); + assert_eq!(offset, 64); + offset = bitmap.find_next_zero(offset + 1).unwrap(); + assert_eq!(offset, 128); + offset = bitmap.find_next_zero(offset + 1).unwrap(); + assert_eq!(offset, 256); + } + + #[test] + fn test_bitmap_find_next_bit() { + let mut bitmap = Bitmap::::new(4); + bitmap.clear_all(); + assert!(bitmap.set(0).is_ok()); + assert!(bitmap.set(32).is_ok()); + assert!(bitmap.set(64).is_ok()); + assert!(bitmap.set(128).is_ok()); + + let mut offset = 0; + offset = bitmap.find_next_bit(offset).unwrap(); + assert_eq!(offset, 0); + offset = bitmap.find_next_bit(offset + 1).unwrap(); + assert_eq!(offset, 32); + offset = bitmap.find_next_bit(offset + 1).unwrap(); + assert_eq!(offset, 64); + offset = bitmap.find_next_bit(offset + 1).unwrap(); + assert_eq!(offset, 128); + offset = bitmap.find_next_bit(offset + 1).unwrap(); + assert_eq!(offset, 256); + } } -- Gitee From 47d2051f5d1eca2537c617bf479006a4123a80d1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 3 Dec 2022 16:54:16 +0800 Subject: [PATCH 0556/2187] VNC: Set the limit size of the output buffer. Set the limit size of the output buffer to prevent the client from stopping receiving data. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 67 ++++++++++++---- vnc/src/utils.rs | 189 ++++++++++++++++++++++++++++++++++---------- vnc/src/vencrypt.rs | 2 +- vnc/src/vnc.rs | 16 ++-- 4 files changed, 214 insertions(+), 60 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index b87f0e9f7..f57b33206 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -18,7 +18,8 @@ use crate::{ utils::BuffPool, vnc::{ framebuffer_upadate, set_area_dirty, write_pixel, DisplayMouse, BIT_PER_BYTE, - DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_RECT_INFO, + DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, MIN_OUTPUT_LIMIT, + OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, }, VncError, }; @@ -349,14 +350,21 @@ impl ClientIoHandler { fn client_handle_write(&mut self) { let client = self.client.clone(); + if client.conn_state.lock().unwrap().dis_conn { + return; + } let mut locked_buffer = client.out_buffer.lock().unwrap(); - let len = locked_buffer.len(); - let buf = locked_buffer.read_front(len); - self.write_msg(buf); - locked_buffer.remov_front(len); + while let Some(bytes) = locked_buffer.read_front_chunk() { + self.write_msg(bytes); + locked_buffer.remove_front_chunk(); + } drop(locked_buffer); } + pub fn flush(&mut self) { + self.client_handle_write(); + } + /// Read buf from stream, return the size. fn read_msg(&mut self) -> Result { let mut buf = Vec::new(); @@ -378,11 +386,10 @@ impl ClientIoHandler { Err(e) => return Err(e), } } - self.client - .in_buffer - .lock() - .unwrap() - .read(&mut buf[..len].to_vec()); + if len > 0 { + buf = buf[..len].to_vec(); + self.client.in_buffer.lock().unwrap().append_limit(buf); + } Ok(len) } @@ -518,7 +525,8 @@ impl ClientIoHandler { if version.major != 3 || ![3, 4, 5, 7, 8].contains(&version.minor) { let mut buf = Vec::new(); buf.append(&mut (AuthState::Invalid as u32).to_be_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } @@ -837,7 +845,7 @@ impl ClientIoHandler { desktop_resize(&client, &server, &mut buf); // VNC display cursor define. display_cursor_define(&client, &server, &mut buf); - client.out_buffer.lock().unwrap().read(&mut buf); + vnc_write(&client, buf); vnc_flush_notify(&client); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) @@ -898,8 +906,9 @@ impl ClientIoHandler { /// Read the data from the receiver buffer. pub fn read_incoming_msg(&mut self) -> Vec { + let mut buf: Vec = vec![0_u8; self.expect]; let mut locked_in_buffer = self.client.in_buffer.lock().unwrap(); - let buf = locked_in_buffer.read_front(self.expect).to_vec(); + let _size: usize = locked_in_buffer.read_front(&mut buf, self.expect); buf } @@ -918,7 +927,7 @@ impl ClientIoHandler { .in_buffer .lock() .unwrap() - .remov_front(self.expect); + .remove_front(self.expect); self.expect = expect; self.msg_handler = msg_handler; } @@ -1319,6 +1328,36 @@ pub fn display_cursor_define( } } +pub fn vnc_write(client: &Arc, buf: Vec) { + if client.conn_state.lock().unwrap().dis_conn { + return; + } + let mut locked_buffer = client.out_buffer.lock().unwrap(); + if !locked_buffer.is_enough(buf.len()) { + client.conn_state.lock().unwrap().dis_conn = true; + return; + } + locked_buffer.append_limit(buf); +} + +/// Set the limit size of the output buffer to prevent the client +/// from stopping receiving data. +pub fn vnc_update_output_throttle(client: &Arc) { + let locked_dpm = client.client_dpm.lock().unwrap(); + let width = locked_dpm.client_width; + let height = locked_dpm.client_height; + let bytes_per_pixel = locked_dpm.pf.pixel_bytes; + let mut offset = width * height * (bytes_per_pixel as i32) * OUTPUT_THROTTLE_SCALE; + drop(locked_dpm); + + offset = cmp::max(offset, MIN_OUTPUT_LIMIT); + client + .out_buffer + .lock() + .unwrap() + .set_limit(Some(offset as usize)); +} + /// Consume the output buffer. pub fn vnc_flush_notify(client: &Arc) { client.write_fd.lock().unwrap().write(1).unwrap(); diff --git a/vnc/src/utils.rs b/vnc/src/utils.rs index b99763dc4..f85f59249 100644 --- a/vnc/src/utils.rs +++ b/vnc/src/utils.rs @@ -10,16 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::cmp; +use std::collections::LinkedList; +use std::io::Read; -/// Simple bufferpool can improve read performance of tcpstream. +/// Linked the bytes buffer by linklist, to avoid the +/// extra copies when appending a new bytes buffer. pub struct BuffPool { /// Cache received data. - buf: Vec, - /// Start Byte. - pos: usize, - /// Number of bytes in buff. - cap: usize, + buf_list: LinkedList>, + /// Limit size of the buffpool. + limit: Option, + /// Total length of Buffer. + len: usize, } impl Default for BuffPool { @@ -28,57 +30,164 @@ impl Default for BuffPool { } } -/// The buffpool to improve read performance. impl BuffPool { pub fn new() -> Self { - BuffPool { - buf: Vec::new(), - pos: 0, - cap: 0, + Self { + buf_list: LinkedList::new(), + limit: None, + len: 0, } } - /// Read from the buff. - pub fn read(&mut self, buf: &mut Vec) { - self.buf.drain(..self.pos); - self.buf.append(buf); - self.pos = 0; - self.cap = self.buf.len(); + /// Update the length of bufflist. + fn update_len(&mut self) { + let mut len: usize = 0; + for bytes in &self.buf_list { + len += bytes.len(); + } + self.len = len; } - /// Return the len of the buffpool. - pub fn len(&mut self) -> usize { - self.cap + /// Return the len of the pool. + pub fn len(&self) -> usize { + self.len } - /// Is empty. - pub fn is_empty(&mut self) -> bool { - self.cap != 0 + /// If it is empty. + pub fn is_empty(&self) -> bool { + self.buf_list.is_empty() } - /// Read from front. - pub fn read_front(&mut self, len: usize) -> &[u8] { - let length = cmp::min(self.cap, len); - &self.buf[self.pos..self.pos + length] + /// For a given length of buffer data, whether there is + /// enough space left to store. + pub fn is_enough(&self, require: usize) -> bool { + if let Some(limit) = self.limit { + if self.len() + require > limit { + return false; + } + } + true } - /// Remove front. - pub fn remov_front(&mut self, len: usize) { - self.pos = cmp::min(self.pos + len, self.buf.len()); - self.cap = cmp::max(0_usize, self.cap - len); + /// Set the limitation for bufferpool. + /// + /// # Example + /// ```rust + /// let mut buffpool = BuffPool::new(); + /// buffpool.set_limit(Some(1)); + /// assert!(!buffpool.is_enough(2)); + /// ``` + pub fn set_limit(&mut self, limit: Option) { + self.limit = limit; + } + + /// Add data to the bufferpool. If the remaining + /// free space is not enough, it will not work. So it is + /// recommended to call is_enouth() before this function. + /// + /// # Example + /// ```rust + /// let mut buffpool = BuffPool::new(); + /// buffpool.append_limit((0_u8).to_be_bytes().to_vec()); + /// ``` + pub fn append_limit(&mut self, buf: Vec) { + let len = buf.len(); + if len == 0 { + return; + } + if self.is_enough(len) { + self.buf_list.push_back(buf); + } + self.update_len(); + } + + /// Read the first n bytes. + /// + /// # Example + /// ```rust + /// let mut buffpool = BuffPool::new(); + /// buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); + /// let mut buf: Vec = vec![0_u8; 4]; + /// buffpool.read_front(&mut buf, 4); + /// assert_eq!(buf, vec![18, 52, 86, 120]); + /// ``` + pub fn read_front(&mut self, buf: &mut [u8], len: usize) -> usize { + if buf.len() < len { + return 0_usize; + } + + let mut offset: usize = 0; + for bytes in &self.buf_list { + if let Ok(n) = bytes.as_slice().read(&mut buf[offset..]) { + offset += n; + } else { + return 0_usize; + } + if offset >= len { + break; + } + } + offset + } + + /// Remove the first n bytes. + /// + /// # Example + /// ```rust + /// let mut buffpool = BuffPool::new(); + /// buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); + /// buffpool.remove_front(1); + /// let mut buf: Vec = vec![0_u8; 3]; + /// buffpool.read_front(&mut buf, 3); + /// assert_eq!(buf, vec![52, 86, 120]); + /// ``` + pub fn remove_front(&mut self, mut len: usize) { + while let Some(mut bytes) = self.buf_list.pop_front() { + if len < bytes.len() { + self.buf_list.push_front(bytes.split_off(len)); + break; + } else { + len -= bytes.len(); + } + } + self.update_len(); + } + + /// Read first chunk of vec in linklist. + pub fn read_front_chunk(&mut self) -> Option<&Vec> { + self.buf_list.front() } -} -#[cfg(test)] + /// Remove first front chunk of vec in linklist. + pub fn remove_front_chunk(&mut self) { + if !self.is_empty() { + self.buf_list.pop_front(); + } + self.update_len(); + } +} mod tests { - use super::*; #[test] - fn test_buff_pool() { + fn test_buffpool_base() { let mut buffpool = BuffPool::new(); - buffpool.read(&mut (0x12345678 as u32).to_be_bytes().to_vec()); - assert!(buffpool.len() == 4 as usize); - buffpool.remov_front(1); - assert!(buffpool.read_front(3) == vec![52, 86, 120]); + buffpool.set_limit(Some(7)); + buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); + buffpool.append_limit((0x12 as u8).to_be_bytes().to_vec()); + buffpool.append_limit((0x1234 as u16).to_be_bytes().to_vec()); + assert!(buffpool.len() == 7 as usize); + buffpool.remove_front(1); + assert!(buffpool.len() == 6 as usize); + let mut buf: Vec = vec![0_u8; 4]; + buffpool.read_front(&mut buf, 4); + assert!(buf == vec![52, 86, 120, 18]); + + let ans: Vec> = vec![vec![52, 86, 120], vec![18], vec![18, 52]]; + let mut idx: usize = 0; + while let Some(buf) = buffpool.read_front_chunk() { + assert_eq!(ans[idx], buf.to_vec()); + idx += 1; + buffpool.remove_front_chunk(); + } } } diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 2d0070302..85be2504a 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -136,7 +136,7 @@ impl ClientIoHandler { .in_buffer .lock() .unwrap() - .remov_front(self.expect); + .remove_front(self.expect); self.expect = 0; self.msg_handler = ClientIoHandler::tls_handshake; Ok(()) diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index fb4c14d9e..169efde27 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -12,8 +12,9 @@ use crate::{ client::{ - desktop_resize, display_cursor_define, set_color_depth, vnc_flush_notify, DisplayMode, - RectInfo, Rectangle, ServerMsg, ENCODING_HEXTILE, ENCODING_RAW, + desktop_resize, display_cursor_define, set_color_depth, vnc_flush_notify, + vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, + ENCODING_HEXTILE, ENCODING_RAW, }, encoding::enc_hextile::hextile_send_framebuffer_update, pixman::{ @@ -56,6 +57,10 @@ pub const DIRTY_WIDTH_BITS: u16 = MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM; pub const VNC_BITMAP_WIDTH: u64 = round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) * u64::BITS as u64; +/// Output throttle scale. +pub const OUTPUT_THROTTLE_SCALE: i32 = 5; +/// Min size of output buffer. +pub const MIN_OUTPUT_LIMIT: i32 = 1024 * 1024 * OUTPUT_THROTTLE_SCALE; const DEFAULT_REFRESH_INTERVAL: u64 = 30; pub const BIT_PER_BYTE: u32 = 8; const MILLI_PER_SEC: u64 = 1_000_000; @@ -181,7 +186,7 @@ fn start_vnc_thread() -> Result<()> { drop(locked_surface); let client = rect_info.client; - client.out_buffer.lock().unwrap().read(&mut buf); + vnc_write(&client, buf); vnc_flush_notify(&client); }) .unwrap(); @@ -561,7 +566,7 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { desktop_resize(&client, &server, &mut buf); // Cursor define. display_cursor_define(&client, &server, &mut buf); - client.out_buffer.lock().unwrap().read(&mut buf); + vnc_write(&client, buf); vnc_flush_notify(&client); client.dirty_bitmap.lock().unwrap().clear_all(); set_area_dirty( @@ -573,6 +578,7 @@ pub fn vnc_display_switch(surface: &DisplaySurface) { guest_width, guest_height, ); + vnc_update_output_throttle(&client); } vnc_refresh_notify(&server); } @@ -620,7 +626,7 @@ pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { let client = client_io.lock().unwrap().client.clone(); let mut buf: Vec = Vec::new(); display_cursor_define(&client, &server, &mut buf); - client.out_buffer.lock().unwrap().read(&mut buf); + vnc_write(&client, buf); vnc_flush_notify(&client); } } -- Gitee From 4a318c4c12fea643965fbc04e41762a8b0752516 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 4 Dec 2022 12:09:41 +0800 Subject: [PATCH 0557/2187] VNC: Adjust the data sending way. Put the data to the output buffer before sending. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 25 +++++++++++++++++-------- vnc/src/utils.rs | 10 ++++++++++ vnc/src/vencrypt.rs | 27 ++++++++++++++++++++------- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index f57b33206..f394174e2 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -509,6 +509,7 @@ impl ClientIoHandler { /// Exchange RFB protocol version with client. fn handle_version(&mut self) -> Result<()> { + let client = self.client.clone(); let buf = self.read_incoming_msg(); let res = String::from_utf8_lossy(&buf); let ver_str = &res[0..12].to_string(); @@ -542,7 +543,7 @@ impl ClientIoHandler { AuthState::No => { let mut buf = Vec::new(); buf.append(&mut (AuthState::No as u32).to_be_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); self.update_event_handler(1, ClientIoHandler::handle_client_init); } _ => { @@ -556,9 +557,10 @@ impl ClientIoHandler { let mut buf = [0u8; 2]; buf[0] = 1; // Number of security types. buf[1] = auth as u8; - self.write_msg(&buf); + vnc_write(&client, buf.to_vec()); self.update_event_handler(1, ClientIoHandler::handle_auth); } + self.flush(); Ok(()) } @@ -588,7 +590,8 @@ impl ClientIoHandler { buf.append(&mut (APP_NAME.to_string().len() as u32).to_be_bytes().to_vec()); buf.append(&mut APP_NAME.to_string().as_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } @@ -597,7 +600,8 @@ impl ClientIoHandler { fn handle_auth(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); let auth = self.server.security_type.lock().unwrap().auth; - let version = self.client.conn_state.lock().unwrap().version.clone(); + let client = self.client.clone(); + let version = client.conn_state.lock().unwrap().version.clone(); if buf[0] != auth as u8 { self.auth_failed("Authentication failed"); @@ -609,7 +613,7 @@ impl ClientIoHandler { AuthState::No => { if version.minor >= 8 { let buf = [0u8; 4]; - self.write_msg(&buf); + vnc_write(&client, buf.to_vec()); } self.update_event_handler(1, ClientIoHandler::handle_client_init); } @@ -619,7 +623,7 @@ impl ClientIoHandler { buf[0] = 0_u8; buf[1] = 2_u8; - self.write_msg(&buf); + vnc_write(&client, buf.to_vec()); self.update_event_handler(2, ClientIoHandler::client_vencrypt_init); } _ => { @@ -628,6 +632,7 @@ impl ClientIoHandler { return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); } } + self.flush(); Ok(()) } @@ -686,7 +691,9 @@ impl ClientIoHandler { buf.append(&mut (b as u16).to_be_bytes().to_vec()); } - self.write_msg(&buf); + let client = self.client.clone(); + vnc_write(&client, buf); + self.flush(); } /// Set image format. @@ -901,7 +908,9 @@ impl ClientIoHandler { buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); buf.append(&mut err_msg.as_bytes().to_vec()); } - self.write_msg(&buf); + let client = self.client.clone(); + vnc_write(&client, buf); + self.flush(); } /// Read the data from the receiver buffer. diff --git a/vnc/src/utils.rs b/vnc/src/utils.rs index f85f59249..7ca3b251f 100644 --- a/vnc/src/utils.rs +++ b/vnc/src/utils.rs @@ -73,6 +73,8 @@ impl BuffPool { /// /// # Example /// ```rust + /// use vnc::utils::BuffPool; + /// /// let mut buffpool = BuffPool::new(); /// buffpool.set_limit(Some(1)); /// assert!(!buffpool.is_enough(2)); @@ -87,6 +89,8 @@ impl BuffPool { /// /// # Example /// ```rust + /// use vnc::utils::BuffPool; + /// /// let mut buffpool = BuffPool::new(); /// buffpool.append_limit((0_u8).to_be_bytes().to_vec()); /// ``` @@ -105,6 +109,8 @@ impl BuffPool { /// /// # Example /// ```rust + /// use vnc::utils::BuffPool; + /// /// let mut buffpool = BuffPool::new(); /// buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); /// let mut buf: Vec = vec![0_u8; 4]; @@ -134,6 +140,8 @@ impl BuffPool { /// /// # Example /// ```rust + /// use vnc::utils::BuffPool; + /// /// let mut buffpool = BuffPool::new(); /// buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); /// buffpool.remove_front(1); @@ -166,7 +174,9 @@ impl BuffPool { self.update_len(); } } +#[cfg(test)] mod tests { + use crate::utils::BuffPool; #[test] fn test_buffpool_base() { diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 85be2504a..2e605a8d8 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -10,7 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{auth::SubAuthState, client::ClientIoHandler, VncError}; +use crate::{ + auth::SubAuthState, + client::{vnc_write, ClientIoHandler}, + VncError, +}; use anyhow::{anyhow, Result}; use log::{error, info}; use rustls::{ @@ -67,13 +71,15 @@ impl ClientIoHandler { /// Exchange auth version with client pub fn client_vencrypt_init(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); + let client = self.client.clone(); let subauth = self.server.security_type.lock().unwrap().subauth; // VeNCrypt version 0.2. if buf[0] != 0 || buf[1] != 2 { let mut buf = Vec::new(); // Reject version. buf.append(&mut (0_u8).to_be_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } else { let mut buf = Vec::new(); @@ -83,9 +89,10 @@ impl ClientIoHandler { buf.append(&mut (1_u8).to_be_bytes().to_vec()); // The supported auth. buf.append(&mut (subauth as u32).to_be_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); } + self.flush(); self.update_event_handler(4, ClientIoHandler::client_vencrypt_auth); Ok(()) } @@ -95,13 +102,15 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); let buf = [buf[0], buf[1], buf[2], buf[3]]; let auth = u32::from_be_bytes(buf); + let client = self.client.clone(); let subauth = self.server.security_type.lock().unwrap().subauth; if auth != subauth as u32 { let mut buf = Vec::new(); // Reject auth. buf.append(&mut (0_u8).to_be_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); error!("Authentication failed"); return Err(anyhow!(VncError::AuthFailed(String::from( "Authentication failed" @@ -111,7 +120,8 @@ impl ClientIoHandler { let mut buf = Vec::new(); // Accept auth. buf.append(&mut (1_u8).to_be_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); if let Some(tls_config) = self.server.security_type.lock().unwrap().tls_config.clone() { match rustls::ServerConnection::new(tls_config) { @@ -191,6 +201,7 @@ impl ClientIoHandler { fn handle_vencrypt_subauth(&mut self) -> Result<()> { let subauth = self.server.security_type.lock().unwrap().subauth; + let client = self.client.clone(); match subauth { SubAuthState::VncAuthVencryptX509Sasl => { self.expect = 4; @@ -199,7 +210,8 @@ impl ClientIoHandler { } SubAuthState::VncAuthVencryptX509None => { let buf = [0u8; 4]; - self.write_msg(&buf); + vnc_write(&client, buf.to_vec()); + self.flush(); self.expect = 1; self.msg_handler = ClientIoHandler::handle_client_init; } @@ -211,7 +223,8 @@ impl ClientIoHandler { let err_msg: String = "Unsupported subauth type".to_string(); buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); buf.append(&mut err_msg.as_bytes().to_vec()); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); } error!("Unsupported subauth type"); return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( -- Gitee From b35d5ba0bfffcbfb918d6b47b1fda00da9263e10 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Dec 2022 22:59:57 +0800 Subject: [PATCH 0558/2187] drive: Fixes for drive backend files lock 1. Keep a count for shared file to fix a bug that when drives share a same file path, the drive delete qmp can only execute once, as the following drive delete qmp for that path will cause panic. 2. For micro VM, the Block should be init with VM's drive_files. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 14 +++++------- machine/src/micro_vm/mod.rs | 6 ++++- machine/src/standard_vm/mod.rs | 2 ++ machine_manager/src/config/drive.rs | 2 ++ machine_manager/src/config/mod.rs | 23 ++++++++++++++++++- virtio/src/block.rs | 34 ++++++++++++++--------------- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3d25d94ed..d1f37cc46 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1322,9 +1322,9 @@ pub trait MachineOps { // Lock the added file if VM is running. let drive_file = drive_files.get_mut(path).unwrap(); let vm_state = self.get_vm_state().deref().0.lock().unwrap(); - if *vm_state == KvmVmState::Running { + if *vm_state == KvmVmState::Running && !drive_file.locked { if let Err(e) = lock_file(&drive_file.file, path, read_only) { - drive_files.remove(path); + VmConfig::remove_drive_file(&mut drive_files, path)?; return Err(e); } drive_file.locked = true; @@ -1333,12 +1333,10 @@ pub trait MachineOps { } /// Unregister a drive backend file. - fn unregister_drive_file(&self, path: &str) -> Result { - self.get_drive_files() - .lock() - .unwrap() - .remove(path) - .with_context(|| "Failed to unregister drive file") + fn unregister_drive_file(&self, path: &str) -> Result<()> { + let files = self.get_drive_files(); + let mut drive_files = files.lock().unwrap(); + VmConfig::remove_drive_file(&mut drive_files, path) } /// Active drive backend files. i.e., Apply lock. diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 6388c319f..cf4bea595 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -265,7 +265,10 @@ impl LightMachine { fn create_replaceable_devices(&mut self) -> Result<()> { let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { - let block = Arc::new(Mutex::new(Block::default())); + let block = Arc::new(Mutex::new(Block::new( + BlkDevConfig::default(), + self.get_drive_files(), + ))); let virtio_mmio = VirtioMmioDevice::new(&self.sys_mem, block.clone()); rpl_devs.push(virtio_mmio); @@ -1139,6 +1142,7 @@ impl DeviceInterface for LightMachine { Ok(()) => Response::create_empty_response(), Err(ref e) => { error!("{:?}", e); + // It's safe to unwrap as the path has been registered. self.unregister_drive_file(&args.file.filename).unwrap(); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index bef579cbb..759bea05c 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1238,6 +1238,7 @@ impl DeviceInterface for StdMachine { Ok(()) => Response::create_empty_response(), Err(e) => { error!("{:?}", e); + // It's safe to unwrap as the path has been registered. self.unregister_drive_file(&args.file.filename).unwrap(); Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), @@ -1255,6 +1256,7 @@ impl DeviceInterface for StdMachine { .del_drive_by_id(&node_name) { Ok(path) => { + // It's safe to unwrap as the path has been registered. self.unregister_drive_file(&path).unwrap(); Response::create_empty_response() } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index e009b3aed..12ae64ba7 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -33,6 +33,8 @@ const MAX_UNIT_ID: usize = 2; pub struct DriveFile { /// The opened file. pub file: File, + /// The num of drives share same file. + pub count: u32, /// File path. pub path: String, /// File is read only or not. diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 9aae29f10..17ac30ef6 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -246,9 +246,10 @@ impl VmConfig { read_only: bool, direct: bool, ) -> Result<()> { - if let Some(drive_file) = drive_files.get(path) { + if let Some(drive_file) = drive_files.get_mut(path) { if drive_file.read_only && read_only { // File can be shared with read_only. + drive_file.count += 1; return Ok(()); } else { return Err(anyhow!( @@ -259,6 +260,7 @@ impl VmConfig { } let drive_file = DriveFile { file: open_file(path, read_only, direct)?, + count: 1, read_only, path: path.to_string(), locked: false, @@ -267,6 +269,25 @@ impl VmConfig { Ok(()) } + /// Remove a file from drive file store. + pub fn remove_drive_file( + drive_files: &mut HashMap, + path: &str, + ) -> Result<()> { + if let Some(drive_file) = drive_files.get_mut(path) { + drive_file.count -= 1; + if drive_file.count == 0 { + drive_files.remove(path); + } + } else { + return Err(anyhow!( + "Failed to remove drive {}, it does not exist", + path + )); + } + Ok(()) + } + /// Create initial drive file store from cmdline drive. pub fn init_drive_files(&self) -> Result> { let mut drive_files: HashMap = HashMap::new(); diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 6b721b01f..8ea7452f9 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1242,10 +1242,9 @@ mod tests { use super::super::*; use super::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use machine_manager::config::IothreadConfig; + use machine_manager::config::{IothreadConfig, VmConfig}; use std::sync::atomic::{AtomicU32, Ordering}; use std::{thread, time::Duration}; - use util::file::open_file; use vmm_sys_util::tempfile::TempFile; const QUEUE_NUM_BLK: usize = 1; @@ -1298,22 +1297,13 @@ mod tests { block.blk_cfg.direct = false; let f = TempFile::new().unwrap(); block.blk_cfg.path_on_host = f.as_path().to_str().unwrap().to_string(); - let drive_file = DriveFile { - file: open_file( - &block.blk_cfg.path_on_host, - block.blk_cfg.read_only, - block.blk_cfg.direct, - ) - .unwrap(), - path: block.blk_cfg.path_on_host.clone(), - read_only: block.blk_cfg.read_only, - locked: false, - }; - block - .drive_files - .lock() - .unwrap() - .insert(block.blk_cfg.path_on_host.clone(), drive_file); + VmConfig::add_drive_file( + &mut block.drive_files.lock().unwrap(), + &block.blk_cfg.path_on_host, + block.blk_cfg.read_only, + block.blk_cfg.direct, + ) + .unwrap(); assert!(block.realize().is_ok()); assert_eq!(block.device_type(), VIRTIO_TYPE_BLOCK); @@ -1444,6 +1434,14 @@ mod tests { block.blk_cfg.iothread = Some(thread_name); block.blk_cfg.iops = Some(100); + VmConfig::add_drive_file( + &mut block.drive_files.lock().unwrap(), + &block.blk_cfg.path_on_host, + block.blk_cfg.read_only, + block.blk_cfg.direct, + ) + .unwrap(); + let mem_space = address_space_init(); let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); -- Gitee From 394d9417c2469e1a36ea7ec1e66b4cae645e4f7a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 05:58:43 +0800 Subject: [PATCH 0559/2187] virtio-blk: Add more limitation to IO merging Linux limits the size of iovecs to 1024 (UIO_MAXIOV in the kernel sources, IOV_MAX in POSIX). Because of this, merged blk requests with many iovecs are rejected with -EINVAL by the io_submit() or readv()/writev() system calls. And refer to Qemu that has a limitation called max_transfer size of a single request to i32::MAX, which has some complex context reason. To be conservative, let's add this limitation too. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 8ea7452f9..dd29b778d 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -65,8 +65,12 @@ const SECTOR_SHIFT: u8 = 9; const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; /// Size of the dummy block device. const DUMMY_IMG_SIZE: u64 = 0; -/// Number of max merged requests. +/// Max number reqs of a merged request. const MAX_NUM_MERGE_REQS: u16 = 32; +/// Max number iovs of a merged request. +const MAX_NUM_MERGE_IOVS: usize = 1024; +/// Max number bytes of a merged request. +const MAX_NUM_MERGE_BYTES: u64 = i32::MAX as u64; /// Max time for every round of process queue. const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; @@ -433,15 +437,21 @@ impl BlockIoHandler { let mut merge_req_queue = Vec::::new(); let mut last_req: Option<&mut Request> = None; - let mut merged_reqs = 1; + let mut merged_reqs = 0; + let mut merged_iovs = 0; + let mut merged_bytes = 0; *last_aio_index = 0; for req in req_queue { + let req_iovs = req.iovec.len(); + let req_bytes = req.data_len; let io = req.out_header.request_type == VIRTIO_BLK_T_IN || req.out_header.request_type == VIRTIO_BLK_T_OUT; let can_merge = match last_req { Some(ref req_ref) => { io && merged_reqs < MAX_NUM_MERGE_REQS + && merged_iovs + req_iovs <= MAX_NUM_MERGE_IOVS + && merged_bytes + req_bytes <= MAX_NUM_MERGE_BYTES && req_ref.out_header.request_type == req.out_header.request_type && (req_ref.out_header.sector + req_ref.get_req_sector_num() == req.out_header.sector) @@ -454,6 +464,8 @@ impl BlockIoHandler { last_req_raw.next = Box::new(Some(req)); last_req = last_req_raw.next.as_mut().as_mut(); merged_reqs += 1; + merged_iovs += req_iovs; + merged_bytes += req_bytes; } else { if io { *last_aio_index = merge_req_queue.len(); @@ -461,6 +473,8 @@ impl BlockIoHandler { merge_req_queue.push(req); last_req = merge_req_queue.last_mut(); merged_reqs = 1; + merged_iovs = req_iovs; + merged_bytes = req_bytes; } } -- Gitee From 88f9eae3420e98e17a936e3e46a4afe5029a5cf3 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 6 Dec 2022 09:45:10 +0800 Subject: [PATCH 0560/2187] virtiofs: add check for memory shared property Virtiofs need to be used with shared memory. Signed-off-by: Li HuaChao --- machine/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9c26906ba..a8d21f624 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -460,6 +460,11 @@ pub trait MachineOps { let id_clone = dev_cfg.id.clone(); let sys_mem = self.get_sys_mem().clone(); let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); + + if !vm_config.machine_config.mem_config.mem_share { + bail!("When configuring the vhost-user-fs-device or vhost-user-fs-pci device, the memory must be shared."); + } + if cfg_args.contains("vhost-user-fs-device") { let device = VirtioMmioDevice::new(&sys_mem, device); self.realize_virtio_mmio_device(device) -- Gitee From c2914bd1f6bcf7155e8dd2034f809c8ba403b2f4 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 6 Dec 2022 09:48:06 +0800 Subject: [PATCH 0561/2187] fix log format error Add newline to real_main function error log. Signed-off-by: Li HuaChao --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9cc02e10b..486a57b1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -134,9 +134,9 @@ fn run() -> Result<()> { Err(ref e) => { set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); if cmd_args.is_present("display log") { - error!("{}", format!("{:?}", e)); + error!("{}", format!("{:?}\r\n", e)); } else { - write!(&mut std::io::stderr(), "{}", format_args!("{:?}", e)) + write!(&mut std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) .expect("Failed to write to stderr"); } // clean temporary file -- Gitee From 152b2047bd55d93983681cf155c5cc8cc427ccf4 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 6 Dec 2022 10:10:42 +0800 Subject: [PATCH 0562/2187] Fix: delete useless code in host.rs Currently, the device is mounted to the PciBus. The member variable device of the PciHost is not used and should to de deleted. Signed-off-by: Mingwang Li --- pci/src/host.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pci/src/host.rs b/pci/src/host.rs index 4612f220d..fb42fc28d 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -52,8 +52,6 @@ const ECAM_OFFSET_MASK: u64 = 0xfff; #[derive(Clone)] pub struct PciHost { pub root_bus: Arc>, - #[allow(dead_code)] - device: Option>>, #[cfg(target_arch = "x86_64")] config_addr: u32, pcie_ecam_range: (u64, u64), @@ -94,7 +92,6 @@ impl PciHost { ); PciHost { root_bus: Arc::new(Mutex::new(root_bus)), - device: None, #[cfg(target_arch = "x86_64")] config_addr: 0, pcie_ecam_range, -- Gitee From e0729c19fec407b3cdd317705bec8c3db0126dca Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 12:53:37 +0800 Subject: [PATCH 0563/2187] drive: Remove file from drive backend when del virtio-blk-mmio For virtio-mmio device, we has no blockdev_del qmp command, just has device_del in one go. Let's remove file from drive backend in the device_del qmp command. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index cf4bea595..40c6f0019 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -429,6 +429,9 @@ impl LightMachine { let mut configs_lock = self.replaceable_info.configs.lock().unwrap(); for (index, config) in configs_lock.iter().enumerate() { if config.id == id { + if let Some(blkconf) = config.dev_config.as_any().downcast_ref::() { + self.unregister_drive_file(&blkconf.path_on_host)?; + } configs_lock.remove(index); is_exist = true; break; -- Gitee From 56d4a77ab8dbe3288096ee8bfaa3bb749006103f Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 1 Dec 2022 16:55:50 +0800 Subject: [PATCH 0564/2187] =?UTF-8?q?Modify=20startovirt=20command=20line?= =?UTF-8?q?=20instruction=20document:=20the=20syntax=20is=20rectified=20ac?= =?UTF-8?q?cording=20to=20the=20following=20rules:=20[]=EF=BC=9A=20can=20w?= =?UTF-8?q?rite=20it=20or=20not=20{}=EF=BC=9Amust=20choose=20one=20of=20th?= =?UTF-8?q?e=20choices=20given=20in=20{}.=20<>=EF=BC=9Aaffirmatively=20cho?= =?UTF-8?q?ose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jiewangqun --- docs/config_guidebook.md | 133 ++++++++++++++++++--------------- machine_manager/src/cmdline.rs | 2 +- 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 774a1899d..33e78ba81 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -18,7 +18,7 @@ NB: machine type "none" is used to get the capabilities of stratovirt. ```shell # cmdline --machine [type=]name[,dump-guest-core=on|off,mem-share=on|off] +-machine [type=]name[,dump-guest-core={on|off}][,mem-share={on|off}] ``` ### 1.2 CPU Config @@ -46,7 +46,7 @@ If it is configured, sockets * dies * clusters * cores * threads must be equal t ```shell # cmdline --smp [cpus=]n[,maxcpus=,sockets=,dies=,clusters=,cores=,threads=] +-smp [cpus=]n[,maxcpus=][,sockets=][,dies=][,clusters=][,cores=][,threads=] ``` #### 1.2.2 CPU Features @@ -60,7 +60,7 @@ Currently, these options are supported. ```shell # cmdline --cpu host[,pmu=on|off] +-cpu host[,pmu={on|off}] ``` ### 1.3 Memory @@ -76,7 +76,8 @@ Default VM memory size is 256M. The supported VM memory size is among [128M, 512 ```shell # cmdline --m [size=]megs +-m [size=][m|M|g|G] + -m 256m -m 256 -m 1G @@ -103,8 +104,7 @@ The path has to be absolute path. ```shell # cmdline --mem-path /path/to/file --mem-path /path/to/dir +-mem-path ``` ### 1.4.1 hugepages @@ -123,7 +123,7 @@ $ sysctl vm.nr_hugepages=1024 $ cat /proc/meminfo # run StratoVirt with backend-file -... -mem-path /path/to/hugepages ... +... -mem-path ``` ### 1.5 NUMA node @@ -162,6 +162,13 @@ The following command shows how to set NUMA node: [-numa dist,src=1,dst=1,val=10] ``` +Detailed configuration instructions: +``` +-object memory-backend-ram,size=,id=,policy={bind|default|preferred|interleave},host-nodes= +-numa node[,nodeid=][,cpus=[-][:[-]]][,memdev=] +-numa dist,src=,dst=,val= +``` + ### 1.6 Kernel and Kernel Parameters StratoVirt supports to launch PE or bzImage (only x86_64) format linux kernel 4.19 and can also set kernel @@ -173,7 +180,10 @@ And the given kernel parameters will be actually analyzed by boot loader. ``` shell # cmdline --kernel /path/to/kernel \ +-kernel \ +-append + +for example: -append "console=ttyS0 rebook=k panic=1 pci=off tsc=reliable ipv6.disable=1" ``` @@ -187,7 +197,7 @@ If you want to use initrd as rootfs, `root=/dev/ram` and `rdinit=/bin/sh` must b ```shell # cmdline --initrd /path/to/initrd +-initrd ``` ### 1.8 Global config @@ -199,7 +209,7 @@ One property can be set: * pcie-root-port.fast-unplug: the fast unplug feature switch, only Kata is supported. ```shell --global pcie-root-port.fast-unplug=1 +-global pcie-root-port.fast-unplug={0|1} ``` ### 1.9 Logging @@ -212,7 +222,7 @@ You can enable StratoVirt's logging by: # Output log to stderr -D # Output log to log file --D /path/to/log/file +-D ``` StratoVirt's log-level depends on env `STRATOVIRT_LOG_LEVEL`. @@ -233,7 +243,7 @@ And you can also restore StratoVirt's **pid number** to a file by: ```shell # cmdline --pidfile /path/to/pidfile +-pidfile ``` ## 2. Device Configuration @@ -256,7 +266,7 @@ There is only one argument for iothread: ```shell # cmdline --object iothread,id=iothread1 -object iothread,id=iothread2 +-object iothread,id= ``` ### 2.2 Virtio-blk @@ -291,11 +301,12 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo ```shell # virtio mmio block device. --drive id=drive_id,file=path_on_host[,readonly=off,direct=off,throttling.iops-total=200] --device virtio-blk-device,drive=drive_id,id=blkid[,iothread=iothread1,serial=serial_num] +-drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=] +-device virtio-blk-device,drive=,id=[,iothread=][,serial=] # virtio pci block device. --drive id=drive_id,file=path_on_host[,readonly=off,direct=off,throttling.iops-total=200] --device virtio-blk-pci,drive=drive_id,bus=pcie.0,addr=0x3.0x0,id=blk-0[,multifunction=on,iothread=iothread1,serial=serial_num,num-queues=N,bootindex=1] +-drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=] +-device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,serial=][,num-queues=][,bootindex=] + ``` StratoVirt also supports vhost-user-blk-pci to get a higher performance in storage, but only standard vm supports it. @@ -306,8 +317,8 @@ You can use it by adding a new device, one more property is supported by vhost-u ```shell # vhost user blk pci device --chardev socket,id=chardevid,path=socket_path --device vhost-user-blk-pci,id=blk1,chardev=chardevid,bus=pcie.0,addr=0x3[,num-queues=N,bootindex=1] +-chardev socket,id=,path= +-device vhost-user-blk-pci,id=,chardev=,bus=,addr=<0x3>[,num-queues=][,bootindex=] ``` Note: More features to be supported. @@ -398,11 +409,11 @@ is a single function device, the function number should be set to zero. ```shell # virtio mmio net device --netdev tap,id=netdevid,ifname=host_dev_name --device virtio-net-device,netdev=netdevid,id=netid[,iothread=iothread1,mac=12:34:56:78:9A:BC] +-netdev tap,id=,ifname= +-device virtio-net-device,id=,netdev=[,iothread=][,mac=] # virtio pci net device --netdev tap,id=netdevid,ifname=host_dev_name[,queues=N] --device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC,mq=on] +-netdev tap,id=,ifname=[,queues=] +-device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}] ``` StratoVirt also supports vhost-net to get a higher performance in network. It can be set by @@ -413,11 +424,11 @@ given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatica ```shell # virtio mmio net device --netdev tap,id=netdevid,ifname=host_dev_name,vhost=on[,vhostfd=2] --device virtio-net-device,netdev=netdevid,id=netid[,iothread=iothread1,mac=12:34:56:78:9A:BC] +-netdev tap,id=,ifname=[,vhost=on[,vhostfd=]] +-device virtio-net-device,id=,netdev=[,iothread=][,mac=] # virtio pci net device --netdev tap,id=netdevid,ifname=host_dev_name,vhost=on[,vhostfd=2,queues=N] --device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC,mq=on] +-netdev tap,id=,ifname=[,vhost=on[,vhostfd=,queues=]] +-device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}] ``` StratoVirt also supports vhost-user net to get a higher performance by ovs-dpdk. Currently, only @@ -427,8 +438,8 @@ hugepages('-mem-path ...' ) when using vhost-user net. ```shell # virtio pci net device -chardev socket,id=chardevid,path=socket_path --netdev vhost-user,id=netdevid,chardev=chardevid[,queues=N] --device virtio-net-pci,netdev=netdevid,id=netid,mac=12:34:56:78:9A:BC,bus=pci.0,addr=0x2.0x0[,mq=on] +-netdev vhost-user,id=,chardev=[,queues=] +-device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}] ``` *How to set a tap device?* @@ -506,14 +517,14 @@ of device and the second one represents function number of it. ```shell # virtio mmio device --device virtio-serial-device[,id=virtio-serial0] --chardev socket,path=socket_path,id=virtioconsole1,server,nowait --device virtconsole,chardev=virtioconsole1,id=console_id +-device virtio-serial-device[,id=] +-chardev socket,path=,id=,server,nowait +-device virtconsole,id=,chardev= # virtio pci device --device virtio-serial-pci,bus=pcie.0,addr=0x1.0x0,id=virtio-serial0[,multifunction=on] --chardev socket,path=socket_path,id=virtioconsole1,server,nowait --device virtconsole,chardev=virtioconsole1,id=console_id +-device virtio-serial-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}] +-chardev socket,path=,id=,server,nowait +-device virtconsole,id=,chardev= ``` NB: Currently, only one virtio console device is supported in standard machine. @@ -542,10 +553,10 @@ of device and the second one represents function number of it. ```shell # virtio mmio device. --device vhost-vsock-device,id=vsock_id,guest-cid=3 +-device vhost-vsock-device,id=,guest-cid= # virtio pci device. --device vhost-vsock-pci,id=vsock_id,guest-cid=3,bus=pcie.0,addr=0x1.0x0[,multifunction=on] +-device vhost-vsock-pci,id=,guest-cid=,bus=,addr=<0x3>[,multifunction={on|off}] ``` *You can only set one virtio vsock device for one VM.* @@ -572,7 +583,7 @@ NB: We can only set *one* serial. To use the first method, chardev for redirection will be required. See [section 2.12 Chardev](#212-chardev) for details. ```shell # add a chardev and redirect the serial port to chardev --chardev backend,id=chardev_id[,path=path,server,nowait] +-chardev backend,id=[,path=,server,nowait] -serial chardev:chardev_id ``` @@ -581,8 +592,8 @@ Or you can simply use `-serial dev` to bind serial with character device. # simplifed redirect methods -serial stdio -serial pty --serial socket,path=socket_path,server,nowait --serial file,path=file_path +-serial socket,path=,server,nowait +-serial file,path= ``` ### 2.7 Virtio-balloon @@ -599,9 +610,9 @@ of device and the second one represents function number of it. ```shell # virtio mmio balloon device --device virtio-balloon-device[,deflate-on-oom=true|false][,free-page-reporting=true|false] +-device virtio-balloon-device[,deflate-on-oom={true|false}][,free-page-reporting={true|false}] # virtio pci balloon device --device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,id=balloon-0[,deflate-on-oom=true|false][,free-page-reporting=true|false][,multifunction=on|off] +-device virtio-balloon-pci,id=,bus=,addr=<0x4>[,deflate-on-oom={true|false}][,free-page-reporting={true|false}][,multifunction={on|off}] ``` Note: avoid using balloon devices and vfio devices together, balloon device is invalid when memory is hugepages. @@ -633,11 +644,11 @@ single function device, the function number should be set to zero. ```shell # virtio mmio rng device --object rng-random,id=objrng0,filename=/path/to/random_file --device virtio-rng-device,rng=objrng0,max-bytes=1234,period=1000 +-object rng-random,id=,filename= +-device virtio-rng-device,rng=,max-bytes=<1234>,period=<1000> # virtio pci rng device --object rng-random,id=objrng0,filename=/path/to/random_file --device virtio-rng-pci,rng=objrng0,max-bytes=1234,period=1000,bus=pcie.0,addr=0x1.0x0,id=rng-id[,multifunction=on] +-object rng-random,id=,filename= +-device virtio-rng-pci,id=,rng=[,max-bytes=<1234>][,period=<1000>],bus=,addr=<0x1>[,multifunction={on|off}] ``` ### 2.9 PCIe root port @@ -654,7 +665,7 @@ Four parameters are supported for pcie root port. If not set, default value is false. ```shell --device pcie-root-port,port=0x1,addr=0x1,bus=pcie.0,id=pcie.1[,multifunction=on] +-device pcie-root-port,id=,port=<0x1>,bus=,addr=<0x1>[,multifunction={on|off}] ``` **The slot number of the device attached to the root port must be 0** @@ -673,8 +684,7 @@ Four properties can be set for PFlash device. ```shell # cmdline --drive file=/path/to/code_storage_file,if=pflash,unit=0[,readonly=true] --drive file=/path/to/data_storage_file,if=pflash,unit=1, +-drive file=,if=pflash,unit={0|1}[,readonly={true|false}] ``` ### 2.11 VFIO @@ -689,7 +699,7 @@ Four properties are supported for VFIO device * addr: including slot number and function number. ```shell --device vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x03.0x0[,multifunction=on] +-device vfio-pci,id=,host=<0000:1a:00.3>,bus=,addr=<0x03>[,multifunction={on|off}] ``` Note: the kernel must contain physical device drivers, otherwise it cannot be loaded normally. @@ -709,10 +719,10 @@ Five properties can be set for chardev. ```shell # redirect methods --chardev stdio,id=chardev_id --chardev pty,id=chardev_id --chardev socket,id=chardev_id,path=socket_path[,server,nowait] --chardev file,id=chardev_id,path=file_path +-chardev stdio,id= +-chardev pty,id= +-chardev socket,id=,path=[,server,nowait] +-chardev file,id=,path= ``` ### 2.13 USB controller @@ -725,7 +735,7 @@ Three properties can be set for USB controller. * addr: including slot number and function number. ```shell --device nec-usb-xhci,id=xhci,bus=pcie.0,addr=0xa.0x0 +-device nec-usb-xhci,id=,bus=,addr=<0xa> ``` Note: Only one USB controller can be configured, USB controller can only support USB keyboard and USB tablet. @@ -738,7 +748,7 @@ One property can be set for USB Keyboard. * id: unique device id. ```shell --device usb-kbd,id=kbd +-device usb-kbd,id= ``` Note: Only one keyboard can be configured. @@ -751,7 +761,7 @@ One property can be set for USB Tablet. * id: unique device id. ```shell --device usb-tablet,id=tablet +-device usb-tablet,id= ``` Note: Only one tablet can be configured. @@ -767,7 +777,7 @@ Five properties can be set for Virtio-Scsi controller. * iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) * num-queues: the optional num-queues attribute controls the number of request queues to be used for the scsi controller. If not set, the default block queue number is 1. The max queues number supported is no more than 32. (optional) ```shell --device virtio-scsi-pci,bus=pcie.1,addr=0x0,id=scsi0[,multifunction=on,iothread=iothread1,num-queues=4] +-device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=] ``` ### 2.17 Virtio Scsi HardDisk Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. @@ -799,6 +809,7 @@ In order to use VNC, the ip and port value must be configured. The IP address ca ```shell -vnc 0.0.0.0:0 +-vnc ``` Tls encryption is an optional configuration.Three properties can be set for encrypted transmission: @@ -808,7 +819,7 @@ Tls encryption is an optional configuration.Three properties can be set for encr * dir: certificate directory. You should place a legal institutional certificate, a server certificate, and a private key for certificate encryption in this directory. ```shell --object tls-creds-x509,id=vnc-tls-creds0,dir=/etc/pki/vnc +-object tls-creds-x509,id=,dir= ``` Authentication is an optional configuration, it depends on the saslauth service . To use this function, you must ensure that the saslauthd service is running normally, and configure the supported authentication mechanism in `/etc/sasl2/stratovirt. conf` @@ -851,8 +862,8 @@ Three properties can be set for virtio fs device. * mount_tag: the mount tag of the shared directory which can be mounted in the guest ```shell --chardev socket,id=chardevid,path=socket_path --device vhost-user-fs-pci,id=device_id,chardev=chardevid,tag=mount_tag +-chardev socket,id=,path= +-device vhost-user-fs-pci,id=,chardev=,tag= ``` ### 2.18.2 vhost_user_fs diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 13f23aab1..10b6014f4 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -194,7 +194,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .multiple(true) .long("netdev") .value_name( - "tap,id=,ifname=[,queue=]", + "tap,id=,ifname=[,vhost=on|off][,queue=]", ) .help("configure a host TAP network with ID 'str'") .takes_values(true), -- Gitee From 42f8f2c62c957b7b6586530046999818bb5635d6 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 5 Dec 2022 16:52:00 +0800 Subject: [PATCH 0565/2187] virtiofs: to support the VM reboots with virtiofs device This commit supports the reset of viritofs devices. Now you can restart the VM with the virtiofs configured. After the restart, the virtiofs device can work normally. Signed-off-by: Li HuaChao --- vhost_user_fs/src/vhost_user_fs.rs | 7 +------ vhost_user_fs/src/virtio_fs.rs | 10 +++++++--- virtio/src/vhost/user/fs.rs | 26 +++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index d20901283..43e6f7852 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -49,12 +49,7 @@ impl CreateEventNotifier for VhostUserServerHandler { server_handler: Arc>, ) -> Option> { let mut notifiers = Vec::new(); - if self.sock.domain.is_accepted() { - if let Err(e) = self.sock.domain.server_connection_refuse() { - error!("Failed to refuse socket for vhost user server, {:?}", e); - } - return None; - } else if let Err(e) = self.sock.domain.accept() { + if let Err(e) = self.sock.domain.accept() { error!("Failed to accept the socket for vhost user server, {:?}", e); return None; } diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 276dab6dc..e7ca48ade 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -426,6 +426,12 @@ impl VhostUserReqHandler for VirtioFs { let mut queue_info = self.config.get_mut_queue_config(queue_index)?; queue_info.config.ready = status == 1; + // Before setting up new notifiers, we should remove old ones. + if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { + EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) + .with_context(|| "Failed to update event for queue status which is not ready")?; + }; + if status == 1 { if queue_info.kick_evt.is_none() || queue_info.call_evt.is_none() { bail!( @@ -450,10 +456,8 @@ impl VhostUserReqHandler for VirtioFs { self.fs_handlers[queue_index] = Some(fs_handler.clone()); EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) .with_context(|| "Failed to update event for queue status which is ready")?; - } else if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { - EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) - .with_context(|| "Failed to update event for queue status which is not ready")?; } + Ok(()) } } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index c2137e698..c4134073f 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -190,8 +190,32 @@ impl VirtioDevice for Fs { } match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), - None => return Err(anyhow!("Failed to get client for vhost-user net")), + None => return Err(anyhow!("Failed to get client for virtio fs")), } Ok(()) } + + fn deactivate(&mut self) -> Result<()> { + self.call_events.clear(); + Ok(()) + } + + fn reset(&mut self) -> Result<()> { + self.avail_features = 0_u64; + self.acked_features = 0_u64; + self.config = VirtioFsConfig::default(); + + let client = match &self.client { + None => return Err(anyhow!("Failed to get client when reseting virtio fs")), + Some(client_) => client_, + }; + client + .lock() + .unwrap() + .delete_event() + .with_context(|| "Failed to delete virtio fs event")?; + self.client = None; + + self.realize() + } } -- Gitee From 2df0f8e2eb6362740595ed5ff10fdc37ae04aa5f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 16:23:11 +0800 Subject: [PATCH 0566/2187] drive: Factor out fetch_drive_file into vmconfig ... to share code logic. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 8 +------- machine_manager/src/config/mod.rs | 13 ++++++++++++- virtio/src/block.rs | 6 ++---- virtio/src/scsi/disk.rs | 7 ++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d1f37cc46..d80560e3b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1304,13 +1304,7 @@ pub trait MachineOps { fn fetch_drive_file(&self, path: &str) -> Result { let files = self.get_drive_files(); let drive_files = files.lock().unwrap(); - match drive_files.get(path) { - Some(drive_file) => drive_file - .file - .try_clone() - .with_context(|| format!("Failed to clone drive backend file {}", path)), - None => Err(anyhow!("The file {} is not in drive backend", path)), - } + VmConfig::fetch_drive_file(&drive_files, path) } /// Register a new drive backend file. diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 17ac30ef6..701c5e287 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -54,9 +54,9 @@ mod usb; mod vfio; pub mod vnc; -use std::any::Any; use std::collections::HashMap; use std::str::FromStr; +use std::{any::Any, fs::File}; use serde::{Deserialize, Serialize}; @@ -288,6 +288,17 @@ impl VmConfig { Ok(()) } + /// Get a file from drive file store. + pub fn fetch_drive_file(drive_files: &HashMap, path: &str) -> Result { + match drive_files.get(path) { + Some(drive_file) => drive_file + .file + .try_clone() + .with_context(|| format!("Failed to clone drive backend file {}", path)), + None => Err(anyhow!("The file {} is not in drive backend", path)), + } + } + /// Create initial drive file store from cmdline drive. pub fn init_drive_files(&self) -> Result> { let mut drive_files: HashMap = HashMap::new(); diff --git a/virtio/src/block.rs b/virtio/src/block.rs index dd29b778d..a913767b3 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -34,7 +34,7 @@ use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, warn}; -use machine_manager::config::DriveFile; +use machine_manager::config::{DriveFile, VmConfig}; use machine_manager::{ config::{BlkDevConfig, ConfigCheck}, event_loop::EventLoop, @@ -1034,9 +1034,7 @@ impl VirtioDevice for Block { let mut disk_size = DUMMY_IMG_SIZE; if !self.blk_cfg.path_on_host.is_empty() { let drive_files = self.drive_files.lock().unwrap(); - // It's safe to unwrap as the path has been registered. - let drive_file = drive_files.get(&self.blk_cfg.path_on_host).unwrap(); - let mut file = drive_file.file.try_clone()?; + let mut file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; disk_size = file.seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for block")? as u64; diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index cdf515020..145dcf74d 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; use crate::ScsiBus::ScsiBus; -use machine_manager::config::{DriveFile, ScsiDevConfig}; +use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -146,10 +146,7 @@ impl ScsiDevice { self.disk_image = None; let drive_files = self.drive_files.lock().unwrap(); - // It's safe to unwrap as the path has been registered. - let drive_file = drive_files.get(&self.config.path_on_host).unwrap(); - let mut file = drive_file.file.try_clone()?; - + let mut file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; disk_size = file .seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for scsi device")? -- Gitee From f11cf1745b1e6426ad43292640ff02809a524079 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 15:43:00 +0800 Subject: [PATCH 0567/2187] drive: Detail the err msg for file locking failure Make the err msg looks more friendly. Signed-off-by: Keqian Zhu --- machine_manager/src/config/mod.rs | 3 ++- util/src/file.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 701c5e287..c150fd363 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -253,7 +253,8 @@ impl VmConfig { return Ok(()); } else { return Err(anyhow!( - "Failed to add drive {}, file can only be shared with read_only", + "Failed to add drive {}, file can only be shared with read_only. \ + Is it used more than once or another process using the same file?", path )); } diff --git a/util/src/file.rs b/util/src/file.rs index 0ee56702b..cb3ffe958 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -42,7 +42,8 @@ pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { let ret = unsafe { libc::flock(file.as_raw_fd(), lockop) }; if ret < 0 { bail!( - "Failed to get {} on file: {}. Maybe it's used more than once. Error: {}", + "Failed to get {} on file: {}. Is it used more than once or \ + another process using the same file?. Error: {}", lockname, path, std::io::Error::last_os_error(), -- Gitee From 218878f811088ef882f452ba0c8578d4f70b78c3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Dec 2022 19:54:05 +0800 Subject: [PATCH 0568/2187] virtio-blk: Avoid use direct flag when open temp file Some Temp filesystems may not implement the O_DIRECT flag, in which case open() fails with the error EINVAL if it's used. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index a913767b3..63bc218c3 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1441,6 +1441,7 @@ mod tests { let mut block = Block::default(); let file = TempFile::new().unwrap(); block.blk_cfg.path_on_host = file.as_path().to_str().unwrap().to_string(); + block.blk_cfg.direct = false; // config iothread and iops block.blk_cfg.iothread = Some(thread_name); -- Gitee From 67c807e0ef5f04758374e76409efb671e292755a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 6 Dec 2022 20:20:25 +0800 Subject: [PATCH 0569/2187] virtio-pci: do not check device_status for queue msix vector When detaching device, the device status will be set to value 0, and check_device_status() will be failed. It will cause setting queue msix vector to INVALID_VECTOR_NUM(0xffff) failed. So, don't check when device status is 0. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index c8b84f1c3..2045a7d32 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -194,7 +194,13 @@ impl VirtioPciCommonConfig { self.device_status & (set | clr) == set } - fn get_mut_queue_config(&mut self) -> PciResult<&mut QueueConfig> { + fn get_mut_queue_config(&mut self, need_check: bool) -> PciResult<&mut QueueConfig> { + if !need_check { + return self + .queues_config + .get_mut(self.queue_select as usize) + .ok_or_else(|| anyhow!("pci-reg queue_select overflows")); + } if self.check_device_status( CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FAILED, @@ -380,37 +386,43 @@ impl VirtioPciCommonConfig { } } COMMON_Q_SIZE_REG => self - .get_mut_queue_config() + .get_mut_queue_config(true) .map(|config| config.size = value as u16)?, COMMON_Q_ENABLE_REG => { if value != 1 { error!("Driver set illegal value for queue_enable {}", value); return Err(anyhow!(PciError::QueueEnable(value))); } - self.get_mut_queue_config() + self.get_mut_queue_config(true) .map(|config| config.ready = true)?; } COMMON_Q_MSIX_REG => { let val = self.revise_queue_vector(value, virtio_pci_dev); - self.get_mut_queue_config() + // It should not check device status when detaching device which + // will set vector to INVALID_VECTOR_NUM. + let mut need_check = true; + if self.device_status == 0 { + need_check = false; + } + self.get_mut_queue_config(need_check) .map(|config| config.vector = val as u16)?; } - COMMON_Q_DESCLO_REG => self.get_mut_queue_config().map(|config| { + COMMON_Q_DESCLO_REG => self.get_mut_queue_config(true).map(|config| { config.desc_table = GuestAddress(config.desc_table.0 | u64::from(value)); })?, - COMMON_Q_DESCHI_REG => self.get_mut_queue_config().map(|config| { + COMMON_Q_DESCHI_REG => self.get_mut_queue_config(true).map(|config| { config.desc_table = GuestAddress(config.desc_table.0 | (u64::from(value) << 32)); })?, - COMMON_Q_AVAILLO_REG => self.get_mut_queue_config().map(|config| { + COMMON_Q_AVAILLO_REG => self.get_mut_queue_config(true).map(|config| { config.avail_ring = GuestAddress(config.avail_ring.0 | u64::from(value)); })?, - COMMON_Q_AVAILHI_REG => self.get_mut_queue_config().map(|config| { + COMMON_Q_AVAILHI_REG => self.get_mut_queue_config(true).map(|config| { config.avail_ring = GuestAddress(config.avail_ring.0 | (u64::from(value) << 32)); })?, - COMMON_Q_USEDLO_REG => self.get_mut_queue_config().map(|config| { + COMMON_Q_USEDLO_REG => self.get_mut_queue_config(true).map(|config| { config.used_ring = GuestAddress(config.used_ring.0 | u64::from(value)); })?, - COMMON_Q_USEDHI_REG => self.get_mut_queue_config().map(|config| { + COMMON_Q_USEDHI_REG => self.get_mut_queue_config(true).map(|config| { config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); })?, _ => { -- Gitee From f47d193e0217e2a6aead7abe24f3967b8511d51c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 6 Dec 2022 20:42:02 +0800 Subject: [PATCH 0570/2187] virtio-net: support setting default mac address Set the default mac address when the user doesn't set mac in virtio-net-pci/device command line. The default mac address is "0x52:0x54:0x00:0x12:0x34:XX", and 'XX' in range [0x56, 0xff). Signed-off-by: Yan Wang --- Cargo.lock | 1 + docs/config_guidebook.md | 3 ++- virtio/Cargo.toml | 1 + virtio/src/net.rs | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 61ce029a0..291e2de38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1013,6 +1013,7 @@ dependencies = [ "machine_manager", "migration", "migration_derive", + "once_cell", "pci", "serde_json", "sysbus", diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 33e78ba81..402a062de 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -398,7 +398,8 @@ It has no effect when vhost is set. * vhost: whether to run as a vhost-net device. * vhostfd: the file descriptor of opened tap device. * vhostfds: file descriptors of opened tap device. -* mac: set mac address in VM (optional). +* mac: set mac address in VM (optional). A default mac address will be created when it is not assigned by user. So, it may + cause the same mac address between two virtio-net devices when one device has mac and the other hasn't. * mq: the optional mq attribute enable device multiple queue feature. Two more properties are supported for virtio pci net device. diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 78d33fdd5..415746a14 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -15,6 +15,7 @@ libc = "0.2" log = "0.4" serde_json = "1.0" vmm-sys-util = "0.11.0" +once_cell = "1.13.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 50987e6ff..be01c5459 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use once_cell::sync::Lazy; use std::collections::HashMap; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; @@ -65,9 +66,17 @@ const ETHERNET_HDR_LENGTH: usize = 14; const CTRL_MAC_TABLE_LEN: usize = 64; /// From 802.1Q definition, the max vlan ID. const CTRL_MAX_VLAN: u16 = 1 << 12; +/// The max num of the mac address. +const MAX_MAC_ADDR_NUM: usize = 0xff; type SenderConfig = Option; +/// The first default mac address. +const FIRST_DEFAULT_MAC: [u8; MAC_ADDR_LEN] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]; +/// Used to mark if the last byte of the mac address is used. +static USED_MAC_TABLE: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new([0_i8; MAX_MAC_ADDR_NUM]))); + /// Configuration of virtio-net devices. #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] @@ -1196,6 +1205,38 @@ pub fn build_device_config_space(device_config: &mut VirtioNetConfig, mac: &str) config_features } +/// Mark the mac table used or free. +fn mark_mac_table(mac: &[u8], used: bool) { + if mac[..MAC_ADDR_LEN - 1] != FIRST_DEFAULT_MAC[..MAC_ADDR_LEN - 1] { + return; + } + let mut val = -1_i8; + if used { + val = 1; + } + let mut locked_mac_table = USED_MAC_TABLE.lock().unwrap(); + for i in FIRST_DEFAULT_MAC[MAC_ADDR_LEN - 1]..MAX_MAC_ADDR_NUM as u8 { + if mac[MAC_ADDR_LEN - 1] == i { + locked_mac_table[i as usize] += val; + } + } +} + +/// Get a default free mac address. +fn get_default_mac_addr() -> Result<[u8; MAC_ADDR_LEN]> { + let mut mac = [0_u8; MAC_ADDR_LEN]; + mac.copy_from_slice(&FIRST_DEFAULT_MAC); + let mut locked_mac_table = USED_MAC_TABLE.lock().unwrap(); + for i in FIRST_DEFAULT_MAC[MAC_ADDR_LEN - 1]..MAX_MAC_ADDR_NUM as u8 { + if locked_mac_table[i as usize] == 0 { + mac[MAC_ADDR_LEN - 1] = i as u8; + locked_mac_table[i as usize] = 1; + return Ok(mac); + } + } + bail!("Failed to get a free mac address"); +} + /// Check that tap flag supports multi queue feature. /// /// # Arguments @@ -1372,12 +1413,22 @@ impl VirtioDevice for Net { if let Some(mac) = &self.net_cfg.mac { locked_state.device_features |= build_device_config_space(&mut locked_state.config_space, mac); + mark_mac_table(&locked_state.config_space.mac, true); + } else if locked_state.config_space.mac == [0; MAC_ADDR_LEN] { + let mac = + get_default_mac_addr().with_context(|| "Failed to get a default mac address")?; + locked_state.config_space.mac.copy_from_slice(&mac); + locked_state.device_features |= 1 << VIRTIO_NET_F_MAC; + } else { + // For microvm which will call realize() twice for one virtio-net-device. + locked_state.device_features |= 1 << VIRTIO_NET_F_MAC; } Ok(()) } fn unrealize(&mut self) -> Result<()> { + mark_mac_table(&self.state.lock().unwrap().config_space.mac, false); MigrationManager::unregister_device_instance( VirtioNetState::descriptor(), &self.net_cfg.id, -- Gitee From 221746260f013a801e25015713626650e61c8143 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 6 Dec 2022 20:48:36 +0800 Subject: [PATCH 0571/2187] virtio-net: enable the filter features Fix commit 66059e5d "virtio-net: disable the filter features". Currently, it supports default mac address. We can enable all the filter features. Signed-off-by: Yan Wang --- virtio/src/net.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index be01c5459..88c0e59c8 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -27,10 +27,12 @@ use super::{ VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, - VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, @@ -54,8 +56,8 @@ use util::tap::{ Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, }; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -/// Number of virtqueues. -const QUEUE_NUM_NET: usize = 2; +/// Number of virtqueues(rx/tx/ctrl). +const QUEUE_NUM_NET: usize = 3; /// Size of each virtqueue. const QUEUE_SIZE_NET: u16 = 256; /// The Mac Address length. @@ -1368,6 +1370,11 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_TSO4 | 1 << VIRTIO_NET_F_HOST_TSO6 | 1 << VIRTIO_NET_F_HOST_UFO + | 1 << VIRTIO_NET_F_CTRL_RX + | 1 << VIRTIO_NET_F_CTRL_VLAN + | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA + | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR + | 1 << VIRTIO_NET_F_CTRL_VQ | 1 << VIRTIO_F_RING_EVENT_IDX; let queue_pairs = self.net_cfg.queues / 2; @@ -1376,7 +1383,6 @@ impl VirtioDevice for Net { .contains(&queue_pairs) { locked_state.device_features |= 1 << VIRTIO_NET_F_MQ; - locked_state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; locked_state.config_space.max_virtqueue_pairs = queue_pairs; } @@ -1708,7 +1714,7 @@ mod tests { // test net realize method net.realize().unwrap(); assert_eq!(net.device_type(), 1); - assert_eq!(net.queue_num(), 2); + assert_eq!(net.queue_num(), 3); assert_eq!(net.queue_size(), 256); // test read_config and write_config method -- Gitee From e19912db06b7e199f854eaa32f6571c462d6276a Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Tue, 6 Dec 2022 22:50:11 +0800 Subject: [PATCH 0572/2187] Fix incorrect use of KVM_SET_IDENTITY_MAP_ADDR ioctl in x86 standard machine The kvm KVM_SET_IDENTITY_MAP_ADDR ioctl API must be called before the vcpu is created. However, in standard virtual machine realization processing, the KVM KVM_SET_IDENTITY_MAP_ADDR ioctl call occurs after the VCPU is created. Signed-off-by: Yan Wen --- machine/src/error.rs | 3 +++ machine/src/standard_vm/x86_64/mod.rs | 18 ++++++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/machine/src/error.rs b/machine/src/error.rs index 271cde945..12bbe44a3 100644 --- a/machine/src/error.rs +++ b/machine/src/error.rs @@ -87,6 +87,9 @@ pub enum MachineError { #[error("Failed to create irq chip.")] #[cfg(target_arch = "x86_64")] CrtIrqchipErr, + #[error("Failed to set identity map address.")] + #[cfg(target_arch = "x86_64")] + SetIdentityMapAddr, #[error("Failed to set tss address.")] #[cfg(target_arch = "x86_64")] SetTssErr, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 725f33450..4c6a86c07 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -21,7 +21,7 @@ use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; -use vmm_sys_util::{eventfd::EventFd, ioctl_ioc_nr, ioctl_iow_nr}; +use vmm_sys_util::eventfd::EventFd; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, @@ -226,16 +226,10 @@ impl StdMachine { let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); let identity_addr: u64 = MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0; - ioctl_iow_nr!( - KVM_SET_IDENTITY_MAP_ADDR, - kvm_bindings::KVMIO, - 0x48, - std::os::raw::c_ulong - ); - // Safe because the following ioctl only sets identity map address to KVM. - unsafe { - vmm_sys_util::ioctl::ioctl_with_ref(vm_fd, KVM_SET_IDENTITY_MAP_ADDR(), &identity_addr); - } + vm_fd + .set_identity_map_address(identity_addr) + .with_context(|| anyhow!(MachineError::SetIdentityMapAddr))?; + // Page table takes 1 page, TSS takes the following 3 pages. vm_fd .set_tss_address((identity_addr + 0x1000) as usize) @@ -434,6 +428,7 @@ impl MachineOps for StdMachine { )?; locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; + StdMachine::arch_init()?; let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); let mut vcpu_fds = vec![]; @@ -478,7 +473,6 @@ impl MachineOps for StdMachine { .with_context(|| "Failed to create ACPI tables")?; } - StdMachine::arch_init()?; locked_vm.register_power_event(&locked_vm.power_button)?; locked_vm -- Gitee From 438175621be9498474d387095d39d3ca517f9617 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Wed, 7 Dec 2022 11:33:49 +0800 Subject: [PATCH 0573/2187] virtiofs: gracefully exit the virtiofsd process when the VM exits Do not abort the virtiofsd process directly when the virtual machine exits. Use EventLoopManager to actively exit and clean up resources. Signed-off-by: Li HuaChao --- vhost_user_fs/src/vhost_user_fs.rs | 33 +++++++++++++++++++------- vhost_user_fs/src/vhost_user_server.rs | 16 ++++++++++--- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index 43e6f7852..5fd7162a6 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -11,12 +11,15 @@ // See the Mulan PSL v2 for more details. use std::os::unix::io::RawFd; -use std::sync::{Arc, Mutex}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, +}; use log::error; use vmm_sys_util::epoll::EventSet; -use machine_manager::event_loop::EventLoop; +use machine_manager::{event_loop::EventLoop, temp_cleaner::TempCleaner}; use util::loop_context::{ EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -34,6 +37,8 @@ use anyhow::{Context, Result}; pub struct VhostUserFs { /// Used to communicate with StratoVirt. server_handler: VhostUserServerHandler, + /// Used to determine whether the process should be terminated. + should_exit: Arc, } trait CreateEventNotifier { @@ -49,6 +54,8 @@ impl CreateEventNotifier for VhostUserServerHandler { server_handler: Arc>, ) -> Option> { let mut notifiers = Vec::new(); + let should_exit = self.should_exit.clone(); + if let Err(e) = self.sock.domain.accept() { error!("Failed to accept the socket for vhost user server, {:?}", e); return None; @@ -64,10 +71,10 @@ impl CreateEventNotifier for VhostUserServerHandler { } if event & EventSet::HANG_UP == EventSet::HANG_UP { - panic!("Receive the event of HANG_UP from stratovirt"); - } else { - None + should_exit.store(true, Ordering::Release); } + + None }); handlers.push(Arc::new(Mutex::new(handler))); @@ -126,6 +133,8 @@ impl VhostUserFs { /// /// * `fs_config` - Configuration of the vhost-user filesystem device. pub fn new(fs_config: FsConfig) -> Result { + let should_exit = Arc::new(AtomicBool::new(false)); + if let Some(limit) = fs_config.rlimit_nofile { set_rlimit_nofile(limit) .with_context(|| format!("Failed to set rlimit nofile {}", limit))?; @@ -136,9 +145,14 @@ impl VhostUserFs { VirtioFs::new(fs_config).with_context(|| "Failed to create virtio fs")?, )); - let server_handler = VhostUserServerHandler::new(sock_path.as_str(), virtio_fs) - .with_context(|| "Failed to create vhost user server")?; - Ok(VhostUserFs { server_handler }) + let server_handler = + VhostUserServerHandler::new(sock_path.as_str(), virtio_fs, should_exit.clone()) + .with_context(|| "Failed to create vhost user server")?; + + Ok(VhostUserFs { + server_handler, + should_exit, + }) } /// Add events to epoll handler for the vhost-user filesystem device. @@ -156,10 +170,11 @@ impl VhostUserFs { impl EventLoopManager for VhostUserFs { fn loop_should_exit(&self) -> bool { - false + self.should_exit.load(Ordering::Acquire) } fn loop_cleanup(&self) -> util::Result<()> { + TempCleaner::clean(); Ok(()) } } diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 713dede48..a7265a376 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -15,7 +15,7 @@ use machine_manager::temp_cleaner::TempCleaner; use std::mem::size_of; use std::os::unix::io::RawFd; use std::slice; -use std::sync::{Arc, Mutex}; +use std::sync::{atomic::AtomicBool, Arc, Mutex}; use util::unix::limit_permission; use virtio::vhost::user::{ RegionMemInfo, VhostUserHdrFlag, VhostUserMemHdr, VhostUserMsgHdr, VhostUserMsgReq, @@ -120,6 +120,8 @@ pub struct VhostUserServerHandler { pub sock: VhostUserSock, /// The backend used to save the data of requests from StratoVirt. backend: Arc>, + /// Used to determine whether the process should be terminated. + pub should_exit: Arc, } fn close_fds(fds: Vec) { @@ -154,7 +156,11 @@ impl VhostUserServerHandler { /// /// * `path` - The path of unix socket file which communicates with StratoVirt. /// * `backend` - The trait of backend used to save the data of requests from StratoVirt. - pub fn new(path: &str, backend: Arc>) -> Result { + pub fn new( + path: &str, + backend: Arc>, + should_exit: Arc, + ) -> Result { let mut sock = VhostUserSock::new(path); sock.domain .bind(false) @@ -162,7 +168,11 @@ impl VhostUserServerHandler { TempCleaner::add_path(path.to_string()); limit_permission(path).with_context(|| format!("Failed to limit permission {}", path))?; - Ok(VhostUserServerHandler { sock, backend }) + Ok(VhostUserServerHandler { + sock, + backend, + should_exit, + }) } fn recv_hdr_and_fds(&mut self) -> Result<(VhostUserMsgHdr, Option>)> { -- Gitee From b21c459796357d19d2b7e49db7ea1ab9e89bbf0a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 8 Dec 2022 16:26:24 +0800 Subject: [PATCH 0574/2187] virtio-net: do not return error when writev tx packets failed Return error will cause the virtio-net device reporting virtio error. But, we don't want to report virtio error. So, just retry for WouldBlock error, and ignore other errors. Signed-off-by: Yan Wang --- virtio/src/net.rs | 49 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 88c0e59c8..5b8f1915a 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -12,7 +12,7 @@ use once_cell::sync::Lazy; use std::collections::HashMap; -use std::io::Write; +use std::io::{ErrorKind, Write}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::sync::mpsc::{channel, Receiver, Sender}; @@ -818,6 +818,29 @@ impl NetIoHandler { Ok(()) } + fn send_packets(&self, tap_fd: libc::c_int, iovecs: &[libc::iovec]) -> i8 { + loop { + let size = unsafe { + libc::writev( + tap_fd, + iovecs.as_ptr() as *const libc::iovec, + iovecs.len() as libc::c_int, + ) + }; + if size < 0 { + let e = std::io::Error::last_os_error(); + match e.kind() { + ErrorKind::Interrupted => continue, + ErrorKind::WouldBlock => return -1_i8, + // Ignore other errors which can not be handled. + _ => error!("Failed to call writev for net handle_tx: {}", e), + } + } + break; + } + 0_i8 + } + fn handle_tx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); @@ -845,21 +868,17 @@ impl NetIoHandler { error!("Failed to get host address for {}", elem_iov.addr.0); } } - let mut read_len = 0; - if let Some(tap) = self.tap.as_mut() { - if !iovecs.is_empty() { - read_len = unsafe { - libc::writev( - tap.as_raw_fd() as libc::c_int, - iovecs.as_ptr() as *const libc::iovec, - iovecs.len() as libc::c_int, - ) - }; - } + let tap_fd = if let Some(tap) = self.tap.as_mut() { + tap.as_raw_fd() as libc::c_int + } else { + -1_i32 }; - if read_len < 0 { - let e = std::io::Error::last_os_error(); - bail!("Failed to call writev for net handle_tx: {}", e); + if tap_fd != -1 && !iovecs.is_empty() && self.send_packets(tap_fd, &iovecs) == -1 { + queue.vring.push_back(); + self.tx.queue_evt.write(1).with_context(|| { + "Failed to trigger tx queue event when writev blocked".to_string() + })?; + return Ok(()); } queue -- Gitee From 02cb5c2fc11b39e978d590d8f436a768f3f10a15 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 8 Dec 2022 16:36:01 +0800 Subject: [PATCH 0575/2187] virtio-net: handle QUEUE_SIZE_NET requests at most once If the virtio-net device used EVENT_INX feature, the request handling may stuck the main loop process. So, just handle QUEUE_SIZE_NET requests at most once. Signed-off-by: Yan Wang --- virtio/src/net.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 5b8f1915a..3d0b26dcd 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -734,11 +734,20 @@ impl NetIoHandler { fn handle_rx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to rx".to_string()); let mut queue = self.rx.queue.lock().unwrap(); + let mut rx_packets = 0; while let Some(tap) = self.tap.as_mut() { if queue.vring.avail_ring_len(&self.mem_space)? == 0 { self.rx.queue_full = true; break; } + rx_packets += 1; + if rx_packets > QUEUE_SIZE_NET { + self.rx + .queue_evt + .write(1) + .with_context(|| "Failed to trigger rx queue event".to_string())?; + break; + } let elem = queue .vring .pop_avail(&self.mem_space, self.driver_features) @@ -844,7 +853,7 @@ impl NetIoHandler { fn handle_tx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); - + let mut tx_packets = 0; loop { let elem = queue .vring @@ -853,6 +862,14 @@ impl NetIoHandler { if elem.desc_num == 0 { break; } + tx_packets += 1; + if tx_packets >= QUEUE_SIZE_NET { + self.tx + .queue_evt + .write(1) + .with_context(|| "Failed to trigger tx queue event".to_string())?; + break; + } let mut iovecs = Vec::new(); for elem_iov in elem.out_iovec.iter() { let host_addr = queue -- Gitee From 74b7f111187e4fc4793a2d822db208381b6960eb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 8 Dec 2022 18:43:18 +0800 Subject: [PATCH 0576/2187] virtio-net: optimize some code 1. optimize the fucntion body of build_device_config_space(). 2. delete unused function set_mac(). Signed-off-by: Yan Wang --- machine_manager/src/config/network.rs | 6 ------ virtio/src/net.rs | 7 ++----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 63538c9f5..268f390ac 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -100,12 +100,6 @@ pub struct NetworkInterfaceConfig { pub socket_path: Option, } -impl NetworkInterfaceConfig { - pub fn set_mac(&mut self, mac_addr: String) { - self.mac = Some(mac_addr); - } -} - impl Default for NetworkInterfaceConfig { fn default() -> Self { NetworkInterfaceConfig { diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 3d0b26dcd..429b3821c 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1228,19 +1228,16 @@ impl Net { /// * `device_config` - Virtio net configurations. /// * `mac` - Mac address configured by user. pub fn build_device_config_space(device_config: &mut VirtioNetConfig, mac: &str) -> u64 { - let mut config_features = 0_u64; let mut bytes = [0_u8; 6]; for (i, s) in mac.split(':').collect::>().iter().enumerate() { bytes[i] = if let Ok(v) = u8::from_str_radix(s, 16) { v } else { - return config_features; + return 0_u64; }; } device_config.mac.copy_from_slice(&bytes); - config_features |= 1 << VIRTIO_NET_F_MAC; - - config_features + 1 << VIRTIO_NET_F_MAC } /// Mark the mac table used or free. -- Gitee From c38e8e58dff8b821a7d3eadd551dfc904288a6ce Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 9 Dec 2022 15:12:20 +0800 Subject: [PATCH 0577/2187] Fix: Delete unnecessary #[allow(dead_code)] Some unused code uses allow(dead_code), which hides some bugs. It's advised to delete the code directly. Some logic problems are found during this process, which wil be tracked and resolved by other issues. Signed-off-by: Mingwang Li --- acpi/src/aml_compiler.rs | 39 ++----------------- acpi/src/lib.rs | 3 -- address_space/src/listener.rs | 3 -- boot_loader/src/x86_64/mod.rs | 1 - boot_loader/src/x86_64/standard_boot/elf.rs | 4 -- cpu/src/aarch64/mod.rs | 7 +--- .../src/interrupt_controller/aarch64/state.rs | 5 +-- devices/src/legacy/mod.rs | 7 +--- devices/src/legacy/pflash.rs | 3 -- devices/src/legacy/ramfb.rs | 1 + devices/src/legacy/rtc.rs | 5 --- machine/src/standard_vm/aarch64/mod.rs | 4 -- machine/src/standard_vm/mod.rs | 1 - migration/src/protocol.rs | 8 ---- pci/src/host.rs | 6 --- pci/src/root_port.rs | 1 - util/src/unix.rs | 1 - virtio/src/balloon.rs | 3 +- virtio/src/lib.rs | 1 - virtio/src/vhost/user/block.rs | 7 ---- virtio/src/vhost/user/client.rs | 20 ---------- virtio/src/vhost/user/message.rs | 2 - 22 files changed, 8 insertions(+), 124 deletions(-) diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index 3fa4fa00f..b07a0ce5b 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -377,14 +377,12 @@ impl AmlBuilder for AmlBuffer { /// Package contains an array of other objects. pub struct AmlPackage { - elem_count: u8, buf: Vec, } impl AmlPackage { pub fn new(elem_count: u8) -> AmlPackage { AmlPackage { - elem_count, buf: vec![elem_count], } } @@ -407,14 +405,12 @@ impl AmlScopeBuilder for AmlPackage { /// Variable-sized Package. pub struct AmlVarPackage { - elem_count: u8, buf: Vec, } impl AmlVarPackage { pub fn new(elem_count: u8) -> AmlVarPackage { AmlVarPackage { - elem_count, buf: vec![elem_count], } } @@ -533,14 +529,6 @@ pub enum AmlFieldUpdateRule { /// Field represents several bits in Operation Field. pub struct AmlField { - /// The name of corresponding OperationRegion. - name: String, - /// The access type of this Field. - access_type: AmlFieldAccessType, - /// Global lock is to be used or not when accessing this field. - lock_rule: AmlFieldLockRule, - /// Unmodified bits of a field are treated as Ones/Zeros/Preserve. - update_rule: AmlFieldUpdateRule, /// Field Unit list. buf: Vec, } @@ -557,13 +545,7 @@ impl AmlField { bytes.extend(build_name_string(name)); bytes.push(flag); - AmlField { - name: name.to_string(), - access_type: acc_ty, - lock_rule: lock_r, - update_rule: update_r, - buf: bytes, - } + AmlField { buf: bytes } } } @@ -613,8 +595,6 @@ impl AmlBuilder for AmlFieldUnit { /// Open a named Scope, can refer any scope within the namespace. pub struct AmlScope { - /// The name of scope. - name: String, /// Contains objects created inside the scope, which are encodes to bytes. buf: Vec, } @@ -622,7 +602,6 @@ pub struct AmlScope { impl AmlScope { pub fn new(name: &str) -> AmlScope { AmlScope { - name: name.to_string(), buf: build_name_string(name), } } @@ -650,14 +629,12 @@ impl AmlScopeBuilder for AmlScope { /// Device object that represents a processor, a device, etc. pub struct AmlDevice { - name: String, buf: Vec, } impl AmlDevice { pub fn new(name: &str) -> AmlDevice { AmlDevice { - name: name.to_string(), buf: build_name_string(name), } } @@ -680,12 +657,6 @@ impl AmlScopeBuilder for AmlDevice { /// Method definition. pub struct AmlMethod { - /// The name of this method. - name: String, - /// Count of Arguments. default value is zero. - args_count: u8, - /// Whether this method is Serialized or not. - serialized: bool, /// The body of this method, which has been converted to byte stream. buf: Vec, } @@ -709,12 +680,7 @@ impl AmlMethod { let mut bytes = build_name_string(name); bytes.push(flag); - AmlMethod { - name: name.to_string(), - args_count, - serialized, - buf: bytes, - } + AmlMethod { buf: bytes } } } @@ -1203,6 +1169,7 @@ pub struct AmlRelease { mutex: Vec, } +#[cfg(test)] impl AmlRelease { fn new(mtx: T) -> AmlRelease { AmlRelease { diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index efbe78bcc..7894ac054 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -11,12 +11,9 @@ // See the Mulan PSL v2 for more details. mod acpi_device; -#[allow(dead_code)] pub mod acpi_table; -#[allow(dead_code)] pub(crate) mod aml_compiler; pub mod error; -#[allow(dead_code)] mod table_loader; pub use acpi_device::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 408708981..2b924d0bd 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -66,7 +66,6 @@ pub trait Listener: Send + Sync { } /// Records information that manage the slot resource and current usage. -#[allow(dead_code)] #[derive(Default, Copy, Clone)] struct MemSlot { /// Index of a memory slot. @@ -78,8 +77,6 @@ struct MemSlot { size: u64, /// Host address. host_addr: u64, - /// Flag. - flag: u32, } /// Kvm memory listener. diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs index 2de509722..5490b178f 100644 --- a/boot_loader/src/x86_64/mod.rs +++ b/boot_loader/src/x86_64/mod.rs @@ -54,7 +54,6 @@ mod bootparam; mod direct_boot; -#[allow(dead_code)] mod standard_boot; use std::path::PathBuf; diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 61abe3ad0..8d2e942f0 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -22,8 +22,6 @@ use util::num_ops::round_up; use anyhow::{anyhow, bail, Context, Result}; const EI_MAG0: usize = 0; -const EI_MAG1: usize = 1; -const EI_MAG2: usize = 2; const EI_MAG3: usize = 3; const EI_CLASS: usize = 4; const EI_DATA: usize = 5; @@ -33,11 +31,9 @@ const ELFMAG1: u8 = b'E'; const ELFMAG2: u8 = b'L'; const ELFMAG3: u8 = b'F'; -const ELFCLASS32: u8 = 1; const ELFCLASS64: u8 = 2; const ELFDATA2LSB: u8 = 1; -const ELFDATA2MSB: u8 = 2; const PT_LOAD: u32 = 1; const PT_NOTE: u32 = 4; diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 678732b15..502abad84 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -72,13 +72,8 @@ pub struct ArmCPUBootConfig { pub boot_pc: u64, } -#[allow(dead_code)] #[derive(Default, Copy, Clone, Debug)] -pub struct ArmCPUTopology { - threads: u8, - cores: u8, - clusters: u8, -} +pub struct ArmCPUTopology {} impl ArmCPUTopology { pub fn new() -> Self { diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index b26e8c0e4..546e1dd1f 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -30,7 +30,6 @@ const GICD_CTLR: u64 = 0x0000; const GICD_STATUSR: u64 = 0x0010; const GICD_IGROUPR: u64 = 0x0080; const GICD_ISENABLER: u64 = 0x0100; -const GICD_ICENABLER: u64 = 0x0180; const GICD_ISPENDR: u64 = 0x0200; const GICD_ISACTIVER: u64 = 0x0300; const GICD_IPRIORITYR: u64 = 0x0400; @@ -47,18 +46,17 @@ const GICR_STATUSR: u64 = 0x0010; const GICR_WAKER: u64 = 0x0014; const GICR_PROPBASER: u64 = 0x0070; const GICR_PENDBASER: u64 = 0x0078; +const NR_GICR_IPRIORITYR: usize = 8; /// SGI and PPI Redistributor registers, offsets from RD_base const GICR_IGROUPR0: u64 = 0x1_0080; const GICR_ISENABLER0: u64 = 0x1_0100; -const GICR_ICENABLER0: u64 = 0x1_0180; const GICR_ISPENDR0: u64 = 0x1_0200; const GICR_ICPENDR0: u64 = 0x1_0280; const GICR_ISACTIVER0: u64 = 0x1_0300; const GICR_ICACTIVER0: u64 = 0x1_0380; const GICR_IPRIORITYR: u64 = 0x1_0400; const GICR_ICFGR1: u64 = 0x1_0C04; -const NR_GICR_IPRIORITYR: usize = 8; /// GIC CPU interface registers const ICC_PMR_EL1: u64 = 0xc230; @@ -87,7 +85,6 @@ const GITS_CBASER: u32 = 0x0080; const GITS_CWRITER: u32 = 0x0088; const GITS_CREADR: u32 = 0x0090; const GITS_BASER: u32 = 0x0100; -const NR_GITS_BASER: usize = 8; /// The status of GICv3 redistributor. #[repr(C)] diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index f893a21f4..5288c842e 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -27,19 +27,14 @@ mod chardev; pub mod error; -#[allow(dead_code)] mod fwcfg; -#[allow(dead_code)] mod pflash; -#[allow(dead_code)] #[cfg(target_arch = "aarch64")] mod pl011; #[cfg(target_arch = "aarch64")] mod pl031; -#[allow(dead_code)] -#[cfg(not(target_env = "musl"))] +#[cfg(all(not(target_env = "musl"), target_arch = "aarch64"))] mod ramfb; -#[allow(dead_code)] #[cfg(target_arch = "x86_64")] mod rtc; mod serial; diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index d0ded2a27..d03ff952c 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -24,8 +24,6 @@ use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; pub struct PFlash { /// Has backend file or not. has_backend: bool, - /// Number of blocks. - blk_num: u32, /// Length of block. block_len: u32, /// The width of PFlash array for vm. @@ -180,7 +178,6 @@ impl PFlash { bank_width, // device id for Intel PFlash. ident: [0x89, 0x18, 0x00, 0x00], - blk_num: blocks_per_device, device_width, max_device_width: device_width, write_cycle: 0, diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index edcf1fb13..6abe6e917 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -29,6 +29,7 @@ const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; const HEIGHT_MAX: u32 = 12_000; +#[allow(dead_code)] #[repr(packed)] #[derive(ByteCode, Clone, Copy)] struct RamfbCfg { diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index b6c75d7ac..8c9c891ec 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -27,16 +27,11 @@ use util::time::{mktime64, NANOSECONDS_PER_SECOND}; /// IO port of RTC device to select Register to read/write. pub const RTC_PORT_INDEX: u64 = 0x70; -/// IO port of RTC device to read/write data from selected register. -pub const RTC_PORT_DATA: u64 = 0x71; /// Index of register of time in RTC static RAM. const RTC_SECONDS: u8 = 0x00; -const RTC_SECONDS_ALARM: u8 = 0x01; const RTC_MINUTES: u8 = 0x02; -const RTC_MINUTES_ALARM: u8 = 0x03; const RTC_HOURS: u8 = 0x04; -const RTC_HOURS_ALARM: u8 = 0x05; const RTC_DAY_OF_WEEK: u8 = 0x06; const RTC_DAY_OF_MONTH: u8 = 0x07; const RTC_MONTH: u8 = 0x08; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 1eaedfc21..d5441f15a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -78,7 +78,6 @@ use virtio::ScsiCntlr::ScsiCntlrMap; pub enum LayoutEntryType { Flash = 0, GicDist, - GicCpu, GicIts, GicRedist, Uart, @@ -87,7 +86,6 @@ pub enum LayoutEntryType { Mmio, PcieMmio, PciePio, - PcieEcam, Mem, HighGicRedist, HighPcieEcam, @@ -98,7 +96,6 @@ pub enum LayoutEntryType { pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0, 0x0800_0000), // Flash (0x0800_0000, 0x0001_0000), // GicDist - (0x0801_0000, 0x0001_0000), // GicCpu (0x0808_0000, 0x0002_0000), // GicIts (0x080A_0000, 0x00F6_0000), // GicRedist (max 123 redistributors) (0x0900_0000, 0x0000_1000), // Uart @@ -107,7 +104,6 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0x0A00_0000, 0x0000_0200), // Mmio (0x1000_0000, 0x2EFF_0000), // PcieMmio (0x3EFF_0000, 0x0001_0000), // PciePio - (0x3F00_0000, 0x0100_0000), // PcieEcam (0x4000_0000, 0x80_0000_0000), // Mem (512 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) (513 << 30, 0x1000_0000), // HighPcieEcam diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 759bea05c..d06c40c90 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[allow(dead_code)] #[cfg(target_arch = "aarch64")] mod aarch64; #[cfg(target_arch = "x86_64")] diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 5d435825e..7bbc87403 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -349,18 +349,12 @@ fn cpu_model() -> [u8; 16] { pub struct MigrationHeader { /// Magic number for migration file/stream. magic_num: [u8; 16], - /// Current version of migration. - #[allow(dead_code)] - current_version: u32, /// Compatible version of migration. compat_version: u32, /// Arch identifier. arch: [u8; 8], /// Endianness of byte order. byte_order: EndianType, - /// The type of hypervisor. - #[allow(dead_code)] - hypervisor_type: [u8; 8], /// The version of hypervisor. hypervisor_version: u32, /// The type of CPU model. @@ -380,11 +374,9 @@ impl Default for MigrationHeader { fn default() -> Self { MigrationHeader { magic_num: MAGIC_NUMBER, - current_version: CURRENT_VERSION, compat_version: COMPAT_VERSION, format: FileFormat::Device, byte_order: EndianType::Little, - hypervisor_type: [b'k', b'v', b'm', b'0', b'0', b'0', b'0', b'0'], hypervisor_version: Kvm::new().unwrap().get_api_version() as u32, #[cfg(target_arch = "x86_64")] cpu_model: cpu_model(), diff --git a/pci/src/host.rs b/pci/src/host.rs index fb42fc28d..f244411d8 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -42,11 +42,8 @@ const PIO_OFFSET_MASK: u32 = 0xff; const CONFIG_BUS_MASK: u32 = 0xff; const CONFIG_DEVFN_MASK: u32 = 0xff; -#[allow(dead_code)] const ECAM_BUS_SHIFT: u32 = 20; -#[allow(dead_code)] const ECAM_DEVFN_SHIFT: u32 = 12; -#[allow(dead_code)] const ECAM_OFFSET_MASK: u64 = 0xfff; #[derive(Clone)] @@ -121,7 +118,6 @@ impl PciHost { /// # Arguments /// /// * `host_bridge` - Host brdige device. - #[allow(dead_code)] pub fn build_mmconfig_ops(host_bridge: Arc>) -> RegionOps { let cloned_hb = host_bridge.clone(); let read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { @@ -141,7 +137,6 @@ impl PciHost { /// # Arguments /// /// * `host_bridge` - Host brdige device. - #[allow(dead_code)] #[cfg(target_arch = "x86_64")] pub fn build_pio_addr_ops(host_bridge: Arc>) -> RegionOps { let cloned_hb = host_bridge.clone(); @@ -171,7 +166,6 @@ impl PciHost { /// # Arguments /// /// * `host_bridge` - Host brdige device. - #[allow(dead_code)] #[cfg(target_arch = "x86_64")] pub fn build_pio_data_ops(host_bridge: Arc>) -> RegionOps { let cloned_hb = host_bridge.clone(); diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index fa22c879d..f797676ec 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -85,7 +85,6 @@ impl RootPort { /// * `devfn` - Device number << 3 | Function number. /// * `port_num` - Root port number. /// * `parent_bus` - Weak reference to the parent bus. - #[allow(dead_code)] pub fn new( name: String, devfn: u8, diff --git a/util/src/unix.rs b/util/src/unix.rs index e2f1659a9..2ce776c1e 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -158,7 +158,6 @@ impl Clone for UnixSock { } } -#[allow(dead_code)] impl UnixSock { pub fn new(path: &str) -> Self { UnixSock { diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index ad2a05431..947e5bb43 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -71,12 +71,11 @@ struct GuestIovec { /// Balloon configuration, which would be used to transport data between `Guest` and `Host`. #[derive(Copy, Clone, Default)] +#[allow(dead_code)] struct VirtioBalloonConfig { /// The target page numbers of balloon device. - #[allow(dead_code)] pub num_pages: u32, /// Number of pages we've actually got in balloon device. - #[allow(dead_code)] pub actual: u32, } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 5ebdaaba9..bde636ab9 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -36,7 +36,6 @@ mod rng; mod scsi; pub mod vhost; mod virtio_mmio; -#[allow(dead_code)] mod virtio_pci; mod virtqueue; pub use anyhow::Result; diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 8f9939fb7..b8ce57350 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -39,7 +39,6 @@ use crate::{ /// Size of each virtqueue. const QUEUE_SIZE_BLK: u16 = 256; -#[allow(dead_code)] pub struct Block { /// Configuration of the block device. blk_cfg: BlkDevConfig, @@ -51,10 +50,6 @@ pub struct Block { client: Option>>, /// The notifier events from host. call_events: Vec, - /// Eventfd used to update the config space. - update_evt: EventFd, - /// Eventfd used to deactivate device. - deactivate_evt: EventFd, } impl Block { @@ -62,8 +57,6 @@ impl Block { Block { blk_cfg: cfg.clone(), state: BlockState::default(), - update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), mem_space: mem_space.clone(), client: None, call_events: Vec::::new(), diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index ab059be9d..56123ddd1 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -50,7 +50,6 @@ struct ClientInternal { max_queue_num: u64, } -#[allow(dead_code)] impl ClientInternal { fn new(sock: VhostUserSock, max_queue_num: u64) -> Self { ClientInternal { @@ -193,7 +192,6 @@ struct VhostUserMemInfo { enabled: bool, } -#[allow(dead_code)] impl VhostUserMemInfo { fn new() -> Self { VhostUserMemInfo { @@ -329,7 +327,6 @@ pub struct VhostUserClient { reconnecting: bool, } -#[allow(dead_code)] impl VhostUserClient { pub fn new(mem_space: &Arc, path: &str, max_queue_num: u64) -> Result { let mut sock = VhostUserSock::new(path); @@ -499,23 +496,6 @@ impl VhostUserClient { Ok(features) } - /// Send set protocol features request to vhost. - pub fn set_protocol_features(&self, features: u64) -> Result<()> { - let client = self.client.lock().unwrap(); - let hdr = VhostUserMsgHdr::new( - VhostUserMsgReq::SetProtocolFeatures as u32, - 0, - size_of::() as u32, - ); - let payload_opt: Option<&[u8]> = None; - client - .sock - .send_msg(Some(&hdr), Some(&features), payload_opt, &[]) - .with_context(|| "Failed to send msg for setting protocols features")?; - - Ok(()) - } - /// Get virtio blk config from vhost. pub fn get_virtio_blk_config(&self) -> Result { let client = self.client.lock().unwrap(); diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 4c807f8d4..2731085ed 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -22,7 +22,6 @@ use anyhow::Result; /// Type of requests sending from vhost user device to the userspace process. #[repr(u32)] -#[allow(dead_code)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum VhostUserMsgReq { None = 0, @@ -127,7 +126,6 @@ pub struct VhostUserMsgHdr { pub size: u32, } -#[allow(dead_code)] impl VhostUserMsgHdr { /// Create a new instance of `VhostUserMsgHeader`. pub fn new(request: u32, flags: u32, size: u32) -> Self { -- Gitee From 250c9706bc284d02546bf565808925ec7963c2e5 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 9 Dec 2022 14:54:01 +0800 Subject: [PATCH 0578/2187] virtiofs: introduce FUSE_IOCTL to virtiofsd Normally the VM should not use ioctl to modify files, but it can be useful in some cases. For example: to modify inode attrs, it is required for per inode DAX. We do not currently support these features, so ioctl is not currently required. Reserve this interface and implement it when needed in the future. Signed-off-by: Li HuaChao --- vhost_user_fs/src/fuse_proc.rs | 16 ++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 3 +++ 2 files changed, 19 insertions(+) diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index d13389f70..aea53ab00 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -1739,3 +1739,19 @@ pub fn do_fuse_lseek( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +/// Process the fuse message of FUSE_IOCTL. +/// Currently not supported, and ENOSYS is directly returned. +/// Normally the VM should not use ioctl to modify files, but it can be useful +/// in some cases. For example: to modify inode attrs, witch is required for per +/// inode DAX. We set aside the ioctl interface, and to implement it in the future +/// if needed. +pub fn do_fuse_ioctl( + sys_mem: &Arc, + _fs: Arc>, + _reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + reply_fuse_msg(writer, sys_mem, in_header, libc::ENOSYS, None, 0_usize) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 8543a5cf7..8d1e33624 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -150,6 +150,9 @@ impl FuseReq { FUSE_LSEEK => { do_fuse_lseek(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_IOCTL => { + do_fuse_ioctl(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From a1ddee9444f46e522a42695c2ad1be8b7a7c7e99 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Wed, 7 Dec 2022 17:43:39 +0800 Subject: [PATCH 0579/2187] vhost-user-blk-pci: add hotplug feature Support hotplug vhost-user-blk-pci device now. Signed-off-by: Xiaowei Xue --- machine/src/standard_vm/mod.rs | 48 ++++++++++++++++++++++++++- machine_manager/src/qmp/qmp_schema.rs | 2 ++ virtio/src/vhost/user/block.rs | 12 ++++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 759bea05c..a70efbb42 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -839,6 +839,43 @@ impl StdMachine { Ok(()) } + fn plug_vhost_user_blk_pci( + &mut self, + pci_bdf: &PciBdf, + args: &qmp_schema::DeviceAddArgument, + ) -> Result<()> { + let multifunction = args.multifunction.unwrap_or(false); + let vm_config = self.get_vm_config().lock().unwrap(); + let chardev = if let Some(dev) = &args.chardev { + dev + } else { + bail!("Chardev not set"); + }; + let socket_path = self + .get_socket_path(&vm_config, chardev.to_string()) + .with_context(|| "Failed to get socket path")?; + let nr_cpus = vm_config.machine_config.nr_cpus; + let dev = BlkDevConfig { + id: args.id.clone(), + queues: args.queues.unwrap_or_else(|| { + VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) + }), + boot_index: args.boot_index, + chardev: Some(chardev.to_string()), + socket_path, + ..BlkDevConfig::default() + }; + + dev.check()?; + drop(vm_config); + + let blk = Arc::new(Mutex::new(VhostUser::Block::new(&dev, self.get_sys_mem()))); + self.add_virtio_pci_device(&args.id, pci_bdf, blk, multifunction, true) + .with_context(|| "Failed to add vhost user blk pci device")?; + + Ok(()) + } + fn get_socket_path(&self, vm_config: &VmConfig, chardev: String) -> Result> { let char_dev = if let Some(char_dev) = vm_config.chardev.get(&chardev) { char_dev @@ -1098,7 +1135,16 @@ impl DeviceInterface for StdMachine { ); } } - + "vhost-user-blk-pci" => { + if let Err(e) = self.plug_vhost_user_blk_pci(&pci_bdf, args.as_ref()) { + error!("{:?}", e); + let err_str = format!("Failed to add vhost user blk pci: {}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(err_str), + None, + ); + } + } "virtio-net-pci" => { if let Err(e) = self.plug_virtio_pci_net(&pci_bdf, args.as_ref()) { error!("{:?}", e); diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index c56b1bbea..bd617ffec 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -498,6 +498,8 @@ pub struct device_add { pub mac: Option, #[serde(rename = "netdev")] pub netdev: Option, + #[serde(rename = "chardev")] + pub chardev: Option, #[serde(rename = "disable-modern")] pub disable_modern: Option, #[serde(rename = "mq")] diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 8f9939fb7..22472272a 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -291,7 +291,7 @@ impl VirtioDevice for Block { Ok(()) } - /// activate device + /// Activate device. fn activate( &mut self, _mem_space: Arc, @@ -310,17 +310,27 @@ impl VirtioDevice for Block { Ok(()) } + /// Deactivate device. fn deactivate(&mut self) -> Result<()> { self.call_events.clear(); self.clean_up()?; self.realize() } + /// Reset device. fn reset(&mut self) -> Result<()> { self.clean_up()?; self.realize() } + /// Unrealize device. + fn unrealize(&mut self) -> Result<()> { + self.delete_event()?; + self.call_events.clear(); + self.client = None; + Ok(()) + } + /// Set guest notifiers for notifying the guest. fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { for fd in queue_evts.iter() { -- Gitee From 6cd8306f271fbf4693fefb6b0b2b4974e329f2ea Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Fri, 9 Dec 2022 20:43:29 +0800 Subject: [PATCH 0580/2187] Update the kernel and rootfs image download link in the README file. Signed-off-by: Yan Wen --- README.ch.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.ch.md b/README.ch.md index 5c02ea369..1837a946e 100644 --- a/README.ch.md +++ b/README.ch.md @@ -26,7 +26,7 @@ $ make build 可以通过以下链接获取我们准备好的linux内核镜像和rootfs镜像: -https://repo.openeuler.org/openEuler-21.03/stratovirt_img/ +https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/ 启动标准机型的虚拟机需要指定遵循UEFI的edk2固件文件。 diff --git a/README.md b/README.md index a41613ade..1ab8e86a9 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ To run StratoVirt quickly, requires You can get kernel and rootfs image from the following link: -https://repo.openeuler.org/openEuler-21.03/stratovirt_img/ +https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/ For standard VM, firmware file of EDK2 which follows UEFI is required. -- Gitee From 1bc90ae6b414e6621185fa63f5e9735204da06e9 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Sat, 10 Dec 2022 14:16:12 +0800 Subject: [PATCH 0581/2187] bugfix:Solve the problem of abnormal heat migration The configuration is not updated after the device is hot swapped Signed-off-by: @jiewangqun --- Cargo.lock | 1 + machine/src/lib.rs | 2 +- machine/src/micro_vm/mod.rs | 8 ++--- machine/src/standard_vm/aarch64/mod.rs | 10 +++--- machine/src/standard_vm/mod.rs | 24 ++++++++----- machine/src/standard_vm/x86_64/mod.rs | 10 +++--- machine_manager/Cargo.toml | 1 + machine_manager/src/config/devices.rs | 13 +++++++ machine_manager/src/config/drive.rs | 49 +++++++++++++++++++++++++- machine_manager/src/config/network.rs | 40 +++++++++++++++++++++ migration/src/manager.rs | 6 ++-- migration/src/migration.rs | 24 ++++++++++--- 12 files changed, 156 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 291e2de38..4592d34f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -458,6 +458,7 @@ dependencies = [ "libc", "log", "once_cell", + "regex", "serde", "serde_json", "strum", diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d80560e3b..9119e9f57 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -298,7 +298,7 @@ pub trait MachineOps { fn get_sys_mem(&mut self) -> &Arc; - fn get_vm_config(&self) -> &Mutex; + fn get_vm_config(&self) -> Arc>; fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)>; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 40c6f0019..133b78251 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -180,7 +180,7 @@ pub struct LightMachine { // VM power button, handle VM `Shutdown` event. power_button: EventFd, // All configuration information of virtual machine. - vm_config: Mutex, + vm_config: Arc>, // Drive backend files. drive_files: Arc>>, } @@ -238,7 +238,7 @@ impl LightMachine { boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, power_button, - vm_config: Mutex::new(vm_config.clone()), + vm_config: Arc::new(Mutex::new(vm_config.clone())), drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) } @@ -617,8 +617,8 @@ impl MachineOps for LightMachine { &self.sys_mem } - fn get_vm_config(&self) -> &Mutex { - &self.vm_config + fn get_vm_config(&self) -> Arc> { + self.vm_config.clone() } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index d5441f15a..9a588181d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -132,7 +132,7 @@ pub struct StdMachine { /// VM power button, handle VM `Shutdown` event. power_button: EventFd, /// All configuration information of virtual machine. - vm_config: Mutex, + vm_config: Arc>, /// Reset request, handle VM `Reset` event. reset_req: EventFd, /// Device Tree Blob. @@ -192,7 +192,7 @@ impl StdMachine { power_button: EventFd::new(libc::EFD_NONBLOCK).with_context(|| { anyhow!(MachineError::InitEventFdErr("power_button".to_string())) })?, - vm_config: Mutex::new(vm_config.clone()), + vm_config: Arc::new(Mutex::new(vm_config.clone())), reset_req: EventFd::new(libc::EFD_NONBLOCK) .with_context(|| anyhow!(MachineError::InitEventFdErr("reset_req".to_string())))?, dtb_vec: Vec::new(), @@ -558,7 +558,7 @@ impl MachineOps for StdMachine { locked_vm.register_power_event(&locked_vm.power_button)?; - MigrationManager::register_vm_config(vm_config); + MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); @@ -615,8 +615,8 @@ impl MachineOps for StdMachine { &self.sys_mem } - fn get_vm_config(&self) -> &Mutex { - &self.vm_config + fn get_vm_config(&self) -> Arc> { + self.vm_config.clone() } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d06c40c90..eb8c05685 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -746,9 +746,10 @@ impl StdMachine { } else { bail!("Drive not set"); }; - - let nr_cpus = self.get_vm_config().lock().unwrap().machine_config.nr_cpus; - let blk = if let Some(conf) = self.get_vm_config().lock().unwrap().drives.get(drive) { + let vm_config = self.get_vm_config(); + let mut locked_vmconfig = vm_config.lock().unwrap(); + let nr_cpus = locked_vmconfig.machine_config.nr_cpus; + let blk = if let Some(conf) = locked_vmconfig.drives.get(drive) { let dev = BlkDevConfig { id: args.id.clone(), path_on_host: conf.path_on_host.clone(), @@ -770,6 +771,8 @@ impl StdMachine { } else { bail!("Drive not found"); }; + locked_vmconfig.add_blk_device_config(args); + drop(locked_vmconfig); if let Some(bootindex) = args.boot_index { self.check_bootindex(bootindex) @@ -878,13 +881,13 @@ impl StdMachine { } else { bail!("Netdev not set"); }; - - let vm_config = self.get_vm_config().lock().unwrap(); - let dev = if let Some(conf) = vm_config.netdevs.get(netdev) { + let vm_config = self.get_vm_config(); + let mut locked_vmconfig = vm_config.lock().unwrap(); + let dev = if let Some(conf) = locked_vmconfig.netdevs.get(netdev) { let mut socket_path: Option = None; if let Some(chardev) = &conf.chardev { socket_path = self - .get_socket_path(&vm_config, (&chardev).to_string()) + .get_socket_path(&locked_vmconfig, (&chardev).to_string()) .with_context(|| "Failed to get socket path")?; } let dev = NetworkInterfaceConfig { @@ -904,7 +907,8 @@ impl StdMachine { } else { bail!("Netdev not found"); }; - drop(vm_config); + locked_vmconfig.add_net_device_config(args); + drop(locked_vmconfig); if dev.vhost_type.is_some() { let mut need_irqfd = false; @@ -1171,6 +1175,10 @@ impl DeviceInterface for StdMachine { let dev_id = locked_dev.name(); drop(locked_pci_host); self.del_bootindex_devices(&dev_id); + let vm_config = self.get_vm_config(); + let mut locked_config = vm_config.lock().unwrap(); + locked_config.del_device_by_id(device_id); + drop(locked_config); Response::create_empty_response() } Err(e) => Response::create_error_response( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 4c6a86c07..300af29cb 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -116,7 +116,7 @@ pub struct StdMachine { /// VM power button, handle VM `Shutdown` event. power_button: EventFd, /// All configuration information of virtual machine. - vm_config: Mutex, + vm_config: Arc>, /// List of guest NUMA nodes information. numa_nodes: Option, /// List contains the boot order of boot devices. @@ -175,7 +175,7 @@ impl StdMachine { power_button: EventFd::new(libc::EFD_NONBLOCK).with_context(|| { anyhow!(MachineError::InitEventFdErr("power_button".to_string())) })?, - vm_config: Mutex::new(vm_config.clone()), + vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, @@ -479,7 +479,7 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; - MigrationManager::register_vm_config(vm_config); + MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); MigrationManager::register_kvm_instance( vm_state::KvmDeviceState::descriptor(), @@ -560,8 +560,8 @@ impl MachineOps for StdMachine { &self.sys_mem } - fn get_vm_config(&self) -> &Mutex { - &self.vm_config + fn get_vm_config(&self) -> Arc> { + self.vm_config.clone() } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 21d2791d2..f68de2df7 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -8,6 +8,7 @@ license = "Mulan PSL v2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1" log = "0.4" libc = "0.2" serde_json = "1.0" diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index 68b7959b0..8ec850212 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -12,6 +12,7 @@ use super::{CmdParser, VmConfig}; use anyhow::Result; +use regex::Regex; impl VmConfig { pub fn add_device(&mut self, device_config: &str) -> Result<()> { @@ -25,6 +26,18 @@ impl VmConfig { Ok(()) } + + pub fn del_device_by_id(&mut self, dev_id: String) { + let rex = format!("id={}(,|$)", dev_id); + let re = Regex::new(rex.as_str()).unwrap(); + + for (index, (_, dev_info)) in self.devices.iter().enumerate() { + if re.is_match(dev_info.as_str()) { + self.devices.remove(index); + return; + } + } + } } pub fn parse_device_id(device_config: &str) -> Result { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 12ae64ba7..08ded7ab8 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -24,7 +24,7 @@ use crate::config::{ get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; - +use crate::qmp::qmp_schema; const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; @@ -494,6 +494,53 @@ impl VmConfig { Ok(()) } + /// Add 'pci blk devices' to `VmConfig devices`. + pub fn add_blk_device_config(&mut self, args: &qmp_schema::DeviceAddArgument) { + let mut device_info = args.driver.clone(); + + device_info = format!("{},id={}", device_info, args.id); + + if let Some(drive) = &args.drive { + device_info = format!("{},drive={}", device_info, drive); + } + + if let Some(serial_num) = &args.serial_num { + device_info = format!("{},serial={}", device_info, serial_num); + } + + if let Some(addr) = &args.addr { + device_info = format!("{},addr={}", device_info, addr); + } + if let Some(bus) = &args.bus { + device_info = format!("{},bus={}", device_info, bus); + } + + if args.multifunction.is_some() { + if args.multifunction.unwrap() { + device_info = format!("{},multifunction=on", device_info); + } else { + device_info = format!("{},multifunction=off", device_info); + } + } + + if let Some(iothread) = &args.iothread { + device_info = format!("{},iothread={}", device_info, iothread); + } + + if let Some(mq) = &args.mq { + device_info = format!("{},mq={}", device_info, mq); + } + + if let Some(queues) = &args.queues { + device_info = format!("{},num-queues={}", device_info, queues); + } + + if let Some(boot_index) = &args.boot_index { + device_info = format!("{},bootindex={}", device_info, boot_index); + } + + self.devices.push((args.driver.clone(), device_info)); + } /// Delete drive config in vm config by id. /// /// # Arguments diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 268f390ac..22535c236 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -420,6 +420,46 @@ impl VmConfig { } Ok(()) } + /// Add 'net devices' to `VmConfig devices`. + pub fn add_net_device_config(&mut self, args: &qmp_schema::DeviceAddArgument) { + let mut device_info = args.driver.clone(); + + device_info = format!("{},id={}", device_info, args.id); + + if let Some(netdev) = &args.netdev { + device_info = format!("{},netdev={}", device_info, netdev); + } + + if let Some(mac) = &args.mac { + device_info = format!("{},mac={}", device_info, mac); + } + + if let Some(addr) = &args.addr { + device_info = format!("{},addr={}", device_info, addr); + } + + if let Some(bus) = &args.bus { + device_info = format!("{},bus={}", device_info, bus); + } + + if args.multifunction.is_some() { + if args.multifunction.unwrap() { + device_info = format!("{},multifunction=on", device_info); + } else { + device_info = format!("{},multifunction=off", device_info); + } + } + + if let Some(iothread) = &args.iothread { + device_info = format!("{},iothread={}", device_info, iothread); + } + + if let Some(mq) = &args.mq { + device_info = format!("{},mq={}", device_info, mq); + } + + self.devices.push((args.driver.clone(), device_info)); + } } fn check_mac_address(mac: &str) -> bool { diff --git a/migration/src/manager.rs b/migration/src/manager.rs index d28d6c4bf..d364a04b1 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -160,7 +160,7 @@ impl ByteCode for Instance {} #[derive(Default)] pub struct Vmm { /// Vm config - pub config: VmConfig, + pub config: Arc>, /// Trait to represent a Vm. pub vm: Option>>, /// Trait to represent CPU devices. @@ -231,8 +231,8 @@ impl MigrationManager { /// # Arguments /// /// * `config` - The configuration from virtual machine. - pub fn register_vm_config(config: &mut VmConfig) { - MIGRATION_MANAGER.vmm.write().unwrap().config = config.clone(); + pub fn register_vm_config(config: Arc>) { + MIGRATION_MANAGER.vmm.write().unwrap().config = config; } /// Register vm instance to vmm. diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 35a81bee2..4310225c5 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -113,7 +113,7 @@ impl MigrationManager { } else { Response::send_msg(fd, TransStatus::Error)?; return Err(anyhow!(MigrationError::MigrationStatusErr( - request.status.to_string(), + (request.status as u16).to_string(), TransStatus::Active.to_string(), ))); } @@ -127,7 +127,7 @@ impl MigrationManager { } else { Response::send_msg(fd, TransStatus::Error)?; return Err(anyhow!(MigrationError::MigrationStatusErr( - request.status.to_string(), + (request.status as u16).to_string(), TransStatus::VmConfig.to_string(), ))); } @@ -165,7 +165,14 @@ impl MigrationManager { where T: Write + Read, { - let vm_config = &MIGRATION_MANAGER.vmm.read().unwrap().config; + let vm_config = &MIGRATION_MANAGER + .vmm + .read() + .unwrap() + .config + .lock() + .unwrap() + .clone(); let config_data = serde_json::to_vec(vm_config)?; Request::send_msg(fd, TransStatus::VmConfig, config_data.len() as u64)?; fd.write_all(&config_data)?; @@ -188,7 +195,14 @@ impl MigrationManager { fd.read_exact(&mut data)?; let src_config: &VmConfig = &serde_json::from_slice(&data)?; - let dest_config: &VmConfig = &MIGRATION_MANAGER.vmm.read().unwrap().config; + let dest_config: &VmConfig = &MIGRATION_MANAGER + .vmm + .read() + .unwrap() + .config + .lock() + .unwrap() + .clone(); // Check vCPU number. Self::check_vcpu(src_config, dest_config)?; Self::check_memory(src_config, dest_config)?; @@ -511,7 +525,7 @@ impl MigrationManager { Response::send_msg(fd, TransStatus::Ok)?; } else { return Err(anyhow!(MigrationError::MigrationStatusErr( - request.status.to_string(), + (request.status as u16).to_string(), TransStatus::Complete.to_string(), ))); } -- Gitee From 3019d974e7a9e9d19ab0081e332f5a9456a32ee7 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Mon, 12 Dec 2022 14:14:50 +0800 Subject: [PATCH 0582/2187] virtio-gpu: modify class value in configuration layout Some userspace programs read the class value in device configuration layout to detemine whether the device is a virtio-gpu. Signed-off-by: Binfeng Wu --- virtio/src/virtio_pci.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 30accf20c..893edae6d 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -47,8 +47,8 @@ use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, - VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_NET, - VIRTIO_TYPE_SCSI, + VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_GPU, + VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -58,6 +58,10 @@ const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; const VIRTIO_PCI_ABI_VERSION: u8 = 1; const VIRTIO_PCI_CLASS_ID_NET: u16 = 0x0280; const VIRTIO_PCI_CLASS_ID_BLOCK: u16 = 0x0100; +#[cfg(target_arch = "aarch64")] +const VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER: u16 = 0x0380; +#[cfg(target_arch = "x86_64")] +const VIRTIO_PCI_CLASS_ID_DISPLAY_VGA: u16 = 0x0300; const VIRTIO_PCI_CLASS_ID_OTHERS: u16 = 0x00ff; const VIRTIO_PCI_CAP_COMMON_OFFSET: u32 = 0x0; @@ -131,7 +135,14 @@ fn get_virtio_class_id(device_type: u32) -> u16 { VIRTIO_TYPE_BLOCK => VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_SCSI => VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_NET => VIRTIO_PCI_CLASS_ID_NET, - _ => VIRTIO_PCI_CLASS_ID_OTHERS, + #[cfg(target_arch = "x86_64")] + VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_VGA, + #[cfg(target_arch = "aarch64")] + VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER, + _ => { + warn!("Unknown device type, please make sure it is supported."); + VIRTIO_PCI_CLASS_ID_OTHERS + } } } -- Gitee From 5674b7c7142958719a63c474bec544ef3476feb7 Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Mon, 12 Dec 2022 21:29:34 +0800 Subject: [PATCH 0583/2187] migration: Add dirty pages generated by manually marking virtio block Some devices will write date directly to VM memory, or using `AddressSpace` to copy date to VM memory. During live migration, these pages will be dirty and can not be tracked by kvm. So, we need to mark dirty page manually here. Signed-off-by: Xinle.Guo --- address_space/src/address_space.rs | 7 ++++++- address_space/src/region.rs | 14 ++++++++++---- migration/src/migration.rs | 4 ++++ virtio/src/block.rs | 14 ++++++++++---- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 9f2bd4e41..c8188c5bf 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -10,18 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{anyhow, Context, Result}; use arc_swap::ArcSwap; use std::fmt; use std::fmt::Debug; use std::io::Write; use std::sync::{Arc, Mutex}; + +use migration::{migration::Migratable, MigrationManager}; use util::byte_code::ByteCode; use crate::{ AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, RegionIoEventFd, RegionType, }; -use anyhow::{anyhow, Context, Result}; /// Contains an array of `FlatRange`. #[derive(Default, Clone, Debug)] @@ -491,6 +493,9 @@ impl AddressSpace { /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { + // Mark vmm dirty page manually if live migration is active. + MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); + let mut dst = unsafe { std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 4107e8315..49659a8bf 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -10,18 +10,21 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, warn}; +use std::fmt; +use std::fmt::Debug; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; +use migration::{migration::Migratable, MigrationManager}; + use crate::address_space::FlatView; use crate::{ AddressRange, AddressSpace, AddressSpaceError, FileBackend, GuestAddress, HostMemMapping, RegionOps, }; -use anyhow::{anyhow, bail, Context, Result}; -use log::{debug, warn}; -use std::fmt; -use std::fmt::Debug; + /// Types of Region. #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -620,6 +623,9 @@ impl Region { match self.region_type { RegionType::Ram | RegionType::RamDevice => { let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); + // Mark vmm dirty page manually if live migration is active. + MigrationManager::mark_dirty_log(host_addr + offset, count); + let slice = unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, count as usize) }; diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 35a81bee2..79f42c7d0 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -705,6 +705,10 @@ pub trait Migratable { /// * `addr` - Start address of dirty memory. /// * `len` - Length of dirty memory. fn mark_dirty_log(addr: u64, len: u64) { + if !MigrationManager::is_active() { + return; + } + let bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); for (_, map) in bitmaps.iter() { if (addr >= map.hva) && ((addr + len) <= (map.hva + map.len)) { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 63bc218c3..63906f88d 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -306,13 +306,19 @@ impl Request { req = req_raw.next.as_ref().as_ref(); } - match self.out_header.request_type { + let request_type = self.out_header.request_type; + if request_type == VIRTIO_BLK_T_IN || request_type == VIRTIO_BLK_T_GET_ID { + // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. + for iov in aiocb.iovec.iter() { + // Mark vmm dirty page manually if live migration is active. + MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); + } + } + + match request_type { VIRTIO_BLK_T_IN => { aiocb.opcode = IoCmd::Preadv; if aio_type.is_some() { - for iov in aiocb.iovec.iter() { - MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); - } (*aio) .as_mut() .rw_aio(aiocb, SECTOR_SIZE, direct) -- Gitee From 293ed41f158d085d974addfc7646881770ab5423 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 13 Dec 2022 23:22:17 +0800 Subject: [PATCH 0584/2187] Vhost-user-blk: fix usage of get_vm_config() Due to the defect of CI compile environment, it cause the patch has been merged compiled failed. For example: 1) Submit patch1 and compile succeed, it use get_vm_config(). 2) Patch2 which modified get_vm_config() submitted and merged. 3) Merge Patch1. The new code will report compiling error in Patch1's code. Signed-off-by: Yan Wang --- machine/src/standard_vm/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 13c99b738..f7484cfdd 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -847,16 +847,17 @@ impl StdMachine { args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { let multifunction = args.multifunction.unwrap_or(false); - let vm_config = self.get_vm_config().lock().unwrap(); + let vm_config = self.get_vm_config(); + let locked_vmconfig = vm_config.lock().unwrap(); let chardev = if let Some(dev) = &args.chardev { dev } else { bail!("Chardev not set"); }; let socket_path = self - .get_socket_path(&vm_config, chardev.to_string()) + .get_socket_path(&locked_vmconfig, chardev.to_string()) .with_context(|| "Failed to get socket path")?; - let nr_cpus = vm_config.machine_config.nr_cpus; + let nr_cpus = locked_vmconfig.machine_config.nr_cpus; let dev = BlkDevConfig { id: args.id.clone(), queues: args.queues.unwrap_or_else(|| { @@ -869,7 +870,7 @@ impl StdMachine { }; dev.check()?; - drop(vm_config); + drop(locked_vmconfig); let blk = Arc::new(Mutex::new(VhostUser::Block::new(&dev, self.get_sys_mem()))); self.add_virtio_pci_device(&args.id, pci_bdf, blk, multifunction, true) -- Gitee From 8653279d5ea413f6b9bd0e55002473be4ec8e023 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 13 Dec 2022 14:33:06 +0800 Subject: [PATCH 0585/2187] Fix: Each queue is assigned a deactivate eventfd. VM startup consists of two processes: EDK startup and guest kernel startup. During EDK startup, the driver enables the multi-queue of the Blk device and then destroys the multi-queue. Destroying multi-queue is an asynchronous operation and a broadcase operation. As a result, the guest kernel initializes the Blk device, and the old resoureces are not released. Some queues use the information in the EDK phase, triggering interrupt loss. Now configure a unique eventfd for each queue deactivated operation. The ioeventfd deregistration mechanism will be added later to prevent the guest kernel from using old resources. Signed-off-by: Mingwang Li --- virtio/src/block.rs | 21 +++++++++++++-------- virtio/src/virtio_pci.rs | 32 +++----------------------------- virtio/src/virtqueue/mod.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 63906f88d..fc8795d3d 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -29,7 +29,7 @@ use super::{ VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; -use crate::VirtioError; +use crate::{NotifyEventFds, VirtioError}; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; @@ -960,7 +960,7 @@ pub struct Block { /// Eventfd for config space update. update_evt: EventFd, /// Eventfd for device deactivate. - deactivate_evt: EventFd, + deactivate_evts: NotifyEventFds, /// Drive backend files. drive_files: Arc>>, } @@ -975,7 +975,7 @@ impl Default for Block { interrupt_cb: None, senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: NotifyEventFds::new(1), drive_files: Arc::new(Mutex::new(HashMap::new())), } } @@ -994,7 +994,7 @@ impl Block { interrupt_cb: None, senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: NotifyEventFds::new(0), drive_files, } } @@ -1135,11 +1135,13 @@ impl VirtioDevice for Block { ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); let mut senders = Vec::new(); + self.deactivate_evts.events.clear(); for queue in queues.iter() { let (sender, receiver) = channel(); senders.push(sender); + let eventfd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut handler = BlockIoHandler { queue: queue.clone(), queue_evt: queue_evts.remove(0), @@ -1153,12 +1155,13 @@ impl VirtioDevice for Block { driver_features: self.state.driver_features, receiver, update_evt: self.update_evt.try_clone().unwrap(), - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + deactivate_evt: eventfd.try_clone().unwrap(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), }; + self.deactivate_evts.events.push(eventfd); handler.aio = Some(handler.build_aio(self.blk_cfg.aio.as_ref())?); EventLoop::update_event( @@ -1173,9 +1176,11 @@ impl VirtioDevice for Block { } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + for evt in &self.deactivate_evts.events { + evt.write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + } + Ok(()) } fn update_config(&mut self, dev_config: Option>) -> Result<()> { diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 893edae6d..0811b87b9 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -40,8 +40,8 @@ use util::offset_of; use vmm_sys_util::eventfd::EventFd; use crate::{ - report_virtio_error, virtio_has_feature, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, - VirtioInterruptType, + report_virtio_error, virtio_has_feature, NotifyEventFds, Queue, QueueConfig, VirtioDevice, + VirtioInterrupt, VirtioInterruptType, }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, @@ -537,32 +537,6 @@ impl VirtioPciNotifyCap { } } -struct NotifyEventFds { - events: Vec, -} - -impl NotifyEventFds { - fn new(queue_num: usize) -> Self { - let mut events = Vec::new(); - for _i in 0..queue_num { - events.push(EventFd::new(libc::EFD_NONBLOCK).unwrap()); - } - - NotifyEventFds { events } - } -} - -impl Clone for NotifyEventFds { - fn clone(&self) -> NotifyEventFds { - let mut queue_evts = Vec::::new(); - for fd in self.events.iter() { - let cloned_evt_fd = fd.try_clone().unwrap(); - queue_evts.push(cloned_evt_fd); - } - NotifyEventFds { events: queue_evts } - } -} - /// The state of virtio-pci device. #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] @@ -619,7 +593,7 @@ pub struct VirtioPciDevice { common_config: Arc>, /// Primary Bus parent_bus: Weak>, - /// Eventfds used for notifying the guest. + /// Eventfds used for guest notify the Device. notify_eventfds: NotifyEventFds, /// The function for interrupt triggering interrupt_cb: Option>, diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs index 0e7981661..87b273d85 100644 --- a/virtio/src/virtqueue/mod.rs +++ b/virtio/src/virtqueue/mod.rs @@ -15,6 +15,7 @@ mod split; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Result}; use std::sync::Arc; +use vmm_sys_util::eventfd::EventFd; use crate::VirtioError; pub use split::*; @@ -204,3 +205,30 @@ impl Queue { self.vring.is_valid(sys_mem) } } + +/// Virt Queue Notify EventFds +pub struct NotifyEventFds { + pub events: Vec, +} + +impl NotifyEventFds { + pub fn new(queue_num: usize) -> Self { + let mut events = Vec::new(); + for _i in 0..queue_num { + events.push(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + } + + NotifyEventFds { events } + } +} + +impl Clone for NotifyEventFds { + fn clone(&self) -> NotifyEventFds { + let mut queue_evts = Vec::::new(); + for fd in self.events.iter() { + let cloned_evt_fd = fd.try_clone().unwrap(); + queue_evts.push(cloned_evt_fd); + } + NotifyEventFds { events: queue_evts } + } +} -- Gitee From a44d42587a32a1e99e117a7326b27d3b88332f89 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 29 Nov 2022 20:48:16 +0800 Subject: [PATCH 0586/2187] usb: remove the useless endpoint field in usb device Remove the useless endpoint field in usb device, use it directly. Signed-off-by: zhouli57 --- usb/src/keyboard.rs | 13 +++++++------ usb/src/tablet.rs | 13 +++++++------ usb/src/usb.rs | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 5a943dd94..35669bd93 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -117,7 +117,6 @@ pub struct UsbKeyboard { hid: Arc>, /// USB controller used to notify controller to transfer data. ctrl: Option>>, - endpoint: Option>>, } impl UsbKeyboard { @@ -127,7 +126,6 @@ impl UsbKeyboard { device: Arc::new(Mutex::new(UsbDevice::new())), hid: Arc::new(Mutex::new(Hid::new(HidType::Keyboard))), ctrl: None, - endpoint: None, } } @@ -148,8 +146,6 @@ impl UsbKeyboard { fn init_hid(&mut self) -> Result<()> { let mut locked_usb = self.device.lock().unwrap(); locked_usb.usb_desc = Some(DESC_KEYBOARD.clone()); - let ep = locked_usb.get_endpoint(USB_TOKEN_IN as u32, 1); - self.endpoint = Some(Arc::downgrade(&ep)); locked_usb.init_descriptor()?; Ok(()) } @@ -235,7 +231,12 @@ impl UsbDeviceOps for UsbKeyboard { self.ctrl.clone() } - fn get_endpoint(&self) -> Option>> { - self.endpoint.clone() + fn get_wakeup_endpoint(&self) -> Option>> { + let ep = self + .device + .lock() + .unwrap() + .get_endpoint(USB_TOKEN_IN as u32, 1); + Some(Arc::downgrade(&ep)) } } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 2189311b7..36fb59ce5 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -122,7 +122,6 @@ pub struct UsbTablet { hid: Arc>, /// USB controller used to notify controller to transfer data. ctrl: Option>>, - endpoint: Option>>, } impl UsbTablet { @@ -132,7 +131,6 @@ impl UsbTablet { device: Arc::new(Mutex::new(UsbDevice::new())), hid: Arc::new(Mutex::new(Hid::new(HidType::Tablet))), ctrl: None, - endpoint: None, } } @@ -153,8 +151,6 @@ impl UsbTablet { fn init_hid(&mut self) -> Result<()> { let mut locked_usb = self.device.lock().unwrap(); locked_usb.usb_desc = Some(DESC_TABLET.clone()); - let ep = locked_usb.get_endpoint(USB_TOKEN_IN as u32, 1); - self.endpoint = Some(Arc::downgrade(&ep)); locked_usb.init_descriptor()?; Ok(()) } @@ -276,7 +272,12 @@ impl UsbDeviceOps for UsbTablet { self.ctrl.clone() } - fn get_endpoint(&self) -> Option>> { - self.endpoint.clone() + fn get_wakeup_endpoint(&self) -> Option>> { + let ep = self + .device + .lock() + .unwrap() + .get_endpoint(USB_TOKEN_IN as u32, 1); + Some(Arc::downgrade(&ep)) } } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 9d0ee3652..987f8bf11 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -497,7 +497,7 @@ pub trait UsbDeviceOps: Send + Sync { fn get_controller(&self) -> Option>>; /// Get the endpoint to wakeup. - fn get_endpoint(&self) -> Option>>; + fn get_wakeup_endpoint(&self) -> Option>>; /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { @@ -669,7 +669,7 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { } } let locked_dev = dev.lock().unwrap(); - let intr = if let Some(intr) = locked_dev.get_endpoint() { + let intr = if let Some(intr) = locked_dev.get_wakeup_endpoint() { intr } else { bail!("No interrupter found"); -- Gitee From 7db5f9b5e8834c940514b07de201f77c726d0b35 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 30 Nov 2022 10:37:09 +0800 Subject: [PATCH 0587/2187] usb: remove useless setup field Remove useless setup field in usb device. Get usb device request from parameter directly. Signed-off-by: zhouli57 --- usb/src/usb.rs | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 987f8bf11..a3896d68f 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -231,12 +231,8 @@ pub struct UsbDevice { pub addr: u8, pub product_desc: String, pub state: UsbDeviceState, - pub setup_buf: Vec, pub data_buf: Vec, pub remote_wakeup: u32, - pub setup_state: SetupState, - pub setup_len: u32, - pub setup_index: u32, pub ep_ctl: Arc>, pub ep_in: Vec>>, pub ep_out: Vec>>, @@ -276,13 +272,9 @@ impl UsbDevice { config: None, altsetting: vec![0; USB_MAX_INTERFACES as usize], state: UsbDeviceState::Removed, - setup_buf: vec![0_u8; 8], data_buf: vec![0_u8; 4096], ifaces: vec![None; USB_MAX_INTERFACES as usize], remote_wakeup: 0, - setup_index: 0, - setup_len: 0, - setup_state: SetupState::Idle, }; for i in 0..USB_MAX_ENDPOINTS as u8 { @@ -585,22 +577,16 @@ pub trait UsbDeviceOps: Send + Sync { fn do_parameter(&mut self, p: &mut UsbPacket) -> Result<()> { let usb_dev = self.get_mut_usb_device(); let mut locked_dev = usb_dev.lock().unwrap(); - for i in 0..8 { - locked_dev.setup_buf[i] = (p.parameter >> (i * 8)) as u8; - } - locked_dev.setup_state = SetupState::Parameter; - locked_dev.setup_index = 0; let device_req = UsbDeviceRequest { - request_type: locked_dev.setup_buf[0], - request: locked_dev.setup_buf[1], - value: (locked_dev.setup_buf[3] as u16) << 8 | locked_dev.setup_buf[2] as u16, - index: (locked_dev.setup_buf[5] as u16) << 8 | locked_dev.setup_buf[4] as u16, - length: (locked_dev.setup_buf[7] as u16) << 8 | locked_dev.setup_buf[6] as u16, + request_type: p.parameter as u8, + request: (p.parameter >> 8) as u8, + value: (p.parameter >> 16) as u16, + index: (p.parameter >> 32) as u16, + length: (p.parameter >> 48) as u16, }; if device_req.length as usize > locked_dev.data_buf.len() { bail!("data buffer small len {}", device_req.length); } - locked_dev.setup_len = device_req.length as u32; if p.pid as u8 == USB_TOKEN_OUT { let len = locked_dev.data_buf.len(); usb_packet_transfer(p, &mut locked_dev.data_buf, len); @@ -614,9 +600,6 @@ pub trait UsbDeviceOps: Send + Sync { if p.status == UsbPacketStatus::Async { return Ok(()); } - if p.actual_length < locked_dev.setup_len { - locked_dev.setup_len = p.actual_length; - } if p.pid as u8 == USB_TOKEN_IN { p.actual_length = 0; let len = locked_dev.data_buf.len(); -- Gitee From 5659a06b0916649821075ce64e46a3f88b099840 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 30 Nov 2022 10:54:58 +0800 Subject: [PATCH 0588/2187] usb: simplify the handle_control There is no need to pass the data buf to handle_control and use the data_buf in usb device directly. Set packet status in some cases of failture. --- usb/src/hid.rs | 3 +++ usb/src/keyboard.rs | 14 ++++------- usb/src/tablet.rs | 13 ++++------- usb/src/usb.rs | 41 +++++++++++++++------------------ usb/src/xhci/xhci_controller.rs | 2 +- 5 files changed, 33 insertions(+), 40 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index ec38eb87c..019f866f0 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -520,6 +520,7 @@ impl Hid { self.handle_token_in(p); } _ => { + error!("Unhandled packet {}", p.pid); p.status = UsbPacketStatus::Stall; } }; @@ -551,9 +552,11 @@ impl Hid { let len = buf.len(); usb_packet_transfer(p, &mut buf, len); } else { + error!("Unhandled endpoint {}", locked_ep.nr); p.status = UsbPacketStatus::Stall; } } else { + error!("USB endpoint not found {}", p); p.status = UsbPacketStatus::Stall; } } diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 35669bd93..a367d5d1b 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -27,7 +27,7 @@ use crate::hid::{ use crate::usb::{ notify_controller, usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbDeviceState, - UsbEndpoint, UsbPacket, + UsbEndpoint, UsbPacket, UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; use anyhow::Result; @@ -182,15 +182,10 @@ impl UsbDeviceOps for UsbKeyboard { locked_hid.reset(); } - fn handle_control( - &mut self, - packet: &mut UsbPacket, - device_req: &UsbDeviceRequest, - data: &mut [u8], - ) { + fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { debug!("handle_control request {:?}", device_req); let mut locked_dev = self.device.lock().unwrap(); - match locked_dev.handle_control_for_descriptor(packet, device_req, data) { + match locked_dev.handle_control_for_descriptor(packet, device_req) { Ok(handled) => { if handled { debug!("Keyboard control handled by descriptor, return directly."); @@ -199,11 +194,12 @@ impl UsbDeviceOps for UsbKeyboard { } Err(e) => { error!("Keyboard descriptor error {}", e); + packet.status = UsbPacketStatus::Stall; return; } } let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.handle_control_packet(packet, device_req, data); + locked_hid.handle_control_packet(packet, device_req, &mut locked_dev.data_buf); } fn handle_data(&mut self, p: &mut UsbPacket) { diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 36fb59ce5..8345904af 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -29,6 +29,7 @@ use crate::{ usb::{ usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceState, UsbEndpoint, UsbPacket, + UsbPacketStatus, }, xhci::xhci_controller::XhciDevice, }; @@ -223,15 +224,10 @@ impl UsbDeviceOps for UsbTablet { locked_hid.reset(); } - fn handle_control( - &mut self, - packet: &mut UsbPacket, - device_req: &UsbDeviceRequest, - data: &mut [u8], - ) { + fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { debug!("handle_control request {:?}", device_req); let mut locked_dev = self.device.lock().unwrap(); - match locked_dev.handle_control_for_descriptor(packet, device_req, data) { + match locked_dev.handle_control_for_descriptor(packet, device_req) { Ok(handled) => { if handled { debug!("Tablet control handled by descriptor, return directly."); @@ -240,11 +236,12 @@ impl UsbDeviceOps for UsbTablet { } Err(e) => { error!("Tablet descriptor error {}", e); + packet.status = UsbPacketStatus::Stall; return; } } let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.handle_control_packet(packet, device_req, data); + locked_hid.handle_control_packet(packet, device_req, &mut locked_dev.data_buf); } fn handle_data(&mut self, p: &mut UsbPacket) { diff --git a/usb/src/usb.rs b/usb/src/usb.rs index a3896d68f..abdd4c758 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -359,7 +359,6 @@ impl UsbDevice { &mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest, - data: &mut [u8], ) -> Result { let value = device_req.value as u32; let index = device_req.index as u32; @@ -369,11 +368,11 @@ impl UsbDevice { USB_REQUEST_GET_DESCRIPTOR => { let res = self.get_descriptor(value)?; let len = std::cmp::min(res.len() as u32, length); - data[..(len as usize)].clone_from_slice(&res[..(len as usize)]); + self.data_buf[..(len as usize)].clone_from_slice(&res[..(len as usize)]); packet.actual_length = len; } USB_REQUEST_GET_CONFIGURATION => { - data[0] = if let Some(conf) = &self.config { + self.data_buf[0] = if let Some(conf) = &self.config { conf.config_desc.bConfigurationValue } else { 0 @@ -387,17 +386,17 @@ impl UsbDevice { let x = &self.device_desc.as_ref().unwrap().confs[0]; x.clone() }; - data[0] = 0; + self.data_buf[0] = 0; if conf.config_desc.bmAttributes & USB_CONFIGURATION_ATTR_SELF_POWER == USB_CONFIGURATION_ATTR_SELF_POWER { - data[0] |= 1 << USB_DEVICE_SELF_POWERED; + self.data_buf[0] |= 1 << USB_DEVICE_SELF_POWERED; } if self.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP { - data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; + self.data_buf[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; } - data[1] = 0x00; + self.data_buf[1] = 0x00; packet.actual_length = 2; } _ => { @@ -433,7 +432,7 @@ impl UsbDevice { USB_INTERFACE_IN_REQUEST => match device_req.request { USB_REQUEST_GET_INTERFACE => { if index < self.ninterfaces { - data[0] = self.altsetting[index as usize] as u8; + self.data_buf[0] = self.altsetting[index as usize] as u8; packet.actual_length = 1; } } @@ -513,12 +512,7 @@ pub trait UsbDeviceOps: Send + Sync { } /// Handle control pakcet. - fn handle_control( - &mut self, - packet: &mut UsbPacket, - device_req: &UsbDeviceRequest, - data: &mut [u8], - ); + fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest); /// Handle data pakcet. fn handle_data(&mut self, packet: &mut UsbPacket); @@ -544,10 +538,12 @@ pub trait UsbDeviceOps: Send + Sync { let ep = if let Some(ep) = &packet.ep { ep.upgrade().unwrap() } else { + packet.status = UsbPacketStatus::NoDev; bail!("Failed to find ep"); }; let locked_ep = ep.lock().unwrap(); let nr = locked_ep.nr; + debug!("process_packet nr {}", nr); drop(locked_ep); if nr == 0 { if packet.parameter != 0 { @@ -585,25 +581,26 @@ pub trait UsbDeviceOps: Send + Sync { length: (p.parameter >> 48) as u16, }; if device_req.length as usize > locked_dev.data_buf.len() { + p.status = UsbPacketStatus::Stall; bail!("data buffer small len {}", device_req.length); } if p.pid as u8 == USB_TOKEN_OUT { - let len = locked_dev.data_buf.len(); - usb_packet_transfer(p, &mut locked_dev.data_buf, len); + usb_packet_transfer(p, &mut locked_dev.data_buf, device_req.length as usize); } - // Drop locked for handle_control use it + // Drop locked for handle_control use it. drop(locked_dev); - let mut data_buf: [u8; 4096] = [0; 4096]; - self.handle_control(p, &device_req, &mut data_buf); + self.handle_control(p, &device_req); let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.data_buf = data_buf.to_vec(); if p.status == UsbPacketStatus::Async { return Ok(()); } + let mut len = device_req.length; + if len > p.actual_length as u16 { + len = p.actual_length as u16; + } if p.pid as u8 == USB_TOKEN_IN { p.actual_length = 0; - let len = locked_dev.data_buf.len(); - usb_packet_transfer(p, &mut locked_dev.data_buf, len); + usb_packet_transfer(p, &mut locked_dev.data_buf, len as usize); } Ok(()) } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 05b2f1cdd..852cc2d60 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -851,7 +851,7 @@ impl XhciDevice { index: 0, length: 0, }; - locked_dev.handle_control(&mut p, &device_req, &mut []); + locked_dev.handle_control(&mut p, &device_req); } fn get_device_context_addr(&self, slot_id: u32) -> u64 { -- Gitee From 1cc88adc99a39e4a6c13591415aa488628d4ef33 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 30 Nov 2022 15:07:51 +0800 Subject: [PATCH 0589/2187] usb: remove unused field in usb endpoint Remove unused field in usb endpoint. Signed-off-by: zhouli57 --- usb/src/descriptor.rs | 2 +- usb/src/usb.rs | 31 ++----------------------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index d1e4c6ec1..028403884 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -321,7 +321,7 @@ impl UsbDescriptorOps for UsbDevice { } fn init_endpoint(&mut self) -> Result<()> { - self.init_usb_endpoint(); + self.reset_usb_endpoint(); for i in 0..self.ninterfaces { let iface = self.ifaces[i as usize].as_ref(); if iface.is_none() { diff --git a/usb/src/usb.rs b/usb/src/usb.rs index abdd4c758..b75f5b415 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -10,10 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{ - collections::LinkedList, - sync::{Arc, Mutex, Weak}, -}; +use std::sync::{Arc, Mutex, Weak}; use crate::config::*; use crate::descriptor::{ @@ -82,10 +79,7 @@ pub struct UsbEndpoint { pub usb_type: u8, pub ifnum: u8, pub max_packet_size: u32, - pub pipeline: bool, - pub halted: bool, pub dev: Option>>, - pub queue: LinkedList, } impl UsbEndpoint { @@ -96,10 +90,7 @@ impl UsbEndpoint { usb_type, ifnum, max_packet_size, - pipeline: false, - halted: false, dev: None, - queue: LinkedList::new(), } } @@ -115,7 +106,7 @@ impl UsbEndpoint { } } -/// Init USB endpoint, similar with init_usb_endpoint, but set dev in endpoint. +/// Init USB endpoint, and set dev in endpoint. pub fn usb_endpoint_init(dev: &Arc>) { let mut locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_mut_usb_device(); @@ -123,12 +114,9 @@ pub fn usb_endpoint_init(dev: &Arc>) { locked_dev.reset_usb_endpoint(); let mut ep_ctl = locked_dev.ep_ctl.lock().unwrap(); ep_ctl.dev = Some(Arc::downgrade(dev)); - ep_ctl.queue = LinkedList::new(); for i in 0..USB_MAX_ENDPOINTS { let mut ep_in = locked_dev.ep_in[i as usize].lock().unwrap(); let mut ep_out = locked_dev.ep_out[i as usize].lock().unwrap(); - ep_in.queue = LinkedList::new(); - ep_out.queue = LinkedList::new(); ep_in.dev = Some(Arc::downgrade(dev)); ep_out.dev = Some(Arc::downgrade(dev)); } @@ -307,25 +295,12 @@ impl UsbDevice { } } - pub fn init_usb_endpoint(&mut self) { - self.reset_usb_endpoint(); - let mut ep_ctl = self.ep_ctl.lock().unwrap(); - ep_ctl.queue = LinkedList::new(); - for i in 0..USB_MAX_ENDPOINTS { - let mut ep_in = self.ep_in[i as usize].lock().unwrap(); - let mut ep_out = self.ep_out[i as usize].lock().unwrap(); - ep_in.queue = LinkedList::new(); - ep_out.queue = LinkedList::new(); - } - } - pub fn reset_usb_endpoint(&mut self) { let mut ep_ctl = self.ep_ctl.lock().unwrap(); ep_ctl.nr = 0; ep_ctl.usb_type = USB_ENDPOINT_ATTR_CONTROL; ep_ctl.ifnum = 0; ep_ctl.max_packet_size = 64; - ep_ctl.pipeline = false; for i in 0..USB_MAX_ENDPOINTS { let mut ep_in = self.ep_in[i as usize].lock().unwrap(); let mut ep_out = self.ep_out[i as usize].lock().unwrap(); @@ -339,8 +314,6 @@ impl UsbDevice { ep_out.ifnum = USB_INTERFACE_INVALID; ep_in.max_packet_size = 0; ep_out.max_packet_size = 0; - ep_in.pipeline = false; - ep_out.pipeline = false; } } -- Gitee From de19b8b95634f40f119857b92174559cb4c2949e Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 11 Dec 2022 22:54:42 +0800 Subject: [PATCH 0590/2187] Optimize timer: optimize the calling process of timer clock. During the callback function in clock lists, the clock lists will be changed, resulting in inconsistent data. Modify the process to: 1.delete the clock first 2.Then execute the callback function on the clock. Signed-off-by: Xiao Ye --- util/src/loop_context.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 04761b05e..5aba975ec 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -459,10 +459,12 @@ impl EventLoopContext { } expired_nr += 1; - (timer.func)(); } - self.timers.drain(0..expired_nr); + let expired_timers: Vec = self.timers.drain(0..expired_nr).collect(); + for timer in expired_timers { + (timer.func)(); + } } fn epoll_wait_manager(&mut self, time_out: i32) -> Result { -- Gitee From c23499f877a3393af680d8475795860514568ea5 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Thu, 8 Dec 2022 10:22:13 +0000 Subject: [PATCH 0591/2187] fix typo in socket.rs messege -> message Signed-off-by: Ikko Ashimine --- machine_manager/src/socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 7c9f439de..34ce61120 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -359,7 +359,7 @@ impl SocketRWHandler { /// Get inner buf as a `String`. pub fn get_buf_string(&mut self) -> Result { if self.buf.len() > MAX_SOCKET_MSG_LENGTH { - bail!("The socket messege is too long."); + bail!("The socket message is too long."); } Ok(String::from_utf8_lossy(&self.buf).trim().to_string()) -- Gitee From 36b7859cdc97482c5c91bb4164918ddaa830967f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Dec 2022 11:54:32 +0800 Subject: [PATCH 0592/2187] usb: delete unnecessary locks Delete the unnecessary locks for USBDevice and Hid, because they've already been protected by the outer lock. Signed-off-by: zhouli57 --- usb/src/keyboard.rs | 70 ++++++++++-------------- usb/src/tablet.rs | 97 +++++++++++++++------------------ usb/src/usb.rs | 50 +++++++---------- usb/src/xhci/xhci_controller.rs | 7 +-- 4 files changed, 94 insertions(+), 130 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index a367d5d1b..a6a59f006 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -113,8 +113,8 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { /// USB keyboard device. pub struct UsbKeyboard { id: String, - device: Arc>, - hid: Arc>, + usb_device: UsbDevice, + hid: Hid, /// USB controller used to notify controller to transfer data. ctrl: Option>>, } @@ -123,17 +123,15 @@ impl UsbKeyboard { pub fn new(id: String) -> Self { Self { id, - device: Arc::new(Mutex::new(UsbDevice::new())), - hid: Arc::new(Mutex::new(Hid::new(HidType::Keyboard))), + usb_device: UsbDevice::new(), + hid: Hid::new(HidType::Keyboard), ctrl: None, } } - pub fn realize(self) -> Result>> { - let mut locked_usb = self.device.lock().unwrap(); - locked_usb.product_desc = String::from("StratoVirt USB keyboard"); - locked_usb.strings = Vec::new(); - drop(locked_usb); + pub fn realize(mut self) -> Result>> { + self.usb_device.product_desc = String::from("StratoVirt USB keyboard"); + self.usb_device.strings = Vec::new(); let kbd = Arc::new(Mutex::new(self)); let cloned_kbd = kbd.clone(); usb_endpoint_init(&(kbd as Arc>)); @@ -144,28 +142,25 @@ impl UsbKeyboard { } fn init_hid(&mut self) -> Result<()> { - let mut locked_usb = self.device.lock().unwrap(); - locked_usb.usb_desc = Some(DESC_KEYBOARD.clone()); - locked_usb.init_descriptor()?; + self.usb_device.usb_desc = Some(DESC_KEYBOARD.clone()); + self.usb_device.init_descriptor()?; Ok(()) } } // Used for VNC to send keyboard event. pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Result<()> { - let locked_kbd = kbd.lock().unwrap(); - let mut locked_hid = locked_kbd.hid.lock().unwrap(); - if scan_codes.len() as u32 + locked_hid.num > QUEUE_LENGTH { + let mut locked_kbd = kbd.lock().unwrap(); + if scan_codes.len() as u32 + locked_kbd.hid.num > QUEUE_LENGTH { debug!("Keyboard queue is full!"); // Return ok to ignore the request. return Ok(()); } for code in scan_codes { - let index = ((locked_hid.head + locked_hid.num) & QUEUE_MASK) as usize; - locked_hid.num += 1; - locked_hid.keyboard.keycodes[index] = *code; + let index = ((locked_kbd.hid.head + locked_kbd.hid.num) & QUEUE_MASK) as usize; + locked_kbd.hid.num += 1; + locked_kbd.hid.keyboard.keycodes[index] = *code; } - drop(locked_hid); drop(locked_kbd); let clone_kbd = kbd.clone(); notify_controller(&(clone_kbd as Arc>)) @@ -174,18 +169,18 @@ pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Resu impl UsbDeviceOps for UsbKeyboard { fn reset(&mut self) { info!("Keyboard device reset"); - let mut locked_usb = self.device.lock().unwrap(); - locked_usb.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; - locked_usb.addr = 0; - locked_usb.state = UsbDeviceState::Default; - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.reset(); + self.usb_device.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; + self.usb_device.addr = 0; + self.usb_device.state = UsbDeviceState::Default; + self.hid.reset(); } fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { debug!("handle_control request {:?}", device_req); - let mut locked_dev = self.device.lock().unwrap(); - match locked_dev.handle_control_for_descriptor(packet, device_req) { + match self + .usb_device + .handle_control_for_descriptor(packet, device_req) + { Ok(handled) => { if handled { debug!("Keyboard control handled by descriptor, return directly."); @@ -198,25 +193,24 @@ impl UsbDeviceOps for UsbKeyboard { return; } } - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.handle_control_packet(packet, device_req, &mut locked_dev.data_buf); + self.hid + .handle_control_packet(packet, device_req, &mut self.usb_device.data_buf); } fn handle_data(&mut self, p: &mut UsbPacket) { - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.handle_data_packet(p); + self.hid.handle_data_packet(p); } fn device_id(&self) -> String { self.id.clone() } - fn get_usb_device(&self) -> Arc> { - self.device.clone() + fn get_usb_device(&self) -> &UsbDevice { + &self.usb_device } - fn get_mut_usb_device(&mut self) -> Arc> { - self.device.clone() + fn get_mut_usb_device(&mut self) -> &mut UsbDevice { + &mut self.usb_device } fn set_controller(&mut self, ctrl: Weak>) { @@ -228,11 +222,7 @@ impl UsbDeviceOps for UsbKeyboard { } fn get_wakeup_endpoint(&self) -> Option>> { - let ep = self - .device - .lock() - .unwrap() - .get_endpoint(USB_TOKEN_IN as u32, 1); + let ep = self.usb_device.get_endpoint(USB_TOKEN_IN as u32, 1); Some(Arc::downgrade(&ep)) } } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 8345904af..b40701839 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -119,8 +119,8 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { /// USB tablet device. pub struct UsbTablet { id: String, - device: Arc>, - hid: Arc>, + usb_device: UsbDevice, + hid: Hid, /// USB controller used to notify controller to transfer data. ctrl: Option>>, } @@ -129,17 +129,15 @@ impl UsbTablet { pub fn new(id: String) -> Self { Self { id, - device: Arc::new(Mutex::new(UsbDevice::new())), - hid: Arc::new(Mutex::new(Hid::new(HidType::Tablet))), + usb_device: UsbDevice::new(), + hid: Hid::new(HidType::Tablet), ctrl: None, } } - pub fn realize(self) -> Result>> { - let mut locked_usb = self.device.lock().unwrap(); - locked_usb.product_desc = String::from("StratoVirt USB Tablet"); - locked_usb.strings = Vec::new(); - drop(locked_usb); + pub fn realize(mut self) -> Result>> { + self.usb_device.product_desc = String::from("StratoVirt USB Tablet"); + self.usb_device.strings = Vec::new(); let tablet = Arc::new(Mutex::new(self)); let cloned_tablet = tablet.clone(); usb_endpoint_init(&(tablet as Arc>)); @@ -150,19 +148,17 @@ impl UsbTablet { } fn init_hid(&mut self) -> Result<()> { - let mut locked_usb = self.device.lock().unwrap(); - locked_usb.usb_desc = Some(DESC_TABLET.clone()); - locked_usb.init_descriptor()?; + self.usb_device.usb_desc = Some(DESC_TABLET.clone()); + self.usb_device.init_descriptor()?; Ok(()) } } // Used for VNC to send pointer event. pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32) -> Result<()> { - let locked_tablet = tablet.lock().unwrap(); - let mut hid = locked_tablet.hid.lock().unwrap(); - let index = ((hid.head + hid.num) & QUEUE_MASK) as usize; - let mut evt = &mut hid.pointer.queue[index]; + let mut locked_tablet = tablet.lock().unwrap(); + let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; + let mut evt = &mut locked_tablet.hid.pointer.queue[index]; if button == INPUT_BUTTON_WHEEL_UP { evt.pos_z += 1; } else if button == INPUT_BUTTON_WHEEL_DOWN { @@ -175,39 +171,37 @@ pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32 } /// Used for VNC to sync pointer event. pub fn pointer_sync(tablet: &Arc>) -> Result<()> { - let locked_tablet = tablet.lock().unwrap(); - let mut locked_hid = locked_tablet.hid.lock().unwrap(); - if locked_hid.num == QUEUE_LENGTH - 1 { + let mut locked_tablet = tablet.lock().unwrap(); + if locked_tablet.hid.num == QUEUE_LENGTH - 1 { debug!("Pointer queue is full!"); return Ok(()); } - let cur_index = ((locked_hid.head + locked_hid.num) & QUEUE_MASK) as usize; + let cur_index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; let pre_index = if cur_index == 0 { QUEUE_MASK as usize } else { (((cur_index as u32) - 1) % QUEUE_MASK) as usize }; let nxt_index = (cur_index + 1) % QUEUE_MASK as usize; - let prev = locked_hid.pointer.queue[pre_index]; - let curr = locked_hid.pointer.queue[cur_index]; + let prev = locked_tablet.hid.pointer.queue[pre_index]; + let curr = locked_tablet.hid.pointer.queue[cur_index]; let mut comp = false; - if locked_hid.num > 0 && curr.button_state == prev.button_state { + if locked_tablet.hid.num > 0 && curr.button_state == prev.button_state { comp = true; } if comp { - locked_hid.pointer.queue[pre_index].pos_x = curr.pos_x; - locked_hid.pointer.queue[pre_index].pos_y = curr.pos_y; - locked_hid.pointer.queue[pre_index].pos_z += curr.pos_z; - locked_hid.pointer.queue[cur_index].pos_z = 0; + locked_tablet.hid.pointer.queue[pre_index].pos_x = curr.pos_x; + locked_tablet.hid.pointer.queue[pre_index].pos_y = curr.pos_y; + locked_tablet.hid.pointer.queue[pre_index].pos_z += curr.pos_z; + locked_tablet.hid.pointer.queue[cur_index].pos_z = 0; return Ok(()); } else { - locked_hid.pointer.queue[nxt_index].pos_x = curr.pos_x; - locked_hid.pointer.queue[nxt_index].pos_y = curr.pos_y; - locked_hid.pointer.queue[nxt_index].pos_z = 0; - locked_hid.pointer.queue[nxt_index].button_state = curr.button_state; - locked_hid.num += 1; + locked_tablet.hid.pointer.queue[nxt_index].pos_x = curr.pos_x; + locked_tablet.hid.pointer.queue[nxt_index].pos_y = curr.pos_y; + locked_tablet.hid.pointer.queue[nxt_index].pos_z = 0; + locked_tablet.hid.pointer.queue[nxt_index].button_state = curr.button_state; + locked_tablet.hid.num += 1; } - drop(locked_hid); drop(locked_tablet); let clone_tablet = tablet.clone(); notify_controller(&(clone_tablet as Arc>)) @@ -216,18 +210,18 @@ pub fn pointer_sync(tablet: &Arc>) -> Result<()> { impl UsbDeviceOps for UsbTablet { fn reset(&mut self) { info!("Tablet device reset"); - let mut locked_usb = self.device.lock().unwrap(); - locked_usb.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; - locked_usb.addr = 0; - locked_usb.state = UsbDeviceState::Default; - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.reset(); + self.usb_device.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; + self.usb_device.addr = 0; + self.usb_device.state = UsbDeviceState::Default; + self.hid.reset(); } fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { debug!("handle_control request {:?}", device_req); - let mut locked_dev = self.device.lock().unwrap(); - match locked_dev.handle_control_for_descriptor(packet, device_req) { + match self + .usb_device + .handle_control_for_descriptor(packet, device_req) + { Ok(handled) => { if handled { debug!("Tablet control handled by descriptor, return directly."); @@ -240,25 +234,24 @@ impl UsbDeviceOps for UsbTablet { return; } } - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.handle_control_packet(packet, device_req, &mut locked_dev.data_buf); + self.hid + .handle_control_packet(packet, device_req, &mut self.usb_device.data_buf); } fn handle_data(&mut self, p: &mut UsbPacket) { - let mut locked_hid = self.hid.lock().unwrap(); - locked_hid.handle_data_packet(p); + self.hid.handle_data_packet(p); } fn device_id(&self) -> String { self.id.clone() } - fn get_usb_device(&self) -> Arc> { - self.device.clone() + fn get_usb_device(&self) -> &UsbDevice { + &self.usb_device } - fn get_mut_usb_device(&mut self) -> Arc> { - self.device.clone() + fn get_mut_usb_device(&mut self) -> &mut UsbDevice { + &mut self.usb_device } fn set_controller(&mut self, ctrl: Weak>) { @@ -270,11 +263,7 @@ impl UsbDeviceOps for UsbTablet { } fn get_wakeup_endpoint(&self) -> Option>> { - let ep = self - .device - .lock() - .unwrap() - .get_endpoint(USB_TOKEN_IN as u32, 1); + let ep = self.usb_device.get_endpoint(USB_TOKEN_IN as u32, 1); Some(Arc::downgrade(&ep)) } } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index b75f5b415..42fd1c8ce 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -110,13 +110,12 @@ impl UsbEndpoint { pub fn usb_endpoint_init(dev: &Arc>) { let mut locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_mut_usb_device(); - let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.reset_usb_endpoint(); - let mut ep_ctl = locked_dev.ep_ctl.lock().unwrap(); + usb_dev.reset_usb_endpoint(); + let mut ep_ctl = usb_dev.ep_ctl.lock().unwrap(); ep_ctl.dev = Some(Arc::downgrade(dev)); for i in 0..USB_MAX_ENDPOINTS { - let mut ep_in = locked_dev.ep_in[i as usize].lock().unwrap(); - let mut ep_out = locked_dev.ep_out[i as usize].lock().unwrap(); + let mut ep_in = usb_dev.ep_in[i as usize].lock().unwrap(); + let mut ep_out = usb_dev.ep_out[i as usize].lock().unwrap(); ep_in.dev = Some(Arc::downgrade(dev)); ep_out.dev = Some(Arc::downgrade(dev)); } @@ -441,12 +440,8 @@ pub trait UsbDeviceOps: Send + Sync { /// Handle the attach ops when attach device to controller. fn handle_attach(&mut self) -> Result<()> { let usb_dev = self.get_mut_usb_device(); - let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.state = UsbDeviceState::Attached; - drop(locked_dev); - let usb_dev = self.get_mut_usb_device(); - let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.set_default_descriptor()?; + usb_dev.state = UsbDeviceState::Attached; + usb_dev.set_default_descriptor()?; Ok(()) } @@ -466,8 +461,7 @@ pub trait UsbDeviceOps: Send + Sync { /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { let usb_dev = self.get_mut_usb_device(); - let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.port = port; + usb_dev.port = port; } /// Handle usb packet, used for controller to deliever packet to device. @@ -494,16 +488,15 @@ pub trait UsbDeviceOps: Send + Sync { fn device_id(&self) -> String; /// Get the UsbDevice. - fn get_usb_device(&self) -> Arc>; + fn get_usb_device(&self) -> &UsbDevice; /// Get the mut UsbDevice. - fn get_mut_usb_device(&mut self) -> Arc>; + fn get_mut_usb_device(&mut self) -> &mut UsbDevice; /// Get the device speed. fn speed(&self) -> u32 { let usb_dev = self.get_usb_device(); - let locked_dev = usb_dev.lock().unwrap(); - locked_dev.speed + usb_dev.speed } fn process_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { @@ -545,7 +538,6 @@ pub trait UsbDeviceOps: Send + Sync { fn do_parameter(&mut self, p: &mut UsbPacket) -> Result<()> { let usb_dev = self.get_mut_usb_device(); - let mut locked_dev = usb_dev.lock().unwrap(); let device_req = UsbDeviceRequest { request_type: p.parameter as u8, request: (p.parameter >> 8) as u8, @@ -553,17 +545,15 @@ pub trait UsbDeviceOps: Send + Sync { index: (p.parameter >> 32) as u16, length: (p.parameter >> 48) as u16, }; - if device_req.length as usize > locked_dev.data_buf.len() { + if device_req.length as usize > usb_dev.data_buf.len() { p.status = UsbPacketStatus::Stall; bail!("data buffer small len {}", device_req.length); } if p.pid as u8 == USB_TOKEN_OUT { - usb_packet_transfer(p, &mut locked_dev.data_buf, device_req.length as usize); + usb_packet_transfer(p, &mut usb_dev.data_buf, device_req.length as usize); } - // Drop locked for handle_control use it. - drop(locked_dev); self.handle_control(p, &device_req); - let mut locked_dev = usb_dev.lock().unwrap(); + let usb_dev = self.get_mut_usb_device(); if p.status == UsbPacketStatus::Async { return Ok(()); } @@ -573,7 +563,7 @@ pub trait UsbDeviceOps: Send + Sync { } if p.pid as u8 == USB_TOKEN_IN { p.actual_length = 0; - usb_packet_transfer(p, &mut locked_dev.data_buf, len as usize); + usb_packet_transfer(p, &mut usb_dev.data_buf, len as usize); } Ok(()) } @@ -592,17 +582,15 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { let mut locked_xhci = xhci.lock().unwrap(); let locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_usb_device(); - drop(locked_dev); - let locked_usb_dev = usb_dev.lock().unwrap(); - let usb_port = if let Some(port) = &locked_usb_dev.port { + let usb_port = if let Some(port) = &usb_dev.port { port.upgrade().unwrap() } else { bail!("No usb port found"); }; - let slot_id = locked_usb_dev.addr; - let wakeup = - locked_usb_dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP; - drop(locked_usb_dev); + let slot_id = usb_dev.addr; + let wakeup = usb_dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP; + // Drop the lock, lookup port need it. + drop(locked_dev); let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { xhci_port } else { diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 852cc2d60..7a7fc99ba 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -840,10 +840,8 @@ impl XhciDevice { let mut p = UsbPacket::default(); let mut locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_mut_usb_device(); - let locked_usb = usb_dev.lock().unwrap(); - let ep = Arc::downgrade(&locked_usb.get_endpoint(USB_TOKEN_OUT as u32, 0)); + let ep = Arc::downgrade(&usb_dev.get_endpoint(USB_TOKEN_OUT as u32, 0)); p.init(USB_TOKEN_OUT as u32, ep); - drop(locked_usb); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, request: USB_REQUEST_SET_ADDRESS, @@ -1450,8 +1448,7 @@ impl XhciDevice { USB_TOKEN_OUT }; let usb_dev = locked_dev.get_mut_usb_device(); - let locked_usb = usb_dev.lock().unwrap(); - let ep = locked_usb.get_endpoint(pid as u32, epid >> 1); + let ep = usb_dev.get_endpoint(pid as u32, epid >> 1); Ok(ep) } -- Gitee From e0b0c71031d89d85155b44010036d00c8a9e64b0 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Dec 2022 15:56:15 +0800 Subject: [PATCH 0593/2187] usb: simplify usb endpoint code Simplify the usb endpoint code, remove the unused filed in UsbEndpoint. And use two more intuitive functions to convert ep id and ep number. Signed-off-by: zhouli57 --- usb/src/config.rs | 3 +- usb/src/descriptor.rs | 31 ++++---------- usb/src/hid.rs | 4 +- usb/src/keyboard.rs | 11 ++--- usb/src/tablet.rs | 11 ++--- usb/src/usb.rs | 73 +++++++++++---------------------- usb/src/xhci/xhci_controller.rs | 31 ++++++++++---- 7 files changed, 63 insertions(+), 101 deletions(-) diff --git a/usb/src/config.rs b/usb/src/config.rs index 6d47f74c1..5cc83cdfe 100644 --- a/usb/src/config.rs +++ b/usb/src/config.rs @@ -214,8 +214,9 @@ pub const USB_ENDPOINT_ATTR_CONTROL: u8 = 0; pub const USB_ENDPOINT_ATTR_ISOC: u8 = 1; pub const USB_ENDPOINT_ATTR_BULK: u8 = 2; pub const USB_ENDPOINT_ATTR_INT: u8 = 3; +pub const USB_ENDPOINT_ATTR_TRANSFER_TYPE_MASK: u8 = 0x3; pub const USB_ENDPOINT_ATTR_INVALID: u8 = 255; -pub const USB_INTERFACE_INVALID: u8 = 255; +pub const USB_ENDPOINT_ADDRESS_NUMBER_MASK: u8 = 0xf; /// See the spec section 9.6.3 Configuration. Standard Configuration Descriptor. pub const USB_CONFIGURATION_ATTR_ONE: u8 = 1 << 7; diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index 028403884..7377f88f6 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -333,32 +333,15 @@ impl UsbDescriptorOps for UsbDevice { bail!("No interface descriptor found."); }; for e in 0..iface.interface_desc.bNumEndpoints { - let pid = if iface.eps[e as usize].endpoint_desc.bEndpointAddress + let in_direction = iface.eps[e as usize].endpoint_desc.bEndpointAddress & USB_DIRECTION_DEVICE_TO_HOST - == USB_DIRECTION_DEVICE_TO_HOST - { - USB_TOKEN_IN - } else { - USB_TOKEN_OUT - }; - - let ep = iface.eps[e as usize].endpoint_desc.bEndpointAddress & 0x0f; - let usb_ep = self.get_endpoint(pid as u32, ep as u32); + == USB_DIRECTION_DEVICE_TO_HOST; + let ep = iface.eps[e as usize].endpoint_desc.bEndpointAddress + & USB_ENDPOINT_ADDRESS_NUMBER_MASK; + let usb_ep = self.get_endpoint(in_direction, ep as u8); let mut locked_usb_ep = usb_ep.lock().unwrap(); - let usb_type = iface.eps[e as usize].endpoint_desc.bmAttributes & 0x03; - locked_usb_ep.usb_type = usb_type; - locked_usb_ep.ifnum = iface.interface_desc.bInterfaceNumber; - let raw = iface.eps[e as usize].endpoint_desc.wMaxPacketSize; - let size = raw & 0x7ff; - let v = (size >> 11) & 3; - let microframes = if v == 1 { - 2 - } else if v == 2 { - 3 - } else { - 1 - }; - locked_usb_ep.max_packet_size = size as u32 * microframes; + locked_usb_ep.ep_type = iface.eps[e as usize].endpoint_desc.bmAttributes + & USB_ENDPOINT_ATTR_TRANSFER_TYPE_MASK; } } Ok(()) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 019f866f0..8d0f2f2a7 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -531,7 +531,7 @@ impl Hid { if let Some(ep) = &p.ep { let ep = ep.upgrade().unwrap(); let locked_ep = ep.lock().unwrap(); - if locked_ep.nr == 1 { + if locked_ep.ep_number == 1 { if self.num == 0 { debug!("No data in usb device."); p.status = UsbPacketStatus::Nak; @@ -552,7 +552,7 @@ impl Hid { let len = buf.len(); usb_packet_transfer(p, &mut buf, len); } else { - error!("Unhandled endpoint {}", locked_ep.nr); + error!("Unhandled endpoint {}", locked_ep.ep_number); p.status = UsbPacketStatus::Stall; } } else { diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index a6a59f006..633ed15f9 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -136,16 +136,11 @@ impl UsbKeyboard { let cloned_kbd = kbd.clone(); usb_endpoint_init(&(kbd as Arc>)); let mut locked_kbd = cloned_kbd.lock().unwrap(); - locked_kbd.init_hid()?; + locked_kbd.usb_device.usb_desc = Some(DESC_KEYBOARD.clone()); + locked_kbd.usb_device.init_descriptor()?; drop(locked_kbd); Ok(cloned_kbd) } - - fn init_hid(&mut self) -> Result<()> { - self.usb_device.usb_desc = Some(DESC_KEYBOARD.clone()); - self.usb_device.init_descriptor()?; - Ok(()) - } } // Used for VNC to send keyboard event. @@ -222,7 +217,7 @@ impl UsbDeviceOps for UsbKeyboard { } fn get_wakeup_endpoint(&self) -> Option>> { - let ep = self.usb_device.get_endpoint(USB_TOKEN_IN as u32, 1); + let ep = self.usb_device.get_endpoint(true, 1); Some(Arc::downgrade(&ep)) } } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index b40701839..0f7478558 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -142,16 +142,11 @@ impl UsbTablet { let cloned_tablet = tablet.clone(); usb_endpoint_init(&(tablet as Arc>)); let mut locked_tablet = cloned_tablet.lock().unwrap(); - locked_tablet.init_hid()?; + locked_tablet.usb_device.usb_desc = Some(DESC_TABLET.clone()); + locked_tablet.usb_device.init_descriptor()?; drop(locked_tablet); Ok(cloned_tablet) } - - fn init_hid(&mut self) -> Result<()> { - self.usb_device.usb_desc = Some(DESC_TABLET.clone()); - self.usb_device.init_descriptor()?; - Ok(()) - } } // Used for VNC to send pointer event. @@ -263,7 +258,7 @@ impl UsbDeviceOps for UsbTablet { } fn get_wakeup_endpoint(&self) -> Option>> { - let ep = self.usb_device.get_endpoint(USB_TOKEN_IN as u32, 1); + let ep = self.usb_device.get_endpoint(true, 1); Some(Arc::downgrade(&ep)) } } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 42fd1c8ce..6676384e5 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -74,36 +74,21 @@ pub struct UsbDeviceRequest { /// The data transmission channel. #[derive(Default)] pub struct UsbEndpoint { - pub nr: u8, - pub pid: u8, - pub usb_type: u8, - pub ifnum: u8, - pub max_packet_size: u32, + pub ep_number: u8, + pub in_direction: bool, + pub ep_type: u8, pub dev: Option>>, } impl UsbEndpoint { - pub fn new(nr: u8, pid: u8, usb_type: u8, ifnum: u8, max_packet_size: u32) -> Self { + pub fn new(ep_number: u8, in_direction: bool, ep_type: u8) -> Self { Self { - nr, - pid, - usb_type, - ifnum, - max_packet_size, + ep_number, + in_direction, + ep_type, dev: None, } } - - pub fn get_ep_id(&self) -> u8 { - if self.nr == 0 { - // Control endpoint - 1 - } else if self.pid == USB_TOKEN_IN { - self.nr * 2 + 1 - } else { - self.nr * 2 - } - } } /// Init USB endpoint, and set dev in endpoint. @@ -243,10 +228,8 @@ impl UsbDevice { addr: 0, ep_ctl: Arc::new(Mutex::new(UsbEndpoint::new( 0, - 0, + false, USB_ENDPOINT_ATTR_CONTROL, - 0, - 64, ))), ep_in: Vec::new(), ep_out: Vec::new(), @@ -267,27 +250,23 @@ impl UsbDevice { for i in 0..USB_MAX_ENDPOINTS as u8 { dev.ep_in.push(Arc::new(Mutex::new(UsbEndpoint::new( i + 1, - USB_TOKEN_IN, + true, USB_ENDPOINT_ATTR_INVALID, - USB_INTERFACE_INVALID, - 0, )))); dev.ep_out.push(Arc::new(Mutex::new(UsbEndpoint::new( i + 1, - USB_TOKEN_OUT, + false, USB_ENDPOINT_ATTR_INVALID, - USB_INTERFACE_INVALID, - 0, )))); } dev } - pub fn get_endpoint(&self, pid: u32, ep: u32) -> Arc> { + pub fn get_endpoint(&self, in_direction: bool, ep: u8) -> Arc> { if ep == 0 { return self.ep_ctl.clone(); } - if pid as u8 == USB_TOKEN_IN { + if in_direction { self.ep_in[(ep - 1) as usize].clone() } else { self.ep_out[(ep - 1) as usize].clone() @@ -296,23 +275,17 @@ impl UsbDevice { pub fn reset_usb_endpoint(&mut self) { let mut ep_ctl = self.ep_ctl.lock().unwrap(); - ep_ctl.nr = 0; - ep_ctl.usb_type = USB_ENDPOINT_ATTR_CONTROL; - ep_ctl.ifnum = 0; - ep_ctl.max_packet_size = 64; + ep_ctl.ep_number = 0; + ep_ctl.ep_type = USB_ENDPOINT_ATTR_CONTROL; for i in 0..USB_MAX_ENDPOINTS { let mut ep_in = self.ep_in[i as usize].lock().unwrap(); let mut ep_out = self.ep_out[i as usize].lock().unwrap(); - ep_in.nr = (i + 1) as u8; - ep_out.nr = (i + 1) as u8; - ep_in.pid = USB_TOKEN_IN; - ep_out.pid = USB_TOKEN_OUT; - ep_in.usb_type = USB_ENDPOINT_ATTR_INVALID; - ep_out.usb_type = USB_ENDPOINT_ATTR_INVALID; - ep_in.ifnum = USB_INTERFACE_INVALID; - ep_out.ifnum = USB_INTERFACE_INVALID; - ep_in.max_packet_size = 0; - ep_out.max_packet_size = 0; + ep_in.ep_number = (i + 1) as u8; + ep_out.ep_number = (i + 1) as u8; + ep_in.in_direction = true; + ep_out.in_direction = false; + ep_in.ep_type = USB_ENDPOINT_ATTR_INVALID; + ep_out.ep_type = USB_ENDPOINT_ATTR_INVALID; } } @@ -508,10 +481,10 @@ pub trait UsbDeviceOps: Send + Sync { bail!("Failed to find ep"); }; let locked_ep = ep.lock().unwrap(); - let nr = locked_ep.nr; - debug!("process_packet nr {}", nr); + let ep_nr = locked_ep.ep_number; + debug!("process_packet nr {}", ep_nr); drop(locked_ep); - if nr == 0 { + if ep_nr == 0 { if packet.parameter != 0 { return self.do_parameter(packet); } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 7a7fc99ba..0ce70afdf 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -840,7 +840,7 @@ impl XhciDevice { let mut p = UsbPacket::default(); let mut locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_mut_usb_device(); - let ep = Arc::downgrade(&usb_dev.get_endpoint(USB_TOKEN_OUT as u32, 0)); + let ep = Arc::downgrade(&usb_dev.get_endpoint(false, 0)); p.init(USB_TOKEN_OUT as u32, ep); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, @@ -1442,13 +1442,9 @@ impl XhciDevice { bail!("No device found in USB port."); }; let mut locked_dev = dev.lock().unwrap(); - let pid = if epid & 1 == 1 { - USB_TOKEN_IN - } else { - USB_TOKEN_OUT - }; let usb_dev = locked_dev.get_mut_usb_device(); - let ep = usb_dev.get_endpoint(pid as u32, epid >> 1); + let (in_direction, ep_number) = endpoint_id_to_number(epid as u8); + let ep = usb_dev.get_endpoint(in_direction, ep_number); Ok(ep) } @@ -1645,7 +1641,7 @@ impl XhciDevice { /// Used for device to wakeup endpoint pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &Arc>) -> Result<()> { let locked_ep = ep.lock().unwrap(); - let ep_id = locked_ep.get_ep_id(); + let ep_id = endpoint_number_to_id(locked_ep.in_direction, locked_ep.ep_number); // Kick endpoint may hold the lock, drop it. drop(locked_ep); self.kick_endpoint(slot_id as u32, ep_id as u32)?; @@ -1834,3 +1830,22 @@ fn dma_write_u32(addr_space: &Arc, addr: GuestAddress, buf: &[u32] fn addr64_from_u32(low: u32, high: u32) -> u64 { (((high << 16) as u64) << 16) | low as u64 } + +// | ep id | < = > | ep direction | ep number | +// | 1 | | | 0 | +// | 2 | | OUT | 1 | +// | 3 | | IN | 1 | +fn endpoint_id_to_number(ep_id: u8) -> (bool, u8) { + (ep_id & 1 == 1, ep_id >> 1) +} + +fn endpoint_number_to_id(in_direction: bool, ep_number: u8) -> u8 { + if ep_number == 0 { + // Control endpoint. + 1 + } else if in_direction { + ep_number * 2 + 1 + } else { + ep_number * 2 + } +} -- Gitee From 12f4777792a6c1b601f54a7550db1017077b0a42 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Dec 2022 16:41:56 +0800 Subject: [PATCH 0594/2187] usb: simplify the pointer event Simplify the pointer event, deleted the compression because it didn't do much. Signed-off-by: zhouli57 --- usb/src/tablet.rs | 43 +++++++------------------------------------ vnc/src/input.rs | 9 +-------- 2 files changed, 8 insertions(+), 44 deletions(-) diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 0f7478558..6fe4db060 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -152,51 +152,22 @@ impl UsbTablet { // Used for VNC to send pointer event. pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32) -> Result<()> { let mut locked_tablet = tablet.lock().unwrap(); + if locked_tablet.hid.num == QUEUE_LENGTH - 1 { + debug!("Pointer queue is full!"); + // Return ok to ignore the request. + return Ok(()); + } let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; let mut evt = &mut locked_tablet.hid.pointer.queue[index]; if button == INPUT_BUTTON_WHEEL_UP { evt.pos_z += 1; } else if button == INPUT_BUTTON_WHEEL_DOWN { - evt.pos_z -= 1 + evt.pos_z -= 1; } evt.button_state = button; evt.pos_x = x; evt.pos_y = y; - Ok(()) -} -/// Used for VNC to sync pointer event. -pub fn pointer_sync(tablet: &Arc>) -> Result<()> { - let mut locked_tablet = tablet.lock().unwrap(); - if locked_tablet.hid.num == QUEUE_LENGTH - 1 { - debug!("Pointer queue is full!"); - return Ok(()); - } - let cur_index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; - let pre_index = if cur_index == 0 { - QUEUE_MASK as usize - } else { - (((cur_index as u32) - 1) % QUEUE_MASK) as usize - }; - let nxt_index = (cur_index + 1) % QUEUE_MASK as usize; - let prev = locked_tablet.hid.pointer.queue[pre_index]; - let curr = locked_tablet.hid.pointer.queue[cur_index]; - let mut comp = false; - if locked_tablet.hid.num > 0 && curr.button_state == prev.button_state { - comp = true; - } - if comp { - locked_tablet.hid.pointer.queue[pre_index].pos_x = curr.pos_x; - locked_tablet.hid.pointer.queue[pre_index].pos_y = curr.pos_y; - locked_tablet.hid.pointer.queue[pre_index].pos_z += curr.pos_z; - locked_tablet.hid.pointer.queue[cur_index].pos_z = 0; - return Ok(()); - } else { - locked_tablet.hid.pointer.queue[nxt_index].pos_x = curr.pos_x; - locked_tablet.hid.pointer.queue[nxt_index].pos_y = curr.pos_y; - locked_tablet.hid.pointer.queue[nxt_index].pos_z = 0; - locked_tablet.hid.pointer.queue[nxt_index].button_state = curr.button_state; - locked_tablet.hid.num += 1; - } + locked_tablet.hid.num += 1; drop(locked_tablet); let clone_tablet = tablet.clone(); notify_controller(&(clone_tablet as Arc>)) diff --git a/vnc/src/input.rs b/vnc/src/input.rs index b742cc5e5..f0ed50e0e 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -15,11 +15,7 @@ use crate::{ pixman::{get_image_height, get_image_width}, }; use log::error; -use usb::{ - keyboard::keyboard_event, - tablet::{pointer_event, pointer_sync}, - INPUT, -}; +use usb::{keyboard::keyboard_event, tablet::pointer_event, INPUT}; // Logical window size for mouse. const ABS_MAX: u64 = 0x7fff; // Up flag. @@ -99,9 +95,6 @@ impl ClientIoHandler { if let Err(e) = pointer_event(tablet, button_mask as u32, x as i32, y as i32) { error!("Point event error: {}", e); } - if let Err(e) = pointer_sync(tablet) { - error!("Point event sync error: {}", e); - } } self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); -- Gitee From c4ad5ffbc446c48f5d321dc57adfb9d478a14e04 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 10:34:00 +0800 Subject: [PATCH 0595/2187] xhci: set ep state to halted if an error occurs Set the endpoint state to halted if an error occurs in the usb packet. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 0ce70afdf..d1fa4b39c 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1486,6 +1486,9 @@ impl XhciDevice { self.report_transfer_error(xfer)?; } } + // Set the endpoint state to halted if an error occurs in the packet. + let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; + epctx.set_state(&self.mem_space, EP_HALTED)?; Ok(()) } -- Gitee From 42e25c132e1c3de8211d4ffd771362ebfe2e403e Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Fri, 16 Dec 2022 15:50:17 +0800 Subject: [PATCH 0596/2187] migration: fix migrate virtio net device It needs to mark dirty page for reading data from tap device to virtual machine memory. This patch add dirty record pages. But mark dirty pages needs to be manager by `AddressSpace`, it should to be optimized. Signed-off-by: Xinle.Guo --- migration/src/migration.rs | 10 +++++----- virtio/src/net.rs | 11 ++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/migration/src/migration.rs b/migration/src/migration.rs index d0b23caf8..5bd01e18b 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -595,7 +595,7 @@ impl DirtyBitmap { /// * `gpa` - Guest physical address of memory slot. /// * `hva` - Host virtual address of memory slot. /// * `len` - Length of memory slot. - pub fn new(gpa: u64, hva: u64, len: u64) -> Self { + fn new(gpa: u64, hva: u64, len: u64) -> Self { let page_size = host_page_size(); let mut num_pages = len / page_size; @@ -621,7 +621,7 @@ impl DirtyBitmap { /// /// * `addr` - Guest physical address of memory. /// * `len` - Length of memory slot. - pub fn mark_bitmap(&self, addr: u64, len: u64) { + fn mark_bitmap(&self, addr: u64, len: u64) { // Just return if len is 0. if len == 0 { return; @@ -629,8 +629,8 @@ impl DirtyBitmap { let offset = addr - self.gpa; let first_bit = offset / self.page_size; - let last_bit = (offset + len) / self.page_size; - for n in first_bit..last_bit { + let last_bit = (offset + len - 1) / self.page_size; + for n in first_bit..=last_bit { // Ignore bit that is out of range. if n >= self.len { break; @@ -640,7 +640,7 @@ impl DirtyBitmap { } /// Get and clear dirty bitmap for vmm. - pub fn get_and_clear_dirty(&self) -> Vec { + fn get_and_clear_dirty(&self) -> Vec { self.map .iter() .map(|m| m.fetch_and(0, Ordering::SeqCst)) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 429b3821c..2e21ef4c3 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -45,7 +45,10 @@ use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, }; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + migration::Migratable, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, + StateTransfer, +}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::{ @@ -771,6 +774,12 @@ impl NetIoHandler { } } + // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. + for iov in iovecs.iter() { + // Mark vmm dirty page manually if live migration is active. + MigrationManager::mark_dirty_log(iov.iov_base as u64, iov.iov_len as u64); + } + // Read the data from the tap device. let size = NetIoHandler::read_from_tap(&mut queue, &iovecs, tap); if size < 0 { -- Gitee From 0bb56af79e8a98ad8673395b7cee9aa3428db659 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 19 Dec 2022 20:29:53 +0800 Subject: [PATCH 0597/2187] Fix: Don't use Weak without circular references Non parent-child member variables in XhciDevice can be directly references without Weak. Signed-off-by: Mingwang Li --- usb/src/hid.rs | 6 +++--- usb/src/usb.rs | 6 +++--- usb/src/xhci/xhci_controller.rs | 26 ++++++++++++-------------- usb/src/xhci/xhci_regs.rs | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 8d0f2f2a7..2685421a5 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -528,9 +528,9 @@ impl Hid { fn handle_token_in(&mut self, p: &mut UsbPacket) { let mut buf = Vec::new(); - if let Some(ep) = &p.ep { - let ep = ep.upgrade().unwrap(); - let locked_ep = ep.lock().unwrap(); + if let Some(ep) = p.ep.as_ref() { + let clone_ep = ep.clone(); + let locked_ep = clone_ep.lock().unwrap(); if locked_ep.ep_number == 1 { if self.num == 0 { debug!("No data in usb device."); diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 6676384e5..5ff6a54cd 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -475,7 +475,7 @@ pub trait UsbDeviceOps: Send + Sync { fn process_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { packet.status = UsbPacketStatus::Success; let ep = if let Some(ep) = &packet.ep { - ep.upgrade().unwrap() + ep } else { packet.status = UsbPacketStatus::NoDev; bail!("Failed to find ep"); @@ -617,7 +617,7 @@ impl Iovec { pub struct UsbPacket { /// USB packet id. pub pid: u32, - pub ep: Option>>, + pub ep: Option>>, pub iovecs: Vec, /// control transfer parameter. pub parameter: u64, @@ -639,7 +639,7 @@ impl std::fmt::Display for UsbPacket { } impl UsbPacket { - pub fn init(&mut self, pid: u32, ep: Weak>) { + pub fn init(&mut self, pid: u32, ep: Arc>) { self.pid = pid; self.ep = Some(ep); self.status = UsbPacketStatus::Success; diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 0ce70afdf..b86b85b78 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -256,7 +256,7 @@ pub struct XhciSlot { pub addressed: bool, pub intr_target: u16, pub slot_ctx_addr: u64, - pub usb_port: Option>>, + pub usb_port: Option>>, pub endpoints: Vec, } @@ -465,7 +465,7 @@ impl XhciDevice { let mut locked_port = locked_xhci.ports[i as usize].lock().unwrap(); locked_port.name = format!("{}-usb2-{}", locked_port.name, i); locked_port.speed_mask = USB_SPEED_LOW | USB_SPEED_HIGH | USB_SPEED_FULL; - locked_port.usb_port = Some(Arc::downgrade(&usb_port)); + locked_port.usb_port = Some(usb_port); } for i in 0..locked_xhci.numports_3 { let idx = i + locked_xhci.numports_2; @@ -475,7 +475,7 @@ impl XhciDevice { let mut locked_port = locked_xhci.ports[idx as usize].lock().unwrap(); locked_port.name = format!("{}-usb3-{}", locked_port.name, idx); locked_port.speed_mask = USB_SPEED_SUPER; - locked_port.usb_port = Some(Arc::downgrade(&usb_port)); + locked_port.usb_port = Some(usb_port); } xhci } @@ -527,8 +527,8 @@ impl XhciDevice { pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { let mut locked_port = xhci_port.lock().unwrap(); if let Some(usb_port) = locked_port.usb_port.as_ref() { - let upg_usb_port = usb_port.upgrade().unwrap(); - let locked_usb_port = upg_usb_port.lock().unwrap(); + let clone_usb_port = usb_port.clone(); + let locked_usb_port = clone_usb_port.lock().unwrap(); let usb_dev = if let Some(dev) = locked_usb_port.dev.as_ref() { dev } else { @@ -578,8 +578,8 @@ impl XhciDevice { locked_port.portsc = PORTSC_PP; let mut pls = PLS_RX_DETECT; if let Some(usb_port) = &locked_port.usb_port { - let usb_port = usb_port.upgrade().unwrap(); - let locked_usb_port = usb_port.lock().unwrap(); + let clone_usb_port = usb_port.clone(); + let locked_usb_port = clone_usb_port.lock().unwrap(); if let Some(dev) = &locked_usb_port.dev { let speed = dev.lock().unwrap().speed(); locked_port.portsc |= PORTSC_CCS; @@ -792,7 +792,7 @@ impl XhciDevice { let ctx_addr = self.get_device_context_addr(slot_id); let mut octx = 0; dma_read_u64(&self.mem_space, GuestAddress(ctx_addr), &mut octx)?; - self.slots[(slot_id - 1) as usize].usb_port = Some(Arc::downgrade(&usb_port)); + self.slots[(slot_id - 1) as usize].usb_port = Some(usb_port.clone()); self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; self.slots[(slot_id - 1) as usize].intr_target = ((slot_ctx.tt_info >> TRB_INTR_SHIFT) & TRB_INTR_MASK) as u16; @@ -840,7 +840,7 @@ impl XhciDevice { let mut p = UsbPacket::default(); let mut locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_mut_usb_device(); - let ep = Arc::downgrade(&usb_dev.get_endpoint(false, 0)); + let ep = usb_dev.get_endpoint(false, 0); p.init(USB_TOKEN_OUT as u32, ep); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, @@ -1120,7 +1120,7 @@ impl XhciDevice { let slot = &mut self.slots[(slot_id - 1) as usize]; let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; if let Some(port) = &slot.usb_port { - if port.upgrade().unwrap().lock().unwrap().dev.is_some() { + if port.lock().unwrap().dev.is_some() { epctx.set_state(&self.mem_space, EP_STOPPED)?; } else { error!("Failed to found usb device"); @@ -1288,7 +1288,6 @@ impl XhciDevice { fn device_handle_packet(&mut self, packet: &mut UsbPacket) { if let Some(ep) = &packet.ep { - let ep = ep.upgrade().unwrap(); let locked_ep = ep.lock().unwrap(); let dev = if let Some(usb_dev) = &locked_ep.dev { usb_dev.upgrade().unwrap() @@ -1386,8 +1385,7 @@ impl XhciDevice { let ep = if let Some(ep) = &xfer.packet.ep { ep.clone() } else { - let ep = self.get_usb_ep(xfer.slotid, xfer.epid)?; - Arc::downgrade(&ep) + self.get_usb_ep(xfer.slotid, xfer.epid)? }; let dir = if xfer.in_xfer { USB_TOKEN_IN @@ -1431,7 +1429,7 @@ impl XhciDevice { fn get_usb_ep(&self, slotid: u32, epid: u32) -> Result>> { let port = if let Some(port) = &self.slots[(slotid - 1) as usize].usb_port { - port.upgrade().unwrap() + port } else { bail!("USB port not found slotid {} epid {}", slotid, epid); }; diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index d210d53d6..2b443f9e5 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -236,7 +236,7 @@ pub struct XhciPort { pub portsc: u32, /// Port ID pub port_idx: u8, - pub usb_port: Option>>, + pub usb_port: Option>>, pub speed_mask: u32, pub name: String, } -- Gitee From e02672a1b26ca782d0104593ab6c2a20fd982913 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Dec 2022 19:25:13 +0800 Subject: [PATCH 0598/2187] aio: Only propagate out fatal errors The err related to specific request can be handled by complete_func. Let caller of aio module to handle fatal err, i.e, report_virtio_error. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 77 ++++++++++++++++++++++------------- virtio/src/block.rs | 19 +++++---- virtio/src/scsi/controller.rs | 16 +------- 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 71dd9e570..f6dd52c78 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -25,7 +25,7 @@ use log::error; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; pub use libaio::*; pub use raw::*; use uring::IoUringContext; @@ -208,7 +208,15 @@ impl Aio { if direct { for iov in cb.iovec.iter() { if iov.iov_base % sector_size != 0 || iov.iov_len % sector_size != 0 { - return self.handle_misaligned_rw(cb); + let res = self.handle_misaligned_rw(&cb).map_or_else( + |e| { + error!("{:?}", e); + -1 + }, + |_| 0, + ); + (self.complete_func)(&cb, res); + return Ok(()); } } } @@ -242,7 +250,14 @@ impl Aio { let mut r = 0; let mut off = cb.offset; for iov in cb.iovec.iter() { - r = raw_read(cb.file_fd, iov.iov_base, iov.iov_len as usize, off)?; + r = raw_read(cb.file_fd, iov.iov_base, iov.iov_len as usize, off) + .unwrap_or_else(|e| { + error!("Failed to do sync read, {:?}", e); + -1 + }); + if r < 0 { + break; + } off += iov.iov_len as usize; } r @@ -251,7 +266,14 @@ impl Aio { let mut r = 0; let mut off = cb.offset; for iov in cb.iovec.iter() { - r = raw_write(cb.file_fd, iov.iov_base, iov.iov_len as usize, off)?; + r = raw_write(cb.file_fd, iov.iov_base, iov.iov_len as usize, off) + .unwrap_or_else(|e| { + error!("Failed to do sync write, {:?}", e); + -1 + }); + if r < 0 { + break; + } off += iov.iov_len as usize; } r @@ -263,11 +285,9 @@ impl Aio { Ok(()) } - fn handle_misaligned_rw(&mut self, cb: AioCb) -> Result<()> { + fn handle_misaligned_rw(&mut self, cb: &AioCb) -> Result<()> { // Safe because we only get the host page size. let host_page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u64; - let mut ret = -1; - match cb.opcode { IoCmd::Preadv => { // Safe because we allocate aligned memory and free it later. @@ -277,7 +297,7 @@ impl Aio { if aligned_buffer.is_null() { bail!("Failed to alloc memory for misaligned read"); } - ret = raw_read( + raw_read( cb.file_fd, aligned_buffer as u64, cb.nbytes as usize, @@ -286,21 +306,21 @@ impl Aio { .map_err(|e| { // Safe because the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; - e + anyhow!("Failed to do raw read for misaligned read, {:?}", e) })?; let src = unsafe { std::slice::from_raw_parts(aligned_buffer as *const u8, cb.nbytes as usize) }; - iov_from_buf_direct(&cb.iovec, src).and_then(|v| { + let res = iov_from_buf_direct(&cb.iovec, src).and_then(|v| { if v == cb.nbytes as usize { Ok(()) } else { - unsafe { libc::free(aligned_buffer) }; - bail!("Failed to copy buff to iovs") + Err(anyhow!("Failed to copy iovs to buff for misaligned read")) } - })?; + }); unsafe { libc::free(aligned_buffer) }; + res } IoCmd::Pwritev => { let aligned_buffer = @@ -311,36 +331,37 @@ impl Aio { let dst = unsafe { std::slice::from_raw_parts_mut(aligned_buffer as *mut u8, cb.nbytes as usize) }; - iov_to_buf_direct(&cb.iovec, dst).and_then(|v| { + if let Err(e) = iov_to_buf_direct(&cb.iovec, dst).and_then(|v| { if v == cb.nbytes as usize { Ok(()) } else { - unsafe { libc::free(aligned_buffer) }; - bail!("Failed to copy iovs to buff") + Err(anyhow!("Failed to copy iovs to buff for misaligned write")) } - })?; + }) { + unsafe { libc::free(aligned_buffer) }; + return Err(e); + } - ret = raw_write( + let res = raw_write( cb.file_fd, aligned_buffer as u64, cb.nbytes as usize, cb.offset, ) - .map_err(|e| { - unsafe { libc::free(aligned_buffer) }; - e - })?; + .map(|_| {}) + .map_err(|e| anyhow!("Failed to do raw write for misaligned write, {:?}", e)); unsafe { libc::free(aligned_buffer) }; + res } - _ => {} - }; - (self.complete_func)(&cb, ret); - - Ok(()) + _ => bail!("Failed to do misaligned rw: unknown cmd type"), + } } pub fn flush_sync(&mut self, cb: AioCb) -> Result<()> { - let ret = raw_datasync(cb.file_fd)?; + let ret = raw_datasync(cb.file_fd).unwrap_or_else(|e| { + error!("Failed to do sync flush, {:?}", e); + -1 + }); (self.complete_func)(&cb, ret); Ok(()) } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index fc8795d3d..114903122 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -356,8 +356,14 @@ impl Request { VIRTIO_BLK_T_GET_ID => { let serial = serial_num.clone().unwrap_or_else(|| String::from("")); let serial_vec = get_serial_num_config(&serial); - iov_from_buf_direct(&self.iovec, &serial_vec)?; - aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_OK); + let status = iov_from_buf_direct(&self.iovec, &serial_vec).map_or_else( + |e| { + error!("Failed to process block request for getting id, {:?}", e); + VIRTIO_BLK_S_IOERR + }, + |_| VIRTIO_BLK_S_OK, + ); + aiocb.iocompletecb.complete_request(status); } _ => bail!( "The type {} of block request is not supported", @@ -555,18 +561,15 @@ impl BlockIoHandler { self.driver_features, ); if let Some(disk_img) = self.disk_image.as_ref() { - if let Err(ref e) = req_rc.execute( + req_rc.execute( self.aio.as_mut().unwrap(), disk_img, &self.serial_num, self.direct, &self.aio_type, req_index == last_aio_req_index, - aiocompletecb.clone(), - ) { - error!("Failed to execute block request, {:?}", e); - aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); - } + aiocompletecb, + )?; } else { warn!("Failed to execute block request, disk_img not specified"); aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 5a3df5115..1997ab2a8 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -976,21 +976,7 @@ impl ScsiCmdHandler { Arc::new(Mutex::new(scsi_req.clone())), ); if let Some(ref mut aio) = self.aio { - if let Err(ref e) = - scsi_req.execute(aio, &disk_img, direct, aio_type, true, scsicompletecb) - { - // If read/write operation by rw_sync or flush operation fails, this request will not - // call complete_func(). So we should process this error here. Otherwise, stratovirt - // will not add used index and notify guest, and then guest will be stuck. - error!( - "Failed to execute scsi device non emulation request, {:?}", - e - ); - let mut cmd_lock = cmd_h.lock().unwrap(); - cmd_lock.resp.response = VIRTIO_SCSI_S_FAILURE; - cmd_lock.complete(&self.mem_space)?; - drop(cmd_lock); - } + scsi_req.execute(aio, &disk_img, direct, aio_type, true, scsicompletecb)?; } } } -- Gitee From 2ebc3674e7543826390b0fefe9dcfe8009ad8145 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Dec 2022 19:26:27 +0800 Subject: [PATCH 0599/2187] virtio: Fix virtio fatal error handling Propagate fatal error and report virtio error in the outside. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 23 +++++------- virtio/src/block.rs | 71 +++++++++++++++++++++-------------- virtio/src/scsi/controller.rs | 26 ++++--------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index f6dd52c78..4f7c7fe69 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -56,7 +56,7 @@ trait AioContext { fn get_events(&mut self) -> &[IoEvent]; } -pub type AioCompleteFunc = Box, i64) + Sync + Send>; +pub type AioCompleteFunc = Box, i64) -> Result<()> + Sync + Send>; pub struct AioCb { pub last_aio: bool, @@ -140,7 +140,7 @@ impl Aio { -1 }; - (self.complete_func)(&(*node).value, res); + (self.complete_func)(&(*node).value, res)?; self.aio_in_flight.unlink(&(*node)); // Construct Box to free mem automatically. drop(Box::from_raw(node)); @@ -148,11 +148,11 @@ impl Aio { } // Drop reference of 'ctx', so below 'process_list' can work. drop(ctx); - self.process_list(); + self.process_list()?; Ok(done) } - fn process_list(&mut self) { + fn process_list(&mut self) -> Result<()> { while self.aio_in_queue.len > 0 && self.aio_in_flight.len < self.max_events { let mut iocbs = Vec::new(); @@ -194,7 +194,7 @@ impl Aio { if is_err { // Fail one request, retry the rest. if let Some(node) = self.aio_in_queue.pop_tail() { - (self.complete_func)(&(node).value, -1); + (self.complete_func)(&(node).value, -1)?; } } else if nr == 0 { // If can't submit any request, break the loop @@ -202,6 +202,7 @@ impl Aio { break; } } + Ok(()) } pub fn rw_aio(&mut self, cb: AioCb, sector_size: u64, direct: bool) -> Result<()> { @@ -215,8 +216,7 @@ impl Aio { }, |_| 0, ); - (self.complete_func)(&cb, res); - return Ok(()); + return (self.complete_func)(&cb, res); } } } @@ -238,7 +238,7 @@ impl Aio { self.aio_in_queue.add_head(node); if last_aio || self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { - self.process_list(); + self.process_list()?; } Ok(()) @@ -280,9 +280,7 @@ impl Aio { } _ => -1, }; - (self.complete_func)(&cb, ret); - - Ok(()) + (self.complete_func)(&cb, ret) } fn handle_misaligned_rw(&mut self, cb: &AioCb) -> Result<()> { @@ -362,8 +360,7 @@ impl Aio { error!("Failed to do sync flush, {:?}", e); -1 }); - (self.complete_func)(&cb, ret); - Ok(()) + (self.complete_func)(&cb, ret) } } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 114903122..2b17a27ed 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -122,18 +122,18 @@ impl AioCompleteCb { } } - fn complete_request(&self, status: u8) { + fn complete_request(&self, status: u8) -> Result<()> { let mut req = Some(self.req.as_ref()); while let Some(req_raw) = req { - self.complete_one_request(req_raw, status); + self.complete_one_request(req_raw, status)?; req = req_raw.next.as_ref().as_ref(); } + Ok(()) } - fn complete_one_request(&self, req: &Request, status: u8) { + fn complete_one_request(&self, req: &Request, status: u8) -> Result<()> { if let Err(ref e) = self.mem_space.write_object(&status, req.in_header) { - error!("Failed to write the status (aio completion) {:?}", e); - return; + bail!("Failed to write the status (blk io completion) {:?}", e); } let mut queue_lock = self.queue.lock().unwrap(); @@ -141,11 +141,12 @@ impl AioCompleteCb { .vring .add_used(&self.mem_space, req.desc_index, req.in_len) { - error!( - "Failed to add used ring(aio completion), index {}, len {} {:?}", - req.desc_index, req.in_len, e, + bail!( + "Failed to add used ring(blk io completion), index {}, len {} {:?}", + req.desc_index, + req.in_len, + e, ); - return; } if queue_lock @@ -157,14 +158,14 @@ impl AioCompleteCb { Some(&queue_lock), false, ) { - error!( - "Failed to trigger interrupt(aio completion) for block device, error is {:?}", + bail!( + "Failed to trigger interrupt(blk io completion), error is {:?}", e ); - return; } self.trace_send_interrupt("Block".to_string()); } + Ok(()) } } @@ -363,12 +364,10 @@ impl Request { }, |_| VIRTIO_BLK_S_OK, ); - aiocb.iocompletecb.complete_request(status); + aiocb.iocompletecb.complete_request(status)?; } - _ => bail!( - "The type {} of block request is not supported", - self.out_header.request_type - ), + // The illegal request type has been handled in method new(). + _ => {} }; Ok(()) } @@ -535,7 +534,7 @@ impl BlockIoHandler { ); // unlock queue, because it will be hold below. drop(queue); - aiocompletecb.complete_request(status); + aiocompletecb.complete_request(status)?; continue; } // Avoid bogus guest stuck IO thread. @@ -572,7 +571,7 @@ impl BlockIoHandler { )?; } else { warn!("Failed to execute block request, disk_img not specified"); - aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR); + aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR)?; } } @@ -663,12 +662,27 @@ impl BlockIoHandler { } } - complete_cb.complete_request(status); + complete_cb.complete_request(status) }) as AioCompleteFunc); Ok(Box::new(Aio::new(complete_func, engine)?)) } + fn aio_complete_handler(&mut self) -> Result { + if let Some(aio) = self.aio.as_mut() { + let result = aio.handle(); + if result.is_err() { + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + Some(&self.deactivate_evt), + ); + } + return result; + } + Ok(false) + } + fn update_evt_handler(&mut self) { match self.receiver.recv() { Ok((image, disk_sectors, serial_num, direct, aio_type)) => { @@ -837,21 +851,20 @@ impl EventNotifierHelper for BlockIoHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - - if let Some(aio) = &mut h_clone.lock().unwrap().aio { - if let Err(ref e) = aio.handle() { - error!("Failed to handle aio, {:?}", e); - } + if let Err(ref e) = h_clone.lock().unwrap().aio_complete_handler() { + error!("Failed to handle aio {:?}", e); } None }); let h_clone = handler.clone(); let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { - let mut done = false; - if let Some(aio) = &mut h_clone.lock().unwrap().aio { - done = aio.handle().with_context(|| "Failed to handle aio").ok()?; - } + let done = h_clone + .lock() + .unwrap() + .aio_complete_handler() + .with_context(|| "Failed to handle aio") + .ok()?; if done { Some(Vec::new()) } else { diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 1997ab2a8..fb8779cb5 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -458,9 +458,6 @@ pub struct VirtioScsiRequest { _sense_size: u32, mode: ScsiXferMode, interrupt_cb: Arc, - /// Virtio device should report virtio error when fatal errors occur. - /// And then, deactivate eventfd is used to disable virtio queues. - deactivate_evt: EventFd, driver_features: u64, /// resp GPA. resp_addr: GuestAddress, @@ -474,7 +471,6 @@ impl VirtioScsiRequest { mem_space: &Arc, queue: Arc>, interrupt_cb: Arc, - deactivate_evt: EventFd, driver_features: u64, elem: &Element, ) -> Result { @@ -512,7 +508,6 @@ impl VirtioScsiRequest { _sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE as u32, mode: ScsiXferMode::ScsiXferNone, interrupt_cb, - deactivate_evt, driver_features, resp_addr: in_iov_elem.addr, req: scsi_req, @@ -663,7 +658,6 @@ impl ScsiCtrlHandler { &self.mem_space, self.queue.clone(), self.interrupt_cb.clone(), - self.deactivate_evt.try_clone().unwrap(), self.driver_features, &elem, )?; @@ -679,7 +673,6 @@ impl ScsiCtrlHandler { &self.mem_space, self.queue.clone(), self.interrupt_cb.clone(), - self.deactivate_evt.try_clone().unwrap(), self.driver_features, &elem, )?; @@ -865,9 +858,15 @@ impl EventNotifierHelper for ScsiCmdHandler { let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Some(aio) = &mut h_clone.lock().unwrap().aio { + let mut h_lock = h_clone.lock().unwrap(); + if let Some(aio) = &mut h_lock.aio { if let Err(ref e) = aio.handle() { error!("Failed to handle aio, {:?}", e); + report_virtio_error( + h_lock.interrupt_cb.clone(), + h_lock.driver_features, + Some(&h_lock.deactivate_evt), + ); } } None @@ -913,7 +912,6 @@ impl ScsiCmdHandler { &self.mem_space, self.queue.clone(), self.interrupt_cb.clone(), - self.deactivate_evt.try_clone().unwrap(), self.driver_features, &elem, )?; @@ -999,15 +997,7 @@ impl ScsiCmdHandler { virtio_scsi_req.resp.status = GOOD; virtio_scsi_req.resp.resid = 0; virtio_scsi_req.resp.sense_len = 0; - let result = virtio_scsi_req.complete(&complete_cb.mem_space); - if result.is_err() { - let deactivate_evt = &virtio_scsi_req.deactivate_evt; - report_virtio_error( - virtio_scsi_req.interrupt_cb.clone(), - virtio_scsi_req.driver_features, - Some(deactivate_evt), - ); - } + virtio_scsi_req.complete(&complete_cb.mem_space) }) as AioCompleteFunc); Ok(Box::new(Aio::new(complete_fun, None)?)) -- Gitee From 6960c5eb07a7ff019ed993175ecb72f1e67ec82e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 18 Dec 2022 14:02:25 +0800 Subject: [PATCH 0600/2187] aio: Drop unnecessary mutex lock on aiocontext The aiocontext is accessed by dedicated iohandler and will not be accessed by multi-thread. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 4f7c7fe69..eb591a356 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -19,7 +19,7 @@ use std::cmp; use std::io::Write; use std::marker::{Send, Sync}; use std::os::unix::io::{AsRawFd, RawFd}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use log::error; use vmm_sys_util::eventfd::EventFd; @@ -87,7 +87,7 @@ impl AioCb { } pub struct Aio { - ctx: Arc>, + ctx: Box, pub fd: EventFd, pub aio_in_queue: CbList, pub aio_in_flight: CbList, @@ -105,14 +105,10 @@ impl Aio { AIO_NATIVE }; - let ctx: Arc> = if aio == AIO_IOURING { - Arc::new(Mutex::new(IoUringContext::new( - max_events as u32, - &fd, - func.clone(), - )?)) + let ctx: Box = if aio == AIO_IOURING { + Box::new(IoUringContext::new(max_events as u32, &fd, func.clone())?) } else { - Arc::new(Mutex::new(LibaioContext::new(max_events as i32)?)) + Box::new(LibaioContext::new(max_events as i32)?) }; Ok(Aio { @@ -127,9 +123,8 @@ impl Aio { pub fn handle(&mut self) -> Result { let mut done = false; - let mut ctx = self.ctx.lock().unwrap(); - for evt in ctx.get_events() { + for evt in self.ctx.get_events() { unsafe { let node = evt.data as *mut CbNode; let res = if (evt.res2 == 0) && (evt.res == (*node).value.nbytes as i64) { @@ -146,8 +141,6 @@ impl Aio { drop(Box::from_raw(node)); } } - // Drop reference of 'ctx', so below 'process_list' can work. - drop(ctx); self.process_list()?; Ok(done) } @@ -170,8 +163,6 @@ impl Aio { // The iocbs must not be empty. let (nr, is_err) = match self .ctx - .lock() - .unwrap() .submit(iocbs.len() as i64, &mut iocbs) .map_err(|e| { error!("{}", e); -- Gitee From d4ac7bfbfed2273704c5b93613040537077225c1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 18:13:05 +0800 Subject: [PATCH 0601/2187] aio: Use function pointer instead of closures for complete_func The complete_func doesn't catch value from its environment, so it is unncessary to use closures. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 3 +-- virtio/src/block.rs | 46 +++++++++++++++++------------------ virtio/src/scsi/controller.rs | 34 +++++++++++++------------- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index eb591a356..eaf951215 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -17,7 +17,6 @@ mod uring; use std::clone::Clone; use std::cmp; use std::io::Write; -use std::marker::{Send, Sync}; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::Arc; @@ -56,7 +55,7 @@ trait AioContext { fn get_events(&mut self) -> &[IoEvent]; } -pub type AioCompleteFunc = Box, i64) -> Result<()> + Sync + Send>; +pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; pub struct AioCb { pub last_aio: bool, diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 2b17a27ed..5e1362bb6 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -44,9 +44,7 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::aio::{ - iov_from_buf_direct, raw_datasync, Aio, AioCb, AioCompleteFunc, IoCmd, Iovec, AIO_NATIVE, -}; +use util::aio::{iov_from_buf_direct, raw_datasync, Aio, AioCb, IoCmd, Iovec, AIO_NATIVE}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ @@ -641,31 +639,31 @@ impl BlockIoHandler { result } - fn build_aio(&self, engine: Option<&String>) -> Result>> { - let complete_func = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { - let mut status = if ret < 0 { - VIRTIO_BLK_S_IOERR - } else { - VIRTIO_BLK_S_OK - }; + fn complete_func(aiocb: &AioCb, ret: i64) -> Result<()> { + let mut status = if ret < 0 { + VIRTIO_BLK_S_IOERR + } else { + VIRTIO_BLK_S_OK + }; - let complete_cb = &aiocb.iocompletecb; - // When driver does not accept FLUSH feature, the device must be of - // writethrough cache type, so flush data before updating used ring. - if !virtio_has_feature(complete_cb.driver_features, VIRTIO_BLK_F_FLUSH) - && aiocb.opcode == IoCmd::Pwritev - && ret >= 0 - { - if let Err(ref e) = raw_datasync(aiocb.file_fd) { - error!("Failed to flush data before send response to guest {:?}", e); - status = VIRTIO_BLK_S_IOERR; - } + let complete_cb = &aiocb.iocompletecb; + // When driver does not accept FLUSH feature, the device must be of + // writethrough cache type, so flush data before updating used ring. + if !virtio_has_feature(complete_cb.driver_features, VIRTIO_BLK_F_FLUSH) + && aiocb.opcode == IoCmd::Pwritev + && ret >= 0 + { + if let Err(ref e) = raw_datasync(aiocb.file_fd) { + error!("Failed to flush data before send response to guest {:?}", e); + status = VIRTIO_BLK_S_IOERR; } + } - complete_cb.complete_request(status) - }) as AioCompleteFunc); + complete_cb.complete_request(status) + } - Ok(Box::new(Aio::new(complete_func, engine)?)) + fn build_aio(&self, engine: Option<&String>) -> Result>> { + Ok(Box::new(Aio::new(Arc::new(Self::complete_func), engine)?)) } fn aio_complete_handler(&mut self) -> Result { diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index fb8779cb5..405d048ce 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -35,7 +35,7 @@ use machine_manager::{ config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; -use util::aio::{Aio, AioCb, AioCompleteFunc, Iovec}; +use util::aio::{Aio, AioCb, Iovec}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -982,25 +982,25 @@ impl ScsiCmdHandler { Ok(()) } - fn build_aio(&self) -> Result>> { - let complete_fun = Arc::new(Box::new(move |aiocb: &AioCb, ret: i64| { - let complete_cb = &aiocb.iocompletecb; - let request = &aiocb.iocompletecb.req.lock().unwrap(); - let mut virtio_scsi_req = request.virtioscsireq.lock().unwrap(); + fn complete_func(aiocb: &AioCb, ret: i64) -> Result<()> { + let complete_cb = &aiocb.iocompletecb; + let request = &aiocb.iocompletecb.req.lock().unwrap(); + let mut virtio_scsi_req = request.virtioscsireq.lock().unwrap(); - virtio_scsi_req.resp.response = if ret < 0 { - VIRTIO_SCSI_S_FAILURE - } else { - VIRTIO_SCSI_S_OK - }; + virtio_scsi_req.resp.response = if ret < 0 { + VIRTIO_SCSI_S_FAILURE + } else { + VIRTIO_SCSI_S_OK + }; - virtio_scsi_req.resp.status = GOOD; - virtio_scsi_req.resp.resid = 0; - virtio_scsi_req.resp.sense_len = 0; - virtio_scsi_req.complete(&complete_cb.mem_space) - }) as AioCompleteFunc); + virtio_scsi_req.resp.status = GOOD; + virtio_scsi_req.resp.resid = 0; + virtio_scsi_req.resp.sense_len = 0; + virtio_scsi_req.complete(&complete_cb.mem_space) + } - Ok(Box::new(Aio::new(complete_fun, None)?)) + fn build_aio(&self) -> Result>> { + Ok(Box::new(Aio::new(Arc::new(Self::complete_func), None)?)) } fn deactivate_evt_handler(&mut self) -> Vec { -- Gitee From 3a882d35632dd248f4d8ef82872f7bf892f3c7ac Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 20:00:47 +0800 Subject: [PATCH 0602/2187] virtio-blk: Refactor execute() to reduce num of parameters Most of parameters are from iohandler, now we give reference of iohandler to it directly. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 73 +++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 5e1362bb6..6ae7f0e74 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -269,29 +269,11 @@ impl Request { Ok(request) } - #[allow(clippy::too_many_arguments)] fn execute( &self, - aio: &mut Box>, - disk: &File, - serial_num: &Option, - direct: bool, - aio_type: &Option, - last_aio: bool, - iocompletecb: AioCompleteCb, + iohandler: &mut BlockIoHandler, + mut aiocb: AioCb, ) -> Result<()> { - let mut aiocb = AioCb { - last_aio, - file_fd: disk.as_raw_fd(), - opcode: IoCmd::Noop, - iovec: Vec::new(), - offset: (self.out_header.sector << SECTOR_SHIFT) as usize, - nbytes: 0, - process: true, - iocb: None, - iocompletecb, - }; - let mut req = Some(self); while let Some(req_raw) = req { for iov in req_raw.iovec.iter() { @@ -314,18 +296,19 @@ impl Request { } } + let aio = iohandler.aio.as_mut().unwrap(); + let serial_num = &iohandler.serial_num; + let aio_type = &iohandler.aio_type; + let direct = iohandler.direct; match request_type { VIRTIO_BLK_T_IN => { aiocb.opcode = IoCmd::Preadv; if aio_type.is_some() { - (*aio) - .as_mut() - .rw_aio(aiocb, SECTOR_SIZE, direct) - .with_context(|| { - "Failed to process block request for reading asynchronously" - })?; + aio.rw_aio(aiocb, SECTOR_SIZE, direct).with_context(|| { + "Failed to process block request for reading asynchronously" + })?; } else { - (*aio).as_mut().rw_sync(aiocb).with_context(|| { + aio.rw_sync(aiocb).with_context(|| { "Failed to process block request for reading synchronously" })?; } @@ -333,23 +316,18 @@ impl Request { VIRTIO_BLK_T_OUT => { aiocb.opcode = IoCmd::Pwritev; if aio_type.is_some() { - (*aio) - .as_mut() - .rw_aio(aiocb, SECTOR_SIZE, direct) - .with_context(|| { - "Failed to process block request for writing asynchronously" - })?; + aio.rw_aio(aiocb, SECTOR_SIZE, direct).with_context(|| { + "Failed to process block request for writing asynchronously" + })?; } else { - (*aio).as_mut().rw_sync(aiocb).with_context(|| { + aio.rw_sync(aiocb).with_context(|| { "Failed to process block request for writing synchronously" })?; } } VIRTIO_BLK_T_FLUSH => { aiocb.opcode = IoCmd::Fdsync; - (*aio) - .as_mut() - .flush_sync(aiocb) + aio.flush_sync(aiocb) .with_context(|| "Failed to process block request for flushing")?; } VIRTIO_BLK_T_GET_ID => { @@ -558,15 +536,18 @@ impl BlockIoHandler { self.driver_features, ); if let Some(disk_img) = self.disk_image.as_ref() { - req_rc.execute( - self.aio.as_mut().unwrap(), - disk_img, - &self.serial_num, - self.direct, - &self.aio_type, - req_index == last_aio_req_index, - aiocompletecb, - )?; + let aiocb = AioCb { + last_aio: req_index == last_aio_req_index, + file_fd: disk_img.as_raw_fd(), + opcode: IoCmd::Noop, + iovec: Vec::new(), + offset: (req_rc.out_header.sector << SECTOR_SHIFT) as usize, + nbytes: 0, + process: true, + iocb: None, + iocompletecb: aiocompletecb, + }; + req_rc.execute(self, aiocb)?; } else { warn!("Failed to execute block request, disk_img not specified"); aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR)?; -- Gitee From 65a27d05d82713ccd5abf0ee69c444505306bdff Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 10:36:29 +0800 Subject: [PATCH 0603/2187] xhci: set xfer to completed before report transfer error The transfer is set to completed when an error occurs in the transfer and the transfer will not be retried. And only update dequeue pointer when transfer completed. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index d1fa4b39c..398798f31 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -192,16 +192,19 @@ impl XhciEpContext { Ok(()) } - /// Update the dequeue pointer in memory. - fn update_dequeue(&mut self, mem: &Arc, dequeue: u64) -> Result<()> { + /// Update the dequeue pointer in endpoint context. + /// If dequeue is None, only flush the dequeue pointer to memory. + fn update_dequeue(&mut self, mem: &Arc, dequeue: Option) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_mut_dwords(), )?; - self.ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); - self.ring.ccs = (dequeue & EP_CTX_DCS) == EP_CTX_DCS; + if let Some(dequeue) = dequeue { + self.ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); + self.ring.ccs = (dequeue & EP_CTX_DCS) == EP_CTX_DCS; + } ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; dma_write_u32(mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_dwords())?; @@ -1159,7 +1162,7 @@ impl XhciDevice { ); return Ok(TRBCCode::ContextStateError); } - epctx.update_dequeue(&self.mem_space, trb.parameter)?; + epctx.update_dequeue(&self.mem_space, Some(trb.parameter))?; Ok(TRBCCode::Success) } @@ -1215,7 +1218,7 @@ impl XhciDevice { self.endpoint_do_transfer(&mut xfer)?; let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if xfer.is_completed() { - epctx.set_state(&self.mem_space, epctx.state)?; + epctx.update_dequeue(&self.mem_space, None)?; epctx.flush_transfer(); } else { epctx.transfers.push_back(xfer.clone()); @@ -1279,7 +1282,7 @@ impl XhciDevice { self.complete_packet(xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if xfer.is_completed() { - epctx.set_state(&self.mem_space, epctx.state)?; + epctx.update_dequeue(&self.mem_space, None)?; epctx.flush_transfer(); } epctx.retry = None; @@ -1552,7 +1555,9 @@ impl XhciDevice { Ok(()) } - fn report_transfer_error(&mut self, xfer: &XhciTransfer) -> Result<()> { + fn report_transfer_error(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + // An error occurs in the transfer. The transfer is set to the completed and will not be retried. + xfer.set_comleted(true); let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); evt.slot_id = xfer.slotid as u8; evt.ep_id = xfer.epid as u8; -- Gitee From b77ee945f52204c8120bbe54c8209229689641da Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 22:23:29 +0800 Subject: [PATCH 0604/2187] virtio-pci: Drop unnecessary atomic type of msix_config ... which is already protected by common_config lock. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 69c4053a8..7895d7aa1 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -161,8 +161,8 @@ struct VirtioPciCommonConfig { config_generation: u8, /// Queue selector. queue_select: u16, - /// The configuration vector for MSI-X. - msix_config: Arc, + /// The MSI-X vector for config change notification. + msix_config: u16, /// The configuration of queues. queues_config: Vec, /// The type of queue, split-vring or packed-vring. @@ -183,7 +183,7 @@ impl VirtioPciCommonConfig { device_status: 0, config_generation: 0, queue_select: 0, - msix_config: Arc::new(AtomicU16::new(INVALID_VECTOR_NUM)), + msix_config: INVALID_VECTOR_NUM, queues_config, queue_type: QUEUE_TYPE_SPLIT_VRING, } @@ -196,7 +196,7 @@ impl VirtioPciCommonConfig { self.device_status = 0; self.config_generation = 0; self.queue_select = 0; - self.msix_config.store(INVALID_VECTOR_NUM, Ordering::SeqCst); + self.msix_config = INVALID_VECTOR_NUM; self.queue_type = QUEUE_TYPE_SPLIT_VRING; self.queues_config.iter_mut().for_each(|q| q.reset()); } @@ -278,7 +278,7 @@ impl VirtioPciCommonConfig { 0 } } - COMMON_MSIX_REG => self.msix_config.load(Ordering::SeqCst) as u32, + COMMON_MSIX_REG => self.msix_config as u32, COMMON_NUMQ_REG => self.queues_config.len() as u32, COMMON_STATUS_REG => self.device_status, COMMON_CFGGENERATION_REG => self.config_generation as u32, @@ -368,7 +368,7 @@ impl VirtioPciCommonConfig { } COMMON_MSIX_REG => { let val = self.revise_queue_vector(value, virtio_pci_dev); - self.msix_config.store(val as u16, Ordering::SeqCst); + self.msix_config = val as u16; self.interrupt_status.store(0_u32, Ordering::SeqCst); } COMMON_STATUS_REG => { @@ -667,7 +667,7 @@ impl VirtioPciDevice { Ordering::SeqCst, ); locked_common_cfg.config_generation += 1; - locked_common_cfg.msix_config.load(Ordering::SeqCst) + locked_common_cfg.msix_config } VirtioInterruptType::Vring => { queue.map_or(0, |q| q.vring.get_queue_config().vector) @@ -1341,7 +1341,7 @@ impl StateTransfer for VirtioPciDevice { { let common_config = self.common_config.lock().unwrap(); state.interrupt_status = common_config.interrupt_status.load(Ordering::SeqCst); - state.msix_config = common_config.msix_config.load(Ordering::SeqCst); + state.msix_config = common_config.msix_config; state.features_select = common_config.features_select; state.acked_features_select = common_config.acked_features_select; state.device_status = common_config.device_status; @@ -1385,9 +1385,7 @@ impl StateTransfer for VirtioPciDevice { common_config .interrupt_status .store(pci_state.interrupt_status, Ordering::SeqCst); - common_config - .msix_config - .store(pci_state.msix_config, Ordering::SeqCst); + common_config.msix_config = pci_state.msix_config; common_config.features_select = pci_state.features_select; common_config.acked_features_select = pci_state.acked_features_select; common_config.device_status = pci_state.device_status; -- Gitee From bc6026b9401ae997b3330b1944b749144e4f4481 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Dec 2022 22:40:54 +0800 Subject: [PATCH 0605/2187] virtio-pci: Drop unnecessary atomic type of interrupt_status ... which is already protected by common_cfg lock. Signed-off-by: Keqian Zhu --- virtio/src/lib.rs | 3 +-- virtio/src/virtio_pci.rs | 30 ++++++++++++------------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index bde636ab9..c117970cc 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -325,8 +325,7 @@ pub trait VirtioDevice: Send { /// # Arguments /// /// * `mem_space` - System mem. - /// * `interrupt_evt` - The eventfd used to send interrupt to guest. - /// * `interrupt_status` - The interrupt status present to guest. + /// * `interrupt_cb` - The callback used to send interrupt to guest. /// * `queues` - The virtio queues. /// * `queue_evts` - The notifier events from guest. fn activate( diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 7895d7aa1..ef51e69ff 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -13,7 +13,7 @@ use std::cmp::{max, min}; use std::collections::HashMap; use std::mem::size_of; -use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; @@ -154,7 +154,7 @@ struct VirtioPciCommonConfig { /// Device (host) feature-setting selector. acked_features_select: u32, /// Interrupt status. - interrupt_status: Arc, + interrupt_status: u32, /// Device status. device_status: u32, /// Configuration atomicity value. @@ -179,7 +179,7 @@ impl VirtioPciCommonConfig { VirtioPciCommonConfig { features_select: 0, acked_features_select: 0, - interrupt_status: Arc::new(AtomicU32::new(0)), + interrupt_status: 0, device_status: 0, config_generation: 0, queue_select: 0, @@ -192,7 +192,7 @@ impl VirtioPciCommonConfig { fn reset(&mut self) { self.features_select = 0; self.acked_features_select = 0; - self.interrupt_status.store(0_u32, Ordering::SeqCst); + self.interrupt_status = 0; self.device_status = 0; self.config_generation = 0; self.queue_select = 0; @@ -369,7 +369,7 @@ impl VirtioPciCommonConfig { COMMON_MSIX_REG => { let val = self.revise_queue_vector(value, virtio_pci_dev); self.msix_config = val as u16; - self.interrupt_status.store(0_u32, Ordering::SeqCst); + self.interrupt_status = 0; } COMMON_STATUS_REG => { if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { @@ -662,10 +662,8 @@ impl VirtioPciDevice { } // Use (CONFIG | VRING) instead of CONFIG, it can be used to solve the // IO stuck problem by change the device configure. - locked_common_cfg.interrupt_status.fetch_or( - VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING, - Ordering::SeqCst, - ); + locked_common_cfg.interrupt_status |= + VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING; locked_common_cfg.config_generation += 1; locked_common_cfg.msix_config } @@ -943,11 +941,9 @@ impl VirtioPciDevice { let cloned_common_cfg = self.common_config.clone(); let isr_read = move |data: &mut [u8], _: GuestAddress, _: u64| -> bool { if let Some(val) = data.get_mut(0) { - *val = cloned_common_cfg - .lock() - .unwrap() - .interrupt_status - .swap(0, Ordering::SeqCst) as u8; + let mut common_cfg_lock = cloned_common_cfg.lock().unwrap(); + *val = common_cfg_lock.interrupt_status as u8; + common_cfg_lock.interrupt_status = 0; } true }; @@ -1340,7 +1336,7 @@ impl StateTransfer for VirtioPciDevice { // Save virtio pci common config state. { let common_config = self.common_config.lock().unwrap(); - state.interrupt_status = common_config.interrupt_status.load(Ordering::SeqCst); + state.interrupt_status = common_config.interrupt_status; state.msix_config = common_config.msix_config; state.features_select = common_config.features_select; state.acked_features_select = common_config.acked_features_select; @@ -1382,9 +1378,7 @@ impl StateTransfer for VirtioPciDevice { // Set virtio pci common config state. { let mut common_config = self.common_config.lock().unwrap(); - common_config - .interrupt_status - .store(pci_state.interrupt_status, Ordering::SeqCst); + common_config.interrupt_status = pci_state.interrupt_status; common_config.msix_config = pci_state.msix_config; common_config.features_select = pci_state.features_select; common_config.acked_features_select = pci_state.acked_features_select; -- Gitee From 335c4226d910bda36b14d2453027e0bbd72b22e6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Dec 2022 02:06:54 +0800 Subject: [PATCH 0606/2187] virtio-pci: Fix error handling of getting ioeventfds It's a fatal error if failed to clone eventfd, log error is not enough. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index ef51e69ff..a21199531 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -688,17 +688,11 @@ impl VirtioPciDevice { fn ioeventfds(&self) -> Vec { let mut ret = Vec::new(); - for (index, eventfd) in self.notify_eventfds.events.iter().enumerate() { + let eventfds = self.notify_eventfds.clone(); + for (index, eventfd) in eventfds.events.into_iter().enumerate() { let addr = index as u64 * u64::from(VIRTIO_PCI_CAP_NOTIFY_OFF_MULTIPLIER); - let eventfd_clone = match eventfd.try_clone() { - Err(e) => { - error!("Failed to clone ioeventfd, error is {}", e); - continue; - } - Ok(fd) => fd, - }; ret.push(RegionIoEventFd { - fd: eventfd_clone, + fd: eventfd, addr_range: AddressRange::from((addr, 2u64)), data_match: false, data: index as u64, -- Gitee From 89be1a481cc18c281c048160732f4e5a56d205dd Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 11:47:06 +0800 Subject: [PATCH 0607/2187] VNC: Add console The console layer can connect the display device with display desktop. The display device can put the image to the console, the display ui take the image from the console and display it on the user's desktop. Signed-off-by: Xiao Ye --- vnc/src/console.rs | 568 +++++++++++++++++++++++++++++++++++++++++++++ vnc/src/lib.rs | 1 + 2 files changed, 569 insertions(+) create mode 100644 vnc/src/console.rs diff --git a/vnc/src/console.rs b/vnc/src/console.rs new file mode 100644 index 000000000..916ce1060 --- /dev/null +++ b/vnc/src/console.rs @@ -0,0 +1,568 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::pixman::{ + get_image_height, get_image_width, pixman_glyph_from_vgafont, pixman_glyph_render, + unref_pixman_image, QemuColorNames, COLOR_TABLE_RGB, +}; +use log::error; +use machine_manager::event_loop::EventLoop; +use once_cell::sync::Lazy; +use std::{ + cmp, ptr, + rc::Rc, + sync::{Arc, Mutex}, +}; +use util::pixman::{pixman_format_code_t, pixman_image_create_bits, pixman_image_t}; + +/// Width of font. +const FONT_WIDTH: i32 = 8; +/// Height of font. +const FONT_HEIGHT: i32 = 16; +/// Width of image in surface. +const DEFAULT_SURFACE_WIDTH: i32 = 640; +/// Height of image in surface. +const DEFAULT_SURFACE_HEIGHT: i32 = 480; +/// Maximum default window width. +pub const MAX_WINDOW_WIDTH: u16 = 2560; +/// Maximum default window height. +pub const MAX_WINDOW_HEIGHT: u16 = 2048; + +/// Minimum refresh interval in ms. +pub const DISPLAY_UPDATE_INTERVAL_DEFAULT: u64 = 30; +/// Update time interval dynamically. +pub const DISPLAY_UPDATE_INTERVAL_INC: u64 = 50; +/// Maximum refresh interval in ms. +pub const DISPLAY_UPDATE_INTERVAL_MAX: u64 = 3_000; +/// Millisecond to nanosecond. +pub const MILLI_PER_SEC: u64 = 1_000_000; + +/// Image data defined in display. +#[derive(Clone, Copy)] +pub struct DisplaySurface { + /// Image format. + pub format: pixman_format_code_t, + /// Pointer to image + pub image: *mut pixman_image_t, +} + +impl Default for DisplaySurface { + fn default() -> Self { + DisplaySurface { + format: pixman_format_code_t::PIXMAN_a8r8g8b8, + image: ptr::null_mut(), + } + } +} + +/// Cursor data defined in Display. +/// hot_x and hot_y indicate the hotspot of the cursor. +/// width and height indicate the width of the cursor in pixel. +/// The data consists of the primary and secondary colours for +/// the cursor, followed by one bitmap for the colour and +/// one bitmask for the transparency. +#[derive(Clone, Default)] +pub struct DisplayMouse { + pub width: u32, + pub height: u32, + pub hot_x: u32, + pub hot_y: u32, + pub data: Vec, +} + +/// UIs (such as VNC) can register interfaces related to image display. +/// After the graphic hardware processes images, these interfaces can be +/// called to display images on the user's desktop. +pub trait DisplayChangeListenerOperations { + /// Switch the image in display surface. + fn dpy_switch(&self, _surface: &DisplaySurface) {} + /// Refresh the image. + fn dpy_refresh(&self, _dcl: &mut DisplayChangeListener) {} + /// Update image. + fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) {} + /// Update the cursor data. + fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) {} +} + +/// Callback functions registered by graphic hardware. +pub trait HardWareOperations { + /// Update image. + fn hw_update(&self, _con_id: Option) {} +} + +/// Listen to the change of image and call the related +/// interface to update the image on user's desktop. +pub struct DisplayChangeListener { + pub con_id: Option, + pub active: bool, + pub update_interval: u64, + pub dpy_opts: Rc, +} + +impl DisplayChangeListener { + pub fn new(dpy_opts: Rc) -> Self { + Self { + con_id: None, + active: false, + update_interval: 0, + dpy_opts, + } + } +} + +/// Graphic hardware can register a console during initialization +/// and store the information of images in this structure. +pub struct DisplayConsole { + pub width: i32, + pub height: i32, + pub surface: Option, + dev_opts: Rc, +} + +impl DisplayConsole { + pub fn new(dev_opts: Rc) -> Self { + Self { + width: DEFAULT_SURFACE_WIDTH, + height: DEFAULT_SURFACE_HEIGHT, + surface: None, + dev_opts, + } + } +} + +/// The state of console layer. +pub struct DisplayState { + /// Refresh interval, which can be dynamic changed. + pub interval: u64, + /// Whether there is a refresh task. + is_refresh: bool, + /// Total number of refresh task. + refresh_num: i32, +} + +impl DisplayState { + fn new() -> Self { + Self { + interval: DISPLAY_UPDATE_INTERVAL_DEFAULT, + is_refresh: false, + refresh_num: 0, + } + } +} + +/// The registered console will be inserted in the console list. +/// If no console is specified, the activate console will be used. +pub struct ConsoleList { + pub activate_id: Option, + pub console_list: Vec>, +} + +impl ConsoleList { + fn new() -> Self { + Self { + activate_id: None, + console_list: Vec::new(), + } + } +} + +/// Refresh display image. +pub fn display_refresh() { + let mut dcl_interval: u64; + let mut interval: u64 = DISPLAY_UPDATE_INTERVAL_MAX; + let mut locked_state = DISPLAY_STATE.lock().unwrap(); + + // Update refresh interval. + unsafe { + for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { + if !dcl.active { + continue; + } + let dcl_opts = dcl.dpy_opts.clone(); + (*dcl_opts).dpy_refresh(dcl); + + dcl_interval = dcl.update_interval; + if dcl_interval == 0 { + dcl_interval = DISPLAY_UPDATE_INTERVAL_MAX; + } + + if interval > dcl_interval { + interval = dcl_interval; + } + } + } + + locked_state.interval = interval; + if locked_state.interval != 0 { + locked_state.is_refresh = true; + setup_refresh(interval); + } +} + +/// Register the timer to execute the scheduled +/// refresh task. +pub fn setup_refresh(update_interval: u64) { + let func = Box::new(move || { + display_refresh(); + }); + + if update_interval != 0 { + if let Some(ctx) = EventLoop::get_ctx(None) { + ctx.delay_call(func, update_interval as u64 * MILLI_PER_SEC); + } + } +} + +/// Switch the image of surface in display. +pub fn display_replace_surface(con_id: Option, surface: Option) { + let _locked_state = DISPLAY_STATE.lock().unwrap(); + let consoles = get_console_by_id(con_id); + let mut con: &mut DisplayConsole; + if let Some(c) = consoles { + con = c; + } else { + return; + } + + if surface.is_none() { + // Create a place holder message. + con.surface = + create_msg_surface(con.width, con.height, "Display is not active.".to_string()); + } else { + con.surface = surface; + } + + if let Some(s) = con.surface { + con.width = get_image_width(s.image); + con.height = get_image_height(s.image); + } + + unsafe { + let activate_id = CONSOLES.activate_id; + for dcl in DISPLAY_LISTS.iter_mut().flatten() { + let dcl_id = if dcl.con_id.is_none() { + activate_id + } else { + dcl.con_id + }; + + if con_id == dcl_id { + let dcl_ops = dcl.dpy_opts.clone(); + if let Some(s) = &con.surface { + (*dcl_ops).dpy_switch(s); + } + } + } + } + drop(_locked_state); +} + +/// Update area of the image. +/// `x` `y` `w` `h` marke the area of image. +pub fn display_graphic_update(con_id: Option, x: i32, y: i32, w: i32, h: i32) { + if con_id.is_none() { + return; + } + + let mut width: i32 = w; + let mut height: i32 = h; + if let Some(con) = get_console_by_id(con_id) { + width = con.width; + height = con.height; + } + let mut x = cmp::max(x, 0); + let mut y = cmp::max(y, 0); + x = cmp::min(x, width); + y = cmp::min(y, height); + let w = cmp::min(w, width - x); + let h = cmp::min(h, height - y); + + unsafe { + let activate_id = CONSOLES.activate_id; + for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { + let dcl_id = if dcl.con_id.is_none() { + activate_id + } else { + dcl.con_id + }; + + if con_id == dcl_id { + let dcl_ops = dcl.dpy_opts.clone(); + (*dcl_ops).dpy_image_update(x, y, w, h); + } + } + } +} + +/// Update cursor data in dispaly. +/// +/// # Arguments +/// +/// * `con_id` - console id in console list. +/// * `cursor` - data of curosr image. +pub fn display_cursor_define(con_id: Option, cursor: &mut DisplayMouse) { + unsafe { + let activate_id = CONSOLES.activate_id; + for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { + let dcl_id = if dcl.con_id.is_none() { + activate_id + } else { + dcl.con_id + }; + + if con_id == dcl_id { + let dcl_ops = dcl.dpy_opts.clone(); + (*dcl_ops).dpy_cursor_update(cursor); + } + } + } +} + +pub fn graphic_hardware_update(con_id: Option) { + let id: Option; + if con_id.is_none() { + unsafe { + id = CONSOLES.activate_id; + } + } else { + id = con_id; + } + + if let Some(con) = get_console_by_id(id) { + let con_opts = con.dev_opts.clone(); + (*con_opts).hw_update(id); + } +} + +/// Register a dcl and return the id. +pub fn register_display(dcl_opts: Rc) -> Option { + let mut dcl_id = 0; + let mut locked_state = DISPLAY_STATE.lock().unwrap(); + let mut dcl = DisplayChangeListener::new(dcl_opts.clone()); + dcl.active = true; + let con_id = dcl.con_id; + unsafe { + let len = DISPLAY_LISTS.len(); + for dcl in &mut DISPLAY_LISTS.iter_mut() { + if dcl.is_none() { + break; + } + dcl_id += 1; + } + + if dcl_id < len { + DISPLAY_LISTS[dcl_id] = Some(dcl); + } else { + DISPLAY_LISTS.push(Some(dcl)); + } + } + + locked_state.refresh_num += 1; + // Register the clock and execute the scheduled refresh event. + if !locked_state.is_refresh && locked_state.interval != 0 { + locked_state.is_refresh = true; + setup_refresh(locked_state.interval); + } + + let console = get_console_by_id(con_id); + if let Some(con) = console { + if let Some(surface) = &mut con.surface { + (*dcl_opts).dpy_switch(surface); + } + } else { + let mut place_holder_image = create_msg_surface( + DEFAULT_SURFACE_WIDTH, + DEFAULT_SURFACE_HEIGHT, + "This VM has no graphic display device.".to_string(), + ); + if let Some(surface) = &mut place_holder_image { + (*dcl_opts).dpy_switch(surface); + } + } + + drop(locked_state); + Some(dcl_id) +} + +/// Unregister display change listener. +pub fn unregister_display(id: Option) { + if id.is_none() { + return; + } + + let mut locked_state = DISPLAY_STATE.lock().unwrap(); + unsafe { + let len = DISPLAY_LISTS.len(); + if let Some(i) = id { + if i < len { + DISPLAY_LISTS[i] = None; + } + } + } + + // Stop refreshing if the current refreshing num is 0 + locked_state.refresh_num -= 1; + if locked_state.refresh_num <= 0 { + locked_state.is_refresh = false; + } + + drop(locked_state); +} + +/// Create a console and add into a gloabl list. Then returen a console id +/// for later finding the assigned console. +pub fn console_init(dev_opts: Rc) -> Option { + let locked_state = DISPLAY_STATE.lock().unwrap(); + let mut new_console = DisplayConsole::new(dev_opts); + new_console.surface = create_msg_surface( + DEFAULT_SURFACE_WIDTH, + DEFAULT_SURFACE_HEIGHT, + "Guest has not initialized the display yet.".to_string(), + ); + + let mut con_id: usize; + unsafe { + let len = CONSOLES.console_list.len(); + con_id = len; + for idx in 0..len { + if CONSOLES.console_list[idx].is_none() { + con_id = idx; + break; + } + } + if con_id < len { + CONSOLES.console_list[con_id] = Some(new_console); + } else { + CONSOLES.console_list.push(Some(new_console)); + } + + if CONSOLES.activate_id.is_none() { + CONSOLES.activate_id = Some(con_id); + } + } + + drop(locked_state); + display_replace_surface(Some(con_id), None); + + Some(con_id) +} + +/// Select the default display device. +/// If con_id is none, then do nothing. +pub fn console_select(con_id: Option) { + if con_id.is_none() { + return; + } + + let _locked_state = DISPLAY_STATE.lock().unwrap(); + unsafe { + if con_id == CONSOLES.activate_id { + return; + } + + if let Some(con) = get_console_by_id(con_id) { + CONSOLES.activate_id = con_id; + for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { + if dcl.con_id.is_some() { + continue; + } + + let dpy_opts = dcl.dpy_opts.clone(); + if let Some(s) = &con.surface { + (*dpy_opts).dpy_switch(s); + } + } + + display_graphic_update(con_id, 0, 0, con.width, con.height); + } + } +} + +/// Get the console by id. +pub fn get_console_by_id(con_id: Option) -> Option<&'static mut DisplayConsole> { + unsafe { + let mut target_id: usize = 0; + if con_id.is_none() && CONSOLES.activate_id.is_none() { + return None; + } + + if let Some(id) = con_id { + target_id = id; + } else if let Some(id) = CONSOLES.activate_id { + target_id = id; + } + + if let Some(con) = CONSOLES.console_list.get_mut(target_id) { + match con { + Some(c) => { + return Some(c); + } + None => { + return None; + } + } + } + None + } +} + +/// Create a default image to display messages. +/// +/// # Arguments +/// +/// * `width` - width of image. +/// * `height` - height of image. +/// * `msg` - test messages showed in display. +fn create_msg_surface(width: i32, height: i32, msg: String) -> Option { + if !(0..MAX_WINDOW_WIDTH as i32).contains(&width) + || !(0..MAX_WINDOW_HEIGHT as i32).contains(&height) + { + error!("The size of image is invalid!"); + return None; + } + let mut surface = DisplaySurface::default(); + + // One pixel occupies four bytes. + unsafe { + surface.image = + pixman_image_create_bits(surface.format, width, height, ptr::null_mut(), width * 4); + } + if surface.image.is_null() { + error!("create default surface failed!"); + return None; + } + + let fg = COLOR_TABLE_RGB[0][QemuColorNames::QemuColorWhite as usize]; + let bg = COLOR_TABLE_RGB[0][QemuColorNames::QemuColorBlack as usize]; + let x = (width / FONT_WIDTH - msg.len() as i32) / 2; + let y = (height / FONT_HEIGHT - 1) / 2; + + for (index, ch) in msg.chars().enumerate() { + let glyph = pixman_glyph_from_vgafont(FONT_HEIGHT, ch as u32); + pixman_glyph_render( + glyph, + surface.image, + &fg, + &bg, + (x + index as i32, y), + FONT_WIDTH, + FONT_HEIGHT, + ); + unref_pixman_image(glyph); + } + Some(surface) +} + +static mut CONSOLES: Lazy = Lazy::new(ConsoleList::new); +static mut DISPLAY_LISTS: Lazy>> = Lazy::new(Vec::new); +static DISPLAY_STATE: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new(DisplayState::new()))); diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index 0455bf9bf..c7e2b4462 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -16,6 +16,7 @@ pub use error::VncError; pub mod auth; pub mod client; +pub mod console; mod data; pub mod encoding; pub mod input; -- Gitee From 67ea723b955741c9dd949d89ab56ee700b12354a Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 11:52:36 +0800 Subject: [PATCH 0608/2187] Ramfb: adjust display interface Use the interface of the console to interact with vnc. Signed-off-by: Xiao Ye --- devices/src/legacy/ramfb.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 6abe6e917..10b2cec63 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -19,11 +19,15 @@ use drm_fourcc::DrmFourcc; use log::error; use migration_derive::ByteCode; use std::mem::size_of; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use util::byte_code::ByteCode; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; -use vnc::vnc::{vnc_display_switch, vnc_loop_update_display, DisplaySurface}; +use vnc::console::{ + console_init, display_graphic_update, display_replace_surface, get_console_by_id, + DisplaySurface, HardWareOperations, +}; const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; @@ -184,8 +188,20 @@ impl FwCfgWriteCallback for RamfbState { self.create_display_surface(width, height, format, stride, addr); - vnc_display_switch(&self.surface.unwrap()); - vnc_loop_update_display(0, 0, width as i32, height as i32); + let ramfb_opts = Rc::new(RamfbInterface::default()); + let con_id = console_init(ramfb_opts); + display_replace_surface(con_id, self.surface); + } +} + +#[derive(Default)] +pub struct RamfbInterface {} +impl HardWareOperations for RamfbInterface { + fn hw_update(&self, con_id: Option) { + let con = get_console_by_id(con_id); + if let Some(c) = con { + display_graphic_update(con_id, 0, 0, c.width, c.height); + } } } -- Gitee From e77435148437b75498c495e591d78e9b476519ce Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 11:52:56 +0800 Subject: [PATCH 0609/2187] Virtio-gpu: adjust display interface Use the interface of the console to interact with vnc. Signed-off-by: Xiao Ye --- virtio/src/gpu.rs | 112 +++++++++------------------------------------- 1 file changed, 22 insertions(+), 90 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index f8cbf8ace..a4e69d575 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -26,6 +26,7 @@ use std::cmp; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{ptr, vec}; use util::byte_code::ByteCode; @@ -43,13 +44,11 @@ use util::pixman::{ }; use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use vnc::pixman::{ - pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, QemuColorNames, - COLOR_TABLE_RGB, -}; -use vnc::vnc::{ - vnc_display_cursor, vnc_display_switch, vnc_display_update, DisplayMouse, DisplaySurface, +use vnc::console::{ + console_init, display_cursor_define, display_graphic_update, display_replace_surface, + DisplayMouse, DisplaySurface, HardWareOperations, }; + /// Number of virtqueues. const QUEUE_NUM_GPU: usize = 2; /// Size of each virtqueue. @@ -337,6 +336,10 @@ pub struct VirtioGpuResourceDetachBacking { impl ByteCode for VirtioGpuResourceDetachBacking {} +#[derive(Default)] +pub struct GpuOpts {} +impl HardWareOperations for GpuOpts {} + #[allow(unused)] #[derive(Default, Clone)] pub struct VirtioGpuRequest { @@ -461,6 +464,7 @@ impl ByteCode for VirtioGpuUpdateCursor {} #[derive(Default)] struct GpuScanout { + con_id: Option, surface: Option, mouse: Option, width: u32, @@ -514,34 +518,6 @@ struct GpuIoHandler { used_hostmem: u64, } -fn display_define_mouse(mouse: &mut Option) { - if let Some(mouse) = mouse { - vnc_display_cursor(mouse); - } -} - -fn display_replace_surface(surface: Option) { - match surface { - Some(surface) => { - vnc_display_switch(&surface); - } - None => unsafe { - match MSG_SURFACE { - Some(surface) => { - vnc_display_switch(&surface); - } - None => { - error!("Default msg surface is none, check is needed") - } - } - }, - } -} - -fn display_update(x: i32, y: i32, w: i32, h: i32) { - vnc_display_update(x, y, w, h); -} - fn create_surface( scanout: &mut GpuScanout, info_set_scanout: VirtioGpuSetScanout, @@ -571,57 +547,12 @@ fn create_surface( // update surface in scanout. scanout.surface = Some(surface); pixman_image_unref(rect); - display_replace_surface(scanout.surface); + display_replace_surface(scanout.con_id, scanout.surface); } }; surface } -const FONT_HEIGHT: i32 = 16; -const FONT_WIDTH: i32 = 8; -const MSG_SURFACE_HEIGHT: i32 = 480; -const MSG_SURFACE_WIDTH: i32 = 640; - -fn create_msg_surface() -> Option { - let mut surface = DisplaySurface::default(); - unsafe { - surface.format = pixman_format_code_t::PIXMAN_x8r8g8b8; - surface.image = pixman_image_create_bits( - surface.format, - MSG_SURFACE_WIDTH, - MSG_SURFACE_HEIGHT, - ptr::null_mut(), - MSG_SURFACE_WIDTH * 4, - ); - if surface.image.is_null() { - error!("create default surface failed!"); - return None; - } - } - let msg = String::from("Display is not active now"); - let fg = COLOR_TABLE_RGB[0][QemuColorNames::QemuColorWhite as usize]; - let bg = COLOR_TABLE_RGB[0][QemuColorNames::QemuColorBlack as usize]; - let x = (MSG_SURFACE_WIDTH / FONT_WIDTH - msg.len() as i32) / 2; - let y = (MSG_SURFACE_HEIGHT / FONT_HEIGHT - 1) / 2; - - for (index, ch) in msg.chars().enumerate() { - let glyph = pixman_glyph_from_vgafont(FONT_HEIGHT, ch as u32); - pixman_glyph_render( - glyph, - surface.image, - &fg, - &bg, - (x + index as i32, y), - FONT_WIDTH, - FONT_HEIGHT, - ); - unref_pixman_image(glyph); - } - Some(surface) -} - -static mut MSG_SURFACE: Option = None; - impl GpuIoHandler { fn gpu_get_pixman_format(&mut self, format: u32) -> Result { match format { @@ -711,7 +642,9 @@ impl GpuIoHandler { } } } - display_define_mouse(&mut scanout.mouse); + if let Some(mouse) = &mut scanout.mouse { + display_cursor_define(scanout.con_id, mouse); + } scanout.cursor = info_cursor; } else { bail!("Wrong header type for cursor queue"); @@ -955,7 +888,7 @@ impl GpuIoHandler { if scanout.resource_id != 0 { // disable the scanout. res.scanouts_bitmask &= !(1 << i); - display_replace_surface(None); + display_replace_surface(scanout.con_id, None); scanout.clear(); } } @@ -1040,7 +973,7 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_index]; res.scanouts_bitmask &= !(1 << info_set_scanout.scanout_id); } - display_replace_surface(None); + display_replace_surface(scanout.con_id, None); scanout.clear(); return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req); } @@ -1238,7 +1171,8 @@ impl GpuIoHandler { -(scanout.y as i32), ); let extents = pixman_region_extents(final_reg_ptr); - display_update( + display_graphic_update( + scanout.con_id, (*extents).x1 as i32, (*extents).y1 as i32, ((*extents).x2 - (*extents).x1) as i32, @@ -1779,6 +1713,7 @@ impl VirtioDevice for Gpu { self.state.base_conf.flags &= !(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); self.state.config.num_scanouts = self.state.base_conf.max_outputs; self.state.config.reserved = 0; + Ok(()) } @@ -1859,16 +1794,13 @@ impl VirtioDevice for Gpu { queues.len() ))); } - - unsafe { - MSG_SURFACE = create_msg_surface(); - } - self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; let mut scanouts = vec![]; for _i in 0..VIRTIO_GPU_MAX_SCANOUTS { - let scanout = GpuScanout::default(); + let mut scanout = GpuScanout::default(); + let gpu_opts = Rc::new(GpuOpts::default()); + scanout.con_id = console_init(gpu_opts); scanouts.push(scanout); } -- Gitee From 039038cae1ec61a07d0c7ba113eaa4eb994300c0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 14:45:47 +0800 Subject: [PATCH 0610/2187] VNC: adjust the interface with display device Use the interface of the console to interact with the display device. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 9 +- vnc/src/input.rs | 44 +++++- vnc/src/server.rs | 103 ++++-------- vnc/src/vnc.rs | 387 ++++++++++++++++++++++++---------------------- 4 files changed, 265 insertions(+), 278 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index db1258d11..b9c2258e7 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -12,13 +12,14 @@ use crate::{ auth::AuthState, + console::DisplayMouse, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, round_up_div, - server::{vnc_refresh_notify, VncServer}, + server::VncServer, utils::BuffPool, vnc::{ - framebuffer_upadate, set_area_dirty, write_pixel, DisplayMouse, BIT_PER_BYTE, - DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, MIN_OUTPUT_LIMIT, + framebuffer_upadate, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, + DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, }, VncError, @@ -892,8 +893,6 @@ impl ClientIoHandler { ); } drop(locked_state); - let server = self.server.clone(); - vnc_refresh_notify(&server); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } diff --git a/vnc/src/input.rs b/vnc/src/input.rs index f0ed50e0e..8b4bfa349 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -13,9 +13,12 @@ use crate::{ client::ClientIoHandler, pixman::{get_image_height, get_image_width}, + vnc::BIT_PER_BYTE, }; use log::error; use usb::{keyboard::keyboard_event, tablet::pointer_event, INPUT}; +use util::bitmap::Bitmap; + // Logical window size for mouse. const ABS_MAX: u64 = 0x7fff; // Up flag. @@ -33,6 +36,39 @@ const ASCII_A: i32 = 65; const ASCII_Z: i32 = 90; const UPPERCASE_TO_LOWERCASE: i32 = 32; +// Keyboard Modifier State +pub enum KeyboardModifier { + KeyModNone = 0, + KeyModShift = 1, + KeyModCtrl = 2, + KeyModAlt = 3, + KeyModAltgr = 4, + KeyModNumlock = 5, + KeyModCapslock = 6, + KeyModMax = 7, +} + +/// Record the keyboard status, +/// Including the press information of keys, +/// and some status information. +pub struct KeyBoardState { + /// Keyboard state. + pub keystate: Bitmap, + /// Key Modifier states. + pub keymods: Bitmap, +} + +impl KeyBoardState { + pub fn new(key_num: usize) -> Self { + Self { + keystate: Bitmap::new(key_num / (BIT_PER_BYTE as usize) + 1), + keymods: Bitmap::new( + KeyboardModifier::KeyModMax as usize / (BIT_PER_BYTE as usize) + 1, + ), + } + } +} + impl ClientIoHandler { /// Keyboard event. pub fn key_envent(&mut self) { @@ -49,13 +85,7 @@ impl ClientIoHandler { keysym += UPPERCASE_TO_LOWERCASE; } - let keycode: u16 = match self - .server - .keysym2keycode - .lock() - .unwrap() - .get(&(keysym as u16)) - { + let keycode: u16 = match self.server.keysym2keycode.get(&(keysym as u16)) { Some(k) => *k, None => 0, }; diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 6e1d6c76f..f48b175ae 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -13,8 +13,9 @@ use crate::{ auth::SaslAuth, auth::{AuthState, SaslConfig, SubAuthState}, - client::{get_rects, ClientIoHandler, ClientState}, - data::keycode::KEYSYM2KEYCODE, + client::{ClientIoHandler, ClientState}, + console::DisplayMouse, + input::KeyBoardState, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, get_image_width, unref_pixman_image, @@ -22,9 +23,8 @@ use crate::{ round_up_div, vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, vnc::{ - update_server_surface, DisplayMouse, DIRTY_PIXELS_NUM, DISPLAY_UPDATE_INTERVAL_DEFAULT, - DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, MAX_WINDOW_HEIGHT, - MAX_WINDOW_WIDTH, VNC_BITMAP_WIDTH, VNC_SERVERS, + update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, + VNC_BITMAP_WIDTH, VNC_SERVERS, }, VncError, }; @@ -50,28 +50,28 @@ use util::{ pixman_image_t, pixman_op_t, }, }; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use vmm_sys_util::epoll::EventSet; const CONNECTION_LIMIT: usize = 1; /// Information of VncServer. pub struct VncServer { - /// Event fd for vnc refresh. - pub refresh_fd: Arc>, /// Client io handler. pub client_handlers: Arc>>>>, /// Security Type for connection. pub security_type: Arc>, + /// keyboard status. + pub keyboard_state: Arc>, /// Mapping ASCII to keycode. - pub keysym2keycode: Arc>>, + pub keysym2keycode: HashMap, /// Image data of surface. pub vnc_surface: Arc>, /// Data for cursor image. pub vnc_cursor: Arc>, + /// Display Change Listener. + pub display_listener: Arc>, /// Connection limit. conn_limits: usize, - /// Updating interval of display devices. - pub update_interval: Arc>, } unsafe impl Send for VncServer {} @@ -79,16 +79,20 @@ unsafe impl Sync for VncServer {} impl VncServer { /// Create a new VncServer. - pub fn new(refresh_fd: Arc>, guest_image: *mut pixman_image_t) -> Self { + pub fn new( + guest_image: *mut pixman_image_t, + keyboard_state: Arc>, + keysym2keycode: HashMap, + ) -> Self { VncServer { - refresh_fd, client_handlers: Arc::new(Mutex::new(HashMap::new())), security_type: Arc::new(Mutex::new(SecurityType::default())), - keysym2keycode: Arc::new(Mutex::new(HashMap::new())), + keyboard_state, + keysym2keycode, vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), vnc_cursor: Arc::new(Mutex::new(VncCursor::default())), + display_listener: Arc::new(Mutex::new(DisplayChangeListener::default())), conn_limits: CONNECTION_LIMIT, - update_interval: Arc::new(Mutex::new(DISPLAY_UPDATE_INTERVAL_DEFAULT as u32)), } } } @@ -128,7 +132,7 @@ impl EventNotifierHelper for VncConnHandler { None as Option> }); - let mut notifiers = vec![ + let notifiers = vec![ (EventNotifier::new( NotifierOperation::AddShared, vnc_io.lock().unwrap().listener.as_raw_fd(), @@ -137,23 +141,6 @@ impl EventNotifierHelper for VncConnHandler { vec![Arc::new(Mutex::new(handler))], )), ]; - - // Register event notifier to refresh - // the image from guest_imag to server image. - let server = vnc_io.lock().unwrap().server.clone(); - let handler: Box Option>> = - Box::new(move |_event, fd: RawFd| { - read_fd(fd); - vnc_refresh(); - None as Option> - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - server.refresh_fd.lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - vec![Arc::new(Mutex::new(handler))], - )); notifiers } } @@ -292,6 +279,12 @@ impl SecurityType { } } +/// Display change listener registered in console. +#[derive(Default)] +pub struct DisplayChangeListener { + pub dcl_id: Option, +} + /// Image date of cursor. #[derive(Default)] pub struct VncCursor { @@ -566,47 +559,9 @@ pub fn handle_connection( EventLoop::update_event(EventNotifierHelper::internal_notifiers(client_io), None)?; update_server_surface(server); - vnc_refresh_notify(server); Ok(()) } -/// Refresh server_image to guest_image. -fn vnc_refresh() { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - if server.client_handlers.lock().unwrap().is_empty() { - return; - } - - let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); - let mut locked_update_interval = server.update_interval.lock().unwrap(); - if dirty_num != 0 { - *locked_update_interval /= 2; - if *locked_update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { - *locked_update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT - } - } else { - *locked_update_interval += DISPLAY_UPDATE_INTERVAL_INC; - if *locked_update_interval > DISPLAY_UPDATE_INTERVAL_MAX { - *locked_update_interval = DISPLAY_UPDATE_INTERVAL_MAX; - } - } - - let mut _rects: i32 = 0; - let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client_io in locked_handlers.values_mut() { - let client = client_io.lock().unwrap().client.clone(); - _rects += get_rects(&client, &server, dirty_num); - } -} - -/// Refresh event. -pub fn vnc_refresh_notify(server: &Arc) { - server.refresh_fd.lock().unwrap().write(1).unwrap(); -} - /// make configuration for VncServer /// /// # Arguments @@ -626,12 +581,6 @@ pub fn make_server_config( .set_security_config(vnc_cfg, object)?; // Set auth type. server.security_type.lock().unwrap().set_auth()?; - let mut locked_keysym2keycode = server.keysym2keycode.lock().unwrap(); - // Mapping ASCII to keycode. - for &(k, v) in KEYSYM2KEYCODE.iter() { - locked_keysym2keycode.insert(k, v); - } - drop(locked_keysym2keycode); Ok(()) } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index adb1888f1..780a765c5 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -12,17 +12,24 @@ use crate::{ client::{ - desktop_resize, display_cursor_define, set_color_depth, vnc_flush_notify, + desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush_notify, vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, ENCODING_HEXTILE, ENCODING_RAW, }, + console::{ + graphic_hardware_update, register_display, DisplayChangeListener, + DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, + DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, + }, + data::keycode::KEYSYM2KEYCODE, encoding::enc_hextile::hextile_send_framebuffer_update, + input::KeyBoardState, pixman::{ bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, unref_pixman_image, }, round_up, round_up_div, - server::{make_server_config, vnc_refresh_notify, VncConnHandler, VncServer, VncSurface}, + server::{make_server_config, VncConnHandler, VncServer, VncSurface}, VncError, }; use anyhow::{anyhow, Result}; @@ -35,8 +42,10 @@ use machine_manager::{ use once_cell::sync::Lazy; use std::{ cmp, + collections::HashMap, net::TcpListener, ptr, + rc::Rc, sync::{Arc, Mutex}, thread, }; @@ -45,7 +54,6 @@ use util::{ loop_context::EventNotifierHelper, pixman::{pixman_format_code_t, pixman_image_create_bits, pixman_image_ref, pixman_image_t}, }; -use vmm_sys_util::eventfd::EventFd; /// The number of dirty pixels represented bt one bit in dirty bitmap. pub const DIRTY_PIXELS_NUM: u16 = 16; @@ -63,37 +71,172 @@ pub const OUTPUT_THROTTLE_SCALE: i32 = 5; pub const MIN_OUTPUT_LIMIT: i32 = 1024 * 1024 * OUTPUT_THROTTLE_SCALE; const DEFAULT_REFRESH_INTERVAL: u64 = 30; pub const BIT_PER_BYTE: u32 = 8; -const MILLI_PER_SEC: u64 = 1_000_000; -pub const DISPLAY_UPDATE_INTERVAL_DEFAULT: u32 = 30; -pub const DISPLAY_UPDATE_INTERVAL_INC: u32 = 50; -pub const DISPLAY_UPDATE_INTERVAL_MAX: u32 = 3_000; - -/// Struct to record image information -#[derive(Clone, Copy)] -pub struct DisplaySurface { - /// image format - pub format: pixman_format_code_t, - /// pointer to image - pub image: *mut pixman_image_t, -} -impl Default for DisplaySurface { - fn default() -> Self { - DisplaySurface { - format: pixman_format_code_t::PIXMAN_a1, - image: ptr::null_mut(), +#[derive(Default)] +pub struct VncInterface {} +impl DisplayChangeListenerOperations for VncInterface { + /// Update guest_image + /// Send a resize command to the client based on whether the image size has changed + fn dpy_switch(&self, surface: &DisplaySurface) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + let need_resize = check_surface(&mut locked_vnc_surface, surface); + unref_pixman_image(locked_vnc_surface.guest_image); + + // Vnc_pixman_image_ref + locked_vnc_surface.guest_image = unsafe { pixman_image_ref(surface.image) }; + locked_vnc_surface.guest_format = surface.format; + + let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); + let guest_height: i32 = get_image_height(locked_vnc_surface.guest_image); + if !need_resize { + set_area_dirty( + &mut locked_vnc_surface.guest_dirty_bitmap, + 0, + 0, + guest_width, + guest_height, + guest_width, + guest_height, + ); + return; + } + drop(locked_vnc_surface); + update_server_surface(&server); + + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client_io in locked_handlers.values_mut() { + let client = client_io.lock().unwrap().client.clone(); + let width = vnc_width(guest_width); + let height = vnc_height(guest_height); + let mut buf: Vec = Vec::new(); + // Set Color depth. + set_color_depth(&client, &mut buf); + // Desktop_resize. + desktop_resize(&client, &server, &mut buf); + // Cursor define. + display_cursor_define(&client, &server, &mut buf); + vnc_write(&client, buf); + vnc_flush_notify(&client); + client.dirty_bitmap.lock().unwrap().clear_all(); + set_area_dirty( + &mut client.dirty_bitmap.lock().unwrap(), + 0, + 0, + width, + height, + guest_width, + guest_height, + ); + vnc_update_output_throttle(&client); + } + } + + /// Refresh server_image to guest_image. + fn dpy_refresh(&self, dcl: &mut DisplayChangeListener) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + if server.client_handlers.lock().unwrap().is_empty() { + return; + } + graphic_hardware_update(dcl.con_id); + + // Update refresh interval. + let mut update_interval = dcl.update_interval; + let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); + if dirty_num != 0 { + update_interval /= 2; + if update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { + update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT + } + } else { + update_interval += DISPLAY_UPDATE_INTERVAL_INC; + if update_interval > DISPLAY_UPDATE_INTERVAL_MAX { + update_interval = DISPLAY_UPDATE_INTERVAL_MAX; + } + } + dcl.update_interval = update_interval; + + let mut _rects: i32 = 0; + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client_io in locked_handlers.values_mut() { + let client = client_io.lock().unwrap().client.clone(); + _rects += get_rects(&client, &server, dirty_num); } } -} -/// Struct to record mouse information -#[derive(Clone, Default)] -pub struct DisplayMouse { - pub width: u32, - pub height: u32, - pub hot_x: u32, - pub hot_y: u32, - pub data: Vec, + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + let g_w = get_image_width(locked_vnc_surface.guest_image); + let g_h = get_image_height(locked_vnc_surface.guest_image); + set_area_dirty( + &mut locked_vnc_surface.guest_dirty_bitmap, + x, + y, + w, + h, + g_w, + g_h, + ); + drop(locked_vnc_surface); + } + + fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let width = cursor.width as u64; + let height = cursor.height as u64; + let bpl = round_up_div(width as u64, BIT_PER_BYTE as u64); + // Set the bit for mask. + let bit_mask: u8 = 0x80; + + let mut mask: Vec = vec![0; (bpl * height) as usize]; + let first_bit = if cfg!(target_endian = "big") { + 0_usize + } else { + (bytes_per_pixel() - 1) as usize + }; + + for j in 0..height { + let mut bit = bit_mask; + for i in 0..width { + let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; + if let Some(n) = cursor.data.get(idx as usize) { + if *n == 0xff { + mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; + } + } + bit >>= 1; + if bit == 0 { + bit = bit_mask; + } + } + } + + server.vnc_cursor.lock().unwrap().cursor = Some(cursor.clone()); + server.vnc_cursor.lock().unwrap().mask = Some(mask.clone()); + + let mut locked_handler = server.client_handlers.lock().unwrap(); + // Send the framebuff for each client. + for client_io in locked_handler.values_mut() { + let client = client_io.lock().unwrap().client.clone(); + let mut buf: Vec = Vec::new(); + display_cursor_define(&client, &server, &mut buf); + vnc_write(&client, buf); + vnc_flush_notify(&client); + } + } } /// Initizlization function of vnc @@ -123,15 +266,37 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { .set_nonblocking(true) .expect("Set noblocking for vnc socket failed"); - let refresh_fd = Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); - let server = Arc::new(VncServer::new(refresh_fd, get_client_image())); + let mut keysym2keycode: HashMap = HashMap::new(); + + let mut max_keycode: u16 = 0; + // Mapping ASCII to keycode. + for &(k, v) in KEYSYM2KEYCODE.iter() { + max_keycode = cmp::max(max_keycode, v); + keysym2keycode.insert(k, v); + } + // Record keyboard state. + let keyboard_state: Arc> = + Arc::new(Mutex::new(KeyBoardState::new(max_keycode as usize))); + + let server = Arc::new(VncServer::new( + get_client_image(), + keyboard_state, + keysym2keycode, + )); // Parameter configuation for VncServeer. make_server_config(&server, vnc_cfg, object)?; // Add an VncServer. add_vnc_server(server.clone()); + // Register the event to listen for client's connection. - let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server))); + let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server.clone()))); + + // Register in display console. + let vnc_opts = Rc::new(VncInterface::default()); + let dcl_id = register_display(vnc_opts); + server.display_listener.lock().unwrap().dcl_id = dcl_id; + // Vnc_thread: a thread to send the framebuffer start_vnc_thread()?; @@ -229,51 +394,6 @@ pub fn set_area_dirty( } } -pub fn vnc_display_update(x: i32, y: i32, w: i32, h: i32) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); - let g_w = get_image_width(locked_vnc_surface.guest_image); - let g_h = get_image_height(locked_vnc_surface.guest_image); - set_area_dirty( - &mut locked_vnc_surface.guest_dirty_bitmap, - x, - y, - w, - h, - g_w, - g_h, - ); - drop(locked_vnc_surface); - vnc_refresh_notify(&server); -} - -fn vnc_get_display_update_interval() -> u32 { - if VNC_SERVERS.lock().unwrap().is_empty() { - return DISPLAY_UPDATE_INTERVAL_DEFAULT; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let update_interval = *server.update_interval.lock().unwrap(); - - update_interval -} - -pub fn vnc_loop_update_display(x: i32, y: i32, width: i32, height: i32) { - let func = Box::new(move || { - vnc_display_update(x, y, width as i32, height as i32); - vnc_loop_update_display(x, y, width, height); - }); - - if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.delay_call( - func, - vnc_get_display_update_interval() as u64 * MILLI_PER_SEC, - ); - } -} - /// Get the width of image. pub fn vnc_width(width: i32) -> i32 { cmp::min( @@ -322,7 +442,6 @@ pub fn update_server_surface(server: &Arc) { g_width, g_height, ); - vnc_refresh_notify(server); } /// Check if the suface for VncClient is need update @@ -523,116 +642,6 @@ fn get_client_image() -> *mut pixman_image_t { } } -/// Update guest_image -/// Send a resize command to the client based on whether the image size has changed -pub fn vnc_display_switch(surface: &DisplaySurface) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); - let need_resize = check_surface(&mut locked_vnc_surface, surface); - unref_pixman_image(locked_vnc_surface.guest_image); - - // Vnc_pixman_image_ref - locked_vnc_surface.guest_image = unsafe { pixman_image_ref(surface.image) }; - locked_vnc_surface.guest_format = surface.format; - - let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); - let guest_height: i32 = get_image_height(locked_vnc_surface.guest_image); - if !need_resize { - set_area_dirty( - &mut locked_vnc_surface.guest_dirty_bitmap, - 0, - 0, - guest_width, - guest_height, - guest_width, - guest_height, - ); - vnc_refresh_notify(&server); - return; - } - drop(locked_vnc_surface); - update_server_surface(&server); - - let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client_io in locked_handlers.values_mut() { - let client = client_io.lock().unwrap().client.clone(); - let width = vnc_width(guest_width); - let height = vnc_height(guest_height); - let mut buf: Vec = Vec::new(); - // Set Color depth. - set_color_depth(&client, &mut buf); - // Desktop_resize. - desktop_resize(&client, &server, &mut buf); - // Cursor define. - display_cursor_define(&client, &server, &mut buf); - vnc_write(&client, buf); - vnc_flush_notify(&client); - client.dirty_bitmap.lock().unwrap().clear_all(); - set_area_dirty( - &mut client.dirty_bitmap.lock().unwrap(), - 0, - 0, - width, - height, - guest_width, - guest_height, - ); - vnc_update_output_throttle(&client); - } - vnc_refresh_notify(&server); -} - -pub fn vnc_display_cursor(cursor: &mut DisplayMouse) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let width = cursor.width as u64; - let height = cursor.height as u64; - let bpl = round_up_div(width as u64, BIT_PER_BYTE as u64); - // Set the bit for mask. - let bit_mask: u8 = 0x80; - - let mut mask: Vec = vec![0; (bpl * height) as usize]; - let first_bit = if cfg!(target_endian = "big") { - 0_usize - } else { - (bytes_per_pixel() - 1) as usize - }; - - for j in 0..height { - let mut bit = bit_mask; - for i in 0..width { - let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; - if let Some(n) = cursor.data.get(idx as usize) { - if *n == 0xff { - mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; - } - } - bit >>= 1; - if bit == 0 { - bit = bit_mask; - } - } - } - - server.vnc_cursor.lock().unwrap().cursor = Some(cursor.clone()); - server.vnc_cursor.lock().unwrap().mask = Some(mask.clone()); - - let mut locked_handler = server.client_handlers.lock().unwrap(); - // Send the framebuff for each client. - for client_io in locked_handler.values_mut() { - let client = client_io.lock().unwrap().client.clone(); - let mut buf: Vec = Vec::new(); - display_cursor_define(&client, &server, &mut buf); - vnc_write(&client, buf); - vnc_flush_notify(&client); - } -} - pub static VNC_SERVERS: Lazy>>> = Lazy::new(|| Mutex::new(Vec::new())); pub static VNC_RECT_INFO: Lazy>>> = Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); -- Gitee From 7bd1141555ea0d4c4ee493929f011dfe3ad11509 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 14:56:12 +0800 Subject: [PATCH 0611/2187] VNC: Support the selection of console in vnc Use of Ctrl+Alt+Num to select the display console Signed-off-by: Xiao Ye --- util/src/bitmap.rs | 31 ++++++++ vnc/src/input.rs | 191 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 192 insertions(+), 30 deletions(-) diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 79003a68a..d01b55ca0 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -196,6 +196,33 @@ impl Bitmap { Ok(()) } + /// Change the bit of bitmap. + /// + /// # Arguments + /// + /// * `num` - the input number + /// # Example + /// + /// ```rust + /// use util::bitmap::Bitmap; + /// let mut bitmap = Bitmap::::new(1); + /// assert!(bitmap.change(15).is_ok()); + /// assert_eq!(bitmap.contain(15).unwrap(), true); + /// assert!(bitmap.change(15).is_ok()); + /// assert_eq!(bitmap.contain(15).unwrap(), false); + /// ``` + pub fn change(&mut self, num: usize) -> Result<()> { + let index = self.bit_index(num); + if index >= self.size() { + return Err(anyhow!(UtilError::OutOfBound( + index as u64, + self.vol() as u64 + ))); + } + self.data[index] = T::bit_xor(self.data[index], T::one().rhs(self.bit_pos(num))); + Ok(()) + } + /// Query bitmap if contains input number or not. /// /// # Arguments @@ -406,8 +433,12 @@ mod tests { let mut bitmap = Bitmap::::new(1); assert!(bitmap.set(15).is_ok()); assert!(bitmap.set(16).is_err()); + assert_eq!(bitmap.contain(15).unwrap(), true); assert_eq!(bitmap.count_front_bits(16).unwrap(), 1); assert_eq!(bitmap.count_front_bits(15).unwrap(), 0); + assert!(bitmap.change(15).is_ok()); + assert!(bitmap.change(16).is_err()); + assert_eq!(bitmap.contain(15).unwrap(), false); } #[test] diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 8b4bfa349..f64e67d0b 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -12,9 +12,11 @@ use crate::{ client::ClientIoHandler, + console::console_select, pixman::{get_image_height, get_image_width}, vnc::BIT_PER_BYTE, }; +use anyhow::Result; use log::error; use usb::{keyboard::keyboard_event, tablet::pointer_event, INPUT}; use util::bitmap::Bitmap; @@ -36,6 +38,18 @@ const ASCII_A: i32 = 65; const ASCII_Z: i32 = 90; const UPPERCASE_TO_LOWERCASE: i32 = 32; +// Keycode. +const KEYCODE_1: u16 = 2; +const KEYCODE_9: u16 = 10; +const KEYCODE_CTRL: u16 = 29; +const KEYCODE_SHIFT: u16 = 42; +const KEYCODE_SHIFT_R: u16 = 54; +const KEYCODE_ALT: u16 = 56; +const KEYCODE_CAPS_LOCK: u16 = 58; +const KEYCODE_NUM_LOCK: u16 = 69; +const KEYCODE_CTRL_R: u16 = 157; +const KEYCODE_ALT_R: u16 = 184; + // Keyboard Modifier State pub enum KeyboardModifier { KeyModNone = 0, @@ -67,6 +81,101 @@ impl KeyBoardState { ), } } + + /// Get the corresponding keyboard modifier. + fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool { + match self.keymods.contain(key_mod as usize) { + Ok(res) => res, + Err(_e) => false, + } + } + + /// Reset all keyboard modifier state. + fn keyboard_state_reset(&mut self) { + self.keymods.clear_all(); + } + + /// Record the press and up state in the keyboard. + fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> { + // Key is not pressed and the incoming key action is up. + if !down && !self.keystate.contain(keycode as usize)? { + return Ok(()); + } + + // Update Keyboard key modifier state. + if down { + self.keystate.set(keycode as usize)?; + } else { + self.keystate.clear(keycode as usize)?; + } + + // Update Keyboard modifier state. + match keycode { + KEYCODE_SHIFT | KEYCODE_SHIFT_R => { + self.keyboard_modstate_update( + KEYCODE_SHIFT, + KEYCODE_SHIFT, + KeyboardModifier::KeyModShift, + )?; + } + KEYCODE_CTRL | KEYCODE_CTRL_R => { + self.keyboard_modstate_update( + KEYCODE_CTRL, + KEYCODE_CTRL_R, + KeyboardModifier::KeyModCtrl, + )?; + } + KEYCODE_ALT => { + self.keyboard_modstate_update( + KEYCODE_ALT, + KEYCODE_ALT, + KeyboardModifier::KeyModAlt, + )?; + } + KEYCODE_ALT_R => { + self.keyboard_modstate_update( + KEYCODE_ALT_R, + KEYCODE_ALT_R, + KeyboardModifier::KeyModAltgr, + )?; + } + KEYCODE_CAPS_LOCK => { + if down { + self.keymods + .change(KeyboardModifier::KeyModCapslock as usize)?; + } + } + KEYCODE_NUM_LOCK => { + if down { + self.keymods + .change(KeyboardModifier::KeyModNumlock as usize)?; + } + } + _ => {} + } + + Ok(()) + } + + /// If one of the keys keycode_1 and keycode_2 is pressed, + /// Then the corresponding keyboard modifier state will be set. + /// Otherwise, it will be clear. + fn keyboard_modstate_update( + &mut self, + keycode_1: u16, + keycode_2: u16, + mod_state: KeyboardModifier, + ) -> Result<()> { + let mut res = self.keystate.contain(keycode_1 as usize)?; + res |= self.keystate.contain(keycode_2 as usize)?; + + if res { + self.keymods.set(mod_state as usize)?; + } else { + self.keymods.clear(mod_state as usize)?; + } + Ok(()) + } } impl ClientIoHandler { @@ -77,19 +186,41 @@ impl ClientIoHandler { return; } let buf = self.read_incoming_msg(); - let down = buf[1] as u8; + let down: bool = buf[1] != 0; let mut keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); + let server = self.server.clone(); // Uppercase -> Lowercase. if (ASCII_A..=ASCII_Z).contains(&keysym) { keysym += UPPERCASE_TO_LOWERCASE; } + let mut locked_kbd_state = server.keyboard_state.lock().unwrap(); + let dcl_id = self.server.display_listener.lock().unwrap().dcl_id; - let keycode: u16 = match self.server.keysym2keycode.get(&(keysym as u16)) { + let keycode: u16 = match server.keysym2keycode.get(&(keysym as u16)) { Some(k) => *k, None => 0, }; - self.do_key_event(down, keycode); + + // Ctr + Alt + Num(1~9) + // Switch to the corresponding display device. + if (KEYCODE_1..KEYCODE_9 + 1).contains(&keycode) + && down + && dcl_id.is_some() + && locked_kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) + && locked_kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) + { + locked_kbd_state.keyboard_state_reset(); + console_select(Some((keycode - KEYCODE_1) as usize)); + } + + if let Err(e) = locked_kbd_state.keyboard_state_update(keycode, down) { + error!("{:?}", e); + return; + } + drop(locked_kbd_state); + do_key_event(keycode, down); + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } @@ -130,33 +261,6 @@ impl ClientIoHandler { self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } - /// Do keyboard event. - /// - /// # Arguments - /// - /// * `down` - press keyboard down or up. - /// * `keycode` - keycode. - pub fn do_key_event(&mut self, down: u8, keycode: u16) { - let mut scancode = Vec::new(); - let mut keycode = keycode; - if keycode & SCANCODE_GREY != 0 { - scancode.push(SCANCODE_EMUL0 as u32); - keycode &= !SCANCODE_GREY; - } - - if down == 0 { - keycode |= SCANCODE_UP; - } - scancode.push(keycode as u32); - // Send key event. - let locked_input = INPUT.lock().unwrap(); - if let Some(keyboard) = &locked_input.keyboard { - if let Err(e) = keyboard_event(keyboard, scancode.as_slice()) { - error!("Key event error: {}", e); - } - } - } - /// Client cut text. pub fn client_cut_event(&mut self) { let buf = self.read_incoming_msg(); @@ -176,3 +280,30 @@ impl ClientIoHandler { self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } } + +/// Do keyboard event. +/// +/// # Arguments +/// +/// * `down` - press keyboard down or up. +/// * `keycode` - keycode. +fn do_key_event(keycode: u16, down: bool) { + let mut scancode = Vec::new(); + let mut keycode = keycode; + if keycode & SCANCODE_GREY != 0 { + scancode.push(SCANCODE_EMUL0 as u32); + keycode &= !SCANCODE_GREY; + } + + if !down { + keycode |= SCANCODE_UP; + } + scancode.push(keycode as u32); + // Send key event. + let locked_input = INPUT.lock().unwrap(); + if let Some(keyboard) = &locked_input.keyboard { + if let Err(e) = keyboard_event(keyboard, scancode.as_slice()) { + error!("Key event error: {}", e); + } + } +} -- Gitee From fd04f2e80d7d11c1e067504b61becf4f399fd760 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 18 Dec 2022 16:03:37 +0800 Subject: [PATCH 0612/2187] VNC: Client login in preemption mode. When the total number of connections exceeds the limit, the old client will be disconnected. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 70 +++++++++++++++++++++++++++-------------------- vnc/src/server.rs | 25 ++++++----------- vnc/src/vnc.rs | 29 +++++++++----------- 3 files changed, 61 insertions(+), 63 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index b9c2258e7..3e1f791f0 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -258,6 +258,10 @@ impl ConnState { /// Struct to record the state with the vnc client. pub struct ClientState { + /// Tcp listening address. + pub addr: String, + /// Disconnect event fd. + pub disconn_evt: Arc>, /// Write event fd. write_fd: Arc>, /// TcpStream receive buffer. @@ -272,9 +276,11 @@ pub struct ClientState { pub dirty_bitmap: Arc>>, } -impl Default for ClientState { - fn default() -> Self { +impl ClientState { + pub fn new(addr: String) -> Self { ClientState { + addr, + disconn_evt: Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), write_fd: Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), in_buffer: Arc::new(Mutex::new(BuffPool::new())), out_buffer: Arc::new(Mutex::new(BuffPool::new())), @@ -302,19 +308,10 @@ pub struct ClientIoHandler { pub client: Arc, /// Configure for vnc server. pub server: Arc, - /// Disconnect event fd. - pub disconn_evt: EventFd, - /// Tcp listening address. - pub addr: String, } impl ClientIoHandler { - pub fn new( - stream: TcpStream, - client: Arc, - server: Arc, - addr: String, - ) -> Self { + pub fn new(stream: TcpStream, client: Arc, server: Arc) -> Self { ClientIoHandler { stream, tls_conn: None, @@ -322,8 +319,6 @@ impl ClientIoHandler { expect: 12, client, server, - disconn_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - addr, } } } @@ -568,8 +563,25 @@ impl ClientIoHandler { /// Initialize the connection of vnc client. pub fn handle_client_init(&mut self) -> Result<()> { let mut buf = Vec::new(); - // Send server framebuffer info. + // If the total number of connection exceeds the limit, + // then the old client will be disconnected. + let server = self.server.clone(); let client = self.client.clone(); + let addr = client.addr.clone(); + let mut locked_clients = server.client_handlers.lock().unwrap(); + let mut len = locked_clients.len() as i32; + for client in locked_clients.values_mut() { + if len <= server.conn_limits as i32 { + break; + } + if client.addr != addr { + client.disconn_evt.lock().unwrap().write(1).unwrap(); + len -= 1; + } + } + drop(locked_clients); + + // Send server framebuffer info. let locked_surface = self.server.vnc_surface.lock().unwrap(); let mut locked_dpm = client.client_dpm.lock().unwrap(); let width = get_image_width(locked_surface.server_image); @@ -958,7 +970,7 @@ impl ClientIoHandler { ), EventNotifier::new( NotifierOperation::Delete, - self.disconn_evt.as_raw_fd(), + self.client.disconn_evt.lock().unwrap().as_raw_fd(), None, EventSet::IN, Vec::new(), @@ -979,26 +991,21 @@ impl EventNotifierHelper for ClientIoHandler { let handler: Box Option>> = Box::new(move |event, _fd: RawFd| { let mut locked_client_io = client_io.lock().unwrap(); + let client = locked_client_io.client.clone(); if event & EventSet::ERROR == EventSet::ERROR || event & EventSet::HANG_UP == EventSet::HANG_UP || event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { - locked_client_io.client.conn_state.lock().unwrap().dis_conn = true; + client.conn_state.lock().unwrap().dis_conn = true; } else if event & EventSet::IN == EventSet::IN { if let Err(_e) = locked_client_io.client_handle_read() { - locked_client_io.client.conn_state.lock().unwrap().dis_conn = true; + client.conn_state.lock().unwrap().dis_conn = true; } } // Do disconnection event. - if locked_client_io - .client - .conn_state - .lock() - .unwrap() - .is_disconnect() - { - locked_client_io.disconn_evt.write(1).unwrap(); + if client.conn_state.lock().unwrap().is_disconnect() { + client.disconn_evt.lock().unwrap().write(1).unwrap(); } drop(locked_client_io); @@ -1020,11 +1027,12 @@ impl EventNotifierHelper for ClientIoHandler { Box::new(move |_event, fd| { read_fd(fd); let mut locked_client_io = client_io.lock().unwrap(); + let client = locked_client_io.client.clone(); locked_client_io.client_handle_write(); // do disconnection event. - if locked_client_io.client.conn_state.lock().unwrap().dis_conn { - locked_client_io.disconn_evt.write(1).unwrap(); + if client.conn_state.lock().unwrap().dis_conn { + client.disconn_evt.lock().unwrap().write(1).unwrap(); } drop(locked_client_io); @@ -1045,7 +1053,8 @@ impl EventNotifierHelper for ClientIoHandler { read_fd(fd); // Drop client info from vnc server. let mut locked_client_io = client_io.lock().unwrap(); - let addr = locked_client_io.addr.clone(); + let client = locked_client_io.client.clone(); + let addr = client.addr.clone(); let server = locked_client_io.server.clone(); let notifiers = locked_client_io.disconn_evt_handler(); // Shutdown stream. @@ -1058,9 +1067,10 @@ impl EventNotifierHelper for ClientIoHandler { Some(notifiers) }); + let client = client_io_handler.lock().unwrap().client.clone(); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - client_io_handler.lock().unwrap().disconn_evt.as_raw_fd(), + client.disconn_evt.lock().unwrap().as_raw_fd(), None, EventSet::IN, vec![Arc::new(Mutex::new(handler))], diff --git a/vnc/src/server.rs b/vnc/src/server.rs index f48b175ae..ddca62cb6 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -37,7 +37,7 @@ use machine_manager::{ use std::{ cmp, collections::HashMap, - net::{Shutdown, SocketAddr, TcpListener, TcpStream}, + net::{SocketAddr, TcpListener, TcpStream}, os::unix::prelude::{AsRawFd, RawFd}, ptr, sync::{Arc, Mutex}, @@ -57,7 +57,7 @@ const CONNECTION_LIMIT: usize = 1; /// Information of VncServer. pub struct VncServer { /// Client io handler. - pub client_handlers: Arc>>>>, + pub client_handlers: Arc>>>, /// Security Type for connection. pub security_type: Arc>, /// keyboard status. @@ -71,7 +71,7 @@ pub struct VncServer { /// Display Change Listener. pub display_listener: Arc>, /// Connection limit. - conn_limits: usize, + pub conn_limits: usize, } unsafe impl Send for VncServer {} @@ -504,8 +504,7 @@ impl VncSurface { fn set_dirty_for_each_clients(x: usize, y: usize) { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client_io in locked_handlers.values_mut() { - let client = client_io.lock().unwrap().client.clone(); + for client in locked_handlers.values_mut() { client .dirty_bitmap .lock() @@ -526,35 +525,27 @@ pub fn handle_connection( stream: TcpStream, addr: SocketAddr, ) -> Result<()> { - if server.client_handlers.lock().unwrap().len() >= server.conn_limits { - stream.shutdown(Shutdown::Both).unwrap(); - return Err(anyhow!(VncError::MakeConnectionFailed(String::from( - "Total connection is exceeding to limit." - )))); - } + info!("New Connection: {:?}", stream); stream .set_nonblocking(true) .expect("set nonblocking failed"); - info!("New Connection: {:?}", stream); // Register event notifier for vnc client. - let client = Arc::new(ClientState::default()); + let client = Arc::new(ClientState::new(addr.to_string())); let client_io = Arc::new(Mutex::new(ClientIoHandler::new( stream, - client, + client.clone(), server.clone(), - addr.to_string(), ))); client_io .lock() .unwrap() .write_msg("RFB 003.008\n".to_string().as_bytes()); - server .client_handlers .lock() .unwrap() - .insert(addr.to_string(), client_io.clone()); + .insert(addr.to_string(), client); EventLoop::update_event(EventNotifierHelper::internal_notifiers(client_io), None)?; diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 780a765c5..3ed65f5cb 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -108,19 +108,18 @@ impl DisplayChangeListenerOperations for VncInterface { update_server_surface(&server); let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client_io in locked_handlers.values_mut() { - let client = client_io.lock().unwrap().client.clone(); + for client in locked_handlers.values_mut() { let width = vnc_width(guest_width); let height = vnc_height(guest_height); let mut buf: Vec = Vec::new(); // Set Color depth. - set_color_depth(&client, &mut buf); + set_color_depth(client, &mut buf); // Desktop_resize. - desktop_resize(&client, &server, &mut buf); + desktop_resize(client, &server, &mut buf); // Cursor define. - display_cursor_define(&client, &server, &mut buf); - vnc_write(&client, buf); - vnc_flush_notify(&client); + display_cursor_define(client, &server, &mut buf); + vnc_write(client, buf); + vnc_flush_notify(client); client.dirty_bitmap.lock().unwrap().clear_all(); set_area_dirty( &mut client.dirty_bitmap.lock().unwrap(), @@ -131,7 +130,7 @@ impl DisplayChangeListenerOperations for VncInterface { guest_width, guest_height, ); - vnc_update_output_throttle(&client); + vnc_update_output_throttle(client); } } @@ -164,9 +163,8 @@ impl DisplayChangeListenerOperations for VncInterface { let mut _rects: i32 = 0; let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client_io in locked_handlers.values_mut() { - let client = client_io.lock().unwrap().client.clone(); - _rects += get_rects(&client, &server, dirty_num); + for client in locked_handlers.values_mut() { + _rects += get_rects(client, &server, dirty_num); } } @@ -229,12 +227,11 @@ impl DisplayChangeListenerOperations for VncInterface { let mut locked_handler = server.client_handlers.lock().unwrap(); // Send the framebuff for each client. - for client_io in locked_handler.values_mut() { - let client = client_io.lock().unwrap().client.clone(); + for client in locked_handler.values_mut() { let mut buf: Vec = Vec::new(); - display_cursor_define(&client, &server, &mut buf); - vnc_write(&client, buf); - vnc_flush_notify(&client); + display_cursor_define(client, &server, &mut buf); + vnc_write(client, buf); + vnc_flush_notify(client); } } } -- Gitee From 45518ef464343a9927333bc46a985b4164cd606c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Dec 2022 08:31:23 +0800 Subject: [PATCH 0613/2187] virtio-pci: Make notify_eventfds of VirtioPciDevice as Arc As we move the copy of virtio_pci_device into common_write region ops, if without Arc type, the data is deep copied. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index a21199531..0f9bdba66 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -594,7 +594,7 @@ pub struct VirtioPciDevice { /// Primary Bus parent_bus: Weak>, /// Eventfds used for guest notify the Device. - notify_eventfds: NotifyEventFds, + notify_eventfds: Arc, /// The function for interrupt triggering interrupt_cb: Option>, /// Virtio queues. The vector and Queue will be shared acrossing thread, so all with Arc> wrapper. @@ -632,7 +632,7 @@ impl VirtioPciDevice { queue_size, queue_num, ))), parent_bus, - notify_eventfds: NotifyEventFds::new(queue_num), + notify_eventfds: Arc::new(NotifyEventFds::new(queue_num)), interrupt_cb: None, queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))), multi_func, @@ -688,7 +688,7 @@ impl VirtioPciDevice { fn ioeventfds(&self) -> Vec { let mut ret = Vec::new(); - let eventfds = self.notify_eventfds.clone(); + let eventfds = (*self.notify_eventfds).clone(); for (index, eventfd) in eventfds.events.into_iter().enumerate() { let addr = index as u64 * u64::from(VIRTIO_PCI_CAP_NOTIFY_OFF_MULTIPLIER); ret.push(RegionIoEventFd { @@ -834,7 +834,7 @@ impl VirtioPciDevice { queue_num -= 1; } let call_evts = NotifyEventFds::new(queue_num); - let queue_evts = cloned_pci_device.notify_eventfds.clone().events; + let queue_evts = (*cloned_pci_device.notify_eventfds).clone().events; if let Some(cb) = cloned_pci_device.interrupt_cb.clone() { if cloned_pci_device.need_irqfd { if let Err(e) = cloned_pci_device @@ -1431,7 +1431,7 @@ impl MigrationHook for VirtioPciDevice { bail!("Failed to update bar, error is {:?}", e); } - let queue_evts = self.notify_eventfds.clone().events; + let queue_evts = (*self.notify_eventfds).clone().events; if let Some(cb) = self.interrupt_cb.clone() { if let Err(e) = self.device.lock().unwrap().activate( self.sys_mem.clone(), -- Gitee From b06ebf11584c153c282c7cc4246f816c624080f3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Dec 2022 08:26:41 +0800 Subject: [PATCH 0614/2187] virtio-pci: Factor out device activate and deactivate To reduce the code len of common_write region ops. Signed-off-by: Keqian Zhu --- virtio/src/virtio_pci.rs | 252 +++++++++++++++++---------------------- 1 file changed, 112 insertions(+), 140 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 0f9bdba66..13ee4ec75 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -386,9 +386,21 @@ impl VirtioPciCommonConfig { return Ok(()); } + let old_status = self.device_status; self.device_status = value; - if self.device_status == 0 { + if self.check_device_status( + CONFIG_STATUS_ACKNOWLEDGE + | CONFIG_STATUS_DRIVER + | CONFIG_STATUS_DRIVER_OK + | CONFIG_STATUS_FEATURES_OK, + CONFIG_STATUS_FAILED, + ) { + // FIXME: handle activation failure. + virtio_pci_dev.activate_device(self); + } else if old_status != 0 && self.device_status == 0 { self.reset(); + // FIXME: handle deactivation failure. + virtio_pci_dev.deactivate_device(); } } COMMON_Q_SELECT_REG => { @@ -715,6 +727,105 @@ impl VirtioPciDevice { Ok(write_start) } + fn activate_device(&self, common_cfg_lock: &mut VirtioPciCommonConfig) -> bool { + if self.device_activated.load(Ordering::Acquire) { + return true; + } + + let queue_type = common_cfg_lock.queue_type; + let queues_config = &mut common_cfg_lock.queues_config; + let mut locked_queues = self.queues.lock().unwrap(); + for q_config in queues_config.iter_mut() { + if !q_config.ready { + warn!("queue is not ready, please check your init process"); + } else { + q_config.addr_cache.desc_table_host = self + .sys_mem + .get_host_address(q_config.desc_table) + .unwrap_or(0); + q_config.addr_cache.avail_ring_host = self + .sys_mem + .get_host_address(q_config.avail_ring) + .unwrap_or(0); + q_config.addr_cache.used_ring_host = self + .sys_mem + .get_host_address(q_config.used_ring) + .unwrap_or(0); + } + let queue = Queue::new(*q_config, queue_type).unwrap(); + if q_config.ready && !queue.is_valid(&self.sys_mem) { + error!("Failed to activate device: Invalid queue"); + return false; + } + let arc_queue = Arc::new(Mutex::new(queue)); + locked_queues.push(arc_queue.clone()); + } + + let mut queue_num = self.device.lock().unwrap().queue_num(); + // No need to create call event for control queue. + // It will be polled in StratoVirt when activating the device. + if self.device.lock().unwrap().has_control_queue() && queue_num % 2 != 0 { + queue_num -= 1; + } + let call_evts = NotifyEventFds::new(queue_num); + let queue_evts = (*self.notify_eventfds).clone().events; + if let Some(cb) = self.interrupt_cb.clone() { + if self.need_irqfd { + if let Err(e) = self + .device + .lock() + .unwrap() + .set_guest_notifiers(&call_evts.events) + { + error!("Failed to set guest notifiers, error is {:?}", e); + return false; + } + } + if let Err(e) = self.device.lock().unwrap().activate( + self.sys_mem.clone(), + cb, + &locked_queues, + queue_evts, + ) { + error!("Failed to activate device, error is {:?}", e); + return false; + } + } else { + error!("Failed to activate device: No interrupt callback"); + return false; + } + drop(locked_queues); + self.device_activated.store(true, Ordering::Release); + + update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); + + if self.need_irqfd + && !virtio_pci_register_irqfd(self, &self.gsi_msi_routes, &call_evts.events) + { + return false; + } + true + } + + fn deactivate_device(&self) -> bool { + if self.need_irqfd { + virtio_pci_unregister_irqfd(self.gsi_msi_routes.clone()); + } + + self.queues.lock().unwrap().clear(); + if self.device_activated.load(Ordering::Acquire) { + self.device_activated.store(false, Ordering::Release); + let cloned_msix = self.config.msix.as_ref().unwrap().clone(); + cloned_msix.lock().unwrap().reset(); + if let Err(e) = self.device.lock().unwrap().deactivate() { + error!("Failed to deactivate virtio device, error is {:?}", e); + return false; + } + } + update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); + true + } + fn build_common_cfg_ops(&mut self) -> RegionOps { let cloned_virtio_dev = self.device.clone(); let cloned_common_cfg = self.common_config.clone(); @@ -738,18 +849,11 @@ impl VirtioPciDevice { }; let cloned_pci_device = self.clone(); - let cloned_mem_space = self.sys_mem.clone(); - let cloned_gsi_routes = self.gsi_msi_routes.clone(); let common_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { let mut value = 0; if !read_data_u32(data, &mut value) { return false; } - let old_dev_status = cloned_pci_device - .common_config - .lock() - .unwrap() - .device_status; // In report_virtio_error(), it will lock cloned_pci_device.common_config. // So, avoid deadlock by getting the returned value firstly. @@ -781,138 +885,6 @@ impl VirtioPciDevice { } return false; } - - if !cloned_pci_device.device_activated.load(Ordering::Acquire) - && cloned_pci_device - .common_config - .lock() - .unwrap() - .check_device_status( - CONFIG_STATUS_ACKNOWLEDGE - | CONFIG_STATUS_DRIVER - | CONFIG_STATUS_DRIVER_OK - | CONFIG_STATUS_FEATURES_OK, - CONFIG_STATUS_FAILED, - ) - { - let queue_type = cloned_pci_device.common_config.lock().unwrap().queue_type; - let queues_config = &mut cloned_pci_device - .common_config - .lock() - .unwrap() - .queues_config; - let mut locked_queues = cloned_pci_device.queues.lock().unwrap(); - for q_config in queues_config.iter_mut() { - if !q_config.ready { - warn!("queue is not ready, please check your init process"); - } else { - q_config.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(q_config.desc_table) - .unwrap_or(0); - q_config.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(q_config.avail_ring) - .unwrap_or(0); - q_config.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(q_config.used_ring) - .unwrap_or(0); - } - let queue = Queue::new(*q_config, queue_type).unwrap(); - if q_config.ready && !queue.is_valid(&cloned_pci_device.sys_mem) { - error!("Failed to activate device: Invalid queue"); - return false; - } - let arc_queue = Arc::new(Mutex::new(queue)); - locked_queues.push(arc_queue.clone()); - } - - let mut queue_num = cloned_pci_device.device.lock().unwrap().queue_num(); - // No need to create call event for control queue. - // It will be polled in StratoVirt when activating the device. - if cloned_pci_device.device.lock().unwrap().has_control_queue() - && queue_num % 2 != 0 - { - queue_num -= 1; - } - let call_evts = NotifyEventFds::new(queue_num); - let queue_evts = (*cloned_pci_device.notify_eventfds).clone().events; - if let Some(cb) = cloned_pci_device.interrupt_cb.clone() { - if cloned_pci_device.need_irqfd { - if let Err(e) = cloned_pci_device - .device - .lock() - .unwrap() - .set_guest_notifiers(&call_evts.events) - { - error!("Failed to set guest notifiers, error is {:?}", e); - return false; - } - } - if let Err(e) = cloned_pci_device.device.lock().unwrap().activate( - cloned_pci_device.sys_mem.clone(), - cb, - &locked_queues, - queue_evts, - ) { - error!("Failed to activate device, error is {:?}", e); - return false; - } - } else { - error!("Failed to activate device: No interrupt callback"); - return false; - } - drop(locked_queues); - cloned_pci_device - .device_activated - .store(true, Ordering::Release); - - update_dev_id( - &cloned_pci_device.parent_bus, - cloned_pci_device.devfn, - &cloned_pci_device.dev_id, - ); - - if cloned_pci_device.need_irqfd - && !virtio_pci_register_irqfd( - &cloned_pci_device, - &cloned_gsi_routes, - &call_evts.events, - ) - { - return false; - } - } - - if old_dev_status != 0 - && cloned_pci_device - .common_config - .lock() - .unwrap() - .device_status - == 0 - { - if cloned_pci_device.need_irqfd { - virtio_pci_unregister_irqfd(cloned_gsi_routes.clone()); - } - - cloned_pci_device.queues.lock().unwrap().clear(); - if cloned_pci_device.device_activated.load(Ordering::Acquire) { - cloned_pci_device - .device_activated - .store(false, Ordering::Release); - let cloned_msix = cloned_pci_device.config.msix.as_ref().unwrap().clone(); - cloned_msix.lock().unwrap().reset(); - if let Err(e) = cloned_pci_device.device.lock().unwrap().deactivate() { - error!("Failed to deactivate virtio device, error is {:?}", e); - return false; - } - } - update_dev_id( - &cloned_pci_device.parent_bus, - cloned_pci_device.devfn, - &cloned_pci_device.dev_id, - ); - } - true }; -- Gitee From 9bc18181d44dc27ef1ae0ca4120b629e8e04a3cf Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 20 Dec 2022 17:36:01 +0800 Subject: [PATCH 0615/2187] Fix: Optimize the lock in the notify_controller function Release the small lock before take the big lock. Signed-off-by: Mingwang Li --- usb/src/keyboard.rs | 5 ++--- usb/src/tablet.rs | 5 ++--- usb/src/usb.rs | 18 ++++-------------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 633ed15f9..6b39fdf42 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -216,8 +216,7 @@ impl UsbDeviceOps for UsbKeyboard { self.ctrl.clone() } - fn get_wakeup_endpoint(&self) -> Option>> { - let ep = self.usb_device.get_endpoint(true, 1); - Some(Arc::downgrade(&ep)) + fn get_wakeup_endpoint(&self) -> Arc> { + self.usb_device.get_endpoint(true, 1) } } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 6fe4db060..4a8f0c179 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -228,8 +228,7 @@ impl UsbDeviceOps for UsbTablet { self.ctrl.clone() } - fn get_wakeup_endpoint(&self) -> Option>> { - let ep = self.usb_device.get_endpoint(true, 1); - Some(Arc::downgrade(&ep)) + fn get_wakeup_endpoint(&self) -> Arc> { + self.usb_device.get_endpoint(true, 1) } } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 5ff6a54cd..9a304134b 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -429,7 +429,7 @@ pub trait UsbDeviceOps: Send + Sync { fn get_controller(&self) -> Option>>; /// Get the endpoint to wakeup. - fn get_wakeup_endpoint(&self) -> Option>>; + fn get_wakeup_endpoint(&self) -> Arc>; /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { @@ -550,10 +550,6 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { } else { bail!("USB controller not found"); }; - drop(locked_dev); - // Lock controller before device to avoid dead lock. - let mut locked_xhci = xhci.lock().unwrap(); - let locked_dev = dev.lock().unwrap(); let usb_dev = locked_dev.get_usb_device(); let usb_port = if let Some(port) = &usb_dev.port { port.upgrade().unwrap() @@ -562,8 +558,10 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { }; let slot_id = usb_dev.addr; let wakeup = usb_dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP; - // Drop the lock, lookup port need it. + let ep = locked_dev.get_wakeup_endpoint(); + // Drop the small lock. drop(locked_dev); + let mut locked_xhci = xhci.lock().unwrap(); let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { xhci_port } else { @@ -582,14 +580,6 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { locked_xhci.port_notify(&xhci_port, PORTSC_PLC)?; } } - let locked_dev = dev.lock().unwrap(); - let intr = if let Some(intr) = locked_dev.get_wakeup_endpoint() { - intr - } else { - bail!("No interrupter found"); - }; - drop(locked_dev); - let ep = intr.upgrade().unwrap(); if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep) { error!("Failed to wakeup endpoint {}", e); } -- Gitee From c898fc799ca3b5cf332f72431c64f7674d606004 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 20 Dec 2022 16:38:18 +0800 Subject: [PATCH 0616/2187] Fix: delete unused member variables Delete unused variables: state in UsbDevice and intr_target in XhciSlot Signed-off-by: Mingwang Li --- usb/src/keyboard.rs | 5 ++--- usb/src/tablet.rs | 4 +--- usb/src/usb.rs | 15 --------------- usb/src/xhci/xhci_controller.rs | 5 ----- 4 files changed, 3 insertions(+), 26 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 633ed15f9..df80c8d67 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -26,8 +26,8 @@ use crate::hid::{ }; use crate::usb::{ notify_controller, usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, - UsbDescIface, UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbDeviceState, - UsbEndpoint, UsbPacket, UsbPacketStatus, + UsbDescIface, UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, + UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; use anyhow::Result; @@ -166,7 +166,6 @@ impl UsbDeviceOps for UsbKeyboard { info!("Keyboard device reset"); self.usb_device.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; self.usb_device.addr = 0; - self.usb_device.state = UsbDeviceState::Default; self.hid.reset(); } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 6fe4db060..f213d51ec 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -28,8 +28,7 @@ use crate::{ }, usb::{ usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, - UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceState, UsbEndpoint, UsbPacket, - UsbPacketStatus, + UsbDescOther, UsbDevice, UsbDeviceOps, UsbEndpoint, UsbPacket, UsbPacketStatus, }, xhci::xhci_controller::XhciDevice, }; @@ -178,7 +177,6 @@ impl UsbDeviceOps for UsbTablet { info!("Tablet device reset"); self.usb_device.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; self.usb_device.addr = 0; - self.usb_device.state = UsbDeviceState::Default; self.hid.reset(); } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 5ff6a54cd..7638ae984 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -48,18 +48,6 @@ pub enum SetupState { Parameter, } -/// USB device state. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum UsbDeviceState { - Removed, - Attached, - Powered, - Default, - Address, - Configured, - Suspended, -} - /// USB request used to transfer to USB device. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -202,7 +190,6 @@ pub struct UsbDevice { pub speed_mask: u32, pub addr: u8, pub product_desc: String, - pub state: UsbDeviceState, pub data_buf: Vec, pub remote_wakeup: u32, pub ep_ctl: Arc>, @@ -241,7 +228,6 @@ impl UsbDevice { ninterfaces: 0, config: None, altsetting: vec![0; USB_MAX_INTERFACES as usize], - state: UsbDeviceState::Removed, data_buf: vec![0_u8; 4096], ifaces: vec![None; USB_MAX_INTERFACES as usize], remote_wakeup: 0, @@ -413,7 +399,6 @@ pub trait UsbDeviceOps: Send + Sync { /// Handle the attach ops when attach device to controller. fn handle_attach(&mut self) -> Result<()> { let usb_dev = self.get_mut_usb_device(); - usb_dev.state = UsbDeviceState::Attached; usb_dev.set_default_descriptor()?; Ok(()) } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 068c365ee..1f3fa7a68 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -257,7 +257,6 @@ impl From for EpType { pub struct XhciSlot { pub enabled: bool, pub addressed: bool, - pub intr_target: u16, pub slot_ctx_addr: u64, pub usb_port: Option>>, pub endpoints: Vec, @@ -268,7 +267,6 @@ impl XhciSlot { XhciSlot { enabled: false, addressed: false, - intr_target: 0, slot_ctx_addr: 0, usb_port: None, endpoints: vec![XhciEpContext::new(mem); MAX_ENDPOINTS as usize], @@ -758,7 +756,6 @@ impl XhciDevice { self.slots[(slot_id - 1) as usize].enabled = false; self.slots[(slot_id - 1) as usize].addressed = false; self.slots[(slot_id - 1) as usize].usb_port = None; - self.slots[(slot_id - 1) as usize].intr_target = 0; Ok(TRBCCode::Success) } @@ -797,8 +794,6 @@ impl XhciDevice { dma_read_u64(&self.mem_space, GuestAddress(ctx_addr), &mut octx)?; self.slots[(slot_id - 1) as usize].usb_port = Some(usb_port.clone()); self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; - self.slots[(slot_id - 1) as usize].intr_target = - ((slot_ctx.tt_info >> TRB_INTR_SHIFT) & TRB_INTR_MASK) as u16; dev.lock().unwrap().reset(); if bsr { slot_ctx.dev_state = SLOT_DEFAULT << SLOT_STATE_SHIFT; -- Gitee From b5f759e11876af7c8e1067d812f3027bfd595026 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 20 Dec 2022 20:20:12 +0800 Subject: [PATCH 0617/2187] virtio-net: support VIRTIO_F_RING_INDIRECT_DESC feature Support VIRTIO_F_RING_INDIRECT_DESC in virtio-net. Signed-off-by: Yan Wang --- virtio/src/net.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 2e21ef4c3..0e65a8a17 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -21,18 +21,18 @@ use std::{cmp, fs, mem}; use super::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, - VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, - VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, - VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, - VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, - VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, - VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_OK, VIRTIO_TYPE_NET, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, + VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, + VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_CTRL_VLAN, + VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, + VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, + VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, @@ -1417,6 +1417,7 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR | 1 << VIRTIO_NET_F_CTRL_VQ + | 1 << VIRTIO_F_RING_INDIRECT_DESC | 1 << VIRTIO_F_RING_EVENT_IDX; let queue_pairs = self.net_cfg.queues / 2; -- Gitee From 6591eaeab71c8947b165ca86f98115601f48652d Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 20 Dec 2022 20:34:41 +0800 Subject: [PATCH 0618/2187] virtio-net: using cache to convert gpa to hva When handling rx/tx packets, using cache to convert gpa to hva. It can improve the performance. Signed-off-by: Yan Wang --- address_space/src/address_space.rs | 22 ++++++++++++++++++++++ virtio/src/net.rs | 14 ++++++++------ virtio/src/virtqueue/mod.rs | 6 +++--- virtio/src/virtqueue/split.rs | 26 +++++++------------------- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index c8188c5bf..adeed20b4 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -362,6 +362,28 @@ impl AddressSpace { }) } + /// Return the host address according to the given `GuestAddress` from cache. + /// + /// # Arguments + /// + /// * `addr` - Guest address. + /// * `cache` - The related region cache. + pub fn get_host_address_from_cache( + &self, + addr: GuestAddress, + cache: &Option, + ) -> Option { + if cache.is_none() { + return self.get_host_address(addr); + } + let region_cache = cache.unwrap(); + if addr.0 >= region_cache.start && addr.0 < region_cache.end { + Some(region_cache.host_base + addr.0 - region_cache.start) + } else { + self.get_host_address(addr) + } + } + /// Check if the GuestAddress is in one of Ram region. /// /// # Arguments diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 0e65a8a17..67a92b7c7 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -760,9 +760,10 @@ impl NetIoHandler { } let mut iovecs = Vec::new(); for elem_iov in elem.in_iovec.iter() { - let host_addr = queue - .vring - .get_host_address_from_cache(elem_iov.addr, &self.mem_space); + let host_addr = self + .mem_space + .get_host_address_from_cache(elem_iov.addr, queue.vring.get_cache()) + .unwrap_or(0); if host_addr != 0 { let iovec = libc::iovec { iov_base: host_addr as *mut libc::c_void, @@ -881,9 +882,10 @@ impl NetIoHandler { } let mut iovecs = Vec::new(); for elem_iov in elem.out_iovec.iter() { - let host_addr = queue - .vring - .get_host_address_from_cache(elem_iov.addr, &self.mem_space); + let host_addr = self + .mem_space + .get_host_address_from_cache(elem_iov.addr, queue.vring.get_cache()) + .unwrap_or(0); if host_addr != 0 { let iovec = libc::iovec { iov_base: host_addr as *mut libc::c_void, diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs index 87b273d85..ca1b12587 100644 --- a/virtio/src/virtqueue/mod.rs +++ b/virtio/src/virtqueue/mod.rs @@ -12,7 +12,7 @@ mod split; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressSpace, GuestAddress, RegionCache}; use anyhow::{anyhow, bail, Result}; use std::sync::Arc; use vmm_sys_util::eventfd::EventFd; @@ -163,8 +163,8 @@ pub trait VringOps { /// The number of descriptor chains in the available ring. fn avail_ring_len(&mut self, sys_mem: &Arc) -> Result; - fn get_host_address_from_cache(&self, addr: GuestAddress, mem_space: &Arc) - -> u64; + /// Get the region cache information of the SplitVring. + fn get_cache(&self) -> &Option; } /// Virtio queue. diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 5fa733953..f5f59ca08 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -325,9 +325,11 @@ impl SplitVringDesc { } else { bail!("Found two indirect descriptor elem in one request"); } - desc_table_host = match sys_mem.get_host_address(desc.addr) { - Some(addr) => addr, - None => bail!("Failed to get descriptor table entry host address"), + desc_table_host = sys_mem + .get_host_address_from_cache(desc.addr, cache) + .unwrap_or(0); + if desc_table_host == 0 { + bail!("Failed to get descriptor table entry host address"); }; queue_size = desc.get_desc_num(); desc = Self::next_desc(sys_mem, desc_table_host, queue_size, 0, cache)?; @@ -876,22 +878,8 @@ impl VringOps for SplitVring { Ok((avail_idx - self.next_avail).0) } - fn get_host_address_from_cache( - &self, - addr: GuestAddress, - mem_space: &Arc, - ) -> u64 { - let host_addr; - if let Some(cache) = self.cache { - if addr.0 >= cache.start && addr.0 < cache.end { - host_addr = cache.host_base + addr.0 - cache.start; - } else { - host_addr = mem_space.get_host_address(addr).unwrap_or(0); - } - } else { - host_addr = mem_space.get_host_address(addr).unwrap_or(0); - } - host_addr + fn get_cache(&self) -> &Option { + &self.cache } } -- Gitee From faf6f5bae8bb655ed534eff1042c869319d51804 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Tue, 20 Dec 2022 21:59:22 +0800 Subject: [PATCH 0619/2187] bugfix: fix incorrect err msg about max_outputs in virtio-gpu conf virtio-gpu support max outputs num is [0,16] Signed-off-by: Binfeng Wu --- machine_manager/src/config/drive.rs | 6 +++++- machine_manager/src/config/error.rs | 4 ++-- machine_manager/src/config/gpu.rs | 8 +++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 08ded7ab8..8616fd21b 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -429,7 +429,11 @@ impl ConfigCheck for PFlashConfig { } if self.unit >= MAX_UNIT_ID { - return Err(anyhow!(ConfigError::UnitIdError(self.unit, MAX_UNIT_ID))); + return Err(anyhow!(ConfigError::UnitIdError( + "PFlash unit id".to_string(), + self.unit, + MAX_UNIT_ID - 1 + ))); } Ok(()) } diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index 589aab688..13afa00cf 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -56,8 +56,8 @@ pub enum ConfigError { NoMetadata(String, String), #[error("Input value {0} is unaligned with {1} for {2}.")] Unaligned(String, u64, u64), - #[error("PFlash unit id given {0} should not be more than {1}")] - UnitIdError(usize, usize), + #[error("{0} given {1} should not be more than {2}")] + UnitIdError(String, usize, usize), #[error("Directory {0} does not exist")] DirNotExist(String), } diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 77b3d80ce..c7f1269b2 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -53,12 +53,10 @@ impl ConfigCheck for GpuConfig { } if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { - return Err(anyhow!(ConfigError::IllegalValue( + return Err(anyhow!(ConfigError::UnitIdError( "max_outputs".to_string(), - 0, - false, - VIRTIO_GPU_MAX_SCANOUTS as u64, - true, + self.max_outputs as usize, + VIRTIO_GPU_MAX_SCANOUTS, ))); } -- Gitee From ba8b5393bda4ad67f3f480b01e35382f7a460c27 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 06:01:11 +0800 Subject: [PATCH 0620/2187] virtio-blk: Delete the Option prefix type of Aio As the Aio context is not optional. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 121 +++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 68 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 6ae7f0e74..dcb1a14b4 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -296,7 +296,7 @@ impl Request { } } - let aio = iohandler.aio.as_mut().unwrap(); + let aio = &mut iohandler.aio; let serial_num = &iohandler.serial_num; let aio_type = &iohandler.aio_type; let direct = iohandler.direct; @@ -397,7 +397,7 @@ struct BlockIoHandler { /// Async IO type. aio_type: Option, /// Aio context. - aio: Option>>, + aio: Box>, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, /// The receiving half of Rust's channel to receive the image file. @@ -643,23 +643,15 @@ impl BlockIoHandler { complete_cb.complete_request(status) } - fn build_aio(&self, engine: Option<&String>) -> Result>> { - Ok(Box::new(Aio::new(Arc::new(Self::complete_func), engine)?)) - } - fn aio_complete_handler(&mut self) -> Result { - if let Some(aio) = self.aio.as_mut() { - let result = aio.handle(); - if result.is_err() { - report_virtio_error( - self.interrupt_cb.clone(), - self.driver_features, - Some(&self.deactivate_evt), - ); - } - return result; - } - Ok(false) + self.aio.handle().map_err(|e| { + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + Some(&self.deactivate_evt), + ); + e + }) } fn update_evt_handler(&mut self) { @@ -708,26 +700,23 @@ impl BlockIoHandler { EventSet::IN, Vec::new(), ), - ]; - if let Some(lb) = self.leak_bucket.as_ref() { - notifiers.push(EventNotifier::new( + EventNotifier::new( NotifierOperation::Delete, - lb.as_raw_fd(), + self.aio.fd.as_raw_fd(), None, EventSet::IN, Vec::new(), - )); - } - if let Some(aio) = &self.aio { + ), + ]; + if let Some(lb) = self.leak_bucket.as_ref() { notifiers.push(EventNotifier::new( NotifierOperation::Delete, - aio.fd.as_raw_fd(), + lb.as_raw_fd(), None, EventSet::IN, Vec::new(), )); } - notifiers } } @@ -826,45 +815,42 @@ impl EventNotifierHelper for BlockIoHandler { } // Register event notifier for aio. - if let Some(ref aio) = handler_raw.aio { - let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { - read_fd(fd); - if let Err(ref e) = h_clone.lock().unwrap().aio_complete_handler() { - error!("Failed to handle aio {:?}", e); - } - None - }); - - let h_clone = handler.clone(); - let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { - let done = h_clone - .lock() - .unwrap() - .aio_complete_handler() - .with_context(|| "Failed to handle aio") - .ok()?; - if done { - Some(Vec::new()) - } else { - None - } - }); + let h_clone = handler.clone(); + let h: Box = Box::new(move |_, fd: RawFd| { + read_fd(fd); + if let Err(ref e) = h_clone.lock().unwrap().aio_complete_handler() { + error!("Failed to handle aio {:?}", e); + } + None + }); - let mut e = EventNotifier::new( - NotifierOperation::AddShared, - aio.fd.as_raw_fd(), - None, - EventSet::IN, - vec![ - Arc::new(Mutex::new(h)), - Arc::new(Mutex::new(handler_iopoll)), - ], - ); - e.io_poll = true; + let h_clone = handler.clone(); + let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { + let done = h_clone + .lock() + .unwrap() + .aio_complete_handler() + .with_context(|| "Failed to handle aio") + .ok()?; + if done { + Some(Vec::new()) + } else { + None + } + }); - notifiers.push(e); - } + let mut e = EventNotifier::new( + NotifierOperation::AddShared, + handler_raw.aio.fd.as_raw_fd(), + None, + EventSet::IN, + vec![ + Arc::new(Mutex::new(h)), + Arc::new(Mutex::new(handler_iopoll)), + ], + ); + e.io_poll = true; + notifiers.push(e); notifiers } @@ -1137,7 +1123,8 @@ impl VirtioDevice for Block { senders.push(sender); let eventfd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let mut handler = BlockIoHandler { + let engine = self.blk_cfg.aio.as_ref(); + let handler = BlockIoHandler { queue: queue.clone(), queue_evt: queue_evts.remove(0), mem_space: mem_space.clone(), @@ -1146,7 +1133,7 @@ impl VirtioDevice for Block { direct: self.blk_cfg.direct, aio_type: self.blk_cfg.aio.clone(), serial_num: self.blk_cfg.serial_num.clone(), - aio: None, + aio: Box::new(Aio::new(Arc::new(BlockIoHandler::complete_func), engine)?), driver_features: self.state.driver_features, receiver, update_evt: self.update_evt.try_clone().unwrap(), @@ -1155,9 +1142,7 @@ impl VirtioDevice for Block { iothread: self.blk_cfg.iothread.clone(), leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), }; - self.deactivate_evts.events.push(eventfd); - handler.aio = Some(handler.build_aio(self.blk_cfg.aio.as_ref())?); EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), -- Gitee From 7ff758dfe232db5b9e427c80977324399ddfe5f5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 06:13:33 +0800 Subject: [PATCH 0621/2187] virtio-blk: Unify notifier builder Make code be more clean. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 51 +++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index dcb1a14b4..8dbf38789 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -721,14 +721,19 @@ impl BlockIoHandler { } } -fn build_event_notifier(fd: RawFd, handler: Box) -> EventNotifier { - EventNotifier::new( +fn build_event_notifier( + fd: RawFd, + handlers: Vec>>>, +) -> EventNotifier { + let mut notifier = EventNotifier::new( NotifierOperation::AddShared, fd, None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], - ) + handlers, + ); + notifier.io_poll = notifier.handlers.len() == 2; + notifier } impl EventNotifierHelper for BlockIoHandler { @@ -743,7 +748,10 @@ impl EventNotifierHelper for BlockIoHandler { h_clone.lock().unwrap().update_evt_handler(); None }); - notifiers.push(build_event_notifier(handler_raw.update_evt.as_raw_fd(), h)); + notifiers.push(build_event_notifier( + handler_raw.update_evt.as_raw_fd(), + vec![Arc::new(Mutex::new(h))], + )); // Register event notifier for deactivate_evt. let h_clone = handler.clone(); @@ -753,20 +761,18 @@ impl EventNotifierHelper for BlockIoHandler { }); notifiers.push(build_event_notifier( handler_raw.deactivate_evt.as_raw_fd(), - h, + vec![Arc::new(Mutex::new(h))], )); // Register event notifier for queue_evt. let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(ref e) = h_clone.lock().unwrap().process_queue() { error!("Failed to handle block IO {:?}", e); } None }); - let h_clone = handler.clone(); let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { let done = h_clone @@ -781,37 +787,31 @@ impl EventNotifierHelper for BlockIoHandler { None } }); - - let mut e = EventNotifier::new( - NotifierOperation::AddShared, + notifiers.push(build_event_notifier( handler_raw.queue_evt.as_raw_fd(), - None, - EventSet::IN, vec![ Arc::new(Mutex::new(h)), Arc::new(Mutex::new(handler_iopoll)), ], - ); - e.io_poll = true; - - notifiers.push(e); + )); // Register timer event notifier for IO limits if let Some(lb) = handler_raw.leak_bucket.as_ref() { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Some(lb) = h_clone.lock().unwrap().leak_bucket.as_mut() { lb.clear_timer(); } - if let Err(ref e) = h_clone.lock().unwrap().process_queue() { error!("Failed to handle block IO {:?}", e); } None }); - notifiers.push(build_event_notifier(lb.as_raw_fd(), h)); + notifiers.push(build_event_notifier( + lb.as_raw_fd(), + vec![Arc::new(Mutex::new(h))], + )); } // Register event notifier for aio. @@ -823,7 +823,6 @@ impl EventNotifierHelper for BlockIoHandler { } None }); - let h_clone = handler.clone(); let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { let done = h_clone @@ -838,19 +837,13 @@ impl EventNotifierHelper for BlockIoHandler { None } }); - - let mut e = EventNotifier::new( - NotifierOperation::AddShared, + notifiers.push(build_event_notifier( handler_raw.aio.fd.as_raw_fd(), - None, - EventSet::IN, vec![ Arc::new(Mutex::new(h)), Arc::new(Mutex::new(handler_iopoll)), ], - ); - e.io_poll = true; - notifiers.push(e); + )); notifiers } -- Gitee From 9890ab11122731820277ddaeda280457f27cf956 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 10:17:51 +0800 Subject: [PATCH 0622/2187] virtio-pci: do not register irqfd for invalid vector The default vector of queue is 0xff which means invalid vector. In edk2 stage, it will activate vhost-user-blk device, but not set the vector of the queue, and it will cause overflow in get_message(). Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 0811b87b9..492bb5a56 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1547,6 +1547,9 @@ fn virtio_pci_register_irqfd( } let vector = queue_mutex.lock().unwrap().vring.get_queue_config().vector; + if vector == INVALID_VECTOR_NUM { + continue; + } let entry = locked_msix.get_message(vector as u16); let msix_vector = MsiVector { msg_addr_lo: entry.address_lo, -- Gitee From be9d29a406199718432f1e3ce0f2a6b073aee00e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 13:22:07 +0800 Subject: [PATCH 0623/2187] virtio: Factor out queue_config from SplitVring And implement deref trait to allow accessing data of queue_config directly. Signed-off-by: Keqian Zhu --- virtio/src/virtqueue/split.rs | 85 +++++++++-------------------------- 1 file changed, 22 insertions(+), 63 deletions(-) diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index f5f59ca08..24881d718 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -13,6 +13,7 @@ use std::cmp::min; use std::mem::size_of; use std::num::Wrapping; +use std::ops::{Deref, DerefMut}; use std::sync::atomic::{fence, Ordering}; use std::sync::Arc; @@ -79,11 +80,11 @@ pub struct QueueConfig { /// Interrupt vector index of the queue for msix pub vector: u16, /// The next index which can be popped in the available vring. - next_avail: u16, + next_avail: Wrapping, /// The next index which can be pushed in the used vring. - next_used: u16, + next_used: Wrapping, /// The index of last descriptor used which has triggered interrupt. - last_signal_used: u16, + last_signal_used: Wrapping, } impl QueueConfig { @@ -104,9 +105,9 @@ impl QueueConfig { size: max_size, ready: false, vector: INVALID_VECTOR_NUM, - next_avail: 0, - next_used: 0, - last_signal_used: 0, + next_avail: Wrapping(0), + next_used: Wrapping(0), + last_signal_used: Wrapping(0), } } @@ -375,41 +376,21 @@ impl ByteCode for SplitVringDesc {} pub struct SplitVring { /// Region cache information. cache: Option, - /// Guest physical address of the descriptor table. - /// The table is composed of descriptors(SplitVringDesc). - desc_table: GuestAddress, - - /// Guest physical address of the available ring. - /// The ring is composed of flags(u16), idx(u16), ring[size](u16) and used_event(u16). - avail_ring: GuestAddress, - - /// Guest physical address of the used ring. - /// The ring is composed of flags(u16), idx(u16), used_ring[size](UsedElem) and avail_event(u16). - used_ring: GuestAddress, - - /// Host address cache. - addr_cache: VirtioAddrCache, - - /// Indicate whether the queue configuration is finished. - ready: bool, - - /// The maximal size in elements offered by the device. - max_size: u16, - - /// The queue size set by frontend. - size: u16, - - /// Interrupt vector index of the queue for msix - vector: u16, - - /// The next index which can be popped in the available vring. - next_avail: Wrapping, + /// The configuration of virtqueue. + queue_config: QueueConfig, +} - /// The next index which can be pushed in the used vring. - next_used: Wrapping, +impl Deref for SplitVring { + type Target = QueueConfig; + fn deref(&self) -> &Self::Target { + &self.queue_config + } +} - /// The index of last descriptor used which has triggered interrupt. - last_signal_used: Wrapping, +impl DerefMut for SplitVring { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.queue_config + } } impl SplitVring { @@ -421,17 +402,7 @@ impl SplitVring { pub fn new(queue_config: QueueConfig) -> Self { SplitVring { cache: None, - desc_table: queue_config.desc_table, - avail_ring: queue_config.avail_ring, - used_ring: queue_config.used_ring, - addr_cache: queue_config.addr_cache, - ready: queue_config.ready, - max_size: queue_config.max_size, - size: queue_config.size, - vector: queue_config.vector, - next_avail: Wrapping(queue_config.next_avail), - next_used: Wrapping(queue_config.next_used), - last_signal_used: Wrapping(queue_config.last_signal_used), + queue_config, } } @@ -856,19 +827,7 @@ impl VringOps for SplitVring { } fn get_queue_config(&self) -> QueueConfig { - QueueConfig { - desc_table: self.desc_table, - avail_ring: self.avail_ring, - used_ring: self.used_ring, - addr_cache: self.addr_cache, - ready: self.ready, - max_size: self.max_size, - size: self.size, - vector: self.vector, - next_avail: self.next_avail.0, - next_used: self.next_used.0, - last_signal_used: self.last_signal_used.0, - } + self.queue_config } /// The number of descriptor chains in the available ring. -- Gitee From afe05bf336baa6e4113020ce0b7876560f2ee6ad Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 21 Dec 2022 10:44:53 +0800 Subject: [PATCH 0624/2187] virtio: Handle wrapping around in virtqueue add_used Though it's almost impossible, we must handle it. Signed-off-by: Keqian Zhu --- virtio/src/virtqueue/split.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 24881d718..481d0d48a 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -85,6 +85,8 @@ pub struct QueueConfig { next_used: Wrapping, /// The index of last descriptor used which has triggered interrupt. last_signal_used: Wrapping, + /// The last_signal_used is valid or not. + signal_used_valid: bool, } impl QueueConfig { @@ -108,6 +110,7 @@ impl QueueConfig { next_avail: Wrapping(0), next_used: Wrapping(0), last_signal_used: Wrapping(0), + signal_used_valid: false, } } @@ -558,8 +561,10 @@ impl SplitVring { } }; + let valid = self.signal_used_valid; + self.signal_used_valid = true; self.last_signal_used = new; - (new - used_event_idx - Wrapping(1)) < (new - old) + !valid || (new - used_event_idx - Wrapping(1)) < (new - old) } fn is_overlap( @@ -782,21 +787,23 @@ impl VringOps for SplitVring { sys_mem .write_object_direct::(&used_elem, used_elem_addr) .with_context(|| "Failed to write object for used element")?; - - self.next_used += Wrapping(1); - + // Make sure used element is filled before updating used idx. fence(Ordering::Release); + self.next_used += Wrapping(1); sys_mem .write_object_direct( &(self.next_used.0 as u16), self.addr_cache.used_ring_host + VRING_IDX_POSITION, ) .with_context(|| "Failed to write next used idx")?; - // Make sure used index is exposed before notifying guest. fence(Ordering::SeqCst); + // Do we wrap around? + if self.next_used == self.last_signal_used { + self.signal_used_valid = false; + } Ok(()) } @@ -827,7 +834,9 @@ impl VringOps for SplitVring { } fn get_queue_config(&self) -> QueueConfig { - self.queue_config + let mut config = self.queue_config; + config.signal_used_valid = false; + config } /// The number of descriptor chains in the available ring. -- Gitee From 0ac9dd2d93dd1bf893633b3f1082a5d2071a8ab0 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 16 Dec 2022 15:01:49 +0800 Subject: [PATCH 0625/2187] virtiofs: supports the feature for lightweight VMs Add call_events for virtiofs. We use this to notify the virtual machine that the request has completed. Signed-off-by: Li HuaChao --- machine/src/lib.rs | 15 ++++-- virtio/src/vhost/user/fs.rs | 92 ++++++++++++++++++++++++++++++++++--- virtio/src/virtio_mmio.rs | 7 +++ 3 files changed, 105 insertions(+), 9 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c62ab7171..bdbc04536 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -463,17 +463,26 @@ pub trait MachineOps { let dev_cfg = parse_fs(vm_config, cfg_args)?; let id_clone = dev_cfg.id.clone(); let sys_mem = self.get_sys_mem().clone(); - let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); if !vm_config.machine_config.mem_config.mem_share { bail!("When configuring the vhost-user-fs-device or vhost-user-fs-pci device, the memory must be shared."); } if cfg_args.contains("vhost-user-fs-device") { - let device = VirtioMmioDevice::new(&sys_mem, device); - self.realize_virtio_mmio_device(device) + let device = Arc::new(Mutex::new(vhost::user::Fs::new( + dev_cfg, + sys_mem.clone(), + false, + ))); + let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); + self.realize_virtio_mmio_device(virtio_mmio_device) .with_context(|| "Failed to add vhost user fs device")?; } else if cfg_args.contains("vhost-user-fs-pci") { + let device = Arc::new(Mutex::new(vhost::user::Fs::new( + dev_cfg, + sys_mem.clone(), + true, + ))); let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index c4134073f..a72795afb 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -20,9 +20,11 @@ const VIRTIO_FS_QUEUE_SIZE: u16 = 128; use crate::VirtioError; use std::cmp; use std::io::Write; +use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; -use vmm_sys_util::eventfd::EventFd; +use log::error; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::AddressSpace; use machine_manager::{ @@ -30,13 +32,13 @@ use machine_manager::{ event_loop::EventLoop, }; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; +use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use util::num_ops::read_u32; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; -use super::super::VhostOps; +use super::super::{VhostNotify, VhostOps}; use super::VhostUserClient; -use crate::VirtioInterrupt; +use crate::{VirtioInterrupt, VirtioInterruptType}; use anyhow::{anyhow, Context, Result}; #[derive(Copy, Clone)] @@ -57,6 +59,53 @@ impl Default for VirtioFsConfig { impl ByteCode for VirtioFsConfig {} +struct VhostUserFsHandler { + interrup_cb: Arc, + host_notifies: Vec, +} + +impl EventNotifierHelper for VhostUserFsHandler { + fn internal_notifiers(vhost_user_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let vhost_user = vhost_user_handler.clone(); + + let handler: Box Option>> = + Box::new(move |_, fd: RawFd| { + read_fd(fd); + + let locked_vhost_user = vhost_user.lock().unwrap(); + + for host_notify in locked_vhost_user.host_notifies.iter() { + if let Err(e) = (locked_vhost_user.interrup_cb)( + &VirtioInterruptType::Vring, + Some(&host_notify.queue.lock().unwrap()), + false, + ) { + error!( + "Failed to trigger interrupt for vhost user device, error is {:?}", + e + ); + } + } + + None as Option> + }); + let h = Arc::new(Mutex::new(handler)); + + for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + host_notify.notify_evt.as_raw_fd(), + None, + EventSet::IN, + vec![h.clone()], + )); + } + + notifiers + } +} + pub struct Fs { fs_cfg: FsConfig, config: VirtioFsConfig, @@ -66,10 +115,18 @@ pub struct Fs { mem_space: Arc, /// The notifier events from host. call_events: Vec, + enable_irqfd: bool, } impl Fs { - pub fn new(fs_cfg: FsConfig, mem_space: Arc) -> Self { + /// The construct function of the Fs device. + /// + /// # Arguments + /// + /// `fs_cfg` - The config of this Fs device. + /// `mem_space` - The address space of this Fs device. + /// `enable_irqfd` - Whether irqfd is enabled on this Fs device. + pub fn new(fs_cfg: FsConfig, mem_space: Arc, enable_irqfd: bool) -> Self { Fs { fs_cfg, config: VirtioFsConfig::default(), @@ -78,6 +135,7 @@ impl Fs { acked_features: 0_u64, mem_space, call_events: Vec::::new(), + enable_irqfd, } } } @@ -167,10 +225,11 @@ impl VirtioDevice for Fs { fn activate( &mut self, _mem_space: Arc, - _interrup_cb: Arc, + interrup_cb: Arc, queues: &[Arc>], queue_evts: Vec, ) -> Result<()> { + let mut host_notifies = Vec::new(); let mut client = match &self.client { Some(client) => client.lock().unwrap(), None => return Err(anyhow!("Failed to get client for virtio fs")), @@ -179,6 +238,27 @@ impl VirtioDevice for Fs { client.set_queues(queues); client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; + + if !self.enable_irqfd { + for (queue_index, queue_mutex) in queues.iter().enumerate() { + let host_notify = VhostNotify { + notify_evt: self.call_events[queue_index].try_clone().unwrap(), + queue: queue_mutex.clone(), + }; + host_notifies.push(host_notify); + } + + let handler = VhostUserFsHandler { + interrup_cb, + host_notifies, + }; + + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), + None, + )?; + } + Ok(()) } diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 8878ec934..6f03f3700 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -437,6 +437,13 @@ impl VirtioMmioDevice { queue_evts.push(evt_fd_clone); } + let mut events = Vec::new(); + for _i in 0..self.device.lock().unwrap().queue_num() { + events.push(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + } + + self.device.lock().unwrap().set_guest_notifiers(&events)?; + if let Some(cb) = self.interrupt_cb.clone() { self.device.lock().unwrap().activate( self.mem_space.clone(), -- Gitee From 6c6253c1ebe0a12932defefcb11f606a3635cee1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 15:22:47 +0800 Subject: [PATCH 0626/2187] virtio-pci: directly return when activate()/deactivate() failed It should return false directly when activate()/deactivate() failed to prevent the normal code from being executed. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 492bb5a56..7ef284b03 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -852,6 +852,7 @@ impl VirtioPciDevice { .set_guest_notifiers(&call_evts.events) { error!("Failed to set guest notifiers, error is {:?}", e); + return false; } } if let Err(e) = cloned_pci_device.device.lock().unwrap().activate( @@ -861,6 +862,7 @@ impl VirtioPciDevice { queue_evts, ) { error!("Failed to activate device, error is {:?}", e); + return false; } } else { error!("Failed to activate device: No interrupt callback"); @@ -910,6 +912,7 @@ impl VirtioPciDevice { cloned_msix.lock().unwrap().reset(); if let Err(e) = cloned_pci_device.device.lock().unwrap().deactivate() { error!("Failed to deactivate virtio device, error is {:?}", e); + return false; } } update_dev_id( -- Gitee From b318f668aba672fb55f717814f51ee638f2b590f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 15:33:52 +0800 Subject: [PATCH 0627/2187] virtio-pci: reduce the lock scope of the queues Reduce the lock scope of VirtioDevice.queues when activate and deactivate the device. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 7ef284b03..69c4053a8 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -868,6 +868,7 @@ impl VirtioPciDevice { error!("Failed to activate device: No interrupt callback"); return false; } + drop(locked_queues); cloned_pci_device .device_activated .store(true, Ordering::Release); @@ -878,7 +879,6 @@ impl VirtioPciDevice { &cloned_pci_device.dev_id, ); - drop(locked_queues); if cloned_pci_device.need_irqfd && !virtio_pci_register_irqfd( &cloned_pci_device, @@ -902,8 +902,7 @@ impl VirtioPciDevice { virtio_pci_unregister_irqfd(cloned_gsi_routes.clone()); } - let mut locked_queues = cloned_pci_device.queues.lock().unwrap(); - locked_queues.clear(); + cloned_pci_device.queues.lock().unwrap().clear(); if cloned_pci_device.device_activated.load(Ordering::Acquire) { cloned_pci_device .device_activated -- Gitee From 6e27a990c8399733a82388a4dc6ed18cb4f2b75c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 16:59:54 +0800 Subject: [PATCH 0628/2187] virtio-net: fix retport virtio error in virtio-net Fix commit b2374fd2 "virtio-net: report virtio error in some error case": 1) do not add report_virtio_error() when handle_tx() failed; 2) directly return after report_virtio_error(); Signed-off-by: Yan Wang --- virtio/src/net.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 67a92b7c7..fe34bc819 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1116,8 +1116,14 @@ impl EventNotifierHelper for NetIoHandler { let cloned_net_io = net_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(ref e) = cloned_net_io.lock().unwrap().handle_tx() { + let mut locked_net_io = cloned_net_io.lock().unwrap(); + if let Err(ref e) = locked_net_io.handle_tx() { error!("Failed to handle tx(tx event) for net, {:?}", e); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + Some(&locked_net_io.deactivate_evt), + ); } None }); @@ -1141,6 +1147,7 @@ impl EventNotifierHelper for NetIoHandler { locked_net_io.driver_features, Some(&locked_net_io.deactivate_evt), ); + return None; } if let Some(tap) = locked_net_io.tap.as_ref() { -- Gitee From 586d50c967a4ed3345fc53661b87faef6a12e9c4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 21 Dec 2022 17:41:14 +0800 Subject: [PATCH 0629/2187] virtio-net: using a common function to convert ElemIovec to libc::iovec It has a common code to convert ElemIovec to libc::iovec in handle_tx() and handle_rx(). So, using a function to do it. Signed-off-by: Yan Wang --- virtio/src/net.rs | 69 ++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index fe34bc819..044bbf86c 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -38,7 +38,7 @@ use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, Element, VirtioError, }; -use address_space::AddressSpace; +use address_space::{AddressSpace, RegionCache}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::{ @@ -734,6 +734,29 @@ impl NetIoHandler { size } + fn get_libc_iovecs( + mem_space: &Arc, + cache: &Option, + elem_iovecs: &[ElemIovec], + ) -> Result> { + let mut iovecs = Vec::new(); + for elem_iov in elem_iovecs.iter() { + let host_addr = mem_space + .get_host_address_from_cache(elem_iov.addr, cache) + .unwrap_or(0); + if host_addr != 0 { + let iovec = libc::iovec { + iov_base: host_addr as *mut libc::c_void, + iov_len: elem_iov.len as libc::size_t, + }; + iovecs.push(iovec); + } else { + bail!("Failed to get host address for {}", elem_iov.addr.0); + } + } + Ok(iovecs) + } + fn handle_rx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to rx".to_string()); let mut queue = self.rx.queue.lock().unwrap(); @@ -758,22 +781,12 @@ impl NetIoHandler { if elem.desc_num == 0 { break; } - let mut iovecs = Vec::new(); - for elem_iov in elem.in_iovec.iter() { - let host_addr = self - .mem_space - .get_host_address_from_cache(elem_iov.addr, queue.vring.get_cache()) - .unwrap_or(0); - if host_addr != 0 { - let iovec = libc::iovec { - iov_base: host_addr as *mut libc::c_void, - iov_len: elem_iov.len as libc::size_t, - }; - iovecs.push(iovec); - } else { - bail!("Failed to get host address for {}", elem_iov.addr.0); - } - } + let iovecs = NetIoHandler::get_libc_iovecs( + &self.mem_space, + queue.vring.get_cache(), + &elem.in_iovec, + ) + .with_context(|| "Failed to get libc iovecs for net rx")?; // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. for iov in iovecs.iter() { @@ -880,22 +893,12 @@ impl NetIoHandler { .with_context(|| "Failed to trigger tx queue event".to_string())?; break; } - let mut iovecs = Vec::new(); - for elem_iov in elem.out_iovec.iter() { - let host_addr = self - .mem_space - .get_host_address_from_cache(elem_iov.addr, queue.vring.get_cache()) - .unwrap_or(0); - if host_addr != 0 { - let iovec = libc::iovec { - iov_base: host_addr as *mut libc::c_void, - iov_len: elem_iov.len as libc::size_t, - }; - iovecs.push(iovec); - } else { - error!("Failed to get host address for {}", elem_iov.addr.0); - } - } + let iovecs = NetIoHandler::get_libc_iovecs( + &self.mem_space, + queue.vring.get_cache(), + &elem.out_iovec, + ) + .with_context(|| "Failed to get libc iovecs for net tx")?; let tap_fd = if let Some(tap) = self.tap.as_mut() { tap.as_raw_fd() as libc::c_int } else { -- Gitee From acfff56f214a49a8748ac38625b99e01383d97c7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 19:49:59 +0800 Subject: [PATCH 0630/2187] usb: only the do_parameter needs to be called on the control plane According to the spec 4.11.2.2, a Setup Stage TRB shall contain immediate data(IDT flag = '1'), its Parameter fields shall contain the 8-byte USB SETUP Data, which defines the request and the request's parameters that will be sent to the device in the USB Setup stage transaction, and its Length field shall be set to '8'. Therefore, only the do_parameter need to be called. Signed-off-by: zhouli57 --- usb/src/usb.rs | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 4743923a9..a93cc3d92 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -19,7 +19,7 @@ use crate::descriptor::{ }; use crate::xhci::xhci_controller::XhciDevice; use anyhow::{bail, Result}; -use log::{debug, error, warn}; +use log::{debug, error}; const USB_MAX_ENDPOINTS: u32 = 15; const USB_MAX_INTERFACES: u32 = 16; @@ -470,24 +470,7 @@ pub trait UsbDeviceOps: Send + Sync { debug!("process_packet nr {}", ep_nr); drop(locked_ep); if ep_nr == 0 { - if packet.parameter != 0 { - return self.do_parameter(packet); - } - match packet.pid as u8 { - USB_TOKEN_SETUP => { - warn!("process_packet USB_TOKEN_SETUP not implemented"); - } - USB_TOKEN_IN => { - warn!("process_packet USB_TOKEN_IN not implemented"); - } - USB_TOKEN_OUT => { - warn!("process_packet USB_TOKEN_OUT not implemented"); - } - _ => { - warn!("Unknown pid {}", packet.pid); - packet.status = UsbPacketStatus::Stall; - } - } + self.do_parameter(packet)?; } else { self.handle_data(packet); } -- Gitee From bd284c26831ec6770295b2c1dd1041e1c0334164 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 20:24:44 +0800 Subject: [PATCH 0631/2187] usb: remove the packet state The packet state is checked only before the handle packet to prevent repeated processing. Currently, the handle packet is entered for multiple times only in the case of NAK. In this case, it is reasonable. Signed-off-by: zhouli57 --- usb/src/usb.rs | 73 +++++++++++++------------------------------------- 1 file changed, 19 insertions(+), 54 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index a93cc3d92..f85351d63 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -38,16 +38,6 @@ pub enum UsbPacketStatus { Async, } -/// USB packet setup state. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum SetupState { - Idle, - Setup, - Data, - Ack, - Parameter, -} - /// USB request used to transfer to USB device. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -119,17 +109,6 @@ pub struct UsbDescString { pub str: String, } -/// USB packet state. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum UsbPacketState { - Undefined = 0, - Setup, - Queued, - Async, - Complete, - Canceled, -} - // USB descriptor pub struct UsbDesc { pub full_dev: Option>, @@ -424,15 +403,24 @@ pub trait UsbDeviceOps: Send + Sync { /// Handle usb packet, used for controller to deliever packet to device. fn handle_packet(&mut self, packet: &mut UsbPacket) { - if packet.state != UsbPacketState::Setup { - error!("The packet state is not Setup"); + packet.status = UsbPacketStatus::Success; + let ep = if let Some(ep) = &packet.ep { + ep + } else { + error!("Failed to find ep"); + packet.status = UsbPacketStatus::NoDev; return; - } - if let Err(e) = self.process_packet(packet) { - error!("Failed to process packet: {}", e); - } - if packet.status != UsbPacketStatus::Nak { - packet.state = UsbPacketState::Complete; + }; + let locked_ep = ep.lock().unwrap(); + let ep_nr = locked_ep.ep_number; + debug!("handle packet endpointer number {}", ep_nr); + drop(locked_ep); + if ep_nr == 0 { + if let Err(e) = self.do_parameter(packet) { + error!("Failed to handle control packet {}", e); + } + } else { + self.handle_data(packet); } } @@ -457,26 +445,6 @@ pub trait UsbDeviceOps: Send + Sync { usb_dev.speed } - fn process_packet(&mut self, packet: &mut UsbPacket) -> Result<()> { - packet.status = UsbPacketStatus::Success; - let ep = if let Some(ep) = &packet.ep { - ep - } else { - packet.status = UsbPacketStatus::NoDev; - bail!("Failed to find ep"); - }; - let locked_ep = ep.lock().unwrap(); - let ep_nr = locked_ep.ep_number; - debug!("process_packet nr {}", ep_nr); - drop(locked_ep); - if ep_nr == 0 { - self.do_parameter(packet)?; - } else { - self.handle_data(packet); - } - Ok(()) - } - fn do_parameter(&mut self, p: &mut UsbPacket) -> Result<()> { let usb_dev = self.get_mut_usb_device(); let device_req = UsbDeviceRequest { @@ -583,15 +551,14 @@ pub struct UsbPacket { pub status: UsbPacketStatus, /// Actually transfer length. pub actual_length: u32, - pub state: UsbPacketState, } impl std::fmt::Display for UsbPacket { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "pid {} param {} status {:?} actual_length {}, state {:?}", - self.pid, self.parameter, self.status, self.actual_length, self.state + "pid {} param {} status {:?} actual_length {}", + self.pid, self.parameter, self.status, self.actual_length ) } } @@ -603,7 +570,6 @@ impl UsbPacket { self.status = UsbPacketStatus::Success; self.actual_length = 0; self.parameter = 0; - self.state = UsbPacketState::Setup; } } @@ -616,7 +582,6 @@ impl Default for UsbPacket { parameter: 0, status: UsbPacketStatus::NoDev, actual_length: 0, - state: UsbPacketState::Undefined, } } } -- Gitee From f5509851b06d2555daeebe6b191a1b218b1d8275 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Dec 2022 20:32:16 +0800 Subject: [PATCH 0632/2187] usb: simplify the tablet polling data Generally, the number of event in queue is greater than 0, copy the current data directly. Signed-off-by: zhouli57 --- usb/src/hid.rs | 12 ++---------- usb/src/tablet.rs | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 2685421a5..031b426da 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -355,27 +355,19 @@ impl Hid { } fn pointer_poll(&mut self) -> Vec { - let index = if self.num > 0 { - self.head - } else if self.head > 0 { - self.head - 1 - } else { - QUEUE_LENGTH - 1 - }; + let index = self.head; if self.num != 0 { self.increase_head(); self.num -= 1; } let evt = &mut self.pointer.queue[(index & QUEUE_MASK) as usize]; - let z = evt.pos_z; - evt.pos_z = 0; vec![ evt.button_state as u8, evt.pos_x as u8, (evt.pos_x >> 8) as u8, evt.pos_y as u8, (evt.pos_y >> 8) as u8, - z as u8, + evt.pos_z as u8, ] } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index c441fb669..9a58eaa81 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -151,7 +151,7 @@ impl UsbTablet { // Used for VNC to send pointer event. pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32) -> Result<()> { let mut locked_tablet = tablet.lock().unwrap(); - if locked_tablet.hid.num == QUEUE_LENGTH - 1 { + if locked_tablet.hid.num >= QUEUE_LENGTH { debug!("Pointer queue is full!"); // Return ok to ignore the request. return Ok(()); -- Gitee From 2c2b1ce0454b2b2972c4075ce1ffdc001069a00c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 20 Dec 2022 09:51:44 +0800 Subject: [PATCH 0633/2187] usb: correct the length of the usb packet during transmisson Correct the length of the usb packet during transmission. Signed-off-by: zhouli57 --- usb/src/usb.rs | 138 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 126 insertions(+), 12 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index f85351d63..5bf6a880e 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; use crate::config::*; @@ -601,35 +602,148 @@ fn write_mem(hva: u64, buf: &[u8]) { /// Transfer packet from host to device or from device to host. pub fn usb_packet_transfer(packet: &mut UsbPacket, vec: &mut [u8], len: usize) { + let len = min(vec.len(), len); let to_host = packet.pid as u8 & USB_TOKEN_IN == USB_TOKEN_IN; - + let mut copyed = 0; if to_host { - let mut copyed = 0; - let mut offset = 0; for iov in &packet.iovecs { - let cnt = std::cmp::min(iov.iov_len, len - copyed); - let tmp = &vec[offset..(offset + cnt)]; + let cnt = min(iov.iov_len, len - copyed); + let tmp = &vec[copyed..(copyed + cnt)]; write_mem(iov.iov_base, tmp); copyed += cnt; - offset += cnt; if len - copyed == 0 { break; } } } else { - let mut copyed = 0; - let mut offset = 0; for iov in &packet.iovecs { - let cnt = std::cmp::min(iov.iov_len, len - copyed); - let tmp = &mut vec[offset..(offset + cnt)]; + let cnt = min(iov.iov_len, len - copyed); + let tmp = &mut vec[copyed..(copyed + cnt)]; read_mem(iov.iov_base, tmp); copyed += cnt; - offset += cnt; if len - copyed == 0 { break; } } } + packet.actual_length = copyed as u32; +} - packet.actual_length += len as u32; +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_usb_packet_transfer_in() { + let buf = [0_u8; 10]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_IN as u32; + packet.iovecs.push(Iovec::new(hva, 4)); + packet.iovecs.push(Iovec::new(hva + 4, 2)); + let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; + usb_packet_transfer(&mut packet, &mut data, 6); + assert_eq!(packet.actual_length, 6); + assert_eq!(buf, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_in_over() { + let buf = [0_u8; 10]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_IN as u32; + packet.iovecs.push(Iovec::new(hva, 4)); + + let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; + usb_packet_transfer(&mut packet, &mut data, 6); + assert_eq!(packet.actual_length, 4); + assert_eq!(buf, [1, 2, 3, 4, 0, 0, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_in_under() { + let buf = [0_u8; 10]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_IN as u32; + packet.iovecs.push(Iovec::new(hva, 4)); + + let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; + usb_packet_transfer(&mut packet, &mut data, 2); + assert_eq!(packet.actual_length, 2); + assert_eq!(buf, [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_in_over_buffer() { + let buf = [0_u8; 10]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_IN as u32; + packet.iovecs.push(Iovec::new(hva, 10)); + + let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; + usb_packet_transfer(&mut packet, &mut data, 10); + assert_eq!(packet.actual_length, 6); + assert_eq!(buf, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_out() { + let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_OUT as u32; + packet.iovecs.push(Iovec::new(hva, 4)); + packet.iovecs.push(Iovec::new(hva + 4, 2)); + + let mut data = [0_u8; 10]; + usb_packet_transfer(&mut packet, &mut data, 6); + assert_eq!(packet.actual_length, 6); + assert_eq!(data, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_out_over() { + let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_OUT as u32; + packet.iovecs.push(Iovec::new(hva, 4)); + packet.iovecs.push(Iovec::new(hva + 4, 2)); + + let mut data = [0_u8; 10]; + usb_packet_transfer(&mut packet, &mut data, 10); + assert_eq!(packet.actual_length, 6); + assert_eq!(data, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_out_under() { + let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_OUT as u32; + packet.iovecs.push(Iovec::new(hva, 4)); + + let mut data = [0_u8; 10]; + usb_packet_transfer(&mut packet, &mut data, 2); + assert_eq!(packet.actual_length, 2); + assert_eq!(data, [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]); + } + + #[test] + fn test_usb_packet_transfer_out_over_buffer() { + let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; + let hva = buf.as_ptr() as u64; + let mut packet = UsbPacket::default(); + packet.pid = USB_TOKEN_OUT as u32; + packet.iovecs.push(Iovec::new(hva, 6)); + + let mut data = [0_u8; 2]; + usb_packet_transfer(&mut packet, &mut data, 6); + assert_eq!(packet.actual_length, 2); + assert_eq!(data, [1, 2]); + } } -- Gitee From 2c1dde89ba41865a0d8e4fc637135d33bd7dc5ff Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 20 Dec 2022 11:29:00 +0800 Subject: [PATCH 0634/2187] usb: use the controller to assign port directly Use the controller to assign port directly, and remove the unused path field in UsbPort. Signed-off-by: zhouli57 --- usb/src/bus.rs | 57 ++------------------------------- usb/src/usb.rs | 4 +-- usb/src/xhci/xhci_controller.rs | 41 ++++++++++++++---------- usb/src/xhci/xhci_pci.rs | 6 +++- 4 files changed, 34 insertions(+), 74 deletions(-) diff --git a/usb/src/bus.rs b/usb/src/bus.rs index 8f69fcab4..7fbc2075a 100644 --- a/usb/src/bus.rs +++ b/usb/src/bus.rs @@ -10,66 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::collections::{HashMap, LinkedList}; +use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use crate::usb::{UsbDeviceOps, UsbPort}; -use anyhow::{bail, Result}; +use crate::usb::UsbDeviceOps; +use anyhow::Result; /// The key is bus name, the value is the device which can attach other devices. pub type BusDeviceMap = Arc>>>>; -/// USB bus used to manage USB ports. -#[derive(Default)] -pub struct UsbBus { - free_ports: LinkedList>>, - used_ports: LinkedList>>, -} - -impl UsbBus { - pub fn new() -> Self { - UsbBus { - free_ports: LinkedList::new(), - used_ports: LinkedList::new(), - } - } - - /// Register USB port to the bus. - pub fn register_usb_port(&mut self, port: &Arc>) { - let mut locked_port = port.lock().unwrap(); - locked_port.path = format!("{}", locked_port.index + 1); - self.free_ports.push_back(port.clone()); - } - - /// Assign USB port and attach the device. - pub fn assign_usb_port( - &mut self, - dev: &Arc>, - ) -> Result>> { - if let Some(port) = self.free_ports.pop_front() { - let mut locked_dev = dev.lock().unwrap(); - locked_dev.set_usb_port(Some(Arc::downgrade(&port))); - let mut locked_port = port.lock().unwrap(); - locked_port.dev = Some(dev.clone()); - drop(locked_port); - self.used_ports.push_back(port.clone()); - Ok(port) - } else { - bail!("No available usb port"); - } - } - - /// Find USB port by path. - pub fn find_usb_port(&self, path: String) -> Option>> { - for usb in &self.used_ports { - if usb.lock().unwrap().path == path { - return Some(usb.clone()); - } - } - None - } -} - /// Bus device ops for USB controller to handle USB device attach/detach. pub trait BusDeviceOps: Send + Sync { fn attach_device(&mut self, dev: &Arc>) -> Result<()>; diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 5bf6a880e..ded08cf50 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -89,8 +89,8 @@ pub fn usb_endpoint_init(dev: &Arc>) { pub struct UsbPort { pub dev: Option>>, pub speed_mask: u32, - pub path: String, pub index: u8, + pub used: bool, } impl UsbPort { @@ -98,8 +98,8 @@ impl UsbPort { Self { dev: None, speed_mask: 0, - path: String::new(), index, + used: false, } } } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 1f3fa7a68..403d04cc1 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -23,7 +23,6 @@ use log::{debug, error, info, warn}; use machine_manager::config::XhciConfig; use util::num_ops::{read_u32, write_u64_low}; -use crate::bus::UsbBus; use crate::config::*; use crate::usb::{ Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, UsbPort, @@ -415,7 +414,6 @@ pub struct XhciDevice { pub intrs: Vec, pub cmd_ring: XhciRing, mem_space: Arc, - pub bus: Arc>, pub ctrl_ops: Option>>, } @@ -446,7 +444,6 @@ impl XhciDevice { intrs: vec![XhciInterrupter::new(mem_space); MAX_INTRS as usize], cmd_ring: XhciRing::new(mem_space), mem_space: mem_space.clone(), - bus: Arc::new(Mutex::new(UsbBus::new())), }; let xhci = Arc::new(Mutex::new(xhci)); let clone_xhci = xhci.clone(); @@ -462,7 +459,6 @@ impl XhciDevice { for i in 0..locked_xhci.numports_2 { let usb_port = Arc::new(Mutex::new(UsbPort::new(i))); locked_xhci.usb_ports.push(usb_port.clone()); - locked_xhci.bus.lock().unwrap().register_usb_port(&usb_port); let mut locked_port = locked_xhci.ports[i as usize].lock().unwrap(); locked_port.name = format!("{}-usb2-{}", locked_port.name, i); locked_port.speed_mask = USB_SPEED_LOW | USB_SPEED_HIGH | USB_SPEED_FULL; @@ -472,7 +468,6 @@ impl XhciDevice { let idx = i + locked_xhci.numports_2; let usb_port = Arc::new(Mutex::new(UsbPort::new(idx))); locked_xhci.usb_ports.push(usb_port.clone()); - locked_xhci.bus.lock().unwrap().register_usb_port(&usb_port); let mut locked_port = locked_xhci.ports[idx as usize].lock().unwrap(); locked_port.name = format!("{}-usb3-{}", locked_port.name, idx); locked_port.speed_mask = USB_SPEED_SUPER; @@ -626,24 +621,18 @@ impl XhciDevice { } fn lookup_usb_port(&mut self, slot_ctx: &XhciSlotCtx) -> Option>> { - let mut path = String::new(); - let mut port = (slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff) as u8; + let port = (slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff) as u8; if port < 1 || port > self.ports.len() as u8 { error!("Invalid port: {}", port); return None; } let usb_port = &self.usb_ports[(port - 1) as usize]; - port = usb_port.lock().unwrap().index + 1; - path += &format!("{}", port); - for i in 0..5 { - port = ((slot_ctx.dev_info >> (4 * i)) & 0x0f) as u8; - if port == 0 { - break; - } - path += &format!(".{}", port); + let locked_port = usb_port.lock().unwrap(); + if locked_port.used { + Some(usb_port.clone()) + } else { + None } - let locked_bus = self.bus.lock().unwrap(); - locked_bus.find_usb_port(path) } /// Control plane @@ -1766,6 +1755,24 @@ impl XhciDevice { .update_intr(v, self.intrs[0].iman & IMAN_IE == IMAN_IE); } } + + /// Assign USB port and attach the device. + pub fn assign_usb_port( + &mut self, + dev: &Arc>, + ) -> Option>> { + for port in &self.usb_ports { + let mut locked_port = port.lock().unwrap(); + if !locked_port.used { + locked_port.used = true; + locked_port.dev = Some(dev.clone()); + let mut locked_dev = dev.lock().unwrap(); + locked_dev.set_usb_port(Some(Arc::downgrade(port))); + return Some(port.clone()); + } + } + None + } } // DMA read/write helpers. diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 93d5cac00..621ec7bcb 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -289,7 +289,11 @@ impl XhciOps for XhciPciDevice { impl BusDeviceOps for XhciPciDevice { fn attach_device(&mut self, dev: &Arc>) -> Result<()> { let mut locked_xhci = self.xhci.lock().unwrap(); - let usb_port = locked_xhci.bus.lock().unwrap().assign_usb_port(dev)?; + let usb_port = if let Some(usb_port) = locked_xhci.assign_usb_port(dev) { + usb_port + } else { + bail!("No available USB port."); + }; let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { xhci_port } else { -- Gitee From b7e546731533515aece920f99f46fb555d0eaec4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 20 Dec 2022 15:48:37 +0800 Subject: [PATCH 0635/2187] xhci: refactor the xhci port Refactor the xhci port, put the port status and control to UsbPort. Signed-off-by: zhouli57 --- usb/src/usb.rs | 35 ++----- usb/src/xhci/xhci_controller.rs | 166 +++++++++++++++++--------------- usb/src/xhci/xhci_pci.rs | 16 +-- usb/src/xhci/xhci_regs.rs | 47 +-------- 4 files changed, 104 insertions(+), 160 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index ded08cf50..c8c71d4e5 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -13,14 +13,15 @@ use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; +use anyhow::{bail, Result}; +use log::{debug, error}; + use crate::config::*; use crate::descriptor::{ UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; -use crate::xhci::xhci_controller::XhciDevice; -use anyhow::{bail, Result}; -use log::{debug, error}; +use crate::xhci::xhci_controller::{UsbPort, XhciDevice}; const USB_MAX_ENDPOINTS: u32 = 15; const USB_MAX_INTERFACES: u32 = 16; @@ -85,25 +86,6 @@ pub fn usb_endpoint_init(dev: &Arc>) { } } -/// USB port which can attached device. -pub struct UsbPort { - pub dev: Option>>, - pub speed_mask: u32, - pub index: u8, - pub used: bool, -} - -impl UsbPort { - pub fn new(index: u8) -> Self { - Self { - dev: None, - speed_mask: 0, - index, - used: false, - } - } -} - /// USB descriptor strings. pub struct UsbDescString { pub index: u32, @@ -499,13 +481,8 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { // Drop the small lock. drop(locked_dev); let mut locked_xhci = xhci.lock().unwrap(); - let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { - xhci_port - } else { - bail!("No xhci port found"); - }; if wakeup { - let mut locked_port = xhci_port.lock().unwrap(); + let mut locked_port = usb_port.lock().unwrap(); let port_status = locked_port.get_port_link_state(); if port_status == PLS_U3 { locked_port.set_port_link_state(PLS_RESUME); @@ -514,7 +491,7 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { locked_port.portsc, port_status ); drop(locked_port); - locked_xhci.port_notify(&xhci_port, PORTSC_PLC)?; + locked_xhci.port_notify(&usb_port, PORTSC_PLC)?; } } if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep) { diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 403d04cc1..047b1cd94 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -24,10 +24,8 @@ use machine_manager::config::XhciConfig; use util::num_ops::{read_u32, write_u64_low}; use crate::config::*; -use crate::usb::{ - Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, UsbPort, -}; -use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter, XhciPort}; +use crate::usb::{Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; +use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter}; use crate::xhci::xhci_ring::{ TRBCCode, TRBType, XhciEventRingSeg, XhciRing, XhciTRB, TRB_EV_ED, TRB_SIZE, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TYPE_SHIFT, @@ -299,6 +297,42 @@ impl XhciSlot { } } +/// USB port which can attached device. +pub struct UsbPort { + pub xhci: Weak>, + /// Port Status and Control + pub portsc: u32, + /// Port ID + pub port_id: u8, + pub speed_mask: u32, + pub dev: Option>>, + pub used: bool, +} + +impl UsbPort { + pub fn new(xhci: &Weak>, i: u8) -> Self { + Self { + xhci: xhci.clone(), + portsc: 0, + port_id: i, + speed_mask: 0, + dev: None, + used: false, + } + } + + /// Get port link state from port status and control register. + pub fn get_port_link_state(&self) -> u32 { + self.portsc >> PORTSC_PLS_SHIFT & PORTSC_PLS_MASK + } + + /// Set port link state in port status and control register. + pub fn set_port_link_state(&mut self, pls: u32) { + self.portsc &= !(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); + self.portsc |= (pls & PORTSC_PLS_MASK) << PORTSC_PLS_SHIFT; + } +} + /// Event usually send to drivers. #[derive(Debug)] pub struct XhciEvent { @@ -409,7 +443,6 @@ pub struct XhciDevice { pub numports_3: u8, pub oper: XchiOperReg, pub usb_ports: Vec>>, - pub ports: Vec>>, pub slots: Vec, pub intrs: Vec, pub cmd_ring: XhciRing, @@ -439,7 +472,6 @@ impl XhciDevice { usb_ports: Vec::new(), numports_3: p3, numports_2: p2, - ports: Vec::new(), slots: vec![XhciSlot::new(mem_space); MAX_SLOTS as usize], intrs: vec![XhciInterrupter::new(mem_space); MAX_INTRS as usize], cmd_ring: XhciRing::new(mem_space), @@ -449,29 +481,21 @@ impl XhciDevice { let clone_xhci = xhci.clone(); let mut locked_xhci = clone_xhci.lock().unwrap(); locked_xhci.oper.usb_status = USB_STS_HCH; - for i in 0..(p2 + p3) { - locked_xhci.ports.push(Arc::new(Mutex::new(XhciPort::new( + for i in 0..locked_xhci.numports_2 { + let usb_port = Arc::new(Mutex::new(UsbPort::new( &Arc::downgrade(&clone_xhci), - format!("xhci-port-{}", i), i + 1, - )))); - } - for i in 0..locked_xhci.numports_2 { - let usb_port = Arc::new(Mutex::new(UsbPort::new(i))); + ))); locked_xhci.usb_ports.push(usb_port.clone()); - let mut locked_port = locked_xhci.ports[i as usize].lock().unwrap(); - locked_port.name = format!("{}-usb2-{}", locked_port.name, i); + let mut locked_port = usb_port.lock().unwrap(); locked_port.speed_mask = USB_SPEED_LOW | USB_SPEED_HIGH | USB_SPEED_FULL; - locked_port.usb_port = Some(usb_port); } for i in 0..locked_xhci.numports_3 { - let idx = i + locked_xhci.numports_2; - let usb_port = Arc::new(Mutex::new(UsbPort::new(idx))); + let idx = i + locked_xhci.numports_2 + 1; + let usb_port = Arc::new(Mutex::new(UsbPort::new(&Arc::downgrade(&clone_xhci), idx))); locked_xhci.usb_ports.push(usb_port.clone()); - let mut locked_port = locked_xhci.ports[idx as usize].lock().unwrap(); - locked_port.name = format!("{}-usb3-{}", locked_port.name, idx); + let mut locked_port = usb_port.lock().unwrap(); locked_port.speed_mask = USB_SPEED_SUPER; - locked_port.usb_port = Some(usb_port); } xhci } @@ -502,8 +526,8 @@ impl XhciDevice { error!("Failed to disable slot {:?}", e); } } - for i in 0..self.ports.len() { - let port = self.ports[i].clone(); + for i in 0..self.usb_ports.len() { + let port = self.usb_ports[i].clone(); if let Err(e) = self.port_update(&port) { error!("Failed to update port: {:?}", e); } @@ -513,47 +537,37 @@ impl XhciDevice { } } - /// Find xhci port by usb port. - pub fn lookup_xhci_port(&self, dev: &Arc>) -> Option>> { - let index = dev.lock().unwrap().index; - Some(self.ports[index as usize].clone()) - } - /// Reset xhci port. - pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { + pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { let mut locked_port = xhci_port.lock().unwrap(); - if let Some(usb_port) = locked_port.usb_port.as_ref() { - let clone_usb_port = usb_port.clone(); - let locked_usb_port = clone_usb_port.lock().unwrap(); - let usb_dev = if let Some(dev) = locked_usb_port.dev.as_ref() { - dev - } else { - // No device, no need to reset. - return Ok(()); - }; - usb_dev.lock().unwrap().reset(); - let speed = usb_dev.lock().unwrap().speed(); - if speed == USB_SPEED_SUPER && warm_reset { - locked_port.portsc |= PORTSC_WRC; + let usb_dev = if let Some(dev) = locked_port.dev.as_ref() { + dev + } else { + // No device, no need to reset. + return Ok(()); + }; + usb_dev.lock().unwrap().reset(); + let speed = usb_dev.lock().unwrap().speed(); + if speed == USB_SPEED_SUPER && warm_reset { + locked_port.portsc |= PORTSC_WRC; + } + match speed { + USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH | USB_SPEED_SUPER => { + locked_port.set_port_link_state(PLS_U0); + locked_port.portsc |= PORTSC_PED; } - match speed { - USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH | USB_SPEED_SUPER => { - locked_port.set_port_link_state(PLS_U0); - locked_port.portsc |= PORTSC_PED; - } - _ => { - error!("Invalid speed {}", speed); - } + _ => { + error!("Invalid speed {}", speed); } - locked_port.portsc &= !PORTSC_PR; - drop(locked_port); - self.port_notify(xhci_port, PORTSC_PRC)?; } + locked_port.portsc &= !PORTSC_PR; + drop(locked_port); + self.port_notify(xhci_port, PORTSC_PRC)?; Ok(()) } /// Send PortStatusChange event to notify drivers. - pub fn port_notify(&mut self, port: &Arc>, flag: u32) -> Result<()> { + pub fn port_notify(&mut self, port: &Arc>, flag: u32) -> Result<()> { let mut locked_port = port.lock().unwrap(); if locked_port.portsc & flag == flag { return Ok(()); @@ -563,36 +577,32 @@ impl XhciDevice { return Ok(()); } let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); - evt.ptr = ((locked_port.port_idx as u32) << PORT_EVENT_ID_SHIFT) as u64; + evt.ptr = ((locked_port.port_id as u32) << PORT_EVENT_ID_SHIFT) as u64; self.send_event(&evt, 0)?; Ok(()) } /// Update the xhci port status and then notify the driver. - pub fn port_update(&mut self, port: &Arc>) -> Result<()> { + pub fn port_update(&mut self, port: &Arc>) -> Result<()> { let mut locked_port = port.lock().unwrap(); locked_port.portsc = PORTSC_PP; let mut pls = PLS_RX_DETECT; - if let Some(usb_port) = &locked_port.usb_port { - let clone_usb_port = usb_port.clone(); - let locked_usb_port = clone_usb_port.lock().unwrap(); - if let Some(dev) = &locked_usb_port.dev { - let speed = dev.lock().unwrap().speed(); - locked_port.portsc |= PORTSC_CCS; - if speed == USB_SPEED_SUPER { - locked_port.portsc |= PORTSC_SPEED_SUPER; - locked_port.portsc |= PORTSC_PED; - pls = PLS_U0; - } else if speed == USB_SPEED_FULL { - locked_port.portsc |= PORTSC_SPEED_FULL; - pls = PLS_POLLING; - } else if speed == USB_SPEED_HIGH { - locked_port.portsc |= PORTSC_SPEED_HIGH; - pls = PLS_POLLING; - } else if speed == USB_SPEED_LOW { - locked_port.portsc |= PORTSC_SPEED_LOW; - pls = PLS_POLLING; - } + if let Some(dev) = &locked_port.dev { + let speed = dev.lock().unwrap().speed(); + locked_port.portsc |= PORTSC_CCS; + if speed == USB_SPEED_SUPER { + locked_port.portsc |= PORTSC_SPEED_SUPER; + locked_port.portsc |= PORTSC_PED; + pls = PLS_U0; + } else if speed == USB_SPEED_FULL { + locked_port.portsc |= PORTSC_SPEED_FULL; + pls = PLS_POLLING; + } else if speed == USB_SPEED_HIGH { + locked_port.portsc |= PORTSC_SPEED_HIGH; + pls = PLS_POLLING; + } else if speed == USB_SPEED_LOW { + locked_port.portsc |= PORTSC_SPEED_LOW; + pls = PLS_POLLING; } } locked_port.set_port_link_state(pls); @@ -622,7 +632,7 @@ impl XhciDevice { fn lookup_usb_port(&mut self, slot_ctx: &XhciSlotCtx) -> Option>> { let port = (slot_ctx.dev_info2 >> SLOT_CTX_PORT_NUMBER_SHIFT & 0xff) as u8; - if port < 1 || port > self.ports.len() as u8 { + if port < 1 || port > self.usb_ports.len() as u8 { error!("Invalid port: {}", port); return None; } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 621ec7bcb..7078a6939 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -111,9 +111,9 @@ impl XhciPciDevice { || "Failed to register oper region.", )?; - let port_num = self.xhci.lock().unwrap().ports.len(); + let port_num = self.xhci.lock().unwrap().usb_ports.len(); for i in 0..port_num { - let port = &self.xhci.lock().unwrap().ports[i]; + let port = &self.xhci.lock().unwrap().usb_ports[i]; let port_region = Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i as u32) as u64; @@ -294,17 +294,11 @@ impl BusDeviceOps for XhciPciDevice { } else { bail!("No available USB port."); }; - let xhci_port = if let Some(xhci_port) = locked_xhci.lookup_xhci_port(&usb_port) { - xhci_port - } else { - bail!("No xhci port found"); - }; - - locked_xhci.port_update(&xhci_port)?; + locked_xhci.port_update(&usb_port)?; let mut locked_dev = dev.lock().unwrap(); debug!( - "Attach usb device: xhci port name {} device id {}", - xhci_port.lock().unwrap().name, + "Attach usb device: xhci port id {} device id {}", + usb_port.lock().unwrap().port_id, locked_dev.device_id() ); locked_dev.handle_attach()?; diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 2b443f9e5..3912826d9 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -12,7 +12,7 @@ use crate::xhci::xhci_controller::dma_write_bytes; use crate::xhci::xhci_ring::XhciTRB; -use std::sync::{Arc, Mutex, Weak}; +use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; @@ -20,8 +20,7 @@ use log::{debug, error}; use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; use crate::config::*; -use crate::usb::UsbPort; -use crate::xhci::xhci_controller::{XhciDevice, XhciEvent}; +use crate::xhci::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; use anyhow::Result; @@ -229,42 +228,6 @@ impl XhciInterrupter { } } -/// XHCI port used to notify device. -pub struct XhciPort { - xhci: Weak>, - /// Port Status and Control - pub portsc: u32, - /// Port ID - pub port_idx: u8, - pub usb_port: Option>>, - pub speed_mask: u32, - pub name: String, -} - -impl XhciPort { - pub fn new(xhci: &Weak>, name: String, i: u8) -> Self { - Self { - xhci: xhci.clone(), - portsc: 0, - port_idx: i, - speed_mask: 0, - usb_port: None, - name, - } - } - - /// Get port link state from port status and control register. - pub fn get_port_link_state(&self) -> u32 { - self.portsc >> PORTSC_PLS_SHIFT & PORTSC_PLS_MASK - } - - /// Set port link state in port status and control register. - pub fn set_port_link_state(&mut self, pls: u32) { - self.portsc &= !(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); - self.portsc |= (pls & PORTSC_PLS_MASK) << PORTSC_PLS_SHIFT; - } -} - /// Build capability region ops. pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { let xhci_dev = xhci_dev.clone(); @@ -621,7 +584,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { } /// Build port region ops. -pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { +pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { let port = xhci_port.clone(); let port_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { debug!("port read {:x} {:x}", addr.0, offset); @@ -670,7 +633,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { } } -fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { +fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { let locked_port = port.lock().unwrap(); let xhci = locked_port.xhci.upgrade().unwrap(); drop(locked_port); @@ -709,7 +672,7 @@ fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { Ok(()) } -fn xhci_portsc_ls_write(port: &mut XhciPort, old_pls: u32, new_pls: u32) -> u32 { +fn xhci_portsc_ls_write(port: &mut UsbPort, old_pls: u32, new_pls: u32) -> u32 { match new_pls { PLS_U0 => { if old_pls != PLS_U0 { -- Gitee From c7aae34ffbaf7a43b9442b9b2127d51622ebc483 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 26 Dec 2022 21:12:25 +0800 Subject: [PATCH 0636/2187] loop_context: Use separate field for pre-polling handler Use handlers[1] to represent pre-polling handler is not suitable, as the epoll_wait_manager() will execute all handlers. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 30 +++++++++++++----------------- virtio/src/block.rs | 18 +++++++++--------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 5aba975ec..c7b8f6acd 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -46,7 +46,7 @@ pub enum NotifierOperation { Resume = 32, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum EventStatus { /// Event is currently monitored in epoll. Alive = 0, @@ -70,10 +70,10 @@ pub struct EventNotifier { pub event: EventSet, /// Event Handler List, one fd event may have many handlers pub handlers: Vec>>>, + /// Pre-polling handler + pub handler_poll: Option>, /// Event status status: EventStatus, - /// The flag representing whether pre polling is required - pub io_poll: bool, } impl fmt::Debug for EventNotifier { @@ -84,7 +84,7 @@ impl fmt::Debug for EventNotifier { .field("parked_fd", &self.parked_fd) .field("event", &self.event) .field("status", &self.status) - .field("io_poll", &self.io_poll) + .field("io_poll", &self.handler_poll.is_some()) .finish() } } @@ -104,8 +104,8 @@ impl EventNotifier { parked_fd, event, handlers, + handler_poll: None, status: EventStatus::Alive, - io_poll: false, } } } @@ -386,21 +386,17 @@ impl EventLoopContext { return Ok(false); } } - let timeout = self.timers_min_timeout(); + let timeout = self.timers_min_timeout(); if timeout == -1 { for _i in 0..AIO_PRFETCH_CYCLE_TIME { - for (_fd, notifer) in self.events.read().unwrap().iter() { - if notifer.io_poll { - if let EventStatus::Alive = notifer.status { - let handle = notifer.handlers[1].lock().unwrap(); - match handle(self.ready_events[1].event_set(), notifer.raw_fd) { - None => {} - Some(_) => { - break; - } - } - } + for notifer in self.events.read().unwrap().values() { + if notifer.status != EventStatus::Alive || notifer.handler_poll.is_none() { + continue; + } + let handler_poll = notifer.handler_poll.as_ref().unwrap(); + if handler_poll(EventSet::empty(), notifer.raw_fd).is_some() { + break; } } } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 08d68baf8..bb2a661e9 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -726,6 +726,7 @@ impl BlockIoHandler { fn build_event_notifier( fd: RawFd, handlers: Vec>>>, + handler_poll: Option>, ) -> EventNotifier { let mut notifier = EventNotifier::new( NotifierOperation::AddShared, @@ -734,7 +735,7 @@ fn build_event_notifier( EventSet::IN, handlers, ); - notifier.io_poll = notifier.handlers.len() == 2; + notifier.handler_poll = handler_poll; notifier } @@ -753,6 +754,7 @@ impl EventNotifierHelper for BlockIoHandler { notifiers.push(build_event_notifier( handler_raw.update_evt.as_raw_fd(), vec![Arc::new(Mutex::new(h))], + None, )); // Register event notifier for deactivate_evt. @@ -764,6 +766,7 @@ impl EventNotifierHelper for BlockIoHandler { notifiers.push(build_event_notifier( handler_raw.deactivate_evt.as_raw_fd(), vec![Arc::new(Mutex::new(h))], + None, )); // Register event notifier for queue_evt. @@ -791,10 +794,8 @@ impl EventNotifierHelper for BlockIoHandler { }); notifiers.push(build_event_notifier( handler_raw.queue_evt.as_raw_fd(), - vec![ - Arc::new(Mutex::new(h)), - Arc::new(Mutex::new(handler_iopoll)), - ], + vec![Arc::new(Mutex::new(h))], + Some(handler_iopoll), )); // Register timer event notifier for IO limits @@ -813,6 +814,7 @@ impl EventNotifierHelper for BlockIoHandler { notifiers.push(build_event_notifier( lb.as_raw_fd(), vec![Arc::new(Mutex::new(h))], + None, )); } @@ -841,10 +843,8 @@ impl EventNotifierHelper for BlockIoHandler { }); notifiers.push(build_event_notifier( handler_raw.aio.fd.as_raw_fd(), - vec![ - Arc::new(Mutex::new(h)), - Arc::new(Mutex::new(handler_iopoll)), - ], + vec![Arc::new(Mutex::new(h))], + Some(handler_iopoll), )); notifiers -- Gitee From 68e5f47fe8e394b311974a06d82487d72246ca02 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 26 Dec 2022 20:43:55 +0800 Subject: [PATCH 0637/2187] loop_context: Fixes for event updater add_event: If there is already one same event monitored, we should also update the eventset. rm_event: Change the comment of rm_event according to code logic. And delete "Removed" status of notifier, as is not used at all. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 59 ++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index c7b8f6acd..235bcd896 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -52,8 +52,6 @@ enum EventStatus { Alive = 0, /// Event is parked, temporarily not monitored. Parked = 1, - /// Event is removed. - Removed = 2, } pub type NotifierCallback = dyn Fn(EventSet, RawFd) -> Option>; @@ -191,9 +189,9 @@ impl EventLoopContext { gc.clear(); } - fn add_event(&mut self, event: EventNotifier) -> Result<()> { - // If there is one same alive event monitored, update the handlers. - // If there is one same parked event, update the handlers but warn. + fn add_event(&mut self, mut event: EventNotifier) -> Result<()> { + // If there is one same alive event monitored, update the handlers and eventset. + // If there is one same parked event, update the handlers and eventset but warn. // If there is no event in the map, insert the event and park the related. let mut events_map = self.events.write().unwrap(); if let Some(notifier) = events_map.get_mut(&event.raw_fd) { @@ -201,24 +199,31 @@ impl EventLoopContext { return Err(anyhow!(UtilError::BadNotifierOperation)); } - let mut event = event; + if notifier.event != event.event { + self.epoll.ctl( + ControlOperation::Modify, + notifier.raw_fd, + EpollEvent::new(notifier.event | event.event, &**notifier as *const _ as u64), + )?; + notifier.event |= event.event; + } notifier.handlers.append(&mut event.handlers); - if let EventStatus::Parked = notifier.status { + if notifier.status == EventStatus::Parked { warn!("Parked event updated!"); } return Ok(()); } - let raw_fd = event.raw_fd; - events_map.insert(raw_fd, Box::new(event)); - let event = events_map.get(&raw_fd).unwrap(); + let event = Box::new(event); self.epoll.ctl( ControlOperation::Add, event.raw_fd, - EpollEvent::new(event.event, &**event as *const _ as u64), + EpollEvent::new(event.event, &*event as *const _ as u64), )?; + let parked_fd = event.parked_fd; + events_map.insert(event.raw_fd, event); - if let Some(parked_fd) = event.parked_fd { + if let Some(parked_fd) = parked_fd { if let Some(parked) = events_map.get_mut(&parked_fd) { self.epoll .ctl(ControlOperation::Delete, parked_fd, EpollEvent::default())?; @@ -232,14 +237,13 @@ impl EventLoopContext { } fn rm_event(&mut self, event: &EventNotifier) -> Result<()> { - // If there is one same parked event, return Ok. // If there is no event in the map, return Error. - // If there is one same alive event monitored, put the event in gc and reactivate the parked event. + // Else put the event in gc and reactivate the parked event. let mut events_map = self.events.write().unwrap(); - match events_map.get_mut(&event.raw_fd) { + match events_map.get(&event.raw_fd) { Some(notifier) => { - if let EventStatus::Alive = notifier.status { - // No need to delete fd if status is Parked, it's done in park_event. + // No need to delete fd if status is Parked, it's done in park_event. + if notifier.status == EventStatus::Alive { if let Err(error) = self.epoll.ctl( ControlOperation::Delete, notifier.raw_fd, @@ -251,10 +255,11 @@ impl EventLoopContext { } } } + let parked_fd = notifier.parked_fd; + let event = events_map.remove(&event.raw_fd).unwrap(); + self.gc.write().unwrap().push(event); - notifier.status = EventStatus::Removed; - - if let Some(parked_fd) = notifier.parked_fd { + if let Some(parked_fd) = parked_fd { if let Some(parked) = events_map.get_mut(&parked_fd) { self.epoll.ctl( ControlOperation::Add, @@ -266,9 +271,6 @@ impl EventLoopContext { return Err(anyhow!(UtilError::NoParkedFd(parked_fd))); } } - - let event = events_map.remove(&event.raw_fd).unwrap(); - self.gc.write().unwrap().push(event); } _ => { return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); @@ -279,12 +281,11 @@ impl EventLoopContext { } /// change the callback for event - fn modify_event(&mut self, event: EventNotifier) -> Result<()> { + fn modify_event(&mut self, mut event: EventNotifier) -> Result<()> { let mut events_map = self.events.write().unwrap(); match events_map.get_mut(&event.raw_fd) { Some(notifier) => { notifier.handlers.clear(); - let mut event = event; notifier.handlers.append(&mut event.handlers); } _ => { @@ -535,13 +536,7 @@ mod test { None => { return None; } - Some(notifier) => { - if let EventStatus::Alive = notifier.status { - Some(true) - } else { - Some(false) - } - } + Some(notifier) => Some(EventStatus::Alive == notifier.status), } } -- Gitee From e90228dc3e50cdf85eda1925e07469eccce7ee9e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 26 Dec 2022 22:13:54 +0800 Subject: [PATCH 0638/2187] loop_context: Protect timers with mutex lock Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 235bcd896..03d5c66a6 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -161,7 +161,7 @@ pub struct EventLoopContext { /// Temp events vector, store wait returned events. ready_events: Vec, /// Timer list - timers: Vec, + timers: Arc>>, } unsafe impl Sync for EventLoopContext {} @@ -176,7 +176,7 @@ impl EventLoopContext { events: Arc::new(RwLock::new(BTreeMap::new())), gc: Arc::new(RwLock::new(Vec::new())), ready_events: vec![EpollEvent::default(); READY_EVENT_MAX], - timers: Vec::new(), + timers: Arc::new(Mutex::new(Vec::new())), } } @@ -416,28 +416,30 @@ impl EventLoopContext { let timer = Timer::new(func, nsec); // insert in order of expire_time - let mut index = self.timers.len(); - for (i, t) in self.timers.iter().enumerate() { + let mut timers = self.timers.lock().unwrap(); + let mut index = timers.len(); + for (i, t) in timers.iter().enumerate() { if timer.expire_time < t.expire_time { index = i; break; } } - self.timers.insert(index, timer); + timers.insert(index, timer); } /// Get the expire_time of the soonest Timer, and then translate it to timeout. fn timers_min_timeout(&self) -> i32 { - if self.timers.is_empty() { + let timers = self.timers.lock().unwrap(); + if timers.is_empty() { return -1; } let now = Instant::now(); - if self.timers[0].expire_time <= now { + if timers[0].expire_time <= now { return 0; } - let timeout = (self.timers[0].expire_time - now).as_millis(); + let timeout = (timers[0].expire_time - now).as_millis(); if timeout >= i32::MAX as u128 { i32::MAX - 1 } else { @@ -450,7 +452,8 @@ impl EventLoopContext { let now = Instant::now(); let mut expired_nr = 0; - for timer in &self.timers { + let mut timers = self.timers.lock().unwrap(); + for timer in timers.iter() { if timer.expire_time > now { break; } @@ -458,7 +461,8 @@ impl EventLoopContext { expired_nr += 1; } - let expired_timers: Vec = self.timers.drain(0..expired_nr).collect(); + let expired_timers: Vec = timers.drain(0..expired_nr).collect(); + drop(timers); for timer in expired_timers { (timer.func)(); } -- Gitee From 311d59a60db63137d7f33c9e487386ad36c9edf1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Dec 2022 16:16:58 +0800 Subject: [PATCH 0639/2187] virtio-devices: Set device as broken when report virtio error Invoke device deactivate is async thus not reliable. And the device should be deactivated by guest after we report virtio error. And delete the report_virtio_error code in virtio_pci: 1. It's not required in virtio spec. 2. It's in vcpu context, so can not serialize with IO handler. Signed-off-by: Keqian Zhu --- virtio/src/balloon.rs | 34 +++++++++++++++----- virtio/src/block.rs | 55 ++++++++++++++++++++++++-------- virtio/src/lib.rs | 11 +++---- virtio/src/net.rs | 39 +++++++++++++++++++--- virtio/src/scsi/controller.rs | 49 ++++++++++++++++++++-------- virtio/src/vhost/kernel/mod.rs | 5 +++ virtio/src/vhost/kernel/net.rs | 8 +++++ virtio/src/vhost/kernel/vsock.rs | 6 ++++ virtio/src/vhost/user/net.rs | 6 ++++ virtio/src/virtio_pci.rs | 28 +++------------- 10 files changed, 173 insertions(+), 68 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 947e5bb43..7947358e9 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -12,7 +12,7 @@ use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use std::{ cmp::{self, Reverse}, @@ -515,6 +515,8 @@ struct BalloonIoHandler { report_evt: Option, /// EventFd for device deactivate deactivate_evt: EventFd, + /// Device is broken or not. + device_broken: Arc, /// The interrupt call back function. interrupt_cb: Arc, /// Balloon Memory information. @@ -687,12 +689,15 @@ impl EventNotifierHelper for BalloonIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if locked_balloon_io.device_broken.load(Ordering::SeqCst) { + return None; + } if let Err(e) = locked_balloon_io.process_balloon_queue(BALLOON_INFLATE_EVENT) { error!("Failed to inflate balloon: {:?}", e); report_virtio_error( locked_balloon_io.interrupt_cb.clone(), locked_balloon_io.driver_features, - Some(&locked_balloon_io.deactivate_evt), + &locked_balloon_io.device_broken, ); }; None @@ -707,12 +712,15 @@ impl EventNotifierHelper for BalloonIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if locked_balloon_io.device_broken.load(Ordering::SeqCst) { + return None; + } if let Err(e) = locked_balloon_io.process_balloon_queue(BALLOON_DEFLATE_EVENT) { error!("Failed to deflate balloon: {:?}", e); report_virtio_error( locked_balloon_io.interrupt_cb.clone(), locked_balloon_io.driver_features, - Some(&locked_balloon_io.deactivate_evt), + &locked_balloon_io.device_broken, ); }; None @@ -728,12 +736,15 @@ impl EventNotifierHelper for BalloonIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if locked_balloon_io.device_broken.load(Ordering::SeqCst) { + return None; + } if let Err(e) = locked_balloon_io.reporting_evt_handler() { error!("Failed to report free pages: {:?}", e); report_virtio_error( locked_balloon_io.interrupt_cb.clone(), locked_balloon_io.driver_features, - Some(&locked_balloon_io.deactivate_evt), + &locked_balloon_io.device_broken, ); } None @@ -756,10 +767,11 @@ impl EventNotifierHelper for BalloonIoHandler { let cloned_balloon_io = balloon_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - cloned_balloon_io - .lock() - .unwrap() - .send_balloon_changed_event(); + let locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if locked_balloon_io.device_broken.load(Ordering::SeqCst) { + return None; + } + locked_balloon_io.send_balloon_changed_event(); None }); notifiers.push(build_event_notifier( @@ -796,6 +808,8 @@ pub struct Balloon { event_timer: Arc>, /// EventFd for device deactivate. deactivate_evt: EventFd, + /// Device is broken or not. + broken: Arc, } impl Balloon { @@ -823,6 +837,7 @@ impl Balloon { mem_space, event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), } } @@ -1056,6 +1071,7 @@ impl VirtioDevice for Balloon { report_queue, report_evt, deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), interrupt_cb, mem_info: self.mem_info.clone(), event_timer: self.event_timer.clone(), @@ -1067,6 +1083,7 @@ impl VirtioDevice for Balloon { None, ) .with_context(|| "Failed to register balloon event notifier to MainLoop")?; + self.broken.store(false, Ordering::SeqCst); Ok(()) } @@ -1341,6 +1358,7 @@ mod tests { report_queue: None, report_evt: None, deactivate_evt: event_deactivate.try_clone().unwrap(), + device_broken: bln.broken.clone(), interrupt_cb: cb.clone(), mem_info: bln.mem_info.clone(), event_timer: bln.event_timer.clone(), diff --git a/virtio/src/block.rs b/virtio/src/block.rs index bb2a661e9..54fd3c98f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -17,6 +17,7 @@ use std::io::{Seek, SeekFrom, Write}; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::time::Instant; @@ -408,6 +409,8 @@ struct BlockIoHandler { update_evt: EventFd, /// Eventfd for device deactivate. deactivate_evt: EventFd, + /// Device is broken or not. + device_broken: Arc, /// Callback to trigger an interrupt. interrupt_cb: Arc, /// thread name of io handler @@ -616,7 +619,7 @@ impl BlockIoHandler { report_virtio_error( self.interrupt_cb.clone(), self.driver_features, - Some(&self.deactivate_evt), + &self.device_broken, ); } result @@ -650,7 +653,7 @@ impl BlockIoHandler { report_virtio_error( self.interrupt_cb.clone(), self.driver_features, - Some(&self.deactivate_evt), + &self.device_broken, ); e }) @@ -748,7 +751,11 @@ impl EventNotifierHelper for BlockIoHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - h_clone.lock().unwrap().update_evt_handler(); + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock.update_evt_handler(); None }); notifiers.push(build_event_notifier( @@ -773,16 +780,22 @@ impl EventNotifierHelper for BlockIoHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(ref e) = h_clone.lock().unwrap().process_queue() { + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + if let Err(ref e) = h_lock.process_queue() { error!("Failed to handle block IO {:?}", e); } None }); let h_clone = handler.clone(); let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { - let done = h_clone - .lock() - .unwrap() + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + let done = h_lock .process_queue() .with_context(|| "Failed to handle block IO") .ok()?; @@ -803,10 +816,14 @@ impl EventNotifierHelper for BlockIoHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Some(lb) = h_clone.lock().unwrap().leak_bucket.as_mut() { + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + if let Some(lb) = h_lock.leak_bucket.as_mut() { lb.clear_timer(); } - if let Err(ref e) = h_clone.lock().unwrap().process_queue() { + if let Err(ref e) = h_lock.process_queue() { error!("Failed to handle block IO {:?}", e); } None @@ -822,16 +839,22 @@ impl EventNotifierHelper for BlockIoHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(ref e) = h_clone.lock().unwrap().aio_complete_handler() { + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + if let Err(ref e) = h_lock.aio_complete_handler() { error!("Failed to handle aio {:?}", e); } None }); let h_clone = handler.clone(); let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { - let done = h_clone - .lock() - .unwrap() + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + let done = h_lock .aio_complete_handler() .with_context(|| "Failed to handle aio") .ok()?; @@ -937,6 +960,8 @@ pub struct Block { update_evt: EventFd, /// Eventfd for device deactivate. deactivate_evts: NotifyEventFds, + /// Device is broken or not. + broken: Arc, /// Drive backend files. drive_files: Arc>>, } @@ -952,6 +977,7 @@ impl Default for Block { senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evts: NotifyEventFds::new(1), + broken: Arc::new(AtomicBool::new(false)), drive_files: Arc::new(Mutex::new(HashMap::new())), } } @@ -971,6 +997,7 @@ impl Block { senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evts: NotifyEventFds::new(0), + broken: Arc::new(AtomicBool::new(false)), drive_files, } } @@ -1133,6 +1160,7 @@ impl VirtioDevice for Block { receiver, update_evt: self.update_evt.try_clone().unwrap(), deactivate_evt: eventfd.try_clone().unwrap(), + device_broken: self.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), @@ -1146,6 +1174,7 @@ impl VirtioDevice for Block { } self.senders = Some(senders); + self.broken.store(false, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index c117970cc..9f00db59a 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -59,6 +59,7 @@ pub use virtio_pci::VirtioPciDevice; pub use virtqueue::*; use std::cmp; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -400,7 +401,7 @@ pub trait VirtioTrace { pub fn report_virtio_error( interrupt_cb: Arc, features: u64, - deactivate_evt: Option<&EventFd>, + broken: &Arc, ) { if virtio_has_feature(features, VIRTIO_F_VERSION_1) { interrupt_cb(&VirtioInterruptType::Config, None, true).unwrap_or_else(|e| { @@ -410,12 +411,8 @@ pub fn report_virtio_error( ) }); } - // The queue should not work when meeting virtio error. - // So, using deactivate evt to disable the queue. - if let Some(evt) = deactivate_evt { - evt.write(1) - .unwrap_or_else(|e| error!("Failed to deactivate event, error is {}", e)); - } + // The device should not work when meeting virtio error. + broken.store(true, Ordering::SeqCst); } /// Read iovec to buf and return the readed number of bytes. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index c0551a115..ea8ba15f1 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use std::io::{ErrorKind, Write}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; @@ -457,6 +458,8 @@ pub struct NetCtrlHandler { pub driver_features: u64, /// Deactivate event to delete net control handler. pub deactivate_evt: EventFd, + /// Device is broken or not. + pub device_broken: Arc, } #[repr(C, packed)] @@ -623,12 +626,15 @@ impl EventNotifierHelper for NetCtrlHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_net_io = cloned_net_io.lock().unwrap(); + if locked_net_io.device_broken.load(Ordering::SeqCst) { + return None; + } locked_net_io.handle_ctrl().unwrap_or_else(|e| { error!("Failed to handle ctrl queue, error is {}.", e); report_virtio_error( locked_net_io.interrupt_cb.clone(), locked_net_io.driver_features, - Some(&locked_net_io.deactivate_evt), + &locked_net_io.device_broken, ); }); None @@ -697,6 +703,7 @@ struct NetIoHandler { receiver: Receiver, update_evt: EventFd, deactivate_evt: EventFd, + device_broken: Arc, is_listening: bool, ctrl_info: Arc>, } @@ -1067,6 +1074,14 @@ impl EventNotifierHelper for NetIoHandler { let cloned_net_io = net_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); + if cloned_net_io + .lock() + .unwrap() + .device_broken + .load(Ordering::SeqCst) + { + return None; + } Some(NetIoHandler::update_evt_handler(&cloned_net_io)) }); let mut notifiers = vec![build_event_notifier( @@ -1092,8 +1107,11 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for rx. let cloned_net_io = net_io.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { - let mut locked_net_io = cloned_net_io.lock().unwrap(); read_fd(fd); + let mut locked_net_io = cloned_net_io.lock().unwrap(); + if locked_net_io.device_broken.load(Ordering::SeqCst) { + return None; + } if let Some(tap) = locked_net_io.tap.as_ref() { if !locked_net_io.is_listening { let notifier = vec![EventNotifier::new( @@ -1122,12 +1140,15 @@ impl EventNotifierHelper for NetIoHandler { let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_net_io = cloned_net_io.lock().unwrap(); + if locked_net_io.device_broken.load(Ordering::SeqCst) { + return None; + } if let Err(ref e) = locked_net_io.handle_tx() { error!("Failed to handle tx(tx event) for net, {:?}", e); report_virtio_error( locked_net_io.interrupt_cb.clone(), locked_net_io.driver_features, - Some(&locked_net_io.deactivate_evt), + &locked_net_io.device_broken, ); } None @@ -1145,12 +1166,15 @@ impl EventNotifierHelper for NetIoHandler { if let Some(tap) = locked_net_io.tap.as_ref() { let handler: Box = Box::new(move |_, _| { let mut locked_net_io = cloned_net_io.lock().unwrap(); + if locked_net_io.device_broken.load(Ordering::SeqCst) { + return None; + } if let Err(ref e) = locked_net_io.handle_rx() { error!("Failed to handle rx(tap event), {:?}", e); report_virtio_error( locked_net_io.interrupt_cb.clone(), locked_net_io.driver_features, - Some(&locked_net_io.deactivate_evt), + &locked_net_io.device_broken, ); return None; } @@ -1211,6 +1235,8 @@ pub struct Net { update_evt: EventFd, /// Eventfd for device deactivate. deactivate_evt: EventFd, + /// Device is broken or not. + broken: Arc, /// The information about control command. ctrl_info: Option>>, } @@ -1224,6 +1250,7 @@ impl Default for Net { senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, } } @@ -1238,6 +1265,7 @@ impl Net { senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, } } @@ -1588,6 +1616,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features: self.state.lock().unwrap().driver_features, deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), }; EventLoop::update_event( @@ -1627,6 +1656,7 @@ impl VirtioDevice for Net { receiver, update_evt: self.update_evt.try_clone().unwrap(), deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), }; @@ -1640,6 +1670,7 @@ impl VirtioDevice for Net { )?; } self.senders = Some(senders); + self.broken.store(false, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 405d048ce..590266cac 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -143,6 +144,8 @@ pub struct ScsiCntlr { pub bus: Option>>, /// Eventfd for Scsi Controller deactivates. deactivate_evt: EventFd, + /// Device is broken or not. + broken: Arc, } impl ScsiCntlr { @@ -152,6 +155,7 @@ impl ScsiCntlr { state: ScsiCntlrState::default(), bus: None, deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), } } } @@ -279,6 +283,7 @@ impl VirtioDevice for ScsiCntlr { mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, + device_broken: self.broken.clone(), }; EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), @@ -294,6 +299,7 @@ impl VirtioDevice for ScsiCntlr { _mem_space: mem_space.clone(), _interrupt_cb: interrupt_cb.clone(), _driver_features: self.state.driver_features, + device_broken: self.broken.clone(), }; EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))), @@ -312,6 +318,7 @@ impl VirtioDevice for ScsiCntlr { mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, + device_broken: self.broken.clone(), }; cmd_handler.aio = Some(cmd_handler.build_aio()?); @@ -324,6 +331,7 @@ impl VirtioDevice for ScsiCntlr { bail!("Scsi controller has no bus!"); } } + self.broken.store(false, Ordering::SeqCst); Ok(()) } @@ -614,6 +622,8 @@ pub struct ScsiCtrlHandler { interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, + /// Device is broken or not. + device_broken: Arc, } impl ScsiCtrlHandler { @@ -623,7 +633,7 @@ impl ScsiCtrlHandler { report_virtio_error( self.interrupt_cb.clone(), self.driver_features, - Some(&self.deactivate_evt), + &self.device_broken, ); } @@ -715,9 +725,11 @@ impl EventNotifierHelper for ScsiCtrlHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - h_clone - .lock() - .unwrap() + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock .handle_ctrl() .unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {}.", e)); None @@ -751,6 +763,8 @@ pub struct ScsiEventHandler { _interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. _driver_features: u64, + /// Device is broken or not. + device_broken: Arc, } impl EventNotifierHelper for ScsiEventHandler { @@ -759,9 +773,11 @@ impl EventNotifierHelper for ScsiEventHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - h_clone - .lock() - .unwrap() + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock .handle_event() .unwrap_or_else(|e| error!("Failed to handle event queue, err is {}", e)); None @@ -824,6 +840,8 @@ pub struct ScsiCmdHandler { driver_features: u64, /// Aio context. aio: Option>>, + /// Device is broken or not. + device_broken: Arc, } impl EventNotifierHelper for ScsiCmdHandler { @@ -832,9 +850,11 @@ impl EventNotifierHelper for ScsiCmdHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - h_clone - .lock() - .unwrap() + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock .handle_cmd() .unwrap_or_else(|e| error!("Failed to handle cmd queue, err is {}", e)); @@ -857,15 +877,18 @@ impl EventNotifierHelper for ScsiCmdHandler { let h_clone = handler.clone(); let h: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - let mut h_lock = h_clone.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + if let Some(aio) = &mut h_lock.aio { if let Err(ref e) = aio.handle() { error!("Failed to handle aio, {:?}", e); report_virtio_error( h_lock.interrupt_cb.clone(), h_lock.driver_features, - Some(&h_lock.deactivate_evt), + &h_lock.device_broken, ); } } @@ -886,7 +909,7 @@ impl ScsiCmdHandler { report_virtio_error( self.interrupt_cb.clone(), self.driver_features, - Some(&self.deactivate_evt), + &self.device_broken, ); } diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index c2cea3dd5..1cdeaa499 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -19,6 +19,7 @@ pub use vsock::{Vsock, VsockState}; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::{ @@ -458,6 +459,7 @@ pub struct VhostIoHandler { interrupt_cb: Arc, host_notifies: Vec, deactivate_evt: EventFd, + device_broken: Arc, } impl VhostIoHandler { @@ -495,6 +497,9 @@ impl EventNotifierHelper for VhostIoHandler { read_fd(fd); let locked_vhost_handler = vhost.lock().unwrap(); + if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { + return None; + } for host_notify in locked_vhost_handler.host_notifies.iter() { if let Err(e) = (locked_vhost_handler.interrupt_cb)( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index d782c4597..1e7006b9f 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -13,6 +13,7 @@ use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use crate::error::VirtioError; @@ -95,6 +96,8 @@ pub struct Net { mem_space: Arc, /// EventFd for device deactivate. deactivate_evt: EventFd, + /// Device is broken or not. + broken: Arc, } impl Net { @@ -109,6 +112,7 @@ impl Net { device_config: VirtioNetConfig::default(), mem_space: mem_space.clone(), deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), } } } @@ -264,6 +268,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), }; EventLoop::update_event( @@ -361,6 +366,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), host_notifies, deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), }; EventLoop::update_event( @@ -368,6 +374,8 @@ impl VirtioDevice for Net { None, )?; } + self.broken.store(false, Ordering::SeqCst); + Ok(()) } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 61e7cfbad..5e06cb89d 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::os::unix::io::RawFd; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -102,6 +103,8 @@ pub struct Vsock { interrupt_cb: Option>, /// EventFd for device deactivate. deactivate_evt: EventFd, + /// Device is broken or not. + broken: Arc, } impl Vsock { @@ -114,6 +117,7 @@ impl Vsock { event_queue: None, interrupt_cb: None, deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), } } @@ -315,12 +319,14 @@ impl VirtioDevice for Vsock { interrupt_cb, host_notifies, deactivate_evt: self.deactivate_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), }; EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), None, )?; + self.broken.store(false, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index dfee7ae71..4559a1459 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -13,6 +13,7 @@ use crate::virtio_has_feature; use std::cmp; use std::io::Write; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -58,6 +59,8 @@ pub struct Net { call_events: Vec, /// EventFd for deactivate control Queue. de_ctrl_evt: EventFd, + /// Device is broken or not. + broken: Arc, } impl Net { @@ -71,6 +74,7 @@ impl Net { client: None, call_events: Vec::::new(), de_ctrl_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + broken: Arc::new(AtomicBool::new(false)), } } @@ -247,6 +251,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, deactivate_evt: self.de_ctrl_evt.try_clone().unwrap(), + device_broken: self.broken.clone(), }; EventLoop::update_event( @@ -265,6 +270,7 @@ impl VirtioDevice for Net { client.set_queues(queues); client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; + self.broken.store(false, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index dc3be1efd..8dab989b4 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -40,8 +40,8 @@ use util::offset_of; use vmm_sys_util::eventfd::EventFd; use crate::{ - report_virtio_error, virtio_has_feature, NotifyEventFds, Queue, QueueConfig, VirtioDevice, - VirtioInterrupt, VirtioInterruptType, + virtio_has_feature, NotifyEventFds, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, + VirtioInterruptType, }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, @@ -855,34 +855,16 @@ impl VirtioPciDevice { return false; } - // In report_virtio_error(), it will lock cloned_pci_device.common_config. - // So, avoid deadlock by getting the returned value firstly. - let err = cloned_pci_device + if let Err(e) = cloned_pci_device .common_config .lock() .unwrap() - .write_common_config(&cloned_pci_device, offset, value); - if let Err(e) = err { + .write_common_config(&cloned_pci_device, offset, value) + { error!( "Failed to write common config of virtio-pci device, error is {:?}", e, ); - if let Some(cb) = cloned_pci_device.interrupt_cb.clone() { - if let Some(PciError::QueueEnable(_)) = e.downcast_ref::() { - let features = (cloned_pci_device - .device - .lock() - .unwrap() - .get_driver_features(1) as u64) - << 32; - report_virtio_error(cb, features, None); - if let Err(e) = cloned_pci_device.device.lock().unwrap().deactivate() { - error!("Failed to deactivate virtio device, error is {:?}", e); - } - } - } else { - error!("Failed to get interrupt callback"); - } return false; } true -- Gitee From c446262439f2c2452377b8e053906bfdffec9a4c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Dec 2022 17:16:05 +0800 Subject: [PATCH 0640/2187] virtio-blk: Do not share same kernel eventfd for update_evt The first scheduled update_handler will read fd, if we share the same kernel eventfd, the remaining handlers may not be scheduled at all. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 54fd3c98f..ba19fe2a8 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -30,7 +30,7 @@ use super::{ VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; -use crate::{NotifyEventFds, VirtioError}; +use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; @@ -957,9 +957,9 @@ pub struct Block { /// The sending half of Rust's channel to send the image file. senders: Option>>, /// Eventfd for config space update. - update_evt: EventFd, + update_evts: Vec, /// Eventfd for device deactivate. - deactivate_evts: NotifyEventFds, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, /// Drive backend files. @@ -975,8 +975,8 @@ impl Default for Block { state: BlockState::default(), interrupt_cb: None, senders: None, - update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evts: NotifyEventFds::new(1), + update_evts: Vec::new(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), drive_files: Arc::new(Mutex::new(HashMap::new())), } @@ -995,8 +995,8 @@ impl Block { state: BlockState::default(), interrupt_cb: None, senders: None, - update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evts: NotifyEventFds::new(0), + update_evts: Vec::new(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), drive_files, } @@ -1138,13 +1138,13 @@ impl VirtioDevice for Block { ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); let mut senders = Vec::new(); - self.deactivate_evts.events.clear(); for queue in queues.iter() { let (sender, receiver) = channel(); senders.push(sender); - let eventfd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let update_evt = EventFd::new(libc::EFD_NONBLOCK)?; + let deactivate_evt = EventFd::new(libc::EFD_NONBLOCK)?; let engine = self.blk_cfg.aio.as_ref(); let handler = BlockIoHandler { queue: queue.clone(), @@ -1158,19 +1158,20 @@ impl VirtioDevice for Block { aio: Box::new(Aio::new(Arc::new(BlockIoHandler::complete_func), engine)?), driver_features: self.state.driver_features, receiver, - update_evt: self.update_evt.try_clone().unwrap(), - deactivate_evt: eventfd.try_clone().unwrap(), + update_evt: update_evt.try_clone()?, + deactivate_evt: deactivate_evt.try_clone()?, device_broken: self.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), }; - self.deactivate_evts.events.push(eventfd); EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), self.blk_cfg.iothread.as_ref(), )?; + self.update_evts.push(update_evt); + self.deactivate_evts.push(deactivate_evt); } self.senders = Some(senders); @@ -1180,10 +1181,12 @@ impl VirtioDevice for Block { } fn deactivate(&mut self) -> Result<()> { - for evt in &self.deactivate_evts.events { + for evt in &self.deactivate_evts { evt.write(1) .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } + self.deactivate_evts.clear(); + self.update_evts.clear(); Ok(()) } @@ -1215,9 +1218,11 @@ impl VirtioDevice for Block { .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; } - self.update_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + for update_evt in &self.update_evts { + update_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + } } if let Some(interrupt_cb) = &self.interrupt_cb { -- Gitee From 13c79eefec3fa21569fe53e3d1df07748ddbdd57 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 22 Dec 2022 09:44:14 +0800 Subject: [PATCH 0641/2187] virtio-gpu: close console in deactivate. Close the console in the deactivate of virtio-gpu. Signed-off-by: Xiao Ye --- virtio/src/gpu.rs | 11 ++++++++--- vnc/src/console.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index a4e69d575..b32ff8783 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -45,8 +45,8 @@ use util::pixman::{ use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use vnc::console::{ - console_init, display_cursor_define, display_graphic_update, display_replace_surface, - DisplayMouse, DisplaySurface, HardWareOperations, + console_close, console_init, display_cursor_define, display_graphic_update, + display_replace_surface, DisplayMouse, DisplaySurface, HardWareOperations, }; /// Number of virtqueues. @@ -1591,7 +1591,12 @@ impl EventNotifierHelper for GpuIoHandler { let gpu_handler_clone = gpu_handler.clone(); let handler: Box = Box::new(move |_, fd: RawFd| { read_fd(fd); - Some(gpu_handler_clone.lock().unwrap().deactivate_evt_handler()) + // Deactivate console. + let mut locked_handler = gpu_handler_clone.lock().unwrap(); + for scanout in &locked_handler.scanouts { + console_close(scanout.con_id); + } + Some(locked_handler.deactivate_evt_handler()) }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, diff --git a/vnc/src/console.rs b/vnc/src/console.rs index 916ce1060..944015c4d 100644 --- a/vnc/src/console.rs +++ b/vnc/src/console.rs @@ -456,6 +456,32 @@ pub fn console_init(dev_opts: Rc) -> Option { Some(con_id) } +/// Close a console. +pub fn console_close(con_id: Option) { + if con_id.is_none() { + return; + } + + unsafe { + let len = CONSOLES.console_list.len(); + let id = con_id.unwrap(); + if id >= len { + return; + } + CONSOLES.console_list[id] = None; + if con_id != CONSOLES.activate_id { + return; + } + CONSOLES.activate_id = None; + for i in 0..len { + if CONSOLES.console_list[i].is_some() { + CONSOLES.activate_id = Some(i); + break; + } + } + } +} + /// Select the default display device. /// If con_id is none, then do nothing. pub fn console_select(con_id: Option) { -- Gitee From 052a42e1a862d45c51a6f7413c726d36ab48fe84 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 23 Dec 2022 15:34:24 +0800 Subject: [PATCH 0642/2187] docs: modify the vhost-user-blk part 1. Delete some unused command for using spdk. 2. Modify the description of chardev in vhost-user-blk-pci. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 0a3cc00d3..a5fbc861c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -313,7 +313,7 @@ StratoVirt also supports vhost-user-blk-pci to get a higher performance in stora You can use it by adding a new device, one more property is supported by vhost-user-blk-pci device than virtio-blk-pci. -* chardev: id for char device, that means you need to add a chardev first, and use its id to index char device. +* chardev: id for char device, that means you need to add a chardev first, and use its id to find the character device. ```shell # vhost user blk pci device @@ -342,18 +342,17 @@ $ make $ ./test/unit/unittest.sh # Setup spdk -$ sudo HUGEMEM=2048 ./scripts/setup.sh +$ HUGEMEM=2048 ./scripts/setup.sh # Mount huge pages, you need to add -mem-path=/dev/hugepages in stratovirt config -$ sudo mount -t hugetlbfs hugetlbfs /dev/hugepages -$ sudo sysctl vm.nr_hugepages=1024 +$ mount -t hugetlbfs hugetlbfs /dev/hugepages +# Assign the number of the hugepage +$ sysctl vm.nr_hugepages=1024 # Start vhost, alloc 1024MB memory, default socket path is /var/tmp/spdk.sock, 0x3 means we use cpu cores 0 and 1 (cpumask 0x3) -$ sudo build/bin/vhost --logflag vhost_blk -S /var/tmp -s 1024 -m 0x3 & -# Attach nvme controller, you can find you nvme id by lspci command -$ sudo ./scripts/rpc.py bdev_nvme_attach_controller -b Nvme0 -t pcie -a you-nvme-id -# Create a bdev which size is 128MB, block size is 512B -$ sudo ./scripts/rpc.py bdev_malloc_create 128 512 -b Malloc0 +$ build/bin/vhost --logflag vhost_blk -S /var/tmp -s 1024 -m 0x3 & +# Create a malloc bdev which size is 128MB, block size is 512B +$ ./scripts/rpc.py bdev_malloc_create 128 512 -b Malloc0 # Create a vhost-blk device exposing Malloc0 bdev, the I/O polling will be pinned to the CPU 0 (cpumask 0x1). -$ sudo ./scripts/rpc.py vhost_create_blk_controller --cpumask 0x1 spdk.sock Malloc0 +$ ./scripts/rpc.py vhost_create_blk_controller --cpumask 0x1 spdk.sock Malloc0 ``` A config template to start stratovirt with vhost-user-blk-pci as below: -- Gitee From 5477479978422e4f3ab7b8f2fee62c70b580bc69 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 22 Dec 2022 21:41:09 +0800 Subject: [PATCH 0643/2187] XHCI: The logic for attaching USB devices is optimized. Find the parent device from the pci_bus, and then attach the child device through the parent device. Signed-off-by: Mingwang Li --- machine/src/lib.rs | 108 ++++++++++++++----------- machine/src/standard_vm/aarch64/mod.rs | 8 -- machine/src/standard_vm/x86_64/mod.rs | 8 -- machine_manager/src/config/mod.rs | 14 +--- pci/src/lib.rs | 3 +- usb/src/bus.rs | 27 ------- usb/src/lib.rs | 1 - usb/src/xhci/xhci_pci.rs | 55 +++++-------- util/src/lib.rs | 13 ++- 9 files changed, 95 insertions(+), 142 deletions(-) delete mode 100644 usb/src/bus.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index bdbc04536..f07b6f855 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -68,8 +68,8 @@ use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; use usb::{ - bus::BusDeviceMap, keyboard::UsbKeyboard, tablet::UsbTablet, usb::UsbDeviceOps, - xhci::xhci_pci::XhciPciDevice, INPUT, + keyboard::UsbKeyboard, tablet::UsbTablet, usb::UsbDeviceOps, xhci::xhci_pci::XhciPciDevice, + INPUT, }; use util::{ arg_parser, @@ -306,12 +306,6 @@ pub trait MachineOps { /// Tcp, Unix, File and Unknown. fn get_migrate_info(&self) -> Incoming; - /// Get the bus device map. The map stores the mapping between bus name and bus device. - /// The bus device is the device which can attach other devices. - fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { - None - } - /// Get the Scsi Controller list. The map stores the mapping between scsi bus name and scsi controller. fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { None @@ -1072,19 +1066,7 @@ pub trait MachineOps { let device_cfg = parse_xhci(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let bus_device = if let Some(bus_device) = self.get_bus_device() { - bus_device.clone() - } else { - bail!("No bus device found"); - }; - - let pcidev = XhciPciDevice::new( - &device_cfg, - devfn, - parent_bus, - self.get_sys_mem(), - bus_device, - ); + let pcidev = XhciPciDevice::new(&device_cfg, devfn, parent_bus, self.get_sys_mem()); pcidev .realize() @@ -1092,30 +1074,58 @@ pub trait MachineOps { Ok(()) } + /// Get the corresponding device from the PCI bus based on the device name. + /// + /// # Arguments + /// + /// * `vm_config` - VM configuration. + /// * `name` - Device Name. + fn get_pci_dev_by_name( + &mut self, + vm_config: &mut VmConfig, + name: &str, + ) -> Option>> { + for dev in &vm_config.devices { + if dev.0.as_str() == name { + let cfg_args = dev.1.as_str(); + let bdf = get_pci_bdf(cfg_args).ok()?; + let devfn = (bdf.addr.0 << 3) + bdf.addr.1; + let pci_host = self.get_pci_host().ok()?; + let root_bus = pci_host.lock().unwrap().root_bus.clone(); + if let Some(pci_bus) = PciBus::find_bus_by_name(&root_bus, &bdf.bus) { + return pci_bus.lock().unwrap().get_device(0, devfn); + } else { + return None; + } + } + } + None + } + /// Add usb keyboard. /// /// # Arguments /// /// * `cfg_args` - Keyboard Configuration. - fn add_usb_keyboard(&mut self, cfg_args: &str) -> Result<()> { + fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_keyboard(cfg_args)?; let keyboard = UsbKeyboard::new(device_cfg.id); let kbd = keyboard .realize() .with_context(|| "Failed to realize usb keyboard device")?; - if let Some(bus_device) = self.get_bus_device() { - let locked_dev = bus_device.lock().unwrap(); - if let Some(ctrl) = locked_dev.get("usb.0") { - let mut locked_ctrl = ctrl.lock().unwrap(); - locked_ctrl - .attach_device(&(kbd.clone() as Arc>)) - .with_context(|| "Failed to attach keyboard device")?; - } else { - bail!("No usb controller found"); - } - } else { - bail!("No bus device found"); + let parent_dev_op = self.get_pci_dev_by_name(vm_config, "nec-usb-xhci"); + if parent_dev_op.is_none() { + bail!("Can not find parent device from pci bus"); + } + let parent_dev = parent_dev_op.unwrap(); + let locked_parent_dev = parent_dev.lock().unwrap(); + let xhci_pci = locked_parent_dev.as_any().downcast_ref::(); + if xhci_pci.is_none() { + bail!("PciDevOps can not downcast to XhciPciDevice"); } + xhci_pci + .unwrap() + .attach_device(&(kbd.clone() as Arc>))?; let mut locked_input = INPUT.lock().unwrap(); locked_input.keyboard = Some(kbd); Ok(()) @@ -1126,25 +1136,25 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - Tablet Configuration. - fn add_usb_tablet(&mut self, cfg_args: &str) -> Result<()> { + fn add_usb_tablet(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_tablet(cfg_args)?; let tablet = UsbTablet::new(device_cfg.id); let tbt = tablet .realize() .with_context(|| "Failed to realize usb tablet device")?; - if let Some(bus_device) = self.get_bus_device() { - let locked_dev = bus_device.lock().unwrap(); - if let Some(ctrl) = locked_dev.get("usb.0") { - let mut locked_ctrl = ctrl.lock().unwrap(); - locked_ctrl - .attach_device(&(tbt.clone() as Arc>)) - .with_context(|| "Failed to attach tablet device")?; - } else { - bail!("No usb controller found"); - } - } else { - bail!("No bus device list found"); + let parent_dev_op = self.get_pci_dev_by_name(vm_config, "nec-usb-xhci"); + if parent_dev_op.is_none() { + bail!("Can not find parent device from pci bus"); } + let parent_dev = parent_dev_op.unwrap(); + let locked_parent_dev = parent_dev.lock().unwrap(); + let xhci_pci = locked_parent_dev.as_any().downcast_ref::(); + if xhci_pci.is_none() { + bail!("PciDevOps can not downcast to XhciPciDevice"); + } + xhci_pci + .unwrap() + .attach_device(&(tbt.clone() as Arc>))?; let mut locked_input = INPUT.lock().unwrap(); locked_input.tablet = Some(tbt); Ok(()) @@ -1232,10 +1242,10 @@ pub trait MachineOps { self.add_usb_xhci(cfg_args)?; } "usb-kbd" => { - self.add_usb_keyboard(cfg_args)?; + self.add_usb_keyboard(vm_config, cfg_args)?; } "usb-tablet" => { - self.add_usb_tablet(cfg_args)?; + self.add_usb_tablet(vm_config, cfg_args)?; } #[cfg(not(target_env = "musl"))] "virtio-gpu-pci" => { diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 9a588181d..14933f44a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -60,7 +60,6 @@ use pci::{PciDevOps, PciHost}; use pci_host_root::PciHostRoot; use sysbus::{SysBus, SysBusDevType, SysRes, IRQ_BASE, IRQ_MAX}; use syscall::syscall_whitelist; -use usb::bus::BusDeviceMap; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -143,8 +142,6 @@ pub struct StdMachine { boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, - /// Bus device used to attach other devices. Only USB controller used now. - bus_device: BusDeviceMap, /// Scsi Controller List. scsi_cntlr_list: ScsiCntlrMap, /// Drive backend files. @@ -199,7 +196,6 @@ impl StdMachine { numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, - bus_device: Arc::new(Mutex::new(HashMap::new())), scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) @@ -650,10 +646,6 @@ impl MachineOps for StdMachine { Some(self.boot_order_list.clone()) } - fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { - Some(&self.bus_device) - } - fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { Some(&self.scsi_cntlr_list) } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 300af29cb..baa305154 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -52,7 +52,6 @@ use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; use sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; use syscall::syscall_whitelist; -use usb::bus::BusDeviceMap; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, }; @@ -123,8 +122,6 @@ pub struct StdMachine { boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, - /// Bus device used to attach other devices. Only USB controller used now. - bus_device: BusDeviceMap, /// Scsi Controller List. scsi_cntlr_list: ScsiCntlrMap, /// Drive backend files. @@ -179,7 +176,6 @@ impl StdMachine { numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, - bus_device: Arc::new(Mutex::new(HashMap::new())), scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) @@ -595,10 +591,6 @@ impl MachineOps for StdMachine { Some(self.boot_order_list.clone()) } - fn get_bus_device(&mut self) -> Option<&BusDeviceMap> { - Some(&self.bus_device) - } - fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { Some(&self.scsi_cntlr_list) } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index c150fd363..caf27add0 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -29,6 +29,7 @@ pub use sasl_auth::*; pub use scsi::*; pub use tls_creds::*; pub use usb::*; +use util::AsAny; pub use vfio::*; pub use vnc::*; @@ -55,8 +56,8 @@ mod vfio; pub mod vnc; use std::collections::HashMap; +use std::fs::File; use std::str::FromStr; -use std::{any::Any, fs::File}; use serde::{Deserialize, Serialize}; @@ -332,17 +333,6 @@ impl device_tree::CompileFDT for VmConfig { } } -/// This trait is to cast trait object to struct. -pub trait AsAny { - fn as_any(&self) -> &dyn Any; -} - -impl AsAny for T { - fn as_any(&self) -> &dyn Any { - self - } -} - /// This trait is to check the legality of Config structure. pub trait ConfigCheck: AsAny + Send + Sync + std::fmt::Debug { /// To check the legality of Config structure. diff --git a/pci/src/lib.rs b/pci/src/lib.rs index d27a15ca2..9b636df49 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -24,6 +24,7 @@ pub use bus::PciBus; pub use host::PciHost; pub use msix::init_msix; pub use root_port::RootPort; +use util::AsAny; use std::{ mem::size_of, @@ -118,7 +119,7 @@ pub fn pci_ext_cap_next(header: u32) -> usize { ((header >> 20) & 0xffc) as usize } -pub trait PciDevOps: Send { +pub trait PciDevOps: Send + AsAny { /// Init writable bit mask. fn init_write_mask(&mut self) -> Result<()>; diff --git a/usb/src/bus.rs b/usb/src/bus.rs deleted file mode 100644 index 7fbc2075a..000000000 --- a/usb/src/bus.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; - -use crate::usb::UsbDeviceOps; -use anyhow::Result; - -/// The key is bus name, the value is the device which can attach other devices. -pub type BusDeviceMap = Arc>>>>; - -/// Bus device ops for USB controller to handle USB device attach/detach. -pub trait BusDeviceOps: Send + Sync { - fn attach_device(&mut self, dev: &Arc>) -> Result<()>; - - fn detach_device(&mut self, dev: &Arc>) -> Result<()>; -} diff --git a/usb/src/lib.rs b/usb/src/lib.rs index c3b25759e..0234d8b10 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -14,7 +14,6 @@ pub mod error; pub use anyhow::Result; pub use error::UsbError; -pub mod bus; pub mod config; mod descriptor; pub mod hid; diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 7078a6939..d1ec2f7e1 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -24,7 +24,6 @@ use pci::config::{ use pci::msix::update_dev_id; use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; -use crate::bus::{BusDeviceMap, BusDeviceOps}; use crate::usb::UsbDeviceOps; use crate::xhci::xhci_controller::{XhciDevice, XhciOps, MAX_INTRS, MAX_SLOTS}; use crate::xhci::xhci_regs::{ @@ -70,7 +69,6 @@ pub struct XhciPciDevice { name: String, parent_bus: Weak>, mem_region: Region, - bus_device: BusDeviceMap, } impl XhciPciDevice { @@ -79,7 +77,6 @@ impl XhciPciDevice { devfn: u8, parent_bus: Weak>, mem_space: &Arc, - bus_device: BusDeviceMap, ) -> Self { Self { pci_config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), @@ -89,7 +86,6 @@ impl XhciPciDevice { name: config.id.to_string(), parent_bus, mem_region: Region::init_container_region(XHCI_PCI_CONFIG_LENGTH as u64), - bus_device, } } @@ -144,6 +140,25 @@ impl XhciPciDevice { )?; Ok(()) } + + pub fn attach_device(&self, dev: &Arc>) -> Result<()> { + let mut locked_xhci = self.xhci.lock().unwrap(); + let usb_port = if let Some(usb_port) = locked_xhci.assign_usb_port(dev) { + usb_port + } else { + bail!("No available USB port."); + }; + locked_xhci.port_update(&usb_port)?; + let mut locked_dev = dev.lock().unwrap(); + debug!( + "Attach usb device: xhci port id {} device id {}", + usb_port.lock().unwrap().port_id, + locked_dev.device_id() + ); + locked_dev.handle_attach()?; + locked_dev.set_controller(Arc::downgrade(&self.xhci)); + Ok(()) + } } impl PciDevOps for XhciPciDevice { @@ -207,7 +222,6 @@ impl PciDevOps for XhciPciDevice { let devfn = self.devfn; let dev = Arc::new(Mutex::new(self)); - let cloned_dev = dev.clone(); // Register xhci-pci to xhci-device for notify. dev.lock().unwrap().xhci.lock().unwrap().ctrl_ops = Some(Arc::downgrade(&dev) as Weak>); @@ -216,7 +230,7 @@ impl PciDevOps for XhciPciDevice { let mut locked_pci_bus = pci_bus.lock().unwrap(); let pci_device = locked_pci_bus.devices.get(&devfn); if pci_device.is_none() { - locked_pci_bus.devices.insert(devfn, dev.clone()); + locked_pci_bus.devices.insert(devfn, dev); } else { bail!( "Devfn {:?} has been used by {:?}", @@ -224,10 +238,6 @@ impl PciDevOps for XhciPciDevice { pci_device.unwrap().lock().unwrap().name() ); } - // Register xhci to bus device. - let locked_dev = dev.lock().unwrap(); - let mut locked_device = locked_dev.bus_device.lock().unwrap(); - locked_device.insert(String::from("usb.0"), cloned_dev); Ok(()) } @@ -285,28 +295,3 @@ impl XhciOps for XhciPciDevice { fn update_intr(&mut self, _n: u32, _enable: bool) {} } - -impl BusDeviceOps for XhciPciDevice { - fn attach_device(&mut self, dev: &Arc>) -> Result<()> { - let mut locked_xhci = self.xhci.lock().unwrap(); - let usb_port = if let Some(usb_port) = locked_xhci.assign_usb_port(dev) { - usb_port - } else { - bail!("No available USB port."); - }; - locked_xhci.port_update(&usb_port)?; - let mut locked_dev = dev.lock().unwrap(); - debug!( - "Attach usb device: xhci port id {} device id {}", - usb_port.lock().unwrap().port_id, - locked_dev.device_id() - ); - locked_dev.handle_attach()?; - locked_dev.set_controller(Arc::downgrade(&self.xhci)); - Ok(()) - } - - fn detach_device(&mut self, _dev: &Arc>) -> Result<()> { - bail!("Detach usb device not implemented"); - } -} diff --git a/util/src/lib.rs b/util/src/lib.rs index bf0ee9f96..19fed784c 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -41,7 +41,7 @@ pub use error::UtilError; use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; use log::debug; use once_cell::sync::Lazy; -use std::sync::Mutex; +use std::{any::Any, sync::Mutex}; use vmm_sys_util::terminal::Terminal; pub static TERMINAL_MODE: Lazy>> = Lazy::new(|| Mutex::new(None)); @@ -85,3 +85,14 @@ pub fn set_termi_canon_mode() -> std::io::Result<()> { Ok(()) } + +/// This trait is to cast trait object to struct. +pub trait AsAny { + fn as_any(&self) -> &dyn Any; +} + +impl AsAny for T { + fn as_any(&self) -> &dyn Any { + self + } +} -- Gitee From 3d87b18ec0dd2a39d50c8bfb8a4588fe4a9c4149 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 08:47:25 +0800 Subject: [PATCH 0644/2187] virtio-console: Deactivate console synchronously 1. Delete ConsoleHandler related notifiers directly. 2. No need to register and unregister chardev related notifiers dynamicaly, we add filed to show whether chardev has been deactivated or not. Signed-off-by: Keqian Zhu --- devices/src/legacy/chardev.rs | 25 ++++++-- virtio/src/console.rs | 117 +++++----------------------------- 2 files changed, 36 insertions(+), 106 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index d38f4c9db..ec3cc6f6d 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -56,6 +56,8 @@ pub struct Chardev { pub output: Option>>, /// Fd of socket stream. pub stream_fd: Option, + /// Device is deactivated or not. + pub deactivated: bool, /// Handle the input data and trigger interrupt if necessary. receive: ReceFn, /// Return the remain space size of receiver buffer. @@ -71,6 +73,7 @@ impl Chardev { input: None, output: None, stream_fd: None, + deactivated: false, receive: None, get_remain_space_size: None, } @@ -202,6 +205,9 @@ fn get_notifier_handler( match backend { ChardevType::Stdio | ChardevType::Pty => Rc::new(move |_, _| { let locked_chardev = chardev.lock().unwrap(); + if locked_chardev.deactivated { + return None; + } let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); let mut buffer = vec![0_u8; buff_size]; let input_h = locked_chardev.input.clone(); @@ -220,6 +226,9 @@ fn get_notifier_handler( }), ChardevType::Socket { .. } => Rc::new(move |_, _| { let mut locked_chardev = chardev.lock().unwrap(); + if locked_chardev.deactivated { + return None; + } let (stream, _) = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); let stream_fd = stream.as_raw_fd(); @@ -230,8 +239,11 @@ fn get_notifier_handler( let cloned_chardev = chardev.clone(); let inner_handler: Rc = Rc::new(move |event, _| { + let mut locked_chardev = cloned_chardev.lock().unwrap(); if event == EventSet::IN { - let locked_chardev = cloned_chardev.lock().unwrap(); + if locked_chardev.deactivated { + return None; + } let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); let mut buffer = vec![0_u8; buff_size]; if let Some(input) = locked_chardev.input.clone() { @@ -243,11 +255,12 @@ fn get_notifier_handler( } else { error!("Failed to get chardev input fd"); } - } - if event & EventSet::HANG_UP == EventSet::HANG_UP { - cloned_chardev.lock().unwrap().input = None; - cloned_chardev.lock().unwrap().output = None; - cloned_chardev.lock().unwrap().stream_fd = None; + None + } else if event & EventSet::HANG_UP == EventSet::HANG_UP { + // Always allow disconnect even if has deactivated. + locked_chardev.input = None; + locked_chardev.output = None; + locked_chardev.stream_fd = None; Some(gen_delete_notifiers(&[stream_fd])) } else { None diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 92deee342..ae0eb04ee 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -25,10 +25,8 @@ use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use devices::legacy::{Chardev, InputReceiver}; use log::{debug, error}; -use machine_manager::{ - config::{ChardevType, VirtioConsole}, - event_loop::EventLoop, -}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use machine_manager::{config::VirtioConsole, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -73,7 +71,6 @@ struct ConsoleHandler { input_queue: Arc>, output_queue: Arc>, output_queue_evt: EventFd, - deactivate_evt: RawFd, mem_space: Arc, interrupt_cb: Arc, driver_features: u64, @@ -215,62 +212,6 @@ impl ConsoleHandler { } } } - - fn deactivate_evt_handler(&self) -> Vec { - let locked_chardev = self.chardev.lock().unwrap(); - let mut notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt, - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.output_queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - match &locked_chardev.backend { - ChardevType::Stdio | ChardevType::Pty => { - if let Some(input) = locked_chardev.input.clone() { - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - input.lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )); - } - } - ChardevType::Socket { .. } => match locked_chardev.stream_fd { - Some(stream_fd) => { - notifiers.push(EventNotifier::new( - NotifierOperation::Park, - stream_fd, - None, - EventSet::IN | EventSet::HANG_UP, - Vec::new(), - )); - } - None => { - let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - listener_fd, - None, - EventSet::IN, - Vec::new(), - )); - } - }, - _ => (), - } - notifiers - } } impl EventNotifierHelper for ConsoleHandler { @@ -291,19 +232,6 @@ impl EventNotifierHelper for ConsoleHandler { vec![handler], )); - let cloned_cls = console_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(cloned_cls.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - console_handler.lock().unwrap().deactivate_evt, - None, - EventSet::IN, - vec![handler], - )); - notifiers } } @@ -326,11 +254,9 @@ pub struct Console { /// Status of console device. state: VirtioConsoleState, /// EventFd for device deactivate. - deactivate_evt: EventFd, + deactivate_evts: Vec, /// Character device for redirection. chardev: Arc>, - /// Connectability status between guest and console. - console_connected: bool, } impl Console { @@ -346,9 +272,8 @@ impl Console { driver_features: 0_u64, config_space: VirtioConsoleConfig::new(), }, - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), chardev: Arc::new(Mutex::new(Chardev::new(console_cfg.chardev))), - console_connected: false, } } } @@ -362,6 +287,11 @@ impl VirtioDevice for Console { .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; + self.chardev.lock().unwrap().deactivated = true; + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(self.chardev.clone()), + None, + )?; Ok(()) } @@ -433,38 +363,25 @@ impl VirtioDevice for Console { mem_space, interrupt_cb, driver_features: self.state.driver_features, - deactivate_evt: self.deactivate_evt.as_raw_fd(), chardev: self.chardev.clone(), }; let dev = Arc::new(Mutex::new(handler)); - EventLoop::update_event(EventNotifierHelper::internal_notifiers(dev.clone()), None)?; - let locked_dev = dev.lock().unwrap(); - locked_dev.chardev.lock().unwrap().set_input_callback(&dev); - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), - None, - )?; - self.console_connected = true; + let notifiers = EventNotifierHelper::internal_notifiers(dev.clone()); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + + self.chardev.lock().unwrap().set_input_callback(&dev); + self.chardev.lock().unwrap().deactivated = false; Ok(()) } fn deactivate(&mut self) -> Result<()> { - if self.console_connected { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) - } else { - Ok(()) - } + self.chardev.lock().unwrap().deactivated = true; + unregister_event_helper(None, &mut self.deactivate_evts) } fn reset(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - self.console_connected = false; - Ok(()) + self.deactivate() } } -- Gitee From 05bc092217a8bf5f13652f470f02126136a95181 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 22 Dec 2022 11:47:01 +0800 Subject: [PATCH 0645/2187] usb: refactor usb endpoint Remove the mutex of usb endpoint in usb device. And remove the dev filed in usb endpoint. Find the usb device in xhci directly instead of using the dev in endpoint. Signed-off-by: zhouli57 --- usb/src/descriptor.rs | 6 +- usb/src/hid.rs | 43 ++++++------- usb/src/keyboard.rs | 18 +++--- usb/src/tablet.rs | 18 +++--- usb/src/usb.rs | 107 ++++++++++++-------------------- usb/src/xhci/xhci_controller.rs | 52 +++++----------- 6 files changed, 90 insertions(+), 154 deletions(-) diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index 7377f88f6..471167bd4 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -332,15 +332,15 @@ impl UsbDescriptorOps for UsbDevice { } else { bail!("No interface descriptor found."); }; + let iface = iface.clone(); for e in 0..iface.interface_desc.bNumEndpoints { let in_direction = iface.eps[e as usize].endpoint_desc.bEndpointAddress & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let ep = iface.eps[e as usize].endpoint_desc.bEndpointAddress & USB_ENDPOINT_ADDRESS_NUMBER_MASK; - let usb_ep = self.get_endpoint(in_direction, ep as u8); - let mut locked_usb_ep = usb_ep.lock().unwrap(); - locked_usb_ep.ep_type = iface.eps[e as usize].endpoint_desc.bmAttributes + let mut usb_ep = self.get_mut_endpoint(in_direction, ep as u8); + usb_ep.ep_type = iface.eps[e as usize].endpoint_desc.bmAttributes & USB_ENDPOINT_ATTR_TRANSFER_TYPE_MASK; } } diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 031b426da..d8d248248 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -520,35 +520,28 @@ impl Hid { fn handle_token_in(&mut self, p: &mut UsbPacket) { let mut buf = Vec::new(); - if let Some(ep) = p.ep.as_ref() { - let clone_ep = ep.clone(); - let locked_ep = clone_ep.lock().unwrap(); - if locked_ep.ep_number == 1 { - if self.num == 0 { - debug!("No data in usb device."); - p.status = UsbPacketStatus::Nak; - return; + if p.ep_number == 1 { + if self.num == 0 { + debug!("No data in usb device."); + p.status = UsbPacketStatus::Nak; + return; + } + match self.kind { + HidType::Keyboard => { + buf = self.keyboard_poll(); } - match self.kind { - HidType::Keyboard => { - buf = self.keyboard_poll(); - } - HidType::Tablet => { - buf = self.pointer_poll(); - } - _ => { - error!("Unsupported HID device"); - p.status = UsbPacketStatus::Stall; - } + HidType::Tablet => { + buf = self.pointer_poll(); + } + _ => { + error!("Unsupported HID device"); + p.status = UsbPacketStatus::Stall; } - let len = buf.len(); - usb_packet_transfer(p, &mut buf, len); - } else { - error!("Unhandled endpoint {}", locked_ep.ep_number); - p.status = UsbPacketStatus::Stall; } + let len = buf.len(); + usb_packet_transfer(p, &mut buf, len); } else { - error!("USB endpoint not found {}", p); + error!("Unhandled endpoint {}", p.ep_number); p.status = UsbPacketStatus::Stall; } } diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 18c6d31a2..984531b00 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -25,8 +25,8 @@ use crate::hid::{ STR_PRODUCT_KEYBOARD, STR_SERIAL_KEYBOARD, }; use crate::usb::{ - notify_controller, usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, - UsbDescIface, UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, + notify_controller, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, + UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; @@ -131,15 +131,11 @@ impl UsbKeyboard { pub fn realize(mut self) -> Result>> { self.usb_device.product_desc = String::from("StratoVirt USB keyboard"); - self.usb_device.strings = Vec::new(); + self.usb_device.reset_usb_endpoint(); + self.usb_device.usb_desc = Some(DESC_KEYBOARD.clone()); + self.usb_device.init_descriptor()?; let kbd = Arc::new(Mutex::new(self)); - let cloned_kbd = kbd.clone(); - usb_endpoint_init(&(kbd as Arc>)); - let mut locked_kbd = cloned_kbd.lock().unwrap(); - locked_kbd.usb_device.usb_desc = Some(DESC_KEYBOARD.clone()); - locked_kbd.usb_device.init_descriptor()?; - drop(locked_kbd); - Ok(cloned_kbd) + Ok(kbd) } } @@ -215,7 +211,7 @@ impl UsbDeviceOps for UsbKeyboard { self.ctrl.clone() } - fn get_wakeup_endpoint(&self) -> Arc> { + fn get_wakeup_endpoint(&self) -> &UsbEndpoint { self.usb_device.get_endpoint(true, 1) } } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 9a58eaa81..d23e5975e 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -27,8 +27,8 @@ use crate::{ STR_SERIAL_TABLET, }, usb::{ - usb_endpoint_init, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, - UsbDescOther, UsbDevice, UsbDeviceOps, UsbEndpoint, UsbPacket, UsbPacketStatus, + UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, + UsbDevice, UsbDeviceOps, UsbEndpoint, UsbPacket, UsbPacketStatus, }, xhci::xhci_controller::XhciDevice, }; @@ -136,15 +136,11 @@ impl UsbTablet { pub fn realize(mut self) -> Result>> { self.usb_device.product_desc = String::from("StratoVirt USB Tablet"); - self.usb_device.strings = Vec::new(); + self.usb_device.reset_usb_endpoint(); + self.usb_device.usb_desc = Some(DESC_TABLET.clone()); + self.usb_device.init_descriptor()?; let tablet = Arc::new(Mutex::new(self)); - let cloned_tablet = tablet.clone(); - usb_endpoint_init(&(tablet as Arc>)); - let mut locked_tablet = cloned_tablet.lock().unwrap(); - locked_tablet.usb_device.usb_desc = Some(DESC_TABLET.clone()); - locked_tablet.usb_device.init_descriptor()?; - drop(locked_tablet); - Ok(cloned_tablet) + Ok(tablet) } } @@ -226,7 +222,7 @@ impl UsbDeviceOps for UsbTablet { self.ctrl.clone() } - fn get_wakeup_endpoint(&self) -> Arc> { + fn get_wakeup_endpoint(&self) -> &UsbEndpoint { self.usb_device.get_endpoint(true, 1) } } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index c8c71d4e5..6c272e1b8 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -52,12 +52,11 @@ pub struct UsbDeviceRequest { } /// The data transmission channel. -#[derive(Default)] +#[derive(Default, Clone)] pub struct UsbEndpoint { pub ep_number: u8, pub in_direction: bool, pub ep_type: u8, - pub dev: Option>>, } impl UsbEndpoint { @@ -66,26 +65,10 @@ impl UsbEndpoint { ep_number, in_direction, ep_type, - dev: None, } } } -/// Init USB endpoint, and set dev in endpoint. -pub fn usb_endpoint_init(dev: &Arc>) { - let mut locked_dev = dev.lock().unwrap(); - let usb_dev = locked_dev.get_mut_usb_device(); - usb_dev.reset_usb_endpoint(); - let mut ep_ctl = usb_dev.ep_ctl.lock().unwrap(); - ep_ctl.dev = Some(Arc::downgrade(dev)); - for i in 0..USB_MAX_ENDPOINTS { - let mut ep_in = usb_dev.ep_in[i as usize].lock().unwrap(); - let mut ep_out = usb_dev.ep_out[i as usize].lock().unwrap(); - ep_in.dev = Some(Arc::downgrade(dev)); - ep_out.dev = Some(Arc::downgrade(dev)); - } -} - /// USB descriptor strings. pub struct UsbDescString { pub index: u32, @@ -154,9 +137,9 @@ pub struct UsbDevice { pub product_desc: String, pub data_buf: Vec, pub remote_wakeup: u32, - pub ep_ctl: Arc>, - pub ep_in: Vec>>, - pub ep_out: Vec>>, + pub ep_ctl: UsbEndpoint, + pub ep_in: Vec, + pub ep_out: Vec, /// USB descriptor pub strings: Vec, pub usb_desc: Option>, @@ -175,11 +158,7 @@ impl UsbDevice { speed: 0, speed_mask: 0, addr: 0, - ep_ctl: Arc::new(Mutex::new(UsbEndpoint::new( - 0, - false, - USB_ENDPOINT_ATTR_CONTROL, - ))), + ep_ctl: UsbEndpoint::new(0, false, USB_ENDPOINT_ATTR_CONTROL), ep_in: Vec::new(), ep_out: Vec::new(), product_desc: String::new(), @@ -196,44 +175,46 @@ impl UsbDevice { }; for i in 0..USB_MAX_ENDPOINTS as u8 { - dev.ep_in.push(Arc::new(Mutex::new(UsbEndpoint::new( - i + 1, - true, - USB_ENDPOINT_ATTR_INVALID, - )))); - dev.ep_out.push(Arc::new(Mutex::new(UsbEndpoint::new( - i + 1, - false, - USB_ENDPOINT_ATTR_INVALID, - )))); + dev.ep_in + .push(UsbEndpoint::new(i + 1, true, USB_ENDPOINT_ATTR_INVALID)); + dev.ep_out + .push(UsbEndpoint::new(i + 1, false, USB_ENDPOINT_ATTR_INVALID)); } dev } - pub fn get_endpoint(&self, in_direction: bool, ep: u8) -> Arc> { + pub fn get_endpoint(&self, in_direction: bool, ep: u8) -> &UsbEndpoint { if ep == 0 { - return self.ep_ctl.clone(); + return &self.ep_ctl; } if in_direction { - self.ep_in[(ep - 1) as usize].clone() + &self.ep_in[(ep - 1) as usize] } else { - self.ep_out[(ep - 1) as usize].clone() + &self.ep_out[(ep - 1) as usize] + } + } + + pub fn get_mut_endpoint(&mut self, in_direction: bool, ep: u8) -> &mut UsbEndpoint { + if ep == 0 { + return &mut self.ep_ctl; + } + if in_direction { + &mut self.ep_in[(ep - 1) as usize] + } else { + &mut self.ep_out[(ep - 1) as usize] } } pub fn reset_usb_endpoint(&mut self) { - let mut ep_ctl = self.ep_ctl.lock().unwrap(); - ep_ctl.ep_number = 0; - ep_ctl.ep_type = USB_ENDPOINT_ATTR_CONTROL; + self.ep_ctl.ep_number = 0; + self.ep_ctl.ep_type = USB_ENDPOINT_ATTR_CONTROL; for i in 0..USB_MAX_ENDPOINTS { - let mut ep_in = self.ep_in[i as usize].lock().unwrap(); - let mut ep_out = self.ep_out[i as usize].lock().unwrap(); - ep_in.ep_number = (i + 1) as u8; - ep_out.ep_number = (i + 1) as u8; - ep_in.in_direction = true; - ep_out.in_direction = false; - ep_in.ep_type = USB_ENDPOINT_ATTR_INVALID; - ep_out.ep_type = USB_ENDPOINT_ATTR_INVALID; + self.ep_in[i as usize].ep_number = (i + 1) as u8; + self.ep_in[i as usize].in_direction = true; + self.ep_in[i as usize].ep_type = USB_ENDPOINT_ATTR_INVALID; + self.ep_out[i as usize].ep_number = (i + 1) as u8; + self.ep_out[i as usize].in_direction = false; + self.ep_out[i as usize].ep_type = USB_ENDPOINT_ATTR_INVALID; } } @@ -376,7 +357,7 @@ pub trait UsbDeviceOps: Send + Sync { fn get_controller(&self) -> Option>>; /// Get the endpoint to wakeup. - fn get_wakeup_endpoint(&self) -> Arc>; + fn get_wakeup_endpoint(&self) -> &UsbEndpoint; /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { @@ -387,17 +368,8 @@ pub trait UsbDeviceOps: Send + Sync { /// Handle usb packet, used for controller to deliever packet to device. fn handle_packet(&mut self, packet: &mut UsbPacket) { packet.status = UsbPacketStatus::Success; - let ep = if let Some(ep) = &packet.ep { - ep - } else { - error!("Failed to find ep"); - packet.status = UsbPacketStatus::NoDev; - return; - }; - let locked_ep = ep.lock().unwrap(); - let ep_nr = locked_ep.ep_number; + let ep_nr = packet.ep_number; debug!("handle packet endpointer number {}", ep_nr); - drop(locked_ep); if ep_nr == 0 { if let Err(e) = self.do_parameter(packet) { error!("Failed to handle control packet {}", e); @@ -477,7 +449,7 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { }; let slot_id = usb_dev.addr; let wakeup = usb_dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP; - let ep = locked_dev.get_wakeup_endpoint(); + let ep = locked_dev.get_wakeup_endpoint().clone(); // Drop the small lock. drop(locked_dev); let mut locked_xhci = xhci.lock().unwrap(); @@ -521,7 +493,6 @@ impl Iovec { pub struct UsbPacket { /// USB packet id. pub pid: u32, - pub ep: Option>>, pub iovecs: Vec, /// control transfer parameter. pub parameter: u64, @@ -529,6 +500,8 @@ pub struct UsbPacket { pub status: UsbPacketStatus, /// Actually transfer length. pub actual_length: u32, + /// Endpoint number. + pub ep_number: u8, } impl std::fmt::Display for UsbPacket { @@ -542,12 +515,12 @@ impl std::fmt::Display for UsbPacket { } impl UsbPacket { - pub fn init(&mut self, pid: u32, ep: Arc>) { + pub fn init(&mut self, pid: u32, ep_number: u8) { self.pid = pid; - self.ep = Some(ep); self.status = UsbPacketStatus::Success; self.actual_length = 0; self.parameter = 0; + self.ep_number = ep_number; } } @@ -555,11 +528,11 @@ impl Default for UsbPacket { fn default() -> UsbPacket { UsbPacket { pid: 0, - ep: None, iovecs: Vec::new(), parameter: 0, status: UsbPacketStatus::NoDev, actual_length: 0, + ep_number: 0, } } } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 047b1cd94..6a7dc4413 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -836,9 +836,7 @@ impl XhciDevice { fn set_device_address(&mut self, dev: &Arc>, addr: u32) { let mut p = UsbPacket::default(); let mut locked_dev = dev.lock().unwrap(); - let usb_dev = locked_dev.get_mut_usb_device(); - let ep = usb_dev.get_endpoint(false, 0); - p.init(USB_TOKEN_OUT as u32, ep); + p.init(USB_TOKEN_OUT as u32, 0); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, request: USB_REQUEST_SET_ADDRESS, @@ -1267,7 +1265,7 @@ impl XhciDevice { .as_ref() .unwrap() .clone(); - self.device_handle_packet(&mut xfer.packet); + self.device_handle_packet(xfer); if xfer.packet.status == UsbPacketStatus::Nak { debug!("USB packet status is NAK"); // NAK need to retry again. @@ -1283,21 +1281,12 @@ impl XhciDevice { Ok(true) } - fn device_handle_packet(&mut self, packet: &mut UsbPacket) { - if let Some(ep) = &packet.ep { - let locked_ep = ep.lock().unwrap(); - let dev = if let Some(usb_dev) = &locked_ep.dev { - usb_dev.upgrade().unwrap() - } else { - packet.status = UsbPacketStatus::NoDev; - error!("Failed to handle packet, No device found in endpoint"); - return; - }; - drop(locked_ep); - let mut locked_dev = dev.lock().unwrap(); - locked_dev.handle_packet(packet); + fn device_handle_packet(&mut self, xfer: &mut XhciTransfer) { + if let Ok(usb_dev) = self.get_usb_dev(xfer.slotid, xfer.epid) { + let mut locked_dev = usb_dev.lock().unwrap(); + locked_dev.handle_packet(&mut xfer.packet); } else { - packet.status = UsbPacketStatus::NoDev; + xfer.packet.status = UsbPacketStatus::NoDev; error!("Failed to handle packet, No endpoint found"); } } @@ -1326,7 +1315,7 @@ impl XhciDevice { return self.report_transfer_error(xfer); } xfer.packet.parameter = trb_setup.parameter; - self.device_handle_packet(&mut xfer.packet); + self.device_handle_packet(xfer); self.complete_packet(xfer)?; Ok(()) } @@ -1372,18 +1361,13 @@ impl XhciDevice { error!("Failed to setup packet when transfer data {}", e); return self.report_transfer_error(xfer); } - self.device_handle_packet(&mut xfer.packet); + self.device_handle_packet(xfer); self.complete_packet(xfer)?; Ok(()) } // Setup USB packet, include mapping dma address to iovector. fn setup_usb_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - let ep = if let Some(ep) = &xfer.packet.ep { - ep.clone() - } else { - self.get_usb_ep(xfer.slotid, xfer.epid)? - }; let dir = if xfer.in_xfer { USB_TOKEN_IN } else { @@ -1419,12 +1403,13 @@ impl XhciDevice { } } } - xfer.packet.init(dir as u32, ep); + let (_, ep_number) = endpoint_id_to_number(xfer.epid as u8); + xfer.packet.init(dir as u32, ep_number); xfer.packet.iovecs = vec; Ok(()) } - fn get_usb_ep(&self, slotid: u32, epid: u32) -> Result>> { + fn get_usb_dev(&self, slotid: u32, epid: u32) -> Result>> { let port = if let Some(port) = &self.slots[(slotid - 1) as usize].usb_port { port } else { @@ -1436,11 +1421,7 @@ impl XhciDevice { } else { bail!("No device found in USB port."); }; - let mut locked_dev = dev.lock().unwrap(); - let usb_dev = locked_dev.get_mut_usb_device(); - let (in_direction, ep_number) = endpoint_id_to_number(epid as u8); - let ep = usb_dev.get_endpoint(in_direction, ep_number); - Ok(ep) + Ok(dev.clone()) } /// Update packet status and then submit transfer. @@ -1639,11 +1620,8 @@ impl XhciDevice { } /// Used for device to wakeup endpoint - pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &Arc>) -> Result<()> { - let locked_ep = ep.lock().unwrap(); - let ep_id = endpoint_number_to_id(locked_ep.in_direction, locked_ep.ep_number); - // Kick endpoint may hold the lock, drop it. - drop(locked_ep); + pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &UsbEndpoint) -> Result<()> { + let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); self.kick_endpoint(slot_id as u32, ep_id as u32)?; Ok(()) } -- Gitee From baf4e38ac8cb86e5c1e76994837562bc30def513 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 22 Dec 2022 14:10:53 +0800 Subject: [PATCH 0646/2187] xhci: simplify the submit transfer According to the spec, the following conditions should force Transfer Event generation to take place immediately: The completion of a TRB that has its IOC flag set. The completion of a Short Packet on a TRB that has its ISP flag set. An error occurs on any Treansfer TRB. We use submit_transfer to report succeed transfer, and use report_transfer_error to report error. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 57 +++++++-------------------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 6a7dc4413..c8d053d15 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -1447,82 +1447,53 @@ impl XhciDevice { match xfer.packet.status { v if v == UsbPacketStatus::NoDev || v == UsbPacketStatus::IoError => { xfer.status = TRBCCode::UsbTransactionError; - self.submit_transfer(xfer)?; } UsbPacketStatus::Stall => { xfer.status = TRBCCode::StallError; - self.submit_transfer(xfer)?; } UsbPacketStatus::Babble => { xfer.status = TRBCCode::BabbleDetected; - self.submit_transfer(xfer)?; } _ => { error!("Unhandle status {:?}", xfer.packet.status); - self.report_transfer_error(xfer)?; } } + self.report_transfer_error(xfer)?; // Set the endpoint state to halted if an error occurs in the packet. let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; epctx.set_state(&self.mem_space, EP_HALTED)?; Ok(()) } - /// Submit transfer TRBs. + /// Submit the succeed transfer TRBs. fn submit_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { // Event Data Transfer Length Accumulator let mut edtla = 0; let mut left = xfer.packet.actual_length; - let mut reported = false; - let mut short_pkt = false; for i in 0..xfer.td.len() { let trb = &xfer.td[i]; let trb_type = trb.get_type(); let mut chunk = trb.status & TRB_TR_LEN_MASK; match trb_type { - TRBType::TrSetup => { - if chunk > 8 { - chunk = 8; - } - } + TRBType::TrSetup => {} TRBType::TrData | TRBType::TrNormal | TRBType::TrIsoch => { if chunk > left { chunk = left; - if xfer.status == TRBCCode::Success { - short_pkt = true; - } + xfer.status = TRBCCode::ShortPacket; } left -= chunk; edtla += chunk; } - TRBType::TrStatus => { - reported = false; - short_pkt = false; - } + TRBType::TrStatus => {} _ => { debug!("Ignore the TRB, unhandled trb type {:?}", trb.get_type()); } } - if !reported - && ((trb.control & TRB_TR_IOC == TRB_TR_IOC) - || (short_pkt && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) - || (xfer.status != TRBCCode::Success && left == 0)) + if (trb.control & TRB_TR_IOC == TRB_TR_IOC) + || (xfer.status == TRBCCode::ShortPacket + && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) { - self.send_transfer_event(xfer, trb, chunk, short_pkt, &mut edtla)?; - reported = true; - if xfer.status != TRBCCode::Success { - // Send unSuccess event succeed, return directly. - warn!( - "A warning is generated when the transfer is submitted, xfer status {:?}", - xfer.status - ); - return Ok(()); - } - } - // Allow reporting events after IOC bit is set in setup TRB. - if trb_type == TRBType::TrSetup { - reported = false; - short_pkt = false; + self.send_transfer_event(xfer, trb, chunk, &mut edtla)?; } } Ok(()) @@ -1534,10 +1505,11 @@ impl XhciDevice { let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); evt.slot_id = xfer.slotid as u8; evt.ep_id = xfer.epid as u8; + evt.ccode = xfer.status; let mut idx = 0; // According to 4.10.1 Transfer TRBs, the TRB pointer field in a Transfer TRB not // only references the TRB that generated the event, but it also provides system software - // with thr latest value of the xHC Dequeue Pointer for the Transfer Ring. + // with the latest value of the xHC Dequeue Pointer for the Transfer Ring. if let Some(trb) = xfer.td.last() { evt.ptr = trb.addr; idx = (trb.status >> TRB_INTR_SHIFT) & TRB_INTR_MASK; @@ -1550,7 +1522,6 @@ impl XhciDevice { xfer: &XhciTransfer, trb: &XhciTRB, transfered: u32, - short_pkt: bool, edtla: &mut u32, ) -> Result<()> { let trb_type = trb.get_type(); @@ -1560,11 +1531,7 @@ impl XhciDevice { evt.length = (trb.status & TRB_TR_LEN_MASK) - transfered; evt.flags = 0; evt.ptr = trb.addr; - evt.ccode = if short_pkt && xfer.status == TRBCCode::Success { - TRBCCode::ShortPacket - } else { - xfer.status - }; + evt.ccode = xfer.status; if trb_type == TRBType::TrEvdata { evt.ptr = trb.parameter; evt.flags |= TRB_EV_ED; -- Gitee From 4ed16a7ebed8cab15a8cec0ca7cf248b991a82ec Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 24 Dec 2022 11:28:23 +0800 Subject: [PATCH 0647/2187] Fix: Don't kick vcpu when vcpu state is paused. In some cases, there is no need to kick a vcpu that is in a pause state after a shutdown command is executed by the guest kernel. The kick operation is to keep the vcpu in the user state so that the vcpu thread can be destroyed. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index cf094c4d2..9be6e8285 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -404,15 +404,17 @@ impl CPUInterface for CPU { fn destroy(&self) -> Result<()> { let (cpu_state, cvar) = &*self.state; - if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { - *cpu_state.lock().unwrap() = CpuLifecycleState::Stopping; - } else if *cpu_state.lock().unwrap() == CpuLifecycleState::Stopped { - *cpu_state.lock().unwrap() = CpuLifecycleState::Nothing; + let mut cpu_state = cpu_state.lock().unwrap(); + if *cpu_state == CpuLifecycleState::Running { + *cpu_state = CpuLifecycleState::Stopping; + } else if *cpu_state == CpuLifecycleState::Stopped + || *cpu_state == CpuLifecycleState::Paused + { + *cpu_state = CpuLifecycleState::Nothing; return Ok(()); } self.kick()?; - let mut cpu_state = cpu_state.lock().unwrap(); cpu_state = cvar .wait_timeout(cpu_state, Duration::from_millis(32)) .unwrap() -- Gitee From bc3acd605ab2d5adba466607c33dc6cc0e0fdb9f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 14:15:06 +0800 Subject: [PATCH 0648/2187] safety: Delete some Sync and Send implementation Some Sync and Send implementation are not used, delete them. Signed-off-by: Keqian Zhu --- machine_manager/src/machine.rs | 3 --- migration_derive/src/lib.rs | 2 -- util/src/loop_context.rs | 3 ++- virtio/src/block.rs | 6 +++--- virtio/src/net.rs | 6 +++--- vnc/src/client.rs | 1 - 6 files changed, 8 insertions(+), 13 deletions(-) diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 8e2fd7766..00195244f 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -48,9 +48,6 @@ pub enum VmEvent { ShutdownCauseInternalError, } -unsafe impl Sync for VmEvent {} -unsafe impl Send for VmEvent {} - /// Trait to handle virtual machine lifecycle. /// /// # Notes diff --git a/migration_derive/src/lib.rs b/migration_derive/src/lib.rs index a219f1ec3..1b39268a4 100644 --- a/migration_derive/src/lib.rs +++ b/migration_derive/src/lib.rs @@ -100,8 +100,6 @@ pub fn derive_bytecode(input: TokenStream) -> TokenStream { #struct_default } } - unsafe impl Send for #ident {} - unsafe impl Sync for #ident {} impl util::byte_code::ByteCode for #ident {} }) .into() diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ff0b7baf6..780ca9d3d 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -191,7 +191,8 @@ pub struct EventLoopContext { timers: Arc>>, } -unsafe impl Sync for EventLoopContext {} +// SAFETY: The closure in EventNotifier and Timer doesn't impl Send, they're +// not sent between threads actually. unsafe impl Send for EventLoopContext {} impl EventLoopContext { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 32b05bc2e..19d370b0f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1169,9 +1169,9 @@ impl VirtioDevice for Block { } } -// Send and Sync is not auto-implemented for `Sender` type. -// Implementing them is safe because `Sender` field of Block won't change in migration -// workflow. +// SAFETY: Send and Sync is not auto-implemented for `Sender` type. +// Implementing them is safe because `Sender` field of Block won't +// change in migration workflow. unsafe impl Sync for Block {} impl StateTransfer for Block { diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 9ca88de26..8406f3d8c 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1629,9 +1629,9 @@ impl VirtioDevice for Net { } } -// Send and Sync is not auto-implemented for `Sender` type. -// Implementing them is safe because `Sender` field of Net won't change in migration -// workflow. +// SAFETY: Send and Sync is not auto-implemented for `Sender` type. +// Implementing them is safe because `Sender` field of Net won't +// change in migration workflow. unsafe impl Sync for Net {} impl StateTransfer for Net { diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 758b9c488..99eba0e29 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -193,7 +193,6 @@ impl Default for DisplayMode { } } -unsafe impl Send for RectInfo {} pub struct RectInfo { /// Vnc client state. pub client: Arc, -- Gitee From ffcdcae6d4f215448f7be7bf30a35eb99bfb96c7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 18:02:11 +0800 Subject: [PATCH 0649/2187] safety: Add comment for some unsafe code blocks Use the standard SAFETY mark. Signed-off-by: Keqian Zhu --- machine_manager/src/event_loop.rs | 5 +++++ util/src/aio/libaio.rs | 5 +++++ util/src/aio/mod.rs | 15 ++++++++++++--- util/src/aio/raw.rs | 3 +++ util/src/aio/uring.rs | 2 ++ util/src/file.rs | 2 ++ util/src/loop_context.rs | 15 +++++++++------ 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index fd0f3908a..1a43822ab 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -54,6 +54,8 @@ impl EventLoop { } } + // SAFETY: This function is called at startup thus no concurrent accessing to + // GLOBAL_EVENT_LOOP. And each iothread has a dedicated EventLoopContext. unsafe { if GLOBAL_EVENT_LOOP.is_none() { GLOBAL_EVENT_LOOP = Some(EventLoop { @@ -94,6 +96,7 @@ impl EventLoop { /// /// * `name` - if None, return main loop, OR return io-thread-loop which is related to `name`. pub fn get_ctx(name: Option<&String>) -> Option<&mut EventLoopContext> { + // SAFETY: All concurrently accessed data of EventLoopContext is protected. unsafe { if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { if let Some(name) = name { @@ -140,6 +143,8 @@ impl EventLoop { /// Once run main loop, `epoll` in `MainLoopContext` will execute /// `epoll_wait()` function to wait for events. pub fn loop_run() -> util::Result<()> { + // SAFETY: the main_loop ctx is dedicated for main thread, thus no concurrent + // accessing. unsafe { if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { loop { diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 6ea1bc58a..703b150cc 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -73,6 +73,7 @@ pub struct LibaioContext { impl Drop for LibaioContext { fn drop(&mut self) { if !self.ctx.is_null() { + // SAFETY: self.ctx is generated by SYS_io_setup. unsafe { libc::syscall(libc::SYS_io_destroy, self.ctx) }; } } @@ -98,6 +99,7 @@ impl LibaioContext { pub fn new(max_size: i32) -> Result { let mut ctx = std::ptr::null_mut(); + // SAFETY: ctx is a valid ptr. let ret = unsafe { libc::syscall(libc::SYS_io_setup, max_size, &mut ctx) }; if ret < 0 { bail!("Failed to setup aio context, return {}.", ret); @@ -115,6 +117,7 @@ impl LibaioContext { impl AioContext for LibaioContext { /// Submit requests. fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result { + // SAFETY: self.ctx is generated by SYS_io_setup. let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; if ret >= 0 { return Ok(ret as usize); @@ -128,6 +131,7 @@ impl AioContext for LibaioContext { /// Get the IO events. fn get_events(&mut self) -> &[IoEvent] { let ring = self.ctx as *mut AioRing; + // SAFETY: self.ctx is generated by SYS_io_setup. let head = unsafe { (*ring).head }; let tail = unsafe { (*ring).tail }; let ring_nr = unsafe { (*ring).nr }; @@ -148,6 +152,7 @@ impl AioContext for LibaioContext { // Avoid head is updated before we consume all io_events. fence(Ordering::Release); + // SAFETY: self.ctx is generated by SYS_io_setup. unsafe { (*ring).head = tail }; &self.events diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index eaf951215..fc00d61df 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -124,6 +124,7 @@ impl Aio { let mut done = false; for evt in self.ctx.get_events() { + // SAFETY: evt.data is specified by submit and not dropped at other place. unsafe { let node = evt.data as *mut CbNode; let res = if (evt.res2 == 0) && (evt.res == (*node).value.nbytes as i64) { @@ -274,11 +275,11 @@ impl Aio { } fn handle_misaligned_rw(&mut self, cb: &AioCb) -> Result<()> { - // Safe because we only get the host page size. + // SAFETY: only get the host page size. let host_page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u64; match cb.opcode { IoCmd::Preadv => { - // Safe because we allocate aligned memory and free it later. + // SAFETY: we allocate aligned memory and free it later. // Alignment is set to host page size to decrease the count of allocated pages. let aligned_buffer = unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; @@ -292,11 +293,12 @@ impl Aio { cb.offset, ) .map_err(|e| { - // Safe because the memory is allocated by us and will not be used anymore. + // SAFETY: the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; anyhow!("Failed to do raw read for misaligned read, {:?}", e) })?; + // SAFETY: the memory is allocated by us. let src = unsafe { std::slice::from_raw_parts(aligned_buffer as *const u8, cb.nbytes as usize) }; @@ -307,15 +309,18 @@ impl Aio { Err(anyhow!("Failed to copy iovs to buff for misaligned read")) } }); + // SAFETY: the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; res } IoCmd::Pwritev => { + // SAFETY: we allocate aligned memory and free it later. let aligned_buffer = unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; if aligned_buffer.is_null() { bail!("Failed to alloc memory for misaligned write"); } + // SAFETY: the memory is allocated by us. let dst = unsafe { std::slice::from_raw_parts_mut(aligned_buffer as *mut u8, cb.nbytes as usize) }; @@ -326,6 +331,7 @@ impl Aio { Err(anyhow!("Failed to copy iovs to buff for misaligned write")) } }) { + // SAFETY: the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; return Err(e); } @@ -338,6 +344,7 @@ impl Aio { ) .map(|_| {}) .map_err(|e| anyhow!("Failed to do raw write for misaligned write, {:?}", e)); + // SAFETY: the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; res } @@ -355,6 +362,7 @@ impl Aio { } fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { + // SAFETY: all callers have valid hva address. let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; (&mut slice) .write(buf) @@ -379,6 +387,7 @@ pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { } pub fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { + // SAFETY: all callers have valid hva address. let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; buf.write(slice) .with_context(|| format!("Failed to read buf from hva:{})", hva))?; diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 3fa8d5af6..a0c4eb187 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -18,6 +18,7 @@ use std::os::unix::io::RawFd; pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result { let mut ret; loop { + // SAFETY: fd and buf is valid. ret = unsafe { pread(fd, buf as *mut c_void, size, offset as i64) as i64 }; if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { break; @@ -33,6 +34,7 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result { let mut ret; loop { + // SAFETY: fd and buf is valid. ret = unsafe { pwrite(fd, buf as *mut c_void, size, offset as i64) as i64 }; if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { break; @@ -46,6 +48,7 @@ pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result } pub fn raw_datasync(fd: RawFd) -> Result { + // SAFETY: fd is valid. let ret = unsafe { i64::from(fdatasync(fd)) }; if ret < 0 { bail!("Failed to fdatasync for {}, return {}.", fd, ret); diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 3360f2876..4350bd56e 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -53,6 +53,7 @@ impl AioContext for IoUringContext { /// Submit requests to OS. fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result { for iocb in iocbp.iter() { + // SAFETY: iocb is valid until request is finished. let offset = unsafe { (*(*iocb)).aio_offset as libc::off_t }; let node = unsafe { (*(*iocb)).data as *mut CbNode }; let aiocb = unsafe { &mut (*node).value as *mut AioCb }; @@ -81,6 +82,7 @@ impl AioContext for IoUringContext { bail!("Invalid entry code"); } }; + // SAFETY: parameters of the entry are valid until request is finished. unsafe { self.ring .submission() diff --git a/util/src/file.rs b/util/src/file.rs index cb3ffe958..97b32654a 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -39,6 +39,7 @@ pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { } else { (libc::LOCK_EX | libc::LOCK_NB, "write lock") }; + // SAFETY: the file has a valid raw fd. let ret = unsafe { libc::flock(file.as_raw_fd(), lockop) }; if ret < 0 { bail!( @@ -54,6 +55,7 @@ pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { } pub fn unlock_file(file: &File, path: &str) -> Result<()> { + // SAFETY: the file has a valid raw fd. let ret = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_UN) }; if ret < 0 { bail!( diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 780ca9d3d..3bf520f60 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -64,15 +64,15 @@ pub type NotifierCallback = dyn Fn(EventSet, RawFd) -> Option /// Epoll Event Notifier Entry. pub struct EventNotifier { /// Raw file descriptor - pub raw_fd: i32, + raw_fd: i32, /// Notifier operation - pub op: NotifierOperation, + op: NotifierOperation, /// Parked fd, temporarily removed from epoll - pub parked_fd: Option, + parked_fd: Option, /// The types of events for which we use this fd - pub event: EventSet, + event: EventSet, /// Event Handler List, one fd event may have many handlers - pub handlers: Vec>, + handlers: Vec>, /// Pre-polling handler pub handler_poll: Option>, /// Event status @@ -511,7 +511,7 @@ impl EventLoopContext { }; for i in 0..ev_count { - // It`s safe because elements in self.events_map never get released in other functions + // SAFETY: elements in self.events_map never get released in other functions let event = unsafe { let event_ptr = self.ready_events[i].data() as *const EventNotifier; &*event_ptr as &EventNotifier @@ -548,6 +548,9 @@ impl Default for EventLoopContext { pub fn read_fd(fd: RawFd) -> u64 { let mut value: u64 = 0; + // SAFETY: this is called by notifier handler and notifier handler + // is executed with fd is is valid. The value is defined above thus + // valid too. let ret = unsafe { read( fd, -- Gitee From d14ea5aab8fd710dfd061f43e1744525dc7ba653 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 24 Dec 2022 18:13:31 +0800 Subject: [PATCH 0650/2187] simple refactor: Data structure dependency optimization, Machine --> CPU --> VcpuFd The LightMachine or StdMachine structure indicates the VM, and the cpu structure indicates the CPU resources of the VM. VM uses the CPU structure to access the system CPU resources without being aware of the virtual cpu resources defined by the operating system. Signed-off-by: Yan Wen --- Cargo.lock | 1 - machine/Cargo.toml | 1 - machine/src/error.rs | 5 - machine/src/lib.rs | 12 ++- machine/src/micro_vm/error.rs | 5 - machine/src/micro_vm/mod.rs | 136 +++++++++++++------------ machine/src/standard_vm/aarch64/mod.rs | 28 ++--- machine/src/standard_vm/x86_64/mod.rs | 7 -- 8 files changed, 88 insertions(+), 107 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4592d34f2..3e229f27d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -430,7 +430,6 @@ dependencies = [ "devices", "hypervisor", "kvm-bindings", - "kvm-ioctls", "libc", "log", "machine_manager", diff --git a/machine/Cargo.toml b/machine/Cargo.toml index d22d8e218..8973f1cfd 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -8,7 +8,6 @@ description = "Emulation machines" [dependencies] kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" log = "0.4" libc = "0.2" serde = { version = "1.0", features = ["derive"] } diff --git a/machine/src/error.rs b/machine/src/error.rs index 12bbe44a3..60877f852 100644 --- a/machine/src/error.rs +++ b/machine/src/error.rs @@ -60,11 +60,6 @@ pub enum MachineError { #[from] source: hypervisor::error::HypervisorError, }, - #[error("KvmIoctl")] - KvmIoctl { - #[from] - source: kvm_ioctls::Error, - }, #[error("Io")] Io { #[from] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f07b6f855..51bbaa4d7 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -25,7 +25,6 @@ use std::os::unix::{io::AsRawFd, net::UnixListener}; use std::path::Path; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; -use kvm_ioctls::VcpuFd; use log::warn; use util::file::{lock_file, unlock_file}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -167,13 +166,11 @@ pub trait MachineOps { /// /// * `vm` - `MachineInterface` to obtain functions cpu can use. /// * `nr_cpus` - The number of vcpus. - /// * `fds` - File descriptors obtained by creating new Vcpu in KVM. /// * `boot_cfg` - Boot message generated by reading boot source to guest memory. fn init_vcpu( vm: Arc>, nr_cpus: u8, topology: &CPUTopology, - fds: &[Arc], boot_cfg: &Option, #[cfg(target_arch = "aarch64")] vcpu_cfg: &Option, ) -> Result>> @@ -183,13 +180,20 @@ pub trait MachineOps { let mut cpus = Vec::>::new(); for vcpu_id in 0..nr_cpus { + let vcpu_fd = KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_vcpu(vcpu_id as u64) + .with_context(|| "Create vcpu failed")?; #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); #[cfg(target_arch = "x86_64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(nr_cpus)); let cpu = Arc::new(CPU::new( - fds[vcpu_id as usize].clone(), + Arc::new(vcpu_fd), vcpu_id, Arc::new(Mutex::new(arch_cpu)), vm.clone(), diff --git a/machine/src/micro_vm/error.rs b/machine/src/micro_vm/error.rs index 150f3e240..d18eedf62 100644 --- a/machine/src/micro_vm/error.rs +++ b/machine/src/micro_vm/error.rs @@ -30,11 +30,6 @@ pub enum MicroVmError { source: std::io::Error, }, #[error("Util")] - Kvm { - #[from] - source: kvm_ioctls::Error, - }, - #[error("Util")] Nul { #[from] source: std::ffi::NulError, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 133b78251..3aab07981 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -60,6 +60,7 @@ use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; #[cfg(target_arch = "aarch64")] use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; +#[cfg(target_arch = "x86_64")] use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; @@ -734,6 +735,13 @@ impl MachineOps for LightMachine { trace_sysbus(&locked_vm.sysbus); trace_vm_state(&locked_vm.vm_state); + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); + trace_cpu_topo(&topology); + locked_vm.init_memory( &vm_config.machine_config.mem_config, #[cfg(target_arch = "x86_64")] @@ -742,81 +750,83 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_cpus, )?; + let migrate_info = locked_vm.get_migrate_info(); + #[cfg(target_arch = "x86_64")] { locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; LightMachine::arch_init()?; - } - let mut vcpu_fds = vec![]; - for vcpu_id in 0..vm_config.machine_config.nr_cpus { - vcpu_fds.push(Arc::new( - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(vcpu_id as u64)?, - )); - } - #[cfg(target_arch = "aarch64")] - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; - // Add mmio devices - locked_vm - .create_replaceable_devices() - .with_context(|| "Failed to create replaceable devices.")?; - locked_vm.add_devices(vm_config)?; - trace_replaceable_info(&locked_vm.replaceable_info); + // Add mmio devices + locked_vm + .create_replaceable_devices() + .with_context(|| "Failed to create replaceable devices.")?; + locked_vm.add_devices(vm_config)?; + trace_replaceable_info(&locked_vm.replaceable_info); - let migrate_info = locked_vm.get_migrate_info(); - let boot_config = if migrate_info.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(None)?) - } else { - None - }; - let topology = CPUTopology::new().set_topology(( - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - )); - trace_cpu_topo(&topology); + let boot_config = if migrate_info.0 == MigrateMode::Unknown { + Some(locked_vm.load_boot_source(None)?) + } else { + None + }; + + // vCPUs init + locked_vm.cpus.extend(::init_vcpu( + vm.clone(), + vm_config.machine_config.nr_cpus, + &topology, + &boot_config, + )?); + } #[cfg(target_arch = "aarch64")] - let cpu_config = if migrate_info.0 == MigrateMode::Unknown { - Some(locked_vm.load_cpu_features(vm_config)?) - } else { - None - }; + { + let (boot_config, cpu_config) = if migrate_info.0 == MigrateMode::Unknown { + ( + Some(locked_vm.load_boot_source(None)?), + Some(locked_vm.load_cpu_features(vm_config)?), + ) + } else { + (None, None) + }; - // vCPUs init,and apply CPU features (for aarch64) - locked_vm.cpus.extend(::init_vcpu( - vm.clone(), - vm_config.machine_config.nr_cpus, - &topology, - &vcpu_fds, - &boot_config, - #[cfg(target_arch = "aarch64")] - &cpu_config, - )?); + // vCPUs init,and apply CPU features (for aarch64) + locked_vm.cpus.extend(::init_vcpu( + vm.clone(), + vm_config.machine_config.nr_cpus, + &topology, + &boot_config, + &cpu_config, + )?); - #[cfg(target_arch = "aarch64")] - if let Some(boot_cfg) = boot_config { - let mut fdt_helper = FdtBuilder::new(); - locked_vm - .generate_fdt_node(&mut fdt_helper) - .with_context(|| anyhow!(MachineError::GenFdtErr))?; - let fdt_vec = fdt_helper.finish()?; + locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + + // Add mmio devices locked_vm - .sys_mem - .write( - &mut fdt_vec.as_slice(), - GuestAddress(boot_cfg.fdt_addr as u64), - fdt_vec.len() as u64, - ) - .with_context(|| { - anyhow!(MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len())) - })?; + .create_replaceable_devices() + .with_context(|| "Failed to create replaceable devices.")?; + locked_vm.add_devices(vm_config)?; + trace_replaceable_info(&locked_vm.replaceable_info); + + if let Some(boot_cfg) = boot_config { + let mut fdt_helper = FdtBuilder::new(); + locked_vm + .generate_fdt_node(&mut fdt_helper) + .with_context(|| anyhow!(MachineError::GenFdtErr))?; + let fdt_vec = fdt_helper.finish()?; + locked_vm + .sys_mem + .write( + &mut fdt_vec.as_slice(), + GuestAddress(boot_cfg.fdt_addr as u64), + fdt_vec.len() as u64, + ) + .with_context(|| { + anyhow!(MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len())) + })?; + } } + locked_vm .register_power_event(&locked_vm.power_button) .with_context(|| anyhow!(MachineError::InitEventFdErr("power_button".to_string())))?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 14933f44a..5dd652e42 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -473,30 +473,10 @@ impl MachineOps for StdMachine { nr_cpus, )?; - let vcpu_fds = { - let mut fds = vec![]; - for vcpu_id in 0..nr_cpus { - fds.push(Arc::new( - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(vcpu_id as u64)?, - )); - } - fds - }; - - // Interrupt Controller Chip init - locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; locked_vm .init_pci_host() .with_context(|| anyhow!(StdErrorKind::InitPCIeHostErr))?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; - locked_vm - .add_devices(vm_config) - .with_context(|| "Failed to add devices")?; #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; @@ -521,11 +501,17 @@ impl MachineOps for StdMachine { vm.clone(), nr_cpus, &CPUTopology::new(), - &vcpu_fds, &boot_config, &cpu_config, )?); + // Interrupt Controller Chip init + locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; + + locked_vm + .add_devices(vm_config) + .with_context(|| "Failed to add devices")?; + if let Some(boot_cfg) = boot_config { let mut fdt_helper = FdtBuilder::new(); locked_vm diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index baa305154..7923bf96f 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -425,12 +425,6 @@ impl MachineOps for StdMachine { locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; StdMachine::arch_init()?; - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let mut vcpu_fds = vec![]; - for cpu_id in 0..nr_cpus { - vcpu_fds.push(Arc::new(vm_fd.create_vcpu(cpu_id as u64)?)); - } locked_vm .init_pci_host() @@ -459,7 +453,6 @@ impl MachineOps for StdMachine { vm.clone(), nr_cpus, &topology, - &vcpu_fds, &boot_config, )?); -- Gitee From d961e7c5f905b6cab8335eb1aeebd0e902969d09 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Dec 2022 19:08:02 +0800 Subject: [PATCH 0651/2187] virtio: Fixes for update_config of device hotplug 1. Use default implementation for balloon and scsi. 2. Send config change notification after receiving configuration. Signed-off-by: Keqian Zhu --- virtio/src/balloon.rs | 9 +-------- virtio/src/block.rs | 26 ++++++++++++++++---------- virtio/src/scsi/controller.rs | 6 +----- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 83815a86e..25bcf1e32 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -24,7 +24,7 @@ use crate::report_virtio_error; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, Context, Result}; use log::{error, warn}; use machine_manager::{ config::{BalloonConfig, DEFAULT_VIRTQUEUE_SIZE}, @@ -1043,13 +1043,6 @@ impl VirtioDevice for Balloon { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.deactivate_evts) } - - fn update_config( - &mut self, - _dev_config: Option>, - ) -> Result<()> { - bail!("Unsupported to update configuration") - } } pub fn qmp_balloon(target: u64) -> bool { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 04d47df47..2ca3014aa 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -661,7 +661,8 @@ impl BlockIoHandler { self.direct = direct; self.aio_type = aio_type; } - Err(_) => { + Err(e) => { + error!("Failed to receive config in updating handler {:?}", e); self.disk_sectors = 0; self.disk_image = None; self.serial_num = None; @@ -670,6 +671,20 @@ impl BlockIoHandler { } }; + if let Err(e) = (*self.interrupt_cb)(&VirtioInterruptType::Config, None, false) { + error!( + "{:?}. {:?}", + VirtioError::InterruptTrigger("block", VirtioInterruptType::Config), + e + ); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + return; + } + if let Err(ref e) = self.process_queue() { error!("Failed to handle block IO for updating handler {:?}", e); } @@ -1154,15 +1169,6 @@ impl VirtioDevice for Block { } } - if let Some(interrupt_cb) = &self.interrupt_cb { - interrupt_cb(&VirtioInterruptType::Config, None, false).with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "block", - VirtioInterruptType::Config - )) - })?; - } - Ok(()) } } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 7ee6f3c73..d3910c06c 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -35,7 +35,7 @@ use address_space::{AddressSpace, GuestAddress}; use log::{debug, error, info}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ - config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, + config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; use util::aio::{Aio, AioCb, Iovec}; @@ -344,10 +344,6 @@ impl VirtioDevice for ScsiCntlr { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(self.config.iothread.as_ref(), &mut self.deactivate_evts) } - - fn update_config(&mut self, _dev_config: Option>) -> Result<()> { - Ok(()) - } } fn build_event_notifier(fd: RawFd, handler: Rc) -> EventNotifier { -- Gitee From f2ad15179fdcb0b2a5f228e71c54908145b17b80 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 25 Dec 2022 03:44:50 +0800 Subject: [PATCH 0652/2187] virtio: Put broken status into migration status The broken status must be migrated. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 8 ++++++-- virtio/src/net.rs | 4 ++++ virtio/src/vhost/kernel/vsock.rs | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 2ca3014aa..cd54bb65c 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -889,6 +889,8 @@ pub struct BlockState { pub driver_features: u64, /// Config space of the block device. pub config_space: VirtioBlkConfig, + /// Device broken status. + broken: bool, } /// Block device structure. @@ -1180,13 +1182,15 @@ unsafe impl Sync for Block {} impl StateTransfer for Block { fn get_state_vec(&self) -> migration::Result> { - Ok(self.state.as_bytes().to_vec()) + let mut state = self.state; + state.broken = self.broken.load(Ordering::SeqCst); + Ok(state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *BlockState::from_bytes(state) .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("BLOCK")))?; - + self.broken.store(self.state.broken, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 91d270dd8..68813e42b 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1102,6 +1102,8 @@ pub struct VirtioNetState { pub driver_features: u64, /// Virtio net configurations. pub config_space: VirtioNetConfig, + /// Device broken status. + broken: bool, } /// Network device structure. @@ -1636,6 +1638,7 @@ unsafe impl Sync for Net {} impl StateTransfer for Net { fn get_state_vec(&self) -> migration::Result> { + self.state.lock().unwrap().broken = self.broken.load(Ordering::SeqCst); Ok(self.state.lock().unwrap().as_bytes().to_vec()) } @@ -1646,6 +1649,7 @@ impl StateTransfer for Net { } let mut locked_state = self.state.lock().unwrap(); locked_state.as_mut_bytes().copy_from_slice(state); + self.broken.store(locked_state.broken, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 2c6c03ec1..752ef9ea1 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -84,6 +84,8 @@ pub struct VsockState { config_space: [u8; 8], /// Last avail idx in vsock backend queue. last_avail_idx: [u16; 2], + /// Device broken status. + broken: bool, } /// Vsock device structure. @@ -344,6 +346,7 @@ impl StateTransfer for Vsock { })?; state.last_avail_idx[0] = self.backend.as_ref().unwrap().get_vring_base(0).unwrap(); state.last_avail_idx[1] = self.backend.as_ref().unwrap().get_vring_base(1).unwrap(); + state.broken = self.broken.load(Ordering::SeqCst); migration::Result::with_context(self.backend.as_ref().unwrap().set_running(true), || { "Failed to set vsock backend running" })?; @@ -357,7 +360,7 @@ impl StateTransfer for Vsock { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *VsockState::from_bytes(state) .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("VSOCK")))?; - + self.broken.store(self.state.broken, Ordering::SeqCst); Ok(()) } -- Gitee From 5840582fb556881cf6e6907f92256bfd8c86a655 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 25 Dec 2022 05:30:03 +0800 Subject: [PATCH 0653/2187] virtio-queue: fix arithmetical overflow checking 1. Unwrap is unsafe, should return error if overflow. 2. Chained desc number may overflow. 3. Add comment for virtio-blk about overflow risk. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 7 +++++++ virtio/src/virtqueue/split.rs | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index cd54bb65c..c69818562 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -208,6 +208,7 @@ impl Request { in_iov_elem.len ); } + // Note: addr plus len has been checked not overflow in virtqueue. let in_header = GuestAddress(in_iov_elem.addr.0 + in_iov_elem.len as u64 - 1); let mut request = Request { @@ -222,6 +223,8 @@ impl Request { // Count in_len before discard iovec. // We always write the last status byte, so count all in_iovs. + // Note: in_iov and out_iov total len is no more than 1<<32, and + // out_iov is more than 1, so in_len will not overflow. for in_iov in elem.in_iovec.iter() { request.in_len += in_iov.len; } @@ -245,6 +248,7 @@ impl Request { iov_len: u64::from(elem_iov.len), }; request.iovec.push(iov); + // Note: elem_iov total len is no more than 1<<32. request.data_len += u64::from(elem_iov.len); } else { bail!("Map desc base {:?} failed", elem_iov.addr); @@ -278,6 +282,8 @@ impl Request { iov_len: iov.iov_len, }; aiocb.iovec.push(iovec); + // Note: total len of each req is no more than 1<<32, + // and reqs count is no more than 1024. aiocb.nbytes += iov.iov_len; } req = req_raw.next.as_ref().as_ref(); @@ -438,6 +444,7 @@ impl BlockIoHandler { && merged_iovs + req_iovs <= MAX_NUM_MERGE_IOVS && merged_bytes + req_bytes <= MAX_NUM_MERGE_BYTES && req_ref.out_header.request_type == req.out_header.request_type + // Note: sector plus sector_num has been checked not overflow. && (req_ref.out_header.sector + req_ref.get_req_sector_num() == req.out_header.sector) } diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 4cbf46cc4..9ed8d30d6 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -222,7 +222,10 @@ impl SplitVringDesc { let base = self.addr.0; let offset = self.len as u64; if base > reg_cache.start && base + offset < reg_cache.end { - base.checked_add(offset).unwrap(); + if base.checked_add(offset).is_none() { + error!("The memory of descriptor is invalid, range overflows"); + return false; + } miss_cached = false; } } else { @@ -337,7 +340,10 @@ impl SplitVringDesc { }; queue_size = desc.get_desc_num(); desc = Self::next_desc(sys_mem, desc_table_host, queue_size, 0, cache)?; - desc_size = elem.desc_num + queue_size; + desc_size = elem + .desc_num + .checked_add(queue_size) + .ok_or_else(|| anyhow!("The chained desc number overflows"))?; } let iovec = ElemIovec { -- Gitee From 5af20a2ee9bf61b7a2f4f0ba0bcac8d9cdb7f29d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 26 Dec 2022 03:38:20 +0800 Subject: [PATCH 0654/2187] Fix: Delete the code related to the Ramfb configuration The code of the Ramfb configuration just are used to convey an all-zero vector with a definite size of the firmware. They are unnecessary code. So delete them and pass an all-zero vector with the size of the Ramfb configuration to fwcfg directly. Signed-off-by: Jinhao Gao --- devices/src/legacy/ramfb.rs | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 10b2cec63..10e651613 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -17,12 +17,10 @@ use address_space::{AddressSpace, GuestAddress}; use anyhow::Context; use drm_fourcc::DrmFourcc; use log::error; -use migration_derive::ByteCode; use std::mem::size_of; use std::rc::Rc; use std::sync::{Arc, Mutex}; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; -use util::byte_code::ByteCode; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; use vnc::console::{ console_init, display_graphic_update, display_replace_surface, get_console_by_id, @@ -33,36 +31,19 @@ const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; const HEIGHT_MAX: u32 = 12_000; -#[allow(dead_code)] #[repr(packed)] -#[derive(ByteCode, Clone, Copy)] struct RamfbCfg { - addr: u64, - fourcc: u32, - flags: u32, - width: u32, - height: u32, - stride: u32, -} - -impl RamfbCfg { - pub fn new() -> Self { - Self { - ..Default::default() - } - } -} - -impl AmlBuilder for RamfbCfg { - fn aml_bytes(&self) -> Vec { - self.as_bytes().to_vec() - } + _addr: u64, + _fourcc: u32, + _flags: u32, + _width: u32, + _height: u32, + _stride: u32, } #[derive(Clone)] pub struct RamfbState { pub surface: Option, - cfg: RamfbCfg, sys_mem: Arc, } @@ -73,7 +54,6 @@ impl RamfbState { pub fn new(sys_mem: Arc) -> Self { Self { surface: None, - cfg: RamfbCfg::new(), sys_mem, } } @@ -81,10 +61,11 @@ impl RamfbState { pub fn setup(&mut self, fw_cfg: &Arc>) -> Result<()> { let mut locked_fw_cfg = fw_cfg.lock().unwrap(); let ramfb_state_cb = self.clone(); + let cfg: Vec = [0; size_of::()].to_vec(); locked_fw_cfg .add_file_callback_entry( "etc/ramfb", - self.cfg.clone().aml_bytes(), + cfg, None, Some(Arc::new(Mutex::new(ramfb_state_cb))), true, @@ -137,7 +118,6 @@ impl RamfbState { fn reset_ramfb_state(&mut self) { self.surface = None; - self.cfg = RamfbCfg::new(); } } -- Gitee From 1f14d1d865410b3929b576c4cfa0064f73f25293 Mon Sep 17 00:00:00 2001 From: Wu Binfeng Date: Sat, 24 Dec 2022 23:17:01 +0800 Subject: [PATCH 0655/2187] bugfix: vm with virtio-gpu starts slowly in the uefi phase Edk2 keep sending reqs to virtio-gpu, and sync wait response by stall cpu. If we process reqs a little slower, there will be a very noticeable delay. We have enabled the general features related to EVENT_IDX and refactor a particularly time-consuming req. Signed-off-by: Binfeng Wu --- virtio/src/gpu.rs | 294 +++++++++++++++++++++++++++++----------------- 1 file changed, 186 insertions(+), 108 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index b32ff8783..b90ddced1 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -11,13 +11,13 @@ // See the Mulan PSL v2 for more details. use super::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_GPU, + Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU, }; use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; -use log::error; +use log::{error, warn}; use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc}; @@ -1194,6 +1194,161 @@ impl GpuIoHandler { } } + fn gpu_cmd_transfer_to_host_2d_params_check( + &mut self, + info_transfer: &VirtioGpuTransferToHost2d, + ) -> u32 { + let res_idx = self + .resources_list + .iter() + .position(|x| x.resource_id == info_transfer.resource_id); + + if res_idx.is_none() { + error!( + "The resource_id {} in transfer to host 2d request is not existed", + info_transfer.resource_id + ); + return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + } + + let res = &self.resources_list[res_idx.unwrap()]; + if res.iov.is_empty() { + error!( + "The resource_id {} in transfer to host 2d request don't have iov", + info_transfer.resource_id + ); + return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + } + + if info_transfer.rect.x_coord > res.width + || info_transfer.rect.y_coord > res.height + || info_transfer.rect.width > res.width + || info_transfer.rect.height > res.height + || info_transfer.rect.width + info_transfer.rect.x_coord > res.width + || info_transfer.rect.height + info_transfer.rect.y_coord > res.height + { + error!( + "The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {})", + res.resource_id, + res.width, + res.height, + info_transfer.offset, + info_transfer.rect.width, + info_transfer.rect.height, + info_transfer.rect.x_coord, + info_transfer.rect.y_coord, + ); + return VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + } + + 0 + } + + fn gpu_cmd_transfer_to_host_2d_update_resource( + &mut self, + info_transfer: &VirtioGpuTransferToHost2d, + ) { + let res = self + .resources_list + .iter() + .find(|&x| x.resource_id == info_transfer.resource_id) + .unwrap(); + let pixman_format; + let bpp; + let stride; + let data; + unsafe { + pixman_format = pixman_image_get_format(res.pixman_image); + bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + stride = pixman_image_get_stride(res.pixman_image); + data = pixman_image_get_data(res.pixman_image); + } + let data_cast: *mut u8 = data.cast(); + let mut dst_ofs: usize = (info_transfer.rect.y_coord * stride as u32 + + info_transfer.rect.x_coord * bpp) as usize; + // It can be considered that PARTIAL or complete image data is stored in + // the res.iov[]. And info_transfer.offset is the offset from res.iov[0].base + // to the start position of the resource we really want to update. + let mut src_ofs: usize = info_transfer.offset as usize; + // current iov's offset + let mut iov_idx: usize = 0; + // current iov's offset + let mut iov_ofs: usize = 0; + let mut iov_len_sum: usize = 0; + + // move to correct iov + loop { + if iov_len_sum == src_ofs || iov_idx >= res.iov.len() { + break; + } + + if res.iov[iov_idx].iov_len as usize + iov_len_sum <= src_ofs { + iov_len_sum += res.iov[iov_idx].iov_len as usize; + iov_idx += 1; + } else { + iov_ofs = src_ofs - iov_len_sum; + break; + } + } + + if iov_idx >= res.iov.len() { + warn!("data from guest is shorted than expected"); + return; + } + + // we divide regions into that need to be copied and can be skipped + let src_cpy_section: usize = (info_transfer.rect.width * bpp) as usize; + let src_expected: usize = info_transfer.offset as usize + + ((info_transfer.rect.height - 1) * stride as u32) as usize + + (info_transfer.rect.width * bpp) as usize; + + loop { + if src_ofs >= src_expected || iov_idx >= res.iov.len() { + break; + } + + let iov_left = res.iov[iov_idx].iov_len as usize - iov_ofs; + + let pos = (src_ofs - info_transfer.offset as usize) % (stride as usize); + if pos >= src_cpy_section { + if pos + iov_left <= stride as usize { + src_ofs += iov_left; + dst_ofs += iov_left; + iov_idx += 1; + iov_ofs = 0; + } else { + src_ofs += stride as usize - pos; + dst_ofs += stride as usize - pos; + iov_ofs += stride as usize - pos; + } + } else if pos + iov_left <= src_cpy_section { + unsafe { + ptr::copy( + (res.iov[iov_idx].iov_base as *const u8).add(iov_ofs), + data_cast.add(dst_ofs), + iov_left, + ); + } + src_ofs += iov_left; + dst_ofs += iov_left; + iov_idx += 1; + iov_ofs = 0; + } else { + // pos + iov_left > src_cpy_section + unsafe { + ptr::copy( + (res.iov[iov_idx].iov_base as *const u8).add(iov_ofs), + data_cast.add(dst_ofs), + src_cpy_section - pos, + ); + } + src_ofs += src_cpy_section - pos; + dst_ofs += src_cpy_section - pos; + iov_ofs += src_cpy_section - pos; + } + } + } + fn gpu_cmd_transfer_to_host_2d( &mut self, need_interrupt: &mut bool, @@ -1210,102 +1365,15 @@ impl GpuIoHandler { )) })?; - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_transfer.resource_id) - { - let res = &self.resources_list[res_index]; - if res.iov.is_empty() { - error!( - "The resource_id {} in transfer to host 2d request don't have iov", - info_transfer.resource_id - ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - req, - ); - } - - if info_transfer.rect.x_coord > res.width - || info_transfer.rect.y_coord > res.height - || info_transfer.rect.width > res.width - || info_transfer.rect.height > res.height - || info_transfer.rect.width + info_transfer.rect.x_coord > res.width - || info_transfer.rect.height + info_transfer.rect.y_coord > res.height - { - error!( - "The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {})", - res.resource_id, - res.width, - res.height, - info_transfer.offset, - info_transfer.rect.width, - info_transfer.rect.height, - info_transfer.rect.x_coord, - info_transfer.rect.y_coord, - ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - req, - ); - } - - unsafe { - let pixman_format = pixman_image_get_format(res.pixman_image); - let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; - let stride = pixman_image_get_stride(res.pixman_image); - let height = pixman_image_get_height(res.pixman_image); - let width = pixman_image_get_width(res.pixman_image); - let data = pixman_image_get_data(res.pixman_image); - let data_cast: *mut u8 = data.cast(); - - let mut iovec_buf = Vec::new(); - for item in res.iov.iter() { - let mut dst = Vec::new(); - let src = std::slice::from_raw_parts( - item.iov_base as *const u8, - item.iov_len as usize, - ); - dst.resize(item.iov_len as usize, 0); - dst[0..item.iov_len as usize].copy_from_slice(src); - iovec_buf.append(&mut dst); - } - - if info_transfer.offset != 0 - || info_transfer.rect.x_coord != 0 - || info_transfer.rect.y_coord != 0 - || info_transfer.rect.width != width as u32 - { - for h in 0..info_transfer.rect.height { - let offset_iov = info_transfer.offset as u32 + stride as u32 * h; - let offset_data = (info_transfer.rect.y_coord + h) * stride as u32 - + (info_transfer.rect.x_coord * bpp); - ptr::copy( - iovec_buf.as_ptr().offset(offset_iov as isize), - data_cast.offset(offset_data as isize), - info_transfer.rect.width as usize * bpp as usize, - ); - } - } else { - ptr::copy( - iovec_buf.as_ptr(), - data_cast, - stride as usize * height as usize, - ); - } - } - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) - } else { - error!( - "The resource_id {} in transfer to host 2d request is not existed", - info_transfer.resource_id - ); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + let errcode = self.gpu_cmd_transfer_to_host_2d_params_check(&info_transfer); + if errcode != 0 { + return self.gpu_response_nodata(need_interrupt, errcode, req); } + + self.gpu_cmd_transfer_to_host_2d_update_resource(&info_transfer); + self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) } + fn gpu_cmd_resource_attach_backing( &mut self, need_interrupt: &mut bool, @@ -1432,6 +1500,7 @@ impl GpuIoHandler { self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } + fn process_control_queue(&mut self, mut req_queue: Vec) -> Result<()> { for req in req_queue.iter_mut() { let mut need_interrupt = true; @@ -1472,17 +1541,22 @@ impl GpuIoHandler { } if need_interrupt { - (self.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&self.ctrl_queue.lock().unwrap()), - false, - ) - .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "gpu", - VirtioInterruptType::Vring - )) - })?; + let mut queue_lock = self.ctrl_queue.lock().unwrap(); + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + if let Err(e) = (*self.interrupt_cb.as_ref())( + &VirtioInterruptType::Vring, + Some(&queue_lock), + false, + ) { + error!( + "Failed to trigger interrupt(aio completion) for gpu device, error is {:?}", + e + ); + } + } } } @@ -1687,6 +1761,7 @@ impl Default for Gpu { impl Gpu { pub fn new(gpu_conf: GpuConfig) -> Gpu { let mut state = GpuState::default(); + state.base_conf.xres = gpu_conf.xres; state.base_conf.yres = gpu_conf.yres; if gpu_conf.edid { @@ -1694,6 +1769,9 @@ impl Gpu { } state.base_conf.max_outputs = gpu_conf.max_outputs; state.device_features = 1u64 << VIRTIO_F_VERSION_1; + state.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; + state.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; + Self { gpu_conf, state, -- Gitee From ff4ceebd9a3b3d1d9a05363810b40160089a2f55 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 26 Dec 2022 16:52:47 +0800 Subject: [PATCH 0656/2187] init: optimise: use method rather than associated function for mch&ich9 use method rather than associated function for mch&ich9. --- machine/src/standard_vm/x86_64/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index baa305154..4a9b30c81 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -249,7 +249,7 @@ impl StdMachine { .with_context(|| "Fail to register reset event in LPC")?; self.register_acpi_shutdown_event(&ich.shutdown_req, clone_vm) .with_context(|| "Fail to register shutdown event in LPC")?; - PciDevOps::realize(ich)?; + ich.realize()?; Ok(()) } } @@ -284,7 +284,7 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to register CONFIG_DATA port in I/O space.")?; let mch = Mch::new(root_bus, mmconfig_region, mmconfig_region_ops); - PciDevOps::realize(mch)?; + mch.realize()?; Ok(()) } -- Gitee From aad6f1b5182a1b29e264eca7648c970e2491a38c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 26 Dec 2022 19:21:31 +0800 Subject: [PATCH 0657/2187] Fix: Delete _PRT object in PciHost As described in Section 6.2.13 of ACPI specification, the _PRT object provides a mapping from PCI interrupt pins to the interrupt inputs of the interrupt controllers. Currently, StratoVirt PCIe supports only MSI/MSI-X and does not require _PRT object. Signed-off-by: Mingwang Li --- pci/src/host.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/pci/src/host.rs b/pci/src/host.rs index f244411d8..93dca1e0d 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -13,11 +13,10 @@ use std::sync::{Arc, Mutex}; use acpi::{ - AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlByte, AmlCacheable, AmlCreateDWordField, - AmlDWord, AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, - AmlInteger, AmlLNot, AmlLocal, AmlMethod, AmlName, AmlNameDecl, AmlOr, AmlPackage, - AmlReadAndWrite, AmlResTemplate, AmlReturn, AmlScopeBuilder, AmlStore, AmlToUuid, AmlWordDesc, - AmlZero, + AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlCacheable, AmlCreateDWordField, + AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, AmlInteger, + AmlLNot, AmlLocal, AmlMethod, AmlName, AmlNameDecl, AmlOr, AmlReadAndWrite, AmlResTemplate, + AmlReturn, AmlScopeBuilder, AmlStore, AmlToUuid, AmlWordDesc, AmlZero, }; #[cfg(target_arch = "x86_64")] use acpi::{AmlIoDecode, AmlIoResource}; @@ -455,20 +454,6 @@ impl AmlBuilder for PciHost { )); pci_host_bridge.append_child(AmlNameDecl::new("_CRS", crs)); - // Build and append pci-routing-table to PCI host bridge node. - let slot_num = 32_u8; - let mut prt_pkg = AmlPackage::new(slot_num); - let pci_irq_base = 16_u32; - (0..slot_num).for_each(|slot| { - let mut pkg = AmlPackage::new(4); - pkg.append_child(AmlDWord(((slot as u32) << 16) as u32 | 0xFFFF)); - pkg.append_child(AmlByte(0)); - pkg.append_child(AmlByte(0)); - pkg.append_child(AmlDWord(pci_irq_base + (slot as u32 % 8))); - prt_pkg.append_child(pkg); - }); - pci_host_bridge.append_child(AmlNameDecl::new("_PRT", prt_pkg)); - pci_host_bridge.aml_bytes() } } -- Gitee From 170edf0e5c1a6d3a3cc02c64553cb6cad4e09edf Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 26 Dec 2022 16:16:37 +0800 Subject: [PATCH 0658/2187] util: fix num parsing logic The value of addr/ports for devices may be decimal or heximal, it starts with 0x/0X for heximal and no prefix for decimal. It treated all numbers as heximal. Fix that by introducing a new pub used func str_to_usize(). --- machine/src/micro_vm/mod.rs | 18 +++++++++---- machine_manager/src/config/pci.rs | 23 +++++++++------- util/src/num_ops.rs | 45 +++++++++++++++++++++++++++++++ virtio/src/net.rs | 5 ++-- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 133b78251..713d08b49 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -83,7 +83,9 @@ use sysbus::{SysBusDevType, SysRes}; use syscall::syscall_whitelist; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, CompileFDT, FdtBuilder}; -use util::{loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode}; +use util::{ + loop_context::EventLoopManager, num_ops::str_to_usize, seccomp::BpfRule, set_termi_canon_mode, +}; use virtio::{ create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, @@ -1053,10 +1055,16 @@ impl DeviceInterface for LightMachine { // get slot of bus by addr or lun let mut slot = 0; if let Some(addr) = args.addr { - let slot_str = addr.as_str().trim_start_matches("0x"); - - if let Ok(n) = usize::from_str_radix(slot_str, 16) { - slot = n; + if let Ok(num) = str_to_usize(addr) { + slot = num; + } else { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!( + "Invalid addr for device {}", + args.id + )), + None, + ); } } else if let Some(lun) = args.lun { slot = lun + 1; diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 5df10e670..0d74c6667 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize}; use super::error::ConfigError; use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::ExBool; +use util::num_ops::str_to_usize; /// Basic information of pci devices such as bus number, /// slot number and function number. @@ -81,17 +82,17 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { addr_vec.len() ); } + let slot = addr_vec.first().unwrap(); - let without_prefix = slot.trim_start_matches("0x"); - let slot = u8::from_str_radix(without_prefix, 16) - .with_context(|| format!("Invalid slot num: {}", slot))?; + let slot = + str_to_usize(slot.to_string()).with_context(|| format!("Invalid slot num: {}", slot))?; if slot > 31 { bail!("Invalid slot num: {}", slot); } + let func = if addr_vec.get(1).is_some() { let function = addr_vec.get(1).unwrap(); - let without_prefix = function.trim_start_matches("0x"); - u8::from_str_radix(without_prefix, 16) + str_to_usize(function.to_string()) .with_context(|| format!("Invalid function num: {}", function))? } else { 0 @@ -99,7 +100,8 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { if func > 7 { bail!("Invalid function num: {}", func); } - Ok((slot, func)) + + Ok((slot as u8, func as u8)) } pub fn get_pci_bdf(pci_cfg: &str) -> Result { @@ -149,12 +151,13 @@ pub fn parse_root_port(rootport_cfg: &str) -> Result { cmd_parser.parse(rootport_cfg)?; let mut root_port = RootPortConfig::default(); - if let Some(port) = cmd_parser.get_value::("port")? { - let without_prefix = port.trim_start_matches("0x"); - root_port.port = u8::from_str_radix(without_prefix, 16).unwrap(); - } else { + let port = cmd_parser.get_value::("port")?; + if port.is_none() { return Err(anyhow!(ConfigError::FieldIsMissing("port", "rootport"))); } + // Safety: as port is validated non-none at the previous line, it's safe to unwrap() it + root_port.port = str_to_usize(port.unwrap())? as u8; + let _ = cmd_parser.get_value::("chassis")?; if let Some(id) = cmd_parser.get_value::("id")? { diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 11f4a59f2..a84b52a92 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -10,8 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::error; + // This module implements some operations of Rust primitive types. /// Calculate the aligned-up u64 value. @@ -380,6 +382,39 @@ pub fn read_data_u16(data: &[u8], value: &mut u16) -> bool { true } +/// Parse a string to a number, decimal and heximal numbers supported now. +/// +/// # Arguments +/// +/// * `string_in` - The string that means a number, eg. "18", "0x1c". +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::str_to_usize; +/// +/// let value = str_to_usize("0x17".to_string()).unwrap(); +/// assert!(value == 0x17); +/// let value = str_to_usize("0X17".to_string()).unwrap(); +/// assert!(value == 0x17); +/// let value = str_to_usize("17".to_string()).unwrap(); +/// assert!(value == 17); +/// ``` +pub fn str_to_usize(string_in: String) -> Result { + let mut base = 10; + if string_in.starts_with("0x") || string_in.starts_with("0X") { + base = 16; + } + let without_prefix = string_in + .trim() + .trim_start_matches("0x") + .trim_start_matches("0X"); + let num = usize::from_str_radix(without_prefix, base) + .with_context(|| format!("Invalid num: {}", string_in))?; + Ok(num) +} + #[cfg(test)] mod test { use super::*; @@ -532,4 +567,14 @@ mod test { let ret = read_data_u32(&[0x11, 0x22, 0x33, 0x44], &mut value); assert!(ret && value == 0x44332211); } + + #[test] + fn test_str_to_usize() { + let value = str_to_usize("0x17".to_string()).unwrap(); + assert!(value == 0x17); + let value = str_to_usize("0X17".to_string()).unwrap(); + assert!(value == 0x17); + let value = str_to_usize("17".to_string()).unwrap(); + assert!(value == 17); + } } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 044bbf86c..ea63157e9 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -54,7 +54,7 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; +use util::num_ops::{read_u32, str_to_usize}; use util::tap::{ Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, }; @@ -1310,8 +1310,7 @@ fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> { let is_mq = queue_pair > 1; let ifr_flag = fs::read_to_string(tap_path) .with_context(|| "Failed to read content from tun_flags file")?; - let flags = u16::from_str_radix(ifr_flag.trim().trim_start_matches("0x"), 16) - .with_context(|| "Failed to parse tap ifr flag")?; + let flags = str_to_usize(ifr_flag)? as u16; if (flags & IFF_MULTI_QUEUE != 0) && !is_mq { bail!(format!( "Tap device supports mq, but command set queue pairs {}.", -- Gitee From f46a894f7251eb3e6139361484d3a8781ea4f193 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Fri, 23 Dec 2022 09:49:52 +0800 Subject: [PATCH 0659/2187] memory: fix the PartialEq function for FlatRange Equality between two FlatRange instances requires that all members be equal. The starting address and size of the addr_range also need to be equal. Otherwise, the following code will cause bugs: if old_r.addr_range.base < new_r.addr_range.base || (old_r.addr_range.base == new_r.addr_range.base && old_r != new_r) { if !is_add { self.call_listeners(Some(old_r), None, ListenerReqType::DeleteRegion)?; } old_idx += 1; continue; } If the flatrange only changes in size, the old flatrange will not be deleted. Signed-off-by: Li HuaChao --- address_space/src/address_space.rs | 2 +- address_space/src/region.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index adeed20b4..253c063a1 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -235,7 +235,7 @@ impl AddressSpace { } old_idx += 1; continue; - } else if old_r.addr_range == new_r.addr_range && old_r == new_r { + } else if old_r == new_r { old_idx += 1; new_idx += 1; continue; diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 49659a8bf..3422dd5a8 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -166,7 +166,7 @@ impl Eq for FlatRange {} impl PartialEq for FlatRange { fn eq(&self, other: &Self) -> bool { - self.addr_range.base == other.addr_range.base + self.addr_range == other.addr_range && self.owner.region_type == other.owner.region_type && self.rom_dev_romd.unwrap_or(false) == other.rom_dev_romd.unwrap_or(false) && self.owner == other.owner -- Gitee From b59323a065235afbec08dfac477589e7a9c2c780 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 26 Dec 2022 20:16:24 +0800 Subject: [PATCH 0660/2187] xhci: remove update_intr Remove the unused update_intr. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 32 ++++++-------------------------- usb/src/xhci/xhci_pci.rs | 2 -- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index c8d053d15..894dc6b5b 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -376,8 +376,6 @@ impl XhciEvent { /// Controller ops registered in XhciDevice. Such as PCI device send MSIX. pub trait XhciOps: Send + Sync { fn trigger_intr(&mut self, n: u32, level: bool) -> bool; - - fn update_intr(&mut self, n: u32, enable: bool); } /// Input Control Context. See the spec 6.2.5 Input Control Context. @@ -1681,34 +1679,16 @@ impl XhciDevice { } pub fn update_intr(&mut self, v: u32) { - let mut level = false; - if v == 0 { - if self.intrs[0].iman & IMAN_IP == IMAN_IP - && self.intrs[0].iman & IMAN_IE == IMAN_IE - && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE - { - level = true; - } + if self.intrs[v as usize].iman & IMAN_IP == IMAN_IP + && self.intrs[v as usize].iman & IMAN_IE == IMAN_IE + && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE + { if let Some(ops) = &self.ctrl_ops { - if ops - .upgrade() - .unwrap() - .lock() - .unwrap() - .trigger_intr(0, level) - { - self.intrs[0].iman &= !IMAN_IP; + if ops.upgrade().unwrap().lock().unwrap().trigger_intr(v, true) { + self.intrs[v as usize].iman &= !IMAN_IP; } } } - - if let Some(ops) = &self.ctrl_ops { - ops.upgrade() - .unwrap() - .lock() - .unwrap() - .update_intr(v, self.intrs[0].iman & IMAN_IE == IMAN_IE); - } } /// Assign USB port and attach the device. diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index d1ec2f7e1..6411ad7da 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -292,6 +292,4 @@ impl XhciOps for XhciPciDevice { } false } - - fn update_intr(&mut self, _n: u32, _enable: bool) {} } -- Gitee From 0f7e53b26f89eef552140b46debfc728aba1f0c7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 26 Dec 2022 20:50:42 +0800 Subject: [PATCH 0661/2187] xhci: remove XhciOps Remove XhciOps, and the register interrupt function no longer references the pci device. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 24 +++++++----------------- usb/src/xhci/xhci_pci.rs | 33 ++++++++++++--------------------- 2 files changed, 19 insertions(+), 38 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 894dc6b5b..6ec6c06d8 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -373,11 +373,6 @@ impl XhciEvent { } } -/// Controller ops registered in XhciDevice. Such as PCI device send MSIX. -pub trait XhciOps: Send + Sync { - fn trigger_intr(&mut self, n: u32, level: bool) -> bool; -} - /// Input Control Context. See the spec 6.2.5 Input Control Context. #[repr(C)] #[derive(Debug, Default, Clone, Copy)] @@ -445,7 +440,7 @@ pub struct XhciDevice { pub intrs: Vec, pub cmd_ring: XhciRing, mem_space: Arc, - pub ctrl_ops: Option>>, + pub send_interrupt_ops: Option>, } impl XhciDevice { @@ -466,7 +461,7 @@ impl XhciDevice { } let xhci = XhciDevice { oper: XchiOperReg::new(), - ctrl_ops: None, + send_interrupt_ops: None, usb_ports: Vec::new(), numports_3: p3, numports_2: p2, @@ -1668,12 +1663,8 @@ impl XhciDevice { return; } - if let Some(ops) = self.ctrl_ops.as_ref() { - ops.upgrade() - .unwrap() - .lock() - .unwrap() - .trigger_intr(idx, true); + if let Some(intr_ops) = self.send_interrupt_ops.as_ref() { + intr_ops(idx); self.intrs[idx as usize].iman &= !IMAN_IP; } } @@ -1683,10 +1674,9 @@ impl XhciDevice { && self.intrs[v as usize].iman & IMAN_IE == IMAN_IE && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE { - if let Some(ops) = &self.ctrl_ops { - if ops.upgrade().unwrap().lock().unwrap().trigger_intr(v, true) { - self.intrs[v as usize].iman &= !IMAN_IP; - } + if let Some(intr_ops) = &self.send_interrupt_ops { + intr_ops(v); + self.intrs[v as usize].iman &= !IMAN_IP; } } } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 6411ad7da..c7881a30c 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressSpace, Region}; -use log::{debug, error}; +use log::debug; use machine_manager::config::XhciConfig; use pci::config::{ PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, @@ -25,7 +25,7 @@ use pci::msix::update_dev_id; use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; use crate::usb::UsbDeviceOps; -use crate::xhci::xhci_controller::{XhciDevice, XhciOps, MAX_INTRS, MAX_SLOTS}; +use crate::xhci::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; use crate::xhci::xhci_regs::{ build_cap_ops, build_doorbell_ops, build_oper_ops, build_port_ops, build_runtime_ops, XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME, @@ -221,10 +221,17 @@ impl PciDevOps for XhciPciDevice { )?; let devfn = self.devfn; + // It is safe to unwrap, because it is initialized in init_msix. + let cloned_msix = self.pci_config.msix.as_ref().unwrap().clone(); + let cloned_dev_id = self.dev_id.clone(); + // Registers the msix to the xhci device for interrupt notification. + self.xhci.lock().unwrap().send_interrupt_ops = Some(Box::new(move |n: u32| { + cloned_msix + .lock() + .unwrap() + .notify(n as u16, cloned_dev_id.load(Ordering::Acquire)); + })); let dev = Arc::new(Mutex::new(self)); - // Register xhci-pci to xhci-device for notify. - dev.lock().unwrap().xhci.lock().unwrap().ctrl_ops = - Some(Arc::downgrade(&dev) as Weak>); // Attach to the PCI bus. let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); let mut locked_pci_bus = pci_bus.lock().unwrap(); @@ -277,19 +284,3 @@ impl PciDevOps for XhciPciDevice { Ok(()) } } - -impl XhciOps for XhciPciDevice { - fn trigger_intr(&mut self, n: u32, trigger: bool) -> bool { - if let Some(msix) = self.pci_config.msix.as_mut() { - if trigger { - msix.lock() - .unwrap() - .notify(n as u16, self.dev_id.load(Ordering::Acquire)); - return true; - } - } else { - error!("Failed to send interrupt: msix does not exist"); - } - false - } -} -- Gitee From a4b75cfc0c7cfda5f56cfc6c25b96afb4a17668e Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 28 Dec 2022 09:23:17 +0800 Subject: [PATCH 0662/2187] migration: marking device dirty pages only if migration is active If migration is not active, there is no need to get virtqueue address to mark dirty pages. So, check the live migration is enable before access to address. Signed-off-by: Xinle.Guo --- virtio/src/block.rs | 4 +++- virtio/src/net.rs | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 8dbf38789..08d68baf8 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -288,7 +288,9 @@ impl Request { } let request_type = self.out_header.request_type; - if request_type == VIRTIO_BLK_T_IN || request_type == VIRTIO_BLK_T_GET_ID { + if MigrationManager::is_active() + && (request_type == VIRTIO_BLK_T_IN || request_type == VIRTIO_BLK_T_GET_ID) + { // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. for iov in aiocb.iovec.iter() { // Mark vmm dirty page manually if live migration is active. diff --git a/virtio/src/net.rs b/virtio/src/net.rs index ea63157e9..c0551a115 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -788,10 +788,12 @@ impl NetIoHandler { ) .with_context(|| "Failed to get libc iovecs for net rx")?; - // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. - for iov in iovecs.iter() { - // Mark vmm dirty page manually if live migration is active. - MigrationManager::mark_dirty_log(iov.iov_base as u64, iov.iov_len as u64); + if MigrationManager::is_active() { + // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. + for iov in iovecs.iter() { + // Mark vmm dirty page manually if live migration is active. + MigrationManager::mark_dirty_log(iov.iov_base as u64, iov.iov_len as u64); + } } // Read the data from the tap device. -- Gitee From 43b43f1697565d8c0a6d9f7a0215222d598879ee Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 24 Dec 2022 15:28:46 +0800 Subject: [PATCH 0663/2187] usb: refactor usb descriptor Refactor usb descriptor, put the descriptor-related fields into the UsbDescriptor structure. And remove the UsbDesc structure, use the UsbDescDevice directly. Signed-off-by: zhouli57 --- usb/src/descriptor.rs | 302 ++++++++++++++++++++---------------------- usb/src/hid.rs | 28 ---- usb/src/keyboard.rs | 61 ++++----- usb/src/tablet.rs | 66 ++++----- usb/src/usb.rs | 110 +++------------ 5 files changed, 226 insertions(+), 341 deletions(-) diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index 471167bd4..1b12f07e3 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -13,11 +13,14 @@ use std::sync::Arc; use anyhow::{bail, Result}; -use log::error; use util::byte_code::ByteCode; use crate::config::*; -use crate::usb::{UsbDescConfig, UsbDescEndpoint, UsbDescIface, UsbDevice}; +use crate::usb::UsbDevice; + +const USB_MAX_INTERFACES: u32 = 16; +const USB_DESCRIPTOR_TYPE_SHIFT: u32 = 8; +const USB_DESCRIPTOR_INDEX_MASK: u32 = 0xff; /// USB device descriptor for transfer #[allow(non_snake_case)] @@ -122,54 +125,57 @@ struct UsbStringDescriptor { impl ByteCode for UsbStringDescriptor {} -/// USB descriptor ops including get/set descriptor. -pub trait UsbDescriptorOps { - fn get_descriptor(&self, value: u32) -> Result>; - - fn get_device_descriptor(&self) -> Result>; - - fn get_config_descriptor(&self, conf: &UsbDescConfig) -> Result>; - - fn get_interface_descriptor(&self, iface: &UsbDescIface) -> Result>; - - fn get_endpoint_descriptor(&self, ep: &UsbDescEndpoint) -> Result>; - - fn get_string_descriptor(&self, index: u32) -> Result>; - - fn set_config_descriptor(&mut self, v: u8) -> Result<()>; - - fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()>; - - fn find_interface(&self, nif: u32, alt: u32) -> Option>; +/// USB device descriptor. +pub struct UsbDescDevice { + pub device_desc: UsbDeviceDescriptor, + pub configs: Vec>, +} - fn init_endpoint(&mut self) -> Result<()>; +/// USB config descriptor. +pub struct UsbDescConfig { + pub config_desc: UsbConfigDescriptor, + pub interfaces: Vec>, +} - fn set_default_descriptor(&mut self) -> Result<()>; +/// USB interface descriptor. +pub struct UsbDescIface { + pub interface_desc: UsbInterfaceDescriptor, + pub other_desc: Vec>, + pub endpoints: Vec>, +} - fn init_descriptor(&mut self) -> Result<()>; +/// USB other descriptor. +pub struct UsbDescOther { + pub length: u8, + pub data: Vec, } -impl UsbDescriptorOps for UsbDevice { - fn get_descriptor(&self, value: u32) -> Result> { - let desc_type = value >> 8; - let index = value & 0xff; +/// USB endpoint descriptor. +pub struct UsbDescEndpoint { + pub endpoint_desc: UsbEndpointDescriptor, + pub extra: Option>, +} - let device_desc = if let Some(desc) = self.device_desc.as_ref() { - desc - } else { - bail!("Device descriptor not found"); - }; +/// USB Descriptor. +pub struct UsbDescriptor { + pub device_desc: Option>, + pub configuration_selected: Option>, + pub interfaces: Vec>>, + pub altsetting: Vec, + pub interface_number: u32, + pub strings: Vec, +} - let conf = &device_desc.as_ref().confs; - let vec = match desc_type as u8 { - USB_DT_DEVICE => self.get_device_descriptor()?, - USB_DT_CONFIGURATION => self.get_config_descriptor(conf[index as usize].as_ref())?, - USB_DT_STRING => self.get_string_descriptor(index)?, - _ => { - bail!("Unknown descriptor type {}", desc_type); - } - }; - Ok(vec) +impl UsbDescriptor { + pub fn new() -> Self { + Self { + device_desc: None, + configuration_selected: None, + interfaces: vec![None; USB_MAX_INTERFACES as usize], + altsetting: vec![0; USB_MAX_INTERFACES as usize], + interface_number: 0, + strings: Vec::new(), + } } fn get_device_descriptor(&self) -> Result> { @@ -180,12 +186,22 @@ impl UsbDescriptorOps for UsbDevice { } } - fn get_config_descriptor(&self, conf: &UsbDescConfig) -> Result> { + fn get_config_descriptor(&self, index: u32) -> Result> { + let confs = if let Some(desc) = self.device_desc.as_ref() { + &desc.configs + } else { + bail!("Device descriptor not found"); + }; + let conf = if let Some(conf) = confs.get(index as usize) { + conf + } else { + bail!("Config descriptor index {} is invalid", index); + }; let mut config_desc = conf.config_desc; let mut total = config_desc.bLength as u16; let mut ifs = Vec::new(); - for i in 0..conf.ifs.len() { - let mut iface = self.get_interface_descriptor(conf.ifs[i].as_ref())?; + for i in 0..conf.interfaces.len() { + let mut iface = self.get_interface_descriptor(conf.interfaces[i].as_ref())?; total += iface.len() as u16; ifs.append(&mut iface); } @@ -205,7 +221,7 @@ impl UsbDescriptorOps for UsbDevice { } } for i in 0..desc.bNumEndpoints as usize { - let mut ep = self.get_endpoint_descriptor(iface.eps[i].as_ref())?; + let mut ep = self.get_endpoint_descriptor(iface.endpoints[i].as_ref())?; buf.append(&mut ep); } Ok(buf) @@ -222,20 +238,11 @@ impl UsbDescriptorOps for UsbDevice { let str: [u8; 4] = [4, 3, 9, 4]; return Ok(str.to_vec()); } - let mut found_str = String::new(); - for str in &self.strings { - if str.index == index { - found_str = str.str.clone(); - break; - } - } - if found_str.is_empty() { - found_str = if let Some(desc) = self.usb_desc.as_ref() { - desc.strings[index as usize].clone() - } else { - bail!("No usb desc found."); - } - } + let found_str = if let Some(str) = self.strings.get(index as usize) { + str + } else { + bail!("String descriptor index {} is invalid", index); + }; let len = found_str.len() as u8 * 2 + 2; let mut vec = vec![0_u8; len as usize]; vec[0] = len; @@ -250,144 +257,129 @@ impl UsbDescriptorOps for UsbDevice { Ok(vec) } + fn find_interface(&self, nif: u32, alt: u32) -> Option> { + let conf = self.configuration_selected.as_ref()?; + for i in 0..conf.interfaces.len() { + let iface = conf.interfaces[i].as_ref(); + if iface.interface_desc.bInterfaceNumber == nif as u8 + && iface.interface_desc.bAlternateSetting == alt as u8 + { + return Some(conf.interfaces[i].clone()); + } + } + None + } +} + +impl Default for UsbDescriptor { + fn default() -> Self { + Self::new() + } +} + +/// USB descriptor ops including get/set descriptor. +pub trait UsbDescriptorOps { + /// Get device/configuration/string descriptor. + fn get_descriptor(&self, value: u32) -> Result>; + + /// Set configuration descriptor with the Configuration Value. + fn set_config_descriptor(&mut self, v: u8) -> Result<()>; + + /// Set interface descriptor with the Interface and Alernate Setting. + fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()>; + + /// Init all endpoint descriptors and reset the USB endpoint. + fn init_endpoint(&mut self) -> Result<()>; + + /// Init descriptor with the device descriptor and string descriptors. + fn init_descriptor(&mut self, desc: Arc, str: Vec) -> Result<()>; +} + +impl UsbDescriptorOps for UsbDevice { + fn get_descriptor(&self, value: u32) -> Result> { + let desc_type = value >> USB_DESCRIPTOR_TYPE_SHIFT; + let index = value & USB_DESCRIPTOR_INDEX_MASK; + let vec = match desc_type as u8 { + USB_DT_DEVICE => self.descriptor.get_device_descriptor()?, + USB_DT_CONFIGURATION => self.descriptor.get_config_descriptor(index)?, + USB_DT_STRING => self.descriptor.get_string_descriptor(index)?, + _ => { + bail!("Unknown descriptor type {}", desc_type); + } + }; + Ok(vec) + } + fn set_config_descriptor(&mut self, v: u8) -> Result<()> { if v == 0 { - self.configuration = 0; - self.ninterfaces = 0; - self.config = None; + self.descriptor.interface_number = 0; + self.descriptor.configuration_selected = None; } else { - let desc = if let Some(desc) = &self.device_desc { + let desc = if let Some(desc) = self.descriptor.device_desc.as_ref() { desc } else { - bail!("Device Desc is None."); + bail!("Device Descriptor not found"); }; let num = desc.device_desc.bNumConfigurations; - let desc = desc.as_ref(); for i in 0..num as usize { - if desc.confs[i].config_desc.bConfigurationValue == v { - self.configuration = v as u32; - self.ninterfaces = desc.confs[i].config_desc.bNumInterfaces as u32; - self.config = Some(desc.confs[i].clone()); + if desc.configs[i].config_desc.bConfigurationValue == v { + self.descriptor.interface_number = + desc.configs[i].config_desc.bNumInterfaces as u32; + self.descriptor.configuration_selected = Some(desc.configs[i].clone()); } } } - for i in 0..self.ninterfaces { + for i in 0..self.descriptor.interface_number { self.set_interface_descriptor(i, 0)?; } - for i in self.altsetting.iter_mut() { + for i in self.descriptor.altsetting.iter_mut() { *i = 0; } + for it in self.descriptor.interfaces.iter_mut() { + *it = None; + } Ok(()) } fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()> { - let iface = if let Some(face) = self.find_interface(index, v) { + let iface = if let Some(face) = self.descriptor.find_interface(index, v) { face } else { - bail!("Interface not found."); + bail!("Interface descriptor not found."); }; - self.altsetting[index as usize] = v; - self.ifaces[index as usize] = Some(iface); + self.descriptor.altsetting[index as usize] = v; + self.descriptor.interfaces[index as usize] = Some(iface); self.init_endpoint()?; Ok(()) } - fn find_interface(&self, nif: u32, alt: u32) -> Option> { - self.config.as_ref()?; - let conf = if let Some(conf) = self.config.as_ref() { - conf - } else { - error!("No config descriptor found"); - return None; - }; - for group in conf.if_groups.iter() { - for iface in group.ifs.iter() { - if iface.interface_desc.bInterfaceNumber == nif as u8 - && iface.interface_desc.bAlternateSetting == alt as u8 - { - return Some(iface.clone()); - } - } - } - for i in 0..conf.ifs.len() { - let iface = conf.ifs[i].clone(); - if iface.interface_desc.bInterfaceNumber == nif as u8 - && iface.interface_desc.bAlternateSetting == alt as u8 - { - return Some(iface); - } - } - None - } - fn init_endpoint(&mut self) -> Result<()> { self.reset_usb_endpoint(); - for i in 0..self.ninterfaces { - let iface = self.ifaces[i as usize].as_ref(); - if iface.is_none() { - continue; - } - let iface = if let Some(iface) = iface { + for i in 0..self.descriptor.interface_number { + let iface = if let Some(iface) = self.descriptor.interfaces[i as usize].as_ref() { iface } else { - bail!("No interface descriptor found."); + continue; }; let iface = iface.clone(); for e in 0..iface.interface_desc.bNumEndpoints { - let in_direction = iface.eps[e as usize].endpoint_desc.bEndpointAddress + let in_direction = iface.endpoints[e as usize].endpoint_desc.bEndpointAddress & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; - let ep = iface.eps[e as usize].endpoint_desc.bEndpointAddress + let ep = iface.endpoints[e as usize].endpoint_desc.bEndpointAddress & USB_ENDPOINT_ADDRESS_NUMBER_MASK; let mut usb_ep = self.get_mut_endpoint(in_direction, ep as u8); - usb_ep.ep_type = iface.eps[e as usize].endpoint_desc.bmAttributes + usb_ep.ep_type = iface.endpoints[e as usize].endpoint_desc.bmAttributes & USB_ENDPOINT_ATTR_TRANSFER_TYPE_MASK; } } Ok(()) } - fn set_default_descriptor(&mut self) -> Result<()> { - if let Some(desc) = &self.usb_desc { - match self.speed { - USB_SPEED_LOW | USB_SPEED_FULL => { - self.device_desc = desc.full_dev.clone(); - } - USB_SPEED_HIGH => { - self.device_desc = desc.high_dev.clone(); - } - USB_SPEED_MASK_SUPER => { - self.device_desc = desc.super_dev.clone(); - } - _ => { - bail!("Unknown device speed."); - } - } - } + fn init_descriptor(&mut self, device_desc: Arc, str: Vec) -> Result<()> { + self.descriptor.device_desc = Some(device_desc); + self.descriptor.strings = str; self.set_config_descriptor(0)?; Ok(()) } - - fn init_descriptor(&mut self) -> Result<()> { - let desc = if let Some(desc) = &self.usb_desc { - desc.clone() - } else { - bail!("Usb descriptor is None"); - }; - - self.speed = USB_SPEED_FULL; - self.speed_mask = 0; - - if desc.full_dev.is_some() { - self.speed_mask |= USB_SPEED_MASK_FULL; - } - if desc.high_dev.is_some() { - self.speed_mask |= USB_SPEED_MASK_HIGH; - } - if desc.super_dev.is_some() { - self.speed_mask |= USB_SPEED_MASK_SUPER; - } - self.set_default_descriptor()?; - Ok(()) - } } diff --git a/usb/src/hid.rs b/usb/src/hid.rs index d8d248248..d41f46b1d 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -50,34 +50,6 @@ pub const QUEUE_LENGTH: u32 = 16; pub const QUEUE_MASK: u32 = QUEUE_LENGTH - 1; const HID_USAGE_ERROR_ROLLOVER: u8 = 0x1; -/// String descriptor index -pub const STR_MANUFACTURER: u8 = 1; -pub const STR_PRODUCT_MOUSE: u8 = 2; -pub const STR_PRODUCT_TABLET: u8 = 3; -pub const STR_PRODUCT_KEYBOARD: u8 = 4; -pub const STR_SERIAL_COMPAT: u8 = 5; -pub const STR_CONFIG_MOUSE: u8 = 6; -pub const STR_CONFIG_TABLET: u8 = 7; -pub const STR_CONFIG_KEYBOARD: u8 = 8; -pub const STR_SERIAL_MOUSE: u8 = 9; -pub const STR_SERIAL_TABLET: u8 = 10; -pub const STR_SERIAL_KEYBOARD: u8 = 11; - -/// String descriptor -pub const DESC_STRINGS: [&str; 12] = [ - "", - "StratoVirt", - "StratoVirt USB Mouse", - "StratoVirt USB Tablet", - "StratoVirt USB Keyboard", - "42", - "HID Mouse", - "HID Tablet", - "HID Keyboard", - "89126", - "28754", - "68284", -]; /// QKeyCode to HID code table const HID_CODE: [u8; 0x100] = [ 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 984531b00..3d40c2253 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -12,36 +12,22 @@ use std::sync::{Arc, Mutex, Weak}; +use anyhow::Result; use log::{debug, error, info}; use once_cell::sync::Lazy; use crate::config::*; use crate::descriptor::{ - UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, - UsbInterfaceDescriptor, -}; -use crate::hid::{ - Hid, HidType, DESC_STRINGS, QUEUE_LENGTH, QUEUE_MASK, STR_CONFIG_KEYBOARD, STR_MANUFACTURER, - STR_PRODUCT_KEYBOARD, STR_SERIAL_KEYBOARD, + UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, + UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; +use crate::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; use crate::usb::{ - notify_controller, UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, - UsbDescOther, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, + notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; -use anyhow::Result; -/// USB Keyboard Descriptor -static DESC_KEYBOARD: Lazy> = Lazy::new(|| { - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); - Arc::new(UsbDesc { - full_dev: Some(DESC_DEVICE_KEYBOARD.clone()), - high_dev: None, - super_dev: None, - strings: s, - }) -}); /// Keyboard device descriptor static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { Arc::new(UsbDescDevice { @@ -51,9 +37,9 @@ static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { idVendor: 0x0627, idProduct: 0x0001, bcdDevice: 0, - iManufacturer: STR_MANUFACTURER, - iProduct: STR_PRODUCT_KEYBOARD, - iSerialNumber: STR_SERIAL_KEYBOARD, + iManufacturer: STR_MANUFACTURER_INDEX, + iProduct: STR_PRODUCT_KEYBOARD_INDEX, + iSerialNumber: STR_SERIAL_KEYBOARD_INDEX, bcdUSB: 0x0100, bDeviceClass: 0, bDeviceSubClass: 0, @@ -61,19 +47,18 @@ static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { bMaxPacketSize0: 8, bNumConfigurations: 1, }, - confs: vec![Arc::new(UsbDescConfig { + configs: vec![Arc::new(UsbDescConfig { config_desc: UsbConfigDescriptor { bLength: USB_DT_CONFIG_SIZE, bDescriptorType: USB_DT_CONFIGURATION, wTotalLength: 0, bNumInterfaces: 1, bConfigurationValue: 1, - iConfiguration: STR_CONFIG_KEYBOARD, + iConfiguration: STR_CONFIG_KEYBOARD_INDEX, bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_REMOTE_WAKEUP, bMaxPower: 50, }, - if_groups: Vec::new(), - ifs: vec![DESC_IFACE_KEYBOARD.clone()], + interfaces: vec![DESC_IFACE_KEYBOARD.clone()], })], }) }); @@ -96,7 +81,7 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { /// HID descriptor data: vec![0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0], })], - eps: vec![Arc::new(UsbDescEndpoint { + endpoints: vec![Arc::new(UsbDescEndpoint { endpoint_desc: UsbEndpointDescriptor { bLength: USB_DT_ENDPOINT_SIZE, bDescriptorType: USB_DT_ENDPOINT, @@ -110,6 +95,21 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { }) }); +/// String descriptor index +const STR_MANUFACTURER_INDEX: u8 = 1; +const STR_PRODUCT_KEYBOARD_INDEX: u8 = 2; +const STR_CONFIG_KEYBOARD_INDEX: u8 = 3; +const STR_SERIAL_KEYBOARD_INDEX: u8 = 4; + +/// String descriptor +const DESC_STRINGS: [&str; 5] = [ + "", + "StratoVirt", + "StratoVirt USB Keyboard", + "HID Keyboard", + "1", +]; + /// USB keyboard device. pub struct UsbKeyboard { id: String, @@ -130,10 +130,11 @@ impl UsbKeyboard { } pub fn realize(mut self) -> Result>> { - self.usb_device.product_desc = String::from("StratoVirt USB keyboard"); self.usb_device.reset_usb_endpoint(); - self.usb_device.usb_desc = Some(DESC_KEYBOARD.clone()); - self.usb_device.init_descriptor()?; + self.usb_device.speed = USB_SPEED_FULL; + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; let kbd = Arc::new(Mutex::new(self)); Ok(kbd) } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index d23e5975e..f56c01f0f 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -12,41 +12,25 @@ use std::sync::{Arc, Mutex, Weak}; +use anyhow::Result; use log::{debug, error, info}; use once_cell::sync::Lazy; use crate::config::*; -use crate::descriptor::{UsbConfigDescriptor, UsbDeviceDescriptor, UsbEndpointDescriptor}; -use crate::descriptor::{UsbDescriptorOps, UsbInterfaceDescriptor}; -use crate::hid::QUEUE_MASK; -use crate::hid::{HidType, QUEUE_LENGTH}; -use crate::usb::{notify_controller, UsbDeviceRequest}; -use crate::{ - hid::{ - Hid, DESC_STRINGS, STR_CONFIG_TABLET, STR_MANUFACTURER, STR_PRODUCT_TABLET, - STR_SERIAL_TABLET, - }, - usb::{ - UsbDesc, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, - UsbDevice, UsbDeviceOps, UsbEndpoint, UsbPacket, UsbPacketStatus, - }, - xhci::xhci_controller::XhciDevice, +use crate::descriptor::{ + UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, + UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; -use anyhow::Result; +use crate::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; +use crate::usb::{ + notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, + UsbPacketStatus, +}; +use crate::xhci::xhci_controller::XhciDevice; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; -/// USB Tablet Descriptor -static DESC_TABLET: Lazy> = Lazy::new(|| { - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); - Arc::new(UsbDesc { - full_dev: Some(DESC_DEVICE_TABLET.clone()), - high_dev: None, - super_dev: None, - strings: s, - }) -}); /// Tablet device descriptor static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { Arc::new(UsbDescDevice { @@ -56,9 +40,9 @@ static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { idVendor: 0x0627, idProduct: 0x0001, bcdDevice: 0, - iManufacturer: STR_MANUFACTURER, - iProduct: STR_PRODUCT_TABLET, - iSerialNumber: STR_SERIAL_TABLET, + iManufacturer: STR_MANUFACTURER_INDEX, + iProduct: STR_PRODUCT_TABLET_INDEX, + iSerialNumber: STR_SERIAL_TABLET_INDEX, bcdUSB: 0x0100, bDeviceClass: 0, bDeviceSubClass: 0, @@ -66,19 +50,18 @@ static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { bMaxPacketSize0: 8, bNumConfigurations: 1, }, - confs: vec![Arc::new(UsbDescConfig { + configs: vec![Arc::new(UsbDescConfig { config_desc: UsbConfigDescriptor { bLength: USB_DT_CONFIG_SIZE, bDescriptorType: USB_DT_CONFIGURATION, wTotalLength: 0, bNumInterfaces: 1, bConfigurationValue: 1, - iConfiguration: STR_CONFIG_TABLET, + iConfiguration: STR_CONFIG_TABLET_INDEX, bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_REMOTE_WAKEUP, bMaxPower: 50, }, - if_groups: Vec::new(), - ifs: vec![DESC_IFACE_TABLET.clone()], + interfaces: vec![DESC_IFACE_TABLET.clone()], })], }) }); @@ -101,7 +84,7 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { /// HID descriptor data: vec![0x09, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0], })], - eps: vec![Arc::new(UsbDescEndpoint { + endpoints: vec![Arc::new(UsbDescEndpoint { endpoint_desc: UsbEndpointDescriptor { bLength: USB_DT_ENDPOINT_SIZE, bDescriptorType: USB_DT_ENDPOINT, @@ -115,6 +98,14 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { }) }); +/// String descriptor index +const STR_MANUFACTURER_INDEX: u8 = 1; +const STR_PRODUCT_TABLET_INDEX: u8 = 2; +const STR_CONFIG_TABLET_INDEX: u8 = 3; +const STR_SERIAL_TABLET_INDEX: u8 = 4; + +/// String descriptor +const DESC_STRINGS: [&str; 5] = ["", "StratoVirt", "StratoVirt USB Tablet", "HID Tablet", "2"]; /// USB tablet device. pub struct UsbTablet { id: String, @@ -135,10 +126,11 @@ impl UsbTablet { } pub fn realize(mut self) -> Result>> { - self.usb_device.product_desc = String::from("StratoVirt USB Tablet"); self.usb_device.reset_usb_endpoint(); - self.usb_device.usb_desc = Some(DESC_TABLET.clone()); - self.usb_device.init_descriptor()?; + self.usb_device.speed = USB_SPEED_FULL; + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; let tablet = Arc::new(Mutex::new(self)); Ok(tablet) } diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 6c272e1b8..d5bd3a65e 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -17,14 +17,10 @@ use anyhow::{bail, Result}; use log::{debug, error}; use crate::config::*; -use crate::descriptor::{ - UsbConfigDescriptor, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, - UsbInterfaceDescriptor, -}; +use crate::descriptor::{UsbDescriptor, UsbDescriptorOps}; use crate::xhci::xhci_controller::{UsbPort, XhciDevice}; const USB_MAX_ENDPOINTS: u32 = 15; -const USB_MAX_INTERFACES: u32 = 16; /// USB max address. const USB_MAX_ADDRESS: u8 = 127; @@ -69,86 +65,18 @@ impl UsbEndpoint { } } -/// USB descriptor strings. -pub struct UsbDescString { - pub index: u32, - pub str: String, -} - -// USB descriptor -pub struct UsbDesc { - pub full_dev: Option>, - pub high_dev: Option>, - pub super_dev: Option>, - pub strings: Vec, -} - -// USB device descriptor -pub struct UsbDescDevice { - pub device_desc: UsbDeviceDescriptor, - pub confs: Vec>, -} - -// USB config descriptor -pub struct UsbDescConfig { - pub config_desc: UsbConfigDescriptor, - pub if_groups: Vec>, - pub ifs: Vec>, -} - -// USB interface descriptor -pub struct UsbDescIface { - pub interface_desc: UsbInterfaceDescriptor, - pub other_desc: Vec>, - pub eps: Vec>, -} - -/* conceptually an Interface Association Descriptor, and related interfaces */ -#[allow(non_snake_case)] -#[repr(C)] -pub struct UsbDescIfaceAssoc { - pub bFirstInterface: u8, - pub bInterfaceCount: u8, - pub bFunctionClass: u8, - pub bFunctionSubClass: u8, - pub bFunctionProtocol: u8, - pub iFunction: u8, - pub ifs: Vec>, -} - -// USB other descriptor -pub struct UsbDescOther { - pub length: u8, - pub data: Vec, -} - -// USB endpoint descriptor -pub struct UsbDescEndpoint { - pub endpoint_desc: UsbEndpointDescriptor, - pub extra: Option>, -} - /// USB device common structure. pub struct UsbDevice { pub port: Option>>, pub speed: u32, - pub speed_mask: u32, pub addr: u8, - pub product_desc: String, pub data_buf: Vec, pub remote_wakeup: u32, pub ep_ctl: UsbEndpoint, pub ep_in: Vec, pub ep_out: Vec, /// USB descriptor - pub strings: Vec, - pub usb_desc: Option>, - pub device_desc: Option>, - pub configuration: u32, - pub ninterfaces: u32, - pub altsetting: Vec, - pub config: Option>, - pub ifaces: Vec>>, + pub descriptor: UsbDescriptor, } impl UsbDevice { @@ -156,22 +84,13 @@ impl UsbDevice { let mut dev = UsbDevice { port: None, speed: 0, - speed_mask: 0, addr: 0, ep_ctl: UsbEndpoint::new(0, false, USB_ENDPOINT_ATTR_CONTROL), ep_in: Vec::new(), ep_out: Vec::new(), - product_desc: String::new(), - strings: Vec::new(), - usb_desc: None, - device_desc: None, - configuration: 0, - ninterfaces: 0, - config: None, - altsetting: vec![0; USB_MAX_INTERFACES as usize], data_buf: vec![0_u8; 4096], - ifaces: vec![None; USB_MAX_INTERFACES as usize], remote_wakeup: 0, + descriptor: UsbDescriptor::new(), }; for i in 0..USB_MAX_ENDPOINTS as u8 { @@ -246,7 +165,7 @@ impl UsbDevice { packet.actual_length = len; } USB_REQUEST_GET_CONFIGURATION => { - self.data_buf[0] = if let Some(conf) = &self.config { + self.data_buf[0] = if let Some(conf) = &self.descriptor.configuration_selected { conf.config_desc.bConfigurationValue } else { 0 @@ -254,11 +173,20 @@ impl UsbDevice { packet.actual_length = 1; } USB_REQUEST_GET_STATUS => { - let conf = if let Some(conf) = &self.config { + let conf = if let Some(conf) = &self.descriptor.configuration_selected { conf.clone() } else { - let x = &self.device_desc.as_ref().unwrap().confs[0]; - x.clone() + let desc = if let Some(desc) = self.descriptor.device_desc.as_ref() { + desc + } else { + bail!("Device descriptor not found"); + }; + let conf = if let Some(conf) = desc.configs.get(0) { + conf + } else { + bail!("Config descriptor not found"); + }; + conf.clone() }; self.data_buf[0] = 0; if conf.config_desc.bmAttributes & USB_CONFIGURATION_ATTR_SELF_POWER @@ -305,8 +233,8 @@ impl UsbDevice { }, USB_INTERFACE_IN_REQUEST => match device_req.request { USB_REQUEST_GET_INTERFACE => { - if index < self.ninterfaces { - self.data_buf[0] = self.altsetting[index as usize] as u8; + if index < self.descriptor.interface_number { + self.data_buf[0] = self.descriptor.altsetting[index as usize] as u8; packet.actual_length = 1; } } @@ -342,7 +270,7 @@ pub trait UsbDeviceOps: Send + Sync { /// Handle the attach ops when attach device to controller. fn handle_attach(&mut self) -> Result<()> { let usb_dev = self.get_mut_usb_device(); - usb_dev.set_default_descriptor()?; + usb_dev.set_config_descriptor(0)?; Ok(()) } -- Gitee From b1567ecce65e25c6d18424c80bdbc67c18dab3b9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 26 Dec 2022 15:10:42 +0800 Subject: [PATCH 0664/2187] tablet: fixed the issue where the scroll wheel logic is incorrect The pos z is not cleared. As a result, old data is used. Signed-off-by: zhouli57 --- usb/src/tablet.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index f56c01f0f..fa89a3dc8 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -147,9 +147,11 @@ pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32 let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; let mut evt = &mut locked_tablet.hid.pointer.queue[index]; if button == INPUT_BUTTON_WHEEL_UP { - evt.pos_z += 1; + evt.pos_z = 1; } else if button == INPUT_BUTTON_WHEEL_DOWN { - evt.pos_z -= 1; + evt.pos_z = -1; + } else { + evt.pos_z = 0; } evt.button_state = button; evt.pos_x = x; -- Gitee From c379961b29ad5a065fff829cf6dc2168412f358c Mon Sep 17 00:00:00 2001 From: "Xinle.Guo" Date: Wed, 28 Dec 2022 11:39:25 +0800 Subject: [PATCH 0665/2187] migration: fix snapshot and restore vhost-vsock device 1. It needs to restore vcpu and kvm after others devices. Change the snapshot and restore order of the device. 2. PCI transport needs to resume before others virtio-pci devices. So, add `transport` element, snapshot and restore `transport` before devices. Signed-off-by: Xinle.Guo --- machine/src/micro_vm/mod.rs | 2 +- migration/src/general.rs | 7 ++++- migration/src/manager.rs | 37 +++++++++++++++++++++- migration/src/snapshot.rs | 63 ++++++++++++++++++++++++------------- virtio/src/virtio_pci.rs | 4 +-- 5 files changed, 87 insertions(+), 26 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 713d08b49..4ad5ce4d7 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -305,7 +305,7 @@ impl LightMachine { used: false, }); - MigrationManager::register_device_instance( + MigrationManager::register_transport_instance( VirtioMmioState::descriptor(), VirtioMmioDevice::realize( dev, diff --git a/migration/src/general.rs b/migration/src/general.rs index 25a346b5b..9f9d8d9ca 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -217,8 +217,13 @@ pub trait Lifecycle { Ok(()) } - /// Resume devices during migration. + /// Resume VM during migration. fn resume() -> Result<()> { + let locked_transports = &MIGRATION_MANAGER.vmm.read().unwrap().transports; + for (_, transport) in locked_transports.iter() { + transport.lock().unwrap().resume()?; + } + let locked_devices = &MIGRATION_MANAGER.vmm.read().unwrap().devices; for (_, device) in locked_devices.iter() { device.lock().unwrap().resume()?; diff --git a/migration/src/manager.rs b/migration/src/manager.rs index d364a04b1..b10c7bc05 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -167,7 +167,9 @@ pub struct Vmm { pub cpus: HashMap>, /// Trait to represent memory devices. pub memory: Option>, - /// Trait to represent virtio-pci devices. + /// Trait to represent transports. + pub transports: HashMap>>, + /// Trait to represent devices. pub devices: HashMap>>, #[cfg(target_arch = "aarch64")] /// Trait to represent GIC devices(GICv3, GICv3 ITS). @@ -281,6 +283,27 @@ impl MigrationManager { locked_vmm.memory = Some(memory); } + /// Register transport instance to vmm. + /// + /// # Arguments + /// + /// * `device_desc` - The `DeviceStateDesc` of device instance. + /// * `device` - The transport instance with MigrationHook trait. + /// * `id` - The unique id for device. + pub fn register_transport_instance( + device_desc: DeviceStateDesc, + device: Arc>, + id: &str, + ) where + T: MigrationHook + Sync + Send + 'static, + { + let name = device_desc.name.clone() + "/" + id; + Self::register_device_desc(device_desc); + + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.transports.insert(translate_id(&name), device); + } + /// Register device instance to vmm. /// /// # Arguments @@ -336,6 +359,18 @@ impl MigrationManager { locked_vmm.gic_group.insert(translate_id(id), gic); } + /// Unregister transport instance from vmm. + /// + /// # Arguments + /// + /// * `device_desc` - The `DeviceStateDesc` of device instance. + /// * `id` - The unique id for device. + pub fn unregister_transport_instance(device_desc: DeviceStateDesc, id: &str) { + let name = device_desc.name + "/" + id; + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.transports.remove(&translate_id(&name)); + } + /// Unregister device instance from vmm. /// /// # Arguments diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index cc27d423d..32b11e790 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -179,6 +179,24 @@ impl MigrationManager { Self::save_desc_db(fd)?; let locked_vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + // Save transports state. + for (id, transport) in locked_vmm.transports.iter() { + transport + .lock() + .unwrap() + .save_device(*id, fd) + .with_context(|| "Failed to save transport state")?; + } + + // Save devices state. + for (id, device) in locked_vmm.devices.iter() { + device + .lock() + .unwrap() + .save_device(*id, fd) + .with_context(|| "Failed to save device state")?; + } + // Save CPUs state. for (id, cpu) in locked_vmm.cpus.iter() { cpu.save_device(*id, fd) @@ -196,15 +214,6 @@ impl MigrationManager { .with_context(|| "Failed to save kvm state")?; } - // Save devices state. - for (id, device) in locked_vmm.devices.iter() { - device - .lock() - .unwrap() - .save_device(*id, fd) - .with_context(|| "Failed to save device state")?; - } - #[cfg(target_arch = "aarch64")] { // Save GICv3 device state. @@ -236,6 +245,30 @@ impl MigrationManager { fd: &mut dyn Read, ) -> Result<()> { let locked_vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + // Restore transports state. + for _ in 0..locked_vmm.transports.len() { + let (transport_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; + if let Some(transport) = locked_vmm.transports.get(&id) { + transport + .lock() + .unwrap() + .restore_mut_device(&transport_data) + .with_context(|| "Failed to restore transport state")?; + } + } + + // Restore devices state. + for _ in 0..locked_vmm.devices.len() { + let (device_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; + if let Some(device) = locked_vmm.devices.get(&id) { + device + .lock() + .unwrap() + .restore_mut_device(&device_data) + .with_context(|| "Failed to restore device state")?; + } + } + // Restore CPUs state. for _ in 0..locked_vmm.cpus.len() { let (cpu_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; @@ -255,18 +288,6 @@ impl MigrationManager { } } - // Restore devices state. - for _ in 0..locked_vmm.devices.len() { - let (device_data, id) = Self::check_vm_state(fd, &snap_desc_db)?; - if let Some(device) = locked_vmm.devices.get(&id) { - device - .lock() - .unwrap() - .restore_mut_device(&device_data) - .with_context(|| "Failed to restore device state")?; - } - } - #[cfg(target_arch = "aarch64")] { // Restore GIC group state. diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 13ee4ec75..dc3be1efd 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1185,7 +1185,7 @@ impl PciDevOps for VirtioPciDevice { pci_device.unwrap().lock().unwrap().name() ); } - MigrationManager::register_device_instance(VirtioPciState::descriptor(), dev, &name); + MigrationManager::register_transport_instance(VirtioPciState::descriptor(), dev, &name); Ok(()) } @@ -1205,7 +1205,7 @@ impl PciDevOps for VirtioPciDevice { } MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name); - MigrationManager::unregister_device_instance(VirtioPciState::descriptor(), &self.name); + MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name); Ok(()) } -- Gitee From bfe44c83651b2092860fa8e9f439eb0167230d19 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 30 Dec 2022 14:49:11 +0800 Subject: [PATCH 0666/2187] Fix: Init ARM PMU after CPUs realize and GIC create. The KVM requires that the CPUs and GIC must be created before the PMU is initialized. Otherwise, the initialization fails. Signed-off-by: Mingwang Li --- cpu/src/aarch64/mod.rs | 27 +++++++++++++------------- machine/src/micro_vm/mod.rs | 14 +++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 13 +++++++++++++ 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 502abad84..427c080f3 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -13,9 +13,11 @@ pub mod caps; mod core_regs; -use std::mem::forget; -use std::os::unix::prelude::{AsRawFd, FromRawFd}; -use std::sync::{Arc, Mutex}; +use std::{ + mem::forget, + os::unix::prelude::{AsRawFd, FromRawFd}, + sync::{Arc, Mutex}, +}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{ @@ -190,7 +192,6 @@ impl ArmCPUState { .get_one_reg(SYS_MPIDR_EL1) .with_context(|| "Failed to get mpidr")? as u64; - ArmCPUState::set_cpu_feature(vcpu_config, vcpu_fd)?; self.features = *vcpu_config; Ok(()) @@ -256,19 +257,18 @@ impl ArmCPUState { self.core_regs.regs.pc = boot_config.boot_pc; } } +} - fn set_cpu_feature(features: &ArmCPUFeatures, vcpu_fd: &Arc) -> Result<()> { - if !features.pmu { - return Ok(()); - } - +impl CPU { + /// Init PMU for ARM CPU + pub fn init_pmu(&self) -> Result<()> { let pmu_attr = kvm_device_attr { group: KVM_ARM_VCPU_PMU_V3_CTRL, attr: KVM_ARM_VCPU_PMU_V3_INIT as u64, addr: 0, flags: 0, }; - let vcpu_device = unsafe { DeviceFd::from_raw_fd(vcpu_fd.as_raw_fd()) }; + let vcpu_device = unsafe { DeviceFd::from_raw_fd(self.fd.as_raw_fd()) }; vcpu_device .has_device_attr(&pmu_attr) .with_context(|| "Kernel does not support PMU for vCPU")?; @@ -336,9 +336,10 @@ impl StateTransfer for CPU { self.fd.vcpu_init(&cpu_state.kvi)?; - //Init CPU features. - ArmCPUState::set_cpu_feature(&cpu_state.features, &self.fd) - .map_err(|_| migration::MigrationError::FromBytesError("failed to set features."))?; + if cpu_state.features.pmu { + self.init_pmu() + .map_err(|_| migration::MigrationError::FromBytesError("failed to init pmu."))?; + } Ok(()) } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 60ce62141..205076927 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -461,6 +461,18 @@ impl LightMachine { } Ok(id.to_string()) } + + /// Must be called after the CPUs have been realized and GIC has been created. + #[cfg(target_arch = "aarch64")] + fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { + let features = vcpu_cfg.unwrap_or_default(); + if features.pmu { + for cpu in self.cpus.iter() { + cpu.init_pmu()?; + } + } + Ok(()) + } } impl MachineOps for LightMachine { @@ -803,6 +815,8 @@ impl MachineOps for LightMachine { locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + locked_vm.cpu_post_init(&cpu_config)?; + // Add mmio devices locked_vm .create_replaceable_devices() diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 5dd652e42..66d8f4b94 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -281,6 +281,17 @@ impl StdMachine { self.build_pptt_clusters(pptt, socket_offset as u32, uid); } } + + /// Must be called after the CPUs have been realized and GIC has been created. + fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { + let features = vcpu_cfg.unwrap_or_default(); + if features.pmu { + for cpu in self.cpus.iter() { + cpu.init_pmu()?; + } + } + Ok(()) + } } impl StdMachineOps for StdMachine { @@ -508,6 +519,8 @@ impl MachineOps for StdMachine { // Interrupt Controller Chip init locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; + locked_vm.cpu_post_init(&cpu_config)?; + locked_vm .add_devices(vm_config) .with_context(|| "Failed to add devices")?; -- Gitee From bcc64c51dbb629d69f19d44a9ed073b5d08fa06a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 3 Jan 2023 19:00:00 +0800 Subject: [PATCH 0667/2187] Fix: The log and interrupt callback input parameter are inconsistent. The interrupt callback input parameter is Config, but the log is Vring. Signed-off-by: Mingwang Li --- virtio/src/balloon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 7947358e9..f887acddc 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -858,7 +858,7 @@ impl Balloon { interrupt_cb(&VirtioInterruptType::Config, None, false).with_context(|| { anyhow!(VirtioError::InterruptTrigger( "balloon", - VirtioInterruptType::Vring + VirtioInterruptType::Config )) }) } else { -- Gitee From a5de467b8512c3383a76eefa9291373bcef2337c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 31 Dec 2022 23:14:24 +0800 Subject: [PATCH 0668/2187] notifier: Drop unnecessary handler lock There is no parallel accessing to notifier handlers. Signed-off-by: Keqian Zhu --- devices/src/legacy/chardev.rs | 23 ++++++-------- machine/src/lib.rs | 10 +++--- machine/src/standard_vm/mod.rs | 41 ++++++++++++------------ machine_manager/src/socket.rs | 19 ++++++------ util/src/loop_context.rs | 21 +++++++------ vhost_user_fs/src/vhost_user_fs.rs | 11 ++++--- vhost_user_fs/src/virtio_fs.rs | 5 +-- virtio/src/balloon.rs | 15 ++++----- virtio/src/block.rs | 26 +++++++--------- virtio/src/console.rs | 9 +++--- virtio/src/gpu.rs | 12 +++---- virtio/src/net.rs | 19 ++++++------ virtio/src/rng.rs | 13 ++++---- virtio/src/scsi/controller.rs | 19 ++++++------ virtio/src/vhost/kernel/mod.rs | 50 ++++++++++++++---------------- virtio/src/vhost/user/client.rs | 11 ++++--- virtio/src/vhost/user/fs.rs | 47 ++++++++++++++-------------- vnc/src/client.rs | 19 ++++++------ vnc/src/server.rs | 7 +++-- 19 files changed, 187 insertions(+), 190 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 7bb4798c9..9a09803a6 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -15,6 +15,7 @@ use std::io::{Stdin, Stdout}; use std::os::unix::io::{AsRawFd, FromRawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -195,9 +196,9 @@ fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { fn get_notifier_handler( chardev: Arc>, backend: ChardevType, -) -> Box { +) -> Rc { match backend { - ChardevType::Stdio | ChardevType::Pty => Box::new(move |_, _| { + ChardevType::Stdio | ChardevType::Pty => Rc::new(move |_, _| { let locked_chardev = chardev.lock().unwrap(); let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); let mut buffer = vec![0_u8; buff_size]; @@ -215,7 +216,7 @@ fn get_notifier_handler( } None }), - ChardevType::Socket { .. } => Box::new(move |_, _| { + ChardevType::Socket { .. } => Rc::new(move |_, _| { let mut locked_chardev = chardev.lock().unwrap(); let (stream, _) = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); @@ -226,7 +227,7 @@ fn get_notifier_handler( locked_chardev.output = Some(stream_arc); let cloned_chardev = chardev.clone(); - let inner_handler = Box::new(move |event, _| { + let inner_handler = Rc::new(move |event, _| { if event == EventSet::IN { let locked_chardev = cloned_chardev.lock().unwrap(); let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); @@ -261,10 +262,10 @@ fn get_notifier_handler( stream_fd, Some(listener_fd), EventSet::IN | EventSet::HANG_UP, - vec![Arc::new(Mutex::new(inner_handler))], + vec![inner_handler], )]) }), - ChardevType::File(_) => Box::new(move |_, _| None), + ChardevType::File(_) => Rc::new(move |_, _| None), } } @@ -281,10 +282,7 @@ impl EventNotifierHelper for Chardev { input.lock().unwrap().as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(get_notifier_handler( - cloned_chardev, - backend, - )))], + vec![get_notifier_handler(cloned_chardev, backend)], )); } } @@ -303,10 +301,7 @@ impl EventNotifierHelper for Chardev { listener.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(get_notifier_handler( - cloned_chardev, - backend, - )))], + vec![get_notifier_handler(cloned_chardev, backend)], )); } } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 51bbaa4d7..b3dc563cd 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -23,6 +23,7 @@ use std::net::TcpListener; use std::ops::Deref; use std::os::unix::{io::AsRawFd, net::UnixListener}; use std::path::Path; +use std::rc::Rc; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use log::warn; @@ -1304,11 +1305,10 @@ pub trait MachineOps { fn register_power_event(&self, power_button: &EventFd) -> Result<()> { let power_button = power_button.try_clone().unwrap(); let button_fd = power_button.as_raw_fd(); - let power_button_handler: Arc>> = - Arc::new(Mutex::new(Box::new(move |_, _| { - let _ret = power_button.read().unwrap(); - None - }))); + let power_button_handler: Rc = Rc::new(move |_, _| { + let _ret = power_button.read().unwrap(); + None + }); let notifier = EventNotifier::new( NotifierOperation::AddShared, button_fd, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index f7484cfdd..5fcf9ae8f 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -33,6 +33,7 @@ use std::mem::size_of; use std::ops::Deref; use std::os::unix::io::RawFd; use std::os::unix::prelude::AsRawFd; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use super::Result as MachineResult; @@ -192,15 +193,14 @@ trait StdMachineOps: AcpiBuilder { ) -> MachineResult<()> { let reset_req = reset_req.try_clone().unwrap(); let reset_req_fd = reset_req.as_raw_fd(); - let reset_req_handler: Arc>> = - Arc::new(Mutex::new(Box::new(move |_, _| { - let _ret = reset_req.read().unwrap(); - if let Err(e) = StdMachine::handle_reset_request(&clone_vm) { - error!("Fail to reboot standard VM, {:?}", e); - } + let reset_req_handler: Rc = Rc::new(move |_, _| { + let _ret = reset_req.read().unwrap(); + if let Err(e) = StdMachine::handle_reset_request(&clone_vm) { + error!("Fail to reboot standard VM, {:?}", e); + } - None - }))); + None + }); let notifier = EventNotifier::new( NotifierOperation::AddShared, reset_req_fd, @@ -221,19 +221,18 @@ trait StdMachineOps: AcpiBuilder { ) -> MachineResult<()> { let shutdown_req = shutdown_req.try_clone().unwrap(); let shutdown_req_fd = shutdown_req.as_raw_fd(); - let shutdown_req_handler: Arc>> = - Arc::new(Mutex::new(Box::new(move |_, _| { - let _ret = shutdown_req.read().unwrap(); - StdMachine::handle_shutdown_request(&clone_vm); - let notifiers = vec![EventNotifier::new( - NotifierOperation::Delete, - shutdown_req_fd, - None, - EventSet::IN, - Vec::new(), - )]; - Some(notifiers) - }))); + let shutdown_req_handler: Rc = Rc::new(move |_, _| { + let _ret = shutdown_req.read().unwrap(); + StdMachine::handle_shutdown_request(&clone_vm); + let notifiers = vec![EventNotifier::new( + NotifierOperation::Delete, + shutdown_req_fd, + None, + EventSet::IN, + Vec::new(), + )]; + Some(notifiers) + }); let notifier = EventNotifier::new( NotifierOperation::AddShared, shutdown_req_fd, diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 34ce61120..018c2ff76 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -14,6 +14,7 @@ use serde::Deserialize; use std::io::{Error, ErrorKind, Read, Write}; use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; +use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; use anyhow::{bail, Result}; @@ -189,8 +190,8 @@ impl Socket { let leak_bucket_fd = leak_bucket.lock().unwrap().as_raw_fd(); let mut handlers = Vec::new(); - let handler: Box Option>> = - Box::new(move |event, _| { + let handler: Rc Option>> = + Rc::new(move |event, _| { if event == EventSet::IN { let socket_mutexed = shared_socket.lock().unwrap(); let stream_fd = socket_mutexed.get_stream_fd(); @@ -231,7 +232,7 @@ impl Socket { None } }); - handlers.push(Arc::new(Mutex::new(handler))); + handlers.push(handler); let qmp_notifier = EventNotifier::new( NotifierOperation::AddShared, @@ -247,11 +248,11 @@ impl Socket { leak_bucket_fd, None, EventSet::IN, - vec![Arc::new(Mutex::new(Box::new(move |_, fd| { + vec![Rc::new(move |_, fd| { read_fd(fd); leak_bucket.lock().unwrap().clear_timer(); None - })))], + })], ); notifiers.push(leak_bucket_notifier); @@ -265,12 +266,10 @@ impl EventNotifierHelper for Socket { let socket = shared_socket.clone(); let mut handlers = Vec::new(); - let handler: Box Option>> = - Box::new(move |_, _| { - Some(socket.lock().unwrap().create_event_notifier(socket.clone())) - }); + let handler: Rc Option>> = + Rc::new(move |_, _| Some(socket.lock().unwrap().create_event_notifier(socket.clone()))); - handlers.push(Arc::new(Mutex::new(handler))); + handlers.push(handler); let notifier = EventNotifier::new( NotifierOperation::AddShared, diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 03d5c66a6..1f1a1e49e 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -12,6 +12,7 @@ use std::collections::BTreeMap; use std::os::unix::io::RawFd; +use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; @@ -67,7 +68,7 @@ pub struct EventNotifier { /// The types of events for which we use this fd pub event: EventSet, /// Event Handler List, one fd event may have many handlers - pub handlers: Vec>>>, + pub handlers: Vec>, /// Pre-polling handler pub handler_poll: Option>, /// Event status @@ -94,7 +95,7 @@ impl EventNotifier { raw_fd: i32, parked_fd: Option, event: EventSet, - handlers: Vec>>>, + handlers: Vec>, ) -> Self { EventNotifier { raw_fd, @@ -484,8 +485,8 @@ impl EventLoopContext { if let EventStatus::Alive = event.status { let mut notifiers = Vec::new(); for j in 0..event.handlers.len() { - let handle = event.handlers[j].lock().unwrap(); - match handle(self.ready_events[i].event_set(), event.raw_fd) { + let handler = &event.handlers[j]; + match handler(self.ready_events[i].event_set(), event.raw_fd) { None => {} Some(mut notifier) => { notifiers.append(&mut notifier); @@ -559,8 +560,8 @@ mod test { } } - fn generate_handler(related_fd: i32) -> Box { - Box::new(move |_, _| { + fn generate_handler(related_fd: i32) -> Rc { + Rc::new(move |_, _| { let mut notifiers = Vec::new(); let event = EventNotifier::new( NotifierOperation::AddShared, @@ -583,13 +584,13 @@ mod test { let handler1 = generate_handler(fd1_related.as_raw_fd()); let mut handlers = Vec::new(); - handlers.push(Arc::new(Mutex::new(handler1))); + handlers.push(handler1); let event1 = EventNotifier::new( NotifierOperation::AddShared, fd1.as_raw_fd(), None, EventSet::OUT, - handlers.clone(), + handlers, ); notifiers.push(event1); @@ -664,7 +665,7 @@ mod test { fd1.as_raw_fd(), None, EventSet::OUT, - vec![Arc::new(Mutex::new(handler1))], + vec![handler1], ); let event1_update = EventNotifier::new( @@ -672,7 +673,7 @@ mod test { fd1.as_raw_fd(), None, EventSet::OUT, - vec![Arc::new(Mutex::new(handler1_update))], + vec![handler1_update], ); notifiers.push(event1); diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index 5fd7162a6..a2051ae58 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::os::unix::io::RawFd; +use std::rc::Rc; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, @@ -62,7 +63,7 @@ impl CreateEventNotifier for VhostUserServerHandler { } let mut handlers = Vec::new(); - let handler: Box = Box::new(move |event, _| { + let handler: Rc = Rc::new(move |event, _| { if event == EventSet::IN { let mut lock_server_handler = server_handler.lock().unwrap(); if let Err(e) = lock_server_handler.handle_request() { @@ -77,7 +78,7 @@ impl CreateEventNotifier for VhostUserServerHandler { None }); - handlers.push(Arc::new(Mutex::new(handler))); + handlers.push(handler); let notifier = EventNotifier::new( NotifierOperation::AddShared, @@ -97,15 +98,15 @@ impl EventNotifierHelper for VhostUserServerHandler { let mut notifiers = Vec::new(); let mut handlers = Vec::new(); let server_handler_clone = server_handler.clone(); - let handler: Box Option>> = - Box::new(move |_, _| { + let handler: Rc Option>> = + Rc::new(move |_, _| { server_handler_clone .lock() .unwrap() .create_event_notifier(server_handler_clone.clone()) }); - handlers.push(Arc::new(Mutex::new(handler))); + handlers.push(handler); let notifier = EventNotifier::new( NotifierOperation::AddShared, diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index e7ca48ade..a0dbfd59a 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -26,6 +26,7 @@ const VIRTIO_FS_VRING_NO_FD_MASK: usize = 0x1 << 8; use crate::cmdline::FsConfig; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use log::error; @@ -126,7 +127,7 @@ impl EventNotifierHelper for FsIoHandler { let mut notifiers = Vec::new(); let fs_handler_clone = fs_handler.clone(); - let handler = Box::new(move |_, fd: RawFd| { + let handler = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = fs_handler_clone.lock().unwrap().process_queue() { @@ -141,7 +142,7 @@ impl EventNotifierHelper for FsIoHandler { fs_handler.lock().unwrap().kick_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); notifiers diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index f887acddc..79062e301 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -12,6 +12,7 @@ use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use std::{ @@ -668,13 +669,13 @@ impl BalloonIoHandler { /// /// * `fd` - Raw file descriptor. /// * `handler` - Handle function. -fn build_event_notifier(fd: RawFd, handler: Box) -> EventNotifier { +fn build_event_notifier(fd: RawFd, handler: Rc) -> EventNotifier { EventNotifier::new( NotifierOperation::AddShared, fd, None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], ) } @@ -686,7 +687,7 @@ impl EventNotifierHelper for BalloonIoHandler { // register event notifier for inflate event. let cloned_balloon_io = balloon_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); if locked_balloon_io.device_broken.load(Ordering::SeqCst) { @@ -709,7 +710,7 @@ impl EventNotifierHelper for BalloonIoHandler { // register event notifier for deflate event. let cloned_balloon_io = balloon_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); if locked_balloon_io.device_broken.load(Ordering::SeqCst) { @@ -733,7 +734,7 @@ impl EventNotifierHelper for BalloonIoHandler { // register event notifier for free page reporting event. if let Some(report_evt) = locked_balloon_io.report_evt.as_ref() { let cloned_balloon_io = balloon_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); if locked_balloon_io.device_broken.load(Ordering::SeqCst) { @@ -754,7 +755,7 @@ impl EventNotifierHelper for BalloonIoHandler { // register event notifier for reset event. let cloned_balloon_io = balloon_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(cloned_balloon_io.lock().unwrap().deactivate_evt_handler()) }); @@ -765,7 +766,7 @@ impl EventNotifierHelper for BalloonIoHandler { // register event notifier for timer event. let cloned_balloon_io = balloon_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let locked_balloon_io = cloned_balloon_io.lock().unwrap(); if locked_balloon_io.device_broken.load(Ordering::SeqCst) { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index ba19fe2a8..51e59ece5 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -728,7 +728,7 @@ impl BlockIoHandler { fn build_event_notifier( fd: RawFd, - handlers: Vec>>>, + handlers: Vec>, handler_poll: Option>, ) -> EventNotifier { let mut notifier = EventNotifier::new( @@ -749,7 +749,7 @@ impl EventNotifierHelper for BlockIoHandler { // Register event notifier for update_evt. let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -760,25 +760,25 @@ impl EventNotifierHelper for BlockIoHandler { }); notifiers.push(build_event_notifier( handler_raw.update_evt.as_raw_fd(), - vec![Arc::new(Mutex::new(h))], + vec![h], None, )); // Register event notifier for deactivate_evt. let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(h_clone.lock().unwrap().deactivate_evt_handler()) }); notifiers.push(build_event_notifier( handler_raw.deactivate_evt.as_raw_fd(), - vec![Arc::new(Mutex::new(h))], + vec![h], None, )); // Register event notifier for queue_evt. let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -807,14 +807,14 @@ impl EventNotifierHelper for BlockIoHandler { }); notifiers.push(build_event_notifier( handler_raw.queue_evt.as_raw_fd(), - vec![Arc::new(Mutex::new(h))], + vec![h], Some(handler_iopoll), )); // Register timer event notifier for IO limits if let Some(lb) = handler_raw.leak_bucket.as_ref() { let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -828,16 +828,12 @@ impl EventNotifierHelper for BlockIoHandler { } None }); - notifiers.push(build_event_notifier( - lb.as_raw_fd(), - vec![Arc::new(Mutex::new(h))], - None, - )); + notifiers.push(build_event_notifier(lb.as_raw_fd(), vec![h], None)); } // Register event notifier for aio. let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -866,7 +862,7 @@ impl EventNotifierHelper for BlockIoHandler { }); notifiers.push(build_event_notifier( handler_raw.aio.fd.as_raw_fd(), - vec![Arc::new(Mutex::new(h))], + vec![h], Some(handler_iopoll), )); diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 938bd318c..9d52dbfaf 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -12,6 +12,7 @@ use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{cmp, usize}; @@ -274,7 +275,7 @@ impl EventNotifierHelper for ConsoleHandler { fn internal_notifiers(console_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); let cloned_cls = console_handler.clone(); - let handler = Box::new(move |_, fd: RawFd| { + let handler = Rc::new(move |_, fd: RawFd| { read_fd(fd); cloned_cls.lock().unwrap().output_handle(); None as Option> @@ -284,11 +285,11 @@ impl EventNotifierHelper for ConsoleHandler { console_handler.lock().unwrap().output_queue_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); let cloned_cls = console_handler.clone(); - let handler = Box::new(move |_, fd: RawFd| { + let handler = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(cloned_cls.lock().unwrap().deactivate_evt_handler()) }); @@ -297,7 +298,7 @@ impl EventNotifierHelper for ConsoleHandler { console_handler.lock().unwrap().deactivate_evt, None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); notifiers diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index b90ddced1..e5ba35962 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -1663,7 +1663,7 @@ impl EventNotifierHelper for GpuIoHandler { // Register event notifier for deactivate_evt. let gpu_handler_clone = gpu_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); // Deactivate console. let mut locked_handler = gpu_handler_clone.lock().unwrap(); @@ -1677,12 +1677,12 @@ impl EventNotifierHelper for GpuIoHandler { gpu_handler.lock().unwrap().deactivate_evt, None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); // Register event notifier for ctrl_queue_evt. let gpu_handler_clone = gpu_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = gpu_handler_clone.lock().unwrap().ctrl_queue_evt_handler() { error!("Failed to process queue for virtio gpu, err: {:?}", e,); @@ -1695,12 +1695,12 @@ impl EventNotifierHelper for GpuIoHandler { gpu_handler.lock().unwrap().ctrl_queue_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); // Register event notifier for cursor_queue_evt. let gpu_handler_clone = gpu_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = gpu_handler_clone.lock().unwrap().cursor_queue_evt_handler() { error!("Failed to process queue for virtio gpu, err: {:?}", e,); @@ -1713,7 +1713,7 @@ impl EventNotifierHelper for GpuIoHandler { gpu_handler.lock().unwrap().cursor_queue_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); notifiers diff --git a/virtio/src/net.rs b/virtio/src/net.rs index ea8ba15f1..e750112b5 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use std::io::{ErrorKind, Write}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; @@ -623,7 +624,7 @@ impl EventNotifierHelper for NetCtrlHandler { fn internal_notifiers(net_io: Arc>) -> Vec { let locked_net_io = net_io.lock().unwrap(); let cloned_net_io = net_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_net_io = cloned_net_io.lock().unwrap(); if locked_net_io.device_broken.load(Ordering::SeqCst) { @@ -650,7 +651,7 @@ impl EventNotifierHelper for NetCtrlHandler { // Register event notifier for deactivate_evt. let cloned_net_io = net_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(cloned_net_io.lock().unwrap().deactivate_evt_handler()) }); @@ -1056,13 +1057,13 @@ fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { fn build_event_notifier( fd: RawFd, - handler: Option>, + handler: Option>, op: NotifierOperation, event: EventSet, ) -> EventNotifier { let mut handlers = Vec::new(); if let Some(h) = handler { - handlers.push(Arc::new(Mutex::new(h))); + handlers.push(h); } EventNotifier::new(op, fd, None, event, handlers) } @@ -1072,7 +1073,7 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for update_evt. let locked_net_io = net_io.lock().unwrap(); let cloned_net_io = net_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if cloned_net_io .lock() @@ -1093,7 +1094,7 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for deactivate_evt. let cloned_net_io = net_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(cloned_net_io.lock().unwrap().deactivate_evt_handler()) }); @@ -1106,7 +1107,7 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for rx. let cloned_net_io = net_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_net_io = cloned_net_io.lock().unwrap(); if locked_net_io.device_broken.load(Ordering::SeqCst) { @@ -1137,7 +1138,7 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for tx. let cloned_net_io = net_io.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut locked_net_io = cloned_net_io.lock().unwrap(); if locked_net_io.device_broken.load(Ordering::SeqCst) { @@ -1164,7 +1165,7 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for tap. let cloned_net_io = net_io.clone(); if let Some(tap) = locked_net_io.tap.as_ref() { - let handler: Box = Box::new(move |_, _| { + let handler: Rc = Rc::new(move |_, _| { let mut locked_net_io = cloned_net_io.lock().unwrap(); if locked_net_io.device_broken.load(Ordering::SeqCst) { return None; diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 26f480324..8ba5a69dd 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -14,6 +14,7 @@ use std::fs::File; use std::os::unix::fs::FileTypeExt; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -183,7 +184,7 @@ impl EventNotifierHelper for RngHandler { // Register event notifier for queue_evt let rng_handler_clone = rng_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Err(ref e) = rng_handler_clone.lock().unwrap().process_queue() { @@ -197,12 +198,12 @@ impl EventNotifierHelper for RngHandler { rng_handler.lock().unwrap().queue_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); // Register event notifier for deactivate_evt let rng_handler_clone = rng_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(rng_handler_clone.lock().unwrap().deactivate_evt_handler()) }); @@ -211,13 +212,13 @@ impl EventNotifierHelper for RngHandler { rng_handler.lock().unwrap().deactivate_evt, None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); // Register timer event notifier for the limit of request bytes per second if let Some(lb) = rng_handler.lock().unwrap().leak_bucket.as_ref() { let rng_handler_clone = rng_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Some(leak_bucket) = rng_handler_clone.lock().unwrap().leak_bucket.as_mut() { @@ -236,7 +237,7 @@ impl EventNotifierHelper for RngHandler { lb.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 590266cac..ece053b34 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -347,13 +348,13 @@ impl VirtioDevice for ScsiCntlr { } } -fn build_event_notifier(fd: RawFd, handler: Box) -> EventNotifier { +fn build_event_notifier(fd: RawFd, handler: Rc) -> EventNotifier { EventNotifier::new( NotifierOperation::AddShared, fd, None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], ) } @@ -723,7 +724,7 @@ impl EventNotifierHelper for ScsiCtrlHandler { fn internal_notifiers(handler: Arc>) -> Vec { let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -740,7 +741,7 @@ impl EventNotifierHelper for ScsiCtrlHandler { notifiers.push(build_event_notifier(ctrl_fd, h)); let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(h_clone.lock().unwrap().deactivate_evt_handler()) }); @@ -771,7 +772,7 @@ impl EventNotifierHelper for ScsiEventHandler { fn internal_notifiers(handler: Arc>) -> Vec { let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -788,7 +789,7 @@ impl EventNotifierHelper for ScsiEventHandler { notifiers.push(build_event_notifier(event_fd, h)); let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(h_clone.lock().unwrap().deactivate_evt_handler()) }); @@ -848,7 +849,7 @@ impl EventNotifierHelper for ScsiCmdHandler { fn internal_notifiers(handler: Arc>) -> Vec { let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { @@ -866,7 +867,7 @@ impl EventNotifierHelper for ScsiCmdHandler { notifiers.push(build_event_notifier(event_fd, h)); let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(h_clone.lock().unwrap().deactivate_evt_handler()) }); @@ -875,7 +876,7 @@ impl EventNotifierHelper for ScsiCmdHandler { // Register event notifier for aio. if let Some(ref aio) = h_locked.aio { let h_clone = handler.clone(); - let h: Box = Box::new(move |_, fd: RawFd| { + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); let mut h_lock = h_clone.lock().unwrap(); if h_lock.device_broken.load(Ordering::SeqCst) { diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 1cdeaa499..bf00b40f6 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -19,6 +19,7 @@ pub use vsock::{Vsock, VsockState}; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -490,47 +491,44 @@ impl VhostIoHandler { impl EventNotifierHelper for VhostIoHandler { fn internal_notifiers(vhost_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); - let vhost = vhost_handler.clone(); - let handler: Box Option>> = - Box::new(move |_, fd: RawFd| { - read_fd(fd); + let vhost = vhost_handler.clone(); + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); - let locked_vhost_handler = vhost.lock().unwrap(); - if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { - return None; - } + let locked_vhost_handler = vhost.lock().unwrap(); + if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { + return None; + } - for host_notify in locked_vhost_handler.host_notifies.iter() { - if let Err(e) = (locked_vhost_handler.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&host_notify.queue.lock().unwrap()), - false, - ) { - error!( - "Failed to trigger interrupt for vhost device, error is {:?}", - e - ); - } + for host_notify in locked_vhost_handler.host_notifies.iter() { + if let Err(e) = (locked_vhost_handler.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&host_notify.queue.lock().unwrap()), + false, + ) { + error!( + "Failed to trigger interrupt for vhost device, error is {:?}", + e + ); } + } - None as Option> - }); - let h = Arc::new(Mutex::new(handler)); - + None as Option> + }); for host_notify in vhost_handler.lock().unwrap().host_notifies.iter() { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, host_notify.notify_evt.as_raw_fd(), None, EventSet::IN, - vec![h.clone()], + vec![handler.clone()], )); } // Register event notifier for deactivate_evt. let vhost = vhost_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(vhost.lock().unwrap().deactivate_evt_handler()) }); @@ -539,7 +537,7 @@ impl EventNotifierHelper for VhostIoHandler { vhost_handler.lock().unwrap().deactivate_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); notifiers diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 56123ddd1..1cf573a36 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -12,6 +12,7 @@ use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; @@ -128,8 +129,8 @@ impl EventNotifierHelper for VhostUserClient { let mut handlers = Vec::new(); let cloned_client = client_handler.clone(); - let handler: Box Option>> = - Box::new(move |event, _| { + let handler: Rc Option>> = + Rc::new(move |event, _| { if event & EventSet::HANG_UP == EventSet::HANG_UP { let mut locked_client = cloned_client.lock().unwrap(); if let Err(e) = locked_client.delete_event() { @@ -145,7 +146,7 @@ impl EventNotifierHelper for VhostUserClient { None } }); - handlers.push(Arc::new(Mutex::new(handler))); + handlers.push(handler); let locked_client = client_handler.lock().unwrap(); notifiers.push(EventNotifier::new( @@ -164,7 +165,7 @@ impl EventNotifierHelper for VhostUserClient { // Register event notifier for delete_evt. let cloned_client = client_handler.clone(); - let handler: Box = Box::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(cloned_client.lock().unwrap().delete_evt_handler()) }); @@ -173,7 +174,7 @@ impl EventNotifierHelper for VhostUserClient { locked_client.delete_evt.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); notifiers diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index a72795afb..8d133b23f 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -21,6 +21,7 @@ use crate::VirtioError; use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use log::error; @@ -32,7 +33,9 @@ use machine_manager::{ event_loop::EventLoop, }; use util::byte_code::ByteCode; -use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use util::num_ops::read_u32; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; @@ -68,37 +71,33 @@ impl EventNotifierHelper for VhostUserFsHandler { fn internal_notifiers(vhost_user_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); let vhost_user = vhost_user_handler.clone(); - - let handler: Box Option>> = - Box::new(move |_, fd: RawFd| { - read_fd(fd); - - let locked_vhost_user = vhost_user.lock().unwrap(); - - for host_notify in locked_vhost_user.host_notifies.iter() { - if let Err(e) = (locked_vhost_user.interrup_cb)( - &VirtioInterruptType::Vring, - Some(&host_notify.queue.lock().unwrap()), - false, - ) { - error!( - "Failed to trigger interrupt for vhost user device, error is {:?}", - e - ); - } + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + + let locked_vhost_user = vhost_user.lock().unwrap(); + + for host_notify in locked_vhost_user.host_notifies.iter() { + if let Err(e) = (locked_vhost_user.interrup_cb)( + &VirtioInterruptType::Vring, + Some(&host_notify.queue.lock().unwrap()), + false, + ) { + error!( + "Failed to trigger interrupt for vhost user device, error is {:?}", + e + ); } + } - None as Option> - }); - let h = Arc::new(Mutex::new(handler)); - + None as Option> + }); for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, host_notify.notify_evt.as_raw_fd(), None, EventSet::IN, - vec![h.clone()], + vec![handler.clone()], )); } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 3e1f791f0..88b6f41c8 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -33,6 +33,7 @@ use std::{ io::{Read, Write}, net::{Shutdown, TcpStream}, os::unix::prelude::{AsRawFd, RawFd}, + rc::Rc, sync::{Arc, Mutex}, }; use util::{ @@ -988,8 +989,8 @@ impl EventNotifierHelper for ClientIoHandler { // Register event notifier for read. let client_io = client_io_handler.clone(); - let handler: Box Option>> = - Box::new(move |event, _fd: RawFd| { + let handler: Rc Option>> = + Rc::new(move |event, _fd: RawFd| { let mut locked_client_io = client_io.lock().unwrap(); let client = locked_client_io.client.clone(); if event & EventSet::ERROR == EventSet::ERROR @@ -1017,14 +1018,14 @@ impl EventNotifierHelper for ClientIoHandler { client_io.lock().unwrap().stream.as_raw_fd(), None, EventSet::IN | EventSet::READ_HANG_UP, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); // Register event notifier for write. let client_io = client_io_handler.clone(); let client = client_io.lock().unwrap().client.clone(); - let handler: Box Option>> = - Box::new(move |_event, fd| { + let handler: Rc Option>> = + Rc::new(move |_event, fd| { read_fd(fd); let mut locked_client_io = client_io.lock().unwrap(); let client = locked_client_io.client.clone(); @@ -1043,13 +1044,13 @@ impl EventNotifierHelper for ClientIoHandler { client.write_fd.lock().unwrap().as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); // Register event for disconnect. let client_io = client_io_handler.clone(); - let handler: Box Option>> = - Box::new(move |_event, fd| { + let handler: Rc Option>> = + Rc::new(move |_event, fd| { read_fd(fd); // Drop client info from vnc server. let mut locked_client_io = client_io.lock().unwrap(); @@ -1073,7 +1074,7 @@ impl EventNotifierHelper for ClientIoHandler { client.disconn_evt.lock().unwrap().as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )); notifiers diff --git a/vnc/src/server.rs b/vnc/src/server.rs index ddca62cb6..3bbe63563 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -40,6 +40,7 @@ use std::{ net::{SocketAddr, TcpListener, TcpStream}, os::unix::prelude::{AsRawFd, RawFd}, ptr, + rc::Rc, sync::{Arc, Mutex}, }; use util::{ @@ -116,8 +117,8 @@ impl EventNotifierHelper for VncConnHandler { let vnc_io_clone = vnc_io.clone(); let server = vnc_io.lock().unwrap().server.clone(); // Register event notifier for connection. - let handler: Box Option>> = - Box::new(move |_event, fd: RawFd| { + let handler: Rc Option>> = + Rc::new(move |_event, fd: RawFd| { read_fd(fd); match vnc_io_clone.clone().lock().unwrap().listener.accept() { Ok((stream, addr)) => { @@ -138,7 +139,7 @@ impl EventNotifierHelper for VncConnHandler { vnc_io.lock().unwrap().listener.as_raw_fd(), None, EventSet::IN, - vec![Arc::new(Mutex::new(handler))], + vec![handler], )), ]; notifiers -- Gitee From 9447310f64154fe3ca8c6a425929f3c8c8e25e45 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 31 Dec 2022 23:15:42 +0800 Subject: [PATCH 0669/2187] notifier: Refactor notifier registration to same style And delete some unnecessary code. Signed-off-by: Keqian Zhu --- devices/src/legacy/chardev.rs | 2 +- machine_manager/src/socket.rs | 97 +++++++++++++------------- vhost_user_fs/src/vhost_user_fs.rs | 48 ++++++------- vhost_user_fs/src/virtio_fs.rs | 9 ++- virtio/src/console.rs | 11 +-- virtio/src/gpu.rs | 2 - virtio/src/net.rs | 6 +- virtio/src/rng.rs | 6 -- virtio/src/scsi/controller.rs | 23 +++---- virtio/src/vhost/kernel/mod.rs | 3 - virtio/src/vhost/user/client.rs | 36 +++++----- virtio/src/vhost/user/fs.rs | 3 - vnc/src/client.rs | 105 ++++++++++++++--------------- vnc/src/server.rs | 47 ++++++------- 14 files changed, 175 insertions(+), 223 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 9a09803a6..26bb09992 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -227,7 +227,7 @@ fn get_notifier_handler( locked_chardev.output = Some(stream_arc); let cloned_chardev = chardev.clone(); - let inner_handler = Rc::new(move |event, _| { + let inner_handler: Rc = Rc::new(move |event, _| { if event == EventSet::IN { let locked_chardev = cloned_chardev.lock().unwrap(); let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 018c2ff76..fb924ecc8 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -20,7 +20,9 @@ use std::sync::{Arc, Mutex, RwLock}; use anyhow::{bail, Result}; use log::{error, info}; use util::leak_bucket::LeakBucket; -use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use vmm_sys_util::epoll::EventSet; use crate::machine::MachineExternalInterface; @@ -189,57 +191,53 @@ impl Socket { let shared_leak_bucket = leak_bucket.clone(); let leak_bucket_fd = leak_bucket.lock().unwrap().as_raw_fd(); - let mut handlers = Vec::new(); - let handler: Rc Option>> = - Rc::new(move |event, _| { - if event == EventSet::IN { - let socket_mutexed = shared_socket.lock().unwrap(); - let stream_fd = socket_mutexed.get_stream_fd(); - - let performer = &socket_mutexed.performer.as_ref().unwrap(); - if let Err(e) = crate::qmp::handle_qmp( - stream_fd, - performer, - &mut shared_leak_bucket.lock().unwrap(), - ) { - error!("{:?}", e); - } + let handler: Rc = Rc::new(move |event, _| { + if event == EventSet::IN { + let socket_mutexed = shared_socket.lock().unwrap(); + let stream_fd = socket_mutexed.get_stream_fd(); + + let performer = &socket_mutexed.performer.as_ref().unwrap(); + if let Err(e) = crate::qmp::handle_qmp( + stream_fd, + performer, + &mut shared_leak_bucket.lock().unwrap(), + ) { + error!("{:?}", e); } - if event & EventSet::HANG_UP == EventSet::HANG_UP { - let socket_mutexed = shared_socket.lock().unwrap(); - let stream_fd = socket_mutexed.get_stream_fd(); - let listener_fd = socket_mutexed.get_listener_fd(); - - QmpChannel::unbind(); - - Some(vec![ - EventNotifier::new( - NotifierOperation::Delete, - stream_fd, - Some(listener_fd), - EventSet::IN | EventSet::HANG_UP, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - leak_bucket_fd, - None, - EventSet::IN, - Vec::new(), - ), - ]) - } else { - None - } - }); - handlers.push(handler); + } + if event & EventSet::HANG_UP == EventSet::HANG_UP { + let socket_mutexed = shared_socket.lock().unwrap(); + let stream_fd = socket_mutexed.get_stream_fd(); + let listener_fd = socket_mutexed.get_listener_fd(); + + QmpChannel::unbind(); + Some(vec![ + EventNotifier::new( + NotifierOperation::Delete, + stream_fd, + Some(listener_fd), + EventSet::IN | EventSet::HANG_UP, + Vec::new(), + ), + EventNotifier::new( + NotifierOperation::Delete, + leak_bucket_fd, + None, + EventSet::IN, + Vec::new(), + ), + ]) + } else { + None + } + }); let qmp_notifier = EventNotifier::new( NotifierOperation::AddShared, self.get_stream_fd(), Some(self.get_listener_fd()), EventSet::IN | EventSet::HANG_UP, - handlers, + vec![handler], ); notifiers.push(qmp_notifier); @@ -265,20 +263,15 @@ impl EventNotifierHelper for Socket { let mut notifiers = Vec::new(); let socket = shared_socket.clone(); - let mut handlers = Vec::new(); - let handler: Rc Option>> = + let handler: Rc = Rc::new(move |_, _| Some(socket.lock().unwrap().create_event_notifier(socket.clone()))); - - handlers.push(handler); - let notifier = EventNotifier::new( NotifierOperation::AddShared, shared_socket.lock().unwrap().get_listener_fd(), None, EventSet::IN, - handlers, + vec![handler], ); - notifiers.push(notifier); notifiers diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index a2051ae58..f27d01994 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::unix::io::RawFd; -use std::rc::Rc; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, +use std::{ + rc::Rc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, }; use log::error; @@ -54,15 +55,14 @@ impl CreateEventNotifier for VhostUserServerHandler { &mut self, server_handler: Arc>, ) -> Option> { - let mut notifiers = Vec::new(); - let should_exit = self.should_exit.clone(); - if let Err(e) = self.sock.domain.accept() { error!("Failed to accept the socket for vhost user server, {:?}", e); return None; } - let mut handlers = Vec::new(); + let mut notifiers = Vec::new(); + + let should_exit = self.should_exit.clone(); let handler: Rc = Rc::new(move |event, _| { if event == EventSet::IN { let mut lock_server_handler = server_handler.lock().unwrap(); @@ -70,25 +70,20 @@ impl CreateEventNotifier for VhostUserServerHandler { error!("Failed to handle request for vhost user server, {:?}", e); } } - if event & EventSet::HANG_UP == EventSet::HANG_UP { should_exit.store(true, Ordering::Release); } - None }); - - handlers.push(handler); - let notifier = EventNotifier::new( NotifierOperation::AddShared, self.sock.domain.get_stream_raw_fd(), None, EventSet::IN | EventSet::HANG_UP, - handlers, + vec![handler], ); - notifiers.push(notifier); + Some(notifiers) } } @@ -96,18 +91,14 @@ impl CreateEventNotifier for VhostUserServerHandler { impl EventNotifierHelper for VhostUserServerHandler { fn internal_notifiers(server_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); - let mut handlers = Vec::new(); - let server_handler_clone = server_handler.clone(); - let handler: Rc Option>> = - Rc::new(move |_, _| { - server_handler_clone - .lock() - .unwrap() - .create_event_notifier(server_handler_clone.clone()) - }); - - handlers.push(handler); + let server_handler_clone = server_handler.clone(); + let handler: Rc = Rc::new(move |_, _| { + server_handler_clone + .lock() + .unwrap() + .create_event_notifier(server_handler_clone.clone()) + }); let notifier = EventNotifier::new( NotifierOperation::AddShared, server_handler @@ -118,9 +109,8 @@ impl EventNotifierHelper for VhostUserServerHandler { .get_listener_raw_fd(), None, EventSet::IN, - handlers, + vec![handler], ); - notifiers.push(notifier); notifiers diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index a0dbfd59a..d41343285 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -34,7 +34,9 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; use machine_manager::event_loop::EventLoop; -use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use super::fs::FileSystem; use super::fuse_req::FuseReq; @@ -127,16 +129,13 @@ impl EventNotifierHelper for FsIoHandler { let mut notifiers = Vec::new(); let fs_handler_clone = fs_handler.clone(); - let handler = Rc::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(e) = fs_handler_clone.lock().unwrap().process_queue() { error!("Failed to process fuse msg, {:?}", e); } - None }); - notifiers.push(EventNotifier::new( NotifierOperation::AddShared, fs_handler.lock().unwrap().kick_evt.as_raw_fd(), diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 9d52dbfaf..92deee342 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -32,7 +32,9 @@ use machine_manager::{ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use util::num_ops::read_u32; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -274,11 +276,12 @@ impl ConsoleHandler { impl EventNotifierHelper for ConsoleHandler { fn internal_notifiers(console_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); + let cloned_cls = console_handler.clone(); - let handler = Rc::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); cloned_cls.lock().unwrap().output_handle(); - None as Option> + None }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, @@ -289,7 +292,7 @@ impl EventNotifierHelper for ConsoleHandler { )); let cloned_cls = console_handler.clone(); - let handler = Rc::new(move |_, fd: RawFd| { + let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); Some(cloned_cls.lock().unwrap().deactivate_evt_handler()) }); diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index e5ba35962..5df740b06 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -1687,7 +1687,6 @@ impl EventNotifierHelper for GpuIoHandler { if let Err(e) = gpu_handler_clone.lock().unwrap().ctrl_queue_evt_handler() { error!("Failed to process queue for virtio gpu, err: {:?}", e,); } - None }); notifiers.push(EventNotifier::new( @@ -1705,7 +1704,6 @@ impl EventNotifierHelper for GpuIoHandler { if let Err(e) = gpu_handler_clone.lock().unwrap().cursor_queue_evt_handler() { error!("Failed to process queue for virtio gpu, err: {:?}", e,); } - None }); notifiers.push(EventNotifier::new( diff --git a/virtio/src/net.rs b/virtio/src/net.rs index e750112b5..5708cd170 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -622,6 +622,8 @@ impl NetCtrlHandler { impl EventNotifierHelper for NetCtrlHandler { fn internal_notifiers(net_io: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let locked_net_io = net_io.lock().unwrap(); let cloned_net_io = net_io.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { @@ -640,10 +642,8 @@ impl EventNotifierHelper for NetCtrlHandler { }); None }); - let mut notifiers = Vec::new(); - let ctrl_fd = locked_net_io.ctrl.queue_evt.as_raw_fd(); notifiers.push(build_event_notifier( - ctrl_fd, + locked_net_io.ctrl.queue_evt.as_raw_fd(), Some(handler), NotifierOperation::AddShared, EventSet::IN, diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 8ba5a69dd..dc73cebd3 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -186,11 +186,9 @@ impl EventNotifierHelper for RngHandler { let rng_handler_clone = rng_handler.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(ref e) = rng_handler_clone.lock().unwrap().process_queue() { error!("Failed to process queue for virtio rng, err: {:?}", e,); } - None }); notifiers.push(EventNotifier::new( @@ -220,18 +218,14 @@ impl EventNotifierHelper for RngHandler { let rng_handler_clone = rng_handler.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - if let Some(leak_bucket) = rng_handler_clone.lock().unwrap().leak_bucket.as_mut() { leak_bucket.clear_timer(); } - if let Err(ref e) = rng_handler_clone.lock().unwrap().process_queue() { error!("Failed to process queue for virtio rng, err: {:?}", e,); } - None }); - notifiers.push(EventNotifier::new( NotifierOperation::AddShared, lb.as_raw_fd(), diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index ece053b34..4dabb1326 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -722,6 +722,8 @@ impl ScsiCtrlHandler { impl EventNotifierHelper for ScsiCtrlHandler { fn internal_notifiers(handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -735,10 +737,7 @@ impl EventNotifierHelper for ScsiCtrlHandler { .unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {}.", e)); None }); - - let mut notifiers = Vec::new(); - let ctrl_fd = h_locked.queue_evt.as_raw_fd(); - notifiers.push(build_event_notifier(ctrl_fd, h)); + notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -770,6 +769,8 @@ pub struct ScsiEventHandler { impl EventNotifierHelper for ScsiEventHandler { fn internal_notifiers(handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -783,10 +784,7 @@ impl EventNotifierHelper for ScsiEventHandler { .unwrap_or_else(|e| error!("Failed to handle event queue, err is {}", e)); None }); - - let mut notifiers = Vec::new(); - let event_fd = h_locked.queue_evt.as_raw_fd(); - notifiers.push(build_event_notifier(event_fd, h)); + notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -847,6 +845,8 @@ pub struct ScsiCmdHandler { impl EventNotifierHelper for ScsiCmdHandler { fn internal_notifiers(handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -861,10 +861,7 @@ impl EventNotifierHelper for ScsiCmdHandler { None }); - - let mut notifiers = Vec::new(); - let event_fd = h_locked.queue_evt.as_raw_fd(); - notifiers.push(build_event_notifier(event_fd, h)); + notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -882,7 +879,6 @@ impl EventNotifierHelper for ScsiCmdHandler { if h_lock.device_broken.load(Ordering::SeqCst) { return None; } - if let Some(aio) = &mut h_lock.aio { if let Err(ref e) = aio.handle() { error!("Failed to handle aio, {:?}", e); @@ -895,7 +891,6 @@ impl EventNotifierHelper for ScsiCmdHandler { } None }); - notifiers.push(build_event_notifier(aio.fd.as_raw_fd(), h)); } diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index bf00b40f6..40be6fb64 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -495,12 +495,10 @@ impl EventNotifierHelper for VhostIoHandler { let vhost = vhost_handler.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - let locked_vhost_handler = vhost.lock().unwrap(); if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { return None; } - for host_notify in locked_vhost_handler.host_notifies.iter() { if let Err(e) = (locked_vhost_handler.interrupt_cb)( &VirtioInterruptType::Vring, @@ -513,7 +511,6 @@ impl EventNotifierHelper for VhostIoHandler { ); } } - None as Option> }); for host_notify in vhost_handler.lock().unwrap().host_notifies.iter() { diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 1cf573a36..b27108774 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -126,28 +126,24 @@ fn vhost_user_reconnect(client: &Arc>) { impl EventNotifierHelper for VhostUserClient { fn internal_notifiers(client_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); - let mut handlers = Vec::new(); let cloned_client = client_handler.clone(); - let handler: Rc Option>> = - Rc::new(move |event, _| { - if event & EventSet::HANG_UP == EventSet::HANG_UP { - let mut locked_client = cloned_client.lock().unwrap(); - if let Err(e) = locked_client.delete_event() { - error!("Failed to delete vhost-user client event, {:?}", e); - } - if !locked_client.reconnecting { - locked_client.reconnecting = true; - drop(locked_client); - vhost_user_reconnect(&cloned_client); - } - None - } else { - None + let handler: Rc = Rc::new(move |event, _| { + if event & EventSet::HANG_UP == EventSet::HANG_UP { + let mut locked_client = cloned_client.lock().unwrap(); + if let Err(e) = locked_client.delete_event() { + error!("Failed to delete vhost-user client event, {:?}", e); } - }); - handlers.push(handler); - + if !locked_client.reconnecting { + locked_client.reconnecting = true; + drop(locked_client); + vhost_user_reconnect(&cloned_client); + } + None + } else { + None + } + }); let locked_client = client_handler.lock().unwrap(); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, @@ -160,7 +156,7 @@ impl EventNotifierHelper for VhostUserClient { .get_stream_raw_fd(), None, EventSet::HANG_UP, - handlers, + vec![handler], )); // Register event notifier for delete_evt. diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 8d133b23f..21117e8a1 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -73,9 +73,7 @@ impl EventNotifierHelper for VhostUserFsHandler { let vhost_user = vhost_user_handler.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - let locked_vhost_user = vhost_user.lock().unwrap(); - for host_notify in locked_vhost_user.host_notifies.iter() { if let Err(e) = (locked_vhost_user.interrup_cb)( &VirtioInterruptType::Vring, @@ -88,7 +86,6 @@ impl EventNotifierHelper for VhostUserFsHandler { ); } } - None as Option> }); for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 88b6f41c8..aafc17386 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -38,7 +38,9 @@ use std::{ }; use util::{ bitmap::Bitmap, - loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}, + loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + }, }; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -989,29 +991,26 @@ impl EventNotifierHelper for ClientIoHandler { // Register event notifier for read. let client_io = client_io_handler.clone(); - let handler: Rc Option>> = - Rc::new(move |event, _fd: RawFd| { - let mut locked_client_io = client_io.lock().unwrap(); - let client = locked_client_io.client.clone(); - if event & EventSet::ERROR == EventSet::ERROR - || event & EventSet::HANG_UP == EventSet::HANG_UP - || event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP - { + let handler: Rc = Rc::new(move |event, _fd: RawFd| { + let mut locked_client_io = client_io.lock().unwrap(); + let client = locked_client_io.client.clone(); + if event & EventSet::ERROR == EventSet::ERROR + || event & EventSet::HANG_UP == EventSet::HANG_UP + || event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP + { + client.conn_state.lock().unwrap().dis_conn = true; + } else if event & EventSet::IN == EventSet::IN { + if let Err(_e) = locked_client_io.client_handle_read() { client.conn_state.lock().unwrap().dis_conn = true; - } else if event & EventSet::IN == EventSet::IN { - if let Err(_e) = locked_client_io.client_handle_read() { - client.conn_state.lock().unwrap().dis_conn = true; - } - } - - // Do disconnection event. - if client.conn_state.lock().unwrap().is_disconnect() { - client.disconn_evt.lock().unwrap().write(1).unwrap(); } - drop(locked_client_io); - - None - }); + } + // Do disconnection event. + if client.conn_state.lock().unwrap().is_disconnect() { + client.disconn_evt.lock().unwrap().write(1).unwrap(); + } + drop(locked_client_io); + None + }); let client_io = client_io_handler.clone(); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, @@ -1024,21 +1023,18 @@ impl EventNotifierHelper for ClientIoHandler { // Register event notifier for write. let client_io = client_io_handler.clone(); let client = client_io.lock().unwrap().client.clone(); - let handler: Rc Option>> = - Rc::new(move |_event, fd| { - read_fd(fd); - let mut locked_client_io = client_io.lock().unwrap(); - let client = locked_client_io.client.clone(); - locked_client_io.client_handle_write(); - - // do disconnection event. - if client.conn_state.lock().unwrap().dis_conn { - client.disconn_evt.lock().unwrap().write(1).unwrap(); - } - drop(locked_client_io); - - None - }); + let handler: Rc = Rc::new(move |_event, fd| { + read_fd(fd); + let mut locked_client_io = client_io.lock().unwrap(); + let client = locked_client_io.client.clone(); + locked_client_io.client_handle_write(); + // do disconnection event. + if client.conn_state.lock().unwrap().dis_conn { + client.disconn_evt.lock().unwrap().write(1).unwrap(); + } + drop(locked_client_io); + None + }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, client.write_fd.lock().unwrap().as_raw_fd(), @@ -1049,25 +1045,22 @@ impl EventNotifierHelper for ClientIoHandler { // Register event for disconnect. let client_io = client_io_handler.clone(); - let handler: Rc Option>> = - Rc::new(move |_event, fd| { - read_fd(fd); - // Drop client info from vnc server. - let mut locked_client_io = client_io.lock().unwrap(); - let client = locked_client_io.client.clone(); - let addr = client.addr.clone(); - let server = locked_client_io.server.clone(); - let notifiers = locked_client_io.disconn_evt_handler(); - // Shutdown stream. - if let Err(e) = locked_client_io.stream.shutdown(Shutdown::Both) { - error!("Shutdown stream failed: {}", e); - } - drop(locked_client_io); - server.client_handlers.lock().unwrap().remove(&addr); - - Some(notifiers) - }); - + let handler: Rc = Rc::new(move |_event, fd| { + read_fd(fd); + // Drop client info from vnc server. + let mut locked_client_io = client_io.lock().unwrap(); + let client = locked_client_io.client.clone(); + let addr = client.addr.clone(); + let server = locked_client_io.server.clone(); + let notifiers = locked_client_io.disconn_evt_handler(); + // Shutdown stream. + if let Err(e) = locked_client_io.stream.shutdown(Shutdown::Both) { + error!("Shutdown stream failed: {}", e); + } + drop(locked_client_io); + server.client_handlers.lock().unwrap().remove(&addr); + Some(notifiers) + }); let client = client_io_handler.lock().unwrap().client.clone(); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 3bbe63563..7e4de9b84 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -45,7 +45,9 @@ use std::{ }; use util::{ bitmap::Bitmap, - loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}, + loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + }, pixman::{ pixman_format_bpp, pixman_format_code_t, pixman_image_composite, pixman_image_create_bits, pixman_image_t, pixman_op_t, @@ -117,32 +119,27 @@ impl EventNotifierHelper for VncConnHandler { let vnc_io_clone = vnc_io.clone(); let server = vnc_io.lock().unwrap().server.clone(); // Register event notifier for connection. - let handler: Rc Option>> = - Rc::new(move |_event, fd: RawFd| { - read_fd(fd); - match vnc_io_clone.clone().lock().unwrap().listener.accept() { - Ok((stream, addr)) => { - if let Err(e) = handle_connection(&server, stream, addr) { - error!("{:?}", e); - } - } - Err(e) => { - error!("Connect failed: {:?}", e); + let handler: Rc = Rc::new(move |_event, fd: RawFd| { + read_fd(fd); + match vnc_io_clone.clone().lock().unwrap().listener.accept() { + Ok((stream, addr)) => { + if let Err(e) = handle_connection(&server, stream, addr) { + error!("{:?}", e); } } - - None as Option> - }); - let notifiers = vec![ - (EventNotifier::new( - NotifierOperation::AddShared, - vnc_io.lock().unwrap().listener.as_raw_fd(), - None, - EventSet::IN, - vec![handler], - )), - ]; - notifiers + Err(e) => { + error!("Connect failed: {:?}", e); + } + } + None + }); + vec![EventNotifier::new( + NotifierOperation::AddShared, + vnc_io.lock().unwrap().listener.as_raw_fd(), + None, + EventSet::IN, + vec![handler], + )] } } -- Gitee From 818c7486fb00be1d804913aa48d06140c4a48b69 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 1 Jan 2023 00:58:19 +0800 Subject: [PATCH 0670/2187] notifier: Wrap event status with mutex lock The event status is accessed in different threads, should wrap it with lock. This makes sure parked/removed notifiers are not executed at all. This also means that notifier handlers must NOT update status of itself, otherwise it causes dead lock. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 1f1a1e49e..30da92644 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -53,8 +53,12 @@ enum EventStatus { Alive = 0, /// Event is parked, temporarily not monitored. Parked = 1, + /// Event is removed, thus not monitored. + Removed = 2, } +// The NotifierCallback must NOT update notifier status of itself, otherwise causes +// deadlock. Instead it should return notifiers and let caller to do so. pub type NotifierCallback = dyn Fn(EventSet, RawFd) -> Option>; /// Epoll Event Notifier Entry. @@ -72,7 +76,7 @@ pub struct EventNotifier { /// Pre-polling handler pub handler_poll: Option>, /// Event status - status: EventStatus, + status: Arc>, } impl fmt::Debug for EventNotifier { @@ -104,7 +108,7 @@ impl EventNotifier { event, handlers, handler_poll: None, - status: EventStatus::Alive, + status: Arc::new(Mutex::new(EventStatus::Alive)), } } } @@ -209,7 +213,7 @@ impl EventLoopContext { notifier.event |= event.event; } notifier.handlers.append(&mut event.handlers); - if notifier.status == EventStatus::Parked { + if *notifier.status.lock().unwrap() == EventStatus::Parked { warn!("Parked event updated!"); } return Ok(()); @@ -228,7 +232,7 @@ impl EventLoopContext { if let Some(parked) = events_map.get_mut(&parked_fd) { self.epoll .ctl(ControlOperation::Delete, parked_fd, EpollEvent::default())?; - parked.status = EventStatus::Parked; + *parked.status.lock().unwrap() = EventStatus::Parked; } else { return Err(anyhow!(UtilError::NoParkedFd(parked_fd))); } @@ -244,7 +248,7 @@ impl EventLoopContext { match events_map.get(&event.raw_fd) { Some(notifier) => { // No need to delete fd if status is Parked, it's done in park_event. - if notifier.status == EventStatus::Alive { + if *notifier.status.lock().unwrap() == EventStatus::Alive { if let Err(error) = self.epoll.ctl( ControlOperation::Delete, notifier.raw_fd, @@ -258,6 +262,7 @@ impl EventLoopContext { } let parked_fd = notifier.parked_fd; let event = events_map.remove(&event.raw_fd).unwrap(); + *event.status.lock().unwrap() = EventStatus::Removed; self.gc.write().unwrap().push(event); if let Some(parked_fd) = parked_fd { @@ -267,7 +272,7 @@ impl EventLoopContext { parked_fd, EpollEvent::new(parked.event, &**parked as *const _ as u64), )?; - parked.status = EventStatus::Alive; + *parked.status.lock().unwrap() = EventStatus::Alive; } else { return Err(anyhow!(UtilError::NoParkedFd(parked_fd))); } @@ -309,7 +314,7 @@ impl EventLoopContext { .with_context(|| { format!("Failed to park event, event fd:{}", notifier.raw_fd) })?; - notifier.status = EventStatus::Parked; + *notifier.status.lock().unwrap() = EventStatus::Parked; } _ => { return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); @@ -331,7 +336,7 @@ impl EventLoopContext { .with_context(|| { format!("Failed to resume event, event fd: {}", notifier.raw_fd) })?; - notifier.status = EventStatus::Alive; + *notifier.status.lock().unwrap() = EventStatus::Alive; } _ => { return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); @@ -393,7 +398,8 @@ impl EventLoopContext { if timeout == -1 { for _i in 0..AIO_PRFETCH_CYCLE_TIME { for notifer in self.events.read().unwrap().values() { - if notifer.status != EventStatus::Alive || notifer.handler_poll.is_none() { + let status_locked = notifer.status.lock().unwrap(); + if *status_locked != EventStatus::Alive || notifer.handler_poll.is_none() { continue; } let handler_poll = notifer.handler_poll.as_ref().unwrap(); @@ -482,8 +488,9 @@ impl EventLoopContext { let event_ptr = self.ready_events[i].data() as *const EventNotifier; &*event_ptr as &EventNotifier }; - if let EventStatus::Alive = event.status { - let mut notifiers = Vec::new(); + let mut notifiers = Vec::new(); + let status_locked = event.status.lock().unwrap(); + if *status_locked == EventStatus::Alive { for j in 0..event.handlers.len() { let handler = &event.handlers[j]; match handler(self.ready_events[i].event_set(), event.raw_fd) { @@ -493,8 +500,9 @@ impl EventLoopContext { } } } - self.update_events(notifiers)?; } + drop(status_locked); + self.update_events(notifiers)?; } self.run_timers(); @@ -541,7 +549,7 @@ mod test { None => { return None; } - Some(notifier) => Some(EventStatus::Alive == notifier.status), + Some(notifier) => Some(*notifier.status.lock().unwrap() == EventStatus::Alive), } } -- Gitee From e2911efb59623de25415a9fa783d65a02016a20b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 2 Jan 2023 17:55:10 +0800 Subject: [PATCH 0671/2187] notifier: Avoid holding gc lock for long time Drop notifiers in gc gradually to avoid holding gc lock for long time. This prepares for deactiving devices in vCPU context synchronously, which will try to hold gc lock. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 30da92644..74e620ef4 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -190,8 +190,13 @@ impl EventLoopContext { } fn clear_gc(&mut self) { - let mut gc = self.gc.write().unwrap(); - gc.clear(); + loop { + // Loop to avoid hold lock for long time. + let mut gc = self.gc.write().unwrap(); + if gc.pop().is_none() { + break; + } + } } fn add_event(&mut self, mut event: EventNotifier) -> Result<()> { -- Gitee From 7da72e0825a839fdae0f11bdc5034631fe7c969a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 2 Jan 2023 17:01:32 +0800 Subject: [PATCH 0672/2187] virtio: Deactivate devices synchronously Deactivate devices async is unsafe. For example, the guest driver may destroy virtqueue soon after deactivation then the iohandler will access destroyed virtqueue. This records rawfds of notifiers and unregister them when deactivate devices, it's a sync operation. Signed-off-by: Keqian Zhu --- devices/src/legacy/chardev.rs | 12 +-- machine/src/standard_vm/mod.rs | 11 +-- machine_manager/src/event_loop.rs | 25 ++++- machine_manager/src/socket.rs | 22 +---- util/src/loop_context.rs | 22 +++++ vhost_user_fs/src/virtio_fs.rs | 11 +-- virtio/src/balloon.rs | 73 ++------------ virtio/src/block.rs | 81 ++-------------- virtio/src/gpu.rs | 82 +++------------- virtio/src/net.rs | 153 ++++-------------------------- virtio/src/rng.rs | 65 ++----------- virtio/src/scsi/controller.rs | 127 ++++--------------------- virtio/src/vhost/kernel/mod.rs | 40 -------- virtio/src/vhost/kernel/net.rs | 30 +++--- virtio/src/vhost/kernel/vsock.rs | 20 ++-- virtio/src/vhost/user/block.rs | 8 +- virtio/src/vhost/user/client.rs | 66 +++---------- virtio/src/vhost/user/fs.rs | 22 ++--- virtio/src/vhost/user/net.rs | 27 +++--- vnc/src/client.rs | 32 ++----- 20 files changed, 197 insertions(+), 732 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 26bb09992..d38f4c9db 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -26,7 +26,9 @@ use machine_manager::{ config::{ChardevConfig, ChardevType}, temp_cleaner::TempCleaner, }; -use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; +use util::loop_context::{ + gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; use util::set_termi_raw_mode; use util::unix::limit_permission; use vmm_sys_util::epoll::EventSet; @@ -246,13 +248,7 @@ fn get_notifier_handler( cloned_chardev.lock().unwrap().input = None; cloned_chardev.lock().unwrap().output = None; cloned_chardev.lock().unwrap().stream_fd = None; - Some(vec![EventNotifier::new( - NotifierOperation::Delete, - stream_fd, - Some(listener_fd), - EventSet::IN | EventSet::HANG_UP, - Vec::new(), - )]) + Some(gen_delete_notifiers(&[stream_fd])) } else { None } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 5fcf9ae8f..6b8a2903b 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -219,19 +219,14 @@ trait StdMachineOps: AcpiBuilder { shutdown_req: &EventFd, clone_vm: Arc>, ) -> MachineResult<()> { + use util::loop_context::gen_delete_notifiers; + let shutdown_req = shutdown_req.try_clone().unwrap(); let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { let _ret = shutdown_req.read().unwrap(); StdMachine::handle_shutdown_request(&clone_vm); - let notifiers = vec![EventNotifier::new( - NotifierOperation::Delete, - shutdown_req_fd, - None, - EventSet::IN, - Vec::new(), - )]; - Some(notifiers) + Some(gen_delete_notifiers(&[shutdown_req_fd])) }); let notifier = EventNotifier::new( NotifierOperation::AddShared, diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index e4c077132..fd0f3908a 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; +use std::os::unix::prelude::RawFd; use std::sync::{Arc, Mutex}; use std::{process, thread}; @@ -20,7 +21,9 @@ use crate::qmp::qmp_schema::IothreadInfo; use anyhow::bail; use log::info; -use util::loop_context::{EventLoopContext, EventLoopManager, EventNotifier}; +use util::loop_context::{ + gen_delete_notifiers, get_notifiers_fds, EventLoopContext, EventLoopManager, EventNotifier, +}; /// This struct used to manage all events occur during VM lifetime. /// # Notes @@ -151,3 +154,23 @@ impl EventLoop { } } } + +pub fn register_event_helper( + notifiers: Vec, + ctx_name: Option<&String>, + record_evts: &mut Vec, +) -> util::Result<()> { + let mut notifiers_fds = get_notifiers_fds(¬ifiers); + EventLoop::update_event(notifiers, ctx_name)?; + record_evts.append(&mut notifiers_fds); + Ok(()) +} + +pub fn unregister_event_helper( + ctx_name: Option<&String>, + record_evts: &mut Vec, +) -> util::Result<()> { + EventLoop::update_event(gen_delete_notifiers(record_evts), ctx_name)?; + record_evts.clear(); + Ok(()) +} diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index fb924ecc8..d58d8befb 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -21,7 +21,8 @@ use anyhow::{bail, Result}; use log::{error, info}; use util::leak_bucket::LeakBucket; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; use vmm_sys_util::epoll::EventSet; @@ -208,26 +209,9 @@ impl Socket { if event & EventSet::HANG_UP == EventSet::HANG_UP { let socket_mutexed = shared_socket.lock().unwrap(); let stream_fd = socket_mutexed.get_stream_fd(); - let listener_fd = socket_mutexed.get_listener_fd(); QmpChannel::unbind(); - - Some(vec![ - EventNotifier::new( - NotifierOperation::Delete, - stream_fd, - Some(listener_fd), - EventSet::IN | EventSet::HANG_UP, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - leak_bucket_fd, - None, - EventSet::IN, - Vec::new(), - ), - ]) + Some(gen_delete_notifiers(&[stream_fd, leak_bucket_fd])) } else { None } diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 74e620ef4..ff0b7baf6 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -123,6 +123,28 @@ pub trait EventNotifierHelper { fn internal_notifiers(_: Arc>) -> Vec; } +pub fn get_notifiers_fds(notifiers: &[EventNotifier]) -> Vec { + let mut fds = Vec::with_capacity(notifiers.len()); + for notifier in notifiers { + fds.push(notifier.raw_fd); + } + fds +} + +pub fn gen_delete_notifiers(fds: &[RawFd]) -> Vec { + let mut notifiers = Vec::with_capacity(fds.len()); + for fd in fds { + notifiers.push(EventNotifier::new( + NotifierOperation::Delete, + *fd, + None, + EventSet::IN, + Vec::new(), + )); + } + notifiers +} + /// EventLoop manager, advise continue running or stop running pub trait EventLoopManager: Send + Sync { fn loop_should_exit(&self) -> bool; diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index d41343285..f92dae78c 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -35,7 +35,8 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; use super::fs::FileSystem; @@ -114,13 +115,7 @@ impl FsIoHandler { } fn delete_notifiers(&self) -> Vec { - vec![EventNotifier::new( - NotifierOperation::Delete, - self.kick_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )] + gen_delete_notifiers(&[self.kick_evt.as_raw_fd()]) } } diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 79062e301..75d0dd8a3 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -26,9 +26,9 @@ use address_space::{ }; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ - config::BalloonConfig, event, event_loop::EventLoop, qmp::qmp_schema::BalloonInfo, - qmp::QmpChannel, + config::BalloonConfig, event, qmp::qmp_schema::BalloonInfo, qmp::QmpChannel, }; use util::{ bitmap::Bitmap, @@ -514,8 +514,6 @@ struct BalloonIoHandler { report_queue: Option>>, /// Reporting EventFd. report_evt: Option, - /// EventFd for device deactivate - deactivate_evt: EventFd, /// Device is broken or not. device_broken: Arc, /// The interrupt call back function. @@ -626,41 +624,6 @@ impl BalloonIoHandler { fn get_balloon_memory_size(&self) -> u64 { (self.balloon_actual.load(Ordering::Acquire) as u64) << VIRTIO_BALLOON_PFN_SHIFT } - - fn deactivate_evt_handler(&self) -> Vec { - let notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.inf_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.def_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.event_timer.clone().lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - - notifiers - } } /// Create a new EventNotifier. @@ -753,17 +716,6 @@ impl EventNotifierHelper for BalloonIoHandler { notifiers.push(build_event_notifier(report_evt.as_raw_fd(), handler)); } - // register event notifier for reset event. - let cloned_balloon_io = balloon_io.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(cloned_balloon_io.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier( - locked_balloon_io.deactivate_evt.as_raw_fd(), - handler, - )); - // register event notifier for timer event. let cloned_balloon_io = balloon_io.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { @@ -808,7 +760,7 @@ pub struct Balloon { /// Event timer for BALLOON_CHANGED event. event_timer: Arc>, /// EventFd for device deactivate. - deactivate_evt: EventFd, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, } @@ -837,7 +789,7 @@ impl Balloon { mem_info: Arc::new(Mutex::new(BlnMemInfo::new(mem_share))), mem_space, event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } } @@ -1071,7 +1023,6 @@ impl VirtioDevice for Balloon { def_evt, report_queue, report_evt, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), device_broken: self.broken.clone(), interrupt_cb, mem_info: self.mem_info.clone(), @@ -1079,20 +1030,16 @@ impl VirtioDevice for Balloon { balloon_actual: self.actual.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), - None, - ) - .with_context(|| "Failed to register balloon event notifier to MainLoop")?; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper(notifiers, None, &mut self.deactivate_evts) + .with_context(|| "Failed to register balloon event notifier to MainLoop")?; self.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + unregister_event_helper(None, &mut self.deactivate_evts) } fn update_config( @@ -1235,7 +1182,6 @@ mod tests { let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); - assert!(bln.deactivate().is_ok()); assert!(bln.update_config(None).is_err()); } @@ -1347,7 +1293,6 @@ mod tests { let event_inf = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let event_def = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let event_deactivate = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut handler = BalloonIoHandler { driver_features: bln.driver_features, @@ -1358,7 +1303,6 @@ mod tests { def_evt: event_def, report_queue: None, report_evt: None, - deactivate_evt: event_deactivate.try_clone().unwrap(), device_broken: bln.broken.clone(), interrupt_cb: cb.clone(), mem_info: bln.mem_info.clone(), @@ -1552,7 +1496,6 @@ mod tests { let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); - assert!(bln.deactivate().is_ok()); assert!(bln.update_config(None).is_err()); } } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 51e59ece5..1e3d69ed6 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -35,11 +35,8 @@ use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, warn}; -use machine_manager::config::{DriveFile, VmConfig}; -use machine_manager::{ - config::{BlkDevConfig, ConfigCheck}, - event_loop::EventLoop, -}; +use machine_manager::config::{BlkDevConfig, ConfigCheck, DriveFile, VmConfig}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use migration::{ migration::Migratable, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer, @@ -407,8 +404,6 @@ struct BlockIoHandler { receiver: Receiver, /// Eventfd for config space update. update_evt: EventFd, - /// Eventfd for device deactivate. - deactivate_evt: EventFd, /// Device is broken or not. device_broken: Arc, /// Callback to trigger an interrupt. @@ -681,49 +676,6 @@ impl BlockIoHandler { error!("Failed to handle block IO for updating handler {:?}", e); } } - - fn deactivate_evt_handler(&mut self) -> Vec { - let mut notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.update_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.aio.fd.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - if let Some(lb) = self.leak_bucket.as_ref() { - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - lb.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )); - } - notifiers - } } fn build_event_notifier( @@ -764,18 +716,6 @@ impl EventNotifierHelper for BlockIoHandler { None, )); - // Register event notifier for deactivate_evt. - let h_clone = handler.clone(); - let h: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(h_clone.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier( - handler_raw.deactivate_evt.as_raw_fd(), - vec![h], - None, - )); - // Register event notifier for queue_evt. let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -955,7 +895,7 @@ pub struct Block { /// Eventfd for config space update. update_evts: Vec, /// Eventfd for device deactivate. - deactivate_evts: Vec, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, /// Drive backend files. @@ -1140,7 +1080,6 @@ impl VirtioDevice for Block { senders.push(sender); let update_evt = EventFd::new(libc::EFD_NONBLOCK)?; - let deactivate_evt = EventFd::new(libc::EFD_NONBLOCK)?; let engine = self.blk_cfg.aio.as_ref(); let handler = BlockIoHandler { queue: queue.clone(), @@ -1155,19 +1094,19 @@ impl VirtioDevice for Block { driver_features: self.state.driver_features, receiver, update_evt: update_evt.try_clone()?, - deactivate_evt: deactivate_evt.try_clone()?, device_broken: self.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper( + notifiers, self.blk_cfg.iothread.as_ref(), + &mut self.deactivate_evts, )?; self.update_evts.push(update_evt); - self.deactivate_evts.push(deactivate_evt); } self.senders = Some(senders); @@ -1177,11 +1116,7 @@ impl VirtioDevice for Block { } fn deactivate(&mut self) -> Result<()> { - for evt in &self.deactivate_evts { - evt.write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - } - self.deactivate_evts.clear(); + unregister_event_helper(self.blk_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; self.update_evts.clear(); Ok(()) } diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 5df740b06..933069832 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -19,7 +19,7 @@ use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; -use machine_manager::event_loop::EventLoop; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc}; use migration_derive::{ByteCode, Desc}; use std::cmp; @@ -496,8 +496,6 @@ struct GpuIoHandler { ctrl_queue_evt: EventFd, /// Eventfd for cursor virtqueue. cursor_queue_evt: EventFd, - /// Eventfd for device deactivate. - deactivate_evt: RawFd, /// Callback to trigger an interrupt. interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. @@ -1628,32 +1626,13 @@ impl GpuIoHandler { Ok(()) } +} - fn deactivate_evt_handler(&mut self) -> Vec { - let notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.ctrl_queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.cursor_queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt, - None, - EventSet::IN, - Vec::new(), - ), - ]; - notifiers +impl Drop for GpuIoHandler { + fn drop(&mut self) { + for scanout in &self.scanouts { + console_close(scanout.con_id); + } } } @@ -1661,25 +1640,6 @@ impl EventNotifierHelper for GpuIoHandler { fn internal_notifiers(gpu_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); - // Register event notifier for deactivate_evt. - let gpu_handler_clone = gpu_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - // Deactivate console. - let mut locked_handler = gpu_handler_clone.lock().unwrap(); - for scanout in &locked_handler.scanouts { - console_close(scanout.con_id); - } - Some(locked_handler.deactivate_evt_handler()) - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - gpu_handler.lock().unwrap().deactivate_evt, - None, - EventSet::IN, - vec![handler], - )); - // Register event notifier for ctrl_queue_evt. let gpu_handler_clone = gpu_handler.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { @@ -1734,6 +1694,7 @@ pub struct GpuState { } /// GPU device structure. +#[derive(Default)] pub struct Gpu { /// Configuration of the GPU device. gpu_conf: GpuConfig, @@ -1742,18 +1703,7 @@ pub struct Gpu { /// Callback to trigger interrupt. interrupt_cb: Option>, /// Eventfd for device deactivate. - deactivate_evt: EventFd, -} - -impl Default for Gpu { - fn default() -> Self { - Gpu { - gpu_conf: GpuConfig::default(), - state: GpuState::default(), - interrupt_cb: None, - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - } - } + deactivate_evts: Vec, } impl Gpu { @@ -1774,7 +1724,7 @@ impl Gpu { gpu_conf, state, interrupt_cb: None, - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), } } } @@ -1891,7 +1841,6 @@ impl VirtioDevice for Gpu { mem_space, ctrl_queue_evt: queue_evts.remove(0), cursor_queue_evt: queue_evts.remove(0), - deactivate_evt: self.deactivate_evt.as_raw_fd(), interrupt_cb, driver_features: self.state.driver_features, resources_list: Vec::new(), @@ -1905,17 +1854,12 @@ impl VirtioDevice for Gpu { gpu_handler.req_states[0].width = self.state.base_conf.xres; gpu_handler.req_states[0].height = self.state.base_conf.yres; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(gpu_handler))), - None, - )?; - + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(gpu_handler))); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; Ok(()) } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + unregister_event_helper(None, &mut self.deactivate_evts) } } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 5708cd170..afd25d56c 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -43,6 +43,7 @@ use crate::{ use address_space::{AddressSpace, RegionCache}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, event_loop::EventLoop, @@ -53,6 +54,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::loop_context::gen_delete_notifiers; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -457,8 +459,6 @@ pub struct NetCtrlHandler { pub interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. pub driver_features: u64, - /// Deactivate event to delete net control handler. - pub deactivate_evt: EventFd, /// Device is broken or not. pub device_broken: Arc, } @@ -597,27 +597,6 @@ impl NetCtrlHandler { Ok(()) } - - fn deactivate_evt_handler(&mut self) -> Vec { - let notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.ctrl.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - - notifiers - } } impl EventNotifierHelper for NetCtrlHandler { @@ -649,19 +628,6 @@ impl EventNotifierHelper for NetCtrlHandler { EventSet::IN, )); - // Register event notifier for deactivate_evt. - let cloned_net_io = net_io.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(cloned_net_io.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier( - locked_net_io.deactivate_evt.as_raw_fd(), - Some(handler), - NotifierOperation::AddShared, - EventSet::IN, - )); - notifiers } } @@ -703,7 +669,6 @@ struct NetIoHandler { driver_features: u64, receiver: Receiver, update_evt: EventFd, - deactivate_evt: EventFd, device_broken: Arc, is_listening: bool, ctrl_info: Arc>, @@ -960,84 +925,20 @@ impl NetIoHandler { locked_net_io.tap_fd = tap.as_raw_fd(); } - let mut notifiers = vec![ - build_event_notifier( - locked_net_io.update_evt.as_raw_fd(), - None, - NotifierOperation::Delete, - EventSet::IN, - ), - build_event_notifier( - locked_net_io.rx.queue_evt.as_raw_fd(), - None, - NotifierOperation::Delete, - EventSet::IN, - ), - build_event_notifier( - locked_net_io.tx.queue_evt.as_raw_fd(), - None, - NotifierOperation::Delete, - EventSet::IN, - ), + let mut notifiers_fds = vec![ + locked_net_io.update_evt.as_raw_fd(), + locked_net_io.rx.queue_evt.as_raw_fd(), + locked_net_io.tx.queue_evt.as_raw_fd(), ]; if old_tap_fd != -1 { - notifiers.push(build_event_notifier( - old_tap_fd, - None, - NotifierOperation::Delete, - EventSet::IN, - )); + notifiers_fds.push(old_tap_fd); } + let mut notifiers = gen_delete_notifiers(¬ifiers_fds); drop(locked_net_io); notifiers.append(&mut EventNotifierHelper::internal_notifiers(net_io.clone())); notifiers } - - fn deactivate_evt_handler(&mut self) -> Vec { - let mut notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.update_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.rx.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.tx.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - if self.tap_fd != -1 { - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - self.tap_fd, - None, - EventSet::IN, - Vec::new(), - )); - self.tap_fd = -1; - } - - notifiers - } } fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { @@ -1092,19 +993,6 @@ impl EventNotifierHelper for NetIoHandler { EventSet::IN, )]; - // Register event notifier for deactivate_evt. - let cloned_net_io = net_io.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(cloned_net_io.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier( - locked_net_io.deactivate_evt.as_raw_fd(), - Some(handler), - NotifierOperation::AddShared, - EventSet::IN, - )); - // Register event notifier for rx. let cloned_net_io = net_io.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { @@ -1235,7 +1123,7 @@ pub struct Net { /// Eventfd for config space update. update_evt: EventFd, /// Eventfd for device deactivate. - deactivate_evt: EventFd, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, /// The information about control command. @@ -1250,7 +1138,7 @@ impl Default for Net { state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, } @@ -1265,7 +1153,7 @@ impl Net { state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, } @@ -1616,13 +1504,15 @@ impl VirtioDevice for Net { mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.lock().unwrap().driver_features, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); + register_event_helper( + notifiers, self.net_cfg.iothread.as_ref(), + &mut self.deactivate_evts, )?; } @@ -1656,7 +1546,6 @@ impl VirtioDevice for Net { driver_features: self.state.lock().unwrap().driver_features, receiver, update_evt: self.update_evt.try_clone().unwrap(), - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), device_broken: self.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), @@ -1665,9 +1554,11 @@ impl VirtioDevice for Net { handler.tap_fd = tap.as_raw_fd(); } - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper( + notifiers, self.net_cfg.iothread.as_ref(), + &mut self.deactivate_evts, )?; } self.senders = Some(senders); @@ -1727,9 +1618,7 @@ impl VirtioDevice for Net { } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts) } fn reset(&mut self) -> Result<()> { diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index dc73cebd3..3127c68cb 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -18,6 +18,7 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{config::RngConfig, event_loop::EventLoop}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use util::aio::raw_read; @@ -58,7 +59,6 @@ fn get_req_data_size(in_iov: &[ElemIovec]) -> Result { struct RngHandler { queue: Arc>, queue_evt: EventFd, - deactivate_evt: RawFd, interrupt_cb: Arc, driver_features: u64, mem_space: Arc, @@ -146,36 +146,6 @@ impl RngHandler { Ok(()) } - - fn deactivate_evt_handler(&self) -> Vec { - let mut notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt, - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - if let Some(lb) = self.leak_bucket.as_ref() { - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - lb.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )); - } - - notifiers - } } impl EventNotifierHelper for RngHandler { @@ -199,20 +169,6 @@ impl EventNotifierHelper for RngHandler { vec![handler], )); - // Register event notifier for deactivate_evt - let rng_handler_clone = rng_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(rng_handler_clone.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - rng_handler.lock().unwrap().deactivate_evt, - None, - EventSet::IN, - vec![handler], - )); - // Register timer event notifier for the limit of request bytes per second if let Some(lb) = rng_handler.lock().unwrap().leak_bucket.as_ref() { let rng_handler_clone = rng_handler.clone(); @@ -259,7 +215,7 @@ pub struct Rng { /// The state of Rng device. state: RngState, /// Eventfd for device deactivate - deactivate_evt: EventFd, + deactivate_evts: Vec, } impl Rng { @@ -271,7 +227,7 @@ impl Rng { device_features: 0, driver_features: 0, }, - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), } } @@ -366,7 +322,6 @@ impl VirtioDevice for Rng { let handler = RngHandler { queue: queues[0].clone(), queue_evt: queue_evts.remove(0), - deactivate_evt: self.deactivate_evt.as_raw_fd(), interrupt_cb, driver_features: self.state.driver_features, mem_space, @@ -379,18 +334,14 @@ impl VirtioDevice for Rng { leak_bucket: self.rng_cfg.bytes_per_sec.map(LeakBucket::new), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), - None, - )?; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; Ok(()) } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + unregister_event_helper(None, &mut self.deactivate_evts) } } @@ -612,11 +563,9 @@ mod tests { queue_config.ready = true; let file = TempFile::new().unwrap(); - let reset_event = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut rng_handler = RngHandler { queue: Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap())), queue_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: reset_event.as_raw_fd(), interrupt_cb, driver_features: 0_u64, mem_space: mem_space.clone(), @@ -697,11 +646,9 @@ mod tests { queue_config.ready = true; let file = TempFile::new().unwrap(); - let reset_event = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let mut rng_handler = RngHandler { queue: Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap())), queue_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - deactivate_evt: reset_event.as_raw_fd(), interrupt_cb, driver_features: 0_u64, mem_space: mem_space.clone(), diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 4dabb1326..feb5bd6af 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -33,6 +33,7 @@ use crate::ScsiBus::{ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use log::{debug, error, info}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ config::{ConfigCheck, ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, @@ -144,7 +145,7 @@ pub struct ScsiCntlr { /// Scsi bus. pub bus: Option>>, /// Eventfd for Scsi Controller deactivates. - deactivate_evt: EventFd, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, } @@ -155,7 +156,7 @@ impl ScsiCntlr { config, state: ScsiCntlrState::default(), bus: None, - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } } @@ -280,15 +281,16 @@ impl VirtioDevice for ScsiCntlr { let ctrl_handler = ScsiCtrlHandler { queue: ctrl_queue, queue_evt: ctrl_queue_evt, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); + register_event_helper( + notifiers, self.config.iothread.as_ref(), + &mut self.deactivate_evts, )?; let event_queue = queues[1].clone(); @@ -296,15 +298,17 @@ impl VirtioDevice for ScsiCntlr { let event_handler = ScsiEventHandler { _queue: event_queue, queue_evt: event_queue_evt, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), _mem_space: mem_space.clone(), _interrupt_cb: interrupt_cb.clone(), _driver_features: self.state.driver_features, device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))), + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))); + register_event_helper( + notifiers, self.config.iothread.as_ref(), + &mut self.deactivate_evts, )?; let queues_num = queues.len(); @@ -315,7 +319,6 @@ impl VirtioDevice for ScsiCntlr { scsibus: bus.clone(), queue: cmd_queue.clone(), queue_evt: queue_evts.remove(0), - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, @@ -324,9 +327,12 @@ impl VirtioDevice for ScsiCntlr { cmd_handler.aio = Some(cmd_handler.build_aio()?); - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))), + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))); + register_event_helper( + notifiers, self.config.iothread.as_ref(), + &mut self.deactivate_evts, )?; } else { bail!("Scsi controller has no bus!"); @@ -338,9 +344,7 @@ impl VirtioDevice for ScsiCntlr { } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + unregister_event_helper(self.config.iothread.as_ref(), &mut self.deactivate_evts) } fn update_config(&mut self, _dev_config: Option>) -> Result<()> { @@ -615,8 +619,6 @@ pub struct ScsiCtrlHandler { queue: Arc>, /// EventFd for the ctrl virtqueue. queue_evt: EventFd, - /// EventFd for deleting ctrl handler. - deactivate_evt: EventFd, /// The address space to which the scsi HBA belongs. mem_space: Arc, /// The interrupt callback function. @@ -699,25 +701,6 @@ impl ScsiCtrlHandler { Ok(()) } - - fn deactivate_evt_handler(&mut self) -> Vec { - vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ] - } } impl EventNotifierHelper for ScsiCtrlHandler { @@ -739,13 +722,6 @@ impl EventNotifierHelper for ScsiCtrlHandler { }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); - let h_clone = handler.clone(); - let h: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(h_clone.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier(h_locked.deactivate_evt.as_raw_fd(), h)); - notifiers } } @@ -755,8 +731,6 @@ pub struct ScsiEventHandler { _queue: Arc>, /// EventFd for the Event virtqueue. queue_evt: EventFd, - /// EventFd for deleting event handler. - deactivate_evt: EventFd, /// The address space to which the scsi HBA belongs. _mem_space: Arc, /// The interrupt callback function. @@ -786,13 +760,6 @@ impl EventNotifierHelper for ScsiEventHandler { }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); - let h_clone = handler.clone(); - let h: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(h_clone.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier(h_locked.deactivate_evt.as_raw_fd(), h)); - notifiers } } @@ -801,25 +768,6 @@ impl ScsiEventHandler { fn handle_event(&mut self) -> Result<()> { Ok(()) } - - fn deactivate_evt_handler(&mut self) -> Vec { - vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ] - } } pub struct ScsiCmdHandler { @@ -829,8 +777,6 @@ pub struct ScsiCmdHandler { queue: Arc>, /// EventFd for the Cmd virtqueue. queue_evt: EventFd, - /// EventFd for deleting cmd handler. - deactivate_evt: EventFd, /// The address space to which the scsi HBA belongs. mem_space: Arc, /// The interrupt callback function. @@ -863,13 +809,6 @@ impl EventNotifierHelper for ScsiCmdHandler { }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); - let h_clone = handler.clone(); - let h: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(h_clone.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(build_event_notifier(h_locked.deactivate_evt.as_raw_fd(), h)); - // Register event notifier for aio. if let Some(ref aio) = h_locked.aio { let h_clone = handler.clone(); @@ -1021,36 +960,6 @@ impl ScsiCmdHandler { fn build_aio(&self) -> Result>> { Ok(Box::new(Aio::new(Arc::new(Self::complete_func), None)?)) } - - fn deactivate_evt_handler(&mut self) -> Vec { - let mut notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.queue_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - ]; - if let Some(aio) = &self.aio { - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - aio.fd.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )); - } - - notifiers - } } #[derive(Clone)] diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 40be6fb64..0d9b5088c 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -459,35 +459,9 @@ impl VhostOps for VhostBackend { pub struct VhostIoHandler { interrupt_cb: Arc, host_notifies: Vec, - deactivate_evt: EventFd, device_broken: Arc, } -impl VhostIoHandler { - fn deactivate_evt_handler(&mut self) -> Vec { - let mut notifiers = Vec::new(); - for host_notify in self.host_notifies.iter() { - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - host_notify.notify_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )); - } - - notifiers.push(EventNotifier::new( - NotifierOperation::Delete, - self.deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - )); - - notifiers - } -} - impl EventNotifierHelper for VhostIoHandler { fn internal_notifiers(vhost_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); @@ -523,20 +497,6 @@ impl EventNotifierHelper for VhostIoHandler { )); } - // Register event notifier for deactivate_evt. - let vhost = vhost_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(vhost.lock().unwrap().deactivate_evt_handler()) - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - vhost_handler.lock().unwrap().deactivate_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler], - )); - notifiers } } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 1e7006b9f..d1a3501e2 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -19,7 +19,8 @@ use std::sync::{Arc, Mutex}; use crate::error::VirtioError; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; -use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; +use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; @@ -95,7 +96,7 @@ pub struct Net { /// System address space. mem_space: Arc, /// EventFd for device deactivate. - deactivate_evt: EventFd, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, } @@ -111,7 +112,7 @@ impl Net { vhost_features: 0_u64, device_config: VirtioNetConfig::default(), mem_space: mem_space.clone(), - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } } @@ -267,13 +268,15 @@ impl VirtioDevice for Net { mem_space, interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); + register_event_helper( + notifiers, self.net_cfg.iothread.as_ref(), + &mut self.deactivate_evts, )?; } @@ -365,13 +368,14 @@ impl VirtioDevice for Net { let handler = VhostIoHandler { interrupt_cb: interrupt_cb.clone(), host_notifies, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), - None, + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper( + notifiers, + self.net_cfg.iothread.as_ref(), + &mut self.deactivate_evts, )?; } self.broken.store(false, Ordering::SeqCst); @@ -380,11 +384,7 @@ impl VirtioDevice for Net { } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - - Ok(()) + unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts) } fn reset(&mut self) -> Result<()> { diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 5e06cb89d..72952355d 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -16,7 +16,8 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; -use machine_manager::{config::VsockConfig, event_loop::EventLoop}; +use machine_manager::config::VsockConfig; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -102,7 +103,7 @@ pub struct Vsock { /// Callback to trigger interrupt. interrupt_cb: Option>, /// EventFd for device deactivate. - deactivate_evt: EventFd, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, } @@ -116,7 +117,7 @@ impl Vsock { mem_space: mem_space.clone(), event_queue: None, interrupt_cb: None, - deactivate_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } } @@ -318,25 +319,18 @@ impl VirtioDevice for Vsock { let handler = VhostIoHandler { interrupt_cb, host_notifies, - deactivate_evt: self.deactivate_evt.try_clone().unwrap(), device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), - None, - )?; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; self.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { - self.deactivate_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - - Ok(()) + unregister_event_helper(None, &mut self.deactivate_evts) } fn reset(&mut self) -> Result<()> { diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index d72651c8b..386e30ef2 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -18,9 +18,7 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use machine_manager::config::BlkDevConfig; -use machine_manager::event_loop::EventLoop; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; @@ -101,11 +99,7 @@ impl Block { "Failed to create the client which communicates with the server for vhost-user blk" })?; let client = Arc::new(Mutex::new(client)); - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(client.clone()), - None, - ) - .with_context(|| "Failed to update event for client sock")?; + VhostUserClient::add_event(&client)?; self.client = Some(client); Ok(()) } diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index b27108774..106300d25 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -20,9 +20,9 @@ use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; use log::{error, info, warn}; -use machine_manager::event_loop::EventLoop; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::time::NANOSECONDS_PER_SECOND; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -36,7 +36,6 @@ use super::message::{ use super::sock::VhostUserSock; use crate::block::VirtioBlkConfig; use crate::VhostUser::message::VhostUserConfig; -use crate::VirtioError; use anyhow::{anyhow, bail, Context, Result}; /// Vhost supports multiple queue @@ -89,7 +88,6 @@ fn vhost_user_reconnect(client: &Arc>) { }); info!("Try to reconnect vhost-user net."); - let cloned_client = client.clone(); if let Err(_e) = client .lock() .unwrap() @@ -110,9 +108,7 @@ fn vhost_user_reconnect(client: &Arc>) { } client.lock().unwrap().reconnecting = false; - if let Err(e) = - EventLoop::update_event(EventNotifierHelper::internal_notifiers(cloned_client), None) - { + if let Err(e) = VhostUserClient::add_event(client) { error!("Failed to update event for client sock, {:?}", e); } @@ -128,18 +124,15 @@ impl EventNotifierHelper for VhostUserClient { let mut notifiers = Vec::new(); let cloned_client = client_handler.clone(); - let handler: Rc = Rc::new(move |event, _| { + let handler: Rc = Rc::new(move |event, fd| { if event & EventSet::HANG_UP == EventSet::HANG_UP { let mut locked_client = cloned_client.lock().unwrap(); - if let Err(e) = locked_client.delete_event() { - error!("Failed to delete vhost-user client event, {:?}", e); - } if !locked_client.reconnecting { locked_client.reconnecting = true; drop(locked_client); vhost_user_reconnect(&cloned_client); } - None + Some(gen_delete_notifiers(&[fd])) } else { None } @@ -159,20 +152,6 @@ impl EventNotifierHelper for VhostUserClient { vec![handler], )); - // Register event notifier for delete_evt. - let cloned_client = client_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - Some(cloned_client.lock().unwrap().delete_evt_handler()) - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - locked_client.delete_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler], - )); - notifiers } } @@ -316,7 +295,7 @@ impl Listener for VhostUserMemInfo { pub struct VhostUserClient { client: Arc>, mem_info: VhostUserMemInfo, - delete_evt: EventFd, + delete_evts: Vec, queues: Vec>>, queue_evts: Vec, call_events: Vec, @@ -340,11 +319,10 @@ impl VhostUserClient { .with_context(|| "Failed to register memory for vhost user client")?; let client = Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))); - let delete_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); Ok(VhostUserClient { client, mem_info, - delete_evt, + delete_evts: Vec::new(), queues: Vec::new(), queue_evts: Vec::new(), call_events: Vec::new(), @@ -448,31 +426,15 @@ impl VhostUserClient { Ok(()) } - /// Delete the socket event in ClientInternal. - pub fn delete_event(&self) -> Result<()> { - self.delete_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - Ok(()) + pub fn add_event(client: &Arc>) -> Result<()> { + let notifiers = EventNotifierHelper::internal_notifiers(client.clone()); + register_event_helper(notifiers, None, &mut client.lock().unwrap().delete_evts) + .with_context(|| "Failed to update event for client sock") } - fn delete_evt_handler(&mut self) -> Vec { - vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.client.lock().unwrap().sock.domain.get_stream_raw_fd(), - None, - EventSet::HANG_UP, - vec![], - ), - EventNotifier::new( - NotifierOperation::Delete, - self.delete_evt.as_raw_fd(), - None, - EventSet::IN, - vec![], - ), - ] + /// Delete the socket event in ClientInternal. + pub fn delete_event(&mut self) -> Result<()> { + unregister_event_helper(None, &mut self.delete_evts) } /// Send get protocol features request to vhost. diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 21117e8a1..1bfda1feb 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -28,10 +28,8 @@ use log::error; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::AddressSpace; -use machine_manager::{ - config::{FsConfig, MAX_TAG_LENGTH}, - event_loop::EventLoop, -}; +use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -111,6 +109,7 @@ pub struct Fs { mem_space: Arc, /// The notifier events from host. call_events: Vec, + deactivate_evts: Vec, enable_irqfd: bool, } @@ -131,6 +130,7 @@ impl Fs { acked_features: 0_u64, mem_space, call_events: Vec::::new(), + deactivate_evts: Vec::new(), enable_irqfd, } } @@ -148,12 +148,7 @@ impl VirtioDevice for Fs { "Failed to create the client which communicates with the server for virtio fs" })?; let client = Arc::new(Mutex::new(client)); - - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(client.clone()), - None, - ) - .with_context(|| "Failed to update event for client sock")?; + VhostUserClient::add_event(&client)?; self.avail_features = client .lock() .unwrap() @@ -249,10 +244,8 @@ impl VirtioDevice for Fs { host_notifies, }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), - None, - )?; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; } Ok(()) @@ -272,6 +265,7 @@ impl VirtioDevice for Fs { } fn deactivate(&mut self) -> Result<()> { + unregister_event_helper(None, &mut self.deactivate_evts)?; self.call_events.clear(); Ok(()) } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 4559a1459..53483f5af 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -13,11 +13,13 @@ use crate::virtio_has_feature; use std::cmp; use std::io::Write; +use std::os::unix::prelude::RawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use machine_manager::{config::NetworkInterfaceConfig, event_loop::EventLoop}; +use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; @@ -58,7 +60,7 @@ pub struct Net { /// The notifier events from host. call_events: Vec, /// EventFd for deactivate control Queue. - de_ctrl_evt: EventFd, + deactivate_evts: Vec, /// Device is broken or not. broken: Arc, } @@ -73,7 +75,7 @@ impl Net { mem_space: mem_space.clone(), client: None, call_events: Vec::::new(), - de_ctrl_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } } @@ -90,9 +92,7 @@ impl Net { None => return Err(anyhow!("Failed to get client when stoping event")), }; if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq { - self.de_ctrl_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; } Ok(()) @@ -123,12 +123,7 @@ impl VirtioDevice for Net { "Failed to create the client which communicates with the server for vhost-user net" })?; let client = Arc::new(Mutex::new(client)); - - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(client.clone()), - None, - ) - .with_context(|| "Failed to update event for client sock")?; + VhostUserClient::add_event(&client)?; self.device_features = client .lock() @@ -250,13 +245,15 @@ impl VirtioDevice for Net { mem_space, interrupt_cb: interrupt_cb.clone(), driver_features: self.driver_features, - deactivate_evt: self.de_ctrl_evt.try_clone().unwrap(), device_broken: self.broken.clone(), }; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))), + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); + register_event_helper( + notifiers, self.net_cfg.iothread.as_ref(), + &mut self.deactivate_evts, )?; } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index aafc17386..30d85a766 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -39,7 +39,8 @@ use std::{ use util::{ bitmap::Bitmap, loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }, }; use vmm_sys_util::epoll::EventSet; @@ -956,31 +957,12 @@ impl ClientIoHandler { } fn disconn_evt_handler(&mut self) -> Vec { - let notifiers = vec![ - EventNotifier::new( - NotifierOperation::Delete, - self.stream.as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.client.write_fd.lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), - EventNotifier::new( - NotifierOperation::Delete, - self.client.disconn_evt.lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - Vec::new(), - ), + let notifiers_fds = vec![ + self.stream.as_raw_fd(), + self.client.write_fd.lock().unwrap().as_raw_fd(), + self.client.disconn_evt.lock().unwrap().as_raw_fd(), ]; - - notifiers + gen_delete_notifiers(¬ifiers_fds) } } -- Gitee From 7400253963bada8cf3b016f80db3cb401e102e4f Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 30 Dec 2022 14:57:35 +0800 Subject: [PATCH 0673/2187] VNC: Optimize data sending. When sending plaintext, if io channel returns the error of WouldBlock. Return immediately and wait for the next sending event to prevent blocking the main loop. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 16 ++++++++---- vnc/src/client.rs | 65 +++++++++++++++++++++++++++++++---------------- vnc/src/server.rs | 7 +++-- 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 11395e4e9..20dd91f3b 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::{ - client::{ClientIoHandler, APP_NAME}, + client::{vnc_write, ClientIoHandler, APP_NAME}, VncError, }; use anyhow::{anyhow, Result}; @@ -206,6 +206,7 @@ impl ClientIoHandler { } let server = self.server.clone(); + let client = self.client.clone(); let mut locked_security = server.security_type.lock().unwrap(); let err: c_int; let mut serverout: *const c_char = ptr::null_mut(); @@ -278,21 +279,24 @@ impl ClientIoHandler { if let Err(err) = self.sasl_check_ssf() { // Reject auth: the strength of ssf is too weak. auth_reject(&mut buf); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); return Err(err); } if let Err(err) = self.sasl_check_authz() { // Reject auth: wrong sasl username. auth_reject(&mut buf); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); return Err(err); } // Accpet auth. buf.append(&mut (0_u32).as_bytes().to_vec()); } - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); self.update_event_handler(1, ClientIoHandler::handle_client_init); Ok(()) } @@ -415,6 +419,7 @@ impl ClientIoHandler { let suffix = CString::new("").unwrap(); let mut mechlist: *const c_char = ptr::null_mut(); let mut locked_security = self.server.security_type.lock().unwrap(); + let client = self.client.clone(); unsafe { err = sasl_listmech( locked_security.saslconfig.sasl_conn, @@ -440,7 +445,8 @@ impl ClientIoHandler { buf.append(&mut (len as u32).to_be_bytes().to_vec()); buf.append(&mut locked_security.saslconfig.mech_list.as_bytes().to_vec()); drop(locked_security); - self.write_msg(&buf); + vnc_write(&client, buf); + self.flush(); Ok(()) } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 3e1f791f0..9538f404a 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -324,8 +324,10 @@ impl ClientIoHandler { } impl ClientIoHandler { + /// This function interacts with the client interface, it includs several + /// steps: Read the data stream from the fd, save the data in buffer, + /// and then process the data by io handle function. fn client_handle_read(&mut self) -> Result<(), anyhow::Error> { - // Read message from tcpstream. self.read_msg()?; let client = self.client.clone(); @@ -344,16 +346,38 @@ impl ClientIoHandler { Ok(()) } + /// Write a chunk of data to client socket. If there is some + /// error in io channel, then return and break the connnection. fn client_handle_write(&mut self) { let client = self.client.clone(); if client.conn_state.lock().unwrap().dis_conn { return; } + let mut locked_buffer = client.out_buffer.lock().unwrap(); while let Some(bytes) = locked_buffer.read_front_chunk() { - self.write_msg(bytes); - locked_buffer.remove_front_chunk(); + let message_len = bytes.len(); + let send_len: usize; + match self.write_msg(bytes) { + Ok(ret) => { + send_len = ret; + } + Err(_e) => { + self.client.conn_state.lock().unwrap().dis_conn = true; + return; + } + } + + locked_buffer.remove_front(send_len); + if send_len != message_len { + break; + } + } + + if !locked_buffer.is_empty() { + vnc_flush_notify(&client); } + drop(locked_buffer); } @@ -440,7 +464,7 @@ impl ClientIoHandler { /// Write buf to stream /// Choose different channel according to whether or not to encrypt - pub fn write_msg(&mut self, buf: &[u8]) { + pub fn write_msg(&mut self, buf: &[u8]) -> Result { if self.tls_conn.is_none() { self.write_plain_msg(buf) } else { @@ -449,14 +473,14 @@ impl ClientIoHandler { } // Send vencrypt message. - fn write_tls_msg(&mut self, buf: &[u8]) { + fn write_tls_msg(&mut self, buf: &[u8]) -> Result { let buf_size = buf.len(); let mut offset = 0; let tc: &mut ServerConnection = match &mut self.tls_conn { Some(ts) => ts, None => { - return; + return Err(anyhow!(VncError::Disconnection)); } }; @@ -465,42 +489,39 @@ impl ClientIoHandler { let tmp_buf = &buf[offset..next].to_vec(); if let Err(e) = tc.writer().write_all(tmp_buf) { error!("write msg error: {:?}", e); - return; + return Err(anyhow!(VncError::Disconnection)); } if let Err(_e) = vnc_write_tls_message(tc, &mut self.stream) { self.client.conn_state.lock().unwrap().dis_conn = true; - return; + return Err(anyhow!(VncError::Disconnection)); } offset = next; } + + Ok(buf_size) } /// Send plain txt. - fn write_plain_msg(&mut self, buf: &[u8]) { + fn write_plain_msg(&mut self, buf: &[u8]) -> Result { let buf_size = buf.len(); let mut offset = 0; - loop { + while offset < buf_size { let tmp_buf = &buf[offset..]; match self.stream.write(tmp_buf) { Ok(ret) => { offset += ret; } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + return Ok(offset); + } Err(e) => { - if e.kind() == std::io::ErrorKind::WouldBlock { - self.stream.flush().unwrap(); - continue; - } else { - error!("write msg error: {:?}", e); - self.client.conn_state.lock().unwrap().dis_conn = true; - return; - } + error!("write msg error: {:?}", e); + return Err(anyhow!(VncError::Disconnection)); } } - self.stream.flush().unwrap(); - if offset >= buf_size { - break; - } } + + Ok(buf_size) } /// Exchange RFB protocol version with client. diff --git a/vnc/src/server.rs b/vnc/src/server.rs index ddca62cb6..58c8ec6c1 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -13,6 +13,7 @@ use crate::{ auth::SaslAuth, auth::{AuthState, SaslConfig, SubAuthState}, + client::vnc_write, client::{ClientIoHandler, ClientState}, console::DisplayMouse, input::KeyBoardState, @@ -537,10 +538,8 @@ pub fn handle_connection( client.clone(), server.clone(), ))); - client_io - .lock() - .unwrap() - .write_msg("RFB 003.008\n".to_string().as_bytes()); + vnc_write(&client, "RFB 003.008\n".as_bytes().to_vec()); + client_io.lock().unwrap().flush(); server .client_handlers .lock() -- Gitee From a634f5b2b7582da13c1ade618474bbbbb5f59df9 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 4 Jan 2023 11:39:58 +0800 Subject: [PATCH 0674/2187] pcie: optimize update pciexbar mapping Skip pciexbar mapping when pciexbar is not updated. Signed-off-by: yezengruan --- machine/src/standard_vm/x86_64/mch.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index bb009bea2..03a43c31a 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -36,6 +36,8 @@ const PCIEXBAR_LENGTH_128MB: u64 = 0x2; const PCIEXBAR_LENGTH_64MB: u64 = 0x4; const PCIEXBAR_128MB_ADDR_MASK: u64 = 1 << 26; const PCIEXBAR_64MB_ADDR_MASK: u64 = 1 << 25; +// Bit 25:3 of PCIEXBAR is reserved. +const PCIEXBAR_RESERVED_MASK: u64 = 0x3ff_fff8; /// Memory controller hub (Device 0:Function 0) pub struct Mch { @@ -100,6 +102,15 @@ impl Mch { } Ok(()) } + + fn check_pciexbar_update(&self, old_pciexbar: u64) -> bool { + let cur_pciexbar: u64 = le_read_u64(&self.config.config, PCIEXBAR as usize).unwrap(); + + if (cur_pciexbar & !PCIEXBAR_RESERVED_MASK) == (old_pciexbar & !PCIEXBAR_RESERVED_MASK) { + return false; + } + true + } } impl PciDevOps for Mch { @@ -144,8 +155,12 @@ impl PciDevOps for Mch { fn write_config(&mut self, offset: usize, data: &[u8]) { let end = offset + data.len(); + let old_pciexbar: u64 = le_read_u64(&self.config.config, PCIEXBAR as usize).unwrap(); self.config.write(offset, data, 0, None, None); - if ranges_overlap(offset, end, PCIEXBAR as usize, PCIEXBAR as usize + 8) { + + if ranges_overlap(offset, end, PCIEXBAR as usize, PCIEXBAR as usize + 8) + && self.check_pciexbar_update(old_pciexbar) + { if let Err(e) = self.update_pciexbar_mapping() { error!("{:?}", e); } -- Gitee From ccf67dc58ec25fbc7678872e901a48571128a451 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 5 Jan 2023 16:31:57 +0800 Subject: [PATCH 0675/2187] Refactor: Move the GSI from the Device structure to the Msix structure. When the GSI information is on the Device side, the MSIx reversely obtains the Device lock when sending interrupt, which may cause deadlock. Therefore, the GSI information is strored on the MSIx side. In addition, some associated functions are modified. Signed-off-by: Mingwang Li --- pci/src/msix.rs | 208 ++++++++++++++++++++++++++++++---- vfio/src/vfio_pci.rs | 2 +- virtio/src/virtio_pci.rs | 237 ++++++++------------------------------- 3 files changed, 238 insertions(+), 209 deletions(-) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 2f9b18b04..125181590 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::cmp::max; +use std::collections::HashMap; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; @@ -22,6 +23,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::{byte_code::ByteCode, num_ops::round_up}; +use vmm_sys_util::eventfd::EventFd; use crate::config::{CapId, PciConfig, RegionType, MINMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; use crate::{ @@ -58,9 +60,11 @@ pub struct Message { pub data: u32, } -/// Trait used to update the interrupt routing table. -pub trait MsixUpdate: Sync + Send { - fn update_irq_routing(&mut self, _vector: u16, _entry: &Message, _is_masked: bool) {} +/// GSI information for routing msix. +struct GsiMsiRoute { + irq_fd: Option, + gsi: i32, + msg: Message, } /// The state of msix device. @@ -87,7 +91,8 @@ pub struct Msix { pub enabled: bool, pub msix_cap_offset: u16, pub dev_id: Arc, - pub msix_update: Option>>, + /// Maintains a list of GSI with irqfds that are registered to kvm. + gsi_msi_routes: HashMap, } impl Msix { @@ -99,15 +104,20 @@ impl Msix { /// * `pba_size` - Size in bytes of MSI-X PBA. /// * `msix_cap_offset` - Offset of MSI-X capability in configuration space. /// * `dev_id` - Dev_id for device. - pub fn new(table_size: u32, pba_size: u32, msix_cap_offset: u16, dev_id: u16) -> Self { + pub fn new( + table_size: u32, + pba_size: u32, + msix_cap_offset: u16, + dev_id: Arc, + ) -> Self { let mut msix = Msix { table: vec![0; table_size as usize], pba: vec![0; pba_size as usize], func_masked: true, enabled: true, msix_cap_offset, - dev_id: Arc::new(AtomicU16::new(dev_id)), - msix_update: None, + dev_id, + gsi_msi_routes: HashMap::new(), }; msix.mask_all_vectors(); msix @@ -165,6 +175,156 @@ impl Msix { le_write_u64(&mut self.pba, offset, old_val & pending_bit).unwrap(); } + fn update_irq_routing(&mut self, vector: u16, is_masked: bool) -> Result<()> { + let entry = self.get_message(vector); + let route = if let Some(route) = self.gsi_msi_routes.get_mut(&vector) { + route + } else { + return Ok(()); + }; + + let msix_vector = MsiVector { + msg_addr_lo: entry.address_lo, + msg_addr_hi: entry.address_hi, + msg_data: entry.data, + masked: false, + #[cfg(target_arch = "aarch64")] + dev_id: self.dev_id.load(Ordering::Acquire) as u32, + }; + + if is_masked { + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .unregister_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) + .map_err(|e| { + error!("Failed to unregister irq, error is {:?}", e); + e + })?; + } else { + let msg = &route.msg; + if msg.data != entry.data + || msg.address_lo != entry.address_lo + || msg.address_hi != entry.address_hi + { + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .update_msi_route(route.gsi as u32, msix_vector) + .map_err(|e| { + error!("Failed to update MSI-X route, error is {:?}", e); + e + })?; + KVM_FDS.load().commit_irq_routing().map_err(|e| { + error!("Failed to commit irq routing, error is {:?}", e); + e + })?; + route.msg = entry; + } + + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .register_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) + .map_err(|e| { + error!("Failed to register irq, error is {:?}", e); + e + })?; + } + Ok(()) + } + + pub fn register_irqfd(&mut self, vector: u16, call_fd: &EventFd) -> Result<()> { + let entry = self.get_message(vector); + let msix_vector = MsiVector { + msg_addr_lo: entry.address_lo, + msg_addr_hi: entry.address_hi, + msg_data: entry.data, + masked: false, + #[cfg(target_arch = "aarch64")] + dev_id: self.dev_id.load(Ordering::Acquire) as u32, + }; + + let gsi = KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .allocate_gsi() + .map_err(|e| { + error!("Failed to allocate gsi, error is {:?}", e); + e + })?; + + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .add_msi_route(gsi as u32, msix_vector) + .map_err(|e| { + error!("Failed to add MSI-X route, error is {:?}", e); + e + })?; + + KVM_FDS.load().commit_irq_routing().map_err(|e| { + error!("Failed to commit irq routing, error is {:?}", e); + e + })?; + + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .register_irqfd(call_fd, gsi as u32) + .map_err(|e| { + error!("Failed to register irq, error is {:?}", e); + e + })?; + + let gsi_route = GsiMsiRoute { + irq_fd: Some(call_fd.try_clone().unwrap()), + gsi: gsi as i32, + msg: entry, + }; + self.gsi_msi_routes.insert(vector, gsi_route); + Ok(()) + } + + pub fn unregister_irqfd(&mut self) -> Result<()> { + for (_, route) in self.gsi_msi_routes.iter() { + if let Some(fd) = &route.irq_fd.as_ref() { + KVM_FDS + .load() + .unregister_irqfd(fd, route.gsi as u32) + .map_err(|e| { + error!("Failed to unregister irq, error is {:?}", e); + e + })?; + + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .release_gsi(route.gsi as u32) + .map_err(|e| { + error!("Failed to release gsi, error is {:?}", e); + e + })?; + } + } + self.gsi_msi_routes.clear(); + Ok(()) + } + fn register_memory_region( msix: Arc>, region: &Region, @@ -209,14 +369,9 @@ impl Msix { locked_msix.table[offset..(offset + 4)].copy_from_slice(data); let is_masked: bool = locked_msix.is_vector_masked(vector); - if was_masked != is_masked { - if let Some(msix_update) = &locked_msix.msix_update { - let entry = locked_msix.get_message(vector); - msix_update - .lock() - .unwrap() - .update_irq_routing(vector, &entry, is_masked); - } + if was_masked != is_masked && locked_msix.update_irq_routing(vector, is_masked).is_err() + { + return false; } // Clear the pending vector just when it is pending. Otherwise, it @@ -508,7 +663,7 @@ pub fn init_msix( table_size, pba_size, msix_cap_offset as u16, - dev_id.load(Ordering::Acquire), + dev_id.clone(), ))); if let Some(region) = parent_region { Msix::register_memory_region( @@ -618,7 +773,12 @@ mod tests { #[test] fn test_mask_vectors() { let nr_vector = 2_u32; - let mut msix = Msix::new(nr_vector * MSIX_TABLE_ENTRY_SIZE as u32, 64, 64, 0); + let mut msix = Msix::new( + nr_vector * MSIX_TABLE_ENTRY_SIZE as u32, + 64, + 64, + Arc::new(AtomicU16::new(0)), + ); assert!(msix.table[MSIX_TABLE_VEC_CTL as usize] & MSIX_TABLE_MASK_BIT > 0); assert!( @@ -634,7 +794,12 @@ mod tests { #[test] fn test_pending_vectors() { - let mut msix = Msix::new(MSIX_TABLE_ENTRY_SIZE as u32, 64, 64, 0); + let mut msix = Msix::new( + MSIX_TABLE_ENTRY_SIZE as u32, + 64, + 64, + Arc::new(AtomicU16::new(0)), + ); msix.set_pending_vector(0); assert!(msix.is_vector_pending(0)); @@ -644,7 +809,12 @@ mod tests { #[test] fn test_get_message() { - let mut msix = Msix::new(MSIX_TABLE_ENTRY_SIZE as u32, 64, 64, 0); + let mut msix = Msix::new( + MSIX_TABLE_ENTRY_SIZE as u32, + 64, + 64, + Arc::new(AtomicU16::new(0)), + ); le_write_u32(&mut msix.table, 0, 0x1000_0000).unwrap(); le_write_u32(&mut msix.table, 4, 0x2000_0000).unwrap(); le_write_u32(&mut msix.table, 8, 0x3000_0000).unwrap(); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 5bbfa606f..3a82f856f 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -456,7 +456,7 @@ impl VfioPciDevice { table_size, table_size / 128, cap_offset as u16, - self.dev_id.load(Ordering::Acquire), + self.dev_id.clone(), ))); self.pci_config.msix = Some(msix.clone()); diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 8dab989b4..8e6e5fd58 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::cmp::{max, min}; -use std::collections::HashMap; use std::mem::size_of; use std::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; @@ -19,7 +18,6 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use anyhow::{anyhow, bail, Context}; use byteorder::{ByteOrder, LittleEndian}; -use hypervisor::kvm::{MsiVector, KVM_FDS}; use log::{error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; @@ -28,7 +26,7 @@ use pci::config::{ REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::msix::{update_dev_id, Message, MsixState, MsixUpdate}; +use pci::msix::{update_dev_id, MsixState}; use pci::Result as PciResult; use pci::{ config::PciConfig, init_msix, init_multifunction, le_write_u16, le_write_u32, ranges_overlap, @@ -576,12 +574,6 @@ pub struct VirtioPciState { queue_num: usize, } -struct GsiMsiRoute { - irq_fd: Option, - gsi: i32, - msg: Message, -} - /// Virtio-PCI device structure #[derive(Clone)] pub struct VirtioPciDevice { @@ -615,8 +607,6 @@ pub struct VirtioPciDevice { multi_func: bool, /// If the device need to register irqfd to kvm. need_irqfd: bool, - /// Maintains a list of GSI with irqfds that are registered to kvm. - gsi_msi_routes: Arc>>, } impl VirtioPciDevice { @@ -649,7 +639,6 @@ impl VirtioPciDevice { queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))), multi_func, need_irqfd: false, - gsi_msi_routes: Arc::new(Mutex::new(HashMap::new())), } } @@ -799,17 +788,26 @@ impl VirtioPciDevice { update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); - if self.need_irqfd - && !virtio_pci_register_irqfd(self, &self.gsi_msi_routes, &call_evts.events) - { + if self.need_irqfd && !self.queues_register_irqfd(&call_evts.events) { return false; } true } fn deactivate_device(&self) -> bool { - if self.need_irqfd { - virtio_pci_unregister_irqfd(self.gsi_msi_routes.clone()); + if self.need_irqfd + && self.config.msix.is_some() + && self + .config + .msix + .as_ref() + .unwrap() + .lock() + .unwrap() + .unregister_irqfd() + .is_err() + { + return false; } self.queues.lock().unwrap().clear(); @@ -1014,6 +1012,39 @@ impl VirtioPciDevice { // to IPI B. min(queues_max as u16 - queues_fixed, nr_cpus as u16) } + + fn queues_register_irqfd(&self, call_fds: &[EventFd]) -> bool { + let mut locked_msix = if let Some(msix) = &self.config.msix { + msix.lock().unwrap() + } else { + error!("Failed to get msix in virtio pci device configure"); + return false; + }; + + let locked_queues = self.queues.lock().unwrap(); + for (queue_index, queue_mutex) in locked_queues.iter().enumerate() { + if self.device.lock().unwrap().has_control_queue() + && queue_index + 1 == locked_queues.len() + && locked_queues.len() % 2 != 0 + { + break; + } + + let vector = queue_mutex.lock().unwrap().vring.get_queue_config().vector; + if vector == INVALID_VECTOR_NUM { + continue; + } + + if locked_msix + .register_irqfd(vector, &call_fds[queue_index]) + .is_err() + { + return false; + } + } + + true + } } impl PciDevOps for VirtioPciDevice { @@ -1126,9 +1157,6 @@ impl PciDevOps for VirtioPciDevice { None, )?; - if let Some(ref msix) = self.config.msix { - msix.lock().unwrap().msix_update = Some(Arc::new(Mutex::new(self.clone()))); - } self.assign_interrupt_cb(); let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) @@ -1182,10 +1210,6 @@ impl PciDevOps for VirtioPciDevice { let bus = self.parent_bus.upgrade().unwrap(); self.config.unregister_bars(&bus)?; - if let Some(ref msix) = self.config.msix { - msix.lock().unwrap().msix_update = None; - } - MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name); MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name); @@ -1404,171 +1428,6 @@ impl MigrationHook for VirtioPciDevice { } } -impl MsixUpdate for VirtioPciDevice { - fn update_irq_routing(&mut self, vector: u16, entry: &Message, is_masked: bool) { - if !self.need_irqfd { - return; - } - - let mut locked_gsi_routes = self.gsi_msi_routes.lock().unwrap(); - let route = if let Some(route) = locked_gsi_routes.get_mut(&vector) { - route - } else { - return; - }; - - update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); - let msix_vector = MsiVector { - msg_addr_lo: entry.address_lo, - msg_addr_hi: entry.address_hi, - msg_data: entry.data, - masked: false, - #[cfg(target_arch = "aarch64")] - dev_id: self.dev_id.load(Ordering::Acquire) as u32, - }; - - if is_masked { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .unregister_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to unregister irq, error is {:?}", e)); - } else { - let msg = &route.msg; - if msg.data != entry.data - || msg.address_lo != entry.address_lo - || msg.address_hi != entry.address_hi - { - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .update_msi_route(route.gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {:?}", e)); - KVM_FDS - .load() - .commit_irq_routing() - .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {:?}", e)); - route.msg = *entry; - } - - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .register_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to register irq, error is {:?}", e)); - } - } -} - -fn virtio_pci_register_irqfd( - pci_device: &VirtioPciDevice, - gsi_routes: &Arc>>, - call_fds: &[EventFd], -) -> bool { - let locked_msix = if let Some(msix) = &pci_device.config.msix { - msix.lock().unwrap() - } else { - error!("Failed to get msix in virtio pci device configure"); - return false; - }; - - let locked_queues = pci_device.queues.lock().unwrap(); - let mut locked_gsi_routes = gsi_routes.lock().unwrap(); - for (queue_index, queue_mutex) in locked_queues.iter().enumerate() { - if pci_device.device.lock().unwrap().has_control_queue() - && queue_index + 1 == locked_queues.len() - && locked_queues.len() % 2 != 0 - { - break; - } - - let vector = queue_mutex.lock().unwrap().vring.get_queue_config().vector; - if vector == INVALID_VECTOR_NUM { - continue; - } - let entry = locked_msix.get_message(vector as u16); - let msix_vector = MsiVector { - msg_addr_lo: entry.address_lo, - msg_addr_hi: entry.address_hi, - msg_data: entry.data, - masked: false, - #[cfg(target_arch = "aarch64")] - dev_id: pci_device.dev_id.load(Ordering::Acquire) as u32, - }; - - let gsi = match KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .allocate_gsi() - { - Ok(g) => g as i32, - Err(e) => { - error!("Failed to allocate gsi, error is {:?}", e); - return false; - } - }; - - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .add_msi_route(gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {:?}", e)); - - KVM_FDS - .load() - .commit_irq_routing() - .unwrap_or_else(|e| error!("Failed to commit irq routing, error is {:?}", e)); - - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .register_irqfd(&call_fds[queue_index], gsi as u32) - .unwrap_or_else(|e| error!("Failed to register irq, error is {:?}", e)); - - let gsi_route = GsiMsiRoute { - irq_fd: Some(call_fds[queue_index].try_clone().unwrap()), - gsi, - msg: entry, - }; - locked_gsi_routes.insert(vector as u16, gsi_route); - } - - true -} - -fn virtio_pci_unregister_irqfd(gsi_routes: Arc>>) { - let mut locked_gsi_routes = gsi_routes.lock().unwrap(); - for (_, route) in locked_gsi_routes.iter() { - if let Some(fd) = &route.irq_fd.as_ref() { - KVM_FDS - .load() - .unregister_irqfd(fd, route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to unregister irq, error is {:?}", e)); - - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .release_gsi(route.gsi as u32) - .unwrap_or_else(|e| error!("Failed to release gsi, error is {:?}", e)); - } - } - locked_gsi_routes.clear(); -} - #[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; -- Gitee From b691674ddf619641fe1fe2977a3c0cd15c8aab6d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 4 Jan 2023 10:51:04 +0800 Subject: [PATCH 0676/2187] virtio-net: support configuration of virtqueue size. Support configuration of virtqueue size for virtio-net by using: -netdev tap,id=,ifname=[,queues=] -device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}][,queue-size=] Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 5 +++-- machine/src/micro_vm/mod.rs | 6 +++++- machine/src/standard_vm/mod.rs | 4 +++- machine_manager/src/config/network.rs | 28 ++++++++++++++++++++++++++- machine_manager/src/qmp/qmp_schema.rs | 2 ++ virtio/src/net.rs | 10 +++++----- virtio/src/vhost/kernel/net.rs | 7 ++++--- virtio/src/vhost/user/net.rs | 4 +--- 8 files changed, 50 insertions(+), 16 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a5fbc861c..0d4188de7 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -401,11 +401,12 @@ It has no effect when vhost is set. cause the same mac address between two virtio-net devices when one device has mac and the other hasn't. * mq: the optional mq attribute enable device multiple queue feature. -Two more properties are supported for virtio pci net device. +Three more properties are supported for virtio pci net device. * bus: name of bus which to attach. * addr: including slot number and function number. The first number represents slot number of device and the second one represents function number of it. For virtio pci net device, it is a single function device, the function number should be set to zero. +* queue-size: the optional virtqueue size for all the queues. (optional) Configuration range is [256, 4096] and queue size must be power of 2. Default queue size is 256. ```shell # virtio mmio net device @@ -413,7 +414,7 @@ is a single function device, the function number should be set to zero. -device virtio-net-device,id=,netdev=[,iothread=][,mac=] # virtio pci net device -netdev tap,id=,ifname=[,queues=] --device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}] +-device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}][,queue-size=] ``` StratoVirt also supports vhost-net to get a higher performance in network. It can be set by diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 205076927..d7de1e5b2 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -73,7 +73,10 @@ use machine_manager::machine::{ MachineInterface, MachineLifecycle, MigrateInterface, }; use machine_manager::{ - config::{BootSource, ConfigCheck, NetworkInterfaceConfig, SerialConfig, VmConfig}, + config::{ + BootSource, ConfigCheck, NetworkInterfaceConfig, SerialConfig, VmConfig, + DEFAULT_QUEUE_SIZE_NET, + }, qmp::{qmp_schema, QmpChannel, Response}, }; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; @@ -1206,6 +1209,7 @@ impl DeviceInterface for LightMachine { queues: 2, mq: false, socket_path: None, + queue_size: DEFAULT_QUEUE_SIZE_NET, }; if let Some(fds) = args.fds { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 36b6dfbd0..cc90e7556 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -51,7 +51,7 @@ use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, - MAX_VIRTIO_QUEUE, + DEFAULT_QUEUE_SIZE_NET, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -913,6 +913,7 @@ impl StdMachine { } else { bail!("Netdev not set"); }; + let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_NET); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); let dev = if let Some(conf) = locked_vmconfig.netdevs.get(netdev) { @@ -933,6 +934,7 @@ impl StdMachine { queues: conf.queues, mq: conf.queues > 2, socket_path, + queue_size, }; dev.check()?; dev diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 22535c236..6d00f81cb 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -22,6 +22,11 @@ use crate::qmp::{qmp_schema, QmpChannel}; const MAC_ADDRESS_LENGTH: usize = 17; +/// Default virtqueue size of each virtqueue. +pub const DEFAULT_QUEUE_SIZE_NET: u16 = 256; +/// Max virtqueue size of each virtqueue. +pub const MAX_QUEUE_SIZE_NET: u16 = 4096; + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetDevcfg { pub id: String, @@ -98,6 +103,8 @@ pub struct NetworkInterfaceConfig { pub queues: u16, pub mq: bool, pub socket_path: Option, + /// All queues of a net device have the same queue size now. + pub queue_size: u16, } impl Default for NetworkInterfaceConfig { @@ -113,6 +120,7 @@ impl Default for NetworkInterfaceConfig { queues: 2, mq: false, socket_path: None, + queue_size: DEFAULT_QUEUE_SIZE_NET, } } } @@ -152,6 +160,20 @@ impl ConfigCheck for NetworkInterfaceConfig { ))); } + if self.queue_size < DEFAULT_QUEUE_SIZE_NET || self.queue_size > MAX_QUEUE_SIZE_NET { + return Err(anyhow!(ConfigError::IllegalValue( + "queue size of net device".to_string(), + DEFAULT_QUEUE_SIZE_NET as u64, + true, + MAX_QUEUE_SIZE_NET as u64, + true + ))); + } + + if self.queue_size & (self.queue_size - 1) != 0 { + bail!("queue size of net device should be power of 2!"); + } + Ok(()) } } @@ -264,7 +286,8 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result Result("iothread")?; netdevinterfacecfg.mac = cmd_parser.get_value::("mac")?; + if let Some(queue_size) = cmd_parser.get_value::("queue-size")? { + netdevinterfacecfg.queue_size = queue_size; + } if let Some(netcfg) = &vm_config.netdevs.remove(&netdev) { netdevinterfacecfg.id = netid; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index bd617ffec..272a9384d 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -515,6 +515,8 @@ pub struct device_add { pub queues: Option, pub boot_index: Option, pub sysfsdev: Option, + #[serde(rename = "queue-size")] + pub queue_size: Option, } pub type DeviceAddArgument = device_add; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 8406f3d8c..91d270dd8 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -65,8 +65,6 @@ use util::tap::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Number of virtqueues(rx/tx/ctrl). const QUEUE_NUM_NET: usize = 3; -/// Size of each virtqueue. -const QUEUE_SIZE_NET: u16 = 256; /// The Mac Address length. pub const MAC_ADDR_LEN: usize = 6; /// The length of ethernet header. @@ -667,6 +665,7 @@ struct NetIoHandler { device_broken: Arc, is_listening: bool, ctrl_info: Arc>, + queue_size: u16, } impl NetIoHandler { @@ -735,7 +734,7 @@ impl NetIoHandler { break; } rx_packets += 1; - if rx_packets > QUEUE_SIZE_NET { + if rx_packets > self.queue_size { self.rx .queue_evt .write(1) @@ -856,7 +855,7 @@ impl NetIoHandler { break; } tx_packets += 1; - if tx_packets >= QUEUE_SIZE_NET { + if tx_packets >= self.queue_size { self.tx .queue_evt .write(1) @@ -1426,7 +1425,7 @@ impl VirtioDevice for Net { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_NET + self.net_cfg.queue_size } /// Get device features from host. @@ -1543,6 +1542,7 @@ impl VirtioDevice for Net { device_broken: self.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), + queue_size: self.queue_size(), }; if let Some(tap) = &handler.tap { handler.tap_fd = tap.as_raw_fd(); diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index a59ae1c76..226e3dc3c 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -42,8 +42,6 @@ use crate::virtio_has_feature; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; -/// Size of each virtqueue. -const QUEUE_SIZE_NET: u16 = 256; /// Feature for vhost-net to add virtio_net_hdr for RX, and strip for TX packets. const VHOST_NET_F_VIRTIO_NET_HDR: u32 = 27; @@ -195,7 +193,7 @@ impl VirtioDevice for Net { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_NET + self.net_cfg.queue_size } /// Get device features from host. @@ -411,6 +409,7 @@ impl VirtioDevice for Net { mod tests { use super::*; use address_space::*; + use machine_manager::config::DEFAULT_QUEUE_SIZE_NET; use std::fs::File; const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; @@ -453,6 +452,7 @@ mod tests { queues: 2, mq: false, socket_path: None, + queue_size: DEFAULT_QUEUE_SIZE_NET, }; let conf = vec![net1]; let confs = Some(conf); @@ -474,6 +474,7 @@ mod tests { queues: 2, mq: false, socket_path: None, + queue_size: DEFAULT_QUEUE_SIZE_NET, }; let conf = vec![net1]; let confs = Some(conf); diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index ed642b66c..a58ef4aa6 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -40,8 +40,6 @@ use anyhow::{anyhow, Context, Result}; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; -/// Size of each virtqueue. -const QUEUE_SIZE_NET: u16 = 256; /// Network device structure. pub struct Net { @@ -176,7 +174,7 @@ impl VirtioDevice for Net { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_NET + self.net_cfg.queue_size } /// Get device features from host. -- Gitee From b49a809b7fc74eae8232bd9302f708c4a1b5acca Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 09:31:40 +0800 Subject: [PATCH 0677/2187] virtio-blk: support configuration of virtqueue size Support configuration of virtqueue size for virtio-blk by using: -drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=] -device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,serial=][,num-queues=][,bootindex=][,queue-size=] Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 7 ++++--- machine/src/micro_vm/mod.rs | 19 +++++++++--------- machine/src/standard_vm/mod.rs | 4 +++- machine_manager/src/config/drive.rs | 30 ++++++++++++++++++++++++++++- virtio/src/block.rs | 16 +++++++-------- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 0d4188de7..b29a5ab0d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -273,7 +273,7 @@ There is only one argument for iothread: Virtio block device is a virtual block device, which process read and write requests in virtio queue from guest. -10 properties are supported for virtio block device. +twelve properties are supported for virtio block device. * id: unique device-id in StratoVirt. * file: the path of backend file on host. @@ -290,11 +290,12 @@ The number ranges from 0 to 255, the smaller the number, the higher the priority It determines the order of bootable devices which firmware will use for booting the guest OS. * aio: the aio type of block device (optional). Possible values are `native`, `io_uring`, or `off`. If not set, default is `native` if `direct` is true, otherwise default is `off`. -For virtio-blk-pci, three more properties are required. +For virtio-blk-pci, four more properties are required. * bus: name of bus which to attach. * addr: including slot number and function number. The first number represents slot number of device and the second one represents function number of it. * multifunction: whether to open multi-function for device. (optional) If not set, default is false. +* queue-size: the optional virtqueue size for all the queues. (optional) Configuration range is (2, 1024] and queue size must be power of 2. Default queue size is 256. If you want to boot VM with a virtio block device as rootfs, you should add `root=DEVICE_NAME_IN_GUESTOS` in Kernel Parameters. `DEVICE_NAME_IN_GUESTOS` will from `vda` to `vdz` in order. @@ -305,7 +306,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo -device virtio-blk-device,drive=,id=[,iothread=][,serial=] # virtio pci block device. -drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=] --device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,serial=][,num-queues=][,bootindex=] +-device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,serial=][,num-queues=][,bootindex=][,queue-size=] ``` diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d7de1e5b2..d589102c9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -64,18 +64,16 @@ use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GI use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; -use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, DriveFile, Incoming, MigrateMode, -}; -use machine_manager::event; -use machine_manager::machine::{ - DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, - MachineInterface, MachineLifecycle, MigrateInterface, -}; use machine_manager::{ config::{ - BootSource, ConfigCheck, NetworkInterfaceConfig, SerialConfig, VmConfig, - DEFAULT_QUEUE_SIZE_NET, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DriveFile, + Incoming, MigrateMode, NetworkInterfaceConfig, SerialConfig, VmConfig, + DEFAULT_QUEUE_SIZE_BLK, DEFAULT_QUEUE_SIZE_NET, + }, + event, + machine::{ + DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, + MachineInterface, MachineLifecycle, MigrateInterface, }, qmp::{qmp_schema, QmpChannel, Response}, }; @@ -1160,6 +1158,7 @@ impl DeviceInterface for LightMachine { } else { None }, + queue_size: DEFAULT_QUEUE_SIZE_BLK, }; if let Err(e) = config.check() { error!("{:?}", e); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index cc90e7556..9c3679458 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -51,7 +51,7 @@ use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, - DEFAULT_QUEUE_SIZE_NET, MAX_VIRTIO_QUEUE, + DEFAULT_QUEUE_SIZE_BLK, DEFAULT_QUEUE_SIZE_NET, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -740,6 +740,7 @@ impl StdMachine { } else { bail!("Drive not set"); }; + let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_BLK); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); let nr_cpus = locked_vmconfig.machine_config.nr_cpus; @@ -759,6 +760,7 @@ impl StdMachine { chardev: None, socket_path: None, aio: conf.aio.clone(), + queue_size, }; dev.check()?; dev diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 8616fd21b..3443c758e 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -29,6 +29,13 @@ const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; +// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-blk should be larger than 2. +const MIN_QUEUE_SIZE_BLK: u16 = 2; +/// Default size of each virtqueue for virtio-blk. +pub const DEFAULT_QUEUE_SIZE_BLK: u16 = 256; +// Max size of each virtqueue for virtio-blk. +const MAX_QUEUE_SIZE_BLK: u16 = 1024; + /// Represent a single drive backend file. pub struct DriveFile { /// The opened file. @@ -58,6 +65,7 @@ pub struct BlkDevConfig { pub chardev: Option, pub socket_path: Option, pub aio: Option, + pub queue_size: u16, } #[derive(Debug, Clone)] @@ -82,6 +90,7 @@ impl Default for BlkDevConfig { chardev: None, socket_path: None, aio: Some(AIO_NATIVE.to_string()), + queue_size: DEFAULT_QUEUE_SIZE_BLK, } } } @@ -224,6 +233,20 @@ impl ConfigCheck for BlkDevConfig { ))); } + if self.queue_size <= MIN_QUEUE_SIZE_BLK || self.queue_size > MAX_QUEUE_SIZE_BLK { + return Err(anyhow!(ConfigError::IllegalValue( + "queue size of block device".to_string(), + MIN_QUEUE_SIZE_BLK as u64, + false, + MAX_QUEUE_SIZE_BLK as u64, + true + ))); + } + + if self.queue_size & (self.queue_size - 1) != 0 { + bail!("Queue size should be power of 2!"); + } + let fake_drive = DriveConfig { path_on_host: self.path_on_host.clone(), direct: self.direct, @@ -311,7 +334,8 @@ pub fn parse_blk( .push("bootindex") .push("serial") .push("iothread") - .push("num-queues"); + .push("num-queues") + .push("queue-size"); cmd_parser.parse(drive_config)?; @@ -348,6 +372,10 @@ pub fn parse_blk( blkdevcfg.queues = queues; } + if let Some(queue_size) = cmd_parser.get_value::("queue-size")? { + blkdevcfg.queue_size = queue_size; + } + if let Some(drive_arg) = &vm_config.drives.remove(&blkdrive) { blkdevcfg.path_on_host = drive_arg.path_on_host.clone(); blkdevcfg.read_only = drive_arg.read_only; diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 19d370b0f..3910e5f98 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -53,8 +53,6 @@ use util::offset_of; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Number of virtqueues. const QUEUE_NUM_BLK: usize = 1; -/// Size of each virtqueue. -const QUEUE_SIZE_BLK: u16 = 256; /// Used to compute the number of sectors. const SECTOR_SHIFT: u8 = 9; /// Size of a sector of the block device. @@ -1009,7 +1007,7 @@ impl VirtioDevice for Block { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_BLK + self.blk_cfg.queue_size } /// Get device features from host. @@ -1205,7 +1203,7 @@ mod tests { use super::super::*; use super::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use machine_manager::config::{IothreadConfig, VmConfig}; + use machine_manager::config::{IothreadConfig, VmConfig, DEFAULT_QUEUE_SIZE_BLK}; use std::sync::atomic::{AtomicU32, Ordering}; use std::{thread, time::Duration}; use vmm_sys_util::tempfile::TempFile; @@ -1271,7 +1269,7 @@ mod tests { assert_eq!(block.device_type(), VIRTIO_TYPE_BLOCK); assert_eq!(block.queue_num(), QUEUE_NUM_BLK); - assert_eq!(block.queue_size(), QUEUE_SIZE_BLK); + assert_eq!(block.queue_size(), DEFAULT_QUEUE_SIZE_BLK); } // Test `write_config` and `read_config`. The main contests include: compare expect data and @@ -1424,17 +1422,17 @@ mod tests { }, ) as VirtioInterrupt); - let mut queue_config = QueueConfig::new(QUEUE_SIZE_BLK); + let mut queue_config = QueueConfig::new(DEFAULT_QUEUE_SIZE_BLK); queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * QUEUE_SIZE_BLK as u64); + queue_config.avail_ring = GuestAddress(16 * DEFAULT_QUEUE_SIZE_BLK as u64); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * QUEUE_SIZE_BLK as u64); + queue_config.used_ring = GuestAddress(32 * DEFAULT_QUEUE_SIZE_BLK as u64); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); - queue_config.size = QUEUE_SIZE_BLK; + queue_config.size = DEFAULT_QUEUE_SIZE_BLK; queue_config.ready = true; let queues: Vec>> = -- Gitee From def89b69e0af680206006c94abe72d9d0c3a4ace Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 10:01:18 +0800 Subject: [PATCH 0678/2187] virtio-scsi: support configuration of virtqueue size. Support configuration of virtqueue size for virtio-blk by using: -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=][,queue-size=] Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 5 +++-- machine/src/standard_vm/mod.rs | 4 +++- machine_manager/src/config/scsi.rs | 33 ++++++++++++++++++++++++++++-- virtio/src/scsi/controller.rs | 4 +--- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index b29a5ab0d..0df9d1bd6 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -771,15 +771,16 @@ Note: Only one tablet can be configured. ### 2.16 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. -Five properties can be set for Virtio-Scsi controller. +Six properties can be set for Virtio-Scsi controller. * id: unique device id. * bus: bus number of the device. * addr: including slot number and function number. * iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) * num-queues: the optional num-queues attribute controls the number of request queues to be used for the scsi controller. If not set, the default block queue number is 1. The max queues number supported is no more than 32. (optional) +* queue-size: the optional virtqueue size for all the queues. Configuration range is (2, 1024] and queue size must be power of 2. Default queue size is 256. ```shell --device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=] +-device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=][,queue-size=] ``` ### 2.17 Virtio Scsi HardDisk Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 9c3679458..26e1a6f1f 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -51,7 +51,7 @@ use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, - DEFAULT_QUEUE_SIZE_BLK, DEFAULT_QUEUE_SIZE_NET, MAX_VIRTIO_QUEUE, + DEFAULT_QUEUE_SIZE_BLK, DEFAULT_QUEUE_SIZE_NET, DEFAULT_QUEUE_SIZE_SCSI, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -798,6 +798,7 @@ impl StdMachine { ) -> Result<()> { let multifunction = args.multifunction.unwrap_or(false); let nr_cpus = self.get_vm_config().lock().unwrap().machine_config.nr_cpus; + let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_SCSI); let dev_cfg = ScsiCntlrConfig { id: args.id.clone(), iothread: args.iothread.clone(), @@ -805,6 +806,7 @@ impl StdMachine { VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) }) as u32, boot_prefix: None, + queue_size, }; dev_cfg.check()?; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 6d4a77ba1..78068a94a 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use super::{error::ConfigError, pci_args_check}; use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; @@ -26,6 +26,13 @@ pub const VIRTIO_SCSI_MAX_LUN: u16 = 16383; /// So, max lun id supported is 255 (2^8 - 1). const SUPPORT_SCSI_MAX_LUN: u16 = 255; +// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi should be larger than 2. +const MIN_QUEUE_SIZE_SCSI: u16 = 2; +/// Default size of each virtqueue for virtio-scsi. +pub const DEFAULT_QUEUE_SIZE_SCSI: u16 = 256; +// Max size of each virtqueue for virtio-scsi. +const MAX_QUEUE_SIZE_SCSI: u16 = 1024; + #[derive(Debug, Clone)] pub struct ScsiCntlrConfig { /// Virtio-scsi-pci device id. @@ -36,6 +43,8 @@ pub struct ScsiCntlrConfig { pub queues: u32, /// Boot path of this scsi controller. It's prefix of scsi device's boot path. pub boot_prefix: Option, + /// Virtqueue size for all queues. + pub queue_size: u16, } impl Default for ScsiCntlrConfig { @@ -46,6 +55,7 @@ impl Default for ScsiCntlrConfig { //At least 1 cmd queue. queues: 1, boot_prefix: None, + queue_size: DEFAULT_QUEUE_SIZE_SCSI, } } } @@ -76,6 +86,20 @@ impl ConfigCheck for ScsiCntlrConfig { ))); } + if self.queue_size <= MIN_QUEUE_SIZE_SCSI || self.queue_size > MAX_QUEUE_SIZE_SCSI { + return Err(anyhow!(ConfigError::IllegalValue( + "virtqueue size of scsi controller".to_string(), + MIN_QUEUE_SIZE_SCSI as u64, + false, + MAX_QUEUE_SIZE_SCSI as u64, + true + ))); + } + + if self.queue_size & (self.queue_size - 1) != 0 { + bail!("Virtqueue size should be power of 2!"); + } + Ok(()) } } @@ -92,7 +116,8 @@ pub fn parse_scsi_controller( .push("addr") .push("multifunction") .push("iothread") - .push("num-queues"); + .push("num-queues") + .push("queue-size"); cmd_parser.parse(drive_config)?; @@ -119,6 +144,10 @@ pub fn parse_scsi_controller( cntlr_cfg.queues = queues as u32; } + if let Some(size) = cmd_parser.get_value::("queue-size")? { + cntlr_cfg.queue_size = size; + } + cntlr_cfg.check()?; Ok(cntlr_cfg) } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index feb5bd6af..7ee6f3c73 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -50,8 +50,6 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; const SCSI_CTRL_QUEUE_NUM: usize = 1; const SCSI_EVENT_QUEUE_NUM: usize = 1; const SCSI_MIN_QUEUE_NUM: usize = 3; -/// Size of each virtqueue. -const QUEUE_SIZE_SCSI: u16 = 256; /// Default values of the cdb and sense data size configuration fields. Cannot change cdb size /// and sense data size Now. @@ -212,7 +210,7 @@ impl VirtioDevice for ScsiCntlr { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_SCSI + self.config.queue_size } /// Get device features from host. -- Gitee From e4daee45e4600a1069e3b4857db52c8150031683 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 14:42:17 +0800 Subject: [PATCH 0679/2187] vhost-user-blk: support configuration of virtqueue size Support configuration of virtqueue size for virtio-blk by using: -chardev socket,id=,path= -device vhost-user-blk-pci,id=,chardev=,bus=,addr=<0x3>[,num-queues=][,bootindex=][,queue-size=] Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- machine/src/standard_vm/mod.rs | 2 ++ machine_manager/src/config/drive.rs | 7 ++++++- virtio/src/vhost/user/block.rs | 5 +---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 0df9d1bd6..50de2b3e2 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -319,7 +319,7 @@ You can use it by adding a new device, one more property is supported by vhost-u ```shell # vhost user blk pci device -chardev socket,id=,path= --device vhost-user-blk-pci,id=,chardev=,bus=,addr=<0x3>[,num-queues=][,bootindex=] +-device vhost-user-blk-pci,id=,chardev=,bus=,addr=<0x3>[,num-queues=][,bootindex=][,queue-size=] ``` Note: More features to be supported. diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 26e1a6f1f..1e8b725cb 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -852,6 +852,7 @@ impl StdMachine { } else { bail!("Chardev not set"); }; + let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_BLK); let socket_path = self .get_socket_path(&locked_vmconfig, chardev.to_string()) .with_context(|| "Failed to get socket path")?; @@ -864,6 +865,7 @@ impl StdMachine { boot_index: args.boot_index, chardev: Some(chardev.to_string()), socket_path, + queue_size, ..BlkDevConfig::default() }; diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 3443c758e..dce6e91d2 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -401,7 +401,8 @@ pub fn parse_vhost_user_blk_pci( .push("bus") .push("addr") .push("num-queues") - .push("chardev"); + .push("chardev") + .push("queue-size"); cmd_parser.parse(drive_config)?; @@ -430,6 +431,10 @@ pub fn parse_vhost_user_blk_pci( blkdevcfg.queues = queues; } + if let Some(size) = cmd_parser.get_value::("queue-size")? { + blkdevcfg.queue_size = size; + } + if let Some(chardev) = &blkdevcfg.chardev { blkdevcfg.socket_path = Some(get_chardev_socket_path(chardev, vm_config)?); } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 386e30ef2..800dcdf19 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -34,9 +34,6 @@ use crate::{ VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; -/// Size of each virtqueue. -const QUEUE_SIZE_BLK: u16 = 256; - pub struct Block { /// Configuration of the block device. blk_cfg: BlkDevConfig, @@ -220,7 +217,7 @@ impl VirtioDevice for Block { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_BLK + self.blk_cfg.queue_size } /// Get device features from host. -- Gitee From 96bd8c59c9e77afd853940d0fcd57544707e963a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 5 Jan 2023 23:34:39 +0800 Subject: [PATCH 0680/2187] virtqueue: using unified variable DEFAULT_VIRTQUEUE_SIZE The default queue size of most virtio devices is 256. So, use unified variable DEFAULT_VIRTQUEUE_SIZE instead of variables defined in different devices' code. No functional change. Signed-off-by: liuxiangdong --- machine/src/micro_vm/mod.rs | 6 +++--- machine/src/standard_vm/mod.rs | 10 ++++----- machine_manager/src/config/drive.rs | 8 +++---- machine_manager/src/config/mod.rs | 2 ++ machine_manager/src/config/network.rs | 11 +++++----- machine_manager/src/config/scsi.rs | 8 +++---- virtio/src/balloon.rs | 10 +++++---- virtio/src/block.rs | 12 +++++------ virtio/src/console.rs | 11 +++++----- virtio/src/gpu.rs | 6 ++---- virtio/src/rng.rs | 30 ++++++++++++++------------- virtio/src/vhost/kernel/net.rs | 6 +++--- virtio/src/vhost/kernel/vsock.rs | 8 +++---- 13 files changed, 64 insertions(+), 64 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d589102c9..45e74941d 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -68,7 +68,7 @@ use machine_manager::{ config::{ parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DriveFile, Incoming, MigrateMode, NetworkInterfaceConfig, SerialConfig, VmConfig, - DEFAULT_QUEUE_SIZE_BLK, DEFAULT_QUEUE_SIZE_NET, + DEFAULT_VIRTQUEUE_SIZE, }, event, machine::{ @@ -1158,7 +1158,7 @@ impl DeviceInterface for LightMachine { } else { None }, - queue_size: DEFAULT_QUEUE_SIZE_BLK, + queue_size: DEFAULT_VIRTQUEUE_SIZE, }; if let Err(e) = config.check() { error!("{:?}", e); @@ -1208,7 +1208,7 @@ impl DeviceInterface for LightMachine { queues: 2, mq: false, socket_path: None, - queue_size: DEFAULT_QUEUE_SIZE_NET, + queue_size: DEFAULT_VIRTQUEUE_SIZE, }; if let Some(fds) = args.fds { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 1e8b725cb..a86720dfb 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -51,7 +51,7 @@ use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, - DEFAULT_QUEUE_SIZE_BLK, DEFAULT_QUEUE_SIZE_NET, DEFAULT_QUEUE_SIZE_SCSI, MAX_VIRTIO_QUEUE, + DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -740,7 +740,7 @@ impl StdMachine { } else { bail!("Drive not set"); }; - let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_BLK); + let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); let nr_cpus = locked_vmconfig.machine_config.nr_cpus; @@ -798,7 +798,7 @@ impl StdMachine { ) -> Result<()> { let multifunction = args.multifunction.unwrap_or(false); let nr_cpus = self.get_vm_config().lock().unwrap().machine_config.nr_cpus; - let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_SCSI); + let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let dev_cfg = ScsiCntlrConfig { id: args.id.clone(), iothread: args.iothread.clone(), @@ -852,7 +852,7 @@ impl StdMachine { } else { bail!("Chardev not set"); }; - let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_BLK); + let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let socket_path = self .get_socket_path(&locked_vmconfig, chardev.to_string()) .with_context(|| "Failed to get socket path")?; @@ -919,7 +919,7 @@ impl StdMachine { } else { bail!("Netdev not set"); }; - let queue_size = args.queue_size.unwrap_or(DEFAULT_QUEUE_SIZE_NET); + let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); let dev = if let Some(conf) = locked_vmconfig.netdevs.get(netdev) { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index dce6e91d2..fca34aa06 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -21,8 +21,8 @@ use util::aio::{AIO_IOURING, AIO_NATIVE}; use super::{error::ConfigError, pci_args_check}; use crate::config::{ - get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, - MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, + MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::qmp_schema; const MAX_SERIAL_NUM: usize = 20; @@ -31,8 +31,6 @@ const MAX_UNIT_ID: usize = 2; // Seg_max = queue_size - 2. So, size of each virtqueue for virtio-blk should be larger than 2. const MIN_QUEUE_SIZE_BLK: u16 = 2; -/// Default size of each virtqueue for virtio-blk. -pub const DEFAULT_QUEUE_SIZE_BLK: u16 = 256; // Max size of each virtqueue for virtio-blk. const MAX_QUEUE_SIZE_BLK: u16 = 1024; @@ -90,7 +88,7 @@ impl Default for BlkDevConfig { chardev: None, socket_path: None, aio: Some(AIO_NATIVE.to_string()), - queue_size: DEFAULT_QUEUE_SIZE_BLK, + queue_size: DEFAULT_VIRTQUEUE_SIZE, } } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index caf27add0..1c8bdd123 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -77,6 +77,8 @@ pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; pub const MAX_TAG_LENGTH: usize = 36; pub const MAX_NODES: u32 = 128; +/// Default virtqueue size for virtio devices excepts virtio-fs. +pub const DEFAULT_VIRTQUEUE_SIZE: u16 = 256; #[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct ObjectConfig { diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 6d00f81cb..ded616421 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -16,14 +16,13 @@ use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check}; use crate::config::get_chardev_socket_path; use crate::config::{ - CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, + MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::{qmp_schema, QmpChannel}; const MAC_ADDRESS_LENGTH: usize = 17; -/// Default virtqueue size of each virtqueue. -pub const DEFAULT_QUEUE_SIZE_NET: u16 = 256; /// Max virtqueue size of each virtqueue. pub const MAX_QUEUE_SIZE_NET: u16 = 4096; @@ -120,7 +119,7 @@ impl Default for NetworkInterfaceConfig { queues: 2, mq: false, socket_path: None, - queue_size: DEFAULT_QUEUE_SIZE_NET, + queue_size: DEFAULT_VIRTQUEUE_SIZE, } } } @@ -160,10 +159,10 @@ impl ConfigCheck for NetworkInterfaceConfig { ))); } - if self.queue_size < DEFAULT_QUEUE_SIZE_NET || self.queue_size > MAX_QUEUE_SIZE_NET { + if self.queue_size < DEFAULT_VIRTQUEUE_SIZE || self.queue_size > MAX_QUEUE_SIZE_NET { return Err(anyhow!(ConfigError::IllegalValue( "queue size of net device".to_string(), - DEFAULT_QUEUE_SIZE_NET as u64, + DEFAULT_VIRTQUEUE_SIZE as u64, true, MAX_QUEUE_SIZE_NET as u64, true diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 78068a94a..338e57db1 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -13,7 +13,9 @@ use anyhow::{anyhow, bail, Result}; use super::{error::ConfigError, pci_args_check}; -use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE}; +use crate::config::{ + CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, +}; /// According to Virtio Spec. /// Max_channel should be 0. @@ -28,8 +30,6 @@ const SUPPORT_SCSI_MAX_LUN: u16 = 255; // Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi should be larger than 2. const MIN_QUEUE_SIZE_SCSI: u16 = 2; -/// Default size of each virtqueue for virtio-scsi. -pub const DEFAULT_QUEUE_SIZE_SCSI: u16 = 256; // Max size of each virtqueue for virtio-scsi. const MAX_QUEUE_SIZE_SCSI: u16 = 1024; @@ -55,7 +55,7 @@ impl Default for ScsiCntlrConfig { //At least 1 cmd queue. queues: 1, boot_prefix: None, - queue_size: DEFAULT_QUEUE_SIZE_SCSI, + queue_size: DEFAULT_VIRTQUEUE_SIZE, } } } diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index b69e0166b..83815a86e 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -26,9 +26,12 @@ use address_space::{ }; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ - config::BalloonConfig, event, qmp::qmp_schema::BalloonInfo, qmp::QmpChannel, + config::{BalloonConfig, DEFAULT_VIRTQUEUE_SIZE}, + event, + event_loop::{register_event_helper, unregister_event_helper}, + qmp::qmp_schema::BalloonInfo, + qmp::QmpChannel, }; use util::{ bitmap::Bitmap, @@ -50,7 +53,6 @@ use super::{ const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; const VIRTIO_BALLOON_F_REPORTING: u32 = 5; const VIRTIO_BALLOON_PFN_SHIFT: u32 = 12; -const QUEUE_SIZE_BALLOON: u16 = 256; const QUEUE_NUM_BALLOON: usize = 2; const BALLOON_PAGE_SIZE: u64 = 1 << VIRTIO_BALLOON_PFN_SHIFT; const BALLOON_INFLATE_EVENT: bool = true; @@ -883,7 +885,7 @@ impl VirtioDevice for Balloon { /// Get the zise of balloon queue. fn queue_size(&self) -> u16 { - QUEUE_SIZE_BALLOON + DEFAULT_VIRTQUEUE_SIZE } /// Get the feature of `balloon` device. diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 3910e5f98..04d47df47 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1203,7 +1203,7 @@ mod tests { use super::super::*; use super::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use machine_manager::config::{IothreadConfig, VmConfig, DEFAULT_QUEUE_SIZE_BLK}; + use machine_manager::config::{IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; use std::sync::atomic::{AtomicU32, Ordering}; use std::{thread, time::Duration}; use vmm_sys_util::tempfile::TempFile; @@ -1269,7 +1269,7 @@ mod tests { assert_eq!(block.device_type(), VIRTIO_TYPE_BLOCK); assert_eq!(block.queue_num(), QUEUE_NUM_BLK); - assert_eq!(block.queue_size(), DEFAULT_QUEUE_SIZE_BLK); + assert_eq!(block.queue_size(), DEFAULT_VIRTQUEUE_SIZE); } // Test `write_config` and `read_config`. The main contests include: compare expect data and @@ -1422,17 +1422,17 @@ mod tests { }, ) as VirtioInterrupt); - let mut queue_config = QueueConfig::new(DEFAULT_QUEUE_SIZE_BLK); + let mut queue_config = QueueConfig::new(DEFAULT_VIRTQUEUE_SIZE); queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * DEFAULT_QUEUE_SIZE_BLK as u64); + queue_config.avail_ring = GuestAddress(16 * DEFAULT_VIRTQUEUE_SIZE as u64); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * DEFAULT_QUEUE_SIZE_BLK as u64); + queue_config.used_ring = GuestAddress(32 * DEFAULT_VIRTQUEUE_SIZE as u64); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); - queue_config.size = DEFAULT_QUEUE_SIZE_BLK; + queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; let queues: Vec>> = diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 1bb71e93e..4e6366827 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -25,8 +25,11 @@ use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use devices::legacy::{Chardev, InputReceiver}; use log::{debug, error}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use machine_manager::{config::VirtioConsole, event_loop::EventLoop}; +use machine_manager::{ + config::{VirtioConsole, DEFAULT_VIRTQUEUE_SIZE}, + event_loop::EventLoop, + event_loop::{register_event_helper, unregister_event_helper}, +}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -39,8 +42,6 @@ use vmm_sys_util::eventfd::EventFd; /// Number of virtqueues. const QUEUE_NUM_CONSOLE: usize = 2; -/// Size of virtqueue. -const QUEUE_SIZE_CONSOLE: u16 = 256; const BUFF_SIZE: usize = 4096; @@ -307,7 +308,7 @@ impl VirtioDevice for Console { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_CONSOLE + DEFAULT_VIRTQUEUE_SIZE } /// Get device features from host. diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index fd211d365..ec81984b0 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -18,7 +18,7 @@ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; -use machine_manager::config::{GpuConfig, VIRTIO_GPU_MAX_SCANOUTS}; +use machine_manager::config::{GpuConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc}; use migration_derive::{ByteCode, Desc}; @@ -51,8 +51,6 @@ use vnc::console::{ /// Number of virtqueues. const QUEUE_NUM_GPU: usize = 2; -/// Size of each virtqueue. -const QUEUE_SIZE_GPU: u16 = 256; /// Flags for virtio gpu base conf. const VIRTIO_GPU_FLAG_VIRGL_ENABLED: u32 = 1; @@ -1760,7 +1758,7 @@ impl VirtioDevice for Gpu { /// Get the queue size of virtio gpu. fn queue_size(&self) -> u16 { - QUEUE_SIZE_GPU + DEFAULT_VIRTQUEUE_SIZE } /// Get device features from host. diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 3127c68cb..65bb44c4c 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -18,8 +18,11 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use machine_manager::{config::RngConfig, event_loop::EventLoop}; +use machine_manager::{ + config::{RngConfig, DEFAULT_VIRTQUEUE_SIZE}, + event_loop::EventLoop, + event_loop::{register_event_helper, unregister_event_helper}, +}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use util::aio::raw_read; use util::byte_code::ByteCode; @@ -42,7 +45,6 @@ use crate::error::VirtioError; use anyhow::{anyhow, bail, Context, Result}; const QUEUE_NUM_RNG: usize = 1; -const QUEUE_SIZE_RNG: u16 = 256; fn get_req_data_size(in_iov: &[ElemIovec]) -> Result { let mut size = 0_u32; @@ -276,7 +278,7 @@ impl VirtioDevice for Rng { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_RNG + DEFAULT_VIRTQUEUE_SIZE } /// Get device features from host. @@ -381,7 +383,7 @@ mod tests { use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use machine_manager::config::RngConfig; + use machine_manager::config::{RngConfig, DEFAULT_VIRTQUEUE_SIZE}; use vmm_sys_util::tempfile::TempFile; const VIRTQ_DESC_F_NEXT: u16 = 0x01; @@ -431,7 +433,7 @@ mod tests { assert_eq!(rng.rng_cfg.bytes_per_sec, Some(64)); assert_eq!(rng.queue_num(), QUEUE_NUM_RNG); - assert_eq!(rng.queue_size(), QUEUE_SIZE_RNG); + assert_eq!(rng.queue_size(), DEFAULT_VIRTQUEUE_SIZE); assert_eq!(rng.device_type(), VIRTIO_TYPE_RNG); } @@ -549,17 +551,17 @@ mod tests { }, ) as VirtioInterrupt); - let mut queue_config = QueueConfig::new(QUEUE_SIZE_RNG); + let mut queue_config = QueueConfig::new(DEFAULT_VIRTQUEUE_SIZE); queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * QUEUE_SIZE_RNG as u64); + queue_config.avail_ring = GuestAddress(16 * DEFAULT_VIRTQUEUE_SIZE as u64); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * QUEUE_SIZE_RNG as u64); + queue_config.used_ring = GuestAddress(32 * DEFAULT_VIRTQUEUE_SIZE as u64); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); - queue_config.size = QUEUE_SIZE_RNG; + queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; let file = TempFile::new().unwrap(); @@ -632,17 +634,17 @@ mod tests { }, ) as VirtioInterrupt); - let mut queue_config = QueueConfig::new(QUEUE_SIZE_RNG); + let mut queue_config = QueueConfig::new(DEFAULT_VIRTQUEUE_SIZE); queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * QUEUE_SIZE_RNG as u64); + queue_config.avail_ring = GuestAddress(16 * DEFAULT_VIRTQUEUE_SIZE as u64); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * QUEUE_SIZE_RNG as u64); + queue_config.used_ring = GuestAddress(32 * DEFAULT_VIRTQUEUE_SIZE as u64); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); - queue_config.size = QUEUE_SIZE_RNG; + queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; let file = TempFile::new().unwrap(); diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 226e3dc3c..3cee68a39 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -409,7 +409,7 @@ impl VirtioDevice for Net { mod tests { use super::*; use address_space::*; - use machine_manager::config::DEFAULT_QUEUE_SIZE_NET; + use machine_manager::config::DEFAULT_VIRTQUEUE_SIZE; use std::fs::File; const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; @@ -452,7 +452,7 @@ mod tests { queues: 2, mq: false, socket_path: None, - queue_size: DEFAULT_QUEUE_SIZE_NET, + queue_size: DEFAULT_VIRTQUEUE_SIZE, }; let conf = vec![net1]; let confs = Some(conf); @@ -474,7 +474,7 @@ mod tests { queues: 2, mq: false, socket_path: None, - queue_size: DEFAULT_QUEUE_SIZE_NET, + queue_size: DEFAULT_VIRTQUEUE_SIZE, }; let conf = vec![net1]; let confs = Some(conf); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 72952355d..2c6c03ec1 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; -use machine_manager::config::VsockConfig; +use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; @@ -37,8 +37,6 @@ use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK /// Number of virtqueues. const QUEUE_NUM_VSOCK: usize = 3; -/// Size of each virtqueue. -const QUEUE_SIZE_VSOCK: u16 = 256; /// Backend vhost-vsock device path. const VHOST_PATH: &str = "/dev/vhost-vsock"; /// Event transport reset @@ -194,7 +192,7 @@ impl VirtioDevice for Vsock { /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { - QUEUE_SIZE_VSOCK + DEFAULT_VIRTQUEUE_SIZE } /// Get device features from host. @@ -417,7 +415,7 @@ mod tests { assert_eq!(vsock.device_type(), VIRTIO_TYPE_VSOCK); assert_eq!(vsock.queue_num(), QUEUE_NUM_VSOCK); - assert_eq!(vsock.queue_size(), QUEUE_SIZE_VSOCK); + assert_eq!(vsock.queue_size(), DEFAULT_VIRTQUEUE_SIZE); // test vsock get_device_features vsock.state.device_features = 0x0123_4567_89ab_cdef; -- Gitee From f0d250b47ea2b1076ce8a616485f4074d03d2cd5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 6 Jan 2023 00:35:37 +0800 Subject: [PATCH 0681/2187] config/error: fix IllegalValue defination error Fix logical error in IllegalValue defination. Signed-off-by: liuxiangdong --- machine_manager/src/config/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index 13afa00cf..b3bead749 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -42,7 +42,7 @@ pub enum ConfigError { UnknownDeviceType(String), #[error("\'{0}\' is missing for \'{1}\' device.")] FieldIsMissing(&'static str, &'static str), - #[error("{0} must >{1} {2} and <{3} {4}.")] + #[error("{0} must >{} {1} and <{} {3}.", if *.2 {"="} else {""}, if *.4 {"="} else {""})] IllegalValue(String, u64, bool, u64, bool), #[error("Mac address is illegal.")] MacFormatError, -- Gitee From ed39feac4ddd524e2fd66499f7dc0442bc20f74e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 6 Jan 2023 12:09:23 +0800 Subject: [PATCH 0682/2187] virtio-scsi: fix arithmetical overflow problems Fix some potential problems of arithmetical overflow. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 5 +++++ virtio/src/scsi/controller.rs | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 137fb5611..c670484b3 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -543,6 +543,7 @@ impl ScsiRequest { iov_len: iov.iov_len, }; aiocb.iovec.push(iovec); + // Note: total len of each req is no more than DESC_CHAIN_MAX_TOTAL_LEN (1 << 32). aiocb.nbytes += iov.iov_len; } @@ -1744,6 +1745,10 @@ fn scsi_command_emulate_get_event_status_notification( /// MSF values are converted to LBA values via such formula: /// lba = ((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET. fn lba_to_msf(lba: u32) -> Vec { + // Note: lba is logical block address and it is in sectors. + // Max lba is u32::MAX * 512byte / 1024 / 1024 / 1024 = 2047GB. + // But, dvd size is less than 4.7G usually and cd size is less than 700M usually. + // So it will not overflow here. let minute = ((lba + CD_MSF_OFFSET) / CD_FRAMES / CD_SECS) as u8; let second = ((lba + CD_MSF_OFFSET) / CD_FRAMES % CD_SECS) as u8; let frame = ((lba + CD_MSF_OFFSET) % CD_FRAMES) as u8; diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 2d6947763..07856e798 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -205,6 +205,7 @@ impl VirtioDevice for ScsiCntlr { /// Get the count of virtio device queues. fn queue_num(&self) -> usize { + // Note: self.config.queues <= MAX_VIRTIO_QUEUE(32). self.config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM } @@ -244,14 +245,15 @@ impl VirtioDevice for ScsiCntlr { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let data_len = data.len(); let config_slice = self.state.config_space.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset, - config_len as u64 - ))); + let config_len = config_slice.len() as u64; + + if offset + .checked_add(data.len() as u64) + .filter(|&end| end <= config_len) + .is_none() + { + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } // Guest can only set sense_size and cdb_size, which are fixed default values @@ -577,6 +579,9 @@ impl VirtioScsiRequest { } let mut queue_lock = self.queue.lock().unwrap(); + // Note: U(response) is the header part of in_iov and self.data_len is the rest part of the in_iov or + // the out_iov. in_iov and out_iov total len is no more than DESC_CHAIN_MAX_TOTAL_LEN(1 << 32). So, + // it will not overflow here. if let Err(ref e) = queue_lock.vring.add_used( mem_space, self.desc_index, -- Gitee From 3277fd31fc6e03aece951d99b8fc27bb257e4183 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 5 Jan 2023 19:31:41 +0800 Subject: [PATCH 0683/2187] net: add control information to vhost kernel/user net Virtio net support some features for control vq. Now, enable it for vhost kernel/user net. Signed-off-by: Yan Wang --- virtio/src/net.rs | 38 ++++++++---------- virtio/src/vhost/kernel/net.rs | 60 ++++++++++++++-------------- virtio/src/vhost/user/net.rs | 71 +++++++++++++++++++--------------- 3 files changed, 87 insertions(+), 82 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index afd25d56c..da819c8a7 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -168,7 +168,7 @@ pub struct CtrlInfo { } impl CtrlInfo { - fn new(state: Arc>) -> Self { + pub fn new(state: Arc>) -> Self { CtrlInfo { rx_mode: CtrlRxMode::default(), mac_info: CtrlMacInfo::default(), @@ -432,14 +432,14 @@ pub struct CtrlVirtio { /// The eventfd used to notify the control queue event. queue_evt: EventFd, /// The information about control command. - ctrl_info: Option>>, + ctrl_info: Arc>, } impl CtrlVirtio { pub fn new( queue: Arc>, queue_evt: EventFd, - ctrl_info: Option>>, + ctrl_info: Arc>, ) -> Self { Self { queue, @@ -498,13 +498,6 @@ impl NetCtrlHandler { ); } - // Get the control information first. - let ctrl_info = if let Some(ctrl_info) = &self.ctrl.ctrl_info { - ctrl_info - } else { - bail!("Control information is None"); - }; - // Get the control request header. let mut ctrl_hdr = CtrlHdr::default(); let mut data_iovec = get_buf_and_discard( @@ -516,7 +509,9 @@ impl NetCtrlHandler { match ctrl_hdr.class { VIRTIO_NET_CTRL_RX => { - ack = ctrl_info + ack = self + .ctrl + .ctrl_info .lock() .unwrap() .handle_rx_mode(&self.mem_space, ctrl_hdr.cmd, &mut data_iovec) @@ -526,14 +521,14 @@ impl NetCtrlHandler { }); } VIRTIO_NET_CTRL_MAC => { - ack = ctrl_info.lock().unwrap().handle_mac( + ack = self.ctrl.ctrl_info.lock().unwrap().handle_mac( &self.mem_space, ctrl_hdr.cmd, &mut data_iovec, ); } VIRTIO_NET_CTRL_VLAN => { - ack = ctrl_info.lock().unwrap().handle_vlan_table( + ack = self.ctrl.ctrl_info.lock().unwrap().handle_vlan_table( &self.mem_space, ctrl_hdr.cmd, &mut data_iovec, @@ -1103,11 +1098,11 @@ impl EventNotifierHelper for NetIoHandler { #[desc_version(compat_version = "0.1.0")] pub struct VirtioNetState { /// Bit mask of features supported by the backend. - device_features: u64, + pub device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. - driver_features: u64, + pub driver_features: u64, /// Virtio net configurations. - config_space: VirtioNetConfig, + pub config_space: VirtioNetConfig, } /// Network device structure. @@ -1493,17 +1488,16 @@ impl VirtioDevice for Net { let queue_num = queues.len(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); self.ctrl_info = Some(ctrl_info.clone()); - if (self.state.lock().unwrap().driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) - && (queue_num % 2 != 0) - { + let driver_features = self.state.lock().unwrap().driver_features; + if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts.remove(queue_num - 1); let ctrl_handler = NetCtrlHandler { - ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, Some(ctrl_info.clone())), + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, ctrl_info.clone()), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.lock().unwrap().driver_features, + driver_features, device_broken: self.broken.clone(), }; @@ -1543,7 +1537,7 @@ impl VirtioDevice for Net { tap_fd: -1, mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.lock().unwrap().driver_features, + driver_features, receiver, update_evt: self.update_evt.try_clone().unwrap(), device_broken: self.broken.clone(), diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index d1a3501e2..a59ae1c76 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -29,7 +29,7 @@ use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::super::{ - net::{build_device_config_space, create_tap, VirtioNetConfig, MAC_ADDR_LEN}, + net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, @@ -83,16 +83,12 @@ pub struct Net { net_cfg: NetworkInterfaceConfig, /// Tap device opened. taps: Option>, + /// The status of net device. + state: Arc>, /// Related vhost-net kernel device. backends: Option>, - /// Bit mask of features supported by the backend. - device_features: u64, - /// Bit mask of features negotiated by the backend and the frontend. - driver_features: u64, /// Bit mask of features supported by the vhost-net kernel. vhost_features: u64, - /// Virtio net configurations. - device_config: VirtioNetConfig, /// System address space. mem_space: Arc, /// EventFd for device deactivate. @@ -106,11 +102,9 @@ impl Net { Net { net_cfg: cfg.clone(), taps: None, + state: Arc::new(Mutex::new(VirtioNetState::default())), backends: None, - device_features: 0_u64, - driver_features: 0_u64, vhost_features: 0_u64, - device_config: VirtioNetConfig::default(), mem_space: mem_space.clone(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), @@ -153,17 +147,18 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_TSO4 | 1 << VIRTIO_NET_F_HOST_UFO; + let mut locked_state = self.state.lock().unwrap(); if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; device_features |= 1 << VIRTIO_NET_F_MQ; - self.device_config.max_virtqueue_pairs = queue_pairs; + locked_state.config_space.max_virtqueue_pairs = queue_pairs; } if let Some(mac) = &self.net_cfg.mac { - device_features |= build_device_config_space(&mut self.device_config, mac); + device_features |= build_device_config_space(&mut locked_state.config_space, mac); } let host_dev_name = match self.net_cfg.host_dev_name.as_str() { @@ -174,7 +169,7 @@ impl VirtioDevice for Net { self.taps = create_tap(self.net_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) .with_context(|| "Failed to create tap for vhost net")?; self.backends = Some(backends); - self.device_features = device_features; + locked_state.device_features = device_features; self.vhost_features = vhost_features; Ok(()) @@ -205,22 +200,23 @@ impl VirtioDevice for Net { /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.device_features, features_select) + read_u32(self.state.lock().unwrap().device_features, features_select) } /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - self.driver_features = self.checked_driver_features(page, value); + self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.driver_features, features_select) + read_u32(self.state.lock().unwrap().driver_features, features_select) } /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.device_config.as_bytes(); + let locked_state = self.state.lock().unwrap(); + let config_slice = locked_state.config_space.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); @@ -235,10 +231,12 @@ impl VirtioDevice for Net { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let config_slice = self.device_config.as_mut_bytes(); + let mut locked_state = self.state.lock().unwrap(); + let driver_features = locked_state.driver_features; + let config_slice = locked_state.config_space.as_mut_bytes(); - if !virtio_has_feature(self.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) - && !virtio_has_feature(self.driver_features, VIRTIO_F_VERSION_1) + if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) + && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) && offset == 0 && data_len == MAC_ADDR_LEN && *data != config_slice[0..data_len] @@ -259,15 +257,17 @@ impl VirtioDevice for Net { mut queue_evts: Vec, ) -> Result<()> { let queue_num = queues.len(); - if (self.driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { + let driver_features = self.state.lock().unwrap().driver_features; + if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts.remove(queue_num - 1); + let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); let ctrl_handler = NetCtrlHandler { - ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, None), + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, ctrl_info), mem_space, interrupt_cb: interrupt_cb.clone(), - driver_features: self.driver_features, + driver_features, device_broken: self.broken.clone(), }; @@ -490,7 +490,7 @@ mod tests { assert_eq!(vhost_net.realize().is_ok(), true); // test for get/set_driver_features - vhost_net.device_features = 0; + vhost_net.state.lock().unwrap().device_features = 0; let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); @@ -498,7 +498,7 @@ mod tests { let new_page = vhost_net.get_device_features(page); assert_eq!(new_page, page); - vhost_net.device_features = 0xffff_ffff_ffff_ffff; + vhost_net.state.lock().unwrap().device_features = 0xffff_ffff_ffff_ffff; let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); @@ -507,9 +507,13 @@ mod tests { assert_ne!(new_page, page); // test for read/write_config - let device_config = vhost_net.device_config.as_bytes(); - let len = device_config.len() as u64; - + let len = vhost_net + .state + .lock() + .unwrap() + .config_space + .as_bytes() + .len() as u64; let offset: u64 = 0; let data: Vec = vec![1; len as usize]; assert_eq!(vhost_net.write_config(offset, &data).is_ok(), true); diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 53483f5af..ed642b66c 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -26,7 +26,7 @@ use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::super::super::{ - net::{build_device_config_space, VirtioNetConfig, MAC_ADDR_LEN}, + net::{build_device_config_space, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, @@ -47,12 +47,8 @@ const QUEUE_SIZE_NET: u16 = 256; pub struct Net { /// Configuration of the vhost user network device. net_cfg: NetworkInterfaceConfig, - /// Bit mask of features supported by the backend. - device_features: u64, - /// Bit mask of features negotiated by the backend and the frontend. - driver_features: u64, - /// Virtio net configurations. - device_config: VirtioNetConfig, + /// The status of net device. + state: Arc>, /// System address space. mem_space: Arc, /// Vhost user client @@ -69,9 +65,7 @@ impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { Net { net_cfg: cfg.clone(), - device_features: 0_u64, - driver_features: 0_u64, - device_config: VirtioNetConfig::default(), + state: Arc::new(Mutex::new(VirtioNetState::default())), mem_space: mem_space.clone(), client: None, call_events: Vec::::new(), @@ -91,7 +85,9 @@ impl Net { } None => return Err(anyhow!("Failed to get client when stoping event")), }; - if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq { + if ((self.state.lock().unwrap().driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) + && self.net_cfg.mq + { unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; } @@ -100,9 +96,10 @@ impl Net { fn clean_up(&mut self) -> Result<()> { self.delete_event()?; - self.device_features = 0_u64; - self.driver_features = 0_u64; - self.device_config = VirtioNetConfig::default(); + let mut locked_state = self.state.lock().unwrap(); + locked_state + .as_mut_bytes() + .copy_from_slice(&[0_u8; std::mem::size_of::()]); self.client = None; Ok(()) @@ -125,7 +122,8 @@ impl VirtioDevice for Net { let client = Arc::new(Mutex::new(client)); VhostUserClient::add_event(&client)?; - self.device_features = client + let mut locked_state = self.state.lock().unwrap(); + locked_state.device_features = client .lock() .unwrap() .get_features() @@ -139,22 +137,23 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_NET_F_MRG_RXBUF | 1 << VIRTIO_F_RING_EVENT_IDX; - self.device_features &= features; + locked_state.device_features &= features; let queue_pairs = self.net_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { - self.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; - self.device_features |= 1 << VIRTIO_NET_F_MQ; - self.device_config.max_virtqueue_pairs = queue_pairs; + locked_state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; + locked_state.device_features |= 1 << VIRTIO_NET_F_MQ; + locked_state.config_space.max_virtqueue_pairs = queue_pairs; } self.client = Some(client); if let Some(mac) = &self.net_cfg.mac { - self.device_features |= build_device_config_space(&mut self.device_config, mac); + locked_state.device_features |= + build_device_config_space(&mut locked_state.config_space, mac); } Ok(()) @@ -182,22 +181,23 @@ impl VirtioDevice for Net { /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.device_features, features_select) + read_u32(self.state.lock().unwrap().device_features, features_select) } /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { - self.driver_features = self.checked_driver_features(page, value); + self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); } /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.driver_features, features_select) + read_u32(self.state.lock().unwrap().driver_features, features_select) } /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.device_config.as_bytes(); + let locked_state = self.state.lock().unwrap(); + let config_slice = locked_state.config_space.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); @@ -212,10 +212,12 @@ impl VirtioDevice for Net { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let config_slice = self.device_config.as_mut_bytes(); + let mut locked_state = self.state.lock().unwrap(); + let driver_features = locked_state.driver_features; + let config_slice = locked_state.config_space.as_mut_bytes(); - if !virtio_has_feature(self.driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) - && !virtio_has_feature(self.driver_features, VIRTIO_F_VERSION_1) + if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) + && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) && offset == 0 && data_len == MAC_ADDR_LEN && *data != config_slice[0..data_len] @@ -236,15 +238,17 @@ impl VirtioDevice for Net { mut queue_evts: Vec, ) -> Result<()> { let queue_num = queues.len(); - if ((self.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { + let driver_features = self.state.lock().unwrap().driver_features; + if ((driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts.remove(queue_num - 1); + let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); let ctrl_handler = NetCtrlHandler { - ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, None), + ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, ctrl_info), mem_space, interrupt_cb: interrupt_cb.clone(), - driver_features: self.driver_features, + driver_features, device_broken: self.broken.clone(), }; @@ -262,7 +266,7 @@ impl VirtioDevice for Net { None => return Err(anyhow!("Failed to get client for vhost-user net")), }; - let features = self.driver_features & !(1 << VIRTIO_NET_F_MAC); + let features = driver_features & !(1 << VIRTIO_NET_F_MAC); client.features = features; client.set_queues(queues); client.set_queue_evts(&queue_evts); @@ -307,6 +311,9 @@ impl VirtioDevice for Net { } fn has_control_queue(&mut self) -> bool { - virtio_has_feature(self.device_features, VIRTIO_NET_F_CTRL_VQ) + virtio_has_feature( + self.state.lock().unwrap().device_features, + VIRTIO_NET_F_CTRL_VQ, + ) } } -- Gitee From f9620d302db6e8e6d3b3cefd6d201b52cd0007a4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 6 Jan 2023 15:57:15 +0800 Subject: [PATCH 0684/2187] Fix: Optimize syntax Optimize syntax to adapt to Rust of 1.66.0. Signed-off-by: Xiao Ye --- address_space/src/listener.rs | 8 +- boot_loader/src/x86_64/direct_boot/gdt.rs | 4 +- boot_loader/src/x86_64/direct_boot/mod.rs | 4 +- boot_loader/src/x86_64/direct_boot/mptable.rs | 2 +- boot_loader/src/x86_64/standard_boot/mod.rs | 2 +- cpu/src/x86_64/mod.rs | 4 +- devices/src/legacy/fwcfg.rs | 10 +- devices/src/legacy/pflash.rs | 20 ++- devices/src/legacy/rtc.rs | 2 +- machine/src/lib.rs | 32 +++-- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 6 +- machine_manager/src/config/machine_config.rs | 2 +- machine_manager/src/machine.rs | 24 ++-- machine_manager/src/socket.rs | 2 +- migration/src/general.rs | 2 +- pci/src/host.rs | 8 +- pci/src/lib.rs | 2 +- pci/src/msix.rs | 4 +- pci/src/root_port.rs | 4 +- usb/src/descriptor.rs | 2 +- usb/src/hid.rs | 2 +- usb/src/keyboard.rs | 2 +- usb/src/tablet.rs | 2 +- usb/src/xhci/xhci_controller.rs | 4 +- usb/src/xhci/xhci_pci.rs | 4 +- util/src/aio/uring.rs | 3 +- util/src/bitmap.rs | 2 +- util/src/num_ops.rs | 2 +- vfio/src/vfio_pci.rs | 4 +- vhost_user_fs/src/cmdline.rs | 14 +-- vhost_user_fs/src/fs.rs | 116 +++++++++--------- vhost_user_fs/src/fs_ops.rs | 18 +-- vhost_user_fs/src/fuse_proc.rs | 2 +- vhost_user_fs/src/virtio_fs.rs | 56 ++++----- virtio/src/balloon.rs | 6 +- virtio/src/block.rs | 6 +- virtio/src/console.rs | 2 +- virtio/src/gpu.rs | 2 +- virtio/src/net.rs | 4 +- virtio/src/scsi/bus.rs | 6 +- virtio/src/scsi/disk.rs | 3 +- virtio/src/vhost/user/fs.rs | 2 +- virtio/src/virtio_mmio.rs | 12 +- virtio/src/virtqueue/split.rs | 2 +- vnc/src/auth.rs | 2 +- vnc/src/client.rs | 39 +++--- vnc/src/console.rs | 2 +- vnc/src/encoding/enc_hextile.rs | 2 +- vnc/src/input.rs | 4 +- vnc/src/pixman.rs | 4 +- vnc/src/vnc.rs | 16 +-- 53 files changed, 236 insertions(+), 257 deletions(-) diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 2b924d0bd..952a1fb1f 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -359,7 +359,7 @@ impl KvmMemoryListener { match length { 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u64), + 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), _ => bail!("Unexpected ioeventfd data length {}", length), } } else { @@ -396,7 +396,7 @@ impl KvmMemoryListener { match length { 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u64), + 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), _ => bail!("Unexpected ioeventfd data length {}", length), } } else { @@ -504,7 +504,7 @@ impl KvmIoListener { match length { 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u64), + 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), _ => bail!("unexpected ioeventfd data length {}", length), } } else { @@ -541,7 +541,7 @@ impl KvmIoListener { match length { 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u64), + 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), _ => bail!("Unexpected ioeventfd data length {}", length), } } else { diff --git a/boot_loader/src/x86_64/direct_boot/gdt.rs b/boot_loader/src/x86_64/direct_boot/gdt.rs index 2abea571a..b229947ea 100644 --- a/boot_loader/src/x86_64/direct_boot/gdt.rs +++ b/boot_loader/src/x86_64/direct_boot/gdt.rs @@ -89,7 +89,7 @@ impl From for u64 { } fn write_gdt_table(table: &[u64], guest_mem: &Arc) -> Result<()> { - let mut boot_gdt_addr = BOOT_GDT_OFFSET as u64; + let mut boot_gdt_addr = BOOT_GDT_OFFSET; for (_, entry) in table.iter().enumerate() { guest_mem .write_object(entry, GuestAddress(boot_gdt_addr)) @@ -109,7 +109,7 @@ fn write_idt_value(val: u64, guest_mem: &Arc) -> Result<()> { } pub fn setup_gdt(guest_mem: &Arc) -> Result { - let gdt_table: [u64; BOOT_GDT_MAX as usize] = [ + let gdt_table: [u64; BOOT_GDT_MAX] = [ GdtEntry::new(0, 0, 0).into(), // NULL GdtEntry::new(0, 0, 0).into(), // NULL GdtEntry::new(0xa09b, 0, 0xfffff).into(), // CODE diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 056b64b73..09b4177e6 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -70,7 +70,7 @@ fn load_bzimage(kernel_image: &mut File) -> Result { setup_size = 4; } setup_size = (setup_size + 1) << 9; - kernel_image.seek(SeekFrom::Start(setup_size as u64))?; + kernel_image.seek(SeekFrom::Start(setup_size))?; Ok(boot_hdr) } @@ -142,7 +142,7 @@ fn load_initrd( let mut initrd_image = File::open(config.initrd.as_ref().unwrap()) .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; - let initrd_size = initrd_image.metadata().unwrap().len() as u64; + let initrd_size = initrd_image.metadata().unwrap().len(); let initrd_addr = (initrd_addr_max - initrd_size) & !0xfff_u64; load_image(&mut initrd_image, initrd_addr, sys_mem).with_context(|| "Failed to load image")?; diff --git a/boot_loader/src/x86_64/direct_boot/mptable.rs b/boot_loader/src/x86_64/direct_boot/mptable.rs index b20c0c1a3..6ec4d8764 100644 --- a/boot_loader/src/x86_64/direct_boot/mptable.rs +++ b/boot_loader/src/x86_64/direct_boot/mptable.rs @@ -299,7 +299,7 @@ pub fn setup_isa_mptable( let mut sum = 0u8; for cpu_id in 0..num_cpus { write_entry!( - ProcessEntry::new(cpu_id as u8, true, cpu_id == 0), + ProcessEntry::new(cpu_id, true, cpu_id == 0), ProcessEntry, sys_mem, offset, diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index dac4052b1..5cb13e67a 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -104,7 +104,7 @@ fn load_initrd( let mut initrd_image = File::open(config.initrd.as_ref().unwrap()) .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; - let initrd_size = initrd_image.metadata().unwrap().len() as u64; + let initrd_size = initrd_image.metadata().unwrap().len(); let initrd_addr = (initrd_addr_max - initrd_size) & !0xfff_u64; load_image(&mut initrd_image, 0, FwCfgEntryType::InitrdData, fwcfg) diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 5a8cb0746..257a0b04e 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -484,7 +484,7 @@ impl X86CPUState { } 0xb => { // Extended Topology Enumeration Leaf - entry.edx = self.apic_id as u32; + entry.edx = self.apic_id; entry.ecx = entry.index & 0xff; match entry.index { 0 => { @@ -513,7 +513,7 @@ impl X86CPUState { continue; } - entry.edx = self.apic_id as u32; + entry.edx = self.apic_id; entry.ecx = entry.index & 0xff; entry.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index d783ac436..15b5b3fbf 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -328,7 +328,7 @@ pub struct FwCfgCommon { impl FwCfgCommon { fn new(sys_mem: Arc) -> Self { FwCfgCommon { - file_slots: FW_CFG_FILE_SLOTS_DFLT as u16, + file_slots: FW_CFG_FILE_SLOTS_DFLT, arch_entries: vec![ FwCfgEntry::default(); (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS_DFLT) as usize @@ -686,7 +686,7 @@ impl FwCfgCommon { if addr == 0 { self.dma_addr = GuestAddress(value << 32); } else if addr == 4 { - self.dma_addr = GuestAddress(self.dma_addr.raw_value() | value as u64); + self.dma_addr = GuestAddress(self.dma_addr.raw_value() | value); self.handle_dma_request()?; } } else if size == 8 && addr == 0 { @@ -717,7 +717,7 @@ impl FwCfgCommon { extract_u64( FW_CFG_DMA_SIGNATURE as u64, ((8 - addr - size as u64) * 8) as u32, - (size * 8) as u32, + size * 8, ) .ok_or_else(|| anyhow!("Failed to extract bits from u64")) } @@ -1092,7 +1092,7 @@ fn read_bytes(fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, off 1 => data[0] = value as u8, 2 => BigEndian::write_u16(data, value as u16), 4 => BigEndian::write_u32(data, value as u32), - 8 => BigEndian::write_u64(data, value as u64), + 8 => BigEndian::write_u64(data, value), _ => { warn!( "Failed to read from FwCfg data register, data length {} is invalid", @@ -1135,7 +1135,7 @@ impl SysBusDevOps for FwCfgIO { 1 => data[0] as u64, 2 => BigEndian::read_u16(data) as u64, 4 => BigEndian::read_u32(data) as u64, - 8 => BigEndian::read_u64(data) as u64, + 8 => BigEndian::read_u64(data), _ => 0, }; if let Err(e) = self.fwcfg.dma_mem_write(offset - 4, value, size) { diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index d03ff952c..8833d1644 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -295,7 +295,7 @@ impl PFlash { } // Repeat data for PFlash device which supports x16-mode but works in x8-mode. for i in 1..self.max_device_width { - resp = deposit_u32(resp, 8 * i as u32, 8, self.cfi_table[index as usize] as u32) + resp = deposit_u32(resp, 8 * i, 8, self.cfi_table[index as usize] as u32) .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; } } @@ -318,9 +318,9 @@ impl PFlash { } // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + size as u64 > mr.size() as u64 { + if offset + size as u64 > mr.size() { return Err(anyhow!(LegacyError::PFlashWriteOverflow( - mr.size() as u64, + mr.size(), offset, size as u64 ))); @@ -347,7 +347,7 @@ impl PFlash { fn read_data(&mut self, data: &mut [u8], offset: u64) -> Result<()> { // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + data.len() as u64 > mr.size() as u64 { + if offset + data.len() as u64 > mr.size() { return Err(anyhow!(LegacyError::PFlashReadOverflow( mr.size(), offset, @@ -356,9 +356,8 @@ impl PFlash { } let host_addr = mr.get_host_address().unwrap(); // Safe because host_addr of the region is local allocated and sanity has been checked. - let src = unsafe { - std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len() as usize) - }; + let src = + unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; data.as_mut() .write_all(src) .with_context(|| "Failed to read data from PFlash Rom")?; @@ -369,7 +368,7 @@ impl PFlash { fn write_data(&mut self, data: &[u8], offset: u64) -> Result<()> { // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + data.len() as u64 > mr.size() as u64 { + if offset + data.len() as u64 > mr.size() { return Err(anyhow!(LegacyError::PFlashWriteOverflow( mr.size(), offset, @@ -378,9 +377,8 @@ impl PFlash { } let host_addr = mr.get_host_address().unwrap(); // Safe because host_addr of the region is local allocated and sanity has been checked. - let mut dst = unsafe { - std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len() as usize) - }; + let mut dst = + unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; dst.write_all(data) .with_context(|| "Failed to write data to PFlash Rom")?; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 8c9c891ec..ee2406bc5 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -135,7 +135,7 @@ impl RTC { tick_offset: SystemTime::now() .duration_since(UNIX_EPOCH) .expect("time wrong") - .as_secs() as u64, + .as_secs(), base_time: Instant::now(), }; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index b3dc563cd..63e73f8de 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -205,20 +205,19 @@ pub trait MachineOps { } if let Some(boot_config) = boot_cfg { - for cpu_index in 0..nr_cpus as usize { - cpus[cpu_index as usize] - .realize( - boot_config, - topology, - #[cfg(target_arch = "aarch64")] - &vcpu_cfg.unwrap_or_default(), + for (cpu_index, cpu) in cpus.iter().enumerate() { + cpu.realize( + boot_config, + topology, + #[cfg(target_arch = "aarch64")] + &vcpu_cfg.unwrap_or_default(), + ) + .with_context(|| { + format!( + "Failed to realize arch cpu register/features for CPU {}/KVM", + cpu_index ) - .with_context(|| { - format!( - "Failed to realize arch cpu register/features for CPU {}/KVM", - cpu_index - ) - })?; + })?; } } @@ -1413,11 +1412,10 @@ pub trait MachineOps { } let nr_vcpus = cpus.len(); - let cpus_thread_barrier = Arc::new(Barrier::new((nr_vcpus + 1) as usize)); - for cpu_index in 0..nr_vcpus { + let cpus_thread_barrier = Arc::new(Barrier::new(nr_vcpus + 1)); + for (cpu_index, cpu) in cpus.iter().enumerate() { let cpu_thread_barrier = cpus_thread_barrier.clone(); - let cpu = cpus[cpu_index as usize].clone(); - if let Err(e) = CPU::start(cpu, cpu_thread_barrier, paused) { + if let Err(e) = CPU::start(cpu.clone(), cpu_thread_barrier, paused) { self.deactive_drive_files()?; return Err(anyhow!("Failed to run vcpu{}, {:?}", cpu_index, e)); } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6b8a2903b..36b6dfbd0 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -628,7 +628,7 @@ trait AcpiBuilder { let slit_begin = StdMachine::add_table_to_loader(acpi_data, loader, &slit) .with_context(|| "Fail to add SLIT table to loader")?; - Ok(slit_begin as u64) + Ok(slit_begin) } /// Build ACPI XSDT table, returns the offset of ACPI XSDT table in `acpi_data`. diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index a4d47307a..17871d4b7 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -110,7 +110,7 @@ impl LPCBridge { let cloned_reset_fd = self.reset_req.try_clone().unwrap(); let write_ops = move |data: &[u8], _addr: GuestAddress, _offset: u64| -> bool { let value: u8 = match data.len() { - 1 => data[0] as u8, + 1 => data[0], n => { error!("Invalid data length {}", n); return false; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index f2ecd7171..3fd1cba6e 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -625,7 +625,7 @@ impl AcpiBuilder for StdMachine { let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) .with_context(|| "Fail to add DSTD table to loader")?; - Ok(dsdt_begin as u64) + Ok(dsdt_begin) } fn build_madt_table( @@ -662,7 +662,7 @@ impl AcpiBuilder for StdMachine { let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) .with_context(|| "Fail to add DSTD table to loader")?; - Ok(madt_begin as u64) + Ok(madt_begin) } fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { @@ -777,7 +777,7 @@ impl AcpiBuilder for StdMachine { let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) .with_context(|| "Fail to add SRAT table to loader")?; - Ok(srat_begin as u64) + Ok(srat_begin) } } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index eb7065993..96f76c48b 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -334,7 +334,7 @@ impl VmConfig { if max_cpus < cpu { return Err(anyhow!(ConfigError::IllegalValue( "maxcpus".to_string(), - cpu as u64, + cpu, true, MAX_NR_CPUS, true, diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index ea468621d..8e2fd7766 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -184,7 +184,7 @@ pub trait DeviceInterface { /// Query the version of StratoVirt. fn query_version(&self) -> Response { let version = Version::new(1, 0, 5); - Response::create_response(serde_json::to_value(&version).unwrap(), None) + Response::create_response(serde_json::to_value(version).unwrap(), None) } /// Query all commands of StratoVirt. @@ -331,22 +331,22 @@ pub trait DeviceInterface { fn query_tpm_models(&self) -> Response { let tpm_models = Vec::::new(); - Response::create_response(serde_json::to_value(&tpm_models).unwrap(), None) + Response::create_response(serde_json::to_value(tpm_models).unwrap(), None) } fn query_tpm_types(&self) -> Response { let tpm_types = Vec::::new(); - Response::create_response(serde_json::to_value(&tpm_types).unwrap(), None) + Response::create_response(serde_json::to_value(tpm_types).unwrap(), None) } fn query_command_line_options(&self) -> Response { let cmd_lines = Vec::::new(); - Response::create_response(serde_json::to_value(&cmd_lines).unwrap(), None) + Response::create_response(serde_json::to_value(cmd_lines).unwrap(), None) } fn query_migrate_capabilities(&self) -> Response { let caps = Vec::::new(); - Response::create_response(serde_json::to_value(&caps).unwrap(), None) + Response::create_response(serde_json::to_value(caps).unwrap(), None) } fn query_qmp_schema(&self) -> Response { @@ -375,38 +375,38 @@ pub trait DeviceInterface { fn qom_list(&self) -> Response { let vec_cmd: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + Response::create_response(serde_json::to_value(vec_cmd).unwrap(), None) } fn qom_get(&self) -> Response { let vec_cmd: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + Response::create_response(serde_json::to_value(vec_cmd).unwrap(), None) } fn query_block(&self) -> Response { let vec_cmd: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + Response::create_response(serde_json::to_value(vec_cmd).unwrap(), None) } fn query_named_block_nodes(&self) -> Response { let vec_cmd: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + Response::create_response(serde_json::to_value(vec_cmd).unwrap(), None) } fn query_blockstats(&self) -> Response { let vec_cmd: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + Response::create_response(serde_json::to_value(vec_cmd).unwrap(), None) } fn query_block_jobs(&self) -> Response { // Fix me: qmp command call, return none temporarily. let vec_cmd: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_cmd).unwrap(), None) + Response::create_response(serde_json::to_value(vec_cmd).unwrap(), None) } fn query_gic_capabilities(&self) -> Response { let vec_gic: Vec = Vec::new(); - Response::create_response(serde_json::to_value(&vec_gic).unwrap(), None) + Response::create_response(serde_json::to_value(vec_gic).unwrap(), None) } fn query_iothreads(&self) -> Response { diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index d58d8befb..de9f2aa08 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -430,7 +430,7 @@ impl SocketRWHandler { let scm_cmsg_header = unsafe { std::slice::from_raw_parts( CMSG_DATA(scm), - std::mem::size_of::<[RawFd; 2]>() as usize, + std::mem::size_of::<[RawFd; 2]>(), ) }; for fd in scm_cmsg_header.iter() { diff --git a/migration/src/general.rs b/migration/src/general.rs index 9f9d8d9ca..4c5527678 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -123,7 +123,7 @@ impl MigrationManager { fd.read_exact(unsafe { std::slice::from_raw_parts_mut( &mut instance as *mut Instance as *mut u8, - size_of::() as usize, + size_of::(), ) }) .with_context(|| "Failed to read instance of object")?; diff --git a/pci/src/host.rs b/pci/src/host.rs index 93dca1e0d..6ea45b454 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -180,8 +180,8 @@ impl PciHost { let mut offset: u32 = (locked_hb.config_addr & !CONFIG_ADDRESS_ENABLE_MASK) + offset as u32; - let bus_num = ((offset as u32 >> PIO_BUS_SHIFT) & CONFIG_BUS_MASK) as u8; - let devfn = ((offset as u32 >> PIO_DEVFN_SHIFT) & CONFIG_DEVFN_MASK) as u8; + let bus_num = ((offset >> PIO_BUS_SHIFT) & CONFIG_BUS_MASK) as u8; + let devfn = ((offset >> PIO_DEVFN_SHIFT) & CONFIG_DEVFN_MASK) as u8; match locked_hb.find_device(bus_num, devfn) { Some(dev) => { offset &= PIO_OFFSET_MASK; @@ -203,8 +203,8 @@ impl PciHost { let mut offset: u32 = (locked_hb.config_addr & !CONFIG_ADDRESS_ENABLE_MASK) + offset as u32; - let bus_num = ((offset as u32 >> PIO_BUS_SHIFT) & CONFIG_BUS_MASK) as u8; - let devfn = ((offset as u32 >> PIO_DEVFN_SHIFT) & CONFIG_DEVFN_MASK) as u8; + let bus_num = ((offset >> PIO_BUS_SHIFT) & CONFIG_BUS_MASK) as u8; + let devfn = ((offset >> PIO_DEVFN_SHIFT) & CONFIG_DEVFN_MASK) as u8; if let Some(dev) = locked_hb.find_device(bus_num, devfn) { offset &= PIO_OFFSET_MASK; dev.lock().unwrap().write_config(offset as usize, data); diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 9b636df49..886bfa679 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -240,7 +240,7 @@ pub fn init_multifunction( if multifunction { header_type |= HEADER_TYPE_MULTIFUNC as u16; } - le_write_u16(config, HEADER_TYPE as usize, header_type as u16)?; + le_write_u16(config, HEADER_TYPE as usize, header_type)?; // Allow two ways of multifunction bit: // 1. The multifunction bit of all devices must be set; diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 125181590..0332dfc5b 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -267,7 +267,7 @@ impl Msix { .irq_route_table .lock() .unwrap() - .add_msi_route(gsi as u32, msix_vector) + .add_msi_route(gsi, msix_vector) .map_err(|e| { error!("Failed to add MSI-X route, error is {:?}", e); e @@ -283,7 +283,7 @@ impl Msix { .vm_fd .as_ref() .unwrap() - .register_irqfd(call_fd, gsi as u32) + .register_irqfd(call_fd, gsi) .map_err(|e| { error!("Failed to register irq, error is {:?}", e); e diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index f797676ec..f708fb803 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -266,8 +266,8 @@ impl RootPort { // Only unplug device when the slot is on // Don't unplug when slot is off for guest OS overwrite the off status before slot on. if (status & PCI_EXP_SLTSTA_PDS != 0) - && (val as u16 & PCI_EXP_SLTCTL_PCC == PCI_EXP_SLTCTL_PCC) - && (val as u16 & PCI_EXP_SLTCTL_PWR_IND_OFF == PCI_EXP_SLTCTL_PWR_IND_OFF) + && (val & PCI_EXP_SLTCTL_PCC == PCI_EXP_SLTCTL_PCC) + && (val & PCI_EXP_SLTCTL_PWR_IND_OFF == PCI_EXP_SLTCTL_PWR_IND_OFF) && (old_ctl & PCI_EXP_SLTCTL_PCC != PCI_EXP_SLTCTL_PCC || old_ctl & PCI_EXP_SLTCTL_PWR_IND_OFF != PCI_EXP_SLTCTL_PWR_IND_OFF) { diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index 1b12f07e3..cebfd152b 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -368,7 +368,7 @@ impl UsbDescriptorOps for UsbDevice { == USB_DIRECTION_DEVICE_TO_HOST; let ep = iface.endpoints[e as usize].endpoint_desc.bEndpointAddress & USB_ENDPOINT_ADDRESS_NUMBER_MASK; - let mut usb_ep = self.get_mut_endpoint(in_direction, ep as u8); + let mut usb_ep = self.get_mut_endpoint(in_direction, ep); usb_ep.ep_type = iface.endpoints[e as usize].endpoint_desc.bmAttributes & USB_ENDPOINT_ATTR_TRANSFER_TYPE_MASK; } diff --git a/usb/src/hid.rs b/usb/src/hid.rs index d41f46b1d..7ba0fdc8a 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -315,7 +315,7 @@ impl Hid { self.convert_to_hid_code(); data[0] = self.keyboard.modifiers as u8; data[1] = 0; - let len = (data.len() - 2) as usize; + let len = data.len() - 2; if self.keyboard.key_num > 6 { for i in 0..len { data[i + 2] = HID_USAGE_ERROR_ROLLOVER; diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 3d40c2253..2baec5dd0 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -85,7 +85,7 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { endpoint_desc: UsbEndpointDescriptor { bLength: USB_DT_ENDPOINT_SIZE, bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST as u8 | 0x1, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | 0x1, bmAttributes: USB_ENDPOINT_ATTR_INT, wMaxPacketSize: 8, bInterval: 0xa, diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index fa89a3dc8..83a55b712 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -88,7 +88,7 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { endpoint_desc: UsbEndpointDescriptor { bLength: USB_DT_ENDPOINT_SIZE, bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST as u8 | 0x1, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | 0x1, bmAttributes: USB_ENDPOINT_ATTR_INT, wMaxPacketSize: 8, bInterval: 0xa, diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index 6ec6c06d8..b94f9e9cf 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -365,7 +365,7 @@ impl XhciEvent { status: self.length | (self.ccode as u32) << EVENT_TRB_CCODE_SHIFT, control: (self.slot_id as u32) << EVENT_TRB_SLOT_ID_SHIFT | (self.ep_id as u32) << EVENT_TRB_EP_ID_SHIFT - | self.flags as u32 + | self.flags | (self.trb_type as u32) << TRB_TYPE_SHIFT, addr: 0, ccs: false, @@ -1582,7 +1582,7 @@ impl XhciDevice { /// Used for device to wakeup endpoint pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &UsbEndpoint) -> Result<()> { let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); - self.kick_endpoint(slot_id as u32, ep_id as u32)?; + self.kick_endpoint(slot_id, ep_id as u32)?; Ok(()) } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index c7881a30c..194ea7f1a 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -50,7 +50,7 @@ const XHCI_PCI_OPER_LENGTH: u32 = 0x400; const XHCI_PCI_RUNTIME_OFFSET: u32 = XHCI_OFF_RUNTIME; const XHCI_PCI_RUNTIME_LENGTH: u32 = (MAX_INTRS as u32 + 1) * 0x20; const XHCI_PCI_DOORBELL_OFFSET: u32 = XHCI_OFF_DOORBELL; -const XHCI_PCI_DOORBELL_LENGTH: u32 = (MAX_SLOTS as u32 + 1) * 0x20; +const XHCI_PCI_DOORBELL_LENGTH: u32 = (MAX_SLOTS + 1) * 0x20; const XHCI_PCI_PORT_OFFSET: u32 = XHCI_PCI_OPER_OFFSET + XHCI_PCI_OPER_LENGTH; const XHCI_PCI_PORT_LENGTH: u32 = 0x10; const XHCI_MSIX_TABLE_OFFSET: u32 = 0x3000; @@ -183,7 +183,7 @@ impl PciDevOps for XhciPciDevice { DEVICE_ID as usize, PCI_DEVICE_ID_REDHAT_XHCI, )?; - le_write_u16(&mut self.pci_config.config, REVISION_ID as usize, 0x3_u16)?; + le_write_u16(&mut self.pci_config.config, REVISION_ID, 0x3_u16)?; le_write_u16( &mut self.pci_config.config, SUB_CLASS_CODE as usize, diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 621aa9e77..3360f2876 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -38,8 +38,7 @@ impl IoUringContext { if (tmp_entries & -tmp_entries) != tmp_entries || tmp_entries == 0 { bail!("Entries must be the power of 2 and larger than 0"); } - let ring = - IoUring::new(entries as u32).with_context(|| "Failed to create io_uring instance")?; + let ring = IoUring::new(entries).with_context(|| "Failed to create io_uring instance")?; ring.submitter() .register_eventfd(eventfd.as_raw_fd()) diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index d01b55ca0..5f4c60e88 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -232,7 +232,7 @@ impl Bitmap { if num > self.vol() { return Err(anyhow!(UtilError::OutOfBound( num as u64, - (self.size() as u64 * T::len() as u64) as u64, + self.size() as u64 * T::len() as u64, ))); } Ok(T::bit_and( diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index a84b52a92..0cbcb038f 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -275,7 +275,7 @@ pub fn write_data_u16(data: &mut [u8], value: u16) -> bool { match data.len() { 1 => data[0] = value as u8, 2 => { - LittleEndian::write_u16(data, value as u16); + LittleEndian::write_u16(data, value); } n => { error!("Invalid data length {} for reading value {}", n, value); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 3a82f856f..d21829334 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -263,7 +263,7 @@ impl VfioPciDevice { table_offset: (table & MSIX_TABLE_OFFSET) as u64, table_size: (enteries * MSIX_TABLE_ENTRY_SIZE) as u64, }, - enteries: enteries as u16, + enteries, vfio_irq, }) } @@ -389,7 +389,7 @@ impl VfioPciDevice { let bar_region = if i == table_bar { region .add_subregion( - Region::init_io_region(table_size as u64, table_ops.clone()), + Region::init_io_region(table_size, table_ops.clone()), table_offset, ) .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index fae61458a..917369303 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -87,7 +87,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { } /// Filesystem configuration parsed from command line for the process. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct FsConfig { /// Source directory in host which can be accessed by guest. pub source_dir: String, @@ -101,18 +101,6 @@ pub struct FsConfig { pub proc_dir_opt: Option, } -impl Default for FsConfig { - fn default() -> Self { - FsConfig { - source_dir: String::from(""), - sock_path: String::from(""), - rlimit_nofile: None, - root_dir: String::from(""), - proc_dir_opt: None, - } - } -} - impl FsConfig { fn check_config(&self) -> Result<()> { if self.source_dir.len() > MAX_PATH_LENGTH { diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 21cf8c767..3502234f4 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -383,7 +383,7 @@ impl FileSystem { let proc_file = self.proc_dir.try_clone().unwrap(); let inode = match self.find_mut_inode(node_id) { Some(inode_) => inode_, - None => return (None, libc::EBADF as i32), + None => return (None, libc::EBADF), }; if let Some(lock) = inode.locks.get_mut(&owner) { @@ -391,7 +391,7 @@ impl FileSystem { } if inode.file_type & libc::S_IFDIR == 0 && inode.file_type & libc::S_IFREG == 0 { - return (None, libc::EBADF as i32); + return (None, libc::EBADF); } let (file_opt, ret) = open_at( @@ -415,7 +415,7 @@ impl FileSystem { fn delete_file_lock(&mut self, node_id: usize, owner: u64) -> i32 { let inode = match self.find_mut_inode(node_id) { Some(inode_) => inode_, - None => return libc::EBADF as i32, + None => return libc::EBADF, }; inode.locks.remove(&owner); @@ -495,7 +495,7 @@ impl FileSystem { let inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; self.internal_lookup(&inode, name, node_id, fuse_attr) @@ -511,7 +511,7 @@ impl FileSystem { let mut inode = match self.find_inode(node_id) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; self.unref_inode(&mut inode, nlookup); @@ -528,7 +528,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; let (stat, ret) = fstat_at( @@ -566,7 +566,7 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } else { @@ -582,7 +582,7 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } @@ -615,7 +615,7 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } @@ -630,14 +630,14 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } else { match self.find_inode(node_id) { Some(i) => { if i.file_type & libc::S_IFREG == 0 && i.file_type & libc::S_IFDIR == 0 { - return libc::EBADF as i32; + return libc::EBADF; } let (file_opt, ret) = open_at( @@ -658,7 +658,7 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } @@ -690,7 +690,7 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } else { @@ -710,7 +710,7 @@ impl FileSystem { } } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; } @@ -729,7 +729,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -741,7 +741,7 @@ impl FileSystem { if let Some(mut buf) = buf_opt { buff.append(&mut buf); } else { - return libc::EBADF as i32; + return libc::EBADF; } FUSE_OK @@ -767,7 +767,7 @@ impl FileSystem { let parent_inode = match self.find_inode(in_header.nodeid as usize) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -810,7 +810,7 @@ impl FileSystem { let parent_inode = match self.find_inode(in_header.nodeid as usize) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -857,7 +857,7 @@ impl FileSystem { let parent_dir = match self.find_inode(in_header.nodeid as usize) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -893,7 +893,7 @@ impl FileSystem { pub fn unlink(&mut self, parent_nodeid: usize, name: CString) -> i32 { let parent_inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), - None => return libc::EBADF as i32, + None => return libc::EBADF, }; let (stat, ret) = fstat_at( @@ -912,7 +912,7 @@ impl FileSystem { match self.inodes.get(&key) { Some(i) => i.clone(), - None => return libc::EIO as i32, + None => return libc::EIO, }; let ret = unlinkat(&parent_inode.file, name, 0); @@ -933,7 +933,7 @@ impl FileSystem { pub fn rmdir(&mut self, parent_nodeid: usize, name: CString) -> i32 { let parent_inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), - None => return libc::EBADF as i32, + None => return libc::EBADF, }; let (stat, ret) = fstat_at( @@ -952,7 +952,7 @@ impl FileSystem { match self.inodes.get(&key) { Some(i) => i.clone(), - None => return libc::EIO as i32, + None => return libc::EIO, }; let ret = unlinkat(&parent_inode.file, name, libc::AT_REMOVEDIR); @@ -983,14 +983,14 @@ impl FileSystem { let parent_inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; let newparent_inode = match self.find_inode(newparent_nodeid) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1010,7 +1010,7 @@ impl FileSystem { match self.inodes.get(&key) { Some(_) => {} - None => return libc::EIO as i32, + None => return libc::EIO, }; rename(&parent_inode.file, oldname, &newparent_inode.file, newname) @@ -1036,12 +1036,12 @@ impl FileSystem { let proc_file = self.proc_dir.try_clone().unwrap(); let parent_inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), - None => return libc::EBADF as i32, + None => return libc::EBADF, }; let inode = match self.find_mut_inode(old_nodeid) { Some(inode_) => inode_, - None => return libc::EBADF as i32, + None => return libc::EBADF, }; let ret = linkat( @@ -1088,12 +1088,12 @@ impl FileSystem { let (inode_fd, file_type) = match self.find_inode(node_id) { Some(i) => (i.as_raw_fd(), i.file_type), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { - return libc::EBADF as i32; + return libc::EBADF; } let (file_opt, ret) = open_at( @@ -1125,7 +1125,7 @@ impl FileSystem { *fd = file.as_raw_fd(); } _ => { - return libc::EBADF as i32; + return libc::EBADF; } } @@ -1144,7 +1144,7 @@ impl FileSystem { *fd = file.as_raw_fd(); } _ => { - return libc::EBADF as i32; + return libc::EBADF; } } @@ -1162,7 +1162,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1202,12 +1202,12 @@ impl FileSystem { let (inode_fd, file_type) = match self.find_inode(fh) { Some(i) => (i.as_raw_fd(), i.file_type), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { - return libc::EBADF as i32; + return libc::EBADF; } let (file_opt, ret_) = open_at( @@ -1223,7 +1223,7 @@ impl FileSystem { if let Some(file) = file_opt { ret = fsync(&file, datasync); } else { - return libc::EBADF as i32; + return libc::EBADF; } } else { match self.file_map.get_value(fh) { @@ -1231,7 +1231,7 @@ impl FileSystem { ret = fsync(file, datasync); } _ => { - return libc::EBADF as i32; + return libc::EBADF; } } } @@ -1260,7 +1260,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1278,7 +1278,7 @@ impl FileSystem { if let Some(file) = file_opt { fset_xattr(&file, name, value, size, flags) } else { - libc::EBADF as i32 + libc::EBADF } } else { if fchdir(&self.proc_dir) != FUSE_OK { @@ -1314,7 +1314,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1338,7 +1338,7 @@ impl FileSystem { buff.append(&mut buf); } } else { - return libc::EBADF as i32; + return libc::EBADF; } } else { if fchdir(&self.proc_dir) != FUSE_OK { @@ -1377,7 +1377,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1401,7 +1401,7 @@ impl FileSystem { buff.append(&mut buf); } } else { - return libc::EBADF as i32; + return libc::EBADF; } } else { if fchdir(&self.proc_dir) != FUSE_OK { @@ -1438,7 +1438,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1459,7 +1459,7 @@ impl FileSystem { return ret; } } else { - return libc::EBADF as i32; + return libc::EBADF; } } else { if fchdir(&self.proc_dir) != FUSE_OK { @@ -1553,7 +1553,7 @@ impl FileSystem { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1567,7 +1567,7 @@ impl FileSystem { return FUSE_OK; } - libc::EBADF as i32 + libc::EBADF } /// read a directory stream with the directory handler in the host filesystem. @@ -1592,7 +1592,7 @@ impl FileSystem { let dir_inode = match self.find_inode(node_id) { Some(i) => i.clone(), None => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1600,12 +1600,12 @@ impl FileSystem { Some(file) => { let (dirp_opt, ret) = fdopen_dir(file.as_raw_fd()); if ret != FUSE_OK { - return libc::EBADF as i32; + return libc::EBADF; } dirp_opt.unwrap() } _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1642,7 +1642,7 @@ impl FileSystem { let (entry_size, gap) = match round_up((only_entry_size + name_len) as u64, 8) { Some(v) => (v as u32, v as usize - (only_entry_size + name_len)), _ => { - return libc::EINVAL as i32; + return libc::EINVAL; } }; if entry_size > remain { @@ -1683,7 +1683,7 @@ impl FileSystem { buff.extend_from_slice( FuseDirentplus { entry_out: FuseEntryOut { - nodeid: son_nodeid as u64, + nodeid: son_nodeid, generation: 0, entry_valid: 0, entry_valid_nsec: 0, @@ -1736,7 +1736,7 @@ impl FileSystem { return ret; } } else { - return libc::EBADF as i32; + return libc::EBADF; } FUSE_OK @@ -1792,7 +1792,7 @@ impl FileSystem { file_lock_in: &FuseFileLock, ) -> i32 { if is_blocking { - return libc::EOPNOTSUPP as i32; + return libc::EOPNOTSUPP; } let (file_opt, ret) = self.create_file_lock(node_id, owner); @@ -1843,7 +1843,7 @@ impl FileSystem { return ret; } } else { - return libc::EBADF as i32; + return libc::EBADF; } FUSE_OK @@ -1871,7 +1871,7 @@ impl FileSystem { let parent_dir = match self.find_inode(in_header.nodeid as usize) { Some(i) => i.clone(), _ => { - return libc::EBADF as i32; + return libc::EBADF; } }; @@ -1934,7 +1934,7 @@ impl FileSystem { return ret; } } else { - return libc::EBADF as i32; + return libc::EBADF; } FUSE_OK @@ -1957,7 +1957,7 @@ impl FileSystem { } *outoffset = offset_tmp; } else { - return libc::EBADF as i32; + return libc::EBADF; } FUSE_OK diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index d7836e8d4..632325a7e 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -235,7 +235,7 @@ pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { }; if ret == -1 { - return (None, errno::errno().0 as i32); + return (None, errno::errno().0); } buf.resize(ret as usize, 0); @@ -257,7 +257,7 @@ pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { let ret = unsafe { libc::symlinkat(link_name.as_ptr(), file.as_raw_fd(), name.as_ptr()) }; if ret == -1 { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK @@ -329,7 +329,7 @@ pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { let ret = unsafe { libc::mknodat(file.as_raw_fd(), name.as_ptr(), mode, rdev as u64) }; if ret == -1 { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK @@ -366,7 +366,7 @@ pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { let ret = unsafe { libc::unlinkat(file.as_raw_fd(), name.as_ptr(), flags) }; if ret == -1 { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK @@ -395,7 +395,7 @@ pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> }; if ret != FUSE_OK { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK @@ -432,7 +432,7 @@ pub fn linkat( }; if ret == -1 { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK @@ -816,7 +816,7 @@ pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { unsafe { libc::fallocate(file.as_raw_fd(), mode as i32, offset as i64, length as i64) }; if ret == -1 { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK @@ -834,7 +834,7 @@ pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { let ret = unsafe { libc::lseek(file.as_raw_fd(), offset as i64, whence as i32) }; if ret == -1 { - return (0, errno::errno().0 as i32); + return (0, errno::errno().0); } (ret as u64, FUSE_OK) @@ -853,7 +853,7 @@ pub fn set_rlimit(rlim_cur: u64, rlim_max: u64) -> i32 { let ret = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &limit) }; if ret == -1 { - return errno::errno().0 as i32; + return errno::errno().0; } FUSE_OK diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index aea53ab00..13eae0807 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -1534,7 +1534,7 @@ pub fn do_fuse_create( }; let open_out = FuseOpenOut { - fh: fh as u64, + fh, open_flags: 0, padding: 0, }; diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index f92dae78c..051dc51ba 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -340,34 +340,34 @@ impl VhostUserReqHandler for VirtioFs { let used_addr = self.get_guest_address(used_ring)?; let avail_addr = self.get_guest_address(avail_ring)?; - if let Err(_ret) = - self.config - .get_mut_queue_config(queue_index as usize) - .map(|queue_info| { - queue_info.config.desc_table = GuestAddress(desc_addr); - queue_info.config.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(GuestAddress(desc_addr)) - .unwrap_or(0); - - queue_info.config.avail_ring = GuestAddress(avail_addr); - queue_info.config.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(GuestAddress(avail_addr)) - .unwrap_or(0); - - queue_info.config.used_ring = GuestAddress(used_addr); - queue_info.config.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(GuestAddress(used_addr)) - .unwrap_or(0); - - if queue_info.config.addr_cache.desc_table_host == 0 - || queue_info.config.addr_cache.avail_ring_host == 0 - || queue_info.config.addr_cache.used_ring_host == 0 - { - return Err(()); - } - - Ok(()) - }) + if let Err(_ret) = self + .config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + queue_info.config.desc_table = GuestAddress(desc_addr); + queue_info.config.addr_cache.desc_table_host = cloned_mem_space + .get_host_address(GuestAddress(desc_addr)) + .unwrap_or(0); + + queue_info.config.avail_ring = GuestAddress(avail_addr); + queue_info.config.addr_cache.avail_ring_host = cloned_mem_space + .get_host_address(GuestAddress(avail_addr)) + .unwrap_or(0); + + queue_info.config.used_ring = GuestAddress(used_addr); + queue_info.config.addr_cache.used_ring_host = cloned_mem_space + .get_host_address(GuestAddress(used_addr)) + .unwrap_or(0); + + if queue_info.config.addr_cache.desc_table_host == 0 + || queue_info.config.addr_cache.avail_ring_host == 0 + || queue_info.config.addr_cache.used_ring_host == 0 + { + return Err(()); + } + + Ok(()) + }) { bail!( "Failed to set vring addr, got host address failed. Index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 75d0dd8a3..b69e0166b 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -559,7 +559,7 @@ impl BalloonIoHandler { } locked_queue .vring - .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) + .add_used(&self.mem_space, req.desc_index, req.elem_cnt) .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { @@ -596,7 +596,7 @@ impl BalloonIoHandler { } locked_queue .vring - .add_used(&self.mem_space, req.desc_index, req.elem_cnt as u32) + .add_used(&self.mem_space, req.desc_index, req.elem_cnt) .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { @@ -869,7 +869,7 @@ impl VirtioDevice for Balloon { /// Get the type of balloon. fn device_type(&self) -> u32 { - VIRTIO_TYPE_BALLOON as u32 + VIRTIO_TYPE_BALLOON } /// Get the number of balloon-device queues. diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 1e3d69ed6..32b05bc2e 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -980,9 +980,9 @@ impl VirtioDevice for Block { if !self.blk_cfg.path_on_host.is_empty() { let drive_files = self.drive_files.lock().unwrap(); let mut file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; - disk_size = - file.seek(SeekFrom::End(0)) - .with_context(|| "Failed to seek the end for block")? as u64; + disk_size = file + .seek(SeekFrom::End(0)) + .with_context(|| "Failed to seek the end for block")?; disk_image = Some(Arc::new(file)); } self.disk_image = disk_image; diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 92deee342..5dee06db9 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -196,7 +196,7 @@ impl ConsoleHandler { } if let Some(output) = &mut self.chardev.lock().unwrap().output { let mut locked_output = output.lock().unwrap(); - if let Err(e) = locked_output.write_all(&buffer[..read_count as usize]) { + if let Err(e) = locked_output.write_all(&buffer[..read_count]) { error!("Failed to write to console output: {:?}", e); } if let Err(e) = locked_output.flush() { diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 933069832..fd211d365 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -606,7 +606,7 @@ impl GpuIoHandler { width: 64, hot_x: info_cursor.hot_x, hot_y: info_cursor.hot_y, - data: vec![0_u8; 64 * 64 * size_of::() as usize], + data: vec![0_u8; 64 * 64 * size_of::()], }; scanout.mouse = Some(tmp_mouse); } else { diff --git a/virtio/src/net.rs b/virtio/src/net.rs index afd25d56c..3e66576a7 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -946,7 +946,7 @@ fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { let mut end: usize = 0; for elem in iovec { - end = cmp::min(start + elem.iov_len as usize, buf.len()); + end = cmp::min(start + elem.iov_len, buf.len()); mem_to_buf(&mut buf[start..end], elem.iov_base as u64)?; if end >= buf.len() { break; @@ -1204,7 +1204,7 @@ fn get_default_mac_addr() -> Result<[u8; MAC_ADDR_LEN]> { let mut locked_mac_table = USED_MAC_TABLE.lock().unwrap(); for i in FIRST_DEFAULT_MAC[MAC_ADDR_LEN - 1]..MAX_MAC_ADDR_NUM as u8 { if locked_mac_table[i as usize] == 0 { - mac[MAC_ADDR_LEN - 1] = i as u8; + mac[MAC_ADDR_LEN - 1] = i; locked_mac_table[i as usize] = 1; return Ok(mac); } diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 589223988..137fb5611 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -721,7 +721,7 @@ impl ScsiRequest { if !outbuf.is_empty() { for (idx, iov) in req.iovec.iter().enumerate() { - if outbuf.len() as u64 > iov.iov_len as u64 { + if outbuf.len() as u64 > iov.iov_len { debug!( "cmd is {:x}, outbuf len is {}, iov_len is {}, idx is {}, iovec size is {}", self.cmd.command, @@ -1236,7 +1236,7 @@ fn scsi_command_emulate_mode_sense( let mut dev_specific_parameter: u8 = 0; let mut nb_sectors = dev_lock.disk_sectors as u32; let scsi_type = dev_lock.scsi_type; - let block_size = dev_lock.block_size as u32; + let block_size = dev_lock.block_size; nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; debug!( @@ -1289,7 +1289,7 @@ fn scsi_command_emulate_mode_sense( // Byte[4]: Reserved. // Byte[5-7]: Block Length. let mut block_desc: Vec = vec![0; 8]; - BigEndian::write_u32(&mut block_desc[0..4], nb_sectors as u32 & 0xffffff); + BigEndian::write_u32(&mut block_desc[0..4], nb_sectors & 0xffffff); BigEndian::write_u32(&mut block_desc[4..8], block_size); outbuf.append(&mut block_desc); } diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 92cfb9471..bf9052d46 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -166,8 +166,7 @@ impl ScsiDevice { let mut file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; disk_size = file .seek(SeekFrom::End(0)) - .with_context(|| "Failed to seek the end for scsi device")? - as u64; + .with_context(|| "Failed to seek the end for scsi device")?; self.disk_image = Some(Arc::new(file)); } else { diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 1bfda1feb..106a49cb3 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -160,7 +160,7 @@ impl VirtioDevice for Fs { } fn device_type(&self) -> u32 { - VIRTIO_TYPE_FS as u32 + VIRTIO_TYPE_FS } fn queue_num(&self) -> usize { diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 6f03f3700..2d1af105f 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -214,7 +214,7 @@ impl VirtioMmioCommonConfig { let value = match offset { MAGIC_VALUE_REG => MMIO_MAGIC_VALUE, VERSION_REG => MMIO_VERSION, - DEVICE_ID_REG => device.lock().unwrap().device_type() as u32, + DEVICE_ID_REG => device.lock().unwrap().device_type(), VENDOR_ID_REG => VENDOR_ID, DEVICE_FEATURES_REG => { let mut features = device @@ -482,7 +482,7 @@ impl VirtioMmioDevice { } VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; - interrupt_status.fetch_or(status as u32, Ordering::SeqCst); + interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) .with_context(|| anyhow!(VirtioError::EventFdWrite))?; @@ -523,11 +523,11 @@ impl SysBusDevOps for VirtioMmioDevice { .device .lock() .unwrap() - .read_config(offset as u64 - 0x100, data) + .read_config(offset - 0x100, data) { error!( "Failed to read virtio-dev config space {} type: {} {:?}", - offset as u64 - 0x100, + offset - 0x100, self.device.lock().unwrap().device_type(), e, ); @@ -595,11 +595,11 @@ impl SysBusDevOps for VirtioMmioDevice { .device .lock() .unwrap() - .write_config(offset as u64 - 0x100, data) + .write_config(offset - 0x100, data) { error!( "Failed to write virtio-dev config space {}, type: {}, {:?}", - offset as u64 - 0x100, + offset - 0x100, self.device.lock().unwrap().device_type(), e, ); diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 481d0d48a..4cbf46cc4 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -793,7 +793,7 @@ impl VringOps for SplitVring { self.next_used += Wrapping(1); sys_mem .write_object_direct( - &(self.next_used.0 as u16), + &(self.next_used.0), self.addr_cache.used_ring_host + VRING_IDX_POSITION, ) .with_context(|| "Failed to write next used idx")?; diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 20dd91f3b..925c8d88f 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -255,7 +255,7 @@ impl ClientIoHandler { // Authentication related information. let serverout = unsafe { CStr::from_ptr(serverout as *const c_char) }; let auth_message = String::from(serverout.to_str().unwrap()); - buf.append(&mut ((serverout_len + 1) as u32).to_be_bytes().to_vec()); + buf.append(&mut (serverout_len + 1).to_be_bytes().to_vec()); buf.append(&mut auth_message.as_bytes().to_vec()); } else { buf.append(&mut (0_u32).to_be_bytes().to_vec()); diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 728bcc923..758b9c488 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -361,16 +361,13 @@ impl ClientIoHandler { let mut locked_buffer = client.out_buffer.lock().unwrap(); while let Some(bytes) = locked_buffer.read_front_chunk() { let message_len = bytes.len(); - let send_len: usize; - match self.write_msg(bytes) { - Ok(ret) => { - send_len = ret; - } + let send_len = match self.write_msg(bytes) { + Ok(ret) => ret, Err(_e) => { self.client.conn_state.lock().unwrap().dis_conn = true; return; } - } + }; locked_buffer.remove_front(send_len); if send_len != message_len { @@ -717,16 +714,16 @@ impl ClientIoHandler { // First color. buf.append(&mut (0_u16).to_be_bytes().to_vec()); // Number of colors. - buf.append(&mut (NUM_OF_COLORMAP as u16).to_be_bytes().to_vec()); + buf.append(&mut NUM_OF_COLORMAP.to_be_bytes().to_vec()); let pf = self.client.client_dpm.lock().unwrap().pf.clone(); - for i in 0..NUM_OF_COLORMAP as u16 { + for i in 0..NUM_OF_COLORMAP { let r = ((i >> pf.red.shift) & pf.red.max as u16) << (16 - pf.red.bits); let g = ((i >> pf.green.shift) & pf.green.max as u16) << (16 - pf.green.bits); let b = ((i >> pf.blue.shift) & pf.blue.max as u16) << (16 - pf.blue.bits); - buf.append(&mut (r as u16).to_be_bytes().to_vec()); - buf.append(&mut (g as u16).to_be_bytes().to_vec()); - buf.append(&mut (b as u16).to_be_bytes().to_vec()); + buf.append(&mut r.to_be_bytes().to_vec()); + buf.append(&mut g.to_be_bytes().to_vec()); + buf.append(&mut b.to_be_bytes().to_vec()); } let client = self.client.clone(); @@ -1099,7 +1096,7 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: loop { // Find the first non-zero bit in dirty bitmap. let offset = locked_dirty.find_next_bit(y as usize * bpl).unwrap() as u64; - if offset >= height as u64 * bpl as u64 { + if offset >= height * bpl as u64 { break; } @@ -1123,7 +1120,7 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: h = i - y; x2 = cmp::min(x2, width / DIRTY_PIXELS_NUM as u64); - if x2 > x as u64 { + if x2 > x { rects.push(Rectangle::new( (x * DIRTY_PIXELS_NUM as u64) as i32, y as i32, @@ -1195,16 +1192,16 @@ pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { let mut locked_dpm = client.client_dpm.lock().unwrap(); locked_dpm.pf.init_pixelformat(); let big_endian: u8 = u8::from(cfg!(target_endian = "big")); - buf.append(&mut (locked_dpm.pf.pixel_bits as u8).to_be_bytes().to_vec()); // Bit per pixel. - buf.append(&mut (locked_dpm.pf.depth as u8).to_be_bytes().to_vec()); // Depth. - buf.append(&mut (big_endian as u8).to_be_bytes().to_vec()); // Big-endian flag. + buf.append(&mut locked_dpm.pf.pixel_bits.to_be_bytes().to_vec()); // Bit per pixel. + buf.append(&mut locked_dpm.pf.depth.to_be_bytes().to_vec()); // Depth. + buf.append(&mut big_endian.to_be_bytes().to_vec()); // Big-endian flag. buf.append(&mut (1_u8).to_be_bytes().to_vec()); // True-color flag. buf.append(&mut (locked_dpm.pf.red.max as u16).to_be_bytes().to_vec()); // Red max. buf.append(&mut (locked_dpm.pf.green.max as u16).to_be_bytes().to_vec()); // Green max. buf.append(&mut (locked_dpm.pf.blue.max as u16).to_be_bytes().to_vec()); // Blue max. - buf.append(&mut (locked_dpm.pf.red.shift as u8).to_be_bytes().to_vec()); // Red shift. - buf.append(&mut (locked_dpm.pf.green.shift as u8).to_be_bytes().to_vec()); // Green shift. - buf.append(&mut (locked_dpm.pf.blue.shift as u8).to_be_bytes().to_vec()); // Blue shift. + buf.append(&mut locked_dpm.pf.red.shift.to_be_bytes().to_vec()); // Red shift. + buf.append(&mut locked_dpm.pf.green.shift.to_be_bytes().to_vec()); // Green shift. + buf.append(&mut locked_dpm.pf.blue.shift.to_be_bytes().to_vec()); // Blue shift. buf.append(&mut [0; 3].to_vec()); // Padding. drop(locked_dpm); } @@ -1312,7 +1309,7 @@ pub fn display_cursor_define( cursor.hot_y as i32, cursor.width as i32, cursor.height as i32, - ENCODING_ALPHA_CURSOR as i32, + ENCODING_ALPHA_CURSOR, buf, ); buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); @@ -1335,7 +1332,7 @@ pub fn display_cursor_define( cursor.hot_y as i32, cursor.width as i32, cursor.height as i32, - ENCODING_RICH_CURSOR as i32, + ENCODING_RICH_CURSOR, buf, ); let dpm = client.client_dpm.lock().unwrap().clone(); diff --git a/vnc/src/console.rs b/vnc/src/console.rs index 944015c4d..6d93db84f 100644 --- a/vnc/src/console.rs +++ b/vnc/src/console.rs @@ -217,7 +217,7 @@ pub fn setup_refresh(update_interval: u64) { if update_interval != 0 { if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.delay_call(func, update_interval as u64 * MILLI_PER_SEC); + ctx.delay_call(func, update_interval * MILLI_PER_SEC); } } } diff --git a/vnc/src/encoding/enc_hextile.rs b/vnc/src/encoding/enc_hextile.rs index 5bdc30eab..56c666dfe 100644 --- a/vnc/src/encoding/enc_hextile.rs +++ b/vnc/src/encoding/enc_hextile.rs @@ -132,7 +132,7 @@ fn compress_each_tile<'a>( _ => {} } - buf.append(&mut (flag as u8).to_be_bytes().to_vec()); // SubEncoding-mask. + buf.append(&mut flag.to_be_bytes().to_vec()); // SubEncoding-mask. if flag & RAW == 0 { if flag & BACKGROUND_SPECIFIC != 0 { write_pixel( diff --git a/vnc/src/input.rs b/vnc/src/input.rs index f64e67d0b..e3837f888 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -244,11 +244,11 @@ impl ClientIoHandler { y = ((y as u64 * ABS_MAX) / height as u64) as u16; // ASCII -> HidCode. - let button_mask: u8 = match buf[1] as u8 { + let button_mask: u8 = match buf[1] { INPUT_POINT_LEFT => 0x01, INPUT_POINT_MIDDLE => 0x04, INPUT_POINT_RIGHT => 0x02, - _ => buf[1] as u8, + _ => buf[1], }; let locked_input = INPUT.lock().unwrap(); diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs index b4da1a3ae..4d5d0ca59 100644 --- a/vnc/src/pixman.rs +++ b/vnc/src/pixman.rs @@ -115,14 +115,14 @@ pub fn get_image_width(image: *mut pixman_image_t) -> i32 { if image.is_null() { return 0; } - unsafe { pixman_image_get_width(image as *mut pixman_image_t) as i32 } + unsafe { pixman_image_get_width(image as *mut pixman_image_t) } } pub fn get_image_height(image: *mut pixman_image_t) -> i32 { if image.is_null() { return 0; } - unsafe { pixman_image_get_height(image as *mut pixman_image_t) as i32 } + unsafe { pixman_image_get_height(image as *mut pixman_image_t) } } pub fn get_image_stride(image: *mut pixman_image_t) -> i32 { diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 3ed65f5cb..3583637b2 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -195,7 +195,7 @@ impl DisplayChangeListenerOperations for VncInterface { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let width = cursor.width as u64; let height = cursor.height as u64; - let bpl = round_up_div(width as u64, BIT_PER_BYTE as u64); + let bpl = round_up_div(width, BIT_PER_BYTE as u64); // Set the bit for mask. let bit_mask: u8 = 0x80; @@ -203,14 +203,14 @@ impl DisplayChangeListenerOperations for VncInterface { let first_bit = if cfg!(target_endian = "big") { 0_usize } else { - (bytes_per_pixel() - 1) as usize + bytes_per_pixel() - 1 }; for j in 0..height { let mut bit = bit_mask; for i in 0..width { let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; - if let Some(n) = cursor.data.get(idx as usize) { + if let Some(n) = cursor.data.get(idx) { if *n == 0xff { mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; } @@ -553,16 +553,16 @@ pub fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { } 4 => { if client_dpm.client_be { - ret = (v as u32).to_be_bytes(); + ret = v.to_be_bytes(); } else { - ret = (v as u32).to_le_bytes(); + ret = v.to_le_bytes(); } } _ => { if client_dpm.client_be { - ret = (v as u32).to_be_bytes(); + ret = v.to_be_bytes(); } else { - ret = (v as u32).to_le_bytes(); + ret = v.to_le_bytes(); } } } @@ -592,7 +592,7 @@ pub fn raw_send_framebuffer_update( let copy_bytes = rect.w as usize * bytes_per_pixel(); for _i in 0..rect.h { - write_pixel(data_ptr, copy_bytes as usize, client_dpm, buf); + write_pixel(data_ptr, copy_bytes, client_dpm, buf); data_ptr = (data_ptr as usize + stride as usize) as *mut u8; } -- Gitee From 9955856ec8e2ab9e1ef5ed3c84e04c23285c2cf3 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 6 Jan 2023 17:37:08 +0800 Subject: [PATCH 0685/2187] Ramfb: Check and comment for unsafe implementation Ramfb contains a raw pointer, but the pointer is not modified by multiple threads at the same time. But it needs to share with threads. Add comment for unsafe use. And modify comment for calling C function. Signed-off-by: Jinhao Gao --- devices/src/legacy/ramfb.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 10e651613..264bea3bb 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -47,6 +47,11 @@ pub struct RamfbState { sys_mem: Arc, } +// SAFETY: The type of image, the field of the struct DisplaySurface +// is the raw pointer. create_display_surface() method will create +// image object. The memory that the image pointer refers to is +// modified by guest OS and accessed by vnc. So implement Sync and +// Send is safe. unsafe impl Sync for RamfbState {} unsafe impl Send for RamfbState {} @@ -103,7 +108,9 @@ impl RamfbState { format, ..Default::default() }; - // pixman_image_create_bits() is C function, it's an unsafe function. + // SAFETY: pixman_image_create_bits() is C function. All + // parameters passed of the function have been checked. + // It returns a raw pointer. unsafe { ds.image = pixman_image_create_bits( format, @@ -113,6 +120,12 @@ impl RamfbState { stride as i32, ); } + + if ds.image.is_null() { + error!("Failed to create the surface of Ramfb!"); + return; + } + self.surface = Some(ds); } -- Gitee From 6aeb1e305d6b4fb99484b97536158f6bbc172855 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 6 Jan 2023 18:51:53 +0800 Subject: [PATCH 0686/2187] Ramfb: Fix unsafe unwrap operation If the format of data which firmware convey to Ramfb is incorrect, try_into.unwrap() operation may result in panic. Add the judgement of data length to prevent panic. Signed-off-by: Jinhao Gao --- devices/src/legacy/ramfb.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 264bea3bb..fae72b71c 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -136,6 +136,10 @@ impl RamfbState { impl FwCfgWriteCallback for RamfbState { fn write_callback(&mut self, data: Vec, _start: u64, _len: usize) { + if data.len() < 28 { + error!("RamfbCfg data format is incorrect"); + return; + } let addr = u64::from_be_bytes( data.as_slice() .split_at(size_of::()) -- Gitee From 16a0bcf7f5502bfa2fd6a48c432b9fb8a985d271 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Jan 2023 14:31:15 +0800 Subject: [PATCH 0687/2187] update unsafe comment for gmtime_r unsafe comment adds `SAFETY` label. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index ee2406bc5..0248cc4be 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -74,7 +74,7 @@ fn rtc_time_to_tm(time_val: i64) -> libc::tm { tm_zone: std::ptr::null_mut(), }; - // Safe because `libc::gmtime_r` just convert calendar time to + // SAFETY: `libc::gmtime_r` just convert calendar time to // broken-down format, and saved to `dest_tm`. unsafe { libc::gmtime_r(&time_val, &mut dest_tm) }; -- Gitee From e9839a27ed8d802b05794b5e69fc72e3da2dade7 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 10 Jan 2023 19:21:19 +0800 Subject: [PATCH 0688/2187] vhost-user-blk: fix some errors when negotiating features 1. It should use the common feature between spdk and stratovirt. 2. Setting features should be happened after feature negotation between stratovirt and guest. Signed-off-by: Yan Wang --- virtio/src/vhost/user/block.rs | 94 +++++++++++++++------------------ virtio/src/vhost/user/client.rs | 32 ++++++----- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 800dcdf19..7aa1547b8 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -31,7 +31,7 @@ use crate::{ virtio_has_feature, BlockState, VirtioDevice, VirtioInterrupt, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, - VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; pub struct Block { @@ -101,19 +101,30 @@ impl Block { Ok(()) } - /// Get config from spdk and setup config space. - fn setup_device(&mut self) -> Result<()> { + /// Negotiate features with spdk. + fn negotiate_features(&mut self) -> Result<()> { let client = self.client.as_ref().unwrap(); - let feature = self.state.device_features; + let features = client + .lock() + .unwrap() + .get_features() + .with_context(|| "Failed to get features for vhost-user blk")?; - if virtio_has_feature(feature, VHOST_USER_F_PROTOCOL_FEATURES) { - let protocol_feature = client + if virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES) { + let protocol_features = client .lock() .unwrap() .get_protocol_features() .with_context(|| "Failed to get protocol features for vhost-user blk")?; + let supported_protocol_features = + 1 << VHOST_USER_PROTOCOL_F_MQ | 1 << VHOST_USER_PROTOCOL_F_CONFIG; + client + .lock() + .unwrap() + .set_protocol_features(supported_protocol_features & protocol_features) + .with_context(|| "Failed to set protocol features for vhost-user blk")?; - if virtio_has_feature(protocol_feature, VHOST_USER_PROTOCOL_F_CONFIG as u32) { + if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_CONFIG as u32) { let config = client .lock() .unwrap() @@ -122,12 +133,12 @@ impl Block { self.state.config_space = config; } else { bail!( - "Failed to get config, spdk doesn't support, spdk protocol feature: {:#b}", - protocol_feature + "Failed to get config, spdk doesn't support, spdk protocol features: {:#b}", + protocol_features ); } - if virtio_has_feature(protocol_feature, VHOST_USER_PROTOCOL_F_MQ as u32) { + if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_MQ as u32) { let max_queue_num = client .lock() .unwrap() @@ -145,53 +156,28 @@ impl Block { } } else if self.blk_cfg.queues > 1 { bail!( - "spdk doesn't support multi queue, spdk protocol feature: {:#b}", - protocol_feature + "spdk doesn't support multi queue, spdk protocol features: {:#b}", + protocol_features ); } } - client - .lock() - .unwrap() - .set_virtio_blk_config(self.state.config_space) - .with_context(|| "Failed to set config for vhost-user blk")?; - Ok(()) - } - - /// Negotiate feature with spdk. - fn negotiate_feature(&mut self) -> Result<()> { - let client = self.client.as_ref().unwrap(); - - let mut feature = client - .lock() - .unwrap() - .get_features() - .with_context(|| "Failed to get features for vhost-user blk")?; - - feature |= 1_u64 << VIRTIO_F_VERSION_1; - feature |= 1_u64 << VIRTIO_BLK_F_SIZE_MAX; - feature |= 1_u64 << VIRTIO_BLK_F_TOPOLOGY; - feature |= 1_u64 << VIRTIO_BLK_F_BLK_SIZE; - feature |= 1_u64 << VIRTIO_BLK_F_FLUSH; - feature |= 1_u64 << VIRTIO_BLK_F_DISCARD; - feature |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; - feature |= 1_u64 << VIRTIO_BLK_F_SEG_MAX; - feature &= !(1_u64 << VIRTIO_F_RING_PACKED); + self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 + | 1_u64 << VIRTIO_BLK_F_SIZE_MAX + | 1_u64 << VIRTIO_BLK_F_TOPOLOGY + | 1_u64 << VIRTIO_BLK_F_BLK_SIZE + | 1_u64 << VIRTIO_BLK_F_FLUSH + | 1_u64 << VIRTIO_BLK_F_DISCARD + | 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES + | 1_u64 << VIRTIO_BLK_F_SEG_MAX; if self.blk_cfg.read_only { - feature |= 1_u64 << VIRTIO_BLK_F_RO; + self.state.device_features |= 1_u64 << VIRTIO_BLK_F_RO; }; if self.blk_cfg.queues > 1 { - feature |= 1_u64 << VIRTIO_BLK_F_MQ; + self.state.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; } + self.state.device_features &= features; - self.state.device_features = feature; - - client - .lock() - .unwrap() - .set_features(feature) - .with_context(|| "Failed to set features for vhost-user blk")?; Ok(()) } } @@ -200,8 +186,8 @@ impl VirtioDevice for Block { /// Realize vhost user blk pci device. fn realize(&mut self) -> Result<()> { self.init_client()?; - self.negotiate_feature()?; - self.setup_device()?; + self.negotiate_features()?; + Ok(()) } @@ -272,6 +258,14 @@ impl VirtioDevice for Block { bail!("Failed to write config to guest for vhost user blk pci, config space address overflow.") } + self.client + .as_ref() + .unwrap() + .lock() + .unwrap() + .set_virtio_blk_config(self.state.config_space) + .with_context(|| "Failed to set config for vhost-user blk")?; + Ok(()) } diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 106300d25..25f2a0703 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -455,6 +455,24 @@ impl VhostUserClient { Ok(features) } + /// Send u64 value to vhost. + fn set_value(&self, request: VhostUserMsgReq, value: u64) -> Result<()> { + let client = self.client.lock().unwrap(); + let hdr = VhostUserMsgHdr::new(request as u32, 0, size_of::() as u32); + let payload_opt: Option<&[u8]> = None; + client + .sock + .send_msg(Some(&hdr), Some(&value), payload_opt, &[]) + .with_context(|| "Failed to send msg for setting value")?; + + Ok(()) + } + + /// Set protocol features to vhost. + pub fn set_protocol_features(&self, features: u64) -> Result<()> { + self.set_value(VhostUserMsgReq::SetProtocolFeatures, features) + } + /// Get virtio blk config from vhost. pub fn get_virtio_blk_config(&self) -> Result { let client = self.client.lock().unwrap(); @@ -548,19 +566,7 @@ impl VhostOps for VhostUserClient { } fn set_features(&self, features: u64) -> Result<()> { - let client = self.client.lock().unwrap(); - let hdr = VhostUserMsgHdr::new( - VhostUserMsgReq::SetFeatures as u32, - 0, - size_of::() as u32, - ); - let payload_opt: Option<&[u8]> = None; - client - .sock - .send_msg(Some(&hdr), Some(&features), payload_opt, &[]) - .with_context(|| "Failed to send msg for setting features")?; - - Ok(()) + self.set_value(VhostUserMsgReq::SetFeatures, features) } fn set_mem_table(&self) -> Result<()> { -- Gitee From 63639979788117bfea39b02860de7dda4ef0632b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 10 Jan 2023 19:28:55 +0800 Subject: [PATCH 0689/2187] vhost-user-blk: support bootindex It supports using vhost-user-blk as the rootfs to boot the common vm. Signed-off-by: Yan Wang --- machine_manager/src/config/drive.rs | 7 ++++++- machine_manager/src/config/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index fca34aa06..cd7b86476 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -400,7 +400,8 @@ pub fn parse_vhost_user_blk_pci( .push("addr") .push("num-queues") .push("chardev") - .push("queue-size"); + .push("queue-size") + .push("bootindex"); cmd_parser.parse(drive_config)?; @@ -408,6 +409,10 @@ pub fn parse_vhost_user_blk_pci( let mut blkdevcfg = BlkDevConfig::default(); + if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { + blkdevcfg.boot_index = Some(boot_index); + } + if let Some(chardev) = cmd_parser.get_value::("chardev")? { blkdevcfg.chardev = Some(chardev); } else { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 1c8bdd123..20bd38ace 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -128,8 +128,8 @@ impl VmConfig { bail!("kernel file is required for microvm machine type, which is not provided"); } - if self.boot_source.initrd.is_none() && self.drives.is_empty() { - bail!("Before Vm start, set a initrd or drive_file as rootfs"); + if self.boot_source.initrd.is_none() && self.drives.is_empty() && self.chardev.is_empty() { + bail!("Before Vm start, set a initrd or drive_file or vhost-user blk as rootfs"); } let mut stdio_count = 0; -- Gitee From 2cb7cf101c3b58b5eb68e80c70509b74dbb7e5b3 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 10 Jan 2023 19:35:50 +0800 Subject: [PATCH 0690/2187] usb: implement usb packet transmission by using method Implement usb packet transmission by using method. Signed-off-by: zhouli57 --- usb/src/hid.rs | 4 +-- usb/src/usb.rs | 83 ++++++++++++++++++++++++++------------------------ 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 7ba0fdc8a..e038f1574 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -15,7 +15,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use log::{debug, error, warn}; use crate::config::*; -use crate::usb::{usb_packet_transfer, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; +use crate::usb::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; /// HID keycode const HID_KEYBOARD_LEFT_CONTROL: u8 = 0xe0; @@ -511,7 +511,7 @@ impl Hid { } } let len = buf.len(); - usb_packet_transfer(p, &mut buf, len); + p.transfer_packet(&mut buf, len); } else { error!("Unhandled endpoint {}", p.ep_number); p.status = UsbPacketStatus::Stall; diff --git a/usb/src/usb.rs b/usb/src/usb.rs index d5bd3a65e..522af8b6d 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -342,7 +342,7 @@ pub trait UsbDeviceOps: Send + Sync { bail!("data buffer small len {}", device_req.length); } if p.pid as u8 == USB_TOKEN_OUT { - usb_packet_transfer(p, &mut usb_dev.data_buf, device_req.length as usize); + p.transfer_packet(&mut usb_dev.data_buf, device_req.length as usize); } self.handle_control(p, &device_req); let usb_dev = self.get_mut_usb_device(); @@ -355,7 +355,7 @@ pub trait UsbDeviceOps: Send + Sync { } if p.pid as u8 == USB_TOKEN_IN { p.actual_length = 0; - usb_packet_transfer(p, &mut usb_dev.data_buf, len as usize); + p.transfer_packet(&mut usb_dev.data_buf, len as usize); } Ok(()) } @@ -450,6 +450,40 @@ impl UsbPacket { self.parameter = 0; self.ep_number = ep_number; } + + /// Transfer USB packet from host to device or from device to host. + /// + /// # Arguments + /// + /// * `vec` - Data buffer. + /// * `len` - Transfer length. + pub fn transfer_packet(&mut self, vec: &mut [u8], len: usize) { + let len = min(vec.len(), len); + let to_host = self.pid as u8 & USB_TOKEN_IN == USB_TOKEN_IN; + let mut copyed = 0; + if to_host { + for iov in &self.iovecs { + let cnt = min(iov.iov_len, len - copyed); + let tmp = &vec[copyed..(copyed + cnt)]; + write_mem(iov.iov_base, tmp); + copyed += cnt; + if len == copyed { + break; + } + } + } else { + for iov in &self.iovecs { + let cnt = min(iov.iov_len, len - copyed); + let tmp = &mut vec[copyed..(copyed + cnt)]; + read_mem(iov.iov_base, tmp); + copyed += cnt; + if len == copyed { + break; + } + } + } + self.actual_length = copyed as u32; + } } impl Default for UsbPacket { @@ -478,35 +512,6 @@ fn write_mem(hva: u64, buf: &[u8]) { } } -/// Transfer packet from host to device or from device to host. -pub fn usb_packet_transfer(packet: &mut UsbPacket, vec: &mut [u8], len: usize) { - let len = min(vec.len(), len); - let to_host = packet.pid as u8 & USB_TOKEN_IN == USB_TOKEN_IN; - let mut copyed = 0; - if to_host { - for iov in &packet.iovecs { - let cnt = min(iov.iov_len, len - copyed); - let tmp = &vec[copyed..(copyed + cnt)]; - write_mem(iov.iov_base, tmp); - copyed += cnt; - if len - copyed == 0 { - break; - } - } - } else { - for iov in &packet.iovecs { - let cnt = min(iov.iov_len, len - copyed); - let tmp = &mut vec[copyed..(copyed + cnt)]; - read_mem(iov.iov_base, tmp); - copyed += cnt; - if len - copyed == 0 { - break; - } - } - } - packet.actual_length = copyed as u32; -} - #[cfg(test)] mod tests { use super::*; @@ -520,7 +525,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva, 4)); packet.iovecs.push(Iovec::new(hva + 4, 2)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; - usb_packet_transfer(&mut packet, &mut data, 6); + packet.transfer_packet(&mut data, 6); assert_eq!(packet.actual_length, 6); assert_eq!(buf, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); } @@ -534,7 +539,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva, 4)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; - usb_packet_transfer(&mut packet, &mut data, 6); + packet.transfer_packet(&mut data, 6); assert_eq!(packet.actual_length, 4); assert_eq!(buf, [1, 2, 3, 4, 0, 0, 0, 0, 0, 0]); } @@ -548,7 +553,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva, 4)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; - usb_packet_transfer(&mut packet, &mut data, 2); + packet.transfer_packet(&mut data, 2); assert_eq!(packet.actual_length, 2); assert_eq!(buf, [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]); } @@ -562,7 +567,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva, 10)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; - usb_packet_transfer(&mut packet, &mut data, 10); + packet.transfer_packet(&mut data, 10); assert_eq!(packet.actual_length, 6); assert_eq!(buf, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); } @@ -577,7 +582,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva + 4, 2)); let mut data = [0_u8; 10]; - usb_packet_transfer(&mut packet, &mut data, 6); + packet.transfer_packet(&mut data, 6); assert_eq!(packet.actual_length, 6); assert_eq!(data, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); } @@ -592,7 +597,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva + 4, 2)); let mut data = [0_u8; 10]; - usb_packet_transfer(&mut packet, &mut data, 10); + packet.transfer_packet(&mut data, 10); assert_eq!(packet.actual_length, 6); assert_eq!(data, [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]); } @@ -606,7 +611,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva, 4)); let mut data = [0_u8; 10]; - usb_packet_transfer(&mut packet, &mut data, 2); + packet.transfer_packet(&mut data, 2); assert_eq!(packet.actual_length, 2); assert_eq!(data, [1, 2, 0, 0, 0, 0, 0, 0, 0, 0]); } @@ -620,7 +625,7 @@ mod tests { packet.iovecs.push(Iovec::new(hva, 6)); let mut data = [0_u8; 2]; - usb_packet_transfer(&mut packet, &mut data, 6); + packet.transfer_packet(&mut data, 6); assert_eq!(packet.actual_length, 2); assert_eq!(data, [1, 2]); } -- Gitee From a00a7b8de9af810c26d170ef22741fa666a4d7bd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 10 Jan 2023 20:09:30 +0800 Subject: [PATCH 0691/2187] usb: use the read/write mem in util Use the read/weite mem in util. Signed-off-by: zhouli57 --- usb/src/usb.rs | 22 +++++++--------------- util/src/aio/mod.rs | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/usb/src/usb.rs b/usb/src/usb.rs index 522af8b6d..ad3c26df9 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; use log::{debug, error}; +use util::aio::{mem_from_buf, mem_to_buf}; use crate::config::*; use crate::descriptor::{UsbDescriptor, UsbDescriptorOps}; @@ -465,7 +466,9 @@ impl UsbPacket { for iov in &self.iovecs { let cnt = min(iov.iov_len, len - copyed); let tmp = &vec[copyed..(copyed + cnt)]; - write_mem(iov.iov_base, tmp); + if let Err(e) = mem_from_buf(tmp, iov.iov_base) { + error!("Failed to write mem: {}", e); + } copyed += cnt; if len == copyed { break; @@ -475,7 +478,9 @@ impl UsbPacket { for iov in &self.iovecs { let cnt = min(iov.iov_len, len - copyed); let tmp = &mut vec[copyed..(copyed + cnt)]; - read_mem(iov.iov_base, tmp); + if let Err(e) = mem_to_buf(tmp, iov.iov_base) { + error!("Failed to read mem {}", e); + } copyed += cnt; if len == copyed { break; @@ -499,19 +504,6 @@ impl Default for UsbPacket { } } -fn read_mem(hva: u64, buf: &mut [u8]) { - let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; - buf.clone_from_slice(&slice[..buf.len()]); -} - -fn write_mem(hva: u64, buf: &[u8]) { - use std::io::Write; - let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; - if let Err(e) = (&mut slice).write(buf) { - error!("Failed to write mem {:?}", e); - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index fc00d61df..4766cbb0b 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -361,7 +361,7 @@ impl Aio { } } -fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { +pub fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { // SAFETY: all callers have valid hva address. let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; (&mut slice) -- Gitee From 9a6d6c76e5538f21766b9c1f43f53ebfd554f6dc Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 12 Jan 2023 10:04:06 +0800 Subject: [PATCH 0692/2187] rtc: update registers when reset The rtc reset phase should reset registers A and B. Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 0248cc4be..cee01efea 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -142,16 +142,7 @@ impl RTC { let tm = rtc_time_to_tm(rtc.get_current_value() as i64); rtc.set_rtc_cmos(tm); - // Set Time frequency divider and Rate selection frequency in Register-A. - // Bits 6-4 = Time frequency divider (010 = 32.768KHz). - // Bits 3-0 = Rate selection frequency (110 = 1.024KHz, 976.562s). - rtc.cmos_data[RTC_REG_A as usize] = 0x26; - - // Set 24 hour mode in Register-B. - rtc.cmos_data[RTC_REG_B as usize] = 0x02; - - // Set VRT bit in Register-D, indicates that RAM and time are valid. - rtc.cmos_data[RTC_REG_D as usize] = 0x80; + rtc.init_rtc_reg(); Ok(rtc) } @@ -195,6 +186,19 @@ impl RTC { } } + fn init_rtc_reg(&mut self) { + // Set Time frequency divider and Rate selection frequency in Register-A. + // Bits 6-4 = Time frequency divider (010 = 32.768KHz). + // Bits 3-0 = Rate selection frequency (110 = 1.024KHz, 976.562s). + self.cmos_data[RTC_REG_A as usize] = 0x26; + + // Set 24 hour mode in Register-B. + self.cmos_data[RTC_REG_B as usize] = 0x02; + + // Set VRT bit in Register-D, indicates that RAM and time are valid. + self.cmos_data[RTC_REG_D as usize] = 0x80; + } + fn read_data(&mut self, data: &mut [u8]) -> bool { if data.len() != 1 { error!("RTC only supports reading data byte by byte."); @@ -388,7 +392,7 @@ impl SysBusDevOps for RTC { fn reset(&mut self) -> sysbus::Result<()> { self.cmos_data.fill(0); - self.cmos_data[RTC_REG_D as usize] = 0x80; + self.init_rtc_reg(); self.set_memory(self.mem_size, self.gap_start); Ok(()) } -- Gitee From 19f2355e5df1aefb087f0b54555bcb5a71fdb696 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 12 Jan 2023 11:56:48 +0800 Subject: [PATCH 0693/2187] rtc: fix overflow risk in get_current_value Fix addition overflow risk. Signed-off-by: yezengruan --- devices/src/legacy/pl031.rs | 2 +- devices/src/legacy/rtc.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 3ba4a3996..269649984 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -121,7 +121,7 @@ impl PL031 { /// Get current clock value. fn get_current_value(&self) -> u32 { - self.base_time.elapsed().as_secs() as u32 + self.tick_offset + (self.base_time.elapsed().as_secs() as u128 + self.tick_offset as u128) as u32 } fn inject_interrupt(&self) { diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index cee01efea..9efcfb22c 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -139,7 +139,7 @@ impl RTC { base_time: Instant::now(), }; - let tm = rtc_time_to_tm(rtc.get_current_value() as i64); + let tm = rtc_time_to_tm(rtc.get_current_value()); rtc.set_rtc_cmos(tm); rtc.init_rtc_reg(); @@ -205,7 +205,7 @@ impl RTC { return false; } - let tm = rtc_time_to_tm(self.get_current_value() as i64); + let tm = rtc_time_to_tm(self.get_current_value()); self.set_rtc_cmos(tm); match self.cur_index { RTC_REG_A => { @@ -282,8 +282,8 @@ impl RTC { } /// Get current clock value. - fn get_current_value(&self) -> u64 { - self.base_time.elapsed().as_secs() + self.tick_offset + fn get_current_value(&self) -> i64 { + (self.base_time.elapsed().as_secs() as i128 + self.tick_offset as i128) as i64 } fn set_rtc_cmos(&mut self, tm: libc::tm) { -- Gitee From 36a5001fbb2abd05077c1cf1fe74dc588f4ed709 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 12 Jan 2023 16:57:28 +0800 Subject: [PATCH 0694/2187] virtio-pci: fix reset() in VirtioPciDevice Reset in VirtioPciDevice should also reset common config and deactivate deivce. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 8e6e5fd58..5e2512b5a 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -820,7 +820,6 @@ impl VirtioPciDevice { return false; } } - update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); true } @@ -1258,7 +1257,11 @@ impl PciDevOps for VirtioPciDevice { .lock() .unwrap() .reset() - .with_context(|| "Fail to reset virtio device") + .with_context(|| "Failed to reset virtio device")?; + self.common_config.lock().unwrap().reset(); + self.deactivate_device(); + + Ok(()) } fn get_dev_path(&self) -> Option { -- Gitee From ddccf51198278ac64268f455385ebea482bdb2a1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 12 Jan 2023 17:33:10 +0800 Subject: [PATCH 0695/2187] virtio-net: no need to clear control information The reset() in VirtioPciDevice will call deactivate() for device and the CtrlInfo will be created in activate(). So, there is no need to reset it, just set it to None in deactivate() to free it. Signed-off-by: Yan Wang --- virtio/src/net.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 91d270dd8..da50cf4ba 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1612,19 +1612,8 @@ impl VirtioDevice for Net { } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts) - } - - fn reset(&mut self) -> Result<()> { - if let Some(ctrl_info) = &self.ctrl_info { - let mut locked_ctrl = ctrl_info.lock().unwrap(); - locked_ctrl.rx_mode = Default::default(); - locked_ctrl.mac_info = Default::default(); - locked_ctrl.vlan_map = HashMap::new(); - } else { - bail!("Control information is None"); - } - + unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + self.ctrl_info = None; Ok(()) } } -- Gitee From eecf19d8092ef8a29b636223bd7f5691bceedc71 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 12 Jan 2023 18:00:32 +0800 Subject: [PATCH 0696/2187] vhost-user-blk: optmize reset() and deactivate() The reset() and deactivate() in vhost-user-blk has lots of unnecessary code, optimize it. Signed-off-by: Yan Wang --- virtio/src/vhost/user/block.rs | 26 ++++++-------------- virtio/src/vhost/user/client.rs | 42 ++++++++++++++++++++++++++++++-- virtio/src/vhost/user/message.rs | 1 + 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 7aa1547b8..f5ea3731b 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -23,7 +23,6 @@ use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; -use crate::block::VirtioBlkConfig; use crate::vhost::VhostOps; use crate::VhostUser::client::{VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ}; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; @@ -73,16 +72,6 @@ impl Block { Ok(()) } - fn clean_up(&mut self) -> Result<()> { - self.delete_event()?; - self.state.config_space = VirtioBlkConfig::default(); - self.state.device_features = 0u64; - self.state.driver_features = 0u64; - self.client = None; - - Ok(()) - } - /// Connect with spdk and register update event. fn init_client(&mut self) -> Result<()> { let socket_path = self @@ -290,15 +279,14 @@ impl VirtioDevice for Block { /// Deactivate device. fn deactivate(&mut self) -> Result<()> { + self.client + .as_ref() + .unwrap() + .lock() + .unwrap() + .reset_vhost_user()?; self.call_events.clear(); - self.clean_up()?; - self.realize() - } - - /// Reset device. - fn reset(&mut self) -> Result<()> { - self.clean_up()?; - self.realize() + self.delete_event() } /// Unrealize device. diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 25f2a0703..5b2a60cd4 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -426,6 +426,26 @@ impl VhostUserClient { Ok(()) } + pub fn reset_vhost_user(&mut self) -> Result<()> { + let mut queue_num = self.queues.len(); + if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { + queue_num -= 1; + } + + for (queue_index, _) in self.queues.iter().enumerate().take(queue_num) { + self.set_vring_enable(queue_index, false) + .with_context(|| format!("Failed to set vring disable, index: {}", queue_index))?; + self.get_vring_base(queue_index) + .with_context(|| format!("Failed to get vring base, index: {}", queue_index))?; + } + + self.queue_evts.clear(); + self.call_events.clear(); + self.queues.clear(); + + Ok(()) + } + pub fn add_event(client: &Arc>) -> Result<()> { let notifiers = EventNotifierHelper::internal_notifiers(client.clone()); register_event_helper(notifiers, None, &mut client.lock().unwrap().delete_evts) @@ -775,7 +795,25 @@ impl VhostOps for VhostUserClient { bail!("Does not support for resetting owner") } - fn get_vring_base(&self, _queue_idx: usize) -> Result { - bail!("Does not support for getting vring base") + fn get_vring_base(&self, queue_idx: usize) -> Result { + let client = self.client.lock().unwrap(); + let request = VhostUserMsgReq::GetVringBase as u32; + let hdr = VhostUserMsgHdr::new( + request, + VhostUserHdrFlag::NeedReply as u32, + size_of::() as u32, + ); + + let vring_state = VhostUserVringState::new(queue_idx as u32, 0_u32); + let payload_opt: Option<&[u8]> = None; + client + .sock + .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) + .with_context(|| "Failed to send msg for getting vring base")?; + let res = client + .wait_ack_msg::(request) + .with_context(|| "Failed to wait ack msg for getting vring base")?; + + Ok(res.value as u16) } } diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 2731085ed..a4ebb2995 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -254,6 +254,7 @@ impl Default for VhostUserMemContext { /// The configuration for the state of virtual ring. #[repr(C)] +#[derive(Default)] pub struct VhostUserVringState { /// Index for virtual ring. pub index: u32, -- Gitee From af383429dcd3af00ad76495c0fdb269d3d7a35e4 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 16:45:20 +0800 Subject: [PATCH 0697/2187] eventfd: Arc evtfd to avoid the fd num changes by try_clone() Frequently dup fd with EventFd::try_clone() makes evt fd quite unstable, and difficult to track, because one EventFd refers to multiple process fd numbers. This patch optimise the memory part, by changing the EventFd to Arc, thus when it's cloned, the fd number stays the same. Signed-off-by: Zhang Bo --- address_space/src/address_space.rs | 2 +- address_space/src/region.rs | 27 ++++----------------------- virtio/src/virtio_mmio.rs | 2 +- virtio/src/virtio_pci.rs | 2 +- 4 files changed, 7 insertions(+), 26 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 253c063a1..9bb3d1e59 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -326,7 +326,7 @@ impl AddressSpace { for fr in self.flat_view.load().0.iter() { let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region).0; for evtfd in fr.owner.ioeventfds().iter() { - let mut evtfd_clone = evtfd.try_clone()?; + let mut evtfd_clone = evtfd.clone(); evtfd_clone.addr_range.base = evtfd_clone.addr_range.base.unchecked_add(region_base); if fr diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 3422dd5a8..86a163808 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -88,9 +88,10 @@ impl fmt::Debug for Region { /// Used to trigger events. /// If `data_match` is enabled, the `EventFd` is triggered iff `data` is written /// to the specified address. +#[derive(Clone)] pub struct RegionIoEventFd { /// EventFd to be triggered when guest writes to the address. - pub fd: vmm_sys_util::eventfd::EventFd, + pub fd: Arc, /// Addr_range contains two params as follows: /// base: in addr_range is the address of EventFd. /// size: can be 2, 4, 8 bytes. @@ -132,21 +133,6 @@ impl RegionIoEventFd { } false } - - /// Return the cloned Region IoEventFd, - /// return error if failed to clone EventFd. - pub(crate) fn try_clone(&self) -> Result { - let fd = self - .fd - .try_clone() - .map_err(|_| anyhow!(AddressSpaceError::IoEventFd))?; - Ok(RegionIoEventFd { - fd, - addr_range: self.addr_range, - data_match: self.data_match, - data: self.data, - }) - } } /// FlatRange is a piece of continuous memory address。 @@ -668,18 +654,13 @@ impl Region { /// Set the ioeventfds within this Region, /// Return the IoEvent of a `Region`. pub fn set_ioeventfds(&self, new_fds: &[RegionIoEventFd]) { - *self.io_evtfds.lock().unwrap() = new_fds.iter().map(|e| e.try_clone().unwrap()).collect(); + *self.io_evtfds.lock().unwrap() = new_fds.to_vec(); } /// Get the ioeventfds within this Region, /// these fds will be register to `KVM` and used for guest notifier. pub fn ioeventfds(&self) -> Vec { - self.io_evtfds - .lock() - .unwrap() - .iter() - .map(|e| e.try_clone().unwrap()) - .collect() + self.io_evtfds.lock().unwrap().to_vec() } /// Add sub-region to this region. diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 2d1af105f..6754a6e22 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -637,7 +637,7 @@ impl SysBusDevOps for VirtioMmioDevice { Ok(fd) => fd, }; ret.push(RegionIoEventFd { - fd: eventfd_clone, + fd: Arc::new(eventfd_clone), addr_range: AddressRange::from((addr, std::mem::size_of::() as u64)), data_match: true, data: index as u64, diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 8e6e5fd58..01a6b470a 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -693,7 +693,7 @@ impl VirtioPciDevice { for (index, eventfd) in eventfds.events.into_iter().enumerate() { let addr = index as u64 * u64::from(VIRTIO_PCI_CAP_NOTIFY_OFF_MULTIPLIER); ret.push(RegionIoEventFd { - fd: eventfd, + fd: Arc::new(eventfd), addr_range: AddressRange::from((addr, 2u64)), data_match: false, data: index as u64, -- Gitee From bf7d3821ac687d97f8be3072495e3c525127498b Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 17:07:38 +0800 Subject: [PATCH 0698/2187] address_sapce: check whether a memregion has fd changed when update ioeventfds In the last patch we make eventfd unchanged by stratovirt in the memory part, thus we can now tell whether the fd related memory region is updated by the guest or not. If the guest has update_bar_mappings and makes the map between mem address and eventfd different with BIOS(it's true for linux guestes), we can now tell. If that happens, we re-set the listener for that fd. Signed-off-by: Zhang Bo --- address_space/src/address_space.rs | 10 ++++++++-- address_space/src/region.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 9bb3d1e59..97b6ff478 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -286,7 +286,11 @@ impl AddressSpace { while old_idx < old_evtfds.len() || new_idx < new_evtfds.len() { let old_fd = old_evtfds.get(old_idx); let new_fd = new_evtfds.get(new_idx); - if old_fd.is_some() && (new_fd.is_none() || old_fd.unwrap().before(new_fd.unwrap())) { + if old_fd.is_some() + && (new_fd.is_none() + || old_fd.unwrap().before(new_fd.unwrap()) + || new_fd.unwrap().fd_changed(old_fd.unwrap())) + { self.call_listeners(None, old_fd, ListenerReqType::DeleteIoeventfd) .with_context(|| { anyhow!(AddressSpaceError::UpdateTopology( @@ -297,7 +301,9 @@ impl AddressSpace { })?; old_idx += 1; } else if new_fd.is_some() - && (old_fd.is_none() || new_fd.unwrap().before(old_fd.unwrap())) + && (old_fd.is_none() + || new_fd.unwrap().before(old_fd.unwrap()) + || new_fd.unwrap().fd_changed(old_fd.unwrap())) { self.call_listeners(None, new_fd, ListenerReqType::AddIoeventfd) .with_context(|| { diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 86a163808..7c77e2f73 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -14,6 +14,7 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{debug, warn}; use std::fmt; use std::fmt::Debug; +use std::os::unix::io::AsRawFd; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; @@ -133,6 +134,21 @@ impl RegionIoEventFd { } false } + + /// Check if this `RegionIoEventFd` has the same address but different fd number. + /// + /// # Arguments + /// + /// * `other` - Other `RegionIoEventFd`. + pub(crate) fn fd_changed(&self, other: &RegionIoEventFd) -> bool { + if self.addr_range.base == other.addr_range.base + && self.fd.as_raw_fd() != other.fd.as_raw_fd() + { + return true; + } + + false + } } /// FlatRange is a piece of continuous memory address。 -- Gitee From 321bfded5615bb8a57c3453d57e173ce2793bcea Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 19:16:27 +0800 Subject: [PATCH 0699/2187] address_space: adapt test cases after evtfd becomes Arc As the evtfd in RegionIoEventFd has become an Arc item, fix the related test cases according to that change. Signed-off-by: Zhang Bo --- address_space/src/address_space.rs | 4 ++-- address_space/src/listener.rs | 8 ++++---- address_space/src/region.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 97b6ff478..6381aa0cd 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -883,7 +883,7 @@ mod test { #[test] fn test_update_ioeventfd() { let ioeventfds = vec![RegionIoEventFd { - fd: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), addr_range: AddressRange::from((0, std::mem::size_of::() as u64)), data_match: true, data: 64_u64, @@ -945,7 +945,7 @@ mod test { #[test] fn test_subregion_ioeventfd() { let ioeventfds = vec![RegionIoEventFd { - fd: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), addr_range: AddressRange::from((0, 4)), data_match: true, data: 0_64, diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 952a1fb1f..fccade1ed 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -628,7 +628,7 @@ mod test { fn generate_region_ioeventfd>(addr: u64, datamatch: T) -> RegionIoEventFd { let data = datamatch.into(); RegionIoEventFd { - fd: EventFd::new(EFD_NONBLOCK).unwrap(), + fd: Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()), addr_range: AddressRange::from((addr, std::mem::size_of::() as u64)), data_match: data != 0, data, @@ -799,21 +799,21 @@ mod test { .is_ok()); // Delete ioeventfd with wrong address will cause an error. - let mut evtfd_to_del = evtfd.try_clone().unwrap(); + let mut evtfd_to_del = evtfd.clone(); evtfd_to_del.addr_range.base.0 = evtfd_to_del.addr_range.base.0 - 2; assert!(kml .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) .is_err()); // Delete ioeventfd with inconsistent data-match will cause error. - let mut evtfd_to_del = evtfd.try_clone().unwrap(); + let mut evtfd_to_del = evtfd.clone(); evtfd_to_del.data_match = false; assert!(kml .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) .is_err()); // Delete ioeventfd with inconsistent matched data will cause an error. - let mut evtfd_to_del = evtfd.try_clone().unwrap(); + let mut evtfd_to_del = evtfd.clone(); evtfd_to_del.data = 128_u64; assert!(kml .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 7c77e2f73..498f6ca66 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1118,13 +1118,13 @@ mod test { #[test] fn test_region_ioeventfd() { let mut fd1 = RegionIoEventFd { - fd: EventFd::new(EFD_NONBLOCK).unwrap(), + fd: Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()), addr_range: AddressRange::from((1000, 4u64)), data_match: false, data: 0, }; // compare length - let mut fd2 = fd1.try_clone().unwrap(); + let mut fd2 = fd1.clone(); fd2.addr_range.size = 8; assert!(fd1.before(&fd2)); -- Gitee From 84c366c6e95793c31085ce6b6fe1c0cdfefad216 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 12 Jan 2023 19:32:46 +0800 Subject: [PATCH 0700/2187] address_space: add test cases for RegionIoEventFd::fd_changed() add test cases for RegionIoEventFd::fd_changed(). Signed-off-by: Zhang Bo --- address_space/src/region.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 498f6ca66..e32d6e4fe 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1123,6 +1123,14 @@ mod test { data_match: false, data: 0, }; + // comapre fd: unchanged + let mut fd2 = fd1.clone(); + assert!(!fd2.fd_changed(&fd1)); + + // comapre fd: changed + fd2.fd = Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()); + assert!(fd2.fd_changed(&fd1)); + // compare length let mut fd2 = fd1.clone(); fd2.addr_range.size = 8; -- Gitee From fd5903bee3023c143f9f83bce680abd31697bace Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 01:05:52 +0800 Subject: [PATCH 0701/2187] address_space: Optimize update_ioeventfds_pass 1. Make logic more intelligible. 2. Allow two operations for each iteration. Signed-off-by: Keqian Zhu --- address_space/src/address_space.rs | 24 +++++----- address_space/src/region.rs | 71 ++++++++++++++---------------- 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 6381aa0cd..64e68e63d 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -286,11 +286,14 @@ impl AddressSpace { while old_idx < old_evtfds.len() || new_idx < new_evtfds.len() { let old_fd = old_evtfds.get(old_idx); let new_fd = new_evtfds.get(new_idx); - if old_fd.is_some() - && (new_fd.is_none() - || old_fd.unwrap().before(new_fd.unwrap()) - || new_fd.unwrap().fd_changed(old_fd.unwrap())) - { + + if old_fd == new_fd { + old_idx += 1; + new_idx += 1; + continue; + } + // Delete old_fd, but do not delete it if it's after new_fd, as it may match later. + if old_fd.is_some() && (new_fd.is_none() || !old_fd.unwrap().after(new_fd.unwrap())) { self.call_listeners(None, old_fd, ListenerReqType::DeleteIoeventfd) .with_context(|| { anyhow!(AddressSpaceError::UpdateTopology( @@ -300,11 +303,9 @@ impl AddressSpace { )) })?; old_idx += 1; - } else if new_fd.is_some() - && (old_fd.is_none() - || new_fd.unwrap().before(old_fd.unwrap()) - || new_fd.unwrap().fd_changed(old_fd.unwrap())) - { + } + // Add new_fd, but do not add it if it's after old_fd, as it may match later. + if new_fd.is_some() && (old_fd.is_none() || !new_fd.unwrap().after(old_fd.unwrap())) { self.call_listeners(None, new_fd, ListenerReqType::AddIoeventfd) .with_context(|| { anyhow!(AddressSpaceError::UpdateTopology( @@ -314,9 +315,6 @@ impl AddressSpace { )) })?; new_idx += 1; - } else { - old_idx += 1; - new_idx += 1; } } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index e32d6e4fe..9b9172f59 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -113,41 +113,23 @@ impl fmt::Debug for RegionIoEventFd { } } -impl RegionIoEventFd { - /// Calculate if this `RegionIoEventFd` is located before the given one. - /// - /// # Arguments - /// - /// * `other` - Other `RegionIoEventFd`. - pub(crate) fn before(&self, other: &RegionIoEventFd) -> bool { - if self.addr_range.base != other.addr_range.base { - return self.addr_range.base < other.addr_range.base; - } - if self.addr_range.size != other.addr_range.size { - return self.addr_range.size < other.addr_range.size; - } - if self.data_match != other.data_match { - return self.data_match && (!other.data_match); - } - if self.data != other.data { - return self.data < other.data; - } - false +impl PartialEq for RegionIoEventFd { + fn eq(&self, other: &Self) -> bool { + self.addr_range == other.addr_range + && self.data_match == other.data_match + && self.data == other.data + && self.fd.as_raw_fd() == other.fd.as_raw_fd() } +} - /// Check if this `RegionIoEventFd` has the same address but different fd number. +impl RegionIoEventFd { + /// Calculate if this `RegionIoEventFd` is located after the given one. /// /// # Arguments /// /// * `other` - Other `RegionIoEventFd`. - pub(crate) fn fd_changed(&self, other: &RegionIoEventFd) -> bool { - if self.addr_range.base == other.addr_range.base - && self.fd.as_raw_fd() != other.fd.as_raw_fd() - { - return true; - } - - false + pub fn after(&self, other: &RegionIoEventFd) -> bool { + self.addr_range.base.0 >= (other.addr_range.base.0 + other.addr_range.size) } } @@ -1123,33 +1105,44 @@ mod test { data_match: false, data: 0, }; - // comapre fd: unchanged + // comapre unchanged let mut fd2 = fd1.clone(); - assert!(!fd2.fd_changed(&fd1)); + assert!(fd2 == fd1); - // comapre fd: changed + // comapre fd fd2.fd = Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()); - assert!(fd2.fd_changed(&fd1)); + assert!(fd2 != fd1); // compare length - let mut fd2 = fd1.clone(); + fd2.fd = fd1.fd.clone(); + assert!(fd2 == fd1); fd2.addr_range.size = 8; - assert!(fd1.before(&fd2)); + assert!(fd1 != fd2); // compare address - fd2.addr_range.base.0 = 1024; fd2.addr_range.size = 4; - assert!(fd1.before(&fd2)); + assert!(fd2 == fd1); + fd2.addr_range.base.0 = 1024; + assert!(fd1 != fd2); // compare datamatch fd2.addr_range = fd1.addr_range; + assert!(fd2 == fd1); fd2.data_match = true; - assert_eq!(fd1.before(&fd2), false); + assert!(fd1 != fd2); // if datamatch, compare data fd1.data_match = true; + assert!(fd2 == fd1); fd2.data = 10u64; - assert!(fd1.before(&fd2)); + assert!(fd1 != fd2); + + // test after + fd2.data = 0; + assert!(fd2 == fd1); + assert!(!fd2.after(&fd1)); + fd2.addr_range.base.0 = 1004; + assert!(fd2.after(&fd1)); } // test add/del sub-region to container-region, and check priority -- Gitee From 5eee24985eaf34e7c7e0fcd0158829914984b123 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 11:35:51 +0800 Subject: [PATCH 0702/2187] virtio-mmio: Check config and device type is same Must check the config type and device type match or not, otherwise it may cause panic in update_config callback. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/error.rs | 2 ++ machine/src/micro_vm/mod.rs | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/machine/src/micro_vm/error.rs b/machine/src/micro_vm/error.rs index d18eedf62..26491cc7c 100644 --- a/machine/src/micro_vm/error.rs +++ b/machine/src/micro_vm/error.rs @@ -36,6 +36,8 @@ pub enum MicroVmError { }, #[error("A maximum of {0} {1} replaceable devices are supported.")] RplDevLmtErr(String, usize), + #[error("The device type is {0}, but the target config is not for this type.")] + DevTypeErr(String), #[error("{0}: failed to update config.")] UpdCfgErr(String), #[error("Failed to realize virtio mmio.")] diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index a2afecac6..0fdbc7b93 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -378,6 +378,20 @@ impl LightMachine { } fn add_replaceable_device(&self, id: &str, driver: &str, slot: usize) -> Result<()> { + // Find the configuration by id. + let configs_lock = self.replaceable_info.configs.lock().unwrap(); + let mut dev_config = None; + for config in configs_lock.iter() { + if config.id == id { + dev_config = Some(config.dev_config.clone()); + } + } + if dev_config.is_none() { + bail!("Failed to find device configuration."); + } + + // Sanity check for config, driver and slot. + let cfg_any = dev_config.as_ref().unwrap().as_any(); let index = if driver.contains("net") { if slot >= MMIO_REPLACEABLE_NET_NR { return Err(anyhow!(MicroVmError::RplDevLmtErr( @@ -385,6 +399,9 @@ impl LightMachine { MMIO_REPLACEABLE_NET_NR ))); } + if cfg_any.downcast_ref::().is_none() { + return Err(anyhow!(MicroVmError::DevTypeErr("net".to_string()))); + } slot + MMIO_REPLACEABLE_BLK_NR } else if driver.contains("blk") { if slot >= MMIO_REPLACEABLE_BLK_NR { @@ -393,23 +410,14 @@ impl LightMachine { MMIO_REPLACEABLE_BLK_NR ))); } + if cfg_any.downcast_ref::().is_none() { + return Err(anyhow!(MicroVmError::DevTypeErr("blk".to_string()))); + } slot } else { bail!("Unsupported replaceable device type."); }; - // Find the configuration by id. - let configs_lock = self.replaceable_info.configs.lock().unwrap(); - let mut dev_config = None; - for config in configs_lock.iter() { - if config.id == id { - dev_config = Some(config.dev_config.clone()); - } - } - if dev_config.is_none() { - bail!("Failed to find device configuration."); - } - // Find the replaceable device and replace it. let mut replaceable_devices = self.replaceable_info.devices.lock().unwrap(); if let Some(device_info) = replaceable_devices.get_mut(index) { -- Gitee From e2a21efc325660e81cb01a22e6d7803d19743e2c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 11:37:37 +0800 Subject: [PATCH 0703/2187] virtio-blk: Fix some virtio-blk related unwrap() issues Some unwrap() operations are unsafe as they may cause panic during VM running, return err instead. Signed-off-by: Keqian Zhu --- machine_manager/src/socket.rs | 9 +++++++-- util/src/aio/mod.rs | 2 +- util/src/leak_bucket.rs | 9 +++++---- virtio/src/block.rs | 23 ++++++++++++----------- virtio/src/gpu.rs | 8 +++----- virtio/src/rng.rs | 5 ++++- virtio/src/scsi/controller.rs | 2 +- 7 files changed, 33 insertions(+), 25 deletions(-) diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index de9f2aa08..1c8befe50 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -186,12 +186,17 @@ impl Socket { /// Create socket's accepted stream to `event_notifier`. fn create_event_notifier(&mut self, shared_socket: Arc>) -> Vec { let mut notifiers = Vec::new(); - self.accept(); - let leak_bucket = Arc::new(Mutex::new(LeakBucket::new(LEAK_BUCKET_LIMIT))); + let leak_bucket = LeakBucket::new(LEAK_BUCKET_LIMIT); + if let Err(e) = leak_bucket { + error!("Failed to create leak bucket, {:?}", e); + return notifiers; + } + let leak_bucket = Arc::new(Mutex::new(leak_bucket.unwrap())); let shared_leak_bucket = leak_bucket.clone(); let leak_bucket_fd = leak_bucket.lock().unwrap().as_raw_fd(); + self.accept(); let handler: Rc = Rc::new(move |event, _| { if event == EventSet::IN { let socket_mutexed = shared_socket.lock().unwrap(); diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 4766cbb0b..a07a3d82d 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -97,7 +97,7 @@ pub struct Aio { impl Aio { pub fn new(func: Arc>, engine: Option<&String>) -> Result { let max_events: usize = 128; - let fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let fd = EventFd::new(libc::EFD_NONBLOCK)?; let aio = if let Some(engine) = engine { engine } else { diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index b5f26601f..2b3d9578a 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -20,6 +20,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::loop_context::EventLoopContext; use crate::time::NANOSECONDS_PER_SECOND; +use anyhow::Result; /// Used to improve the accuracy of bucket level. const ACCURACY_SCALE: u64 = 1000; @@ -45,14 +46,14 @@ impl LeakBucket { /// # Arguments /// /// * `units_ps` - units per second. - pub fn new(units_ps: u64) -> Self { - LeakBucket { + pub fn new(units_ps: u64) -> Result { + Ok(LeakBucket { capacity: units_ps * ACCURACY_SCALE, level: 0, prev_time: Instant::now(), timer_started: false, - timer_wakeup: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - } + timer_wakeup: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + }) } /// Return true if the bucket is full, and caller must return directly instead of launching IO. diff --git a/virtio/src/block.rs b/virtio/src/block.rs index f5b61ac8f..9cc09b6ca 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -95,7 +95,7 @@ pub struct AioCompleteCb { mem_space: Arc, /// The head of merged Request list. req: Rc, - interrupt_cb: Option>, + interrupt_cb: Arc, driver_features: u64, } @@ -104,7 +104,7 @@ impl AioCompleteCb { queue: Arc>, mem_space: Arc, req: Rc, - interrupt_cb: Option>, + interrupt_cb: Arc, driver_features: u64, ) -> Self { AioCompleteCb { @@ -147,11 +147,9 @@ impl AioCompleteCb { .vring .should_notify(&self.mem_space, self.driver_features) { - if let Err(e) = (*self.interrupt_cb.as_ref().unwrap())( - &VirtioInterruptType::Vring, - Some(&queue_lock), - false, - ) { + if let Err(e) = + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + { bail!( "Failed to trigger interrupt(blk io completion), error is {:?}", e @@ -510,7 +508,7 @@ impl BlockIoHandler { self.queue.clone(), self.mem_space.clone(), Rc::new(req), - Some(self.interrupt_cb.clone()), + self.interrupt_cb.clone(), self.driver_features, ); // unlock queue, because it will be hold below. @@ -537,7 +535,7 @@ impl BlockIoHandler { self.queue.clone(), self.mem_space.clone(), req_rc.clone(), - Some(self.interrupt_cb.clone()), + self.interrupt_cb.clone(), self.driver_features, ); if let Some(disk_img) = self.disk_image.as_ref() { @@ -678,7 +676,7 @@ impl BlockIoHandler { } }; - if let Err(e) = (*self.interrupt_cb)(&VirtioInterruptType::Config, None, false) { + if let Err(e) = (self.interrupt_cb)(&VirtioInterruptType::Config, None, false) { error!( "{:?}. {:?}", VirtioError::InterruptTrigger("block", VirtioInterruptType::Config), @@ -1119,7 +1117,10 @@ impl VirtioDevice for Block { device_broken: self.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), - leak_bucket: self.blk_cfg.iops.map(LeakBucket::new), + leak_bucket: match self.blk_cfg.iops { + Some(iops) => Some(LeakBucket::new(iops)?), + None => None, + }, }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 155009b89..2cd0dae79 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -1542,11 +1542,9 @@ impl GpuIoHandler { .vring .should_notify(&self.mem_space, self.driver_features) { - if let Err(e) = (*self.interrupt_cb.as_ref())( - &VirtioInterruptType::Vring, - Some(&queue_lock), - false, - ) { + if let Err(e) = + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + { error!( "Failed to trigger interrupt(aio completion) for gpu device, error is {:?}", e diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 234baa3c8..83bdcce4d 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -333,7 +333,10 @@ impl VirtioDevice for Rng { .unwrap() .try_clone() .with_context(|| "Failed to clone random file for virtio rng")?, - leak_bucket: self.rng_cfg.bytes_per_sec.map(LeakBucket::new), + leak_bucket: match self.rng_cfg.bytes_per_sec { + Some(bps) => Some(LeakBucket::new(bps)?), + None => None, + }, }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 2d6947763..d2ea40708 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -595,7 +595,7 @@ impl VirtioScsiRequest { .should_notify(mem_space, self.driver_features) { if let Err(e) = - (*self.interrupt_cb.as_ref())(&VirtioInterruptType::Vring, Some(&queue_lock), false) + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) { bail!( "Failed to trigger interrupt(aio completion) for scsi controller, error is {:?}", -- Gitee From e8160bd0bb00623ba2c12a81255f5c82bf1da17d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 14 Jan 2023 14:24:10 +0800 Subject: [PATCH 0704/2187] EventFd: Avoiding the EventFd to try_clone The try_clone of EventFd consumes file handles. Therefore, Arc is used instead of EventFd. In this way, it can clone directly. Signed-off-by: Mingwang Li --- machine/src/lib.rs | 3 +- machine/src/micro_vm/mod.rs | 10 +++-- machine/src/standard_vm/aarch64/mod.rs | 19 +++++---- machine/src/standard_vm/mod.rs | 6 +-- machine/src/standard_vm/x86_64/ich9_lpc.rs | 14 +++---- machine/src/standard_vm/x86_64/mod.rs | 12 +++--- pci/src/msix.rs | 48 +++++++++++----------- util/src/leak_bucket.rs | 7 ++-- vfio/src/vfio_pci.rs | 12 +++--- vhost_user_fs/src/virtio_fs.rs | 24 +++++------ virtio/src/balloon.rs | 18 ++++---- virtio/src/block.rs | 18 ++++---- virtio/src/console.rs | 4 +- virtio/src/gpu.rs | 6 +-- virtio/src/lib.rs | 4 +- virtio/src/net.rs | 24 +++++------ virtio/src/rng.rs | 16 ++++---- virtio/src/scsi/controller.rs | 8 ++-- virtio/src/vhost/kernel/mod.rs | 4 +- virtio/src/vhost/kernel/net.rs | 12 +++--- virtio/src/vhost/kernel/vsock.rs | 12 +++--- virtio/src/vhost/mod.rs | 6 +-- virtio/src/vhost/user/block.rs | 10 ++--- virtio/src/vhost/user/client.rs | 20 ++++----- virtio/src/vhost/user/fs.rs | 12 +++--- virtio/src/vhost/user/net.rs | 10 ++--- virtio/src/virtio_mmio.rs | 47 ++++++--------------- virtio/src/virtio_pci.rs | 8 ++-- virtio/src/virtqueue/mod.rs | 16 ++------ 29 files changed, 193 insertions(+), 217 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 63e73f8de..65d950774 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1301,8 +1301,7 @@ pub trait MachineOps { /// # Arguments /// /// * `power_button` - Eventfd of the power button. - fn register_power_event(&self, power_button: &EventFd) -> Result<()> { - let power_button = power_button.try_clone().unwrap(); + fn register_power_event(&self, power_button: Arc) -> Result<()> { let button_fd = power_button.as_raw_fd(); let power_button_handler: Rc = Rc::new(move |_, _| { let _ret = power_button.read().unwrap(); diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 45e74941d..a2afecac6 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -182,7 +182,7 @@ pub struct LightMachine { // Vm boot_source config. boot_source: Arc>, // VM power button, handle VM `Shutdown` event. - power_button: EventFd, + power_button: Arc, // All configuration information of virtual machine. vm_config: Arc>, // Drive backend files. @@ -216,8 +216,10 @@ impl LightMachine { // Machine state init let vm_state = Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())); - let power_button = EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| anyhow!(MachineError::InitEventFdErr("power_button".to_string())))?; + let power_button = + Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + anyhow!(MachineError::InitEventFdErr("power_button".to_string())) + })?); Ok(LightMachine { cpu_topo: CpuTopology::new( @@ -845,7 +847,7 @@ impl MachineOps for LightMachine { } locked_vm - .register_power_event(&locked_vm.power_button) + .register_power_event(locked_vm.power_button.clone()) .with_context(|| anyhow!(MachineError::InitEventFdErr("power_button".to_string())))?; MigrationManager::register_vm_instance(vm.clone()); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 66d8f4b94..e36a5255f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -129,11 +129,11 @@ pub struct StdMachine { /// Vm boot_source config. boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. - power_button: EventFd, + power_button: Arc, /// All configuration information of virtual machine. vm_config: Arc>, /// Reset request, handle VM `Reset` event. - reset_req: EventFd, + reset_req: Arc, /// Device Tree Blob. dtb_vec: Vec, /// List of guest NUMA nodes information. @@ -186,12 +186,15 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), - power_button: EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + power_button: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { anyhow!(MachineError::InitEventFdErr("power_button".to_string())) - })?, + })?), vm_config: Arc::new(Mutex::new(vm_config.clone())), - reset_req: EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| anyhow!(MachineError::InitEventFdErr("reset_req".to_string())))?, + reset_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + anyhow!(MachineError::InitEventFdErr("reset_req".to_string())) + })?, + ), dtb_vec: Vec::new(), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -475,7 +478,7 @@ impl MachineOps for StdMachine { let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm - .register_reset_event(&locked_vm.reset_req, clone_vm) + .register_reset_event(locked_vm.reset_req.clone(), clone_vm) .with_context(|| "Fail to register reset event")?; locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( @@ -551,7 +554,7 @@ impl MachineOps for StdMachine { .with_context(|| "Failed to create ACPI tables")?; } - locked_vm.register_power_event(&locked_vm.power_button)?; + locked_vm.register_power_event(locked_vm.power_button.clone())?; MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index a86720dfb..adeab0850 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -188,10 +188,9 @@ trait StdMachineOps: AcpiBuilder { /// * `clone_vm` - Reference of the StdMachine. fn register_reset_event( &self, - reset_req: &EventFd, + reset_req: Arc, clone_vm: Arc>, ) -> MachineResult<()> { - let reset_req = reset_req.try_clone().unwrap(); let reset_req_fd = reset_req.as_raw_fd(); let reset_req_handler: Rc = Rc::new(move |_, _| { let _ret = reset_req.read().unwrap(); @@ -216,12 +215,11 @@ trait StdMachineOps: AcpiBuilder { #[cfg(target_arch = "x86_64")] fn register_acpi_shutdown_event( &self, - shutdown_req: &EventFd, + shutdown_req: Arc, clone_vm: Arc>, ) -> MachineResult<()> { use util::loop_context::gen_delete_notifiers; - let shutdown_req = shutdown_req.try_clone().unwrap(); let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { let _ret = shutdown_req.read().unwrap(); diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 17871d4b7..3d5e7df08 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -51,8 +51,8 @@ pub struct LPCBridge { pm_evt: Arc>, pm_ctrl: Arc>, /// Reset request trigged by ACPI PM1 Control Registers. - pub reset_req: EventFd, - pub shutdown_req: EventFd, + pub reset_req: Arc, + pub shutdown_req: Arc, } impl LPCBridge { @@ -65,8 +65,8 @@ impl LPCBridge { pm_evt: Arc::new(Mutex::new(AcpiPmEvent::new())), pm_ctrl: Arc::new(Mutex::new(AcpiPmCtrl::new())), rst_ctrl: Arc::new(AtomicU8::new(0)), - reset_req: EventFd::new(libc::EFD_NONBLOCK).unwrap(), - shutdown_req: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + shutdown_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), } } @@ -107,7 +107,7 @@ impl LPCBridge { }; let cloned_rst_ctrl = self.rst_ctrl.clone(); - let cloned_reset_fd = self.reset_req.try_clone().unwrap(); + let cloned_reset_fd = self.reset_req.clone(); let write_ops = move |data: &[u8], _addr: GuestAddress, _offset: u64| -> bool { let value: u8 = match data.len() { 1 => data[0], @@ -142,7 +142,7 @@ impl LPCBridge { true }; - let cloned_shutdown_fd = self.shutdown_req.try_clone().unwrap(); + let cloned_shutdown_fd = self.shutdown_req.clone(); let write_ops = move |_data: &[u8], _addr: GuestAddress, _offset: u64| -> bool { cloned_shutdown_fd.write(1).unwrap(); true @@ -189,7 +189,7 @@ impl LPCBridge { }; let clone_pmctrl = self.pm_ctrl.clone(); - let cloned_shutdown_fd = self.shutdown_req.try_clone().unwrap(); + let cloned_shutdown_fd = self.shutdown_req.clone(); let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { if clone_pmctrl.lock().unwrap().write(data, addr, offset) { cloned_shutdown_fd.write(1).unwrap(); diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 3fd1cba6e..432c29631 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -113,7 +113,7 @@ pub struct StdMachine { /// Vm boot_source config. boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. - power_button: EventFd, + power_button: Arc, /// All configuration information of virtual machine. vm_config: Arc>, /// List of guest NUMA nodes information. @@ -169,9 +169,9 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, - power_button: EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + power_button: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { anyhow!(MachineError::InitEventFdErr("power_button".to_string())) - })?, + })?), vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -245,9 +245,9 @@ impl StdMachine { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone()); - self.register_reset_event(&ich.reset_req, vm) + self.register_reset_event(ich.reset_req.clone(), vm) .with_context(|| "Fail to register reset event in LPC")?; - self.register_acpi_shutdown_event(&ich.shutdown_req, clone_vm) + self.register_acpi_shutdown_event(ich.shutdown_req.clone(), clone_vm) .with_context(|| "Fail to register shutdown event in LPC")?; ich.realize()?; Ok(()) @@ -462,7 +462,7 @@ impl MachineOps for StdMachine { .with_context(|| "Failed to create ACPI tables")?; } - locked_vm.register_power_event(&locked_vm.power_button)?; + locked_vm.register_power_event(locked_vm.power_button.clone())?; locked_vm .reset_fwcfg_boot_order() diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 0332dfc5b..b62688c06 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -62,7 +62,7 @@ pub struct Message { /// GSI information for routing msix. struct GsiMsiRoute { - irq_fd: Option, + irq_fd: Arc, gsi: i32, msg: Message, } @@ -198,7 +198,7 @@ impl Msix { .vm_fd .as_ref() .unwrap() - .unregister_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) + .unregister_irqfd(route.irq_fd.as_ref(), route.gsi as u32) .map_err(|e| { error!("Failed to unregister irq, error is {:?}", e); e @@ -231,7 +231,7 @@ impl Msix { .vm_fd .as_ref() .unwrap() - .register_irqfd(route.irq_fd.as_ref().unwrap(), route.gsi as u32) + .register_irqfd(route.irq_fd.as_ref(), route.gsi as u32) .map_err(|e| { error!("Failed to register irq, error is {:?}", e); e @@ -240,7 +240,7 @@ impl Msix { Ok(()) } - pub fn register_irqfd(&mut self, vector: u16, call_fd: &EventFd) -> Result<()> { + pub fn register_irqfd(&mut self, vector: u16, call_fd: Arc) -> Result<()> { let entry = self.get_message(vector); let msix_vector = MsiVector { msg_addr_lo: entry.address_lo, @@ -283,14 +283,14 @@ impl Msix { .vm_fd .as_ref() .unwrap() - .register_irqfd(call_fd, gsi) + .register_irqfd(call_fd.as_ref(), gsi) .map_err(|e| { error!("Failed to register irq, error is {:?}", e); e })?; let gsi_route = GsiMsiRoute { - irq_fd: Some(call_fd.try_clone().unwrap()), + irq_fd: call_fd, gsi: gsi as i32, msg: entry, }; @@ -300,26 +300,24 @@ impl Msix { pub fn unregister_irqfd(&mut self) -> Result<()> { for (_, route) in self.gsi_msi_routes.iter() { - if let Some(fd) = &route.irq_fd.as_ref() { - KVM_FDS - .load() - .unregister_irqfd(fd, route.gsi as u32) - .map_err(|e| { - error!("Failed to unregister irq, error is {:?}", e); - e - })?; + KVM_FDS + .load() + .unregister_irqfd(route.irq_fd.as_ref(), route.gsi as u32) + .map_err(|e| { + error!("Failed to unregister irq, error is {:?}", e); + e + })?; - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .release_gsi(route.gsi as u32) - .map_err(|e| { - error!("Failed to release gsi, error is {:?}", e); - e - })?; - } + KVM_FDS + .load() + .irq_route_table + .lock() + .unwrap() + .release_gsi(route.gsi as u32) + .map_err(|e| { + error!("Failed to release gsi, error is {:?}", e); + e + })?; } self.gsi_msi_routes.clear(); Ok(()) diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index c17d04144..b5f26601f 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -12,6 +12,7 @@ /// We use Leaky Bucket Algorithm to limit iops of block device and qmp. use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::Arc; use std::time::Instant; use log::error; @@ -35,7 +36,7 @@ pub struct LeakBucket { timer_started: bool, /// When bucket is ready for allowing more IO operation, the internal callback will write this FD. /// This FD should be listened by IO thread. - timer_wakeup: EventFd, + timer_wakeup: Arc, } impl LeakBucket { @@ -50,7 +51,7 @@ impl LeakBucket { level: 0, prev_time: Instant::now(), timer_started: false, - timer_wakeup: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + timer_wakeup: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), } } @@ -82,7 +83,7 @@ impl LeakBucket { // need to be throttled if self.level > self.capacity { - let wakeup_clone = self.timer_wakeup.try_clone().unwrap(); + let wakeup_clone = self.timer_wakeup.clone(); let func = Box::new(move || { wakeup_clone .write(1) diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index d21829334..7ba03cff7 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -73,7 +73,7 @@ struct VfioBar { } struct GsiMsiRoute { - irq_fd: Option, + irq_fd: Option>, gsi: i32, nr: u32, } @@ -505,7 +505,7 @@ impl VfioPciDevice { let mut gsi_route = locked_gsi_routes.get_mut(vector as usize).unwrap(); if gsi_route.irq_fd.is_none() { let irq_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - gsi_route.irq_fd = Some(irq_fd); + gsi_route.irq_fd = Some(Arc::new(irq_fd)); } if gsi_route.gsi == -1 { gsi_route.gsi = match KVM_FDS @@ -716,7 +716,7 @@ impl VfioPciDevice { if gsi_routes.len() == 0 { let irq_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let gsi_route = GsiMsiRoute { - irq_fd: Some(irq_fd), + irq_fd: Some(Arc::new(irq_fd)), gsi: -1, nr: 0, }; @@ -755,8 +755,10 @@ impl VfioPciDevice { fn vfio_unregister_all_irqfd(&mut self) -> Result<()> { let routes = self.gsi_msi_routes.lock().unwrap(); for route in routes.iter() { - if let Some(fd) = &route.irq_fd.as_ref() { - KVM_FDS.load().unregister_irqfd(fd, route.gsi as u32)?; + if let Some(fd) = route.irq_fd.as_ref() { + KVM_FDS + .load() + .unregister_irqfd(fd.as_ref(), route.gsi as u32)?; // No need to release gsi. if route.gsi == -1 { diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 051dc51ba..b5914b324 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -52,8 +52,8 @@ use virtio::{ struct FsIoHandler { queue: Queue, - kick_evt: EventFd, - call_evt: EventFd, + kick_evt: Arc, + call_evt: Arc, mem_space: Arc, driver_features: u64, fs: Arc>, @@ -62,8 +62,8 @@ struct FsIoHandler { impl FsIoHandler { fn new( queue_config: QueueConfig, - kick_evt: &EventFd, - call_evt: &EventFd, + kick_evt: Arc, + call_evt: Arc, mem_space: &Arc, driver_features: u64, fs: Arc>, @@ -76,8 +76,8 @@ impl FsIoHandler { Ok(FsIoHandler { queue, - kick_evt: kick_evt.try_clone().unwrap(), - call_evt: call_evt.try_clone().unwrap(), + kick_evt, + call_evt, mem_space: mem_space.clone(), driver_features, fs, @@ -145,8 +145,8 @@ impl EventNotifierHelper for FsIoHandler { struct QueueInfo { config: QueueConfig, - kick_evt: Option, - call_evt: Option, + kick_evt: Option>, + call_evt: Option>, } impl QueueInfo { @@ -394,7 +394,7 @@ impl VhostUserReqHandler for VirtioFs { .get_mut_queue_config(index) .map(|queue_info| { let call_evt = unsafe { EventFd::from_raw_fd(fd) }; - queue_info.call_evt = Some(call_evt); + queue_info.call_evt = Some(Arc::new(call_evt)); }) .with_context(|| format!("Failed to set vring call, index: {}", index))?; Ok(()) @@ -409,7 +409,7 @@ impl VhostUserReqHandler for VirtioFs { .get_mut_queue_config(index) .map(|queue_info| { let kick_evt = unsafe { EventFd::from_raw_fd(fd) }; - queue_info.kick_evt = Some(kick_evt); + queue_info.kick_evt = Some(Arc::new(kick_evt)); }) .with_context(|| format!("Failed to set vring kick, index: {}", index))?; Ok(()) @@ -439,8 +439,8 @@ impl VhostUserReqHandler for VirtioFs { let fs_handler = Arc::new(Mutex::new( FsIoHandler::new( queue_info.config, - queue_info.kick_evt.as_ref().unwrap(), - queue_info.call_evt.as_ref().unwrap(), + queue_info.kick_evt.as_ref().unwrap().clone(), + queue_info.call_evt.as_ref().unwrap().clone(), &self.sys_mem, driver_features, self.fs.clone(), diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 25bcf1e32..1657d9e21 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -507,15 +507,15 @@ struct BalloonIoHandler { /// Inflate queue. inf_queue: Arc>, /// Inflate EventFd. - inf_evt: EventFd, + inf_evt: Arc, /// Deflate queue. def_queue: Arc>, /// Deflate EventFd. - def_evt: EventFd, + def_evt: Arc, /// Reporting queue. report_queue: Option>>, /// Reporting EventFd. - report_evt: Option, + report_evt: Option>, /// Device is broken or not. device_broken: Arc, /// The interrupt call back function. @@ -979,7 +979,7 @@ impl VirtioDevice for Balloon { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { if queues.len() != self.queue_num() { return Err(anyhow!(VirtioError::IncorrectQueueNum( @@ -1286,14 +1286,14 @@ mod tests { let queue1 = Arc::new(Mutex::new(Queue::new(queue_config_inf, 1).unwrap())); let queue2 = Arc::new(Mutex::new(Queue::new(queue_config_def, 1).unwrap())); - let event_inf = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let event_def = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let event_inf = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + let event_def = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); let mut handler = BalloonIoHandler { driver_features: bln.driver_features, mem_space: mem_space.clone(), inf_queue: queue1, - inf_evt: event_inf.try_clone().unwrap(), + inf_evt: event_inf.clone(), def_queue: queue2, def_evt: event_def, report_queue: None, @@ -1395,8 +1395,8 @@ mod tests { let mut queues: Vec>> = Vec::new(); let queue1 = Arc::new(Mutex::new(Queue::new(queue_config_inf, 1).unwrap())); queues.push(queue1); - let event_inf = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let queue_evts: Vec = vec![event_inf.try_clone().unwrap()]; + let event_inf = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + let queue_evts: Vec> = vec![event_inf.clone()]; let bln_cfg = BalloonConfig { id: "bln".to_string(), diff --git a/virtio/src/block.rs b/virtio/src/block.rs index c69818562..f5b61ac8f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -387,7 +387,7 @@ struct BlockIoHandler { /// The virtqueue. queue: Arc>, /// Eventfd of the virtqueue for IO event. - queue_evt: EventFd, + queue_evt: Arc, /// The address space to which the block device belongs. mem_space: Arc, /// The image file opened by the block device. @@ -407,7 +407,7 @@ struct BlockIoHandler { /// The receiving half of Rust's channel to receive the image file. receiver: Receiver, /// Eventfd for config space update. - update_evt: EventFd, + update_evt: Arc, /// Device is broken or not. device_broken: Arc, /// Callback to trigger an interrupt. @@ -582,7 +582,7 @@ impl BlockIoHandler { let now = Instant::now(); if (now - start_time).as_millis() > MAX_MILLIS_TIME_PROCESS_QUEUE as u128 { // Make sure we can come back. - self.queue_evt.try_clone()?.write(1)?; + self.queue_evt.write(1)?; break; } @@ -915,7 +915,7 @@ pub struct Block { /// The sending half of Rust's channel to send the image file. senders: Option>>, /// Eventfd for config space update. - update_evts: Vec, + update_evts: Vec>, /// Eventfd for device deactivate. deactivate_evts: Vec, /// Device is broken or not. @@ -1092,7 +1092,7 @@ impl VirtioDevice for Block { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); let mut senders = Vec::new(); @@ -1101,7 +1101,7 @@ impl VirtioDevice for Block { let (sender, receiver) = channel(); senders.push(sender); - let update_evt = EventFd::new(libc::EFD_NONBLOCK)?; + let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); let engine = self.blk_cfg.aio.as_ref(); let handler = BlockIoHandler { queue: queue.clone(), @@ -1115,7 +1115,7 @@ impl VirtioDevice for Block { aio: Box::new(Aio::new(Arc::new(BlockIoHandler::complete_func), engine)?), driver_features: self.state.driver_features, receiver, - update_evt: update_evt.try_clone()?, + update_evt: update_evt.clone(), device_broken: self.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), @@ -1454,7 +1454,7 @@ mod tests { let queues: Vec>> = vec![Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap()))]; - let event = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let event = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); // activate block device block @@ -1462,7 +1462,7 @@ mod tests { mem_space.clone(), interrupt_cb, &queues, - vec![event.try_clone().unwrap()], + vec![event.clone()], ) .unwrap(); diff --git a/virtio/src/console.rs b/virtio/src/console.rs index 4e6366827..cbcf80138 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -71,7 +71,7 @@ impl VirtioConsoleConfig { struct ConsoleHandler { input_queue: Arc>, output_queue: Arc>, - output_queue_evt: EventFd, + output_queue_evt: Arc, mem_space: Arc, interrupt_cb: Arc, driver_features: u64, @@ -353,7 +353,7 @@ impl VirtioDevice for Console { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { queue_evts.remove(0); // input_queue_evt never used diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index ec81984b0..155009b89 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -491,9 +491,9 @@ struct GpuIoHandler { /// The address space to which the GPU device belongs. mem_space: Arc, /// Eventfd for contorl virtqueue. - ctrl_queue_evt: EventFd, + ctrl_queue_evt: Arc, /// Eventfd for cursor virtqueue. - cursor_queue_evt: EventFd, + cursor_queue_evt: Arc, /// Callback to trigger an interrupt. interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. @@ -1815,7 +1815,7 @@ impl VirtioDevice for Gpu { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { if queues.len() != QUEUE_NUM_GPU { return Err(anyhow!(VirtioError::IncorrectQueueNum( diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9f00db59a..2d9ae8278 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -334,7 +334,7 @@ pub trait VirtioDevice: Send { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - queue_evts: Vec, + queue_evts: Vec>, ) -> Result<()>; /// Deactivate virtio device, this function remove event fd @@ -366,7 +366,7 @@ pub trait VirtioDevice: Send { /// # Arguments /// /// * `_queue_evts` - The notifier events from host. - fn set_guest_notifiers(&mut self, _queue_evts: &[EventFd]) -> Result<()> { + fn set_guest_notifiers(&mut self, _queue_evts: &[Arc]) -> Result<()> { Ok(()) } diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 5439690ed..7579898b8 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -428,7 +428,7 @@ pub struct CtrlVirtio { /// The control queue. queue: Arc>, /// The eventfd used to notify the control queue event. - queue_evt: EventFd, + queue_evt: Arc, /// The information about control command. ctrl_info: Arc>, } @@ -436,7 +436,7 @@ pub struct CtrlVirtio { impl CtrlVirtio { pub fn new( queue: Arc>, - queue_evt: EventFd, + queue_evt: Arc, ctrl_info: Arc>, ) -> Self { Self { @@ -627,11 +627,11 @@ impl EventNotifierHelper for NetCtrlHandler { struct TxVirtio { queue: Arc>, - queue_evt: EventFd, + queue_evt: Arc, } impl TxVirtio { - fn new(queue: Arc>, queue_evt: EventFd) -> Self { + fn new(queue: Arc>, queue_evt: Arc) -> Self { TxVirtio { queue, queue_evt } } } @@ -639,11 +639,11 @@ impl TxVirtio { struct RxVirtio { queue_full: bool, queue: Arc>, - queue_evt: EventFd, + queue_evt: Arc, } impl RxVirtio { - fn new(queue: Arc>, queue_evt: EventFd) -> Self { + fn new(queue: Arc>, queue_evt: Arc) -> Self { RxVirtio { queue_full: false, queue, @@ -661,7 +661,7 @@ struct NetIoHandler { interrupt_cb: Arc, driver_features: u64, receiver: Receiver, - update_evt: EventFd, + update_evt: Arc, device_broken: Arc, is_listening: bool, ctrl_info: Arc>, @@ -1117,7 +1117,7 @@ pub struct Net { /// The send half of Rust's channel to send tap information. senders: Option>>, /// Eventfd for config space update. - update_evt: EventFd, + update_evt: Arc, /// Eventfd for device deactivate. deactivate_evts: Vec, /// Device is broken or not. @@ -1133,7 +1133,7 @@ impl Default for Net { taps: None, state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, - update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + update_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, @@ -1148,7 +1148,7 @@ impl Net { taps: None, state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, - update_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + update_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, @@ -1484,7 +1484,7 @@ impl VirtioDevice for Net { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); @@ -1540,7 +1540,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features, receiver, - update_evt: self.update_evt.try_clone().unwrap(), + update_evt: self.update_evt.clone(), device_broken: self.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 65bb44c4c..234baa3c8 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -60,7 +60,7 @@ fn get_req_data_size(in_iov: &[ElemIovec]) -> Result { struct RngHandler { queue: Arc>, - queue_evt: EventFd, + queue_evt: Arc, interrupt_cb: Arc, driver_features: u64, mem_space: Arc, @@ -319,7 +319,7 @@ impl VirtioDevice for Rng { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { let handler = RngHandler { queue: queues[0].clone(), @@ -535,8 +535,8 @@ mod tests { #[test] fn test_rng_process_queue_01() { let mem_space = address_space_init(); - let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let cloned_interrupt_evt = interrupt_evt.try_clone().unwrap(); + let interrupt_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + let cloned_interrupt_evt = interrupt_evt.clone(); let interrupt_status = Arc::new(AtomicU32::new(0)); let interrupt_cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { @@ -567,7 +567,7 @@ mod tests { let file = TempFile::new().unwrap(); let mut rng_handler = RngHandler { queue: Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap())), - queue_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + queue_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), interrupt_cb, driver_features: 0_u64, mem_space: mem_space.clone(), @@ -618,8 +618,8 @@ mod tests { #[test] fn test_rng_process_queue_02() { let mem_space = address_space_init(); - let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let cloned_interrupt_evt = interrupt_evt.try_clone().unwrap(); + let interrupt_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + let cloned_interrupt_evt = interrupt_evt.clone(); let interrupt_status = Arc::new(AtomicU32::new(0)); let interrupt_cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, _needs_reset: bool| { @@ -650,7 +650,7 @@ mod tests { let file = TempFile::new().unwrap(); let mut rng_handler = RngHandler { queue: Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap())), - queue_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + queue_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), interrupt_cb, driver_features: 0_u64, mem_space: mem_space.clone(), diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index d3910c06c..2d6947763 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -267,7 +267,7 @@ impl VirtioDevice for ScsiCntlr { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); if queue_num < SCSI_MIN_QUEUE_NUM { @@ -612,7 +612,7 @@ pub struct ScsiCtrlHandler { /// The ctrl virtqueue. queue: Arc>, /// EventFd for the ctrl virtqueue. - queue_evt: EventFd, + queue_evt: Arc, /// The address space to which the scsi HBA belongs. mem_space: Arc, /// The interrupt callback function. @@ -724,7 +724,7 @@ pub struct ScsiEventHandler { /// The Event virtqueue. _queue: Arc>, /// EventFd for the Event virtqueue. - queue_evt: EventFd, + queue_evt: Arc, /// The address space to which the scsi HBA belongs. _mem_space: Arc, /// The interrupt callback function. @@ -770,7 +770,7 @@ pub struct ScsiCmdHandler { /// The Cmd virtqueue. queue: Arc>, /// EventFd for the Cmd virtqueue. - queue_evt: EventFd, + queue_evt: Arc, /// The address space to which the scsi HBA belongs. mem_space: Arc, /// The interrupt callback function. diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 0d9b5088c..54897db64 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -427,7 +427,7 @@ impl VhostOps for VhostBackend { Ok(vring_state.num as u16) } - fn set_vring_call(&self, queue_idx: usize, fd: &EventFd) -> Result<()> { + fn set_vring_call(&self, queue_idx: usize, fd: Arc) -> Result<()> { let vring_file = VhostVringFile { index: queue_idx as u32, fd: fd.as_raw_fd(), @@ -441,7 +441,7 @@ impl VhostOps for VhostBackend { Ok(()) } - fn set_vring_kick(&self, queue_idx: usize, fd: &EventFd) -> Result<()> { + fn set_vring_kick(&self, queue_idx: usize, fd: Arc) -> Result<()> { let vring_file = VhostVringFile { index: queue_idx as u32, fd: fd.as_raw_fd(), diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 3cee68a39..52aaac6f7 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -252,7 +252,7 @@ impl VirtioDevice for Net { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; @@ -324,7 +324,7 @@ impl VirtioDevice for Net { ) })?; backend - .set_vring_kick(queue_index, &queue_evts[index * 2 + queue_index]) + .set_vring_kick(queue_index, queue_evts[index * 2 + queue_index].clone()) .with_context(|| { format!( "Failed to set vring kick for vhost net, index: {}", @@ -335,12 +335,14 @@ impl VirtioDevice for Net { drop(queue); let host_notify = VhostNotify { - notify_evt: EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| anyhow!(VirtioError::EventFdCreate))?, + notify_evt: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| anyhow!(VirtioError::EventFdCreate))?, + ), queue: queue_mutex.clone(), }; backend - .set_vring_call(queue_index, &host_notify.notify_evt) + .set_vring_call(queue_index, host_notify.notify_evt.clone()) .with_context(|| { format!( "Failed to set vring call for vhost net, index: {}", diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 752ef9ea1..17bc342d5 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -251,7 +251,7 @@ impl VirtioDevice for Vsock { _: Arc, interrupt_cb: Arc, queues: &[Arc>], - queue_evts: Vec, + queue_evts: Vec>, ) -> Result<()> { let cid = self.vsock_cfg.guest_cid; let mut host_notifies = Vec::new(); @@ -294,19 +294,21 @@ impl VirtioDevice for Vsock { format!("Failed to set vring base for vsock, index: {}", queue_index) })?; backend - .set_vring_kick(queue_index, &queue_evts[queue_index]) + .set_vring_kick(queue_index, queue_evts[queue_index].clone()) .with_context(|| { format!("Failed to set vring kick for vsock, index: {}", queue_index) })?; drop(queue); let host_notify = VhostNotify { - notify_evt: EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| anyhow!(VirtioError::EventFdCreate))?, + notify_evt: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| anyhow!(VirtioError::EventFdCreate))?, + ), queue: queue_mutex.clone(), }; backend - .set_vring_call(queue_index, &host_notify.notify_evt) + .set_vring_call(queue_index, host_notify.notify_evt.clone()) .with_context(|| { format!("Failed to set vring call for vsock, index: {}", queue_index) })?; diff --git a/virtio/src/vhost/mod.rs b/virtio/src/vhost/mod.rs index db1a66e9a..03d65f617 100644 --- a/virtio/src/vhost/mod.rs +++ b/virtio/src/vhost/mod.rs @@ -23,7 +23,7 @@ use anyhow::Result; /// Vhost vring call notify structure. pub struct VhostNotify { /// Used to register in vhost kernel, when virtio queue have io request will notify to vhost. - pub notify_evt: EventFd, + pub notify_evt: Arc, /// The related virtio queue. pub queue: Arc>, } @@ -82,14 +82,14 @@ pub trait VhostOps { /// # Arguments /// * `queue_idx` - Index of the queue to modify. /// * `fd` - EventFd to trigger. - fn set_vring_call(&self, queue_idx: usize, fd: &EventFd) -> Result<()>; + fn set_vring_call(&self, queue_idx: usize, fd: Arc) -> Result<()>; /// Set eventfd to poll for added buffers. /// /// # Arguments /// * `queue_idx` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. - fn set_vring_kick(&self, queue_idx: usize, fd: &EventFd) -> Result<()>; + fn set_vring_kick(&self, queue_idx: usize, fd: Arc) -> Result<()>; /// Set the status of ring. /// diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index f5ea3731b..d04c39d72 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -43,7 +43,7 @@ pub struct Block { /// Vhost user client client: Option>>, /// The notifier events from host. - call_events: Vec, + call_events: Vec>, } impl Block { @@ -53,7 +53,7 @@ impl Block { state: BlockState::default(), mem_space: mem_space.clone(), client: None, - call_events: Vec::::new(), + call_events: Vec::>::new(), } } @@ -264,7 +264,7 @@ impl VirtioDevice for Block { _mem_space: Arc, _interrupt_cb: Arc, queues: &[Arc>], - queue_evts: Vec, + queue_evts: Vec>, ) -> Result<()> { let mut client = match &self.client { Some(client) => client.lock().unwrap(), @@ -298,9 +298,9 @@ impl VirtioDevice for Block { } /// Set guest notifiers for notifying the guest. - fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { + fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { for fd in queue_evts.iter() { - let cloned_evt_fd = fd.try_clone().unwrap(); + let cloned_evt_fd = fd.clone(); self.call_events.push(cloned_evt_fd); } diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 5b2a60cd4..35283dd29 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -297,8 +297,8 @@ pub struct VhostUserClient { mem_info: VhostUserMemInfo, delete_evts: Vec, queues: Vec>>, - queue_evts: Vec, - call_events: Vec, + queue_evts: Vec>, + call_events: Vec>, pub features: u64, reconnecting: bool, } @@ -339,16 +339,16 @@ impl VhostUserClient { } /// Save eventfd used for reconnection. - pub fn set_queue_evts(&mut self, queue_evts: &[EventFd]) { + pub fn set_queue_evts(&mut self, queue_evts: &[Arc]) { for evt in queue_evts.iter() { - self.queue_evts.push(evt.try_clone().unwrap()); + self.queue_evts.push(evt.clone()); } } /// Save irqfd used for reconnection. - pub fn set_call_events(&mut self, call_events: &[EventFd]) { + pub fn set_call_events(&mut self, call_events: &[Arc]) { for evt in call_events.iter() { - self.call_events.push(evt.try_clone().unwrap()); + self.call_events.push(evt.clone()); } } @@ -398,14 +398,14 @@ impl VhostUserClient { queue_index, ) })?; - self.set_vring_kick(queue_index, &self.queue_evts[queue_index]) + self.set_vring_kick(queue_index, self.queue_evts[queue_index].clone()) .with_context(|| { format!( "Failed to set vring kick for vhost-user net, index: {}", queue_index, ) })?; - self.set_vring_call(queue_index, &self.call_events[queue_index]) + self.set_vring_call(queue_index, self.call_events[queue_index].clone()) .with_context(|| { format!( "Failed to set vring call for vhost-user net, index: {}", @@ -718,7 +718,7 @@ impl VhostOps for VhostUserClient { Ok(()) } - fn set_vring_call(&self, queue_idx: usize, fd: &EventFd) -> Result<()> { + fn set_vring_call(&self, queue_idx: usize, fd: Arc) -> Result<()> { let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( @@ -742,7 +742,7 @@ impl VhostOps for VhostUserClient { Ok(()) } - fn set_vring_kick(&self, queue_idx: usize, fd: &EventFd) -> Result<()> { + fn set_vring_kick(&self, queue_idx: usize, fd: Arc) -> Result<()> { let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 106a49cb3..89201a14b 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -108,7 +108,7 @@ pub struct Fs { acked_features: u64, mem_space: Arc, /// The notifier events from host. - call_events: Vec, + call_events: Vec>, deactivate_evts: Vec, enable_irqfd: bool, } @@ -129,7 +129,7 @@ impl Fs { avail_features: 0_u64, acked_features: 0_u64, mem_space, - call_events: Vec::::new(), + call_events: Vec::>::new(), deactivate_evts: Vec::new(), enable_irqfd, } @@ -218,7 +218,7 @@ impl VirtioDevice for Fs { _mem_space: Arc, interrup_cb: Arc, queues: &[Arc>], - queue_evts: Vec, + queue_evts: Vec>, ) -> Result<()> { let mut host_notifies = Vec::new(); let mut client = match &self.client { @@ -233,7 +233,7 @@ impl VirtioDevice for Fs { if !self.enable_irqfd { for (queue_index, queue_mutex) in queues.iter().enumerate() { let host_notify = VhostNotify { - notify_evt: self.call_events[queue_index].try_clone().unwrap(), + notify_evt: self.call_events[queue_index].clone(), queue: queue_mutex.clone(), }; host_notifies.push(host_notify); @@ -252,9 +252,9 @@ impl VirtioDevice for Fs { } /// Set guest notifiers for notifying the guest. - fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { + fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { for fd in queue_evts.iter() { - let cloned_evt_fd = fd.try_clone().unwrap(); + let cloned_evt_fd = fd.clone(); self.call_events.push(cloned_evt_fd); } match &self.client { diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index a58ef4aa6..14655c523 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -52,7 +52,7 @@ pub struct Net { /// Vhost user client client: Option>>, /// The notifier events from host. - call_events: Vec, + call_events: Vec>, /// EventFd for deactivate control Queue. deactivate_evts: Vec, /// Device is broken or not. @@ -66,7 +66,7 @@ impl Net { state: Arc::new(Mutex::new(VirtioNetState::default())), mem_space: mem_space.clone(), client: None, - call_events: Vec::::new(), + call_events: Vec::>::new(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } @@ -233,7 +233,7 @@ impl VirtioDevice for Net { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec, + mut queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; @@ -275,9 +275,9 @@ impl VirtioDevice for Net { } /// Set guest notifiers for notifying the guest. - fn set_guest_notifiers(&mut self, queue_evts: &[EventFd]) -> Result<()> { + fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { for fd in queue_evts.iter() { - let cloned_evt_fd = fd.try_clone().unwrap(); + let cloned_evt_fd = fd.clone(); self.call_events.push(cloned_evt_fd); } diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/virtio_mmio.rs index 6754a6e22..316989866 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/virtio_mmio.rs @@ -90,14 +90,14 @@ const MAXIMUM_NR_QUEUES: usize = 8; /// HostNotifyInfo includes the info needed for notifying backend from guest. pub struct HostNotifyInfo { /// Eventfds which notify backend to use the avail ring. - events: Vec, + events: Vec>, } impl HostNotifyInfo { pub fn new(queue_num: usize) -> Self { let mut events = Vec::new(); for _i in 0..queue_num { - events.push(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); } HostNotifyInfo { events } @@ -327,7 +327,7 @@ pub struct VirtioMmioDevice { // The entity of low level device. pub device: Arc>, // EventFd used to send interrupt to VM - interrupt_evt: EventFd, + interrupt_evt: Arc, // Interrupt status. interrupt_status: Arc, // HostNotifyInfo used for guest notifier @@ -351,7 +351,7 @@ impl VirtioMmioDevice { VirtioMmioDevice { device, - interrupt_evt: EventFd::new(libc::EFD_NONBLOCK).unwrap(), + interrupt_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), interrupt_status: Arc::new(AtomicU32::new(0)), host_notify_info: HostNotifyInfo::new(queue_num), state: Arc::new(Mutex::new(VirtioMmioState { @@ -425,21 +425,14 @@ impl VirtioMmioDevice { } drop(locked_state); - let mut queue_evts = Vec::::new(); + let mut queue_evts = Vec::>::new(); for fd in self.host_notify_info.events.iter() { - let evt_fd_clone = match fd.try_clone() { - Ok(fd) => fd, - Err(e) => { - error!("Failed to clone IoEventFd, {:?}", e); - continue; - } - }; - queue_evts.push(evt_fd_clone); + queue_evts.push(fd.clone()); } let mut events = Vec::new(); for _i in 0..self.device.lock().unwrap().queue_num() { - events.push(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); } self.device.lock().unwrap().set_guest_notifiers(&events)?; @@ -460,7 +453,7 @@ impl VirtioMmioDevice { fn assign_interrupt_cb(&mut self) { let interrupt_status = self.interrupt_status.clone(); - let interrupt_evt = self.interrupt_evt.try_clone().unwrap(); + let interrupt_evt = self.interrupt_evt.clone(); let cloned_state = self.state.clone(); let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, needs_reset: bool| { @@ -629,15 +622,8 @@ impl SysBusDevOps for VirtioMmioDevice { let mut ret = Vec::new(); for (index, eventfd) in self.host_notify_info.events.iter().enumerate() { let addr = u64::from(NOTIFY_REG_OFFSET); - let eventfd_clone = match eventfd.try_clone() { - Err(e) => { - error!("Failed to clone ioeventfd, error is {:?}", e); - continue; - } - Ok(fd) => fd, - }; ret.push(RegionIoEventFd { - fd: Arc::new(eventfd_clone), + fd: eventfd.clone(), addr_range: AddressRange::from((addr, std::mem::size_of::() as u64)), data_match: true, data: index as u64, @@ -647,7 +633,7 @@ impl SysBusDevOps for VirtioMmioDevice { } fn interrupt_evt(&self) -> Option<&EventFd> { - Some(&self.interrupt_evt) + Some(self.interrupt_evt.as_ref()) } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { @@ -724,16 +710,9 @@ impl StateTransfer for VirtioMmioDevice { impl MigrationHook for VirtioMmioDevice { fn resume(&mut self) -> migration::Result<()> { if self.state.lock().unwrap().activated { - let mut queue_evts = Vec::::new(); + let mut queue_evts = Vec::>::new(); for fd in self.host_notify_info.events.iter() { - let evt_fd_clone = match fd.try_clone() { - Ok(fd) => fd, - Err(e) => { - error!("Failed to clone IoEventFd, {:?}", e); - continue; - } - }; - queue_evts.push(evt_fd_clone); + queue_evts.push(fd.clone()); } if let Some(cb) = self.interrupt_cb.clone() { @@ -890,7 +869,7 @@ mod tests { _mem_space: Arc, _interrupt_cb: Arc, _queues: &[Arc>], - mut _queue_evts: Vec, + mut _queue_evts: Vec>, ) -> Result<()> { self.b_active = true; Ok(()) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 7db6c51da..444222399 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -693,7 +693,7 @@ impl VirtioPciDevice { for (index, eventfd) in eventfds.events.into_iter().enumerate() { let addr = index as u64 * u64::from(VIRTIO_PCI_CAP_NOTIFY_OFF_MULTIPLIER); ret.push(RegionIoEventFd { - fd: Arc::new(eventfd), + fd: eventfd.clone(), addr_range: AddressRange::from((addr, 2u64)), data_match: false, data: index as u64, @@ -1012,7 +1012,7 @@ impl VirtioPciDevice { min(queues_max as u16 - queues_fixed, nr_cpus as u16) } - fn queues_register_irqfd(&self, call_fds: &[EventFd]) -> bool { + fn queues_register_irqfd(&self, call_fds: &[Arc]) -> bool { let mut locked_msix = if let Some(msix) = &self.config.msix { msix.lock().unwrap() } else { @@ -1035,7 +1035,7 @@ impl VirtioPciDevice { } if locked_msix - .register_irqfd(vector, &call_fds[queue_index]) + .register_irqfd(vector, call_fds[queue_index].clone()) .is_err() { return false; @@ -1508,7 +1508,7 @@ mod tests { _mem_space: Arc, _interrupt_cb: Arc, _queues: &[Arc>], - _queue_evts: Vec, + _queue_evts: Vec>, ) -> VirtioResult<()> { self.is_activated = true; Ok(()) diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs index ca1b12587..80393824e 100644 --- a/virtio/src/virtqueue/mod.rs +++ b/virtio/src/virtqueue/mod.rs @@ -207,28 +207,18 @@ impl Queue { } /// Virt Queue Notify EventFds +#[derive(Clone)] pub struct NotifyEventFds { - pub events: Vec, + pub events: Vec>, } impl NotifyEventFds { pub fn new(queue_num: usize) -> Self { let mut events = Vec::new(); for _i in 0..queue_num { - events.push(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); } NotifyEventFds { events } } } - -impl Clone for NotifyEventFds { - fn clone(&self) -> NotifyEventFds { - let mut queue_evts = Vec::::new(); - for fd in self.events.iter() { - let cloned_evt_fd = fd.try_clone().unwrap(); - queue_evts.push(cloned_evt_fd); - } - NotifyEventFds { events: queue_evts } - } -} -- Gitee From 18541b76cc3d2a0bb813daf802f901b44c2e1b58 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 16 Jan 2023 01:28:29 +0000 Subject: [PATCH 0705/2187] Fix Virtio-fs title from 2.18 to 2.19 Signed-off-by: wubinfeng --- docs/config_guidebook.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 50de2b3e2..ce023bbac 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -857,10 +857,10 @@ Sample Configuration: Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. -### 2.18 Virtio-fs +### 2.19 Virtio-fs Virtio-fs is a shared file system that lets virtual machines access a directory tree on the host. Unlike existing approaches, it is designed to offer local file system semantics and performance. -### 2.18.1 virtio fs device +#### 2.19.1 virtio fs device Three properties can be set for virtio fs device. * chardevid: id for char device * device_id: the unique id for device @@ -871,7 +871,7 @@ Three properties can be set for virtio fs device. -device vhost-user-fs-pci,id=,chardev=,tag= ``` -### 2.18.2 vhost_user_fs +#### 2.19.2 vhost_user_fs The vhost-user filesystem device contains virtio fs device and the vhost-user server which can be connected with the vhost-user client in StratoVirt through socket. Seven properties are supported for vhost_user_fs. -- Gitee From 066c6498f7b92cbb93641e6b8b9c5bf6290887dd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 14 Jan 2023 15:03:41 +0800 Subject: [PATCH 0706/2187] eventloop: Implement kick mechanism When update events or timers, use eventfd to force epoll.wait to exit, so they can be re-evaluated. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 66 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 3bf520f60..34c3cbd23 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -11,14 +11,16 @@ // See the Mulan PSL v2 for more details. use std::collections::BTreeMap; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; -use libc::{c_void, read}; +use libc::{c_void, read, EFD_NONBLOCK}; use log::warn; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; +use vmm_sys_util::eventfd::EventFd; use crate::UtilError; use anyhow::{anyhow, Context, Result}; @@ -181,6 +183,13 @@ pub struct EventLoopContext { epoll: Epoll, /// Control epoll loop running. manager: Option>>, + /// Used to wakeup epoll to re-evaluate events or timers. + kick_event: EventFd, + /// Used to avoid unnecessary kick operation when the + /// next re-evaluation is performed before next epoll. + kick_me: AtomicBool, + /// Used to identify that a kick operation ocurred. + kicked: AtomicBool, /// Fds registered to the `EventLoop`. events: Arc>>>, /// Events abandoned are stored in garbage collector. @@ -198,13 +207,46 @@ unsafe impl Send for EventLoopContext {} impl EventLoopContext { /// Constructs a new `EventLoopContext`. pub fn new() -> Self { - EventLoopContext { + let mut ctx = EventLoopContext { epoll: Epoll::new().unwrap(), manager: None, + kick_event: EventFd::new(EFD_NONBLOCK).unwrap(), + kick_me: AtomicBool::new(false), + kicked: AtomicBool::new(false), events: Arc::new(RwLock::new(BTreeMap::new())), gc: Arc::new(RwLock::new(Vec::new())), ready_events: vec![EpollEvent::default(); READY_EVENT_MAX], timers: Arc::new(Mutex::new(Vec::new())), + }; + ctx.init_kick(); + ctx + } + + fn init_kick(&mut self) { + let kick_handler: Rc = Rc::new(|_, fd| { + read_fd(fd); + None + }); + self.add_event(EventNotifier::new( + NotifierOperation::AddExclusion, + self.kick_event.as_raw_fd(), + None, + EventSet::IN, + vec![kick_handler], + )) + .unwrap(); + } + + // Force epoll.wait to exit to re-evaluate events and timers. + fn kick(&mut self) { + self.kicked.store(true, Ordering::SeqCst); + if self.kick_me.load(Ordering::SeqCst) { + if let Err(e) = self.kick_event.write(1) { + // Rarely fails when event is full, even if this + // occurs, no need to add event again, so log is + // enough for error handling. + warn!("Failed to kick eventloop, {:?}", e); + } } } @@ -398,6 +440,7 @@ impl EventLoopContext { } } } + self.kick(); Ok(()) } @@ -460,10 +503,14 @@ impl EventLoopContext { } } timers.insert(index, timer); + drop(timers); + self.kick(); } /// Get the expire_time of the soonest Timer, and then translate it to timeout. fn timers_min_timeout(&self) -> i32 { + // The kick event happens before re-evaluate can be ignored. + self.kicked.store(false, Ordering::SeqCst); let timers = self.timers.lock().unwrap(); if timers.is_empty() { return -1; @@ -503,12 +550,22 @@ impl EventLoopContext { } } - fn epoll_wait_manager(&mut self, time_out: i32) -> Result { + fn epoll_wait_manager(&mut self, mut time_out: i32) -> Result { + let need_kick = time_out != 0; + if need_kick { + self.kick_me.store(true, Ordering::SeqCst); + if self.kicked.load(Ordering::SeqCst) { + time_out = 0; + } + } let ev_count = match self.epoll.wait(time_out, &mut self.ready_events[..]) { Ok(ev_count) => ev_count, Err(e) if e.raw_os_error() == Some(libc::EINTR) => 0, Err(e) => return Err(anyhow!(UtilError::EpollWait(e))), }; + if need_kick { + self.kick_me.store(false, Ordering::SeqCst); + } for i in 0..ev_count { // SAFETY: elements in self.events_map never get released in other functions @@ -569,7 +626,6 @@ pub fn read_fd(fd: RawFd) -> u64 { #[cfg(test)] mod test { use super::*; - use libc::*; use std::os::unix::io::{AsRawFd, RawFd}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -- Gitee From 9dce1ab4d28a66b096c7863f28d196f01d589768 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 15 Jan 2023 16:55:32 +0800 Subject: [PATCH 0707/2187] aio/io_uring: Avoid sync mod in aio submit Submit io requestis with sync mod will heavily affect performance, use async mod instead. Signed-off-by: Keqian Zhu --- util/src/aio/uring.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 4350bd56e..35bbbb14a 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -51,7 +51,7 @@ impl IoUringContext { impl AioContext for IoUringContext { #[allow(clippy::zero_ptr)] /// Submit requests to OS. - fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result { + fn submit(&mut self, _nr: i64, iocbp: &mut [*mut IoCb]) -> Result { for iocb in iocbp.iter() { // SAFETY: iocb is valid until request is finished. let offset = unsafe { (*(*iocb)).aio_offset as libc::off_t }; @@ -90,9 +90,7 @@ impl AioContext for IoUringContext { .with_context(|| "Failed to push entry")?; } } - self.ring - .submit_and_wait(nr as usize) - .with_context(|| "Failed to submit sqe") + self.ring.submit().with_context(|| "Failed to submit sqe") } /// Get the events. -- Gitee From 8a23e1040d1123a23cf9df40a301d60e2bf4f0d2 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 15 Jan 2023 21:35:39 +0800 Subject: [PATCH 0708/2187] iothread: Recommand to config iothread in doc iothread is strongly recommanded if a specific device supports it, otherwise the main thread has the risk of getting stuck. Signed-off-by: Keqian Zhu --- docs/config_guidebook.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a6c510338..bd1dd4dd0 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -257,8 +257,9 @@ root bus named pcie.0. As a result, a total of 32 pci devices can be configured. ### 2.1 iothread -Iothread is used by devices to improve io performance. StratoVirt will spawn some extra threads due to `iothread` configuration, -and these threads can be used by devices exclusively improving performance. +Iothread is used by devices to improve io performance. StratoVirt will spawn some extra threads due to `iothread` configuration, and these threads can be used by devices exclusively improving performance. + +Note: iothread is strongly recommanded if a specific device supports it, otherwise the main thread has the risk of getting stuck. There is only one argument for iothread: -- Gitee From f47bcf1948ca7e32b8e1bd1babac10552950d28c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 8 Jan 2023 22:34:29 +0800 Subject: [PATCH 0709/2187] aio: Refactor base aio layer There are many linux aio related data struct in base aio layer, move them into linux aio file and introduce common data struct. Signed-off-by: Keqian Zhu --- util/src/aio/libaio.rs | 120 ++++++++++++++++++++++++++--------------- util/src/aio/mod.rs | 115 +++++++++++++++++---------------------- util/src/aio/uring.rs | 72 +++++++++---------------- virtio/src/block.rs | 15 +++--- virtio/src/scsi/bus.rs | 13 +++-- 5 files changed, 164 insertions(+), 171 deletions(-) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 703b150cc..258905793 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -10,43 +10,50 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{fence, Ordering}; -use super::{AioContext, IoEvent, Result}; +use super::{AioCb, AioContext, AioEvent, OpCode, Result}; use anyhow::bail; use kvm_bindings::__IncompleteArrayField; +use vmm_sys_util::eventfd::EventFd; -pub const IOCB_FLAG_RESFD: u32 = 1; -pub const IOCB_FLAG_IOPRIO: u32 = 1 << 1; +const IOCB_FLAG_RESFD: u32 = 1; +#[allow(dead_code)] +const IOCB_FLAG_IOPRIO: u32 = 1 << 1; -#[derive(Debug, Clone)] -pub struct Iovec { - pub iov_base: u64, - pub iov_len: u64, +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Default, Clone)] +struct IoEvent { + data: u64, + obj: u64, + res: i64, + res2: i64, } #[repr(C)] #[allow(non_camel_case_types)] #[derive(Default)] -pub struct IoCb { - pub data: u64, - pub key: u32, - pub aio_reserved1: u32, - pub aio_lio_opcode: u16, - pub aio_reqprio: u16, - pub aio_fildes: u32, - pub aio_buf: u64, - pub aio_nbytes: u64, - pub aio_offset: u64, - pub aio_reserved2: u64, - pub aio_flags: u32, - pub aio_resfd: u32, +struct IoCb { + data: u64, + key: u32, + aio_reserved1: u32, + aio_lio_opcode: u16, + aio_reqprio: u16, + aio_fildes: u32, + aio_buf: u64, + aio_nbytes: u64, + aio_offset: u64, + aio_reserved2: u64, + aio_flags: u32, + aio_resfd: u32, } #[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum IoCmd { +#[allow(non_camel_case_types, dead_code)] +#[derive(Copy, Clone)] +enum IoCmd { Pread = 0, Pwrite = 1, Fsync = 2, @@ -57,17 +64,12 @@ pub enum IoCmd { } #[allow(non_camel_case_types)] -pub enum IoContext {} - -pub struct EventResult { - pub events: Vec, - pub nr: usize, -} +enum IoContext {} -pub struct LibaioContext { - pub ctx: *mut IoContext, - pub events: Vec, - pub max_size: i32, +pub(crate) struct LibaioContext { + ctx: *mut IoContext, + resfd: RawFd, + events: Vec, } impl Drop for LibaioContext { @@ -81,7 +83,7 @@ impl Drop for LibaioContext { #[repr(C)] #[derive(Default)] -pub struct AioRing { +struct AioRing { id: u32, nr: u32, head: u32, @@ -96,7 +98,7 @@ pub struct AioRing { } impl LibaioContext { - pub fn new(max_size: i32) -> Result { + pub fn new(max_size: u32, eventfd: &EventFd) -> Result { let mut ctx = std::ptr::null_mut(); // SAFETY: ctx is a valid ptr. @@ -107,18 +109,46 @@ impl LibaioContext { Ok(LibaioContext { ctx, + resfd: eventfd.as_raw_fd(), events: Vec::with_capacity(max_size as usize), - max_size, }) } } /// Implements the AioContext for libaio. -impl AioContext for LibaioContext { - /// Submit requests. - fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result { +impl AioContext for LibaioContext { + fn submit(&mut self, iocbp: &[*const AioCb]) -> Result { + let mut iocbs = Vec::with_capacity(iocbp.len()); + for iocb in iocbp { + // SAFETY: iocb is valid until request is finished. + let cb = unsafe { &*(*iocb) }; + let opcode = match cb.opcode { + OpCode::Preadv => IoCmd::Preadv, + OpCode::Pwritev => IoCmd::Pwritev, + _ => bail!("Failed to submit aio, opcode is not supported."), + }; + iocbs.push(IoCb { + data: cb.user_data, + aio_lio_opcode: opcode as u16, + aio_fildes: cb.file_fd as u32, + aio_buf: cb.iovec.as_ptr() as u64, + aio_nbytes: cb.iovec.len() as u64, + aio_offset: cb.offset as u64, + aio_flags: IOCB_FLAG_RESFD, + aio_resfd: self.resfd as u32, + ..Default::default() + }) + } + + // SYS_io_submit needs vec of references. + let mut iocbp = Vec::with_capacity(iocbs.len()); + for iocb in iocbs.iter() { + iocbp.push(iocb); + } + // SAFETY: self.ctx is generated by SYS_io_setup. - let ret = unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, nr, iocbp.as_ptr()) }; + let ret = + unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, iocbp.len(), iocbp.as_ptr()) }; if ret >= 0 { return Ok(ret as usize); } @@ -128,8 +158,7 @@ impl AioContext for LibaioContext { Ok(0) } - /// Get the IO events. - fn get_events(&mut self) -> &[IoEvent] { + fn get_events(&mut self) -> &[AioEvent] { let ring = self.ctx as *mut AioRing; // SAFETY: self.ctx is generated by SYS_io_setup. let head = unsafe { (*ring).head }; @@ -147,7 +176,12 @@ impl AioContext for LibaioContext { fence(Ordering::Acquire); self.events.clear(); for i in head..(head + nr) { - self.events.push(io_events[(i % ring_nr) as usize].clone()); + let io_event = &io_events[(i % ring_nr) as usize]; + self.events.push(AioEvent { + user_data: io_event.data, + status: io_event.res2, + res: io_event.res, + }) } // Avoid head is updated before we consume all io_events. diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index a07a3d82d..23ac81fdb 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -17,7 +17,7 @@ mod uring; use std::clone::Clone; use std::cmp; use std::io::Write; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::RawFd; use std::sync::Arc; use log::error; @@ -25,68 +25,61 @@ use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; use anyhow::{anyhow, bail, Context, Result}; -pub use libaio::*; +use libaio::LibaioContext; pub use raw::*; use uring::IoUringContext; type CbList = List>; type CbNode = Node>; -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Default, Clone)] -pub struct IoEvent { - pub data: u64, - pub obj: u64, - pub res: i64, - pub res2: i64, -} - /// Io-uring aio type. pub const AIO_IOURING: &str = "io_uring"; /// Native aio type. pub const AIO_NATIVE: &str = "native"; +#[derive(Debug, Clone)] +pub struct Iovec { + pub iov_base: u64, + pub iov_len: u64, +} + /// The trait for Asynchronous IO operation. -trait AioContext { +trait AioContext { /// Submit IO requests to the OS, the nr submitted is returned. - fn submit(&mut self, nr: i64, iocbp: &mut [*mut IoCb]) -> Result; + fn submit(&mut self, iocbp: &[*const AioCb]) -> Result; /// Get the IO events of the requests sumbitted earlier. - fn get_events(&mut self) -> &[IoEvent]; + fn get_events(&mut self) -> &[AioEvent]; } -pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; +pub struct AioEvent { + pub user_data: u64, + pub status: i64, + pub res: i64, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum OpCode { + Noop = 0, + Preadv = 1, + Pwritev = 2, + Fdsync = 3, +} pub struct AioCb { pub last_aio: bool, pub file_fd: RawFd, - pub opcode: IoCmd, + pub opcode: OpCode, pub iovec: Vec, pub offset: usize, pub nbytes: u64, - pub process: bool, - pub iocb: Option>, + pub user_data: u64, pub iocompletecb: T, } -impl AioCb { - pub fn new(cb: T) -> Self { - AioCb { - last_aio: true, - file_fd: 0, - opcode: IoCmd::Noop, - iovec: Vec::new(), - offset: 0, - nbytes: 0, - process: false, - iocb: None, - iocompletecb: cb, - } - } -} +pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; pub struct Aio { - ctx: Box, + ctx: Box>, pub fd: EventFd, pub aio_in_queue: CbList, pub aio_in_flight: CbList, @@ -104,10 +97,10 @@ impl Aio { AIO_NATIVE }; - let ctx: Box = if aio == AIO_IOURING { - Box::new(IoUringContext::new(max_events as u32, &fd, func.clone())?) + let ctx: Box> = if aio == AIO_IOURING { + Box::new(IoUringContext::new(max_events as u32, &fd)?) } else { - Box::new(LibaioContext::new(max_events as i32)?) + Box::new(LibaioContext::new(max_events as u32, &fd)?) }; Ok(Aio { @@ -126,12 +119,15 @@ impl Aio { for evt in self.ctx.get_events() { // SAFETY: evt.data is specified by submit and not dropped at other place. unsafe { - let node = evt.data as *mut CbNode; - let res = if (evt.res2 == 0) && (evt.res == (*node).value.nbytes as i64) { + let node = evt.user_data as *mut CbNode; + let res = if (evt.status == 0) && (evt.res == (*node).value.nbytes as i64) { done = true; evt.res } else { - error!("Async IO request failed, res2 {} res {}", evt.res2, evt.res); + error!( + "Async IO request failed, status {} res {}", + evt.status, evt.res + ); -1 }; @@ -151,9 +147,8 @@ impl Aio { for _ in self.aio_in_flight.len..self.max_events { match self.aio_in_queue.pop_tail() { - Some(mut node) => { - let iocb = node.value.iocb.as_mut().unwrap(); - iocbs.push(&mut **iocb as *mut IoCb); + Some(node) => { + iocbs.push(&node.value as *const AioCb); self.aio_in_flight.add_head(node); } None => break, @@ -161,13 +156,10 @@ impl Aio { } // The iocbs must not be empty. - let (nr, is_err) = match self - .ctx - .submit(iocbs.len() as i64, &mut iocbs) - .map_err(|e| { - error!("{}", e); - e - }) { + let (nr, is_err) = match self.ctx.submit(&iocbs).map_err(|e| { + error!("{}", e); + e + }) { Ok(nr) => (nr, false), Err(_) => (0, true), }; @@ -212,20 +204,9 @@ impl Aio { } } - let mut iocb = IoCb { - aio_lio_opcode: cb.opcode as u16, - aio_fildes: cb.file_fd as u32, - aio_buf: (*cb.iovec).as_ptr() as u64, - aio_nbytes: cb.iovec.len() as u64, - aio_offset: cb.offset as u64, - aio_flags: IOCB_FLAG_RESFD, - aio_resfd: self.fd.as_raw_fd() as u32, - ..Default::default() - }; let last_aio = cb.last_aio; let mut node = Box::new(Node::new(cb)); - iocb.data = (&mut (*node) as *mut CbNode) as u64; - node.value.iocb = Some(Box::new(iocb)); + node.value.user_data = (&mut (*node) as *mut CbNode) as u64; self.aio_in_queue.add_head(node); if last_aio || self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { @@ -237,7 +218,7 @@ impl Aio { pub fn rw_sync(&mut self, cb: AioCb) -> Result<()> { let ret = match cb.opcode { - IoCmd::Preadv => { + OpCode::Preadv => { let mut r = 0; let mut off = cb.offset; for iov in cb.iovec.iter() { @@ -253,7 +234,7 @@ impl Aio { } r } - IoCmd::Pwritev => { + OpCode::Pwritev => { let mut r = 0; let mut off = cb.offset; for iov in cb.iovec.iter() { @@ -278,7 +259,7 @@ impl Aio { // SAFETY: only get the host page size. let host_page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u64; match cb.opcode { - IoCmd::Preadv => { + OpCode::Preadv => { // SAFETY: we allocate aligned memory and free it later. // Alignment is set to host page size to decrease the count of allocated pages. let aligned_buffer = @@ -313,7 +294,7 @@ impl Aio { unsafe { libc::free(aligned_buffer) }; res } - IoCmd::Pwritev => { + OpCode::Pwritev => { // SAFETY: we allocate aligned memory and free it later. let aligned_buffer = unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 35bbbb14a..b00b9b918 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -12,27 +12,21 @@ use libc; use std::os::unix::io::AsRawFd; -use std::sync::Arc; use anyhow::{bail, Context}; use io_uring::{opcode, squeue, types, IoUring}; use vmm_sys_util::eventfd::EventFd; -use super::libaio::{IoCb, IoCmd}; -use super::{AioCb, AioCompleteFunc, AioContext, CbNode, IoEvent, Result}; +use super::{AioCb, AioContext, AioEvent, OpCode, Result}; /// The io-uring context. -pub(crate) struct IoUringContext { +pub(crate) struct IoUringContext { ring: IoUring, - events: Vec, - - #[allow(dead_code)] - // Only used to refering type T. - func: Arc>, + events: Vec, } -impl IoUringContext { - pub fn new(entries: u32, eventfd: &EventFd, func: Arc>) -> Result { +impl IoUringContext { + pub fn new(entries: u32, eventfd: &EventFd) -> Result { let tmp_entries = entries as i32; // Ensure the power of 2. if (tmp_entries & -tmp_entries) != tmp_entries || tmp_entries == 0 { @@ -44,37 +38,32 @@ impl IoUringContext { .register_eventfd(eventfd.as_raw_fd()) .with_context(|| "Failed to register event fd")?; let events = Vec::with_capacity(entries as usize); - Ok(IoUringContext { ring, func, events }) + Ok(IoUringContext { ring, events }) } } -impl AioContext for IoUringContext { - #[allow(clippy::zero_ptr)] - /// Submit requests to OS. - fn submit(&mut self, _nr: i64, iocbp: &mut [*mut IoCb]) -> Result { +impl AioContext for IoUringContext { + fn submit(&mut self, iocbp: &[*const AioCb]) -> Result { for iocb in iocbp.iter() { // SAFETY: iocb is valid until request is finished. - let offset = unsafe { (*(*iocb)).aio_offset as libc::off_t }; - let node = unsafe { (*(*iocb)).data as *mut CbNode }; - let aiocb = unsafe { &mut (*node).value as *mut AioCb }; - let raw_fd = unsafe { (*(*iocb)).aio_fildes as i32 }; - let data = unsafe { (*(*iocb)).data }; - let code = unsafe { (*aiocb).opcode }; - let len = unsafe { (*(*iocb)).aio_nbytes }; - let iovs = unsafe { (*(*iocb)).aio_buf }; - let fd = types::Fd(raw_fd); - let entry = match code { - IoCmd::Preadv => opcode::Readv::new(fd, iovs as *const libc::iovec, len as u32) + let cb = unsafe { &*(*iocb) }; + let offset = cb.offset as libc::off_t; + let data = cb.user_data; + let len = cb.iovec.len(); + let iovs = cb.iovec.as_ptr(); + let fd = types::Fd(cb.file_fd); + let entry = match cb.opcode { + OpCode::Preadv => opcode::Readv::new(fd, iovs as *const libc::iovec, len as u32) .offset(offset) .build() .flags(squeue::Flags::ASYNC) .user_data(data), - IoCmd::Pwritev => opcode::Writev::new(fd, iovs as *const libc::iovec, len as u32) + OpCode::Pwritev => opcode::Writev::new(fd, iovs as *const libc::iovec, len as u32) .offset(offset) .build() .flags(squeue::Flags::ASYNC) .user_data(data), - IoCmd::Fdsync => opcode::Fsync::new(fd) + OpCode::Fdsync => opcode::Fsync::new(fd) .build() .flags(squeue::Flags::ASYNC) .user_data(data), @@ -93,24 +82,15 @@ impl AioContext for IoUringContext { self.ring.submit().with_context(|| "Failed to submit sqe") } - /// Get the events. - fn get_events(&mut self) -> &[IoEvent] { - let mut queue = self.ring.completion(); + fn get_events(&mut self) -> &[AioEvent] { + let queue = self.ring.completion(); self.events.clear(); - let l = queue.len(); - for _i in 0..l { - match queue.next() { - None => break, - Some(cqe) => { - let event = IoEvent { - data: cqe.user_data(), - obj: 0, - res: cqe.result() as i64, - res2: 0, - }; - self.events.push(event); - } - } + for cqe in queue { + self.events.push(AioEvent { + user_data: cqe.user_data(), + status: 0, + res: cqe.result() as i64, + }); } &self.events } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 9cc09b6ca..59b2b09fe 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -42,7 +42,7 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::aio::{iov_from_buf_direct, raw_datasync, Aio, AioCb, IoCmd, Iovec, AIO_NATIVE}; +use util::aio::{iov_from_buf_direct, raw_datasync, Aio, AioCb, Iovec, OpCode, AIO_NATIVE}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ @@ -304,7 +304,7 @@ impl Request { let direct = iohandler.direct; match request_type { VIRTIO_BLK_T_IN => { - aiocb.opcode = IoCmd::Preadv; + aiocb.opcode = OpCode::Preadv; if aio_type.is_some() { aio.rw_aio(aiocb, SECTOR_SIZE, direct).with_context(|| { "Failed to process block request for reading asynchronously" @@ -316,7 +316,7 @@ impl Request { } } VIRTIO_BLK_T_OUT => { - aiocb.opcode = IoCmd::Pwritev; + aiocb.opcode = OpCode::Pwritev; if aio_type.is_some() { aio.rw_aio(aiocb, SECTOR_SIZE, direct).with_context(|| { "Failed to process block request for writing asynchronously" @@ -328,7 +328,7 @@ impl Request { } } VIRTIO_BLK_T_FLUSH => { - aiocb.opcode = IoCmd::Fdsync; + aiocb.opcode = OpCode::Fdsync; aio.flush_sync(aiocb) .with_context(|| "Failed to process block request for flushing")?; } @@ -542,12 +542,11 @@ impl BlockIoHandler { let aiocb = AioCb { last_aio: req_index == last_aio_req_index, file_fd: disk_img.as_raw_fd(), - opcode: IoCmd::Noop, + opcode: OpCode::Noop, iovec: Vec::new(), offset: (req_rc.out_header.sector << SECTOR_SHIFT) as usize, nbytes: 0, - process: true, - iocb: None, + user_data: 0, iocompletecb: aiocompletecb, }; req_rc.execute(self, aiocb)?; @@ -634,7 +633,7 @@ impl BlockIoHandler { // When driver does not accept FLUSH feature, the device must be of // writethrough cache type, so flush data before updating used ring. if !virtio_has_feature(complete_cb.driver_features, VIRTIO_BLK_F_FLUSH) - && aiocb.opcode == IoCmd::Pwritev + && aiocb.opcode == OpCode::Pwritev && ret >= 0 { if let Err(ref e) = raw_datasync(aiocb.file_fd) { diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index c670484b3..0dcd60b53 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -31,7 +31,7 @@ use crate::ScsiDisk::{ use address_space::AddressSpace; use byteorder::{BigEndian, ByteOrder}; use log::{debug, error, info}; -use util::aio::{Aio, AioCb, IoCmd, Iovec}; +use util::aio::{Aio, AioCb, Iovec, OpCode}; /// Scsi Operation code. pub const TEST_UNIT_READY: u8 = 0x00; @@ -528,12 +528,11 @@ impl ScsiRequest { let mut aiocb = AioCb { last_aio, file_fd: disk.as_raw_fd(), - opcode: IoCmd::Noop, + opcode: OpCode::Noop, iovec: Vec::new(), offset: (self.cmd.lba << offset) as usize, nbytes: 0, - process: true, - iocb: None, + user_data: 0, iocompletecb, }; @@ -548,7 +547,7 @@ impl ScsiRequest { } if self.cmd.command == SYNCHRONIZE_CACHE { - aiocb.opcode = IoCmd::Fdsync; + aiocb.opcode = OpCode::Fdsync; (*aio) .as_mut() .flush_sync(aiocb) @@ -558,7 +557,7 @@ impl ScsiRequest { match self.cmd.mode { ScsiXferMode::ScsiXferFromDev => { - aiocb.opcode = IoCmd::Preadv; + aiocb.opcode = OpCode::Preadv; if aio_type.is_some() { (*aio) .as_mut() @@ -573,7 +572,7 @@ impl ScsiRequest { } } ScsiXferMode::ScsiXferToDev => { - aiocb.opcode = IoCmd::Pwritev; + aiocb.opcode = OpCode::Pwritev; if aio_type.is_some() { (*aio) .as_mut() -- Gitee From c9efbe20fe2d01e00c9ed331f41ad1b513262ebf Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 16 Jan 2023 01:29:21 +0000 Subject: [PATCH 0710/2187] Add virtio-gpu guide Signed-off-by: wubinfeng --- docs/config_guidebook.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index ce023bbac..a6c510338 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -910,6 +910,25 @@ host# stratovirt \ guest# mount -t virtiofs myfs /mnt ``` +### 2.20 virtio-gpu +virtio-gpu is an virtualized graphics card that lets virtual machines can display with it. +Usually used in conjunction with VNC, the final images is rendered to the VNC client. + +Sample Configuration: +```shell +-device virtio-gpu-pci,id=,bus=pcie.0,addr=0x2.0x0[,max_outputs=][,edid=true|false][,xres=][,yres= ][,max_hostmem=] +``` + +In addition to the required slot information, five optional properties are supported for virtio-gpu. +* max_outputs: Number of screens supported by the current graphics card. The maximun value is 16. (can switch by using ctrl + alt + , for details, see vnc Client switchover) +* edid: Edid feature, the virtual machine's kernel may checks this feature for HiDPi. You are advised to set to true. +* xres/yres: The size of the login windows. +* max_hostmem: The maximum memory that a graphics card can occupy on the host is expressed in byte. You are advised to set not less than 256MiB, otherwise the final supported resoltuion is affected. + +Note: +1. Only virtio-gpu 2D supported. +2. Live migration is not supported. + ## 3. Trace Users can specify the configuration file which lists events to trace. -- Gitee From 571003d0f8e8b7e6ed505101d576591b09425750 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 11 Jan 2023 19:45:15 +0800 Subject: [PATCH 0711/2187] usb: the reset operation of the controller and device is optimized The reset operation of the usb controller and device is optimized. Signed-off-by: zhouli57 --- usb/src/keyboard.rs | 2 +- usb/src/tablet.rs | 2 +- usb/src/xhci/xhci_controller.rs | 1 + usb/src/xhci/xhci_regs.rs | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 2baec5dd0..5818a71b0 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -161,7 +161,7 @@ pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Resu impl UsbDeviceOps for UsbKeyboard { fn reset(&mut self) { info!("Keyboard device reset"); - self.usb_device.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; + self.usb_device.remote_wakeup = 0; self.usb_device.addr = 0; self.hid.reset(); } diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 83a55b712..b551be5c2 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -165,7 +165,7 @@ pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32 impl UsbDeviceOps for UsbTablet { fn reset(&mut self) { info!("Tablet device reset"); - self.usb_device.remote_wakeup &= !USB_DEVICE_REMOTE_WAKEUP; + self.usb_device.remote_wakeup = 0; self.usb_device.addr = 0; self.hid.reset(); } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index b94f9e9cf..b3d24db71 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -528,6 +528,7 @@ impl XhciDevice { for i in 0..self.intrs.len() { self.intrs[i].reset(); } + self.cmd_ring.init(0); } /// Reset xhci port. diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 3912826d9..2653f7a2c 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -182,7 +182,7 @@ impl XhciInterrupter { erstsz: 0, erstba: 0, erdp: 0, - er_pcs: false, + er_pcs: true, er_start: 0, er_size: 0, er_ep_idx: 0, @@ -195,7 +195,7 @@ impl XhciInterrupter { self.erstsz = 0; self.erstba = 0; self.erdp = 0; - self.er_pcs = false; + self.er_pcs = true; self.er_start = 0; self.er_size = 0; self.er_ep_idx = 0; -- Gitee From ef194585930c5f38cc7abdd1a0f7de65a06c3d1f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 12 Jan 2023 21:12:25 +0800 Subject: [PATCH 0712/2187] usb: fix a potential arithmetic overflow issue Fix a potential arithmetic overflow issue by using checked_add. Signed-off-by: zhouli57 --- usb/src/error.rs | 2 + usb/src/xhci/xhci_controller.rs | 93 ++++++++++++++++++++++++++++----- usb/src/xhci/xhci_regs.rs | 27 ++++++++-- usb/src/xhci/xhci_ring.rs | 9 +++- 4 files changed, 111 insertions(+), 20 deletions(-) diff --git a/usb/src/error.rs b/usb/src/error.rs index 1d381e80e..6e7e1c781 100644 --- a/usb/src/error.rs +++ b/usb/src/error.rs @@ -29,4 +29,6 @@ pub enum UsbError { #[from] source: std::io::Error, }, + #[error("Memory access overflow, addr: 0x{0:X} offset: 0x{1:X}")] + MemoryAccessOverflow(u64, u64), } diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index b3d24db71..c01e4e66b 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -17,6 +17,8 @@ use std::slice::from_raw_parts_mut; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; +use anyhow::{anyhow, bail, Context, Result}; + use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, warn}; @@ -30,7 +32,7 @@ use crate::xhci::xhci_ring::{ TRBCCode, TRBType, XhciEventRingSeg, XhciRing, XhciTRB, TRB_EV_ED, TRB_SIZE, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TYPE_SHIFT, }; -use anyhow::{bail, Context, Result}; +use crate::UsbError; use super::xhci_ring::SETUP_TRB_TR_LEN; use super::xhci_ring::TRB_TR_DIR; @@ -83,6 +85,10 @@ const TRANSFER_LEN_MASK: u32 = 0xffffff; const XHCI_MAX_PORT2: u8 = 15; const XHCI_MAX_PORT3: u8 = 15; const XHCI_DEFAULT_PORT: u8 = 4; +/// Input Context. +const INPUT_CONTEXT_SIZE: u64 = 0x420; +/// Device Context. +const DEVICE_CONTEXT_SIZE: u64 = 0x400; /// Slot Context. const SLOT_INPUT_CTX_OFFSET: u64 = 0x20; const SLOT_CTX_MAX_EXIT_LATENCY_MASK: u32 = 0xffff; @@ -754,6 +760,13 @@ impl XhciDevice { fn address_device(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; + if ictx.checked_add(INPUT_CONTEXT_SIZE).is_none() { + bail!( + "Input Context access overflow, addr {:x} size {:x}", + ictx, + INPUT_CONTEXT_SIZE + ); + } let ccode = self.check_input_ctx(ictx)?; if ccode != TRBCCode::Success { return Ok(ccode); @@ -761,7 +774,10 @@ impl XhciDevice { let mut slot_ctx = XhciSlotCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(ictx + SLOT_INPUT_CTX_OFFSET), + GuestAddress( + // It is safe to plus here becuase we previously verify the address. + ictx + SLOT_INPUT_CTX_OFFSET, + ), slot_ctx.as_mut_dwords(), )?; let bsr = trb.control & TRB_CR_BSR == TRB_CR_BSR; @@ -782,9 +798,16 @@ impl XhciDevice { error!("No device found in usb port."); return Ok(TRBCCode::UsbTransactionError); }; - let ctx_addr = self.get_device_context_addr(slot_id); + let ctx_addr = self.get_device_context_addr(slot_id)?; let mut octx = 0; dma_read_u64(&self.mem_space, GuestAddress(ctx_addr), &mut octx)?; + if octx.checked_add(DEVICE_CONTEXT_SIZE).is_none() { + bail!( + "Device Context access overflow, addr {:x} size {:x}", + octx, + DEVICE_CONTEXT_SIZE + ); + } self.slots[(slot_id - 1) as usize].usb_port = Some(usb_port.clone()); self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; dev.lock().unwrap().reset(); @@ -841,8 +864,16 @@ impl XhciDevice { locked_dev.handle_control(&mut p, &device_req); } - fn get_device_context_addr(&self, slot_id: u32) -> u64 { - self.oper.dcbaap + (8 * slot_id) as u64 + fn get_device_context_addr(&self, slot_id: u32) -> Result { + self.oper + .dcbaap + .checked_add((8 * slot_id) as u64) + .ok_or_else(|| { + anyhow!(UsbError::MemoryAccessOverflow( + self.oper.dcbaap, + (8 * slot_id) as u64 + )) + }) } fn configure_endpoint(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { @@ -877,6 +908,13 @@ impl XhciDevice { } fn config_slot_ep(&mut self, slot_id: u32, ictx: u64) -> Result { + if ictx.checked_add(INPUT_CONTEXT_SIZE).is_none() { + bail!( + "Input Context access overflow, addr {:x} size {:x}", + ictx, + INPUT_CONTEXT_SIZE + ); + } let mut ictl_ctx = XhciInputCtrlCtx::default(); dma_read_u32( &self.mem_space, @@ -929,6 +967,13 @@ impl XhciDevice { return Ok(TRBCCode::ContextStateError); } let ictx = trb.parameter; + if ictx.checked_add(INPUT_CONTEXT_SIZE).is_none() { + bail!( + "Input Context access overflow, addr {:x} size {:x}", + ictx, + INPUT_CONTEXT_SIZE + ); + } let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; let mut ictl_ctx = XhciInputCtrlCtx::default(); dma_read_u32( @@ -944,7 +989,10 @@ impl XhciDevice { let mut islot_ctx = XhciSlotCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(ictx + SLOT_INPUT_CTX_OFFSET), + GuestAddress( + // It is safe to plus here becuase we previously verify the address. + ictx + SLOT_INPUT_CTX_OFFSET, + ), islot_ctx.as_mut_dwords(), )?; let mut slot_ctx = XhciSlotCtx::default(); @@ -964,19 +1012,26 @@ impl XhciDevice { let mut iep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(ictx + EP_INPUT_CTX_OFFSET), + GuestAddress( + // It is safe to use plus here becuase we previously verify the address. + ictx + EP_INPUT_CTX_OFFSET, + ), iep_ctx.as_mut_dwords(), )?; let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, - GuestAddress(octx + EP_CTX_OFFSET), + GuestAddress( + // It is safe to use plus here becuase we previously verify the address. + octx + EP_CTX_OFFSET, + ), ep_ctx.as_mut_dwords(), )?; ep_ctx.ep_info2 &= !EP_CTX_MAX_PACKET_SIZE_MASK; ep_ctx.ep_info2 |= iep_ctx.ep_info2 & EP_CTX_MAX_PACKET_SIZE_MASK; dma_write_u32( &self.mem_space, + // It is safe to use plus here becuase we previously verify the address. GuestAddress(octx + EP_CTX_OFFSET), ep_ctx.as_dwords(), )?; @@ -1021,6 +1076,7 @@ impl XhciDevice { let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, + // It is safe to use plus here becuase we previously verify the address on the outer layer. GuestAddress(input_ctx + EP_INPUT_CTX_OFFSET + entry_offset), ep_ctx.as_mut_dwords(), )?; @@ -1028,12 +1084,14 @@ impl XhciDevice { let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; epctx.epid = ep_id; epctx.enabled = true; + // It is safe to use plus here becuase we previously verify the address on the outer layer. epctx.init_ctx(output_ctx + EP_CTX_OFFSET + entry_offset, &ep_ctx); epctx.state = EP_RUNNING; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= EP_RUNNING; dma_write_u32( &self.mem_space, + // It is safe to use plus here becuase we previously verify the address on the outer layer. GuestAddress(output_ctx + EP_CTX_OFFSET + entry_offset), ep_ctx.as_dwords(), )?; @@ -1462,7 +1520,7 @@ impl XhciDevice { /// Submit the succeed transfer TRBs. fn submit_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { // Event Data Transfer Length Accumulator - let mut edtla = 0; + let mut edtla: u32 = 0; let mut left = xfer.packet.actual_length; for i in 0..xfer.td.len() { let trb = &xfer.td[i]; @@ -1476,7 +1534,11 @@ impl XhciDevice { xfer.status = TRBCCode::ShortPacket; } left -= chunk; - edtla += chunk; + if let Some(v) = edtla.checked_add(chunk) { + edtla = v; + } else { + bail!("Event Data Transfer Length Accumulator overflow, edtla {:x} offset {:x}", edtla, chunk); + } } TRBType::TrStatus => {} _ => { @@ -1617,9 +1679,14 @@ impl XhciDevice { bail!("Invalid index, out of range {}", idx); } let intr = &self.intrs[idx as usize]; - if intr.erdp < intr.er_start - || intr.erdp >= (intr.er_start + (TRB_SIZE * intr.er_size) as u64) - { + let er_end = intr + .er_start + .checked_add((TRB_SIZE * intr.er_size) as u64) + .ok_or(UsbError::MemoryAccessOverflow( + intr.er_start, + (TRB_SIZE * intr.er_size) as u64, + ))?; + if intr.erdp < intr.er_start || intr.erdp >= er_end { bail!( "DMA out of range, erdp {} er_start {:x} er_size {}", intr.erdp, diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 2653f7a2c..13078ce3a 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -19,9 +19,9 @@ use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error}; use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; -use crate::config::*; use crate::xhci::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; +use crate::{config::*, UsbError}; use anyhow::Result; /// Capability offset or size. @@ -218,7 +218,13 @@ impl XhciInterrupter { } fn write_trb(&mut self, trb: &XhciTRB) -> Result<()> { - let addr = self.er_start + (TRB_SIZE * self.er_ep_idx) as u64; + let addr = self + .er_start + .checked_add((TRB_SIZE * self.er_ep_idx) as u64) + .ok_or(UsbError::MemoryAccessOverflow( + self.er_start, + (TRB_SIZE * self.er_ep_idx) as u64, + ))?; let mut buf = [0_u8; TRB_SIZE as usize]; LittleEndian::write_u64(&mut buf, trb.parameter); LittleEndian::write_u32(&mut buf[8..], trb.status); @@ -508,10 +514,21 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { intr.erdp = write_u64_low(intr.erdp, erdp_lo); if value & ERDP_EHB == ERDP_EHB { let erdp = intr.erdp; - let dp_idx = (erdp - intr.er_start) / TRB_SIZE as u64; + let er_end = if let Some(addr) = + intr.er_start.checked_add((TRB_SIZE * intr.er_size) as u64) + { + addr + } else { + error!( + "Memory access overflow, addr {:x} offset {:x}", + intr.er_start, + (TRB_SIZE * intr.er_size) as u64 + ); + return false; + }; if erdp >= intr.er_start - && erdp < intr.er_start + (TRB_SIZE * intr.er_size) as u64 - && dp_idx != intr.er_ep_idx as u64 + && erdp < er_end + && (erdp - intr.er_start) / TRB_SIZE as u64 != intr.er_ep_idx as u64 { xhci.send_intr(idx); } diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index c29ea04c1..80fcb5e71 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -18,6 +18,7 @@ use byteorder::{ByteOrder, LittleEndian}; use log::debug; use crate::xhci::xhci_controller::dma_read_bytes; +use crate::UsbError; use anyhow::{bail, Result}; /// Transfer Request Block @@ -253,7 +254,9 @@ impl XhciRing { self.ccs = !self.ccs; } } else { - self.dequeue += TRB_SIZE as u64; + self.dequeue = self.dequeue.checked_add(TRB_SIZE as u64).ok_or( + UsbError::MemoryAccessOverflow(self.dequeue, TRB_SIZE as u64), + )?; return Ok(Some(trb)); } } @@ -303,7 +306,9 @@ impl XhciRing { } } else { td.push(trb); - dequeue += TRB_SIZE as u64; + dequeue = dequeue + .checked_add(TRB_SIZE as u64) + .ok_or(UsbError::MemoryAccessOverflow(dequeue, TRB_SIZE as u64))?; if trb_type == TRBType::TrSetup { ctrl_td = true; } else if trb_type == TRBType::TrStatus { -- Gitee From 1d8cae1e1e98c8b215f941ec0b02f74107a7e1f9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 9 Jan 2023 00:19:01 +0800 Subject: [PATCH 0713/2187] aio: Refactory and enhancement for aio engine 1. Use enum for aio engine, which makes code more clean and robust. 2. Try probe aio engine in config check, as it's late to report error when Guest active device. 3. Update aio engine in mmio block hotplug. Signed-off-by: Keqian Zhu --- Cargo.lock | 1 + machine/src/micro_vm/mod.rs | 6 +- machine/src/standard_vm/mod.rs | 8 +- machine_manager/src/config/drive.rs | 59 +++++------ machine_manager/src/config/scsi.rs | 25 ++++- util/Cargo.toml | 1 + util/src/aio/libaio.rs | 11 ++- util/src/aio/mod.rs | 147 ++++++++++++++++++++-------- util/src/aio/uring.rs | 6 +- virtio/src/block.rs | 68 +++++++------ virtio/src/scsi/bus.rs | 35 ++----- virtio/src/scsi/controller.rs | 12 ++- 12 files changed, 222 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e229f27d..9cd864f4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,6 +935,7 @@ dependencies = [ "libc", "log", "once_cell", + "serde", "thiserror", "vmm-sys-util", ] diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 9d0ac49e6..6cece85f5 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -30,7 +30,7 @@ pub mod error; pub use error::MicroVmError; -use util::aio::AIO_NATIVE; +use util::aio::AioEngine; mod mem_layout; mod syscall; @@ -1167,9 +1167,9 @@ impl DeviceInterface for LightMachine { socket_path: None, // TODO Add aio option by qmp, now we set it based on "direct". aio: if direct { - Some(String::from(AIO_NATIVE)) + AioEngine::Native } else { - None + AioEngine::Off }, queue_size: DEFAULT_VIRTQUEUE_SIZE, }; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 63667321f..8ee2caf6e 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -22,7 +22,7 @@ pub use error::StandardVmError; pub use aarch64::StdMachine; use log::error; use machine_manager::event_loop::EventLoop; -use util::aio::AIO_NATIVE; +use util::aio::AioEngine; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -757,7 +757,7 @@ impl StdMachine { boot_index: args.boot_index, chardev: None, socket_path: None, - aio: conf.aio.clone(), + aio: conf.aio, queue_size, }; dev.check()?; @@ -1255,9 +1255,9 @@ impl DeviceInterface for StdMachine { iops: args.iops, // TODO Add aio option by qmp, now we set it based on "direct". aio: if direct { - Some(String::from(AIO_NATIVE)) + AioEngine::Native } else { - None + AioEngine::Off }, }; diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index cd7b86476..c4dbefddc 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -17,7 +17,6 @@ use std::path::Path; use anyhow::{anyhow, bail, Result}; use log::error; use serde::{Deserialize, Serialize}; -use util::aio::{AIO_IOURING, AIO_NATIVE}; use super::{error::ConfigError, pci_args_check}; use crate::config::{ @@ -25,6 +24,7 @@ use crate::config::{ MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::qmp_schema; +use util::aio::{aio_probe, AioEngine}; const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; @@ -62,7 +62,7 @@ pub struct BlkDevConfig { pub boot_index: Option, pub chardev: Option, pub socket_path: Option, - pub aio: Option, + pub aio: AioEngine, pub queue_size: u16, } @@ -87,7 +87,7 @@ impl Default for BlkDevConfig { boot_index: None, chardev: None, socket_path: None, - aio: Some(AIO_NATIVE.to_string()), + aio: AioEngine::Native, queue_size: DEFAULT_VIRTQUEUE_SIZE, } } @@ -103,7 +103,7 @@ pub struct DriveConfig { pub read_only: bool, pub direct: bool, pub iops: Option, - pub aio: Option, + pub aio: AioEngine, } impl Default for DriveConfig { @@ -114,7 +114,7 @@ impl Default for DriveConfig { read_only: false, direct: true, iops: None, - aio: Some(String::from(AIO_NATIVE)), + aio: AioEngine::Native, } } } @@ -182,13 +182,15 @@ impl ConfigCheck for DriveConfig { true, ))); } - if self.aio == Some(String::from(AIO_NATIVE)) && !self.direct { - return Err(anyhow!(ConfigError::InvalidParam( - "aio".to_string(), - "native aio type should be used with \"direct\" on".to_string(), - ))); - } - if self.aio.is_none() && self.direct { + if self.aio != AioEngine::Off { + if self.aio == AioEngine::Native && !self.direct { + return Err(anyhow!(ConfigError::InvalidParam( + "aio".to_string(), + "native aio type should be used with \"direct\" on".to_string(), + ))); + } + aio_probe(self.aio)?; + } else if self.direct { return Err(anyhow!(ConfigError::InvalidParam( "aio".to_string(), "low performance expected when use sync io with \"direct\" on".to_string(), @@ -249,7 +251,7 @@ impl ConfigCheck for BlkDevConfig { path_on_host: self.path_on_host.clone(), direct: self.direct, iops: self.iops, - aio: self.aio.clone(), + aio: self.aio, ..Default::default() }; fake_drive.check()?; @@ -290,26 +292,15 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { drive.direct = direct.into(); } drive.iops = cmd_parser.get_value::("throttling.iops-total")?; - drive.aio = if let Some(aio) = cmd_parser.get_value::("aio")? { - let aio_off = "off"; - if aio != AIO_NATIVE && aio != AIO_IOURING && aio != aio_off { - bail!( - "Invalid aio configure, should be one of {}|{}|{}", - AIO_NATIVE, - AIO_IOURING, - aio_off - ); - } - if aio != aio_off { - Some(aio) - } else { - None - } - } else if drive.direct { - Some(AIO_NATIVE.to_string()) - } else { - None - }; + drive.aio = cmd_parser + .get_value::("aio")? + .unwrap_or_else(|| { + if drive.direct { + AioEngine::Native + } else { + AioEngine::Off + } + }); drive.check()?; #[cfg(not(test))] drive.check_path()?; @@ -379,7 +370,7 @@ pub fn parse_blk( blkdevcfg.read_only = drive_arg.read_only; blkdevcfg.direct = drive_arg.direct; blkdevcfg.iops = drive_arg.iops; - blkdevcfg.aio = drive_arg.aio.clone(); + blkdevcfg.aio = drive_arg.aio; } else { bail!("No drive configured matched for blk device"); } diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 338e57db1..5a632cf6b 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -16,6 +16,7 @@ use super::{error::ConfigError, pci_args_check}; use crate::config::{ CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; +use util::aio::AioEngine; /// According to Virtio Spec. /// Max_channel should be 0. @@ -152,7 +153,7 @@ pub fn parse_scsi_controller( Ok(cntlr_cfg) } -#[derive(Clone, Default)] +#[derive(Clone)] pub struct ScsiDevConfig { /// Scsi Device id. pub id: String, @@ -167,7 +168,7 @@ pub struct ScsiDevConfig { /// If true, use direct access io. pub direct: bool, /// Async IO type. - pub aio_type: Option, + pub aio_type: AioEngine, /// Boot order. pub boot_index: Option, /// Scsi four level hierarchical address(host, channel, target, lun). @@ -176,6 +177,24 @@ pub struct ScsiDevConfig { pub lun: u16, } +impl Default for ScsiDevConfig { + fn default() -> Self { + ScsiDevConfig { + id: "".to_string(), + path_on_host: "".to_string(), + serial: None, + bus: "".to_string(), + read_only: false, + direct: true, + aio_type: AioEngine::Native, + boot_index: None, + channel: 0, + target: 0, + lun: 0, + } + } +} + pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result { let mut cmd_parser = CmdParser::new("scsi-device"); cmd_parser @@ -251,7 +270,7 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); scsi_dev_cfg.read_only = drive_arg.read_only; scsi_dev_cfg.direct = drive_arg.direct; - scsi_dev_cfg.aio_type = drive_arg.aio.clone(); + scsi_dev_cfg.aio_type = drive_arg.aio; } Ok(scsi_dev_cfg) diff --git a/util/Cargo.toml b/util/Cargo.toml index 6d1eca527..1e7c96611 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -20,3 +20,4 @@ byteorder = "1.4.3" once_cell = "1.13.0" io-uring = "0.5.7" errno = "0.2.8" +serde = { version = "1.0", features = ["derive"] } diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 258905793..86d870f54 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -64,7 +64,7 @@ enum IoCmd { } #[allow(non_camel_case_types)] -enum IoContext {} +pub(crate) enum IoContext {} pub(crate) struct LibaioContext { ctx: *mut IoContext, @@ -98,15 +98,18 @@ struct AioRing { } impl LibaioContext { - pub fn new(max_size: u32, eventfd: &EventFd) -> Result { + pub fn probe(max_size: u32) -> Result<*mut IoContext> { let mut ctx = std::ptr::null_mut(); - // SAFETY: ctx is a valid ptr. let ret = unsafe { libc::syscall(libc::SYS_io_setup, max_size, &mut ctx) }; if ret < 0 { - bail!("Failed to setup aio context, return {}.", ret); + bail!("Failed to setup linux native aio context, return {}.", ret); } + Ok(ctx) + } + pub fn new(max_size: u32, eventfd: &EventFd) -> Result { + let ctx = Self::probe(max_size)?; Ok(LibaioContext { ctx, resfd: eventfd.as_raw_fd(), diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 23ac81fdb..96e0cd8a0 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -15,12 +15,13 @@ mod raw; mod uring; use std::clone::Clone; -use std::cmp; use std::io::Write; use std::os::unix::io::RawFd; use std::sync::Arc; +use std::{cmp, str::FromStr}; -use log::error; +use log::{error, warn}; +use serde::{Deserialize, Serialize}; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; @@ -32,10 +33,32 @@ use uring::IoUringContext; type CbList = List>; type CbNode = Node>; -/// Io-uring aio type. -pub const AIO_IOURING: &str = "io_uring"; +/// None aio type. +const AIO_OFF: &str = "off"; /// Native aio type. -pub const AIO_NATIVE: &str = "native"; +const AIO_NATIVE: &str = "native"; +/// Io-uring aio type. +const AIO_IOURING: &str = "io_uring"; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] +pub enum AioEngine { + Off = 0, + Native = 1, + IoUring = 2, +} + +impl FromStr for AioEngine { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + match s { + AIO_OFF => Ok(AioEngine::Off), + AIO_NATIVE => Ok(AioEngine::Native), + AIO_IOURING => Ok(AioEngine::IoUring), + _ => Err(()), + } + } +} #[derive(Debug, Clone)] pub struct Iovec { @@ -67,6 +90,8 @@ pub enum OpCode { pub struct AioCb { pub last_aio: bool, + pub direct: bool, + pub sector_size: u64, pub file_fd: RawFd, pub opcode: OpCode, pub iovec: Vec, @@ -79,7 +104,8 @@ pub struct AioCb { pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; pub struct Aio { - ctx: Box>, + ctx: Option>>, + engine: AioEngine, pub fd: EventFd, pub aio_in_queue: CbList, pub aio_in_flight: CbList, @@ -87,24 +113,34 @@ pub struct Aio { complete_func: Arc>, } +pub fn aio_probe(engine: AioEngine) -> Result<()> { + match engine { + AioEngine::Off => {} + AioEngine::Native => { + let ctx = LibaioContext::probe(1)?; + // SAFETY: if no err, ctx is valid. + unsafe { libc::syscall(libc::SYS_io_destroy, ctx) }; + } + AioEngine::IoUring => { + IoUringContext::probe(1)?; + } + } + Ok(()) +} + impl Aio { - pub fn new(func: Arc>, engine: Option<&String>) -> Result { + pub fn new(func: Arc>, engine: AioEngine) -> Result { let max_events: usize = 128; let fd = EventFd::new(libc::EFD_NONBLOCK)?; - let aio = if let Some(engine) = engine { - engine - } else { - AIO_NATIVE - }; - - let ctx: Box> = if aio == AIO_IOURING { - Box::new(IoUringContext::new(max_events as u32, &fd)?) - } else { - Box::new(LibaioContext::new(max_events as u32, &fd)?) + let ctx: Option>> = match engine { + AioEngine::Off => None, + AioEngine::Native => Some(Box::new(LibaioContext::new(max_events as u32, &fd)?)), + AioEngine::IoUring => Some(Box::new(IoUringContext::new(max_events as u32, &fd)?)), }; Ok(Aio { ctx, + engine, fd, aio_in_queue: List::new(), aio_in_flight: List::new(), @@ -113,10 +149,46 @@ impl Aio { }) } - pub fn handle(&mut self) -> Result { - let mut done = false; + pub fn get_engine(&self) -> AioEngine { + self.engine + } - for evt in self.ctx.get_events() { + pub fn submit_request(&mut self, cb: AioCb) -> Result<()> { + if cb.direct && (cb.opcode == OpCode::Preadv || cb.opcode == OpCode::Pwritev) { + for iov in cb.iovec.iter() { + if iov.iov_base % cb.sector_size != 0 || iov.iov_len % cb.sector_size != 0 { + let res = self.handle_misaligned_rw(&cb).map_or_else( + |e| { + error!("{:?}", e); + -1 + }, + |_| 0, + ); + return (self.complete_func)(&cb, res); + } + } + } + + return match cb.opcode { + OpCode::Preadv | OpCode::Pwritev => { + if self.ctx.is_some() { + self.rw_aio(cb) + } else { + self.rw_sync(cb) + } + } + OpCode::Fdsync => self.flush_sync(cb), + OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), + }; + } + + pub fn handle_complete(&mut self) -> Result { + let mut done = false; + if self.ctx.is_none() { + warn!("Can not handle aio complete with invalid ctx."); + return Ok(done); + } + for evt in self.ctx.as_mut().unwrap().get_events() { // SAFETY: evt.data is specified by submit and not dropped at other place. unsafe { let node = evt.user_data as *mut CbNode; @@ -142,6 +214,10 @@ impl Aio { } fn process_list(&mut self) -> Result<()> { + if self.ctx.is_none() { + warn!("Can not process aio list with invalid ctx."); + return Ok(()); + } while self.aio_in_queue.len > 0 && self.aio_in_flight.len < self.max_events { let mut iocbs = Vec::new(); @@ -156,12 +232,12 @@ impl Aio { } // The iocbs must not be empty. - let (nr, is_err) = match self.ctx.submit(&iocbs).map_err(|e| { - error!("{}", e); - e - }) { + let (nr, is_err) = match self.ctx.as_mut().unwrap().submit(&iocbs) { Ok(nr) => (nr, false), - Err(_) => (0, true), + Err(e) => { + error!("{:?}", e); + (0, true) + } }; // Push back unsubmitted requests. This should rarely happen, so the @@ -188,22 +264,7 @@ impl Aio { Ok(()) } - pub fn rw_aio(&mut self, cb: AioCb, sector_size: u64, direct: bool) -> Result<()> { - if direct { - for iov in cb.iovec.iter() { - if iov.iov_base % sector_size != 0 || iov.iov_len % sector_size != 0 { - let res = self.handle_misaligned_rw(&cb).map_or_else( - |e| { - error!("{:?}", e); - -1 - }, - |_| 0, - ); - return (self.complete_func)(&cb, res); - } - } - } - + fn rw_aio(&mut self, cb: AioCb) -> Result<()> { let last_aio = cb.last_aio; let mut node = Box::new(Node::new(cb)); node.value.user_data = (&mut (*node) as *mut CbNode) as u64; @@ -216,7 +277,7 @@ impl Aio { Ok(()) } - pub fn rw_sync(&mut self, cb: AioCb) -> Result<()> { + fn rw_sync(&mut self, cb: AioCb) -> Result<()> { let ret = match cb.opcode { OpCode::Preadv => { let mut r = 0; @@ -333,7 +394,7 @@ impl Aio { } } - pub fn flush_sync(&mut self, cb: AioCb) -> Result<()> { + fn flush_sync(&mut self, cb: AioCb) -> Result<()> { let ret = raw_datasync(cb.file_fd).unwrap_or_else(|e| { error!("Failed to do sync flush, {:?}", e); -1 diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index b00b9b918..fb247cd01 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -26,13 +26,17 @@ pub(crate) struct IoUringContext { } impl IoUringContext { + pub fn probe(entries: u32) -> Result { + IoUring::new(entries).with_context(|| "Failed to create io_uring instance.") + } + pub fn new(entries: u32, eventfd: &EventFd) -> Result { let tmp_entries = entries as i32; // Ensure the power of 2. if (tmp_entries & -tmp_entries) != tmp_entries || tmp_entries == 0 { bail!("Entries must be the power of 2 and larger than 0"); } - let ring = IoUring::new(entries).with_context(|| "Failed to create io_uring instance")?; + let ring = Self::probe(entries)?; ring.submitter() .register_eventfd(eventfd.as_raw_fd()) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 59b2b09fe..d5a70022a 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -42,7 +42,7 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::aio::{iov_from_buf_direct, raw_datasync, Aio, AioCb, Iovec, OpCode, AIO_NATIVE}; +use util::aio::{iov_from_buf_direct, raw_datasync, Aio, AioCb, AioEngine, Iovec, OpCode}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ @@ -68,7 +68,7 @@ const MAX_NUM_MERGE_BYTES: u64 = i32::MAX as u64; /// Max time for every round of process queue. const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; -type SenderConfig = (Option>, u64, Option, bool, Option); +type SenderConfig = (Option>, u64, Option, bool, AioEngine); fn get_serial_num_config(serial_num: &str) -> Vec { let mut id_bytes = vec![0; VIRTIO_BLK_ID_BYTES as usize]; @@ -300,36 +300,20 @@ impl Request { let aio = &mut iohandler.aio; let serial_num = &iohandler.serial_num; - let aio_type = &iohandler.aio_type; - let direct = iohandler.direct; match request_type { VIRTIO_BLK_T_IN => { aiocb.opcode = OpCode::Preadv; - if aio_type.is_some() { - aio.rw_aio(aiocb, SECTOR_SIZE, direct).with_context(|| { - "Failed to process block request for reading asynchronously" - })?; - } else { - aio.rw_sync(aiocb).with_context(|| { - "Failed to process block request for reading synchronously" - })?; - } + aio.submit_request(aiocb) + .with_context(|| "Failed to process block request for reading")?; } VIRTIO_BLK_T_OUT => { aiocb.opcode = OpCode::Pwritev; - if aio_type.is_some() { - aio.rw_aio(aiocb, SECTOR_SIZE, direct).with_context(|| { - "Failed to process block request for writing asynchronously" - })?; - } else { - aio.rw_sync(aiocb).with_context(|| { - "Failed to process block request for writing synchronously" - })?; - } + aio.submit_request(aiocb) + .with_context(|| "Failed to process block request for writing")?; } VIRTIO_BLK_T_FLUSH => { aiocb.opcode = OpCode::Fdsync; - aio.flush_sync(aiocb) + aio.submit_request(aiocb) .with_context(|| "Failed to process block request for flushing")?; } VIRTIO_BLK_T_GET_ID => { @@ -396,8 +380,6 @@ struct BlockIoHandler { serial_num: Option, /// If use direct access io. direct: bool, - /// Async IO type. - aio_type: Option, /// Aio context. aio: Box>, /// Bit mask of features negotiated by the backend and the frontend. @@ -541,6 +523,8 @@ impl BlockIoHandler { if let Some(disk_img) = self.disk_image.as_ref() { let aiocb = AioCb { last_aio: req_index == last_aio_req_index, + direct: self.direct, + sector_size: SECTOR_SIZE, file_fd: disk_img.as_raw_fd(), opcode: OpCode::Noop, iovec: Vec::new(), @@ -646,7 +630,7 @@ impl BlockIoHandler { } fn aio_complete_handler(&mut self) -> Result { - self.aio.handle().map_err(|e| { + self.aio.handle_complete().map_err(|e| { report_virtio_error( self.interrupt_cb.clone(), self.driver_features, @@ -657,13 +641,14 @@ impl BlockIoHandler { } fn update_evt_handler(&mut self) { + let aio_engine; match self.receiver.recv() { - Ok((image, disk_sectors, serial_num, direct, aio_type)) => { + Ok((image, disk_sectors, serial_num, direct, aio)) => { self.disk_sectors = disk_sectors; self.disk_image = image; self.serial_num = serial_num; self.direct = direct; - self.aio_type = aio_type; + aio_engine = aio; } Err(e) => { error!("Failed to receive config in updating handler {:?}", e); @@ -671,10 +656,27 @@ impl BlockIoHandler { self.disk_image = None; self.serial_num = None; self.direct = true; - self.aio_type = Some(String::from(AIO_NATIVE)); + aio_engine = AioEngine::Native; } }; + if self.aio.get_engine() != aio_engine { + match Aio::new(Arc::new(Self::complete_func), aio_engine) { + Ok(aio) => { + self.aio = Box::new(aio); + } + Err(e) => { + error!("{:?}", e); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + return; + } + } + } + if let Err(e) = (self.interrupt_cb)(&VirtioInterruptType::Config, None, false) { error!( "{:?}. {:?}", @@ -807,6 +809,9 @@ impl EventNotifierHelper for BlockIoHandler { if h_lock.device_broken.load(Ordering::SeqCst) { return None; } + if h_lock.aio.get_engine() == AioEngine::Off { + return None; + } let done = h_lock .aio_complete_handler() .with_context(|| "Failed to handle aio") @@ -1099,7 +1104,7 @@ impl VirtioDevice for Block { senders.push(sender); let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); - let engine = self.blk_cfg.aio.as_ref(); + let engine = self.blk_cfg.aio; let handler = BlockIoHandler { queue: queue.clone(), queue_evt: queue_evts.remove(0), @@ -1107,7 +1112,6 @@ impl VirtioDevice for Block { disk_image: self.disk_image.clone(), disk_sectors: self.disk_sectors, direct: self.blk_cfg.direct, - aio_type: self.blk_cfg.aio.clone(), serial_num: self.blk_cfg.serial_num.clone(), aio: Box::new(Aio::new(Arc::new(BlockIoHandler::complete_func), engine)?), driver_features: self.state.driver_features, @@ -1166,7 +1170,7 @@ impl VirtioDevice for Block { self.disk_sectors, self.blk_cfg.serial_num.clone(), self.blk_cfg.direct, - self.blk_cfg.aio.clone(), + self.blk_cfg.aio, )) .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; } diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 0dcd60b53..7599c26e3 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -516,7 +516,6 @@ impl ScsiRequest { aio: &mut Box>, disk: &File, direct: bool, - aio_type: Option, last_aio: bool, iocompletecb: ScsiCompleteCb, ) -> Result { @@ -527,6 +526,8 @@ impl ScsiRequest { }; let mut aiocb = AioCb { last_aio, + direct, + sector_size: SECTOR_SIZE, file_fd: disk.as_raw_fd(), opcode: OpCode::Noop, iovec: Vec::new(), @@ -548,9 +549,7 @@ impl ScsiRequest { if self.cmd.command == SYNCHRONIZE_CACHE { aiocb.opcode = OpCode::Fdsync; - (*aio) - .as_mut() - .flush_sync(aiocb) + aio.submit_request(aiocb) .with_context(|| "Failed to process scsi request for flushing")?; return Ok(0); } @@ -558,33 +557,13 @@ impl ScsiRequest { match self.cmd.mode { ScsiXferMode::ScsiXferFromDev => { aiocb.opcode = OpCode::Preadv; - if aio_type.is_some() { - (*aio) - .as_mut() - .rw_aio(aiocb, SECTOR_SIZE, direct) - .with_context(|| { - "Failed to process scsi request for reading asynchronously" - })?; - } else { - (*aio).as_mut().rw_sync(aiocb).with_context(|| { - "Failed to process scsi request for reading synchronously" - })?; - } + aio.submit_request(aiocb) + .with_context(|| "Failed to process scsi request for reading")?; } ScsiXferMode::ScsiXferToDev => { aiocb.opcode = OpCode::Pwritev; - if aio_type.is_some() { - (*aio) - .as_mut() - .rw_aio(aiocb, SECTOR_SIZE, direct) - .with_context(|| { - "Failed to process block request for writing asynchronously" - })?; - } else { - (*aio).as_mut().rw_sync(aiocb).with_context(|| { - "Failed to process block request for writing synchronously" - })?; - } + aio.submit_request(aiocb) + .with_context(|| "Failed to process block request for writing")?; } _ => { info!("xfer none"); diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 228a9154a..2be65ce50 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -38,7 +38,7 @@ use machine_manager::{ config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; -use util::aio::{Aio, AioCb, Iovec}; +use util::aio::{Aio, AioCb, AioEngine, Iovec}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -818,7 +818,7 @@ impl EventNotifierHelper for ScsiCmdHandler { return None; } if let Some(aio) = &mut h_lock.aio { - if let Err(ref e) = aio.handle() { + if let Err(ref e) = aio.handle_complete() { error!("Failed to handle aio, {:?}", e); report_virtio_error( h_lock.interrupt_cb.clone(), @@ -921,7 +921,6 @@ impl ScsiCmdHandler { // If found device's lun id is not equal to request lun id, this request is a target request. scsi_req.emulate_execute(scsicompletecb, req_lun_id, lun)?; } else { - let aio_type = scsi_device_lock.config.aio_type.clone(); let direct = scsi_device_lock.config.direct; let disk_img = scsi_device_lock.disk_image.as_ref().unwrap().clone(); drop(scsi_device_lock); @@ -931,7 +930,7 @@ impl ScsiCmdHandler { Arc::new(Mutex::new(scsi_req.clone())), ); if let Some(ref mut aio) = self.aio { - scsi_req.execute(aio, &disk_img, direct, aio_type, true, scsicompletecb)?; + scsi_req.execute(aio, &disk_img, direct, true, scsicompletecb)?; } } } @@ -957,7 +956,10 @@ impl ScsiCmdHandler { } fn build_aio(&self) -> Result>> { - Ok(Box::new(Aio::new(Arc::new(Self::complete_func), None)?)) + Ok(Box::new(Aio::new( + Arc::new(Self::complete_func), + AioEngine::Off, + )?)) } } -- Gitee From 699989bdd9e6d1570cde7bd6554bdd8eec629d84 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 18:00:11 +0800 Subject: [PATCH 0714/2187] virtio-blk: Log err when iopoll handler failed The ok method will ignore err, which is not friendly for debug. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index d5a70022a..789811b06 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -754,15 +754,19 @@ impl EventNotifierHelper for BlockIoHandler { if h_lock.device_broken.load(Ordering::SeqCst) { return None; } - let done = h_lock - .process_queue() - .with_context(|| "Failed to handle block IO") - .ok()?; - if done { - Some(Vec::new()) - } else { - None - } + return match h_lock.process_queue() { + Ok(done) => { + if done { + Some(Vec::new()) + } else { + None + } + } + Err(e) => { + error!("Failed to handle block IO {:?}", e); + None + } + }; }); notifiers.push(build_event_notifier( handler_raw.queue_evt.as_raw_fd(), @@ -812,15 +816,19 @@ impl EventNotifierHelper for BlockIoHandler { if h_lock.aio.get_engine() == AioEngine::Off { return None; } - let done = h_lock - .aio_complete_handler() - .with_context(|| "Failed to handle aio") - .ok()?; - if done { - Some(Vec::new()) - } else { - None - } + return match h_lock.aio_complete_handler() { + Ok(done) => { + if done { + Some(Vec::new()) + } else { + None + } + } + Err(e) => { + error!("Failed to handle aio {:?}", e); + None + } + }; }); notifiers.push(build_event_notifier( handler_raw.aio.fd.as_raw_fd(), -- Gitee From c58560e78273722659929595c45c0c75465f08aa Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 16 Jan 2023 10:45:08 +0800 Subject: [PATCH 0715/2187] Balloon: fix read_config overflow The read_config function may overflow if the offset is big enough. Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 47 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 25bcf1e32..0e00b03af 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -922,7 +922,7 @@ impl VirtioDevice for Balloon { let config_len = size_of::() as u64; let data_len = data.len() as u64; - if offset + data_len > config_len { + if offset >= config_len { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } @@ -1190,13 +1190,50 @@ mod tests { let mem_space = address_space_init(); let balloon = Balloon::new(&bln_cfg, mem_space, false); - let write_data = [0, 0, 0, 0, 1, 0, 0, 0]; - let mut random_data: Vec = vec![0; 8]; + let ret_data = [0, 0, 0, 0, 1, 0, 0, 0]; + let mut read_data: Vec = vec![0; 8]; let addr = 0x00; assert_eq!(balloon.get_balloon_memory_size(), 0); balloon.actual.store(1, Ordering::Release); - balloon.read_config(addr, &mut random_data).unwrap(); - assert_eq!(random_data, write_data); + balloon.read_config(addr, &mut read_data).unwrap(); + assert_eq!(read_data, ret_data); + } + + #[test] + fn test_read_config_partial() { + let bln_cfg = BalloonConfig { + id: "bln".to_string(), + deflate_on_oom: true, + free_page_reporting: Default::default(), + }; + + let mem_space = address_space_init(); + let balloon = Balloon::new(&bln_cfg, mem_space, false); + let ret_data = [1, 0, 0, 0, 0, 0, 0, 0]; + let mut read_data: Vec = vec![0; 8]; + let addr = 0x4; + assert_eq!(balloon.get_balloon_memory_size(), 0); + balloon.actual.store(1, Ordering::Release); + balloon.read_config(addr, &mut read_data).unwrap(); + assert_eq!(read_data, ret_data); + } + + #[test] + fn test_read_config_overflow() { + let bln_cfg = BalloonConfig { + id: "bln".to_string(), + deflate_on_oom: true, + free_page_reporting: Default::default(), + }; + + let mem_space = address_space_init(); + let balloon = Balloon::new(&bln_cfg, mem_space, false); + let mut read_data: Vec = vec![0; 8]; + let addr: u64 = 0xffff_ffff_ffff_ffff; + assert_eq!(balloon.get_balloon_memory_size(), 0); + balloon.actual.store(1, Ordering::Release); + let ret = balloon.read_config(addr, &mut read_data); + assert!(ret.is_err()); } #[test] -- Gitee From de3f5e0b465fca16a12aff56e6db22fbbc4f5006 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 22:52:24 +0800 Subject: [PATCH 0716/2187] virtio-blk: Do not register handler of disabled queue .. which also speed up the deactivation process. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 789811b06..23fefc54f 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -1108,20 +1108,26 @@ impl VirtioDevice for Block { let mut senders = Vec::new(); for queue in queues.iter() { + let queue_evt = queue_evts.remove(0); + if !queue.lock().unwrap().is_enabled() { + continue; + } let (sender, receiver) = channel(); senders.push(sender); - let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); - let engine = self.blk_cfg.aio; + let aio = Box::new(Aio::new( + Arc::new(BlockIoHandler::complete_func), + self.blk_cfg.aio, + )?); let handler = BlockIoHandler { queue: queue.clone(), - queue_evt: queue_evts.remove(0), + queue_evt, mem_space: mem_space.clone(), disk_image: self.disk_image.clone(), disk_sectors: self.disk_sectors, direct: self.blk_cfg.direct, serial_num: self.blk_cfg.serial_num.clone(), - aio: Box::new(Aio::new(Arc::new(BlockIoHandler::complete_func), engine)?), + aio, driver_features: self.state.driver_features, receiver, update_evt: update_evt.clone(), -- Gitee From 2c5acf0c46b2dc4de6b9b64fa8b8bf64d7837bf7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 22:59:39 +0800 Subject: [PATCH 0717/2187] virtio-blk: Change type of senders as Vec The senders and update_evts are partners to realize block hotplug, thus should make them to be same type. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 48 ++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 23fefc54f..a75ac54e3 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -923,7 +923,7 @@ pub struct Block { /// Callback to trigger interrupt. interrupt_cb: Option>, /// The sending half of Rust's channel to send the image file. - senders: Option>>, + senders: Vec>, /// Eventfd for config space update. update_evts: Vec>, /// Eventfd for device deactivate. @@ -942,7 +942,7 @@ impl Default for Block { disk_sectors: 0, state: BlockState::default(), interrupt_cb: None, - senders: None, + senders: Vec::new(), update_evts: Vec::new(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), @@ -962,7 +962,7 @@ impl Block { disk_sectors: 0, state: BlockState::default(), interrupt_cb: None, - senders: None, + senders: Vec::new(), update_evts: Vec::new(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), @@ -1105,15 +1105,12 @@ impl VirtioDevice for Block { mut queue_evts: Vec>, ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); - let mut senders = Vec::new(); - for queue in queues.iter() { let queue_evt = queue_evts.remove(0); if !queue.lock().unwrap().is_enabled() { continue; } let (sender, receiver) = channel(); - senders.push(sender); let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); let aio = Box::new(Aio::new( Arc::new(BlockIoHandler::complete_func), @@ -1147,9 +1144,8 @@ impl VirtioDevice for Block { &mut self.deactivate_evts, )?; self.update_evts.push(update_evt); + self.senders.push(sender); } - - self.senders = Some(senders); self.broken.store(false, Ordering::SeqCst); Ok(()) @@ -1158,6 +1154,7 @@ impl VirtioDevice for Block { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(self.blk_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; self.update_evts.clear(); + self.senders.clear(); Ok(()) } @@ -1176,24 +1173,21 @@ impl VirtioDevice for Block { self.realize()?; - if let Some(senders) = &self.senders { - for sender in senders { - sender - .send(( - self.disk_image.clone(), - self.disk_sectors, - self.blk_cfg.serial_num.clone(), - self.blk_cfg.direct, - self.blk_cfg.aio, - )) - .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; - } - - for update_evt in &self.update_evts { - update_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; - } + for sender in &self.senders { + sender + .send(( + self.disk_image.clone(), + self.disk_sectors, + self.blk_cfg.serial_num.clone(), + self.blk_cfg.direct, + self.blk_cfg.aio, + )) + .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; + } + for update_evt in &self.update_evts { + update_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; } Ok(()) @@ -1286,7 +1280,7 @@ mod tests { assert_eq!(block.state.config_space.as_bytes().len(), CONFIG_SPACE_SIZE); assert!(block.disk_image.is_none()); assert!(block.interrupt_cb.is_none()); - assert!(block.senders.is_none()); + assert!(block.senders.is_empty()); // Realize block device: create TempFile as backing file. block.blk_cfg.read_only = true; -- Gitee From a74def309fe94340694325a701e596e88dbfebeb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 16 Jan 2023 23:40:13 +0800 Subject: [PATCH 0718/2187] eventloop: Fix possible infinite gc process The gc process holds and release lock periodically to avoid holding lock for long time. It has risk of infinite loop if other thread continously adds elem to gc. This fixes it. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 34c3cbd23..7954ebc85 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -255,12 +255,19 @@ impl EventLoopContext { } fn clear_gc(&mut self) { + let max_cnt = self.gc.write().unwrap().len(); + let mut pop_cnt = 0; + loop { // Loop to avoid hold lock for long time. - let mut gc = self.gc.write().unwrap(); - if gc.pop().is_none() { + if pop_cnt >= max_cnt { break; } + // SAFETY: We will stop removing when reach max_cnt and no other place + // removes element of gc. This is to avoid infinite poping if other + // thread continuously adds element to gc. + self.gc.write().unwrap().remove(0); + pop_cnt += 1; } } -- Gitee From e83211bc8104c41538fb6012fb7f93fdf57bde6f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 17 Jan 2023 11:00:03 +0800 Subject: [PATCH 0719/2187] virtio: deactivate device before reset it Before reset the device, it should deactivate the device to delete the notifier event. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 3 ++- virtio/src/virtio_pci.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9f00db59a..6927895bc 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -346,7 +346,8 @@ pub trait VirtioDevice: Send { ); } - /// Reset virtio device. + /// Reset virtio device, used to do some special reset action for + /// different device. fn reset(&mut self) -> Result<()> { Ok(()) } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 5e2512b5a..a8000f275 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -1253,13 +1253,13 @@ impl PciDevOps for VirtioPciDevice { } fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { + self.deactivate_device(); self.device .lock() .unwrap() .reset() .with_context(|| "Failed to reset virtio device")?; self.common_config.lock().unwrap().reset(); - self.deactivate_device(); Ok(()) } -- Gitee From 647068941c3194b88074ebec569c06d53d800ec8 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 17 Jan 2023 19:18:07 +0800 Subject: [PATCH 0720/2187] Virtio-console: Delete reset() method of virtio-console VirtioPciDevice will call deactivate() method when it resets. So reset() method is unnecessary. Delete it. Signed-off-by: Jinhao Gao --- virtio/src/console.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/virtio/src/console.rs b/virtio/src/console.rs index cbcf80138..cf3d57986 100644 --- a/virtio/src/console.rs +++ b/virtio/src/console.rs @@ -380,10 +380,6 @@ impl VirtioDevice for Console { self.chardev.lock().unwrap().deactivated = true; unregister_event_helper(None, &mut self.deactivate_evts) } - - fn reset(&mut self) -> Result<()> { - self.deactivate() - } } impl StateTransfer for Console { -- Gitee From c2fc04bcdc23ee1902eba3472ea5431c2209b27f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 16 Jan 2023 21:34:08 +0800 Subject: [PATCH 0721/2187] virtio-net: fix arithmetical overflow problems Fix some potential problems of arithmetical overflow. Signed-off-by: Yan Wang --- machine_manager/src/config/network.rs | 25 ++++++++++++++++++------- virtio/src/net.rs | 26 ++++++++++++++++++++------ virtio/src/virtqueue/mod.rs | 6 +++--- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index ded616421..ce8562e64 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -212,9 +212,8 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { net.ifname = ifname; } if let Some(queue_pairs) = cmd_parser.get_value::("queues")? { - let queues = queue_pairs * 2; - - if !is_netdev_queues_valid(queues) { + let queues = queue_pairs.checked_mul(2); + if queues.is_none() || !is_netdev_queues_valid(queues.unwrap()) { return Err(anyhow!(ConfigError::IllegalValue( "number queues of net device".to_string(), 1, @@ -224,7 +223,7 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { ))); } - net.queues = queues; + net.queues = queues.unwrap(); } if let Some(tap_fd) = parse_fds(&cmd_parser, "fd")? { @@ -233,7 +232,10 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { net.tap_fds = Some(tap_fds); } if let Some(fds) = &net.tap_fds { - let fds_num = (fds.len() * 2) as u16; + let fds_num = + fds.len() + .checked_mul(2) + .ok_or_else(|| anyhow!("Invalid fds number {}", fds.len()))? as u16; if fds_num > net.queues { net.queues = fds_num; } @@ -255,7 +257,11 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { net.vhost_fds = Some(vhost_fds); } if let Some(fds) = &net.vhost_fds { - let fds_num = (fds.len() * 2) as u16; + let fds_num = fds + .len() + .checked_mul(2) + .ok_or_else(|| anyhow!("Invalid vhostfds number {}", fds.len()))? + as u16; if fds_num > net.queues { net.queues = fds_num; } @@ -331,13 +337,18 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result) -> Result { + let queues = args + .queues + .unwrap_or(1) + .checked_mul(2) + .ok_or_else(|| anyhow!("Invalid 'queues' value"))?; let mut config = NetDevcfg { id: args.id, tap_fds: None, vhost_type: None, vhost_fds: None, ifname: String::new(), - queues: args.queues.unwrap_or(1) * 2, + queues, chardev: args.chardev, }; diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 5439690ed..2fed13feb 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -236,15 +236,23 @@ impl CtrlInfo { continue; } - let mut macs = vec![0_u8; entries as usize * MAC_ADDR_LEN]; - *data_iovec = get_buf_and_discard(mem_space, data_iovec, &mut macs) - .with_context(|| "Failed to get multicast MAC entries".to_string())?; + let size = entries as u64 * MAC_ADDR_LEN as u64; + if size > Element::iovec_size(data_iovec) { + bail!("Invalid request for setting mac table."); + } if entries as usize > CTRL_MAC_TABLE_LEN - mac_table_len { + *data_iovec = iov_discard_front(data_iovec, size) + .with_context(|| "Failed to discard iovec from front side".to_string())? + .to_vec(); *overflow = true; mac_table.clear(); continue; } + let mut macs = vec![0_u8; size as usize]; + *data_iovec = get_buf_and_discard(mem_space, data_iovec, &mut macs) + .with_context(|| "Failed to get multicast MAC entries".to_string())?; + mac_table.clear(); for i in 0..entries { let offset = i as usize * MAC_ADDR_LEN; @@ -486,8 +494,8 @@ impl NetCtrlHandler { // Validate the control request. let in_size = Element::iovec_size(&elem.in_iovec); let out_size = Element::iovec_size(&elem.out_iovec); - if in_size < mem::size_of_val(&ack) as u32 - || out_size < mem::size_of::() as u32 + if in_size < mem::size_of_val(&ack) as u64 + || out_size < mem::size_of::() as u64 { bail!( "Invalid length, in_iovec size is {}, out_iovec size is {}", @@ -940,7 +948,10 @@ fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { let mut end: usize = 0; for elem in iovec { - end = cmp::min(start + elem.iov_len, buf.len()); + end = start + .checked_add(elem.iov_len) + .ok_or_else(|| anyhow!("Overflow when getting the net header"))?; + end = cmp::min(end, buf.len()); mem_to_buf(&mut buf[start..end], elem.iov_base as u64)?; if end >= buf.len() { break; @@ -1487,6 +1498,9 @@ impl VirtioDevice for Net { mut queue_evts: Vec, ) -> Result<()> { let queue_num = queues.len(); + if queue_num == 0 { + bail!("Length of queues is 0 when activating virtio net"); + } let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); self.ctrl_info = Some(ctrl_info.clone()); let driver_features = self.state.lock().unwrap().driver_features; diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs index ca1b12587..94dacb28b 100644 --- a/virtio/src/virtqueue/mod.rs +++ b/virtio/src/virtqueue/mod.rs @@ -91,10 +91,10 @@ impl Element { } } - pub fn iovec_size(iovec: &[ElemIovec]) -> u32 { - let mut size: u32 = 0; + pub fn iovec_size(iovec: &[ElemIovec]) -> u64 { + let mut size: u64 = 0; for elem in iovec.iter() { - size += elem.len; + size += elem.len as u64; } size } -- Gitee From ce59feb79c9f671fbfd0b7c408cb60d4bb848e56 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 16 Jan 2023 21:38:32 +0800 Subject: [PATCH 0722/2187] vhost-user-blk: fix arithmetical overflow problems Fix some potential problems of arithmetical overflow. Signed-off-by: Yan Wang --- virtio/src/vhost/user/client.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 5b2a60cd4..2c8e3feb6 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -179,9 +179,18 @@ impl VhostUserMemInfo { fn addr_to_host(&self, addr: GuestAddress) -> Option { let addr = addr.raw_value(); for reg_info in self.regions.lock().unwrap().iter() { - if addr >= reg_info.region.guest_phys_addr - && addr < reg_info.region.guest_phys_addr + reg_info.region.memory_size - { + let gpa_end = reg_info + .region + .guest_phys_addr + .checked_add(reg_info.region.memory_size) + .ok_or_else(|| { + anyhow!( + "Overflow when adding gpa with memory_size in region {:x?}", + reg_info.region + ) + }) + .ok()?; + if addr >= reg_info.region.guest_phys_addr && addr < gpa_end { let offset = addr - reg_info.region.guest_phys_addr; return Some(reg_info.region.userspace_addr + offset); } -- Gitee From 42fe7ebf0ba3d18de124567220a1f6ab89aeb349 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 18 Jan 2023 14:20:37 +0800 Subject: [PATCH 0723/2187] vhost-user-blk: optmize some locks Two methods to narrow the locking range: 1. Drop lock directly after used. 2. Using the member directly which has been locked. Signed-off-by: Yan Wang --- virtio/src/vhost/user/block.rs | 23 ++++++-------------- virtio/src/vhost/user/client.rs | 38 +++++++++++++++++---------------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index d04c39d72..32b1cb33e 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -92,31 +92,23 @@ impl Block { /// Negotiate features with spdk. fn negotiate_features(&mut self) -> Result<()> { - let client = self.client.as_ref().unwrap(); - let features = client - .lock() - .unwrap() + let locked_client = self.client.as_ref().unwrap().lock().unwrap(); + let features = locked_client .get_features() .with_context(|| "Failed to get features for vhost-user blk")?; if virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES) { - let protocol_features = client - .lock() - .unwrap() + let protocol_features = locked_client .get_protocol_features() .with_context(|| "Failed to get protocol features for vhost-user blk")?; let supported_protocol_features = 1 << VHOST_USER_PROTOCOL_F_MQ | 1 << VHOST_USER_PROTOCOL_F_CONFIG; - client - .lock() - .unwrap() + locked_client .set_protocol_features(supported_protocol_features & protocol_features) .with_context(|| "Failed to set protocol features for vhost-user blk")?; if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_CONFIG as u32) { - let config = client - .lock() - .unwrap() + let config = locked_client .get_virtio_blk_config() .with_context(|| "Failed to get config for vhost-user blk")?; self.state.config_space = config; @@ -128,9 +120,7 @@ impl Block { } if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_MQ as u32) { - let max_queue_num = client - .lock() - .unwrap() + let max_queue_num = locked_client .get_max_queue_num() .with_context(|| "Failed to get queue num for vhost-user blk")?; if self.queue_num() > max_queue_num as usize { @@ -150,6 +140,7 @@ impl Block { ); } } + drop(locked_client); self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_BLK_F_SIZE_MAX diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 6e9e5754c..e1a0d2011 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -379,8 +379,7 @@ impl VhostUserClient { // Set all vring num to notify ovs/dpdk how many queues it needs to poll // before setting vring info. for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { - let queue = queue_mutex.lock().unwrap(); - let actual_size = queue.vring.actual_size(); + let actual_size = queue_mutex.lock().unwrap().vring.actual_size(); self.set_vring_num(queue_index, actual_size) .with_context(|| { format!( @@ -391,9 +390,7 @@ impl VhostUserClient { } for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { - let queue = queue_mutex.lock().unwrap(); - let queue_config = queue.vring.get_queue_config(); - + let queue_config = queue_mutex.lock().unwrap().vring.get_queue_config(); self.set_vring_addr(&queue_config, queue_index, 0) .with_context(|| { format!( @@ -468,11 +465,11 @@ impl VhostUserClient { /// Send get protocol features request to vhost. pub fn get_protocol_features(&self) -> Result { - let client = self.client.lock().unwrap(); let request = VhostUserMsgReq::GetProtocolFeatures as u32; let hdr = VhostUserMsgHdr::new(request, VhostUserHdrFlag::NeedReply as u32, 0); let body_opt: Option<&u32> = None; let payload_opt: Option<&[u8]> = None; + let client = self.client.lock().unwrap(); client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) @@ -486,10 +483,11 @@ impl VhostUserClient { /// Send u64 value to vhost. fn set_value(&self, request: VhostUserMsgReq, value: u64) -> Result<()> { - let client = self.client.lock().unwrap(); let hdr = VhostUserMsgHdr::new(request as u32, 0, size_of::() as u32); let payload_opt: Option<&[u8]> = None; - client + self.client + .lock() + .unwrap() .sock .send_msg(Some(&hdr), Some(&value), payload_opt, &[]) .with_context(|| "Failed to send msg for setting value")?; @@ -504,7 +502,6 @@ impl VhostUserClient { /// Get virtio blk config from vhost. pub fn get_virtio_blk_config(&self) -> Result { - let client = self.client.lock().unwrap(); let request = VhostUserMsgReq::GetConfig as u32; let config_len = size_of::>(); let hdr = VhostUserMsgHdr::new( @@ -520,6 +517,7 @@ impl VhostUserClient { config_len, ) }); + let client = self.client.lock().unwrap(); client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) @@ -547,11 +545,11 @@ impl VhostUserClient { /// Get max queues number that vhost supports. pub fn get_max_queue_num(&self) -> Result { - let client = self.client.lock().unwrap(); let request = VhostUserMsgReq::GetQueueNum as u32; let hdr = VhostUserMsgHdr::new(request, VhostUserHdrFlag::NeedReply as u32, 0); let body_opt: Option<&u32> = None; let payload_opt: Option<&[u8]> = None; + let client = self.client.lock().unwrap(); client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) @@ -565,11 +563,12 @@ impl VhostUserClient { impl VhostOps for VhostUserClient { fn set_owner(&self) -> Result<()> { - let client = self.client.lock().unwrap(); let hdr = VhostUserMsgHdr::new(VhostUserMsgReq::SetOwner as u32, 0, 0); let body_opt: Option<&u32> = None; let payload_opt: Option<&[u8]> = None; - client + self.client + .lock() + .unwrap() .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) .with_context(|| "Failed to send msg for setting owner")?; @@ -578,11 +577,11 @@ impl VhostOps for VhostUserClient { } fn get_features(&self) -> Result { - let client = self.client.lock().unwrap(); let request = VhostUserMsgReq::GetFeatures as u32; let hdr = VhostUserMsgHdr::new(request, VhostUserHdrFlag::NeedReply as u32, 0); let body_opt: Option<&u32> = None; let payload_opt: Option<&[u8]> = None; + let client = self.client.lock().unwrap(); client .sock .send_msg(Some(&hdr), body_opt, payload_opt, &[]) @@ -611,12 +610,14 @@ impl VhostOps for VhostUserClient { memcontext.region_add(region_info.region); fds.push(region_info.file_back.file.as_raw_fd()); } + drop(mem_regions); - let client = self.client.lock().unwrap(); let len = size_of::() + num_region * size_of::(); let hdr = VhostUserMsgHdr::new(VhostUserMsgReq::SetMemTable as u32, 0, len as u32); let memhdr = VhostUserMemHdr::new(num_region as u32, 0); - client + self.client + .lock() + .unwrap() .sock .send_msg( Some(&hdr), @@ -655,7 +656,6 @@ impl VhostOps for VhostUserClient { } fn set_vring_addr(&self, queue: &QueueConfig, index: usize, flags: u32) -> Result<()> { - let client = self.client.lock().unwrap(); let hdr = VhostUserMsgHdr::new( VhostUserMsgReq::SetVringAddr as u32, 0, @@ -694,7 +694,9 @@ impl VhostOps for VhostUserClient { avail_user_addr, log_guest_addr: 0_u64, }; - client + self.client + .lock() + .unwrap() .sock .send_msg(Some(&hdr), Some(&_vring_addr), payload_opt, &[]) .with_context(|| "Failed to send msg for setting vring addr")?; @@ -805,7 +807,6 @@ impl VhostOps for VhostUserClient { } fn get_vring_base(&self, queue_idx: usize) -> Result { - let client = self.client.lock().unwrap(); let request = VhostUserMsgReq::GetVringBase as u32; let hdr = VhostUserMsgHdr::new( request, @@ -815,6 +816,7 @@ impl VhostOps for VhostUserClient { let vring_state = VhostUserVringState::new(queue_idx as u32, 0_u32); let payload_opt: Option<&[u8]> = None; + let client = self.client.lock().unwrap(); client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) -- Gitee From b3491c62f72b725762e22f3b1de9aa5f31ed8872 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 14 Jan 2023 14:00:47 +0800 Subject: [PATCH 0724/2187] VNC: Frame buffer update The frame buffer update request from the client is Force. Even if the corresponding metadata does not change, it needs to be synchronized with the client Signed-off-by: Xiao Ye --- vnc/src/client.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 99eba0e29..592bde9a8 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -254,6 +254,19 @@ impl ConnState { self.dis_conn } + /// Whether the client's image data needs to be updated. + pub fn is_need_update(&mut self, dirty_num: i32) -> bool { + if self.is_disconnect() { + return false; + } + + match self.update_state { + UpdateState::No => false, + UpdateState::Incremental => dirty_num > 0, + UpdateState::Force => true, + } + } + pub fn has_feature(&self, feature: VncFeatures) -> bool { self.feature & (1 << feature as usize) != 0 } @@ -1076,7 +1089,7 @@ impl EventNotifierHelper for ClientIoHandler { /// Generate the data that needs to be sent. /// Add to send queue pub fn get_rects(client: &Arc, server: &Arc, dirty_num: i32) -> i32 { - if !is_need_update(client) || dirty_num == 0 { + if !client.conn_state.lock().unwrap().is_need_update(dirty_num) { return 0; } @@ -1171,21 +1184,6 @@ fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> R Ok(()) } -/// Whether the client's image data needs to be updated. -pub fn is_need_update(client: &Arc) -> bool { - match client.conn_state.lock().unwrap().update_state { - UpdateState::No => false, - UpdateState::Incremental => { - // throttle_output_offset - true - } - UpdateState::Force => { - // force_update_offset - true - } - } -} - /// Set pixformat for client. pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { let mut locked_dpm = client.client_dpm.lock().unwrap(); -- Gitee From e9f739056a69c759ec7137bd41d41d5cf0db0e5d Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 14 Jan 2023 11:58:51 +0800 Subject: [PATCH 0725/2187] VNC: Modify code style. Modify code style Signed-off-by: Xiao Ye --- vnc/src/vnc.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 3583637b2..3b55bf6f9 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -242,12 +242,10 @@ impl DisplayChangeListenerOperations for VncInterface { /// /// * `VncConfig` `object`- vnc related parameters pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { - let vnc_cfg; - if let Some(v) = vnc { - vnc_cfg = v; - } else { - return Ok(()); - } + let vnc_cfg = match vnc { + Some(cfg) => cfg, + None => return Ok(()), + }; let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); let listener: TcpListener = match TcpListener::bind(addr.as_str()) { -- Gitee From 948453b6f1e2c7f03847aa5e067ef475fd1db667 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Thu, 19 Jan 2023 16:13:33 +0800 Subject: [PATCH 0726/2187] vhost-user-blk-pci: make sure spdk support `VHOST_USER_F_PROTOCOL_FEATURES` Exit if spdk don't support `VHOST_USER_F_PROTOCOL_FEATURES`. Signed-off-by: Xiaowei Xue --- virtio/src/vhost/user/block.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 32b1cb33e..e2da45506 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -139,6 +139,8 @@ impl Block { protocol_features ); } + } else { + bail!("Bad spdk feature: {:#b}", features); } drop(locked_client); -- Gitee From c4210f122523a23fca09b20312ec4785e8ca57cc Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Fri, 20 Jan 2023 09:45:38 +0800 Subject: [PATCH 0727/2187] vhost-user: modify error prompt message Change vhost-user-net to vhost-user Signed-off-by: Xiaowei Xue --- virtio/src/vhost/user/client.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index e1a0d2011..e7b7aac0a 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -364,13 +364,13 @@ impl VhostUserClient { /// Activate device by vhost-user protocol. pub fn activate_vhost_user(&mut self) -> Result<()> { self.set_owner() - .with_context(|| "Failed to set owner for vhost-user net")?; + .with_context(|| "Failed to set owner for vhost-user")?; self.set_features(self.features) - .with_context(|| "Failed to set features for vhost-user net")?; + .with_context(|| "Failed to set features for vhost-user")?; self.set_mem_table() - .with_context(|| "Failed to set mem table for vhost-user net")?; + .with_context(|| "Failed to set mem table for vhost-user")?; let mut queue_num = self.queues.len(); if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { @@ -383,7 +383,7 @@ impl VhostUserClient { self.set_vring_num(queue_index, actual_size) .with_context(|| { format!( - "Failed to set vring num for vhost-user net, index: {}, size: {}", + "Failed to set vring num for vhost-user, index: {}, size: {}", queue_index, actual_size, ) })?; @@ -394,7 +394,7 @@ impl VhostUserClient { self.set_vring_addr(&queue_config, queue_index, 0) .with_context(|| { format!( - "Failed to set vring addr for vhost-user net, index: {}", + "Failed to set vring addr for vhost-user, index: {}", queue_index, ) })?; @@ -407,14 +407,14 @@ impl VhostUserClient { self.set_vring_kick(queue_index, self.queue_evts[queue_index].clone()) .with_context(|| { format!( - "Failed to set vring kick for vhost-user net, index: {}", + "Failed to set vring kick for vhost-user, index: {}", queue_index, ) })?; self.set_vring_call(queue_index, self.call_events[queue_index].clone()) .with_context(|| { format!( - "Failed to set vring call for vhost-user net, index: {}", + "Failed to set vring call for vhost-user, index: {}", queue_index, ) })?; @@ -423,7 +423,7 @@ impl VhostUserClient { for (queue_index, _) in self.queues.iter().enumerate().take(queue_num) { self.set_vring_enable(queue_index, true).with_context(|| { format!( - "Failed to set vring enable for vhost-user net, index: {}", + "Failed to set vring enable for vhost-user, index: {}", queue_index, ) })?; -- Gitee From bc1690e345d5bcb9404febaa12d03a6c92cf8146 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 19 Jan 2023 21:08:00 +0800 Subject: [PATCH 0728/2187] CPU: bugfix some unreasonable unwrap operations Some unwraps can directly cause the process to panic, so avoid them. Signed-off-by: Mingwang Li --- cpu/src/aarch64/mod.rs | 2 +- cpu/src/x86_64/caps.rs | 5 ++-- cpu/src/x86_64/mod.rs | 19 +++++++-------- machine/src/lib.rs | 10 +++++--- machine/src/micro_vm/mod.rs | 6 ++++- machine/src/standard_vm/aarch64/mod.rs | 17 ++++++++++++-- machine/src/standard_vm/mod.rs | 5 ++-- machine/src/standard_vm/x86_64/ich9_lpc.rs | 27 ++++++++++++++-------- machine/src/standard_vm/x86_64/mod.rs | 7 ++++-- 9 files changed, 67 insertions(+), 31 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 427c080f3..92e4d3a46 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -308,7 +308,7 @@ impl StateTransfer for CPU { cpu_state_locked.mp_state = mp_state; } - let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES).unwrap(); + let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES)?; self.fd.get_reg_list(&mut cpreg_list)?; cpu_state_locked.cpreg_len = 0; for (index, cpreg) in cpreg_list.as_slice().iter().enumerate() { diff --git a/cpu/src/x86_64/caps.rs b/cpu/src/x86_64/caps.rs index 5f131389c..a69c4f5e9 100644 --- a/cpu/src/x86_64/caps.rs +++ b/cpu/src/x86_64/caps.rs @@ -12,6 +12,7 @@ use kvm_bindings::{kvm_msr_entry, Msrs}; use kvm_ioctls::{Cap, Kvm}; +use vmm_sys_util::fam::Error; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/msr-index.h#L558 const MSR_IA32_MISC_ENABLE: ::std::os::raw::c_uint = 0x1a0; @@ -45,7 +46,7 @@ impl X86CPUCaps { } /// Create `Msrs` (a list of `kvm_msr_entry`) from capabilities supported_msrs. - pub fn create_msr_entries(&self) -> Msrs { + pub fn create_msr_entries(&self) -> Result { let entry_vec: Vec = self .supported_msrs .iter() @@ -65,6 +66,6 @@ impl X86CPUCaps { } }) .collect(); - Msrs::from_entries(&entry_vec).unwrap() + Msrs::from_entries(&entry_vec) } } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 257a0b04e..35f75867a 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -243,7 +243,7 @@ impl X86CPUState { .set_lapic(&self.lapic) .with_context(|| format!("Failed to set lapic for CPU {}", self.apic_id))?; vcpu_fd - .set_msrs(&Msrs::from_entries(&self.msr_list[0..self.msr_len]).unwrap()) + .set_msrs(&Msrs::from_entries(&self.msr_list[0..self.msr_len])?) .with_context(|| format!("Failed to set msrs for CPU {}", self.apic_id))?; vcpu_fd .set_vcpu_events(&self.cpu_events) @@ -392,9 +392,9 @@ impl X86CPUState { } } - fn adjust_cpuid(&self, cpuid: &mut CpuId) { + fn adjust_cpuid(&self, cpuid: &mut CpuId) -> Result<()> { if self.nr_dies < 2 { - return; + return Ok(()); } // Intel CPU topology with multi-dies support requies CPUID[0x1f]. @@ -402,21 +402,22 @@ impl X86CPUState { for entry in entries.iter_mut() { if entry.function == 0 { if entry.eax >= 0x1f { - return; + return Ok(()); } else { entry.eax = 0x1f; } break; } } - (0..4).for_each(|index| { + for index in 0..4 { let entry = kvm_cpuid_entry2 { function: 0x1f, index, ..Default::default() }; - cpuid.push(entry).unwrap(); - }); + cpuid.push(entry)?; + } + Ok(()) } fn setup_cpuid(&self, vcpu_fd: &Arc) -> Result<()> { @@ -432,7 +433,7 @@ impl X86CPUState { .with_context(|| { format!("Failed to get supported cpuid for CPU {}/KVM", self.apic_id) })?; - self.adjust_cpuid(&mut cpuid); + self.adjust_cpuid(&mut cpuid)?; let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { @@ -564,7 +565,7 @@ impl X86CPUState { impl StateTransfer for CPU { fn get_state_vec(&self) -> Result> { - let mut msr_entries = self.caps.create_msr_entries(); + let mut msr_entries = self.caps.create_msr_entries()?; let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); cpu_state_locked.mp_state = self.fd.get_mp_state()?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 65d950774..67ebc43b1 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -28,6 +28,7 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use log::warn; use util::file::{lock_file, unlock_file}; +use util::loop_context::read_fd; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub use micro_vm::LightMachine; @@ -543,7 +544,7 @@ pub trait MachineOps { } fn reset_fwcfg_boot_order(&mut self) -> Result<()> { - // unwrap is safe because stand machine always make sure it not return null. + // SAFETY: unwrap is safe because stand machine always make sure it not return null. let boot_order_vec = self.get_boot_order_list().unwrap(); let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone(); if locked_boot_order_vec.is_empty() { @@ -577,7 +578,7 @@ pub trait MachineOps { /// /// * `bootindex` - The boot index of the device. fn check_bootindex(&mut self, boot_index: u8) -> Result<()> { - // Unwrap is safe because StdMachine will overwrite this function, + // SAFETY: Unwrap is safe because StdMachine will overwrite this function, // which ensure boot_order_list is not None. let boot_order_list = self.get_boot_order_list().unwrap(); if boot_order_list @@ -600,6 +601,8 @@ pub trait MachineOps { /// * `dev_path` - The firmware device path of the device. /// * `dev_id` - The id of the device. fn add_bootindex_devices(&mut self, boot_index: u8, dev_path: &str, dev_id: &str) { + // SAFETY: Unwrap is safe because StdMachine will overwrite this function, + // which ensure boot_order_list is not None. let boot_order_list = self.get_boot_order_list().unwrap(); boot_order_list.lock().unwrap().push(BootIndexInfo { boot_index, @@ -1304,7 +1307,7 @@ pub trait MachineOps { fn register_power_event(&self, power_button: Arc) -> Result<()> { let button_fd = power_button.as_raw_fd(); let power_button_handler: Rc = Rc::new(move |_, _| { - let _ret = power_button.read().unwrap(); + read_fd(button_fd); None }); let notifier = EventNotifier::new( @@ -1452,6 +1455,7 @@ pub trait MachineOps { } #[cfg(target_arch = "aarch64")] + // SAFETY: ARM architecture must have interrupt controllers in user mode. irq_chip.as_ref().unwrap().stop(); *vm_state = KvmVmState::Paused; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 0fdbc7b93..9d0ac49e6 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -905,7 +905,10 @@ impl MachineLifecycle for LightMachine { return false; } - self.power_button.write(1).unwrap(); + if self.power_button.write(1).is_err() { + error!("Micro vm write power button failed"); + return false; + } true } @@ -1631,6 +1634,7 @@ impl device_tree::CompileFDT for LightMachine { self.generate_memory_node(fdt)?; self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; + // SAFETY: ARM architecture must have interrupt controllers in user mode. self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; fdt.end_node(node_dep)?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index e36a5255f..25846b64d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,6 +14,7 @@ mod pci_host_root; mod syscall; pub use crate::error::MachineError; +use log::error; use std::borrow::Borrow; use std::collections::HashMap; use std::mem::size_of; @@ -843,6 +844,7 @@ impl AcpiBuilder for StdMachine { gic_redist.base_addr = MEM_LAYOUT[LayoutEntryType::GicRedist as usize].0; gic_redist.length = 16; madt.append_child(&gic_redist.aml_bytes()); + // SAFETY: ARM architecture must have interrupt controllers in user mode. if self.irq_chip.as_ref().unwrap().get_redist_count() > 1 { gic_redist.range_length = MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].1 as u32; gic_redist.base_addr = MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].0; @@ -911,6 +913,7 @@ impl AcpiBuilder for StdMachine { srat.append_child(&[0_u8; 8_usize]); let mut next_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + // SAFETY: the SRAT table is created only when numa node configured. for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { self.build_srat_cpu(*id, node, &mut srat); next_base = self.build_srat_mem(next_base, *id, node, &mut srat); @@ -963,12 +966,18 @@ impl MachineLifecycle for StdMachine { return false; } - self.power_button.write(1).unwrap(); + if self.power_button.write(1).is_err() { + error!("ARM stdndard vm write power button failed"); + return false; + } true } fn reset(&mut self) -> bool { - self.reset_req.write(1).unwrap(); + if self.reset_req.write(1).is_err() { + error!("ARM standard vm write reset req failed"); + return false; + } true } @@ -1406,15 +1415,19 @@ impl CompileFDTHelper for StdMachine { let mut locked_dev = dev.lock().unwrap(); match locked_dev.get_type() { SysBusDevType::PL011 => { + // SAFETY: Legacy devices guarantee is not empty. generate_serial_device_node(fdt, locked_dev.get_sys_resource().unwrap())? } SysBusDevType::Rtc => { + // SAFETY: Legacy devices guarantee is not empty. generate_rtc_device_node(fdt, locked_dev.get_sys_resource().unwrap())? } SysBusDevType::VirtioMmio => { + // SAFETY: Legacy devices guarantee is not empty. generate_virtio_devices_node(fdt, locked_dev.get_sys_resource().unwrap())? } SysBusDevType::FwCfg => { + // SAFETY: Legacy devices guarantee is not empty. generate_fwcfg_device_node(fdt, locked_dev.get_sys_resource().unwrap())?; } _ => (), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index adeab0850..63667321f 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -23,7 +23,7 @@ pub use aarch64::StdMachine; use log::error; use machine_manager::event_loop::EventLoop; use util::aio::AIO_NATIVE; -use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; +use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] @@ -193,7 +193,7 @@ trait StdMachineOps: AcpiBuilder { ) -> MachineResult<()> { let reset_req_fd = reset_req.as_raw_fd(); let reset_req_handler: Rc = Rc::new(move |_, _| { - let _ret = reset_req.read().unwrap(); + read_fd(reset_req_fd); if let Err(e) = StdMachine::handle_reset_request(&clone_vm) { error!("Fail to reboot standard VM, {:?}", e); } @@ -822,6 +822,7 @@ impl StdMachine { let result = self.add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false); let pci_dev = if let Err(ref e) = result { + // SAFETY: unwrap is safe because Standard machine always make sure it not return null. self.get_scsi_cntlr_list() .unwrap() .lock() diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 3d5e7df08..de5cbd465 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -56,8 +56,8 @@ pub struct LPCBridge { } impl LPCBridge { - pub fn new(parent_bus: Weak>, sys_io: Arc) -> Self { - Self { + pub fn new(parent_bus: Weak>, sys_io: Arc) -> Result { + Ok(Self { config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), parent_bus, sys_io, @@ -65,9 +65,9 @@ impl LPCBridge { pm_evt: Arc::new(Mutex::new(AcpiPmEvent::new())), pm_ctrl: Arc::new(Mutex::new(AcpiPmCtrl::new())), rst_ctrl: Arc::new(AtomicU8::new(0)), - reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - shutdown_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - } + reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + shutdown_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + }) } fn update_pm_base(&self) -> Result<()> { @@ -117,7 +117,10 @@ impl LPCBridge { } }; if value & 0x4_u8 != 0 { - cloned_reset_fd.write(1).unwrap(); + if cloned_reset_fd.write(1).is_err() { + error!("X86 standard vm write reset fd failed"); + return false; + } return true; } cloned_rst_ctrl.store(value & 0xA, Ordering::SeqCst); @@ -144,7 +147,10 @@ impl LPCBridge { let cloned_shutdown_fd = self.shutdown_req.clone(); let write_ops = move |_data: &[u8], _addr: GuestAddress, _offset: u64| -> bool { - cloned_shutdown_fd.write(1).unwrap(); + if cloned_shutdown_fd.write(1).is_err() { + error!("X86 standard vm write shutdown fd failed"); + return false; + } true }; @@ -191,8 +197,11 @@ impl LPCBridge { let clone_pmctrl = self.pm_ctrl.clone(); let cloned_shutdown_fd = self.shutdown_req.clone(); let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { - if clone_pmctrl.lock().unwrap().write(data, addr, offset) { - cloned_shutdown_fd.write(1).unwrap(); + if clone_pmctrl.lock().unwrap().write(data, addr, offset) + && cloned_shutdown_fd.write(1).is_err() + { + error!("X86 standard vm write shutdown fd failed"); + return false; } true }; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 432c29631..910336b69 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -244,7 +244,7 @@ impl StdMachine { fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); - let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone()); + let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone())?; self.register_reset_event(ich.reset_req.clone(), vm) .with_context(|| "Fail to register reset event in LPC")?; self.register_acpi_shutdown_event(ich.shutdown_req.clone(), clone_vm) @@ -809,7 +809,10 @@ impl MachineLifecycle for StdMachine { return false; } - self.power_button.write(1).unwrap(); + if self.power_button.write(1).is_err() { + error!("X86 standard vm write power button failed"); + return false; + } true } -- Gitee From 0b8ca35b75bc5f5424648ca104ec612ceaf77fb2 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Fri, 20 Jan 2023 10:33:44 +0800 Subject: [PATCH 0729/2187] vhost-user: fix typo Change `VHOST_USER_GET_CONFIG` to `VHOST_USER_SET_CONFIG` Signed-off-by: Xiaowei Xue --- virtio/src/vhost/user/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index e7b7aac0a..0072a1c8a 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -40,7 +40,7 @@ use anyhow::{anyhow, bail, Context, Result}; /// Vhost supports multiple queue pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; -/// Vhost supports `VHOST_USER_GET_CONFIG` and `VHOST_USER_GET_CONFIG` msg. +/// Vhost supports `VHOST_USER_SET_CONFIG` and `VHOST_USER_GET_CONFIG` msg. pub const VHOST_USER_PROTOCOL_F_CONFIG: u8 = 9; struct ClientInternal { -- Gitee From 9505fbebe1d572407aca41f4742cb72d2abb3e08 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Fri, 20 Jan 2023 10:41:14 +0800 Subject: [PATCH 0730/2187] virtiofs: remove write_config The config of virtiofs should not be modified, and the modification logic is removed. Signed-off-by: jiewang-group Signed-off-by: Li huachao --- virtio/src/vhost/user/fs.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 89201a14b..aab233f80 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -197,19 +197,7 @@ impl VirtioDevice for Fs { Ok(()) } - fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let data_len = data.len(); - let config_slice = self.config.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset, - config_len as u64 - ))); - } - - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); - + fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { Ok(()) } -- Gitee From 505f118f6bf572032ef4774e339a0ccecc6c9171 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 19 Jan 2023 15:16:40 +0800 Subject: [PATCH 0731/2187] virtio-net: fix read/write configure of virtio-net 1. Driver may write config byte by byte. 2. Optimize code for read_config(). Signed-off-by: Yan Wang --- virtio/src/net.rs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 28e5c1b6d..80019b6db 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1461,12 +1461,15 @@ impl VirtioDevice for Net { let locked_state = self.state.lock().unwrap(); let config_slice = locked_state.config_space.as_bytes(); let config_len = config_slice.len() as u64; - if offset >= config_len { + if offset + .checked_add(data.len() as u64) + .filter(|&end| end <= config_len) + .is_none() + { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; - } + data.write_all(&config_slice[offset as usize..(offset as usize + data.len())])?; + Ok(()) } @@ -1476,11 +1479,21 @@ impl VirtioDevice for Net { let mut locked_state = self.state.lock().unwrap(); let driver_features = locked_state.driver_features; let config_slice = locked_state.config_space.as_mut_bytes(); + + if offset + .checked_add(data_len as u64) + .filter(|&end| end <= MAC_ADDR_LEN as u64) + .is_none() + { + return Err(anyhow!(VirtioError::DevConfigOverflow( + offset, + config_slice.len() as u64 + ))); + } + if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) - && offset == 0 - && data_len == MAC_ADDR_LEN - && *data != config_slice[0..data_len] + && *data != config_slice[offset as usize..(offset as usize + data_len)] { config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); } @@ -1703,7 +1716,7 @@ mod tests { net.write_config(0x00, &write_data).unwrap(); net.read_config(0x00, &mut random_data).unwrap(); - assert_ne!(random_data, write_data); + assert_eq!(random_data, write_data); net.write_config(0x00, &origin_data).unwrap(); @@ -1725,14 +1738,14 @@ mod tests { let offset: u64 = len; let mut data: Vec = vec![0; 1]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), true); + assert_eq!(net.write_config(offset, &mut data).is_ok(), false); let offset: u64 = len - 1; let mut data: Vec = vec![0; 1]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), true); + assert_eq!(net.write_config(offset, &mut data).is_ok(), false); let offset: u64 = 0; let mut data: Vec = vec![0; len as usize]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), true); + assert_eq!(net.write_config(offset, &mut data).is_ok(), false); } } -- Gitee From 89a4ed07cf702513e0752c22bcdcec4f8bc20f16 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 18 Jan 2023 15:54:11 +0800 Subject: [PATCH 0732/2187] Pci: Modify the reset method of some pci devices The reset methods of VirtioPciDevice and XhciPciDevice need to reset the common configuration fields, update bar region and reset msix. So fix them. Signed-off-by: Jinhao Gao --- pci/src/config.rs | 89 ++++++++++++++++++++++++++++++---------- usb/src/xhci/xhci_pci.rs | 3 ++ virtio/src/virtio_pci.rs | 4 +- 3 files changed, 73 insertions(+), 23 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 0f72a5e1d..89e6a605a 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -323,6 +323,8 @@ pub struct Bar { address: u64, pub size: u64, pub region: Option, + pub parent_io_region: Option>>, + pub parent_mem_region: Option>>, } /// Capbility ID defined by PCIe/PCI spec. @@ -400,6 +402,8 @@ impl PciConfig { address: 0, size: 0, region: None, + parent_io_region: None, + parent_mem_region: None, }); } @@ -615,6 +619,25 @@ impl PciConfig { Ok(()) } + /// General reset process for pci devices + pub fn reset(&mut self) -> Result<()> { + self.reset_common_regs()?; + + if let Err(e) = self.update_bar_mapping( + #[cfg(target_arch = "x86_64")] + None, + None, + ) { + error!("{:?}", e); + } + + if let Some(msix) = &self.msix { + msix.lock().unwrap().reset(); + } + + Ok(()) + } + /// Get base offset of the capability in PCIe/PCI configuration space. /// /// # Arguments @@ -791,6 +814,39 @@ impl PciConfig { continue; } + if self.bars[id].address != BAR_SPACE_UNMAPPED { + match self.bars[id].region_type { + #[cfg(target_arch = "x86_64")] + RegionType::Io => { + if self.bars[id].parent_io_region.is_some() { + self.bars[id] + .parent_io_region + .as_ref() + .unwrap() + .lock() + .unwrap() + .delete_subregion(self.bars[id].region.as_ref().unwrap()) + .with_context(|| { + format!("Failed to unmap BAR{} in I/O space.", id) + })?; + } + } + _ => { + if self.bars[id].parent_mem_region.is_some() { + self.bars[id] + .parent_mem_region + .as_ref() + .unwrap() + .lock() + .unwrap() + .delete_subregion(self.bars[id].region.as_ref().unwrap()) + .with_context(|| anyhow!(PciError::UnregMemBar(id)))? + } + } + } + self.bars[id].address = BAR_SPACE_UNMAPPED; + } + if self.is_bar_region_empty( id, #[cfg(target_arch = "x86_64")] @@ -800,36 +856,27 @@ impl PciConfig { return Ok(()); } - if self.bars[id].address != BAR_SPACE_UNMAPPED { - match self.bars[id].region_type { - RegionType::Io => { - #[cfg(target_arch = "x86_64")] - io_region - .unwrap() - .delete_subregion(self.bars[id].region.as_ref().unwrap()) - .with_context(|| format!("Failed to unmap BAR{} in I/O space.", id))?; - } - _ => mem_region - .unwrap() - .delete_subregion(self.bars[id].region.as_ref().unwrap()) - .with_context(|| anyhow!(PciError::UnregMemBar(id)))?, - } - self.bars[id].address = BAR_SPACE_UNMAPPED; - } if new_addr != BAR_SPACE_UNMAPPED { match self.bars[id].region_type { + #[cfg(target_arch = "x86_64")] RegionType::Io => { - #[cfg(target_arch = "x86_64")] io_region .unwrap() .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) .with_context(|| format!("Failed to map BAR{} in I/O space.", id))?; + self.bars[id].parent_io_region = + Some(Arc::new(Mutex::new(io_region.unwrap().clone()))); + } + _ => { + mem_region + .unwrap() + .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) + .with_context(|| anyhow!(PciError::UnregMemBar(id)))?; + self.bars[id].parent_mem_region = + Some(Arc::new(Mutex::new(mem_region.unwrap().clone()))); } - _ => mem_region - .unwrap() - .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) - .with_context(|| anyhow!(PciError::UnregMemBar(id)))?, } + self.bars[id].address = new_addr; } } diff --git a/usb/src/xhci/xhci_pci.rs b/usb/src/xhci/xhci_pci.rs index 194ea7f1a..72a494f3c 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/usb/src/xhci/xhci_pci.rs @@ -281,6 +281,9 @@ impl PciDevOps for XhciPciDevice { fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { self.xhci.lock().unwrap().reset(); + + self.pci_config.reset()?; + Ok(()) } } diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index c2814c676..8a0f2595c 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -813,8 +813,6 @@ impl VirtioPciDevice { self.queues.lock().unwrap().clear(); if self.device_activated.load(Ordering::Acquire) { self.device_activated.store(false, Ordering::Release); - let cloned_msix = self.config.msix.as_ref().unwrap().clone(); - cloned_msix.lock().unwrap().reset(); if let Err(e) = self.device.lock().unwrap().deactivate() { error!("Failed to deactivate virtio device, error is {:?}", e); return false; @@ -1261,6 +1259,8 @@ impl PciDevOps for VirtioPciDevice { .with_context(|| "Failed to reset virtio device")?; self.common_config.lock().unwrap().reset(); + self.config.reset()?; + Ok(()) } -- Gitee From e2ff581a31d32e8dfc5f34f4f2277fcdc2566e94 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 20 Jan 2023 11:07:21 +0800 Subject: [PATCH 0733/2187] Pci: Fix the secondary bus reset problem According to the PCIe specification, setting the BRIDGE_CTL_SEC_BUS_RESET bit of the root port configuration triggers a hot reset the corresponding root port. The operation was missing before. So fix it. Signed-off-by: Jinhao Gao Pci: Fix the secondary bus reset problem According to the PCIe specification, setting the BRIDGE_CTL_SEC_BUS_RESET bit of the root port configuration triggers a hot reset the corresponding root port. The operation was missing before. So fix it. Signed-off-by: Jinhao Gao --- pci/src/root_port.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index f708fb803..d330afddb 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -36,6 +36,7 @@ use super::config::{ PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; +use crate::config::{BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET}; use crate::hotplug::HotplugOps; use crate::msix::init_msix; use crate::{init_multifunction, PciError}; @@ -398,6 +399,8 @@ impl PciDevOps for RootPort { let old_status = le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let old_br_ctl = le_read_u16(&self.config.config, BRIDGE_CONTROL.into()).unwrap(); + self.config.write( offset, data, @@ -406,6 +409,17 @@ impl PciDevOps for RootPort { Some(&self.io_region), Some(&self.mem_region), ); + + let new_br_ctl = le_read_u16(&self.config.config, BRIDGE_CONTROL.into()).unwrap(); + if (!old_br_ctl & new_br_ctl & BRIDGE_CTL_SEC_BUS_RESET) != 0 { + if let Err(e) = self.reset(true) { + error!( + "Failed to reset child devices under root port {}: {}", + self.name, e + ) + } + } + if ranges_overlap(offset, end, COMMAND as usize, (COMMAND + 1) as usize) || ranges_overlap(offset, end, IO_BASE as usize, (IO_BASE + 2) as usize) || ranges_overlap( -- Gitee From fdb41d0cb6f556b004da7780c7e07c20be7293ff Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 20 Jan 2023 16:01:17 +0800 Subject: [PATCH 0734/2187] virtio-net: Do not share same kernel eventfd for update_evt The first scheduled update_handler will read fd, if we share the same kernel eventfd, the remaining handlers may not be scheduled at all. Signed-off-by: Yan Wang --- virtio/src/net.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 28e5c1b6d..021ca0eb4 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -1128,7 +1128,7 @@ pub struct Net { /// The send half of Rust's channel to send tap information. senders: Option>>, /// Eventfd for config space update. - update_evt: Arc, + update_evts: Vec>, /// Eventfd for device deactivate. deactivate_evts: Vec, /// Device is broken or not. @@ -1144,7 +1144,7 @@ impl Default for Net { taps: None, state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, - update_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + update_evts: Vec::new(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, @@ -1159,7 +1159,7 @@ impl Net { taps: None, state: Arc::new(Mutex::new(VirtioNetState::default())), senders: None, - update_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + update_evts: Vec::new(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), ctrl_info: None, @@ -1545,6 +1545,7 @@ impl VirtioDevice for Net { .with_context(|| "Failed to set tap offload")?; } + let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); let mut handler = NetIoHandler { rx: RxVirtio::new(rx_queue, rx_queue_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), @@ -1554,7 +1555,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features, receiver, - update_evt: self.update_evt.clone(), + update_evt: update_evt.clone(), device_broken: self.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), @@ -1570,6 +1571,7 @@ impl VirtioDevice for Net { self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts, )?; + self.update_evts.push(update_evt); } self.senders = Some(senders); self.broken.store(false, Ordering::SeqCst); @@ -1619,9 +1621,11 @@ impl VirtioDevice for Net { } } - self.update_evt - .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + for update_evt in &self.update_evts { + update_evt + .write(1) + .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + } } Ok(()) @@ -1629,6 +1633,7 @@ impl VirtioDevice for Net { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + self.update_evts.clear(); self.ctrl_info = None; Ok(()) } -- Gitee From 29221b66957dfa95016ab07914bdaaf941199134 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 20 Jan 2023 16:47:49 +0800 Subject: [PATCH 0735/2187] vhost-user-blk: fix some unwrap issues Fix some unwrap operations which may cause panic. Signed-off-by: Yan Wang --- virtio/src/vhost/user/block.rs | 22 +++++++++------------- virtio/src/vhost/user/client.rs | 13 ++++++++++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 32b1cb33e..bb1ad9dd3 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -58,17 +58,13 @@ impl Block { } fn delete_event(&mut self) -> Result<()> { - match &self.client { - Some(client) => { - client - .lock() - .unwrap() - .delete_event() - .with_context(|| "Failed to delete vhost-user blk event")?; - } - None => return Err(anyhow!("Failed to get client when stoping event")), - }; - + self.client + .as_ref() + .ok_or_else(|| anyhow!("Failed to get client when stoping event"))? + .lock() + .unwrap() + .delete_event() + .with_context(|| "Failed to delete vhost-user blk event")?; Ok(()) } @@ -240,7 +236,7 @@ impl VirtioDevice for Block { self.client .as_ref() - .unwrap() + .ok_or_else(|| anyhow!("Failed to get client when writing config"))? .lock() .unwrap() .set_virtio_blk_config(self.state.config_space) @@ -272,7 +268,7 @@ impl VirtioDevice for Block { fn deactivate(&mut self) -> Result<()> { self.client .as_ref() - .unwrap() + .ok_or_else(|| anyhow!("Failed to get client when deactivating device"))? .lock() .unwrap() .reset_vhost_user()?; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index e1a0d2011..6085fdb12 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -234,7 +234,10 @@ impl VhostUserMemInfo { return Ok(()); } - let file_back = fr.owner.get_file_backend().unwrap(); + let file_back = fr + .owner + .get_file_backend() + .ok_or_else(|| anyhow!("Failed to get file backend"))?; let mut mem_regions = self.regions.lock().unwrap(); let host_address = match fr.owner.get_host_address() { Some(addr) => addr, @@ -289,10 +292,14 @@ impl Listener for VhostUserMemInfo { ) -> std::result::Result<(), anyhow::Error> { match req_type { ListenerReqType::AddRegion => { - self.add_mem_range(range.unwrap())?; + self.add_mem_range( + range.ok_or_else(|| anyhow!("Flat range is None when adding region"))?, + )?; } ListenerReqType::DeleteRegion => { - self.delete_mem_range(range.unwrap())?; + self.delete_mem_range( + range.ok_or_else(|| anyhow!("Flat range is None when deleting region"))?, + )?; } _ => {} } -- Gitee From 32944ddc45f7cb89ae442aca07df81adee359e51 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 13:37:12 +0800 Subject: [PATCH 0736/2187] virtio-gpu: add virtio-gpu in list type Add virtio-gpu in list type, which is used by libvirt. Signed-off-by: Xiao Ye --- machine_manager/src/machine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 00195244f..915e14a57 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -293,6 +293,7 @@ pub trait DeviceInterface { ("nec-usb-xhci", "base-xhci"), ("usb-tablet", "usb-hid"), ("usb-kbd", "usb-hid"), + ("virtio-gpu-pci", "virtio-gpu"), ]; for list in list_types { -- Gitee From 9715efe32874a88954f137d8e7f4d489985ebcbc Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 11:39:28 +0800 Subject: [PATCH 0737/2187] VNC: add the qmp interface query_vnc Add the qmp interface of query_vnc, which is used to get the current information of VNC server. Signed-off-by: Xiao Ye --- docs/stratovirt_aarch64.xml | 7 +++ docs/stratovirt_x86.xml | 7 +++ machine/src/micro_vm/mod.rs | 10 ++++ machine/src/standard_vm/mod.rs | 15 ++++++ machine_manager/src/machine.rs | 3 ++ machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 66 ++++++++++++++++++++++++++- vnc/src/vnc.rs | 25 ++++++++++ 8 files changed, 133 insertions(+), 1 deletion(-) diff --git a/docs/stratovirt_aarch64.xml b/docs/stratovirt_aarch64.xml index fa37d2baa..b0dbfc5f5 100644 --- a/docs/stratovirt_aarch64.xml +++ b/docs/stratovirt_aarch64.xml @@ -61,6 +61,13 @@ /path/to/random_file
+ + + + + diff --git a/docs/stratovirt_x86.xml b/docs/stratovirt_x86.xml index 68069c02c..8732a9a43 100644 --- a/docs/stratovirt_x86.xml +++ b/docs/stratovirt_x86.xml @@ -69,6 +69,13 @@ /path/to/random_file
+ + + + + diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 9d0ac49e6..55d3e5fc8 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1089,6 +1089,16 @@ impl DeviceInterface for LightMachine { ) } + /// VNC is not supported by light machine currently. + fn query_vnc(&self) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "The service of VNC is not supported".to_string(), + ), + None, + ) + } + fn device_add(&mut self, args: Box) -> Response { // get slot of bus by addr or lun let mut slot = 0; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 63667321f..90f7941d3 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -26,6 +26,8 @@ use util::aio::AIO_NATIVE; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; +#[cfg(not(target_env = "musl"))] +use vnc::vnc::qmp_query_vnc; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; @@ -1099,6 +1101,19 @@ impl DeviceInterface for StdMachine { ) } + fn query_vnc(&self) -> Response { + #[cfg(not(target_env = "musl"))] + if let Some(vnc_info) = qmp_query_vnc() { + return Response::create_response(serde_json::to_value(&vnc_info).unwrap(), None); + } + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "The service of VNC is not supported".to_string(), + ), + None, + ) + } + fn device_add(&mut self, args: Box) -> Response { if let Err(e) = self.check_device_id_existed(&args.id) { return Response::create_error_response( diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 915e14a57..0d9080595 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -175,6 +175,9 @@ pub trait DeviceInterface { /// Query balloon's size. fn query_balloon(&self) -> Response; + /// Query the info of vnc server. + fn query_vnc(&self) -> Response; + /// Set balloon's size. fn balloon(&self, size: u64) -> Response; diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 8fe47a3e5..deb9df237 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -449,6 +449,7 @@ fn qmp_command_exec( (cancel_migrate, cancel_migrate), (query_cpus, query_cpus), (query_balloon, query_balloon), + (query_vnc, query_vnc), (list_type, list_type), (query_hotpluggable_cpus, query_hotpluggable_cpus); (device_list_properties, device_list_properties, typename), diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 272a9384d..c1dd4e8dc 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -166,6 +166,13 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "query-vnc")] + query_vnc { + #[serde(default)] + arguments: query_vnc, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, #[serde(rename = "migrate")] migrate { arguments: migrate, @@ -1288,6 +1295,63 @@ pub struct BalloonInfo { pub actual: u64, } +/// query-vnc: +/// Information about current VNC server. +/// +/// # Examples +/// +/// For pc machine type started with -vnc ip:port(for example: 0.0.0.0:0): +/// ```text +/// -> { "execute": "query-vnc" } +/// <- {"return": { +/// "enabled": true, +/// "host": "0.0.0.0", +/// "service": "50401", +/// "auth": "None", +/// "family": "ipv4", +/// "clients": [ +/// "host": "127.0.0.1", +/// "service": "50401", +/// "family": "ipv4", +/// ] +/// } +/// } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct query_vnc {} +impl Command for query_vnc { + type Res = VncInfo; + fn back(self) -> VncInfo { + Default::default() + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct VncInfo { + #[serde(rename = "enabled")] + pub enabled: bool, + #[serde(rename = "host")] + pub host: String, + #[serde(rename = "service")] + pub service: String, + #[serde(rename = "auth")] + pub auth: String, + #[serde(rename = "family")] + pub family: String, + #[serde(rename = "clients")] + pub clients: Vec, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct VncClientInfo { + #[serde(rename = "host")] + pub host: String, + #[serde(rename = "service")] + pub service: String, + #[serde(rename = "family")] + pub family: String, +} + /// balloon: /// /// Advice VM to change memory size with the argument `value`. @@ -1353,7 +1417,7 @@ impl Command for query_version { /// {"name":"cont"},{"name":"device_add"},{"name":"device_del"},{"name":"netdev_add"}, /// {"name":"netdev_del"},{"name":"query-hotpluggable-cpus"},{"name":"query-cpus"}, /// {"name":"query_status"},{"name":"getfd"},{"name":"blockdev_add"}, -/// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"}, +/// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"},{"name":"query_vnc"}, /// {"name":"migrate"},{"name":"query_migrate"},{"name":"query_version"}, /// {"name":"query_target"},{"name":"query_commands"}]} /// ``` diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 3b55bf6f9..0959ea6c3 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -38,6 +38,7 @@ use log::error; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, + qmp::qmp_schema::{VncClientInfo, VncInfo}, }; use once_cell::sync::Lazy; use std::{ @@ -358,6 +359,30 @@ fn add_vnc_server(server: Arc) { VNC_SERVERS.lock().unwrap().push(server); } +/// Qmp: return the information about current VNC server. +pub fn qmp_query_vnc() -> Option { + let mut vnc_info = VncInfo::default(); + if VNC_SERVERS.lock().unwrap().is_empty() { + vnc_info.enabled = false; + return Some(vnc_info); + } + vnc_info.enabled = true; + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + vnc_info.family = "ipv4".to_string(); + + let mut locked_handler = server.client_handlers.lock().unwrap(); + for client in locked_handler.values_mut() { + let mut client_info = VncClientInfo { + host: client.addr.clone(), + ..Default::default() + }; + client_info.family = "ipv4".to_string(); + vnc_info.clients.push(client_info); + } + + Some(vnc_info) +} + /// Set dirty in bitmap. pub fn set_area_dirty( dirty: &mut Bitmap, -- Gitee From 9abb5777145db233fb6d97ae4f0fbee82ca9fe5b Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 18:15:31 +0800 Subject: [PATCH 0738/2187] VNC: the qmp command of query vnc adapt to libvirt command Rename query_vnc to query-vnc, adapt to libvirt command. Signed-off-by: Xiao Ye --- machine_manager/src/qmp/qmp_schema.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index c1dd4e8dc..3a59dbd94 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -167,6 +167,7 @@ pub enum QmpCommand { id: Option, }, #[serde(rename = "query-vnc")] + #[strum(serialize = "query-vnc")] query_vnc { #[serde(default)] arguments: query_vnc, -- Gitee From 4a11e91d85d9fc5c027820962af5ec14feadfae7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 14 Jan 2023 12:16:18 +0800 Subject: [PATCH 0739/2187] VNC: modify Value overflow. Fix potential value overflow Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 12 ++---------- vnc/src/client.rs | 10 ++++------ vnc/src/server.rs | 5 +++-- vnc/src/vnc.rs | 26 ++++++++++++++------------ 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 925c8d88f..50d3dd4b6 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -116,17 +116,9 @@ impl ClientIoHandler { pub fn get_mechname_length(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); let len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]); - - if len > MECHNAME_MAX_LEN { - error!("SASL mechname too long"); - return Err(anyhow!(VncError::AuthFailed(String::from( - "SASL mechname too long" - )))); - } - if len < MECHNAME_MIN_LEN { - error!("SASL mechname too short"); + if !(MECHNAME_MIN_LEN..MECHNAME_MAX_LEN).contains(&len) { return Err(anyhow!(VncError::AuthFailed(String::from( - "SASL mechname too short" + "SASL mechname too short or too long" )))); } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 592bde9a8..8aea851b2 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -19,7 +19,7 @@ use crate::{ utils::BuffPool, vnc::{ framebuffer_upadate, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, - DIRTY_WIDTH_BITS, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, MIN_OUTPUT_LIMIT, + DIRTY_WIDTH_BITS, MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, }, VncError, @@ -620,9 +620,7 @@ impl ClientIoHandler { let mut locked_dpm = client.client_dpm.lock().unwrap(); let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); - if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) - || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) - { + if !(0..=MAX_IMAGE_SIZE).contains(&width) || !(0..=MAX_IMAGE_SIZE).contains(&height) { error!("Invalid Image Size!"); return Err(anyhow!(VncError::InvalidImageSize)); } @@ -1215,8 +1213,8 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & } let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); - if !(0..=MAX_WINDOW_WIDTH as i32).contains(&width) - || !(0..=MAX_WINDOW_HEIGHT as i32).contains(&height) + if !(0..=MAX_IMAGE_SIZE as i32).contains(&width) + || !(0..=MAX_IMAGE_SIZE as i32).contains(&height) { error!("Invalid Image Size!"); return; diff --git a/vnc/src/server.rs b/vnc/src/server.rs index b7e33cc35..e79714827 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -341,11 +341,12 @@ impl VncSurface { /// Return the number of dirty area. pub fn update_server_image(&mut self) -> i32 { let mut dirty_num = 0; - let height = self.get_min_height(); + let height = self.get_min_height() as usize; let g_bpl = self.guest_dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; + let total_dirty_bits = height.checked_mul(g_bpl).unwrap_or(0); let mut offset = self.guest_dirty_bitmap.find_next_bit(0).unwrap(); - if offset >= (height as usize) * g_bpl { + if offset >= total_dirty_bits { return dirty_num; } diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 0959ea6c3..f8b034e7f 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -65,6 +65,7 @@ pub const MAX_WINDOW_HEIGHT: u16 = 2048; pub const DIRTY_WIDTH_BITS: u16 = MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM; pub const VNC_BITMAP_WIDTH: u64 = round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) * u64::BITS as u64; +pub const MAX_IMAGE_SIZE: i32 = 65535; /// Output throttle scale. pub const OUTPUT_THROTTLE_SCALE: i32 = 5; @@ -466,11 +467,20 @@ pub fn update_server_surface(server: &Arc) { /// Check if the suface for VncClient is need update fn check_surface(locked_vnc_surface: &mut VncSurface, surface: &DisplaySurface) -> bool { + let guest_width = get_image_width(surface.image); + let guest_height = get_image_height(surface.image); + let server_width = get_image_width(locked_vnc_surface.server_image); + let server_height = get_image_height(locked_vnc_surface.server_image); + if !(0..=MAX_IMAGE_SIZE).contains(&guest_width) || !(0..=MAX_IMAGE_SIZE).contains(&guest_height) + { + return false; + } + if surface.image.is_null() || locked_vnc_surface.server_image.is_null() || locked_vnc_surface.guest_format != surface.format - || get_image_width(locked_vnc_surface.server_image) != get_image_width(surface.image) - || get_image_height(locked_vnc_surface.server_image) != get_image_height(surface.image) + || guest_width != server_width + || guest_height != server_height { return true; } @@ -480,21 +490,13 @@ fn check_surface(locked_vnc_surface: &mut VncSurface, surface: &DisplaySurface) /// Check if rectangle is in spec fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { - if rect.x >= width { + if rect.x >= width || rect.y >= height { return false; } rect.w = cmp::min(width - rect.x, rect.w); - if rect.w == 0 { - return false; - } - - if rect.y >= height { - return false; - } - rect.h = cmp::min(height - rect.y, rect.h); - if rect.h == 0 { + if rect.w <= 0 || rect.h <= 0 { return false; } -- Gitee From 8855b8d83a0a99a2c984fc3ce37ee98ce6e01b0d Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 11 Jan 2023 21:32:03 +0800 Subject: [PATCH 0740/2187] VNC: Optimize mutex Optimize the mutex in VNC, mainly including the following aspects: 1. Adjust the data structure to avoid holding two mutex of same level. 2. Reduce the scope of the lock and release the lock in advance if it is not necessary. 3. If some data structures are only used in the main thread, use RefCell instead of mutex, and try to use borrow() instead of borrow_Mut() to get the reference of the element. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 80 +++++++++++++--------------- vnc/src/client.rs | 123 ++++++++++++++++++-------------------------- vnc/src/input.rs | 11 ++-- vnc/src/server.rs | 14 ++--- vnc/src/vencrypt.rs | 8 +-- vnc/src/vnc.rs | 15 +++--- 6 files changed, 111 insertions(+), 140 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index 50d3dd4b6..e907ddc70 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -145,21 +145,21 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); let mech_name = String::from_utf8_lossy(&buf).to_string(); - let mut locked_security = self.server.security_type.lock().unwrap(); - let mech_list: Vec<&str> = locked_security.saslconfig.mech_list.split(',').collect(); + let mut security = self.server.security_type.borrow_mut(); + let mech_list: Vec<&str> = security.saslconfig.mech_list.split(',').collect(); for mech in mech_list { if mech_name == *mech { - locked_security.saslconfig.mech_name = mech_name; + security.saslconfig.mech_name = mech_name; break; } } // Unsupported mechanism. - if locked_security.saslconfig.mech_name.is_empty() { + if security.saslconfig.mech_name.is_empty() { return Err(anyhow!(VncError::AuthFailed( "Unsupported mechanism".to_string() ))); } - drop(locked_security); + drop(security); self.update_event_handler(4, ClientIoHandler::get_authmessage_length); Ok(()) @@ -199,17 +199,17 @@ impl ClientIoHandler { let server = self.server.clone(); let client = self.client.clone(); - let mut locked_security = server.security_type.lock().unwrap(); + let mut security = server.security_type.borrow_mut(); let err: c_int; let mut serverout: *const c_char = ptr::null_mut(); let mut serverout_len: c_uint = 0; - let mech_name = CString::new(locked_security.saslconfig.mech_name.as_str()).unwrap(); + let mech_name = CString::new(security.saslconfig.mech_name.as_str()).unwrap(); // Start authentication. - if locked_security.saslconfig.sasl_stage == SaslStage::SaslServerStart { + if security.saslconfig.sasl_stage == SaslStage::SaslServerStart { unsafe { err = sasl_server_start( - locked_security.saslconfig.sasl_conn, + security.saslconfig.sasl_conn, mech_name.as_ptr(), client_data.as_ptr() as *const c_char, client_len, @@ -220,7 +220,7 @@ impl ClientIoHandler { } else { unsafe { err = sasl_server_step( - locked_security.saslconfig.sasl_conn, + security.saslconfig.sasl_conn, client_data.as_ptr() as *const c_char, client_len, &mut serverout, @@ -230,12 +230,12 @@ impl ClientIoHandler { } if err != SASL_OK && err != SASL_CONTINUE { - unsafe { sasl_dispose(&mut locked_security.saslconfig.sasl_conn) } + unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) } error!("Auth failed!"); return Err(anyhow!(VncError::AuthFailed("Auth failed!".to_string()))); } if serverout_len > SASL_DATA_MAX_LEN { - unsafe { sasl_dispose(&mut locked_security.saslconfig.sasl_conn) } + unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) } error!("SASL data too long"); return Err(anyhow!(VncError::AuthFailed( "SASL data too long".to_string() @@ -258,14 +258,14 @@ impl ClientIoHandler { } else if err == SASL_CONTINUE { buf.append(&mut (0_u8).as_bytes().to_vec()); } - drop(locked_security); + drop(security); if err == SASL_CONTINUE { // Authentication continue. - let mut locked_security = server.security_type.lock().unwrap(); - locked_security.saslconfig.sasl_stage = SaslStage::SaslServerStep; + let mut security = server.security_type.borrow_mut(); + security.saslconfig.sasl_stage = SaslStage::SaslServerStep; self.update_event_handler(4, ClientIoHandler::get_authmessage_length); - drop(locked_security); + drop(security); return Ok(()); } else { if let Err(err) = self.sasl_check_ssf() { @@ -344,7 +344,7 @@ impl ClientIoHandler { err )))); } - self.server.security_type.lock().unwrap().saslconfig = saslconfig; + self.server.security_type.borrow_mut().saslconfig = saslconfig; Ok(()) } @@ -355,10 +355,10 @@ impl ClientIoHandler { let mut err: c_int; let ssf: sasl_ssf_t = 256; let ssf = &ssf as *const sasl_ssf_t; - let locked_security = self.server.security_type.lock().unwrap(); + let security = self.server.security_type.borrow_mut(); unsafe { err = sasl_setprop( - locked_security.saslconfig.sasl_conn, + security.saslconfig.sasl_conn, SASL_SSF_EXTERNAL as i32, ssf as *const c_void, ); @@ -386,7 +386,7 @@ impl ClientIoHandler { let props = &saslprops as *const sasl_security_properties_t; unsafe { err = sasl_setprop( - locked_security.saslconfig.sasl_conn, + security.saslconfig.sasl_conn, SASL_SEC_PROPS.try_into().unwrap(), props as *const c_void, ); @@ -410,11 +410,11 @@ impl ClientIoHandler { let sep = CString::new(",").unwrap(); let suffix = CString::new("").unwrap(); let mut mechlist: *const c_char = ptr::null_mut(); - let mut locked_security = self.server.security_type.lock().unwrap(); + let mut security = self.server.security_type.borrow_mut(); let client = self.client.clone(); unsafe { err = sasl_listmech( - locked_security.saslconfig.sasl_conn, + security.saslconfig.sasl_conn, ptr::null_mut(), prefix.as_ptr(), sep.as_ptr(), @@ -431,12 +431,12 @@ impl ClientIoHandler { ))); } let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) }; - locked_security.saslconfig.mech_list = String::from(mech_list.to_str().unwrap()); + security.saslconfig.mech_list = String::from(mech_list.to_str().unwrap()); let mut buf = Vec::new(); - let len = locked_security.saslconfig.mech_list.len(); + let len = security.saslconfig.mech_list.len(); buf.append(&mut (len as u32).to_be_bytes().to_vec()); - buf.append(&mut locked_security.saslconfig.mech_list.as_bytes().to_vec()); - drop(locked_security); + buf.append(&mut security.saslconfig.mech_list.as_bytes().to_vec()); + drop(security); vnc_write(&client, buf); self.flush(); @@ -446,19 +446,13 @@ impl ClientIoHandler { /// Check whether the ssf layer of sasl meets the strength requirements. fn sasl_check_ssf(&mut self) -> Result<()> { let server = self.server.clone(); - let mut locked_security = server.security_type.lock().unwrap(); - if !locked_security.saslconfig.want_ssf { + let mut security = server.security_type.borrow_mut(); + if !security.saslconfig.want_ssf { return Ok(()); } let err: c_int; let mut val: *const c_void = ptr::null_mut(); - unsafe { - err = sasl_getprop( - locked_security.saslconfig.sasl_conn, - SASL_SSF as c_int, - &mut val, - ) - } + unsafe { err = sasl_getprop(security.saslconfig.sasl_conn, SASL_SSF as c_int, &mut val) } if err != SASL_OK { error!("sasl_getprop: internal error"); return Err(anyhow!(VncError::AuthFailed(String::from( @@ -474,23 +468,23 @@ impl ClientIoHandler { )))); } - locked_security.saslconfig.run_ssf = 1; - drop(locked_security); + security.saslconfig.run_ssf = 1; + drop(security); Ok(()) } /// Check username. fn sasl_check_authz(&mut self) -> Result<()> { - let locked_security = self.server.security_type.lock().unwrap(); + let security = self.server.security_type.borrow_mut(); let mut val: *const c_void = ptr::null_mut(); let err = unsafe { sasl_getprop( - locked_security.saslconfig.sasl_conn, + security.saslconfig.sasl_conn, SASL_USERNAME as c_int, &mut val, ) }; - drop(locked_security); + drop(security); if err != SASL_OK { return Err(anyhow!(VncError::AuthFailed(String::from( "Cannot fetch SASL username" @@ -505,8 +499,8 @@ impl ClientIoHandler { let username = String::from(username.to_str().unwrap()); let server = self.server.clone(); - let locked_security = server.security_type.lock().unwrap(); - if let Some(saslauth) = &locked_security.saslauth { + let security = server.security_type.borrow_mut(); + if let Some(saslauth) = &security.saslauth { if saslauth.identity != username { return Err(anyhow!(VncError::AuthFailed(String::from( "No SASL username set" @@ -517,7 +511,7 @@ impl ClientIoHandler { "No SASL username set" )))); } - drop(locked_security); + drop(security); Ok(()) } diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 8aea851b2..c36a142ac 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -43,8 +43,7 @@ use util::{ NotifierOperation, }, }; -use vmm_sys_util::epoll::EventSet; -use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub const APP_NAME: &str = "stratovirt"; const MAX_RECVBUF_LEN: usize = 1024; @@ -160,6 +159,8 @@ impl Rectangle { /// Display Output mode information of client. #[derive(Clone)] pub struct DisplayMode { + /// Vnc display feature. + feature: i32, /// Width of client display. pub client_width: i32, /// Height of client display. @@ -177,6 +178,7 @@ pub struct DisplayMode { impl DisplayMode { pub fn new(enc: i32, client_be: bool, convert: bool, pf: PixelFormat) -> Self { DisplayMode { + feature: 0, client_width: 0, client_height: 0, enc, @@ -185,6 +187,10 @@ impl DisplayMode { pf, } } + + pub fn has_feature(&self, feature: VncFeatures) -> bool { + self.feature & (1 << feature as usize) != 0 + } } impl Default for DisplayMode { @@ -234,8 +240,6 @@ pub struct ConnState { update_state: UpdateState, /// RFB protocol version. pub version: VncVersion, - /// Vnc display feature. - feature: i32, } impl Default for ConnState { @@ -244,7 +248,6 @@ impl Default for ConnState { dis_conn: false, update_state: UpdateState::No, version: VncVersion::default(), - feature: 0, } } } @@ -266,10 +269,6 @@ impl ConnState { UpdateState::Force => true, } } - - pub fn has_feature(&self, feature: VncFeatures) -> bool { - self.feature & (1 << feature as usize) != 0 - } } /// Struct to record the state with the vnc client. @@ -565,7 +564,7 @@ impl ClientIoHandler { version.minor = 3; } self.client.conn_state.lock().unwrap().version = version; - let auth = self.server.security_type.lock().unwrap().auth; + let auth = self.server.security_type.borrow().auth; if self.client.conn_state.lock().unwrap().version.minor == 3 { error!("Waiting for handle minor=3 ..."); @@ -617,17 +616,17 @@ impl ClientIoHandler { // Send server framebuffer info. let locked_surface = self.server.vnc_surface.lock().unwrap(); - let mut locked_dpm = client.client_dpm.lock().unwrap(); let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); + drop(locked_surface); if !(0..=MAX_IMAGE_SIZE).contains(&width) || !(0..=MAX_IMAGE_SIZE).contains(&height) { error!("Invalid Image Size!"); return Err(anyhow!(VncError::InvalidImageSize)); } + let mut locked_dpm = client.client_dpm.lock().unwrap(); locked_dpm.client_width = width; locked_dpm.client_height = height; drop(locked_dpm); - drop(locked_surface); buf.append(&mut (width as u16).to_be_bytes().to_vec()); buf.append(&mut (height as u16).to_be_bytes().to_vec()); @@ -644,7 +643,7 @@ impl ClientIoHandler { /// Authentication fn handle_auth(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); - let auth = self.server.security_type.lock().unwrap().auth; + let auth = self.server.security_type.borrow().auth; let client = self.client.clone(); let version = client.conn_state.lock().unwrap().version.clone(); @@ -827,7 +826,6 @@ impl ClientIoHandler { } let mut locked_dpm = self.client.client_dpm.lock().unwrap(); - let mut locked_state = self.client.conn_state.lock().unwrap(); while num_encoding > 0 { let offset = (4 * num_encoding) as usize; let enc = i32::from_be_bytes([ @@ -841,48 +839,48 @@ impl ClientIoHandler { locked_dpm.enc = enc; } ENCODING_HEXTILE => { - locked_state.feature |= 1 << VncFeatures::VncFeatureHextile as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureHextile as usize; locked_dpm.enc = enc; } ENCODING_TIGHT => { - locked_state.feature |= 1 << VncFeatures::VncFeatureTight as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureTight as usize; locked_dpm.enc = enc; } ENCODING_ZLIB => { // ZRLE compress better than ZLIB, so prioritize ZRLE. - if locked_state.feature & (1 << VncFeatures::VncFeatureZrle as usize) == 0 { - locked_state.feature |= 1 << VncFeatures::VncFeatureZlib as usize; + if locked_dpm.feature & (1 << VncFeatures::VncFeatureZrle as usize) == 0 { + locked_dpm.feature |= 1 << VncFeatures::VncFeatureZlib as usize; locked_dpm.enc = enc; } } ENCODING_ZRLE => { - locked_state.feature |= 1 << VncFeatures::VncFeatureZrle as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureZrle as usize; locked_dpm.enc = enc; } ENCODING_ZYWRLE => { - locked_state.feature |= 1 << VncFeatures::VncFeatureZywrle as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureZywrle as usize; locked_dpm.enc = enc; } ENCODING_DESKTOPRESIZE => { - locked_state.feature |= 1 << VncFeatures::VncFeatureResize as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureResize as usize; } ENCODING_DESKTOP_RESIZE_EXT => { - locked_state.feature |= 1 << VncFeatures::VncFeatureResizeExt as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureResizeExt as usize; } ENCODING_POINTER_TYPE_CHANGE => { - locked_state.feature |= 1 << VncFeatures::VncFeaturePointerTypeChange as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeaturePointerTypeChange as usize; } ENCODING_RICH_CURSOR => { - locked_state.feature |= 1 << VncFeatures::VncFeatureRichCursor as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureRichCursor as usize; } ENCODING_ALPHA_CURSOR => { - locked_state.feature |= 1 << VncFeatures::VncFeatureAlphaCursor as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureAlphaCursor as usize; } ENCODING_WMVI => { - locked_state.feature |= 1 << VncFeatures::VncFeatureWmvi as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureWmvi as usize; } ENCODING_LED_STATE => { - locked_state.feature |= 1 << VncFeatures::VncFeatureLedState as usize; + locked_dpm.feature |= 1 << VncFeatures::VncFeatureLedState as usize; } _ => {} } @@ -891,7 +889,6 @@ impl ClientIoHandler { } drop(locked_dpm); - drop(locked_state); let mut buf: Vec = Vec::new(); // VNC desktop resize. desktop_resize(&client, &server, &mut buf); @@ -910,10 +907,10 @@ impl ClientIoHandler { return; } let buf = self.read_incoming_msg(); - let locked_surface = self.server.vnc_surface.lock().unwrap(); - let width = get_image_width(locked_surface.server_image); - let height = get_image_height(locked_surface.server_image); - drop(locked_surface); + let locked_dpm = self.client.client_dpm.lock().unwrap(); + let width = locked_dpm.client_width; + let height = locked_dpm.client_height; + drop(locked_dpm); let client = self.client.clone(); let mut locked_state = client.conn_state.lock().unwrap(); if buf[1] != 0 { @@ -1086,23 +1083,23 @@ impl EventNotifierHelper for ClientIoHandler { /// Generate the data that needs to be sent. /// Add to send queue -pub fn get_rects(client: &Arc, server: &Arc, dirty_num: i32) -> i32 { +pub fn get_rects(client: &Arc, dirty_num: i32) { if !client.conn_state.lock().unwrap().is_need_update(dirty_num) { - return 0; + return; } - let mut num_rects = 0; let mut x: u64; let mut y: u64 = 0; let mut h: u64; let mut x2: u64; let mut rects = Vec::new(); - let bpl = client.dirty_bitmap.lock().unwrap().vol() / MAX_WINDOW_HEIGHT as usize; - let locked_surface = server.vnc_surface.lock().unwrap(); + let locked_dpm = client.client_dpm.lock().unwrap(); + let height = locked_dpm.client_height as u64; + let width = locked_dpm.client_width as u64; + drop(locked_dpm); let mut locked_dirty = client.dirty_bitmap.lock().unwrap(); + let bpl = locked_dirty.vol() / MAX_WINDOW_HEIGHT as usize; - let height = get_image_height(locked_surface.server_image) as u64; - let width = get_image_width(locked_surface.server_image) as u64; loop { // Find the first non-zero bit in dirty bitmap. let offset = locked_dirty.find_next_bit(y as usize * bpl).unwrap() as u64; @@ -1123,7 +1120,7 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: let len = (x2 - x) as usize; if let Err(e) = locked_dirty.clear_range(start, len) { error!("clear bitmap error: {:?}", e); - return num_rects; + return; } i += 1; } @@ -1137,7 +1134,6 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: ((x2 - x) * DIRTY_PIXELS_NUM as u64) as i32, h as i32, )); - num_rects += 1; } if x == 0 && x2 == width / DIRTY_PIXELS_NUM as u64 { @@ -1148,19 +1144,13 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: } } + drop(locked_dirty); VNC_RECT_INFO .lock() .unwrap() .push(RectInfo::new(client, rects)); - drop(locked_dirty); - drop(locked_surface); - - let mut locked_state = client.conn_state.lock().unwrap(); - locked_state.update_state = UpdateState::No; - drop(locked_state); - - num_rects + client.conn_state.lock().unwrap().update_state = UpdateState::No; } fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> Result<()> { @@ -1204,13 +1194,6 @@ pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { /// Set Desktop Size. pub fn desktop_resize(client: &Arc, server: &Arc, buf: &mut Vec) { let locked_surface = server.vnc_surface.lock().unwrap(); - let locked_state = client.conn_state.lock().unwrap(); - let mut locked_dpm = client.client_dpm.lock().unwrap(); - if !locked_state.has_feature(VncFeatures::VncFeatureResizeExt) - && !locked_state.has_feature(VncFeatures::VncFeatureResize) - { - return; - } let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); if !(0..=MAX_IMAGE_SIZE as i32).contains(&width) @@ -1219,15 +1202,17 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & error!("Invalid Image Size!"); return; } - - if locked_dpm.client_width == width && locked_dpm.client_height == height { + drop(locked_surface); + let mut locked_dpm = client.client_dpm.lock().unwrap(); + if (!locked_dpm.has_feature(VncFeatures::VncFeatureResizeExt) + && !locked_dpm.has_feature(VncFeatures::VncFeatureResize)) + || (locked_dpm.client_width == width && locked_dpm.client_height == height) + { return; } locked_dpm.client_width = width; locked_dpm.client_height = height; drop(locked_dpm); - drop(locked_state); - drop(locked_surface); buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); buf.append(&mut (0_u8).to_be_bytes().to_vec()); @@ -1237,8 +1222,8 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & /// Set color depth for client. pub fn set_color_depth(client: &Arc, buf: &mut Vec) { - let locked_state = client.conn_state.lock().unwrap(); - if locked_state.has_feature(VncFeatures::VncFeatureWmvi) { + let mut locked_dpm = client.client_dpm.lock().unwrap(); + if locked_dpm.has_feature(VncFeatures::VncFeatureWmvi) { let width = client.client_dpm.lock().unwrap().client_width; let height = client.client_dpm.lock().unwrap().client_height; buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); @@ -1247,14 +1232,8 @@ pub fn set_color_depth(client: &Arc, buf: &mut Vec) { framebuffer_upadate(0, 0, width, height, ENCODING_WMVI, buf); buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); pixel_format_message(client, buf); - } else if !client - .client_dpm - .lock() - .unwrap() - .pf - .is_default_pixel_format() - { - client.client_dpm.lock().unwrap().convert = true; + } else if !locked_dpm.pf.is_default_pixel_format() { + locked_dpm.convert = true; } } @@ -1290,7 +1269,7 @@ pub fn display_cursor_define( return; } if client - .conn_state + .client_dpm .lock() .unwrap() .has_feature(VncFeatures::VncFeatureAlphaCursor) @@ -1313,7 +1292,7 @@ pub fn display_cursor_define( } if client - .conn_state + .client_dpm .lock() .unwrap() .has_feature(VncFeatures::VncFeatureRichCursor) diff --git a/vnc/src/input.rs b/vnc/src/input.rs index e3837f888..6b2bf9128 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -194,7 +194,7 @@ impl ClientIoHandler { if (ASCII_A..=ASCII_Z).contains(&keysym) { keysym += UPPERCASE_TO_LOWERCASE; } - let mut locked_kbd_state = server.keyboard_state.lock().unwrap(); + let mut kbd_state = server.keyboard_state.borrow_mut(); let dcl_id = self.server.display_listener.lock().unwrap().dcl_id; let keycode: u16 = match server.keysym2keycode.get(&(keysym as u16)) { @@ -207,18 +207,17 @@ impl ClientIoHandler { if (KEYCODE_1..KEYCODE_9 + 1).contains(&keycode) && down && dcl_id.is_some() - && locked_kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) - && locked_kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) + && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) + && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) { - locked_kbd_state.keyboard_state_reset(); + kbd_state.keyboard_state_reset(); console_select(Some((keycode - KEYCODE_1) as usize)); } - if let Err(e) = locked_kbd_state.keyboard_state_update(keycode, down) { + if let Err(e) = kbd_state.keyboard_state_update(keycode, down) { error!("{:?}", e); return; } - drop(locked_kbd_state); do_key_event(keycode, down); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); diff --git a/vnc/src/server.rs b/vnc/src/server.rs index e79714827..f96e5dc66 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -36,6 +36,7 @@ use machine_manager::{ event_loop::EventLoop, }; use std::{ + cell::RefCell, cmp, collections::HashMap, net::{SocketAddr, TcpListener, TcpStream}, @@ -63,9 +64,9 @@ pub struct VncServer { /// Client io handler. pub client_handlers: Arc>>>, /// Security Type for connection. - pub security_type: Arc>, + pub security_type: Rc>, /// keyboard status. - pub keyboard_state: Arc>, + pub keyboard_state: Rc>, /// Mapping ASCII to keycode. pub keysym2keycode: HashMap, /// Image data of surface. @@ -85,12 +86,12 @@ impl VncServer { /// Create a new VncServer. pub fn new( guest_image: *mut pixman_image_t, - keyboard_state: Arc>, + keyboard_state: Rc>, keysym2keycode: HashMap, ) -> Self { VncServer { client_handlers: Arc::new(Mutex::new(HashMap::new())), - security_type: Arc::new(Mutex::new(SecurityType::default())), + security_type: Rc::new(RefCell::new(SecurityType::default())), keyboard_state, keysym2keycode, vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), @@ -565,11 +566,10 @@ pub fn make_server_config( // Set security config. server .security_type - .lock() - .unwrap() + .borrow_mut() .set_security_config(vnc_cfg, object)?; // Set auth type. - server.security_type.lock().unwrap().set_auth()?; + server.security_type.borrow_mut().set_auth()?; Ok(()) } diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 2e605a8d8..94f9fe3c1 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -72,7 +72,7 @@ impl ClientIoHandler { pub fn client_vencrypt_init(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); let client = self.client.clone(); - let subauth = self.server.security_type.lock().unwrap().subauth; + let subauth = self.server.security_type.borrow().subauth; // VeNCrypt version 0.2. if buf[0] != 0 || buf[1] != 2 { let mut buf = Vec::new(); @@ -103,7 +103,7 @@ impl ClientIoHandler { let buf = [buf[0], buf[1], buf[2], buf[3]]; let auth = u32::from_be_bytes(buf); let client = self.client.clone(); - let subauth = self.server.security_type.lock().unwrap().subauth; + let subauth = self.server.security_type.borrow().subauth; if auth != subauth as u32 { let mut buf = Vec::new(); @@ -123,7 +123,7 @@ impl ClientIoHandler { vnc_write(&client, buf); self.flush(); - if let Some(tls_config) = self.server.security_type.lock().unwrap().tls_config.clone() { + if let Some(tls_config) = self.server.security_type.borrow().tls_config.clone() { match rustls::ServerConnection::new(tls_config) { Ok(tls_conn) => { self.tls_conn = Some(tls_conn); @@ -200,7 +200,7 @@ impl ClientIoHandler { } fn handle_vencrypt_subauth(&mut self) -> Result<()> { - let subauth = self.server.security_type.lock().unwrap().subauth; + let subauth = self.server.security_type.borrow().subauth; let client = self.client.clone(); match subauth { SubAuthState::VncAuthVencryptX509Sasl => { diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index f8b034e7f..8059fa0cc 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -42,6 +42,7 @@ use machine_manager::{ }; use once_cell::sync::Lazy; use std::{ + cell::RefCell, cmp, collections::HashMap, net::TcpListener, @@ -163,10 +164,9 @@ impl DisplayChangeListenerOperations for VncInterface { } dcl.update_interval = update_interval; - let mut _rects: i32 = 0; let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { - _rects += get_rects(client, &server, dirty_num); + get_rects(client, dirty_num); } } @@ -272,8 +272,8 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { keysym2keycode.insert(k, v); } // Record keyboard state. - let keyboard_state: Arc> = - Arc::new(Mutex::new(KeyBoardState::new(max_keycode as usize))); + let keyboard_state: Rc> = + Rc::new(RefCell::new(KeyBoardState::new(max_keycode as usize))); let server = Arc::new(VncServer::new( get_client_image(), @@ -330,11 +330,11 @@ fn start_vnc_thread() -> Result<()> { buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut [0_u8; 2].to_vec()); - let locked_surface = server.vnc_surface.lock().unwrap(); - let width = get_image_width(locked_surface.server_image); - let height = get_image_height(locked_surface.server_image); for rect in rect_info.rects.iter_mut() { + let locked_surface = server.vnc_surface.lock().unwrap(); let dpm = rect_info.client.client_dpm.lock().unwrap().clone(); + let width = dpm.client_width; + let height = dpm.client_height; if check_rect(rect, width, height) { let n = send_framebuffer_update(locked_surface.server_image, rect, &dpm, &mut buf); @@ -345,7 +345,6 @@ fn start_vnc_thread() -> Result<()> { } buf[2] = (num_rects >> 8) as u8; buf[3] = num_rects as u8; - drop(locked_surface); let client = rect_info.client; vnc_write(&client, buf); -- Gitee From 5063aab4fcfa3356b3921e074df1b30703b413e2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 26 Jan 2023 21:51:53 +0800 Subject: [PATCH 0741/2187] VNC: fix unsafe unwrap operation Add the handling of possible unwrap errors to avoid the potential panic risk of the program. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 48 ++++++++++++++++++-------------------------- vnc/src/client.rs | 49 +++++++++++++++++++++++++++------------------ vnc/src/console.rs | 2 +- vnc/src/server.rs | 22 ++++++++++++-------- vnc/src/vencrypt.rs | 21 +++++++++---------- vnc/src/vnc.rs | 11 +++++----- 6 files changed, 79 insertions(+), 74 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index e907ddc70..ac3fd4d87 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::{ - client::{vnc_write, ClientIoHandler, APP_NAME}, + client::{vnc_flush, vnc_write, ClientIoHandler, APP_NAME}, VncError, }; use anyhow::{anyhow, Result}; @@ -203,7 +203,7 @@ impl ClientIoHandler { let err: c_int; let mut serverout: *const c_char = ptr::null_mut(); let mut serverout_len: c_uint = 0; - let mech_name = CString::new(security.saslconfig.mech_name.as_str()).unwrap(); + let mech_name = CString::new(security.saslconfig.mech_name.as_str())?; // Start authentication. if security.saslconfig.sasl_stage == SaslStage::SaslServerStart { @@ -246,7 +246,7 @@ impl ClientIoHandler { if serverout_len > 0 { // Authentication related information. let serverout = unsafe { CStr::from_ptr(serverout as *const c_char) }; - let auth_message = String::from(serverout.to_str().unwrap()); + let auth_message = String::from(serverout.to_str().unwrap_or("")); buf.append(&mut (serverout_len + 1).to_be_bytes().to_vec()); buf.append(&mut auth_message.as_bytes().to_vec()); } else { @@ -272,7 +272,7 @@ impl ClientIoHandler { // Reject auth: the strength of ssf is too weak. auth_reject(&mut buf); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); return Err(err); } @@ -280,7 +280,7 @@ impl ClientIoHandler { // Reject auth: wrong sasl username. auth_reject(&mut buf); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); return Err(err); } // Accpet auth. @@ -288,7 +288,7 @@ impl ClientIoHandler { } vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); self.update_event_handler(1, ClientIoHandler::handle_client_init); Ok(()) } @@ -296,23 +296,13 @@ impl ClientIoHandler { /// Sasl server init. fn sasl_server_init(&mut self) -> Result<()> { let mut err: c_int; - let service = CString::new(SERVICE).unwrap(); - let appname = CString::new(APP_NAME).unwrap(); - let local_addr = self - .stream - .local_addr() - .unwrap() - .to_string() - .replace(':', ";"); - let remote_addr = self - .stream - .peer_addr() - .unwrap() - .to_string() - .replace(':', ";"); + let service = CString::new(SERVICE)?; + let appname = CString::new(APP_NAME)?; + let local_addr = self.stream.local_addr()?.to_string().replace(':', ";"); + let remote_addr = self.stream.peer_addr()?.to_string().replace(':', ";"); info!("local_addr: {} remote_addr: {}", local_addr, remote_addr); - let local_addr = CString::new(local_addr).unwrap(); - let remote_addr = CString::new(remote_addr).unwrap(); + let local_addr = CString::new(local_addr)?; + let remote_addr = CString::new(remote_addr)?; // Sasl server init. unsafe { err = sasl_server_init(ptr::null_mut(), appname.as_ptr()); @@ -387,7 +377,7 @@ impl ClientIoHandler { unsafe { err = sasl_setprop( security.saslconfig.sasl_conn, - SASL_SEC_PROPS.try_into().unwrap(), + SASL_SEC_PROPS.try_into()?, props as *const c_void, ); } @@ -406,9 +396,9 @@ impl ClientIoHandler { /// Send the mechlist to client. fn send_mech_list(&mut self) -> Result<()> { let err: c_int; - let prefix = CString::new("").unwrap(); - let sep = CString::new(",").unwrap(); - let suffix = CString::new("").unwrap(); + let prefix = CString::new("")?; + let sep = CString::new(",")?; + let suffix = CString::new("")?; let mut mechlist: *const c_char = ptr::null_mut(); let mut security = self.server.security_type.borrow_mut(); let client = self.client.clone(); @@ -431,14 +421,14 @@ impl ClientIoHandler { ))); } let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) }; - security.saslconfig.mech_list = String::from(mech_list.to_str().unwrap()); + security.saslconfig.mech_list = String::from(mech_list.to_str()?); let mut buf = Vec::new(); let len = security.saslconfig.mech_list.len(); buf.append(&mut (len as u32).to_be_bytes().to_vec()); buf.append(&mut security.saslconfig.mech_list.as_bytes().to_vec()); drop(security); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); Ok(()) } @@ -496,7 +486,7 @@ impl ClientIoHandler { )))); } let username = unsafe { CStr::from_ptr(val as *const c_char) }; - let username = String::from(username.to_str().unwrap()); + let username = String::from(username.to_str()?); let server = self.server.clone(); let security = server.security_type.borrow_mut(); diff --git a/vnc/src/client.rs b/vnc/src/client.rs index c36a142ac..7152806ea 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -387,16 +387,12 @@ impl ClientIoHandler { } if !locked_buffer.is_empty() { - vnc_flush_notify(&client); + vnc_flush(&client); } drop(locked_buffer); } - pub fn flush(&mut self) { - self.client_handle_write(); - } - /// Read buf from stream, return the size. fn read_msg(&mut self) -> Result { let mut buf = Vec::new(); @@ -556,7 +552,7 @@ impl ClientIoHandler { let mut buf = Vec::new(); buf.append(&mut (AuthState::Invalid as u32).to_be_bytes().to_vec()); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } @@ -589,7 +585,7 @@ impl ClientIoHandler { vnc_write(&client, buf.to_vec()); self.update_event_handler(1, ClientIoHandler::handle_auth); } - self.flush(); + vnc_flush(&client); Ok(()) } @@ -608,7 +604,7 @@ impl ClientIoHandler { break; } if client.addr != addr { - client.disconn_evt.lock().unwrap().write(1).unwrap(); + vnc_disconnect_start(client); len -= 1; } } @@ -635,7 +631,7 @@ impl ClientIoHandler { buf.append(&mut (APP_NAME.to_string().len() as u32).to_be_bytes().to_vec()); buf.append(&mut APP_NAME.to_string().as_bytes().to_vec()); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } @@ -676,7 +672,7 @@ impl ClientIoHandler { return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); } } - self.flush(); + vnc_flush(&client); Ok(()) } @@ -737,7 +733,7 @@ impl ClientIoHandler { let client = self.client.clone(); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); } /// Set image format. @@ -895,7 +891,7 @@ impl ClientIoHandler { // VNC display cursor define. display_cursor_define(&client, &server, &mut buf); vnc_write(&client, buf); - vnc_flush_notify(&client); + vnc_flush(&client); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } @@ -950,7 +946,7 @@ impl ClientIoHandler { } let client = self.client.clone(); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); } /// Read the data from the receiver buffer. @@ -1013,7 +1009,7 @@ impl EventNotifierHelper for ClientIoHandler { } // Do disconnection event. if client.conn_state.lock().unwrap().is_disconnect() { - client.disconn_evt.lock().unwrap().write(1).unwrap(); + vnc_disconnect_start(&client); } drop(locked_client_io); None @@ -1036,8 +1032,8 @@ impl EventNotifierHelper for ClientIoHandler { let client = locked_client_io.client.clone(); locked_client_io.client_handle_write(); // do disconnection event. - if client.conn_state.lock().unwrap().dis_conn { - client.disconn_evt.lock().unwrap().write(1).unwrap(); + if client.conn_state.lock().unwrap().is_disconnect() { + vnc_disconnect_start(&client); } drop(locked_client_io); None @@ -1347,7 +1343,22 @@ pub fn vnc_update_output_throttle(client: &Arc) { .set_limit(Some(offset as usize)); } -/// Consume the output buffer. -pub fn vnc_flush_notify(client: &Arc) { - client.write_fd.lock().unwrap().write(1).unwrap(); +/// Flush the output buffer. +pub fn vnc_flush(client: &Arc) { + client + .write_fd + .lock() + .unwrap() + .write(1) + .unwrap_or_else(|e| error!("Error occurrs during data flush:{:?}", e)); +} + +/// Disconnect for vnc client. +pub fn vnc_disconnect_start(client: &Arc) { + client + .disconn_evt + .lock() + .unwrap() + .write(1) + .unwrap_or_else(|e| error!("Error occurrs during disconnection: {:?}", e)); } diff --git a/vnc/src/console.rs b/vnc/src/console.rs index 777694936..aee13804d 100644 --- a/vnc/src/console.rs +++ b/vnc/src/console.rs @@ -464,7 +464,7 @@ pub fn console_close(con_id: Option) { unsafe { let len = CONSOLES.console_list.len(); - let id = con_id.unwrap(); + let id = con_id.unwrap_or(0); if id >= len { return; } diff --git a/vnc/src/server.rs b/vnc/src/server.rs index f96e5dc66..4b869163e 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -14,7 +14,7 @@ use crate::{ auth::SaslAuth, auth::{AuthState, SaslConfig, SubAuthState}, client::vnc_write, - client::{ClientIoHandler, ClientState}, + client::{vnc_flush, ClientIoHandler, ClientState}, console::DisplayMouse, input::KeyBoardState, pixman::{ @@ -345,8 +345,11 @@ impl VncSurface { let height = self.get_min_height() as usize; let g_bpl = self.guest_dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; let total_dirty_bits = height.checked_mul(g_bpl).unwrap_or(0); + let mut offset = self + .guest_dirty_bitmap + .find_next_bit(0) + .unwrap_or(total_dirty_bits); - let mut offset = self.guest_dirty_bitmap.find_next_bit(0).unwrap(); if offset >= total_dirty_bits { return dirty_num; } @@ -392,8 +395,11 @@ impl VncSurface { g_info.ptr = (g_info.ptr as usize + x * cmp_bytes) as *mut u8; dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes); y += 1; - offset = self.guest_dirty_bitmap.find_next_bit(y * g_bpl).unwrap(); - if offset >= (height as usize) * g_bpl { + offset = self + .guest_dirty_bitmap + .find_next_bit(y * g_bpl) + .unwrap_or(total_dirty_bits); + if offset >= total_dirty_bits { break; } } @@ -425,7 +431,7 @@ impl VncSurface { if !self .guest_dirty_bitmap .contain(x + y * VNC_BITMAP_WIDTH as usize) - .unwrap() + .unwrap_or(false) { x += 1; g_info.ptr = (g_info.ptr as usize + cmp_bytes) as *mut u8; @@ -434,7 +440,7 @@ impl VncSurface { } self.guest_dirty_bitmap .clear(x + y * VNC_BITMAP_WIDTH as usize) - .unwrap(); + .unwrap_or_else(|e| error!("Error occurrs during clearing the bitmap: {:?}", e)); let mut _cmp_bytes = cmp_bytes; if (x + 1) * cmp_bytes > line_bytes as usize { _cmp_bytes = line_bytes as usize - x * cmp_bytes; @@ -511,7 +517,7 @@ fn set_dirty_for_each_clients(x: usize, y: usize) { .lock() .unwrap() .set(x + y * VNC_BITMAP_WIDTH as usize) - .unwrap(); + .unwrap_or_else(|e| error!("{:?}", e)); } } @@ -539,7 +545,7 @@ pub fn handle_connection( server.clone(), ))); vnc_write(&client, "RFB 003.008\n".as_bytes().to_vec()); - client_io.lock().unwrap().flush(); + vnc_flush(&client); server .client_handlers .lock() diff --git a/vnc/src/vencrypt.rs b/vnc/src/vencrypt.rs index 94f9fe3c1..6ce81dfd7 100644 --- a/vnc/src/vencrypt.rs +++ b/vnc/src/vencrypt.rs @@ -12,7 +12,7 @@ use crate::{ auth::SubAuthState, - client::{vnc_write, ClientIoHandler}, + client::{vnc_flush, vnc_write, ClientIoHandler}, VncError, }; use anyhow::{anyhow, Result}; @@ -79,7 +79,7 @@ impl ClientIoHandler { // Reject version. buf.append(&mut (0_u8).to_be_bytes().to_vec()); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } else { let mut buf = Vec::new(); @@ -92,7 +92,7 @@ impl ClientIoHandler { vnc_write(&client, buf); } - self.flush(); + vnc_flush(&client); self.update_event_handler(4, ClientIoHandler::client_vencrypt_auth); Ok(()) } @@ -110,7 +110,7 @@ impl ClientIoHandler { // Reject auth. buf.append(&mut (0_u8).to_be_bytes().to_vec()); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); error!("Authentication failed"); return Err(anyhow!(VncError::AuthFailed(String::from( "Authentication failed" @@ -121,7 +121,7 @@ impl ClientIoHandler { // Accept auth. buf.append(&mut (1_u8).to_be_bytes().to_vec()); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); if let Some(tls_config) = self.server.security_type.borrow().tls_config.clone() { match rustls::ServerConnection::new(tls_config) { @@ -211,7 +211,7 @@ impl ClientIoHandler { SubAuthState::VncAuthVencryptX509None => { let buf = [0u8; 4]; vnc_write(&client, buf.to_vec()); - self.flush(); + vnc_flush(&client); self.expect = 1; self.msg_handler = ClientIoHandler::handle_client_init; } @@ -224,7 +224,7 @@ impl ClientIoHandler { buf.append(&mut (err_msg.len() as u32).to_be_bytes().to_vec()); buf.append(&mut err_msg.as_bytes().to_vec()); vnc_write(&client, buf); - self.flush(); + vnc_flush(&client); } error!("Unsupported subauth type"); return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( @@ -254,7 +254,7 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result }; let mut client_auth_roots = RootCertStore::empty(); for root in roots { - client_auth_roots.add(&root).unwrap(); + client_auth_roots.add(&root)?; } if CLIENT_REQUIRE_AUTH { AllowAnyAuthenticatedClient::new(client_auth_roots) @@ -294,7 +294,7 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result // Limit data size in one time. config.session_storage = rustls::server::ServerSessionMemoryCache::new(MAXIMUM_SESSION_STORAGE); // Tickets. - config.ticketer = rustls::Ticketer::new().unwrap(); + config.ticketer = rustls::Ticketer::new()?; config.alpn_protocols = Vec::new(); Ok(Arc::new(config)) @@ -349,8 +349,7 @@ fn load_certs(filepath: &str) -> Result> { } }; let mut reader = BufReader::new(certfile); - let certs = rustls_pemfile::certs(&mut reader) - .unwrap() + let certs = rustls_pemfile::certs(&mut reader)? .iter() .map(|v| rustls::Certificate(v.clone())) .collect(); diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 8059fa0cc..1e4b72a70 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -12,7 +12,7 @@ use crate::{ client::{ - desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush_notify, + desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush, vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, ENCODING_HEXTILE, ENCODING_RAW, }, @@ -122,7 +122,7 @@ impl DisplayChangeListenerOperations for VncInterface { // Cursor define. display_cursor_define(client, &server, &mut buf); vnc_write(client, buf); - vnc_flush_notify(client); + vnc_flush(client); client.dirty_bitmap.lock().unwrap().clear_all(); set_area_dirty( &mut client.dirty_bitmap.lock().unwrap(), @@ -233,7 +233,7 @@ impl DisplayChangeListenerOperations for VncInterface { let mut buf: Vec = Vec::new(); display_cursor_define(client, &server, &mut buf); vnc_write(client, buf); - vnc_flush_notify(client); + vnc_flush(client); } } } @@ -348,9 +348,8 @@ fn start_vnc_thread() -> Result<()> { let client = rect_info.client; vnc_write(&client, buf); - vnc_flush_notify(&client); - }) - .unwrap(); + vnc_flush(&client); + })?; Ok(()) } -- Gitee From 48e42cf04e3a5b6ef614b1de9168c0d3ec77730e Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 27 Jan 2023 09:25:10 +0800 Subject: [PATCH 0742/2187] VNC: Optimize the architecture of console Optimize the architecture of console. 1. lock the two global variables of CONSOLES and DISPLAY_STATE when using them, so we can add support for multi-threading. 2. Add two unit tests for console. Signed-off-by: Xiao Ye --- devices/src/legacy/ramfb.rs | 29 +- virtio/src/gpu.rs | 25 +- vnc/src/console.rs | 621 ++++++++++++++++++++++-------------- vnc/src/input.rs | 6 +- vnc/src/server.rs | 15 +- vnc/src/vnc.rs | 24 +- 6 files changed, 437 insertions(+), 283 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index fae72b71c..78e2c02b8 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -18,13 +18,12 @@ use anyhow::Context; use drm_fourcc::DrmFourcc; use log::error; use std::mem::size_of; -use std::rc::Rc; use std::sync::{Arc, Mutex}; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; use vnc::console::{ - console_init, display_graphic_update, display_replace_surface, get_console_by_id, - DisplaySurface, HardWareOperations, + console_init, display_graphic_update, display_replace_surface, DisplayConsole, DisplaySurface, + HardWareOperations, }; const BYTES_PER_PIXELS: u32 = 8; @@ -185,20 +184,24 @@ impl FwCfgWriteCallback for RamfbState { self.create_display_surface(width, height, format, stride, addr); - let ramfb_opts = Rc::new(RamfbInterface::default()); - let con_id = console_init(ramfb_opts); - display_replace_surface(con_id, self.surface); + let ramfb_opts = Arc::new(RamfbInterface { + width: width as i32, + height: height as i32, + }); + let con = console_init(ramfb_opts); + display_replace_surface(&con, self.surface) + .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); } } -#[derive(Default)] -pub struct RamfbInterface {} +pub struct RamfbInterface { + width: i32, + height: i32, +} impl HardWareOperations for RamfbInterface { - fn hw_update(&self, con_id: Option) { - let con = get_console_by_id(con_id); - if let Some(c) = con { - display_graphic_update(con_id, 0, 0, c.width, c.height); - } + fn hw_update(&self, con: Arc>) { + display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, self.width, self.height) + .unwrap_or_else(|e| error!("Error occurs during graphic updating: {:?}", e)); } } diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 1311f1d24..3c82d7913 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -34,7 +34,7 @@ use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; use util::byte_code::ByteCode; @@ -54,7 +54,7 @@ use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use vnc::console::{ console_close, console_init, display_cursor_define, display_graphic_update, - display_replace_surface, DisplayMouse, DisplaySurface, HardWareOperations, + display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, }; // number of virtqueues @@ -364,7 +364,7 @@ impl ByteCode for VirtioGpuUpdateCursor {} #[derive(Default)] struct GpuScanout { - con_id: Option, + con: Option>>, surface: Option, mouse: Option, width: u32, @@ -446,7 +446,8 @@ fn create_surface( // update surface in scanout. scanout.surface = Some(surface); pixman_image_unref(rect); - display_replace_surface(scanout.con_id, scanout.surface); + display_replace_surface(&scanout.con, scanout.surface) + .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); } }; surface @@ -497,7 +498,8 @@ fn disable_scanout(scanout: &mut GpuScanout) { return; } // TODO: present 'Guest disabled display.' in surface. - display_replace_surface(scanout.con_id, None); + display_replace_surface(&scanout.con, None) + .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); scanout.clear(); } @@ -623,7 +625,7 @@ impl GpuIoHandler { } } if let Some(mouse) = &mut scanout.mouse { - display_cursor_define(scanout.con_id, mouse); + display_cursor_define(&scanout.con, mouse)?; } scanout.cursor = info_cursor; } else { @@ -981,12 +983,12 @@ impl GpuIoHandler { ); let extents = pixman_region_extents(final_reg_ptr); display_graphic_update( - scanout.con_id, + &scanout.con, (*extents).x1 as i32, (*extents).y1 as i32, ((*extents).x2 - (*extents).x1) as i32, ((*extents).y2 - (*extents).y1) as i32, - ); + )?; pixman_region_fini(rect_reg_ptr); pixman_region_fini(final_reg_ptr); } @@ -1401,7 +1403,8 @@ impl GpuIoHandler { impl Drop for GpuIoHandler { fn drop(&mut self) { for scanout in &self.scanouts { - console_close(scanout.con_id); + console_close(&scanout.con) + .unwrap_or_else(|e| error!("Error occurs during console closing:{:?}", e)); } while !self.resources_list.is_empty() { @@ -1625,8 +1628,8 @@ impl VirtioDevice for Gpu { let mut scanouts = vec![]; for _i in 0..VIRTIO_GPU_MAX_SCANOUTS { let mut scanout = GpuScanout::default(); - let gpu_opts = Rc::new(GpuOpts::default()); - scanout.con_id = console_init(gpu_opts); + let gpu_opts = Arc::new(GpuOpts::default()); + scanout.con = console_init(gpu_opts); scanouts.push(scanout); } diff --git a/vnc/src/console.rs b/vnc/src/console.rs index aee13804d..eb1ecf5eb 100644 --- a/vnc/src/console.rs +++ b/vnc/src/console.rs @@ -14,16 +14,21 @@ use crate::pixman::{ get_image_height, get_image_width, pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, ColorNames, COLOR_TABLE_RGB, }; +use anyhow::Result; use log::error; use machine_manager::event_loop::EventLoop; use once_cell::sync::Lazy; use std::{ cmp, ptr, - rc::Rc, - sync::{Arc, Mutex}, + sync::{Arc, Mutex, Weak}, }; use util::pixman::{pixman_format_code_t, pixman_image_create_bits, pixman_image_t}; +static CONSOLES: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new(ConsoleList::new()))); +static DISPLAY_STATE: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new(DisplayState::new()))); + /// Width of font. const FONT_WIDTH: i32 = 8; /// Height of font. @@ -86,7 +91,7 @@ pub trait DisplayChangeListenerOperations { /// Switch the image in display surface. fn dpy_switch(&self, _surface: &DisplaySurface) {} /// Refresh the image. - fn dpy_refresh(&self, _dcl: &mut DisplayChangeListener) {} + fn dpy_refresh(&self, _dcl: &Arc>) {} /// Update image. fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) {} /// Update the cursor data. @@ -96,22 +101,24 @@ pub trait DisplayChangeListenerOperations { /// Callback functions registered by graphic hardware. pub trait HardWareOperations { /// Update image. - fn hw_update(&self, _con_id: Option) {} + fn hw_update(&self, _con: Arc>) {} } /// Listen to the change of image and call the related /// interface to update the image on user's desktop. pub struct DisplayChangeListener { pub con_id: Option, + pub dcl_id: Option, pub active: bool, pub update_interval: u64, - pub dpy_opts: Rc, + pub dpy_opts: Arc, } impl DisplayChangeListener { - pub fn new(dpy_opts: Rc) -> Self { + pub fn new(dcl_id: Option, dpy_opts: Arc) -> Self { Self { con_id: None, + dcl_id, active: false, update_interval: 0, dpy_opts, @@ -122,17 +129,25 @@ impl DisplayChangeListener { /// Graphic hardware can register a console during initialization /// and store the information of images in this structure. pub struct DisplayConsole { + pub con_id: Option, pub width: i32, pub height: i32, pub surface: Option, - dev_opts: Rc, + pub console_list: Weak>, + dev_opts: Arc, } impl DisplayConsole { - pub fn new(dev_opts: Rc) -> Self { + pub fn new( + con_id: Option, + console_list: Weak>, + dev_opts: Arc, + ) -> Self { Self { + con_id, width: DEFAULT_SURFACE_WIDTH, height: DEFAULT_SURFACE_HEIGHT, + console_list, surface: None, dev_opts, } @@ -145,15 +160,22 @@ pub struct DisplayState { pub interval: u64, /// Whether there is a refresh task. is_refresh: bool, + /// A list of DisplayChangeListeners. + listeners: Vec>>>, /// Total number of refresh task. refresh_num: i32, } +// SAFETY: The Arc in rust doesn't impl Send, it will be delivered only once during initialization process, +// and only be saved in the single thread. So implement Send is safe. +unsafe impl Send for DisplayState {} + impl DisplayState { fn new() -> Self { Self { interval: DISPLAY_UPDATE_INTERVAL_DEFAULT, is_refresh: false, + listeners: Vec::new(), refresh_num: 0, } } @@ -163,9 +185,16 @@ impl DisplayState { /// If no console is specified, the activate console will be used. pub struct ConsoleList { pub activate_id: Option, - pub console_list: Vec>, + pub console_list: Vec>>>, } +// SAFETY: +// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image by this pointer. +// 2. The Arc in rust doesn't impl Send, it will be delivered only once during initialization process, +// and only be saved in the single thread. +// So implement Send is safe. +unsafe impl Send for ConsoleList {} + impl ConsoleList { fn new() -> Self { Self { @@ -173,34 +202,52 @@ impl ConsoleList { console_list: Vec::new(), } } + + /// Get the console by id. + fn get_console_by_id(&mut self, con_id: Option) -> Option>> { + if con_id.is_none() && self.activate_id.is_none() { + return None; + } + + let mut target_id: usize = 0; + if let Some(id) = con_id { + target_id = id; + } else if let Some(id) = self.activate_id { + target_id = id; + } + + self.console_list.get(target_id)?.clone() + } } /// Refresh display image. pub fn display_refresh() { let mut dcl_interval: u64; let mut interval: u64 = DISPLAY_UPDATE_INTERVAL_MAX; + let mut locked_state = DISPLAY_STATE.lock().unwrap(); + let mut related_listeners: Vec>> = vec![]; + for dcl in &mut locked_state.listeners.iter_mut().flatten() { + related_listeners.push(dcl.clone()); + } + drop(locked_state); - // Update refresh interval. - unsafe { - for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { - if !dcl.active { - continue; - } - let dcl_opts = dcl.dpy_opts.clone(); - (*dcl_opts).dpy_refresh(dcl); + for dcl in &mut related_listeners.iter() { + let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); + (*dcl_opts).dpy_refresh(dcl); - dcl_interval = dcl.update_interval; - if dcl_interval == 0 { - dcl_interval = DISPLAY_UPDATE_INTERVAL_MAX; - } + // Update refresh interval. + dcl_interval = dcl.lock().unwrap().update_interval; + if dcl_interval == 0 { + dcl_interval = DISPLAY_UPDATE_INTERVAL_MAX; + } - if interval > dcl_interval { - interval = dcl_interval; - } + if interval > dcl_interval { + interval = dcl_interval } } + let mut locked_state = DISPLAY_STATE.lock().unwrap(); locked_state.interval = interval; if locked_state.interval != 0 { locked_state.is_refresh = true; @@ -223,61 +270,81 @@ pub fn setup_refresh(update_interval: u64) { } /// Switch the image of surface in display. -pub fn display_replace_surface(con_id: Option, surface: Option) { - let _locked_state = DISPLAY_STATE.lock().unwrap(); - let consoles = get_console_by_id(con_id); - let mut con: &mut DisplayConsole; - if let Some(c) = consoles { - con = c; - } else { - return; - } - +pub fn display_replace_surface( + console: &Option>>, + surface: Option, +) -> Result<()> { + let con = match console.as_ref().and_then(|c| c.upgrade()) { + Some(c) => c, + None => return Ok(()), + }; + + let mut locked_con = con.lock().unwrap(); + let old_surface = locked_con.surface; if surface.is_none() { // Create a place holder message. - con.surface = - create_msg_surface(con.width, con.height, "Display is not active.".to_string()); + locked_con.surface = create_msg_surface( + locked_con.width, + locked_con.height, + "Display is not active.".to_string(), + ); } else { - con.surface = surface; + locked_con.surface = surface; } - if let Some(s) = con.surface { - con.width = get_image_width(s.image); - con.height = get_image_height(s.image); + if let Some(s) = locked_con.surface { + locked_con.width = get_image_width(s.image); + locked_con.height = get_image_height(s.image); } + let con_id = locked_con.con_id; + if let Some(s) = old_surface { + unref_pixman_image(s.image); + } + drop(locked_con); - unsafe { - let activate_id = CONSOLES.activate_id; - for dcl in DISPLAY_LISTS.iter_mut().flatten() { - let dcl_id = if dcl.con_id.is_none() { - activate_id - } else { - dcl.con_id - }; - - if con_id == dcl_id { - let dcl_ops = dcl.dpy_opts.clone(); - if let Some(s) = &con.surface { - (*dcl_ops).dpy_switch(s); - } - } + let mut related_listeners: Vec>> = vec![]; + let activate_id = CONSOLES.lock().unwrap().activate_id; + let locked_state = DISPLAY_STATE.lock().unwrap(); + for dcl in locked_state.listeners.iter().flatten() { + let mut dcl_id = dcl.lock().unwrap().con_id; + if dcl_id.is_none() { + dcl_id = activate_id; + } + + if con_id == dcl_id { + related_listeners.push(dcl.clone()); } } - drop(_locked_state); + drop(locked_state); + + for dcl in related_listeners.iter() { + let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); + if let Some(s) = &con.lock().unwrap().surface.clone() { + (*dcl_opts).dpy_switch(s); + } + } + Ok(()) } /// Update area of the image. /// `x` `y` `w` `h` marke the area of image. -pub fn display_graphic_update(con_id: Option, x: i32, y: i32, w: i32, h: i32) { - if con_id.is_none() { - return; - } - +pub fn display_graphic_update( + console: &Option>>, + x: i32, + y: i32, + w: i32, + h: i32, +) -> Result<()> { + let con = match console.as_ref().and_then(|c| c.upgrade()) { + Some(c) => c, + None => return Ok(()), + }; let mut width: i32 = w; let mut height: i32 = h; - if let Some(con) = get_console_by_id(con_id) { - width = con.width; - height = con.height; + let locked_con = con.lock().unwrap(); + if let Some(s) = locked_con.surface { + width = get_image_width(s.image); + height = get_image_height(s.image); } let mut x = cmp::max(x, 0); let mut y = cmp::max(y, 0); @@ -285,22 +352,29 @@ pub fn display_graphic_update(con_id: Option, x: i32, y: i32, w: i32, h: y = cmp::min(y, height); let w = cmp::min(w, width - x); let h = cmp::min(h, height - y); + let con_id = locked_con.con_id; + drop(locked_con); - unsafe { - let activate_id = CONSOLES.activate_id; - for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { - let dcl_id = if dcl.con_id.is_none() { - activate_id - } else { - dcl.con_id - }; - - if con_id == dcl_id { - let dcl_ops = dcl.dpy_opts.clone(); - (*dcl_ops).dpy_image_update(x, y, w, h); - } + let activate_id = CONSOLES.lock().unwrap().activate_id; + let mut related_listeners: Vec>> = vec![]; + let locked_state = DISPLAY_STATE.lock().unwrap(); + for dcl in locked_state.listeners.iter().flatten() { + let mut dcl_id = dcl.lock().unwrap().con_id; + if dcl_id.is_none() { + dcl_id = activate_id; } + + if con_id == dcl_id { + related_listeners.push(dcl.clone()); + } + } + drop(locked_state); + + for dcl in related_listeners.iter() { + let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); + (*dcl_opts).dpy_image_update(x, y, w, h); } + Ok(()) } /// Update cursor data in dispaly. @@ -309,73 +383,75 @@ pub fn display_graphic_update(con_id: Option, x: i32, y: i32, w: i32, h: /// /// * `con_id` - console id in console list. /// * `cursor` - data of curosr image. -pub fn display_cursor_define(con_id: Option, cursor: &mut DisplayMouse) { - unsafe { - let activate_id = CONSOLES.activate_id; - for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { - let dcl_id = if dcl.con_id.is_none() { - activate_id - } else { - dcl.con_id - }; - - if con_id == dcl_id { - let dcl_ops = dcl.dpy_opts.clone(); - (*dcl_ops).dpy_cursor_update(cursor); - } +pub fn display_cursor_define( + console: &Option>>, + cursor: &mut DisplayMouse, +) -> Result<()> { + let con = match console.as_ref().and_then(|c| c.upgrade()) { + Some(c) => c, + None => return Ok(()), + }; + let activate_id = CONSOLES.lock().unwrap().activate_id; + let con_id = con.lock().unwrap().con_id; + let mut related_listeners: Vec>> = vec![]; + let locked_state = DISPLAY_STATE.lock().unwrap(); + for dcl in locked_state.listeners.iter().flatten() { + let mut dcl_id = dcl.lock().unwrap().con_id; + if dcl_id.is_none() { + dcl_id = activate_id; } - } -} -pub fn graphic_hardware_update(con_id: Option) { - let id: Option; - if con_id.is_none() { - unsafe { - id = CONSOLES.activate_id; + if con_id == dcl_id { + related_listeners.push(dcl.clone()); } - } else { - id = con_id; } + drop(locked_state); + + for dcl in related_listeners.iter() { + let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); + (*dcl_opts).dpy_cursor_update(cursor); + } + Ok(()) +} - if let Some(con) = get_console_by_id(id) { - let con_opts = con.dev_opts.clone(); - (*con_opts).hw_update(id); +pub fn graphic_hardware_update(con_id: Option) { + let console = CONSOLES.lock().unwrap().get_console_by_id(con_id); + if let Some(con) = console { + let con_opts = con.lock().unwrap().dev_opts.clone(); + (*con_opts).hw_update(con); } } /// Register a dcl and return the id. -pub fn register_display(dcl_opts: Rc) -> Option { +pub fn register_display(dcl: &Arc>) -> Result<()> { let mut dcl_id = 0; let mut locked_state = DISPLAY_STATE.lock().unwrap(); - let mut dcl = DisplayChangeListener::new(dcl_opts.clone()); - dcl.active = true; - let con_id = dcl.con_id; - unsafe { - let len = DISPLAY_LISTS.len(); - for dcl in &mut DISPLAY_LISTS.iter_mut() { - if dcl.is_none() { - break; - } - dcl_id += 1; - } - - if dcl_id < len { - DISPLAY_LISTS[dcl_id] = Some(dcl); - } else { - DISPLAY_LISTS.push(Some(dcl)); + let len = locked_state.listeners.len(); + for dcl in &mut locked_state.listeners.iter() { + if dcl.is_none() { + break; } + dcl_id += 1; + } + if dcl_id < len { + locked_state.listeners[dcl_id] = Some(dcl.clone()); + } else { + locked_state.listeners.push(Some(dcl.clone())); } - locked_state.refresh_num += 1; // Register the clock and execute the scheduled refresh event. if !locked_state.is_refresh && locked_state.interval != 0 { locked_state.is_refresh = true; setup_refresh(locked_state.interval); } + drop(locked_state); + dcl.lock().unwrap().dcl_id = Some(dcl_id); + let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); - let console = get_console_by_id(con_id); + let con_id = dcl.lock().unwrap().con_id; + let console = CONSOLES.lock().unwrap().get_console_by_id(con_id); if let Some(con) = console { - if let Some(surface) = &mut con.surface { + if let Some(surface) = &mut con.lock().unwrap().surface.clone() { (*dcl_opts).dpy_switch(surface); } } else { @@ -389,156 +465,148 @@ pub fn register_display(dcl_opts: Rc) -> Op } } - drop(locked_state); - Some(dcl_id) + Ok(()) } /// Unregister display change listener. -pub fn unregister_display(id: Option) { - if id.is_none() { - return; - } - +pub fn unregister_display(dcl: &Option>>) -> Result<()> { + let dcl = match dcl.as_ref().and_then(|d| d.upgrade()) { + Some(d) => d, + None => return Ok(()), + }; + let dcl_id = dcl.lock().unwrap().dcl_id; let mut locked_state = DISPLAY_STATE.lock().unwrap(); - unsafe { - let len = DISPLAY_LISTS.len(); - if let Some(i) = id { - if i < len { - DISPLAY_LISTS[i] = None; - } - } + let len = locked_state.listeners.len(); + let id = dcl_id.unwrap_or(len); + if id >= len { + return Ok(()); } - + locked_state.listeners[id] = None; // Stop refreshing if the current refreshing num is 0 locked_state.refresh_num -= 1; if locked_state.refresh_num <= 0 { locked_state.is_refresh = false; } - drop(locked_state); + Ok(()) } /// Create a console and add into a gloabl list. Then returen a console id /// for later finding the assigned console. -pub fn console_init(dev_opts: Rc) -> Option { - let locked_state = DISPLAY_STATE.lock().unwrap(); - let mut new_console = DisplayConsole::new(dev_opts); +pub fn console_init(dev_opts: Arc) -> Option>> { + let mut locked_consoles = CONSOLES.lock().unwrap(); + let len = locked_consoles.console_list.len(); + let mut con_id = len; + for idx in 0..len { + if locked_consoles.console_list[idx].is_none() { + con_id = idx; + break; + } + } + let mut new_console = + DisplayConsole::new(Some(con_id), Arc::downgrade(&CONSOLES), dev_opts.clone()); new_console.surface = create_msg_surface( DEFAULT_SURFACE_WIDTH, DEFAULT_SURFACE_HEIGHT, "Guest has not initialized the display yet.".to_string(), ); - - let mut con_id: usize; - unsafe { - let len = CONSOLES.console_list.len(); - con_id = len; - for idx in 0..len { - if CONSOLES.console_list[idx].is_none() { - con_id = idx; - break; - } - } - if con_id < len { - CONSOLES.console_list[con_id] = Some(new_console); - } else { - CONSOLES.console_list.push(Some(new_console)); - } - - if CONSOLES.activate_id.is_none() { - CONSOLES.activate_id = Some(con_id); - } + new_console.width = DEFAULT_SURFACE_WIDTH; + new_console.height = DEFAULT_SURFACE_HEIGHT; + let console = Arc::new(Mutex::new(new_console)); + if con_id < len { + locked_consoles.console_list[con_id] = Some(console.clone()) + } else { + locked_consoles.console_list.push(Some(console.clone())); } + if locked_consoles.activate_id.is_none() { + locked_consoles.activate_id = Some(con_id); + } + drop(locked_consoles); - drop(locked_state); - display_replace_surface(Some(con_id), None); - - Some(con_id) + let con = Arc::downgrade(&console); + display_replace_surface(&Some(con.clone()), None) + .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); + Some(con) } /// Close a console. -pub fn console_close(con_id: Option) { +pub fn console_close(console: &Option>>) -> Result<()> { + let con = match console.as_ref().and_then(|c| c.upgrade()) { + Some(c) => c, + None => return Ok(()), + }; + let con_id = con.lock().unwrap().con_id; + let mut locked_consoles = CONSOLES.lock().unwrap(); if con_id.is_none() { - return; - } - - unsafe { - let len = CONSOLES.console_list.len(); - let id = con_id.unwrap_or(0); - if id >= len { - return; - } - CONSOLES.console_list[id] = None; - if con_id != CONSOLES.activate_id { - return; - } - CONSOLES.activate_id = None; - for i in 0..len { - if CONSOLES.console_list[i].is_some() { - CONSOLES.activate_id = Some(i); - break; + return Ok(()); + } + let len = locked_consoles.console_list.len(); + let id = con_id.unwrap_or(len); + if id >= len { + return Ok(()); + } + locked_consoles.console_list[id] = None; + match locked_consoles.activate_id { + Some(activate_id) if id == activate_id => { + locked_consoles.activate_id = None; + for i in 0..len { + if locked_consoles.console_list[i].is_some() { + locked_consoles.activate_id = Some(i); + break; + } } } + _ => {} } + drop(locked_consoles); + Ok(()) } /// Select the default display device. /// If con_id is none, then do nothing. -pub fn console_select(con_id: Option) { - if con_id.is_none() { - return; - } - - let _locked_state = DISPLAY_STATE.lock().unwrap(); - unsafe { - if con_id == CONSOLES.activate_id { - return; - } - - if let Some(con) = get_console_by_id(con_id) { - CONSOLES.activate_id = con_id; - for dcl in &mut DISPLAY_LISTS.iter_mut().flatten() { - if dcl.con_id.is_some() { - continue; - } - - let dpy_opts = dcl.dpy_opts.clone(); - if let Some(s) = &con.surface { - (*dpy_opts).dpy_switch(s); - } - } - - display_graphic_update(con_id, 0, 0, con.width, con.height); +pub fn console_select(con_id: Option) -> Result<()> { + let mut locked_consoles = CONSOLES.lock().unwrap(); + if locked_consoles.activate_id == con_id { + return Ok(()); + } + let activate_console: Option>> = match con_id { + Some(id) if locked_consoles.console_list.get(id).is_some() => { + locked_consoles.activate_id = Some(id); + locked_consoles.console_list[id].clone() } + _ => None, + }; + let activate_id: Option = locked_consoles.activate_id; + if activate_id.is_none() { + return Ok(()); } -} + drop(locked_consoles); -/// Get the console by id. -pub fn get_console_by_id(con_id: Option) -> Option<&'static mut DisplayConsole> { - unsafe { - let mut target_id: usize = 0; - if con_id.is_none() && CONSOLES.activate_id.is_none() { - return None; + let mut related_listeners: Vec>> = vec![]; + let mut locked_state = DISPLAY_STATE.lock().unwrap(); + for dcl in locked_state.listeners.iter_mut().flatten() { + if dcl.lock().unwrap().con_id.is_some() { + continue; } - if let Some(id) = con_id { - target_id = id; - } else if let Some(id) = CONSOLES.activate_id { - target_id = id; - } + related_listeners.push(dcl.clone()); + } + drop(locked_state); - if let Some(con) = CONSOLES.console_list.get_mut(target_id) { - match con { - Some(c) => { - return Some(c); - } - None => { - return None; - } - } + let con = match activate_console { + Some(c) => c, + None => return Ok(()), + }; + let width = con.lock().unwrap().width; + let height = con.lock().unwrap().height; + for dcl in related_listeners { + let dpy_opts = dcl.lock().unwrap().dpy_opts.clone(); + if let Some(s) = &mut con.lock().unwrap().surface { + (*dpy_opts).dpy_switch(s); } - None } + + display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, width, height) } /// Create a default image to display messages. @@ -588,7 +656,88 @@ fn create_msg_surface(width: i32, height: i32, msg: String) -> Option = Lazy::new(ConsoleList::new); -static mut DISPLAY_LISTS: Lazy>> = Lazy::new(Vec::new); -static DISPLAY_STATE: Lazy>> = - Lazy::new(|| Arc::new(Mutex::new(DisplayState::new()))); +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::VmConfig; + pub struct DclOpts {} + impl DisplayChangeListenerOperations for DclOpts {} + struct HwOpts {} + impl HardWareOperations for HwOpts {} + + #[test] + fn test_console_select() { + let con_opts = Arc::new(HwOpts {}); + let con_0 = console_init(con_opts.clone()); + assert_eq!( + con_0 + .clone() + .unwrap() + .upgrade() + .unwrap() + .lock() + .unwrap() + .con_id, + Some(0) + ); + let con_1 = console_init(con_opts.clone()); + assert_eq!( + con_1.unwrap().upgrade().unwrap().lock().unwrap().con_id, + Some(1) + ); + let con_2 = console_init(con_opts.clone()); + assert_eq!( + con_2.unwrap().upgrade().unwrap().lock().unwrap().con_id, + Some(2) + ); + assert!(console_close(&con_0).is_ok()); + let con_3 = console_init(con_opts.clone()); + assert_eq!( + con_3.unwrap().upgrade().unwrap().lock().unwrap().con_id, + Some(0) + ); + assert!(console_select(Some(0)).is_ok()); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(0)); + assert!(console_select(Some(1)).is_ok()); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(1)); + assert!(console_select(Some(2)).is_ok()); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(2)); + assert!(console_select(Some(3)).is_ok()); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(2)); + assert!(console_select(None).is_ok()); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(2)); + } + + #[test] + fn test_register_display() { + let vm_config = VmConfig::default(); + assert!(EventLoop::object_init(&vm_config.iothreads).is_ok()); + let dcl_opts = Arc::new(DclOpts {}); + let dcl_0 = Arc::new(Mutex::new(DisplayChangeListener::new( + None, + dcl_opts.clone(), + ))); + let dcl_1 = Arc::new(Mutex::new(DisplayChangeListener::new( + None, + dcl_opts.clone(), + ))); + let dcl_2 = Arc::new(Mutex::new(DisplayChangeListener::new( + None, + dcl_opts.clone(), + ))); + let dcl_3 = Arc::new(Mutex::new(DisplayChangeListener::new( + None, + dcl_opts.clone(), + ))); + + assert!(register_display(&dcl_0).is_ok()); + assert_eq!(dcl_0.lock().unwrap().dcl_id, Some(0)); + assert!(register_display(&dcl_1).is_ok()); + assert_eq!(dcl_1.lock().unwrap().dcl_id, Some(1)); + assert!(register_display(&dcl_2).is_ok()); + assert_eq!(dcl_2.lock().unwrap().dcl_id, Some(2)); + assert!(unregister_display(&Some(Arc::downgrade(&dcl_0))).is_ok()); + assert!(register_display(&dcl_3).is_ok()); + assert_eq!(dcl_3.lock().unwrap().dcl_id, Some(0)); + } +} diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 6b2bf9128..356a1bafa 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -195,7 +195,6 @@ impl ClientIoHandler { keysym += UPPERCASE_TO_LOWERCASE; } let mut kbd_state = server.keyboard_state.borrow_mut(); - let dcl_id = self.server.display_listener.lock().unwrap().dcl_id; let keycode: u16 = match server.keysym2keycode.get(&(keysym as u16)) { Some(k) => *k, @@ -206,12 +205,13 @@ impl ClientIoHandler { // Switch to the corresponding display device. if (KEYCODE_1..KEYCODE_9 + 1).contains(&keycode) && down - && dcl_id.is_some() + && self.server.display_listener.is_some() && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) { kbd_state.keyboard_state_reset(); - console_select(Some((keycode - KEYCODE_1) as usize)); + console_select(Some((keycode - KEYCODE_1) as usize)) + .unwrap_or_else(|e| error!("{:?}", e)); } if let Err(e) = kbd_state.keyboard_state_update(keycode, down) { diff --git a/vnc/src/server.rs b/vnc/src/server.rs index 4b869163e..b72b48cb9 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -15,7 +15,7 @@ use crate::{ auth::{AuthState, SaslConfig, SubAuthState}, client::vnc_write, client::{vnc_flush, ClientIoHandler, ClientState}, - console::DisplayMouse, + console::{DisplayChangeListener, DisplayMouse}, input::KeyBoardState, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, @@ -43,7 +43,7 @@ use std::{ os::unix::prelude::{AsRawFd, RawFd}, ptr, rc::Rc, - sync::{Arc, Mutex}, + sync::{Arc, Mutex, Weak}, }; use util::{ bitmap::Bitmap, @@ -74,7 +74,7 @@ pub struct VncServer { /// Data for cursor image. pub vnc_cursor: Arc>, /// Display Change Listener. - pub display_listener: Arc>, + pub display_listener: Option>>, /// Connection limit. pub conn_limits: usize, } @@ -88,6 +88,7 @@ impl VncServer { guest_image: *mut pixman_image_t, keyboard_state: Rc>, keysym2keycode: HashMap, + display_listener: Option>>, ) -> Self { VncServer { client_handlers: Arc::new(Mutex::new(HashMap::new())), @@ -96,7 +97,7 @@ impl VncServer { keysym2keycode, vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), vnc_cursor: Arc::new(Mutex::new(VncCursor::default())), - display_listener: Arc::new(Mutex::new(DisplayChangeListener::default())), + display_listener, conn_limits: CONNECTION_LIMIT, } } @@ -279,12 +280,6 @@ impl SecurityType { } } -/// Display change listener registered in console. -#[derive(Default)] -pub struct DisplayChangeListener { - pub dcl_id: Option, -} - /// Image date of cursor. #[derive(Default)] pub struct VncCursor { diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 1e4b72a70..9a5b617c7 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -138,7 +138,7 @@ impl DisplayChangeListenerOperations for VncInterface { } /// Refresh server_image to guest_image. - fn dpy_refresh(&self, dcl: &mut DisplayChangeListener) { + fn dpy_refresh(&self, dcl: &Arc>) { if VNC_SERVERS.lock().unwrap().is_empty() { return; } @@ -146,10 +146,11 @@ impl DisplayChangeListenerOperations for VncInterface { if server.client_handlers.lock().unwrap().is_empty() { return; } - graphic_hardware_update(dcl.con_id); + let con_id = dcl.lock().unwrap().con_id; + graphic_hardware_update(con_id); // Update refresh interval. - let mut update_interval = dcl.update_interval; + let mut update_interval = dcl.lock().unwrap().update_interval; let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); if dirty_num != 0 { update_interval /= 2; @@ -162,7 +163,7 @@ impl DisplayChangeListenerOperations for VncInterface { update_interval = DISPLAY_UPDATE_INTERVAL_MAX; } } - dcl.update_interval = update_interval; + dcl.lock().unwrap().update_interval = update_interval; let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { @@ -275,24 +276,27 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { let keyboard_state: Rc> = Rc::new(RefCell::new(KeyBoardState::new(max_keycode as usize))); + let vnc_opts = Arc::new(VncInterface::default()); + let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, vnc_opts))); + let server = Arc::new(VncServer::new( get_client_image(), keyboard_state, keysym2keycode, + Some(Arc::downgrade(&dcl)), )); + // Parameter configuation for VncServeer. make_server_config(&server, vnc_cfg, object)?; // Add an VncServer. add_vnc_server(server.clone()); - // Register the event to listen for client's connection. - let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server.clone()))); - // Register in display console. - let vnc_opts = Rc::new(VncInterface::default()); - let dcl_id = register_display(vnc_opts); - server.display_listener.lock().unwrap().dcl_id = dcl_id; + register_display(&dcl)?; + + // Register the event to listen for client's connection. + let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server))); // Vnc_thread: a thread to send the framebuffer start_vnc_thread()?; -- Gitee From 85b040980234f94e7407f987f51cb84ad25b6d09 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 28 Jan 2023 01:41:00 +0800 Subject: [PATCH 0743/2187] VNC: Fix set_encodings Vnc Server should set the enc and feature to 0 for the each time before receviing the request of set_encodings. Signed-off-by: Xiao Ye --- vnc/src/client.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 7152806ea..15f0bbb8b 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -822,6 +822,8 @@ impl ClientIoHandler { } let mut locked_dpm = self.client.client_dpm.lock().unwrap(); + locked_dpm.feature = 0; + locked_dpm.enc = 0; while num_encoding > 0 { let offset = (4 * num_encoding) as usize; let enc = i32::from_be_bytes([ -- Gitee From 9e62cb801e9bdfe13d78b68194bcf1071cba0fef Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 27 Jan 2023 00:21:53 +0800 Subject: [PATCH 0744/2187] VNC: Check unsafe implementation Check and modify unsafe implementation in vnc. Signed-off-by: Xiao Ye --- vnc/src/auth.rs | 37 +++++++++++----- vnc/src/console.rs | 11 ++--- vnc/src/encoding/enc_hextile.rs | 55 +++++++++++------------ vnc/src/pixman.rs | 78 ++++++++++++++++++++++++++++++++- vnc/src/server.rs | 76 +++++++++++--------------------- vnc/src/vnc.rs | 42 +++++++++--------- 6 files changed, 178 insertions(+), 121 deletions(-) diff --git a/vnc/src/auth.rs b/vnc/src/auth.rs index ac3fd4d87..bc9513dc1 100644 --- a/vnc/src/auth.rs +++ b/vnc/src/auth.rs @@ -200,15 +200,16 @@ impl ClientIoHandler { let server = self.server.clone(); let client = self.client.clone(); let mut security = server.security_type.borrow_mut(); - let err: c_int; let mut serverout: *const c_char = ptr::null_mut(); let mut serverout_len: c_uint = 0; let mech_name = CString::new(security.saslconfig.mech_name.as_str())?; // Start authentication. - if security.saslconfig.sasl_stage == SaslStage::SaslServerStart { - unsafe { - err = sasl_server_start( + let err: c_int = match security.saslconfig.sasl_stage { + // SAFETY: sasl_server_start() and sasl_server_step() is C function. All parameters passed of the + // function have been checked. Memory will be allocated for the incoming pointer inside the function. + SaslStage::SaslServerStart => unsafe { + sasl_server_start( security.saslconfig.sasl_conn, mech_name.as_ptr(), client_data.as_ptr() as *const c_char, @@ -216,20 +217,21 @@ impl ClientIoHandler { &mut serverout, &mut serverout_len, ) - } - } else { - unsafe { - err = sasl_server_step( + }, + SaslStage::SaslServerStep => unsafe { + sasl_server_step( security.saslconfig.sasl_conn, client_data.as_ptr() as *const c_char, client_len, &mut serverout, &mut serverout_len, ) - } - } + }, + }; if err != SASL_OK && err != SASL_CONTINUE { + // SAFETY: sasl_dispose() is C function. All parameters passed of the + // function have been checked. unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) } error!("Auth failed!"); return Err(anyhow!(VncError::AuthFailed("Auth failed!".to_string()))); @@ -303,6 +305,8 @@ impl ClientIoHandler { info!("local_addr: {} remote_addr: {}", local_addr, remote_addr); let local_addr = CString::new(local_addr)?; let remote_addr = CString::new(remote_addr)?; + // SAFETY: sasl_server_init() and sasl_server_new() is C function. All parameters passed of the + // function have been checked. Memory will be allocated for the incoming pointer inside the function. // Sasl server init. unsafe { err = sasl_server_init(ptr::null_mut(), appname.as_ptr()); @@ -346,6 +350,8 @@ impl ClientIoHandler { let ssf: sasl_ssf_t = 256; let ssf = &ssf as *const sasl_ssf_t; let security = self.server.security_type.borrow_mut(); + // SAFETY: sasl_setprop() and sasl_server_new() is C function. It can be ensure + // that security.saslconfig.sasl_conn is not null. unsafe { err = sasl_setprop( security.saslconfig.sasl_conn, @@ -374,6 +380,8 @@ impl ClientIoHandler { }; let props = &saslprops as *const sasl_security_properties_t; + // SAFETY: sasl_setprop() and sasl_server_new() is C function. It can be ensure + // that security.saslconfig.sasl_conn is not null. unsafe { err = sasl_setprop( security.saslconfig.sasl_conn, @@ -402,6 +410,8 @@ impl ClientIoHandler { let mut mechlist: *const c_char = ptr::null_mut(); let mut security = self.server.security_type.borrow_mut(); let client = self.client.clone(); + // SAFETY: sasl_listmech() is C function. It can be ensure + // that security.saslconfig.sasl_conn is not null. unsafe { err = sasl_listmech( security.saslconfig.sasl_conn, @@ -420,6 +430,7 @@ impl ClientIoHandler { "SASL_FAIL: no support sasl mechlist".to_string() ))); } + // SAFETY: It can be ensure that the pointer of mechlist is not null. let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) }; security.saslconfig.mech_list = String::from(mech_list.to_str()?); let mut buf = Vec::new(); @@ -442,6 +453,8 @@ impl ClientIoHandler { } let err: c_int; let mut val: *const c_void = ptr::null_mut(); + // SAFETY: sasl_getprop() is C function. It can be ensure + // that security.saslconfig.sasl_conn is not null. unsafe { err = sasl_getprop(security.saslconfig.sasl_conn, SASL_SSF as c_int, &mut val) } if err != SASL_OK { error!("sasl_getprop: internal error"); @@ -450,6 +463,7 @@ impl ClientIoHandler { )))); } + // SAFETY: It can be ensure that the ptr of val is not null. let ssf: usize = unsafe { *(val as *const usize) }; if ssf < MIN_SSF_LENGTH { error!("SASL SSF too weak"); @@ -467,6 +481,8 @@ impl ClientIoHandler { fn sasl_check_authz(&mut self) -> Result<()> { let security = self.server.security_type.borrow_mut(); let mut val: *const c_void = ptr::null_mut(); + // SAFETY: sasl_getprop() is C function. It can be ensure + // that security.saslconfig.sasl_conn is not null. let err = unsafe { sasl_getprop( security.saslconfig.sasl_conn, @@ -485,6 +501,7 @@ impl ClientIoHandler { "No SASL username set" )))); } + // SAFETY: It can ensure that the pointer val is not null. let username = unsafe { CStr::from_ptr(val as *const c_char) }; let username = String::from(username.to_str()?); diff --git a/vnc/src/console.rs b/vnc/src/console.rs index eb1ecf5eb..d74fa51cf 100644 --- a/vnc/src/console.rs +++ b/vnc/src/console.rs @@ -11,8 +11,8 @@ // See the Mulan PSL v2 for more details. use crate::pixman::{ - get_image_height, get_image_width, pixman_glyph_from_vgafont, pixman_glyph_render, - unref_pixman_image, ColorNames, COLOR_TABLE_RGB, + create_pixman_image, get_image_height, get_image_width, pixman_glyph_from_vgafont, + pixman_glyph_render, unref_pixman_image, ColorNames, COLOR_TABLE_RGB, }; use anyhow::Result; use log::error; @@ -22,7 +22,7 @@ use std::{ cmp, ptr, sync::{Arc, Mutex, Weak}, }; -use util::pixman::{pixman_format_code_t, pixman_image_create_bits, pixman_image_t}; +use util::pixman::{pixman_format_code_t, pixman_image_t}; static CONSOLES: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(ConsoleList::new()))); @@ -626,10 +626,7 @@ fn create_msg_surface(width: i32, height: i32, msg: String) -> Option = None; let ptr = (data_ptr as usize + (j * stride) as usize) as *mut u32; for i in 0..sub_rect.w { + // SAFETY: it can be ensure the raw pointer will not exceed the range. let value = unsafe { *ptr.offset(i as isize) }; match last_color { Some(color) => { @@ -310,6 +312,7 @@ fn pixel_statistical<'a>( for j in 0..sub_rect.h { let ptr = (data_ptr as usize + (j * stride) as usize) as *mut u32; for i in 0..sub_rect.w { + // SAFETY: it can be ensure the raw pointer will not exceed the range. let value = unsafe { *ptr.offset(i as isize) }; match n_colors { 0 => { @@ -357,9 +360,9 @@ mod tests { IMAGE_DATA_MULTI_PIXELS, IMAGE_DATA_SINGLE_PIXEL, IMAGE_DATA_TWO_PIXEL, TARGET_DATA_MULTI_PIXELS, TARGET_DATA_SINGLE_PIXEL, TARGET_DATA_TWO_PIXEL, }, - pixman::PixelFormat, + pixman::{create_pixman_image, PixelFormat}, }; - use util::pixman::{pixman_format_code_t, pixman_image_create_bits}; + use util::pixman::pixman_format_code_t; fn color_init() -> PixelFormat { let mut pf = PixelFormat::default(); pf.red.set_color_info(16, 255); @@ -384,15 +387,13 @@ mod tests { let image_height: i32 = 32; let image_stride: i32 = 128; - let image = unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - image_width as i32, - image_height as i32, - image_data.as_ptr() as *mut u32, - image_stride, - ) - }; + let image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + image_width as i32, + image_height as i32, + image_data.as_ptr() as *mut u32, + image_stride, + ); let mut buf: Vec = Vec::new(); let rect = Rectangle { x: 0, @@ -417,15 +418,13 @@ mod tests { let image_height: i32 = 40; let image_stride: i32 = 160; - let image = unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - image_width as i32, - image_height as i32, - image_data.as_ptr() as *mut u32, - image_stride, - ) - }; + let image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + image_width as i32, + image_height as i32, + image_data.as_ptr() as *mut u32, + image_stride, + ); let mut buf: Vec = Vec::new(); let rect = Rectangle { x: 0, @@ -450,15 +449,13 @@ mod tests { let image_height: i32 = 40; let image_stride: i32 = 160; - let image = unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - image_width as i32, - image_height as i32, - image_data.as_ptr() as *mut u32, - image_stride, - ) - }; + let image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + image_width as i32, + image_height as i32, + image_data.as_ptr() as *mut u32, + image_stride, + ); let mut buf: Vec = Vec::new(); let rect = Rectangle { x: 0, diff --git a/vnc/src/pixman.rs b/vnc/src/pixman.rs index c1ac90140..e191509b1 100644 --- a/vnc/src/pixman.rs +++ b/vnc/src/pixman.rs @@ -17,9 +17,11 @@ use util::pixman::{ pixman_format_depth, pixman_format_g, pixman_format_r, pixman_image_composite, pixman_image_create_bits, pixman_image_create_solid_fill, pixman_image_get_data, pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, - pixman_image_get_width, pixman_image_t, pixman_image_unref, pixman_op_t, + pixman_image_get_width, pixman_image_ref, pixman_image_t, pixman_image_unref, pixman_op_t, }; +const MAX_IMAGE_SIZE: i32 = 65535; + #[derive(Clone, Default)] pub struct ColorInfo { /// Mask color. @@ -111,6 +113,8 @@ impl PixelFormat { } } +// SAFETY: Before calling the c function of pixman. All +// parameters passed of the function have been checked. pub fn get_image_width(image: *mut pixman_image_t) -> i32 { if image.is_null() { return 0; @@ -146,6 +150,20 @@ pub fn get_image_format(image: *mut pixman_image_t) -> pixman_format_code_t { unsafe { pixman_image_get_format(image as *mut pixman_image_t) } } +pub fn create_pixman_image( + image_format: pixman_format_code_t, + width: i32, + height: i32, + image_data: *mut u32, + stride: i32, +) -> *mut pixman_image_t { + if !(0..MAX_IMAGE_SIZE).contains(&width) || !(0..MAX_IMAGE_SIZE).contains(&height) { + return ptr::null_mut() as *mut pixman_image_t; + } + + unsafe { pixman_image_create_bits(image_format, width, height, image_data as *mut u32, stride) } +} + /// Bpp: bit per pixel pub fn bytes_per_pixel() -> usize { ((pixman_format_bpp(pixman_format_code_t::PIXMAN_x8r8g8b8 as u32) + 7) / 8) as usize @@ -162,6 +180,62 @@ pub fn unref_pixman_image(image: *mut pixman_image_t) { unsafe { pixman_image_unref(image as *mut pixman_image_t) }; } +/// Increase the reference of image +/// # Arguments +/// +/// * `image` - the pointer to image in pixman +pub fn ref_pixman_image(image: *mut pixman_image_t) -> *mut pixman_image_t { + if image.is_null() { + return ptr::null_mut() as *mut pixman_image_t; + } + unsafe { pixman_image_ref(image as *mut pixman_image_t) } +} + +/// Create a pixman image with a height of 1 +pub fn pixman_image_linebuf_create( + image_format: pixman_format_code_t, + width: i32, +) -> *mut pixman_image_t { + if !(0..MAX_IMAGE_SIZE).contains(&width) { + return ptr::null_mut() as *mut pixman_image_t; + } + unsafe { pixman_image_create_bits(image_format, width, 1, ptr::null_mut(), 0) } +} + +pub fn pixman_image_linebuf_fill( + line_buf: *mut pixman_image_t, + fb: *mut pixman_image_t, + width: i32, + x: i32, + y: i32, +) { + if line_buf.is_null() + || fb.is_null() + || !(0..MAX_IMAGE_SIZE).contains(&width) + || !(0..MAX_IMAGE_SIZE).contains(&x) + || !(0..MAX_IMAGE_SIZE).contains(&y) + { + return; + }; + + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + fb as *mut pixman_image_t, + ptr::null_mut(), + line_buf as *mut pixman_image_t, + x as i16, + y as i16, + 0, + 0, + 0, + 0, + width as u16, + 1, + ); + }; +} + pub enum ColorNames { ColorBlack = 0, ColorBlue = 1, @@ -539,7 +613,7 @@ pub fn pixman_glyph_from_vgafont(height: i32, ch: u32) -> *mut pixman_image_t { let glyph; unsafe { - glyph = pixman_image_create_bits( + glyph = create_pixman_image( pixman_format_code_t::PIXMAN_a8, 8, height, diff --git a/vnc/src/server.rs b/vnc/src/server.rs index b72b48cb9..7623b2ada 100644 --- a/vnc/src/server.rs +++ b/vnc/src/server.rs @@ -19,7 +19,8 @@ use crate::{ input::KeyBoardState, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, - get_image_width, unref_pixman_image, + get_image_width, pixman_image_linebuf_create, pixman_image_linebuf_fill, + unref_pixman_image, }, round_up_div, vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, @@ -50,10 +51,7 @@ use util::{ loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }, - pixman::{ - pixman_format_bpp, pixman_format_code_t, pixman_image_composite, pixman_image_create_bits, - pixman_image_t, pixman_op_t, - }, + pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_t}, }; use vmm_sys_util::epoll::EventSet; @@ -79,6 +77,10 @@ pub struct VncServer { pub conn_limits: usize, } +// SAFETY: +// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image by this pointer. +// 2. It can be sure that Rc> and Rc> are used only in single thread. +// So implement Send and Sync is safe. unsafe impl Send for VncServer {} unsafe impl Sync for VncServer {} @@ -359,7 +361,16 @@ impl VncSurface { s_info.stride as usize, ); - let line_buf = self.get_one_line_buf(&mut s_info, &mut g_info); + let mut line_buf = ptr::null_mut(); + if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { + line_buf = pixman_image_linebuf_create( + pixman_format_code_t::PIXMAN_x8r8g8b8, + get_image_width(self.server_image), + ); + g_info.stride = s_info.stride; + g_info.length = g_info.stride; + } + loop { let mut y = offset / g_bpl; let x = offset % g_bpl; @@ -367,22 +378,13 @@ impl VncSurface { (s_info.data as usize + y * s_info.stride as usize + x * cmp_bytes) as *mut u8; if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { - unsafe { - pixman_image_composite( - pixman_op_t::PIXMAN_OP_SRC, - self.guest_image, - ptr::null_mut(), - line_buf, - 0, - y as i16, - 0, - 0, - 0, - 0, - self.get_min_width() as u16, - 1, - ); - }; + pixman_image_linebuf_fill( + line_buf, + self.guest_image, + self.get_min_width(), + 0_i32, + y as i32, + ); g_info.ptr = get_image_data(line_buf) as *mut u8; } else { g_info.ptr = (g_info.data as usize + y * g_info.stride as usize) as *mut u8; @@ -441,6 +443,7 @@ impl VncSurface { _cmp_bytes = line_bytes as usize - x * cmp_bytes; } + // SAFETY: it can be ensure the raw pointer will not exceed the range. unsafe { if libc::memcmp( s_info.ptr as *mut libc::c_void, @@ -467,35 +470,6 @@ impl VncSurface { count } - - /// Transfer dirty data to buff in one line - /// - /// # Arguments - /// - /// * `s_info` - Info of Server image. - /// * `g_info` - Info of Guest image. - fn get_one_line_buf( - &self, - s_info: &mut ImageInfo, - g_info: &mut ImageInfo, - ) -> *mut pixman_image_t { - let mut line_buf = ptr::null_mut(); - if self.guest_format != pixman_format_code_t::PIXMAN_x8r8g8b8 { - line_buf = unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - get_image_width(self.server_image), - 1, - ptr::null_mut(), - 0, - ) - }; - g_info.stride = s_info.stride; - g_info.length = g_info.stride; - } - - line_buf - } } /// Set diry for each client. diff --git a/vnc/src/vnc.rs b/vnc/src/vnc.rs index 9a5b617c7..38144a82e 100644 --- a/vnc/src/vnc.rs +++ b/vnc/src/vnc.rs @@ -25,8 +25,8 @@ use crate::{ encoding::enc_hextile::hextile_send_framebuffer_update, input::KeyBoardState, pixman::{ - bytes_per_pixel, get_image_data, get_image_height, get_image_stride, get_image_width, - unref_pixman_image, + bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, + get_image_width, ref_pixman_image, unref_pixman_image, }, round_up, round_up_div, server::{make_server_config, VncConnHandler, VncServer, VncSurface}, @@ -54,7 +54,7 @@ use std::{ use util::{ bitmap::Bitmap, loop_context::EventNotifierHelper, - pixman::{pixman_format_code_t, pixman_image_create_bits, pixman_image_ref, pixman_image_t}, + pixman::{pixman_format_code_t, pixman_image_t}, }; /// The number of dirty pixels represented bt one bit in dirty bitmap. @@ -90,7 +90,7 @@ impl DisplayChangeListenerOperations for VncInterface { unref_pixman_image(locked_vnc_surface.guest_image); // Vnc_pixman_image_ref - locked_vnc_surface.guest_image = unsafe { pixman_image_ref(surface.image) }; + locked_vnc_surface.guest_image = ref_pixman_image(surface.image); locked_vnc_surface.guest_format = surface.format; let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); @@ -445,15 +445,13 @@ pub fn update_server_surface(server: &Arc) { let g_height = get_image_height(locked_vnc_surface.guest_image); let width = vnc_width(g_width); let height = vnc_height(g_height); - locked_vnc_surface.server_image = unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - width, - height, - ptr::null_mut(), - 0, - ) - }; + locked_vnc_surface.server_image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + width, + height, + ptr::null_mut(), + 0, + ); locked_vnc_surface.guest_dirty_bitmap.clear_all(); set_area_dirty( @@ -535,6 +533,7 @@ pub fn write_pixel( ) { if !client_dpm.convert { let mut con = vec![0; copy_bytes]; + // SAFETY: it can be ensure the raw pointer will not exceed the range. unsafe { ptr::copy(data_ptr as *mut u8, con.as_mut_ptr(), copy_bytes); } @@ -543,6 +542,7 @@ pub fn write_pixel( let num = copy_bytes >> 2; let ptr = data_ptr as *mut u32; for i in 0..num { + // SAFETY: it can be ensure the raw pointer will not exceed the range. let color = unsafe { *ptr.add(i) }; convert_pixel(client_dpm, buf, color); } @@ -655,15 +655,13 @@ fn send_framebuffer_update( /// Initialize a default image /// Default: width is 640, height is 480, stride is 640 * 4 fn get_client_image() -> *mut pixman_image_t { - unsafe { - pixman_image_create_bits( - pixman_format_code_t::PIXMAN_x8r8g8b8, - 640, - 480, - ptr::null_mut(), - 640 * 4, - ) - } + create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + 640, + 480, + ptr::null_mut(), + 640 * 4, + ) } pub static VNC_SERVERS: Lazy>>> = Lazy::new(|| Mutex::new(Vec::new())); -- Gitee From d15283f91b201a97bfc40970279524e48de08b63 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Wed, 18 Jan 2023 11:44:49 +0000 Subject: [PATCH 0745/2187] virtio-gpu: code refactor 1. remove v1.2 cmds which not yet realized. 2. gpu_get_pixman_format no longer treated as object method. 3. add and fix unrealized VirtioDevice trait. 4. add a period to line comments. Signed-off-by: Binfeng Wu --- virtio/src/gpu.rs | 115 +++++++++++++++++++++++++--------------------- virtio/src/lib.rs | 2 +- 2 files changed, 63 insertions(+), 54 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index ec81984b0..6ff4e0124 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -20,9 +20,8 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::config::{GpuConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use migration::{DeviceStateDesc, FieldDesc}; +use migration::{DeviceStateDesc, FieldDesc, MigrationManager}; use migration_derive::{ByteCode, Desc}; -use std::cmp; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; @@ -49,23 +48,23 @@ use vnc::console::{ display_replace_surface, DisplayMouse, DisplaySurface, HardWareOperations, }; -/// Number of virtqueues. +// number of virtqueues const QUEUE_NUM_GPU: usize = 2; -/// Flags for virtio gpu base conf. +// flags for virtio gpu base conf const VIRTIO_GPU_FLAG_VIRGL_ENABLED: u32 = 1; #[allow(unused)] const VIRTIO_GPU_FLAG_STATS_ENABLED: u32 = 2; const VIRTIO_GPU_FLAG_EDID_ENABLED: u32 = 3; -/// Features which virtio gpu cmd can support +// features which virtio gpu cmd can support const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; -/// flag used to distinguish the cmd type and format VirtioGpuRequest +// Flags used to distinguish the cmd type and format VirtioGpuRequest. const VIRTIO_GPU_CMD_CTRL: u32 = 0; const VIRTIO_GPU_CMD_CURSOR: u32 = 1; -/// virtio_gpu_ctrl_type: 2d commands. +// virtio_gpu_ctrl_type: 2d commands const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101; const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102; @@ -74,24 +73,22 @@ const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104; const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105; const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106; const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107; -const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x0108; -const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x0109; const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x010a; -/// virtio_gpu_ctrl_type: cursor commands. +// virtio_gpu_ctrl_type: cursor commands const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300; const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301; -/// virtio_gpu_ctrl_type: success responses. +// virtio_gpu_ctrl_type: success responses const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100; const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101; const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104; -/// virtio_gpu_ctrl_type: error responses. +// virtio_gpu_ctrl_type: error responses const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200; const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201; const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202; const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203; const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205; -/// simple formats for fbcon/X use +// simple formats for fbcon/X use const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3; @@ -176,8 +173,6 @@ impl VirtioGpuCtrlHdr { | VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D | VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING | VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING - | VIRTIO_GPU_CMD_GET_CAPSET_INFO - | VIRTIO_GPU_CMD_GET_CAPSET | VIRTIO_GPU_CMD_GET_EDID => true, _ => { error!("request type {} is not supported for GPU", self.hdr_type); @@ -486,7 +481,7 @@ impl GpuScanout { struct GpuIoHandler { /// The virtqueue for for sending control commands. ctrl_queue: Arc>, - /// The virtqueue for sending cursor updates + /// The virtqueue for sending cursor updates. cursor_queue: Arc>, /// The address space to which the GPU device belongs. mem_space: Arc, @@ -506,7 +501,7 @@ struct GpuIoHandler { base_conf: VirtioGpuBaseConf, /// States of all request in scanout. req_states: [VirtioGpuReqState; VIRTIO_GPU_MAX_SCANOUTS], - /// + /// Scanouts of gpu. scanouts: Vec, /// Max host mem for resource. max_hostmem: u64, @@ -549,23 +544,23 @@ fn create_surface( surface } -impl GpuIoHandler { - fn gpu_get_pixman_format(&mut self, format: u32) -> Result { - match format { - VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8r8g8b8), - VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8r8g8b8), - VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM => Ok(pixman_format_code_t::PIXMAN_b8g8r8a8), - VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM => Ok(pixman_format_code_t::PIXMAN_b8g8r8x8), - VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8b8g8r8), - VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8x8), - VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8a8), - VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8b8g8r8), - _ => { - bail!("Unsupport pixman format") - } +fn gpu_get_pixman_format(format: u32) -> Result { + match format { + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8r8g8b8), + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8r8g8b8), + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM => Ok(pixman_format_code_t::PIXMAN_b8g8r8a8), + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM => Ok(pixman_format_code_t::PIXMAN_b8g8r8x8), + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8b8g8r8), + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8x8), + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8a8), + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8b8g8r8), + _ => { + bail!("Unsupport pixman format") } } +} +impl GpuIoHandler { fn gpu_get_image_hostmem( &mut self, format: pixman_format_code_t, @@ -831,9 +826,8 @@ impl GpuIoHandler { resource_id: info_create_2d.resource_id, ..Default::default() }; - let pixman_format = self - .gpu_get_pixman_format(res.format) - .with_context(|| "Fail to parse guest format")?; + let pixman_format = + gpu_get_pixman_format(res.format).with_context(|| "Fail to parse guest format")?; res.host_mem = self.gpu_get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); if res.host_mem + self.used_hostmem < self.max_hostmem { @@ -882,7 +876,7 @@ impl GpuIoHandler { if (res.scanouts_bitmask & (1 << i)) != 0 { let scanout = &mut self.scanouts[i as usize]; if scanout.resource_id != 0 { - // disable the scanout. + // disable the scanout res.scanouts_bitmask &= !(1 << i); display_replace_surface(scanout.con_id, None); scanout.clear(); @@ -957,10 +951,10 @@ impl GpuIoHandler { ); } - // TODO refactor to disable function + // TODO: refactor to disable function let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; if info_set_scanout.resource_id == 0 { - // set resource_id to 0 means disable the scanout. + // Set resource_id to 0 means disable the scanout. if let Some(res_index) = self .resources_list .iter() @@ -1292,7 +1286,7 @@ impl GpuIoHandler { return; } - // we divide regions into that need to be copied and can be skipped + // We divide regions into that need to be copied and can be skipped. let src_cpy_section: usize = (info_transfer.rect.width * bpp) as usize; let src_expected: usize = info_transfer.offset as usize + ((info_transfer.rect.height - 1) * stride as u32) as usize @@ -1746,6 +1740,12 @@ impl VirtioDevice for Gpu { Ok(()) } + /// Unrealize low level device. + fn unrealize(&mut self) -> Result<()> { + MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.gpu_conf.id); + Ok(()) + } + /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_GPU @@ -1780,36 +1780,45 @@ impl VirtioDevice for Gpu { fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config.as_bytes(); let config_len = config_slice.len() as u64; - if offset >= config_len { + + if offset + .checked_add(data.len() as u64) + .filter(|&end| end <= config_len) + .is_none() + { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; - } + + let read_end: usize = offset as usize + data.len(); + data.write_all(&config_slice[offset as usize..read_end])?; + Ok(()) } /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let data_len = data.len(); - let config_slice = self.state.config.as_mut_bytes(); - let config_len = config_slice.len(); - if offset as usize + data_len > config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset, - config_len as u64 - ))); + let mut config_cpy = self.state.config; + let config_cpy_slice = config_cpy.as_mut_bytes(); + let config_len = config_cpy_slice.len() as u64; + + if offset + .checked_add(data.len() as u64) + .filter(|&end| end <= config_len) + .is_none() + { + return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); - + config_cpy_slice[(offset as usize)..(offset as usize + data.len())].copy_from_slice(data); if self.state.config.events_clear != 0 { - self.state.config.events_read &= !self.state.config.events_clear; + self.state.config.events_read &= !config_cpy.events_clear; } Ok(()) } + /// Activate the virtio device, this function is called by vcpu thread when frontend + /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9f00db59a..0f1619546 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -273,7 +273,7 @@ pub trait VirtioDevice: Send { /// Realize low level device. fn realize(&mut self) -> Result<()>; - /// Unrealize low level device + /// Unrealize low level device. fn unrealize(&mut self) -> Result<()> { bail!("Unrealize of the virtio device is not implemented"); } -- Gitee From 4dad0d575ed2c0bf4dfbc34d618dfcd0a7b3c6cd Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sun, 29 Jan 2023 18:40:34 +0800 Subject: [PATCH 0746/2187] virtio-net: fix unsafe function Fix the using of unsafe function. Signed-off-by: Yan Wang --- virtio/src/net.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 22227683e..c3c916893 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -678,6 +678,7 @@ struct NetIoHandler { impl NetIoHandler { fn read_from_tap(queue: &mut Queue, iovecs: &[libc::iovec], tap: &mut Tap) -> i32 { + // SAFETY: the arguments of readv has been checked and is correct. let size = unsafe { libc::readv( tap.as_raw_fd() as libc::c_int, @@ -741,6 +742,7 @@ impl NetIoHandler { self.rx.queue_full = true; break; } + rx_packets += 1; if rx_packets > self.queue_size { self.rx @@ -749,12 +751,15 @@ impl NetIoHandler { .with_context(|| "Failed to trigger rx queue event".to_string())?; break; } + let elem = queue .vring .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net rx")?; if elem.desc_num == 0 { break; + } else if elem.in_iovec.is_empty() { + bail!("The lengh of in iovec is 0"); } let iovecs = NetIoHandler::get_libc_iovecs( &self.mem_space, @@ -829,6 +834,7 @@ impl NetIoHandler { fn send_packets(&self, tap_fd: libc::c_int, iovecs: &[libc::iovec]) -> i8 { loop { + // SAFETY: the arguments of writev has been checked and is correct. let size = unsafe { libc::writev( tap_fd, @@ -861,7 +867,10 @@ impl NetIoHandler { .with_context(|| "Failed to pop avail ring for net tx")?; if elem.desc_num == 0 { break; + } else if elem.out_iovec.is_empty() { + bail!("The lengh of out iovec is 0"); } + tx_packets += 1; if tx_packets >= self.queue_size { self.tx @@ -870,6 +879,7 @@ impl NetIoHandler { .with_context(|| "Failed to trigger tx queue event".to_string())?; break; } + let iovecs = NetIoHandler::get_libc_iovecs( &self.mem_space, queue.vring.get_cache(), @@ -881,7 +891,7 @@ impl NetIoHandler { } else { -1_i32 }; - if tap_fd != -1 && !iovecs.is_empty() && self.send_packets(tap_fd, &iovecs) == -1 { + if tap_fd != -1 && self.send_packets(tap_fd, &iovecs) == -1 { queue.vring.push_back(); self.tx.queue_evt.write(1).with_context(|| { "Failed to trigger tx queue event when writev blocked".to_string() -- Gitee From a8e69f6ef06c950d8ef10fdbd457e408c4730b4f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sun, 29 Jan 2023 18:43:02 +0800 Subject: [PATCH 0747/2187] vhost-user-blk: fix unsafe function Fix the using of unsafe function. Signed-off-by: Yan Wang --- virtio/src/vhost/user/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 6085fdb12..23b0e457b 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -518,6 +518,7 @@ impl VhostUserClient { ); let cnf = VhostUserConfig::new(0, 0, VirtioBlkConfig::default())?; let body_opt: Option<&u32> = None; + // SAFETY: the memory is allocated by us and it has been already aligned. let payload_opt: Option<&[u8]> = Some(unsafe { from_raw_parts( (&cnf as *const VhostUserConfig) as *const u8, -- Gitee From 3282dc648411c495c2be622ac1dc3374ff9b827f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 31 Jan 2023 14:35:52 +0800 Subject: [PATCH 0748/2187] rtc: set a date in 2080 for test_set_year_20xx Ensure there is no year-2080 overflow. Signed-off-by: yezengruan --- devices/src/legacy/pl031.rs | 15 +++++++++++++-- devices/src/legacy/rtc.rs | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 269649984..36caa9938 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -238,13 +238,24 @@ mod test { fn test_set_year_20xx() { let mut rtc = PL031::default(); // Set rtc time: 2013-11-13 02:04:56. - let wtick = mktime64(2013, 11, 13, 2, 4, 56) as u32; + let mut wtick = mktime64(2013, 11, 13, 2, 4, 56) as u32; let mut data = [0; 4]; LittleEndian::write_u32(&mut data, wtick); PL031::write(&mut rtc, &mut data, GuestAddress(0), RTC_LR); PL031::read(&mut rtc, &mut data, GuestAddress(0), RTC_DR); - let rtick = LittleEndian::read_u32(&data); + let mut rtick = LittleEndian::read_u32(&data); + + assert!((rtick - wtick) <= WIGGLE); + + // Set rtc time: 2080-11-13 02:04:56, ensure there is no year-2080 overflow. + wtick = mktime64(2080, 11, 13, 2, 4, 56) as u32; + data = [0; 4]; + LittleEndian::write_u32(&mut data, wtick); + PL031::write(&mut rtc, &mut data, GuestAddress(0), RTC_LR); + + PL031::read(&mut rtc, &mut data, GuestAddress(0), RTC_DR); + rtick = LittleEndian::read_u32(&data); assert!((rtick - wtick) <= WIGGLE); } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 9efcfb22c..48e858960 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -460,6 +460,17 @@ mod test { assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x13); assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x20); + // Set rtc time: 2080-11-13 02:04:56, ensure there is no year-2080 overflow. + cmos_write(&mut rtc, RTC_YEAR, 0x80); + + assert!((cmos_read(&mut rtc, RTC_SECONDS) - 0x56) <= WIGGLE); + assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x04); + assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x02); + assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x13); + assert_eq!(cmos_read(&mut rtc, RTC_MONTH), 0x11); + assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x80); + assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x20); + Ok(()) } -- Gitee From 17eead2ec67c27a3369ce2f441232769e56dde06 Mon Sep 17 00:00:00 2001 From: xuexiaowei1 Date: Fri, 20 Jan 2023 10:47:04 +0800 Subject: [PATCH 0749/2187] vhost-user-blk-pci: add inflight support Vhost-user-blk-pci device supports inflight feature now. Signed-off-by: Xiaowei Xue vhost-user-blk-pci: add inflight support Vhost-user-blk-pci device supports inflight feature now. Signed-off-by: Xiaowei Xue --- virtio/src/vhost/user/client.rs | 173 +++++++++++++++++++++++++++++--- virtio/src/virtqueue/mod.rs | 3 + virtio/src/virtqueue/split.rs | 4 + 3 files changed, 167 insertions(+), 13 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 0072a1c8a..fa43ecea8 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -10,8 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::fs::File; use std::mem::size_of; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::rc::Rc; use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; @@ -35,13 +36,17 @@ use super::message::{ }; use super::sock::VhostUserSock; use crate::block::VirtioBlkConfig; +use crate::virtio_has_feature; use crate::VhostUser::message::VhostUserConfig; use anyhow::{anyhow, bail, Context, Result}; +use util::unix::do_mmap; /// Vhost supports multiple queue pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; /// Vhost supports `VHOST_USER_SET_CONFIG` and `VHOST_USER_GET_CONFIG` msg. pub const VHOST_USER_PROTOCOL_F_CONFIG: u8 = 9; +/// Vhost supports `VHOST_USER_SET_INFLIGHT_FD` and `VHOST_USER_GET_INFLIGHT_FD` msg. +pub const VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD: u8 = 12; struct ClientInternal { // Used to send requests to the vhost user backend in userspace. @@ -59,15 +64,24 @@ impl ClientInternal { } fn wait_ack_msg(&self, request: u32) -> Result { + self.wait_ack_msg_and_data::(request, None, &mut []) + } + + fn wait_ack_msg_and_data( + &self, + request: u32, + payload_opt: Option<&mut [u8]>, + fds: &mut [RawFd], + ) -> Result { let mut hdr = VhostUserMsgHdr::default(); let mut body: T = Default::default(); - let payload_opt: Option<&mut [u8]> = None; - - let (recv_len, _fds_num) = self + let (recv_len, fds_num) = self .sock - .recv_msg(Some(&mut hdr), Some(&mut body), payload_opt, &mut []) + .recv_msg(Some(&mut hdr), Some(&mut body), payload_opt, fds) .with_context(|| "Failed to recv ack msg")?; - + if fds_num != fds.len() { + bail!("Unexpected fds num: {}, expected: {}", fds_num, fds.len()); + } if request != hdr.request || recv_len != (size_of::() + size_of::()) || !hdr.is_reply() @@ -300,16 +314,43 @@ impl Listener for VhostUserMemInfo { } } +/// Struct for set and get inflight fd request, field is defined by dpdk. +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct VhostUserInflight { + // The size of memory area to track inflight I/O. + pub mmap_size: u64, + // The offset from the start of the supplied file descriptor. + pub mmap_offset: u64, + // The number of virtqueues. + pub queue_num: u16, + // The size of virtqueues. + pub queue_size: u16, +} + +/// Struct for saving inflight info, create this struct to save inflight info when +/// vhost client start, use this struct to set inflight fd when vhost client reconnect. +#[derive(Debug)] +pub struct VhostInflight { + // The inflight file. + pub file: Arc, + // Fd mmap addr, used for migration. + pub addr: u64, + pub inner: VhostUserInflight, +} + /// Struct for communication with the vhost user backend in userspace pub struct VhostUserClient { client: Arc>, mem_info: VhostUserMemInfo, delete_evts: Vec, + mem_space: Arc, queues: Vec>>, queue_evts: Vec>, call_events: Vec>, pub features: u64, reconnecting: bool, + inflight: Option, } impl VhostUserClient { @@ -332,11 +373,13 @@ impl VhostUserClient { client, mem_info, delete_evts: Vec::new(), + mem_space: mem_space.clone(), queues: Vec::new(), queue_evts: Vec::new(), call_events: Vec::new(), features: 0, reconnecting: false, + inflight: None, }) } @@ -361,6 +404,46 @@ impl VhostUserClient { } } + /// Set inflight fd, include get inflight fd from vhost and set inflight to vhost. + pub fn set_inflight(&mut self, queue_num: u16, queue_size: u16) -> Result<()> { + let protocol_feature = self + .get_protocol_features() + .with_context(|| "Failed to get protocol features for vhost-user blk")?; + if virtio_has_feature( + protocol_feature, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD as u32, + ) { + if self.inflight.is_none() { + // Expect 1 fd. + let mut fds = [RawFd::default()]; + let vhost_user_inflight = self.get_inflight_fd(queue_num, queue_size, &mut fds)?; + let file = Arc::new(unsafe { File::from_raw_fd(fds[0]) }); + let hva = do_mmap( + &Some(file.as_ref()), + vhost_user_inflight.mmap_size, + vhost_user_inflight.mmap_offset, + true, + true, + false, + )?; + let inflight = VhostInflight { + file, + addr: hva as u64, + inner: vhost_user_inflight, + }; + self.inflight = Some(inflight); + } + let inflight = self.inflight.as_ref().unwrap(); + self.set_inflight_fd(inflight.inner.clone(), inflight.file.as_raw_fd())?; + } else { + bail!( + "Failed to get inflight fd, spdk doesn't support, spdk protocol feature: {:#b}", + protocol_feature + ); + } + Ok(()) + } + /// Activate device by vhost-user protocol. pub fn activate_vhost_user(&mut self) -> Result<()> { self.set_owner() @@ -376,6 +459,16 @@ impl VhostUserClient { if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { queue_num -= 1; } + + let queue_size = self + .queues + .first() + .unwrap() + .lock() + .unwrap() + .vring + .actual_size(); + self.set_inflight(queue_num as u16, queue_size)?; // Set all vring num to notify ovs/dpdk how many queues it needs to poll // before setting vring info. for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { @@ -390,7 +483,9 @@ impl VhostUserClient { } for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { - let queue_config = queue_mutex.lock().unwrap().vring.get_queue_config(); + let queue = queue_mutex.lock().unwrap(); + let queue_config = queue.vring.get_queue_config(); + self.set_vring_addr(&queue_config, queue_index, 0) .with_context(|| { format!( @@ -398,12 +493,14 @@ impl VhostUserClient { queue_index, ) })?; - self.set_vring_base(queue_index, 0).with_context(|| { - format!( - "Failed to set vring base for vhost-user net, index: {}", - queue_index, - ) - })?; + let last_avail_idx = queue.vring.get_avail_idx(&self.mem_space)?; + self.set_vring_base(queue_index, last_avail_idx) + .with_context(|| { + format!( + "Failed to set vring base for vhost-user, index: {}", + queue_index, + ) + })?; self.set_vring_kick(queue_index, self.queue_evts[queue_index].clone()) .with_context(|| { format!( @@ -559,6 +656,56 @@ impl VhostUserClient { .with_context(|| "Failed to wait ack msg for getting queue num")?; Ok(queue_num) } + + /// Get inflight file info and inflight fd from vhost. + pub fn get_inflight_fd( + &self, + queue_num: u16, + queue_size: u16, + fds: &mut [RawFd], + ) -> Result { + let request = VhostUserMsgReq::GetInflightFd as u32; + let data_len = size_of::(); + let hdr = + VhostUserMsgHdr::new(request, VhostUserHdrFlag::NeedReply as u32, data_len as u32); + let inflight = VhostUserInflight { + mmap_size: 0, + mmap_offset: 0, + queue_num, + queue_size, + }; + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = Some(unsafe { + from_raw_parts( + (&inflight as *const VhostUserInflight) as *const u8, + data_len, + ) + }); + let client = self.client.lock().unwrap(); + client + .sock + .send_msg(Some(&hdr), body_opt, payload_opt, &[]) + .with_context(|| "Failed to send msg for getting inflight fd")?; + let res = client + .wait_ack_msg_and_data::(request, None, fds) + .with_context(|| "Failed to wait ack msg for getting inflight fd")?; + Ok(res) + } + + /// Set inflight file info and send inflight fd to vhost. + pub fn set_inflight_fd(&self, inflight: VhostUserInflight, fd: RawFd) -> Result<()> { + let request = VhostUserMsgReq::SetInflightFd as u32; + let len = size_of::(); + let hdr = VhostUserMsgHdr::new(request, 0, len as u32); + let payload_opt: Option<&[u8]> = None; + self.client + .lock() + .unwrap() + .sock + .send_msg(Some(&hdr), Some(&inflight), payload_opt, &[fd]) + .with_context(|| "Failed to send msg for setting inflight fd")?; + Ok(()) + } } impl VhostOps for VhostUserClient { diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs index 1f93dfffb..4014a3133 100644 --- a/virtio/src/virtqueue/mod.rs +++ b/virtio/src/virtqueue/mod.rs @@ -163,6 +163,9 @@ pub trait VringOps { /// The number of descriptor chains in the available ring. fn avail_ring_len(&mut self, sys_mem: &Arc) -> Result; + /// Get the avail index of the vring. + fn get_avail_idx(&self, sys_mem: &Arc) -> Result; + /// Get the region cache information of the SplitVring. fn get_cache(&self) -> &Option; } diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 9ed8d30d6..d398f06d2 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -852,6 +852,10 @@ impl VringOps for SplitVring { Ok((avail_idx - self.next_avail).0) } + fn get_avail_idx(&self, sys_mem: &Arc) -> Result { + SplitVring::get_avail_idx(self, sys_mem) + } + fn get_cache(&self) -> &Option { &self.cache } -- Gitee From 7de97284bf4122568500b0f167304b534bf608c4 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 30 Jan 2023 09:38:35 +0800 Subject: [PATCH 0750/2187] =?UTF-8?q?socket:=20signal=20triggered=20the=20?= =?UTF-8?q?process=20to=20exit=201.Fixed=20the=20issue=20that=20when=20the?= =?UTF-8?q?=20client=20exited=20abnormally,=20the=20sigpipe=20signal=20tri?= =?UTF-8?q?ggered=EF=BC=9B=202.The=20error=20handling=20flow=20results=20i?= =?UTF-8?q?n=20panic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jiewangqun --- machine_manager/src/qmp/mod.rs | 18 +++++++++++------ machine_manager/src/socket.rs | 37 +++++++++++----------------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 305b82b5e..8fe47a3e5 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -37,7 +37,7 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; -use log::{info, warn}; +use log::{error, info, warn}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -558,13 +558,19 @@ impl QmpChannel { #[allow(clippy::unused_io_amount)] pub fn send_event(event: &schema::QmpEvent) { if Self::is_connected() { - let event_str = serde_json::to_string(&event).unwrap(); + let mut event_str = serde_json::to_string(&event).unwrap(); let mut writer_unlocked = Self::inner().event_writer.write().unwrap(); let writer = writer_unlocked.as_mut().unwrap(); - writer.flush().unwrap(); - writer.write(event_str.as_bytes()).unwrap(); - writer.write(&[b'\r']).unwrap(); - writer.write(&[b'\n']).unwrap(); + + if let Err(e) = writer.flush() { + error!("flush err, {:?}", e); + return; + } + event_str.push_str("\r\n"); + if let Err(e) = writer.write(event_str.as_bytes()) { + error!("write err, {:?}", e); + return; + } info!("EVENT: --> {:?}", event); } } diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 1c8befe50..efc17fac2 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -27,10 +27,7 @@ use util::loop_context::{ use vmm_sys_util::epoll::EventSet; use crate::machine::MachineExternalInterface; -use crate::{ - qmp::qmp_schema::QmpEvent, - qmp::{QmpChannel, QmpGreeting, Response}, -}; +use crate::qmp::{QmpChannel, QmpGreeting, Response}; const MAX_SOCKET_MSG_LENGTH: usize = 8192; pub(crate) const LEAK_BUCKET_LIMIT: u64 = 100; @@ -101,9 +98,6 @@ impl Socket { self.bind_unix_stream(stream); } } - - QmpChannel::bind_writer(SocketRWHandler::new(self.get_stream_fd())); - self.send_response(true); } /// Accept a new incoming connection unix stream from unix listener. @@ -151,26 +145,12 @@ impl Socket { SocketHandler::new(self.get_stream_fd()) } - /// In qmp feature, send event to client. - /// - /// # Arguments - /// - /// * `event` - The `QmpEvent` will be sent to client. - pub fn send_event(&self, event: &QmpEvent) { - if self.is_connected() { - let mut handler = self.get_socket_handler(); - let event_str = serde_json::to_string(&event).unwrap(); - handler.send_str(&event_str).unwrap(); - info!("EVENT: --> {:?}", event); - } - } - /// In qmp feature, send empty or greeting response to client. /// /// # Arguments /// /// * `is_greeting` - Whether sending greeting response or not. - pub fn send_response(&self, is_greeting: bool) { + pub fn send_response(&self, is_greeting: bool) -> std::io::Result<()> { if self.is_connected() { let mut handler = self.get_socket_handler(); let resp = if is_greeting { @@ -178,9 +158,10 @@ impl Socket { } else { serde_json::to_string(&Response::create_empty_response()).unwrap() + "\r" }; - handler.send_str(&resp).unwrap(); + handler.send_str(&resp)?; info!("QMP: --> {:?}", resp); } + Ok(()) } /// Create socket's accepted stream to `event_notifier`. @@ -197,6 +178,12 @@ impl Socket { let leak_bucket_fd = leak_bucket.lock().unwrap().as_raw_fd(); self.accept(); + QmpChannel::bind_writer(SocketRWHandler::new(self.get_stream_fd())); + if let Err(e) = self.send_response(true) { + error!("{:?}", e); + QmpChannel::unbind(); + return notifiers; + } let handler: Rc = Rc::new(move |event, _| { if event == EventSet::IN { let socket_mutexed = shared_socket.lock().unwrap(); @@ -470,7 +457,7 @@ impl SocketRWHandler { /// # Errors /// The socket file descriptor is broken. fn write_fd(&mut self, length: usize) -> std::io::Result<()> { - use libc::{c_void, iovec, msghdr, sendmsg}; + use libc::{c_void, iovec, msghdr, sendmsg, MSG_NOSIGNAL}; let mut iov = iovec { iov_base: self.buf.as_slice()[(self.pos - length)..(self.pos - 1)].as_ptr() @@ -489,7 +476,7 @@ impl SocketRWHandler { mhdr.msg_controllen = 0; mhdr.msg_flags = 0; - if unsafe { sendmsg(self.socket_fd, &mhdr, 0) } == -1 { + if unsafe { sendmsg(self.socket_fd, &mhdr, MSG_NOSIGNAL) } == -1 { Err(Error::new( ErrorKind::BrokenPipe, "The socket pipe is broken!", -- Gitee From 156ed9668dfdf0e3b1f715ccace88d40e019ce4d Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 19 Jan 2023 06:54:54 +0000 Subject: [PATCH 0751/2187] virtio-gpu: add GpuDevConfig check Change name from GpuConfig to GpuDevConfig, distinguish from base_conf. And add a check for max_hostmem with tests. Signed-off-by: Binfeng Wu --- machine_manager/src/config/error.rs | 2 + machine_manager/src/config/gpu.rs | 66 +++++++++++++++++++---------- virtio/src/gpu.rs | 24 +++++------ 3 files changed, 57 insertions(+), 35 deletions(-) diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index b3bead749..d315e9986 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -44,6 +44,8 @@ pub enum ConfigError { FieldIsMissing(&'static str, &'static str), #[error("{0} must >{} {1} and <{} {3}.", if *.2 {"="} else {""}, if *.4 {"="} else {""})] IllegalValue(String, u64, bool, u64, bool), + #[error("{0} must {}{} {3}.", if *.1 {">"} else {"<"}, if *.2 {"="} else {""})] + IllegalValueUnilateral(String, bool, bool, u64), #[error("Mac address is illegal.")] MacFormatError, #[error("Unknown vhost type.")] diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index c7f1269b2..9c3856146 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -21,7 +21,7 @@ pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; pub const VIRTIO_GPU_MAX_HOSTMEM: u64 = 256 * M; #[derive(Clone, Debug)] -pub struct GpuConfig { +pub struct GpuDevConfig { pub id: String, pub max_outputs: u32, pub edid: bool, @@ -30,9 +30,9 @@ pub struct GpuConfig { pub max_hostmem: u64, } -impl Default for GpuConfig { +impl Default for GpuDevConfig { fn default() -> Self { - GpuConfig { + GpuDevConfig { id: "".to_string(), max_outputs: 1, edid: true, @@ -43,7 +43,7 @@ impl Default for GpuConfig { } } -impl ConfigCheck for GpuConfig { +impl ConfigCheck for GpuDevConfig { fn check(&self) -> Result<()> { if self.id.len() > MAX_STRING_LENGTH { return Err(anyhow!(ConfigError::StringLengthTooLong( @@ -52,11 +52,22 @@ impl ConfigCheck for GpuConfig { ))); } - if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { - return Err(anyhow!(ConfigError::UnitIdError( + if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 || self.max_outputs == 0 { + return Err(anyhow!(ConfigError::IllegalValue( "max_outputs".to_string(), - self.max_outputs as usize, - VIRTIO_GPU_MAX_SCANOUTS, + 0, + false, + VIRTIO_GPU_MAX_SCANOUTS as u64, + true + ))); + } + + if self.max_hostmem == 0 { + return Err(anyhow!(ConfigError::IllegalValueUnilateral( + "max_hostmem".to_string(), + true, + false, + 0 ))); } @@ -72,7 +83,7 @@ impl ConfigCheck for GpuConfig { } } -pub fn parse_gpu(gpu_config: &str) -> Result { +pub fn parse_gpu(gpu_config: &str) -> Result { let mut cmd_parser = CmdParser::new("virtio-gpu-pci"); cmd_parser .push("") @@ -86,7 +97,7 @@ pub fn parse_gpu(gpu_config: &str) -> Result { .push("addr"); cmd_parser.parse(gpu_config)?; - let mut gpu_cfg: GpuConfig = GpuConfig::default(); + let mut gpu_cfg: GpuDevConfig = GpuDevConfig::default(); if let Some(id) = cmd_parser.get_value::("id")? { gpu_cfg.id = id; } @@ -115,7 +126,7 @@ mod tests { use super::*; #[test] - fn test_parse_gpu_max_hostmem_greater_than_limit() { + fn test_parse_pci_gpu_config_cmdline_parser() { let max_hostmem = VIRTIO_GPU_MAX_HOSTMEM + 1; let gpu_cfg_cmdline = format!( "{}{}", @@ -123,30 +134,39 @@ mod tests { max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=", max_hostmem.to_string() ); - let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); - assert!(gpu_cfg_.is_ok()); - let gpu_cfg = gpu_cfg_.unwrap(); + assert_eq!(gpu_cfg.id, "gpu_1"); + assert_eq!(gpu_cfg.max_outputs, 1); + assert_eq!(gpu_cfg.edid, true); + assert_eq!(gpu_cfg.xres, 1024); + assert_eq!(gpu_cfg.yres, 768); assert_eq!(gpu_cfg.max_hostmem, max_hostmem); - } - #[test] - fn test_parse_gpu_max_hostmem_less_than_limit() { - let max_hostmem = VIRTIO_GPU_MAX_HOSTMEM - 1; + // max_outputs is illegal let gpu_cfg_cmdline = format!( "{}{}", "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ - max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=", + max_outputs=17,edid=true,xres=1024,yres=768,max_hostmem=", max_hostmem.to_string() ); - let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); + assert!(gpu_cfg_.is_err()); - assert!(gpu_cfg_.is_ok()); + let gpu_cfg_cmdline = format!( + "{}{}", + "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ + max_outputs=0,edid=true,xres=1024,yres=768,max_hostmem=", + max_hostmem.to_string() + ); + let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); + assert!(gpu_cfg_.is_err()); - let gpu_cfg = gpu_cfg_.unwrap(); - assert_eq!(gpu_cfg.max_hostmem, max_hostmem); + // max_hostmem is illegal + let gpu_cfg_cmdline = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ + max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=0"; + let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); + assert!(gpu_cfg_.is_err()); } } diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index b6d457cb5..17785b465 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -18,7 +18,7 @@ use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; -use machine_manager::config::{GpuConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_SCANOUTS}; +use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationManager}; use migration_derive::{ByteCode, Desc}; @@ -1687,7 +1687,7 @@ pub struct GpuState { #[derive(Default)] pub struct Gpu { /// Configuration of the GPU device. - gpu_conf: GpuConfig, + gpu_cfg: GpuDevConfig, /// Status of the GPU device. state: GpuState, /// Callback to trigger interrupt. @@ -1697,21 +1697,21 @@ pub struct Gpu { } impl Gpu { - pub fn new(gpu_conf: GpuConfig) -> Gpu { + pub fn new(gpu_cfg: GpuDevConfig) -> Gpu { let mut state = GpuState::default(); - state.base_conf.xres = gpu_conf.xres; - state.base_conf.yres = gpu_conf.yres; - if gpu_conf.edid { + state.base_conf.xres = gpu_cfg.xres; + state.base_conf.yres = gpu_cfg.yres; + if gpu_cfg.edid { state.base_conf.flags &= 1 << VIRTIO_GPU_FLAG_EDID_ENABLED; } - state.base_conf.max_outputs = gpu_conf.max_outputs; + state.base_conf.max_outputs = gpu_cfg.max_outputs; state.device_features = 1u64 << VIRTIO_F_VERSION_1; state.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; state.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; Self { - gpu_conf, + gpu_cfg, state, interrupt_cb: None, deactivate_evts: Vec::new(), @@ -1722,10 +1722,10 @@ impl Gpu { impl VirtioDevice for Gpu { /// Realize virtio gpu device. fn realize(&mut self) -> Result<()> { - if self.gpu_conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { + if self.gpu_cfg.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { bail!( "Invalid max_outputs {} which is bigger than {}", - self.gpu_conf.max_outputs, + self.gpu_cfg.max_outputs, VIRTIO_GPU_MAX_SCANOUTS ); } @@ -1740,7 +1740,7 @@ impl VirtioDevice for Gpu { /// Unrealize low level device. fn unrealize(&mut self) -> Result<()> { - MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.gpu_conf.id); + MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.gpu_cfg.id); Ok(()) } @@ -1853,7 +1853,7 @@ impl VirtioDevice for Gpu { base_conf: self.state.base_conf, scanouts, req_states, - max_hostmem: self.gpu_conf.max_hostmem, + max_hostmem: self.gpu_cfg.max_hostmem, used_hostmem: 0, }; gpu_handler.req_states[0].width = self.state.base_conf.xres; -- Gitee From db6e99c49603d0574a78cd8dcaa993a4d6d97a17 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Fri, 27 Jan 2023 22:04:41 +0800 Subject: [PATCH 0752/2187] virtio-gpu: remove unnecessary attribute VirtioGpuBaseConf is unnecessary, keep it in struct GPU. And we refactor new and realize codes. Signed-off-by: Binfeng Wu --- virtio/src/gpu.rs | 151 +++++++++++++++++----------------------------- virtio/src/lib.rs | 45 ++++++++++++++ 2 files changed, 100 insertions(+), 96 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 17785b465..a07157dc8 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -12,9 +12,17 @@ use super::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_GPU, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, + VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, + VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; -use crate::VirtioError; +use crate::{VirtioError, VIRTIO_GPU_F_EDID}; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; @@ -51,43 +59,10 @@ use vnc::console::{ // number of virtqueues const QUEUE_NUM_GPU: usize = 2; -// flags for virtio gpu base conf -const VIRTIO_GPU_FLAG_VIRGL_ENABLED: u32 = 1; -#[allow(unused)] -const VIRTIO_GPU_FLAG_STATS_ENABLED: u32 = 2; -const VIRTIO_GPU_FLAG_EDID_ENABLED: u32 = 3; - -// features which virtio gpu cmd can support -const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; - -// Flags used to distinguish the cmd type and format VirtioGpuRequest. +// the type of virtio gpu cmd const VIRTIO_GPU_CMD_CTRL: u32 = 0; const VIRTIO_GPU_CMD_CURSOR: u32 = 1; -// virtio_gpu_ctrl_type: 2d commands -const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; -const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101; -const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102; -const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103; -const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104; -const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105; -const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106; -const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107; -const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x010a; -// virtio_gpu_ctrl_type: cursor commands -const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300; -const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301; -// virtio_gpu_ctrl_type: success responses -const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100; -const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101; -const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104; -// virtio_gpu_ctrl_type: error responses -const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200; -const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201; -const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202; -const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203; -const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205; - // simple formats for fbcon/X use const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; @@ -98,22 +73,6 @@ const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; -#[derive(Clone, Copy, Debug, ByteCode)] -pub struct VirtioGpuConfig { - events_read: u32, - events_clear: u32, - num_scanouts: u32, - reserved: u32, -} - -#[derive(Clone, Copy, Debug, ByteCode)] -pub struct VirtioGpuBaseConf { - max_outputs: u32, - flags: u32, - xres: u32, - yres: u32, -} - #[derive(Debug)] struct GpuResource { resource_id: u32, @@ -465,6 +424,7 @@ struct GpuScanout { x: u32, y: u32, resource_id: u32, + // Unused with vnc backend, work in others. cursor: VirtioGpuUpdateCursor, } @@ -497,11 +457,11 @@ struct GpuIoHandler { resources_list: Vec, /// The bit mask of whether scanout is enabled or not. enable_output_bitmask: u32, - /// Baisc Configure of GPU device. - base_conf: VirtioGpuBaseConf, + /// The number of scanouts + num_scanouts: u32, /// States of all request in scanout. req_states: [VirtioGpuReqState; VIRTIO_GPU_MAX_SCANOUTS], - /// Scanouts of gpu. + /// Scanouts of gpu, mouse doesn't realize copy trait, so it is a vector. scanouts: Vec, /// Max host mem for resource. max_hostmem: u64, @@ -687,7 +647,7 @@ impl GpuIoHandler { *need_interrupt = true; let mut display_info = VirtioGpuDisplayInfo::default(); display_info.header.hdr_type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO; - for i in 0..self.base_conf.max_outputs { + for i in 0..self.num_scanouts { if (self.enable_output_bitmask & (1 << i)) != 0 { let i = i as usize; display_info.pmodes[i].enabled = 1; @@ -738,10 +698,10 @@ impl GpuIoHandler { )) })?; - if edid_req.scanouts >= self.base_conf.max_outputs { + if edid_req.scanouts >= self.num_scanouts { error!( "The scanouts {} of request exceeds the max_outputs {}", - edid_req.scanouts, self.base_conf.max_outputs + edid_req.scanouts, self.num_scanouts ); return self.gpu_response_nodata( need_interrupt, @@ -872,7 +832,7 @@ impl GpuIoHandler { { let res = &mut self.resources_list[res_index]; if res.scanouts_bitmask != 0 { - for i in 0..self.base_conf.max_outputs { + for i in 0..self.num_scanouts { if (res.scanouts_bitmask & (1 << i)) != 0 { let scanout = &mut self.scanouts[i as usize]; if scanout.resource_id != 0 { @@ -939,7 +899,7 @@ impl GpuIoHandler { )) })?; - if info_set_scanout.scanout_id >= self.base_conf.max_outputs { + if info_set_scanout.scanout_id >= self.num_scanouts { error!( "The scanout id {} is out of range", info_set_scanout.scanout_id @@ -1135,7 +1095,7 @@ impl GpuIoHandler { info_res_flush.rect.width, info_res_flush.rect.height, ); - for i in 0..self.base_conf.max_outputs { + for i in 0..self.num_scanouts { // Flushes any scanouts the resource is being used on. if res.scanouts_bitmask & (1 << i) != 0 { let scanout = &self.scanouts[i as usize]; @@ -1553,11 +1513,8 @@ impl GpuIoHandler { fn ctrl_queue_evt_handler(&mut self) -> Result<()> { let mut queue = self.ctrl_queue.lock().unwrap(); - if !queue.is_valid(&self.mem_space) { - bail!("Failed to handle any request, the queue is not ready"); - } - let mut req_queue = Vec::new(); + while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { if elem.desc_num == 0 { break; @@ -1582,9 +1539,6 @@ impl GpuIoHandler { fn cursor_queue_evt_handler(&mut self) -> Result<()> { let cursor_queue = self.cursor_queue.clone(); let mut queue = cursor_queue.lock().unwrap(); - if !queue.is_valid(&self.mem_space) { - bail!("Failed to handle any request, the queue is not ready"); - } while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { if elem.desc_num == 0 { @@ -1668,6 +1622,14 @@ impl EventNotifierHelper for GpuIoHandler { } } +#[derive(Clone, Copy, Debug, ByteCode)] +pub struct VirtioGpuConfig { + events_read: u32, + events_clear: u32, + num_scanouts: u32, + reserved: u32, +} + /// State of gpu device. #[repr(C)] #[derive(Clone, Copy, Desc, ByteCode)] @@ -1678,9 +1640,7 @@ pub struct GpuState { /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, /// Config space of the GPU device. - config: VirtioGpuConfig, - /// Baisc Configure of GPU device. - base_conf: VirtioGpuBaseConf, + config_space: VirtioGpuConfig, } /// GPU device structure. @@ -1698,25 +1658,18 @@ pub struct Gpu { impl Gpu { pub fn new(gpu_cfg: GpuDevConfig) -> Gpu { - let mut state = GpuState::default(); - - state.base_conf.xres = gpu_cfg.xres; - state.base_conf.yres = gpu_cfg.yres; - if gpu_cfg.edid { - state.base_conf.flags &= 1 << VIRTIO_GPU_FLAG_EDID_ENABLED; - } - state.base_conf.max_outputs = gpu_cfg.max_outputs; - state.device_features = 1u64 << VIRTIO_F_VERSION_1; - state.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; - state.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; - Self { gpu_cfg, - state, + state: GpuState::default(), interrupt_cb: None, deactivate_evts: Vec::new(), } } + + fn build_device_config_space(&mut self) { + self.state.config_space.num_scanouts = self.gpu_cfg.max_outputs; + self.state.config_space.reserved = 0; + } } impl VirtioDevice for Gpu { @@ -1730,10 +1683,14 @@ impl VirtioDevice for Gpu { ); } - // Virgl is not supported. - self.state.base_conf.flags &= !(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); - self.state.config.num_scanouts = self.state.base_conf.max_outputs; - self.state.config.reserved = 0; + self.state.device_features = 1u64 << VIRTIO_F_VERSION_1; + self.state.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; + self.state.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; + if self.gpu_cfg.edid { + self.state.device_features |= 1 << VIRTIO_GPU_F_EDID; + } + + self.build_device_config_space(); Ok(()) } @@ -1776,7 +1733,7 @@ impl VirtioDevice for Gpu { /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.state.config.as_bytes(); + let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset @@ -1795,7 +1752,7 @@ impl VirtioDevice for Gpu { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let mut config_cpy = self.state.config; + let mut config_cpy = self.state.config_space; let config_cpy_slice = config_cpy.as_mut_bytes(); let config_len = config_cpy_slice.len() as u64; @@ -1808,8 +1765,8 @@ impl VirtioDevice for Gpu { } config_cpy_slice[(offset as usize)..(offset as usize + data.len())].copy_from_slice(data); - if self.state.config.events_clear != 0 { - self.state.config.events_read &= !config_cpy.events_clear; + if self.state.config_space.events_clear != 0 { + self.state.config_space.events_read &= !config_cpy.events_clear; } Ok(()) @@ -1830,6 +1787,7 @@ impl VirtioDevice for Gpu { queues.len() ))); } + self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; let mut scanouts = vec![]; @@ -1850,17 +1808,18 @@ impl VirtioDevice for Gpu { driver_features: self.state.driver_features, resources_list: Vec::new(), enable_output_bitmask: 1, - base_conf: self.state.base_conf, - scanouts, + num_scanouts: self.gpu_cfg.max_outputs, req_states, + scanouts, max_hostmem: self.gpu_cfg.max_hostmem, used_hostmem: 0, }; - gpu_handler.req_states[0].width = self.state.base_conf.xres; - gpu_handler.req_states[0].height = self.state.base_conf.yres; + gpu_handler.req_states[0].width = self.gpu_cfg.xres; + gpu_handler.req_states[0].height = self.gpu_cfg.yres; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(gpu_handler))); register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + Ok(()) } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 997043b0d..708e1f991 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -162,6 +162,8 @@ pub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10; pub const VIRTIO_BLK_F_DISCARD: u32 = 13; /// WRITE ZEROES is supported. pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; +/// GPU EDID feature is supported. +pub const VIRTIO_GPU_F_EDID: u32 = 1; /// The device sets control ok status to driver. pub const VIRTIO_NET_OK: u8 = 0; @@ -237,6 +239,49 @@ pub const VIRTIO_BLK_S_IOERR: u8 = 1; /// Unsupport. pub const VIRTIO_BLK_S_UNSUPP: u8 = 2; +/// The Type of virtio gpu, refer to Virtio Spec. +/// 2D commands: +/// Retrieve the current output configuration. +pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; +/// Create a 2D resource on the host. +pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101; +/// Destroy a resource on the host. +pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102; +/// Set the scanout parameters for a single output. +pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103; +/// Flush a scanout resource. +pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104; +/// Transfer from guest memory to host resource. +pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105; +/// Assign backing pages to a resource. +pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106; +/// Detach backing pages from a resource. +pub const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107; +//// Retrieve the EDID data for a given scanout. +pub const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x010a; +/// update cursor +pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300; +/// move cursor +pub const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301; +/// Success for cmd without data back. +pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100; +/// Success for VIRTIO_GPU_CMD_GET_DISPLAY_INFO. +pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101; +/// Success for VIRTIO_GPU_CMD_GET_EDID. +pub const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104; +/// unspecificated +pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200; +/// out of host memory +pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201; +/// invalid id of scanout +pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202; +/// invalid id of 2D resource +pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203; +/// invalid parameter +pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205; +/// Flags in virtio gpu cmd which means need a fence. +pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; + /// Interrupt status: Used Buffer Notification pub const VIRTIO_MMIO_INT_VRING: u32 = 0x01; /// Interrupt status: Configuration Change Notification -- Gitee From a6fe9d3d6f362db28e23e317a34e78300bf2a19e Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Feb 2023 02:23:35 +0000 Subject: [PATCH 0753/2187] virtio-gpu: refactor request\respone process 1. Extract the request get and the response send into a pub method. 2. Replace read\write object from\to iov_base with iov_to_buf\iov_from_buf for better compatibility with front-end possible structure. 3. Delete redundant structs in request header. Signed-off-by: Binfeng Wu --- util/src/aio/mod.rs | 13 + virtio/src/gpu.rs | 850 +++++++++++++++++--------------------------- 2 files changed, 345 insertions(+), 518 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 23ac81fdb..533cfe614 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -390,3 +390,16 @@ pub fn iov_to_buf_direct(iovec: &[Iovec], buf: &mut [u8]) -> Result { } Ok(end) } + +/// Discard "size" bytes of the front of iovec. +pub fn iov_discard_front_direct(iovec: &mut [Iovec], mut size: u64) -> Option<&mut [Iovec]> { + for (index, iov) in iovec.iter_mut().enumerate() { + if iov.iov_len as u64 > size { + iov.iov_base += size; + iov.iov_len -= size as u64; + return Some(&mut iovec[index..]); + } + size -= iov.iov_len as u64; + } + None +} diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index a07157dc8..3533c5840 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -22,7 +22,7 @@ use super::{ VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; -use crate::{VirtioError, VIRTIO_GPU_F_EDID}; +use crate::{iov_discard_front, iov_to_buf, VirtioError, VIRTIO_GPU_F_EDID}; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; @@ -36,6 +36,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{ptr, vec}; +use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -59,10 +60,6 @@ use vnc::console::{ // number of virtqueues const QUEUE_NUM_GPU: usize = 2; -// the type of virtio gpu cmd -const VIRTIO_GPU_CMD_CTRL: u32 = 0; -const VIRTIO_GPU_CMD_CURSOR: u32 = 1; - // simple formats for fbcon/X use const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; @@ -119,28 +116,6 @@ pub struct VirtioGpuCtrlHdr { padding: u32, } -impl VirtioGpuCtrlHdr { - fn is_valid(&self) -> bool { - match self.hdr_type { - VIRTIO_GPU_CMD_UPDATE_CURSOR - | VIRTIO_GPU_CMD_MOVE_CURSOR - | VIRTIO_GPU_CMD_GET_DISPLAY_INFO - | VIRTIO_GPU_CMD_RESOURCE_CREATE_2D - | VIRTIO_GPU_CMD_RESOURCE_UNREF - | VIRTIO_GPU_CMD_SET_SCANOUT - | VIRTIO_GPU_CMD_RESOURCE_FLUSH - | VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D - | VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING - | VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING - | VIRTIO_GPU_CMD_GET_EDID => true, - _ => { - error!("request type {} is not supported for GPU", self.hdr_type); - false - } - } - } -} - impl ByteCode for VirtioGpuCtrlHdr {} #[repr(C)] @@ -173,7 +148,6 @@ impl ByteCode for VirtioGpuDisplayInfo {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuGetEdid { - header: VirtioGpuCtrlHdr, scanouts: u32, padding: u32, } @@ -205,7 +179,6 @@ impl ByteCode for VirtioGpuRespEdid {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuResourceCreate2d { - header: VirtioGpuCtrlHdr, resource_id: u32, format: u32, width: u32, @@ -217,7 +190,6 @@ impl ByteCode for VirtioGpuResourceCreate2d {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuResourceUnref { - header: VirtioGpuCtrlHdr, resource_id: u32, padding: u32, } @@ -227,7 +199,6 @@ impl ByteCode for VirtioGpuResourceUnref {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuSetScanout { - header: VirtioGpuCtrlHdr, rect: VirtioGpuRect, scanout_id: u32, resource_id: u32, @@ -238,7 +209,6 @@ impl ByteCode for VirtioGpuSetScanout {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuResourceFlush { - header: VirtioGpuCtrlHdr, rect: VirtioGpuRect, resource_id: u32, padding: u32, @@ -249,7 +219,6 @@ impl ByteCode for VirtioGpuResourceFlush {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuTransferToHost2d { - header: VirtioGpuCtrlHdr, rect: VirtioGpuRect, offset: u64, resource_id: u32, @@ -261,7 +230,6 @@ impl ByteCode for VirtioGpuTransferToHost2d {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuResourceAttachBacking { - header: VirtioGpuCtrlHdr, resource_id: u32, nr_entries: u32, } @@ -281,7 +249,6 @@ impl ByteCode for VirtioGpuMemEntry {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuResourceDetachBacking { - header: VirtioGpuCtrlHdr, resource_id: u32, padding: u32, } @@ -297,95 +264,77 @@ impl HardWareOperations for GpuOpts {} pub struct VirtioGpuRequest { header: VirtioGpuCtrlHdr, index: u16, - desc_num: u16, out_iovec: Vec, + out_len: u32, in_iovec: Vec, - in_header: GuestAddress, - out_header: GuestAddress, + in_len: u32, } impl VirtioGpuRequest { - fn new(mem_space: &Arc, elem: &Element, cmd_type: u32) -> Result { - if cmd_type != VIRTIO_GPU_CMD_CTRL && cmd_type != VIRTIO_GPU_CMD_CURSOR { - bail!("unsupport GPU request: {} ", cmd_type); - } - - if elem.out_iovec.is_empty() - || (cmd_type == VIRTIO_GPU_CMD_CTRL && elem.in_iovec.is_empty()) - || (cmd_type == VIRTIO_GPU_CMD_CURSOR && !elem.in_iovec.is_empty()) - { + fn new(mem_space: &Arc, elem: &Element) -> Result { + // Report errors for out_iovec invalid here, deal with in_iovec + // error in cmd process. + if elem.out_iovec.is_empty() { bail!( - "Missed header for GPU request: out {} in {} desc num {}", + "Missed header for gpu request: out {} in {} desc num {}.", elem.out_iovec.len(), elem.in_iovec.len(), elem.desc_num ); } - let out_elem = elem.out_iovec.get(0).unwrap(); - if out_elem.len < size_of::() as u32 { - bail!( - "Invalid out header for GPU request: length {}", - out_elem.len - ); - } - - let out_header = mem_space - .read_object::(out_elem.addr) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's request header", - out_elem.addr.0 - )) - })?; - if !out_header.is_valid() { - bail!("Unsupported GPU request type"); - } - - let in_elem_addr = match cmd_type { - VIRTIO_GPU_CMD_CTRL => { - let in_elem = elem.in_iovec.last().unwrap(); - if in_elem.len < 1 { - bail!("Invalid in header for GPU request: length {}", in_elem.len) - } - in_elem.addr + let mut header = VirtioGpuCtrlHdr::default(); + iov_to_buf(mem_space, &elem.out_iovec, header.as_mut_bytes()).and_then(|size| { + if size < size_of::() { + bail!("Invalid header for gpu request: len {}.", size) } - VIRTIO_GPU_CMD_CURSOR => GuestAddress(0), - _ => { - bail!("unsupport GPU request: {}", cmd_type) - } - }; + Ok(()) + })?; + // Note: in_iov and out_iov total len is no more than 1<<32, and + // out_iov is more than 1, so in_len and out_len will not overflow. let mut request = VirtioGpuRequest { - header: out_header, + header, index: elem.index, - desc_num: elem.desc_num, - out_iovec: Vec::with_capacity(elem.out_iovec.len()), - in_iovec: Vec::with_capacity(elem.in_iovec.len()), - in_header: in_elem_addr, - out_header: out_elem.addr, + out_iovec: Vec::with_capacity(elem.desc_num as usize), + out_len: 0, + in_iovec: Vec::with_capacity(elem.desc_num as usize), + in_len: 0, }; - for (index, elem_iov) in elem.in_iovec.iter().enumerate() { - if index == elem.in_iovec.len() - 1 { - break; + let mut out_iovec = elem.out_iovec.clone(); + // Size of out_iovec no less than sizeo of VirtioGpuCtrlHdr, so + // it is possible to get none back. + if let Some(data_iovec) = + iov_discard_front(&mut out_iovec, size_of::() as u64) + { + for elem_iov in data_iovec { + if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { + let iov = Iovec { + iov_base: hva, + iov_len: u64::from(elem_iov.len), + }; + request.out_iovec.push(iov); + request.out_len += elem_iov.len; + } else { + bail!("Map desc base {:?} failed.", elem_iov.addr); + } } + } + + for elem_iov in elem.in_iovec.iter() { if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { let iov = Iovec { iov_base: hva, iov_len: u64::from(elem_iov.len), }; request.in_iovec.push(iov); + request.in_len += elem_iov.len; + } else { + bail!("Map desc base {:?} failed.", elem_iov.addr); } } - for (_index, elem_iov) in elem.out_iovec.iter().enumerate() { - request.out_iovec.push(Iovec { - iov_base: elem_iov.addr.0, - iov_len: elem_iov.len as u64, - }); - } - Ok(request) } } @@ -404,7 +353,6 @@ impl ByteCode for VirtioGpuCursorPos {} #[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuUpdateCursor { - header: VirtioGpuCtrlHdr, pos: VirtioGpuCursorPos, resource_id: u32, hot_x: u32, @@ -532,27 +480,88 @@ impl GpuIoHandler { height as u64 * stride } - fn gpu_clear_resource_iovs(&mut self, res_index: usize) { - let res = &mut self.resources_list[res_index]; - res.iov.clear(); + fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { + if header.out_len < size_of::() as u32 { + bail!("Invalid header for gpu request: len {}.", header.out_len) + } + + iov_to_buf_direct(&header.out_iovec, req.as_mut_bytes()).and_then(|size| { + if size == size_of::() { + Ok(()) + } else { + bail!("Invalid header for gpu request: len {}.", size) + } + }) + } + + fn send_response(&mut self, req: &VirtioGpuRequest, resp: &T) -> Result<()> { + if let Err(e) = iov_from_buf_direct(&req.in_iovec, resp.as_bytes()).and_then(|size| { + if size == size_of::() { + Ok(()) + } else { + bail!( + "Failed to response gpu request, invalid reponse len {}.", + size + ); + } + }) { + error!( + "Failed to response gpu request, {:?}, may cause suspended.", + e + ); + } + + let mut queue_lock = self.ctrl_queue.lock().unwrap(); + if let Err(e) = queue_lock + .vring + .add_used(&self.mem_space, req.index, size_of::() as u32) + { + bail!( + "Failed to add used ring(gpu ctrl), index {}, len {} {:?}.", + req.index, + size_of::() as u32, + e, + ); + } + + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + if let Err(e) = + (*self.interrupt_cb.as_ref())(&VirtioInterruptType::Vring, Some(&queue_lock), false) + { + error!("Failed to trigger interrupt(gpu ctrl), error is {:?}.", e); + } + } + + Ok(()) + } + + fn gpu_response_nodata(&mut self, resp_head_type: u32, req: &VirtioGpuRequest) -> Result<()> { + let mut resp = VirtioGpuCtrlHdr { + hdr_type: resp_head_type, + ..Default::default() + }; + + if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { + resp.flags |= VIRTIO_GPU_FLAG_FENCE; + resp.fence_id = req.header.fence_id; + resp.ctx_id = req.header.ctx_id; + } + + self.send_response(req, &resp) } fn gpu_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { - let info_cursor = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's cursor request header", - req.out_header.0 - )) - })?; + let mut info_cursor = VirtioGpuUpdateCursor::default(); + self.get_request(req, &mut info_cursor)?; let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; - if info_cursor.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { + if req.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { scanout.cursor.pos.x_coord = info_cursor.hot_x; scanout.cursor.pos.y_coord = info_cursor.hot_y; - } else if info_cursor.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { + } else if req.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { if scanout.mouse.is_none() { let tmp_mouse = DisplayMouse { height: 64, @@ -604,47 +613,7 @@ impl GpuIoHandler { Ok(()) } - fn gpu_response_nodata( - &mut self, - need_interrupt: &mut bool, - resp_head_type: u32, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let mut resp = VirtioGpuCtrlHdr { - hdr_type: resp_head_type, - ..Default::default() - }; - - if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { - resp.flags |= VIRTIO_GPU_FLAG_FENCE; - resp.fence_id = req.header.fence_id; - resp.ctx_id = req.header.ctx_id; - } - - self.mem_space - .write_object(&resp, req.in_header) - .with_context(|| "Fail to write nodata response")?; - self.ctrl_queue - .lock() - .unwrap() - .vring - .add_used( - &self.mem_space, - req.index, - size_of::() as u32, - ) - .with_context(|| "Fail to add used elem for control queue")?; - - Ok(()) - } - - fn gpu_cmd_get_display_info( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; + fn gpu_cmd_get_display_info(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut display_info = VirtioGpuDisplayInfo::default(); display_info.header.hdr_type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO; for i in 0..self.num_scanouts { @@ -662,52 +631,27 @@ impl GpuIoHandler { display_info.header.fence_id = req.header.fence_id; display_info.header.ctx_id = req.header.ctx_id; } - self.mem_space - .write_object(&display_info, req.in_header) - .with_context(|| "Fail to write displayinfo")?; - self.ctrl_queue - .lock() - .unwrap() - .vring - .add_used( - &self.mem_space, - req.index, - size_of::() as u32, - ) - .with_context(|| "Fail to add used elem for control queue")?; - - Ok(()) + self.send_response(req, &display_info) } - fn gpu_cmd_get_edid( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let mut edid_resp = VirtioGpuRespEdid::default(); - edid_resp.header.hdr_type = VIRTIO_GPU_RESP_OK_EDID; - - let edid_req = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's edid request header", - req.out_header.0 - )) - })?; + fn gpu_cmd_get_edid(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut edid_req = VirtioGpuGetEdid::default(); + self.get_request(req, &mut edid_req)?; if edid_req.scanouts >= self.num_scanouts { error!( - "The scanouts {} of request exceeds the max_outputs {}", + "GuestError: The scanouts {} of request exceeds the max_outputs {}.", edid_req.scanouts, self.num_scanouts ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - req, - ); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); + } + + let mut edid_resp = VirtioGpuRespEdid::default(); + edid_resp.header.hdr_type = VIRTIO_GPU_RESP_OK_EDID; + if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { + edid_resp.header.flags |= VIRTIO_GPU_FLAG_FENCE; + edid_resp.header.fence_id = req.header.fence_id; + edid_resp.header.ctx_id = req.header.ctx_id; } let mut edid_info = EdidInfo::new( @@ -720,50 +664,18 @@ impl GpuIoHandler { edid_info.edid_array_fulfill(&mut edid_resp.edid.to_vec()); edid_resp.size = edid_resp.edid.len() as u32; - if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { - edid_resp.header.flags |= VIRTIO_GPU_FLAG_FENCE; - edid_resp.header.fence_id = req.header.fence_id; - edid_resp.header.ctx_id = req.header.ctx_id; - } - self.mem_space - .write_object(&edid_resp, req.in_header) - .with_context(|| "Fail to write displayinfo")?; - self.ctrl_queue - .lock() - .unwrap() - .vring - .add_used( - &self.mem_space, - req.index, - size_of::() as u32, - ) - .with_context(|| "Fail to add used elem for control queue")?; + self.send_response(req, &edid_resp)?; Ok(()) } - fn gpu_cmd_resource_create_2d( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_create_2d = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's resource create 2d request header", - req.out_header.0, - )) - })?; + fn gpu_cmd_resource_create_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_create_2d = VirtioGpuResourceCreate2d::default(); + self.get_request(req, &mut info_create_2d)?; + if info_create_2d.resource_id == 0 { - error!("The 0 value for resource_id is illegal"); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - req, - ); + error!("GuestError: resource id 0 is not allowed."); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } if let Some(res) = self @@ -771,12 +683,8 @@ impl GpuIoHandler { .iter() .find(|&x| x.resource_id == info_create_2d.resource_id) { - error!("The resource_id {} is already existed", res.resource_id); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - req, - ); + error!("GuestError: resource {} already exists.", res.resource_id); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } let mut res = GpuResource { @@ -786,8 +694,14 @@ impl GpuIoHandler { resource_id: info_create_2d.resource_id, ..Default::default() }; - let pixman_format = - gpu_get_pixman_format(res.format).with_context(|| "Fail to parse guest format")?; + let pixman_format = match gpu_get_pixman_format(res.format) { + Ok(f) => f, + Err(e) => { + error!("GuestError: {:?}.", e); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + } + }; + res.host_mem = self.gpu_get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); if res.host_mem + self.used_hostmem < self.max_hostmem { @@ -801,34 +715,27 @@ impl GpuIoHandler { ) } } - if res.pixman_image.is_null() { error!( - "Fail to create resource(id {}, width {}, height {}) on host", + "GuestError: Fail to create resource(id {}, width {}, height {}) on host.", res.resource_id, res.width, res.height ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - req, - ); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, req); } self.used_hostmem += res.host_mem; self.resources_list.push(res); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } - fn gpu_destroy_resoure( - &mut self, - res_id: u32, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { + fn gpu_cmd_resource_unref(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_resource_unref = VirtioGpuResourceUnref::default(); + self.get_request(req, &mut info_resource_unref)?; + if let Some(res_index) = self .resources_list .iter() - .position(|x| x.resource_id == res_id) + .position(|x| x.resource_id == info_resource_unref.resource_id) { let res = &mut self.resources_list[res_index]; if res.scanouts_bitmask != 0 { @@ -848,67 +755,28 @@ impl GpuIoHandler { pixman_image_unref(res.pixman_image); } self.used_hostmem -= res.host_mem; - self.gpu_clear_resource_iovs(res_index); + res.iov.clear(); self.resources_list.remove(res_index); + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { - error!("The resource_id {} is not existed", res_id); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - req, + error!( + "GuestError: illegal resource specified {}.", + info_resource_unref.resource_id, ); + self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } - - Ok(()) - } - - fn gpu_cmd_resource_unref( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_resource_unref = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's resource unref request header", - req.out_header.0, - )) - })?; - - self.gpu_destroy_resoure(info_resource_unref.resource_id, need_interrupt, req) - .with_context(|| "Fail to unref guest resource")?; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) } - fn gpu_cmd_set_scanout( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_set_scanout = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's set scanout request header", - req.out_header.0 - )) - })?; + fn gpu_cmd_set_scanout(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_set_scanout = VirtioGpuSetScanout::default(); + self.get_request(req, &mut info_set_scanout)?; if info_set_scanout.scanout_id >= self.num_scanouts { error!( - "The scanout id {} is out of range", + "GuestError: The scanout id {} is out of range.", info_set_scanout.scanout_id ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, - req, - ); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, req); } // TODO: refactor to disable function @@ -925,7 +793,7 @@ impl GpuIoHandler { } display_replace_surface(scanout.con_id, None); scanout.clear(); - return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req); } if let Some(res_index) = self @@ -944,7 +812,7 @@ impl GpuIoHandler { || info_set_scanout.rect.height + info_set_scanout.rect.y_coord > res.height { error!( - "The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {})", + "GuestError: The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, res.width, res.height, @@ -954,11 +822,7 @@ impl GpuIoHandler { info_set_scanout.rect.x_coord, info_set_scanout.rect.y_coord, ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - req, - ); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } let pixman_format = unsafe { pixman_image_get_format(res.pixman_image) }; @@ -982,11 +846,8 @@ impl GpuIoHandler { .image .is_null() { - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_UNSPEC, - req, - ); + error!("HostError: surface image create failed, check pixman libary."); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } Some(sur) => { @@ -1005,11 +866,8 @@ impl GpuIoHandler { .image .is_null() { - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_UNSPEC, - req, - ); + error!("HostError: surface pixman image create failed, please check pixman libary."); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } } @@ -1032,31 +890,19 @@ impl GpuIoHandler { scanout.width = info_set_scanout.rect.width; scanout.height = info_set_scanout.rect.height; - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( - "The resource_id {} in set_scanout {} request is not existed", + "GuestError: The resource_id {} in set_scanout {} request is not existed.", info_set_scanout.resource_id, info_set_scanout.scanout_id ); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } - fn gpu_cmd_resource_flush( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_res_flush = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's resource flush request header", - req.out_header.0, - )) - })?; + fn gpu_cmd_resource_flush(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_res_flush = VirtioGpuResourceFlush::default(); + self.get_request(req, &mut info_res_flush)?; if let Some(res_index) = self .resources_list @@ -1072,16 +918,12 @@ impl GpuIoHandler { || info_res_flush.rect.height + info_res_flush.rect.y_coord > res.height { error!( - "The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {})", + "GuestError: The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, res.width, res.height, info_res_flush.rect.width, info_res_flush.rect.height, info_res_flush.rect.x_coord, info_res_flush.rect.y_coord, ); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - req, - ); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } unsafe { @@ -1134,13 +976,13 @@ impl GpuIoHandler { } pixman_region_fini(flush_reg_ptr); } - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( - "The resource_id {} in resource flush request is not existed", + "GuestError: The resource_id {} in resource flush request is not existed.", info_res_flush.resource_id ); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } @@ -1155,7 +997,7 @@ impl GpuIoHandler { if res_idx.is_none() { error!( - "The resource_id {} in transfer to host 2d request is not existed", + "GuestError: The resource_id {} in transfer to host 2d request is not existed.", info_transfer.resource_id ); return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; @@ -1164,7 +1006,7 @@ impl GpuIoHandler { let res = &self.resources_list[res_idx.unwrap()]; if res.iov.is_empty() { error!( - "The resource_id {} in transfer to host 2d request don't have iov", + "GuestError: The resource_id {} in transfer to host 2d request don't have iov.", info_transfer.resource_id ); return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; @@ -1178,7 +1020,7 @@ impl GpuIoHandler { || info_transfer.rect.height + info_transfer.rect.y_coord > res.height { error!( - "The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {})", + "GuestError: The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, res.width, res.height, @@ -1220,7 +1062,7 @@ impl GpuIoHandler { // the res.iov[]. And info_transfer.offset is the offset from res.iov[0].base // to the start position of the resource we really want to update. let mut src_ofs: usize = info_transfer.offset as usize; - // current iov's offset + // current iov's index let mut iov_idx: usize = 0; // current iov's offset let mut iov_ofs: usize = 0; @@ -1242,7 +1084,7 @@ impl GpuIoHandler { } if iov_idx >= res.iov.len() { - warn!("data from guest is shorted than expected"); + warn!("GuestWarn: the start pos of transfer data from guest is longer than resource's len."); return; } @@ -1299,46 +1141,22 @@ impl GpuIoHandler { } } - fn gpu_cmd_transfer_to_host_2d( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_transfer = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's transfer to host 2d request header", - req.out_header.0, - )) - })?; + fn gpu_cmd_transfer_to_host_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_transfer = VirtioGpuTransferToHost2d::default(); + self.get_request(req, &mut info_transfer)?; let errcode = self.gpu_cmd_transfer_to_host_2d_params_check(&info_transfer); if errcode != 0 { - return self.gpu_response_nodata(need_interrupt, errcode, req); + return self.gpu_response_nodata(errcode, req); } self.gpu_cmd_transfer_to_host_2d_update_resource(&info_transfer); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } - fn gpu_cmd_resource_attach_backing( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_attach_backing = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's resource attach backing request header", - req.out_header.0, - )) - })?; + fn gpu_cmd_resource_attach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_attach_backing = VirtioGpuResourceAttachBacking::default(); + self.get_request(req, &mut info_attach_backing)?; if let Some(res_index) = self .resources_list @@ -1348,84 +1166,92 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_index]; if !res.iov.is_empty() { error!( - "The resource_id {} in resource attach backing request allready has iov", + "GuestError: The resource_id {} in resource attach backing request allready has iov.", info_attach_backing.resource_id ); - return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } if info_attach_backing.nr_entries > 16384 { error!( - "The nr_entries in resource attach backing request is too large ( {} > 16384)", + "GuestError: The nr_entries in resource attach backing request is too large ( {} > 16384).", info_attach_backing.nr_entries ); - return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } - let mut base_entry = req.out_iovec.get(0).unwrap(); - let mut offset = size_of::() as u64; let esize = size_of::() as u64 * info_attach_backing.nr_entries as u64; - if base_entry.iov_len < offset + esize { - if req.out_iovec.len() <= 1 || req.out_iovec.get(1).unwrap().iov_len < esize { - error!("The entries is not setted"); - return self.gpu_response_nodata( - need_interrupt, - VIRTIO_GPU_RESP_ERR_UNSPEC, - req, - ); - } - base_entry = req.out_iovec.get(1).unwrap(); - offset = 0; + if esize > req.out_len as u64 { + error!( + "GuestError: The nr_entries {} in resource attach backing request is larger than total len {}.", + info_attach_backing.nr_entries, req.out_len, + ); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } + let mut data_iovec = req.out_iovec.clone(); + // Move to entries part first. + data_iovec = iov_discard_front_direct( + &mut data_iovec, + size_of::() as u64, + ) + .unwrap() + .to_vec(); + for i in 0..info_attach_backing.nr_entries { - let entry_addr = - base_entry.iov_base + offset + i as u64 * size_of::() as u64; - let info_gpu_mem_entry = self - .mem_space - .read_object::(GuestAddress(entry_addr)) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's resource attach backing request header", - req.out_header.0, - )) - })?; - let iov_base = self - .mem_space - .get_host_address(GuestAddress(info_gpu_mem_entry.addr)) - .with_context(|| "Fail to get gpu mem entry host addr")?; - let iov_item = Iovec { - iov_base, - iov_len: info_gpu_mem_entry.length as u64, - }; - res.iov.push(iov_item); - } + if i != 0 { + data_iovec = iov_discard_front_direct( + &mut data_iovec, + size_of::() as u64, + ) + .unwrap() + .to_vec(); + } - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + let mut entry = VirtioGpuMemEntry::default(); + if let Err(e) = + iov_to_buf_direct(&data_iovec, entry.as_mut_bytes()).and_then(|size| { + if size == size_of::() { + Ok(()) + } else { + bail!( + "GuestError: Invalid size of gpu request data: len {}.", + size + ); + } + }) + { + res.iov.clear(); + error!("{:?}", e); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } + + if let Some(iov_base) = self.mem_space.get_host_address(GuestAddress(entry.addr)) { + let iov_item = Iovec { + iov_base, + iov_len: entry.length as u64, + }; + res.iov.push(iov_item); + } else { + res.iov.clear(); + error!("GuestError: Map desc base {:?} failed.", entry.addr); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } + } + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( - "The resource_id {} in attach backing request request is not existed", + "The resource_id {} in attach backing request request is not existed.", info_attach_backing.resource_id ); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } - fn gpu_cmd_resource_detach_backing( - &mut self, - need_interrupt: &mut bool, - req: &VirtioGpuRequest, - ) -> Result<()> { - *need_interrupt = true; - let info_detach_backing = self - .mem_space - .read_object::(req.out_header) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the GPU's resource detach backing request header", - req.out_header.0, - )) - })?; + + fn gpu_cmd_resource_detach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { + let mut info_detach_backing = VirtioGpuResourceDetachBacking::default(); + self.get_request(req, &mut info_detach_backing)?; if let Some(res_index) = self .resources_list @@ -1435,76 +1261,37 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_index]; if res.iov.is_empty() { error!( - "The resource_id {} in resource detach backing request don't have iov", + "GuestError: The resource_id {} in resource detach backing request don't have iov.", info_detach_backing.resource_id ); - return self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } - self.gpu_clear_resource_iovs(res_index); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_OK_NODATA, req) + res.iov.clear(); + self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( - "The resource_id {} in detach backing request request is not existed", + "GuestError: The resource_id {} in detach backing request request is not existed.", info_detach_backing.resource_id ); - self.gpu_response_nodata(need_interrupt, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } fn process_control_queue(&mut self, mut req_queue: Vec) -> Result<()> { for req in req_queue.iter_mut() { - let mut need_interrupt = true; - if let Err(e) = match req.header.hdr_type { - VIRTIO_GPU_CMD_GET_DISPLAY_INFO => self - .gpu_cmd_get_display_info(&mut need_interrupt, req) - .with_context(|| "Fail to get display info"), - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => self - .gpu_cmd_resource_create_2d(&mut need_interrupt, req) - .with_context(|| "Fail to create 2d resource"), - VIRTIO_GPU_CMD_RESOURCE_UNREF => self - .gpu_cmd_resource_unref(&mut need_interrupt, req) - .with_context(|| "Fail to unref resource"), - VIRTIO_GPU_CMD_SET_SCANOUT => self - .gpu_cmd_set_scanout(&mut need_interrupt, req) - .with_context(|| "Fail to set scanout"), - VIRTIO_GPU_CMD_RESOURCE_FLUSH => self - .gpu_cmd_resource_flush(&mut need_interrupt, req) - .with_context(|| "Fail to flush resource"), - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self - .gpu_cmd_transfer_to_host_2d(&mut need_interrupt, req) - .with_context(|| "Fail to transfer fo host 2d resource"), - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self - .gpu_cmd_resource_attach_backing(&mut need_interrupt, req) - .with_context(|| "Fail to attach backing"), - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self - .gpu_cmd_resource_detach_backing(&mut need_interrupt, req) - .with_context(|| "Fail to detach backing"), - VIRTIO_GPU_CMD_GET_EDID => self - .gpu_cmd_get_edid(&mut need_interrupt, req) - .with_context(|| "Fail to get edid info"), - _ => self - .gpu_response_nodata(&mut need_interrupt, VIRTIO_GPU_RESP_ERR_UNSPEC, req) - .with_context(|| "Fail to get nodata response"), + VIRTIO_GPU_CMD_GET_DISPLAY_INFO => self.gpu_cmd_get_display_info(req), + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => self.gpu_cmd_resource_create_2d(req), + VIRTIO_GPU_CMD_RESOURCE_UNREF => self.gpu_cmd_resource_unref(req), + VIRTIO_GPU_CMD_SET_SCANOUT => self.gpu_cmd_set_scanout(req), + VIRTIO_GPU_CMD_RESOURCE_FLUSH => self.gpu_cmd_resource_flush(req), + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self.gpu_cmd_transfer_to_host_2d(req), + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self.gpu_cmd_resource_attach_backing(req), + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self.gpu_cmd_resource_detach_backing(req), + VIRTIO_GPU_CMD_GET_EDID => self.gpu_cmd_get_edid(req), + _ => self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), } { - error!("Fail to handle GPU request, {:?}", e); - } - - if need_interrupt { - let mut queue_lock = self.ctrl_queue.lock().unwrap(); - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { - if let Err(e) = - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) - { - error!( - "Failed to trigger interrupt(aio completion) for gpu device, error is {:?}", - e - ); - } - } + bail!("Fail to handle GPU request, {:?}.", e); } } @@ -1515,23 +1302,33 @@ impl GpuIoHandler { let mut queue = self.ctrl_queue.lock().unwrap(); let mut req_queue = Vec::new(); - while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + loop { + let elem = queue + .vring + .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { break; } - match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CTRL) { + + match VirtioGpuRequest::new(&self.mem_space, &elem) { Ok(req) => { req_queue.push(req); } + // TODO: Ignore this request may cause vnc suspended Err(e) => { + error!( + "GuestError: Failed to create GPU request, {:?}, just ignore it.", + e + ); queue .vring .add_used(&self.mem_space, elem.index, 0) .with_context(|| "Failed to add used ring")?; - error!("failed to create GPU request, {:?}", e); + break; } } } + drop(queue); self.process_control_queue(req_queue) } @@ -1540,32 +1337,49 @@ impl GpuIoHandler { let cursor_queue = self.cursor_queue.clone(); let mut queue = cursor_queue.lock().unwrap(); - while let Ok(elem) = queue.vring.pop_avail(&self.mem_space, self.driver_features) { + loop { + let elem = queue + .vring + .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { break; } - match VirtioGpuRequest::new(&self.mem_space, &elem, VIRTIO_GPU_CMD_CURSOR) { - Ok(req) => { - self.gpu_update_cursor(&req) - .with_context(|| "Fail to update cursor")?; - } - Err(e) => { - error!("failed to create GPU request, {:?}", e); + + match VirtioGpuRequest::new(&self.mem_space, &elem) { + Ok(req) => match self.gpu_update_cursor(&req) { + Ok(_) => {} + Err(e) => { + error!("Failed to handle gpu cursor cmd for {:?}.", e); + } + }, + // Ignore the request has no effect, because we handle it later. + Err(err) => { + error!("Failed to create GPU request, {:?}, just ignore it", err); } + }; + + if let Err(e) = queue.vring.add_used(&self.mem_space, elem.index, 0) { + bail!( + "Failed to add used ring(cursor), index {} {:?}", + elem.index, + e + ); } - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false).with_context( - || { - anyhow!(VirtioError::InterruptTrigger( - "gpu", + if queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + if let Err(e) = + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) + { + error!("{:?}", e); + return Err(anyhow!(VirtioError::InterruptTrigger( + "gpu cursor", VirtioInterruptType::Vring - )) - }, - )?; + ))); + } + } } Ok(()) -- Gitee From 3a4abadb95500781861cebdc656871c0ff32f447 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 6 Feb 2023 23:06:42 +0800 Subject: [PATCH 0754/2187] virtio-gpu: change name from gpu_x to x 1. gpu_x is a redundant name, it is better to change x. 2. move get_image_hostmem as pub function. Signed-off-by: Binfeng Wu --- virtio/src/gpu.rs | 185 ++++++++++++++++++++++------------------------ 1 file changed, 90 insertions(+), 95 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 3533c5840..cb140f284 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -452,7 +452,7 @@ fn create_surface( surface } -fn gpu_get_pixman_format(format: u32) -> Result { +fn get_pixman_format(format: u32) -> Result { match format { VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8r8g8b8), VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8r8g8b8), @@ -468,18 +468,13 @@ fn gpu_get_pixman_format(format: u32) -> Result { } } -impl GpuIoHandler { - fn gpu_get_image_hostmem( - &mut self, - format: pixman_format_code_t, - width: u32, - height: u32, - ) -> u64 { - let bpp = pixman_format_bpp(format as u32); - let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); - height as u64 * stride - } +fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) -> u64 { + let bpp = pixman_format_bpp(format as u32); + let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); + height as u64 * stride +} +impl GpuIoHandler { fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { if header.out_len < size_of::() as u32 { bail!("Invalid header for gpu request: len {}.", header.out_len) @@ -538,7 +533,7 @@ impl GpuIoHandler { Ok(()) } - fn gpu_response_nodata(&mut self, resp_head_type: u32, req: &VirtioGpuRequest) -> Result<()> { + fn response_nodata(&mut self, resp_head_type: u32, req: &VirtioGpuRequest) -> Result<()> { let mut resp = VirtioGpuCtrlHdr { hdr_type: resp_head_type, ..Default::default() @@ -553,7 +548,7 @@ impl GpuIoHandler { self.send_response(req, &resp) } - fn gpu_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_cursor = VirtioGpuUpdateCursor::default(); self.get_request(req, &mut info_cursor)?; @@ -613,7 +608,7 @@ impl GpuIoHandler { Ok(()) } - fn gpu_cmd_get_display_info(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_get_display_info(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut display_info = VirtioGpuDisplayInfo::default(); display_info.header.hdr_type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO; for i in 0..self.num_scanouts { @@ -634,7 +629,7 @@ impl GpuIoHandler { self.send_response(req, &display_info) } - fn gpu_cmd_get_edid(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_get_edid(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut edid_req = VirtioGpuGetEdid::default(); self.get_request(req, &mut edid_req)?; @@ -643,7 +638,7 @@ impl GpuIoHandler { "GuestError: The scanouts {} of request exceeds the max_outputs {}.", edid_req.scanouts, self.num_scanouts ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } let mut edid_resp = VirtioGpuRespEdid::default(); @@ -669,13 +664,13 @@ impl GpuIoHandler { Ok(()) } - fn gpu_cmd_resource_create_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_resource_create_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_create_2d = VirtioGpuResourceCreate2d::default(); self.get_request(req, &mut info_create_2d)?; if info_create_2d.resource_id == 0 { error!("GuestError: resource id 0 is not allowed."); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } if let Some(res) = self @@ -684,7 +679,7 @@ impl GpuIoHandler { .find(|&x| x.resource_id == info_create_2d.resource_id) { error!("GuestError: resource {} already exists.", res.resource_id); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } let mut res = GpuResource { @@ -694,16 +689,16 @@ impl GpuIoHandler { resource_id: info_create_2d.resource_id, ..Default::default() }; - let pixman_format = match gpu_get_pixman_format(res.format) { + let pixman_format = match get_pixman_format(res.format) { Ok(f) => f, Err(e) => { error!("GuestError: {:?}.", e); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } }; res.host_mem = - self.gpu_get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); + get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); if res.host_mem + self.used_hostmem < self.max_hostmem { res.pixman_image = unsafe { pixman_image_create_bits( @@ -720,15 +715,15 @@ impl GpuIoHandler { "GuestError: Fail to create resource(id {}, width {}, height {}) on host.", res.resource_id, res.width, res.height ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, req); } self.used_hostmem += res.host_mem; self.resources_list.push(res); - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } - fn gpu_cmd_resource_unref(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_resource_unref(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_resource_unref = VirtioGpuResourceUnref::default(); self.get_request(req, &mut info_resource_unref)?; @@ -757,17 +752,17 @@ impl GpuIoHandler { self.used_hostmem -= res.host_mem; res.iov.clear(); self.resources_list.remove(res_index); - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( "GuestError: illegal resource specified {}.", info_resource_unref.resource_id, ); - self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } - fn gpu_cmd_set_scanout(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_set_scanout(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_set_scanout = VirtioGpuSetScanout::default(); self.get_request(req, &mut info_set_scanout)?; @@ -776,7 +771,7 @@ impl GpuIoHandler { "GuestError: The scanout id {} is out of range.", info_set_scanout.scanout_id ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, req); } // TODO: refactor to disable function @@ -793,7 +788,7 @@ impl GpuIoHandler { } display_replace_surface(scanout.con_id, None); scanout.clear(); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req); + return self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req); } if let Some(res_index) = self @@ -822,7 +817,7 @@ impl GpuIoHandler { info_set_scanout.rect.x_coord, info_set_scanout.rect.y_coord, ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } let pixman_format = unsafe { pixman_image_get_format(res.pixman_image) }; @@ -847,7 +842,7 @@ impl GpuIoHandler { .is_null() { error!("HostError: surface image create failed, check pixman libary."); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } Some(sur) => { @@ -867,7 +862,7 @@ impl GpuIoHandler { .is_null() { error!("HostError: surface pixman image create failed, please check pixman libary."); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } } @@ -890,17 +885,17 @@ impl GpuIoHandler { scanout.width = info_set_scanout.rect.width; scanout.height = info_set_scanout.rect.height; - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( "GuestError: The resource_id {} in set_scanout {} request is not existed.", info_set_scanout.resource_id, info_set_scanout.scanout_id ); - self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } - fn gpu_cmd_resource_flush(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_resource_flush(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_res_flush = VirtioGpuResourceFlush::default(); self.get_request(req, &mut info_res_flush)?; @@ -923,7 +918,7 @@ impl GpuIoHandler { info_res_flush.rect.width, info_res_flush.rect.height, info_res_flush.rect.x_coord, info_res_flush.rect.y_coord, ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } unsafe { @@ -976,17 +971,17 @@ impl GpuIoHandler { } pixman_region_fini(flush_reg_ptr); } - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( "GuestError: The resource_id {} in resource flush request is not existed.", info_res_flush.resource_id ); - self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } - fn gpu_cmd_transfer_to_host_2d_params_check( + fn cmd_transfer_to_host_2d_params_check( &mut self, info_transfer: &VirtioGpuTransferToHost2d, ) -> u32 { @@ -1036,7 +1031,7 @@ impl GpuIoHandler { 0 } - fn gpu_cmd_transfer_to_host_2d_update_resource( + fn cmd_transfer_to_host_2d_update_resource( &mut self, info_transfer: &VirtioGpuTransferToHost2d, ) { @@ -1141,20 +1136,20 @@ impl GpuIoHandler { } } - fn gpu_cmd_transfer_to_host_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_transfer_to_host_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_transfer = VirtioGpuTransferToHost2d::default(); self.get_request(req, &mut info_transfer)?; - let errcode = self.gpu_cmd_transfer_to_host_2d_params_check(&info_transfer); + let errcode = self.cmd_transfer_to_host_2d_params_check(&info_transfer); if errcode != 0 { - return self.gpu_response_nodata(errcode, req); + return self.response_nodata(errcode, req); } - self.gpu_cmd_transfer_to_host_2d_update_resource(&info_transfer); - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.cmd_transfer_to_host_2d_update_resource(&info_transfer); + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } - fn gpu_cmd_resource_attach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_resource_attach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_attach_backing = VirtioGpuResourceAttachBacking::default(); self.get_request(req, &mut info_attach_backing)?; @@ -1169,7 +1164,7 @@ impl GpuIoHandler { "GuestError: The resource_id {} in resource attach backing request allready has iov.", info_attach_backing.resource_id ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } if info_attach_backing.nr_entries > 16384 { @@ -1177,7 +1172,7 @@ impl GpuIoHandler { "GuestError: The nr_entries in resource attach backing request is too large ( {} > 16384).", info_attach_backing.nr_entries ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } let esize = @@ -1187,7 +1182,7 @@ impl GpuIoHandler { "GuestError: The nr_entries {} in resource attach backing request is larger than total len {}.", info_attach_backing.nr_entries, req.out_len, ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } let mut data_iovec = req.out_iovec.clone(); @@ -1224,7 +1219,7 @@ impl GpuIoHandler { { res.iov.clear(); error!("{:?}", e); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } if let Some(iov_base) = self.mem_space.get_host_address(GuestAddress(entry.addr)) { @@ -1236,20 +1231,20 @@ impl GpuIoHandler { } else { res.iov.clear(); error!("GuestError: Map desc base {:?} failed.", entry.addr); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( "The resource_id {} in attach backing request request is not existed.", info_attach_backing.resource_id ); - self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } - fn gpu_cmd_resource_detach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { + fn cmd_resource_detach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_detach_backing = VirtioGpuResourceDetachBacking::default(); self.get_request(req, &mut info_detach_backing)?; @@ -1264,32 +1259,32 @@ impl GpuIoHandler { "GuestError: The resource_id {} in resource detach backing request don't have iov.", info_detach_backing.resource_id ); - return self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } res.iov.clear(); - self.gpu_response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( "GuestError: The resource_id {} in detach backing request request is not existed.", info_detach_backing.resource_id ); - self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) } } fn process_control_queue(&mut self, mut req_queue: Vec) -> Result<()> { for req in req_queue.iter_mut() { if let Err(e) = match req.header.hdr_type { - VIRTIO_GPU_CMD_GET_DISPLAY_INFO => self.gpu_cmd_get_display_info(req), - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => self.gpu_cmd_resource_create_2d(req), - VIRTIO_GPU_CMD_RESOURCE_UNREF => self.gpu_cmd_resource_unref(req), - VIRTIO_GPU_CMD_SET_SCANOUT => self.gpu_cmd_set_scanout(req), - VIRTIO_GPU_CMD_RESOURCE_FLUSH => self.gpu_cmd_resource_flush(req), - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self.gpu_cmd_transfer_to_host_2d(req), - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self.gpu_cmd_resource_attach_backing(req), - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self.gpu_cmd_resource_detach_backing(req), - VIRTIO_GPU_CMD_GET_EDID => self.gpu_cmd_get_edid(req), - _ => self.gpu_response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), + VIRTIO_GPU_CMD_GET_DISPLAY_INFO => self.cmd_get_display_info(req), + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => self.cmd_resource_create_2d(req), + VIRTIO_GPU_CMD_RESOURCE_UNREF => self.cmd_resource_unref(req), + VIRTIO_GPU_CMD_SET_SCANOUT => self.cmd_set_scanout(req), + VIRTIO_GPU_CMD_RESOURCE_FLUSH => self.cmd_resource_flush(req), + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self.cmd_transfer_to_host_2d(req), + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self.cmd_resource_attach_backing(req), + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self.cmd_resource_detach_backing(req), + VIRTIO_GPU_CMD_GET_EDID => self.cmd_get_edid(req), + _ => self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), } { bail!("Fail to handle GPU request, {:?}.", e); } @@ -1346,7 +1341,7 @@ impl GpuIoHandler { } match VirtioGpuRequest::new(&self.mem_space, &elem) { - Ok(req) => match self.gpu_update_cursor(&req) { + Ok(req) => match self.cmd_update_cursor(&req) { Ok(_) => {} Err(e) => { error!("Failed to handle gpu cursor cmd for {:?}.", e); @@ -1395,41 +1390,41 @@ impl Drop for GpuIoHandler { } impl EventNotifierHelper for GpuIoHandler { - fn internal_notifiers(gpu_handler: Arc>) -> Vec { + fn internal_notifiers(handler: Arc>) -> Vec { let mut notifiers = Vec::new(); // Register event notifier for ctrl_queue_evt. - let gpu_handler_clone = gpu_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { + let handler_clone = handler.clone(); + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(e) = gpu_handler_clone.lock().unwrap().ctrl_queue_evt_handler() { + if let Err(e) = handler_clone.lock().unwrap().ctrl_queue_evt_handler() { error!("Failed to process queue for virtio gpu, err: {:?}", e,); } None }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - gpu_handler.lock().unwrap().ctrl_queue_evt.as_raw_fd(), + handler.lock().unwrap().ctrl_queue_evt.as_raw_fd(), None, EventSet::IN, - vec![handler], + vec![h], )); // Register event notifier for cursor_queue_evt. - let gpu_handler_clone = gpu_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { + let handler_clone = handler.clone(); + let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - if let Err(e) = gpu_handler_clone.lock().unwrap().cursor_queue_evt_handler() { + if let Err(e) = handler_clone.lock().unwrap().cursor_queue_evt_handler() { error!("Failed to process queue for virtio gpu, err: {:?}", e,); } None }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - gpu_handler.lock().unwrap().cursor_queue_evt.as_raw_fd(), + handler.lock().unwrap().cursor_queue_evt.as_raw_fd(), None, EventSet::IN, - vec![handler], + vec![h], )); notifiers @@ -1461,7 +1456,7 @@ pub struct GpuState { #[derive(Default)] pub struct Gpu { /// Configuration of the GPU device. - gpu_cfg: GpuDevConfig, + cfg: GpuDevConfig, /// Status of the GPU device. state: GpuState, /// Callback to trigger interrupt. @@ -1471,9 +1466,9 @@ pub struct Gpu { } impl Gpu { - pub fn new(gpu_cfg: GpuDevConfig) -> Gpu { + pub fn new(cfg: GpuDevConfig) -> Gpu { Self { - gpu_cfg, + cfg, state: GpuState::default(), interrupt_cb: None, deactivate_evts: Vec::new(), @@ -1481,7 +1476,7 @@ impl Gpu { } fn build_device_config_space(&mut self) { - self.state.config_space.num_scanouts = self.gpu_cfg.max_outputs; + self.state.config_space.num_scanouts = self.cfg.max_outputs; self.state.config_space.reserved = 0; } } @@ -1489,10 +1484,10 @@ impl Gpu { impl VirtioDevice for Gpu { /// Realize virtio gpu device. fn realize(&mut self) -> Result<()> { - if self.gpu_cfg.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { + if self.cfg.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { bail!( "Invalid max_outputs {} which is bigger than {}", - self.gpu_cfg.max_outputs, + self.cfg.max_outputs, VIRTIO_GPU_MAX_SCANOUTS ); } @@ -1500,7 +1495,7 @@ impl VirtioDevice for Gpu { self.state.device_features = 1u64 << VIRTIO_F_VERSION_1; self.state.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; self.state.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; - if self.gpu_cfg.edid { + if self.cfg.edid { self.state.device_features |= 1 << VIRTIO_GPU_F_EDID; } @@ -1511,7 +1506,7 @@ impl VirtioDevice for Gpu { /// Unrealize low level device. fn unrealize(&mut self) -> Result<()> { - MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.gpu_cfg.id); + MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.cfg.id); Ok(()) } @@ -1612,7 +1607,7 @@ impl VirtioDevice for Gpu { scanouts.push(scanout); } - let mut gpu_handler = GpuIoHandler { + let mut handler = GpuIoHandler { ctrl_queue: queues[0].clone(), cursor_queue: queues[1].clone(), mem_space, @@ -1622,16 +1617,16 @@ impl VirtioDevice for Gpu { driver_features: self.state.driver_features, resources_list: Vec::new(), enable_output_bitmask: 1, - num_scanouts: self.gpu_cfg.max_outputs, + num_scanouts: self.cfg.max_outputs, req_states, scanouts, - max_hostmem: self.gpu_cfg.max_hostmem, + max_hostmem: self.cfg.max_hostmem, used_hostmem: 0, }; - gpu_handler.req_states[0].width = self.gpu_cfg.xres; - gpu_handler.req_states[0].height = self.gpu_cfg.yres; + handler.req_states[0].width = self.cfg.xres; + handler.req_states[0].height = self.cfg.yres; - let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(gpu_handler))); + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); register_event_helper(notifiers, None, &mut self.deactivate_evts)?; Ok(()) -- Gitee From 4fd3bacd4e66f4c4707b0ebc6bc53a6599fbcd35 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 6 Feb 2023 17:18:48 +0800 Subject: [PATCH 0755/2187] Fix: Reduces vcpu overhead during boot and restart. 1. Before the vcpu runs for the first time, the reset signal is sent to itself, which increases the signal processing overhead. It can directly call reset_vcpu. 2. When restart VM, it need to paused the vcpu. If it only kick vcpu, the vcpu is still running during the restart. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 9 ++++++++- machine/src/standard_vm/x86_64/mod.rs | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 9be6e8285..4c5bfe66c 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -688,7 +688,14 @@ impl CPUThreadWorker { // reset its running environment. #[cfg(not(test))] self.thread_cpu - .reset() + .arch_cpu + .lock() + .unwrap() + .reset_vcpu( + &self.thread_cpu.fd, + #[cfg(target_arch = "x86_64")] + &self.thread_cpu.caps, + ) .with_context(|| "Failed to reset for cpu register state")?; // Wait for all vcpu to complete the running diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 910336b69..45eaad8bb 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -185,20 +185,24 @@ impl StdMachine { let mut locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { - Result::with_context(cpu.kick(), || format!("Failed to kick vcpu{}", cpu_index))?; + cpu.pause() + .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; cpu.set_to_boot_state(); } - Result::with_context(locked_vm.reset_all_devices(), || { - "Fail to reset all devices" - })?; - Result::with_context(locked_vm.reset_fwcfg_boot_order(), || { - "Fail to update boot order information to FwCfg device" - })?; + locked_vm + .reset_all_devices() + .with_context(|| "Fail to reset all devices")?; + locked_vm + .reset_fwcfg_boot_order() + .with_context(|| "Fail to update boot order information to FwCfg device")?; for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { - Result::with_context(cpu.reset(), || format!("Failed to reset vcpu{}", cpu_index))?; + cpu.reset() + .with_context(|| format!("Failed to reset vcpu{}", cpu_index))?; + cpu.resume() + .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; } Ok(()) -- Gitee From 73eb35dd211d19958bf60108535f831e07ac2cee Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 3 Feb 2023 14:26:34 +0800 Subject: [PATCH 0756/2187] support parameter -accel The libvirt v7.10.0 (commit id d20ebdda28) changed `-machine accel=XXX` to `-accel=XXX`, let's add `-accel` parameter support for compatibility with libvirt. Signed-off-by: yezengruan --- machine_manager/src/cmdline.rs | 8 ++++++++ machine_manager/src/config/machine_config.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 10b6014f4..a1db6680d 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -92,6 +92,13 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { 'mem-share' sets guest memory is shareable.") .takes_value(true), ) + .arg( + Arg::with_name("accel") + .long("accel") + .value_name("[accel]") + .help("select accelerator, only 'kvm' is supported now.") + .takes_value(true), + ) .arg( Arg::with_name("smp") .long("smp") @@ -451,6 +458,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { // Parse cmdline args which need to set in VmConfig add_args_to_config!((args.value_of("name")), vm_cfg, add_name); add_args_to_config!((args.value_of("machine")), vm_cfg, add_machine); + add_args_to_config!((args.value_of("accel")), vm_cfg, add_accel); add_args_to_config!((args.value_of("memory")), vm_cfg, add_memory); add_args_to_config!((args.value_of("mem-path")), vm_cfg, add_mem_path); add_args_to_config!((args.value_of("smp")), vm_cfg, add_cpu); diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 96f76c48b..57d0ac92b 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -212,6 +212,7 @@ impl VmConfig { } if let Some(accel) = cmd_parser.get_value::("accel")? { + // Libvirt checks the parameter types of 'kvm', 'kvm:tcg' and 'tcg'. if accel.ne("kvm:tcg") && accel.ne("tcg") && accel.ne("kvm") { bail!("Only \'kvm\', \'kvm:tcg\' and \'tcg\' are supported for \'accel\' of \'machine\'"); } @@ -243,6 +244,21 @@ impl VmConfig { Ok(()) } + /// Add '-accel' accelerator config to `VmConfig`. + pub fn add_accel(&mut self, accel_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("accel"); + cmd_parser.push(""); + cmd_parser.parse(accel_config)?; + + if let Some(accel) = cmd_parser.get_value::("")? { + if accel.ne("kvm") { + bail!("Only \'kvm\' is supported for \'accel\'"); + } + } + + Ok(()) + } + /// Add '-m' memory config to `VmConfig`. pub fn add_memory(&mut self, mem_config: &str) -> Result<()> { let mut cmd_parser = CmdParser::new("m"); -- Gitee From 32c891da812da7e482f9304c74ad6b1484e78c8f Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Thu, 9 Feb 2023 12:42:19 +0000 Subject: [PATCH 0757/2187] rename specific name like QemuXX to XX Signed-off-by: wubinfeng --- vnc/src/console.rs | 6 +++--- vnc/src/pixman.rs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/vnc/src/console.rs b/vnc/src/console.rs index 6d93db84f..777694936 100644 --- a/vnc/src/console.rs +++ b/vnc/src/console.rs @@ -12,7 +12,7 @@ use crate::pixman::{ get_image_height, get_image_width, pixman_glyph_from_vgafont, pixman_glyph_render, - unref_pixman_image, QemuColorNames, COLOR_TABLE_RGB, + unref_pixman_image, ColorNames, COLOR_TABLE_RGB, }; use log::error; use machine_manager::event_loop::EventLoop; @@ -567,8 +567,8 @@ fn create_msg_surface(width: i32, height: i32, msg: String) -> Option Date: Sun, 12 Feb 2023 10:47:55 +0000 Subject: [PATCH 0758/2187] virtio-gpu: refactor redundant unwrap calls Use temporary variables instead of redundant unwrap calls. Signed-off-by: wubinfeng --- virtio/src/gpu.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index cb140f284..f409fc4c1 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -581,18 +581,16 @@ impl GpuIoHandler { unsafe { let res_width = pixman_image_get_width(res.pixman_image); let res_height = pixman_image_get_height(res.pixman_image); + let mse = scanout.mouse.as_mut().unwrap(); - if res_width as u32 == scanout.mouse.as_ref().unwrap().width - && res_height as u32 == scanout.mouse.as_ref().unwrap().height - { - let pixels = scanout.mouse.as_ref().unwrap().width - * scanout.mouse.as_ref().unwrap().height; + if res_width as u32 == mse.width && res_height as u32 == mse.height { + let pixels = mse.width * mse.height; let mouse_data_size = pixels * (size_of::() as u32); let mut con = vec![0u8; 64 * 64 * 4]; let res_data_ptr = pixman_image_get_data(res.pixman_image) as *mut u8; ptr::copy(res_data_ptr, con.as_mut_ptr(), mouse_data_size as usize); - scanout.mouse.as_mut().unwrap().data.clear(); - scanout.mouse.as_mut().unwrap().data.append(&mut con); + mse.data.clear(); + mse.data.append(&mut con); } } } @@ -1035,6 +1033,7 @@ impl GpuIoHandler { &mut self, info_transfer: &VirtioGpuTransferToHost2d, ) { + // SAFETY: unwrap is safe because it has been checked in params check. let res = self .resources_list .iter() @@ -1391,6 +1390,7 @@ impl Drop for GpuIoHandler { impl EventNotifierHelper for GpuIoHandler { fn internal_notifiers(handler: Arc>) -> Vec { + let handler_raw = handler.lock().unwrap(); let mut notifiers = Vec::new(); // Register event notifier for ctrl_queue_evt. @@ -1404,7 +1404,7 @@ impl EventNotifierHelper for GpuIoHandler { }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - handler.lock().unwrap().ctrl_queue_evt.as_raw_fd(), + handler_raw.ctrl_queue_evt.as_raw_fd(), None, EventSet::IN, vec![h], @@ -1421,7 +1421,7 @@ impl EventNotifierHelper for GpuIoHandler { }); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, - handler.lock().unwrap().cursor_queue_evt.as_raw_fd(), + handler_raw.cursor_queue_evt.as_raw_fd(), None, EventSet::IN, vec![h], -- Gitee From af6f4d9ecc31b6b63669a07a44d409a9295b7535 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 13 Feb 2023 08:44:35 +0000 Subject: [PATCH 0759/2187] virtio-gpu: fix overflow problems Fix boundary value bug in max memory check, and using checked_XX to simplify checking. Signed-off-by: wubinfeng --- virtio/src/gpu.rs | 50 ++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index f409fc4c1..d479ca58a 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -474,6 +474,23 @@ fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) -> u height as u64 * stride } +fn is_rect_in_resouce(rect: &VirtioGpuRect, res: &GpuResource) -> bool { + if rect + .x_coord + .checked_add(rect.width) + .filter(|&sum| sum <= res.width) + .is_some() + && rect + .y_coord + .checked_add(rect.height) + .filter(|&sum| sum <= res.height) + .is_some() + { + return true; + } + false +} + impl GpuIoHandler { fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { if header.out_len < size_of::() as u32 { @@ -697,7 +714,13 @@ impl GpuIoHandler { res.host_mem = get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); - if res.host_mem + self.used_hostmem < self.max_hostmem { + + if res + .host_mem + .checked_add(self.used_hostmem) + .filter(|&sum| sum <= self.max_hostmem) + .is_some() + { res.pixman_image = unsafe { pixman_image_create_bits( pixman_format, @@ -795,14 +818,9 @@ impl GpuIoHandler { .position(|x| x.resource_id == info_set_scanout.resource_id) { let res = &self.resources_list[res_index]; - if info_set_scanout.rect.x_coord > res.width - || info_set_scanout.rect.y_coord > res.height - || info_set_scanout.rect.width > res.width - || info_set_scanout.rect.height > res.height - || info_set_scanout.rect.width < 16 + if info_set_scanout.rect.width < 16 || info_set_scanout.rect.height < 16 - || info_set_scanout.rect.width + info_set_scanout.rect.x_coord > res.width - || info_set_scanout.rect.height + info_set_scanout.rect.y_coord > res.height + || !is_rect_in_resouce(&info_set_scanout.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {}).", @@ -903,13 +921,7 @@ impl GpuIoHandler { .position(|x| x.resource_id == info_res_flush.resource_id) { let res = &self.resources_list[res_index]; - if info_res_flush.rect.x_coord > res.width - || info_res_flush.rect.y_coord > res.height - || info_res_flush.rect.width > res.width - || info_res_flush.rect.height > res.height - || info_res_flush.rect.width + info_res_flush.rect.x_coord > res.width - || info_res_flush.rect.height + info_res_flush.rect.y_coord > res.height - { + if !is_rect_in_resouce(&info_res_flush.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, res.width, res.height, @@ -1005,13 +1017,7 @@ impl GpuIoHandler { return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; } - if info_transfer.rect.x_coord > res.width - || info_transfer.rect.y_coord > res.height - || info_transfer.rect.width > res.width - || info_transfer.rect.height > res.height - || info_transfer.rect.width + info_transfer.rect.x_coord > res.width - || info_transfer.rect.height + info_transfer.rect.y_coord > res.height - { + if !is_rect_in_resouce(&info_transfer.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, -- Gitee From 61bae821a42a161d33290c5f5ad89f1dbeb41940 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 17 Feb 2023 15:00:41 +0800 Subject: [PATCH 0760/2187] pl031: add comment for RTC_MR register The RTC alarm function is not implemented yet, add comment info for RTC_MR register. Signed-off-by: yezengruan --- devices/src/legacy/pl031.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 36caa9938..dca4b4c0c 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -163,7 +163,14 @@ impl SysBusDevOps for PL031 { let value = LittleEndian::read_u32(data); match offset { - RTC_MR => self.state.mr = value, + RTC_MR => { + // TODO: The MR register is used for implemeting the RTC alarm. A RTC alarm is a feature + // that can be used to allow a computer to 'wake up' after shut down to execute tasks + // every day or on a certain day. It can sometimes be found in the 'Power Management' + // section of motherboard's BIOS setup. This RTC alarm function is not implemented yet, + // here is a reminder just in case. + self.state.mr = value; + } RTC_LR => { self.state.lr = value; self.tick_offset = value; -- Gitee From a3837d14e3efdeb02f745c535e4a3ace44d9488d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 01:11:27 +0800 Subject: [PATCH 0761/2187] virtio-scsi: check in_iov and out_iov before using them In_iov and out_iov should never be zero length in scsi request queue. Check them. Signed-off-by: liuxiangdong --- virtio/src/scsi/controller.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 56b36ffe5..b14be0a98 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -483,6 +483,15 @@ impl VirtioScsiRequest { driver_features: u64, elem: &Element, ) -> Result { + if elem.out_iovec.is_empty() || elem.in_iovec.is_empty() { + bail!( + "Missed header for scsi request: out {} in {} desc num {}", + elem.out_iovec.len(), + elem.in_iovec.len(), + elem.desc_num + ); + } + let out_iov_elem = elem.out_iovec.get(0).unwrap(); if out_iov_elem.len < size_of::() as u32 { bail!( -- Gitee From 9c789e9df3f7c665d8df03884875bdbd799ea52f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 20:28:40 +0800 Subject: [PATCH 0762/2187] virtio-scsi: delete some useless code Remove some useless code. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 65 +++-------------------------------------- virtio/src/scsi/disk.rs | 16 ---------- 2 files changed, 4 insertions(+), 77 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index d753e0956..b2fc79883 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -588,7 +588,6 @@ impl ScsiRequest { sense = Some(SCSI_SENSE_NO_SENSE); Ok(Vec::new()) } - WRITE_SAME_10 | WRITE_SAME_16 => Ok(Vec::new()), TEST_UNIT_READY => { let dev_lock = self.dev.lock().unwrap(); if dev_lock.disk_image.is_none() { @@ -756,7 +755,6 @@ fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc>) -> i32 { let dev_lock = dev.lock().unwrap(); let block_size = dev_lock.block_size as i32; - let scsi_type = dev_lock.scsi_type; drop(dev_lock); let mut xfer = match cdb[0] >> 5 { @@ -774,79 +772,24 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc { + TEST_UNIT_READY | START_STOP | SYNCHRONIZE_CACHE | SYNCHRONIZE_CACHE_16 => { xfer = 0; } - VERIFY_10 | VERIFY_12 | VERIFY_16 => { - if cdb[1] & 2 == 0 { - xfer = 0; - } else if cdb[1] & 4 != 0 { - xfer = 1; - } - xfer *= block_size; - } - WRITE_SAME_10 | WRITE_SAME_16 => { - if cdb[1] & 1 != 0 { - xfer = 0; - } else { - xfer = block_size; - } - } READ_CAPACITY_10 => { xfer = 8; } - READ_BLOCK_LIMITS => { - xfer = 6; - } - SEND_VOLUME_TAG => { - xfer = match scsi_type { - SCSI_TYPE_DISK => i32::from(cdb[9]) | i32::from(cdb[8]) << 8, - _ => i32::from(cdb[10]) | i32::from(cdb[8]) << 8, - }; - } - WRITE_6 | READ_6 | READ_REVERSE => { + WRITE_6 | READ_6 => { // length 0 means 256 blocks. if xfer == 0 { xfer = 256 * block_size; } } - WRITE_10 | WRITE_VERIFY_10 | WRITE_12 | WRITE_VERIFY_12 | WRITE_16 | WRITE_VERIFY_16 - | READ_10 | READ_12 | READ_16 => { + WRITE_10 | WRITE_12 | WRITE_16 | READ_10 | READ_12 | READ_16 => { xfer *= block_size; } - FORMAT_UNIT => { - xfer = if (scsi_type == SCSI_TYPE_ROM) && (cdb[1] & 16 != 0) { - 12 - } else if cdb[1] & 16 == 0 { - 0 - } else if cdb[1] & 32 == 0 { - 4 - } else { - 8 - }; - } - INQUIRY | RECEIVE_DIAGNOSTIC | SEND_DIAGNOSTIC => { + INQUIRY => { xfer = i32::from(cdb[4]) | i32::from(cdb[3]) << 8; } - READ_CD | READ_BUFFER | WRITE_BUFFER | SEND_CUE_SHEET => { - xfer = i32::from(cdb[8]) | i32::from(cdb[7]) << 8 | (u32::from(cdb[6]) << 16) as i32; - } - PERSISTENT_RESERVE_OUT => { - xfer = BigEndian::read_i32(&cdb[5..]); - } - ERASE_12 => {} - MECHANISM_STATUS | READ_DVD_STRUCTURE | SEND_DVD_STRUCTURE | MAINTENANCE_OUT - | MAINTENANCE_IN => { - if scsi_type == SCSI_TYPE_ROM { - xfer = i32::from(cdb[9]) | i32::from(cdb[8]) << 8; - } - } - ATA_PASSTHROUGH_12 => {} - ATA_PASSTHROUGH_16 => {} _ => {} } xfer diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 4e7815e1f..7983d6506 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -110,22 +110,6 @@ pub struct ScsiDevice { drive_files: Arc>>, } -impl Default for ScsiDevice { - fn default() -> Self { - ScsiDevice { - config: Default::default(), - state: Default::default(), - disk_image: None, - req_align: 1, - buf_align: 1, - disk_sectors: 0, - block_size: SCSI_DISK_DEFAULT_BLOCK_SIZE, - scsi_type: SCSI_TYPE_DISK, - parent_bus: Weak::new(), - drive_files: Arc::new(Mutex::new(HashMap::new())), - } - } -} impl ScsiDevice { pub fn new( -- Gitee From 88c2490f94188d4de8c284494c3e0a7d7e1066fb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 23:54:34 +0800 Subject: [PATCH 0763/2187] scsi-bus: check read/write buffer size to avoid out of range Check read/write buffer size to avoid out of range in READ_10/WRITE_10 scsi command. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 35 +++++++++++++++++++++++++++++++++-- virtio/src/scsi/controller.rs | 2 +- virtio/src/scsi/disk.rs | 3 +-- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index b2fc79883..7afc92db6 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -24,7 +24,7 @@ use crate::ScsiCntlr::{ use crate::ScsiDisk::{ ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, - SCSI_TYPE_ROM, + SCSI_TYPE_ROM, SECTOR_SHIFT, }; use address_space::AddressSpace; use byteorder::{BigEndian, ByteOrder}; @@ -481,15 +481,46 @@ impl ScsiRequest { scsibus: Arc>, scsidevice: Arc>, ) -> Result { + let req_lock = req.lock().unwrap(); + let cdb = req_lock.req.cdb; + let req_size = req_lock.data_len; + if let Some(cmd) = scsibus .lock() .unwrap() - .scsi_bus_parse_req_cdb(req.lock().unwrap().req.cdb, scsidevice.clone()) + .scsi_bus_parse_req_cdb(cdb, scsidevice.clone()) { let ops = cmd.command; let opstype = scsi_operation_type(ops); let _resid = cmd.xfer; + if ops == WRITE_10 || ops == READ_10 { + let dev_lock = scsidevice.lock().unwrap(); + let disk_size = dev_lock.disk_sectors << SECTOR_SHIFT; + let disk_type = dev_lock.scsi_type; + drop(dev_lock); + let offset_shift = match disk_type { + SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, + _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, + }; + let offset = if let Some(off) = cmd.lba.checked_shl(offset_shift) { + off + } else { + bail!("Too large offset IO!"); + }; + + if offset + .checked_add(req_size as u64) + .filter(|&off| off <= disk_size) + .is_none() + { + bail!( + "Error CDB! ops {}, read/write length {} from {} is larger than disk size {}", + ops, req_size, offset, disk_size, + ); + } + } + Ok(ScsiRequest { cmd, _sense: [0; SCSI_SENSE_BUF_SIZE], diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index b14be0a98..a8790210c 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -462,7 +462,7 @@ pub struct VirtioScsiRequest { desc_index: u16, /// Read or Write data, HVA, except resp. pub iovec: Vec, - data_len: u32, + pub data_len: u32, _cdb_size: u32, _sense_size: u32, mode: ScsiXferMode, diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index 7983d6506..9a7724b7c 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -44,7 +44,7 @@ pub const SCSI_DISK_F_REMOVABLE: u32 = 0; pub const SCSI_DISK_F_DPOFUA: u32 = 1; /// Used to compute the number of sectors. -const SECTOR_SHIFT: u8 = 9; +pub const SECTOR_SHIFT: u8 = 9; /// Size of the dummy block device. const DUMMY_IMG_SIZE: u64 = 0; pub const DEFAULT_SECTOR_SIZE: u32 = 1_u32 << SECTOR_SHIFT; @@ -110,7 +110,6 @@ pub struct ScsiDevice { drive_files: Arc>>, } - impl ScsiDevice { pub fn new( config: ScsiDevConfig, -- Gitee From 9fa82b566350d5c2968571828f06cee60841cc3f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 05:12:38 +0800 Subject: [PATCH 0764/2187] vhost-client: only support inflight for vhost-user-blk Only vhost-user-blk supports inflight for reconnecting, and other vhost backends do not support. Signed-off-by: liuxiangdong --- virtio/src/vhost/user/block.rs | 13 ++++++++++--- virtio/src/vhost/user/client.rs | 20 +++++++++++++++++++- virtio/src/vhost/user/fs.rs | 15 ++++++++++----- virtio/src/vhost/user/net.rs | 11 ++++++++--- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 515ed5a9b..7677978dd 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -24,7 +24,9 @@ use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; use crate::vhost::VhostOps; -use crate::VhostUser::client::{VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ}; +use crate::VhostUser::client::{ + VhostBackendType, VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ, +}; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; use crate::{ virtio_has_feature, BlockState, VirtioDevice, VirtioInterrupt, VIRTIO_BLK_F_BLK_SIZE, @@ -76,8 +78,13 @@ impl Block { .as_ref() .map(|path| path.to_string()) .with_context(|| "vhost-user: socket path is not found")?; - let client = VhostUserClient::new(&self.mem_space, &socket_path, self.queue_num() as u64) - .with_context(|| { + let client = VhostUserClient::new( + &self.mem_space, + &socket_path, + self.queue_num() as u64, + VhostBackendType::TypeBlock, + ) + .with_context(|| { "Failed to create the client which communicates with the server for vhost-user blk" })?; let client = Arc::new(Mutex::new(client)); diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 23f80b4d8..98782bd64 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -346,6 +346,13 @@ pub struct VhostInflight { pub inner: VhostUserInflight, } +#[derive(PartialEq)] +pub enum VhostBackendType { + TypeNet, + TypeBlock, + TypeFs, +} + /// Struct for communication with the vhost user backend in userspace pub struct VhostUserClient { client: Arc>, @@ -358,10 +365,16 @@ pub struct VhostUserClient { pub features: u64, reconnecting: bool, inflight: Option, + backend_type: VhostBackendType, } impl VhostUserClient { - pub fn new(mem_space: &Arc, path: &str, max_queue_num: u64) -> Result { + pub fn new( + mem_space: &Arc, + path: &str, + max_queue_num: u64, + backend_type: VhostBackendType, + ) -> Result { let mut sock = VhostUserSock::new(path); sock.domain.connect().with_context(|| { format!( @@ -387,6 +400,7 @@ impl VhostUserClient { features: 0, reconnecting: false, inflight: None, + backend_type, }) } @@ -413,6 +427,10 @@ impl VhostUserClient { /// Set inflight fd, include get inflight fd from vhost and set inflight to vhost. pub fn set_inflight(&mut self, queue_num: u16, queue_size: u16) -> Result<()> { + if self.backend_type != VhostBackendType::TypeBlock { + // Only vhost-user-blk supports inflight fd now. + return Ok(()); + } let protocol_feature = self .get_protocol_features() .with_context(|| "Failed to get protocol features for vhost-user blk")?; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index aab233f80..a2c823360 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -38,7 +38,7 @@ use util::num_ops::read_u32; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; use super::super::{VhostNotify, VhostOps}; -use super::VhostUserClient; +use super::{VhostBackendType, VhostUserClient}; use crate::{VirtioInterrupt, VirtioInterruptType}; use anyhow::{anyhow, Context, Result}; @@ -143,10 +143,15 @@ impl VirtioDevice for Fs { self.config.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; - let client = VhostUserClient::new(&self.mem_space, &self.fs_cfg.sock, queues_num as u64) - .with_context(|| { - "Failed to create the client which communicates with the server for virtio fs" - })?; + let client = VhostUserClient::new( + &self.mem_space, + &self.fs_cfg.sock, + queues_num as u64, + VhostBackendType::TypeFs, + ) + .with_context(|| { + "Failed to create the client which communicates with the server for virtio fs" + })?; let client = Arc::new(Mutex::new(client)); VhostUserClient::add_event(&client)?; self.avail_features = client diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 14655c523..9e0f7eb16 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -34,7 +34,7 @@ use super::super::super::{ VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; use super::super::VhostOps; -use super::VhostUserClient; +use super::{VhostBackendType, VhostUserClient}; use crate::error::VirtioError; use anyhow::{anyhow, Context, Result}; @@ -113,8 +113,13 @@ impl VirtioDevice for Net { .as_ref() .map(|path| path.to_string()) .with_context(|| "vhost-user: socket path is not found")?; - let client = VhostUserClient::new(&self.mem_space, &socket_path, self.queue_num() as u64) - .with_context(|| { + let client = VhostUserClient::new( + &self.mem_space, + &socket_path, + self.queue_num() as u64, + VhostBackendType::TypeNet, + ) + .with_context(|| { "Failed to create the client which communicates with the server for vhost-user net" })?; let client = Arc::new(Mutex::new(client)); -- Gitee From d4fa2bb576f709bd26eddf6662a4c21dba0d4323 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 18 Feb 2023 17:25:12 +0800 Subject: [PATCH 0765/2187] Fix: Delete unused code in fwcfg and optimize the code There some function defined in fwcfg but does't use. Signed-off-by: Mingwang Li --- .../src/interrupt_controller/aarch64/gicv3.rs | 6 +----- devices/src/legacy/fwcfg.rs | 19 ------------------- machine_manager/src/config/drive.rs | 16 +++++++--------- util/src/aio/mod.rs | 4 ++-- virtio/src/block.rs | 8 ++++---- 5 files changed, 14 insertions(+), 39 deletions(-) diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 6db4a2ce4..4f5d6f61b 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -304,11 +304,7 @@ impl GICv3Access for GICv3 { let affid = (aff1 << 8) | aff0; let cpu_affid: u64 = ((affid & 0xFF_0000_0000) >> 8) | (affid & 0xFF_FFFF); - let last = if (self.vcpu_count - 1) == cpu as u64 { - 1 - } else { - 0 - }; + let last = u64::from((self.vcpu_count - 1) == cpu as u64); ((cpu_affid << 32) | (1 << 24) | (1 << 8) | (last << 4)) & kvm_bindings::KVM_DEV_ARM_VGIC_V3_MPIDR_MASK as u64 diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 15b5b3fbf..b6e592043 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -176,25 +176,6 @@ struct FwCfgFile { name: [u8; 56], } -impl Eq for FwCfgFile {} - -impl PartialEq for FwCfgFile { - fn eq(&self, other: &Self) -> bool { - self.name.to_vec() == other.name.to_vec() - } -} - -impl Default for FwCfgFile { - fn default() -> Self { - FwCfgFile { - size: 0_u32, - select: 0_u16, - reserved: 0_u16, - name: [0_u8; 56], - } - } -} - impl FwCfgFile { fn new(size: u32, select: u16, name: &str) -> Self { let len = std::cmp::min(56, name.len()); diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index c4dbefddc..3e787fa0e 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -292,15 +292,13 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { drive.direct = direct.into(); } drive.iops = cmd_parser.get_value::("throttling.iops-total")?; - drive.aio = cmd_parser - .get_value::("aio")? - .unwrap_or_else(|| { - if drive.direct { - AioEngine::Native - } else { - AioEngine::Off - } - }); + drive.aio = cmd_parser.get_value::("aio")?.unwrap_or({ + if drive.direct { + AioEngine::Native + } else { + AioEngine::Off + } + }); drive.check()?; #[cfg(not(test))] drive.check_path()?; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 6ffb7da39..df5cde977 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -169,7 +169,7 @@ impl Aio { } } - return match cb.opcode { + match cb.opcode { OpCode::Preadv | OpCode::Pwritev => { if self.ctx.is_some() { self.rw_aio(cb) @@ -179,7 +179,7 @@ impl Aio { } OpCode::Fdsync => self.flush_sync(cb), OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), - }; + } } pub fn handle_complete(&mut self) -> Result { diff --git a/virtio/src/block.rs b/virtio/src/block.rs index a75ac54e3..c156b3e32 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -754,7 +754,7 @@ impl EventNotifierHelper for BlockIoHandler { if h_lock.device_broken.load(Ordering::SeqCst) { return None; } - return match h_lock.process_queue() { + match h_lock.process_queue() { Ok(done) => { if done { Some(Vec::new()) @@ -766,7 +766,7 @@ impl EventNotifierHelper for BlockIoHandler { error!("Failed to handle block IO {:?}", e); None } - }; + } }); notifiers.push(build_event_notifier( handler_raw.queue_evt.as_raw_fd(), @@ -816,7 +816,7 @@ impl EventNotifierHelper for BlockIoHandler { if h_lock.aio.get_engine() == AioEngine::Off { return None; } - return match h_lock.aio_complete_handler() { + match h_lock.aio_complete_handler() { Ok(done) => { if done { Some(Vec::new()) @@ -828,7 +828,7 @@ impl EventNotifierHelper for BlockIoHandler { error!("Failed to handle aio {:?}", e); None } - }; + } }); notifiers.push(build_event_notifier( handler_raw.aio.fd.as_raw_fd(), -- Gitee From 0a88ae17b3f551af2be0f552de9f494e407f760e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 14:36:39 +0800 Subject: [PATCH 0766/2187] message: opt_long help message should be handled Handle opt_long cmd options in help message. Signed-off-by: liuxiangdong --- util/src/arg_parser.rs | 54 ++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index 424f8bc87..d0befdb2d 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -467,9 +467,13 @@ impl<'a> Arg<'a> { /// Produce help message for argument. fn help_message(&self) -> (String, HelpType) { + let mut help_str; + if self.hiddable { - (String::new(), HelpType::Hidden) - } else if self.short.is_some() { + return (String::new(), HelpType::Hidden); + } + + if self.short.is_some() { let font_str = format!( "{}{}{}, {}{}", FOUR_BLANK, @@ -478,29 +482,41 @@ impl<'a> Arg<'a> { PREFIX_CHARS_LONG, self.long.unwrap_or("") ); - let mut help_str = format!("{}{}", TWENTY_FOUT_BLANK, self.help.unwrap_or("")); + help_str = format!("{}{}", TWENTY_FOUT_BLANK, self.help.unwrap_or("")); let font_offset = font_str.len(); help_str.replace_range(..font_offset, &font_str); - (help_str, HelpType::Flags) - } else { - let font_str = if self.values.is_some() { - format!( - "{}{}{} {}...", - EIGHT_BLANK, - PREFIX_CHARS_LONG, - self.long.unwrap(), - self.value_name.unwrap_or(self.name) - ) + return (help_str, HelpType::Flags); + } + + if self.long.is_some() || self.opt_long.is_some() { + let font_str = if self.long.is_some() { + if self.values.is_some() { + format!( + "{}{}{} {}...", + EIGHT_BLANK, + PREFIX_CHARS_LONG, + self.long.unwrap(), + self.value_name.unwrap_or(self.name) + ) + } else { + format!( + "{}{}{} {}", + EIGHT_BLANK, + PREFIX_CHARS_LONG, + self.long.unwrap(), + self.value_name.unwrap_or(self.name) + ) + } } else { format!( - "{}{}{} {}", + "{}{}{}={}", EIGHT_BLANK, - PREFIX_CHARS_LONG, - self.long.unwrap(), + PREFIX_OPT_LONG, + self.opt_long.unwrap(), self.value_name.unwrap_or(self.name) ) }; - let mut help_str = format!( + help_str = format!( "{}{}{}{}", TWENTY_FOUT_BLANK, TWENTY_FOUT_BLANK, @@ -513,8 +529,10 @@ impl<'a> Arg<'a> { } else { help_str.replace_range(..font_offset, &font_str); } - (help_str, HelpType::Optional) + return (help_str, HelpType::Optional); } + + (String::new(), HelpType::Hidden) } fn possible_value_check(&self, value: &'a str) -> bool { -- Gitee From fe8f1673163b8afb5332a482dddbe53a944216d5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 14:44:21 +0800 Subject: [PATCH 0767/2187] vhost_user_fs: modify help message Modify some wrong field in help message for vhost_user_fs. Signed-off-by: liuxiangdong --- vhost_user_fs/src/cmdline.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 917369303..74ee7a79c 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -33,23 +33,23 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("source dir") .long("source") - .value_name("source directory in host") - .help("set source directory in host") + .value_name("shared_path") + .help("set source shared directory in host") .takes_value(true) .required(true), ) .arg( Arg::with_name("socket path") .long("socket-path") - .value_name("sock path which communicates with StratoVirt") - .help("sock path which communicates with StratoVirt") + .value_name("socket_path") + .help("vhost-user socket path which communicates with StratoVirt") .takes_value(true) .required(true), ) .arg( Arg::with_name("rlimit nofile") .long("rlimit-nofile") - .value_name("file resource limits for the process") + .value_name("num") .help("set file resource limits for the process") .takes_value(true), ) @@ -64,24 +64,24 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("seccomp") .long("seccomp") - .value_name("limit syscall(allow, kill, log, trap)") - .help("-seccomp kill") + .value_name("[allow | kill | log | trap]") + .help("limit syscall(allow, kill, log, trap) eg: -seccomp kill") .takes_value(true) .possible_values(vec!["allow", "kill", "log", "trap"]), ) .arg( Arg::with_name("sandbox") .long("sandbox") - .value_name("isolate the daemon process(chroot, namespace)") - .help("-sandbox namespace") + .value_name("[chroot | namespace]") + .help("isolate the daemon process(chroot, namespace). eg: -sandbox namespace") .takes_value(true) .possible_values(vec!["namespace", "chroot"]), ) .arg( Arg::with_name("modcaps") .opt_long("modcaps") - .value_name("add or delete modcaps") - .help("--modcaps=-LEASE,+KILL") + .value_name("capabilities_list") + .help("modify the list of capabilities. eg: --modcaps=-LEASE,+KILL") .takes_value(true), ) } -- Gitee From 84936f05c6b91eaac2a25bfe70c308bf1ef602e8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 19:46:42 +0800 Subject: [PATCH 0768/2187] scsi-bus: check xfer field in INQUIRY command Check xfer field in INQUIRY command. Signed-off-by: liuxiangdong --- virtio/src/scsi/bus.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 7afc92db6..234e75389 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -1067,7 +1067,11 @@ fn scsi_command_emulate_target_inquiry(lun: u16, cmd: &ScsiCommand) -> Result Date: Sat, 18 Feb 2023 21:31:05 +0800 Subject: [PATCH 0769/2187] Fix handler::tests::test_disinfect_process failure Below is the error handling code let fd_entries = read_dir(SELF_FD) for entry in fd_entries { if fd > 2 { syscall::close(fd); } } The read_dir function calls libc::opendir to open a file directory, Returns the ReadDir structure to represent an open file directory, and the ReadDir structure contains a descriptor of the open directory file. When ReadDir calls the drop function, it will call libc::closedir to close the file directory. Because the open directory file descriptor is closed during the for loop processing, libc::closedir will fail to close the directory file processing. Signed-off-by: Yan Wen --- ozone/src/handler.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index e5300f6da..aa792aef9 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -330,6 +330,7 @@ impl OzoneHandler { /// Disinfect the process before launching the ozone process. fn disinfect_process() -> Result<()> { let fd_entries = read_dir(SELF_FD).with_context(|| "Failed to open process fd proc")?; + let mut open_fds = vec![]; for entry in fd_entries { if entry.is_err() { break; @@ -339,9 +340,17 @@ fn disinfect_process() -> Result<()> { let fd = file_name.parse::().unwrap_or(0); if fd > 2 { - syscall::close(fd).with_context(|| format!("Failed to close fd: {}", fd))?; + open_fds.push(fd); } } + + for fd in open_fds { + let ret = unsafe { libc::fcntl(fd, libc::F_GETFD) }; + if ret != -1 { + syscall::close(fd).with_context(|| format!("Failed to close fd: {}", fd))? + } + } + Ok(()) } -- Gitee From ed14c3e6dd5abec6fd1e67519b6d0f72a6f556d8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Feb 2023 09:28:20 +0800 Subject: [PATCH 0770/2187] MST: add virtio-scsi mst Add virtio-scsi mst. Signed-off-by: liuxiangdong --- tests/mod_test/src/libdriver/virtio_block.rs | 56 +- tests/mod_test/src/utils.rs | 39 + tests/mod_test/tests/block_test.rs | 34 +- tests/mod_test/tests/fwcfg_test.rs | 6 +- tests/mod_test/tests/scsi_test.rs | 2360 ++++++++++++++++++ tests/mod_test/tests/virtio_test.rs | 6 +- 6 files changed, 2430 insertions(+), 71 deletions(-) create mode 100644 tests/mod_test/tests/scsi_test.rs diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index bce3cb33f..c5d1afa78 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -10,6 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::mem::size_of; +use std::rc::Rc; +use util::num_ops::round_up; + use super::machine::TestStdMachine; use super::malloc::GuestAllocator; use super::virtio::TestVirtQueue; @@ -19,15 +24,7 @@ use crate::libdriver::virtio::{ TestVringDescEntry, VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, }; use crate::libtest::{test_init, TestState}; -use crate::utils::get_rand_str; - -use std::cell::RefCell; -use std::fs; -use std::mem::size_of; -use std::path::Path; -use std::process::Command; -use std::rc::Rc; -use util::num_ops::round_up; +use crate::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; pub const VIRTIO_BLK_F_BARRIER: u64 = 0; pub const VIRTIO_BLK_F_SIZE_MAX: u64 = 1; @@ -59,7 +56,6 @@ pub const VIRTIO_BLK_S_IOERR: u8 = 1; /// Unsupport request. pub const VIRTIO_BLK_S_UNSUPP: u8 = 2; -pub const TEST_IMAGE_SIZE: u64 = 64 * 1024 * 1024; pub const TIMEOUT_US: u64 = 15 * 1000 * 1000; pub const DEFAULT_IO_REQS: u64 = 5; pub const REQ_ADDR_LEN: u32 = 16; @@ -203,42 +199,6 @@ pub fn virtio_blk_request( addr } -/// Create image file. -pub fn create_blk_img(size: u64, flag: u8) -> String { - let rng_name: String = get_rand_str(8); - - assert!(cfg!(target_os = "linux")); - - let mut image_path = format!("/tmp/stratovirt-{}.img", rng_name); - if flag == 1 { - image_path = format!("/var/log/stratovirt-{}.img", rng_name); - } - - let image_path_of = format!("of={}", &image_path); - let image_size_of = format!("bs={}", size); - let output = Command::new("dd") - .arg("if=/dev/zero") - .arg(&image_path_of) - .arg(&image_size_of) - .arg("count=1") - .output() - .expect("failed to create image"); - assert!(output.status.success()); - image_path -} - -/// Delete image file. -pub fn cleanup_blk_img(image_path: String) { - let img_path = Path::new(&image_path); - assert!(img_path.exists()); - - let metadata = fs::metadata(img_path).expect("can not get file metadata"); - let file_type = metadata.file_type(); - assert!(file_type.is_file()); - - fs::remove_file(img_path).expect("lack permissions to remove the file"); -} - pub fn add_blk_request( test_state: Rc>, alloc: Rc>, @@ -397,7 +357,7 @@ pub fn set_up() -> ( Rc>, Rc, ) { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=false")); let other_args = Rc::new(String::from("")); @@ -417,6 +377,6 @@ pub fn tear_down( blk.borrow_mut().destroy_device(alloc.clone(), vqs); test_state.borrow_mut().stop(); if !image_path.is_empty() { - cleanup_blk_img(image_path.to_string()); + cleanup_img(image_path.to_string()); } } diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index 8cef83076..783965a39 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -13,6 +13,8 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use std::fs; +use std::path::Path; +use std::process::Command; pub fn get_rand_str(size: usize) -> String { thread_rng() @@ -59,3 +61,40 @@ pub fn swap_u64(value: u64) -> u64 { let higher_u32 = swap_u32((value >> 32) as u32) as u64; lower_u32 << 32 | higher_u32 } + +pub const TEST_IMAGE_SIZE: u64 = 64 * 1024 * 1024; +/// Create image file. +pub fn create_img(size: u64, flag: u8) -> String { + let rng_name: String = get_rand_str(8); + + assert!(cfg!(target_os = "linux")); + + let mut image_path = format!("/tmp/stratovirt-{}.img", rng_name); + if flag == 1 { + image_path = format!("/var/log/stratovirt-{}.img", rng_name); + } + + let image_path_of = format!("of={}", &image_path); + let image_size_of = format!("bs={}", size); + let output = Command::new("dd") + .arg("if=/dev/zero") + .arg(&image_path_of) + .arg(&image_size_of) + .arg("count=1") + .output() + .expect("failed to create image"); + assert!(output.status.success()); + image_path +} + +/// Delete image file. +pub fn cleanup_img(image_path: String) { + let img_path = Path::new(&image_path); + assert!(img_path.exists()); + + let metadata = fs::metadata(img_path).expect("can not get file metadata"); + let file_type = metadata.file_type(); + assert!(file_type.is_file()); + + fs::remove_file(img_path).expect("lack permissions to remove the file"); +} diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index de875ad5d..ad250f5a0 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -16,18 +16,18 @@ use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::TestVringDescEntry; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; use mod_test::libdriver::virtio_block::{ - add_blk_request, create_blk, create_blk_img, set_up, tear_down, virtio_blk_defalut_feature, - virtio_blk_read, virtio_blk_request, virtio_blk_write, TestVirtBlkReq, DEFAULT_IO_REQS, - REQ_ADDR_LEN, REQ_DATA_LEN, REQ_STATUS_LEN, TEST_IMAGE_SIZE, TIMEOUT_US, VIRTIO_BLK_F_BARRIER, - VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, - VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_LIFETIME, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, - VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, - VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_ILLGEAL, VIRTIO_BLK_T_IN, - VIRTIO_BLK_T_OUT, + add_blk_request, create_blk, set_up, tear_down, virtio_blk_defalut_feature, virtio_blk_read, + virtio_blk_request, virtio_blk_write, TestVirtBlkReq, DEFAULT_IO_REQS, REQ_ADDR_LEN, + REQ_DATA_LEN, REQ_STATUS_LEN, TIMEOUT_US, VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_BLK_SIZE, + VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_GEOMETRY, + VIRTIO_BLK_F_LIFETIME, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SECURE_ERASE, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, + VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_FLUSH, + VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_ILLGEAL, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::TestState; +use mod_test::utils::{create_img, TEST_IMAGE_SIZE}; use std::cell::RefCell; use std::rc::Rc; @@ -194,7 +194,7 @@ fn blk_basic() { /// 1/2/4: success, 3: failed. #[test] fn blk_features_negotiate() { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); let device_args = Rc::new(String::from(",num-queues=4")); let drive_args = Rc::new(String::from(",direct=false,readonly=on")); let other_args = Rc::new(String::from("")); @@ -448,7 +448,7 @@ fn blk_feature_flush() { /// 1/2/3: success. #[test] fn blk_feature_mq() { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); let device_args = Rc::new(String::from(",num-queues=4")); let drive_args = Rc::new(String::from(",direct=false")); let other_args = Rc::new(String::from("")); @@ -559,7 +559,7 @@ fn blk_feature_mq() { /// 1/2/3: success. #[test] fn blk_all_features() { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1)); let device_args = Rc::new(String::from( ",multifunction=on,serial=111111,num-queues=4,bootindex=1,iothread=iothread1", )); @@ -615,7 +615,7 @@ fn blk_all_features() { #[test] fn blk_small_file_511b() { let size = 511; - let image_path = Rc::new(create_blk_img(size, 1)); + let image_path = Rc::new(create_img(size, 1)); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=false")); let other_args = Rc::new(String::from("")); @@ -715,7 +715,7 @@ fn blk_small_file_511b() { #[test] fn blk_serial() { let serial_num = String::from("11111111111111111111"); - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); let device_args = Rc::new(format!(",serial={}", serial_num)); let drive_args = Rc::new(String::from(",direct=false")); let other_args = Rc::new(String::from("")); @@ -772,7 +772,7 @@ fn blk_serial() { /// 1/2/3: success. #[test] fn blk_iops() { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=false,throttling.iops-total=1")); let other_args = Rc::new(String::from("")); @@ -835,7 +835,7 @@ fn blk_iops() { /// 1/2/3: success. #[test] fn blk_aio_native() { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1)); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=on,aio=native")); let other_args = Rc::new(String::from("")); @@ -884,7 +884,7 @@ fn blk_aio_native() { /// 1/2/3: success. #[test] fn blk_aio_io_uring() { - let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1)); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=on,aio=io_uring")); let other_args = Rc::new(String::from("")); diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index c591b6a56..07712f0fe 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -14,8 +14,8 @@ use byteorder::{ByteOrder, LittleEndian}; use devices::legacy::FwCfgEntryType; use mod_test::libdriver::fwcfg::{bios_args, FW_CFG_BASE}; use mod_test::libdriver::machine::TestStdMachine; -use mod_test::libdriver::virtio_block::{cleanup_blk_img, create_blk_img, TEST_IMAGE_SIZE}; use mod_test::libtest::test_init; +use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; use mod_test::utils::{swap_u16, swap_u32}; use std::cell::RefCell; @@ -187,7 +187,7 @@ fn test_boot_index() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); - let image_path = create_blk_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0); let dev_path = "/pci@ffffffffffffffff/scsi@1/disk@0,0\n\0".to_string(); @@ -223,7 +223,7 @@ fn test_boot_index() { test_state.borrow_mut().stop(); if !image_path.is_empty() { - cleanup_blk_img(image_path) + cleanup_img(image_path) } } diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs new file mode 100644 index 000000000..21f5e0eb6 --- /dev/null +++ b/tests/mod_test/tests/scsi_test.rs @@ -0,0 +1,2360 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cell::RefCell; +use std::mem::size_of; +use std::rc::Rc; +use std::slice::from_raw_parts; +use std::{thread, time}; + +use rand::Rng; +use util::byte_code::ByteCode; +use util::offset_of; + +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{ + TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VIRTIO_CONFIG_S_NEEDS_RESET, + VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, +}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libtest::{test_init, TestState}; +use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; + +const TEST_VIRTIO_SCSI_CDB_SIZE: usize = 32; +const TEST_VIRTIO_SCSI_SENSE_SIZE: usize = 96; + +/// According to Virtio Spec. +/// Max_channel should be 0. +/// Max_target should be less than or equal to 255. +const TEST_VIRTIO_SCSI_MAX_TARGET: u16 = 255; +/// Max_lun should be less than or equal to 16383 (2^14 - 1). +const TEST_VIRTIO_SCSI_MAX_LUN: u32 = 16383; + +const TIMEOUT_US: u64 = 10 * 1000 * 1000; +const DEFAULT_SCSI_DESC_ELEM: usize = 3; + +/// Default serial number of scsi device. +const DEFAULT_SCSI_SERIAL: &str = "123456"; + +const READ_10: u8 = 0x28; +const WRITE_10: u8 = 0x2a; +const TEST_UNIT_READY: u8 = 0x00; +const INQUIRY: u8 = 0x12; +const REPORT_LUNS: u8 = 0xa0; +const READ_CAPACITY_10: u8 = 0x25; +const MODE_SENSE: u8 = 0x1a; +const REQUEST_SENSE: u8 = 0x03; +const GET_CONFIGURATION: u8 = 0x46; +const READ_DISC_INFORMATION: u8 = 0x51; +const GET_EVENT_STATUS_NOTIFICATION: u8 = 0x4a; +const READ_TOC: u8 = 0x43; + +const VIRTIO_SCSI_S_OK: u8 = 0; +const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; +const VIRTIO_SCSI_S_FAILURE: u8 = 9; + +/// Mode page codes for mode sense/set. +const MODE_PAGE_CACHING: u8 = 0x08; +const MODE_PAGE_CAPABILITIES: u8 = 0x2a; +const MODE_PAGE_ALLS: u8 = 0x3f; + +/// Basic length of fixed format sense data. +const TEST_SCSI_SENSE_LEN: u32 = 18; + +const MODE_SENSE_LEN_DATA_LEN: u8 = 36; +const READ_DISC_INFORMATION_DATA_LEN: u8 = 34; +const GET_CONFIGURATION_DATA_LEN: u8 = 40; +const GET_EVENT_STATUS_NOTIFICATION_DATA_LEN: u8 = 8; +const REPORT_LUNS_DATA_LEN: u8 = 16; +const INQUIRY_TARGET_DATA_LEN: u8 = 36; +const READ_CAPACITY_10_DATA_LEN: u8 = 8; +const INQUIRY_DATA_LEN: u8 = 96; +const MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN: u8 = 32; +const MODE_SENSE_PAGE_ALL_DATA_LEN: u8 = 44; +const INQUIRY_SUPPORTED_VPD_PAGES_DATA_LEN: u8 = 10; +const INQUIRY_UNIT_SERIAL_NUMBER_DATA_LEN: u8 = 254; +const INQUIRY_DEVICE_IDENTIFICATION_DATA_LEN: u8 = 254; +const INQUIRY_BLOCK_LIMITS_DATA_LEN: u8 = 64; +const INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN: u8 = 64; +const INQUIRY_LOGICAL_BLOCK_PROVISIONING_DATA_LEN: u8 = 8; +const INQUIRY_REFERRALS_DATA_LEN: u8 = 64; +const READ_TOC_DATA_LEN: u8 = 20; +const READ_TOC_MSF_DATA_LEN: u8 = 12; +const READ_TOC_FORMAT_DATA_LEN: u8 = 12; + +struct VirtioScsiTest { + cntlr: Rc>, + scsi_devices: Vec, + state: Rc>, + alloc: Rc>, + queues: Vec>>, +} + +impl VirtioScsiTest { + /// Init test case. It will create a virtio-scsi controller with a scsi device using given args. + /// + /// # Arguments + /// + /// * `scsi_type` - The type of the only scsi device. Supports Harddisk and CD-ROM. + /// * `target` - The given target id of the only scsi device. + /// * `lun` - The given lun id of the only scsi device. + /// * `image_size`- The size of the backend image. + /// * `iothread` - If true, virtio-scsi controller will use iothread to process IO. + /// + /// # Return + /// + /// * `VirtioScsiTest` - Basic object for most tests, including the virtio scsi controller, + /// - the scsi device's config, the state of testcase, the memory management + /// - structure and virtqueues of this controller. + fn testcase_start_with_config( + scsi_type: ScsiDeviceType, + target: u8, + lun: u16, + image_size: u64, + iothread: bool, + ) -> VirtioScsiTest { + let image_path = Rc::new(create_img(image_size, 1)); + + let cntlrcfg = CntlrConfig { + id: 0, + use_iothread: iothread, + }; + + let readonly = if scsi_type == ScsiDeviceType::ScsiHd { + false + } else { + true + }; + let scsi_devices: Vec = vec![ScsiDeviceConfig { + cntlr_id: 0, + device_type: scsi_type, + image_path: image_path.clone(), + target, + lun, + read_only: readonly, + direct: false, + aio: TestAioType::AioOff, + serial: Some(DEFAULT_SCSI_SERIAL.to_string()), + }]; + + let (cntlr, state, alloc) = scsi_test_init(cntlrcfg, scsi_devices.clone()); + let features = virtio_scsi_defalut_feature(cntlr.clone()); + let queues = cntlr + .borrow_mut() + .init_device(state.clone(), alloc.clone(), features, 3); + + VirtioScsiTest { + cntlr, + scsi_devices, + state, + alloc, + queues, + } + } + + fn general_testcase_run(scsi_type: ScsiDeviceType, target: u8, lun: u16) -> VirtioScsiTest { + VirtioScsiTest::testcase_start_with_config(scsi_type, target, lun, TEST_IMAGE_SIZE, false) + } + + // String is not end with "/0" in Rust, so we should add data_in_len parameter to control length + // of the reading data. + fn virtio_scsi_do_command( + &mut self, + req: TestVirtioScsiCmdReq, + data_out: &Option, + resp: &mut TestVirtioScsiCmdResp, + data_in: &mut Vec, + data_in_len: u32, + ) { + assert!(data_in_len <= data_in.capacity() as u32); + + let virtqueue = &self.queues[2]; + let mut len = Some(0); + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_SCSI_DESC_ELEM); + + // Request Header. + let cmdreq_len = size_of::() as u64; + let req_addr = self + .alloc + .borrow_mut() + .alloc(cmdreq_len.try_into().unwrap()); + let req_bytes = req.as_bytes(); + self.state.borrow().memwrite(req_addr, req_bytes); + + data_entries.push(TestVringDescEntry { + data: req_addr, + len: cmdreq_len as u32, + write: false, + }); + + // Data out. + if let Some(data) = data_out { + let out_len = data.len() as u32; + let out_bytes = data.as_bytes().to_vec(); + let out_addr = self.alloc.borrow_mut().alloc(out_len.try_into().unwrap()); + self.state.borrow().memwrite(out_addr, out_bytes.as_slice()); + data_entries.push(TestVringDescEntry { + data: out_addr, + len: out_len, + write: false, + }); + } + + // Response. + let cmdresp_len = size_of::() as u64; + let resp_addr = self + .alloc + .borrow_mut() + .alloc((cmdresp_len + data_in_len as u64).try_into().unwrap()); + let resp_bytes = resp.as_bytes(); + self.state.borrow().memwrite(resp_addr, resp_bytes); + + // Data in. + data_entries.push(TestVringDescEntry { + data: resp_addr, + len: cmdresp_len as u32, + write: true, + }); + + if data_in_len > 0 { + data_entries.push(TestVringDescEntry { + data: resp_addr + cmdresp_len, + len: data_in_len, + write: true, + }); + } + + let free_head = virtqueue + .borrow_mut() + .add_chained(self.state.clone(), data_entries); + + self.cntlr + .borrow() + .kick_virtqueue(self.state.clone(), virtqueue.clone()); + self.cntlr.borrow().poll_used_elem( + self.state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut len, + true, + ); + + let resp_bytes_new = self.state.borrow().memread(resp_addr, cmdresp_len); + let slice = unsafe { + from_raw_parts( + resp_bytes_new.as_ptr() as *const TestVirtioScsiCmdResp, + size_of::(), + ) + }; + *resp = slice[0].clone(); + + if data_in_len > 0 { + data_in.append( + self.state + .borrow() + .memread(resp_addr + cmdresp_len, data_in_len as u64) + .as_mut(), + ); + } + } + + fn scsi_cdb_test(&mut self, cdb_test: CdbTest) -> Option> { + let scsi_req = TestVirtioScsiCmdReq::new(cdb_test.target, cdb_test.lun, cdb_test.cdb); + let mut scsi_resp = TestVirtioScsiCmdResp::default(); + let mut data_in = Vec::::with_capacity(cdb_test.data_in_length as usize); + + self.virtio_scsi_do_command( + scsi_req, + &cdb_test.data_out, + &mut scsi_resp, + &mut data_in, + cdb_test.data_in_length, + ); + + assert_eq!(scsi_resp.response, cdb_test.expect_response); + if let Some(result_vec) = cdb_test.expect_result_data { + assert_eq!(result_vec, data_in); + } + if let Some(sense_vec) = cdb_test.expect_sense { + assert_eq!(sense_vec, scsi_resp.sense); + } + + if cdb_test.data_in_length != 0 { + Some(data_in) + } else { + None + } + } + + fn testcase_tear_down(&mut self) { + self.cntlr + .borrow_mut() + .destroy_device(self.alloc.clone(), self.queues.clone()); + self.state.borrow_mut().stop(); + for device in self.scsi_devices.iter() { + cleanup_img(device.image_path.clone().to_string()); + } + } + + // Basic IO function test. + fn scsi_try_io(&mut self, target: u8, lun: u16, scsi_type: ScsiDeviceType) { + // Test: scsi command: WRITE_10. + // Write to LBA(logical block address) 0, transfer length 1 sector. + // Test Result: Check if scsi command WRITE_10 was handled successfully for scsi harddisk and + // was failure for scsi CD-ROM. + let mut write_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + write_cdb[0] = WRITE_10; + write_cdb[8] = 0x1; // 1 sector. + let data = vec![0x8; 512]; + let write_data = String::from_utf8(data).unwrap(); + let expect_response = if scsi_type == ScsiDeviceType::ScsiHd { + VIRTIO_SCSI_S_OK + } else { + VIRTIO_SCSI_S_FAILURE + }; + let cdb_test_args = CdbTest { + cdb: write_cdb, + target, + lun, + data_out: Some(write_data.clone()), + data_in_length: 0, + expect_response, + expect_result_data: None, + expect_sense: None, + }; + self.scsi_cdb_test(cdb_test_args); + + // Test: scsi command: READ_10. + // Read from LBA(logical block address) 0, transfer length 1. + // Test Result: Check if scsi command READ_10 was handled successfully. And check the read data is + // the right data which was sent in WRITE_10 test for scsi harddisk. + let mut read_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_cdb[0] = READ_10; + read_cdb[8] = 0x1; // 1 sector. + + let (data_in_length, expect_result_data) = if scsi_type == ScsiDeviceType::ScsiHd { + (write_data.len(), Some(write_data.into_bytes())) + } else { + (0, None) + }; + + let cdb_test_args = CdbTest { + cdb: read_cdb, + target, + lun, + data_out: None, + data_in_length: data_in_length as u32, // Read 1sector data. + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data, + expect_sense: None, + }; + self.scsi_cdb_test(cdb_test_args); + } +} + +struct CdbTest { + cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE], + target: u8, + lun: u16, + data_out: Option, + data_in_length: u32, + expect_response: u8, + expect_result_data: Option>, + expect_sense: Option<[u8; TEST_VIRTIO_SCSI_SENSE_SIZE]>, +} + +#[derive(Default)] +struct ScsiSense { + /// Sense key. + key: u8, + /// Additional sense code. + asc: u8, + /// Additional sense code qualifier. + ascq: u8, +} + +const SCSI_SENSE_INVALID_OPCODE: ScsiSense = ScsiSense { + key: 0x05, + asc: 0x20, + ascq: 0x00, +}; + +const SCSI_SENSE_INVALID_FIELD: ScsiSense = ScsiSense { + key: 0x05, + asc: 0x24, + ascq: 0x00, +}; + +const SCSI_SENSE_LUN_NOT_SUPPORTED: ScsiSense = ScsiSense { + key: 0x05, + asc: 0x25, + ascq: 0x00, +}; + +const SCSI_SENSE_NO_SENSE: ScsiSense = ScsiSense { + key: 0, + asc: 0, + ascq: 0, +}; + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug, Default)] +struct TestVirtioScsiCmdReq { + lun: [u8; 8], + tag: u64, + task_attr: u8, + prio: u8, + crn: u8, + cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE], +} + +impl TestVirtioScsiCmdReq { + fn new(target: u8, lun: u16, cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE]) -> Self { + let mut req = TestVirtioScsiCmdReq::default(); + let mut target_lun = [0_u8; 8]; + target_lun[0] = 1; + target_lun[1] = target; + target_lun[2] = (lun >> 8) as u8 & 0xff; + target_lun[3] = lun as u8 & 0xff; + + req.lun = target_lun; + req.cdb = cdb; + + req + } +} + +impl ByteCode for TestVirtioScsiCmdReq {} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +struct TestVirtioScsiCmdResp { + sense_len: u32, + resid: u32, + status_qualifier: u16, + status: u8, + response: u8, + sense: [u8; TEST_VIRTIO_SCSI_SENSE_SIZE], +} + +impl ByteCode for TestVirtioScsiCmdResp {} + +impl Default for TestVirtioScsiCmdResp { + fn default() -> Self { + TestVirtioScsiCmdResp { + sense_len: 0, + resid: 0, + status_qualifier: 0, + status: 0, + response: 0, + sense: [0; TEST_VIRTIO_SCSI_SENSE_SIZE], + } + } +} + +struct CntlrConfig { + // Controller id. + id: u8, + // If true, use iothread. + use_iothread: bool, +} + +#[derive(PartialEq, Clone, Debug)] +enum ScsiDeviceType { + // Scsi Harddisk. + ScsiHd = 0, + // Scsi CD-ROM/DVD-ROM. + ScsiCd = 1, +} + +impl std::fmt::Display for ScsiDeviceType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + ScsiDeviceType::ScsiHd => "scsi-hd", + ScsiDeviceType::ScsiCd => "scsi-cd", + } + ) + } +} + +#[derive(Clone, Debug, Copy)] +enum TestAioType { + AioOff = 0, + AioNative = 1, + AioIOUring = 2, +} + +impl std::fmt::Display for TestAioType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + TestAioType::AioIOUring => "io_uring", + TestAioType::AioNative => "native", + TestAioType::AioOff => "off", + } + ) + } +} + +#[derive(Clone, Debug)] +struct ScsiDeviceConfig { + cntlr_id: u8, + device_type: ScsiDeviceType, + image_path: Rc, + target: u8, + lun: u16, + read_only: bool, + direct: bool, + aio: TestAioType, + serial: Option, +} + +impl ScsiDeviceConfig { + fn cmdline(&self) -> String { + let serial_args = if let Some(serial) = &self.serial { + format!(",serial={}", serial) + } else { + "".to_string() + }; + + let device_args = format!( + "-device {},bus=scsi{}.0,scsi-id={},lun={},drive=drive-scsi0-0-{}-{},id=scsi0-0-{}-{}{}", + self.device_type, self.cntlr_id, self.target, self.lun, self.target, self.lun, self.target, + self.lun, serial_args, + ); + + let drive_args = format!( + "-drive file={},id=drive-scsi0-0-{}-{},direct={},readonly={},aio={}", + self.image_path, self.target, self.lun, self.direct, self.read_only, self.aio, + ); + + format!("{} {} ", device_args, drive_args) + } +} + +fn get_sense_bytes(sense: ScsiSense) -> [u8; TEST_VIRTIO_SCSI_SENSE_SIZE] { + let mut bytes = [0; TEST_VIRTIO_SCSI_SENSE_SIZE]; + bytes[0] = 0x70; // Fixed. Current errors. + bytes[2] = sense.key; + bytes[7] = 10; // Fixed. sense length: 10; + bytes[12] = sense.asc; + bytes[13] = sense.ascq; + + bytes +} + +pub fn virtio_scsi_defalut_feature(cntlr: Rc>) -> u64 { + let mut features = cntlr.borrow().get_device_features(); + features &= + !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX); + + features +} + +fn scsi_test_init( + controller: CntlrConfig, + scsidevice: Vec, +) -> ( + Rc>, + Rc>, + Rc>, +) { + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + + let pci_fn = 0; + let pci_slot = 0x4; + + let iothread_args = if controller.use_iothread { + let mut iothread_args_vec: Vec<&str> = "-object iothread,id=iothread1".split(' ').collect(); + args.append(&mut iothread_args_vec); + ",iothread=iothread1" + } else { + "" + }; + + let cntlr_args = format!( + "-device virtio-scsi-pci,id=scsi{},bus=pcie.0,addr={}.0{}", + controller.id, pci_slot, iothread_args + ); + let mut cntlr_str_vec: Vec<&str> = cntlr_args[..].split(' ').collect(); + args.append(&mut cntlr_str_vec); + + let mut scsi_device_args = String::new(); + + for device in scsidevice.iter() { + let disk_args = device.cmdline(); + scsi_device_args.push_str(&disk_args); + } + + let mut disk_args_vec: Vec<&str> = scsi_device_args.trim().split(' ').collect(); + args.append(&mut disk_args_vec); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let virtio_scsi = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + virtio_scsi.borrow_mut().init(pci_slot, pci_fn); + + (virtio_scsi, test_state, allocator) +} + +/// Virtio Scsi hard disk basic function test. target 31, lun 7. +/// TestStep: +/// 0. Init process. +/// 1. Traverse all possible targets from 0 to VIRTIO_SCSI_MAX_TARGET(255). +/// (using scsi command INQUIRY) (lun is always 0 in this traverse process). +/// 2. Get all luns info in target 31.(using scsi command REPORT_LUNS) +/// 3. Check if scsi device is OK.(using scsi command TEST_UNIT_READY) +/// 4. Get the capacity of the disk.(using scsi command READ_CAPACITY_10) +/// 5. Get the caching strategy of the disk.(using scsi command MODE_SENSE) +/// 6. Get some other information of the disk.(using scsi command INQUITY) +/// 7. Basic IO test. +/// 8. Test ends. Destroy device. +/// Expect: +/// 1. 1/2/3/4/5/6/7/8: success. +/// step 2. Response VIRTIO_SCSI_S_BAD_TARGET for INQUIRY command in target 0-30. +/// Response VIRTIO_SCSI_S_OK for INQUIRY command in target 31. +/// step 3. Reported lun is 7. +/// step 4. Response VIRTIO_SCSI_S_OK. +/// step 5. Get the right mode information of disk. +/// step 6. Get the right information of disk. +/// step 7. READ/WRITE is OK. +#[test] +fn scsi_hd_basic_test() { + let target = 31; + let lun = 7; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Test 1: scsi command: INQUIRY for scsi controller. + // Traverse all possible targets from 0 to VIRTIO_SCSI_MAX_TARGET(255). + // Note: stratovirt mst can only has 256 num free, so just traverse from 0 to 31. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[4] = INQUIRY_DATA_LEN; + for i in 0..32 { + // Test 1 Result: Only response 0 for target == 31. Otherwise response VIRTIO_SCSI_S_BAD_TARGET. + let expect_result = if i == target as u16 { + VIRTIO_SCSI_S_OK + } else { + VIRTIO_SCSI_S_BAD_TARGET + }; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target: i as u8, + lun: 0, + data_out: None, + data_in_length: INQUIRY_DATA_LEN as u32, + expect_response: expect_result, + expect_result_data: None, + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + } + + // Test 2: scsi command: REPORT_LUNS. + // Test 2 Result: Check if scsi command REPORT_LUNS was handled successfully. + // And check the read data is the right lun information (target 31, lun 7). + let mut report_luns_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + report_luns_cdb[0] = REPORT_LUNS; + report_luns_cdb[9] = REPORT_LUNS_DATA_LEN; + let mut expect_result_vec = vec![0_u8; REPORT_LUNS_DATA_LEN as usize]; + // REPORT_LUNS parameter data format. + // Expect result: Only 1 lun and lun id is 7. + // Bytes[0..3]: Lun list length (n-7). + expect_result_vec[3] = 0x8; + // Bytes[4..7]: Reserved. + // Bytes[8..15]: Lun[first]. + expect_result_vec[9] = lun as u8; + // Bytes[n-7..n]: Lun[last]. + + let cdb_test_args = CdbTest { + cdb: report_luns_cdb, + target, + lun, + data_out: None, + data_in_length: REPORT_LUNS_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3: scsi command: TESE_UNIT_READY. + // Test 3 Result: Check if scsi command TESE_UNIT_READY was handled successfully. + let mut test_unit_ready_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + test_unit_ready_cdb[0] = TEST_UNIT_READY; + let cdb_test_args = CdbTest { + cdb: test_unit_ready_cdb, + target, + lun, + data_out: None, + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 4: scsi command: READ_CAPACITY_10 + // Test 4 Result: Check if scsi command READ_CAPACITY_10 was handled successfully. + // And the returned capacity is right. + let mut read_capacity_10_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_capacity_10_cdb[0] = READ_CAPACITY_10; + let cdb_test_args = CdbTest { + cdb: read_capacity_10_cdb, + target, + lun, + data_out: None, + data_in_length: READ_CAPACITY_10_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + let data_in = vst.scsi_cdb_test(cdb_test_args); + + // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical block). + // Bytes[4-7]: Logical Block Length In Bytes. + // Total size = (last logical block address + 1) * block length. + assert_eq!( + (u32::from_be_bytes(data_in.as_ref().unwrap()[0..4].try_into().unwrap()) as u64 + 1) + * (u32::from_be_bytes(data_in.as_ref().unwrap()[4..8].try_into().unwrap()) as u64), + TEST_IMAGE_SIZE + ); + + // Test 5: scsi command: MODE_SENSE. + // Byte2: bits[0-5]: page code. bits[6-7]: page control. + // Test 5.1 page code = MODE_PAGE_CACHING. + // Test 5.1 Result: Check if scsi command MODE_SENSE was handled successfully. + // And the returned mode data is right. + let mut mode_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + mode_sense_cdb[0] = MODE_SENSE; + mode_sense_cdb[2] = MODE_PAGE_CACHING; + mode_sense_cdb[4] = MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN; + let mut expect_result_vec = vec![0; MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN as usize]; + // MODE_SENSE MODE_PAGE_CACHING(0x8) parameter data format. + // Bytes[0-1]: Mode Data Length (n-1). + expect_result_vec[0] = 0x1f; + // Bytes[2]: Device Specific Parameter. + // Bytes[3]: Block Descriptor Length. + expect_result_vec[3] = 0x8; + // Byte[4]: density code. + // Bytes[5-7]: number of blocks. + expect_result_vec[5] = 0x2; + // Byte[8]: Reserved. + // Byte[9-11]: Block Length. + expect_result_vec[10] = 0x2; + // Bytes[12]: page code. + expect_result_vec[12] = 0x8; + // Byte[13]: page length(0x12). + expect_result_vec[13] = 0x12; + // Byte[14]: IC/ABPF/CAP/DISC/SIZE/WCE/MF/RCD. + expect_result_vec[14] = 0x4; + // Bytes[15-31]: do not support now. + + let cdb_test_args = CdbTest { + cdb: mode_sense_cdb, + target, + lun, + data_out: None, + data_in_length: MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 5.2 page code = MODE_PAGE_ALLS. + // Test 5.2 Result: Check if scsi command MODE_SENSE was handled successfully. + // And the returned mode data is right. + let mut mode_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + mode_sense_cdb[0] = MODE_SENSE; + mode_sense_cdb[2] = MODE_PAGE_ALLS; + mode_sense_cdb[4] = MODE_SENSE_PAGE_ALL_DATA_LEN; + let mut expect_result_vec = vec![0; MODE_SENSE_PAGE_ALL_DATA_LEN as usize]; + // MODE_SENSE MODE_PAGE_ALLS parameter data format. + // Bytes[0-1]: Mode Data Length (n-1). + expect_result_vec[0] = MODE_SENSE_PAGE_ALL_DATA_LEN - 1; + // Bytes[2]: Device Specific Parameter. + // Bytes[3]: Block Descriptor Length. + expect_result_vec[3] = 0x8; + // Byte[4]: density code. + // Bytes[5-7]: number of blocks. + expect_result_vec[5] = 0x2; + // Byte[8]: Reserved. + // Bytes[9-11]: Block Length. + expect_result_vec[10] = 0x2; + // Bytes[12-23]: MODE_PAGE_R_W_ERROR(0x1) parameter data format. + // Byte[12]: page code. + expect_result_vec[12] = 0x1; + // Byte[13]: page length(0xa). + expect_result_vec[13] = 0xa; + // Byte[14]: AWRE/ARRE/TB/RC/EER/PER/DTE/DCR + expect_result_vec[14] = 0x80; + // Bytes[15-23]: do not support now. + // Bytes[24-43]: MODE_PAGE_CACHING(0x8) parameter data format. See test 5.1. + expect_result_vec[24] = 0x8; + expect_result_vec[25] = 0x12; + expect_result_vec[26] = 0x4; + let cdb_test_args = CdbTest { + cdb: mode_sense_cdb, + target, + lun, + data_out: None, + data_in_length: MODE_SENSE_PAGE_ALL_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 6: scsi command: INQUIRY for scsi device. + // Byte1 bit0: EVPD(enable vital product data). + // Byte2: page code for vital product data. + // Test 6.1 EVPD = 0: Inquiry basic information of this scsi device such as vendor + // and product information. + // Test 6.1 Result: Check if scsi command INQUIRY was handled successfully. And + // it has product/vendor information. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[4] = INQUIRY_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + let data_in = vst.scsi_cdb_test(cdb_test_args); + assert!(std::str::from_utf8(&data_in.unwrap()) + .unwrap() + .contains("STRA")); + + // Test 6.2 EVPD = 1, byte_code = 0x00: Inquiry supported VPD Pages of this scsi device. + // Test 6.2 Result: Check if scsi command INQUIRY was handled successfully. And the + // returned supported VPD pages is right. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[4] = INQUIRY_SUPPORTED_VPD_PAGES_DATA_LEN; + let expect_result_vec = vec![0, 0, 0, 0x6, 0, 0x80, 0x83, 0xb0, 0xb1, 0xb2]; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_SUPPORTED_VPD_PAGES_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 6.3 EVPD = 1, byte_code = 0x80: Inquiry unit serial number. + // Test 6.3 Result: Check if scsi command INQUIRY was handled successfully. And the + // returned serial number is DEFAULT_SCSI_SERIAL. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[2] = 0x80; + inquiry_cdb[4] = INQUIRY_UNIT_SERIAL_NUMBER_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_UNIT_SERIAL_NUMBER_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + let data_in = vst.scsi_cdb_test(cdb_test_args); + // Unit Serial Number starts from Byte 4. + assert!(std::str::from_utf8(&data_in.unwrap()[4..]) + .unwrap() + .contains(DEFAULT_SCSI_SERIAL)); + + // Test 6.4 EVPD = 1, byte_code = 0x83: Inquiry scsi device identification. + // Test 6.4 Result: Check if scsi command INQUIRY was handled successfully. + // Note: Stratovirt does not reply anything usefully for scsi device identification now. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[2] = 0x83; + inquiry_cdb[4] = INQUIRY_DEVICE_IDENTIFICATION_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_DEVICE_IDENTIFICATION_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + let data_in = vst.scsi_cdb_test(cdb_test_args); + assert!(data_in.as_ref().unwrap()[1] == 0x83); + + // Test 6.5 EVPD = 1, byte_code = 0xb0: Inquiry scsi block limits. + // Test 6.5 Result: Check if scsi command INQUIRY was handled successfully. + // Note: Stratovirt does not reply anything usefully for scsi block limits now. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[2] = 0xb0; + inquiry_cdb[4] = INQUIRY_BLOCK_LIMITS_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_BLOCK_LIMITS_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + let data_in = vst.scsi_cdb_test(cdb_test_args); + assert!(data_in.as_ref().unwrap()[1] == 0xb0); + assert!(data_in.unwrap()[3] == 64 - 4); + + // Test 6.6 EVPD = 1, byte_code = 0xb1: Inquiry block device characteristics. + // Test 6.6 Result: Check if scsi command INQUIRY was handled successfully. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[2] = 0xb1; + inquiry_cdb[4] = INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN; + // Byte0: bits[0-4]: Scsi device type. + // Byte1: Page code. + // Byte2: Reserved. + // Byte3: page length(length - 4). + let mut expect_result_vec = vec![0, 0xb1, 0, 0x3c]; + expect_result_vec.resize(INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN as usize, 0); + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 6.7 EVPD = 1, byte_code = 0xb2: Inquiry Logical Block Provisioning. + // Test 6.7 Result: Check if scsi command INQUIRY was handled successfully. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[2] = 0xb2; + inquiry_cdb[4] = INQUIRY_LOGICAL_BLOCK_PROVISIONING_DATA_LEN; + // Byte0: bits[0-4]: Scsi device type. + // Byte1: Page code. + // Byte2: Reserved. + // Byte3: page length(length - 4). + // Byte4: Threshold exponent. + // Byte5: LBPU(bit 7) / LBPWS / LBPWS10 / LBPRZ / ANC_SUP / DP. + // Byte6: Threshold percentage / Provisioning Type. + // Byte7: Threshold percentage. + let expect_result_vec = vec![0, 0xb2, 0, 0x4, 0, 0x60, 0x1, 0]; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_LOGICAL_BLOCK_PROVISIONING_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 6.7 EVPD = 1, byte_code = 0xb3: Referrals VPD page. + // Test 6.7 Result: Check if scsi command INQUIRY was failure. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; + inquiry_cdb[2] = 0xb3; + inquiry_cdb[4] = INQUIRY_REFERRALS_DATA_LEN; + let expect_sense = get_sense_bytes(SCSI_SENSE_INVALID_FIELD); + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun, + data_out: None, + data_in_length: INQUIRY_REFERRALS_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(expect_sense), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 7: basic io test. + vst.scsi_try_io(target, lun, ScsiDeviceType::ScsiHd); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi CD-ROM basic function test. target 0, lun 7. +/// TestStep: +/// 0. Init process. +/// 1. Get the mode page capabilities.(Using scsi command MODE_SENSE) +/// 2. Request if there exist errors.(Using scsi command REQUEST_SENSE) +/// 3. Read the table of Content.(Using scsi command READ_TOC) +/// 4. Read the disc information.(Using scsi command READ_DISC_INFORMATION) +/// 5. Get configuration of the CD/DVD.(Using scsi command GET_CONFIGURATION) +/// 6. Test CD/DVD's event status notification(Using scsi command GET_EVENT_STATUS_NOTIFICATION) +/// 7. Basic IO test. +/// 8. Test ends. Destroy device. +/// Note: +/// 1. Do not test TEST_UNIT_READY/REPORT_LUNS/READ_CAPACITY_10 again. See test scsi_hd_basic. +/// Expect: +/// 1. 1/2/3/4/5/6/7/8: success. +#[test] +fn scsi_cd_basic_test() { + let target = 0; + let lun = 7; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiCd, target, lun); + + // Test 1: scsi command: MODE_SENSE. + // Test 1.1 page code = MODE_PAGE_CAPABILITIES. + // Test 1.1 Result: Check if scsi command MODE_SENSE was handled successfully. + // And the returned mode data is right. + let mut mode_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + mode_sense_cdb[0] = MODE_SENSE; + mode_sense_cdb[2] = MODE_PAGE_CAPABILITIES; + mode_sense_cdb[4] = MODE_SENSE_LEN_DATA_LEN; + let mut expect_result_vec = vec![0; MODE_SENSE_LEN_DATA_LEN as usize]; + // MODE_SENSE MODE_PAGE_ALLS(0x2a) parameter data format. + // Byte[0]: Mode Data Length (n-1). + expect_result_vec[0] = MODE_SENSE_LEN_DATA_LEN - 1; + // Bytes[1-3]: 0. + // Byte[4]: PS/Reserved/Bits[0-5]: Page Code(0x2A). + expect_result_vec[4] = 0x2a; + // Byte[5]: Page Length(28). + expect_result_vec[5] = 28; + // Byte[6]: Reserved/Reserved/DVD-RAW Read(1)/DVD-R READ(1)/DVD-ROM READ(1)/ + // Method 2/CD-RW Read(1)/CD-R Read(1). + expect_result_vec[6] = 0x3b; + // Byte[7]: Reserved/Reserved/DVD-RAW WRITE/DVD-R WRITE/Reserved/Test Write/ + // CD-R/RW Write/CD-R Write. + // Byte[8]: BUF/Multi Session(1)/Mode 2 Form 2(1)/Mode 2 Form 1(1)/Digital Port 2(1)/ + // Digital Port 1(1)/Composite(1)/Audio Play(1). + expect_result_vec[8] = 0x7f; + // Byte[9]: Read Bar Code(1)/UPC(1)/ISRC(1)/C2 Pointers supported(1)/R-W Deinterleaved & corrected(1)/ + // R-W supported(1)/CD-DA Stream is Accurate(1)/CD-DA Cmds supported(1). + expect_result_vec[9] = 0xff; + // Byte[10]: Bits[5-7]: Loading Mechanism Type(1)/Reserved/Eject(1)/Prevent Jumper(1)/ + // Lock State/Lock(1). + expect_result_vec[10] = 0x2d; + // Byte[11]: Bits[6-7]: Reserved/R-W in Lead-in/Side Change Capable/SSS/Changer Supports Disc Present/ + // Separate Channel Mute/Separate volume levels + // Bytes[12-13]: Obsolete. + // Bytes[14-15]: Number of Volume Levels Supported. + expect_result_vec[15] = 0x2; + // Bytes[16-17]: Buffer Size Supported. + expect_result_vec[16] = 0x8; + // Bytes[18-25]: Do not support now. + let cdb_test_args = CdbTest { + cdb: mode_sense_cdb, + target, + lun, + data_out: None, + data_in_length: MODE_SENSE_LEN_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2: scsi command: REQUEST_SENSE. + // Test 2 Result: Check if scsi command REQUEST_SENSE was handled successfully. + // And the returned sense is SCSI_SENSE_NO_SENSE. + let mut request_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + request_sense_cdb[0] = REQUEST_SENSE; + request_sense_cdb[4] = TEST_SCSI_SENSE_LEN as u8; + let cdb_test_args = CdbTest { + cdb: request_sense_cdb, + target, + lun, + data_out: None, + data_in_length: TEST_SCSI_SENSE_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_NO_SENSE)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3: scsi command: READ_TOC. + // Test 3.1: + // Byte1 bit1: MSF = 0. Byte2 bits[0-3]: Format = 0; + // Test 3.1 Result: Check if scsi command READ_TOC was handled successfully. And check the read data + // is the same with the expect result. + let mut read_toc_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_toc_cdb[0] = READ_TOC; + read_toc_cdb[8] = READ_TOC_DATA_LEN; + let mut expect_result_vec = vec![0; READ_TOC_DATA_LEN as usize]; + // Bytes[0-1]: TOC Data Length. + expect_result_vec[1] = 0x12; + // Byte[2]: First Track Number. + expect_result_vec[2] = 1; + // Byte[3]: Last Track Number. + expect_result_vec[3] = 1; + // Byte[4]: Reserved. + // Byte[5]: Bits[5-7]: ADR, Bits[0-4]: CONTROL. + expect_result_vec[5] = 0x14; + // Byte[6]: Track Number. + expect_result_vec[6] = 0x1; + // Byte[7]: Reserved. + // Bytes[8-11]: Track Start Address(LBA form = 000000h, MSF form = 00:00:02:00). + // Byte[12]: Reserved. + // Byte[13]: Bits[5-7]: ADR, Bits[0-4]: CONTROL. + expect_result_vec[13] = 0x14; + // Byte[14]: Track Number. + expect_result_vec[14] = 0xaa; + // Byte[15]: Reserved. + // Bytes[16-19]: Track Start Address. + expect_result_vec[17] = 2; + let cdb_test_args = CdbTest { + cdb: read_toc_cdb, + target, + lun, + data_out: None, + data_in_length: READ_TOC_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3.2: scsi command: READ_TOC. + // Byte1 bit1: MSF = 1. + // Byte2 bits[0-3]: Format = 0; (Format(Select specific returned data format)(CD: 0,1,2)). + // Byte6: Track/Session Number. + // Test 3.2 Result: Check if scsi command READ_TOC was handled successfully. And check the read data + // is the same with the expect result. + let mut read_toc_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_toc_cdb[0] = READ_TOC; + read_toc_cdb[1] = 2; + read_toc_cdb[6] = 0xaa; + read_toc_cdb[8] = READ_TOC_MSF_DATA_LEN; + // Bytes[0-1]: TOC Data Length. + // Byte[2]: First Track Number. + // Byte[3]: Last Track Number. + // Byte[4]: Reserved. + // Byte[5]: Bits[5-7]: ADR, Bits[0-4]: CONTROL. + // Byte[6]: Track Number. + // Byte[7]: Reserved. + // Bytes[8-11]: Track Start Address(LBA form = 000000h, MSF form = 00:00:02:00). + let expect_result_vec = vec![0, 0xa, 1, 1, 0, 0x14, 0xaa, 0, 0, 0x1d, 9, 0x2f]; + let cdb_test_args = CdbTest { + cdb: read_toc_cdb, + target, + lun, + data_out: None, + data_in_length: READ_TOC_MSF_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3.3: scsi command: READ_TOC. + // Byte1 bit1: MSF = 0. + // Byte2 bits[0-3]: Format = 1; (Format(Select specific returned data format)(CD: 0,1,2)). + // Byte6: Track/Session Number. + // Test 3.3 Result: Check if scsi command READ_TOC was handled successfully. And check the read data + // is the same with the expect result. + let mut read_toc_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_toc_cdb[0] = READ_TOC; + read_toc_cdb[2] = 1; + read_toc_cdb[8] = READ_TOC_FORMAT_DATA_LEN; + let expect_result_vec = vec![0, 0xa, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]; + let cdb_test_args = CdbTest { + cdb: read_toc_cdb, + target, + lun, + data_out: None, + data_in_length: READ_TOC_FORMAT_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 4: scsi command: READ_DISC_INFORMATION. + // Test 4 Result: Check if scsi command READ_DISC_INFORMATION was handled successfully. And check the read data + // is the same with the expect result. + let mut read_disc_information_cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE] = + [0; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_disc_information_cdb[0] = READ_DISC_INFORMATION; + read_disc_information_cdb[8] = READ_DISC_INFORMATION_DATA_LEN; + // Bytes[0-1]: Disc Information Length(32). + // Byte2: Disc Information Data Type(000b) | Erasable(0) | State of last Session(01b) | Disc Status(11b). + // Byte3: Number of First Track on Disc. + // Byte4: Number of Sessions. + // Byte5: First Track Number in Last Session(Least Significant Byte). + // Byte6: Last Track Number in Last Session(Last Significant Byte). + // Byte7: DID_V | DBC_V | URU:Unrestricted Use Disc(1) | DAC_V | Reserved | Legacy | BG Format Status. + // Byte8: Disc Type(00h: CD-DA or CD-ROM Disc). + // Byte9: Number of sessions(Most Significant Byte). + // Byte10: First Trace Number in Last Session(Most Significant Byte). + // Byte11: Last Trace Number in Last Session(Most Significant Byte). + // Bytes12-15: Disc Identification. + // Bytes16-19: Last Session Lead-in Start Address. + // Bytes20-23: Last Possible Lead-Out Start Address. + // Bytes24-31: Disc Bar Code. + // Byte32: Disc Application Code. + // Byte33: Number of OPC Tables.(0) + let mut expect_result_vec = vec![0, 0x20, 0xe, 1, 1, 1, 1, 0x20]; + expect_result_vec.resize(READ_DISC_INFORMATION_DATA_LEN as usize, 0); + let cdb_test_args = CdbTest { + cdb: read_disc_information_cdb, + target, + lun, + data_out: None, + data_in_length: READ_DISC_INFORMATION_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 5: scsi command: GET_CONFIGURATION. + // The size of test img is TEST_IMAGE_SIZE(64M), so it is a CD-ROM. + // Test 5 Result: Check if scsi command GET_CONFIGURATION was handled successfully. And check the read data + // is the same with the expect result. + let mut get_configuration_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + get_configuration_cdb[0] = GET_CONFIGURATION; + get_configuration_cdb[8] = GET_CONFIGURATION_DATA_LEN; + let mut expect_result_vec = vec![0; GET_CONFIGURATION_DATA_LEN as usize]; + // Bytes[0-7]: Feature Header. + // Bytes[0-3]: Data Length(36 = 40 - 4). + expect_result_vec[3] = GET_CONFIGURATION_DATA_LEN - 4; + // Bytes[4-5]: Reserved. + // Bytes[6-7]: Current Profile. + expect_result_vec[7] = 8; + // Bytes[8-n]: Feature Descriptor(s): + // Bytes[8-19]: Feature 0: Profile List Feature: + // Bytes[8-9]: Feature code(0000h). + // Byte[10]: Bits[6-7]: Reserved. Bits[2-5]: Version. Bit 1: Persistent. Bit 0: Current(1). + expect_result_vec[10] = 3; + // Byte[11]: Additional Length. + expect_result_vec[11] = 8; + // Byte[12-19]: Profile Descriptors.(2 descriptors: CD and DVD) + // Byte[12-13]: Profile Number(CD). + expect_result_vec[13] = 8; + // Byte[14]: Bits[1-7]: Reserved. Bit 0: CurrentP. + expect_result_vec[14] = 1; + // Byte[15]: Reserved. + // Byte[16-17]: Profile Number(DVD). + expect_result_vec[17] = 0x10; + // Byte[18]: Bits[1-7]: Reserved. Bit 0: CurrentP. + // Byte[19]: Reserved. + // Bytes[20-31]: Feature 1: Core Feature: + // Bytes[20-21]: Feature Code(0001h). + expect_result_vec[21] = 0x1; + // Byte[22]: Bits[6-7]: Reserved. Bits[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + expect_result_vec[22] = 0xb; + // Byte[23]: Additional Length(8). + expect_result_vec[23] = 8; + // Bytes[24-27]: Physical Interface Standard. (Scsi Family: 00000001h) + expect_result_vec[27] = 1; + // Byte[28]: Bits[2-7]: Reserved. Bit 1: INQ2. Bit 0: DBE(1). + expect_result_vec[28] = 1; + // Bytes[29-31]: Reserved. + // Bytes[32-40]: Feature 2: Removable media feature: + // Bytes[32-33]: Feature Code(0003h). + expect_result_vec[33] = 3; + // Byte[34]: Bits[6-7]: Reserved. Bit[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + expect_result_vec[34] = 0xb; + // Byte[35]: Additional Length(4). + expect_result_vec[35] = 4; + // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). Bit 2: Pvnt Jmpr. + // Bit 1: DBML. Bit 0: Lock(1). + expect_result_vec[36] = 0x39; + // Byte[37-39]: Reserved. + let cdb_test_args = CdbTest { + cdb: get_configuration_cdb, + target, + lun, + data_out: None, + data_in_length: GET_CONFIGURATION_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 6: scsi command: GET_EVENT_STATUS_NOTIFICATION. + // Test 6 Result: Check if scsi command GET_EVENT_STATUS_NOTIFICATION was handled successfully. And check the read data + // is the same with the expect result. + let mut get_event_status_notification_cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE] = + [0; TEST_VIRTIO_SCSI_CDB_SIZE]; + get_event_status_notification_cdb[0] = GET_EVENT_STATUS_NOTIFICATION; + get_event_status_notification_cdb[1] = 1; + // Byte[4]: Notification Class Request. + get_event_status_notification_cdb[4] = 0x10; + get_event_status_notification_cdb[8] = GET_EVENT_STATUS_NOTIFICATION_DATA_LEN; + // Bytes[0-3]: Event Header. + // Bytes[4-n]: Event Descriptor. + // Bytes[0-1]: Event Descriptor Length. + // Byte2: Bit7: NEC(No Event Available). Bits[0-2]: Notification Class. + // NEC = 1: The Drive supports none of the requested notification classes. + // NEC = 0: At least one of the requested notification classes is supported. + // Byte3: Supported Event Class. + // Bytes[4-7]: Media Event Descriptor. + // Byte4: Bits[4-7]: reserved. Bits[0-3]: Event Code. + // Byte5: Media Status. Bits[2-7] reserved. Bit 1: Media Present. Bit 0: Door or Tray open. + // Byte6: Start Slot. + // Byte7: End Slot. + let expect_result_vec = vec![0, 6, 4, 0x10, 0, 2, 0, 0]; + let cdb_test_args = CdbTest { + cdb: get_event_status_notification_cdb, + target, + lun, + data_out: None, + data_in_length: GET_EVENT_STATUS_NOTIFICATION_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 7: basic io test. + vst.scsi_try_io(target, lun, ScsiDeviceType::ScsiCd); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi target cdb test. Test some commands no matter it's right or wrong. +/// Target cdb means that the target has at least one lun but the lun id of cdb will not +/// be found in target's all luns' id. +/// Using command REPORT_LUNS/INQUIRY/REQUEST_SENSE/TEST_UNIT_READY as target cdb are supported. +/// Others are not supported now. +/// TestStep: +/// 0. Init process. +/// 1. Test scsi command REPORT_LUNS. +/// 2. Test scsi command INQUIRY. +/// 3. Test scsi command REQUEST_SENSE. +/// 4. Test scsi command TESE_UNIT_READY. +/// 5. Test other scsi command, e.g. READ_CAPACITY_10. +/// 6. Destroy device. +/// Expect: +/// 0/1/2/3/4/5/6: success. +#[test] +fn scsi_target_cdb_test() { + let target = 15; + let lun = 5; + let req_lun = 3; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiCd, target, lun); + + // Test 1: scsi command: REPORT_LUNS. + // Test 1 Result: Check if scsi command REPORT_LUNS was handled successfully. + // And check the read data is the right lun information (target 15, lun 5). + let mut report_luns_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + report_luns_cdb[0] = REPORT_LUNS; + report_luns_cdb[9] = REPORT_LUNS_DATA_LEN; + let expect_result_vec = vec![0, 0, 0, 8, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0]; + let cdb_test_args = CdbTest { + cdb: report_luns_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: REPORT_LUNS_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2: scsi command: INQUIRY. + // Test 2.1: request lun id != 0. EVPD = 0. page code = 0. + // Test 2.1 Result: Check if scsi command INQUIRY was handled successfully. + // And check the read data is TYPE_NO_LUN. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[4] = INQUIRY_TARGET_DATA_LEN; + // Byte[0]: TYPE_NO_LUN(0x7f): Scsi target device is not capable of supporting a peripheral + // device connected to this logical unit. + let mut expect_result_vec = vec![0x7f]; + expect_result_vec.resize(INQUIRY_TARGET_DATA_LEN as usize, 0); + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2.2: request lun id == 0. EVPD = 0. page code = 0. + // Test 2.2 Result: Check if scsi command INQUIRY was handled successfully. + // And check the read data is the right target inquiry information. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[4] = INQUIRY_TARGET_DATA_LEN; + let mut expect_result_vec = vec![0; INQUIRY_TARGET_DATA_LEN as usize]; + // Byte0: Peripheral Qualifier/peripheral device type. + expect_result_vec[0] = 0x3f; + // Byte1:RMB. + // Byte2: VERSION. + expect_result_vec[2] = 0x5; + // Byte3: NORMACA/HISUP/Response Data Format. + expect_result_vec[3] = 0x12; + // Byte4: Additional length(length - 5). + expect_result_vec[4] = INQUIRY_TARGET_DATA_LEN - 5; + // Byte5: SCCS/ACC/TPGS/3PC/RESERVED/PROTECT. + // Byte6: ENCSERV/VS/MULTIP/ADDR16. + // Byte7: WBUS16/SYNC/CMDQUE/VS. + expect_result_vec[7] = 0x12; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun: 0, + data_out: None, + data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: Some(expect_result_vec), + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2.3: request lun id != 0. EVPD = 1. page code = 0. + // Test 2.3 Result: Check if scsi command INQUIRY was handled successfully. + // And check the read data is the right target inquiry information. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; // Byte1: bit0: EVPD (Enable Vital product bit). + inquiry_cdb[4] = INQUIRY_TARGET_DATA_LEN; + // Byte[3]: Page Length. Supported VPD page list in stratovirt only has 0x00 item. + let mut expect_result_vec = vec![0, 0, 0, 1]; + expect_result_vec.resize(INQUIRY_TARGET_DATA_LEN as usize, 0); + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2.4: request lun id == 0. EVPD = 1. page code = 0x80. + // Test 2.4 Result: Check if scsi command INQUIRY was handled successfully. + // And check the sense data is SCSI_SENSE_INVALID_FIELD. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; // Byte1: bit0: EVPD (Enable Vital product bit). + inquiry_cdb[2] = 0x80; + inquiry_cdb[4] = INQUIRY_TARGET_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun: 0, + data_out: None, + data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2.5: request lun id != 0. EVPD = 0. page code = 0x80. + // Test 2.5 Result: Check if scsi command INQUIRY was handled successfully. + // And check the sense data is SCSI_SENSE_INVALID_FIELD. + let mut inquiry_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + inquiry_cdb[0] = INQUIRY; + inquiry_cdb[1] = 0x1; // Byte1: bit0: EVPD (Enable Vital product bit). + inquiry_cdb[2] = 0x80; + inquiry_cdb[4] = INQUIRY_TARGET_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: inquiry_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3: scsi command: REQUEST_SENSE. + // Test 3.1 req_lun != 0; + // Test 3.1 Result: Check if scsi command REQUEST_SENSE was handled successfully. + // And check the sense data is SCSI_SENSE_LUN_NOT_SUPPORTED. + let mut request_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + request_sense_cdb[0] = REQUEST_SENSE; + request_sense_cdb[4] = TEST_SCSI_SENSE_LEN as u8; + let cdb_test_args = CdbTest { + cdb: request_sense_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: TEST_SCSI_SENSE_LEN, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_LUN_NOT_SUPPORTED)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3.2 req_lun == 0; + // Just return. + // Test 3.1 Result: Check if scsi command REQUEST_SENSE was handled successfully. + let mut request_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + request_sense_cdb[0] = REQUEST_SENSE; + request_sense_cdb[4] = TEST_SCSI_SENSE_LEN as u8; + let cdb_test_args = CdbTest { + cdb: request_sense_cdb, + target, + lun: 0, + data_out: None, + data_in_length: TEST_SCSI_SENSE_LEN, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 4: scsi command: TESE_UNIT_READY. + // Test 4 Result: Check if scsi command TESE_UNIT_READY was handled successfully. + let mut test_unit_ready_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + test_unit_ready_cdb[0] = TEST_UNIT_READY; + let cdb_test_args = CdbTest { + cdb: test_unit_ready_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: None, + }; + vst.scsi_cdb_test(cdb_test_args); + + // TEST 5: other scsi command, eg: READ_CAPACITY_10. + // Test 4 Result: Check if scsi command READ_CAPACITY_10 was handled successfully. + // And check the sense data is SCSI_SENSE_INVALID_OPCODE. + let mut read_capacity_10_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_capacity_10_cdb[0] = READ_CAPACITY_10; + let cdb_test_args = CdbTest { + cdb: read_capacity_10_cdb, + target, + lun: req_lun, + data_out: None, + data_in_length: READ_CAPACITY_10_DATA_LEN as u32, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), + }; + vst.scsi_cdb_test(cdb_test_args); + + vst.testcase_tear_down(); +} + +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioScsiConfig { + num_queues: u32, + seg_max: u32, + max_sectors: u32, + cmd_per_lun: u32, + event_info_size: u32, + sense_size: u32, + cdb_size: u32, + max_channel: u16, + max_target: u16, + max_lun: u32, +} + +/// Virtio Scsi pci device config Test. +/// Virtio spec requires that only cdb_size and sense size in virtio scsi pci device config +/// can be set from guest. +/// TestStep: +/// 1. Init process. +/// 2. For every parameter in VirtioScsiConfig, do check just like: +/// Read default value -> Set other value -> Read value again -> Check if value was setted successfully. +/// 3. Destroy device. +/// Note: +/// 1. sense size and cdb size can not be changed in stratovirt now. So, they are 0 now. +/// Expect: +/// 1/2/3: success. +/// 2: only sense_size and cdb_size are setted successfully. +#[test] +fn device_config_test() { + let target = 0x0; + let lun = 0x0; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + let mut num_queues = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, num_queues) as u64); + assert_eq!(num_queues, 1); + vst.cntlr + .borrow() + .config_writel(offset_of!(VirtioScsiConfig, num_queues) as u64, 5); + num_queues = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, num_queues) as u64); + assert_eq!(num_queues, 1); + + let mut seg_max = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, seg_max) as u64); + assert_eq!(seg_max, 254); + vst.cntlr + .borrow() + .config_writel(offset_of!(VirtioScsiConfig, seg_max) as u64, 126); + seg_max = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, seg_max) as u64); + assert_eq!(seg_max, 254); + + let mut max_sectors = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, max_sectors) as u64); + assert_eq!(max_sectors, 0xFFFF_u32); + vst.cntlr + .borrow() + .config_writel(offset_of!(VirtioScsiConfig, max_sectors) as u64, 0xFF_u32); + max_sectors = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, max_sectors) as u64); + assert_eq!(max_sectors, 0xFFFF_u32); + + let mut cmd_per_lun = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, cmd_per_lun) as u64); + assert_eq!(cmd_per_lun, 128); + vst.cntlr + .borrow() + .config_writel(offset_of!(VirtioScsiConfig, cmd_per_lun) as u64, 256); + cmd_per_lun = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, cmd_per_lun) as u64); + assert_eq!(cmd_per_lun, 128); + + let mut event_info_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, event_info_size) as u64); + assert_eq!(event_info_size, 0); + vst.cntlr + .borrow() + .config_writel(offset_of!(VirtioScsiConfig, event_info_size) as u64, 32); + event_info_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, event_info_size) as u64); + assert_eq!(event_info_size, 0); + + let mut sense_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, sense_size) as u64); + assert_eq!(sense_size, 0); + vst.cntlr.borrow().config_writel( + offset_of!(VirtioScsiConfig, sense_size) as u64, + TEST_VIRTIO_SCSI_SENSE_SIZE as u32 + 2, + ); + sense_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, sense_size) as u64); + assert_eq!(sense_size, 0); + vst.cntlr.borrow().config_writel( + offset_of!(VirtioScsiConfig, sense_size) as u64, + TEST_VIRTIO_SCSI_SENSE_SIZE as u32, + ); + sense_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, sense_size) as u64); + assert_eq!(sense_size, 0); + + let mut cdb_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, cdb_size) as u64); + assert_eq!(cdb_size, 0); + vst.cntlr.borrow().config_writel( + offset_of!(VirtioScsiConfig, cdb_size) as u64, + TEST_VIRTIO_SCSI_CDB_SIZE as u32 + 3, + ); + cdb_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, cdb_size) as u64); + assert_eq!(cdb_size, 0); + vst.cntlr.borrow().config_writel( + offset_of!(VirtioScsiConfig, cdb_size) as u64, + TEST_VIRTIO_SCSI_CDB_SIZE as u32, + ); + cdb_size = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, cdb_size) as u64); + assert_eq!(cdb_size, 0); + + let mut max_channel = vst + .cntlr + .borrow() + .config_readw(offset_of!(VirtioScsiConfig, max_channel) as u64); + assert_eq!(max_channel, 0); + vst.cntlr + .borrow() + .config_writew(offset_of!(VirtioScsiConfig, max_channel) as u64, 126); + max_channel = vst + .cntlr + .borrow() + .config_readw(offset_of!(VirtioScsiConfig, max_channel) as u64); + assert_eq!(max_channel, 0); + + let mut max_target = vst + .cntlr + .borrow() + .config_readw(offset_of!(VirtioScsiConfig, max_target) as u64); + assert_eq!(max_target, TEST_VIRTIO_SCSI_MAX_TARGET); + vst.cntlr + .borrow() + .config_writew(offset_of!(VirtioScsiConfig, max_target) as u64, 126); + max_target = vst + .cntlr + .borrow() + .config_readw(offset_of!(VirtioScsiConfig, max_target) as u64); + assert_eq!(max_target, TEST_VIRTIO_SCSI_MAX_TARGET); + + let mut max_lun = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, max_lun) as u64); + assert_eq!(max_lun, TEST_VIRTIO_SCSI_MAX_LUN); + vst.cntlr + .borrow() + .config_writel(offset_of!(VirtioScsiConfig, max_lun) as u64, 1024); + max_lun = vst + .cntlr + .borrow() + .config_readl(offset_of!(VirtioScsiConfig, max_lun) as u64); + assert_eq!(max_lun, TEST_VIRTIO_SCSI_MAX_LUN); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi I/O processing in iothread test. +/// TestStep: +/// 1. Config iothread in scsi controller with a scsi harddisk. Init process. +/// 2. Write Data / Read Data. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: The data read out from the disk is exactly the data written down. +#[test] +fn iothread_test() { + let target = 0x1; + let lun = 0x2; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Test: basic io test. + vst.scsi_try_io(target, lun, ScsiDeviceType::ScsiHd); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi I/O processing in different AIO model. +/// TestStep: +/// 1. Config different AIO model in scsi disk. Init process. +/// 2. Write Data / Read Data. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: The data read out from the disk is exactly the data written down. +#[test] +fn aio_model_test() { + let cntlrcfg = CntlrConfig { + id: 0, + use_iothread: false, + }; + let target = 0x1; + let mut lun = 0x2; + let mut device_vec: Vec = Vec::new(); + // Scsi Disk 1. AIO io_uring. Direct false. + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: false, + aio: TestAioType::AioIOUring, + serial: None, + }); + + // Scsi Disk 2. AIO io_uring. Direct true. + lun += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: true, + aio: TestAioType::AioIOUring, + serial: None, + }); + + // Scsi Disk 3. AIO OFF. Direct true. This is not allowed. + // Stratovirt will report "low performance expect when use sync io with direct on" + + //Scsi Disk 4. AIO OFF. Direct false. + lun += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: false, + aio: TestAioType::AioOff, + serial: None, + }); + // Scsi Disk 5. AIO native. Direct false. This is not allowed. + // Stratovirt will report "native aio type should be used with direct on" + + // Scsi Disk 6. AIO native. Direct true. + lun += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: true, + aio: TestAioType::AioNative, + serial: None, + }); + + let (cntlr, state, alloc) = scsi_test_init(cntlrcfg, device_vec.clone()); + let features = virtio_scsi_defalut_feature(cntlr.clone()); + let queues = cntlr + .borrow_mut() + .init_device(state.clone(), alloc.clone(), features, 3); + + let mut vst = VirtioScsiTest { + cntlr, + scsi_devices: device_vec, + state, + alloc, + queues, + }; + + for device in vst.scsi_devices.clone().iter() { + // Test: basic io test. + vst.scsi_try_io(device.target, device.lun, ScsiDeviceType::ScsiHd); + } + + vst.testcase_tear_down(); +} + +/// Virtio Scsi random CDB test. +/// TestStep: +/// 1. Init process. +/// 2. Generate random u8 vector as CDB and send. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Stratovirt will not crash. +#[test] +fn random_cdb_test() { + let target = 0xff; + let lun = 0xff; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Test: Generate random u8 array as cdb. + let mut randcdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + let mut rng = rand::thread_rng(); + for i in 0..TEST_VIRTIO_SCSI_CDB_SIZE { + randcdb[i] = rng.gen(); + } + + let scsi_req = TestVirtioScsiCmdReq::new(target, lun, randcdb); + let mut scsi_resp = TestVirtioScsiCmdResp::default(); + let mut data_in = Vec::::with_capacity(512); + vst.virtio_scsi_do_command(scsi_req, &None, &mut scsi_resp, &mut data_in, 0); + + // Test: the scsi device works normally. + vst.scsi_try_io(target, lun, ScsiDeviceType::ScsiHd); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi wrong size virtioscsirequest test. +/// TestStep: +/// 1. Config virtio scsi controller with a scsi harddisk. Init process. +/// 2. Send virtioscsirequest which is less than expect length. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Report VIRTIO ERROR. Stratovirt will not crash. +#[test] +fn wrong_virtioscsirequest_test() { + let target = 0xff; + let lun = 0xff; + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_SCSI_DESC_ELEM); + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Error request. + let error_req_size = size_of::() as u64 - 1; + let req = vec![1; error_req_size as usize]; + let req_addr = vst + .alloc + .borrow_mut() + .alloc(error_req_size.try_into().unwrap()); + vst.state.borrow().memwrite(req_addr, &req); + + data_entries.push(TestVringDescEntry { + data: req_addr, + len: error_req_size as u32, + write: false, + }); + + // Response. + let cmdresp_len = size_of::() as u64; + let resp = TestVirtioScsiCmdResp::default(); + let resp_addr = vst + .alloc + .borrow_mut() + .alloc(cmdresp_len.try_into().unwrap()); + let resp_bytes = resp.as_bytes(); + vst.state.borrow().memwrite(resp_addr, resp_bytes); + + data_entries.push(TestVringDescEntry { + data: resp_addr, + len: cmdresp_len as u32, + write: false, + }); + + vst.queues[2] + .borrow_mut() + .add_chained(vst.state.clone(), data_entries); + + vst.cntlr + .borrow() + .kick_virtqueue(vst.state.clone(), vst.queues[2].clone()); + + thread::sleep(time::Duration::from_secs(1)); + assert!(vst.cntlr.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET != 0); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi wrong size virtioscsiresponse test. +/// TestStep: +/// 1. Config virtio scsi controller with a scsi harddisk. Init process. +/// 2. Send virtioscsiresponse which is less than expect length. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Report VIRTIO ERROR. Stratovirt will not crash. +#[test] +fn wrong_size_virtioscsiresponse_test() { + let target = 0xff; + let lun = 0xff; + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_SCSI_DESC_ELEM); + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Request Header. + let req_len = size_of::() as u64; + let req = vec![1; req_len as usize]; + let req_addr = vst.alloc.borrow_mut().alloc(req_len.try_into().unwrap()); + vst.state.borrow().memwrite(req_addr, &req); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: req_len as u32, + write: false, + }); + + // Response. + let err_resp_len = size_of::() as u64 - 1; + let resp = vec![1; err_resp_len as usize]; + let resp_addr = vst + .alloc + .borrow_mut() + .alloc(err_resp_len.try_into().unwrap()); + vst.state.borrow().memwrite(resp_addr, &resp); + data_entries.push(TestVringDescEntry { + data: resp_addr, + len: err_resp_len as u32, + write: true, + }); + + vst.queues[2] + .borrow_mut() + .add_chained(vst.state.clone(), data_entries); + vst.cntlr + .borrow() + .kick_virtqueue(vst.state.clone(), vst.queues[2].clone()); + + thread::sleep(time::Duration::from_secs(1)); + assert!(vst.cntlr.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET != 0); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi missing virtioscsirequest test. +/// TestStep: +/// 1. Config virtio scsi controller with a scsi harddisk. Init process. +/// 2. Do not send virtioscsirequest in virtqueue. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Report VIRTIO ERROR. Stratovirt will not crash. +#[test] +fn missing_virtioscsirequest_test() { + let target = 0xff; + let lun = 0xff; + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_SCSI_DESC_ELEM); + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Response. + let resp_len = size_of::() as u64; + let resp = vec![1; resp_len as usize]; + let resp_addr = vst.alloc.borrow_mut().alloc(resp_len.try_into().unwrap()); + vst.state.borrow().memwrite(resp_addr, &resp); + + data_entries.push(TestVringDescEntry { + data: resp_addr, + len: resp_len as u32, + write: true, + }); + + vst.queues[2] + .borrow_mut() + .add_chained(vst.state.clone(), data_entries); + vst.cntlr + .borrow() + .kick_virtqueue(vst.state.clone(), vst.queues[2].clone()); + + thread::sleep(time::Duration::from_secs(1)); + assert!(vst.cntlr.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET != 0); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi missing virtioscsiresponse test. +/// TestStep: +/// 1. Config virtio scsi controller with a scsi harddisk. Init process. +/// 2. Do not send virtioscsiresponse in virtqueue. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Report VIRTIO ERROR. Stratovirt will not crash. +#[test] +fn missing_virtioscsiresponse_test() { + let target = 0xff; + let lun = 0xff; + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_SCSI_DESC_ELEM); + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Request Header. + let req_len = size_of::() as u64; + let req = vec![1; req_len as usize]; + let req_addr = vst.alloc.borrow_mut().alloc(req_len.try_into().unwrap()); + vst.state.borrow().memwrite(req_addr, &req); + + data_entries.push(TestVringDescEntry { + data: req_addr, + len: req_len as u32, + write: false, + }); + + vst.queues[2] + .borrow_mut() + .add_chained(vst.state.clone(), data_entries); + vst.cntlr + .borrow() + .kick_virtqueue(vst.state.clone(), vst.queues[2].clone()); + + thread::sleep(time::Duration::from_secs(1)); + assert!(vst.cntlr.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET != 0); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi wrong lun in virtioscsiresponse test. +/// #[repr(C, packed)] +/// struct TestVirtioScsiCmdReq { +/// lun: [u8; 8], +/// tag: u64, +/// task_attr: u8, +/// prio: u8, +/// crn: u8, +/// cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE], +/// } +/// TestStep: +/// 1. Config virtio scsi controller with a scsi harddisk. Init process. +/// 2. Send virtioscsirequest which has wrong lun parameter. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Return no such target/lun. Stratovirt will not crash. +#[test] +fn wrong_lun_in_virtioscsirequest_test() { + let target = 0xff; + let lun = 0xff; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + let mut test_unit_ready_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + test_unit_ready_cdb[0] = TEST_UNIT_READY; + let err_lun_scsi_req = TestVirtioScsiCmdReq { + lun: [0; 8], // Error lun. + tag: 0, + task_attr: 0, + prio: 0, + crn: 0, + cdb: test_unit_ready_cdb, + }; + let mut scsi_resp = TestVirtioScsiCmdResp::default(); + vst.virtio_scsi_do_command(err_lun_scsi_req, &None, &mut scsi_resp, &mut Vec::new(), 0); + + assert!(scsi_resp.response == VIRTIO_SCSI_S_BAD_TARGET); + + vst.testcase_tear_down(); +} + +/// Send scsi-cd command to scsi-hd. +/// TestStep: +/// 1. Config virtio scsi controller with a scsi harddisk. Init process. +/// 2. Send scsi command which is used for scsi CD-ROM. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +/// 2: Return not supported. Stratovirt will not crash. +#[test] +fn send_cd_command_to_hd_test() { + let target = 3; + let lun = 0; + let mut vst = VirtioScsiTest::general_testcase_run(ScsiDeviceType::ScsiHd, target, lun); + + // Test 1: Scsi Command: MODE_SENSE + let mut mode_sense_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + mode_sense_cdb[0] = MODE_SENSE; + mode_sense_cdb[2] = MODE_PAGE_CAPABILITIES; + mode_sense_cdb[4] = MODE_SENSE_LEN_DATA_LEN; + + let cdb_test_args = CdbTest { + cdb: mode_sense_cdb, + target, + lun, + data_out: None, + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 2: scsi command: READ_DISC_INFORMATION. + // Test 2 Result: Check if scsi command READ_DISC_INFORMATION was failure. + let mut read_disc_information_cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE] = + [0; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_disc_information_cdb[0] = READ_DISC_INFORMATION; + read_disc_information_cdb[8] = READ_DISC_INFORMATION_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: read_disc_information_cdb, + target, + lun, + data_out: None, + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 3: scsi command: GET_CONFIGURATION. + // Test 3 Result: Check if scsi command GET_CONFIGURATION was failure. + let mut get_configuration_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + get_configuration_cdb[0] = GET_CONFIGURATION; + get_configuration_cdb[8] = GET_CONFIGURATION_DATA_LEN; + let cdb_test_args = CdbTest { + cdb: get_configuration_cdb, + target, + lun, + data_out: None, + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test 4: scsi command: GET_EVENT_STATUS_NOTIFICATION. + // Test 4 Result: Check if scsi command GET_EVENT_STATUS_NOTIFICATION was failure. + let mut get_event_status_notification_cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE] = + [0; TEST_VIRTIO_SCSI_CDB_SIZE]; + get_event_status_notification_cdb[0] = GET_EVENT_STATUS_NOTIFICATION; + get_event_status_notification_cdb[1] = 1; + get_event_status_notification_cdb[4] = 0x10; + get_event_status_notification_cdb[8] = GET_EVENT_STATUS_NOTIFICATION_DATA_LEN; + + let cdb_test_args = CdbTest { + cdb: get_configuration_cdb, + target, + lun, + data_out: None, + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), + }; + vst.scsi_cdb_test(cdb_test_args); + + vst.testcase_tear_down(); +} + +/// Virtio Scsi Wrong io request test. +/// TestStep: +/// 1. Init process. +/// 2. Send READ_10/WRITE_10 CDB. +/// 2.1 READ_10/WRITE_10 transfer length is larger than disk size. +/// 2.2 READ_10/WRITE_10 read/write offset is larget than disk size. +/// 3. Wait for return value. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +/// 2: Stratovirt will not crash. +/// 3. Return error. +#[test] +fn wrong_io_test() { + let target = 0xff; + let lun = 0xff; + let size = 1 * 1024; // Disk size: 1K. + + let mut vst = + VirtioScsiTest::testcase_start_with_config(ScsiDeviceType::ScsiHd, target, lun, size, true); + + // Test1: scsi command: WRITE_10. + // Write to LBA(logical block address) 0, transfer length 2KB and disk is 1KB size. + // Test Result: Check if scsi command WRITE_10 was failure. + let mut write_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + write_cdb[0] = WRITE_10; + write_cdb[8] = (2048 / 512) as u8; // 2KB data. + let data = vec![0x5; 2048]; // 2KB data. + let write_data = String::from_utf8(data).unwrap(); + let cdb_test_args = CdbTest { + cdb: write_cdb, + target, + lun, + data_out: Some(write_data), + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test2: scsi command: READ_10. + // Read from LBA(logical block address) 0, transfer length 2KB and disk is 1KB size. + // Test Result: Check if scsi command READ_10 was failure. + let mut read_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_cdb[0] = READ_10; + read_cdb[8] = (2048 / 512) as u8; // 2KB data. + let cdb_test_args = CdbTest { + cdb: read_cdb, + target, + lun, + data_out: None, + data_in_length: 2048, // Read 2K data. + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test3: scsi command: WRITE_10. + // Write to LBA(logical block address) 2K, transfer length 1 secotr and disk is 1KB size. + // Test Result: Check if scsi command WRITE_10 was failure. + let mut write_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + write_cdb[0] = WRITE_10; + write_cdb[5] = ((2 * 1024) & 0xff) as u8; + write_cdb[4] = ((2 * 1024) >> 8 & 0xff) as u8; + write_cdb[8] = 1; // 1 sector data. + let data = vec![0x5; 512]; // 1 sector data. + let write_data = String::from_utf8(data).unwrap(); + let cdb_test_args = CdbTest { + cdb: write_cdb, + target, + lun, + data_out: Some(write_data), + data_in_length: 0, + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), + }; + vst.scsi_cdb_test(cdb_test_args); + + // Test4: scsi command: READ_10. + // Read from LBA(logical block address) 2K, transfer length 1 sector and disk is 1KB size. + // Test Result: Check if scsi command READ_10 was failure. + let mut read_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; + read_cdb[0] = READ_10; + read_cdb[5] = ((2 * 1024) & 0xff) as u8; + read_cdb[4] = ((2 * 1024) >> 8 & 0xff) as u8; + read_cdb[8] = 1; // 1 sector data. + let cdb_test_args = CdbTest { + cdb: read_cdb, + target, + lun, + data_out: None, + data_in_length: 512, // 1 sector data. + expect_response: VIRTIO_SCSI_S_OK, + expect_result_data: None, + expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), + }; + vst.scsi_cdb_test(cdb_test_args); + + vst.testcase_tear_down(); +} diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 13fcef9db..e45773705 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -26,12 +26,12 @@ use mod_test::libdriver::virtio::{ }; use mod_test::libdriver::virtio_block::{ add_blk_request, set_up, tear_down, virtio_blk_read, virtio_blk_request, virtio_blk_write, - TestVirtBlkReq, DEFAULT_IO_REQS, REQ_ADDR_LEN, REQ_DATA_LEN, REQ_STATUS_OFFSET, - TEST_IMAGE_SIZE, TIMEOUT_US, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_IN, - VIRTIO_BLK_T_OUT, + TestVirtBlkReq, DEFAULT_IO_REQS, REQ_ADDR_LEN, REQ_DATA_LEN, REQ_STATUS_OFFSET, TIMEOUT_US, + VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, }; use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; use mod_test::libtest::TestState; +use mod_test::utils::TEST_IMAGE_SIZE; fn add_request( test_state: Rc>, -- Gitee From 307faa0c615fcadc1ef3d92c1f46cc16af118a76 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Feb 2023 22:58:28 +0800 Subject: [PATCH 0771/2187] scsi-test: check env aio support before testing Check whether env supports aio before testing. Signed-off-by: liuxiangdong --- machine/src/standard_vm/mod.rs | 12 +++-- tests/mod_test/tests/scsi_test.rs | 90 ++++++++++++++++--------------- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6e7fcbeba..33cf55947 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1482,11 +1482,13 @@ impl DeviceInterface for StdMachine { let mut fd = None; if args.region_type.eq("rom_device_region") || args.region_type.eq("ram_device_region") { if let Some(file_name) = args.device_fd_path { - fd = Some(std::fs::OpenOptions::new() - .read(true) - .write(true) - .open(&file_name) - .unwrap()); + fd = Some( + std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&file_name) + .unwrap(), + ); } } diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 21f5e0eb6..cf2cf12d3 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -17,6 +17,7 @@ use std::slice::from_raw_parts; use std::{thread, time}; use rand::Rng; +use util::aio::{aio_probe, AioEngine}; use util::byte_code::ByteCode; use util::offset_of; @@ -354,7 +355,7 @@ impl VirtioScsiTest { target, lun, data_out: None, - data_in_length: data_in_length as u32, // Read 1sector data. + data_in_length: data_in_length as u32, // Read 1 sector data. expect_response: VIRTIO_SCSI_S_OK, expect_result_data, expect_sense: None, @@ -1812,34 +1813,37 @@ fn aio_model_test() { let target = 0x1; let mut lun = 0x2; let mut device_vec: Vec = Vec::new(); - // Scsi Disk 1. AIO io_uring. Direct false. - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); - device_vec.push(ScsiDeviceConfig { - cntlr_id: 0, - device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), - target: target, - lun: lun, - read_only: false, - direct: false, - aio: TestAioType::AioIOUring, - serial: None, - }); - // Scsi Disk 2. AIO io_uring. Direct true. - lun += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); - device_vec.push(ScsiDeviceConfig { - cntlr_id: 0, - device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), - target: target, - lun: lun, - read_only: false, - direct: true, - aio: TestAioType::AioIOUring, - serial: None, - }); + if aio_probe(AioEngine::IoUring).is_ok() { + // Scsi Disk 1. AIO io_uring. Direct false. + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: false, + aio: TestAioType::AioIOUring, + serial: None, + }); + + // Scsi Disk 2. AIO io_uring. Direct true. + lun += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: true, + aio: TestAioType::AioIOUring, + serial: None, + }); + } // Scsi Disk 3. AIO OFF. Direct true. This is not allowed. // Stratovirt will report "low performance expect when use sync io with direct on" @@ -1861,20 +1865,22 @@ fn aio_model_test() { // Scsi Disk 5. AIO native. Direct false. This is not allowed. // Stratovirt will report "native aio type should be used with direct on" - // Scsi Disk 6. AIO native. Direct true. - lun += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); - device_vec.push(ScsiDeviceConfig { - cntlr_id: 0, - device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), - target: target, - lun: lun, - read_only: false, - direct: true, - aio: TestAioType::AioNative, - serial: None, - }); + if aio_probe(AioEngine::Native).is_ok() { + // Scsi Disk 6. AIO native. Direct true. + lun += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + device_vec.push(ScsiDeviceConfig { + cntlr_id: 0, + device_type: ScsiDeviceType::ScsiHd, + image_path: image_path.clone(), + target: target, + lun: lun, + read_only: false, + direct: true, + aio: TestAioType::AioNative, + serial: None, + }); + } let (cntlr, state, alloc) = scsi_test_init(cntlrcfg, device_vec.clone()); let features = virtio_scsi_defalut_feature(cntlr.clone()); -- Gitee From b246783f0a42297008552e1a8ddd571419023e61 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 Feb 2023 01:08:12 +0800 Subject: [PATCH 0772/2187] vhost-user-fs-pci: add pci class code for virtiofs Set pci class code for vhost-user-fs-pci device. Signed-off-by: liuxiangdong --- virtio/src/virtio_pci.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 8a0f2595c..be4cf7a8c 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -45,8 +45,8 @@ use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, - VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_GPU, - VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, + VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_FS, + VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -56,6 +56,7 @@ const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; const VIRTIO_PCI_ABI_VERSION: u8 = 1; const VIRTIO_PCI_CLASS_ID_NET: u16 = 0x0280; const VIRTIO_PCI_CLASS_ID_BLOCK: u16 = 0x0100; +const VIRTIO_PCI_CLASS_ID_STORAGE_OTHER: u16 = 0x0180; #[cfg(target_arch = "aarch64")] const VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER: u16 = 0x0380; #[cfg(target_arch = "x86_64")] @@ -132,6 +133,7 @@ fn get_virtio_class_id(device_type: u32) -> u16 { match device_type { VIRTIO_TYPE_BLOCK => VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_SCSI => VIRTIO_PCI_CLASS_ID_BLOCK, + VIRTIO_TYPE_FS => VIRTIO_PCI_CLASS_ID_STORAGE_OTHER, VIRTIO_TYPE_NET => VIRTIO_PCI_CLASS_ID_NET, #[cfg(target_arch = "x86_64")] VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_VGA, -- Gitee From c2fbd0f1a08a2f40912e9871266fa6fff59fe229 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 Feb 2023 02:59:11 +0800 Subject: [PATCH 0773/2187] virtio-fs: get value correctly in FUSE_SETXATTR xattr format is "$name/0$value", so we can get "$name/0" as CString to acquire name. But "$value" is not a CString, modify it. Signed-off-by: liuxiangdong --- vhost_user_fs/src/fuse_msg.rs | 2 +- vhost_user_fs/src/fuse_proc.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 9b9a8581e..6cbe9144d 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -236,7 +236,7 @@ impl FuseBuffer { Ok(cstring) } - fn read_slice( + pub fn read_slice( &mut self, sys_mem: &Arc, dst: &mut [u8], diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 13eae0807..d1e47b8bd 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -944,18 +944,20 @@ pub fn do_fuse_setxattr( } }; - let value = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read value for setxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; + let mut value = Vec::new(); + value.resize(setxattr_in.size as usize, 0); + + if let Err(e) = reader.read_slice(sys_mem, &mut value, setxattr_in.size as usize) { + error!("Failed to read value for setxattr_in, {:?}", e); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let cvalue = CString::new(value).unwrap_or_else(|_| CString::from(Vec::new())); let ret = fs.lock().unwrap().setxattr( in_header.nodeid as usize, name, - value, + cvalue, setxattr_in.size, setxattr_in.flags, ); -- Gitee From 41ec9b57140264ba09921cdb21e255491a8dd134 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 17 Jan 2023 07:08:01 +0800 Subject: [PATCH 0774/2187] aio: Add async flush support As virtio spec states, virtio devices support async flush CMD. Signed-off-by: Keqian Zhu --- util/src/aio/libaio.rs | 9 +++++++-- util/src/aio/mod.rs | 16 +++++++++++++--- virtio/src/block.rs | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 86d870f54..560bc0a5d 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -128,19 +128,24 @@ impl AioContext for LibaioContext { let opcode = match cb.opcode { OpCode::Preadv => IoCmd::Preadv, OpCode::Pwritev => IoCmd::Pwritev, + OpCode::Fdsync => IoCmd::Fdsync, _ => bail!("Failed to submit aio, opcode is not supported."), }; + let aio_buf = match cb.opcode { + OpCode::Fdsync => 0, + _ => cb.iovec.as_ptr() as u64, + }; iocbs.push(IoCb { data: cb.user_data, aio_lio_opcode: opcode as u16, aio_fildes: cb.file_fd as u32, - aio_buf: cb.iovec.as_ptr() as u64, + aio_buf, aio_nbytes: cb.iovec.len() as u64, aio_offset: cb.offset as u64, aio_flags: IOCB_FLAG_RESFD, aio_resfd: self.resfd as u32, ..Default::default() - }) + }); } // SYS_io_submit needs vec of references. diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index df5cde977..d545b1284 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -172,12 +172,18 @@ impl Aio { match cb.opcode { OpCode::Preadv | OpCode::Pwritev => { if self.ctx.is_some() { - self.rw_aio(cb) + self.rw_async(cb) } else { self.rw_sync(cb) } } - OpCode::Fdsync => self.flush_sync(cb), + OpCode::Fdsync => { + if self.ctx.is_some() { + self.flush_async(cb) + } else { + self.flush_sync(cb) + } + } OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), } } @@ -264,7 +270,7 @@ impl Aio { Ok(()) } - fn rw_aio(&mut self, cb: AioCb) -> Result<()> { + fn rw_async(&mut self, cb: AioCb) -> Result<()> { let last_aio = cb.last_aio; let mut node = Box::new(Node::new(cb)); node.value.user_data = (&mut (*node) as *mut CbNode) as u64; @@ -394,6 +400,10 @@ impl Aio { } } + fn flush_async(&mut self, cb: AioCb) -> Result<()> { + self.rw_async(cb) + } + fn flush_sync(&mut self, cb: AioCb) -> Result<()> { let ret = raw_datasync(cb.file_fd).unwrap_or_else(|e| { error!("Failed to do sync flush, {:?}", e); diff --git a/virtio/src/block.rs b/virtio/src/block.rs index c156b3e32..3ebbe2fe0 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -439,7 +439,7 @@ impl BlockIoHandler { merged_iovs += req_iovs; merged_bytes += req_bytes; } else { - if io { + if io || req.out_header.request_type == VIRTIO_BLK_T_FLUSH { *last_aio_index = merge_req_queue.len(); } merge_req_queue.push(req); -- Gitee From 49a323127f4ae2cf8646a9cbba38a692a276f66c Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Mon, 20 Feb 2023 07:10:54 +0000 Subject: [PATCH 0775/2187] virtio-gpu: fix resources leak in reset The resource must be released, otherwise the pixman image bound to the resource will not be released correctly. Signed-off-by: wubinfeng --- virtio/src/gpu.rs | 61 ++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index d479ca58a..1311f1d24 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -491,6 +491,16 @@ fn is_rect_in_resouce(rect: &VirtioGpuRect, res: &GpuResource) -> bool { false } +// Mask resource's scanout bit before disable a scanout. +fn disable_scanout(scanout: &mut GpuScanout) { + if scanout.resource_id == 0 { + return; + } + // TODO: present 'Guest disabled display.' in surface. + display_replace_surface(scanout.con_id, None); + scanout.clear(); +} + impl GpuIoHandler { fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { if header.out_len < size_of::() as u32 { @@ -744,6 +754,28 @@ impl GpuIoHandler { self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } + fn resource_destroy(&mut self, res_index: usize) { + let res = &mut self.resources_list[res_index]; + + if res.scanouts_bitmask == 0 { + return; + } + + for i in 0..self.num_scanouts { + if (res.scanouts_bitmask & (1 << i)) != 0 { + let scanout = &mut self.scanouts[i as usize]; + res.scanouts_bitmask &= !(1 << i); + disable_scanout(scanout); + } + } + + unsafe { + pixman_image_unref(res.pixman_image); + } + self.used_hostmem -= res.host_mem; + res.iov.clear(); + } + fn cmd_resource_unref(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_resource_unref = VirtioGpuResourceUnref::default(); self.get_request(req, &mut info_resource_unref)?; @@ -753,25 +785,7 @@ impl GpuIoHandler { .iter() .position(|x| x.resource_id == info_resource_unref.resource_id) { - let res = &mut self.resources_list[res_index]; - if res.scanouts_bitmask != 0 { - for i in 0..self.num_scanouts { - if (res.scanouts_bitmask & (1 << i)) != 0 { - let scanout = &mut self.scanouts[i as usize]; - if scanout.resource_id != 0 { - // disable the scanout - res.scanouts_bitmask &= !(1 << i); - display_replace_surface(scanout.con_id, None); - scanout.clear(); - } - } - } - } - unsafe { - pixman_image_unref(res.pixman_image); - } - self.used_hostmem -= res.host_mem; - res.iov.clear(); + self.resource_destroy(res_index); self.resources_list.remove(res_index); self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { @@ -795,7 +809,6 @@ impl GpuIoHandler { return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, req); } - // TODO: refactor to disable function let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; if info_set_scanout.resource_id == 0 { // Set resource_id to 0 means disable the scanout. @@ -807,8 +820,7 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_index]; res.scanouts_bitmask &= !(1 << info_set_scanout.scanout_id); } - display_replace_surface(scanout.con_id, None); - scanout.clear(); + disable_scanout(scanout); return self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req); } @@ -1391,6 +1403,11 @@ impl Drop for GpuIoHandler { for scanout in &self.scanouts { console_close(scanout.con_id); } + + while !self.resources_list.is_empty() { + self.resource_destroy(0); + self.resources_list.remove(0); + } } } -- Gitee From f5fa4e3f971769182c753007b0e0167698a4d4c9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 17 Jan 2023 09:56:11 +0800 Subject: [PATCH 0776/2187] aio/raw: Some optimizations for sync mode 1. Make raw functions return result directly, and let higher module makes decision. 2. Add vectorized read/write support for sync io. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/syscall.rs | 2 + machine/src/standard_vm/aarch64/syscall.rs | 2 + machine/src/standard_vm/x86_64/syscall.rs | 2 + util/src/aio/mod.rs | 71 +++++--------- util/src/aio/raw.rs | 109 +++++++++++++++++---- virtio/src/block.rs | 4 +- virtio/src/rng.rs | 8 +- 7 files changed, 129 insertions(+), 69 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 75f4ab3ed..89ce5c29b 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -107,7 +107,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_getpid), BpfRule::new(libc::SYS_fstat), BpfRule::new(libc::SYS_pread64), + BpfRule::new(libc::SYS_preadv), BpfRule::new(libc::SYS_pwrite64), + BpfRule::new(libc::SYS_pwritev), BpfRule::new(libc::SYS_statx), #[cfg(all(target_env = "musl", target_arch = "x86_64"))] BpfRule::new(libc::SYS_stat), diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index c50adae73..0ec34a53b 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -115,7 +115,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_getpid), BpfRule::new(libc::SYS_fstat), BpfRule::new(libc::SYS_pread64), + BpfRule::new(libc::SYS_preadv), BpfRule::new(libc::SYS_pwrite64), + BpfRule::new(libc::SYS_pwritev), BpfRule::new(libc::SYS_statx), BpfRule::new(libc::SYS_mkdirat), BpfRule::new(libc::SYS_unlinkat), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 5362ae2ca..277294f8d 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -120,7 +120,9 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_fstat), BpfRule::new(libc::SYS_newfstatat), BpfRule::new(libc::SYS_pread64), + BpfRule::new(libc::SYS_preadv), BpfRule::new(libc::SYS_pwrite64), + BpfRule::new(libc::SYS_pwritev), BpfRule::new(libc::SYS_statx), BpfRule::new(libc::SYS_mkdir), BpfRule::new(libc::SYS_unlink), diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index d545b1284..a1f19d232 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -284,41 +284,17 @@ impl Aio { } fn rw_sync(&mut self, cb: AioCb) -> Result<()> { - let ret = match cb.opcode { - OpCode::Preadv => { - let mut r = 0; - let mut off = cb.offset; - for iov in cb.iovec.iter() { - r = raw_read(cb.file_fd, iov.iov_base, iov.iov_len as usize, off) - .unwrap_or_else(|e| { - error!("Failed to do sync read, {:?}", e); - -1 - }); - if r < 0 { - break; - } - off += iov.iov_len as usize; - } - r - } - OpCode::Pwritev => { - let mut r = 0; - let mut off = cb.offset; - for iov in cb.iovec.iter() { - r = raw_write(cb.file_fd, iov.iov_base, iov.iov_len as usize, off) - .unwrap_or_else(|e| { - error!("Failed to do sync write, {:?}", e); - -1 - }); - if r < 0 { - break; - } - off += iov.iov_len as usize; - } - r - } + let mut ret = match cb.opcode { + OpCode::Preadv => raw_readv(cb.file_fd, &cb.iovec, cb.offset), + OpCode::Pwritev => raw_writev(cb.file_fd, &cb.iovec, cb.offset), _ => -1, }; + if ret < 0 { + error!("Failed to do sync read/write."); + } else if ret as u64 != cb.nbytes { + error!("Incomplete sync read/write."); + ret = -1; + } (self.complete_func)(&cb, ret) } @@ -334,17 +310,17 @@ impl Aio { if aligned_buffer.is_null() { bail!("Failed to alloc memory for misaligned read"); } - raw_read( + let len = raw_read( cb.file_fd, aligned_buffer as u64, cb.nbytes as usize, cb.offset, - ) - .map_err(|e| { + ); + if len < 0 || len as u64 != cb.nbytes { // SAFETY: the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; - anyhow!("Failed to do raw read for misaligned read, {:?}", e) - })?; + bail!("Failed to do raw read for misaligned read."); + } // SAFETY: the memory is allocated by us. let src = unsafe { @@ -384,17 +360,18 @@ impl Aio { return Err(e); } - let res = raw_write( + let len = raw_write( cb.file_fd, aligned_buffer as u64, cb.nbytes as usize, cb.offset, - ) - .map(|_| {}) - .map_err(|e| anyhow!("Failed to do raw write for misaligned write, {:?}", e)); + ); // SAFETY: the memory is allocated by us and will not be used anymore. unsafe { libc::free(aligned_buffer) }; - res + if len < 0 || len as u64 != cb.nbytes { + bail!("Failed to do raw write for misaligned write."); + } + Ok(()) } _ => bail!("Failed to do misaligned rw: unknown cmd type"), } @@ -405,10 +382,10 @@ impl Aio { } fn flush_sync(&mut self, cb: AioCb) -> Result<()> { - let ret = raw_datasync(cb.file_fd).unwrap_or_else(|e| { - error!("Failed to do sync flush, {:?}", e); - -1 - }); + let ret = raw_datasync(cb.file_fd); + if ret < 0 { + error!("Failed to do sync flush."); + } (self.complete_func)(&cb, ret) } } diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index a0c4eb187..c62a62670 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -10,49 +10,124 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::Result; -use anyhow::bail; -use libc::{c_void, fdatasync, pread, pwrite}; +use super::Iovec; +use libc::{c_int, c_void, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t}; +use log::error; use std::os::unix::io::RawFd; -pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result { +pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { let mut ret; loop { // SAFETY: fd and buf is valid. - ret = unsafe { pread(fd, buf as *mut c_void, size, offset as i64) as i64 }; + ret = unsafe { + pread( + fd as c_int, + buf as *mut c_void, + size as size_t, + offset as off_t, + ) as i64 + }; if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { break; } } - if ret < 0 || ret as usize != size { - bail!("Failed to pread for {}, size {} return {}.", fd, size, ret); + if ret < 0 { + error!( + "Failed to pread: buf{}, size{}, offset{}, errno{}.", + buf, + size, + offset, + errno::errno().0 + ); } + ret +} - Ok(ret) +pub fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { + let mut ret; + loop { + // SAFETY: fd and buf is valid. + ret = unsafe { + preadv( + fd as c_int, + iovec.as_ptr() as *const iovec, + iovec.len() as c_int, + offset as off_t, + ) as i64 + }; + if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + break; + } + } + if ret < 0 { + error!( + "Failed to preadv: offset{}, errno{}.", + offset, + errno::errno().0, + ); + } + ret } -pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> Result { +pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { let mut ret; loop { // SAFETY: fd and buf is valid. - ret = unsafe { pwrite(fd, buf as *mut c_void, size, offset as i64) as i64 }; + ret = unsafe { + pwrite( + fd as c_int, + buf as *mut c_void, + size as size_t, + offset as off_t, + ) as i64 + }; if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { break; } } - if ret < 0 || ret as usize != size { - bail!("Failed to pwrite for {}, size {} return {}.", fd, size, ret); + if ret < 0 { + error!( + "Failed to pwrite: buf{}, size{}, offset{}, errno{}.", + buf, + size, + offset, + errno::errno().0, + ); } + ret +} - Ok(ret) +pub fn raw_writev(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { + let mut ret; + loop { + // SAFETY: fd and buf is valid. + ret = unsafe { + pwritev( + fd as c_int, + iovec.as_ptr() as *const iovec, + iovec.len() as c_int, + offset as off_t, + ) as i64 + }; + if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + break; + } + } + if ret < 0 { + error!( + "Failed to pwritev: offset{}, errno{}.", + offset, + errno::errno().0, + ); + } + ret } -pub fn raw_datasync(fd: RawFd) -> Result { +pub fn raw_datasync(fd: RawFd) -> i64 { // SAFETY: fd is valid. let ret = unsafe { i64::from(fdatasync(fd)) }; if ret < 0 { - bail!("Failed to fdatasync for {}, return {}.", fd, ret); + error!("Failed to fdatasync: errno{}.", errno::errno().0); } - - Ok(ret) + ret } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 3ebbe2fe0..bb8d268e2 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -620,8 +620,8 @@ impl BlockIoHandler { && aiocb.opcode == OpCode::Pwritev && ret >= 0 { - if let Err(ref e) = raw_datasync(aiocb.file_fd) { - error!("Failed to flush data before send response to guest {:?}", e); + if raw_datasync(aiocb.file_fd) < 0 { + error!("Failed to flush data before send response to guest."); status = VIRTIO_BLK_S_IOERR; } } diff --git a/virtio/src/rng.rs b/virtio/src/rng.rs index 83bdcce4d..de747b67c 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/rng.rs @@ -112,13 +112,15 @@ impl RngHandler { } let mut buffer = vec![0_u8; size as usize]; - raw_read( + let ret = raw_read( self.random_file.as_raw_fd(), buffer.as_mut_ptr() as u64, size as usize, 0, - ) - .with_context(|| format!("Failed to read random file, size: {}", size))?; + ); + if ret < 0 || ret as u32 != size { + bail!("Failed to read random file, size: {}", size); + } self.write_req_data(&elem.in_iovec, &mut buffer)?; -- Gitee From eb9647d6fdeb7fb81ac66121dd64b5752432e0b4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 17 Jan 2023 13:41:02 +0800 Subject: [PATCH 0777/2187] aio: Fix alignment detection for direct IO The default alignment 512 for direct IO is not always correct. There is two alignment requirements: 1. request alignment: for offset and len. 2. buffer alignment: for iova base of memory buffer. Signed-off-by: Keqian Zhu --- machine_manager/src/config/drive.rs | 4 + machine_manager/src/config/mod.rs | 36 +++- util/src/aio/mod.rs | 245 +++++++++++++++++++--------- util/src/align.rs | 27 +++ util/src/file.rs | 60 +++++++ util/src/lib.rs | 1 + virtio/src/block.rs | 59 +++++-- virtio/src/scsi/bus.rs | 25 +-- virtio/src/scsi/controller.rs | 19 ++- virtio/src/scsi/disk.rs | 15 +- 10 files changed, 372 insertions(+), 119 deletions(-) create mode 100644 util/src/align.rs diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 3e787fa0e..c31d37501 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -46,6 +46,10 @@ pub struct DriveFile { pub read_only: bool, /// File lock status. pub locked: bool, + /// The align requirement of request(offset/len). + pub req_align: u32, + /// The align requirement of buffer(iova_base). + pub buf_align: u32, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 20bd38ace..257762738 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -29,7 +29,6 @@ pub use sasl_auth::*; pub use scsi::*; pub use tls_creds::*; pub use usb::*; -use util::AsAny; pub use vfio::*; pub use vnc::*; @@ -57,15 +56,21 @@ pub mod vnc; use std::collections::HashMap; use std::fs::File; +use std::io::{Seek, SeekFrom}; use std::str::FromStr; use serde::{Deserialize, Serialize}; use anyhow::{anyhow, bail, Context, Result}; use log::error; +use util::align::is_aligned; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; -use util::{file::open_file, trace::enable_trace_events}; +use util::{ + file::{get_file_alignment, open_file}, + trace::enable_trace_events, + AsAny, +}; pub const MAX_STRING_LENGTH: usize = 255; pub const MAX_PATH_LENGTH: usize = 4096; @@ -262,12 +267,26 @@ impl VmConfig { )); } } + let mut file = open_file(path, read_only, direct)?; + let (req_align, buf_align) = get_file_alignment(&file, direct); + if req_align == 0 || buf_align == 0 { + bail!( + "Failed to detect alignment requirement of drive file {}.", + path + ); + } + let file_size = file.seek(SeekFrom::End(0))?; + if !is_aligned(file_size, req_align) { + bail!("The size of file {} is not aligned to {}.", path, req_align); + } let drive_file = DriveFile { - file: open_file(path, read_only, direct)?, + file, count: 1, read_only, path: path.to_string(), locked: false, + req_align, + buf_align, }; drive_files.insert(path.to_string(), drive_file); Ok(()) @@ -303,6 +322,17 @@ impl VmConfig { } } + /// Get alignment requirement from drive file store. + pub fn fetch_drive_align( + drive_files: &HashMap, + path: &str, + ) -> Result<(u32, u32)> { + match drive_files.get(path) { + Some(drive_file) => Ok((drive_file.req_align, drive_file.buf_align)), + None => Err(anyhow!("The file {} is not in drive backend", path)), + } + } + /// Create initial drive file store from cmdline drive. pub fn init_drive_files(&self) -> Result> { let mut drive_files: HashMap = HashMap::new(); diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index a1f19d232..3ec5fa892 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -20,11 +20,14 @@ use std::os::unix::io::RawFd; use std::sync::Arc; use std::{cmp, str::FromStr}; +use libc::c_void; use log::{error, warn}; use serde::{Deserialize, Serialize}; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; +use crate::align::{align_down, align_up, is_aligned}; +use crate::unix::host_page_size; use anyhow::{anyhow, bail, Context, Result}; use libaio::LibaioContext; pub use raw::*; @@ -39,6 +42,8 @@ const AIO_OFF: &str = "off"; const AIO_NATIVE: &str = "native"; /// Io-uring aio type. const AIO_IOURING: &str = "io_uring"; +/// Max bytes of bounce buffer for misaligned IO. +const MAX_LEN_BOUNCE_BUFF: u64 = 1 << 20; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] pub enum AioEngine { @@ -91,7 +96,8 @@ pub enum OpCode { pub struct AioCb { pub last_aio: bool, pub direct: bool, - pub sector_size: u64, + pub req_align: u32, + pub buf_align: u32, pub file_fd: RawFd, pub opcode: OpCode, pub iovec: Vec, @@ -153,20 +159,31 @@ impl Aio { self.engine } - pub fn submit_request(&mut self, cb: AioCb) -> Result<()> { - if cb.direct && (cb.opcode == OpCode::Preadv || cb.opcode == OpCode::Pwritev) { - for iov in cb.iovec.iter() { - if iov.iov_base % cb.sector_size != 0 || iov.iov_len % cb.sector_size != 0 { - let res = self.handle_misaligned_rw(&cb).map_or_else( - |e| { - error!("{:?}", e); - -1 - }, - |_| 0, - ); - return (self.complete_func)(&cb, res); - } + pub fn submit_request(&mut self, mut cb: AioCb) -> Result<()> { + if self.request_misaligned(&cb) { + let max_len = align_down(cb.nbytes + cb.req_align as u64 * 2, cb.req_align); + // Set upper limit of buffer length to avoid OOM. + let buff_len = cmp::min(max_len, MAX_LEN_BOUNCE_BUFF); + // SAFETY: we allocate aligned memory and free it later. Alignment is set to + // host page size to decrease the count of allocated pages. + let bounce_buffer = + unsafe { libc::memalign(host_page_size() as usize, buff_len as usize) }; + if bounce_buffer.is_null() { + error!("Failed to alloc memory for misaligned read/write."); + return (self.complete_func)(&cb, -1); } + + let res = match self.handle_misaligned_rw(&mut cb, bounce_buffer, buff_len) { + Ok(()) => 0, + Err(e) => { + error!("{:?}", e); + -1 + } + }; + + // SAFETY: the memory is allocated by us and will not be used anymore. + unsafe { libc::free(bounce_buffer) }; + return (self.complete_func)(&cb, res); } match cb.opcode { @@ -298,78 +315,152 @@ impl Aio { (self.complete_func)(&cb, ret) } - fn handle_misaligned_rw(&mut self, cb: &AioCb) -> Result<()> { - // SAFETY: only get the host page size. - let host_page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u64; - match cb.opcode { - OpCode::Preadv => { - // SAFETY: we allocate aligned memory and free it later. - // Alignment is set to host page size to decrease the count of allocated pages. - let aligned_buffer = - unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; - if aligned_buffer.is_null() { - bail!("Failed to alloc memory for misaligned read"); + fn request_misaligned(&self, cb: &AioCb) -> bool { + if cb.direct && (cb.opcode == OpCode::Preadv || cb.opcode == OpCode::Pwritev) { + if !is_aligned(cb.offset as u64, cb.req_align) { + return true; + } + for iov in cb.iovec.iter() { + if !is_aligned(iov.iov_base, cb.buf_align) { + return true; } - let len = raw_read( - cb.file_fd, - aligned_buffer as u64, - cb.nbytes as usize, - cb.offset, - ); - if len < 0 || len as u64 != cb.nbytes { - // SAFETY: the memory is allocated by us and will not be used anymore. - unsafe { libc::free(aligned_buffer) }; - bail!("Failed to do raw read for misaligned read."); + if !is_aligned(iov.iov_len, cb.req_align) { + return true; } + } + } + false + } - // SAFETY: the memory is allocated by us. - let src = unsafe { - std::slice::from_raw_parts(aligned_buffer as *const u8, cb.nbytes as usize) - }; - let res = iov_from_buf_direct(&cb.iovec, src).and_then(|v| { - if v == cb.nbytes as usize { - Ok(()) - } else { - Err(anyhow!("Failed to copy iovs to buff for misaligned read")) + fn handle_misaligned_rw( + &mut self, + cb: &mut AioCb, + bounce_buffer: *mut c_void, + buffer_len: u64, + ) -> Result<()> { + let offset_align = align_down(cb.offset as u64, cb.req_align); + let high = cb.offset as u64 + cb.nbytes; + let high_align = align_up(high, cb.req_align); + + match cb.opcode { + OpCode::Preadv => { + let mut offset = offset_align; + let mut iovecs = &mut cb.iovec[..]; + loop { + // Step1: Read file to bounce buffer. + let nbytes = cmp::min(high_align - offset, buffer_len); + let len = raw_read( + cb.file_fd, + bounce_buffer as u64, + nbytes as usize, + offset as usize, + ); + if len < 0 || len as u64 != nbytes { + bail!("Failed to do raw read for misaligned read."); + } + + let real_offset = cmp::max(offset, cb.offset as u64); + let real_high = cmp::min(offset + nbytes, high); + let real_nbytes = real_high - real_offset; + // SAFETY: the memory is allocated by us. + let src = unsafe { + std::slice::from_raw_parts( + (bounce_buffer as u64 + real_offset - offset) as *const u8, + real_nbytes as usize, + ) + }; + + // Step2: Copy bounce buffer to iovec. + iov_from_buf_direct(iovecs, src).and_then(|v| { + if v == real_nbytes as usize { + Ok(()) + } else { + Err(anyhow!("Failed to copy iovs to buff for misaligned read")) + } + })?; + + // Step3: Adjust offset and iovec for next loop. + offset += nbytes; + if offset >= high_align { + break; } - }); - // SAFETY: the memory is allocated by us and will not be used anymore. - unsafe { libc::free(aligned_buffer) }; - res + iovecs = iov_discard_front_direct(iovecs, real_nbytes) + .ok_or_else(|| anyhow!("Failed to adjust iovec for misaligned read"))?; + } + Ok(()) } OpCode::Pwritev => { - // SAFETY: we allocate aligned memory and free it later. - let aligned_buffer = - unsafe { libc::memalign(host_page_size as usize, cb.nbytes as usize) }; - if aligned_buffer.is_null() { - bail!("Failed to alloc memory for misaligned write"); - } - // SAFETY: the memory is allocated by us. - let dst = unsafe { - std::slice::from_raw_parts_mut(aligned_buffer as *mut u8, cb.nbytes as usize) - }; - if let Err(e) = iov_to_buf_direct(&cb.iovec, dst).and_then(|v| { - if v == cb.nbytes as usize { - Ok(()) - } else { - Err(anyhow!("Failed to copy iovs to buff for misaligned write")) + // Load the head from file before fill iovec to buffer. + if cb.offset as u64 > offset_align { + let len = raw_read( + cb.file_fd, + bounce_buffer as u64, + cb.req_align as usize, + offset_align as usize, + ); + if len < 0 || len as u32 != cb.req_align { + bail!("Failed to load head for misaligned write."); } - }) { - // SAFETY: the memory is allocated by us and will not be used anymore. - unsafe { libc::free(aligned_buffer) }; - return Err(e); } + // Is head and tail in the same alignment section? + let tail_loaded = (offset_align + cb.req_align as u64) >= high; + let need_tail = !tail_loaded && (high_align > high); + + let mut offset = offset_align; + let mut iovecs = &mut cb.iovec[..]; + loop { + // Step1: Load iovec to bounce buffer. + let nbytes = cmp::min(high_align - offset, buffer_len); + + let real_offset = cmp::max(offset, cb.offset as u64); + let real_high = cmp::min(offset + nbytes, high); + let real_nbytes = real_high - real_offset; + + if real_high == high && need_tail { + let len = raw_read( + cb.file_fd, + bounce_buffer as u64 + nbytes - cb.req_align as u64, + cb.req_align as usize, + (offset + nbytes) as usize - cb.req_align as usize, + ); + if len < 0 || len as u32 != cb.req_align { + bail!("Failed to load tail for misaligned write."); + } + } - let len = raw_write( - cb.file_fd, - aligned_buffer as u64, - cb.nbytes as usize, - cb.offset, - ); - // SAFETY: the memory is allocated by us and will not be used anymore. - unsafe { libc::free(aligned_buffer) }; - if len < 0 || len as u64 != cb.nbytes { - bail!("Failed to do raw write for misaligned write."); + // SAFETY: the memory is allocated by us. + let dst = unsafe { + std::slice::from_raw_parts_mut( + (bounce_buffer as u64 + real_offset - offset) as *mut u8, + real_nbytes as usize, + ) + }; + iov_to_buf_direct(iovecs, dst).and_then(|v| { + if v == real_nbytes as usize { + Ok(()) + } else { + Err(anyhow!("Failed to copy iovs to buff for misaligned write")) + } + })?; + + // Step2: Write bounce buffer to file. + let len = raw_write( + cb.file_fd, + bounce_buffer as u64, + nbytes as usize, + offset as usize, + ); + if len < 0 || len as u64 != nbytes { + bail!("Failed to do raw write for misaligned write."); + } + + // Step3: Adjuest offset and iovec for next loop. + offset += nbytes; + if offset >= high_align { + break; + } + iovecs = iov_discard_front_direct(iovecs, real_nbytes) + .ok_or_else(|| anyhow!("Failed to adjust iovec for misaligned write"))?; } Ok(()) } diff --git a/util/src/align.rs b/util/src/align.rs new file mode 100644 index 000000000..a257904dd --- /dev/null +++ b/util/src/align.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub fn is_aligned(offset: u64, align: u32) -> bool { + offset & (align as u64 - 1) == 0 +} + +pub fn align_down(offset: u64, align: u32) -> u64 { + offset - (offset & (align as u64 - 1)) +} + +pub fn align_up(offset: u64, align: u32) -> u64 { + if !is_aligned(offset, align) { + align_down(offset, align) + align as u64 + } else { + offset + } +} diff --git a/util/src/file.rs b/util/src/file.rs index 97b32654a..a0855eaba 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -16,6 +16,9 @@ use std::os::unix::io::AsRawFd; use anyhow::{bail, Context, Result}; +const MIN_FILE_ALIGN: u32 = 512; +const MAX_FILE_ALIGN: u32 = 4096; + pub fn open_file(path: &str, read_only: bool, direct: bool) -> Result { let mut options = OpenOptions::new(); options.read(true).write(!read_only); @@ -33,6 +36,63 @@ pub fn open_file(path: &str, read_only: bool, direct: bool) -> Result { Ok(file) } +fn is_io_aligned(file: &File, buf: u64, size: usize) -> bool { + // SAFETY: file and buf is valid. + let ret = unsafe { + libc::pread( + file.as_raw_fd() as libc::c_int, + buf as *mut libc::c_void, + size as libc::size_t, + 0, + ) + }; + ret >= 0 || errno::errno().0 != libc::EINVAL +} + +pub fn get_file_alignment(file: &File, direct: bool) -> (u32, u32) { + if !direct { + return (1, 1); + } + + let mut req_align = 0; + let mut buf_align = 0; + // SAFETY: we allocate aligned memory and free it later. + let aligned_buffer = unsafe { + libc::memalign( + MAX_FILE_ALIGN as libc::size_t, + (MAX_FILE_ALIGN * 2) as libc::size_t, + ) + }; + + // Guess alignment requirement of request. + let mut align = MIN_FILE_ALIGN; + while align <= MAX_FILE_ALIGN { + if is_io_aligned(file, aligned_buffer as u64, align as usize) { + req_align = align; + break; + } + align <<= 1; + } + + // Guess alignment requirement of buffer. + let mut align = MIN_FILE_ALIGN; + while align <= MAX_FILE_ALIGN { + if is_io_aligned( + file, + aligned_buffer as u64 + align as u64, + MAX_FILE_ALIGN as usize, + ) { + buf_align = align; + break; + } + align <<= 1; + } + + // SAFETY: the memory is allocated by us and will not be used anymore. + unsafe { libc::free(aligned_buffer) }; + (req_align, buf_align) +} + pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { let (lockop, lockname) = if read_only { (libc::LOCK_SH | libc::LOCK_NB, "read lock") diff --git a/util/src/lib.rs b/util/src/lib.rs index 19fed784c..d7edcf853 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod aio; +pub mod align; pub mod arg_parser; pub mod bitmap; pub mod byte_code; diff --git a/virtio/src/block.rs b/virtio/src/block.rs index bb8d268e2..868cd5f17 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -68,7 +68,15 @@ const MAX_NUM_MERGE_BYTES: u64 = i32::MAX as u64; /// Max time for every round of process queue. const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; -type SenderConfig = (Option>, u64, Option, bool, AioEngine); +type SenderConfig = ( + Option>, + u32, + u32, + u64, + Option, + bool, + AioEngine, +); fn get_serial_num_config(serial_num: &str) -> Vec { let mut id_bytes = vec![0; VIRTIO_BLK_ID_BYTES as usize]; @@ -374,6 +382,10 @@ struct BlockIoHandler { mem_space: Arc, /// The image file opened by the block device. disk_image: Option>, + /// The align requirement of request(offset/len). + pub req_align: u32, + /// The align requirement of buffer(iova_base). + pub buf_align: u32, /// The number of sectors of the disk image. disk_sectors: u64, /// Serial number of the block device. @@ -524,7 +536,8 @@ impl BlockIoHandler { let aiocb = AioCb { last_aio: req_index == last_aio_req_index, direct: self.direct, - sector_size: SECTOR_SIZE, + req_align: self.req_align, + buf_align: self.buf_align, file_fd: disk_img.as_raw_fd(), opcode: OpCode::Noop, iovec: Vec::new(), @@ -619,11 +632,10 @@ impl BlockIoHandler { if !virtio_has_feature(complete_cb.driver_features, VIRTIO_BLK_F_FLUSH) && aiocb.opcode == OpCode::Pwritev && ret >= 0 + && raw_datasync(aiocb.file_fd) < 0 { - if raw_datasync(aiocb.file_fd) < 0 { - error!("Failed to flush data before send response to guest."); - status = VIRTIO_BLK_S_IOERR; - } + error!("Failed to flush data before send response to guest."); + status = VIRTIO_BLK_S_IOERR; } complete_cb.complete_request(status) @@ -643,9 +655,11 @@ impl BlockIoHandler { fn update_evt_handler(&mut self) { let aio_engine; match self.receiver.recv() { - Ok((image, disk_sectors, serial_num, direct, aio)) => { + Ok((image, req_align, buf_align, disk_sectors, serial_num, direct, aio)) => { self.disk_sectors = disk_sectors; self.disk_image = image; + self.req_align = req_align; + self.buf_align = buf_align; self.serial_num = serial_num; self.direct = direct; aio_engine = aio; @@ -654,6 +668,8 @@ impl BlockIoHandler { error!("Failed to receive config in updating handler {:?}", e); self.disk_sectors = 0; self.disk_image = None; + self.req_align = 1; + self.buf_align = 1; self.serial_num = None; self.direct = true; aio_engine = AioEngine::Native; @@ -916,6 +932,10 @@ pub struct Block { blk_cfg: BlkDevConfig, /// Image file opened. disk_image: Option>, + /// The align requirement of request(offset/len). + pub req_align: u32, + /// The align requirement of buffer(iova_base). + pub buf_align: u32, /// Number of sectors of the image file. disk_sectors: u64, /// Status of block device. @@ -939,6 +959,8 @@ impl Default for Block { Block { blk_cfg: Default::default(), disk_image: None, + req_align: 1, + buf_align: 1, disk_sectors: 0, state: BlockState::default(), interrupt_cb: None, @@ -959,6 +981,8 @@ impl Block { Self { blk_cfg, disk_image: None, + req_align: 1, + buf_align: 1, disk_sectors: 0, state: BlockState::default(), interrupt_cb: None, @@ -1007,18 +1031,23 @@ impl VirtioDevice for Block { self.state.config_space.num_queues = self.blk_cfg.queues; } - let mut disk_image = None; - let mut disk_size = DUMMY_IMG_SIZE; + self.disk_image = None; + self.disk_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; + self.req_align = 1; + self.buf_align = 1; if !self.blk_cfg.path_on_host.is_empty() { let drive_files = self.drive_files.lock().unwrap(); let mut file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; - disk_size = file + let alignments = VmConfig::fetch_drive_align(&drive_files, &self.blk_cfg.path_on_host)?; + let disk_size = file .seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for block")?; - disk_image = Some(Arc::new(file)); + + self.disk_image = Some(Arc::new(file)); + self.disk_sectors = disk_size >> SECTOR_SHIFT; + self.req_align = alignments.0; + self.buf_align = alignments.1; } - self.disk_image = disk_image; - self.disk_sectors = disk_size >> SECTOR_SHIFT; self.state.config_space.capacity = self.disk_sectors; Ok(()) @@ -1121,6 +1150,8 @@ impl VirtioDevice for Block { queue_evt, mem_space: mem_space.clone(), disk_image: self.disk_image.clone(), + req_align: self.req_align, + buf_align: self.buf_align, disk_sectors: self.disk_sectors, direct: self.blk_cfg.direct, serial_num: self.blk_cfg.serial_num.clone(), @@ -1177,6 +1208,8 @@ impl VirtioDevice for Block { sender .send(( self.disk_image.clone(), + self.req_align, + self.buf_align, self.disk_sectors, self.blk_cfg.serial_num.clone(), self.blk_cfg.direct, diff --git a/virtio/src/scsi/bus.rs b/virtio/src/scsi/bus.rs index 7599c26e3..d753e0956 100644 --- a/virtio/src/scsi/bus.rs +++ b/virtio/src/scsi/bus.rs @@ -12,9 +12,7 @@ use std::cmp; use std::collections::HashMap; -use std::fs::File; use std::io::Write; -use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; @@ -179,11 +177,6 @@ pub const SCSI_SENSE_BUF_SIZE: usize = 252; /// SERVICE ACTION IN subcodes. pub const SUBCODE_READ_CAPACITY_16: u8 = 0x10; -/// Used to compute the number of sectors. -const SECTOR_SHIFT: u8 = 9; -/// Size of a sector of the block device. -const SECTOR_SIZE: u64 = (0x01_u64) << SECTOR_SHIFT; - /// Sense Keys. pub const NO_SENSE: u8 = 0x00; pub const RECOVERED_ERROR: u8 = 0x01; @@ -514,28 +507,14 @@ impl ScsiRequest { pub fn execute( &self, aio: &mut Box>, - disk: &File, - direct: bool, - last_aio: bool, - iocompletecb: ScsiCompleteCb, + mut aiocb: AioCb, ) -> Result { let dev_lock = self.dev.lock().unwrap(); let offset = match dev_lock.scsi_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, }; - let mut aiocb = AioCb { - last_aio, - direct, - sector_size: SECTOR_SIZE, - file_fd: disk.as_raw_fd(), - opcode: OpCode::Noop, - iovec: Vec::new(), - offset: (self.cmd.lba << offset) as usize, - nbytes: 0, - user_data: 0, - iocompletecb, - }; + aiocb.offset = (self.cmd.lba << offset) as usize; for iov in self.virtioscsireq.lock().unwrap().iovec.iter() { let iovec = Iovec { diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index 2be65ce50..56b36ffe5 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -38,7 +38,7 @@ use machine_manager::{ config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; -use util::aio::{Aio, AioCb, AioEngine, Iovec}; +use util::aio::{Aio, AioCb, AioEngine, Iovec, OpCode}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -923,6 +923,8 @@ impl ScsiCmdHandler { } else { let direct = scsi_device_lock.config.direct; let disk_img = scsi_device_lock.disk_image.as_ref().unwrap().clone(); + let req_align = scsi_device_lock.req_align; + let buf_align = scsi_device_lock.buf_align; drop(scsi_device_lock); let scsicompletecb = ScsiCompleteCb::new( @@ -930,7 +932,20 @@ impl ScsiCmdHandler { Arc::new(Mutex::new(scsi_req.clone())), ); if let Some(ref mut aio) = self.aio { - scsi_req.execute(aio, &disk_img, direct, true, scsicompletecb)?; + let aiocb = AioCb { + last_aio: true, + direct, + req_align, + buf_align, + file_fd: disk_img.as_raw_fd(), + opcode: OpCode::Noop, + iovec: Vec::new(), + offset: 0, + nbytes: 0, + user_data: 0, + iocompletecb: scsicompletecb, + }; + scsi_req.execute(aio, aiocb)?; } } } diff --git a/virtio/src/scsi/disk.rs b/virtio/src/scsi/disk.rs index bf9052d46..4e7815e1f 100644 --- a/virtio/src/scsi/disk.rs +++ b/virtio/src/scsi/disk.rs @@ -94,6 +94,10 @@ pub struct ScsiDevice { pub state: ScsiDevState, /// Image file opened. pub disk_image: Option>, + /// The align requirement of request(offset/len). + pub req_align: u32, + /// The align requirement of buffer(iova_base). + pub buf_align: u32, /// Number of sectors of the image file. pub disk_sectors: u64, /// Scsi Device block size. @@ -112,6 +116,8 @@ impl Default for ScsiDevice { config: Default::default(), state: Default::default(), disk_image: None, + req_align: 1, + buf_align: 1, disk_sectors: 0, block_size: SCSI_DISK_DEFAULT_BLOCK_SIZE, scsi_type: SCSI_TYPE_DISK, @@ -131,6 +137,8 @@ impl ScsiDevice { config, state: ScsiDevState::new(), disk_image: None, + req_align: 1, + buf_align: 1, disk_sectors: 0, block_size: 0, scsi_type, @@ -167,10 +175,15 @@ impl ScsiDevice { disk_size = file .seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for scsi device")?; - self.disk_image = Some(Arc::new(file)); + + let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; + self.req_align = alignments.0; + self.buf_align = alignments.1; } else { self.disk_image = None; + self.req_align = 1; + self.buf_align = 1; } self.disk_sectors = disk_size >> SECTOR_SHIFT; -- Gitee From 7c8826253921211625e3354d6c3d52aaa568e63b Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 6 Feb 2023 09:44:20 +0800 Subject: [PATCH 0778/2187] Balloon: add REMOVE error type for memory_advise We added the remove advise handling, which also requires the corresponding error handling if something goes wrong. Signed-off-by: Li HuaChao --- virtio/src/balloon.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 04579a778..9772b3157 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -147,10 +147,10 @@ fn iov_to_buf( fn memory_advise(addr: *mut libc::c_void, len: libc::size_t, advice: libc::c_int) { // Safe, because the memory to be freed is allocated by guest. if unsafe { libc::madvise(addr, len, advice) } != 0 { - let evt_type = if advice == libc::MADV_WILLNEED { - "WILLNEED".to_string() - } else { - "DONTNEED".to_string() + let evt_type = match advice { + libc::MADV_DONTNEED => "DONTNEED".to_string(), + libc::MADV_REMOVE => "REMOVE".to_string(), + _ => "WILLNEED".to_string(), }; let e = std::io::Error::last_os_error(); error!( -- Gitee From 72cd8a9677b5c25818ddfc50c003f72aee6ef6f7 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 21 Feb 2023 14:33:44 +0800 Subject: [PATCH 0779/2187] Fix: clear compilation alarms When the cargo test --all command is executed, two alarms are displayed. Signed-off-by: Mingwang Li --- machine_manager/src/qmp/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index deb9df237..53bf086f6 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -750,20 +750,22 @@ mod tests { socket.bind_unix_stream(server); // 1.send greeting response - socket.send_response(true); + let res = socket.send_response(true); let length = client.read(&mut buffer).unwrap(); let qmp_response: QmpGreeting = serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); let qmp_greeting = QmpGreeting::create_greeting(1, 0, 5); assert_eq!(qmp_greeting, qmp_response); + assert_eq!(res.is_err(), false); // 2.send empty response - socket.send_response(false); + let res = socket.send_response(false); let length = client.read(&mut buffer).unwrap(); let qmp_response: Response = serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); let qmp_empty_response = Response::create_empty_response(); assert_eq!(qmp_empty_response, qmp_response); + assert_eq!(res.is_err(), false); // After test. Environment Recover recover_unix_socket_environment("07"); -- Gitee From cf3e2f6dc981b34ae9c923dc1cc93f6e1e1f4549 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 14:44:22 +0800 Subject: [PATCH 0780/2187] virtio-pci: delete duplicated check for queue If the queue is ready will be checked in queue.is_invalid(), no need to check q_config which is duplicated. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 8a0f2595c..1f99940d0 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -742,7 +742,7 @@ impl VirtioPciDevice { .unwrap_or(0); } let queue = Queue::new(*q_config, queue_type).unwrap(); - if q_config.ready && !queue.is_valid(&self.sys_mem) { + if !queue.is_valid(&self.sys_mem) { error!("Failed to activate device: Invalid queue"); return false; } -- Gitee From 1eab3225b07a3500b78f5897eedc520d94d44475 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 22 Feb 2023 16:16:27 +0800 Subject: [PATCH 0781/2187] pl031: Add MST testcases Signed-off-by: yezengruan --- devices/src/legacy/mod.rs | 2 +- devices/src/legacy/pl031.rs | 8 +-- tests/mod_test/tests/pl031_test.rs | 112 +++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 tests/mod_test/tests/pl031_test.rs diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 5288c842e..e6b0ae7c6 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -52,7 +52,7 @@ pub use pflash::PFlash; #[cfg(target_arch = "aarch64")] pub use pl011::PL011; #[cfg(target_arch = "aarch64")] -pub use pl031::PL031; +pub use pl031::{PL031, RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; #[cfg(target_arch = "aarch64")] #[cfg(not(target_env = "musl"))] pub use ramfb::Ramfb; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index dca4b4c0c..563c40c35 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -31,15 +31,15 @@ use vmm_sys_util::eventfd::EventFd; /// Registers for pl031 from ARM PrimeCell Real Time Clock Technical Reference Manual. /// Data Register. -const RTC_DR: u64 = 0x00; +pub const RTC_DR: u64 = 0x00; /// Match Register. const RTC_MR: u64 = 0x04; /// Load Register. -const RTC_LR: u64 = 0x08; +pub const RTC_LR: u64 = 0x08; /// Control Register. -const RTC_CR: u64 = 0x0c; +pub const RTC_CR: u64 = 0x0c; /// Interrupt Mask Set or Clear Register. -const RTC_IMSC: u64 = 0x10; +pub const RTC_IMSC: u64 = 0x10; /// Raw Interrupt Status Register. const RTC_RIS: u64 = 0x14; /// Masked Interrupt Status Register. diff --git a/tests/mod_test/tests/pl031_test.rs b/tests/mod_test/tests/pl031_test.rs new file mode 100644 index 000000000..42de84b81 --- /dev/null +++ b/tests/mod_test/tests/pl031_test.rs @@ -0,0 +1,112 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use devices::legacy::{RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; +use mod_test::libtest::{test_init, test_stop, TestState}; +use rand::{thread_rng, Rng}; +use std::thread::sleep; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +const RTC_ADDR_BASE: u64 = 0x0901_0000; + +fn pl031_read_time(ts: &TestState) -> u32 { + ts.readl(RTC_ADDR_BASE + RTC_DR) +} + +fn pl031_set_time(ts: &TestState, time: u32) { + ts.writel(RTC_ADDR_BASE + RTC_LR, time); +} + +fn get_wall_time() -> u32 { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(tick) => tick.as_secs() as u32, + _ => panic!("Failed to get wall time."), + } +} + +fn pl031_read_reg(ts: &TestState, reg: u64) -> u32 { + ts.readl(RTC_ADDR_BASE + reg) +} + +fn pl031_write_reg(ts: &TestState, reg: u64, val: u32) { + ts.writel(RTC_ADDR_BASE + reg, val); +} + +#[test] +fn check_time() { + let mut ts = test_init(Vec::new()); + + let time1 = pl031_read_time(&ts); + let time2 = pl031_read_time(&ts); + + sleep(Duration::from_millis(2000)); + let time3 = pl031_read_time(&ts); + let time4 = pl031_read_time(&ts); + let wall_time = get_wall_time(); + + assert!((time2 - time1) <= 1); + assert!((time3 - time2) <= 3); + assert!((time3 - time2) >= 2); + assert!((time4 - time3) <= 1); + assert!((wall_time - time4) <= 1); + + test_stop(&mut ts); +} + +#[test] +fn set_time() { + let mut ts = test_init(Vec::new()); + let time1 = pl031_read_time(&ts); + + // Time passes about 5 years. + let time_lapse = 1_5768_0000; + pl031_set_time(&ts, time1 + time_lapse); + + let time2 = pl031_read_time(&ts); + + assert!((time2 - time1) >= time_lapse); + assert!((time2 - time1) <= time_lapse + 1); + test_stop(&mut ts); +} + +#[test] +fn rtc_enable() { + let mut ts = test_init(Vec::new()); + + assert_eq!(pl031_read_reg(&ts, RTC_CR), 1); + test_stop(&mut ts); +} + +#[test] +fn set_mask() { + let mut ts = test_init(Vec::new()); + + pl031_write_reg(&ts, RTC_IMSC, 1); + + assert_eq!(pl031_read_reg(&ts, RTC_IMSC), 1); + test_stop(&mut ts); +} + +#[test] +fn reg_fuzz() { + let mut ts = test_init(Vec::new()); + let mut rng = thread_rng(); + + for _ in 0..1000 { + let reg = rng.gen_range(0..=32); + let val = rng.gen_range(0..=1024); + pl031_read_reg(&ts, reg); + pl031_write_reg(&ts, reg, val); + } + + test_stop(&mut ts); +} -- Gitee From a8d9fd9a7215a4441668711d2434d7d593e596a1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 18:52:16 +0800 Subject: [PATCH 0782/2187] virtio: check the indirect flag of the descriptor elem in indirect table When get the desc elem in indirect table, it should also check if it has the INDIRECT flag. Signed-off-by: Yan Wang --- virtio/src/virtqueue/split.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index d398f06d2..0ab3cd52e 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -344,6 +344,7 @@ impl SplitVringDesc { .desc_num .checked_add(queue_size) .ok_or_else(|| anyhow!("The chained desc number overflows"))?; + continue; } let iovec = ElemIovec { @@ -1639,6 +1640,33 @@ mod tests { } else { assert!(false); } + + // The VIRTQ_DESC_F_NEXT must not set to the descriptor in indirect table. + vring + .set_desc( + &sys_space, + 0, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + 16, + VIRTQ_DESC_F_INDIRECT, + 0, + ) + .unwrap(); + + set_indirect_desc( + &sys_space, + GuestAddress(SYSTEM_SPACE_SIZE / 2), + GuestAddress(0x444), + 100, + VIRTQ_DESC_F_INDIRECT | VIRTQ_DESC_F_WRITE, + 1, + ) + .unwrap(); + if let Err(err) = vring.pop_avail(&sys_space, features) { + assert_eq!(err.to_string(), "Failed to get vring element"); + } else { + assert!(false); + } } #[test] -- Gitee From df88b3c680054d1708fcd760aa4d03131aca4008 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 19:04:14 +0800 Subject: [PATCH 0783/2187] virtio: fix overflow for the indirect descriptor table length The indirect descriptor table length should not bigger than u16::MAX, otherwise it will cause overflow when calling get_desc_num(). Signed-off-by: Yan Wang --- virtio/src/virtqueue/split.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 0ab3cd52e..8840b8829 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -286,7 +286,10 @@ impl SplitVringDesc { /// Return true if the indirect descriptor is valid. /// The len can be divided evenly by the size of descriptor and can not be zero. fn is_valid_indirect_desc(&self) -> bool { - if self.len == 0 || u64::from(self.len) % DESCRIPTOR_LEN != 0 { + if self.len == 0 + || u64::from(self.len) % DESCRIPTOR_LEN != 0 + || u64::from(self.len) / DESCRIPTOR_LEN > u16::MAX as u64 + { error!("The indirect descriptor is invalid, len: {}", self.len); return false; } -- Gitee From 8c4c4c9e13a33e0c74c1a89dd95b499f8e3b918c Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 12 Jan 2023 10:20:06 +0800 Subject: [PATCH 0784/2187] tests: Add module test framework Signed-off-by: Gan Qixin --- Cargo.toml | 1 + address_space/src/address_space.rs | 35 +++ cpu/src/lib.rs | 18 +- machine/src/standard_vm/aarch64/mod.rs | 3 +- machine/src/standard_vm/x86_64/mod.rs | 3 +- machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 7 + machine_manager/src/config/mod.rs | 7 +- machine_manager/src/lib.rs | 1 + machine_manager/src/machine.rs | 3 + machine_manager/src/socket.rs | 14 ++ machine_manager/src/test_server.rs | 236 +++++++++++++++++++ pci/src/msix.rs | 15 +- src/main.rs | 25 ++ tests/mod_test/Cargo.toml | 11 + tests/mod_test/src/lib.rs | 14 ++ tests/mod_test/src/libtest.rs | 306 +++++++++++++++++++++++++ tests/mod_test/src/utils.rs | 45 ++++ util/src/lib.rs | 1 + util/src/loop_context.rs | 63 +++-- util/src/test_helper.rs | 127 ++++++++++ 21 files changed, 910 insertions(+), 26 deletions(-) create mode 100644 machine_manager/src/test_server.rs create mode 100644 tests/mod_test/Cargo.toml create mode 100644 tests/mod_test/src/lib.rs create mode 100644 tests/mod_test/src/libtest.rs create mode 100644 tests/mod_test/src/utils.rs create mode 100644 util/src/test_helper.rs diff --git a/Cargo.toml b/Cargo.toml index 18d3cf187..d336e2c7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ "vhost_user_fs", "ozone", "vfio", + "tests/mod_test", ] [[bin]] diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 8d7472e7c..adbb4be5d 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -19,6 +19,7 @@ use std::sync::{Arc, Mutex}; use migration::{migration::Migratable, MigrationManager}; use util::byte_code::ByteCode; +use util::test_helper::is_test_enabled; use crate::{ AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, @@ -473,6 +474,40 @@ impl AddressSpace { let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let offset_in_region = fr.offset_in_region + offset; + + if is_test_enabled() { + for evtfd in self.ioeventfds.lock().unwrap().iter() { + if addr != evtfd.addr_range.base || count != evtfd.addr_range.size { + continue; + } + if !evtfd.data_match { + evtfd.fd.write(1).unwrap(); + return Ok(()); + } + + let mut buf = Vec::new(); + src.read_to_end(&mut buf).unwrap(); + + if buf.len() <= 8 { + let data = u64::from_bytes(buf.as_slice()).unwrap(); + if *data == evtfd.data { + evtfd.fd.write(1).unwrap(); + return Ok(()); + } + } + + return fr.owner + .write(&mut buf.as_slice(), region_base, offset_in_region, count) + .with_context(|| + format!( + "Failed to write region, region base 0x{:X}, offset in region 0x{:X}, size 0x{:X}", + region_base.raw_value(), + offset_in_region, + count + )); + } + } + fr.owner .write(src, region_base, offset_in_region, count) .with_context(|| diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 4c5bfe66c..2745e93d1 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -74,6 +74,7 @@ use log::{error, info, warn}; use machine_manager::event; use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; +use util::test_helper::is_test_enabled; use vmm_sys_util::signal::{register_signal_handler, Killable}; // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal @@ -705,14 +706,19 @@ impl CPUThreadWorker { info!("vcpu{} start running", self.thread_cpu.id); while let Ok(true) = self.ready_for_running() { #[cfg(not(test))] - if !self - .thread_cpu - .kvm_vcpu_exec() - .with_context(|| format!("VCPU {}/KVM emulate error!", self.thread_cpu.id()))? { - break; + if is_test_enabled() { + thread::sleep(Duration::from_millis(5)); + continue; + } + if !self + .thread_cpu + .kvm_vcpu_exec() + .with_context(|| format!("VCPU {}/KVM emulate error!", self.thread_cpu.id()))? + { + break; + } } - #[cfg(test)] { thread::sleep(Duration::from_millis(5)); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index ebe5e1f90..f88c0613f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -53,7 +53,7 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, - MachineLifecycle, MigrateInterface, + MachineLifecycle, MachineTestInterface, MigrateInterface, }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; @@ -1033,6 +1033,7 @@ impl MigrateInterface for StdMachine { impl MachineInterface for StdMachine {} impl MachineExternalInterface for StdMachine {} +impl MachineTestInterface for StdMachine {} impl EventLoopManager for StdMachine { fn loop_should_exit(&self) -> bool { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 45eaad8bb..663e69496 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -44,7 +44,7 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, - MachineLifecycle, MigrateInterface, + MachineLifecycle, MachineTestInterface, MigrateInterface, }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; @@ -900,6 +900,7 @@ impl MigrateInterface for StdMachine { impl MachineInterface for StdMachine {} impl MachineExternalInterface for StdMachine {} +impl MachineTestInterface for StdMachine {} impl EventLoopManager for StdMachine { fn loop_should_exit(&self) -> bool { diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index f68de2df7..967dfa54f 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -13,6 +13,7 @@ log = "0.4" libc = "0.2" serde_json = "1.0" vmm-sys-util = "0.11.0" +hex = "0.4.3" serde = { version = "1.0", features = ["derive"] } strum = "0.20" strum_macros = "0.20" diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index a1db6680d..c2407a2c8 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -186,6 +186,13 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("set QMP's unix socket path") .takes_value(true) ) + .arg( + Arg::with_name("mod-test") + .long("mod-test") + .value_name("unix:socket_path") + .help("set module test's unixsocket path") + .takes_value(true) + ) .arg( Arg::with_name("drive") .multiple(true) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 19e00b4d5..e63a1daaa 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -70,6 +70,7 @@ use util::align::is_aligned; use util::device_tree::{self, FdtBuilder}; use util::{ file::{get_file_alignment, open_file}, + test_helper::is_test_enabled, trace::enable_trace_events, AsAny, }; @@ -135,7 +136,11 @@ impl VmConfig { bail!("kernel file is required for microvm machine type, which is not provided"); } - if self.boot_source.initrd.is_none() && self.drives.is_empty() && self.chardev.is_empty() { + if self.boot_source.initrd.is_none() + && self.drives.is_empty() + && self.chardev.is_empty() + && !is_test_enabled() + { bail!("Before Vm start, set a initrd or drive_file or vhost-user blk as rootfs"); } diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index acce9e88a..e873be623 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -31,3 +31,4 @@ pub mod signal_handler; pub mod socket; pub mod temp_cleaner; pub use error::MachineManagerError; +pub mod test_server; diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 0d9080595..62583e25c 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -447,5 +447,8 @@ pub trait MachineInterface: MachineLifecycle + MachineAddressInterface {} /// Machine interface which is exposed to outer hypervisor. pub trait MachineExternalInterface: MachineLifecycle + DeviceInterface + MigrateInterface {} +/// Machine interface which is exposed to test server. +pub trait MachineTestInterface: MachineAddressInterface {} + pub static PTY_PATH: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); pub static IOTHREADS: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index efc17fac2..bd85ac2f9 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -565,6 +565,20 @@ impl SocketHandler { } } + pub fn get_line(&mut self) -> Result> { + self.buffer.clear(); + self.stream.clear(); + self.stream.read_fd().unwrap(); + self.stream.get_buf_string().map(|buffer| { + self.buffer = buffer; + if self.stream.pos == 0 { + None + } else { + Some(self.buffer.clone()) + } + }) + } + /// Parse the bytes received by `SocketHandler`. /// /// # Notes diff --git a/machine_manager/src/test_server.rs b/machine_manager/src/test_server.rs new file mode 100644 index 000000000..cb957d37c --- /dev/null +++ b/machine_manager/src/test_server.rs @@ -0,0 +1,236 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::event_loop::EventLoop; +use crate::machine::{MachineTestInterface, IOTHREADS}; +use crate::socket::SocketHandler; +use hex::FromHexError; +use std::os::unix::io::RawFd; +use std::os::unix::net::UnixStream; +use std::os::unix::prelude::AsRawFd; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; +use util::test_helper::{get_test_clock, has_msix_msg, set_test_clock}; +use vmm_sys_util::epoll::EventSet; + +pub struct TestSock { + stream: UnixStream, + controller: Arc>, +} + +impl TestSock { + pub fn new(path: &str, controller: Arc>) -> Self { + let stream = match UnixStream::connect(path) { + Ok(s) => s, + Err(e) => { + panic!("Failed to connect test socket: {}", e); + } + }; + TestSock { stream, controller } + } + + pub fn get_stream_fd(&self) -> RawFd { + self.stream.as_raw_fd() + } +} + +impl EventNotifierHelper for TestSock { + fn internal_notifiers(socket: Arc>) -> Vec { + let socket_clone = socket.clone(); + let handler: Rc = Rc::new(move |_, _| { + let locked_socket = socket_clone.lock().unwrap(); + handle_test_cmd(locked_socket.get_stream_fd(), &locked_socket.controller); + None + }); + + let mut notifiers = Vec::new(); + let handlers = vec![handler]; + notifiers.push(EventNotifier::new( + NotifierOperation::AddExclusion, + socket.lock().unwrap().get_stream_fd(), + None, + EventSet::IN, + handlers, + )); + notifiers + } +} + +fn get_min_timeout() -> i64 { + let mut min_timeout = EventLoop::get_ctx(None).unwrap().timers_min_timeout_ns(); + + for thread in IOTHREADS.lock().unwrap().iter() { + let timeout = EventLoop::get_ctx(Some(&thread.id)) + .unwrap() + .timers_min_timeout_ns(); + if timeout >= 0 && (timeout < min_timeout || min_timeout < 0) { + min_timeout = timeout; + } + } + min_timeout +} + +fn update_clock(target: u64) { + let mut current = get_test_clock(); + while current < target { + let timeout = get_min_timeout(); + let mut step = target.checked_sub(current).unwrap(); + if timeout != -1 && step > timeout as u64 { + step = timeout as u64; + } + + set_test_clock(current.checked_add(step).unwrap()); + EventLoop::get_ctx(None).unwrap().run_timers(); + for thread in IOTHREADS.lock().unwrap().iter() { + EventLoop::get_ctx(Some(&thread.id)).unwrap().run_timers(); + } + + current = get_test_clock(); + } +} + +fn handle_test_cmd(stream_fd: RawFd, controller: &Arc>) { + let mut handler = SocketHandler::new(stream_fd); + let msg = handler.get_line().unwrap().unwrap(); + + let cmd: Vec<&str> = msg.split(' ').collect(); + assert!(!cmd.is_empty()); + match cmd[0] { + "read" => { + assert!(cmd.len() == 3); + let addr = u64::from_str_radix(cmd[1].trim_start_matches("0x"), 16).unwrap(); + let size = usize::from_str_radix(cmd[2].trim_start_matches("0x"), 16).unwrap(); + let mut data = vec![0_u8; size]; + + controller + .lock() + .unwrap() + .mmio_read(addr, data.as_mut_slice()); + handler + .send_str(format!("OK 0x{}", hex::encode(data).as_str()).as_str()) + .unwrap(); + } + "readb" | "readw" | "readl" | "readq" => { + assert!(cmd.len() == 2); + let addr = u64::from_str_radix(cmd[1].trim_start_matches("0x"), 16).unwrap(); + let size = match cmd[0] { + "readb" => 1, + "readw" => 2, + "readl" => 4, + "readq" => 8, + _ => unreachable!(), + }; + let mut data = vec![0_u8; 8]; + + controller + .lock() + .unwrap() + .mmio_read(addr, data[..size].as_mut()); + data.reverse(); + + handler + .send_str(format!("OK 0x{}", hex::encode(data).as_str()).as_str()) + .unwrap(); + } + "write" => { + assert!(cmd.len() == 4); + let addr = u64::from_str_radix(cmd[1].trim_start_matches("0x"), 16).unwrap(); + let size = usize::from_str_radix(cmd[2].trim_start_matches("0x"), 16).unwrap(); + let data_str = cmd[3].trim_start_matches("0x"); + let data = match hex::decode(data_str) { + Ok(d) => d, + Err(FromHexError::OddLength) => hex::decode(format!("0{}", data_str)).unwrap(), + Err(e) => panic!("Unable to decode {} to hex: {}", data_str, e), + }; + assert!(data.len() == size); + + controller.lock().unwrap().mmio_write(addr, data.as_slice()); + handler.send_str("OK").unwrap(); + } + "writeb" | "writew" | "writel" | "writeq" => { + assert!(cmd.len() == 3); + let addr = u64::from_str_radix(cmd[1].trim_start_matches("0x"), 16).unwrap(); + let input_str = cmd[2].trim_start_matches("0x"); + let input = match hex::decode(input_str) { + Ok(i) => i, + Err(FromHexError::OddLength) => hex::decode(format!("0{}", input_str)).unwrap(), + Err(e) => panic!("Unable to decode {} to hex: {}", input_str, e), + }; + let size = match cmd[0] { + "writeb" => 1, + "writew" => 2, + "writel" => 4, + "writeq" => 8, + _ => unreachable!(), + }; + let mut data = vec![0_u8; size]; + data[size - input.len()..].copy_from_slice(input.as_slice()); + data.reverse(); + + controller.lock().unwrap().mmio_write(addr, data.as_slice()); + handler.send_str("OK").unwrap(); + } + "memset" => { + assert!(cmd.len() == 4); + let addr = u64::from_str_radix(cmd[1].trim_start_matches("0x"), 16).unwrap(); + let size = usize::from_str_radix(cmd[2].trim_start_matches("0x"), 16).unwrap(); + let pat = hex::decode(cmd[3].trim_start_matches("0x")).unwrap(); + let pat_size = pat.len(); + let mut data = vec![0_u8; size]; + for index in 0..data.len() { + data[index] = pat[index % pat_size]; + } + + controller.lock().unwrap().mmio_write(addr, data.as_slice()); + handler.send_str("OK").unwrap(); + } + "clock_step" => { + assert!(cmd.len() < 3); + let value = match cmd.len() { + 1 => get_min_timeout(), + 2 => cmd[1].parse::().unwrap(), + _ => panic!("Too many arguments in clock_step command"), + }; + let ns: u64 = std::cmp::max(value, 0) as u64; + + update_clock(get_test_clock().checked_add(ns).unwrap()); + handler + .send_str(format!("OK {}", get_test_clock()).as_str()) + .unwrap(); + } + "clock_set" => { + assert!(cmd.len() == 2); + let value = cmd[1].parse::().unwrap(); + let ns: u64 = std::cmp::max(value, 0) as u64; + + update_clock(ns); + handler + .send_str(format!("OK {}", get_test_clock()).as_str()) + .unwrap(); + } + "query_msix" => { + assert!(cmd.len() == 3); + let addr = cmd[1].parse::().unwrap(); + let data = cmd[2].parse::().unwrap(); + match has_msix_msg(addr, data) { + true => handler.send_str("OK TRUE".to_string().as_str()).unwrap(), + false => handler.send_str("OK FALSE".to_string().as_str()).unwrap(), + } + } + _ => { + handler + .send_str(format!("Unsupported command: {}", cmd[0]).as_str()) + .unwrap(); + } + }; +} diff --git a/pci/src/msix.rs b/pci/src/msix.rs index b62688c06..e5adfd912 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -22,7 +22,11 @@ use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::{byte_code::ByteCode, num_ops::round_up}; +use util::{ + byte_code::ByteCode, + num_ops::round_up, + test_helper::{add_msix_msg, is_test_enabled}, +}; use vmm_sys_util::eventfd::EventFd; use crate::config::{CapId, PciConfig, RegionType, MINMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; @@ -597,6 +601,15 @@ fn send_msix(msg: Message, dev_id: u16) { devid: dev_id as u32, pad: [0; 12], }; + + if is_test_enabled() { + let data = msg.data; + let mut addr: u64 = msg.address_hi as u64; + addr = (addr << 32) + msg.address_lo as u64; + add_msix_msg(addr, data); + return; + } + if let Err(e) = KVM_FDS.load().vm_fd.as_ref().unwrap().signal_msi(kvm_msi) { error!("Send msix error: {:?}", e); }; diff --git a/src/main.rs b/src/main.rs index 486a57b1d..8ca8ed422 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,8 +26,10 @@ use machine_manager::{ signal_handler::{exit_with_code, register_kill_signal, VM_EXIT_GENE_ERR}, socket::Socket, temp_cleaner::TempCleaner, + test_server::TestSock, }; use util::loop_context::EventNotifierHelper; +use util::test_helper::{is_test_enabled, set_test_enabled}; use util::{arg_parser, daemonize::daemonize, logger, set_termi_canon_mode}; use thiserror::Error; @@ -88,6 +90,10 @@ fn main() { fn run() -> Result<()> { let cmd_args = create_args_parser().get_matches()?; + if cmd_args.is_present("mod-test") { + set_test_enabled(); + } + if let Some(logfile_path) = cmd_args.value_of("display log") { if logfile_path.is_empty() { logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) @@ -173,6 +179,9 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let mut sockets = Vec::new(); let vm: Arc> = match vm_config.machine_config.mach_type { MachineType::MicroVm => { + if is_test_enabled() { + panic!("module test framework does not support microvm.") + } let vm = Arc::new(Mutex::new( LightMachine::new(vm_config).with_context(|| "Failed to init MicroVM")?, )); @@ -192,16 +201,32 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res .with_context(|| "Failed to realize standard VM.")?; EventLoop::set_manager(vm.clone(), None); + if is_test_enabled() { + let sock_path = cmd_args.value_of("mod-test"); + let test_sock = Some(TestSock::new(sock_path.unwrap().as_str(), vm.clone())); + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new( + test_sock.unwrap(), + ))), + None, + ) + .with_context(|| "Failed to add test socket to MainLoop")?; + } + for listener in listeners { sockets.push(Socket::from_unix_listener(listener, Some(vm.clone()))); } vm } MachineType::None => { + if is_test_enabled() { + panic!("please specify machine type.") + } let vm = Arc::new(Mutex::new( StdMachine::new(vm_config).with_context(|| "Failed to init NoneVM")?, )); EventLoop::set_manager(vm.clone(), None); + for listener in listeners { sockets.push(Socket::from_unix_listener(listener, Some(vm.clone()))); } diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml new file mode 100644 index 000000000..35c1c9b23 --- /dev/null +++ b/tests/mod_test/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "mod_test" +version = "2.2.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" + +[dependencies] +rand = "0.8.5" +hex = "0.4.3" +serde_json = "1.0" \ No newline at end of file diff --git a/tests/mod_test/src/lib.rs b/tests/mod_test/src/lib.rs new file mode 100644 index 000000000..0f91c9cc6 --- /dev/null +++ b/tests/mod_test/src/lib.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod libtest; +pub mod utils; diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs new file mode 100644 index 000000000..abfec4827 --- /dev/null +++ b/tests/mod_test/src/libtest.rs @@ -0,0 +1,306 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use serde_json::Value; +use std::io::Read; +use std::io::Write; +use std::os::unix::net::{UnixListener, UnixStream}; +use std::path::Path; +use std::process::{Child, Command}; +use std::thread::sleep; +use std::time::Duration; +use std::time::Instant; +use std::{env, fs}; + +use hex; + +use crate::utils::get_tmp_dir; + +pub struct StreamHandler { + stream: UnixStream, +} + +impl StreamHandler { + fn new(stream: UnixStream) -> Self { + StreamHandler { stream } + } + + fn write_line(&self, cmd: &str) { + self.stream + .try_clone() + .unwrap() + .write_all(cmd.as_bytes()) + .unwrap(); + } + + fn read_line(&self, timeout: Duration) -> String { + let start = Instant::now(); + let mut resp = String::new(); + let mut stream = self.stream.try_clone().unwrap(); + stream.set_nonblocking(true).unwrap(); + + let pos = loop { + if start + timeout < Instant::now() || resp.find('\n').is_some() { + break resp.find('\n'); + } + + let mut buff = [0u8; 1024]; + if let Ok(size) = stream.read(&mut buff) { + resp.push_str(String::from_utf8(buff[..size].to_vec()).unwrap().as_str()); + } + }; + + let (line, _) = resp.split_at(pos.unwrap()); + line.trim().to_string() + } +} + +pub struct TestState { + process: Child, + test_sock: StreamHandler, + qmp_sock: StreamHandler, + pub resource_path: String, +} + +impl Drop for TestState { + fn drop(&mut self) { + if let Ok(None) = self.process.try_wait() { + self.process.kill().unwrap() + } + + if Path::new(&self.resource_path).exists() { + fs::remove_dir_all(&self.resource_path).unwrap(); + } + } +} + +impl TestState { + pub fn new( + process: Child, + test_sock: StreamHandler, + qmp_sock: StreamHandler, + resource_path: String, + ) -> Self { + let ts = Self { + process, + test_sock, + qmp_sock, + resource_path, + }; + ts.check_qmp_greet(); + ts + } + + pub fn stop(&mut self) { + self.qmp("{\"execute\": \"quit\"}"); + self.process.wait().unwrap(); + } + + fn check_qmp_greet(&self) { + let timeout = Duration::from_secs(10); + let resp: Value = + serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap(); + assert!(resp.get("QMP").is_some()); + } + + pub fn qmp(&self, cmd: &str) -> Value { + let timeout = Duration::from_secs(10); + self.qmp_sock.write_line(cmd); + serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap() + } + + fn send_test_cmd(&self, cmd: &str) -> String { + let timeout = Duration::from_secs(10); + self.test_sock.write_line(cmd); + self.test_sock.read_line(timeout) + } + + fn send_read_cmd(&self, cmd: &str) -> u64 { + let buf = self.send_test_cmd(cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + assert_eq!(resp.len(), 2); + match resp[0] { + "OK" => u64::from_str_radix(resp[1].replace("0x", "").as_str(), 16).unwrap(), + _ => panic!("Failed to execute {}.", cmd), + } + } + + fn send_write_cmd(&self, cmd: &str) { + let buf = self.send_test_cmd(cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + match resp[0] { + "OK" => (), + _ => panic!("Failed to execute {}", cmd), + } + } + + fn send_clock_cmd(&self, cmd: &str) -> u64 { + let buf = self.send_test_cmd(cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + assert_eq!(resp.len(), 2); + match resp[0] { + "OK" => resp[1].parse::().unwrap(), + _ => panic!("Failed to execute {}.", cmd), + } + } + + pub fn readb(&self, addr: u64) -> u8 { + let cmd = format!("readb 0x{:x}", addr); + self.send_read_cmd(&cmd) as u8 + } + + pub fn readw(&self, addr: u64) -> u16 { + let cmd = format!("readw 0x{:x}", addr); + self.send_read_cmd(&cmd) as u16 + } + + pub fn readl(&self, addr: u64) -> u32 { + let cmd = format!("readl 0x{:x}", addr); + self.send_read_cmd(&cmd) as u32 + } + + pub fn readq(&self, addr: u64) -> u64 { + let cmd = format!("readq 0x{:x}", addr); + self.send_read_cmd(&cmd) as u64 + } + + pub fn memread(&self, addr: u64, size: u64) -> Vec { + let cmd = format!("read 0x{:x} 0x{:x}", addr, size); + let buf = self.send_test_cmd(&cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + assert_eq!(resp.len(), 2); + match resp[0] { + "OK" => { + let data = resp[1].replace("0x", ""); + hex::decode(data).unwrap() + } + _ => panic!("Failed to execute {}", cmd), + } + } + + pub fn writeb(&self, addr: u64, data: u8) { + let cmd = format!("writeb 0x{:x} 0x{:x}", addr, data); + self.send_write_cmd(&cmd); + } + + pub fn writew(&self, addr: u64, data: u16) { + let cmd = format!("writew 0x{:x} 0x{:x}", addr, data); + self.send_write_cmd(&cmd); + } + + pub fn writel(&self, addr: u64, data: u32) { + let cmd = format!("writel 0x{:x} 0x{:x}", addr, data); + self.send_write_cmd(&cmd); + } + + pub fn writeq(&self, addr: u64, data: u64) { + let cmd = format!("writeq 0x{:x} 0x{:x}", addr, data); + self.send_write_cmd(&cmd); + } + + pub fn memwrite(&self, addr: u64, data: &[u8], size: u64) { + let cmd = format!("write 0x{:x} 0x{:x} 0x{}", addr, size, hex::encode(data)); + let buf = self.send_test_cmd(&cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + match resp[0] { + "OK" => (), + _ => panic!("Failed to execute {}", cmd), + } + } + + pub fn memset(&self, addr: u64, size: u64, pat: &[u8]) { + let cmd = format!("memset 0x{:x} 0x{:x} 0x{}", addr, size, hex::encode(pat)); + let buf = self.send_test_cmd(&cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + match resp[0] { + "OK" => (), + _ => panic!("Failed to execute {}", cmd), + } + } + + pub fn clock_step(&self) -> u64 { + let cmd = "clock_step".to_string(); + self.send_clock_cmd(&cmd) + } + + pub fn clock_step_ns(&self, ns: u64) -> u64 { + let cmd = format!("clock_step {}", ns); + self.send_clock_cmd(&cmd) + } + + pub fn clock_set(&self, tgt: u64) -> u64 { + let cmd = format!("clock_set {}", tgt); + self.send_clock_cmd(&cmd) + } + + pub fn query_msix(&self, addr: u64, data: u32) -> bool { + let cmd = format!("query_msix {} {}", addr, data); + let buf = self.send_test_cmd(&cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + assert_eq!(resp.len(), 2); + match resp[0] { + "OK" => match resp[1] { + "TRUE" => true, + "FALSE" => false, + _ => panic!("Failed to execute {}.", cmd), + }, + _ => panic!("Failed to execute {}.", cmd), + } + } +} + +fn init_socket(path: &str) -> UnixListener { + let socket = Path::new(path); + if socket.exists() { + fs::remove_file(socket).unwrap(); + } + UnixListener::bind(socket).unwrap() +} + +fn connect_socket(path: &str) -> UnixStream { + UnixStream::connect(path).unwrap() +} + +fn socket_accept_wait(listener: UnixListener, timeout: Duration) -> Option { + let start = Instant::now(); + listener.set_nonblocking(true).unwrap(); + + while start + timeout > Instant::now() { + match listener.accept() { + Ok((stream, _addr)) => return Some(stream), + Err(_) => sleep(Duration::from_millis(100)), + }; + } + None +} + +pub fn test_init(extra_arg: Vec<&str>) -> TestState { + let binary_path = env::var("STRATOVIRT_BINARY").unwrap(); + let tmp_dir = get_tmp_dir(); + let test_socket = format!("{}/test.socket", tmp_dir); + let qmp_socket = format!("{}/qmp.socket", tmp_dir); + + let listener = init_socket(&test_socket); + + let child = Command::new(binary_path) + .args(["-qmp", &format!("unix:{},server,nowait", qmp_socket)]) + .args(["-mod-test", &test_socket]) + .args(extra_arg) + .spawn() + .unwrap(); + + let test_sock = + StreamHandler::new(socket_accept_wait(listener, Duration::from_secs(10)).unwrap()); + let qmp_sock = StreamHandler::new(connect_socket(&qmp_socket)); + + TestState::new(child, test_sock, qmp_sock, tmp_dir) +} diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs new file mode 100644 index 000000000..b85d8b010 --- /dev/null +++ b/tests/mod_test/src/utils.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; +use std::fs; + +pub fn get_rand_str(size: usize) -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(size) + .map(char::from) + .collect() +} + +pub fn create_dir(dir_path: &str) { + fs::create_dir(dir_path).unwrap(); +} + +pub fn get_tmp_dir() -> String { + let dir_name = format!("/tmp/test-{}", get_rand_str(10)); + create_dir(&dir_name); + dir_name +} + +pub fn read_le_u16(input: &mut &[u8]) -> u16 { + let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + *input = rest; + u16::from_le_bytes(int_bytes.try_into().unwrap()) +} + +pub fn read_le_u32(input: &mut &[u8]) -> u32 { + let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + *input = rest; + u32::from_le_bytes(int_bytes.try_into().unwrap()) +} diff --git a/util/src/lib.rs b/util/src/lib.rs index d7edcf853..5cbb3c4da 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -34,6 +34,7 @@ pub mod reader; pub mod seccomp; pub mod syscall; pub mod tap; +pub mod test_helper; pub mod time; pub mod trace; pub mod unix; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 7954ebc85..5734a99a0 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -22,6 +22,7 @@ use log::warn; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; +use crate::test_helper::{get_test_time, is_test_enabled}; use crate::UtilError; use anyhow::{anyhow, Context, Result}; use std::fmt; @@ -147,6 +148,14 @@ pub fn gen_delete_notifiers(fds: &[RawFd]) -> Vec { notifiers } +fn get_current_time() -> Instant { + if is_test_enabled() { + get_test_time() + } else { + Instant::now() + } +} + /// EventLoop manager, advise continue running or stop running pub trait EventLoopManager: Send + Sync { fn loop_should_exit(&self) -> bool; @@ -171,7 +180,7 @@ impl Timer { pub fn new(func: Box, nsec: u64) -> Self { Timer { func, - expire_time: Instant::now() + Duration::new(0, nsec as u32), + expire_time: get_current_time() + Duration::new(0, nsec as u32), } } } @@ -461,7 +470,7 @@ impl EventLoopContext { } } - self.epoll_wait_manager(self.timers_min_timeout()) + self.epoll_wait_manager(self.timers_min_timeout_ms()) } pub fn iothread_run(&mut self) -> Result { @@ -472,7 +481,7 @@ impl EventLoopContext { } } - let timeout = self.timers_min_timeout(); + let timeout = self.timers_min_timeout_ms(); if timeout == -1 { for _i in 0..AIO_PRFETCH_CYCLE_TIME { for notifer in self.events.read().unwrap().values() { @@ -514,31 +523,53 @@ impl EventLoopContext { self.kick(); } - /// Get the expire_time of the soonest Timer, and then translate it to timeout. - fn timers_min_timeout(&self) -> i32 { + /// Get the expire_time of the soonest Timer, and then translate it to duration. + fn timers_min_duration(&self) -> Option { // The kick event happens before re-evaluate can be ignored. self.kicked.store(false, Ordering::SeqCst); let timers = self.timers.lock().unwrap(); if timers.is_empty() { - return -1; + return None; } - let now = Instant::now(); - if timers[0].expire_time <= now { - return 0; + Some( + timers[0] + .expire_time + .saturating_duration_since(get_current_time()), + ) + } + + fn timers_min_timeout_ms(&self) -> i32 { + match self.timers_min_duration() { + Some(d) => { + let timeout = d.as_millis(); + if timeout >= i32::MAX as u128 { + i32::MAX - 1 + } else { + timeout as i32 + } + } + None => -1, } + } - let timeout = (timers[0].expire_time - now).as_millis(); - if timeout >= i32::MAX as u128 { - i32::MAX - 1 - } else { - timeout as i32 + pub fn timers_min_timeout_ns(&self) -> i64 { + match self.timers_min_duration() { + Some(d) => { + let timeout = d.as_nanos(); + if timeout >= i64::MAX as u128 { + i64::MAX + } else { + timeout as i64 + } + } + None => -1, } } /// Call function of the timers which have already expired. - fn run_timers(&mut self) { - let now = Instant::now(); + pub fn run_timers(&mut self) { + let now = get_current_time(); let mut expired_nr = 0; let mut timers = self.timers.lock().unwrap(); diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs new file mode 100644 index 000000000..cc3c9e6ef --- /dev/null +++ b/util/src/test_helper.rs @@ -0,0 +1,127 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use once_cell::sync::{Lazy, OnceCell}; +use std::sync::{Arc, Mutex, RwLock}; +use std::time::{Duration, Instant}; + +#[derive(Default, Clone, Copy)] +struct MsixMsg { + addr: u64, + data: u32, +} + +impl MsixMsg { + fn new(addr: u64, data: u32) -> Self { + MsixMsg { addr, data } + } +} + +static TEST_ENABLED: OnceCell = OnceCell::new(); +static TEST_BASE_TIME: OnceCell = OnceCell::new(); +static mut TEST_CLOCK: Option>> = None; +static TEST_MSIX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); + +pub fn set_test_enabled() { + #[cfg(target_arch = "x86_64")] + panic!("module test framework does not support x86_64."); + #[cfg(target_arch = "aarch64")] + { + if let Err(_e) = TEST_ENABLED.set(true) { + panic!("Failed to enable test server."); + } + if let Err(_e) = TEST_BASE_TIME.set(Instant::now()) { + panic!("Failed to initialize clock"); + } + unsafe { + if TEST_CLOCK.is_none() { + TEST_CLOCK = Some(Arc::new(RwLock::new(0))); + } + } + } +} + +pub fn is_test_enabled() -> bool { + *TEST_ENABLED.get_or_init(|| false) +} + +pub fn set_test_clock(value: u64) { + unsafe { + if TEST_CLOCK.is_none() { + panic!("TEST_CLOCK has not been initialized."); + } + if value <= get_test_clock() { + return; + } + + let mut test_clock = TEST_CLOCK.as_ref().unwrap().write().unwrap(); + *test_clock = value; + } +} + +pub fn get_test_clock() -> u64 { + unsafe { + if TEST_CLOCK.is_none() { + panic!("TEST_CLOCK has not been initialized."); + } + + *TEST_CLOCK.as_ref().unwrap().read().unwrap() + } +} + +pub fn get_test_time() -> Instant { + unsafe { + if TEST_CLOCK.is_none() { + panic!("TEST_CLOCK has not been initialized."); + } + + TEST_BASE_TIME + .get() + .unwrap() + .checked_add(Duration::from_nanos(get_test_clock())) + .unwrap() + } +} + +pub fn add_msix_msg(addr: u64, data: u32) { + let new_msg = MsixMsg::new(addr, data); + let mut msix_list_lock = TEST_MSIX_LIST.lock().unwrap(); + + for msg in msix_list_lock.iter() { + if new_msg.addr == msg.addr && new_msg.data == msg.data { + return; + } + } + + msix_list_lock.push(new_msg); +} + +pub fn has_msix_msg(addr: u64, data: u32) -> bool { + let target_msg = MsixMsg::new(addr, data); + let mut target_index: Option = None; + let mut msix_list_lock = TEST_MSIX_LIST.lock().unwrap(); + + for (index, msg) in msix_list_lock.iter().enumerate() { + if target_msg.addr == msg.addr && target_msg.data == msg.data { + target_index = Some(index); + break; + } + } + + match target_index { + Some(i) => { + msix_list_lock.remove(i); + true + } + None => false, + } +} -- Gitee From df6e8502dc42c417a2a6e5b434faba044d1d87b4 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 22 Feb 2023 20:05:56 +0800 Subject: [PATCH 0785/2187] pl031_test: Adapt for test framework Signed-off-by: Gan Qixin --- tests/mod_test/Cargo.toml | 3 ++- tests/mod_test/tests/pl031_test.rs | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 35c1c9b23..340cc303c 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -8,4 +8,5 @@ license = "Mulan PSL v2" [dependencies] rand = "0.8.5" hex = "0.4.3" -serde_json = "1.0" \ No newline at end of file +serde_json = "1.0" +devices = { path = "../../devices" } \ No newline at end of file diff --git a/tests/mod_test/tests/pl031_test.rs b/tests/mod_test/tests/pl031_test.rs index 42de84b81..dfd52bbc8 100644 --- a/tests/mod_test/tests/pl031_test.rs +++ b/tests/mod_test/tests/pl031_test.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use devices::legacy::{RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; -use mod_test::libtest::{test_init, test_stop, TestState}; +use mod_test::libtest::{test_init, TestState}; use rand::{thread_rng, Rng}; use std::thread::sleep; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -42,6 +42,7 @@ fn pl031_write_reg(ts: &TestState, reg: u64, val: u32) { } #[test] +#[cfg(target_arch = "aarch64")] fn check_time() { let mut ts = test_init(Vec::new()); @@ -59,10 +60,11 @@ fn check_time() { assert!((time4 - time3) <= 1); assert!((wall_time - time4) <= 1); - test_stop(&mut ts); + ts.stop(); } #[test] +#[cfg(target_arch = "aarch64")] fn set_time() { let mut ts = test_init(Vec::new()); let time1 = pl031_read_time(&ts); @@ -75,28 +77,32 @@ fn set_time() { assert!((time2 - time1) >= time_lapse); assert!((time2 - time1) <= time_lapse + 1); - test_stop(&mut ts); + + ts.stop(); } #[test] +#[cfg(target_arch = "aarch64")] fn rtc_enable() { let mut ts = test_init(Vec::new()); assert_eq!(pl031_read_reg(&ts, RTC_CR), 1); - test_stop(&mut ts); + ts.stop(); } #[test] +#[cfg(target_arch = "aarch64")] fn set_mask() { let mut ts = test_init(Vec::new()); pl031_write_reg(&ts, RTC_IMSC, 1); assert_eq!(pl031_read_reg(&ts, RTC_IMSC), 1); - test_stop(&mut ts); + ts.stop(); } #[test] +#[cfg(target_arch = "aarch64")] fn reg_fuzz() { let mut ts = test_init(Vec::new()); let mut rng = thread_rng(); @@ -108,5 +114,5 @@ fn reg_fuzz() { pl031_write_reg(&ts, reg, val); } - test_stop(&mut ts); + ts.stop(); } -- Gitee From 1bf094a3341717bd4b1ea152103f9dcb88583a94 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 22 Feb 2023 22:29:23 +0800 Subject: [PATCH 0786/2187] tests: Create the libdriver dir and add some general methods Signed-off-by: Gan Qixin --- Cargo.lock | 71 +++++++++++ tests/mod_test/Cargo.toml | 3 +- tests/mod_test/src/lib.rs | 1 + tests/mod_test/src/libdriver/machine.rs | 50 ++++++++ tests/mod_test/src/libdriver/malloc.rs | 150 ++++++++++++++++++++++++ tests/mod_test/src/libdriver/mod.rs | 15 +++ tests/mod_test/src/libdriver/pci_bus.rs | 101 ++++++++++++++++ 7 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 tests/mod_test/src/libdriver/machine.rs create mode 100644 tests/mod_test/src/libdriver/malloc.rs create mode 100644 tests/mod_test/src/libdriver/mod.rs create mode 100644 tests/mod_test/src/libdriver/pci_bus.rs diff --git a/Cargo.lock b/Cargo.lock index 9cd864f4b..9d5143266 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -297,6 +297,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.26.2" @@ -312,6 +323,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hypervisor" version = "2.2.0" @@ -454,6 +471,7 @@ name = "machine_manager" version = "2.2.0" dependencies = [ "anyhow", + "hex", "libc", "log", "once_cell", @@ -511,6 +529,17 @@ dependencies = [ "adler", ] +[[package]] +name = "mod_test" +version = "2.2.0" +dependencies = [ + "devices", + "hex", + "rand", + "serde_json", + "util", +] + [[package]] name = "object" version = "0.29.0" @@ -600,6 +629,12 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.44" @@ -618,6 +653,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1063,6 +1128,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.83" diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 340cc303c..a70514a59 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -9,4 +9,5 @@ license = "Mulan PSL v2" rand = "0.8.5" hex = "0.4.3" serde_json = "1.0" -devices = { path = "../../devices" } \ No newline at end of file +devices = { path = "../../devices" } +util = { path = "../../util" } \ No newline at end of file diff --git a/tests/mod_test/src/lib.rs b/tests/mod_test/src/lib.rs index 0f91c9cc6..4c8516f9a 100644 --- a/tests/mod_test/src/lib.rs +++ b/tests/mod_test/src/lib.rs @@ -10,5 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod libdriver; pub mod libtest; pub mod utils; diff --git a/tests/mod_test/src/libdriver/machine.rs b/tests/mod_test/src/libdriver/machine.rs new file mode 100644 index 000000000..bada3c0ad --- /dev/null +++ b/tests/mod_test/src/libdriver/machine.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::malloc::GuestAllocator; +use super::pci_bus::TestPciBus; +use crate::libtest::TestState; +use std::cell::RefCell; +use std::rc::Rc; + +const ARM_VIRT_RAM_ADDR: u64 = 0x40000000; +const ARM_VIRT_RAM_SIZE: u64 = 0x20000000; +const ARM_PAGE_SIZE: u64 = 4096; + +pub struct TestStdMachine { + pub pci_bus: Rc>, + pub allocator: Rc>, +} + +impl TestStdMachine { + pub fn new(test_state: Rc>) -> Self { + Self { + pci_bus: Rc::new(RefCell::new(TestPciBus::new(test_state))), + allocator: Rc::new(RefCell::new(GuestAllocator::new( + ARM_VIRT_RAM_ADDR, + ARM_VIRT_RAM_SIZE, + ARM_PAGE_SIZE, + ))), + } + } + + pub fn new_bymem(test_state: Rc>, memsize: u64, page_size: u64) -> Self { + Self { + pci_bus: Rc::new(RefCell::new(TestPciBus::new(test_state))), + allocator: Rc::new(RefCell::new(GuestAllocator::new( + ARM_VIRT_RAM_ADDR, + memsize, + page_size, + ))), + } + } +} diff --git a/tests/mod_test/src/libdriver/malloc.rs b/tests/mod_test/src/libdriver/malloc.rs new file mode 100644 index 000000000..f7c63446e --- /dev/null +++ b/tests/mod_test/src/libdriver/malloc.rs @@ -0,0 +1,150 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use util::num_ops::round_up; + +#[derive(Clone, Copy)] +struct MemBlock { + start: u64, + size: u64, +} + +impl MemBlock { + fn new(start: u64, size: u64) -> Self { + MemBlock { start, size } + } + + pub fn reduce(&mut self, size: u64) { + assert!(self.size > size); + self.start += size; + self.size -= size; + } +} + +pub struct GuestAllocator { + start: u64, + end: u64, + page_size: u64, + free: Vec, + used: Vec, +} + +impl GuestAllocator { + pub fn new(start: u64, size: u64, page_size: u64) -> Self { + Self { + start, + end: start + size, + page_size, + free: vec![MemBlock::new(start, size)], + used: Vec::new(), + } + } + + fn add_free_block(&mut self, new_mb: MemBlock) { + let mut target = self.free.len(); + for (i, mb) in self.free.iter().enumerate() { + if mb.size >= new_mb.size { + target = i; + break; + } + } + self.free.insert(target, new_mb); + } + + fn add_used_block(&mut self, new_mb: MemBlock) { + let mut target = self.used.len(); + for (i, mb) in self.used.iter().enumerate() { + if mb.start >= new_mb.start { + target = i; + break; + } + } + self.used.insert(target, new_mb); + } + + fn alloc_free_block(&mut self, index: usize, size: u64) { + let start = self.free[index].start; + let used_mb = MemBlock::new(start, size); + self.add_used_block(used_mb); + if self.free[index].size == size { + self.free.remove(index); + } else { + self.free[index].reduce(size); + } + } + + fn free_used_block(&mut self, index: usize) { + let free_mb = self.used[index]; + self.add_free_block(free_mb); + self.used.remove(index); + } + + pub fn alloc(&mut self, size: u64) -> u64 { + let alloc_size = round_up(size, self.page_size).unwrap(); + + let mut addr: Option = None; + let mut index: Option = None; + for (i, mb) in self.free.iter().enumerate() { + if mb.size >= alloc_size { + addr = Some(mb.start); + index = Some(i); + break; + } + } + + self.alloc_free_block(index.unwrap(), alloc_size); + addr.unwrap() + } + + pub fn free(&mut self, addr: u64) { + assert!(self.start <= addr && addr < self.end); + let mut index: Option = None; + for (i, mb) in self.used.iter().enumerate() { + if mb.start >= addr { + index = Some(i); + break; + } + } + + if let Some(i) = index { + self.free_used_block(i); + } + } +} + +#[cfg(test)] +mod test { + use crate::malloc::GuestAllocator; + + const PAGE_SIZE_4K: u64 = 1 << 12; + const ADDRESS_BASE: u64 = 0x4000_0000; + const ADDRESS_SIZE: u64 = 0x2000_0000; + + #[test] + fn test_guest_allocator() { + let mut guest_allocator = GuestAllocator::new(ADDRESS_BASE, ADDRESS_SIZE, PAGE_SIZE_4K); + + let mut expect_addr = ADDRESS_BASE; + let mut addr = guest_allocator.alloc(4096 + 1); + assert_eq!(addr, expect_addr); + guest_allocator.free(addr); + + addr = guest_allocator.alloc(4096 * 10); + expect_addr += 4096 * 2; + assert_eq!(addr, expect_addr); + guest_allocator.free(addr); + + addr = guest_allocator.alloc(4096); + expect_addr = ADDRESS_BASE; + assert_eq!(addr, expect_addr); + } +} diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs new file mode 100644 index 000000000..6883421e1 --- /dev/null +++ b/tests/mod_test/src/libdriver/mod.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod machine; +pub mod malloc; +pub mod pci_bus; diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs new file mode 100644 index 000000000..30306ae1c --- /dev/null +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -0,0 +1,101 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::libtest::TestState; +use crate::utils::{read_le_u16, read_le_u32}; +use std::cell::RefCell; +use std::rc::Rc; + +pub trait PciBusOps { + fn memread(&self, addr: u32, buf: &mut Vec, len: usize); + fn memwrite(&self, addr: u32, buf: &[u8], len: usize); + + fn config_readb(&self, devfn: u32, offset: u8) -> u8; + fn config_readw(&self, devfn: u32, offset: u8) -> u16; + fn config_readl(&self, devfn: u32, offset: u8) -> u32; + + fn config_writeb(&self, devfn: u32, offset: u8, value: u8); + fn config_writew(&self, devfn: u32, offset: u8, value: u16); + fn config_writel(&self, devfn: u32, offset: u8, value: u32); +} + +#[allow(unused)] +pub struct TestPciBus { + pub mmio_alloc_ptr: u64, + pub mmio_limit: u64, + ecam_alloc_ptr: u64, + not_hotpluggable: bool, + pub test_state: Rc>, +} + +impl TestPciBus { + pub fn new(test_state: Rc>) -> Self { + Self { + mmio_alloc_ptr: 0x10000000, + mmio_limit: 0x2eff0000, + ecam_alloc_ptr: 0x8040000000, + not_hotpluggable: false, + test_state, + } + } + + fn get_addr(&self, devfn: u32, offset: u8) -> u64 { + self.ecam_alloc_ptr + (devfn << 12 | offset as u32) as u64 + } +} + +impl PciBusOps for TestPciBus { + fn memread(&self, addr: u32, buf: &mut Vec, len: usize) { + *buf = self.test_state.borrow().memread(addr as u64, len as u64); + } + + fn memwrite(&self, addr: u32, buf: &[u8], len: usize) { + self.test_state + .borrow() + .memwrite(addr as u64, &*buf, len as u64); + } + + fn config_readb(&self, devfn: u32, offset: u8) -> u8 { + let addr = self.ecam_alloc_ptr + (devfn << 12 | offset as u32) as u64; + self.test_state.borrow().memread(addr, 1)[0] + } + + fn config_readw(&self, devfn: u32, offset: u8) -> u16 { + let addr = self.get_addr(devfn, offset); + let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 2)[0..2]; + read_le_u16(&mut buf) + } + + fn config_readl(&self, devfn: u32, offset: u8) -> u32 { + let addr = self.get_addr(devfn, offset); + let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 4)[0..4]; + read_le_u32(&mut buf) + } + + fn config_writeb(&self, devfn: u32, offset: u8, value: u8) { + let addr = self.get_addr(devfn, offset); + let buf = value.to_le_bytes(); + self.test_state.borrow().memwrite(addr, &buf, 1); + } + + fn config_writew(&self, devfn: u32, offset: u8, value: u16) { + let addr = self.get_addr(devfn, offset); + let buf = value.to_le_bytes(); + self.test_state.borrow().memwrite(addr, &buf, 2); + } + + fn config_writel(&self, devfn: u32, offset: u8, value: u32) { + let addr = self.get_addr(devfn, offset); + let buf = value.to_le_bytes(); + self.test_state.borrow().memwrite(addr, &buf, 4); + } +} -- Gitee From 4eee58edb80ed2eb5f0623d114dd64895304d9b1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Feb 2023 00:24:25 +0800 Subject: [PATCH 0787/2187] VNC: Adjust the interaction between USB kbdMouse and VNC Adjust the interaction between kbdMouse and VNC. In this way, the input device can choose to mount itself on VNC during the initialization. Signed-off-by: Xiao Ye --- Cargo.lock | 10 +-- machine/src/lib.rs | 30 ++++---- usb/Cargo.toml | 2 + usb/src/keyboard.rs | 66 +++++++++++----- usb/src/lib.rs | 18 +---- usb/src/tablet.rs | 56 ++++++++------ vnc/Cargo.toml | 11 +-- vnc/src/input.rs | 178 ++++++++++++++++++++++++++++++++++---------- vnc/src/lib.rs | 1 - 9 files changed, 243 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cd864f4b..09bbdbcae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -919,6 +919,7 @@ dependencies = [ "pci", "thiserror", "util", + "vnc", ] [[package]] @@ -1038,27 +1039,18 @@ dependencies = [ name = "vnc" version = "2.2.0" dependencies = [ - "acpi", - "address_space", "anyhow", "bitintr", - "byteorder", - "hypervisor", "libc", "log", "machine_manager", - "migration", - "migration_derive", "once_cell", - "pci", "rustls", "rustls-pemfile", "sasl2-sys", "serde_json", "sscanf", - "sysbus", "thiserror", - "usb", "util", "vmm-sys-util", ] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index af42f6b7c..33532cc71 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -48,17 +48,17 @@ use devices::legacy::FwCfgOps; use devices::InterruptController; use hypervisor::kvm::KVM_FDS; -#[cfg(not(target_env = "musl"))] -use machine_manager::config::parse_gpu; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, - parse_root_port, parse_scsi_controller, parse_scsi_device, parse_usb_keyboard, - parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, - parse_vsock, parse_xhci, BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, - NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, - VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, + parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, + DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, + NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + MAX_VIRTIO_QUEUE, }; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::{parse_gpu, parse_usb_keyboard, parse_usb_tablet, parse_xhci}; use machine_manager::{ event_loop::EventLoop, machine::{KvmVmState, MachineInterface}, @@ -68,9 +68,9 @@ use pci::{DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; +#[cfg(not(target_env = "musl"))] use usb::{ keyboard::UsbKeyboard, tablet::UsbTablet, usb::UsbDeviceOps, xhci::xhci_pci::XhciPciDevice, - INPUT, }; use util::{ arg_parser, @@ -1068,6 +1068,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - XHCI Configuration. + #[cfg(not(target_env = "musl"))] fn add_usb_xhci(&mut self, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let device_cfg = parse_xhci(cfg_args)?; @@ -1114,6 +1115,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - Keyboard Configuration. + #[cfg(not(target_env = "musl"))] fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_keyboard(cfg_args)?; let keyboard = UsbKeyboard::new(device_cfg.id); @@ -1132,9 +1134,7 @@ pub trait MachineOps { } xhci_pci .unwrap() - .attach_device(&(kbd.clone() as Arc>))?; - let mut locked_input = INPUT.lock().unwrap(); - locked_input.keyboard = Some(kbd); + .attach_device(&(kbd as Arc>))?; Ok(()) } @@ -1143,6 +1143,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - Tablet Configuration. + #[cfg(not(target_env = "musl"))] fn add_usb_tablet(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_tablet(cfg_args)?; let tablet = UsbTablet::new(device_cfg.id); @@ -1161,9 +1162,7 @@ pub trait MachineOps { } xhci_pci .unwrap() - .attach_device(&(tbt.clone() as Arc>))?; - let mut locked_input = INPUT.lock().unwrap(); - locked_input.tablet = Some(tbt); + .attach_device(&(tbt as Arc>))?; Ok(()) } @@ -1245,12 +1244,15 @@ pub trait MachineOps { "vhost-user-fs-pci" | "vhost-user-fs-device" => { self.add_virtio_fs(vm_config, cfg_args)?; } + #[cfg(not(target_env = "musl"))] "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } + #[cfg(not(target_env = "musl"))] "usb-kbd" => { self.add_usb_keyboard(vm_config, cfg_args)?; } + #[cfg(not(target_env = "musl"))] "usb-tablet" => { self.add_usb_tablet(vm_config, cfg_args)?; } diff --git a/usb/Cargo.toml b/usb/Cargo.toml index 368eaaef2..82416ac41 100644 --- a/usb/Cargo.toml +++ b/usb/Cargo.toml @@ -18,3 +18,5 @@ util = { path = "../util" } pci = { path = "../pci" } machine_manager = { path = "../machine_manager" } +[target.'cfg(not(target_env = "musl"))'.dependencies] +vnc = { path = "../vnc" } \ No newline at end of file diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index c33cc9a86..701c2484c 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -27,6 +27,7 @@ use crate::usb::{ UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; +use vnc::input::{register_keyboard, KeyboardOpts}; /// Keyboard device descriptor static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { @@ -100,6 +101,13 @@ const STR_PRODUCT_KEYBOARD_INDEX: u8 = 2; const STR_CONFIG_KEYBOARD_INDEX: u8 = 3; const STR_SERIAL_KEYBOARD_INDEX: u8 = 4; +// Up flag. +const SCANCODE_UP: u16 = 0x80; +// Grey keys. +const SCANCODE_GREY: u16 = 0x80; +// Used to expand Grey keys. +const SCANCODE_EMUL0: u16 = 0xe0; + /// String descriptor const DESC_STRINGS: [&str; 5] = [ "", @@ -118,6 +126,41 @@ pub struct UsbKeyboard { ctrl: Option>>, } +pub struct UsbKeyboardAdapter { + usb_kbd: Arc>, +} + +impl KeyboardOpts for UsbKeyboardAdapter { + fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()> { + let mut scan_codes = Vec::new(); + let mut keycode = keycode; + if keycode & SCANCODE_GREY != 0 { + scan_codes.push(SCANCODE_EMUL0 as u32); + keycode &= !SCANCODE_GREY; + } + + if !down { + keycode |= SCANCODE_UP; + } + scan_codes.push(keycode as u32); + + let mut locked_kbd = self.usb_kbd.lock().unwrap(); + if scan_codes.len() as u32 + locked_kbd.hid.num > QUEUE_LENGTH { + debug!("Keyboard queue is full!"); + // Return ok to ignore the request. + return Ok(()); + } + for code in scan_codes { + let index = ((locked_kbd.hid.head + locked_kbd.hid.num) & QUEUE_MASK) as usize; + locked_kbd.hid.num += 1; + locked_kbd.hid.keyboard.keycodes[index] = code; + } + drop(locked_kbd); + let clone_kbd = self.usb_kbd.clone(); + notify_controller(&(clone_kbd as Arc>)) + } +} + impl UsbKeyboard { pub fn new(id: String) -> Self { Self { @@ -135,26 +178,13 @@ impl UsbKeyboard { self.usb_device .init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; let kbd = Arc::new(Mutex::new(self)); - Ok(kbd) - } -} + let kbd_adapter = Arc::new(Mutex::new(UsbKeyboardAdapter { + usb_kbd: kbd.clone(), + })); + register_keyboard("UsbKeyboard", kbd_adapter); -// Used for VNC to send keyboard event. -pub fn keyboard_event(kbd: &Arc>, scan_codes: &[u32]) -> Result<()> { - let mut locked_kbd = kbd.lock().unwrap(); - if scan_codes.len() as u32 + locked_kbd.hid.num > QUEUE_LENGTH { - debug!("Keyboard queue is full!"); - // Return ok to ignore the request. - return Ok(()); - } - for code in scan_codes { - let index = ((locked_kbd.hid.head + locked_kbd.hid.num) & QUEUE_MASK) as usize; - locked_kbd.hid.num += 1; - locked_kbd.hid.keyboard.keycodes[index] = *code; + Ok(kbd) } - drop(locked_kbd); - let clone_kbd = kbd.clone(); - notify_controller(&(clone_kbd as Arc>)) } impl UsbDeviceOps for UsbKeyboard { diff --git a/usb/src/lib.rs b/usb/src/lib.rs index 0234d8b10..90d55ad6b 100644 --- a/usb/src/lib.rs +++ b/usb/src/lib.rs @@ -17,23 +17,9 @@ pub use error::UsbError; pub mod config; mod descriptor; pub mod hid; +#[cfg(not(target_env = "musl"))] pub mod keyboard; +#[cfg(not(target_env = "musl"))] pub mod tablet; pub mod usb; pub mod xhci; - -use crate::keyboard::UsbKeyboard; -use crate::tablet::UsbTablet; -use once_cell::sync::Lazy; -use std::sync::{Arc, Mutex}; - -pub struct Input { - pub keyboard: Option>>, - pub tablet: Option>>, -} -pub static INPUT: Lazy>> = Lazy::new(|| { - Arc::new(Mutex::new(Input { - keyboard: None, - tablet: None, - })) -}); diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index a53568f4c..07433594e 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -28,6 +28,7 @@ use crate::usb::{ UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; +use vnc::input::{register_pointer, PointerOpts}; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; @@ -134,34 +135,43 @@ impl UsbTablet { self.usb_device .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; let tablet = Arc::new(Mutex::new(self)); + let tablet_adapter = Arc::new(Mutex::new(UsbTabletAdapter { + tablet: tablet.clone(), + })); + register_pointer("UsbTablet", tablet_adapter); Ok(tablet) } } -// Used for VNC to send pointer event. -pub fn pointer_event(tablet: &Arc>, button: u32, x: u32, y: u32) -> Result<()> { - let mut locked_tablet = tablet.lock().unwrap(); - if locked_tablet.hid.num >= QUEUE_LENGTH { - debug!("Pointer queue is full!"); - // Return ok to ignore the request. - return Ok(()); - } - let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; - let mut evt = &mut locked_tablet.hid.pointer.queue[index]; - if button == INPUT_BUTTON_WHEEL_UP { - evt.pos_z = 1; - } else if button == INPUT_BUTTON_WHEEL_DOWN { - evt.pos_z = -1; - } else { - evt.pos_z = 0; +pub struct UsbTabletAdapter { + tablet: Arc>, +} + +impl PointerOpts for UsbTabletAdapter { + fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { + let mut locked_tablet = self.tablet.lock().unwrap(); + if locked_tablet.hid.num >= QUEUE_LENGTH { + debug!("Pointer queue is full!"); + // Return ok to ignore the request. + return Ok(()); + } + let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; + let mut evt = &mut locked_tablet.hid.pointer.queue[index]; + if button == INPUT_BUTTON_WHEEL_UP { + evt.pos_z = 1; + } else if button == INPUT_BUTTON_WHEEL_DOWN { + evt.pos_z = -1; + } else { + evt.pos_z = 0; + } + evt.button_state = button & INPUT_BUTTON_MASK; + evt.pos_x = min(x, INPUT_COORDINATES_MAX); + evt.pos_y = min(y, INPUT_COORDINATES_MAX); + locked_tablet.hid.num += 1; + drop(locked_tablet); + let clone_tablet = self.tablet.clone(); + notify_controller(&(clone_tablet as Arc>)) } - evt.button_state = button & INPUT_BUTTON_MASK; - evt.pos_x = min(x, INPUT_COORDINATES_MAX); - evt.pos_y = min(y, INPUT_COORDINATES_MAX); - locked_tablet.hid.num += 1; - drop(locked_tablet); - let clone_tablet = tablet.clone(); - notify_controller(&(clone_tablet as Arc>)) } impl UsbDeviceOps for UsbTablet { diff --git a/vnc/Cargo.toml b/vnc/Cargo.toml index 73b355cae..96aae6a8a 100644 --- a/vnc/Cargo.toml +++ b/vnc/Cargo.toml @@ -7,7 +7,6 @@ license = "Mulan PSL v2" description = "Visual Network Computing" [dependencies] -byteorder = "1.3.4" thiserror = "1.0" anyhow = "1.0" libc = "0.2" @@ -20,13 +19,5 @@ rustls = "0.20.6" rustls-pemfile = "1.0.0" sasl2-sys = "0.1.20" bitintr = "0.2.0" -usb = { path = "../usb" } -address_space = { path = "../address_space" } -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } -migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } -sysbus = { path = "../sysbus" } -util = { path = "../util" } -pci = { path = "../pci" } -acpi = { path = "../acpi" } \ No newline at end of file +util = { path = "../util" } \ No newline at end of file diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 55791dd62..6785221ac 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -18,17 +18,15 @@ use crate::{ }; use anyhow::Result; use log::error; -use usb::{keyboard::keyboard_event, tablet::pointer_event, INPUT}; +use once_cell::sync::Lazy; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; use util::bitmap::Bitmap; // Logical window size for mouse. const ABS_MAX: u64 = 0x7fff; -// Up flag. -const SCANCODE_UP: u16 = 0x80; -// Grey keys. -const SCANCODE_GREY: u16 = 0x80; -// Used to expand Grey keys. -const SCANCODE_EMUL0: u16 = 0xe0; // Event type of Point. const INPUT_POINT_LEFT: u8 = 0x01; const INPUT_POINT_MIDDLE: u8 = 0x02; @@ -214,11 +212,10 @@ impl ClientIoHandler { .unwrap_or_else(|e| error!("{:?}", e)); } - if let Err(e) = kbd_state.keyboard_state_update(keycode, down) { - error!("{:?}", e); - return; - } - do_key_event(keycode, down); + kbd_state + .keyboard_state_update(keycode, down) + .unwrap_or_else(|e| error!("Key State update error: {:?}", e)); + key_event(keycode, down).unwrap_or_else(|e| error!("Key event error: {:?}", e)); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } @@ -250,12 +247,8 @@ impl ClientIoHandler { _ => buf[1], }; - let locked_input = INPUT.lock().unwrap(); - if let Some(tablet) = &locked_input.tablet { - if let Err(e) = pointer_event(tablet, button_mask as u32, x as u32, y as u32) { - error!("Point event error: {}", e); - } - } + point_event(button_mask as u32, x as u32, y as u32) + .unwrap_or_else(|e| error!("Point event error: {:?}", e)); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } @@ -280,29 +273,138 @@ impl ClientIoHandler { } } -/// Do keyboard event. -/// -/// # Arguments -/// -/// * `down` - press keyboard down or up. -/// * `keycode` - keycode. -fn do_key_event(keycode: u16, down: bool) { - let mut scancode = Vec::new(); - let mut keycode = keycode; - if keycode & SCANCODE_GREY != 0 { - scancode.push(SCANCODE_EMUL0 as u32); - keycode &= !SCANCODE_GREY; +static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); +#[derive(Default)] +struct Inputs { + active_kbd: Option, + active_tablet: Option, + kbd_lists: HashMap>>, + tablet_lists: HashMap>>, +} + +impl Inputs { + fn register_kbd(&mut self, device: &str, kbd: Arc>) { + if self.active_kbd.is_none() { + self.active_kbd = Some(device.to_string()); + } + + self.kbd_lists.insert(device.to_string(), kbd); + } + + fn register_mouse(&mut self, device: &str, tablet: Arc>) { + if self.active_tablet.is_none() { + self.active_tablet = Some(device.to_string()); + } + + self.tablet_lists.insert(device.to_string(), tablet); + } + + fn get_active_kbd(&mut self) -> Option>> { + match &self.active_kbd { + Some(active_kbd) => { + let kbd = self.kbd_lists.get(active_kbd)?.clone(); + Some(kbd) + } + None => None, + } + } + + fn get_active_mouse(&mut self) -> Option>> { + match &self.active_tablet { + Some(active_mouse) => { + let mouse = self.tablet_lists.get(active_mouse)?.clone(); + Some(mouse) + } + None => None, + } + } +} + +pub fn register_keyboard(device: &str, kbd: Arc>) { + INPUTS.lock().unwrap().register_kbd(device, kbd); +} + +pub fn register_pointer(device: &str, tablet: Arc>) { + INPUTS.lock().unwrap().register_mouse(device, tablet); +} + +pub fn key_event(keycode: u16, down: bool) -> Result<()> { + let kbd = INPUTS.lock().unwrap().get_active_kbd(); + if let Some(k) = kbd { + k.lock().unwrap().do_key_event(keycode, down)?; + } + Ok(()) +} + +pub fn point_event(button: u32, x: u32, y: u32) -> Result<()> { + let mouse = INPUTS.lock().unwrap().get_active_mouse(); + if let Some(m) = mouse { + m.lock().unwrap().do_point_event(button, x, y)?; + } + Ok(()) +} + +pub trait KeyboardOpts: Send { + fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()>; +} + +pub trait PointerOpts: Send { + fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()>; +} + +#[cfg(test)] +mod tests { + use super::*; + #[derive(Default)] + pub struct TestKbd { + keycode: u16, + down: bool, } - if !down { - keycode |= SCANCODE_UP; + impl KeyboardOpts for TestKbd { + fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()> { + self.keycode = keycode; + self.down = down; + Ok(()) + } } - scancode.push(keycode as u32); - // Send key event. - let locked_input = INPUT.lock().unwrap(); - if let Some(keyboard) = &locked_input.keyboard { - if let Err(e) = keyboard_event(keyboard, scancode.as_slice()) { - error!("Key event error: {}", e); + + #[derive(Default)] + pub struct TestTablet { + pub button: u32, + x: u32, + y: u32, + } + impl PointerOpts for TestTablet { + fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { + self.button = button; + self.x = x; + self.y = y; + Ok(()) } } + + #[test] + fn test_input_basic() { + // Test keyboard event. + let test_kdb = Arc::new(Mutex::new(TestKbd { + keycode: 0, + down: false, + })); + register_keyboard("TestKeyboard", test_kdb.clone()); + assert!(key_event(12, true).is_ok()); + assert_eq!(test_kdb.lock().unwrap().keycode, 12); + assert_eq!(test_kdb.lock().unwrap().down, true); + + // Test point event. + let test_mouse = Arc::new(Mutex::new(TestTablet::default())); + assert_eq!(test_mouse.lock().unwrap().button, 0); + assert_eq!(test_mouse.lock().unwrap().x, 0); + assert_eq!(test_mouse.lock().unwrap().y, 0); + register_pointer("TestPointer", test_mouse.clone()); + assert!(point_event(1, 54, 12).is_ok()); + assert_eq!(test_mouse.lock().unwrap().button, 1); + assert_eq!(test_mouse.lock().unwrap().x, 54); + assert_eq!(test_mouse.lock().unwrap().y, 12); + } } diff --git a/vnc/src/lib.rs b/vnc/src/lib.rs index c7e2b4462..a45290aa7 100644 --- a/vnc/src/lib.rs +++ b/vnc/src/lib.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod error; -pub use anyhow::Result; pub use error::VncError; pub mod auth; -- Gitee From ad3663d71b61314c91c898afa149d7a2a1c86dde Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Feb 2023 15:37:07 +0800 Subject: [PATCH 0788/2187] Demo pci device: Add deviceTypeOperation trait. Add deviceTypeOperation trait and the config of device type. You can choose different device function based on the parameter of device_type. Signed-off-by: Xiao Ye --- machine_manager/src/config/demo_dev.rs | 25 +++++++- pci/src/demo_dev.rs | 88 +++++++++++++++++++------- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/machine_manager/src/config/demo_dev.rs b/machine_manager/src/config/demo_dev.rs index c9729d9f9..10d21994e 100644 --- a/machine_manager/src/config/demo_dev.rs +++ b/machine_manager/src/config/demo_dev.rs @@ -19,8 +19,10 @@ use super::{pci_args_check, CmdParser, VmConfig}; #[derive(Debug, Clone)] pub struct DemoDevConfig { pub id: String, + // Different device implementations can be configured based on this parameter + pub device_type: String, pub bar_num: u8, - // every bar has the same size just for simplification. + // Every bar has the same size just for simplification. pub bar_size: u64, } @@ -28,6 +30,7 @@ impl DemoDevConfig { pub fn new() -> Self { Self { id: "".to_string(), + device_type: "".to_string(), bar_num: 0, bar_size: 0, } @@ -46,6 +49,7 @@ pub fn parse_demo_dev(_vm_config: &mut VmConfig, args_str: String) -> Result Result("device_type")? { + demo_dev_cfg.device_type = device_type; + } + if let Some(bar_num) = cmd_parser.get_value::("bar_num")? { demo_dev_cfg.bar_num = bar_num; } @@ -72,3 +80,18 @@ pub fn parse_demo_dev(_vm_config: &mut VmConfig, args_str: String) -> Result>, dev_id: Arc, + device: Arc>, } -static mut HASHMAP: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); - impl DemoDev { pub fn new( cfg: DemoDevConfig, @@ -70,6 +69,10 @@ impl DemoDev { sys_mem: Arc, parent_bus: Weak>, ) -> Self { + // You can choose different device function based on the parameter of device_type. + let device = match cfg.device_type { + _ => Arc::new(Mutex::new(BaseDevice::new())), + }; DemoDev { name: cfg.id.clone(), cmd_cfg: cfg.clone(), @@ -78,6 +81,7 @@ impl DemoDev { devfn, parent_bus, dev_id: Arc::new(AtomicU16::new(0)), + device, } } @@ -107,28 +111,24 @@ impl DemoDev { Ok(()) } - // do some mathmetic algrithm when writing to mmio. - // here we just multiply it with 2. - fn write_data_internal_func(innum: u8) -> u8 { - innum.checked_mul(2).unwrap_or(0) - } - fn register_data_handling_bar(&mut self) -> Result<()> { - let write_ops = move |data: &[u8], addr: GuestAddress, _offset: u64| -> bool { - let d = Self::write_data_internal_func(data[0]); - unsafe { - // supports only saving data[0] now for simplification - HASHMAP.lock().unwrap().insert(addr.raw_value(), d); - } + let device = self.device.clone(); + let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + device + .lock() + .unwrap() + .write(data, addr, offset) + .unwrap_or_else(|e| error!("Some error occur in writing: {:?}", e)); true }; - let read_ops = move |data: &mut [u8], addr: GuestAddress, _offset: u64| -> bool { - unsafe { - data[0] = *HASHMAP.lock().unwrap().get(&addr.raw_value()).unwrap_or(&0); - // rm the data after reading, as we assume that the data becomes useless after the test process checked the addr. - HASHMAP.lock().unwrap().remove(&addr.raw_value()); - } + let device = self.device.clone(); + let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + device + .lock() + .unwrap() + .read(data, addr, offset) + .unwrap_or_else(|e| error!("Some error occur in reading: {:?}", e)); true }; @@ -184,6 +184,7 @@ impl PciDevOps for DemoDev { } self.register_data_handling_bar()?; + self.device.lock().unwrap().realize()?; self.attach_to_parent_bus()?; Ok(()) @@ -191,7 +192,7 @@ impl PciDevOps for DemoDev { /// Unrealize PCI/PCIe device. fn unrealize(&mut self) -> Result<()> { - bail!("Unrealize of the demo device is not implemented yet"); + self.device.lock().unwrap().unrealize() } /// read the pci configuration space @@ -225,3 +226,46 @@ impl PciDevOps for DemoDev { Some(self.devfn) } } + +pub struct BaseDevice { + result: HashMap, +} + +impl BaseDevice { + fn new() -> Self { + Self { + result: HashMap::new(), + } + } +} + +impl DeviceTypeOperation for BaseDevice { + // The base device can multiply the value with 2 when writing to mmio. + fn read(&mut self, data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { + let value = data[0].checked_mul(2).unwrap_or(0); + self.result.insert(addr.raw_value(), value); + Ok(()) + } + + // Rm the data after reading, as we assume that the data becomes useless after the test + // process checked the addr. + fn write(&mut self, _data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { + self.result.remove(&addr.raw_value()); + Ok(()) + } + + fn realize(&mut self) -> Result<()> { + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } +} + +pub trait DeviceTypeOperation: Send { + fn read(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; + fn write(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; + fn realize(&mut self) -> Result<()>; + fn unrealize(&mut self) -> Result<()>; +} -- Gitee From 32cb784cad080ae502e545f590ba4a0f26933083 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 24 Feb 2023 23:28:01 +0800 Subject: [PATCH 0789/2187] Demo device: add demo keyboard-mouse device and demo GPU device 1. Demo keyboard device is a simple pci device. It can be used to test whether VNC can correctly receive the input from the client and transmit it to the keyboard and mouse device. 2. Demo GPU device is a simple pci device. Its purpose is to simulate the input of image to test the basic functions of VNC. Signed-off-by: Xiao Ye --- Cargo.lock | 1 + machine/src/lib.rs | 2 +- pci/Cargo.toml | 3 + pci/src/demo_dev.rs | 46 +---- pci/src/demo_device/base_device.rs | 55 +++++ pci/src/demo_device/gpu_device.rs | 233 ++++++++++++++++++++++ pci/src/demo_device/kbd_pointer_device.rs | 166 +++++++++++++++ pci/src/demo_device/mod.rs | 5 + pci/src/lib.rs | 4 +- 9 files changed, 474 insertions(+), 41 deletions(-) create mode 100644 pci/src/demo_device/base_device.rs create mode 100644 pci/src/demo_device/gpu_device.rs create mode 100644 pci/src/demo_device/kbd_pointer_device.rs create mode 100644 pci/src/demo_device/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 09bbdbcae..51af9f8bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,6 +592,7 @@ dependencies = [ "thiserror", "util", "vmm-sys-util", + "vnc", ] [[package]] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 33532cc71..269922ab7 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -64,7 +64,7 @@ use machine_manager::{ machine::{KvmVmState, MachineInterface}, }; use migration::MigrationManager; -use pci::{DemoDev, PciBus, PciDevOps, PciHost, RootPort}; +use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; diff --git a/pci/Cargo.toml b/pci/Cargo.toml index e2e248fd0..e1b226ec8 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -24,3 +24,6 @@ migration_derive = { path = "../migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } acpi = { path = "../acpi" } + +[target.'cfg(not(target_env = "musl"))'.dependencies] +vnc = { path = "../vnc" } diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index b729ce9af..a0ad92d9b 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -30,7 +30,6 @@ /// "-device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0,bar_num=3,bar_size=4096" /// use std::{ - collections::HashMap, sync::Mutex, sync::{ atomic::{AtomicU16, Ordering}, @@ -42,6 +41,9 @@ use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use log::error; use machine_manager::config::DemoDevConfig; +use crate::demo_device::base_device::BaseDevice; +#[cfg(not(target_env = "musl"))] +use crate::demo_device::{gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse}; use crate::{ config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, PCIE_CONFIG_SPACE_SIZE, @@ -70,7 +72,11 @@ impl DemoDev { parent_bus: Weak>, ) -> Self { // You can choose different device function based on the parameter of device_type. - let device = match cfg.device_type { + let device: Arc> = match cfg.device_type.as_str() { + #[cfg(not(target_env = "musl"))] + "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(sys_mem.clone()))), + #[cfg(not(target_env = "musl"))] + "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(sys_mem.clone()))), _ => Arc::new(Mutex::new(BaseDevice::new())), }; DemoDev { @@ -227,42 +233,6 @@ impl PciDevOps for DemoDev { } } -pub struct BaseDevice { - result: HashMap, -} - -impl BaseDevice { - fn new() -> Self { - Self { - result: HashMap::new(), - } - } -} - -impl DeviceTypeOperation for BaseDevice { - // The base device can multiply the value with 2 when writing to mmio. - fn read(&mut self, data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { - let value = data[0].checked_mul(2).unwrap_or(0); - self.result.insert(addr.raw_value(), value); - Ok(()) - } - - // Rm the data after reading, as we assume that the data becomes useless after the test - // process checked the addr. - fn write(&mut self, _data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { - self.result.remove(&addr.raw_value()); - Ok(()) - } - - fn realize(&mut self) -> Result<()> { - Ok(()) - } - - fn unrealize(&mut self) -> Result<()> { - Ok(()) - } -} - pub trait DeviceTypeOperation: Send { fn read(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; fn write(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; diff --git a/pci/src/demo_device/base_device.rs b/pci/src/demo_device/base_device.rs new file mode 100644 index 000000000..e845e018d --- /dev/null +++ b/pci/src/demo_device/base_device.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// BaseDevice is a simplest demo-pci-device. Its function is to +/// multiply data writed by two and return it when reading. +use crate::demo_dev::DeviceTypeOperation; +use address_space::GuestAddress; +pub use anyhow::{bail, Result}; +use std::collections::HashMap; + +#[derive(Default)] +pub struct BaseDevice { + result: HashMap, +} + +impl BaseDevice { + pub fn new() -> Self { + Self { + result: HashMap::new(), + } + } +} + +impl DeviceTypeOperation for BaseDevice { + // The base device can multiply the value with 2 when writing to mmio. + fn read(&mut self, data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { + let value = data[0].checked_mul(2).unwrap_or(0); + self.result.insert(addr.raw_value(), value); + Ok(()) + } + + // Rm the data after reading, as we assume that the data becomes useless after the test + // process checked the addr. + fn write(&mut self, _data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { + self.result.remove(&addr.raw_value()); + Ok(()) + } + + fn realize(&mut self) -> Result<()> { + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs new file mode 100644 index 000000000..f715ea48b --- /dev/null +++ b/pci/src/demo_device/gpu_device.rs @@ -0,0 +1,233 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +// Demo GPU device is a simple pci device. Its purpose is to simulate the input of image +// to test the basic functions of VNC. During the initialization of device, +// it will register in the console. Users can write a rom address in the mmio +// configuration space of the device. The Demo GPU device can do corresponding +// operations by reading the commands. Currently, the supported operations are: +// Replace surface 、Update surface 、 +// Set dirty for target area of the surface 、 +// Update the cursor image. + +use address_space::{AddressSpace, GuestAddress}; +use anyhow::{bail, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::info; +use std::{ + ptr, + sync::{Arc, Mutex, Weak}, +}; +use util::pixman::pixman_format_code_t; +use vnc::{ + console::{ + console_close, console_init, display_cursor_define, display_graphic_update, + display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, + }, + pixman::{ + create_pixman_image, get_image_data, get_image_format, get_image_stride, ref_pixman_image, + }, +}; + +use crate::demo_dev::DeviceTypeOperation; + +#[derive(Debug)] +pub enum GpuEvent { + ReplaceSurface = 0, + ReplaceCursor = 1, + GraphicUpdateArea = 2, + GraphicUpdateDirty = 3, + DeactiveEvent = 4, +} + +impl From for GpuEvent { + fn from(v: u8) -> Self { + match v { + 0 => GpuEvent::ReplaceSurface, + 1 => GpuEvent::ReplaceCursor, + 2 => GpuEvent::GraphicUpdateArea, + 3 => GpuEvent::GraphicUpdateDirty, + _ => GpuEvent::DeactiveEvent, + } + } +} + +pub struct DemoGpu { + sys_mem: Arc, + con: Option>>, + width: u32, + height: u32, + pub surface: Option, + mouse: Option, +} +unsafe impl Send for DemoGpu {} + +impl DemoGpu { + pub fn new(sys_mem: Arc) -> Self { + Self { + sys_mem, + con: None, + width: 0, + height: 0, + surface: None, + mouse: None, + } + } +} + +impl DemoGpu { + /// Create a new surface, and replace the surface. + pub fn hw_replace_surface(&mut self, width: u32, height: u32) -> Result<()> { + // Create Image. + self.width = width; + self.height = height; + let image = create_pixman_image( + pixman_format_code_t::PIXMAN_a8b8g8r8, + self.width as i32, + self.height as i32, + ptr::null_mut(), + self.width as i32 * 4, + ); + let surface = DisplaySurface { + format: get_image_format(image), + image: ref_pixman_image(image), + }; + self.surface = Some(surface); + self.graphic_replace_surface() + } + + /// Create a new cursor image, and update it. + pub fn hw_replace_cursor( + &mut self, + width: u32, + height: u32, + hot_x: u32, + hot_y: u32, + mouse_data: u32, + ) -> Result<()> { + let mut mouse = DisplayMouse { + width, + height, + hot_x, + hot_y, + data: vec![0_u8; mouse_data as usize], + }; + display_cursor_define(&self.con, &mut mouse)?; + self.mouse = Some(mouse); + Ok(()) + } + + /// Change the pixels of the specified area in the image. + pub fn update_image_area(&mut self, x: u32, y: u32, w: u32, h: u32) -> Result<()> { + let image = self.surface.unwrap().image; + let image_ptr = get_image_data(image) as *mut u8; + let stride = get_image_stride(image); + for i in y..y + h { + let ptr = (image_ptr as usize + i as usize * stride as usize) as *mut u8; + for j in x..x + w { + let tmp_ptr = (ptr as usize + 4 * j as usize) as *mut u8; + // ^_ , byte reverse. + unsafe { *tmp_ptr ^= 0xff }; + } + } + self.graphic_update(x, y, w, h) + } + + /// Set a area dirty. + pub fn graphic_update(&mut self, x: u32, y: u32, w: u32, h: u32) -> Result<()> { + display_graphic_update(&self.con, x as i32, y as i32, w as i32, h as i32) + } + + /// Update the cursor image. + pub fn graphic_cursor_define(&mut self) -> Result<()> { + if let Some(mouse) = &mut self.mouse { + display_cursor_define(&self.con, mouse)?; + } + Ok(()) + } + + /// Change surface in display. + pub fn graphic_replace_surface(&mut self) -> Result<()> { + display_replace_surface(&self.con, self.surface) + } +} + +impl DeviceTypeOperation for DemoGpu { + fn read(&mut self, _data: &[u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + bail!("read is not support"); + } + + fn write(&mut self, data: &[u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + let mem_addr = LittleEndian::read_u64(data); + // Event Type. + let mut buf: Vec = vec![]; + self.sys_mem + .read(&mut buf, address_space::GuestAddress(mem_addr), 21)?; + let event_type = GpuEvent::from(buf[0]); + let x = LittleEndian::read_u32(&buf[1..5]); + let y = LittleEndian::read_u32(&buf[5..9]); + let w = LittleEndian::read_u32(&buf[9..13]); + let h = LittleEndian::read_u32(&buf[13..17]); + let data_len = LittleEndian::read_u32(&buf[17..21]); + info!( + "GpuEvent: {:?}, x: {}, y: {}, w: {}, h: {}, data_len: {}", + event_type, x, y, w, h, data_len + ); + match event_type { + GpuEvent::ReplaceSurface => self.hw_replace_surface(w, h), + GpuEvent::ReplaceCursor => self.hw_replace_cursor(w, h, x, y, data_len), + GpuEvent::GraphicUpdateArea => self.update_image_area(x, y, w, h), + GpuEvent::GraphicUpdateDirty => self.graphic_update(x, y, w, h), + _ => self.unrealize(), + } + } + + fn realize(&mut self) -> Result<()> { + let con_opts = Arc::new(HwOpts {}); + self.con = console_init(con_opts); + + // Create Image. + self.width = 640; + self.height = 480; + let image = create_pixman_image( + pixman_format_code_t::PIXMAN_a8b8g8r8, + self.width as i32, + self.height as i32, + ptr::null_mut(), + self.width as i32 * 4, + ); + let surface = DisplaySurface { + format: get_image_format(image), + image: ref_pixman_image(image), + }; + self.surface = Some(surface); + + // Create image. + let mouse = DisplayMouse { + width: 64_u32, + height: 64_u32, + hot_x: 4_u32, + hot_y: 4_u32, + data: vec![0_u8; 64 * 64 * 4], + }; + self.mouse = Some(mouse); + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + let con = self.con.clone(); + console_close(&con) + } +} + +pub struct HwOpts {} +impl HardWareOperations for HwOpts {} diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/pci/src/demo_device/kbd_pointer_device.rs new file mode 100644 index 000000000..4f2f1e36e --- /dev/null +++ b/pci/src/demo_device/kbd_pointer_device.rs @@ -0,0 +1,166 @@ +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +// Demo keyboard-pointer device is a simple pci device. It can be used to test whether +// VNC can correctly receive the input from the client and transmit it to the keyboard and pointer device +// Users can write a rom address in the mmio configuration space of the device. +// Then if an input event occurs, the event information will be recorded to the corresponding memory by this device. + +use address_space::{AddressSpace, GuestAddress}; +use byteorder::{ByteOrder, LittleEndian}; +use once_cell::sync::Lazy; +use std::sync::{Arc, Mutex}; +use vnc::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; + +use anyhow::{bail, Result}; + +use crate::demo_dev::DeviceTypeOperation; +static MEM_ADDR: Lazy>> = Lazy::new(|| { + Arc::new(Mutex::new(MemSpace { + sys_mem: None, + addr: None, + })) +}); + +pub struct MemSpace { + pub sys_mem: Option>, + pub addr: Option, +} + +impl MemSpace { + pub fn send_kbdmouse_message(&mut self, msg: &PointerMessage) -> Result<()> { + let sys_mem = match &self.sys_mem { + Some(m) => m, + None => { + bail!("Memory space is not initialized!") + } + }; + let addr = match self.addr { + Some(a) => a, + None => { + bail!("No memory allocated!") + } + }; + sys_mem.write_object(&(msg.event_type as u8), address_space::GuestAddress(addr))?; + sys_mem.write_object(&msg.keycode, address_space::GuestAddress(addr + 1))?; + sys_mem.write_object(&msg.down, address_space::GuestAddress(addr + 3))?; + sys_mem.write_object(&msg.button, address_space::GuestAddress(addr + 4))?; + sys_mem.write_object(&msg.x, address_space::GuestAddress(addr + 8))?; + sys_mem.write_object(&msg.y, address_space::GuestAddress(addr + 12))?; + + Ok(()) + } +} + +pub struct DemoKbdMouse { + pub sys_mem: Arc, + pub kbd_name: String, + pub pointer_name: String, + pub test_kbd: Arc>, + pub test_pointer: Arc>, +} + +impl DemoKbdMouse { + pub fn new(sys_mem: Arc) -> Self { + MEM_ADDR.lock().unwrap().sys_mem = Some(sys_mem.clone()); + Self { + sys_mem, + kbd_name: "test-pci-keyboard".to_string(), + pointer_name: "test-pci-pointer".to_string(), + test_kbd: Arc::new(Mutex::new(TestPciKbd {})), + test_pointer: Arc::new(Mutex::new(TestPciPointer {})), + } + } +} + +pub struct TestPciKbd {} + +impl KeyboardOpts for TestPciKbd { + fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()> { + let msg = PointerMessage { + event_type: InputEvent::KbdEvent, + keycode, + down: down as u8, + ..Default::default() + }; + MEM_ADDR.lock().unwrap().send_kbdmouse_message(&msg) + } +} + +pub struct TestPciPointer {} + +impl PointerOpts for TestPciPointer { + fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { + let msg = PointerMessage { + event_type: InputEvent::PointerEvent, + button: button as u32, + x, + y, + ..Default::default() + }; + MEM_ADDR.lock().unwrap().send_kbdmouse_message(&msg) + } +} + +#[derive(Debug, Clone, Copy)] +pub enum InputEvent { + KbdEvent = 0, + PointerEvent = 1, + InvalidEvent = 2, +} + +impl Default for InputEvent { + fn default() -> Self { + InputEvent::InvalidEvent + } +} + +impl From for InputEvent { + fn from(v: u8) -> Self { + match v { + 0 => InputEvent::KbdEvent, + 1 => InputEvent::PointerEvent, + _ => InputEvent::InvalidEvent, + } + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct PointerMessage { + pub event_type: InputEvent, + pub keycode: u16, + pub down: u8, + pub button: u32, + pub x: u32, + pub y: u32, +} + +impl DeviceTypeOperation for DemoKbdMouse { + fn read(&mut self, _data: &[u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + Ok(()) + } + + fn write(&mut self, data: &[u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + let mem_addr = LittleEndian::read_u64(data); + MEM_ADDR.lock().unwrap().addr = Some(mem_addr); + Ok(()) + } + + fn realize(&mut self) -> Result<()> { + let test_kbd = self.test_kbd.clone(); + let test_pointer = self.test_pointer.clone(); + register_keyboard(&self.kbd_name, test_kbd); + register_pointer(&self.pointer_name, test_pointer); + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/pci/src/demo_device/mod.rs b/pci/src/demo_device/mod.rs new file mode 100644 index 000000000..15323ef71 --- /dev/null +++ b/pci/src/demo_device/mod.rs @@ -0,0 +1,5 @@ +pub mod base_device; +#[cfg(not(target_env = "musl"))] +pub mod gpu_device; +#[cfg(not(target_env = "musl"))] +pub mod kbd_pointer_device; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 5b59bfc50..25c0ae73d 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -13,16 +13,16 @@ pub mod error; pub use error::PciError; pub mod config; +pub mod demo_dev; pub mod hotplug; pub mod msix; mod bus; -mod demo_dev; +pub mod demo_device; mod host; mod root_port; pub use bus::PciBus; -pub use demo_dev::*; pub use host::PciHost; pub use msix::init_msix; pub use root_port::RootPort; -- Gitee From d579452ee8ba729215acac5075ab2d4eba8c3d6c Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Thu, 23 Feb 2023 06:31:51 +0800 Subject: [PATCH 0790/2187] seccomp: Add coverage-related syscalls to the whitelist When compiling with -CInstrument-coverage, some system calls that are not in the whitelist will be executed. By setting STRATOVIRT_COV=on to allow related system calls to execute. Signed-off-by: Gan Qixin --- machine/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3d0f68fa9..eadc0f933 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1308,6 +1308,12 @@ pub trait MachineOps { balloon_allow_list(&mut bpf_rules); } + if let Ok(cov_enable) = std::env::var("STRATOVIRT_COV") { + if cov_enable.eq("on") { + coverage_allow_list(&mut bpf_rules); + } + } + for bpf_rule in &mut bpf_rules { seccomp_filter.push(bpf_rule); } @@ -1657,3 +1663,10 @@ fn start_incoming_migration(vm: &Arc>) -> Re fn trace_eventnotifier(eventnotifier: &EventNotifier) { util::ftrace!(trace_eventnotifier, "{:#?}", eventnotifier); } + +fn coverage_allow_list(syscall_allow_list: &mut Vec) { + syscall_allow_list.extend(vec![ + BpfRule::new(libc::SYS_fcntl), + BpfRule::new(libc::SYS_ftruncate), + ]) +} -- Gitee From 7003c171298294d3377291d0103f9ef5e3639731 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 09:31:34 +0800 Subject: [PATCH 0791/2187] memory: change high pcie mmio range for PCI host The high pcie mmio base should get the same alignment as the size, ie. a 512GiB entry will be aligned on a 512GiB boundary. So we just change the base to 512 GiB. At the same time we also modify accordingly HighGicRedist and HighPcieEcam. We also need to make modifications to avoid overlapping the MEM area with the PCIe area at the back. Signed-off-by: Li HuaChao --- machine/src/standard_vm/aarch64/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 25846b64d..ebe5e1f90 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -104,10 +104,10 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0x0A00_0000, 0x0000_0200), // Mmio (0x1000_0000, 0x2EFF_0000), // PcieMmio (0x3EFF_0000, 0x0001_0000), // PciePio - (0x4000_0000, 0x80_0000_0000), // Mem - (512 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) - (513 << 30, 0x1000_0000), // HighPcieEcam - (514 << 30, 512 << 30), // HighPcieMmio + (0x4000_0000, 0x7F_4000_0000), // Mem + (510 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) + (511 << 30, 0x1000_0000), // HighPcieEcam + (512 << 30, 512 << 30), // HighPcieMmio ]; /// Standard machine structure. -- Gitee From d264c485d37e5ebd57577ac8e2b6d7321353137a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Feb 2023 09:45:52 +0800 Subject: [PATCH 0792/2187] Fix: Need return false when parameter check overflow When the fwcfg data is read, StratoVirt checks whether the parameters are normal. If an exception occurs, the StratoVirt return false in advance. Otherwise, the subsequent process overflow. Signed-off-by: Mingwang Li --- devices/src/legacy/fwcfg.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index b6e592043..0b6e650cd 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -888,6 +888,7 @@ fn read_bytes( offset - 0x10, data.len() ); + return false; } match fwcfg_arch .fwcfg @@ -1047,6 +1048,7 @@ fn read_bytes(fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, off offset - 0x10, data.len() ); + return false; } match fwcfg_arch.fwcfg.dma_mem_read(offset - 4, data.len() as u32) { Err(e) => { -- Gitee From e285252fdaa58e364db65af518c9e7f5081e895d Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 22 Feb 2023 14:44:22 +0800 Subject: [PATCH 0793/2187] Revert "virtio-pci: delete duplicated check for queue" When queue_config is not ready, the patch "cf3e2f6dc9" will cause the vm can not boot. So, revert it. Signed-off-by: Yan Wang --- virtio/src/virtio_pci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index 1f99940d0..8a0f2595c 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -742,7 +742,7 @@ impl VirtioPciDevice { .unwrap_or(0); } let queue = Queue::new(*q_config, queue_type).unwrap(); - if !queue.is_valid(&self.sys_mem) { + if q_config.ready && !queue.is_valid(&self.sys_mem) { error!("Failed to activate device: Invalid queue"); return false; } -- Gitee From 4b372ab70ae49eeab2ac4c4d6a113aad695ebbd8 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 11:34:17 +0800 Subject: [PATCH 0794/2187] memory: Repair the out-of-bounds problem of the I/O region If the start address is within the region range, but the length of the data value may exceed the region range, we need to determine this situation. Signed-off-by: Li HuaChao --- address_space/src/region.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 9b9172f59..f1e0f7215 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -502,11 +502,11 @@ impl Region { offset: u64, count: u64, ) -> Result<()> { + self.check_valid_offset(offset, count).with_context(|| { + anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + })?; match self.region_type { RegionType::Ram | RegionType::RamDevice => { - self.check_valid_offset(offset, count).with_context(|| { - anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) - })?; let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let slice = unsafe { std::slice::from_raw_parts((host_addr + offset) as *const u8, count as usize) @@ -515,9 +515,6 @@ impl Region { .with_context(|| "Failed to write content of Ram to mutable buffer")?; } RegionType::RomDevice => { - self.check_valid_offset(offset, count).with_context(|| { - anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) - })?; if self.rom_dev_romd.as_ref().load(Ordering::SeqCst) { let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let read_ret = unsafe { @@ -542,9 +539,6 @@ impl Region { } } RegionType::IO => { - if count >= std::usize::MAX as u64 { - return Err(anyhow!(AddressSpaceError::Overflow(count))); - } let mut slice = vec![0_u8; count as usize]; let read_ops = self.ops.as_ref().unwrap().read.as_ref(); if matches!(self.max_access_size, Some(access_size) if count > access_size) { -- Gitee From 96862cdf3be9270e791aa3588cbaf3e20e6b35c2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 11:02:48 +0800 Subject: [PATCH 0795/2187] usb: return error when no matched configuration value 1. When the configuration descriptor is set to an invalid value, an error is returned. 2. Restricting the coordinate range of the tablet device. 3. When the copy_from_slice is called, ensure that the length of the destination is same as that of the source. Signed-off-by: zhouli57 --- usb/src/descriptor.rs | 12 ++++++++++-- usb/src/hid.rs | 8 ++++---- usb/src/keyboard.rs | 1 - usb/src/tablet.rs | 12 +++++++----- vnc/src/input.rs | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/usb/src/descriptor.rs b/usb/src/descriptor.rs index cebfd152b..3b09e56db 100644 --- a/usb/src/descriptor.rs +++ b/usb/src/descriptor.rs @@ -146,7 +146,6 @@ pub struct UsbDescIface { /// USB other descriptor. pub struct UsbDescOther { - pub length: u8, pub data: Vec, } @@ -321,13 +320,18 @@ impl UsbDescriptorOps for UsbDevice { bail!("Device Descriptor not found"); }; let num = desc.device_desc.bNumConfigurations; + let mut found = false; for i in 0..num as usize { if desc.configs[i].config_desc.bConfigurationValue == v { self.descriptor.interface_number = desc.configs[i].config_desc.bNumInterfaces as u32; self.descriptor.configuration_selected = Some(desc.configs[i].clone()); + found = true; } } + if !found { + bail!("Invalid bConfigurationValue {}", v); + } } for i in 0..self.descriptor.interface_number { self.set_interface_descriptor(i, 0)?; @@ -345,7 +349,11 @@ impl UsbDescriptorOps for UsbDevice { let iface = if let Some(face) = self.descriptor.find_interface(index, v) { face } else { - bail!("Interface descriptor not found."); + bail!( + "Interface descriptor not found. index {} value {}", + index, + v + ); }; self.descriptor.altsetting[index as usize] = v; self.descriptor.interfaces[index as usize] = Some(iface); diff --git a/usb/src/hid.rs b/usb/src/hid.rs index e038f1574..891598898 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -185,9 +185,9 @@ impl HidKeyboard { #[derive(Debug, Clone, Copy, Default)] pub struct HidPointerEvent { /// Direction: left to right. - pub pos_x: i32, + pub pos_x: u32, /// Direction: up to down. - pub pos_y: i32, + pub pos_y: u32, /// Wheel up or down. pub pos_z: i32, pub button_state: u32, @@ -421,12 +421,12 @@ impl Hid { HID_GET_REPORT => match self.kind { HidType::Tablet => { let buf = self.pointer_poll(); - data.copy_from_slice(buf.as_slice()); + data[0..buf.len()].copy_from_slice(buf.as_slice()); packet.actual_length = buf.len() as u32; } HidType::Keyboard => { let buf = self.keyboard_poll(); - data.copy_from_slice(buf.as_slice()); + data[0..buf.len()].copy_from_slice(buf.as_slice()); packet.actual_length = buf.len() as u32; } _ => { diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 5818a71b0..c33cc9a86 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -77,7 +77,6 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { iInterface: 0, }, other_desc: vec![Arc::new(UsbDescOther { - length: 0, /// HID descriptor data: vec![0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0], })], diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index b551be5c2..a53568f4c 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; @@ -30,6 +31,8 @@ use crate::xhci::xhci_controller::XhciDevice; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; +const INPUT_BUTTON_MASK: u32 = 0x7; +const INPUT_COORDINATES_MAX: u32 = 0x7fff; /// Tablet device descriptor static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { @@ -80,7 +83,6 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { iInterface: 0, }, other_desc: vec![Arc::new(UsbDescOther { - length: 0, /// HID descriptor data: vec![0x09, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0], })], @@ -137,7 +139,7 @@ impl UsbTablet { } // Used for VNC to send pointer event. -pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32) -> Result<()> { +pub fn pointer_event(tablet: &Arc>, button: u32, x: u32, y: u32) -> Result<()> { let mut locked_tablet = tablet.lock().unwrap(); if locked_tablet.hid.num >= QUEUE_LENGTH { debug!("Pointer queue is full!"); @@ -153,9 +155,9 @@ pub fn pointer_event(tablet: &Arc>, button: u32, x: i32, y: i32 } else { evt.pos_z = 0; } - evt.button_state = button; - evt.pos_x = x; - evt.pos_y = y; + evt.button_state = button & INPUT_BUTTON_MASK; + evt.pos_x = min(x, INPUT_COORDINATES_MAX); + evt.pos_y = min(y, INPUT_COORDINATES_MAX); locked_tablet.hid.num += 1; drop(locked_tablet); let clone_tablet = tablet.clone(); diff --git a/vnc/src/input.rs b/vnc/src/input.rs index 356a1bafa..55791dd62 100644 --- a/vnc/src/input.rs +++ b/vnc/src/input.rs @@ -252,7 +252,7 @@ impl ClientIoHandler { let locked_input = INPUT.lock().unwrap(); if let Some(tablet) = &locked_input.tablet { - if let Err(e) = pointer_event(tablet, button_mask as u32, x as i32, y as i32) { + if let Err(e) = pointer_event(tablet, button_mask as u32, x as u32, y as u32) { error!("Point event error: {}", e); } } -- Gitee From 182b52a47cdd259a18ab36dfbd5146a4c07b7c46 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 11:12:24 +0800 Subject: [PATCH 0796/2187] usb: correct the ERSTBA mask 1. Correct the ERSTBA mask. 2. The logic for updating portsc is modified, otherwise the link state is not updated. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_regs.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 13078ce3a..7394cfd7f 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -496,7 +496,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_INTR_REG_IMOD => intr.imod = value, XHCI_INTR_REG_ERSTSZ => intr.erstsz = value & 0xffff, XHCI_INTR_REG_ERSTBA_LO => { - intr.erstba = write_u64_low(intr.erstba, value & 0xfffffff0); + intr.erstba = write_u64_low(intr.erstba, value & 0xffffffc0); } XHCI_INTR_REG_ERSTBA_HI => { intr.erstba = write_u64_high(intr.erstba, value); @@ -663,10 +663,10 @@ fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { return locked_xhci.reset_port(port, false); } let mut locked_port = port.lock().unwrap(); - let mut portsc = locked_port.portsc; + let old_portsc = locked_port.portsc; let mut notify = 0; // Write 1 to clear. - portsc &= !(value + locked_port.portsc &= !(value & (PORTSC_CSC | PORTSC_PEC | PORTSC_WRC @@ -675,13 +675,12 @@ fn xhci_portsc_write(port: &Arc>, value: u32) -> Result<()> { | PORTSC_PLC | PORTSC_CEC)); if value & PORTSC_LWS == PORTSC_LWS { - let old_pls = (locked_port.portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + let old_pls = (old_portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; let new_pls = (value >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; notify = xhci_portsc_ls_write(&mut locked_port, old_pls, new_pls); } - portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); - portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); - locked_port.portsc = portsc; + locked_port.portsc &= !(PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); + locked_port.portsc |= value & (PORTSC_PP | PORTSC_WCE | PORTSC_WDE | PORTSC_WOE); drop(locked_port); if notify != 0 { locked_xhci.port_notify(port, notify)?; -- Gitee From b829700cb11bcf16afe3d1e58b1dc6c0b06eb414 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 23 Feb 2023 12:43:35 +0800 Subject: [PATCH 0797/2187] Demo-device: construct image with pixman format. Add options of image attribute in constructing image. Signed-off-by: Xiao Ye --- pci/src/demo_device/gpu_device.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index f715ea48b..83d7f4d63 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -86,12 +86,20 @@ impl DemoGpu { impl DemoGpu { /// Create a new surface, and replace the surface. - pub fn hw_replace_surface(&mut self, width: u32, height: u32) -> Result<()> { + pub fn hw_replace_surface(&mut self, width: u32, height: u32, format: u32) -> Result<()> { + let pixman_format = match format { + 1 => pixman_format_code_t::PIXMAN_x2r10g10b10, + 2 => pixman_format_code_t::PIXMAN_r8g8b8, + 3 => pixman_format_code_t::PIXMAN_a1, + 4 => pixman_format_code_t::PIXMAN_yuy2, + _ => pixman_format_code_t::PIXMAN_a8b8g8r8, + }; + // Create Image. self.width = width; self.height = height; let image = create_pixman_image( - pixman_format_code_t::PIXMAN_a8b8g8r8, + pixman_format, self.width as i32, self.height as i32, ptr::null_mut(), @@ -183,7 +191,7 @@ impl DeviceTypeOperation for DemoGpu { event_type, x, y, w, h, data_len ); match event_type { - GpuEvent::ReplaceSurface => self.hw_replace_surface(w, h), + GpuEvent::ReplaceSurface => self.hw_replace_surface(w, h, data_len), GpuEvent::ReplaceCursor => self.hw_replace_cursor(w, h, x, y, data_len), GpuEvent::GraphicUpdateArea => self.update_image_area(x, y, w, h), GpuEvent::GraphicUpdateDirty => self.graphic_update(x, y, w, h), -- Gitee From c524628fbc3dd37607d7c8d1554e629d757f0f4b Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 23 Feb 2023 10:54:13 +0800 Subject: [PATCH 0798/2187] VNC: add dirty_number Add variable to record dirty num. There is an exception: 1. VNC server updates the image and refreshes the pixel, get_rect() ->num 2. VNC client sends an Increment request, flush, get_ rect() -> 0, then this part of the image update is discarded Signed-off-by: Xiao Ye --- vnc/src/client.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/vnc/src/client.rs b/vnc/src/client.rs index 15f0bbb8b..3f879d522 100644 --- a/vnc/src/client.rs +++ b/vnc/src/client.rs @@ -234,6 +234,8 @@ impl Clone for RectInfo { /// The connection state of vnc client. pub struct ConnState { + /// Dirty number need to update. + dirty_num: i32, /// Connection status. pub dis_conn: bool, /// State flags whether the image needs to be updated for the client. @@ -245,6 +247,7 @@ pub struct ConnState { impl Default for ConnState { fn default() -> Self { ConnState { + dirty_num: 0, dis_conn: false, update_state: UpdateState::No, version: VncVersion::default(), @@ -258,17 +261,22 @@ impl ConnState { } /// Whether the client's image data needs to be updated. - pub fn is_need_update(&mut self, dirty_num: i32) -> bool { + pub fn is_need_update(&mut self) -> bool { if self.is_disconnect() { return false; } match self.update_state { UpdateState::No => false, - UpdateState::Incremental => dirty_num > 0, + UpdateState::Incremental => self.dirty_num > 0, UpdateState::Force => true, } } + + pub fn clear_update_state(&mut self) { + self.dirty_num = 0; + self.update_state = UpdateState::No; + } } /// Struct to record the state with the vnc client. @@ -1082,9 +1090,13 @@ impl EventNotifierHelper for ClientIoHandler { /// Generate the data that needs to be sent. /// Add to send queue pub fn get_rects(client: &Arc, dirty_num: i32) { - if !client.conn_state.lock().unwrap().is_need_update(dirty_num) { + let mut locked_state = client.conn_state.lock().unwrap(); + let num = locked_state.dirty_num; + locked_state.dirty_num = num.checked_add(dirty_num).unwrap_or(0); + if !locked_state.is_need_update() { return; } + drop(locked_state); let mut x: u64; let mut y: u64 = 0; @@ -1148,7 +1160,7 @@ pub fn get_rects(client: &Arc, dirty_num: i32) { .unwrap() .push(RectInfo::new(client, rects)); - client.conn_state.lock().unwrap().update_state = UpdateState::No; + client.conn_state.lock().unwrap().clear_update_state(); } fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> Result<()> { -- Gitee From eb5ad8f2f0f0477a182b520da3413c72ecd2bf6c Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 14:42:39 +0800 Subject: [PATCH 0799/2187] memory: delete unused codes Only containers can be expanded to a flat view. Other types of regions should not call generate_flatview. Signed-off-by: Li HuaChao --- address_space/src/region.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index f1e0f7215..bb4bd88da 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -927,17 +927,7 @@ impl Region { ) })?; } - RegionType::Ram | RegionType::IO | RegionType::RomDevice | RegionType::RamDevice => { - self.render_terminate_region(base, addr_range, &mut flat_view) - .with_context(|| { - format!( - "Failed to render terminate region, base 0x{:X}, addr_range (0x{:X}, 0x{:X})", - base.raw_value(), - addr_range.base.raw_value(), - addr_range.size - ) - })?; - } + _ => bail!("Generate flat view failed: only the container supported"), } Ok(flat_view) } -- Gitee From f24564acf0d3daad8a988fb7072ff5dd4b3a3963 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 14:53:36 +0800 Subject: [PATCH 0800/2187] memory: delete unused default function for RegionCache Signed-off-by: Li HuaChao --- address_space/src/address_space.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 64e68e63d..8d7472e7c 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -47,17 +47,6 @@ pub struct RegionCache { pub end: u64, } -impl Default for RegionCache { - fn default() -> Self { - RegionCache { - reg_type: RegionType::Ram, - host_base: 0, - start: 0, - end: 0, - } - } -} - type ListenerObj = Arc>; /// Address Space of memory. -- Gitee From eca634f45eee37eaefed323e2bc9a5aa313af9cb Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 26 Dec 2022 18:54:41 +0800 Subject: [PATCH 0801/2187] demo: add a new demo device DemoDev is a demo PCIe device, that has 2 bars, 1 for msix, 1 for data handling. --- pci/src/demo_dev.rs | 146 ++++++++++++++++++++++++++++++++++++++++++++ pci/src/lib.rs | 1 + 2 files changed, 147 insertions(+) create mode 100644 pci/src/demo_dev.rs diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs new file mode 100644 index 000000000..9d149d3ee --- /dev/null +++ b/pci/src/demo_dev.rs @@ -0,0 +1,146 @@ +/// DemoDev is a demo PCIe device, that has 2 bars, 1 for msix, 1 for data handling. +/// 1. its functionality is to print heximal values that the guest writes, +/// and do nothing if the guest reads its device memory. +/// 2. After printing, it sends back a msix interrupt to the guest, which +/// means that it has also msix capability. We assume msix bar is in bar 0. +/// 3. Finally, it supports hotplug/hotunplug. +/// As that it has device memory, it means it has a bar space, we assume the +/// bar size is 4KB in bar 1. +/// +/// The cmdline for the device is: -device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0 +use std::{ + sync::Mutex, + sync::{ + atomic::{AtomicU16, Ordering}, + Arc, Weak, + }, +}; + +use address_space::Region; +use anyhow::Ok; + +use crate::{ + config::{ + PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, + PCIE_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, + }, + init_msix, le_write_u16, PciBus, PciDevOps, +}; +pub use anyhow::{bail, Result}; + +//bar[0] for msix and bar[1] for dev mmio mem +const DEMO_DEV_BAR_NUM: u8 = 2; + +pub struct DemoDev { + name: String, + config: PciConfig, + mem_region: Region, + devfn: u8, + parent_bus: Weak>, + dev_id: Arc, +} + +impl DemoDev { + pub fn new(name: String, devfn: u8, parent_bus: Weak>) -> Self { + DemoDev { + name, + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, DEMO_DEV_BAR_NUM), + mem_region: Region::init_container_region(u64::max_value() >> 10), + devfn, + parent_bus, + dev_id: Arc::new(AtomicU16::new(0)), + } + } +} + +// reference to https://pci-ids.ucw.cz/read/PC?restrict=1 +// "DEAD BEEF" seems will not be used for a long time. +const VENDOR_ID_DEMO: u16 = 0xDEAD; +const DEVICE_ID_DEMO: u16 = 0xBEEF; +// reference to https://pci-ids.ucw.cz/read/PD/ +const CLASS_CODE_DEMO: u16 = 0xEE; + +impl PciDevOps for DemoDev { + fn init_write_mask(&mut self) -> Result<()> { + self.config.init_common_write_mask() + } + + fn init_write_clear_mask(&mut self) -> Result<()> { + self.config.init_common_write_clear_mask() + } + + /// Realize PCI/PCIe device. + fn realize(mut self) -> Result<()> { + self.init_write_mask()?; + self.init_write_clear_mask()?; + + let config = &mut self.config.config; + le_write_u16(config, DEVICE_ID as usize, DEVICE_ID_DEMO)?; + le_write_u16(config, VENDOR_ID as usize, VENDOR_ID_DEMO)?; + le_write_u16(config, SUB_CLASS_CODE as usize, CLASS_CODE_DEMO)?; + config[HEADER_TYPE as usize] = HEADER_TYPE_ENDPOINT; + + let region = Region::init_container_region(0x1000); + self.config + .register_bar(1, region, RegionType::Mem64Bit, false, 0x1000)?; + init_msix( + 0, + 1, + &mut self.config, + self.dev_id.clone(), + &self.name, + None, + None, + )?; + + let parent_bus = self.parent_bus.upgrade().unwrap(); + let mut locked_parent_bus = parent_bus.lock().unwrap(); + if locked_parent_bus.devices.get(&self.devfn).is_some() { + bail!("device already existed"); + } + let devfn = self.devfn; + let demo_pci_dev = Arc::new(Mutex::new(self)); + locked_parent_bus.devices.insert(devfn, demo_pci_dev); + + Ok(()) + } + + /// Unrealize PCI/PCIe device. + fn unrealize(&mut self) -> Result<()> { + bail!("Unrealize of the demo device is not implemented yet"); + } + + /// read the pci configuration space + fn read_config(&mut self, offset: usize, data: &mut [u8]) { + self.config.read(offset, data); + } + + /// write the pci configuration space + fn write_config(&mut self, offset: usize, data: &[u8]) { + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + + self.config.write( + offset, + data, + self.dev_id.load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + None, + Some(&locked_parent_bus.mem_region), + ); + } + + fn name(&self) -> String { + self.name.clone() + } + + /// Reset device + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.config.reset_common_regs() + } + + /// Get device devfn + fn devfn(&self) -> Option { + Some(self.devfn) + } +} diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 886bfa679..f77fdcf1f 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -17,6 +17,7 @@ pub mod hotplug; pub mod msix; mod bus; +mod demo_dev; mod host; mod root_port; -- Gitee From f63ab89bbe18c4ab26874158f0e6fd2949e14ecb Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 28 Dec 2022 10:59:52 +0800 Subject: [PATCH 0802/2187] demo_dev: add cmdline args parsing for demo_dev add cmdline args parsing and the initialization for demo_dev --- machine/src/lib.rs | 31 ++++++++++++++------ machine_manager/src/config/demo_dev.rs | 40 ++++++++++++++++++++++++++ machine_manager/src/config/mod.rs | 2 ++ pci/src/demo_dev.rs | 13 ++++++++- pci/src/lib.rs | 1 + 5 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 machine_manager/src/config/demo_dev.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 67ebc43b1..23c400af8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -51,20 +51,20 @@ use hypervisor::kvm::KVM_FDS; #[cfg(not(target_env = "musl"))] use machine_manager::config::parse_gpu; use machine_manager::config::{ - complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_scsi_controller, parse_scsi_device, parse_usb_keyboard, parse_usb_tablet, parse_vfio, - parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, parse_xhci, - BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, - NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, - MAX_VIRTIO_QUEUE, + complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, + parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, + parse_root_port, parse_scsi_controller, parse_scsi_device, parse_usb_keyboard, + parse_usb_tablet, parse_vfio, parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, + parse_vsock, parse_xhci, BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, + NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, + VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::{ event_loop::EventLoop, machine::{KvmVmState, MachineInterface}, }; use migration::MigrationManager; -use pci::{PciBus, PciDevOps, PciHost, RootPort}; +use pci::{DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; @@ -1262,6 +1262,9 @@ pub trait MachineOps { "ramfb" => { self.add_ramfb()?; } + "pcie-demo-dev" => { + self.add_demo_dev(vm_config, cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } @@ -1279,6 +1282,18 @@ pub trait MachineOps { bail!("ramfb device is not supported!"); } + fn add_demo_dev(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + + let demo_cfg = parse_demo_dev(vm_config, cfg_args.to_string()) + .with_context(|| "failed to parse cmdline for demo dev.")?; + + let demo_dev = DemoDev::new(demo_cfg.id, devfn, parent_bus); + + demo_dev.realize() + } + /// Return the syscall whitelist for seccomp. fn syscall_whitelist(&self) -> Vec; diff --git a/machine_manager/src/config/demo_dev.rs b/machine_manager/src/config/demo_dev.rs new file mode 100644 index 000000000..edd83855a --- /dev/null +++ b/machine_manager/src/config/demo_dev.rs @@ -0,0 +1,40 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Result}; + +use super::{pci_args_check, CmdParser, VmConfig}; + +/// Config struct for `demo_dev`. +/// Contains demo_dev device's attr. +#[derive(Debug, Clone)] +pub struct DemoDevConfig { + pub id: String, +} + +pub fn parse_demo_dev(_vm_config: &mut VmConfig, args_str: String) -> Result { + let mut cmd_parser = CmdParser::new("demo-dev"); + cmd_parser.push("").push("id").push("addr").push("bus"); + cmd_parser.parse(&args_str)?; + + pci_args_check(&cmd_parser)?; + + let mut demo_dev_cfg = DemoDevConfig { id: "".to_string() }; + + if let Some(id) = cmd_parser.get_value::("id")? { + demo_dev_cfg.id = id; + } else { + bail!("No id configured for demo device"); + } + + Ok(demo_dev_cfg) +} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 20bd38ace..370a6f982 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -13,6 +13,7 @@ pub use balloon::*; pub use boot_source::*; pub use chardev::*; +pub use demo_dev::*; pub use devices::*; pub use drive::*; pub use error::ConfigError; @@ -36,6 +37,7 @@ pub use vnc::*; mod balloon; mod boot_source; mod chardev; +mod demo_dev; mod devices; mod drive; pub mod error; diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 9d149d3ee..47c59301f 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -1,3 +1,15 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + /// DemoDev is a demo PCIe device, that has 2 bars, 1 for msix, 1 for data handling. /// 1. its functionality is to print heximal values that the guest writes, /// and do nothing if the guest reads its device memory. @@ -69,7 +81,6 @@ impl PciDevOps for DemoDev { self.config.init_common_write_clear_mask() } - /// Realize PCI/PCIe device. fn realize(mut self) -> Result<()> { self.init_write_mask()?; self.init_write_clear_mask()?; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index f77fdcf1f..5b59bfc50 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -22,6 +22,7 @@ mod host; mod root_port; pub use bus::PciBus; +pub use demo_dev::*; pub use host::PciHost; pub use msix::init_msix; pub use root_port::RootPort; -- Gitee From d2dc8bb3f79994ceb459a0bb912365aca8642519 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Feb 2023 14:34:34 +0800 Subject: [PATCH 0803/2187] demo_dev: support configuring bar num and bar size In order to make bar_num and bar_size configrable, for test cases for pci devices, make them configurable. usage: "-device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0,bar_num=3,bar_size=4096" Note: it only supports decimal number only due to arg parsing architecture, refactor that in the future. --- machine/src/lib.rs | 2 +- machine_manager/src/config/demo_dev.rs | 38 ++++++++++++++++++++++++-- pci/src/demo_dev.rs | 35 ++++++++++++++++++------ 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 23c400af8..36bdb9121 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1289,7 +1289,7 @@ pub trait MachineOps { let demo_cfg = parse_demo_dev(vm_config, cfg_args.to_string()) .with_context(|| "failed to parse cmdline for demo dev.")?; - let demo_dev = DemoDev::new(demo_cfg.id, devfn, parent_bus); + let demo_dev = DemoDev::new(demo_cfg, devfn, parent_bus); demo_dev.realize() } diff --git a/machine_manager/src/config/demo_dev.rs b/machine_manager/src/config/demo_dev.rs index edd83855a..c9729d9f9 100644 --- a/machine_manager/src/config/demo_dev.rs +++ b/machine_manager/src/config/demo_dev.rs @@ -19,16 +19,41 @@ use super::{pci_args_check, CmdParser, VmConfig}; #[derive(Debug, Clone)] pub struct DemoDevConfig { pub id: String, + pub bar_num: u8, + // every bar has the same size just for simplification. + pub bar_size: u64, +} + +impl DemoDevConfig { + pub fn new() -> Self { + Self { + id: "".to_string(), + bar_num: 0, + bar_size: 0, + } + } +} + +impl Default for DemoDevConfig { + fn default() -> Self { + Self::new() + } } pub fn parse_demo_dev(_vm_config: &mut VmConfig, args_str: String) -> Result { let mut cmd_parser = CmdParser::new("demo-dev"); - cmd_parser.push("").push("id").push("addr").push("bus"); + cmd_parser + .push("") + .push("id") + .push("addr") + .push("bus") + .push("bar_num") + .push("bar_size"); cmd_parser.parse(&args_str)?; pci_args_check(&cmd_parser)?; - let mut demo_dev_cfg = DemoDevConfig { id: "".to_string() }; + let mut demo_dev_cfg = DemoDevConfig::new(); if let Some(id) = cmd_parser.get_value::("id")? { demo_dev_cfg.id = id; @@ -36,5 +61,14 @@ pub fn parse_demo_dev(_vm_config: &mut VmConfig, args_str: String) -> Result("bar_num")? { + demo_dev_cfg.bar_num = bar_num; + } + + // todo: support parsing hex num "0x**". It just supports decimal number now. + if let Some(bar_size) = cmd_parser.get_value::("bar_size")? { + demo_dev_cfg.bar_size = bar_size; + } + Ok(demo_dev_cfg) } diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 47c59301f..b9e46f358 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -30,6 +30,7 @@ use std::{ use address_space::Region; use anyhow::Ok; +use machine_manager::config::DemoDevConfig; use crate::{ config::{ @@ -40,11 +41,9 @@ use crate::{ }; pub use anyhow::{bail, Result}; -//bar[0] for msix and bar[1] for dev mmio mem -const DEMO_DEV_BAR_NUM: u8 = 2; - pub struct DemoDev { name: String, + cmd_cfg: DemoDevConfig, config: PciConfig, mem_region: Region, devfn: u8, @@ -53,10 +52,11 @@ pub struct DemoDev { } impl DemoDev { - pub fn new(name: String, devfn: u8, parent_bus: Weak>) -> Self { + pub fn new(cfg: DemoDevConfig, devfn: u8, parent_bus: Weak>) -> Self { DemoDev { - name, - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, DEMO_DEV_BAR_NUM), + name: cfg.id.clone(), + cmd_cfg: cfg.clone(), + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), mem_region: Region::init_container_region(u64::max_value() >> 10), devfn, parent_bus, @@ -91,9 +91,26 @@ impl PciDevOps for DemoDev { le_write_u16(config, SUB_CLASS_CODE as usize, CLASS_CODE_DEMO)?; config[HEADER_TYPE as usize] = HEADER_TYPE_ENDPOINT; - let region = Region::init_container_region(0x1000); - self.config - .register_bar(1, region, RegionType::Mem64Bit, false, 0x1000)?; + let region_size = self + .cmd_cfg + .bar_size + .checked_mul(self.cmd_cfg.bar_num as u64); + if region_size.is_none() { + bail!( + "bar size overflow with 0x{:x} * 0x{:x}", + self.cmd_cfg.bar_size, + self.cmd_cfg.bar_num + ); + } + let region_size = region_size.unwrap(); + let region = Region::init_container_region(region_size as u64); + self.config.register_bar( + 1, + region, + RegionType::Mem64Bit, + false, + self.cmd_cfg.bar_size as u64, + )?; init_msix( 0, 1, -- Gitee From eb5d5f54cebbdf85e337d342816f81bf93168fed Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Feb 2023 15:18:54 +0800 Subject: [PATCH 0804/2187] refactor: devide demo dev's realize() method into several sub methods just do a refact, no function changes. --- pci/src/demo_dev.rs | 71 ++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index b9e46f358..4db46569e 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -63,25 +63,8 @@ impl DemoDev { dev_id: Arc::new(AtomicU16::new(0)), } } -} - -// reference to https://pci-ids.ucw.cz/read/PC?restrict=1 -// "DEAD BEEF" seems will not be used for a long time. -const VENDOR_ID_DEMO: u16 = 0xDEAD; -const DEVICE_ID_DEMO: u16 = 0xBEEF; -// reference to https://pci-ids.ucw.cz/read/PD/ -const CLASS_CODE_DEMO: u16 = 0xEE; - -impl PciDevOps for DemoDev { - fn init_write_mask(&mut self) -> Result<()> { - self.config.init_common_write_mask() - } - - fn init_write_clear_mask(&mut self) -> Result<()> { - self.config.init_common_write_clear_mask() - } - fn realize(mut self) -> Result<()> { + fn init_pci_config(&mut self) -> Result<()> { self.init_write_mask()?; self.init_write_clear_mask()?; @@ -91,6 +74,10 @@ impl PciDevOps for DemoDev { le_write_u16(config, SUB_CLASS_CODE as usize, CLASS_CODE_DEMO)?; config[HEADER_TYPE as usize] = HEADER_TYPE_ENDPOINT; + Ok(()) + } + + fn init_bars(&mut self) -> Result<()> { let region_size = self .cmd_cfg .bar_size @@ -111,16 +98,10 @@ impl PciDevOps for DemoDev { false, self.cmd_cfg.bar_size as u64, )?; - init_msix( - 0, - 1, - &mut self.config, - self.dev_id.clone(), - &self.name, - None, - None, - )?; + Ok(()) + } + fn attach_to_parent_bus(self) -> Result<()> { let parent_bus = self.parent_bus.upgrade().unwrap(); let mut locked_parent_bus = parent_bus.lock().unwrap(); if locked_parent_bus.devices.get(&self.devfn).is_some() { @@ -132,6 +113,42 @@ impl PciDevOps for DemoDev { Ok(()) } +} + +// reference to https://pci-ids.ucw.cz/read/PC?restrict=1 +// "DEAD BEEF" seems will not be used for a long time. +const VENDOR_ID_DEMO: u16 = 0xDEAD; +const DEVICE_ID_DEMO: u16 = 0xBEEF; +// reference to https://pci-ids.ucw.cz/read/PD/ +const CLASS_CODE_DEMO: u16 = 0xEE; + +impl PciDevOps for DemoDev { + fn init_write_mask(&mut self) -> Result<()> { + self.config.init_common_write_mask() + } + + fn init_write_clear_mask(&mut self) -> Result<()> { + self.config.init_common_write_clear_mask() + } + + /// Realize PCI/PCIe device. + fn realize(mut self) -> Result<()> { + self.init_pci_config()?; + self.init_bars()?; + + init_msix( + 0, + 1, + &mut self.config, + self.dev_id.clone(), + &self.name, + None, + None, + )?; + + self.attach_to_parent_bus()?; + Ok(()) + } /// Unrealize PCI/PCIe device. fn unrealize(&mut self) -> Result<()> { -- Gitee From 183487ebaf2990a70fc11d4e0d3c3eb6d4f29373 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 11:23:57 +0800 Subject: [PATCH 0805/2187] usb: clean up the slot address when disable slot 1. Clean up the slot address when disable slot. 2. Report trb error directly when the trb is invalid. Signed-off-by: zhouli57 --- usb/src/xhci/xhci_controller.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index c01e4e66b..e9fb24014 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -53,8 +53,9 @@ const EP_TYPE_MASK: u32 = 0x7; /// Slot state const SLOT_STATE_MASK: u32 = 0x1f; const SLOT_STATE_SHIFT: u32 = 27; -#[allow(unused)] -const SLOT_ENABLED: u32 = 0; +/// 6.2.3 Slot Context. Table 6-7. +/// The values of both enabled and disabled are 0. +const SLOT_DISABLED_ENABLED: u32 = 0; const SLOT_DEFAULT: u32 = 1; const SLOT_ADDRESSED: u32 = 2; const SLOT_CONFIGURED: u32 = 3; @@ -287,15 +288,19 @@ impl XhciSlot { Ok(slot_ctx) } - /// Get the slot state from the memory. - fn get_slot_state(&self, mem: &Arc) -> Result { + /// Get the slot state in slot context. + fn get_slot_state_in_context(&self, mem: &Arc) -> Result { + // Table 4-1: Device Slot State Code Definitions. + if self.slot_ctx_addr == 0 { + return Ok(SLOT_DISABLED_ENABLED); + } let slot_ctx = self.get_slot_ctx(mem)?; let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; Ok(slot_state) } fn slot_state_is_valid(&self, mem: &Arc) -> Result { - let slot_state = self.get_slot_state(mem)?; + let slot_state = self.get_slot_state_in_context(mem)?; let valid = slot_state == SLOT_DEFAULT || slot_state == SLOT_ADDRESSED || slot_state == SLOT_CONFIGURED; @@ -755,6 +760,7 @@ impl XhciDevice { self.slots[(slot_id - 1) as usize].enabled = false; self.slots[(slot_id - 1) as usize].addressed = false; self.slots[(slot_id - 1) as usize].usb_port = None; + self.slots[(slot_id - 1) as usize].slot_ctx_addr = 0; Ok(TRBCCode::Success) } @@ -842,8 +848,8 @@ impl XhciDevice { fn check_slot_state(&self, slot_ctx: &XhciSlotCtx, bsr: bool) -> Result { let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; - if !(slot_state == SLOT_ENABLED || !bsr && slot_state == SLOT_DEFAULT) { - error!("Invalid slot state: {:?}", slot_ctx); + if !(slot_state == SLOT_DISABLED_ENABLED || !bsr && slot_state == SLOT_DEFAULT) { + error!("Invalid slot state: {:?}", slot_state); return Ok(TRBCCode::ContextStateError); } Ok(TRBCCode::Success) @@ -877,7 +883,8 @@ impl XhciDevice { } fn configure_endpoint(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { - let slot_state = self.slots[(slot_id - 1) as usize].get_slot_state(&self.mem_space)?; + let slot_state = + self.slots[(slot_id - 1) as usize].get_slot_state_in_context(&self.mem_space)?; if trb.control & TRB_CR_DC == TRB_CR_DC { if slot_state != SLOT_CONFIGURED { error!("Invalid slot state: {:?}", slot_state); @@ -889,8 +896,7 @@ impl XhciDevice { error!("Invalid slot state: {:?}", slot_state); return Ok(TRBCCode::ContextStateError); } - self.config_slot_ep(slot_id, trb.parameter)?; - Ok(TRBCCode::Success) + self.config_slot_ep(slot_id, trb.parameter) } fn deconfigure_endpoint(&mut self, slot_id: u32) -> Result { @@ -1356,6 +1362,7 @@ impl XhciDevice { fn do_ctrl_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { if let Err(e) = self.check_ctrl_transfer(xfer) { error!("Failed to check control transfer {}", e); + xfer.status = TRBCCode::TrbError; return self.report_transfer_error(xfer); } let trb_setup = xfer.td[0]; @@ -1364,6 +1371,7 @@ impl XhciDevice { bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; if let Err(e) = self.setup_usb_packet(xfer) { error!("Failed to setup packet when transfer control {}", e); + xfer.status = TRBCCode::TrbError; return self.report_transfer_error(xfer); } xfer.packet.parameter = trb_setup.parameter; @@ -1411,6 +1419,7 @@ impl XhciDevice { } if let Err(e) = self.setup_usb_packet(xfer) { error!("Failed to setup packet when transfer data {}", e); + xfer.status = TRBCCode::TrbError; return self.report_transfer_error(xfer); } self.device_handle_packet(xfer); -- Gitee From c42fa7dd107cf889af4d8e229f9967231e48bd28 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Feb 2023 17:20:28 +0800 Subject: [PATCH 0806/2187] demo_dev: add the data handling bar If the mmio address is writen, multiply the data[0] with 2. The data is reserved in a hashmap that's inside this device. The data will be removed after reading, as that we think it becomes useless after the test process has validated the data. Meanwhile, rm init_bars() because bars should be registered at the time that we register region_ops. --- machine/src/lib.rs | 3 +- pci/src/demo_dev.rs | 138 ++++++++++++++++++++++++++++---------------- 2 files changed, 89 insertions(+), 52 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 36bdb9121..af42f6b7c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1289,7 +1289,8 @@ pub trait MachineOps { let demo_cfg = parse_demo_dev(vm_config, cfg_args.to_string()) .with_context(|| "failed to parse cmdline for demo dev.")?; - let demo_dev = DemoDev::new(demo_cfg, devfn, parent_bus); + let sys_mem = self.get_sys_mem().clone(); + let demo_dev = DemoDev::new(demo_cfg, devfn, sys_mem, parent_bus); demo_dev.realize() } diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 4db46569e..aa1846477 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -10,17 +10,27 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -/// DemoDev is a demo PCIe device, that has 2 bars, 1 for msix, 1 for data handling. -/// 1. its functionality is to print heximal values that the guest writes, -/// and do nothing if the guest reads its device memory. -/// 2. After printing, it sends back a msix interrupt to the guest, which -/// means that it has also msix capability. We assume msix bar is in bar 0. +/// DemoDev is a demo PCIe device, that can have device properites configurable, eg. +/// bar num, max msix vector num, etc. +/// It can have 0-6 bars, if set, msix always lives in bar 0, data handling in bar 1. +/// 1. its functionality is to read and write data for the guest, meanwhile, do a little +/// mathmetic logic(multiply data[0] with 2) with the write op. +/// 2. After r/w, it sends back a msix interrupt to the guest, which means that it has +/// also msix capability. We assume msix bar is in bar 0. /// 3. Finally, it supports hotplug/hotunplug. /// As that it has device memory, it means it has a bar space, we assume the /// bar size is 4KB in bar 1. +/// As that it has device memory, it means it has a bar space other than the msix one.( +/// therotically they can share the same bar as well). +/// +/// Note: developers can also add yourself mmio r/w ops for this device by changing the +/// callback fn write_data_internal_func(), using trait to expand this function is recommended. +/// +/// The example cmdline for the device is: +/// "-device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0,bar_num=3,bar_size=4096" /// -/// The cmdline for the device is: -device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0 use std::{ + collections::HashMap, sync::Mutex, sync::{ atomic::{AtomicU16, Ordering}, @@ -28,14 +38,14 @@ use std::{ }, }; -use address_space::Region; -use anyhow::Ok; +use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use machine_manager::config::DemoDevConfig; +use once_cell::sync::Lazy; use crate::{ config::{ - PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, - PCIE_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, + PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, PCIE_CONFIG_SPACE_SIZE, + SUB_CLASS_CODE, VENDOR_ID, }, init_msix, le_write_u16, PciBus, PciDevOps, }; @@ -45,19 +55,26 @@ pub struct DemoDev { name: String, cmd_cfg: DemoDevConfig, config: PciConfig, - mem_region: Region, + sys_mem: Arc, devfn: u8, parent_bus: Weak>, dev_id: Arc, } +static mut HASHMAP: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); + impl DemoDev { - pub fn new(cfg: DemoDevConfig, devfn: u8, parent_bus: Weak>) -> Self { + pub fn new( + cfg: DemoDevConfig, + devfn: u8, + sys_mem: Arc, + parent_bus: Weak>, + ) -> Self { DemoDev { name: cfg.id.clone(), cmd_cfg: cfg.clone(), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), - mem_region: Region::init_container_region(u64::max_value() >> 10), + sys_mem, devfn, parent_bus, dev_id: Arc::new(AtomicU16::new(0)), @@ -77,30 +94,6 @@ impl DemoDev { Ok(()) } - fn init_bars(&mut self) -> Result<()> { - let region_size = self - .cmd_cfg - .bar_size - .checked_mul(self.cmd_cfg.bar_num as u64); - if region_size.is_none() { - bail!( - "bar size overflow with 0x{:x} * 0x{:x}", - self.cmd_cfg.bar_size, - self.cmd_cfg.bar_num - ); - } - let region_size = region_size.unwrap(); - let region = Region::init_container_region(region_size as u64); - self.config.register_bar( - 1, - region, - RegionType::Mem64Bit, - false, - self.cmd_cfg.bar_size as u64, - )?; - Ok(()) - } - fn attach_to_parent_bus(self) -> Result<()> { let parent_bus = self.parent_bus.upgrade().unwrap(); let mut locked_parent_bus = parent_bus.lock().unwrap(); @@ -113,6 +106,49 @@ impl DemoDev { Ok(()) } + + // do some mathmetic algrithm when writing to mmio. + // here we just multiply it with 2. + fn write_data_internal_func(innum: u8) -> u8 { + innum.checked_mul(2).unwrap_or(0) + } + + fn register_data_handling_bar(&mut self) -> Result<()> { + let write_ops = move |data: &[u8], addr: GuestAddress, _offset: u64| -> bool { + let d = Self::write_data_internal_func(data[0]); + unsafe { + // supports only saving data[0] now for simplification + HASHMAP.lock().unwrap().insert(addr.raw_value(), d); + } + true + }; + + let read_ops = move |data: &mut [u8], addr: GuestAddress, _offset: u64| -> bool { + unsafe { + data[0] = *HASHMAP.lock().unwrap().get(&addr.raw_value()).unwrap_or(&0); + // rm the data after reading, as we assume that the data becomes useless after the test process checked the addr. + HASHMAP.lock().unwrap().remove(&addr.raw_value()); + } + true + }; + + let region_ops = RegionOps { + read: Arc::new(read_ops), + write: Arc::new(write_ops), + }; + + let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops); + + self.config.register_bar( + 0, + region, + crate::config::RegionType::Mem64Bit, + false, + self.cmd_cfg.bar_size, + )?; + + Ok(()) + } } // reference to https://pci-ids.ucw.cz/read/PC?restrict=1 @@ -134,17 +170,20 @@ impl PciDevOps for DemoDev { /// Realize PCI/PCIe device. fn realize(mut self) -> Result<()> { self.init_pci_config()?; - self.init_bars()?; - init_msix( - 0, - 1, - &mut self.config, - self.dev_id.clone(), - &self.name, - None, - None, - )?; + if self.cmd_cfg.bar_num > 0 { + init_msix( + 0, + 1, + &mut self.config, + self.dev_id.clone(), + &self.name, + None, + None, + )?; + } + + self.register_data_handling_bar()?; self.attach_to_parent_bus()?; Ok(()) @@ -162,16 +201,13 @@ impl PciDevOps for DemoDev { /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - self.config.write( offset, data, self.dev_id.load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] None, - Some(&locked_parent_bus.mem_region), + Some(self.sys_mem.root()), ); } -- Gitee From a2f23569eda76d5dee9fdc5fc59dabd911fc8087 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 25 Feb 2023 14:29:07 +0800 Subject: [PATCH 0807/2187] pl031: add set_up for MST testcases Signed-off-by: yezengruan --- tests/mod_test/tests/pl031_test.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/mod_test/tests/pl031_test.rs b/tests/mod_test/tests/pl031_test.rs index dfd52bbc8..d9ed2571d 100644 --- a/tests/mod_test/tests/pl031_test.rs +++ b/tests/mod_test/tests/pl031_test.rs @@ -41,10 +41,15 @@ fn pl031_write_reg(ts: &TestState, reg: u64, val: u32) { ts.writel(RTC_ADDR_BASE + reg, val); } +fn set_up() -> TestState { + let extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + test_init(extra_args) +} + #[test] #[cfg(target_arch = "aarch64")] fn check_time() { - let mut ts = test_init(Vec::new()); + let mut ts = set_up(); let time1 = pl031_read_time(&ts); let time2 = pl031_read_time(&ts); @@ -66,7 +71,7 @@ fn check_time() { #[test] #[cfg(target_arch = "aarch64")] fn set_time() { - let mut ts = test_init(Vec::new()); + let mut ts = set_up(); let time1 = pl031_read_time(&ts); // Time passes about 5 years. @@ -84,7 +89,7 @@ fn set_time() { #[test] #[cfg(target_arch = "aarch64")] fn rtc_enable() { - let mut ts = test_init(Vec::new()); + let mut ts = set_up(); assert_eq!(pl031_read_reg(&ts, RTC_CR), 1); ts.stop(); @@ -93,7 +98,7 @@ fn rtc_enable() { #[test] #[cfg(target_arch = "aarch64")] fn set_mask() { - let mut ts = test_init(Vec::new()); + let mut ts = set_up(); pl031_write_reg(&ts, RTC_IMSC, 1); @@ -104,7 +109,7 @@ fn set_mask() { #[test] #[cfg(target_arch = "aarch64")] fn reg_fuzz() { - let mut ts = test_init(Vec::new()); + let mut ts = set_up(); let mut rng = thread_rng(); for _ in 0..1000 { -- Gitee From bb14c6863369195e32e4e44bdcfdbe860d8cc043 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Feb 2023 14:24:13 +0800 Subject: [PATCH 0808/2187] MST: add fwcfg test and bugfix bootorder in fwcfg boot_order_list is set when add_devices. However, fwcfg sets bootorder before add_devices. As a result, boot_order_list is empty and no data is added. Signed-off-by: Mingwang Li --- Cargo.lock | 1 + cpu/src/lib.rs | 1 + machine/src/standard_vm/aarch64/mod.rs | 8 +- tests/mod_test/Cargo.toml | 1 + tests/mod_test/src/libdriver/fwcfg.rs | 137 ++++++++++++ tests/mod_test/src/libdriver/malloc.rs | 2 +- tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/utils.rs | 16 ++ tests/mod_test/tests/fwcfg_test.rs | 278 +++++++++++++++++++++++++ 9 files changed, 440 insertions(+), 5 deletions(-) create mode 100644 tests/mod_test/src/libdriver/fwcfg.rs create mode 100644 tests/mod_test/tests/fwcfg_test.rs diff --git a/Cargo.lock b/Cargo.lock index 9d5143266..57239bf98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -533,6 +533,7 @@ dependencies = [ name = "mod_test" version = "2.2.0" dependencies = [ + "byteorder", "devices", "hex", "rand", diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 2745e93d1..f1cb54026 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -74,6 +74,7 @@ use log::{error, info, warn}; use machine_manager::event; use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; +#[cfg(not(test))] use util::test_helper::is_test_enabled; use vmm_sys_util::signal::{register_signal_handler, Killable}; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index f88c0613f..bcf16b0db 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -508,10 +508,6 @@ impl MachineOps for StdMachine { None }; - locked_vm - .reset_fwcfg_boot_order() - .with_context(|| "Fail to update boot order imformation to FwCfg device")?; - locked_vm.cpus.extend(::init_vcpu( vm.clone(), nr_cpus, @@ -555,6 +551,10 @@ impl MachineOps for StdMachine { .with_context(|| "Failed to create ACPI tables")?; } + locked_vm + .reset_fwcfg_boot_order() + .with_context(|| "Fail to update boot order imformation to FwCfg device")?; + locked_vm.register_power_event(locked_vm.power_button.clone())?; MigrationManager::register_vm_config(locked_vm.get_vm_config()); diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index a70514a59..d9e89c77f 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -9,5 +9,6 @@ license = "Mulan PSL v2" rand = "0.8.5" hex = "0.4.3" serde_json = "1.0" +byteorder = "1.4.3" devices = { path = "../../devices" } util = { path = "../../util" } \ No newline at end of file diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs new file mode 100644 index 000000000..08996bd02 --- /dev/null +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -0,0 +1,137 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem; + +use devices::legacy::FwCfgEntryType; + +use super::malloc::GuestAllocator; +use crate::libtest::TestState; +use crate::utils::{swap_u16, swap_u32, swap_u64}; + +#[cfg(target_arch = "aarch64")] +pub const FW_CFG_BASE: u64 = 0x09020000; +#[cfg(target_arch = "x86_64")] +pub const FW_CFG_BASE: u64 = 0x510; + +const FW_CFG_FNAME_SIZE: usize = 56; + +#[repr(C)] +pub struct FwCfgDmaAccess { + control: u32, + length: u32, + address: u64, +} + +pub fn bios_args(base_args: &mut Vec<&str>) { + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + base_args.append(&mut args); + args = "-drive file=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true" + .split(' ') + .collect(); + base_args.append(&mut args); +} + +impl TestState { + pub fn fw_cfg_read_bytes(&self, key: u16, data: &mut Vec, len: u32) { + self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); + for _i in 0..len { + data.push(self.readb(FW_CFG_BASE)) + } + } + + pub fn fw_cfg_read_u16(&self, key: u16) -> u16 { + self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); + self.readw(FW_CFG_BASE) + } + + pub fn fw_cfg_read_u32(&self, key: u16) -> u32 { + self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); + self.readl(FW_CFG_BASE) + } + + pub fn dma_transfer_bytes(&self, access: u64, buff: u64, size: u32, ctrl: u32) { + self.writel(access, swap_u32(ctrl)); + self.writel(access + 4, swap_u32(size)); + self.writeq(access + 8, swap_u64(buff)); + + self.writeq(FW_CFG_BASE + 0x10, swap_u64(access)); + } + + pub fn fw_cfg_read_file( + &self, + allocator: &mut GuestAllocator, + file_name: &str, + data: &mut Vec, + data_len: u32, + ) -> u32 { + let file_name_len = file_name.to_string().len(); + let mut file_size = 0; + let mut name: [u8; FW_CFG_FNAME_SIZE] = [0; FW_CFG_FNAME_SIZE]; + let buff = allocator.alloc(FW_CFG_FNAME_SIZE as u64); + let access = allocator.alloc(mem::size_of::() as u64); + + self.writew(FW_CFG_BASE + 0x8, swap_u16(FwCfgEntryType::FileDir as u16)); + let count = swap_u32(self.readl(FW_CFG_BASE)); + for _i in 0..count { + let mut size = swap_u32(self.readl(FW_CFG_BASE)); + let select = swap_u16(self.readw(FW_CFG_BASE)); + let _reserved = swap_u16(self.readw(FW_CFG_BASE)); + // Read file name by DMA. + self.dma_transfer_bytes(access, buff, FW_CFG_FNAME_SIZE as u32, 2); + for i in 0..FW_CFG_FNAME_SIZE { + name[i] = self.readb(buff + i as u64); + } + if String::from_utf8_lossy(&name[0..file_name_len]).eq(file_name) { + file_size = size; + if size > data_len { + size = data_len; + } + self.fw_cfg_read_bytes(select, data, size); + break; + } + } + file_size + } + + pub fn fw_cfg_write_file( + &self, + allocator: &mut GuestAllocator, + file_name: &str, + data_access: u64, + data_addr: u64, + data_len: u32, + ) { + let file_name_len = file_name.to_string().len(); + let mut name: [u8; FW_CFG_FNAME_SIZE] = [0; FW_CFG_FNAME_SIZE]; + let buff = allocator.alloc(FW_CFG_FNAME_SIZE as u64); + let access = allocator.alloc(mem::size_of::() as u64); + + self.writew(FW_CFG_BASE + 0x8, swap_u16(FwCfgEntryType::FileDir as u16)); + let count = swap_u32(self.readl(FW_CFG_BASE)); + for _i in 0..count { + let _size = swap_u32(self.readl(FW_CFG_BASE)); + let select = swap_u16(self.readw(FW_CFG_BASE)); + let _reserved = swap_u16(self.readw(FW_CFG_BASE)); + // Read file name by DMA. + self.dma_transfer_bytes(access, buff, FW_CFG_FNAME_SIZE as u32, 2); + for i in 0..FW_CFG_FNAME_SIZE { + name[i] = self.readb(buff + i as u64); + } + if String::from_utf8_lossy(&name[0..file_name_len]).eq(file_name) { + self.writew(FW_CFG_BASE + 0x8, swap_u16(select)); + self.dma_transfer_bytes(data_access, data_addr, data_len, 16); + break; + } + } + } +} diff --git a/tests/mod_test/src/libdriver/malloc.rs b/tests/mod_test/src/libdriver/malloc.rs index f7c63446e..49cb9a680 100644 --- a/tests/mod_test/src/libdriver/malloc.rs +++ b/tests/mod_test/src/libdriver/malloc.rs @@ -123,7 +123,7 @@ impl GuestAllocator { #[cfg(test)] mod test { - use crate::malloc::GuestAllocator; + use super::GuestAllocator; const PAGE_SIZE_4K: u64 = 1 << 12; const ADDRESS_BASE: u64 = 0x4000_0000; diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 6883421e1..2bc4e0de8 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod fwcfg; pub mod machine; pub mod malloc; pub mod pci_bus; diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index b85d8b010..8cef83076 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -43,3 +43,19 @@ pub fn read_le_u32(input: &mut &[u8]) -> u32 { *input = rest; u32::from_le_bytes(int_bytes.try_into().unwrap()) } + +pub fn swap_u16(value: u16) -> u16 { + return value << 8 | value >> 8; +} + +pub fn swap_u32(value: u32) -> u32 { + let lower_u16 = swap_u16(value as u16) as u32; + let higher_u16 = swap_u16((value >> 16) as u16) as u32; + lower_u16 << 16 | higher_u16 +} + +pub fn swap_u64(value: u64) -> u64 { + let lower_u32 = swap_u32(value as u32) as u64; + let higher_u32 = swap_u32((value >> 32) as u32) as u64; + lower_u32 << 32 | higher_u32 +} diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs new file mode 100644 index 000000000..20325a4b5 --- /dev/null +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -0,0 +1,278 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use byteorder::{ByteOrder, LittleEndian}; +use devices::legacy::FwCfgEntryType; +use mod_test::libdriver::fwcfg::{bios_args, FW_CFG_BASE}; +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libtest::test_init; +use mod_test::utils::{get_rand_str, swap_u16, swap_u32}; + +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::{fs, mem}; + +// FwCfg Signature +const FW_CFG_DMA_SIGNATURE: u128 = 0x51454d5520434647; + +#[test] +fn test_signature() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let mut test_state = test_init(args); + + let mut read_data: Vec = Vec::with_capacity(4); + let target_data: [u8; 4] = ['Q' as u8, 'E' as u8, 'M' as u8, 'U' as u8]; + + // Select Signature entry and read it. + test_state.fw_cfg_read_bytes(FwCfgEntryType::Signature as u16, &mut read_data, 4); + assert_eq!(read_data.as_slice(), target_data); + + test_state.stop(); +} + +#[test] +fn test_id() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let mut test_state = test_init(args); + + // Select Id entry and read it. + let read_data = test_state.fw_cfg_read_u32(FwCfgEntryType::Id as u16); + assert_eq!(read_data, 3); + + test_state.stop(); +} + +#[test] +fn test_nographic() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let mut test_state = test_init(args); + + // Select NoGraphic entry and read it. + let read_data = test_state.fw_cfg_read_u32(FwCfgEntryType::NoGraphic as u16); + assert_eq!(read_data, 0); + + test_state.stop(); +} + +#[test] +fn test_nbcpus() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let mut extra_args: Vec<&str> = "-smp 10".split(' ').collect(); + args.append(&mut extra_args); + let mut test_state = test_init(args); + + // Select NbCpus entry and read it. + let read_data = test_state.fw_cfg_read_u16(FwCfgEntryType::NbCpus as u16); + assert_eq!(read_data, 10); + + test_state.stop(); +} + +#[test] +fn test_kernel_initrd_cmdlint() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + assert!(cfg!(target_os = "linux")); + let kernel_path = "/tmp/kernel"; + let initrd_path = "/tmp/initrd"; + let kernel_of = format!("of={}", kernel_path); + let initrd_of = format!("of={}", initrd_path); + let mut output = Command::new("dd") + .arg("if=/dev/zero") + .arg(&kernel_of) + .arg("bs=1M") + .arg("count=10") + .output() + .expect("Failed to create tmp kernel"); + assert!(output.status.success()); + + output = Command::new("dd") + .arg("if=/dev/zero") + .arg(&initrd_of) + .arg("bs=1M") + .arg("count=1") + .output() + .expect("Failed to create tmp initrd"); + assert!(output.status.success()); + + let kernel_para = format!("-kernel {}", kernel_path); + let initrd_para = format!("-initrd {}", initrd_path); + let mut extra_args: Vec<&str> = kernel_para.split(' ').collect(); + args.append(&mut extra_args); + extra_args = initrd_para.split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-m 1G".split(' ').collect(); + args.append(&mut extra_args); + + // set cmdlint + let cmdline = "-append console=ttyS0 root=/dev/vda reboot=k panic=1"; + extra_args = cmdline.split(' ').collect(); + args.append(&mut extra_args); + let mut test_state = test_init(args); + + // Select KernelSize entry and read it. + let read_data = test_state.fw_cfg_read_u32(FwCfgEntryType::KernelSize as u16); + assert_eq!(read_data, 10 * 1024 * 1024); + + // Select InitrdAddr entry and read it. + let read_data = test_state.fw_cfg_read_u32(FwCfgEntryType::InitrdAddr as u16); + // Initrd addr = (mem start) + (mem end) - (initrd size) + let initrd_addr = 0x4000_0000 + 0x4000_0000 - 0x10_0000; + assert_eq!(read_data, initrd_addr); + + // Select CmdlineSize entry and read it. + let read_data = test_state.fw_cfg_read_u32(FwCfgEntryType::CmdlineSize as u16); + // cmdline size = cmdline - "-append". + let cmdline_size = cmdline.to_string().len() as u32 - 8; + assert_eq!(read_data, cmdline_size + 1); + + // Select CmdlineData entry and read it. + let mut read_data: Vec = Vec::with_capacity(cmdline_size as usize); + test_state.fw_cfg_read_bytes( + FwCfgEntryType::CmdlineData as u16, + &mut read_data, + cmdline_size, + ); + assert_eq!(String::from_utf8_lossy(&read_data), cmdline[8..]); + + fs::remove_file(kernel_path).expect("Failed to remove the kernel file"); + fs::remove_file(initrd_path).expect("Failed to remove the initrd file"); + test_state.stop(); +} + +#[test] +fn test_filedir_by_dma() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let file_name = "etc/boot-fail-wait"; + let mut read_data: Vec = Vec::with_capacity(mem::size_of::()); + + // Select FileDir entry and read it. + let file_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + file_name, + &mut read_data, + mem::size_of::() as u32, + ); + assert_eq!(file_size, mem::size_of::() as u32); + + let time_out = LittleEndian::read_u32(&read_data); + assert_eq!(time_out, 5); + + test_state.borrow_mut().stop(); +} + +#[test] +fn test_boot_index() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let rng_name: String = get_rand_str(8); + + assert!(cfg!(target_os = "linux")); + let image_path = format!("/tmp/stratovirt-{}.img", rng_name); + let image_of = format!("of={}", image_path); + let output = Command::new("dd") + .arg("if=/dev/zero") + .arg(&image_of) + .arg("bs=1M") + .arg("count=10") + .output() + .expect("Failed to create tmp image"); + assert!(output.status.success()); + + let dev_path = "/pci@ffffffffffffffff/scsi@1/disk@0,0\n\0".to_string(); + + let mut extra_args = + "-device virtio-blk-pci,id=drv0,drive=drive0,bus=pcie.0,addr=0x1.0,bootindex=0" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let image_para = format!( + "-drive if=none,id=drive0,file={},format=raw,direct=false", + image_path + ); + extra_args = image_para.split(' ').collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let file_name = "bootorder"; + let mut read_data: Vec = Vec::with_capacity(dev_path.len()); + + // Select FileDir entry and read it. + let file_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + file_name, + &mut read_data, + dev_path.len() as u32, + ); + assert_eq!(file_size, dev_path.len() as u32); + assert_eq!(&read_data, dev_path.as_bytes()); + + test_state.borrow_mut().stop(); +} + +#[test] +fn test_exception_by_ctrl_reg() { + let mut args = Vec::new(); + bios_args(&mut args); + let mut test_state = test_init(args); + + // Select Signature entry and read it by control register. + test_state.writew(FW_CFG_BASE, swap_u16(FwCfgEntryType::Signature as u16)); + let read_data = test_state.readw(FW_CFG_BASE + 0x8); + + // Read data by control register always return 0. + assert_eq!(read_data, 0); + + test_state.stop(); +} + +#[test] +fn test_exception_scenarios() { + let mut args = Vec::new(); + bios_args(&mut args); + let mut test_state = test_init(args); + + // Select entry which is not exit and read it. + let read_data = test_state.fw_cfg_read_u32(0xffff); + assert_eq!(read_data, 0); + + // Read data exceeds the original size. + let read_data = test_state.fw_cfg_read_u32(FwCfgEntryType::Id as u16); + assert_eq!(read_data, 3); + assert_eq!(test_state.readl(FW_CFG_BASE), 0); + + // Read data offset: 0x17 + size: 4 > 0x18, which is overflow + assert_eq!(test_state.readl(FW_CFG_BASE + 0x17), 0); + + // Read FW_CFG_DMA_SIGNATURE high 32bit + assert_eq!( + swap_u32(test_state.readl(FW_CFG_BASE + 0x10)), + (FW_CFG_DMA_SIGNATURE >> 32) as u32 + ); + + test_state.stop(); +} -- Gitee From 5000e463f6a49a05fcbb4a8f6b927cce2cf19e27 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Feb 2023 14:27:10 +0800 Subject: [PATCH 0809/2187] MST: add acpi table test Signed-off-by: Mingwang Li --- Cargo.lock | 2 + machine/src/lib.rs | 2 +- machine/src/standard_vm/mod.rs | 2 +- tests/mod_test/Cargo.toml | 4 +- tests/mod_test/tests/acpi_test.rs | 435 ++++++++++++++++++++++++++++++ 5 files changed, 442 insertions(+), 3 deletions(-) create mode 100644 tests/mod_test/tests/acpi_test.rs diff --git a/Cargo.lock b/Cargo.lock index 57239bf98..a6b055cbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -533,9 +533,11 @@ dependencies = [ name = "mod_test" version = "2.2.0" dependencies = [ + "acpi", "byteorder", "devices", "hex", + "machine", "rand", "serde_json", "util", diff --git a/machine/src/lib.rs b/machine/src/lib.rs index af42f6b7c..9c2335073 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -12,7 +12,7 @@ pub mod error; mod micro_vm; -mod standard_vm; +pub mod standard_vm; #[cfg(target_arch = "x86_64")] mod vm_state; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d5c740bdb..35160d87d 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. #[cfg(target_arch = "aarch64")] -mod aarch64; +pub mod aarch64; #[cfg(target_arch = "x86_64")] mod x86_64; diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index d9e89c77f..fb23ff9b6 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -11,4 +11,6 @@ hex = "0.4.3" serde_json = "1.0" byteorder = "1.4.3" devices = { path = "../../devices" } -util = { path = "../../util" } \ No newline at end of file +util = { path = "../../util" } +acpi = { path = "../../acpi" } +machine = { path = "../../machine" } diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs new file mode 100644 index 000000000..a54f45aab --- /dev/null +++ b/tests/mod_test/tests/acpi_test.rs @@ -0,0 +1,435 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use acpi::{ + AcpiGicCpu, AcpiGicDistributor, AcpiGicRedistributor, AcpiRsdp, AcpiSratGiccAffinity, + AcpiSratMemoryAffinity, AcpiTableHeader, ProcessorHierarchyNode, +}; +use byteorder::{ByteOrder, LittleEndian}; +use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use std::{cell::RefCell, mem, rc::Rc}; + +use mod_test::libdriver::fwcfg::bios_args; +use mod_test::libtest::{test_init, TestState}; + +fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { + let file_name = "etc/acpi/rsdp"; + let mut read_data: Vec = Vec::with_capacity(mem::size_of::()); + + // Select FileDir entry and read it. + let file_size = test_state.fw_cfg_read_file( + alloc, + file_name, + &mut read_data, + mem::size_of::() as u32, + ); + + assert_eq!(file_size, mem::size_of::() as u32); + // Check RSDP signature: "RSD PTR". + assert_eq!(String::from_utf8_lossy(&read_data[..8]), "RSD PTR "); + // Check RSDP revison: 2. + assert_eq!(read_data[15], 2); + + // Check 32-bit address of RSDT table: 0 + let rsdt_addr = LittleEndian::read_u32(&read_data[16..]); + assert_eq!(rsdt_addr, 0); + + // Check 64-bit address of XSDT table. + let xsdt_addr = LittleEndian::read_u64(&read_data[24..]); + assert_ne!(xsdt_addr, 0); + + xsdt_addr +} + +fn check_dsdt(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "DSDT"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 736); // Check length +} + +fn check_fadt(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "FACP"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 276); // Check length + + assert_eq!(LittleEndian::read_i32(&data[112..]), 0x10_0500); // Enable HW_REDUCED_ACPI bit + assert_eq!(LittleEndian::read_u16(&data[129..]), 0x3); // ARM Boot Architecture Flags + assert_eq!(LittleEndian::read_i32(&data[131..]), 3); // FADT minor revision +} + +fn check_madt(data: &[u8], cpu: u8) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "APIC"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 744); // Check length + + let mut offset = 44; + + // Check GIC Distributor + assert_eq!( + data[offset + 1] as usize, + mem::size_of::() + ); + let gicd_addr = LittleEndian::read_u64(&data[(offset + 8)..]); + assert_eq!(gicd_addr, MEM_LAYOUT[LayoutEntryType::GicDist as usize].0); + + // Check GIC verison + assert_eq!(data[offset + 20], 3); + + // Check GIC CPU + offset = offset + mem::size_of::(); + for i in 0..cpu { + assert_eq!(data[offset + 1], 80); // The length of this structure + assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), i as u32); // CPU interface number + assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), i as u32); // ACPI processor UID + assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), 5); // Flags + assert_eq!(LittleEndian::read_u32(&data[(offset + 20)..]), 23); // Performance monitoring interrupts + assert_eq!(LittleEndian::read_u64(&data[(offset + 56)..]), 25); // Virtual GIC maintenance interrupt + assert_eq!(LittleEndian::read_u64(&data[(offset + 68)..]), i as u64); // MPIDR + offset = offset + mem::size_of::(); + } + + // Check GIC Redistributor + let mut addr = LittleEndian::read_u64(&data[(offset + 4)..]); + assert_eq!(MEM_LAYOUT[LayoutEntryType::GicRedist as usize].0, addr); + + // Check GIC Its + offset = offset + mem::size_of::(); + addr = LittleEndian::read_u64(&data[(offset + 8)..]); + assert_eq!(MEM_LAYOUT[LayoutEntryType::GicIts as usize].0, addr); +} + +fn check_gtdt(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "GTDT"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 96); // Check length + + assert_eq!(LittleEndian::read_u32(&data[48..]), 29); // Secure EL1 interrupt + assert_eq!(LittleEndian::read_u32(&data[52..]), 0); // Secure EL1 flags + assert_eq!(LittleEndian::read_u32(&data[56..]), 30); // Non secure EL1 interrupt + assert_eq!(LittleEndian::read_u32(&data[60..]), 4); // Non secure EL1 flags + assert_eq!(LittleEndian::read_u32(&data[64..]), 27); // Virtual timer interrupt + assert_eq!(LittleEndian::read_u32(&data[68..]), 0); // Virtual timer flags + assert_eq!(LittleEndian::read_u32(&data[72..]), 26); // Non secure EL2 interrupt + assert_eq!(LittleEndian::read_u32(&data[76..]), 0); // Non secure EL2 flags +} + +fn check_iort(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "IORT"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 128); // Check length + + // Check IORT nodes is 2: ITS group node and Root Complex Node. + assert_eq!(LittleEndian::read_u32(&data[36..]), 2); + assert_eq!(LittleEndian::read_u32(&data[40..]), 48); // Node offset + assert_eq!(data[48], 0); // ITS group node + assert_eq!(LittleEndian::read_u16(&data[49..]), 24); // ITS node length + assert_eq!(LittleEndian::read_u32(&data[64..]), 1); // ITS count + assert_eq!(data[72], 2); // Root Complex Node + assert_eq!(LittleEndian::read_u16(&data[73..]), 56); // Length of Root Complex Node + assert_eq!(LittleEndian::read_u32(&data[80..]), 1); // Mapping counts of Root Complex Node + assert_eq!(LittleEndian::read_u32(&data[84..]), 36); // Mapping offset of Root Complex Node + assert_eq!(LittleEndian::read_u32(&data[88..]), 1); // Cache of coherent device + assert_eq!(data[95], 3); // Memory flags of coherent device + assert_eq!(LittleEndian::read_u32(&data[112..]), 0xffff); // Identity RID mapping + assert_eq!(LittleEndian::read_u32(&data[120..]), 48); // Without SMMU, id mapping is the first node in ITS group node +} + +fn check_spcr(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "SPCR"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 80); // Check length + + assert_eq!(data[36], 3); // Interface type: ARM PL011 UART + assert_eq!(data[41], 8); // Bit width of AcpiGenericAddress + assert_eq!(data[43], 1); // Access width of AcpiGenericAddress + assert_eq!( + LittleEndian::read_u64(&data[44..]), + MEM_LAYOUT[LayoutEntryType::Uart as usize].0 + ); + assert_eq!(data[52], 1_u8 << 3); // Interrupt Type: Arm GIC interrupu + assert_eq!(LittleEndian::read_u32(&data[54..]), 65); // Irq number used by the UART + assert_eq!(data[58], 3); // Set baud rate: 3 = 9600 + assert_eq!(data[60], 1); // Stop bit + assert_eq!(data[61], 2); // Hardware flow control + assert_eq!(LittleEndian::read_u16(&data[64..]), 0xffff); // PCI Device ID: it is not a PCI device + assert_eq!(LittleEndian::read_u16(&data[66..]), 0xffff); // PCI Vendor ID: it is not a PCI device +} + +fn check_mcfg(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "MCFG"); + assert_eq!(LittleEndian::read_u32(&data[4..]), 60); // Check length + + assert_eq!( + LittleEndian::read_u64(&data[44..]), + MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0 + ); + assert_eq!(LittleEndian::read_u16(&data[52..]), 0); // PCI Segment Group Number + assert_eq!(data[54], 0); // Start Bus Number + assert_eq!(data[55], 255); // End Bus Number +} + +fn check_srat(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "SRAT"); + + // offset = AcpiTable.len = 36 + reserved.len = 12 + let mut offset = 36 + 12; + let mut base_addr = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + // Check Numa Node: + // -object memory-backend-ram,size=2G,id=mem0,host-nodes=0-1,policy=bind + // -object memory-backend-ram,size=2G,id=mem1,host-nodes=0-1,policy=bind + // -numa node,nodeid=0,cpus=0-3,memdev=mem0 + // -numa node,nodeid=1,cpus=4-7,memdev=mem1 + for i in 0..2 { + for j in 0..4 { + let proximity_domian = LittleEndian::read_u32(&data[(offset + 2)..]); + assert_eq!(proximity_domian, i); + let process_uid = LittleEndian::read_u32(&data[(offset + 6)..]); + assert_eq!(process_uid, (i * 4) + j); + offset = offset + mem::size_of::(); + } + assert_eq!(LittleEndian::read_u64(&data[(offset + 8)..]), base_addr); + let size = LittleEndian::read_u64(&data[(offset + 16)..]); + assert_eq!(size, 0x8000_0000); + base_addr = base_addr + size; + offset = offset + mem::size_of::(); + } +} + +fn check_slit(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "SLIT"); + + // offset = AcpiTable.len + NumaNode.len + let mut offset = 44; + // -numa dist,src=0,dst=1,val=30 + // -numa dist,src=1,dst=0,val=30 + for i in 0..2 { + for j in 0..2 { + if i == j { + assert_eq!(data[offset], 10); + } else { + assert_eq!(data[offset], 30); + } + offset = offset + 1; + } + } +} + +fn check_pptt(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "PPTT"); + + // offset = AcpiTable.len = 36 + let mut offset = 36; + // sockets = 1, clusters = 1, cores = 4, threads = 2 + // Check sockets flags and processor_id. + assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 1); + assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), 0); + + // Check clusters flags and processor_id. + offset = offset + mem::size_of::(); + assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 0); + assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), 0); + + // Check cores flags and processor_id. + for i in 0..4 { + offset = offset + mem::size_of::(); + assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 0); + assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), i); + for j in 0..2 { + // Check threads flags and processor_id. + offset = offset + mem::size_of::(); + assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 0xE); + assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), i * 2 + j); + } + } +} + +fn test_tables(test_state: &TestState, alloc: &mut GuestAllocator, xsdt_addr: usize, cpu: u8) { + let file_name = "etc/acpi/tables"; + // Now acpi tables data length is 2864. + let mut read_data: Vec = Vec::with_capacity(2864); + + // Select FileDir entry and read it. + let file_size = test_state.fw_cfg_read_file(alloc, file_name, &mut read_data, 2864); + assert_eq!(file_size, 2864); + + // Check XSDT + assert_eq!( + String::from_utf8_lossy(&read_data[xsdt_addr..(xsdt_addr + 4)]), + "XSDT" + ); + + // XSDT entry: An array of 64-bit physical addresses that point to other DESCRIPTION_HEADERs. + // DESCRIPTION_HEADERs: DSDT, FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT + let entry_addr = xsdt_addr + mem::size_of::() - 8; + + // Check DSDT + let mut offset = entry_addr; + let dsdt_addr = LittleEndian::read_u64(&read_data[offset..]); + check_dsdt(&read_data[(dsdt_addr as usize)..]); + + // Check FADT + offset = entry_addr + 1 * 8; + let fadt_addr = LittleEndian::read_u64(&read_data[offset..]); + check_fadt(&read_data[(fadt_addr as usize)..]); + + // Check MADT + offset = entry_addr + 2 * 8; + let madt_addr = LittleEndian::read_u64(&read_data[offset..]); + check_madt(&read_data[(madt_addr as usize)..], cpu); + + // Check GTDT + offset = entry_addr + 3 * 8; + let gtdt_addr = LittleEndian::read_u64(&read_data[offset..]); + check_gtdt(&read_data[(gtdt_addr as usize)..]); + + // Check IORT + offset = entry_addr + 4 * 8; + let iort_addr = LittleEndian::read_u64(&read_data[offset..]); + check_iort(&read_data[(iort_addr as usize)..]); + + // Check SPCR + offset = entry_addr + 5 * 8; + let spcr_addr = LittleEndian::read_u64(&read_data[offset..]); + check_spcr(&read_data[(spcr_addr as usize)..]); + + // Check MCFG + offset = entry_addr + 6 * 8; + let mcfg_addr = LittleEndian::read_u64(&read_data[offset..]); + check_mcfg(&read_data[(mcfg_addr as usize)..]); + + // Check SRAT + offset = entry_addr + 7 * 8; + let srat_addr = LittleEndian::read_u64(&read_data[offset..]); + check_srat(&read_data[(srat_addr as usize)..]); + + // Check SLIT + offset = entry_addr + 8 * 8; + let slit_addr = LittleEndian::read_u64(&read_data[offset..]); + check_slit(&read_data[(slit_addr as usize)..]); + + // Check PPTT + offset = entry_addr + 9 * 8; + let pptt_addr = LittleEndian::read_u64(&read_data[offset..]); + check_pptt(&read_data[(pptt_addr as usize)..]); +} + +fn check_madt_of_two_gicr( + test_state: &TestState, + alloc: &mut GuestAllocator, + xsdt_addr: usize, + cpus: usize, +) { + let file_name = "etc/acpi/tables"; + // Now acpi tables data length is 29212. + let mut read_data: Vec = Vec::with_capacity(29212); + + // Select FileDir entry and read it. + test_state.fw_cfg_read_file(alloc, file_name, &mut read_data, 29212); + + // XSDT entry: An array of 64-bit physical addresses that point to other DESCRIPTION_HEADERs. + // DESCRIPTION_HEADERs: DSDT, FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT + let entry_addr = xsdt_addr + mem::size_of::() - 8; + + // MADT offset base on XSDT + let mut offset = entry_addr + 2 * 8; + let madt_addr = LittleEndian::read_u64(&read_data[offset..]) as usize; + + // Check second GIC Redistributor + // Second GIC Redistributor addr offset base on MADT: header len = 44 + offset = 44 + + mem::size_of::() + + mem::size_of::() * cpus + + mem::size_of::(); + let addr = LittleEndian::read_u64(&read_data[(madt_addr + offset + 4)..]); + assert_eq!(MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].0, addr); + let len = LittleEndian::read_u32(&read_data[(madt_addr + offset + 12)..]); + assert_eq!( + MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].1, + len as u64 + ); +} + +#[cfg(target_arch = "aarch64")] +#[test] +fn test_acpi_virt() { + let mut args = Vec::new(); + bios_args(&mut args); + + let cpu = 8; + let cpu_args = format!( + "-smp {},sockets=1,cores=4,threads=2 -cpu host,pmu=on -m 4G", + cpu + ); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-object memory-backend-ram,size=2G,id=mem0,host-nodes=0-1,policy=bind" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-object memory-backend-ram,size=2G,id=mem1,host-nodes=0-1,policy=bind" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=0,cpus=0-3,memdev=mem0" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=1,cpus=4-7,memdev=mem1" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=0,dst=1,val=30".split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=1,dst=0,val=30".split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-serial pty".split(' ').collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let alloc = machine.allocator.clone(); + + let xsdt_addr = test_rsdp(&test_state.borrow(), &mut alloc.borrow_mut()); + test_tables( + &test_state.borrow(), + &mut alloc.borrow_mut(), + xsdt_addr as usize, + cpu, + ); + + test_state.borrow_mut().stop(); +} + +#[cfg(target_arch = "aarch64")] +#[test] +fn test_acpi_two_gicr() { + let mut args = Vec::new(); + bios_args(&mut args); + + let cpus = 200; + let cpu_args = format!( + "-smp {},sockets=2,cores=50,threads=2 -cpu host,pmu=on -m 4G", + cpus + ); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let alloc = machine.allocator.clone(); + + let xsdt_addr = test_rsdp(&test_state.borrow(), &mut alloc.borrow_mut()); + check_madt_of_two_gicr( + &test_state.borrow(), + &mut alloc.borrow_mut(), + xsdt_addr as usize, + cpus, + ); + + test_state.borrow_mut().stop(); +} -- Gitee From e4dd22055b03367685391db9e4a1877f638c6920 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 21:04:13 +0800 Subject: [PATCH 0810/2187] memory-test: add a qmp to support region tests Add a qmp command to add and delete a region. Signed-off-by: Li HuaChao --- machine/src/micro_vm/mod.rs | 8 ++ machine/src/standard_vm/mod.rs | 172 ++++++++++++++++++++++++++ machine_manager/src/machine.rs | 4 +- machine_manager/src/qmp/mod.rs | 3 +- machine_manager/src/qmp/qmp_schema.rs | 64 ++++++++++ 5 files changed, 249 insertions(+), 2 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 145828fbd..ad8c46a00 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -30,6 +30,7 @@ pub mod error; pub use error::MicroVmError; +use machine_manager::qmp::qmp_schema::UpdateRegionArgument; use util::aio::AioEngine; mod mem_layout; @@ -1322,6 +1323,13 @@ impl DeviceInterface for LightMachine { Response::create_error_response(err_resp, None) } } + + fn update_region(&mut self, _args: UpdateRegionArgument) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError("The micro vm is not supported".to_string()), + None, + ) + } } impl MigrateInterface for LightMachine { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d5c740bdb..c237e9bfe 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -22,6 +22,7 @@ pub use error::StandardVmError; pub use aarch64::StdMachine; use log::error; use machine_manager::event_loop::EventLoop; +use machine_manager::qmp::qmp_schema::UpdateRegionArgument; use util::aio::AioEngine; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; @@ -31,6 +32,7 @@ use vnc::vnc::qmp_query_vnc; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; +use std::fs::File; use std::mem::size_of; use std::ops::Deref; use std::os::unix::io::RawFd; @@ -46,6 +48,9 @@ use acpi::{ AcpiRsdp, AcpiTable, AmlBuilder, TableLoader, ACPI_RSDP_FILE, ACPI_TABLE_FILE, ACPI_TABLE_LOADER_FILE, TABLE_CHECKSUM_OFFSET, }; +use address_space::{ + AddressRange, FileBackend, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, +}; pub use anyhow::Result; use anyhow::{bail, Context}; use cpu::{CpuTopology, CPU}; @@ -1424,4 +1429,171 @@ impl DeviceInterface for StdMachine { Response::create_error_response(err_resp, None) } } + + fn update_region(&mut self, args: UpdateRegionArgument) -> Response { + #[derive(Default)] + struct DummyDevice { + head: u64, + } + + impl DummyDevice { + fn read(&mut self, data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool { + if data.len() != std::mem::size_of::() { + return false; + } + + for (i, data) in data.iter_mut().enumerate().take(std::mem::size_of::()) { + *data = (self.head >> (8 * i)) as u8; + } + true + } + + fn write(&mut self, data: &[u8], _addr: GuestAddress, _offset: u64) -> bool { + if data.len() != std::mem::size_of::() { + return false; + } + + let ptr: *const u8 = data.as_ptr(); + let ptr: *const u64 = ptr as *const u64; + self.head = unsafe { *ptr } * 2; + true + } + } + + let dummy_dev = Arc::new(Mutex::new(DummyDevice::default())); + let dummy_dev_clone = dummy_dev.clone(); + let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + let mut device_locked = dummy_dev_clone.lock().unwrap(); + device_locked.read(data, addr, offset) + }; + let dummy_dev_clone = dummy_dev; + let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + let mut device_locked = dummy_dev_clone.lock().unwrap(); + device_locked.write(data, addr, offset) + }; + + let dummy_dev_ops = RegionOps { + read: Arc::new(read_ops), + write: Arc::new(write_ops), + }; + + let mut fd = None; + if args.region_type.eq("rom_device_region") || args.region_type.eq("ram_device_region") { + let file_name = if args.region_type.eq("rom_device_region") { + "rom_dev_file.fd" + } else { + "ram_dev_file.fd" + }; + + let file = File::create(file_name).unwrap(); + file.set_len(args.size).unwrap(); + drop(file); + fd = Some( + std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(file_name) + .unwrap(), + ); + } + + let region; + match args.region_type.as_str() { + "io_region" => { + region = Region::init_io_region(args.size, dummy_dev_ops); + if args.ioeventfd.is_some() && args.ioeventfd.unwrap() { + let ioeventfds = vec![RegionIoEventFd { + fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + addr_range: AddressRange::from(( + 0, + args.ioeventfd_size.unwrap_or_default(), + )), + data_match: args.ioeventfd_data.is_some(), + data: args.ioeventfd_data.unwrap_or_default(), + }]; + region.set_ioeventfds(&ioeventfds); + } + } + "rom_device_region" => { + region = Region::init_rom_device_region( + Arc::new( + HostMemMapping::new( + GuestAddress(args.offset), + None, + args.size, + fd.map(FileBackend::new_common), + false, + true, + true, + ) + .unwrap(), + ), + dummy_dev_ops, + ); + } + "ram_device_region" => { + region = Region::init_ram_device_region(Arc::new( + HostMemMapping::new( + GuestAddress(args.offset), + None, + args.size, + fd.map(FileBackend::new_common), + false, + true, + false, + ) + .unwrap(), + )); + } + _ => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError("invalid rergion_type".to_string()), + None, + ); + } + }; + + region.set_priority(args.priority as i32); + if let Some(read_only) = args.romd { + if region.set_rom_device_romd(read_only).is_err() { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "set_rom_device_romd failed".to_string(), + ), + None, + ); + } + } + + let sys_mem = self.get_sys_mem(); + match args.update_type.as_str() { + "add" => { + if sys_mem.root().add_subregion(region, args.offset).is_err() { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError("add subregion failed".to_string()), + None, + ); + } + } + "delete" => { + region.set_offset(GuestAddress(args.offset)); + if sys_mem.root().delete_subregion(®ion).is_err() { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "delete subregion failed".to_string(), + ), + None, + ); + } + } + _ => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError("invalid update_type".to_string()), + None, + ) + } + }; + + Response::create_empty_response() + } } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 0d9080595..3d1b2259b 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -19,7 +19,7 @@ use strum::VariantNames; use crate::qmp::qmp_schema::{ BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, DeviceAddArgument, DeviceProps, Events, GicCap, IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, - NetDevAddArgument, PropList, QmpCommand, QmpEvent, Target, TypeLists, + NetDevAddArgument, PropList, QmpCommand, QmpEvent, Target, TypeLists, UpdateRegionArgument, }; use crate::qmp::{Response, Version}; @@ -418,6 +418,8 @@ pub trait DeviceInterface { } Response::create_response(serde_json::to_value(&vec_iothreads).unwrap(), None) } + + fn update_region(&mut self, args: UpdateRegionArgument) -> Response; } /// Migrate external api diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 53bf086f6..5d25d7249 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -462,7 +462,8 @@ fn qmp_command_exec( (device_add, device_add), (blockdev_add, blockdev_add), (netdev_add, netdev_add), - (chardev_add, chardev_add) + (chardev_add, chardev_add), + (update_region, update_region) ); // Handle the Qmp command which macro can't cover diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 3a59dbd94..52e48ee6c 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -373,6 +373,14 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "update_region")] + #[strum(serialize = "update_region")] + update_region { + #[serde(default)] + arguments: update_region, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, } /// qmp_capabilities @@ -537,6 +545,62 @@ impl Command for device_add { } } +/// update_region +/// +/// # Arguments +/// +/// * `update_type` - update type: add or delete. +/// * `region_type` - the type of the region: io, ram_device, rom_device. +/// * `offset` - the offset of the father region. +/// * `size` - the size of the region. +/// * `priority` - the priority of the region. +/// * `romd` - read only mode. +/// * `ioeventfd` - is there an ioeventfd. +/// * `ioeventfd_data` - the matching data for ioeventfd. +/// * `ioeventfd_size` - the size of matching data. +/// +/// Additional arguments depend on the type. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "update_region", +/// "arguments": { "update_type": "add", "region_type": "io_region", "offset": 0, "size": 4096, "priority": 99 }} +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct update_region { + #[serde(rename = "update_type")] + pub update_type: String, + #[serde(rename = "region_type")] + pub region_type: String, + #[serde(rename = "offset")] + pub offset: u64, + #[serde(rename = "size")] + pub size: u64, + #[serde(rename = "priority")] + pub priority: u64, + #[serde(rename = "read_only_mode")] + pub romd: Option, + #[serde(rename = "ioeventfd")] + pub ioeventfd: Option, + #[serde(rename = "ioeventfd_data")] + pub ioeventfd_data: Option, + #[serde(rename = "ioeventfd_size")] + pub ioeventfd_size: Option, +} + +pub type UpdateRegionArgument = update_region; + +impl Command for update_region { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct FileOptions { -- Gitee From 6df6db75e1f7e3f3847a3be028bb39a937dfe738 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 26 Feb 2023 13:07:09 +0800 Subject: [PATCH 0811/2187] mst/libdriver: Some fixes for pci_bus Keep ecam base same with that of arm virt machine. Signed-off-by: Keqian Zhu --- tests/mod_test/src/libdriver/pci_bus.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 30306ae1c..3070688f3 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -15,8 +15,12 @@ use crate::utils::{read_le_u16, read_le_u32}; use std::cell::RefCell; use std::rc::Rc; +const PCIE_MMIO_BASE: u64 = 0x1000_0000; +const PCIE_MMIO_SIZE: u64 = 0x2EFF_0000; +const PCIE_ECAM_BASE: u64 = 511 << 30; + pub trait PciBusOps { - fn memread(&self, addr: u32, buf: &mut Vec, len: usize); + fn memread(&self, addr: u32, len: usize) -> Vec; fn memwrite(&self, addr: u32, buf: &[u8], len: usize); fn config_readb(&self, devfn: u32, offset: u8) -> u8; @@ -40,9 +44,9 @@ pub struct TestPciBus { impl TestPciBus { pub fn new(test_state: Rc>) -> Self { Self { - mmio_alloc_ptr: 0x10000000, - mmio_limit: 0x2eff0000, - ecam_alloc_ptr: 0x8040000000, + mmio_alloc_ptr: PCIE_MMIO_BASE, + mmio_limit: PCIE_MMIO_SIZE, + ecam_alloc_ptr: PCIE_ECAM_BASE, not_hotpluggable: false, test_state, } @@ -54,18 +58,18 @@ impl TestPciBus { } impl PciBusOps for TestPciBus { - fn memread(&self, addr: u32, buf: &mut Vec, len: usize) { - *buf = self.test_state.borrow().memread(addr as u64, len as u64); + fn memread(&self, addr: u32, len: usize) -> Vec { + self.test_state.borrow().memread(addr as u64, len as u64) } fn memwrite(&self, addr: u32, buf: &[u8], len: usize) { self.test_state .borrow() - .memwrite(addr as u64, &*buf, len as u64); + .memwrite(addr as u64, buf, len as u64); } fn config_readb(&self, devfn: u32, offset: u8) -> u8 { - let addr = self.ecam_alloc_ptr + (devfn << 12 | offset as u32) as u64; + let addr = self.get_addr(devfn, offset); self.test_state.borrow().memread(addr, 1)[0] } -- Gitee From c6e57935ba57bfa1f4cb96edbe6df0f5e49b3643 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 26 Feb 2023 13:26:36 +0800 Subject: [PATCH 0812/2187] QMP: add system_reset command External operators can run the qmp command: system_reset to reset VMs. In addition, fix a format alarm. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 5 -- docs/qmp.md | 12 +++++ machine/src/standard_vm/aarch64/mod.rs | 5 ++ machine/src/standard_vm/x86_64/ich9_lpc.rs | 8 ++- machine/src/standard_vm/x86_64/mod.rs | 22 +++++++- machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 61 ++++++++++++++++++++-- virtio/src/vhost/user/client.rs | 2 +- 8 files changed, 103 insertions(+), 13 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index f1cb54026..e90d7bc42 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -461,11 +461,6 @@ impl CPUInterface for CPU { return Err(anyhow!(CpuError::NoMachineInterface)); } - if QmpChannel::is_connected() { - let reset_msg = schema::Reset { guest: true }; - event!(Reset; reset_msg); - } - Ok(()) } diff --git a/docs/qmp.md b/docs/qmp.md index 89a2f9a98..243194646 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -265,6 +265,18 @@ Resume all guest VCPUs execution. -> {"return":{}} ``` +### system_reset + +Reset all guest VCPUs execution. + +#### Example + +```json +<- {"execute":"system_reset"} +-> {"return":{}} +-> {"event":"RESET","data":{"guest":true},"timestamp":{"seconds":1677381086,"microseconds":432033}} +``` + ### quit This command will cause StratoVirt process to exit gracefully. diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index bcf16b0db..115a2037c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -238,6 +238,11 @@ impl StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; + if QmpChannel::is_connected() { + let reset_msg = qmp_schema::Reset { guest: true }; + event!(Reset; reset_msg); + } + for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.resume() .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index de5cbd465..38cf7663e 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -56,7 +56,11 @@ pub struct LPCBridge { } impl LPCBridge { - pub fn new(parent_bus: Weak>, sys_io: Arc) -> Result { + pub fn new( + parent_bus: Weak>, + sys_io: Arc, + reset_req: Arc, + ) -> Result { Ok(Self { config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), parent_bus, @@ -65,7 +69,7 @@ impl LPCBridge { pm_evt: Arc::new(Mutex::new(AcpiPmEvent::new())), pm_ctrl: Arc::new(Mutex::new(AcpiPmCtrl::new())), rst_ctrl: Arc::new(AtomicU8::new(0)), - reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + reset_req, shutdown_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), }) } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 663e69496..1b9aa3430 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -114,6 +114,8 @@ pub struct StdMachine { boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. power_button: Arc, + /// Reset request, handle VM `Reset` event. + reset_req: Arc, /// All configuration information of virtual machine. vm_config: Arc>, /// List of guest NUMA nodes information. @@ -172,6 +174,9 @@ impl StdMachine { power_button: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { anyhow!(MachineError::InitEventFdErr("power_button".to_string())) })?), + reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + anyhow!(MachineError::InitEventFdErr("reset request".to_string())) + })?), vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -198,6 +203,11 @@ impl StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order information to FwCfg device")?; + if QmpChannel::is_connected() { + let reset_msg = qmp_schema::Reset { guest: true }; + event!(Reset; reset_msg); + } + for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.reset() .with_context(|| format!("Failed to reset vcpu{}", cpu_index))?; @@ -248,8 +258,8 @@ impl StdMachine { fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); - let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone())?; - self.register_reset_event(ich.reset_req.clone(), vm) + let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone(), self.reset_req.clone())?; + self.register_reset_event(self.reset_req.clone(), vm) .with_context(|| "Fail to register reset event in LPC")?; self.register_acpi_shutdown_event(ich.shutdown_req.clone(), clone_vm) .with_context(|| "Fail to register shutdown event in LPC")?; @@ -820,6 +830,14 @@ impl MachineLifecycle for StdMachine { true } + fn reset(&mut self) -> bool { + if self.reset_req.write(1).is_err() { + error!("X86 standard vm write reset request failed"); + return false; + } + true + } + fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { self.vm_state_transfer(&self.cpus, &mut self.vm_state.0.lock().unwrap(), old, new) .is_ok() diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 53bf086f6..0bd4376b5 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -423,6 +423,7 @@ fn qmp_command_exec( qmp_command.clone(); controller.lock().unwrap(); qmp_response; (stop, pause), (cont, resume), + (system_reset, reset), (query_status, query_status), (query_version, query_version), (query_commands, query_commands), diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 3a59dbd94..698a87051 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -80,6 +80,12 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + system_reset { + #[serde(default)] + arguments: system_reset, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, device_add { arguments: Box, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -466,6 +472,28 @@ impl Command for cont { } } +/// system_reset +/// +/// Reset guest VCPU execution. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "system_reset" } +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct system_reset {} + +impl Command for system_reset { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + /// device_add /// /// # Arguments @@ -1415,9 +1443,9 @@ impl Command for query_version { /// ```text /// -> { "execute": "query-commands" } /// <- {"return":[{"name":"qmp_capabilities"},{"name":"quit"},{"name":"stop"}, -/// {"name":"cont"},{"name":"device_add"},{"name":"device_del"},{"name":"netdev_add"}, -/// {"name":"netdev_del"},{"name":"query-hotpluggable-cpus"},{"name":"query-cpus"}, -/// {"name":"query_status"},{"name":"getfd"},{"name":"blockdev_add"}, +/// {"name":"cont"},{"name":"system_reset"},{"name":"device_add"},{"name":"device_del"}, +/// {"name":"netdev_add"},{"name":"netdev_del"},{"name":"query-hotpluggable-cpus"}, +/// {"name":"query-cpus"},{"name":"query_status"},{"name":"getfd"},{"name":"blockdev_add"}, /// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"},{"name":"query_vnc"}, /// {"name":"migrate"},{"name":"query_migrate"},{"name":"query_version"}, /// {"name":"query_target"},{"name":"query_commands"}]} @@ -2059,6 +2087,33 @@ mod tests { let ret_msg = r#"invalid type: string "isdf", expected struct cont"#; assert!(err_msg == ret_msg); + // qmp: system_reset. + let json_msg = r#" + { + "execute": "system_reset" + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let ret_msg = r#"ok"#; + assert!(err_msg == ret_msg); + + // unexpected arguments for system_reset. + let json_msg = r#" + { + "execute": "system_reset" , + "arguments": "isdf" + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let ret_msg = r#"invalid type: string "isdf", expected struct system_reset"#; + assert!(err_msg == ret_msg); + // qmp: query-hotpluggable-cpus. let json_msg = r#" { diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 98782bd64..7a1bf5390 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -346,7 +346,7 @@ pub struct VhostInflight { pub inner: VhostUserInflight, } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum VhostBackendType { TypeNet, TypeBlock, -- Gitee From ce67a57e7fcae263e927a69141698220be46218a Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Sun, 26 Feb 2023 14:58:46 +0800 Subject: [PATCH 0813/2187] memory: fix the firmware on x86 cannot work properly The IO request sent by KVM originally include count and size, but KVM IOCTLs of rust multiplied these two variables as the array length of the read request. The length may exceeds the limit of the IO region size. For now, we remove offset check for IO region in `address_space:: region::Region::read` function. And the RegionOps need to do the check. Signed-off-by: Li HuaChao --- address_space/src/region.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index bb4bd88da..93f34119c 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -502,11 +502,11 @@ impl Region { offset: u64, count: u64, ) -> Result<()> { - self.check_valid_offset(offset, count).with_context(|| { - anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) - })?; match self.region_type { RegionType::Ram | RegionType::RamDevice => { + self.check_valid_offset(offset, count).with_context(|| { + anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + })?; let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let slice = unsafe { std::slice::from_raw_parts((host_addr + offset) as *const u8, count as usize) @@ -515,6 +515,9 @@ impl Region { .with_context(|| "Failed to write content of Ram to mutable buffer")?; } RegionType::RomDevice => { + self.check_valid_offset(offset, count).with_context(|| { + anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + })?; if self.rom_dev_romd.as_ref().load(Ordering::SeqCst) { let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let read_ret = unsafe { -- Gitee From 54843b1eb0d162db0633e9913dc935377762ca8d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 26 Feb 2023 15:59:08 +0800 Subject: [PATCH 0814/2187] MST: Add ramfb mst testcase In addition, fix some constants. Signed-off-by: Jinhao Gao --- tests/mod_test/src/libdriver/fwcfg.rs | 3 +- tests/mod_test/src/libdriver/pci_bus.rs | 7 +- tests/mod_test/tests/ramfb_test.rs | 314 ++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 tests/mod_test/tests/ramfb_test.rs diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs index 08996bd02..3bb71792b 100644 --- a/tests/mod_test/src/libdriver/fwcfg.rs +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -17,9 +17,10 @@ use devices::legacy::FwCfgEntryType; use super::malloc::GuestAllocator; use crate::libtest::TestState; use crate::utils::{swap_u16, swap_u32, swap_u64}; +use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "aarch64")] -pub const FW_CFG_BASE: u64 = 0x09020000; +pub const FW_CFG_BASE: u64 = MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0; #[cfg(target_arch = "x86_64")] pub const FW_CFG_BASE: u64 = 0x510; diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 3070688f3..735f4dffe 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -12,12 +12,13 @@ use crate::libtest::TestState; use crate::utils::{read_le_u16, read_le_u32}; +use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; use std::cell::RefCell; use std::rc::Rc; -const PCIE_MMIO_BASE: u64 = 0x1000_0000; -const PCIE_MMIO_SIZE: u64 = 0x2EFF_0000; -const PCIE_ECAM_BASE: u64 = 511 << 30; +const PCIE_MMIO_BASE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; +const PCIE_MMIO_SIZE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; +const PCIE_ECAM_BASE: u64 = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; pub trait PciBusOps { fn memread(&self, addr: u32, len: usize) -> Vec; diff --git a/tests/mod_test/tests/ramfb_test.rs b/tests/mod_test/tests/ramfb_test.rs new file mode 100644 index 000000000..ed0b4b94b --- /dev/null +++ b/tests/mod_test/tests/ramfb_test.rs @@ -0,0 +1,314 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libdriver::fwcfg::{bios_args, FwCfgDmaAccess}; +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libtest::{test_init, TestState}; +use mod_test::utils::{swap_u32, swap_u64}; + +use std::cell::RefCell; +use std::fs; +use std::mem; +use std::path::Path; +use std::rc::Rc; + +const FRAMEBUFFER_SIZE: u64 = 3 * 1024 * 1024; +const RAMFB_FORMAT: u32 = 0x34325258; +const HORIZONTAL_RESOLUTION: u32 = 800; +const VERTICAL_RESOLUTION: u32 = 600; +const RAMFB_BPP: u32 = 4; +const ABNORMAL_FB_BASE: u64 = 0x60000001; + +#[repr(C, packed(1))] +#[derive(Default)] +struct RamfbConfig { + address: u64, + fourcc: u32, + flags: u32, + width: u32, + height: u32, + stride: u32, +} + +fn write_ramfb_config( + allocator: &mut GuestAllocator, + test_state: &TestState, + file_name: &str, + framebuffer_base: u64, + fourcc: u32, + flags: u32, + width: u32, + height: u32, + ramfb_bpp: u32, +) { + let mut ramfb_config = RamfbConfig::default(); + ramfb_config.address = swap_u64(framebuffer_base); + ramfb_config.fourcc = swap_u32(fourcc); + ramfb_config.flags = swap_u32(flags); + ramfb_config.width = swap_u32(width); + ramfb_config.height = swap_u32(height); + ramfb_config.stride = swap_u32(width * ramfb_bpp); + + let ramfb_config_addr = allocator.alloc(mem::size_of::() as u64); + test_state.writeq(ramfb_config_addr, ramfb_config.address); + test_state.writel(ramfb_config_addr + 8, ramfb_config.fourcc); + test_state.writel(ramfb_config_addr + 12, ramfb_config.flags); + test_state.writel(ramfb_config_addr + 16, ramfb_config.width); + test_state.writel(ramfb_config_addr + 20, ramfb_config.height); + test_state.writel(ramfb_config_addr + 24, ramfb_config.stride); + + let access = allocator.alloc(mem::size_of::() as u64); + test_state.fw_cfg_write_file( + allocator, + file_name, + access, + ramfb_config_addr, + mem::size_of::() as u32, + ); +} + +#[test] +fn test_basic() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let ramfb_args = String::from("-device ramfb,id=ramfb1"); + args.append(&mut ramfb_args[..].split(' ').collect()); + let log_path = "/tmp/ramfb_test_basic.log"; + let log_args = format!("-D {}", log_path); + args.append(&mut log_args[..].split(' ').collect()); + + if Path::new(log_path).exists() { + fs::remove_file(log_path).unwrap(); + } + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let file_name = "etc/ramfb"; + let framebuffer_base = allocator.borrow_mut().alloc(FRAMEBUFFER_SIZE); + + let mut file_contents = String::from(""); + match fs::File::create(log_path) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + framebuffer_base, + RAMFB_FORMAT, + 0, + HORIZONTAL_RESOLUTION, + VERTICAL_RESOLUTION, + RAMFB_BPP, + ); + + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + assert!( + file_contents + .find("ERROR: Failed to create image of ramfb!") + .is_none(), + "Failed to create image!" + ); + assert!(file_contents.find("ERROR").is_none(), "Unexpected error!"); + + match fs::remove_file(log_path) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + test_state.borrow_mut().stop(); +} + +#[test] +fn test_abnormal_param() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + let ramfb_args = String::from("-device ramfb,id=ramfb1"); + args.append(&mut ramfb_args[..].split(' ').collect()); + let log_path = "/tmp/ramfb_test_abnormal.log"; + let log_args = format!("-D {}", log_path); + args.append(&mut log_args[..].split(' ').collect()); + + if Path::new(log_path).exists() { + fs::remove_file(log_path).unwrap(); + } + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let file_name = "etc/ramfb"; + let framebuffer_base = allocator.borrow_mut().alloc(FRAMEBUFFER_SIZE); + let mut file_contents = String::from(""); + + match fs::File::create(log_path) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + + // Set frambuffer address is abnormal. + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + ABNORMAL_FB_BASE, + RAMFB_FORMAT, + 0, + HORIZONTAL_RESOLUTION, + VERTICAL_RESOLUTION, + RAMFB_BPP, + ); + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + assert!( + file_contents + .find("ERROR: Failed to get the host address of the framebuffer") + .is_some(), + "Failed to check framebuffer address!" + ); + + // Set drm format is unsupported. + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + framebuffer_base, + 0, + 0, + HORIZONTAL_RESOLUTION, + VERTICAL_RESOLUTION, + RAMFB_BPP, + ); + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + assert!( + file_contents + .find("ERROR: Unsupported drm format") + .is_some(), + "Failed to check Fourcc!" + ); + + // Set width = 15, which is less than the minimum. + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + framebuffer_base, + RAMFB_FORMAT, + 0, + 15, + VERTICAL_RESOLUTION, + RAMFB_BPP, + ); + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + let mut err_msg = format!( + "ERROR: The resolution: {}x{} is unsupported", + 15, VERTICAL_RESOLUTION + ); + assert!( + file_contents.find(&err_msg).is_some(), + "Failed to check min width!" + ); + + // Set width = 16001, which is exceeded the maximum. + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + framebuffer_base, + RAMFB_FORMAT, + 0, + 16001, + VERTICAL_RESOLUTION, + RAMFB_BPP, + ); + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + err_msg = format!( + "ERROR: The resolution: {}x{} is unsupported", + 16001, VERTICAL_RESOLUTION + ); + assert!( + file_contents.find(&err_msg).is_some(), + "Failed to check max width!" + ); + + // Set height = 15, which is less than the minimum. + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + framebuffer_base, + RAMFB_FORMAT, + 0, + HORIZONTAL_RESOLUTION, + 15, + RAMFB_BPP, + ); + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + err_msg = format!( + "ERROR: The resolution: {}x{} is unsupported", + HORIZONTAL_RESOLUTION, 15 + ); + assert!( + file_contents.find(&err_msg).is_some(), + "Failed to check min height!" + ); + + // Set height = 16001, which is exceeded the maximum. + write_ramfb_config( + &mut allocator.borrow_mut(), + &test_state.borrow(), + file_name, + framebuffer_base, + RAMFB_FORMAT, + 0, + HORIZONTAL_RESOLUTION, + 12001, + RAMFB_BPP, + ); + match fs::read_to_string(log_path) { + Ok(contents) => file_contents = contents, + Err(e) => assert!(false, "{}", e), + } + err_msg = format!( + "ERROR: The resolution: {}x{} is unsupported", + HORIZONTAL_RESOLUTION, 12001 + ); + assert!( + file_contents.find(&err_msg).is_some(), + "Failed to check max height!" + ); + + match fs::remove_file(log_path) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + test_state.borrow_mut().stop(); +} -- Gitee From 5a0d4bc7f10575e62b2253ed5eb7aeb9eb498f19 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 22 Feb 2023 22:45:59 +0800 Subject: [PATCH 0815/2187] tests: Add general methods for devices Signed-off-by: Gan Qixin --- tests/mod_test/src/libdriver/fw_cfg.rs | 152 ++++ tests/mod_test/src/libdriver/mod.rs | 5 + tests/mod_test/src/libdriver/pci.rs | 315 ++++++++ tests/mod_test/src/libdriver/virtio.rs | 597 +++++++++++++++ .../mod_test/src/libdriver/virtio_console.rs | 89 +++ tests/mod_test/src/libdriver/virtio_net.rs | 86 +++ .../src/libdriver/virtio_pci_modern.rs | 719 ++++++++++++++++++ 7 files changed, 1963 insertions(+) create mode 100644 tests/mod_test/src/libdriver/fw_cfg.rs create mode 100644 tests/mod_test/src/libdriver/pci.rs create mode 100644 tests/mod_test/src/libdriver/virtio.rs create mode 100644 tests/mod_test/src/libdriver/virtio_console.rs create mode 100644 tests/mod_test/src/libdriver/virtio_net.rs create mode 100644 tests/mod_test/src/libdriver/virtio_pci_modern.rs diff --git a/tests/mod_test/src/libdriver/fw_cfg.rs b/tests/mod_test/src/libdriver/fw_cfg.rs new file mode 100644 index 000000000..92157e00c --- /dev/null +++ b/tests/mod_test/src/libdriver/fw_cfg.rs @@ -0,0 +1,152 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem; + +use devices::legacy::FwCfgEntryType; + +use super::malloc::GuestAllocator; +use crate::libtest::TestState; + +#[cfg(target_arch = "aarch64")] +pub const FW_CFG_BASE: u64 = 0x09020000; +#[cfg(target_arch = "x86_64")] +const FW_CFG_BASE: u64 = 0x510; + +const FW_CFG_FNAME_SIZE: usize = 56; + +#[repr(C)] +pub struct FwCfgDmaAccess { + control: u32, + length: u32, + address: u64, +} + +pub fn base_args(base_args: &mut Vec<&str>) { + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + base_args.append(&mut args); + args = "-drive file=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true" + .split(' ') + .collect(); + base_args.append(&mut args); +} + +pub fn swap_u16(value: u16) -> u16 { + value << 8 | value >> 8 +} + +pub fn swap_u32(value: u32) -> u32 { + let lower_u16 = swap_u16(value as u16) as u32; + let higher_u16 = swap_u16((value >> 16) as u16) as u32; + lower_u16 << 16 | higher_u16 +} + +pub fn swap_u64(value: u64) -> u64 { + let lower_u32 = swap_u32(value as u32) as u64; + let higher_u32 = swap_u32((value >> 32) as u32) as u64; + lower_u32 << 32 | higher_u32 +} + +impl TestState { + pub fn fw_cfg_read_bytes(&self, key: u16, data: &mut Vec, len: u32) { + self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); + for _i in 0..len { + data.push(self.readb(FW_CFG_BASE)) + } + } + + pub fn fw_cfg_read_u16(&self, key: u16) -> u16 { + self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); + self.readw(FW_CFG_BASE) + } + + pub fn fw_cfg_read_u32(&self, key: u16) -> u32 { + self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); + self.readl(FW_CFG_BASE) + } + + pub fn dma_transfer_bytes(&self, access: u64, buff: u64, size: u32, ctrl: u32) { + self.writel(access, swap_u32(ctrl)); + self.writel(access + 4, swap_u32(size)); + self.writeq(access + 8, swap_u64(buff)); + + self.writeq(FW_CFG_BASE + 0x10, swap_u64(access)); + } + + pub fn fw_cfg_read_file( + &self, + allocator: &mut GuestAllocator, + file_name: &str, + data: &mut Vec, + data_len: u32, + ) -> u32 { + let file_name_len = file_name.to_string().len(); + let mut file_size = 0; + let mut name: [u8; FW_CFG_FNAME_SIZE] = [0; FW_CFG_FNAME_SIZE]; + let buff = allocator.alloc(FW_CFG_FNAME_SIZE as u64); + let access = allocator.alloc(mem::size_of::() as u64); + + self.writew(FW_CFG_BASE + 0x8, swap_u16(FwCfgEntryType::FileDir as u16)); + let count = swap_u32(self.readl(FW_CFG_BASE)); + for _i in 0..count { + let mut size = swap_u32(self.readl(FW_CFG_BASE)); + let select = swap_u16(self.readw(FW_CFG_BASE)); + let _reserved = swap_u16(self.readw(FW_CFG_BASE)); + // Read file name by DMA. + self.dma_transfer_bytes(access, buff, FW_CFG_FNAME_SIZE as u32, 2); + for (i, n) in name.iter_mut().enumerate().take(FW_CFG_FNAME_SIZE) { + *n = self.readb(buff + i as u64); + } + if String::from_utf8_lossy(&name[0..file_name_len]).eq(file_name) { + file_size = size; + if size > data_len { + size = data_len; + } + self.fw_cfg_read_bytes(select, data, size); + break; + } + } + file_size + } + + pub fn fw_cfg_write_file( + &self, + allocator: &mut GuestAllocator, + file_name: &str, + data_access: u64, + data_addr: u64, + data_len: u32, + ) { + let file_name_len = file_name.to_string().len(); + let mut name: [u8; FW_CFG_FNAME_SIZE] = [0; FW_CFG_FNAME_SIZE]; + let buff = allocator.alloc(FW_CFG_FNAME_SIZE as u64); + let access = allocator.alloc(mem::size_of::() as u64); + + self.writew(FW_CFG_BASE + 0x8, swap_u16(FwCfgEntryType::FileDir as u16)); + let count = swap_u32(self.readl(FW_CFG_BASE)); + for _i in 0..count { + let _size = swap_u32(self.readl(FW_CFG_BASE)); + let select = swap_u16(self.readw(FW_CFG_BASE)); + let _reserved = swap_u16(self.readw(FW_CFG_BASE)); + // Read file name by DMA. + self.dma_transfer_bytes(access, buff, FW_CFG_FNAME_SIZE as u32, 2); + for (i, n) in name.iter_mut().enumerate().take(FW_CFG_FNAME_SIZE) { + *n = self.readb(buff + i as u64); + } + if String::from_utf8_lossy(&name[0..file_name_len]).eq(file_name) { + self.writew(FW_CFG_BASE + 0x8, swap_u16(select)); + self.dma_transfer_bytes(data_access, data_addr, data_len, 16); + break; + } + } + } +} diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 2bc4e0de8..1767393e7 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -13,4 +13,9 @@ pub mod fwcfg; pub mod machine; pub mod malloc; +pub mod pci; pub mod pci_bus; +pub mod virtio; +pub mod virtio_console; +pub mod virtio_net; +pub mod virtio_pci_modern; diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs new file mode 100644 index 000000000..599aa9073 --- /dev/null +++ b/tests/mod_test/src/libdriver/pci.rs @@ -0,0 +1,315 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::pci_bus::PciBusOps; +use super::pci_bus::TestPciBus; +use std::cell::RefCell; +use std::rc::Rc; + +const BAR_MAP: [u8; 6] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; +pub const PCI_VENDOR_ID: u8 = 0x00; +pub const PCI_DEVICE_ID: u8 = 0x02; +const PCI_COMMAND: u8 = 0x04; +const PCI_COMMAND_IO: u8 = 0x1; +const PCI_COMMAND_MEMORY: u8 = 0x2; +const PCI_COMMAND_MASTER: u8 = 0x4; + +pub const PCI_SUBSYSTEM_ID: u8 = 0x2e; +const PCI_CAPABILITY_LIST: u8 = 0x34; + +const PCI_CAP_LIST_NEXT: u8 = 1; +pub const PCI_CAP_ID_VNDR: u8 = 0x09; +const PCI_CAP_ID_MSIX: u8 = 0x11; + +const PCI_MSIX_FLAGS: u8 = 2; +const PCI_MSIX_FLAGS_QSIZE: u16 = 0x07FF; +const PCI_MSIX_TABLE: u8 = 4; +const PCI_MSIX_PBA: u8 = 8; +const PCI_MSIX_FLAGS_MASKALL: u16 = 0x4000; +const PCI_MSIX_FLAGS_ENABLE: u16 = 0x8000; +const PCI_MSIX_TABLE_BIR: u32 = 0x00000007; + +const PCI_MSIX_ENTRY_SIZE: u16 = 16; +pub const PCI_MSIX_ENTRY_LOWER_ADDR: u64 = 0x0; +pub const PCI_MSIX_ENTRY_UPPER_ADDR: u64 = 0x4; +pub const PCI_MSIX_ENTRY_DATA: u64 = 0x8; +pub const PCI_MSIX_ENTRY_VECTOR_CTRL: u64 = 0xc; +pub const PCI_MSIX_ENTRY_CTRL_MASKBIT: u32 = 0x00000001; +pub type PCIBarAddr = u64; + +pub trait PciMsixOps { + fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32); +} + +pub struct TestPciDev { + pub pci_bus: Rc>, + pub devfn: u32, + pub msix_enabled: bool, + pub msix_table_bar: PCIBarAddr, + msix_pba_bar: PCIBarAddr, + pub msix_table_off: u64, + msix_pba_off: u64, + pub msix_used_vectors: u32, +} + +impl TestPciDev { + pub fn new(pci_bus: Rc>) -> Self { + Self { + pci_bus, + devfn: 0, + msix_enabled: false, + msix_table_bar: 0, + msix_pba_bar: 0, + msix_table_off: 0, + msix_pba_off: 0, + msix_used_vectors: 0, + } + } + + pub fn enable(&self) { + let mut cmd = self.config_readw(PCI_COMMAND); + cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER) as u16; + self.config_writew(PCI_COMMAND, cmd); + + cmd = self.config_readw(PCI_COMMAND); + assert!(cmd & PCI_COMMAND_IO as u16 == PCI_COMMAND_IO as u16); + assert!(cmd & PCI_COMMAND_MEMORY as u16 == PCI_COMMAND_MEMORY as u16); + assert!(cmd & PCI_COMMAND_MASTER as u16 == PCI_COMMAND_MASTER as u16); + } + + pub fn find_capability(&self, id: u8, start_addr: u8) -> u8 { + let mut addr = if start_addr != 0 { + self.config_readb(start_addr + PCI_CAP_LIST_NEXT) + } else { + self.config_readb(start_addr + PCI_CAPABILITY_LIST) + }; + + loop { + let cap = self.config_readb(addr); + if cap != id { + addr = self.config_readb(addr + PCI_CAP_LIST_NEXT); + } + if cap == id || addr == 0 { + break; + } + } + addr + } + + pub fn enable_msix(&mut self) { + let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); + assert!(addr != 0); + let value = self.config_readw(addr + PCI_MSIX_FLAGS); + self.config_writew(addr + PCI_MSIX_FLAGS, value | PCI_MSIX_FLAGS_ENABLE); + + let table = self.config_readl(addr + PCI_MSIX_TABLE); + let bar_table = table & PCI_MSIX_TABLE_BIR; + self.msix_table_bar = self.io_map(bar_table.try_into().unwrap()); + self.msix_table_off = (table & !PCI_MSIX_TABLE_BIR).try_into().unwrap(); + + let table = self.config_readl(addr + PCI_MSIX_PBA); + let bar_pba = table & PCI_MSIX_TABLE_BIR; + if bar_pba != bar_table { + self.msix_pba_bar = self.io_map(bar_pba.try_into().unwrap()); + } else { + self.msix_pba_bar = self.msix_table_bar; + } + self.msix_pba_off = (table & !PCI_MSIX_TABLE_BIR).try_into().unwrap(); + self.msix_enabled = true; + } + + pub fn disable_msix(&mut self) { + let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); + assert!(addr != 0); + let value = self.config_readw(addr + PCI_MSIX_FLAGS); + self.config_writew(addr + PCI_MSIX_FLAGS, value & !PCI_MSIX_FLAGS_ENABLE); + + self.msix_enabled = false; + self.msix_table_off = 0; + self.msix_pba_off = 0; + } + + pub fn msix_is_pending(&self, entry: u16) -> bool { + assert!(self.msix_enabled); + let bit = entry % 32; + let offset = (entry / 32) * 4; + + let pba_entry = self.io_readl(self.msix_pba_bar, self.msix_pba_off + offset as u64); + self.io_writel( + self.msix_pba_bar, + self.msix_pba_off + offset as u64, + pba_entry & !(1 << bit), + ); + (pba_entry & (1 << bit)) != 0 + } + + pub fn msix_is_masked(&self, entry: u16) -> bool { + assert!(self.msix_enabled); + let offset: u64 = self.msix_table_off + (entry * PCI_MSIX_ENTRY_SIZE) as u64; + + let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); + assert!(addr != 0); + let value = self.config_readw(addr + PCI_MSIX_FLAGS); + + if (value & PCI_MSIX_FLAGS_MASKALL) != 0 { + true + } else { + self.io_readl( + self.msix_table_bar, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + ) != 0 + } + } + + pub fn get_msix_table_size(&self) -> u16 { + let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); + assert!(addr != 0); + + let value = self.config_readw(addr + PCI_MSIX_FLAGS); + (value & PCI_MSIX_FLAGS_QSIZE) + 1 + } + + pub fn io_readb(&self, bar_addr: PCIBarAddr, offset: u64) -> u8 { + let pci_bus = self.pci_bus.borrow_mut(); + let value_bytes = pci_bus.memread((bar_addr + offset) as u32, 1); + assert!(!value_bytes.is_empty()); + u8::from_le_bytes(value_bytes[0..1].try_into().unwrap()) + } + + pub fn io_readw(&self, bar_addr: PCIBarAddr, offset: u64) -> u16 { + let pci_bus = self.pci_bus.borrow_mut(); + let value_bytes = pci_bus.memread((bar_addr + offset) as u32, 2); + assert!(value_bytes.len() >= 2); + u16::from_le_bytes(value_bytes[0..2].try_into().unwrap()) + } + + pub fn io_readl(&self, bar_addr: PCIBarAddr, offset: u64) -> u32 { + let pci_bus = self.pci_bus.borrow_mut(); + let value_bytes = pci_bus.memread((bar_addr + offset) as u32, 4); + assert!(value_bytes.len() >= 4); + u32::from_le_bytes(value_bytes[0..4].try_into().unwrap()) + } + + pub fn io_readq(&self, bar_addr: PCIBarAddr, offset: u64) -> u64 { + let pci_bus = self.pci_bus.borrow_mut(); + let value_bytes = pci_bus.memread((bar_addr + offset) as u32, 8); + assert!(value_bytes.len() >= 8); + u64::from_le_bytes(value_bytes[0..8].try_into().unwrap()) + } + + pub fn io_writeb(&self, bar_addr: PCIBarAddr, offset: u64, value: u8) { + let value_buf = value.to_le_bytes().to_vec(); + let pci_bus = self.pci_bus.borrow_mut(); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + } + + pub fn io_writew(&self, bar_addr: PCIBarAddr, offset: u64, value: u16) { + let value_buf = value.to_le_bytes().to_vec(); + let pci_bus = self.pci_bus.borrow_mut(); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + } + + pub fn io_writel(&self, bar_addr: PCIBarAddr, offset: u64, value: u32) { + let value_buf = value.to_le_bytes().to_vec(); + let pci_bus = self.pci_bus.borrow_mut(); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + } + + #[allow(unused)] + pub fn io_writeq(&self, bar_addr: PCIBarAddr, offset: u64, value: u64) { + let value_buf = value.to_le_bytes().to_vec(); + let pci_bus = self.pci_bus.borrow_mut(); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + } + + pub fn io_map(&self, barnum: u8) -> u64 { + let addr: u32; + let size: u64; + let location: u64; + let bar_addr: PCIBarAddr; + + assert!(barnum <= 5); + let bar_offset: u8 = BAR_MAP[barnum as usize]; + + self.config_writel(bar_offset, 0xFFFFFFFF); + addr = self.config_readl(bar_offset) & !(0x0F_u32); + assert!(addr != 0); + + size = 1 << addr.trailing_zeros(); + location = (self.pci_bus.borrow().mmio_alloc_ptr + size - 1) / size * size; + assert!(location >= self.pci_bus.borrow().mmio_alloc_ptr); + assert!(location + size <= self.pci_bus.borrow().mmio_limit); + self.pci_bus.borrow_mut().mmio_alloc_ptr = location + size; + self.config_writel(bar_offset, location as u32); + bar_addr = location; + bar_addr + } + + pub fn config_readb(&self, offset: u8) -> u8 { + self.pci_bus.borrow().config_readb(self.devfn, offset) + } + + pub fn config_readw(&self, offset: u8) -> u16 { + self.pci_bus.borrow().config_readw(self.devfn, offset) + } + + pub fn config_readl(&self, offset: u8) -> u32 { + self.pci_bus.borrow().config_readl(self.devfn, offset) + } + + #[allow(unused)] + pub fn config_writeb(&self, offset: u8, value: u8) { + self.pci_bus + .borrow() + .config_writeb(self.devfn, offset, value); + } + + pub fn config_writew(&self, offset: u8, value: u16) { + self.pci_bus + .borrow() + .config_writew(self.devfn, offset, value); + } + + pub fn config_writel(&self, offset: u8, value: u32) { + self.pci_bus + .borrow() + .config_writel(self.devfn, offset, value); + } +} + +impl PciMsixOps for TestPciDev { + fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32) { + assert!(self.msix_enabled); + assert!(msix_entry <= self.get_msix_table_size()); + let offset = self.msix_table_off + (msix_entry * 16) as u64; + + let msix_table_bar = self.msix_table_bar; + self.io_writel( + msix_table_bar, + offset + PCI_MSIX_ENTRY_LOWER_ADDR, + msix_addr.try_into().unwrap(), + ); + self.io_writel( + msix_table_bar, + offset + PCI_MSIX_ENTRY_UPPER_ADDR, + (msix_addr >> 32).try_into().unwrap(), + ); + self.io_writel(msix_table_bar, offset + PCI_MSIX_ENTRY_DATA, msix_data); + + let ctl = self.io_readl(msix_table_bar, offset + PCI_MSIX_ENTRY_VECTOR_CTRL); + self.io_writel( + msix_table_bar, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL, + ctl & !PCI_MSIX_ENTRY_CTRL_MASKBIT, + ); + } +} diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs new file mode 100644 index 000000000..be034dc12 --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -0,0 +1,597 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::malloc::GuestAllocator; +use crate::libtest::TestState; +use std::cell::RefCell; +use std::mem::size_of; +use std::rc::Rc; +use std::time; +use util::byte_code::ByteCode; +use util::num_ops::round_up; +use util::offset_of; + +pub const VIRTIO_F_BAD_FEATURE: u64 = 0x40000000; +pub const VIRTIO_F_VERSION_1: u64 = 32; +pub const VIRTIO_CONFIG_S_ACKNOWLEDGE: u8 = 1; +pub const VIRTIO_CONFIG_S_DRIVER: u8 = 2; +pub const VIRTIO_CONFIG_S_DRIVER_OK: u8 = 4; +pub const VIRTIO_CONFIG_S_FEATURES_OK: u8 = 8; +pub const VIRTIO_CONFIG_S_NEEDS_RESET: u8 = 0x40; +pub const VIRTIO_CONFIG_S_FAILED: u8 = 0x80; +pub const VRING_DESC_F_NEXT: u16 = 1; +pub const VRING_DESC_F_WRITE: u16 = 2; +pub const VRING_DESC_F_INDIRECT: u16 = 4; +pub const VRING_USED_F_NO_NOTIFY: u16 = 1; +pub const VIRTIO_PCI_VRING_ALIGN: u32 = 4096; +pub const VIRTIO_F_NOTIFY_ON_EMPTY: u64 = 24; +pub const VIRTIO_RING_F_INDIRECT_DESC: u64 = 28; +pub const VIRTIO_RING_F_EVENT_IDX: u64 = 29; +/// When host consumes a buffer, don't interrupt the guest. +pub const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; + +pub trait VirtioDeviceOps { + fn config_readb(&self, addr: u64) -> u8; + fn config_readw(&self, addr: u64) -> u16; + fn config_readl(&self, addr: u64) -> u32; + fn config_readq(&self, addr: u64) -> u64; + fn config_writeb(&self, addr: u64, value: u8); + fn config_writew(&self, addr: u64, value: u16); + fn config_writel(&self, addr: u64, value: u32); + fn config_writeq(&self, addr: u64, value: u64); + fn enable_interrupt(&mut self); + fn disable_interrupt(&mut self); + fn get_device_features(&self) -> u64; + fn set_guest_features(&self, features: u64); + fn get_guest_features(&self) -> u64; + fn get_status(&self) -> u8; + fn set_status(&self, status: u8); + fn get_queue_nums(&self) -> u16; + fn queue_select(&self, index: u16); + fn get_queue_select(&self) -> u16; + fn set_queue_size(&self, size: u16); + fn get_queue_size(&self) -> u16; + fn activate_queue(&self, desc: u64, avail: u64, used: u64); + fn queue_was_notified(&self, virtqueue: Rc>) -> bool; + fn setup_virtqueue( + &self, + test_state: Rc>, + alloc: Rc>, + index: u16, + ) -> Rc>; + fn cleanup_virtqueue(&self, alloc: Rc>, desc_addr: u64); + fn init_virtqueue( + &mut self, + test_state: Rc>, + alloc: Rc>, + num_queues: usize, + ) -> Vec>>; + fn virtqueue_notify(&self, virtqueue: Rc>); + fn kick_virtqueue( + &self, + test_state: Rc>, + virtqueue: Rc>, + ); + fn poll_used_elem( + &self, + test_state: Rc>, + virtqueue: Rc>, + desc_idx: u32, + timeout_us: u64, + len: &mut Option, + wait_notified: bool, + ); + fn init_device( + &mut self, + test_state: Rc>, + alloc: Rc>, + features: u64, + num_queues: usize, + ) -> Vec>>; + fn destroy_device( + &mut self, + alloc: Rc>, + vqs: Vec>>, + ); + fn reset(&mut self); + fn negotiate_features(&mut self, features: u64); + fn set_features_ok(&mut self); + fn set_driver_ok(&self); + + fn start(&mut self) { + self.reset(); + self.set_acknowledge(); + self.set_driver(); + } + + fn set_acknowledge(&self) { + let status = self.get_status() | VIRTIO_CONFIG_S_ACKNOWLEDGE; + self.set_status(status); + + assert_eq!(self.get_status(), status); + } + + fn set_driver(&self) { + let status = self.get_status() | VIRTIO_CONFIG_S_DRIVER; + self.set_status(status); + + assert_eq!(self.get_status(), status); + } + + fn req_result(&self, test_state: Rc>, addr: u64, timeout_us: u64) -> u8 { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(timeout_us); + let mut value = test_state.borrow().readb(addr); + + while value == 0xFF { + test_state.borrow().clock_step_ns(100); + assert!( + time::Instant::now() - start_time <= timeout_us, + "The device failed to process the request!" + ); + value = test_state.borrow().readb(addr); + } + + value + } +} + +#[repr(C, packed(16))] +#[derive(Default, Copy, Clone, Debug)] +pub struct VringDesc { + addr: u64, + len: u32, + flags: u16, + next: u16, +} + +impl ByteCode for VringDesc {} +pub static VRING_DESC_SIZE: u64 = size_of::() as u64; + +#[allow(unused)] +#[repr(C, packed(2))] +pub struct VringAvail { + flags: u16, + idx: u16, + ring: Vec, +} + +#[repr(C, packed(4))] +pub struct VringUsedElem { + id: u32, + len: u32, +} + +#[repr(C, packed(4))] +pub struct VringUsed { + flags: u16, + idx: u16, + ring: Vec, +} + +#[allow(unused)] +struct Vring { + num: u32, + desc: VringDesc, + avail: VringAvail, + used: VringUsed, +} + +#[derive(Default)] +pub struct TestVringIndirectDesc { + pub desc: u64, + pub index: u16, + pub elem: u16, +} + +#[derive(Default)] +pub struct TestVringDescEntry { + pub data: u64, + pub len: u32, + pub write: bool, +} + +impl TestVringIndirectDesc { + pub fn new() -> Self { + Self { + ..Default::default() + } + } + + pub fn setup( + &mut self, + alloc: Rc>, + test_state: Rc>, + elem: u16, + ) { + self.elem = elem; + self.desc = alloc + .borrow_mut() + .alloc((size_of::() * elem as usize).try_into().unwrap()); + + for i in 0..elem - 1 { + test_state + .borrow() + .writeq(self.desc + (size_of::() * i as usize) as u64, 0); + + test_state.borrow().writel( + self.desc + + (size_of::() * i as usize + offset_of!(VringDesc, len)) as u64, + 0, + ); + + test_state.borrow().writew( + self.desc + + (size_of::() * i as usize + offset_of!(VringDesc, flags)) as u64, + VRING_DESC_F_NEXT, + ); + + test_state.borrow().writew( + self.desc + + (size_of::() * i as usize + offset_of!(VringDesc, next)) as u64, + i + 1, + ); + } + } + + pub fn add_desc( + &mut self, + test_state: Rc>, + data: u64, + len: u32, + write: bool, + ) { + assert!(self.index < self.elem); + + let mut flags = test_state.borrow().readw( + self.desc + + (size_of::() as u64 * self.index as u64) + + offset_of!(VringDesc, flags) as u64, + ); + + if write { + flags |= VRING_DESC_F_WRITE; + } + + test_state.borrow().writeq( + self.desc + (size_of::() * self.index as usize) as u64, + data, + ); + test_state.borrow().writel( + self.desc + + (size_of::() * self.index as usize + offset_of!(VringDesc, len)) + as u64, + len, + ); + test_state.borrow().writew( + self.desc + + (size_of::() * self.index as usize + offset_of!(VringDesc, flags)) + as u64, + flags, + ); + + self.index += 1; + } + + pub fn set_desc_flag(&mut self, test_state: Rc>, idx: u64, flag: u16) { + test_state.borrow().writew( + self.desc + + (size_of::() * idx as usize + offset_of!(VringDesc, flags)) as u64, + flag, + ); + } +} + +#[derive(Default)] +pub struct TestVirtQueue { + pub desc: u64, + pub avail: u64, + pub used: u64, + pub index: u16, + pub size: u32, + pub free_head: u32, + pub num_free: u32, + pub align: u32, + last_used_idx: u16, + pub indirect: bool, + pub event: bool, + pub msix_entry: u16, + pub msix_addr: u64, + pub msix_data: u32, + pub queue_notify_off: u64, +} + +impl TestVirtQueue { + pub fn new() -> Self { + Self { + ..Default::default() + } + } + + pub fn vring_init(&self, test_state: Rc>) { + // desc[i]->addr = 0, desc[i]->next = i + 1; + for i in 0..self.size - 1 { + test_state + .borrow() + .writeq(self.desc + (size_of::() * i as usize) as u64, 0); + test_state.borrow().writew( + self.desc + + (size_of::() * i as usize + offset_of!(VringDesc, next)) as u64, + (i + 1).try_into().unwrap(), + ); + } + + // virtqueue.avail.flags + test_state.borrow().writew(self.avail, 0); + // virtqueue.avail.idx + test_state + .borrow() + .writew(self.avail + offset_of!(VringAvail, idx) as u64, 0); + // virtqueue.avail.used_event + test_state.borrow().writew( + self.avail + + offset_of!(VringAvail, ring) as u64 + + (size_of::() * self.size as usize) as u64, + 0, + ); + + // virtqueue.used.flags + test_state.borrow().writew(self.used, 0); + // virtqueue.used.idx + test_state + .borrow() + .writew(self.used + offset_of!(VringUsed, idx) as u64, 0); + // virtqueue.used.avail_event + test_state.borrow().writew( + self.used + + offset_of!(VringUsed, ring) as u64 + + (size_of::() as u64 * self.size as u64), + 0, + ); + } + + pub fn setup( + &mut self, + virtio_dev: &dyn VirtioDeviceOps, + alloc: Rc>, + index: u16, + ) { + let num_queues = virtio_dev.get_queue_nums(); + assert!(index <= num_queues); + + let features = virtio_dev.get_guest_features(); + virtio_dev.queue_select(index); + + let queue_size = virtio_dev.get_queue_size().try_into().unwrap(); + assert!(queue_size != 0); + assert!(queue_size & (queue_size - 1) == 0); + + self.index = index; + self.size = queue_size; + self.free_head = 0; + self.num_free = self.size; + self.align = VIRTIO_PCI_VRING_ALIGN; + self.indirect = (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) != 0; + self.event = (features & (1 << VIRTIO_RING_F_EVENT_IDX)) != 0; + + let addr = alloc + .borrow_mut() + .alloc(get_vring_size(self.size, self.align) as u64); + self.desc = addr; + self.avail = self.desc + (self.size * size_of::() as u32) as u64; + self.used = round_up( + self.avail + (size_of::() as u32 * (3 + self.size)) as u64, + self.align as u64, + ) + .unwrap(); + } + + pub fn get_buf( + &mut self, + test_state: Rc>, + desc_idx: &mut Option, + len: &mut Option, + ) -> bool { + let mut ret = false; + loop { + let index = test_state + .borrow() + .readw(self.used + offset_of!(VringUsed, idx) as u64); + if index == self.last_used_idx { + break; + } + + let elem_addr = self.used + + offset_of!(VringUsed, ring) as u64 + + (self.last_used_idx as u32 % self.size) as u64 + * size_of::() as u64; + + if let Some(id) = desc_idx.as_mut() { + let addr = elem_addr + offset_of!(VringUsedElem, id) as u64; + *id = test_state.borrow().readl(addr); + } + + if let Some(len) = len.as_mut() { + let addr = elem_addr + offset_of!(VringUsedElem, len) as u64; + *len = test_state.borrow().readl(addr); + } + + self.last_used_idx += 1; + ret = true; + } + ret + } + + pub fn get_avail_event(&self, test_state: Rc>) -> u16 { + assert!(self.event); + + test_state.borrow().readw( + self.used + + offset_of!(VringUsed, ring) as u64 + + (size_of::() as u64 * self.size as u64), + ) + } + + pub fn set_used_event(&self, test_state: Rc>, index: u16) { + test_state.borrow().writew( + self.avail + + offset_of!(VringAvail, ring) as u64 + + (size_of::() as u64 * self.size as u64), + index, + ); + } + + pub fn set_avail_flags(&self, test_state: Rc>, flags: u16) { + test_state + .borrow() + .writew(self.avail + offset_of!(VringAvail, flags) as u64, flags); + } + + fn set_avail_idx(&self, test_state: Rc>, index: u16) { + test_state + .borrow() + .writew(self.avail + offset_of!(VringAvail, idx) as u64, index); + } + + fn set_avail_ring(&self, test_state: Rc>, desc_idx: u16) { + let idx: u16 = test_state + .borrow() + .readw(self.avail + offset_of!(VringAvail, idx) as u64); + test_state.borrow().writew( + self.avail + + offset_of!(VringAvail, ring) as u64 + + (size_of::() * (idx as u32 % self.size) as usize) as u64, + desc_idx, + ); + } + + fn update_avail(&self, test_state: Rc>, desc_idx: u32) { + let idx: u16 = test_state + .borrow() + .readw(self.avail + offset_of!(VringAvail, idx) as u64); + // Update avail.used_event. + if self.event { + self.set_used_event(test_state.clone(), idx); + } + // avail.ring[idx] = desc_idx. + self.set_avail_ring(test_state.clone(), desc_idx as u16); + // Update avail.idx. + self.set_avail_idx(test_state, idx + 1); + } + + pub fn add( + &mut self, + test_state: Rc>, + data: u64, + len: u32, + write: bool, + ) -> u32 { + let free_head = self.free_head; + let mut flags: u16 = 0; + if write { + flags |= VRING_DESC_F_WRITE; + } + + let desc_elem = VringDesc { + addr: data, + len: len, + flags, + next: 0, + }; + self.add_elem_to_desc(test_state.clone(), desc_elem); + self.update_avail(test_state.clone(), free_head); + + free_head + } + + pub fn add_chained( + &mut self, + test_state: Rc>, + data_entries: Vec, + ) -> u32 { + let free_head = self.free_head; + + for (i, entry) in data_entries.iter().enumerate() { + let mut flags: u16 = 0; + let mut next_desc = 0; + if entry.write { + flags |= VRING_DESC_F_WRITE; + } + if i < data_entries.len() - 1 { + flags |= VRING_DESC_F_NEXT; + next_desc = self.free_head + 1; + } + + let desc_elem = VringDesc { + addr: entry.data, + len: entry.len, + flags, + next: next_desc as u16, + }; + self.add_elem_to_desc(test_state.clone(), desc_elem); + } + self.update_avail(test_state.clone(), free_head); + free_head + } + + pub fn add_indirect( + &mut self, + test_state: Rc>, + indirect: TestVringIndirectDesc, + ) -> u32 { + assert!(indirect.index >= indirect.elem); + + let free_head = self.free_head; + let desc_elem = VringDesc { + addr: indirect.desc, + len: size_of::() as u32 * indirect.elem as u32, + flags: VRING_DESC_F_INDIRECT, + next: 0, + }; + self.add_elem_to_desc(test_state.clone(), desc_elem); + self.update_avail(test_state.clone(), free_head); + free_head + } + + // Add a vring desc elem to desc table. + fn add_elem_to_desc(&mut self, test_state: Rc>, elem: VringDesc) { + self.num_free -= 1; + let desc_elem_addr = self.desc + VRING_DESC_SIZE * self.free_head as u64; + test_state + .borrow() + .memwrite(desc_elem_addr, elem.as_bytes(), VRING_DESC_SIZE); + self.free_head += 1; + } +} + +#[derive(Default)] +pub struct TestVirtioDev { + pub features: u64, + pub device_type: u16, + pub feature_negotiated: bool, +} + +impl TestVirtioDev { + pub fn new() -> Self { + Self { + ..Default::default() + } + } +} + +#[inline] +pub fn get_vring_size(num: u32, align: u32) -> u32 { + let desc_avail = + (size_of::() as u32 * num + size_of::() as u32 * (3 + num)) as u64; + let desc_avail_align = round_up(desc_avail, align as u64).unwrap() as u32; + desc_avail_align + size_of::() as u32 * 3 + size_of::() as u32 * num +} diff --git a/tests/mod_test/src/libdriver/virtio_console.rs b/tests/mod_test/src/libdriver/virtio_console.rs new file mode 100644 index 000000000..15850e90a --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio_console.rs @@ -0,0 +1,89 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::machine::TestStdMachine; +use super::malloc::GuestAllocator; +use super::virtio_pci_modern::TestVirtioPciDev; +use crate::libtest::{test_init, TestState}; +use std::cell::RefCell; +use std::rc::Rc; + +pub enum ChardevType { + Stdio, + Pty, + Socket { + path: String, + server: bool, + nowait: bool, + }, + File { + path: String, + }, +} + +pub fn create_console( + chardev_type: ChardevType, +) -> ( + Rc>, + Rc>, + Rc>, +) { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let serial_pci_args = format!( + "-device {},id=serial0,bus=pcie.0,addr={}.0", + "virtio-serial-pci", pci_slot + ); + extra_args.append(&mut serial_pci_args[..].split(' ').collect()); + + let chardev_args = match chardev_type { + ChardevType::Stdio => String::from("-chardev stdio,id=charconsole0"), + ChardevType::Pty => String::from("-chardev pty,id=charconsole0"), + ChardevType::Socket { + path, + server, + nowait, + } => { + let mut args = format!("-chardev socket,id=charconsole0,path={}", path); + if server { + args.push_str(",server") + } + if nowait { + args.push_str(",nowait") + } + args + } + ChardevType::File { path } => { + let args = format!("-chardev file,id=charconsole0,path={}", path); + args + } + }; + extra_args.append(&mut chardev_args.split(' ').collect()); + + let console_args = String::from("-device virtconsole,chardev=charconsole0,id=console0"); + extra_args.append(&mut console_args.split(' ').collect()); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let virtio_console = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); + + virtio_console.borrow_mut().init(pci_slot, pci_fn); + + (virtio_console, test_state, allocator) +} diff --git a/tests/mod_test/src/libdriver/virtio_net.rs b/tests/mod_test/src/libdriver/virtio_net.rs new file mode 100644 index 000000000..6d72f5061 --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio_net.rs @@ -0,0 +1,86 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::machine::TestStdMachine; +use super::malloc::GuestAllocator; +use super::virtio::{ + TestVirtQueue, VirtioDeviceOps, VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, + VIRTIO_RING_F_INDIRECT_DESC, +}; +use super::virtio_pci_modern::TestVirtioPciDev; +use crate::libtest::{test_init, TestState}; +use std::cell::RefCell; +use std::rc::Rc; + +const VIRTIO_NET_F_MQ: u64 = 22; + +#[allow(unused)] +pub fn virtio_net_setup( + net: Rc>, + test_state: Rc>, + alloc: Rc>, +) -> Vec>> { + let mut queue_num: u16; + let mut features = net.borrow().get_device_features(); + features &= + !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX); + net.borrow_mut().negotiate_features(features); + net.borrow_mut().set_features_ok(); + + if features & (1 << VIRTIO_NET_F_MQ) != 0 { + queue_num = net.borrow().config_readw(8) * 2; + } else { + queue_num = 2; + } + queue_num += 1; + + let queues = net.borrow_mut().init_virtqueue(test_state, alloc, 1); + net.borrow().set_driver_ok(); + queues +} + +#[allow(unused)] +pub fn create_net() -> ( + Rc>, + Rc>, + Rc>, + Vec>>, +) { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let blk_pci_args = format!( + "-device {},id=net0,netdev=netdev0,bus=pcie.0,addr={}.0,mq=on", + "virtio-net-pci", pci_slot + ); + args = blk_pci_args[..].split(' ').collect(); + extra_args.append(&mut args); + let blk_args = String::from("-netdev tap,id=netdev0,ifname=tap_mq,queues=4"); + args = blk_args.split(' ').collect(); + extra_args.append(&mut args); + println!("start args is {:?}", extra_args); + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let virtio_net = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); + + virtio_net.borrow_mut().init(pci_slot, pci_fn); + let virtio_net_queues = + virtio_net_setup(virtio_net.clone(), test_state.clone(), allocator.clone()); + + (virtio_net, test_state, allocator, virtio_net_queues) +} diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs new file mode 100644 index 000000000..c7da6a1e1 --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -0,0 +1,719 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::malloc::GuestAllocator; +use super::pci::{ + PCIBarAddr, PciMsixOps, TestPciDev, PCI_CAP_ID_VNDR, PCI_DEVICE_ID, PCI_SUBSYSTEM_ID, + PCI_VENDOR_ID, +}; +use super::pci_bus::TestPciBus; +use super::virtio::{ + TestVirtQueue, TestVirtioDev, VirtioDeviceOps, VIRTIO_CONFIG_S_DRIVER_OK, + VIRTIO_CONFIG_S_FEATURES_OK, VIRTIO_F_VERSION_1, +}; +use crate::libtest::TestState; +use std::cell::RefCell; +use std::rc::Rc; +use std::time::{Duration, Instant}; +use util::offset_of; + +const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1; +const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2; +const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3; +const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4; + +#[repr(C, packed)] +pub struct VirtioPciCap { + cap_vndr: u8, + cap_next: u8, + cap_len: u8, + cfg_type: u8, + bar: u8, + id: u8, + padding: [u8; 2], + offset: u32, + length: u32, +} + +#[repr(C, packed)] +pub struct VirtioPciNotifyCap { + cap: VirtioPciCap, + notify_off_multiplier: u32, +} + +#[repr(C, packed)] +pub struct VirtioPciCommonCfg { + device_feature_select: u32, + device_feature: u32, + guest_feature_select: u32, + guest_feature: u32, + msix_config: u16, + num_queues: u16, + device_status: u8, + config_generation: u8, + + queue_select: u16, + queue_size: u16, + queue_msix_vector: u16, + pub queue_enable: u16, + pub queue_notify_off: u16, + queue_desc_lo: u32, + queue_desc_hi: u32, + queue_avail_lo: u32, + queue_avail_hi: u32, + queue_used_lo: u32, + queue_used_hi: u32, +} + +pub trait VirtioPCIMSIXOps { + fn set_config_vector(&self, entry: u16); + fn set_queue_vector(&self, vq_idx: u16, vector: u16); +} + +pub struct TestVirtioPciDev { + pub pci_dev: TestPciDev, + pub bar: PCIBarAddr, + bar_idx: u8, + pub config_msix_entry: u16, + pub config_msix_addr: u64, + pub config_msix_data: u32, + pub common_base: u32, + isr_base: u32, + pub notify_base: u32, + pub notify_off_multiplier: u32, + device_base: u32, + pub virtio_dev: TestVirtioDev, +} + +impl TestVirtioPciDev { + pub fn new(pci_bus: Rc>) -> Self { + Self { + pci_dev: TestPciDev::new(pci_bus), + bar: 0, + bar_idx: 0, + config_msix_entry: 0, + config_msix_addr: 0, + config_msix_data: 0, + common_base: 0, + isr_base: 0, + notify_base: 0, + notify_off_multiplier: 0, + device_base: 0, + virtio_dev: TestVirtioDev::new(), + } + } + + pub fn init(&mut self, pci_slot: u8, pci_fn: u8) { + let devfn = (pci_slot << 3 | pci_fn) as u32; + assert!(self.find_pci_device(devfn)); + + let device_type = self.pci_device_type_probe().unwrap_or(0); + self.virtio_dev.device_type = device_type; + self.enable(); + self.start(); + } + + fn enable(&mut self) { + self.pci_dev.enable(); + self.bar = self.pci_dev.io_map(self.bar_idx); + } + + fn find_pci_device(&mut self, devfn: u32) -> bool { + self.pci_dev.devfn = devfn; + self.pci_dev.config_readw(PCI_VENDOR_ID) != 0xFFFF + } + + fn find_structure( + &self, + cfg_type: u8, + bar: &mut u8, + offset: &mut u32, + cfg_addr: Rc>>, + ) -> bool { + let mut addr: u8 = 0; + loop { + addr = self.pci_dev.find_capability(PCI_CAP_ID_VNDR, addr); + if addr == 0 { + break; + } + + let config_type = self + .pci_dev + .config_readb(addr + offset_of!(VirtioPciCap, cfg_type) as u8); + if config_type != cfg_type { + continue; + } + + *bar = self + .pci_dev + .config_readb(addr + offset_of!(VirtioPciCap, bar) as u8); + *offset = self + .pci_dev + .config_readl(addr + offset_of!(VirtioPciCap, offset) as u8); + + if cfg_addr.borrow().is_some() { + cfg_addr.borrow_mut().replace(addr); + } + + return true; + } + + false + } + + fn pci_device_type_probe(&mut self) -> Option { + let device_type; + let vendor_id = self.pci_dev.config_readw(PCI_VENDOR_ID); + if vendor_id != 0x1af4 { + return None; + } + + let device_id = self.pci_dev.config_readw(PCI_DEVICE_ID); + if !(0x1000..=0x107f).contains(&device_id) { + return None; + } + if device_id < 0x1040 { + device_type = self.pci_dev.config_readw(PCI_SUBSYSTEM_ID); + } else { + device_type = device_id - 0x1040; + } + + self.pci_layout_probe(); + Some(device_type) + } + + fn pci_layout_probe(&mut self) -> bool { + let mut bar: u8 = 0; + let notify_cfg_addr: Rc>> = Rc::new(RefCell::new(Some(0))); + + let mut offset: u32 = 0; + + if !self.find_structure( + VIRTIO_PCI_CAP_COMMON_CFG, + &mut bar, + &mut offset, + Rc::new(RefCell::new(None)), + ) { + return false; + } + self.common_base = offset; + self.bar_idx = bar; + + if !self.find_structure( + VIRTIO_PCI_CAP_ISR_CFG, + &mut bar, + &mut offset, + Rc::new(RefCell::new(None)), + ) { + return false; + } + self.isr_base = offset; + assert!(bar == self.bar_idx); + + if !self.find_structure( + VIRTIO_PCI_CAP_NOTIFY_CFG, + &mut bar, + &mut offset, + notify_cfg_addr.clone(), + ) { + return false; + } + self.notify_base = offset; + assert!(bar == self.bar_idx); + + self.notify_off_multiplier = self.pci_dev.config_readl( + notify_cfg_addr.borrow().unwrap() + + offset_of!(VirtioPciNotifyCap, notify_off_multiplier) as u8, + ); + + if !self.find_structure( + VIRTIO_PCI_CAP_DEVICE_CFG, + &mut bar, + &mut offset, + Rc::new(RefCell::new(None)), + ) { + return false; + } + self.device_base = offset; + assert!(bar == self.bar_idx); + + true + } + + pub fn setup_msix_configuration_vector( + &mut self, + alloc: Rc>, + entry: u16, + ) { + self.config_msix_entry = entry; + self.config_msix_data = 0x12345678; + self.config_msix_addr = alloc.borrow_mut().alloc(4); + + self.pci_dev.set_msix_vector( + self.config_msix_entry, + self.config_msix_addr, + self.config_msix_data, + ); + self.set_config_vector(self.config_msix_entry); + } + + fn has_msix(&self, msix_entry: u16, msix_addr: u64, msix_data: u32) -> bool { + assert!(msix_entry != 0xFFFF); + if self.pci_dev.msix_is_masked(msix_entry) { + return self.pci_dev.msix_is_pending(msix_entry); + } + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow() + .query_msix(msix_addr, msix_data) + } + + pub fn setup_virtqueue_intr( + &self, + entry: u16, + alloc: Rc>, + virtqueue: Rc>, + ) { + virtqueue.borrow_mut().msix_entry = entry; + virtqueue.borrow_mut().msix_addr = alloc.borrow_mut().alloc(4); + virtqueue.borrow_mut().msix_data = 0x12345678; + + self.pci_dev.set_msix_vector( + virtqueue.borrow().msix_entry, + virtqueue.borrow().msix_addr, + virtqueue.borrow().msix_data, + ); + self.set_queue_vector(virtqueue.borrow().index, entry); + } +} + +impl VirtioDeviceOps for TestVirtioPciDev { + fn config_readb(&self, addr: u64) -> u8 { + self.pci_dev + .io_readb(self.bar, self.device_base as u64 + addr) + } + + fn config_readw(&self, addr: u64) -> u16 { + self.pci_dev + .io_readw(self.bar, self.device_base as u64 + addr) + } + + fn config_readl(&self, addr: u64) -> u32 { + self.pci_dev + .io_readl(self.bar, self.device_base as u64 + addr) + } + + fn config_readq(&self, addr: u64) -> u64 { + self.pci_dev + .io_readq(self.bar, self.device_base as u64 + addr) + } + + #[allow(unused)] + fn config_writeb(&self, addr: u64, value: u8) { + self.pci_dev + .io_writeb(self.bar, self.device_base as u64 + addr, value) + } + + #[allow(unused)] + fn config_writew(&self, addr: u64, value: u16) { + self.pci_dev + .io_writew(self.bar, self.device_base as u64 + addr, value) + } + + #[allow(unused)] + fn config_writel(&self, addr: u64, value: u32) { + self.pci_dev + .io_writel(self.bar, self.device_base as u64 + addr, value) + } + + #[allow(unused)] + fn config_writeq(&self, addr: u64, value: u64) { + self.pci_dev + .io_writeq(self.bar, self.device_base as u64 + addr, value) + } + + fn enable_interrupt(&mut self) { + self.pci_dev.enable_msix(); + } + + fn disable_interrupt(&mut self) { + self.pci_dev.disable_msix(); + } + + fn get_device_features(&self) -> u64 { + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature_select) as u64, + 0, + ); + let lo: u64 = self.pci_dev.io_readl( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature) as u64, + ) as u64; + + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature_select) as u64, + 1, + ); + let hi: u64 = self.pci_dev.io_readl( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature) as u64, + ) as u64; + (hi << 32) | lo + } + + fn set_guest_features(&self, features: u64) { + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + 0, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + features as u32, + ); + + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + 1, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + (features >> 32) as u32, + ); + } + + fn get_guest_features(&self) -> u64 { + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + 0, + ); + let lo: u64 = self.pci_dev.io_readl( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + ) as u64; + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + 1, + ); + let hi: u64 = self.pci_dev.io_readl( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + ) as u64; + (hi << 32) | lo + } + + fn get_status(&self) -> u8 { + self.pci_dev.io_readb( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_status) as u64, + ) + } + + fn set_status(&self, status: u8) { + self.pci_dev.io_writeb( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_status) as u64, + status, + ) + } + + fn get_queue_nums(&self) -> u16 { + self.pci_dev.io_readw( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, num_queues) as u64, + ) + } + + fn queue_select(&self, index: u16) { + self.pci_dev.io_writew( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_select) as u64, + index, + ); + } + + fn get_queue_select(&self) -> u16 { + self.pci_dev.io_readw( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_select) as u64, + ) + } + + fn set_queue_size(&self, size: u16) { + self.pci_dev.io_writew( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_size) as u64, + size, + ) + } + + fn get_queue_size(&self) -> u16 { + self.pci_dev.io_readw( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_size) as u64, + ) + } + + fn activate_queue(&self, desc: u64, avail: u64, used: u64) { + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_desc_lo) as u64, + desc as u32, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_desc_hi) as u64, + (desc >> 32) as u32, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_avail_lo) as u64, + avail as u32, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_avail_hi) as u64, + (avail >> 32) as u32, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_used_lo) as u64, + used as u32, + ); + self.pci_dev.io_writel( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_used_hi) as u64, + (used >> 32) as u32, + ); + } + + fn poll_used_elem( + &self, + test_state: Rc>, + virtqueue: Rc>, + desc_idx: u32, + timeout_us: u64, + len: &mut Option, + wait_notified: bool, + ) { + let start_time = Instant::now(); + let timeout_us = Duration::from_micros(timeout_us); + + loop { + let mut got_desc_idx = Some(0); + + if (!wait_notified || self.queue_was_notified(virtqueue.clone())) + && virtqueue + .borrow_mut() + .get_buf(test_state.clone(), &mut got_desc_idx, len) + { + assert!(got_desc_idx.unwrap() == desc_idx); + return; + } + + assert!(Instant::now() - start_time < timeout_us); + } + } + + fn queue_was_notified(&self, virtqueue: Rc>) -> bool { + assert!(self.pci_dev.msix_enabled); + return self.has_msix( + virtqueue.borrow().msix_entry, + virtqueue.borrow().msix_addr, + virtqueue.borrow().msix_data, + ); + } + + fn setup_virtqueue( + &self, + test_state: Rc>, + alloc: Rc>, + index: u16, + ) -> Rc> { + let virtqueue = Rc::new(RefCell::new(TestVirtQueue::new())); + virtqueue.borrow_mut().setup(self, alloc, index); + virtqueue.borrow().vring_init(test_state); + + let desc = virtqueue.borrow().desc; + let avail = virtqueue.borrow().avail; + let used = virtqueue.borrow().used; + self.activate_queue(desc, avail, used); + + let notify_off = self.pci_dev.io_readw( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, + ); + + virtqueue.borrow_mut().queue_notify_off = + self.notify_base as u64 + notify_off as u64 * self.notify_off_multiplier as u64; + + self.pci_dev.io_writew( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, + 1, + ); + + virtqueue + } + + fn cleanup_virtqueue(&self, alloc: Rc>, desc_addr: u64) { + alloc.borrow_mut().free(desc_addr); + } + + fn virtqueue_notify(&self, virtqueue: Rc>) { + let index = virtqueue.borrow().index; + let notify_offset = virtqueue.borrow().queue_notify_off; + self.pci_dev.io_writew(self.bar, notify_offset, index); + } + + /// Notify the virtio device to process req. free_head is Head + /// of free buffer list of descriptor table. num_add is the number + /// of the io request added to the virtqueue. + fn kick_virtqueue( + &self, + test_state: Rc>, + virtqueue: Rc>, + ) { + let vq = virtqueue.borrow(); + let idx: u16 = test_state.borrow().readw(vq.avail + 2); + + if (!vq.event) || (idx >= vq.get_avail_event(test_state.clone()) + 1) { + self.virtqueue_notify(virtqueue.clone()); + } + } + + fn reset(&mut self) { + self.set_status(0); + assert_eq!(self.get_status(), 0); + self.virtio_dev.feature_negotiated = false; + } + + fn negotiate_features(&mut self, features: u64) { + self.virtio_dev.features = features; + self.set_guest_features(features); + } + + fn set_features_ok(&mut self) { + if (self.get_guest_features() & (1 << VIRTIO_F_VERSION_1)) != 0 { + let status: u8 = self.get_status() | VIRTIO_CONFIG_S_FEATURES_OK; + self.set_status(status); + assert_eq!(self.get_status(), status); + } + + self.virtio_dev.feature_negotiated = true; + } + + fn set_driver_ok(&self) { + let status = self.get_status() | VIRTIO_CONFIG_S_DRIVER_OK; + self.set_status(status); + assert_eq!(self.get_status(), status); + } + + fn init_virtqueue( + &mut self, + test_state: Rc>, + alloc: Rc>, + num_queues: usize, + ) -> Vec>> { + assert!(num_queues < (1 << 15)); + let mut virtqueues = Vec::new(); + for i in 0..num_queues { + let virtqueue = self.setup_virtqueue(test_state.clone(), alloc.clone(), i as u16); + self.setup_virtqueue_intr((i + 1) as u16, alloc.clone(), virtqueue.clone()); + virtqueues.push(virtqueue); + } + + virtqueues + } + + fn init_device( + &mut self, + test_state: Rc>, + alloc: Rc>, + features: u64, + num_queues: usize, + ) -> Vec>> { + // Reset device by write 0 to device status. + self.reset(); + self.set_acknowledge(); + self.set_driver(); + self.negotiate_features(features); + assert_eq!(self.get_guest_features(), features); + self.set_features_ok(); + // FIXME: add handling the specific device features as needed. + + self.pci_dev.enable_msix(); + self.setup_msix_configuration_vector(alloc.clone(), 0); + let vqs = self.init_virtqueue(test_state, alloc, num_queues); + + self.set_driver_ok(); + vqs + } + + fn destroy_device( + &mut self, + alloc: Rc>, + vqs: Vec>>, + ) { + self.reset(); + self.pci_dev.disable_msix(); + for vq in vqs.iter() { + self.cleanup_virtqueue(alloc.clone(), vq.borrow().desc); + } + } +} + +impl VirtioPCIMSIXOps for TestVirtioPciDev { + fn set_config_vector(&self, vector: u16) { + self.pci_dev.io_writew( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, msix_config) as u64, + vector, + ); + let vector_get: u16 = self.pci_dev.io_readw( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, msix_config) as u64, + ); + assert_eq!( + vector, vector_get, + "WARN: set config vector {}, get vector {}", + vector, vector_get + ); + } + + fn set_queue_vector(&self, vq_idx: u16, vector: u16) { + self.queue_select(vq_idx); + self.pci_dev.io_writew( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, + vector, + ); + let vector_get: u16 = self.pci_dev.io_readw( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, + ); + assert_eq!( + vector, vector_get, + "WARN: set queue vector {}, get vector {}", + vector, vector_get + ); + } +} -- Gitee From 63b1f4464a7da2b1997e0e2e807fa059c0533584 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 26 Feb 2023 18:25:17 +0800 Subject: [PATCH 0816/2187] MST: The ramfb test code is optimized The write_ramfb_config function has too many input parameters and contains large amount of repeated code. Signed-off-by: Jinhao Gao --- tests/mod_test/tests/ramfb_test.rs | 168 ++++++++++------------------- 1 file changed, 57 insertions(+), 111 deletions(-) diff --git a/tests/mod_test/tests/ramfb_test.rs b/tests/mod_test/tests/ramfb_test.rs index ed0b4b94b..2687b2549 100644 --- a/tests/mod_test/tests/ramfb_test.rs +++ b/tests/mod_test/tests/ramfb_test.rs @@ -40,41 +40,41 @@ struct RamfbConfig { stride: u32, } -fn write_ramfb_config( - allocator: &mut GuestAllocator, - test_state: &TestState, - file_name: &str, - framebuffer_base: u64, - fourcc: u32, - flags: u32, - width: u32, - height: u32, - ramfb_bpp: u32, -) { - let mut ramfb_config = RamfbConfig::default(); - ramfb_config.address = swap_u64(framebuffer_base); - ramfb_config.fourcc = swap_u32(fourcc); - ramfb_config.flags = swap_u32(flags); - ramfb_config.width = swap_u32(width); - ramfb_config.height = swap_u32(height); - ramfb_config.stride = swap_u32(width * ramfb_bpp); +impl RamfbConfig { + fn new(base: u64) -> Self { + RamfbConfig { + address: base, + fourcc: RAMFB_FORMAT, + flags: 0, + width: HORIZONTAL_RESOLUTION, + height: VERTICAL_RESOLUTION, + stride: RAMFB_BPP * HORIZONTAL_RESOLUTION, + } + } - let ramfb_config_addr = allocator.alloc(mem::size_of::() as u64); - test_state.writeq(ramfb_config_addr, ramfb_config.address); - test_state.writel(ramfb_config_addr + 8, ramfb_config.fourcc); - test_state.writel(ramfb_config_addr + 12, ramfb_config.flags); - test_state.writel(ramfb_config_addr + 16, ramfb_config.width); - test_state.writel(ramfb_config_addr + 20, ramfb_config.height); - test_state.writel(ramfb_config_addr + 24, ramfb_config.stride); + fn write_to_file( + &self, + allocator: &mut GuestAllocator, + test_state: &TestState, + file_name: &str, + ) { + let ramfb_config_addr = allocator.alloc(mem::size_of::() as u64); + test_state.writeq(ramfb_config_addr, swap_u64(self.address)); + test_state.writel(ramfb_config_addr + 8, swap_u32(self.fourcc)); + test_state.writel(ramfb_config_addr + 12, swap_u32(self.flags)); + test_state.writel(ramfb_config_addr + 16, swap_u32(self.width)); + test_state.writel(ramfb_config_addr + 20, swap_u32(self.height)); + test_state.writel(ramfb_config_addr + 24, swap_u32(self.stride)); - let access = allocator.alloc(mem::size_of::() as u64); - test_state.fw_cfg_write_file( - allocator, - file_name, - access, - ramfb_config_addr, - mem::size_of::() as u32, - ); + let access = allocator.alloc(mem::size_of::() as u64); + test_state.fw_cfg_write_file( + allocator, + file_name, + access, + ramfb_config_addr, + mem::size_of::() as u32, + ); + } } #[test] @@ -103,17 +103,8 @@ fn test_basic() { Err(e) => assert!(false, "{}", e), } - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - framebuffer_base, - RAMFB_FORMAT, - 0, - HORIZONTAL_RESOLUTION, - VERTICAL_RESOLUTION, - RAMFB_BPP, - ); + let ramfb_config = RamfbConfig::new(framebuffer_base); + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, @@ -161,17 +152,9 @@ fn test_abnormal_param() { } // Set frambuffer address is abnormal. - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - ABNORMAL_FB_BASE, - RAMFB_FORMAT, - 0, - HORIZONTAL_RESOLUTION, - VERTICAL_RESOLUTION, - RAMFB_BPP, - ); + let mut ramfb_config = RamfbConfig::new(ABNORMAL_FB_BASE); + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); + match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, Err(e) => assert!(false, "{}", e), @@ -184,17 +167,10 @@ fn test_abnormal_param() { ); // Set drm format is unsupported. - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - framebuffer_base, - 0, - 0, - HORIZONTAL_RESOLUTION, - VERTICAL_RESOLUTION, - RAMFB_BPP, - ); + ramfb_config.address = framebuffer_base; + ramfb_config.fourcc = 0; + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); + match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, Err(e) => assert!(false, "{}", e), @@ -207,17 +183,10 @@ fn test_abnormal_param() { ); // Set width = 15, which is less than the minimum. - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - framebuffer_base, - RAMFB_FORMAT, - 0, - 15, - VERTICAL_RESOLUTION, - RAMFB_BPP, - ); + ramfb_config.fourcc = RAMFB_FORMAT; + ramfb_config.width = 15; + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); + match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, Err(e) => assert!(false, "{}", e), @@ -232,17 +201,9 @@ fn test_abnormal_param() { ); // Set width = 16001, which is exceeded the maximum. - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - framebuffer_base, - RAMFB_FORMAT, - 0, - 16001, - VERTICAL_RESOLUTION, - RAMFB_BPP, - ); + ramfb_config.width = 16001; + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); + match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, Err(e) => assert!(false, "{}", e), @@ -257,17 +218,10 @@ fn test_abnormal_param() { ); // Set height = 15, which is less than the minimum. - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - framebuffer_base, - RAMFB_FORMAT, - 0, - HORIZONTAL_RESOLUTION, - 15, - RAMFB_BPP, - ); + ramfb_config.width = HORIZONTAL_RESOLUTION; + ramfb_config.height = 15; + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); + match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, Err(e) => assert!(false, "{}", e), @@ -281,18 +235,10 @@ fn test_abnormal_param() { "Failed to check min height!" ); - // Set height = 16001, which is exceeded the maximum. - write_ramfb_config( - &mut allocator.borrow_mut(), - &test_state.borrow(), - file_name, - framebuffer_base, - RAMFB_FORMAT, - 0, - HORIZONTAL_RESOLUTION, - 12001, - RAMFB_BPP, - ); + // Set height = 12001, which is exceeded the maximum. + ramfb_config.height = 12001; + ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); + match fs::read_to_string(log_path) { Ok(contents) => file_contents = contents, Err(e) => assert!(false, "{}", e), -- Gitee From 4e822064734b1a000a3a477ddf37d1708113b30d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 26 Feb 2023 19:20:13 +0800 Subject: [PATCH 0817/2187] MST: delete unused libdriver fw_cfg.rs file The test case invokes the internal interface in fwcfg.rs. The fw_cfg.rs file is no longer used. Therefore , the fw_cfg.rs is deleted. Signed-off-by: Mingwang Li --- tests/mod_test/src/libdriver/fw_cfg.rs | 152 ------------------------- 1 file changed, 152 deletions(-) delete mode 100644 tests/mod_test/src/libdriver/fw_cfg.rs diff --git a/tests/mod_test/src/libdriver/fw_cfg.rs b/tests/mod_test/src/libdriver/fw_cfg.rs deleted file mode 100644 index 92157e00c..000000000 --- a/tests/mod_test/src/libdriver/fw_cfg.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::mem; - -use devices::legacy::FwCfgEntryType; - -use super::malloc::GuestAllocator; -use crate::libtest::TestState; - -#[cfg(target_arch = "aarch64")] -pub const FW_CFG_BASE: u64 = 0x09020000; -#[cfg(target_arch = "x86_64")] -const FW_CFG_BASE: u64 = 0x510; - -const FW_CFG_FNAME_SIZE: usize = 56; - -#[repr(C)] -pub struct FwCfgDmaAccess { - control: u32, - length: u32, - address: u64, -} - -pub fn base_args(base_args: &mut Vec<&str>) { - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); - base_args.append(&mut args); - args = "-drive file=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true" - .split(' ') - .collect(); - base_args.append(&mut args); -} - -pub fn swap_u16(value: u16) -> u16 { - value << 8 | value >> 8 -} - -pub fn swap_u32(value: u32) -> u32 { - let lower_u16 = swap_u16(value as u16) as u32; - let higher_u16 = swap_u16((value >> 16) as u16) as u32; - lower_u16 << 16 | higher_u16 -} - -pub fn swap_u64(value: u64) -> u64 { - let lower_u32 = swap_u32(value as u32) as u64; - let higher_u32 = swap_u32((value >> 32) as u32) as u64; - lower_u32 << 32 | higher_u32 -} - -impl TestState { - pub fn fw_cfg_read_bytes(&self, key: u16, data: &mut Vec, len: u32) { - self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); - for _i in 0..len { - data.push(self.readb(FW_CFG_BASE)) - } - } - - pub fn fw_cfg_read_u16(&self, key: u16) -> u16 { - self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); - self.readw(FW_CFG_BASE) - } - - pub fn fw_cfg_read_u32(&self, key: u16) -> u32 { - self.writew(FW_CFG_BASE + 0x8, swap_u16(key)); - self.readl(FW_CFG_BASE) - } - - pub fn dma_transfer_bytes(&self, access: u64, buff: u64, size: u32, ctrl: u32) { - self.writel(access, swap_u32(ctrl)); - self.writel(access + 4, swap_u32(size)); - self.writeq(access + 8, swap_u64(buff)); - - self.writeq(FW_CFG_BASE + 0x10, swap_u64(access)); - } - - pub fn fw_cfg_read_file( - &self, - allocator: &mut GuestAllocator, - file_name: &str, - data: &mut Vec, - data_len: u32, - ) -> u32 { - let file_name_len = file_name.to_string().len(); - let mut file_size = 0; - let mut name: [u8; FW_CFG_FNAME_SIZE] = [0; FW_CFG_FNAME_SIZE]; - let buff = allocator.alloc(FW_CFG_FNAME_SIZE as u64); - let access = allocator.alloc(mem::size_of::() as u64); - - self.writew(FW_CFG_BASE + 0x8, swap_u16(FwCfgEntryType::FileDir as u16)); - let count = swap_u32(self.readl(FW_CFG_BASE)); - for _i in 0..count { - let mut size = swap_u32(self.readl(FW_CFG_BASE)); - let select = swap_u16(self.readw(FW_CFG_BASE)); - let _reserved = swap_u16(self.readw(FW_CFG_BASE)); - // Read file name by DMA. - self.dma_transfer_bytes(access, buff, FW_CFG_FNAME_SIZE as u32, 2); - for (i, n) in name.iter_mut().enumerate().take(FW_CFG_FNAME_SIZE) { - *n = self.readb(buff + i as u64); - } - if String::from_utf8_lossy(&name[0..file_name_len]).eq(file_name) { - file_size = size; - if size > data_len { - size = data_len; - } - self.fw_cfg_read_bytes(select, data, size); - break; - } - } - file_size - } - - pub fn fw_cfg_write_file( - &self, - allocator: &mut GuestAllocator, - file_name: &str, - data_access: u64, - data_addr: u64, - data_len: u32, - ) { - let file_name_len = file_name.to_string().len(); - let mut name: [u8; FW_CFG_FNAME_SIZE] = [0; FW_CFG_FNAME_SIZE]; - let buff = allocator.alloc(FW_CFG_FNAME_SIZE as u64); - let access = allocator.alloc(mem::size_of::() as u64); - - self.writew(FW_CFG_BASE + 0x8, swap_u16(FwCfgEntryType::FileDir as u16)); - let count = swap_u32(self.readl(FW_CFG_BASE)); - for _i in 0..count { - let _size = swap_u32(self.readl(FW_CFG_BASE)); - let select = swap_u16(self.readw(FW_CFG_BASE)); - let _reserved = swap_u16(self.readw(FW_CFG_BASE)); - // Read file name by DMA. - self.dma_transfer_bytes(access, buff, FW_CFG_FNAME_SIZE as u32, 2); - for (i, n) in name.iter_mut().enumerate().take(FW_CFG_FNAME_SIZE) { - *n = self.readb(buff + i as u64); - } - if String::from_utf8_lossy(&name[0..file_name_len]).eq(file_name) { - self.writew(FW_CFG_BASE + 0x8, swap_u16(select)); - self.dma_transfer_bytes(data_access, data_addr, data_len, 16); - break; - } - } - } -} -- Gitee From 36f8baabf959d672e0ed03ee453c173c82d54ef3 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Sun, 26 Feb 2023 21:32:11 +0800 Subject: [PATCH 0818/2187] memory: fix debugging information about delete_subregion Signed-off-by: Li HuaChao --- address_space/src/region.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 93f34119c..a7590d114 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -757,7 +757,7 @@ impl Region { .update_topology() .with_context(|| "Failed to update topology for address_space")?; } else { - debug!("add subregion to container region, which has no belonged address-space"); + debug!("delete subregion from container region, which has no belonged address-space"); } child.del_belonged_address_space(); -- Gitee From ab4d4e01e9dda3eb15ed77615c930420868526b6 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 22 Feb 2023 16:40:09 +0800 Subject: [PATCH 0819/2187] mod-test: Add virtio-rng MST testcases Signed-off-by: yezengruan --- tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/virtio_rng.rs | 57 ++++ tests/mod_test/tests/rng_test.rs | 353 +++++++++++++++++++++ 3 files changed, 411 insertions(+) create mode 100644 tests/mod_test/src/libdriver/virtio_rng.rs create mode 100644 tests/mod_test/tests/rng_test.rs diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 1767393e7..10d0abcba 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -19,3 +19,4 @@ pub mod virtio; pub mod virtio_console; pub mod virtio_net; pub mod virtio_pci_modern; +pub mod virtio_rng; diff --git a/tests/mod_test/src/libdriver/virtio_rng.rs b/tests/mod_test/src/libdriver/virtio_rng.rs new file mode 100644 index 000000000..b14cb03a7 --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio_rng.rs @@ -0,0 +1,57 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::machine::TestStdMachine; +use super::malloc::GuestAllocator; +use super::virtio_pci_modern::TestVirtioPciDev; +use crate::libtest::{test_init, TestState}; + +use std::cell::RefCell; +use std::rc::Rc; + +pub fn create_rng( + random_file: String, + max_bytes: u64, + period: u64, +) -> ( + Rc>, + Rc>, + Rc>, +) { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let rng_pci_args = format!( + "-device {},rng=objrng0,max-bytes={},period={},bus=pcie.0,addr={}.0x0,id=rng-id", + "virtio-rng-pci", max_bytes, period, pci_slot + ); + args = rng_pci_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let rng_args = format!("-object rng-random,id=objrng0,filename={}", random_file); + args = rng_args.split(' ').collect(); + extra_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let rng = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + + rng.borrow_mut().init(pci_slot, pci_fn); + + (rng, test_state, allocator) +} diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs new file mode 100644 index 000000000..d42a57000 --- /dev/null +++ b/tests/mod_test/tests/rng_test.rs @@ -0,0 +1,353 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{ + TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VIRTIO_F_VERSION_1, +}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libdriver::virtio_rng::create_rng; +use mod_test::libtest::TestState; + +use std::cell::RefCell; +use std::collections::HashSet; +use std::os::unix::fs::FileTypeExt; +use std::path::Path; +use std::rc::Rc; + +const TIMEOUT_US: u64 = 10 * 1000 * 1000; +const RANDOM_FILE: &str = "/dev/random"; +const RNG_DATA_BYTES: u64 = 64; +const THRESHOLD: usize = 10; +const DEFAULT_RNG_REQS: u64 = 6; + +fn get_random_file() -> String { + let random_file: String = RANDOM_FILE.to_string(); + let path = Path::new(&random_file); + if path.exists() && path.metadata().unwrap().file_type().is_char_device() { + return random_file; + } + + panic!("Failed to get random file."); +} + +// Check if the distinct random numbers are greater than the THRESHOLD. +fn random_num_check(data: Vec) -> bool { + let mut rand_set: HashSet = HashSet::new(); + + for num in data.iter() { + if *num != 0 { + rand_set.insert(*num); + + if rand_set.len() > THRESHOLD { + return true; + } + } + } + + false +} + +// Read RNG_DATA_BYTES bytes from virtio-rng device, and +// perform DEFAULT_RNG_REQS reqs. +fn virtio_rng_read_batch( + rng: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + bytes: u64, +) -> Vec { + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + let mut len = Some(0); + + for _i in 0..DEFAULT_RNG_REQS { + req_addr = alloc.borrow_mut().alloc(bytes); + free_head = virtqueue + .borrow_mut() + .add(test_state.clone(), req_addr, bytes as u32, true); + } + + rng.borrow() + .kick_virtqueue(test_state.clone(), virtqueue.clone()); + rng.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut len, + true, + ); + + assert!(len.unwrap() >= 1); + assert!(len.unwrap() as u64 <= bytes); + + test_state.borrow().memread(req_addr, RNG_DATA_BYTES) +} + +// Read RNG_DATA_BYTES*DEFAULT_RNG_REQS bytes from virtio-rng device. +fn virtio_rng_read_chained( + rng: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + bytes: u64, +) -> Vec { + let req_addr = alloc.borrow_mut().alloc(bytes * DEFAULT_RNG_REQS); + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_RNG_REQS as usize); + let mut len = Some(0); + + for i in 0..DEFAULT_RNG_REQS { + data_entries.push(TestVringDescEntry { + data: req_addr + i * bytes, + len: bytes as u32, + write: true, + }); + } + + let free_head = virtqueue + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + rng.borrow() + .kick_virtqueue(test_state.clone(), virtqueue.clone()); + rng.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut len, + true, + ); + + assert!(len.unwrap() >= 1); + assert!(len.unwrap() as u64 <= bytes * DEFAULT_RNG_REQS); + + test_state.borrow().memread(req_addr, RNG_DATA_BYTES) +} + +fn tear_down( + rng: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, +) { + rng.borrow_mut().destroy_device(alloc.clone(), vqs); + test_state.borrow_mut().stop(); +} + +/// Rng device read random numbers function test. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request, check random numbers. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn rng_read() { + let max_bytes = 1024; + let period = 1000; + + let random_file = get_random_file(); + let (rng, test_state, alloc) = create_rng(random_file, max_bytes, period); + + let virtqueues = rng.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let mut data = virtio_rng_read_chained( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + RNG_DATA_BYTES, + ); + assert!(random_num_check(data)); + + data = virtio_rng_read_chained( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + RNG_DATA_BYTES, + ); + assert!(random_num_check(data)); + + tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); +} + +/// Rng device batch read random numbers function test. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request, check random numbers. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn rng_read_batch() { + let max_bytes = 1024; + let period = 1000; + + let random_file = get_random_file(); + let (rng, test_state, alloc) = create_rng(random_file, max_bytes, period); + + let virtqueues = rng.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let mut data = virtio_rng_read_batch( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + RNG_DATA_BYTES, + ); + assert!(random_num_check(data)); + + data = virtio_rng_read_batch( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + RNG_DATA_BYTES, + ); + assert!(random_num_check(data)); + + tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); +} + +/// Rng device rate limit random numbers reading test. +/// TestStep: +/// 1. Init device with rate limit 64 bytes/sec. +/// 2. Do the I/O request, check random numbers. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn rng_limited_rate() { + let max_bytes = 64; + let period = 1000; + + let random_file = get_random_file(); + let (rng, test_state, alloc) = create_rng(random_file, max_bytes, period); + + let virtqueues = rng.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let data = virtio_rng_read_chained( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + RNG_DATA_BYTES, + ); + assert!(random_num_check(data)); + + let req_addr = alloc.borrow_mut().alloc(RNG_DATA_BYTES * DEFAULT_RNG_REQS); + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_RNG_REQS as usize); + + for i in 0..DEFAULT_RNG_REQS { + data_entries.push(TestVringDescEntry { + data: req_addr + i * RNG_DATA_BYTES, + len: RNG_DATA_BYTES as u32, + write: true, + }); + } + + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + rng.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + test_state.borrow().clock_step(); + + assert!(!random_num_check( + test_state.borrow().memread(req_addr, RNG_DATA_BYTES) + )); + + loop { + let mut got_desc_idx = Some(0); + + test_state.borrow().clock_step(); + + if rng.borrow().queue_was_notified(virtqueues[0].clone()) + && virtqueues[0] + .borrow_mut() + .get_buf(test_state.clone(), &mut got_desc_idx, &mut None) + { + assert!(got_desc_idx.unwrap() == free_head); + break; + } + } + + assert!(random_num_check(test_state.borrow().memread( + req_addr + (DEFAULT_RNG_REQS - 1) * RNG_DATA_BYTES, + RNG_DATA_BYTES + ))); + + tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); +} + +/// Rng device read a large number of random numbers test. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request, check random numbers. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn rng_read_with_max() { + let max_bytes = 1000000000; + let period = 1000; + let max_bytes_read = 2048000; + + let random_file = get_random_file(); + let (rng, test_state, alloc) = create_rng(random_file, max_bytes, period); + + let virtqueues = rng.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let mut data = virtio_rng_read_chained( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + max_bytes_read, + ); + assert!(random_num_check(data)); + + data = virtio_rng_read_chained( + rng.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + max_bytes_read, + ); + assert!(random_num_check(data)); + + tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); +} -- Gitee From 8b74ede764d37164702c7833c924460f7b627cc3 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 26 Feb 2023 20:55:36 +0800 Subject: [PATCH 0820/2187] msix: consider enable bit changed while mask bit stays 0 as ready to send msix enable bit and mask bit has similar effect to msix notify, if mask bit stays 0 and enable bit vary from 0 to 1, we take this situation as proper to send msix. --- pci/src/msix.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index e5adfd912..5b778aa36 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -464,10 +464,14 @@ impl Msix { return; } - let func_masked: bool = is_msix_func_masked(self.msix_cap_offset as usize, config); + let masked: bool = is_msix_func_masked(self.msix_cap_offset as usize, config); let enabled: bool = is_msix_enabled(self.msix_cap_offset as usize, config); + let mask_state_changed = !((self.func_masked == masked) && (self.enabled == enabled)); - if enabled && self.func_masked && !func_masked { + self.func_masked = masked; + self.enabled = enabled; + + if mask_state_changed && (self.enabled && !self.func_masked) { let max_vectors_nr: u16 = self.table.len() as u16 / MSIX_TABLE_ENTRY_SIZE; for v in 0..max_vectors_nr { if !self.is_vector_masked(v) && self.is_vector_pending(v) { @@ -476,8 +480,6 @@ impl Msix { } } } - self.func_masked = func_masked; - self.enabled = enabled; } } -- Gitee From 6ba3594ef1b7e430eb3de221059706d9ae1ed2d1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 27 Feb 2023 11:02:30 +0800 Subject: [PATCH 0821/2187] mod_test/libdirver: Fix virtqueue get_buf The request may complete not in order, we should save all used elements and search for wanted desc. Signed-off-by: Keqian Zhu --- tests/mod_test/src/libdriver/virtio.rs | 24 +++++++------------ .../src/libdriver/virtio_pci_modern.rs | 15 ++++++------ tests/mod_test/tests/rng_test.rs | 9 ++----- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index be034dc12..101abc2ac 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -13,6 +13,7 @@ use super::malloc::GuestAllocator; use crate::libtest::TestState; use std::cell::RefCell; +use std::collections::HashMap; use std::mem::size_of; use std::rc::Rc; use std::time; @@ -308,6 +309,7 @@ pub struct TestVirtQueue { pub msix_addr: u64, pub msix_data: u32, pub queue_notify_off: u64, + pub desc_len: HashMap, } impl TestVirtQueue { @@ -395,12 +397,8 @@ impl TestVirtQueue { .unwrap(); } - pub fn get_buf( - &mut self, - test_state: Rc>, - desc_idx: &mut Option, - len: &mut Option, - ) -> bool { + /// Get used elements from used ring and save to self.desc_len + pub fn get_buf(&mut self, test_state: Rc>) -> bool { let mut ret = false; loop { let index = test_state @@ -415,15 +413,11 @@ impl TestVirtQueue { + (self.last_used_idx as u32 % self.size) as u64 * size_of::() as u64; - if let Some(id) = desc_idx.as_mut() { - let addr = elem_addr + offset_of!(VringUsedElem, id) as u64; - *id = test_state.borrow().readl(addr); - } - - if let Some(len) = len.as_mut() { - let addr = elem_addr + offset_of!(VringUsedElem, len) as u64; - *len = test_state.borrow().readl(addr); - } + let id_addr = elem_addr + offset_of!(VringUsedElem, id) as u64; + let id_val = test_state.borrow().readl(id_addr); + let len_addr = elem_addr + offset_of!(VringUsedElem, len) as u64; + let len_val = test_state.borrow().readl(len_addr); + self.desc_len.insert(id_val, len_val); self.last_used_idx += 1; ret = true; diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index c7da6a1e1..290f8fe68 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -518,17 +518,16 @@ impl VirtioDeviceOps for TestVirtioPciDev { let timeout_us = Duration::from_micros(timeout_us); loop { - let mut got_desc_idx = Some(0); - if (!wait_notified || self.queue_was_notified(virtqueue.clone())) - && virtqueue - .borrow_mut() - .get_buf(test_state.clone(), &mut got_desc_idx, len) + && virtqueue.borrow_mut().get_buf(test_state.clone()) { - assert!(got_desc_idx.unwrap() == desc_idx); - return; + if let Some(got_len) = virtqueue.borrow().desc_len.get(&desc_idx) { + if let Some(len) = len { + *len = *got_len; + } + break; + } } - assert!(Instant::now() - start_time < timeout_us); } } diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs index d42a57000..8d0eea3f6 100644 --- a/tests/mod_test/tests/rng_test.rs +++ b/tests/mod_test/tests/rng_test.rs @@ -286,16 +286,11 @@ fn rng_limited_rate() { )); loop { - let mut got_desc_idx = Some(0); - test_state.borrow().clock_step(); - if rng.borrow().queue_was_notified(virtqueues[0].clone()) - && virtqueues[0] - .borrow_mut() - .get_buf(test_state.clone(), &mut got_desc_idx, &mut None) + && virtqueues[0].borrow_mut().get_buf(test_state.clone()) { - assert!(got_desc_idx.unwrap() == free_head); + assert!(virtqueues[0].borrow().desc_len.contains_key(&free_head)); break; } } -- Gitee From 2ec1e422fac63b4da1157248a453b91c275b8259 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 26 Feb 2023 11:29:50 +0800 Subject: [PATCH 0822/2187] mod-test: Add virtio-block MST testcases Signed-off-by: yezengruan --- Cargo.lock | 1 + tests/mod_test/Cargo.toml | 1 + tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/virtio_block.rs | 426 ++++++ tests/mod_test/tests/block_test.rs | 1275 ++++++++++++++++++ virtio/src/block.rs | 2 +- virtio/src/lib.rs | 2 +- 7 files changed, 1706 insertions(+), 2 deletions(-) create mode 100644 tests/mod_test/src/libdriver/virtio_block.rs create mode 100644 tests/mod_test/tests/block_test.rs diff --git a/Cargo.lock b/Cargo.lock index fc2a5c152..952fa6e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,6 +541,7 @@ dependencies = [ "rand", "serde_json", "util", + "virtio", ] [[package]] diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index fb23ff9b6..23d93c035 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -14,3 +14,4 @@ devices = { path = "../../devices" } util = { path = "../../util" } acpi = { path = "../../acpi" } machine = { path = "../../machine" } +virtio = { path = "../../virtio"} diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 10d0abcba..de70e5867 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -16,6 +16,7 @@ pub mod malloc; pub mod pci; pub mod pci_bus; pub mod virtio; +pub mod virtio_block; pub mod virtio_console; pub mod virtio_net; pub mod virtio_pci_modern; diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs new file mode 100644 index 000000000..cd8072aa1 --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -0,0 +1,426 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::machine::TestStdMachine; +use super::malloc::GuestAllocator; +use super::virtio::TestVirtQueue; +use super::virtio::VirtioDeviceOps; +use super::virtio_pci_modern::TestVirtioPciDev; +use crate::libdriver::virtio::{ + TestVringDescEntry, VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, +}; +use crate::libtest::{test_init, TestState}; +use crate::utils::get_rand_str; + +use std::cell::RefCell; +use std::fs; +use std::mem::size_of; +use std::path::Path; +use std::process::Command; +use std::rc::Rc; +use util::num_ops::round_up; + +pub const VIRTIO_BLK_F_BARRIER: u64 = 0; +pub const VIRTIO_BLK_F_SIZE_MAX: u64 = 1; +pub const VIRTIO_BLK_F_SEG_MAX: u64 = 2; +pub const VIRTIO_BLK_F_GEOMETRY: u64 = 4; +pub const VIRTIO_BLK_F_RO: u64 = 5; +pub const VIRTIO_BLK_F_BLK_SIZE: u64 = 6; +pub const VIRTIO_BLK_F_SCSI: u64 = 7; +pub const VIRTIO_BLK_F_FLUSH: u64 = 9; +pub const VIRTIO_BLK_F_TOPOLOGY: u64 = 10; +pub const VIRTIO_BLK_F_CONFIG_WCE: u64 = 11; +pub const VIRTIO_BLK_F_MQ: u64 = 12; +pub const VIRTIO_BLK_F_DISCARD: u64 = 13; +pub const VIRTIO_BLK_F_WRITE_ZEROES: u64 = 14; +pub const VIRTIO_BLK_F_LIFETIME: u64 = 15; +pub const VIRTIO_BLK_F_SECURE_ERASE: u64 = 16; + +pub const VIRTIO_BLK_T_IN: u32 = 0; +pub const VIRTIO_BLK_T_OUT: u32 = 1; +pub const VIRTIO_BLK_T_FLUSH: u32 = 4; +pub const VIRTIO_BLK_T_GET_ID: u32 = 8; +pub const VIRTIO_BLK_T_DISCARD: u32 = 11; +pub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13; +pub const VIRTIO_BLK_T_ILLGEAL: u32 = 32; +/// Success +pub const VIRTIO_BLK_S_OK: u8 = 0; +/// IO error. +pub const VIRTIO_BLK_S_IOERR: u8 = 1; +/// Unsupport request. +pub const VIRTIO_BLK_S_UNSUPP: u8 = 2; + +pub const TEST_IMAGE_SIZE: u64 = 64 * 1024 * 1024; +pub const TIMEOUT_US: u64 = 15 * 1000 * 1000; +pub const DEFAULT_IO_REQS: u64 = 5; +pub const REQ_ADDR_LEN: u32 = 16; +pub const REQ_DATA_LEN: u32 = 512; +pub const REQ_STATUS_LEN: u32 = 1; +pub const REQ_DATA_OFFSET: u64 = REQ_ADDR_LEN as u64; +pub const REQ_STATUS_OFFSET: u64 = (REQ_ADDR_LEN + REQ_DATA_LEN) as u64; + +#[allow(unused)] +pub struct VirtBlkDiscardWriteZeroes { + sector: u64, + num_sectors: u32, + flags: u32, +} + +#[allow(unused)] +pub struct TestVirtBlkReq { + req_type: u32, + io_priority: u32, + sector: u64, + pub data: String, + status: u8, +} + +impl TestVirtBlkReq { + pub fn new(req_type: u32, io_priority: u32, sector: u64, data_size: usize) -> Self { + Self { + req_type, + io_priority, + sector, + data: String::with_capacity(data_size), + status: 0, + } + } + + pub fn as_bytes(&self) -> Vec { + let mut bytes_buf = Vec::new(); + bytes_buf.append(&mut self.req_type.to_le_bytes().to_vec()); + bytes_buf.append(&mut self.io_priority.to_le_bytes().to_vec()); + bytes_buf.append(&mut self.sector.to_le_bytes().to_vec()); + bytes_buf + } +} + +pub fn create_blk( + image_path: Rc, + device_args: Rc, + drive_args: Rc, + other_args: Rc, +) -> ( + Rc>, + Rc>, + Rc>, +) { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let blk_pci_args = format!( + "-device {},id=drv0,drive=drive0,bus=pcie.0,addr={}.0{}", + "virtio-blk-pci", pci_slot, device_args, + ); + args = blk_pci_args[..].split(' ').collect(); + extra_args.append(&mut args); + let blk_args = format!( + "-drive if=none,id=drive0,file={},format=raw{}", + image_path, drive_args, + ); + args = blk_args.split(' ').collect(); + extra_args.append(&mut args); + + if !other_args.is_empty() { + args = other_args.split(' ').collect(); + extra_args.append(&mut args); + } + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let virtio_blk = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + + virtio_blk.borrow_mut().init(pci_slot, pci_fn); + + (virtio_blk, test_state, allocator) +} + +pub fn virtio_blk_request( + test_state: Rc>, + alloc: Rc>, + req: TestVirtBlkReq, + align: bool, +) -> u64 { + let status: u8 = 0xFF; + let data_size = req.data.capacity(); + + match req.req_type { + VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT => { + assert_eq!(data_size % (REQ_DATA_LEN as usize), 0) + } + VIRTIO_BLK_T_FLUSH => {} + VIRTIO_BLK_T_GET_ID => {} + VIRTIO_BLK_T_DISCARD => { + assert_eq!(data_size % (REQ_DATA_LEN as usize), 0) + } + VIRTIO_BLK_T_WRITE_ZEROES => { + assert_eq!(data_size % size_of::(), 0) + } + VIRTIO_BLK_T_ILLGEAL => {} + _ => { + assert_eq!(data_size, 0) + } + } + + let addr = alloc.borrow_mut().alloc( + (size_of::() + data_size + 512) + .try_into() + .unwrap(), + ); + + let data_addr = if align { + round_up(addr + REQ_ADDR_LEN as u64, 512).unwrap() + } else { + addr + REQ_DATA_OFFSET + }; + + let req_bytes = req.as_bytes(); + test_state.borrow().memwrite(addr, req_bytes.as_slice(), 16); + let mut data_bytes = req.data.as_bytes().to_vec(); + data_bytes.resize(data_size, 0); + test_state.borrow().memwrite( + data_addr, + data_bytes.as_slice(), + data_size.try_into().unwrap(), + ); + test_state.borrow().memwrite( + data_addr + data_size as u64, + &status.to_le_bytes(), + size_of::().try_into().unwrap(), + ); + + addr +} + +/// Create image file. +pub fn create_blk_img(size: u64, flag: u8) -> String { + let rng_name: String = get_rand_str(8); + + assert!(cfg!(target_os = "linux")); + + let mut image_path = format!("/tmp/stratovirt-{}.img", rng_name); + if flag == 1 { + image_path = format!("/var/log/stratovirt-{}.img", rng_name); + } + + let image_path_of = format!("of={}", &image_path); + let image_size_of = format!("bs={}", size); + let output = Command::new("dd") + .arg("if=/dev/zero") + .arg(&image_path_of) + .arg(&image_size_of) + .arg("count=1") + .output() + .expect("failed to create image"); + assert!(output.status.success()); + image_path +} + +/// Delete image file. +pub fn cleanup_blk_img(image_path: String) { + let img_path = Path::new(&image_path); + assert!(img_path.exists()); + + let metadata = fs::metadata(img_path).expect("can not get file metadata"); + let file_type = metadata.file_type(); + assert!(file_type.is_file()); + + fs::remove_file(img_path).expect("lack permissions to remove the file"); +} + +pub fn add_blk_request( + test_state: Rc>, + alloc: Rc>, + vq: Rc>, + req_type: u32, + sector: u64, + align: bool, +) -> (u32, u64) { + let mut read = true; + // [req_type, io_priority, sector, data_size] + let mut blk_req = TestVirtBlkReq::new(req_type, 1, sector, REQ_DATA_LEN as usize); + if req_type == VIRTIO_BLK_T_OUT { + blk_req.data.push_str("TEST"); + read = false; + } + // Get addr and write to Stratovirt. + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, align); + // Desc elem: [addr, len, flags, next]. + + let data_addr = if align { + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + } else { + req_addr + REQ_DATA_OFFSET + }; + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: REQ_ADDR_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr, + len: REQ_DATA_LEN, + write: read, + }); + data_entries.push(TestVringDescEntry { + data: data_addr + REQ_DATA_LEN as u64, + len: REQ_STATUS_LEN, + write: true, + }); + + let free_head = vq + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + (free_head, req_addr) +} + +/// Write DEFAULT_IO_REQS requests to disk. +pub fn virtio_blk_write( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + sector: u64, + align: bool, +) { + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + for i in sector..sector + DEFAULT_IO_REQS { + (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueue.clone(), + VIRTIO_BLK_T_OUT, + i, + align, + ); + } + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueue.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = if align { + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64 + } else { + req_addr + REQ_STATUS_OFFSET + }; + + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); +} + +/// Read 512 byte from disk. +pub fn virtio_blk_read( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + sector: u64, + align: bool, +) { + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueue.clone(), + VIRTIO_BLK_T_IN, + sector, + align, + ); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueue.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let data_addr = if align { + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + } else { + req_addr + REQ_ADDR_LEN as u64 + }; + + let status_addr = if align { + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64 + } else { + req_addr + REQ_STATUS_OFFSET + }; + + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + + assert_eq!( + String::from_utf8(test_state.borrow().memread(data_addr, 4)).unwrap(), + "TEST" + ); +} + +pub fn virtio_blk_defalut_feature(blk: Rc>) -> u64 { + let mut features = blk.borrow().get_device_features(); + features &= !(VIRTIO_F_BAD_FEATURE + | 1 << VIRTIO_RING_F_INDIRECT_DESC + | 1 << VIRTIO_RING_F_EVENT_IDX + | 1 << VIRTIO_BLK_F_SCSI); + + features +} + +pub fn set_up() -> ( + Rc>, + Rc>, + Rc>, + Rc, +) { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + (blk, test_state, alloc, image_path) +} + +pub fn tear_down( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, + image_path: Rc, +) { + blk.borrow_mut().destroy_device(alloc.clone(), vqs); + test_state.borrow_mut().stop(); + if !image_path.is_empty() { + cleanup_blk_img(image_path.to_string()); + } +} diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs new file mode 100644 index 000000000..de875ad5d --- /dev/null +++ b/tests/mod_test/tests/block_test.rs @@ -0,0 +1,1275 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use virtio::block::VirtioBlkConfig; + +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::TestVringDescEntry; +use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; +use mod_test::libdriver::virtio_block::{ + add_blk_request, create_blk, create_blk_img, set_up, tear_down, virtio_blk_defalut_feature, + virtio_blk_read, virtio_blk_request, virtio_blk_write, TestVirtBlkReq, DEFAULT_IO_REQS, + REQ_ADDR_LEN, REQ_DATA_LEN, REQ_STATUS_LEN, TEST_IMAGE_SIZE, TIMEOUT_US, VIRTIO_BLK_F_BARRIER, + VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, + VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_LIFETIME, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, + VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, + VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_ILLGEAL, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, +}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libtest::TestState; + +use std::cell::RefCell; +use std::rc::Rc; +use util::num_ops::round_up; +use util::offset_of; + +const TEST_IMAGE_SIZE_1M: u64 = 1024 * 1024; + +fn virtio_blk_get_id( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + serial_num: String, +) { + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueue.clone(), + VIRTIO_BLK_T_GET_ID, + 0, + true, + ); + blk.borrow().virtqueue_notify(virtqueue.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + + let data_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap(); + assert_eq!( + String::from_utf8( + test_state + .borrow() + .memread(data_addr, serial_num.len() as u64) + ) + .unwrap(), + serial_num + ); +} + +fn virtio_blk_flush( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + sector: u64, +) { + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueue.clone(), + VIRTIO_BLK_T_FLUSH, + sector, + true, + ); + blk.borrow().virtqueue_notify(virtqueue.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); +} + +fn virtio_blk_illegal_req( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + req_type: u32, +) { + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueue.clone(), + req_type, + 0, + true, + ); + blk.borrow().virtqueue_notify(virtqueue.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_UNSUPP); +} + +/// Block device sends I/O request. +/// TestStep: +/// 1. Init block device. +/// 2. Do the I/O request. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_basic() { + let (blk, test_state, alloc, image_path) = set_up(); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device negotiate different features. +/// TestStep: +/// 1. Init block device. +/// 2. Negotiate supported features. +/// 3. Negotiate unsupported features. +/// 4. Destroy device. +/// Expect: +/// 1/2/4: success, 3: failed. +#[test] +fn blk_features_negotiate() { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let device_args = Rc::new(String::from(",num-queues=4")); + let drive_args = Rc::new(String::from(",direct=false,readonly=on")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + + let mut features = blk.borrow().get_device_features(); + features |= 1 << VIRTIO_BLK_F_SEG_MAX + | 1 << VIRTIO_BLK_F_RO + | 1 << VIRTIO_BLK_F_FLUSH + | 1 << VIRTIO_BLK_F_MQ; + blk.borrow_mut().negotiate_features(features); + blk.borrow_mut().set_features_ok(); + assert_eq!(features, blk.borrow_mut().get_guest_features()); + + let unsupported_features = 1 << VIRTIO_BLK_F_BARRIER + | 1 << VIRTIO_BLK_F_SIZE_MAX + | 1 << VIRTIO_BLK_F_GEOMETRY + | 1 << VIRTIO_BLK_F_BLK_SIZE + | 1 << VIRTIO_BLK_F_TOPOLOGY + | 1 << VIRTIO_BLK_F_CONFIG_WCE + | 1 << VIRTIO_BLK_F_DISCARD + | 1 << VIRTIO_BLK_F_WRITE_ZEROES + | 1 << VIRTIO_BLK_F_LIFETIME + | 1 << VIRTIO_BLK_F_SECURE_ERASE; + features |= unsupported_features; + blk.borrow_mut().negotiate_features(features); + blk.borrow_mut().set_features_ok(); + assert_ne!(features, blk.borrow_mut().get_guest_features()); + assert_eq!( + unsupported_features & blk.borrow_mut().get_guest_features(), + 0 + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + Vec::new(), + image_path.clone(), + ); +} + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_SEG_MAX'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_SEG_MAX'. +/// 2. Do the I/O request, check seg_max. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_feature_seg_max() { + let (blk, test_state, alloc, image_path) = set_up(); + + let mut features = virtio_blk_defalut_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_SEG_MAX; + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let seg_max = blk + .borrow() + .config_readl(offset_of!(VirtioBlkConfig, seg_max) as u64); + let queue_size = virtqueues[0].borrow_mut().size; + assert_eq!(seg_max, (queue_size - 2)); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_RO'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_RO'. +/// 2. Do the 'read' I/O request. +/// 3. Do the 'write' I/O request. +/// 4. Destroy device. +/// Expect: +/// 1/2/4: success, failed: 3. +#[test] +fn blk_feature_ro() { + let (blk, test_state, alloc, image_path) = set_up(); + + let mut features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + Rc::new("".to_string()), + ); + + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false,readonly=on")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + features |= 1 << VIRTIO_BLK_F_RO; + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + true, + ); + blk.borrow().virtqueue_notify(virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_FLUSH'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_FLUSH'. +/// 2. Do the I/O request. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_feature_flush() { + let (blk, test_state, alloc, image_path) = set_up(); + + let mut features = virtio_blk_defalut_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_FLUSH; + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + virtio_blk_flush( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 10, + ); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_MQ'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_MQ'. +/// 2. Do the I/O multiple queue requests. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_feature_mq() { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let device_args = Rc::new(String::from(",num-queues=4")); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let mut features = virtio_blk_defalut_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_MQ; + + let num_queues = 4; + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, num_queues); + + let cfg_num_queues = blk + .borrow() + .config_readw(offset_of!(VirtioBlkConfig, num_queues) as u64); + assert_eq!(num_queues as u16, cfg_num_queues); + + let mut free_head: Vec = Vec::with_capacity(num_queues); + let mut req_addr: Vec = Vec::with_capacity(num_queues); + for i in 0..num_queues { + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, i as u64, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + + req_addr.push(virtio_blk_request( + test_state.clone(), + alloc.clone(), + blk_req, + true, + )); + + let data_addr = round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap(); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr[i], + len: REQ_ADDR_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr, + len: REQ_DATA_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr + REQ_DATA_LEN as u64, + len: REQ_STATUS_LEN, + write: true, + }); + + free_head.push( + virtqueues[i] + .borrow_mut() + .add_chained(test_state.clone(), data_entries), + ); + } + + for i in 0..num_queues { + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[i].clone()); + } + + for i in 0..num_queues { + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[i].clone(), + free_head[i], + TIMEOUT_US, + &mut None, + true, + ); + } + + for i in 0..num_queues { + let status_addr = + round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + } + + for i in 0..num_queues { + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[i].clone(), + i as u64, + true, + ); + } + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request, configure all parameters. +/// TestStep: +/// 1. Init block device, configure all parameters. +/// 2. Do the I/O request. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_all_features() { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); + let device_args = Rc::new(String::from( + ",multifunction=on,serial=111111,num-queues=4,bootindex=1,iothread=iothread1", + )); + let drive_args = Rc::new(String::from( + ",direct=on,aio=io_uring,readonly=off,throttling.iops-total=1024", + )); + let other_args = Rc::new(String::from("-object iothread,id=iothread1")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let mut features = virtio_blk_defalut_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_FLUSH | 1 << VIRTIO_BLK_F_MQ; + + let num_queues = 4; + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, num_queues); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request to a file with a size of 511b. +/// TestStep: +/// 1. Init block device with a 511b file. +/// 2. Do the I/O request. +/// 3. Destroy device. +/// Expect: +/// 1/3: success, 2: failed. +#[test] +fn blk_small_file_511b() { + let size = 511; + let image_path = Rc::new(create_blk_img(size, 1)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, size / REQ_DATA_LEN as u64); + + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, true); + let data_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap(); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: REQ_ADDR_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr, + len: REQ_DATA_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr + REQ_DATA_LEN as u64, + len: REQ_STATUS_LEN, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_IN, + 0, + true, + ); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request, configured as 'serial=11111111111111111111'. +/// TestStep: +/// 1. Init block device, configured as 'serial=11111111111111111111'. +/// 2. Do the I/O request, check serial number. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_serial() { + let serial_num = String::from("11111111111111111111"); + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let device_args = Rc::new(format!(",serial={}", serial_num)); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_get_id( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + serial_num, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request, configured as 'throttling.iops-total=1'. +/// TestStep: +/// 1. Init block device, configured as 'throttling.iops-total=1'. +/// 2. Do the I/O request, check iops. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_iops() { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE, 0)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false,throttling.iops-total=1")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + + for i in 0..DEFAULT_IO_REQS { + (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + i, + true, + ); + } + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + + loop { + test_state.borrow().clock_step_ns(100); + + if blk.borrow().queue_was_notified(virtqueues[0].clone()) + && virtqueues[0].borrow_mut().get_buf(test_state.clone()) + { + assert!(!virtqueues[0].borrow().desc_len.contains_key(&free_head)); + break; + } + } + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_ne!(status, VIRTIO_BLK_S_OK); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request, configured as 'aio=native'. +/// TestStep: +/// 1. Init block device, configured as 'aio=native'. +/// 2. Do the I/O request. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_aio_native() { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=on,aio=native")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends I/O request, configured as 'aio=io_uring'. +/// TestStep: +/// 1. Init block device, configured as 'aio=io_uring'. +/// 2. Do the I/O request. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_aio_io_uring() { + let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=on,aio=io_uring")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + false, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + false, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends an illegal type of I/O request. +/// TestStep: +/// 1. Init block device. +/// 2. Do the I/O request of illegal type. +/// 3. Destroy device. +/// Expect: +/// 1/3: success, 2: failed. +#[test] +fn blk_illegal_req_type() { + let (blk, test_state, alloc, image_path) = set_up(); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + virtio_blk_illegal_req( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_ILLGEAL, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device configuration space read and write. +/// TestStep: +/// 1. Init block device. +/// 2. Read block device configuration space. +/// 3. Write block device configuration space. +/// 4. Destroy device. +/// Expect: +/// 1/2/4: success, 3: failed. +#[test] +fn blk_rw_config() { + let (blk, test_state, alloc, image_path) = set_up(); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + + blk.borrow().config_writeq(0, 1024); + let capacity = blk.borrow().config_readq(0); + assert_ne!(capacity, 1024); + + let discard_sector_alignment = blk.borrow().config_readl(40); + blk.borrow().config_writel(40, 1024); + assert_eq!(blk.borrow().config_readl(40), discard_sector_alignment); + assert_ne!(blk.borrow().config_readl(40), 1024); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device send I/O requests in an abnormal format. +/// TestStep: +/// 1. Init block device. +/// 2. Do the I/O requests in an abnormal format. +/// 3. Destroy device. +/// Expect: +/// 1/3: success, 2: failed. +#[test] +fn blk_abnormal_req() { + let (blk, test_state, alloc, image_path) = set_up(); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 8, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 8, + len: 256, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 264, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + 264); + assert_ne!(status, VIRTIO_BLK_S_OK); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 32, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 32, + len: 512, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 544, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + 544); + assert_ne!(status, VIRTIO_BLK_S_OK); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 256, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 272, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + 272); + assert_ne!(status, VIRTIO_BLK_S_OK); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device send different types of I/O requests in parallel. +/// TestStep: +/// 1. Init block device. +/// 2. Do the different types I/O requests in parallel. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_parallel_req() { + let (blk, test_state, alloc, image_path) = set_up(); + + let mut features = virtio_blk_defalut_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_FLUSH; + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let mut free_head_vec: Vec = Vec::with_capacity(4); + let mut req_addr_vec: Vec = Vec::with_capacity(4); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_IN, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_FLUSH, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_GET_ID, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head_vec[3], + TIMEOUT_US, + &mut None, + true, + ); + + for i in 0..4 { + let status_addr = + round_up(req_addr_vec[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + } + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device sends an I/O request that exceeds the capacity range. +/// TestStep: +/// 1. Init block device. +/// 2. Do the I/O request that exceeds the capacity range. +/// 3. Destroy device. +/// Expect: +/// 1/3: success, 2: failed. +#[test] +fn blk_exceed_capacity() { + let (blk, test_state, alloc, image_path) = set_up(); + + let features = virtio_blk_defalut_feature(blk.clone()); + + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + capacity + 1, + true, + ); + + blk.borrow().virtqueue_notify(virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 868cd5f17..7a073dfb0 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -874,7 +874,7 @@ pub struct VirtioBlkConfig { /// The maximum segment size. size_max: u32, /// Tne maximum number of segments. - seg_max: u32, + pub seg_max: u32, /// Geometry of the block device. geometry: VirtioBlkGeometry, /// Block size of device. diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 708e1f991..b747949b9 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -26,7 +26,7 @@ //! - `aarch64` mod balloon; -mod block; +pub mod block; mod console; pub mod error; #[cfg(not(target_env = "musl"))] -- Gitee From 288a6a458065c3c12d8a0c8578827654202941f9 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Thu, 23 Feb 2023 21:06:47 +0800 Subject: [PATCH 0823/2187] memory-test: add memory MST tests Signed-off-by: Li HuaChao --- tests/mod_test/tests/memory_test.rs | 627 ++++++++++++++++++++++++++++ 1 file changed, 627 insertions(+) create mode 100644 tests/mod_test/tests/memory_test.rs diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs new file mode 100644 index 000000000..c8687f9d8 --- /dev/null +++ b/tests/mod_test/tests/memory_test.rs @@ -0,0 +1,627 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::{ + libdriver::{machine::TestStdMachine, malloc::GuestAllocator}, + libtest::{test_init, TestState}, +}; +use serde_json::{json, Value::String as JsonString}; +use std::{cell::RefCell, process::Command, rc::Rc, string::String}; + +pub struct MemoryTest { + pub state: Rc>, + pub alloctor: Rc>, +} + +const MEM_SIZE: u64 = 2048; // 2GB +const PAGE_SIZE: u64 = 4096; +const ADDRESS_BASE: u64 = 0x4000_0000; + +impl MemoryTest { + pub fn new( + memsize: u64, + page_size: u64, + shared: bool, + prealloc: bool, + hugepage_path: Option, + ram_file: Option, + ) -> Self { + let mut extra_args: Vec<&str> = Vec::new(); + let mut args: Vec<&str> = "-machine".split(' ').collect(); + if shared { + args.push("virt,mem-share=on"); + } else { + args.push("virt"); + } + if prealloc { + args.push("-mem-prealloc"); + } + extra_args.append(&mut args); + + let mem_args = format!("-m {}", memsize); + args = mem_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let mem_args; + if let Some(file) = hugepage_path { + mem_args = format!("-mem-path {:?}", file); + args = mem_args[..].split(' ').collect(); + extra_args.append(&mut args); + } + + let mem_args_path; + if let Some(file) = ram_file { + mem_args_path = format!("-mem-path {:?}", file); + args = mem_args_path[..].split(' ').collect(); + extra_args.append(&mut args); + } + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = + TestStdMachine::new_bymem(test_state.clone(), memsize * 1024 * 1024, page_size); + let allocator = machine.allocator.clone(); + + MemoryTest { + state: test_state, + alloctor: allocator, + } + } +} + +fn ram_read_write(memory_test: &MemoryTest) { + let str = "test memory read write"; + let addr = memory_test.alloctor.borrow_mut().alloc(PAGE_SIZE); + + memory_test + .state + .borrow_mut() + .memwrite(addr, str.as_bytes(), str.len() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, str.len() as u64); + assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); + + memory_test.state.borrow_mut().stop(); +} + +/// Ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn normal_ram_read_write() { + ram_read_write(&MemoryTest::new( + MEM_SIZE, PAGE_SIZE, false, false, None, None, + )); +} + +/// Io region read and write Test. +/// TestStep: +/// 1. Add an io region. +/// 2. Write some data([0x1u8; 8]) to the address. +/// 3. Read data from the address and check it. +/// 4. Write overflow. +/// 5. Read overflow. +/// 6. Destroy device. +/// Expect: +/// 1/2/6: Success. +/// 4/5: Failed +/// 3: Got [0x2u8; 8]. The function of the device is to multiply the written value by 2. +#[test] +fn io_region_read_write() { + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + let addr = 0x100_0000_0000; // 1TB + + // Add a dummy device by qmp. The function of the device is to multiply the written value by 2 + // through the write interface and save it, and read the saved value through the read interface. + memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + let data = [0x01u8; 8]; + memory_test + .state + .borrow_mut() + .memwrite(addr, &data, std::mem::size_of::() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x02u8; 8]); + + memory_test.state.borrow_mut().stop(); +} + +/// Read and write the overlapping region Test. +/// TestStep: +/// 1. Write some data[0x1u8; 8] to the ram. +/// 2. Read the data([0x1u8; 8]) from the address. +/// 3. Add a region that overlaps the ram region. +/// 4. Write some data[0x1u8; 8] the overlaps region. +/// 5. Read data from the overlaps region. +/// Expect: +/// 1/3/4: success. +/// 2: Got [0x1u8; 8]. +/// 5: Got [0x2u8; 8]. We read the io region data witch has a higher priority. +#[test] +fn region_priority() { + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + let addr = memory_test.alloctor.borrow_mut().alloc(PAGE_SIZE); + let data = [0x01u8; 8]; + + // Ram write and read. + memory_test + .state + .borrow_mut() + .memwrite(addr, &data, std::mem::size_of::() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x01u8; 8]); + + // Add an overlapping region to write and read again. + let qmp_cmd = format!("{{ \"execute\": \"update_region\", \"arguments\": {{ \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": {}, \"size\": 4096, \"priority\": 99 }} }}", addr); + memory_test.state.borrow_mut().qmp(&qmp_cmd); + memory_test + .state + .borrow_mut() + .memwrite(addr, &data, std::mem::size_of::() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x02u8; 8]); +} + +/// Some region update exception operations. +/// TestStep: +/// 1. Add the wrong attribute.(add read only for ram_device) +/// 2. Repeat adding region. +/// 3. Delete a non-existent region +/// 4. Add a region that extends beyond its father +/// Expect: +/// 1: Success. +/// 2/3: Failed. +#[test] +fn region_update_exception() { + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + + // Add read only attribute for io region. + let ret = memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 2199023255552, \"size\": 4096, \"priority\": 100, \"read_only_mode\": true }}"); + assert_eq!( + *ret.get("error").unwrap(), + json!({"class": JsonString("GenericError".to_string()), "desc": JsonString("set_rom_device_romd failed".to_string())}) + ); + + // Repeat adding region. + let ret = memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Delete a non-existent region + let ret = memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"delete\", \"region_type\": \"io_region\", \"offset\": 2199023255552, \"size\": 4096, \"priority\": 100 }}"); + assert_eq!( + *ret.get("error").unwrap(), + json!({"class": JsonString("GenericError".to_string()), "desc": JsonString("delete subregion failed".to_string())}) + ); + + // Add a region that extends beyond its father + let ret = memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 18446744073709551615, \"size\": 4096, \"priority\": 99 }}"); + assert_eq!( + *ret.get("error").unwrap(), + json!({"class": JsonString("GenericError".to_string()), "desc": JsonString("add subregion failed".to_string())}) + ); + + memory_test.state.borrow_mut().stop(); +} + +/// Rom device region write Test. +/// TestStep: +/// 1. Add a rom_device region with read_only_mode equals false. +/// 2. Write some data([0x01u8; 8]) to the rom device. +/// 3. Read data from the rom device and check it. +/// 4. Write overflow test. +/// 5. Read overflow test. +/// 6. Add a rom_device region with read_only_mode equals true. +/// 7. Write some data([0x01u8; 8]) to the rom device. +/// 8. Read data from the rom device and check it. +/// Expect: +/// 1/2/6/7: Success. +/// 4/5: Failed. +/// 3: Got [0x02u8; 8] from the device. The read and write behavior is the same as io region. +/// 8: Got [0x00u8; 8] fro the device. The write opration does nothing, and read the original data. +#[test] +fn rom_device_region_readwrite() { + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + let addr = 0x100_0000_0000; // 1TB + + // Add a dummy rom device by qmp. The function of the device is to multiply the written value by 2 + // through the write interface and save it, and read the saved value through the read interface. + memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"rom_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99, \"read_only_mode\": false }}"); + let data = [0x01u8; 8]; + memory_test + .state + .borrow_mut() + .memwrite(addr, &data, std::mem::size_of::() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x02u8; 8]); + + // Write overflow + memory_test.state.borrow_mut().memwrite( + addr + PAGE_SIZE - 1, + &data, + std::mem::size_of::() as u64, + ); + // Read overflow + let ret = memory_test + .state + .borrow_mut() + .memread(addr + PAGE_SIZE - 1, std::mem::size_of::() as u64); + assert_eq!(ret, [0x00u8; 8]); + + // Add a dummy rom device by qmp. And set read only mode. The write operation is sent to the + // device. The device can set the write mode to writable according to the device status during + // the write operation, or directly return an error indicating that the write is not allowed. + // The read operation is the same as that of IO region. + memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"rom_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 100, \"read_only_mode\": true }}"); + let data = [0x01u8; 8]; + memory_test + .state + .borrow_mut() + .memwrite(addr, &data, std::mem::size_of::() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x00u8; 8]); + + memory_test.state.borrow_mut().stop(); +} + +/// Ram device region write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data([0x01u8; 8]) to the ram device. +/// 3. Read data from the ram device and check it. +/// 4. Write overflow. +/// 5. Read overflow. +/// 6. Destroy device. +/// Expect: +/// 1/2/6: Success. +/// 4/5: Failed. +/// 3: Got [0x01u8; 8] from the device. The read and write behavior is the same as ram. +#[test] +fn ram_device_region_readwrite() { + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + let addr = 0x100_0000_0000; // 1TB + + memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + let data = [0x01u8; 8]; + memory_test + .state + .borrow_mut() + .memwrite(addr, &data, std::mem::size_of::() as u64); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x01u8; 8]); + + // Write overflow + memory_test.state.borrow_mut().memwrite( + addr + PAGE_SIZE - 1, + &data, + std::mem::size_of::() as u64, + ); + // Read overflow + let ret = memory_test + .state + .borrow_mut() + .memread(addr + PAGE_SIZE - 1, std::mem::size_of::() as u64); + assert_eq!(ret, [0x00u8; 8]); + + memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"delete\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + assert_eq!(ret, [0x0u8; 8]); + + memory_test.state.borrow_mut().stop(); +} + +/// Io region ioeventfd read and write Test. +/// TestStep: +/// 1. Add an io region with ioeventfd(data: 1, size 8). +/// 2. Write 1 to the ioeventfd. +/// 3. Read data from the address and check it. +/// Expect: +/// 1/2: success. +/// 3: Got value 0. +#[test] +fn io_region_ioeventfd() { + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + let addr = 0x100_0000_0000; // 1TB + + memory_test + .state + .borrow_mut() + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99, \"ioeventfd\": true, \"ioeventfd_data\": 1, \"ioeventfd_size\": 8 }}"); + memory_test.state.borrow_mut().writew(addr, 1); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + let cmp = [0x0u8; 8]; + assert_eq!(ret, cmp); + + memory_test.state.borrow_mut().stop(); +} + +/// Shared ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn shared_ram_read_write() { + ram_read_write(&MemoryTest::new( + MEM_SIZE, PAGE_SIZE, true, false, None, None, + )); +} + +/// Prealloc ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn prealloc_ram_read_write() { + ram_read_write(&MemoryTest::new( + MEM_SIZE, PAGE_SIZE, false, true, None, None, + )); +} + +/// Hugepage ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn hugepage_ram_read_write() { + // crate hugetlbfs directory + let _output = Command::new("rm") + .arg("-rf") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to rm directory"); + let _output = Command::new("mkdir") + .arg("-p") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to create directory"); + + // mount hugetlbfs on a directory on host + let output = Command::new("mount") + .arg("-t") + .arg("hugetlbfs") + .arg("hugetlbfs") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to mount hugetlbfs"); + assert!(output.status.success()); + + // set the count of hugepages + let output = Command::new("sysctl") + .arg("vm.nr_hugepages=1024") + .output() + .expect("Failed to set the count of hugepages"); + assert!(output.status.success()); + + ram_read_write(&MemoryTest::new( + MEM_SIZE, + PAGE_SIZE, + false, + false, + Some("/tmp/stratovirt/hugepages".to_string()), + None, + )); + + // remove hugetlbfs + let _output = Command::new("umount") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to mount hugetlbfs"); + let _output = Command::new("rm") + .arg("-rf") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to rm directory"); +} + +/// File backend ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn filebackend_ram_read_write() { + // crate hugetlbfs directory + let _output = Command::new("rm") + .arg("-rf") + .arg("/tmp/stratovirt/dir") + .output() + .expect("Failed to rm directory"); + let _output = Command::new("mkdir") + .arg("-p") + .arg("/tmp/stratovirt/dir") + .output() + .expect("Failed to create directory"); + let _output = Command::new("touch") + .arg("/tmp/stratovirt/dir/ram-file") + .output() + .expect("Failed to create directory"); + + ram_read_write(&MemoryTest::new( + MEM_SIZE, + PAGE_SIZE, + false, + false, + None, + Some("/tmp/stratovirt/dir/ram-file".to_string()), + )); +} + +/// Ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn ram_readwrite_exception() { + let str = "test memory read write"; + const SIZE: u64 = 22; + let str_overflow = "test memory read write overflow"; + const SIZE_OVERFLOW: u64 = 31; + let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); + let addr = 0x100_0000_0000; // 1TB + + // The start address is out of range. + memory_test + .state + .borrow_mut() + .memwrite(addr, str.as_bytes(), SIZE); + let ret = memory_test.state.borrow_mut().memread(addr, SIZE); + assert_eq!(ret, [0u8; SIZE as usize]); + + // The start address is in range, but the size is out of bounds. + memory_test.state.borrow_mut().memwrite( + MEM_SIZE * 1024 * 1024 - SIZE + ADDRESS_BASE, + str_overflow.as_bytes(), + SIZE_OVERFLOW, + ); + let ret = memory_test.state.borrow_mut().memread(addr, SIZE_OVERFLOW); + assert_eq!(ret, [0u8; SIZE_OVERFLOW as usize]); + + memory_test.state.borrow_mut().stop(); +} + +/// Ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// And the read/write will across numa. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn ram_readwrite_numa() { + let mut args: Vec<&str> = Vec::new(); + let mut extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + args.append(&mut extra_args); + + let cpu = 8; + let cpu_args = format!( + "-smp {},sockets=1,cores=4,threads=2 -cpu host,pmu=on -m 2G", + cpu + ); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-object memory-backend-ram,size=1G,id=mem0,host-nodes=0-1,policy=bind" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-object memory-backend-ram,size=1G,id=mem1,host-nodes=0-1,policy=bind" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=0,cpus=0-3,memdev=mem0" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=1,cpus=4-7,memdev=mem1" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=0,dst=1,val=30".split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=1,dst=0,val=30".split(' ').collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + + let str = "test memory read write"; + let start_base = ADDRESS_BASE + MEM_SIZE * 1024 * 1024 / 2 - 4; + test_state + .borrow_mut() + .memwrite(start_base, str.as_bytes(), str.len() as u64); + let ret = test_state + .borrow_mut() + .memread(start_base, str.len() as u64); + assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); + + test_state.borrow_mut().stop(); +} -- Gitee From 7046485f153cdb731f5a256f837fd17402f2ff6b Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 14:11:46 +0800 Subject: [PATCH 0824/2187] virtio-net: fix some problems found by mst 1. fix some error messge; 2. optimize the handle mq code. 3. fix the error vlan id offset. 4. check the queue status when receiving pacnets. 5. check the rx packet length which should include ethernet and virtio_net hdr. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 2 +- virtio/src/net.rs | 106 ++++++++++++++++++++++++++++++---------------- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b747949b9..cb735ac7e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -484,7 +484,7 @@ pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) /// Discard "size" bytes of the front of iovec. pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> { for (index, iov) in iovec.iter_mut().enumerate() { - if iov.len as u64 > size { + if iov.len as u64 >= size { iov.addr.0 += size; iov.len -= size as u32; return Some(&mut iovec[index..]); diff --git a/virtio/src/net.rs b/virtio/src/net.rs index c3c916893..50913b369 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -75,6 +75,12 @@ const CTRL_MAC_TABLE_LEN: usize = 64; const CTRL_MAX_VLAN: u16 = 1 << 12; /// The max num of the mac address. const MAX_MAC_ADDR_NUM: usize = 0xff; +/// The header length of virtio net packet. +const NET_HDR_LENGTH: usize = mem::size_of::(); +/// The legnth of vlan tag. +const VLAN_TAG_LENGTH: usize = 4; +/// The offset of vlan tpid for 802.1Q tag. +const VLAN_TPID_LENGTH: usize = 2; type SenderConfig = Option; @@ -230,20 +236,23 @@ impl CtrlInfo { let mut entries: u32 = 0; *data_iovec = get_buf_and_discard(mem_space, data_iovec, entries.as_mut_bytes()) - .with_context(|| "Failed to get unicast MAC entries".to_string())?; + .with_context(|| "Failed to get MAC entries".to_string())?; if entries == 0 { mac_table.clear(); continue; } let size = entries as u64 * MAC_ADDR_LEN as u64; - if size > Element::iovec_size(data_iovec) { + let res_len = Element::iovec_size(data_iovec); + if size > res_len { bail!("Invalid request for setting mac table."); } if entries as usize > CTRL_MAC_TABLE_LEN - mac_table_len { - *data_iovec = iov_discard_front(data_iovec, size) - .with_context(|| "Failed to discard iovec from front side".to_string())? - .to_vec(); + if size < res_len { + *data_iovec = iov_discard_front(data_iovec, size) + .with_context(|| "Failed to discard iovec from front side".to_string())? + .to_vec(); + } *overflow = true; mac_table.clear(); continue; @@ -251,7 +260,7 @@ impl CtrlInfo { let mut macs = vec![0_u8; size as usize]; *data_iovec = get_buf_and_discard(mem_space, data_iovec, &mut macs) - .with_context(|| "Failed to get multicast MAC entries".to_string())?; + .with_context(|| "Failed to get MAC entries".to_string())?; mac_table.clear(); for i in 0..entries { @@ -351,6 +360,42 @@ impl CtrlInfo { ack } + fn handle_mq( + &mut self, + mem_space: &AddressSpace, + cmd: u8, + data_iovec: &mut Vec, + ) -> u8 { + let mut ack = VIRTIO_NET_OK; + if cmd as u16 == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { + let mut queue_pairs: u16 = 0; + *data_iovec = get_buf_and_discard(mem_space, data_iovec, queue_pairs.as_mut_bytes()) + .unwrap_or_else(|e| { + error!("Failed to get queue pairs {}", e); + ack = VIRTIO_NET_ERR; + Vec::new() + }); + if ack == VIRTIO_NET_ERR { + return ack; + } + + if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) + .contains(&queue_pairs) + { + error!("Invalid queue pairs {}", queue_pairs); + ack = VIRTIO_NET_ERR; + } + } else { + error!( + "Control queue header command can't match {}", + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + ); + ack = VIRTIO_NET_ERR; + } + + ack + } + fn filter_packets(&mut self, buf: &[u8]) -> bool { // Broadcast address: 0xff:0xff:0xff:0xff:0xff:0xff. let bcast = [0xff; MAC_ADDR_LEN]; @@ -361,8 +406,8 @@ impl CtrlInfo { return false; } - if buf[..vlan.len()] == vlan { - let vid = u16::from_be_bytes([buf[14], buf[15]]); + if buf[ETHERNET_HDR_LENGTH - VLAN_TPID_LENGTH..ETHERNET_HDR_LENGTH] == vlan { + let vid = u16::from_be_bytes([buf[ETHERNET_HDR_LENGTH], buf[ETHERNET_HDR_LENGTH + 1]]); let value = if let Some(value) = self.vlan_map.get(&(vid >> 5)) { *value } else { @@ -541,25 +586,11 @@ impl NetCtrlHandler { ); } VIRTIO_NET_CTRL_MQ => { - if ctrl_hdr.cmd as u16 != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { - error!( - "Control queue header command can't match {}", - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET - ); - ack = VIRTIO_NET_ERR; - } - if let Some(mq_desc) = elem.out_iovec.get(1) { - let queue_pairs = self - .mem_space - .read_object::(mq_desc.addr) - .with_context(|| "Failed to read multi queue descriptor")?; - if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) - .contains(&queue_pairs) - { - error!("Invalid queue pairs {}", queue_pairs); - ack = VIRTIO_NET_ERR; - } - } + ack = self.ctrl.ctrl_info.lock().unwrap().handle_mq( + &self.mem_space, + ctrl_hdr.cmd, + &mut data_iovec, + ); } _ => { error!( @@ -677,7 +708,7 @@ struct NetIoHandler { } impl NetIoHandler { - fn read_from_tap(queue: &mut Queue, iovecs: &[libc::iovec], tap: &mut Tap) -> i32 { + fn read_from_tap(iovecs: &[libc::iovec], tap: &mut Tap) -> i32 { // SAFETY: the arguments of readv has been checked and is correct. let size = unsafe { libc::readv( @@ -688,7 +719,6 @@ impl NetIoHandler { } as i32; if size < 0 { let e = std::io::Error::last_os_error(); - queue.vring.push_back(); if e.kind() == std::io::ErrorKind::WouldBlock { return size; } @@ -736,6 +766,9 @@ impl NetIoHandler { fn handle_rx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to rx".to_string()); let mut queue = self.rx.queue.lock().unwrap(); + if !queue.is_enabled() { + return Ok(()); + } let mut rx_packets = 0; while let Some(tap) = self.tap.as_mut() { if queue.vring.avail_ring_len(&self.mem_space)? == 0 { @@ -744,7 +777,7 @@ impl NetIoHandler { } rx_packets += 1; - if rx_packets > self.queue_size { + if rx_packets >= self.queue_size { self.rx .queue_evt .write(1) @@ -777,13 +810,13 @@ impl NetIoHandler { } // Read the data from the tap device. - let size = NetIoHandler::read_from_tap(&mut queue, &iovecs, tap); - if size < 0 { + let size = NetIoHandler::read_from_tap(&iovecs, tap); + if size < (NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH) as i32 { + queue.vring.push_back(); break; } - let net_hdr_len = mem::size_of::(); - let mut buf = vec![0_u8; net_hdr_len + ETHERNET_HDR_LENGTH]; + let mut buf = vec![0_u8; NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH]; get_net_header(&iovecs, &mut buf).and_then(|size| { if size != buf.len() { bail!( @@ -798,7 +831,7 @@ impl NetIoHandler { .ctrl_info .lock() .unwrap() - .filter_packets(&buf[net_hdr_len..]) + .filter_packets(&buf[NET_HDR_LENGTH..]) { queue.vring.push_back(); continue; @@ -1301,8 +1334,7 @@ pub fn create_tap( })? }; - let vnet_hdr_size = mem::size_of::() as u32; - tap.set_hdr_size(vnet_hdr_size) + tap.set_hdr_size(NET_HDR_LENGTH as u32) .with_context(|| "Failed to set tap hdr size")?; taps.push(tap); -- Gitee From 8323bce6d6058177bf890f5b3719186cd194ffbe Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 27 Feb 2023 15:30:19 +0800 Subject: [PATCH 0825/2187] memory: add illegal ioeventfd tests for MST Added an illegal ioeventfd test where the data value is different from the value defined in ioeventfd. Signed-off-by: Li HuaChao --- tests/mod_test/tests/memory_test.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index c8687f9d8..0a024c0cc 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -382,9 +382,12 @@ fn ram_device_region_readwrite() { /// 1. Add an io region with ioeventfd(data: 1, size 8). /// 2. Write 1 to the ioeventfd. /// 3. Read data from the address and check it. +/// 4. Write 2 to the ioeventfd. +/// 5. Read data from the address and check it. /// Expect: -/// 1/2: success. +/// 1/2/4: success. /// 3: Got value 0. +/// 5: Got value 4. #[test] fn io_region_ioeventfd() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); @@ -394,7 +397,7 @@ fn io_region_ioeventfd() { .state .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99, \"ioeventfd\": true, \"ioeventfd_data\": 1, \"ioeventfd_size\": 8 }}"); - memory_test.state.borrow_mut().writew(addr, 1); + memory_test.state.borrow_mut().writeq(addr, 1); let ret = memory_test .state .borrow_mut() @@ -402,6 +405,15 @@ fn io_region_ioeventfd() { let cmp = [0x0u8; 8]; assert_eq!(ret, cmp); + memory_test.state.borrow_mut().writeq(addr, 2); + let ret = memory_test + .state + .borrow_mut() + .memread(addr, std::mem::size_of::() as u64); + let mut cmp = [0x0u8; 8]; + cmp[0] = 4; + assert_eq!(ret, cmp); + memory_test.state.borrow_mut().stop(); } -- Gitee From c63ca310714b064bdbb749c1d728766207794f0e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 26 Feb 2023 19:32:34 +0800 Subject: [PATCH 0826/2187] MST: add virtio-console test case Signed-off-by: Jinhao Gao --- .../mod_test/src/libdriver/virtio_console.rs | 4 +- tests/mod_test/tests/console_test.rs | 496 ++++++++++++++++++ 2 files changed, 498 insertions(+), 2 deletions(-) create mode 100644 tests/mod_test/tests/console_test.rs diff --git a/tests/mod_test/src/libdriver/virtio_console.rs b/tests/mod_test/src/libdriver/virtio_console.rs index 15850e90a..2336b6bb7 100644 --- a/tests/mod_test/src/libdriver/virtio_console.rs +++ b/tests/mod_test/src/libdriver/virtio_console.rs @@ -32,13 +32,13 @@ pub enum ChardevType { pub fn create_console( chardev_type: ChardevType, + pci_slot: u8, + pci_fn: u8, ) -> ( Rc>, Rc>, Rc>, ) { - let pci_slot: u8 = 0x4; - let pci_fn: u8 = 0x0; let mut extra_args: Vec<&str> = Vec::new(); let mut args: Vec<&str> = "-machine virt".split(' ').collect(); diff --git a/tests/mod_test/tests/console_test.rs b/tests/mod_test/tests/console_test.rs new file mode 100644 index 000000000..61e6f7481 --- /dev/null +++ b/tests/mod_test/tests/console_test.rs @@ -0,0 +1,496 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use serde_json::json; +use std::cell::RefCell; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::net::Shutdown; +use std::os::unix::net::UnixStream; +use std::path::Path; +use std::rc::Rc; +use std::time; + +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; +use mod_test::libdriver::virtio_console::{create_console, ChardevType}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libtest::TestState; + +const TIMEOUT_US: u64 = 15 * 1000 * 1000; +const QUEUE_NUM: u16 = 2; +const QUEUE_SIZE: u16 = 256; +const ROWS_DEFAULT: u16 = 0; +const COLS_DEFAULT: u16 = 0; +const EMERG_WR_DEFAULT: u32 = 0; +const VIRTIO_F_VERSION_1: u32 = 32; +const VIRTIO_CONSOLE_F_SIZE: u64 = 0; +const BUFFER_LEN: usize = 96; + +fn console_setup( + console: Rc>, + test_state: Rc>, + alloc: Rc>, +) -> Vec>> { + let features = console.borrow().get_device_features(); + let vqs = console + .borrow_mut() + .init_device(test_state, alloc, features, 2); + vqs +} + +fn verify_output_data(test_state: Rc>, addr: u64, len: u32, test_data: &String) { + let mut data_buf: Vec = Vec::with_capacity(len.try_into().unwrap()); + data_buf.append( + test_state + .borrow() + .memread(addr, len.try_into().unwrap()) + .as_mut(), + ); + let data = String::from_utf8(data_buf).unwrap(); + assert_eq!(data, *test_data); +} + +fn verify_input_data(input: &mut dyn Read, test_data: &String) { + let mut buffer = [0; BUFFER_LEN]; + match input.read(&mut buffer[0..test_data.len()]) { + Ok(size) => { + let response = String::from_utf8_lossy(&buffer[0..size]).to_string(); + assert_eq!(response, *test_data); + } + Err(e) => assert!(false, "Failed to read contents from socket: {}", e), + } +} + +fn get_pty_path(test_state: Rc>) -> String { + let ret = test_state.borrow().qmp("{\"execute\": \"query-chardev\"}"); + if (*ret.get("return").unwrap()).as_array().unwrap().len() != 0 + && (*ret.get("return").unwrap())[0].get("filename").is_some() + { + let filename = (*ret.get("return").unwrap())[0] + .get("filename") + .unwrap() + .to_string() + .replace('"', ""); + let mut file_path: Vec<&str> = filename.split("pty:").collect(); + return file_path.pop().unwrap().to_string(); + } else { + return String::from(""); + } +} + +#[test] +fn test_console_basic() { + let chardev = ChardevType::Pty; + let pci_slot = 0x04; + let pci_fn = 0x0; + let (console, test_state, _alloc) = create_console(chardev, pci_slot, pci_fn); + + assert_eq!( + console.borrow().get_queue_nums(), + QUEUE_NUM, + "The virtqueue number of console is uncorrect or the testcase parament is out of date!" + ); + + assert_eq!( + console.borrow().get_queue_size(), + QUEUE_SIZE, + "The virtqueue size of console is uncorrect or the testcase parament is out of date!" + ); + + assert_eq!( + console.borrow().config_readw(0), + ROWS_DEFAULT, + "The rows of the console config is uncorrect or the testcase parament is out of date!" + ); + + assert_eq!( + console.borrow().config_readw(2), + COLS_DEFAULT, + "The cols of the console config is uncorrect or the testcase parament is out of date!" + ); + + assert_eq!( + console.borrow().config_readl(8), + EMERG_WR_DEFAULT, + "The emerg_wr of the console config is uncorrect or the testcase parament is out of date!" + ); + + console.borrow().config_writew(0, 1); + assert_eq!( + console.borrow().config_readw(0), + ROWS_DEFAULT, + "The console device doesn't support writing config. But config was written!" + ); + + assert_eq!( + console.borrow().get_device_features(), + 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_CONSOLE_F_SIZE, + "The feature which the console supports is uncorrect or the testcase parament is out of date!"); + + test_state.borrow_mut().stop(); +} + +#[test] +fn test_pty_basic() { + let pty = ChardevType::Pty; + let pci_slot = 0x04; + let pci_fn = 0x0; + let (console, test_state, alloc) = create_console(pty, pci_slot, pci_fn); + + let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); + let input_queue = vqs[0].clone(); + let output_queue = vqs[1].clone(); + + let pty_path = get_pty_path(test_state.clone()); + assert_ne!(pty_path, String::from("")); + + let test_data = String::from("Test\n"); + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + test_state.borrow().memwrite( + addr, + test_data.as_bytes(), + test_data.len().try_into().unwrap(), + ); + let free_head = output_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + false, + ); + + console + .borrow() + .kick_virtqueue(test_state.clone(), output_queue.clone()); + console.borrow().poll_used_elem( + test_state.clone(), + output_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + let mut input: Option = None; + match File::open(&pty_path) { + Ok(file) => input = Some(file), + Err(e) => assert!(false, "{}", e), + } + + verify_input_data(&mut input.unwrap(), &test_data); + + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + let free_head = input_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + true, + ); + console + .borrow() + .kick_virtqueue(test_state.clone(), input_queue.clone()); + + let mut output: Option = None; + match File::create(&pty_path) { + Ok(file) => output = Some(file), + Err(e) => assert!(false, "{}", e), + } + match output.unwrap().write(&test_data.as_bytes()) { + Ok(_num) => { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + loop { + let mut len: Option = Some(0); + console.borrow().poll_used_elem( + test_state.clone(), + input_queue.clone(), + free_head, + TIMEOUT_US, + &mut len, + false, + ); + if len.unwrap() != 0 { + verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); + break; + } + assert!(time::Instant::now() - start_time < timeout_us); + } + } + Err(e) => assert!(false, "Failed to write contents to socket: {}", e), + } + + console.borrow_mut().destroy_device(alloc, vqs); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_socket_basic() { + let socket_path = "/tmp/test-console0.sock"; + if Path::new(socket_path).exists() { + fs::remove_file(socket_path).unwrap(); + } + let socket = ChardevType::Socket { + path: String::from(socket_path), + server: true, + nowait: true, + }; + + let pci_slot = 0x4; + let pci_fn = 0x0; + let (console, test_state, alloc) = create_console(socket, pci_slot, pci_fn); + + let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); + let input_queue = vqs[0].clone(); + let output_queue = vqs[1].clone(); + + let mut stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); + stream + .set_nonblocking(true) + .expect("Couldn't set nonblocking"); + + let test_data = String::from("Test\n"); + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + test_state.borrow().memwrite( + addr, + test_data.as_bytes(), + test_data.len().try_into().unwrap(), + ); + let free_head = output_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + false, + ); + + console + .borrow() + .kick_virtqueue(test_state.clone(), output_queue.clone()); + console.borrow().poll_used_elem( + test_state.clone(), + output_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + verify_input_data(&mut stream, &test_data); + + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + let free_head = input_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + true, + ); + console + .borrow() + .kick_virtqueue(test_state.clone(), input_queue.clone()); + + match stream.write(&test_data.as_bytes()) { + Ok(_num) => { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + loop { + let mut len: Option = Some(0); + console.borrow().poll_used_elem( + test_state.clone(), + input_queue.clone(), + free_head, + TIMEOUT_US, + &mut len, + false, + ); + if len.unwrap() != 0 { + verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); + break; + } + assert!(time::Instant::now() - start_time < timeout_us); + } + } + Err(e) => assert!(false, "Failed to write contents to socket: {}", e), + } + + stream + .shutdown(Shutdown::Both) + .expect("shutdown function failed"); + console.borrow_mut().destroy_device(alloc, vqs); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_console_reset() { + let socket_path = "/tmp/test-console1.sock"; + if Path::new(socket_path).exists() { + fs::remove_file(socket_path).unwrap(); + } + let socket = ChardevType::Socket { + path: String::from(socket_path), + server: true, + nowait: true, + }; + + let pci_slot = 0x4; + let pci_fn = 0x0; + let (console, test_state, alloc) = create_console(socket, pci_slot, pci_fn); + + let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); + let input_queue = vqs[0].clone(); + let output_queue = vqs[1].clone(); + + let mut stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); + stream + .set_nonblocking(true) + .expect("Couldn't set nonblocking"); + + let test_data = String::from("Test\n"); + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + test_state.borrow().memwrite( + addr, + test_data.as_bytes(), + test_data.len().try_into().unwrap(), + ); + let free_head = output_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + false, + ); + + console + .borrow() + .kick_virtqueue(test_state.clone(), output_queue.clone()); + let mut len: Option = Some(0); + console.borrow().poll_used_elem( + test_state.clone(), + output_queue.clone(), + free_head, + TIMEOUT_US, + &mut len, + false, + ); + + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + let free_head = input_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + true, + ); + console + .borrow() + .kick_virtqueue(test_state.clone(), input_queue.clone()); + + verify_input_data(&mut stream, &test_data); + + match stream.write(&test_data.as_bytes()) { + Ok(_num) => { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + loop { + let mut len: Option = Some(0); + console.borrow().poll_used_elem( + test_state.clone(), + input_queue.clone(), + free_head, + TIMEOUT_US, + &mut len, + false, + ); + if len.unwrap() != 0 { + verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); + break; + } + assert!(time::Instant::now() - start_time < timeout_us); + } + } + Err(e) => assert!(false, "Failed to write contents to socket: {}", e), + } + + let ret = test_state.borrow().qmp("{\"execute\": \"system_reset\"}"); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + console.borrow_mut().init(pci_slot, pci_fn); + + let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); + let input_queue = vqs[0].clone(); + let output_queue = vqs[1].clone(); + + let test_data = String::from("Test\n"); + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + test_state.borrow().memwrite( + addr, + test_data.as_bytes(), + test_data.len().try_into().unwrap(), + ); + let free_head = output_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + false, + ); + + console + .borrow() + .kick_virtqueue(test_state.clone(), output_queue.clone()); + console.borrow().poll_used_elem( + test_state.clone(), + output_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + verify_input_data(&mut stream, &test_data); + + let addr = alloc.borrow_mut().alloc(test_data.len() as u64); + let free_head = input_queue.borrow_mut().add( + test_state.clone(), + addr, + test_data.len().try_into().unwrap(), + true, + ); + console + .borrow() + .kick_virtqueue(test_state.clone(), input_queue.clone()); + + match stream.write(&test_data.as_bytes()) { + Ok(_num) => { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + loop { + let mut len: Option = Some(0); + console.borrow().poll_used_elem( + test_state.clone(), + input_queue.clone(), + free_head, + TIMEOUT_US, + &mut len, + false, + ); + if len.unwrap() != 0 { + verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); + break; + } + assert!(time::Instant::now() - start_time < timeout_us); + } + } + Err(e) => assert!(false, "Failed to write contents to socket: {}", e), + } + + stream + .shutdown(Shutdown::Both) + .expect("shutdown function failed"); + console.borrow_mut().destroy_device(alloc, vqs); + test_state.borrow_mut().stop(); +} -- Gitee From 2af3bdbf8b5f1d1a3a96eb45c9ffd1a8b4026458 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Sun, 26 Feb 2023 19:32:12 +0800 Subject: [PATCH 0827/2187] MST:add balloon test. Signed-off-by: jiewangqun --- tests/mod_test/src/libtest.rs | 5 + tests/mod_test/tests/balloon_test.rs | 777 +++++++++++++++++++++++++++ 2 files changed, 782 insertions(+) create mode 100644 tests/mod_test/tests/balloon_test.rs diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index abfec4827..92feb4c4c 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -118,6 +118,11 @@ impl TestState { serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap() } + pub fn qmp_read(&self) -> Value { + let timeout = Duration::from_secs(10); + serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap() + } + fn send_test_cmd(&self, cmd: &str) -> String { let timeout = Duration::from_secs(10); self.test_sock.write_line(cmd); diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs new file mode 100644 index 000000000..714700d81 --- /dev/null +++ b/tests/mod_test/tests/balloon_test.rs @@ -0,0 +1,777 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; +use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; +use mod_test::libtest::{test_init, TestState}; +use serde_json::json; +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::{thread, time}; +use util::offset_of; + +const BALLOON_F_DEFLATE_ON_OOM_TEST: u32 = 2; +const BALLOON_F_PRPORTING_TEST: u32 = 5; +const BALLOON_F_VERSION1_TEST: u64 = 32; +const PAGE_SIZE_UNIT: u64 = 4096; +const TIMEOUT_US: u64 = 15 * 1000 * 1000; +const MBSIZE: u64 = 1024 * 1024; + +pub struct VirtioBalloonTest { + pub device: Rc>, + pub state: Rc>, + pub allocator: Rc>, + pub inf_queue: Rc>, + pub def_queue: Rc>, + pub fpr_queue: Option>>, +} + +impl VirtioBalloonTest { + pub fn new(memsize: u64, page_size: u64, shared: bool, fpr: bool, huge: bool) -> Self { + let pci_slot: u8 = 0x4; + let mut extra_args: Vec<&str> = Vec::new(); + let mut fpr_switch = String::from("false"); + let mem_path = format!("-mem-path /tmp/stratovirt/hugepages"); + + let mut args: Vec<&str> = "-machine".split(' ').collect(); + if shared { + args.push("virt,mem-share=on"); + } else { + args.push("virt"); + } + extra_args.append(&mut args); + + let mem_args = format!("-m {}", memsize); + args = mem_args[..].split(' ').collect(); + extra_args.append(&mut args); + + if huge { + args = mem_path[..].split(' ').collect(); + extra_args.append(&mut args); + } + + if fpr { + fpr_switch = String::from("true"); + } + let dev_args = format!( + "-device {},id=drv0,bus=pcie.0,addr={}.0,free-page-reporting={}", + "virtio-balloon-pci", pci_slot, fpr_switch + ); + args = dev_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new_bymem(test_state.clone(), memsize * MBSIZE, page_size); + let allocator = machine.allocator.clone(); + + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + dev.borrow_mut().init(pci_slot, 0); + + let features = dev.borrow_mut().get_device_features(); + let inf_queue; + let def_queue; + let mut fpr_queue = None; + if fpr { + let ques = + dev.borrow_mut() + .init_device(test_state.clone(), allocator.clone(), features, 3); + inf_queue = ques[0].clone(); + def_queue = ques[1].clone(); + fpr_queue = Some(ques[2].clone()); + } else { + let ques = + dev.borrow_mut() + .init_device(test_state.clone(), allocator.clone(), features, 2); + inf_queue = ques[0].clone(); + def_queue = ques[1].clone(); + } + + VirtioBalloonTest { + device: dev, + state: test_state, + allocator, + inf_queue, + def_queue, + fpr_queue, + } + } +} + +fn inflate_fun(shared: bool) { + let page_num = 255_u32; + let mut idx = 0_u32; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, false); + + let free_page = balloon + .allocator + .borrow_mut() + .alloc(page_num as u64 * PAGE_SIZE_UNIT); + let pfn = (free_page >> 12) as u32; + let pfn_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); + while idx < page_num { + balloon + .state + .borrow_mut() + .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + idx += 1; + } + // balloon Illegal addresses + balloon.state.borrow_mut().writel(pfn_addr, pfn - 1024); + let free_head = balloon + .inf_queue + .borrow_mut() + .add(balloon.state.clone(), pfn_addr, 4, false); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.inf_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.inf_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + // begin balloon addresses + let mut loop_num = 0_u32; + let mut msg = Vec::new(); + + while loop_num < page_num { + balloon + .state + .borrow_mut() + .writel(pfn_addr + 4 * loop_num as u64, pfn + loop_num); + let entry = TestVringDescEntry { + data: pfn_addr + (loop_num as u64 * 4), + len: 4, + write: false, + }; + msg.push(entry); + loop_num += 1; + } + let free_head = balloon + .inf_queue + .borrow_mut() + .add_chained(balloon.state.clone(), msg); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.inf_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.inf_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + balloon.state.borrow_mut().stop(); +} + +fn balloon_fun(shared: bool, huge: bool) { + let page_num = 255_u32; + let mut idx = 0_u32; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, huge); + + let free_page = balloon + .allocator + .borrow_mut() + .alloc(page_num as u64 * PAGE_SIZE_UNIT); + let pfn = (free_page >> 12) as u32; + let pfn_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); + while idx < page_num { + balloon + .state + .borrow_mut() + .writel(pfn_addr + 4 * idx as u64, pfn + idx); + balloon + .state + .borrow_mut() + .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + idx += 1; + } + + // begin inflate addresses + let mut loop_num = 0_u32; + let mut msg = Vec::new(); + + while loop_num < page_num { + let entry = TestVringDescEntry { + data: pfn_addr + (loop_num as u64 * 4), + len: 4, + write: false, + }; + msg.push(entry); + loop_num += 1; + } + let free_head = balloon + .inf_queue + .borrow_mut() + .add_chained(balloon.state.clone(), msg); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.inf_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.inf_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + // begin deflate addresses + let mut loop_num = 0_u32; + let mut msg = Vec::new(); + + while loop_num < page_num { + let entry = TestVringDescEntry { + data: pfn_addr + (loop_num as u64 * 4), + len: 4, + write: false, + }; + msg.push(entry); + loop_num += 1; + } + let free_head = balloon + .def_queue + .borrow_mut() + .add_chained(balloon.state.clone(), msg); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.def_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.def_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + // begin deflate Illegal addresses + balloon.state.borrow_mut().writel(pfn_addr, pfn - 1024); + let free_head = balloon + .def_queue + .borrow_mut() + .add(balloon.state.clone(), pfn_addr, 4, false); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.def_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.def_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + balloon.state.borrow_mut().stop(); +} + +/// balloon device inflate test +/// TestStep: +/// 1.Init device +/// 2.Populate the inflate queue with illegal addresses +/// 3.Populate the inflate queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.Memory need by addr +#[test] +fn balloon_inflate_001() { + inflate_fun(false); +} +/// balloon device inflate test +/// TestStep: +/// 1.Init device +/// 2.Populate the inflate queue with illegal addresses +/// 3.Populate the inflate queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.memory released by addr +#[test] +fn balloon_inflate_002() { + inflate_fun(true); +} + +fn create_huge_mem_path() { + let _output = Command::new("rm") + .arg("-rf") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to rm dir"); + + let _output = Command::new("mkdir") + .arg("-p") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to mkdir dir"); + + let _output = Command::new("mount") + .arg("-t") + .arg("hugetlbfs") + .arg("hugetlbfs") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to mount dir"); + + let _output = Command::new("sysctl") + .arg("vm.nr_hugepages=1024") + .output() + .expect("Failed to set count hugepages"); +} + +fn clean_huge_mem_path() { + let _output = Command::new("umount") + .arg("/tmp/stratovirt/hugepages") + .output() + .expect("Failed to mount dir"); +} + +/// balloon device deflate and inflate test +/// TestStep: +/// 1.Init device +/// 2.Populate the inflate queue with illegal addresses +/// 3.Populate the inflate queue with legal addresses +/// 4.Populate the deflate queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.Memory re by addr +/// 4.Free memory +#[test] +fn balloon_fun_001() { + balloon_fun(false, false); +} + +/// balloon device deflate and inflate test +/// TestStep: +/// 1.Init device +/// 2.Populate the inflate queue with illegal addresses +/// 3.Populate the inflate queue with legal addresses +/// 4.Populate the deflate queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.Memory reallocte by addr +/// 4.Free memory +#[test] +fn balloon_fun_002() { + balloon_fun(true, false); +} + +/// TestStep: +/// 1.Init device +/// 2.Populate the inflate queue with illegal addresses +/// 3.Populate the inflate queue with legal addresses +/// 4.Populate the deflate queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.Memory reallocte by addr +/// 4.Free memory +#[test] +fn balloon_huge_fun_001() { + create_huge_mem_path(); + balloon_fun(false, true); + balloon_fun(true, true); + clean_huge_mem_path(); +} + +/// balloon device features config test +/// TestStep: +/// 1.Init device +/// 2.set guest feature 0xFFFFFFFFFFFFFFFF +/// Expect: +/// 1.Success +/// 2.guest feature equel device feature +#[test] +fn balloon_feature_001() { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let mem_args = format!("-m {}", 128); + args = mem_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let dev_args = format!( + "-device {},id=drv0,bus=pcie.{},addr={}.0", + "virtio-balloon-pci", pci_fn, pci_slot + ); + args = dev_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new_bymem(test_state.clone(), 128 * MBSIZE, PAGE_SIZE_UNIT); + let allocator = machine.allocator.clone(); + + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + dev.borrow_mut().init(pci_slot, pci_fn); + + dev.borrow_mut().pci_dev.enable_msix(); + dev.borrow_mut() + .setup_msix_configuration_vector(allocator.clone(), 0); + + let features = dev.borrow_mut().get_device_features(); + + dev.borrow_mut().set_guest_features(0xFFFFFFFFFFFFFFFF); + let features_guest = dev.borrow_mut().get_guest_features(); + assert_eq!(features, features_guest); + + test_state.borrow_mut().stop(); +} + +/// balloon device features config test +/// TestStep: +/// 1.Init device +/// 2.get device feature +/// Expect: +/// 1.Success +/// 2.feature OK +#[test] +fn balloon_feature_002() { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let mem_args = format!("-m {}", 128); + args = mem_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let pci_args = format!( + "-device {},id=drv0,bus=pcie.{},addr={}.0,deflate-on-oom=true,free-page-reporting=true", + "virtio-balloon-pci", pci_fn, pci_slot + ); + args = pci_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new_bymem(test_state.clone(), 128 * MBSIZE, PAGE_SIZE_UNIT); + let allocator = machine.allocator.clone(); + + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + dev.borrow_mut().init(pci_slot, pci_fn); + + dev.borrow_mut().pci_dev.enable_msix(); + dev.borrow_mut() + .setup_msix_configuration_vector(allocator.clone(), 0); + + let features = dev.borrow_mut().get_device_features(); + + assert_eq!( + features, + 1u64 << BALLOON_F_VERSION1_TEST + | 1u64 << BALLOON_F_PRPORTING_TEST + | 1u64 << BALLOON_F_DEFLATE_ON_OOM_TEST + ); + + dev.borrow_mut() + .set_guest_features(1u64 << BALLOON_F_VERSION1_TEST); + let features_guest = dev.borrow_mut().get_guest_features(); + assert_eq!(1u64 << BALLOON_F_VERSION1_TEST, features_guest); + + test_state.borrow_mut().stop(); +} + +fn balloon_fpr_fun(shared: bool) { + let page_num = 255_u32; + let mut idx = 0_u32; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, true, false); + + let free_page = balloon + .allocator + .borrow_mut() + .alloc(page_num as u64 * PAGE_SIZE_UNIT); + let pfn = (free_page >> 12) as u32; + let pfn_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); + while idx < page_num { + balloon + .state + .borrow_mut() + .writel(pfn_addr + 4 * idx as u64, pfn + idx); + balloon + .state + .borrow_mut() + .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + idx += 1; + } + // balloon Illegal addresses + balloon.state.borrow_mut().writel(pfn_addr, pfn - 1024); + let fpr = balloon.fpr_queue.unwrap(); + let free_head = fpr + .borrow_mut() + .add(balloon.state.clone(), pfn_addr, 4, true); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), fpr.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + fpr.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + // begin fpr addresses + let mut loop_num = 0_u32; + let mut msg = Vec::new(); + + while loop_num < page_num { + let entry = TestVringDescEntry { + data: pfn_addr + (loop_num as u64 * 4), + len: 4, + write: true, + }; + msg.push(entry); + loop_num += 1; + } + let free_head = fpr.borrow_mut().add_chained(balloon.state.clone(), msg); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), fpr.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + fpr.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + balloon.state.borrow_mut().stop(); +} + +/// balloon device fpr features test +/// TestStep: +/// 1.Init device +/// 2.Populate the fpr queue with illegal addresses +/// 3.Populate the fpr queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.Free memory +#[test] +fn balloon_fpr_001() { + balloon_fpr_fun(true); +} + +/// balloon device fpr features test +/// TestStep: +/// 1.Init device +/// 2.Populate the fpr queue with illegal addresses +/// 3.Populate the fpr queue with legal addresses +/// Expect: +/// 1.Success +/// 2.There are no exceptions in the process +/// 3.Free memory +#[test] +fn balloon_fpr_002() { + balloon_fpr_fun(false); +} + +struct VirtioBalloonConfig { + /// The target page numbers of balloon device. + pub num_pages: u32, + /// Number of pages we've actually got in balloon device. + pub actual: u32, +} + +#[test] +fn query() { + let balloon = VirtioBalloonTest::new(2048, PAGE_SIZE_UNIT, false, false, false); + let ret = balloon + .state + .borrow_mut() + .qmp("{\"execute\": \"query-balloon\"}"); + + assert_eq!( + *ret.get("return").unwrap(), + json!({"actual": 2147483648 as u64}) + ); + + balloon.state.borrow_mut().stop(); +} + +/// balloon device qmp config test +/// TestStep: +/// 1.Init device +/// 2.qmp config page 512M +/// 3.qmp query result 512M +/// Expect: +/// 1/2/3.Success +#[test] +fn balloon_config_001() { + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, false); + + balloon + .state + .borrow_mut() + .qmp("{\"execute\": \"balloon\", \"arguments\": {\"value\": 536870912}}"); + let ret = balloon.state.borrow_mut().qmp_read(); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + let num_pages = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, num_pages) as u64); + assert_eq!(num_pages, 131072); + let actual = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, actual) as u64); + assert_eq!(actual, 0); + balloon + .device + .borrow_mut() + .config_writel(offset_of!(VirtioBalloonConfig, actual) as u64, 131072); + let actual = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, actual) as u64); + assert_eq!(actual, 131072); + let _actual = balloon + .device + .borrow_mut() + .config_readl((offset_of!(VirtioBalloonConfig, actual) + 8) as u64); + let ten_millis = time::Duration::from_millis(10); + thread::sleep(ten_millis); + let ret = balloon.state.borrow_mut().qmp_read(); + assert_eq!( + *ret.get("data").unwrap(), + json!({"actual": 536870912 as u64}) + ); + + balloon + .state + .borrow_mut() + .qmp("{\"execute\": \"balloon\", \"arguments\": {\"value\": 1610612736}}"); + let num_pages = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, num_pages) as u64); + assert_eq!(num_pages, 0); + balloon.state.borrow_mut().stop(); +} + +/// balloon device qmp config test +/// TestStep: +/// 1.Init device +/// 2.qmp config page 512M +/// 3.qmp query result 512M +/// Expect: +/// 1/2/3.Success +#[test] +fn balloon_config_002() { + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, true); + + balloon + .state + .borrow_mut() + .qmp("{\"execute\": \"balloon\", \"arguments\": {\"value\": 536870912}}"); + let ret = balloon.state.borrow_mut().qmp_read(); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + let num_pages = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, num_pages) as u64); + assert_eq!(num_pages, 131072); + let actual = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, actual) as u64); + assert_eq!(actual, 0); + balloon + .device + .borrow_mut() + .config_writel(offset_of!(VirtioBalloonConfig, actual) as u64, 131072); + let actual = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, actual) as u64); + assert_eq!(actual, 131072); + let _actual = balloon + .device + .borrow_mut() + .config_readl((offset_of!(VirtioBalloonConfig, actual) + 8) as u64); + let ten_millis = time::Duration::from_millis(10); + thread::sleep(ten_millis); + let ret = balloon.state.borrow_mut().qmp_read(); + assert_eq!( + *ret.get("data").unwrap(), + json!({"actual": 536870912 as u64}) + ); + + balloon + .state + .borrow_mut() + .qmp("{\"execute\": \"balloon\", \"arguments\": {\"value\": 1610612736}}"); + let num_pages = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, num_pages) as u64); + assert_eq!(num_pages, 0); + balloon.state.borrow_mut().stop(); +} + +/// balloon device deactive config test +/// TestStep: +/// 1.Init device +/// 2.guest write queue disable +/// Expect: +/// 1/2.Success +#[test] +fn balloon_deactive_001() { + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, false); + + let bar = balloon.device.borrow().bar; + let common_base = balloon.device.borrow().common_base as u64; + + balloon.device.borrow().pci_dev.io_writel( + bar, + common_base + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, + 0, + ); + + let ten_millis = time::Duration::from_millis(10); + thread::sleep(ten_millis); + + let ret = balloon + .state + .borrow_mut() + .qmp("{\"execute\": \"query-balloon\"}"); + assert_eq!( + *ret.get("return").unwrap(), + json!({"actual": 1073741824 as u64}) + ); + balloon.state.borrow_mut().stop(); +} -- Gitee From d9b16949088dc4250b659ff113d18e31087f2063 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 14:13:22 +0800 Subject: [PATCH 0828/2187] tests: add virti-net test case Add mst test case for virtio net. Signed-off-by: Yan Wang --- tests/mod_test/src/libdriver/virtio.rs | 15 +- .../src/libdriver/virtio_pci_modern.rs | 8 +- tests/mod_test/tests/virtio_net.rs | 1950 +++++++++++++++++ 3 files changed, 1962 insertions(+), 11 deletions(-) create mode 100644 tests/mod_test/tests/virtio_net.rs diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 101abc2ac..0392dbcae 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -151,7 +151,7 @@ pub trait VirtioDeviceOps { pub struct VringDesc { addr: u64, len: u32, - flags: u16, + pub flags: u16, next: u16, } @@ -169,14 +169,14 @@ pub struct VringAvail { #[repr(C, packed(4))] pub struct VringUsedElem { id: u32, - len: u32, + pub len: u32, } #[repr(C, packed(4))] pub struct VringUsed { flags: u16, - idx: u16, - ring: Vec, + pub idx: u16, + pub ring: Vec, } #[allow(unused)] @@ -468,7 +468,7 @@ impl TestVirtQueue { ); } - fn update_avail(&self, test_state: Rc>, desc_idx: u32) { + pub fn update_avail(&self, test_state: Rc>, desc_idx: u32) { let idx: u16 = test_state .borrow() .readw(self.avail + offset_of!(VringAvail, idx) as u64); @@ -541,6 +541,7 @@ impl TestVirtQueue { &mut self, test_state: Rc>, indirect: TestVringIndirectDesc, + mixed: bool, ) -> u32 { assert!(indirect.index >= indirect.elem); @@ -552,7 +553,9 @@ impl TestVirtQueue { next: 0, }; self.add_elem_to_desc(test_state.clone(), desc_elem); - self.update_avail(test_state.clone(), free_head); + if !mixed { + self.update_avail(test_state.clone(), free_head); + } free_head } diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index 290f8fe68..77a619fd5 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -709,10 +709,8 @@ impl VirtioPCIMSIXOps for TestVirtioPciDev { self.bar, self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, ); - assert_eq!( - vector, vector_get, - "WARN: set queue vector {}, get vector {}", - vector, vector_get - ); + if vector_get != vector { + println!("WARN: set vector {}, get vector {}", vector, vector_get); + } } } diff --git a/tests/mod_test/tests/virtio_net.rs b/tests/mod_test/tests/virtio_net.rs new file mode 100644 index 000000000..5bd6dd170 --- /dev/null +++ b/tests/mod_test/tests/virtio_net.rs @@ -0,0 +1,1950 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use rand::Rng; +use serde_json::json; +use std::cell::RefCell; +use std::mem::size_of; +use std::process::Command; +use std::rc::Rc; +use std::thread::sleep; +use std::time; +use util::byte_code::ByteCode; +use util::offset_of; + +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{ + TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VringUsed, VringUsedElem, + VIRTIO_CONFIG_S_DRIVER_OK, VIRTIO_CONFIG_S_NEEDS_RESET, VIRTIO_F_VERSION_1, + VIRTIO_RING_F_EVENT_IDX, VRING_DESC_SIZE, +}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libtest::{test_init, TestState}; + +/// Device handles packets with partial checksum. +const VIRTIO_NET_F_CSUM: u32 = 0; +/// Driver handles packets with partial checksum. +const VIRTIO_NET_F_GUEST_CSUM: u32 = 1; +/// Driver can receive TSOv4. +const VIRTIO_NET_F_GUEST_TSO4: u32 = 7; +/// Driver can receive TSOv6. +const VIRTIO_NET_F_GUEST_TSO6: u32 = 8; +/// Driver can receive UFO. +const VIRTIO_NET_F_GUEST_UFO: u32 = 10; +/// Device can receive TSOv4. +const VIRTIO_NET_F_HOST_TSO4: u32 = 11; +/// Device can receive TSOv6. +const VIRTIO_NET_F_HOST_TSO6: u32 = 12; +/// Device can receive UFO. +const VIRTIO_NET_F_HOST_UFO: u32 = 14; +/// Control channel is available. +const VIRTIO_NET_F_CTRL_VQ: u32 = 17; +/// Control channel RX mode support. +const VIRTIO_NET_F_CTRL_RX: u32 = 18; +/// Control channel VLAN filtering. +const VIRTIO_NET_F_CTRL_VLAN: u32 = 19; +/// Extra RX mode control support. +const VIRTIO_NET_F_CTRL_RX_EXTRA: u32 = 20; +/// Set Mac Address through control channel. +const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23; + +/// The device sets control ok status to driver. +pub const VIRTIO_NET_OK: u8 = 0; +/// The device sets control err status to driver. +pub const VIRTIO_NET_ERR: u8 = 1; + +/// Driver can send control commands. +pub const VIRTIO_NET_CTRL_RX: u8 = 0; +/// Control commands for promiscuous mode. +pub const VIRTIO_NET_CTRL_RX_PROMISC: u8 = 0; +/// Control commands for all-multicast receive. +pub const VIRTIO_NET_CTRL_RX_ALLMULTI: u8 = 1; +/// Control commands for all-unicast receive. +pub const VIRTIO_NET_CTRL_RX_ALLUNI: u8 = 2; +/// Control commands for suppressing multicast receive. +pub const VIRTIO_NET_CTRL_RX_NOMULTI: u8 = 3; +/// Control commands for suppressing unicast receive. +pub const VIRTIO_NET_CTRL_RX_NOUNI: u8 = 4; +/// Control commands for suppressing broadcast receive. +pub const VIRTIO_NET_CTRL_RX_NOBCAST: u8 = 5; + +/// The driver can send control commands for MAC address filtering. +pub const VIRTIO_NET_CTRL_MAC: u8 = 1; +/// The driver sets the unicast/multicast addresse table. +pub const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0; +/// The driver sets the default MAC address which rx filtering accepts. +pub const VIRTIO_NET_CTRL_MAC_ADDR_SET: u8 = 1; + +/// The driver can send control commands for vlan filtering. +pub const VIRTIO_NET_CTRL_VLAN: u8 = 2; +/// The driver adds a vlan id to the vlan filtering table. +pub const VIRTIO_NET_CTRL_VLAN_ADD: u8 = 0; +/// The driver adds a vlan id from the vlan filtering table. +pub const VIRTIO_NET_CTRL_VLAN_DEL: u8 = 1; + +/// Driver configure the class before enabling virtqueue. +pub const VIRTIO_NET_CTRL_MQ: u8 = 4; +/// Driver configure the command before enabling virtqueue. +pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0; +/// The minimum pairs of multiple queue. +pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: u16 = 1; +/// The maximum pairs of multiple queue. +pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000; +/// Support more than one virtqueue. +pub const VIRTIO_BLK_F_MQ: u32 = 12; + +const QUEUE_SIZE_NET: u16 = 256; + +const DEFAULT_NET_FEATURES: u64 = 1 << VIRTIO_F_VERSION_1 + | 1 << VIRTIO_NET_F_CSUM + | 1 << VIRTIO_NET_F_GUEST_CSUM + | 1 << VIRTIO_NET_F_GUEST_TSO4 + | 1 << VIRTIO_NET_F_GUEST_TSO6 + | 1 << VIRTIO_NET_F_GUEST_UFO + | 1 << VIRTIO_NET_F_HOST_TSO4 + | 1 << VIRTIO_NET_F_HOST_TSO6 + | 1 << VIRTIO_NET_F_HOST_UFO + | 1 << VIRTIO_NET_F_CTRL_RX + | 1 << VIRTIO_NET_F_CTRL_VLAN + | 1 << VIRTIO_NET_F_CTRL_RX_EXTRA + | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR + | 1 << VIRTIO_NET_F_CTRL_VQ + | 1 << VIRTIO_RING_F_EVENT_IDX; + +const TIMEOUT_US: u64 = 15 * 1000 * 1000; + +const VIRTIO_NET_HDR_SIZE: usize = size_of::(); +/// dest_mac(6), source_mac(6), ether_type(2). +const ETHERNET_HDR_SIZE: usize = 14; +/// Arp packet header. +const ARP_HDR_SIZE: usize = 8; + +/// The maximum incoming packet(tcp/udp): 65536 byte, +/// plus ethernet header: 14 byte, +/// plus virtio_net_hdr: 12 byte. +const MAX_PACKET_LEN: u64 = 65562; + +/// The Mac Address length. +const MAC_ADDR_LEN: usize = 6; +/// The source mac address in arp packet. +const ARP_SOURCE_MAC: [u8; MAC_ADDR_LEN] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]; +const CMD_LINE_MAC: [u8; MAC_ADDR_LEN] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x57]; +const MAX_MAC_TABLE_LEN: usize = 64; +const TEST_MAC_ADDR_NUMS: u8 = 3; + +static USED_ELEM_SIZE: u64 = size_of::() as u64; + +#[repr(C, packed)] +pub struct VirtioNetConfig { + /// Mac Address. + pub mac: [u8; MAC_ADDR_LEN], + /// Device status. + pub status: u16, + /// Maximum number of each of transmit and receive queues. + pub max_virtqueue_pairs: u16, + /// Maximum Transmission Unit. + pub mtu: u16, + /// Speed, in units of 1Mb. + pub speed: u32, + /// 0x00 - half duplex + /// 0x01 - full duplex + pub duplex: u8, +} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct CtrlHdr { + class: u8, + cmd: u8, +} +impl ByteCode for CtrlHdr {} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct CtrlMacAddr { + ctrl_hdr: CtrlHdr, + mac: [u8; MAC_ADDR_LEN], + ack: u8, +} +impl ByteCode for CtrlMacAddr {} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct CtrlRxInfo { + ctrl_hdr: CtrlHdr, + switch: u8, + ack: u8, +} +impl CtrlRxInfo { + pub fn new(class: u8, cmd: u8, switch: u8) -> Self { + CtrlRxInfo { + ctrl_hdr: CtrlHdr { class, cmd }, + switch, + ack: 0xff, + } + } +} +impl ByteCode for CtrlRxInfo {} + +#[repr(C, packed)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct CtrlVlanInfo { + ctrl_hdr: CtrlHdr, + vid: u16, + ack: u8, +} +impl CtrlVlanInfo { + pub fn new(class: u8, cmd: u8, vid: u16) -> Self { + CtrlVlanInfo { + ctrl_hdr: CtrlHdr { class, cmd }, + vid, + ack: 0xff, + } + } +} +impl ByteCode for CtrlVlanInfo {} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct EthernetHdr { + dst_mac: [u8; MAC_ADDR_LEN], + src_mac: [u8; MAC_ADDR_LEN], + // 0x0800: IP + // 0x0806: ARP + // 0x86dd: IPV6 + // 0x0810: 802.1Q Tag, it has vlan id + e_type: [u8; 2], +} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct EthernetHdrVlan { + dst_mac: [u8; MAC_ADDR_LEN], + src_mac: [u8; MAC_ADDR_LEN], + tpid: [u8; 2], + vlan_id: [u8; 2], + // 0x0800: IP + // 0x0806: ARP + // 0x86dd: IPV6 + // 0x0810: 802.1Q Tag, it has vlan id + e_type: [u8; 2], +} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct ArpPacket { + h_type: [u8; 2], + p_type: [u8; 2], + h_len: u8, + p_len: u8, + op: [u8; 2], + src_mac: [u8; 6], + src_ip: [u8; 4], + dst_mac: [u8; 6], + dst_ip: [u8; 4], +} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct ArpRequestPacket { + net_hdr: VirtioNetHdr, + eth_hdr: EthernetHdr, + arp_packet: ArpPacket, +} +impl ByteCode for ArpRequestPacket {} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy, Default)] +struct ArpRequestPacketVlan { + net_hdr: VirtioNetHdr, + eth_hdr: EthernetHdrVlan, + arp_packet: ArpPacket, +} +impl ByteCode for ArpRequestPacketVlan {} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy)] +struct MacAddress { + address: [u8; MAC_ADDR_LEN], +} +impl ByteCode for MacAddress {} +impl Default for MacAddress { + fn default() -> Self { + MacAddress { + address: [0; MAC_ADDR_LEN], + } + } +} + +#[repr(C, packed(2))] +#[allow(unused)] +#[derive(Clone, Copy)] +struct CtrlMacTableReq { + ctrl_hdr: CtrlHdr, + uni_entries: u32, + uni_macs: [MacAddress; MAX_MAC_TABLE_LEN + 1], + mul_entries: u32, + mul_macs: [MacAddress; MAX_MAC_TABLE_LEN + 1], + ack: u8, +} +impl ByteCode for CtrlMacTableReq {} +impl Default for CtrlMacTableReq { + fn default() -> Self { + CtrlMacTableReq { + ctrl_hdr: CtrlHdr::default(), + uni_entries: 0, + uni_macs: [MacAddress::default(); MAX_MAC_TABLE_LEN + 1], + mul_entries: 0, + mul_macs: [MacAddress::default(); MAX_MAC_TABLE_LEN + 1], + ack: 0xff, + } + } +} + +/// Packet header. +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +struct VirtioNetHdr { + flags: u8, + gso_type: u8, + hdr_len: u16, + gso_size: u16, + csum_start: u16, + csum_offset: u16, + num_buffers: u16, +} +impl ByteCode for VirtioNetHdr {} + +/// Execute cmd used to create br/tap. +fn execute_cmd(cmd: String) { + let args = cmd.split(' ').collect::>(); + if args.len() <= 0 { + return; + } + + let mut cmd_exe = Command::new(args[0]); + for i in 1..args.len() { + cmd_exe.arg(args[i]); + } + + let output = cmd_exe + .output() + .expect(format!("Failed to execute {}", cmd).as_str()); + assert!(output.status.success()); +} + +fn create_tap(id: u8, mq: bool) { + let br_name = "qbr".to_string() + &id.to_string(); + let tap_name = "qtap".to_string() + &id.to_string(); + execute_cmd("brctl addbr ".to_string() + &br_name); + if mq { + execute_cmd( + "ip tuntap add ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), + ); + } else { + execute_cmd("ip tuntap add ".to_string() + &tap_name + &" mode tap".to_string()); + } + execute_cmd("brctl addif ".to_string() + &br_name + &" ".to_string() + &tap_name); + execute_cmd("ip link set ".to_string() + &br_name + &" up".to_string()); + execute_cmd("ip link set ".to_string() + &tap_name + &" up".to_string()); + execute_cmd( + "ip address add 2.1.1.".to_string() + &id.to_string() + &"/24 dev ".to_string() + &br_name, + ); + execute_cmd( + "arp -s 2.1.1.".to_string() + + &(id + 2).to_string() + + &" ee:a6:10:07:ec:".to_string() + + &id.to_string(), + ); +} + +fn clear_tap(id: u8, mq: bool) { + let br_name = "qbr".to_string() + &id.to_string(); + let tap_name = "qtap".to_string() + &id.to_string(); + execute_cmd("ip link set ".to_string() + &tap_name + &" down".to_string()); + execute_cmd("ip link set ".to_string() + &br_name + &" down".to_string()); + if mq { + execute_cmd( + "ip tuntap del ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), + ); + } else { + execute_cmd("ip tuntap del ".to_string() + &tap_name + &" mode tap".to_string()); + } + execute_cmd("brctl delbr ".to_string() + &br_name); +} + +#[allow(unused)] +pub fn create_net( + id: u8, + mq: bool, + num_queues: u16, + with_mac: bool, + iothread: bool, +) -> ( + Rc>, + Rc>, + Rc>, +) { + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + let mut iothread_arg = ""; + if iothread { + let mut args: Vec<&str> = "-object iothread,id=iothread1".split(' ').collect(); + extra_args.append(&mut args); + iothread_arg = ",iothread=iothread1"; + } + // Multi-queue command line. + let mut mq_flag = ""; + let mut mq_queues = "".to_string(); + if mq { + mq_flag = ",mq=on"; + mq_queues = ",queues=".to_string() + &num_queues.to_string(); + } + let mut mac_address = ""; + if with_mac { + // Same as CMD_LINE_MAC. + mac_address = ",mac=52:54:00:12:34:57"; + } + let net_pci_args = format!( + "-device {},id=net0,netdev=netdev0,bus=pcie.{},addr={}.0{}{}{}", + "virtio-net-pci", pci_fn, pci_slot, mq_flag, mac_address, iothread_arg, + ); + args = net_pci_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let net_args = + String::from("-netdev tap,id=netdev0,ifname=qtap") + &id.to_string() + &mq_queues; + args = net_args.split(' ').collect(); + extra_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + let virtio_net = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + virtio_net.borrow_mut().init(pci_slot, pci_fn); + + (virtio_net, test_state, allocator) +} + +fn set_up( + id: u8, + mq: bool, + num_queues: u16, + with_mac: bool, +) -> ( + Rc>, + Rc>, + Rc>, +) { + create_tap(id, mq); + create_net(id, mq, num_queues, with_mac, false) +} + +// Set the iothread argument in comand line. +fn set_up_iothread( + id: u8, + mq: bool, + num_queues: u16, + with_mac: bool, +) -> ( + Rc>, + Rc>, + Rc>, +) { + create_tap(id, mq); + create_net(id, mq, num_queues, with_mac, true) +} + +fn tear_down( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, + id: u8, + mq: bool, +) { + net.borrow_mut().destroy_device(alloc.clone(), vqs); + test_state.borrow_mut().stop(); + clear_tap(id, mq); +} + +/// Alloc space for rx virtqueue. +fn fill_rx_vq( + test_state: Rc>, + alloc: Rc>, + vq: Rc>, +) { + let size = vq.borrow().size; + for _ in 0..size { + let addr = alloc.borrow_mut().alloc(MAX_PACKET_LEN).try_into().unwrap(); + vq.borrow_mut() + .add(test_state.clone(), addr, MAX_PACKET_LEN as u32, true); + } + vq.borrow().set_used_event(test_state.clone(), 0); +} + +fn init_net_device( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + features: u64, + num_queues: usize, +) -> Vec>> { + net.borrow_mut().reset(); + net.borrow_mut().set_acknowledge(); + net.borrow_mut().set_driver(); + net.borrow_mut().negotiate_features(features); + net.borrow_mut().set_features_ok(); + net.borrow_mut().pci_dev.enable_msix(); + net.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + let vqs = net + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), num_queues); + // vqs[0] is rx queue. + fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); + net.borrow().set_driver_ok(); + + vqs +} + +fn check_arp_mac( + net: Rc>, + test_state: Rc>, + vqs: Vec>>, + arp_request: &[u8], + need_reply: bool, +) { + let mut start = 0_u64; + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + let timeout_us_no_reply = time::Duration::from_micros(TIMEOUT_US / 5); + loop { + if need_reply { + assert!(time::Instant::now() - start_time < timeout_us); + if !net.borrow().queue_was_notified(vqs[0].clone()) { + continue; + } + } else if time::Instant::now() - start_time > timeout_us_no_reply { + return; + } + + let idx = test_state + .borrow() + .readw(vqs[0].borrow().used + offset_of!(VringUsed, idx) as u64); + for i in start..idx as u64 { + let len = test_state.borrow().readw( + vqs[0].borrow().used + + offset_of!(VringUsed, ring) as u64 + + i * USED_ELEM_SIZE + + offset_of!(VringUsedElem, len) as u64, + ); + if len == arp_request.len() as u16 { + let id = test_state.borrow().readw( + vqs[0].borrow().used + offset_of!(VringUsed, ring) as u64 + i * USED_ELEM_SIZE, + ); + + let addr = test_state + .borrow() + .readq(vqs[0].borrow().desc + id as u64 * VRING_DESC_SIZE); + let packets = test_state.borrow().memread(addr, len as u64); + let src_mac_pos = VIRTIO_NET_HDR_SIZE + ETHERNET_HDR_SIZE + ARP_HDR_SIZE; + let dst_mac_pos = src_mac_pos + 10; + if arp_request[src_mac_pos..src_mac_pos + MAC_ADDR_LEN] + == packets[dst_mac_pos..dst_mac_pos + MAC_ADDR_LEN] + { + if need_reply { + return; + } else { + assert!(false); + } + } + } + } + start = idx as u64; + } +} + +fn get_arp_request(id: u8) -> ArpRequestPacket { + ArpRequestPacket { + net_hdr: VirtioNetHdr::default(), + eth_hdr: EthernetHdr { + dst_mac: [0xff; MAC_ADDR_LEN], + src_mac: ARP_SOURCE_MAC, + e_type: [0x08, 0x06], + }, + arp_packet: ArpPacket { + h_type: [0x00, 0x01], + p_type: [0x08, 0x00], + h_len: 0x06, + p_len: 0x04, + op: [0x00, 0x01], + src_mac: ARP_SOURCE_MAC, + src_ip: [0x02, 0x01, 0x01, id + 1], + dst_mac: [0x00; MAC_ADDR_LEN], + dst_ip: [0x02, 0x01, 0x01, id], + }, + } +} + +fn get_arp_request_vlan(id: u8) -> ArpRequestPacketVlan { + ArpRequestPacketVlan { + net_hdr: VirtioNetHdr::default(), + eth_hdr: EthernetHdrVlan { + dst_mac: [0xff; MAC_ADDR_LEN], + src_mac: ARP_SOURCE_MAC, + tpid: [0x81, 0x00], + vlan_id: [0x08, 0x01], + e_type: [0x08, 0x06], + }, + arp_packet: ArpPacket { + h_type: [0x00, 0x01], + p_type: [0x08, 0x00], + h_len: 0x06, + p_len: 0x04, + op: [0x00, 0x01], + src_mac: ARP_SOURCE_MAC, + src_ip: [0x02, 0x01, 0x01, id + 1], + dst_mac: [0x00; MAC_ADDR_LEN], + dst_ip: [0x02, 0x01, 0x01, id], + }, + } +} + +fn send_request( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, + request: &[u8], +) { + let length = request.len() as u64; + let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + + let k_bytes = 1024; + let num_k = length / k_bytes; + let mut offset; + // write 1024 bytes once. + for i in 0..num_k { + offset = i * k_bytes; + test_state.borrow().memwrite( + addr + offset, + &request[offset as usize..(offset + k_bytes) as usize], + k_bytes, + ); + } + let res = length % k_bytes; + if res > 0 { + offset = num_k * k_bytes; + test_state + .borrow() + .memwrite(addr + offset, &request[offset as usize..], res); + } + let free_head = vqs[1] + .borrow_mut() + .add(test_state.clone(), addr, request.len() as u32, false); + net.borrow().virtqueue_notify(vqs[1].clone()); + net.borrow().poll_used_elem( + test_state.clone(), + vqs[1].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); +} + +fn send_arp_request( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, + arp_request: &[u8], + need_reply: bool, +) { + send_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request, + ); + check_arp_mac( + net.clone(), + test_state.clone(), + vqs.clone(), + &arp_request, + need_reply, + ); +} + +/// Send and receive packet test. +/// TestStep: +/// 1. Init device. +/// 2. Send ARP packet and check the reply. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_rx_tx_test() { + let id = 1 * TEST_MAC_ADDR_NUMS; + let (net, test_state, alloc) = set_up(id, false, 0, false); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + 3, + ); + + let arp_request = get_arp_request(id); + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request.as_bytes(), + true, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} + +/// Send and receive packet test with iothread. +/// TestStep: +/// 1. Init device. +/// 2. Send ARP packet and check the reply. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_rx_tx_test_iothread() { + let id = 2 * TEST_MAC_ADDR_NUMS; + let (net, test_state, alloc) = set_up_iothread(id, false, 0, false); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + 3, + ); + + let arp_request = get_arp_request(id); + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request.as_bytes(), + true, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} + +/// Test the control mq command. +/// TestStep: +/// 1. Init device: enable multi-queue and VIRTIO_NET_CTRL_MQ. +/// 2. Send VIRTIO_NET_CTRL_MQ to set vq pairs: +/// 1) set normal vq pairs; +/// 2) set invalid request length; +/// 3) set invalid request cmd; +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_ctrl_mq_test() { + let id = 3 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 4; + let queues: usize = 2 * queue_pairs as usize + 1; + let (net, test_state, alloc) = set_up(id, true, queue_pairs, false); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + queues, + ); + + // (test_type, queue_pairs, ack) + // test_type: + // 0 - normal request + // 1 - invalid request length + // 2 - invalid cmd + let reqs = [ + (0, queue_pairs, VIRTIO_NET_OK), + (0, u16::MAX, VIRTIO_NET_ERR), + (0, 0, VIRTIO_NET_ERR), + (1, queue_pairs, VIRTIO_NET_ERR), + (2, queue_pairs, VIRTIO_NET_ERR), + ]; + + for (test_type, vq_pairs, status) in reqs { + let ack: u8 = 0xff; + // The message: CtrlHdr, vq_pairs, ack. + let addr = alloc + .borrow_mut() + .alloc(size_of::() as u64 + 2 + 1) + .try_into() + .unwrap(); + + let mut cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET as u8; + if test_type == 2 { + cmd = u8::MAX; + } + let ctrl_hdr = CtrlHdr { + class: VIRTIO_NET_CTRL_MQ, + cmd, + }; + test_state + .borrow() + .memwrite(addr, &ctrl_hdr.as_bytes(), size_of::() as u64); + test_state + .borrow() + .writew(addr + size_of::() as u64, vq_pairs); + test_state + .borrow() + .writeb(addr + size_of::() as u64 + 2, ack); + + let ctrl_vq = &vqs[queues - 1]; + // CtrlHdr + vq_pairs. + let mut len = size_of::() as u32 + 2; + if test_type == 1 { + len -= 1; + } + + let data_entries: Vec = vec![ + TestVringDescEntry { + data: addr, + len, + write: false, + }, + TestVringDescEntry { + data: addr + size_of::() as u64 + 2, + len: 1, + write: true, + }, + ]; + let free_head = ctrl_vq + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + net.borrow() + .kick_virtqueue(test_state.clone(), ctrl_vq.clone()); + + net.borrow().poll_used_elem( + test_state.clone(), + ctrl_vq.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let ack = test_state + .borrow() + .readb(addr + size_of::() as u64 + 2); + assert_eq!(ack, status); + } + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + true, + ); +} + +/// Write or Read mac address from device config. +fn net_config_mac_rw( + net: Rc>, + mac: Option<&[u8; MAC_ADDR_LEN]>, +) -> [u8; MAC_ADDR_LEN] { + if let Some(mac) = mac { + for i in 0..MAC_ADDR_LEN { + net.borrow() + .config_writeb((offset_of!(VirtioNetConfig, mac) + i) as u64, mac[i]); + } + } + + let mut mac_read = [0_u8; MAC_ADDR_LEN]; + for i in 0..MAC_ADDR_LEN { + mac_read[i] = net + .borrow() + .config_readb((offset_of!(VirtioNetConfig, mac) + i) as u64); + } + mac_read +} + +/// Virtio net configure is not allowed to change except mac. +fn write_net_config_check(net: Rc>, offset: u64, value: u64, size: u8) { + let origin_value = net.borrow().config_readw(offset) as u64; + assert_ne!(origin_value, value); + match size { + 1 => net.borrow().config_writeb(offset, value as u8), + 2 => net.borrow().config_writew(offset, value as u16), + 4 => net.borrow().config_writel(offset, value as u32), + _ => (), + }; + let value = net.borrow().config_readw(offset) as u64; + assert_eq!(origin_value, value); +} + +/// Write value to virtio net configure, and check the write result. +/// TestStep: +/// 1. Init device. +/// 2. Write value to virtio net configure which can not be changed +/// except mac in some conditions. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_write_and_check_config() { + let id = 4 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + + let reqs = [ + DEFAULT_NET_FEATURES & !(1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_CTRL_MAC_ADDR), + DEFAULT_NET_FEATURES, + ]; + for features in reqs { + let (net, test_state, alloc) = set_up(id, false, queue_pairs, true); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + features, + queues, + ); + + // Get the mac address in the device configure space. + let mac_origin = net_config_mac_rw(net.clone(), None); + assert_eq!(mac_origin, CMD_LINE_MAC); + + // Write 0xff:0xff:0xff:0xff:0xff to virtio_net_config->mac. + let mac = net_config_mac_rw(net.clone(), Some(&[0xff; MAC_ADDR_LEN])); + if features & (1 << VIRTIO_F_VERSION_1) != 0 { + assert_eq!(mac, mac_origin); + } else { + assert_eq!(mac, [0xff; MAC_ADDR_LEN]); + } + + // Write abnornal value to virito_net_config. + write_net_config_check( + net.clone(), + offset_of!(VirtioNetConfig, status) as u64, + u16::MAX as u64, + 2, + ); + write_net_config_check( + net.clone(), + offset_of!(VirtioNetConfig, max_virtqueue_pairs) as u64, + u16::MAX as u64, + 2, + ); + write_net_config_check( + net.clone(), + offset_of!(VirtioNetConfig, mtu) as u64, + u16::MAX as u64, + 2, + ); + write_net_config_check( + net.clone(), + offset_of!(VirtioNetConfig, speed) as u64, + u32::MAX as u64, + 4, + ); + write_net_config_check( + net.clone(), + offset_of!(VirtioNetConfig, duplex) as u64, + u8::MAX as u64, + 1, + ); + + write_net_config_check( + net.clone(), + size_of:: as u64 + 1, + u8::MAX as u64, + 1, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); + } +} + +// Send request with control virtqueue. +fn send_ctrl_vq_request( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, + ctrl_data: &[u8], + ack: u8, +) { + let ctrl_vq = &vqs[2]; + let addr = alloc + .borrow_mut() + .alloc(ctrl_data.len() as u64) + .try_into() + .unwrap(); + test_state + .borrow() + .memwrite(addr, &ctrl_data, ctrl_data.len() as u64); + let data_entries: Vec = vec![ + TestVringDescEntry { + data: addr, + len: ctrl_data.len() as u32 - 1, + write: false, + }, + TestVringDescEntry { + data: addr + ctrl_data.len() as u64 - 1, + len: 1, + write: true, + }, + ]; + let free_head = ctrl_vq + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + net.borrow() + .kick_virtqueue(test_state.clone(), ctrl_vq.clone()); + + net.borrow().poll_used_elem( + test_state.clone(), + ctrl_vq.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let res_ack = test_state.borrow().readb(addr + ctrl_data.len() as u64 - 1); + assert_eq!(res_ack, ack); +} + +// Set uni_entries/mul_entries macs to unicast/multicast mac table. +fn ctrl_vq_set_mac_table( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, + uni_entries: u32, + mul_entries: u32, + ack: u8, +) { + let mut ctrl_mac_table = CtrlMacTableReq { + ctrl_hdr: CtrlHdr { + class: VIRTIO_NET_CTRL_MAC, + cmd: VIRTIO_NET_CTRL_MAC_TABLE_SET, + }, + uni_entries, + uni_macs: [MacAddress::default(); MAX_MAC_TABLE_LEN + 1], + mul_entries, + ..CtrlMacTableReq::default() + }; + + for i in 0..uni_entries + mul_entries { + let mut mac = MacAddress { + address: ARP_SOURCE_MAC, + }; + mac.address[MAC_ADDR_LEN - 1] += i as u8 + 1; + if i < uni_entries { + ctrl_mac_table.uni_macs[i as usize] = mac; + } else { + mac.address[0] += 1; + ctrl_mac_table.mul_macs[(i - uni_entries) as usize] = mac; + } + } + + let mut ctrl_data: Vec = Vec::new(); + let mut offset = offset_of!(CtrlMacTableReq, uni_macs) + uni_entries as usize * MAC_ADDR_LEN; + ctrl_data.append(&mut ctrl_mac_table.as_bytes()[..offset].to_vec()); + ctrl_data.append(&mut mul_entries.as_bytes().to_vec()); + offset = offset_of!(CtrlMacTableReq, mul_macs); + ctrl_data.append( + &mut ctrl_mac_table.as_bytes()[offset..offset + mul_entries as usize * MAC_ADDR_LEN] + .to_vec(), + ); + ctrl_data.append(&mut ctrl_mac_table.ack.as_bytes().to_vec()); + + assert_eq!( + 11 + (uni_entries + mul_entries) as usize * MAC_ADDR_LEN, + ctrl_data.len() + ); + + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_data, + ack, + ); +} + +fn ctrl_vq_set_mac_address( + net: Rc>, + test_state: Rc>, + alloc: Rc>, + vqs: Vec>>, +) { + // Get the mac address in the device configure space. + let mac_origin = net_config_mac_rw(net.clone(), None); + assert_eq!(mac_origin, CMD_LINE_MAC); + // Set mac address. + let ctrl_mac_addr = CtrlMacAddr { + ctrl_hdr: CtrlHdr { + class: VIRTIO_NET_CTRL_MAC, + cmd: VIRTIO_NET_CTRL_MAC_ADDR_SET, + }, + mac: ARP_SOURCE_MAC, + ack: 0xff, + }; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_mac_addr.as_bytes(), + VIRTIO_NET_OK, + ); + // Check mac address result. + let config_mac = net_config_mac_rw(net.clone(), None); + assert_eq!(config_mac, ARP_SOURCE_MAC); +} + +/// Test the control vlan command. +/// TestStep: +/// 1. Init device with control vq. +/// 2. Test the control vlan command: +/// 1) add vid 0/0/1/0xfff/0xffff, success(ignore invalid/repeated value) +/// 2) del vid 0/0/1/0xfff/0xffff, success(ignore invalid/repeated value) +/// 3) invalid ctrl class and cmd, expect reply error +/// 4) invalid ctrl cmd, expect reply error +/// 5) invalid vid length, expect reply error +/// 3. Send ARP packet and check the reply. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn virtio_net_ctrl_vlan_test() { + let id = 5 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + + let (net, test_state, alloc) = set_up(id, false, queue_pairs, false); + + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + queues, + ); + + // Turn off rx mode promisc. + let ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, 0); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_rx_info.as_bytes(), + VIRTIO_NET_OK, + ); + + let reqs = [ + (0, VIRTIO_NET_OK), + (0, VIRTIO_NET_OK), + (1, VIRTIO_NET_OK), + (0xfff, VIRTIO_NET_OK), + (0xffff, VIRTIO_NET_ERR), + ]; + // Test VIRTIO_NET_CTRL_VLAN_ADD. + for (vid, ack) in reqs { + let ctrl_vlan_info = CtrlVlanInfo::new(VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, vid); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_vlan_info.as_bytes(), + ack, + ); + } + // Test VIRTIO_NET_CTRL_VLAN_DEL. + for (vid, ack) in reqs { + let ctrl_vlan_info = CtrlVlanInfo::new(VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_DEL, vid); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_vlan_info.as_bytes(), + ack, + ); + } + // Test invalid class and cmd. + let ctrl_vlan_info = CtrlVlanInfo::new(u8::MAX, u8::MAX, 0); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_vlan_info.as_bytes(), + VIRTIO_NET_ERR, + ); + // Test invalid cmd. + let ctrl_vlan_info = CtrlVlanInfo::new(VIRTIO_NET_CTRL_VLAN, u8::MAX, 0); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_vlan_info.as_bytes(), + VIRTIO_NET_ERR, + ); + // Test invalid vid length. + let ctrl_vlan_info = CtrlVlanInfo::new(VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, 0); + let data_size = size_of::() - 1; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_vlan_info.as_bytes()[..data_size], + VIRTIO_NET_ERR, + ); + + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &get_arp_request(id).as_bytes(), + true, + ); + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &get_arp_request_vlan(id).as_bytes(), + false, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} + +/// Test the control mac command. +/// TestStep: +/// 1. Init device with control vq. +/// 2. Test the control mac command: +/// 1) set mac address +/// 2) set mac table with different unicast and multicast entries +/// 3) invalid test: +/// a) invalid unicast entries +/// b) invalid unicast mac table +/// c) invalid ctrl mac cmd +/// 3. Send ARP packet and check the reply. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn virtio_net_ctrl_mac_test() { + let id = 6 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + + let max_table_len = MAX_MAC_TABLE_LEN as u32; + // (type, unicast_macs, multicast_macs) + let mac_reqs = [ + (VIRTIO_NET_CTRL_MAC_ADDR_SET, 0, 0), + (VIRTIO_NET_CTRL_MAC_TABLE_SET, 2, 2), + (VIRTIO_NET_CTRL_MAC_TABLE_SET, 2, max_table_len - 2 + 1), + (VIRTIO_NET_CTRL_MAC_TABLE_SET, max_table_len - 2, 2), + (VIRTIO_NET_CTRL_MAC_TABLE_SET, max_table_len + 1, 0), + (VIRTIO_NET_CTRL_MAC_TABLE_SET, max_table_len + 1, 2), + (VIRTIO_NET_CTRL_MAC_TABLE_SET, 2, max_table_len), + (u8::MAX, 0, 0), + ]; + + for (mac_type, uni, mul) in mac_reqs { + let (net, test_state, alloc) = set_up(id, false, queue_pairs, true); + + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + queues, + ); + + let mut arp_request = get_arp_request(id); + // Test VIRTIO_NET_CTRL_MAC_ADDR_SET. + match mac_type { + VIRTIO_NET_CTRL_MAC_ADDR_SET => { + // Normal test. + ctrl_vq_set_mac_address( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + ); + // Abnormal test: mac_address(2 byte), ack(1byte) + let req_data_bytes = [ + VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_ADDR_SET, + 0, + 0, + 0xff, + ]; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &req_data_bytes, + VIRTIO_NET_ERR, + ); + } + VIRTIO_NET_CTRL_MAC_TABLE_SET => { + // Turn off rx mode promisc. + let ctrl_rx_info = + CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, 0); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_rx_info.as_bytes(), + VIRTIO_NET_OK, + ); + + // Test VIRTIO_NET_CTRL_MAC_TABLE_SET. + ctrl_vq_set_mac_table( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + uni, + mul, + VIRTIO_NET_OK, + ); + arp_request.arp_packet.src_mac[MAC_ADDR_LEN - 1] += 2; + } + _ => { + // Invalid unicast entries test. + let req_data_bytes = [VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET, 0, 0xff]; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &req_data_bytes, + VIRTIO_NET_ERR, + ); + // Invalid unicast mac table test. + let req_data_bytes = [ + VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_TABLE_SET, + 0, + 0, + 0, + 1, + 0, + 0xff, + ]; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &req_data_bytes, + VIRTIO_NET_ERR, + ); + // Invalid cmd test. + let req_data_bytes = [VIRTIO_NET_CTRL_MAC, u8::MAX, 0, 0, 0, 0, 0, 0, 0, 0, 0xff]; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &req_data_bytes, + VIRTIO_NET_ERR, + ); + } + } + + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request.as_bytes(), + true, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); + } +} + +/// Test the control rx command. +/// TestStep: +/// 1. Init device with control vq. +/// 2. Test the control rx command: +/// 1) PROMISC/NOUNI/ALLUNI/NOBCAST/NOMULTI/ALLMULTI +/// 2) invalid class/cmd/switch +/// 3. Send ARP packet and check the reply. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn virtio_net_ctrl_rx_test() { + let id = 7 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + + // (req_type, cmd, value, need_reply, with_mac, ack) + let reqs = [ + (1, VIRTIO_NET_CTRL_RX_PROMISC, 0, true, false, VIRTIO_NET_OK), + (1, VIRTIO_NET_CTRL_RX_NOUNI, 1, false, false, VIRTIO_NET_OK), + (1, VIRTIO_NET_CTRL_RX_ALLUNI, 1, true, false, VIRTIO_NET_OK), + (1, u8::MAX, 0, true, false, VIRTIO_NET_ERR), + (1, VIRTIO_NET_CTRL_RX_NOBCAST, 1, false, true, VIRTIO_NET_OK), + ( + 2, + VIRTIO_NET_CTRL_RX_NOMULTI, + 1, + false, + false, + VIRTIO_NET_OK, + ), + ( + 2, + VIRTIO_NET_CTRL_RX_ALLMULTI, + 1, + true, + false, + VIRTIO_NET_OK, + ), + ( + 2, + u8::MAX, + MAX_MAC_TABLE_LEN as u8, + true, + false, + VIRTIO_NET_ERR, + ), + (2, u8::MAX, 2, true, false, VIRTIO_NET_ERR), + (3, 0, 0, true, false, VIRTIO_NET_ERR), + (u8::MAX, 0, 0, true, false, VIRTIO_NET_ERR), + ]; + + for (req_type, cmd, value, need_reply, with_mac, ack) in reqs { + let (net, test_state, alloc) = set_up(id, false, queue_pairs, with_mac); + + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + queues, + ); + + let mut arp_request = get_arp_request(id); + // Turn off rx mode promisc. + let ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, 0); + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_rx_info.as_bytes(), + VIRTIO_NET_OK, + ); + let mut ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, 0, 0); + match req_type { + 1 => { + ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, cmd, value); + } + 2 => { + ctrl_vq_set_mac_table( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + 0, + value as u32, + VIRTIO_NET_OK, + ); + arp_request.arp_packet.src_mac[0] += 1; + arp_request.arp_packet.src_mac[MAC_ADDR_LEN - 1] += 1; + ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, cmd, value); + } + 3 => { + // Test invalid class. + ctrl_rx_info = CtrlRxInfo::new(u8::MAX, 0, 0); + } + _ => { + // Test no switch data. + let ctrl_rx_data = [VIRTIO_NET_CTRL_RX, 0, 0xff]; + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_rx_data, + VIRTIO_NET_ERR, + ); + } + } + + if req_type != u8::MAX { + send_ctrl_vq_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &ctrl_rx_info.as_bytes(), + ack, + ); + } + + if cmd == VIRTIO_NET_CTRL_RX_NOBCAST { + // Test receive filter: broadcast. + arp_request.arp_packet.src_mac = [0xff; 6]; + } + + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request.as_bytes(), + need_reply, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); + } +} + +/// Test the control abnormal command. +/// TestStep: +/// 1. Init device with control vq. +/// 2. Test the control rx command without ack, expect NEEDS_RESET. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_ctrl_abnormal_test() { + let id = 8 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + let (net, test_state, alloc) = set_up(id, false, queue_pairs, false); + + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + queues, + ); + + let ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, 0); + let ctrl_data = &ctrl_rx_info.as_bytes(); + + let ctrl_vq = &vqs[2]; + let addr = alloc + .borrow_mut() + .alloc(ctrl_data.len() as u64) + .try_into() + .unwrap(); + test_state + .borrow() + .memwrite(addr, &ctrl_data, ctrl_data.len() as u64); + + let data_entries: Vec = vec![ + TestVringDescEntry { + data: addr, + len: 1, + write: false, + }, + TestVringDescEntry { + data: addr + 1, + len: 1, + write: false, + }, + TestVringDescEntry { + data: addr + 2, + len: ctrl_data.len() as u32 - 3, + write: false, + }, + ]; + ctrl_vq + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + net.borrow() + .kick_virtqueue(test_state.clone(), ctrl_vq.clone()); + assert!(net.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} + +/// Test the abnormal rx/tx request. +/// TestStep: +/// 1. Init device. +/// 2. Test the rx/tx request: +/// 1) rx queue is full, and recover it +/// 2) cause the tx packet limitation once +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_abnormal_rx_tx_test() { + let id = 9 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + + let (net, test_state, alloc) = set_up(id, false, queue_pairs, false); + + net.borrow_mut().reset(); + net.borrow_mut().set_acknowledge(); + net.borrow_mut().set_driver(); + net.borrow_mut().negotiate_features(DEFAULT_NET_FEATURES); + net.borrow_mut().set_features_ok(); + net.borrow_mut().pci_dev.enable_msix(); + net.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + let vqs = net + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), queues); + fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); + + // Test rx queue is full. + // Set 0 to rx->avail->idx. + test_state.borrow().writew(vqs[0].borrow().avail + 2, 0); + net.borrow().set_driver_ok(); + + // Test send 256 packet to execeed the handle_tx limitation once. + let request = get_arp_request(id); + let length = request.as_bytes().len() as u64; + let size = net.borrow().get_queue_size(); + assert_eq!(size, QUEUE_SIZE_NET); + for _ in 0..size { + let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + test_state + .borrow() + .memwrite(addr, &request.as_bytes(), length); + vqs[1] + .borrow_mut() + .add(test_state.clone(), addr, length as u32, false); + } + net.borrow().virtqueue_notify(vqs[1].clone()); + + // Recover the rx->avail->idx for receiving packets. + test_state + .borrow() + .writew(vqs[0].borrow().avail + 2, vqs[0].borrow().size as u16); + net.borrow().virtqueue_notify(vqs[0].clone()); + + // Check rx vq is ok. + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US / 5); + loop { + if net.borrow().queue_was_notified(vqs[0].clone()) + && vqs[0].borrow_mut().get_buf(test_state.clone()) + { + break; + } + assert!(time::Instant::now() - start_time < timeout_us); + } + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} + +/// Test the abnormal rx/tx request 2. +/// TestStep: +/// 1. Init device. +/// 2. Test the rx/tx request: +/// 1) handle rx error +/// 2) handle tx error +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_abnormal_rx_tx_test_2() { + let id = 19 * TEST_MAC_ADDR_NUMS; + let queue_pairs: u16 = 1; + let queues: usize = 2 * queue_pairs as usize + 1; + + for i in 0..2 { + let (net, test_state, alloc) = set_up(id, false, queue_pairs, false); + + net.borrow_mut().reset(); + net.borrow_mut().set_acknowledge(); + net.borrow_mut().set_driver(); + net.borrow_mut().negotiate_features(DEFAULT_NET_FEATURES); + net.borrow_mut().set_features_ok(); + net.borrow_mut().pci_dev.enable_msix(); + net.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + let vqs = net + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), queues); + fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); + + // Test receive packet failed. + // Set u16::MAX to rx->avail->ring[0]. + if i == 0 { + test_state + .borrow() + .writew(vqs[0].borrow().avail + 4, u16::MAX); + } + // Set driver ok. + let status = net.borrow().get_status() | VIRTIO_CONFIG_S_DRIVER_OK; + net.borrow().set_status(status); + + let request = get_arp_request(id); + let length = request.as_bytes().len() as u64; + let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + test_state + .borrow() + .memwrite(addr, &request.as_bytes(), length); + vqs[1] + .borrow_mut() + .add(test_state.clone(), addr, length as u32, false); + if i == 1 { + test_state + .borrow() + .writew(vqs[1].borrow().avail + 4, u16::MAX); + } + net.borrow().virtqueue_notify(vqs[1].clone()); + sleep(time::Duration::from_millis(500)); + assert!(net.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); + } +} + +/// Test set abnormal feature. +/// TestStep: +/// 1. Init device, set abnormal feature 40 which will be ignored. +/// 2. Send ARP packet and check the reply. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_set_abnormal_feature() { + let id = 10 * TEST_MAC_ADDR_NUMS; + let (net, test_state, alloc) = set_up(id, false, 0, false); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES | 1 << 40, + 3, + ); + assert_eq!(net.borrow().get_guest_features(), DEFAULT_NET_FEATURES); + + let arp_request = get_arp_request(id); + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request.as_bytes(), + true, + ); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} + +/// Send abnormal packet. +/// TestStep: +/// 1. Init device. +/// 2. Send abnormal packet: +/// 1) invalid virtio_net_hdr +/// 2) random a packet +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2. success or failure. +/// 1/3/4: success. +#[test] +fn virtio_net_send_abnormal_packet() { + let id = 11 * TEST_MAC_ADDR_NUMS; + let (net, test_state, alloc) = set_up(id, false, 0, false); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + 3, + ); + + let mut arp_request = get_arp_request(id); + arp_request.net_hdr.flags = u8::MAX; + send_arp_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &arp_request.as_bytes(), + false, + ); + + let data_bytes = arp_request.as_mut_bytes(); + let mut rng = rand::thread_rng(); + let test_packets = 8; + for _ in 0..test_packets { + for _ in 0..data_bytes.len() / 3 { + let idx = rng.gen_range(0..data_bytes.len()); + data_bytes[idx] = rng.gen_range(0..0xff); + } + + send_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &data_bytes, + ); + } + + for _ in 0..test_packets { + let mut data_bytes = [0; MAX_PACKET_LEN as usize + 8]; + for j in 0..MAX_PACKET_LEN as usize + 8 { + data_bytes[j] = rng.gen_range(0..0xff); + } + send_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + &data_bytes, + ); + } + + let ret = test_state + .borrow() + .qmp("{\"execute\": \"qmp_capabilities\"}"); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} -- Gitee From 9317ebea624eb35716471f4a4ad6e55197b2d9b8 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 27 Feb 2023 15:34:42 +0800 Subject: [PATCH 0829/2187] MST: rm assert() in io_map() io_map() checkes whether there's enough mmio memory space or not, but it uses assert(), which would cause test process abort. Now we make it just return an invalid address when something is wrong, so that the test cases can catch such abnormal situation and make asserts there to avoid process aborts. --- tests/mod_test/src/libdriver/pci.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 599aa9073..05effae41 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -44,7 +44,9 @@ pub const PCI_MSIX_ENTRY_UPPER_ADDR: u64 = 0x4; pub const PCI_MSIX_ENTRY_DATA: u64 = 0x8; pub const PCI_MSIX_ENTRY_VECTOR_CTRL: u64 = 0xc; pub const PCI_MSIX_ENTRY_CTRL_MASKBIT: u32 = 0x00000001; + pub type PCIBarAddr = u64; +pub const INVALID_BAR_ADDR: u64 = u64::MAX; pub trait PciMsixOps { fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32); @@ -244,11 +246,15 @@ impl TestPciDev { addr = self.config_readl(bar_offset) & !(0x0F_u32); assert!(addr != 0); + let mut pci_bus = self.pci_bus.borrow_mut(); size = 1 << addr.trailing_zeros(); - location = (self.pci_bus.borrow().mmio_alloc_ptr + size - 1) / size * size; - assert!(location >= self.pci_bus.borrow().mmio_alloc_ptr); - assert!(location + size <= self.pci_bus.borrow().mmio_limit); - self.pci_bus.borrow_mut().mmio_alloc_ptr = location + size; + location = (pci_bus.mmio_alloc_ptr + size - 1) / size * size; + if location < pci_bus.mmio_alloc_ptr || location + size > pci_bus.mmio_limit { + return INVALID_BAR_ADDR; + } + + pci_bus.mmio_alloc_ptr = location + size; + drop(pci_bus); self.config_writel(bar_offset, location as u32); bar_addr = location; bar_addr -- Gitee From 291a50001210caf1cd52054428241485d7079460 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Tue, 28 Feb 2023 01:55:23 +0800 Subject: [PATCH 0830/2187] memory: clear the file fd resource in memory MST tests 1.fd is passed as a qmp parameter 2.fd was deleted after the test Signed-off-by: Li HuaChao --- machine/src/standard_vm/mod.rs | 20 ++------ machine_manager/src/qmp/qmp_schema.rs | 2 + tests/mod_test/tests/memory_test.rs | 69 ++++++++++++++++++++------- 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 90bdf05d0..6e7fcbeba 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -35,7 +35,6 @@ use vnc::{ #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; -use std::fs::File; use std::mem::size_of; use std::ops::Deref; use std::os::unix::io::RawFd; @@ -1482,22 +1481,13 @@ impl DeviceInterface for StdMachine { let mut fd = None; if args.region_type.eq("rom_device_region") || args.region_type.eq("ram_device_region") { - let file_name = if args.region_type.eq("rom_device_region") { - "rom_dev_file.fd" - } else { - "ram_dev_file.fd" - }; - - let file = File::create(file_name).unwrap(); - file.set_len(args.size).unwrap(); - drop(file); - fd = Some( - std::fs::OpenOptions::new() + if let Some(file_name) = args.device_fd_path { + fd = Some(std::fs::OpenOptions::new() .read(true) .write(true) - .open(file_name) - .unwrap(), - ); + .open(&file_name) + .unwrap()); + } } let region; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 6573f164a..57e2cfa91 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -623,6 +623,8 @@ pub struct update_region { pub ioeventfd_data: Option, #[serde(rename = "ioeventfd_size")] pub ioeventfd_size: Option, + #[serde(rename = "device_fd_path")] + pub device_fd_path: Option, } pub type UpdateRegionArgument = update_region; diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 024800443..4cca54664 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -15,7 +15,7 @@ use mod_test::{ libtest::{test_init, TestState}, }; use serde_json::{json, Value::String as JsonString}; -use std::{cell::RefCell, process::Command, rc::Rc, string::String}; +use std::{cell::RefCell, fs::File, process::Command, rc::Rc, string::String}; pub struct MemoryTest { pub state: Rc>, @@ -25,6 +25,12 @@ pub struct MemoryTest { const MEM_SIZE: u64 = 2048; // 2GB const PAGE_SIZE: u64 = 4096; const ADDRESS_BASE: u64 = 0x4000_0000; +const ROM_DEV_PATH: &str = "rom_dev_file.fd"; +const RAM_DEV_PATH: &str = "ram_dev_file.fd"; + +fn remove_file(path: String) { + let _output = Command::new("rm").arg("-f").arg(path).output(); +} impl MemoryTest { pub fn new( @@ -259,10 +265,20 @@ fn rom_device_region_readwrite() { // Add a dummy rom device by qmp. The function of the device is to multiply the written value by 2 // through the write interface and save it, and read the saved value through the read interface. - memory_test - .state - .borrow_mut() - .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"rom_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99, \"read_only_mode\": false }}"); + let file = File::create(&ROM_DEV_PATH).unwrap(); + file.set_len(PAGE_SIZE).unwrap(); + let qmp_str = format!( + "{{ \"execute\": \"update_region\", + \"arguments\": {{ \"update_type\": \"add\", + \"region_type\": \"rom_device_region\", + \"offset\": 1099511627776, + \"size\": 4096, + \"priority\": 99, + \"read_only_mode\": false, + \"device_fd_path\": {:?} }} }}", + ROM_DEV_PATH + ); + memory_test.state.borrow_mut().qmp(&qmp_str); let data = [0x01u8; 8]; memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test @@ -270,6 +286,7 @@ fn rom_device_region_readwrite() { .borrow_mut() .memread(addr, std::mem::size_of::() as u64); assert_eq!(ret, [0x02u8; 8]); + remove_file(ROM_DEV_PATH.to_string()); // Write overflow memory_test @@ -287,10 +304,20 @@ fn rom_device_region_readwrite() { // device. The device can set the write mode to writable according to the device status during // the write operation, or directly return an error indicating that the write is not allowed. // The read operation is the same as that of IO region. - memory_test - .state - .borrow_mut() - .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"rom_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 100, \"read_only_mode\": true }}"); + let file = File::create(&ROM_DEV_PATH).unwrap(); + file.set_len(PAGE_SIZE).unwrap(); + let qmp_str = format!( + "{{ \"execute\": \"update_region\", + \"arguments\": {{ \"update_type\": \"add\", + \"region_type\": \"rom_device_region\", + \"offset\": 1099511627776, + \"size\": 4096, + \"priority\": 99, + \"read_only_mode\": true, + \"device_fd_path\": {:?} }} }}", + ROM_DEV_PATH + ); + memory_test.state.borrow_mut().qmp(&qmp_str); let data = [0x01u8; 8]; memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test @@ -298,6 +325,7 @@ fn rom_device_region_readwrite() { .borrow_mut() .memread(addr, std::mem::size_of::() as u64); assert_eq!(ret, [0x00u8; 8]); + remove_file(ROM_DEV_PATH.to_string()); memory_test.state.borrow_mut().stop(); } @@ -319,10 +347,19 @@ fn ram_device_region_readwrite() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); let addr = 0x100_0000_0000; // 1TB - memory_test - .state - .borrow_mut() - .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + let file = File::create(&RAM_DEV_PATH).unwrap(); + file.set_len(PAGE_SIZE).unwrap(); + let qmp_str = format!( + "{{ \"execute\": \"update_region\", + \"arguments\": {{ \"update_type\": \"add\", + \"region_type\": \"ram_device_region\", + \"offset\": 1099511627776, + \"size\": 4096, + \"priority\": 99, + \"device_fd_path\": {:?} }} }}", + RAM_DEV_PATH + ); + memory_test.state.borrow_mut().qmp(&qmp_str); let data = [0x01u8; 8]; memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test @@ -348,11 +385,7 @@ fn ram_device_region_readwrite() { .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"delete\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); - let ret = memory_test - .state - .borrow_mut() - .memread(addr, std::mem::size_of::() as u64); - assert_eq!(ret, [0x0u8; 8]); + remove_file(RAM_DEV_PATH.to_string()); memory_test.state.borrow_mut().stop(); } -- Gitee From e0c502b90952c6037c852728518c7cb5e4911207 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 19:29:33 +0800 Subject: [PATCH 0831/2187] tests: rename virtio_net.rs to net_test.rs To keep the same naming notations, rename virtio_net.rs to net_test.rs Signed-off-by: Yan Wang --- tests/mod_test/tests/{virtio_net.rs => net_test.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/mod_test/tests/{virtio_net.rs => net_test.rs} (100%) diff --git a/tests/mod_test/tests/virtio_net.rs b/tests/mod_test/tests/net_test.rs similarity index 100% rename from tests/mod_test/tests/virtio_net.rs rename to tests/mod_test/tests/net_test.rs -- Gitee From cbb178a872500514bed6b3cb9dd3a1c24d9adba3 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 18:04:03 +0800 Subject: [PATCH 0832/2187] tests: delete unused file virtio_net.rs in libdriver The content in mod_test/src/libdriver/virtio_net.rs has been contained in mod_test/src/tests/virtio_net.rs Signed-off-by: Yan Wang --- tests/mod_test/src/libdriver/mod.rs | 1 - tests/mod_test/src/libdriver/virtio_net.rs | 86 ---------------------- 2 files changed, 87 deletions(-) delete mode 100644 tests/mod_test/src/libdriver/virtio_net.rs diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index de70e5867..2810c6fd9 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -18,6 +18,5 @@ pub mod pci_bus; pub mod virtio; pub mod virtio_block; pub mod virtio_console; -pub mod virtio_net; pub mod virtio_pci_modern; pub mod virtio_rng; diff --git a/tests/mod_test/src/libdriver/virtio_net.rs b/tests/mod_test/src/libdriver/virtio_net.rs deleted file mode 100644 index 6d72f5061..000000000 --- a/tests/mod_test/src/libdriver/virtio_net.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use super::machine::TestStdMachine; -use super::malloc::GuestAllocator; -use super::virtio::{ - TestVirtQueue, VirtioDeviceOps, VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, - VIRTIO_RING_F_INDIRECT_DESC, -}; -use super::virtio_pci_modern::TestVirtioPciDev; -use crate::libtest::{test_init, TestState}; -use std::cell::RefCell; -use std::rc::Rc; - -const VIRTIO_NET_F_MQ: u64 = 22; - -#[allow(unused)] -pub fn virtio_net_setup( - net: Rc>, - test_state: Rc>, - alloc: Rc>, -) -> Vec>> { - let mut queue_num: u16; - let mut features = net.borrow().get_device_features(); - features &= - !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX); - net.borrow_mut().negotiate_features(features); - net.borrow_mut().set_features_ok(); - - if features & (1 << VIRTIO_NET_F_MQ) != 0 { - queue_num = net.borrow().config_readw(8) * 2; - } else { - queue_num = 2; - } - queue_num += 1; - - let queues = net.borrow_mut().init_virtqueue(test_state, alloc, 1); - net.borrow().set_driver_ok(); - queues -} - -#[allow(unused)] -pub fn create_net() -> ( - Rc>, - Rc>, - Rc>, - Vec>>, -) { - let pci_slot: u8 = 0x4; - let pci_fn: u8 = 0x0; - let mut extra_args: Vec<&str> = Vec::new(); - - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); - extra_args.append(&mut args); - - let blk_pci_args = format!( - "-device {},id=net0,netdev=netdev0,bus=pcie.0,addr={}.0,mq=on", - "virtio-net-pci", pci_slot - ); - args = blk_pci_args[..].split(' ').collect(); - extra_args.append(&mut args); - let blk_args = String::from("-netdev tap,id=netdev0,ifname=tap_mq,queues=4"); - args = blk_args.split(' ').collect(); - extra_args.append(&mut args); - println!("start args is {:?}", extra_args); - let test_state = Rc::new(RefCell::new(test_init(extra_args))); - let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); - - let virtio_net = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); - - virtio_net.borrow_mut().init(pci_slot, pci_fn); - let virtio_net_queues = - virtio_net_setup(virtio_net.clone(), test_state.clone(), allocator.clone()); - - (virtio_net, test_state, allocator, virtio_net_queues) -} -- Gitee From d71dc7991fc04058c8694d1f6e4dca886e09935d Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 27 Feb 2023 14:49:03 +0800 Subject: [PATCH 0833/2187] tests: add virtio test case Add mst test case for virtio. Signed-off-by: Yan Wang --- tests/mod_test/tests/virtio_test.rs | 2326 +++++++++++++++++++++++++++ 1 file changed, 2326 insertions(+) create mode 100644 tests/mod_test/tests/virtio_test.rs diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs new file mode 100644 index 000000000..13fcef9db --- /dev/null +++ b/tests/mod_test/tests/virtio_test.rs @@ -0,0 +1,2326 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use rand::Rng; +use serde_json::json; +use std::cell::RefCell; +use std::mem::size_of; +use std::rc::Rc; +use util::offset_of; + +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{ + get_vring_size, TestVirtQueue, TestVringIndirectDesc, VirtioDeviceOps, VringDesc, + VIRTIO_CONFIG_S_NEEDS_RESET, VIRTIO_F_VERSION_1, VIRTIO_PCI_VRING_ALIGN, + VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, VRING_AVAIL_F_NO_INTERRUPT, + VRING_DESC_F_INDIRECT, VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, VRING_DESC_SIZE, +}; +use mod_test::libdriver::virtio_block::{ + add_blk_request, set_up, tear_down, virtio_blk_read, virtio_blk_request, virtio_blk_write, + TestVirtBlkReq, DEFAULT_IO_REQS, REQ_ADDR_LEN, REQ_DATA_LEN, REQ_STATUS_OFFSET, + TEST_IMAGE_SIZE, TIMEOUT_US, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, +}; +use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; +use mod_test::libtest::TestState; + +fn add_request( + test_state: Rc>, + alloc: Rc>, + vq: Rc>, + req_type: u32, + sector: u64, +) -> (u32, u64) { + add_blk_request(test_state, alloc, vq, req_type, sector, false) +} + +fn virtio_read( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + vq: Rc>, + sector: u64, +) { + virtio_blk_read(blk, test_state, alloc, vq, sector, false); +} + +fn virtio_write( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + vq: Rc>, + sector: u64, +) { + virtio_blk_write(blk, test_state, alloc, vq, sector, false); +} + +fn virtio_request( + test_state: Rc>, + alloc: Rc>, + req: TestVirtBlkReq, +) -> u64 { + virtio_blk_request(test_state, alloc, req, false) +} + +fn send_one_request( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + vq: Rc>, +) { + let (free_head, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vq.clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + blk.borrow().virtqueue_notify(vq.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vq.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(status, VIRTIO_BLK_S_OK); +} + +fn check_stratovirt_status(test_state: Rc>) { + let ret = test_state + .borrow() + .qmp("{\"execute\": \"qmp_capabilities\"}"); + assert_eq!(*ret.get("return").unwrap(), json!({})); +} + +fn init_device_step( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + tests: Vec<[usize; 8]>, +) { + for elem in tests { + let mut vqs: Vec>> = Vec::new(); + for j in elem.iter() { + match j { + 1 => blk.borrow_mut().reset(), + 2 => blk.borrow().set_acknowledge(), + 3 => blk.borrow().set_driver(), + 4 => blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1), + 5 => blk.borrow_mut().set_features_ok(), + 7 => { + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + vqs = blk + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), 1); + () + } + 8 => { + blk.borrow().set_driver_ok(); + } + 9 => blk.borrow().set_status(128), + _ => continue, + } + } + + // Try to send write and read request to StratoVirt, ignore + // the interrupt from device. + if vqs.len() > 0 { + let (_, _) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + let (_, _) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_IN, + 0, + ); + blk.borrow().virtqueue_notify(vqs[0].clone()); + } + + check_stratovirt_status(test_state.clone()); + } +} + +fn check_req_result( + blk: Rc>, + test_state: Rc>, + vq: Rc>, + addr: u64, + timeout_us: u64, +) { + let status = blk + .borrow() + .req_result(test_state.clone(), addr, timeout_us); + assert!(!blk.borrow().queue_was_notified(vq)); + assert_eq!(status, VIRTIO_BLK_S_OK); +} + +fn do_event_idx_with_flag(flag: u16) { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_RING_F_EVENT_IDX, + 1, + ); + + let (free_head, mut req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + vqs[0].borrow().set_used_event(test_state.clone(), 1); + blk.borrow().virtqueue_notify(vqs[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(status, VIRTIO_BLK_S_OK); + + // DEFAULT_IO_REQS write requests: + // Write "TEST" to sector 0 to DEFAULT_IO_REQS. + //let mut req_addr = 0_u64; + for i in 1..DEFAULT_IO_REQS { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set avail->used_event to DEFAULT_IO_REQS which the rigth value is DEFAULT_IO_REQS - 1, + // it will not get the interrupt which means event index feature works. + vqs[0] + .borrow() + .set_used_event(test_state.clone(), DEFAULT_IO_REQS as u16); + blk.borrow().virtqueue_notify(vqs[0].clone()); + check_req_result( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + req_addr + REQ_STATUS_OFFSET, + TIMEOUT_US, + ); + + // Create two write requests, the avail->used_event will be the update to the right value. + // It will get the interrupt from device. + let mut free_head = 0_u32; + for i in DEFAULT_IO_REQS..DEFAULT_IO_REQS * 2 { + (free_head, _) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set flag to avail->flag. + test_state.borrow().writew(vqs[0].borrow().avail, flag); + blk.borrow().virtqueue_notify(vqs[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + // Read the content in sector DEFAULT_IO_REQS * 2 - 1. + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + DEFAULT_IO_REQS * 2 - 1, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Feature Test. +/// Driver don't enable feature, and failed to do the I/O request. +/// TestStep: +/// 1. Init device: no virtio feature negotiation. +/// 2. Do the I/O request. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 1/3/4: success. +/// 2: device can't handle the io request. +#[test] +fn virtio_feature_none() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), 0, 1); + + let mut req_addr = 0_u64; + for i in 0..DEFAULT_IO_REQS { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + blk.borrow().virtqueue_notify(vqs[0].clone()); + + let status = test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(status, 0xff); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Feature Test. +/// Driver just enable VIRTIO_F_VERSION_1 feature, and succeed to do the I/O request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request: +/// 1) avail->flags with VRING_AVAIL_F_NO_INTERRUPT; +/// 2) avail->flags with not VRING_AVAIL_F_NO_INTERRUPT; +/// 3. For different avail->flags: +/// 1) check the request status, it has been handled. +/// 2) it will get the interrupt from device. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn virtio_feature_vertion_1() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + // 1) avail->flags with VRING_AVAIL_F_NO_INTERRUPT(1). + vqs[0] + .borrow() + .set_avail_flags(test_state.clone(), VRING_AVAIL_F_NO_INTERRUPT); + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + for i in 0..DEFAULT_IO_REQS { + (free_head, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + assert!(!blk.borrow().queue_was_notified(vqs[0].clone())); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + // need be changed. + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + assert!(!blk.borrow().queue_was_notified(vqs[0].clone())); + let status = test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(status, VIRTIO_BLK_S_OK); + + // 2) avail->flags with no VRING_AVAIL_F_NO_INTERRUPT. + vqs[0].borrow().set_avail_flags(test_state.clone(), 0); + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + DEFAULT_IO_REQS, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + DEFAULT_IO_REQS, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Driver just enable VIRTIO_F_VERSION_1|VIRTIO_RING_F_INDIRECT_DESC feature, +/// and succeed to do the I/O request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request(indirect and indirect + normal). +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_feature_indirect() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_RING_F_INDIRECT_DESC, + 1, + ); + + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + for i in 0..DEFAULT_IO_REQS { + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, i, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + free_head = vqs[0] + .borrow_mut() + .add(test_state.clone(), req_addr, 8, false); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); + test_state.borrow().writew( + vqs[0].borrow().desc + offset as u64 + 2, + free_head as u16 + 1, + ); + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), 2); + indirect_req.add_desc(test_state.clone(), req_addr + 8, 520, false); + indirect_req.add_desc(test_state.clone(), req_addr + REQ_STATUS_OFFSET, 1, true); + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + } + blk.borrow().virtqueue_notify(vqs[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + let status = test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(status, VIRTIO_BLK_S_OK); + + let blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_IN, 1, 0, REQ_DATA_LEN as usize); + let req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + free_head = vqs[0] + .borrow_mut() + .add(test_state.clone(), req_addr, 8, false); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); + test_state.borrow().writew( + vqs[0].borrow().desc + offset as u64 + 2, + free_head as u16 + 1, + ); + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), 2); + indirect_req.add_desc(test_state.clone(), req_addr + 8, 8, false); + indirect_req.add_desc( + test_state.clone(), + req_addr + REQ_ADDR_LEN as u64, + 513, + true, + ); + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + blk.borrow() + .kick_virtqueue(test_state.clone(), vqs[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(status, VIRTIO_BLK_S_OK); + + assert_eq!( + String::from_utf8( + test_state + .borrow() + .memread(req_addr + REQ_ADDR_LEN as u64, 4) + ) + .unwrap(), + "TEST" + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Driver just enable VIRTIO_F_VERSION_1|VIRTIO_RING_F_EVENT_IDX feature, +/// and succeed to do the I/O request. +/// TestStep: +/// 1. Init device with VIRTIO_F_VERSION_1|VIRTIO_RING_F_EVENT_IDX feature. +/// 2. Do the I/O request: +/// 1) create 5 request, and modify avail->used_event to 5. +/// 2) If the event idx works, we will not get the interrupt from device. +/// 3) create 5 request, and use the right avail->used_event. +/// 4) we will get the interrupt from device. +/// 5) read the sector 10 to check the write content, which is same as write. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_feature_event_idx() { + do_event_idx_with_flag(0); +} + +/// Driver just enable these featues: +/// VIRTIO_F_VERSION_1 | VIRTIO_RING_F_INDIRECT_DESC | VIRTIO_RING_F_EVENT_IDX +/// and succeed to do the I/O request(normal + indirect) which has opened the event idx. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request(indirect and indirect + normal). +/// 1) create 5 request(with indirect), and modify avail->used_event to 5. +/// 2) If the event idx works, we will not get the interrupt from device. +/// 3) create 5 request, and use the right avail->used_event. +/// 4) we will get the interrupt from device. +/// 5) read the sector 10 to check the write content, which is same as write. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_feature_indirect_and_event_idx() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX, + 1, + ); + + send_one_request( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + ); + + // Test write. + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + let req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + let free_head = vqs[0] + .borrow_mut() + .add(test_state.clone(), req_addr, REQ_ADDR_LEN, false); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); + test_state.borrow().writew( + vqs[0].borrow().desc + offset as u64 + 2, + free_head as u16 + 1, + ); + // 2 desc elems in indirect desc table. + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), 2); + indirect_req.add_desc( + test_state.clone(), + req_addr + REQ_ADDR_LEN as u64, + REQ_DATA_LEN, + false, + ); + indirect_req.add_desc(test_state.clone(), req_addr + REQ_STATUS_OFFSET, 1, true); + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + + let mut req_addr = 0_u64; + for i in 2..DEFAULT_IO_REQS { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set avail->used_event to DEFAULT_IO_REQS which the rigth value is DEFAULT_IO_REQS - 1, + // it will not get the interrupt which means event index feature works. + vqs[0] + .borrow() + .set_used_event(test_state.clone(), DEFAULT_IO_REQS as u16); + blk.borrow().virtqueue_notify(vqs[0].clone()); + check_req_result( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + req_addr + REQ_STATUS_OFFSET, + TIMEOUT_US, + ); + assert_eq!( + vqs[0].borrow().get_avail_event(test_state.clone()), + DEFAULT_IO_REQS as u16 + ); + + // Create two write requests, the avail->used_event will be the update to the right value. + // It will get the interrupt from device. + let mut free_head = 0_u32; + for i in DEFAULT_IO_REQS..DEFAULT_IO_REQS * 2 { + (free_head, _) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + blk.borrow().virtqueue_notify(vqs[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + // Read the content in sector DEFAULT_IO_REQS * 2 - 1. + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + DEFAULT_IO_REQS * 2 - 1, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Setting abnormal status in device initialization. +/// TestStep: +/// 1. Init device. +/// 1) set device status: special status and random status. +/// 2) ACKNOWLEDGE -> DRIVER -> DRIVER -> negotiate_features -> FEATURES_OK +/// -> setup_virtqueue -> DRIVER_OK. +/// 2. Do the I/O request. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failure. +/// 3/4: success. +#[test] +fn virtio_init_device_abnormal_status() { + let (blk, test_state, alloc, image_path) = set_up(); + + // Test some special status. + let status = [31, 0, 2, 16, 31, 0, 1, 16, 31, 0, 7, 16, 31, 64, 128]; + for i in 0..status.len() { + blk.borrow().set_status(status[i]); + if i % 4 == 0 { + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + } + if i % 7 == 0 { + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + blk.borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), 1); + } + } + + // Test 16 times of random status in [0, 0xff). + let mut rng = rand::thread_rng(); + for i in 0..16 { + blk.borrow().set_status(rng.gen_range(0..0xff)); + if i % 4 == 0 { + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + } + if i % 7 == 0 { + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + blk.borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), 1); + } + } + + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_driver(); + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + blk.borrow_mut().set_features_ok(); + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + let vqs = blk + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), 1); + blk.borrow_mut().set_driver_ok(); + + // 2. Do the I/O request. + for i in 0..DEFAULT_IO_REQS { + add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + blk.borrow().virtqueue_notify(vqs[0].clone()); + + // 3. Send qmp to StratoVirt. + check_stratovirt_status(test_state.clone()); + + // 4. Destroy device. + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Setting abnormal feature in device initialization. +/// TestStep: +/// 1. Init device. +/// negotiate unsupport features: +/// 1) 1 << 63; +/// 2) 1 << 63 | 1 << VIRTIO_F_VERSION_1; +/// 2. Do the I/O request. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failure. +/// 3/4: success. +#[test] +fn virtio_init_device_abnormal_features() { + for i in 0..2 { + let (blk, test_state, alloc, image_path) = set_up(); + + // 1. Init device. + blk.borrow_mut().reset(); + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_driver(); + // Set unsupported feature 1 << 63 or (1 << 63 | 1 << VIRTIO_F_VERSION_1). + let mut features = 1 << 63; + if i == 0 { + features |= 1 << VIRTIO_F_VERSION_1; + } + blk.borrow_mut().negotiate_features(features); + blk.borrow_mut().set_features_ok(); + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + let vqs = blk + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), 1); + blk.borrow_mut().set_driver_ok(); + + // 2. Do the I/O request. + if i == 0 { + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + } else { + for i in 0..DEFAULT_IO_REQS { + add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + blk.borrow().virtqueue_notify(vqs[0].clone()); + } + + // 3. Send qmp to StratoVirt. + check_stratovirt_status(test_state.clone()); + + // 4. Destroy device. + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting abnormal vring info in device initialization. +/// TestStep: +/// 1. Init device with abnormal steps: +/// 1) use invalid value to select queue(not enable multi-queue): +/// 2, u16::MAX +/// 2) set invalid queue size: +/// 0, 255, 1<<15, u16::MAX +/// 3) set address overlap in desc/avail/used; +/// 4) set not aligned desc/avail/used address; +/// 5) set invalid desc/avail/used address: +/// 0, 1 << 48, u64::MAX +/// 6) set 0 to enable vq; +/// 2. Do the I/O request. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failure. +/// 3/4: success. +#[test] +fn virtio_init_device_abnormal_vring_info() { + // (err_type, value, ack, device_status) + let reqs = [ + (0, u16::MAX as u64, 0, 0), + (0, 2, 0, 0), + (1, 0_u64, 0xff, 0), + (1, 255, 0xff, 0), + (1, 1 << 15, 0xff, 0), + (1, u16::MAX as u64, 0xff, 0), + (2, 0, 0xff, 0), + (3, 0, 0xff, 0), + (4, 0, 0xff, 0), + (5, 0, 0xff, 0), + (6, 0, 0xff, 0), + (6, 1 << 48, 0xff, 0), + (6, u64::MAX, 0xff, 0), + (7, 0, 0xff, 0), + (7, 1 << 48, 0xff, 0), + (7, u64::MAX, 0xff, 0), + (8, 0, 0xff, 0), + (8, 1 << 48, 0xff, 0), + (8, u64::MAX, 0xff, 0), + (9, 0, 0xff, 0), + ]; + + for (err_type, value, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + // 1. Init device. + blk.borrow_mut().reset(); + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_driver(); + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + blk.borrow_mut().set_features_ok(); + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + + let mut vqs = Vec::new(); + let vq = Rc::new(RefCell::new(TestVirtQueue::new())); + let features = blk.borrow().get_guest_features(); + // Set invalid value to select queue. + if err_type == 0 { + blk.borrow().queue_select(value as u16); + } + + let queue_size = blk.borrow().get_queue_size() as u32; + + // Set invalid queue size. + if err_type == 1 { + blk.borrow().set_queue_size(value as u16); + assert_eq!(blk.borrow().get_queue_size(), value as u16); + } + + vq.borrow_mut().index = 0; + vq.borrow_mut().size = queue_size; + vq.borrow_mut().free_head = 0; + vq.borrow_mut().num_free = queue_size; + vq.borrow_mut().align = VIRTIO_PCI_VRING_ALIGN; + vq.borrow_mut().indirect = (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) != 0; + vq.borrow_mut().event = (features & (1 << VIRTIO_RING_F_EVENT_IDX)) != 0; + + let addr = alloc + .borrow_mut() + .alloc(get_vring_size(queue_size, VIRTIO_PCI_VRING_ALIGN) as u64); + + vq.borrow_mut().desc = addr; + let avail = addr + (queue_size * size_of::() as u32) as u64 + 16; + vq.borrow_mut().avail = avail; + let used = (avail + + (size_of::() as u32 * (3 + queue_size)) as u64 + + VIRTIO_PCI_VRING_ALIGN as u64 + - 1) + & !(VIRTIO_PCI_VRING_ALIGN as u64 - 1) + 16; + vq.borrow_mut().used = used + 16; + + match err_type { + 2 => { + // Test desc and avail address overlap. + vq.borrow_mut().desc = addr + 16 + 1; + } + 3 => { + // TEST desc not aligned + vq.borrow_mut().desc = addr + 1; + } + 4 => { + // TEST avail not aligned. + vq.borrow_mut().avail = avail + 1; + } + 5 => { + // TEST used not aligned. + vq.borrow_mut().used = used + 1; + } + 6 => { + // TEST invalid desc address. + if value != u64::MAX { + vq.borrow_mut().desc = value; + } + } + 7 => { + // TEST invalie avail address. + if value != u64::MAX { + vq.borrow_mut().avail = value; + } + } + 8 => { + // TEST invalie used address. + if value != u64::MAX { + vq.borrow_mut().used = value; + } + } + _ => (), + } + + let mut desc = vq.borrow().desc; + let mut avail = vq.borrow().avail; + let mut used = vq.borrow().used; + if queue_size > 0 { + vq.borrow().vring_init(test_state.clone()); + } + // TEST invalid desc address. + if err_type == 6 && value == u64::MAX { + desc = value; + } + // TEST invalid avail address. + if err_type == 7 && value == u64::MAX { + avail = value; + } + // TEST invalid used address. + if err_type == 8 && value == u64::MAX { + used = value; + } + blk.borrow().activate_queue(desc, avail, used); + + let notify_off = blk.borrow().pci_dev.io_readw( + blk.borrow().bar, + blk.borrow().common_base as u64 + + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, + ); + vq.borrow_mut().queue_notify_off = blk.borrow().notify_base as u64 + + notify_off as u64 * blk.borrow().notify_off_multiplier as u64; + + // TEST enable vq with 0 + if err_type == 9 { + blk.borrow().pci_dev.io_writew( + blk.borrow().bar, + blk.borrow().common_base as u64 + + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, + 0, + ); + } else { + blk.borrow().pci_dev.io_writew( + blk.borrow().bar, + blk.borrow().common_base as u64 + + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, + 1, + ); + } + + blk.borrow() + .setup_virtqueue_intr(1, alloc.clone(), vq.clone()); + vqs.push(vq); + + blk.clone().borrow_mut().set_driver_ok(); + + // 2. Do the I/O request. + let mut req_addr: u64 = 0; + if queue_size > 0 { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + } + blk.borrow().virtqueue_notify(vqs[0].clone()); + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + // 3. Send qmp to StratoVirt. + check_stratovirt_status(test_state.clone()); + + // ecover the addr for free. + vqs[0].borrow_mut().desc = addr; + + // 4. Destroy device. + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Init device out of order test 1. +/// TestStep: +/// 1. Abnormal init device. +/// 1.1->1.3->1.2->1.4->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.4->1.3->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.5->1.4->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.6->1.5->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.7->1.6->1.5->1.8 +/// 2. Noraml init device. +/// 3. Write and read. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failed, stratovirt process status is normal. +/// 3/4: success. +#[test] +fn virtio_init_device_out_of_order_1() { + let (blk, test_state, alloc, image_path) = set_up(); + + let tests = vec![ + [1, 3, 2, 4, 5, 6, 7, 8], + [1, 2, 4, 3, 5, 6, 7, 8], + [1, 2, 3, 5, 4, 6, 7, 8], + [1, 2, 3, 4, 6, 5, 7, 8], + [1, 2, 3, 4, 7, 6, 5, 8], + ]; + + init_device_step(blk.clone(), test_state.clone(), alloc.clone(), tests); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Init device out of order test 2. +/// TestStep: +/// 1. Abnormal init device. +/// 1.1->1.2->1.3->1.4->1.8->1.6->1.7->1.5 +/// 1.1->1.3->1.4->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.4->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.5->1.6->1.8 +/// 2. Noraml init device. +/// 3. Write and read. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failed, stratovirt process status is normal. +/// 3/4: success. +#[test] +fn virtio_init_device_out_of_order_2() { + let (blk, test_state, alloc, image_path) = set_up(); + + let tests = vec![ + [1, 2, 3, 4, 8, 6, 7, 5], + [1, 3, 4, 5, 6, 7, 8, 0], + [1, 2, 4, 5, 6, 7, 8, 0], + [1, 2, 3, 4, 6, 7, 8, 0], + [1, 2, 3, 4, 5, 6, 8, 0], + ]; + + init_device_step(blk.clone(), test_state.clone(), alloc.clone(), tests); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Init device out of order test 3. +/// TestStep: +/// 1. Abnormal init device. +/// 1.1->1.2->1.3->1.4->1.5->1.6->1.7 +/// 1.1->1.2->1.3->1.4->1.9 +/// 1.1->1.2->1.3->1.5->1.8 +/// 1.1->1.2->1.3->1.4->1.9(FAILED)->normal init process +/// 1.1->1.2->1.3->1.4->1.9(FAILED)->1.2->1.3->1.4->1.5->1.6->1.7->1.8 +/// 2. Noraml init device. +/// 3. Write and read. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failed, stratovirt process status is normal. +/// 3/4: success. +#[test] +fn virtio_init_device_out_of_order_3() { + let (blk, test_state, alloc, image_path) = set_up(); + + let tests = vec![ + [1, 2, 3, 4, 5, 6, 7, 0], + [1, 2, 3, 4, 9, 0, 0, 0], + [1, 2, 3, 5, 8, 0, 0, 0], + [1, 2, 3, 4, 9, 0, 0, 0], + [1, 2, 3, 4, 5, 6, 7, 8], + [1, 2, 3, 4, 9, 0, 0, 0], + [2, 3, 4, 5, 6, 7, 8, 0], + ]; + + init_device_step(blk.clone(), test_state.clone(), alloc.clone(), tests); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Repeat the initialization operation. +/// TestStep: +/// 1. Init device. +/// reset -> reset -> ACKNOWLEDGE -> ACKNOWLEDGE -> DRIVER -> DRIVER -> +/// negotiate_features -> FEATURES_OK -> FEATURES_OK -> setup_virtqueue -> +/// DRIVER_OK. +/// 2. Do the I/O request. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 1/2: success or failed, stratovirt process status is normal. +/// 3/4: success. +#[test] +fn virtio_init_device_repeat() { + let (blk, test_state, alloc, image_path) = set_up(); + + // Reset virtio device twice. + blk.borrow_mut().reset(); + blk.borrow_mut().reset(); + // Set ACKNOWLEDGE twice. + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_acknowledge(); + // Set DRIVER twice. + blk.borrow_mut().set_driver(); + blk.borrow_mut().set_driver(); + + let features = blk.borrow().get_device_features() + | 1 << VIRTIO_RING_F_INDIRECT_DESC + | 1 << VIRTIO_RING_F_EVENT_IDX; + blk.borrow_mut().negotiate_features(features); + // Set FEATURES_OK twice. + blk.borrow_mut().set_features_ok(); + blk.borrow_mut().set_features_ok(); + + let capability = blk.borrow().config_readq(0); + assert_eq!(capability, TEST_IMAGE_SIZE / 512); + + blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + + let vqs = blk + .borrow_mut() + .init_virtqueue(test_state.clone(), alloc.clone(), 1); + blk.borrow_mut().set_driver_ok(); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Setting abnormal desc addr in IO request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request with abnormal desc[i]->addr: +/// 0, address unaligned, 0x5000, u64::MAX +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_addr() { + // (addr, ack, device_status) + let reqs = [ + (0, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (1, 0x2, 0), + (0x5000, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (u64::MAX, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + ]; + for (mut addr, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + if addr == 1 { + addr += req_addr; + } + test_state.borrow().writeq(vqs[0].borrow().desc, addr); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting abnormal desc length in IO request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request with abnormal desc[i]->len: +/// 1) 0 with 1 request 3 desc elems; +/// 2) 0x5000 with 1 request 3 desc elems; +/// 3) u32::MAX with 1 request 3 desc elems; +/// 4) total length of all desc is bigger than (1 << 32): +/// ((1 << 32) / 64) with indirect request which has 65 desc elems; +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_len() { + // (length, num of IO, ack, device_status) + let reqs = [ + (0, 1, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (0x5000, 1, VIRTIO_BLK_S_IOERR, 0), + (u32::MAX, 1, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (1 << 26, 65, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + ]; + for (length, io_num, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let req_addr: u64; + if io_num <= 1 { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + test_state.borrow().writel(vqs[0].borrow().desc + 8, length); + } else { + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), io_num); + for _ in 0..io_num { + indirect_req.add_desc(test_state.clone(), req_addr, length, true); + } + let free_head = + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + vqs[0].borrow().update_avail(test_state.clone(), free_head); + } + blk.borrow().virtqueue_notify(vqs[0].clone()); + + test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET); + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting abnormal desc flag in IO request, testcase 1. +/// TestStep: +/// 1. Init device, not negotiate INDIRECT_DESC feature. +/// 2. Do the I/O request with abnormal desc[i]->flags: +/// 1) add VRING_DESC_F_INDIRECT to flags +/// 2) add invalid value 16 to flags +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_flags_1() { + // (flag, ack, device_status) + let reqs = [ + (VRING_DESC_F_INDIRECT, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (16, 0, 0), + ]; + for (flag, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + + // Add VRING_DESC_F_INDIRECT or 16 to desc[0]->flags; + let flags = test_state.borrow().readw(vqs[0].borrow().desc + 12) | flag; + test_state.borrow().writew(vqs[0].borrow().desc + 12, flags); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting abnormal desc flag in IO request, testcase 2. +/// TestStep: +/// 1. Init device, negotiate INDIRECT_DESC feature. +/// 2. Do the I/O request with abnormal desc[i]->flags: +/// add VRING_DESC_F_INDIRECT to flags in indirect desc table. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_flags_2() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_RING_F_INDIRECT_DESC, + 1, + ); + + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + let req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + let free_head = vqs[0] + .borrow_mut() + .add(test_state.clone(), req_addr, REQ_ADDR_LEN, false); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); + test_state.borrow().writew( + vqs[0].borrow().desc + offset as u64 + 2, + free_head as u16 + 1, + ); + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), 2); + indirect_req.add_desc( + test_state.clone(), + req_addr + REQ_ADDR_LEN as u64, + REQ_DATA_LEN, + false, + ); + indirect_req.add_desc(test_state.clone(), req_addr + REQ_STATUS_OFFSET, 1, true); + indirect_req.set_desc_flag(test_state.clone(), 0, VRING_DESC_F_INDIRECT); + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!( + test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), + 0xff + ); + assert!(blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Setting abnormal desc flag in IO request, testcase 3. +/// TestStep: +/// 1. Init device, negotiate INDIRECT_DESC feature. +/// 2. Do the I/O request with abnormal desc[i]->flags: +/// add VRING_DESC_F_INDIRECT | VRING_DESC_F_WRITE to flags in indirect desc table, +/// and the device will ignore the VRING_DESC_F_WRITE flag. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_flags_3() { + // (flag, ack, device_status) + let reqs = [ + (VRING_DESC_F_WRITE, 0, 0), + (VRING_DESC_F_NEXT, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + ]; + for (flag, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_RING_F_INDIRECT_DESC, + 1, + ); + + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); + let req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + let free_head = vqs[0] + .borrow_mut() + .add(test_state.clone(), req_addr, 8, false); + //vqs[0].borrow().set_desc_flag(free_head, VRING_DESC_F_NEXT); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); + test_state.borrow().writew( + vqs[0].borrow().desc + offset as u64 + 2, + free_head as u16 + 1, + ); + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), 2); + indirect_req.add_desc(test_state.clone(), req_addr + 8, 520, false); + indirect_req.add_desc(test_state.clone(), req_addr + REQ_STATUS_OFFSET, 1, true); + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + + // Add VRING_DESC_F_WRITE or VRING_DESC_F_NEXT to desc[0]->flags; + let addr = vqs[0].borrow().desc + 16_u64 * (free_head + 1) as u64 + 12; + let flags = test_state.borrow().readw(addr) | flag; + test_state.borrow().writew(addr, flags); + blk.borrow().virtqueue_notify(vqs[0].clone()); + if flag == VRING_DESC_F_WRITE { + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting abnormal desc next in IO request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request with abnormal desc[i]->next: +/// 1) point to the wrong place in the queue_size; +/// 2) create an circuit; +/// 3) point to the place beyond the queue_size; +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_next() { + // (next, ack, device_status) + let reqs = [ + (0, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (16, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (u16::MAX, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + ]; + for (next, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + // It will create a write request with 3 desc elems: + // desc[0]: addr, len, flags(NEXT), next(0) + // desc[1]: addr, len, flags(NEXT), next(1) + // desc[2]: addr, len, flags(WRITE), next + let (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + ); + + assert_eq!(test_state.borrow().readw(vqs[0].borrow().desc + 14), 1); + // desc[1]->next = next; + test_state + .borrow() + .writew(vqs[0].borrow().desc + 16 + 14, next); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting desc elems in abnormal place in IO request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request with writable desc elem before +/// readable desc elem. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_desc_elem_place() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + // It will create a read request with 3 desc elems: + // desc[0]: addr, len, flags(NEXT), next(0) + // desc[1]: addr, len, flags(NEXT|WRITE), next(1) + // desc[2]: addr, len, flags(WRITE), next + let (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_IN, + 0, + ); + + // The address of desc[2]->flag. + assert_eq!(VRING_DESC_SIZE, 16); + let addr = vqs[0].borrow().desc + VRING_DESC_SIZE * 2 + 12; + assert_eq!(test_state.borrow().readw(addr), VRING_DESC_F_WRITE); + // desc[2]->flag = 0. + test_state.borrow().writew(addr, 0); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!( + test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), + 0xff + ); + assert!(blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Setting (queue_size + 1) indirect desc elems in IO request. +/// TestStep: +/// 1. Init device with INDIRECT feature. +/// 2. Do the I/O request with (queue_size + 1) desc elems in +/// indirect desc table. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 1/3/4: success. +/// 2: success or failure. +#[test] +fn virtio_io_abnormal_indirect_desc_elem_num() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let queue_size = vqs[0].borrow().size as usize; + + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, 2 * queue_size); + blk_req.data.push_str("TEST"); + let req_addr = virtio_request(test_state.clone(), alloc.clone(), blk_req); + let free_head = vqs[0] + .borrow_mut() + .add(test_state.clone(), req_addr, REQ_ADDR_LEN, false); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); + test_state.borrow().writew( + vqs[0].borrow().desc + offset as u64 + 2, + free_head as u16 + 1, + ); + let mut indirect_req = TestVringIndirectDesc::new(); + indirect_req.setup(alloc.clone(), test_state.clone(), queue_size as u16 + 1); + for i in 0..queue_size { + indirect_req.add_desc(test_state.clone(), req_addr + 16 + 2 * i as u64, 2, false); + } + indirect_req.add_desc( + test_state.clone(), + req_addr + 16 * 2 * queue_size as u64, + 1, + true, + ); + vqs[0] + .borrow_mut() + .add_indirect(test_state.clone(), indirect_req, true); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!( + test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), + 0xff + ); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Setting invalid flags to avail->flag in IO request. +/// TestStep: +/// 1. Init device with EVENT_IDX feature. +/// 2. Do the I/O request with avail->flags: +/// 1) invalid value: 2; +/// 2) VRING_AVAIL_F_NO_INTERRUPT with EVENT_IDX feature; +/// 3) VRING_AVAIL_F_NO_INTERRUPT | 2 with EVENT_IDX feature; +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_io_abnormal_avail_flags() { + let flags = [ + VRING_AVAIL_F_NO_INTERRUPT, + 2, + VRING_AVAIL_F_NO_INTERRUPT | 2, + ]; + for flag in flags { + do_event_idx_with_flag(flag); + } +} + +/// Setting invalid idx to avail->idx in IO request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request with avail->idx: +/// 1) assign 16 to avail->idx, but do not add req to desc; +/// 2) assign u16::MAX to avail->idx, which is bigger than queue size; +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_avail_idx() { + let idxs = [16, u16::MAX]; + for idx in idxs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + for i in 1..DEFAULT_IO_REQS { + add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set flag to avail->idx. + test_state.borrow().writew(vqs[0].borrow().avail + 2, idx); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting invalid desc_idx to avail->ring[i] in IO request. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request with avail->ring[i]: +/// 1) assign u16::MAX to avail->ring[i], which is bigger than queue size; +/// 2) avail->ring[i..j] point to the same desc index; +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_avail_ring() { + // (ring[i], ack, device_status) + let reqs = [(u16::MAX, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), (0, 0xff, 0)]; + for (value, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let mut req_addr = 0_u64; + for i in 0..DEFAULT_IO_REQS { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set value to avail->ring[DEFAULT_IO_REQS - 1]. + test_state + .borrow() + .writew(vqs[0].borrow().avail + 4 + 2 * (DEFAULT_IO_REQS - 1), value); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting invalid value to avail->used_event in IO request. +/// TestStep: +/// 1. Init device with or with not EVENT_IDX feature. +/// 2. Do the I/O request with avail->used_event: +/// 1) without EVENT_IDX, set valud to used_event. +/// 2) with EVENT_IDX, set u16::MAX to used_event. +/// 3) with EVENT_IDX, do not modify used_event. +/// 3. Send qmp to StratoVirt. +/// 4. Destroy device. +/// Expect: +/// 2: success or failure. +/// 1/3/4: success. +#[test] +fn virtio_io_abnormal_used_event() { + // (feature, used_event, ack, device_status) + let reqs = [ + (VIRTIO_F_VERSION_1, DEFAULT_IO_REQS as u16 - 1, 0, 0), + (VIRTIO_RING_F_EVENT_IDX, u16::MAX, 0, 0), + (VIRTIO_RING_F_EVENT_IDX, 0, 0, 0), + ]; + for (feature, used_event, ack, device_status) in reqs { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1 | 1 << feature, + 1, + ); + + send_one_request( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + ); + + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + for i in 1..DEFAULT_IO_REQS { + (free_head, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set used_event to avail->used_event. + vqs[0] + .borrow() + .set_used_event(test_state.clone(), used_event); + blk.borrow().virtqueue_notify(vqs[0].clone()); + + if feature == VIRTIO_RING_F_EVENT_IDX { + check_req_result( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + req_addr + REQ_STATUS_OFFSET, + TIMEOUT_US, + ); + } else { + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } + + assert_eq!(test_state.borrow().readb(req_addr + REQ_STATUS_OFFSET), ack); + assert_eq!( + blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET, + device_status + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + } +} + +/// Setting invalid value to used->idx in IO request. +/// TestStep: +/// 1. Init device; +/// 2. Do the I/O request with avail->used_event = u16::MAX; +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_io_abnormal_used_idx() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let mut free_head = 0_u32; + for i in 0..DEFAULT_IO_REQS { + (free_head, _) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i, + ); + } + + // Set u16::MAX to used->idx. + test_state + .borrow() + .writew(vqs[0].borrow().used + 2, u16::MAX); + blk.borrow().virtqueue_notify(vqs[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Virtio test step out of order, testcase 1. +/// TestStep: +/// 1. Init device. +/// 2. Do the I/O request(normal io in desc). +/// 3. Init device. +/// 4. Do the I/O request(normal io in desc). +/// 5. Send qmp to StratoVirt. +/// 6. Destroy device. +/// Expect: +/// 1/2/5/6: success. +/// 3/4: success or failure. +#[test] +fn virtio_test_out_of_order_1() { + let (blk, test_state, alloc, image_path) = set_up(); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + check_stratovirt_status(test_state.clone()); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Virtio test step out of order, testcase 2. +/// TestStep: +/// 1. Init device. +/// 2. Destroy device. +/// 3. Init device. +/// 4. Do the I/O request(normal io in desc). +/// 5. Destroy device. +/// Expect: +/// 1/2/3/4/5: success. +#[test] +fn virtio_test_out_of_order_2() { + let (blk, test_state, alloc, image_path) = set_up(); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); + + let (blk, test_state, alloc, image_path) = set_up(); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs, + image_path.clone(), + ); +} + +/// Virtio test step repeat. +/// TestStep: +/// 1. Init device. +/// 2. Init device. +/// 3. Do the I/O request(normal io in desc). +/// 4. Do the I/O request(normal io in desc). +/// 5. Send qmp to StratoVirt. +/// 6. Destroy device. +/// 7. Destroy device. +/// Expect: +/// 1/2/3/4/5/6/7: success. +#[test] +fn virtio_test_repeat() { + let (blk, test_state, alloc, image_path) = set_up(); + + blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + 0, + ); + virtio_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + DEFAULT_IO_REQS, + ); + virtio_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + DEFAULT_IO_REQS * 2 - 1, + ); + + check_stratovirt_status(test_state.clone()); + + blk.borrow_mut().destroy_device(alloc.clone(), vqs.clone()); + blk.borrow_mut().destroy_device(alloc.clone(), vqs.clone()); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + vqs.clone(), + image_path.clone(), + ); +} -- Gitee From 2f1c9a710c73d5c12b03124185807a587350fce8 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 27 Feb 2023 22:08:56 +0800 Subject: [PATCH 0834/2187] balloon: fix madvise Unordered address testing Signed-off-by: jiewangqun --- tests/mod_test/tests/balloon_test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 714700d81..76947c7bb 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -110,8 +110,8 @@ impl VirtioBalloonTest { } fn inflate_fun(shared: bool) { - let page_num = 255_u32; - let mut idx = 0_u32; + let page_num = 255_i32; + let mut idx = 0_i32; let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, false); let free_page = balloon @@ -147,21 +147,21 @@ fn inflate_fun(shared: bool) { ); // begin balloon addresses - let mut loop_num = 0_u32; + let mut loop_num = page_num - 1; let mut msg = Vec::new(); - while loop_num < page_num { + while loop_num >= 0 { balloon .state .borrow_mut() - .writel(pfn_addr + 4 * loop_num as u64, pfn + loop_num); + .writel(pfn_addr + 4 * loop_num as u64, pfn + loop_num as u32); let entry = TestVringDescEntry { data: pfn_addr + (loop_num as u64 * 4), len: 4, write: false, }; msg.push(entry); - loop_num += 1; + loop_num -= 1; } let free_head = balloon .inf_queue -- Gitee From c735cc91d7c14898ed6d2f8ca521f87215ce190e Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 23 Feb 2023 15:16:26 +0800 Subject: [PATCH 0835/2187] VNC: add mst for vnc Add MST test for vnc. Signed-off-by: Xiao Ye --- Cargo.lock | 2 + tests/mod_test/Cargo.toml | 3 + tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/vnc.rs | 1441 +++++++++++++++++++++++++++ tests/mod_test/tests/vnc_test.rs | 745 ++++++++++++++ 5 files changed, 2192 insertions(+) create mode 100644 tests/mod_test/src/libdriver/vnc.rs create mode 100644 tests/mod_test/tests/vnc_test.rs diff --git a/Cargo.lock b/Cargo.lock index 952fa6e0e..a71943f1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,6 +534,7 @@ name = "mod_test" version = "2.2.0" dependencies = [ "acpi", + "anyhow", "byteorder", "devices", "hex", @@ -542,6 +543,7 @@ dependencies = [ "serde_json", "util", "virtio", + "vmm-sys-util", ] [[package]] diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 23d93c035..e267749b6 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -8,6 +8,8 @@ license = "Mulan PSL v2" [dependencies] rand = "0.8.5" hex = "0.4.3" +vmm-sys-util = "0.11.0" +anyhow = "1.0" serde_json = "1.0" byteorder = "1.4.3" devices = { path = "../../devices" } @@ -15,3 +17,4 @@ util = { path = "../../util" } acpi = { path = "../../acpi" } machine = { path = "../../machine" } virtio = { path = "../../virtio"} + diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index de70e5867..f929d2365 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -21,3 +21,4 @@ pub mod virtio_console; pub mod virtio_net; pub mod virtio_pci_modern; pub mod virtio_rng; +pub mod vnc; diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs new file mode 100644 index 000000000..f75265b32 --- /dev/null +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -0,0 +1,1441 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::{ + libdriver::vnc::EncodingType::*, + libtest::{test_init, TestState}, +}; +use anyhow::{bail, Result}; +use core::time; +use std::{ + cell::RefCell, + cmp, + io::{self, Read, Write}, + net::{Shutdown, SocketAddr, TcpStream}, + os::unix::prelude::AsRawFd, + rc::Rc, +}; +use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; + +use super::{ + machine::TestStdMachine, + malloc::GuestAllocator, + pci::{PCIBarAddr, TestPciDev, PCI_VENDOR_ID}, + pci_bus::TestPciBus, +}; + +const EPOLL_DEFAULT_TIMEOUT: i32 = 1000; +pub const MAX_RECVBUF_LEN: usize = 1024; +pub const READ_TIME_OUT: u64 = 30; +pub const RFB_PORT_OFFSET: u16 = 5900; +/// Size of subrectangle. +const HEXTILE_BLOCK_SIZE: usize = 16; +/// SubEncoding type of hextile. +const RAW: u8 = 0x01; +const BACKGROUND_SPECIFIC: u8 = 0x02; +const FOREGROUND_SPECIFIC: u8 = 0x04; +const ANY_SUBRECTS: u8 = 0x08; +const SUBRECTS_COLOURED: u8 = 0x10; + +pub const PIXMAN_A8B8G8R8: u32 = 0; +pub const PIXMAN_X2R10G10B10: u32 = 1; +pub const PIXMAN_R8G8B8: u32 = 2; +pub const PIXMAN_A1: u32 = 3; +pub const PIXMAN_YUY2: u32 = 4; +pub const REFRESH_TIME_INTERVAL: u64 = 3000 * 1000 * 1000; + +/// Input event. +#[derive(Debug, Clone, Copy)] +pub enum InputEvent { + KbdEvent = 0, + MouseEvent = 1, + InvalidEvent = 255, +} + +impl Default for InputEvent { + fn default() -> Self { + InputEvent::InvalidEvent + } +} + +impl From for InputEvent { + fn from(v: u8) -> Self { + match v { + 0 => InputEvent::KbdEvent, + 1 => InputEvent::MouseEvent, + _ => InputEvent::InvalidEvent, + } + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct InputMessage { + pub event_type: InputEvent, + pub keycode: u16, + pub down: u8, + pub button: u32, + pub x: u32, + pub y: u32, +} + +/// GPU device Event. +#[derive(Debug, Clone, Copy)] +pub enum GpuEvent { + ReplaceSurface = 0, + ReplaceCursor = 1, + GraphicUpdateArea = 2, + GraphicUpdateDirty = 3, + Deactive = 4, +} + +impl Default for GpuEvent { + fn default() -> Self { + GpuEvent::Deactive + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct TestGpuCmd { + pub event_type: GpuEvent, + pub x: u32, + pub y: u32, + pub w: u32, + pub h: u32, + pub data_len: u32, +} + +// Encodings Type +#[repr(u32)] +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum EncodingType { + EncodingRaw = 0x00000000, + EncodingCopyrect = 0x00000001, + EncodingHextile = 0x00000005, + EncodingZlib = 0x00000006, + EncodingTight = 0x00000007, + EncodingZrle = 0x00000010, + EncodingZywrle = 0x00000011, + EncodingCompresslevel0 = 0xFFFFFF00, + EncodingQualitylevel0 = 0xFFFFFFE0, + EncodingRichCursor = 0xFFFFFF11, + EncodingAlphaCursor = 0xFFFFFEC6, + EncodingDesktopresize = 0xFFFFFF21, + EncodingPointerTypeChange = 0xFFFFFEFF, + EncodingExtKeyEvent = 0xFFFFFEFE, + EncodingAudio = 0xFFFFFEFD, + EncodingTightPng = 0xFFFFFEFC, + EncodingLedState = 0xFFFFFEFB, + EncodingWmvi = 0x574D5669, + EncodingInvalid = 0xFFFFFFFF, +} + +impl EncodingType { + pub const ENCODINGTYPE: [Self; 18] = [ + EncodingRaw, + EncodingCopyrect, + EncodingHextile, + EncodingZlib, + EncodingTight, + EncodingZrle, + EncodingZywrle, + EncodingCompresslevel0, + EncodingQualitylevel0, + EncodingRichCursor, + EncodingAlphaCursor, + EncodingDesktopresize, + EncodingPointerTypeChange, + EncodingExtKeyEvent, + EncodingAudio, + EncodingTightPng, + EncodingLedState, + EncodingWmvi, + ]; +} + +impl From for EncodingType { + fn from(v: u32) -> Self { + match v { + 0x00000000 => EncodingType::EncodingRaw, + 0x00000001 => EncodingType::EncodingCopyrect, + 0x00000005 => EncodingType::EncodingHextile, + 0x00000006 => EncodingType::EncodingZlib, + 0x00000007 => EncodingType::EncodingTight, + 0x00000010 => EncodingType::EncodingZrle, + 0x00000011 => EncodingType::EncodingZywrle, + 0xFFFFFF00 => EncodingType::EncodingCompresslevel0, + 0xFFFFFFE0 => EncodingType::EncodingQualitylevel0, + 0xFFFFFF11 => EncodingType::EncodingRichCursor, + 0xFFFFFEC6 => EncodingType::EncodingAlphaCursor, + 0xFFFFFF21 => EncodingType::EncodingDesktopresize, + 0xFFFFFEFF => EncodingType::EncodingPointerTypeChange, + 0xFFFFFEFE => EncodingType::EncodingExtKeyEvent, + 0xFFFFFEFD => EncodingType::EncodingAudio, + 0xFFFFFEFC => EncodingType::EncodingTightPng, + 0xFFFFFEFB => EncodingType::EncodingLedState, + 0x574D5669 => EncodingType::EncodingWmvi, + _ => EncodingType::EncodingInvalid, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum RfbServerMsg { + FramebufferUpdate = 0, + SetColourMapEntries = 1, + InvalidMsg, +} + +impl From for RfbServerMsg { + fn from(v: u8) -> Self { + match v { + 0 => RfbServerMsg::FramebufferUpdate, + 1 => RfbServerMsg::SetColourMapEntries, + _ => RfbServerMsg::InvalidMsg, + } + } +} + +#[derive(Clone, Copy)] +pub enum UpdateState { + NotIncremental = 0, + Incremental = 1, +} + +#[derive(Clone, Copy)] +pub enum TestAuthType { + Invalid = 0, + VncAuthNone = 1, +} + +#[derive(Clone, Copy, Default, Debug)] +pub struct RfbPixelFormat { + bit_per_pixel: u8, + depth: u8, + big_endian: u8, + true_color_flag: u8, + red_max: u16, + green_max: u16, + blue_max: u16, + red_shift: u8, + green_shift: u8, + blue_shift: u8, + pad1: u8, + pad2: u16, +} + +impl RfbPixelFormat { + pub fn new( + bit_per_pixel: u8, + depth: u8, + big_endian: u8, + true_color_flag: u8, + red_max: u16, + green_max: u16, + blue_max: u16, + red_shift: u8, + green_shift: u8, + blue_shift: u8, + ) -> Self { + Self { + bit_per_pixel, + depth, + big_endian, + true_color_flag, + red_max, + green_max, + blue_max, + red_shift, + green_shift, + blue_shift, + pad1: 0_u8, + pad2: 0_u16, + } + } + + fn from_bytes(&mut self, buf: &Vec) { + self.bit_per_pixel = buf[0]; + self.depth = buf[1]; + self.big_endian = buf[2]; + self.true_color_flag = buf[3]; + self.red_max = u16::from_be_bytes([buf[4], buf[5]]); + self.green_max = u16::from_be_bytes([buf[6], buf[7]]); + self.blue_max = u16::from_be_bytes([buf[8], buf[9]]); + self.red_shift = buf[10]; + self.green_shift = buf[11]; + self.blue_shift = buf[12]; + self.pad1 = buf[13]; + self.pad2 = u16::from_be_bytes([buf[14], buf[15]]); + } + + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append(&mut self.bit_per_pixel.to_be_bytes().to_vec()); + buf.append(&mut self.depth.to_be_bytes().to_vec()); + buf.append(&mut self.big_endian.to_be_bytes().to_vec()); + buf.append(&mut self.true_color_flag.to_be_bytes().to_vec()); + buf.append(&mut self.red_max.to_be_bytes().to_vec()); + buf.append(&mut self.green_max.to_be_bytes().to_vec()); + buf.append(&mut self.blue_max.to_be_bytes().to_vec()); + buf.append(&mut self.red_shift.to_be_bytes().to_vec()); + buf.append(&mut self.green_shift.to_be_bytes().to_vec()); + buf.append(&mut self.blue_shift.to_be_bytes().to_vec()); + buf.append(&mut self.pad1.to_be_bytes().to_vec()); + buf.append(&mut self.pad2.to_be_bytes().to_vec()); + buf + } +} + +pub struct RfbFrameBuffHead { + pub flag: RfbServerMsg, + pub pad0: u8, + pub num_rects: u16, +} + +impl RfbFrameBuffHead { + fn new(buf: &Vec) -> Self { + assert!(buf.len() >= 4); + Self { + flag: RfbServerMsg::from(buf[0]), + pad0: buf[1], + num_rects: u16::from_be_bytes([buf[2], buf[3]]), + } + } +} + +#[derive(Debug)] +pub struct RfbFrameBuff { + pub x: u16, + pub y: u16, + pub w: u16, + pub h: u16, + pub enc: u32, +} + +impl RfbFrameBuff { + fn new(buf: &Vec) -> Self { + assert!(buf.len() >= 12); + Self { + x: u16::from_be_bytes([buf[0], buf[1]]), + y: u16::from_be_bytes([buf[2], buf[3]]), + w: u16::from_be_bytes([buf[4], buf[5]]), + h: u16::from_be_bytes([buf[6], buf[7]]), + enc: u32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]), + } + } +} + +pub struct RfbSetColourMap { + pub flag: RfbServerMsg, + pub pad0: u8, + pub first_color: u16, + pub num_of_colurs: u16, +} + +impl RfbSetColourMap { + fn new(buf: &Vec) -> Self { + assert!(buf.len() >= 6); + Self { + flag: RfbServerMsg::from(buf[0]), + pad0: buf[1], + first_color: u16::from_be_bytes([buf[2], buf[3]]), + num_of_colurs: u16::from_be_bytes([buf[4], buf[5]]), + } + } +} + +pub trait TestEventOperation { + fn to_be_bytes(&self) -> Vec; +} + +#[derive(Clone, Copy)] +pub enum RfbClientMessage { + RrbSetPixelFormat = 0, + RfbSetEncoding = 2, + RfbUpdateRequest = 3, + RfbKeyEvent = 4, + RfbPointerEvent = 5, + RfbClientCutText = 6, +} + +pub struct TestPointEvent { + pub evnet_type: RfbClientMessage, + pub button_mask: u8, + pub x: u16, + pub y: u16, +} + +impl TestPointEvent { + fn new(button_mask: u8, x: u16, y: u16) -> Self { + Self { + evnet_type: RfbClientMessage::RfbPointerEvent, + button_mask, + x, + y, + } + } +} + +impl TestEventOperation for TestPointEvent { + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append(&mut (self.evnet_type as u8).to_be_bytes().to_vec()); + buf.append(&mut self.button_mask.to_be_bytes().to_vec()); + buf.append(&mut self.x.to_be_bytes().to_vec()); + buf.append(&mut self.y.to_be_bytes().to_vec()); + buf + } +} + +pub struct TestKeyEvent { + pub event_type: RfbClientMessage, + pub down: u8, + pub pad: u16, + pub keysym: u32, +} + +impl TestKeyEvent { + fn new(down: u8, keysym: u32) -> Self { + Self { + event_type: RfbClientMessage::RfbKeyEvent, + down, + pad: 0_u16, + keysym, + } + } +} + +impl TestEventOperation for TestKeyEvent { + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append(&mut (self.event_type as u8).to_be_bytes().to_vec()); + buf.append(&mut self.down.to_be_bytes().to_vec()); + buf.append(&mut self.pad.to_be_bytes().to_vec()); + buf.append(&mut self.keysym.to_be_bytes().to_vec()); + buf + } +} + +pub struct TestSetupEncoding { + pub event_type: RfbClientMessage, + pub pad: u8, + pub num_encodings: u16, + pub encs: Vec, +} + +impl TestSetupEncoding { + fn new() -> Self { + Self { + event_type: RfbClientMessage::RfbSetEncoding, + pad: 0_u8, + num_encodings: 0_u16, + encs: Vec::new(), + } + } +} + +impl TestEventOperation for TestSetupEncoding { + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append(&mut (self.event_type as u8).to_be_bytes().to_vec()); + buf.append(&mut self.pad.to_be_bytes().to_vec()); + buf.append(&mut self.num_encodings.to_be_bytes().to_vec()); + for enc in self.encs.iter() { + buf.append(&mut (*enc as u32).to_be_bytes().to_vec()); + } + buf + } +} + +pub struct TestUpdateFrameBuffer { + pub event_type: RfbClientMessage, + pub incremental: UpdateState, + pub x: u16, + pub y: u16, + pub w: u16, + pub h: u16, +} + +impl TestUpdateFrameBuffer { + fn new(incremental: UpdateState, x: u16, y: u16, w: u16, h: u16) -> Self { + Self { + event_type: RfbClientMessage::RfbUpdateRequest, + incremental, + x, + y, + w, + h, + } + } +} + +impl TestEventOperation for TestUpdateFrameBuffer { + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append(&mut (self.event_type as u8).to_be_bytes().to_vec()); + buf.append(&mut (self.incremental as u8).to_be_bytes().to_vec()); + buf.append(&mut self.x.to_be_bytes().to_vec()); + buf.append(&mut self.y.to_be_bytes().to_vec()); + buf.append(&mut self.w.to_be_bytes().to_vec()); + buf.append(&mut self.h.to_be_bytes().to_vec()); + buf + } +} + +pub struct TestSetPixelFormat { + pub event_type: RfbClientMessage, + pub pad0: u8, + pub pad1: u16, + pub pf: RfbPixelFormat, +} + +impl TestSetPixelFormat { + fn new(pf: RfbPixelFormat) -> Self { + Self { + event_type: RfbClientMessage::RrbSetPixelFormat, + pad0: 0_u8, + pad1: 0_u16, + pf, + } + } +} + +impl TestEventOperation for TestSetPixelFormat { + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append(&mut (self.event_type as u8).to_be_bytes().to_vec()); + buf.append(&mut self.pad0.to_be_bytes().to_vec()); + buf.append(&mut self.pad1.to_be_bytes().to_vec()); + buf.append(&mut self.pf.to_be_bytes()); + buf + } +} + +/// Display mode information. +#[derive(Default)] +pub struct DisplayMode { + pub width: u16, + pub height: u16, + pub test_pf: RfbPixelFormat, + pub app_name: String, +} + +impl DisplayMode { + pub fn from_bytes(&mut self, buf: &mut Vec) { + self.width = u16::from_be_bytes([buf[0], buf[1]]); + self.height = u16::from_be_bytes([buf[2], buf[3]]); + buf.drain(..4); + + // Pixel format message. + self.test_pf.from_bytes(&buf[..16].to_vec()); + buf.drain(..16); + + // Application name + len. + let name_len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]); + buf.drain(..4); + self.app_name = String::from_utf8(buf[..name_len as usize].to_vec()).unwrap(); + buf.drain(..name_len as usize); + + println!( + "Display infomation set by server:\n \ + application name: {:?} Image size: width: {:?}, height: {:?}\n \ + big endian: {:?}, true color flag: {:?} red max {:?} red shift {:?}\n \ + green max {:?} green shift {:?} blue max {:?} blue shift {:?}\n", + self.app_name, + self.width, + self.height, + self.test_pf.big_endian, + self.test_pf.true_color_flag, + self.test_pf.red_max, + self.test_pf.red_shift, + self.test_pf.green_max, + self.test_pf.green_shift, + self.test_pf.blue_max, + self.test_pf.blue_shift + ); + } + + pub fn check(&mut self) { + assert!(0 < self.width && self.width <= 2560); + assert!(0 < self.height && self.height <= 2048); + assert!(self.app_name.len() <= 100); + } +} + +pub trait IoOperations { + fn channel_write_full(&mut self, buf: &[u8]) -> Result; + fn channel_read_full(&mut self, buf: &mut Vec) -> Result; +} + +pub struct IoChannel { + stream: TcpStream, +} + +impl IoChannel { + pub fn new(stream: TcpStream) -> Self { + Self { stream } + } +} + +impl IoOperations for IoChannel { + fn channel_write_full(&mut self, buf: &[u8]) -> Result { + let buf_size = buf.len(); + let mut offset = 0; + while offset < buf_size { + let tmp_buf = &buf[offset..]; + match self.stream.write(tmp_buf) { + Ok(ret) => { + offset += ret; + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + return Ok(offset); + } + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => { + continue; + } + Err(e) => { + bail!("Unable to write msg on socket: {:?}", e); + } + } + } + + Ok(buf_size) + } + + fn channel_read_full(&mut self, buf: &mut Vec) -> Result { + let mut len = 0_usize; + loop { + let mut bytes = vec![0_u8; MAX_RECVBUF_LEN]; + match self.stream.read(&mut bytes) { + Ok(ret) => { + buf.append(&mut bytes[..ret].to_vec()); + len += ret; + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + return Ok(len); + } + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => { + continue; + } + Err(e) => { + bail!("Unable to read msg from socket: {:?}", e); + } + } + break; + } + + Ok(len) + } +} + +pub struct VncClient { + pub stream: TcpStream, + pub io_channel: Rc>, + pub display_mod: DisplayMode, + epoll: Epoll, + pub ready_events: Vec, +} + +impl VncClient { + pub fn new(stream: TcpStream, io_channel: Rc>) -> Self { + let epoll = Epoll::new().unwrap(); + Self { + stream, + io_channel, + display_mod: DisplayMode::default(), + epoll, + ready_events: vec![EpollEvent::default(); 1], + } + } + + pub fn epoll_ctl(&mut self, event: EpollEvent) -> io::Result<()> { + self.epoll + .ctl(ControlOperation::Add, self.stream.as_raw_fd() as i32, event) + } + + /// Wait for events on the epoll. + /// or wait util the timeout. + /// Step: + /// 1. Switch listening event. + /// 2. Return if event happen or time out. + pub fn epoll_wait(&mut self, event_set: EventSet) -> io::Result { + let event = EpollEvent::new(event_set, self.stream.as_raw_fd() as u64); + if let Err(e) = self.epoll.ctl( + ControlOperation::Modify, + self.stream.as_raw_fd() as i32, + event, + ) { + return Err(e); + } + self.epoll + .wait(EPOLL_DEFAULT_TIMEOUT, &mut self.ready_events[..]) + } + + /// Read the data in the Stream util empty. + pub fn stream_read_to_end(&mut self) -> Result<()> { + let mut buf: Vec = Vec::new(); + let event = EpollEvent::new(EventSet::IN, self.stream.as_raw_fd() as u64); + self.epoll.ctl( + ControlOperation::Modify, + self.stream.as_raw_fd() as i32, + event, + )?; + + match self + .epoll + .wait(EPOLL_DEFAULT_TIMEOUT, &mut self.ready_events[..]) + { + Ok(event_counts) if event_counts > 0 => { + self.io_channel.borrow_mut().channel_read_full(&mut buf)?; + buf.clear(); + } + _ => return Ok(()), + } + + Ok(()) + } + + /// Read message until the total number of bytes is exceed the expect. + pub fn read_msg(&mut self, buf: &mut Vec, expect: usize) -> Result { + let mut total_received: usize = 0; + loop { + if buf.len() >= expect { + break; + } + // Wait event. + match self.epoll_wait(EventSet::IN) { + Ok(n) if n > 0 => {} + _ => bail!("Io Channel is broken"), + } + let mut tmp_buf: Vec = Vec::new(); + let len = match self.io_channel.borrow_mut().channel_read_full(&mut tmp_buf) { + Ok(n) => { + total_received += n; + n + } + Err(e) => return Err(e), + }; + buf.append(&mut tmp_buf[..len].to_vec()); + } + + Ok(total_received) + } + + /// Write message. + pub fn write_msg(&mut self, buf: &[u8]) -> Result { + let total_byte = buf.len(); + loop { + let mut send_bytes: usize = 0; + match self.io_channel.borrow_mut().channel_write_full(buf) { + Ok(len) => send_bytes += len, + Err(e) => return Err(e), + } + + if send_bytes >= total_byte { + break; + } + } + Ok(total_byte) + } + + pub fn connect(&mut self, sec_type: TestAuthType) -> Result<()> { + let mut buf: Vec = Vec::new(); + println!("Connect to server."); + // Step 1: Exchange RFB Protocol: RFB 003.008. + self.read_msg(&mut buf, 12)?; + if "RFB 003.008\n".as_bytes().to_vec() != buf[..12].to_vec() { + bail!("Unsupport RFB version"); + } + self.write_msg(&"RFB 003.008\n".as_bytes().to_vec())?; + buf.drain(..12); + + // Step 2: Auth num is 1. + self.read_msg(&mut buf, 1)?; + let auth_num = buf[0]; + assert!(auth_num > 0); + buf.drain(..1); + self.read_msg(&mut buf, auth_num as usize)?; + if sec_type as u8 != buf[0] { + bail!("Unsupport security type!"); + } + buf.drain(..auth_num as usize); + self.write_msg(&(sec_type as u8).to_be_bytes().to_vec())?; + + match sec_type { + TestAuthType::VncAuthNone => { + // Step 3. Handle_auth: Authstate::No, Server accept auth and client send share mode. + self.read_msg(&mut buf, 4)?; + if buf[..4].to_vec() != [0_u8; 4].to_vec() { + bail!("Reject by vnc server"); + } + self.write_msg(&0_u8.to_be_bytes().to_vec())?; + buf.drain(..4); + + // Step 4. display mode information init: width + height + pixelformat + app_name. + self.read_msg(&mut buf, 24)?; + self.display_mod.from_bytes(&mut buf); + self.display_mod.check(); + } + _ => {} + } + self.stream_read_to_end()?; + println!("Connection established!"); + Ok(()) + } + + /// Send point event to VncServer. + pub fn test_point_event(&mut self, buttom_mask: u8, x: u16, y: u16) -> Result<()> { + println!("Test point event."); + let test_event = TestPointEvent::new(buttom_mask, x, y); + self.write_msg(&mut test_event.to_be_bytes())?; + Ok(()) + } + + /// Send point event to VncServer. + pub fn test_key_event(&mut self, down: u8, keysym: u32) -> Result<()> { + println!("Test key event."); + let test_event = TestKeyEvent::new(down, keysym); + self.write_msg(&mut test_event.to_be_bytes())?; + Ok(()) + } + + /// Send set encodings to VncServer. + /// + /// # Arguments. + /// + /// * `enc_num` - total number of feature support by VncClient. + /// * `enc` - features supported by VncClient. + pub fn test_setup_encodings( + &mut self, + enc_num: Option, + enc: Option, + ) -> Result<()> { + println!("Test setup encodings"); + let mut test_event = TestSetupEncoding::new(); + if let Some(encoding) = enc { + test_event.encs.push(encoding); + test_event.num_encodings = match enc_num { + Some(num) => num, + None => 1_u16, + }; + } else { + for encoding in EncodingType::ENCODINGTYPE { + test_event.encs.push(encoding); + } + test_event.num_encodings = match enc_num { + Some(num) => num, + None => 17_u16, + }; + } + self.write_msg(&mut test_event.to_be_bytes())?; + Ok(()) + } + + /// Sent update framebuffer request to VncServer. + pub fn test_update_request( + &mut self, + incremental: UpdateState, + x: u16, + y: u16, + w: u16, + h: u16, + ) -> Result<()> { + println!("Test update frambuff request."); + let test_event = TestUpdateFrameBuffer::new(incremental, x, y, w, h); + self.write_msg(&mut test_event.to_be_bytes())?; + Ok(()) + } + + /// Send set pixel format to VncClient. + pub fn test_set_pixel_format(&mut self, pf: RfbPixelFormat) -> Result<()> { + println!("Test set pixel format."); + let test_event = TestSetPixelFormat::new(pf); + self.write_msg(&mut test_event.to_be_bytes())?; + Ok(()) + } + + /// Receive the framebuferr data, and verify the format. + /// + /// # Arguments + /// * `pf` - Pixel format set to server. + /// * `enc` - Image encoding type. + pub fn test_recv_server_data( + &mut self, + pf: RfbPixelFormat, + ) -> Result> { + let mut buf: Vec = Vec::new(); + let mut rfb_event: Vec<(RfbServerMsg, EncodingType)> = Vec::new(); + loop { + // Wait event. + match self.epoll_wait(EventSet::IN) { + Ok(n) if n > 0 => {} + _ => break, + } + + self.read_msg(&mut buf, 1)?; + match RfbServerMsg::from(buf[0]) { + RfbServerMsg::FramebufferUpdate => { + self.read_msg(&mut buf, 4)?; + let frame_head = RfbFrameBuffHead::new(&mut buf); + buf.drain(..4); + println!("Total number of rects : {:?}", frame_head.num_rects); + for i in 0..frame_head.num_rects { + println!("Rect: {:?}", i + 1); + self.read_msg(&mut buf, 12)?; + let frame_buff = RfbFrameBuff::new(&mut buf); + buf.drain(..12); + rfb_event.push(( + RfbServerMsg::FramebufferUpdate, + EncodingType::from(frame_buff.enc), + )); + self.handle_server_msg(pf, frame_buff, &mut buf)?; + } + } + RfbServerMsg::SetColourMapEntries => { + rfb_event.push(( + RfbServerMsg::SetColourMapEntries, + EncodingType::EncodingInvalid, + )); + self.read_msg(&mut buf, 6)?; + let colour_map = RfbSetColourMap::new(&buf); + buf.drain(..6); + let message_len = colour_map.num_of_colurs * 6; + self.read_msg(&mut buf, message_len as usize)?; + buf.drain(..message_len as usize); + assert_eq!(buf.len(), 0_usize); + println!( + "Set Color Map Entries, total num of colours:{:?}", + colour_map.num_of_colurs + ); + } + _ => { + assert!(false); + } + } + } + Ok(rfb_event) + } + + /// Handle messages from Vnc Server. + fn handle_server_msg( + &mut self, + pf: RfbPixelFormat, + frame_buff: RfbFrameBuff, + buf: &mut Vec, + ) -> Result<()> { + match EncodingType::from(frame_buff.enc) { + EncodingType::EncodingRaw => { + self.parse_raw_image_data(pf, frame_buff, buf)?; + } + EncodingType::EncodingHextile => { + self.parse_hextile_image_data(pf, frame_buff, buf)?; + } + EncodingType::EncodingDesktopresize => { + self.display_mod.width = frame_buff.w; + self.display_mod.height = frame_buff.h; + } + EncodingType::EncodingRichCursor => { + let data_len = frame_buff.w * frame_buff.h * 4; + // cursor.data + mask + let mask_len = (frame_buff.w + 8 - 1) / 8 * frame_buff.h; + self.read_msg(buf, (data_len + mask_len) as usize)?; + buf.drain(..(data_len + mask_len) as usize); + } + EncodingType::EncodingAlphaCursor => { + let data_len = frame_buff.w * frame_buff.h * 4; + // EncodingType + cursor.data + self.read_msg(buf, (4 + data_len) as usize)?; + buf.drain(..(4 + data_len) as usize); + } + _ => { + assert!( + false, + "unsupport event type from client: {}", + frame_buff.enc + ); + } + } + Ok(()) + } + + fn parse_raw_image_data( + &mut self, + pf: RfbPixelFormat, + frame_buff: RfbFrameBuff, + buf: &mut Vec, + ) -> Result<()> { + let message_len: usize = + frame_buff.w as usize * frame_buff.h as usize * (pf.bit_per_pixel as usize / 8); + println!("Total bytes of image data: {:?}", message_len); + self.read_msg(buf, message_len as usize)?; + buf.drain(..message_len); + Ok(()) + } + + fn parse_hextile_image_data( + &mut self, + pf: RfbPixelFormat, + frame_buff: RfbFrameBuff, + buf: &mut Vec, + ) -> Result<()> { + let bytes_per_pixel: usize = (pf.bit_per_pixel / 8) as usize; + let mut total_received: usize = 0; + for j in (0..frame_buff.h).step_by(HEXTILE_BLOCK_SIZE) { + for i in (0..frame_buff.w).step_by(HEXTILE_BLOCK_SIZE) { + self.read_msg(buf, 1)?; + let flag = buf[0]; + buf.drain(..1); + total_received += 1; + if flag & RAW != 0 { + let w = cmp::min(HEXTILE_BLOCK_SIZE as u16, frame_buff.w - i); + let h = cmp::min(HEXTILE_BLOCK_SIZE as u16, frame_buff.h - j); + let expect = w as usize * h as usize * bytes_per_pixel; + self.read_msg(buf, expect)?; + total_received += expect; + } else { + // Background colour. + if flag & BACKGROUND_SPECIFIC != 0 { + self.read_msg(buf, bytes_per_pixel)?; + total_received += bytes_per_pixel; + buf.drain(..bytes_per_pixel); + } + // Foreground colour. + if flag & FOREGROUND_SPECIFIC != 0 { + self.read_msg(buf, bytes_per_pixel)?; + total_received += bytes_per_pixel; + buf.drain(..bytes_per_pixel); + } + + if flag & ANY_SUBRECTS != 0 { + self.read_msg(buf, 1)?; + total_received += 1; + let num_tiles = buf[0] as usize; + buf.drain(..1); + let expect = match flag & SUBRECTS_COLOURED == 0 { + true => num_tiles * 2, + false => num_tiles * (bytes_per_pixel + 2), + }; + self.read_msg(buf, expect)?; + total_received += expect; + buf.drain(..expect); + } + } + } + } + println!("Total bytes encoded by Hextile: {:?}", total_received); + Ok(()) + } + + pub fn disconnect(&mut self) -> Result<()> { + self.stream.shutdown(Shutdown::Both)?; + Ok(()) + } +} + +/// Create a new vnc client. +/// +/// # Arguments +/// +/// * `port` - Local port listened by vnc server. +pub fn create_new_client(port: u16) -> Result { + let port = port + RFB_PORT_OFFSET; + let addrs = [SocketAddr::from(([127, 0, 0, 1], port))]; + let stream = TcpStream::connect(&addrs[..]).unwrap(); + stream + .set_nonblocking(true) + .expect("set nonblocking failed"); + stream + .set_read_timeout(Some(time::Duration::from_millis(READ_TIME_OUT))) + .unwrap(); + let stream_clone = stream.try_clone().expect("clone failed..."); + let io_channel = Rc::new(RefCell::new(IoChannel::new(stream_clone))); + let mut vnc_client = VncClient::new(stream, io_channel); + // Register epoll event. + let event = EpollEvent::new( + EventSet::READ_HANG_UP | EventSet::IN, + vnc_client.stream.as_raw_fd() as u64, + ); + vnc_client.epoll_ctl(event)?; + Ok(vnc_client) +} + +pub struct TestDemoGpuDevice { + pub pci_dev: TestPciDev, + pub bar_addr: PCIBarAddr, + bar_idx: u8, + allocator: Rc>, +} + +impl TestDemoGpuDevice { + pub fn new(pci_bus: Rc>, allocator: Rc>) -> Self { + Self { + pci_dev: TestPciDev::new(pci_bus), + bar_addr: 0, + bar_idx: 0, + allocator, + } + } + + /// Send the deactive event to demo gpu. + pub fn deactive(&mut self) { + let cmd = TestGpuCmd { + event_type: GpuEvent::Deactive, + ..Default::default() + }; + self.do_gpu_event(&cmd); + } + + /// Replace the surface of the display. + /// The width and height corresponding the width and height of the suface. + pub fn replace_surface(&mut self, width: u32, height: u32, pixman_format: u32) { + let cmd = TestGpuCmd { + event_type: GpuEvent::ReplaceSurface, + w: width, + h: height, + data_len: pixman_format, + ..Default::default() + }; + self.do_gpu_event(&cmd); + } + + /// Update the cursor image for display. + pub fn replace_cursor( + &mut self, + width: u32, + height: u32, + hot_x: u32, + hot_y: u32, + mouse_data: u32, + ) { + let cmd = TestGpuCmd { + event_type: GpuEvent::ReplaceCursor, + x: hot_x, + y: hot_y, + w: width, + h: height, + data_len: mouse_data, + }; + self.do_gpu_event(&cmd); + } + + /// Change the pixel data of the specified area, + /// (x, y, w, h) represents the specific area on the image. + pub fn update_image_area(&mut self, x: u32, y: u32, w: u32, h: u32) { + let cmd = TestGpuCmd { + event_type: GpuEvent::GraphicUpdateArea, + x, + y, + w, + h, + ..Default::default() + }; + self.do_gpu_event(&cmd); + } + + /// Notify VNC that the specific area of pixel has been updated. + pub fn set_area_dirty(&mut self, x: u32, y: u32, w: u32, h: u32) { + let cmd = TestGpuCmd { + event_type: GpuEvent::GraphicUpdateDirty, + x, + y, + w, + h, + ..Default::default() + }; + self.do_gpu_event(&cmd); + } + + /// Send a gpu cmd. + pub fn do_gpu_event(&mut self, cmd: &TestGpuCmd) { + // Malloc a memory, and write the data in this addr. + let addr = self.allocator.borrow_mut().alloc(21); + let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); + test_state.borrow_mut().writeb(addr, cmd.event_type as u8); + test_state.borrow_mut().writel(addr + 1, cmd.x); + test_state.borrow_mut().writel(addr + 5, cmd.y); + test_state.borrow_mut().writel(addr + 9, cmd.w); + test_state.borrow_mut().writel(addr + 13, cmd.h); + test_state.borrow_mut().writel(addr + 17, cmd.data_len); + // Write to specific address. + self.pci_dev.io_writeq(self.bar_addr, 0 as u64, addr); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + println!("cmd : {:?}", cmd); + } + + pub fn set_devfn(&mut self, devfn: u32) { + self.pci_dev.devfn = devfn; + } + + pub fn find_pci_device(&mut self, devfn: u32) -> bool { + self.set_devfn(devfn); + if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { + return false; + } + true + } + + pub fn init(&mut self, pci_slot: u8) { + let devfn = (pci_slot << 3) as u32; + assert!(self.find_pci_device(devfn)); + + self.pci_dev.enable(); + self.bar_addr = self.pci_dev.io_map(self.bar_idx); + } +} + +pub struct TestDemoInputDevice { + pub pci_dev: TestPciDev, + pub bar_addr: PCIBarAddr, + bar_idx: u8, + mem_addr: u64, + allocator: Rc>, +} + +impl TestDemoInputDevice { + pub fn new(pci_bus: Rc>, allocator: Rc>) -> Self { + Self { + pci_dev: TestPciDev::new(pci_bus), + bar_addr: 0, + bar_idx: 0, + mem_addr: 0, + allocator, + } + } + + /// Alloc a memory space, and write the address to the input device configuration space. + pub fn activate(&mut self) { + let addr = self.allocator.borrow_mut().alloc(100); + self.mem_addr = addr; + self.pci_dev.io_writeq(self.bar_addr, 0, addr) + } + + /// Read an input event from a memory. + pub fn read_input_event(&mut self) -> InputMessage { + let addr = self.mem_addr; + let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); + + let mut msg = InputMessage::default(); + msg.event_type = InputEvent::from(test_state.borrow_mut().readb(addr)); + msg.keycode = test_state.borrow_mut().readw(addr + 1); + msg.down = test_state.borrow_mut().readb(addr + 3); + msg.button = test_state.borrow_mut().readl(addr + 4); + msg.x = test_state.borrow_mut().readl(addr + 8); + msg.y = test_state.borrow_mut().readl(addr + 12); + println!("kbd mouse event: {:?}", msg); + msg + } + + pub fn set_devfn(&mut self, devfn: u32) { + self.pci_dev.devfn = devfn; + } + + pub fn find_pci_device(&mut self, devfn: u32) -> bool { + self.set_devfn(devfn); + if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { + return false; + } + true + } + + pub fn init(&mut self, pci_slot: u8) { + let devfn = (pci_slot << 3) as u32; + assert!(self.find_pci_device(devfn)); + + self.pci_dev.enable(); + self.bar_addr = self.pci_dev.io_map(self.bar_idx); + self.activate(); + } +} + +pub struct DemoGpuConfig { + pub pci_slot: u8, + pub id: String, +} + +pub struct InputConfig { + pub pci_slot: u8, + pub id: String, +} + +/// Environment Setup. +pub fn set_up( + gpu_conf: Vec, + input_conf: InputConfig, + port: u16, +) -> ( + Vec>>, + Rc>, + Rc>, +) { + let mut args: Vec = Vec::new(); + // vm args. + let vm_args = String::from("-machine virt"); + let vm_args: Vec<&str> = vm_args[..].split(' ').collect(); + let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut vm_args); + // Log. + let vm_args = String::from("-D /var/log/vnc_test.log"); + let vm_args: Vec<&str> = vm_args[..].split(' ').collect(); + let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut vm_args); + // Demo GPU Device. + for conf in &gpu_conf { + let gpu_args = format!( + "-device {},bus=pcie.0,addr={}.0,id={},bar_num=3,device_type=demo-gpu,bar_size=4096", + "pcie-demo-dev", conf.pci_slot, conf.id, + ); + let gpu_args: Vec<&str> = gpu_args[..].split(' ').collect(); + let mut gpu_args = gpu_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut gpu_args); + } + // Demo Input Device. + let input_args = format!( + "-device {},bus=pcie.0,addr={}.0,id={},bar_num=3,device_type=demo-input,bar_size=4096", + "pcie-demo-dev", input_conf.pci_slot, input_conf.id, + ); + let input_args: Vec<&str> = input_args[..].split(' ').collect(); + let mut input_args = input_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut input_args); + + // VNC server + let vnc_args = format!("-vnc 0.0.0.0:{}", port); + let vnc_args: Vec<&str> = vnc_args[..].split(' ').collect(); + let mut vnc_args = vnc_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut vnc_args); + let args = args.iter().map(AsRef::as_ref).collect(); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let mut gpu_lists: Vec>> = Vec::new(); + for conf in gpu_conf { + let demo_gpu = Rc::new(RefCell::new(TestDemoGpuDevice::new( + machine.pci_bus.clone(), + allocator.clone(), + ))); + demo_gpu.borrow_mut().init(conf.pci_slot); + demo_gpu + .borrow_mut() + .replace_surface(640, 480, PIXMAN_A8B8G8R8); + gpu_lists.push(demo_gpu); + } + + let input = Rc::new(RefCell::new(TestDemoInputDevice::new( + machine.pci_bus.clone(), + allocator, + ))); + input.borrow_mut().init(input_conf.pci_slot); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + (gpu_lists, input, test_state) +} + +pub fn tear_down( + gpu_list: Vec>>, + _input: Rc>, + test_state: Rc>, +) { + for demo_gpu in gpu_list { + demo_gpu.borrow_mut().deactive(); + } + test_state.borrow_mut().stop(); +} + +/// Key mapping. +/// Vnc client send keysym -> vnc server +/// Vnc server send keycode -> usb. +pub const KEYEVENTLIST: [(&str, u16, u16); 41] = [ + ("space", 0x0020, 0x0039), + ("0", 0x0030, 0x000b), + ("1", 0x0031, 0x0002), + ("2", 0x0032, 0x0003), + ("3", 0x0033, 0x0004), + ("4", 0x0034, 0x0005), + ("5", 0x0035, 0x0006), + ("6", 0x0036, 0x0007), + ("7", 0x0037, 0x0008), + ("8", 0x0038, 0x0009), + ("9", 0x0039, 0x000a), + ("a", 0x0061, 0x001e), + ("b", 0x0062, 0x0030), + ("c", 0x0063, 0x002e), + ("d", 0x0064, 0x0020), + ("e", 0x0065, 0x0012), + ("f", 0x0066, 0x0021), + ("g", 0x0067, 0x0022), + ("h", 0x0068, 0x0023), + ("i", 0x0069, 0x0017), + ("j", 0x006a, 0x0024), + ("k", 0x006b, 0x0025), + ("l", 0x006c, 0x0026), + ("m", 0x006d, 0x0032), + ("n", 0x006e, 0x0031), + ("o", 0x006f, 0x0018), + ("p", 0x0070, 0x0019), + ("q", 0x0071, 0x0010), + ("r", 0x0072, 0x0013), + ("s", 0x0073, 0x001f), + ("t", 0x0074, 0x0014), + ("u", 0x0075, 0x0016), + ("v", 0x0076, 0x002f), + ("w", 0x0077, 0x0011), + ("x", 0x0078, 0x002d), + ("y", 0x0079, 0x0015), + ("z", 0x007a, 0x002c), + ("ctl", 0xFFE3, 0x001d), + ("alt", 0xFFE9, 0x0038), + ("caps_lock", 0xFFE5, 0x003a), + ("num_lock", 0xFF7F, 0x0045), +]; + +// Event type of Point. +pub const INPUT_POINT_LEFT: u8 = 0x01; +pub const INPUT_POINT_MIDDLE: u8 = 0x02; +pub const INPUT_POINT_RIGHT: u8 = 0x04; + +// Coordinates of pointer movement on the desktop. +pub const POINTEVENTLIST: [(u8, u16, u16); 16] = [ + (INPUT_POINT_LEFT, 0x0070, 0x0002), + (INPUT_POINT_RIGHT, 0x0000, 0x0005), + (INPUT_POINT_LEFT, 0x0005, 0x0005), + (INPUT_POINT_RIGHT, 0x0005, 0x0136), + (INPUT_POINT_MIDDLE, 0x0005, 0x0011), + (INPUT_POINT_LEFT, 0x0005, 0x0017), + (INPUT_POINT_RIGHT, 0x00aa, 0x0016), + (INPUT_POINT_LEFT, 0x0013, 0x0018), + (INPUT_POINT_RIGHT, 0x000b, 0x001b), + (INPUT_POINT_MIDDLE, 0x0078, 0x001b), + (INPUT_POINT_LEFT, 0x0011, 0x001b), + (INPUT_POINT_LEFT, 0x0011, 0x00c8), + (INPUT_POINT_MIDDLE, 0x0043, 0x00d2), + (INPUT_POINT_LEFT, 0x006d, 0x00c8), + (INPUT_POINT_MIDDLE, 0x00be, 0x00c8), + (INPUT_POINT_RIGHT, 0x00be, 0x0122), +]; + +pub const TEST_CLIENT_RAND_MSG: [u8; 256] = [ + 0x67, 0xa5, 0x3a, 0xeb, 0x4e, 0x30, 0xb0, 0x8d, 0xd7, 0x5e, 0x63, 0x3a, 0xdb, 0xb5, 0xd6, 0x51, + 0x54, 0x66, 0xb7, 0x38, 0xe3, 0xea, 0x89, 0x3b, 0xfa, 0x64, 0xfd, 0xed, 0xc7, 0xe5, 0xbb, 0x4d, + 0x60, 0x0e, 0x8c, 0xc8, 0x6d, 0x97, 0x1b, 0x17, 0xe8, 0x4c, 0x9a, 0xfa, 0x28, 0x03, 0xdb, 0x03, + 0xb5, 0x7f, 0xf1, 0x45, 0x5c, 0xb8, 0x8b, 0xe9, 0x1b, 0x62, 0xe3, 0xb6, 0x7c, 0x94, 0x96, 0xa1, + 0xbf, 0xd0, 0xc9, 0xde, 0x12, 0x3e, 0x21, 0x8a, 0x14, 0x0b, 0x3e, 0x4f, 0x9e, 0xc6, 0x92, 0xb3, + 0xed, 0x5b, 0x71, 0xa3, 0x88, 0x8e, 0x0b, 0x63, 0x66, 0x66, 0xd9, 0xf6, 0xfb, 0xa9, 0x2d, 0x98, + 0xea, 0x6b, 0x05, 0xe3, 0x21, 0xcf, 0x4a, 0xc9, 0x76, 0x1e, 0x6d, 0x00, 0xde, 0x0b, 0x9d, 0xa5, + 0xd0, 0xd1, 0xe4, 0x24, 0x92, 0x19, 0xb8, 0x66, 0xde, 0x6d, 0x1d, 0x98, 0x91, 0x63, 0xa7, 0x03, + 0xdf, 0xbc, 0x98, 0x56, 0x04, 0x8f, 0xf6, 0x92, 0xfe, 0xe5, 0x3b, 0xaf, 0x2e, 0x10, 0x85, 0x94, + 0xa9, 0xc1, 0xed, 0x0a, 0x39, 0x4a, 0xe9, 0x8a, 0x52, 0xa9, 0x8d, 0x13, 0x40, 0x28, 0x21, 0x43, + 0x8b, 0x75, 0x01, 0xf1, 0xf9, 0xde, 0x6e, 0xc6, 0x2c, 0xb0, 0x42, 0x78, 0x2b, 0xf8, 0x34, 0x24, + 0x7a, 0x71, 0xc7, 0x94, 0xac, 0xa8, 0x7d, 0x9b, 0x85, 0xfe, 0x47, 0xc9, 0xd4, 0x70, 0x07, 0x7a, + 0x63, 0x07, 0xb8, 0x83, 0xcb, 0xee, 0x1a, 0x24, 0x58, 0xb3, 0xc3, 0x48, 0xb8, 0xa2, 0x01, 0x8c, + 0x20, 0x3a, 0xe0, 0xe6, 0xa7, 0xf8, 0x5b, 0x1a, 0xd8, 0xfe, 0x7f, 0x4b, 0x50, 0x14, 0x4d, 0xe5, + 0x6f, 0x6f, 0x2f, 0xfa, 0xbb, 0x95, 0x85, 0xfc, 0x33, 0xe7, 0xcf, 0x0d, 0xe1, 0x28, 0x0e, 0xc0, + 0xba, 0xe8, 0xbd, 0x23, 0xc3, 0x7b, 0x25, 0x11, 0xf5, 0x30, 0x30, 0x5f, 0xb8, 0x57, 0xfe, 0xd5, +]; diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs new file mode 100644 index 000000000..f9fcc494b --- /dev/null +++ b/tests/mod_test/tests/vnc_test.rs @@ -0,0 +1,745 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::Result; +use mod_test::{ + libdriver::vnc::{ + create_new_client, set_up, tear_down, DemoGpuConfig, EncodingType, InputConfig, + RfbPixelFormat, RfbServerMsg, TestAuthType, UpdateState, KEYEVENTLIST, PIXMAN_A1, + PIXMAN_A8B8G8R8, PIXMAN_R8G8B8, PIXMAN_X2R10G10B10, PIXMAN_YUY2, POINTEVENTLIST, + REFRESH_TIME_INTERVAL, TEST_CLIENT_RAND_MSG, + }, + libtest::TestState, +}; +use serde_json::Value; +use std::{cell::RefCell, rc::Rc}; +use vmm_sys_util::epoll::EventSet; + +fn qmp_query_vnc(test_state: Rc>) -> Value { + let str = "{\"execute\": \"query-vnc\"}".to_string(); + test_state.borrow_mut().qmp(&str) +} + +/// Brief: +/// 1. When received a framebuffer request from the client, the vnc server can +/// send the pixel which is changed to the client. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client set pixel format Raw. +/// 3. VNC client send framebuffer request + Incremental +/// + The Demo GPU device changes the pixel and update image -> expect 1. +/// 4. The Demo GPU device changes the pixel and update image +/// + VNC client send framebuffer request + Incremental -> expect 1. +/// 5. VNC client set pixel format Raw. +/// 6. VNC client send framebuffer request + Incremental +/// + The Demo GPU device changes the pixel and update image -> expect 1. +/// ExpectOutput +/// 1. VNC client can receive image updates event, and the image format meets expectations. +#[test] +fn test_set_area_dirty() { + let port: u16 = 0; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu = gpu_list[0].clone(); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + + // Encoding -> Raw. + // Demo update image -> VNC client send update request. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingRaw)) + .is_ok()); + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) + .is_ok()); + demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); + demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); + + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res + .unwrap() + .contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); + + // Encoding -> Raw + // VNC client send update request -> Demo update image. + demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); + demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); + assert!(vnc_client + .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res + .unwrap() + .contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); + + // Encoding -> Hextile. + // Demo update image -> VNC client send update request. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingHextile)) + .is_ok()); + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) + .is_ok()); + demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); + demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); + + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingHextile + ))); + + assert!(vnc_client.disconnect().is_ok()); + + tear_down(gpu_list, input, test_state); +} + +/// Brief: +/// 1. VNC Server can update cursor image. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client setting feature of EncodingRichCursor. +/// 3. Demo GPU update the cursor image of VNC server -> expect 1. +/// 4. VNC client setting feature of EncodingAlphaCursor. +/// 5. Demo GPU update the cursor image of VNC server -> expect 1. +/// 6. VNC client setting feature of EncodingRichCursor. +/// 7. Demo GPU update the abnormal cursor image of VNC server -> expect 2. +/// 8. VNC client setting feature of EncodingAlphaCursor. +/// 9. Demo GPU update the abnormal cursor image of VNC server -> expect 2. +/// ExpectOutput: +/// 1. The client receives the cursor image, and the format meets expect. +/// 2. The state of VNC client and server are normal, and the next normal connection will not be effect. +#[test] +fn test_send_cursor_image() { + let port: u16 = 1; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu = gpu_list[0].clone(); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + assert!(vnc_client + .test_setup_encodings(Some(1), Some(EncodingType::EncodingRichCursor)) + .is_ok()); + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + demo_gpu + .borrow_mut() + .replace_cursor(64, 64, 16, 16, 64 * 64 * 4); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingRichCursor + ))); + + assert!(vnc_client + .test_setup_encodings(Some(1), Some(EncodingType::EncodingAlphaCursor)) + .is_ok()); + demo_gpu + .borrow_mut() + .replace_cursor(64, 64, 16, 16, 64 * 64 * 4); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingAlphaCursor + ))); + + assert!(vnc_client + .test_setup_encodings(Some(1), Some(EncodingType::EncodingRichCursor)) + .is_ok()); + demo_gpu.borrow_mut().replace_cursor(64, 64, 16, 16, 0); + assert!(vnc_client + .test_setup_encodings(Some(1), Some(EncodingType::EncodingAlphaCursor)) + .is_ok()); + demo_gpu.borrow_mut().replace_cursor(64, 64, 16, 16, 0); + assert!(vnc_client.disconnect().is_ok()); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + assert!(vnc_client.disconnect().is_ok()); + + tear_down(gpu_list, input, test_state); +} + +/// Brief: +/// When the surface size of VNC server is changed, server will inform +/// clients which has desktop_resize feature that the desktop size has been changed. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client setting feature of EncodingDesktopresize. +/// 3. Demo GPU replace the surface of VNC server. -> expect 1. +/// 4. VNC client setting feature of Raw. +/// 5. Demo GPU replace the surface of VNC server. -> expect 2. +/// ExpectOutput +/// 1. VNC client received a desktop resize request from VNC server. +/// 2. VNC client not received any desktop resize request from VNC server. +#[test] +fn test_desktop_resize() { + let port: u16 = 2; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu = gpu_list[0].clone(); + + demo_gpu + .borrow_mut() + .replace_surface(640, 480, PIXMAN_A8B8G8R8); + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingDesktopresize)) + .is_ok()); + demo_gpu + .borrow_mut() + .replace_surface(1920, 1080, PIXMAN_A8B8G8R8); + + let pf = RfbPixelFormat::new(8, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingDesktopresize + ))); + + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingRaw)) + .is_ok()); + demo_gpu + .borrow_mut() + .replace_surface(640, 480, PIXMAN_A8B8G8R8); + let pf = RfbPixelFormat::new(8, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + let res = vnc_client.test_recv_server_data(pf); + assert!(!res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingDesktopresize + ))); + + assert!(vnc_client.disconnect().is_ok()); + tear_down(gpu_list, input, test_state); +} + +/// Brief: +/// The VNC server can receive image update request and return image data to client. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client set pixel format. +/// 3. VNC client send framebuffer request + NotIncremental. +/// 4. VNC client check the image data from VNC server. +/// Situation: +/// 1. Pixel format: Raw + bit_per_pixel=32 + true_color_flag=1 -> expect 1. +/// 2. Pixel format: Raw + bit_per_pixel=16 + true_color_flag=1 -> expect 1. +/// 3. Pixel format: Raw + bit_per_pixel=8 + true_color_flag=0. -> expect 2. +/// 4. Pixel format: Hextile + bit_per_pixel=32 + true_color_flag=1 -> expect 1. +/// 5. Pixel format: Hextile + bit_per_pixel=8 + true_color_flag=1 -> expect 1. +/// 6. Pixel format: Hextile + bit_per_pixel=8 + true_color_flag=2 -> expect 2. +/// ExpectOutput: +/// 1. The image format meets expectations. +/// 2. The Image format meets expectations, VNC client receives the messages of set +/// color map information from VNC server. +#[test] +fn test_set_pixel_format() { + let port: u16 = 3; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingRaw)) + .is_ok()); + assert!(vnc_client.stream_read_to_end().is_ok()); + + // Raw + bit_per_pixel=32 + true_color_flag=1. + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res + .unwrap() + .contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); + + // Raw + bit_per_pixel=16 + true_color_flag=1. + let pf = RfbPixelFormat::new(16, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res + .unwrap() + .contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); + + // Raw + bit_per_pixel=8 + true_color_flag=0. + let pf = RfbPixelFormat::new(8, 8, 0_u8, 0_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf.clone()); + assert!(res.is_ok()); + let res = res.unwrap(); + assert!(res.contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); + assert!(res.contains(&( + RfbServerMsg::SetColourMapEntries, + EncodingType::EncodingInvalid + ))); + + // Hextile + bit_per_pixel=32 + true_color_flag=1. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingHextile)) + .is_ok()); + assert!(vnc_client.stream_read_to_end().is_ok()); + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingHextile + ))); + + // Hextile + bit_per_pixel=8 + true_color_flag=1. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingHextile)) + .is_ok()); + assert!(vnc_client.stream_read_to_end().is_ok()); + let pf = RfbPixelFormat::new(8, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingHextile + ))); + + // Hextile + bit_per_pixel=8 + true_color_flag=0. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingHextile)) + .is_ok()); + assert!(vnc_client.stream_read_to_end().is_ok()); + let pf = RfbPixelFormat::new(8, 8, 0_u8, 0_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) + .is_ok()); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + let res = res.unwrap(); + assert!(res.contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingHextile + ))); + assert!(res.contains(&( + RfbServerMsg::SetColourMapEntries, + EncodingType::EncodingInvalid + ))); + assert!(vnc_client.disconnect().is_ok()); + + tear_down(gpu_list, input, test_state); +} + +/// Brief: +/// The VNC server can receive keyboard and pointer events. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep +/// 1. VNC client connect to server. +/// 2. VNC client send key event -> expect 1. +/// 3. VNC client send pointer event -> expect 2. +/// ExpectOutput: +/// 1. VNC server received the keyboard event, the observed key value in demo keyboard device meets the expectation. +/// 2. VNC server received the pointer event, the observed coordinate in demo pointer device has been changed. +#[test] +fn test_vnc_kbd_mouse() { + let port: u16 = 4; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu = gpu_list[0].clone(); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + // Key event. + for &(name, keysym, keycode) in KEYEVENTLIST.iter() { + assert!(vnc_client.test_key_event(0, keysym as u32).is_ok()); + let msg = input.borrow_mut().read_input_event(); + println!("key {:?}: {:?}", name, msg); + assert_eq!(msg.keycode, keycode); + assert_eq!(msg.down, 0); + assert!(vnc_client.test_key_event(1, keysym as u32).is_ok()); + let msg = input.borrow_mut().read_input_event(); + println!("key {:?}: {:?}", name, msg); + assert_eq!(msg.keycode, keycode); + assert_eq!(msg.down, 1); + } + + // Pointer event. + let (button_mask, x, y) = POINTEVENTLIST[0]; + assert!(vnc_client.test_point_event(button_mask, x, y).is_ok()); + let mut old_msg = input.borrow_mut().read_input_event(); + for &(button_mask, x, y) in POINTEVENTLIST[1..].iter() { + assert!(vnc_client.test_point_event(button_mask, x, y).is_ok()); + let msg = input.borrow_mut().read_input_event(); + // After sending the pointer event, the coordinate shoule be changed + assert!(!(old_msg.button == msg.button && old_msg.x == msg.x && old_msg.y == msg.y)); + old_msg = msg; + println!("msg: {:?}", msg); + } + assert!(vnc_client.disconnect().is_ok()); + demo_gpu.borrow_mut().deactive(); + tear_down(gpu_list, input, test_state); +} + +/// Brief: +/// The display device can be switched through Ctl+Alt+Num on VNC client. +/// Preparation: +/// 1. Configure a demo pointer device and two test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. First demo gpu device create an image with size of 640 * 480. +/// 4. Second demo gpu device create an image with size of 1920 * 1080. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client setting feature of EncodingDesktopresize. +/// 3. VNC client send the key event of Ctl+Alt+Num -> expect 1. +/// ExpectOutput: +/// 1. The activate display device is be changed, and the VNC client receive the message of desktopresize. +#[test] +fn test_switch_display_device() { + let port: u16 = 5; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu_1".to_string(), + }; + gpu_list.push(gpu_conf); + let gpu_conf = DemoGpuConfig { + pci_slot: 4, + id: "demo-pci-gpu_2".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 5, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu_1 = gpu_list[0].clone(); + let demo_gpu_2 = gpu_list[1].clone(); + demo_gpu_1 + .borrow_mut() + .replace_surface(640, 480, PIXMAN_A8B8G8R8); + demo_gpu_2 + .borrow_mut() + .replace_surface(1920, 1080, PIXMAN_A8B8G8R8); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingDesktopresize)) + .is_ok()); + + // Ctl + Alt + 2. + assert!(vnc_client.test_key_event(1, 0xffe3).is_ok()); + assert!(vnc_client.test_key_event(1, 0xffe9).is_ok()); + assert!(vnc_client.test_key_event(1, 0x32).is_ok()); + + let pf = RfbPixelFormat::new(8, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingDesktopresize + ))); + + tear_down(gpu_list, input, test_state); +} + +/// Brief: +/// Test possible exceptions during image update. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. Demo GPU do some operation. +/// Abnormal Situation: +/// 1. The area to set dirty is out of range -> expect 1. +/// 2. The image size exceeds value -> expect 1. +/// 3. Switch different pixman formats -> expect 1. +/// ExpectOutput: +/// 1. The status of VNC server status is normal and can handle the next connect request. +#[test] +fn test_update_image_abnormal() { + let port: u16 = 6; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu = gpu_list[0].clone(); + + demo_gpu + .borrow_mut() + .replace_surface(640, 480, PIXMAN_A8B8G8R8); + demo_gpu.borrow_mut().set_area_dirty(0, 0, 65535, 65535); + demo_gpu + .borrow_mut() + .replace_surface(65535, 65535, PIXMAN_A8B8G8R8); + demo_gpu + .borrow_mut() + .replace_surface(640, 480, PIXMAN_X2R10G10B10); + demo_gpu + .borrow_mut() + .replace_surface(1080, 720, PIXMAN_R8G8B8); + demo_gpu.borrow_mut().replace_surface(640, 480, PIXMAN_A1); + demo_gpu + .borrow_mut() + .replace_surface(1080, 720, PIXMAN_YUY2); + demo_gpu + .borrow_mut() + .replace_surface(640, 480, PIXMAN_A8B8G8R8); + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + let value = qmp_query_vnc(test_state.clone()); + let client_num = value["return"]["clients"].as_array().unwrap().len(); + assert!(client_num >= 1); + assert!(vnc_client.disconnect().is_ok()); + tear_down(gpu_list, input, test_state); +} + +fn test_rfb_version_abnormal(port: u16) -> Result<()> { + let mut buf: Vec = Vec::new(); + let mut vnc_client = create_new_client(port).unwrap(); + println!("Connect to server."); + assert!(vnc_client.read_msg(&mut buf, 12).is_ok()); + assert_eq!(buf[..12].to_vec(), "RFB 003.008\n".as_bytes().to_vec()); + println!("Client Rfb version: RFB 003.010"); + assert!(vnc_client + .write_msg(&"RFB 003.010\n".as_bytes().to_vec()) + .is_ok()); + buf.drain(..12); + // VNC server closed connection. + let res = vnc_client.epoll_wait(EventSet::READ_HANG_UP); + assert!(res.is_ok()); + assert!(res.unwrap() > 0); + assert_ne!( + vnc_client.ready_events[0].events() & EventSet::READ_HANG_UP.bits(), + 0 + ); + assert!(vnc_client.disconnect().is_ok()); + + Ok(()) +} + +fn test_unsupport_sec_type(port: u16) -> Result<()> { + let mut buf: Vec = Vec::new(); + let mut vnc_client = create_new_client(port).unwrap(); + println!("Connect to server."); + assert!(vnc_client.read_msg(&mut buf, 12).is_ok()); + assert_eq!(buf[..12].to_vec(), "RFB 003.008\n".as_bytes().to_vec()); + assert!(vnc_client + .write_msg(&"RFB 003.008\n".as_bytes().to_vec()) + .is_ok()); + buf.drain(..12); + + // Step 2: Auth num is 1. + assert!(vnc_client.read_msg(&mut buf, 1).is_ok()); + let auth_num = buf[0]; + assert!(auth_num > 0); + buf.drain(..1); + assert!(vnc_client.read_msg(&mut buf, auth_num as usize).is_ok()); + buf.drain(..auth_num as usize); + assert!(vnc_client + .write_msg(&(TestAuthType::Invalid as u8).to_be_bytes().to_vec()) + .is_ok()); + // VNC server close the connection. + let res = vnc_client.epoll_wait(EventSet::READ_HANG_UP); + assert!(res.is_ok()); + assert!(res.unwrap() > 0); + assert_ne!( + vnc_client.ready_events[0].events() & EventSet::READ_HANG_UP.bits(), + 0 + ); + assert!(vnc_client.disconnect().is_ok()); + + Ok(()) +} + +fn test_set_pixel_format_abnormal(port: u16) -> Result<()> { + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + let pf = RfbPixelFormat::new(17, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + + // VNC server close the connection. + let res = vnc_client.epoll_wait(EventSet::READ_HANG_UP)?; + assert!(res > 0); + assert_ne!( + vnc_client.ready_events[0].events() & EventSet::READ_HANG_UP.bits(), + 0 + ); + + assert!(vnc_client.disconnect().is_ok()); + Ok(()) +} + +fn test_client_rand_bytes(port: u16) -> Result<()> { + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + let mut buf = TEST_CLIENT_RAND_MSG; + vnc_client.write_msg(&mut buf)?; + assert!(vnc_client.disconnect().is_ok()); + Ok(()) +} + +/// Brief: +/// Test possible exceptions during RFB protocol connection. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client set pixel format. +/// 3. VNC client send framebuffer request + NotIncremental. +/// 4. VNC client check the image data from VNC server. +/// Abnormal Situation: +/// 1. Unsupport RFB version -> expect 1 + 2. +/// 2. Unsupport security type -> expect 1 + 2. +/// 3. The message of set pixel formal is abnormal -> expect 1 + 2. +/// 4. Send the rand bytes from the client -> expect 2. +/// ExpectOutput: +/// 1. VNC server close the connection. +/// 2. The status of VNC server status is normal and can handle the next connect request. +#[test] +fn test_rfb_abnormal() { + let port: u16 = 7; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + + assert!(test_rfb_version_abnormal(port).is_ok()); + assert!(test_unsupport_sec_type(port).is_ok()); + assert!(test_set_pixel_format_abnormal(port).is_ok()); + assert!(test_client_rand_bytes(port).is_ok()); + + let mut vnc_client = create_new_client(port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + let value = qmp_query_vnc(test_state.clone()); + let client_num = value["return"]["clients"].as_array().unwrap().len(); + assert!(client_num >= 1); + assert!(vnc_client.disconnect().is_ok()); + + tear_down(gpu_list, input, test_state); +} -- Gitee From 5962caf47109ff79fd6bb9d7c688dde3c6a50633 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 27 Feb 2023 23:31:31 +0800 Subject: [PATCH 0836/2187] mst: Delete parameter size of memwrite As we can get it from data itself. Signed-off-by: Keqian Zhu --- tests/mod_test/src/libdriver/pci.rs | 8 +-- tests/mod_test/src/libdriver/pci_bus.rs | 14 +++-- tests/mod_test/src/libdriver/virtio.rs | 2 +- tests/mod_test/src/libdriver/virtio_block.rs | 18 +++---- tests/mod_test/src/libtest.rs | 9 +++- tests/mod_test/tests/console_test.rs | 24 ++------- tests/mod_test/tests/memory_test.rs | 57 ++++++-------------- tests/mod_test/tests/net_test.rs | 23 +++----- 8 files changed, 52 insertions(+), 103 deletions(-) diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 05effae41..23eb08cc7 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -211,26 +211,26 @@ impl TestPciDev { pub fn io_writeb(&self, bar_addr: PCIBarAddr, offset: u64, value: u8) { let value_buf = value.to_le_bytes().to_vec(); let pci_bus = self.pci_bus.borrow_mut(); - pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf); } pub fn io_writew(&self, bar_addr: PCIBarAddr, offset: u64, value: u16) { let value_buf = value.to_le_bytes().to_vec(); let pci_bus = self.pci_bus.borrow_mut(); - pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf); } pub fn io_writel(&self, bar_addr: PCIBarAddr, offset: u64, value: u32) { let value_buf = value.to_le_bytes().to_vec(); let pci_bus = self.pci_bus.borrow_mut(); - pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf); } #[allow(unused)] pub fn io_writeq(&self, bar_addr: PCIBarAddr, offset: u64, value: u64) { let value_buf = value.to_le_bytes().to_vec(); let pci_bus = self.pci_bus.borrow_mut(); - pci_bus.memwrite((bar_addr + offset) as u32, &value_buf, value_buf.len()); + pci_bus.memwrite((bar_addr + offset) as u32, &value_buf); } pub fn io_map(&self, barnum: u8) -> u64 { diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 735f4dffe..1b8cf179b 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -22,7 +22,7 @@ const PCIE_ECAM_BASE: u64 = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0 pub trait PciBusOps { fn memread(&self, addr: u32, len: usize) -> Vec; - fn memwrite(&self, addr: u32, buf: &[u8], len: usize); + fn memwrite(&self, addr: u32, buf: &[u8]); fn config_readb(&self, devfn: u32, offset: u8) -> u8; fn config_readw(&self, devfn: u32, offset: u8) -> u16; @@ -63,10 +63,8 @@ impl PciBusOps for TestPciBus { self.test_state.borrow().memread(addr as u64, len as u64) } - fn memwrite(&self, addr: u32, buf: &[u8], len: usize) { - self.test_state - .borrow() - .memwrite(addr as u64, buf, len as u64); + fn memwrite(&self, addr: u32, buf: &[u8]) { + self.test_state.borrow().memwrite(addr as u64, buf); } fn config_readb(&self, devfn: u32, offset: u8) -> u8 { @@ -89,18 +87,18 @@ impl PciBusOps for TestPciBus { fn config_writeb(&self, devfn: u32, offset: u8, value: u8) { let addr = self.get_addr(devfn, offset); let buf = value.to_le_bytes(); - self.test_state.borrow().memwrite(addr, &buf, 1); + self.test_state.borrow().memwrite(addr, &buf); } fn config_writew(&self, devfn: u32, offset: u8, value: u16) { let addr = self.get_addr(devfn, offset); let buf = value.to_le_bytes(); - self.test_state.borrow().memwrite(addr, &buf, 2); + self.test_state.borrow().memwrite(addr, &buf); } fn config_writel(&self, devfn: u32, offset: u8, value: u32) { let addr = self.get_addr(devfn, offset); let buf = value.to_le_bytes(); - self.test_state.borrow().memwrite(addr, &buf, 4); + self.test_state.borrow().memwrite(addr, &buf); } } diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 0392dbcae..e39fdbed6 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -565,7 +565,7 @@ impl TestVirtQueue { let desc_elem_addr = self.desc + VRING_DESC_SIZE * self.free_head as u64; test_state .borrow() - .memwrite(desc_elem_addr, elem.as_bytes(), VRING_DESC_SIZE); + .memwrite(desc_elem_addr, elem.as_bytes()); self.free_head += 1; } } diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index cd8072aa1..bce3cb33f 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -190,19 +190,15 @@ pub fn virtio_blk_request( }; let req_bytes = req.as_bytes(); - test_state.borrow().memwrite(addr, req_bytes.as_slice(), 16); + test_state.borrow().memwrite(addr, req_bytes.as_slice()); let mut data_bytes = req.data.as_bytes().to_vec(); data_bytes.resize(data_size, 0); - test_state.borrow().memwrite( - data_addr, - data_bytes.as_slice(), - data_size.try_into().unwrap(), - ); - test_state.borrow().memwrite( - data_addr + data_size as u64, - &status.to_le_bytes(), - size_of::().try_into().unwrap(), - ); + test_state + .borrow() + .memwrite(data_addr, data_bytes.as_slice()); + test_state + .borrow() + .memwrite(data_addr + data_size as u64, &status.to_le_bytes()); addr } diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 92feb4c4c..be1888f1a 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -212,8 +212,13 @@ impl TestState { self.send_write_cmd(&cmd); } - pub fn memwrite(&self, addr: u64, data: &[u8], size: u64) { - let cmd = format!("write 0x{:x} 0x{:x} 0x{}", addr, size, hex::encode(data)); + pub fn memwrite(&self, addr: u64, data: &[u8]) { + let cmd = format!( + "write 0x{:x} 0x{:x} 0x{}", + addr, + data.len(), + hex::encode(data) + ); let buf = self.send_test_cmd(&cmd); let resp: Vec<&str> = buf.split(' ').collect(); match resp[0] { diff --git a/tests/mod_test/tests/console_test.rs b/tests/mod_test/tests/console_test.rs index 61e6f7481..b2e9a7a44 100644 --- a/tests/mod_test/tests/console_test.rs +++ b/tests/mod_test/tests/console_test.rs @@ -156,11 +156,7 @@ fn test_pty_basic() { let test_data = String::from("Test\n"); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite( - addr, - test_data.as_bytes(), - test_data.len().try_into().unwrap(), - ); + test_state.borrow().memwrite(addr, test_data.as_bytes()); let free_head = output_queue.borrow_mut().add( test_state.clone(), addr, @@ -259,11 +255,7 @@ fn test_socket_basic() { let test_data = String::from("Test\n"); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite( - addr, - test_data.as_bytes(), - test_data.len().try_into().unwrap(), - ); + test_state.borrow().memwrite(addr, test_data.as_bytes()); let free_head = output_queue.borrow_mut().add( test_state.clone(), addr, @@ -354,11 +346,7 @@ fn test_console_reset() { let test_data = String::from("Test\n"); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite( - addr, - test_data.as_bytes(), - test_data.len().try_into().unwrap(), - ); + test_state.borrow().memwrite(addr, test_data.as_bytes()); let free_head = output_queue.borrow_mut().add( test_state.clone(), addr, @@ -427,11 +415,7 @@ fn test_console_reset() { let test_data = String::from("Test\n"); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite( - addr, - test_data.as_bytes(), - test_data.len().try_into().unwrap(), - ); + test_state.borrow().memwrite(addr, test_data.as_bytes()); let free_head = output_queue.borrow_mut().add( test_state.clone(), addr, diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index c8687f9d8..de6b947d7 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -84,7 +84,7 @@ fn ram_read_write(memory_test: &MemoryTest) { memory_test .state .borrow_mut() - .memwrite(addr, str.as_bytes(), str.len() as u64); + .memwrite(addr, str.as_bytes()); let ret = memory_test .state .borrow_mut() @@ -133,10 +133,7 @@ fn io_region_read_write() { .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); let data = [0x01u8; 8]; - memory_test - .state - .borrow_mut() - .memwrite(addr, &data, std::mem::size_of::() as u64); + memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test .state .borrow_mut() @@ -164,10 +161,7 @@ fn region_priority() { let data = [0x01u8; 8]; // Ram write and read. - memory_test - .state - .borrow_mut() - .memwrite(addr, &data, std::mem::size_of::() as u64); + memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test .state .borrow_mut() @@ -177,10 +171,7 @@ fn region_priority() { // Add an overlapping region to write and read again. let qmp_cmd = format!("{{ \"execute\": \"update_region\", \"arguments\": {{ \"update_type\": \"add\", \"region_type\": \"io_region\", \"offset\": {}, \"size\": 4096, \"priority\": 99 }} }}", addr); memory_test.state.borrow_mut().qmp(&qmp_cmd); - memory_test - .state - .borrow_mut() - .memwrite(addr, &data, std::mem::size_of::() as u64); + memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test .state .borrow_mut() @@ -273,10 +264,7 @@ fn rom_device_region_readwrite() { .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"rom_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99, \"read_only_mode\": false }}"); let data = [0x01u8; 8]; - memory_test - .state - .borrow_mut() - .memwrite(addr, &data, std::mem::size_of::() as u64); + memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test .state .borrow_mut() @@ -284,11 +272,10 @@ fn rom_device_region_readwrite() { assert_eq!(ret, [0x02u8; 8]); // Write overflow - memory_test.state.borrow_mut().memwrite( - addr + PAGE_SIZE - 1, - &data, - std::mem::size_of::() as u64, - ); + memory_test + .state + .borrow_mut() + .memwrite(addr + PAGE_SIZE - 1, &data); // Read overflow let ret = memory_test .state @@ -305,10 +292,7 @@ fn rom_device_region_readwrite() { .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"rom_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 100, \"read_only_mode\": true }}"); let data = [0x01u8; 8]; - memory_test - .state - .borrow_mut() - .memwrite(addr, &data, std::mem::size_of::() as u64); + memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test .state .borrow_mut() @@ -340,10 +324,7 @@ fn ram_device_region_readwrite() { .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"add\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); let data = [0x01u8; 8]; - memory_test - .state - .borrow_mut() - .memwrite(addr, &data, std::mem::size_of::() as u64); + memory_test.state.borrow_mut().memwrite(addr, &data); let ret = memory_test .state .borrow_mut() @@ -351,11 +332,10 @@ fn ram_device_region_readwrite() { assert_eq!(ret, [0x01u8; 8]); // Write overflow - memory_test.state.borrow_mut().memwrite( - addr + PAGE_SIZE - 1, - &data, - std::mem::size_of::() as u64, - ); + memory_test + .state + .borrow_mut() + .memwrite(addr + PAGE_SIZE - 1, &data); // Read overflow let ret = memory_test .state @@ -552,7 +532,7 @@ fn ram_readwrite_exception() { memory_test .state .borrow_mut() - .memwrite(addr, str.as_bytes(), SIZE); + .memwrite(addr, str.as_bytes()); let ret = memory_test.state.borrow_mut().memread(addr, SIZE); assert_eq!(ret, [0u8; SIZE as usize]); @@ -560,7 +540,6 @@ fn ram_readwrite_exception() { memory_test.state.borrow_mut().memwrite( MEM_SIZE * 1024 * 1024 - SIZE + ADDRESS_BASE, str_overflow.as_bytes(), - SIZE_OVERFLOW, ); let ret = memory_test.state.borrow_mut().memread(addr, SIZE_OVERFLOW); assert_eq!(ret, [0u8; SIZE_OVERFLOW as usize]); @@ -615,9 +594,7 @@ fn ram_readwrite_numa() { let str = "test memory read write"; let start_base = ADDRESS_BASE + MEM_SIZE * 1024 * 1024 / 2 - 4; - test_state - .borrow_mut() - .memwrite(start_base, str.as_bytes(), str.len() as u64); + test_state.borrow_mut().memwrite(start_base, str.as_bytes()); let ret = test_state .borrow_mut() .memread(start_base, str.len() as u64); diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 5bd6dd170..76badcf9f 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -655,7 +655,6 @@ fn send_request( test_state.borrow().memwrite( addr + offset, &request[offset as usize..(offset + k_bytes) as usize], - k_bytes, ); } let res = length % k_bytes; @@ -663,7 +662,7 @@ fn send_request( offset = num_k * k_bytes; test_state .borrow() - .memwrite(addr + offset, &request[offset as usize..], res); + .memwrite(addr + offset, &request[offset as usize..]); } let free_head = vqs[1] .borrow_mut() @@ -841,9 +840,7 @@ fn virtio_net_ctrl_mq_test() { class: VIRTIO_NET_CTRL_MQ, cmd, }; - test_state - .borrow() - .memwrite(addr, &ctrl_hdr.as_bytes(), size_of::() as u64); + test_state.borrow().memwrite(addr, &ctrl_hdr.as_bytes()); test_state .borrow() .writew(addr + size_of::() as u64, vq_pairs); @@ -1043,9 +1040,7 @@ fn send_ctrl_vq_request( .alloc(ctrl_data.len() as u64) .try_into() .unwrap(); - test_state - .borrow() - .memwrite(addr, &ctrl_data, ctrl_data.len() as u64); + test_state.borrow().memwrite(addr, &ctrl_data); let data_entries: Vec = vec![ TestVringDescEntry { data: addr, @@ -1636,9 +1631,7 @@ fn virtio_net_ctrl_abnormal_test() { .alloc(ctrl_data.len() as u64) .try_into() .unwrap(); - test_state - .borrow() - .memwrite(addr, &ctrl_data, ctrl_data.len() as u64); + test_state.borrow().memwrite(addr, &ctrl_data); let data_entries: Vec = vec![ TestVringDescEntry { @@ -1716,9 +1709,7 @@ fn virtio_net_abnormal_rx_tx_test() { assert_eq!(size, QUEUE_SIZE_NET); for _ in 0..size { let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); - test_state - .borrow() - .memwrite(addr, &request.as_bytes(), length); + test_state.borrow().memwrite(addr, &request.as_bytes()); vqs[1] .borrow_mut() .add(test_state.clone(), addr, length as u32, false); @@ -1798,9 +1789,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { let request = get_arp_request(id); let length = request.as_bytes().len() as u64; let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); - test_state - .borrow() - .memwrite(addr, &request.as_bytes(), length); + test_state.borrow().memwrite(addr, &request.as_bytes()); vqs[1] .borrow_mut() .add(test_state.clone(), addr, length as u32, false); -- Gitee From 44ba85945d47f5f6683bbe2a2fb96341c78619e8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 27 Feb 2023 23:40:16 +0800 Subject: [PATCH 0837/2187] MST: remove duplicate codes Signed-off-by: Mingwang Li --- tests/mod_test/tests/fwcfg_test.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index 20325a4b5..c591b6a56 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -14,8 +14,9 @@ use byteorder::{ByteOrder, LittleEndian}; use devices::legacy::FwCfgEntryType; use mod_test::libdriver::fwcfg::{bios_args, FW_CFG_BASE}; use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::virtio_block::{cleanup_blk_img, create_blk_img, TEST_IMAGE_SIZE}; use mod_test::libtest::test_init; -use mod_test::utils::{get_rand_str, swap_u16, swap_u32}; +use mod_test::utils::{swap_u16, swap_u32}; use std::cell::RefCell; use std::process::Command; @@ -185,19 +186,8 @@ fn test_filedir_by_dma() { fn test_boot_index() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); - let rng_name: String = get_rand_str(8); - assert!(cfg!(target_os = "linux")); - let image_path = format!("/tmp/stratovirt-{}.img", rng_name); - let image_of = format!("of={}", image_path); - let output = Command::new("dd") - .arg("if=/dev/zero") - .arg(&image_of) - .arg("bs=1M") - .arg("count=10") - .output() - .expect("Failed to create tmp image"); - assert!(output.status.success()); + let image_path = create_blk_img(TEST_IMAGE_SIZE, 0); let dev_path = "/pci@ffffffffffffffff/scsi@1/disk@0,0\n\0".to_string(); @@ -232,6 +222,9 @@ fn test_boot_index() { assert_eq!(&read_data, dev_path.as_bytes()); test_state.borrow_mut().stop(); + if !image_path.is_empty() { + cleanup_blk_img(image_path) + } } #[test] -- Gitee From 25216f037e6da217627eaad2da5c68c0b053d7e0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 28 Feb 2023 00:03:57 +0800 Subject: [PATCH 0838/2187] tests/net: fix some problems cause some testcase failes 1. Using different network segment for different testcase. 2. Delete useless "arp -s .." command. 3. Adjust the testcase id. Signed-off-by: Yan Wang --- tests/mod_test/tests/net_test.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 5bd6dd170..440e4d471 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -140,7 +140,7 @@ const MAC_ADDR_LEN: usize = 6; const ARP_SOURCE_MAC: [u8; MAC_ADDR_LEN] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]; const CMD_LINE_MAC: [u8; MAC_ADDR_LEN] = [0x52, 0x54, 0x00, 0x12, 0x34, 0x57]; const MAX_MAC_TABLE_LEN: usize = 64; -const TEST_MAC_ADDR_NUMS: u8 = 3; +const TEST_MAC_ADDR_NUMS: u8 = 2; static USED_ELEM_SIZE: u64 = size_of::() as u64; @@ -368,13 +368,12 @@ fn create_tap(id: u8, mq: bool) { execute_cmd("ip link set ".to_string() + &br_name + &" up".to_string()); execute_cmd("ip link set ".to_string() + &tap_name + &" up".to_string()); execute_cmd( - "ip address add 2.1.1.".to_string() + &id.to_string() + &"/24 dev ".to_string() + &br_name, - ); - execute_cmd( - "arp -s 2.1.1.".to_string() - + &(id + 2).to_string() - + &" ee:a6:10:07:ec:".to_string() - + &id.to_string(), + "ip address add ".to_string() + + &id.to_string() + + &".1.1.".to_string() + + &id.to_string() + + &"/24 dev ".to_string() + + &br_name, ); } @@ -605,9 +604,9 @@ fn get_arp_request(id: u8) -> ArpRequestPacket { p_len: 0x04, op: [0x00, 0x01], src_mac: ARP_SOURCE_MAC, - src_ip: [0x02, 0x01, 0x01, id + 1], + src_ip: [id, 0x01, 0x01, id + 1], dst_mac: [0x00; MAC_ADDR_LEN], - dst_ip: [0x02, 0x01, 0x01, id], + dst_ip: [id, 0x01, 0x01, id], }, } } @@ -629,9 +628,9 @@ fn get_arp_request_vlan(id: u8) -> ArpRequestPacketVlan { p_len: 0x04, op: [0x00, 0x01], src_mac: ARP_SOURCE_MAC, - src_ip: [0x02, 0x01, 0x01, id + 1], + src_ip: [id, 0x01, 0x01, id + 1], dst_mac: [0x00; MAC_ADDR_LEN], - dst_ip: [0x02, 0x01, 0x01, id], + dst_ip: [id, 0x01, 0x01, id], }, } } @@ -1764,7 +1763,7 @@ fn virtio_net_abnormal_rx_tx_test() { /// 1/2/3: success. #[test] fn virtio_net_abnormal_rx_tx_test_2() { - let id = 19 * TEST_MAC_ADDR_NUMS; + let id = 10 * TEST_MAC_ADDR_NUMS; let queue_pairs: u16 = 1; let queues: usize = 2 * queue_pairs as usize + 1; @@ -1833,7 +1832,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { /// 1/2/3: success. #[test] fn virtio_net_set_abnormal_feature() { - let id = 10 * TEST_MAC_ADDR_NUMS; + let id = 11 * TEST_MAC_ADDR_NUMS; let (net, test_state, alloc) = set_up(id, false, 0, false); // Three virtqueues: tx/rx/ctrl. @@ -1879,7 +1878,7 @@ fn virtio_net_set_abnormal_feature() { /// 1/3/4: success. #[test] fn virtio_net_send_abnormal_packet() { - let id = 11 * TEST_MAC_ADDR_NUMS; + let id = 12 * TEST_MAC_ADDR_NUMS; let (net, test_state, alloc) = set_up(id, false, 0, false); // Three virtqueues: tx/rx/ctrl. -- Gitee From 55ce0ad2707c196f4ca05c7abbda2affdd6572cc Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 27 Feb 2023 23:30:37 +0800 Subject: [PATCH 0839/2187] VNC: vnc mst reinforce Update the clock before waiting for image data. Signed-off-by: Xiao Ye --- tests/mod_test/src/libdriver/vnc.rs | 19 +++++++-- tests/mod_test/tests/vnc_test.rs | 65 +++++++++++++---------------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index f4c0b29e0..78f68ce30 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -23,6 +23,8 @@ use std::{ net::{Shutdown, SocketAddr, TcpStream}, os::unix::prelude::AsRawFd, rc::Rc, + thread::sleep, + time::Duration, }; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; @@ -639,17 +641,23 @@ impl IoOperations for IoChannel { pub struct VncClient { pub stream: TcpStream, pub io_channel: Rc>, + test_state: Rc>, pub display_mod: DisplayMode, epoll: Epoll, pub ready_events: Vec, } impl VncClient { - pub fn new(stream: TcpStream, io_channel: Rc>) -> Self { + pub fn new( + stream: TcpStream, + io_channel: Rc>, + test_state: Rc>, + ) -> Self { let epoll = Epoll::new().unwrap(); Self { stream, io_channel, + test_state, display_mod: DisplayMode::default(), epoll, ready_events: vec![EpollEvent::default(); 1], @@ -873,6 +881,10 @@ impl VncClient { ) -> Result> { let mut buf: Vec = Vec::new(); let mut rfb_event: Vec<(RfbServerMsg, EncodingType)> = Vec::new(); + sleep(Duration::from_millis(50)); + self.test_state + .borrow_mut() + .clock_step_ns(REFRESH_TIME_INTERVAL); loop { // Wait event. match self.epoll_wait(EventSet::IN) { @@ -1045,7 +1057,7 @@ impl VncClient { /// # Arguments /// /// * `port` - Local port listened by vnc server. -pub fn create_new_client(port: u16) -> Result { +pub fn create_new_client(test_state: Rc>, port: u16) -> Result { let port = port + RFB_PORT_OFFSET; let addrs = [SocketAddr::from(([127, 0, 0, 1], port))]; let stream = TcpStream::connect(&addrs[..]).unwrap(); @@ -1057,7 +1069,7 @@ pub fn create_new_client(port: u16) -> Result { .unwrap(); let stream_clone = stream.try_clone().expect("clone failed..."); let io_channel = Rc::new(RefCell::new(IoChannel::new(stream_clone))); - let mut vnc_client = VncClient::new(stream, io_channel); + let mut vnc_client = VncClient::new(stream, io_channel, test_state); // Register epoll event. let event = EpollEvent::new( EventSet::READ_HANG_UP | EventSet::IN, @@ -1219,6 +1231,7 @@ impl TestDemoInputDevice { /// Read an input event from a memory. pub fn read_input_event(&mut self) -> InputMessage { + sleep(Duration::from_millis(50)); let addr = self.mem_addr; let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index 09cf2b850..bbdcd34df 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -16,12 +16,12 @@ use mod_test::{ create_new_client, set_up, tear_down, DemoGpuConfig, EncodingType, InputConfig, RfbPixelFormat, RfbServerMsg, TestAuthType, UpdateState, KEYEVENTLIST, PIXMAN_A1, PIXMAN_A8B8G8R8, PIXMAN_R8G8B8, PIXMAN_X2R10G10B10, PIXMAN_YUY2, POINTEVENTLIST, - REFRESH_TIME_INTERVAL, TEST_CLIENT_RAND_MSG, + TEST_CLIENT_RAND_MSG, }, libtest::TestState, }; use serde_json::Value; -use std::{cell::RefCell, rc::Rc, thread::sleep, time::Duration}; +use std::{cell::RefCell, rc::Rc}; use vmm_sys_util::epoll::EventSet; fn qmp_query_vnc(test_state: Rc>) -> Value { @@ -64,7 +64,7 @@ fn test_set_area_dirty() { let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); let demo_gpu = gpu_list[0].clone(); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); // Encoding -> Raw. @@ -80,8 +80,6 @@ fn test_set_area_dirty() { demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); - sleep(Duration::from_millis(2000)); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL * 2); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res @@ -95,7 +93,6 @@ fn test_set_area_dirty() { assert!(vnc_client .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); @@ -115,8 +112,6 @@ fn test_set_area_dirty() { .is_ok()); demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); - sleep(Duration::from_millis(2000)); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); @@ -165,7 +160,7 @@ fn test_send_cursor_image() { let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); let demo_gpu = gpu_list[0].clone(); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); assert!(vnc_client .test_setup_encodings(Some(1), Some(EncodingType::EncodingRichCursor)) @@ -204,7 +199,7 @@ fn test_send_cursor_image() { demo_gpu.borrow_mut().replace_cursor(64, 64, 16, 16, 0); assert!(vnc_client.disconnect().is_ok()); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); assert!(vnc_client.disconnect().is_ok()); @@ -246,7 +241,7 @@ fn test_desktop_resize() { demo_gpu .borrow_mut() .replace_surface(640, 480, PIXMAN_A8B8G8R8); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); assert!(vnc_client .test_setup_encodings(None, Some(EncodingType::EncodingDesktopresize)) @@ -317,7 +312,7 @@ fn test_set_pixel_format() { }; let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); assert!(vnc_client .test_setup_encodings(None, Some(EncodingType::EncodingRaw)) @@ -330,7 +325,6 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res @@ -343,8 +337,7 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - sleep(Duration::from_millis(500)); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL * 2); + let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res @@ -357,7 +350,7 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf.clone()); assert!(res.is_ok()); let res = res.unwrap(); @@ -377,7 +370,7 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res.unwrap().contains(&( @@ -395,7 +388,6 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res.unwrap().contains(&( @@ -413,7 +405,7 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); let res = res.unwrap(); @@ -459,7 +451,7 @@ fn test_vnc_kbd_mouse() { let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); let demo_gpu = gpu_list[0].clone(); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); // Key event. for &(name, keysym, keycode) in KEYEVENTLIST.iter() { @@ -469,8 +461,7 @@ fn test_vnc_kbd_mouse() { assert_eq!(msg.keycode, keycode); assert_eq!(msg.down, 0); assert!(vnc_client.test_key_event(1, keysym as u32).is_ok()); - sleep(Duration::from_millis(50)); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + let msg = input.borrow_mut().read_input_event(); println!("key {:?}: {:?}", name, msg); assert_eq!(msg.keycode, keycode); @@ -535,7 +526,7 @@ fn test_switch_display_device() { .borrow_mut() .replace_surface(1920, 1080, PIXMAN_A8B8G8R8); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); assert!(vnc_client .test_setup_encodings(None, Some(EncodingType::EncodingDesktopresize)) @@ -608,7 +599,7 @@ fn test_update_image_abnormal() { demo_gpu .borrow_mut() .replace_surface(640, 480, PIXMAN_A8B8G8R8); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); let value = qmp_query_vnc(test_state.clone()); let client_num = value["return"]["clients"].as_array().unwrap().len(); @@ -617,9 +608,9 @@ fn test_update_image_abnormal() { tear_down(gpu_list, input, test_state); } -fn test_rfb_version_abnormal(port: u16) -> Result<()> { +fn test_rfb_version_abnormal(test_state: Rc>, port: u16) -> Result<()> { let mut buf: Vec = Vec::new(); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state, port).unwrap(); println!("Connect to server."); assert!(vnc_client.read_msg(&mut buf, 12).is_ok()); assert_eq!(buf[..12].to_vec(), "RFB 003.008\n".as_bytes().to_vec()); @@ -641,9 +632,9 @@ fn test_rfb_version_abnormal(port: u16) -> Result<()> { Ok(()) } -fn test_unsupport_sec_type(port: u16) -> Result<()> { +fn test_unsupport_sec_type(test_state: Rc>, port: u16) -> Result<()> { let mut buf: Vec = Vec::new(); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state, port).unwrap(); println!("Connect to server."); assert!(vnc_client.read_msg(&mut buf, 12).is_ok()); assert_eq!(buf[..12].to_vec(), "RFB 003.008\n".as_bytes().to_vec()); @@ -675,8 +666,8 @@ fn test_unsupport_sec_type(port: u16) -> Result<()> { Ok(()) } -fn test_set_pixel_format_abnormal(port: u16) -> Result<()> { - let mut vnc_client = create_new_client(port).unwrap(); +fn test_set_pixel_format_abnormal(test_state: Rc>, port: u16) -> Result<()> { + let mut vnc_client = create_new_client(test_state, port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); let pf = RfbPixelFormat::new(17, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); @@ -693,8 +684,8 @@ fn test_set_pixel_format_abnormal(port: u16) -> Result<()> { Ok(()) } -fn test_client_rand_bytes(port: u16) -> Result<()> { - let mut vnc_client = create_new_client(port).unwrap(); +fn test_client_rand_bytes(test_state: Rc>, port: u16) -> Result<()> { + let mut vnc_client = create_new_client(test_state, port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); let mut buf = TEST_CLIENT_RAND_MSG; vnc_client.write_msg(&mut buf)?; @@ -736,12 +727,12 @@ fn test_rfb_abnormal() { }; let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); - assert!(test_rfb_version_abnormal(port).is_ok()); - assert!(test_unsupport_sec_type(port).is_ok()); - assert!(test_set_pixel_format_abnormal(port).is_ok()); - assert!(test_client_rand_bytes(port).is_ok()); + assert!(test_rfb_version_abnormal(test_state.clone(), port).is_ok()); + assert!(test_unsupport_sec_type(test_state.clone(), port).is_ok()); + assert!(test_set_pixel_format_abnormal(test_state.clone(), port).is_ok()); + assert!(test_client_rand_bytes(test_state.clone(), port).is_ok()); - let mut vnc_client = create_new_client(port).unwrap(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); let value = qmp_query_vnc(test_state.clone()); let client_num = value["return"]["clients"].as_array().unwrap().len(); -- Gitee From db17b8a6735fec03c0d51d248136f87029635e05 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 01:15:23 +0800 Subject: [PATCH 0840/2187] util: Delete align util There already have round_down and round_up functions. Signed-off-by: Keqian Zhu --- machine_manager/src/config/mod.rs | 3 +-- util/src/aio/mod.rs | 17 ++++++++++------- util/src/align.rs | 27 --------------------------- util/src/lib.rs | 1 - 4 files changed, 11 insertions(+), 37 deletions(-) delete mode 100644 util/src/align.rs diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index e63a1daaa..2e4b53463 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -65,7 +65,6 @@ use serde::{Deserialize, Serialize}; use anyhow::{anyhow, bail, Context, Result}; use log::error; -use util::align::is_aligned; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::{ @@ -283,7 +282,7 @@ impl VmConfig { ); } let file_size = file.seek(SeekFrom::End(0))?; - if !is_aligned(file_size, req_align) { + if file_size & (req_align as u64 - 1) != 0 { bail!("The size of file {} is not aligned to {}.", path, req_align); } let drive_file = DriveFile { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 3ec5fa892..d69ccef3d 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize}; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; -use crate::align::{align_down, align_up, is_aligned}; +use crate::num_ops::{round_down, round_up}; use crate::unix::host_page_size; use anyhow::{anyhow, bail, Context, Result}; use libaio::LibaioContext; @@ -161,7 +161,8 @@ impl Aio { pub fn submit_request(&mut self, mut cb: AioCb) -> Result<()> { if self.request_misaligned(&cb) { - let max_len = align_down(cb.nbytes + cb.req_align as u64 * 2, cb.req_align); + let max_len = round_down(cb.nbytes + cb.req_align as u64 * 2, cb.req_align as u64) + .ok_or_else(|| anyhow!("Failed to round down request length."))?; // Set upper limit of buffer length to avoid OOM. let buff_len = cmp::min(max_len, MAX_LEN_BOUNCE_BUFF); // SAFETY: we allocate aligned memory and free it later. Alignment is set to @@ -317,14 +318,14 @@ impl Aio { fn request_misaligned(&self, cb: &AioCb) -> bool { if cb.direct && (cb.opcode == OpCode::Preadv || cb.opcode == OpCode::Pwritev) { - if !is_aligned(cb.offset as u64, cb.req_align) { + if (cb.offset as u64) & (cb.req_align as u64 - 1) != 0 { return true; } for iov in cb.iovec.iter() { - if !is_aligned(iov.iov_base, cb.buf_align) { + if iov.iov_base & (cb.buf_align as u64 - 1) != 0 { return true; } - if !is_aligned(iov.iov_len, cb.req_align) { + if iov.iov_len & (cb.req_align as u64 - 1) != 0 { return true; } } @@ -338,9 +339,11 @@ impl Aio { bounce_buffer: *mut c_void, buffer_len: u64, ) -> Result<()> { - let offset_align = align_down(cb.offset as u64, cb.req_align); + let offset_align = round_down(cb.offset as u64, cb.req_align as u64) + .ok_or_else(|| anyhow!("Failed to round down request offset."))?; let high = cb.offset as u64 + cb.nbytes; - let high_align = align_up(high, cb.req_align); + let high_align = round_up(high, cb.req_align as u64) + .ok_or_else(|| anyhow!("Failed to round up request high edge."))?; match cb.opcode { OpCode::Preadv => { diff --git a/util/src/align.rs b/util/src/align.rs deleted file mode 100644 index a257904dd..000000000 --- a/util/src/align.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub fn is_aligned(offset: u64, align: u32) -> bool { - offset & (align as u64 - 1) == 0 -} - -pub fn align_down(offset: u64, align: u32) -> u64 { - offset - (offset & (align as u64 - 1)) -} - -pub fn align_up(offset: u64, align: u32) -> u64 { - if !is_aligned(offset, align) { - align_down(offset, align) + align as u64 - } else { - offset - } -} diff --git a/util/src/lib.rs b/util/src/lib.rs index 5cbb3c4da..c39f055cd 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod aio; -pub mod align; pub mod arg_parser; pub mod bitmap; pub mod byte_code; -- Gitee From 46d80c1dbdfd62df723f54e626856fe202e9d247 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 10:33:29 +0800 Subject: [PATCH 0841/2187] usb: adjust the position of the structure Move the pub structure to mod.rs. And add some get/set field function. Signed-off-by: zhouli57 --- usb/src/hid.rs | 12 +-- usb/src/usb.rs | 2 +- usb/src/xhci/mod.rs | 143 +++++++++++++++++++++++++++++ usb/src/xhci/xhci_controller.rs | 153 +++++++++++++++++++++++++------- usb/src/xhci/xhci_regs.rs | 26 +++--- usb/src/xhci/xhci_ring.rs | 147 +----------------------------- 6 files changed, 287 insertions(+), 196 deletions(-) diff --git a/usb/src/hid.rs b/usb/src/hid.rs index 891598898..c4f4c8950 100644 --- a/usb/src/hid.rs +++ b/usb/src/hid.rs @@ -34,12 +34,12 @@ const HID_KEYBOARD_RIGHT_ALT: u8 = 0xe6; const HID_KEYBOARD_RIGHT_GUI: u8 = 0xe7; /// See the spec section 7.2 Class-Specific Requests -const HID_GET_REPORT: u8 = 0x01; -const HID_GET_IDLE: u8 = 0x02; -const HID_GET_PROTOCOL: u8 = 0x03; -const HID_SET_REPORT: u8 = 0x09; -const HID_SET_IDLE: u8 = 0x0a; -const HID_SET_PROTOCOL: u8 = 0x0b; +pub const HID_GET_REPORT: u8 = 0x01; +pub const HID_GET_IDLE: u8 = 0x02; +pub const HID_GET_PROTOCOL: u8 = 0x03; +pub const HID_SET_REPORT: u8 = 0x09; +pub const HID_SET_IDLE: u8 = 0x0a; +pub const HID_SET_PROTOCOL: u8 = 0x0b; /// See the spec section 7.2.5 Get Protocol Request #[allow(unused)] diff --git a/usb/src/usb.rs b/usb/src/usb.rs index ad3c26df9..84dc1ac36 100644 --- a/usb/src/usb.rs +++ b/usb/src/usb.rs @@ -39,7 +39,7 @@ pub enum UsbPacketStatus { /// USB request used to transfer to USB device. #[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub struct UsbDeviceRequest { pub request_type: u8, pub request: u8, diff --git a/usb/src/xhci/mod.rs b/usb/src/xhci/mod.rs index 62151bb2a..3f839eebc 100644 --- a/usb/src/xhci/mod.rs +++ b/usb/src/xhci/mod.rs @@ -14,3 +14,146 @@ pub mod xhci_controller; pub mod xhci_pci; pub mod xhci_regs; mod xhci_ring; + +/// Transfer Request Block +pub const TRB_SIZE: u32 = 16; +pub const TRB_TYPE_SHIFT: u32 = 10; +pub const TRB_TYPE_MASK: u32 = 0x3f; +/// Cycle bit +pub const TRB_C: u32 = 1; +/// Event Data +pub const TRB_EV_ED: u32 = 1 << 2; +/// Toggle Cycle +pub const TRB_LK_TC: u32 = 1 << 1; +/// Interrupt-on Short Packet +pub const TRB_TR_ISP: u32 = 1 << 2; +/// Chain bit +pub const TRB_TR_CH: u32 = 1 << 4; +/// Interrupt On Completion +pub const TRB_TR_IOC: u32 = 1 << 5; +/// Immediate Data. +pub const TRB_TR_IDT: u32 = 1 << 6; +/// Direction of the data transfer. +pub const TRB_TR_DIR: u32 = 1 << 16; +/// TRB Transfer Length Mask +pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; +/// Setup Stage TRB Length always 8 +pub const SETUP_TRB_TR_LEN: u32 = 8; + +/// TRB Type Definitions. See the spec 6.4.6 TRB types. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TRBType { + TrbReserved = 0, + TrNormal, + TrSetup, + TrData, + TrStatus, + TrIsoch, + TrLink, + TrEvdata, + TrNoop, + CrEnableSlot, + CrDisableSlot, + CrAddressDevice, + CrConfigureEndpoint, + CrEvaluateContext, + CrResetEndpoint, + CrStopEndpoint, + CrSetTrDequeue, + CrResetDevice, + CrForceEvent, + CrNegotiateBw, + CrSetLatencyTolerance, + CrGetPortBandwidth, + CrForceHeader, + CrNoop, + ErTransfer = 32, + ErCommandComplete, + ErPortStatusChange, + ErBandwidthRequest, + ErDoorbell, + ErHostController, + ErDeviceNotification, + ErMfindexWrap, + Unknown, +} + +impl From for TRBType { + fn from(t: u32) -> TRBType { + match t { + 0 => TRBType::TrbReserved, + 1 => TRBType::TrNormal, + 2 => TRBType::TrSetup, + 3 => TRBType::TrData, + 4 => TRBType::TrStatus, + 5 => TRBType::TrIsoch, + 6 => TRBType::TrLink, + 7 => TRBType::TrEvdata, + 8 => TRBType::TrNoop, + 9 => TRBType::CrEnableSlot, + 10 => TRBType::CrDisableSlot, + 11 => TRBType::CrAddressDevice, + 12 => TRBType::CrConfigureEndpoint, + 13 => TRBType::CrEvaluateContext, + 14 => TRBType::CrResetEndpoint, + 15 => TRBType::CrStopEndpoint, + 16 => TRBType::CrSetTrDequeue, + 17 => TRBType::CrResetDevice, + 18 => TRBType::CrForceEvent, + 19 => TRBType::CrNegotiateBw, + 20 => TRBType::CrSetLatencyTolerance, + 21 => TRBType::CrGetPortBandwidth, + 22 => TRBType::CrForceHeader, + 23 => TRBType::CrNoop, + 32 => TRBType::ErTransfer, + 33 => TRBType::ErCommandComplete, + 34 => TRBType::ErPortStatusChange, + 35 => TRBType::ErBandwidthRequest, + 36 => TRBType::ErDoorbell, + 37 => TRBType::ErHostController, + 38 => TRBType::ErDeviceNotification, + 39 => TRBType::ErMfindexWrap, + _ => TRBType::Unknown, + } + } +} + +/// TRB Completion Code. See the spec 6.4.5 TRB Completion Codes. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TRBCCode { + Invalid = 0, + Success, + DataBufferError, + BabbleDetected, + UsbTransactionError, + TrbError, + StallError, + ResourceError, + BandwidthError, + NoSlotsError, + InvalidStreamTypeError, + SlotNotEnabledError, + EpNotEnabledError, + ShortPacket, + RingUnderrun, + RingOverrun, + VfErFull, + ParameterError, + BandwidthOverrun, + ContextStateError, + NoPingResponseError, + EventRingFullError, + IncompatibleDeviceError, + MissedServiceError, + CommandRingStopped, + CommandAborted, + Stopped, + StoppedLengthInvalid, + MaxExitLatencyTooLargeError = 29, + IsochBufferOverrun = 31, + EventLostError, + UndefinedError, + InvalidStreamIdError, + SecondaryBandwidthError, + SplitTransactionError, +} diff --git a/usb/src/xhci/xhci_controller.rs b/usb/src/xhci/xhci_controller.rs index e9fb24014..6922a6041 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/usb/src/xhci/xhci_controller.rs @@ -28,25 +28,25 @@ use util::num_ops::{read_u32, write_u64_low}; use crate::config::*; use crate::usb::{Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter}; -use crate::xhci::xhci_ring::{ - TRBCCode, TRBType, XhciEventRingSeg, XhciRing, XhciTRB, TRB_EV_ED, TRB_SIZE, TRB_TR_IDT, - TRB_TR_IOC, TRB_TR_ISP, TRB_TYPE_SHIFT, -}; use crate::UsbError; -use super::xhci_ring::SETUP_TRB_TR_LEN; -use super::xhci_ring::TRB_TR_DIR; -use super::xhci_ring::TRB_TR_LEN_MASK; +use super::xhci_ring::XhciEventRingSeg; +use super::xhci_ring::XhciRing; +use super::xhci_ring::XhciTRB; +use super::{ + TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_SIZE, TRB_TR_DIR, TRB_TR_IDT, TRB_TR_IOC, + TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TYPE_SHIFT, +}; pub const MAX_INTRS: u16 = 16; pub const MAX_SLOTS: u32 = 64; /// Endpoint state -const EP_STATE_MASK: u32 = 0x7; -const EP_DISABLED: u32 = 0; -const EP_RUNNING: u32 = 1; -const EP_HALTED: u32 = 2; -const EP_STOPPED: u32 = 3; -const EP_ERROR: u32 = 4; +pub const EP_STATE_MASK: u32 = 0x7; +pub const EP_DISABLED: u32 = 0; +pub const EP_RUNNING: u32 = 1; +pub const EP_HALTED: u32 = 2; +pub const EP_STOPPED: u32 = 3; +pub const EP_ERROR: u32 = 4; /// Endpoint type const EP_TYPE_SHIFT: u32 = 3; const EP_TYPE_MASK: u32 = 0x7; @@ -55,13 +55,10 @@ const SLOT_STATE_MASK: u32 = 0x1f; const SLOT_STATE_SHIFT: u32 = 27; /// 6.2.3 Slot Context. Table 6-7. /// The values of both enabled and disabled are 0. -const SLOT_DISABLED_ENABLED: u32 = 0; -const SLOT_DEFAULT: u32 = 1; -const SLOT_ADDRESSED: u32 = 2; -const SLOT_CONFIGURED: u32 = 3; -const SLOT_CONTEXT_ENTRIES_MASK: u32 = 0x1f; -const SLOT_CONTEXT_ENTRIES_SHIFT: u32 = 27; -const SLOT_CONTEXT_DEVICE_ADDRESS_MASK: u32 = 0xff; +pub const SLOT_DISABLED_ENABLED: u32 = 0; +pub const SLOT_DEFAULT: u32 = 1; +pub const SLOT_ADDRESSED: u32 = 2; +pub const SLOT_CONFIGURED: u32 = 3; /// TRB flags const TRB_CR_BSR: u32 = 1 << 9; const TRB_CR_EPID_SHIFT: u32 = 16; @@ -92,15 +89,30 @@ const INPUT_CONTEXT_SIZE: u64 = 0x420; const DEVICE_CONTEXT_SIZE: u64 = 0x400; /// Slot Context. const SLOT_INPUT_CTX_OFFSET: u64 = 0x20; -const SLOT_CTX_MAX_EXIT_LATENCY_MASK: u32 = 0xffff; -const SLOT_CTX_INTERRUPTER_TARGET_MASK: u32 = 0xffc00000; +const SLOT_CONTEXT_MAX_EXIT_LATENCY_MASK: u32 = 0xffff; +const SLOT_CONTEXT_MAX_EXIT_LATENCY_SHIFT: u32 = 0; +const SLOT_CONTEXT_INTERRUPTER_TARGET_MASK: u32 = 0x3ff; +const SLOT_CONTEXT_INTERRUPTER_TARGET_SHIFT: u32 = 22; +const SLOT_CONTEXT_PORT_NUMBER_MASK: u32 = 0xff; +const SLOT_CONTEXT_PORT_NUMBER_SHIFT: u32 = 16; +const SLOT_CONTEXT_ENTRIES_MASK: u32 = 0x1f; +const SLOT_CONTEXT_ENTRIES_SHIFT: u32 = 27; +const SLOT_CONTEXT_DEVICE_ADDRESS_MASK: u32 = 0xff; +const SLOT_CONTEXT_DEVICE_ADDRESS_SHIFT: u32 = 0; /// Endpoint Context. const EP_INPUT_CTX_ENTRY_SIZE: u64 = 0x20; const EP_INPUT_CTX_OFFSET: u64 = 0x40; const EP_CTX_OFFSET: u64 = 0x20; const EP_CTX_TR_DEQUEUE_POINTER_MASK: u64 = !0xf; const EP_CTX_DCS: u64 = 1; -const EP_CTX_MAX_PACKET_SIZE_MASK: u32 = 0xffff0000; +const EP_CONTEXT_MAX_PACKET_SIZE_MASK: u32 = 0xffff; +const EP_CONTEXT_MAX_PACKET_SIZE_SHIFT: u32 = 16; +const EP_CONTEXT_INTERVAL_MASK: u32 = 0xff; +const EP_CONTEXT_INTERVAL_SHIFT: u32 = 16; +const EP_CONTEXT_EP_STATE_MASK: u32 = 0x7; +const EP_CONTEXT_EP_STATE_SHIFT: u32 = 0; +const EP_CONTEXT_EP_TYPE_MASK: u32 = 0x7; +const EP_CONTEXT_EP_TYPE_SHIFT: u32 = 3; type DmaAddr = u64; @@ -405,15 +417,57 @@ pub struct XhciSlotCtx { } impl XhciSlotCtx { - fn set_slot_state(&mut self, state: u32) { + pub fn set_slot_state(&mut self, state: u32) { self.dev_state &= !(SLOT_STATE_MASK << SLOT_STATE_SHIFT); self.dev_state |= (state & SLOT_STATE_MASK) << SLOT_STATE_SHIFT; } - fn set_context_entry(&mut self, num: u32) { + pub fn get_slot_state(&self) -> u32 { + self.dev_state >> SLOT_STATE_SHIFT & SLOT_STATE_MASK + } + + pub fn set_context_entry(&mut self, num: u32) { self.dev_info &= !(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT); self.dev_info |= (num & SLOT_CONTEXT_ENTRIES_MASK) << SLOT_CONTEXT_ENTRIES_SHIFT; } + + pub fn set_port_number(&mut self, port_number: u32) { + self.dev_info &= !(SLOT_CONTEXT_PORT_NUMBER_MASK << SLOT_CONTEXT_PORT_NUMBER_SHIFT); + self.dev_info2 |= + (port_number & SLOT_CONTEXT_PORT_NUMBER_MASK) << SLOT_CONTEXT_PORT_NUMBER_SHIFT; + } + + pub fn get_max_exit_latency(&self) -> u32 { + self.dev_info2 >> SLOT_CONTEXT_MAX_EXIT_LATENCY_SHIFT & SLOT_CONTEXT_MAX_EXIT_LATENCY_MASK + } + + pub fn set_max_exit_latency(&mut self, state: u32) { + self.dev_info2 &= + !(SLOT_CONTEXT_MAX_EXIT_LATENCY_MASK << SLOT_CONTEXT_MAX_EXIT_LATENCY_SHIFT); + self.dev_info2 |= + (state & SLOT_CONTEXT_MAX_EXIT_LATENCY_MASK) << SLOT_CONTEXT_MAX_EXIT_LATENCY_SHIFT; + } + + pub fn get_interrupter_target(&self) -> u32 { + self.tt_info >> SLOT_CONTEXT_INTERRUPTER_TARGET_SHIFT & SLOT_CONTEXT_INTERRUPTER_TARGET_MASK + } + + pub fn set_interrupter_target(&mut self, state: u32) { + self.tt_info &= + !(SLOT_CONTEXT_INTERRUPTER_TARGET_MASK << SLOT_CONTEXT_INTERRUPTER_TARGET_SHIFT); + self.tt_info |= + (state & SLOT_CONTEXT_INTERRUPTER_TARGET_MASK) << SLOT_CONTEXT_INTERRUPTER_TARGET_SHIFT; + } + + pub fn get_usb_device_address(&self) -> u32 { + self.dev_state >> SLOT_CONTEXT_DEVICE_ADDRESS_SHIFT & SLOT_CONTEXT_DEVICE_ADDRESS_MASK + } + + pub fn set_usb_device_address(&mut self, state: u32) { + self.dev_state &= !(SLOT_CONTEXT_DEVICE_ADDRESS_MASK << SLOT_CONTEXT_DEVICE_ADDRESS_SHIFT); + self.dev_state |= + (state & SLOT_CONTEXT_DEVICE_ADDRESS_MASK) << SLOT_CONTEXT_DEVICE_ADDRESS_SHIFT; + } } impl DwordOrder for XhciSlotCtx {} @@ -429,9 +483,45 @@ pub struct XhciEpCtx { pub tx_info: u32, } +impl XhciEpCtx { + pub fn set_tr_dequeue_pointer(&mut self, dequeue: u64) { + self.deq_lo = dequeue as u32; + self.deq_hi = (dequeue >> 32) as u32; + } + + pub fn get_max_packet_size(&self) -> u32 { + self.ep_info2 >> EP_CONTEXT_MAX_PACKET_SIZE_SHIFT & EP_CONTEXT_MAX_PACKET_SIZE_MASK + } + + pub fn set_max_packet_size(&mut self, size: u32) { + self.ep_info2 &= !(EP_CONTEXT_MAX_PACKET_SIZE_MASK << EP_CONTEXT_MAX_PACKET_SIZE_SHIFT); + self.ep_info2 |= + (size & EP_CONTEXT_MAX_PACKET_SIZE_MASK) << EP_CONTEXT_MAX_PACKET_SIZE_SHIFT; + } + + pub fn set_interval(&mut self, inter: u32) { + self.ep_info &= !(EP_CONTEXT_INTERVAL_MASK << EP_CONTEXT_INTERVAL_SHIFT); + self.ep_info |= (inter & EP_CONTEXT_INTERVAL_MASK) << EP_CONTEXT_INTERVAL_SHIFT; + } + + pub fn get_ep_state(&self) -> u32 { + self.ep_info >> EP_CONTEXT_EP_STATE_SHIFT & EP_CONTEXT_EP_STATE_MASK + } + + pub fn set_ep_state(&mut self, state: u32) { + self.ep_info &= !(EP_CONTEXT_EP_STATE_MASK << EP_CONTEXT_EP_STATE_SHIFT); + self.ep_info |= (state & EP_CONTEXT_EP_STATE_MASK) << EP_CONTEXT_EP_STATE_SHIFT; + } + + pub fn set_ep_type(&mut self, state: u32) { + self.ep_info2 &= !(EP_CONTEXT_EP_TYPE_MASK << EP_CONTEXT_EP_TYPE_SHIFT); + self.ep_info2 |= (state & EP_CONTEXT_EP_TYPE_MASK) << EP_CONTEXT_EP_TYPE_SHIFT; + } +} + impl DwordOrder for XhciEpCtx {} -trait DwordOrder: Default + Copy + Send + Sync { +pub trait DwordOrder: Default + Copy + Send + Sync { fn as_dwords(&self) -> &[u32] { unsafe { from_raw_parts(self as *const Self as *const u32, size_of::() / 4) } } @@ -1007,10 +1097,8 @@ impl XhciDevice { GuestAddress(octx), slot_ctx.as_mut_dwords(), )?; - slot_ctx.dev_info2 &= !SLOT_CTX_MAX_EXIT_LATENCY_MASK; - slot_ctx.dev_info2 |= islot_ctx.dev_info2 & SLOT_CTX_MAX_EXIT_LATENCY_MASK; - slot_ctx.tt_info &= !SLOT_CTX_INTERRUPTER_TARGET_MASK; - slot_ctx.tt_info |= islot_ctx.tt_info & SLOT_CTX_INTERRUPTER_TARGET_MASK; + slot_ctx.set_max_exit_latency(islot_ctx.get_max_exit_latency()); + slot_ctx.set_interrupter_target(islot_ctx.get_interrupter_target()); dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; } if ictl_ctx.add_flags & 0x2 == 0x2 { @@ -1033,8 +1121,7 @@ impl XhciDevice { ), ep_ctx.as_mut_dwords(), )?; - ep_ctx.ep_info2 &= !EP_CTX_MAX_PACKET_SIZE_MASK; - ep_ctx.ep_info2 |= iep_ctx.ep_info2 & EP_CTX_MAX_PACKET_SIZE_MASK; + ep_ctx.set_max_packet_size(iep_ctx.get_max_packet_size()); dma_write_u32( &self.mem_space, // It is safe to use plus here becuase we previously verify the address. @@ -1066,7 +1153,7 @@ impl XhciDevice { } slot_ctx.set_slot_state(SLOT_DEFAULT); slot_ctx.set_context_entry(1); - slot_ctx.dev_state &= !SLOT_CONTEXT_DEVICE_ADDRESS_MASK; + slot_ctx.set_usb_device_address(0); dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; Ok(TRBCCode::Success) } diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 7394cfd7f..4568daec7 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -10,19 +10,21 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::xhci::xhci_controller::dma_write_bytes; -use crate::xhci::xhci_ring::XhciTRB; use std::sync::{Arc, Mutex}; +use anyhow::Result; + use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error}; use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; +use super::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; + +use crate::xhci::xhci_controller::dma_write_bytes; use crate::xhci::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; -use crate::xhci::xhci_ring::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; +use crate::xhci::xhci_ring::XhciTRB; use crate::{config::*, UsbError}; -use anyhow::Result; /// Capability offset or size. pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; @@ -90,14 +92,14 @@ const XHCI_CRCR_CRP_MASK: u64 = !0x3f; /// Notification Enable. const XHCI_OPER_NE_MASK: u32 = 0xffff; /// Interrupter Registers. -const XHCI_INTR_REG_IMAN: u64 = 0x00; -const XHCI_INTR_REG_IMOD: u64 = 0x04; -const XHCI_INTR_REG_ERSTSZ: u64 = 0x08; -const XHCI_INTR_REG_ERSTBA_LO: u64 = 0x10; -const XHCI_INTR_REG_ERSTBA_HI: u64 = 0x14; -const XHCI_INTR_REG_ERDP_LO: u64 = 0x18; -const XHCI_INTR_REG_ERDP_HI: u64 = 0x1c; -const XHCI_INTR_REG_SIZE: u64 = 0x20; +pub const XHCI_INTR_REG_IMAN: u64 = 0x00; +pub const XHCI_INTR_REG_IMOD: u64 = 0x04; +pub const XHCI_INTR_REG_ERSTSZ: u64 = 0x08; +pub const XHCI_INTR_REG_ERSTBA_LO: u64 = 0x10; +pub const XHCI_INTR_REG_ERSTBA_HI: u64 = 0x14; +pub const XHCI_INTR_REG_ERDP_LO: u64 = 0x18; +pub const XHCI_INTR_REG_ERDP_HI: u64 = 0x1c; +pub const XHCI_INTR_REG_SIZE: u64 = 0x20; const XHCI_INTR_REG_SHIFT: u64 = 5; /// Doorbell Register Bit Field. /// DB Target. diff --git a/usb/src/xhci/xhci_ring.rs b/usb/src/xhci/xhci_ring.rs index 80fcb5e71..8dd36ae48 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/usb/src/xhci/xhci_ring.rs @@ -13,38 +13,15 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use std::sync::Arc; +use anyhow::{bail, Result}; + use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use log::debug; +use super::{TRBType, TRB_C, TRB_LK_TC, TRB_SIZE, TRB_TR_CH, TRB_TYPE_MASK, TRB_TYPE_SHIFT}; use crate::xhci::xhci_controller::dma_read_bytes; use crate::UsbError; -use anyhow::{bail, Result}; - -/// Transfer Request Block -pub const TRB_SIZE: u32 = 16; -pub const TRB_TYPE_SHIFT: u32 = 10; -pub const TRB_TYPE_MASK: u32 = 0x3f; -/// Cycle bit -pub const TRB_C: u32 = 1; -/// Event Data -pub const TRB_EV_ED: u32 = 1 << 2; -/// Toggle Cycle -pub const TRB_LK_TC: u32 = 1 << 1; -/// Interrupt-on Short Packet -pub const TRB_TR_ISP: u32 = 1 << 2; -/// Chain bit -pub const TRB_TR_CH: u32 = 1 << 4; -/// Interrupt On Completion -pub const TRB_TR_IOC: u32 = 1 << 5; -/// Immediate Data. -pub const TRB_TR_IDT: u32 = 1 << 6; -/// Direction of the data transfer. -pub const TRB_TR_DIR: u32 = 1 << 16; -/// TRB Transfer Length Mask -pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; -/// Setup Stage TRB Length always 8 -pub const SETUP_TRB_TR_LEN: u32 = 8; const TRB_LINK_LIMIT: u32 = 32; /// The max size of a ring segment in bytes is 64k. @@ -52,124 +29,6 @@ const RING_SEGMENT_LIMIT: u32 = 0x1_0000; /// The max size of ring. const RING_LEN_LIMIT: u32 = TRB_LINK_LIMIT * RING_SEGMENT_LIMIT / TRB_SIZE; -/// TRB Type Definitions. See the spec 6.4.6 TRB types. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TRBType { - TrbReserved = 0, - TrNormal, - TrSetup, - TrData, - TrStatus, - TrIsoch, - TrLink, - TrEvdata, - TrNoop, - CrEnableSlot, - CrDisableSlot, - CrAddressDevice, - CrConfigureEndpoint, - CrEvaluateContext, - CrResetEndpoint, - CrStopEndpoint, - CrSetTrDequeue, - CrResetDevice, - CrForceEvent, - CrNegotiateBw, - CrSetLatencyTolerance, - CrGetPortBandwidth, - CrForceHeader, - CrNoop, - ErTransfer = 32, - ErCommandComplete, - ErPortStatusChange, - ErBandwidthRequest, - ErDoorbell, - ErHostController, - ErDeviceNotification, - ErMfindexWrap, - Unknown, -} - -impl From for TRBType { - fn from(t: u32) -> TRBType { - match t { - 0 => TRBType::TrbReserved, - 1 => TRBType::TrNormal, - 2 => TRBType::TrSetup, - 3 => TRBType::TrData, - 4 => TRBType::TrStatus, - 5 => TRBType::TrIsoch, - 6 => TRBType::TrLink, - 7 => TRBType::TrEvdata, - 8 => TRBType::TrNoop, - 9 => TRBType::CrEnableSlot, - 10 => TRBType::CrDisableSlot, - 11 => TRBType::CrAddressDevice, - 12 => TRBType::CrConfigureEndpoint, - 13 => TRBType::CrEvaluateContext, - 14 => TRBType::CrResetEndpoint, - 15 => TRBType::CrStopEndpoint, - 16 => TRBType::CrSetTrDequeue, - 17 => TRBType::CrResetDevice, - 18 => TRBType::CrForceEvent, - 19 => TRBType::CrNegotiateBw, - 20 => TRBType::CrSetLatencyTolerance, - 21 => TRBType::CrGetPortBandwidth, - 22 => TRBType::CrForceHeader, - 23 => TRBType::CrNoop, - 32 => TRBType::ErTransfer, - 33 => TRBType::ErCommandComplete, - 34 => TRBType::ErPortStatusChange, - 35 => TRBType::ErBandwidthRequest, - 36 => TRBType::ErDoorbell, - 37 => TRBType::ErHostController, - 38 => TRBType::ErDeviceNotification, - 39 => TRBType::ErMfindexWrap, - _ => TRBType::Unknown, - } - } -} - -/// TRB Completion Code. See the spec 6.4.5 TRB Completion Codes. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TRBCCode { - Invalid = 0, - Success, - DataBufferError, - BabbleDetected, - UsbTransactionError, - TrbError, - StallError, - ResourceError, - BandwidthError, - NoSlotsError, - InvalidStreamTypeError, - SlotNotEnabledError, - EpNotEnabledError, - ShortPacket, - RingUnderrun, - RingOverrun, - VfErFull, - ParameterError, - BandwidthOverrun, - ContextStateError, - NoPingResponseError, - EventRingFullError, - IncompatibleDeviceError, - MissedServiceError, - CommandRingStopped, - CommandAborted, - Stopped, - StoppedLengthInvalid, - MaxExitLatencyTooLargeError = 29, - IsochBufferOverrun = 31, - EventLostError, - UndefinedError, - InvalidStreamIdError, - SecondaryBandwidthError, - SplitTransactionError, -} - type DmaAddr = u64; /// XHCI Transfer Request Block -- Gitee From 66778077cb4a97d20f01617ff40eeba408020f71 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 22 Feb 2023 16:43:03 +0800 Subject: [PATCH 0842/2187] usb: support send input event by qmp Support send input event by qmp for MST. Signed-off-by: zhouli57 --- machine/src/standard_vm/mod.rs | 45 ++++++++++++++++- machine_manager/src/machine.rs | 5 ++ machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 69 +++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d8bec2bb4..90bdf05d0 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -28,7 +28,10 @@ use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOpera use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; #[cfg(not(target_env = "musl"))] -use vnc::vnc::qmp_query_vnc; +use vnc::{ + input::{key_event, point_event}, + vnc::qmp_query_vnc, +}; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; @@ -1596,4 +1599,44 @@ impl DeviceInterface for StdMachine { Response::create_empty_response() } + + #[cfg(not(target_env = "musl"))] + fn input_event(&self, key: String, value: String) -> Response { + match send_input_event(key, value) { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + } + } +} + +#[cfg(not(target_env = "musl"))] +fn send_input_event(key: String, value: String) -> Result<()> { + match key.as_str() { + "keyboard" => { + let vec: Vec<&str> = value.split(',').collect(); + if vec.len() != 2 { + bail!("Invalid keyboard format: {}", value); + } + let keycode = vec[0].parse::()?; + let down = vec[1].parse::()? == 1; + key_event(keycode, down)?; + } + "pointer" => { + let vec: Vec<&str> = value.split(',').collect(); + if vec.len() != 3 { + bail!("Invalid pointer format: {}", value); + } + let x = vec[0].parse::()?; + let y = vec[1].parse::()?; + let btn = vec[2].parse::()?; + point_event(btn, x, y)?; + } + _ => { + bail!("Invalid input type: {}", key); + } + }; + Ok(()) } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index ed0f589f7..64d540445 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -420,6 +420,11 @@ pub trait DeviceInterface { } fn update_region(&mut self, args: UpdateRegionArgument) -> Response; + + // Send event to input device for testing only. + fn input_event(&self, _k: String, _v: String) -> Response { + Response::create_empty_response() + } } /// Migrate external api diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 5559d539b..472961707 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -453,6 +453,7 @@ fn qmp_command_exec( (query_vnc, query_vnc), (list_type, list_type), (query_hotpluggable_cpus, query_hotpluggable_cpus); + (input_event, input_event, key, value), (device_list_properties, device_list_properties, typename), (device_del, device_del, id), (blockdev_del, blockdev_del, node_name), diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 2e5c7809a..6573f164a 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -387,6 +387,12 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + input_event { + #[serde(default)] + arguments: input_event, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, } /// qmp_capabilities @@ -2063,6 +2069,33 @@ impl Command for query_iothreads { Default::default() } } +/// input_event +/// +/// # Arguments +/// +/// * `key` - the input type such as 'keyboard' or 'pointer'. +/// * `value` - the input value. + +/// # Examples +/// +/// ```text +/// -> { "execute": "input_event", +/// "arguments": { "key": "pointer", "value": "100,200,1" }} +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct input_event { + pub key: String, + pub value: String, +} + +impl Command for input_event { + type Res = Vec; + + fn back(self) -> Vec { + Default::default() + } +} #[cfg(test)] mod tests { @@ -2527,4 +2560,40 @@ mod tests { let part_msg = r#"ok"#; assert!(err_msg.contains(part_msg)); } + + #[test] + fn test_qmp_input_event() { + // key event + let json_msg = r#" + { + "execute": "input_event" , + "arguments": { + "key": "keyboard", + "value": "2,1" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"ok"#; + assert!(err_msg.contains(part_msg)); + // pointer event + let json_msg = r#" + { + "execute": "input_event" , + "arguments": { + "key": "pointer", + "value": "4,5,1" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"ok"#; + assert!(err_msg.contains(part_msg)); + } } -- Gitee From 3628067eb8de977ae13c47bf4d052905badce651 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 23 Feb 2023 15:09:03 +0800 Subject: [PATCH 0843/2187] mst: add usb test Signed-off-by: zhouli57 --- Cargo.lock | 1 + tests/mod_test/Cargo.toml | 2 +- tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/pci.rs | 13 +- tests/mod_test/src/libdriver/usb.rs | 1975 +++++++++++++++ .../src/libdriver/virtio_pci_modern.rs | 4 +- tests/mod_test/tests/balloon_test.rs | 4 +- tests/mod_test/tests/block_test.rs | 2 +- tests/mod_test/tests/net_test.rs | 6 +- tests/mod_test/tests/usb_test.rs | 2214 +++++++++++++++++ tests/mod_test/tests/virtio_test.rs | 14 +- usb/src/xhci/xhci_regs.rs | 8 +- 12 files changed, 4222 insertions(+), 22 deletions(-) create mode 100644 tests/mod_test/src/libdriver/usb.rs create mode 100644 tests/mod_test/tests/usb_test.rs diff --git a/Cargo.lock b/Cargo.lock index a71943f1d..86a8c0ff9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,6 +541,7 @@ dependencies = [ "machine", "rand", "serde_json", + "usb", "util", "virtio", "vmm-sys-util", diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index e267749b6..7ed441d33 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -17,4 +17,4 @@ util = { path = "../../util" } acpi = { path = "../../acpi" } machine = { path = "../../machine" } virtio = { path = "../../virtio"} - +usb = { path = "../../usb" } diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index b9cb533a1..dd2189ea0 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -15,6 +15,7 @@ pub mod machine; pub mod malloc; pub mod pci; pub mod pci_bus; +pub mod usb; pub mod virtio; pub mod virtio_block; pub mod virtio_console; diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 23eb08cc7..4a1534589 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -107,7 +107,12 @@ impl TestPciDev { addr } - pub fn enable_msix(&mut self) { + /// Enable MSI-X. + /// + /// # Arguments + /// + /// `bar_addr` - Address of the bar where the MSI-X is located. Address allocated by Default. + pub fn enable_msix(&mut self, bar_addr: Option) { let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); assert!(addr != 0); let value = self.config_readw(addr + PCI_MSIX_FLAGS); @@ -115,7 +120,11 @@ impl TestPciDev { let table = self.config_readl(addr + PCI_MSIX_TABLE); let bar_table = table & PCI_MSIX_TABLE_BIR; - self.msix_table_bar = self.io_map(bar_table.try_into().unwrap()); + self.msix_table_bar = if let Some(addr) = bar_addr { + addr + } else { + self.io_map(bar_table.try_into().unwrap()) + }; self.msix_table_off = (table & !PCI_MSIX_TABLE_BIR).try_into().unwrap(); let table = self.config_readl(addr + PCI_MSIX_PBA); diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs new file mode 100644 index 000000000..70dc06719 --- /dev/null +++ b/tests/mod_test/src/libdriver/usb.rs @@ -0,0 +1,1975 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + cell::{RefCell, RefMut}, + collections::{HashMap, LinkedList}, + mem::size_of, + rc::Rc, + time::Duration, +}; + +use byteorder::{ByteOrder, LittleEndian}; + +use super::{ + machine::TestStdMachine, + malloc::GuestAllocator, + pci::{PCIBarAddr, TestPciDev, PCI_VENDOR_ID}, + pci_bus::TestPciBus, +}; +use crate::libdriver::pci::{PciMsixOps, PCI_DEVICE_ID}; +use crate::libtest::{test_init, TestState}; +use usb::{ + config::*, + hid::{ + HID_GET_IDLE, HID_GET_PROTOCOL, HID_GET_REPORT, HID_SET_IDLE, HID_SET_PROTOCOL, + HID_SET_REPORT, + }, + usb::UsbDeviceRequest, + xhci::{ + xhci_controller::{ + DwordOrder, XhciEpCtx, XhciInputCtrlCtx, XhciSlotCtx, EP_RUNNING, SLOT_DEFAULT, + }, + xhci_regs::{ + XHCI_INTR_REG_ERDP_LO, XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, + XHCI_INTR_REG_IMAN, XHCI_INTR_REG_SIZE, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_PAGESIZE, + XHCI_OPER_REG_USBCMD, XHCI_OPER_REG_USBSTS, + }, + TRBCCode, TRBType, TRB_SIZE, + }, +}; + +pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; +pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; +pub const PCI_CLASS_PI: u8 = 0x9; +pub const SUB_CLASS_CODE: u8 = 0xa; +pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; + +pub const XHCI_PCI_CAP_OFFSET: u32 = 0; +pub const XHCI_PCI_OPER_OFFSET: u32 = 0x40; +pub const XHCI_PCI_PORT_OFFSET: u32 = 0x440; +pub const XHCI_PCI_PORT_LENGTH: u32 = 0x10; +pub const XHCI_PCI_RUNTIME_OFFSET: u32 = 0x1000; +pub const XHCI_PCI_DOORBELL_OFFSET: u32 = 0x2000; +pub const XHCI_PORTSC_OFFSET: u64 = 0x0; +pub const XHCI_OPER_REG_DCBAAP: u64 = 0x30; +pub const XHCI_OPER_REG_CMD_RING_CTRL: u64 = 0x18; + +pub const USB_CONFIG_MAX_SLOTS_EN_MASK: u32 = 0xff; +const DEVICE_CONTEXT_ENTRY_SIZE: u32 = 0x8; +const EVENT_RING_SEGMENT_TABLE_ENTRY_SIZE: u32 = 0x10; +const RUNTIME_REGS_INTERRUPT_OFFSET: u64 = 0x20; +const PORT_EVENT_ID_SHIFT: u32 = 24; +const PORT_EVENT_ID_MASK: u32 = 0xff; +// TRB +const TRB_INTR_SHIFT: u32 = 22; +const TRB_INTR_MASK: u32 = 0x3ff; +const TRB_C: u32 = 1; +const TRB_TYPE_SHIFT: u32 = 10; +const TRB_TYPE_MASK: u32 = 0x3f; +const TRB_SLOT_ID_SHIFT: u32 = 24; +const TRB_SLOT_ID_MASK: u32 = 0xff; +const TRB_EP_ID_SHIFT: u32 = 16; +const TRB_EP_ID_MASK: u32 = 0x1f; +const TRB_BSR_SHIFT: u32 = 9; +const TRB_BSR_MASK: u32 = 0x1; +const TRB_TD_SIZE_SHIFT: u32 = 9; +const TRB_TD_SIZE_MASK: u32 = 0x1; +const TRB_TRANSFER_LENGTH_SHIFT: u32 = 0; +const TRB_TRANSFER_LENGTH_MASK: u32 = 0xffff; +const TRB_IOC_SHIFT: u32 = 5; +const TRB_IOC_MASK: u32 = 0x1; +const TRB_CH_SHIFT: u32 = 4; +const TRB_CH_MASK: u32 = 0x1; +const TRB_IDT_SHIFT: u32 = 6; +const TRB_IDT_MASK: u32 = 0x1; +const TRB_ISP_SHIFT: u32 = 2; +const TRB_ISP_MASK: u32 = 0x1; +const TRB_DIR_SHIFT: u32 = 16; +const TRB_DIR_MASK: u32 = 0x1; +const TRB_TRT_SHIFT: u32 = 16; +const TRB_TRT_MASK: u32 = 0x1; +const TRB_TC_SHIFT: u32 = 1; +const TRB_TC_MASK: u32 = 0x1; +const TRB_DC_SHIFT: u32 = 9; +const TRB_DC_MASK: u32 = 0x1; +const DEVICE_CONTEXT_SIZE: u64 = 0x400; +const INPUT_CONTEXT_SIZE: u64 = 0x420; +pub const CONTROL_ENDPOINT_ID: u32 = 1; +pub const HID_KEYBOARD_LEN: u64 = 8; +pub const HID_POINTER_LEN: u64 = 6; +pub const KEYCODE_SPACE: u32 = 57; +pub const KEYCODE_NUM1: u32 = 2; +// Descriptor type +pub const USB_DESCRIPTOR_TYPE_DEVICE: u8 = 1; +pub const USB_DESCRIPTOR_TYPE_CONFIG: u8 = 2; +pub const USB_DESCRIPTOR_TYPE_STRING: u8 = 3; +pub const USB_DESCRIPTOR_TYPE_INTERFACE: u8 = 4; +pub const USB_DESCRIPTOR_TYPE_ENDPOINT: u8 = 5; +// Test config +pub const USB_CONFIG_MAX_SLOTS_ENABLED: u32 = 4; +pub const USB_CONFIG_MAX_INTERRUPTER: u32 = 4; +pub const COMMAND_RING_LEN: u64 = 256; +pub const EVENT_RING_SEGMENT_TABLE_LEN: u32 = 1; +pub const EVENT_RING_LEN: u64 = 64; +pub const TRANSFER_RING_LEN: u64 = 256; +// Max TRB limit in one TD. +pub const TD_TRB_LIMIT: u64 = 0x20000 + 10; +// The USB keyboard and tablet intr endpoint id. +pub const HID_DEVICE_ENDPOINT_ID: u32 = 3; +// Primary Interrupter +pub const PRIMARY_INTERRUPTER_ID: usize = 0; +pub const XHCI_PCI_SLOT_NUM: u8 = 0x5; +pub const XHCI_PCI_FUN_NUM: u8 = 0; + +#[derive(Debug, Default, Copy, Clone)] +pub struct TestNormalTRB { + parameter: u64, + status: u32, + control: u32, + // Force mismatch cycle + pub force_cycle: bool, +} + +impl TestNormalTRB { + pub fn generate_setup_td(device_req: &UsbDeviceRequest) -> TestNormalTRB { + let mut setup_trb = TestNormalTRB::default(); + setup_trb.parameter = (device_req.length as u64) << 48 + | (device_req.index as u64) << 32 + | (device_req.value as u64) << 16 + | (device_req.request as u64) << 8 + | device_req.request_type as u64; + setup_trb.set_idt_flag(true); + setup_trb.set_ch_flag(true); + setup_trb.set_trb_type(TRBType::TrSetup as u32); + setup_trb.set_trb_transfer_length(8); + setup_trb.set_transfer_type(3); + setup_trb + } + + pub fn generate_data_td(ptr: u64, len: u16, in_dir: bool) -> TestNormalTRB { + let mut data_trb = TestNormalTRB::default(); + data_trb.set_pointer(ptr); + data_trb.set_ch_flag(true); + data_trb.set_dir_flag(in_dir); + data_trb.set_trb_type(TRBType::TrData as u32); + data_trb.set_trb_transfer_length(len as u32); + data_trb + } + + pub fn generate_status_td(dir: bool) -> TestNormalTRB { + let mut status_trb = TestNormalTRB::default(); + status_trb.set_ch_flag(false); + status_trb.set_ioc_flag(true); + status_trb.set_dir_flag(dir); + status_trb.set_trb_type(TRBType::TrStatus as u32); + status_trb + } + + pub fn generate_event_data_trb(ptr: u64) -> TestNormalTRB { + let mut ev_data_trb = TestNormalTRB::default(); + ev_data_trb.set_pointer(ptr); + ev_data_trb.set_ioc_flag(true); + ev_data_trb.set_trb_type(TRBType::TrEvdata as u32); + ev_data_trb + } + + pub fn set_interrupter_target(&mut self, v: u32) { + self.status &= !(TRB_INTR_MASK << TRB_INTR_SHIFT); + self.status |= (v & TRB_INTR_MASK) << TRB_INTR_SHIFT; + } + + pub fn set_cycle_bit(&mut self, v: bool) { + if v { + self.control |= TRB_C; + } else { + self.control &= !TRB_C; + } + } + + pub fn set_trb_type(&mut self, v: u32) { + self.control &= !(TRB_TYPE_MASK << TRB_TYPE_SHIFT); + self.control |= (v & TRB_TYPE_MASK) << TRB_TYPE_SHIFT; + } + + pub fn set_slot_id(&mut self, v: u32) { + self.control &= !(TRB_SLOT_ID_MASK << TRB_SLOT_ID_SHIFT); + self.control |= (v & TRB_SLOT_ID_MASK) << TRB_SLOT_ID_SHIFT; + } + + pub fn set_ep_id(&mut self, v: u32) { + self.control &= !(TRB_EP_ID_MASK << TRB_EP_ID_SHIFT); + self.control |= (v & TRB_EP_ID_MASK) << TRB_EP_ID_SHIFT; + } + + pub fn set_bsr(&mut self, v: bool) { + self.control &= !(TRB_BSR_MASK << TRB_BSR_SHIFT); + self.control |= (if v { 1 } else { 0 } & TRB_BSR_MASK) << TRB_BSR_SHIFT; + } + + fn to_xhci_event(&self) -> TestXhciEvent { + let mut evt = TestXhciEvent::default(); + evt.ptr = self.parameter; + evt.ccode = (self.status >> 24) & 0xff; + evt.length = self.status & 0xffffff; + evt.flags = self.control; + evt + } + + pub fn set_pointer(&mut self, dequeue: u64) { + self.parameter = dequeue; + } + + pub fn set_td_size(&mut self, sz: u32) { + self.status &= !(TRB_TD_SIZE_MASK << TRB_TD_SIZE_SHIFT); + self.status |= (sz & TRB_TD_SIZE_MASK) << TRB_TD_SIZE_SHIFT; + } + + pub fn set_trb_transfer_length(&mut self, len: u32) { + self.status &= !(TRB_TRANSFER_LENGTH_MASK << TRB_TRANSFER_LENGTH_SHIFT); + self.status |= (len & TRB_TRANSFER_LENGTH_MASK) << TRB_TRANSFER_LENGTH_SHIFT; + } + + pub fn set_ioc_flag(&mut self, v: bool) { + if v { + self.control |= TRB_IOC_MASK << TRB_IOC_SHIFT; + } else { + self.control &= !(TRB_IOC_MASK << TRB_IOC_SHIFT); + } + } + + pub fn set_ch_flag(&mut self, v: bool) { + if v { + self.control |= TRB_CH_MASK << TRB_CH_SHIFT; + } else { + self.control &= !(TRB_CH_MASK << TRB_CH_SHIFT); + } + } + + pub fn set_idt_flag(&mut self, v: bool) { + if v { + self.control |= TRB_IDT_MASK << TRB_IDT_SHIFT; + } else { + self.control &= !(TRB_IDT_MASK << TRB_IDT_SHIFT); + } + } + + pub fn set_isp_flag(&mut self, v: bool) { + if v { + self.control |= TRB_ISP_MASK << TRB_ISP_SHIFT; + } else { + self.control &= !(TRB_ISP_MASK << TRB_ISP_SHIFT); + } + } + + pub fn set_dir_flag(&mut self, v: bool) { + if v { + self.control |= TRB_DIR_MASK << TRB_DIR_SHIFT; + } else { + self.control &= !(TRB_DIR_MASK << TRB_DIR_SHIFT); + } + } + + pub fn set_transfer_type(&mut self, v: u32) { + self.control &= !(TRB_TRT_MASK << TRB_TRT_SHIFT); + self.control |= (v & TRB_TRT_MASK) << TRB_TRT_SHIFT; + } + + pub fn set_toggle_cycle(&mut self, v: bool) { + if v { + self.control |= TRB_TC_MASK << TRB_TC_SHIFT; + } else { + self.control &= !(TRB_TC_MASK << TRB_TC_SHIFT); + } + } + + pub fn set_dc_flag(&mut self, v: bool) { + self.control &= !(TRB_DC_MASK << TRB_DC_SHIFT); + self.control |= (if v { 1 } else { 0 } & TRB_DC_MASK) << TRB_DC_SHIFT; + } +} + +#[derive(Default)] +pub struct TestXhciEvent { + pub ccode: u32, + pub ptr: u64, + pub length: u32, + flags: u32, +} + +impl TestXhciEvent { + pub fn get_port_id(&self) -> u32 { + (self.ptr as u32) >> PORT_EVENT_ID_SHIFT & PORT_EVENT_ID_MASK + } + + pub fn get_slot_id(&self) -> u32 { + (self.flags >> TRB_SLOT_ID_SHIFT) & TRB_SLOT_ID_MASK + } + + pub fn get_ep_id(&self) -> u32 { + (self.flags >> TRB_EP_ID_SHIFT) & TRB_EP_ID_MASK + } + + pub fn get_trb_type(&self) -> u32 { + (self.flags >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK + } +} + +#[derive(Default, Debug, Copy, Clone)] +struct TestXhciRing { + pointer: u64, + start: u64, + size: u64, + cycle_bit: bool, +} + +impl TestXhciRing { + fn new() -> Self { + Self { + pointer: 0, + start: 0, + size: 0, + cycle_bit: true, + } + } + + fn init(&mut self, addr: u64, sz: u64) { + self.pointer = addr; + self.start = addr; + self.size = sz; + self.cycle_bit = true; + } + + fn update_pointer(&mut self, addr: u64) { + self.pointer = addr; + } + + fn increase_pointer(&mut self, sz: u64) { + self.pointer += sz; + } +} + +pub struct TestEventRingSegment { + pub addr: u64, + pub size: u32, + pub reserved: u32, +} + +impl TestEventRingSegment { + pub fn new() -> Self { + Self { + addr: 0, + size: 0, + reserved: 0, + } + } + + pub fn init(&mut self, addr: u64, sz: u32) { + self.addr = addr; + self.size = sz; + } +} + +#[derive(Default, Clone, Copy)] +struct TestXhciInterrupter { + erstsz: u32, + erstba: u64, + segment_index: u32, + cycle_bit: bool, + er_pointer: u64, + trb_count: u32, +} + +#[derive(Default, Clone, Copy)] +struct DeviceSlot { + endpoints: [EndpointContext; 31], +} + +#[derive(Default, Clone, Copy)] +struct EndpointContext { + transfer_ring: TestXhciRing, +} + +// Iovec for test transfer. +#[derive(Default, Clone, Copy)] +pub struct TestIovec { + pub io_base: u64, + pub io_len: usize, + pub direct: bool, + // Whether the trb is event data trb. + pub event_data: bool, +} + +impl TestIovec { + pub fn new(base: u64, len: usize, direct: bool) -> Self { + Self { + io_base: base, + io_len: len, + direct, + event_data: false, + } + } +} + +struct TestXhciDevice { + cmd_ring: TestXhciRing, + dcbaap: u64, + device_slot: Vec, + interrupter: Vec, +} + +impl TestXhciDevice { + fn new() -> Self { + Self { + cmd_ring: TestXhciRing::new(), + dcbaap: 0, + device_slot: vec![DeviceSlot::default(); (USB_CONFIG_MAX_SLOTS_ENABLED + 1) as usize], + interrupter: vec![TestXhciInterrupter::default(); USB_CONFIG_MAX_INTERRUPTER as usize], + } + } +} + +pub struct TestXhciPciDevice { + pub pci_dev: TestPciDev, + pub bar_addr: PCIBarAddr, + bar_idx: u8, + allocator: Rc>, + xhci: TestXhciDevice, + pub device_config: HashMap, + // Event list to save all ready event when has msix. + event_list: LinkedList, + // msix config + config_msix_entry: u16, + config_msix_addr: u64, + config_msix_data: u32, +} + +impl TestXhciPciDevice { + pub fn new(pci_bus: Rc>, allocator: Rc>) -> Self { + Self { + pci_dev: TestPciDev::new(pci_bus), + bar_addr: 0, + bar_idx: 0, + allocator, + xhci: TestXhciDevice::new(), + device_config: HashMap::new(), + event_list: LinkedList::new(), + config_msix_entry: 0, + config_msix_addr: 0, + config_msix_data: 0, + } + } + + pub fn run(&mut self) { + let status = self.pci_dev.io_readl( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + ); + assert!(status & USB_STS_HCH == USB_STS_HCH); + let cmd = self.pci_dev.io_readl( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBCMD as u64, + ); + self.pci_dev.io_writel( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBCMD as u64, + cmd | USB_CMD_RUN, + ); + let status = self.pci_dev.io_readl( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + ); + assert!(status & USB_STS_HCH != USB_STS_HCH); + } + + /// Reference 4.2 Host Controller initialization. + pub fn init_host_controller(&mut self, pci_slot: u8, pci_fn: u8) { + self.init_pci_device(pci_slot, pci_fn); + self.read_pci_config(); + self.read_capability(); + self.init_memory(); + self.init_max_device_slot_enabled(); + self.init_device_context_base_address_array_pointer(); + self.init_command_ring_dequeue_pointer(); + self.init_interrupter(); + } + + pub fn init_device(&mut self, port_id: u32) -> u32 { + // reset usb port + self.reset_port(port_id); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + self.enable_slot(); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + if *self + .device_config + .get("address_device_bsr") + .unwrap_or(&false) + { + // address device bsr = 1 + let slot_id = evt.get_slot_id(); + self.address_device(slot_id, true, port_id); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_ctx = self.get_slot_context(slot_id); + assert_eq!(slot_ctx.get_slot_state(), SLOT_DEFAULT); + let ep0_ctx = self.get_endpoint_context(slot_id, CONTROL_ENDPOINT_ID); + assert_eq!(ep0_ctx.get_ep_state(), EP_RUNNING); + // reset device + self.reset_device(slot_id); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_ctx = self.get_slot_context(slot_id); + assert_eq!(slot_ctx.get_slot_state(), SLOT_DEFAULT); + assert_eq!(slot_ctx.get_usb_device_address(), 0); + } + // address device bsr = 0 + let slot_id = evt.get_slot_id(); + self.address_device(slot_id, false, port_id); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get descriptor + self.get_usb_descriptor(slot_id); + // get hid report descriptor + self.check_hid_report_descriptor(slot_id); + // evaluate context + self.evaluate_context(slot_id, 0x1234, 0, 64); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_ctx = self.get_slot_context(slot_id); + assert_eq!(slot_ctx.get_max_exit_latency(), 0x1234); + assert_eq!(slot_ctx.get_interrupter_target(), 0); + let ep0_ctx = self.get_endpoint_context(slot_id, CONTROL_ENDPOINT_ID); + assert_eq!(ep0_ctx.get_max_packet_size(), 64); + // get configuration + self.get_configuration(slot_id); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 1); + assert_eq!(buf[0], 0); + // configure endpoint + self.configure_endpoint(slot_id, false); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // set feature + self.set_feature(slot_id, USB_DEVICE_REMOTE_WAKEUP as u16); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get status + self.get_status(slot_id); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - 16, 2); + assert_eq!(buf, [2, 0]); + slot_id + } + + /// Rest host controller. + pub fn reset_controller(&mut self, auto_run: bool) { + // reset xhci + self.oper_regs_write(0, USB_CMD_HCRST); + let status = self.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE != USB_STS_HCE); + if auto_run { + self.init_host_controller(XHCI_PCI_SLOT_NUM, XHCI_PCI_FUN_NUM); + self.run(); + } else { + self.init_pci_device(XHCI_PCI_SLOT_NUM, XHCI_PCI_FUN_NUM); + } + // clean up the event list. + self.event_list.clear(); + } + + // Low Level API + + pub fn oper_regs_read(&self, offset: u64) -> u32 { + self.pci_dev + .io_readl(self.bar_addr, XHCI_PCI_OPER_OFFSET as u64 + offset) + } + + pub fn oper_regs_write(&mut self, offset: u64, value: u32) { + self.pci_dev + .io_writel(self.bar_addr, XHCI_PCI_OPER_OFFSET as u64 + offset, value); + } + + pub fn interrupter_regs_read(&self, intr_idx: u64, offset: u64) -> u32 { + self.pci_dev.io_readl( + self.bar_addr, + XHCI_PCI_RUNTIME_OFFSET as u64 + + XHCI_INTR_REG_SIZE + + intr_idx * XHCI_INTR_REG_SIZE + + offset, + ) + } + + pub fn interrupter_regs_write(&mut self, intr_idx: u64, offset: u64, value: u32) { + self.pci_dev.io_writel( + self.bar_addr, + XHCI_PCI_RUNTIME_OFFSET as u64 + + RUNTIME_REGS_INTERRUPT_OFFSET + + intr_idx * XHCI_INTR_REG_SIZE + + offset, + value, + ); + } + + pub fn interrupter_regs_readq(&self, intr_idx: u64, offset: u64) -> u64 { + self.pci_dev.io_readq( + self.bar_addr, + XHCI_PCI_RUNTIME_OFFSET as u64 + + XHCI_INTR_REG_SIZE + + intr_idx * XHCI_INTR_REG_SIZE + + offset, + ) + } + + pub fn interrupter_regs_writeq(&mut self, intr_idx: u64, offset: u64, value: u64) { + self.pci_dev.io_writeq( + self.bar_addr, + XHCI_PCI_RUNTIME_OFFSET as u64 + + RUNTIME_REGS_INTERRUPT_OFFSET + + intr_idx * XHCI_INTR_REG_SIZE + + offset, + value, + ); + } + + pub fn port_regs_read(&self, port_id: u32, offset: u64) -> u32 { + self.pci_dev.io_readl( + self.bar_addr, + (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) as u64 + offset, + ) + } + + pub fn port_regs_write(&mut self, port_id: u32, offset: u64, value: u32) { + self.pci_dev.io_writel( + self.bar_addr, + (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) as u64 + offset, + value, + ); + } + + pub fn doorbell_write(&mut self, slot_id: u32, target: u32) { + self.pci_dev.io_writel( + self.bar_addr, + XHCI_PCI_DOORBELL_OFFSET as u64 + (slot_id << 2) as u64, + target, + ); + } + + pub fn init_pci_device(&mut self, pci_slot: u8, pci_fn: u8) { + let devfn = (pci_slot << 3 | pci_fn) as u32; + assert!(self.find_pci_device(devfn)); + + self.pci_dev.enable(); + self.bar_addr = self.pci_dev.io_map(self.bar_idx); + } + + pub fn read_pci_config(&self) { + let vendor_id = self.pci_dev.config_readw(PCI_VENDOR_ID); + assert_eq!(vendor_id, PCI_VENDOR_ID_REDHAT); + // device id + let device_id = self.pci_dev.config_readw(PCI_DEVICE_ID); + assert_eq!(device_id, PCI_DEVICE_ID_REDHAT_XHCI); + // class code + let pi = self.pci_dev.config_readb(PCI_CLASS_PI); + assert_eq!(pi, 0x30); + let class_code = self.pci_dev.config_readw(SUB_CLASS_CODE); + assert_eq!(class_code, PCI_CLASS_SERIAL_USB); + } + + pub fn read_capability(&self) { + // Interface Version Number + let cap = self + .pci_dev + .io_readl(self.bar_addr, XHCI_PCI_CAP_OFFSET as u64); + assert!(cap & 0x01000000 == 0x01000000); + // HCSPARAMS1 + let hcsparams1 = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x4) as u64); + assert_eq!(hcsparams1, 0x08001040); + // HCSPARAMS2 + let hcsparams2 = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x8) as u64); + assert_eq!(hcsparams2, 0xf); + // HCSPARAMS3 + let hcsparams3 = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0xc) as u64); + assert_eq!(hcsparams3, 0); + // HCCPARAMS1 + let hccparams1 = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x10) as u64); + // AC64 = 1 + assert_eq!(hccparams1 & 1, 1); + // HCCPARAMS2 + let hccparams2 = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x1c) as u64); + assert_eq!(hccparams2, 0); + // USB 2.0 + let usb2_version = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x20) as u64); + assert!(usb2_version & 0x02000000 == 0x02000000); + let usb2_name = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x24) as u64); + assert_eq!(usb2_name, 0x20425355); + let usb2_port = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x28) as u64); + assert!(usb2_port & 0x400 == 0x400); + // USB 3.0 + let usb3_version = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x30) as u64); + assert!(usb3_version & 0x03000000 == 0x03000000); + let usb3_name = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x34) as u64); + assert_eq!(usb3_name, 0x20425355); + let usb3_port = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x38) as u64); + assert!(usb3_port & 0x400 == 0x400); + } + + pub fn init_max_device_slot_enabled(&mut self) { + // NOTE: not implement yet. use a fake value. + let enabled_slot = USB_CONFIG_MAX_SLOTS_ENABLED & USB_CONFIG_MAX_SLOTS_EN_MASK; + self.pci_dev.io_writel( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + enabled_slot, + ); + + let config = self.pci_dev.io_readl( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + ); + assert_eq!(config, enabled_slot); + } + + pub fn init_device_context_base_address_array_pointer(&mut self) { + let dcba = DEVICE_CONTEXT_ENTRY_SIZE * (USB_CONFIG_MAX_SLOTS_ENABLED + 1); + let dcbaap = self.allocator.borrow_mut().alloc(dcba as u64); + self.pci_dev.io_writeq( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DCBAAP as u64, + dcbaap, + ); + + let value = self.pci_dev.io_readq( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DCBAAP as u64, + ); + assert_eq!(value, dcbaap); + self.xhci.dcbaap = value; + } + + pub fn init_command_ring_dequeue_pointer(&mut self) { + let cmd_ring_sz = TRB_SIZE as u64 * COMMAND_RING_LEN; + let cmd_ring = self.allocator.borrow_mut().alloc(cmd_ring_sz); + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow_mut() + .memset(cmd_ring, cmd_ring_sz, &[0]); + self.xhci.cmd_ring.init(cmd_ring, cmd_ring_sz); + self.pci_dev.io_writeq( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CMD_RING_CTRL as u64, + cmd_ring, + ); + // Read dequeue pointer return 0. + let cmd_ring = self.pci_dev.io_readq( + self.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CMD_RING_CTRL as u64, + ); + assert_eq!(cmd_ring, 0); + } + + pub fn init_interrupter(&mut self) { + // init event ring. + self.init_event_ring( + PRIMARY_INTERRUPTER_ID, + EVENT_RING_SEGMENT_TABLE_LEN, + EVENT_RING_LEN as u32, + ); + self.init_msix(); + } + + pub fn init_msix(&mut self) { + self.pci_dev.enable_msix(Some(self.bar_addr)); + self.config_msix_entry = 0; + // Random data, which is used only to check whether read and write data are consistent. + self.config_msix_data = 0x12345678; + self.config_msix_addr = self.allocator.borrow_mut().alloc(4); + self.pci_dev.set_msix_vector( + self.config_msix_entry, + self.config_msix_addr, + self.config_msix_data, + ); + } + + pub fn reset_port(&mut self, port_id: u32) { + assert!(port_id > 0); + let port_offset = + (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) as u64; + self.pci_dev.io_writel( + self.bar_addr, + port_offset + XHCI_PORTSC_OFFSET, + PORTSC_PR as u32, + ); + self.oper_regs_write(XHCI_OPER_REG_USBSTS, USB_STS_PCD); + let status = self.oper_regs_read(XHCI_OPER_REG_USBSTS); + assert!(status & USB_STS_PCD != USB_STS_PCD); + } + + pub fn no_op(&mut self) { + let mut trb = TestNormalTRB::default(); + trb.set_interrupter_target(0); + trb.set_trb_type(TRBType::CrNoop as u32); + self.queue_command(&mut trb); + } + + pub fn enable_slot(&mut self) { + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(0); + trb.set_trb_type(TRBType::CrEnableSlot as u32); + self.queue_command(&mut trb); + } + + pub fn disable_slot(&mut self, slot_id: u32) { + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(slot_id); + trb.set_trb_type(TRBType::CrDisableSlot as u32); + self.queue_command(&mut trb); + } + + // Return the address of input context to allow outside modify. + pub fn address_device(&mut self, slot_id: u32, bsr: bool, port_number: u32) -> u64 { + let output_ctx_addr = self.alloc_device_context(); + self.set_device_context_address(slot_id, output_ctx_addr); + let input_ctx_addr = self.alloc_input_context(); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.add_flags |= 0x3; // slot and ep0. + self.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + // Slot context + let mut slot_ctx = XhciSlotCtx::default(); + slot_ctx.set_context_entry(1); + slot_ctx.set_port_number(port_number); + self.mem_write_u32(input_ctx_addr + 0x20, slot_ctx.as_dwords()); + // Endpoint 0 context + let mut ep0_ctx = XhciEpCtx::default(); + let ep0_tr_ring = self + .allocator + .borrow_mut() + .alloc(TRB_SIZE as u64 * TRANSFER_RING_LEN); + ep0_ctx.set_tr_dequeue_pointer(ep0_tr_ring | 1); + ep0_ctx.set_ep_state(0); + ep0_ctx.set_ep_type(4); + self.mem_write_u32(input_ctx_addr + 0x40, ep0_ctx.as_dwords()); + self.xhci.device_slot[slot_id as usize].endpoints[(CONTROL_ENDPOINT_ID - 1) as usize] + .transfer_ring + .init(ep0_tr_ring, TRB_SIZE as u64 * TRANSFER_RING_LEN); + + let mut trb = TestNormalTRB::default(); + trb.parameter = input_ctx_addr; + trb.set_trb_type(TRBType::CrAddressDevice as u32); + trb.set_slot_id(slot_id); + trb.set_bsr(bsr); + self.queue_command(&mut trb); + input_ctx_addr + } + + // Return the address of input context to allow outside modify. + pub fn configure_endpoint(&mut self, slot_id: u32, dc: bool) -> u64 { + let output_ctx_addr = self.get_device_context_address(slot_id); + // Input context. + let input_ctx_addr = self.alloc_input_context(); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.add_flags |= 0x1 | 1 << HID_DEVICE_ENDPOINT_ID; + input_ctx.drop_flags = 1 << HID_DEVICE_ENDPOINT_ID; + self.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + // Slot context. + let mut slot_ctx = XhciSlotCtx::default(); + self.mem_read_u32(output_ctx_addr, slot_ctx.as_mut_dwords()); + slot_ctx.set_context_entry(4); + self.mem_write_u32(input_ctx_addr + 0x20, slot_ctx.as_dwords()); + // Endpoint context. + let mut ep_ctx = XhciEpCtx::default(); + let tr_ring_size = if *self + .device_config + .get("over_transfer_ring") + .unwrap_or(&false) + { + TD_TRB_LIMIT + } else { + TRB_SIZE as u64 * TRANSFER_RING_LEN + }; + let ep_tr_ring = self.allocator.borrow_mut().alloc(tr_ring_size); + ep_ctx.set_tr_dequeue_pointer(ep_tr_ring | 1); + ep_ctx.set_interval(10); + ep_ctx.set_ep_state(0); + ep_ctx.set_ep_type(7); + self.mem_write_u32(input_ctx_addr + 0x80, ep_ctx.as_dwords()); + self.xhci.device_slot[slot_id as usize].endpoints[(HID_DEVICE_ENDPOINT_ID - 1) as usize] + .transfer_ring + .init(ep_tr_ring, tr_ring_size); + + let mut trb = TestNormalTRB::default(); + trb.parameter = input_ctx_addr; + trb.set_trb_type(TRBType::CrConfigureEndpoint as u32); + trb.set_slot_id(slot_id); + trb.set_dc_flag(dc); + self.queue_command(&mut trb); + input_ctx_addr + } + + pub fn evaluate_context( + &mut self, + slot_id: u32, + max_exit_latency: u32, + intr_target: u32, + max_pkt_sz: u32, + ) -> u64 { + let input_ctx_addr = self.alloc_input_context(); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.add_flags = 0x1 | 1 << CONTROL_ENDPOINT_ID; + self.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + // Slot context. + let mut slot_ctx = XhciSlotCtx::default(); + slot_ctx.set_max_exit_latency(max_exit_latency); + slot_ctx.set_interrupter_target(intr_target); + self.mem_write_u32(input_ctx_addr + 0x20, slot_ctx.as_dwords()); + // Endpoint 0 context. + let mut ep0_ctx = XhciEpCtx::default(); + ep0_ctx.set_max_packet_size(max_pkt_sz); + self.mem_write_u32(input_ctx_addr + 0x40, ep0_ctx.as_dwords()); + + let mut trb = TestNormalTRB::default(); + trb.set_pointer(input_ctx_addr); + trb.set_slot_id(slot_id); + trb.set_trb_type(TRBType::CrEvaluateContext as u32); + self.queue_command(&mut trb); + input_ctx_addr + } + + pub fn stop_endpoint(&mut self, slot_id: u32, ep_id: u32) { + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(slot_id); + trb.set_ep_id(ep_id); + // NOTE: Suspend flag not supported. + trb.set_trb_type(TRBType::CrStopEndpoint as u32); + self.queue_command(&mut trb); + } + + pub fn reset_endpoint(&mut self, slot_id: u32, ep_id: u32) { + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(slot_id); + trb.set_ep_id(ep_id); + // NOTE: TSP flag not supported. + trb.set_trb_type(TRBType::CrResetEndpoint as u32); + self.queue_command(&mut trb); + } + + pub fn set_tr_dequeue(&mut self, ptr: u64, slot_id: u32, ep_id: u32) { + let mut trb = TestNormalTRB::default(); + if self.get_cycle_bit(slot_id, ep_id) { + trb.set_pointer(ptr | 1); + } else { + trb.set_pointer(ptr); + } + trb.set_slot_id(slot_id); + trb.set_ep_id(ep_id); + trb.set_trb_type(TRBType::CrSetTrDequeue as u32); + self.queue_command(&mut trb); + // update transfer dequeue pointer in the ring together. + self.set_transfer_pointer(ptr, slot_id, ep_id); + } + + pub fn reset_device(&mut self, slot_id: u32) { + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(slot_id); + trb.set_trb_type(TRBType::CrResetDevice as u32); + self.queue_command(&mut trb); + } + + pub fn fetch_event(&mut self, intr_idx: usize) -> Option { + const MSIX_LIMIT: u32 = 4; + for _ in 0..MSIX_LIMIT { + if self.has_msix( + self.config_msix_entry, + self.config_msix_addr, + self.config_msix_data, + ) { + for _ in 0..EVENT_RING_LEN { + let ptr = self.xhci.interrupter[intr_idx].er_pointer; + let trb = self.read_event(ptr); + let event = trb.to_xhci_event(); + if (event.flags & TRB_C == TRB_C) == self.xhci.interrupter[intr_idx].cycle_bit { + let event = trb.to_xhci_event(); + self.increase_event_ring(intr_idx); + self.interrupter_regs_writeq( + intr_idx as u64, + XHCI_INTR_REG_ERDP_LO, + self.xhci.interrupter[intr_idx].er_pointer | ERDP_EHB as u64, + ); + self.event_list.push_back(event); + } else { + break; + } + } + break; + } + std::thread::sleep(Duration::from_millis(50)); + } + self.event_list.pop_front() + } + + pub fn queue_device_reqeust(&mut self, slot_id: u32, device_req: &UsbDeviceRequest) { + // Setup Stage. + let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); + // Data Stage. + let ptr = self.allocator.borrow_mut().alloc(device_req.length as u64); + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); + self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + // Status Stage. + let mut status_trb = TestNormalTRB::generate_status_td(false); + self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + } + + // Queue TD with multi-TRB. + pub fn queue_td_by_iovec( + &mut self, + slot_id: u32, + ep_id: u32, + iovecs: &mut Vec, + dir: bool, + ) { + for i in 0..iovecs.len() { + let iovec = &mut iovecs[i]; + let mut trb = TestNormalTRB::default(); + if iovec.event_data { + trb.set_pointer(iovec.io_base); + trb.set_trb_type(TRBType::TrEvdata as u32); + } else { + if iovec.direct { + trb.set_idt_flag(true); + iovec.io_base = self.get_transfer_pointer(slot_id, ep_id); + } + trb.set_pointer(iovec.io_base); + trb.set_trb_transfer_length(iovec.io_len as u32); + trb.set_dir_flag(dir); + trb.set_trb_type(TRBType::TrNormal as u32); + } + if i != iovecs.len() - 1 { + trb.set_ch_flag(true); + } else { + trb.set_ioc_flag(true); + } + trb.set_cycle_bit(self.get_cycle_bit(slot_id, ep_id)); + let en_ptr = self.get_transfer_pointer(slot_id, ep_id); + self.write_trb(en_ptr, &trb); + self.increase_transfer_ring(slot_id, ep_id, 1); + } + } + + // Queue TD (single TRB) with IDT=1 + pub fn queue_direct_td(&mut self, slot_id: u32, ep_id: u32, len: u64) { + let mut trb = TestNormalTRB::default(); + trb.set_ioc_flag(true); + trb.set_isp_flag(true); + trb.set_idt_flag(true); + trb.set_trb_type(TRBType::TrNormal as u32); + trb.set_trb_transfer_length(len as u32); + self.queue_trb(slot_id, ep_id, &mut trb); + } + + // Queue multi-TD with IDT=1 + pub fn queue_multi_direct_td(&mut self, slot_id: u32, ep_id: u32, sz: u64, num: usize) { + for _ in 0..num { + self.queue_direct_td(slot_id, ep_id, sz); + } + } + + // Queue TD (single TRB) + pub fn queue_indirect_td(&mut self, slot_id: u32, ep_id: u32, sz: u64) -> u64 { + let mut trb = TestNormalTRB::default(); + let ptr = self.allocator.borrow_mut().alloc(sz); + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow_mut() + .memset(ptr, sz, &[0]); + trb.set_pointer(ptr); + trb.set_ioc_flag(true); + trb.set_isp_flag(true); + trb.set_trb_type(TRBType::TrNormal as u32); + trb.set_trb_transfer_length(sz as u32); + self.queue_trb(slot_id, ep_id, &mut trb); + ptr + } + + // Queue multi-TD + pub fn queue_multi_indirect_td(&mut self, slot_id: u32, ep_id: u32, sz: u64, num: usize) { + for _ in 0..num { + self.queue_indirect_td(slot_id, ep_id, sz); + } + } + + pub fn get_transfer_data_by_iovec(&self, iovecs: &Vec) -> Vec { + let mut buf = Vec::new(); + for iov in iovecs.iter() { + let tmp = self.mem_read(iov.io_base, iov.io_len); + for e in tmp.iter() { + buf.push(*e); + } + } + buf + } + + // Read data from parameter directly. + pub fn get_transfer_data_direct(&self, addr: u64, len: u64) -> Vec { + let buf = self.mem_read(addr, len as usize); + buf + } + + // Read data from parameter as address. + pub fn get_transfer_data_indirect(&self, addr: u64, len: u64) -> Vec { + let buf = self.mem_read(addr, 8); + let mem = LittleEndian::read_u64(&buf); + let buf = self.mem_read(mem, len as usize); + buf + } + + pub fn get_transfer_data_indirect_with_offset( + &self, + addr: u64, + len: usize, + offset: u64, + ) -> Vec { + let buf = self.mem_read(addr, 8); + let mem = LittleEndian::read_u64(&buf); + let buf = self.mem_read(mem + offset, len); + buf + } + + pub fn get_command_pointer(&self) -> u64 { + self.xhci.cmd_ring.pointer + } + + pub fn get_transfer_pointer(&self, slot_id: u32, ep_id: u32) -> u64 { + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] + .transfer_ring + .pointer + & !0xf + } + + pub fn get_event_pointer(&self, intr_idx: usize) -> u64 { + self.xhci.interrupter[intr_idx].er_pointer + } + + pub fn set_transfer_pointer(&mut self, ptr: u64, slot_id: u32, ep_id: u32) { + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] + .transfer_ring + .pointer = ptr; + } + + pub fn get_slot_context(&self, slot_id: u32) -> XhciSlotCtx { + let output_ctx_addr = self.get_device_context_address(slot_id); + let mut slot_ctx = XhciSlotCtx::default(); + self.mem_read_u32(output_ctx_addr, slot_ctx.as_mut_dwords()); + slot_ctx + } + + pub fn get_endpoint_context(&self, slot_id: u32, ep_id: u32) -> XhciEpCtx { + let output_ctx_addr = self.get_device_context_address(slot_id); + let mut ep_ctx = XhciEpCtx::default(); + self.mem_read_u32( + output_ctx_addr + 0x20 * ep_id as u64, + ep_ctx.as_mut_dwords(), + ); + ep_ctx + } + + /// Queue one TRB to command ring. + pub fn queue_command(&mut self, trb: &mut TestNormalTRB) { + trb.set_cycle_bit(self.xhci.cmd_ring.cycle_bit); + self.write_trb(self.xhci.cmd_ring.pointer, trb); + self.increase_command_ring(); + if *self + .device_config + .get("command_auto_doorbell") + .unwrap_or(&false) + { + self.doorbell_write(0, 0); + } + } + + /// Queue one TRB to transfer ring. + pub fn queue_trb(&mut self, slot_id: u32, ep_id: u32, trb: &mut TestNormalTRB) { + if trb.force_cycle { + trb.set_cycle_bit(!self.get_cycle_bit(slot_id, ep_id)); + } else { + trb.set_cycle_bit(self.get_cycle_bit(slot_id, ep_id)); + } + let en_ptr = self.get_transfer_pointer(slot_id, ep_id); + self.write_trb(en_ptr, &trb); + self.increase_transfer_ring(slot_id, ep_id, 1); + } + + pub fn queue_link_trb(&mut self, slot_id: u32, ep_id: u32, ptr: u64, tc: bool) { + let mut trb = TestNormalTRB::default(); + trb.parameter = ptr & !0xf; + trb.set_trb_type(TRBType::TrLink as u32); + trb.set_toggle_cycle(tc); + // Command ring + if slot_id == 0 { + trb.set_cycle_bit(self.xhci.cmd_ring.cycle_bit); + self.write_trb(self.xhci.cmd_ring.pointer, &trb); + if tc { + self.xhci.cmd_ring.cycle_bit = !self.xhci.cmd_ring.cycle_bit; + } + self.xhci.cmd_ring.update_pointer(trb.parameter); + } else { + trb.set_cycle_bit( + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] + .transfer_ring + .cycle_bit, + ); + self.write_trb(self.get_transfer_pointer(slot_id, ep_id), &trb); + if tc { + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] + .transfer_ring + .cycle_bit = !self.xhci.device_slot[slot_id as usize].endpoints + [(ep_id - 1) as usize] + .transfer_ring + .cycle_bit; + } + self.set_transfer_pointer(ptr, slot_id, ep_id); + } + } + + pub fn init_event_ring(&mut self, intr_idx: usize, erstsz: u32, ersz: u32) { + // ERSTSZ + self.interrupter_regs_write(intr_idx as u64, XHCI_INTR_REG_ERSTSZ, erstsz); + self.xhci.interrupter[intr_idx].erstsz = erstsz; + let data = self.interrupter_regs_read(intr_idx as u64, XHCI_INTR_REG_ERSTSZ); + assert_eq!(data, erstsz); + // ERSTBA + let table_size = EVENT_RING_SEGMENT_TABLE_ENTRY_SIZE * erstsz; + let evt_ring_seg_table = self.allocator.borrow_mut().alloc(table_size as u64); + self.xhci.interrupter[intr_idx].erstba = evt_ring_seg_table; + // NOTE: Only support one Segment now. + let mut seg = TestEventRingSegment::new(); + let evt_ring_sz = (TRB_SIZE * ersz) as u64; + let evt_ring = self.allocator.borrow_mut().alloc(evt_ring_sz); + seg.init(evt_ring, ersz); + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow_mut() + .memset(evt_ring, evt_ring_sz, &[0]); + + let mut buf = [0_u8; TRB_SIZE as usize]; + LittleEndian::write_u64(&mut buf, seg.addr); + LittleEndian::write_u32(&mut buf[8..], seg.size); + LittleEndian::write_u32(&mut buf[12..], seg.reserved); + self.mem_write(self.xhci.interrupter[intr_idx].erstba, &buf); + // init event ring + self.load_event_segment(intr_idx); + self.xhci.interrupter[intr_idx].cycle_bit = true; + + // Write ERSTBA last, because write it will trigger reset event ring. + self.interrupter_regs_writeq(intr_idx as u64, XHCI_INTR_REG_ERSTBA_LO, evt_ring_seg_table); + let data = self.interrupter_regs_readq(intr_idx as u64, XHCI_INTR_REG_ERSTBA_LO); + assert_eq!(data, evt_ring_seg_table & !0x3f); + // Write ERDP + self.interrupter_regs_writeq( + intr_idx as u64, + XHCI_INTR_REG_ERDP_LO, + self.get_event_pointer(intr_idx), + ); + let data = self.interrupter_regs_readq(intr_idx as u64, XHCI_INTR_REG_ERDP_LO); + assert_eq!(data, self.get_event_pointer(intr_idx)); + + // enable USB_CMD_INTE + let value = self.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); + self.oper_regs_write(XHCI_OPER_REG_USBCMD, value | USB_CMD_INTE); + // enable INTE + let value = self.interrupter_regs_read(intr_idx as u64, XHCI_INTR_REG_IMAN); + self.interrupter_regs_write(intr_idx as u64, XHCI_INTR_REG_IMAN, value | IMAN_IE); + } + + // Fake init memory. + fn init_memory(&mut self) { + let page_size = self.oper_regs_read(XHCI_OPER_REG_PAGESIZE); + assert_eq!(page_size, 1); + } + + fn get_cycle_bit(&self, slot_id: u32, ep_id: u32) -> bool { + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] + .transfer_ring + .cycle_bit + } + + fn increase_event_ring(&mut self, intr_idx: usize) { + self.xhci.interrupter[intr_idx].trb_count -= 1; + self.xhci.interrupter[intr_idx].er_pointer += TRB_SIZE as u64; + if self.xhci.interrupter[intr_idx].trb_count == 0 { + self.xhci.interrupter[intr_idx].segment_index += 1; + if self.xhci.interrupter[intr_idx].segment_index + == self.xhci.interrupter[intr_idx].erstsz + { + self.xhci.interrupter[intr_idx].cycle_bit = + !self.xhci.interrupter[intr_idx].cycle_bit; + self.xhci.interrupter[intr_idx].segment_index = 0; + } + self.load_event_segment(intr_idx); + } + } + + fn load_event_segment(&mut self, intr_idx: usize) { + let idx = self.xhci.interrupter[intr_idx].segment_index; + let evt_seg = self.read_segment_entry(intr_idx, idx); + self.xhci.interrupter[intr_idx].er_pointer = evt_seg.addr; + self.xhci.interrupter[intr_idx].trb_count = evt_seg.size; + } + + fn read_segment_entry(&self, intr_idx: usize, index: u32) -> TestEventRingSegment { + assert!(index <= self.xhci.interrupter[intr_idx].erstsz); + let addr = self.xhci.interrupter[intr_idx].erstba + (TRB_SIZE * index) as u64; + let evt_seg_buf = self.mem_read(addr, TRB_SIZE as usize); + let mut evt_seg = TestEventRingSegment::new(); + evt_seg.addr = LittleEndian::read_u64(&evt_seg_buf); + evt_seg.size = LittleEndian::read_u32(&evt_seg_buf[8..]); + evt_seg.reserved = LittleEndian::read_u32(&evt_seg_buf[12..]); + evt_seg + } + + fn set_devfn(&mut self, devfn: u32) { + self.pci_dev.devfn = devfn; + } + + fn find_pci_device(&mut self, devfn: u32) -> bool { + self.set_devfn(devfn); + if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { + return false; + } + true + } + + fn set_device_context_address(&mut self, slot_id: u32, addr: u64) { + let device_ctx_addr = self.xhci.dcbaap + (slot_id * DEVICE_CONTEXT_ENTRY_SIZE) as u64; + let mut buf = [0_u8; 8]; + LittleEndian::write_u64(&mut buf, addr); + self.mem_write(device_ctx_addr, &buf); + } + + fn get_device_context_address(&self, slot_id: u32) -> u64 { + let device_ctx_addr = self.xhci.dcbaap + (slot_id * DEVICE_CONTEXT_ENTRY_SIZE) as u64; + let mut buf = self.mem_read(device_ctx_addr, 8); + let addr = LittleEndian::read_u64(&mut buf); + addr + } + + fn has_msix(&mut self, msix_entry: u16, msix_addr: u64, msix_data: u32) -> bool { + if self.pci_dev.msix_is_masked(msix_entry) { + return self.pci_dev.msix_is_pending(msix_entry); + } + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow() + .query_msix(msix_addr, msix_data) + } + + fn increase_command_ring(&mut self) { + let cmd_ring = self.xhci.cmd_ring; + if cmd_ring.pointer + TRB_SIZE as u64 >= cmd_ring.start + cmd_ring.size * TRB_SIZE as u64 { + self.queue_link_trb(0, 0, cmd_ring.start, true); + } + self.xhci.cmd_ring.pointer += TRB_SIZE as u64; + } + + fn increase_transfer_ring(&mut self, slot_id: u32, ep_id: u32, len: u64) { + let tr_ring = + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize].transfer_ring; + if tr_ring.pointer + TRB_SIZE as u64 >= tr_ring.start + tr_ring.size * TRB_SIZE as u64 { + self.queue_link_trb(slot_id, ep_id, tr_ring.start, true); + } + self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] + .transfer_ring + .increase_pointer(TRB_SIZE as u64 * len); + } + + fn write_trb(&mut self, addr: u64, trb: &TestNormalTRB) { + let mut buf = [0_u8; TRB_SIZE as usize]; + LittleEndian::write_u64(&mut buf, trb.parameter); + LittleEndian::write_u32(&mut buf[8..], trb.status); + LittleEndian::write_u32(&mut buf[12..], trb.control); + self.mem_write(addr, &buf); + } + + fn read_event(&self, addr: u64) -> TestNormalTRB { + let buf = self.mem_read(addr, 16); + let mut trb = TestNormalTRB::default(); + trb.parameter = LittleEndian::read_u64(&buf); + trb.status = LittleEndian::read_u32(&buf[8..]); + trb.control = LittleEndian::read_u32(&buf[12..]); + trb + } + + fn alloc_input_context(&mut self) -> u64 { + let input_ctx_addr = self.allocator.borrow_mut().alloc(INPUT_CONTEXT_SIZE); + input_ctx_addr + } + + fn alloc_device_context(&mut self) -> u64 { + let output_ctx_addr = self.allocator.borrow_mut().alloc(DEVICE_CONTEXT_SIZE); + output_ctx_addr + } +} + +// Descriptor +impl TestXhciPciDevice { + pub fn get_usb_descriptor(&mut self, slot_id: u32) { + // device descriptor + self.get_device_descriptor(slot_id); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = + self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, USB_DT_DEVICE_SIZE as u64); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_DEVICE); + // bcdUSB + assert_eq!(buf[3..5], [1, 0]); + // config descriptor + self.get_config_descriptor(slot_id); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let addr = evt.ptr - TRB_SIZE as u64; + let mut offset = 0; + let buf = + self.get_transfer_data_indirect_with_offset(addr, USB_DT_CONFIG_SIZE as usize, offset); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_CONFIG); + // configure value + assert_eq!(buf[5], 1); + offset += USB_DT_CONFIG_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset( + addr, + USB_DT_INTERFACE_SIZE as usize, + offset, + ); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_INTERFACE); + // hid class + assert_eq!(buf[5], 3); + // hid descriptor + offset += USB_DT_INTERFACE_SIZE as u64; + if *self.device_config.get("tablet").unwrap_or(&false) { + let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); + assert_eq!(buf, [0x9, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0]); + } else if *self.device_config.get("keyboard").unwrap_or(&false) { + let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); + assert_eq!(buf, [0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0]); + } + offset += 9; + // endpoint descriptor + let buf = self.get_transfer_data_indirect_with_offset( + addr, + USB_DT_ENDPOINT_SIZE as usize, + offset, + ); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); + // endpoint address + assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x1); + // string descriptor + self.get_string_descriptor(slot_id, 0); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - 16, 4); + // Language ID + assert_eq!(buf, [4, 3, 9, 4]); + self.get_string_descriptor(slot_id, 3); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + if *self.device_config.get("tablet").unwrap_or(&false) { + let hid_str = "HID Tablet"; + let len = hid_str.len() * 2 + 2; + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + for i in 0..hid_str.len() { + assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + } + } else if *self.device_config.get("keyboard").unwrap_or(&false) { + let hid_str = "HID Keyboard"; + let len = hid_str.len() * 2 + 2; + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + for i in 0..hid_str.len() { + assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + } + } + } + + pub fn check_hid_report_descriptor(&mut self, slot_id: u32) { + if *self.device_config.get("keyboard").unwrap_or(&false) { + self.get_hid_report_descriptor(slot_id, 63); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 63); + assert_eq!( + buf, + [ + 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, 0x19, + 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, + 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, + 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, + 0x25, 0xff, 0x05, 0x07, 0x19, 0x00, 0x29, 0xff, 0x81, 0x00, 0xc0 + ] + ); + } else if *self.device_config.get("tablet").unwrap_or(&false) { + self.get_hid_report_descriptor(slot_id, 74); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 74); + assert_eq!( + buf, + [ + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, + 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, + 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, + 0x00, 0x26, 0xff, 0x7f, 0x35, 0x00, 0x46, 0xff, 0x7f, 0x75, 0x10, 0x95, 0x02, + 0x81, 0x02, 0x05, 0x01, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x35, 0x00, 0x45, + 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0xc0, 0xc0, + ] + ); + } + } + + pub fn get_device_descriptor(&mut self, slot_id: u32) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_DESCRIPTOR, + value: (USB_DT_DEVICE as u16) << 8, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_config_descriptor(&mut self, slot_id: u32) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_DESCRIPTOR, + value: (USB_DT_CONFIGURATION as u16) << 8, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_string_descriptor(&mut self, slot_id: u32, index: u16) { + let buf_len = 128; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_DESCRIPTOR, + value: (USB_DT_STRING as u16) << 8 | index, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_status(&mut self, slot_id: u32) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_STATUS, + value: 0, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_configuration(&mut self, slot_id: u32) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_CONFIGURATION, + value: 0, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn set_configuration(&mut self, slot_id: u32, v: u16) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_OUT_REQUEST, + request: USB_REQUEST_SET_CONFIGURATION, + value: v, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn clear_feature(&mut self, slot_id: u32, v: u16) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_OUT_REQUEST, + request: USB_REQUEST_CLEAR_FEATURE, + value: v, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn set_feature(&mut self, slot_id: u32, v: u16) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_OUT_REQUEST, + request: USB_REQUEST_SET_FEATURE, + value: v, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_interface(&mut self, slot_id: u32, index: u16) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_IN_REQUEST, + request: USB_REQUEST_GET_INTERFACE, + value: 0, + index: index, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn set_interface(&mut self, slot_id: u32, v: u16, index: u16) { + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_OUT_REQUEST, + request: USB_REQUEST_SET_INTERFACE, + value: v, + index: index, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_hid_report_descriptor(&mut self, slot_id: u32, len: u16) { + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_IN_REQUEST, + request: USB_REQUEST_GET_DESCRIPTOR, + value: 0x22 << 8, + index: 0, + length: len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_report(&mut self, slot_id: u32) { + let buf_len = 8; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_IN_REQUEST, + request: HID_GET_REPORT, + value: 0, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn set_report(&mut self, slot_id: u32, v: u16) { + // NOTE: set with data, and keyboard not implement yet. + let buf_len = 64; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_OUT_REQUEST, + request: HID_SET_REPORT, + value: v, + index: 0, + length: buf_len, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_protocol(&mut self, slot_id: u32) { + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_IN_REQUEST, + request: HID_GET_PROTOCOL, + value: 0, + index: 0, + length: 1, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn set_protocol(&mut self, slot_id: u32, v: u16) { + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_OUT_REQUEST, + request: HID_SET_PROTOCOL, + value: v, + index: 0, + length: 0, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn get_idle(&mut self, slot_id: u32) { + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_IN_REQUEST, + request: HID_GET_IDLE, + value: 0, + index: 0, + length: 1, + }; + self.queue_device_reqeust(slot_id, &device_req); + } + + pub fn set_idle(&mut self, slot_id: u32, v: u16) { + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_OUT_REQUEST, + request: HID_SET_IDLE, + value: v, + index: 0, + length: 0, + }; + self.queue_device_reqeust(slot_id, &device_req); + } +} + +// Device operation +impl TestXhciPciDevice { + pub fn test_keyboard_event(&mut self, slot_id: u32, test_state: Rc>) { + qmp_send_key_event(test_state.borrow_mut(), 57, true); + qmp_send_key_event(test_state.borrow_mut(), 57, false); + self.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 2); + self.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 44, 0, 0, 0, 0, 0]); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + } + + pub fn test_pointer_event(&mut self, slot_id: u32, test_state: Rc>) { + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + qmp_send_pointer_event(test_state.borrow_mut(), 200, 100, 1); + self.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN, 2); + self.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [1, 200, 0, 100, 0, 0]); + } +} + +// Memory operation +impl TestXhciPciDevice { + pub fn mem_read_u32(&self, addr: u64, buf: &mut [u32]) { + let vec_len = size_of::() * buf.len(); + let tmp = self.mem_read(addr, vec_len); + for i in 0..buf.len() { + buf[i] = LittleEndian::read_u32(&tmp[(size_of::() * i)..]); + } + } + + pub fn mem_write_u32(&self, addr: u64, buf: &[u32]) { + let vec_len = size_of::() * buf.len(); + let mut vec = vec![0_u8; vec_len]; + let tmp = vec.as_mut_slice(); + for i in 0..buf.len() { + LittleEndian::write_u32(&mut tmp[(size_of::() * i)..], buf[i]); + } + self.mem_write(addr, tmp); + } + + pub fn mem_read(&self, addr: u64, len: usize) -> Vec { + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow_mut() + .memread(addr, len as u64) + } + + pub fn mem_write(&self, addr: u64, buf: &[u8]) { + self.pci_dev + .pci_bus + .borrow() + .test_state + .borrow_mut() + .memwrite(addr, buf); + } +} + +pub struct TestUsbBuilder { + args: Vec, + config: HashMap, +} + +impl TestUsbBuilder { + pub fn new() -> Self { + let mut args = Vec::new(); + let machine: Vec<&str> = "-machine virt".split(' ').collect(); + let mut arg = machine.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut arg); + Self { + args, + config: HashMap::new(), + } + } + + pub fn with_xhci(mut self, id: &str) -> Self { + let args = format!( + "-device nec-usb-xhci,id={},bus=pcie.0,addr={}", + id, XHCI_PCI_SLOT_NUM + ); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + self + } + + pub fn with_usb_keyboard(mut self, id: &str) -> Self { + let args = format!("-device usb-kbd,id={}", id); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + self.config.insert(String::from("keyboard"), true); + self + } + + pub fn with_usb_tablet(mut self, id: &str) -> Self { + let args = format!("-device usb-tablet,id={}", id); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + self.config.insert(String::from("tablet"), true); + self + } + + pub fn with_config(mut self, k: &str, v: bool) -> Self { + self.config.insert(k.to_string(), v); + self + } + + pub fn build( + &mut self, + ) -> ( + Rc>, + Rc>, + Rc>, + ) { + let args = self.args.iter().map(AsRef::as_ref).collect(); + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let xhci = Rc::new(RefCell::new(TestXhciPciDevice::new( + machine.pci_bus, + allocator.clone(), + ))); + + for (k, v) in self.config.iter() { + xhci.borrow_mut().device_config.insert(k.clone(), *v); + } + + if *self.config.get("auto_run").unwrap_or(&false) { + // init host controller. + let mut xhci = xhci.borrow_mut(); + xhci.init_host_controller(XHCI_PCI_SLOT_NUM, XHCI_PCI_FUN_NUM); + xhci.run(); + } else { + // only init pci, let testcase init controller. + let mut xhci = xhci.borrow_mut(); + xhci.init_pci_device(XHCI_PCI_SLOT_NUM, XHCI_PCI_FUN_NUM); + } + + (xhci, test_state, allocator) + } +} + +// Helper +pub fn qmp_send_key_event(test_state: RefMut, v: u32, down: bool) { + let value_str = format!("{},{}", v, if down { 1 } else { 0 }); + let mut str = + "{\"execute\": \"input_event\", \"arguments\": { \"key\": \"keyboard\", \"value\":\"" + .to_string(); + str += &value_str; + str += "\" }}"; + test_state.qmp(&str); +} + +pub fn qmp_send_multi_key_event(test_state: Rc>, key_list: &[u32], down: bool) { + for item in key_list { + qmp_send_key_event(test_state.borrow_mut(), *item, down); + } +} + +pub fn qmp_send_pointer_event(test_state: RefMut, x: i32, y: i32, btn: i32) { + let value_str = format!("{},{},{}", x, y, btn); + let mut str = + "{\"execute\": \"input_event\", \"arguments\": { \"key\": \"pointer\", \"value\":\"" + .to_string(); + str += &value_str; + str += "\" }}"; + test_state.qmp(&str); +} + +pub fn clear_iovec(test_state: RefMut, iovecs: &Vec) { + for iov in iovecs.iter() { + test_state.memwrite(iov.io_base, &vec![0; iov.io_len as usize]); + } +} diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index 77a619fd5..591a0d88c 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -344,7 +344,7 @@ impl VirtioDeviceOps for TestVirtioPciDev { } fn enable_interrupt(&mut self) { - self.pci_dev.enable_msix(); + self.pci_dev.enable_msix(None); } fn disable_interrupt(&mut self) { @@ -659,7 +659,7 @@ impl VirtioDeviceOps for TestVirtioPciDev { self.set_features_ok(); // FIXME: add handling the specific device features as needed. - self.pci_dev.enable_msix(); + self.pci_dev.enable_msix(None); self.setup_msix_configuration_vector(alloc.clone(), 0); let vqs = self.init_virtqueue(test_state, alloc, num_queues); diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 76947c7bb..348b26571 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -431,7 +431,7 @@ fn balloon_feature_001() { let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); dev.borrow_mut().init(pci_slot, pci_fn); - dev.borrow_mut().pci_dev.enable_msix(); + dev.borrow_mut().pci_dev.enable_msix(None); dev.borrow_mut() .setup_msix_configuration_vector(allocator.clone(), 0); @@ -478,7 +478,7 @@ fn balloon_feature_002() { let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); dev.borrow_mut().init(pci_slot, pci_fn); - dev.borrow_mut().pci_dev.enable_msix(); + dev.borrow_mut().pci_dev.enable_msix(None); dev.borrow_mut() .setup_msix_configuration_vector(allocator.clone(), 0); diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index de875ad5d..a697dcc91 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -201,7 +201,7 @@ fn blk_features_negotiate() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index cfa60b4f8..e6815d7d7 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -519,7 +519,7 @@ fn init_net_device( net.borrow_mut().set_driver(); net.borrow_mut().negotiate_features(features); net.borrow_mut().set_features_ok(); - net.borrow_mut().pci_dev.enable_msix(); + net.borrow_mut().pci_dev.enable_msix(None); net.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); let vqs = net @@ -1688,7 +1688,7 @@ fn virtio_net_abnormal_rx_tx_test() { net.borrow_mut().set_driver(); net.borrow_mut().negotiate_features(DEFAULT_NET_FEATURES); net.borrow_mut().set_features_ok(); - net.borrow_mut().pci_dev.enable_msix(); + net.borrow_mut().pci_dev.enable_msix(None); net.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); let vqs = net @@ -1766,7 +1766,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { net.borrow_mut().set_driver(); net.borrow_mut().negotiate_features(DEFAULT_NET_FEATURES); net.borrow_mut().set_features_ok(); - net.borrow_mut().pci_dev.enable_msix(); + net.borrow_mut().pci_dev.enable_msix(None); net.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); let vqs = net diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs new file mode 100644 index 000000000..f18d4fd6b --- /dev/null +++ b/tests/mod_test/tests/usb_test.rs @@ -0,0 +1,2214 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libdriver::pci::{PCI_DEVICE_ID, PCI_VENDOR_ID}; +use mod_test::libdriver::usb::{ + clear_iovec, qmp_send_key_event, qmp_send_multi_key_event, qmp_send_pointer_event, TestIovec, + TestNormalTRB, TestUsbBuilder, CONTROL_ENDPOINT_ID, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, + HID_POINTER_LEN, KEYCODE_NUM1, KEYCODE_SPACE, PCI_CLASS_PI, PRIMARY_INTERRUPTER_ID, + TD_TRB_LIMIT, XHCI_PCI_OPER_OFFSET, XHCI_PORTSC_OFFSET, +}; +use usb::config::*; +use usb::usb::UsbDeviceRequest; +use usb::xhci::xhci_controller::{ + DwordOrder, XhciInputCtrlCtx, XhciSlotCtx, EP_RUNNING, SLOT_ADDRESSED, +}; +use usb::xhci::xhci_regs::{ + XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_USBSTS, +}; +use usb::xhci::{TRBCCode, TRBType, TRB_SIZE}; + +#[test] +fn test_xhci_keyboard_basic() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // Case 1 + // Space down + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, true); + let transfer_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + let data_ptr = xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(transfer_ptr, evt.ptr); + let buf = xhci.get_transfer_data_direct(data_ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 44, 0, 0, 0, 0, 0]); + // Space up + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, false); + let transfer_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + let data_ptr = xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(transfer_ptr, evt.ptr); + let buf = xhci.get_transfer_data_direct(data_ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + + // Case 2 + let key_list = vec![ + KEYCODE_NUM1, + KEYCODE_NUM1 + 1, + KEYCODE_NUM1 + 2, + KEYCODE_NUM1 + 3, + ]; + qmp_send_multi_key_event(test_state.clone(), &key_list, true); + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + key_list.len(), + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // 1 2 3 4 down + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 32, 33, 0, 0]); + // 1 2 3 4 Up + qmp_send_multi_key_event(test_state.clone(), &key_list, false); + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + key_list.len(), + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 32, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_direct() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let key_list = vec![ + KEYCODE_NUM1, + KEYCODE_NUM1 + 1, + KEYCODE_NUM1 + 2, + KEYCODE_NUM1 + 3, + ]; + qmp_send_multi_key_event(test_state.clone(), &key_list, true); + xhci.queue_multi_direct_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + key_list.len(), + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // 1 2 3 4 Down + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 32, 33, 0, 0]); + // 1 2 3 4 Up + qmp_send_multi_key_event(test_state.clone(), &key_list, false); + xhci.queue_multi_direct_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + key_list.len(), + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 32, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_multi_trb() { + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let key_list = vec![ + KEYCODE_NUM1, + KEYCODE_NUM1 + 1, + KEYCODE_NUM1 + 2, + KEYCODE_NUM1 + 3, + ]; + qmp_send_multi_key_event(test_state.clone(), &key_list, true); + let mut io_list = Vec::new(); + for _ in 0..4 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, false); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, true); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(4); + let iovec = TestIovec::new(ptr, 4, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, true); + io_list.push(iovecs); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // 1 2 3 4 Down + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[0]); + assert_eq!(buf, [0, 0, 30, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[1]); + assert_eq!(buf, [0, 0, 30, 31, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[2]); + assert_eq!(buf, [0, 0, 30, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[3]); + assert_eq!(buf, [0, 0, 30, 31, 32, 33, 0, 0]); + // 1 2 3 4 Up + qmp_send_multi_key_event(test_state.clone(), &key_list, false); + let mut io_list = Vec::new(); + for _ in 0..4 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, false); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, true); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(4); + let iovec = TestIovec::new(ptr, 4, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, true); + io_list.push(iovecs); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[0]); + assert_eq!(buf, [0, 0, 33, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[1]); + assert_eq!(buf, [0, 0, 33, 32, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[2]); + assert_eq!(buf, [0, 0, 33, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[3]); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_event_data() { + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let key_list = vec![ + KEYCODE_NUM1, + KEYCODE_NUM1 + 1, + KEYCODE_NUM1 + 2, + KEYCODE_NUM1 + 3, + ]; + qmp_send_multi_key_event(test_state.clone(), &key_list, true); + let mut io_list = Vec::new(); + for _ in 0..4 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, false); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, true); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(4); + let iovec = TestIovec::new(ptr, 4, false); + iovecs.push(iovec); + // Event Data TRB + let mut iovec = TestIovec::new(0xff00ff00ff00ff00, 0, false); + iovec.event_data = true; + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, true); + io_list.push(iovecs); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // 1 2 3 4 Down + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[0]); + assert_eq!(buf, [0, 0, 30, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[1]); + assert_eq!(buf, [0, 0, 30, 31, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[2]); + assert_eq!(buf, [0, 0, 30, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[3]); + assert_eq!(buf, [0, 0, 30, 31, 32, 33, 0, 0]); + // 1 2 3 4 Up + qmp_send_multi_key_event(test_state.clone(), &key_list, false); + let mut io_list = Vec::new(); + for _ in 0..4 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, true); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, false); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(4); + let iovec = TestIovec::new(ptr, 4, false); + iovecs.push(iovec); + // Event Data TRB + let mut iovec = TestIovec::new(0xff00ff00ff00ff00, 0, false); + iovec.event_data = true; + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, true); + io_list.push(iovecs); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[0]); + assert_eq!(buf, [0, 0, 33, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[1]); + assert_eq!(buf, [0, 0, 33, 32, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[2]); + assert_eq!(buf, [0, 0, 33, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.length, 8); + assert_eq!(evt.ptr, 0xff00ff00ff00ff00); + let buf = xhci.get_transfer_data_by_iovec(&io_list[3]); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_over_hid_buffer() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + const HID_BUFFER_SIZE: u32 = 16; + let event_cnt = 20; + // 1 -> 0 down / up + for i in 0..(event_cnt / 2) { + qmp_send_key_event(test_state.borrow_mut(), 2 + i, true); + qmp_send_key_event(test_state.borrow_mut(), 2 + i, false); + } + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + event_cnt as usize, + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + for i in 0..event_cnt { + if i < HID_BUFFER_SIZE { + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + if i % 2 == 0 { + assert_eq!(buf, [0, 0, 30 + i as u8 / 2, 0, 0, 0, 0, 0]); + } else { + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + } + } else { + // event lost. + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + } + } + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_over_ring_limit() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let org_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + // Fake ring length. + let transfer_limit = 32; + let test_cnt = 3; + for i in 0..test_cnt { + for _ in 0..(transfer_limit / 2) { + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, false); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 44, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + } + if i == 0 { + // Fake link new addrress. + xhci.queue_link_trb( + slot_id, + HID_DEVICE_ENDPOINT_ID, + org_ptr + TRB_SIZE as u64 * 64, + false, + ); + } else if i == 1 { + // Goto the origin address. + xhci.queue_link_trb(slot_id, HID_DEVICE_ENDPOINT_ID, org_ptr, true); + } else { + xhci.queue_link_trb(slot_id, HID_DEVICE_ENDPOINT_ID, org_ptr, true); + let ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + assert_eq!(org_ptr, ptr); + } + } + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_reorder() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + xhci.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 4); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + let key_list = vec![ + KEYCODE_NUM1, + KEYCODE_NUM1 + 1, + KEYCODE_NUM1 + 2, + KEYCODE_NUM1 + 3, + ]; + qmp_send_multi_key_event(test_state.clone(), &key_list, true); + // 1 2 3 4 Down + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 30, 31, 32, 33, 0, 0]); + // 1 2 3 4 Up + let key_list = vec![ + KEYCODE_NUM1, + KEYCODE_NUM1 + 1, + KEYCODE_NUM1 + 2, + KEYCODE_NUM1 + 3, + ]; + qmp_send_multi_key_event(test_state.clone(), &key_list[0..2], false); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + xhci.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 2); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + key_list.len() - 2, + ); + qmp_send_multi_key_event(test_state.clone(), &key_list[2..], false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 31, 32, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 32, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 33, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_remote_wakeup() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // U0 -> U3 + // NOTE: write PLS field should set LWS field. + xhci.port_regs_write( + port_id, + XHCI_PORTSC_OFFSET, + PORTSC_LWS | PLS_U3 << PORTSC_PLS_SHIFT, + ); + let portsc = xhci.port_regs_read(port_id, XHCI_PORTSC_OFFSET); + assert!(portsc >> PORTSC_PLS_SHIFT & PLS_U3 == PLS_U3); + + // Set remote wakeup. + xhci.set_feature(slot_id, USB_DEVICE_REMOTE_WAKEUP as u16); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, false); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + + // U3 -> U0 + xhci.port_regs_write( + port_id, + XHCI_PORTSC_OFFSET, + PORTSC_LWS | PLS_U0 << PORTSC_PLS_SHIFT, + ); + let portsc = xhci.port_regs_read(port_id, XHCI_PORTSC_OFFSET); + assert!(portsc >> PORTSC_PLS_SHIFT & PLS_U0 == PLS_U0); + assert!(portsc & PORTSC_PLC == PORTSC_PLC); + + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.get_trb_type(), TRBType::ErPortStatusChange as u32); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 44, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + test_state.borrow_mut().stop(); +} + +// Abnormal +#[test] +fn test_xhci_keyboard_invalid_value() { + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // Case 1: invalid code + qmp_send_key_event(test_state.borrow_mut(), 0, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + + // Case 2: invalid cycle bit + qmp_send_key_event(test_state.borrow_mut(), 2, true); + let mut trb = TestNormalTRB::default(); + let ptr = guest_allocator.borrow_mut().alloc(8); + trb.set_pointer(ptr); + trb.set_ioc_flag(true); + trb.set_trb_type(TRBType::TrNormal as u32); + trb.force_cycle = true; + trb.set_trb_transfer_length(8); + xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + // clean td + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let old_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.set_tr_dequeue(old_ptr + 0x20, slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + // Case 3: invalid down length. + const KEY_LIMIT: u32 = 6; + let key_len = 10; + for i in 0..key_len { + qmp_send_key_event(test_state.borrow_mut(), 2 + i, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + if i >= KEY_LIMIT { + // rollover + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 1, 1, 1, 1, 1, 1]); + } + } + for i in (0..key_len).rev() { + qmp_send_key_event(test_state.borrow_mut(), 2 + i, false); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + if i == 0 { + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); + } + } + + // Case 4: length over 8 when IDT = 1. + qmp_send_key_event(test_state.borrow_mut(), 2, true); + let mut trb = TestNormalTRB::default(); + trb.set_ioc_flag(true); + trb.set_isp_flag(true); + trb.set_idt_flag(true); + trb.set_trb_type(TRBType::TrNormal as u32); + trb.set_trb_transfer_length(10); + xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + // clean up key event. + qmp_send_key_event(test_state.borrow_mut(), 2, false); + xhci.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 2); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_invalid_buffer() { + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // over limit + qmp_send_key_event(test_state.borrow_mut(), 2, true); + qmp_send_key_event(test_state.borrow_mut(), 3, true); + let mut io_list = Vec::new(); + for _ in 0..2 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(5); + let iovec = TestIovec::new(ptr, 5, false); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, true); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(4); + let iovec = TestIovec::new(ptr, 4, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, true); + // NOTE: ensure the memory is zero. + clear_iovec(test_state.borrow_mut(), &iovecs); + io_list.push(iovecs); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[0]); + assert_eq!(buf, [0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[1]); + assert_eq!(buf, [0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0]); + // less buffer. + qmp_send_key_event(test_state.borrow_mut(), 2, false); + qmp_send_key_event(test_state.borrow_mut(), 3, false); + let mut io_list = Vec::new(); + for _ in 0..2 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, true); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, false); + iovecs.push(iovec); + let ptr = guest_allocator.borrow_mut().alloc(2); + let iovec = TestIovec::new(ptr, 2, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, true); + // NOTE: ensure the memory is zero. + clear_iovec(test_state.borrow_mut(), &iovecs); + io_list.push(iovecs); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[0]); + assert_eq!(buf, [0, 0, 31, 0, 0, 0]); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_by_iovec(&io_list[1]); + assert_eq!(buf, [0, 0, 0, 0, 0, 0]); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_over_transfer_ring() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .with_config("over_transfer_ring", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + qmp_send_key_event(test_state.borrow_mut(), 2, true); + qmp_send_key_event(test_state.borrow_mut(), 3, true); + xhci.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 2); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // link trb overlimit by setting next trb to itself. + let ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.queue_link_trb(slot_id, HID_DEVICE_ENDPOINT_ID, ptr, false); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // Host Controller Error + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(true); + let slot_id = xhci.init_device(port_id); + // Invalid iovec over td limit. + let trb_limit = TD_TRB_LIMIT; + let mut iovecs = vec![TestIovec::new(0, 1, true); trb_limit as usize]; + xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, false); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // Host Controller Error + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(true); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_over_event_ring() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + // reset event ring. + let evt_ring_sz = 16; + xhci.init_event_ring(0, 1, evt_ring_sz); + xhci.init_msix(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // only one trb left in event ring it will report ring full error. + for i in 0..evt_ring_sz - 1 { + qmp_send_key_event(test_state.borrow_mut(), 2 + i, true); + } + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + evt_ring_sz as usize - 1, + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // NOTE: the last event is lost for the current implementation. + for _ in 0..evt_ring_sz - 2 { + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::EventRingFullError as u32); + + for i in 0..evt_ring_sz { + qmp_send_key_event(test_state.borrow_mut(), 2 + i, false); + } + + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, + evt_ring_sz as usize, + ); + + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(true); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_invalid_doorbell() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("command_auto_doorbell", true) + .with_config("auto_run", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + qmp_send_key_event(test_state.borrow_mut(), 2, true); + qmp_send_key_event(test_state.borrow_mut(), 3, true); + xhci.queue_indirect_td(slot_id, 2, HID_KEYBOARD_LEN); + xhci.queue_indirect_td(0, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + xhci.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 2); + xhci.doorbell_write(slot_id, 0xff); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + xhci.doorbell_write(0xff, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + qmp_send_key_event(test_state.borrow_mut(), 2, false); + qmp_send_key_event(test_state.borrow_mut(), 3, false); + xhci.queue_indirect_td(2, 2, HID_KEYBOARD_LEN); + xhci.queue_indirect_td(0, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + xhci.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, 2); + xhci.doorbell_write(0, 0); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + xhci.doorbell_write(0, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +// Init +#[test] +fn test_xhci_keyboard_controller_init_invalid_register() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + // Case 1: write value to pci config. + xhci.read_pci_config(); + // write vendor id. + xhci.pci_dev.config_writew(PCI_VENDOR_ID, 0xf0ff); + // write device id. + xhci.pci_dev.config_writew(PCI_DEVICE_ID, 0xff0f); + // write class code. + // write invalid data. + xhci.pci_dev.config_writel(PCI_CLASS_PI, 0xf0f0ffff); + xhci.read_pci_config(); + + // Case 2: write value to capability registers. + xhci.read_capability(); + // write invalid data. + xhci.pci_dev.io_writel(xhci.bar_addr, 0, 0xffffffff); + xhci.read_capability(); + + // Case 3: write invalid slot. + xhci.pci_dev.io_writel( + xhci.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + 0xffff, + ); + let config = xhci.pci_dev.io_readl( + xhci.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + ); + assert_ne!(config, 0xffff); + + // Case 4: invalid oper + xhci.pci_dev.io_writel( + xhci.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + 0xffff, + ); + let status = xhci.pci_dev.io_readl( + xhci.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + ); + assert_ne!(status, 0xffff); + + xhci.init_device_context_base_address_array_pointer(); + xhci.init_command_ring_dequeue_pointer(); + + // Case 5: write invalid interrupter. + xhci.interrupter_regs_write(0, XHCI_INTR_REG_ERSTSZ, 0); + xhci.interrupter_regs_writeq(0, XHCI_INTR_REG_ERSTBA_LO, 0); + + // Case 6: invalid size + xhci.init_event_ring(0, 1, 12); + xhci.init_msix(); + xhci.run(); + xhci.no_op(); + // NOTE: no event now. + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + + xhci.reset_controller(true); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_controller_init_miss_step() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + // Case 1: miss init command ring. + xhci.read_pci_config(); + xhci.read_capability(); + xhci.init_max_device_slot_enabled(); + xhci.init_device_context_base_address_array_pointer(); + xhci.init_interrupter(); + xhci.run(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + // Host Controller Error + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(false); + // Case 2: miss init dcbaap. + xhci.read_pci_config(); + xhci.read_capability(); + xhci.init_max_device_slot_enabled(); + xhci.init_command_ring_dequeue_pointer(); + xhci.init_interrupter(); + xhci.run(); + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // address device + xhci.address_device(slot_id, false, port_id); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + // Host Controller Error + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(false); + // Case 3: miss init interrupter. + xhci.read_pci_config(); + xhci.read_capability(); + xhci.init_max_device_slot_enabled(); + xhci.init_command_ring_dequeue_pointer(); + xhci.run(); + // reset usb port + xhci.reset_port(port_id); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + // NOTE: no HCE now. only print error log. + + xhci.reset_controller(true); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_control_command() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .with_config("address_device_bsr", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // no op + let ptr = xhci.get_command_pointer(); + xhci.no_op(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.ptr, ptr); + + let slot_id = xhci.init_device(port_id); + + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_OUT_REQUEST, + request: USB_REQUEST_SET_CONFIGURATION, + value: 0, + index: 0, + length: 8, + }; + // Setup Stage. + let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); + // Data Stage. + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(0, device_req.length, in_dir); + data_trb.set_idt_flag(true); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + // Status Stage. + let mut status_trb = TestNormalTRB::generate_status_td(false); + status_trb.set_ch_flag(true); + status_trb.set_ioc_flag(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + // Event Data TRB. + let mut event_data_trb = TestNormalTRB::generate_event_data_trb(0x1234); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut event_data_trb); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.ptr, 0x1234); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_control_command_invalid_order() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // address device bsr = 0 + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_ctx = xhci.get_slot_context(slot_id); + assert_eq!(slot_ctx.get_slot_state(), SLOT_ADDRESSED); + let ep0_ctx = xhci.get_endpoint_context(slot_id, CONTROL_ENDPOINT_ID); + assert_eq!(ep0_ctx.get_ep_state(), EP_RUNNING); + // get descriptor + xhci.get_usb_descriptor(slot_id); + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + // disable slot + xhci.disable_slot(slot_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // reset endpoint after disable slot + xhci.reset_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + + // set tr dequeue + let old_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.set_tr_dequeue(old_ptr, slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + + // reset device + xhci.reset_device(slot_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + // stop endpoint after reset device + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // address device + let slot_id = evt.get_slot_id(); + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + // stop endpoint. invalid slot id in trb. + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(slot_id); + trb.set_ep_id(2); + trb.set_trb_type(TRBType::CrStopEndpoint as u32); + xhci.queue_command(&mut trb); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::EpNotEnabledError as u32); + // set tr dequeue. + let mut trb = TestNormalTRB::default(); + trb.set_pointer(0xff00 | 1); + trb.set_slot_id(slot_id); + trb.set_ep_id(2); + trb.set_trb_type(TRBType::CrSetTrDequeue as u32); + xhci.queue_command(&mut trb); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::EpNotEnabledError as u32); + // reset endpoint + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(slot_id); + trb.set_ep_id(2); + trb.set_trb_type(TRBType::CrResetEndpoint as u32); + xhci.queue_command(&mut trb); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::EpNotEnabledError as u32); + + // stop endpoint. + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure agian. + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_over_command_ring() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let org_ptr = xhci.get_command_pointer(); + // Fake ring length. + let ring_len = 32; + for _ in 0..ring_len - 1 { + let ptr = xhci.get_command_pointer(); + xhci.no_op(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.ptr, ptr); + } + xhci.queue_link_trb(0, 0, org_ptr, true); + let ptr = xhci.get_command_pointer(); + assert_eq!(org_ptr, ptr); + for _ in 0..ring_len - 1 { + let ptr = xhci.get_command_pointer(); + xhci.no_op(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.ptr, ptr); + } + let ptr = xhci.get_command_pointer(); + // link trb overlimit by setting next trb to itself. + xhci.queue_link_trb(0, 0, ptr, false); + xhci.doorbell_write(0, 0); + // Host Controller Error + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(true); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_invalid_value() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // Case 1: invalid value when address device + // Invalid port id. + xhci.address_device(slot_id, true, 0xff); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + xhci.address_device(slot_id, true, port_id + 1); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + // Invalid add flag. + let input_ctx_addr = xhci.address_device(slot_id, true, port_id); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.add_flags |= 0xf0; + xhci.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ParameterError as u32); + // Invalid slot state. + let input_ctx_addr = xhci.address_device(slot_id, false, port_id); + let mut slot_ctx = XhciSlotCtx::default(); + slot_ctx.set_slot_state(SLOT_ADDRESSED); + slot_ctx.set_context_entry(1); + slot_ctx.set_port_number(port_id); + xhci.mem_write_u32(input_ctx_addr + 0x20, slot_ctx.as_dwords()); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ContextStateError as u32); + // correct + xhci.address_device(slot_id, false, port_id); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Case 2: invalid value when evaluate context + // Invalid input context. + let input_ctx_addr = xhci.evaluate_context(slot_id, 0x1234, 3, 128); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.drop_flags = 0xf; + xhci.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + // Invalid slot id. + xhci.evaluate_context(slot_id + 1, 0x1234, 3, 128); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + // correct + xhci.evaluate_context(slot_id, 0x1234, 0, 64); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Case 3: invalid value when configure endpoint + // DC when endpoint is not configured. + xhci.configure_endpoint(slot_id, true); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ContextStateError as u32); + // Invalid input context. + let input_ctx_addr = xhci.configure_endpoint(slot_id, false); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.add_flags = 0x2; + xhci.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + // correct + xhci.configure_endpoint(slot_id, false); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + // Case 4: invalid command + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(0); + xhci.queue_command(&mut trb); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_invalid_request() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // address device + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get descriptor + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_DESCRIPTOR, + value: (USB_DT_CONFIGURATION as u16) << 8 | 6, + index: 10, + length: 10, + }; + xhci.queue_device_reqeust(slot_id, &device_req); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + // Stall Error. + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + // reset endpoint + xhci.reset_endpoint(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // evaluate context + xhci.evaluate_context(slot_id, 0x1234, 0, 64); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // SET_CONFIGURATION invalid value. + // Inject invalid usb device request to let endpoint halted. + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_OUT_REQUEST, + request: USB_REQUEST_SET_CONFIGURATION, + value: 0xff, + index: 2, + length: 64, + }; + xhci.queue_device_reqeust(slot_id, &device_req); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + // Stall Error. + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + // reset endpoint + xhci.reset_endpoint(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // set report + xhci.set_report(slot_id, 3); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_invalid_control() { + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // address device + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get descriptor, invalid control value + let device_req = UsbDeviceRequest { + request_type: USB_DEVICE_IN_REQUEST, + request: USB_REQUEST_GET_DESCRIPTOR, + value: (USB_DT_CONFIGURATION as u16) << 8, + index: 0, + length: 64, + }; + // Case 1: no SetUp Stage. + // Data Stage. + let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + // Status Stage. + let mut status_trb = TestNormalTRB::generate_status_td(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // Case 2: no Status Stage. + // Setup Stage. + let org_ptr = xhci.get_transfer_pointer(slot_id, CONTROL_ENDPOINT_ID); + let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); + // Data Stage. + let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); + data_trb.set_ch_flag(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + // NOTE: no event for current implement now. + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + // clean up. rewrite tr dequeue. + xhci.set_transfer_pointer(org_ptr, slot_id, CONTROL_ENDPOINT_ID); + + // Case 3: no IDT = 1. in SetUp TD + // Setup Stage. + let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + setup_trb.set_idt_flag(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); + // Data Stage. + let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + // Status Stage. + let mut status_trb = TestNormalTRB::generate_status_td(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // Case 4: invalid length in Setup TD + // Setup Stage. + let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + setup_trb.set_trb_transfer_length(11); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); + // Data Stage. + let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + // Status Stage. + let mut status_trb = TestNormalTRB::generate_status_td(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // Case 5: Data direction mismatch EP + // Setup Stage. + let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); + // Data Stage. + let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let in_dir = + device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; + let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); + data_trb.set_dir_flag(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut data_trb); + // Status Stage. + let mut status_trb = TestNormalTRB::generate_status_td(false); + xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // evaluate context + xhci.evaluate_context(slot_id, 0x1234, 0, 64); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_invalid_order() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // Case 1: configure endpoint before address device + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ContextStateError as u32); + // address device + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Case 2: address device after configure endpoint + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // recover, configure again + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Case 3: set tr dequeue when not stop. + let old_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.set_tr_dequeue(old_ptr, slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ContextStateError as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + + // disable slot + xhci.disable_slot(slot_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Case 4: stop endpoint after disable slot + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + // Case 5: reset endpoint after disable slot + xhci.reset_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + // Case 6: disable slot again + xhci.disable_slot(slot_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::SlotNotEnabledError as u32); + // reenable + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // address device + let slot_id = evt.get_slot_id(); + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_reset_device() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // Case 1: reset afer enable slot. + xhci.reset_device(slot_id); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + + xhci.reset_controller(true); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_multi_enable_slot() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + + let enable_limit = 64; + for _ in 0..enable_limit - 1 { + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + assert_ne!(slot_id, 0); + } + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::NoSlotsError as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_reconfigure_endpoint() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let cnt = 3; + for _ in 0..cnt { + xhci.configure_endpoint(slot_id, true); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_device_request_repeat() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // address device + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let cnt = 3; + for i in 0..cnt { + // get descriptor + xhci.get_device_descriptor(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + xhci.get_config_descriptor(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + xhci.get_string_descriptor(slot_id, i); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + } + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let cnt = 3; + for _ in 0..cnt { + // get status + xhci.get_status(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 2); + assert_eq!(buf, [0, 0]); + // set configuration + xhci.set_configuration(slot_id, 1); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get configuration + xhci.get_configuration(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 2); + assert_eq!(buf[0], 1); + // Set remote wakeup. + xhci.set_feature(slot_id, USB_DEVICE_REMOTE_WAKEUP as u16); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get status + xhci.get_status(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 2); + assert_eq!(buf, [2, 0]); + // Clear remote wakeup. + xhci.clear_feature(slot_id, USB_DEVICE_REMOTE_WAKEUP as u16); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Set interface. + xhci.set_interface(slot_id, 0, 0); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Get interface. + xhci.get_interface(slot_id, 0); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + // set protocol + xhci.set_protocol(slot_id, 1); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + //get protocol + xhci.get_protocol(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // NOTE: set idle, not fully implemented yet. + xhci.set_idle(slot_id, 0x3 << 8); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get idle + xhci.get_idle(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // set report + xhci.set_report(slot_id, 3); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // get report + xhci.get_report(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_device_init_device_miss_step() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // enable slot + xhci.enable_slot(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + // address device + xhci.address_device(slot_id, false, port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // NOTE: not kick acually, just print error. + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + + // configure endpoint + xhci.configure_endpoint(slot_id, false); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // clean + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_some()); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +// Tablet +#[test] +fn test_xhci_tablet_basic() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let cnt = 10; + for i in 0..cnt { + qmp_send_pointer_event(test_state.borrow_mut(), i * 10, i * 20, i % 3); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + + for i in 0..cnt { + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!( + buf, + [ + i as u8 % 3, + (i * 10) as u8, + (i * 10 >> 8) as u8, + (i * 20) as u8, + (i * 20 >> 8) as u8, + 0 + ] + ); + } + + for _ in 0..cnt { + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x8); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + for _ in 0..cnt { + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 10, 0, 20, 0, 1]); + } + + for _ in 0..cnt { + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x10); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + } + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + for _ in 0..cnt { + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 10, 0, 20, 0, 255]); + } + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_tablet_over_hid_buffer() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + const HID_BUFFER_SIZE: u32 = 16; + let event_cnt = 20; + for i in 0..event_cnt { + qmp_send_pointer_event(test_state.borrow_mut(), i, i + 100, 0); + } + xhci.queue_multi_indirect_td( + slot_id, + HID_DEVICE_ENDPOINT_ID, + HID_POINTER_LEN, + event_cnt as usize, + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + for i in 0..event_cnt as u32 { + if i < HID_BUFFER_SIZE { + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, i as u8, 0, (i + 100) as u8, 0, 0]); + } else { + // event lost. + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + } + } + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_tablet_over_ring_limit() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let org_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + // Fake ring length. + let transfer_limit = 32; + let test_cnt = 3; + for i in 0..test_cnt { + for _ in 0..transfer_limit { + qmp_send_pointer_event(test_state.borrow_mut(), 50, 100, 0); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 50, 0, 100, 0, 0]); + } + if i == 0 { + // Fake link new addrress. + xhci.queue_link_trb( + slot_id, + HID_DEVICE_ENDPOINT_ID, + org_ptr + TRB_SIZE as u64 * 64, + false, + ); + } else if i == 1 { + // Goto the origin address. + xhci.queue_link_trb(slot_id, HID_DEVICE_ENDPOINT_ID, org_ptr, true); + } else { + xhci.queue_link_trb(slot_id, HID_DEVICE_ENDPOINT_ID, org_ptr, true); + let ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + assert_eq!(org_ptr, ptr); + } + } + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_tablet_invalid_value() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + qmp_send_pointer_event(test_state.borrow_mut(), 0xfffff, 0xfffff, 0xff); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [7, 255, 127, 255, 127, 0]); + + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_tablet_device_init_control_command() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .with_config("address_device_bsr", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // no op + let ptr = xhci.get_command_pointer(); + xhci.no_op(); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + assert_eq!(evt.ptr, ptr); + + let slot_id = xhci.init_device(port_id); + + // get report + xhci.get_report(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, HID_POINTER_LEN); + assert_eq!(buf, [0, 0, 0, 0, 0, 0]); + + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_keyboard_tablet_basic() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + xhci.device_config.insert(String::from("keyboard"), true); + xhci.device_config.insert(String::from("tablet"), false); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + + let port_id = 2; + xhci.device_config.insert(String::from("keyboard"), false); + xhci.device_config.insert(String::from("tablet"), true); + let slot_id = xhci.init_device(port_id); + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 13fcef9db..0df8d58a2 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -121,7 +121,7 @@ fn init_device_step( 4 => blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1), 5 => blk.borrow_mut().set_features_ok(), 7 => { - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); vqs = blk @@ -691,7 +691,7 @@ fn virtio_init_device_abnormal_status() { blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); } if i % 7 == 0 { - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); blk.borrow_mut() @@ -707,7 +707,7 @@ fn virtio_init_device_abnormal_status() { blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); } if i % 7 == 0 { - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); blk.borrow_mut() @@ -719,7 +719,7 @@ fn virtio_init_device_abnormal_status() { blk.borrow_mut().set_driver(); blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); blk.borrow_mut().set_features_ok(); - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); let vqs = blk @@ -780,7 +780,7 @@ fn virtio_init_device_abnormal_features() { } blk.borrow_mut().negotiate_features(features); blk.borrow_mut().set_features_ok(); - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); let vqs = blk @@ -884,7 +884,7 @@ fn virtio_init_device_abnormal_vring_info() { blk.borrow_mut().set_driver(); blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); blk.borrow_mut().set_features_ok(); - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); @@ -1266,7 +1266,7 @@ fn virtio_init_device_repeat() { let capability = blk.borrow().config_readq(0); assert_eq!(capability, TEST_IMAGE_SIZE / 512); - blk.borrow_mut().pci_dev.enable_msix(); + blk.borrow_mut().pci_dev.enable_msix(None); blk.borrow_mut() .setup_msix_configuration_vector(alloc.clone(), 0); diff --git a/usb/src/xhci/xhci_regs.rs b/usb/src/xhci/xhci_regs.rs index 4568daec7..bc382ca16 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/usb/src/xhci/xhci_regs.rs @@ -75,15 +75,15 @@ const CAP_EXT_USB_REVISION_2_0: u32 = 0x0200; /// USB 3.0. const CAP_EXT_USB_REVISION_3_0: u32 = 0x0300; /// Operational Registers. -const XHCI_OPER_REG_USBCMD: u64 = 0x00; -const XHCI_OPER_REG_USBSTS: u64 = 0x04; -const XHCI_OPER_REG_PAGESIZE: u64 = 0x08; +pub const XHCI_OPER_REG_USBCMD: u64 = 0x00; +pub const XHCI_OPER_REG_USBSTS: u64 = 0x04; +pub const XHCI_OPER_REG_PAGESIZE: u64 = 0x08; const XHCI_OPER_REG_DNCTRL: u64 = 0x14; const XHCI_OPER_REG_CMD_RING_CTRL_LO: u64 = 0x18; const XHCI_OPER_REG_CMD_RING_CTRL_HI: u64 = 0x1c; const XHCI_OPER_REG_DCBAAP_LO: u64 = 0x30; const XHCI_OPER_REG_DCBAAP_HI: u64 = 0x34; -const XHCI_OPER_REG_CONFIG: u64 = 0x38; +pub const XHCI_OPER_REG_CONFIG: u64 = 0x38; const XHCI_OPER_PAGESIZE: u32 = 1; /// Command Ring Control Register RCS/CS/CA mask. const XHCI_CRCR_CTRL_LO_MASK: u32 = 0xffffffc7; -- Gitee From 72575a73cb92fe6f379f3bad44fdf7126de8a64e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 28 Feb 2023 02:47:04 +0800 Subject: [PATCH 0844/2187] mst: check env aio support before virtio-block testing Signed-off-by: yezengruan --- tests/mod_test/tests/block_test.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index de875ad5d..68e4e09e7 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -31,6 +31,7 @@ use mod_test::libtest::TestState; use std::cell::RefCell; use std::rc::Rc; +use util::aio::{aio_probe, AioEngine}; use util::num_ops::round_up; use util::offset_of; @@ -563,9 +564,15 @@ fn blk_all_features() { let device_args = Rc::new(String::from( ",multifunction=on,serial=111111,num-queues=4,bootindex=1,iothread=iothread1", )); - let drive_args = Rc::new(String::from( - ",direct=on,aio=io_uring,readonly=off,throttling.iops-total=1024", - )); + let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { + Rc::new(String::from( + ",direct=on,aio=io_uring,readonly=off,throttling.iops-total=1024", + )) + } else { + Rc::new(String::from( + ",direct=false,readonly=off,throttling.iops-total=1024", + )) + }; let other_args = Rc::new(String::from("-object iothread,id=iothread1")); let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); @@ -837,7 +844,11 @@ fn blk_iops() { fn blk_aio_native() { let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(String::from(",direct=on,aio=native")); + let drive_args = if aio_probe(AioEngine::Native).is_ok() { + Rc::new(String::from(",direct=on,aio=native")) + } else { + Rc::new(String::from(",direct=false")) + }; let other_args = Rc::new(String::from("")); let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); @@ -886,7 +897,11 @@ fn blk_aio_native() { fn blk_aio_io_uring() { let image_path = Rc::new(create_blk_img(TEST_IMAGE_SIZE_1M, 1)); let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(String::from(",direct=on,aio=io_uring")); + let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { + Rc::new(String::from(",direct=on,aio=io_uring")) + } else { + Rc::new(String::from(",direct=false")) + }; let other_args = Rc::new(String::from("")); let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); -- Gitee From fc8eeac491e3b56f08582fef4dcda9d30aabfaa3 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 28 Feb 2023 03:45:57 +0800 Subject: [PATCH 0845/2187] balloon: Fixed the failure issue caused by insufficient memory for huge pages Signed-off-by: jiewangqun --- tests/mod_test/tests/balloon_test.rs | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 76947c7bb..453d31480 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -17,6 +17,8 @@ use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCf use mod_test::libtest::{test_init, TestState}; use serde_json::json; use std::cell::RefCell; +use std::fs::File; +use std::io::{self, BufRead, BufReader}; use std::process::Command; use std::rc::Rc; use std::{thread, time}; @@ -29,6 +31,31 @@ const PAGE_SIZE_UNIT: u64 = 4096; const TIMEOUT_US: u64 = 15 * 1000 * 1000; const MBSIZE: u64 = 1024 * 1024; +fn read_lines(filename: String) -> io::Lines> { + let file = File::open(filename).unwrap(); + return io::BufReader::new(file).lines(); +} + +fn get_hugesize() -> u64 { + let mut free_page = 0_u64; + let lines = read_lines("/proc/meminfo".to_string()); + for line in lines { + if let Ok(info) = line { + if info.starts_with("HugePages_Free:") { + let free: Vec<&str> = info.split(":").collect(); + free_page = free[1].trim().parse::().unwrap(); + } + if info.starts_with("Hugepagesize:") { + let huges: Vec<&str> = info.split(":").collect(); + let sizes: Vec<&str> = huges[1].trim().split(" ").collect(); + let size = sizes[0].trim().parse::().unwrap(); + return free_page * size; + } + } + } + 0_u64 +} + pub struct VirtioBalloonTest { pub device: Rc>, pub state: Rc>, @@ -392,6 +419,11 @@ fn balloon_fun_002() { #[test] fn balloon_huge_fun_001() { create_huge_mem_path(); + let size_kb = get_hugesize(); + if size_kb < 1024 * 1024 { + clean_huge_mem_path(); + return; + } balloon_fun(false, true); balloon_fun(true, true); clean_huge_mem_path(); @@ -691,6 +723,10 @@ fn balloon_config_001() { /// 1/2/3.Success #[test] fn balloon_config_002() { + let size_kb = get_hugesize(); + if size_kb < 1024 * 1024 { + return; + } let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, true); balloon -- Gitee From 624e7dc454331ffc01934c42a3cdf390b4426e4e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 27 Feb 2023 10:35:54 +0800 Subject: [PATCH 0846/2187] MST: Add pci mst testcase Add pci mst testcase. Signed-off-by: Jinhao Gao --- tests/mod_test/src/libdriver/pci.rs | 149 +- tests/mod_test/src/libdriver/pci_bus.rs | 93 +- tests/mod_test/src/libdriver/usb.rs | 17 +- .../src/libdriver/virtio_pci_modern.rs | 25 +- tests/mod_test/src/libdriver/vnc.rs | 12 +- tests/mod_test/src/libtest.rs | 8 + tests/mod_test/src/utils.rs | 6 + tests/mod_test/tests/pci_test.rs | 2276 +++++++++++++++++ 8 files changed, 2471 insertions(+), 115 deletions(-) create mode 100644 tests/mod_test/tests/pci_test.rs diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 4a1534589..43b432119 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -18,33 +18,67 @@ use std::rc::Rc; const BAR_MAP: [u8; 6] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; pub const PCI_VENDOR_ID: u8 = 0x00; pub const PCI_DEVICE_ID: u8 = 0x02; -const PCI_COMMAND: u8 = 0x04; +pub const PCI_COMMAND: u8 = 0x04; + const PCI_COMMAND_IO: u8 = 0x1; const PCI_COMMAND_MEMORY: u8 = 0x2; const PCI_COMMAND_MASTER: u8 = 0x4; +pub const PCI_STATUS: u8 = 0x06; +pub const PCI_STATUS_INTERRUPT: u16 = 0x08; +pub const PCI_STATUS_CAP_LIST: u16 = 0x10; +pub const PCI_REVISION_ID: u8 = 0x08; +pub const PCI_SUB_CLASS_DEVICE: u8 = 0x0a; +pub const PCI_HEADER_TYPE: u8 = 0x0e; +pub const PCI_PRIMARY_BUS: u8 = 0x18; +pub const PCI_SECONDARY_BUS: u8 = 0x19; +pub const PCI_SUBORDINATE_BUS: u8 = 0x1a; +pub const PCI_SUBSYSTEM_VENDOR_ID: u8 = 0x2c; pub const PCI_SUBSYSTEM_ID: u8 = 0x2e; -const PCI_CAPABILITY_LIST: u8 = 0x34; - -const PCI_CAP_LIST_NEXT: u8 = 1; -pub const PCI_CAP_ID_VNDR: u8 = 0x09; -const PCI_CAP_ID_MSIX: u8 = 0x11; -const PCI_MSIX_FLAGS: u8 = 2; -const PCI_MSIX_FLAGS_QSIZE: u16 = 0x07FF; -const PCI_MSIX_TABLE: u8 = 4; -const PCI_MSIX_PBA: u8 = 8; -const PCI_MSIX_FLAGS_MASKALL: u16 = 0x4000; -const PCI_MSIX_FLAGS_ENABLE: u16 = 0x8000; -const PCI_MSIX_TABLE_BIR: u32 = 0x00000007; +pub const PCI_CAPABILITY_LIST: u8 = 0x34; +pub const PCI_BRIDGE_CONTROL: u8 = 0x3e; -const PCI_MSIX_ENTRY_SIZE: u16 = 16; +pub const PCI_CAP_LIST_NEXT: u8 = 1; +pub const PCI_CAP_ID_VNDR: u8 = 0x09; +pub const PCI_CAP_ID_EXP: u8 = 0x10; +pub const PCI_CAP_ID_MSIX: u8 = 0x11; + +pub const PCI_MSIX_MSG_CTL: u8 = 2; +pub const PCI_MSIX_MSG_CTL_TSIZE: u16 = 0x07FF; +pub const PCI_MSIX_MSG_CTL_MASKALL: u16 = 0x4000; +pub const PCI_MSIX_MSG_CTL_ENABLE: u16 = 0x8000; +pub const PCI_MSIX_TABLE: u8 = 4; +pub const PCI_MSIX_TABLE_BIR: u32 = 0x00000007; +pub const PCI_MSIX_PBA: u8 = 8; +pub const PCI_MSIX_PBA_BIR: u32 = 0x00000007; + +pub const PCI_MSIX_ENTRY_SIZE: u16 = 16; pub const PCI_MSIX_ENTRY_LOWER_ADDR: u64 = 0x0; pub const PCI_MSIX_ENTRY_UPPER_ADDR: u64 = 0x4; pub const PCI_MSIX_ENTRY_DATA: u64 = 0x8; pub const PCI_MSIX_ENTRY_VECTOR_CTRL: u64 = 0xc; pub const PCI_MSIX_ENTRY_CTRL_MASKBIT: u32 = 0x00000001; +pub const PCI_EXP_LNKSTA: u8 = 0x12; +pub const PCI_EXP_LNKSTA_CLS: u16 = 0x000f; +pub const PCI_EXP_LNKSTA_NLW: u16 = 0x03f0; +pub const PCI_EXP_LNKSTA_DLLLA: u16 = 0x2000; + +pub const PCI_EXP_SLTSTA: u8 = 0x1a; +pub const PCI_EXP_SLTSTA_ABP: u16 = 0x0001; +pub const PCI_EXP_SLTSTA_PDC: u16 = 0x0008; +pub const PCI_EXP_SLTSTA_CC: u16 = 0x0010; +pub const PCI_EXP_SLTSTA_PDS: u16 = 0x0040; + +pub const PCI_EXP_SLTCTL: u8 = 0x18; +pub const PCI_EXP_SLTCTL_PIC: u16 = 0x0300; +pub const PCI_EXP_SLTCTL_PWR_IND_ON: u16 = 0x0100; +pub const PCI_EXP_SLTCTL_PWR_IND_BLINK: u16 = 0x0200; +pub const PCI_EXP_SLTCTL_PWR_IND_OFF: u16 = 0x0300; +pub const PCI_EXP_SLTCTL_PCC: u16 = 0x0400; +pub const PCI_EXP_SLTCTL_PWR_ON: u16 = 0x0000; +pub const PCI_EXP_SLTCTL_PWR_OFF: u16 = 0x0400; pub type PCIBarAddr = u64; pub const INVALID_BAR_ADDR: u64 = u64::MAX; @@ -52,9 +86,11 @@ pub trait PciMsixOps { fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32); } +#[derive(Clone)] pub struct TestPciDev { pub pci_bus: Rc>, - pub devfn: u32, + pub bus_num: u8, + pub devfn: u8, pub msix_enabled: bool, pub msix_table_bar: PCIBarAddr, msix_pba_bar: PCIBarAddr, @@ -67,6 +103,7 @@ impl TestPciDev { pub fn new(pci_bus: Rc>) -> Self { Self { pci_bus, + bus_num: 0, devfn: 0, msix_enabled: false, msix_table_bar: 0, @@ -77,6 +114,10 @@ impl TestPciDev { } } + pub fn set_bus_num(&mut self, bus_num: u8) { + self.bus_num = bus_num; + } + pub fn enable(&self) { let mut cmd = self.config_readw(PCI_COMMAND); cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER) as u16; @@ -115,8 +156,8 @@ impl TestPciDev { pub fn enable_msix(&mut self, bar_addr: Option) { let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); assert!(addr != 0); - let value = self.config_readw(addr + PCI_MSIX_FLAGS); - self.config_writew(addr + PCI_MSIX_FLAGS, value | PCI_MSIX_FLAGS_ENABLE); + let value = self.config_readw(addr + PCI_MSIX_MSG_CTL); + self.config_writew(addr + PCI_MSIX_MSG_CTL, value | PCI_MSIX_MSG_CTL_ENABLE); let table = self.config_readl(addr + PCI_MSIX_TABLE); let bar_table = table & PCI_MSIX_TABLE_BIR; @@ -141,52 +182,28 @@ impl TestPciDev { pub fn disable_msix(&mut self) { let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); assert!(addr != 0); - let value = self.config_readw(addr + PCI_MSIX_FLAGS); - self.config_writew(addr + PCI_MSIX_FLAGS, value & !PCI_MSIX_FLAGS_ENABLE); + let value = self.config_readw(addr + PCI_MSIX_MSG_CTL); + self.config_writew(addr + PCI_MSIX_MSG_CTL, value & !PCI_MSIX_MSG_CTL_ENABLE); self.msix_enabled = false; self.msix_table_off = 0; self.msix_pba_off = 0; } - pub fn msix_is_pending(&self, entry: u16) -> bool { - assert!(self.msix_enabled); - let bit = entry % 32; - let offset = (entry / 32) * 4; - - let pba_entry = self.io_readl(self.msix_pba_bar, self.msix_pba_off + offset as u64); - self.io_writel( - self.msix_pba_bar, - self.msix_pba_off + offset as u64, - pba_entry & !(1 << bit), - ); - (pba_entry & (1 << bit)) != 0 - } - - pub fn msix_is_masked(&self, entry: u16) -> bool { - assert!(self.msix_enabled); - let offset: u64 = self.msix_table_off + (entry * PCI_MSIX_ENTRY_SIZE) as u64; - - let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); - assert!(addr != 0); - let value = self.config_readw(addr + PCI_MSIX_FLAGS); - - if (value & PCI_MSIX_FLAGS_MASKALL) != 0 { - true - } else { - self.io_readl( - self.msix_table_bar, - offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, - ) != 0 - } + pub fn has_msix(&self, msix_addr: u64, msix_data: u32) -> bool { + self.pci_bus + .borrow() + .test_state + .borrow() + .query_msix(msix_addr, msix_data) } pub fn get_msix_table_size(&self) -> u16 { let addr = self.find_capability(PCI_CAP_ID_MSIX, 0); assert!(addr != 0); - let value = self.config_readw(addr + PCI_MSIX_FLAGS); - (value & PCI_MSIX_FLAGS_QSIZE) + 1 + let value = self.config_readw(addr + PCI_MSIX_MSG_CTL); + (value & PCI_MSIX_MSG_CTL_TSIZE) + 1 } pub fn io_readb(&self, bar_addr: PCIBarAddr, offset: u64) -> u8 { @@ -270,34 +287,52 @@ impl TestPciDev { } pub fn config_readb(&self, offset: u8) -> u8 { - self.pci_bus.borrow().config_readb(self.devfn, offset) + self.pci_bus + .borrow() + .config_readb(self.bus_num, self.devfn, offset) } pub fn config_readw(&self, offset: u8) -> u16 { - self.pci_bus.borrow().config_readw(self.devfn, offset) + self.pci_bus + .borrow() + .config_readw(self.bus_num, self.devfn, offset) } pub fn config_readl(&self, offset: u8) -> u32 { - self.pci_bus.borrow().config_readl(self.devfn, offset) + self.pci_bus + .borrow() + .config_readl(self.bus_num, self.devfn, offset) + } + + pub fn config_readq(&self, offset: u8) -> u64 { + self.pci_bus + .borrow() + .config_readq(self.bus_num, self.devfn, offset) } #[allow(unused)] pub fn config_writeb(&self, offset: u8, value: u8) { self.pci_bus .borrow() - .config_writeb(self.devfn, offset, value); + .config_writeb(self.bus_num, self.devfn, offset, value); } pub fn config_writew(&self, offset: u8, value: u16) { self.pci_bus .borrow() - .config_writew(self.devfn, offset, value); + .config_writew(self.bus_num, self.devfn, offset, value); } pub fn config_writel(&self, offset: u8, value: u32) { self.pci_bus .borrow() - .config_writel(self.devfn, offset, value); + .config_writel(self.bus_num, self.devfn, offset, value); + } + + pub fn config_writeq(&self, offset: u8, value: u64) { + self.pci_bus + .borrow() + .config_writeq(self.bus_num, self.devfn, offset, value); } } diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 1b8cf179b..eabf4f6be 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -10,8 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::libdriver::pci::*; use crate::libtest::TestState; -use crate::utils::{read_le_u16, read_le_u32}; +use crate::utils::{read_le_u16, read_le_u32, read_le_u64}; use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; use std::cell::RefCell; use std::rc::Rc; @@ -24,13 +25,15 @@ pub trait PciBusOps { fn memread(&self, addr: u32, len: usize) -> Vec; fn memwrite(&self, addr: u32, buf: &[u8]); - fn config_readb(&self, devfn: u32, offset: u8) -> u8; - fn config_readw(&self, devfn: u32, offset: u8) -> u16; - fn config_readl(&self, devfn: u32, offset: u8) -> u32; + fn config_readb(&self, bus_num: u8, devfn: u8, offset: u8) -> u8; + fn config_readw(&self, bus_num: u8, devfn: u8, offset: u8) -> u16; + fn config_readl(&self, bus_num: u8, devfn: u8, offset: u8) -> u32; + fn config_readq(&self, bus_num: u8, devfn: u8, offset: u8) -> u64; - fn config_writeb(&self, devfn: u32, offset: u8, value: u8); - fn config_writew(&self, devfn: u32, offset: u8, value: u16); - fn config_writel(&self, devfn: u32, offset: u8, value: u32); + fn config_writeb(&self, bus_num: u8, devfn: u8, offset: u8, value: u8); + fn config_writew(&self, bus_num: u8, devfn: u8, offset: u8, value: u16); + fn config_writel(&self, bus_num: u8, devfn: u8, offset: u8, value: u32); + fn config_writeq(&self, bus_num: u8, devfn: u8, offset: u8, value: u64); } #[allow(unused)] @@ -53,8 +56,44 @@ impl TestPciBus { } } - fn get_addr(&self, devfn: u32, offset: u8) -> u64 { - self.ecam_alloc_ptr + (devfn << 12 | offset as u32) as u64 + fn get_addr(&self, bus_num: u8, devfn: u8, offset: u8) -> u64 { + self.ecam_alloc_ptr + ((bus_num as u32) << 20 | (devfn as u32) << 12 | offset as u32) as u64 + } + + pub fn pci_auto_bus_scan(&self, root_port_num: u8) { + let current_bus = 0; + let mut sub_bus = 0; + + for addr in 0..=root_port_num { + let devfn = addr << 3; + if current_bus == 0 && devfn == 0 { + continue; + } + + if devfn & 0x7 != 0 { + continue; + } + + if self.config_readb(current_bus, devfn, PCI_HEADER_TYPE) == 0 { + continue; + } + + let vendor_id = self.config_readw(current_bus, devfn, PCI_VENDOR_ID); + if vendor_id == 0xffff || vendor_id == 0x0000 { + continue; + } + + if self.config_readw(current_bus, devfn, PCI_SUB_CLASS_DEVICE) == 0x0604 { + self.pciauto_scan_setup_bridge(current_bus, devfn, sub_bus); + sub_bus += 1 + } + } + } + + fn pciauto_scan_setup_bridge(&self, current_bus: u8, devfn: u8, sub_bus: u8) { + self.config_writeb(current_bus, devfn, PCI_PRIMARY_BUS, 0); + self.config_writeb(current_bus, devfn, PCI_SECONDARY_BUS, sub_bus + 1); + self.config_writeb(current_bus, devfn, PCI_SUBORDINATE_BUS, sub_bus + 1); } } @@ -67,37 +106,49 @@ impl PciBusOps for TestPciBus { self.test_state.borrow().memwrite(addr as u64, buf); } - fn config_readb(&self, devfn: u32, offset: u8) -> u8 { - let addr = self.get_addr(devfn, offset); + fn config_readb(&self, bus_num: u8, devfn: u8, offset: u8) -> u8 { + let addr = self.get_addr(bus_num, devfn, offset); self.test_state.borrow().memread(addr, 1)[0] } - fn config_readw(&self, devfn: u32, offset: u8) -> u16 { - let addr = self.get_addr(devfn, offset); + fn config_readw(&self, bus_num: u8, devfn: u8, offset: u8) -> u16 { + let addr = self.get_addr(bus_num, devfn, offset); let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 2)[0..2]; read_le_u16(&mut buf) } - fn config_readl(&self, devfn: u32, offset: u8) -> u32 { - let addr = self.get_addr(devfn, offset); + fn config_readl(&self, bus_num: u8, devfn: u8, offset: u8) -> u32 { + let addr = self.get_addr(bus_num, devfn, offset); let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 4)[0..4]; read_le_u32(&mut buf) } - fn config_writeb(&self, devfn: u32, offset: u8, value: u8) { - let addr = self.get_addr(devfn, offset); + fn config_readq(&self, bus_num: u8, devfn: u8, offset: u8) -> u64 { + let addr = self.get_addr(bus_num, devfn, offset); + let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 4)[0..8]; + read_le_u64(&mut buf) + } + + fn config_writeb(&self, bus_num: u8, devfn: u8, offset: u8, value: u8) { + let addr = self.get_addr(bus_num, devfn, offset); + let buf = value.to_le_bytes(); + self.test_state.borrow().memwrite(addr, &buf); + } + + fn config_writew(&self, bus_num: u8, devfn: u8, offset: u8, value: u16) { + let addr = self.get_addr(bus_num, devfn, offset); let buf = value.to_le_bytes(); self.test_state.borrow().memwrite(addr, &buf); } - fn config_writew(&self, devfn: u32, offset: u8, value: u16) { - let addr = self.get_addr(devfn, offset); + fn config_writel(&self, bus_num: u8, devfn: u8, offset: u8, value: u32) { + let addr = self.get_addr(bus_num, devfn, offset); let buf = value.to_le_bytes(); self.test_state.borrow().memwrite(addr, &buf); } - fn config_writel(&self, devfn: u32, offset: u8, value: u32) { - let addr = self.get_addr(devfn, offset); + fn config_writeq(&self, bus_num: u8, devfn: u8, offset: u8, value: u64) { + let addr = self.get_addr(bus_num, devfn, offset); let buf = value.to_le_bytes(); self.test_state.borrow().memwrite(addr, &buf); } diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 70dc06719..948846cc6 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -672,7 +672,7 @@ impl TestXhciPciDevice { } pub fn init_pci_device(&mut self, pci_slot: u8, pci_fn: u8) { - let devfn = (pci_slot << 3 | pci_fn) as u32; + let devfn = pci_slot << 3 | pci_fn; assert!(self.find_pci_device(devfn)); self.pci_dev.enable(); @@ -1018,11 +1018,7 @@ impl TestXhciPciDevice { pub fn fetch_event(&mut self, intr_idx: usize) -> Option { const MSIX_LIMIT: u32 = 4; for _ in 0..MSIX_LIMIT { - if self.has_msix( - self.config_msix_entry, - self.config_msix_addr, - self.config_msix_data, - ) { + if self.has_msix(self.config_msix_addr, self.config_msix_data) { for _ in 0..EVENT_RING_LEN { let ptr = self.xhci.interrupter[intr_idx].er_pointer; let trb = self.read_event(ptr); @@ -1373,11 +1369,11 @@ impl TestXhciPciDevice { evt_seg } - fn set_devfn(&mut self, devfn: u32) { + fn set_devfn(&mut self, devfn: u8) { self.pci_dev.devfn = devfn; } - fn find_pci_device(&mut self, devfn: u32) -> bool { + fn find_pci_device(&mut self, devfn: u8) -> bool { self.set_devfn(devfn); if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { return false; @@ -1399,10 +1395,7 @@ impl TestXhciPciDevice { addr } - fn has_msix(&mut self, msix_entry: u16, msix_addr: u64, msix_data: u32) -> bool { - if self.pci_dev.msix_is_masked(msix_entry) { - return self.pci_dev.msix_is_pending(msix_entry); - } + fn has_msix(&mut self, msix_addr: u64, msix_data: u32) -> bool { self.pci_dev .pci_bus .borrow() diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index 591a0d88c..feb4ca0f0 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -82,7 +82,7 @@ pub trait VirtioPCIMSIXOps { pub struct TestVirtioPciDev { pub pci_dev: TestPciDev, pub bar: PCIBarAddr, - bar_idx: u8, + pub bar_idx: u8, pub config_msix_entry: u16, pub config_msix_addr: u64, pub config_msix_data: u32, @@ -113,7 +113,7 @@ impl TestVirtioPciDev { } pub fn init(&mut self, pci_slot: u8, pci_fn: u8) { - let devfn = (pci_slot << 3 | pci_fn) as u32; + let devfn = pci_slot << 3 | pci_fn; assert!(self.find_pci_device(devfn)); let device_type = self.pci_device_type_probe().unwrap_or(0); @@ -127,7 +127,7 @@ impl TestVirtioPciDev { self.bar = self.pci_dev.io_map(self.bar_idx); } - fn find_pci_device(&mut self, devfn: u32) -> bool { + fn find_pci_device(&mut self, devfn: u8) -> bool { self.pci_dev.devfn = devfn; self.pci_dev.config_readw(PCI_VENDOR_ID) != 0xFFFF } @@ -266,17 +266,8 @@ impl TestVirtioPciDev { self.set_config_vector(self.config_msix_entry); } - fn has_msix(&self, msix_entry: u16, msix_addr: u64, msix_data: u32) -> bool { - assert!(msix_entry != 0xFFFF); - if self.pci_dev.msix_is_masked(msix_entry) { - return self.pci_dev.msix_is_pending(msix_entry); - } - self.pci_dev - .pci_bus - .borrow() - .test_state - .borrow() - .query_msix(msix_addr, msix_data) + fn has_msix(&self, msix_addr: u64, msix_data: u32) -> bool { + return self.pci_dev.has_msix(msix_addr, msix_data); } pub fn setup_virtqueue_intr( @@ -534,11 +525,7 @@ impl VirtioDeviceOps for TestVirtioPciDev { fn queue_was_notified(&self, virtqueue: Rc>) -> bool { assert!(self.pci_dev.msix_enabled); - return self.has_msix( - virtqueue.borrow().msix_entry, - virtqueue.borrow().msix_addr, - virtqueue.borrow().msix_data, - ); + return self.has_msix(virtqueue.borrow().msix_addr, virtqueue.borrow().msix_data); } fn setup_virtqueue( diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index f75265b32..f4c0b29e0 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -1170,11 +1170,11 @@ impl TestDemoGpuDevice { println!("cmd : {:?}", cmd); } - pub fn set_devfn(&mut self, devfn: u32) { + pub fn set_devfn(&mut self, devfn: u8) { self.pci_dev.devfn = devfn; } - pub fn find_pci_device(&mut self, devfn: u32) -> bool { + pub fn find_pci_device(&mut self, devfn: u8) -> bool { self.set_devfn(devfn); if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { return false; @@ -1183,7 +1183,7 @@ impl TestDemoGpuDevice { } pub fn init(&mut self, pci_slot: u8) { - let devfn = (pci_slot << 3) as u32; + let devfn = pci_slot << 3; assert!(self.find_pci_device(devfn)); self.pci_dev.enable(); @@ -1233,11 +1233,11 @@ impl TestDemoInputDevice { msg } - pub fn set_devfn(&mut self, devfn: u32) { + pub fn set_devfn(&mut self, devfn: u8) { self.pci_dev.devfn = devfn; } - pub fn find_pci_device(&mut self, devfn: u32) -> bool { + pub fn find_pci_device(&mut self, devfn: u8) -> bool { self.set_devfn(devfn); if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { return false; @@ -1246,7 +1246,7 @@ impl TestDemoInputDevice { } pub fn init(&mut self, pci_slot: u8) { - let devfn = (pci_slot << 3) as u32; + let devfn = pci_slot << 3; assert!(self.find_pci_device(devfn)); self.pci_dev.enable(); diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index be1888f1a..1d47e8e28 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -112,6 +112,14 @@ impl TestState { assert!(resp.get("QMP").is_some()); } + pub fn wait_qmp_event(&self) -> Value { + let timeout = Duration::from_secs(10); + let resp: Value = + serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap(); + assert!(resp.get("event").is_some()); + return resp; + } + pub fn qmp(&self, cmd: &str) -> Value { let timeout = Duration::from_secs(10); self.qmp_sock.write_line(cmd); diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index 783965a39..77a8672b6 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -46,6 +46,12 @@ pub fn read_le_u32(input: &mut &[u8]) -> u32 { u32::from_le_bytes(int_bytes.try_into().unwrap()) } +pub fn read_le_u64(input: &mut &[u8]) -> u64 { + let (int_bytes, rest) = input.split_at(std::mem::size_of::()); + *input = rest; + u64::from_le_bytes(int_bytes.try_into().unwrap()) +} + pub fn swap_u16(value: u16) -> u16 { return value << 8 | value >> 8; } diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs new file mode 100644 index 000000000..756f65dc4 --- /dev/null +++ b/tests/mod_test/tests/pci_test.rs @@ -0,0 +1,2276 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::pci::*; +use mod_test::libdriver::pci_bus::{PciBusOps, TestPciBus}; +use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps, VIRTIO_F_VERSION_1}; +use mod_test::libdriver::virtio_block::{ + add_blk_request, virtio_blk_defalut_feature, virtio_blk_read, virtio_blk_write, + VIRTIO_BLK_T_OUT, +}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libtest::{test_init, TestState}; +use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; + +use serde_json::json; +use std::cell::RefCell; +use std::rc::Rc; +use std::time; + +const VIRTIO_PCI_VENDOR: u16 = 0x1af4; +const BLK_DEVICE_ID: u16 = 0x1042; +const MAX_DEVICE_NUM_IN_MULTIFUNC: u8 = 248; +const MAX_DEVICE_NUM: u8 = 32; +const TIMEOUT_S: u64 = 5; + +#[derive(Default, Clone)] +pub struct MsixVector { + pub msix_entry: u16, + pub msix_addr: u64, + pub msix_data: u32, +} + +impl MsixVector { + fn new(entry: u16, alloc: Rc>) -> Self { + Self { + msix_entry: entry, + msix_addr: alloc.borrow_mut().alloc(4), + msix_data: 0x12345678, + } + } +} + +pub struct RootPort { + pub rp_dev: TestPciDev, + pub rp_misx_vector: MsixVector, +} + +impl RootPort { + fn new( + machine: Rc>, + alloc: Rc>, + bus_num: u8, + devfn: u8, + ) -> Self { + let mut root_port = TestPciDev::new(machine.clone().borrow().pci_bus.clone()); + root_port.set_bus_num(bus_num); + root_port.devfn = devfn; + assert_eq!(root_port.config_readw(PCI_SUB_CLASS_DEVICE), 0x0604); + root_port.enable(); + root_port.enable_msix(None); + let root_port_msix = MsixVector::new(0, alloc.clone()); + root_port.set_msix_vector( + root_port_msix.msix_entry, + root_port_msix.msix_addr, + root_port_msix.msix_data, + ); + Self { + rp_dev: root_port, + rp_misx_vector: root_port_msix, + } + } +} + +fn build_root_port_args(root_port_nums: u8) -> Vec { + if root_port_nums == 0 { + return Vec::new(); + } + let mut multifunc = false; + if root_port_nums > 32 { + multifunc = true; + } + let mut root_port_args: Vec = Vec::with_capacity(root_port_nums.try_into().unwrap()); + let mut addr = 1; + let mut func = 0; + for bus in 1..=root_port_nums { + let mut arg = format!( + "-device pcie-root-port,port=0x0,chassis=1,bus=pcie.0,addr={:#x}.{},id=pcie.{}", + addr, func, bus + ); + + if func == 0 && multifunc { + arg.push_str(",multifunction=on"); + } + + if multifunc { + addr = bus / 8 + 1; + func += 1; + func = func % 8; + } else { + addr += 1; + func = 0; + } + + root_port_args.push(arg); + } + root_port_args +} + +fn build_blk_args( + blk_nums: u8, + attach_in_rp: bool, + multifunc: bool, +) -> Vec<(String, u8, u8, u8, u8, bool)> { + if multifunc { + assert!(blk_nums < MAX_DEVICE_NUM_IN_MULTIFUNC); + } else { + assert!(blk_nums < MAX_DEVICE_NUM); + } + + let mut blk_args: Vec<(String, u8, u8, u8, u8, bool)> = + Vec::with_capacity(blk_nums.try_into().unwrap()); + let mut slot = 0; + let mut func = 0; + let mut nums = 0; + let mut bus = 0; + + if attach_in_rp { + bus = 1; + } else { + slot = 1; + } + + while nums < blk_nums { + if multifunc { + if func % 8 == 0 { + blk_args.push((String::from("virtio-blk-pci"), nums, bus, slot, func, true)); + } else { + blk_args.push((String::from("virtio-blk-pci"), nums, bus, slot, func, false)); + } + func += 1; + slot += func / 8 + } else { + blk_args.push((String::from("virtio-blk-pci"), nums, bus, slot, func, false)); + bus += 1; + } + nums += 1; + } + + blk_args +} + +fn build_blk_driver_args(blk_nums: u8) -> (Vec, Vec) { + let mut driver_args: Vec = Vec::new(); + let mut image_paths: Vec = Vec::new(); + + for i in 0..blk_nums { + let image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(image_path.clone()); + let driver_arg_str = format!( + "-drive if=none,id=drive-{},file={},format=raw,direct=false", + i, image_path + ); + driver_args.push(driver_arg_str.clone()); + } + + (driver_args, image_paths) +} + +fn build_hotplug_blk_cmd( + hotplug_blk_id: u8, + hotplug_image_path: String, + bus_num: u8, + slot: u8, + func: u8, +) -> (String, String) { + let add_blk_command = format!( + "{{\"execute\": \"blockdev-add\", \ + \"arguments\": {{\"node-name\": \"drive-{}\", \"file\": {{\"driver\": \ + \"file\", \"filename\": \"{}\"}}, \"cache\": {{\"direct\": true}}, \ + \"read-only\": false}}}}", + hotplug_blk_id, hotplug_image_path + ); + let add_device_command = format!( + "{{\"execute\":\"device_add\", \ + \"arguments\": {{\"id\":\"blk-{}\", \"driver\":\"virtio-blk-pci\", \ + \"drive\": \"drive-{}\", \"addr\":\"{:#x}.{:#x}\", \"bus\": \"pcie.{}\"}}}}", + hotplug_blk_id, hotplug_blk_id, slot, func, bus_num + ); + (add_blk_command, add_device_command) +} + +fn build_hotunplug_blk_cmd(unplug_blk_id: u8) -> (String, String) { + let delete_device_command = format!( + "{{\"execute\": \"device_del\",\ + \"arguments\": {{\"id\":\"blk-{}\"}}}}", + unplug_blk_id + ); + let delete_blk_command = format!( + "{{\"execute\": \"blockdev-del\",\ + \"arguments\": {{\"node-name\":\"drive-{}\"}}}}", + unplug_blk_id + ); + + (delete_device_command, delete_blk_command) +} + +fn build_all_device_args( + root_port_nums: u8, + pci_device_param: Vec<(String, u8, u8, u8, u8, bool)>, +) -> Vec { + let mut device_args: Vec = Vec::new(); + let mut root_port_args = build_root_port_args(root_port_nums); + if root_port_args.len() != 0 { + device_args.append(&mut root_port_args); + } + + for i in 0..pci_device_param.len() { + let mut device_arg_str = format!( + "-device {},id=blk-{},drive=drive-{},bus=pcie.{},addr={}.{}", + pci_device_param.get(i).unwrap().0, + pci_device_param.get(i).unwrap().1, + pci_device_param.get(i).unwrap().1, + pci_device_param.get(i).unwrap().2, + pci_device_param.get(i).unwrap().3, + pci_device_param.get(i).unwrap().4, + ); + if pci_device_param.get(i).unwrap().5 { + let multi_func_arg = String::from(",multifunction=on"); + device_arg_str.push_str(&multi_func_arg); + } + device_args.push(device_arg_str.clone()); + } + + device_args +} + +fn create_blk( + machine: Rc>, + bus_num: u8, + pci_slot: u8, + pci_fn: u8, +) -> Rc> { + let virtio_blk = Rc::new(RefCell::new(TestVirtioPciDev::new( + machine.clone().borrow().pci_bus.clone(), + ))); + virtio_blk.borrow_mut().pci_dev.set_bus_num(bus_num); + virtio_blk.borrow_mut().init(pci_slot, pci_fn); + virtio_blk +} + +fn create_machine( + root_port_nums: u8, + device_args: Vec, + driver_args: Vec, + other_args: Option>, +) -> ( + Rc>, + Rc>, + Rc>, +) { + let mut extra_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + extra_args.append(&mut args); + + for device_arg in device_args.iter() { + let mut arg = device_arg[..].split(' ').collect(); + extra_args.append(&mut arg); + } + + for driver_arg in driver_args.iter() { + let mut arg = driver_arg[..].split(' ').collect(); + extra_args.append(&mut arg); + } + + let mut args: Vec = Vec::new(); + if other_args.is_some() { + args = other_args.unwrap(); + } + for other_arg in args.iter() { + let mut arg = other_arg[..].split(' ').collect(); + extra_args.append(&mut arg); + } + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = Rc::new(RefCell::new(TestStdMachine::new(test_state.clone()))); + machine + .borrow() + .pci_bus + .borrow() + .pci_auto_bus_scan(root_port_nums as u8); + let allocator = machine.borrow().allocator.clone(); + (test_state, machine, allocator) +} + +fn set_up( + root_port_nums: u8, + blk_nums: u8, + attach_in_rp: bool, + multifunc: bool, +) -> ( + Rc>, + Rc>, + Rc>, + Vec, +) { + let device_args = build_all_device_args( + root_port_nums, + build_blk_args(blk_nums, attach_in_rp, multifunc), + ); + let (blk_driver_args, image_paths) = build_blk_driver_args(blk_nums); + let (test_state, machine, alloc) = + create_machine(root_port_nums, device_args, blk_driver_args, None); + (test_state, machine, alloc, image_paths) +} + +fn tear_down( + blk: Option>>, + test_state: Rc>, + alloc: Rc>, + vqs: Option>>>, + image_paths: Option>, +) { + if blk.is_some() { + blk.clone().unwrap().borrow_mut().reset(); + blk.clone().unwrap().borrow_mut().pci_dev.disable_msix(); + } + if vqs.is_some() { + blk.clone() + .unwrap() + .borrow_mut() + .destroy_device(alloc.clone(), vqs.unwrap()); + } + + test_state.borrow_mut().stop(); + if let Some(img_paths) = image_paths { + img_paths.iter().enumerate().for_each(|(_i, image_path)| { + cleanup_img(image_path.to_string()); + }) + } +} + +fn validate_config_value_2byte( + pci_bus: Rc>, + bus_num: u8, + devfn: u8, + offset: u8, + expected_value: u16, + mask: u16, +) { + let config_value = pci_bus.borrow().config_readw(bus_num, devfn, offset); + assert_eq!(config_value & mask, expected_value); +} + +fn validate_config_perm_1byte( + pci_dev: TestPciDev, + offset: u8, + expected_value: u8, + writed_value: u8, + mask: u8, +) { + let config_value = + pci_dev + .pci_bus + .borrow() + .config_readb(pci_dev.bus_num, pci_dev.devfn, offset); + assert_eq!(config_value & mask, expected_value); + pci_dev + .pci_bus + .borrow() + .config_writeb(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + let config_value = + pci_dev + .pci_bus + .borrow() + .config_readb(pci_dev.bus_num, pci_dev.devfn, offset); + assert_eq!(config_value & mask, expected_value); +} + +fn validate_config_perm_2byte( + pci_dev: TestPciDev, + offset: u8, + expected_value: u16, + writed_value: u16, + mask: u16, +) { + pci_dev + .pci_bus + .borrow() + .config_writew(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + let config_value = + pci_dev + .pci_bus + .borrow() + .config_readw(pci_dev.bus_num, pci_dev.devfn, offset); + assert_eq!(config_value & mask, expected_value); +} + +#[allow(unused)] +fn validate_config_perm_4byte( + pci_dev: TestPciDev, + offset: u8, + expected_value: u32, + writed_value: u32, + mask: u32, +) { + let config_value = + pci_dev + .pci_bus + .borrow() + .config_readl(pci_dev.bus_num, pci_dev.devfn, offset); + assert_eq!(config_value & mask, expected_value); + pci_dev + .pci_bus + .borrow() + .config_writel(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + let config_value = + pci_dev + .pci_bus + .borrow() + .config_readl(pci_dev.bus_num, pci_dev.devfn, offset); + assert_eq!(config_value & mask, expected_value); +} + +fn get_slot_ctl_val(root_port: Rc>) -> (u16, u8) { + let rp_borrowed = root_port.borrow(); + let exp_cap_addr = rp_borrowed.rp_dev.find_capability(0x10, 0); + let slot_ctl = rp_borrowed + .rp_dev + .config_readw(exp_cap_addr + PCI_EXP_SLTCTL); + + (slot_ctl, exp_cap_addr) +} + +fn power_on_device(root_port: Rc>) { + let (slot_ctl, addr) = get_slot_ctl_val(root_port.clone()); + + let mask = PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_PCC; + root_port.borrow().rp_dev.config_writew( + addr + PCI_EXP_SLTCTL, + (slot_ctl & !mask) | PCI_EXP_SLTCTL_PWR_IND_ON | PCI_EXP_SLTCTL_PWR_ON, + ); +} + +fn power_off_device(root_port: Rc>) { + let (slot_ctl, addr) = get_slot_ctl_val(root_port.clone()); + + let mask = PCI_EXP_SLTCTL_PIC | PCI_EXP_SLTCTL_PCC; + root_port.borrow().rp_dev.config_writew( + addr + PCI_EXP_SLTCTL, + (slot_ctl & !mask) | PCI_EXP_SLTCTL_PWR_IND_OFF | PCI_EXP_SLTCTL_PWR_OFF, + ); +} + +fn power_indicator_blink(root_port: Rc>) { + let (slot_ctl, addr) = get_slot_ctl_val(root_port.clone()); + + let mask = PCI_EXP_SLTCTL_PIC; + root_port.borrow().rp_dev.config_writew( + addr + PCI_EXP_SLTCTL, + (slot_ctl & !mask) | PCI_EXP_SLTCTL_PWR_IND_BLINK, + ); +} + +fn power_indicator_off(root_port: Rc>) { + let (slot_ctl, addr) = get_slot_ctl_val(root_port.clone()); + + let mask = PCI_EXP_SLTCTL_PIC; + root_port.borrow().rp_dev.config_writew( + addr + PCI_EXP_SLTCTL, + (slot_ctl & !mask) | PCI_EXP_SLTCTL_PWR_IND_OFF, + ); +} + +fn validate_blk_io_success( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, +) { + let features = virtio_blk_defalut_feature(blk.clone()); + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + validate_std_blk_io( + blk.clone(), + test_state.clone(), + virtqueues.clone(), + alloc.clone(), + ); + + blk.borrow_mut().pci_dev.disable_msix(); + blk.borrow() + .cleanup_virtqueue(alloc, virtqueues[0].borrow().desc); +} + +fn simple_blk_io_req( + blk: Rc>, + test_state: Rc>, + virtqueue: Rc>, + alloc: Rc>, +) -> u32 { + let (free_head, _req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueue.clone(), + VIRTIO_BLK_T_OUT, + 0, + false, + ); + blk.borrow().virtqueue_notify(virtqueue.clone()); + free_head +} + +fn wait_msix_timeout( + blk: Rc>, + virtqueue: Rc>, + timeout_us: u64, +) -> bool { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(timeout_us); + + loop { + if blk.borrow().queue_was_notified(virtqueue.clone()) { + return false; + } + + if time::Instant::now() - start_time > timeout_us { + return true; + } + } +} + +fn validate_std_blk_io( + blk: Rc>, + test_state: Rc>, + virtqueues: Vec>>, + alloc: Rc>, +) { + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + false, + ); + + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + false, + ); +} + +fn wait_root_port_intr(root_port: Rc>) -> bool { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_secs(TIMEOUT_S); + let rp_borrowed = root_port.borrow(); + loop { + if rp_borrowed.rp_dev.has_msix( + rp_borrowed.rp_misx_vector.msix_addr, + rp_borrowed.rp_misx_vector.msix_data, + ) { + return true; + } + if (time::Instant::now() - start_time) >= timeout_us { + return false; + } + } +} + +fn wait_cci_set(root_port: Rc>) -> bool { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_secs(TIMEOUT_S); + let rp_borrowed = root_port.borrow(); + let cci_mask = PCI_EXP_SLTSTA_CC; + let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); + + loop { + if rp_borrowed + .rp_dev + .config_readw(cap_exp_addr + PCI_EXP_SLTSTA) + & cci_mask + == 1 + { + return true; + } + if (time::Instant::now() - start_time) >= timeout_us { + return false; + } + } +} + +fn lookup_all_cap_addr(cap_id: u8, pci_dev: TestPciDev) -> Vec { + let mut addr = pci_dev.config_readb(PCI_CAPABILITY_LIST); + let mut cap_addrs: Vec = Vec::new(); + loop { + let cap = pci_dev.config_readb(addr); + if cap == cap_id { + cap_addrs.push(addr); + } + + addr = pci_dev.config_readb(addr + PCI_CAP_LIST_NEXT); + if addr == 0 { + break; + } + } + cap_addrs +} + +fn get_msix_flag(pci_dev: TestPciDev) -> u16 { + let addr = pci_dev.find_capability(PCI_CAP_ID_MSIX, 0); + assert_ne!(addr, 0); + let old_value = pci_dev.config_readw(addr + PCI_MSIX_MSG_CTL); + old_value +} + +fn set_msix_enable(pci_dev: TestPciDev) { + let addr = pci_dev.find_capability(PCI_CAP_ID_MSIX, 0); + let old_value = get_msix_flag(pci_dev.clone()); + pci_dev.config_writew(addr + PCI_MSIX_MSG_CTL, old_value | PCI_MSIX_MSG_CTL_ENABLE); +} + +fn set_msix_disable(pci_dev: TestPciDev) { + let addr = pci_dev.find_capability(PCI_CAP_ID_MSIX, 0); + let old_value = get_msix_flag(pci_dev.clone()); + pci_dev.config_writew( + addr + PCI_MSIX_MSG_CTL, + old_value & !PCI_MSIX_MSG_CTL_ENABLE, + ); +} + +fn mask_msix_global(pci_dev: TestPciDev) { + let addr = pci_dev.find_capability(PCI_CAP_ID_MSIX, 0); + let old_value = get_msix_flag(pci_dev.clone()); + pci_dev.config_writew( + addr + PCI_MSIX_MSG_CTL, + old_value | PCI_MSIX_MSG_CTL_MASKALL, + ); +} + +fn unmask_msix_global(pci_dev: TestPciDev) { + let addr = pci_dev.find_capability(PCI_CAP_ID_MSIX, 0); + let old_value = get_msix_flag(pci_dev.clone()); + pci_dev.config_writew( + addr + PCI_MSIX_MSG_CTL, + old_value & !PCI_MSIX_MSG_CTL_MASKALL, + ); +} + +fn mask_msix_vector(pci_dev: TestPciDev, vector: u16) { + let offset: u64 = pci_dev.msix_table_off + (vector * PCI_MSIX_ENTRY_SIZE) as u64; + + let vector_mask = pci_dev.io_readl( + pci_dev.msix_table_bar, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + ); + + pci_dev.io_writel( + pci_dev.msix_table_bar, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + vector_mask | PCI_MSIX_ENTRY_CTRL_MASKBIT, + ); +} + +fn unmask_msix_vector(pci_dev: TestPciDev, vector: u16) { + let offset: u64 = pci_dev.msix_table_off + (vector * PCI_MSIX_ENTRY_SIZE) as u64; + + let vector_control = pci_dev.io_readl( + pci_dev.msix_table_bar, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + ); + + pci_dev.io_writel( + pci_dev.msix_table_bar, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + vector_control & !PCI_MSIX_ENTRY_CTRL_MASKBIT, + ); +} + +fn hotplug_blk( + test_state: Rc>, + root_port: Rc>, + image_paths: &mut Vec, + hotplug_blk_id: u8, + bus: u8, + slot: u8, + func: u8, +) { + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose bdf is 2:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), bus, slot, func); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify the vendor id for the virtio block device hotplugged. + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + bus, + slot << 3 | func, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_on_device(root_port.clone()); +} + +fn hotunplug_blk( + test_state: Rc>, + blk: Rc>, + root_port: Rc>, + hotunplug_blk_id: u8, +) { + // Hotunplug the virtio block device. + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotunplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + + power_off_device(root_port.clone()); + + assert_eq!(*ret.get("return").unwrap(), json!({})); + test_state.borrow().wait_qmp_event(); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + wait_cci_set(root_port.clone()); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); +} + +/// Query the config of the device which has attached the bus. +#[test] +fn test_pci_device_discovery_001() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(blk_nums, root_port_nums, true, false); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Verify the vendor id for non-existent devices. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + 1, + 1 << 3 | 0, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + // Verify the device id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_DEVICE_ID, + BLK_DEVICE_ID, + 0xFFFF, + ); + + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); +} + +/// Hotunplug the device which has attached the bus and hotplug another block device. +#[test] +fn test_pci_device_discovery_002() { + let blk_nums = 1; + let root_port_nums = 2; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + // Hotplug a block device whose id is 0. + hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), 0); + + // Hotplug a block device whose id is 1 and bdf is 2:0:0. + hotplug_blk( + test_state.clone(), + root_port.clone(), + &mut image_paths, + 1, + 2, + 0, + 0, + ); + + // Create a block device whose bdf is 2:0:0. + let blk = create_blk(machine.clone(), 2, 0, 0); + // Verify the vendor id for the virtio block device hotplugged. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); +} + +/// Repeat hotplug the same device and query the related ecam space(vendor id). +#[test] +fn test_pci_device_discovery_003() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Verify the vendor id for the virtio block device hotplugged. + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port_nums, + 0, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + let blk_id = 1; + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose bdf is 1:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(blk_id, hotplug_image_path.clone(), 1, 0, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + // Verify the vendor id for the virtio block device hotplugged. + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port_nums, + 0, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplug and hotunplug the same device. +#[test] +fn test_pci_device_discovery_004() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + let blk_id = 0; + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose id is 0 and bdf is 1:0:0. + hotplug_blk( + test_state.clone(), + root_port.clone(), + &mut image_paths, + blk_id, + 1, + 0, + 0, + ); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Hotunplug the virtio block device whose id is 0. + hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), blk_id); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +// Check the permission and initial value of type0 pci device's configuration space. +#[test] +fn test_pci_type0_config() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Verify that the vendor id of type0 device is read-only. + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0x1234, + 0xFFFF, + ); + // Verify that the device id of type0 device is read-only. + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + PCI_DEVICE_ID, + BLK_DEVICE_ID, + 0x1234, + 0xFFFF, + ); + + // verify that the lower three bits of the command register of type0 device is readable and writable. + validate_config_perm_2byte(blk.borrow().pci_dev.clone(), PCI_COMMAND, 0x4, 0x4, 0x7); + + // verify that the interrupt status of the status register of type0 device is read-only. + let intr_status = blk.borrow().pci_dev.pci_bus.borrow().config_readw( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_STATUS, + ) & PCI_STATUS_INTERRUPT; + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + PCI_STATUS, + intr_status, + !intr_status, + PCI_STATUS_INTERRUPT, + ); + + // verify that the capabilities list of the status register of type0 device is read-only. + let cap_list = blk.borrow().pci_dev.pci_bus.borrow().config_readw( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_STATUS, + ) & PCI_STATUS_CAP_LIST; + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + PCI_STATUS, + cap_list, + !cap_list, + PCI_STATUS_CAP_LIST, + ); + + // verify that the revision id of type0 device is read-only. + let revision_id = blk.borrow().pci_dev.pci_bus.borrow().config_readb( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_REVISION_ID, + ); + validate_config_perm_1byte( + blk.borrow().pci_dev.clone(), + PCI_REVISION_ID, + 1, + !revision_id, + 0xff, + ); + + let sub_class = blk.borrow().pci_dev.pci_bus.borrow().config_readb( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_SUB_CLASS_DEVICE, + ); + // verify that the sub class id of type0 device is read-only. + validate_config_perm_1byte( + blk.borrow().pci_dev.clone(), + PCI_SUB_CLASS_DEVICE, + sub_class, + !sub_class, + 0xFF, + ); + + // verify that the header type of type0 device is read-only. + let header_type = blk.borrow().pci_dev.pci_bus.borrow().config_readb( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_HEADER_TYPE, + ); + validate_config_perm_1byte( + blk.borrow().pci_dev.clone(), + PCI_HEADER_TYPE, + header_type, + !header_type, + 0xff, + ); + + // verify that the capabilities pointer of type0 device is read-only. + let cap_pointer = blk.borrow().pci_dev.pci_bus.borrow().config_readb( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_CAPABILITY_LIST, + ); + validate_config_perm_1byte( + blk.borrow().pci_dev.clone(), + PCI_CAPABILITY_LIST, + cap_pointer, + !cap_pointer, + 0xFF, + ); + + // verify that the sub vendor id of type0 device is read-only. + let sub_vender_id = blk.borrow().pci_dev.pci_bus.borrow().config_readw( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_SUBSYSTEM_VENDOR_ID, + ); + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + PCI_SUBSYSTEM_VENDOR_ID, + sub_vender_id, + !sub_vender_id, + 0xFFFF, + ); + // verify that the sub system id of type0 device is read-only. + let sub_system_id = blk.borrow().pci_dev.pci_bus.borrow().config_readw( + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_SUBSYSTEM_ID, + ); + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + PCI_SUBSYSTEM_ID, + sub_system_id, + !sub_system_id, + 0xFFFF, + ); + + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); +} + +// Check the permission and initial value of type1 pci device's configuration space. +#[test] +fn test_pci_type1_config() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + + assert_eq!(root_port.rp_dev.config_readb(PCI_PRIMARY_BUS), 0); + assert_ne!(root_port.rp_dev.config_readb(PCI_SECONDARY_BUS), 0); + assert_ne!(root_port.rp_dev.config_readb(PCI_SUBORDINATE_BUS), 0); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +#[test] +fn test_repeat_io_map_bar() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + // Verify that the function of the block device is normal. + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + let old_feature = blk.borrow().get_guest_features(); + let old_bar_addr = blk.borrow().bar; + + // IO map the bar of virtio block device again. + let bar_idx = blk.borrow().bar_idx; + let bar_addr = blk.borrow().pci_dev.io_map(bar_idx); + blk.borrow_mut().bar = bar_addr; + // Verify that the configuration of virtio block can be read normally. + assert_eq!(blk.borrow().get_guest_features(), old_feature); + // Verify that the common config bar of virtio block has changed. + assert_ne!(blk.borrow().bar, old_bar_addr); + + // Verify that the function of the block device is normal. + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + +#[test] +fn test_pci_type0_msix_config() { + let blk_nums = 1; + let root_port_nums = 0; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, false, false); + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 0, 1, 0); + + // Verify that there is only one msix capability addr of the type0 pci device. + let blk_cap_msix_addrs = lookup_all_cap_addr(PCI_CAP_ID_MSIX, blk.borrow().pci_dev.clone()); + assert_eq!(blk_cap_msix_addrs.len(), 1); + + // Verify that the table size of msix is read-only. + let table_size = blk + .borrow() + .pci_dev + .config_readw(blk_cap_msix_addrs[0] + PCI_MSIX_MSG_CTL) + & PCI_MSIX_MSG_CTL_TSIZE; + validate_config_perm_2byte( + blk.borrow().pci_dev.clone(), + blk_cap_msix_addrs[0] + PCI_MSIX_MSG_CTL, + table_size, + !table_size, + PCI_MSIX_MSG_CTL_TSIZE, + ); + + // Verify that the table size of msix is read-only. + let msix_table = blk + .borrow() + .pci_dev + .config_readl(blk_cap_msix_addrs[0] + PCI_MSIX_TABLE); + let msix_table_bir = msix_table & PCI_MSIX_TABLE_BIR; + // Verify that the bir of table of the type0 pci device is less than or equal to 5. + assert!(msix_table_bir <= 5); + // Verify that the msix table of the type0 pci device is read-only. + validate_config_perm_4byte( + blk.borrow().pci_dev.clone(), + blk_cap_msix_addrs[0] + PCI_MSIX_TABLE, + msix_table, + !msix_table, + 0xFFFFFFFF, + ); + + let msix_pba = blk + .borrow() + .pci_dev + .config_readl(blk_cap_msix_addrs[0] + PCI_MSIX_PBA); + let msix_pba_bir = msix_pba & PCI_MSIX_PBA_BIR; + // Verify that the bir of pba of the type0 pci device is less than or equal to 5. + assert!(msix_pba_bir <= 5); + // Verify that the msix pba of the type0 pci device is read-only. + validate_config_perm_4byte( + blk.borrow().pci_dev.clone(), + blk_cap_msix_addrs[0] + PCI_MSIX_PBA, + msix_pba, + !msix_pba, + 0xFFFFFFFF, + ); + + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); +} + +// Test whether the Function Mask bit in the control register for MSI-X works well, +// which means that when it's set, msix pends notification, and starts to notify as +// soon as the mask bit is cleared by the OS. +#[test] +fn test_pci_msix_global_ctl() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + set_msix_disable(blk.borrow().pci_dev.clone()); + let mut free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + alloc.clone(), + ); + // Verify that the os can not receive msix interrupt when msix is disabled. + assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + + set_msix_enable(blk.borrow().pci_dev.clone()); + // Verify that the os can receive msix interrupt when msix is enabled. + assert!(!wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_S, + &mut None, + false, + ); + + mask_msix_global(blk.borrow().pci_dev.clone()); + + free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + alloc.clone(), + ); + // Verify that the os can not receive msix interrupt when the function of vectors is masked. + assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + + unmask_msix_global(blk.borrow().pci_dev.clone()); + // Verify that the os can receive msix interrupt when the function of vectors is unmasked. + assert!(!wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_S, + &mut None, + false, + ); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + +// Test whether the Mask bit in the vector register in msix table works well, +// which means that when it's set, msix pends notification of the related vecotr, +// and starts to notify as soon as the mask bit is cleared by the OS. +#[test] +fn test_pci_msix_local_ctl() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + mask_msix_vector(blk.borrow().pci_dev.clone(), 1); + let free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + alloc.clone(), + ); + // Verify that the os can not receive msix interrupt when the vectors of virtqueue is masked. + assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + + unmask_msix_vector(blk.borrow().pci_dev.clone(), 1); + // Verify that the os canreceive msix interrupt when the vectors of virtqueue is unmasked. + assert!(!wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + blk.borrow().poll_used_elem( + test_state.clone(), + vqs[0].clone(), + free_head, + TIMEOUT_S, + &mut None, + false, + ); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + +/// Basic hotplug testcase. +#[test] +fn test_pci_hotplug_001() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:2:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Hotplug a block device whose id is 1 and bdf is 1:0:0. + hotplug_blk( + test_state.clone(), + root_port.clone(), + &mut image_paths, + 0, + 1, + 0, + 0, + ); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + +/// Hotplug two devices at the same time. +#[test] +fn test_pci_hotplug_002() { + let blk_nums = 0; + let root_port_nums = 2; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port_1 = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Create a root port whose bdf is 0:2:0. + let root_port_2 = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 2 << 3 | 0, + ))); + + // Hotplug a block device whose id is 1 and bdf is 1:0:0. + hotplug_blk( + test_state.clone(), + root_port_1.clone(), + &mut image_paths, + 1, + 1, + 0, + 0, + ); + let blk_1 = create_blk(machine.clone(), 1, 0, 0); + + // Hotplug a block device whose id is 2 and bdf is 2:0:0. + hotplug_blk( + test_state.clone(), + root_port_2.clone(), + &mut image_paths, + 2, + 2, + 0, + 0, + ); + let blk_2 = create_blk(machine.clone(), 2, 0, 0); + + validate_blk_io_success(blk_1.clone(), test_state.clone(), alloc.clone()); + validate_blk_io_success(blk_2.clone(), test_state.clone(), alloc.clone()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplug the device in non-zero slot. +#[test] +fn test_pci_hotplug_003() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, _machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose id is 0, bdf is 1:1:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(0, hotplug_image_path.clone(), 1, 1, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + // Verify that hotpluging the device in non-zero slot will fail. + let ret = test_state.borrow().qmp(&add_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplug the device in the bus 0. +#[test] +fn test_pci_hotplug_004() { + let blk_nums = 0; + let root_port_nums = 0; + let (test_state, _machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + let hotplug_blk_id = 1; + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), 0, 1, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplug a device which dosn't have the backend file. +#[test] +fn test_pci_hotplug_005() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, _machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + let hotplug_blk_id = 0; + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, String::from(""), 1, 0, 0); + + let ret = test_state.borrow().qmp(&add_blk_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + let ret = test_state.borrow().qmp(&add_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplug a device which dosn't have the backend file. +#[test] +fn test_pci_hotplug_006() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, _machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + let hotplug_blk_id = 0; + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, 2, 0, 0); + + let ret = test_state.borrow().qmp(&add_blk_command); + println!("ret is {}", ret); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Basic hotunplug testcase. +#[test] +fn test_pci_hotunplug_001() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Hotunplug the block device whose bdf is 1:0:0. + hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), 0); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotunplug a device that does not exist. +#[test] +fn test_pci_hotunplug_002() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, _machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Hotunplug a device that does not exist. + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(0); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotunplug a device but power indicator of root port is abnormal. +#[test] +fn test_pci_hotunplug_003() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:2:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + let unplug_blk_id = 0; + // Hotunplug the block device attaching the root port. + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + // The block device will not be unplugged when it is power on. + power_on_device(root_port.clone()); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify the vendor id for the virtio block device is correct. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + // The block device will not be unplugged when indicator of power is blinking. + power_indicator_blink(root_port.clone()); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + let (delete_device_command, _delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + // The block device will be unplugged when indicator of power and slot is power off. + power_off_device(root_port.clone()); + test_state.borrow().wait_qmp_event(); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotunplug two device at the same time. +#[test] +fn test_pci_hotunplug_004() { + let blk_nums = 2; + let root_port_nums = 2; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create root port whose bdf is 0:1:0. + let root_port_1 = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Create root port whose bdf is 0:2:0. + let root_port_2 = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 2 << 3 | 0, + ))); + + // Create a block device whose bdf is 1:0:0. + let blk_1 = create_blk(machine.clone(), 1, 0, 0); + + // Create a block device whose bdf is 2:0:0. + let blk_2 = create_blk(machine.clone(), 2, 0, 0); + + let unplug_blk_id = 0; + let (delete_device_command, delete_blk_command_1) = build_hotunplug_blk_cmd(unplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + let unplug_blk_id = 1; + let (delete_device_command, delete_blk_command_2) = build_hotunplug_blk_cmd(unplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + assert!( + wait_root_port_intr(root_port_1.clone()), + "Wait for interrupt of root port timeout" + ); + + assert!( + wait_root_port_intr(root_port_2.clone()), + "Wait for interrupt of root port timeout" + ); + + power_off_device(root_port_1.clone()); + test_state.borrow().wait_qmp_event(); + + power_off_device(root_port_2.clone()); + test_state.borrow().wait_qmp_event(); + + // The block device will be unplugged when indicator of power and slot is power off. + let ret = test_state.borrow().qmp(&delete_blk_command_1); + assert_eq!(*ret.get("return").unwrap(), json!({})); + // The block device will be unplugged when indicator of power and slot is power off. + let ret = test_state.borrow().qmp(&delete_blk_command_2); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk_1.borrow().pci_dev.pci_bus.clone(), + blk_1.borrow().pci_dev.bus_num, + blk_1.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk_2.borrow().pci_dev.pci_bus.clone(), + blk_2.borrow().pci_dev.bus_num, + blk_2.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Repeate hotunplug the same device. +#[test] +fn test_pci_hotunplug_005() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Hotplug the block device whose id is 0 and bdf is 1:0:0. + hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), 0); + + let (delete_device_command, _delete_blk_command) = build_hotunplug_blk_cmd(0); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotunplug the device attaching the host bus +#[test] +fn test_pci_hotunplug_006() { + let blk_nums = 1; + let root_port_nums = 0; + let (test_state, _machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, false, false); + + let unplug_blk_id = 0; + let (delete_device_command, _delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplug and hotunplug in sequence. +#[test] +fn test_pci_hotplug_combine_001() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:2:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + let hotplug_blk_id = 0; + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose bdf is 1:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_on_device(root_port.clone()); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + // Verify that the function of the block device is normal. + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_off_device(root_port.clone()); + + assert_eq!(*ret.get("return").unwrap(), json!({})); + test_state.borrow().wait_qmp_event(); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + let hotplug_blk_id = 1; + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose bdf is 1:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_on_device(root_port.clone()); + + // Verify the virtio block device has been plugged. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + // Verify that the function of the block device is normal. + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_off_device(root_port.clone()); + + assert_eq!(*ret.get("return").unwrap(), json!({})); + test_state.borrow().wait_qmp_event(); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify that the virtio block device has been unplugged. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotunplugging during hotplugging. +#[test] +fn test_pci_hotplug_combine_002() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + let hotplug_blk_id = 0; + // Hotplug a block device whose id is 0 and bdf is 1:0:0. + hotplug_blk( + test_state.clone(), + root_port.clone(), + &mut image_paths, + hotplug_blk_id, + 1, + 0, + 0, + ); + power_indicator_off(root_port.clone()); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + validate_blk_io_success(blk.clone(), test_state.clone(), alloc.clone()); + + // Hotplug the block device whose id is 0 and bdf is 1:0:0. + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(0); + let ret = test_state.borrow().qmp(&delete_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_indicator_blink(root_port.clone()); + + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify the virtio block device has not been unplugged. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_off_device(root_port.clone()); + test_state.borrow().wait_qmp_event(); + + let ret = test_state.borrow().qmp(&delete_blk_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + // Verify that the virtio block device has been unplugged. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Hotplugging during hotunpluging. +#[test] +fn test_pci_hotplug_combine_003() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + let hotunplug_blk_id = 0; + // Hotunplug the block device attaching the root port; + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotunplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose bdf is 1:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotunplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + + power_off_device(root_port.clone()); + test_state.borrow().wait_qmp_event(); + + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + // Hotplug a block device whose bdf is 1:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotunplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert!(!(*ret.get("error").unwrap()).is_null()); + let ret = test_state.borrow().qmp(&add_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + // Verify that the function of the block device is normal. + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + +/// Validate express capability of the root port. +#[test] +fn test_pci_root_port_exp_cap() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + let nlw_range: Vec = [1, 2, 4, 8, 16, 32].to_vec(); + let cls_range: Vec = [1, 2, 3, 4, 5, 6, 7].to_vec(); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); + + let negotiated_link_width_mask = PCI_EXP_LNKSTA_NLW; + let negotiated_link_width = (root_port + .borrow() + .rp_dev + .config_readw(cap_exp_addr + PCI_EXP_LNKSTA) + & negotiated_link_width_mask) + >> 4; + assert!(nlw_range.binary_search(&negotiated_link_width).is_ok()); + + let current_link_speed_mask = PCI_EXP_LNKSTA_CLS; + let current_link_speed = root_port + .borrow() + .rp_dev + .config_readw(cap_exp_addr + PCI_EXP_LNKSTA) + & current_link_speed_mask; + assert!(cls_range.binary_search(¤t_link_speed).is_ok()); + + let dllla_mask = PCI_EXP_LNKSTA_DLLLA; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_LNKSTA, + 0, + dllla_mask, + ); + + let abp_mask = PCI_EXP_SLTSTA_ABP; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + 0, + abp_mask, + ); + + let pds_mask = PCI_EXP_SLTSTA_PDS; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + 0, + pds_mask, + ); + + let pdc_mask = PCI_EXP_SLTSTA_PDC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + 0, + pdc_mask, + ); + + let pcc_mask = PCI_EXP_SLTCTL_PCC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC, + pcc_mask, + ); + + let hotplug_blk_id = 0; + // Hotplug a block device whose id is 0 and bdf is 1:0:0. + hotplug_blk( + test_state.clone(), + root_port.clone(), + &mut image_paths, + hotplug_blk_id, + 1, + 0, + 0, + ); + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + let nlw_mask = PCI_EXP_LNKSTA_NLW; + let negotiated_link_width = (root_port.borrow().rp_dev.pci_bus.borrow().config_readw( + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_LNKSTA, + ) & nlw_mask) + >> 4; + assert!(nlw_range.binary_search(&negotiated_link_width).is_ok()); + + let cls_mask = PCI_EXP_LNKSTA_CLS; + let current_link_speed = root_port.borrow().rp_dev.pci_bus.borrow().config_readw( + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_LNKSTA, + ) & cls_mask; + assert!(cls_range.binary_search(¤t_link_speed).is_ok()); + + let dllla_mask = PCI_EXP_LNKSTA_DLLLA; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA, + dllla_mask, + ); + + let abp_mask = PCI_EXP_SLTSTA_ABP; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP, + abp_mask, + ); + + let pds_mask = PCI_EXP_SLTSTA_PDS; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS, + pds_mask, + ); + + let pdc_mask = PCI_EXP_SLTSTA_PDC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC, + pdc_mask, + ); + + let pcc_mask = PCI_EXP_SLTCTL_PCC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTCTL, + 0, + pcc_mask, + ); + + // Hotplug the block device whose id is 0 and bdf is 1:0:0. + hotunplug_blk( + test_state.clone(), + blk.clone(), + root_port.clone(), + hotplug_blk_id, + ); + + let dllla_mask = PCI_EXP_LNKSTA_DLLLA; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_LNKSTA, + 0, + dllla_mask, + ); + + let abp_mask = PCI_EXP_SLTSTA_ABP; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP, + abp_mask, + ); + + let pds_mask = PCI_EXP_SLTSTA_PDS; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + 0, + pds_mask, + ); + + let pdc_mask = PCI_EXP_SLTSTA_PDC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC, + pdc_mask, + ); + + let pcc_mask = PCI_EXP_SLTCTL_PCC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PCC, + pcc_mask, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} -- Gitee From 2f582c7ee3dfcc67602a72bb498d33f9e1668365 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Feb 2023 04:09:48 +0800 Subject: [PATCH 0847/2187] demo_dev: fix mem region bar write/read ops for base device got exchanged wrongly, fix that. Signed-off-by: Zhang Bo --- pci/src/demo_dev.rs | 22 +++++++++++++--------- pci/src/demo_device/base_device.rs | 5 +++-- pci/src/demo_device/gpu_device.rs | 2 +- pci/src/demo_device/kbd_pointer_device.rs | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index a0ad92d9b..000902ab9 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -57,7 +57,7 @@ pub struct DemoDev { name: String, cmd_cfg: DemoDevConfig, config: PciConfig, - sys_mem: Arc, + mem_region: Region, devfn: u8, parent_bus: Weak>, dev_id: Arc, @@ -68,22 +68,22 @@ impl DemoDev { pub fn new( cfg: DemoDevConfig, devfn: u8, - sys_mem: Arc, + _sys_mem: Arc, parent_bus: Weak>, ) -> Self { // You can choose different device function based on the parameter of device_type. let device: Arc> = match cfg.device_type.as_str() { #[cfg(not(target_env = "musl"))] - "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(sys_mem.clone()))), + "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem))), #[cfg(not(target_env = "musl"))] - "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(sys_mem.clone()))), + "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), _ => Arc::new(Mutex::new(BaseDevice::new())), }; DemoDev { name: cfg.id.clone(), cmd_cfg: cfg.clone(), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), - sys_mem, + mem_region: Region::init_container_region(u32::MAX as u64), devfn, parent_bus, dev_id: Arc::new(AtomicU16::new(0)), @@ -145,12 +145,13 @@ impl DemoDev { let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops); + self.mem_region.add_subregion(region, 0)?; self.config.register_bar( 0, - region, + self.mem_region.clone(), crate::config::RegionType::Mem64Bit, false, - self.cmd_cfg.bar_size, + (self.cmd_cfg.bar_size * self.cmd_cfg.bar_num as u64).next_power_of_two(), )?; Ok(()) @@ -208,13 +209,16 @@ impl PciDevOps for DemoDev { /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { + let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus_locked = parent_bus.lock().unwrap(); + self.config.write( offset, data, self.dev_id.load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] None, - Some(self.sys_mem.root()), + Some(&parent_bus_locked.mem_region), ); } @@ -234,7 +238,7 @@ impl PciDevOps for DemoDev { } pub trait DeviceTypeOperation: Send { - fn read(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; + fn read(&mut self, data: &mut [u8], addr: GuestAddress, offset: u64) -> Result<()>; fn write(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; fn realize(&mut self) -> Result<()>; fn unrealize(&mut self) -> Result<()>; diff --git a/pci/src/demo_device/base_device.rs b/pci/src/demo_device/base_device.rs index e845e018d..819d296bc 100644 --- a/pci/src/demo_device/base_device.rs +++ b/pci/src/demo_device/base_device.rs @@ -32,7 +32,7 @@ impl BaseDevice { impl DeviceTypeOperation for BaseDevice { // The base device can multiply the value with 2 when writing to mmio. - fn read(&mut self, data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { + fn write(&mut self, data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { let value = data[0].checked_mul(2).unwrap_or(0); self.result.insert(addr.raw_value(), value); Ok(()) @@ -40,7 +40,8 @@ impl DeviceTypeOperation for BaseDevice { // Rm the data after reading, as we assume that the data becomes useless after the test // process checked the addr. - fn write(&mut self, _data: &[u8], addr: GuestAddress, _offset: u64) -> Result<()> { + fn read(&mut self, data: &mut [u8], addr: GuestAddress, _offset: u64) -> Result<()> { + data[0] = *self.result.get(&addr.raw_value()).unwrap_or(&0); self.result.remove(&addr.raw_value()); Ok(()) } diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 83d7f4d63..4257b4392 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -170,7 +170,7 @@ impl DemoGpu { } impl DeviceTypeOperation for DemoGpu { - fn read(&mut self, _data: &[u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + fn read(&mut self, _data: &mut [u8], _addr: GuestAddress, _offset: u64) -> Result<()> { bail!("read is not support"); } diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/pci/src/demo_device/kbd_pointer_device.rs index 4f2f1e36e..0ef489e7c 100644 --- a/pci/src/demo_device/kbd_pointer_device.rs +++ b/pci/src/demo_device/kbd_pointer_device.rs @@ -142,7 +142,7 @@ pub struct PointerMessage { } impl DeviceTypeOperation for DemoKbdMouse { - fn read(&mut self, _data: &[u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + fn read(&mut self, _data: &mut [u8], _addr: GuestAddress, _offset: u64) -> Result<()> { Ok(()) } -- Gitee From d5374bcf16a54fcdbea4913f94e9d5099a08c26c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 28 Feb 2023 02:11:49 +0800 Subject: [PATCH 0848/2187] PCI: Add some testcase Signed-off-by: Jinhao Gao --- tests/mod_test/src/libdriver/pci.rs | 2 +- tests/mod_test/tests/pci_test.rs | 236 ++++++++++++++++++++++++++-- 2 files changed, 227 insertions(+), 11 deletions(-) diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 43b432119..88dcd8c9f 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -21,7 +21,7 @@ pub const PCI_DEVICE_ID: u8 = 0x02; pub const PCI_COMMAND: u8 = 0x04; const PCI_COMMAND_IO: u8 = 0x1; -const PCI_COMMAND_MEMORY: u8 = 0x2; +pub const PCI_COMMAND_MEMORY: u8 = 0x2; const PCI_COMMAND_MASTER: u8 = 0x4; pub const PCI_STATUS: u8 = 0x06; diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 756f65dc4..e02c7dc2e 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -34,6 +34,61 @@ const MAX_DEVICE_NUM_IN_MULTIFUNC: u8 = 248; const MAX_DEVICE_NUM: u8 = 32; const TIMEOUT_S: u64 = 5; +#[derive(Clone, Copy)] +struct DemoDev { + bar_num: u8, + bar_size: u64, + bus_num: u8, + dev_num: u8, +} + +fn fmt_demo_deves(cfg: DemoDev, num: u8) -> String { + // let mut dev_str = format!("-device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.{}", cfg.bus_num); + let mut dev_str: String = String::new(); + + for i in 1..num + 1 { + let tmp = format!( + "-device pcie-demo-dev,addr=0x{:x},bus=pcie.{},id=demo{},bar_num={},bar_size={}", + cfg.dev_num + i - 1, + cfg.bus_num, + i, + cfg.bar_num, + cfg.bar_size + ); + let sep = match i { + 1 => "", + _ => " ", + }; + dev_str = format!("{}{}{}", dev_str, sep, tmp); + } + + dev_str +} + +fn init_demo_dev(cfg: DemoDev, dev_num: u8) -> (Rc>, Rc>) { + let mut demo_dev_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = "-machine virt -D /tmp/oscar.log".split(' ').collect(); + demo_dev_args.append(&mut args); + + let demo_str = fmt_demo_deves(cfg.clone(), dev_num); + args = demo_str[..].split(' ').collect(); + demo_dev_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(demo_dev_args))); + let machine = Rc::new(RefCell::new(TestStdMachine::new(test_state.clone()))); + let _allocator = machine.borrow().allocator.clone(); + + let mut pci_dev = TestPciDev::new(machine.clone().borrow().pci_bus.clone()); + let devfn = cfg.dev_num << 3; + pci_dev.devfn = devfn; + + pci_dev.set_bus_num(cfg.bus_num); + pci_dev.enable(); + + (Rc::new(RefCell::new(pci_dev)), test_state) +} + #[derive(Default, Clone)] pub struct MsixVector { pub msix_entry: u16, @@ -67,6 +122,7 @@ impl RootPort { root_port.set_bus_num(bus_num); root_port.devfn = devfn; assert_eq!(root_port.config_readw(PCI_SUB_CLASS_DEVICE), 0x0604); + root_port.enable(); root_port.enable_msix(None); let root_port_msix = MsixVector::new(0, alloc.clone()); @@ -75,6 +131,7 @@ impl RootPort { root_port_msix.msix_addr, root_port_msix.msix_data, ); + Self { rp_dev: root_port, rp_misx_vector: root_port_msix, @@ -90,6 +147,7 @@ fn build_root_port_args(root_port_nums: u8) -> Vec { if root_port_nums > 32 { multifunc = true; } + let mut root_port_args: Vec = Vec::with_capacity(root_port_nums.try_into().unwrap()); let mut addr = 1; let mut func = 0; @@ -114,6 +172,7 @@ fn build_root_port_args(root_port_nums: u8) -> Vec { root_port_args.push(arg); } + root_port_args } @@ -191,12 +250,14 @@ fn build_hotplug_blk_cmd( \"read-only\": false}}}}", hotplug_blk_id, hotplug_image_path ); + let add_device_command = format!( "{{\"execute\":\"device_add\", \ \"arguments\": {{\"id\":\"blk-{}\", \"driver\":\"virtio-blk-pci\", \ \"drive\": \"drive-{}\", \"addr\":\"{:#x}.{:#x}\", \"bus\": \"pcie.{}\"}}}}", hotplug_blk_id, hotplug_blk_id, slot, func, bus_num ); + (add_blk_command, add_device_command) } @@ -206,6 +267,7 @@ fn build_hotunplug_blk_cmd(unplug_blk_id: u8) -> (String, String) { \"arguments\": {{\"id\":\"blk-{}\"}}}}", unplug_blk_id ); + let delete_blk_command = format!( "{{\"execute\": \"blockdev-del\",\ \"arguments\": {{\"node-name\":\"drive-{}\"}}}}", @@ -235,6 +297,7 @@ fn build_all_device_args( pci_device_param.get(i).unwrap().3, pci_device_param.get(i).unwrap().4, ); + if pci_device_param.get(i).unwrap().5 { let multi_func_arg = String::from(",multifunction=on"); device_arg_str.push_str(&multi_func_arg); @@ -301,6 +364,7 @@ fn create_machine( .borrow() .pci_auto_bus_scan(root_port_nums as u8); let allocator = machine.borrow().allocator.clone(); + (test_state, machine, allocator) } @@ -376,10 +440,12 @@ fn validate_config_perm_1byte( .borrow() .config_readb(pci_dev.bus_num, pci_dev.devfn, offset); assert_eq!(config_value & mask, expected_value); + pci_dev .pci_bus .borrow() .config_writeb(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + let config_value = pci_dev .pci_bus @@ -407,7 +473,6 @@ fn validate_config_perm_2byte( assert_eq!(config_value & mask, expected_value); } -#[allow(unused)] fn validate_config_perm_4byte( pci_dev: TestPciDev, offset: u8, @@ -421,10 +486,12 @@ fn validate_config_perm_4byte( .borrow() .config_readl(pci_dev.bus_num, pci_dev.devfn, offset); assert_eq!(config_value & mask, expected_value); + pci_dev .pci_bus .borrow() .config_writel(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + let config_value = pci_dev .pci_bus @@ -520,6 +587,7 @@ fn simple_blk_io_req( false, ); blk.borrow().virtqueue_notify(virtqueue.clone()); + free_head } @@ -710,6 +778,7 @@ fn hotplug_blk( build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), bus, slot, func); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); + let ret = test_state.borrow().qmp(&add_device_command); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -749,6 +818,7 @@ fn hotunplug_blk( assert_eq!(*ret.get("return").unwrap(), json!({})); test_state.borrow().wait_qmp_event(); + let ret = test_state.borrow().qmp(&delete_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -957,7 +1027,7 @@ fn test_pci_device_discovery_004() { tear_down(None, test_state, alloc, None, Some(image_paths)); } -// Check the permission and initial value of type0 pci device's configuration space. +/// Check the permission and initial value of type0 pci device's configuration space. #[test] fn test_pci_type0_config() { let blk_nums = 1; @@ -1101,7 +1171,7 @@ fn test_pci_type0_config() { tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); } -// Check the permission and initial value of type1 pci device's configuration space. +/// Check the permission and initial value of type1 pci device's configuration space. #[test] fn test_pci_type1_config() { let blk_nums = 0; @@ -1216,9 +1286,9 @@ fn test_pci_type0_msix_config() { tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); } -// Test whether the Function Mask bit in the control register for MSI-X works well, -// which means that when it's set, msix pends notification, and starts to notify as -// soon as the mask bit is cleared by the OS. +/// Test whether the Function Mask bit in the control register for MSI-X works well, +/// which means that when it's set, msix pends notification, and starts to notify as +/// soon as the mask bit is cleared by the OS. #[test] fn test_pci_msix_global_ctl() { let blk_nums = 1; @@ -1282,9 +1352,9 @@ fn test_pci_msix_global_ctl() { tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); } -// Test whether the Mask bit in the vector register in msix table works well, -// which means that when it's set, msix pends notification of the related vecotr, -// and starts to notify as soon as the mask bit is cleared by the OS. +/// Test whether the Mask bit in the vector register in msix table works well, +/// which means that when it's set, msix pends notification of the related vecotr, +/// and starts to notify as soon as the mask bit is cleared by the OS. #[test] fn test_pci_msix_local_ctl() { let blk_nums = 1; @@ -1504,7 +1574,7 @@ fn test_pci_hotplug_006() { build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, 2, 0, 0); let ret = test_state.borrow().qmp(&add_blk_command); - println!("ret is {}", ret); + assert_eq!(*ret.get("return").unwrap(), json!({})); let ret = test_state.borrow().qmp(&add_device_command); assert!(!(*ret.get("error").unwrap()).is_null()); @@ -2274,3 +2344,149 @@ fn test_pci_root_port_exp_cap() { tear_down(None, test_state, alloc, None, Some(image_paths)); } + +/// r/w demo dev's mmio +#[test] +fn test_pci_combine_000() { + let cfg = DemoDev { + bar_num: 3, + bar_size: 0x100_0000, //16MB + bus_num: 0, + dev_num: 5, + }; + + let (pci_dev, test_state) = init_demo_dev(cfg, 1); + + let bar_addr = pci_dev.borrow().io_map(0); + + let start = bar_addr; + + test_state.borrow().writeb(start, 5); + let out = test_state.borrow().readb(start); + + assert!(out == 10); // just multiply it with 2. + test_state.borrow().writeb(start + 2, 7); + let out = test_state.borrow().readb(start + 2); + assert!(out == 14); // just multiply it with 2. + + test_state.borrow_mut().stop(); +} + +/// change memory enabled during r/w demo dev's mmio +#[test] +fn test_pci_combine_001() { + let cfg = DemoDev { + bar_num: 3, + bar_size: 0x100_0000, //16MB + bus_num: 0, + dev_num: 5, + }; + + let (pci_dev, test_state) = init_demo_dev(cfg, 1); + let dev_locked = pci_dev.borrow(); + + let bar_addr = dev_locked.io_map(1); + + // set memory enabled = 0 + let mut val = dev_locked.config_readw(PCI_COMMAND); + val &= !(PCI_COMMAND_MEMORY as u16); + dev_locked.config_writew(PCI_COMMAND, val); + + // mmio r/w stops working. + test_state.borrow().writeb(bar_addr, 5); + let out = test_state.borrow().readb(bar_addr); + assert_ne!(out, 10); + + // set memory enabled = 1 + val |= PCI_COMMAND_MEMORY as u16; + dev_locked.config_writew(PCI_COMMAND, val); + + // mmio r/w gets back to work. + test_state.borrow().writeb(bar_addr, 5); + let out = test_state.borrow().readb(bar_addr); + assert_eq!(out, 0); + + drop(dev_locked); + + test_state.borrow_mut().stop(); +} + +/// r/w mmio during hotunplug +#[test] +fn test_pci_combine_002() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + let blk = Rc::new(RefCell::new(TestVirtioPciDev::new( + machine.borrow().pci_bus.clone(), + ))); + blk.borrow_mut().pci_dev.bus_num = 1; + blk.borrow_mut().init(0, 0); + let bar_addr = blk.borrow().bar; + + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(0); + let ret = test_state.borrow().qmp(&delete_device_command); + + // r/w mmio during hotunplug + test_state.borrow().writeb(bar_addr, 5); + assert!(test_state.borrow().readb(bar_addr) == 5); + + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + power_off_device(root_port.clone()); + + // r/w mmio during hotunplug + test_state.borrow().writeb(bar_addr, 5); + assert!(test_state.borrow().readb(bar_addr) != 5); + + assert_eq!(*ret.get("return").unwrap(), json!({})); + test_state.borrow().qmp_read(); + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + validate_config_value_2byte( + machine.borrow().pci_bus.clone(), + root_port_nums, + 0, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + // r/w mmio during hotunplug + test_state.borrow().writeb(bar_addr, 5); + assert!(test_state.borrow().readb(bar_addr) != 5); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// too large bar space +#[test] +fn test_pci_combine_003() { + let mut cfg = DemoDev { + bar_num: 3, + bar_size: 0x100_0000, //16MB + bus_num: 0, + dev_num: 5, + }; + + let (pci_dev, _) = init_demo_dev(cfg, 1); + let bar_addr = pci_dev.borrow().io_map(0); + // the mmio space is 78MB, bar1 got over bounded + assert!(bar_addr != INVALID_BAR_ADDR); + + cfg.bar_size = 0x1000_0000; //2GB + let (pci_dev, _) = init_demo_dev(cfg, 1); + let bar_addr = pci_dev.borrow().io_map(0); + + assert!(bar_addr == INVALID_BAR_ADDR); +} -- Gitee From 9d5beb49e6e5ca3d7b50f821ae7c63025032c51e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 28 Feb 2023 07:23:46 +0800 Subject: [PATCH 0849/2187] vnc_test: fix some failed testcase due to clock_step Some testcase should sleep to wait the stratovirt to write memory or other things before get the result. Otherwise, it will be failed. Signed-off-by: Yan Wang --- tests/mod_test/tests/vnc_test.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index f9fcc494b..09cf2b850 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -21,7 +21,7 @@ use mod_test::{ libtest::TestState, }; use serde_json::Value; -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, rc::Rc, thread::sleep, time::Duration}; use vmm_sys_util::epoll::EventSet; fn qmp_query_vnc(test_state: Rc>) -> Value { @@ -80,6 +80,8 @@ fn test_set_area_dirty() { demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); + sleep(Duration::from_millis(2000)); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL * 2); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res @@ -113,6 +115,8 @@ fn test_set_area_dirty() { .is_ok()); demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); + sleep(Duration::from_millis(2000)); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); @@ -339,7 +343,8 @@ fn test_set_pixel_format() { assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); + sleep(Duration::from_millis(500)); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL * 2); let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); assert!(res @@ -464,6 +469,8 @@ fn test_vnc_kbd_mouse() { assert_eq!(msg.keycode, keycode); assert_eq!(msg.down, 0); assert!(vnc_client.test_key_event(1, keysym as u32).is_ok()); + sleep(Duration::from_millis(50)); + test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); let msg = input.borrow_mut().read_input_event(); println!("key {:?}: {:?}", name, msg); assert_eq!(msg.keycode, keycode); -- Gitee From be3d0be24d8334b4a7b3300d0d3f37e9a177b591 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 28 Feb 2023 06:56:32 +0800 Subject: [PATCH 0850/2187] VNC: add two exception tests Add two exception tests: 1. Abnormal total number of encodings. 2. Unsupport event: Client Cut event. Signed-off-by: Xiao Ye --- tests/mod_test/src/libdriver/vnc.rs | 35 +++++++++++++++++++++-- tests/mod_test/tests/vnc_test.rs | 44 ++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index 78f68ce30..c22dcdfc0 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -521,6 +521,30 @@ impl TestEventOperation for TestSetPixelFormat { } } +pub struct TestClientCut { + pub event_type: RfbClientMessage, + pub pad0: u8, + pub pad1: u16, + pub length: u32, + pub text: String, +} + +impl TestEventOperation for TestClientCut { + fn to_be_bytes(&self) -> Vec { + let mut buf: Vec = Vec::new(); + buf.append( + &mut (RfbClientMessage::RfbClientCutText as u8) + .to_be_bytes() + .to_vec(), + ); + buf.append(&mut self.pad0.to_be_bytes().to_vec()); + buf.append(&mut self.pad1.to_be_bytes().to_vec()); + buf.append(&mut (self.text.len()).to_be_bytes().to_vec()); + buf.append(&mut self.text.as_bytes().to_vec()); + buf + } +} + /// Display mode information. #[derive(Default)] pub struct DisplayMode { @@ -840,7 +864,7 @@ impl VncClient { } test_event.num_encodings = match enc_num { Some(num) => num, - None => 17_u16, + None => EncodingType::ENCODINGTYPE.len() as u16, }; } self.write_msg(&mut test_event.to_be_bytes())?; @@ -870,6 +894,13 @@ impl VncClient { Ok(()) } + /// Send client cut event to VncServer. + pub fn test_send_client_cut(&mut self, client_cut: TestClientCut) -> Result<()> { + println!("Test send client cut evnet."); + self.write_msg(&mut client_cut.to_be_bytes())?; + Ok(()) + } + /// Receive the framebuferr data, and verify the format. /// /// # Arguments @@ -1295,7 +1326,7 @@ pub fn set_up( let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); args.append(&mut vm_args); // Log. - let vm_args = String::from("-D /var/log/vnc_test.log"); + let vm_args = String::from("-D /tmp/vnc_test.log"); let vm_args: Vec<&str> = vm_args[..].split(' ').collect(); let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); args.append(&mut vm_args); diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index bbdcd34df..fc50536d6 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -14,9 +14,9 @@ use anyhow::Result; use mod_test::{ libdriver::vnc::{ create_new_client, set_up, tear_down, DemoGpuConfig, EncodingType, InputConfig, - RfbPixelFormat, RfbServerMsg, TestAuthType, UpdateState, KEYEVENTLIST, PIXMAN_A1, - PIXMAN_A8B8G8R8, PIXMAN_R8G8B8, PIXMAN_X2R10G10B10, PIXMAN_YUY2, POINTEVENTLIST, - TEST_CLIENT_RAND_MSG, + RfbClientMessage, RfbPixelFormat, RfbServerMsg, TestAuthType, TestClientCut, UpdateState, + KEYEVENTLIST, PIXMAN_A1, PIXMAN_A8B8G8R8, PIXMAN_R8G8B8, PIXMAN_X2R10G10B10, PIXMAN_YUY2, + POINTEVENTLIST, TEST_CLIENT_RAND_MSG, }, libtest::TestState, }; @@ -684,6 +684,38 @@ fn test_set_pixel_format_abnormal(test_state: Rc>, port: u16) Ok(()) } +fn test_set_encoding_abnormal(test_state: Rc>, port: u16) -> Result<()> { + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + assert!(vnc_client.test_setup_encodings(Some(100), None).is_ok()); + // Send a qmp to query vnc client state. + let value = qmp_query_vnc(test_state.clone()); + let client_num = value["return"]["clients"].as_array().unwrap().len(); + assert_eq!(client_num, 1); + assert!(vnc_client.disconnect().is_ok()); + Ok(()) +} + +fn test_client_cut_event(test_state: Rc>, port: u16) -> Result<()> { + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + let text = "Stratovirt".to_string(); + let client_cut = TestClientCut { + event_type: RfbClientMessage::RfbClientCutText, + pad0: 0, + pad1: 0, + length: text.len() as u32, + text: text, + }; + assert!(vnc_client.test_send_client_cut(client_cut).is_ok()); + // Send a qmp to query vnc client state. + let value = qmp_query_vnc(test_state.clone()); + let client_num = value["return"]["clients"].as_array().unwrap().len(); + assert_eq!(client_num, 1); + assert!(vnc_client.disconnect().is_ok()); + Ok(()) +} + fn test_client_rand_bytes(test_state: Rc>, port: u16) -> Result<()> { let mut vnc_client = create_new_client(test_state, port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); @@ -709,6 +741,8 @@ fn test_client_rand_bytes(test_state: Rc>, port: u16) -> Resu /// 2. Unsupport security type -> expect 1 + 2. /// 3. The message of set pixel formal is abnormal -> expect 1 + 2. /// 4. Send the rand bytes from the client -> expect 2. +/// 5. Send set encoding event: encoding number abnormal -> expect 2. +/// 6. Unsupported event: Client cut event is not supported now -> expect 2. /// ExpectOutput: /// 1. VNC server close the connection. /// 2. The status of VNC server status is normal and can handle the next connect request. @@ -730,13 +764,15 @@ fn test_rfb_abnormal() { assert!(test_rfb_version_abnormal(test_state.clone(), port).is_ok()); assert!(test_unsupport_sec_type(test_state.clone(), port).is_ok()); assert!(test_set_pixel_format_abnormal(test_state.clone(), port).is_ok()); + assert!(test_set_encoding_abnormal(test_state.clone(), port).is_ok()); + assert!(test_client_cut_event(test_state.clone(), port).is_ok()); assert!(test_client_rand_bytes(test_state.clone(), port).is_ok()); let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); let value = qmp_query_vnc(test_state.clone()); let client_num = value["return"]["clients"].as_array().unwrap().len(); - assert!(client_num >= 1); + assert_eq!(client_num, 1); assert!(vnc_client.disconnect().is_ok()); tear_down(gpu_list, input, test_state); -- Gitee From 06df011d484e45e07532a5765235464ddc1d1c44 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 09:11:12 +0800 Subject: [PATCH 0851/2187] eventloop: Fix timer constructor The parameter nsec may exceeds 32bit. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 5734a99a0..bd3ea823c 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -23,6 +23,7 @@ use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; use crate::test_helper::{get_test_time, is_test_enabled}; +use crate::time::NANOSECONDS_PER_SECOND; use crate::UtilError; use anyhow::{anyhow, Context, Result}; use std::fmt; @@ -178,10 +179,11 @@ impl Timer { /// * `func` - the function will be called later. /// * `nsec` - delay time in nanosecond. pub fn new(func: Box, nsec: u64) -> Self { - Timer { - func, - expire_time: get_current_time() + Duration::new(0, nsec as u32), - } + let secs = nsec / NANOSECONDS_PER_SECOND; + let nsecs = (nsec % NANOSECONDS_PER_SECOND) as u32; + let expire_time = get_current_time() + Duration::new(secs, nsecs); + + Timer { func, expire_time } } } -- Gitee From aa345c7379aa73effe54cd0dee233f3209700a3c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 09:33:48 +0800 Subject: [PATCH 0852/2187] mst: Fix io throttling for test We should use get_test_time in leak_bucket so we can control throttling in mst testcase. Signed-off-by: Keqian Zhu --- tests/mod_test/tests/block_test.rs | 19 +++++++++++++++++++ tests/mod_test/tests/rng_test.rs | 6 +++--- util/src/leak_bucket.rs | 6 +++--- util/src/loop_context.rs | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index cc855a51a..ca125f8c8 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -31,6 +31,7 @@ use mod_test::utils::{create_img, TEST_IMAGE_SIZE}; use std::cell::RefCell; use std::rc::Rc; +use std::time::{Duration, Instant}; use util::aio::{aio_probe, AioEngine}; use util::num_ops::round_up; use util::offset_of; @@ -824,6 +825,24 @@ fn blk_iops() { let status = test_state.borrow().readb(status_addr); assert_ne!(status, VIRTIO_BLK_S_OK); + let time_out = Instant::now() + Duration::from_micros(TIMEOUT_US); + loop { + test_state.borrow().clock_step(); + + if blk.borrow().queue_was_notified(virtqueues[0].clone()) + && virtqueues[0].borrow_mut().get_buf(test_state.clone()) + { + if virtqueues[0].borrow().desc_len.contains_key(&free_head) { + break; + } + } + assert!(Instant::now() <= time_out); + } + + let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + tear_down( blk.clone(), test_state.clone(), diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs index b456695db..ee43001fe 100644 --- a/tests/mod_test/tests/rng_test.rs +++ b/tests/mod_test/tests/rng_test.rs @@ -23,6 +23,7 @@ use std::collections::HashSet; use std::os::unix::fs::FileTypeExt; use std::path::Path; use std::rc::Rc; +use std::time::{Duration, Instant}; const TIMEOUT_US: u64 = 10 * 1000 * 1000; const RANDOM_FILE: &str = "/dev/random"; @@ -276,15 +277,13 @@ fn rng_limited_rate() { let free_head = virtqueues[0] .borrow_mut() .add_chained(test_state.clone(), data_entries); - rng.borrow() .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - test_state.borrow().clock_step(); - assert!(!random_num_check( test_state.borrow().memread(req_addr, RNG_DATA_BYTES) )); + let time_out = Instant::now() + Duration::from_micros(TIMEOUT_US); loop { test_state.borrow().clock_step(); if rng.borrow().queue_was_notified(virtqueues[0].clone()) @@ -293,6 +292,7 @@ fn rng_limited_rate() { assert!(virtqueues[0].borrow().desc_len.contains_key(&free_head)); break; } + assert!(Instant::now() <= time_out); } assert!(random_num_check(test_state.borrow().memread( diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 2b3d9578a..e355cae90 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -18,7 +18,7 @@ use std::time::Instant; use log::error; use vmm_sys_util::eventfd::EventFd; -use crate::loop_context::EventLoopContext; +use crate::loop_context::{get_current_time, EventLoopContext}; use crate::time::NANOSECONDS_PER_SECOND; use anyhow::Result; @@ -50,7 +50,7 @@ impl LeakBucket { Ok(LeakBucket { capacity: units_ps * ACCURACY_SCALE, level: 0, - prev_time: Instant::now(), + prev_time: get_current_time(), timer_started: false, timer_wakeup: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), }) @@ -72,7 +72,7 @@ impl LeakBucket { } // update the water level - let now = Instant::now(); + let now = get_current_time(); let nanos = (now - self.prev_time).as_nanos(); if nanos > (self.level * NANOSECONDS_PER_SECOND / self.capacity) as u128 { self.level = 0; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index bd3ea823c..e8914593b 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -149,7 +149,7 @@ pub fn gen_delete_notifiers(fds: &[RawFd]) -> Vec { notifiers } -fn get_current_time() -> Instant { +pub fn get_current_time() -> Instant { if is_test_enabled() { get_test_time() } else { -- Gitee From 38218f14332cf5b324b4065eb2e16da5f9dead31 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 28 Feb 2023 09:06:54 +0800 Subject: [PATCH 0853/2187] VNC: Optimize VNC MST 1. Increase the randomness of update image operation by demo gpu. 2. After parsing the image data, the old data should be deleted. Signed-off-by: Xiao Ye --- pci/src/demo_device/gpu_device.rs | 15 +++++++++++---- tests/mod_test/src/libdriver/vnc.rs | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 4257b4392..864375481 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -37,7 +37,7 @@ use vnc::{ create_pixman_image, get_image_data, get_image_format, get_image_stride, ref_pixman_image, }, }; - +pub const UPDATE_FACTOR: [u8; 7] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40]; use crate::demo_dev::DeviceTypeOperation; #[derive(Debug)] @@ -142,9 +142,16 @@ impl DemoGpu { for i in y..y + h { let ptr = (image_ptr as usize + i as usize * stride as usize) as *mut u8; for j in x..x + w { - let tmp_ptr = (ptr as usize + 4 * j as usize) as *mut u8; - // ^_ , byte reverse. - unsafe { *tmp_ptr ^= 0xff }; + let tmp_ptr = ptr as usize + 4 * j as usize; + let rand_factor = (i * j) as usize; + let len = UPDATE_FACTOR.len(); + unsafe { + // byte reverse by ^. + *(tmp_ptr as *mut u8) ^= UPDATE_FACTOR[rand_factor % len]; + *((tmp_ptr + 1) as *mut u8) ^= UPDATE_FACTOR[(rand_factor + 1) % len]; + *((tmp_ptr + 2) as *mut u8) ^= UPDATE_FACTOR[(rand_factor + 2) % len]; + *((tmp_ptr + 3) as *mut u8) ^= UPDATE_FACTOR[(rand_factor + 3) % len]; + } } } self.graphic_update(x, y, w, h) diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index c22dcdfc0..f9777fa23 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -1043,6 +1043,7 @@ impl VncClient { let expect = w as usize * h as usize * bytes_per_pixel; self.read_msg(buf, expect)?; total_received += expect; + buf.drain(..expect); } else { // Background colour. if flag & BACKGROUND_SPECIFIC != 0 { -- Gitee From 72ffc001db17382dd2b4e073e126a8a1bf4e2bf2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 28 Feb 2023 10:40:22 +0800 Subject: [PATCH 0854/2187] VNC: Add an VNC MST Add a scene where multiple areas in the image are modified at one time. Signed-off-by: Xiao Ye --- tests/mod_test/tests/vnc_test.rs | 81 ++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index fc50536d6..a5b7546fc 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -125,6 +125,87 @@ fn test_set_area_dirty() { tear_down(gpu_list, input, test_state); } +/// Brief: +/// 1. When received a framebuffer request from the client, the vnc server can +/// send the pixel which is changed to the client. +/// Preparation: +/// 1. Configure a demo pointer device and test GPU device. +/// 2. Start a VNC Server and listens on local ports. +/// 3. The demo gpu device create an image with size of 640 * 480 and sends it to VNC. +/// TestStep: +/// 1. VNC client connect to server. +/// 2. VNC client set pixel format (Raw of Hextile). +/// 3. VNC client send framebuffer request + NotIncremental +/// + The Demo GPU device changes the pixel and update image -> expect 1. +/// ExpectOutput +/// 1. VNC client can receive image updates event, and the image format meets expectations. +#[test] +fn test_set_multiple_area_dirty() { + let port: u16 = 8; + let mut gpu_list: Vec = vec![]; + let gpu_conf = DemoGpuConfig { + pci_slot: 3, + id: "demo-pci-gpu".to_string(), + }; + gpu_list.push(gpu_conf); + let input_conf = InputConfig { + pci_slot: 4, + id: "demo-pci-input".to_string(), + }; + let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); + let demo_gpu = gpu_list[0].clone(); + let mut vnc_client = create_new_client(test_state.clone(), port).unwrap(); + assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); + // Encoding -> Raw. + // Multiple areas of image have been updated. + // Demo update image -> VNC client send update request. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingRaw)) + .is_ok()); + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); + demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); + demo_gpu.borrow_mut().update_image_area(72, 72, 109, 109); + demo_gpu.borrow_mut().update_image_area(119, 120, 160, 160); + demo_gpu.borrow_mut().set_area_dirty(0, 0, 640, 480); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 640 as u16, 480 as u16,) + .is_ok()); + + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res + .unwrap() + .contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); + + // Encoding -> Hextile. + // Multiple areas of image have been updated. + // Demo update image -> VNC client send update request. + assert!(vnc_client + .test_setup_encodings(None, Some(EncodingType::EncodingHextile)) + .is_ok()); + let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); + demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); + demo_gpu.borrow_mut().update_image_area(72, 72, 109, 109); + demo_gpu.borrow_mut().update_image_area(119, 120, 160, 160); + demo_gpu.borrow_mut().set_area_dirty(0, 0, 640, 480); + assert!(vnc_client + .test_update_request(UpdateState::NotIncremental, 0, 0, 640 as u16, 480 as u16,) + .is_ok()); + + let res = vnc_client.test_recv_server_data(pf); + assert!(res.is_ok()); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingHextile + ))); + + assert!(vnc_client.disconnect().is_ok()); + + tear_down(gpu_list, input, test_state); +} + /// Brief: /// 1. VNC Server can update cursor image. /// Preparation: -- Gitee From a2011da07f0db5ef5bcfef10a37c04071e928758 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 28 Feb 2023 18:40:25 +0800 Subject: [PATCH 0855/2187] aio: Add flush_request support The last_aio mechanism is dangerous as the device has little knowledge of aio module thus it's easy to set last_aio wrong. Now aio module has a flush_request interface, device can flush queued requests in one go. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 12 +++++++++--- virtio/src/block.rs | 17 ++++------------- virtio/src/scsi/controller.rs | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index d69ccef3d..f14f077b1 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -94,7 +94,6 @@ pub enum OpCode { } pub struct AioCb { - pub last_aio: bool, pub direct: bool, pub req_align: u32, pub buf_align: u32, @@ -206,6 +205,14 @@ impl Aio { } } + pub fn flush_request(&mut self) -> Result<()> { + if self.ctx.is_some() { + self.process_list() + } else { + Ok(()) + } + } + pub fn handle_complete(&mut self) -> Result { let mut done = false; if self.ctx.is_none() { @@ -289,12 +296,11 @@ impl Aio { } fn rw_async(&mut self, cb: AioCb) -> Result<()> { - let last_aio = cb.last_aio; let mut node = Box::new(Node::new(cb)); node.value.user_data = (&mut (*node) as *mut CbNode) as u64; self.aio_in_queue.add_head(node); - if last_aio || self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { + if self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { self.process_list()?; } diff --git a/virtio/src/block.rs b/virtio/src/block.rs index d18cf063a..9af7e97f3 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -411,11 +411,7 @@ struct BlockIoHandler { } impl BlockIoHandler { - fn merge_req_queue( - &self, - mut req_queue: Vec, - last_aio_index: &mut usize, - ) -> Vec { + fn merge_req_queue(&self, mut req_queue: Vec) -> Vec { req_queue.sort_by(|a, b| a.out_header.sector.cmp(&b.out_header.sector)); let mut merge_req_queue = Vec::::new(); @@ -424,7 +420,6 @@ impl BlockIoHandler { let mut merged_iovs = 0; let mut merged_bytes = 0; - *last_aio_index = 0; for req in req_queue { let req_iovs = req.iovec.len(); let req_bytes = req.data_len; @@ -451,9 +446,6 @@ impl BlockIoHandler { merged_iovs += req_iovs; merged_bytes += req_bytes; } else { - if io || req.out_header.request_type == VIRTIO_BLK_T_FLUSH { - *last_aio_index = merge_req_queue.len(); - } merge_req_queue.push(req); last_req = merge_req_queue.last_mut(); merged_reqs = 1; @@ -467,7 +459,6 @@ impl BlockIoHandler { fn process_queue_internal(&mut self) -> Result { let mut req_queue = Vec::new(); - let mut last_aio_req_index = 0; let mut done = false; loop { @@ -522,8 +513,8 @@ impl BlockIoHandler { return Ok(done); } - let merge_req_queue = self.merge_req_queue(req_queue, &mut last_aio_req_index); - for (req_index, req) in merge_req_queue.into_iter().enumerate() { + let merge_req_queue = self.merge_req_queue(req_queue); + for req in merge_req_queue.into_iter() { let req_rc = Rc::new(req); let aiocompletecb = AioCompleteCb::new( self.queue.clone(), @@ -534,7 +525,6 @@ impl BlockIoHandler { ); if let Some(disk_img) = self.disk_image.as_ref() { let aiocb = AioCb { - last_aio: req_index == last_aio_req_index, direct: self.direct, req_align: self.req_align, buf_align: self.buf_align, @@ -552,6 +542,7 @@ impl BlockIoHandler { aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR)?; } } + self.aio.flush_request()?; Ok(done) } diff --git a/virtio/src/scsi/controller.rs b/virtio/src/scsi/controller.rs index a8790210c..b51c430cf 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/scsi/controller.rs @@ -942,7 +942,6 @@ impl ScsiCmdHandler { ); if let Some(ref mut aio) = self.aio { let aiocb = AioCb { - last_aio: true, direct, req_align, buf_align, @@ -955,6 +954,7 @@ impl ScsiCmdHandler { iocompletecb: scsicompletecb, }; scsi_req.execute(aio, aiocb)?; + aio.flush_request()?; } } } -- Gitee From c9b93d812292246e59107b10c27ff327dd618888 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 28 Feb 2023 22:33:46 +0800 Subject: [PATCH 0856/2187] MST: Add some PCI MST testcase Signed-off-by: Jinhao Gao --- tests/mod_test/src/libdriver/pci.rs | 4 +- tests/mod_test/src/libdriver/pci_bus.rs | 2 +- tests/mod_test/tests/pci_test.rs | 108 ++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 88dcd8c9f..01b198e9d 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -93,9 +93,9 @@ pub struct TestPciDev { pub devfn: u8, pub msix_enabled: bool, pub msix_table_bar: PCIBarAddr, - msix_pba_bar: PCIBarAddr, + pub msix_pba_bar: PCIBarAddr, pub msix_table_off: u64, - msix_pba_off: u64, + pub msix_pba_off: u64, pub msix_used_vectors: u32, } diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index eabf4f6be..50194ca81 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -40,7 +40,7 @@ pub trait PciBusOps { pub struct TestPciBus { pub mmio_alloc_ptr: u64, pub mmio_limit: u64, - ecam_alloc_ptr: u64, + pub ecam_alloc_ptr: u64, not_hotpluggable: bool, pub test_state: Rc>, } diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index e02c7dc2e..dc7e07436 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -33,6 +33,7 @@ const BLK_DEVICE_ID: u16 = 0x1042; const MAX_DEVICE_NUM_IN_MULTIFUNC: u8 = 248; const MAX_DEVICE_NUM: u8 = 32; const TIMEOUT_S: u64 = 5; +const MAX_TABLE_SIZE_MAX: u32 = 0x1000; #[derive(Clone, Copy)] struct DemoDev { @@ -1188,6 +1189,81 @@ fn test_pci_type1_config() { tear_down(None, test_state, alloc, None, Some(image_paths)); } +/// Verify that out-of-bounds access to the configuration space +#[test] +fn test_out_boundry_config_access() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + let devfn = 1 << 3 | 1; + let addr = machine.borrow().pci_bus.borrow().ecam_alloc_ptr + + ((0 as u32) << 20 | (devfn as u32) << 12 | 0 as u32) as u64 + - 4; + + let write_value = u8::max_value(); + let buf = write_value.to_le_bytes(); + test_state.borrow().memwrite(addr, &buf); + + let mut buf: &[u8] = &test_state.borrow().memread(addr, 1)[0]; + let read_value = read_le_u8(&mut buf); + assert_ne!(write_value, read_value); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Verify that out-of-size access to the configuration space +#[test] +fn test_out_size_config_access() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + + let vendor_device_id = root_port.rp_dev.config_readl(PCI_VENDOR_ID); + let command_status = root_port.rp_dev.config_readl(PCI_COMMAND); + let value = root_port.rp_dev.config_readq(0); + assert_ne!(value, vendor_device_id << 32 | command_status); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + +/// Verify that out-of-bounds access to the msix bar space. +#[test] +fn test_out_boundry_msix_access() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + + let write_value = u32::max_value(); + root_port.rp_dev.io_writel( + root_port.rp_dev.msix_table_bar, + PCI_MSIX_ENTRY_VECTOR_CTRL - 2, + write_value, + ); + let read_value = root_port.rp_dev.io_readl( + root_port.rp_dev.msix_table_bar, + PCI_MSIX_ENTRY_VECTOR_CTRL - 2, + ); + assert_ne!(write_value, read_value); + + let write_value = u64::max_value(); + root_port + .rp_dev + .io_writeq(root_port.rp_dev.msix_pba_bar, 4, write_value); + let read_value = root_port + .rp_dev + .io_readq(root_port.rp_dev.msix_table_bar, 4); + assert_ne!(write_value, read_value); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + #[test] fn test_repeat_io_map_bar() { let blk_nums = 1; @@ -1395,6 +1471,38 @@ fn test_pci_msix_local_ctl() { tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); } +#[test] +fn test_alloc_abnormal_vector() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let queue_num = blk.borrow().get_queue_nums(); + + blk.borrow() + .setup_virtqueue_intr((queue_num + 2) as u16, alloc.clone(), virtqueue.clone()); + + let free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + alloc.clone(), + ); + // Verify that the os can not receive msix interrupt when the vectors of virtqueue is . + assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + /// Basic hotplug testcase. #[test] fn test_pci_hotplug_001() { -- Gitee From fb525da2d56a893d81a8e4d8993a0befff11ef5c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 1 Mar 2023 00:40:53 +0800 Subject: [PATCH 0857/2187] virtio-pci: Fix a bug of the msix vector. The max vector of virtqueue is the length of msix table divided by the size of msix table entry. Signed-off-by: Jinhao Gao --- virtio/src/virtio_pci.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs index be4cf7a8c..37b7e9f99 100644 --- a/virtio/src/virtio_pci.rs +++ b/virtio/src/virtio_pci.rs @@ -26,7 +26,7 @@ use pci::config::{ REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::msix::{update_dev_id, MsixState}; +use pci::msix::{update_dev_id, MsixState, MSIX_TABLE_ENTRY_SIZE}; use pci::Result as PciResult; use pci::{ config::PciConfig, init_msix, init_multifunction, le_write_u16, le_write_u32, ranges_overlap, @@ -235,7 +235,8 @@ impl VirtioPciCommonConfig { if msix.is_none() { return INVALID_VECTOR_NUM as u32; } - let max_vector = msix.as_ref().unwrap().lock().unwrap().table.len(); + let max_vector = + msix.as_ref().unwrap().lock().unwrap().table.len() / MSIX_TABLE_ENTRY_SIZE as usize; if vector_nr >= max_vector as u32 { INVALID_VECTOR_NUM as u32 } else { -- Gitee From ca6b9ab6194d647295c863b6dbf66a8ff0c03b67 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 1 Mar 2023 00:39:04 +0800 Subject: [PATCH 0858/2187] MST: Add some pci testcase Signed-off-by: Jinhao Gao --- tests/mod_test/src/libdriver/pci.rs | 2 +- tests/mod_test/src/libdriver/pci_bus.rs | 2 +- tests/mod_test/tests/pci_test.rs | 94 ++++++++++++++++++------- 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 01b198e9d..120dbd46c 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -38,6 +38,7 @@ pub const PCI_SUBSYSTEM_ID: u8 = 0x2e; pub const PCI_CAPABILITY_LIST: u8 = 0x34; pub const PCI_BRIDGE_CONTROL: u8 = 0x3e; +pub const BRIDGE_CTL_SEC_BUS_RESET: u8 = 0x40; pub const PCI_CAP_LIST_NEXT: u8 = 1; pub const PCI_CAP_ID_VNDR: u8 = 0x09; @@ -339,7 +340,6 @@ impl TestPciDev { impl PciMsixOps for TestPciDev { fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32) { assert!(self.msix_enabled); - assert!(msix_entry <= self.get_msix_table_size()); let offset = self.msix_table_off + (msix_entry * 16) as u64; let msix_table_bar = self.msix_table_bar; diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 50194ca81..fbff7b464 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -125,7 +125,7 @@ impl PciBusOps for TestPciBus { fn config_readq(&self, bus_num: u8, devfn: u8, offset: u8) -> u64 { let addr = self.get_addr(bus_num, devfn, offset); - let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 4)[0..8]; + let mut buf: &[u8] = &self.test_state.borrow().memread(addr, 8)[0..8]; read_le_u64(&mut buf) } diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index dc7e07436..501b2347e 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -21,19 +21,18 @@ use mod_test::libdriver::virtio_block::{ }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::{test_init, TestState}; -use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; +use mod_test::utils::{cleanup_img, create_img, read_le_u16, TEST_IMAGE_SIZE}; use serde_json::json; use std::cell::RefCell; use std::rc::Rc; -use std::time; +use std::{thread, time}; const VIRTIO_PCI_VENDOR: u16 = 0x1af4; const BLK_DEVICE_ID: u16 = 0x1042; const MAX_DEVICE_NUM_IN_MULTIFUNC: u8 = 248; const MAX_DEVICE_NUM: u8 = 32; const TIMEOUT_S: u64 = 5; -const MAX_TABLE_SIZE_MAX: u32 = 0x1000; #[derive(Clone, Copy)] struct DemoDev { @@ -1189,6 +1188,39 @@ fn test_pci_type1_config() { tear_down(None, test_state, alloc, None, Some(image_paths)); } +#[test] +fn test_pci_type1_reset() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + + let command = root_port.rp_dev.config_readw(PCI_COMMAND); + let cmd_memory = command & PCI_COMMAND_MEMORY as u16; + + // Bitwise inversion of memory space enable. + let write_cmd = command | (command & (!PCI_COMMAND_MEMORY as u16)) | !cmd_memory; + root_port.rp_dev.config_writew(PCI_COMMAND, write_cmd); + let old_command = root_port.rp_dev.config_readw(PCI_COMMAND); + assert_ne!(old_command, write_cmd); + + root_port + .rp_dev + .config_writeb(PCI_BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET); + + // Sleep three seconds to wait root port reset second bus. + let sleep_s = time::Duration::from_secs(3); + thread::sleep(sleep_s); + + let new_command = root_port.rp_dev.config_readw(PCI_COMMAND); + // verify that the block device is reset. + assert_ne!(old_command, new_command); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + /// Verify that out-of-bounds access to the configuration space #[test] fn test_out_boundry_config_access() { @@ -1199,14 +1231,14 @@ fn test_out_boundry_config_access() { let devfn = 1 << 3 | 1; let addr = machine.borrow().pci_bus.borrow().ecam_alloc_ptr + ((0 as u32) << 20 | (devfn as u32) << 12 | 0 as u32) as u64 - - 4; + - 1; - let write_value = u8::max_value(); + let write_value = u16::max_value(); let buf = write_value.to_le_bytes(); test_state.borrow().memwrite(addr, &buf); - let mut buf: &[u8] = &test_state.borrow().memread(addr, 1)[0]; - let read_value = read_le_u8(&mut buf); + let mut buf: &[u8] = &test_state.borrow().memread(addr, 2)[0..2]; + let read_value = read_le_u16(&mut buf); assert_ne!(write_value, read_value); tear_down(None, test_state, alloc, None, Some(image_paths)); @@ -1225,7 +1257,10 @@ fn test_out_size_config_access() { let vendor_device_id = root_port.rp_dev.config_readl(PCI_VENDOR_ID); let command_status = root_port.rp_dev.config_readl(PCI_COMMAND); let value = root_port.rp_dev.config_readq(0); - assert_ne!(value, vendor_device_id << 32 | command_status); + assert_ne!( + value, + (vendor_device_id as u64) << 32 | command_status as u64 + ); tear_down(None, test_state, alloc, None, Some(image_paths)); } @@ -1240,26 +1275,23 @@ fn test_out_boundry_msix_access() { // Create a root port whose bdf is 0:1:0. let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + // Out-of-bounds access to the msix table. let write_value = u32::max_value(); root_port.rp_dev.io_writel( root_port.rp_dev.msix_table_bar, - PCI_MSIX_ENTRY_VECTOR_CTRL - 2, + PCI_MSIX_ENTRY_VECTOR_CTRL + 2, write_value, ); let read_value = root_port.rp_dev.io_readl( root_port.rp_dev.msix_table_bar, - PCI_MSIX_ENTRY_VECTOR_CTRL - 2, + PCI_MSIX_ENTRY_VECTOR_CTRL + 2, ); assert_ne!(write_value, read_value); - let write_value = u64::max_value(); - root_port - .rp_dev - .io_writeq(root_port.rp_dev.msix_pba_bar, 4, write_value); - let read_value = root_port + // Out-of-bounds access to the msix pba. + let _read_value = root_port .rp_dev .io_readq(root_port.rp_dev.msix_table_bar, 4); - assert_ne!(write_value, read_value); tear_down(None, test_state, alloc, None, Some(image_paths)); } @@ -1479,28 +1511,38 @@ fn test_alloc_abnormal_vector() { // Create a block device whose bdf is 1:0:0. let blk = create_blk(machine.clone(), 1, 0, 0); - let vqs = blk.borrow_mut().init_device( - test_state.clone(), - alloc.clone(), - 1 << VIRTIO_F_VERSION_1, - 1, - ); + + // 1. Init device. + blk.borrow_mut().reset(); + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_driver(); + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + blk.borrow_mut().set_features_ok(); + blk.borrow_mut().pci_dev.enable_msix(None); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); let queue_num = blk.borrow().get_queue_nums(); + let virtqueue = blk + .borrow() + .setup_virtqueue(test_state.clone(), alloc.clone(), 0 as u16); blk.borrow() .setup_virtqueue_intr((queue_num + 2) as u16, alloc.clone(), virtqueue.clone()); + blk.borrow().set_driver_ok(); - let free_head = simple_blk_io_req( + let _free_head = simple_blk_io_req( blk.clone(), test_state.clone(), - vqs[0].clone(), + virtqueue.clone(), alloc.clone(), ); // Verify that the os can not receive msix interrupt when the vectors of virtqueue is . - assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + assert!(wait_msix_timeout(blk.clone(), virtqueue.clone(), TIMEOUT_S)); - tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); + blk.borrow_mut() + .cleanup_virtqueue(alloc.clone(), virtqueue.borrow().desc); + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); } /// Basic hotplug testcase. -- Gitee From 25180aee27309724efa571085b402ca29aed8267 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 1 Mar 2023 09:17:04 +0800 Subject: [PATCH 0859/2187] util/byte_code: Fix UT failure under high version rustc The memory layout of high version rustc is different, use repr C to fix the layout. Signed-off-by: Keqian Zhu --- util/src/byte_code.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index cd4604591..0bdfe0867 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -75,6 +75,7 @@ impl ByteCode for i128 {} mod test { use super::*; + #[repr(C)] #[allow(dead_code)] #[derive(Copy, Clone, Default)] struct TestData { @@ -105,9 +106,9 @@ mod test { }; let mut target = Vec::new(); + target.extend_from_slice(b"bytecode"); target.extend_from_slice(&[0x79, 0x56, 0x34, 0x12]); target.extend_from_slice(&[0_u8; 4]); - target.extend_from_slice(b"bytecode"); assert_eq!(data.as_bytes().to_vec(), target); // Convert failed because byte stream's length is not equal to size of struct. -- Gitee From c0204770d2a7f616cc96e727b4270ab10d1137ae Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 1 Mar 2023 16:31:22 +0800 Subject: [PATCH 0860/2187] blk: move the Default of block to mod tests The Default implementation of block is only used in the mod tests, move it to the mod tests. Signed-off-by: yezengruan --- virtio/src/block.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 7a073dfb0..d18cf063a 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -954,25 +954,6 @@ pub struct Block { drive_files: Arc>>, } -impl Default for Block { - fn default() -> Self { - Block { - blk_cfg: Default::default(), - disk_image: None, - req_align: 1, - buf_align: 1, - disk_sectors: 0, - state: BlockState::default(), - interrupt_cb: None, - senders: Vec::new(), - update_evts: Vec::new(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), - drive_files: Arc::new(Mutex::new(HashMap::new())), - } - } -} - impl Block { pub fn new( blk_cfg: BlkDevConfig, @@ -1276,6 +1257,25 @@ mod tests { const VIRTQ_DESC_F_WRITE: u16 = 0x02; const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; + impl Default for Block { + fn default() -> Self { + Block { + blk_cfg: Default::default(), + disk_image: None, + req_align: 1, + buf_align: 1, + disk_sectors: 0, + state: BlockState::default(), + interrupt_cb: None, + senders: Vec::new(), + update_evts: Vec::new(), + deactivate_evts: Vec::new(), + broken: Arc::new(AtomicBool::new(false)), + drive_files: Arc::new(Mutex::new(HashMap::new())), + } + } + } + // build dummy address space of vm fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36); -- Gitee From cf4d03d6e809f185ba9dc4899c26a579c801155b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 1 Mar 2023 16:56:57 +0800 Subject: [PATCH 0861/2187] mst: add rng read/write config testcase Signed-off-by: yezengruan --- tests/mod_test/tests/rng_test.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs index 8d0eea3f6..b456695db 100644 --- a/tests/mod_test/tests/rng_test.rs +++ b/tests/mod_test/tests/rng_test.rs @@ -346,3 +346,35 @@ fn rng_read_with_max() { tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); } + +/// Rng device read/write config space. +/// TestStep: +/// 1. Init device. +/// 2. Read/write rng device config space. +/// 3. Destroy device. +/// Expect: +/// 1/3: success, 2: failed. +#[test] +fn rng_rw_config() { + let max_bytes = 1024; + let period = 1000; + + let random_file = get_random_file(); + let (rng, test_state, alloc) = create_rng(random_file, max_bytes, period); + + let virtqueues = rng.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + let config = rng.borrow().config_readq(0); + assert_eq!(config, 0); + + rng.borrow().config_writeq(0, 0xff); + let config = rng.borrow().config_readq(0); + assert_ne!(config, 0xff); + + tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); +} -- Gitee From c214a1f141538a5ee48c9ee6c3608ec7c2b30f72 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 1 Mar 2023 18:14:19 +0800 Subject: [PATCH 0862/2187] virtio-test: add some check point for testcase Add a testcase to check if the driver writed queue info is right, which has be accepted by the driver. Signed-off-by: Yan Wang --- tests/mod_test/src/libdriver/virtio.rs | 1 + .../src/libdriver/virtio_pci_modern.rs | 19 ++++++--- tests/mod_test/tests/virtio_test.rs | 40 ++++++++++++++++++- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index e39fdbed6..b9e4d4423 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -57,6 +57,7 @@ pub trait VirtioDeviceOps { fn get_status(&self) -> u8; fn set_status(&self, status: u8); fn get_queue_nums(&self) -> u16; + fn get_generation(&self) -> u8; fn queue_select(&self, index: u16); fn get_queue_select(&self) -> u16; fn set_queue_size(&self, size: u16); diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index feb4ca0f0..8b4a15eda 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -66,12 +66,12 @@ pub struct VirtioPciCommonCfg { queue_msix_vector: u16, pub queue_enable: u16, pub queue_notify_off: u16, - queue_desc_lo: u32, - queue_desc_hi: u32, - queue_avail_lo: u32, - queue_avail_hi: u32, - queue_used_lo: u32, - queue_used_hi: u32, + pub queue_desc_lo: u32, + pub queue_desc_hi: u32, + pub queue_avail_lo: u32, + pub queue_avail_hi: u32, + pub queue_used_lo: u32, + pub queue_used_hi: u32, } pub trait VirtioPCIMSIXOps { @@ -426,6 +426,13 @@ impl VirtioDeviceOps for TestVirtioPciDev { ) } + fn get_generation(&self) -> u8 { + self.pci_dev.io_readb( + self.bar, + self.common_base as u64 + offset_of!(VirtioPciCommonCfg, config_generation) as u64, + ) + } + fn get_queue_nums(&self) -> u16 { self.pci_dev.io_readw( self.bar, diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 4388d23e1..535070af7 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -177,6 +177,26 @@ fn check_req_result( assert_eq!(status, VIRTIO_BLK_S_OK); } +fn check_queue(blk: Rc>, desc: u64, avail: u64, used: u64) { + let bar = blk.borrow().bar; + let common_base = blk.borrow().common_base as u64; + let reqs = [ + (offset_of!(VirtioPciCommonCfg, queue_desc_lo), desc), + (offset_of!(VirtioPciCommonCfg, queue_desc_hi), desc >> 32), + (offset_of!(VirtioPciCommonCfg, queue_avail_lo), avail), + (offset_of!(VirtioPciCommonCfg, queue_avail_hi), avail >> 32), + (offset_of!(VirtioPciCommonCfg, queue_used_lo), used), + (offset_of!(VirtioPciCommonCfg, queue_used_hi), used >> 32), + ]; + for (offset, value) in reqs { + let addr = blk + .borrow() + .pci_dev + .io_readl(bar, common_base as u64 + offset as u64); + assert_eq!(addr, value as u32); + } +} + fn do_event_idx_with_flag(flag: u16) { let (blk, test_state, alloc, image_path) = set_up(); @@ -843,6 +863,7 @@ fn virtio_init_device_abnormal_features() { /// 5) set invalid desc/avail/used address: /// 0, 1 << 48, u64::MAX /// 6) set 0 to enable vq; +/// 7) check if the writed queue info is right. /// 2. Do the I/O request. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. @@ -873,6 +894,7 @@ fn virtio_init_device_abnormal_vring_info() { (8, 1 << 48, 0xff, 0), (8, u64::MAX, 0xff, 0), (9, 0, 0xff, 0), + (10, 1, 0, 0), ]; for (err_type, value, ack, device_status) in reqs { @@ -880,6 +902,7 @@ fn virtio_init_device_abnormal_vring_info() { // 1. Init device. blk.borrow_mut().reset(); + assert_eq!(blk.borrow().get_generation(), 0); blk.borrow_mut().set_acknowledge(); blk.borrow_mut().set_driver(); blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); @@ -893,6 +916,8 @@ fn virtio_init_device_abnormal_vring_info() { let features = blk.borrow().get_guest_features(); // Set invalid value to select queue. if err_type == 0 { + let q_select = blk.borrow().get_queue_select(); + assert_ne!(q_select, value as u16); blk.borrow().queue_select(value as u16); } @@ -983,6 +1008,10 @@ fn virtio_init_device_abnormal_vring_info() { used = value; } blk.borrow().activate_queue(desc, avail, used); + // TEST if the writed queue info is right. + if err_type == 10 { + check_queue(blk.clone(), desc, avail, used); + } let notify_off = blk.borrow().pci_dev.io_readw( blk.borrow().bar, @@ -992,12 +1021,12 @@ fn virtio_init_device_abnormal_vring_info() { vq.borrow_mut().queue_notify_off = blk.borrow().notify_base as u64 + notify_off as u64 * blk.borrow().notify_off_multiplier as u64; + let offset = offset_of!(VirtioPciCommonCfg, queue_enable) as u64; // TEST enable vq with 0 if err_type == 9 { blk.borrow().pci_dev.io_writew( blk.borrow().bar, - blk.borrow().common_base as u64 - + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, + blk.borrow().common_base as u64 + offset, 0, ); } else { @@ -1007,6 +1036,13 @@ fn virtio_init_device_abnormal_vring_info() { + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, 1, ); + if err_type == 10 { + let status = blk + .borrow() + .pci_dev + .io_readw(blk.borrow().bar, blk.borrow().common_base as u64 + offset); + assert_eq!(status, 1); + } } blk.borrow() -- Gitee From efb7977f636fb0b734cd2832e214ead70832faad Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 1 Mar 2023 21:39:52 +0800 Subject: [PATCH 0863/2187] cargo/workspace: Build ozone and vhost_user_fs by bins The root package is stratovirt, ozone and vhost_user_fs are packages too. Don't build them in root package, they can be built through --bins option. Signed-off-by: Keqian Zhu --- Cargo.lock | 35 ++++++++++++----------------------- Cargo.toml | 42 +----------------------------------------- Makefile | 6 +++++- 3 files changed, 18 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 77a3f9ae1..888a511c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,29 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "StratoVirt" -version = "2.2.0" -dependencies = [ - "anyhow", - "capng", - "error-chain", - "hypervisor", - "kvm-ioctls", - "libc", - "log", - "machine", - "machine_manager", - "migration", - "thiserror", - "util", - "vfio", - "vhost_user_fs", - "virtio", - "vmm-sys-util", - "vnc", -] - [[package]] name = "acpi" version = "2.2.0" @@ -886,6 +863,18 @@ dependencies = [ "syn", ] +[[package]] +name = "stratovirt" +version = "2.2.0" +dependencies = [ + "anyhow", + "log", + "machine", + "machine_manager", + "thiserror", + "util", +] + [[package]] name = "strum" version = "0.20.0" diff --git a/Cargo.toml b/Cargo.toml index d336e2c7c..bdb211dff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "StratoVirt" +name = "stratovirt" version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2021" @@ -9,58 +9,18 @@ license = "Mulan PSL v2" [dependencies] thiserror = "1.0" anyhow = "1.0" -capng = "0.2.2" -error-chain = "0.12.4" -kvm-ioctls = "0.12.0" -libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.0" -hypervisor = { path = "hypervisor" } machine = { path = "machine" } machine_manager = { path = "machine_manager" } -migration = { path = "migration" } util = { path = "util" } -virtio = { path = "virtio" } -vhost_user_fs = { path = "vhost_user_fs" } -vfio = { path = "vfio" } - -[target.'cfg(not(target_env = "musl"))'.dependencies] -vnc = { path = "vnc" } [workspace] members = [ - "address_space", - "boot_loader", - "cpu", - "devices", - "hypervisor", - "machine", - "machine_manager", - "migration", - "migration_derive", - "pci", - "sysbus", - "util", - "acpi", - "virtio", "vhost_user_fs", "ozone", - "vfio", "tests/mod_test", ] -[[bin]] -name = "stratovirt" -path = "src/main.rs" - -[[bin]] -name = "ozone" -path = "ozone/src/main.rs" - -[[bin]] -name = "vhost_user_fs" -path = "vhost_user_fs/src/main.rs" - [features] default = [] boot_time = ["machine/boot_time"] diff --git a/Makefile b/Makefile index 86fe90507..b490e7f9c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ .PHONY: build build: yum-deps - cargo build --release + cargo build --workspace --bins --release + +.PHONY: dbg-build +dbg-build: yum-deps + cargo build --workspace --bins .PHONY: install install: -- Gitee From e033db21f1455dae90b0648b3fd67fbc2350f54a Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 1 Mar 2023 22:13:38 +0800 Subject: [PATCH 0864/2187] MST: pci test add testcases that writes PIC/PCC twice during hotunplug If guest sets PIC/PCC twice during hotunplug, the device ignores the 2nd write to speed up hotunplug. Signed-off-by: Zhang Bo --- tests/mod_test/tests/pci_test.rs | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 501b2347e..08a15f848 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -1985,6 +1985,53 @@ fn test_pci_hotunplug_006() { tear_down(None, test_state, alloc, None, Some(image_paths)); } +/// Guest sets PIC/PCC twice during hotunplug, the device ignores the 2nd write to speed up hotunplug. +#[test] +fn test_pci_hotunplug_007() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:2:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + let unplug_blk_id = 0; + // Hotunplug the block device attaching the root port. + let (delete_device_command, _delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); + let _ret = test_state.borrow().qmp(&delete_device_command); + assert!( + wait_root_port_intr(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + + // The block device will be unplugged when indicator of power and slot is power off. + power_off_device(root_port.clone()); + // Trigger a 2nd write to PIC/PCC, which will be ignored by the device, and causes no harm. + power_off_device(root_port.clone()); + + test_state.borrow().wait_qmp_event(); + + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + /// Hotplug and hotunplug in sequence. #[test] fn test_pci_hotplug_combine_001() { -- Gitee From 0fd425a6e388505632bafcc4603ea0a702372bbb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 1 Mar 2023 23:42:41 +0800 Subject: [PATCH 0865/2187] makefile: Complete dependencies in yum-deps Add libcap-ng-devel and cyrus-sasl-devel. Signed-off-by: Keqian Zhu --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index b490e7f9c..178864390 100644 --- a/Makefile +++ b/Makefile @@ -16,3 +16,5 @@ clean: yum-deps: @yum install pixman-devel + @yum install libcap-ng-devel + @yum install cyrus-sasl-devel -- Gitee From 9eab5bf157e57a5cd590832f774e2759b8774db9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 2 Mar 2023 08:35:03 +0800 Subject: [PATCH 0866/2187] virtio-blk: Adjust judgements when merge request The easily failed judgements should be put front for better performance. Signed-off-by: Keqian Zhu --- virtio/src/block.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 88a776712..b5120bdce 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -424,13 +424,12 @@ impl BlockIoHandler { || req.out_header.request_type == VIRTIO_BLK_T_OUT; let can_merge = match last_req { Some(ref req_ref) => { - io && merged_reqs < MAX_NUM_MERGE_REQS + io && req_ref.out_header.request_type == req.out_header.request_type + // Note: sector plus sector_num has been checked not overflow. + && (req_ref.out_header.sector + req_ref.get_req_sector_num() == req.out_header.sector) + && merged_reqs < MAX_NUM_MERGE_REQS && merged_iovs + req_iovs <= MAX_NUM_MERGE_IOVS && merged_bytes + req_bytes <= MAX_NUM_MERGE_BYTES - && req_ref.out_header.request_type == req.out_header.request_type - // Note: sector plus sector_num has been checked not overflow. - && (req_ref.out_header.sector + req_ref.get_req_sector_num() - == req.out_header.sector) } None => false, }; -- Gitee From b0ea456bc454e4938c97724579808e1bcad42915 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:27:45 +0800 Subject: [PATCH 0867/2187] virtio-gpu: prepare to mst testcase 1. Move functions and structs which will be used in testcase to lib.rs. Keep gpu.rs private. 2. Make warn and error messages more clear and detailed. 3. Add scanout id check in cmd_update_cursor. Signed-off-by: wubinfeng --- machine_manager/src/config/gpu.rs | 2 +- virtio/src/gpu.rs | 150 +++++++++++++++++------------- 2 files changed, 87 insertions(+), 65 deletions(-) diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 9c3856146..3272effcd 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -73,7 +73,7 @@ impl ConfigCheck for GpuDevConfig { if self.max_hostmem < VIRTIO_GPU_MAX_HOSTMEM { warn!( - "max_hostmem must >= {}, allocating less than it may cause \ + "max_hostmem should >= {}, allocating less than it may cause \ the GPU to fail to start or refresh.", VIRTIO_GPU_MAX_HOSTMEM ); diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index 3c82d7913..d5c53615b 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -24,7 +24,7 @@ use super::{ }; use crate::{iov_discard_front, iov_to_buf, VirtioError, VIRTIO_GPU_F_EDID}; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{anyhow, bail, Result}; use log::{error, warn}; use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_SCANOUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -60,16 +60,6 @@ use vnc::console::{ // number of virtqueues const QUEUE_NUM_GPU: usize = 2; -// simple formats for fbcon/X use -const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; -const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; -const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3; -const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4; -const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; -const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; -const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; -const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; - #[derive(Debug)] struct GpuResource { resource_id: u32, @@ -99,7 +89,7 @@ impl Default for GpuResource { #[allow(unused)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuReqState { +struct VirtioGpuReqState { width: u32, height: u32, x_coor: i32, @@ -108,7 +98,7 @@ pub struct VirtioGpuReqState { #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuCtrlHdr { +struct VirtioGpuCtrlHdr { hdr_type: u32, flags: u32, fence_id: u64, @@ -120,7 +110,7 @@ impl ByteCode for VirtioGpuCtrlHdr {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuRect { +struct VirtioGpuRect { x_coord: u32, y_coord: u32, width: u32, @@ -130,7 +120,7 @@ pub struct VirtioGpuRect { impl ByteCode for VirtioGpuRect {} #[derive(Default, Clone, Copy)] -pub struct VirtioGpuDisplayOne { +struct VirtioGpuDisplayOne { rect: VirtioGpuRect, enabled: u32, flags: u32, @@ -139,7 +129,7 @@ pub struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} #[derive(Default, Clone, Copy)] -pub struct VirtioGpuDisplayInfo { +struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS], } @@ -147,7 +137,7 @@ impl ByteCode for VirtioGpuDisplayInfo {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuGetEdid { +struct VirtioGpuGetEdid { scanouts: u32, padding: u32, } @@ -156,7 +146,7 @@ impl ByteCode for VirtioGpuGetEdid {} #[allow(unused)] // data which transfer to frontend need padding #[derive(Clone, Copy)] -pub struct VirtioGpuRespEdid { +struct VirtioGpuRespEdid { header: VirtioGpuCtrlHdr, size: u32, padding: u32, @@ -178,7 +168,7 @@ impl ByteCode for VirtioGpuRespEdid {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuResourceCreate2d { +struct VirtioGpuResourceCreate2d { resource_id: u32, format: u32, width: u32, @@ -189,7 +179,7 @@ impl ByteCode for VirtioGpuResourceCreate2d {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuResourceUnref { +struct VirtioGpuResourceUnref { resource_id: u32, padding: u32, } @@ -198,7 +188,7 @@ impl ByteCode for VirtioGpuResourceUnref {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuSetScanout { +struct VirtioGpuSetScanout { rect: VirtioGpuRect, scanout_id: u32, resource_id: u32, @@ -208,7 +198,7 @@ impl ByteCode for VirtioGpuSetScanout {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuResourceFlush { +struct VirtioGpuResourceFlush { rect: VirtioGpuRect, resource_id: u32, padding: u32, @@ -218,7 +208,7 @@ impl ByteCode for VirtioGpuResourceFlush {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuTransferToHost2d { +struct VirtioGpuTransferToHost2d { rect: VirtioGpuRect, offset: u64, resource_id: u32, @@ -229,7 +219,7 @@ impl ByteCode for VirtioGpuTransferToHost2d {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuResourceAttachBacking { +struct VirtioGpuResourceAttachBacking { resource_id: u32, nr_entries: u32, } @@ -238,7 +228,7 @@ impl ByteCode for VirtioGpuResourceAttachBacking {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuMemEntry { +struct VirtioGpuMemEntry { addr: u64, length: u32, padding: u32, @@ -248,7 +238,7 @@ impl ByteCode for VirtioGpuMemEntry {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuResourceDetachBacking { +struct VirtioGpuResourceDetachBacking { resource_id: u32, padding: u32, } @@ -261,7 +251,7 @@ impl HardWareOperations for GpuOpts {} #[allow(unused)] #[derive(Default, Clone)] -pub struct VirtioGpuRequest { +struct VirtioGpuRequest { header: VirtioGpuCtrlHdr, index: u16, out_iovec: Vec, @@ -341,7 +331,7 @@ impl VirtioGpuRequest { #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuCursorPos { +struct VirtioGpuCursorPos { scanout_id: u32, x_coord: u32, y_coord: u32, @@ -352,7 +342,7 @@ impl ByteCode for VirtioGpuCursorPos {} #[repr(C)] #[derive(Default, Clone, Copy)] -pub struct VirtioGpuUpdateCursor { +struct VirtioGpuUpdateCursor { pos: VirtioGpuCursorPos, resource_id: u32, hot_x: u32, @@ -453,7 +443,18 @@ fn create_surface( surface } -fn get_pixman_format(format: u32) -> Result { +// simple formats for fbcon/X use +pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; +pub const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; +pub const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3; +pub const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4; +pub const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; +pub const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; +pub const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; +pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; +pub const VIRTIO_GPU_FORMAT_INVALID_UNORM: u32 = 135; + +pub fn get_pixman_format(format: u32) -> Result { match format { VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM => Ok(pixman_format_code_t::PIXMAN_a8r8g8b8), VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8r8g8b8), @@ -469,7 +470,7 @@ fn get_pixman_format(format: u32) -> Result { } } -fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) -> u64 { +pub fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) -> u64 { let bpp = pixman_format_bpp(format as u32); let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); height as u64 * stride @@ -518,32 +519,14 @@ impl GpuIoHandler { }) } - fn send_response(&mut self, req: &VirtioGpuRequest, resp: &T) -> Result<()> { - if let Err(e) = iov_from_buf_direct(&req.in_iovec, resp.as_bytes()).and_then(|size| { - if size == size_of::() { - Ok(()) - } else { - bail!( - "Failed to response gpu request, invalid reponse len {}.", - size - ); - } - }) { - error!( - "Failed to response gpu request, {:?}, may cause suspended.", - e - ); - } - + fn complete_one_request(&mut self, index: u16, len: u32) -> Result<()> { let mut queue_lock = self.ctrl_queue.lock().unwrap(); - if let Err(e) = queue_lock - .vring - .add_used(&self.mem_space, req.index, size_of::() as u32) - { + + if let Err(e) = queue_lock.vring.add_used(&self.mem_space, index, len) { bail!( "Failed to add used ring(gpu ctrl), index {}, len {} {:?}.", - req.index, - size_of::() as u32, + index, + len, e, ); } @@ -555,13 +538,37 @@ impl GpuIoHandler { if let Err(e) = (*self.interrupt_cb.as_ref())(&VirtioInterruptType::Vring, Some(&queue_lock), false) { - error!("Failed to trigger interrupt(gpu ctrl), error is {:?}.", e); + bail!("Failed to trigger interrupt(gpu ctrl), error is {:?}.", e); } } Ok(()) } + fn send_response(&mut self, req: &VirtioGpuRequest, resp: &T) -> Result<()> { + let mut len = 0; + if let Err(e) = iov_from_buf_direct(&req.in_iovec, resp.as_bytes()).and_then(|size| { + len = size; + if size == size_of::() { + Ok(()) + } else { + bail!( + "Expected response length is {}, actual get reponse length {}.", + size_of::(), + size + ); + } + }) { + error!( + "GuestError: An incomplete response will be used instead of the expected, because {:?}. \ + Also, be aware that the virtual machine may suspended if reponse is too short to \ + carry the necessary information.", + e + ); + } + self.complete_one_request(req.index, len as u32) + } + fn response_nodata(&mut self, resp_head_type: u32, req: &VirtioGpuRequest) -> Result<()> { let mut resp = VirtioGpuCtrlHdr { hdr_type: resp_head_type, @@ -581,6 +588,14 @@ impl GpuIoHandler { let mut info_cursor = VirtioGpuUpdateCursor::default(); self.get_request(req, &mut info_cursor)?; + if info_cursor.pos.scanout_id >= self.num_scanouts { + error!( + "GuestError: The scanout id {} is out of range.", + info_cursor.pos.scanout_id + ); + return Ok(()); + } + let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; if req.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { scanout.cursor.pos.x_coord = info_cursor.hot_x; @@ -720,7 +735,7 @@ impl GpuIoHandler { Ok(f) => f, Err(e) => { error!("GuestError: {:?}.", e); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } }; @@ -1305,7 +1320,8 @@ impl GpuIoHandler { VIRTIO_GPU_CMD_GET_EDID => self.cmd_get_edid(req), _ => self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), } { - bail!("Fail to handle GPU request, {:?}.", e); + error!("Fail to handle GPU request, {:?}.", e); + self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req)?; } } @@ -1315,6 +1331,8 @@ impl GpuIoHandler { fn ctrl_queue_evt_handler(&mut self) -> Result<()> { let mut queue = self.ctrl_queue.lock().unwrap(); let mut req_queue = Vec::new(); + let mut need_break = false; + let mut invalid_elem_index = 0; loop { let elem = queue @@ -1328,23 +1346,27 @@ impl GpuIoHandler { Ok(req) => { req_queue.push(req); } - // TODO: Ignore this request may cause vnc suspended Err(e) => { error!( - "GuestError: Failed to create GPU request, {:?}, just ignore it.", + "GuestError: Request will be ignored, because request header is incomplete and {:?}. \ + Also, be aware that the virtual machine may suspended if reponse does not \ + carry the necessary information.", e ); - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| "Failed to add used ring")?; + need_break = true; + invalid_elem_index = elem.index; break; } } } drop(queue); - self.process_control_queue(req_queue) + + self.process_control_queue(req_queue)?; + if need_break { + self.complete_one_request(invalid_elem_index, 0)?; + } + Ok(()) } fn cursor_queue_evt_handler(&mut self) -> Result<()> { -- Gitee From 7d58f96f4f40f886646bb415f211ad86687aec94 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:30:25 +0800 Subject: [PATCH 0868/2187] virtio-gpu: add demo_dpy_device Demo dpy device is a demo pci device which will be treated like display (vnc server or gtk). It caches both the surface and cursor images. Demo_dpy_device will be used in mst testcases. Signed-off-by: wubinfeng --- pci/src/demo_dev.rs | 6 +- pci/src/demo_device/dpy_device.rs | 238 ++++++++++++++++++++++++++++++ pci/src/demo_device/mod.rs | 2 + 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 pci/src/demo_device/dpy_device.rs diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 000902ab9..3bddf366f 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -43,7 +43,9 @@ use machine_manager::config::DemoDevConfig; use crate::demo_device::base_device::BaseDevice; #[cfg(not(target_env = "musl"))] -use crate::demo_device::{gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse}; +use crate::demo_device::{ + dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, +}; use crate::{ config::{ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, PCIE_CONFIG_SPACE_SIZE, @@ -77,6 +79,8 @@ impl DemoDev { "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem))), #[cfg(not(target_env = "musl"))] "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), + #[cfg(not(target_env = "musl"))] + "demo-display" => Arc::new(Mutex::new(DemoDisplay::new(_sys_mem))), _ => Arc::new(Mutex::new(BaseDevice::new())), }; DemoDev { diff --git a/pci/src/demo_device/dpy_device.rs b/pci/src/demo_device/dpy_device.rs new file mode 100644 index 000000000..42556a85a --- /dev/null +++ b/pci/src/demo_device/dpy_device.rs @@ -0,0 +1,238 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +// Demo Dpy device is a simple pci device. Its purpose is to simulate the display +// of image from GPU to test the basic functions of GPU. During the initialization +// of device, it will register in the console. Users can write different address in +// the bar space of the device, the Demo Dpy device can do corresponding operations. +// Currently, the supported operations are: +// Get surface size, Get cursor image size, Get Surface data, Get cursor image data. + +use crate::demo_dev::DeviceTypeOperation; +use address_space::{AddressSpace, GuestAddress}; +use anyhow::{bail, Ok, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::error; +use once_cell::sync::Lazy; +use std::{ + ptr, + sync::{Arc, Mutex}, +}; +use util::pixman::{pixman_format_bpp, pixman_image_get_stride, pixman_image_t}; +use vnc::{ + console::{ + register_display, DisplayChangeListener, DisplayChangeListenerOperations, DisplayMouse, + DisplaySurface, + }, + pixman::{ + get_image_data, get_image_format, get_image_height, ref_pixman_image, unref_pixman_image, + }, +}; + +unsafe impl Send for Surface {} +unsafe impl Sync for Surface {} +pub struct Surface { + pub pixman_image: *mut pixman_image_t, + image: Vec, + cursor: Vec, +} + +impl Default for Surface { + fn default() -> Self { + Self { + pixman_image: ptr::null_mut(), + image: vec![], + cursor: vec![], + } + } +} + +pub static DISPLAY: Lazy>>>> = Lazy::new(|| Mutex::new(Vec::new())); + +pub struct DemoDisplay { + sys_mem: Arc, +} + +impl DemoDisplay { + pub fn new(sys_mem: Arc) -> Self { + Self { sys_mem } + } +} + +#[derive(Default)] +pub struct DpyInterface {} + +impl DisplayChangeListenerOperations for DpyInterface { + fn dpy_switch(&self, surface: &DisplaySurface) { + if DISPLAY.lock().unwrap().is_empty() { + error!("Demo Display is empty, check initialize"); + return; + } + + let ds_clone = DISPLAY.lock().unwrap()[0].clone(); + let mut ds = ds_clone.lock().unwrap(); + unref_pixman_image(ds.pixman_image); + ds.pixman_image = ref_pixman_image(surface.image); + + let res_data_ptr = get_image_data(surface.image) as *mut u8; + let height = get_image_height(surface.image); + let stride; + unsafe { + stride = pixman_image_get_stride(surface.image); + } + + let size = height * stride; + + let mut data: Vec = vec![0u8; size as usize]; + unsafe { + ptr::copy(res_data_ptr, data.as_mut_ptr(), size as usize); + } + ds.image = data; + } + + fn dpy_refresh(&self, _dcl: &Arc>) {} + + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) { + if DISPLAY.lock().unwrap().is_empty() { + error!("Demo Display is empty, check initialize"); + return; + } + + let ds_clone = DISPLAY.lock().unwrap()[0].clone(); + let mut ds = ds_clone.lock().unwrap(); + let res_data_ptr = get_image_data(ds.pixman_image) as *mut u8; + + let bpp = pixman_format_bpp(get_image_format(ds.pixman_image) as u32); + let stride; + unsafe { + stride = pixman_image_get_stride(ds.pixman_image); + } + + let mut i = 0; + let mut offset = y * stride as i32 + x * bpp as i32 / 8; + let count = w * bpp as i32 / 8; + while i < h { + error!( + "update from {} to {}, before is {}", + offset, + offset + count, + ds.image[offset as usize] + ); + + unsafe { + ptr::copy( + res_data_ptr.add(offset as usize), + ds.image.as_mut_ptr().add(offset as usize), + count as usize, + ); + } + error!( + "update from {} to {}, after is {}", + offset, + offset + count, + ds.image[offset as usize] + ); + offset += stride as i32; + i += 1; + } + } + + fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) { + if DISPLAY.lock().unwrap().is_empty() { + error!("Demo Display is empty, check initialize"); + return; + } + + let ds_clone = DISPLAY.lock().unwrap()[0].clone(); + let mut ds = ds_clone.lock().unwrap(); + + ds.cursor = cursor.data.clone(); + } +} + +#[derive(Debug, Clone, Copy)] +pub enum DpyEvent { + QuerySurface = 0, + QueryCursor = 1, + GetSurface = 2, + GetCursor = 3, + Deactive = 4, +} + +// Support at max 64 * 64 image which format is 4 bytes. +// 0x0 WO for surface size +// 0x1 WO for cursor size +// 0x2 WO, surface data +// 0x3 WO, cursor data +impl DeviceTypeOperation for DemoDisplay { + fn read(&mut self, _data: &mut [u8], _addr: GuestAddress, _offset: u64) -> Result<()> { + bail!("Read is not support"); + } + + fn write(&mut self, data: &[u8], _addr: GuestAddress, offset: u64) -> Result<()> { + if DISPLAY.lock().unwrap().is_empty() { + error!("Demo Display is empty, write after get image"); + return Ok(()); + } + + let ds_clone = DISPLAY.lock().unwrap()[0].clone(); + let ds = ds_clone.lock().unwrap(); + + let mem_addr = LittleEndian::read_u64(data); + let mut buf: Vec = vec![]; + + match offset { + 0 => { + buf.push(ds.image.len() as u8); + buf.push((ds.image.len() as u16 >> 8) as u8); + } + 1 => { + buf.push(ds.cursor.len() as u8); + buf.push((ds.cursor.len() as u16 >> 8) as u8); + } + 2 => { + buf = ds.image.clone(); + } + 3 => { + buf = ds.cursor.clone(); + } + _ => { + return self.unrealize(); + } + } + return self.sys_mem.write( + &mut buf.as_slice(), + address_space::GuestAddress(mem_addr), + buf.len() as u64, + ); + } + + fn realize(&mut self) -> Result<()> { + DISPLAY + .lock() + .unwrap() + .push(Arc::new(Mutex::new(Surface::default()))); + let opts = Arc::new(DpyInterface::default()); + let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, opts))); + register_display(&dcl)?; + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + if DISPLAY.lock().unwrap().is_empty() { + error!("Demo Display is empty, write after get image"); + return Ok(()); + } + DISPLAY.lock().unwrap().pop(); + Ok(()) + } +} diff --git a/pci/src/demo_device/mod.rs b/pci/src/demo_device/mod.rs index 15323ef71..128a25148 100644 --- a/pci/src/demo_device/mod.rs +++ b/pci/src/demo_device/mod.rs @@ -1,5 +1,7 @@ pub mod base_device; #[cfg(not(target_env = "musl"))] +pub mod dpy_device; +#[cfg(not(target_env = "musl"))] pub mod gpu_device; #[cfg(not(target_env = "musl"))] pub mod kbd_pointer_device; -- Gitee From b6f5a95f68a4a78317e893ac18d7fdd69d1c30a1 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:31:26 +0800 Subject: [PATCH 0869/2187] virtio-gpu: add wrapper functions for virtio-gpu cmd These wrapper functions can be considered atomic cmds to implement mst testcases.And we implemented bar space writes of demo_dpy_dev to get display images. Signed-off-by: wubinfeng --- tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/virtio_gpu.rs | 925 +++++++++++++++++++++ 2 files changed, 926 insertions(+) create mode 100644 tests/mod_test/src/libdriver/virtio_gpu.rs diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index dd2189ea0..76356df82 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -19,6 +19,7 @@ pub mod usb; pub mod virtio; pub mod virtio_block; pub mod virtio_console; +pub mod virtio_gpu; pub mod virtio_pci_modern; pub mod virtio_rng; pub mod vnc; diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs new file mode 100644 index 000000000..fa0f40bdc --- /dev/null +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -0,0 +1,925 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::{ + machine::TestStdMachine, + malloc::GuestAllocator, + pci::{PCIBarAddr, TestPciDev, PCI_VENDOR_ID}, + pci_bus::TestPciBus, +}; +use crate::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; +use crate::libdriver::virtio_pci_modern::TestVirtioPciDev; +use crate::libtest::{test_init, TestState}; + +use std::{cell::RefCell, mem::size_of, rc::Rc, slice::from_raw_parts, vec}; +use util::byte_code::ByteCode; +use virtio::{ + VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_UPDATE_CURSOR, +}; + +const TIMEOUT_US: u64 = 15 * 1000 * 1000; + +pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuCtrlHdr { + pub hdr_type: u32, + pub flags: u32, + pub fence_id: u64, + pub ctx_id: u32, + pub padding: u32, +} + +impl ByteCode for VirtioGpuCtrlHdr {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuRect { + pub x_coord: u32, + pub y_coord: u32, + pub width: u32, + pub height: u32, +} + +impl VirtioGpuRect { + pub fn new(x_coord: u32, y_coord: u32, width: u32, height: u32) -> Self { + Self { + x_coord, + y_coord, + width, + height, + } + } +} + +impl ByteCode for VirtioGpuRect {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuDisplayOne { + pub rect: VirtioGpuRect, + pub enabled: u32, + pub flags: u32, +} + +impl ByteCode for VirtioGpuDisplayOne {} + +#[repr(C)] +#[allow(unused)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuDisplayInfo { + pub header: VirtioGpuCtrlHdr, + pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS], +} +impl ByteCode for VirtioGpuDisplayInfo {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuGetEdid { + pub scanouts: u32, + pub padding: u32, +} + +impl VirtioGpuGetEdid { + pub fn new(scanouts: u32) -> Self { + Self { + scanouts, + padding: 0, + } + } +} + +impl ByteCode for VirtioGpuGetEdid {} + +#[repr(C)] +#[allow(unused)] +#[derive(Clone, Copy)] +pub struct VirtioGpuRespEdid { + pub header: VirtioGpuCtrlHdr, + pub size: u32, + pub padding: u32, + pub edid: [u8; 1024], +} + +impl Default for VirtioGpuRespEdid { + fn default() -> Self { + VirtioGpuRespEdid { + header: VirtioGpuCtrlHdr::default(), + size: 0, + padding: 0, + edid: [0; 1024], + } + } +} + +impl ByteCode for VirtioGpuRespEdid {} + +#[repr(C)] +#[derive(Default, Clone, Copy, Debug)] +pub struct VirtioGpuResourceCreate2d { + pub resource_id: u32, + pub format: u32, + pub width: u32, + pub height: u32, +} + +impl VirtioGpuResourceCreate2d { + pub fn new(resource_id: u32, format: u32, width: u32, height: u32) -> Self { + Self { + resource_id, + format, + width, + height, + } + } +} + +impl ByteCode for VirtioGpuResourceCreate2d {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceUnref { + pub resource_id: u32, + pub padding: u32, +} + +impl VirtioGpuResourceUnref { + pub fn new(resource_id: u32) -> Self { + Self { + resource_id, + padding: 0, + } + } +} + +impl ByteCode for VirtioGpuResourceUnref {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuSetScanout { + pub rect: VirtioGpuRect, + pub scanout_id: u32, + pub resource_id: u32, +} + +impl VirtioGpuSetScanout { + pub fn new(rect: VirtioGpuRect, scanout_id: u32, resource_id: u32) -> Self { + Self { + rect, + scanout_id, + resource_id, + } + } +} + +impl ByteCode for VirtioGpuSetScanout {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceFlush { + pub rect: VirtioGpuRect, + pub resource_id: u32, + pub padding: u32, +} + +impl VirtioGpuResourceFlush { + pub fn new(rect: VirtioGpuRect, resource_id: u32) -> Self { + Self { + rect, + resource_id, + padding: 0, + } + } +} + +impl ByteCode for VirtioGpuResourceFlush {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuTransferToHost2d { + pub rect: VirtioGpuRect, + pub offset: u64, + pub resource_id: u32, + pub padding: u32, +} + +impl VirtioGpuTransferToHost2d { + pub fn new(rect: VirtioGpuRect, offset: u64, resource_id: u32) -> Self { + Self { + rect, + offset, + resource_id, + padding: 0, + } + } +} + +impl ByteCode for VirtioGpuTransferToHost2d {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceAttachBacking { + pub resource_id: u32, + pub nr_entries: u32, +} + +impl VirtioGpuResourceAttachBacking { + pub fn new(resource_id: u32, nr_entries: u32) -> Self { + Self { + resource_id, + nr_entries, + } + } +} + +impl ByteCode for VirtioGpuResourceAttachBacking {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuMemEntry { + pub addr: u64, + pub length: u32, + pub padding: u32, +} + +impl VirtioGpuMemEntry { + pub fn new(addr: u64, length: u32) -> Self { + Self { + addr, + length, + padding: 0, + } + } +} + +impl ByteCode for VirtioGpuMemEntry {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuResourceDetachBacking { + pub resource_id: u32, + pub padding: u32, +} + +impl VirtioGpuResourceDetachBacking { + pub fn new(resource_id: u32) -> Self { + Self { + resource_id, + padding: 0, + } + } +} + +impl ByteCode for VirtioGpuResourceDetachBacking {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuCursorPos { + pub scanout_id: u32, + pub x_coord: u32, + pub y_coord: u32, + pub padding: u32, +} + +impl ByteCode for VirtioGpuCursorPos {} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct VirtioGpuUpdateCursor { + pub pos: VirtioGpuCursorPos, + pub resource_id: u32, + pub hot_x: u32, + pub hot_y: u32, + pub padding: u32, +} + +impl ByteCode for VirtioGpuUpdateCursor {} + +#[derive(Debug, Clone, Copy)] +pub enum DpyEvent { + QuerySurface = 0, + QueryCursor = 1, + GetSurface = 2, + GetCursor = 3, + Deactive = 4, +} + +pub struct TestDemoDpyDevice { + pub pci_dev: TestPciDev, + pub bar_addr: PCIBarAddr, + bar_idx: u8, + allocator: Rc>, +} + +pub struct TestVirtioGpu { + pub device: Rc>, + pub allocator: Rc>, + pub state: Rc>, + pub ctrl_q: Rc>, + pub cursor_q: Rc>, +} + +impl TestDemoDpyDevice { + pub fn new(pci_bus: Rc>, allocator: Rc>) -> Self { + Self { + pci_dev: TestPciDev::new(pci_bus), + bar_addr: 0, + bar_idx: 0, + allocator, + } + } + + pub fn deactive(&mut self) { + let addr = self.allocator.borrow_mut().alloc(1); + self.pci_dev + .io_writeq(self.bar_addr, DpyEvent::Deactive as u64, addr); + } + + pub fn query_surface(&mut self) -> u16 { + let addr = self.allocator.borrow_mut().alloc(size_of::() as u64); + let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); + self.pci_dev + .io_writeq(self.bar_addr, DpyEvent::QuerySurface as u64, addr); + return test_state.borrow_mut().readw(addr); + } + + pub fn query_cursor(&mut self) -> u16 { + let addr = self.allocator.borrow_mut().alloc(size_of::() as u64); + let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); + self.pci_dev + .io_writeq(self.bar_addr, DpyEvent::QueryCursor as u64, addr); + return test_state.borrow_mut().readw(addr); + } + + pub fn get_surface(&mut self, size: u64) -> Vec { + let addr = self.allocator.borrow_mut().alloc(size_of::() as u64); + let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); + self.pci_dev + .io_writeq(self.bar_addr, DpyEvent::GetSurface as u64, addr); + return test_state.borrow_mut().memread(addr, size); + } + + pub fn get_cursor(&mut self, size: u64) -> Vec { + let addr = self.allocator.borrow_mut().alloc(size_of::() as u64); + let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); + self.pci_dev + .io_writeq(self.bar_addr, DpyEvent::GetCursor as u64, addr); + return test_state.borrow_mut().memread(addr, size); + } + + pub fn set_devfn(&mut self, devfn: u8) { + self.pci_dev.devfn = devfn; + } + + pub fn find_pci_device(&mut self, devfn: u8) -> bool { + self.set_devfn(devfn); + if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { + return false; + } + true + } + + pub fn init(&mut self, pci_slot: u8) { + let devfn = pci_slot << 3; + assert!(self.find_pci_device(devfn)); + + self.pci_dev.enable(); + self.bar_addr = self.pci_dev.io_map(self.bar_idx); + } +} + +impl TestVirtioGpu { + pub fn new( + pci_bus: Rc>, + allocator: Rc>, + state: Rc>, + ) -> Self { + Self { + device: Rc::new(RefCell::new(TestVirtioPciDev::new(pci_bus))), + allocator, + state: state, + ctrl_q: Rc::new(RefCell::new(TestVirtQueue::new())), + cursor_q: Rc::new(RefCell::new(TestVirtQueue::new())), + } + } + + pub fn init(&mut self, pci_slot: u8, pci_fn: u8) { + self.device.borrow_mut().init(pci_slot, pci_fn); + self.device.borrow_mut().pci_dev.enable_msix(None); + self.device + .borrow_mut() + .setup_msix_configuration_vector(self.allocator.clone(), 0); + let features = self.device.borrow_mut().get_device_features(); + self.device.borrow_mut().negotiate_features(features); + self.device.borrow_mut().set_features_ok(); + + let ctrl_q = + self.device + .borrow_mut() + .setup_virtqueue(self.state.clone(), self.allocator.clone(), 0); + self.device + .borrow_mut() + .setup_virtqueue_intr(1, self.allocator.clone(), ctrl_q.clone()); + let cursor_q = + self.device + .borrow_mut() + .setup_virtqueue(self.state.clone(), self.allocator.clone(), 1); + self.device + .borrow_mut() + .setup_virtqueue_intr(2, self.allocator.clone(), cursor_q.clone()); + + self.ctrl_q = ctrl_q.clone(); + self.cursor_q = cursor_q.clone(); + + self.device.borrow_mut().set_driver_ok(); + } + + pub fn request_complete( + &mut self, + ctrl_q: bool, + hdr: &[u8], + hdr_ctx: Option<&[u8]>, + ctx: Option<&[u8]>, + resp: Option<&mut T>, + ) { + let mut offset = 0; + let mut vec = Vec::new(); + let hdr_len = hdr.len() as u64; + let mut hdr_ctx_len = 0; + if hdr_ctx.is_some() { + hdr_ctx_len = hdr_ctx.as_ref().unwrap().len() as u64; + } + let mut ctx_len = 0; + if ctx.is_some() { + ctx_len = ctx.as_ref().unwrap().len() as u64; + } + let mut resp_len = 0; + if resp.is_some() { + resp_len = size_of::() as u64; + } + + let addr = self + .allocator + .borrow_mut() + .alloc(hdr_len + hdr_ctx_len + ctx_len + resp_len); + + // write first + self.state.borrow().memwrite(addr, hdr); + + let mut tmp = TestVringDescEntry::default(); + + if hdr_ctx.is_some() { + self.state + .borrow() + .memwrite(addr + hdr_len, hdr_ctx.unwrap()); + let mut tmp = TestVringDescEntry::default(); + tmp.data = addr; + tmp.len = (hdr_len + hdr_ctx_len) as u32; + tmp.write = false; + vec.push(tmp); + offset += hdr_len + hdr_ctx_len; + } else { + tmp.data = addr; + tmp.len = hdr_len as u32; + tmp.write = false; + vec.push(tmp); + offset += hdr_len; + } + + if ctx.is_some() { + self.state.borrow().memwrite(addr + offset, ctx.unwrap()); + let mut tmp = TestVringDescEntry::default(); + tmp.data = addr + offset; + tmp.len = ctx_len as u32; + tmp.write = false; + vec.push(tmp); + offset += ctx_len; + } + + if resp.is_some() { + self.state + .borrow() + .memwrite(addr + offset, resp.as_ref().unwrap().as_bytes()); + let mut tmp = TestVringDescEntry::default(); + tmp.data = addr + offset; + tmp.len = resp_len as u32; + tmp.write = true; + vec.push(tmp); + } + + if ctrl_q { + let free_head = self + .ctrl_q + .borrow_mut() + .add_chained(self.state.clone(), vec); + + self.device + .borrow_mut() + .kick_virtqueue(self.state.clone(), self.ctrl_q.clone()); + + self.device.borrow_mut().poll_used_elem( + self.state.clone(), + self.ctrl_q.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } else { + let free_head = self + .cursor_q + .borrow_mut() + .add_chained(self.state.clone(), vec); + + self.device + .borrow_mut() + .kick_virtqueue(self.state.clone(), self.cursor_q.clone()); + + self.device.borrow_mut().poll_used_elem( + self.state.clone(), + self.cursor_q.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } + + if resp.is_some() { + let resp_bytes_new = self + .state + .borrow_mut() + .memread(addr + hdr_len + hdr_ctx_len + ctx_len, resp_len); + + let slice = + unsafe { from_raw_parts(resp_bytes_new.as_ptr() as *const T, size_of::()) }; + + *resp.unwrap() = slice[0].clone(); + } + } +} + +#[derive(Clone, Debug)] +pub struct GpuDevConfig { + pub id: String, + pub max_outputs: u32, + pub edid: bool, + pub xres: u32, + pub yres: u32, + pub max_hostmem: u64, +} + +impl Default for GpuDevConfig { + fn default() -> Self { + Self { + id: String::from("gpu"), + max_outputs: 1, + edid: true, + xres: 1024, + yres: 768, + max_hostmem: 1024 * 1024 * 4, + } + } +} + +pub fn set_up( + gpu_conf: &GpuDevConfig, +) -> (Rc>, Rc>) { + let gpu_pci_slot: u8 = 0x4; + let gpu_pci_fn: u8 = 0x0; + let dpy_pci_slot: u8 = 0x3; + let dpy_pci_fn: u8 = 0x0; + + let mut args: Vec = Vec::new(); + // vm args + let vm_args = String::from("-machine virt"); + let vm_args: Vec<&str> = vm_args[..].split(' ').collect(); + let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut vm_args); + // log args + let log_args = String::from("-D /tmp/virtio_gpu_test_log"); + let log_args: Vec<&str> = log_args[..].split(' ').collect(); + let mut log_args = log_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut log_args); + // virtio-gpu args + let gpu_args = format!( + "-device virtio-gpu-pci,id=drv0,bus=pcie.{},addr={}.0,max_hostmem={}", + gpu_pci_fn, gpu_pci_slot, gpu_conf.max_hostmem + ); + let gpu_args: Vec<&str> = gpu_args[..].split(' ').collect(); + let mut gpu_args = gpu_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut gpu_args); + // demo dpy device args + let dpy_args = format!( + "-device pcie-demo-dev,bus=pcie.{},addr={}.0,id=1,\ + bar_num=3,device_type=demo-display,bar_size=4096", + dpy_pci_fn, dpy_pci_slot + ); + let dpy_args: Vec<&str> = dpy_args[..].split(' ').collect(); + let mut dpy_args = dpy_args.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut dpy_args); + let args = args.iter().map(AsRef::as_ref).collect(); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new_bymem(test_state.clone(), 1280 * 1024 * 1024, 4096); + let allocator = machine.allocator.clone(); + + let demo_dpy = Rc::new(RefCell::new(TestDemoDpyDevice::new( + machine.pci_bus.clone(), + allocator.clone(), + ))); + demo_dpy.borrow_mut().init(dpy_pci_slot); + + let virtgpu = Rc::new(RefCell::new(TestVirtioGpu::new( + machine.pci_bus.clone(), + allocator.clone(), + test_state.clone(), + ))); + virtgpu.borrow_mut().init(gpu_pci_slot, gpu_pci_fn); + + (demo_dpy, virtgpu) +} + +pub fn tear_down(dpy: Rc>, gpu: Rc>) { + dpy.borrow_mut().deactive(); + gpu.borrow_mut().state.borrow_mut().stop(); +} + +// VIRTIO_GPU_CMD_GET_DISPLAY_INFO +pub fn get_display_info(gpu: &Rc>) -> VirtioGpuDisplayInfo { + let mut hdr = VirtioGpuCtrlHdr::default(); + hdr.hdr_type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; + + let mut resp = VirtioGpuDisplayInfo::default(); + + gpu.borrow_mut() + .request_complete(true, hdr.as_bytes(), None, None, Some(&mut resp)); + return resp; +} + +// VIRTIO_GPU_CMD_GET_EDID +pub fn get_edid(gpu: &Rc>, hdr_ctx: VirtioGpuGetEdid) -> VirtioGpuRespEdid { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_GET_EDID; + + let mut resp = VirtioGpuRespEdid::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +pub fn current_curosr_check(dpy: &Rc>, local: &Vec) -> bool { + let size = dpy.borrow_mut().query_cursor(); + if size as usize != local.len() { + return false; + } + let remote = dpy.borrow_mut().get_cursor(size as u64); + + for (i, v) in remote.iter().enumerate() { + if v != local.get(i).unwrap() { + return false; + } + } + true +} + +pub fn current_surface_check(dpy: &Rc>, local: &Vec) -> bool { + let size = dpy.borrow_mut().query_surface(); + if size as usize != local.len() { + return false; + } + let remote = dpy.borrow_mut().get_surface(size as u64); + + for (i, v) in remote.iter().enumerate() { + if v != local.get(i).unwrap() { + return false; + } + } + true +} + +// VIRTIO_GPU_CMD_RESOURCE_CREATE_2D +pub fn resource_create( + gpu: &Rc>, + hdr_ctx: VirtioGpuResourceCreate2d, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +// VIRTIO_GPU_CMD_RESOURCE_UNREF +pub fn resource_unref( + gpu: &Rc>, + hdr_ctx: VirtioGpuResourceUnref, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_UNREF; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +// VIRTIO_GPU_CMD_RESOURCE_FLUSH +pub fn resource_flush( + gpu: &Rc>, + hdr_ctx: VirtioGpuResourceFlush, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_FLUSH; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +// VIRTIO_GPU_CMD_UPDATE_CURSOR +pub fn update_cursor(gpu: &Rc>, resource_id: u32, scanout_id: u32) { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_UPDATE_CURSOR; + + let mut hdr_ctx = VirtioGpuUpdateCursor::default(); + + hdr_ctx.pos.scanout_id = scanout_id; + hdr_ctx.resource_id = resource_id; + + gpu.borrow_mut().request_complete::( + false, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + None, + ); +} + +// VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING +pub fn resource_attach_backing( + gpu: &Rc>, + hdr_ctx: VirtioGpuResourceAttachBacking, + ctxs: Vec, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING; + + let mut ctx: Vec = vec![]; + for i in ctxs { + // let tmp = &i; + // let mut tmp = tmp.as_bytes().to_vec(); + ctx.append(&mut i.as_bytes().to_vec()); + } + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + Some(&ctx), + Some(&mut resp), + ); + return resp; +} + +pub fn resource_attach_backing_with_invalid_ctx_len( + gpu: &Rc>, + hdr_ctx: VirtioGpuResourceAttachBacking, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +// VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING +pub fn resource_detach_backing( + gpu: &Rc>, + hdr_ctx: VirtioGpuResourceDetachBacking, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +// VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D +pub fn transfer_to_host( + gpu: &Rc>, + hdr_ctx: VirtioGpuTransferToHost2d, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + hdr.hdr_type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +// VIRTIO_GPU_CMD_SET_SCANOUT +pub fn set_scanout( + gpu: &Rc>, + hdr_ctx: VirtioGpuSetScanout, +) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + + hdr.hdr_type = VIRTIO_GPU_CMD_SET_SCANOUT; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut().request_complete( + true, + hdr.as_bytes(), + Some(hdr_ctx.as_bytes()), + None, + Some(&mut resp), + ); + return resp; +} + +pub fn invalid_cmd_test(gpu: &Rc>) -> VirtioGpuCtrlHdr { + let mut hdr = VirtioGpuCtrlHdr::default(); + hdr.hdr_type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO - 1; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut() + .request_complete(true, hdr.as_bytes(), None, None, Some(&mut resp)); + return resp; +} -- Gitee From 68c4abdeb8e911336601291fddfe6f6e7947f8a4 Mon Sep 17 00:00:00 2001 From: Bingsong Si Date: Tue, 28 Feb 2023 10:42:02 +0800 Subject: [PATCH 0870/2187] docs: fix cargo doc warning Signed-off-by: Bingsong Si --- acpi/src/acpi_table.rs | 4 ++-- ozone/src/syscall.rs | 2 +- util/src/seccomp.rs | 2 +- virtio/src/vhost/kernel/mod.rs | 2 +- virtio/src/vhost/user/message.rs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 2f998876a..508b807e0 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -271,7 +271,7 @@ pub struct AcpiSratProcessorAffinity { pub type_id: u8, /// The length of this structure. pub length: u8, - /// Bit [7:0] of the proximity domain to which the processor belongs. + /// Bit `\[`7:0`\]` of the proximity domain to which the processor belongs. pub proximity_lo: u8, /// The processor local APIC ID. pub local_apic_id: u8, @@ -279,7 +279,7 @@ pub struct AcpiSratProcessorAffinity { pub flags: u32, /// The processor local SAPIC EID. pub local_sapic_eid: u8, - /// Bit [31:8] of the proximity domain to which the processor belongs. + /// Bit `\[`31:8`\]` of the proximity domain to which the processor belongs. pub proximity_hi: [u8; 3], /// The clock domain to which the processor belongs. pub clock_domain: u32, diff --git a/ozone/src/syscall.rs b/ozone/src/syscall.rs index 8c5f658b9..648f3fc20 100644 --- a/ozone/src/syscall.rs +++ b/ozone/src/syscall.rs @@ -142,7 +142,7 @@ pub fn set_host_name(host_name: &str) -> Result<()> { /// /// # Arguments /// -/// * `fd` - File descriptor referring to one of magic links in a /proc/[pid]/ns/ directory. +/// * `fd` - File descriptor referring to one of magic links in a /proc/`\[`pid`\]`/ns/ directory. /// * `nstype` - Namespace type. pub fn setns(fd: i32, nstype: i32) -> Result<()> { SyscallResult { diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 03c84d097..168ecc90e 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -218,7 +218,7 @@ impl SeccompData { /// Filter block /// -/// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/filter.h#L24 +/// See: `` #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct SockFilter { diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 54897db64..a16f0fb21 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -60,7 +60,7 @@ ioctl_iow_nr!(VHOST_VSOCK_SET_GUEST_CID, VHOST, 0x60, u64); ioctl_iow_nr!(VHOST_VSOCK_SET_RUNNING, VHOST, 0x61, i32); /// Refer to vhost_vring_file in -/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. +/// `` #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct VhostVringFile { diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index a4ebb2995..31699f7c3 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -105,9 +105,9 @@ impl From for VhostUserMsgReq { pub enum VhostUserHdrFlag { /// Bits[0..1] is message version number. Version = 0x3, - /// Bits[2] Mark message as reply. + /// Bits`\[`2`]` Mark message as reply. Reply = 0x4, - /// Bits[3] Sender anticipates a reply message from the peer. + /// Bits`\[`3`\]` Sender anticipates a reply message from the peer. NeedReply = 0x8, /// All valid bits. AllFlags = 0xc, -- Gitee From 01ec1e8845dcdef17f1d022db78f263f70b3b471 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 1 Mar 2023 21:15:33 +0800 Subject: [PATCH 0871/2187] VNC: Convert the directory of vnc to ui 1. Convert directory name from vnc to ui, as vnc is one of ui display way. 2. Move vnc related file to ui/vnc. Signed-off-by: Xiao Ye --- Cargo.lock | 50 +++++++++---------- devices/Cargo.toml | 2 +- devices/src/legacy/ramfb.rs | 4 +- machine/Cargo.toml | 2 +- machine/src/standard_vm/aarch64/mod.rs | 6 +-- machine/src/standard_vm/mod.rs | 10 ++-- machine/src/standard_vm/x86_64/mod.rs | 6 +-- pci/Cargo.toml | 2 +- pci/src/demo_device/dpy_device.rs | 4 +- pci/src/demo_device/gpu_device.rs | 4 +- pci/src/demo_device/kbd_pointer_device.rs | 2 +- {vnc => ui}/Cargo.toml | 4 +- {vnc => ui}/src/console.rs | 0 {vnc => ui}/src/data/keycode.rs | 0 {vnc => ui}/src/data/mod.rs | 0 {vnc => ui}/src/error.rs | 0 {vnc => ui}/src/input.rs | 3 +- {vnc => ui}/src/lib.rs | 9 +--- {vnc => ui}/src/pixman.rs | 0 {vnc => ui}/src/utils.rs | 8 +-- vnc/src/auth.rs => ui/src/vnc/auth_sasl.rs | 4 +- .../src/vnc/auth_vencrypt.rs | 8 +-- vnc/src/client.rs => ui/src/vnc/client_io.rs | 14 +++--- .../src/vnc}/encoding/enc_hextile.rs | 22 +++++--- {vnc/src => ui/src/vnc}/encoding/mod.rs | 0 .../vnc}/encoding/test_hextile_image_data.rs | 0 ui/src/vnc/mod.rs | 18 +++++++ vnc/src/server.rs => ui/src/vnc/server_io.rs | 16 +++--- {vnc/src => ui/src/vnc}/vnc.rs | 18 ++++--- usb/Cargo.toml | 2 +- usb/src/keyboard.rs | 2 +- usb/src/tablet.rs | 2 +- virtio/Cargo.toml | 2 +- virtio/src/gpu.rs | 8 +-- 34 files changed, 127 insertions(+), 105 deletions(-) rename {vnc => ui}/Cargo.toml (88%) rename {vnc => ui}/src/console.rs (100%) rename {vnc => ui}/src/data/keycode.rs (100%) rename {vnc => ui}/src/data/mod.rs (100%) rename {vnc => ui}/src/error.rs (100%) rename {vnc => ui}/src/input.rs (99%) rename {vnc => ui}/src/lib.rs (88%) rename {vnc => ui}/src/pixman.rs (100%) rename {vnc => ui}/src/utils.rs (97%) rename vnc/src/auth.rs => ui/src/vnc/auth_sasl.rs (99%) rename vnc/src/vencrypt.rs => ui/src/vnc/auth_vencrypt.rs (98%) rename vnc/src/client.rs => ui/src/vnc/client_io.rs (99%) rename {vnc/src => ui/src/vnc}/encoding/enc_hextile.rs (96%) rename {vnc/src => ui/src/vnc}/encoding/mod.rs (100%) rename {vnc/src => ui/src/vnc}/encoding/test_hextile_image_data.rs (100%) create mode 100644 ui/src/vnc/mod.rs rename vnc/src/server.rs => ui/src/vnc/server_io.rs (97%) rename {vnc/src => ui/src/vnc}/vnc.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 888a511c0..73f39cf74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,9 +220,9 @@ dependencies = [ "serial_test", "sysbus", "thiserror", + "ui", "util", "vmm-sys-util", - "vnc", ] [[package]] @@ -428,13 +428,13 @@ dependencies = [ "serde_json", "sysbus", "thiserror", + "ui", "usb", "util", "vfio", "vfio-bindings", "virtio", "vmm-sys-util", - "vnc", ] [[package]] @@ -597,9 +597,9 @@ dependencies = [ "once_cell", "sysbus", "thiserror", + "ui", "util", "vmm-sys-util", - "vnc", ] [[package]] @@ -937,6 +937,26 @@ dependencies = [ "syn", ] +[[package]] +name = "ui" +version = "2.2.0" +dependencies = [ + "anyhow", + "bitintr", + "libc", + "log", + "machine_manager", + "once_cell", + "rustls", + "rustls-pemfile", + "sasl2-sys", + "serde_json", + "sscanf", + "thiserror", + "util", + "vmm-sys-util", +] + [[package]] name = "unicode-ident" version = "1.0.4" @@ -974,8 +994,8 @@ dependencies = [ "once_cell", "pci", "thiserror", + "ui", "util", - "vnc", ] [[package]] @@ -1075,9 +1095,9 @@ dependencies = [ "serde_json", "sysbus", "thiserror", + "ui", "util", "vmm-sys-util", - "vnc", ] [[package]] @@ -1090,26 +1110,6 @@ dependencies = [ "libc", ] -[[package]] -name = "vnc" -version = "2.2.0" -dependencies = [ - "anyhow", - "bitintr", - "libc", - "log", - "machine_manager", - "once_cell", - "rustls", - "rustls-pemfile", - "sasl2-sys", - "serde_json", - "sscanf", - "thiserror", - "util", - "vmm-sys-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 982dcc4cc..8848a209e 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -25,7 +25,7 @@ sysbus = { path = "../sysbus" } util = { path = "../util" } acpi = { path = "../acpi" } [target.'cfg(not(target_env = "musl"))'.dependencies] -vnc = { path = "../vnc" } +ui = { path = "../ui" } [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 78e2c02b8..b4c8531bf 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -20,11 +20,11 @@ use log::error; use std::mem::size_of; use std::sync::{Arc, Mutex}; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; -use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; -use vnc::console::{ +use ui::console::{ console_init, display_graphic_update, display_replace_surface, DisplayConsole, DisplaySurface, HardWareOperations, }; +use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 8973f1cfd..1a11a3020 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -33,7 +33,7 @@ vfio = { path = "../vfio" } usb = { path = "../usb" } [target.'cfg(not(target_env = "musl"))'.dependencies] -vnc = { path = "../vnc" } +ui = { path = "../ui" } [features] default = ["qmp"] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 916eada4d..19bd40083 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -64,13 +64,13 @@ use pci::{PciDevOps, PciHost}; use pci_host_root::PciHostRoot; use sysbus::{SysBus, SysBusDevType, SysRes, IRQ_BASE, IRQ_MAX}; use syscall::syscall_whitelist; +#[cfg(not(target_env = "musl"))] +use ui::vnc; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; -#[cfg(not(target_env = "musl"))] -use vnc::vnc; use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; use crate::MachineOps; @@ -515,7 +515,7 @@ impl MachineOps for StdMachine { .with_context(|| anyhow!(StdErrorKind::InitPCIeHostErr))?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; #[cfg(not(target_env = "musl"))] - vnc::vnc_init(&vm_config.vnc, &vm_config.object) + vnc::vnc::vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; let migrate = locked_vm.get_migrate_info(); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 33cf55947..08b9284c8 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -23,15 +23,15 @@ pub use aarch64::StdMachine; use log::error; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::UpdateRegionArgument; +#[cfg(not(target_env = "musl"))] +use ui::{ + input::{key_event, point_event}, + vnc::vnc::qmp_query_vnc, +}; use util::aio::AioEngine; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -#[cfg(not(target_env = "musl"))] -use vnc::{ - input::{key_event, point_event}, - vnc::qmp_query_vnc, -}; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 7cc9fda32..2ca3b7acd 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -62,9 +62,9 @@ use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; use crate::{vm_state, MachineOps}; use anyhow::{anyhow, bail, Context, Result}; -use virtio::ScsiCntlr::ScsiCntlrMap; #[cfg(not(target_env = "musl"))] -use vnc::vnc; +use ui::vnc; +use virtio::ScsiCntlr::ScsiCntlrMap; const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -444,7 +444,7 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; #[cfg(not(target_env = "musl"))] - vnc::vnc_init(&vm_config.vnc, &vm_config.object) + vnc::vnc::vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; diff --git a/pci/Cargo.toml b/pci/Cargo.toml index e1b226ec8..3834ce9e5 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -26,4 +26,4 @@ util = { path = "../util" } acpi = { path = "../acpi" } [target.'cfg(not(target_env = "musl"))'.dependencies] -vnc = { path = "../vnc" } +ui = { path = "../ui" } diff --git a/pci/src/demo_device/dpy_device.rs b/pci/src/demo_device/dpy_device.rs index 42556a85a..895a4922a 100644 --- a/pci/src/demo_device/dpy_device.rs +++ b/pci/src/demo_device/dpy_device.rs @@ -27,8 +27,7 @@ use std::{ ptr, sync::{Arc, Mutex}, }; -use util::pixman::{pixman_format_bpp, pixman_image_get_stride, pixman_image_t}; -use vnc::{ +use ui::{ console::{ register_display, DisplayChangeListener, DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, @@ -37,6 +36,7 @@ use vnc::{ get_image_data, get_image_format, get_image_height, ref_pixman_image, unref_pixman_image, }, }; +use util::pixman::{pixman_format_bpp, pixman_image_get_stride, pixman_image_t}; unsafe impl Send for Surface {} unsafe impl Sync for Surface {} diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 864375481..57cedaedb 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -27,8 +27,7 @@ use std::{ ptr, sync::{Arc, Mutex, Weak}, }; -use util::pixman::pixman_format_code_t; -use vnc::{ +use ui::{ console::{ console_close, console_init, display_cursor_define, display_graphic_update, display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, @@ -37,6 +36,7 @@ use vnc::{ create_pixman_image, get_image_data, get_image_format, get_image_stride, ref_pixman_image, }, }; +use util::pixman::pixman_format_code_t; pub const UPDATE_FACTOR: [u8; 7] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40]; use crate::demo_dev::DeviceTypeOperation; diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/pci/src/demo_device/kbd_pointer_device.rs index 0ef489e7c..0211a51c3 100644 --- a/pci/src/demo_device/kbd_pointer_device.rs +++ b/pci/src/demo_device/kbd_pointer_device.rs @@ -16,7 +16,7 @@ use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use once_cell::sync::Lazy; use std::sync::{Arc, Mutex}; -use vnc::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; +use ui::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; use anyhow::{bail, Result}; diff --git a/vnc/Cargo.toml b/ui/Cargo.toml similarity index 88% rename from vnc/Cargo.toml rename to ui/Cargo.toml index 96aae6a8a..348201f34 100644 --- a/vnc/Cargo.toml +++ b/ui/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "vnc" +name = "ui" version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" -description = "Visual Network Computing" +description = "User Interface" [dependencies] thiserror = "1.0" diff --git a/vnc/src/console.rs b/ui/src/console.rs similarity index 100% rename from vnc/src/console.rs rename to ui/src/console.rs diff --git a/vnc/src/data/keycode.rs b/ui/src/data/keycode.rs similarity index 100% rename from vnc/src/data/keycode.rs rename to ui/src/data/keycode.rs diff --git a/vnc/src/data/mod.rs b/ui/src/data/mod.rs similarity index 100% rename from vnc/src/data/mod.rs rename to ui/src/data/mod.rs diff --git a/vnc/src/error.rs b/ui/src/error.rs similarity index 100% rename from vnc/src/error.rs rename to ui/src/error.rs diff --git a/vnc/src/input.rs b/ui/src/input.rs similarity index 99% rename from vnc/src/input.rs rename to ui/src/input.rs index 6785221ac..40f8cbb68 100644 --- a/vnc/src/input.rs +++ b/ui/src/input.rs @@ -11,10 +11,9 @@ // See the Mulan PSL v2 for more details. use crate::{ - client::ClientIoHandler, console::console_select, pixman::{get_image_height, get_image_width}, - vnc::BIT_PER_BYTE, + vnc::{client_io::ClientIoHandler, vnc::BIT_PER_BYTE}, }; use anyhow::Result; use log::error; diff --git a/vnc/src/lib.rs b/ui/src/lib.rs similarity index 88% rename from vnc/src/lib.rs rename to ui/src/lib.rs index a45290aa7..323d0043f 100644 --- a/vnc/src/lib.rs +++ b/ui/src/lib.rs @@ -10,19 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod error; -pub use error::VncError; - -pub mod auth; -pub mod client; pub mod console; mod data; -pub mod encoding; +pub mod error; pub mod input; pub mod pixman; -pub mod server; pub mod utils; -pub mod vencrypt; pub mod vnc; pub const fn round_up_div(n: u64, d: u64) -> u64 { diff --git a/vnc/src/pixman.rs b/ui/src/pixman.rs similarity index 100% rename from vnc/src/pixman.rs rename to ui/src/pixman.rs diff --git a/vnc/src/utils.rs b/ui/src/utils.rs similarity index 97% rename from vnc/src/utils.rs rename to ui/src/utils.rs index 7ca3b251f..fff54d516 100644 --- a/vnc/src/utils.rs +++ b/ui/src/utils.rs @@ -73,7 +73,7 @@ impl BuffPool { /// /// # Example /// ```rust - /// use vnc::utils::BuffPool; + /// use ui::utils::BuffPool; /// /// let mut buffpool = BuffPool::new(); /// buffpool.set_limit(Some(1)); @@ -89,7 +89,7 @@ impl BuffPool { /// /// # Example /// ```rust - /// use vnc::utils::BuffPool; + /// use ui::utils::BuffPool; /// /// let mut buffpool = BuffPool::new(); /// buffpool.append_limit((0_u8).to_be_bytes().to_vec()); @@ -109,7 +109,7 @@ impl BuffPool { /// /// # Example /// ```rust - /// use vnc::utils::BuffPool; + /// use ui::utils::BuffPool; /// /// let mut buffpool = BuffPool::new(); /// buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); @@ -140,7 +140,7 @@ impl BuffPool { /// /// # Example /// ```rust - /// use vnc::utils::BuffPool; + /// use ui::utils::BuffPool; /// /// let mut buffpool = BuffPool::new(); /// buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); diff --git a/vnc/src/auth.rs b/ui/src/vnc/auth_sasl.rs similarity index 99% rename from vnc/src/auth.rs rename to ui/src/vnc/auth_sasl.rs index bc9513dc1..64ab780bd 100644 --- a/vnc/src/auth.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -11,8 +11,8 @@ // See the Mulan PSL v2 for more details. use crate::{ - client::{vnc_flush, vnc_write, ClientIoHandler, APP_NAME}, - VncError, + error::VncError, + vnc::client_io::{vnc_flush, vnc_write, ClientIoHandler, APP_NAME}, }; use anyhow::{anyhow, Result}; use libc::{c_char, c_int, c_uint, c_void}; diff --git a/vnc/src/vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs similarity index 98% rename from vnc/src/vencrypt.rs rename to ui/src/vnc/auth_vencrypt.rs index 6ce81dfd7..32103ea55 100644 --- a/vnc/src/vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -11,9 +11,11 @@ // See the Mulan PSL v2 for more details. use crate::{ - auth::SubAuthState, - client::{vnc_flush, vnc_write, ClientIoHandler}, - VncError, + error::VncError, + vnc::{ + auth_sasl::SubAuthState, + client_io::{vnc_flush, vnc_write, ClientIoHandler}, + }, }; use anyhow::{anyhow, Result}; use log::{error, info}; diff --git a/vnc/src/client.rs b/ui/src/vnc/client_io.rs similarity index 99% rename from vnc/src/client.rs rename to ui/src/vnc/client_io.rs index 3f879d522..5d14ce424 100644 --- a/vnc/src/client.rs +++ b/ui/src/vnc/client_io.rs @@ -11,18 +11,20 @@ // See the Mulan PSL v2 for more details. use crate::{ - auth::AuthState, console::DisplayMouse, + error::VncError, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, round_up_div, - server::VncServer, utils::BuffPool, vnc::{ - framebuffer_upadate, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, - DIRTY_WIDTH_BITS, MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, - OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, + auth_sasl::AuthState, + server_io::VncServer, + vnc::{ + framebuffer_upadate, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, + DIRTY_WIDTH_BITS, MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, + OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, + }, }, - VncError, }; use anyhow::{anyhow, Result}; use log::error; diff --git a/vnc/src/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs similarity index 96% rename from vnc/src/encoding/enc_hextile.rs rename to ui/src/vnc/encoding/enc_hextile.rs index 3a1e8d474..a2862cf7d 100644 --- a/vnc/src/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -10,9 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::client::{DisplayMode, Rectangle}; -use crate::pixman::{bytes_per_pixel, get_image_data, get_image_stride}; -use crate::vnc::write_pixel; +use crate::{ + pixman::{bytes_per_pixel, get_image_data, get_image_stride}, + vnc::{ + client_io::{DisplayMode, Rectangle}, + vnc::write_pixel, + }, +}; use std::{cmp, mem}; use util::pixman::pixman_image_t; @@ -355,12 +359,14 @@ fn pixel_statistical<'a>( mod tests { use super::hextile_send_framebuffer_update; use crate::{ - client::{DisplayMode, Rectangle, ENCODING_HEXTILE}, - encoding::test_hextile_image_data::{ - IMAGE_DATA_MULTI_PIXELS, IMAGE_DATA_SINGLE_PIXEL, IMAGE_DATA_TWO_PIXEL, - TARGET_DATA_MULTI_PIXELS, TARGET_DATA_SINGLE_PIXEL, TARGET_DATA_TWO_PIXEL, - }, pixman::{create_pixman_image, PixelFormat}, + vnc::{ + client_io::{DisplayMode, Rectangle, ENCODING_HEXTILE}, + encoding::test_hextile_image_data::{ + IMAGE_DATA_MULTI_PIXELS, IMAGE_DATA_SINGLE_PIXEL, IMAGE_DATA_TWO_PIXEL, + TARGET_DATA_MULTI_PIXELS, TARGET_DATA_SINGLE_PIXEL, TARGET_DATA_TWO_PIXEL, + }, + }, }; use util::pixman::pixman_format_code_t; fn color_init() -> PixelFormat { diff --git a/vnc/src/encoding/mod.rs b/ui/src/vnc/encoding/mod.rs similarity index 100% rename from vnc/src/encoding/mod.rs rename to ui/src/vnc/encoding/mod.rs diff --git a/vnc/src/encoding/test_hextile_image_data.rs b/ui/src/vnc/encoding/test_hextile_image_data.rs similarity index 100% rename from vnc/src/encoding/test_hextile_image_data.rs rename to ui/src/vnc/encoding/test_hextile_image_data.rs diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs new file mode 100644 index 000000000..e27dda8c1 --- /dev/null +++ b/ui/src/vnc/mod.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod auth_sasl; +pub mod auth_vencrypt; +pub mod client_io; +pub mod encoding; +pub mod server_io; +pub mod vnc; diff --git a/vnc/src/server.rs b/ui/src/vnc/server_io.rs similarity index 97% rename from vnc/src/server.rs rename to ui/src/vnc/server_io.rs index 7623b2ada..ae061a53d 100644 --- a/vnc/src/server.rs +++ b/ui/src/vnc/server_io.rs @@ -11,11 +11,8 @@ // See the Mulan PSL v2 for more details. use crate::{ - auth::SaslAuth, - auth::{AuthState, SaslConfig, SubAuthState}, - client::vnc_write, - client::{vnc_flush, ClientIoHandler, ClientState}, console::{DisplayChangeListener, DisplayMouse}, + error::VncError, input::KeyBoardState, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, @@ -23,12 +20,15 @@ use crate::{ unref_pixman_image, }, round_up_div, - vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, vnc::{ - update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, - VNC_BITMAP_WIDTH, VNC_SERVERS, + auth_sasl::{AuthState, SaslAuth, SaslConfig, SubAuthState}, + auth_vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, + client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState}, + vnc::{ + update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, + VNC_BITMAP_WIDTH, VNC_SERVERS, + }, }, - VncError, }; use anyhow::{anyhow, Result}; use log::{error, info}; diff --git a/vnc/src/vnc.rs b/ui/src/vnc/vnc.rs similarity index 98% rename from vnc/src/vnc.rs rename to ui/src/vnc/vnc.rs index 38144a82e..fa366f061 100644 --- a/vnc/src/vnc.rs +++ b/ui/src/vnc/vnc.rs @@ -11,26 +11,28 @@ // See the Mulan PSL v2 for more details. use crate::{ - client::{ - desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush, - vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, - ENCODING_HEXTILE, ENCODING_RAW, - }, console::{ graphic_hardware_update, register_display, DisplayChangeListener, DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, }, data::keycode::KEYSYM2KEYCODE, - encoding::enc_hextile::hextile_send_framebuffer_update, + error::VncError, input::KeyBoardState, pixman::{ bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, ref_pixman_image, unref_pixman_image, }, round_up, round_up_div, - server::{make_server_config, VncConnHandler, VncServer, VncSurface}, - VncError, + vnc::{ + client_io::{ + desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush, + vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, + ENCODING_HEXTILE, ENCODING_RAW, + }, + encoding::enc_hextile::hextile_send_framebuffer_update, + server_io::{make_server_config, VncConnHandler, VncServer, VncSurface}, + }, }; use anyhow::{anyhow, Result}; use core::time; diff --git a/usb/Cargo.toml b/usb/Cargo.toml index 82416ac41..8aaeef21a 100644 --- a/usb/Cargo.toml +++ b/usb/Cargo.toml @@ -19,4 +19,4 @@ pci = { path = "../pci" } machine_manager = { path = "../machine_manager" } [target.'cfg(not(target_env = "musl"))'.dependencies] -vnc = { path = "../vnc" } \ No newline at end of file +ui = { path = "../ui" } \ No newline at end of file diff --git a/usb/src/keyboard.rs b/usb/src/keyboard.rs index 701c2484c..a3672e6ee 100644 --- a/usb/src/keyboard.rs +++ b/usb/src/keyboard.rs @@ -27,7 +27,7 @@ use crate::usb::{ UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; -use vnc::input::{register_keyboard, KeyboardOpts}; +use ui::input::{register_keyboard, KeyboardOpts}; /// Keyboard device descriptor static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { diff --git a/usb/src/tablet.rs b/usb/src/tablet.rs index 07433594e..0507947d1 100644 --- a/usb/src/tablet.rs +++ b/usb/src/tablet.rs @@ -28,7 +28,7 @@ use crate::usb::{ UsbPacketStatus, }; use crate::xhci::xhci_controller::XhciDevice; -use vnc::input::{register_pointer, PointerOpts}; +use ui::input::{register_pointer, PointerOpts}; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 415746a14..58cc24cd4 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -28,4 +28,4 @@ acpi = { path = "../acpi" } devices = {path = "../devices"} [target.'cfg(not(target_env = "musl"))'.dependencies] -vnc = { path = "../vnc" } +ui = { path = "../ui" } diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index d5c53615b..6733b7c76 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -36,6 +36,10 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; +use ui::console::{ + console_close, console_init, display_cursor_define, display_graphic_update, + display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, +}; use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; use util::byte_code::ByteCode; use util::loop_context::{ @@ -52,10 +56,6 @@ use util::pixman::{ }; use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use vnc::console::{ - console_close, console_init, display_cursor_define, display_graphic_update, - display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, -}; // number of virtqueues const QUEUE_NUM_GPU: usize = 2; -- Gitee From fce534accb674fb4022a1c740c49824cf1ed356a Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 03:16:49 +0800 Subject: [PATCH 0872/2187] VNC: rename vnc.rs to mod.rs Rename vnc.rs to mod.rs. Otherwise, it will have the same name as the external module. Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 2 +- ui/src/input.rs | 2 +- ui/src/lib.rs | 8 - ui/src/vnc/client_io.rs | 11 +- ui/src/vnc/encoding/enc_hextile.rs | 2 +- ui/src/vnc/mod.rs | 668 +++++++++++++++++++++++- ui/src/vnc/server_io.rs | 7 +- ui/src/vnc/vnc.rs | 671 ------------------------- 10 files changed, 677 insertions(+), 698 deletions(-) delete mode 100644 ui/src/vnc/vnc.rs diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 19bd40083..918c29563 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -515,7 +515,7 @@ impl MachineOps for StdMachine { .with_context(|| anyhow!(StdErrorKind::InitPCIeHostErr))?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; #[cfg(not(target_env = "musl"))] - vnc::vnc::vnc_init(&vm_config.vnc, &vm_config.object) + vnc::vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; let migrate = locked_vm.get_migrate_info(); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 08b9284c8..6324d8924 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -26,7 +26,7 @@ use machine_manager::qmp::qmp_schema::UpdateRegionArgument; #[cfg(not(target_env = "musl"))] use ui::{ input::{key_event, point_event}, - vnc::vnc::qmp_query_vnc, + vnc::qmp_query_vnc, }; use util::aio::AioEngine; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 2ca3b7acd..f1dc49e3f 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -444,7 +444,7 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; #[cfg(not(target_env = "musl"))] - vnc::vnc::vnc_init(&vm_config.vnc, &vm_config.object) + vnc::vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; diff --git a/ui/src/input.rs b/ui/src/input.rs index 40f8cbb68..a6023fe58 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -13,7 +13,7 @@ use crate::{ console::console_select, pixman::{get_image_height, get_image_width}, - vnc::{client_io::ClientIoHandler, vnc::BIT_PER_BYTE}, + vnc::{client_io::ClientIoHandler, BIT_PER_BYTE}, }; use anyhow::Result; use log::error; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 323d0043f..e2dd7ff87 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -17,11 +17,3 @@ pub mod input; pub mod pixman; pub mod utils; pub mod vnc; - -pub const fn round_up_div(n: u64, d: u64) -> u64 { - (n + d - 1) / d -} - -pub const fn round_up(n: u64, d: u64) -> u64 { - round_up_div(n, d) * d -} diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 5d14ce424..4b0c7478d 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -14,16 +14,11 @@ use crate::{ console::DisplayMouse, error::VncError, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, - round_up_div, utils::BuffPool, vnc::{ - auth_sasl::AuthState, - server_io::VncServer, - vnc::{ - framebuffer_upadate, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, - DIRTY_WIDTH_BITS, MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, - OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, - }, + auth_sasl::AuthState, framebuffer_upadate, round_up_div, server_io::VncServer, + set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, + MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, }, }; use anyhow::{anyhow, Result}; diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index a2862cf7d..f105aaef5 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -14,7 +14,7 @@ use crate::{ pixman::{bytes_per_pixel, get_image_data, get_image_stride}, vnc::{ client_io::{DisplayMode, Rectangle}, - vnc::write_pixel, + write_pixel, }, }; use std::{cmp, mem}; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index e27dda8c1..f52e5cf8e 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -15,4 +15,670 @@ pub mod auth_vencrypt; pub mod client_io; pub mod encoding; pub mod server_io; -pub mod vnc; + +use crate::{ + console::{ + graphic_hardware_update, register_display, DisplayChangeListener, + DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, + DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, + }, + data::keycode::KEYSYM2KEYCODE, + error::VncError, + input::KeyBoardState, + pixman::{ + bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, + get_image_width, ref_pixman_image, unref_pixman_image, + }, + vnc::{ + client_io::{ + desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush, + vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, + ENCODING_HEXTILE, ENCODING_RAW, + }, + encoding::enc_hextile::hextile_send_framebuffer_update, + server_io::{make_server_config, VncConnHandler, VncServer, VncSurface}, + }, +}; +use anyhow::{anyhow, Result}; +use core::time; +use log::error; +use machine_manager::{ + config::{ObjectConfig, VncConfig}, + event_loop::EventLoop, + qmp::qmp_schema::{VncClientInfo, VncInfo}, +}; +use once_cell::sync::Lazy; +use std::{ + cell::RefCell, + cmp, + collections::HashMap, + net::TcpListener, + ptr, + rc::Rc, + sync::{Arc, Mutex}, + thread, +}; +use util::{ + bitmap::Bitmap, + loop_context::EventNotifierHelper, + pixman::{pixman_format_code_t, pixman_image_t}, +}; + +/// The number of dirty pixels represented bt one bit in dirty bitmap. +pub const DIRTY_PIXELS_NUM: u16 = 16; +/// The default max window width. +pub const MAX_WINDOW_WIDTH: u16 = round_up(2560, DIRTY_PIXELS_NUM as u64) as u16; +/// The default max window height. +pub const MAX_WINDOW_HEIGHT: u16 = 2048; +pub const DIRTY_WIDTH_BITS: u16 = MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM; +pub const VNC_BITMAP_WIDTH: u64 = + round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) * u64::BITS as u64; +pub const MAX_IMAGE_SIZE: i32 = 65535; + +/// Output throttle scale. +pub const OUTPUT_THROTTLE_SCALE: i32 = 5; +/// Min size of output buffer. +pub const MIN_OUTPUT_LIMIT: i32 = 1024 * 1024 * OUTPUT_THROTTLE_SCALE; +const DEFAULT_REFRESH_INTERVAL: u64 = 30; +pub const BIT_PER_BYTE: u32 = 8; + +pub const fn round_up_div(n: u64, d: u64) -> u64 { + (n + d - 1) / d +} + +pub const fn round_up(n: u64, d: u64) -> u64 { + round_up_div(n, d) * d +} + +#[derive(Default)] +pub struct VncInterface {} +impl DisplayChangeListenerOperations for VncInterface { + /// Update guest_image + /// Send a resize command to the client based on whether the image size has changed + fn dpy_switch(&self, surface: &DisplaySurface) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + let need_resize = check_surface(&mut locked_vnc_surface, surface); + unref_pixman_image(locked_vnc_surface.guest_image); + + // Vnc_pixman_image_ref + locked_vnc_surface.guest_image = ref_pixman_image(surface.image); + locked_vnc_surface.guest_format = surface.format; + + let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); + let guest_height: i32 = get_image_height(locked_vnc_surface.guest_image); + if !need_resize { + set_area_dirty( + &mut locked_vnc_surface.guest_dirty_bitmap, + 0, + 0, + guest_width, + guest_height, + guest_width, + guest_height, + ); + return; + } + drop(locked_vnc_surface); + update_server_surface(&server); + + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client in locked_handlers.values_mut() { + let width = vnc_width(guest_width); + let height = vnc_height(guest_height); + let mut buf: Vec = Vec::new(); + // Set Color depth. + set_color_depth(client, &mut buf); + // Desktop_resize. + desktop_resize(client, &server, &mut buf); + // Cursor define. + display_cursor_define(client, &server, &mut buf); + vnc_write(client, buf); + vnc_flush(client); + client.dirty_bitmap.lock().unwrap().clear_all(); + set_area_dirty( + &mut client.dirty_bitmap.lock().unwrap(), + 0, + 0, + width, + height, + guest_width, + guest_height, + ); + vnc_update_output_throttle(client); + } + } + + /// Refresh server_image to guest_image. + fn dpy_refresh(&self, dcl: &Arc>) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + if server.client_handlers.lock().unwrap().is_empty() { + return; + } + let con_id = dcl.lock().unwrap().con_id; + graphic_hardware_update(con_id); + + // Update refresh interval. + let mut update_interval = dcl.lock().unwrap().update_interval; + let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); + if dirty_num != 0 { + update_interval /= 2; + if update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { + update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT + } + } else { + update_interval += DISPLAY_UPDATE_INTERVAL_INC; + if update_interval > DISPLAY_UPDATE_INTERVAL_MAX { + update_interval = DISPLAY_UPDATE_INTERVAL_MAX; + } + } + dcl.lock().unwrap().update_interval = update_interval; + + let mut locked_handlers = server.client_handlers.lock().unwrap(); + for client in locked_handlers.values_mut() { + get_rects(client, dirty_num); + } + } + + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + let g_w = get_image_width(locked_vnc_surface.guest_image); + let g_h = get_image_height(locked_vnc_surface.guest_image); + set_area_dirty( + &mut locked_vnc_surface.guest_dirty_bitmap, + x, + y, + w, + h, + g_w, + g_h, + ); + drop(locked_vnc_surface); + } + + fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) { + if VNC_SERVERS.lock().unwrap().is_empty() { + return; + } + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let width = cursor.width as u64; + let height = cursor.height as u64; + let bpl = round_up_div(width, BIT_PER_BYTE as u64); + // Set the bit for mask. + let bit_mask: u8 = 0x80; + + let mut mask: Vec = vec![0; (bpl * height) as usize]; + let first_bit = if cfg!(target_endian = "big") { + 0_usize + } else { + bytes_per_pixel() - 1 + }; + + for j in 0..height { + let mut bit = bit_mask; + for i in 0..width { + let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; + if let Some(n) = cursor.data.get(idx) { + if *n == 0xff { + mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; + } + } + bit >>= 1; + if bit == 0 { + bit = bit_mask; + } + } + } + + server.vnc_cursor.lock().unwrap().cursor = Some(cursor.clone()); + server.vnc_cursor.lock().unwrap().mask = Some(mask.clone()); + + let mut locked_handler = server.client_handlers.lock().unwrap(); + // Send the framebuff for each client. + for client in locked_handler.values_mut() { + let mut buf: Vec = Vec::new(); + display_cursor_define(client, &server, &mut buf); + vnc_write(client, buf); + vnc_flush(client); + } + } +} + +/// Initizlization function of vnc +/// +/// # Arguments +/// +/// * `VncConfig` `object`- vnc related parameters +pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { + let vnc_cfg = match vnc { + Some(cfg) => cfg, + None => return Ok(()), + }; + + let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); + let listener: TcpListener = match TcpListener::bind(addr.as_str()) { + Ok(l) => l, + Err(e) => { + let msg = format!("Bind {} failed {}", addr, e); + error!("{}", e); + return Err(anyhow!(VncError::TcpBindFailed(msg))); + } + }; + + listener + .set_nonblocking(true) + .expect("Set noblocking for vnc socket failed"); + + let mut keysym2keycode: HashMap = HashMap::new(); + + let mut max_keycode: u16 = 0; + // Mapping ASCII to keycode. + for &(k, v) in KEYSYM2KEYCODE.iter() { + max_keycode = cmp::max(max_keycode, v); + keysym2keycode.insert(k, v); + } + // Record keyboard state. + let keyboard_state: Rc> = + Rc::new(RefCell::new(KeyBoardState::new(max_keycode as usize))); + + let vnc_opts = Arc::new(VncInterface::default()); + let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, vnc_opts))); + + let server = Arc::new(VncServer::new( + get_client_image(), + keyboard_state, + keysym2keycode, + Some(Arc::downgrade(&dcl)), + )); + + // Parameter configuation for VncServeer. + make_server_config(&server, vnc_cfg, object)?; + + // Add an VncServer. + add_vnc_server(server.clone()); + + // Register in display console. + register_display(&dcl)?; + + // Register the event to listen for client's connection. + let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server))); + + // Vnc_thread: a thread to send the framebuffer + start_vnc_thread()?; + + EventLoop::update_event(EventNotifierHelper::internal_notifiers(vnc_io), None)?; + Ok(()) +} + +fn start_vnc_thread() -> Result<()> { + let interval = DEFAULT_REFRESH_INTERVAL; + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + let _handle = thread::Builder::new() + .name("vnc_worker".to_string()) + .spawn(move || loop { + if VNC_RECT_INFO.lock().unwrap().is_empty() { + thread::sleep(time::Duration::from_millis(interval)); + continue; + } + + let mut rect_info; + match VNC_RECT_INFO.lock().unwrap().get_mut(0) { + Some(rect) => { + rect_info = rect.clone(); + } + None => { + thread::sleep(time::Duration::from_millis(interval)); + continue; + } + } + VNC_RECT_INFO.lock().unwrap().remove(0); + + let mut num_rects: i32 = 0; + let mut buf = Vec::new(); + buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + buf.append(&mut [0_u8; 2].to_vec()); + + for rect in rect_info.rects.iter_mut() { + let locked_surface = server.vnc_surface.lock().unwrap(); + let dpm = rect_info.client.client_dpm.lock().unwrap().clone(); + let width = dpm.client_width; + let height = dpm.client_height; + if check_rect(rect, width, height) { + let n = + send_framebuffer_update(locked_surface.server_image, rect, &dpm, &mut buf); + if n >= 0 { + num_rects += n; + } + } + } + buf[2] = (num_rects >> 8) as u8; + buf[3] = num_rects as u8; + + let client = rect_info.client; + vnc_write(&client, buf); + vnc_flush(&client); + })?; + Ok(()) +} + +/// Add a vnc server during initialization. +fn add_vnc_server(server: Arc) { + VNC_SERVERS.lock().unwrap().push(server); +} + +/// Qmp: return the information about current VNC server. +pub fn qmp_query_vnc() -> Option { + let mut vnc_info = VncInfo::default(); + if VNC_SERVERS.lock().unwrap().is_empty() { + vnc_info.enabled = false; + return Some(vnc_info); + } + vnc_info.enabled = true; + let server = VNC_SERVERS.lock().unwrap()[0].clone(); + vnc_info.family = "ipv4".to_string(); + + let mut locked_handler = server.client_handlers.lock().unwrap(); + for client in locked_handler.values_mut() { + let mut client_info = VncClientInfo { + host: client.addr.clone(), + ..Default::default() + }; + client_info.family = "ipv4".to_string(); + vnc_info.clients.push(client_info); + } + + Some(vnc_info) +} + +/// Set dirty in bitmap. +pub fn set_area_dirty( + dirty: &mut Bitmap, + mut x: i32, + mut y: i32, + mut w: i32, + mut h: i32, + g_w: i32, + g_h: i32, +) { + let width: i32 = vnc_width(g_w); + let height: i32 = vnc_height(g_h); + + w += x % DIRTY_PIXELS_NUM as i32; + x -= x % DIRTY_PIXELS_NUM as i32; + + x = cmp::min(x, width); + y = cmp::min(y, height); + w = cmp::min(x + w, width) - x; + h = cmp::min(y + h, height); + while y < h { + let pos = (y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32) as usize; + let len = round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as usize; + if let Err(e) = dirty.set_range(pos, len) { + error!("set bitmap error: {:?}", e); + return; + } + y += 1; + } +} + +/// Get the width of image. +pub fn vnc_width(width: i32) -> i32 { + cmp::min( + MAX_WINDOW_WIDTH as i32, + round_up(width as u64, DIRTY_PIXELS_NUM as u64) as i32, + ) +} + +/// Get the height of image. +fn vnc_height(height: i32) -> i32 { + cmp::min(MAX_WINDOW_HEIGHT as i32, height) +} + +/// Update server image +pub fn update_server_surface(server: &Arc) { + let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); + unref_pixman_image(locked_vnc_surface.server_image); + locked_vnc_surface.server_image = ptr::null_mut(); + // Server image changes, clear the task queue. + VNC_RECT_INFO.lock().unwrap().clear(); + if server.client_handlers.lock().unwrap().is_empty() { + return; + } + + let g_width = get_image_width(locked_vnc_surface.guest_image); + let g_height = get_image_height(locked_vnc_surface.guest_image); + let width = vnc_width(g_width); + let height = vnc_height(g_height); + locked_vnc_surface.server_image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + width, + height, + ptr::null_mut(), + 0, + ); + + locked_vnc_surface.guest_dirty_bitmap.clear_all(); + set_area_dirty( + &mut locked_vnc_surface.guest_dirty_bitmap, + 0, + 0, + width, + height, + g_width, + g_height, + ); +} + +/// Check if the suface for VncClient is need update +fn check_surface(locked_vnc_surface: &mut VncSurface, surface: &DisplaySurface) -> bool { + let guest_width = get_image_width(surface.image); + let guest_height = get_image_height(surface.image); + let server_width = get_image_width(locked_vnc_surface.server_image); + let server_height = get_image_height(locked_vnc_surface.server_image); + if !(0..=MAX_IMAGE_SIZE).contains(&guest_width) || !(0..=MAX_IMAGE_SIZE).contains(&guest_height) + { + return false; + } + + if surface.image.is_null() + || locked_vnc_surface.server_image.is_null() + || locked_vnc_surface.guest_format != surface.format + || guest_width != server_width + || guest_height != server_height + { + return true; + } + + false +} + +/// Check if rectangle is in spec +fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { + if rect.x >= width || rect.y >= height { + return false; + } + + rect.w = cmp::min(width - rect.x, rect.w); + rect.h = cmp::min(height - rect.y, rect.h); + if rect.w <= 0 || rect.h <= 0 { + return false; + } + + true +} + +/// Send updated pixel information to client +/// +/// # Arguments +/// +/// * `x` `y` `w` `h` - coordinate, width, height +/// * `buf` - send buffer +pub fn framebuffer_upadate(x: i32, y: i32, w: i32, h: i32, encoding: i32, buf: &mut Vec) { + buf.append(&mut (x as u16).to_be_bytes().to_vec()); + buf.append(&mut (y as u16).to_be_bytes().to_vec()); + buf.append(&mut (w as u16).to_be_bytes().to_vec()); + buf.append(&mut (h as u16).to_be_bytes().to_vec()); + buf.append(&mut encoding.to_be_bytes().to_vec()); +} + +/// Write pixel to client. +/// +/// # Arguments +/// +/// * `data_ptr` - pointer to the data need. +/// * `copy_bytes` - total pixel to write. +/// * `client_dpm` - Output mod of client display. +/// * `buf` - send buffer. +pub fn write_pixel( + data_ptr: *mut u8, + copy_bytes: usize, + client_dpm: &DisplayMode, + buf: &mut Vec, +) { + if !client_dpm.convert { + let mut con = vec![0; copy_bytes]; + // SAFETY: it can be ensure the raw pointer will not exceed the range. + unsafe { + ptr::copy(data_ptr as *mut u8, con.as_mut_ptr(), copy_bytes); + } + buf.append(&mut con); + } else if client_dpm.convert && bytes_per_pixel() == 4 { + let num = copy_bytes >> 2; + let ptr = data_ptr as *mut u32; + for i in 0..num { + // SAFETY: it can be ensure the raw pointer will not exceed the range. + let color = unsafe { *ptr.add(i) }; + convert_pixel(client_dpm, buf, color); + } + } +} + +/// Convert the sent information to a format supported +/// by the client depend on byte arrangement +/// +/// # Arguments +/// +/// * `client_dpm` - Output mod of client display. +/// * `buf` - send buffer. +/// * `color` - the pixel value need to be convert. +pub fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { + let mut ret = [0u8; 4]; + let r = ((color & 0x00ff0000) >> 16) << client_dpm.pf.red.bits >> 8; + let g = ((color & 0x0000ff00) >> 8) << client_dpm.pf.green.bits >> 8; + let b = (color & 0x000000ff) << client_dpm.pf.blue.bits >> 8; + let v = (r << client_dpm.pf.red.shift) + | (g << client_dpm.pf.green.shift) + | (b << client_dpm.pf.blue.shift); + match client_dpm.pf.pixel_bytes { + 1 => { + ret[0] = v as u8; + } + 2 => { + if client_dpm.client_be { + ret[0] = (v >> 8) as u8; + ret[1] = v as u8; + } else { + ret[1] = (v >> 8) as u8; + ret[0] = v as u8; + } + } + 4 => { + if client_dpm.client_be { + ret = v.to_be_bytes(); + } else { + ret = v.to_le_bytes(); + } + } + _ => { + if client_dpm.client_be { + ret = v.to_be_bytes(); + } else { + ret = v.to_le_bytes(); + } + } + } + buf.append(&mut ret[..client_dpm.pf.pixel_bytes as usize].to_vec()); +} + +/// Send raw data directly without compression +/// +/// # Arguments +/// +/// * `image` - pointer to the data need to be send. +/// * `rect` - dirty area of image. +/// * `client_dpm` - Output mod information of client display. +/// * `buf` - send buffer. +pub fn raw_send_framebuffer_update( + image: *mut pixman_image_t, + rect: &Rectangle, + client_dpm: &DisplayMode, + buf: &mut Vec, +) -> i32 { + let mut data_ptr = get_image_data(image) as *mut u8; + let stride = get_image_stride(image); + data_ptr = (data_ptr as usize + + (rect.y * stride) as usize + + rect.x as usize * bytes_per_pixel()) as *mut u8; + + let copy_bytes = rect.w as usize * bytes_per_pixel(); + + for _i in 0..rect.h { + write_pixel(data_ptr, copy_bytes, client_dpm, buf); + data_ptr = (data_ptr as usize + stride as usize) as *mut u8; + } + + 1 +} + +/// Send data according to compression algorithm +/// +/// # Arguments +/// +/// * `image` = pointer to the data need to be send. +/// * `rect` - dirty area of image. +/// * `client_dpm` - Output mod information of client display. +/// * `buf` - send buffer. +fn send_framebuffer_update( + image: *mut pixman_image_t, + rect: &Rectangle, + client_dpm: &DisplayMode, + buf: &mut Vec, +) -> i32 { + match client_dpm.enc { + ENCODING_HEXTILE => { + framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_HEXTILE, buf); + hextile_send_framebuffer_update(image, rect, client_dpm, buf) + } + _ => { + framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_RAW, buf); + raw_send_framebuffer_update(image, rect, client_dpm, buf) + } + } +} + +/// Initialize a default image +/// Default: width is 640, height is 480, stride is 640 * 4 +fn get_client_image() -> *mut pixman_image_t { + create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + 640, + 480, + ptr::null_mut(), + 640 * 4, + ) +} + +pub static VNC_SERVERS: Lazy>>> = Lazy::new(|| Mutex::new(Vec::new())); +pub static VNC_RECT_INFO: Lazy>>> = + Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index ae061a53d..5bc3f468e 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -19,15 +19,12 @@ use crate::{ get_image_width, pixman_image_linebuf_create, pixman_image_linebuf_fill, unref_pixman_image, }, - round_up_div, vnc::{ auth_sasl::{AuthState, SaslAuth, SaslConfig, SubAuthState}, auth_vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState}, - vnc::{ - update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, - VNC_BITMAP_WIDTH, VNC_SERVERS, - }, + round_up_div, update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, + VNC_BITMAP_WIDTH, VNC_SERVERS, }, }; use anyhow::{anyhow, Result}; diff --git a/ui/src/vnc/vnc.rs b/ui/src/vnc/vnc.rs deleted file mode 100644 index fa366f061..000000000 --- a/ui/src/vnc/vnc.rs +++ /dev/null @@ -1,671 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights r&eserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use crate::{ - console::{ - graphic_hardware_update, register_display, DisplayChangeListener, - DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, - DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, - }, - data::keycode::KEYSYM2KEYCODE, - error::VncError, - input::KeyBoardState, - pixman::{ - bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, - get_image_width, ref_pixman_image, unref_pixman_image, - }, - round_up, round_up_div, - vnc::{ - client_io::{ - desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush, - vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, - ENCODING_HEXTILE, ENCODING_RAW, - }, - encoding::enc_hextile::hextile_send_framebuffer_update, - server_io::{make_server_config, VncConnHandler, VncServer, VncSurface}, - }, -}; -use anyhow::{anyhow, Result}; -use core::time; -use log::error; -use machine_manager::{ - config::{ObjectConfig, VncConfig}, - event_loop::EventLoop, - qmp::qmp_schema::{VncClientInfo, VncInfo}, -}; -use once_cell::sync::Lazy; -use std::{ - cell::RefCell, - cmp, - collections::HashMap, - net::TcpListener, - ptr, - rc::Rc, - sync::{Arc, Mutex}, - thread, -}; -use util::{ - bitmap::Bitmap, - loop_context::EventNotifierHelper, - pixman::{pixman_format_code_t, pixman_image_t}, -}; - -/// The number of dirty pixels represented bt one bit in dirty bitmap. -pub const DIRTY_PIXELS_NUM: u16 = 16; -/// The default max window width. -pub const MAX_WINDOW_WIDTH: u16 = round_up(2560, DIRTY_PIXELS_NUM as u64) as u16; -/// The default max window height. -pub const MAX_WINDOW_HEIGHT: u16 = 2048; -pub const DIRTY_WIDTH_BITS: u16 = MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM; -pub const VNC_BITMAP_WIDTH: u64 = - round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) * u64::BITS as u64; -pub const MAX_IMAGE_SIZE: i32 = 65535; - -/// Output throttle scale. -pub const OUTPUT_THROTTLE_SCALE: i32 = 5; -/// Min size of output buffer. -pub const MIN_OUTPUT_LIMIT: i32 = 1024 * 1024 * OUTPUT_THROTTLE_SCALE; -const DEFAULT_REFRESH_INTERVAL: u64 = 30; -pub const BIT_PER_BYTE: u32 = 8; - -#[derive(Default)] -pub struct VncInterface {} -impl DisplayChangeListenerOperations for VncInterface { - /// Update guest_image - /// Send a resize command to the client based on whether the image size has changed - fn dpy_switch(&self, surface: &DisplaySurface) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); - let need_resize = check_surface(&mut locked_vnc_surface, surface); - unref_pixman_image(locked_vnc_surface.guest_image); - - // Vnc_pixman_image_ref - locked_vnc_surface.guest_image = ref_pixman_image(surface.image); - locked_vnc_surface.guest_format = surface.format; - - let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); - let guest_height: i32 = get_image_height(locked_vnc_surface.guest_image); - if !need_resize { - set_area_dirty( - &mut locked_vnc_surface.guest_dirty_bitmap, - 0, - 0, - guest_width, - guest_height, - guest_width, - guest_height, - ); - return; - } - drop(locked_vnc_surface); - update_server_surface(&server); - - let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client in locked_handlers.values_mut() { - let width = vnc_width(guest_width); - let height = vnc_height(guest_height); - let mut buf: Vec = Vec::new(); - // Set Color depth. - set_color_depth(client, &mut buf); - // Desktop_resize. - desktop_resize(client, &server, &mut buf); - // Cursor define. - display_cursor_define(client, &server, &mut buf); - vnc_write(client, buf); - vnc_flush(client); - client.dirty_bitmap.lock().unwrap().clear_all(); - set_area_dirty( - &mut client.dirty_bitmap.lock().unwrap(), - 0, - 0, - width, - height, - guest_width, - guest_height, - ); - vnc_update_output_throttle(client); - } - } - - /// Refresh server_image to guest_image. - fn dpy_refresh(&self, dcl: &Arc>) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - if server.client_handlers.lock().unwrap().is_empty() { - return; - } - let con_id = dcl.lock().unwrap().con_id; - graphic_hardware_update(con_id); - - // Update refresh interval. - let mut update_interval = dcl.lock().unwrap().update_interval; - let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); - if dirty_num != 0 { - update_interval /= 2; - if update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { - update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT - } - } else { - update_interval += DISPLAY_UPDATE_INTERVAL_INC; - if update_interval > DISPLAY_UPDATE_INTERVAL_MAX { - update_interval = DISPLAY_UPDATE_INTERVAL_MAX; - } - } - dcl.lock().unwrap().update_interval = update_interval; - - let mut locked_handlers = server.client_handlers.lock().unwrap(); - for client in locked_handlers.values_mut() { - get_rects(client, dirty_num); - } - } - - fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); - let g_w = get_image_width(locked_vnc_surface.guest_image); - let g_h = get_image_height(locked_vnc_surface.guest_image); - set_area_dirty( - &mut locked_vnc_surface.guest_dirty_bitmap, - x, - y, - w, - h, - g_w, - g_h, - ); - drop(locked_vnc_surface); - } - - fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) { - if VNC_SERVERS.lock().unwrap().is_empty() { - return; - } - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let width = cursor.width as u64; - let height = cursor.height as u64; - let bpl = round_up_div(width, BIT_PER_BYTE as u64); - // Set the bit for mask. - let bit_mask: u8 = 0x80; - - let mut mask: Vec = vec![0; (bpl * height) as usize]; - let first_bit = if cfg!(target_endian = "big") { - 0_usize - } else { - bytes_per_pixel() - 1 - }; - - for j in 0..height { - let mut bit = bit_mask; - for i in 0..width { - let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; - if let Some(n) = cursor.data.get(idx) { - if *n == 0xff { - mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; - } - } - bit >>= 1; - if bit == 0 { - bit = bit_mask; - } - } - } - - server.vnc_cursor.lock().unwrap().cursor = Some(cursor.clone()); - server.vnc_cursor.lock().unwrap().mask = Some(mask.clone()); - - let mut locked_handler = server.client_handlers.lock().unwrap(); - // Send the framebuff for each client. - for client in locked_handler.values_mut() { - let mut buf: Vec = Vec::new(); - display_cursor_define(client, &server, &mut buf); - vnc_write(client, buf); - vnc_flush(client); - } - } -} - -/// Initizlization function of vnc -/// -/// # Arguments -/// -/// * `VncConfig` `object`- vnc related parameters -pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { - let vnc_cfg = match vnc { - Some(cfg) => cfg, - None => return Ok(()), - }; - - let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); - let listener: TcpListener = match TcpListener::bind(addr.as_str()) { - Ok(l) => l, - Err(e) => { - let msg = format!("Bind {} failed {}", addr, e); - error!("{}", e); - return Err(anyhow!(VncError::TcpBindFailed(msg))); - } - }; - - listener - .set_nonblocking(true) - .expect("Set noblocking for vnc socket failed"); - - let mut keysym2keycode: HashMap = HashMap::new(); - - let mut max_keycode: u16 = 0; - // Mapping ASCII to keycode. - for &(k, v) in KEYSYM2KEYCODE.iter() { - max_keycode = cmp::max(max_keycode, v); - keysym2keycode.insert(k, v); - } - // Record keyboard state. - let keyboard_state: Rc> = - Rc::new(RefCell::new(KeyBoardState::new(max_keycode as usize))); - - let vnc_opts = Arc::new(VncInterface::default()); - let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, vnc_opts))); - - let server = Arc::new(VncServer::new( - get_client_image(), - keyboard_state, - keysym2keycode, - Some(Arc::downgrade(&dcl)), - )); - - // Parameter configuation for VncServeer. - make_server_config(&server, vnc_cfg, object)?; - - // Add an VncServer. - add_vnc_server(server.clone()); - - // Register in display console. - register_display(&dcl)?; - - // Register the event to listen for client's connection. - let vnc_io = Arc::new(Mutex::new(VncConnHandler::new(listener, server))); - - // Vnc_thread: a thread to send the framebuffer - start_vnc_thread()?; - - EventLoop::update_event(EventNotifierHelper::internal_notifiers(vnc_io), None)?; - Ok(()) -} - -fn start_vnc_thread() -> Result<()> { - let interval = DEFAULT_REFRESH_INTERVAL; - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let _handle = thread::Builder::new() - .name("vnc_worker".to_string()) - .spawn(move || loop { - if VNC_RECT_INFO.lock().unwrap().is_empty() { - thread::sleep(time::Duration::from_millis(interval)); - continue; - } - - let mut rect_info; - match VNC_RECT_INFO.lock().unwrap().get_mut(0) { - Some(rect) => { - rect_info = rect.clone(); - } - None => { - thread::sleep(time::Duration::from_millis(interval)); - continue; - } - } - VNC_RECT_INFO.lock().unwrap().remove(0); - - let mut num_rects: i32 = 0; - let mut buf = Vec::new(); - buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); - buf.append(&mut (0_u8).to_be_bytes().to_vec()); - buf.append(&mut [0_u8; 2].to_vec()); - - for rect in rect_info.rects.iter_mut() { - let locked_surface = server.vnc_surface.lock().unwrap(); - let dpm = rect_info.client.client_dpm.lock().unwrap().clone(); - let width = dpm.client_width; - let height = dpm.client_height; - if check_rect(rect, width, height) { - let n = - send_framebuffer_update(locked_surface.server_image, rect, &dpm, &mut buf); - if n >= 0 { - num_rects += n; - } - } - } - buf[2] = (num_rects >> 8) as u8; - buf[3] = num_rects as u8; - - let client = rect_info.client; - vnc_write(&client, buf); - vnc_flush(&client); - })?; - Ok(()) -} - -/// Add a vnc server during initialization. -fn add_vnc_server(server: Arc) { - VNC_SERVERS.lock().unwrap().push(server); -} - -/// Qmp: return the information about current VNC server. -pub fn qmp_query_vnc() -> Option { - let mut vnc_info = VncInfo::default(); - if VNC_SERVERS.lock().unwrap().is_empty() { - vnc_info.enabled = false; - return Some(vnc_info); - } - vnc_info.enabled = true; - let server = VNC_SERVERS.lock().unwrap()[0].clone(); - vnc_info.family = "ipv4".to_string(); - - let mut locked_handler = server.client_handlers.lock().unwrap(); - for client in locked_handler.values_mut() { - let mut client_info = VncClientInfo { - host: client.addr.clone(), - ..Default::default() - }; - client_info.family = "ipv4".to_string(); - vnc_info.clients.push(client_info); - } - - Some(vnc_info) -} - -/// Set dirty in bitmap. -pub fn set_area_dirty( - dirty: &mut Bitmap, - mut x: i32, - mut y: i32, - mut w: i32, - mut h: i32, - g_w: i32, - g_h: i32, -) { - let width: i32 = vnc_width(g_w); - let height: i32 = vnc_height(g_h); - - w += x % DIRTY_PIXELS_NUM as i32; - x -= x % DIRTY_PIXELS_NUM as i32; - - x = cmp::min(x, width); - y = cmp::min(y, height); - w = cmp::min(x + w, width) - x; - h = cmp::min(y + h, height); - while y < h { - let pos = (y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32) as usize; - let len = round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as usize; - if let Err(e) = dirty.set_range(pos, len) { - error!("set bitmap error: {:?}", e); - return; - } - y += 1; - } -} - -/// Get the width of image. -pub fn vnc_width(width: i32) -> i32 { - cmp::min( - MAX_WINDOW_WIDTH as i32, - round_up(width as u64, DIRTY_PIXELS_NUM as u64) as i32, - ) -} - -/// Get the height of image. -fn vnc_height(height: i32) -> i32 { - cmp::min(MAX_WINDOW_HEIGHT as i32, height) -} - -/// Update server image -pub fn update_server_surface(server: &Arc) { - let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); - unref_pixman_image(locked_vnc_surface.server_image); - locked_vnc_surface.server_image = ptr::null_mut(); - // Server image changes, clear the task queue. - VNC_RECT_INFO.lock().unwrap().clear(); - if server.client_handlers.lock().unwrap().is_empty() { - return; - } - - let g_width = get_image_width(locked_vnc_surface.guest_image); - let g_height = get_image_height(locked_vnc_surface.guest_image); - let width = vnc_width(g_width); - let height = vnc_height(g_height); - locked_vnc_surface.server_image = create_pixman_image( - pixman_format_code_t::PIXMAN_x8r8g8b8, - width, - height, - ptr::null_mut(), - 0, - ); - - locked_vnc_surface.guest_dirty_bitmap.clear_all(); - set_area_dirty( - &mut locked_vnc_surface.guest_dirty_bitmap, - 0, - 0, - width, - height, - g_width, - g_height, - ); -} - -/// Check if the suface for VncClient is need update -fn check_surface(locked_vnc_surface: &mut VncSurface, surface: &DisplaySurface) -> bool { - let guest_width = get_image_width(surface.image); - let guest_height = get_image_height(surface.image); - let server_width = get_image_width(locked_vnc_surface.server_image); - let server_height = get_image_height(locked_vnc_surface.server_image); - if !(0..=MAX_IMAGE_SIZE).contains(&guest_width) || !(0..=MAX_IMAGE_SIZE).contains(&guest_height) - { - return false; - } - - if surface.image.is_null() - || locked_vnc_surface.server_image.is_null() - || locked_vnc_surface.guest_format != surface.format - || guest_width != server_width - || guest_height != server_height - { - return true; - } - - false -} - -/// Check if rectangle is in spec -fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { - if rect.x >= width || rect.y >= height { - return false; - } - - rect.w = cmp::min(width - rect.x, rect.w); - rect.h = cmp::min(height - rect.y, rect.h); - if rect.w <= 0 || rect.h <= 0 { - return false; - } - - true -} - -/// Send updated pixel information to client -/// -/// # Arguments -/// -/// * `x` `y` `w` `h` - coordinate, width, height -/// * `buf` - send buffer -pub fn framebuffer_upadate(x: i32, y: i32, w: i32, h: i32, encoding: i32, buf: &mut Vec) { - buf.append(&mut (x as u16).to_be_bytes().to_vec()); - buf.append(&mut (y as u16).to_be_bytes().to_vec()); - buf.append(&mut (w as u16).to_be_bytes().to_vec()); - buf.append(&mut (h as u16).to_be_bytes().to_vec()); - buf.append(&mut encoding.to_be_bytes().to_vec()); -} - -/// Write pixel to client. -/// -/// # Arguments -/// -/// * `data_ptr` - pointer to the data need. -/// * `copy_bytes` - total pixel to write. -/// * `client_dpm` - Output mod of client display. -/// * `buf` - send buffer. -pub fn write_pixel( - data_ptr: *mut u8, - copy_bytes: usize, - client_dpm: &DisplayMode, - buf: &mut Vec, -) { - if !client_dpm.convert { - let mut con = vec![0; copy_bytes]; - // SAFETY: it can be ensure the raw pointer will not exceed the range. - unsafe { - ptr::copy(data_ptr as *mut u8, con.as_mut_ptr(), copy_bytes); - } - buf.append(&mut con); - } else if client_dpm.convert && bytes_per_pixel() == 4 { - let num = copy_bytes >> 2; - let ptr = data_ptr as *mut u32; - for i in 0..num { - // SAFETY: it can be ensure the raw pointer will not exceed the range. - let color = unsafe { *ptr.add(i) }; - convert_pixel(client_dpm, buf, color); - } - } -} - -/// Convert the sent information to a format supported -/// by the client depend on byte arrangement -/// -/// # Arguments -/// -/// * `client_dpm` - Output mod of client display. -/// * `buf` - send buffer. -/// * `color` - the pixel value need to be convert. -pub fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { - let mut ret = [0u8; 4]; - let r = ((color & 0x00ff0000) >> 16) << client_dpm.pf.red.bits >> 8; - let g = ((color & 0x0000ff00) >> 8) << client_dpm.pf.green.bits >> 8; - let b = (color & 0x000000ff) << client_dpm.pf.blue.bits >> 8; - let v = (r << client_dpm.pf.red.shift) - | (g << client_dpm.pf.green.shift) - | (b << client_dpm.pf.blue.shift); - match client_dpm.pf.pixel_bytes { - 1 => { - ret[0] = v as u8; - } - 2 => { - if client_dpm.client_be { - ret[0] = (v >> 8) as u8; - ret[1] = v as u8; - } else { - ret[1] = (v >> 8) as u8; - ret[0] = v as u8; - } - } - 4 => { - if client_dpm.client_be { - ret = v.to_be_bytes(); - } else { - ret = v.to_le_bytes(); - } - } - _ => { - if client_dpm.client_be { - ret = v.to_be_bytes(); - } else { - ret = v.to_le_bytes(); - } - } - } - buf.append(&mut ret[..client_dpm.pf.pixel_bytes as usize].to_vec()); -} - -/// Send raw data directly without compression -/// -/// # Arguments -/// -/// * `image` - pointer to the data need to be send. -/// * `rect` - dirty area of image. -/// * `client_dpm` - Output mod information of client display. -/// * `buf` - send buffer. -pub fn raw_send_framebuffer_update( - image: *mut pixman_image_t, - rect: &Rectangle, - client_dpm: &DisplayMode, - buf: &mut Vec, -) -> i32 { - let mut data_ptr = get_image_data(image) as *mut u8; - let stride = get_image_stride(image); - data_ptr = (data_ptr as usize - + (rect.y * stride) as usize - + rect.x as usize * bytes_per_pixel()) as *mut u8; - - let copy_bytes = rect.w as usize * bytes_per_pixel(); - - for _i in 0..rect.h { - write_pixel(data_ptr, copy_bytes, client_dpm, buf); - data_ptr = (data_ptr as usize + stride as usize) as *mut u8; - } - - 1 -} - -/// Send data according to compression algorithm -/// -/// # Arguments -/// -/// * `image` = pointer to the data need to be send. -/// * `rect` - dirty area of image. -/// * `client_dpm` - Output mod information of client display. -/// * `buf` - send buffer. -fn send_framebuffer_update( - image: *mut pixman_image_t, - rect: &Rectangle, - client_dpm: &DisplayMode, - buf: &mut Vec, -) -> i32 { - match client_dpm.enc { - ENCODING_HEXTILE => { - framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_HEXTILE, buf); - hextile_send_framebuffer_update(image, rect, client_dpm, buf) - } - _ => { - framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_RAW, buf); - raw_send_framebuffer_update(image, rect, client_dpm, buf) - } - } -} - -/// Initialize a default image -/// Default: width is 640, height is 480, stride is 640 * 4 -fn get_client_image() -> *mut pixman_image_t { - create_pixman_image( - pixman_format_code_t::PIXMAN_x8r8g8b8, - 640, - 480, - ptr::null_mut(), - 640 * 4, - ) -} - -pub static VNC_SERVERS: Lazy>>> = Lazy::new(|| Mutex::new(Vec::new())); -pub static VNC_RECT_INFO: Lazy>>> = - Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); -- Gitee From 02c3b4558b1ceda34f0b221f188e6568d28d3952 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 17 Mar 2023 23:09:32 +0800 Subject: [PATCH 0873/2187] VNC: Move vnc related functions from ui/input to ui/vnc/client_io Move vnc related functions from ui/input to ui/vnc/client_io, as input is an independent module, and other modules may also rely on it. Signed-off-by: Xiao Ye --- ui/src/input.rs | 132 +++++----------------------------------- ui/src/vnc/client_io.rs | 101 +++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 118 deletions(-) diff --git a/ui/src/input.rs b/ui/src/input.rs index a6023fe58..9cc8e1344 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -10,13 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{ - console::console_select, - pixman::{get_image_height, get_image_width}, - vnc::{client_io::ClientIoHandler, BIT_PER_BYTE}, -}; use anyhow::Result; -use log::error; use once_cell::sync::Lazy; use std::{ collections::HashMap, @@ -25,19 +19,20 @@ use std::{ use util::bitmap::Bitmap; // Logical window size for mouse. -const ABS_MAX: u64 = 0x7fff; +pub const ABS_MAX: u64 = 0x7fff; // Event type of Point. -const INPUT_POINT_LEFT: u8 = 0x01; -const INPUT_POINT_MIDDLE: u8 = 0x02; -const INPUT_POINT_RIGHT: u8 = 0x04; +pub const INPUT_POINT_LEFT: u8 = 0x01; +pub const INPUT_POINT_MIDDLE: u8 = 0x02; +pub const INPUT_POINT_RIGHT: u8 = 0x04; // ASCII value. -const ASCII_A: i32 = 65; -const ASCII_Z: i32 = 90; -const UPPERCASE_TO_LOWERCASE: i32 = 32; +pub const ASCII_A: i32 = 65; +pub const ASCII_Z: i32 = 90; +pub const UPPERCASE_TO_LOWERCASE: i32 = 32; +const BIT_PER_BYTE: u32 = 8; // Keycode. -const KEYCODE_1: u16 = 2; -const KEYCODE_9: u16 = 10; +pub const KEYCODE_1: u16 = 2; +pub const KEYCODE_9: u16 = 10; const KEYCODE_CTRL: u16 = 29; const KEYCODE_SHIFT: u16 = 42; const KEYCODE_SHIFT_R: u16 = 54; @@ -47,6 +42,8 @@ const KEYCODE_NUM_LOCK: u16 = 69; const KEYCODE_CTRL_R: u16 = 157; const KEYCODE_ALT_R: u16 = 184; +static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); + // Keyboard Modifier State pub enum KeyboardModifier { KeyModNone = 0, @@ -80,7 +77,7 @@ impl KeyBoardState { } /// Get the corresponding keyboard modifier. - fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool { + pub fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool { match self.keymods.contain(key_mod as usize) { Ok(res) => res, Err(_e) => false, @@ -88,12 +85,12 @@ impl KeyBoardState { } /// Reset all keyboard modifier state. - fn keyboard_state_reset(&mut self) { + pub fn keyboard_state_reset(&mut self) { self.keymods.clear_all(); } /// Record the press and up state in the keyboard. - fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> { + pub fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> { // Key is not pressed and the incoming key action is up. if !down && !self.keystate.contain(keycode as usize)? { return Ok(()); @@ -174,105 +171,6 @@ impl KeyBoardState { Ok(()) } } - -impl ClientIoHandler { - /// Keyboard event. - pub fn key_envent(&mut self) { - if self.expect == 1 { - self.expect = 8; - return; - } - let buf = self.read_incoming_msg(); - let down: bool = buf[1] != 0; - let mut keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); - let server = self.server.clone(); - - // Uppercase -> Lowercase. - if (ASCII_A..=ASCII_Z).contains(&keysym) { - keysym += UPPERCASE_TO_LOWERCASE; - } - let mut kbd_state = server.keyboard_state.borrow_mut(); - - let keycode: u16 = match server.keysym2keycode.get(&(keysym as u16)) { - Some(k) => *k, - None => 0, - }; - - // Ctr + Alt + Num(1~9) - // Switch to the corresponding display device. - if (KEYCODE_1..KEYCODE_9 + 1).contains(&keycode) - && down - && self.server.display_listener.is_some() - && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) - && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) - { - kbd_state.keyboard_state_reset(); - console_select(Some((keycode - KEYCODE_1) as usize)) - .unwrap_or_else(|e| error!("{:?}", e)); - } - - kbd_state - .keyboard_state_update(keycode, down) - .unwrap_or_else(|e| error!("Key State update error: {:?}", e)); - key_event(keycode, down).unwrap_or_else(|e| error!("Key event error: {:?}", e)); - - self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); - } - - // Mouse event. - pub fn point_event(&mut self) { - if self.expect == 1 { - self.expect = 6; - return; - } - - let buf = self.read_incoming_msg(); - let mut x = ((buf[2] as u16) << 8) + buf[3] as u16; - let mut y = ((buf[4] as u16) << 8) + buf[5] as u16; - - // Window size alignment. - let locked_surface = self.server.vnc_surface.lock().unwrap(); - let width = get_image_width(locked_surface.server_image); - let height = get_image_height(locked_surface.server_image); - drop(locked_surface); - x = ((x as u64 * ABS_MAX) / width as u64) as u16; - y = ((y as u64 * ABS_MAX) / height as u64) as u16; - - // ASCII -> HidCode. - let button_mask: u8 = match buf[1] { - INPUT_POINT_LEFT => 0x01, - INPUT_POINT_MIDDLE => 0x04, - INPUT_POINT_RIGHT => 0x02, - _ => buf[1], - }; - - point_event(button_mask as u32, x as u32, y as u32) - .unwrap_or_else(|e| error!("Point event error: {:?}", e)); - - self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); - } - - /// Client cut text. - pub fn client_cut_event(&mut self) { - let buf = self.read_incoming_msg(); - if self.expect == 1 { - self.expect = 8; - return; - } - if self.expect == 8 { - let buf = [buf[4], buf[5], buf[6], buf[7]]; - let len = u32::from_be_bytes(buf); - if len > 0 { - self.expect += len as usize; - return; - } - } - - self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); - } -} - -static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); #[derive(Default)] struct Inputs { active_kbd: Option, diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 4b0c7478d..60d47dd9e 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -11,8 +11,12 @@ // See the Mulan PSL v2 for more details. use crate::{ - console::DisplayMouse, + console::{console_select, DisplayMouse}, error::VncError, + input::{ + key_event, point_event, KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, INPUT_POINT_LEFT, + INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, UPPERCASE_TO_LOWERCASE, + }, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, vnc::{ @@ -940,6 +944,101 @@ impl ClientIoHandler { self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); } + /// Keyboard event. + pub fn key_envent(&mut self) { + if self.expect == 1 { + self.expect = 8; + return; + } + let buf = self.read_incoming_msg(); + let down: bool = buf[1] != 0; + let mut keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); + let server = self.server.clone(); + + // Uppercase -> Lowercase. + if (ASCII_A..=ASCII_Z).contains(&keysym) { + keysym += UPPERCASE_TO_LOWERCASE; + } + let mut kbd_state = server.keyboard_state.borrow_mut(); + + let keycode: u16 = match server.keysym2keycode.get(&(keysym as u16)) { + Some(k) => *k, + None => 0, + }; + + // Ctr + Alt + Num(1~9) + // Switch to the corresponding display device. + if (KEYCODE_1..KEYCODE_9 + 1).contains(&keycode) + && down + && self.server.display_listener.is_some() + && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) + && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) + { + kbd_state.keyboard_state_reset(); + console_select(Some((keycode - KEYCODE_1) as usize)) + .unwrap_or_else(|e| error!("{:?}", e)); + } + + kbd_state + .keyboard_state_update(keycode, down) + .unwrap_or_else(|e| error!("Key State update error: {:?}", e)); + key_event(keycode, down).unwrap_or_else(|e| error!("Key event error: {:?}", e)); + + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + } + + // Mouse event. + pub fn point_event(&mut self) { + if self.expect == 1 { + self.expect = 6; + return; + } + + let buf = self.read_incoming_msg(); + let mut x = ((buf[2] as u16) << 8) + buf[3] as u16; + let mut y = ((buf[4] as u16) << 8) + buf[5] as u16; + + // Window size alignment. + let locked_surface = self.server.vnc_surface.lock().unwrap(); + let width = get_image_width(locked_surface.server_image); + let height = get_image_height(locked_surface.server_image); + drop(locked_surface); + x = ((x as u64 * ABS_MAX) / width as u64) as u16; + y = ((y as u64 * ABS_MAX) / height as u64) as u16; + + // ASCII -> HidCode. + let button_mask: u8 = match buf[1] { + INPUT_POINT_LEFT => 0x01, + INPUT_POINT_MIDDLE => 0x04, + INPUT_POINT_RIGHT => 0x02, + _ => buf[1], + }; + + point_event(button_mask as u32, x as u32, y as u32) + .unwrap_or_else(|e| error!("Point event error: {:?}", e)); + + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + } + + /// Client cut text. + pub fn client_cut_event(&mut self) { + let buf = self.read_incoming_msg(); + if self.expect == 1 { + self.expect = 8; + return; + } + if self.expect == 8 { + let buf = [buf[4], buf[5], buf[6], buf[7]]; + let len = u32::from_be_bytes(buf); + if len > 0 { + self.expect += len as usize; + return; + } + } + + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + } + /// Invalid authentication, send 1 to reject. fn auth_failed(&mut self, msg: &str) { let auth_rej: u8 = 1; -- Gitee From e8edcbf160f8c5df31b747cbd3399d79d26882ac Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 1 Mar 2023 22:31:38 +0800 Subject: [PATCH 0874/2187] VNC: Add error reporting for some functions 1. Add error reporting for some functions, and printing errors on the outermost layer. 2. Adjust some error format. Signed-off-by: Xiao Ye --- pci/src/demo_device/dpy_device.rs | 19 ++-- ui/src/console.rs | 43 ++++++--- ui/src/error.rs | 8 +- ui/src/vnc/auth_sasl.rs | 107 +++++++++++----------- ui/src/vnc/auth_vencrypt.rs | 147 +++++++++++++----------------- ui/src/vnc/client_io.rs | 99 ++++++++++---------- ui/src/vnc/mod.rs | 54 +++++------ ui/src/vnc/server_io.rs | 27 +++--- 8 files changed, 252 insertions(+), 252 deletions(-) diff --git a/pci/src/demo_device/dpy_device.rs b/pci/src/demo_device/dpy_device.rs index 895a4922a..3de96d644 100644 --- a/pci/src/demo_device/dpy_device.rs +++ b/pci/src/demo_device/dpy_device.rs @@ -72,10 +72,10 @@ impl DemoDisplay { pub struct DpyInterface {} impl DisplayChangeListenerOperations for DpyInterface { - fn dpy_switch(&self, surface: &DisplaySurface) { + fn dpy_switch(&self, surface: &DisplaySurface) -> Result<()> { if DISPLAY.lock().unwrap().is_empty() { error!("Demo Display is empty, check initialize"); - return; + return Ok(()); } let ds_clone = DISPLAY.lock().unwrap()[0].clone(); @@ -97,14 +97,17 @@ impl DisplayChangeListenerOperations for DpyInterface { ptr::copy(res_data_ptr, data.as_mut_ptr(), size as usize); } ds.image = data; + Ok(()) } - fn dpy_refresh(&self, _dcl: &Arc>) {} + fn dpy_refresh(&self, _dcl: &Arc>) -> Result<()> { + Ok(()) + } - fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) { + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) -> Result<()> { if DISPLAY.lock().unwrap().is_empty() { error!("Demo Display is empty, check initialize"); - return; + return Ok(()); } let ds_clone = DISPLAY.lock().unwrap()[0].clone(); @@ -144,18 +147,20 @@ impl DisplayChangeListenerOperations for DpyInterface { offset += stride as i32; i += 1; } + Ok(()) } - fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) { + fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) -> Result<()> { if DISPLAY.lock().unwrap().is_empty() { error!("Demo Display is empty, check initialize"); - return; + return Ok(()); } let ds_clone = DISPLAY.lock().unwrap()[0].clone(); let mut ds = ds_clone.lock().unwrap(); ds.cursor = cursor.data.clone(); + Ok(()) } } diff --git a/ui/src/console.rs b/ui/src/console.rs index d74fa51cf..e2c684400 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -89,13 +89,13 @@ pub struct DisplayMouse { /// called to display images on the user's desktop. pub trait DisplayChangeListenerOperations { /// Switch the image in display surface. - fn dpy_switch(&self, _surface: &DisplaySurface) {} + fn dpy_switch(&self, _surface: &DisplaySurface) -> Result<()>; /// Refresh the image. - fn dpy_refresh(&self, _dcl: &Arc>) {} + fn dpy_refresh(&self, _dcl: &Arc>) -> Result<()>; /// Update image. - fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) {} + fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) -> Result<()>; /// Update the cursor data. - fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) {} + fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) -> Result<()>; } /// Callback functions registered by graphic hardware. @@ -234,7 +234,10 @@ pub fn display_refresh() { for dcl in &mut related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); - (*dcl_opts).dpy_refresh(dcl); + if let Err(e) = (*dcl_opts).dpy_refresh(dcl) { + error!("{}", e); + return; + } // Update refresh interval. dcl_interval = dcl.lock().unwrap().update_interval; @@ -320,7 +323,7 @@ pub fn display_replace_surface( for dcl in related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); if let Some(s) = &con.lock().unwrap().surface.clone() { - (*dcl_opts).dpy_switch(s); + (*dcl_opts).dpy_switch(s)?; } } Ok(()) @@ -372,7 +375,7 @@ pub fn display_graphic_update( for dcl in related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); - (*dcl_opts).dpy_image_update(x, y, w, h); + (*dcl_opts).dpy_image_update(x, y, w, h)?; } Ok(()) } @@ -409,7 +412,7 @@ pub fn display_cursor_define( for dcl in related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); - (*dcl_opts).dpy_cursor_update(cursor); + (*dcl_opts).dpy_cursor_update(cursor)?; } Ok(()) } @@ -452,7 +455,7 @@ pub fn register_display(dcl: &Arc>) -> Result<()> { let console = CONSOLES.lock().unwrap().get_console_by_id(con_id); if let Some(con) = console { if let Some(surface) = &mut con.lock().unwrap().surface.clone() { - (*dcl_opts).dpy_switch(surface); + (*dcl_opts).dpy_switch(surface)?; } } else { let mut place_holder_image = create_msg_surface( @@ -461,7 +464,7 @@ pub fn register_display(dcl: &Arc>) -> Result<()> { "This VM has no graphic display device.".to_string(), ); if let Some(surface) = &mut place_holder_image { - (*dcl_opts).dpy_switch(surface); + (*dcl_opts).dpy_switch(surface)?; } } @@ -602,7 +605,7 @@ pub fn console_select(con_id: Option) -> Result<()> { for dcl in related_listeners { let dpy_opts = dcl.lock().unwrap().dpy_opts.clone(); if let Some(s) = &mut con.lock().unwrap().surface { - (*dpy_opts).dpy_switch(s); + (*dpy_opts).dpy_switch(s)?; } } @@ -658,7 +661,23 @@ mod tests { use super::*; use machine_manager::config::VmConfig; pub struct DclOpts {} - impl DisplayChangeListenerOperations for DclOpts {} + impl DisplayChangeListenerOperations for DclOpts { + fn dpy_switch(&self, _surface: &DisplaySurface) -> Result<()> { + Ok(()) + } + + fn dpy_refresh(&self, _dcl: &Arc>) -> Result<()> { + Ok(()) + } + + fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) -> Result<()> { + Ok(()) + } + + fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) -> Result<()> { + Ok(()) + } + } struct HwOpts {} impl HardWareOperations for HwOpts {} diff --git a/ui/src/error.rs b/ui/src/error.rs index 08cf8d91b..dce0857cb 100644 --- a/ui/src/error.rs +++ b/ui/src/error.rs @@ -21,8 +21,8 @@ pub enum VncError { }, #[error("Unsupported RFB Protocol Version!")] UnsupportRFBProtocolVersion, - #[error("Invalid Image Size!")] - InvalidImageSize, + #[error("Invalid Image Size: width: {0}, height: {0}")] + InvalidImageSize(i32, i32), #[error("Tcp bind failed: {0}")] TcpBindFailed(String), #[error("Make connection failed: {0}")] @@ -33,8 +33,8 @@ pub enum VncError { ProtocolMessageFailed(String), #[error("Read buf form tcpstream failed: {0}")] ReadMessageFailed(String), - #[error("Authentication failed: {0}")] - AuthFailed(String), + #[error("Authentication failed: func: {0} reason: {0}")] + AuthFailed(String, String), #[error("ParseKeyBoardFailed: {0}")] ParseKeyBoardFailed(String), #[error("Disconnection")] diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 64ab780bd..ef2a4499c 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -16,7 +16,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use libc::{c_char, c_int, c_uint, c_void}; -use log::{error, info}; +use log::info; use sasl2_sys::prelude::{ sasl_conn_t, sasl_dispose, sasl_getprop, sasl_listmech, sasl_security_properties_t, sasl_server_init, sasl_server_new, sasl_server_start, sasl_server_step, sasl_setprop, @@ -117,9 +117,10 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); let len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]); if !(MECHNAME_MIN_LEN..MECHNAME_MAX_LEN).contains(&len) { - return Err(anyhow!(VncError::AuthFailed(String::from( - "SASL mechname too short or too long" - )))); + return Err(anyhow!(VncError::AuthFailed( + "get_mechname_length".to_string(), + "SASL mechname too short or too long".to_string() + ))); } self.update_event_handler(len as usize, ClientIoHandler::get_sasl_mechname); @@ -156,6 +157,7 @@ impl ClientIoHandler { // Unsupported mechanism. if security.saslconfig.mech_name.is_empty() { return Err(anyhow!(VncError::AuthFailed( + "get_sasl_mechname".to_string(), "Unsupported mechanism".to_string() ))); } @@ -172,8 +174,8 @@ impl ClientIoHandler { let len = u32::from_be_bytes(buf); if len > SASL_DATA_MAX_LEN { - error!("SASL start len too large"); return Err(anyhow!(VncError::AuthFailed( + "get_authmessage_length".to_string(), "SASL start len too large".to_string() ))); } @@ -233,13 +235,15 @@ impl ClientIoHandler { // SAFETY: sasl_dispose() is C function. All parameters passed of the // function have been checked. unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) } - error!("Auth failed!"); - return Err(anyhow!(VncError::AuthFailed("Auth failed!".to_string()))); + return Err(anyhow!(VncError::AuthFailed( + "client_sasl_auth".to_string(), + "Auth failed!".to_string() + ))); } if serverout_len > SASL_DATA_MAX_LEN { unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) } - error!("SASL data too long"); return Err(anyhow!(VncError::AuthFailed( + "client_sasl_auth".to_string(), "SASL data too long".to_string() ))); } @@ -312,11 +316,10 @@ impl ClientIoHandler { err = sasl_server_init(ptr::null_mut(), appname.as_ptr()); } if err != SASL_OK { - error!("SASL_FAIL error code {}", err); - return Err(anyhow!(VncError::AuthFailed(format!( - "SASL_FAIL error code {}", - err - )))); + return Err(anyhow!(VncError::AuthFailed( + "sasl_server_init".to_string(), + format!("SASL_FAIL error code {}", err) + ))); } let mut saslconfig = SaslConfig::default(); unsafe { @@ -332,11 +335,10 @@ impl ClientIoHandler { ); } if err != SASL_OK { - error!("SASL_FAIL error code {}", err); - return Err(anyhow!(VncError::AuthFailed(format!( - "SASL_FAIL error code {}", - err - )))); + return Err(anyhow!(VncError::AuthFailed( + "sasl_server_init".to_string(), + format!("SASL_FAIL error code {}", err) + ))); } self.server.security_type.borrow_mut().saslconfig = saslconfig; @@ -360,11 +362,10 @@ impl ClientIoHandler { ); } if err != SASL_OK { - error!("SASL_FAIL error code {}", err); - return Err(anyhow!(VncError::AuthFailed(format!( - "SASL_FAIL error code {}", - err - )))); + return Err(anyhow!(VncError::AuthFailed( + "set_ssf_for_sasl".to_string(), + format!("SASL_FAIL error code {}", err) + ))); } // Already using tls, disable ssf in sasl. @@ -390,11 +391,10 @@ impl ClientIoHandler { ); } if err != SASL_OK { - error!("SASL_FAIL error code {}", err); - return Err(anyhow!(VncError::AuthFailed(format!( - "SASL_FAIL error code {}", - err - )))); + return Err(anyhow!(VncError::AuthFailed( + "set_ssf_for_sasl".to_string(), + format!("SASL_FAIL error code {}", err) + ))); } Ok(()) @@ -425,8 +425,8 @@ impl ClientIoHandler { ); } if err != SASL_OK || mechlist.is_null() { - error!("SASL_FAIL: no support sasl mechlist"); return Err(anyhow!(VncError::AuthFailed( + "send_mech_list".to_string(), "SASL_FAIL: no support sasl mechlist".to_string() ))); } @@ -457,19 +457,19 @@ impl ClientIoHandler { // that security.saslconfig.sasl_conn is not null. unsafe { err = sasl_getprop(security.saslconfig.sasl_conn, SASL_SSF as c_int, &mut val) } if err != SASL_OK { - error!("sasl_getprop: internal error"); - return Err(anyhow!(VncError::AuthFailed(String::from( - "sasl_getprop: internal error" - )))); + return Err(anyhow!(VncError::AuthFailed( + "sasl_check_ssf".to_string(), + "sasl_getprop: internal error".to_string() + ))); } // SAFETY: It can be ensure that the ptr of val is not null. let ssf: usize = unsafe { *(val as *const usize) }; if ssf < MIN_SSF_LENGTH { - error!("SASL SSF too weak"); - return Err(anyhow!(VncError::AuthFailed(String::from( - "SASL SSF too weak" - )))); + return Err(anyhow!(VncError::AuthFailed( + "sasl_check_ssf".to_string(), + "SASL SSF too weak".to_string() + ))); } security.saslconfig.run_ssf = 1; @@ -492,14 +492,16 @@ impl ClientIoHandler { }; drop(security); if err != SASL_OK { - return Err(anyhow!(VncError::AuthFailed(String::from( - "Cannot fetch SASL username" - )))); + return Err(anyhow!(VncError::AuthFailed( + "sasl_check_authz".to_string(), + "Cannot fetch SASL username".to_string() + ))); } if val.is_null() { - return Err(anyhow!(VncError::AuthFailed(String::from( - "No SASL username set" - )))); + return Err(anyhow!(VncError::AuthFailed( + "sasl_check_authz".to_string(), + "No SASL username set".to_string() + ))); } // SAFETY: It can ensure that the pointer val is not null. let username = unsafe { CStr::from_ptr(val as *const c_char) }; @@ -507,20 +509,13 @@ impl ClientIoHandler { let server = self.server.clone(); let security = server.security_type.borrow_mut(); - if let Some(saslauth) = &security.saslauth { - if saslauth.identity != username { - return Err(anyhow!(VncError::AuthFailed(String::from( - "No SASL username set" - )))); - } - } else { - return Err(anyhow!(VncError::AuthFailed(String::from( - "No SASL username set" - )))); + match &security.saslauth { + Some(saslauth) if saslauth.identity == username => Ok(()), + _ => Err(anyhow!(VncError::AuthFailed( + "sasl_check_authz".to_string(), + "No SASL username set".to_string() + ))), } - drop(security); - - Ok(()) } } diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index 32103ea55..e080e766c 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -18,7 +18,7 @@ use crate::{ }, }; use anyhow::{anyhow, Result}; -use log::{error, info}; +use log::info; use rustls::{ self, cipher_suite::{ @@ -27,9 +27,13 @@ use rustls::{ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, }, kx_group::{SECP256R1, SECP384R1, X25519}, - server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth}, + server::{ + AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth, + ServerSessionMemoryCache, + }, version::{TLS12, TLS13}, - RootCertStore, SupportedCipherSuite, SupportedKxGroup, SupportedProtocolVersion, + Certificate, KeyLogFile, PrivateKey, RootCertStore, ServerConfig, ServerConnection, + SupportedCipherSuite, SupportedKxGroup, SupportedProtocolVersion, Ticketer, }; use std::{fs::File, io::BufReader, sync::Arc}; @@ -113,10 +117,10 @@ impl ClientIoHandler { buf.append(&mut (0_u8).to_be_bytes().to_vec()); vnc_write(&client, buf); vnc_flush(&client); - error!("Authentication failed"); - return Err(anyhow!(VncError::AuthFailed(String::from( - "Authentication failed" - )))); + return Err(anyhow!(VncError::AuthFailed( + "client_vencrypt_auth".to_string(), + "sub auth is not supported".to_string() + ))); } let mut buf = Vec::new(); @@ -125,24 +129,15 @@ impl ClientIoHandler { vnc_write(&client, buf); vnc_flush(&client); - if let Some(tls_config) = self.server.security_type.borrow().tls_config.clone() { - match rustls::ServerConnection::new(tls_config) { - Ok(tls_conn) => { - self.tls_conn = Some(tls_conn); - } - Err(e) => { - error!("Can't make ServerConnection: {}", e); - return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "Can't make ServerConnection", - )))); - } - } - } else { - error!("There is no ventrypt configuration!"); - return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "There is no ventrypt configuration!", - )))); - } + let tls_config = self + .server + .security_type + .borrow() + .tls_config + .clone() + .unwrap(); + let tls_conn = ServerConnection::new(tls_config)?; + self.tls_conn = Some(tls_conn); self.client .in_buffer @@ -157,31 +152,42 @@ impl ClientIoHandler { /// Tls handshake. pub fn tls_handshake(&mut self) -> Result<()> { if let Some(tc) = &mut self.tls_conn { - info!("tls_handshake"); match tc.read_tls(&mut self.stream) { Err(err) => { - error!("{:?}", err); - return Err(anyhow!(VncError::AuthFailed(format!("{:?}", err)))); + return Err(anyhow!(VncError::AuthFailed( + "tls_handshake".to_string(), + format!("{:?}", err) + ))); } Ok(0) => { - error!("EOF"); - return Err(anyhow!(VncError::AuthFailed(String::from("EOF")))); + return Err(anyhow!(VncError::AuthFailed( + "tls_handshake".to_string(), + "EOF".to_string() + ))); } Ok(_) => {} } if let Err(err) = tc.process_new_packets() { - error!("Cannot process packet: {:?}", err); let rc = tc.write_tls(&mut self.stream); if rc.is_err() { - return Err(anyhow!(VncError::AuthFailed(format!("{:?}", rc)))); + return Err(anyhow!(VncError::AuthFailed( + "tls_handshake".to_string(), + format!("{:?}", rc) + ))); } - return Err(anyhow!(VncError::AuthFailed(format!("{:?}", err)))); + return Err(anyhow!(VncError::AuthFailed( + "tls_handshake".to_string(), + format!("{:?}", err) + ))); } if tc.wants_write() { if let Err(err) = tc.write_tls(&mut self.stream) { - return Err(anyhow!(VncError::AuthFailed(format!("{:?}", err)))); + return Err(anyhow!(VncError::AuthFailed( + "tls_handshake".to_string(), + format!("{:?}", err) + ))); } } @@ -194,9 +200,10 @@ impl ClientIoHandler { self.handle_vencrypt_subauth()?; } } else { - return Err(anyhow!(VncError::AuthFailed(String::from( - "Handshake failed" - )))); + return Err(anyhow!(VncError::AuthFailed( + "tls_handshake".to_string(), + "Handshake failed".to_string() + ))); } Ok(()) } @@ -228,7 +235,7 @@ impl ClientIoHandler { vnc_write(&client, buf); vnc_flush(&client); } - error!("Unsupported subauth type"); + return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( "Unsupported subauth type", )))); @@ -243,17 +250,14 @@ impl ClientIoHandler { /// # Arguments /// /// * `args` - tls configuration. -pub fn make_vencrypt_config(args: &TlsCreds) -> Result> { +pub fn make_vencrypt_config(args: &TlsCreds) -> Result> { let server_cacert = args.dir.clone() + "/" + TLS_CREDS_SERVER_CACERT; let server_cert = args.dir.clone() + "/" + TLS_CREDS_SERVERCERT; let server_key = args.dir.clone() + "/" + TLS_CREDS_SERVERKEY; // Load cacert.pem and provide verification for certificate chain let client_auth = if args.verifypeer { - let roots = match load_certs(server_cacert.as_str()) { - Ok(r) => r, - Err(e) => return Err(e), - }; + let roots = load_certs(server_cacert.as_str())?; let mut client_auth_roots = RootCertStore::empty(); for root in roots { client_auth_roots.add(&root)?; @@ -272,17 +276,11 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result // Tls protocol version supported by server. let versions = TLS_VERSIONS.to_vec(); // Server certificate. - let certs: Vec = match load_certs(server_cert.as_str()) { - Ok(c) => c, - Err(e) => return Err(e), - }; + let certs: Vec = load_certs(server_cert.as_str())?; // Server private key. - let privkey: rustls::PrivateKey = match load_private_key(server_key.as_str()) { - Ok(key) => key, - Err(e) => return Err(e), - }; + let privkey: PrivateKey = load_private_key(server_key.as_str())?; - let mut config = rustls::ServerConfig::builder() + let mut config = ServerConfig::builder() .with_cipher_suites(&suites) .with_kx_groups(&TLS_KX_GROUPS) .with_protocol_versions(&versions) @@ -292,11 +290,11 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result .expect("Invalid Certificate format"); // SSLKEYLOGFILE=path configure key log path. - config.key_log = Arc::new(rustls::KeyLogFile::new()); + config.key_log = Arc::new(KeyLogFile::new()); // Limit data size in one time. - config.session_storage = rustls::server::ServerSessionMemoryCache::new(MAXIMUM_SESSION_STORAGE); + config.session_storage = ServerSessionMemoryCache::new(MAXIMUM_SESSION_STORAGE); // Tickets. - config.ticketer = rustls::Ticketer::new()?; + config.ticketer = Ticketer::new()?; config.alpn_protocols = Vec::new(); Ok(Arc::new(config)) @@ -307,32 +305,23 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result /// # Arguments /// /// * `filepath` - the path private key. -fn load_private_key(filepath: &str) -> Result { - let file = match File::open(filepath) { - Ok(file) => file, - Err(e) => { - error!("Can not open file of the private key!: {}", e); - return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "File of the private key is no exit!", - )))); - } - }; +fn load_private_key(filepath: &str) -> Result { + let file = File::open(filepath)?; let mut reader = BufReader::new(file); loop { match rustls_pemfile::read_one(&mut reader).expect("Cannot parse .pem file") { - Some(rustls_pemfile::Item::RSAKey(ras)) => return Ok(rustls::PrivateKey(ras)), - Some(rustls_pemfile::Item::PKCS8Key(pkcs8)) => return Ok(rustls::PrivateKey(pkcs8)), - Some(rustls_pemfile::Item::ECKey(ec)) => return Ok(rustls::PrivateKey(ec)), + Some(rustls_pemfile::Item::RSAKey(ras)) => return Ok(PrivateKey(ras)), + Some(rustls_pemfile::Item::PKCS8Key(pkcs8)) => return Ok(PrivateKey(pkcs8)), + Some(rustls_pemfile::Item::ECKey(ec)) => return Ok(PrivateKey(ec)), None => break, _ => {} } } - error!("Load private key failed!"); - Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "Load private key failed!" - )))) + Err(anyhow!(VncError::MakeTlsConnectionFailed( + "Load private key failed!".to_string() + ))) } /// Load certificate. @@ -340,20 +329,12 @@ fn load_private_key(filepath: &str) -> Result { /// # Arguments /// /// * `filepath` - the file path of certificate. -fn load_certs(filepath: &str) -> Result> { - let certfile = match File::open(filepath) { - Ok(file) => file, - Err(e) => { - error!("Cannot open certificate file: {}", e); - return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( - "Cannot open certificate file", - )))); - } - }; +fn load_certs(filepath: &str) -> Result> { + let certfile = File::open(filepath)?; let mut reader = BufReader::new(certfile); let certs = rustls_pemfile::certs(&mut reader)? .iter() - .map(|v| rustls::Certificate(v.clone())) + .map(|v| Certificate(v.clone())) .collect(); Ok(certs) } diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 60d47dd9e..820c7cb5b 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -549,9 +549,7 @@ impl ClientIoHandler { let ver_str = &res[0..12].to_string(); let ver = match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { Ok(v) => v, - Err(e) => { - let msg = format!("Unsupport RFB version: {}", e); - error!("{}", msg); + Err(_e) => { return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); } }; @@ -572,7 +570,6 @@ impl ClientIoHandler { let auth = self.server.security_type.borrow().auth; if self.client.conn_state.lock().unwrap().version.minor == 3 { - error!("Waiting for handle minor=3 ..."); match auth { AuthState::No => { let mut buf = Vec::new(); @@ -582,9 +579,10 @@ impl ClientIoHandler { } _ => { self.auth_failed("Unsupported auth method"); - return Err(anyhow!(VncError::AuthFailed(String::from( - "Unsupported auth method" - )))); + return Err(anyhow!(VncError::AuthFailed( + "handle_version".to_string(), + "Unsupported auth method".to_string() + ))); } } } else { @@ -625,8 +623,7 @@ impl ClientIoHandler { let height = get_image_height(locked_surface.server_image); drop(locked_surface); if !(0..=MAX_IMAGE_SIZE).contains(&width) || !(0..=MAX_IMAGE_SIZE).contains(&height) { - error!("Invalid Image Size!"); - return Err(anyhow!(VncError::InvalidImageSize)); + return Err(anyhow!(VncError::InvalidImageSize(width, height))); } let mut locked_dpm = client.client_dpm.lock().unwrap(); locked_dpm.client_width = width; @@ -654,8 +651,10 @@ impl ClientIoHandler { if buf[0] != auth as u8 { self.auth_failed("Authentication failed"); - error!("handle_auth"); - return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); + return Err(anyhow!(VncError::AuthFailed( + "handle_auth".to_string(), + "auth type is not supported".to_string() + ))); } match auth { @@ -677,8 +676,10 @@ impl ClientIoHandler { } _ => { self.auth_failed("Unhandled auth method"); - error!("handle_auth"); - return Err(anyhow!(VncError::AuthFailed(String::from("handle_auth")))); + return Err(anyhow!(VncError::AuthFailed( + "handle_auth".to_string(), + "auth type is not supported".to_string() + ))); } } vnc_flush(&client); @@ -691,19 +692,21 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); match ClientMsg::from(buf[0]) { ClientMsg::SetPixelFormat => { - return self.set_pixel_format(); + self.set_pixel_format()?; } ClientMsg::SetEncodings => { - return self.set_encodings(); + self.set_encodings()?; } ClientMsg::FramebufferUpdateRequest => { - self.update_frame_buff(); + self.update_frame_buff()?; } ClientMsg::KeyEvent => { - self.key_envent(); + self.key_envent() + .unwrap_or_else(|e| error!("Key event error: {}", e)); } ClientMsg::PointerEvent => { - self.point_event(); + self.point_event() + .unwrap_or_else(|e| error!("Point event error: {}", e)); } ClientMsg::ClientCutText => { self.client_cut_event(); @@ -776,7 +779,6 @@ impl ClientIoHandler { // bit_per_pixel: Bits occupied by each pixel. if ![8, 16, 32].contains(&bit_per_pixel) { self.client.conn_state.lock().unwrap().dis_conn = true; - error!("Worng format of bits_per_pixel"); return Err(anyhow!(VncError::ProtocolMessageFailed(String::from( "set pixel format" )))); @@ -898,7 +900,7 @@ impl ClientIoHandler { drop(locked_dpm); let mut buf: Vec = Vec::new(); // VNC desktop resize. - desktop_resize(&client, &server, &mut buf); + desktop_resize(&client, &server, &mut buf)?; // VNC display cursor define. display_cursor_define(&client, &server, &mut buf); vnc_write(&client, buf); @@ -908,10 +910,10 @@ impl ClientIoHandler { } /// Update image for client. - fn update_frame_buff(&mut self) { + fn update_frame_buff(&mut self) -> Result<()> { if self.expect == 1 { self.expect = 10; - return; + return Ok(()); } let buf = self.read_incoming_msg(); let locked_dpm = self.client.client_dpm.lock().unwrap(); @@ -938,17 +940,18 @@ impl ClientIoHandler { h, width, height, - ); + )?; } drop(locked_state); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + Ok(()) } /// Keyboard event. - pub fn key_envent(&mut self) { + pub fn key_envent(&mut self) -> Result<()> { if self.expect == 1 { self.expect = 8; - return; + return Ok(()); } let buf = self.read_incoming_msg(); let down: bool = buf[1] != 0; @@ -975,23 +978,21 @@ impl ClientIoHandler { && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) { kbd_state.keyboard_state_reset(); - console_select(Some((keycode - KEYCODE_1) as usize)) - .unwrap_or_else(|e| error!("{:?}", e)); + console_select(Some((keycode - KEYCODE_1) as usize))?; } - kbd_state - .keyboard_state_update(keycode, down) - .unwrap_or_else(|e| error!("Key State update error: {:?}", e)); - key_event(keycode, down).unwrap_or_else(|e| error!("Key event error: {:?}", e)); + kbd_state.keyboard_state_update(keycode, down)?; + key_event(keycode, down)?; self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + Ok(()) } // Mouse event. - pub fn point_event(&mut self) { + pub fn point_event(&mut self) -> Result<()> { if self.expect == 1 { self.expect = 6; - return; + return Ok(()); } let buf = self.read_incoming_msg(); @@ -1014,10 +1015,9 @@ impl ClientIoHandler { _ => buf[1], }; - point_event(button_mask as u32, x as u32, y as u32) - .unwrap_or_else(|e| error!("Point event error: {:?}", e)); - + point_event(button_mask as u32, x as u32, y as u32)?; self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); + Ok(()) } /// Client cut text. @@ -1109,7 +1109,8 @@ impl EventNotifierHelper for ClientIoHandler { { client.conn_state.lock().unwrap().dis_conn = true; } else if event & EventSet::IN == EventSet::IN { - if let Err(_e) = locked_client_io.client_handle_read() { + if let Err(e) = locked_client_io.client_handle_read() { + error!("{}", e); client.conn_state.lock().unwrap().dis_conn = true; } } @@ -1185,12 +1186,12 @@ impl EventNotifierHelper for ClientIoHandler { /// Generate the data that needs to be sent. /// Add to send queue -pub fn get_rects(client: &Arc, dirty_num: i32) { +pub fn get_rects(client: &Arc, dirty_num: i32) -> Result<()> { let mut locked_state = client.conn_state.lock().unwrap(); let num = locked_state.dirty_num; locked_state.dirty_num = num.checked_add(dirty_num).unwrap_or(0); if !locked_state.is_need_update() { - return; + return Ok(()); } drop(locked_state); @@ -1224,10 +1225,7 @@ pub fn get_rects(client: &Arc, dirty_num: i32) { } let start = (i * bpl as u64 + x) as usize; let len = (x2 - x) as usize; - if let Err(e) = locked_dirty.clear_range(start, len) { - error!("clear bitmap error: {:?}", e); - return; - } + locked_dirty.clear_range(start, len)?; i += 1; } @@ -1257,6 +1255,7 @@ pub fn get_rects(client: &Arc, dirty_num: i32) { .push(RectInfo::new(client, rects)); client.conn_state.lock().unwrap().clear_update_state(); + Ok(()) } fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> Result<()> { @@ -1279,7 +1278,7 @@ fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> R } /// Set pixformat for client. -pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { +fn pixel_format_message(client: &Arc, buf: &mut Vec) { let mut locked_dpm = client.client_dpm.lock().unwrap(); locked_dpm.pf.init_pixelformat(); let big_endian: u8 = u8::from(cfg!(target_endian = "big")); @@ -1298,15 +1297,18 @@ pub fn pixel_format_message(client: &Arc, buf: &mut Vec) { } /// Set Desktop Size. -pub fn desktop_resize(client: &Arc, server: &Arc, buf: &mut Vec) { +pub fn desktop_resize( + client: &Arc, + server: &Arc, + buf: &mut Vec, +) -> Result<()> { let locked_surface = server.vnc_surface.lock().unwrap(); let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); if !(0..=MAX_IMAGE_SIZE as i32).contains(&width) || !(0..=MAX_IMAGE_SIZE as i32).contains(&height) { - error!("Invalid Image Size!"); - return; + return Err(anyhow!(VncError::InvalidImageSize(width, height))); } drop(locked_surface); let mut locked_dpm = client.client_dpm.lock().unwrap(); @@ -1314,7 +1316,7 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & && !locked_dpm.has_feature(VncFeatures::VncFeatureResize)) || (locked_dpm.client_width == width && locked_dpm.client_height == height) { - return; + return Ok(()); } locked_dpm.client_width = width; locked_dpm.client_height = height; @@ -1324,6 +1326,7 @@ pub fn desktop_resize(client: &Arc, server: &Arc, buf: & buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut (1_u16).to_be_bytes().to_vec()); framebuffer_upadate(0, 0, width, height, ENCODING_DESKTOPRESIZE, buf); + Ok(()) } /// Set color depth for client. diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index f52e5cf8e..56dbc4850 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -41,7 +41,6 @@ use crate::{ }; use anyhow::{anyhow, Result}; use core::time; -use log::error; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, @@ -95,9 +94,9 @@ pub struct VncInterface {} impl DisplayChangeListenerOperations for VncInterface { /// Update guest_image /// Send a resize command to the client based on whether the image size has changed - fn dpy_switch(&self, surface: &DisplaySurface) { + fn dpy_switch(&self, surface: &DisplaySurface) -> Result<()> { if VNC_SERVERS.lock().unwrap().is_empty() { - return; + return Ok(()); } let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); @@ -119,11 +118,11 @@ impl DisplayChangeListenerOperations for VncInterface { guest_height, guest_width, guest_height, - ); - return; + )?; + return Ok(()); } drop(locked_vnc_surface); - update_server_surface(&server); + update_server_surface(&server)?; let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { @@ -133,7 +132,7 @@ impl DisplayChangeListenerOperations for VncInterface { // Set Color depth. set_color_depth(client, &mut buf); // Desktop_resize. - desktop_resize(client, &server, &mut buf); + desktop_resize(client, &server, &mut buf)?; // Cursor define. display_cursor_define(client, &server, &mut buf); vnc_write(client, buf); @@ -147,26 +146,27 @@ impl DisplayChangeListenerOperations for VncInterface { height, guest_width, guest_height, - ); + )?; vnc_update_output_throttle(client); } + Ok(()) } /// Refresh server_image to guest_image. - fn dpy_refresh(&self, dcl: &Arc>) { + fn dpy_refresh(&self, dcl: &Arc>) -> Result<()> { if VNC_SERVERS.lock().unwrap().is_empty() { - return; + return Ok(()); } let server = VNC_SERVERS.lock().unwrap()[0].clone(); if server.client_handlers.lock().unwrap().is_empty() { - return; + return Ok(()); } let con_id = dcl.lock().unwrap().con_id; graphic_hardware_update(con_id); // Update refresh interval. let mut update_interval = dcl.lock().unwrap().update_interval; - let dirty_num = server.vnc_surface.lock().unwrap().update_server_image(); + let dirty_num = server.vnc_surface.lock().unwrap().update_server_image()?; if dirty_num != 0 { update_interval /= 2; if update_interval < DISPLAY_UPDATE_INTERVAL_DEFAULT { @@ -182,13 +182,14 @@ impl DisplayChangeListenerOperations for VncInterface { let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { - get_rects(client, dirty_num); + get_rects(client, dirty_num)?; } + Ok(()) } - fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) { + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) -> Result<()> { if VNC_SERVERS.lock().unwrap().is_empty() { - return; + return Ok(()); } let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); @@ -202,13 +203,14 @@ impl DisplayChangeListenerOperations for VncInterface { h, g_w, g_h, - ); + )?; drop(locked_vnc_surface); + Ok(()) } - fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) { + fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) -> Result<()> { if VNC_SERVERS.lock().unwrap().is_empty() { - return; + return Ok(()); } let server = VNC_SERVERS.lock().unwrap()[0].clone(); let width = cursor.width as u64; @@ -251,6 +253,7 @@ impl DisplayChangeListenerOperations for VncInterface { vnc_write(client, buf); vnc_flush(client); } + Ok(()) } } @@ -270,7 +273,6 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { Ok(l) => l, Err(e) => { let msg = format!("Bind {} failed {}", addr, e); - error!("{}", e); return Err(anyhow!(VncError::TcpBindFailed(msg))); } }; @@ -410,7 +412,7 @@ pub fn set_area_dirty( mut h: i32, g_w: i32, g_h: i32, -) { +) -> Result<()> { let width: i32 = vnc_width(g_w); let height: i32 = vnc_height(g_h); @@ -424,12 +426,10 @@ pub fn set_area_dirty( while y < h { let pos = (y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32) as usize; let len = round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as usize; - if let Err(e) = dirty.set_range(pos, len) { - error!("set bitmap error: {:?}", e); - return; - } + dirty.set_range(pos, len)?; y += 1; } + Ok(()) } /// Get the width of image. @@ -446,14 +446,14 @@ fn vnc_height(height: i32) -> i32 { } /// Update server image -pub fn update_server_surface(server: &Arc) { +pub fn update_server_surface(server: &Arc) -> Result<()> { let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); unref_pixman_image(locked_vnc_surface.server_image); locked_vnc_surface.server_image = ptr::null_mut(); // Server image changes, clear the task queue. VNC_RECT_INFO.lock().unwrap().clear(); if server.client_handlers.lock().unwrap().is_empty() { - return; + return Ok(()); } let g_width = get_image_width(locked_vnc_surface.guest_image); @@ -477,7 +477,7 @@ pub fn update_server_surface(server: &Arc) { height, g_width, g_height, - ); + ) } /// Check if the suface for VncClient is need update diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 5bc3f468e..2ebe57ca4 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -259,7 +259,6 @@ impl SecurityType { } if !is_x509 && !is_anon { - error!("Unsupported tls cred type"); return Err(anyhow!(VncError::MakeTlsConnectionFailed(String::from( "Unsupported tls cred type", )))); @@ -334,7 +333,7 @@ impl VncSurface { /// Flush dirty data from guest_image to server_image. /// Return the number of dirty area. - pub fn update_server_image(&mut self) -> i32 { + pub fn update_server_image(&mut self) -> Result { let mut dirty_num = 0; let height = self.get_min_height() as usize; let g_bpl = self.guest_dirty_bitmap.vol() / MAX_WINDOW_HEIGHT as usize; @@ -345,7 +344,7 @@ impl VncSurface { .unwrap_or(total_dirty_bits); if offset >= total_dirty_bits { - return dirty_num; + return Ok(dirty_num); } let mut s_info = ImageInfo::new(self.server_image); @@ -387,7 +386,7 @@ impl VncSurface { g_info.ptr = (g_info.data as usize + y * g_info.stride as usize) as *mut u8; } g_info.ptr = (g_info.ptr as usize + x * cmp_bytes) as *mut u8; - dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes); + dirty_num += self.update_one_line(x, y, &mut s_info, &mut g_info, cmp_bytes)?; y += 1; offset = self .guest_dirty_bitmap @@ -399,7 +398,7 @@ impl VncSurface { } unref_pixman_image(line_buf); - dirty_num + Ok(dirty_num) } /// Update each line @@ -416,7 +415,7 @@ impl VncSurface { s_info: &mut ImageInfo, g_info: &mut ImageInfo, cmp_bytes: usize, - ) -> i32 { + ) -> Result { let mut count = 0; let width = self.get_min_width(); let line_bytes = cmp::min(s_info.stride, g_info.length); @@ -433,8 +432,7 @@ impl VncSurface { continue; } self.guest_dirty_bitmap - .clear(x + y * VNC_BITMAP_WIDTH as usize) - .unwrap_or_else(|e| error!("Error occurrs during clearing the bitmap: {:?}", e)); + .clear(x + y * VNC_BITMAP_WIDTH as usize)?; let mut _cmp_bytes = cmp_bytes; if (x + 1) * cmp_bytes > line_bytes as usize { _cmp_bytes = line_bytes as usize - x * cmp_bytes; @@ -457,7 +455,7 @@ impl VncSurface { ptr::copy(g_info.ptr, s_info.ptr, _cmp_bytes); }; - set_dirty_for_each_clients(x, y); + set_dirty_for_each_clients(x, y)?; count += 1; x += 1; @@ -465,7 +463,7 @@ impl VncSurface { s_info.ptr = (s_info.ptr as usize + cmp_bytes) as *mut u8; } - count + Ok(count) } } @@ -474,7 +472,7 @@ impl VncSurface { /// # Arguments /// /// * `x` `y`- coordinates of dirty area. -fn set_dirty_for_each_clients(x: usize, y: usize) { +fn set_dirty_for_each_clients(x: usize, y: usize) -> Result<()> { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { @@ -482,9 +480,9 @@ fn set_dirty_for_each_clients(x: usize, y: usize) { .dirty_bitmap .lock() .unwrap() - .set(x + y * VNC_BITMAP_WIDTH as usize) - .unwrap_or_else(|e| error!("{:?}", e)); + .set(x + y * VNC_BITMAP_WIDTH as usize)?; } + Ok(()) } /// Accpet client's connection. @@ -520,8 +518,7 @@ pub fn handle_connection( EventLoop::update_event(EventNotifierHelper::internal_notifiers(client_io), None)?; - update_server_surface(server); - Ok(()) + update_server_surface(server) } /// make configuration for VncServer -- Gitee From c95ff25c6794a01aafa55310bfef8b65dc1673fa Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 01:09:52 +0800 Subject: [PATCH 0875/2187] VNC: move global variable VNC_RECT_INFO to VncServer. VNC_RECT_INFO saves all image regions that need to be updated. It will be sent to vnc_worker thread, and be transfered into byte stream, which will be sent to vnc client in main loop. Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 10 ++++++---- ui/src/vnc/mod.rs | 15 +++++++-------- ui/src/vnc/server_io.rs | 7 ++++++- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 820c7cb5b..537e5d548 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -22,7 +22,7 @@ use crate::{ vnc::{ auth_sasl::AuthState, framebuffer_upadate, round_up_div, server_io::VncServer, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, - MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, VNC_RECT_INFO, + MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, }, }; use anyhow::{anyhow, Result}; @@ -806,7 +806,7 @@ impl ClientIoHandler { self.send_color_map(); } - VNC_RECT_INFO.lock().unwrap().clear(); + self.server.rect_jobs.lock().unwrap().clear(); self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } @@ -1186,7 +1186,7 @@ impl EventNotifierHelper for ClientIoHandler { /// Generate the data that needs to be sent. /// Add to send queue -pub fn get_rects(client: &Arc, dirty_num: i32) -> Result<()> { +pub fn get_rects(client: &Arc, server: &Arc, dirty_num: i32) -> Result<()> { let mut locked_state = client.conn_state.lock().unwrap(); let num = locked_state.dirty_num; locked_state.dirty_num = num.checked_add(dirty_num).unwrap_or(0); @@ -1249,7 +1249,9 @@ pub fn get_rects(client: &Arc, dirty_num: i32) -> Result<()> { } drop(locked_dirty); - VNC_RECT_INFO + + server + .rect_jobs .lock() .unwrap() .push(RectInfo::new(client, rects)); diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 56dbc4850..5489ec938 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -32,7 +32,7 @@ use crate::{ vnc::{ client_io::{ desktop_resize, display_cursor_define, get_rects, set_color_depth, vnc_flush, - vnc_update_output_throttle, vnc_write, DisplayMode, RectInfo, Rectangle, ServerMsg, + vnc_update_output_throttle, vnc_write, DisplayMode, Rectangle, ServerMsg, ENCODING_HEXTILE, ENCODING_RAW, }, encoding::enc_hextile::hextile_send_framebuffer_update, @@ -182,7 +182,7 @@ impl DisplayChangeListenerOperations for VncInterface { let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { - get_rects(client, dirty_num)?; + get_rects(client, &server, dirty_num)?; } Ok(()) } @@ -328,13 +328,14 @@ fn start_vnc_thread() -> Result<()> { let _handle = thread::Builder::new() .name("vnc_worker".to_string()) .spawn(move || loop { - if VNC_RECT_INFO.lock().unwrap().is_empty() { + let rect_jobs = server.rect_jobs.clone(); + if rect_jobs.lock().unwrap().is_empty() { thread::sleep(time::Duration::from_millis(interval)); continue; } let mut rect_info; - match VNC_RECT_INFO.lock().unwrap().get_mut(0) { + match rect_jobs.lock().unwrap().get_mut(0) { Some(rect) => { rect_info = rect.clone(); } @@ -343,7 +344,7 @@ fn start_vnc_thread() -> Result<()> { continue; } } - VNC_RECT_INFO.lock().unwrap().remove(0); + rect_jobs.lock().unwrap().remove(0); let mut num_rects: i32 = 0; let mut buf = Vec::new(); @@ -451,7 +452,7 @@ pub fn update_server_surface(server: &Arc) -> Result<()> { unref_pixman_image(locked_vnc_surface.server_image); locked_vnc_surface.server_image = ptr::null_mut(); // Server image changes, clear the task queue. - VNC_RECT_INFO.lock().unwrap().clear(); + server.rect_jobs.lock().unwrap().clear(); if server.client_handlers.lock().unwrap().is_empty() { return Ok(()); } @@ -680,5 +681,3 @@ fn get_client_image() -> *mut pixman_image_t { } pub static VNC_SERVERS: Lazy>>> = Lazy::new(|| Mutex::new(Vec::new())); -pub static VNC_RECT_INFO: Lazy>>> = - Lazy::new(|| Arc::new(Mutex::new(Vec::new()))); diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 2ebe57ca4..2a810ae51 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -22,7 +22,7 @@ use crate::{ vnc::{ auth_sasl::{AuthState, SaslAuth, SaslConfig, SubAuthState}, auth_vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, - client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState}, + client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState, RectInfo}, round_up_div, update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_BITMAP_WIDTH, VNC_SERVERS, }, @@ -70,6 +70,10 @@ pub struct VncServer { pub vnc_cursor: Arc>, /// Display Change Listener. pub display_listener: Option>>, + /// Saves all image regions that need to be updated. + /// It will be sent to vnc_worker thread, and be transfered into byte stream, + /// which will be sent to vnc client in main loop. + pub rect_jobs: Arc>>, /// Connection limit. pub conn_limits: usize, } @@ -97,6 +101,7 @@ impl VncServer { vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), vnc_cursor: Arc::new(Mutex::new(VncCursor::default())), display_listener, + rect_jobs: Arc::new(Mutex::new(Vec::new())), conn_limits: CONNECTION_LIMIT, } } -- Gitee From f163c3743dcd89c12df84f195f21a8b396cf9291 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 01:38:00 +0800 Subject: [PATCH 0876/2187] VNC: Add weak reference to ClientIoHandler in ConnState. Add weak reference to ClientIoHandler in ConnState. So ClientState can change the call back function for ClientIoHandler. Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 5 ++++- ui/src/vnc/server_io.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 537e5d548..f6055e9b4 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -35,7 +35,7 @@ use std::{ net::{Shutdown, TcpStream}, os::unix::prelude::{AsRawFd, RawFd}, rc::Rc, - sync::{Arc, Mutex}, + sync::{Arc, Mutex, Weak}, }; use util::{ bitmap::Bitmap, @@ -243,6 +243,8 @@ pub struct ConnState { update_state: UpdateState, /// RFB protocol version. pub version: VncVersion, + /// Point to Client Io handler. + pub client_io: Option>>, } impl Default for ConnState { @@ -252,6 +254,7 @@ impl Default for ConnState { dis_conn: false, update_state: UpdateState::No, version: VncVersion::default(), + client_io: None, } } } diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 2a810ae51..fd07af283 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -513,6 +513,7 @@ pub fn handle_connection( client.clone(), server.clone(), ))); + client.conn_state.lock().unwrap().client_io = Some(Arc::downgrade(&client_io)); vnc_write(&client, "RFB 003.008\n".as_bytes().to_vec()); vnc_flush(&client); server -- Gitee From acbc9aa5bf811d93604e6d3d7dc1209cb9c2f23a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 2 Mar 2023 03:28:20 +0800 Subject: [PATCH 0877/2187] virtio-console: Modify virtio console mst testcase Split test_console_basic testcase to console_features_negotiate testcase and console_rw_conifg testcase and add verifing io proces. Rename test_pty_basic and test_socket_basic. Modify test_console_reset testcast to test parellel request. Signed-off-by: Jinhao Gao --- tests/mod_test/tests/console_test.rs | 159 ++++++++++++++++----------- 1 file changed, 95 insertions(+), 64 deletions(-) diff --git a/tests/mod_test/tests/console_test.rs b/tests/mod_test/tests/console_test.rs index b2e9a7a44..3b30f94fd 100644 --- a/tests/mod_test/tests/console_test.rs +++ b/tests/mod_test/tests/console_test.rs @@ -27,13 +27,12 @@ use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::TestState; const TIMEOUT_US: u64 = 15 * 1000 * 1000; -const QUEUE_NUM: u16 = 2; -const QUEUE_SIZE: u16 = 256; const ROWS_DEFAULT: u16 = 0; const COLS_DEFAULT: u16 = 0; const EMERG_WR_DEFAULT: u32 = 0; -const VIRTIO_F_VERSION_1: u32 = 32; const VIRTIO_CONSOLE_F_SIZE: u64 = 0; +const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 1; +const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 2; const BUFFER_LEN: usize = 96; fn console_setup( @@ -88,65 +87,11 @@ fn get_pty_path(test_state: Rc>) -> String { } } -#[test] -fn test_console_basic() { - let chardev = ChardevType::Pty; - let pci_slot = 0x04; - let pci_fn = 0x0; - let (console, test_state, _alloc) = create_console(chardev, pci_slot, pci_fn); - - assert_eq!( - console.borrow().get_queue_nums(), - QUEUE_NUM, - "The virtqueue number of console is uncorrect or the testcase parament is out of date!" - ); - - assert_eq!( - console.borrow().get_queue_size(), - QUEUE_SIZE, - "The virtqueue size of console is uncorrect or the testcase parament is out of date!" - ); - - assert_eq!( - console.borrow().config_readw(0), - ROWS_DEFAULT, - "The rows of the console config is uncorrect or the testcase parament is out of date!" - ); - - assert_eq!( - console.borrow().config_readw(2), - COLS_DEFAULT, - "The cols of the console config is uncorrect or the testcase parament is out of date!" - ); - - assert_eq!( - console.borrow().config_readl(8), - EMERG_WR_DEFAULT, - "The emerg_wr of the console config is uncorrect or the testcase parament is out of date!" - ); - - console.borrow().config_writew(0, 1); - assert_eq!( - console.borrow().config_readw(0), - ROWS_DEFAULT, - "The console device doesn't support writing config. But config was written!" - ); - - assert_eq!( - console.borrow().get_device_features(), - 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_CONSOLE_F_SIZE, - "The feature which the console supports is uncorrect or the testcase parament is out of date!"); - - test_state.borrow_mut().stop(); -} - -#[test] -fn test_pty_basic() { - let pty = ChardevType::Pty; - let pci_slot = 0x04; - let pci_fn = 0x0; - let (console, test_state, alloc) = create_console(pty, pci_slot, pci_fn); - +fn verify_pty_io( + test_state: Rc>, + alloc: Rc>, + console: Rc>, +) { let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); let input_queue = vqs[0].clone(); let output_queue = vqs[1].clone(); @@ -225,11 +170,97 @@ fn test_pty_basic() { } console.borrow_mut().destroy_device(alloc, vqs); +} + +#[test] +fn console_rw_conifg() { + let chardev = ChardevType::Pty; + let pci_slot = 0x04; + let pci_fn = 0x0; + let (console, test_state, alloc) = create_console(chardev, pci_slot, pci_fn); + + assert_eq!( + console.borrow().config_readw(0), + ROWS_DEFAULT, + "The rows of the console config is uncorrect or the testcase parament is out of date!" + ); + + assert_eq!( + console.borrow().config_readw(2), + COLS_DEFAULT, + "The cols of the console config is uncorrect or the testcase parament is out of date!" + ); + + assert_eq!( + console.borrow().config_readl(8), + EMERG_WR_DEFAULT, + "The emerg_wr of the console config is uncorrect or the testcase parament is out of date!" + ); + + console.borrow().config_writew(0, 1); + assert_eq!( + console.borrow().config_readw(0), + ROWS_DEFAULT, + "The console device doesn't support writing config. But config was written!" + ); + + verify_pty_io(test_state.clone(), alloc.clone(), console.clone()); + + test_state.borrow_mut().stop(); +} + +#[test] +fn console_features_negotiate() { + let chardev = ChardevType::Pty; + let pci_slot = 0x04; + let pci_fn = 0x0; + let (console, test_state, alloc) = create_console(chardev, pci_slot, pci_fn); + + let mut features = console.borrow().get_device_features(); + features |= 1 << VIRTIO_CONSOLE_F_SIZE; + console.borrow_mut().negotiate_features(features); + console.borrow_mut().set_features_ok(); + assert_eq!(features, console.borrow_mut().get_guest_features()); + + let unsupported_features = 1 << VIRTIO_CONSOLE_F_MULTIPORT; + features |= unsupported_features; + console.borrow_mut().negotiate_features(features); + console.borrow_mut().set_features_ok(); + assert_ne!(features, console.borrow_mut().get_guest_features()); + assert_eq!( + unsupported_features & console.borrow_mut().get_guest_features(), + 0 + ); + + let unsupported_features = 1 << VIRTIO_CONSOLE_F_EMERG_WRITE; + features |= unsupported_features; + console.borrow_mut().negotiate_features(features); + console.borrow_mut().set_features_ok(); + assert_ne!(features, console.borrow_mut().get_guest_features()); + assert_eq!( + unsupported_features & console.borrow_mut().get_guest_features(), + 0 + ); + + verify_pty_io(test_state.clone(), alloc.clone(), console.clone()); + + test_state.borrow_mut().stop(); +} + +#[test] +fn console_pty_basic() { + let pty = ChardevType::Pty; + let pci_slot = 0x04; + let pci_fn = 0x0; + let (console, test_state, alloc) = create_console(pty, pci_slot, pci_fn); + + verify_pty_io(test_state.clone(), alloc.clone(), console.clone()); + test_state.borrow_mut().stop(); } #[test] -fn test_socket_basic() { +fn console_socket_basic() { let socket_path = "/tmp/test-console0.sock"; if Path::new(socket_path).exists() { fs::remove_file(socket_path).unwrap(); @@ -320,7 +351,7 @@ fn test_socket_basic() { } #[test] -fn test_console_reset() { +fn console_parallel_req() { let socket_path = "/tmp/test-console1.sock"; if Path::new(socket_path).exists() { fs::remove_file(socket_path).unwrap(); -- Gitee From c35f65ff0e40232082e6ff1e4e774b04db7ab118 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 01:57:34 +0800 Subject: [PATCH 0878/2187] VNC: add interface of IoOperations. Abstract tls encryption io and plain io behavior into iochannel. Signed-off-by: Xiao Ye --- ui/src/vnc/auth_vencrypt.rs | 211 +++++++++++++++++++++---------- ui/src/vnc/client_io.rs | 239 ++++++++++++++---------------------- ui/src/vnc/server_io.rs | 4 +- 3 files changed, 243 insertions(+), 211 deletions(-) diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index e080e766c..051b47cfc 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -14,11 +14,12 @@ use crate::{ error::VncError, vnc::{ auth_sasl::SubAuthState, - client_io::{vnc_flush, vnc_write, ClientIoHandler}, + client_io::{vnc_flush, vnc_write, ClientIoHandler, IoOperations}, }, }; -use anyhow::{anyhow, Result}; -use log::info; +use anyhow::{anyhow, bail, Result}; +use log::error; +use machine_manager::event_loop::EventLoop; use rustls::{ self, cipher_suite::{ @@ -35,7 +36,19 @@ use rustls::{ Certificate, KeyLogFile, PrivateKey, RootCertStore, ServerConfig, ServerConnection, SupportedCipherSuite, SupportedKxGroup, SupportedProtocolVersion, Ticketer, }; -use std::{fs::File, io::BufReader, sync::Arc}; +use std::{ + cell::RefCell, + fs::File, + io::{BufReader, ErrorKind, Read, Write}, + net::TcpStream, + os::unix::prelude::{AsRawFd, RawFd}, + rc::Rc, + sync::Arc, +}; +use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; +use vmm_sys_util::epoll::EventSet; + +use super::client_io::vnc_disconnect_start; const TLS_CREDS_SERVER_CACERT: &str = "cacert.pem"; const TLS_CREDS_SERVERCERT: &str = "servercert.pem"; @@ -137,7 +150,50 @@ impl ClientIoHandler { .clone() .unwrap(); let tls_conn = ServerConnection::new(tls_config)?; - self.tls_conn = Some(tls_conn); + let tls_io_channel = Rc::new(RefCell::new(TlsIoChannel::new( + self.stream.try_clone().unwrap(), + tls_conn, + ))); + + let handler: Rc = Rc::new(move |event, _fd: RawFd| { + let mut dis_conn = false; + if event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { + dis_conn = true; + } else if event & EventSet::IN == EventSet::IN { + if let Err(e) = tls_io_channel.borrow_mut().tls_handshake() { + error!("Tls handle shake error: {}", e); + dis_conn = true; + } + } + + if !dis_conn && !tls_io_channel.borrow().tls_conn.is_handshaking() { + let client_io = client.conn_state.lock().unwrap().client_io.clone(); + let client_io = client_io.and_then(|c| c.upgrade()).unwrap(); + let mut locked_client = client_io.lock().unwrap(); + locked_client.io_channel = tls_io_channel.clone(); + if let Err(_e) = locked_client.tls_handshake_done() { + dis_conn = true; + } + } + if dis_conn { + client.conn_state.lock().unwrap().dis_conn = true; + vnc_disconnect_start(&client); + } + None + }); + self.handlers + .insert("vnc_tls_io".to_string(), handler.clone()); + let handlers = vec![handler]; + EventLoop::update_event( + vec![EventNotifier::new( + NotifierOperation::Modify, + self.stream.as_raw_fd(), + None, + EventSet::IN | EventSet::READ_HANG_UP, + handlers, + )], + None, + )?; self.client .in_buffer @@ -145,66 +201,23 @@ impl ClientIoHandler { .unwrap() .remove_front(self.expect); self.expect = 0; - self.msg_handler = ClientIoHandler::tls_handshake; Ok(()) } - /// Tls handshake. - pub fn tls_handshake(&mut self) -> Result<()> { - if let Some(tc) = &mut self.tls_conn { - match tc.read_tls(&mut self.stream) { - Err(err) => { - return Err(anyhow!(VncError::AuthFailed( - "tls_handshake".to_string(), - format!("{:?}", err) - ))); - } - Ok(0) => { - return Err(anyhow!(VncError::AuthFailed( - "tls_handshake".to_string(), - "EOF".to_string() - ))); - } - Ok(_) => {} - } - - if let Err(err) = tc.process_new_packets() { - let rc = tc.write_tls(&mut self.stream); - if rc.is_err() { - return Err(anyhow!(VncError::AuthFailed( - "tls_handshake".to_string(), - format!("{:?}", rc) - ))); - } - return Err(anyhow!(VncError::AuthFailed( - "tls_handshake".to_string(), - format!("{:?}", err) - ))); - } - - if tc.wants_write() { - if let Err(err) = tc.write_tls(&mut self.stream) { - return Err(anyhow!(VncError::AuthFailed( - "tls_handshake".to_string(), - format!("{:?}", err) - ))); - } - } - - if tc.is_handshaking() { - // Tls handshake continue. - self.msg_handler = ClientIoHandler::tls_handshake; - } else { - info!("Finished tls handshaking"); - // Tls handshake finished. - self.handle_vencrypt_subauth()?; - } - } else { - return Err(anyhow!(VncError::AuthFailed( - "tls_handshake".to_string(), - "Handshake failed".to_string() - ))); - } + pub fn tls_handshake_done(&mut self) -> Result<()> { + let handler = self.handlers.get("vnc_client_io").unwrap().clone(); + let handlers = vec![handler]; + EventLoop::update_event( + vec![EventNotifier::new( + NotifierOperation::Modify, + self.stream.as_raw_fd(), + None, + EventSet::IN | EventSet::READ_HANG_UP, + handlers, + )], + None, + )?; + self.handle_vencrypt_subauth()?; Ok(()) } @@ -338,3 +351,77 @@ fn load_certs(filepath: &str) -> Result> { .collect(); Ok(certs) } + +pub struct TlsIoChannel { + /// TcpStream connected with client. + pub stream: TcpStream, + /// Tls server connection. + pub tls_conn: ServerConnection, +} + +impl TlsIoChannel { + pub fn new(stream: TcpStream, tls_conn: ServerConnection) -> Self { + Self { stream, tls_conn } + } + + pub fn tls_handshake(&mut self) -> Result<()> { + if self.tls_conn.read_tls(&mut self.stream)? == 0 { + bail!("Tls hand shake failed: EOF"); + } + self.tls_conn.process_new_packets()?; + if self.tls_conn.wants_write() { + self.tls_conn.write_tls(&mut self.stream)?; + } + Ok(()) + } +} + +impl IoOperations for TlsIoChannel { + fn channel_write(&mut self, buf: &[u8]) -> Result { + let buf_size = buf.len(); + let mut offset = 0; + while offset < buf_size { + let tmp_buf = &buf[offset..]; + match self.tls_conn.writer().write(tmp_buf) { + Ok(0) => { + bail!("Failed to write tls message!"); + } + Ok(n) => offset += n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + bail!("Internal error: {}", e); + } + } + + while self.tls_conn.wants_write() { + match self.tls_conn.write_tls(&mut self.stream) { + Ok(_) => {} + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + continue; + } + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => { + continue; + } + Err(e) => { + bail!("Unable to write msg on tls socket: {:?}", e); + } + } + } + } + + Ok(buf_size) + } + + fn channel_read(&mut self, buf: &mut Vec) -> Result { + let mut len = 0_usize; + self.tls_conn.read_tls(&mut self.stream)?; + + let io_state = self.tls_conn.process_new_packets()?; + if io_state.plaintext_bytes_to_read() > 0 { + len = io_state.plaintext_bytes_to_read(); + buf.resize(len, 0u8); + self.tls_conn.reader().read_exact(buf)?; + } + Ok(len) + } +} diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index f6055e9b4..97c5a67e2 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -25,12 +25,13 @@ use crate::{ MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, }, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use log::error; -use rustls::ServerConnection; use sscanf::scanf; use std::{ + cell::RefCell, cmp, + collections::HashMap, io::{Read, Write}, net::{Shutdown, TcpStream}, os::unix::prelude::{AsRawFd, RawFd}, @@ -48,7 +49,6 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub const APP_NAME: &str = "stratovirt"; const MAX_RECVBUF_LEN: usize = 1024; -const MAX_SEND_LEN: usize = 64 * 1024; const NUM_OF_COLORMAP: u16 = 256; // VNC encodings types. @@ -66,6 +66,13 @@ const ENCODING_DESKTOP_RESIZE_EXT: i32 = -308; pub const ENCODING_ALPHA_CURSOR: i32 = -314; const ENCODING_WMVI: i32 = 1464686185; +/// This trait is used to send bytes, +/// the return is the total number of bytes sented. +pub trait IoOperations { + fn channel_write(&mut self, buf: &[u8]) -> Result; + fn channel_read(&mut self, buf: &mut Vec) -> Result; +} + /// Image display feature. pub enum VncFeatures { VncFeatureResize, @@ -233,6 +240,67 @@ impl Clone for RectInfo { } } +pub struct IoChannel { + stream: TcpStream, +} + +impl IoChannel { + pub fn new(stream: TcpStream) -> Self { + Self { stream } + } +} + +impl IoOperations for IoChannel { + fn channel_write(&mut self, buf: &[u8]) -> Result { + let buf_size = buf.len(); + let mut offset = 0; + while offset < buf_size { + let tmp_buf = &buf[offset..]; + match self.stream.write(tmp_buf) { + Ok(ret) => { + offset += ret; + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + return Ok(offset); + } + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => { + continue; + } + Err(e) => { + bail!("Unable to write msg on socket: {:?}", e); + } + } + } + + Ok(buf_size) + } + + fn channel_read(&mut self, buf: &mut Vec) -> Result { + let mut len = 0_usize; + loop { + let mut bytes = vec![0_u8; MAX_RECVBUF_LEN]; + match self.stream.read(&mut bytes) { + Ok(ret) => { + buf.append(&mut bytes[..ret].to_vec()); + len += ret; + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { + return Ok(len); + } + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => { + continue; + } + Err(e) => { + bail!("Unable to read msg from socket: {:?}", e); + } + } + break; + } + + Ok(len) + } +} + /// The connection state of vnc client. pub struct ConnState { /// Dirty number need to update. @@ -325,6 +393,10 @@ impl ClientState { pub struct ClientIoHandler { /// TcpStream connected with client. pub stream: TcpStream, + /// Io channel to handle read or write. + pub io_channel: Rc>, + /// Vnc client io handler. + pub handlers: HashMap>, /// Tls server connection. pub tls_conn: Option, /// Message handler. @@ -338,9 +410,16 @@ pub struct ClientIoHandler { } impl ClientIoHandler { - pub fn new(stream: TcpStream, client: Arc, server: Arc) -> Self { + pub fn new( + stream: TcpStream, + io_channel: Rc>, + client: Arc, + server: Arc, + ) -> Self { ClientIoHandler { stream, + io_channel, + handlers: HashMap::new(), tls_conn: None, msg_handler: ClientIoHandler::handle_version, expect: 12, @@ -407,141 +486,19 @@ impl ClientIoHandler { /// Read buf from stream, return the size. fn read_msg(&mut self) -> Result { - let mut buf = Vec::new(); - let mut len: usize = 0; - if self.tls_conn.is_none() { - match self.read_plain_msg(&mut buf) { - Ok(n) => len = n, - Err(e) => return Err(e), - } - } - if let Some(tc) = &self.tls_conn { - if tc.is_handshaking() { - return Ok(0_usize); - } - } - if self.tls_conn.is_some() { - match self.read_tls_msg(&mut buf) { - Ok(n) => len = n, - Err(e) => return Err(e), - } - } + let mut buf: Vec = vec![]; + let len = self.io_channel.borrow_mut().channel_read(&mut buf)?; if len > 0 { buf = buf[..len].to_vec(); self.client.in_buffer.lock().unwrap().append_limit(buf); } - - Ok(len) - } - - // Read from vencrypt channel. - fn read_tls_msg(&mut self, buf: &mut Vec) -> Result { - let mut len = 0_usize; - if self.tls_conn.is_none() { - return Ok(0_usize); - } - let tc: &mut ServerConnection = match &mut self.tls_conn { - Some(sc) => sc, - None => return Ok(0_usize), - }; - - if let Err(e) = tc.read_tls(&mut self.stream) { - error!("tls_conn read error {:?}", e); - return Err(anyhow!(VncError::ReadMessageFailed(format!( - "tls_conn read error {:?}", - e - )))); - } - - if let Ok(io_state) = tc.process_new_packets() { - if io_state.plaintext_bytes_to_read() > 0 { - len = io_state.plaintext_bytes_to_read(); - buf.resize(len, 0u8); - if let Err(e) = tc.reader().read_exact(buf) { - error!("tls_conn read error {:?}", e); - buf.clear(); - return Err(anyhow!(VncError::ReadMessageFailed(format!( - "tls_conn read error {:?}", - e - )))); - } - } - } - Ok(len) - } - - /// Read plain txt. - fn read_plain_msg(&mut self, buf: &mut Vec) -> Result { - let mut len = 0_usize; - buf.resize(MAX_RECVBUF_LEN, 0u8); - match self.stream.read(buf) { - Ok(ret) => len = ret, - Err(e) => error!("read msg error: {:?}", e), - } - Ok(len) } /// Write buf to stream /// Choose different channel according to whether or not to encrypt pub fn write_msg(&mut self, buf: &[u8]) -> Result { - if self.tls_conn.is_none() { - self.write_plain_msg(buf) - } else { - self.write_tls_msg(buf) - } - } - - // Send vencrypt message. - fn write_tls_msg(&mut self, buf: &[u8]) -> Result { - let buf_size = buf.len(); - let mut offset = 0; - - let tc: &mut ServerConnection = match &mut self.tls_conn { - Some(ts) => ts, - None => { - return Err(anyhow!(VncError::Disconnection)); - } - }; - - while offset < buf_size { - let next = cmp::min(buf_size, offset + MAX_SEND_LEN); - let tmp_buf = &buf[offset..next].to_vec(); - if let Err(e) = tc.writer().write_all(tmp_buf) { - error!("write msg error: {:?}", e); - return Err(anyhow!(VncError::Disconnection)); - } - if let Err(_e) = vnc_write_tls_message(tc, &mut self.stream) { - self.client.conn_state.lock().unwrap().dis_conn = true; - return Err(anyhow!(VncError::Disconnection)); - } - offset = next; - } - - Ok(buf_size) - } - - /// Send plain txt. - fn write_plain_msg(&mut self, buf: &[u8]) -> Result { - let buf_size = buf.len(); - let mut offset = 0; - while offset < buf_size { - let tmp_buf = &buf[offset..]; - match self.stream.write(tmp_buf) { - Ok(ret) => { - offset += ret; - } - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { - return Ok(offset); - } - Err(e) => { - error!("write msg error: {:?}", e); - return Err(anyhow!(VncError::Disconnection)); - } - } - } - - Ok(buf_size) + self.io_channel.borrow_mut().channel_write(buf) } /// Exchange RFB protocol version with client. @@ -1125,6 +1082,11 @@ impl EventNotifierHelper for ClientIoHandler { None }); let client_io = client_io_handler.clone(); + client_io + .lock() + .unwrap() + .handlers + .insert("vnc_client_io".to_string(), handler.clone()); notifiers.push(EventNotifier::new( NotifierOperation::AddShared, client_io.lock().unwrap().stream.as_raw_fd(), @@ -1263,25 +1225,6 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: Ok(()) } -fn vnc_write_tls_message(tc: &mut ServerConnection, stream: &mut TcpStream) -> Result<()> { - while tc.wants_write() { - match tc.write_tls(stream) { - Ok(_) => {} - Err(e) => { - if e.kind() == std::io::ErrorKind::WouldBlock { - stream.flush().unwrap(); - continue; - } else { - error!("write msg error: {:?}", e); - return Err(anyhow!(VncError::Disconnection)); - } - } - } - } - - Ok(()) -} - /// Set pixformat for client. fn pixel_format_message(client: &Arc, buf: &mut Vec) { let mut locked_dpm = client.client_dpm.lock().unwrap(); diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index fd07af283..0c032241b 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -22,7 +22,7 @@ use crate::{ vnc::{ auth_sasl::{AuthState, SaslAuth, SaslConfig, SubAuthState}, auth_vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, - client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState, RectInfo}, + client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState, IoChannel, RectInfo}, round_up_div, update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_BITMAP_WIDTH, VNC_SERVERS, }, @@ -506,10 +506,12 @@ pub fn handle_connection( .set_nonblocking(true) .expect("set nonblocking failed"); + let io_channel = Rc::new(RefCell::new(IoChannel::new(stream.try_clone().unwrap()))); // Register event notifier for vnc client. let client = Arc::new(ClientState::new(addr.to_string())); let client_io = Arc::new(Mutex::new(ClientIoHandler::new( stream, + io_channel, client.clone(), server.clone(), ))); -- Gitee From 798dcf481414ee97e07b4da9f4c8f4ef97233c18 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 02:35:28 +0800 Subject: [PATCH 0879/2187] VNC: Modify function of set_color_depth It may cause deadlocks when access client_dpm, so clone this data instead of locking, to avoid causing deadlocks in internal functions Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 97c5a67e2..79908a86a 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -1281,12 +1281,13 @@ pub fn desktop_resize( pub fn set_color_depth(client: &Arc, buf: &mut Vec) { let mut locked_dpm = client.client_dpm.lock().unwrap(); if locked_dpm.has_feature(VncFeatures::VncFeatureWmvi) { - let width = client.client_dpm.lock().unwrap().client_width; - let height = client.client_dpm.lock().unwrap().client_height; + let client_width = locked_dpm.client_width; + let client_height = locked_dpm.client_height; + drop(locked_dpm); buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); - buf.append(&mut (0_u8).to_be_bytes().to_vec()); // Padding. - buf.append(&mut (1_u16).to_be_bytes().to_vec()); // Number of pixel block. - framebuffer_upadate(0, 0, width, height, ENCODING_WMVI, buf); + buf.append(&mut (0_u8).to_be_bytes().to_vec()); + buf.append(&mut (1_u16).to_be_bytes().to_vec()); + framebuffer_upadate(0, 0, client_width, client_height, ENCODING_WMVI, buf); buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); pixel_format_message(client, buf); } else if !locked_dpm.pf.is_default_pixel_format() { -- Gitee From 55ac8ad86fa03cb28a52c1939e2bd7fc6ce03412 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 03:53:21 +0800 Subject: [PATCH 0880/2187] VNC: delete some unused function Delete some unused function. Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 9 +-------- ui/src/vnc/server_io.rs | 11 ----------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 79908a86a..9588384b9 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -234,10 +234,6 @@ impl Clone for RectInfo { rects, } } - - fn clone_from(&mut self, source: &Self) { - *self = source.clone() - } } pub struct IoChannel { @@ -1063,10 +1059,7 @@ impl EventNotifierHelper for ClientIoHandler { let handler: Rc = Rc::new(move |event, _fd: RawFd| { let mut locked_client_io = client_io.lock().unwrap(); let client = locked_client_io.client.clone(); - if event & EventSet::ERROR == EventSet::ERROR - || event & EventSet::HANG_UP == EventSet::HANG_UP - || event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP - { + if event & EventSet::READ_HANG_UP == EventSet::READ_HANG_UP { client.conn_state.lock().unwrap().dis_conn = true; } else if event & EventSet::IN == EventSet::IN { if let Err(e) = locked_client_io.client_handle_read() { diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 0c032241b..593aae623 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -163,17 +163,6 @@ pub struct ImageInfo { ptr: *mut u8, } -impl Default for ImageInfo { - fn default() -> Self { - ImageInfo { - data: ptr::null_mut(), - stride: 0, - length: 0, - ptr: ptr::null_mut(), - } - } -} - impl ImageInfo { fn new(image: *mut pixman_image_t) -> Self { let bpp = pixman_format_bpp(get_image_format(image) as u32); -- Gitee From 7866e1bf12e97da956a8b6d564cad14dbc6d159f Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 2 Mar 2023 10:31:53 +0800 Subject: [PATCH 0881/2187] virtio-gpu: add mst testcase Signed-off-by: wubinfeng --- tests/mod_test/tests/virtio_gpu_test.rs | 731 ++++++++++++++++++++++++ 1 file changed, 731 insertions(+) create mode 100644 tests/mod_test/tests/virtio_gpu_test.rs diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs new file mode 100644 index 000000000..1085931e3 --- /dev/null +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -0,0 +1,731 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libdriver::virtio_gpu::{ + current_curosr_check, current_surface_check, get_display_info, get_edid, invalid_cmd_test, + resource_attach_backing, resource_attach_backing_with_invalid_ctx_len, resource_create, + resource_detach_backing, resource_flush, resource_unref, set_scanout, transfer_to_host, + update_cursor, GpuDevConfig, VirtioGpuCtrlHdr, VirtioGpuDisplayInfo, VirtioGpuGetEdid, + VirtioGpuMemEntry, VirtioGpuRect, VirtioGpuResourceAttachBacking, VirtioGpuResourceCreate2d, + VirtioGpuResourceDetachBacking, VirtioGpuResourceFlush, VirtioGpuResourceUnref, + VirtioGpuSetScanout, VirtioGpuTransferToHost2d, +}; +use mod_test::libdriver::virtio_gpu::{set_up, tear_down}; +use std::vec; +use util::byte_code::ByteCode; +use virtio::{ + get_image_hostmem, get_pixman_format, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_FORMAT_INVALID_UNORM, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, + VIRTIO_GPU_RESP_OK_NODATA, +}; + +const D_RES_ID: u32 = 1; +const D_SCANOUT_ID: u32 = 0; +const D_INVALID_SCANOUT_ID: u32 = 100; +const D_FMT: u32 = 2; +const D_INVALD_FMT: u32 = VIRTIO_GPU_FORMAT_INVALID_UNORM; +const D_WIDTH: u32 = 64; +const D_HEIGHT: u32 = 64; +const D_BYTE_PER_PIXEL: u32 = 4; +const D_IMG_SIZE: u32 = D_WIDTH * D_HEIGHT * D_BYTE_PER_PIXEL; +const D_OFFSET: u64 = 0; +const D_X_COORD: u32 = 0; +const D_Y_COORD: u32 = 0; +const D_INVALD_NR_ENTRIES: u32 = 1 + 16384; + +#[test] +fn image_display_fun() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + let image_byte_0 = vec![0 as u8; 1]; + let image_byte_1 = vec![1 as u8; 1]; + let image_0 = vec![0 as u8; image_size as usize]; + + // image with half data 1 + let mut image_half_1 = vec![0 as u8; image_size as usize]; + let mut i = 0; + while i < image_size / 2 { + image_half_1[i as usize] = 1; + i += 1; + } + // image with quarter data1 + let mut image_quarter_1 = vec![0 as u8; image_size as usize]; + let mut i = 0; + while i < image_size / 4 { + image_quarter_1[i as usize] = 1; + i += 1; + } + + assert_eq!( + VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + get_display_info(&gpu).header.hdr_type + ); + + assert_eq!( + VIRTIO_GPU_RESP_OK_EDID, + get_edid(&gpu, VirtioGpuGetEdid::new(D_SCANOUT_ID)) + .header + .hdr_type + ); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + gpu.borrow_mut() + .state + .borrow_mut() + .memset(image_addr, image_size, &image_byte_0); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1), + vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] + ) + .hdr_type + ); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + transfer_to_host( + &gpu, + VirtioGpuTransferToHost2d::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_OFFSET, + D_RES_ID, + ), + ) + .hdr_type + ); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + set_scanout( + &gpu, + VirtioGpuSetScanout::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_SCANOUT_ID, + D_RES_ID, + ) + ) + .hdr_type + ); + assert!(current_surface_check(&dpy, &image_0)); + + // update image, half of image change to 1 + gpu.borrow_mut() + .state + .borrow_mut() + .memset(image_addr, image_size / 2, &image_byte_1); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + transfer_to_host( + &gpu, + VirtioGpuTransferToHost2d::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_OFFSET, + D_RES_ID, + ), + ) + .hdr_type + ); + + // But we only flush quarter of the image. So check the image is quarter 1 or not. + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_flush( + &gpu, + VirtioGpuResourceFlush::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT / 4), + D_RES_ID + ) + ) + .hdr_type + ); + assert!(current_surface_check(&dpy, &image_quarter_1)); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_detach_backing(&gpu, VirtioGpuResourceDetachBacking::new(D_RES_ID),).hdr_type + ); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_unref(&gpu, VirtioGpuResourceUnref::new(D_RES_ID)).hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn cursor_display_fun() { + let image_0: Vec = vec![0 as u8; D_IMG_SIZE as usize]; + let image_1: Vec = vec![1 as u8; D_IMG_SIZE as usize]; + let image_byte_1 = vec![1 as u8; 1]; + + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + + let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // init data is all 0 + update_cursor(&gpu, D_RES_ID, D_SCANOUT_ID); + assert!(current_curosr_check(&dpy, &image_0)); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1), + vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] + ) + .hdr_type + ); + + // update image to 1 + gpu.borrow_mut() + .state + .borrow_mut() + .memset(image_addr, image_size, &image_byte_1); + + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + transfer_to_host( + &gpu, + VirtioGpuTransferToHost2d::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_OFFSET, + D_RES_ID, + ), + ) + .hdr_type + ); + + // now resource data is all 1 + update_cursor(&gpu, D_RES_ID, D_SCANOUT_ID); + assert!(current_curosr_check(&dpy, &image_1)); + + tear_down(dpy, gpu); +} + +#[test] +fn resource_create_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + + // exceed max_hostmem + assert_eq!( + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH + 1, D_HEIGHT) + ) + .hdr_type + ); + + // invalid format + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_INVALD_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // invalid resource id 0 + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(0, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // resource id exist + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT / 2) + ) + .hdr_type + ); + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT / 2) + ) + .hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn resource_destroy_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + let (dpy, gpu) = set_up(&gpu_cfg); + + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + resource_unref(&gpu, VirtioGpuResourceUnref::new(D_RES_ID)).hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn resource_attach_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // resource is invalid yet + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1), + vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] + ) + .hdr_type + ); + + // create resource first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // invalid nr_entries + assert_eq!( + VIRTIO_GPU_RESP_ERR_UNSPEC, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, D_INVALD_NR_ENTRIES), + vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] + ) + .hdr_type + ); + + // invalid context length + assert_eq!( + VIRTIO_GPU_RESP_ERR_UNSPEC, + resource_attach_backing_with_invalid_ctx_len( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1) + ) + .hdr_type + ); + + // invalid context address + assert_eq!( + VIRTIO_GPU_RESP_ERR_UNSPEC, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1), + vec![VirtioGpuMemEntry::new(0, image_size as u32)] + ) + .hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn resource_detach_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // invlid resource id + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + resource_detach_backing(&gpu, VirtioGpuResourceDetachBacking::new(D_RES_ID),).hdr_type + ); + + // create resource first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // invlid resource id + assert_eq!( + VIRTIO_GPU_RESP_ERR_UNSPEC, + resource_detach_backing(&gpu, VirtioGpuResourceDetachBacking::new(D_RES_ID),).hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn resource_transfer_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // invlid resource id + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + transfer_to_host( + &gpu, + VirtioGpuTransferToHost2d::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_OFFSET, + D_RES_ID, + ), + ) + .hdr_type + ); + + // create resource first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // have not attach any data source + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + transfer_to_host( + &gpu, + VirtioGpuTransferToHost2d::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_OFFSET, + D_RES_ID, + ), + ) + .hdr_type + ); + + // attach first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1), + vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] + ) + .hdr_type + ); + + // invalid rect region + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + transfer_to_host( + &gpu, + VirtioGpuTransferToHost2d::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH + 1, D_HEIGHT - 1), + D_OFFSET, + D_RES_ID, + ), + ) + .hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn scanout_set_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // invalid scanout id + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + set_scanout( + &gpu, + VirtioGpuSetScanout::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_INVALID_SCANOUT_ID, + D_RES_ID, + ) + ) + .hdr_type + ); + + // invalid resource id + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + set_scanout( + &gpu, + VirtioGpuSetScanout::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_SCANOUT_ID, + D_RES_ID, + ) + ) + .hdr_type + ); + + // create resource first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // invalid rect region + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + set_scanout( + &gpu, + VirtioGpuSetScanout::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH + 1, D_HEIGHT), + D_SCANOUT_ID, + D_RES_ID, + ) + ) + .hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn scanout_flush_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // invalid resource id + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + resource_flush( + &gpu, + VirtioGpuResourceFlush::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + D_RES_ID + ) + ) + .hdr_type + ); + + // create resource first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + + // invalid rect region + assert_eq!( + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + resource_flush( + &gpu, + VirtioGpuResourceFlush::new( + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH + 1, D_HEIGHT), + D_RES_ID + ) + ) + .hdr_type + ); + + tear_down(dpy, gpu); +} + +#[test] +fn cursor_update_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + let image_empty: Vec = vec![]; + let image_0: Vec = vec![0 as u8; D_IMG_SIZE as usize]; + + // invalid scanout id + assert!(current_curosr_check(&dpy, &image_empty)); + update_cursor(&gpu, D_RES_ID, D_INVALID_SCANOUT_ID); + assert!(current_curosr_check(&dpy, &image_empty)); + + // invalid resource id + update_cursor(&gpu, D_RES_ID, D_SCANOUT_ID); + assert!(current_curosr_check(&dpy, &image_0)); + + // create resource which have invalid width + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH / 2, D_HEIGHT) + ) + .hdr_type + ); + // invalid rect region even resource is exist + update_cursor(&gpu, D_RES_ID, D_SCANOUT_ID); + assert!(current_curosr_check(&dpy, &image_0)); + + tear_down(dpy, gpu); +} + +#[test] +fn invalid_cmd_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // invalid cmd + assert_eq!(VIRTIO_GPU_RESP_ERR_UNSPEC, invalid_cmd_test(&gpu).hdr_type); + + tear_down(dpy, gpu); +} + +#[test] +fn crash_dfx() { + let pixman_format = get_pixman_format(D_FMT).unwrap(); + let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + + let mut gpu_cfg = GpuDevConfig::default(); + gpu_cfg.max_hostmem = image_size; + + let (dpy, gpu) = set_up(&gpu_cfg); + gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + + // invalid request header length + let mut hdr = VirtioGpuCtrlHdr::default(); + hdr.hdr_type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; + + let mut resp = VirtioGpuDisplayInfo::default(); + resp.header.hdr_type = 0x1234; // will not change because req has been ignored + + let temp = hdr.as_bytes(); + let slice = &temp[4..]; + gpu.borrow_mut() + .request_complete(true, slice, None, None, Some(&mut resp)); + assert_eq!(0x1234, resp.header.hdr_type); + + // invlid hdr_ctx + let mut hdr = VirtioGpuCtrlHdr::default(); + hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; + + let hdr_ctx = VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT); + + let mut resp = VirtioGpuCtrlHdr::default(); + + let temp = hdr_ctx.as_bytes(); + let slice = &temp[4..]; + + gpu.borrow_mut() + .request_complete(true, hdr.as_bytes(), Some(slice), None, Some(&mut resp)); + assert_eq!(VIRTIO_GPU_RESP_ERR_UNSPEC, resp.hdr_type); + + // invalid resp can work + let mut hdr = VirtioGpuCtrlHdr::default(); + hdr.hdr_type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; + + let mut resp = VirtioGpuCtrlHdr::default(); + + gpu.borrow_mut() + .request_complete(true, hdr.as_bytes(), None, None, Some(&mut resp)); + assert_eq!(VIRTIO_GPU_RESP_OK_DISPLAY_INFO, resp.hdr_type); + + tear_down(dpy, gpu); +} -- Gitee From a63096b03da3bf67e426b634620933a6c7555272 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 19:21:39 +0800 Subject: [PATCH 0882/2187] UI: resove memory leak in console When closing the console, it is necessary to free the memory of the image. Signed-off-by: Xiao Ye --- ui/src/console.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index e2c684400..853080726 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -538,7 +538,12 @@ pub fn console_close(console: &Option>>) -> Result<() Some(c) => c, None => return Ok(()), }; - let con_id = con.lock().unwrap().con_id; + let locked_con = con.lock().unwrap(); + let con_id = locked_con.con_id; + if let Some(surface) = locked_con.surface { + unref_pixman_image(surface.image); + } + drop(locked_con); let mut locked_consoles = CONSOLES.lock().unwrap(); if con_id.is_none() { return Ok(()); -- Gitee From a77168f07eaa809a53a6bfb497d4037f8e60063d Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 2 Mar 2023 19:47:43 +0800 Subject: [PATCH 0883/2187] VNC: add a testcase Add a testcase that the vnc client is configured with all the features, and VNC server changes the desktop size. The client can receive the desktop resize message. --- tests/mod_test/tests/vnc_test.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index a5b7546fc..b1a4e6410 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -300,6 +300,8 @@ fn test_send_cursor_image() { /// 3. Demo GPU replace the surface of VNC server. -> expect 1. /// 4. VNC client setting feature of Raw. /// 5. Demo GPU replace the surface of VNC server. -> expect 2. +/// 6. VNC client setting feature of All. +/// 7. Demo GPU replace the surface of VNC server. -> expect 2. /// ExpectOutput /// 1. VNC client received a desktop resize request from VNC server. /// 2. VNC client not received any desktop resize request from VNC server. @@ -352,6 +354,17 @@ fn test_desktop_resize() { EncodingType::EncodingDesktopresize ))); + assert!(vnc_client.test_setup_encodings(None, None).is_ok()); + demo_gpu + .borrow_mut() + .replace_surface(1920, 1080, PIXMAN_A8B8G8R8); + let pf = RfbPixelFormat::new(8, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); + let res = vnc_client.test_recv_server_data(pf); + assert!(res.unwrap().contains(&( + RfbServerMsg::FramebufferUpdate, + EncodingType::EncodingDesktopresize + ))); + assert!(vnc_client.disconnect().is_ok()); tear_down(gpu_list, input, test_state); } -- Gitee From 75a4c0c4e5a477455fcdf5d6a5982277bb833f89 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Thu, 2 Mar 2023 13:04:07 +0000 Subject: [PATCH 0884/2187] bugfix: add repr for transport data in virtio-gpu Add repr(c) to unify the the order, size, and alignment of fields. Signed-off-by: wubinfeng --- virtio/src/gpu.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index d5c53615b..a5ad4fea8 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -119,6 +119,7 @@ struct VirtioGpuRect { impl ByteCode for VirtioGpuRect {} +#[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayOne { rect: VirtioGpuRect, @@ -128,6 +129,7 @@ struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} +#[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, @@ -143,6 +145,7 @@ struct VirtioGpuGetEdid { } impl ByteCode for VirtioGpuGetEdid {} +#[repr(C)] #[allow(unused)] // data which transfer to frontend need padding #[derive(Clone, Copy)] -- Gitee From 8e323ab7cdeaa84df350b0b388b2e0692b334ae5 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Fri, 3 Mar 2023 23:03:05 +0800 Subject: [PATCH 0885/2187] virtio-gpu: fix inappropriate assumptions Fix code may cause problem by assume front-end driven data layout. Signed-off-by: wubinfeng --- tests/mod_test/src/libdriver/virtio_gpu.rs | 3 --- tests/mod_test/tests/virtio_gpu_test.rs | 10 ---------- virtio/src/gpu.rs | 3 --- 3 files changed, 16 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index fa0f40bdc..6d42abd33 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -68,7 +68,6 @@ impl VirtioGpuRect { impl ByteCode for VirtioGpuRect {} -#[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuDisplayOne { pub rect: VirtioGpuRect, @@ -78,7 +77,6 @@ pub struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} -#[repr(C)] #[allow(unused)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuDisplayInfo { @@ -105,7 +103,6 @@ impl VirtioGpuGetEdid { impl ByteCode for VirtioGpuGetEdid {} -#[repr(C)] #[allow(unused)] #[derive(Clone, Copy)] pub struct VirtioGpuRespEdid { diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index 1085931e3..37702f7a0 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -717,15 +717,5 @@ fn crash_dfx() { .request_complete(true, hdr.as_bytes(), Some(slice), None, Some(&mut resp)); assert_eq!(VIRTIO_GPU_RESP_ERR_UNSPEC, resp.hdr_type); - // invalid resp can work - let mut hdr = VirtioGpuCtrlHdr::default(); - hdr.hdr_type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; - - let mut resp = VirtioGpuCtrlHdr::default(); - - gpu.borrow_mut() - .request_complete(true, hdr.as_bytes(), None, None, Some(&mut resp)); - assert_eq!(VIRTIO_GPU_RESP_OK_DISPLAY_INFO, resp.hdr_type); - tear_down(dpy, gpu); } diff --git a/virtio/src/gpu.rs b/virtio/src/gpu.rs index a5ad4fea8..d5c53615b 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/gpu.rs @@ -119,7 +119,6 @@ struct VirtioGpuRect { impl ByteCode for VirtioGpuRect {} -#[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayOne { rect: VirtioGpuRect, @@ -129,7 +128,6 @@ struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} -#[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, @@ -145,7 +143,6 @@ struct VirtioGpuGetEdid { } impl ByteCode for VirtioGpuGetEdid {} -#[repr(C)] #[allow(unused)] // data which transfer to frontend need padding #[derive(Clone, Copy)] -- Gitee From 665e36153e977bda73e9b5b6ba9cb09394a8b47c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 4 Mar 2023 15:27:28 +0800 Subject: [PATCH 0886/2187] virtio: fix an error in iov_discard_front() Fix 7046485f "virtio-net: fix some problems found by mst" It should not modify the condition "iov.len > size", which will lead an empty iovec when handling block io request. It may cause some unknown error. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index cb735ac7e..b747949b9 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -484,7 +484,7 @@ pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) /// Discard "size" bytes of the front of iovec. pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> { for (index, iov) in iovec.iter_mut().enumerate() { - if iov.len as u64 >= size { + if iov.len as u64 > size { iov.addr.0 += size; iov.len -= size as u32; return Some(&mut iovec[index..]); -- Gitee From 0b989261bf76f4413ea804c6760a9e631f7c7772 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 4 Mar 2023 17:24:31 +0800 Subject: [PATCH 0887/2187] virtio: modify some error branched 1. delete some untriggered error check. 2. optimize and fix the overflow judgement. Signed-off-by: Yan Wang --- virtio/src/virtqueue/mod.rs | 11 ++-------- virtio/src/virtqueue/split.rs | 40 +++++++++++------------------------ 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/virtqueue/mod.rs index 4014a3133..5f58f7a39 100644 --- a/virtio/src/virtqueue/mod.rs +++ b/virtio/src/virtqueue/mod.rs @@ -13,11 +13,10 @@ mod split; use address_space::{AddressSpace, GuestAddress, RegionCache}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{bail, Result}; use std::sync::Arc; use vmm_sys_util::eventfd::EventFd; -use crate::VirtioError; pub use split::*; /// Split Virtqueue. @@ -46,13 +45,7 @@ fn checked_offset_mem( offset ); } - base.checked_add(offset).ok_or_else(|| { - anyhow!(VirtioError::AddressOverflow( - "queue", - base.raw_value(), - offset - )) - }) + Ok(base.unchecked_add(offset)) } /// IO vector element which contains the information of a descriptor. diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/virtqueue/split.rs index 8840b8829..564fd0401 100644 --- a/virtio/src/virtqueue/split.rs +++ b/virtio/src/virtqueue/split.rs @@ -221,11 +221,14 @@ impl SplitVringDesc { if let Some(reg_cache) = cache { let base = self.addr.0; let offset = self.len as u64; - if base > reg_cache.start && base + offset < reg_cache.end { - if base.checked_add(offset).is_none() { + let end = match base.checked_add(offset) { + Some(addr) => addr, + None => { error!("The memory of descriptor is invalid, range overflows"); return false; } + }; + if base > reg_cache.start && end < reg_cache.end { miss_cached = false; } } else { @@ -337,10 +340,7 @@ impl SplitVringDesc { } desc_table_host = sys_mem .get_host_address_from_cache(desc.addr, cache) - .unwrap_or(0); - if desc_table_host == 0 { - bail!("Failed to get descriptor table entry host address"); - }; + .ok_or_else(|| anyhow!("Failed to get descriptor table entry host address"))?; queue_size = desc.get_desc_num(); desc = Self::next_desc(sys_mem, desc_table_host, queue_size, 0, cache)?; desc_size = elem @@ -518,17 +518,9 @@ impl SplitVring { VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.actual_size()); // Make sure the event idx read from sys_mem is new. fence(Ordering::SeqCst); - let used_event_addr = self - .addr_cache - .avail_ring_host - .checked_add(used_event_offset) - .with_context(|| { - anyhow!(VirtioError::AddressOverflow( - "getting used event idx", - self.avail_ring.raw_value(), - used_event_offset, - )) - })?; + // The GPA of avail_ring_host with avail table lenth has been checked in + // is_invalid_memory which must not be overflowed. + let used_event_addr = self.addr_cache.avail_ring_host + used_event_offset; let used_event = sys_mem .read_object_direct::(used_event_addr) .with_context(|| { @@ -687,17 +679,9 @@ impl SplitVring { ) -> Result<()> { let index_offset = VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.next_avail.0 % self.actual_size()); - let desc_index_addr = self - .addr_cache - .avail_ring_host - .checked_add(index_offset) - .with_context(|| { - anyhow!(VirtioError::AddressOverflow( - "popping avail ring", - self.avail_ring.raw_value(), - index_offset, - )) - })?; + // The GPA of avail_ring_host with avail table lenth has been checked in + // is_invalid_memory which must not be overflowed. + let desc_index_addr = self.addr_cache.avail_ring_host + index_offset; let desc_index = sys_mem .read_object_direct::(desc_index_addr) .with_context(|| { -- Gitee From 0619bf97291a9513a97a58af791ac4f223ba6fef Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 4 Mar 2023 17:42:42 +0800 Subject: [PATCH 0888/2187] tests/virtio: add two testcase desc 1. Test overflow of desc[1]->addr + desc[1]->len. 2. Test invalid lengh of the indirect desc elem. Signed-off-by: Yan Wang --- tests/mod_test/src/libdriver/virtio.rs | 2 +- tests/mod_test/tests/virtio_test.rs | 42 ++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index b9e4d4423..22a4afc57 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -151,7 +151,7 @@ pub trait VirtioDeviceOps { #[derive(Default, Copy, Clone, Debug)] pub struct VringDesc { addr: u64, - len: u32, + pub len: u32, pub flags: u16, next: u16, } diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 535070af7..8d030edcf 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -1402,8 +1402,10 @@ fn virtio_io_abnormal_desc_addr() { /// 1) 0 with 1 request 3 desc elems; /// 2) 0x5000 with 1 request 3 desc elems; /// 3) u32::MAX with 1 request 3 desc elems; -/// 4) total length of all desc is bigger than (1 << 32): +/// 4) u32::MAX with 2 request to test overflow; +/// 5) total length of all desc is bigger than (1 << 32): /// ((1 << 32) / 64) with indirect request which has 65 desc elems; +/// 6) test the invalid length of the indirect desc. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. /// Expect: @@ -1416,7 +1418,9 @@ fn virtio_io_abnormal_desc_len() { (0, 1, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), (0x5000, 1, VIRTIO_BLK_S_IOERR, 0), (u32::MAX, 1, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (u32::MAX, 2, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), (1 << 26, 65, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), + (16, 65, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), ]; for (length, io_num, ack, device_status) in reqs { let (blk, test_state, alloc, image_path) = set_up(); @@ -1428,7 +1432,7 @@ fn virtio_io_abnormal_desc_len() { 1, ); - let req_addr: u64; + let mut req_addr: u64 = 0; if io_num <= 1 { (_, req_addr) = add_request( test_state.clone(), @@ -1437,7 +1441,27 @@ fn virtio_io_abnormal_desc_len() { VIRTIO_BLK_T_OUT, 0, ); - test_state.borrow().writel(vqs[0].borrow().desc + 8, length); + test_state.borrow().writel( + vqs[0].borrow().desc + offset_of!(VringDesc, len) as u64, + length, + ); + } else if io_num == 2 { + // Io request 1 is valid, used to create cache of desc[0]->addr. + // Io request 2 is invalid, test overflow for desc[1]->addr + desc[1]->len. + for i in 0..2 { + (_, req_addr) = add_request( + test_state.clone(), + alloc.clone(), + vqs[0].clone(), + VIRTIO_BLK_T_OUT, + i as u64, + ); + } + let req_1_addr = vqs[0].borrow().desc + VRING_DESC_SIZE; + test_state.borrow().writeq(req_1_addr, u64::MAX); + test_state + .borrow() + .writel(req_1_addr + offset_of!(VringDesc, len) as u64, length); } else { let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); blk_req.data.push_str("TEST"); @@ -1447,11 +1471,23 @@ fn virtio_io_abnormal_desc_len() { for _ in 0..io_num { indirect_req.add_desc(test_state.clone(), req_addr, length, true); } + let indirect_desc = indirect_req.desc; let free_head = vqs[0] .borrow_mut() .add_indirect(test_state.clone(), indirect_req, true); vqs[0].borrow().update_avail(test_state.clone(), free_head); + // Test invalid lengh of the indirect desc elem. + if length == 16 { + test_state.borrow().writel( + indirect_desc + offset_of!(VringDesc, len) as u64, + u16::MAX as u32 * (VRING_DESC_SIZE as u32 + 1), + ); + test_state.borrow().writel( + indirect_desc + offset_of!(VringDesc, flags) as u64, + (VRING_DESC_F_INDIRECT | VRING_DESC_F_NEXT) as u32, + ); + } } blk.borrow().virtqueue_notify(vqs[0].clone()); -- Gitee From 7a9df2ffd45c2e6c6baaeb3b67b35e4600923422 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 6 Mar 2023 14:37:14 +0800 Subject: [PATCH 0889/2187] MST: add virtio-fs mst Add virtio-fs mst. Signed-off-by: liuxiangdong Signed-off-by: lihuachao Signed-off-by: jiewangqun Signed-off-by: wangyan --- tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/virtiofs.rs | 827 +++++++++ tests/mod_test/tests/virtiofs_test.rs | 2044 ++++++++++++++++++++++ 3 files changed, 2872 insertions(+) create mode 100644 tests/mod_test/src/libdriver/virtiofs.rs create mode 100644 tests/mod_test/tests/virtiofs_test.rs diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 76356df82..613cd8ce6 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -22,4 +22,5 @@ pub mod virtio_console; pub mod virtio_gpu; pub mod virtio_pci_modern; pub mod virtio_rng; +pub mod virtiofs; pub mod vnc; diff --git a/tests/mod_test/src/libdriver/virtiofs.rs b/tests/mod_test/src/libdriver/virtiofs.rs new file mode 100644 index 000000000..c6a95462e --- /dev/null +++ b/tests/mod_test/src/libdriver/virtiofs.rs @@ -0,0 +1,827 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use util::byte_code::ByteCode; + +pub const FUSE_LOOKUP: u32 = 1; +pub const FUSE_FORGET: u32 = 2; +pub const FUSE_GETATTR: u32 = 3; +pub const FUSE_SETATTR: u32 = 4; +pub const FUSE_READLINK: u32 = 5; +pub const FUSE_SYMLINK: u32 = 6; +pub const FUSE_MKNOD: u32 = 8; +pub const FUSE_MKDIR: u32 = 9; +pub const FUSE_UNLINK: u32 = 10; +pub const FUSE_RMDIR: u32 = 11; +pub const FUSE_RENAME: u32 = 12; +pub const FUSE_LINK: u32 = 13; +pub const FUSE_OPEN: u32 = 14; +pub const FUSE_READ: u32 = 15; +pub const FUSE_WRITE: u32 = 16; +pub const FUSE_STATFS: u32 = 17; +pub const FUSE_RELEASE: u32 = 18; +pub const FUSE_FSYNC: u32 = 20; +pub const FUSE_SETXATTR: u32 = 21; +pub const FUSE_GETXATTR: u32 = 22; +pub const FUSE_LISTXATTR: u32 = 23; +pub const FUSE_REMOVEXATTR: u32 = 24; +pub const FUSE_FLUSH: u32 = 25; +pub const FUSE_INIT: u32 = 26; +pub const FUSE_OPENDIR: u32 = 27; +pub const FUSE_READDIR: u32 = 28; +pub const FUSE_RELEASEDIR: u32 = 29; +pub const FUSE_FSYNCDIR: u32 = 30; +pub const FUSE_GETLK: u32 = 31; +pub const FUSE_SETLK: u32 = 32; +pub const FUSE_SETLKW: u32 = 33; +pub const FUSE_ACCESS: u32 = 34; +pub const FUSE_CREATE: u32 = 35; +pub const FUSE_INTERRUPT: u32 = 36; +pub const FUSE_BMAP: u32 = 37; +pub const FUSE_DESTROY: u32 = 38; +pub const FUSE_IOCTL: u32 = 39; +pub const FUSE_POLL: u32 = 40; +pub const FUSE_NOTIFY_REPLY: u32 = 41; +pub const FUSE_BATCH_FORGET: u32 = 42; +pub const FUSE_FALLOCATE: u32 = 43; +pub const FUSE_READDIRPLUS: u32 = 44; +pub const FUSE_RENAME2: u32 = 45; +pub const FUSE_LSEEK: u32 = 46; +pub const FUSE_COPY_FILE_RANGE: u32 = 47; +pub const FUSE_SETUPMAPPING: u32 = 48; +pub const FUSE_REMOVEMAPPING: u32 = 49; + +/// The kernel version which is supported by fuse messages. +pub const FUSE_KERNEL_VERSION: u32 = 7; +/// The minor version which is supported by fuse messages. +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; + +/// The supported bit that supports asynchronous read requests. +pub const FUSE_ASYNC_READ: u32 = 1 << 0; +/// The supported bit that supports posix file locks. +pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; +/// The supported bit that supports the O_TRUNC open flag. +pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; +/// The supported bit that supports lookups of "." and "..". +pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; +/// The supported bit that don't apply umask to file mode on create operation. +pub const FUSE_DONT_MASK: u32 = 1 << 6; +/// The supported bit that supports BSD file locks. +pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; +/// The supported bit that automatically checks invalid cached file. +pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; +/// The supported bit that supports readdirplus. +pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; +/// The supported bit that supports adaptive readdirplus. +pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; +/// The supported bit that supports asynchronous direct I/O submission. +pub const FUSE_ASYNC_DIO: u32 = 1 << 15; +/// The supported bit that supports for parallel directory operations. +pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; +/// The supported bit that supports POSIX ACLs. +pub const FUSE_POSIX_ACL: u32 = 1 << 20; +/// The supported bit that needs to reply the max number of pages in init fuse message. +pub const FUSE_MAX_PAGES: u32 = 1 << 22; + +pub const FATTR_MODE: u32 = 1 << 0; +pub const FATTR_UID: u32 = 1 << 1; +pub const FATTR_GID: u32 = 1 << 2; +pub const FATTR_SIZE: u32 = 1 << 3; +pub const FATTR_ATIME: u32 = 1 << 4; +pub const FATTR_MTIME: u32 = 1 << 5; +pub const FATTR_FH: u32 = 1 << 6; +pub const FATTR_ATIME_NOW: u32 = 1 << 7; +pub const FATTR_MTIME_NOW: u32 = 1 << 8; +pub const FATTR_LOCKOWNER: u32 = 1 << 9; +pub const FATTR_CTIME: u32 = 1 << 10; + +/// Successfully process the fuse message. +pub const FUSE_OK: i32 = 0; +pub const FUSE_SET_ATTR_MODE: u32 = 1 << 0; +pub const FUSE_SET_ATTR_UID: u32 = 1 << 1; +pub const FUSE_SET_ATTR_GID: u32 = 1 << 2; +pub const FUSE_SET_ATTR_SIZE: u32 = 1 << 3; +pub const FUSE_SET_ATTR_ATIME: u32 = 1 << 4; +pub const FUSE_SET_ATTR_MTIME: u32 = 1 << 5; +pub const FUSE_SET_ATTR_ATIME_NOW: u32 = 1 << 7; +pub const FUSE_SET_ATTR_MTIME_NOW: u32 = 1 << 8; +pub const FUSE_SET_ATTR_CTIME: u32 = 1 << 10; + +pub const XATTR_CREATE: u32 = 0x1; // set value, fail if attr already exists +pub const XATTR_REPLACE: u32 = 0x2; // set value, fail if attr does not exist + +pub const TEST_MAX_READAHEAD: u32 = 1048576; + +pub const TEST_FLAG: u32 = FUSE_ASYNC_READ + | FUSE_POSIX_LOCKS + | FUSE_ATOMIC_O_TRUNC + | FUSE_EXPORT_SUPPORT + | FUSE_DONT_MASK + | FUSE_FLOCK_LOCKS + | FUSE_AUTO_INVAL_DATA + | FUSE_DO_READDIRPLUS + | FUSE_READDIRPLUS_AUTO + | FUSE_ASYNC_DIO + | FUSE_PARALLEL_DIROPS + | FUSE_POSIX_ACL + | FUSE_MAX_PAGES; + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInHeader { + pub len: u32, + pub opcode: u32, + pub unique: u64, + pub nodeid: u64, + pub uid: u32, + pub gid: u32, + pub pid: u32, + pub padding: u32, +} + +impl FuseInHeader { + pub fn new( + len: u32, + opcode: u32, + unique: u64, + nodeid: u64, + uid: u32, + gid: u32, + pid: u32, + padding: u32, + ) -> FuseInHeader { + FuseInHeader { + len, + opcode, + unique, + nodeid, + uid, + gid, + pid, + padding, + } + } +} + +impl ByteCode for FuseInHeader {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOutHeader { + pub len: u32, + pub error: i32, + pub unique: u64, +} + +impl ByteCode for FuseOutHeader {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseAttr { + pub ino: u64, + pub size: u64, + pub blocks: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub nlink: u32, + pub uid: u32, + pub gid: u32, + pub rdev: u32, + pub blksize: u32, + pub flags: u32, +} + +impl ByteCode for FuseAttr {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseEntryOut { + pub nodeid: u64, + pub generation: u64, + pub entry_valid: u64, + pub attr_valid: u64, + pub entry_valid_nsec: u32, + pub attr_valid_nsec: u32, + pub attr: FuseAttr, +} + +impl ByteCode for FuseEntryOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetIn { + pub nlookup: u64, +} + +impl ByteCode for FuseForgetIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetOut { + pub dummy: u64, +} + +impl ByteCode for FuseForgetOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseAttrOut { + pub attr_valid: u64, + pub attr_valid_nsec: u32, + pub dummy: u32, + pub attr: FuseAttr, +} + +impl ByteCode for FuseAttrOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseGetAttrIn { + pub getattr_flags: u32, + pub dummy: u32, + pub fh: u64, +} + +impl ByteCode for FuseGetAttrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseSetattrIn { + pub valid: u32, + pub padding: u32, + pub fh: u64, + pub size: u64, + pub lock_owner: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub unused4: u32, + pub uid: u32, + pub gid: u32, + pub unused5: u32, +} + +impl ByteCode for FuseSetattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseMknodIn { + pub mode: u32, + pub rdev: u32, + pub umask: u32, + pub padding: u32, + pub name: String, +} + +impl FuseMknodIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.mode.as_bytes().to_vec()); + bytes.append(&mut self.rdev.as_bytes().to_vec()); + bytes.append(&mut self.umask.as_bytes().to_vec()); + bytes.append(&mut self.padding.as_bytes().to_vec()); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseRenameIn { + pub newdir: u64, + pub oldname: String, + pub newname: String, +} + +impl FuseRenameIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.newdir.as_bytes().to_vec()); + bytes.append(&mut self.oldname.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes.append(&mut self.newname.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseLinkIn { + pub oldnodeid: u64, + pub newname: String, +} + +impl FuseLinkIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.oldnodeid.as_bytes().to_vec()); + bytes.append(&mut self.newname.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOpenIn { + pub flags: u32, + pub unused: u32, +} + +impl ByteCode for FuseOpenIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOpenOut { + pub fh: u64, + pub open_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseOpenOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseReadIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub read_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseReadIn {} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseWriteIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub write_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, + pub write_buf: String, +} + +impl FuseWriteIn { + pub fn new(fh: u64, offset: u64, write_buf: String) -> Self { + FuseWriteIn { + fh, + offset, + size: (write_buf.len() + 1) as u32, + write_flags: 0_u32, + lock_owner: 0_u64, + flags: O_WRONLY, + padding: 0, + write_buf, + } + } + + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.fh.as_bytes().to_vec()); + bytes.append(&mut self.offset.as_bytes().to_vec()); + bytes.append(&mut self.size.as_bytes().to_vec()); + bytes.append(&mut self.write_flags.as_bytes().to_vec()); + bytes.append(&mut self.lock_owner.as_bytes().to_vec()); + bytes.append(&mut self.flags.as_bytes().to_vec()); + bytes.append(&mut self.padding.as_bytes().to_vec()); + bytes.append(&mut self.write_buf.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseWriteOut { + pub size: u32, + pub padding: u32, +} + +impl ByteCode for FuseWriteOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseKstatfs { + pub blocks: u64, + pub bfree: u64, + pub bavail: u64, + pub files: u64, + pub ffree: u64, + pub bsize: u32, + pub namelen: u32, + pub frsize: u32, + pub padding: u32, + pub spare: [u32; 6], +} + +impl ByteCode for FuseKstatfs {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseStatfsOut { + pub st: FuseKstatfs, +} + +impl ByteCode for FuseStatfsOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseReleaseIn { + pub fh: u64, + pub flags: u32, + pub release_flags: u32, + pub lock_owner: u64, +} + +impl ByteCode for FuseReleaseIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFsyncIn { + pub fh: u64, + pub fsync_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseFsyncIn {} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseSetxattrIn { + pub size: u32, + pub flags: u32, + pub name: String, + pub value: String, +} + +impl FuseSetxattrIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.size.as_bytes().to_vec()); + bytes.append(&mut self.flags.as_bytes().to_vec()); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes.append(&mut self.value.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseGetxattrIn { + pub size: u32, + pub padding: u32, + pub name: String, +} + +impl FuseGetxattrIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.size.as_bytes().to_vec()); + bytes.append(&mut self.padding.as_bytes().to_vec()); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +pub struct FuseRemoveXattrIn { + pub name: String, +} + +impl FuseRemoveXattrIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInitIn { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, +} + +impl ByteCode for FuseInitIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInitOut { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, + pub max_background: u16, + pub congestion_threshold: u16, + pub max_write: u32, + pub time_gran: u32, + pub max_pages: u16, + pub map_alignment: u16, + pub unused: [u32; 8], +} + +impl ByteCode for FuseInitOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseDirent { + pub ino: u64, + pub off: u64, + pub namelen: u32, + pub type_: u32, + pub name: [u8; 0], +} + +impl ByteCode for FuseDirent {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseDirentplus { + pub entry_out: FuseEntryOut, + pub dirent: FuseDirent, +} + +impl ByteCode for FuseDirentplus {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFlushIn { + pub fh: u64, + pub unused: u32, + pub padding: u32, + pub lock_owner: u64, +} + +impl ByteCode for FuseFlushIn {} + +pub const F_RDLCK: u32 = 0; +pub const F_WRLCK: u32 = 1; +pub const F_UNLCK: u32 = 2; + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFileLock { + pub start: u64, + pub end: u64, + pub lock_type: u32, + pub pid: u32, +} + +impl ByteCode for FuseFileLock {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLkIn { + pub fh: u64, + pub owner: u64, + pub lk: FuseFileLock, + pub lk_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseLkIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLkOut { + pub lk: FuseFileLock, +} + +impl ByteCode for FuseLkOut {} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseCreateIn { + pub flags: u32, + pub mode: u32, + pub umask: u32, + pub padding: u32, + pub name: String, +} + +impl FuseCreateIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.flags.as_bytes().to_vec()); + bytes.append(&mut self.mode.as_bytes().to_vec()); + bytes.append(&mut self.umask.as_bytes().to_vec()); + bytes.append(&mut self.padding.as_bytes().to_vec()); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseCreateOut { + pub create_out: FuseEntryOut, + pub open_out: FuseOpenOut, +} + +impl ByteCode for FuseCreateOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseBatchForgetIn { + pub count: u32, + pub dummy: u32, +} + +impl ByteCode for FuseBatchForgetIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetDataIn { + pub ino: u64, + pub nlookup: u64, +} + +impl ByteCode for FuseForgetDataIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFallocateIn { + pub fh: u64, + pub offset: u64, + pub length: u64, + pub mode: u32, + pub padding: u32, +} + +impl ByteCode for FuseFallocateIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLseekIn { + pub fh: u64, + pub offset: u64, + pub whence: u32, + pub padding: u32, +} + +impl ByteCode for FuseLseekIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLseekOut { + pub offset: u64, +} + +impl ByteCode for FuseLseekOut {} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseLookupIn { + pub name: String, +} + +impl FuseLookupIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +pub struct FuseUnlinkrIn { + pub name: String, +} + +impl FuseUnlinkrIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct FusesysmlinkIn { + pub name: String, + pub linkname: String, +} + +impl FusesysmlinkIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes.append(&mut self.linkname.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +#[repr(C)] +#[derive(Debug, Default, Clone)] +pub struct FuseMkdirIn { + pub mode: u32, + pub umask: u32, + pub name: String, +} + +impl FuseMkdirIn { + pub fn as_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.append(&mut self.mode.as_bytes().to_vec()); + bytes.append(&mut self.umask.as_bytes().to_vec()); + bytes.append(&mut self.name.as_bytes().to_vec()); + bytes.append(&mut vec![0]); + bytes + } +} + +pub enum SeccompAction { + Allow, + Kill, + Log, + Trap, +} + +impl std::fmt::Display for SeccompAction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + SeccompAction::Allow => "allow", + SeccompAction::Kill => "kill", + SeccompAction::Log => "log", + SeccompAction::Trap => "trap", + } + ) + } +} + +pub enum SandBoxMechanism { + Chroot, + Namespace, +} + +impl std::fmt::Display for SandBoxMechanism { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + SandBoxMechanism::Chroot => "chroot", + SandBoxMechanism::Namespace => "namespace", + } + ) + } +} + +// Read only. +pub const O_RDONLY: u32 = 0o000000; +// Write only. +pub const O_WRONLY: u32 = 0o000001; +// Read-Write. +pub const O_RDWR: u32 = 0o000002; +pub const O_CREAT: u32 = 0o000100; +pub const O_TRUNC: u32 = 0o001000; +pub const O_NONBLOCK: u32 = 0o004000; +// Direct disk access hint. +pub const O_DIRECT: u32 = 0o040000; +// Don't follow links. +pub const O_NOFOLLOW: u32 = 0o400000; + +// lseek. +pub const SEEK_SET: u32 = 0; +pub const SEEK_CUR: u32 = 1; +pub const SEEK_END: u32 = 2; diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs new file mode 100644 index 000000000..9e41fe8da --- /dev/null +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -0,0 +1,2044 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +use std::{ + cell::RefCell, env, mem::size_of, path::Path, process::Command, rc::Rc, slice::from_raw_parts, +}; + +use util::byte_code::ByteCode; +use util::offset_of; + +use mod_test::libdriver::{ + machine::TestStdMachine, + malloc::GuestAllocator, + virtio::{ + TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VIRTIO_F_BAD_FEATURE, + VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, + }, + virtio_pci_modern::TestVirtioPciDev, + virtiofs::*, +}; +use mod_test::libtest::{test_init, TestState}; +use mod_test::utils::get_rand_str; + +const DEFAULT_FS_DESC_ELEM: usize = 4; // 4 elems: inheader/inbody/outheader/outbody. +const TIMEOUT_US: u64 = 10 * 1000 * 1000; // 10s timeout. +const PARENT_NODEID: u64 = 1; // parent dir nodeid. +const TEST_MEM_SIZE: u64 = 1024; // 1G mem size. +const TEST_PAGE_SIZE: u64 = 4096; //4k page size. +const TEST_FILE_NAME: &str = "testfile"; +const TEST_CHARDEV_NAME: &str = "testchar"; +const DEFAULT_READ_SIZE: usize = 1024; // 1024 Bytes. +const DEFAULT_XATTR_SIZE: u32 = 1024; // 1024 Bytes. +const MAX_TAG_LENGTH: usize = 36; // Tag buffer's max length in pci device config. + +#[derive(Copy, Clone)] +#[repr(C, packed)] +struct VirtioFsConfig { + tag: [u8; MAX_TAG_LENGTH], + num_request_queues: u32, +} + +struct VirtioFsTest { + device: Rc>, + state: Rc>, + allocator: Rc>, + queues: Vec>>, +} + +fn env_prepare(temp: bool) -> (String, String, String) { + let rng_name: String = get_rand_str(8); + + let dir = if temp { "/tmp" } else { "/var" }; + let virtiofs_test_dir = format!("{}/mst-virtiofs-{}", dir, rng_name); + let virtiofs_shared_dir = format!("{}/{}", virtiofs_test_dir, "shared"); + let virtiofs_test_file = format!("{}/{}", virtiofs_shared_dir, TEST_FILE_NAME); + let virtiofs_test_character_device = format!("{}/{}", virtiofs_shared_dir, TEST_CHARDEV_NAME); + + Command::new("mkdir") + .arg("-p") + .arg(virtiofs_shared_dir.clone()) + .output() + .unwrap(); + + Command::new("touch") + .arg(virtiofs_test_file.clone()) + .output() + .unwrap(); + + Command::new("mknod") + .arg(virtiofs_test_character_device.clone()) + .arg("c") + .arg("1") + .arg("1") + .output() + .unwrap(); + + let output = Command::new("dd") + .arg("if=/dev/zero") + .arg(format!("of={}", virtiofs_test_file)) + .arg("bs=1M") + .arg("count=10") + .output() + .unwrap(); + assert!(output.status.success()); + + (virtiofs_test_dir, virtiofs_shared_dir, virtiofs_test_file) +} + +fn env_clean(test_dir: String) { + Command::new("rm").arg("-rf").arg(test_dir).spawn().unwrap(); +} + +fn virtio_fs_defalut_feature(dev: Rc>) -> u64 { + let mut features = dev.borrow().get_device_features(); + features &= + !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX); + + features +} + +impl VirtioFsTest { + fn new(memsize: u64, page_size: u64, virtiofs_sock: String) -> Self { + let tag = "myfs"; + let pci_slot: u8 = 0x4; + let pci_fn: u8 = 0x0; + + let mut args = "-D -machine virt,mem-share=on".to_string(); + let mem_args = format!(" -m {}", memsize); + args.push_str(&mem_args); + let chardev_args = format!( + " -chardev socket,id=virtio_fs,path={},server,nowait", + virtiofs_sock + ); + args.push_str(&chardev_args); + let virtiofs_pci_args = format!( + " -device vhost-user-fs-pci,id=device_id,chardev=virtio_fs,tag={},bus=pcie.0,addr=0x4", + tag, + ); + args.push_str(&virtiofs_pci_args); + + let args_vec: Vec<&str> = args.trim().split(' ').collect(); + let test_state = Rc::new(RefCell::new(test_init(args_vec))); + let machine = + TestStdMachine::new_bymem(test_state.clone(), memsize * 1024 * 1024, page_size); + let allocator = machine.allocator.clone(); + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + dev.borrow_mut().init(pci_slot, pci_fn); + let features = virtio_fs_defalut_feature(dev.clone()); + let queues = + dev.borrow_mut() + .init_device(test_state.clone(), allocator.clone(), features, 2); + + VirtioFsTest { + device: dev, + state: test_state, + allocator: allocator, + queues, + } + } + + fn handle_request_member( + &self, + reqmember: Option<&[u8]>, + data_entries: &mut Vec, + is_write: bool, + ) -> Option { + if let Some(member) = reqmember { + let member_size = member.len() as u64; + let member_addr = self + .allocator + .borrow_mut() + .alloc(member_size) + .try_into() + .unwrap(); + self.state.borrow().memwrite(member_addr, &member); + data_entries.push(TestVringDescEntry { + data: member_addr, + len: member_size as u32, + write: is_write, + }); + + return Some(member_addr); + } + + None + } + + fn do_virtio_request( + &self, + fuseinheader: Option<&[u8]>, + fuseinbody: Option<&[u8]>, + fuseoutheader: Option<&[u8]>, + fuseoutbody: Option<&[u8]>, + ) -> (Option, Option, Option, Option) { + let mut data_entries: Vec = Vec::with_capacity(DEFAULT_FS_DESC_ELEM); + + // FuseInHeader. + let fuseinheader_addr = self.handle_request_member(fuseinheader, &mut data_entries, false); + // FuseInBody. + let fuseinbody_addr = self.handle_request_member(fuseinbody, &mut data_entries, false); + // FuseOutHeader. + let fuseoutheader_addr = self.handle_request_member(fuseoutheader, &mut data_entries, true); + // FuseOutbody. + let fuseoutbody_addr = self.handle_request_member(fuseoutbody, &mut data_entries, true); + + let free_head = self.queues[1] + .clone() + .borrow_mut() + .add_chained(self.state.clone(), data_entries); + + // Kick. + self.device + .borrow_mut() + .kick_virtqueue(self.state.clone(), self.queues[1].clone()); + + // Wait for response. + self.wait_for_response(free_head); + + ( + fuseinheader_addr, + fuseinbody_addr, + fuseoutheader_addr, + fuseoutbody_addr, + ) + } + + fn virtiofs_do_virtio_request( + &self, + fuseinheader: &[u8], + fuseinbody: &[u8], + fuseoutheader: &[u8], + fuseoutbody: &[u8], + ) -> (u64, u64) { + let (_, _, fuseoutheader_addr, fuseoutbody_addr) = self.do_virtio_request( + Some(fuseinheader), + Some(fuseinbody), + Some(fuseoutheader), + Some(fuseoutbody), + ); + + (fuseoutheader_addr.unwrap(), fuseoutbody_addr.unwrap()) + } + + fn vhost_user_fs_start_with_config( + dir_temp: bool, + seccomp: Option, + sandbox: Option, + modcaps: Option<&str>, + rlimit_nofile: Option, + logfile: bool, + ) -> (String, String, String) { + let binary_path = env::var("VHOST_USER_FS_BINARY").unwrap(); + let (virtiofs_test_dir, virtiofs_shared_dir, virtiofs_test_file) = env_prepare(dir_temp); + + let absolute_virtiofs_sock = format!("{}/virtiofs.sock", virtiofs_shared_dir); + + // Use shared directory as root directory when sandbox is enabled. + let virtiofs_sock = if sandbox.is_some() { + "/virtiofs.sock".to_string() + } else { + absolute_virtiofs_sock.clone() + }; + + let mut args = "-D".to_string(); + if logfile { + let filepath = format!(" {}/fs.log", virtiofs_test_dir); + args.push_str(&filepath); + } + if seccomp.is_some() { + let seccomp_args = format!(" -seccomp {}", seccomp.unwrap()); + args.push_str(&seccomp_args); + } + if sandbox.is_some() { + let sandbox_args = format!(" -sandbox {}", sandbox.unwrap()); + args.push_str(&sandbox_args); + } + if modcaps.is_some() { + let modcaps_args = format!(" {}", modcaps.unwrap()); + args.push_str(&modcaps_args); + } + if rlimit_nofile.is_some() { + let rlimit_args = format!(" -rlimit-nofile {}", rlimit_nofile.unwrap()); + args.push_str(&rlimit_args); + } + + let args_vec: Vec<&str> = args.trim().split(' ').collect(); + + Command::new(binary_path) + .arg("-source") + .arg(virtiofs_shared_dir) + .arg("-socket-path") + .arg(virtiofs_sock.clone()) + .args(args_vec) + .spawn() + .unwrap(); + + // Wait totally 10s for that the vhost user fs socket is being created. + let path = absolute_virtiofs_sock.clone(); + let sock_path = Path::new(&path); + for _ in 0..100 { + if sock_path.exists() { + break; + } + // Query at 0.1s interval. + std::thread::sleep(std::time::Duration::from_millis(100)); + } + + ( + virtiofs_test_dir, + absolute_virtiofs_sock, + virtiofs_test_file, + ) + } + + fn vhost_user_fs_start() -> (String, String, String) { + VirtioFsTest::vhost_user_fs_start_with_config(true, None, None, None, None, false) + } + + fn testcase_end(&self, test_dir: String) { + self.testcase_check_and_end(None, test_dir.clone()); + } + + fn testcase_check_and_end(&self, absolute_virtiofs_sock: Option, test_dir: String) { + self.device + .borrow_mut() + .destroy_device(self.allocator.clone(), self.queues.clone()); + + if let Some(path) = absolute_virtiofs_sock { + let path_clone = path.clone(); + let sock_path = Path::new(&path_clone); + assert_eq!(sock_path.exists(), true); + self.state.borrow_mut().stop(); + assert_eq!(sock_path.exists(), false); + } else { + self.state.borrow_mut().stop(); + } + + env_clean(test_dir); + } + + fn wait_for_response(&self, free_head: u32) { + self.device.borrow().poll_used_elem( + self.state.clone(), + self.queues[1].clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + } +} + +fn read_obj(test_state: Rc>, read_addr: u64) -> T { + let read_len = size_of::() as u64; + let read_bytes = test_state.borrow().memread(read_addr, read_len); + let slice = unsafe { from_raw_parts(read_bytes.as_ptr() as *const T, size_of::()) }; + slice[0].clone() +} + +fn fuse_init(fs: &VirtioFsTest) -> (FuseOutHeader, FuseInitOut) { + let len = size_of::() + size_of::(); + let fuse_in_head = FuseInHeader::new(len as u32, FUSE_INIT, 0, 0, 0, 0, 0, 0); + let fuse_init_in = FuseInitIn { + major: FUSE_KERNEL_VERSION, + minor: FUSE_KERNEL_MINOR_VERSION, + max_readahead: TEST_MAX_READAHEAD, + flags: TEST_FLAG, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_init_out = FuseInitOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_init_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_init_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + let init_out = read_obj::(fs.state.clone(), outbodyaddr); + + (out_header, init_out) +} + +fn fuse_destroy(fs: &VirtioFsTest) -> FuseOutHeader { + let len = size_of::(); + let fuse_in_head = FuseInHeader::new(len as u32, FUSE_DESTROY, 0, 0, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheaderaddr, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + None, + Some(&fuse_out_head.as_bytes()), + None, + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr.unwrap()); + + out_header +} + +fn fuse_lookup(fs: &VirtioFsTest, name: String) -> u64 { + // The reason why add "1" is that there exists "\0" after string. + let len = (size_of::() + name.len() + 1) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_LOOKUP, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_lookup_in = FuseLookupIn { name }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_lookup_out = FuseEntryOut::default(); + let (_outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_lookup_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_lookup_out.as_bytes(), + ); + + let entry_out = read_obj::(fs.state.clone(), outbodyaddr); + + entry_out.nodeid +} + +fn fuse_open(fs: &VirtioFsTest, nodeid: u64) -> u64 { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_OPEN, 0, nodeid, 0, 0, 0, 0); + let fuse_open_in = FuseOpenIn { + flags: O_RDWR, + unused: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_open_out = FuseOpenOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_open_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_open_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + let openout = read_obj::(fs.state.clone(), outbodyaddr); + + openout.fh +} + +fn fuse_open_dir(fs: &VirtioFsTest, nodeid: u64) -> u64 { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_OPENDIR, 0, nodeid, 0, 0, 0, 0); + let fuse_open_in = FuseOpenIn { + flags: 0, + unused: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_open_out = FuseOpenOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_open_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_open_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + let openout = read_obj::(fs.state.clone(), outbodyaddr); + + openout.fh +} + +fn fuse_lseek( + fs: &VirtioFsTest, + nodeid: u64, + fh: u64, + trim: usize, +) -> (FuseOutHeader, FuseLseekOut) { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_LSEEK, 0, nodeid, 0, 0, 0, 0); + let fuse_lseek_in = FuseLseekIn { + fh, + offset: 0, + whence: SEEK_END, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_lseek_out = FuseLseekOut::default(); + let lseek_in_len = fuse_lseek_in.as_bytes().len(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_lseek_in.as_bytes()[0..lseek_in_len - trim], + &fuse_out_head.as_bytes(), + &fuse_lseek_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + let lseekout = read_obj::(fs.state.clone(), outbodyaddr); + + (out_header, lseekout) +} + +fn fuse_getattr(fs: &VirtioFsTest, nodeid: u64, fh: u64) -> (FuseOutHeader, FuseAttrOut) { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_GETATTR, 0, nodeid, 0, 0, 0, 0); + let fuse_getattr_in = FuseGetAttrIn { + getattr_flags: 0, + dummy: 0, + fh, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_getattr_out = FuseAttrOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_getattr_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_getattr_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + let attrout = read_obj::(fs.state.clone(), outbodyaddr); + + (out_header, attrout) +} + +// Test situation: mount -t virtiofs myfs /mnt. +#[test] +fn mount_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start_with_config( + true, + Some(SeccompAction::Kill), + Some(SandBoxMechanism::Namespace), + Some("--modcaps=-LEASE,+KILL"), + None, + false, + ); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock.clone()); + + // basic function test. + fuse_init(&fs); + + // kill process and clean env. + fs.testcase_check_and_end(Some(virtiofs_sock), virtiofs_test_dir); +} + +// Test situation: umount /mnt. +#[test] +fn umount_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start_with_config( + true, + Some(SeccompAction::Allow), + Some(SandBoxMechanism::Chroot), + Some("--modcaps=-LEASE,+KILL"), + None, + false, + ); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // mount. + fuse_init(&fs); + + // unmout and check. + let resp = fuse_destroy(&fs); + assert_eq!(resp.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +/// Test: mkdir /mnt/dir +#[test] +fn mkdir_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start_with_config( + true, + Some(SeccompAction::Log), + None, + None, + Some(4096), // Test rlimit_nofile config. -rlimit_nofile 4096. + true, + ); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + // do request. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_MKDIR, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_mkdir_in = FuseMkdirIn { + mode: 0o777, // Directory right: 777. + umask: 0, + name: String::from("dir"), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_mkdir_out = FuseEntryOut::default(); + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_mkdir_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_mkdir_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + let mut linkpath = virtiofs_test_dir.clone(); + linkpath.push_str("/shared/dir"); + let linkpath_clone = linkpath.clone(); + let link_path = Path::new(&linkpath_clone); + assert_eq!(link_path.is_dir(), true); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +/// Test: sync /mnt/testfile. +#[test] +fn sync_fun() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start_with_config( + true, + Some(SeccompAction::Trap), + None, + None, + None, + false, + ); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + fuse_init(&fs); + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + let fh = fuse_open(&fs, nodeid); + + // sync file. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_FSYNC, 0, nodeid, 0, 0, 0, 0); + let fuse_fallocate_in = FuseFsyncIn { + fh, + fsync_flags: 0, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_fallocate_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +/// Test: sync /mnt +#[test] +fn syncdir_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + let fh = fuse_open_dir(&fs, PARENT_NODEID); + + // sync directory. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_FSYNCDIR, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_fallocate_in = FuseFsyncIn { + fh, + fsync_flags: 0, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_fallocate_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn invalid_fuse_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // generate fake fuse request and send it. + let fake_fuse_in_body = [0]; + let fake_len = (size_of::() + fake_fuse_in_body.len()) as u32; + let fake_ops = 50; // No such fuse command. + let fuse_in_head = FuseInHeader::new(fake_len, fake_ops, 0, 0, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let fake_fuse_out_body = [0]; + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fake_fuse_in_body, + &fuse_out_head.as_bytes(), + &fake_fuse_out_body, + ); + + // Check returned error. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert!(out_header.error != 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn missing_fuseinbody_fuseoutbody_virtiorequest_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // generate fake fuse request and send it. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_INIT, 0, 0, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(fuse_in_head.as_bytes()), + None, + Some(fuse_out_head.as_bytes()), + None, + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert!(out_header.error != 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn virtiofs_device_config_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // get values from device config. + let mut tag = [0; MAX_TAG_LENGTH]; + for i in 0..MAX_TAG_LENGTH { + tag[i] = fs + .device + .borrow() + .config_readb((offset_of!(VirtioFsConfig, tag) + i) as u64); + } + let num_request_queues = fs + .device + .borrow() + .config_readl(offset_of!(VirtioFsConfig, num_request_queues) as u64); + + // set values to device config. + for i in 0..MAX_TAG_LENGTH { + fs.device + .borrow() + .config_writeb((offset_of!(VirtioFsConfig, tag) + i) as u64, 0x10); + } + fs.device + .borrow() + .config_writel(offset_of!(VirtioFsConfig, num_request_queues) as u64, 5); + + // get values from device config. + let mut tag_new = [0; MAX_TAG_LENGTH]; + for i in 0..MAX_TAG_LENGTH { + tag_new[i] = fs + .device + .borrow() + .config_readb((offset_of!(VirtioFsConfig, tag) + i) as u64); + } + let num_request_queues_new = fs + .device + .borrow() + .config_readl(offset_of!(VirtioFsConfig, num_request_queues) as u64); + + // Check config can not be changed. + assert!(num_request_queues == num_request_queues_new); + assert!(tag == tag_new); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn ls_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // init filesystem. + fuse_init(&fs); + + // FUSE_OPENDIR. + let fh = fuse_open_dir(&fs, PARENT_NODEID); + + // FUSE_READDIRPLUS. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_READDIRPLUS, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_read_in = FuseReadIn { + fh, + offset: 0, + size: DEFAULT_READ_SIZE as u32, + ..Default::default() + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_read_out = [0; DEFAULT_READ_SIZE]; + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_read_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_read_out, + ); + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + // FUSE_FORGET. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_FORGET, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_read_in = FuseForgetIn { nlookup: 1 }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_forget_out = FuseForgetOut::default(); + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_read_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_forget_out.as_bytes(), + ); + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + // FUSE_READDIR. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_READDIR, 0, 1, 0, 0, 0, 0); + let fuse_read_in = FuseReadIn { + fh, + offset: 0, + size: DEFAULT_READ_SIZE as u32, + ..Default::default() + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_read_out = [0_u8; DEFAULT_READ_SIZE]; + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_read_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_read_out, + ); + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + // FUSE_RELEASEDIR. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_RELEASEDIR, 0, 1, 0, 0, 0, 0); + let fuse_read_in = FuseReleaseIn { + fh, + ..Default::default() + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_read_out = [0_u8; DEFAULT_READ_SIZE]; + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_read_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_read_out, + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +fn fuse_setattr( + fs: &VirtioFsTest, + nodeid: u64, + fuse_setattr_in: FuseSetattrIn, +) -> (FuseOutHeader, FuseAttrOut) { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_SETATTR, 0, nodeid, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let fuse_attr_out = FuseAttrOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_setattr_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_attr_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + let attr_out = read_obj::(fs.state.clone(), outbodyaddr); + + (out_header, attr_out) +} + +#[test] +fn setattr_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // do init. + fuse_init(&fs); + + // do lookup + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + let fh = fuse_open(&fs, nodeid); + + // chmod 666 testfile + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.mode = 0o666; // file right: 666. + fuse_setattr_in.valid = FATTR_MODE | FATTR_FH; + fuse_setattr_in.fh = fh; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.mode & 0o666 == 0o666); + + // chmod 777 testfile + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.mode = 0o777; // file right: 777. + fuse_setattr_in.valid = FATTR_MODE; + fuse_setattr_in.fh = fh; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.mode & 0o777 == 0o777); + + // chown. + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.valid = FATTR_UID | FATTR_GID; + fuse_setattr_in.fh = fh; + fuse_setattr_in.uid = 100; + fuse_setattr_in.gid = 200; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.uid == 100); + assert!(attr.attr.gid == 200); + + // truncate /mnt/testfile -s 1k + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.size = 1024; // 1k + fuse_setattr_in.valid = FATTR_SIZE | FATTR_FH; + fuse_setattr_in.fh = fh; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.size == 1024); + + // truncate /mnt/testfile -s 2k + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.size = 2048; // 2k + fuse_setattr_in.valid = FATTR_SIZE; + fuse_setattr_in.fh = fh; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.size == 2048); + + // touch -m -t 202301010000 test.c + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.mtime = 1672531200; // 2023.01.01 00:00 + fuse_setattr_in.valid = FATTR_MTIME; + fuse_setattr_in.fh = fh; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.mtime == 1672531200); + + // touch -a -t 202301010000 test.c + let mut fuse_setattr_in = FuseSetattrIn::default(); + fuse_setattr_in.atime = 1672531200; // 2023.01.01 00:00 + fuse_setattr_in.valid = FATTR_ATIME | FATTR_FH; + fuse_setattr_in.fh = fh; + let (out_header, _attr) = fuse_setattr(&fs, nodeid, fuse_setattr_in); + assert_eq!(out_header.error, 0); + + let (_out_header, attr) = fuse_getattr(&fs, nodeid, fh); + assert!(attr.attr.atime == 1672531200); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +// unlink /mnt/testfile +#[test] +fn unlink_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + fuse_init(&fs); + fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + + // unlink request. + let len = (size_of::() + TEST_FILE_NAME.len() + 1) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_UNLINK, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_unlink_in = FuseUnlinkrIn { + name: String::from(TEST_FILE_NAME), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_unlink_out = FuseEntryOut::default(); + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_unlink_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_unlink_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + let mut linkpath = virtiofs_test_dir.clone(); + linkpath.push_str("/shared/testfile"); + let linkpath_clone = linkpath.clone(); + let link_path = Path::new(&linkpath_clone); + assert_eq!(link_path.exists(), false); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn rmdir_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + let mut dir = virtiofs_test_dir.clone(); + dir.push_str("/shared/dir"); + Command::new("mkdir") + .arg("-p") + .arg(dir.clone()) + .output() + .unwrap(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + fuse_init(&fs); + fuse_lookup(&fs, "dir".to_string()); + + // rmdir request. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_RMDIR, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_unlink_in = FuseUnlinkrIn { + name: String::from("dir"), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_unlink_out = FuseEntryOut::default(); + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_unlink_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_unlink_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + let mut linkpath = virtiofs_test_dir.clone(); + linkpath.push_str("/shared/dir"); + let linkpath_clone = linkpath.clone(); + let link_path = Path::new(&linkpath_clone); + assert_eq!(link_path.exists(), false); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn symlink_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + // do request. + let linkname = "link".to_string(); + let len = (linkname.len() + size_of::() + TEST_FILE_NAME.len() + 2) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_SYMLINK, 4, PARENT_NODEID, 0, 0, 0, 0); + let fuse_init_in = FusesysmlinkIn { + name: linkname.clone(), + linkname: String::from(TEST_FILE_NAME), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_init_out = FuseEntryOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_init_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_init_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + let entryout = read_obj::(fs.state.clone(), outbodyaddr); + assert_eq!(entryout.attr.nlink, 1); + + let mut linkpath = virtiofs_test_dir.clone(); + linkpath.push_str("/shared/link"); + let linkpath_clone = linkpath.clone(); + let link_path = Path::new(&linkpath_clone); + assert_eq!(link_path.is_symlink(), true); + + // Read link + let node_id = fuse_lookup(&fs, linkname.clone()); + let len = size_of::() as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_READLINK, 8, node_id, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let fuse_read_link_out = [0_u8; 1024]; + let (_, _, outheader, outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + None, + Some(&fuse_out_head.as_bytes()), + Some(&fuse_read_link_out), + ); + + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(0, out_header.error); + let fuse_read_link_out = fs.state.borrow().memread(outbodyaddr.unwrap(), 1024); + let read_path = String::from_utf8(fuse_read_link_out); + let mut read_path = read_path.unwrap(); + read_path.truncate(TEST_FILE_NAME.len()); + assert_eq!(TEST_FILE_NAME.to_string(), read_path); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +// fallocate -l 1024K /mnt/testfile +#[test] +fn fallocate_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + let fh = fuse_open(&fs, nodeid); + + // FUSE_FALLOCATE. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_FALLOCATE, 0, nodeid, 0, 0, 0, 0); + let fuse_fallocate_in = FuseFallocateIn { + fh, + offset: 0, + length: 1048576, // 1KB. + mode: 0, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_fallocate_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +// fcntl() function test. +#[test] +fn posix_file_lock_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + let fh = fuse_open(&fs, nodeid); + + // getlk write lock. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_GETLK, 0, nodeid, 0, 0, 0, 0); + let fuse_lk_in = FuseLkIn { + fh, + owner: 0, + lk: FuseFileLock { + start: 0, + end: 1, + lock_type: F_WRLCK, + pid: 1, + }, + lk_flags: 0, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_lk_out = FuseLkOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_lk_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_lk_out.as_bytes(), + ); + + // Check file is unlock. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + let lkout = read_obj::(fs.state.clone(), outbodyaddr); + assert_eq!(lkout.lk.lock_type, F_UNLCK); + + // setlk write lock. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_SETLK, 0, nodeid, 0, 0, 0, 0); + let fuse_lk_in = FuseLkIn { + fh, + owner: 0, + lk: FuseFileLock { + start: 0, + end: 1, + lock_type: F_WRLCK, + pid: 1, + }, + lk_flags: 0, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_lk_out = FuseLkOut::default(); + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_lk_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_lk_out.as_bytes(), + ); + + // check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn mknod_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + // FUSE_MKNOD. + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_MKNOD, 4, PARENT_NODEID, 0, 0, 0, 0); + let fuse_init_in = FuseMknodIn { + mode: 0o666, // right mode 666. + rdev: 0, + umask: 0, + padding: 0, + name: String::from("node"), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_init_out = FuseEntryOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_init_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_init_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + let entryout = read_obj::(fs.state.clone(), outbodyaddr); + assert_eq!(entryout.attr.nlink, 1); + + let mut nodepath = virtiofs_test_dir.clone(); + nodepath.push_str("/shared/node"); + let nodepath_clone = nodepath.clone(); + let node_path = Path::new(&nodepath_clone); + assert!(node_path.exists()); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +fn get_xattr(fs: &VirtioFsTest, name: String, nodeid: u64) -> (FuseOutHeader, String) { + let len = + (size_of::() + offset_of!(FuseGetxattrIn, name) + name.len() + 1) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_GETXATTR, 0, nodeid, 0, 0, 0, 0); + let fuse_in = FuseGetxattrIn { + size: DEFAULT_XATTR_SIZE, + padding: 0, + name, + }; + + let fuse_out_head = FuseOutHeader::default(); + let fuse_out = [0_u8; DEFAULT_XATTR_SIZE as usize]; + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_out, + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + let fuse_read_out = fs + .state + .borrow() + .memread(outbodyaddr, DEFAULT_XATTR_SIZE as u64); + let attr = String::from_utf8(fuse_read_out).unwrap(); + + (out_header, attr) +} + +fn flush_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_FLUSH, 0, nodeid, 0, 0, 0, 0); + let fuse_in = FuseFlushIn { + fh, + unused: 0, + padding: 0, + lock_owner: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(out_header.error, 0); +} + +fn write_file(fs: &VirtioFsTest, nodeid: u64, fh: u64, write_buf: String) { + let len = (size_of::() + offset_of!(FuseWriteIn, write_buf) + write_buf.len() + 1) + as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_WRITE, 0, nodeid, 0, 0, 0, 0); + let fuse_write_in = FuseWriteIn::new(fh, 0, write_buf.clone()); + let fuse_out_head = FuseOutHeader::default(); + let fuse_write_out = FuseWriteOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_write_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_write_out.as_bytes(), + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + + let write_out = read_obj::(fs.state.clone(), outbodyaddr); + assert_eq!(write_out.size, (write_buf.len() + 1) as u32); +} + +fn release_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_RELEASE, 0, nodeid, 0, 0, 0, 0); + let fuse_read_in = FuseReleaseIn { + fh, + flags: O_NONBLOCK | O_DIRECT, + ..Default::default() + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_read_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(out_header.error, 0); +} + +fn create_file(fs: &VirtioFsTest, name: String) -> (FuseOutHeader, FuseCreateOut) { + let len = (size_of::() + offset_of!(FuseCreateIn, name) + name.len() + 1) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_CREATE, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_in = FuseCreateIn { + flags: O_CREAT | O_TRUNC | O_RDWR, + mode: 0o777, // file right mode 777. + umask: 0, + padding: 0, + name, + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_out = FuseCreateOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + let createout = read_obj::(fs.state.clone(), outbodyaddr); + + (out_header, createout) +} + +#[test] +fn writefile_fun() { + let file = "text.txt".to_string(); + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + let (out_head, attr) = create_file(&fs, file); + assert_eq!(out_head.error, 0); + + let mut nodepath = virtiofs_test_dir.clone(); + nodepath.push_str("/shared/text.txt"); + + let nodepath_clone = nodepath.clone(); + let node_path = Path::new(&nodepath_clone); + assert!(node_path.exists()); + + flush_file(&fs, attr.create_out.nodeid, attr.open_out.fh); + get_xattr(&fs, "security.selinux".to_string(), attr.create_out.nodeid); + + write_file( + &fs, + attr.create_out.nodeid, + attr.open_out.fh, + "12345".to_string(), + ); + flush_file(&fs, attr.create_out.nodeid, attr.open_out.fh); + + release_file(&fs, attr.create_out.nodeid, attr.open_out.fh); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +fn read_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) -> String { + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_READ, 0, nodeid, 0, 0, 0, 0); + let fuse_in = FuseReadIn { + fh, + offset: 0, + size: DEFAULT_READ_SIZE as u32, + ..Default::default() + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_out = [0_u8; DEFAULT_READ_SIZE]; + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_out, + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, 0); + let fuse_read_out = fs.state.borrow().memread(outbodyaddr, 5); + let str = String::from_utf8(fuse_read_out).unwrap(); + + str +} + +#[test] +fn openfile_test() { + let file = TEST_FILE_NAME.to_string(); + + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + let nodeid = fuse_lookup(&fs, file.clone()); + + // open/write/flush/close/open/read/close + let fh = fuse_open(&fs, nodeid); + let mut nodepath = virtiofs_test_dir.clone(); + nodepath.push_str("/shared/testfile"); + let nodepath_clone = nodepath.clone(); + let node_path = Path::new(&nodepath_clone); + assert!(node_path.exists()); + write_file(&fs, nodeid, fh, "12345".to_string()); + flush_file(&fs, nodeid, fh); + release_file(&fs, nodeid, fh); + + let fh = fuse_open(&fs, nodeid); + let get_str = read_file(&fs, nodeid, fh); + assert_eq!(get_str, "12345".to_string()); + release_file(&fs, nodeid, fh); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn rename_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + // FUSE_RENAME. Rename testfile to file. + fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_RENAME, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_rename_in = FuseRenameIn { + newdir: PARENT_NODEID, + oldname: TEST_FILE_NAME.to_string(), + newname: "file".to_string(), + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_rename_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(0, out_header.error); + let path = virtiofs_test_dir.clone() + "/shared" + "/file"; + let path = Path::new(path.as_str()); + assert!(path.exists()); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn link_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + // FUSE_LINK. + let oldnodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + + let len = (size_of::() + size_of::()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_LINK, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_rename_in = FuseLinkIn { + oldnodeid, + newname: "file_link".to_string(), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_entry_out = FuseEntryOut::default(); + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_rename_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_entry_out.as_bytes(), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(0, out_header.error); + let entry_out = read_obj::(fs.state.clone(), outbodyaddr); + // link a file will make its nlink count +1 + assert_eq!(2, entry_out.attr.nlink); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn statfs_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + + // do request. + let len = size_of::() as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_STATFS, 0, PARENT_NODEID, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let fuse_statfs_out = FuseKstatfs::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + None, + Some(&fuse_out_head.as_bytes()), + Some(&fuse_statfs_out.as_bytes()), + ); + + // Check. + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + assert_eq!(0, out_header.error); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn virtio_fs_fuse_ioctl_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // init filesystem. + fuse_init(&fs); + + // FUSE_LOOKUP. + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + + // FUSE_IOCTL. + let len = size_of::() as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_IOCTL, 0, nodeid, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &[0], + &fuse_out_head.as_bytes(), + &[0], + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_ne!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn virtio_fs_fuse_abnormal_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // init filesystem. + fuse_init(&fs); + + // Unsupported message 0xff. + let len = size_of::() as u32; + let fuse_in_head = FuseInHeader::new(len, 0xff, 0, 0, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); + + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &[0], + &fuse_out_head.as_bytes(), + &[0], + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_ne!(out_header.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +// Read "\0" c string from buffer[start..] and return the end position's next in buffer. +fn read_cstring(buffer: Vec, start: usize) -> (Option, usize) { + let mut pos = start; + + for i in start..buffer.len() { + if buffer[i] == b'\0' { + pos = i; + break; + } + } + + if pos == start { + return (None, pos); + } + + let cstring = String::from_utf8(buffer[start..pos].to_vec()).unwrap(); + + (Some(cstring), pos + 1) +} + +fn fuse_setxattr(fs: &VirtioFsTest, name: String, value: String, nodeid: u64) -> FuseOutHeader { + // 8: offset_of!(name, FuseSetxattrIn). + // 2: two "/0". + let len = (size_of::() + 8 + name.len() + value.len() + 2) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_SETXATTR, 4, nodeid, 0, 0, 0, 0); + let fuse_setxattr_in = FuseSetxattrIn { + size: value.len() as u32, + flags: XATTR_CREATE, + name, + value, + }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_setxattr_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + + out_header +} + +fn fuse_removexattr(fs: &VirtioFsTest, name: String, nodeid: u64) -> FuseOutHeader { + let len = (size_of::() + name.len() + 1) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_REMOVEXATTR, 0, nodeid, 0, 0, 0, 0); + let fuse_removexattr_in = FuseRemoveXattrIn { name }; + let fuse_out_head = FuseOutHeader::default(); + let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( + Some(&fuse_in_head.as_bytes()), + Some(&fuse_removexattr_in.as_bytes()), + Some(&fuse_out_head.as_bytes()), + None, + ); + + let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); + out_header +} + +fn fuse_listxattr(fs: &VirtioFsTest, nodeid: u64) -> (FuseOutHeader, u64) { + // 8: offset_of!(name, FuseGetxattrIn). + let len = (size_of::() + 8) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_LISTXATTR, 0, nodeid, 0, 0, 0, 0); + let fuse_in = FuseGetxattrIn { + size: DEFAULT_XATTR_SIZE, + padding: 0, + name: "".to_string(), + }; + let fuse_out_head = FuseOutHeader::default(); + let fuse_out = [0_u8; DEFAULT_XATTR_SIZE as usize]; + let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_in.as_bytes(), + &fuse_out_head.as_bytes(), + &fuse_out, + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + + (out_header, outbodyaddr) +} + +// setfattr -n user.abc -v valtest testfile +// getfattr -n user.abc testfile +// getfattr testfile +// setfattr -x user.abc testfile +#[test] +fn regularfile_xattr_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, false); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + + let testattr_name = "user.abc"; + let testattr_value = "valtest"; + + // SETXATTR. + let fuseout = fuse_setxattr( + &fs, + testattr_name.to_string(), + testattr_value.to_string(), + nodeid, + ); + assert_eq!(fuseout.error, 0); + + // GETXATTR + let (header, value) = get_xattr(&fs, testattr_name.to_string(), nodeid); + assert_eq!(0, header.error); + assert_eq!(value[0..testattr_value.len()], testattr_value.to_string()); + + // LISTXATTR + let (header, outbodyaddr) = fuse_listxattr(&fs, nodeid); + assert_eq!(header.error, 0); + let attr_list = fs + .state + .borrow() + .memread(outbodyaddr, DEFAULT_XATTR_SIZE as u64); + // The first attr is "security.selinux" + let (_attr1, next1) = read_cstring(attr_list.clone(), 0); + // The next attrs are what we set by FUSE_SETXATTR. Check it. + let (attr2, _next2) = read_cstring(attr_list.clone(), next1); + assert_eq!(attr2.unwrap(), testattr_name); + + // REMOVEXATTR + let outheader = fuse_removexattr(&fs, testattr_name.to_string(), nodeid); + assert_eq!(0, outheader.error); + + // GETXATTR + // Xattr "user.abc" has been removed, should receive ERROR. + let (header, _value) = get_xattr(&fs, testattr_name.to_string(), nodeid); + assert_ne!(0, header.error); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +// setfattr -n user.abc -v valtest /mnt/testchar +// getfattr -n user.abc /mnt/testchar +// getfattr /mnt/testchar +// setfattr -x user.abc /mnt/testchar +#[test] +fn character_file_xattr_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, false); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + fuse_init(&fs); + let nodeid = fuse_lookup(&fs, TEST_CHARDEV_NAME.to_string()); + + let testattr_name = "user.abc"; + let testattr_value = "valtest"; + + // SETXATTR. + let fuseout = fuse_setxattr( + &fs, + testattr_name.to_string(), + testattr_value.to_string(), + nodeid, + ); + // can not setxattr for character device. + assert_ne!(fuseout.error, 0); + + // GETXATTR nothing. + let (header, _value) = get_xattr(&fs, testattr_name.to_string(), nodeid); + assert_ne!(0, header.error); + + // LISTXATTR + let (header, _outbodyaddr) = fuse_listxattr(&fs, nodeid); + assert_eq!(header.error, 0); + + // REMOVEXATTR + let outheader = fuse_removexattr(&fs, testattr_name.to_string(), nodeid); + assert_ne!(0, outheader.error); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +#[test] +fn virtio_fs_fuse_lseek_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // init filesystem. + fuse_init(&fs); + + // FUSE_LOOKUP. + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + // FUSE_OPEN. + let fh = fuse_open(&fs, nodeid); + + // FUSE_GETATTR. + let (out_header, _attrout) = fuse_getattr(&fs, nodeid, fh); + assert_eq!(out_header.error, 0); + + // FUSE_LSEEK. + assert_ne!(fuse_lseek(&fs, nodeid, fh + 1, 1).0.error, 0); + assert_ne!(fuse_lseek(&fs, nodeid, fh + 1, 0).0.error, 0); + assert_eq!(fuse_lseek(&fs, nodeid, fh, 0).0.error, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +fn fuse_batch_forget(fs: &VirtioFsTest, nodeid: u64, trim: usize) { + let len = + size_of::() + size_of::() + size_of::(); + let fuse_in_head = FuseInHeader::new(len as u32, FUSE_BATCH_FORGET, 0, 0, 0, 0, 0, 0); + let fuse_batch_forget_in = FuseBatchForgetIn { count: 1, dummy: 0 }; + let fuse_forget_data_in = FuseForgetDataIn { + ino: nodeid, + nlookup: 1, + }; + let data_bytes = [ + fuse_batch_forget_in.as_bytes(), + fuse_forget_data_in.as_bytes(), + ] + .concat(); + let (_, _) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &data_bytes[0..data_bytes.len() - trim], + &[0], + &[0], + ); +} + +#[test] +fn virtio_fs_fuse_batch_forget_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // init filesystem. + fuse_init(&fs); + + // FUSE_LOOKUP. + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + + // FUSE_BATCH_FORGET. + // Incomplete FuseBatchForgetIn. + fuse_batch_forget(&fs, nodeid, size_of::() + 1); + // Incomplete FuseForgetDataIn. + fuse_batch_forget(&fs, nodeid, size_of::() - 1); + // Normal test. + fuse_batch_forget(&fs, nodeid, 0); + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} + +// flock. +#[test] +fn virtio_fs_fuse_setlkw_test() { + // start virtiofsd process. + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = + VirtioFsTest::vhost_user_fs_start(); + + // start vm. + let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); + + // init filesystem. + fuse_init(&fs); + + // FUSE_LOOKUP. + let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); + // FUSE_OPEN. + let fh = fuse_open(&fs, nodeid); + + let reqs = [ + //(req_type, lk_flags, lock_type, fh, error), + (0, 1, 0, fh, 0), // Normal F_RDLCK test. + (0, 1, 1, fh, 0), // Normal F_WDLCK test. + (0, 1, 2, fh, 0), // Normal F_UNLCK test. + (0, 0, 1, fh, -95), // Abnormal test with error -libc::EOPNOTSUPP. + (0, 1, 0, fh + 1, -9), // Abnormal test with error -libc::EBADF. + (0, 1, 0, fh + 1, -9), // Abnormal test with error -libc::EBADF. + (1, 1, 0, fh, -22), // Abnormal test with error -libc::EINVAL. + ]; + + // FUSE_SETLKW. + for (req_type, lk_flags, lock_type, fh, error) in reqs { + let len = size_of::() + size_of::(); + let fuse_in_head = FuseInHeader::new(len as u32, FUSE_SETLKW, 0, nodeid, 0, 0, 0, 0); + let fuse_lk_in = FuseLkIn { + fh, + owner: 0, + lk: FuseFileLock { + start: 0, + end: 1, + lock_type, + pid: 0, + }, + lk_flags, + padding: 0, + }; + let fuse_out_head = FuseOutHeader::default(); + let mut fuse_lk_in_bytes = fuse_lk_in.as_bytes(); + if req_type == 1 { + fuse_lk_in_bytes = &fuse_lk_in.as_bytes()[0..1]; + } + + let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( + &fuse_in_head.as_bytes(), + &fuse_lk_in_bytes, + &fuse_out_head.as_bytes(), + &[0], + ); + + let out_header = read_obj::(fs.state.clone(), outheaderaddr); + assert_eq!(out_header.error, error); + } + + // kill process and clean env. + fs.testcase_end(virtiofs_test_dir); +} -- Gitee From 1374d8ae9a70b156a6ae523fb426ef4e72336ae4 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 1 Mar 2023 10:37:37 +0800 Subject: [PATCH 0890/2187] devices: Add ged device to stratovirt. 1. Ged: Add Generic event device. 2. Change the shutdown event notification to kick. Signed-off-by:mayunlong --- devices/src/acpi/ged.rs | 205 +++++++++++++++++++++++++ devices/src/acpi/mod.rs | 13 ++ devices/src/lib.rs | 1 + machine/src/lib.rs | 49 ++---- machine/src/micro_vm/mod.rs | 26 ++-- machine/src/standard_vm/aarch64/mod.rs | 29 +++- machine/src/standard_vm/x86_64/mod.rs | 16 +- util/src/loop_context.rs | 2 +- 8 files changed, 268 insertions(+), 73 deletions(-) create mode 100644 devices/src/acpi/ged.rs create mode 100644 devices/src/acpi/mod.rs diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs new file mode 100644 index 000000000..500c2ea76 --- /dev/null +++ b/devices/src/acpi/ged.rs @@ -0,0 +1,205 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; +use address_space::GuestAddress; +use anyhow::{anyhow, Context, Result}; +use log::error; +use machine_manager::event_loop::EventLoop; +use std::os::unix::prelude::AsRawFd; +use std::rc::Rc; +use std::sync::atomic::{AtomicU32, Ordering}; +use sysbus::{SysBus, SysBusDevOps, SysRes}; +use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; +use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; +use vmm_sys_util::epoll::EventSet; + +use acpi::{ + AmlActiveLevel, AmlAddressSpaceType, AmlAnd, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlEqual, + AmlExtendedInterrupt, AmlField, AmlFieldUnit, AmlIf, AmlIntShare, AmlInteger, AmlLocal, + AmlMethod, AmlName, AmlNameDecl, AmlNotify, AmlOpRegion, AmlResTemplate, AmlResourceUsage, + AmlScopeBuilder, AmlStore, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, +}; + +use std::sync::{Arc, Mutex}; + +use vmm_sys_util::eventfd::EventFd; + +#[derive(Clone, Copy)] +enum AcpiEvent { + Nothing = 0, + PowerDown = 1, +} + +const AML_GED_EVT_REG: &str = "EREG"; +const AML_GED_EVT_SEL: &str = "ESEL"; + +#[derive(Clone)] +pub struct Ged { + interrupt_evt: Arc>, + notification_type: Arc, + /// System resource. + res: SysRes, +} + +impl Default for Ged { + fn default() -> Self { + Self { + interrupt_evt: Arc::new(None), + notification_type: Arc::new(AtomicU32::new(AcpiEvent::Nothing as u32)), + res: SysRes::default(), + } + } +} + +impl Ged { + pub fn realize( + mut self, + sysbus: &mut SysBus, + power_button: Arc, + region_base: u64, + region_size: u64, + ) -> Result<()> { + self.interrupt_evt = Arc::new(Some(EventFd::new(libc::EFD_NONBLOCK)?)); + self.set_sys_resource(sysbus, region_base, region_size) + .with_context(|| anyhow!(AcpiError::Alignment(region_size.try_into().unwrap())))?; + + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev, region_base, region_size)?; + + let ged = dev.lock().unwrap(); + ged.register_acpi_powerdown_event(power_button) + } + + fn register_acpi_powerdown_event(&self, power_button: Arc) -> Result<()> { + let power_down_fd = power_button.as_raw_fd(); + let ged_clone = self.clone(); + let power_down_handler: Rc = Rc::new(move |_, _| { + read_fd(power_down_fd); + ged_clone + .notification_type + .store(AcpiEvent::PowerDown as u32, Ordering::SeqCst); + ged_clone.inject_interrupt(); + None + }); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + power_down_fd, + None, + EventSet::IN, + vec![power_down_handler], + ); + + EventLoop::update_event(vec![notifier], None) + .with_context(|| "Failed to register powerdown notifier.")?; + Ok(()) + } + + fn inject_interrupt(&self) { + if let Some(evt_fd) = self.interrupt_evt() { + evt_fd + .write(1) + .unwrap_or_else(|e| error!("ged: failed to write interrupt eventfd ({}).", e)); + return; + } + error!("ged: failed to get interrupt event fd."); + } +} + +impl SysBusDevOps for Ged { + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { + if offset != 0 { + return false; + } + let value = self + .notification_type + .swap(AcpiEvent::Nothing as u32, Ordering::SeqCst); + + write_data_u32(data, value) + } + + fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { + true + } + + fn interrupt_evt(&self) -> Option<&EventFd> { + self.interrupt_evt.as_ref().as_ref() + } + + fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + Some(&mut self.res) + } +} + +impl AmlBuilder for Ged { + fn aml_bytes(&self) -> Vec { + let mut acpi_dev = AmlDevice::new("\\_SB.GED"); + acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0013".to_string()))); + acpi_dev.append_child(AmlNameDecl::new("_UID", AmlString("GED".to_string()))); + + let mut res = AmlResTemplate::new(); + + // SPI start at interrupt number 32 on aarch64 platform. + let irq_base = INTERRUPT_PPIS_COUNT + INTERRUPT_SGIS_COUNT; + res.append_child(AmlExtendedInterrupt::new( + AmlResourceUsage::Consumer, + AmlEdgeLevel::Edge, + AmlActiveLevel::High, + AmlIntShare::Exclusive, + vec![self.res.irq as u32 + irq_base], + )); + acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); + + acpi_dev.append_child(AmlOpRegion::new( + "EREG", + AmlAddressSpaceType::SystemMemory, + self.res.region_base, + self.res.region_size, + )); + + let mut field = AmlField::new( + AML_GED_EVT_REG, + AmlFieldAccessType::DWord, + AmlFieldLockRule::NoLock, + AmlFieldUpdateRule::WriteAsZeros, + ); + + let elemt = AmlFieldUnit::new(Some(AML_GED_EVT_SEL), 32); + field.append_child(elemt); + acpi_dev.append_child(field); + + let mut method = AmlMethod::new("_EVT", 1, true); + let store = AmlStore::new(AmlName(AML_GED_EVT_SEL.to_string()), AmlLocal(0)); + method.append_child(store); + let mut if_scope = AmlIf::new(AmlEqual::new( + AmlAnd::new(AmlLocal(0), AmlInteger(1), AmlLocal(0)), + AmlInteger(1), + )); + if_scope.append_child(AmlNotify::new( + AmlName("PWRB".to_string()), + AmlInteger(0x80), + )); + method.append_child(if_scope); + acpi_dev.append_child(method); + + acpi_dev.aml_bytes() + } +} + +pub fn acpi_dsdt_add_power_button() -> AmlDevice { + let mut acpi_dev = AmlDevice::new("PWRB"); + acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("PNP0C0C".to_string()))); + acpi_dev.append_child(AmlNameDecl::new("_UID", AmlInteger(1))); + + acpi_dev +} diff --git a/devices/src/acpi/mod.rs b/devices/src/acpi/mod.rs new file mode 100644 index 000000000..54c1644a4 --- /dev/null +++ b/devices/src/acpi/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod ged; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 14e32d2a4..cc42da10e 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -16,6 +16,7 @@ //! - interrupt controller (aarch64) //! - legacy devices, such as serial devices +pub mod acpi; mod interrupt_controller; pub mod legacy; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index eadc0f933..64b5b5f3d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -21,15 +21,12 @@ use std::collections::{BTreeMap, HashMap}; use std::fs::{remove_file, File}; use std::net::TcpListener; use std::ops::Deref; -use std::os::unix::{io::AsRawFd, net::UnixListener}; +use std::os::unix::net::UnixListener; use std::path::Path; -use std::rc::Rc; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use log::warn; use util::file::{lock_file, unlock_file}; -use util::loop_context::read_fd; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub use micro_vm::LightMachine; @@ -59,10 +56,7 @@ use machine_manager::config::{ }; #[cfg(not(target_env = "musl"))] use machine_manager::config::{parse_gpu, parse_usb_keyboard, parse_usb_tablet, parse_xhci}; -use machine_manager::{ - event_loop::EventLoop, - machine::{KvmVmState, MachineInterface}, -}; +use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::Result as StdResult; @@ -74,7 +68,6 @@ use usb::{ }; use util::{ arg_parser, - loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; use vfio::{VfioDevice, VfioPciDevice}; @@ -235,6 +228,10 @@ pub trait MachineOps { /// Add RTC device. fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] mem_size: u64) -> Result<()>; + /// Add Generic event device. + #[cfg(target_arch = "aarch64")] + fn add_ged_device(&mut self) -> Result<()>; + /// Add serial device. /// /// # Arguments @@ -1178,6 +1175,10 @@ pub trait MachineOps { ) .with_context(|| anyhow!(MachineError::AddDevErr("RTC".to_string())))?; + #[cfg(target_arch = "aarch64")] + self.add_ged_device() + .with_context(|| anyhow!(MachineError::AddDevErr("Ged".to_string())))?; + let cloned_vm_config = vm_config.clone(); if let Some(serial) = cloned_vm_config.serial.as_ref() { self.add_serial_device(serial) @@ -1323,31 +1324,6 @@ pub trait MachineOps { Ok(()) } - /// Register event notifier for power button of mainboard. - /// - /// # Arguments - /// - /// * `power_button` - Eventfd of the power button. - fn register_power_event(&self, power_button: Arc) -> Result<()> { - let button_fd = power_button.as_raw_fd(); - let power_button_handler: Rc = Rc::new(move |_, _| { - read_fd(button_fd); - None - }); - let notifier = EventNotifier::new( - NotifierOperation::AddShared, - button_fd, - None, - EventSet::IN, - vec![power_button_handler], - ); - trace_eventnotifier(¬ifier); - - EventLoop::update_event(vec![notifier], None) - .with_context(|| anyhow!(MachineError::RegNotifierErr))?; - Ok(()) - } - /// Get the drive backend files. fn get_drive_files(&self) -> Arc>>; @@ -1659,11 +1635,6 @@ fn start_incoming_migration(vm: &Arc>) -> Re Ok(()) } -/// Description of the trace for eventnotifier. -fn trace_eventnotifier(eventnotifier: &EventNotifier) { - util::ftrace!(trace_eventnotifier, "{:#?}", eventnotifier); -} - fn coverage_allow_list(syscall_allow_list: &mut Vec) { syscall_allow_list.extend(vec![ BpfRule::new(libc::SYS_fcntl), diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index ad8c46a00..b5178fe90 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -30,6 +30,7 @@ pub mod error; pub use error::MicroVmError; +use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::UpdateRegionArgument; use util::aio::AioEngine; @@ -37,7 +38,7 @@ mod mem_layout; mod syscall; use super::Result as MachineResult; -use log::error; +use log::{error, info}; use std::collections::HashMap; use std::fmt; use std::fmt::Debug; @@ -45,7 +46,6 @@ use std::ops::Deref; use std::os::unix::io::RawFd; use std::sync::{Arc, Condvar, Mutex}; use std::vec::Vec; -use vmm_sys_util::eventfd::EventFd; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -182,8 +182,6 @@ pub struct LightMachine { vm_state: Arc<(Mutex, Condvar)>, // Vm boot_source config. boot_source: Arc>, - // VM power button, handle VM `Shutdown` event. - power_button: Arc, // All configuration information of virtual machine. vm_config: Arc>, // Drive backend files. @@ -217,10 +215,6 @@ impl LightMachine { // Machine state init let vm_state = Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())); - let power_button = - Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { - anyhow!(MachineError::InitEventFdErr("power_button".to_string())) - })?); Ok(LightMachine { cpu_topo: CpuTopology::new( @@ -244,7 +238,6 @@ impl LightMachine { replaceable_info: MmioReplaceableInfo::new(), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, - power_button, vm_config: Arc::new(Mutex::new(vm_config.clone())), drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) @@ -681,6 +674,11 @@ impl MachineOps for LightMachine { Ok(()) } + #[cfg(target_arch = "aarch64")] + fn add_ged_device(&mut self) -> MachineResult<()> { + Ok(()) + } + fn add_serial_device(&mut self, config: &SerialConfig) -> MachineResult<()> { #[cfg(target_arch = "x86_64")] let region_base: u64 = SERIAL_ADDR; @@ -855,10 +853,6 @@ impl MachineOps for LightMachine { } } - locked_vm - .register_power_event(locked_vm.power_button.clone()) - .with_context(|| anyhow!(MachineError::InitEventFdErr("power_button".to_string())))?; - MigrationManager::register_vm_instance(vm.clone()); #[cfg(target_arch = "x86_64")] MigrationManager::register_kvm_instance( @@ -906,9 +900,9 @@ impl MachineLifecycle for LightMachine { return false; } - if self.power_button.write(1).is_err() { - error!("Micro vm write power button failed"); - return false; + if let Some(ctx) = EventLoop::get_ctx(None) { + info!("vm destroy"); + ctx.kick(); } true } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 115a2037c..db7856618 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,7 +14,9 @@ mod pci_host_root; mod syscall; pub use crate::error::MachineError; -use log::error; +use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; +use log::{error, info}; +use machine_manager::event_loop::EventLoop; use std::borrow::Borrow; use std::collections::HashMap; use std::mem::size_of; @@ -83,6 +85,7 @@ pub enum LayoutEntryType { Uart, Rtc, FwCfg, + Ged, Mmio, PcieMmio, PciePio, @@ -101,6 +104,7 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0x0900_0000, 0x0000_1000), // Uart (0x0901_0000, 0x0000_1000), // Rtc (0x0902_0000, 0x0000_0018), // FwCfg + (0x0908_0000, 0x0000_0004), // Ged (0x0A00_0000, 0x0000_0200), // Mmio (0x1000_0000, 0x2EFF_0000), // PcieMmio (0x3EFF_0000, 0x0001_0000), // PciePio @@ -452,6 +456,18 @@ impl MachineOps for StdMachine { Ok(()) } + fn add_ged_device(&mut self) -> Result<()> { + let ged = Ged::default(); + ged.realize( + &mut self.sysbus, + self.power_button.clone(), + MEM_LAYOUT[LayoutEntryType::Ged as usize].0, + MEM_LAYOUT[LayoutEntryType::Ged as usize].1, + ) + .with_context(|| "Failed to realize Ged")?; + Ok(()) + } + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; @@ -560,8 +576,6 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; - locked_vm.register_power_event(locked_vm.power_button.clone())?; - MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { @@ -799,6 +813,9 @@ impl AcpiBuilder for StdMachine { // 2. Create pci host bridge node. sb_scope.append_child(self.pci_host.lock().unwrap().clone()); + + sb_scope.append_child(acpi_dsdt_add_power_button()); + dsdt.append_child(sb_scope.aml_bytes().as_slice()); // 3. Info of devices attached to system bus. @@ -971,9 +988,9 @@ impl MachineLifecycle for StdMachine { return false; } - if self.power_button.write(1).is_err() { - error!("ARM stdndard vm write power button failed"); - return false; + if let Some(ctx) = EventLoop::get_ctx(None) { + info!("vm destroy"); + ctx.kick(); } true } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 1b9aa3430..7cc9fda32 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -15,7 +15,7 @@ mod mch; mod syscall; use crate::error::MachineError; -use log::error; +use log::{error, info}; use std::collections::HashMap; use std::io::{Seek, SeekFrom}; use std::mem::size_of; @@ -42,6 +42,7 @@ use machine_manager::config::{ NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, @@ -112,8 +113,6 @@ pub struct StdMachine { vm_state: Arc<(Mutex, Condvar)>, /// Vm boot_source config. boot_source: Arc>, - /// VM power button, handle VM `Shutdown` event. - power_button: Arc, /// Reset request, handle VM `Reset` event. reset_req: Arc, /// All configuration information of virtual machine. @@ -171,9 +170,6 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, - power_button: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { - anyhow!(MachineError::InitEventFdErr("power_button".to_string())) - })?), reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { anyhow!(MachineError::InitEventFdErr("reset request".to_string())) })?), @@ -476,8 +472,6 @@ impl MachineOps for StdMachine { .with_context(|| "Failed to create ACPI tables")?; } - locked_vm.register_power_event(locked_vm.power_button.clone())?; - locked_vm .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; @@ -823,9 +817,9 @@ impl MachineLifecycle for StdMachine { return false; } - if self.power_button.write(1).is_err() { - error!("X86 standard vm write power button failed"); - return false; + if let Some(ctx) = EventLoop::get_ctx(None) { + info!("vm destroy"); + ctx.kick(); } true } diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index e8914593b..7056564e3 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -249,7 +249,7 @@ impl EventLoopContext { } // Force epoll.wait to exit to re-evaluate events and timers. - fn kick(&mut self) { + pub fn kick(&mut self) { self.kicked.store(true, Ordering::SeqCst); if self.kick_me.load(Ordering::SeqCst) { if let Err(e) = self.kick_event.write(1) { -- Gitee From c4eadadb62ad555e1535f68782f5173c5c56bdbc Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 1 Mar 2023 14:45:38 +0800 Subject: [PATCH 0891/2187] machine_manager: add a qmp command. 1. add qmp command system_powerdown and event "POWERDOWN". 2. support libvirt command: "virsh shutdown" and "virsh reboot" Signed-off-by:mayunlong --- cpu/src/lib.rs | 19 ++++++--- devices/src/acpi/ged.rs | 5 +++ docs/qmp.md | 12 ++++++ machine/src/standard_vm/aarch64/mod.rs | 17 ++++++++ machine_manager/src/cmdline.rs | 6 +++ machine_manager/src/config/machine_config.rs | 20 +++++++++ machine_manager/src/machine.rs | 11 +++++ machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 45 +++++++++++++++++++- 9 files changed, 129 insertions(+), 7 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index e90d7bc42..4934962e5 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -71,9 +71,11 @@ use std::time::Duration; use kvm_ioctls::{VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info, warn}; +use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; use machine_manager::machine::MachineInterface; use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; + #[cfg(not(test))] use util::test_helper::is_test_enabled; use vmm_sys_util::signal::{register_signal_handler, Killable}; @@ -434,11 +436,18 @@ impl CPUInterface for CPU { } fn guest_shutdown(&self) -> Result<()> { - let (cpu_state, _) = &*self.state; - *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; - if let Some(vm) = self.vm.upgrade() { - vm.lock().unwrap().destroy(); + let shutdown_act = vm.lock().unwrap().get_shutdown_action(); + match shutdown_act { + ShutdownActionPoweroff => { + let (cpu_state, _) = &*self.state; + *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; + vm.lock().unwrap().destroy(); + } + ShutdownActionPause => { + vm.lock().unwrap().pause(); + } + } } else { return Err(anyhow!(CpuError::NoMachineInterface)); } @@ -514,6 +523,7 @@ impl CPUInterface for CPU { ); self.guest_shutdown() .with_context(|| "Some error occurred in guest shutdown")?; + return Ok(true); } else if event == kvm_bindings::KVM_SYSTEM_EVENT_RESET { info!( "Vcpu{} received an KVM_SYSTEM_EVENT_RESET signal", @@ -530,7 +540,6 @@ impl CPUInterface for CPU { flags ); } - return Ok(false); } VcpuExit::FailEntry(reason, cpuid) => { diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 500c2ea76..10f2d378f 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -14,7 +14,9 @@ use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; use address_space::GuestAddress; use anyhow::{anyhow, Context, Result}; use log::error; +use machine_manager::event; use machine_manager::event_loop::EventLoop; +use machine_manager::qmp::QmpChannel; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; @@ -89,6 +91,9 @@ impl Ged { .notification_type .store(AcpiEvent::PowerDown as u32, Ordering::SeqCst); ged_clone.inject_interrupt(); + if QmpChannel::is_connected() { + event!(Powerdown); + } None }); diff --git a/docs/qmp.md b/docs/qmp.md index 243194646..f2c41494f 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -277,6 +277,18 @@ Reset all guest VCPUs execution. -> {"event":"RESET","data":{"guest":true},"timestamp":{"seconds":1677381086,"microseconds":432033}} ``` +### system_powerdown + +Requests that a guest perform a powerdown operation. + +### Example + +```json +<- {"execute":"system_powerdown"} +-> {"return":{}} +-> {"event":"POWERDOWN","data":{},"timestamp":{"seconds":1677850193,"microseconds":617907}} +``` + ### quit This command will cause StratoVirt process to exit gracefully. diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index db7856618..916eada4d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -16,6 +16,7 @@ mod syscall; pub use crate::error::MachineError; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; use log::{error, info}; +use machine_manager::config::ShutdownAction; use machine_manager::event_loop::EventLoop; use std::borrow::Borrow; use std::collections::HashMap; @@ -995,6 +996,22 @@ impl MachineLifecycle for StdMachine { true } + fn powerdown(&self) -> bool { + if self.power_button.write(1).is_err() { + error!("ARM standard vm write power button failed"); + return false; + } + true + } + + fn get_shutdown_action(&self) -> ShutdownAction { + self.vm_config + .lock() + .unwrap() + .machine_config + .shutdown_action + } + fn reset(&mut self) -> bool { if self.reset_req.write(1).is_err() { error!("ARM standard vm write reset req failed"); diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index c2407a2c8..036e3cc94 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -475,6 +475,12 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); + add_args_to_config!( + (args.is_present("no-shutdown")), + vm_cfg, + add_no_shutdown, + bool + ); add_args_to_config!( (args.is_present("mem-prealloc")), vm_cfg, diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 57d0ac92b..0eb5fcf50 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -140,6 +140,18 @@ impl Default for PmuConfig { } } +impl Default for ShutdownAction { + fn default() -> Self { + ShutdownAction::ShutdownActionPoweroff + } +} + +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] +pub enum ShutdownAction { + ShutdownActionPoweroff, + ShutdownActionPause, +} + /// Config struct for machine-config. /// Contains some basic Vm config about cpu, memory, name. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -154,6 +166,7 @@ pub struct MachineConfig { pub max_cpus: u8, pub mem_config: MachineMemConfig, pub cpu_config: CpuConfig, + pub shutdown_action: ShutdownAction, } impl Default for MachineConfig { @@ -170,6 +183,7 @@ impl Default for MachineConfig { max_cpus: DEFAULT_MAX_CPUS, mem_config: MachineMemConfig::default(), cpu_config: CpuConfig::default(), + shutdown_action: ShutdownAction::default(), } } } @@ -396,6 +410,11 @@ impl VmConfig { pub fn enable_mem_prealloc(&mut self) { self.machine_config.mem_config.mem_prealloc = true; } + + pub fn add_no_shutdown(&mut self) -> bool { + self.machine_config.shutdown_action = ShutdownAction::ShutdownActionPause; + true + } } impl VmConfig { @@ -653,6 +672,7 @@ mod tests { max_cpus: MIN_NR_CPUS as u8, mem_config: memory_config, cpu_config: CpuConfig::default(), + shutdown_action: ShutdownAction::default(), }; assert!(machine_config.check().is_ok()); diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 64d540445..2eb36d6ca 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -16,6 +16,7 @@ use std::sync::Mutex; use once_cell::sync::Lazy; use strum::VariantNames; +use crate::config::ShutdownAction; use crate::qmp::qmp_schema::{ BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, DeviceAddArgument, DeviceProps, Events, GicCap, IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, @@ -91,6 +92,11 @@ pub trait MachineLifecycle { self.notify_lifecycle(KvmVmState::Running, KvmVmState::Shutdown) } + /// Close VM by power_button. + fn powerdown(&self) -> bool { + self.notify_lifecycle(KvmVmState::Running, KvmVmState::Shutdown) + } + /// Reset VM, stop running and restart a new VM. fn reset(&mut self) -> bool { self.notify_lifecycle(KvmVmState::Running, KvmVmState::Shutdown) @@ -103,6 +109,11 @@ pub trait MachineLifecycle { /// * `old` - The current `KvmVmState`. /// * `new` - The new `KvmVmState` expected to transform. fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool; + + /// Get shutdown_action to determine the poweroff operation. + fn get_shutdown_action(&self) -> ShutdownAction { + ShutdownAction::ShutdownActionPoweroff + } } /// `AddressSpace` access interface of `Machine`. diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 472961707..a60126ee6 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -423,6 +423,7 @@ fn qmp_command_exec( qmp_command.clone(); controller.lock().unwrap(); qmp_response; (stop, pause), (cont, resume), + (system_powerdown, powerdown), (system_reset, reset), (query_status, query_status), (query_version, query_version), diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 57e2cfa91..266d2961b 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -80,6 +80,12 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + system_powerdown { + #[serde(default)] + arguments: system_powerdown, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, system_reset { #[serde(default)] arguments: system_reset, @@ -486,6 +492,28 @@ impl Command for cont { } } +/// system_powerdown +/// +/// Requests that a guest perform a powerdown operation. +/// +/// # Examples +/// +/// ```test +/// -> { "execute": "system_powerdown" } +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct system_powerdown {} + +impl Command for system_powerdown { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + /// system_reset /// /// Reset guest VCPU execution. @@ -1309,6 +1337,13 @@ pub struct Stop {} #[serde(deny_unknown_fields)] pub struct Resume {} +/// Powerdown +/// +/// Emitted when the virtual machine powerdown execution +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct Powerdown {} + /// DeviceDeleted /// /// Emitted whenever the device removal completion is acknowledged by the guest. @@ -1356,6 +1391,12 @@ pub enum QmpEvent { data: Resume, timestamp: TimeStamp, }, + #[serde(rename = "POWERDOWN")] + Powerdown { + #[serde(default)] + data: Powerdown, + timestamp: TimeStamp, + }, #[serde(rename = "DEVICE_DELETED")] DeviceDeleted { data: DeviceDeleted, @@ -1515,8 +1556,8 @@ impl Command for query_version { /// ```text /// -> { "execute": "query-commands" } /// <- {"return":[{"name":"qmp_capabilities"},{"name":"quit"},{"name":"stop"}, -/// {"name":"cont"},{"name":"system_reset"},{"name":"device_add"},{"name":"device_del"}, -/// {"name":"netdev_add"},{"name":"netdev_del"},{"name":"query-hotpluggable-cpus"}, +/// {"name":"cont"},{"name":"system_powerdown"},{"name":"system_reset"},{"name":"device_add"}, +/// {"name":"device_del"},{"name":"netdev_add"},{"name":"netdev_del"},{"name":"query-hotpluggable-cpus"}, /// {"name":"query-cpus"},{"name":"query_status"},{"name":"getfd"},{"name":"blockdev_add"}, /// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"},{"name":"query_vnc"}, /// {"name":"migrate"},{"name":"query_migrate"},{"name":"query_version"}, -- Gitee From d633412d2595bab923a65896bd77655611c1e286 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Tue, 7 Mar 2023 22:29:40 +0800 Subject: [PATCH 0892/2187] MST: add ged mst. 1. add ged mst, 3. adapt acpi testcase. Signed-off-by:mayunlong --- tests/mod_test/tests/acpi_test.rs | 57 ++++++++++++----- tests/mod_test/tests/ged_test.rs | 103 ++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 tests/mod_test/tests/ged_test.rs diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index a54f45aab..a5fdb7197 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -23,6 +23,25 @@ use std::{cell::RefCell, mem, rc::Rc}; use mod_test::libdriver::fwcfg::bios_args; use mod_test::libtest::{test_init, TestState}; +// Now dsdt table data length is 877. +const DSDT_TABLE_DATA_LENGTH: u32 = 877; +// Now fadt table data length is 276. +const FADT_TABLE_DATA_LENGTH: u32 = 276; +// Now madt table data length is 744. +const MADT_TABLE_DATA_LENGTH: u32 = 744; +// Now gtdt table data length is 96. +const GTDT_TABLE_DATA_LENGTH: u32 = 96; +// Now iort table data length is 128. +const IORT_TABLE_DATA_LENGTH: u32 = 128; +// Now spcr table data length is 80. +const SPCR_TABLE_DATA_LENGTH: u32 = 80; +// Now mcfg table data length is 60. +const MCFG_TABLE_DATA_LENGTH: u32 = 60; +// Now acpi tables data length is 3005(cpu number is 8). +const ACPI_TABLES_DATA_LENGTH_8: usize = 3005; +// Now acpi tables data length is 29354(cpu number is 200). +const ACPI_TABLES_DATA_LENGTH_200: usize = 29354; + fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { let file_name = "etc/acpi/rsdp"; let mut read_data: Vec = Vec::with_capacity(mem::size_of::()); @@ -54,12 +73,12 @@ fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { fn check_dsdt(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "DSDT"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 736); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), DSDT_TABLE_DATA_LENGTH); // Check length } fn check_fadt(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "FACP"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 276); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), FADT_TABLE_DATA_LENGTH); // Check length assert_eq!(LittleEndian::read_i32(&data[112..]), 0x10_0500); // Enable HW_REDUCED_ACPI bit assert_eq!(LittleEndian::read_u16(&data[129..]), 0x3); // ARM Boot Architecture Flags @@ -68,7 +87,7 @@ fn check_fadt(data: &[u8]) { fn check_madt(data: &[u8], cpu: u8) { assert_eq!(String::from_utf8_lossy(&data[..4]), "APIC"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 744); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), MADT_TABLE_DATA_LENGTH); // Check length let mut offset = 44; @@ -108,7 +127,7 @@ fn check_madt(data: &[u8], cpu: u8) { fn check_gtdt(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "GTDT"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 96); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), GTDT_TABLE_DATA_LENGTH); // Check length assert_eq!(LittleEndian::read_u32(&data[48..]), 29); // Secure EL1 interrupt assert_eq!(LittleEndian::read_u32(&data[52..]), 0); // Secure EL1 flags @@ -122,7 +141,7 @@ fn check_gtdt(data: &[u8]) { fn check_iort(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "IORT"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 128); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), IORT_TABLE_DATA_LENGTH); // Check length // Check IORT nodes is 2: ITS group node and Root Complex Node. assert_eq!(LittleEndian::read_u32(&data[36..]), 2); @@ -142,7 +161,7 @@ fn check_iort(data: &[u8]) { fn check_spcr(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "SPCR"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 80); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), SPCR_TABLE_DATA_LENGTH); // Check length assert_eq!(data[36], 3); // Interface type: ARM PL011 UART assert_eq!(data[41], 8); // Bit width of AcpiGenericAddress @@ -152,7 +171,7 @@ fn check_spcr(data: &[u8]) { MEM_LAYOUT[LayoutEntryType::Uart as usize].0 ); assert_eq!(data[52], 1_u8 << 3); // Interrupt Type: Arm GIC interrupu - assert_eq!(LittleEndian::read_u32(&data[54..]), 65); // Irq number used by the UART + assert_eq!(LittleEndian::read_u32(&data[54..]), 66); // Irq number used by the UART assert_eq!(data[58], 3); // Set baud rate: 3 = 9600 assert_eq!(data[60], 1); // Stop bit assert_eq!(data[61], 2); // Hardware flow control @@ -162,7 +181,7 @@ fn check_spcr(data: &[u8]) { fn check_mcfg(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "MCFG"); - assert_eq!(LittleEndian::read_u32(&data[4..]), 60); // Check length + assert_eq!(LittleEndian::read_u32(&data[4..]), MCFG_TABLE_DATA_LENGTH); // Check length assert_eq!( LittleEndian::read_u64(&data[44..]), @@ -250,12 +269,16 @@ fn check_pptt(data: &[u8]) { fn test_tables(test_state: &TestState, alloc: &mut GuestAllocator, xsdt_addr: usize, cpu: u8) { let file_name = "etc/acpi/tables"; - // Now acpi tables data length is 2864. - let mut read_data: Vec = Vec::with_capacity(2864); + let mut read_data: Vec = Vec::with_capacity(ACPI_TABLES_DATA_LENGTH_8); // Select FileDir entry and read it. - let file_size = test_state.fw_cfg_read_file(alloc, file_name, &mut read_data, 2864); - assert_eq!(file_size, 2864); + let file_size = test_state.fw_cfg_read_file( + alloc, + file_name, + &mut read_data, + ACPI_TABLES_DATA_LENGTH_8 as u32, + ); + assert_eq!(file_size, ACPI_TABLES_DATA_LENGTH_8 as u32); // Check XSDT assert_eq!( @@ -325,11 +348,15 @@ fn check_madt_of_two_gicr( cpus: usize, ) { let file_name = "etc/acpi/tables"; - // Now acpi tables data length is 29212. - let mut read_data: Vec = Vec::with_capacity(29212); + let mut read_data: Vec = Vec::with_capacity(ACPI_TABLES_DATA_LENGTH_200); // Select FileDir entry and read it. - test_state.fw_cfg_read_file(alloc, file_name, &mut read_data, 29212); + test_state.fw_cfg_read_file( + alloc, + file_name, + &mut read_data, + ACPI_TABLES_DATA_LENGTH_200 as u32, + ); // XSDT entry: An array of 64-bit physical addresses that point to other DESCRIPTION_HEADERs. // DESCRIPTION_HEADERs: DSDT, FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT diff --git a/tests/mod_test/tests/ged_test.rs b/tests/mod_test/tests/ged_test.rs new file mode 100644 index 000000000..c6a4a4449 --- /dev/null +++ b/tests/mod_test/tests/ged_test.rs @@ -0,0 +1,103 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use mod_test::libtest::{test_init, TestState}; + +use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +pub const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::Ged as usize].0; +const ADD_ADDRESS: u64 = 1; + +fn ged_read_evt(ts: &TestState) -> u32 { + ts.readl(GED_ADDR_BASE) +} + +fn ged_read_abnormal(ts: &TestState) -> u32 { + ts.readl(GED_ADDR_BASE + ADD_ADDRESS) +} + +fn ged_write_evt(ts: &TestState, val: u32) { + ts.writel(GED_ADDR_BASE, val); +} + +fn ged_args(base_args: &mut Vec<&str>) { + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + base_args.append(&mut args); + args = "-no-shutdown".split(' ').collect(); + base_args.append(&mut args); + args = "-drive file=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true" + .split(' ') + .collect(); + base_args.append(&mut args); +} + +fn set_up() -> TestState { + let mut args: Vec<&str> = Vec::new(); + ged_args(&mut args); + test_init(args) +} + +/// Test the read and write functions of a ged device. +/// +/// Steps +/// 1. Send qmp command "system_powerdown" +/// 2. Read ged event. +/// 3. Read abnormal address, except 0. +/// 4. Write event and read, excepy 0 because +/// ged can't write. +#[test] +#[cfg(target_arch = "aarch64")] +fn test_shutdown() { + let mut ts = set_up(); + + ts.qmp("{\"execute\": \"system_powerdown\"}"); + + let event = ged_read_evt(&ts); + assert_eq!(event, 1); + + let addr = ged_read_abnormal(&ts); + assert_eq!(addr, 0); + + ged_write_evt(&ts, 1); + let event = ged_read_evt(&ts); + assert_eq!(event, 0); + + ts.stop(); +} + +/// Verify that the restart function is normal. +/// +/// Steps +/// 1. Send qmp command "system_powerdown" and +/// "system_reset" to achieve "reboot" +/// 2. Read ged event. +/// 3. Send qmp command "query-status" to get +/// the status of vm, except "running". +#[test] +#[cfg(target_arch = "aarch64")] +fn test_reboot() { + let mut ts = set_up(); + + ts.qmp("{\"execute\": \"system_powerdown\"}"); + ts.qmp_read(); + + let event = ged_read_evt(&ts); + assert_eq!(event, 1); + + ts.qmp("{\"execute\": \"system_reset\"}"); + ts.qmp_read(); + + let value = ts.qmp("{\"execute\": \"query-status\"}"); + let status = value["return"]["status"].as_str().unwrap().to_string(); + assert_eq!(status, "running".to_string()); + + ts.stop(); +} -- Gitee From be10ba25fe9ea5822d16d88393da63e8e0a83854 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 8 Mar 2023 11:47:49 +0800 Subject: [PATCH 0893/2187] Ged: remove useless code Remove useless code. Signed-off-by: mayunlong --- devices/src/acpi/ged.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 10f2d378f..2d8fa091e 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -115,9 +115,7 @@ impl Ged { evt_fd .write(1) .unwrap_or_else(|e| error!("ged: failed to write interrupt eventfd ({}).", e)); - return; } - error!("ged: failed to get interrupt event fd."); } } -- Gitee From 8d170fe9d2620b114d5302a10431f06e52989960 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 Mar 2023 18:35:49 +0800 Subject: [PATCH 0894/2187] util/tap: using macro instead of numbuer Using macro IFNAME_SIZE instead of 16. Signed-off-by: Yan Wang --- util/src/tap.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/src/tap.rs b/util/src/tap.rs index dbe5808a6..189e8010d 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -32,6 +32,7 @@ pub const IFF_MULTI_QUEUE: u16 = 0x100; const IFF_NO_PI: u16 = 0x1000; const IFF_VNET_HDR: u16 = 0x4000; const TUNTAP_PATH: &str = "/dev/net/tun"; +const IFNAME_SIZE: usize = 16; ioctl_iow_nr!(TUNSETIFF, 84, 202, ::std::os::raw::c_int); ioctl_ior_nr!(TUNGETFEATURES, 84, 207, ::std::os::raw::c_uint); @@ -40,7 +41,7 @@ ioctl_iow_nr!(TUNSETVNETHDRSZ, 84, 216, ::std::os::raw::c_int); #[repr(C)] pub struct IfReq { - ifr_name: [u8; 16], + ifr_name: [u8; IFNAME_SIZE], ifr_flags: u16, } @@ -53,11 +54,11 @@ impl Tap { let file; if let Some(name) = name { - if name.len() > 15 { + if name.len() > IFNAME_SIZE - 1 { return Err(anyhow!("Open tap {} failed, name too long.", name)); } - let mut ifr_name = [0_u8; 16]; + let mut ifr_name = [0_u8; IFNAME_SIZE]; let (left, _) = ifr_name.split_at_mut(name.len()); left.copy_from_slice(name.as_bytes()); -- Gitee From 8764507f0ac6c47d097cd7398940d27fdd898051 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 Mar 2023 18:38:15 +0800 Subject: [PATCH 0895/2187] virtio-net: optimize some code and add unit test 1. Remove unnecessary check point. 2. Remove the redundant judgement. 3. Check the queue status in handle_tx(). 4. Add ut for some functions. Signed-off-by: Yan Wang --- virtio/src/net.rs | 192 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 150 insertions(+), 42 deletions(-) diff --git a/virtio/src/net.rs b/virtio/src/net.rs index 50913b369..aacadb1b8 100644 --- a/virtio/src/net.rs +++ b/virtio/src/net.rs @@ -744,23 +744,20 @@ impl NetIoHandler { mem_space: &Arc, cache: &Option, elem_iovecs: &[ElemIovec], - ) -> Result> { + ) -> Vec { let mut iovecs = Vec::new(); for elem_iov in elem_iovecs.iter() { + // elem_iov.addr has been checked in pop_avail(). let host_addr = mem_space .get_host_address_from_cache(elem_iov.addr, cache) - .unwrap_or(0); - if host_addr != 0 { - let iovec = libc::iovec { - iov_base: host_addr as *mut libc::c_void, - iov_len: elem_iov.len as libc::size_t, - }; - iovecs.push(iovec); - } else { - bail!("Failed to get host address for {}", elem_iov.addr.0); - } + .unwrap(); + let iovec = libc::iovec { + iov_base: host_addr as *mut libc::c_void, + iov_len: elem_iov.len as libc::size_t, + }; + iovecs.push(iovec); } - Ok(iovecs) + iovecs } fn handle_rx(&mut self) -> Result<()> { @@ -769,27 +766,15 @@ impl NetIoHandler { if !queue.is_enabled() { return Ok(()); } + let mut rx_packets = 0; while let Some(tap) = self.tap.as_mut() { - if queue.vring.avail_ring_len(&self.mem_space)? == 0 { - self.rx.queue_full = true; - break; - } - - rx_packets += 1; - if rx_packets >= self.queue_size { - self.rx - .queue_evt - .write(1) - .with_context(|| "Failed to trigger rx queue event".to_string())?; - break; - } - let elem = queue .vring .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net rx")?; if elem.desc_num == 0 { + self.rx.queue_full = true; break; } else if elem.in_iovec.is_empty() { bail!("The lengh of in iovec is 0"); @@ -798,8 +783,7 @@ impl NetIoHandler { &self.mem_space, queue.vring.get_cache(), &elem.in_iovec, - ) - .with_context(|| "Failed to get libc iovecs for net rx")?; + ); if MigrationManager::is_active() { // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. @@ -860,6 +844,15 @@ impl NetIoHandler { })?; self.trace_send_interrupt("Net".to_string()); } + + rx_packets += 1; + if rx_packets >= self.queue_size { + self.rx + .queue_evt + .write(1) + .with_context(|| "Failed to trigger rx queue event".to_string())?; + break; + } } Ok(()) @@ -892,6 +885,9 @@ impl NetIoHandler { fn handle_tx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); + if !queue.is_enabled() { + return Ok(()); + } let mut tx_packets = 0; loop { let elem = queue @@ -904,21 +900,11 @@ impl NetIoHandler { bail!("The lengh of out iovec is 0"); } - tx_packets += 1; - if tx_packets >= self.queue_size { - self.tx - .queue_evt - .write(1) - .with_context(|| "Failed to trigger tx queue event".to_string())?; - break; - } - let iovecs = NetIoHandler::get_libc_iovecs( &self.mem_space, queue.vring.get_cache(), &elem.out_iovec, - ) - .with_context(|| "Failed to get libc iovecs for net tx")?; + ); let tap_fd = if let Some(tap) = self.tap.as_mut() { tap.as_raw_fd() as libc::c_int } else { @@ -950,6 +936,14 @@ impl NetIoHandler { })?; self.trace_send_interrupt("Net".to_string()); } + tx_packets += 1; + if tx_packets >= self.queue_size { + self.tx + .queue_evt + .write(1) + .with_context(|| "Failed to trigger tx queue event".to_string())?; + break; + } } Ok(()) @@ -1106,6 +1100,7 @@ impl EventNotifierHelper for NetIoHandler { if locked_net_io.device_broken.load(Ordering::SeqCst) { return None; } + if let Err(ref e) = locked_net_io.handle_rx() { error!("Failed to handle rx(tap event), {:?}", e); report_virtio_error( @@ -1553,9 +1548,6 @@ impl VirtioDevice for Net { mut queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); - if queue_num == 0 { - bail!("Length of queues is 0 when activating virtio net"); - } let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); self.ctrl_info = Some(ctrl_info.clone()); let driver_features = self.state.lock().unwrap().driver_features; @@ -1795,4 +1787,120 @@ mod tests { let mut data: Vec = vec![0; len as usize]; assert_eq!(net.write_config(offset, &mut data).is_ok(), false); } + + #[test] + fn test_net_create_tap() { + // Test None net_fds and host_dev_name. + assert!(create_tap(None, None, 16).unwrap().is_none()); + + // Test create tap with net_fds and host_dev_name. + let net_fds = vec![32, 33]; + let tap_name = "tap0"; + if let Err(err) = create_tap(Some(&net_fds), Some(&tap_name), 1) { + let err_msg = format!("Failed to create tap, index is 0"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + + // Test create tap with empty net_fds. + if let Err(err) = create_tap(Some(&vec![]), None, 1) { + let err_msg = format!("Failed to get fd from index 0"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + + // Test create tap with tap_name which is not exist. + if let Err(err) = create_tap(None, Some("the_tap_is_not_exist"), 1) { + let err_msg = + format!("Failed to create tap with name the_tap_is_not_exist, index is 0"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + } + + #[test] + fn test_net_filter_vlan() { + let mut ctrl_info = CtrlInfo::new(Arc::new(Mutex::new(VirtioNetState::default()))); + ctrl_info.rx_mode.promisc = false; + let mut buf = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, + ]; + // It has no vla vid, the packet is filtered. + assert_eq!(ctrl_info.filter_packets(&buf), true); + + // It has valid vlan id, the packet is not filtered. + let vid: u16 = 1023; + buf[ETHERNET_HDR_LENGTH] = u16::to_be_bytes(vid)[0]; + buf[ETHERNET_HDR_LENGTH + 1] = u16::to_be_bytes(vid)[1]; + ctrl_info.vlan_map.insert(vid >> 5, 1 << (vid & 0x1f)); + assert_eq!(ctrl_info.filter_packets(&buf), false); + } + + #[test] + fn test_net_config_space() { + let mut net_config = VirtioNetConfig::default(); + // Parsing the normal mac address. + let mac = "52:54:00:12:34:56"; + let ret = build_device_config_space(&mut net_config, &mac); + assert_eq!(ret, 1 << VIRTIO_NET_F_MAC); + + // Parsing the abnormale mac address. + let mac = "52:54:00:12:34:"; + let ret = build_device_config_space(&mut net_config, &mac); + assert_eq!(ret, 0); + } + + #[test] + fn test_mac_table() { + let mut mac = FIRST_DEFAULT_MAC; + // Add mac to mac table. + mark_mac_table(&mac, true); + assert_eq!( + USED_MAC_TABLE.lock().unwrap()[mac[MAC_ADDR_LEN - 1] as usize], + 1 + ); + // Delete mac from mac table. + mark_mac_table(&mac, false); + assert_eq!( + USED_MAC_TABLE.lock().unwrap()[mac[MAC_ADDR_LEN - 1] as usize], + 0 + ); + + // Mac not in the default mac range. + mac[0] += 1; + mark_mac_table(&mac, true); + assert_eq!( + USED_MAC_TABLE.lock().unwrap()[mac[MAC_ADDR_LEN - 1] as usize], + 0 + ); + + // Test no free mac in mac table. + for i in FIRST_DEFAULT_MAC[MAC_ADDR_LEN - 1]..MAX_MAC_ADDR_NUM as u8 { + USED_MAC_TABLE.lock().unwrap()[i as usize] = 1; + } + assert!(get_default_mac_addr().is_err()); + // Recover it. + for i in FIRST_DEFAULT_MAC[MAC_ADDR_LEN - 1]..MAX_MAC_ADDR_NUM as u8 { + USED_MAC_TABLE.lock().unwrap()[i as usize] = 0; + } + } + + #[test] + fn test_iothread() { + let mut net = Net::default(); + net.net_cfg.iothread = Some("iothread".to_string()); + if let Err(err) = net.realize() { + let err_msg = format!( + "IOThread {:?} of Net is not configured in params.", + net.net_cfg.iothread + ); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + } } -- Gitee From 1de77efdc5c4a4c433f9f7b6e9476f1fa2259fbf Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 Mar 2023 18:43:09 +0800 Subject: [PATCH 0896/2187] net_test: add several testcase and optimize the test code 1. Fill rx vq for multi-queue scene. 2. For abnormal control rx test, send twice command without ack. 3. Add several test for abnormal rx/tx test: 1) send tx packet without in_iovec; 2) send rx/tx request without enabled virtqueue; 3) add multi-queue test; Signed-off-by: Yan Wang --- tests/mod_test/src/libdriver/virtio.rs | 2 +- tests/mod_test/tests/net_test.rs | 275 +++++++++++++++++++------ 2 files changed, 217 insertions(+), 60 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 22a4afc57..547217bd0 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -164,7 +164,7 @@ pub static VRING_DESC_SIZE: u64 = size_of::() as u64; pub struct VringAvail { flags: u16, idx: u16, - ring: Vec, + pub ring: Vec, } #[repr(C, packed(4))] diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index e6815d7d7..a286d41b8 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -24,11 +24,11 @@ use util::offset_of; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::{ - TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VringUsed, VringUsedElem, - VIRTIO_CONFIG_S_DRIVER_OK, VIRTIO_CONFIG_S_NEEDS_RESET, VIRTIO_F_VERSION_1, - VIRTIO_RING_F_EVENT_IDX, VRING_DESC_SIZE, + TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VringAvail, VringDesc, VringUsed, + VringUsedElem, VIRTIO_CONFIG_S_DRIVER_OK, VIRTIO_CONFIG_S_NEEDS_RESET, VIRTIO_F_VERSION_1, + VIRTIO_RING_F_EVENT_IDX, VRING_DESC_F_WRITE, VRING_DESC_SIZE, }; -use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; use mod_test::libtest::{test_init, TestState}; /// Device handles packets with partial checksum. @@ -350,6 +350,7 @@ fn execute_cmd(cmd: String) { let output = cmd_exe .output() .expect(format!("Failed to execute {}", cmd).as_str()); + println!("{:?}", args); assert!(output.status.success()); } @@ -525,8 +526,9 @@ fn init_net_device( let vqs = net .borrow_mut() .init_virtqueue(test_state.clone(), alloc.clone(), num_queues); - // vqs[0] is rx queue. - fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); + for i in 0..num_queues / 2 { + fill_rx_vq(test_state.clone(), alloc.clone(), vqs[i * 2].clone()); + } net.borrow().set_driver_ok(); vqs @@ -535,7 +537,7 @@ fn init_net_device( fn check_arp_mac( net: Rc>, test_state: Rc>, - vqs: Vec>>, + vq: Rc>, arp_request: &[u8], need_reply: bool, ) { @@ -546,7 +548,7 @@ fn check_arp_mac( loop { if need_reply { assert!(time::Instant::now() - start_time < timeout_us); - if !net.borrow().queue_was_notified(vqs[0].clone()) { + if !net.borrow().queue_was_notified(vq.clone()) { continue; } } else if time::Instant::now() - start_time > timeout_us_no_reply { @@ -555,22 +557,22 @@ fn check_arp_mac( let idx = test_state .borrow() - .readw(vqs[0].borrow().used + offset_of!(VringUsed, idx) as u64); + .readw(vq.borrow().used + offset_of!(VringUsed, idx) as u64); for i in start..idx as u64 { let len = test_state.borrow().readw( - vqs[0].borrow().used + vq.borrow().used + offset_of!(VringUsed, ring) as u64 + i * USED_ELEM_SIZE + offset_of!(VringUsedElem, len) as u64, ); if len == arp_request.len() as u16 { let id = test_state.borrow().readw( - vqs[0].borrow().used + offset_of!(VringUsed, ring) as u64 + i * USED_ELEM_SIZE, + vq.borrow().used + offset_of!(VringUsed, ring) as u64 + i * USED_ELEM_SIZE, ); let addr = test_state .borrow() - .readq(vqs[0].borrow().desc + id as u64 * VRING_DESC_SIZE); + .readq(vq.borrow().desc + id as u64 * VRING_DESC_SIZE); let packets = test_state.borrow().memread(addr, len as u64); let src_mac_pos = VIRTIO_NET_HDR_SIZE + ETHERNET_HDR_SIZE + ARP_HDR_SIZE; let dst_mac_pos = src_mac_pos + 10; @@ -639,7 +641,7 @@ fn send_request( net: Rc>, test_state: Rc>, alloc: Rc>, - vqs: Vec>>, + vq: Rc>, request: &[u8], ) { let length = request.len() as u64; @@ -663,13 +665,13 @@ fn send_request( .borrow() .memwrite(addr + offset, &request[offset as usize..]); } - let free_head = vqs[1] + let free_head = vq .borrow_mut() .add(test_state.clone(), addr, request.len() as u32, false); - net.borrow().virtqueue_notify(vqs[1].clone()); + net.borrow().virtqueue_notify(vq.clone()); net.borrow().poll_used_elem( test_state.clone(), - vqs[1].clone(), + vq, free_head, TIMEOUT_US, &mut None, @@ -689,18 +691,30 @@ fn send_arp_request( net.clone(), test_state.clone(), alloc.clone(), - vqs.clone(), + vqs[1].clone(), &arp_request, ); check_arp_mac( net.clone(), test_state.clone(), - vqs.clone(), + vqs[0].clone(), &arp_request, need_reply, ); } +fn check_device_status(net: Rc>, status: u8) { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + loop { + if net.borrow().get_status() & status > 0 { + break; + } + sleep(time::Duration::from_millis(50)); + assert!(time::Instant::now() - start_time < timeout_us); + } +} + /// Send and receive packet test. /// TestStep: /// 1. Init device. @@ -1602,7 +1616,7 @@ fn virtio_net_ctrl_rx_test() { /// Test the control abnormal command. /// TestStep: /// 1. Init device with control vq. -/// 2. Test the control rx command without ack, expect NEEDS_RESET. +/// 2. Test the control rx command without ack(2 times), expect NEEDS_RESET. /// 3. Destroy device. /// Expect: /// 1/2/3: success. @@ -1624,37 +1638,50 @@ fn virtio_net_ctrl_abnormal_test() { let ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, 0); let ctrl_data = &ctrl_rx_info.as_bytes(); - let ctrl_vq = &vqs[2]; - let addr = alloc - .borrow_mut() - .alloc(ctrl_data.len() as u64) - .try_into() - .unwrap(); - test_state.borrow().memwrite(addr, &ctrl_data); + // The first request cause device needs reset. + // The second request test if the device can handle request. + let test_num = 2; + for i in 0..test_num { + let ctrl_vq = &vqs[2]; + let addr = alloc + .borrow_mut() + .alloc(ctrl_data.len() as u64) + .try_into() + .unwrap(); + test_state.borrow().memwrite(addr, &ctrl_data); - let data_entries: Vec = vec![ - TestVringDescEntry { - data: addr, - len: 1, - write: false, - }, - TestVringDescEntry { - data: addr + 1, - len: 1, - write: false, - }, - TestVringDescEntry { - data: addr + 2, - len: ctrl_data.len() as u32 - 3, - write: false, - }, - ]; - ctrl_vq - .borrow_mut() - .add_chained(test_state.clone(), data_entries); - net.borrow() - .kick_virtqueue(test_state.clone(), ctrl_vq.clone()); - assert!(net.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); + // ctrl_rx_info.switch: u8 + let mut data_len = 1; + if i == test_num - 1 { + // ctrl_rx_info.switch and ctrl_rx_info.ack + data_len = 2; + } + let data_entries: Vec = vec![ + // ctrl_rx_info.ctrl_hdr.class: u8 + TestVringDescEntry { + data: addr, + len: 1, + write: false, + }, + // ctrl_rx_info.ctrl_hdr.cmd: u8 + TestVringDescEntry { + data: addr + 1, + len: 1, + write: false, + }, + TestVringDescEntry { + data: addr + 2, + len: data_len, + write: false, + }, + ]; + ctrl_vq + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + net.borrow() + .kick_virtqueue(test_state.clone(), ctrl_vq.clone()); + check_device_status(net.clone(), VIRTIO_CONFIG_S_NEEDS_RESET); + } tear_down( net.clone(), @@ -1749,6 +1776,7 @@ fn virtio_net_abnormal_rx_tx_test() { /// 2. Test the rx/tx request: /// 1) handle rx error /// 2) handle tx error +/// 3) test tx packet with no in_iovec /// 3. Destroy device. /// Expect: /// 1/2/3: success. @@ -1758,7 +1786,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { let queue_pairs: u16 = 1; let queues: usize = 2 * queue_pairs as usize + 1; - for i in 0..2 { + for i in 0..3 { let (net, test_state, alloc) = set_up(id, false, queue_pairs, false); net.borrow_mut().reset(); @@ -1774,8 +1802,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { .init_virtqueue(test_state.clone(), alloc.clone(), queues); fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); - // Test receive packet failed. - // Set u16::MAX to rx->avail->ring[0]. + // Test rx packet failed with invalid avail->ring[i]. if i == 0 { test_state .borrow() @@ -1793,13 +1820,28 @@ fn virtio_net_abnormal_rx_tx_test_2() { .borrow_mut() .add(test_state.clone(), addr, length as u32, false); if i == 1 { - test_state - .borrow() - .writew(vqs[1].borrow().avail + 4, u16::MAX); + // Test tx packet failed with invalid avail->ring[i]. + test_state.borrow().writew( + vqs[1].borrow().avail + offset_of!(VringAvail, ring) as u64, + u16::MAX, + ); + } else if i == 2 { + // Test tx packet with no in_iovec. + test_state.borrow().writew( + vqs[1].borrow().desc + offset_of!(VringDesc, flags) as u64, + VRING_DESC_F_WRITE, + ); } net.borrow().virtqueue_notify(vqs[1].clone()); - sleep(time::Duration::from_millis(500)); - assert!(net.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); + // Check if it will affect the stratovirt when device broken. + net.borrow().virtqueue_notify(vqs[0].clone()); + check_device_status(net.clone(), VIRTIO_CONFIG_S_NEEDS_RESET); + sleep(time::Duration::from_millis(5000)); + + let ret = test_state + .borrow() + .qmp("{\"execute\": \"qmp_capabilities\"}"); + assert_eq!(*ret.get("return").unwrap(), json!({})); tear_down( net.clone(), @@ -1903,7 +1945,7 @@ fn virtio_net_send_abnormal_packet() { net.clone(), test_state.clone(), alloc.clone(), - vqs.clone(), + vqs[1].clone(), &data_bytes, ); } @@ -1917,7 +1959,7 @@ fn virtio_net_send_abnormal_packet() { net.clone(), test_state.clone(), alloc.clone(), - vqs.clone(), + vqs[1].clone(), &data_bytes, ); } @@ -1936,3 +1978,118 @@ fn virtio_net_send_abnormal_packet() { false, ); } + +/// Send and receive packet test with mq. +/// TestStep: +/// 1. Init device with mq. +/// 2. Send ARP packet and check the reply. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_rx_tx_mq_test() { + let id = 13 * TEST_MAC_ADDR_NUMS; + let num_queues = 2; + let (net, test_state, alloc) = set_up(id, true, num_queues, false); + + // Three virtqueues: tx/rx/ctrl. + let vqs = init_net_device( + net.clone(), + test_state.clone(), + alloc.clone(), + DEFAULT_NET_FEATURES, + 2 * num_queues as usize + 1, + ); + + for i in 0..num_queues { + send_request( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs[i as usize * 2 + 1].clone(), + &get_arp_request(id + i as u8 * TEST_MAC_ADDR_NUMS).as_bytes(), + ); + } + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + true, + ); +} + +/// Test the abnormal rx/tx request 3. +/// TestStep: +/// 1. Init device. +/// 2. Test the rx/tx request with not enable virtqueue: +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtio_net_abnormal_rx_tx_test_3() { + let id = 14 * TEST_MAC_ADDR_NUMS; + let (net, test_state, alloc) = set_up(id, false, 0, false); + + net.borrow_mut().reset(); + net.borrow_mut().set_acknowledge(); + net.borrow_mut().set_driver(); + net.borrow_mut().negotiate_features(DEFAULT_NET_FEATURES); + net.borrow_mut().set_features_ok(); + net.borrow_mut().pci_dev.enable_msix(None); + net.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + + let mut vqs = Vec::new(); + // Init virtqueue, but don't enable it. + for i in 0..3 { + let vq = Rc::new(RefCell::new(TestVirtQueue::new())); + vq.borrow_mut().setup(&*net.borrow(), alloc.clone(), i); + vq.borrow().vring_init(test_state.clone()); + + let desc = vq.borrow().desc; + let avail = vq.borrow().avail; + let used = vq.borrow().used; + net.borrow().activate_queue(desc, avail, used); + + let notify_off = net.borrow().pci_dev.io_readw( + net.borrow().bar, + net.borrow().common_base as u64 + + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, + ); + + vq.borrow_mut().queue_notify_off = net.borrow().notify_base as u64 + + notify_off as u64 * net.borrow().notify_off_multiplier as u64; + + net.borrow() + .setup_virtqueue_intr((i + 1) as u16, alloc.clone(), vq.clone()); + vqs.push(vq); + } + fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); + net.borrow().set_driver_ok(); + + let request = get_arp_request(id); + let length = request.as_bytes().len() as u64; + let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + test_state.borrow().memwrite(addr, &request.as_bytes()); + vqs[1] + .borrow_mut() + .add(test_state.clone(), addr, length as u32, false); + net.borrow().virtqueue_notify(vqs[1].clone()); + sleep(time::Duration::from_millis(500)); + let used_idx = test_state + .borrow() + .readw(vqs[1].borrow().used + offset_of!(VringUsed, idx) as u64); + assert_eq!(used_idx, 0); + + tear_down( + net.clone(), + test_state.clone(), + alloc.clone(), + vqs, + id, + false, + ); +} -- Gitee From 72814821a4c2ab6debb2e167cf93ee748cdbbede Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 9 Mar 2023 20:11:55 +0800 Subject: [PATCH 0897/2187] mst: add abnormal request test cases for virtio block Add some test cases: 1. small size desc 2. miss data desc 3. all out desc 4. zero data length 5. only status desc Signed-off-by: yezengruan --- tests/mod_test/tests/block_test.rs | 136 +++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index ca125f8c8..daa794279 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -32,6 +32,7 @@ use mod_test::utils::{create_img, TEST_IMAGE_SIZE}; use std::cell::RefCell; use std::rc::Rc; use std::time::{Duration, Instant}; +use std::{thread, time}; use util::aio::{aio_probe, AioEngine}; use util::num_ops::round_up; use util::offset_of; @@ -1053,6 +1054,7 @@ fn blk_abnormal_req() { let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + // Desc: req_hdr length 8, data length 256. let mut data_entries: Vec = Vec::with_capacity(3); data_entries.push(TestVringDescEntry { data: req_addr, @@ -1087,6 +1089,7 @@ fn blk_abnormal_req() { let status = test_state.borrow().readb(req_addr + 264); assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: req_hdr length 32. let mut data_entries: Vec = Vec::with_capacity(3); data_entries.push(TestVringDescEntry { data: req_addr, @@ -1121,6 +1124,7 @@ fn blk_abnormal_req() { let status = test_state.borrow().readb(req_addr + 544); assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: data length 256. let mut data_entries: Vec = Vec::with_capacity(3); data_entries.push(TestVringDescEntry { data: req_addr, @@ -1155,6 +1159,138 @@ fn blk_abnormal_req() { let status = test_state.borrow().readb(req_addr + 272); assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: data length 4, small size desc. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 4, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 20, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + + let status = test_state.borrow().readb(req_addr + 20); + assert_ne!(status, VIRTIO_BLK_S_OK); + + // Desc: miss data. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); + + let status = test_state.borrow().readb(req_addr + 16); + assert_ne!(status, VIRTIO_BLK_S_OK); + + // Desc: all 'out' desc. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: true, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 512, + write: true, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 528, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); + + let status = test_state.borrow().readb(req_addr + 528); + assert_ne!(status, VIRTIO_BLK_S_OK); + + // Desc: data length 0. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 0, + write: true, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 20, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); + + let status = test_state.borrow().readb(req_addr + 20); + assert_ne!(status, VIRTIO_BLK_S_OK); + + // Desc: only status desc. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); + + let status = test_state.borrow().readb(req_addr); + assert_ne!(status, VIRTIO_BLK_S_OK); + tear_down( blk.clone(), test_state.clone(), -- Gitee From 569d8b6725eaf8b2c6155787f53e095d2b411583 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 10 Mar 2023 10:42:19 +0800 Subject: [PATCH 0898/2187] virtio-block: code clean up Replace bail() with with_context(). Signed-off-by: yezengruan --- virtio/src/block.rs | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/virtio/src/block.rs b/virtio/src/block.rs index 9af7e97f3..88a776712 100644 --- a/virtio/src/block.rs +++ b/virtio/src/block.rs @@ -139,30 +139,27 @@ impl AioCompleteCb { } let mut queue_lock = self.queue.lock().unwrap(); - if let Err(ref e) = queue_lock + queue_lock .vring .add_used(&self.mem_space, req.desc_index, req.in_len) - { - bail!( - "Failed to add used ring(blk io completion), index {}, len {} {:?}", - req.desc_index, - req.in_len, - e, - ); - } + .with_context(|| { + format!( + "Failed to add used ring(blk io completion), index {}, len {}", + req.desc_index, req.in_len + ) + })?; if queue_lock .vring .should_notify(&self.mem_space, self.driver_features) { - if let Err(e) = - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) - { - bail!( - "Failed to trigger interrupt(blk io completion), error is {:?}", - e - ); - } + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "blk io completion", + VirtioInterruptType::Vring + )) + })?; self.trace_send_interrupt("Block".to_string()); } Ok(()) @@ -477,11 +474,6 @@ impl BlockIoHandler { queue.vring.push_back(); break; } - } else { - bail!( - "IOThread {:?} of Block is not found in cmdline.", - self.iothread, - ); }; } -- Gitee From 565f2e3b02ea306b2ec83ace213cfc77a4338e90 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 11 Mar 2023 05:19:00 +0800 Subject: [PATCH 0899/2187] vhost_user_fs: don't compile lib crate Compile bin crate for vhost_user_fs only and don't compile lib crate. Signed-off-by: liuxiangdong --- vhost_user_fs/src/cmdline.rs | 18 +++++++------ vhost_user_fs/src/fs.rs | 26 ++++++++++--------- vhost_user_fs/src/fs_ops.rs | 6 ++--- vhost_user_fs/src/fuse_msg.rs | 26 +++++++++---------- vhost_user_fs/src/fuse_proc.rs | 14 ++++++----- vhost_user_fs/src/fuse_req.rs | 6 +++-- vhost_user_fs/src/lib.rs | 26 ------------------- vhost_user_fs/src/main.rs | 35 ++++++++++++++++++-------- vhost_user_fs/src/sandbox.rs | 5 ++-- vhost_user_fs/src/securecomputing.rs | 1 + vhost_user_fs/src/vhost_user_fs.rs | 14 +++++------ vhost_user_fs/src/vhost_user_server.rs | 9 ++++--- vhost_user_fs/src/virtio_fs.rs | 18 ++++++------- 13 files changed, 100 insertions(+), 104 deletions(-) delete mode 100644 vhost_user_fs/src/lib.rs diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 74ee7a79c..a8293f786 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -10,20 +10,22 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::ffi::CString; +use std::fs::File; +use std::{fs, path::PathBuf}; + +use anyhow::{bail, Context, Result}; + +use crate::fs_ops::open; +use crate::fuse_msg::FUSE_OK; +use util::arg_parser::{Arg, ArgMatches, ArgParser}; + // Read the programe version in `Cargo.toml`. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const MAX_PATH_LENGTH: usize = 4096; // Maximum length of the socket path is restricted by linux. const MAX_SOCK_PATH_LENGTH: usize = 108; -use crate::fs_ops::open; -use crate::fuse_msg::FUSE_OK; -use anyhow::{bail, Context, Result}; -use std::ffi::CString; -use std::fs::File; -use std::{fs, path::PathBuf}; -use util::arg_parser::{Arg, ArgMatches, ArgParser}; - /// This function is to define all command line arguments. pub fn create_args_parser<'a>() -> ArgParser<'a> { ArgParser::new("VhostUserFs") diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 3502234f4..0b273a2a6 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -10,6 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::{BTreeMap, HashMap}; +use std::ffi::CString; +use std::fs::{read_to_string, File}; +use std::mem; +use std::os::unix::io::{AsRawFd, RawFd}; + +use anyhow::{bail, Context, Result}; + +use super::fs_ops::*; +use super::fuse_msg::*; +use crate::cmdline::FsConfig; +use util::byte_code::ByteCode; +use util::num_ops::round_up; + /// The map length used to extend the file/inode map. const MAP_EXTEND_LENGTH: usize = 256; const F_RDLCK: u32 = 0; @@ -19,18 +33,6 @@ const RLIMIT_NOFILE_MIN: u64 = 20; /// The inode 0 is reserved, 1 is root inode. const ROOT_INODE: usize = 1; -use super::fs_ops::*; -use super::fuse_msg::*; -use crate::cmdline::FsConfig; -use anyhow::{bail, Context, Result}; -use std::collections::{BTreeMap, HashMap}; -use std::ffi::CString; -use std::fs::{read_to_string, File}; -use std::mem; -use std::os::unix::io::{AsRawFd, RawFd}; -use util::byte_code::ByteCode; -use util::num_ops::round_up; - /// Used as the key of the inode. #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] struct StatKey { diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 632325a7e..e914ce020 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -10,15 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -const MAX_PATH_LEN: usize = 4096; -const OFFSET_MAX: u64 = 0x7fffffffffffffff; - use std::ffi::CString; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use super::fuse_msg::*; +const MAX_PATH_LEN: usize = 4096; +const OFFSET_MAX: u64 = 0x7fffffffffffffff; + /// The pointer to open a directory. pub type DirPtr = *mut libc::DIR; /// The pointer to a directory entry in the directory stream. diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 6cbe9144d..718480651 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -10,6 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp; +use std::collections::VecDeque; +use std::ffi::CString; +use std::mem::size_of; +use std::sync::Arc; + +use anyhow::{bail, Context, Result}; +use log::error; + +use address_space::AddressSpace; +use util::byte_code::ByteCode; +use virtio::ElemIovec; + pub const FUSE_LOOKUP: u32 = 1; pub const FUSE_FORGET: u32 = 2; pub const FUSE_GETATTR: u32 = 3; @@ -139,19 +152,6 @@ pub const FUSE_SET_ATTR_ATIME_NOW: u32 = 1 << 7; pub const FUSE_SET_ATTR_MTIME_NOW: u32 = 1 << 8; pub const FUSE_SET_ATTR_CTIME: u32 = 1 << 10; -use std::cmp; -use std::collections::VecDeque; -use std::ffi::CString; -use std::mem::size_of; -use std::sync::Arc; - -use address_space::AddressSpace; -use log::error; -use util::byte_code::ByteCode; - -use anyhow::{bail, Context, Result}; -use virtio::ElemIovec; - /// Get the buffers from the element of virtio queue to parse fuse message or /// reply fuse message. pub struct FuseBuffer { diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index d1e47b8bd..a389129b7 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -10,17 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -const MAX_WRITE_SIZE: u32 = 1 << 20; - -use super::fs::FileSystem; -use super::fuse_msg::*; -use address_space::AddressSpace; -use log::error; use std::convert::TryInto; use std::ffi::CString; use std::mem; use std::sync::{Arc, Mutex}; +use log::error; + +use super::fs::FileSystem; +use super::fuse_msg::*; +use address_space::AddressSpace; + +const MAX_WRITE_SIZE: u32 = 1 << 20; + fn is_safe_path(path: &CString) -> bool { let path_str = match path.clone().into_string() { Ok(str_) => str_, diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 8d1e33624..b2f8ed6d3 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -10,12 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::{Arc, Mutex}; + +use log::error; + use super::fs::FileSystem; use super::fuse_msg::*; use super::fuse_proc::*; use address_space::AddressSpace; -use log::error; -use std::sync::{Arc, Mutex}; use virtio::Element; /// The request of fuse message parsed from virtio queue. diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs deleted file mode 100644 index 8e7686428..000000000 --- a/vhost_user_fs/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod cmdline; -pub mod fs; -pub mod fs_ops; -pub mod fuse_msg; -pub mod fuse_proc; -pub mod fuse_req; -pub mod sandbox; -pub mod securecomputing; -pub mod vhost_user_fs; -pub mod vhost_user_server; -pub mod virtio_fs; - -pub mod error; -pub use error::VhostUserFsError; diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 161b9df37..6189562d2 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -10,29 +10,44 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{bail, Context, Result}; -use log::{error, info}; -use machine_manager::event_loop::EventLoop; -use machine_manager::signal_handler; -use machine_manager::temp_cleaner::TempCleaner; +pub mod cmdline; +pub mod error; +pub mod fs; +pub mod fs_ops; +pub mod fuse_msg; +pub mod fuse_proc; +pub mod fuse_req; +pub mod sandbox; +pub mod securecomputing; +pub mod vhost_user_fs; +pub mod vhost_user_server; +pub mod virtio_fs; + use std::collections::HashSet; use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; +use log::{error, info}; use thiserror::Error; + +use crate::cmdline::{create_args_parser, create_fs_config, FsConfig}; +use crate::sandbox::Sandbox; +use crate::securecomputing::{seccomp_filter, string_to_seccompopt}; +use crate::vhost_user_fs::VhostUserFs; +use machine_manager::event_loop::EventLoop; +use machine_manager::signal_handler; +use machine_manager::temp_cleaner::TempCleaner; use util::arg_parser::ArgMatches; use util::{arg_parser, logger, seccomp::SeccompOpt}; -use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; -use vhost_user_fs::sandbox::Sandbox; -use vhost_user_fs::securecomputing::{seccomp_filter, string_to_seccompopt}; -use vhost_user_fs::vhost_user_fs::VhostUserFs; #[derive(Error, Debug)] pub enum MainError { #[error("VhostUserFs")] VhostUserFs { #[from] - source: vhost_user_fs::error::VhostUserFsError, + source: crate::error::VhostUserFsError, }, #[error("Util")] Util { diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs index dc44c0f54..3317cfe9d 100644 --- a/vhost_user_fs/src/sandbox.rs +++ b/vhost_user_fs/src/sandbox.rs @@ -10,13 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{bail, Result}; -use log::error; use std::ffi::CString; use std::fs; use std::fs::File; use std::os::unix::io::FromRawFd; +use anyhow::{bail, Result}; +use log::error; + /// Sandbox mechanism to isolate process. pub struct Sandbox { /// Source directory in host which can be accessed by guest. diff --git a/vhost_user_fs/src/securecomputing.rs b/vhost_user_fs/src/securecomputing.rs index 7a26df743..42380df52 100644 --- a/vhost_user_fs/src/securecomputing.rs +++ b/vhost_user_fs/src/securecomputing.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use anyhow::{Context, Result}; + use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; fn syscall_whitelist() -> Vec { diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index f27d01994..044021db0 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -18,20 +18,18 @@ use std::{ }, }; +use anyhow::{Context, Result}; use log::error; -use vmm_sys_util::epoll::EventSet; - -use machine_manager::{event_loop::EventLoop, temp_cleaner::TempCleaner}; -use util::loop_context::{ - EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; use super::cmdline::FsConfig; use super::fs::set_rlimit_nofile; use super::vhost_user_server::VhostUserServerHandler; use super::virtio_fs::VirtioFs; - -use anyhow::{Context, Result}; +use machine_manager::{event_loop::EventLoop, temp_cleaner::TempCleaner}; +use util::loop_context::{ + EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; +use vmm_sys_util::epoll::EventSet; /// The vhost-user filesystem device contains virtio fs device and the vhost-user /// server which can be connected with the vhost-user client in StratoVirt. diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index a7265a376..f79534a42 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -10,12 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::error; -use machine_manager::temp_cleaner::TempCleaner; use std::mem::size_of; use std::os::unix::io::RawFd; use std::slice; use std::sync::{atomic::AtomicBool, Arc, Mutex}; + +use anyhow::{bail, Context, Result}; +use log::error; + +use machine_manager::temp_cleaner::TempCleaner; use util::unix::limit_permission; use virtio::vhost::user::{ RegionMemInfo, VhostUserHdrFlag, VhostUserMemHdr, VhostUserMsgHdr, VhostUserMsgReq, @@ -23,8 +26,6 @@ use virtio::vhost::user::{ }; use virtio::VhostUser::VhostUserSock; -use anyhow::{bail, Context, Result}; - /// The trait for dealing with vhost-user request in the server. pub trait VhostUserReqHandler: Send + Sync { /// Set the current process as the owner of this file descriptor. diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index b5914b324..70b10dafa 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -21,34 +21,32 @@ const VIRTIO_FS_VRING_IDX_MASK: usize = 0xff; /// VRING_ERR, Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid /// FD flag. This flag is set when there is no file descriptor in the ancillary data. /// This signals that polling should be used instead of waiting for the kick. -const VIRTIO_FS_VRING_NO_FD_MASK: usize = 0x1 << 8; - -use crate::cmdline::FsConfig; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, bail, Context, Result}; use log::error; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use super::fs::FileSystem; +use super::fuse_req::FuseReq; +use super::vhost_user_server::VhostUserReqHandler; +use crate::cmdline::FsConfig; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; use machine_manager::event_loop::EventLoop; use util::loop_context::{ gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; - -use super::fs::FileSystem; -use super::fuse_req::FuseReq; -use super::vhost_user_server::VhostUserReqHandler; - -use anyhow::{anyhow, bail, Context, Result}; use virtio::vhost::user::RegionMemInfo; use virtio::{ Queue, QueueConfig, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, }; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +const VIRTIO_FS_VRING_NO_FD_MASK: usize = 0x1 << 8; struct FsIoHandler { queue: Queue, -- Gitee From 515de9b1337e5cf0c2f7737fe5343527f644335a Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 14 Mar 2023 15:46:22 +0800 Subject: [PATCH 0900/2187] vhost_user_fs: remove libseccomp-sys StratoVirt already support seccomp, using it instead of libseccomp-sys. Signed-off-by: Yan Wang --- Cargo.lock | 7 --- ...Third_Party_Open_Source_Software_Notice.md | 6 -- util/src/seccomp.rs | 4 ++ vhost_user_fs/Cargo.toml | 1 - vhost_user_fs/src/main.rs | 4 +- vhost_user_fs/src/securecomputing.rs | 56 ++++--------------- 6 files changed, 16 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86a8c0ff9..77a3f9ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,12 +410,6 @@ version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" -[[package]] -name = "libseccomp-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a7cbbd4ad467251987c6e5b47d53b11a5a05add08f2447a9e2d70aef1e0d138" - [[package]] name = "lock_api" version = "0.4.9" @@ -1059,7 +1053,6 @@ dependencies = [ "error-chain", "hypervisor", "libc", - "libseccomp-sys", "log", "machine_manager", "migration", diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 8857e13d0..5a6561f93 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -426,12 +426,6 @@ Copyright (c) tokio-rs License: MIT OR Apache-2.0 Please see above. -Software: libseccomp-sys 0.2.1 -Copyright notice: -Copyright (c) 2021 Sony Group Corporation -License: MIT OR Apache-2.0 -Please see above. - Software: capng 0.2.2 Copyright notice: Copyright (C) 2020 Red Hat, Inc. All rights reserved. diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 168ecc90e..47d14d626 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -114,6 +114,7 @@ const SECCOMP_RET_TRAP: u32 = 0x0003_0000; const SECCOMP_RET_ERRNO: u32 = 0x0005_0000; const SECCOMP_RET_TRACE: u32 = 0x7ff0_0000; const SECCOMP_RET_ALLOW: u32 = 0x7fff_0000; +const SECCOMP_RET_LOG: u32 = 0x7ffc_0000; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/seccomp.h#L45 const SECCOMP_RET_MASK: u32 = 0x0000_ffff; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/seccomp.h#L16 @@ -170,6 +171,8 @@ pub enum SeccompOpt { Trace(u32), /// Allow. Allow, + /// The syscall will be logged. + Log, } impl From for u32 { @@ -180,6 +183,7 @@ impl From for u32 { SeccompOpt::Errno(x) => SECCOMP_RET_ERRNO | (x & SECCOMP_RET_MASK), SeccompOpt::Trace(x) => SECCOMP_RET_TRACE | (x & SECCOMP_RET_MASK), SeccompOpt::Allow => SECCOMP_RET_ALLOW, + SeccompOpt::Log => SECCOMP_RET_LOG, } } } diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index a2294b796..730eb6b4b 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -13,7 +13,6 @@ log = "0.4.8" libc = "0.2" thiserror = "1.0" anyhow = "1.0" -libseccomp-sys = "0.2.1" error-chain = "0.12.4" vmm-sys-util = "0.11.0" address_space = { path = "../address_space" } diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 20705d4e6..161b9df37 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -21,10 +21,10 @@ use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; use thiserror::Error; use util::arg_parser::ArgMatches; -use util::{arg_parser, logger}; +use util::{arg_parser, logger, seccomp::SeccompOpt}; use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; use vhost_user_fs::sandbox::Sandbox; -use vhost_user_fs::securecomputing::{seccomp_filter, string_to_seccompopt, SeccompOpt}; +use vhost_user_fs::securecomputing::{seccomp_filter, string_to_seccompopt}; use vhost_user_fs::vhost_user_fs::VhostUserFs; #[derive(Error, Debug)] diff --git a/vhost_user_fs/src/securecomputing.rs b/vhost_user_fs/src/securecomputing.rs index 859479ca7..7a26df743 100644 --- a/vhost_user_fs/src/securecomputing.rs +++ b/vhost_user_fs/src/securecomputing.rs @@ -10,38 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{bail, Result}; -use libc::c_int; -use libseccomp_sys::{ - seccomp_init, seccomp_load, seccomp_release, seccomp_rule_add, SCMP_ACT_ALLOW, - SCMP_ACT_KILL_PROCESS, SCMP_ACT_LOG, SCMP_ACT_TRAP, -}; +use anyhow::{Context, Result}; +use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; -/// Seccomp option parsed from command line. -#[derive(Copy, Clone, Debug)] -pub enum SeccompOpt { - /// The seccomp filter will have no effect. - Allow, - /// Same as 'allow', but the syscall will be logged. - Log, - /// Kill the task immediately. - Kill, - /// Disallow and force a SIGSYS. - Trap, -} - -impl From for u32 { - fn from(action: SeccompOpt) -> u32 { - match action { - SeccompOpt::Allow => SCMP_ACT_ALLOW, - SeccompOpt::Kill => SCMP_ACT_KILL_PROCESS, - SeccompOpt::Log => SCMP_ACT_LOG, - SeccompOpt::Trap => SCMP_ACT_TRAP, - } - } -} - -pub fn add_syscall() -> Vec { +fn syscall_whitelist() -> Vec { let mut v = vec![libc::SYS_accept4]; v.push(libc::SYS_brk); v.push(libc::SYS_bind); @@ -154,22 +126,14 @@ pub fn add_syscall() -> Vec { /// /// * `action` - The default action. pub fn seccomp_filter(action: SeccompOpt) -> Result<()> { - let action_value = action.into(); - let scmp_filter_ctx = unsafe { seccomp_init(action_value) }; - if scmp_filter_ctx.is_null() { - bail!("seccomp_init fail"); - } - - let allowed_syscalls = add_syscall(); - for i in allowed_syscalls { - if unsafe { seccomp_rule_add(scmp_filter_ctx, SCMP_ACT_ALLOW, i as c_int, 0) } != 0 { - bail!("seccomp rule add fail {}", i); - } - } - if unsafe { seccomp_load(scmp_filter_ctx) } != 0 { - bail!("seccomp_load fail"); + let mut seccomp_filter = SyscallFilter::new(action); + let allowed_syscalls = syscall_whitelist(); + for call in allowed_syscalls { + seccomp_filter.push(&mut BpfRule::new(call)); } - unsafe { seccomp_release(scmp_filter_ctx) }; + seccomp_filter + .realize() + .with_context(|| "Failed to realize seccomp filter.")?; Ok(()) } -- Gitee From 2028578bbe9f022ce5e95b84a56fbd1c6ebfa95c Mon Sep 17 00:00:00 2001 From: mayunlong Date: Fri, 17 Mar 2023 12:08:16 +0800 Subject: [PATCH 0901/2187] qmp: when qmp command is not right, send error message to libvirt The string about message need "\r" in the end if it need to send to libvirt. Signed-off-by:mayunlong --- machine_manager/src/qmp/mod.rs | 5 +---- machine_manager/src/socket.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index a60126ee6..3dc44b611 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -487,10 +487,7 @@ fn qmp_command_exec( // Change response id with input qmp message qmp_response.change_id(id); - ( - serde_json::to_string(&qmp_response).unwrap() + "\r", - shutdown_flag, - ) + (serde_json::to_string(&qmp_response).unwrap(), shutdown_flag) } /// The struct `QmpChannel` is the only struct can handle Global variable diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index bd85ac2f9..f863fd687 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -154,9 +154,9 @@ impl Socket { if self.is_connected() { let mut handler = self.get_socket_handler(); let resp = if is_greeting { - serde_json::to_string(&QmpGreeting::create_greeting(1, 0, 5)).unwrap() + "\r" + serde_json::to_string(&QmpGreeting::create_greeting(1, 0, 5)).unwrap() } else { - serde_json::to_string(&Response::create_empty_response()).unwrap() + "\r" + serde_json::to_string(&Response::create_empty_response()).unwrap() }; handler.send_str(&resp)?; info!("QMP: --> {:?}", resp); @@ -626,7 +626,8 @@ impl SocketHandler { /// The socket file descriptor is broken. pub fn send_str(&mut self, s: &str) -> std::io::Result<()> { self.stream.flush().unwrap(); - match self.stream.write(s.as_bytes()) { + let msg = s.to_string() + "\r"; + match self.stream.write(msg.as_bytes()) { Ok(_) => { let _ = self.stream.write(&[b'\n'])?; Ok(()) @@ -746,7 +747,7 @@ mod tests { let length = client.read(&mut response).unwrap(); assert_eq!( String::from_utf8_lossy(&response[..length]), - "I am a test str\n".to_string() + "I am a test str\r\n".to_string() ); // 2.send String @@ -755,7 +756,7 @@ mod tests { let length = client.read(&mut response).unwrap(); assert_eq!( String::from_utf8_lossy(&response[..length]), - "I am a test String\n".to_string() + "I am a test String\r\n".to_string() ); // After test. Environment Recover -- Gitee From 08fda8426d1bb99bb5725e8ebe0258de5a160653 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 16 Mar 2023 21:06:41 +0800 Subject: [PATCH 0902/2187] socket: fix error handling fd In read_fd(), it has two problems: 1) do not check the scm.cmsg_len which can decide the num of fd; 2) it should not use the last non-zero u8 value as the fd, the fd type is RawFd(4byte). So, it should use scm.cmsg_len to caculate the num of fd, and use RawFd type to get the received fd. Signed-off-by: Yan Wang --- machine_manager/src/socket.rs | 119 +++++++++++++++------------------- 1 file changed, 52 insertions(+), 67 deletions(-) diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index bd85ac2f9..738695def 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -10,26 +10,35 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use serde::Deserialize; use std::io::{Error, ErrorKind, Read, Write}; +use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; use anyhow::{bail, Result}; +use libc::{ + c_void, iovec, msghdr, recvmsg, sendmsg, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, + MSG_DONTWAIT, MSG_NOSIGNAL, SCM_RIGHTS, SOL_SOCKET, +}; use log::{error, info}; +use serde::Deserialize; +use vmm_sys_util::epoll::EventSet; + +use crate::machine::MachineExternalInterface; +use crate::qmp::{QmpChannel, QmpGreeting, Response}; use util::leak_bucket::LeakBucket; use util::loop_context::{ gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use vmm_sys_util::epoll::EventSet; - -use crate::machine::MachineExternalInterface; -use crate::qmp::{QmpChannel, QmpGreeting, Response}; const MAX_SOCKET_MSG_LENGTH: usize = 8192; +/// The max buffer length received by recvmsg. +const MAX_RECV_BUF_LEN: usize = 4096; +/// The max buffer length used by recvmsg for file descriptors. +const MAX_RECV_FDS_LEN: usize = MAX_RECV_BUF_LEN; pub(crate) const LEAK_BUCKET_LIMIT: u64 = 100; /// The wrapper over Unix socket and socket handler. @@ -342,6 +351,31 @@ impl SocketRWHandler { } } + fn parse_fd(&mut self, mhdr: &msghdr) { + // At least it should has one RawFd. + let min_cmsg_len = unsafe { CMSG_LEN(size_of::() as u32) as usize }; + if (mhdr.msg_controllen as usize) < min_cmsg_len { + return; + } + + let mut cmsg_hdr = unsafe { CMSG_FIRSTHDR(mhdr as *const msghdr).as_ref() }; + while cmsg_hdr.is_some() { + let scm = cmsg_hdr.unwrap(); + if scm.cmsg_level == SOL_SOCKET + && scm.cmsg_type == SCM_RIGHTS + && scm.cmsg_len as usize >= min_cmsg_len + { + let fds = unsafe { + let fd_num = + (scm.cmsg_len as usize - CMSG_LEN(0) as usize) / size_of::(); + std::slice::from_raw_parts(CMSG_DATA(scm) as *const RawFd, fd_num) + }; + self.scm_fd.append(&mut fds.to_vec()); + } + cmsg_hdr = unsafe { CMSG_NXTHDR(mhdr as *const msghdr, scm).as_ref() }; + } + } + /// Receive bytes and scm_fd from socket file descriptor. /// /// # Notes @@ -354,30 +388,13 @@ impl SocketRWHandler { /// # Errors /// The socket file descriptor is broken. fn read_fd(&mut self) -> std::io::Result<()> { - use libc::{ - c_uint, c_void, cmsghdr, iovec, msghdr, recvmsg, CMSG_DATA, CMSG_FIRSTHDR, CMSG_SPACE, - MSG_DONTWAIT, SCM_RIGHTS, SOL_SOCKET, + let recv_buf = [0_u8; MAX_RECV_BUF_LEN]; + let mut iov = iovec { + iov_base: recv_buf.as_ptr() as *mut c_void, + iov_len: MAX_RECV_BUF_LEN, }; - + let mut cmsg_space = [0_u8; MAX_RECV_FDS_LEN]; loop { - let tmp_buf = [0_u8; 1]; - let mut iov = iovec { - iov_base: tmp_buf.as_ptr() as *mut c_void, - iov_len: 1, - }; - - let mut cmsg_space = { - let mut space = 0; - space += - unsafe { CMSG_SPACE(std::mem::size_of::<[RawFd; 2]>() as c_uint) } as usize; - Some(Vec::::with_capacity(space)) - }; - - let (msg_control, msg_controllen) = cmsg_space - .as_mut() - .map(|v| (v.as_mut_ptr(), v.capacity())) - .unwrap_or((std::ptr::null_mut(), 0)); - // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. let mut mhdr: msghdr = unsafe { std::mem::zeroed() }; @@ -385,14 +402,13 @@ impl SocketRWHandler { mhdr.msg_namelen = 0; mhdr.msg_iov = &mut iov as *mut iovec; mhdr.msg_iovlen = 1; - mhdr.msg_control = msg_control as *mut c_void; - mhdr.msg_controllen = msg_controllen as _; + mhdr.msg_control = cmsg_space.as_mut_ptr() as *mut c_void; + mhdr.msg_controllen = cmsg_space.len() as _; mhdr.msg_flags = 0; // MSG_DONTWAIT: Enables nonblocking operation, if the operation would block the call // fails with the error EAGAIN or EWOULDBLOCK. When this error occurs, break loop let ret = unsafe { recvmsg(self.socket_fd, &mut mhdr, MSG_DONTWAIT) }; - if ret == -1 { let sock_err = Error::last_os_error(); if sock_err.kind() == ErrorKind::WouldBlock { @@ -400,44 +416,15 @@ impl SocketRWHandler { } else { return Err(sock_err); } - } else if ret == 0 { - break; } - - let cmsg_hdr: Option<&cmsghdr> = unsafe { - if mhdr.msg_controllen > 0 { - cmsg_space - .as_mut() - .unwrap() - .set_len(mhdr.msg_controllen as usize); - CMSG_FIRSTHDR(&mhdr as *const msghdr) + self.parse_fd(&mhdr); + if ret > 0 { + self.buf.extend(&recv_buf[..ret as usize]); + if let Some(pos) = self.pos.checked_add(ret as usize) { + self.pos = pos; } else { - std::ptr::null() - } - .as_ref() - }; - - if let Some(scm) = cmsg_hdr { - if scm.cmsg_level == SOL_SOCKET && scm.cmsg_type == SCM_RIGHTS { - let scm_cmsg_header = unsafe { - std::slice::from_raw_parts( - CMSG_DATA(scm), - std::mem::size_of::<[RawFd; 2]>(), - ) - }; - for fd in scm_cmsg_header.iter() { - if *fd != 0 { - self.scm_fd.push(i32::from(*fd)); - } - } + return Err(ErrorKind::InvalidInput.into()); } - }; - - self.buf.push(tmp_buf[0]); - if let Some(pos) = self.pos.checked_add(1) { - self.pos = pos; - } else { - return Err(ErrorKind::InvalidInput.into()); } } Ok(()) @@ -457,8 +444,6 @@ impl SocketRWHandler { /// # Errors /// The socket file descriptor is broken. fn write_fd(&mut self, length: usize) -> std::io::Result<()> { - use libc::{c_void, iovec, msghdr, sendmsg, MSG_NOSIGNAL}; - let mut iov = iovec { iov_base: self.buf.as_slice()[(self.pos - length)..(self.pos - 1)].as_ptr() as *mut c_void, -- Gitee From b2446a36d25358a93ebdc659c88a698ec51a9658 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 16 Mar 2023 21:16:29 +0800 Subject: [PATCH 0903/2187] network: fix attaching net device error by libvirt Due to the mismatched netdev_add qmp arguments between libvirt and stratovirt, it will cause error when attach device. So, fix the code as follows: 1) fix the mismatched arguments; 2) fix the related handling functions of qmp arguments; 3) fix and add related unit test; Signed-off-by: Yan Wang --- docs/qmp.md | 10 +- machine_manager/src/config/network.rs | 408 ++++++++++++++++---------- machine_manager/src/qmp/qmp_schema.rs | 85 ++++-- 3 files changed, 336 insertions(+), 167 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index f2c41494f..7051d82ef 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -106,7 +106,13 @@ Add a network backend. * `id` : the device's ID, must be unique. * `ifname` : the backend tap dev name. -* `fds` : the file fd opened by upper level. +* `fd` : the opened tap fd. +* `fds` : the opened tap fds. +* `queues` : the num of queues for multi-queue. +* `vhost` : whether to run as a vhost-net device. +* `vhostfd` : the vhost-net device fd. +* `vhostfds` : the vhost-net device fds. +* `chardev` : the chardev name for vhost-user net. #### Notes @@ -116,6 +122,8 @@ Add a network backend. * For `addr`, it start at `0x0` mapping in guest with `eth0`. +* It does not support multi-queue. + #### Example ```json diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index ce8562e64..5fdbe031f 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -10,7 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Result}; +use std::os::unix::io::RawFd; + +use anyhow::{anyhow, bail, Context, Result}; use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check}; @@ -25,6 +27,8 @@ const MAC_ADDRESS_LENGTH: usize = 17; /// Max virtqueue size of each virtqueue. pub const MAX_QUEUE_SIZE_NET: u16 = 4096; +/// Max num of virtqueues. +const MAX_QUEUE_PAIRS: usize = MAX_VIRTIO_QUEUE / 2; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetDevcfg { @@ -336,12 +340,47 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result Result { + if let Some(fd) = QmpChannel::get_fd(fd_name) { + Ok(fd) + } else { + // try to convert string to RawFd + let fd_num = fd_name + .parse::() + .with_context(|| anyhow!("Failed to parse fd: {}", fd_name))?; + Ok(fd_num) + } +} + +fn get_netdev_fds(fds_name: &str) -> Result> { + let fds_vec: Vec<&str> = fds_name.split(':').collect(); + let mut fds = Vec::new(); + for fd_name in fds_vec { + fds.push(get_netdev_fd(fd_name)?); + } + if fds.len() > MAX_QUEUE_PAIRS { + bail!( + "The num of fd {} is bigger than max queue num {}", + fds.len(), + MAX_QUEUE_PAIRS + ); + } + Ok(fds) +} + pub fn get_netdev_config(args: Box) -> Result { let queues = args .queues .unwrap_or(1) .checked_mul(2) .ok_or_else(|| anyhow!("Invalid 'queues' value"))?; + if !is_netdev_queues_valid(queues) { + bail!( + "The 'queues' {} is bigger than max queue num {}", + queues / 2, + MAX_QUEUE_PAIRS + ); + } let mut config = NetDevcfg { id: args.id, tap_fds: None, @@ -352,67 +391,63 @@ pub fn get_netdev_config(args: Box) -> Result = fds.split(':').collect(); - String::from(col[col.len() - 1]) - } else { - String::from(&fds) - }; - if let Some(fd_num) = QmpChannel::get_fd(&netdev_fd) { - config.tap_fds = Some(vec![fd_num]); - } else { - // try to convert string to RawFd - let fd_num = match netdev_fd.parse::() { - Ok(fd) => fd, - _ => { - bail!("Failed to parse fd: {}", netdev_fd); - } - }; - config.tap_fds = Some(vec![fd_num]); + if let Some(tap_fd) = args.fd { + if args.if_name.is_some() + || args.script.is_some() + || args.downscript.is_some() + || args.queues.is_some() + || args.fds.is_some() + || args.vhostfds.is_some() + { + bail!("fd is conflict with ifname/script/downscript/queues/fds/vhostfds"); + } + let fd = get_netdev_fd(&tap_fd)?; + config.tap_fds = Some(vec![fd]); + + if let Some(vhostfd) = args.vhostfd { + let fd = get_netdev_fd(&vhostfd)?; + config.vhost_fds = Some(vec![fd]); + } + } else if let Some(tap_fds) = args.fds { + if args.if_name.is_some() + || args.script.is_some() + || args.downscript.is_some() + || args.queues.is_some() + || args.vhostfd.is_some() + { + bail!("fds are conflict with ifname/script/downscript/queues/vhostfd"); + } + config.tap_fds = Some(get_netdev_fds(&tap_fds)?); + config.queues = 2 * config.tap_fds.as_ref().unwrap().len() as u16; + + if let Some(vhostfds) = args.vhostfds { + config.vhost_fds = Some(get_netdev_fds(&vhostfds)?); + if config.tap_fds.as_ref().unwrap().len() != config.vhost_fds.as_ref().unwrap().len() { + bail!("The num of vhostfds must equal to fds"); + } } } else if let Some(if_name) = args.if_name { config.ifname = if_name; } - let netdev_type = if let Some(net_type) = args.net_type { - net_type - } else { - "".to_string() - }; - - if let Some(vhost) = args.vhost { - match vhost.parse::() { - Ok(vhost) => { - if vhost.into() { - if netdev_type.ne("vhost-user") { - config.vhost_type = Some(String::from("vhost-kernel")); - } else { - bail!("vhost-user netdev does not support \"vhost\" option"); - } - } - } - Err(_) => { - bail!("Failed to get vhost type: {}", vhost); - } - }; + // Get net device type. + let netdev_type = args.net_type.unwrap_or_default(); + let vhost = args.vhost.unwrap_or_default(); + if vhost { + if netdev_type.ne("vhost-user") { + config.vhost_type = Some(String::from("vhost-kernel")); + } else { + bail!("vhost-user netdev does not support 'vhost' option"); + } } else if netdev_type.eq("vhost-user") { config.vhost_type = Some(netdev_type.clone()); } - if let Some(vhostfd) = args.vhostfds { - match vhostfd.parse::() { - Ok(fd) => config.vhost_fds = Some(vec![fd]), - Err(_e) => { - bail!("Failed to get vhost fd: {}", vhostfd); - } - }; - } if config.vhost_fds.is_some() && config.vhost_type.is_none() { - bail!("Argument \'vhostfd\' is not needed for virtio-net device"); + bail!("Argument 'vhostfd' or 'vhostfds' are not needed for virtio-net device"); } if config.tap_fds.is_none() && config.ifname.eq("") && netdev_type.ne("vhost-user") { - bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device"); + bail!("Tap device is missing, use 'ifname' or 'fd' to configure a tap device"); } Ok(config) @@ -796,108 +831,187 @@ mod tests { } } - fn create_netdev_add( - id: String, - if_name: Option, - fds: Option, - vhost: Option, - vhostfds: Option, - ) -> Box { - Box::new(qmp_schema::NetDevAddArgument { - id, - if_name, - fds, - dnssearch: None, - net_type: None, - vhost, - vhostfds, - ifname: None, - downscript: None, - script: None, - queues: None, - chardev: None, - }) + fn check_err_msg(netdev: Box, err_msg: &str) { + if let Err(err) = get_netdev_config(netdev) { + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } } #[test] fn test_get_netdev_config() { - // Invalid vhost - let netdev_add = create_netdev_add( - String::from("netdev"), - None, - None, - Some(String::from("1")), - None, - ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_err()); - - // Invalid vhost fd - let netdev_add = create_netdev_add( - String::from("netdev"), - None, - None, - None, - Some(String::from("999999999999999999999")), - ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_err()); - - // No need to config vhost fd - let netdev_add = create_netdev_add( - String::from("netdev"), - None, - None, - None, - Some(String::from("55")), - ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_err()); - - // No ifname or fd - let netdev_add = create_netdev_add( - String::from("netdev"), - None, - None, - Some(String::from("on")), - Some(String::from("55")), - ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_err()); - - let netdev_add = create_netdev_add( - String::from("netdev"), - Some(String::from("tap0")), - None, - None, - None, - ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_ok()); - assert_eq!(net_cfg.unwrap().ifname, "tap0"); - - let netdev_add = create_netdev_add( - String::from("netdev"), - Some(String::from("tap0")), - None, - Some(String::from("on")), - None, + QmpChannel::object_init(); + // Normal test with common elem. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + id: "netdev".to_string(), + if_name: Some("tap0".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + let net_cfg = get_netdev_config(netdev).unwrap(); + assert_eq!(net_cfg.id, "netdev"); + assert_eq!(net_cfg.ifname, "tap0"); + + // Set fd_name and fd_value to qmp channel. + for i in 0..5 { + let fd_name = "fd-net0".to_string() + &i.to_string(); + QmpChannel::set_fd(fd_name, 11 + i); + let vhostfd_name = "vhostfd-net0".to_string() + &i.to_string(); + QmpChannel::set_fd(vhostfd_name, 21 + i); + } + + // Normal test with 'fd' value or name. + for value in ["11", "fd-net00"] { + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fd: Some(value.to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + let net_cfg = get_netdev_config(netdev).unwrap(); + assert_eq!(net_cfg.tap_fds.unwrap()[0], 11); + } + + // Normal test with 'fds' value or name. + for value in ["11:12:13:14", "fd-net00:fd-net01:fd-net02:fd-net03"] { + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fds: Some(value.to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + let net_cfg = get_netdev_config(netdev).unwrap(); + assert_eq!(net_cfg.tap_fds.unwrap(), [11, 12, 13, 14]); + } + + // Normal test with 'vhostfd'. + for (fd, vhostfd) in [("11", "21"), ("fd-net00", "vhostfd-net00")] { + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fd: Some(fd.to_string()), + vhostfd: Some(vhostfd.to_string()), + vhost: Some(true), + ..qmp_schema::NetDevAddArgument::default() + }); + let net_cfg = get_netdev_config(netdev).unwrap(); + assert_eq!(net_cfg.tap_fds.unwrap()[0], 11); + assert_eq!(net_cfg.vhost_fds.unwrap()[0], 21); + assert_eq!(net_cfg.vhost_type.unwrap(), "vhost-kernel"); + } + + // Normal test with 'vhostfds'. + for (fds, vhostfds) in [ + ("11:12:13:14", "21:22:23:24"), + ( + "fd-net00:fd-net01:fd-net02:fd-net03", + "vhostfd-net00:vhostfd-net01:vhostfd-net02:vhostfd-net03", + ), + ] { + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fds: Some(fds.to_string()), + vhostfds: Some(vhostfds.to_string()), + vhost: Some(true), + ..qmp_schema::NetDevAddArgument::default() + }); + let net_cfg = get_netdev_config(netdev).unwrap(); + assert_eq!(net_cfg.tap_fds.unwrap(), [11, 12, 13, 14]); + assert_eq!(net_cfg.vhost_fds.unwrap(), [21, 22, 23, 24]); + assert_eq!(net_cfg.vhost_type.unwrap(), "vhost-kernel"); + } + + let err_msgs = [ + "Invalid 'queues' value", + "fd is conflict with ifname/script/downscript/queues/fds/vhostfds", + "fds are conflict with ifname/script/downscript/queues/vhostfd", + "The num of vhostfds must equal to fds", + "vhost-user netdev does not support 'vhost' option", + "Argument 'vhostfd' or 'vhostfds' are not needed for virtio-net device", + "Tap device is missing, use 'ifname' or 'fd' to configure a tap device", + ]; + + // Abnornal test with invalid 'queues': u16::MAX. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + queues: Some(u16::MAX), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[0]); + + // Abnornal test with invalid 'queues': MAX_QUEUE_PAIRS + 1. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + queues: Some(MAX_QUEUE_PAIRS as u16 + 1), + ..qmp_schema::NetDevAddArgument::default() + }); + let err_msg = format!( + "The 'queues' {} is bigger than max queue num {}", + MAX_QUEUE_PAIRS + 1, + MAX_QUEUE_PAIRS ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_ok()); - assert_eq!(net_cfg.unwrap().vhost_type.unwrap(), "vhost-kernel"); - - let netdev_add = create_netdev_add( - String::from("netdev"), - Some(String::from("tap0")), - None, - Some(String::from("on")), - Some(String::from("12")), + check_err_msg(netdev, &err_msg); + + // Abnornal test with 'fd' and 'vhostfds'. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fd: Some("11".to_string()), + vhostfds: Some("21:22:23:24".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[1]); + + // Abnornal test with 'fds' and 'vhostfd'. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fds: Some("11:12:13:14".to_string()), + vhostfd: Some("21".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[2]); + + // Abnornal test with different num of 'fds' and 'vhostfds'. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fds: Some("11:12:13:14".to_string()), + vhostfds: Some("21:22:23".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[3]); + + // Abnornal test with 'net_type=vhost-user'. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fd: Some("11".to_string()), + vhostfd: Some("21".to_string()), + vhost: Some(true), + net_type: Some("vhost-user".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[4]); + + // Abnornal test with 'fds/vhostfds' and no 'vhost'. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fds: Some("11:12:13:14".to_string()), + vhostfds: Some("21:22:23:24".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[5]); + + // Abnornal test with all default value. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, &err_msgs[6]); + + // Abnornal test with invalid fd value. + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fd: Some("invalid_fd".to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + check_err_msg(netdev, "Failed to parse fd: invalid_fd"); + + // Abnornal test with fd num bigger than MAX_QUEUE_PAIRS. + let mut fds = "0".to_string(); + for i in 1..MAX_QUEUE_PAIRS + 1 { + fds += &(":".to_string() + &i.to_string()); + } + let netdev = Box::new(qmp_schema::NetDevAddArgument { + fds: Some(fds.to_string()), + ..qmp_schema::NetDevAddArgument::default() + }); + let err_msg = format!( + "The num of fd {} is bigger than max queue num {}", + MAX_QUEUE_PAIRS + 1, + MAX_QUEUE_PAIRS ); - let net_cfg = get_netdev_config(netdev_add); - assert!(net_cfg.is_ok()); - let net_cfg = net_cfg.unwrap(); - assert_eq!(net_cfg.vhost_type.unwrap(), "vhost-kernel"); - assert_eq!(net_cfg.vhost_fds.unwrap()[0], 12); + check_err_msg(netdev, &err_msg); } } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 266d2961b..e62976e14 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -753,13 +753,14 @@ pub struct netdev_add { pub id: String, #[serde(rename = "ifname")] pub if_name: Option, + pub fd: Option, pub fds: Option, pub dnssearch: Option, #[serde(rename = "type")] pub net_type: Option, - pub vhost: Option, + pub vhost: Option, + pub vhostfd: Option, pub vhostfds: Option, - pub ifname: Option, pub downscript: Option, pub script: Option, pub queues: Option, @@ -2511,23 +2512,6 @@ mod tests { }; let ret_msg = r#"ok"#; assert!(err_msg == ret_msg); - - // right arguments for netdev-add. - let json_msg = r#" - { - "execute": "netdev_add", - "arguments": { - "id": "net-0", - "ifname":"tap0" - } - } - "#; - let err_msg = match serde_json::from_str::(json_msg) { - Ok(_) => "ok".to_string(), - Err(e) => e.to_string(), - }; - let ret_msg = r#"ok"#; - assert!(err_msg == ret_msg); } #[test] @@ -2604,6 +2588,69 @@ mod tests { assert!(err_msg.contains(part_msg)); } + #[test] + fn test_qmp_netdev_add() { + // Normal netdev_add test. + let json_msg = r#" + { + "execute": "netdev_add", + "arguments": { + "id": "net0", + "ifname": "tap0", + "fd": "11", + "fds": "fd-net00:fd-net01", + "dnssearch": "test", + "type": "vhost-user", + "vhost": true, + "vhostfd": "21", + "vhostfds": "vhostfd-net00:vhostfd-net01", + "downscript": "/etc/ifdown.sh", + "script": "/etc/ifup.sh", + "queues": 16, + "chardev": "char_dev_name" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"ok"#; + assert!(err_msg.contains(part_msg)); + + // Abnormal netdev_add test with invalid vhost type. + let json_msg = r#" + { + "execute": "netdev_add", + "arguments": { + "vhost": "invalid_type" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"expected a boolean"#; + assert!(err_msg.contains(part_msg)); + + // Abnormal netdev_add test with invalid queues type. + let json_msg = r#" + { + "execute": "netdev_add", + "arguments": { + "queues": "invalid_type" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"expected u16"#; + assert!(err_msg.contains(part_msg)); + } + #[test] fn test_qmp_input_event() { // key event -- Gitee From 5b5de9e93a08468128b185bd9539debbd754a56b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 2 Mar 2023 13:05:13 +0800 Subject: [PATCH 0904/2187] cargo/package: Move usb package into devices Signed-off-by: Keqian Zhu --- Cargo.lock | 21 ++-------------- devices/Cargo.toml | 4 +++ devices/src/lib.rs | 1 + {usb/src => devices/src/usb}/config.rs | 0 {usb/src => devices/src/usb}/descriptor.rs | 4 +-- {usb/src => devices/src/usb}/error.rs | 0 {usb/src => devices/src/usb}/hid.rs | 4 +-- {usb/src => devices/src/usb}/keyboard.rs | 10 ++++---- usb/src/usb.rs => devices/src/usb/mod.rs | 21 +++++++++++++--- {usb/src => devices/src/usb}/tablet.rs | 10 ++++---- {usb/src => devices/src/usb}/xhci/mod.rs | 0 .../src/usb}/xhci/xhci_controller.rs | 6 ++--- {usb/src => devices/src/usb}/xhci/xhci_pci.rs | 9 ++++--- .../src => devices/src/usb}/xhci/xhci_regs.rs | 8 +++--- .../src => devices/src/usb}/xhci/xhci_ring.rs | 4 +-- machine/Cargo.toml | 1 - machine/src/lib.rs | 8 +++--- tests/mod_test/Cargo.toml | 1 - tests/mod_test/src/libdriver/usb.rs | 4 +-- tests/mod_test/tests/usb_test.rs | 18 ++++++------- usb/Cargo.toml | 22 ---------------- usb/src/lib.rs | 25 ------------------- 22 files changed, 67 insertions(+), 114 deletions(-) rename {usb/src => devices/src/usb}/config.rs (100%) rename {usb/src => devices/src/usb}/descriptor.rs (99%) rename {usb/src => devices/src/usb}/error.rs (100%) rename {usb/src => devices/src/usb}/hid.rs (99%) rename {usb/src => devices/src/usb}/keyboard.rs (97%) rename usb/src/usb.rs => devices/src/usb/mod.rs (98%) rename {usb/src => devices/src/usb}/tablet.rs (97%) rename {usb/src => devices/src/usb}/xhci/mod.rs (100%) rename {usb/src => devices/src/usb}/xhci/xhci_controller.rs (99%) rename {usb/src => devices/src/usb}/xhci/xhci_pci.rs (99%) rename {usb/src => devices/src/usb}/xhci/xhci_regs.rs (99%) rename {usb/src => devices/src/usb}/xhci/xhci_ring.rs (99%) delete mode 100644 usb/Cargo.toml delete mode 100644 usb/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 73f39cf74..e7bf8adfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,8 @@ dependencies = [ "machine_manager", "migration", "migration_derive", + "once_cell", + "pci", "serde", "serial_test", "sysbus", @@ -429,7 +431,6 @@ dependencies = [ "sysbus", "thiserror", "ui", - "usb", "util", "vfio", "vfio-bindings", @@ -512,7 +513,6 @@ dependencies = [ "machine", "rand", "serde_json", - "usb", "util", "virtio", "vmm-sys-util", @@ -981,23 +981,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "usb" -version = "2.2.0" -dependencies = [ - "address_space", - "anyhow", - "byteorder", - "libc", - "log", - "machine_manager", - "once_cell", - "pci", - "thiserror", - "ui", - "util", -] - [[package]] name = "util" version = "2.2.0" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 8848a209e..c31ca8736 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -4,6 +4,7 @@ version = "2.2.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" +description = "Misc device emulation" [dependencies] thiserror = "1.0" @@ -15,6 +16,7 @@ serde = { version = "1.0", features = ["derive"] } vmm-sys-util = "0.11.0" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" +once_cell = "1.9.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } @@ -22,8 +24,10 @@ machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration_derive" } sysbus = { path = "../sysbus" } +pci = { path = "../pci" } util = { path = "../util" } acpi = { path = "../acpi" } + [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index cc42da10e..f89326f96 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -19,6 +19,7 @@ pub mod acpi; mod interrupt_controller; pub mod legacy; +pub mod usb; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ diff --git a/usb/src/config.rs b/devices/src/usb/config.rs similarity index 100% rename from usb/src/config.rs rename to devices/src/usb/config.rs diff --git a/usb/src/descriptor.rs b/devices/src/usb/descriptor.rs similarity index 99% rename from usb/src/descriptor.rs rename to devices/src/usb/descriptor.rs index 3b09e56db..5093ef6e0 100644 --- a/usb/src/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -15,8 +15,8 @@ use std::sync::Arc; use anyhow::{bail, Result}; use util::byte_code::ByteCode; -use crate::config::*; -use crate::usb::UsbDevice; +use super::config::*; +use super::UsbDevice; const USB_MAX_INTERFACES: u32 = 16; const USB_DESCRIPTOR_TYPE_SHIFT: u32 = 8; diff --git a/usb/src/error.rs b/devices/src/usb/error.rs similarity index 100% rename from usb/src/error.rs rename to devices/src/usb/error.rs diff --git a/usb/src/hid.rs b/devices/src/usb/hid.rs similarity index 99% rename from usb/src/hid.rs rename to devices/src/usb/hid.rs index c4f4c8950..7e4927cc0 100644 --- a/usb/src/hid.rs +++ b/devices/src/usb/hid.rs @@ -14,8 +14,8 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use log::{debug, error, warn}; -use crate::config::*; -use crate::usb::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; +use super::config::*; +use super::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; /// HID keycode const HID_KEYBOARD_LEFT_CONTROL: u8 = 0xe0; diff --git a/usb/src/keyboard.rs b/devices/src/usb/keyboard.rs similarity index 97% rename from usb/src/keyboard.rs rename to devices/src/usb/keyboard.rs index a3672e6ee..32dce752b 100644 --- a/usb/src/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -16,17 +16,17 @@ use anyhow::Result; use log::{debug, error, info}; use once_cell::sync::Lazy; -use crate::config::*; -use crate::descriptor::{ +use super::config::*; +use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; -use crate::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; -use crate::usb::{ +use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; +use super::xhci::xhci_controller::XhciDevice; +use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use crate::xhci::xhci_controller::XhciDevice; use ui::input::{register_keyboard, KeyboardOpts}; /// Keyboard device descriptor diff --git a/usb/src/usb.rs b/devices/src/usb/mod.rs similarity index 98% rename from usb/src/usb.rs rename to devices/src/usb/mod.rs index 84dc1ac36..2c50c709f 100644 --- a/usb/src/usb.rs +++ b/devices/src/usb/mod.rs @@ -10,16 +10,29 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod error; +pub use anyhow::Result; +pub use error::UsbError; + +pub mod config; +mod descriptor; +pub mod hid; +#[cfg(not(target_env = "musl"))] +pub mod keyboard; +#[cfg(not(target_env = "musl"))] +pub mod tablet; +pub mod xhci; + use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{bail, Result}; +use anyhow::bail; use log::{debug, error}; use util::aio::{mem_from_buf, mem_to_buf}; -use crate::config::*; -use crate::descriptor::{UsbDescriptor, UsbDescriptorOps}; -use crate::xhci::xhci_controller::{UsbPort, XhciDevice}; +use config::*; +use descriptor::{UsbDescriptor, UsbDescriptorOps}; +use xhci::xhci_controller::{UsbPort, XhciDevice}; const USB_MAX_ENDPOINTS: u32 = 15; /// USB max address. diff --git a/usb/src/tablet.rs b/devices/src/usb/tablet.rs similarity index 97% rename from usb/src/tablet.rs rename to devices/src/usb/tablet.rs index 0507947d1..a693a23ef 100644 --- a/usb/src/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -17,17 +17,17 @@ use anyhow::Result; use log::{debug, error, info}; use once_cell::sync::Lazy; -use crate::config::*; -use crate::descriptor::{ +use super::config::*; +use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; -use crate::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; -use crate::usb::{ +use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; +use super::xhci::xhci_controller::XhciDevice; +use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use crate::xhci::xhci_controller::XhciDevice; use ui::input::{register_pointer, PointerOpts}; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; diff --git a/usb/src/xhci/mod.rs b/devices/src/usb/xhci/mod.rs similarity index 100% rename from usb/src/xhci/mod.rs rename to devices/src/usb/xhci/mod.rs diff --git a/usb/src/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs similarity index 99% rename from usb/src/xhci/xhci_controller.rs rename to devices/src/usb/xhci/xhci_controller.rs index 6922a6041..30a1b5be0 100644 --- a/usb/src/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -25,10 +25,10 @@ use log::{debug, error, info, warn}; use machine_manager::config::XhciConfig; use util::num_ops::{read_u32, write_u64_low}; -use crate::config::*; +use super::xhci_regs::{XchiOperReg, XhciInterrupter}; +use crate::usb::config::*; +use crate::usb::UsbError; use crate::usb::{Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; -use crate::xhci::xhci_regs::{XchiOperReg, XhciInterrupter}; -use crate::UsbError; use super::xhci_ring::XhciEventRingSeg; use super::xhci_ring::XhciRing; diff --git a/usb/src/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs similarity index 99% rename from usb/src/xhci/xhci_pci.rs rename to devices/src/usb/xhci/xhci_pci.rs index 72a494f3c..e43a1c40e 100644 --- a/usb/src/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -14,6 +14,8 @@ use std::cmp::max; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; +use anyhow::{bail, Context, Result}; + use address_space::{AddressSpace, Region}; use log::debug; use machine_manager::config::XhciConfig; @@ -24,13 +26,12 @@ use pci::config::{ use pci::msix::update_dev_id; use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; -use crate::usb::UsbDeviceOps; -use crate::xhci::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; -use crate::xhci::xhci_regs::{ +use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; +use super::xhci_regs::{ build_cap_ops, build_doorbell_ops, build_oper_ops, build_port_ops, build_runtime_ops, XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME, }; -use anyhow::{bail, Context, Result}; +use crate::usb::UsbDeviceOps; /// 5.2 PCI Configuration Registers(USB) const PCI_CLASS_PI: u16 = 0x09; diff --git a/usb/src/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs similarity index 99% rename from usb/src/xhci/xhci_regs.rs rename to devices/src/usb/xhci/xhci_regs.rs index bc382ca16..1c40f1bae 100644 --- a/usb/src/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -21,10 +21,10 @@ use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, wri use super::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; -use crate::xhci::xhci_controller::dma_write_bytes; -use crate::xhci::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; -use crate::xhci::xhci_ring::XhciTRB; -use crate::{config::*, UsbError}; +use super::xhci_controller::dma_write_bytes; +use super::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; +use super::xhci_ring::XhciTRB; +use crate::usb::{config::*, UsbError}; /// Capability offset or size. pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; diff --git a/usb/src/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs similarity index 99% rename from usb/src/xhci/xhci_ring.rs rename to devices/src/usb/xhci/xhci_ring.rs index 8dd36ae48..376f92498 100644 --- a/usb/src/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -19,9 +19,9 @@ use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use log::debug; +use super::super::UsbError; +use super::xhci_controller::dma_read_bytes; use super::{TRBType, TRB_C, TRB_LK_TC, TRB_SIZE, TRB_TR_CH, TRB_TYPE_MASK, TRB_TYPE_SHIFT}; -use crate::xhci::xhci_controller::dma_read_bytes; -use crate::UsbError; const TRB_LINK_LIMIT: u32 = 32; /// The max size of a ring segment in bytes is 64k. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 1a11a3020..e44ba56d4 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -30,7 +30,6 @@ sysbus = { path = "../sysbus" } util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } -usb = { path = "../usb" } [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 64b5b5f3d..956849b67 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -44,6 +44,10 @@ use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; +#[cfg(not(target_env = "musl"))] +use devices::usb::{ + keyboard::UsbKeyboard, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, +}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, @@ -62,10 +66,6 @@ use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps}; -#[cfg(not(target_env = "musl"))] -use usb::{ - keyboard::UsbKeyboard, tablet::UsbTablet, usb::UsbDeviceOps, xhci::xhci_pci::XhciPciDevice, -}; use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 7ed441d33..9702f5452 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -17,4 +17,3 @@ util = { path = "../../util" } acpi = { path = "../../acpi" } machine = { path = "../../machine" } virtio = { path = "../../virtio"} -usb = { path = "../../usb" } diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 948846cc6..79e5df2e2 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -28,13 +28,12 @@ use super::{ }; use crate::libdriver::pci::{PciMsixOps, PCI_DEVICE_ID}; use crate::libtest::{test_init, TestState}; -use usb::{ +use devices::usb::{ config::*, hid::{ HID_GET_IDLE, HID_GET_PROTOCOL, HID_GET_REPORT, HID_SET_IDLE, HID_SET_PROTOCOL, HID_SET_REPORT, }, - usb::UsbDeviceRequest, xhci::{ xhci_controller::{ DwordOrder, XhciEpCtx, XhciInputCtrlCtx, XhciSlotCtx, EP_RUNNING, SLOT_DEFAULT, @@ -46,6 +45,7 @@ use usb::{ }, TRBCCode, TRBType, TRB_SIZE, }, + UsbDeviceRequest, }; pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index f18d4fd6b..788bf5b8c 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -10,6 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use devices::usb::config::*; +use devices::usb::xhci::xhci_controller::{ + DwordOrder, XhciInputCtrlCtx, XhciSlotCtx, EP_RUNNING, SLOT_ADDRESSED, +}; +use devices::usb::xhci::xhci_regs::{ + XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_USBSTS, +}; +use devices::usb::xhci::{TRBCCode, TRBType, TRB_SIZE}; +use devices::usb::UsbDeviceRequest; use mod_test::libdriver::pci::{PCI_DEVICE_ID, PCI_VENDOR_ID}; use mod_test::libdriver::usb::{ clear_iovec, qmp_send_key_event, qmp_send_multi_key_event, qmp_send_pointer_event, TestIovec, @@ -17,15 +26,6 @@ use mod_test::libdriver::usb::{ HID_POINTER_LEN, KEYCODE_NUM1, KEYCODE_SPACE, PCI_CLASS_PI, PRIMARY_INTERRUPTER_ID, TD_TRB_LIMIT, XHCI_PCI_OPER_OFFSET, XHCI_PORTSC_OFFSET, }; -use usb::config::*; -use usb::usb::UsbDeviceRequest; -use usb::xhci::xhci_controller::{ - DwordOrder, XhciInputCtrlCtx, XhciSlotCtx, EP_RUNNING, SLOT_ADDRESSED, -}; -use usb::xhci::xhci_regs::{ - XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_USBSTS, -}; -use usb::xhci::{TRBCCode, TRBType, TRB_SIZE}; #[test] fn test_xhci_keyboard_basic() { diff --git a/usb/Cargo.toml b/usb/Cargo.toml deleted file mode 100644 index 8aaeef21a..000000000 --- a/usb/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "usb" -version = "2.2.0" -authors = ["Huawei StratoVirt Team"] -edition = "2021" -license = "Mulan PSL v2" -description = "USB controller and device emulation" - -[dependencies] -byteorder = "1.3.4" -thiserror = "1.0" -anyhow = "1.0" -libc = "0.2" -log = "0.4.8" -once_cell = "1.9.0" -address_space = { path = "../address_space" } -util = { path = "../util" } -pci = { path = "../pci" } -machine_manager = { path = "../machine_manager" } - -[target.'cfg(not(target_env = "musl"))'.dependencies] -ui = { path = "../ui" } \ No newline at end of file diff --git a/usb/src/lib.rs b/usb/src/lib.rs deleted file mode 100644 index 90d55ad6b..000000000 --- a/usb/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod error; -pub use anyhow::Result; -pub use error::UsbError; - -pub mod config; -mod descriptor; -pub mod hid; -#[cfg(not(target_env = "musl"))] -pub mod keyboard; -#[cfg(not(target_env = "musl"))] -pub mod tablet; -pub mod usb; -pub mod xhci; -- Gitee From c6efa22f23a888a2e6c0bd4e1affa49fabe7d710 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 2 Mar 2023 13:06:14 +0800 Subject: [PATCH 0905/2187] cargo/package: Adjust mod organization of virtio 1. Move virtio devices into device mod. 2. Move virtio_mmio and virtio_pci into transport mod. 3. Rename virtioqueue as queue. Signed-off-by: Keqian Zhu --- tests/mod_test/tests/block_test.rs | 2 +- virtio/src/{ => device}/balloon.rs | 4 +-- virtio/src/{ => device}/block.rs | 6 ++-- virtio/src/{ => device}/console.rs | 4 +-- virtio/src/{ => device}/gpu.rs | 4 +-- virtio/src/device/mod.rs | 20 +++++++++++ virtio/src/{ => device}/net.rs | 10 +++--- virtio/src/{ => device}/rng.rs | 6 ++-- virtio/src/{ => device}/scsi/bus.rs | 0 virtio/src/{ => device}/scsi/controller.rs | 10 +++--- virtio/src/{ => device}/scsi/disk.rs | 0 virtio/src/{ => device}/scsi/mod.rs | 0 virtio/src/lib.rs | 40 +++++++++------------- virtio/src/{virtqueue => queue}/mod.rs | 0 virtio/src/{virtqueue => queue}/split.rs | 0 virtio/src/transport/mod.rs | 14 ++++++++ virtio/src/{ => transport}/virtio_mmio.rs | 2 +- virtio/src/{ => transport}/virtio_pci.rs | 0 virtio/src/vhost/kernel/net.rs | 10 +++--- virtio/src/vhost/user/client.rs | 2 +- virtio/src/vhost/user/net.rs | 10 +++--- 21 files changed, 85 insertions(+), 59 deletions(-) rename virtio/src/{ => device}/balloon.rs (99%) rename virtio/src/{ => device}/block.rs (99%) rename virtio/src/{ => device}/console.rs (99%) rename virtio/src/{ => device}/gpu.rs (99%) create mode 100644 virtio/src/device/mod.rs rename virtio/src/{ => device}/net.rs (99%) rename virtio/src/{ => device}/rng.rs (99%) rename virtio/src/{ => device}/scsi/bus.rs (100%) rename virtio/src/{ => device}/scsi/controller.rs (99%) rename virtio/src/{ => device}/scsi/disk.rs (100%) rename virtio/src/{ => device}/scsi/mod.rs (100%) rename virtio/src/{virtqueue => queue}/mod.rs (100%) rename virtio/src/{virtqueue => queue}/split.rs (100%) create mode 100644 virtio/src/transport/mod.rs rename virtio/src/{ => transport}/virtio_mmio.rs (99%) rename virtio/src/{ => transport}/virtio_pci.rs (100%) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index daa794279..16675884b 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use virtio::block::VirtioBlkConfig; +use virtio::device::block::VirtioBlkConfig; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::TestVringDescEntry; diff --git a/virtio/src/balloon.rs b/virtio/src/device/balloon.rs similarity index 99% rename from virtio/src/balloon.rs rename to virtio/src/device/balloon.rs index 9772b3157..e009d9e04 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -45,7 +45,7 @@ use util::{ }; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; -use super::{ +use crate::{ error::*, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; @@ -1086,8 +1086,8 @@ impl VirtioTrace for BalloonIoHandler {} #[cfg(test)] mod tests { - pub use super::super::*; pub use super::*; + pub use crate::*; use address_space::{AddressRange, HostMemMapping, Region}; diff --git a/virtio/src/block.rs b/virtio/src/device/block.rs similarity index 99% rename from virtio/src/block.rs rename to virtio/src/device/block.rs index b5120bdce..1662b8e48 100644 --- a/virtio/src/block.rs +++ b/virtio/src/device/block.rs @@ -22,7 +22,8 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::time::Instant; -use super::{ +use crate::VirtioError; +use crate::{ iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, @@ -30,7 +31,6 @@ use super::{ VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; -use crate::VirtioError; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; @@ -1225,8 +1225,8 @@ impl VirtioTrace for AioCompleteCb {} #[cfg(test)] mod tests { - use super::super::*; use super::*; + use crate::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use machine_manager::config::{IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; use std::sync::atomic::{AtomicU32, Ordering}; diff --git a/virtio/src/console.rs b/virtio/src/device/console.rs similarity index 99% rename from virtio/src/console.rs rename to virtio/src/device/console.rs index cf3d57986..c5df38edc 100644 --- a/virtio/src/console.rs +++ b/virtio/src/device/console.rs @@ -16,11 +16,11 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{cmp, usize}; -use super::{ +use crate::VirtioError; +use crate::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; -use crate::VirtioError; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use devices::legacy::{Chardev, InputReceiver}; diff --git a/virtio/src/gpu.rs b/virtio/src/device/gpu.rs similarity index 99% rename from virtio/src/gpu.rs rename to virtio/src/device/gpu.rs index 6733b7c76..a0d49dba4 100644 --- a/virtio/src/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -10,7 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::{ +use crate::{iov_discard_front, iov_to_buf, VirtioError, VIRTIO_GPU_F_EDID}; +use crate::{ Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, @@ -22,7 +23,6 @@ use super::{ VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; -use crate::{iov_discard_front, iov_to_buf, VirtioError, VIRTIO_GPU_F_EDID}; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Result}; use log::{error, warn}; diff --git a/virtio/src/device/mod.rs b/virtio/src/device/mod.rs new file mode 100644 index 000000000..d2ab84f4d --- /dev/null +++ b/virtio/src/device/mod.rs @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod balloon; +pub mod block; +pub mod console; +#[cfg(not(target_env = "musl"))] +pub mod gpu; +pub mod net; +pub mod rng; +pub mod scsi; diff --git a/virtio/src/net.rs b/virtio/src/device/net.rs similarity index 99% rename from virtio/src/net.rs rename to virtio/src/device/net.rs index aacadb1b8..260f3e4b4 100644 --- a/virtio/src/net.rs +++ b/virtio/src/device/net.rs @@ -21,7 +21,11 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; -use super::{ +use crate::{ + iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, + Element, VirtioError, +}; +use crate::{ Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, @@ -36,10 +40,6 @@ use super::{ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; -use crate::{ - iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, - Element, VirtioError, -}; use address_space::{AddressSpace, RegionCache}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; diff --git a/virtio/src/rng.rs b/virtio/src/device/rng.rs similarity index 99% rename from virtio/src/rng.rs rename to virtio/src/device/rng.rs index de747b67c..aba627982 100644 --- a/virtio/src/rng.rs +++ b/virtio/src/device/rng.rs @@ -37,11 +37,11 @@ use migration_derive::{ByteCode, Desc}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use super::{ +use crate::error::VirtioError; +use crate::{ ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; -use crate::error::VirtioError; use anyhow::{anyhow, bail, Context, Result}; const QUEUE_NUM_RNG: usize = 1; @@ -379,8 +379,8 @@ impl VirtioTrace for RngHandler {} #[cfg(test)] mod tests { - use super::super::*; use super::*; + use crate::*; use std::io::Write; use std::mem::size_of; diff --git a/virtio/src/scsi/bus.rs b/virtio/src/device/scsi/bus.rs similarity index 100% rename from virtio/src/scsi/bus.rs rename to virtio/src/device/scsi/bus.rs diff --git a/virtio/src/scsi/controller.rs b/virtio/src/device/scsi/controller.rs similarity index 99% rename from virtio/src/scsi/controller.rs rename to virtio/src/device/scsi/controller.rs index b51c430cf..8197a5cf2 100644 --- a/virtio/src/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -21,16 +21,16 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; -use super::super::{ - report_virtio_error, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, - VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, -}; use crate::ScsiBus::{ virtio_scsi_get_lun, ScsiBus, ScsiRequest, ScsiSense, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_OPCODE, }; use crate::VirtioError; +use crate::{ + report_virtio_error, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, + VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, +}; use address_space::{AddressSpace, GuestAddress}; use log::{debug, error, info}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; diff --git a/virtio/src/scsi/disk.rs b/virtio/src/device/scsi/disk.rs similarity index 100% rename from virtio/src/scsi/disk.rs rename to virtio/src/device/scsi/disk.rs diff --git a/virtio/src/scsi/mod.rs b/virtio/src/device/scsi/mod.rs similarity index 100% rename from virtio/src/scsi/mod.rs rename to virtio/src/device/scsi/mod.rs diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b747949b9..8fd8e559b 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -25,38 +25,30 @@ //! - `x86_64` //! - `aarch64` -mod balloon; -pub mod block; -mod console; +pub mod device; pub mod error; -#[cfg(not(target_env = "musl"))] -mod gpu; -mod net; -mod rng; -mod scsi; +mod queue; +mod transport; pub mod vhost; -mod virtio_mmio; -mod virtio_pci; -mod virtqueue; pub use anyhow::Result; -pub use balloon::*; -pub use block::{Block, BlockState}; -pub use console::{Console, VirtioConsoleState}; +pub use device::balloon::*; +pub use device::block::{Block, BlockState}; +pub use device::console::{Console, VirtioConsoleState}; +#[cfg(not(target_env = "musl"))] +pub use device::gpu::*; +pub use device::net::*; +pub use device::rng::{Rng, RngState}; +pub use device::scsi::bus as ScsiBus; +pub use device::scsi::controller as ScsiCntlr; +pub use device::scsi::disk as ScsiDisk; pub use error::VirtioError; pub use error::*; -#[cfg(not(target_env = "musl"))] -pub use gpu::*; use log::{error, warn}; -pub use net::*; -pub use rng::{Rng, RngState}; -pub use scsi::bus as ScsiBus; -pub use scsi::controller as ScsiCntlr; -pub use scsi::disk as ScsiDisk; +pub use queue::*; +pub use transport::virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; +pub use transport::virtio_pci::VirtioPciDevice; pub use vhost::kernel as VhostKern; pub use vhost::user as VhostUser; -pub use virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; -pub use virtio_pci::VirtioPciDevice; -pub use virtqueue::*; use std::cmp; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/virtio/src/virtqueue/mod.rs b/virtio/src/queue/mod.rs similarity index 100% rename from virtio/src/virtqueue/mod.rs rename to virtio/src/queue/mod.rs diff --git a/virtio/src/virtqueue/split.rs b/virtio/src/queue/split.rs similarity index 100% rename from virtio/src/virtqueue/split.rs rename to virtio/src/queue/split.rs diff --git a/virtio/src/transport/mod.rs b/virtio/src/transport/mod.rs new file mode 100644 index 000000000..9a56e1418 --- /dev/null +++ b/virtio/src/transport/mod.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod virtio_mmio; +pub mod virtio_pci; diff --git a/virtio/src/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs similarity index 99% rename from virtio/src/virtio_mmio.rs rename to virtio/src/transport/virtio_mmio.rs index 316989866..1ae4b2165 100644 --- a/virtio/src/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -25,7 +25,7 @@ use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; -use super::{ +use crate::{ virtio_has_feature, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, VirtioInterruptType, CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, NOTIFY_REG_OFFSET, diff --git a/virtio/src/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs similarity index 100% rename from virtio/src/virtio_pci.rs rename to virtio/src/transport/virtio_pci.rs diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 52aaac6f7..1e4d30896 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -28,17 +28,17 @@ use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::super::{ - net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, +use super::super::{VhostNotify, VhostOps}; +use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; +use crate::virtio_has_feature; +use crate::{ + device::net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; -use crate::virtio_has_feature; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 7a1bf5390..81b1a5f87 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -35,7 +35,7 @@ use super::message::{ VhostUserMsgReq, VhostUserVringAddr, VhostUserVringState, }; use super::sock::VhostUserSock; -use crate::block::VirtioBlkConfig; +use crate::device::block::VirtioBlkConfig; use crate::virtio_has_feature; use crate::VhostUser::message::VhostUserConfig; use anyhow::{anyhow, bail, Context, Result}; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 9e0f7eb16..c00530305 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -25,17 +25,17 @@ use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; -use super::super::super::{ - net::{build_device_config_space, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, +use super::super::VhostOps; +use super::{VhostBackendType, VhostUserClient}; +use crate::error::VirtioError; +use crate::{ + device::net::{build_device_config_space, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; -use super::super::VhostOps; -use super::{VhostBackendType, VhostUserClient}; -use crate::error::VirtioError; use anyhow::{anyhow, Context, Result}; /// Number of virtqueues. -- Gitee From b84385e61d19dc26aeed7a63a3e19b0f3ea38156 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 23 Mar 2023 19:37:31 +0800 Subject: [PATCH 0906/2187] virtio_pci: Refactor activate_device() and deactivate_device() Move set_guest_notifiers and queues_register_irqfd together. Signed-off-by: Keqian Zhu --- virtio/src/transport/virtio_pci.rs | 91 ++++++++++++------------------ 1 file changed, 36 insertions(+), 55 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 37b7e9f99..252f0afd0 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -171,11 +171,6 @@ struct VirtioPciCommonConfig { impl VirtioPciCommonConfig { fn new(queue_size: u16, queue_num: usize) -> Self { - let mut queues_config = Vec::new(); - for _ in 0..queue_num { - queues_config.push(QueueConfig::new(queue_size)) - } - VirtioPciCommonConfig { features_select: 0, acked_features_select: 0, @@ -184,7 +179,7 @@ impl VirtioPciCommonConfig { config_generation: 0, queue_select: 0, msix_config: INVALID_VECTOR_NUM, - queues_config, + queues_config: vec![QueueConfig::new(queue_size); queue_num], queue_type: QUEUE_TYPE_SPLIT_VRING, } } @@ -396,11 +391,9 @@ impl VirtioPciCommonConfig { | CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_FAILED, ) { - // FIXME: handle activation failure. virtio_pci_dev.activate_device(self); } else if old_status != 0 && self.device_status == 0 { self.reset(); - // FIXME: handle deactivation failure. virtio_pci_dev.deactivate_device(); } } @@ -752,65 +745,53 @@ impl VirtioPciDevice { let arc_queue = Arc::new(Mutex::new(queue)); locked_queues.push(arc_queue.clone()); } + drop(locked_queues); - let mut queue_num = self.device.lock().unwrap().queue_num(); - // No need to create call event for control queue. - // It will be polled in StratoVirt when activating the device. - if self.device.lock().unwrap().has_control_queue() && queue_num % 2 != 0 { - queue_num -= 1; - } - let call_evts = NotifyEventFds::new(queue_num); - let queue_evts = (*self.notify_eventfds).clone().events; - if let Some(cb) = self.interrupt_cb.clone() { - if self.need_irqfd { - if let Err(e) = self - .device - .lock() - .unwrap() - .set_guest_notifiers(&call_evts.events) - { - error!("Failed to set guest notifiers, error is {:?}", e); - return false; - } + update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); + if self.need_irqfd { + let mut queue_num = self.device.lock().unwrap().queue_num(); + // No need to create call event for control queue. + // It will be polled in StratoVirt when activating the device. + if self.device.lock().unwrap().has_control_queue() && queue_num % 2 != 0 { + queue_num -= 1; } - if let Err(e) = self.device.lock().unwrap().activate( - self.sys_mem.clone(), - cb, - &locked_queues, - queue_evts, - ) { - error!("Failed to activate device, error is {:?}", e); + let call_evts = NotifyEventFds::new(queue_num); + if let Err(e) = self + .device + .lock() + .unwrap() + .set_guest_notifiers(&call_evts.events) + { + error!("Failed to set guest notifiers, error is {:?}", e); + return false; + } + if !self.queues_register_irqfd(&call_evts.events) { + error!("Failed to register queues irqfd."); return false; } - } else { - error!("Failed to activate device: No interrupt callback"); - return false; } - drop(locked_queues); - self.device_activated.store(true, Ordering::Release); - - update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); - if self.need_irqfd && !self.queues_register_irqfd(&call_evts.events) { + let queue_evts = (*self.notify_eventfds).clone().events; + if let Err(e) = self.device.lock().unwrap().activate( + self.sys_mem.clone(), + self.interrupt_cb.clone().unwrap(), + &self.queues.lock().unwrap(), + queue_evts, + ) { + error!("Failed to activate device, error is {:?}", e); return false; } + + self.device_activated.store(true, Ordering::Release); true } fn deactivate_device(&self) -> bool { - if self.need_irqfd - && self.config.msix.is_some() - && self - .config - .msix - .as_ref() - .unwrap() - .lock() - .unwrap() - .unregister_irqfd() - .is_err() - { - return false; + if self.need_irqfd && self.config.msix.is_some() { + let msix = self.config.msix.as_ref().unwrap(); + if msix.lock().unwrap().unregister_irqfd().is_err() { + return false; + } } self.queues.lock().unwrap().clear(); -- Gitee From 2901d5d7190f817082dba621554adc7eda88b6fb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 23 Mar 2023 19:43:56 +0800 Subject: [PATCH 0907/2187] irqfd: Delete unused call_evts irqfd These call_evts are not used, remove them. Signed-off-by: Keqian Zhu --- virtio/src/vhost/user/block.rs | 10 ---------- virtio/src/vhost/user/net.rs | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 7677978dd..b26cc6707 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -44,8 +44,6 @@ pub struct Block { state: BlockState, /// Vhost user client client: Option>>, - /// The notifier events from host. - call_events: Vec>, } impl Block { @@ -55,7 +53,6 @@ impl Block { state: BlockState::default(), mem_space: mem_space.clone(), client: None, - call_events: Vec::>::new(), } } @@ -281,25 +278,18 @@ impl VirtioDevice for Block { .lock() .unwrap() .reset_vhost_user()?; - self.call_events.clear(); self.delete_event() } /// Unrealize device. fn unrealize(&mut self) -> Result<()> { self.delete_event()?; - self.call_events.clear(); self.client = None; Ok(()) } /// Set guest notifiers for notifying the guest. fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { - for fd in queue_evts.iter() { - let cloned_evt_fd = fd.clone(); - self.call_events.push(cloned_evt_fd); - } - match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), None => return Err(anyhow!("Failed to get client for vhost-user blk")), diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index c00530305..686de0265 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -51,8 +51,6 @@ pub struct Net { mem_space: Arc, /// Vhost user client client: Option>>, - /// The notifier events from host. - call_events: Vec>, /// EventFd for deactivate control Queue. deactivate_evts: Vec, /// Device is broken or not. @@ -66,7 +64,6 @@ impl Net { state: Arc::new(Mutex::new(VirtioNetState::default())), mem_space: mem_space.clone(), client: None, - call_events: Vec::>::new(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), } @@ -281,11 +278,6 @@ impl VirtioDevice for Net { /// Set guest notifiers for notifying the guest. fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { - for fd in queue_evts.iter() { - let cloned_evt_fd = fd.clone(); - self.call_events.push(cloned_evt_fd); - } - match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), None => return Err(anyhow!("Failed to get client for vhost-user net")), @@ -295,7 +287,6 @@ impl VirtioDevice for Net { } fn deactivate(&mut self) -> Result<()> { - self.call_events.clear(); self.clean_up()?; self.realize() } @@ -307,7 +298,6 @@ impl VirtioDevice for Net { fn unrealize(&mut self) -> Result<()> { self.delete_event()?; - self.call_events.clear(); self.client = None; Ok(()) -- Gitee From 5f5a5e9d89b62a70d9c9394e1df1b0f18a19529e Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Fri, 24 Mar 2023 10:31:54 +0800 Subject: [PATCH 0908/2187] pci: fix typo error when setting max bar num if the bar is bridge, then the max bar num shall be BAR_NUM_MAX_FOR_BRIDGE, rather than BAR_NUM_MAX_FOR_ENDPOINT. --- pci/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 89e6a605a..02a420a91 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -561,7 +561,7 @@ impl PciConfig { let mut bar_num = BAR_NUM_MAX_FOR_ENDPOINT; if self.config[HEADER_TYPE as usize] == HEADER_TYPE_BRIDGE { - bar_num = BAR_NUM_MAX_FOR_ENDPOINT; + bar_num = BAR_NUM_MAX_FOR_BRIDGE; } if ranges_overlap(old_offset, end, COMMAND as usize, (COMMAND + 1) as usize) || ranges_overlap( -- Gitee From efd132c4cf006382c1fb343cea34fe86027db429 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 24 Mar 2023 07:32:00 +0800 Subject: [PATCH 0909/2187] vhost_user_net: Do not set control queue to client The control queue is handled by StratoVirt, no need to set it to client. Signed-off-by: Keqian Zhu --- virtio/src/vhost/user/client.rs | 34 ++++++++++++--------------------- virtio/src/vhost/user/net.rs | 10 ++++++++-- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 81b1a5f87..c1aceee7d 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -17,6 +17,9 @@ use std::rc::Rc; use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, bail, Context, Result}; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + use address_space::{ AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, }; @@ -26,9 +29,8 @@ use util::loop_context::{ gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::time::NANOSECONDS_PER_SECOND; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use util::unix::do_mmap; -use super::super::super::{Queue, QueueConfig, VIRTIO_NET_F_CTRL_VQ}; use super::super::VhostOps; use super::message::{ RegionMemInfo, VhostUserHdrFlag, VhostUserMemContext, VhostUserMemHdr, VhostUserMsgHdr, @@ -36,10 +38,8 @@ use super::message::{ }; use super::sock::VhostUserSock; use crate::device::block::VirtioBlkConfig; -use crate::virtio_has_feature; use crate::VhostUser::message::VhostUserConfig; -use anyhow::{anyhow, bail, Context, Result}; -use util::unix::do_mmap; +use crate::{virtio_has_feature, Queue, QueueConfig}; /// Vhost supports multiple queue pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; @@ -406,8 +406,8 @@ impl VhostUserClient { /// Save queue info used for reconnection. pub fn set_queues(&mut self, queues: &[Arc>]) { - for (queue_index, _) in queues.iter().enumerate() { - self.queues.push(queues[queue_index].clone()); + for queue in queues.iter() { + self.queues.push(queue.clone()); } } @@ -480,11 +480,6 @@ impl VhostUserClient { self.set_mem_table() .with_context(|| "Failed to set mem table for vhost-user")?; - let mut queue_num = self.queues.len(); - if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { - queue_num -= 1; - } - let queue_size = self .queues .first() @@ -493,10 +488,10 @@ impl VhostUserClient { .unwrap() .vring .actual_size(); - self.set_inflight(queue_num as u16, queue_size)?; + self.set_inflight(self.queues.len() as u16, queue_size)?; // Set all vring num to notify ovs/dpdk how many queues it needs to poll // before setting vring info. - for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { + for (queue_index, queue_mutex) in self.queues.iter().enumerate() { let actual_size = queue_mutex.lock().unwrap().vring.actual_size(); self.set_vring_num(queue_index, actual_size) .with_context(|| { @@ -507,7 +502,7 @@ impl VhostUserClient { })?; } - for (queue_index, queue_mutex) in self.queues.iter().enumerate().take(queue_num) { + for (queue_index, queue_mutex) in self.queues.iter().enumerate() { let queue = queue_mutex.lock().unwrap(); let queue_config = queue.vring.get_queue_config(); @@ -542,7 +537,7 @@ impl VhostUserClient { })?; } - for (queue_index, _) in self.queues.iter().enumerate().take(queue_num) { + for (queue_index, _) in self.queues.iter().enumerate() { self.set_vring_enable(queue_index, true).with_context(|| { format!( "Failed to set vring enable for vhost-user, index: {}", @@ -555,12 +550,7 @@ impl VhostUserClient { } pub fn reset_vhost_user(&mut self) -> Result<()> { - let mut queue_num = self.queues.len(); - if ((self.features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { - queue_num -= 1; - } - - for (queue_index, _) in self.queues.iter().enumerate().take(queue_num) { + for (queue_index, _) in self.queues.iter().enumerate() { self.set_vring_enable(queue_index, false) .with_context(|| format!("Failed to set vring disable, index: {}", queue_index))?; self.get_vring_base(queue_index) diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 686de0265..a42fdc294 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -239,7 +239,9 @@ impl VirtioDevice for Net { ) -> Result<()> { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; - if ((driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && (queue_num % 2 != 0) { + let has_control_queue = + (driver_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0) && (queue_num % 2 != 0); + if has_control_queue { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts.remove(queue_num - 1); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); @@ -268,7 +270,11 @@ impl VirtioDevice for Net { let features = driver_features & !(1 << VIRTIO_NET_F_MAC); client.features = features; - client.set_queues(queues); + if has_control_queue { + client.set_queues(&queues[..(queue_num - 1)]); + } else { + client.set_queues(queues); + } client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; self.broken.store(false, Ordering::SeqCst); -- Gitee From 19898f838c2ff7c62a727de29fcd0c57b0661ca0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 24 Mar 2023 20:04:42 +0800 Subject: [PATCH 0910/2187] virtio: Clone queue eventfd when activate device Keep the code style same. Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 10 +++++----- virtio/src/device/block.rs | 7 +++---- virtio/src/device/console.rs | 7 +++---- virtio/src/device/gpu.rs | 6 +++--- virtio/src/device/net.rs | 8 ++++---- virtio/src/device/rng.rs | 4 ++-- virtio/src/device/scsi/controller.rs | 11 +++++------ virtio/src/vhost/kernel/net.rs | 4 ++-- virtio/src/vhost/user/net.rs | 19 ++++++++++--------- 9 files changed, 37 insertions(+), 39 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 54d3cb217..05b3cdae1 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1100,7 +1100,7 @@ impl VirtioDevice for Balloon { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { if queues.len() != self.queue_num() { return Err(anyhow!(VirtioError::IncorrectQueueNum( @@ -1110,9 +1110,9 @@ impl VirtioDevice for Balloon { } let inf_queue = queues[0].clone(); - let inf_evt = queue_evts.remove(0); + let inf_evt = queue_evts[0].clone(); let def_queue = queues[1].clone(); - let def_evt = queue_evts.remove(0); + let def_evt = queue_evts[1].clone(); let mut current_queue_index = 1; let (report_queue, report_evt) = @@ -1123,7 +1123,7 @@ impl VirtioDevice for Balloon { current_queue_index += 1; ( Some(queues[current_queue_index].clone()), - Some(queue_evts.remove(0)), + Some(queue_evts[current_queue_index].clone()), ) } else { if current_queue_index + 1 >= self.queue_num() { @@ -1144,7 +1144,7 @@ impl VirtioDevice for Balloon { current_queue_index += 1; ( Some(queues[current_queue_index].clone()), - Some(queue_evts.remove(0)), + Some(queue_evts[current_queue_index].clone()), ) } else { if current_queue_index + 1 >= self.queue_num() { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 1662b8e48..f04c1a15f 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1094,11 +1094,10 @@ impl VirtioDevice for Block { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); - for queue in queues.iter() { - let queue_evt = queue_evts.remove(0); + for (index, queue) in queues.iter().enumerate() { if !queue.lock().unwrap().is_enabled() { continue; } @@ -1110,7 +1109,7 @@ impl VirtioDevice for Block { )?); let handler = BlockIoHandler { queue: queue.clone(), - queue_evt, + queue_evt: queue_evts[index].clone(), mem_space: mem_space.clone(), disk_image: self.disk_image.clone(), req_align: self.req_align, diff --git a/virtio/src/device/console.rs b/virtio/src/device/console.rs index c5df38edc..f3d4c1116 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/console.rs @@ -353,14 +353,13 @@ impl VirtioDevice for Console { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { - queue_evts.remove(0); // input_queue_evt never used - let handler = ConsoleHandler { input_queue: queues[0].clone(), output_queue: queues[1].clone(), - output_queue_evt: queue_evts.remove(0), + // input_queue_evt never used + output_queue_evt: queue_evts[1].clone(), mem_space, interrupt_cb, driver_features: self.state.driver_features, diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a0d49dba4..4b525cd96 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1636,7 +1636,7 @@ impl VirtioDevice for Gpu { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { if queues.len() != QUEUE_NUM_GPU { return Err(anyhow!(VirtioError::IncorrectQueueNum( @@ -1659,8 +1659,8 @@ impl VirtioDevice for Gpu { ctrl_queue: queues[0].clone(), cursor_queue: queues[1].clone(), mem_space, - ctrl_queue_evt: queue_evts.remove(0), - cursor_queue_evt: queue_evts.remove(0), + ctrl_queue_evt: queue_evts[0].clone(), + cursor_queue_evt: queue_evts[1].clone(), interrupt_cb, driver_features: self.state.driver_features, resources_list: Vec::new(), diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 260f3e4b4..1d99eb08c 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1545,7 +1545,7 @@ impl VirtioDevice for Net { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); @@ -1553,7 +1553,7 @@ impl VirtioDevice for Net { let driver_features = self.state.lock().unwrap().driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); - let ctrl_queue_evt = queue_evts.remove(queue_num - 1); + let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); let ctrl_handler = NetCtrlHandler { ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, ctrl_info.clone()), @@ -1580,9 +1580,9 @@ impl VirtioDevice for Net { let queue_pairs = queue_num / 2; for index in 0..queue_pairs { let rx_queue = queues[index * 2].clone(); - let rx_queue_evt = queue_evts.remove(0); + let rx_queue_evt = queue_evts[index * 2].clone(); let tx_queue = queues[index * 2 + 1].clone(); - let tx_queue_evt = queue_evts.remove(0); + let tx_queue_evt = queue_evts[index * 2 + 1].clone(); let (sender, receiver) = channel(); senders.push(sender); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index aba627982..06c95bcc6 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -321,11 +321,11 @@ impl VirtioDevice for Rng { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { let handler = RngHandler { queue: queues[0].clone(), - queue_evt: queue_evts.remove(0), + queue_evt: queue_evts[0].clone(), interrupt_cb, driver_features: self.state.driver_features, mem_space, diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index 0a0256bfa..b1c16a87a 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -269,7 +269,7 @@ impl VirtioDevice for ScsiCntlr { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); if queue_num < SCSI_MIN_QUEUE_NUM { @@ -277,7 +277,7 @@ impl VirtioDevice for ScsiCntlr { } let ctrl_queue = queues[0].clone(); - let ctrl_queue_evt = queue_evts.remove(0); + let ctrl_queue_evt = queue_evts[0].clone(); let ctrl_handler = ScsiCtrlHandler { queue: ctrl_queue, queue_evt: ctrl_queue_evt, @@ -294,7 +294,7 @@ impl VirtioDevice for ScsiCntlr { )?; let event_queue = queues[1].clone(); - let event_queue_evt = queue_evts.remove(0); + let event_queue_evt = queue_evts[1].clone(); let event_handler = ScsiEventHandler { _queue: event_queue, queue_evt: event_queue_evt, @@ -311,13 +311,12 @@ impl VirtioDevice for ScsiCntlr { &mut self.deactivate_evts, )?; - let queues_num = queues.len(); - for cmd_queue in queues.iter().take(queues_num).skip(2) { + for (index, cmd_queue) in queues[2..].iter().enumerate() { let bus = self.bus.as_ref().unwrap(); let cmd_handler = ScsiCmdHandler { scsibus: bus.clone(), queue: cmd_queue.clone(), - queue_evt: queue_evts.remove(0), + queue_evt: queue_evts[index + 2].clone(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features: self.state.driver_features, diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 1e4d30896..d7ca2e6ba 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -252,13 +252,13 @@ impl VirtioDevice for Net { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); - let ctrl_queue_evt = queue_evts.remove(queue_num - 1); + let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); let ctrl_handler = NetCtrlHandler { diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index a42fdc294..d67c40fba 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::virtio_has_feature; use std::cmp; use std::io::Write; use std::os::unix::prelude::RawFd; @@ -30,11 +29,12 @@ use super::{VhostBackendType, VhostUserClient}; use crate::error::VirtioError; use crate::{ device::net::{build_device_config_space, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, - CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, + virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; use anyhow::{anyhow, Context, Result}; @@ -235,7 +235,7 @@ impl VirtioDevice for Net { mem_space: Arc, interrupt_cb: Arc, queues: &[Arc>], - mut queue_evts: Vec>, + queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; @@ -243,7 +243,7 @@ impl VirtioDevice for Net { (driver_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0) && (queue_num % 2 != 0); if has_control_queue { let ctrl_queue = queues[queue_num - 1].clone(); - let ctrl_queue_evt = queue_evts.remove(queue_num - 1); + let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); let ctrl_handler = NetCtrlHandler { @@ -272,10 +272,11 @@ impl VirtioDevice for Net { client.features = features; if has_control_queue { client.set_queues(&queues[..(queue_num - 1)]); + client.set_queue_evts(&queue_evts[..(queue_num - 1)]); } else { client.set_queues(queues); + client.set_queue_evts(&queue_evts); } - client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; self.broken.store(false, Ordering::SeqCst); -- Gitee From 36409f3b113dbea0e933dbc4305f1c8cfff7cc84 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 24 Mar 2023 09:19:49 +0800 Subject: [PATCH 0911/2187] virtio: Correctly handle disabled virt queues 1. Some queues must be enabled, add check for that. 2. Some queues is optional enabled, do not listen on it when it's disabled. Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 72 +++++++++-------------- virtio/src/device/block.rs | 4 -- virtio/src/device/console.rs | 7 ++- virtio/src/device/gpu.rs | 18 +++--- virtio/src/device/net.rs | 12 ++-- virtio/src/device/rng.rs | 9 +-- virtio/src/device/scsi/controller.rs | 85 ++++++++++++++-------------- virtio/src/error.rs | 4 +- virtio/src/lib.rs | 13 +++++ virtio/src/vhost/kernel/net.rs | 8 ++- virtio/src/vhost/kernel/vsock.rs | 5 +- virtio/src/vhost/user/client.rs | 16 +++--- 12 files changed, 128 insertions(+), 125 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 05b3cdae1..ac37e21f9 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -20,7 +20,6 @@ use std::{ time::Duration, }; -use crate::report_virtio_error; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; @@ -47,8 +46,9 @@ use util::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use crate::{ - error::*, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, - VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, + check_queue_enabled, error::*, report_virtio_error, virtio_has_feature, Element, Queue, + VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_BALLOON, }; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; @@ -1104,58 +1104,40 @@ impl VirtioDevice for Balloon { ) -> Result<()> { if queues.len() != self.queue_num() { return Err(anyhow!(VirtioError::IncorrectQueueNum( - QUEUE_NUM_BALLOON, + self.queue_num(), queues.len() ))); } + check_queue_enabled("balloon", queues, 0)?; + check_queue_enabled("balloon", queues, 1)?; let inf_queue = queues[0].clone(); let inf_evt = queue_evts[0].clone(); let def_queue = queues[1].clone(); let def_evt = queue_evts[1].clone(); - let mut current_queue_index = 1; - let (report_queue, report_evt) = - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) - && current_queue_index + 1 < self.queue_num() - && !queue_evts.is_empty() - { - current_queue_index += 1; - ( - Some(queues[current_queue_index].clone()), - Some(queue_evts[current_queue_index].clone()), - ) - } else { - if current_queue_index + 1 >= self.queue_num() { - error!( - "Queue index: {} is invalid, correct index is from 0 to {}!", - current_queue_index + 1, - self.queue_num() - ) - } - (None, None) - }; + // Get report queue and eventfd. + let mut queue_index = 2; + let mut report_queue = None; + let mut report_evt = None; + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { + if queues[queue_index].lock().unwrap().is_enabled() { + report_queue = Some(queues[queue_index].clone()); + report_evt = Some(queue_evts[queue_index].clone()); + } + queue_index += 1; + } + + // Get msg queue and eventfd. + let mut msg_queue = None; + let mut msg_evt = None; + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) + && queues[queue_index].lock().unwrap().is_enabled() + { + msg_queue = Some(queues[queue_index].clone()); + msg_evt = Some(queue_evts[queue_index].clone()); + } - let (msg_queue, msg_evt) = - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) - && current_queue_index + 1 < self.queue_num() - && !queue_evts.is_empty() - { - current_queue_index += 1; - ( - Some(queues[current_queue_index].clone()), - Some(queue_evts[current_queue_index].clone()), - ) - } else { - if current_queue_index + 1 >= self.queue_num() { - error!( - "Queue index: {} is invalid, correct index is from 0 to {}!", - current_queue_index + 1, - self.queue_num() - ) - } - (None, None) - }; self.interrupt_cb = Some(interrupt_cb.clone()); let handler = BalloonIoHandler { driver_features: self.driver_features, diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index f04c1a15f..d216d6c82 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -542,10 +542,6 @@ impl BlockIoHandler { let mut done = false; let start_time = Instant::now(); - if !self.queue.lock().unwrap().is_enabled() { - done = true; - return Ok(done); - } while self .queue .lock() diff --git a/virtio/src/device/console.rs b/virtio/src/device/console.rs index f3d4c1116..80622ce8b 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/console.rs @@ -16,10 +16,9 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{cmp, usize}; -use crate::VirtioError; use crate::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_SIZE, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, + check_queue_enabled, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VirtioTrace, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; @@ -355,6 +354,8 @@ impl VirtioDevice for Console { queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + check_queue_enabled("console", queues, 0)?; + check_queue_enabled("console", queues, 1)?; let handler = ConsoleHandler { input_queue: queues[0].clone(), output_queue: queues[1].clone(), diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 4b525cd96..9432e8224 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -10,18 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{iov_discard_front, iov_to_buf, VirtioError, VIRTIO_GPU_F_EDID}; use crate::{ - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, - VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + check_queue_enabled, iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, + VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, + VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, - VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, + VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Result}; @@ -1644,6 +1644,8 @@ impl VirtioDevice for Gpu { queues.len() ))); } + check_queue_enabled("gpu", queues, 0)?; + check_queue_enabled("gpu", queues, 1)?; self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 1d99eb08c..119837499 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -22,12 +22,10 @@ use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; use crate::{ - iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, - Element, VirtioError, -}; -use crate::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, + check_queue_enabled, iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, + virtio_has_feature, ElemIovec, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, @@ -888,6 +886,7 @@ impl NetIoHandler { if !queue.is_enabled() { return Ok(()); } + let mut tx_packets = 0; loop { let elem = queue @@ -1552,6 +1551,7 @@ impl VirtioDevice for Net { self.ctrl_info = Some(ctrl_info.clone()); let driver_features = self.state.lock().unwrap().driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { + check_queue_enabled("net", queues, queue_num)?; let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 06c95bcc6..d2f664c47 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -39,8 +39,8 @@ use vmm_sys_util::eventfd::EventFd; use crate::error::VirtioError; use crate::{ - ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, + check_queue_enabled, ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; use anyhow::{anyhow, bail, Context, Result}; @@ -86,10 +86,6 @@ impl RngHandler { let mut queue_lock = self.queue.lock().unwrap(); let mut need_interrupt = false; - if !queue_lock.is_enabled() { - return Ok(()); - } - while let Ok(elem) = queue_lock .vring .pop_avail(&self.mem_space, self.driver_features) @@ -323,6 +319,7 @@ impl VirtioDevice for Rng { queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + check_queue_enabled("rng", queues, 0)?; let handler = RngHandler { queue: queues[0].clone(), queue_evt: queue_evts[0].clone(), diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index b1c16a87a..2a05e8154 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -25,11 +25,10 @@ use crate::ScsiBus::{ virtio_scsi_get_lun, ScsiBus, ScsiRequest, ScsiSense, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_OPCODE, }; -use crate::VirtioError; use crate::{ - report_virtio_error, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, - VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, + report_virtio_error, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; use log::{debug, error, info}; @@ -277,41 +276,49 @@ impl VirtioDevice for ScsiCntlr { } let ctrl_queue = queues[0].clone(); - let ctrl_queue_evt = queue_evts[0].clone(); - let ctrl_handler = ScsiCtrlHandler { - queue: ctrl_queue, - queue_evt: ctrl_queue_evt, - mem_space: mem_space.clone(), - interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, - device_broken: self.broken.clone(), - }; - let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); - register_event_helper( - notifiers, - self.config.iothread.as_ref(), - &mut self.deactivate_evts, - )?; + if ctrl_queue.lock().unwrap().is_enabled() { + let ctrl_queue_evt = queue_evts[0].clone(); + let ctrl_handler = ScsiCtrlHandler { + queue: ctrl_queue, + queue_evt: ctrl_queue_evt, + mem_space: mem_space.clone(), + interrupt_cb: interrupt_cb.clone(), + driver_features: self.state.driver_features, + device_broken: self.broken.clone(), + }; + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); + register_event_helper( + notifiers, + self.config.iothread.as_ref(), + &mut self.deactivate_evts, + )?; + } let event_queue = queues[1].clone(); - let event_queue_evt = queue_evts[1].clone(); - let event_handler = ScsiEventHandler { - _queue: event_queue, - queue_evt: event_queue_evt, - _mem_space: mem_space.clone(), - _interrupt_cb: interrupt_cb.clone(), - _driver_features: self.state.driver_features, - device_broken: self.broken.clone(), - }; - let notifiers = - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))); - register_event_helper( - notifiers, - self.config.iothread.as_ref(), - &mut self.deactivate_evts, - )?; + if event_queue.lock().unwrap().is_enabled() { + let event_queue_evt = queue_evts[1].clone(); + let event_handler = ScsiEventHandler { + _queue: event_queue, + queue_evt: event_queue_evt, + _mem_space: mem_space.clone(), + _interrupt_cb: interrupt_cb.clone(), + _driver_features: self.state.driver_features, + device_broken: self.broken.clone(), + }; + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))); + register_event_helper( + notifiers, + self.config.iothread.as_ref(), + &mut self.deactivate_evts, + )?; + } for (index, cmd_queue) in queues[2..].iter().enumerate() { + if !cmd_queue.lock().unwrap().is_enabled() { + continue; + } let bus = self.bus.as_ref().unwrap(); let cmd_handler = ScsiCmdHandler { scsibus: bus.clone(), @@ -649,10 +656,6 @@ impl ScsiCtrlHandler { } fn handle_ctrl_request(&mut self) -> Result<()> { - if !self.queue.lock().unwrap().is_enabled() { - return Ok(()); - } - loop { let mut queue = self.queue.lock().unwrap(); let elem = queue @@ -894,10 +897,6 @@ impl ScsiCmdHandler { } fn handle_cmd_request(&mut self) -> Result<()> { - if !self.queue.lock().unwrap().is_enabled() { - return Ok(()); - } - loop { let mut queue = self.queue.lock().unwrap(); let elem = queue diff --git a/virtio/src/error.rs b/virtio/src/error.rs index 13ceb8aae..a53c1d6f4 100644 --- a/virtio/src/error.rs +++ b/virtio/src/error.rs @@ -56,8 +56,10 @@ pub enum VirtioError { VhostIoctl(String), #[error("Failed to get iovec from element!")] ElementEmpty, - #[error("IVirt queue is none!o")] + #[error("Virt queue is none!")] VirtQueueIsNone, + #[error("Device {0} virt queue {1} is not enabled!")] + VirtQueueNotEnabled(String, usize), #[error("Cannot perform activate. Expected {0} queue(s), got {1}")] IncorrectQueueNum(usize, usize), #[error("Incorrect offset, expected {0}, got {1}")] diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 8fd8e559b..3809bf0e7 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -62,6 +62,8 @@ use util::aio::mem_to_buf; use util::num_ops::write_u32; use vmm_sys_util::eventfd::EventFd; +use crate::VirtioError::VirtQueueNotEnabled; + /// Check if the bit of features is configured. pub fn virtio_has_feature(feature: u64, fbit: u32) -> bool { feature & (1 << fbit) != 0 @@ -435,6 +437,17 @@ pub trait VirtioTrace { } } +pub fn check_queue_enabled( + dev_name: &str, + queues: &[Arc>], + index: usize, +) -> Result<()> { + if !queues[index].lock().unwrap().is_enabled() { + return Err(anyhow!(VirtQueueNotEnabled(dev_name.to_string(), index))); + } + Ok(()) +} + /// The function used to inject interrupt to guest when encounter an virtio error. pub fn report_virtio_error( interrupt_cb: Arc, diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index d7ca2e6ba..f520a4d5d 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -30,7 +30,7 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; -use crate::virtio_has_feature; +use crate::{check_queue_enabled, virtio_has_feature}; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_ACCESS_PLATFORM, @@ -257,6 +257,7 @@ impl VirtioDevice for Net { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { + check_queue_enabled("vhost kernel net", queues, queue_num)?; let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); @@ -280,6 +281,11 @@ impl VirtioDevice for Net { let queue_pairs = queue_num / 2; for index in 0..queue_pairs { + if !queues[index * 2].lock().unwrap().is_enabled() + || !queues[index * 2 + 1].lock().unwrap().is_enabled() + { + continue; + } let mut host_notifies = Vec::new(); let backend = match &self.backends { None => return Err(anyhow!("Failed to get backend for vhost net")), diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 17bc342d5..f6c444968 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -26,7 +26,7 @@ use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use crate::VirtioError; +use crate::{check_queue_enabled, VirtioError}; use anyhow::{anyhow, bail, Context, Result}; use super::super::super::{ @@ -256,8 +256,11 @@ impl VirtioDevice for Vsock { let cid = self.vsock_cfg.guest_cid; let mut host_notifies = Vec::new(); // The receive queue and transmit queue will be handled in vhost. + check_queue_enabled("vhost kernel vsock", queues, 0)?; + check_queue_enabled("vhost kernel vsock", queues, 1)?; let vhost_queues = queues[..2].to_vec(); // This event queue will be handled. + check_queue_enabled("vhost kernel vsock", queues, 2)?; self.event_queue = Some(queues[2].clone()); self.interrupt_cb = Some(interrupt_cb.clone()); diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index c1aceee7d..11ec96fe6 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -537,13 +537,15 @@ impl VhostUserClient { })?; } - for (queue_index, _) in self.queues.iter().enumerate() { - self.set_vring_enable(queue_index, true).with_context(|| { - format!( - "Failed to set vring enable for vhost-user, index: {}", - queue_index, - ) - })?; + for (queue_index, queue) in self.queues.iter().enumerate() { + let enabled = queue.lock().unwrap().is_enabled(); + self.set_vring_enable(queue_index, enabled) + .with_context(|| { + format!( + "Failed to set vring enable for vhost-user, index: {}", + queue_index, + ) + })?; } Ok(()) -- Gitee From 8679c8ee0fd25d373de65ec233f2a2ce4241aa5e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 01:46:03 +0800 Subject: [PATCH 0912/2187] virtqueue: Fix queue enable checking According to virtio spec, no queue must be enabled. If queue is not enabled, pop_avail will return empty element now. Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 17 ++----- virtio/src/device/console.rs | 6 +-- virtio/src/device/gpu.rs | 24 +++++----- virtio/src/device/net.rs | 18 ++----- virtio/src/device/rng.rs | 5 +- virtio/src/device/scsi/controller.rs | 70 ++++++++++++---------------- virtio/src/lib.rs | 13 ------ virtio/src/queue/split.rs | 2 +- virtio/src/vhost/kernel/net.rs | 17 ++----- virtio/src/vhost/kernel/vsock.rs | 18 +++---- 10 files changed, 69 insertions(+), 121 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index ac37e21f9..f0026c3fb 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -46,9 +46,8 @@ use util::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use crate::{ - check_queue_enabled, error::*, report_virtio_error, virtio_has_feature, Element, Queue, - VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_BALLOON, + error::*, report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, + VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; @@ -1109,8 +1108,6 @@ impl VirtioDevice for Balloon { ))); } - check_queue_enabled("balloon", queues, 0)?; - check_queue_enabled("balloon", queues, 1)?; let inf_queue = queues[0].clone(); let inf_evt = queue_evts[0].clone(); let def_queue = queues[1].clone(); @@ -1121,19 +1118,15 @@ impl VirtioDevice for Balloon { let mut report_queue = None; let mut report_evt = None; if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { - if queues[queue_index].lock().unwrap().is_enabled() { - report_queue = Some(queues[queue_index].clone()); - report_evt = Some(queue_evts[queue_index].clone()); - } + report_queue = Some(queues[queue_index].clone()); + report_evt = Some(queue_evts[queue_index].clone()); queue_index += 1; } // Get msg queue and eventfd. let mut msg_queue = None; let mut msg_evt = None; - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) - && queues[queue_index].lock().unwrap().is_enabled() - { + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { msg_queue = Some(queues[queue_index].clone()); msg_evt = Some(queue_evts[queue_index].clone()); } diff --git a/virtio/src/device/console.rs b/virtio/src/device/console.rs index 80622ce8b..73ecfac49 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/console.rs @@ -17,8 +17,8 @@ use std::sync::{Arc, Mutex}; use std::{cmp, usize}; use crate::{ - check_queue_enabled, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, - VirtioTrace, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, + Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; @@ -354,8 +354,6 @@ impl VirtioDevice for Console { queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { - check_queue_enabled("console", queues, 0)?; - check_queue_enabled("console", queues, 1)?; let handler = ConsoleHandler { input_queue: queues[0].clone(), output_queue: queues[1].clone(), diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 9432e8224..69fea18b4 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -11,17 +11,17 @@ // See the Mulan PSL v2 for more details. use crate::{ - check_queue_enabled, iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, - VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, - VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, - VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, - VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, - VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, + iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_F_EDID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, + VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Result}; @@ -1644,8 +1644,6 @@ impl VirtioDevice for Gpu { queues.len() ))); } - check_queue_enabled("gpu", queues, 0)?; - check_queue_enabled("gpu", queues, 1)?; self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 119837499..46bcae3df 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -22,12 +22,11 @@ use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; use crate::{ - check_queue_enabled, iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, - virtio_has_feature, ElemIovec, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, - VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, + iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, + Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, + VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, + VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_CTRL_VLAN, @@ -761,9 +760,6 @@ impl NetIoHandler { fn handle_rx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to rx".to_string()); let mut queue = self.rx.queue.lock().unwrap(); - if !queue.is_enabled() { - return Ok(()); - } let mut rx_packets = 0; while let Some(tap) = self.tap.as_mut() { @@ -883,9 +879,6 @@ impl NetIoHandler { fn handle_tx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); - if !queue.is_enabled() { - return Ok(()); - } let mut tx_packets = 0; loop { @@ -1551,7 +1544,6 @@ impl VirtioDevice for Net { self.ctrl_info = Some(ctrl_info.clone()); let driver_features = self.state.lock().unwrap().driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { - check_queue_enabled("net", queues, queue_num)?; let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index d2f664c47..d1c55a872 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -39,8 +39,8 @@ use vmm_sys_util::eventfd::EventFd; use crate::error::VirtioError; use crate::{ - check_queue_enabled, ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, + ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; use anyhow::{anyhow, bail, Context, Result}; @@ -319,7 +319,6 @@ impl VirtioDevice for Rng { queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { - check_queue_enabled("rng", queues, 0)?; let handler = RngHandler { queue: queues[0].clone(), queue_evt: queue_evts[0].clone(), diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index bc85b7106..ffed2fa37 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -272,49 +272,41 @@ impl VirtioDevice for ScsiCntlr { } let ctrl_queue = queues[0].clone(); - if ctrl_queue.lock().unwrap().is_enabled() { - let ctrl_queue_evt = queue_evts[0].clone(); - let ctrl_handler = ScsiCtrlHandler { - queue: ctrl_queue, - queue_evt: ctrl_queue_evt, - mem_space: mem_space.clone(), - interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, - device_broken: self.broken.clone(), - }; - let notifiers = - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); - register_event_helper( - notifiers, - self.config.iothread.as_ref(), - &mut self.deactivate_evts, - )?; - } + let ctrl_queue_evt = queue_evts[0].clone(); + let ctrl_handler = ScsiCtrlHandler { + queue: ctrl_queue, + queue_evt: ctrl_queue_evt, + mem_space: mem_space.clone(), + interrupt_cb: interrupt_cb.clone(), + driver_features: self.state.driver_features, + device_broken: self.broken.clone(), + }; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); + register_event_helper( + notifiers, + self.config.iothread.as_ref(), + &mut self.deactivate_evts, + )?; let event_queue = queues[1].clone(); - if event_queue.lock().unwrap().is_enabled() { - let event_queue_evt = queue_evts[1].clone(); - let event_handler = ScsiEventHandler { - _queue: event_queue, - queue_evt: event_queue_evt, - _mem_space: mem_space.clone(), - _interrupt_cb: interrupt_cb.clone(), - _driver_features: self.state.driver_features, - device_broken: self.broken.clone(), - }; - let notifiers = - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))); - register_event_helper( - notifiers, - self.config.iothread.as_ref(), - &mut self.deactivate_evts, - )?; - } + let event_queue_evt = queue_evts[1].clone(); + let event_handler = ScsiEventHandler { + _queue: event_queue, + queue_evt: event_queue_evt, + _mem_space: mem_space.clone(), + _interrupt_cb: interrupt_cb.clone(), + _driver_features: self.state.driver_features, + device_broken: self.broken.clone(), + }; + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))); + register_event_helper( + notifiers, + self.config.iothread.as_ref(), + &mut self.deactivate_evts, + )?; for (index, cmd_queue) in queues[2..].iter().enumerate() { - if !cmd_queue.lock().unwrap().is_enabled() { - continue; - } let bus = self.bus.as_ref().unwrap(); let cmd_handler = ScsiCmdHandler { scsibus: bus.clone(), diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b52dc143d..84df32e51 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -63,8 +63,6 @@ use util::num_ops::write_u32; use util::AsAny; use vmm_sys_util::eventfd::EventFd; -use crate::VirtioError::VirtQueueNotEnabled; - /// Check if the bit of features is configured. pub fn virtio_has_feature(feature: u64, fbit: u32) -> bool { feature & (1 << fbit) != 0 @@ -438,17 +436,6 @@ pub trait VirtioTrace { } } -pub fn check_queue_enabled( - dev_name: &str, - queues: &[Arc>], - index: usize, -) -> Result<()> { - if !queues[index].lock().unwrap().is_enabled() { - return Err(anyhow!(VirtQueueNotEnabled(dev_name.to_string(), index))); - } - Ok(()) -} - /// The function used to inject interrupt to guest when encounter an virtio error. pub fn report_virtio_error( interrupt_cb: Arc, diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 564fd0401..8a4c40e50 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -749,7 +749,7 @@ impl VringOps for SplitVring { fn pop_avail(&mut self, sys_mem: &Arc, features: u64) -> Result { let mut element = Element::new(0); - if self.avail_ring_len(sys_mem)? == 0 { + if !self.is_enabled() || self.avail_ring_len(sys_mem)? == 0 { return Ok(element); } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index f520a4d5d..f69428b35 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -30,14 +30,13 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; -use crate::{check_queue_enabled, virtio_has_feature}; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, - CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, VIRTIO_F_ACCESS_PLATFORM, - VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, + virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, + VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; /// Number of virtqueues. @@ -257,7 +256,6 @@ impl VirtioDevice for Net { let queue_num = queues.len(); let driver_features = self.state.lock().unwrap().driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { - check_queue_enabled("vhost kernel net", queues, queue_num)?; let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); @@ -281,11 +279,6 @@ impl VirtioDevice for Net { let queue_pairs = queue_num / 2; for index in 0..queue_pairs { - if !queues[index * 2].lock().unwrap().is_enabled() - || !queues[index * 2 + 1].lock().unwrap().is_enabled() - { - continue; - } let mut host_notifies = Vec::new(); let backend = match &self.backends { None => return Err(anyhow!("Failed to get backend for vhost net")), diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index f6c444968..fc83dbd89 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -14,6 +14,10 @@ use std::os::unix::io::RawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, bail, Context, Result}; +use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::ioctl::ioctl_with_ref; + use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; @@ -23,17 +27,12 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; -use vmm_sys_util::eventfd::EventFd; -use vmm_sys_util::ioctl::ioctl_with_ref; - -use crate::{check_queue_enabled, VirtioError}; -use anyhow::{anyhow, bail, Context, Result}; -use super::super::super::{ - Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_VSOCK, -}; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; +use crate::{ + Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_VSOCK, +}; /// Number of virtqueues. const QUEUE_NUM_VSOCK: usize = 3; @@ -256,11 +255,8 @@ impl VirtioDevice for Vsock { let cid = self.vsock_cfg.guest_cid; let mut host_notifies = Vec::new(); // The receive queue and transmit queue will be handled in vhost. - check_queue_enabled("vhost kernel vsock", queues, 0)?; - check_queue_enabled("vhost kernel vsock", queues, 1)?; let vhost_queues = queues[..2].to_vec(); // This event queue will be handled. - check_queue_enabled("vhost kernel vsock", queues, 2)?; self.event_queue = Some(queues[2].clone()); self.interrupt_cb = Some(interrupt_cb.clone()); -- Gitee From 10a9e4f4e45f3ef2b76baafcf8b1bcf9326ec437 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 23 Mar 2023 17:14:23 +0800 Subject: [PATCH 0913/2187] memory: Resolve memory leaks when creating backend file Retakes ownership of a CString that was transferred to C via CString::into_raw. Signed-off-by: jiewangqun --- address_space/src/host_mmap.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index df4694c90..27e25a668 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -81,6 +81,10 @@ impl FileBackend { let raw_fd = unsafe { libc::mkstemp(fs_cstr) }; if raw_fd < 0 { + // SAFETY: fs_cstr obtained by calling CString::into_raw. + unsafe { + drop(std::ffi::CString::from_raw(fs_cstr)); + } return Err(std::io::Error::last_os_error()).with_context(|| { format!("Failed to create file in directory: {} ", file_path) }); @@ -93,9 +97,13 @@ impl FileBackend { std::io::Error::last_os_error() ); } + // SAFETY: fs_cstr obtained by calling CString::into_raw. + unsafe { + drop(std::ffi::CString::from_raw(fs_cstr)); + } unsafe { File::from_raw_fd(raw_fd) } } else { - let existed = !path.exists(); + let not_existed = !path.exists(); // Open the file, if not exist, create it. let file_ret = std::fs::OpenOptions::new() .read(true) @@ -104,15 +112,20 @@ impl FileBackend { .open(path) .with_context(|| format!("Failed to open file: {}", file_path))?; - if existed - && unsafe { libc::unlink(std::ffi::CString::new(file_path).unwrap().into_raw()) } - != 0 - { - error!( - "Failed to unlink file \"{}\", error: {}", - file_path, - std::io::Error::last_os_error() - ); + if not_existed { + let fs_cstr = std::ffi::CString::new(file_path).unwrap().into_raw(); + + if unsafe { libc::unlink(fs_cstr) } != 0 { + error!( + "Failed to unlink file \"{}\", error: {}", + file_path, + std::io::Error::last_os_error() + ); + } + // SAFETY: fs_cstr obtained by calling CString::into_raw. + unsafe { + drop(std::ffi::CString::from_raw(fs_cstr)); + } } file_ret -- Gitee From 590a07baa2db82a73e1bb69dd53540c7a5134b3b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 04:21:04 +0800 Subject: [PATCH 0914/2187] Error: Replace ok_or_else() with with_context() Keep error generation style same. And anyhow! is not required when using with_context. Signed-off-by: Keqian Zhu --- acpi/src/table_loader.rs | 6 +-- address_space/src/address_space.rs | 22 +++++----- address_space/src/listener.rs | 14 +++---- address_space/src/region.rs | 4 +- address_space/src/state.rs | 5 ++- boot_loader/src/aarch64/mod.rs | 14 +++---- boot_loader/src/x86_64/direct_boot/mod.rs | 7 ++-- boot_loader/src/x86_64/standard_boot/elf.rs | 16 ++++---- boot_loader/src/x86_64/standard_boot/mod.rs | 6 +-- cpu/src/aarch64/mod.rs | 6 +-- cpu/src/lib.rs | 4 +- cpu/src/x86_64/mod.rs | 4 +- devices/src/acpi/ged.rs | 4 +- .../src/interrupt_controller/aarch64/state.rs | 14 ++++--- devices/src/legacy/fwcfg.rs | 4 +- devices/src/legacy/pflash.rs | 8 ++-- devices/src/legacy/pl011.rs | 11 ++--- devices/src/legacy/pl031.rs | 10 ++--- devices/src/legacy/serial.rs | 8 ++-- devices/src/usb/xhci/xhci_controller.rs | 9 +---- machine/src/lib.rs | 18 ++++----- machine/src/micro_vm/mod.rs | 30 +++++++------- machine/src/standard_vm/aarch64/mod.rs | 40 +++++++++---------- machine/src/standard_vm/x86_64/mod.rs | 31 +++++++------- machine/src/vm_state.rs | 10 +++-- machine_manager/src/config/network.rs | 8 ++-- machine_manager/src/config/scream.rs | 8 ++-- migration/src/general.rs | 2 +- ozone/src/capability.rs | 14 ++++--- pci/src/config.rs | 4 +- pci/src/msix.rs | 9 +++-- pci/src/root_port.rs | 2 +- util/src/aio/mod.rs | 10 ++--- util/src/device_tree.rs | 10 ++--- vfio/src/vfio_pci.rs | 10 ++--- vhost_user_fs/src/virtio_fs.rs | 4 +- virtio/src/device/balloon.rs | 24 +++-------- virtio/src/device/block.rs | 13 +++--- virtio/src/device/console.rs | 2 +- virtio/src/device/net.rs | 25 ++++-------- virtio/src/device/rng.rs | 13 +++--- virtio/src/lib.rs | 32 +++++++-------- virtio/src/queue/split.rs | 29 +++++--------- virtio/src/transport/virtio_mmio.rs | 10 ++--- virtio/src/transport/virtio_pci.rs | 13 +++--- virtio/src/vhost/kernel/mod.rs | 16 ++++---- virtio/src/vhost/kernel/net.rs | 2 +- virtio/src/vhost/kernel/vsock.rs | 7 ++-- virtio/src/vhost/user/block.rs | 6 +-- virtio/src/vhost/user/client.rs | 39 +++++++++--------- 50 files changed, 289 insertions(+), 328 deletions(-) diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 21bf56f63..eb5f5e18e 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -268,7 +268,7 @@ impl TableLoader { let file = file.to_string(); let file_entry = self .find_matched_file(&file) - .with_context(|| anyhow!(AcpiError::NoMatchedFile(file.clone())))?; + .with_context(|| AcpiError::NoMatchedFile(file.clone()))?; let file_entry_len = file_entry.file_blob.lock().unwrap().len(); @@ -327,10 +327,10 @@ impl TableLoader { let src_file = src_file.to_string(); let dst_file_entry = self .find_matched_file(&dst_file) - .with_context(|| anyhow!(AcpiError::NoMatchedFile(dst_file.clone())))?; + .with_context(|| AcpiError::NoMatchedFile(dst_file.clone()))?; let src_file_entry = self .find_matched_file(&src_file) - .with_context(|| anyhow!(AcpiError::NoMatchedFile(src_file.clone())))?; + .with_context(|| AcpiError::NoMatchedFile(src_file.clone()))?; let dst_file_len = dst_file_entry.file_blob.lock().unwrap().len(); let src_file_len = src_file_entry.file_blob.lock().unwrap().len(); diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index adbb4be5d..7a604bc38 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use arc_swap::ArcSwap; use std::fmt; use std::fmt::Debug; @@ -234,11 +234,11 @@ impl AddressSpace { if !is_add { self.call_listeners(Some(old_r), None, ListenerReqType::DeleteRegion) .with_context(|| { - anyhow!(AddressSpaceError::UpdateTopology( + AddressSpaceError::UpdateTopology( old_r.addr_range.base.raw_value(), old_r.addr_range.size, old_r.owner.region_type(), - )) + ) })?; } old_idx += 1; @@ -250,11 +250,11 @@ impl AddressSpace { if is_add && new_range.is_some() { self.call_listeners(new_range, None, ListenerReqType::AddRegion) .with_context(|| { - anyhow!(AddressSpaceError::UpdateTopology( + AddressSpaceError::UpdateTopology( new_range.unwrap().addr_range.base.raw_value(), new_range.unwrap().addr_range.size, new_range.unwrap().owner.region_type(), - )) + ) })?; } new_idx += 1; @@ -286,11 +286,11 @@ impl AddressSpace { if old_fd.is_some() && (new_fd.is_none() || !old_fd.unwrap().after(new_fd.unwrap())) { self.call_listeners(None, old_fd, ListenerReqType::DeleteIoeventfd) .with_context(|| { - anyhow!(AddressSpaceError::UpdateTopology( + AddressSpaceError::UpdateTopology( old_fd.unwrap().addr_range.base.raw_value(), old_fd.unwrap().addr_range.size, RegionType::IO, - )) + ) })?; old_idx += 1; } @@ -298,11 +298,11 @@ impl AddressSpace { if new_fd.is_some() && (old_fd.is_none() || !new_fd.unwrap().after(old_fd.unwrap())) { self.call_listeners(None, new_fd, ListenerReqType::AddIoeventfd) .with_context(|| { - anyhow!(AddressSpaceError::UpdateTopology( + AddressSpaceError::UpdateTopology( new_fd.unwrap().addr_range.base.raw_value(), new_fd.unwrap().addr_range.size, RegionType::IO, - )) + ) })?; new_idx += 1; } @@ -438,7 +438,7 @@ impl AddressSpace { let (fr, offset) = view .find_flatrange(addr) .map(|fr| (fr, addr.offset_from(fr.addr_range.base))) - .with_context(|| anyhow!(AddressSpaceError::RegionNotFound(addr.raw_value())))?; + .with_context(|| AddressSpaceError::RegionNotFound(addr.raw_value()))?; let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let offset_in_region = fr.offset_in_region + offset; @@ -470,7 +470,7 @@ impl AddressSpace { let (fr, offset) = view .find_flatrange(addr) .map(|fr| (fr, addr.offset_from(fr.addr_range.base))) - .with_context(|| anyhow!(AddressSpaceError::RegionNotFound(addr.raw_value())))?; + .with_context(|| AddressSpaceError::RegionNotFound(addr.raw_value()))?; let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let offset_in_region = fr.offset_in_region + offset; diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index fccade1ed..aa5a755c2 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -183,12 +183,10 @@ impl KvmMemoryListener { /// /// Return Error if Memslot size is zero after aligned. fn align_mem_slot(range: AddressRange, alignment: u64) -> Result { - let aligned_addr = range.base.align_up(alignment).with_context(|| { - anyhow!(AddressSpaceError::AddrAlignUp( - range.base.raw_value(), - alignment - )) - })?; + let aligned_addr = range + .base + .align_up(alignment) + .with_context(|| AddressSpaceError::AddrAlignUp(range.base.raw_value(), alignment))?; let aligned_size = range .size @@ -473,7 +471,7 @@ impl Listener for KvmMemoryListener { ), }; - req_ret.with_context(|| anyhow!(AddressSpaceError::ListenerRequest(req_type))) + req_ret.with_context(|| AddressSpaceError::ListenerRequest(req_type)) } } @@ -611,7 +609,7 @@ impl Listener for KvmIoListener { _ => return Ok(()), }; - handle_ret.with_context(|| anyhow!(AddressSpaceError::ListenerRequest(req_type))) + handle_ret.with_context(|| AddressSpaceError::ListenerRequest(req_type)) } } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index a7590d114..ace087721 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -505,7 +505,7 @@ impl Region { match self.region_type { RegionType::Ram | RegionType::RamDevice => { self.check_valid_offset(offset, count).with_context(|| { - anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + AddressSpaceError::InvalidOffset(offset, count, self.size()) })?; let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); let slice = unsafe { @@ -516,7 +516,7 @@ impl Region { } RegionType::RomDevice => { self.check_valid_offset(offset, count).with_context(|| { - anyhow!(AddressSpaceError::InvalidOffset(offset, count, self.size())) + AddressSpaceError::InvalidOffset(offset, count, self.size()) })?; if self.rom_dev_romd.as_ref().load(Ordering::SeqCst) { let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 513ce82d9..f693eba1a 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -15,7 +15,8 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::sync::Arc; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; + use migration::{ error::MigrationError, DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, }; @@ -101,7 +102,7 @@ impl MigrationHook for AddressSpace { fn restore_memory(&self, memory: Option<&File>, state: &[u8]) -> Result<()> { let address_space_state: &AddressSpaceState = AddressSpaceState::from_bytes(&state[0..size_of::()]) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("MEMORY")))?; + .with_context(|| MigrationError::FromBytesError("MEMORY"))?; let memfile_arc = Arc::new(memory.unwrap().try_clone().unwrap()); for ram_state in address_space_state.ram_region_state diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index 8d6731217..a2a6297ca 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -54,7 +54,7 @@ fn load_kernel( sys_mem: &Arc, ) -> Result { let mut kernel_image = - File::open(kernel_path).with_context(|| anyhow!(BootLoaderError::BootLoaderOpenKernel))?; + File::open(kernel_path).with_context(|| BootLoaderError::BootLoaderOpenKernel)?; let kernel_size = kernel_image.metadata().unwrap().len(); let kernel_end = kernel_start + kernel_size; @@ -67,10 +67,10 @@ fn load_kernel( FwCfgEntryType::KernelSize, (kernel_size as u32).as_bytes().to_vec(), ) - .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("KernelSize".to_string())))?; + .with_context(|| FwcfgErrorKind::AddEntryErr("KernelSize".to_string()))?; lock_dev .add_data_entry(FwCfgEntryType::KernelData, kernel_data) - .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("KernelData".to_string())))?; + .with_context(|| FwcfgErrorKind::AddEntryErr("KernelData".to_string()))?; } else { if sys_mem .memory_end_address() @@ -97,7 +97,7 @@ fn load_initrd( kernel_end: u64, ) -> Result<(u64, u64)> { let mut initrd_image = - File::open(initrd_path).with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; + File::open(initrd_path).with_context(|| BootLoaderError::BootLoaderOpenInitrd)?; let initrd_size = initrd_image.metadata().unwrap().len(); let initrd_start = if let Some(addr) = sys_mem @@ -123,16 +123,16 @@ fn load_initrd( FwCfgEntryType::InitrdAddr, (initrd_start as u32).as_bytes().to_vec(), ) - .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("InitrdAddr".to_string())))?; + .with_context(|| FwcfgErrorKind::AddEntryErr("InitrdAddr".to_string()))?; lock_dev .add_data_entry( FwCfgEntryType::InitrdSize, (initrd_size as u32).as_bytes().to_vec(), ) - .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("InitrdSize".to_string())))?; + .with_context(|| FwcfgErrorKind::AddEntryErr("InitrdSize".to_string()))?; lock_dev .add_data_entry(FwCfgEntryType::InitrdData, initrd_data) - .with_context(|| anyhow!(FwcfgErrorKind::AddEntryErr("InitrdData".to_string())))?; + .with_context(|| FwcfgErrorKind::AddEntryErr("InitrdData".to_string()))?; } else { sys_mem .write(&mut initrd_image, GuestAddress(initrd_start), initrd_size) diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 09b4177e6..6182a040c 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -30,7 +30,8 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::error::BootLoaderError; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; + /// Load bzImage linux kernel to Guest Memory. /// /// # Notes @@ -101,7 +102,7 @@ fn load_kernel_image( boot_layout: &mut X86BootLoader, ) -> Result { let mut kernel_image = - File::open(kernel_path).with_context(|| anyhow!(BootLoaderError::BootLoaderOpenKernel))?; + File::open(kernel_path).with_context(|| BootLoaderError::BootLoaderOpenKernel)?; let (boot_hdr, kernel_start, vmlinux_start) = if let Ok(hdr) = load_bzimage(&mut kernel_image) { ( @@ -141,7 +142,7 @@ fn load_initrd( }; let mut initrd_image = File::open(config.initrd.as_ref().unwrap()) - .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; + .with_context(|| BootLoaderError::BootLoaderOpenInitrd)?; let initrd_size = initrd_image.metadata().unwrap().len(); let initrd_addr = (initrd_addr_max - initrd_size) & !0xfff_u64; diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 8d2e942f0..1fd6e9860 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -14,13 +14,13 @@ use std::fs::File; use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; +use anyhow::{bail, Context, Result}; + use address_space::{AddressSpace, GuestAddress}; use devices::legacy::{FwCfgEntryType, FwCfgOps}; use util::byte_code::ByteCode; use util::num_ops::round_up; -use anyhow::{anyhow, bail, Context, Result}; - const EI_MAG0: usize = 0; const EI_MAG3: usize = 3; const EI_CLASS: usize = 4; @@ -181,11 +181,11 @@ pub fn load_elf_kernel( let p_align = ph.p_align; let aligned_namesz = - round_up(note_hdr.namesz as u64, p_align).ok_or_else(|| { - anyhow!(format!( + round_up(note_hdr.namesz as u64, p_align).with_context(|| { + format!( "Overflows when align up: num 0x{:x}, alignment 0x{:x}", note_hdr.namesz as u64, p_align, - )) + ) })?; if note_hdr.type_ == XEN_ELFNOTE_PHYS32_ENTRY { kernel_image.seek(SeekFrom::Current(aligned_namesz as i64))?; @@ -196,11 +196,11 @@ pub fn load_elf_kernel( break; } else { let aligned_descsz = - round_up(note_hdr.descsz as u64, p_align).ok_or_else(|| { - anyhow!(format!( + round_up(note_hdr.descsz as u64, p_align).with_context(|| { + format!( "Overflows when align up, num 0x{:x}, alignment 0x{:x}", note_hdr.descsz as u64, p_align, - )) + ) })?; let tail_size = aligned_namesz + aligned_descsz; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 5cb13e67a..659362ad9 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -29,7 +29,7 @@ use super::{BOOT_HDR_START, CMDLINE_START}; use crate::error::BootLoaderError; use crate::x86_64::bootparam::{E820Entry, E820_RAM, E820_RESERVED, UEFI_OVMF_ID}; use crate::x86_64::{INITRD_ADDR_MAX, SETUP_START}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; fn load_image( image: &mut File, @@ -103,7 +103,7 @@ fn load_initrd( }; let mut initrd_image = File::open(config.initrd.as_ref().unwrap()) - .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenInitrd))?; + .with_context(|| BootLoaderError::BootLoaderOpenInitrd)?; let initrd_size = initrd_image.metadata().unwrap().len(); let initrd_addr = (initrd_addr_max - initrd_size) & !0xfff_u64; @@ -208,7 +208,7 @@ pub fn load_linux( } let mut kernel_image = File::open(config.kernel.as_ref().unwrap().clone()) - .with_context(|| anyhow!(BootLoaderError::BootLoaderOpenKernel))?; + .with_context(|| BootLoaderError::BootLoaderOpenKernel)?; let mut boot_header = RealModeKernelHeader::default(); kernel_image.seek(SeekFrom::Start(BOOT_HDR_START))?; diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 92e4d3a46..4dbdd2750 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -19,6 +19,7 @@ use std::{ sync::{Arc, Mutex}, }; +use anyhow::{Context, Result}; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{ kvm_device_attr, kvm_mp_state, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, RegList, @@ -31,7 +32,6 @@ use self::caps::CpregListEntry; pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; use self::core_regs::{get_core_regs, set_core_regs}; use crate::CPU; -use anyhow::{anyhow, Context, Result}; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, @@ -329,7 +329,7 @@ impl StateTransfer for CPU { fn set_state(&self, state: &[u8]) -> migration::Result<()> { let cpu_state = *ArmCPUState::from_bytes(state) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("CPU")))?; + .with_context(|| MigrationError::FromBytesError("CPU"))?; let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); *cpu_state_locked = cpu_state; @@ -338,7 +338,7 @@ impl StateTransfer for CPU { if cpu_state.features.pmu { self.init_pmu() - .map_err(|_| migration::MigrationError::FromBytesError("failed to init pmu."))?; + .map_err(|_| MigrationError::FromBytesError("failed to init pmu."))?; } Ok(()) } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 4934962e5..f11368dde 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -354,7 +354,7 @@ impl CPUInterface for CPU { match task.as_ref() { Some(thread) => thread .kill(VCPU_RESET_SIGNAL) - .with_context(|| anyhow!(CpuError::KickVcpu("Fail to reset vcpu".to_string()))), + .with_context(|| CpuError::KickVcpu("Fail to reset vcpu".to_string())), None => { warn!("VCPU thread not started, no need to reset"); Ok(()) @@ -367,7 +367,7 @@ impl CPUInterface for CPU { match task.as_ref() { Some(thread) => thread .kill(VCPU_TASK_SIGNAL) - .with_context(|| anyhow!(CpuError::KickVcpu("Fail to kick vcpu".to_string()))), + .with_context(|| CpuError::KickVcpu("Fail to kick vcpu".to_string())), None => { warn!("VCPU thread not started, no need to kick"); Ok(()) diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 35f75867a..4f3dbbaee 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -15,7 +15,7 @@ mod cpuid; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use kvm_bindings::{ kvm_cpuid_entry2, kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, CpuId, Msrs, @@ -592,7 +592,7 @@ impl StateTransfer for CPU { fn set_state(&self, state: &[u8]) -> migration::Result<()> { let cpu_state = *X86CPUState::from_bytes(state) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("CPU")))?; + .with_context(|| MigrationError::FromBytesError("CPU"))?; let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); *cpu_state_locked = cpu_state; diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 2d8fa091e..ae37409de 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -12,7 +12,7 @@ use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; use address_space::GuestAddress; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use log::error; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -73,7 +73,7 @@ impl Ged { ) -> Result<()> { self.interrupt_evt = Arc::new(Some(EventFd::new(libc::EFD_NONBLOCK)?)); self.set_sys_resource(sysbus, region_base, region_size) - .with_context(|| anyhow!(AcpiError::Alignment(region_size.try_into().unwrap())))?; + .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 546e1dd1f..1d5a00289 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -12,15 +12,17 @@ use std::mem::size_of; -use super::gicv3::{GICv3, GICv3Access, GICv3Its}; -use super::GIC_IRQ_INTERNAL; -use crate::interrupt_controller::Result; - -use anyhow::anyhow; +use anyhow::{anyhow, Context}; use libc::c_uint; + use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; + +use super::gicv3::{GICv3, GICv3Access, GICv3Its}; +use super::GIC_IRQ_INTERNAL; +use crate::interrupt_controller::Result; + /// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. const REGISTER_SIZE: u64 = size_of::() as u64; @@ -736,7 +738,7 @@ impl StateTransfer for GICv3Its { use migration::MigrationError; let mut its_state = *GICv3ItsState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("GICv3Its")))?; + .with_context(|| MigrationError::FromBytesError("GICv3Its"))?; self.access_gic_its(GITS_IIDR, &mut its_state.iidr, true) .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its iidr", e.to_string())))?; diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 0b6e650cd..265b2f062 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -509,7 +509,7 @@ impl FwCfgCommon { .files .iter() .position(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) - .ok_or_else(|| anyhow!(LegacyError::EntryNotFound(filename.to_owned())))?; + .with_context(|| LegacyError::EntryNotFound(filename.to_owned()))?; self.files[index].size = data.len() as u32; // Update FileDir entry @@ -700,7 +700,7 @@ impl FwCfgCommon { ((8 - addr - size as u64) * 8) as u32, size * 8, ) - .ok_or_else(|| anyhow!("Failed to extract bits from u64")) + .with_context(|| "Failed to extract bits from u64") } /// Read data register diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 8833d1644..1b52d2b95 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -264,7 +264,7 @@ impl PFlash { let mut i: u32 = self.device_width; while i < self.bank_width { resp = deposit_u32(resp, 8 * i, 8 * self.device_width, resp) - .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; + .with_context(|| "Failed to deposit bits to u32")?; i += self.device_width; } } @@ -296,7 +296,7 @@ impl PFlash { // Repeat data for PFlash device which supports x16-mode but works in x8-mode. for i in 1..self.max_device_width { resp = deposit_u32(resp, 8 * i, 8, self.cfi_table[index as usize] as u32) - .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; + .with_context(|| "Failed to deposit bits to u32")?; } } // Responses are repeated for every device in bank. @@ -304,7 +304,7 @@ impl PFlash { let mut i: u32 = self.device_width; while i < self.bank_width { resp = deposit_u32(resp, 8 * i, 8 * self.device_width, resp) - .ok_or_else(|| anyhow!("Failed to deposit bits to u32"))?; + .with_context(|| "Failed to deposit bits to u32")?; i += self.device_width; } } @@ -328,7 +328,7 @@ impl PFlash { let addr: u64 = mr .get_host_address() - .ok_or_else(|| anyhow!("Failed to get host address."))?; + .with_context(|| "Failed to get host address.")?; let ret = unsafe { // Safe as addr and size are valid. libc::msync( diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 23617d1fb..c46352e04 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -20,15 +20,15 @@ use acpi::{ AmlScopeBuilder, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::GuestAddress; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use log::{debug, error}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, }; use migration::{ - snapshot::PL011_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, - StateTransfer, + snapshot::PL011_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, + MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; @@ -36,6 +36,7 @@ use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_data_u32; use vmm_sys_util::eventfd::EventFd; + const PL011_FLAG_TXFE: u8 = 0x80; const PL011_FLAG_RXFF: u8 = 0x40; const PL011_FLAG_RXFE: u8 = 0x10; @@ -187,7 +188,7 @@ impl PL011 { EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, ) - .with_context(|| anyhow!(LegacyError::RegNotifierErr))?; + .with_context(|| LegacyError::RegNotifierErr)?; Ok(()) } } @@ -403,7 +404,7 @@ impl StateTransfer for PL011 { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *PL011State::from_bytes(state) - .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("PL011")))?; + .with_context(|| MigrationError::FromBytesError("PL011"))?; Ok(()) } diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 563c40c35..1e87e53a6 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -16,12 +16,12 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use super::error::LegacyError; use acpi::AmlBuilder; use address_space::GuestAddress; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::error; use migration::{ - snapshot::PL031_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, - StateTransfer, + snapshot::PL031_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, + MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; @@ -105,7 +105,7 @@ impl PL031 { ) -> Result<()> { self.interrupt_evt = Some(EventFd::new(libc::EFD_NONBLOCK)?); self.set_sys_resource(sysbus, region_base, region_size) - .with_context(|| anyhow!(LegacyError::SetSysResErr))?; + .with_context(|| LegacyError::SetSysResErr)?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; @@ -218,7 +218,7 @@ impl StateTransfer for PL031 { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *PL031State::from_bytes(state) - .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("PL031")))?; + .with_context(|| MigrationError::FromBytesError("PL031"))?; Ok(()) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 3ca5951c3..b3ba910bd 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -36,7 +36,7 @@ use vmm_sys_util::eventfd::EventFd; use super::chardev::{Chardev, InputReceiver}; use super::error::LegacyError; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; pub const SERIAL_ADDR: u64 = 0x3f8; const UART_IER_RDI: u8 = 0x01; @@ -149,7 +149,7 @@ impl Serial { .with_context(|| "Failed to realize chardev")?; self.interrupt_evt = Some(EventFd::new(libc::EFD_NONBLOCK)?); self.set_sys_resource(sysbus, region_base, region_size) - .with_context(|| anyhow!(LegacyError::SetSysResErr))?; + .with_context(|| LegacyError::SetSysResErr)?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; @@ -170,7 +170,7 @@ impl Serial { EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, ) - .with_context(|| anyhow!(LegacyError::RegNotifierErr))?; + .with_context(|| LegacyError::RegNotifierErr)?; Ok(()) } @@ -438,7 +438,7 @@ impl StateTransfer for Serial { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { let serial_state = *SerialState::from_bytes(state) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("SERIAL")))?; + .with_context(|| MigrationError::FromBytesError("SERIAL"))?; let mut rbr = VecDeque::::default(); for i in 0..serial_state.rbr_len { rbr.push_back(serial_state.rbr_value[i]); diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 30a1b5be0..7ebc59480 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -17,7 +17,7 @@ use std::slice::from_raw_parts_mut; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; @@ -964,12 +964,7 @@ impl XhciDevice { self.oper .dcbaap .checked_add((8 * slot_id) as u64) - .ok_or_else(|| { - anyhow!(UsbError::MemoryAccessOverflow( - self.oper.dcbaap, - (8 * slot_id) as u64 - )) - }) + .with_context(|| UsbError::MemoryAccessOverflow(self.oper.dcbaap, (8 * slot_id) as u64)) } fn configure_endpoint(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 69c4c92e9..56c5ff59b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -149,7 +149,7 @@ pub trait MachineOps { sys_mem .root() .add_subregion(Region::init_ram_region(mmap.clone()), base) - .with_context(|| anyhow!(MachineError::RegMemRegionErr(base, size)))?; + .with_context(|| MachineError::RegMemRegionErr(base, size))?; } } @@ -266,7 +266,7 @@ pub trait MachineOps { MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) - .with_context(|| anyhow!(MachineError::RlzVirtioMmioErr))?, + .with_context(|| MachineError::RlzVirtioMmioErr)?, &device_cfg.id, ); } else { @@ -365,7 +365,7 @@ pub trait MachineOps { MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) - .with_context(|| anyhow!(MachineError::RlzVirtioMmioErr))?, + .with_context(|| MachineError::RlzVirtioMmioErr)?, &device_cfg.id, ); } else { @@ -1093,8 +1093,8 @@ pub trait MachineOps { .object .mem_object .remove(&dev_cfg.memdev) - .ok_or_else(|| { - anyhow!( + .with_context(|| { + format!( "Object for memory-backend-ram {} config not found", dev_cfg.memdev ) @@ -1214,21 +1214,21 @@ pub trait MachineOps { #[cfg(target_arch = "x86_64")] vm_config.machine_config.mem_config.mem_size, ) - .with_context(|| anyhow!(MachineError::AddDevErr("RTC".to_string())))?; + .with_context(|| MachineError::AddDevErr("RTC".to_string()))?; #[cfg(target_arch = "aarch64")] self.add_ged_device() - .with_context(|| anyhow!(MachineError::AddDevErr("Ged".to_string())))?; + .with_context(|| MachineError::AddDevErr("Ged".to_string()))?; let cloned_vm_config = vm_config.clone(); if let Some(serial) = cloned_vm_config.serial.as_ref() { self.add_serial_device(serial) - .with_context(|| anyhow!(MachineError::AddDevErr("serial".to_string())))?; + .with_context(|| MachineError::AddDevErr("serial".to_string()))?; } if let Some(pflashs) = cloned_vm_config.pflashs.as_ref() { self.add_pflash_device(pflashs) - .with_context(|| anyhow!(MachineError::AddDevErr("pflash".to_string())))?; + .with_context(|| MachineError::AddDevErr("pflash".to_string()))?; } for dev in &cloned_vm_config.devices { diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index b5178fe90..285f6052e 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -196,10 +196,10 @@ impl LightMachine { /// * `vm_config` - Represents the configuration for VM. pub fn new(vm_config: &VmConfig) -> MachineResult { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .with_context(|| anyhow!(MachineError::CrtMemSpaceErr))?; + .with_context(|| MachineError::CrtMemSpaceErr)?; #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) - .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; + .with_context(|| MachineError::CrtIoSpaceErr)?; let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, @@ -249,7 +249,7 @@ impl LightMachine { let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); vm_fd .set_tss_address(0xfffb_d000_usize) - .with_context(|| anyhow!(MachineError::SetTssErr))?; + .with_context(|| MachineError::SetTssErr)?; let pit_config = kvm_pit_config { flags: KVM_PIT_SPEAKER_DUMMY, @@ -257,7 +257,7 @@ impl LightMachine { }; vm_fd .create_pit2(pit_config) - .with_context(|| anyhow!(MachineError::CrtPitErr))?; + .with_context(|| MachineError::CrtPitErr)?; Ok(()) } @@ -313,7 +313,7 @@ impl LightMachine { #[cfg(target_arch = "x86_64")] &self.boot_source, ) - .with_context(|| anyhow!(MicroVmError::RlzVirtioMmioErr))?, + .with_context(|| MicroVmError::RlzVirtioMmioErr)?, &id.to_string(), ); region_base += region_size; @@ -341,7 +341,7 @@ impl LightMachine { .lock() .unwrap() .update_config(Some(dev_config.clone())) - .with_context(|| anyhow!(MicroVmError::UpdCfgErr(id.to_string())))?; + .with_context(|| MicroVmError::UpdCfgErr(id.to_string()))?; } self.add_replaceable_config(id, dev_config)?; @@ -426,7 +426,7 @@ impl LightMachine { .lock() .unwrap() .update_config(dev_config) - .with_context(|| anyhow!(MicroVmError::UpdCfgErr(id.to_string())))?; + .with_context(|| MicroVmError::UpdCfgErr(id.to_string()))?; } Ok(()) } @@ -457,7 +457,7 @@ impl LightMachine { .lock() .unwrap() .update_config(None) - .with_context(|| anyhow!(MicroVmError::UpdCfgErr(id.to_string())))?; + .with_context(|| MicroVmError::UpdCfgErr(id.to_string()))?; } } @@ -511,7 +511,7 @@ impl MachineOps for LightMachine { .as_ref() .unwrap() .create_irq_chip() - .with_context(|| anyhow!(MachineError::CrtIrqchipErr))?; + .with_context(|| MachineError::CrtIrqchipErr)?; Ok(()) } @@ -570,7 +570,7 @@ impl MachineOps for LightMachine { prot64_mode: true, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .with_context(|| anyhow!(MachineError::LoadKernErr))?; + .with_context(|| MachineError::LoadKernErr)?; Ok(CPUBootConfig { prot64_mode: true, @@ -602,7 +602,7 @@ impl MachineOps for LightMachine { mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .with_context(|| anyhow!(MachineError::LoadKernErr))?; + .with_context(|| MachineError::LoadKernErr)?; if let Some(rd) = &mut boot_source.initrd { rd.initrd_addr = layout.initrd_start; rd.initrd_size = layout.initrd_size; @@ -628,7 +628,7 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "x86_64")] &self.boot_source, ) - .with_context(|| anyhow!(MicroVmError::RlzVirtioMmioErr))?; + .with_context(|| MicroVmError::RlzVirtioMmioErr)?; self.sysbus.min_free_base += region_size; Ok(realized_virtio_mmio_device) } @@ -838,7 +838,7 @@ impl MachineOps for LightMachine { let mut fdt_helper = FdtBuilder::new(); locked_vm .generate_fdt_node(&mut fdt_helper) - .with_context(|| anyhow!(MachineError::GenFdtErr))?; + .with_context(|| MachineError::GenFdtErr)?; let fdt_vec = fdt_helper.finish()?; locked_vm .sys_mem @@ -847,9 +847,7 @@ impl MachineOps for LightMachine { GuestAddress(boot_cfg.fdt_addr as u64), fdt_vec.len() as u64, ) - .with_context(|| { - anyhow!(MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len())) - })?; + .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 0ea701a4d..d0f3038b0 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -74,7 +74,7 @@ use util::set_termi_canon_mode; use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; use crate::MachineOps; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { @@ -163,7 +163,7 @@ impl StdMachine { vm_config.machine_config.max_cpus, ); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; + .with_context(|| MachineError::CrtIoSpaceErr)?; let sysbus = SysBus::new( &sys_mem, (IRQ_BASE, IRQ_MAX), @@ -189,14 +189,14 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), - power_button: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { - anyhow!(MachineError::InitEventFdErr("power_button".to_string())) - })?), + power_button: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("power_button".to_string()))?, + ), vm_config: Arc::new(Mutex::new(vm_config.clone())), reset_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK).with_context(|| { - anyhow!(MachineError::InitEventFdErr("reset_req".to_string())) - })?, + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("reset_req".to_string()))?, ), dtb_vec: Vec::new(), numa_nodes: None, @@ -336,7 +336,7 @@ impl StdMachineOps for StdMachine { let mut fwcfg = FwCfgMem::new(self.sys_mem.clone()); fwcfg .add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec()) - .with_context(|| anyhow!(DevErrorKind::AddEntryErr("NbCpus".to_string())))?; + .with_context(|| DevErrorKind::AddEntryErr("NbCpus".to_string()))?; let cmdline = self.boot_source.lock().unwrap().kernel_cmdline.to_string(); fwcfg @@ -344,20 +344,20 @@ impl StdMachineOps for StdMachine { FwCfgEntryType::CmdlineSize, (cmdline.len() + 1).as_bytes().to_vec(), ) - .with_context(|| anyhow!(DevErrorKind::AddEntryErr("CmdlineSize".to_string())))?; + .with_context(|| DevErrorKind::AddEntryErr("CmdlineSize".to_string()))?; fwcfg .add_string_entry(FwCfgEntryType::CmdlineData, cmdline.as_str()) - .with_context(|| anyhow!(DevErrorKind::AddEntryErr("CmdlineData".to_string())))?; + .with_context(|| DevErrorKind::AddEntryErr("CmdlineData".to_string()))?; let boot_order = Vec::::new(); fwcfg .add_file_entry("bootorder", boot_order) - .with_context(|| anyhow!(DevErrorKind::AddEntryErr("bootorder".to_string())))?; + .with_context(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; let bios_geometry = Vec::::new(); fwcfg .add_file_entry("bios-geometry", bios_geometry) - .with_context(|| anyhow!(DevErrorKind::AddEntryErr("bios-geometry".to_string())))?; + .with_context(|| DevErrorKind::AddEntryErr("bios-geometry".to_string()))?; let fwcfg_dev = FwCfgMem::realize( fwcfg, @@ -429,7 +429,7 @@ impl MachineOps for StdMachine { mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .with_context(|| anyhow!(MachineError::LoadKernErr))?; + .with_context(|| MachineError::LoadKernErr)?; if let Some(rd) = &mut boot_source.initrd { rd.initrd_addr = layout.initrd_start; rd.initrd_size = layout.initrd_size; @@ -508,7 +508,7 @@ impl MachineOps for StdMachine { locked_vm .init_pci_host() - .with_context(|| anyhow!(StdErrorKind::InitPCIeHostErr))?; + .with_context(|| StdErrorKind::InitPCIeHostErr)?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; #[cfg(not(target_env = "musl"))] vnc::vnc_init(&vm_config.vnc, &vm_config.object) @@ -547,7 +547,7 @@ impl MachineOps for StdMachine { let mut fdt_helper = FdtBuilder::new(); locked_vm .generate_fdt_node(&mut fdt_helper) - .with_context(|| anyhow!(MachineError::GenFdtErr))?; + .with_context(|| MachineError::GenFdtErr)?; let fdt_vec = fdt_helper.finish()?; locked_vm.dtb_vec = fdt_vec.clone(); locked_vm @@ -557,9 +557,7 @@ impl MachineOps for StdMachine { GuestAddress(boot_cfg.fdt_addr as u64), fdt_vec.len() as u64, ) - .with_context(|| { - anyhow!(MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len())) - })?; + .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; } // If it is direct kernel boot mode, the ACPI can not be enabled. @@ -599,9 +597,9 @@ impl MachineOps for StdMachine { }; let pflash = PFlash::new(flash_size, &fd, sector_len, 4, 2, read_only) - .with_context(|| anyhow!(StdErrorKind::InitPflashErr))?; + .with_context(|| StdErrorKind::InitPflashErr)?; PFlash::realize(pflash, &mut self.sysbus, flash_base, flash_size, fd) - .with_context(|| anyhow!(StdErrorKind::RlzPflashErr))?; + .with_context(|| StdErrorKind::RlzPflashErr)?; flash_base += flash_size; } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 6cfd8ed1b..01227b489 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -61,7 +61,7 @@ use self::ich9_lpc::SLEEP_CTRL_OFFSET; use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; use crate::{vm_state, MachineOps}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; #[cfg(not(target_env = "musl"))] use ui::vnc; @@ -138,9 +138,9 @@ impl StdMachine { vm_config.machine_config.max_cpus, ); let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) - .with_context(|| anyhow!(MachineError::CrtMemSpaceErr))?; + .with_context(|| MachineError::CrtMemSpaceErr)?; let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .with_context(|| anyhow!(MachineError::CrtIoSpaceErr))?; + .with_context(|| MachineError::CrtIoSpaceErr)?; let sysbus = SysBus::new( &sys_io, &sys_mem, @@ -167,9 +167,10 @@ impl StdMachine { ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, - reset_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK).with_context(|| { - anyhow!(MachineError::InitEventFdErr("reset request".to_string())) - })?), + reset_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("reset request".to_string()))?, + ), vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -230,12 +231,12 @@ impl StdMachine { vm_fd .set_identity_map_address(identity_addr) - .with_context(|| anyhow!(MachineError::SetIdentityMapAddr))?; + .with_context(|| MachineError::SetIdentityMapAddr)?; // Page table takes 1 page, TSS takes the following 3 pages. vm_fd .set_tss_address((identity_addr + 0x1000) as usize) - .with_context(|| anyhow!(MachineError::SetTssErr))?; + .with_context(|| MachineError::SetTssErr)?; let pit_config = kvm_pit_config { flags: KVM_PIT_SPEAKER_DUMMY, @@ -243,7 +244,7 @@ impl StdMachine { }; vm_fd .create_pit2(pit_config) - .with_context(|| anyhow!(MachineError::CrtPitErr))?; + .with_context(|| MachineError::CrtPitErr)?; Ok(()) } @@ -303,7 +304,7 @@ impl StdMachineOps for StdMachine { let boot_order = Vec::::new(); fwcfg .add_file_entry("bootorder", boot_order) - .with_context(|| anyhow!(DevErrorKind::AddEntryErr("bootorder".to_string())))?; + .with_context(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) .with_context(|| "Failed to realize fwcfg device")?; @@ -345,7 +346,7 @@ impl MachineOps for StdMachine { .as_ref() .unwrap() .create_irq_chip() - .with_context(|| anyhow!(MachineError::CrtIrqchipErr))?; + .with_context(|| MachineError::CrtIrqchipErr)?; KVM_FDS .load() .irq_route_table @@ -375,7 +376,7 @@ impl MachineOps for StdMachine { prot64_mode: false, }; let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) - .with_context(|| anyhow!(MachineError::LoadKernErr))?; + .with_context(|| MachineError::LoadKernErr)?; Ok(CPUBootConfig { prot64_mode: false, @@ -434,7 +435,7 @@ impl MachineOps for StdMachine { locked_vm .init_pci_host() - .with_context(|| anyhow!(StandardVmError::InitPCIeHostErr))?; + .with_context(|| StandardVmError::InitPCIeHostErr)?; locked_vm .init_ich9_lpc(clone_vm) .with_context(|| "Fail to init LPC bridge")?; @@ -530,7 +531,7 @@ impl MachineOps for StdMachine { 1_u32, config.read_only, ) - .with_context(|| anyhow!(StandardVmError::InitPflashErr))?; + .with_context(|| StandardVmError::InitPflashErr)?; PFlash::realize( pflash, &mut self.sysbus, @@ -538,7 +539,7 @@ impl MachineOps for StdMachine { pfl_size, backend, ) - .with_context(|| anyhow!(StandardVmError::RlzPflashErr))?; + .with_context(|| StandardVmError::RlzPflashErr)?; flash_end -= pfl_size; } diff --git a/machine/src/vm_state.rs b/machine/src/vm_state.rs index 64b9e9719..493141064 100644 --- a/machine/src/vm_state.rs +++ b/machine/src/vm_state.rs @@ -10,12 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::Context; use kvm_bindings::{kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_IRQCHIP_IOAPIC}; -use migration_derive::{ByteCode, Desc}; -use anyhow::anyhow; use hypervisor::kvm::KVM_FDS; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration::{ + DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, +}; +use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; /// Structure to wrapper kvm_device related function. @@ -66,7 +68,7 @@ impl StateTransfer for KvmDevice { let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); let kvm_state = KvmDeviceState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::MigrationError::FromBytesError("KVM_DEVICE")))?; + .with_context(|| MigrationError::FromBytesError("KVM_DEVICE"))?; vm_fd.set_pit2(&kvm_state.pit_state)?; vm_fd.set_clock(&kvm_state.kvm_clock)?; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 5fdbe031f..a9ac5d97c 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -239,7 +239,7 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { let fds_num = fds.len() .checked_mul(2) - .ok_or_else(|| anyhow!("Invalid fds number {}", fds.len()))? as u16; + .with_context(|| format!("Invalid fds number {}", fds.len()))? as u16; if fds_num > net.queues { net.queues = fds_num; } @@ -264,7 +264,7 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { let fds_num = fds .len() .checked_mul(2) - .ok_or_else(|| anyhow!("Invalid vhostfds number {}", fds.len()))? + .with_context(|| format!("Invalid vhostfds number {}", fds.len()))? as u16; if fds_num > net.queues { net.queues = fds_num; @@ -347,7 +347,7 @@ fn get_netdev_fd(fd_name: &str) -> Result { // try to convert string to RawFd let fd_num = fd_name .parse::() - .with_context(|| anyhow!("Failed to parse fd: {}", fd_name))?; + .with_context(|| format!("Failed to parse fd: {}", fd_name))?; Ok(fd_num) } } @@ -373,7 +373,7 @@ pub fn get_netdev_config(args: Box) -> Result Result { dev_cfg.memdev = cmd_parser .get_value::("memdev")? - .ok_or_else(|| anyhow!("No memdev configured for scream device"))?; + .with_context(|| "No memdev configured for scream device")?; if let Some(interface) = cmd_parser.get_value::("interface")? { dev_cfg.interface = interface; @@ -66,10 +66,10 @@ pub fn parse_scream(cfg_args: &str) -> Result { if dev_cfg.interface.eq(&"Demo".to_string()) { dev_cfg.playback = cmd_parser .get_value::("playback")? - .ok_or_else(|| anyhow!("No playback configured for interface"))?; + .with_context(|| "No playback configured for interface")?; dev_cfg.record = cmd_parser .get_value::("record")? - .ok_or_else(|| anyhow!("No record configured for interface"))?; + .with_context(|| "No record configured for interface")?; } Ok(dev_cfg) diff --git a/migration/src/general.rs b/migration/src/general.rs index 4c5527678..d4ba732be 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -64,7 +64,7 @@ impl MigrationManager { fd.read_exact(&mut place_holder)?; Ok(*MigrationHeader::from_bytes(&header_bytes) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("HEADER")))?) + .with_context(|| MigrationError::FromBytesError("HEADER"))?) } /// Write all `DeviceStateDesc` in `desc_db` hashmap to `Write` trait object. diff --git a/ozone/src/capability.rs b/ozone/src/capability.rs index ca1c1ad6c..116225cd4 100644 --- a/ozone/src/capability.rs +++ b/ozone/src/capability.rs @@ -12,10 +12,12 @@ //! Remove all capability for ozone when uid is 0, use -capability cap_* to add capability. +use std::{collections::HashMap, io::Write}; + +use anyhow::{bail, Context, Result}; + use crate::syscall; use crate::OzoneError; -use anyhow::{anyhow, bail, Context, Result}; -use std::{collections::HashMap, io::Write}; const CAPS_V3: u32 = 0x20080522; const NR_ALL_CAP: u8 = 41; @@ -110,9 +112,9 @@ fn has_cap(cap: u8) -> Result { // so we set Bounding to limit child process. pub fn clear_all_capabilities() -> Result<()> { for cap in 0..NR_ALL_CAP { - if has_cap(cap).with_context(|| anyhow!(OzoneError::CapsError("CAPGET")))? { + if has_cap(cap).with_context(|| OzoneError::CapsError("CAPGET"))? { syscall::drop_bounding_caps(cap) - .with_context(|| anyhow!(OzoneError::CapsError("PR_CAPBSET_DROP")))?; + .with_context(|| OzoneError::CapsError("PR_CAPBSET_DROP"))?; } } @@ -145,9 +147,9 @@ pub fn set_capability_for_ozone(capability: &str) -> Result<()> { if cap_add_arr.contains(item.0) { continue; } - if has_cap(item.1 .0).with_context(|| anyhow!(OzoneError::CapsError("CAPGET")))? { + if has_cap(item.1 .0).with_context(|| OzoneError::CapsError("CAPGET"))? { syscall::drop_bounding_caps(item.1 .0) - .with_context(|| anyhow!(OzoneError::CapsError("PR_CAPBSET_DROP")))?; + .with_context(|| OzoneError::CapsError("PR_CAPBSET_DROP"))?; } } Ok(()) diff --git a/pci/src/config.rs b/pci/src/config.rs index a4fba98c6..f8e248f5a 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -845,7 +845,7 @@ impl PciConfig { .lock() .unwrap() .delete_subregion(self.bars[id].region.as_ref().unwrap()) - .with_context(|| anyhow!(PciError::UnregMemBar(id)))? + .with_context(|| PciError::UnregMemBar(id))? } } } @@ -876,7 +876,7 @@ impl PciConfig { mem_region .unwrap() .add_subregion(self.bars[id].region.clone().unwrap(), new_addr) - .with_context(|| anyhow!(PciError::UnregMemBar(id)))?; + .with_context(|| PciError::UnregMemBar(id))?; self.bars[id].parent_mem_region = Some(Arc::new(Mutex::new(mem_region.unwrap().clone()))); } diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 5b778aa36..bb40397e8 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -15,9 +15,12 @@ use std::collections::HashMap; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; +use anyhow::{bail, Context, Result}; +use log::{error, warn}; +use vmm_sys_util::eventfd::EventFd; + use address_space::{GuestAddress, Region, RegionOps}; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::{error, warn}; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; @@ -27,14 +30,12 @@ use util::{ num_ops::round_up, test_helper::{add_msix_msg, is_test_enabled}, }; -use vmm_sys_util::eventfd::EventFd; use crate::config::{CapId, PciConfig, RegionType, MINMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, ranges_overlap, PciBus, }; -use anyhow::{anyhow, bail, Context, Result}; pub const MSIX_TABLE_ENTRY_SIZE: u16 = 16; pub const MSIX_TABLE_SIZE_MAX: u16 = 0x7ff; @@ -503,7 +504,7 @@ impl StateTransfer for Msix { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { let msix_state = *MsixState::from_bytes(state) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("MSIX_DEVICE")))?; + .with_context(|| MigrationError::FromBytesError("MSIX_DEVICE"))?; let table_length = self.table.len(); let pba_length = self.pba.len(); diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index d330afddb..5731bbe83 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -590,7 +590,7 @@ impl StateTransfer for RootPort { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { let root_port_state = *RootPortState::from_bytes(state) - .ok_or_else(|| anyhow!(MigrationError::FromBytesError("ROOT_PORT")))?; + .with_context(|| MigrationError::FromBytesError("ROOT_PORT"))?; let length = self.config.config.len(); self.config.config = root_port_state.config_space[..length].to_vec(); diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index f14f077b1..a68839ab8 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -161,7 +161,7 @@ impl Aio { pub fn submit_request(&mut self, mut cb: AioCb) -> Result<()> { if self.request_misaligned(&cb) { let max_len = round_down(cb.nbytes + cb.req_align as u64 * 2, cb.req_align as u64) - .ok_or_else(|| anyhow!("Failed to round down request length."))?; + .with_context(|| "Failed to round down request length.")?; // Set upper limit of buffer length to avoid OOM. let buff_len = cmp::min(max_len, MAX_LEN_BOUNCE_BUFF); // SAFETY: we allocate aligned memory and free it later. Alignment is set to @@ -346,10 +346,10 @@ impl Aio { buffer_len: u64, ) -> Result<()> { let offset_align = round_down(cb.offset as u64, cb.req_align as u64) - .ok_or_else(|| anyhow!("Failed to round down request offset."))?; + .with_context(|| "Failed to round down request offset.")?; let high = cb.offset as u64 + cb.nbytes; let high_align = round_up(high, cb.req_align as u64) - .ok_or_else(|| anyhow!("Failed to round up request high edge."))?; + .with_context(|| "Failed to round up request high edge.")?; match cb.opcode { OpCode::Preadv => { @@ -394,7 +394,7 @@ impl Aio { break; } iovecs = iov_discard_front_direct(iovecs, real_nbytes) - .ok_or_else(|| anyhow!("Failed to adjust iovec for misaligned read"))?; + .with_context(|| "Failed to adjust iovec for misaligned read")?; } Ok(()) } @@ -469,7 +469,7 @@ impl Aio { break; } iovecs = iov_discard_front_direct(iovecs, real_nbytes) - .ok_or_else(|| anyhow!("Failed to adjust iovec for misaligned write"))?; + .with_context(|| "Failed to adjust iovec for misaligned write")?; } Ok(()) } diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 48f03c55d..a280136e0 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -225,17 +225,17 @@ impl FdtBuilder { // The string property should end with null('\0'). val_array.push(0x0_u8); self.set_property(prop, &val_array) - .with_context(|| anyhow!(UtilError::SetPropertyErr("string".to_string()))) + .with_context(|| UtilError::SetPropertyErr("string".to_string())) } pub fn set_property_u32(&mut self, prop: &str, val: u32) -> Result<()> { self.set_property(prop, &val.to_be_bytes()[..]) - .with_context(|| anyhow!(UtilError::SetPropertyErr("u32".to_string()))) + .with_context(|| UtilError::SetPropertyErr("u32".to_string())) } pub fn set_property_u64(&mut self, prop: &str, val: u64) -> Result<()> { self.set_property(prop, &val.to_be_bytes()[..]) - .with_context(|| anyhow!(UtilError::SetPropertyErr("u64".to_string()))) + .with_context(|| UtilError::SetPropertyErr("u64".to_string())) } pub fn set_property_array_u32(&mut self, prop: &str, array: &[u32]) -> Result<()> { @@ -244,7 +244,7 @@ impl FdtBuilder { prop_array.extend_from_slice(&element.to_be_bytes()[..]); } self.set_property(prop, &prop_array) - .with_context(|| anyhow!(UtilError::SetPropertyErr("u32 array".to_string()))) + .with_context(|| UtilError::SetPropertyErr("u32 array".to_string())) } pub fn set_property_array_u64(&mut self, prop: &str, array: &[u64]) -> Result<()> { @@ -253,7 +253,7 @@ impl FdtBuilder { prop_array.extend_from_slice(&element.to_be_bytes()[..]); } self.set_property(prop, &prop_array) - .with_context(|| anyhow!(UtilError::SetPropertyErr("u64 array".to_string()))) + .with_context(|| UtilError::SetPropertyErr("u64 array".to_string())) } pub fn set_property(&mut self, property_name: &str, property_val: &[u8]) -> Result<()> { diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 7ba03cff7..bee857174 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -392,12 +392,12 @@ impl VfioPciDevice { Region::init_io_region(table_size, table_ops.clone()), table_offset, ) - .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; + .with_context(|| VfioError::AddRegBar(i as usize))?; if table_offset > 0 { region .add_subregion(Region::init_io_region(table_offset, bar_ops.clone()), 0) - .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; + .with_context(|| VfioError::AddRegBar(i as usize))?; } if table_offset + table_size < size { @@ -409,13 +409,13 @@ impl VfioPciDevice { ), table_offset + table_size, ) - .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; + .with_context(|| VfioError::AddRegBar(i as usize))?; } region } else { region .add_subregion(Region::init_io_region(size, bar_ops.clone()), 0) - .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; + .with_context(|| VfioError::AddRegBar(i as usize))?; region }; @@ -705,7 +705,7 @@ impl VfioPciDevice { .as_ref() .unwrap() .add_subregion(ram_device, mmap.offset) - .with_context(|| anyhow!(VfioError::AddRegBar(i as usize)))?; + .with_context(|| VfioError::AddRegBar(i as usize))?; } } Ok(()) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 70b10dafa..0b4b36a2c 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -26,7 +26,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use log::error; use super::fs::FileSystem; @@ -186,7 +186,7 @@ impl VirtioFsConfig { fn get_mut_queue_config(&mut self, queue_index: usize) -> Result<&mut QueueInfo> { self.queues_info .get_mut(queue_index) - .ok_or_else(|| anyhow!("The select index of queue {} overflows", queue_index)) + .with_context(|| format!("The select index of queue {} overflows", queue_index)) } } diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index f0026c3fb..91551c0ec 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -590,10 +590,7 @@ impl BalloonIoHandler { .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("balloon", VirtioInterruptType::Vring) })? } @@ -627,10 +624,7 @@ impl BalloonIoHandler { .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("balloon", VirtioInterruptType::Vring) })?; } @@ -678,10 +672,7 @@ impl BalloonIoHandler { .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("balloon", VirtioInterruptType::Vring) })?; } @@ -916,10 +907,7 @@ impl Balloon { fn signal_config_change(&self) -> Result<()> { if let Some(interrupt_cb) = &self.interrupt_cb { interrupt_cb(&VirtioInterruptType::Config, None, false).with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "balloon", - VirtioInterruptType::Config - )) + VirtioError::InterruptTrigger("balloon", VirtioInterruptType::Config) }) } else { Err(anyhow!(VirtioError::DeviceNotActivated( @@ -1417,7 +1405,7 @@ mod tests { interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + .with_context(|| VirtioError::EventFdWrite) }, ) as VirtioInterrupt); @@ -1556,7 +1544,7 @@ mod tests { interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + .with_context(|| VirtioError::EventFdWrite) }, ) as VirtioInterrupt); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index d216d6c82..5baa3a67d 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -155,10 +155,7 @@ impl AioCompleteCb { { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "blk io completion", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("blk io completion", VirtioInterruptType::Vring) })?; self.trace_send_interrupt("Block".to_string()); } @@ -1173,12 +1170,12 @@ impl VirtioDevice for Block { self.blk_cfg.direct, self.blk_cfg.aio, )) - .with_context(|| anyhow!(VirtioError::ChannelSend("image fd".to_string())))?; + .with_context(|| VirtioError::ChannelSend("image fd".to_string()))?; } for update_evt in &self.update_evts { update_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + .with_context(|| VirtioError::EventFdWrite)?; } Ok(()) @@ -1199,7 +1196,7 @@ impl StateTransfer for Block { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *BlockState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("BLOCK")))?; + .with_context(|| migration::error::MigrationError::FromBytesError("BLOCK"))?; self.broken.store(self.state.broken, Ordering::SeqCst); Ok(()) } @@ -1455,7 +1452,7 @@ mod tests { interrupt_status.fetch_or(status as u32, Ordering::SeqCst); interrupt_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + .with_context(|| VirtioError::EventFdWrite)?; Ok(()) }, diff --git a/virtio/src/device/console.rs b/virtio/src/device/console.rs index 73ecfac49..c8fb94912 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/console.rs @@ -387,7 +387,7 @@ impl StateTransfer for Console { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *VirtioConsoleState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("CONSOLE")))?; + .with_context(|| migration::error::MigrationError::FromBytesError("CONSOLE"))?; Ok(()) } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 46bcae3df..aceae17e8 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -616,10 +616,7 @@ impl NetCtrlHandler { { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "ctrl", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("ctrl", VirtioInterruptType::Vring) })?; } } @@ -831,10 +828,7 @@ impl NetIoHandler { { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "net", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) })?; self.trace_send_interrupt("Net".to_string()); } @@ -921,10 +915,7 @@ impl NetIoHandler { { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "net", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) })?; self.trace_send_interrupt("Net".to_string()); } @@ -979,7 +970,7 @@ fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { for elem in iovec { end = start .checked_add(elem.iov_len) - .ok_or_else(|| anyhow!("Overflow when getting the net header"))?; + .with_context(|| "Overflow when getting the net header")?; end = cmp::min(end, buf.len()); mem_to_buf(&mut buf[start..end], elem.iov_base as u64)?; if end >= buf.len() { @@ -1650,9 +1641,9 @@ impl VirtioDevice for Net { .get(index) .cloned() .with_context(|| format!("Failed to get index {} tap", index))?; - sender.send(Some(tap)).with_context(|| { - anyhow!(VirtioError::ChannelSend("tap fd".to_string())) - })?; + sender + .send(Some(tap)) + .with_context(|| VirtioError::ChannelSend("tap fd".to_string()))?; } None => sender .send(None) @@ -1663,7 +1654,7 @@ impl VirtioDevice for Net { for update_evt in &self.update_evts { update_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + .with_context(|| VirtioError::EventFdWrite)?; } } diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index d1c55a872..6adcff92c 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -42,7 +42,7 @@ use crate::{ ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; const QUEUE_NUM_RNG: usize = 1; @@ -136,10 +136,7 @@ impl RngHandler { if need_interrupt { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { - anyhow!(VirtioError::InterruptTrigger( - "rng", - VirtioInterruptType::Vring - )) + VirtioError::InterruptTrigger("rng", VirtioInterruptType::Vring) })?; self.trace_send_interrupt("Rng".to_string()); } @@ -355,7 +352,7 @@ impl StateTransfer for Rng { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *RngState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("RNG")))?; + .with_context(|| migration::error::MigrationError::FromBytesError("RNG"))?; Ok(()) } @@ -548,7 +545,7 @@ mod tests { interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + .with_context(|| VirtioError::EventFdWrite) }, ) as VirtioInterrupt); @@ -631,7 +628,7 @@ mod tests { interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite)) + .with_context(|| VirtioError::EventFdWrite) }, ) as VirtioInterrupt); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 84df32e51..941fd883e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -30,7 +30,21 @@ pub mod error; mod queue; mod transport; pub mod vhost; -pub use anyhow::Result; + +use std::cmp; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; +use log::{error, warn}; +use vmm_sys_util::eventfd::EventFd; + +use address_space::AddressSpace; +use machine_manager::config::ConfigCheck; +use util::aio::mem_to_buf; +use util::num_ops::write_u32; +use util::AsAny; + pub use device::balloon::*; pub use device::block::{Block, BlockState}; pub use device::console::{Console, VirtioConsoleState}; @@ -43,26 +57,12 @@ pub use device::scsi::controller as ScsiCntlr; pub use device::scsi::disk as ScsiDisk; pub use error::VirtioError; pub use error::*; -use log::{error, warn}; pub use queue::*; pub use transport::virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; pub use transport::virtio_pci::VirtioPciDevice; pub use vhost::kernel as VhostKern; pub use vhost::user as VhostUser; -use std::cmp; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; - -use address_space::AddressSpace; -use anyhow::anyhow; -use anyhow::bail; -use machine_manager::config::ConfigCheck; -use util::aio::mem_to_buf; -use util::num_ops::write_u32; -use util::AsAny; -use vmm_sys_util::eventfd::EventFd; - /// Check if the bit of features is configured. pub fn virtio_has_feature(feature: u64, fbit: u32) -> bool { feature & (1 << fbit) != 0 @@ -464,7 +464,7 @@ pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) end = cmp::min(start + iov.len as usize, buf.len()); hva = mem_space .get_host_address(iov.addr) - .ok_or_else(|| anyhow!("Map iov base failed"))?; + .with_context(|| "Map iov base failed")?; mem_to_buf(&mut buf[start..end], hva)?; if end >= buf.len() { break; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 8a4c40e50..98972fcc0 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -189,15 +189,15 @@ impl SplitVringDesc { let desc_addr = desc_table_host .checked_add(u64::from(index) * DESCRIPTOR_LEN) .with_context(|| { - anyhow!(VirtioError::AddressOverflow( + VirtioError::AddressOverflow( "creating a descriptor", desc_table_host, u64::from(index) * DESCRIPTOR_LEN, - )) + ) })?; let desc = sys_mem .read_object_direct::(desc_addr) - .with_context(|| anyhow!(VirtioError::ReadObjectErr("a descriptor", desc_addr)))?; + .with_context(|| VirtioError::ReadObjectErr("a descriptor", desc_addr))?; if desc.is_valid(sys_mem, queue_size, cache) { Ok(desc) @@ -340,13 +340,13 @@ impl SplitVringDesc { } desc_table_host = sys_mem .get_host_address_from_cache(desc.addr, cache) - .ok_or_else(|| anyhow!("Failed to get descriptor table entry host address"))?; + .with_context(|| "Failed to get descriptor table entry host address")?; queue_size = desc.get_desc_num(); desc = Self::next_desc(sys_mem, desc_table_host, queue_size, 0, cache)?; desc_size = elem .desc_num .checked_add(queue_size) - .ok_or_else(|| anyhow!("The chained desc number overflows"))?; + .with_context(|| "The chained desc number overflows")?; continue; } @@ -429,10 +429,7 @@ impl SplitVring { sys_mem .read_object_direct::(self.addr_cache.avail_ring_host) .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "avail flags idx", - self.avail_ring.raw_value() - )) + VirtioError::ReadObjectErr("avail flags idx", self.avail_ring.raw_value()) }) } @@ -455,10 +452,7 @@ impl SplitVring { sys_mem .read_object_direct::(self.addr_cache.used_ring_host) .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "used flags idx", - self.used_ring.raw_value() - )) + VirtioError::ReadObjectErr("used flags idx", self.used_ring.raw_value()) }) } @@ -523,9 +517,7 @@ impl SplitVring { let used_event_addr = self.addr_cache.avail_ring_host + used_event_offset; let used_event = sys_mem .read_object_direct::(used_event_addr) - .with_context(|| { - anyhow!(VirtioError::ReadObjectErr("used event id", used_event_addr)) - })?; + .with_context(|| VirtioError::ReadObjectErr("used event id", used_event_addr))?; Ok(used_event) } @@ -685,10 +677,7 @@ impl SplitVring { let desc_index = sys_mem .read_object_direct::(desc_index_addr) .with_context(|| { - anyhow!(VirtioError::ReadObjectErr( - "the index of descriptor", - desc_index_addr - )) + VirtioError::ReadObjectErr("the index of descriptor", desc_index_addr) })?; let desc = SplitVringDesc::new( diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 1ae4b2165..c4007a23c 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -175,8 +175,8 @@ impl VirtioMmioCommonConfig { let queue_select = self.queue_select; self.queues_config .get_mut(queue_select as usize) - .ok_or_else(|| { - anyhow!( + .with_context(|| { + format!( "Mmio-reg queue_select {} overflows for mutable queue config", queue_select, ) @@ -191,8 +191,8 @@ impl VirtioMmioCommonConfig { let queue_select = self.queue_select; self.queues_config .get(queue_select as usize) - .ok_or_else(|| { - anyhow!( + .with_context(|| { + format!( "Mmio-reg queue_select overflows {} for immutable queue config", queue_select, ) @@ -478,7 +478,7 @@ impl VirtioMmioDevice { interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + .with_context(|| VirtioError::EventFdWrite)?; Ok(()) }, diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 04bba1d75..9a298bdf4 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -205,7 +205,7 @@ impl VirtioPciCommonConfig { return self .queues_config .get_mut(self.queue_select as usize) - .ok_or_else(|| anyhow!("pci-reg queue_select overflows")); + .with_context(|| "pci-reg queue_select overflows"); } if self.check_device_status( CONFIG_STATUS_FEATURES_OK, @@ -213,7 +213,7 @@ impl VirtioPciCommonConfig { ) { self.queues_config .get_mut(self.queue_select as usize) - .ok_or_else(|| anyhow!("pci-reg queue_select overflows")) + .with_context(|| "pci-reg queue_select overflows") } else { Err(anyhow!(PciError::DeviceStatus(self.device_status))) } @@ -222,7 +222,7 @@ impl VirtioPciCommonConfig { fn get_queue_config(&self) -> PciResult<&QueueConfig> { self.queues_config .get(self.queue_select as usize) - .ok_or_else(|| anyhow!("pci-reg queue_select overflows")) + .with_context(|| "pci-reg queue_select overflows") } fn revise_queue_vector(&self, vector_nr: u32, virtio_pci_dev: &VirtioPciDevice) -> u32 { @@ -1323,11 +1323,8 @@ impl StateTransfer for VirtioPciDevice { } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - let mut pci_state = *VirtioPciState::from_bytes(state).ok_or_else(|| { - anyhow!(migration::error::MigrationError::FromBytesError( - "PCI_DEVICE" - ),) - })?; + let mut pci_state = *VirtioPciState::from_bytes(state) + .with_context(|| migration::error::MigrationError::FromBytesError("PCI_DEVICE"))?; // Set virtio pci config state. let config_length = self.config.config.len(); diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index a16f0fb21..19638e1a8 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -357,24 +357,24 @@ impl VhostOps for VhostBackend { let locked_mem_info = self.mem_info.lock().unwrap(); let desc_user_addr = locked_mem_info .addr_to_host(queue_config.desc_table) - .ok_or_else(|| { - anyhow!(anyhow!( + .with_context(|| { + format!( "Failed to transform desc-table address {}", queue_config.desc_table.0 - )) + ) })?; let used_user_addr = locked_mem_info .addr_to_host(queue_config.used_ring) - .ok_or_else(|| { - anyhow!(anyhow!( + .with_context(|| { + format!( "Failed to transform used ring address {}", queue_config.used_ring.0 - )) + ) })?; let avail_user_addr = locked_mem_info .addr_to_host(queue_config.avail_ring) - .ok_or_else(|| { - anyhow!( + .with_context(|| { + format!( "Failed to transform avail ring address {}", queue_config.avail_ring.0 ) diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index f69428b35..88dffd770 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -336,7 +336,7 @@ impl VirtioDevice for Net { let host_notify = VhostNotify { notify_evt: Arc::new( EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| anyhow!(VirtioError::EventFdCreate))?, + .with_context(|| VirtioError::EventFdCreate)?, ), queue: queue_mutex.clone(), }; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index fc83dbd89..c5de9fd56 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -156,7 +156,7 @@ impl Vsock { Some(&*event_queue_locked), false, ) - .with_context(|| anyhow!(VirtioError::EventFdWrite))?; + .with_context(|| VirtioError::EventFdWrite)?; } } @@ -301,8 +301,7 @@ impl VirtioDevice for Vsock { let host_notify = VhostNotify { notify_evt: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| anyhow!(VirtioError::EventFdCreate))?, + EventFd::new(libc::EFD_NONBLOCK).with_context(|| VirtioError::EventFdCreate)?, ), queue: queue_mutex.clone(), }; @@ -360,7 +359,7 @@ impl StateTransfer for Vsock { fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { self.state = *VsockState::from_bytes(state) - .ok_or_else(|| anyhow!(migration::error::MigrationError::FromBytesError("VSOCK")))?; + .with_context(|| migration::error::MigrationError::FromBytesError("VSOCK"))?; self.broken.store(self.state.broken, Ordering::SeqCst); Ok(()) } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index b26cc6707..e470b4b22 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -59,7 +59,7 @@ impl Block { fn delete_event(&mut self) -> Result<()> { self.client .as_ref() - .ok_or_else(|| anyhow!("Failed to get client when stoping event"))? + .with_context(|| "Failed to get client when stoping event")? .lock() .unwrap() .delete_event() @@ -242,7 +242,7 @@ impl VirtioDevice for Block { self.client .as_ref() - .ok_or_else(|| anyhow!("Failed to get client when writing config"))? + .with_context(|| "Failed to get client when writing config")? .lock() .unwrap() .set_virtio_blk_config(self.state.config_space) @@ -274,7 +274,7 @@ impl VirtioDevice for Block { fn deactivate(&mut self) -> Result<()> { self.client .as_ref() - .ok_or_else(|| anyhow!("Failed to get client when deactivating device"))? + .with_context(|| "Failed to get client when deactivating device")? .lock() .unwrap() .reset_vhost_user()?; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 11ec96fe6..8359e9ebb 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -17,7 +17,7 @@ use std::rc::Rc; use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::{ @@ -197,8 +197,8 @@ impl VhostUserMemInfo { .region .guest_phys_addr .checked_add(reg_info.region.memory_size) - .ok_or_else(|| { - anyhow!( + .with_context(|| { + format!( "Overflow when adding gpa with memory_size in region {:x?}", reg_info.region ) @@ -251,7 +251,7 @@ impl VhostUserMemInfo { let file_back = fr .owner .get_file_backend() - .ok_or_else(|| anyhow!("Failed to get file backend"))?; + .with_context(|| "Failed to get file backend")?; let mut mem_regions = self.regions.lock().unwrap(); let host_address = match fr.owner.get_host_address() { Some(addr) => addr, @@ -307,12 +307,12 @@ impl Listener for VhostUserMemInfo { match req_type { ListenerReqType::AddRegion => { self.add_mem_range( - range.ok_or_else(|| anyhow!("Flat range is None when adding region"))?, + range.with_context(|| "Flat range is None when adding region")?, )?; } ListenerReqType::DeleteRegion => { self.delete_mem_range( - range.ok_or_else(|| anyhow!("Flat range is None when deleting region"))?, + range.with_context(|| "Flat range is None when deleting region")?, )?; } _ => {} @@ -830,26 +830,29 @@ impl VhostOps for VhostUserClient { let desc_user_addr = self .mem_info .addr_to_host(queue.desc_table) - .ok_or_else(|| { - anyhow!(format!( + .with_context(|| { + format!( "Failed to transform desc-table address {}", queue.desc_table.0 - )) + ) + })?; + let used_user_addr = self + .mem_info + .addr_to_host(queue.used_ring) + .with_context(|| { + format!( + "Failed to transform used ring address {}", + queue.used_ring.0 + ) })?; - let used_user_addr = self.mem_info.addr_to_host(queue.used_ring).ok_or_else(|| { - anyhow!(format!( - "Failed to transform used ring address {}", - queue.used_ring.0 - )) - })?; let avail_user_addr = self .mem_info .addr_to_host(queue.avail_ring) - .ok_or_else(|| { - anyhow!(format!( + .with_context(|| { + format!( "Failed to transform avail ring address {}", queue.avail_ring.0 - )) + ) })?; let _vring_addr = VhostUserVringAddr { index: index as u32, -- Gitee From 72de99b6085493cf6f0dbd4f55d89066403ca2d8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 06:14:29 +0800 Subject: [PATCH 0915/2187] Error: Fix error logging Should use {:?} to log error chain, otherwise only the last error is logged. Signed-off-by: Keqian Zhu --- address_space/src/host_mmap.rs | 6 +- cpu/src/lib.rs | 14 +-- devices/src/acpi/ged.rs | 2 +- .../src/interrupt_controller/aarch64/gicv3.rs | 5 +- devices/src/legacy/fwcfg.rs | 20 +--- devices/src/legacy/pflash.rs | 97 ++++++------------- devices/src/legacy/pl011.rs | 6 +- devices/src/legacy/pl031.rs | 2 +- devices/src/legacy/rtc.rs | 2 +- devices/src/legacy/serial.rs | 2 +- devices/src/misc/scream/pulseaudio.rs | 4 +- devices/src/usb/keyboard.rs | 2 +- devices/src/usb/mod.rs | 8 +- devices/src/usb/tablet.rs | 2 +- devices/src/usb/xhci/xhci_controller.rs | 8 +- hypervisor/src/kvm/mod.rs | 4 +- machine_manager/src/qmp/mod.rs | 2 +- migration/src/lib.rs | 8 +- pci/src/root_port.rs | 2 +- ui/src/console.rs | 2 +- ui/src/vnc/auth_vencrypt.rs | 2 +- ui/src/vnc/client_io.rs | 8 +- util/src/unix.rs | 2 +- vfio/src/vfio_pci.rs | 34 +++---- vhost_user_fs/src/fuse_req.rs | 2 +- vhost_user_fs/src/sandbox.rs | 2 +- virtio/src/device/console.rs | 2 +- virtio/src/device/gpu.rs | 2 +- virtio/src/device/net.rs | 20 ++-- virtio/src/device/scsi/controller.rs | 6 +- virtio/src/lib.rs | 2 +- virtio/src/transport/virtio_pci.rs | 2 +- 32 files changed, 114 insertions(+), 168 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 27e25a668..f93a73019 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -92,7 +92,7 @@ impl FileBackend { if unsafe { libc::unlink(fs_cstr) } != 0 { error!( - "Failed to unlink file \"{}\", error: {}", + "Failed to unlink file \"{}\", error: {:?}", fs_path, std::io::Error::last_os_error() ); @@ -117,7 +117,7 @@ impl FileBackend { if unsafe { libc::unlink(fs_cstr) } != 0 { error!( - "Failed to unlink file \"{}\", error: {}", + "Failed to unlink file \"{}\", error: {:?}", file_path, std::io::Error::last_os_error() ); @@ -227,7 +227,7 @@ fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { // join all threads to wait for pre-allocating. while let Some(thread) = threads_join.pop() { if let Err(ref e) = thread.join() { - error!("{}", format!("Failed to join thread: {:?}", e)); + error!("Failed to join thread: {:?}", e); } } } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index f11368dde..3e85e0226 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -336,11 +336,8 @@ impl CPUInterface for CPU { .spawn(move || { if let Err(e) = cpu_thread_worker.handle(thread_barrier) { error!( - "{}", - format!( - "Some error occurred in cpu{} thread: {:?}", - cpu_thread_worker.thread_cpu.id, e - ) + "Some error occurred in cpu{} thread: {:?}", + cpu_thread_worker.thread_cpu.id, e ); } }) @@ -632,7 +629,7 @@ impl CPUThreadWorker { #[cfg(target_arch = "x86_64")] &vcpu.caps, ) { - error!("Failed to reset vcpu state: {}", e.to_string()) + error!("Failed to reset vcpu state: {:?}", e) } }); } @@ -682,10 +679,7 @@ impl CPUThreadWorker { fn handle(&self, thread_barrier: Arc) -> Result<()> { self.init_local_thread_vcpu(); if let Err(e) = Self::init_signals() { - error!( - "{}", - format!("Failed to init cpu{} signal:{:?}", self.thread_cpu.id, e) - ); + error!("Failed to init cpu{} signal:{:?}", self.thread_cpu.id, e); } self.thread_cpu.set_tid(); diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index ae37409de..ca8a6f182 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -114,7 +114,7 @@ impl Ged { if let Some(evt_fd) = self.interrupt_evt() { evt_fd .write(1) - .unwrap_or_else(|e| error!("ged: failed to write interrupt eventfd ({}).", e)); + .unwrap_or_else(|e| error!("ged: failed to write interrupt eventfd ({:?}).", e)); } } } diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 4f5d6f61b..e22f90f0d 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -265,10 +265,7 @@ impl MachineLifecycle for GICv3 { // The ITS tables need to be flushed into guest RAM before VM pause. if let Some(its_dev) = &self.its_dev { if let Err(e) = its_dev.access_gic_its_tables(true) { - error!( - "{}", - format!("Failed to access GIC ITS tables, error: {:?}", e) - ); + error!("Failed to access GIC ITS tables, error: {:?}", e); return false; } } diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 265b2f062..66a57f93c 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -870,10 +870,7 @@ fn read_bytes( 0..=7 => match fwcfg_arch.fwcfg.read_data_reg(offset, data.len() as u32) { Ok(val) => val, Err(e) => { - error!( - "{}", - format!("Failed to read from FwCfg data register, error is {:?}", e) - ); + error!("Failed to read from FwCfg data register, error is {:?}", e); return false; } }, @@ -896,10 +893,7 @@ fn read_bytes( { Ok(val) => val, Err(e) => { - error!( - "{}", - format!("Failed to handle FwCfg DMA-read, error is {:?}", e) - ); + error!("Failed to handle FwCfg DMA-read, error is {:?}", e); return false; } } @@ -1052,10 +1046,7 @@ fn read_bytes(fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, off } match fwcfg_arch.fwcfg.dma_mem_read(offset - 4, data.len() as u32) { Err(e) => { - error!( - "{}", - format!("Failed to handle FwCfg DMA-read, error is {:?}", e) - ); + error!("Failed to handle FwCfg DMA-read, error is {:?}", e); return false; } Ok(val) => val, @@ -1122,10 +1113,7 @@ impl SysBusDevOps for FwCfgIO { _ => 0, }; if let Err(e) = self.fwcfg.dma_mem_write(offset - 4, value, size) { - error!( - "{}", - format!("Failed to handle FwCfg DMA-write, error is {:?}", e) - ); + error!("Failed to handle FwCfg DMA-write, error is {:?}", e); return false; } } diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 1b52d2b95..abe17601f 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -391,10 +391,8 @@ impl PFlash { 0x00 | 0xf0 | 0xff => { if let Err(e) = self.set_read_array_mode(false) { error!( - "{}", - format!("Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", - cmd, - e) + "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", + cmd, e ); return false; } @@ -408,14 +406,11 @@ impl PFlash { if !self.read_only { let all_one = vec![0xff_u8; self.block_len as usize]; if let Err(e) = self.write_data(all_one.as_slice(), offset_mask) { - error!("{}", format!("Failed to write PFlash device: {:?}.", e)); + error!("Failed to write PFlash device: {:?}", e); } if let Err(e) = self.update_content(offset_mask, self.block_len) { - error!( - "{}", - format!("Failed to update content for PFlash device: {:?}.", e) - ); + error!("Failed to update content for PFlash device: {:?}", e); } } else { // Block erase error. @@ -428,10 +423,8 @@ impl PFlash { self.status = 0x0; if let Err(e) = self.set_read_array_mode(false) { error!( - "{}", - format!("Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", - cmd, - e) + "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", + cmd, e ); return false; } @@ -454,10 +447,8 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "{}", - format!("Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", - cmd, - e) + "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", + cmd, e ); return false; } @@ -484,10 +475,7 @@ impl PFlash { error!("Failed to write to PFlash device: {:?}.", e); } if let Err(e) = self.update_content(offset, data_len.into()) { - error!( - "{}", - format!("Failed to update content for PFlash device: {:?}.", e) - ); + error!("Failed to update content for PFlash device: {:?}", e); } } else { self.status |= 0x10; @@ -501,22 +489,18 @@ impl PFlash { self.status |= 0x80; } else if cmd == 0xff { if let Err(e) = self.set_read_array_mode(false) { - error!( - "{}", - format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", + error!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", cmd, - e) + e ); return false; } return true; } else { if let Err(e) = self.set_read_array_mode(true) { - error!( - "{}", - format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", + error!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e) + e ); return false; } @@ -546,22 +530,18 @@ impl PFlash { self.status |= 0x80; } else if cmd == 0xff { if let Err(e) = self.set_read_array_mode(false) { - error!( - "{}", - format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", + error!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e) + e ); return false; } return true; } else { if let Err(e) = self.set_read_array_mode(true) { - error!( - "{}", - format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", + error!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e) + e ); return false; } @@ -571,11 +551,9 @@ impl PFlash { 0x98 => { if cmd == 0xff { if let Err(e) = self.set_read_array_mode(false) { - error!( - "{}", - format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", + error!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, - e) + e ); return false; } @@ -585,10 +563,8 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "{}", - format!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", - self.cmd, - e) + "Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", + self.cmd, e ); return false; } @@ -603,7 +579,7 @@ impl PFlash { 0xe8 => { if !self.read_only { if let Err(e) = self.write_data(data, offset) { - error!("{}", format!("Failed to write to PFlash device: {:?}.", e)); + error!("Failed to write to PFlash device: {:?}", e); } } else { self.status |= 0x10; @@ -614,10 +590,7 @@ impl PFlash { self.write_cycle += 1; if !self.read_only { if let Err(e) = self.update_content(offset & mask, self.write_blk_size) { - error!( - "{}", - format!("Failed to update content for PFlash device: {:?}.", e) - ); + error!("Failed to update content for PFlash device: {:?}", e); } } else { self.status |= 0x10; @@ -629,10 +602,8 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "{}", - format!("Failed to set read array mode, write cycle 2, cmd 0x{:x}, error is {:?}", - self.cmd, - e) + "Failed to set read array mode, write cycle 2, cmd 0x{:x}, error is {:?}", + self.cmd, e ); return false; } @@ -650,11 +621,9 @@ impl PFlash { self.status |= 0x80; } else { if let Err(e) = self.set_read_array_mode(false) { - error!( - "{}", - format!("Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {:?}", + error!("Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {:?}", self.cmd, - e) + e ); return false; } @@ -664,10 +633,8 @@ impl PFlash { _ => { if let Err(e) = self.set_read_array_mode(true) { error!( - "{}", - format!("Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {:?}", - self.cmd, - e) + "Failed to set read array mode, write cycle 3, cmd 0x{:x}, error is {:?}", + self.cmd, e ); return false; } @@ -737,7 +704,7 @@ impl SysBusDevOps for PFlash { while i < data_len { match self.query_devid(offset + (i * self.bank_width) as u64) { Err(e) => { - error!("{}", format!("Failed to query devid {:?}.", e)); + error!("Failed to query devid {:?}", e); break; } Ok(fieldval) => { @@ -777,7 +744,7 @@ impl SysBusDevOps for PFlash { while i < data_len { match self.query_cfi(offset + (i * self.bank_width) as u64) { Err(e) => { - error!("{}", format!("Failed to query devid, {:?}.", e)); + error!("Failed to query devid, {:?}", e); break; } Ok(fieldval) => { @@ -804,7 +771,7 @@ impl SysBusDevOps for PFlash { self.write_cycle = 0; self.cmd = 0x00; if let Err(e) = self.read_data(data, offset) { - error!("{}", format!("Failed to read data from PFlash: {:?}.", e)); + error!("Failed to read data from PFlash: {:?}", e); } } } diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index c46352e04..23ca96a01 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -146,7 +146,7 @@ impl PL011 { if flag & irq_mask != 0 { if let Err(e) = self.interrupt_evt.write(1) { error!( - "Failed to trigger interrupt for PL011, flag is 0x{:x}, error is {}", + "Failed to trigger interrupt for PL011, flag is 0x{:x}, error is {:?}", flag, e, ) } @@ -318,10 +318,10 @@ impl SysBusDevOps for PL011 { if let Some(output) = &mut self.chardev.lock().unwrap().output { let mut locked_output = output.lock().unwrap(); if let Err(e) = locked_output.write_all(&[ch]) { - error!("Failed to write to pl011 output fd, error is {}", e); + error!("Failed to write to pl011 output fd, error is {:?}", e); } if let Err(e) = locked_output.flush() { - error!("Failed to flush pl011, error is {}", e); + error!("Failed to flush pl011, error is {:?}", e); } } else { debug!("Failed to get output fd"); diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 1e87e53a6..912e86640 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -127,7 +127,7 @@ impl PL031 { fn inject_interrupt(&self) { if let Some(evt_fd) = self.interrupt_evt() { if let Err(e) = evt_fd.write(1) { - error!("pl031: failed to write interrupt eventfd ({}).", e); + error!("pl031: failed to write interrupt eventfd ({:?}).", e); } return; } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 48e858960..a2098eb1f 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -274,7 +274,7 @@ impl RTC { fn inject_interrupt(&self) { if let Some(evt_fd) = self.interrupt_evt() { if let Err(e) = evt_fd.write(1) { - error!("cmos rtc: failed to write interrupt eventfd ({}).", e); + error!("cmos rtc: failed to write interrupt eventfd ({:?}).", e); } return; } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index b3ba910bd..2f972ef9f 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -191,7 +191,7 @@ impl Serial { if iir != UART_IIR_NO_INT { if let Some(evt) = self.interrupt_evt() { if let Err(e) = evt.write(1) { - error!("serial: failed to write interrupt eventfd ({}).", e); + error!("serial: failed to write interrupt eventfd ({:?}).", e); } return; } diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 195b0f383..a97ae6fd9 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -252,7 +252,7 @@ impl AudioInterface for PulseStreamData { }; if let Err(e) = self.simple.as_ref().unwrap().write(data) { - error!("PulseAudio write data failed: {}", e); + error!("PulseAudio write data failed: {:?}", e); } } @@ -273,7 +273,7 @@ impl AudioInterface for PulseStreamData { }; if let Err(e) = self.simple.as_ref().unwrap().read(data) { - error!("PulseAudio read data failed: {}", e); + error!("PulseAudio read data failed: {:?}", e); self.ss.rate = 0; return false; } diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 32dce752b..baed8a07d 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -208,7 +208,7 @@ impl UsbDeviceOps for UsbKeyboard { } } Err(e) => { - error!("Keyboard descriptor error {}", e); + error!("Keyboard descriptor error {:?}", e); packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 2c50c709f..e3931ffd8 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -314,7 +314,7 @@ pub trait UsbDeviceOps: Send + Sync { debug!("handle packet endpointer number {}", ep_nr); if ep_nr == 0 { if let Err(e) = self.do_parameter(packet) { - error!("Failed to handle control packet {}", e); + error!("Failed to handle control packet {:?}", e); } } else { self.handle_data(packet); @@ -409,7 +409,7 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { } } if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep) { - error!("Failed to wakeup endpoint {}", e); + error!("Failed to wakeup endpoint {:?}", e); } Ok(()) } @@ -480,7 +480,7 @@ impl UsbPacket { let cnt = min(iov.iov_len, len - copyed); let tmp = &vec[copyed..(copyed + cnt)]; if let Err(e) = mem_from_buf(tmp, iov.iov_base) { - error!("Failed to write mem: {}", e); + error!("Failed to write mem: {:?}", e); } copyed += cnt; if len == copyed { @@ -492,7 +492,7 @@ impl UsbPacket { let cnt = min(iov.iov_len, len - copyed); let tmp = &mut vec[copyed..(copyed + cnt)]; if let Err(e) = mem_to_buf(tmp, iov.iov_base) { - error!("Failed to read mem {}", e); + error!("Failed to read mem {:?}", e); } copyed += cnt; if len == copyed { diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index a693a23ef..c5f52340b 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -195,7 +195,7 @@ impl UsbDeviceOps for UsbTablet { } } Err(e) => { - error!("Tablet descriptor error {}", e); + error!("Tablet descriptor error {:?}", e); packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 7ebc59480..acbe3a1a2 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1303,7 +1303,7 @@ impl XhciDevice { let epctx = match self.get_endpoint_ctx(slot_id, ep_id) { Ok(epctx) => epctx, Err(e) => { - error!("Kick endpoint error: {}", e); + error!("Kick endpoint error: {:?}", e); // No need to return the error, just ignore it. return Ok(()); } @@ -1443,7 +1443,7 @@ impl XhciDevice { /// Control Transfer, TRBs include Setup, Data(option), Status. fn do_ctrl_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { if let Err(e) = self.check_ctrl_transfer(xfer) { - error!("Failed to check control transfer {}", e); + error!("Failed to check control transfer {:?}", e); xfer.status = TRBCCode::TrbError; return self.report_transfer_error(xfer); } @@ -1452,7 +1452,7 @@ impl XhciDevice { xfer.in_xfer = bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; if let Err(e) = self.setup_usb_packet(xfer) { - error!("Failed to setup packet when transfer control {}", e); + error!("Failed to setup packet when transfer control {:?}", e); xfer.status = TRBCCode::TrbError; return self.report_transfer_error(xfer); } @@ -1500,7 +1500,7 @@ impl XhciDevice { warn!("Unhandled ep_type {:?}", epctx.ep_type); } if let Err(e) = self.setup_usb_packet(xfer) { - error!("Failed to setup packet when transfer data {}", e); + error!("Failed to setup packet when transfer data {:?}", e); xfer.status = TRBCCode::TrbError; return self.report_transfer_error(xfer); } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index a23277e33..12863f3a6 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -110,7 +110,7 @@ impl KVMFds { let vm_fd = match fd.create_vm() { Ok(vm_fd) => vm_fd, Err(e) => { - error!("Failed to create VM in KVM: {}", e); + error!("Failed to create VM in KVM: {:?}", e); return KVMFds::default(); } }; @@ -123,7 +123,7 @@ impl KVMFds { } } Err(e) => { - error!("Failed to open /dev/kvm: {}", e); + error!("Failed to open /dev/kvm: {:?}", e); KVMFds::default() } } diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 542cbb25c..e1f0acbe7 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -399,7 +399,7 @@ pub fn handle_qmp( } (Err(e), _) => { let err_resp = schema::QmpErrorClass::GenericError(format!("{}", &e)); - warn!("Qmp json parser made an error:{}", e); + warn!("Qmp json parser made an error: {:?}", e); qmp_service.send_str(&serde_json::to_string(&Response::create_error_response( err_resp, None, ))?)?; diff --git a/migration/src/lib.rs b/migration/src/lib.rs index b469af499..1cc892cec 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -83,7 +83,7 @@ pub fn migration_unix_mode(path: String) -> Response { error!("Failed to send migration: {:?}", e); let _ = MigrationManager::recover_from_migration(); let _ = MigrationManager::set_status(MigrationStatus::Failed) - .map_err(|e| error!("{}", e)); + .map_err(|e| error!("{:?}", e)); } }) { @@ -108,10 +108,10 @@ pub fn migration_tcp_mode(path: String) -> Response { let time_out = Some(Duration::from_secs(30)); _sock .set_read_timeout(time_out) - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); _sock .set_write_timeout(time_out) - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); _sock } Err(e) => { @@ -129,7 +129,7 @@ pub fn migration_tcp_mode(path: String) -> Response { error!("Failed to send migration: {:?}", e); let _ = MigrationManager::recover_from_migration(); let _ = MigrationManager::set_status(MigrationStatus::Failed) - .map_err(|e| error!("{}", e)); + .map_err(|e| error!("{:?}", e)); } }) { diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 5731bbe83..dbac18cce 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -414,7 +414,7 @@ impl PciDevOps for RootPort { if (!old_br_ctl & new_br_ctl & BRIDGE_CTL_SEC_BUS_RESET) != 0 { if let Err(e) = self.reset(true) { error!( - "Failed to reset child devices under root port {}: {}", + "Failed to reset child devices under root port {}: {:?}", self.name, e ) } diff --git a/ui/src/console.rs b/ui/src/console.rs index 853080726..497f8172b 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -235,7 +235,7 @@ pub fn display_refresh() { for dcl in &mut related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); if let Err(e) = (*dcl_opts).dpy_refresh(dcl) { - error!("{}", e); + error!("{:?}", e); return; } diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index 051b47cfc..f3dc6497e 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -161,7 +161,7 @@ impl ClientIoHandler { dis_conn = true; } else if event & EventSet::IN == EventSet::IN { if let Err(e) = tls_io_channel.borrow_mut().tls_handshake() { - error!("Tls handle shake error: {}", e); + error!("Tls handle shake error: {:?}", e); dis_conn = true; } } diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 9588384b9..2fcdc14d6 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -658,11 +658,11 @@ impl ClientIoHandler { } ClientMsg::KeyEvent => { self.key_envent() - .unwrap_or_else(|e| error!("Key event error: {}", e)); + .unwrap_or_else(|e| error!("Key event error: {:?}", e)); } ClientMsg::PointerEvent => { self.point_event() - .unwrap_or_else(|e| error!("Point event error: {}", e)); + .unwrap_or_else(|e| error!("Point event error: {:?}", e)); } ClientMsg::ClientCutText => { self.client_cut_event(); @@ -1063,7 +1063,7 @@ impl EventNotifierHelper for ClientIoHandler { client.conn_state.lock().unwrap().dis_conn = true; } else if event & EventSet::IN == EventSet::IN { if let Err(e) = locked_client_io.client_handle_read() { - error!("{}", e); + error!("{:?}", e); client.conn_state.lock().unwrap().dis_conn = true; } } @@ -1123,7 +1123,7 @@ impl EventNotifierHelper for ClientIoHandler { let notifiers = locked_client_io.disconn_evt_handler(); // Shutdown stream. if let Err(e) = locked_client_io.stream.shutdown(Shutdown::Both) { - error!("Shutdown stream failed: {}", e); + error!("Shutdown stream failed: {:?}", e); } drop(locked_client_io); server.client_handlers.lock().unwrap().remove(&addr); diff --git a/util/src/unix.rs b/util/src/unix.rs index 2ce776c1e..fcf0f3fcb 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -131,7 +131,7 @@ fn set_memory_undumpable(host_addr: *mut libc::c_void, size: u64) { let ret = unsafe { libc::madvise(host_addr, size as libc::size_t, libc::MADV_DONTDUMP) }; if ret < 0 { error!( - "Syscall madvise(with MADV_DONTDUMP) failed, OS error is {}", + "Syscall madvise(with MADV_DONTDUMP) failed, OS error is {:?}", std::io::Error::last_os_error() ); } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index bee857174..1d48e3f7f 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -517,7 +517,7 @@ impl VfioPciDevice { { Ok(g) => g as i32, Err(e) => { - error!("Failed to allocate gsi, error is {}", e); + error!("Failed to allocate gsi, error is {:?}", e); return true; } }; @@ -528,15 +528,15 @@ impl VfioPciDevice { .lock() .unwrap() .add_msi_route(gsi_route.gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {:?}", e)); KVM_FDS .load() .commit_irq_routing() - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); KVM_FDS .load() .register_irqfd(gsi_route.irq_fd.as_ref().unwrap(), gsi_route.gsi as u32) - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); } else { KVM_FDS .load() @@ -544,25 +544,25 @@ impl VfioPciDevice { .lock() .unwrap() .update_msi_route(gsi_route.gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {:?}", e)); KVM_FDS .load() .commit_irq_routing() - .unwrap_or_else(|e| error!("{}", e)); + .unwrap_or_else(|e| error!("{:?}", e)); } let mut locked_dev = cloned_dev.lock().unwrap(); if (vector + 1) > (locked_dev.nr_vectors as u64) { locked_dev .disable_irqs() - .unwrap_or_else(|e| error!("Failed to disable irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to disable irq, error is {:?}", e)); locked_dev .enable_irqs( get_irq_rawfds(&locked_gsi_routes, 0, (vector + 1) as u32), 0, ) - .unwrap_or_else(|e| error!("Failed to enable irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to enable irq, error is {:?}", e)); locked_dev.nr_vectors = (vector + 1) as usize; } else { locked_dev @@ -570,7 +570,7 @@ impl VfioPciDevice { get_irq_rawfds(&locked_gsi_routes, vector as u32, 1), vector as u32, ) - .unwrap_or_else(|e| error!("Failed to enable irq, error is {}", e)); + .unwrap_or_else(|e| error!("Failed to enable irq, error is {:?}", e)); } true }; @@ -603,7 +603,7 @@ impl VfioPciDevice { .read_region(data, r.region_offset, offset) { error!( - "Failed to read bar region, address is {}, offset is {}, error is {}", + "Failed to read bar region, address is {}, offset is {}, error is {:?}", addr.0, offset, e, ); } @@ -633,7 +633,7 @@ impl VfioPciDevice { .write_region(data, r.region_offset, offset) { error!( - "Failed to write bar region, address is {}, offset is {}, error is {}", + "Failed to write bar region, address is {}, offset is {}, error is {:?}", addr.0, offset, e, ); } @@ -877,7 +877,7 @@ impl PciDevOps for VfioPciDevice { fn unrealize(&mut self) -> pci::Result<()> { if let Err(e) = VfioPciDevice::unrealize(self) { - error!("{}", format!("{:?}", e)); + error!("{:?}", e); bail!("Failed to unrealize vfio-pci."); } Ok(()) @@ -919,7 +919,7 @@ impl PciDevOps for VfioPciDevice { .unwrap() .read_region(data, self.config_offset, offset as u64) { - error!("Failed to read device pci config, error is {}", e); + error!("Failed to read device pci config, error is {:?}", e); return; } for (i, data) in data.iter_mut().enumerate().take(size) { @@ -949,7 +949,7 @@ impl PciDevOps for VfioPciDevice { .unwrap() .write_region(data, self.config_offset, offset as u64) { - error!("Failed to write device pci config, error is {}", e); + error!("Failed to write device pci config, error is {:?}", e); return; } @@ -975,7 +975,7 @@ impl PciDevOps for VfioPciDevice { != 0 { if let Err(e) = self.setup_bars_mmap() { - error!("Failed to map bar regions, error is {}", format!("{:?}", e)); + error!("Failed to map bar regions, error is {:?}", e); } } } else if ranges_overlap(offset, end, cap_offset, cap_offset + MSIX_CAP_SIZE as usize) { @@ -983,11 +983,11 @@ impl PciDevOps for VfioPciDevice { if !was_enable && is_enable { if let Err(e) = self.vfio_enable_msix() { - error!("{}\nFailed to enable MSI-X.", format!("{:?}", e)); + error!("{:?}\nFailed to enable MSI-X.", e); } } else if was_enable && !is_enable { if let Err(e) = self.vfio_disable_msix() { - error!("{}\nFailed to disable MSI-X.", format!("{:?}", e)); + error!("{:?}\nFailed to disable MSI-X.", e); } } } diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index b2f8ed6d3..bb50e9de1 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -55,7 +55,7 @@ impl FuseReq { let in_header = match self.reader.read_obj::(sys_mem) { Ok(data) => data, Err(err) => { - error!("Failed to read the header of fuse msg, {:?}", err,); + error!("Failed to read the header of fuse msg, {:?}", err); return (self.desc_index, 0); } }; diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs index 3317cfe9d..fd8c529ed 100644 --- a/vhost_user_fs/src/sandbox.rs +++ b/vhost_user_fs/src/sandbox.rs @@ -197,7 +197,7 @@ impl Sandbox { pub fn parent_process_wait_child(&self, fork_ans: i32) -> Result<()> { capng::clear(capng::Set::BOTH); if let Err(err) = capng::apply(capng::Set::BOTH) { - error!("apply fail {}", err); + error!("apply fail {:?}", err); } let mut wstatus = 0; diff --git a/virtio/src/device/console.rs b/virtio/src/device/console.rs index c8fb94912..d552294f6 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/console.rs @@ -141,7 +141,7 @@ impl InputReceiver for ConsoleHandler { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) { error!( - "Failed to trigger interrupt for console, int-type {:?} {:?} ", + "Failed to trigger interrupt for console, int-type {:?} {:?}", VirtioInterruptType::Vring, e ) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 69fea18b4..f399f6ff6 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -734,7 +734,7 @@ impl GpuIoHandler { let pixman_format = match get_pixman_format(res.format) { Ok(f) => f, Err(e) => { - error!("GuestError: {:?}.", e); + error!("GuestError: {:?}", e); return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } }; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index aceae17e8..ce49d1baf 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -283,7 +283,7 @@ impl CtrlInfo { let mut mac = [0; MAC_ADDR_LEN]; *data_iovec = get_buf_and_discard(mem_space, data_iovec, &mut mac).unwrap_or_else(|e| { - error!("Failed to get MAC address, error is {}", e); + error!("Failed to get MAC address, error is {:?}", e); ack = VIRTIO_NET_ERR; Vec::new() }); @@ -301,7 +301,7 @@ impl CtrlInfo { ack = self .set_mac_table(mem_space, data_iovec) .unwrap_or_else(|e| { - error!("Failed to get Unicast Mac address, error is {}", e); + error!("Failed to get Unicast Mac address, error is {:?}", e); VIRTIO_NET_ERR }); } @@ -325,7 +325,7 @@ impl CtrlInfo { *data_iovec = get_buf_and_discard(mem_space, data_iovec, vid.as_mut_bytes()) .unwrap_or_else(|e| { - error!("Failed to get vlan id, error is {}", e); + error!("Failed to get vlan id, error is {:?}", e); ack = VIRTIO_NET_ERR; Vec::new() }); @@ -368,7 +368,7 @@ impl CtrlInfo { let mut queue_pairs: u16 = 0; *data_iovec = get_buf_and_discard(mem_space, data_iovec, queue_pairs.as_mut_bytes()) .unwrap_or_else(|e| { - error!("Failed to get queue pairs {}", e); + error!("Failed to get queue pairs {:?}", e); ack = VIRTIO_NET_ERR; Vec::new() }); @@ -564,7 +564,7 @@ impl NetCtrlHandler { .unwrap() .handle_rx_mode(&self.mem_space, ctrl_hdr.cmd, &mut data_iovec) .unwrap_or_else(|e| { - error!("Failed to handle rx mode, error is {}", e); + error!("Failed to handle rx mode, error is {:?}", e); VIRTIO_NET_ERR }); } @@ -638,7 +638,7 @@ impl EventNotifierHelper for NetCtrlHandler { return None; } locked_net_io.handle_ctrl().unwrap_or_else(|e| { - error!("Failed to handle ctrl queue, error is {}.", e); + error!("Failed to handle ctrl queue, error is {:?}.", e); report_virtio_error( locked_net_io.interrupt_cb.clone(), locked_net_io.driver_features, @@ -725,10 +725,10 @@ impl NetIoHandler { Ok(cnt) => error!("Failed to call readv but tap read is ok: cnt {}", cnt), Err(e) => { // When the backend tap device is abnormally removed, read return EBADFD. - error!("Failed to read tap: {}", e); + error!("Failed to read tap: {:?}", e); } } - error!("Failed to call readv for net handle_rx: {}", e); + error!("Failed to call readv for net handle_rx: {:?}", e); } size @@ -862,7 +862,7 @@ impl NetIoHandler { ErrorKind::Interrupted => continue, ErrorKind::WouldBlock => return -1_i8, // Ignore other errors which can not be handled. - _ => error!("Failed to call writev for net handle_tx: {}", e), + _ => error!("Failed to call writev for net handle_tx: {:?}", e), } } break; @@ -937,7 +937,7 @@ impl NetIoHandler { locked_net_io.tap = match locked_net_io.receiver.recv() { Ok(tap) => tap, Err(e) => { - error!("Failed to receive the tap {}", e); + error!("Failed to receive the tap {:?}", e); None } }; diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index ffed2fa37..312f76cfe 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -713,7 +713,7 @@ impl EventNotifierHelper for ScsiCtrlHandler { } h_lock .handle_ctrl() - .unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {}.", e)); + .unwrap_or_else(|e| error!("Failed to handle ctrl queue, error is {:?}", e)); None }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); @@ -751,7 +751,7 @@ impl EventNotifierHelper for ScsiEventHandler { } h_lock .handle_event() - .unwrap_or_else(|e| error!("Failed to handle event queue, err is {}", e)); + .unwrap_or_else(|e| error!("Failed to handle event queue, err is {:?}", e)); None }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); @@ -815,7 +815,7 @@ impl EventNotifierHelper for ScsiCmdHandler { } h_lock .handle_cmd() - .unwrap_or_else(|e| error!("Failed to handle cmd queue, err is {}", e)); + .unwrap_or_else(|e| error!("Failed to handle cmd queue, err is {:?}", e)); None }); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 941fd883e..3c4b4ca0e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -445,7 +445,7 @@ pub fn report_virtio_error( if virtio_has_feature(features, VIRTIO_F_VERSION_1) { interrupt_cb(&VirtioInterruptType::Config, None, true).unwrap_or_else(|e| { error!( - "Failed to trigger interrupt for virtio error, error is {}", + "Failed to trigger interrupt for virtio error, error is {:?}", e ) }); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 9a298bdf4..e4e612358 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1405,7 +1405,7 @@ impl MigrationHook for VirtioPciDevice { &self.queues.lock().unwrap(), queue_evts, ) { - error!("Failed to resume device, error is {}", e); + error!("Failed to resume device, error is {:?}", e); } } else { error!("Failed to resume device: No interrupt callback"); -- Gitee From e37c3728c413ac217ceacc243cb27e3e881db78e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 07:34:08 +0800 Subject: [PATCH 0916/2187] Error: Fix some map_err() with with_context() Signed-off-by: Keqian Zhu --- cpu/src/aarch64/mod.rs | 2 +- machine_manager/src/config/machine_config.rs | 28 ++++++-------------- machine_manager/src/config/mod.rs | 4 +-- machine_manager/src/config/network.rs | 2 +- machine_manager/src/config/numa.rs | 9 ++----- machine_manager/src/config/vnc.rs | 6 ++--- migration/src/lib.rs | 8 +++--- ozone/src/handler.rs | 6 ++--- 8 files changed, 24 insertions(+), 41 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 4dbdd2750..2c123ae55 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -338,7 +338,7 @@ impl StateTransfer for CPU { if cpu_state.features.pmu { self.init_pmu() - .map_err(|_| MigrationError::FromBytesError("failed to init pmu."))?; + .with_context(|| MigrationError::FromBytesError("Failed to init pmu."))?; } Ok(()) } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index fd520f0db..488818ca0 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -452,11 +452,8 @@ impl VmConfig { fn get_mem_zone_host_nodes(&self, cmd_parser: &CmdParser) -> Result>> { if let Some(mut host_nodes) = cmd_parser .get_value::("host-nodes") - .map_err(|_| { - anyhow!(ConfigError::ConvertValueFailed( - String::from("u32"), - "host-nodes".to_string() - )) + .with_context(|| { + ConfigError::ConvertValueFailed(String::from("u32"), "host-nodes".to_string()) })? .map(|v| v.0.iter().map(|e| *e as u32).collect::>()) { @@ -617,11 +614,8 @@ fn memory_unit_conversion(origin_value: &str) -> Result { get_inner( value .parse::() - .map_err(|_| { - anyhow!(ConfigError::ConvertValueFailed( - origin_value.to_string(), - String::from("u64") - )) + .with_context(|| { + ConfigError::ConvertValueFailed(origin_value.to_string(), String::from("u64")) })? .checked_mul(M), ) @@ -633,20 +627,14 @@ fn memory_unit_conversion(origin_value: &str) -> Result { get_inner( value .parse::() - .map_err(|_| { - anyhow!(ConfigError::ConvertValueFailed( - origin_value.to_string(), - String::from("u64") - )) + .with_context(|| { + ConfigError::ConvertValueFailed(origin_value.to_string(), String::from("u64")) })? .checked_mul(G), ) } else { - let size = origin_value.parse::().map_err(|_| { - anyhow!(ConfigError::ConvertValueFailed( - origin_value.to_string(), - String::from("u64") - )) + let size = origin_value.parse::().with_context(|| { + ConfigError::ConvertValueFailed(origin_value.to_string(), String::from("u64")) })?; let memory_size = size.checked_mul(M); diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ccfb76202..4c4767f82 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -600,12 +600,12 @@ impl FromStr for IntegerList { let start = items[0] .parse::() - .map_err(|_| error!("Invalid value {}", items[0]))?; + .map_err(|e| error!("Invalid value {}, error is {:?}", items[0], e))?; integer_list.push(start); if items.len() == 2 { let end = items[1] .parse::() - .map_err(|_| error!("Invalid value {}", items[1]))?; + .map_err(|e| error!("Invalid value {}, error is {:?}", items[1], e))?; if start >= end { return Err(()); } diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index a9ac5d97c..c18130ba9 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -188,7 +188,7 @@ fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result>> { raw_fds.push( (*fd) .parse::() - .map_err(|_| anyhow!("Failed to parse fds"))?, + .with_context(|| "Failed to parse fds")?, ); } Ok(Some(raw_fds)) diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 485992d80..c5e07282e 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -13,7 +13,7 @@ use std::cmp::max; use std::collections::{BTreeMap, HashSet}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use super::error::ConfigError; use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; @@ -139,12 +139,7 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { } if let Some(mut cpus) = cmd_parser .get_value::("cpus") - .map_err(|_| { - anyhow!(ConfigError::ConvertValueFailed( - String::from("u8"), - "cpus".to_string() - )) - })? + .with_context(|| ConfigError::ConvertValueFailed(String::from("u8"), "cpus".to_string()))? .map(|v| v.0.iter().map(|e| *e as u8).collect::>()) { cpus.sort_unstable(); diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index 61a40adc8..6361adf9f 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; use crate::config::ConfigError; use crate::config::{CmdParser, VmConfig}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use std::net::Ipv4Addr; /// Configuration of vnc. @@ -80,10 +80,10 @@ fn parse_port(vnc_config: &mut VncConfig, addr: String) -> Result<()> { } let ip = v[0] .parse::() - .map_err(|_| anyhow!("Invalid Ip param for vnc!"))?; + .with_context(|| "Invalid Ip param for vnc!")?; let base_port = v[1] .parse::() - .map_err(|_| anyhow!("Invalid Port param for vnc!"))?; + .with_context(|| "Invalid Port param for vnc!")?; // Prevent the base_port out of bounds. if !(0..=VNC_MAX_PORT_NUM - VNC_PORT_OFFSET).contains(&base_port) { return Err(anyhow!(ConfigError::InvalidParam( diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 1cc892cec..82c346349 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -14,6 +14,7 @@ //! //! Offer snapshot and migration interface for VM. +pub mod error; pub mod general; pub mod manager; pub mod migration; @@ -23,14 +24,13 @@ pub mod snapshot; use std::time::Duration; use std::{net::TcpStream, os::unix::net::UnixStream, thread}; -use anyhow::anyhow; pub use anyhow::Result; use log::error; + +pub use error::MigrationError; use machine_manager::qmp::{qmp_schema, Response}; pub use manager::{MigrationHook, MigrationManager}; pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; -pub mod error; -pub use error::MigrationError; /// Start to snapshot VM. /// @@ -40,7 +40,7 @@ pub use error::MigrationError; pub fn snapshot(path: String) -> Response { if let Err(e) = MigrationManager::save_snapshot(&path) { error!("Failed to migrate to path \'{:?}\': {:?}", path, e); - let _ = MigrationManager::set_status(MigrationStatus::Failed).map_err(|e| anyhow!("{}", e)); + let _ = MigrationManager::set_status(MigrationStatus::Failed); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index aa792aef9..50cb8b42d 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -81,7 +81,7 @@ impl OzoneHandler { if let Some(uid) = args.value_of("uid") { let user_id = (uid) .parse::() - .map_err(|_| anyhow!(OzoneError::DigitalParseError("uid", uid)))?; + .with_context(|| OzoneError::DigitalParseError("uid", uid))?; if user_id > MAX_ID_NUMBER { bail!("Input uid should be no more than 65535"); } @@ -90,7 +90,7 @@ impl OzoneHandler { if let Some(gid) = args.value_of("gid") { let group_id = (gid) .parse::() - .map_err(|_| anyhow!(OzoneError::DigitalParseError("gid", gid)))?; + .with_context(|| OzoneError::DigitalParseError("gid", gid))?; if group_id > MAX_ID_NUMBER { bail!("Input gid should be no more than 65535"); } @@ -113,7 +113,7 @@ impl OzoneHandler { handler.node = Some( (node) .parse::() - .map_err(|_| anyhow!(OzoneError::DigitalParseError("numa", node)))?, + .with_context(|| OzoneError::DigitalParseError("numa", node))?, ); } if let Some(config) = args.values_of("cgroup") { -- Gitee From 0244906a8aff88d7c1b1cf63d5992b740e2ab64f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 25 Mar 2023 08:41:28 +0800 Subject: [PATCH 0917/2187] Error: Simplify some map_err() anyhow! is not required when use ? operation to propagete error. Signed-off-by: Keqian Zhu --- address_space/src/state.rs | 12 +- .../src/interrupt_controller/aarch64/state.rs | 103 +++++------------- 2 files changed, 35 insertions(+), 80 deletions(-) diff --git a/address_space/src/state.rs b/address_space/src/state.rs index f693eba1a..1d9422a85 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -15,7 +15,7 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::sync::Arc; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use migration::{ error::MigrationError, DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, @@ -92,7 +92,7 @@ impl MigrationHook for AddressSpace { if let Some(base_addr) = region.start_addr() { region .read(fd, base_addr, 0, region.size()) - .map_err(|e| anyhow!(MigrationError::SaveVmMemoryErr(e.to_string())))?; + .map_err(|e| MigrationError::SaveVmMemoryErr(e.to_string()))?; } } @@ -124,14 +124,14 @@ impl MigrationHook for AddressSpace { false, false, ) - .map_err(|e| anyhow!(MigrationError::RestoreVmMemoryErr(e.to_string())))?, + .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?, ); self.root() .add_subregion( Region::init_ram_region(host_mmap.clone()), host_mmap.start_address().raw_value(), ) - .map_err(|e| anyhow!(MigrationError::RestoreVmMemoryErr(e.to_string())))?; + .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?; } Ok(()) @@ -139,14 +139,14 @@ impl MigrationHook for AddressSpace { fn send_memory(&self, fd: &mut dyn Write, range: MemBlock) -> Result<()> { self.read(fd, GuestAddress(range.gpa), range.len) - .map_err(|e| anyhow!(MigrationError::SendVmMemoryErr(e.to_string())))?; + .map_err(|e| MigrationError::SendVmMemoryErr(e.to_string()))?; Ok(()) } fn recv_memory(&self, fd: &mut dyn Read, range: MemBlock) -> Result<()> { self.write(fd, GuestAddress(range.gpa), range.len) - .map_err(|e| anyhow!(MigrationError::RecvVmMemoryErr(e.to_string())))?; + .map_err(|e| MigrationError::RecvVmMemoryErr(e.to_string()))?; Ok(()) } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 1d5a00289..c82198846 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -10,10 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::mem::size_of; - -use anyhow::{anyhow, Context}; +use anyhow::Context; use libc::c_uint; +use std::mem::size_of; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; @@ -578,48 +577,33 @@ impl StateTransfer for GICv3 { let mut state = GICv3State::default(); self.access_gic_redistributor(GICR_TYPER, 0, &mut state.redist_typer_l, false) - .map_err(|e| { - anyhow!(MigrationError::GetGicRegsError( - "redist_typer_l", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::GetGicRegsError("redist_typer_l", e.to_string()))?; self.access_gic_redistributor(GICR_TYPER + 4, 0, &mut state.redist_typer_h, false) - .map_err(|e| { - anyhow!(MigrationError::GetGicRegsError( - "redist_typer_h", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::GetGicRegsError("redist_typer_h", e.to_string()))?; self.access_gic_distributor(GICD_CTLR, &mut state.gicd_ctlr, false) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("gicd_ctlr", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("gicd_ctlr", e.to_string()))?; let plpis = (state.redist_typer_l & 1) != 0; for cpu in 0..self.vcpu_count { state.vcpu_redist[state.redist_len] = self .get_redist(cpu as usize, plpis) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("redist", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("redist", e.to_string()))?; state.redist_len += 1; } self.access_gic_distributor(GICD_STATUSR, &mut state.gicd_statusr, false) - .map_err(|e| { - anyhow!(MigrationError::GetGicRegsError( - "gicd_statusr", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::GetGicRegsError("gicd_statusr", e.to_string()))?; for irq in (GIC_IRQ_INTERNAL..self.nr_irqs).step_by(32) { state.irq_dist[state.dist_len] = self .get_dist(irq as u64) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("dist", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("dist", e.to_string()))?; state.dist_len += 1; } for cpu in 0..self.vcpu_count { state.vcpu_iccr[state.iccr_len] = self .get_cpu(cpu as usize) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("cpu", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("cpu", e.to_string()))?; state.iccr_len += 1; } @@ -633,48 +617,33 @@ impl StateTransfer for GICv3 { let mut regu32 = state.redist_typer_l; self.access_gic_redistributor(GICR_TYPER, 0, &mut regu32, false) - .map_err(|e| { - anyhow!(MigrationError::SetGicRegsError( - "gicr_typer_l", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::SetGicRegsError("gicr_typer_l", e.to_string()))?; let plpis: bool = regu32 & 1 != 0; regu32 = state.redist_typer_h; self.access_gic_redistributor(GICR_TYPER + 4, 0, &mut regu32, false) - .map_err(|e| { - anyhow!(MigrationError::SetGicRegsError( - "gicr_typer_h", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::SetGicRegsError("gicr_typer_h", e.to_string()))?; regu32 = state.gicd_ctlr; self.access_gic_distributor(GICD_CTLR, &mut regu32, true) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("gicd_ctlr", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("gicd_ctlr", e.to_string()))?; for gicv3_redist in state.vcpu_redist[0..state.redist_len].iter() { self.set_redist(*gicv3_redist, plpis) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("redist", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("redist", e.to_string()))?; } regu32 = state.gicd_statusr; self.access_gic_distributor(GICD_STATUSR, &mut regu32, true) - .map_err(|e| { - anyhow!(MigrationError::SetGicRegsError( - "gicd_statusr", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::SetGicRegsError("gicd_statusr", e.to_string()))?; for gicv3_dist in state.irq_dist[0..state.dist_len].iter() { self.set_dist(*gicv3_dist) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("dist", e.to_string())))? + .map_err(|e| MigrationError::SetGicRegsError("dist", e.to_string()))? } for gicv3_iccr in state.vcpu_iccr[0..state.iccr_len].iter() { self.set_cpu(*gicv3_iccr) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("cpu", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("cpu", e.to_string()))?; } Ok(()) @@ -711,25 +680,18 @@ impl StateTransfer for GICv3Its { let mut state = GICv3ItsState::default(); for i in 0..8 { self.access_gic_its(GITS_BASER + 8 * i as u32, &mut state.baser[i], false) - .map_err(|e| { - anyhow!(MigrationError::GetGicRegsError("Its baser", e.to_string())) - })?; + .map_err(|e| MigrationError::GetGicRegsError("Its baser", e.to_string()))?; } self.access_gic_its(GITS_CTLR, &mut state.ctlr, false) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its ctlr", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("Its ctlr", e.to_string()))?; self.access_gic_its(GITS_CBASER, &mut state.cbaser, false) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its cbaser", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("Its cbaser", e.to_string()))?; self.access_gic_its(GITS_CREADR, &mut state.creadr, false) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its creadr", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("Its creadr", e.to_string()))?; self.access_gic_its(GITS_CWRITER, &mut state.cwriter, false) - .map_err(|e| { - anyhow!(MigrationError::GetGicRegsError( - "Its cwriter", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::GetGicRegsError("Its cwriter", e.to_string()))?; self.access_gic_its(GITS_IIDR, &mut state.iidr, false) - .map_err(|e| anyhow!(MigrationError::GetGicRegsError("Its iidr", e.to_string())))?; + .map_err(|e| MigrationError::GetGicRegsError("Its iidr", e.to_string()))?; Ok(state.as_bytes().to_vec()) } @@ -741,31 +703,24 @@ impl StateTransfer for GICv3Its { .with_context(|| MigrationError::FromBytesError("GICv3Its"))?; self.access_gic_its(GITS_IIDR, &mut its_state.iidr, true) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its iidr", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("Its iidr", e.to_string()))?; // It must be written before GITS_CREADR, because GITS_CBASER write access will reset // GITS_CREADR. self.access_gic_its(GITS_CBASER, &mut its_state.cbaser, true) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its cbaser", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("Its cbaser", e.to_string()))?; self.access_gic_its(GITS_CREADR, &mut its_state.creadr, true) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its readr", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("Its readr", e.to_string()))?; self.access_gic_its(GITS_CWRITER, &mut its_state.cwriter, true) - .map_err(|e| { - anyhow!(MigrationError::SetGicRegsError( - "Its cwriter", - e.to_string() - )) - })?; + .map_err(|e| MigrationError::SetGicRegsError("Its cwriter", e.to_string()))?; for i in 0..8 { self.access_gic_its(GITS_BASER + 8 * i as u32, &mut its_state.baser[i], true) - .map_err(|e| { - anyhow!(MigrationError::SetGicRegsError("Its baser", e.to_string())) - })?; + .map_err(|e| MigrationError::SetGicRegsError("Its baser", e.to_string()))?; } self.access_gic_its_tables(false) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its table", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("Its table", e.to_string()))?; self.access_gic_its(GITS_CTLR, &mut its_state.ctlr, true) - .map_err(|e| anyhow!(MigrationError::SetGicRegsError("Its ctlr", e.to_string())))?; + .map_err(|e| MigrationError::SetGicRegsError("Its ctlr", e.to_string()))?; Ok(()) } -- Gitee From ca11de303cda56b3642e9aa2b56b45b2a9fe0ff3 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 24 Mar 2023 11:12:09 +0800 Subject: [PATCH 0918/2187] qmp: add qmp human-monitor-command Currently, stratovirt does not support "-blockdev", it will report unsupported "human-monitor-command" error when using libvirt to attach disk. So, add qmp humap-monitor-command. Signed-off-by: Yan Wang --- machine/src/standard_vm/mod.rs | 67 +++++++++++++++++++++++++++ machine_manager/src/config/drive.rs | 6 ++- machine_manager/src/machine.rs | 12 ++++- machine_manager/src/qmp/mod.rs | 3 +- machine_manager/src/qmp/qmp_schema.rs | 63 +++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 5 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6324d8924..a2bf71fd1 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1602,6 +1602,73 @@ impl DeviceInterface for StdMachine { ), } } + + fn human_monitor_command(&self, args: qmp_schema::HumanMonitorCmdArgument) -> Response { + let cmd_args: Vec<&str> = args.command_line.split(' ').collect(); + match cmd_args[0] { + "drive_add" => { + // The drive_add command has three arguments splited by space: + // "drive_add dummy file=/path/to/file,format=raw,if=none,id=drive-id..." + // The 'dummy' here is a placeholder for pci address which is not needed for drive. + if cmd_args.len() != 3 { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "Invalid number of arguments".to_string(), + ), + None, + ); + } + let drive_cfg = match self + .get_vm_config() + .lock() + .unwrap() + .add_block_drive(cmd_args[2]) + { + Ok(cfg) => cfg, + Err(ref e) => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + }; + if let Err(e) = self.register_drive_file( + &drive_cfg.path_on_host, + drive_cfg.read_only, + drive_cfg.direct, + ) { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + } + "drive_del" => { + // The drive_del command has two arguments splited by space: + // "drive_del drive-id" + if cmd_args.len() != 2 { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "Invalid number of arguments".to_string(), + ), + None, + ); + } + return self.blockdev_del(cmd_args[1].to_string()); + } + _ => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!( + "Unsupported command: {}", + cmd_args[0] + )), + None, + ); + } + } + Response::create_empty_response() + } } #[cfg(not(target_env = "musl"))] diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index c31d37501..bb7b157cb 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -495,7 +495,8 @@ impl VmConfig { Ok(()) } - fn add_block_drive(&mut self, block_config: &str) -> Result<()> { + /// Add block drive config to vm and return the added drive config. + pub fn add_block_drive(&mut self, block_config: &str) -> Result { let mut cmd_parser = CmdParser::new("drive"); cmd_parser .push("file") @@ -509,7 +510,8 @@ impl VmConfig { cmd_parser.parse(block_config)?; let drive_cfg = parse_drive(cmd_parser)?; - self.add_drive_with_config(drive_cfg) + self.add_drive_with_config(drive_cfg.clone())?; + Ok(drive_cfg) } /// Add drive config to vm config. diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 2eb36d6ca..c765dd899 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -19,8 +19,9 @@ use strum::VariantNames; use crate::config::ShutdownAction; use crate::qmp::qmp_schema::{ BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, DeviceAddArgument, - DeviceProps, Events, GicCap, IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, - NetDevAddArgument, PropList, QmpCommand, QmpEvent, Target, TypeLists, UpdateRegionArgument, + DeviceProps, Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, MachineInfo, + MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, Target, + TypeLists, UpdateRegionArgument, }; use crate::qmp::{Response, Version}; @@ -436,6 +437,13 @@ pub trait DeviceInterface { fn input_event(&self, _k: String, _v: String) -> Response { Response::create_empty_response() } + + fn human_monitor_command(&self, _args: HumanMonitorCmdArgument) -> Response { + Response::create_error_response( + QmpErrorClass::GenericError("human-monitor-command is not supported yet".to_string()), + None, + ) + } } /// Migrate external api diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 3dc44b611..542cbb25c 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -466,7 +466,8 @@ fn qmp_command_exec( (blockdev_add, blockdev_add), (netdev_add, netdev_add), (chardev_add, chardev_add), - (update_region, update_region) + (update_region, update_region), + (human_monitor_command, human_monitor_command) ); // Handle the Qmp command which macro can't cover diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index e62976e14..a3a436474 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -399,6 +399,12 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "human-monitor-command")] + human_monitor_command { + arguments: human_monitor_command, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, } /// qmp_capabilities @@ -2141,6 +2147,28 @@ impl Command for input_event { } } +/// human-monitor-command +/// +/// # Arguments +/// +/// * `command_line` - the command line will be executed. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "human-monitor-command", +/// "arguments": { "command-line": "drive_add dummy +/// file=/path/to/file,format=raw,if=none,id=drive-id" }} +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct human_monitor_command { + #[serde(rename = "command-line")] + pub command_line: String, +} +pub type HumanMonitorCmdArgument = human_monitor_command; + #[cfg(test)] mod tests { use super::*; @@ -2686,4 +2714,39 @@ mod tests { let part_msg = r#"ok"#; assert!(err_msg.contains(part_msg)); } + + #[test] + fn test_qmp_human_monitor_command() { + // Normal test. + let json_msg = r#" + { + "execute": "human-monitor-command" , + "arguments": { + "command-line": "drive_add dummy file=/path/to/file,format=raw,if=none,id=drive-id" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"ok"#; + assert!(err_msg.contains(part_msg)); + + // Abnormal test with invalid arguments. + let json_msg = r#" + { + "execute": "human-monitor-command" , + "arguments": { + "invalid_key": "invalid_value" + } + } + "#; + let err_msg = match serde_json::from_str::(json_msg) { + Ok(_) => "ok".to_string(), + Err(e) => e.to_string(), + }; + let part_msg = r#"unknown field `invalid_key`, expected `command-line`"#; + assert!(err_msg.contains(part_msg)); + } } -- Gitee From f0bc7b98eb2b6932478f38ae6716cce6573a2c7b Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 21 Mar 2023 17:30:35 +0800 Subject: [PATCH 0919/2187] balloon: add auto-balloon feature use auto-balloon feature to reduce memory footprint Signed-off-by: jiewangqun --- machine_manager/src/config/balloon.rs | 57 ++++++- tests/mod_test/tests/balloon_test.rs | 218 +++++++++++++++++++++++--- virtio/src/balloon.rs | 173 +++++++++++++++++++- 3 files changed, 417 insertions(+), 31 deletions(-) diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index ff411f174..ca4f4f7d2 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -16,11 +16,21 @@ use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check, ConfigCheck, MAX_STRING_LENGTH}; use crate::config::{CmdParser, ExBool, VmConfig}; +const MEM_BUFFER_PERCENT_MIN: u32 = 20; +const MEM_BUFFER_PERCENT_MAX: u32 = 80; +const MEM_BUFFER_PERCENT_DEFAULT: u32 = 50; +const MONITOR_INTERVAL_SECOND_MIN: u32 = 5; +const MONITOR_INTERVAL_SECOND_MAX: u32 = 300; +const MONITOR_INTERVAL_SECOND_DEFAULT: u32 = 10; + #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct BalloonConfig { pub id: String, pub deflate_on_oom: bool, pub free_page_reporting: bool, + pub auto_balloon: bool, + pub membuf_percent: u32, + pub monitor_interval: u32, } impl ConfigCheck for BalloonConfig { @@ -31,7 +41,31 @@ impl ConfigCheck for BalloonConfig { MAX_STRING_LENGTH, ))); } - + if !self.auto_balloon { + return Ok(()); + } + if self.membuf_percent > MEM_BUFFER_PERCENT_MAX + || self.membuf_percent < MEM_BUFFER_PERCENT_MIN + { + return Err(anyhow!(ConfigError::IllegalValue( + "balloon membuf-percent".to_string(), + MEM_BUFFER_PERCENT_MIN as u64, + false, + MEM_BUFFER_PERCENT_MAX as u64, + false, + ))); + } + if self.monitor_interval > MONITOR_INTERVAL_SECOND_MAX + || self.monitor_interval < MONITOR_INTERVAL_SECOND_MIN + { + return Err(anyhow!(ConfigError::IllegalValue( + "balloon monitor-interval".to_string(), + MONITOR_INTERVAL_SECOND_MIN as u64, + false, + MONITOR_INTERVAL_SECOND_MAX as u64, + false, + ))); + } Ok(()) } } @@ -48,11 +82,19 @@ pub fn parse_balloon(vm_config: &mut VmConfig, balloon_config: &str) -> Result("deflate-on-oom")? { balloon.deflate_on_oom = default.into(); } @@ -62,6 +104,15 @@ pub fn parse_balloon(vm_config: &mut VmConfig, balloon_config: &str) -> Result("id")? { balloon.id = id; } + if let Some(default) = cmd_parser.get_value::("auto-balloon")? { + balloon.auto_balloon = default.into(); + } + if let Some(membuf_percent) = cmd_parser.get_value::("membuf-percent")? { + balloon.membuf_percent = membuf_percent; + } + if let Some(monitor_interval) = cmd_parser.get_value::("monitor-interval")? { + balloon.monitor_interval = monitor_interval; + } balloon.check()?; vm_config.dev_name.insert("balloon".to_string(), 1); Ok(balloon) diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 844bc1ac2..8b57a1a54 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -22,7 +22,7 @@ use std::io::{self, BufRead, BufReader}; use std::process::Command; use std::rc::Rc; use std::{thread, time}; -use util::offset_of; +use util::{byte_code::ByteCode, offset_of}; const BALLOON_F_DEFLATE_ON_OOM_TEST: u32 = 2; const BALLOON_F_PRPORTING_TEST: u32 = 5; @@ -30,6 +30,8 @@ const BALLOON_F_VERSION1_TEST: u64 = 32; const PAGE_SIZE_UNIT: u64 = 4096; const TIMEOUT_US: u64 = 15 * 1000 * 1000; const MBSIZE: u64 = 1024 * 1024; +const MEM_BUFFER_PERCENT_DEFAULT: u32 = 50; +const MONITOR_INTERVAL_SECOND_DEFAULT: u32 = 10; fn read_lines(filename: String) -> io::Lines> { let file = File::open(filename).unwrap(); @@ -63,13 +65,28 @@ pub struct VirtioBalloonTest { pub inf_queue: Rc>, pub def_queue: Rc>, pub fpr_queue: Option>>, + pub auto_queue: Option>>, +} + +pub struct BalloonTestCfg { + pub fpr: bool, + pub auto_balloon: bool, + pub percent: u32, + pub interval: u32, } impl VirtioBalloonTest { - pub fn new(memsize: u64, page_size: u64, shared: bool, fpr: bool, huge: bool) -> Self { + pub fn new( + memsize: u64, + page_size: u64, + shared: bool, + huge: bool, + cfg: BalloonTestCfg, + ) -> Self { let pci_slot: u8 = 0x4; let mut extra_args: Vec<&str> = Vec::new(); let mut fpr_switch = String::from("false"); + let mut auto_switch = String::from("false"); let mem_path = format!("-mem-path /tmp/stratovirt/hugepages"); let mut args: Vec<&str> = "-machine".split(' ').collect(); @@ -89,12 +106,15 @@ impl VirtioBalloonTest { extra_args.append(&mut args); } - if fpr { + if cfg.fpr { fpr_switch = String::from("true"); } + if cfg.auto_balloon { + auto_switch = String::from("true"); + } let dev_args = format!( - "-device {},id=drv0,bus=pcie.0,addr={}.0,free-page-reporting={}", - "virtio-balloon-pci", pci_slot, fpr_switch + "-device virtio-balloon-pci,id=drv0,bus=pcie.0,addr={}.0,free-page-reporting={},auto-balloon={},membuf-percent={},monitor-interval={}", + pci_slot, fpr_switch, auto_switch, cfg.percent, cfg.interval ); args = dev_args[..].split(' ').collect(); extra_args.append(&mut args); @@ -110,19 +130,26 @@ impl VirtioBalloonTest { let inf_queue; let def_queue; let mut fpr_queue = None; - if fpr { - let ques = - dev.borrow_mut() - .init_device(test_state.clone(), allocator.clone(), features, 3); - inf_queue = ques[0].clone(); - def_queue = ques[1].clone(); - fpr_queue = Some(ques[2].clone()); - } else { - let ques = - dev.borrow_mut() - .init_device(test_state.clone(), allocator.clone(), features, 2); - inf_queue = ques[0].clone(); - def_queue = ques[1].clone(); + let mut auto_queue = None; + let mut que_num = 2_usize; + let mut idx = 2_usize; + if cfg.fpr { + que_num += 1; + } + if cfg.auto_balloon { + que_num += 1; + } + let ques = + dev.borrow_mut() + .init_device(test_state.clone(), allocator.clone(), features, que_num); + inf_queue = ques[0].clone(); + def_queue = ques[1].clone(); + if cfg.fpr { + fpr_queue = Some(ques[idx].clone()); + idx += 1; + } + if cfg.auto_balloon { + auto_queue = Some(ques[idx].clone()); } VirtioBalloonTest { @@ -132,6 +159,7 @@ impl VirtioBalloonTest { inf_queue, def_queue, fpr_queue, + auto_queue, } } } @@ -139,7 +167,13 @@ impl VirtioBalloonTest { fn inflate_fun(shared: bool) { let page_num = 255_i32; let mut idx = 0_i32; - let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, false); + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, cfg); let free_page = balloon .allocator @@ -212,7 +246,13 @@ fn inflate_fun(shared: bool) { fn balloon_fun(shared: bool, huge: bool) { let page_num = 255_u32; let mut idx = 0_u32; - let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, huge); + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, huge, cfg); let free_page = balloon .allocator @@ -534,7 +574,13 @@ fn balloon_feature_002() { fn balloon_fpr_fun(shared: bool) { let page_num = 255_u32; let mut idx = 0_u32; - let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, true, false); + let cfg = BalloonTestCfg { + fpr: true, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, shared, false, cfg); let free_page = balloon .allocator @@ -629,17 +675,33 @@ fn balloon_fpr_001() { fn balloon_fpr_002() { balloon_fpr_fun(false); } - +#[allow(dead_code)] struct VirtioBalloonConfig { /// The target page numbers of balloon device. pub num_pages: u32, /// Number of pages we've actually got in balloon device. pub actual: u32, + pub _reserved: u32, + pub _reserved1: u32, + /// Buffer percent is a percentage of memory actually needed by + /// the applications and services running inside the virtual machine. + /// This parameter takes effect only when VIRTIO_BALLOON_F_MESSAGE_VQ is supported. + /// Recommended value range: [20, 80] and default is 50. + pub membuf_percent: u32, + /// Monitor interval host wants to adjust VM memory size. + /// Recommended value range: [5, 300] and default is 10. + pub monitor_interval: u32, } #[test] fn query() { - let balloon = VirtioBalloonTest::new(2048, PAGE_SIZE_UNIT, false, false, false); + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(2048, PAGE_SIZE_UNIT, false, false, cfg); let ret = balloon .state .borrow_mut() @@ -662,7 +724,13 @@ fn query() { /// 1/2/3.Success #[test] fn balloon_config_001() { - let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, false); + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, cfg); balloon .state @@ -727,7 +795,13 @@ fn balloon_config_002() { if size_kb < 1024 * 1024 { return; } - let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, true); + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, true, cfg); balloon .state @@ -787,7 +861,13 @@ fn balloon_config_002() { /// 1/2.Success #[test] fn balloon_deactive_001() { - let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, false); + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: false, + percent: 0, + interval: 0, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, cfg); let bar = balloon.device.borrow().bar; let common_base = balloon.device.borrow().common_base as u64; @@ -811,3 +891,89 @@ fn balloon_deactive_001() { ); balloon.state.borrow_mut().stop(); } + +#[derive(Clone, Copy, Default)] +#[allow(dead_code)] +#[repr(packed(1))] +struct BalloonStat { + tag: u16, + val: u64, +} +impl ByteCode for BalloonStat {} +/// balloon device deactive config test +/// TestStep: +/// 1.Init device +/// 2.geust send msg to host by auto balloon +/// Expect: +/// 1/2.Success +#[test] +fn auto_balloon_test_001() { + let cfg = BalloonTestCfg { + fpr: false, + auto_balloon: true, + percent: MEM_BUFFER_PERCENT_DEFAULT, + interval: MONITOR_INTERVAL_SECOND_DEFAULT, + }; + let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, cfg); + + let num_pages = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, num_pages) as u64); + assert_eq!(num_pages, 0); + let percent = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, membuf_percent) as u64); + assert_eq!(percent, MEM_BUFFER_PERCENT_DEFAULT); + let interval = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, monitor_interval) as u64); + assert_eq!(interval, MONITOR_INTERVAL_SECOND_DEFAULT); + + let stat = BalloonStat { + tag: 0, + val: 131070, + }; + let msg_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); + balloon + .state + .borrow_mut() + .memwrite(msg_addr, &stat.as_bytes()); + + let auto_queue = balloon.auto_queue.unwrap(); + + let free_head = auto_queue.borrow_mut().add( + balloon.state.clone(), + msg_addr, + std::mem::size_of::() as u32, + false, + ); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), auto_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + auto_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + let num_pages = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, num_pages) as u64); + assert_eq!(num_pages, 131070); + balloon + .device + .borrow_mut() + .config_writel(offset_of!(VirtioBalloonConfig, actual) as u64, 131070); + let actual = balloon + .device + .borrow_mut() + .config_readl(offset_of!(VirtioBalloonConfig, actual) as u64); + assert_eq!(actual, 131070); +} diff --git a/virtio/src/balloon.rs b/virtio/src/balloon.rs index 9772b3157..46d06a776 100644 --- a/virtio/src/balloon.rs +++ b/virtio/src/balloon.rs @@ -40,6 +40,7 @@ use util::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }, num_ops::{read_u32, round_down}, + offset_of, seccomp::BpfRule, unix::host_page_size, }; @@ -52,6 +53,8 @@ use super::{ const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; const VIRTIO_BALLOON_F_REPORTING: u32 = 5; +/// The feature for Auto-balloon +const VIRTIO_BALLOON_F_MESSAGE_VQ: u32 = 6; const VIRTIO_BALLOON_PFN_SHIFT: u32 = 12; const QUEUE_NUM_BALLOON: usize = 2; const BALLOON_PAGE_SIZE: u64 = 1 << VIRTIO_BALLOON_PFN_SHIFT; @@ -72,6 +75,14 @@ struct GuestIovec { iov_len: u64, } +#[derive(Clone, Copy, Default)] +#[allow(dead_code)] +#[repr(packed(1))] +struct BalloonStat { + _tag: u16, + val: u64, +} + /// Balloon configuration, which would be used to transport data between `Guest` and `Host`. #[derive(Copy, Clone, Default)] #[allow(dead_code)] @@ -80,8 +91,19 @@ struct VirtioBalloonConfig { pub num_pages: u32, /// Number of pages we've actually got in balloon device. pub actual: u32, + pub _reserved: u32, + pub _reserved1: u32, + /// Buffer percent is a percentage of memory actually needed by + /// the applications and services running inside the virtual machine. + /// This parameter takes effect only when VIRTIO_BALLOON_F_MESSAGE_VQ is supported. + /// Recommended value range: [20, 80] and default is 50. + pub membuf_percent: u32, + /// Monitor interval(second) host wants to adjust VM memory size. + /// Recommended value range: [5, 300] and default is 10. + pub monitor_interval: u32, } +impl ByteCode for BalloonStat {} impl ByteCode for GuestIovec {} impl ByteCode for VirtioBalloonConfig {} @@ -234,7 +256,7 @@ impl Request { let hva = match mem.lock().unwrap().get_host_address(gpa) { Some(addr) => addr, None => { - error!("Can not get host address, gpa: {}", gpa.raw_value()); + // Windows OS will populate the address with PA of 0 continue; } }; @@ -516,6 +538,10 @@ struct BalloonIoHandler { report_queue: Option>>, /// Reporting EventFd. report_evt: Option>, + /// Auto balloon msg queue. + msg_queue: Option>>, + /// Auto balloon msg EventFd. + msg_evt: Option>, /// Device is broken or not. device_broken: Arc, /// The interrupt call back function. @@ -612,6 +638,57 @@ impl BalloonIoHandler { Ok(()) } + fn auto_msg_evt_handler(&mut self) -> Result<()> { + let queue = &self.msg_queue; + if queue.is_none() { + return Err(anyhow!(VirtioError::VirtQueueIsNone)); + } + + let unwraped_queue = queue.as_ref().unwrap(); + let mut locked_queue = unwraped_queue.lock().unwrap(); + + loop { + let elem = locked_queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + .with_context(|| "Failed to pop avail ring")?; + + if elem.desc_num == 0 { + break; + } + let req = Request::parse(&elem, OUT_IOVEC) + .with_context(|| "Fail to parse available descriptor chain")?; + if let Some(dev) = unsafe { &BALLOON_DEV } { + let mut balloon_dev = dev.lock().unwrap(); + for iov in req.iovec.iter() { + if let Some(stat) = iov_to_buf::(&self.mem_space, iov, 0) { + let ram_size = (balloon_dev.mem_info.lock().unwrap().get_ram_size() + >> VIRTIO_BALLOON_PFN_SHIFT) + as u32; + balloon_dev.set_num_pages(cmp::min(stat.val as u32, ram_size)); + } + } + balloon_dev + .signal_config_change() + .with_context(|| "Failed to notify guest")?; + } + + locked_queue + .vring + .add_used(&self.mem_space, req.desc_index, req.elem_cnt) + .with_context(|| "Failed to add balloon response into used queue")?; + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) + .with_context(|| { + anyhow!(VirtioError::InterruptTrigger( + "balloon", + VirtioInterruptType::Vring + )) + })?; + } + + Ok(()) + } + /// Send balloon changed event. fn send_balloon_changed_event(&self) { let ram_size = self.mem_info.lock().unwrap().get_ram_size(); @@ -718,6 +795,27 @@ impl EventNotifierHelper for BalloonIoHandler { notifiers.push(build_event_notifier(report_evt.as_raw_fd(), handler)); } + if let Some(msg_evt) = locked_balloon_io.msg_evt.as_ref() { + let cloned_balloon_io = balloon_io.clone(); + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let mut locked_balloon_io = cloned_balloon_io.lock().unwrap(); + if locked_balloon_io.device_broken.load(Ordering::SeqCst) { + return None; + } + if let Err(e) = locked_balloon_io.auto_msg_evt_handler() { + error!("Failed to msg: {:?}", e); + report_virtio_error( + locked_balloon_io.interrupt_cb.clone(), + locked_balloon_io.driver_features, + &locked_balloon_io.device_broken, + ); + } + None + }); + notifiers.push(build_event_notifier(msg_evt.as_raw_fd(), handler)); + } + // register event notifier for timer event. let cloned_balloon_io = balloon_io.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { @@ -765,6 +863,9 @@ pub struct Balloon { deactivate_evts: Vec, /// Device is broken or not. broken: Arc, + /// For auto balloon + membuf_percent: u32, + monitor_interval: u32, } impl Balloon { @@ -781,6 +882,9 @@ impl Balloon { if bln_cfg.free_page_reporting { device_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING; } + if bln_cfg.auto_balloon { + device_features |= 1u64 << VIRTIO_BALLOON_F_MESSAGE_VQ; + } Balloon { device_features, @@ -793,6 +897,8 @@ impl Balloon { event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), + membuf_percent: bln_cfg.membuf_percent, + monitor_interval: bln_cfg.monitor_interval, } } @@ -858,6 +964,9 @@ impl Balloon { pub fn get_guest_memory_size(&self) -> u64 { self.mem_info.lock().unwrap().get_ram_size() - self.get_balloon_memory_size() } + pub fn set_num_pages(&mut self, target: u32) { + self.num_pages = target; + } } impl VirtioDevice for Balloon { @@ -880,6 +989,9 @@ impl VirtioDevice for Balloon { if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { queue_num += 1; } + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + queue_num += 1; + } queue_num } @@ -918,9 +1030,18 @@ impl VirtioDevice for Balloon { let new_config = VirtioBalloonConfig { num_pages: self.num_pages, actual: self.actual.load(Ordering::Acquire), + _reserved: 0_u32, + _reserved1: 0_u32, + membuf_percent: self.membuf_percent, + monitor_interval: self.monitor_interval, + }; + + let config_len = if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + size_of::() as u64 + } else { + offset_of!(VirtioBalloonConfig, _reserved) as u64 }; - let config_len = size_of::() as u64; let data_len = data.len() as u64; if offset >= config_len { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); @@ -1015,6 +1136,26 @@ impl VirtioDevice for Balloon { (None, None) }; + let (msg_queue, msg_evt) = + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) + && current_queue_index + 1 < self.queue_num() + && !queue_evts.is_empty() + { + current_queue_index += 1; + ( + Some(queues[current_queue_index].clone()), + Some(queue_evts.remove(0)), + ) + } else { + if current_queue_index + 1 >= self.queue_num() { + error!( + "Queue index: {} is invalid, correct index is from 0 to {}!", + current_queue_index + 1, + self.queue_num() + ) + } + (None, None) + }; self.interrupt_cb = Some(interrupt_cb.clone()); let handler = BalloonIoHandler { driver_features: self.driver_features, @@ -1025,6 +1166,8 @@ impl VirtioDevice for Balloon { def_evt, report_queue, report_evt, + msg_queue, + msg_evt, device_broken: self.broken.clone(), interrupt_cb, mem_info: self.mem_info.clone(), @@ -1140,6 +1283,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mem_space = address_space_init(); @@ -1186,6 +1332,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mem_space = address_space_init(); @@ -1205,6 +1354,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mem_space = address_space_init(); @@ -1224,6 +1376,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mem_space = address_space_init(); @@ -1242,6 +1397,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mem_space = address_space_init(); @@ -1260,6 +1418,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mut bln = Balloon::new(&bln_cfg, mem_space.clone(), false); bln.realize().unwrap(); @@ -1335,6 +1496,8 @@ mod tests { def_evt: event_def, report_queue: None, report_evt: None, + msg_queue: None, + msg_evt: None, device_broken: bln.broken.clone(), interrupt_cb: cb.clone(), mem_info: bln.mem_info.clone(), @@ -1439,6 +1602,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: Default::default(), + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mut bln = Balloon::new(&bln_cfg, mem_space.clone(), false); assert!(bln @@ -1503,6 +1669,9 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: true, + auto_balloon: false, + membuf_percent: 0, + monitor_interval: 0, }; let mem_space = address_space_init(); let mut bln = Balloon::new(&bln_cfg, mem_space, false); -- Gitee From 1575f86460c92bd8fdcbee6920b08836b16ab7a8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 1 Mar 2023 17:46:51 +0800 Subject: [PATCH 0920/2187] scream: add ivshmem device Ivshmem device provides a shared memory channel for the scream sound card. In addition, windows does not require the PCI bar space to be 4Kb aligned. Signed-off-by: Mingwang Li --- devices/Cargo.toml | 1 + devices/src/lib.rs | 1 + devices/src/misc/ivshmem.rs | 164 +++++++++++++++++++ devices/src/misc/mod.rs | 14 ++ devices/src/misc/scream/mod.rs | 65 ++++++++ docs/config_guidebook.md | 21 ++- machine/src/lib.rs | 32 ++++ machine_manager/src/config/machine_config.rs | 70 +++++--- machine_manager/src/config/mod.rs | 7 +- machine_manager/src/config/scream.rs | 32 ++++ pci/src/config.rs | 13 +- virtio/src/transport/virtio_pci.rs | 6 +- 12 files changed, 385 insertions(+), 41 deletions(-) create mode 100644 devices/src/misc/ivshmem.rs create mode 100644 devices/src/misc/mod.rs create mode 100644 devices/src/misc/scream/mod.rs create mode 100644 machine_manager/src/config/scream.rs diff --git a/devices/Cargo.toml b/devices/Cargo.toml index c31ca8736..12dd3684d 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -30,6 +30,7 @@ acpi = { path = "../acpi" } [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } +pci = { path = "../pci" } [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/lib.rs b/devices/src/lib.rs index f89326f96..e129547c0 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -19,6 +19,7 @@ pub mod acpi; mod interrupt_controller; pub mod legacy; +pub mod misc; pub mod usb; #[cfg(target_arch = "aarch64")] diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs new file mode 100644 index 000000000..89e9ea1fb --- /dev/null +++ b/devices/src/misc/ivshmem.rs @@ -0,0 +1,164 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{ + atomic::{AtomicU16, Ordering}, + Arc, Mutex, Weak, +}; + +use anyhow::bail; + +use address_space::{GuestAddress, Region, RegionOps}; +use pci::{ + config::{ + PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, + PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, + }, + le_write_u16, PciBus, PciDevOps, +}; + +const PCI_VENDOR_ID_IVSHMEM: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; +const PCI_DEVICE_ID_IVSHMEM: u16 = 0x1110; +const PCI_REVIRSION_ID_IVSHMEM: u8 = 1; + +const PCI_BAR_MAX_IVSHMEM: u8 = 3; + +const IVSHMEM_REG_BAR_SIZE: u64 = 0x100; + +/// Intel-VM shared memory device structure. +pub struct Ivshmem { + config: PciConfig, + devfn: u8, + dev_id: Arc, + name: String, + parent_bus: Weak>, + ram_mem_region: Region, +} + +impl Ivshmem { + pub fn new( + name: String, + devfn: u8, + parent_bus: Weak>, + ram_mem_region: Region, + ) -> Self { + Self { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), + devfn, + dev_id: Arc::new(AtomicU16::new(0)), + name, + parent_bus, + ram_mem_region, + } + } + + fn register_bars(&mut self) -> pci::Result<()> { + // Currently, ivshmem uses only the shared memory and does not use interrupt. + // Therefore, bar0 read and write callback is not implemented. + let reg_read = move |_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }; + let reg_write = move |_: &[u8], _: GuestAddress, _: u64| -> bool { true }; + let reg_region_ops = RegionOps { + read: Arc::new(reg_read), + write: Arc::new(reg_write), + }; + + // bar0: mmio register + self.config.register_bar( + 0, + Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops), + RegionType::Mem64Bit, + false, + IVSHMEM_REG_BAR_SIZE, + )?; + + // bar2: ram + self.config.register_bar( + 2, + self.ram_mem_region.clone(), + RegionType::Mem64Bit, + true, + self.ram_mem_region.size(), + ) + } +} + +impl PciDevOps for Ivshmem { + fn realize(mut self) -> pci::Result<()> { + self.init_write_mask()?; + self.init_write_clear_mask()?; + le_write_u16( + &mut self.config.config, + VENDOR_ID as usize, + PCI_VENDOR_ID_IVSHMEM, + )?; + le_write_u16( + &mut self.config.config, + DEVICE_ID as usize, + PCI_DEVICE_ID_IVSHMEM, + )?; + self.config.config[REVISION_ID] = PCI_REVIRSION_ID_IVSHMEM; + + le_write_u16( + &mut self.config.config, + SUB_CLASS_CODE as usize, + PCI_CLASS_MEMORY_RAM, + )?; + + self.register_bars()?; + + // Attach to the PCI bus. + let pci_bus = self.parent_bus.upgrade().unwrap(); + let mut locked_pci_bus = pci_bus.lock().unwrap(); + let pci_device = locked_pci_bus.devices.get(&self.devfn); + match pci_device { + Some(device) => bail!( + "Devfn {:?} has been used by {:?}", + &self.devfn, + device.lock().unwrap().name() + ), + None => locked_pci_bus + .devices + .insert(self.devfn, Arc::new(Mutex::new(self))), + }; + Ok(()) + } + + fn init_write_mask(&mut self) -> pci::Result<()> { + self.config.init_common_write_mask() + } + + fn init_write_clear_mask(&mut self) -> pci::Result<()> { + self.config.init_common_write_clear_mask() + } + + fn read_config(&mut self, offset: usize, data: &mut [u8]) { + self.config.read(offset, data); + } + + fn write_config(&mut self, offset: usize, data: &[u8]) { + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + + self.config.write( + offset, + data, + self.dev_id.load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), + ); + } + + fn name(&self) -> String { + self.name.clone() + } +} diff --git a/devices/src/misc/mod.rs b/devices/src/misc/mod.rs new file mode 100644 index 000000000..3b0de4b73 --- /dev/null +++ b/devices/src/misc/mod.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod ivshmem; +pub mod scream; diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs new file mode 100644 index 000000000..04f79b27d --- /dev/null +++ b/devices/src/misc/scream/mod.rs @@ -0,0 +1,65 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + sync::{Arc, Mutex, Weak}, + thread, +}; + +use address_space::{GuestAddress, HostMemMapping, Region}; +use anyhow::{Context, Result}; +use core::time; + +use super::ivshmem::Ivshmem; +use pci::{PciBus, PciDevOps}; + +/// Scream sound card device structure. +pub struct Scream { + hva: u64, + size: u64, +} + +impl Scream { + pub fn new(size: u64) -> Self { + Self { hva: 0, size } + } + + fn start(&self) -> Result<()> { + thread::Builder::new() + .name("scream audio worker".to_string()) + .spawn(move || loop { + thread::sleep(time::Duration::from_millis(50)); + }) + .with_context(|| "Failed to create thread scream")?; + Ok(()) + } + + pub fn realize(mut self, devfn: u8, parent_bus: Weak>) -> Result<()> { + let host_mmap = Arc::new(HostMemMapping::new( + GuestAddress(0), + None, + self.size, + None, + false, + true, + false, + )?); + self.hva = host_mmap.host_address(); + + let mem_region = Region::init_ram_region(host_mmap); + + let ivshmem = Ivshmem::new("ivshmem".to_string(), devfn, parent_bus, mem_region); + ivshmem.realize()?; + + self.start() + } +} diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index bd1dd4dd0..eb8c7112c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -134,7 +134,7 @@ Each NUMA node is given a list of command lines option, there will be described 1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] It describes the size and id of each memory zone, the policy of binding to host memory node. you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. - The optional policies are default, preferred, bind and interleave. + The optional policies are default, preferred, bind and interleave. If it is not configured, `default` is used. 2. -numa node,cpus=0-1,memdev=mem0 It describes id and cpu set of the NUMA node, and the id belongs to which memory zone. 3. -numa dist,src=0,dst=0,val=10 @@ -930,6 +930,25 @@ Note: 1. Only virtio-gpu 2D supported. 2. Live migration is not supported. +### 2.21 ivshmem-scream + +ivshmem-scream is a virtual sound card that relies on Intel-VM shared memory to transmit audio data. + +Six properties are supported for ivshmem-scream device. +* id: unique device id. +* memdev: configuration of the back-end memory device used by the ivshmem. +* bus: bus number of the device. +* addr: including slot number and function number. +* share: the shared memory must be set to `on`. +* size: size of th shared memory, 2M is suggested. + +Sample Configuration: + +```shell +-device ivshmem-scream,id=,memdev=,bus=pcie.0,addr=0x2.0x0 +-object memory-backend-ram,id=,share=on,size=2M +``` + ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 956849b67..d26197554 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -25,7 +25,9 @@ use std::os::unix::net::UnixListener; use std::path::Path; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; +use devices::misc::scream::Scream; use log::warn; +use machine_manager::config::scream::parse_scream; use util::file::{lock_file, unlock_file}; pub use micro_vm::LightMachine; @@ -1079,6 +1081,33 @@ pub trait MachineOps { Ok(()) } + /// Add scream sound based on ivshmem. + /// + /// # Arguments + /// + /// * `cfg_args` - scream configuration. + fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + + let memdev = + parse_scream(cfg_args).with_context(|| "Failed to parse cmdline for ivshmem")?; + + let mem_cfg = + vm_config.object.mem_object.remove(&memdev).ok_or_else(|| { + anyhow!("Object for memory-backend-ram {} config not found", memdev) + })?; + + if !mem_cfg.share { + bail!("Object for share config is not on"); + } + + let scream = Scream::new(mem_cfg.size); + scream + .realize(devfn, parent_bus) + .with_context(|| "Failed to realize scream device") + } + /// Get the corresponding device from the PCI bus based on the device name. /// /// # Arguments @@ -1268,6 +1297,9 @@ pub trait MachineOps { "pcie-demo-dev" => { self.add_demo_dev(vm_config, cfg_args)?; } + "ivshmem-scream" => { + self.add_ivshmem_scream(vm_config, cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 0eb5fcf50..fd520f0db 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -86,6 +86,7 @@ pub struct MemZoneConfig { pub size: u64, pub host_numa_nodes: Option>, pub policy: String, + pub share: bool, } impl Default for MemZoneConfig { @@ -95,6 +96,7 @@ impl Default for MemZoneConfig { size: 0, host_numa_nodes: None, policy: String::from("bind"), + share: false, } } } @@ -470,26 +472,34 @@ impl VmConfig { } Ok(Some(host_nodes)) } else { - Err(anyhow!(ConfigError::FieldIsMissing( - "host-nodes", - "memory-backend-ram" - ))) + Ok(None) } } fn get_mem_zone_policy(&self, cmd_parser: &CmdParser) -> Result { - if let Some(policy) = cmd_parser.get_value::("policy")? { - if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { - return Err(anyhow!(ConfigError::InvalidParam( - "policy".to_string(), - policy - ))); - } - Ok(policy) + let policy = cmd_parser + .get_value::("policy")? + .unwrap_or_else(|| "default".to_string()); + if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { + return Err(anyhow!(ConfigError::InvalidParam( + "policy".to_string(), + policy + ))); + } + Ok(policy) + } + + fn get_mem_share(&self, cmd_parser: &CmdParser) -> Result { + let share = cmd_parser + .get_value::("share")? + .unwrap_or_else(|| "off".to_string()); + + if share.eq("on") || share.eq("off") { + Ok(share.eq("on")) } else { - Err(anyhow!(ConfigError::FieldIsMissing( - "policy", - "memory-backend-ram" + Err(anyhow!(ConfigError::InvalidParam( + "share".to_string(), + share ))) } } @@ -506,7 +516,8 @@ impl VmConfig { .push("id") .push("size") .push("host-nodes") - .push("policy"); + .push("policy") + .push("share"); cmd_parser.parse(mem_zone)?; let zone_config = MemZoneConfig { @@ -514,8 +525,13 @@ impl VmConfig { size: self.get_mem_zone_size(&cmd_parser)?, host_numa_nodes: self.get_mem_zone_host_nodes(&cmd_parser)?, policy: self.get_mem_zone_policy(&cmd_parser)?, + share: self.get_mem_share(&cmd_parser)?, }; + if zone_config.host_numa_nodes.is_none() { + return Ok(zone_config); + } + if self.machine_config.mem_config.mem_zones.is_some() { self.machine_config .mem_config @@ -1001,23 +1017,23 @@ mod tests { assert_eq!(zone_config_1.host_numa_nodes, Some(vec![1])); assert_eq!(zone_config_1.policy, "bind"); - assert!(vm_config - .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1") - .is_err()); - assert!(vm_config - .add_mem_zone("-object memory-backend-ram,size=2G") - .is_err()); - assert!(vm_config - .add_mem_zone("-object memory-backend-ram,id=mem1") - .is_err()); - - let mut vm_config = VmConfig::default(); let zone_config_2 = vm_config .add_mem_zone( "-object memory-backend-ram,size=2G,id=mem1,host-nodes=1-2,policy=default", ) .unwrap(); assert_eq!(zone_config_2.host_numa_nodes, Some(vec![1, 2])); + + let zone_config_3 = vm_config + .add_mem_zone("-object memory-backend-ram,size=2M,id=mem1,share=on") + .unwrap(); + assert_eq!(zone_config_3.size, 2 * 1024 * 1024); + assert_eq!(zone_config_3.share, true); + + let zone_config_4 = vm_config + .add_mem_zone("-object memory-backend-ram,size=2M,id=mem1") + .unwrap(); + assert_eq!(zone_config_4.share, false); } #[test] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 2e4b53463..ccfb76202 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -50,6 +50,7 @@ mod numa; mod pci; mod rng; mod sasl_auth; +pub mod scream; mod scsi; mod tls_creds; mod usb; @@ -204,10 +205,10 @@ impl VmConfig { } } "memory-backend-ram" => { - let zone_config = self.add_mem_zone(object_args)?; - let id = zone_config.id.clone(); + let config = self.add_mem_zone(object_args)?; + let id = config.id.clone(); if self.object.mem_object.get(&id).is_none() { - self.object.mem_object.insert(id, zone_config); + self.object.mem_object.insert(id, config); } else { bail!("Object: {} has been added", id); } diff --git a/machine_manager/src/config/scream.rs b/machine_manager/src/config/scream.rs new file mode 100644 index 000000000..9b9adfc06 --- /dev/null +++ b/machine_manager/src/config/scream.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{anyhow, Result}; + +use super::{pci_args_check, CmdParser}; + +pub fn parse_scream(cfg_args: &str) -> Result { + let mut cmd_parser = CmdParser::new("scream"); + cmd_parser + .push("") + .push("memdev") + .push("id") + .push("bus") + .push("addr"); + cmd_parser.parse(cfg_args)?; + + pci_args_check(&cmd_parser)?; + + cmd_parser + .get_value::("memdev")? + .ok_or_else(|| anyhow!("No memdev configured for scream device")) +} diff --git a/pci/src/config.rs b/pci/src/config.rs index 02a420a91..a4fba98c6 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -85,6 +85,8 @@ pub const HEADER_TYPE_ENDPOINT: u8 = 0x0; pub const HEADER_TYPE_BRIDGE: u8 = 0x01; /// Multi-function device. pub const HEADER_TYPE_MULTIFUNC: u8 = 0x80; +/// The vendor ID for Red Hat / Qumranet. +pub const PCI_VENDOR_ID_REDHAT_QUMRANET: u16 = 0x1af4; /// The vendor ID for PCI devices other than virtio. pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; @@ -292,6 +294,9 @@ pub const PCI_EXP_HP_EV_CCI: u16 = PCI_EXP_SLTCTL_CCIE; // XHCI device id pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; + +/* Device classes and subclasses */ +pub const PCI_CLASS_MEMORY_RAM: u16 = 0x0500; pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; /// Type of bar region. @@ -1109,8 +1114,6 @@ impl PciConfig { fn validate_bar_size(&self, bar_type: RegionType, size: u64) -> Result<()> { if !size.is_power_of_two() - || ((bar_type == RegionType::Mem32Bit || bar_type == RegionType::Mem64Bit) - && size < MINMUM_BAR_SIZE_FOR_MMIO.try_into().unwrap()) || (bar_type == RegionType::Io && size < MINMUM_BAR_SIZE_FOR_PIO.try_into().unwrap()) || (bar_type == RegionType::Mem32Bit && size > u32::MAX as u64) || (bar_type == RegionType::Io && size > u16::MAX as u64) @@ -1181,11 +1184,7 @@ mod tests { assert!(pci_config .register_bar(7, region, RegionType::Mem64Bit, true, 8192) .is_err()); - // test when bar size is incorrect(below 4KB, or not power of 2) - let region_size_too_small = Region::init_io_region(2048, region_ops.clone()); - assert!(pci_config - .register_bar(3, region_size_too_small, RegionType::Mem64Bit, true, 2048) - .is_err()); + // test when bar size is incorrect(not power of 2) let region_size_not_pow_2 = Region::init_io_region(4238, region_ops); assert!(pci_config .register_bar(4, region_size_not_pow_2, RegionType::Mem64Bit, true, 4238) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 252f0afd0..17d02ab99 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -23,8 +23,8 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use pci::config::{ RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, - REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, - SUB_CLASS_CODE, VENDOR_ID, + PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, + SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::{update_dev_id, MsixState, MSIX_TABLE_ENTRY_SIZE}; use pci::Result as PciResult; @@ -51,7 +51,7 @@ use crate::{ const VIRTIO_QUEUE_MAX: u32 = 1024; -const VIRTIO_PCI_VENDOR_ID: u16 = 0x1af4; +const VIRTIO_PCI_VENDOR_ID: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040; const VIRTIO_PCI_ABI_VERSION: u8 = 1; const VIRTIO_PCI_CLASS_ID_NET: u16 = 0x0280; -- Gitee From 30286485206941aca635d4b6199bc2a5e4773ffe Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 Mar 2023 22:08:34 +0800 Subject: [PATCH 0921/2187] scream: Add a thread for playback sound The scream receives audio data from the ivshmem shared memory and sends the audio data to the PulseAudio service on the host. Signed-off-by: Mingwang Li --- Cargo.lock | 70 ++++++++ devices/Cargo.toml | 2 + devices/src/misc/mod.rs | 2 + devices/src/misc/scream/mod.rs | 231 +++++++++++++++++++++++- devices/src/misc/scream/pulseaudio.rs | 250 ++++++++++++++++++++++++++ machine/src/lib.rs | 4 + 6 files changed, 553 insertions(+), 6 deletions(-) create mode 100644 devices/src/misc/scream/pulseaudio.rs diff --git a/Cargo.lock b/Cargo.lock index e7bf8adfe..cd23340e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,6 +212,8 @@ dependencies = [ "kvm-bindings", "kvm-ioctls", "libc", + "libpulse-binding", + "libpulse-simple-binding", "log", "machine_manager", "migration", @@ -389,6 +391,54 @@ version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +[[package]] +name = "libpulse-binding" +version = "2.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890" +dependencies = [ + "bitflags", + "libc", + "libpulse-sys", + "num-derive", + "num-traits", + "winapi", +] + +[[package]] +name = "libpulse-simple-binding" +version = "2.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ced94199e6e44133431374e4043f34e1f0697ebfb7b7d6c244a65bfaedf0e31" +dependencies = [ + "libpulse-binding", + "libpulse-simple-sys", + "libpulse-sys", +] + +[[package]] +name = "libpulse-simple-sys" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e423d9c619c908ce9b4916080e65ab586ca55b8c4939379f15e6e72fb43842" +dependencies = [ + "libpulse-sys", + "pkg-config", +] + +[[package]] +name = "libpulse-sys" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2191e6880818d1df4cf72eac8e91dce7a5a52ba0da4b2a5cdafabc22b937eadb" +dependencies = [ + "libc", + "num-derive", + "num-traits", + "pkg-config", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -518,6 +568,26 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.29.0" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 12dd3684d..6f5b54769 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -31,6 +31,8 @@ acpi = { path = "../acpi" } [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } pci = { path = "../pci" } +pulse = { version = "2.0", package = "libpulse-binding" } +psimple = { version = "2.0", package = "libpulse-simple-binding" } [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/misc/mod.rs b/devices/src/misc/mod.rs index 3b0de4b73..a829356c1 100644 --- a/devices/src/misc/mod.rs +++ b/devices/src/misc/mod.rs @@ -10,5 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(not(target_env = "musl"))] mod ivshmem; +#[cfg(not(target_env = "musl"))] pub mod scream; diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 04f79b27d..05bf63eee 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -10,17 +10,212 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod pulseaudio; + use std::{ + mem, sync::{Arc, Mutex, Weak}, thread, }; use address_space::{GuestAddress, HostMemMapping, Region}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use core::time; +use log::error; use super::ivshmem::Ivshmem; use pci::{PciBus, PciDevOps}; +use pulseaudio::{PulseStreamData, TAGET_LATENCY_MS}; + +// A frame of back-end audio data is 50ms, and the next frame of audio data needs +// to be trained in polling within 50ms. Theoretically, the shorter the polling time, +// the better. However, if the value is too small, the overhead is high. So take a +// compromise: 50 * 1000 / 8 us. +const POLL_DELAY_US: u64 = (TAGET_LATENCY_MS as u64) * 1000 / 8; + +const SCREAM_MAGIC: u64 = 0x02032023; + +/// The scream device defines the audio directions. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ScreamDirection { + Playback, + Record, +} + +/// Audio stream header information in the shared memory. +#[repr(C)] +#[derive(Default, Clone, Copy)] +struct ShmemStreamHeader { + /// Whether audio is started. + pub is_started: u32, + /// Current audio chunk position. + pub chunk_idx: u16, + /// Maximum number of audio chunk. + max_chunks: u16, + /// Size of a single audio chunk. + chunk_size: u32, + /// Offset of the first audio data based on shared memory. + offset: u32, + start_time_ns: i64, + /// Audio stream format. + fmt: ShmemStreamFmt, +} + +impl ShmemStreamHeader { + pub fn check(&self, shmem_size: u64) -> bool { + let boundary = self.offset as u64 + self.chunk_size as u64 * self.max_chunks as u64; + if boundary > shmem_size { + error!( + "Guest set bad stream params: offset {:x} max chunk num is {}, chunk size is {}", + self.offset, self.max_chunks, self.chunk_size + ); + return false; + } + + if self.chunk_idx > self.max_chunks { + error!( + "The chunk index of stream {} exceeds the maximum number of chunks {}", + self.chunk_idx, self.max_chunks + ); + return false; + } + if self.fmt.channels == 0 || self.fmt.channel_map == 0 { + error!( + "The fmt channels {} or channel_map {} is invalid", + self.fmt.channels, self.fmt.channel_map + ); + return false; + } + true + } +} + +/// First Header data in the shared memory. +#[repr(C)] +#[derive(Default)] +struct ShmemHeader { + magic: u64, + /// PlayBack audio stream header. + play: ShmemStreamHeader, + /// Record audio stream header. + capt: ShmemStreamHeader, +} + +/// Audio stream format in the shared memory. +#[repr(C)] +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct ShmemStreamFmt { + /// Indicates whether the audio format is changed. + pub fmt_generation: u32, + /// Audio sampling rate. + pub rate: u8, + /// Numner of audio sampling bits. + pub size: u8, + //// Number of audio channel. + pub channels: u8, + pad: u8, + /// Mapping of audio channel. + pub channel_map: u32, + pad2: u32, +} + +impl Default for ShmemStreamFmt { + fn default() -> Self { + Self { + fmt_generation: 0, + rate: 0, + size: 0, + channels: 2, + pad: 0, + channel_map: 0x03, + pad2: 0, + } + } +} + +/// Audio stream data structure. +#[derive(Default)] +pub struct StreamData { + pub fmt: ShmemStreamFmt, + chunk_idx: u16, + /// Size of the data to be played or recorded. + pub audio_size: u32, + /// Location of the played or recorded audio data in the shared memory. + pub audio_base: u64, +} + +impl StreamData { + fn init(&mut self, header: &ShmemStreamHeader) { + self.fmt = header.fmt; + self.chunk_idx = header.chunk_idx; + } + + fn wait_for_ready( + &mut self, + dir: ScreamDirection, + poll_delay_us: u64, + hva: u64, + shmem_size: u64, + ) { + // SAFETY: hva is the shared memory base address. It already verifies the validity + // of the address range during the scream realize. + let mut header = &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; + + let stream_header = match dir { + ScreamDirection::Playback => &header.play, + ScreamDirection::Record => &header.capt, + }; + + loop { + if header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { + while header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { + thread::sleep(time::Duration::from_millis(10)); + header = + &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; + } + self.init(stream_header); + } + + // Audio playback requires waiting for the guest to play audio data. + if dir == ScreamDirection::Playback && self.chunk_idx == stream_header.chunk_idx { + thread::sleep(time::Duration::from_micros(poll_delay_us)); + continue; + } + + if !stream_header.check(shmem_size) { + continue; + } + + // Guest reformats the audio, and the scream device also needs to be init. + if self.fmt != stream_header.fmt { + self.init(stream_header); + continue; + } + + return; + } + } + + fn update_play_buffer(&mut self, hva: u64) { + // SAFETY: hva is the shared memory base address. It already verifies the validity + // of the address range during the header check. + let header = &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; + let play = header.play; + + self.chunk_idx = (self.chunk_idx + 1) % play.max_chunks; + + // If the difference between the currently processed chunk_idx and the chunk_idx in + // the shared memory is greater than 3, the processing of the backend device is too + // slow and the backward data is skipped. + if (play.chunk_idx + play.max_chunks - self.chunk_idx) % play.max_chunks > 3 { + self.chunk_idx = (play.chunk_idx + play.max_chunks - 1) % play.max_chunks; + } + + self.audio_size = play.chunk_size; + self.audio_base = + hva + play.offset as u64 + (play.chunk_size as u64) * (self.chunk_idx as u64); + } +} /// Scream sound card device structure. pub struct Scream { @@ -33,17 +228,41 @@ impl Scream { Self { hva: 0, size } } - fn start(&self) -> Result<()> { + fn start_play_thread_fn(&self) -> Result<()> { + let hva = self.hva; + let shmem_size = self.size; thread::Builder::new() - .name("scream audio worker".to_string()) - .spawn(move || loop { - thread::sleep(time::Duration::from_millis(50)); + .name("scream audio play worker".to_string()) + .spawn(move || { + let mut output = PulseStreamData::init("Scream", ScreamDirection::Playback); + let mut play_data = StreamData::default(); + + loop { + play_data.wait_for_ready( + ScreamDirection::Playback, + POLL_DELAY_US, + hva, + shmem_size, + ); + play_data.update_play_buffer(hva); + + output.send(&play_data); + } }) .with_context(|| "Failed to create thread scream")?; Ok(()) } pub fn realize(mut self, devfn: u8, parent_bus: Weak>) -> Result<()> { + let header_size = mem::size_of::() as u64; + if self.size < header_size { + bail!( + "The size {} of the shared memory is smaller then audio header {}", + self.size, + header_size + ); + } + let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(0), None, @@ -60,6 +279,6 @@ impl Scream { let ivshmem = Ivshmem::new("ivshmem".to_string(), devfn, parent_bus, mem_region); ivshmem.realize()?; - self.start() + self.start_play_thread_fn() } } diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs new file mode 100644 index 000000000..201092980 --- /dev/null +++ b/devices/src/misc/scream/pulseaudio.rs @@ -0,0 +1,250 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::atomic::{fence, Ordering}; + +use log::{error, warn}; +use psimple::Simple; +use pulse::{ + channelmap::{Map, MapDef, Position}, + def::BufferAttr, + sample::{Format, Spec}, + stream::Direction, + time::MicroSeconds, +}; + +use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData}; + +const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; +const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; +const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; + +pub const TAGET_LATENCY_MS: u32 = 50; +const MAX_LATENCY_MS: u32 = 100; + +const STREAM_NAME: &str = "Audio"; + +const WINDOWS_POSITION_CNT: usize = 11; +const PULSEAUDIO_POSITION: [Position; WINDOWS_POSITION_CNT] = [ + Position::FrontLeft, + Position::FrontRight, + Position::FrontCenter, + Position::Lfe, + Position::RearLeft, + Position::RearRight, + Position::FrontLeftOfCenter, + Position::FrontRightOfCenter, + Position::RearCenter, + Position::SideLeft, + Position::SideRight, +]; + +impl ScreamDirection { + fn transform(&self) -> Direction { + match self { + Self::Playback => Direction::Playback, + Self::Record => Direction::Record, + } + } +} + +/// Data structure of the audio processed by the pulseaudio. +pub struct PulseStreamData { + simple: Option, + ss: Spec, + channel_map: Map, + buffer_attr: BufferAttr, + stream_fmt: ShmemStreamFmt, + latency: u32, + app_name: String, + stream_name: String, + dir: Direction, +} + +impl PulseStreamData { + pub fn init(name: &str, dir: ScreamDirection) -> Self { + // Map to stereo, it's the default number of channels. + let mut channel_map = Map::default(); + channel_map.init_stereo(); + + // Start with base default format, rate and channels. Will switch to actual format later. + let ss = Spec { + format: Format::S16le, + rate: AUDIO_SAMPLE_RATE_44KHZ, + channels: 2, + }; + + // Init receiver format to track changes. + let stream_fmt = ShmemStreamFmt::default(); + + // Set buffer size for requested latency. + let buffer_attr = BufferAttr { + maxlength: ss.usec_to_bytes(MicroSeconds(MAX_LATENCY_MS as u64 * 1000)) as u32, + tlength: ss.usec_to_bytes(MicroSeconds(TAGET_LATENCY_MS as u64 * 1000)) as u32, + prebuf: std::u32::MAX, + minreq: std::u32::MAX, + fragsize: std::u32::MAX, + }; + + let pa_dir = dir.transform(); + + let simple = Simple::new( + None, + name, + pa_dir, + None, + STREAM_NAME, + &ss, + Some(&channel_map), + Some(&buffer_attr), + ) + .unwrap_or_else(|e| panic!("PulseAudio init failed : {}", e)); + + Self { + simple: Some(simple), + ss, + channel_map, + buffer_attr, + stream_fmt, + latency: TAGET_LATENCY_MS, + app_name: name.to_string(), + stream_name: STREAM_NAME.to_string(), + dir: pa_dir, + } + } + + fn transfer_channel_map(&mut self, format: &ShmemStreamFmt) { + self.channel_map.init(); + self.channel_map.set_len(format.channels); + let map: &mut [Position] = self.channel_map.get_mut(); + /* In Windows, the channel mask shows as following figure. + * 31 11 10 9 8 7 6 5 4 3 2 1 0 + * | | | SR | SL | BC | FRC| FLC| BR | BL | LFE| FC | FR | FL | + * + * Each bit in the channel mask represents a particular speaker position. + * Now, it map a windows SPEAKER_* position to a PA_CHANNEL_POSITION_*. + */ + let mut key: i32 = -1; + for (i, item) in map.iter_mut().enumerate().take(format.channels as usize) { + for j in (key + 1)..32 { + if (format.channel_map >> j) & 0x01 == 1 { + key = j; + break; + } + } + // Map the key value to a pulseaudio channel position. + if (key as usize) < WINDOWS_POSITION_CNT { + *item = PULSEAUDIO_POSITION[key as usize]; + } else { + warn!("Channel {} can not be mapped, Falling back to 'center'.", i); + *item = Position::FrontCenter; + } + } + } + + fn check_fmt_update(&mut self, recv_data: &StreamData) { + if self.stream_fmt == recv_data.fmt { + return; + } + // If audio format changed, reconfigure + self.stream_fmt = recv_data.fmt; + self.ss.channels = recv_data.fmt.channels; + self.ss.rate = if recv_data.fmt.rate >= WINDOWS_SAMPLE_BASE_RATE { + AUDIO_SAMPLE_RATE_44KHZ + } else { + AUDIO_SAMPLE_RATE_48KHZ + } * (recv_data.fmt.rate % WINDOWS_SAMPLE_BASE_RATE) as u32; + + match recv_data.fmt.size { + 16 => self.ss.format = Format::S16le, + 24 => self.ss.format = Format::S24le, + 32 => self.ss.format = Format::S32le, + _ => { + warn!( + "Unsuported sample size {}, not playing until next format switch", + recv_data.fmt.size + ); + self.ss.rate = 0; + } + } + + if recv_data.fmt.channels == 1 { + self.channel_map.init_mono(); + } else if recv_data.fmt.channels == 2 { + self.channel_map.init_stereo(); + } else { + self.transfer_channel_map(&recv_data.fmt); + } + + if !self.channel_map.is_valid() { + warn!("Invalid channel mapping, falling back to MapDef::WAVEEx"); + self.channel_map + .init_extend(recv_data.fmt.channels, MapDef::WAVEEx); + } + if !self.channel_map.is_compatible_with_sample_spec(&self.ss) { + warn!("Incompatible channel mapping."); + self.ss.rate = 0; + } + + if self.ss.rate > 0 { + // Sample spec has changed, so the playback buffer size for the requested latency must be recalculated as well. + self.buffer_attr.tlength = + self.ss + .usec_to_bytes(MicroSeconds(self.latency as u64 * 1000)) as u32; + + self.simple = Simple::new( + None, + self.app_name.as_str(), + self.dir, + None, + self.stream_name.as_str(), + &self.ss, + Some(&self.channel_map), + Some(&self.buffer_attr), + ) + .map_or_else( + |_| { + warn!( + "Unable to open PulseAudio with sample rate {}, sample size {} and channels {}", + self.ss.rate, recv_data.fmt.size, recv_data.fmt.channels + ); + None + }, + Some, + ); + } + } + + pub fn send(&mut self, recv_data: &StreamData) { + self.check_fmt_update(recv_data); + + if self.ss.rate == 0 || self.simple.is_none() { + return; + } + + // Make sure audio read does not bypass chunk_idx read. + fence(Ordering::Acquire); + + // SAFETY: audio_base is the shared memory. It already verifies the validity + // of the address range during the header check. + let data = unsafe { + std::slice::from_raw_parts( + recv_data.audio_base as *const u8, + recv_data.audio_size as usize, + ) + }; + + if let Err(e) = self.simple.as_ref().unwrap().write(data) { + error!("PulseAudio write data failed: {}", e); + } + } +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d26197554..4a51b8245 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -25,8 +25,10 @@ use std::os::unix::net::UnixListener; use std::path::Path; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; +#[cfg(not(target_env = "musl"))] use devices::misc::scream::Scream; use log::warn; +#[cfg(not(target_env = "musl"))] use machine_manager::config::scream::parse_scream; use util::file::{lock_file, unlock_file}; @@ -1086,6 +1088,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - scream configuration. + #[cfg(not(target_env = "musl"))] fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; @@ -1297,6 +1300,7 @@ pub trait MachineOps { "pcie-demo-dev" => { self.add_demo_dev(vm_config, cfg_args)?; } + #[cfg(not(target_env = "musl"))] "ivshmem-scream" => { self.add_ivshmem_scream(vm_config, cfg_args)?; } -- Gitee From 68d73930c9dcd525db643c8a5d94cc19f398c391 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sun, 19 Mar 2023 14:48:36 +0800 Subject: [PATCH 0922/2187] scream: Add a thread for capture audio data The scream capture thread takes the audio data from the host, puts the audio data into shared memory, and then notifies the guest of the data update by updating the chunk_idx. Signed-off-by: Jinhao Gao --- devices/src/misc/scream/mod.rs | 88 ++++++++++++++++++++++++--- devices/src/misc/scream/pulseaudio.rs | 25 ++++++++ 2 files changed, 105 insertions(+), 8 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 05bf63eee..1497231b2 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -14,14 +14,17 @@ mod pulseaudio; use std::{ mem, - sync::{Arc, Mutex, Weak}, + sync::{ + atomic::{fence, Ordering}, + Arc, Mutex, Weak, + }, thread, }; use address_space::{GuestAddress, HostMemMapping, Region}; use anyhow::{bail, Context, Result}; use core::time; -use log::error; +use log::{error, warn}; use super::ivshmem::Ivshmem; use pci::{PciBus, PciDevOps}; @@ -62,7 +65,14 @@ struct ShmemStreamHeader { } impl ShmemStreamHeader { - pub fn check(&self, shmem_size: u64) -> bool { + pub fn check(&self, shmem_size: u64, last_end: u64) -> bool { + if (self.offset as u64) < last_end { + warn!( + "Guest set bad offset {} exceeds last stream buffer end {}", + self.offset, last_end + ); + } + let boundary = self.offset as u64 + self.chunk_size as u64 * self.max_chunks as u64; if boundary > shmem_size { error!( @@ -182,7 +192,15 @@ impl StreamData { continue; } - if !stream_header.check(shmem_size) { + let mut last_end = 0; + // The recording buffer is behind the playback buffer. Thereforce, the end position of + // the playback buffer must be calculted to determine whether the two buffers overlap. + if dir == ScreamDirection::Record && header.play.is_started != 0 { + last_end = header.play.offset as u64 + + header.play.chunk_size as u64 * header.play.max_chunks as u64; + } + + if !stream_header.check(shmem_size, last_end) { continue; } @@ -211,9 +229,35 @@ impl StreamData { self.chunk_idx = (play.chunk_idx + play.max_chunks - 1) % play.max_chunks; } - self.audio_size = play.chunk_size; - self.audio_base = - hva + play.offset as u64 + (play.chunk_size as u64) * (self.chunk_idx as u64); + self.update_buffer_by_chunk_idx(hva, &play); + } + + fn update_buffer_by_chunk_idx(&mut self, hva: u64, stream_header: &ShmemStreamHeader) { + self.audio_size = stream_header.chunk_size; + self.audio_base = hva + + stream_header.offset as u64 + + (stream_header.chunk_size as u64) * (self.chunk_idx as u64); + } + + fn update_capt_buffer(&mut self, hva: u64) { + // SAFETY: hva is the shared memory base address. It already verifies the validity + // of the address range during the header check. + let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; + + self.update_buffer_by_chunk_idx(hva, &header.capt); + } + + fn update_capt_idx(&mut self, hva: u64) { + // SAFETY: hva is the shared memory base address. It already verifies the validity + // of the address range during the header check. + let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; + + self.chunk_idx = (self.chunk_idx + 1) % header.capt.max_chunks; + + // Make sure chunk_idx write does not bypass audio chunk write. + fence(Ordering::SeqCst); + + header.capt.chunk_idx = self.chunk_idx; } } @@ -253,6 +297,33 @@ impl Scream { Ok(()) } + fn start_record_thread_fn(&self) -> Result<()> { + let hva = self.hva; + let shmem_size = self.size; + thread::Builder::new() + .name("scream audio capt worker".to_string()) + .spawn(move || { + let mut input = PulseStreamData::init("ScreamCapt", ScreamDirection::Record); + let mut capt_data = StreamData::default(); + + loop { + capt_data.wait_for_ready( + ScreamDirection::Record, + POLL_DELAY_US, + hva, + shmem_size, + ); + capt_data.update_capt_buffer(hva); + + if input.receive(&capt_data) { + capt_data.update_capt_idx(hva); + } + } + }) + .with_context(|| "Failed to create thread scream")?; + Ok(()) + } + pub fn realize(mut self, devfn: u8, parent_bus: Weak>) -> Result<()> { let header_size = mem::size_of::() as u64; if self.size < header_size { @@ -279,6 +350,7 @@ impl Scream { let ivshmem = Ivshmem::new("ivshmem".to_string(), devfn, parent_bus, mem_region); ivshmem.realize()?; - self.start_play_thread_fn() + self.start_play_thread_fn()?; + self.start_record_thread_fn() } } diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 201092980..3f7331bc5 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -247,4 +247,29 @@ impl PulseStreamData { error!("PulseAudio write data failed: {}", e); } } + + pub fn receive(&mut self, recv_data: &StreamData) -> bool { + self.check_fmt_update(recv_data); + + if self.simple.is_none() { + return false; + } + + // SAFETY: audio_base is the shared memory. It already verifies the validity + // of the address range during the header check. + let data = unsafe { + std::slice::from_raw_parts_mut( + recv_data.audio_base as *mut u8, + recv_data.audio_size as usize, + ) + }; + + if let Err(e) = self.simple.as_ref().unwrap().read(data) { + error!("PulseAudio read data failed: {}", e); + self.ss.rate = 0; + return false; + } + + true + } } -- Gitee From 56b678a6d6b8d60f3b9889e3e2e5d752773f99b2 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Mar 2023 16:27:29 +0800 Subject: [PATCH 0923/2187] Syscall: add five syscall to whitelist Scream device makes five additional linux syscall that need to be whitelisted. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/syscall.rs | 12 +++++++++++- machine/src/standard_vm/x86_64/syscall.rs | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 0ec34a53b..a6876c743 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 81 syscalls +/// * aarch64-unknown-gnu: 86 syscalls /// * aarch64-unknown-musl: 59 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -173,6 +173,16 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_sched_getaffinity), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_rseq), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_pipe2), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fcntl), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_memfd_create), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_ftruncate), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_futex), ] } diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 277294f8d..e37944317 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 82 syscalls +/// * x86_64-unknown-gnu: 87 syscalls /// * x86_64-unknown-musl: 62 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -181,6 +181,16 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_set_robust_list), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sched_getaffinity), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_pipe2), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fcntl), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_memfd_create), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_ftruncate), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_futex), ] } -- Gitee From 5fabdbc5bf4a022afbadd0735ae54e3f5c1d6f93 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Mar 2023 12:42:57 +0800 Subject: [PATCH 0924/2187] scream: Add Demo interface for MST When interface=Demo is configured during the MST test, audio data is written to the corresponding file, and audio data is obtained from the corresponding file during recording. Signed-off-by: Mingwang Li --- devices/src/misc/scream/audio_demo.rs | 81 +++++++++++++++++++++++++++ devices/src/misc/scream/mod.rs | 49 ++++++++++++++-- devices/src/misc/scream/pulseaudio.rs | 7 ++- docs/config_guidebook.md | 7 ++- machine/src/lib.rs | 16 ++++-- machine_manager/src/config/scream.rs | 50 ++++++++++++++++- 6 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 devices/src/misc/scream/audio_demo.rs diff --git a/devices/src/misc/scream/audio_demo.rs b/devices/src/misc/scream/audio_demo.rs new file mode 100644 index 000000000..9ea063f34 --- /dev/null +++ b/devices/src/misc/scream/audio_demo.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::{File, OpenOptions}, + io::{Read, Write}, + thread, +}; + +use core::time; +use log::error; + +use super::{AudioInterface, ScreamDirection, StreamData}; + +pub struct AudioDemo { + file: File, +} + +impl AudioDemo { + pub fn init(dir: ScreamDirection, playback: String, record: String) -> Self { + let file = match dir { + ScreamDirection::Playback => OpenOptions::new() + .append(true) + .open(playback) + .unwrap_or_else(|e| { + error!("Failed to append open Audio Demo file: {:?}", e); + panic!() + }), + ScreamDirection::Record => File::open(record).unwrap_or_else(|e| { + error!("Failed to append open Audio Demo file: {:?}", e); + panic!() + }), + }; + + Self { file } + } +} + +impl AudioInterface for AudioDemo { + fn send(&mut self, recv_data: &StreamData) { + let data = unsafe { + std::slice::from_raw_parts( + recv_data.audio_base as *const u8, + recv_data.audio_size as usize, + ) + }; + + self.file + .write_all(data) + .unwrap_or_else(|e| error!("Failed to write data to file: {:?}", e)); + + self.file + .flush() + .unwrap_or_else(|e| error!("Failed to flush data to file: {:?}", e)); + } + + fn receive(&mut self, recv_data: &StreamData) -> bool { + thread::sleep(time::Duration::from_millis(20)); + let data = unsafe { + std::slice::from_raw_parts_mut( + recv_data.audio_base as *mut u8, + recv_data.audio_size as usize, + ) + }; + let size = self.file.read(data).unwrap_or_else(|e| { + error!("Failed to read data to file: {:?}", e); + 0 + }); + + size == data.len() + } +} diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 1497231b2..1455dcbe7 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod audio_demo; mod pulseaudio; use std::{ @@ -26,7 +27,9 @@ use anyhow::{bail, Context, Result}; use core::time; use log::{error, warn}; +use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; +use machine_manager::config::scream::ScreamConfig; use pci::{PciBus, PciDevOps}; use pulseaudio::{PulseStreamData, TAGET_LATENCY_MS}; @@ -265,20 +268,48 @@ impl StreamData { pub struct Scream { hva: u64, size: u64, + interface: String, + playback: String, + record: String, } impl Scream { - pub fn new(size: u64) -> Self { - Self { hva: 0, size } + pub fn new(size: u64, dev_cfg: &ScreamConfig) -> Self { + Self { + hva: 0, + size, + interface: dev_cfg.interface.clone(), + playback: dev_cfg.playback.clone(), + record: dev_cfg.record.clone(), + } + } + + fn interface_init(&self, name: &str, dir: ScreamDirection) -> Arc> { + match self.interface.as_str() { + "PulseAudio" => Arc::new(Mutex::new(PulseStreamData::init(name, dir))), + "Demo" => Arc::new(Mutex::new(AudioDemo::init( + dir, + self.playback.clone(), + self.record.clone(), + ))), + _ => { + error!( + "Unsupported audio interface {}, falling back to Pulseaudio", + self.interface + ); + Arc::new(Mutex::new(PulseStreamData::init(name, dir))) + } + } } fn start_play_thread_fn(&self) -> Result<()> { let hva = self.hva; let shmem_size = self.size; + let interface = self.interface_init("Scream", ScreamDirection::Playback); thread::Builder::new() .name("scream audio play worker".to_string()) .spawn(move || { - let mut output = PulseStreamData::init("Scream", ScreamDirection::Playback); + let mut interface_locked = interface.lock().unwrap(); let mut play_data = StreamData::default(); loop { @@ -290,7 +321,7 @@ impl Scream { ); play_data.update_play_buffer(hva); - output.send(&play_data); + interface_locked.send(&play_data); } }) .with_context(|| "Failed to create thread scream")?; @@ -300,10 +331,11 @@ impl Scream { fn start_record_thread_fn(&self) -> Result<()> { let hva = self.hva; let shmem_size = self.size; + let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { - let mut input = PulseStreamData::init("ScreamCapt", ScreamDirection::Record); + let mut interface_locked = interface.lock().unwrap(); let mut capt_data = StreamData::default(); loop { @@ -315,7 +347,7 @@ impl Scream { ); capt_data.update_capt_buffer(hva); - if input.receive(&capt_data) { + if interface_locked.receive(&capt_data) { capt_data.update_capt_idx(hva); } } @@ -354,3 +386,8 @@ impl Scream { self.start_record_thread_fn() } } + +pub trait AudioInterface: Send { + fn send(&mut self, recv_data: &StreamData); + fn receive(&mut self, recv_data: &StreamData) -> bool; +} diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 3f7331bc5..0fc9014dc 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -22,6 +22,7 @@ use pulse::{ time::MicroSeconds, }; +use super::AudioInterface; use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData}; const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; @@ -223,8 +224,10 @@ impl PulseStreamData { ); } } +} - pub fn send(&mut self, recv_data: &StreamData) { +impl AudioInterface for PulseStreamData { + fn send(&mut self, recv_data: &StreamData) { self.check_fmt_update(recv_data); if self.ss.rate == 0 || self.simple.is_none() { @@ -248,7 +251,7 @@ impl PulseStreamData { } } - pub fn receive(&mut self, recv_data: &StreamData) -> bool { + fn receive(&mut self, recv_data: &StreamData) -> bool { self.check_fmt_update(recv_data); if self.simple.is_none() { diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index eb8c7112c..2cc8703e8 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -934,9 +934,12 @@ Note: ivshmem-scream is a virtual sound card that relies on Intel-VM shared memory to transmit audio data. -Six properties are supported for ivshmem-scream device. +Nine properties are supported for ivshmem-scream device. * id: unique device id. * memdev: configuration of the back-end memory device used by the ivshmem. +* interface: configuring audio playback and recording interfaces, currently can be set to `PulseAudio` or `Demo`. +* playback: Path for storing audio. When interface is set to Demo, playback is mandatory. +* record: Path for obtaining audio. When interface is set to Demo, record is mandatory. * bus: bus number of the device. * addr: including slot number and function number. * share: the shared memory must be set to `on`. @@ -945,7 +948,7 @@ Six properties are supported for ivshmem-scream device. Sample Configuration: ```shell --device ivshmem-scream,id=,memdev=,bus=pcie.0,addr=0x2.0x0 +-device ivshmem-scream,id=,memdev=[,interface=][,playback=][,record=],bus=pcie.0,addr=0x2.0x0 -object memory-backend-ram,id=,share=on,size=2M ``` diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4a51b8245..1b13fdc20 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1093,19 +1093,25 @@ pub trait MachineOps { let bdf = get_pci_bdf(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let memdev = + let dev_cfg = parse_scream(cfg_args).with_context(|| "Failed to parse cmdline for ivshmem")?; - let mem_cfg = - vm_config.object.mem_object.remove(&memdev).ok_or_else(|| { - anyhow!("Object for memory-backend-ram {} config not found", memdev) + let mem_cfg = vm_config + .object + .mem_object + .remove(&dev_cfg.memdev) + .ok_or_else(|| { + anyhow!( + "Object for memory-backend-ram {} config not found", + dev_cfg.memdev + ) })?; if !mem_cfg.share { bail!("Object for share config is not on"); } - let scream = Scream::new(mem_cfg.size); + let scream = Scream::new(mem_cfg.size, &dev_cfg); scream .realize(devfn, parent_bus) .with_context(|| "Failed to realize scream device") diff --git a/machine_manager/src/config/scream.rs b/machine_manager/src/config/scream.rs index 9b9adfc06..f7264b9ac 100644 --- a/machine_manager/src/config/scream.rs +++ b/machine_manager/src/config/scream.rs @@ -14,11 +14,38 @@ use anyhow::{anyhow, Result}; use super::{pci_args_check, CmdParser}; -pub fn parse_scream(cfg_args: &str) -> Result { +pub struct ScreamConfig { + pub memdev: String, + pub interface: String, + pub playback: String, + pub record: String, +} + +impl ScreamConfig { + pub fn new() -> Self { + Self { + memdev: "".to_string(), + interface: "PulseAudio".to_string(), + playback: "".to_string(), + record: "".to_string(), + } + } +} + +impl Default for ScreamConfig { + fn default() -> Self { + Self::new() + } +} + +pub fn parse_scream(cfg_args: &str) -> Result { let mut cmd_parser = CmdParser::new("scream"); cmd_parser .push("") .push("memdev") + .push("interface") + .push("playback") + .push("record") .push("id") .push("bus") .push("addr"); @@ -26,7 +53,24 @@ pub fn parse_scream(cfg_args: &str) -> Result { pci_args_check(&cmd_parser)?; - cmd_parser + let mut dev_cfg = ScreamConfig::new(); + + dev_cfg.memdev = cmd_parser .get_value::("memdev")? - .ok_or_else(|| anyhow!("No memdev configured for scream device")) + .ok_or_else(|| anyhow!("No memdev configured for scream device"))?; + + if let Some(interface) = cmd_parser.get_value::("interface")? { + dev_cfg.interface = interface; + } + + if dev_cfg.interface.eq(&"Demo".to_string()) { + dev_cfg.playback = cmd_parser + .get_value::("playback")? + .ok_or_else(|| anyhow!("No playback configured for interface"))?; + dev_cfg.record = cmd_parser + .get_value::("record")? + .ok_or_else(|| anyhow!("No record configured for interface"))?; + } + + Ok(dev_cfg) } -- Gitee From a6ef92867082d1ecaeb765ec3b685fa16e1c62ef Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Mar 2023 13:15:19 +0800 Subject: [PATCH 0925/2187] MST: add scream MST Signed-off-by: Mingwang Li --- devices/src/misc/scream/mod.rs | 14 +- tests/mod_test/src/libdriver/ivshmem.rs | 66 +++ tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/pci.rs | 5 + tests/mod_test/src/libdriver/usb.rs | 14 +- tests/mod_test/src/libdriver/virtio_gpu.rs | 16 +- .../src/libdriver/virtio_pci_modern.rs | 7 +- tests/mod_test/tests/scream_test.rs | 524 ++++++++++++++++++ 8 files changed, 607 insertions(+), 40 deletions(-) create mode 100644 tests/mod_test/src/libdriver/ivshmem.rs create mode 100644 tests/mod_test/tests/scream_test.rs diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 1455dcbe7..c17f1c075 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -39,7 +39,7 @@ use pulseaudio::{PulseStreamData, TAGET_LATENCY_MS}; // compromise: 50 * 1000 / 8 us. const POLL_DELAY_US: u64 = (TAGET_LATENCY_MS as u64) * 1000 / 8; -const SCREAM_MAGIC: u64 = 0x02032023; +pub const SCREAM_MAGIC: u64 = 0x02032023; /// The scream device defines the audio directions. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -51,20 +51,20 @@ pub enum ScreamDirection { /// Audio stream header information in the shared memory. #[repr(C)] #[derive(Default, Clone, Copy)] -struct ShmemStreamHeader { +pub struct ShmemStreamHeader { /// Whether audio is started. pub is_started: u32, /// Current audio chunk position. pub chunk_idx: u16, /// Maximum number of audio chunk. - max_chunks: u16, + pub max_chunks: u16, /// Size of a single audio chunk. - chunk_size: u32, + pub chunk_size: u32, /// Offset of the first audio data based on shared memory. - offset: u32, + pub offset: u32, start_time_ns: i64, /// Audio stream format. - fmt: ShmemStreamFmt, + pub fmt: ShmemStreamFmt, } impl ShmemStreamHeader { @@ -106,7 +106,7 @@ impl ShmemStreamHeader { /// First Header data in the shared memory. #[repr(C)] #[derive(Default)] -struct ShmemHeader { +pub struct ShmemHeader { magic: u64, /// PlayBack audio stream header. play: ShmemStreamHeader, diff --git a/tests/mod_test/src/libdriver/ivshmem.rs b/tests/mod_test/src/libdriver/ivshmem.rs new file mode 100644 index 000000000..03d766295 --- /dev/null +++ b/tests/mod_test/src/libdriver/ivshmem.rs @@ -0,0 +1,66 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, rc::Rc}; + +use super::{ + pci::{PCIBarAddr, TestPciDev}, + pci_bus::TestPciBus, +}; + +pub struct TestIvshmemDev { + pub pci_dev: TestPciDev, + pub bar_addr: PCIBarAddr, + bar_idx: u8, +} + +impl TestIvshmemDev { + pub fn new(pci_bus: Rc>) -> Self { + Self { + pci_dev: TestPciDev::new(pci_bus), + bar_addr: 0, + bar_idx: 2, + } + } + + pub fn init(&mut self, pci_slot: u8) { + let devfn = pci_slot << 3; + assert!(self.pci_dev.find_pci_device(devfn)); + + self.pci_dev.enable(); + self.bar_addr = self.pci_dev.io_map(self.bar_idx); + } + + pub fn writeb(&mut self, offset: u64, value: u8) { + self.pci_dev.io_writeb(self.bar_addr, offset, value); + } + + pub fn writew(&mut self, offset: u64, value: u16) { + self.pci_dev.io_writew(self.bar_addr, offset, value); + } + + pub fn writel(&mut self, offset: u64, value: u32) { + self.pci_dev.io_writel(self.bar_addr, offset, value); + } + + pub fn writeq(&mut self, offset: u64, value: u64) { + self.pci_dev.io_writeq(self.bar_addr, offset, value); + } + + pub fn readw(&self, offset: u64) -> u16 { + self.pci_dev.io_readw(self.bar_addr, offset) + } + + pub fn readl(&self, offset: u64) -> u32 { + self.pci_dev.io_readl(self.bar_addr, offset) + } +} diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 613cd8ce6..48a8badd8 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod fwcfg; +pub mod ivshmem; pub mod machine; pub mod malloc; pub mod pci; diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 120dbd46c..9d7ce657d 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -260,6 +260,11 @@ impl TestPciDev { pci_bus.memwrite((bar_addr + offset) as u32, &value_buf); } + pub fn find_pci_device(&mut self, devfn: u8) -> bool { + self.devfn = devfn; + self.config_readw(PCI_VENDOR_ID) != 0xFFFF + } + pub fn io_map(&self, barnum: u8) -> u64 { let addr: u32; let size: u64; diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 79e5df2e2..4cb5748ee 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -673,7 +673,7 @@ impl TestXhciPciDevice { pub fn init_pci_device(&mut self, pci_slot: u8, pci_fn: u8) { let devfn = pci_slot << 3 | pci_fn; - assert!(self.find_pci_device(devfn)); + assert!(self.pci_dev.find_pci_device(devfn)); self.pci_dev.enable(); self.bar_addr = self.pci_dev.io_map(self.bar_idx); @@ -1369,18 +1369,6 @@ impl TestXhciPciDevice { evt_seg } - fn set_devfn(&mut self, devfn: u8) { - self.pci_dev.devfn = devfn; - } - - fn find_pci_device(&mut self, devfn: u8) -> bool { - self.set_devfn(devfn); - if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { - return false; - } - true - } - fn set_device_context_address(&mut self, slot_id: u32, addr: u64) { let device_ctx_addr = self.xhci.dcbaap + (slot_id * DEVICE_CONTEXT_ENTRY_SIZE) as u64; let mut buf = [0_u8; 8]; diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index 6d42abd33..e5f44ca15 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -13,7 +13,7 @@ use super::{ machine::TestStdMachine, malloc::GuestAllocator, - pci::{PCIBarAddr, TestPciDev, PCI_VENDOR_ID}, + pci::{PCIBarAddr, TestPciDev}, pci_bus::TestPciBus, }; use crate::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; @@ -378,21 +378,9 @@ impl TestDemoDpyDevice { return test_state.borrow_mut().memread(addr, size); } - pub fn set_devfn(&mut self, devfn: u8) { - self.pci_dev.devfn = devfn; - } - - pub fn find_pci_device(&mut self, devfn: u8) -> bool { - self.set_devfn(devfn); - if self.pci_dev.config_readw(PCI_VENDOR_ID) == 0xFFFF { - return false; - } - true - } - pub fn init(&mut self, pci_slot: u8) { let devfn = pci_slot << 3; - assert!(self.find_pci_device(devfn)); + assert!(self.pci_dev.find_pci_device(devfn)); self.pci_dev.enable(); self.bar_addr = self.pci_dev.io_map(self.bar_idx); diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index 8b4a15eda..c093fbfb7 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -114,7 +114,7 @@ impl TestVirtioPciDev { pub fn init(&mut self, pci_slot: u8, pci_fn: u8) { let devfn = pci_slot << 3 | pci_fn; - assert!(self.find_pci_device(devfn)); + assert!(self.pci_dev.find_pci_device(devfn)); let device_type = self.pci_device_type_probe().unwrap_or(0); self.virtio_dev.device_type = device_type; @@ -127,11 +127,6 @@ impl TestVirtioPciDev { self.bar = self.pci_dev.io_map(self.bar_idx); } - fn find_pci_device(&mut self, devfn: u8) -> bool { - self.pci_dev.devfn = devfn; - self.pci_dev.config_readw(PCI_VENDOR_ID) != 0xFFFF - } - fn find_structure( &self, cfg_type: u8, diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs new file mode 100644 index 000000000..4901507db --- /dev/null +++ b/tests/mod_test/tests/scream_test.rs @@ -0,0 +1,524 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + cell::RefCell, + fs::{self, File}, + io::{Read, Write}, + mem, + path::Path, + rc::Rc, + thread, +}; + +use core::time; + +use devices::misc::scream::{ShmemHeader, ShmemStreamFmt, ShmemStreamHeader, SCREAM_MAGIC}; +use mod_test::{ + libdriver::{ivshmem::TestIvshmemDev, machine::TestStdMachine}, + libtest::{test_init, TestState}, + utils::get_rand_str, +}; +use util::{num_ops::read_data_u32, offset_of}; + +const PLAY_BASE: u64 = mem::size_of::() as u64; +const PLAY_DADA_OFFSET: u64 = mem::size_of::() as u64; + +const RECORD_BASE: u64 = PLAY_BASE + mem::size_of::() as u64; +const RECORD_DATA_OFFSET: u64 = PLAY_DADA_OFFSET + (AUDIO_CHUNK_SIZE * AUDIO_CHUNK_CNT) as u64; + +const IVSHMEM_DEFAULT_SIZE: u32 = 2; +const AUDIO_CHUNK_SIZE: u32 = 4; +const AUDIO_CHUNK_CNT: u32 = 7; +const AUDIO_DEFAULT_DATA: [u8; (AUDIO_CHUNK_SIZE * AUDIO_CHUNK_CNT) as usize] = [ + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, +]; + +const POLL_DELAY_MS: u64 = 20; +const POLL_MAX_CNT: u32 = 5; + +fn get_audio_file_name() -> (String, String) { + let playback_path = format!("/tmp/audio-{}.pcm", get_rand_str(8)); + let record_path = format!("/tmp/audio-{}.pcm", get_rand_str(8)); + (playback_path, record_path) +} + +fn set_up( + size: u32, + pci_slot: u8, + playback_path: String, + record_path: String, +) -> (Rc>, Rc>) { + let mut extra_args: Vec<&str> = Vec::new(); + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + + extra_args.append(&mut args); + + let scream_device = format!( + "-device ivshmem-scream,memdev=scream,id=scream,interface=Demo,playback={},record={},bus=pcie.0,addr={}", + playback_path, record_path, pci_slot, + ); + args = scream_device.split(' ').collect(); + extra_args.append(&mut args); + + let object = format!( + "-object memory-backend-ram,id=scream,share=on,size={}M", + size + ); + args = object.split(' ').collect(); + extra_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(extra_args))); + let machine = TestStdMachine::new(test_state.clone()); + + let ivshmem = Rc::new(RefCell::new(TestIvshmemDev::new(machine.pci_bus))); + + (ivshmem, test_state) +} + +fn stream_header_init(ivshmem: &mut TestIvshmemDev, base: u64, offset: u64) { + // set is_started + ivshmem.writel(base + offset_of!(ShmemStreamHeader, is_started) as u64, 1); + // set chunk_idx + ivshmem.writew(base + offset_of!(ShmemStreamHeader, chunk_idx) as u64, 0); + // set max_chunks + ivshmem.writew( + base + offset_of!(ShmemStreamHeader, max_chunks) as u64, + AUDIO_CHUNK_CNT as u16 - 1, + ); + // set chunk_size + ivshmem.writel( + base + offset_of!(ShmemStreamHeader, chunk_size) as u64, + AUDIO_CHUNK_SIZE, + ); + // set offset + ivshmem.writel( + base + offset_of!(ShmemStreamHeader, offset) as u64, + offset as u32, + ); + + let fmt_base = base + offset_of!(ShmemStreamHeader, fmt) as u64; + // set fmt_generation + ivshmem.writel( + fmt_base + offset_of!(ShmemStreamFmt, fmt_generation) as u64, + 1, + ); + // set rate + ivshmem.writeb(fmt_base + offset_of!(ShmemStreamFmt, rate) as u64, 128); + // set size + ivshmem.writeb(fmt_base + offset_of!(ShmemStreamFmt, size) as u64, 16); + // set channel + ivshmem.writeb(fmt_base + offset_of!(ShmemStreamFmt, channels) as u64, 2); + // set channel_map + ivshmem.writel(fmt_base + offset_of!(ShmemStreamFmt, channel_map) as u64, 3); +} + +fn play_header_init(ivshmem: &mut TestIvshmemDev) { + // set magic + ivshmem.writeq(0, SCREAM_MAGIC); + let base = PLAY_BASE; + stream_header_init(ivshmem, base, PLAY_DADA_OFFSET); +} + +fn play_audio_data_init(playback: String) { + if Path::new(playback.as_str()).exists() { + match fs::remove_file(playback.clone()) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + } + match fs::File::create(playback) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } +} + +fn record_header_init(ivshmem: &mut TestIvshmemDev) { + play_header_init(ivshmem); + let base = RECORD_BASE; + stream_header_init(ivshmem, base, RECORD_DATA_OFFSET); +} + +fn record_audio_data_init(record: String) { + if Path::new(record.as_str()).exists() { + match fs::remove_file(record.clone()) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + } + let mut file = match fs::File::create(record) { + Ok(file) => file, + Err(e) => { + assert!(false, "{}", e); + return; + } + }; + match file.write(&AUDIO_DEFAULT_DATA) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } +} + +fn audio_data_init(playback: String, record: String) { + play_audio_data_init(playback); + record_audio_data_init(record); +} + +fn scream_tmp_clear(playback_path: String, record_path: String) { + if Path::new(playback_path.as_str()).exists() { + match fs::remove_file(playback_path) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + } + + if Path::new(record_path.as_str()).exists() { + match fs::remove_file(record_path) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } + } +} + +fn read_and_check_data(file: &mut File, src: &[u8], len: u32) { + let mut data: [u8; AUDIO_CHUNK_SIZE as usize] = [0; AUDIO_CHUNK_SIZE as usize]; + let size = match file.read(&mut data) { + Ok(size) => size, + Err(e) => { + assert!(false, "{}", e); + 0 + } + }; + assert_eq!(size, len as usize); + if len != 0 { + assert_eq!(data, src); + } +} + +/// scream device playback audio. +/// TestStep: +/// 1. Init scream device. +/// 2. Send first audio frame. +/// 3. Send four consecutive audio frames. +/// 4. change audio format. +/// 5. Stop VM. +/// 6. Check audio frames from audio file. +/// Expect: +/// 1/2/3/4/5/6: success. +#[test] +fn scream_playback_basic_test() { + let pci_slot = 0x1; + let (playback_path, record_path) = get_audio_file_name(); + audio_data_init(playback_path.clone(), record_path.clone()); + let (ivshmem, test_state) = set_up( + IVSHMEM_DEFAULT_SIZE, + pci_slot, + playback_path.clone(), + record_path.clone(), + ); + ivshmem.borrow_mut().init(pci_slot); + + // Wait for 1s until the scream device is initialized and enters the polling state to + // prevent subsequent audio frame data loss. + thread::sleep(time::Duration::from_millis(1000)); + + play_header_init(&mut ivshmem.borrow_mut()); + + thread::sleep(time::Duration::from_millis(POLL_DELAY_MS)); + + // write one audio chunk + for i in 0..AUDIO_CHUNK_SIZE { + ivshmem.borrow_mut().writeb( + PLAY_DADA_OFFSET + (AUDIO_CHUNK_SIZE + i) as u64, + AUDIO_DEFAULT_DATA[i as usize], + ); + } + + // update play header chunk_idx + ivshmem.borrow_mut().writew( + PLAY_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64, + 1, + ); + + thread::sleep(time::Duration::from_millis(1000)); + + // When four consecutive frames of data are written, only the last two frames of data can be read. + for i in 0..AUDIO_CHUNK_SIZE { + ivshmem + .borrow_mut() + .writeb(PLAY_DADA_OFFSET + i as u64, AUDIO_DEFAULT_DATA[i as usize]); + } + + // update play header chunk_idx + ivshmem.borrow_mut().writew( + PLAY_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64, + 0, + ); + + thread::sleep(time::Duration::from_millis(1000)); + + // Reformat audio, change fmt_generation from 1 to 2. + let fmt_base = PLAY_BASE + offset_of!(ShmemStreamHeader, fmt) as u64; + ivshmem.borrow_mut().writel( + fmt_base + offset_of!(ShmemStreamFmt, fmt_generation) as u64, + 2, + ); + + ivshmem.borrow_mut().writew( + PLAY_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64, + 1, + ); + + thread::sleep(time::Duration::from_millis(1000)); + + // Stop the StratoVirt process before verifying data. Otherwise, + // audio data may not be updated to the file. + test_state.borrow_mut().stop(); + + let mut file = match File::open(playback_path.clone()) { + Ok(file) => file, + Err(e) => { + assert!(false, "{}", e); + return; + } + }; + + // Check first frame + read_and_check_data( + &mut file, + &AUDIO_DEFAULT_DATA[0..AUDIO_CHUNK_SIZE as usize], + AUDIO_CHUNK_SIZE, + ); + + // Check penultimate frame + read_and_check_data(&mut file, &[0; AUDIO_CHUNK_SIZE as usize], AUDIO_CHUNK_SIZE); + + // Check last frame + read_and_check_data( + &mut file, + &AUDIO_DEFAULT_DATA[0..AUDIO_CHUNK_SIZE as usize], + AUDIO_CHUNK_SIZE, + ); + + // No audio frame after audio format changed. + read_and_check_data(&mut file, &[0; AUDIO_CHUNK_SIZE as usize], 0); + + scream_tmp_clear(playback_path, record_path); +} + +/// scream device record audio. +/// TestStep: +/// 1. Init scream device and start recording. +/// 2. Check first frame audio. +/// 3. Check last frame audio. +/// 4. Stop VM. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn scream_record_basic_test() { + let pci_slot = 0x1; + let (playback_path, record_path) = get_audio_file_name(); + audio_data_init(playback_path.clone(), record_path.clone()); + let (ivshmem, test_state) = set_up( + IVSHMEM_DEFAULT_SIZE, + pci_slot, + playback_path.clone(), + record_path.clone(), + ); + ivshmem.borrow_mut().init(pci_slot); + + record_header_init(&mut ivshmem.borrow_mut()); + + let mut cnt = 0; + let mut chunk_idx = 0; + // Waiting for first chunk data write to ivshmem, then check first chunk data. + while cnt < POLL_MAX_CNT { + thread::sleep(time::Duration::from_millis(POLL_DELAY_MS >> 1)); + + // read chunk_idx + let offset = RECORD_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64; + chunk_idx = ivshmem.borrow_mut().readw(offset); + if chunk_idx > 0 { + break; + } + cnt += 1; + } + + assert_eq!(chunk_idx, 1); + + let audio_data = ivshmem.borrow_mut().readl(RECORD_DATA_OFFSET); + let mut check_data = 0; + read_data_u32( + &AUDIO_DEFAULT_DATA[0..AUDIO_CHUNK_SIZE as usize], + &mut check_data, + ); + + assert_eq!(audio_data, check_data); + + // Sleep 2S to wait last chunk data write to ivshmem, and check last chunk data. + thread::sleep(time::Duration::from_millis(2000)); + // read chunk_idx + let offset = RECORD_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64; + chunk_idx = ivshmem.borrow_mut().readw(offset); + + assert_eq!(chunk_idx as u32, AUDIO_CHUNK_CNT % (AUDIO_CHUNK_CNT - 1)); + + let audio_data = ivshmem.borrow_mut().readl(RECORD_DATA_OFFSET); + let mut check_data = 0; + let start = ((AUDIO_CHUNK_CNT - 1) * AUDIO_CHUNK_SIZE) as usize; + let end = (AUDIO_CHUNK_CNT * AUDIO_CHUNK_SIZE) as usize; + read_data_u32(&AUDIO_DEFAULT_DATA[start..end], &mut check_data); + + assert_eq!(audio_data, check_data); + + test_state.borrow_mut().stop(); + scream_tmp_clear(playback_path, record_path); +} + +/// scream device exception 001. +/// TestStep: +/// 1. Init scream device. +/// 2. Set buffer offset exceeded shared memory size. +/// 3. Check StratoVirt process. +/// 4. Stop VM. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn scream_exception_001() { + let pci_slot = 0x1; + let (playback_path, record_path) = get_audio_file_name(); + audio_data_init(playback_path.clone(), record_path.clone()); + let (ivshmem, test_state) = set_up( + IVSHMEM_DEFAULT_SIZE, + pci_slot, + playback_path.clone(), + record_path.clone(), + ); + ivshmem.borrow_mut().init(pci_slot); + + play_header_init(&mut ivshmem.borrow_mut()); + record_header_init(&mut ivshmem.borrow_mut()); + + // Setting playback and record buffer offset exceeded shared memory size. + let playback_offset = PLAY_BASE + offset_of!(ShmemStreamHeader, offset) as u64; + let mut buffer_offset = IVSHMEM_DEFAULT_SIZE * 1024 * 1024 + 1; + ivshmem.borrow_mut().writel(playback_offset, buffer_offset); + + let record_offset = RECORD_BASE + offset_of!(ShmemStreamHeader, offset) as u64; + buffer_offset = IVSHMEM_DEFAULT_SIZE * 1024 * 1024 + 2; + ivshmem.borrow_mut().writel(record_offset, buffer_offset); + + // Wait for 1s, query StratoVirt status. + thread::sleep(time::Duration::from_millis(1000)); + + let value = test_state + .borrow_mut() + .qmp("{\"execute\": \"query-status\"}"); + let status = value["return"]["status"].as_str().unwrap().to_string(); + assert_eq!(status, "running".to_string()); + + play_header_init(&mut ivshmem.borrow_mut()); + record_header_init(&mut ivshmem.borrow_mut()); + + // Setting chunk_idx > max_chunk + let mut chunk_offset = PLAY_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64; + ivshmem + .borrow_mut() + .writew(chunk_offset, AUDIO_CHUNK_CNT as u16); + chunk_offset = RECORD_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64; + ivshmem + .borrow_mut() + .writew(chunk_offset, AUDIO_CHUNK_CNT as u16); + + // Wait for 1s, query StratoVirt status. + thread::sleep(time::Duration::from_millis(1000)); + + let value = test_state + .borrow_mut() + .qmp("{\"execute\": \"query-status\"}"); + let status = value["return"]["status"].as_str().unwrap().to_string(); + assert_eq!(status, "running".to_string()); + + test_state.borrow_mut().stop(); + scream_tmp_clear(playback_path, record_path); +} + +/// scream device exception 002. +/// TestStep: +/// 1. Init scream device. +/// 2. Set invalid channels and channel_map. +/// 3. Send audio data. +/// 4. Stop VM. +/// 5. Check audio frames from audio file. +/// Expect: +/// 1/2/3/4/5: success. +#[test] +fn scream_exception_002() { + let pci_slot = 0x1; + let (playback_path, record_path) = get_audio_file_name(); + audio_data_init(playback_path.clone(), record_path.clone()); + let (ivshmem, test_state) = set_up( + IVSHMEM_DEFAULT_SIZE, + pci_slot, + playback_path.clone(), + record_path.clone(), + ); + ivshmem.borrow_mut().init(pci_slot); + + // Wait for 1s until the scream device is initialized and enters the polling state to + // prevent subsequent audio frame data loss. + thread::sleep(time::Duration::from_millis(1000)); + + play_header_init(&mut ivshmem.borrow_mut()); + + thread::sleep(time::Duration::from_millis(POLL_DELAY_MS)); + + // Setting channels and channel_map to 0. + let fmt_base = PLAY_BASE + offset_of!(ShmemStreamHeader, fmt) as u64; + ivshmem + .borrow_mut() + .writeb(fmt_base + offset_of!(ShmemStreamFmt, channels) as u64, 0); + ivshmem + .borrow_mut() + .writel(fmt_base + offset_of!(ShmemStreamFmt, channel_map) as u64, 0); + + // write one audio chunk + for i in 0..AUDIO_CHUNK_SIZE { + ivshmem.borrow_mut().writeb( + PLAY_DADA_OFFSET + (AUDIO_CHUNK_SIZE + i) as u64, + AUDIO_DEFAULT_DATA[i as usize], + ); + } + + // update play header chunk_idx + ivshmem.borrow_mut().writew( + PLAY_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64, + 1, + ); + + thread::sleep(time::Duration::from_millis(1000)); + + // Stop the StratoVirt process before verifying data. Otherwise, + // audio data may not be updated to the file. + test_state.borrow_mut().stop(); + + let mut file = match File::open(playback_path.clone()) { + Ok(file) => file, + Err(e) => { + assert!(false, "{}", e); + return; + } + }; + + read_and_check_data(&mut file, &[0; AUDIO_CHUNK_SIZE as usize], 0); +} -- Gitee From cb793cced96c32b381f5298abfccbb76cffff66c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 28 Mar 2023 16:00:49 +0800 Subject: [PATCH 0926/2187] scream: Add UT for pulseaudio Signed-off-by: Mingwang Li --- devices/src/misc/scream/pulseaudio.rs | 98 +++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 0fc9014dc..195b0f383 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -98,20 +98,25 @@ impl PulseStreamData { let pa_dir = dir.transform(); - let simple = Simple::new( - None, - name, - pa_dir, - None, - STREAM_NAME, - &ss, - Some(&channel_map), - Some(&buffer_attr), - ) - .unwrap_or_else(|e| panic!("PulseAudio init failed : {}", e)); + #[cfg(not(test))] + let simple = Some( + Simple::new( + None, + name, + pa_dir, + None, + STREAM_NAME, + &ss, + Some(&channel_map), + Some(&buffer_attr), + ) + .unwrap_or_else(|e| panic!("PulseAudio init failed : {}", e)), + ); + #[cfg(test)] + let simple = None; Self { - simple: Some(simple), + simple, ss, channel_map, buffer_attr, @@ -276,3 +281,72 @@ impl AudioInterface for PulseStreamData { true } } + +#[cfg(test)] +mod tests { + use pulse::{channelmap::Position, sample::Format}; + + use super::{ + PulseStreamData, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, + }; + use crate::misc::scream::{ScreamDirection, StreamData}; + + #[test] + fn test_channel_map_transfer() { + let mut pulse = PulseStreamData::init("test", ScreamDirection::Playback); + let mut test_data = StreamData::default(); + + // set 8: BC, 6: FLC, 4: BL, 2: FC, 0: FL + test_data.fmt.channels = 5; + test_data.fmt.channel_map = 0b1_0101_0101; + pulse.transfer_channel_map(&test_data.fmt); + + assert_eq!(pulse.channel_map.len(), 5); + let map = pulse.channel_map.get_mut(); + assert_eq!(map[0], Position::FrontLeft); + assert_eq!(map[1], Position::FrontCenter); + assert_eq!(map[2], Position::RearLeft); + assert_eq!(map[3], Position::FrontLeftOfCenter); + assert_eq!(map[4], Position::RearCenter); + + // The first 12 bits are set to 1. + test_data.fmt.channels = 12; + test_data.fmt.channel_map = 0b1111_1111_1111; + pulse.transfer_channel_map(&test_data.fmt); + + assert_eq!(pulse.channel_map.len(), 12); + let map = pulse.channel_map.get_mut(); + assert_eq!(map[11], Position::FrontCenter); + } + + #[test] + fn test_pulseaudio_fmt_update() { + let mut pulse = PulseStreamData::init("test", ScreamDirection::Playback); + let mut test_data = StreamData::default(); + + // Setting sample rate to AUDIO_SAMPLE_RATE_44KHZ, sample size to 16. + test_data.fmt.rate = WINDOWS_SAMPLE_BASE_RATE + 1; + test_data.fmt.size = 16; + + pulse.check_fmt_update(&test_data); + + assert_eq!(pulse.ss.rate, AUDIO_SAMPLE_RATE_44KHZ); + assert_eq!(pulse.ss.format, Format::S16le); + + // Setting sample rate to AUDIO_SAMPLE_RATE_48KHZ, sample size to 24. + test_data.fmt.rate = 1; + test_data.fmt.size = 24; + + pulse.check_fmt_update(&test_data); + + assert_eq!(pulse.ss.rate, AUDIO_SAMPLE_RATE_48KHZ); + assert_eq!(pulse.ss.format, Format::S24le); + + // Settint invalid sample size to 100. + test_data.fmt.size = 100; + + pulse.check_fmt_update(&test_data); + + assert_eq!(pulse.ss.rate, 0); + } +} -- Gitee From 29e786af7f3a1f3e91459202ba4dcf2c167182fc Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 10 Mar 2023 14:27:38 +0800 Subject: [PATCH 0927/2187] virtio-scsi: fix aio logical error in virtio-scsi Aio should be device's function instead of controller's function. Fix it. Signed-off-by: liuxiangdong --- virtio/src/device/scsi/bus.rs | 17 +-- virtio/src/device/scsi/controller.rs | 163 ++++++++++++++------------- virtio/src/device/scsi/disk.rs | 10 +- 3 files changed, 104 insertions(+), 86 deletions(-) diff --git a/virtio/src/device/scsi/bus.rs b/virtio/src/device/scsi/bus.rs index 234e75389..d41be210e 100644 --- a/virtio/src/device/scsi/bus.rs +++ b/virtio/src/device/scsi/bus.rs @@ -16,6 +16,8 @@ use std::io::Write; use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; +use byteorder::{BigEndian, ByteOrder}; +use log::{debug, error, info}; use crate::ScsiCntlr::{ ScsiCntlr, ScsiCompleteCb, ScsiXferMode, VirtioScsiCmdReq, VirtioScsiCmdResp, @@ -27,9 +29,7 @@ use crate::ScsiDisk::{ SCSI_TYPE_ROM, SECTOR_SHIFT, }; use address_space::AddressSpace; -use byteorder::{BigEndian, ByteOrder}; -use log::{debug, error, info}; -use util::aio::{Aio, AioCb, Iovec, OpCode}; +use util::aio::{AioCb, Iovec, OpCode}; /// Scsi Operation code. pub const TEST_UNIT_READY: u8 = 0x00; @@ -535,11 +535,7 @@ impl ScsiRequest { } } - pub fn execute( - &self, - aio: &mut Box>, - mut aiocb: AioCb, - ) -> Result { + pub fn execute(&self, mut aiocb: AioCb) -> Result { let dev_lock = self.dev.lock().unwrap(); let offset = match dev_lock.scsi_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, @@ -547,6 +543,8 @@ impl ScsiRequest { }; aiocb.offset = (self.cmd.lba << offset) as usize; + let mut aio = dev_lock.aio.as_ref().unwrap().lock().unwrap(); + for iov in self.virtioscsireq.lock().unwrap().iovec.iter() { let iovec = Iovec { iov_base: iov.iov_base, @@ -561,6 +559,7 @@ impl ScsiRequest { aiocb.opcode = OpCode::Fdsync; aio.submit_request(aiocb) .with_context(|| "Failed to process scsi request for flushing")?; + aio.flush_request()?; return Ok(0); } @@ -579,6 +578,8 @@ impl ScsiRequest { info!("xfer none"); } } + + aio.flush_request()?; Ok(0) } diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index 8197a5cf2..0a0256bfa 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -38,7 +38,7 @@ use machine_manager::{ config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; -use util::aio::{Aio, AioCb, AioEngine, Iovec, OpCode}; +use util::aio::{Aio, AioCb, Iovec, OpCode}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -313,30 +313,28 @@ impl VirtioDevice for ScsiCntlr { let queues_num = queues.len(); for cmd_queue in queues.iter().take(queues_num).skip(2) { - if let Some(bus) = &self.bus { - let mut cmd_handler = ScsiCmdHandler { - aio: None, - scsibus: bus.clone(), - queue: cmd_queue.clone(), - queue_evt: queue_evts.remove(0), - mem_space: mem_space.clone(), - interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, - device_broken: self.broken.clone(), - }; - - cmd_handler.aio = Some(cmd_handler.build_aio()?); + let bus = self.bus.as_ref().unwrap(); + let cmd_handler = ScsiCmdHandler { + scsibus: bus.clone(), + queue: cmd_queue.clone(), + queue_evt: queue_evts.remove(0), + mem_space: mem_space.clone(), + interrupt_cb: interrupt_cb.clone(), + driver_features: self.state.driver_features, + device_broken: self.broken.clone(), + }; - let notifiers = - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))); - register_event_helper( - notifiers, - self.config.iothread.as_ref(), - &mut self.deactivate_evts, - )?; - } else { - bail!("Scsi controller has no bus!"); + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))); + if notifiers.is_empty() { + bail!("Error in create scsi device aio!"); } + + register_event_helper( + notifiers, + self.config.iothread.as_ref(), + &mut self.deactivate_evts, + )?; } self.broken.store(false, Ordering::SeqCst); @@ -778,6 +776,23 @@ impl ScsiEventHandler { } } +fn complete_func(aiocb: &AioCb, ret: i64) -> Result<()> { + let complete_cb = &aiocb.iocompletecb; + let request = &aiocb.iocompletecb.req.lock().unwrap(); + let mut virtio_scsi_req = request.virtioscsireq.lock().unwrap(); + + virtio_scsi_req.resp.response = if ret < 0 { + VIRTIO_SCSI_S_FAILURE + } else { + VIRTIO_SCSI_S_OK + }; + + virtio_scsi_req.resp.status = GOOD; + virtio_scsi_req.resp.resid = 0; + virtio_scsi_req.resp.sense_len = 0; + virtio_scsi_req.complete(&complete_cb.mem_space) +} + pub struct ScsiCmdHandler { /// The scsi controller. scsibus: Arc>, @@ -791,8 +806,6 @@ pub struct ScsiCmdHandler { interrupt_cb: Arc, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, - /// Aio context. - aio: Option>>, /// Device is broken or not. device_broken: Arc, } @@ -801,6 +814,7 @@ impl EventNotifierHelper for ScsiCmdHandler { fn internal_notifiers(handler: Arc>) -> Vec { let mut notifiers = Vec::new(); + // Register event notifier for queue evt. let h_locked = handler.lock().unwrap(); let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { @@ -817,8 +831,22 @@ impl EventNotifierHelper for ScsiCmdHandler { }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); - // Register event notifier for aio. - if let Some(ref aio) = h_locked.aio { + // Register event notifier for device aio. + let locked_bus = h_locked.scsibus.lock().unwrap(); + for device in locked_bus.devices.values() { + let mut locked_device = device.lock().unwrap(); + + let aio = if let Ok(engine_aio) = + Aio::new(Arc::new(complete_func), locked_device.config.aio_type) + { + engine_aio + } else { + return Vec::new(); + }; + let dev_aio = Arc::new(Mutex::new(aio)); + let dev_aio_h = dev_aio.clone(); + locked_device.aio = Some(dev_aio.clone()); + let h_clone = handler.clone(); let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); @@ -826,19 +854,15 @@ impl EventNotifierHelper for ScsiCmdHandler { if h_lock.device_broken.load(Ordering::SeqCst) { return None; } - if let Some(aio) = &mut h_lock.aio { - if let Err(ref e) = aio.handle_complete() { - error!("Failed to handle aio, {:?}", e); - report_virtio_error( - h_lock.interrupt_cb.clone(), - h_lock.driver_features, - &h_lock.device_broken, - ); - } + if let Err(ref e) = h_lock.aio_complete_handler(&dev_aio_h) { + error!("Failed to handle aio {:?}", e); } None }); - notifiers.push(build_event_notifier(aio.fd.as_raw_fd(), h)); + notifiers.push(build_event_notifier( + (*dev_aio).lock().unwrap().fd.as_raw_fd(), + h, + )); } notifiers @@ -846,6 +870,17 @@ impl EventNotifierHelper for ScsiCmdHandler { } impl ScsiCmdHandler { + fn aio_complete_handler(&mut self, aio: &Arc>>) -> Result { + aio.lock().unwrap().handle_complete().map_err(|e| { + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + e + }) + } + fn handle_cmd(&mut self) -> Result<()> { let result = self.handle_cmd_request(); if result.is_err() { @@ -940,51 +975,25 @@ impl ScsiCmdHandler { self.mem_space.clone(), Arc::new(Mutex::new(scsi_req.clone())), ); - if let Some(ref mut aio) = self.aio { - let aiocb = AioCb { - direct, - req_align, - buf_align, - file_fd: disk_img.as_raw_fd(), - opcode: OpCode::Noop, - iovec: Vec::new(), - offset: 0, - nbytes: 0, - user_data: 0, - iocompletecb: scsicompletecb, - }; - scsi_req.execute(aio, aiocb)?; - aio.flush_request()?; - } + + let aiocb = AioCb { + direct, + req_align, + buf_align, + file_fd: disk_img.as_raw_fd(), + opcode: OpCode::Noop, + iovec: Vec::new(), + offset: 0, + nbytes: 0, + user_data: 0, + iocompletecb: scsicompletecb, + }; + scsi_req.execute(aiocb)?; } } Ok(()) } - - fn complete_func(aiocb: &AioCb, ret: i64) -> Result<()> { - let complete_cb = &aiocb.iocompletecb; - let request = &aiocb.iocompletecb.req.lock().unwrap(); - let mut virtio_scsi_req = request.virtioscsireq.lock().unwrap(); - - virtio_scsi_req.resp.response = if ret < 0 { - VIRTIO_SCSI_S_FAILURE - } else { - VIRTIO_SCSI_S_OK - }; - - virtio_scsi_req.resp.status = GOOD; - virtio_scsi_req.resp.resid = 0; - virtio_scsi_req.resp.sense_len = 0; - virtio_scsi_req.complete(&complete_cb.mem_space) - } - - fn build_aio(&self) -> Result>> { - Ok(Box::new(Aio::new( - Arc::new(Self::complete_func), - AioEngine::Off, - )?)) - } } #[derive(Clone)] diff --git a/virtio/src/device/scsi/disk.rs b/virtio/src/device/scsi/disk.rs index 9a7724b7c..86ab4eb20 100644 --- a/virtio/src/device/scsi/disk.rs +++ b/virtio/src/device/scsi/disk.rs @@ -18,7 +18,9 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; use crate::ScsiBus::ScsiBus; +use crate::ScsiCntlr::ScsiCompleteCb; use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; +use util::aio::Aio; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -86,7 +88,6 @@ impl ScsiDevState { } } -#[derive(Clone)] pub struct ScsiDevice { /// Configuration of the scsi device. pub config: ScsiDevConfig, @@ -108,8 +109,14 @@ pub struct ScsiDevice { pub parent_bus: Weak>, /// Drive backend files. drive_files: Arc>>, + /// Aio context. + pub aio: Option>>>, } +// SAFETY: the devices attached in one scsi controller will process IO in the same thread. +unsafe impl Send for ScsiDevice {} +unsafe impl Sync for ScsiDevice {} + impl ScsiDevice { pub fn new( config: ScsiDevConfig, @@ -127,6 +134,7 @@ impl ScsiDevice { scsi_type, parent_bus: Weak::new(), drive_files, + aio: None, } } -- Gitee From f0e9c1aee989effb77e690f0a4bf6ef1d72728ed Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 Mar 2023 19:54:30 +0800 Subject: [PATCH 0928/2187] scsi: remove scsi_cntlr_list from Stdmachine StdMachine should maintain the entire vm configuration. Scsi controller list should not be one of them because we can get the scsi controller by id from device list. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 123 +++++++++++++------------ machine/src/standard_vm/aarch64/mod.rs | 8 -- machine/src/standard_vm/mod.rs | 25 +---- machine/src/standard_vm/x86_64/mod.rs | 8 -- machine_manager/src/config/scsi.rs | 13 ++- virtio/src/device/scsi/controller.rs | 4 - virtio/src/lib.rs | 3 +- virtio/src/transport/virtio_pci.rs | 4 + 8 files changed, 82 insertions(+), 106 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1b13fdc20..69c4c92e9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -82,7 +82,6 @@ use virtio::{ ScsiCntlr, ScsiDisk, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; -use ScsiCntlr::ScsiCntlrMap; use ScsiDisk::{SCSI_TYPE_DISK, SCSI_TYPE_ROM}; pub trait MachineOps { @@ -312,11 +311,6 @@ pub trait MachineOps { /// Tcp, Unix, File and Unknown. fn get_migrate_info(&self) -> Incoming; - /// Get the Scsi Controller list. The map stores the mapping between scsi bus name and scsi controller. - fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { - None - } - /// Add net device. /// /// # Arguments @@ -679,12 +673,6 @@ pub trait MachineOps { let bus_name = format!("{}.0", device_cfg.id); ScsiBus::create_scsi_bus(&bus_name, &device)?; - if let Some(cntlr_list) = self.get_scsi_cntlr_list() { - let mut lock_cntlr_list = cntlr_list.lock().unwrap(); - lock_cntlr_list.insert(bus_name, device.clone()); - } else { - bail!("No scsi controller list found!"); - } let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) @@ -711,38 +699,43 @@ pub trait MachineOps { self.get_drive_files(), ))); - let cntlr_list = self - .get_scsi_cntlr_list() - .ok_or_else(|| anyhow!("Wrong! No scsi controller list found!"))?; - let cntlr_list_clone = cntlr_list.clone(); - let cntlr_list_lock = cntlr_list_clone.lock().unwrap(); - - let cntlr = cntlr_list_lock - .get(&device_cfg.bus) - .ok_or_else(|| anyhow!("Wrong! Bus {} not found in list", &device_cfg.bus))?; - - if let Some(bus) = &cntlr.lock().unwrap().bus { - if bus - .lock() - .unwrap() - .devices - .contains_key(&(device_cfg.target, device_cfg.lun)) - { - bail!("Wrong! Two scsi devices have the same scsi-id and lun"); - } - bus.lock() - .unwrap() - .devices - .insert((device_cfg.target, device_cfg.lun), device.clone()); - device.lock().unwrap().parent_bus = Arc::downgrade(bus); - } else { - bail!("Wrong! Controller has no bus {} !", &device_cfg.bus); + let pci_dev = self + .get_pci_dev_by_id_and_type(vm_config, Some(&device_cfg.cntlr), "virtio-scsi-pci") + .with_context(|| { + format!( + "Can not find scsi controller from pci bus {}", + device_cfg.cntlr + ) + })?; + let locked_pcidev = pci_dev.lock().unwrap(); + let virtio_pcidev = locked_pcidev + .as_any() + .downcast_ref::() + .unwrap(); + let virtio_device = virtio_pcidev.get_virtio_device().lock().unwrap(); + let cntlr = virtio_device + .as_any() + .downcast_ref::() + .unwrap(); + + let bus = cntlr.bus.as_ref().unwrap(); + if bus + .lock() + .unwrap() + .devices + .contains_key(&(device_cfg.target, device_cfg.lun)) + { + bail!("Wrong! Two scsi devices have the same scsi-id and lun"); } + bus.lock() + .unwrap() + .devices + .insert((device_cfg.target, device_cfg.lun), device.clone()); + device.lock().unwrap().parent_bus = Arc::downgrade(bus); device.lock().unwrap().realize()?; if let Some(bootindex) = device_cfg.boot_index { - let mut cntlr_locked = cntlr.lock().unwrap(); // Eg: OpenFirmware device path(virtio-scsi disk): // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3 // | | | | | | @@ -750,10 +743,10 @@ pub trait MachineOps { // | | | channel(unused, fixed 0). // | PCI slot,[function] holding SCSI controller. // PCI root as system bus port. - let dev_path = cntlr_locked.config.boot_prefix.as_mut().unwrap(); - let str = format! {"/channel@0/disk@{:x},{:x}", device_cfg.target, device_cfg.lun}; - dev_path.push_str(&str); - self.add_bootindex_devices(bootindex, dev_path, &device_cfg.id); + let prefix = cntlr.config.boot_prefix.as_ref().unwrap(); + let dev_path = + format! {"{}/channel@0/disk@{:x},{:x}", prefix, device_cfg.target, device_cfg.lun}; + self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); } Ok(()) } @@ -1117,29 +1110,39 @@ pub trait MachineOps { .with_context(|| "Failed to realize scream device") } - /// Get the corresponding device from the PCI bus based on the device name. + /// Get the corresponding device from the PCI bus based on the device id and device type name. /// /// # Arguments /// /// * `vm_config` - VM configuration. - /// * `name` - Device Name. - fn get_pci_dev_by_name( + /// * `id` - Device id. + /// * `dev_type` - Device type name. + fn get_pci_dev_by_id_and_type( &mut self, vm_config: &mut VmConfig, - name: &str, + id: Option<&str>, + dev_type: &str, ) -> Option>> { + let (id_check, id_str) = if id.is_some() { + (true, format! {"id={}", id.unwrap()}) + } else { + (false, "".to_string()) + }; + for dev in &vm_config.devices { - if dev.0.as_str() == name { - let cfg_args = dev.1.as_str(); - let bdf = get_pci_bdf(cfg_args).ok()?; - let devfn = (bdf.addr.0 << 3) + bdf.addr.1; - let pci_host = self.get_pci_host().ok()?; - let root_bus = pci_host.lock().unwrap().root_bus.clone(); - if let Some(pci_bus) = PciBus::find_bus_by_name(&root_bus, &bdf.bus) { - return pci_bus.lock().unwrap().get_device(0, devfn); - } else { - return None; - } + if dev.0.as_str() != dev_type || id_check && !dev.1.contains(&id_str) { + continue; + } + + let cfg_args = dev.1.as_str(); + let bdf = get_pci_bdf(cfg_args).ok()?; + let devfn = (bdf.addr.0 << 3) + bdf.addr.1; + let pci_host = self.get_pci_host().ok()?; + let root_bus = pci_host.lock().unwrap().root_bus.clone(); + if let Some(pci_bus) = PciBus::find_bus_by_name(&root_bus, &bdf.bus) { + return pci_bus.lock().unwrap().get_device(0, devfn); + } else { + return None; } } None @@ -1157,7 +1160,7 @@ pub trait MachineOps { let kbd = keyboard .realize() .with_context(|| "Failed to realize usb keyboard device")?; - let parent_dev_op = self.get_pci_dev_by_name(vm_config, "nec-usb-xhci"); + let parent_dev_op = self.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci"); if parent_dev_op.is_none() { bail!("Can not find parent device from pci bus"); } @@ -1185,7 +1188,7 @@ pub trait MachineOps { let tbt = tablet .realize() .with_context(|| "Failed to realize usb tablet device")?; - let parent_dev_op = self.get_pci_dev_by_name(vm_config, "nec-usb-xhci"); + let parent_dev_op = self.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci"); if parent_dev_op.is_none() { bail!("Can not find parent device from pci bus"); } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 918c29563..0ea701a4d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -75,7 +75,6 @@ use util::set_termi_canon_mode; use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; use crate::MachineOps; use anyhow::{anyhow, bail, Context, Result}; -use virtio::ScsiCntlr::ScsiCntlrMap; /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { @@ -148,8 +147,6 @@ pub struct StdMachine { boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, - /// Scsi Controller List. - scsi_cntlr_list: ScsiCntlrMap, /// Drive backend files. drive_files: Arc>>, } @@ -205,7 +202,6 @@ impl StdMachine { numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, - scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) } @@ -668,10 +664,6 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } - - fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { - Some(&self.scsi_cntlr_list) - } } impl AcpiBuilder for StdMachine { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index a2bf71fd1..a1c9943d5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -821,28 +821,11 @@ impl StdMachine { let bus_name = format!("{}.0", dev_cfg.id); ScsiBus::create_scsi_bus(&bus_name, &device)?; - if let Some(cntlr_list) = self.get_scsi_cntlr_list() { - let mut lock_cntlr_list = cntlr_list.lock().unwrap(); - lock_cntlr_list.insert(bus_name.clone(), device.clone()); - } else { - bail!("No scsi controller list found"); - } - - let result = - self.add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false); - let pci_dev = if let Err(ref e) = result { - // SAFETY: unwrap is safe because Standard machine always make sure it not return null. - self.get_scsi_cntlr_list() - .unwrap() - .lock() - .unwrap() - .remove(&bus_name); - bail!("Failed to add virtio scsi controller, error is {:?}", e); - } else { - result.unwrap() - }; - device.lock().unwrap().config.boot_prefix = pci_dev.lock().unwrap().get_dev_path(); + let virtio_pci_dev = self + .add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false) + .with_context(|| "Failed to add virtio scsi controller")?; + device.lock().unwrap().config.boot_prefix = virtio_pci_dev.lock().unwrap().get_dev_path(); Ok(()) } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index f1dc49e3f..6cfd8ed1b 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -64,7 +64,6 @@ use crate::{vm_state, MachineOps}; use anyhow::{anyhow, bail, Context, Result}; #[cfg(not(target_env = "musl"))] use ui::vnc; -use virtio::ScsiCntlr::ScsiCntlrMap; const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -123,8 +122,6 @@ pub struct StdMachine { boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, - /// Scsi Controller List. - scsi_cntlr_list: ScsiCntlrMap, /// Drive backend files. drive_files: Arc>>, } @@ -177,7 +174,6 @@ impl StdMachine { numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, - scsi_cntlr_list: Arc::new(Mutex::new(HashMap::new())), drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), }) } @@ -591,10 +587,6 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } - - fn get_scsi_cntlr_list(&mut self) -> Option<&ScsiCntlrMap> { - Some(&self.scsi_cntlr_list) - } } impl AcpiBuilder for StdMachine { diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 5a632cf6b..e6d72c228 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -161,8 +161,8 @@ pub struct ScsiDevConfig { pub path_on_host: String, /// Serial number of the scsi device. pub serial: Option, - /// Scsi bus which the scsi device attaches to. - pub bus: String, + /// Scsi controller which the scsi device attaches to. + pub cntlr: String, /// Scsi device can not do write operation. pub read_only: bool, /// If true, use direct access io. @@ -183,7 +183,7 @@ impl Default for ScsiDevConfig { id: "".to_string(), path_on_host: "".to_string(), serial: None, - bus: "".to_string(), + cntlr: "".to_string(), read_only: false, direct: true, aio_type: AioEngine::Native, @@ -232,7 +232,12 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result } if let Some(bus) = cmd_parser.get_value::("bus")? { - scsi_dev_cfg.bus = bus; + // Format "$parent_cntlr_name.0" is required by scsi bus. + let strs = bus.split('.').collect::>(); + if strs.len() != 2 || strs[1] != "0" { + bail!("Invalid scsi bus {}", bus); + } + scsi_dev_cfg.cntlr = strs[0].to_string(); } else { return Err(anyhow!(ConfigError::FieldIsMissing("bus", "scsi device"))); } diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index 0a0256bfa..4704ab3ce 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::cmp; -use std::collections::HashMap; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; @@ -60,9 +59,6 @@ pub const VIRTIO_SCSI_SENSE_DEFAULT_SIZE: usize = 96; /// Basic length of fixed format sense data. pub const SCSI_SENSE_LEN: u32 = 18; -/// The key is bus name, the value is the attached Scsi Controller. -pub type ScsiCntlrMap = Arc>>>>; - /// Control type codes. /// Task Management Function. pub const VIRTIO_SCSI_T_TMF: u32 = 0; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 8fd8e559b..84df32e51 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -60,6 +60,7 @@ use anyhow::bail; use machine_manager::config::ConfigCheck; use util::aio::mem_to_buf; use util::num_ops::write_u32; +use util::AsAny; use vmm_sys_util::eventfd::EventFd; /// Check if the bit of features is configured. @@ -306,7 +307,7 @@ pub type VirtioInterrupt = Box, bool) -> Result<()> + Send + Sync>; /// The trait for virtio device operations. -pub trait VirtioDevice: Send { +pub trait VirtioDevice: Send + AsAny { /// Realize low level device. fn realize(&mut self) -> Result<()>; diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 17d02ab99..04bba1d75 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1026,6 +1026,10 @@ impl VirtioPciDevice { true } + + pub fn get_virtio_device(&self) -> &Arc> { + &self.device + } } impl PciDevOps for VirtioPciDevice { -- Gitee From 64351432625c8df7ece972458f5db9a05c8c483f Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 29 Mar 2023 11:12:02 +0800 Subject: [PATCH 0929/2187] bugfix: free raw pointer memory Description A raw pointer memory was temporarily applied for during the registration of the interrupt routing table. However, no verification result and active release was obtained after the application, resulting in memory leakage. Signed-off-by: Mingwang Li --- hypervisor/src/kvm/interrupt.rs | 16 ++-------------- hypervisor/src/kvm/mod.rs | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 0d0e3ec0f..d0ad55ea9 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -10,14 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::mem::{align_of, size_of}; - +use anyhow::{Context, Result}; use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; -use util::bitmap::Bitmap; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; -use anyhow::{Context, Result}; +use util::bitmap::Bitmap; pub(crate) type IrqRoute = kvm_bindings::kvm_irq_routing; pub(crate) type IrqRouteEntry = kvm_bindings::kvm_irq_routing_entry; @@ -220,16 +218,6 @@ pub struct MsiVector { pub dev_id: u32, } -pub(crate) unsafe fn refact_vec_with_field(count: usize) -> *mut T { - let layout = std::alloc::Layout::from_size_align( - size_of::() + count * size_of::(), - std::cmp::max(align_of::(), align_of::()), - ) - .unwrap(); - - std::alloc::alloc(layout) as *mut T -} - #[cfg(test)] mod tests { use super::super::KVMFds; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index ffd12c6fc..a23277e33 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; +use std::mem::{align_of, size_of}; use std::sync::{Arc, Mutex}; use arc_swap::ArcSwap; @@ -23,9 +24,9 @@ use vmm_sys_util::{ eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; pub use interrupt::MsiVector; -use interrupt::{refact_vec_with_field, IrqRoute, IrqRouteEntry, IrqRouteTable}; +use interrupt::{IrqRoute, IrqRouteEntry, IrqRouteTable}; mod interrupt; @@ -132,19 +133,31 @@ impl KVMFds { pub fn commit_irq_routing(&self) -> Result<()> { let routes = self.irq_route_table.lock().unwrap().irq_routes.clone(); + let layout = std::alloc::Layout::from_size_align( + size_of::() + routes.len() * size_of::(), + std::cmp::max(align_of::(), align_of::()), + )?; + // Safe because data in `routes` is reliable. unsafe { - let mut irq_routing = refact_vec_with_field::(routes.len()); + let mut irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; + if irq_routing.is_null() { + bail!("Failed to alloc irq routing"); + } (*irq_routing).nr = routes.len() as u32; (*irq_routing).flags = 0; let entries: &mut [IrqRouteEntry] = (*irq_routing).entries.as_mut_slice(routes.len()); entries.copy_from_slice(&routes); - self.vm_fd + let ret = self + .vm_fd .as_ref() .unwrap() .set_gsi_routing(&*irq_routing) - .with_context(|| "Failed to set gsi routing") + .with_context(|| "Failed to set gsi routing"); + + std::alloc::dealloc(irq_routing as *mut u8, layout); + ret } } -- Gitee From b033228908f88fe2bd0dea0dd881e6a90d236c28 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 30 Mar 2023 21:52:19 +0800 Subject: [PATCH 0930/2187] MST: delete tmp file in scream_test Signed-off-by: Mingwang Li --- tests/mod_test/tests/scream_test.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index 4901507db..82c39a5d3 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -521,4 +521,6 @@ fn scream_exception_002() { }; read_and_check_data(&mut file, &[0; AUDIO_CHUNK_SIZE as usize], 0); + + scream_tmp_clear(playback_path, record_path); } -- Gitee From bc322a39000346db4fa6c86af79a81c9e334f48f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 31 Mar 2023 09:51:08 +0800 Subject: [PATCH 0931/2187] xhci: remove unused code Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_ring.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 376f92498..713a916be 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::fmt::{Display, Formatter, Result as FmtResult}; use std::sync::Arc; use anyhow::{bail, Result}; @@ -42,16 +41,6 @@ pub struct XhciTRB { } impl XhciTRB { - pub fn new() -> Self { - Self { - parameter: 0, - status: 0, - control: 0, - addr: 0, - ccs: true, - } - } - /// Get TRB type pub fn get_type(&self) -> TRBType { ((self.control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK).into() @@ -185,14 +174,7 @@ impl XhciRing { } } -impl Display for XhciRing { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!(f, "XhciRing dequeue {:x} ccs {}", self.dequeue, self.ccs) - } -} - /// Event Ring Segment Table Entry. See in the specs 6.5 Event Ring Segment Table. -#[derive(Clone)] pub struct XhciEventRingSeg { mem: Arc, pub addr_lo: u32, @@ -201,16 +183,6 @@ pub struct XhciEventRingSeg { pub rsvd: u32, } -impl Display for XhciEventRingSeg { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!( - f, - "XhciEventRingSeg addr_lo {:x} addr_hi {:x} size {} rsvd {}", - self.addr_lo, self.addr_hi, self.size, self.rsvd - ) - } -} - impl XhciEventRingSeg { pub fn new(mem: &Arc) -> Self { Self { -- Gitee From bed65965c97c8b34981e6138b2aa35b268fcca13 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 31 Mar 2023 09:55:21 +0800 Subject: [PATCH 0932/2187] usb: add some mst Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_regs.rs | 4 +- tests/mod_test/src/libdriver/usb.rs | 105 ++++++-- tests/mod_test/tests/usb_test.rs | 369 ++++++++++++++++++++++++++-- 3 files changed, 447 insertions(+), 31 deletions(-) diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 1c40f1bae..85a0f1a14 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -78,7 +78,7 @@ const CAP_EXT_USB_REVISION_3_0: u32 = 0x0300; pub const XHCI_OPER_REG_USBCMD: u64 = 0x00; pub const XHCI_OPER_REG_USBSTS: u64 = 0x04; pub const XHCI_OPER_REG_PAGESIZE: u64 = 0x08; -const XHCI_OPER_REG_DNCTRL: u64 = 0x14; +pub const XHCI_OPER_REG_DNCTRL: u64 = 0x14; const XHCI_OPER_REG_CMD_RING_CTRL_LO: u64 = 0x18; const XHCI_OPER_REG_CMD_RING_CTRL_HI: u64 = 0x1c; const XHCI_OPER_REG_DCBAAP_LO: u64 = 0x30; @@ -90,7 +90,7 @@ const XHCI_CRCR_CTRL_LO_MASK: u32 = 0xffffffc7; /// Command Ring Pointer Mask. const XHCI_CRCR_CRP_MASK: u64 = !0x3f; /// Notification Enable. -const XHCI_OPER_NE_MASK: u32 = 0xffff; +pub const XHCI_OPER_NE_MASK: u32 = 0xffff; /// Interrupter Registers. pub const XHCI_INTR_REG_IMAN: u64 = 0x00; pub const XHCI_INTR_REG_IMOD: u64 = 0x04; diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 4cb5748ee..8901e4aa7 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -40,8 +40,8 @@ use devices::usb::{ }, xhci_regs::{ XHCI_INTR_REG_ERDP_LO, XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, - XHCI_INTR_REG_IMAN, XHCI_INTR_REG_SIZE, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_PAGESIZE, - XHCI_OPER_REG_USBCMD, XHCI_OPER_REG_USBSTS, + XHCI_INTR_REG_IMAN, XHCI_INTR_REG_IMOD, XHCI_INTR_REG_SIZE, XHCI_OPER_REG_CONFIG, + XHCI_OPER_REG_PAGESIZE, XHCI_OPER_REG_USBCMD, XHCI_OPER_REG_USBSTS, }, TRBCCode, TRBType, TRB_SIZE, }, @@ -141,6 +141,17 @@ pub struct TestNormalTRB { } impl TestNormalTRB { + pub fn generate_normal_td(target: u32, len: u32) -> TestNormalTRB { + let mut trb = TestNormalTRB::default(); + trb.set_ioc_flag(true); + trb.set_isp_flag(true); + trb.set_idt_flag(true); + trb.set_interrupter_target(target); + trb.set_trb_type(TRBType::TrNormal as u32); + trb.set_trb_transfer_length(len); + trb + } + pub fn generate_setup_td(device_req: &UsbDeviceRequest) -> TestNormalTRB { let mut setup_trb = TestNormalTRB::default(); setup_trb.parameter = (device_req.length as u64) << 48 @@ -719,6 +730,16 @@ impl TestXhciPciDevice { .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x10) as u64); // AC64 = 1 assert_eq!(hccparams1 & 1, 1); + // doorbell offset + let db_offset = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x14) as u64); + assert_eq!(db_offset, 0x2000); + // runtime offset + let runtime_offset = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x18) as u64); + assert_eq!(runtime_offset, 0x1000); // HCCPARAMS2 let hccparams2 = self .pci_dev @@ -737,6 +758,11 @@ impl TestXhciPciDevice { .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x28) as u64); assert!(usb2_port & 0x400 == 0x400); + // extend capability end + let end = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x2c) as u64); + assert_eq!(end, 0); // USB 3.0 let usb3_version = self .pci_dev @@ -750,6 +776,11 @@ impl TestXhciPciDevice { .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x38) as u64); assert!(usb3_port & 0x400 == 0x400); + // extend capability end + let end = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x3c) as u64); + assert_eq!(end, 0); } pub fn init_max_device_slot_enabled(&mut self) { @@ -1096,12 +1127,7 @@ impl TestXhciPciDevice { // Queue TD (single TRB) with IDT=1 pub fn queue_direct_td(&mut self, slot_id: u32, ep_id: u32, len: u64) { - let mut trb = TestNormalTRB::default(); - trb.set_ioc_flag(true); - trb.set_isp_flag(true); - trb.set_idt_flag(true); - trb.set_trb_type(TRBType::TrNormal as u32); - trb.set_trb_transfer_length(len as u32); + let mut trb = TestNormalTRB::generate_normal_td(0, len as u32); self.queue_trb(slot_id, ep_id, &mut trb); } @@ -1114,7 +1140,7 @@ impl TestXhciPciDevice { // Queue TD (single TRB) pub fn queue_indirect_td(&mut self, slot_id: u32, ep_id: u32, sz: u64) -> u64 { - let mut trb = TestNormalTRB::default(); + let mut trb = TestNormalTRB::generate_normal_td(0, sz as u32); let ptr = self.allocator.borrow_mut().alloc(sz); self.pci_dev .pci_bus @@ -1123,10 +1149,7 @@ impl TestXhciPciDevice { .borrow_mut() .memset(ptr, sz, &[0]); trb.set_pointer(ptr); - trb.set_ioc_flag(true); - trb.set_isp_flag(true); - trb.set_trb_type(TRBType::TrNormal as u32); - trb.set_trb_transfer_length(sz as u32); + trb.set_idt_flag(false); self.queue_trb(slot_id, ep_id, &mut trb); ptr } @@ -1191,6 +1214,9 @@ impl TestXhciPciDevice { } pub fn set_transfer_pointer(&mut self, ptr: u64, slot_id: u32, ep_id: u32) { + if self.check_slot_ep_invalid(slot_id, ep_id) { + return; + } self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] .transfer_ring .pointer = ptr; @@ -1321,6 +1347,37 @@ impl TestXhciPciDevice { // enable INTE let value = self.interrupter_regs_read(intr_idx as u64, XHCI_INTR_REG_IMAN); self.interrupter_regs_write(intr_idx as u64, XHCI_INTR_REG_IMAN, value | IMAN_IE); + // set IMOD + self.interrupter_regs_write(intr_idx as u64, XHCI_INTR_REG_IMOD, 8); + let value = self.interrupter_regs_read(intr_idx as u64, XHCI_INTR_REG_IMOD); + assert_eq!(value, 8); + } + + pub fn recovery_endpoint(&mut self, slot_id: u32, ep_id: u32) { + self.reset_endpoint(slot_id, ep_id); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } + + pub fn test_invalie_device_request( + &mut self, + slot_id: u32, + request_type: u8, + request: u8, + value: u16, + ) { + let device_req = UsbDeviceRequest { + request_type, + request, + value, + index: 0, + length: 64, + }; + self.queue_device_reqeust(slot_id, &device_req); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + self.recovery_endpoint(slot_id, CONTROL_ENDPOINT_ID); } // Fake init memory. @@ -1330,11 +1387,19 @@ impl TestXhciPciDevice { } fn get_cycle_bit(&self, slot_id: u32, ep_id: u32) -> bool { + if self.check_slot_ep_invalid(slot_id, ep_id) { + return false; + } self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] .transfer_ring .cycle_bit } + fn check_slot_ep_invalid(&self, slot_id: u32, ep_id: u32) -> bool { + slot_id as usize >= self.xhci.device_slot.len() + || ep_id as usize > self.xhci.device_slot[slot_id as usize].endpoints.len() + } + fn increase_event_ring(&mut self, intr_idx: usize) { self.xhci.interrupter[intr_idx].trb_count -= 1; self.xhci.interrupter[intr_idx].er_pointer += TRB_SIZE as u64; @@ -1852,17 +1917,27 @@ impl TestUsbBuilder { } } - pub fn with_xhci(mut self, id: &str) -> Self { - let args = format!( + pub fn with_xhci_config(mut self, id: &str, port2: u32, port3: u32) -> Self { + let mut args = format!( "-device nec-usb-xhci,id={},bus=pcie.0,addr={}", id, XHCI_PCI_SLOT_NUM ); + if port2 != 0 { + args = format!("{},p2={}", args, port2); + } + if port3 != 0 { + args = format!("{},p3={}", args, port3); + } let args: Vec<&str> = args[..].split(' ').collect(); let mut args = args.into_iter().map(|s| s.to_string()).collect(); self.args.append(&mut args); self } + pub fn with_xhci(self, id: &str) -> Self { + self.with_xhci_config(id, 0, 0) + } + pub fn with_usb_keyboard(mut self, id: &str) -> Self { let args = format!("-device usb-kbd,id={}", id); let args: Vec<&str> = args[..].split(' ').collect(); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 788bf5b8c..4bca842ab 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -11,11 +11,13 @@ // See the Mulan PSL v2 for more details. use devices::usb::config::*; +use devices::usb::hid::HID_SET_REPORT; use devices::usb::xhci::xhci_controller::{ DwordOrder, XhciInputCtrlCtx, XhciSlotCtx, EP_RUNNING, SLOT_ADDRESSED, }; use devices::usb::xhci::xhci_regs::{ - XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_USBSTS, + XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, XHCI_INTR_REG_IMAN, XHCI_OPER_NE_MASK, + XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_DNCTRL, XHCI_OPER_REG_USBCMD, XHCI_OPER_REG_USBSTS, }; use devices::usb::xhci::{TRBCCode, TRBType, TRB_SIZE}; use devices::usb::UsbDeviceRequest; @@ -24,7 +26,9 @@ use mod_test::libdriver::usb::{ clear_iovec, qmp_send_key_event, qmp_send_multi_key_event, qmp_send_pointer_event, TestIovec, TestNormalTRB, TestUsbBuilder, CONTROL_ENDPOINT_ID, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, HID_POINTER_LEN, KEYCODE_NUM1, KEYCODE_SPACE, PCI_CLASS_PI, PRIMARY_INTERRUPTER_ID, - TD_TRB_LIMIT, XHCI_PCI_OPER_OFFSET, XHCI_PORTSC_OFFSET, + TD_TRB_LIMIT, XHCI_PCI_CAP_OFFSET, XHCI_PCI_DOORBELL_OFFSET, XHCI_PCI_FUN_NUM, + XHCI_PCI_OPER_OFFSET, XHCI_PCI_PORT_OFFSET, XHCI_PCI_RUNTIME_OFFSET, XHCI_PCI_SLOT_NUM, + XHCI_PORTSC_OFFSET, }; #[test] @@ -661,13 +665,10 @@ fn test_xhci_keyboard_invalid_value() { // Case 2: invalid cycle bit qmp_send_key_event(test_state.borrow_mut(), 2, true); - let mut trb = TestNormalTRB::default(); + let mut trb = TestNormalTRB::generate_normal_td(0, 8); let ptr = guest_allocator.borrow_mut().alloc(8); trb.set_pointer(ptr); - trb.set_ioc_flag(true); - trb.set_trb_type(TRBType::TrNormal as u32); trb.force_cycle = true; - trb.set_trb_transfer_length(8); xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); @@ -713,12 +714,7 @@ fn test_xhci_keyboard_invalid_value() { // Case 4: length over 8 when IDT = 1. qmp_send_key_event(test_state.borrow_mut(), 2, true); - let mut trb = TestNormalTRB::default(); - trb.set_ioc_flag(true); - trb.set_isp_flag(true); - trb.set_idt_flag(true); - trb.set_trb_type(TRBType::TrNormal as u32); - trb.set_trb_transfer_length(10); + let mut trb = TestNormalTRB::generate_normal_td(0, 10); xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); @@ -732,6 +728,16 @@ fn test_xhci_keyboard_invalid_value() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); xhci.test_keyboard_event(slot_id, test_state.clone()); + + // Case 5: invalid interrupter target + qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, false); + let mut trb = TestNormalTRB::generate_normal_td(100, HID_KEYBOARD_LEN as u32); + xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + // Host Controller Error + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + assert!(status & USB_STS_HCE == USB_STS_HCE); + test_state.borrow_mut().stop(); } @@ -926,6 +932,8 @@ fn test_xhci_keyboard_invalid_doorbell() { let port_id = 1; let slot_id = xhci.init_device(port_id); + // Kick invalid slot. + xhci.doorbell_write(10, HID_DEVICE_ENDPOINT_ID); qmp_send_key_event(test_state.borrow_mut(), 2, true); qmp_send_key_event(test_state.borrow_mut(), 3, true); @@ -993,6 +1001,15 @@ fn test_xhci_keyboard_controller_init_invalid_register() { // write invalid data. xhci.pci_dev.io_writel(xhci.bar_addr, 0, 0xffffffff); xhci.read_capability(); + let old_value = xhci + .pci_dev + .io_readl(xhci.bar_addr, XHCI_PCI_CAP_OFFSET as u64 + 0x2c); + xhci.pci_dev + .io_writel(xhci.bar_addr, XHCI_PCI_CAP_OFFSET as u64 + 0x2c, 0xffff); + let value = xhci + .pci_dev + .io_readl(xhci.bar_addr, XHCI_PCI_CAP_OFFSET as u64 + 0x2c); + assert_eq!(value, old_value); // Case 3: write invalid slot. xhci.pci_dev.io_writel( @@ -1017,6 +1034,28 @@ fn test_xhci_keyboard_controller_init_invalid_register() { XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, ); assert_ne!(status, 0xffff); + // Device Notify Control + xhci.pci_dev.io_writel( + xhci.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DNCTRL as u64, + 0x12345, + ); + let ndctrl = xhci.pci_dev.io_readl( + xhci.bar_addr, + XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DNCTRL as u64, + ); + assert_eq!(ndctrl, 0x12345 & XHCI_OPER_NE_MASK); + // invalid port offset. + let invalid_offset = 0x7; + xhci.pci_dev.io_writel( + xhci.bar_addr, + XHCI_PCI_PORT_OFFSET as u64 + invalid_offset, + 0xff, + ); + let invalid_offset = xhci + .pci_dev + .io_readl(xhci.bar_addr, XHCI_PCI_PORT_OFFSET as u64 + invalid_offset); + assert_eq!(invalid_offset, 0); xhci.init_device_context_base_address_array_pointer(); xhci.init_command_ring_dequeue_pointer(); @@ -1024,8 +1063,30 @@ fn test_xhci_keyboard_controller_init_invalid_register() { // Case 5: write invalid interrupter. xhci.interrupter_regs_write(0, XHCI_INTR_REG_ERSTSZ, 0); xhci.interrupter_regs_writeq(0, XHCI_INTR_REG_ERSTBA_LO, 0); - - // Case 6: invalid size + // micro frame index. + xhci.pci_dev + .io_writel(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64, 0xf); + let mf_index = xhci + .pci_dev + .io_readl(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64); + assert_eq!(mf_index, 0); + // invalid offset + xhci.pci_dev + .io_writel(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64 + 0x1008, 0xf); + let over_offset = xhci + .pci_dev + .io_readl(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64 + 0x1008); + assert_eq!(over_offset, 0); + + // Case 6: invalid doorbell + xhci.pci_dev + .io_writel(xhci.bar_addr, XHCI_PCI_DOORBELL_OFFSET as u64, 0xf); + let invalid_db = xhci + .pci_dev + .io_readl(xhci.bar_addr, XHCI_PCI_DOORBELL_OFFSET as u64); + assert_eq!(invalid_db, 0); + + // Case 7: invalid size xhci.init_event_ring(0, 1, 12); xhci.init_msix(); xhci.run(); @@ -1037,6 +1098,14 @@ fn test_xhci_keyboard_controller_init_invalid_register() { let port_id = 1; let slot_id = xhci.init_device(port_id); xhci.test_keyboard_event(slot_id, test_state.clone()); + + // Case 8: invalid PLS. + xhci.port_regs_write( + port_id, + XHCI_PORTSC_OFFSET, + PORTSC_LWS | 18 << PORTSC_PLS_SHIFT, + ); + test_state.borrow_mut().stop(); } @@ -2188,6 +2257,278 @@ fn test_xhci_tablet_device_init_control_command() { test_state.borrow_mut().stop(); } +#[test] +fn test_xhci_keyboard_invalid_value_002() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_keyboard("kbd") + .with_config("auto_run", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + // reset usb port + xhci.reset_port(port_id); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.enable_slot(); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let slot_id = evt.get_slot_id(); + xhci.address_device(slot_id, false, port_id); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + // Case: invalid port. + xhci.reset_port(2); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + + // Case: invalid slot id. + let mut trb = TestNormalTRB::default(); + trb.set_slot_id(128); + trb.set_trb_type(TRBType::CrResetDevice as u32); + xhci.queue_command(&mut trb); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // Case: only enable slot without endpoint. + let input_ctx_addr = xhci.configure_endpoint(slot_id, false); + let mut input_ctx = XhciInputCtrlCtx::default(); + input_ctx.add_flags = 1; + input_ctx.drop_flags = 0; + xhci.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // configure endpoint. + xhci.configure_endpoint(slot_id, false); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + // Case: stop invalid endpoint + xhci.stop_endpoint(slot_id, 32); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // Case: stop when not running + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ContextStateError as u32); + + // Case: reset invalid endpoint + xhci.reset_endpoint(slot_id, 32); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + // Case: reset endpoint when endpoint is not halted + xhci.reset_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ContextStateError as u32); + + // Case set tr dequeue for invalid endpoint + let old_ptr = xhci.get_transfer_pointer(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.set_tr_dequeue(old_ptr, slot_id, 32); + xhci.doorbell_write(0, 0); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + + xhci.test_keyboard_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_tablet_flush() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + // Case: stop endpoint when transfer is doing. + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); + // No data, the xhci report short packet. + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + // Stop command return success. + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_command_config() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci_config("xhci", 16, 16) + .with_usb_tablet("tbt") + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + xhci.init_max_device_slot_enabled(); + xhci.init_device_context_base_address_array_pointer(); + xhci.init_command_ring_dequeue_pointer(); + xhci.init_interrupter(); + xhci.run(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_reset() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + test_state + .borrow_mut() + .qmp("{\"execute\": \"system_reset\"}"); + test_state.borrow_mut().qmp_read(); + + let mut xhci = xhci.borrow_mut(); + xhci.init_host_controller(XHCI_PCI_SLOT_NUM, XHCI_PCI_FUN_NUM); + xhci.run(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_disable_interrupt() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let mut xhci = xhci.borrow_mut(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // Case: disable USB_CMD_INTE + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + xhci.queue_direct_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); + xhci.oper_regs_write(XHCI_OPER_REG_USBCMD, value & !USB_CMD_INTE); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); + xhci.oper_regs_write(XHCI_OPER_REG_USBCMD, value | USB_CMD_INTE); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + + // Case: disable IMAN_IE + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + xhci.queue_direct_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + let value = + xhci.interrupter_regs_read(PRIMARY_INTERRUPTER_ID as u64, XHCI_INTR_REG_IMAN as u64); + xhci.interrupter_regs_write( + PRIMARY_INTERRUPTER_ID as u64, + XHCI_INTR_REG_IMAN, + value & !IMAN_IE & IMAN_IP, + ); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + let value = xhci.interrupter_regs_read(PRIMARY_INTERRUPTER_ID as u64, XHCI_INTR_REG_IMAN); + xhci.interrupter_regs_write( + PRIMARY_INTERRUPTER_ID as u64, + XHCI_INTR_REG_IMAN, + value & !IMAN_IP | IMAN_IE, + ); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_direct(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + + test_state.borrow_mut().stop(); +} + +#[test] +fn test_xhci_tablet_invalid_request() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let mut xhci = xhci.borrow_mut(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // unsupport report request for tablet. + xhci.test_invalie_device_request(slot_id, USB_INTERFACE_CLASS_OUT_REQUEST, HID_SET_REPORT, 0); + // invalid descriptor type. + xhci.test_invalie_device_request( + slot_id, + USB_DEVICE_IN_REQUEST, + USB_REQUEST_GET_DESCRIPTOR, + 17 << 8, + ); + // invalid string index + xhci.get_string_descriptor(slot_id, 100); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + xhci.recovery_endpoint(slot_id, CONTROL_ENDPOINT_ID); + // invalid interface index + xhci.set_interface(slot_id, 5, 5); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + xhci.recovery_endpoint(slot_id, CONTROL_ENDPOINT_ID); + // invalid request type + xhci.test_invalie_device_request( + slot_id, + USB_INTERFACE_CLASS_OUT_REQUEST + 5, + HID_SET_REPORT, + 0, + ); + // invalid in request + xhci.test_invalie_device_request(slot_id, USB_INTERFACE_CLASS_IN_REQUEST, 0xf, 0); + // invalid out request + xhci.test_invalie_device_request(slot_id, USB_INTERFACE_CLASS_OUT_REQUEST, 0xf, 0); + // invalid interface request value + xhci.test_invalie_device_request( + slot_id, + USB_INTERFACE_IN_REQUEST, + USB_REQUEST_GET_DESCRIPTOR, + 20 << 8, + ); + // invalid interface request + xhci.test_invalie_device_request(slot_id, USB_INTERFACE_IN_REQUEST, 0xf, 0x22 << 8); + + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} + #[test] fn test_xhci_keyboard_tablet_basic() { let (xhci, test_state, _) = TestUsbBuilder::new() -- Gitee From a7a6d5f61578ea03c3f9743e53c5445e181c116a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 2 Apr 2023 14:19:15 +0800 Subject: [PATCH 0933/2187] MST: bugfix scream playback basic test occasional failed When the value of fmt is assigned after is_started, the back-end directly obtains the value of fmt after judging is_started. At this time, the front-end fmt is still in the process of assignment, and the obtained value is not the final value. When the backend checks fmt, the fmt is changed, and the chunk_idx value is reassigned. The data in the first frame is lost. So, the is_started must be setting at the end. Signed-off-by: Mingwang Li --- tests/mod_test/tests/scream_test.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index 82c39a5d3..4d06f6b6e 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -87,8 +87,6 @@ fn set_up( } fn stream_header_init(ivshmem: &mut TestIvshmemDev, base: u64, offset: u64) { - // set is_started - ivshmem.writel(base + offset_of!(ShmemStreamHeader, is_started) as u64, 1); // set chunk_idx ivshmem.writew(base + offset_of!(ShmemStreamHeader, chunk_idx) as u64, 0); // set max_chunks @@ -121,6 +119,9 @@ fn stream_header_init(ivshmem: &mut TestIvshmemDev, base: u64, offset: u64) { ivshmem.writeb(fmt_base + offset_of!(ShmemStreamFmt, channels) as u64, 2); // set channel_map ivshmem.writel(fmt_base + offset_of!(ShmemStreamFmt, channel_map) as u64, 3); + + // Setting is_started, it must be set at the end. Otherwise, the fmt data may not be updated in time. + ivshmem.writel(base + offset_of!(ShmemStreamHeader, is_started) as u64, 1); } fn play_header_init(ivshmem: &mut TestIvshmemDev) { -- Gitee From f80919a3df9a3f05fb1ec78bb5a66e582f03f5fe Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 2 Apr 2023 21:39:54 +0800 Subject: [PATCH 0934/2187] scream: Add memory barrier before read header info Otherwise we may read invalid fmt and chunk_idx. Signed-off-by: Keqian Zhu --- devices/src/misc/scream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index c17f1c075..4ad4afbc6 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -159,6 +159,7 @@ pub struct StreamData { impl StreamData { fn init(&mut self, header: &ShmemStreamHeader) { + fence(Ordering::Acquire); self.fmt = header.fmt; self.chunk_idx = header.chunk_idx; } -- Gitee From 97450690777a1d05cd887dad250c07d96a004e19 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Apr 2023 10:33:16 +0800 Subject: [PATCH 0935/2187] rng: Relax the read size limit Return the actual read size to the rng device. Signed-off-by: yezengruan --- virtio/src/device/rng.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index d2f664c47..76d219f51 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::min; use std::fs::File; use std::os::unix::fs::FileTypeExt; use std::os::unix::io::{AsRawFd, RawFd}; @@ -69,12 +70,20 @@ struct RngHandler { } impl RngHandler { - fn write_req_data(&self, in_iov: &[ElemIovec], buffer: &mut [u8]) -> Result<()> { + fn write_req_data(&self, in_iov: &[ElemIovec], buffer: &mut [u8], size: u32) -> Result<()> { let mut offset = 0_usize; for iov in in_iov { + if offset as u32 >= size { + break; + } self.mem_space - .write(&mut buffer[offset..].as_ref(), iov.addr, iov.len as u64) + .write( + &mut buffer[offset..].as_ref(), + iov.addr, + min(size - offset as u32, iov.len) as u64, + ) .with_context(|| "Failed to write request data for virtio rng")?; + offset += iov.len as usize; } @@ -93,7 +102,7 @@ impl RngHandler { if elem.desc_num == 0 { break; } - let size = + let mut size = get_req_data_size(&elem.in_iovec).with_context(|| "Failed to get request size")?; if let Some(leak_bucket) = self.leak_bucket.as_mut() { @@ -114,11 +123,12 @@ impl RngHandler { size as usize, 0, ); - if ret < 0 || ret as u32 != size { + if ret < 0 { bail!("Failed to read random file, size: {}", size); } + size = ret as u32; - self.write_req_data(&elem.in_iovec, &mut buffer)?; + self.write_req_data(&elem.in_iovec, &mut buffer, size)?; queue_lock .vring -- Gitee From 857db9d4f54e0dcc81ea218b6eadf534a69da439 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Apr 2023 10:42:45 +0800 Subject: [PATCH 0936/2187] rng: Limit the max read size to prevent oom The rng device needs to limit the read size to prevent the risk of oom. 1. Windows rng driver read size is limited PAGE_SIZE. 2. Linux rng dirver read size is limited SMP_CACHE_BYTES(64). Signed-off-by: yezengruan --- virtio/src/device/rng.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 76d219f51..21350d489 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -46,6 +46,7 @@ use crate::{ use anyhow::{anyhow, bail, Context, Result}; const QUEUE_NUM_RNG: usize = 1; +const RNG_SIZE_MAX: u32 = 1 << 20; fn get_req_data_size(in_iov: &[ElemIovec]) -> Result { let mut size = 0_u32; @@ -56,6 +57,8 @@ fn get_req_data_size(in_iov: &[ElemIovec]) -> Result { }; } + size = min(size, RNG_SIZE_MAX); + Ok(size) } -- Gitee From 2e5a51179a8ed695502090518be6ad86953a3552 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Sat, 1 Apr 2023 16:27:29 +0800 Subject: [PATCH 0937/2187] log: add default log output if we don't set logfile to stratovirt, it will output log messages to stderr. This can help libvirt write the log of stratovirt to preset file. Signed-off-by:mayunlong --- docs/config_guidebook.md | 1 + src/main.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2cc8703e8..8e6115046 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -227,6 +227,7 @@ You can enable StratoVirt's logging by: StratoVirt's log-level depends on env `STRATOVIRT_LOG_LEVEL`. StratoVirt supports five log-levels: `trace`, `debug`, `info`, `warn`, `error`. The default level is `error`. +If "-D" parameter is not set, logs are output to stderr by default. ### 1.10 Daemonize diff --git a/src/main.rs b/src/main.rs index 8ca8ed422..5dc3f1f8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,11 +94,8 @@ fn run() -> Result<()> { set_test_enabled(); } - if let Some(logfile_path) = cmd_args.value_of("display log") { - if logfile_path.is_empty() { - logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) - .with_context(|| "Failed to init logger.")?; - } else { + match cmd_args.value_of("display log") { + Some(ref logfile_path) if !logfile_path.is_empty() => { let logfile = std::fs::OpenOptions::new() .read(false) .write(true) @@ -110,6 +107,10 @@ fn run() -> Result<()> { logger::init_logger_with_env(Some(Box::new(logfile))) .with_context(|| "Failed to init logger.")?; } + _ => { + logger::init_logger_with_env(Some(Box::new(std::io::stderr()))) + .with_context(|| "Failed to init logger.")?; + } } std::panic::set_hook(Box::new(|panic_msg| { -- Gitee From dcd845c94ebe060780f05c2fce8723eb7f4594a4 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 20 Mar 2023 17:33:49 +0800 Subject: [PATCH 0938/2187] camera: add framework add framework codes for camera device Signed-off-by: Zhang Bo --- boot_loader/src/x86_64/bootparam.rs | 2 +- devices/src/camera_backend/demo.rs | 23 ++++++ devices/src/camera_backend/mod.rs | 92 ++++++++++++++++++++++ devices/src/camera_backend/v4l2.rs | 114 ++++++++++++++++++++++++++++ devices/src/lib.rs | 1 + devices/src/usb/camera.rs | 96 +++++++++++++++++++++++ devices/src/usb/mod.rs | 2 + 7 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 devices/src/camera_backend/demo.rs create mode 100644 devices/src/camera_backend/mod.rs create mode 100644 devices/src/camera_backend/v4l2.rs create mode 100644 devices/src/usb/camera.rs diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs index 45de66a05..ce023680f 100644 --- a/boot_loader/src/x86_64/bootparam.rs +++ b/boot_loader/src/x86_64/bootparam.rs @@ -41,7 +41,7 @@ pub struct RealModeKernelHeader { root_flags: u16, syssize: u32, ram_size: u16, - vid_mode: u16, + video_mode: u16, root_dev: u16, boot_flag: u16, jump: u16, diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs new file mode 100644 index 000000000..fd17f7f54 --- /dev/null +++ b/devices/src/camera_backend/demo.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! Demo backend for vCamera device, that helps for testing. + +use super::CamFmt; + +#[allow(dead_code)] +pub struct DemoHostDev { + buffer: Vec, // buffer that stores video frame + + hostfmt: CamFmt, // the combination of video formats that the hardware supports + pub cur_fmt: CamFmt, // the combination of video formats that we negotiated with the hardware +} diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs new file mode 100644 index 000000000..9b8574b0c --- /dev/null +++ b/devices/src/camera_backend/mod.rs @@ -0,0 +1,92 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! The abstract layer that connects different frontend & backend camera devices. +//! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait CameraHostdevOps. + +pub mod demo; +pub mod v4l2; + +use anyhow::Result; + +#[allow(dead_code)] +#[derive(Default)] +pub struct CamFmt { + // Basic 3 configurations: frame size, format, frame frequency. + basic_fmt: CamBasicFmt, + // Processing Unit Configuration: brightness, hue, etc. + pu_fmt: CamPUFmt, + // Camera Terminal Configuration: focus, exposure time, iris, etc. + lens_fmt: CamLensFmt, +} + +impl CamFmt { + pub fn new() -> Self { + Self { + ..Default::default() + } + } +} + +#[allow(dead_code)] +#[derive(Default)] +pub struct CamBasicFmt { + width: u16, + height: u16, + fps: u16, + fmttype: FmtType, +} + +#[allow(dead_code)] +#[derive(Default)] +pub struct CamPUFmt { + bright: u64, + contrast: u64, + hue: u64, + saturatio: u64, + // TODO: to be extended. +} + +#[allow(dead_code)] +#[derive(Default)] +pub struct CamLensFmt { + focus: u64, + zoom: u64, + // TODO: to be extended. +} + +#[allow(dead_code)] +enum FmtType { + Uncompressed = 0, + Mjpg, +} + +impl Default for FmtType { + fn default() -> Self { + FmtType::Uncompressed + } +} + +pub trait CameraHostdevOps: Send + Sync { + fn init(&self) -> Result<()>; + fn is_camera(&self) -> Result; + fn get_fmt(&self) -> Result<()>; + fn set_fmt(&self, fmt: u64) -> Result<()>; + fn set_ctl(&self) -> Result<()>; + + // Turn stream on to start to receive frame buffer. + fn video_stream_on(&self) -> Result<()>; + // The callback function used to poll on backend video devices, such as /dev/video0. + fn video_stream_run(&self) -> Result<()>; + // Turn stream off to end receiving frame buffer. + fn video_stream_off(&self) -> Result<()>; +} diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs new file mode 100644 index 000000000..4918bfbc4 --- /dev/null +++ b/devices/src/camera_backend/v4l2.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! V4L2 backend for vCamera device. /dev/videoX and VIDIOC_XX ioctls are used. + +use anyhow::Result; +use std::os::unix::io::RawFd; + +use super::CamFmt; +use super::CameraHostdevOps; + +#[allow(dead_code)] +pub struct V4l2HostDev { + device: String, // backend host device, eg. "/dev/video0" + pub fd: RawFd, // the fd for "device" + + buffer: Vec, // buffer that stores video frame + buf_addr: u64, // video buffer related hpa + buf_len: u64, // video buffer size + + hostfmt: CamFmt, // the combination of video formats that the hardware supports + pub cur_fmt: CamFmt, // the combination of video formats that we negotiated with the hardware +} + +#[allow(dead_code)] +impl V4l2HostDev { + pub fn new(device: String) -> Self { + V4l2HostDev { + device, + fd: -1, + buffer: vec![], + buf_addr: 0, + buf_len: 0, + hostfmt: CamFmt::new(), + cur_fmt: CamFmt::new(), + } + } + + pub fn realize(self) -> Result<()> { + Ok(()) + } + + // Below funcs are just encapsulation for v4l2 ioctls. + fn map_buffer() -> Result<()> { + Ok(()) + } + fn query_cap() -> Result<()> { + Ok(()) + } + fn query_buffer() -> Result<()> { + Ok(()) + } + fn g_fmt() -> Result<()> { + Ok(()) + } + fn s_fmt() -> Result<()> { + Ok(()) + } + fn require_buf() -> Result<()> { + Ok(()) + } + fn query_buf() -> Result<()> { + Ok(()) + } + fn qbuf() -> Result<()> { + Ok(()) + } + fn dqbuf() -> Result<()> { + Ok(()) + } + fn stream_on() -> Result<()> { + Ok(()) + } + fn stream_off() -> Result<()> { + Ok(()) + } +} + +impl CameraHostdevOps for V4l2HostDev { + fn init(&self) -> Result<()> { + Ok(()) + } + fn is_camera(&self) -> Result { + Ok(true) + } + fn get_fmt(&self) -> Result<()> { + Ok(()) + } + fn set_fmt(&self, _fmt: u64) -> Result<()> { + Ok(()) + } + fn set_ctl(&self) -> Result<()> { + Ok(()) + } + + fn video_stream_on(&self) -> Result<()> { + Ok(()) + } + fn video_stream_run(&self) -> Result<()> { + Ok(()) + } + fn video_stream_off(&self) -> Result<()> { + Ok(()) + } +} diff --git a/devices/src/lib.rs b/devices/src/lib.rs index e129547c0..842511b7d 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -17,6 +17,7 @@ //! - legacy devices, such as serial devices pub mod acpi; +pub mod camera_backend; mod interrupt_controller; pub mod legacy; pub mod misc; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs new file mode 100644 index 000000000..11a05cdb0 --- /dev/null +++ b/devices/src/usb/camera.rs @@ -0,0 +1,96 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! Emulated camera device that based on UVC(USB video class) protocol. + +use anyhow::Result; +use std::sync::{Mutex, Weak}; + +use super::xhci::xhci_controller::XhciDevice; +use crate::camera_backend::CameraHostdevOps; +use crate::usb::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket}; + +#[allow(dead_code)] +pub struct UsbCamera { + id: String, // uniq device id + usb_device: UsbDevice, // general usb device object + frame: Vec, // video frame data + max_pkt_size: u32, // the max size of the packet that can be seperated + vs_eps: Vec, // the endpoints that the VS uses + hostdev: Option>, // backend device, eg. v4l2, demo, etc. +} + +#[allow(dead_code)] +impl UsbCamera { + pub fn new() -> Self { + UsbCamera { + id: "".to_string(), + usb_device: UsbDevice::new(), + frame: Vec::new(), + max_pkt_size: 0, + vs_eps: Vec::new(), + hostdev: None, + } + } + + pub fn realize(&mut self) -> Result<()> { + Ok(()) + } + + fn read_backend_video_frame() -> Result<()> { + Ok(()) + } + + fn send_video_frame_to_guest() -> Result<()> { + Ok(()) + } + + fn set_control() -> Result<()> { + Ok(()) + } +} + +impl Default for UsbCamera { + fn default() -> Self { + Self::new() + } +} + +impl UsbDeviceOps for UsbCamera { + fn reset(&mut self) {} + + fn handle_control(&mut self, _packet: &mut UsbPacket, _device_req: &UsbDeviceRequest) {} + + fn handle_data(&mut self, _p: &mut UsbPacket) {} + + fn device_id(&self) -> String { + self.id.clone() + } + + fn set_controller(&mut self, _ctrl: Weak>) {} + + fn get_controller(&self) -> Option>> { + None + } + + fn get_wakeup_endpoint(&self) -> &UsbEndpoint { + self.usb_device.get_endpoint(true, 1) + } + + fn get_usb_device(&self) -> &UsbDevice { + &self.usb_device + } + + fn get_mut_usb_device(&mut self) -> &mut UsbDevice { + &mut self.usb_device + } +} diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 2c50c709f..44e745f7f 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -14,6 +14,8 @@ pub mod error; pub use anyhow::Result; pub use error::UsbError; +#[cfg(not(target_env = "musl"))] +pub mod camera; pub mod config; mod descriptor; pub mod hid; -- Gitee From 1677bf748616a61f46f8062d71b526f092389062 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Mar 2023 14:43:29 +0800 Subject: [PATCH 0939/2187] refactor: usb: unify duplicated codes for attaching usb dev to xhci keyboard and tablet has the same logic of attaching themselves to xhci controller, there're duplicated codes, unify them. Signed-off-by: Zhang Bo --- machine/src/lib.rs | 55 ++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 69c4c92e9..11e8bc16f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1148,18 +1148,18 @@ pub trait MachineOps { None } - /// Add usb keyboard. + /// Attach usb device to xhci controller. /// /// # Arguments /// - /// * `cfg_args` - Keyboard Configuration. + /// * `vm_config` - VM configuration. + /// * `usb_dev` - Usb device. #[cfg(not(target_env = "musl"))] - fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_keyboard(cfg_args)?; - let keyboard = UsbKeyboard::new(device_cfg.id); - let kbd = keyboard - .realize() - .with_context(|| "Failed to realize usb keyboard device")?; + fn attach_usb_to_xhci_controller( + &mut self, + vm_config: &mut VmConfig, + usb_dev: Arc>, + ) -> Result<()> { let parent_dev_op = self.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci"); if parent_dev_op.is_none() { bail!("Can not find parent device from pci bus"); @@ -1170,9 +1170,26 @@ pub trait MachineOps { if xhci_pci.is_none() { bail!("PciDevOps can not downcast to XhciPciDevice"); } - xhci_pci - .unwrap() - .attach_device(&(kbd as Arc>))?; + + xhci_pci.unwrap().attach_device(&(usb_dev))?; + + Ok(()) + } + + /// Add usb keyboard. + /// + /// # Arguments + /// + /// * `cfg_args` - Keyboard Configuration. + #[cfg(not(target_env = "musl"))] + fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let device_cfg = parse_usb_keyboard(cfg_args)?; + let keyboard = UsbKeyboard::new(device_cfg.id); + let kbd = keyboard + .realize() + .with_context(|| "Failed to realize usb keyboard device")?; + + self.attach_usb_to_xhci_controller(vm_config, kbd as Arc>)?; Ok(()) } @@ -1188,19 +1205,9 @@ pub trait MachineOps { let tbt = tablet .realize() .with_context(|| "Failed to realize usb tablet device")?; - let parent_dev_op = self.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci"); - if parent_dev_op.is_none() { - bail!("Can not find parent device from pci bus"); - } - let parent_dev = parent_dev_op.unwrap(); - let locked_parent_dev = parent_dev.lock().unwrap(); - let xhci_pci = locked_parent_dev.as_any().downcast_ref::(); - if xhci_pci.is_none() { - bail!("PciDevOps can not downcast to XhciPciDevice"); - } - xhci_pci - .unwrap() - .attach_device(&(tbt as Arc>))?; + + self.attach_usb_to_xhci_controller(vm_config, tbt as Arc>)?; + Ok(()) } -- Gitee From 5dce557d04460623a7c6948dbedfa5c9f82b1232 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Mar 2023 18:56:37 +0800 Subject: [PATCH 0940/2187] refactor: arg parse: fix redunt codes of checking args' max length Args' max length is checked for different devices' args, such as "id", too many duplicated codes. Add one function to simplify this. Signed-off-by: Zhang Bo --- machine_manager/src/config/balloon.rs | 12 ++---- machine_manager/src/config/boot_source.rs | 16 ++------ machine_manager/src/config/chardev.rs | 27 +++--------- machine_manager/src/config/drive.rs | 20 +++------ machine_manager/src/config/gpu.rs | 10 +---- machine_manager/src/config/iothread.rs | 11 +---- machine_manager/src/config/machine_config.rs | 9 +--- machine_manager/src/config/mod.rs | 18 +++++--- machine_manager/src/config/network.rs | 43 ++++---------------- machine_manager/src/config/pci.rs | 13 ++---- machine_manager/src/config/scsi.rs | 16 ++------ machine_manager/src/config/usb.rs | 10 +---- machine_manager/src/config/vfio.rs | 17 ++------ 13 files changed, 57 insertions(+), 165 deletions(-) diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index ca4f4f7d2..d2b931d7f 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -13,8 +13,8 @@ use anyhow::{anyhow, bail, Result}; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, pci_args_check, ConfigCheck, MAX_STRING_LENGTH}; -use crate::config::{CmdParser, ExBool, VmConfig}; +use super::{pci_args_check, ConfigCheck}; +use crate::config::{check_arg_too_long, CmdParser, ConfigError, ExBool, VmConfig}; const MEM_BUFFER_PERCENT_MIN: u32 = 20; const MEM_BUFFER_PERCENT_MAX: u32 = 80; @@ -35,12 +35,8 @@ pub struct BalloonConfig { impl ConfigCheck for BalloonConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "balloon id".to_string(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(&self.id, "balloon id")?; + if !self.auto_balloon { return Ok(()); } diff --git a/machine_manager/src/config/boot_source.rs b/machine_manager/src/config/boot_source.rs index f582907d1..d66f9bdcc 100644 --- a/machine_manager/src/config/boot_source.rs +++ b/machine_manager/src/config/boot_source.rs @@ -14,7 +14,7 @@ use std::fmt; use std::path::PathBuf; use super::error::ConfigError; -use crate::config::{ConfigCheck, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH}; +use crate::config::{check_arg_too_long, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; @@ -82,12 +82,7 @@ impl InitrdConfig { impl ConfigCheck for InitrdConfig { fn check(&self) -> Result<()> { - if self.initrd_file.to_str().unwrap().len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "initrd_file".to_string(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(self.initrd_file.to_str().unwrap(), "initrd_file")?; if !self.initrd_file.is_file() { return Err(anyhow!(ConfigError::UnRegularFile( @@ -110,12 +105,7 @@ pub struct KernelParams { impl ConfigCheck for KernelParams { fn check(&self) -> Result<()> { for param in self.params.clone() { - if param.value.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "kernel params".to_string(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(¶m.value, "kernel params")?; } Ok(()) diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 6eb164fbf..ca6705ff7 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -15,7 +15,9 @@ use log::error; use serde::{Deserialize, Serialize}; use super::{error::ConfigError, get_pci_bdf, pci_args_check, PciBdf}; -use crate::config::{CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH}; +use crate::config::{ + check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, +}; use crate::qmp::qmp_schema; const MAX_GUEST_CID: u64 = 4_294_967_295; @@ -50,12 +52,7 @@ pub struct ChardevConfig { impl ConfigCheck for ChardevConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "chardev id".to_string(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(&self.id, "chardev id")?; let len = match &self.backend { ChardevType::Socket { path, .. } => path.len(), @@ -386,12 +383,7 @@ pub struct VsockConfig { impl ConfigCheck for VsockConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "vsock id".to_string(), - MAX_STRING_LENGTH - ))); - } + check_arg_too_long(&self.id, "vsock id")?; if self.guest_cid < MIN_GUEST_CID || self.guest_cid >= MAX_GUEST_CID { return Err(anyhow!(ConfigError::IllegalValue( @@ -449,14 +441,7 @@ pub struct VirtioSerialInfo { impl ConfigCheck for VirtioSerialInfo { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "virtio-serial id".to_string(), - MAX_STRING_LENGTH, - ))); - } - - Ok(()) + check_arg_too_long(&self.id, "virtio-serial id") } } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index bb7b157cb..c69a23e4e 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -20,8 +20,8 @@ use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check}; use crate::config::{ - get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, - MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + check_arg_too_long, get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, + DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::qmp_schema; use util::aio::{aio_probe, AioEngine}; @@ -165,12 +165,8 @@ impl DriveConfig { impl ConfigCheck for DriveConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "Drive id".to_string(), - MAX_STRING_LENGTH - ))); - } + check_arg_too_long(&self.id, "Drive id")?; + if self.path_on_host.len() > MAX_PATH_LENGTH { return Err(anyhow!(ConfigError::StringLengthTooLong( "Drive device path".to_string(), @@ -206,13 +202,7 @@ impl ConfigCheck for DriveConfig { impl ConfigCheck for BlkDevConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "drive device id".to_string(), - MAX_STRING_LENGTH, - ))); - } - + check_arg_too_long(&self.id, "drive device id")?; if self.serial_num.is_some() && self.serial_num.as_ref().unwrap().len() > MAX_SERIAL_NUM { return Err(anyhow!(ConfigError::StringLengthTooLong( "drive serial number".to_string(), diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 3272effcd..151dd17aa 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use super::{error::ConfigError, M}; -use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; +use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; use anyhow::{anyhow, Result}; use log::warn; @@ -45,13 +45,7 @@ impl Default for GpuDevConfig { impl ConfigCheck for GpuDevConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "id".to_string(), - MAX_STRING_LENGTH - ))); - } - + check_arg_too_long(&self.id, "id")?; if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 || self.max_outputs == 0 { return Err(anyhow!(ConfigError::IllegalValue( "max_outputs".to_string(), diff --git a/machine_manager/src/config/iothread.rs b/machine_manager/src/config/iothread.rs index cc53992fd..69fa9a41b 100644 --- a/machine_manager/src/config/iothread.rs +++ b/machine_manager/src/config/iothread.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use super::error::ConfigError; -use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_STRING_LENGTH}; +use crate::config::{check_arg_too_long, CmdParser, ConfigCheck, VmConfig}; use anyhow::{anyhow, Result}; const MAX_IOTHREAD_NUM: usize = 8; @@ -26,14 +26,7 @@ pub struct IothreadConfig { impl ConfigCheck for IothreadConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "iothread id".to_string(), - MAX_STRING_LENGTH, - ))); - } - - Ok(()) + check_arg_too_long(&self.id, "iothread id") } } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index fd520f0db..c11c8d026 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize}; use super::error::ConfigError; use crate::config::{ - CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES, MAX_STRING_LENGTH, + check_arg_too_long, CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES, }; const DEFAULT_CPUS: u8 = 1; @@ -422,12 +422,7 @@ impl VmConfig { impl VmConfig { fn get_mem_zone_id(&self, cmd_parser: &CmdParser) -> Result { if let Some(id) = cmd_parser.get_value::("id")? { - if id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "id".to_string(), - MAX_STRING_LENGTH - ))); - } + check_arg_too_long(&id, "id")?; Ok(id) } else { Err(anyhow!(ConfigError::FieldIsMissing( diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ccfb76202..5149bfb7f 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -124,12 +124,8 @@ impl VmConfig { self.boot_source.check()?; self.machine_config.check()?; - if self.guest_name.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "name".to_string(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(&self.guest_name, "name")?; + if self.boot_source.kernel_file.is_none() && self.machine_config.mach_type == MachineType::MicroVm { @@ -620,6 +616,16 @@ impl FromStr for IntegerList { } } +pub fn check_arg_too_long(arg: &str, name: &str) -> Result<()> { + if arg.len() > MAX_STRING_LENGTH { + bail!(ConfigError::StringLengthTooLong( + name.to_string(), + MAX_STRING_LENGTH + )); + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 5fdbe031f..3657df266 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -18,8 +18,8 @@ use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check}; use crate::config::get_chardev_socket_path; use crate::config::{ - CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, - MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, + MAX_PATH_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::{qmp_schema, QmpChannel}; @@ -57,19 +57,8 @@ impl Default for NetDevcfg { impl ConfigCheck for NetDevcfg { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "id".to_string(), - MAX_STRING_LENGTH - ))); - } - - if self.ifname.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - self.ifname.clone(), - MAX_STRING_LENGTH - ))); - } + check_arg_too_long(&self.id, "id")?; + check_arg_too_long(&self.ifname, "ifname")?; if let Some(vhost_type) = self.vhost_type.as_ref() { if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" { @@ -130,29 +119,15 @@ impl Default for NetworkInterfaceConfig { impl ConfigCheck for NetworkInterfaceConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "id".to_string(), - MAX_STRING_LENGTH - ))); - } - - if self.host_dev_name.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - self.host_dev_name.clone(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(&self.id, "id")?; + check_arg_too_long(&self.host_dev_name, "host dev name")?; if self.mac.is_some() && !check_mac_address(self.mac.as_ref().unwrap()) { return Err(anyhow!(ConfigError::MacFormatError)); } - if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "iothread name".to_string(), - MAX_STRING_LENGTH, - ))); + if self.iothread.is_some() { + check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; } if self.socket_path.is_some() && self.socket_path.as_ref().unwrap().len() > MAX_PATH_LENGTH @@ -568,7 +543,7 @@ fn is_netdev_queues_valid(queues: u16) -> bool { #[cfg(test)] mod tests { - use crate::config::get_pci_bdf; + use crate::config::{get_pci_bdf, MAX_STRING_LENGTH}; use super::*; diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 0d74c6667..cc9bd6885 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -14,8 +14,8 @@ use anyhow::{anyhow, bail, Context, Result}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; -use super::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; -use crate::config::ExBool; +use super::{CmdParser, ConfigCheck}; +use crate::config::{check_arg_too_long, ExBool}; use util::num_ops::str_to_usize; /// Basic information of pci devices such as bus number, @@ -53,14 +53,7 @@ pub struct RootPortConfig { impl ConfigCheck for RootPortConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "root_port id".to_string(), - MAX_STRING_LENGTH, - ))); - } - - Ok(()) + check_arg_too_long(&self.id, "root_port id") } } diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index e6d72c228..ff4646e4a 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -14,7 +14,7 @@ use anyhow::{anyhow, bail, Result}; use super::{error::ConfigError, pci_args_check}; use crate::config::{ - CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + check_arg_too_long, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; use util::aio::AioEngine; @@ -63,18 +63,10 @@ impl Default for ScsiCntlrConfig { impl ConfigCheck for ScsiCntlrConfig { fn check(&self) -> Result<()> { - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "virtio-scsi-pci device id".to_string(), - MAX_STRING_LENGTH, - ))); - } + check_arg_too_long(&self.id, "virtio-scsi-pci device id")?; - if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "iothread name".to_string(), - MAX_STRING_LENGTH, - ))); + if self.iothread.is_some() { + check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; } if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u32 { diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index a02ae5f5e..612ac1c3f 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -13,7 +13,7 @@ use super::error::ConfigError; use anyhow::{anyhow, bail, Result}; -use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; +use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; /// XHCI contoller configuration. #[derive(Debug)] @@ -156,11 +156,5 @@ pub fn parse_usb_tablet(conf: &str) -> Result { } fn check_id(id: &str) -> Result<()> { - if id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "id".to_string(), - MAX_STRING_LENGTH - ))); - } - Ok(()) + check_arg_too_long(id, "id") } diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs index 5004a0aa0..1c9ce2b48 100644 --- a/machine_manager/src/config/vfio.rs +++ b/machine_manager/src/config/vfio.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use super::error::ConfigError; -use crate::config::{CmdParser, ConfigCheck, MAX_STRING_LENGTH}; +use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; use anyhow::{anyhow, Result}; #[derive(Default, Debug)] pub struct VfioConfig { @@ -22,19 +22,8 @@ pub struct VfioConfig { impl ConfigCheck for VfioConfig { fn check(&self) -> Result<()> { - if self.host.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "host".to_string(), - MAX_STRING_LENGTH - ))); - } - - if self.id.len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "id".to_string(), - MAX_STRING_LENGTH - ))); - } + check_arg_too_long(&self.host, "host")?; + check_arg_too_long(&self.id, "id")?; Ok(()) } -- Gitee From 1ac10868d5f7bddcd5aa4ba8b4bf938091036201 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Tue, 28 Mar 2023 20:24:38 +0800 Subject: [PATCH 0941/2187] refactor: usb: arg: check arg exist logic 1. refactor check_id() of usb devices and xhci, to let the check logic in just one function, rather than in 2 places. 2. change ConfigError::FieldIsMissing's args from &str to String to avoid lifetime issue. Signed-off-by: Zhang Bo --- devices/src/usb/xhci/xhci_pci.rs | 2 +- machine/src/lib.rs | 6 ++- machine_manager/src/config/chardev.rs | 37 ++++++++++----- machine_manager/src/config/drive.rs | 29 +++++++++--- machine_manager/src/config/error.rs | 2 +- machine_manager/src/config/fs.rs | 15 +++++-- machine_manager/src/config/machine_config.rs | 18 +++++--- machine_manager/src/config/mod.rs | 6 +++ machine_manager/src/config/network.rs | 10 ++++- machine_manager/src/config/numa.rs | 30 ++++++++++--- machine_manager/src/config/pci.rs | 10 ++++- machine_manager/src/config/rng.rs | 14 ++++-- machine_manager/src/config/sasl_auth.rs | 5 ++- machine_manager/src/config/scsi.rs | 19 +++++--- machine_manager/src/config/tls_creds.rs | 5 ++- machine_manager/src/config/usb.rs | 47 ++++++++------------ machine_manager/src/config/vfio.rs | 4 +- machine_manager/src/config/vnc.rs | 10 ++++- 18 files changed, 185 insertions(+), 84 deletions(-) diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index e43a1c40e..45389ded5 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -84,7 +84,7 @@ impl XhciPciDevice { devfn, xhci: XhciDevice::new(mem_space, config), dev_id: Arc::new(AtomicU16::new(0)), - name: config.id.to_string(), + name: config.id.clone().unwrap(), parent_bus, mem_region: Region::init_container_region(XHCI_PCI_CONFIG_LENGTH as u64), } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 11e8bc16f..50bc74103 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1184,7 +1184,8 @@ pub trait MachineOps { #[cfg(not(target_env = "musl"))] fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_keyboard(cfg_args)?; - let keyboard = UsbKeyboard::new(device_cfg.id); + // SAFETY: id is already checked not none in parse_usb_keyboard(). + let keyboard = UsbKeyboard::new(device_cfg.id.unwrap()); let kbd = keyboard .realize() .with_context(|| "Failed to realize usb keyboard device")?; @@ -1201,7 +1202,8 @@ pub trait MachineOps { #[cfg(not(target_env = "musl"))] fn add_usb_tablet(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_tablet(cfg_args)?; - let tablet = UsbTablet::new(device_cfg.id); + // SAFETY: id is already checked not none in parse_usb_tablet(). + let tablet = UsbTablet::new(device_cfg.id.unwrap()); let tbt = tablet .realize() .with_context(|| "Failed to realize usb tablet device")?; diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index ca6705ff7..9d5510700 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -112,7 +112,10 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { let chardev_id = if let Some(chardev_id) = cmd_parser.get_value::("id")? { chardev_id } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "chardev"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "chardev".to_string() + ))); }; let backend = cmd_parser.get_value::("")?; let path = cmd_parser.get_value::("path")?; @@ -146,8 +149,8 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { } } else { return Err(anyhow!(ConfigError::FieldIsMissing( - "path", - "socket-type chardev" + "path".to_string(), + "socket-type chardev".to_string() ))); } } @@ -156,8 +159,8 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { ChardevType::File(path) } else { return Err(anyhow!(ConfigError::FieldIsMissing( - "path", - "file-type chardev" + "path".to_string(), + "file-type chardev".to_string() ))); } } @@ -169,7 +172,10 @@ pub fn parse_chardev(cmd_parser: CmdParser) -> Result { } } } else { - return Err(anyhow!(ConfigError::FieldIsMissing("backend", "chardev"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "backend".to_string(), + "chardev".to_string() + ))); }; Ok(ChardevConfig { @@ -260,15 +266,18 @@ pub fn parse_virtconsole(vm_config: &mut VmConfig, config_args: &str) -> Result< chardev } else { return Err(anyhow!(ConfigError::FieldIsMissing( - "chardev", - "virtconsole" + "chardev".to_string(), + "virtconsole".to_string() ))); }; let id = if let Some(chardev_id) = cmd_parser.get_value::("id")? { chardev_id } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "virtconsole"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "virtconsole".to_string() + ))); }; if let Some(char_dev) = vm_config.chardev.remove(&chardev_name) { @@ -414,13 +423,19 @@ pub fn parse_vsock(vsock_config: &str) -> Result { let id = if let Some(vsock_id) = cmd_parser.get_value::("id")? { vsock_id } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "vsock"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "vsock".to_string() + ))); }; let guest_cid = if let Some(cid) = cmd_parser.get_value::("guest-cid")? { cid } else { - return Err(anyhow!(ConfigError::FieldIsMissing("guest-cid", "vsock"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "guest-cid".to_string(), + "vsock".to_string() + ))); }; let vhost_fd = cmd_parser.get_value::("vhostfd")?; diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index c69a23e4e..02e867ac1 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -270,13 +270,19 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { if let Some(id) = cmd_parser.get_value::("id")? { drive.id = id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "blk"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "blk".to_string() + ))); } if let Some(file) = cmd_parser.get_value::("file")? { drive.path_on_host = file; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("file", "blk"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "file".to_string(), + "blk".to_string() + ))); } if let Some(read_only) = cmd_parser.get_value::("readonly")? { @@ -330,7 +336,10 @@ pub fn parse_blk( let blkdrive = if let Some(drive) = cmd_parser.get_value::("drive")? { drive } else { - return Err(anyhow!(ConfigError::FieldIsMissing("drive", "blk"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "drive".to_string(), + "blk".to_string() + ))); }; if let Some(iothread) = cmd_parser.get_value::("iothread")? { @@ -400,8 +409,8 @@ pub fn parse_vhost_user_blk_pci( blkdevcfg.chardev = Some(chardev); } else { return Err(anyhow!(ConfigError::FieldIsMissing( - "chardev", - "vhost-user-blk-pci" + "chardev".to_string(), + "vhost-user-blk-pci".to_string() ))); }; @@ -619,7 +628,10 @@ impl VmConfig { if let Some(drive_path) = cmd_parser.get_value::("file")? { pflash.path_on_host = drive_path; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("file", "pflash"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "file".to_string(), + "pflash".to_string() + ))); } if let Some(read_only) = cmd_parser.get_value::("readonly")? { @@ -629,7 +641,10 @@ impl VmConfig { if let Some(unit_id) = cmd_parser.get_value::("unit")? { pflash.unit = unit_id as usize; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("unit", "pflash"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "unit".to_string(), + "pflash".to_string() + ))); } pflash.check()?; diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index d315e9986..cd946973c 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -41,7 +41,7 @@ pub enum ConfigError { #[error("Unknown device type: {0}!")] UnknownDeviceType(String), #[error("\'{0}\' is missing for \'{1}\' device.")] - FieldIsMissing(&'static str, &'static str), + FieldIsMissing(String, String), #[error("{0} must >{} {1} and <{} {3}.", if *.2 {"="} else {""}, if *.4 {"="} else {""})] IllegalValue(String, u64, bool, u64, bool), #[error("{0} must {}{} {3}.", if *.1 {">"} else {"<"}, if *.2 {"="} else {""})] diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index eae8da3ca..21487e6d1 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -82,13 +82,19 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { if let Some(tag) = cmd_parser.get_value::("tag")? { fs_cfg.tag = tag; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("tag", "virtio-fs"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "tag".to_string(), + "virtio-fs".to_string() + ))); } if let Some(id) = cmd_parser.get_value::("id")? { fs_cfg.id = id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "virtio-fs"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "virtio-fs".to_string() + ))); } if let Some(name) = cmd_parser.get_value::("chardev")? { @@ -105,7 +111,10 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { bail!("Chardev {:?} not found or is in use", &name); } } else { - return Err(anyhow!(ConfigError::FieldIsMissing("chardev", "virtio-fs"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "chardev".to_string(), + "virtio-fs".to_string() + ))); } fs_cfg.check()?; diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index c11c8d026..4458803eb 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -287,7 +287,10 @@ impl VmConfig { } else if let Some(mem_size) = cmd_parser.get_value::("size")? { memory_unit_conversion(&mem_size)? } else { - return Err(anyhow!(ConfigError::FieldIsMissing("size", "memory"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "size".to_string(), + "memory".to_string() + ))); }; self.machine_config.mem_config.mem_size = mem; @@ -324,7 +327,10 @@ impl VmConfig { } cpu } else { - return Err(anyhow!(ConfigError::FieldIsMissing("cpus", "smp"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "cpus".to_string(), + "smp".to_string() + ))); }; let sockets = smp_read_and_check(&cmd_parser, "sockets", 0)?; @@ -426,8 +432,8 @@ impl VmConfig { Ok(id) } else { Err(anyhow!(ConfigError::FieldIsMissing( - "id", - "memory-backend-ram" + "id".to_string(), + "memory-backend-ram".to_string() ))) } } @@ -438,8 +444,8 @@ impl VmConfig { Ok(size) } else { Err(anyhow!(ConfigError::FieldIsMissing( - "size", - "memory-backend-ram" + "size".to_string(), + "memory-backend-ram".to_string() ))) } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 5149bfb7f..47bd93766 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -626,6 +626,12 @@ pub fn check_arg_too_long(arg: &str, name: &str) -> Result<()> { Ok(()) } +pub fn check_arg_nonexist(arg: Option, name: &str, device: &str) -> Result<()> { + arg.with_context(|| ConfigError::FieldIsMissing(name.to_string(), device.to_string()))?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 3657df266..518cb5982 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -185,7 +185,10 @@ fn parse_netdev(cmd_parser: CmdParser) -> Result { if let Some(net_id) = cmd_parser.get_value::("id")? { net.id = net_id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "netdev"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "netdev".to_string() + ))); } if let Some(ifname) = cmd_parser.get_value::("ifname")? { net.ifname = ifname; @@ -280,7 +283,10 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result("netdev")? { devname } else { - return Err(anyhow!(ConfigError::FieldIsMissing("netdev", "net"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "netdev".to_string(), + "net".to_string() + ))); }; let netid = if let Some(id) = cmd_parser.get_value::("id")? { id diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 485992d80..b44918ad1 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -135,7 +135,10 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { } config.numa_id = node_id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("nodeid", "numa"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "nodeid".to_string(), + "numa".to_string() + ))); } if let Some(mut cpus) = cmd_parser .get_value::("cpus") @@ -150,12 +153,18 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { cpus.sort_unstable(); config.cpus = cpus; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("cpus", "numa"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "cpus".to_string(), + "numa".to_string() + ))); } if let Some(mem_dev) = cmd_parser.get_value::("memdev")? { config.mem_dev = mem_dev; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("memdev", "numa"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "memdev".to_string(), + "numa".to_string() + ))); } Ok(config) @@ -184,7 +193,10 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { } src } else { - return Err(anyhow!(ConfigError::FieldIsMissing("src", "numa"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "src".to_string(), + "numa".to_string() + ))); }; if let Some(dst) = cmd_parser.get_value::("dst")? { if dst >= MAX_NODES { @@ -198,7 +210,10 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { } dist.destination = dst; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("dst", "numa"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "dst".to_string(), + "numa".to_string() + ))); } if let Some(val) = cmd_parser.get_value::("val")? { if val < MIN_NUMA_DISTANCE { @@ -216,7 +231,10 @@ pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { dist.distance = val; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("val", "numa"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "val".to_string(), + "numa".to_string() + ))); } Ok((numa_id, dist)) diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index cc9bd6885..8a2258c38 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -146,7 +146,10 @@ pub fn parse_root_port(rootport_cfg: &str) -> Result { let mut root_port = RootPortConfig::default(); let port = cmd_parser.get_value::("port")?; if port.is_none() { - return Err(anyhow!(ConfigError::FieldIsMissing("port", "rootport"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "port".to_string(), + "rootport".to_string() + ))); } // Safety: as port is validated non-none at the previous line, it's safe to unwrap() it root_port.port = str_to_usize(port.unwrap())? as u8; @@ -156,7 +159,10 @@ pub fn parse_root_port(rootport_cfg: &str) -> Result { if let Some(id) = cmd_parser.get_value::("id")? { root_port.id = id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "rootport"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "rootport".to_string() + ))); } root_port.multifunction = if let Some(multi_func) = cmd_parser.get_value::("multifunction")? { diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 09e238120..a42b3af9c 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -84,7 +84,10 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result("rng")? { rng_id } else { - return Err(anyhow!(ConfigError::FieldIsMissing("rng", "rng"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "rng".to_string(), + "rng".to_string() + ))); }; rng_cfg.id = if let Some(rng_id) = cmd_parser.get_value::("id")? { @@ -131,14 +134,17 @@ pub fn parse_rng_obj(object_args: &str) -> Result { let id = if let Some(obj_id) = cmd_params.get_value::("id")? { obj_id } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "rng-object"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "rng-object".to_string() + ))); }; let filename = if let Some(name) = cmd_params.get_value::("filename")? { name } else { return Err(anyhow!(ConfigError::FieldIsMissing( - "filename", - "rng-object" + "filename".to_string(), + "rng-object".to_string() ))); }; let rng_obj_cfg = RngObjConfig { id, filename }; diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs index 00e056c0c..7e74bee9e 100644 --- a/machine_manager/src/config/sasl_auth.rs +++ b/machine_manager/src/config/sasl_auth.rs @@ -34,7 +34,10 @@ impl VmConfig { if let Some(id) = cmd_parser.get_value::("id")? { saslauth.id = id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "vnc sasl_auth"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "vnc sasl_auth".to_string() + ))); } if let Some(identity) = cmd_parser.get_value::("identity")? { diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index ff4646e4a..908522d85 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -126,8 +126,8 @@ pub fn parse_scsi_controller( cntlr_cfg.id = id; } else { return Err(anyhow!(ConfigError::FieldIsMissing( - "id", - "virtio scsi pci" + "id".to_string(), + "virtio scsi pci".to_string() ))); } @@ -206,7 +206,10 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result let scsi_drive = if let Some(drive) = cmd_parser.get_value::("drive")? { drive } else { - return Err(anyhow!(ConfigError::FieldIsMissing("drive", "scsi device"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "drive".to_string(), + "scsi device".to_string() + ))); }; if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { @@ -220,7 +223,10 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result if let Some(id) = cmd_parser.get_value::("id")? { scsi_dev_cfg.id = id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "scsi device"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "scsi device".to_string() + ))); } if let Some(bus) = cmd_parser.get_value::("bus")? { @@ -231,7 +237,10 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result } scsi_dev_cfg.cntlr = strs[0].to_string(); } else { - return Err(anyhow!(ConfigError::FieldIsMissing("bus", "scsi device"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "bus".to_string(), + "scsi device".to_string() + ))); } if let Some(target) = cmd_parser.get_value::("scsi-id")? { diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index 6a5d409d1..8d31ad4c3 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -40,7 +40,10 @@ impl VmConfig { if let Some(id) = cmd_parser.get_value::("id")? { tlscred.id = id; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("id", "vnc tls_creds"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "vnc tls_creds".to_string() + ))); } if let Some(dir) = cmd_parser.get_value::("dir")? { diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 612ac1c3f..2f41d01fb 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -11,14 +11,14 @@ // See the Mulan PSL v2 for more details. use super::error::ConfigError; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, Result}; -use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; +use crate::config::{check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck}; /// XHCI contoller configuration. #[derive(Debug)] pub struct XhciConfig { - pub id: String, + pub id: Option, // number of usb2.0 ports pub p2: Option, // number of usb3.0 ports @@ -28,7 +28,7 @@ pub struct XhciConfig { impl XhciConfig { fn new() -> Self { XhciConfig { - id: String::new(), + id: None, p2: None, p3: None, } @@ -59,7 +59,7 @@ impl XhciConfig { impl ConfigCheck for XhciConfig { fn check(&self) -> Result<()> { - check_id(&self.id)?; + check_id(self.id.clone(), "xhci controller")?; self.check_ports() } } @@ -75,11 +75,7 @@ pub fn parse_xhci(conf: &str) -> Result { .push("p3"); cmd_parser.parse(conf)?; let mut dev = XhciConfig::new(); - if let Some(id) = cmd_parser.get_value::("id")? { - dev.id = id; - } else { - bail!("id is none for usb xhci"); - } + dev.id = cmd_parser.get_value::("id")?; if let Some(p2) = cmd_parser.get_value::("p2")? { dev.p2 = Some(p2); @@ -95,18 +91,18 @@ pub fn parse_xhci(conf: &str) -> Result { #[derive(Debug)] pub struct UsbKeyboardConfig { - pub id: String, + pub id: Option, } impl UsbKeyboardConfig { fn new() -> Self { - UsbKeyboardConfig { id: String::new() } + UsbKeyboardConfig { id: None } } } impl ConfigCheck for UsbKeyboardConfig { fn check(&self) -> Result<()> { - check_id(&self.id) + check_id(self.id.clone(), "usb-keyboard") } } @@ -115,29 +111,26 @@ pub fn parse_usb_keyboard(conf: &str) -> Result { cmd_parser.push("").push("id").push("bus").push("port"); cmd_parser.parse(conf)?; let mut dev = UsbKeyboardConfig::new(); - if let Some(id) = cmd_parser.get_value::("id")? { - dev.id = id; - } else { - bail!("id is none for usb keyboard"); - } + dev.id = cmd_parser.get_value::("id")?; + dev.check()?; Ok(dev) } #[derive(Debug)] pub struct UsbTabletConfig { - pub id: String, + pub id: Option, } impl UsbTabletConfig { fn new() -> Self { - UsbTabletConfig { id: String::new() } + UsbTabletConfig { id: None } } } impl ConfigCheck for UsbTabletConfig { fn check(&self) -> Result<()> { - check_id(&self.id) + check_id(self.id.clone(), "usb-tablet") } } @@ -146,15 +139,13 @@ pub fn parse_usb_tablet(conf: &str) -> Result { cmd_parser.push("").push("id").push("bus").push("port"); cmd_parser.parse(conf)?; let mut dev = UsbTabletConfig::new(); - if let Some(id) = cmd_parser.get_value::("id")? { - dev.id = id; - } else { - bail!("id is none for usb tablet"); - } + dev.id = cmd_parser.get_value::("id")?; + dev.check()?; Ok(dev) } -fn check_id(id: &str) -> Result<()> { - check_arg_too_long(id, "id") +fn check_id(id: Option, device: &str) -> Result<()> { + check_arg_nonexist(id.clone(), "id", device)?; + check_arg_too_long(&id.unwrap(), "id") } diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs index 1c9ce2b48..2c6e545d6 100644 --- a/machine_manager/src/config/vfio.rs +++ b/machine_manager/src/config/vfio.rs @@ -52,8 +52,8 @@ pub fn parse_vfio(vfio_config: &str) -> Result { if vfio.host.is_empty() && vfio.sysfsdev.is_empty() { return Err(anyhow!(ConfigError::FieldIsMissing( - "host nor sysfsdev", - "vfio" + "host nor sysfsdev".to_string(), + "vfio".to_string() ))); } diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index 61a40adc8..2e9b15880 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -51,7 +51,10 @@ impl VmConfig { if let Some(addr) = cmd_parser.get_value::("")? { parse_port(&mut vnc_config, addr)?; } else { - return Err(anyhow!(ConfigError::FieldIsMissing("ip", "port"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "ip".to_string(), + "port".to_string() + ))); } // VNC Security Type. @@ -76,7 +79,10 @@ impl VmConfig { fn parse_port(vnc_config: &mut VncConfig, addr: String) -> Result<()> { let v: Vec<&str> = addr.split(':').collect(); if v.len() != 2 { - return Err(anyhow!(ConfigError::FieldIsMissing("ip", "port"))); + return Err(anyhow!(ConfigError::FieldIsMissing( + "ip".to_string(), + "port".to_string() + ))); } let ip = v[0] .parse::() -- Gitee From 0cb327cb0d670c04b07e928eb3b85d8439e8745b Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 11:43:40 +0800 Subject: [PATCH 0942/2187] camera: add cmdline support no function yet, just support cmdline parsing/check. Signed-off-by: Zhang Bo --- Cargo.lock | 30 +++++------ devices/src/camera_backend/v4l2.rs | 12 +++-- devices/src/usb/camera.rs | 42 ++++++++++++--- devices/src/usb/mod.rs | 1 + machine/Cargo.toml | 2 + machine/src/lib.rs | 27 +++++++++- machine_manager/Cargo.toml | 4 +- machine_manager/src/config/error.rs | 2 + machine_manager/src/config/usb.rs | 84 ++++++++++++++++++++++++++++- 9 files changed, 174 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd23340e2..4604fd7fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -297,12 +297,9 @@ checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "heck" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hex" @@ -478,6 +475,8 @@ dependencies = [ "pci", "serde", "serde_json", + "strum", + "strum_macros", "sysbus", "thiserror", "ui", @@ -800,6 +799,12 @@ dependencies = [ "base64", ] +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + [[package]] name = "ryu" version = "1.0.11" @@ -947,19 +952,20 @@ dependencies = [ [[package]] name = "strum" -version = "0.20.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" [[package]] name = "strum_macros" -version = "0.20.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", "proc-macro2", "quote", + "rustversion", "syn", ] @@ -1033,12 +1039,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - [[package]] name = "unicode-xid" version = "0.2.4" diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 4918bfbc4..f27129fcc 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -12,8 +12,9 @@ //! V4L2 backend for vCamera device. /dev/videoX and VIDIOC_XX ioctls are used. -use anyhow::Result; -use std::os::unix::io::RawFd; +use anyhow::{anyhow, Context, Result}; +use std::fs::File; +use std::os::unix::io::{IntoRawFd, RawFd}; use super::CamFmt; use super::CameraHostdevOps; @@ -45,7 +46,12 @@ impl V4l2HostDev { } } - pub fn realize(self) -> Result<()> { + pub fn realize(&mut self) -> Result<()> { + // open /dev/videoX + self.fd = File::open(self.device.clone()) + .with_context(|| anyhow!("failed to open video device"))? + .into_raw_fd(); + Ok(()) } diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 11a05cdb0..ed6511120 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -12,11 +12,13 @@ //! Emulated camera device that based on UVC(USB video class) protocol. -use anyhow::Result; -use std::sync::{Mutex, Weak}; +use anyhow::{bail, Result}; +use machine_manager::config::{CamBackendType, UsbCameraConfig}; +use std::sync::{Arc, Mutex, Weak}; +use super::config::USB_SPEED_HIGH; use super::xhci::xhci_controller::XhciDevice; -use crate::camera_backend::CameraHostdevOps; +use crate::camera_backend::{v4l2::V4l2HostDev, CameraHostdevOps}; use crate::usb::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket}; #[allow(dead_code)] @@ -26,23 +28,49 @@ pub struct UsbCamera { frame: Vec, // video frame data max_pkt_size: u32, // the max size of the packet that can be seperated vs_eps: Vec, // the endpoints that the VS uses + backend_type: CamBackendType, // backend interface, eg. v4l2 + backend_path: String, // backend interface file, eg. /dev/video0 hostdev: Option>, // backend device, eg. v4l2, demo, etc. } #[allow(dead_code)] impl UsbCamera { - pub fn new() -> Self { + pub fn new(config: UsbCameraConfig) -> Self { UsbCamera { - id: "".to_string(), + id: config.id.unwrap(), usb_device: UsbDevice::new(), frame: Vec::new(), max_pkt_size: 0, vs_eps: Vec::new(), + backend_type: config.backend, + backend_path: config.path.unwrap(), hostdev: None, } } - pub fn realize(&mut self) -> Result<()> { + pub fn realize(mut self) -> Result>> { + self.set_hostdev()?; + + self.usb_device.reset_usb_endpoint(); + self.usb_device.speed = USB_SPEED_HIGH; + //TODO: init descriptor. + let camera = Arc::new(Mutex::new(self)); + + Ok(camera) + } + + fn set_hostdev(&mut self) -> Result<()> { + match self.backend_type { + CamBackendType::V4l2 => { + let mut hostdev = V4l2HostDev::new(self.backend_path.clone()); + hostdev.realize()?; + self.hostdev = Some(Box::new(hostdev)); + } + _ => { + bail!("Unsupported backend yet."); + } + } + Ok(()) } @@ -61,7 +89,7 @@ impl UsbCamera { impl Default for UsbCamera { fn default() -> Self { - Self::new() + Self::new(UsbCameraConfig::new()) } } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 44e745f7f..3efd692eb 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -19,6 +19,7 @@ pub mod camera; pub mod config; mod descriptor; pub mod hid; + #[cfg(not(target_env = "musl"))] pub mod keyboard; #[cfg(not(target_env = "musl"))] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index e44ba56d4..5640312aa 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -16,6 +16,8 @@ vmm-sys-util = "0.11.0" vfio-bindings = "0.3" thiserror = "1.0" anyhow = "1.0" +strum = "0.24.1" +strum_macros = "0.24.3" acpi = { path = "../acpi" } address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 50bc74103..4c61a8d5a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -50,7 +50,8 @@ use devices::InterruptController; #[cfg(not(target_env = "musl"))] use devices::usb::{ - keyboard::UsbKeyboard, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, + camera::UsbCamera, keyboard::UsbKeyboard, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, + UsbDeviceOps, }; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ @@ -63,7 +64,9 @@ use machine_manager::config::{ MAX_VIRTIO_QUEUE, }; #[cfg(not(target_env = "musl"))] -use machine_manager::config::{parse_gpu, parse_usb_keyboard, parse_usb_tablet, parse_xhci}; +use machine_manager::config::{ + parse_gpu, parse_usb_camera, parse_usb_keyboard, parse_usb_tablet, parse_xhci, +}; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; @@ -1213,6 +1216,22 @@ pub trait MachineOps { Ok(()) } + /// Add usb camera. + /// + /// # Arguments + /// + /// * `cfg_args` - Camera Configuration. + #[cfg(not(target_env = "musl"))] + fn add_usb_camera(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let device_cfg = parse_usb_camera(cfg_args)?; + let camera = UsbCamera::new(device_cfg); + let camera = camera.realize()?; + + self.attach_usb_to_xhci_controller(vm_config, camera as Arc>)?; + + Ok(()) + } + /// Add peripheral devices. /// /// # Arguments @@ -1308,6 +1327,10 @@ pub trait MachineOps { self.add_usb_tablet(vm_config, cfg_args)?; } #[cfg(not(target_env = "musl"))] + "usb-camera" => { + self.add_usb_camera(vm_config, cfg_args)?; + } + #[cfg(not(target_env = "musl"))] "virtio-gpu-pci" => { self.add_virtio_pci_gpu(cfg_args)?; } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 967dfa54f..4548df300 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -15,8 +15,8 @@ serde_json = "1.0" vmm-sys-util = "0.11.0" hex = "0.4.3" serde = { version = "1.0", features = ["derive"] } -strum = "0.20" -strum_macros = "0.20" +strum = "0.24.1" +strum_macros = "0.24.3" once_cell = "1.13.0" thiserror = "1.0" anyhow = "1.0" diff --git a/machine_manager/src/config/error.rs b/machine_manager/src/config/error.rs index cd946973c..694c9ef3d 100644 --- a/machine_manager/src/config/error.rs +++ b/machine_manager/src/config/error.rs @@ -62,4 +62,6 @@ pub enum ConfigError { UnitIdError(String, usize, usize), #[error("Directory {0} does not exist")] DirNotExist(String), + #[error("File {0} does not exist")] + FileNotExist(String), } diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 2f41d01fb..89488985c 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -10,8 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{path::Path, str::FromStr}; + use super::error::ConfigError; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; +use strum::{EnumCount, IntoEnumIterator}; +use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use crate::config::{check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck}; @@ -145,7 +149,85 @@ pub fn parse_usb_tablet(conf: &str) -> Result { Ok(dev) } +#[derive(Clone, Copy, Debug, EnumCountMacro, EnumIter)] +pub enum CamBackendType { + V4l2, + Demo, +} + +impl FromStr for CamBackendType { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + for i in CamBackendType::iter() { + if s == CAM_OPT_STR_BACKEND_TYPES[i as usize] { + return Ok(i); + } + } + + Err(()) + } +} + +pub const CAM_OPT_STR_BACKEND_TYPES: [&str; CamBackendType::COUNT] = ["v4l2", "demo"]; + +pub fn parse_usb_camera(conf: &str) -> Result { + let mut cmd_parser = CmdParser::new("usb-camera"); + cmd_parser.push("").push("id").push("backend").push("path"); + cmd_parser.parse(conf)?; + + let mut dev = UsbCameraConfig::new(); + dev.id = cmd_parser.get_value::("id")?; + dev.backend = cmd_parser.get_value::("backend")?.unwrap(); + dev.path = cmd_parser.get_value::("path")?; + + dev.check()?; + Ok(dev) +} + fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; check_arg_too_long(&id.unwrap(), "id") } + +#[derive(Clone, Debug)] +pub struct UsbCameraConfig { + pub id: Option, + pub backend: CamBackendType, + pub path: Option, +} + +impl UsbCameraConfig { + pub fn new() -> Self { + UsbCameraConfig { + id: None, + backend: CamBackendType::Demo, + path: None, + } + } +} + +impl Default for UsbCameraConfig { + fn default() -> Self { + Self::new() + } +} + +impl ConfigCheck for UsbCameraConfig { + fn check(&self) -> Result<()> { + // Note: backend has already been checked during args parsing. + check_id(self.id.clone(), "usb-camera")?; + check_camera_path(self.path.clone()) + } +} + +fn check_camera_path(path: Option) -> Result<()> { + check_arg_nonexist(path.clone(), "path", "usb-camera")?; + + let path = path.unwrap(); + if !Path::new(&path).exists() { + bail!(ConfigError::FileNotExist(path)); + } + + Ok(()) +} -- Gitee From a12f891890cd0ea9dd7f4f30681677deb98b7ad8 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 13:51:19 +0800 Subject: [PATCH 0943/2187] camera: support handle usb control Signed-off-by: Zhang Bo --- Cargo.lock | 2 + devices/Cargo.toml | 2 + devices/src/usb/camera.rs | 111 ++++++++++++++++++++++++++++++++++++-- devices/src/usb/config.rs | 2 + 4 files changed, 113 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4604fd7fd..edcba47c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,6 +222,8 @@ dependencies = [ "pci", "serde", "serial_test", + "strum", + "strum_macros", "sysbus", "thiserror", "ui", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 6f5b54769..287976fae 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -17,6 +17,8 @@ vmm-sys-util = "0.11.0" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" once_cell = "1.9.0" +strum = "0.24.1" +strum_macros = "0.24.3" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index ed6511120..e848cfce5 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -13,13 +13,27 @@ //! Emulated camera device that based on UVC(USB video class) protocol. use anyhow::{bail, Result}; -use machine_manager::config::{CamBackendType, UsbCameraConfig}; +use log::{debug, error}; +use once_cell::sync::Lazy; + use std::sync::{Arc, Mutex, Weak}; +use strum::EnumCount; +use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use super::config::USB_SPEED_HIGH; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{v4l2::V4l2HostDev, CameraHostdevOps}; -use crate::usb::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket}; +use crate::usb::config::*; +use crate::usb::descriptor::*; +use crate::usb::{ + UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, +}; +use machine_manager::config::{CamBackendType, UsbCameraConfig}; + +// CRC16 of "STRATOVIRT" +const UVC_VENDOR_ID: u16 = 0xB74C; +// The first 4 chars of "VIDEO", 5 substitutes V. +const UVC_PRODUCT_ID: u16 = 0x51DE; #[allow(dead_code)] pub struct UsbCamera { @@ -33,6 +47,74 @@ pub struct UsbCamera { hostdev: Option>, // backend device, eg. v4l2, demo, etc. } +#[derive(Debug, EnumCountMacro, EnumIter)] +enum UsbCameraStringIDs { + Invalid = 0, + Manufacture, + Product, + SerialNumber, + Configuration, + Iad, + VideoControl, + InputTerminal, + OutputTerminal, + SelectUnit, + ProcessingUnit, + VideoStreaming, +} + +const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ + "", + "StratoVirt", + "USB Camera", + "1", + "USB Camera Configuration", + "USB Camera Interface Associated Description", + "Video Control", + "Input Terminal", + "Output Terminal", + "Select Unit", + "Processing Unit", + "Video Streaming", +]; + +/// UVC Camera device descriptor +static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDevice { + device_desc: UsbDeviceDescriptor { + bLength: USB_DT_DEVICE_SIZE, + bDescriptorType: USB_DT_DEVICE, + idVendor: UVC_VENDOR_ID, + idProduct: UVC_PRODUCT_ID, + bcdDevice: 0, + iManufacturer: UsbCameraStringIDs::Manufacture as u8, + iProduct: UsbCameraStringIDs::Product as u8, + iSerialNumber: UsbCameraStringIDs::SerialNumber as u8, + bcdUSB: 0x0200, // TODO: support 3.0 Super + bDeviceClass: USB_CLASS_MISCELLANEOUS, + // Refer to https://www.usb.org/defined-class-codes for details. + bDeviceSubClass: 2, + bDeviceProtocol: 1, // Interface Association + bMaxPacketSize0: 64, + bNumConfigurations: 1, + }, + configs: vec![Arc::new(UsbDescConfig { + config_desc: UsbConfigDescriptor { + bLength: USB_DT_CONFIG_SIZE, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: 0, + bNumInterfaces: 2, + bConfigurationValue: 1, + iConfiguration: UsbCameraStringIDs::Configuration as u8, + bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, + bMaxPower: 50, + }, + // TODO: Add IAD descriptor, including VC&VS + interfaces: vec![], + })], + }) +}); + #[allow(dead_code)] impl UsbCamera { pub fn new(config: UsbCameraConfig) -> Self { @@ -53,7 +135,10 @@ impl UsbCamera { self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_HIGH; - //TODO: init descriptor. + let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_CAMERA.clone(), s)?; + // TODO: add device qualifier table. let camera = Arc::new(Mutex::new(self)); Ok(camera) @@ -96,7 +181,25 @@ impl Default for UsbCamera { impl UsbDeviceOps for UsbCamera { fn reset(&mut self) {} - fn handle_control(&mut self, _packet: &mut UsbPacket, _device_req: &UsbDeviceRequest) {} + fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + debug!("Into camera handle_control"); + match self + .usb_device + .handle_control_for_descriptor(packet, device_req) + { + Ok(handled) => { + if handled { + debug!("Camera control handled by descriptor, return directly."); + } else { + error!("Camera: unhandled control msg: {}", device_req.request_type); + } + } + Err(e) => { + error!("Camera descriptor error {:?}", e); + packet.status = UsbPacketStatus::Stall; + } + } + } fn handle_data(&mut self, _p: &mut UsbPacket) {} diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 5cc83cdfe..1c38d0f19 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -225,3 +225,5 @@ pub const USB_CONFIGURATION_ATTR_REMOTE_WAKEUP: u8 = 1 << 5; // USB Class pub const USB_CLASS_HID: u8 = 3; +pub const USB_CLASS_VIDEO: u8 = 0xe; +pub const USB_CLASS_MISCELLANEOUS: u8 = 0xef; -- Gitee From d6e97bf0ba1dde9fbf50fe2c57f96b99842c9d06 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 16:01:05 +0800 Subject: [PATCH 0944/2187] refactor: usb: optimize get_config_descriptor() make get_config_descriptor() more readable by abstract the detailed loop logic into one single method. Signed-off-by: Zhang Bo --- devices/src/usb/descriptor.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 5093ef6e0..8bd52b6a0 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -197,20 +197,26 @@ impl UsbDescriptor { bail!("Config descriptor index {} is invalid", index); }; let mut config_desc = conf.config_desc; - let mut total = config_desc.bLength as u16; - let mut ifs = Vec::new(); - for i in 0..conf.interfaces.len() { - let mut iface = self.get_interface_descriptor(conf.interfaces[i].as_ref())?; - total += iface.len() as u16; - ifs.append(&mut iface); - } - config_desc.wTotalLength = total; + let mut ifs = self.get_interfaces_descriptor(conf.interfaces.as_ref())?; + + config_desc.wTotalLength = config_desc.bLength as u16 + ifs.len() as u16; + let mut buf = config_desc.as_bytes().to_vec(); buf.append(&mut ifs); Ok(buf) } - fn get_interface_descriptor(&self, iface: &UsbDescIface) -> Result> { + fn get_interfaces_descriptor(&self, ifaces: &[Arc]) -> Result> { + let mut ifs = Vec::new(); + for iface in ifaces { + let mut buf = self.get_single_interface_descriptor(iface.as_ref())?; + ifs.append(&mut buf); + } + + Ok(ifs) + } + + fn get_single_interface_descriptor(&self, iface: &UsbDescIface) -> Result> { let desc = iface.interface_desc; let mut buf = desc.as_bytes().to_vec(); for i in 0..iface.other_desc.len() { -- Gitee From 015577d40241bf6cf5ffbaf3f3ccea2a7b2c9a67 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 16:29:03 +0800 Subject: [PATCH 0945/2187] usb: support iad descriptor add the iad descriptor that is used by some kind of usb devices such as video class. Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 1 + devices/src/usb/descriptor.rs | 57 ++++++++++++++++++++++++++++++++++- devices/src/usb/keyboard.rs | 1 + devices/src/usb/tablet.rs | 1 + 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index e848cfce5..ff40ada12 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -110,6 +110,7 @@ static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { bMaxPower: 50, }, // TODO: Add IAD descriptor, including VC&VS + iad_desc: vec![], interfaces: vec![], })], }) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 8bd52b6a0..e8f6d4480 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -134,9 +134,32 @@ pub struct UsbDescDevice { /// USB config descriptor. pub struct UsbDescConfig { pub config_desc: UsbConfigDescriptor, + pub iad_desc: Vec>, pub interfaces: Vec>, } +/// USB Interface Association Descriptor, and related interfaces +pub struct UsbDescIAD { + pub iad_desc: UsbIadDescriptor, + pub itfs: Vec>, +} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbIadDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bFirstInterface: u8, + pub bInterfaceCount: u8, + pub bFunctionClass: u8, + pub bFunctionSubClass: u8, + pub bFunctionProtocol: u8, + pub iFunction: u8, +} + +impl ByteCode for UsbIadDescriptor {} + /// USB interface descriptor. pub struct UsbDescIface { pub interface_desc: UsbInterfaceDescriptor, @@ -197,15 +220,36 @@ impl UsbDescriptor { bail!("Config descriptor index {} is invalid", index); }; let mut config_desc = conf.config_desc; + let mut iads = self.get_iads_descriptor(conf.iad_desc.as_ref())?; let mut ifs = self.get_interfaces_descriptor(conf.interfaces.as_ref())?; - config_desc.wTotalLength = config_desc.bLength as u16 + ifs.len() as u16; + config_desc.wTotalLength = + config_desc.bLength as u16 + iads.len() as u16 + ifs.len() as u16; let mut buf = config_desc.as_bytes().to_vec(); + buf.append(&mut iads); buf.append(&mut ifs); Ok(buf) } + fn get_iads_descriptor(&self, iad_desc: &[Arc]) -> Result> { + let mut iads = Vec::new(); + for iad in iad_desc { + let mut buf = self.get_single_iad_descriptor(iad.as_ref())?; + iads.append(&mut buf); + } + Ok(iads) + } + + fn get_single_iad_descriptor(&self, iad: &UsbDescIAD) -> Result> { + let mut buf = iad.iad_desc.as_bytes().to_vec(); + + let mut ifs = self.get_interfaces_descriptor(iad.itfs.as_ref())?; + buf.append(&mut ifs); + + Ok(buf) + } + fn get_interfaces_descriptor(&self, ifaces: &[Arc]) -> Result> { let mut ifs = Vec::new(); for iface in ifaces { @@ -264,6 +308,17 @@ impl UsbDescriptor { fn find_interface(&self, nif: u32, alt: u32) -> Option> { let conf = self.configuration_selected.as_ref()?; + + for i in 0..conf.iad_desc.len() { + let ifaces = &conf.iad_desc[i].as_ref().itfs; + for iface in ifaces { + if iface.interface_desc.bInterfaceNumber == nif as u8 + && iface.interface_desc.bAlternateSetting == alt as u8 + { + return Some(iface.clone()); + } + } + } for i in 0..conf.interfaces.len() { let iface = conf.interfaces[i].as_ref(); if iface.interface_desc.bInterfaceNumber == nif as u8 diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 32dce752b..1608fc429 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -59,6 +59,7 @@ static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_REMOTE_WAKEUP, bMaxPower: 50, }, + iad_desc: vec![], interfaces: vec![DESC_IFACE_KEYBOARD.clone()], })], }) diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index a693a23ef..bf0336f5d 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -65,6 +65,7 @@ static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_REMOTE_WAKEUP, bMaxPower: 50, }, + iad_desc: vec![], interfaces: vec![DESC_IFACE_TABLET.clone()], })], }) -- Gitee From c443967f82fbed59ffe4bfffaf6c0dc41bf2ae2f Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 22:53:47 +0800 Subject: [PATCH 0946/2187] camera: add iad descriptor header iad descriptor header with empty interfaces. VC/VS interface descriptors will be added in the coming patch. Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index ff40ada12..cbb8d93de 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -35,6 +35,11 @@ const UVC_VENDOR_ID: u16 = 0xB74C; // The first 4 chars of "VIDEO", 5 substitutes V. const UVC_PRODUCT_ID: u16 = 0x51DE; +const INTERFACE_ID_CONTROL: u8 = 0; + +const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; +const PC_PROTOCOL_UNDEFINED: u8 = 0x0; + #[allow(dead_code)] pub struct UsbCamera { id: String, // uniq device id @@ -78,6 +83,22 @@ const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ "Video Streaming", ]; +static DESC_IAD_CAMERA: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIAD { + iad_desc: UsbIadDescriptor { + bLength: 0x8, + bDescriptorType: USB_DT_INTERFACE_ASSOCIATION, + bFirstInterface: INTERFACE_ID_CONTROL, + bInterfaceCount: 2, + bFunctionClass: USB_CLASS_VIDEO, + bFunctionSubClass: SC_VIDEO_INTERFACE_COLLECTION, + bFunctionProtocol: PC_PROTOCOL_UNDEFINED, + iFunction: UsbCameraStringIDs::Iad as u8, + }, + itfs: vec![], + }) +}); + /// UVC Camera device descriptor static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { Arc::new(UsbDescDevice { @@ -109,8 +130,7 @@ static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, bMaxPower: 50, }, - // TODO: Add IAD descriptor, including VC&VS - iad_desc: vec![], + iad_desc: vec![DESC_IAD_CAMERA.clone()], interfaces: vec![], })], }) -- Gitee From 02f29bed356fe01b9be58a22e6766037730e1da4 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 23:02:31 +0800 Subject: [PATCH 0947/2187] camera: add VC/VS interface descriptors in iad Video Control Interface and Video Streaming Interface are added inside iad, so that guest can tell the detailed video capability. Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 275 +++++++++++++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index cbb8d93de..bc732496e 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -36,9 +36,37 @@ const UVC_VENDOR_ID: u16 = 0xB74C; const UVC_PRODUCT_ID: u16 = 0x51DE; const INTERFACE_ID_CONTROL: u8 = 0; +const INTERFACE_ID_STREAMING: u8 = 1; +const TERMINAL_ID_INPUT_TERMINAL: u8 = 1; +const TERMINAL_ID_OUTPUT_TERMINAL: u8 = 3; +const UNIT_ID_SELECTOR_UNIT: u8 = 4; +const UNIT_ID_PROCESSING_UNIT: u8 = 5; + +const ENDPOINT_ID_CONTROL: u8 = 0x1; +const ENDPOINT_ID_STREAMING: u8 = 0x2; + +// According to UVC specification 1.5 +// A.2. Video Interface Subclass Codes +const SC_VIDEOCONTROL: u8 = 0x01; +const SC_VIDEOSTREAMING: u8 = 0x02; const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; +// A.3. Video Interface Protocol Codes const PC_PROTOCOL_UNDEFINED: u8 = 0x0; +const PC_PROTOCOL_15: u8 = 0x1; +// A.4. Video Class-Specific Descriptor Types +const CS_INTERFACE: u8 = 0x24; +// A.5. Video Class-Specific VC Interface Descriptor Subtypes +const VC_HEADER: u8 = 0x01; +const VC_INPUT_TERMINAL: u8 = 0x02; +const VC_OUTPUT_TERMINAL: u8 = 0x03; +const VC_SELECTOR_UNIT: u8 = 0x04; +const VC_PROCESSING_UNIT: u8 = 0x05; +// A.6 Video Class-Specific VS Interface Descriptor Subtypes +const VS_INPUT_HEADER: u8 = 0x01; +const VS_FORMAT_UNCOMPRESSED: u8 = 0x04; +const VS_FRAME_UNCOMPRESSED: u8 = 0x05; +const VS_COLORFORMAT: u8 = 0x0D; #[allow(dead_code)] pub struct UsbCamera { @@ -83,6 +111,248 @@ const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ "Video Streaming", ]; +static DESC_INTERFACE_CAMERA_VC: Lazy> = Lazy::new(|| { + // VideoControl Interface Descriptor + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: 0x9, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: INTERFACE_ID_CONTROL, + bAlternateSetting: 0, + bNumEndpoints: 1, + bInterfaceClass: USB_CLASS_VIDEO, + bInterfaceSubClass: SC_VIDEOCONTROL, + bInterfaceProtocol: PC_PROTOCOL_15, + iInterface: UsbCameraStringIDs::VideoControl as u8, + }, + other_desc: vec![ + Arc::new(UsbDescOther { + // Class-specific VS Interface Input Header Descriptor + data: vec![ + 0xd, // bLength + CS_INTERFACE, // bDescriptorType + VC_HEADER, // bDescriptorSubtype + 0x10, // bcdADC + 0x01, + 0x3b, //wTotalLength + 0x00, + 0x80, //dwClockFrequency, 6MHz + 0x8D, + 0x5B, + 0x00, + 0x01, // bInCollection + 0x01, // baInterfaceNr + ], + }), + // Input Terminal Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x11, // bLength + CS_INTERFACE, // bDescriptorType + VC_INPUT_TERMINAL, // bDescriptorSubtype + TERMINAL_ID_INPUT_TERMINAL, // bTerminalID + 0x01, // Fixme, wTerminalType, ITT_CAMERA, 0x0201 + 0x02, + 0x00, // bAssocTerminal + UsbCameraStringIDs::InputTerminal as u8, + 0x00, // wObjectiveFocalLengthMin + 0x00, + 0x00, // wObjectiveFocalLengthMax + 0x00, + 0x00, // wOcularFocalLength + 0x00, + 0x02, // bControlSize + 0x00, // bmControls + 0x00, + ], + }), + // Output Terminal Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x09, // bLength + CS_INTERFACE, // bDescriptorType + VC_OUTPUT_TERMINAL, // bDescriptorSubtype + TERMINAL_ID_OUTPUT_TERMINAL, // bTerminalID + 0x01, // wTerminalType, TT_STREAMING, 0x0101 + 0x01, + 0x00, // bAssocTerminal + UNIT_ID_PROCESSING_UNIT, // bSourceID + UsbCameraStringIDs::OutputTerminal as u8, // iTerminal + ], + }), + // Selector Unit Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x07, // bLength + CS_INTERFACE, //bDescriptorType + VC_SELECTOR_UNIT, //bDescriptorSubtype + UNIT_ID_SELECTOR_UNIT, // bUnitID + 0x01, // bNrInPins + TERMINAL_ID_INPUT_TERMINAL, // baSourceID(1) + UsbCameraStringIDs::SelectUnit as u8, // iSelector + ], + }), + // Processing Unit Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x0d, // bLength + CS_INTERFACE, // bDescriptorType + VC_PROCESSING_UNIT, // bDescriptorSubtype + UNIT_ID_PROCESSING_UNIT, // bUnitID + UNIT_ID_SELECTOR_UNIT, // bSourceID + 0x00, // u16 wMaxMultiplier + 0x00, + 0x03, // bControlSize + 0x00, // u24 bmControls + 0x00, + 0x00, + UsbCameraStringIDs::ProcessingUnit as u8, // iProcessing + 0x00, // bmVideoStandards + ], + }), + ], + endpoints: vec![Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | ENDPOINT_ID_CONTROL, + bmAttributes: USB_ENDPOINT_ATTR_INT, + wMaxPacketSize: 0x40, + bInterval: 0x20, + }, + extra: None, + })], + }) +}); + +static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { + // VideoStreaming Interface Descriptor + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: INTERFACE_ID_STREAMING, + bAlternateSetting: 0, + bNumEndpoints: 1, + bInterfaceClass: USB_CLASS_VIDEO, + bInterfaceSubClass: SC_VIDEOSTREAMING, + bInterfaceProtocol: PC_PROTOCOL_15, + iInterface: UsbCameraStringIDs::VideoStreaming as u8, + }, + other_desc: vec![ + // VC-Specific VS Video Input Header Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0xf, // bLength + CS_INTERFACE, // bDescriptorType + VS_INPUT_HEADER, // bDescriptorSubtype + 0x1, // bNumFormats + 0x4E, //wTotalLength + 0x00, + 0x81, // bEndpointAddress, EP 1 IN + 0x00, // bmInfo + UsbCameraStringIDs::OutputTerminal as u8, // bTerminalLink + 0x00, // bStillCaptureMethod + 0x00, // bTriggerSupport + 0x00, // bTriggerUsage + 0x01, // bControlSize + 0x00, // bmaControls(0) + 0x00, // bmaControls(1) + ], + }), + // VS Uncompressed Format Type Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x1B, // bLength + CS_INTERFACE, // bDescriptorType + VS_FORMAT_UNCOMPRESSED, //bDescriptorSubtype + 0x01, // bFormatIndex + 0x01, // bNumFrameDescriptors + 0x59, // guidFormat {32595559-0000-0010-8000-00AA00389B71} (YUY2) + 0x55, + 0x59, + 0x32, + 0x00, + 0x00, + 0x10, + 0x00, + 0x80, + 0x00, + 0x00, + 0xaa, + 0x00, + 0x38, + 0x9b, + 0x71, + 0x10, // bBitsPerPixel (16 bits per pixel) + 0x01, // bDefaultFrameIndex (Index 1) + 0x00, // bAspectRatioX + 0x00, // bAspectRatioY + 0x00, // bmInterlaceFlags + 0x00, // bCopyProtect + ], + }), + // VS Uncompressed Frame Type Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x1E, // bLength + CS_INTERFACE, // bDescriptorType + VS_FRAME_UNCOMPRESSED, // bDescriptorSubtype + 0x01, // bFrameIndex + 0x00, // bmCapabilities (Still image unsupported) + 0x00, // wWidth, 1280 + 0x05, // + 0xD0, // wHeight, 720 + 0x02, // + 0x00, // dwMinBitRate, (147456000 bps -> 18.432 MB/s) + 0x00, // + 0xCA, // + 0x08, // + 0x00, // dwMaxBitRate, (147456000 bps -> 18.432 MB/s) + 0x00, // + 0xCA, // + 0x08, // + 0x00, // dwMaxVideoFrameBufferSize, (1843200 bytes) + 0x20, // + 0x1C, // + 0x00, // + 0x40, // dwDefaultFrameInterval, (100.0000 ms -> 10.0000 fps) + 0x42, // + 0x0F, // + 0x00, // + 0x01, // bFrameIntervalType, (1 discrete frame interval supported) + 0x40, // adwFrameInterval,(100.0000 ms -> 10.0000 fps) + 0x42, // + 0x0F, // + 0x00, // + ], + }), + // VS Color Matching Descriptor Descriptor + Arc::new(UsbDescOther { + data: vec![ + 0x06, // bLength + CS_INTERFACE, // bDescriptorType + VS_COLORFORMAT, // bDescriptorSubtype + 0x01, // bColorPrimaries (BT.709,sRGB) + 0x01, // bTransferCharacteristics (BT.709) + 0x04, // bMatrixCoefficients (SMPTE 170M (BT.601)) + ], + }), + ], + endpoints: vec![Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | ENDPOINT_ID_STREAMING, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 0x400, + bInterval: 0x20, + }, + extra: None, + })], + }) +}); + static DESC_IAD_CAMERA: Lazy> = Lazy::new(|| { Arc::new(UsbDescIAD { iad_desc: UsbIadDescriptor { @@ -95,7 +365,10 @@ static DESC_IAD_CAMERA: Lazy> = Lazy::new(|| { bFunctionProtocol: PC_PROTOCOL_UNDEFINED, iFunction: UsbCameraStringIDs::Iad as u8, }, - itfs: vec![], + itfs: vec![ + DESC_INTERFACE_CAMERA_VC.clone(), + DESC_INTERFACE_CAMERA_VS.clone(), + ], }) }); -- Gitee From b515debd0d2801630c07a7643ebf485eb570e95d Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 23:43:01 +0800 Subject: [PATCH 0948/2187] usb: support device qualifier descriptor Signed-off-by: Zhang Bo --- devices/src/usb/config.rs | 1 + devices/src/usb/descriptor.rs | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 1c38d0f19..964c38ac4 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -195,6 +195,7 @@ pub const USB_DT_CONFIGURATION: u8 = 2; pub const USB_DT_STRING: u8 = 3; pub const USB_DT_INTERFACE: u8 = 4; pub const USB_DT_ENDPOINT: u8 = 5; +pub const USB_DT_DEVICE_QUALIFIER: u8 = 6; pub const USB_DT_INTERFACE_POWER: u8 = 8; pub const USB_DT_OTG: u8 = 9; pub const USB_DT_DEBUG: u8 = 10; diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index e8f6d4480..f36543d0e 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -45,6 +45,23 @@ pub struct UsbDeviceDescriptor { impl ByteCode for UsbDeviceDescriptor {} +/// USB device qualifier descriptor for transfer +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbDeviceQualifierDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bcdUSB: u16, + pub bDeviceClass: u8, + pub bDeviceSubClass: u8, + pub bDeviceProtocol: u8, + pub bMaxPacketSize0: u8, + pub bNumConfigurations: u8, + pub bReserved: u8, +} +impl ByteCode for UsbDeviceQualifierDescriptor {} + /// USB config descriptor for transfer #[allow(non_snake_case)] #[repr(C, packed)] @@ -131,6 +148,11 @@ pub struct UsbDescDevice { pub configs: Vec>, } +/// USB device qualifier descriptor. +pub struct UsbDescDeviceQualifier { + pub qualifier_desc: UsbDeviceQualifierDescriptor, +} + /// USB config descriptor. pub struct UsbDescConfig { pub config_desc: UsbConfigDescriptor, @@ -181,6 +203,7 @@ pub struct UsbDescEndpoint { /// USB Descriptor. pub struct UsbDescriptor { pub device_desc: Option>, + pub device_qualifier_desc: Option>, pub configuration_selected: Option>, pub interfaces: Vec>>, pub altsetting: Vec, @@ -192,6 +215,7 @@ impl UsbDescriptor { pub fn new() -> Self { Self { device_desc: None, + device_qualifier_desc: None, configuration_selected: None, interfaces: vec![None; USB_MAX_INTERFACES as usize], altsetting: vec![0; USB_MAX_INTERFACES as usize], @@ -306,6 +330,14 @@ impl UsbDescriptor { Ok(vec) } + fn get_device_qualifier_descriptor(&self) -> Result> { + if let Some(desc) = self.device_qualifier_desc.as_ref() { + Ok(desc.qualifier_desc.as_bytes().to_vec()) + } else { + bail!("Device qualifier descriptor not found"); + } + } + fn find_interface(&self, nif: u32, alt: u32) -> Option> { let conf = self.configuration_selected.as_ref()?; @@ -353,6 +385,12 @@ pub trait UsbDescriptorOps { /// Init descriptor with the device descriptor and string descriptors. fn init_descriptor(&mut self, desc: Arc, str: Vec) -> Result<()>; + + /// Init device qualifier descriptor. + fn init_device_qualifier_descriptor( + &mut self, + device_qualifier_desc: Arc, + ) -> Result<()>; } impl UsbDescriptorOps for UsbDevice { @@ -363,6 +401,7 @@ impl UsbDescriptorOps for UsbDevice { USB_DT_DEVICE => self.descriptor.get_device_descriptor()?, USB_DT_CONFIGURATION => self.descriptor.get_config_descriptor(index)?, USB_DT_STRING => self.descriptor.get_string_descriptor(index)?, + USB_DT_DEVICE_QUALIFIER => self.descriptor.get_device_qualifier_descriptor()?, _ => { bail!("Unknown descriptor type {}", desc_type); } @@ -451,4 +490,12 @@ impl UsbDescriptorOps for UsbDevice { self.set_config_descriptor(0)?; Ok(()) } + + fn init_device_qualifier_descriptor( + &mut self, + device_qualifier_desc: Arc, + ) -> Result<()> { + self.descriptor.device_qualifier_desc = Some(device_qualifier_desc); + Ok(()) + } } -- Gitee From a9ef6bd56b223aee29fdfed6c93f09e23dfd8062 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 29 Mar 2023 23:47:55 +0800 Subject: [PATCH 0949/2187] camera: support device qualifier descriptor Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index bc732496e..dcaf2dd5b 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -409,6 +409,22 @@ static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { }) }); +static DESC_DEVICE_QUALIFIER_CAMERA: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDeviceQualifier { + qualifier_desc: UsbDeviceQualifierDescriptor { + bLength: 0xa, + bDescriptorType: USB_DT_DEVICE_QUALIFIER, + bcdUSB: DESC_DEVICE_CAMERA.device_desc.bcdUSB, + bDeviceClass: DESC_DEVICE_CAMERA.device_desc.bDeviceClass, + bDeviceSubClass: DESC_DEVICE_CAMERA.device_desc.bDeviceSubClass, + bDeviceProtocol: DESC_DEVICE_CAMERA.device_desc.bDeviceProtocol, + bMaxPacketSize0: DESC_DEVICE_CAMERA.device_desc.bMaxPacketSize0, + bNumConfigurations: DESC_DEVICE_CAMERA.device_desc.bNumConfigurations, + bReserved: 0, + }, + }) +}); + #[allow(dead_code)] impl UsbCamera { pub fn new(config: UsbCameraConfig) -> Self { @@ -432,7 +448,8 @@ impl UsbCamera { let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); self.usb_device .init_descriptor(DESC_DEVICE_CAMERA.clone(), s)?; - // TODO: add device qualifier table. + self.usb_device + .init_device_qualifier_descriptor(DESC_DEVICE_QUALIFIER_CAMERA.clone())?; let camera = Arc::new(Mutex::new(self)); Ok(camera) -- Gitee From 6bbd1e762c49d86955e858bc3fd94c193a87a252 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 30 Mar 2023 11:53:24 +0800 Subject: [PATCH 0950/2187] usb: support querying debug descriptor empty data returned to clear the error message. Signed-off-by: Zhang Bo --- devices/src/usb/descriptor.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index f36543d0e..d31473ea5 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -338,6 +338,11 @@ impl UsbDescriptor { } } + fn get_debug_descriptor(&self) -> Result> { + log::debug!("usb DEBUG descriptor"); + Ok(vec![]) + } + fn find_interface(&self, nif: u32, alt: u32) -> Option> { let conf = self.configuration_selected.as_ref()?; @@ -402,6 +407,7 @@ impl UsbDescriptorOps for UsbDevice { USB_DT_CONFIGURATION => self.descriptor.get_config_descriptor(index)?, USB_DT_STRING => self.descriptor.get_string_descriptor(index)?, USB_DT_DEVICE_QUALIFIER => self.descriptor.get_device_qualifier_descriptor()?, + USB_DT_DEBUG => self.descriptor.get_debug_descriptor()?, _ => { bail!("Unknown descriptor type {}", desc_type); } -- Gitee From 79e9cdf316a4651fec104eadd983eff775671b7f Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 30 Mar 2023 14:43:34 +0800 Subject: [PATCH 0951/2187] camera: test: validata VC and VS interface's data length field VC and VS's length field is calculated according to individual data vector's length, to avoid human mistake of counting, add this test case. Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 32 ++++++++++++++++++++++++++++++++ devices/src/usb/descriptor.rs | 1 + 2 files changed, 33 insertions(+) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index dcaf2dd5b..3816d1765 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -536,3 +536,35 @@ impl UsbDeviceOps for UsbCamera { &mut self.usb_device } } + +#[cfg(test)] +mod test { + use super::*; + + fn test_interface_table_data_len(interface: Arc, size_offset: usize) { + let descs = &interface.other_desc; + let mut total_len_exact: usize = 0; + + let total_len_setted: usize = (descs[0].data[size_offset] as usize + + ((descs[0].data[size_offset + 1] as usize) << 8)) + as usize; // field 'wTotalLength' in the 1st data desc + + for desc in descs { + let sub_len_setted = desc.data[0] as usize; // field 'bLength' + let sub_len_exact = desc.data.len(); + assert_eq!(sub_len_setted, sub_len_exact); + + total_len_exact += sub_len_exact; + } + + assert_eq!(total_len_setted, total_len_exact); + } + + #[test] + fn test_interfaces_table_data_len() { + // VC and VS's header differents, their wTotalSize field's offset are the bit 5 and 4 respectively in their data[0] vector. + // the rest datas follow the same principle that the 1st element is the very data vector's length. + test_interface_table_data_len(DESC_INTERFACE_CAMERA_VC.clone(), 5); + test_interface_table_data_len(DESC_INTERFACE_CAMERA_VS.clone(), 4); + } +} diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index d31473ea5..89d40d44d 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -190,6 +190,7 @@ pub struct UsbDescIface { } /// USB other descriptor. +#[derive(Debug)] pub struct UsbDescOther { pub data: Vec, } -- Gitee From 334d961249781ad09bb9fb76954d42f6adb20218 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 30 Mar 2023 14:55:01 +0800 Subject: [PATCH 0952/2187] camera: doc: add usb camera in config guidebook Signed-off-by: Zhang Bo --- docs/config_guidebook.md | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2cc8703e8..04583f3e4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -769,7 +769,23 @@ One property can be set for USB Tablet. Note: Only one tablet can be configured. -### 2.16 Virtio Scsi Controller +### 2.16 USB Camera +Video Camera Device that based on USB video class protocol. It should be attached to USB controller. + +3 properties can be set for USB Camera. + +* id: unique device id. +* backend: backend device type, either `v4l2` or `demo`. +* path: the file path used to connect to the backend, required for `v4l2`, but not for `demo`. eg. `/dev/video0`. + +```shell +-device usb-camera,id=,backend="v4l2",path="/dev/video0" +-device usb-camera,id=,backend="demo" +``` + +Note: Only one camera can be configured. + +### 2.17 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. Six properties can be set for Virtio-Scsi controller. @@ -783,7 +799,7 @@ Six properties can be set for Virtio-Scsi controller. ```shell -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=][,queue-size=] ``` -### 2.17 Virtio Scsi HardDisk +### 2.18 Virtio Scsi HardDisk Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. Note: Only support using raw image file as backend now. @@ -808,7 +824,7 @@ It determines the order of bootable devices which firmware will use for booting -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,serial=123456,bootindex=1] ``` -### 2.18 VNC +### 2.19 VNC VNC can provide the users with way to login virtual machines remotely. In order to use VNC, the ip and port value must be configured. The IP address can be set to a specified value or `0.0.0.0`, which means that all IP addresses on the host network card are monitored @@ -858,10 +874,10 @@ Sample Configuration: Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. -### 2.19 Virtio-fs +### 2.20 Virtio-fs Virtio-fs is a shared file system that lets virtual machines access a directory tree on the host. Unlike existing approaches, it is designed to offer local file system semantics and performance. -#### 2.19.1 virtio fs device +#### 2.20.1 virtio fs device Three properties can be set for virtio fs device. * chardevid: id for char device * device_id: the unique id for device @@ -872,7 +888,7 @@ Three properties can be set for virtio fs device. -device vhost-user-fs-pci,id=,chardev=,tag= ``` -#### 2.19.2 vhost_user_fs +#### 2.20.2 vhost_user_fs The vhost-user filesystem device contains virtio fs device and the vhost-user server which can be connected with the vhost-user client in StratoVirt through socket. Seven properties are supported for vhost_user_fs. @@ -911,7 +927,7 @@ host# stratovirt \ guest# mount -t virtiofs myfs /mnt ``` -### 2.20 virtio-gpu +### 2.21 virtio-gpu virtio-gpu is an virtualized graphics card that lets virtual machines can display with it. Usually used in conjunction with VNC, the final images is rendered to the VNC client. @@ -930,7 +946,7 @@ Note: 1. Only virtio-gpu 2D supported. 2. Live migration is not supported. -### 2.21 ivshmem-scream +### 2.22 ivshmem-scream ivshmem-scream is a virtual sound card that relies on Intel-VM shared memory to transmit audio data. -- Gitee From 52142281168c6961ea8e5bb545e92ff0f6a324c2 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 5 Apr 2023 16:28:12 +0800 Subject: [PATCH 0953/2187] fix some typos fix some typos by tool: https://crates.io/crates/typos Signed-off-by: yezengruan --- address_space/src/host_mmap.rs | 4 +- address_space/src/region.rs | 4 +- cpu/src/x86_64/mod.rs | 2 +- devices/src/legacy/pl031.rs | 2 +- devices/src/legacy/ramfb.rs | 2 +- devices/src/misc/scream/mod.rs | 4 +- devices/src/misc/scream/pulseaudio.rs | 2 +- devices/src/usb/camera.rs | 10 ++--- devices/src/usb/hid.rs | 2 +- devices/src/usb/mod.rs | 24 ++++++------ devices/src/usb/xhci/xhci_controller.rs | 6 +-- devices/src/usb/xhci/xhci_regs.rs | 2 +- docs/config_guidebook.md | 12 +++--- docs/migration.md | 4 +- docs/trace.md | 2 +- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 2 +- machine_manager/src/config/chardev.rs | 2 +- machine_manager/src/config/machine_config.rs | 2 +- machine_manager/src/config/usb.rs | 2 +- ozone/src/args.rs | 2 +- pci/src/demo_dev.rs | 2 +- pci/src/demo_device/base_device.rs | 2 +- pci/src/lib.rs | 2 +- tests/mod_test/src/libdriver/usb.rs | 38 +++++++++---------- tests/mod_test/src/libdriver/virtio_block.rs | 4 +- tests/mod_test/src/libdriver/vnc.rs | 18 ++++----- tests/mod_test/tests/acpi_test.rs | 8 ++-- tests/mod_test/tests/balloon_test.rs | 2 +- tests/mod_test/tests/block_test.rs | 34 ++++++++--------- tests/mod_test/tests/console_test.rs | 6 +-- tests/mod_test/tests/memory_test.rs | 10 ++--- tests/mod_test/tests/net_test.rs | 6 +-- tests/mod_test/tests/pci_test.rs | 6 +-- tests/mod_test/tests/ramfb_test.rs | 2 +- tests/mod_test/tests/scsi_test.rs | 24 ++++++------ tests/mod_test/tests/usb_test.rs | 16 ++++---- tests/mod_test/tests/virtio_gpu_test.rs | 8 ++-- tests/mod_test/tests/virtio_test.rs | 20 +++++----- tests/mod_test/tests/virtiofs_test.rs | 4 +- tests/mod_test/tests/vnc_test.rs | 10 ++--- ui/src/console.rs | 4 +- ui/src/utils.rs | 2 +- ui/src/vnc/auth_sasl.rs | 4 +- ui/src/vnc/client_io.rs | 16 ++++---- ui/src/vnc/encoding/enc_hextile.rs | 2 +- .../vnc/encoding/test_hextile_image_data.rs | 2 +- ui/src/vnc/mod.rs | 10 ++--- ui/src/vnc/server_io.rs | 4 +- util/src/aio/mod.rs | 6 +-- util/src/byte_code.rs | 4 +- util/src/edid.rs | 2 +- util/src/loop_context.rs | 2 +- util/src/unix.rs | 2 +- vhost_user_fs/src/fs.rs | 20 +++++----- vhost_user_fs/src/fuse_msg.rs | 2 +- vhost_user_fs/src/sandbox.rs | 2 +- virtio/src/device/balloon.rs | 6 +-- virtio/src/device/gpu.rs | 24 ++++++------ virtio/src/device/net.rs | 6 +-- virtio/src/device/scsi/bus.rs | 8 ++-- virtio/src/device/scsi/controller.rs | 8 ++-- virtio/src/lib.rs | 8 ++-- virtio/src/queue/split.rs | 4 +- virtio/src/transport/virtio_pci.rs | 2 +- virtio/src/vhost/user/block.rs | 2 +- virtio/src/vhost/user/client.rs | 6 +-- virtio/src/vhost/user/fs.rs | 10 ++--- virtio/src/vhost/user/net.rs | 2 +- 69 files changed, 243 insertions(+), 243 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index f93a73019..13f3364f5 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -188,8 +188,8 @@ fn touch_pages(start: u64, page_size: u64, nr_pages: u64) { unsafe { let read_addr = addr as *mut u8; let data: u8 = *read_addr; - // This function is used to prevent complier optimization. - // If `*read = data` is used, the complier optimizes it as no-op, + // This function is used to prevent compiler optimization. + // If `*read = data` is used, the compiler optimizes it as no-op, // which means that the pages will not be touched. std::ptr::write_volatile(read_addr, data); } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index ace087721..628e9dfd2 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1092,11 +1092,11 @@ mod test { data_match: false, data: 0, }; - // comapre unchanged + // compare unchanged let mut fd2 = fd1.clone(); assert!(fd2 == fd1); - // comapre fd + // compare fd fd2.fd = Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()); assert!(fd2 != fd1); diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 4f3dbbaee..f281e63dd 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -397,7 +397,7 @@ impl X86CPUState { return Ok(()); } - // Intel CPU topology with multi-dies support requies CPUID[0x1f]. + // Intel CPU topology with multi-dies support requires CPUID[0x1f]. let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { if entry.function == 0 { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 912e86640..437876854 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -164,7 +164,7 @@ impl SysBusDevOps for PL031 { match offset { RTC_MR => { - // TODO: The MR register is used for implemeting the RTC alarm. A RTC alarm is a feature + // TODO: The MR register is used for implementing the RTC alarm. A RTC alarm is a feature // that can be used to allow a computer to 'wake up' after shut down to execute tasks // every day or on a certain day. It can sometimes be found in the 'Power Management' // section of motherboard's BIOS setup. This RTC alarm function is not implemented yet, diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index b4c8531bf..d50dc05d5 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -230,7 +230,7 @@ impl SysBusDevOps for Ramfb { } fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { - error!("Ramfb can not be writed!"); + error!("Ramfb can not be written!"); false } diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 4ad4afbc6..02fb6f712 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -122,9 +122,9 @@ pub struct ShmemStreamFmt { pub fmt_generation: u32, /// Audio sampling rate. pub rate: u8, - /// Numner of audio sampling bits. + /// Number of audio sampling bits. pub size: u8, - //// Number of audio channel. + /// Number of audio channel. pub channels: u8, pad: u8, /// Mapping of audio channel. diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index a97ae6fd9..7eb3201fa 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -176,7 +176,7 @@ impl PulseStreamData { 32 => self.ss.format = Format::S32le, _ => { warn!( - "Unsuported sample size {}, not playing until next format switch", + "Unsupported sample size {}, not playing until next format switch", recv_data.fmt.size ); self.ss.rate = 0; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 3816d1765..c63f4300d 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -73,7 +73,7 @@ pub struct UsbCamera { id: String, // uniq device id usb_device: UsbDevice, // general usb device object frame: Vec, // video frame data - max_pkt_size: u32, // the max size of the packet that can be seperated + max_pkt_size: u32, // the max size of the packet that can be separated vs_eps: Vec, // the endpoints that the VS uses backend_type: CamBackendType, // backend interface, eg. v4l2 backend_path: String, // backend interface file, eg. /dev/video0 @@ -545,19 +545,19 @@ mod test { let descs = &interface.other_desc; let mut total_len_exact: usize = 0; - let total_len_setted: usize = (descs[0].data[size_offset] as usize + let total_len_set: usize = (descs[0].data[size_offset] as usize + ((descs[0].data[size_offset + 1] as usize) << 8)) as usize; // field 'wTotalLength' in the 1st data desc for desc in descs { - let sub_len_setted = desc.data[0] as usize; // field 'bLength' + let sub_len_set = desc.data[0] as usize; // field 'bLength' let sub_len_exact = desc.data.len(); - assert_eq!(sub_len_setted, sub_len_exact); + assert_eq!(sub_len_set, sub_len_exact); total_len_exact += sub_len_exact; } - assert_eq!(total_len_setted, total_len_exact); + assert_eq!(total_len_set, total_len_exact); } #[test] diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 7e4927cc0..97944c11d 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -155,7 +155,7 @@ pub enum HidType { /// HID keyboard including keycode and modifier. pub struct HidKeyboard { - /// Recive keycode from VNC. + /// Receive keycode from VNC. pub keycodes: [u32; QUEUE_LENGTH as usize], pub modifiers: u16, /// Send keycode to driver. diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 913d75089..3b5d41cf2 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -295,10 +295,10 @@ pub trait UsbDeviceOps: Send + Sync { fn reset(&mut self); /// Set the controller which the USB device attached. - /// USB deivce need to kick controller in some cases. + /// USB device need to kick controller in some cases. fn set_controller(&mut self, ctrl: Weak>); - /// Set the controller which the USB device attached. + /// Get the controller which the USB device attached. fn get_controller(&self) -> Option>>; /// Get the endpoint to wakeup. @@ -477,33 +477,33 @@ impl UsbPacket { pub fn transfer_packet(&mut self, vec: &mut [u8], len: usize) { let len = min(vec.len(), len); let to_host = self.pid as u8 & USB_TOKEN_IN == USB_TOKEN_IN; - let mut copyed = 0; + let mut copied = 0; if to_host { for iov in &self.iovecs { - let cnt = min(iov.iov_len, len - copyed); - let tmp = &vec[copyed..(copyed + cnt)]; + let cnt = min(iov.iov_len, len - copied); + let tmp = &vec[copied..(copied + cnt)]; if let Err(e) = mem_from_buf(tmp, iov.iov_base) { error!("Failed to write mem: {:?}", e); } - copyed += cnt; - if len == copyed { + copied += cnt; + if len == copied { break; } } } else { for iov in &self.iovecs { - let cnt = min(iov.iov_len, len - copyed); - let tmp = &mut vec[copyed..(copyed + cnt)]; + let cnt = min(iov.iov_len, len - copied); + let tmp = &mut vec[copied..(copied + cnt)]; if let Err(e) = mem_to_buf(tmp, iov.iov_base) { error!("Failed to read mem {:?}", e); } - copyed += cnt; - if len == copyed { + copied += cnt; + if len == copied { break; } } } - self.actual_length = copyed as u32; + self.actual_length = copied as u32; } } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index acbe3a1a2..a425757d1 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1028,7 +1028,7 @@ impl XhciDevice { } // From section 4.6.6 Configure Endpoint of the spec: // If all Endpoints are Disabled: - // Set the Slot State in the Output Slot Context to Addresed. + // Set the Slot State in the Output Slot Context to Addressed. // else (An Endpoint is Enabled): // Set the Slot State in the Output Slot Context to Configured. // Set the Context Entries field in the Output Slot Context to the index of @@ -1668,14 +1668,14 @@ impl XhciDevice { &mut self, xfer: &XhciTransfer, trb: &XhciTRB, - transfered: u32, + transferred: u32, edtla: &mut u32, ) -> Result<()> { let trb_type = trb.get_type(); let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); evt.slot_id = xfer.slotid as u8; evt.ep_id = xfer.epid as u8; - evt.length = (trb.status & TRB_TR_LEN_MASK) - transfered; + evt.length = (trb.status & TRB_TR_LEN_MASK) - transferred; evt.flags = 0; evt.ptr = trb.addr; evt.ccode = xfer.status; diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 85a0f1a14..0f2a3ce72 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -614,7 +614,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { XHCI_PORTLI => 0, XHCI_PORTHLPMC => 0, _ => { - error!("Faield to read port register: offset {:x}", offset); + error!("Failed to read port register: offset {:x}", offset); return false; } }; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 43fba77ae..31a3bf605 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -260,7 +260,7 @@ root bus named pcie.0. As a result, a total of 32 pci devices can be configured. Iothread is used by devices to improve io performance. StratoVirt will spawn some extra threads due to `iothread` configuration, and these threads can be used by devices exclusively improving performance. -Note: iothread is strongly recommanded if a specific device supports it, otherwise the main thread has the risk of getting stuck. +Note: iothread is strongly recommended if a specific device supports it, otherwise the main thread has the risk of getting stuck. There is only one argument for iothread: @@ -593,7 +593,7 @@ To use the first method, chardev for redirection will be required. See [section Or you can simply use `-serial dev` to bind serial with character device. ```shell -# simplifed redirect methods +# simplified redirect methods -serial stdio -serial pty -serial socket,path=,server,nowait @@ -895,7 +895,7 @@ The vhost-user filesystem device contains virtio fs device and the vhost-user se Seven properties are supported for vhost_user_fs. * source: Shared directory path. * socket-path: vhost user socket path. -* rlimit-nofile: Set maxinum number of file descriptors, The limit of file resources which can be opened for the process. +* rlimit-nofile: Set maximum number of file descriptors, The limit of file resources which can be opened for the process. * D: log file path. * seccomp: Action to take when seccomp finds a not allowed syscall (allow, kill, log, trap). - **allow**: The seccomp filter will have no effect on the thread calling the syscall if it matches the filter rule. @@ -938,10 +938,10 @@ Sample Configuration: ``` In addition to the required slot information, five optional properties are supported for virtio-gpu. -* max_outputs: Number of screens supported by the current graphics card. The maximun value is 16. (can switch by using ctrl + alt + , for details, see vnc Client switchover) +* max_outputs: Number of screens supported by the current graphics card. The maximum value is 16. (can switch by using ctrl + alt + , for details, see vnc Client switchover) * edid: Edid feature, the virtual machine's kernel may checks this feature for HiDPi. You are advised to set to true. * xres/yres: The size of the login windows. -* max_hostmem: The maximum memory that a graphics card can occupy on the host is expressed in byte. You are advised to set not less than 256MiB, otherwise the final supported resoltuion is affected. +* max_hostmem: The maximum memory that a graphics card can occupy on the host is expressed in byte. You are advised to set not less than 256MiB, otherwise the final supported resolutions is affected. Note: 1. Only virtio-gpu 2D supported. @@ -1061,7 +1061,7 @@ About the arguments: * `clean-resource` : a flag to clean resource. * `numa` : numa node, this argument must be configured if `cpuset.cpus` is set. * `cgroup` : set cgroup controller value. supported controller: `cpuset.cpus` and `memory.limit_in_bytes`. -* `--` : these two dashes are used to splite args, the args followed are used to launched StratoVirt. +* `--` : these two dashes are used to split args, the args followed are used to launched StratoVirt. ### 6.2 Example As ozone uses a directory to mount as a root directory, after ozone is launched, the directory "/srv/zozne/{exec_file}/{name}" will be created. (Where, `exec_file` is the executable binary file, usually it is `stratovirt`, while `name` is the name of ozone, it is given by users, but the length of it should be no more than 255 bytes.) In order to run ozone normally, please make sure that the directory "/srv/zozne/{exec_file}/{name}" does not exists before launching ozone. diff --git a/docs/migration.md b/docs/migration.md index 2b1112856..f09be99b7 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -126,8 +126,8 @@ Before live migration: During live migration: - source and destination networks cannot be disconnected. -- it is banned to operate VM lifecycle, inclues using the QMP command and executing in the VM. +- it is banned to operate VM lifecycle, includes using the QMP command and executing in the VM. - live migration time is affected by network performance, total memory of VM and applications. After live migration: -- it needs to wait for the source VM to release resources before fetching back the live migration operation. \ No newline at end of file +- it needs to wait for the source VM to release resources before fetching back the live migration operation. diff --git a/docs/trace.md b/docs/trace.md index fb737f55d..8bd59654b 100644 --- a/docs/trace.md +++ b/docs/trace.md @@ -9,7 +9,7 @@ Ftrace is a tracer provided by Linux kernel, which can help linux developers to debug or analyze issues. As ftrace can avoid performance penalty, it's especially suited for performance issues. -StratoVirt use ftrace by writting trace data to ftrace marker, and developers can +StratoVirt use ftrace by writing trace data to ftrace marker, and developers can read trace records from *trace* file under mounted ftrace director, e.g. /sys/kernel/debug/tracing/trace. diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index a1c9943d5..8161add8b 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -490,7 +490,7 @@ trait AcpiBuilder { fadt.set_field(148, 0x01_u8); fadt.set_field(149, 0x20_u8); fadt.set_field(152, PM_EVENT_OFFSET as u64); - // PM1a contol register bit, offset is 172. + // PM1a control register bit, offset is 172. fadt.set_field(172, 0x01_u8); fadt.set_field(173, 0x10_u8); fadt.set_field(176, PM_CTRL_OFFSET as u64); diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 38cf7663e..6473b3cd9 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -50,7 +50,7 @@ pub struct LPCBridge { rst_ctrl: Arc, pm_evt: Arc>, pm_ctrl: Arc>, - /// Reset request trigged by ACPI PM1 Control Registers. + /// Reset request triggered by ACPI PM1 Control Registers. pub reset_req: Arc, pub shutdown_req: Arc, } diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 9d5510700..7edc1b197 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -23,7 +23,7 @@ use crate::qmp::qmp_schema; const MAX_GUEST_CID: u64 = 4_294_967_295; const MIN_GUEST_CID: u64 = 3; -/// Charecter device options. +/// Character device options. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ChardevType { Stdio, diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 2cb84c1f8..3a50bee96 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -942,7 +942,7 @@ mod tests { let mut vm_config = VmConfig::default(); let memory_path_str = "/path/to/memory-backend"; let mem_path = vm_config.machine_config.mem_config.mem_path.clone(); - // defalut value is none. + // default value is none. assert!(mem_path.is_none()); let mem_cfg_ret = vm_config.add_mem_path(memory_path_str); assert!(mem_cfg_ret.is_ok()); diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 89488985c..21565c4ca 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -19,7 +19,7 @@ use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use crate::config::{check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck}; -/// XHCI contoller configuration. +/// XHCI controller configuration. #[derive(Debug)] pub struct XhciConfig { pub id: Option, diff --git a/ozone/src/args.rs b/ozone/src/args.rs index 28da385ea..85e9ee965 100644 --- a/ozone/src/args.rs +++ b/ozone/src/args.rs @@ -86,7 +86,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("cgroup") .long("cgroup") - .help("set cgroup arguments, use -cgroup = ...") + .help("set cgroup arguments, use -cgroup = ...") .required(false) .takes_values(true), ) diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 3bddf366f..fb92d04a8 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -/// DemoDev is a demo PCIe device, that can have device properites configurable, eg. +/// DemoDev is a demo PCIe device, that can have device properties configurable, eg. /// bar num, max msix vector num, etc. /// It can have 0-6 bars, if set, msix always lives in bar 0, data handling in bar 1. /// 1. its functionality is to read and write data for the guest, meanwhile, do a little diff --git a/pci/src/demo_device/base_device.rs b/pci/src/demo_device/base_device.rs index 819d296bc..852a1ac1c 100644 --- a/pci/src/demo_device/base_device.rs +++ b/pci/src/demo_device/base_device.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. /// BaseDevice is a simplest demo-pci-device. Its function is to -/// multiply data writed by two and return it when reading. +/// multiply data written by two and return it when reading. use crate::demo_dev::DeviceTypeOperation; use address_space::GuestAddress; pub use anyhow::{bail, Result}; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 25c0ae73d..be3031cb6 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -203,7 +203,7 @@ pub trait PciDevOps: Send + AsAny { parent_dev_path } - /// Fill the device path accroding to parent device path and device function. + /// Fill the device path according to parent device path and device function. fn populate_dev_path(&self, parent_dev_path: String, devfn: u8, dev_type: &str) -> String { let slot = pci_slot(devfn); let function = pci_func(devfn); diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 8901e4aa7..d6465c0e6 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -1074,7 +1074,7 @@ impl TestXhciPciDevice { self.event_list.pop_front() } - pub fn queue_device_reqeust(&mut self, slot_id: u32, device_req: &UsbDeviceRequest) { + pub fn queue_device_request(&mut self, slot_id: u32, device_req: &UsbDeviceRequest) { // Setup Stage. let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); @@ -1373,7 +1373,7 @@ impl TestXhciPciDevice { index: 0, length: 64, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::StallError as u32); @@ -1636,7 +1636,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_config_descriptor(&mut self, slot_id: u32) { @@ -1648,7 +1648,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_string_descriptor(&mut self, slot_id: u32, index: u16) { @@ -1660,7 +1660,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_status(&mut self, slot_id: u32) { @@ -1672,7 +1672,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_configuration(&mut self, slot_id: u32) { @@ -1684,7 +1684,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn set_configuration(&mut self, slot_id: u32, v: u16) { @@ -1696,7 +1696,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn clear_feature(&mut self, slot_id: u32, v: u16) { @@ -1708,7 +1708,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn set_feature(&mut self, slot_id: u32, v: u16) { @@ -1720,7 +1720,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_interface(&mut self, slot_id: u32, index: u16) { @@ -1732,7 +1732,7 @@ impl TestXhciPciDevice { index: index, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn set_interface(&mut self, slot_id: u32, v: u16, index: u16) { @@ -1744,7 +1744,7 @@ impl TestXhciPciDevice { index: index, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_hid_report_descriptor(&mut self, slot_id: u32, len: u16) { @@ -1755,7 +1755,7 @@ impl TestXhciPciDevice { index: 0, length: len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_report(&mut self, slot_id: u32) { @@ -1767,7 +1767,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn set_report(&mut self, slot_id: u32, v: u16) { @@ -1780,7 +1780,7 @@ impl TestXhciPciDevice { index: 0, length: buf_len, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_protocol(&mut self, slot_id: u32) { @@ -1791,7 +1791,7 @@ impl TestXhciPciDevice { index: 0, length: 1, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn set_protocol(&mut self, slot_id: u32, v: u16) { @@ -1802,7 +1802,7 @@ impl TestXhciPciDevice { index: 0, length: 0, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn get_idle(&mut self, slot_id: u32) { @@ -1813,7 +1813,7 @@ impl TestXhciPciDevice { index: 0, length: 1, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } pub fn set_idle(&mut self, slot_id: u32, v: u16) { @@ -1824,7 +1824,7 @@ impl TestXhciPciDevice { index: 0, length: 0, }; - self.queue_device_reqeust(slot_id, &device_req); + self.queue_device_request(slot_id, &device_req); } } diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index c5d1afa78..8150ef708 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -53,7 +53,7 @@ pub const VIRTIO_BLK_T_ILLGEAL: u32 = 32; pub const VIRTIO_BLK_S_OK: u8 = 0; /// IO error. pub const VIRTIO_BLK_S_IOERR: u8 = 1; -/// Unsupport request. +/// Unsupported request. pub const VIRTIO_BLK_S_UNSUPP: u8 = 2; pub const TIMEOUT_US: u64 = 15 * 1000 * 1000; @@ -341,7 +341,7 @@ pub fn virtio_blk_read( ); } -pub fn virtio_blk_defalut_feature(blk: Rc>) -> u64 { +pub fn virtio_blk_default_feature(blk: Rc>) -> u64 { let mut features = blk.borrow().get_device_features(); features &= !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index f9777fa23..abab4890e 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -369,7 +369,7 @@ pub enum RfbClientMessage { } pub struct TestPointEvent { - pub evnet_type: RfbClientMessage, + pub event_type: RfbClientMessage, pub button_mask: u8, pub x: u16, pub y: u16, @@ -378,7 +378,7 @@ pub struct TestPointEvent { impl TestPointEvent { fn new(button_mask: u8, x: u16, y: u16) -> Self { Self { - evnet_type: RfbClientMessage::RfbPointerEvent, + event_type: RfbClientMessage::RfbPointerEvent, button_mask, x, y, @@ -389,7 +389,7 @@ impl TestPointEvent { impl TestEventOperation for TestPointEvent { fn to_be_bytes(&self) -> Vec { let mut buf: Vec = Vec::new(); - buf.append(&mut (self.evnet_type as u8).to_be_bytes().to_vec()); + buf.append(&mut (self.event_type as u8).to_be_bytes().to_vec()); buf.append(&mut self.button_mask.to_be_bytes().to_vec()); buf.append(&mut self.x.to_be_bytes().to_vec()); buf.append(&mut self.y.to_be_bytes().to_vec()); @@ -571,7 +571,7 @@ impl DisplayMode { buf.drain(..name_len as usize); println!( - "Display infomation set by server:\n \ + "Display information set by server:\n \ application name: {:?} Image size: width: {:?}, height: {:?}\n \ big endian: {:?}, true color flag: {:?} red max {:?} red shift {:?}\n \ green max {:?} green shift {:?} blue max {:?} blue shift {:?}\n", @@ -784,7 +784,7 @@ impl VncClient { // Step 1: Exchange RFB Protocol: RFB 003.008. self.read_msg(&mut buf, 12)?; if "RFB 003.008\n".as_bytes().to_vec() != buf[..12].to_vec() { - bail!("Unsupport RFB version"); + bail!("Unsupported RFB version"); } self.write_msg(&"RFB 003.008\n".as_bytes().to_vec())?; buf.drain(..12); @@ -796,7 +796,7 @@ impl VncClient { buf.drain(..1); self.read_msg(&mut buf, auth_num as usize)?; if sec_type as u8 != buf[0] { - bail!("Unsupport security type!"); + bail!("Unsupported security type!"); } buf.drain(..auth_num as usize); self.write_msg(&(sec_type as u8).to_be_bytes().to_vec())?; @@ -896,7 +896,7 @@ impl VncClient { /// Send client cut event to VncServer. pub fn test_send_client_cut(&mut self, client_cut: TestClientCut) -> Result<()> { - println!("Test send client cut evnet."); + println!("Test send client cut event."); self.write_msg(&mut client_cut.to_be_bytes())?; Ok(()) } @@ -1001,7 +1001,7 @@ impl VncClient { _ => { assert!( false, - "unsupport event type from client: {}", + "unsupported event type from client: {}", frame_buff.enc ); } @@ -1138,7 +1138,7 @@ impl TestDemoGpuDevice { } /// Replace the surface of the display. - /// The width and height corresponding the width and height of the suface. + /// The width and height corresponding the width and height of the surface. pub fn replace_surface(&mut self, width: u32, height: u32, pixman_format: u32) { let cmd = TestGpuCmd { event_type: GpuEvent::ReplaceSurface, diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index a5fdb7197..c8def978a 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -57,7 +57,7 @@ fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { assert_eq!(file_size, mem::size_of::() as u32); // Check RSDP signature: "RSD PTR". assert_eq!(String::from_utf8_lossy(&read_data[..8]), "RSD PTR "); - // Check RSDP revison: 2. + // Check RSDP revision: 2. assert_eq!(read_data[15], 2); // Check 32-bit address of RSDT table: 0 @@ -99,7 +99,7 @@ fn check_madt(data: &[u8], cpu: u8) { let gicd_addr = LittleEndian::read_u64(&data[(offset + 8)..]); assert_eq!(gicd_addr, MEM_LAYOUT[LayoutEntryType::GicDist as usize].0); - // Check GIC verison + // Check GIC version assert_eq!(data[offset + 20], 3); // Check GIC CPU @@ -205,8 +205,8 @@ fn check_srat(data: &[u8]) { // -numa node,nodeid=1,cpus=4-7,memdev=mem1 for i in 0..2 { for j in 0..4 { - let proximity_domian = LittleEndian::read_u32(&data[(offset + 2)..]); - assert_eq!(proximity_domian, i); + let proximity_domain = LittleEndian::read_u32(&data[(offset + 2)..]); + assert_eq!(proximity_domain, i); let process_uid = LittleEndian::read_u32(&data[(offset + 6)..]); assert_eq!(process_uid, (i * 4) + j); offset = offset + mem::size_of::(); diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 8b57a1a54..bee1af2f1 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -475,7 +475,7 @@ fn balloon_huge_fun_001() { /// 2.set guest feature 0xFFFFFFFFFFFFFFFF /// Expect: /// 1.Success -/// 2.guest feature equel device feature +/// 2.guest feature equal device feature #[test] fn balloon_feature_001() { let pci_slot: u8 = 0x4; diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 16675884b..183bd4c06 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -16,7 +16,7 @@ use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::TestVringDescEntry; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; use mod_test::libdriver::virtio_block::{ - add_blk_request, create_blk, set_up, tear_down, virtio_blk_defalut_feature, virtio_blk_read, + add_blk_request, create_blk, set_up, tear_down, virtio_blk_default_feature, virtio_blk_read, virtio_blk_request, virtio_blk_write, TestVirtBlkReq, DEFAULT_IO_REQS, REQ_ADDR_LEN, REQ_DATA_LEN, REQ_STATUS_LEN, TIMEOUT_US, VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_GEOMETRY, @@ -151,7 +151,7 @@ fn virtio_blk_illegal_req( fn blk_basic() { let (blk, test_state, alloc, image_path) = set_up(); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -256,7 +256,7 @@ fn blk_features_negotiate() { fn blk_feature_seg_max() { let (blk, test_state, alloc, image_path) = set_up(); - let mut features = virtio_blk_defalut_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); features |= 1 << VIRTIO_BLK_F_SEG_MAX; let virtqueues = blk @@ -308,7 +308,7 @@ fn blk_feature_seg_max() { fn blk_feature_ro() { let (blk, test_state, alloc, image_path) = set_up(); - let mut features = virtio_blk_defalut_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -400,7 +400,7 @@ fn blk_feature_ro() { fn blk_feature_flush() { let (blk, test_state, alloc, image_path) = set_up(); - let mut features = virtio_blk_defalut_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); features |= 1 << VIRTIO_BLK_F_FLUSH; let virtqueues = blk @@ -458,7 +458,7 @@ fn blk_feature_mq() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let mut features = virtio_blk_defalut_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); features |= 1 << VIRTIO_BLK_F_MQ; let num_queues = 4; @@ -579,7 +579,7 @@ fn blk_all_features() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let mut features = virtio_blk_defalut_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); features |= 1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_FLUSH | 1 << VIRTIO_BLK_F_MQ; let num_queues = 4; @@ -631,7 +631,7 @@ fn blk_small_file_511b() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -731,7 +731,7 @@ fn blk_serial() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -788,7 +788,7 @@ fn blk_iops() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -873,7 +873,7 @@ fn blk_aio_native() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -926,7 +926,7 @@ fn blk_aio_io_uring() { let (blk, test_state, alloc) = create_blk(image_path.clone(), device_args, drive_args, other_args); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -970,7 +970,7 @@ fn blk_aio_io_uring() { fn blk_illegal_req_type() { let (blk, test_state, alloc, image_path) = set_up(); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -1005,7 +1005,7 @@ fn blk_illegal_req_type() { fn blk_rw_config() { let (blk, test_state, alloc, image_path) = set_up(); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -1043,7 +1043,7 @@ fn blk_rw_config() { fn blk_abnormal_req() { let (blk, test_state, alloc, image_path) = set_up(); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() @@ -1311,7 +1311,7 @@ fn blk_abnormal_req() { fn blk_parallel_req() { let (blk, test_state, alloc, image_path) = set_up(); - let mut features = virtio_blk_defalut_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); features |= 1 << VIRTIO_BLK_F_FLUSH; let virtqueues = blk @@ -1403,7 +1403,7 @@ fn blk_parallel_req() { fn blk_exceed_capacity() { let (blk, test_state, alloc, image_path) = set_up(); - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() diff --git a/tests/mod_test/tests/console_test.rs b/tests/mod_test/tests/console_test.rs index 3b30f94fd..d26135f34 100644 --- a/tests/mod_test/tests/console_test.rs +++ b/tests/mod_test/tests/console_test.rs @@ -182,19 +182,19 @@ fn console_rw_conifg() { assert_eq!( console.borrow().config_readw(0), ROWS_DEFAULT, - "The rows of the console config is uncorrect or the testcase parament is out of date!" + "The rows of the console config is incorrect or the testcase parament is out of date!" ); assert_eq!( console.borrow().config_readw(2), COLS_DEFAULT, - "The cols of the console config is uncorrect or the testcase parament is out of date!" + "The cols of the console config is incorrect or the testcase parament is out of date!" ); assert_eq!( console.borrow().config_readl(8), EMERG_WR_DEFAULT, - "The emerg_wr of the console config is uncorrect or the testcase parament is out of date!" + "The emerg_wr of the console config is incorrect or the testcase parament is out of date!" ); console.borrow().config_writew(0, 1); diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 4cca54664..ae566b739 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -19,7 +19,7 @@ use std::{cell::RefCell, fs::File, process::Command, rc::Rc, string::String}; pub struct MemoryTest { pub state: Rc>, - pub alloctor: Rc>, + pub allocator: Rc>, } const MEM_SIZE: u64 = 2048; // 2GB @@ -78,14 +78,14 @@ impl MemoryTest { MemoryTest { state: test_state, - alloctor: allocator, + allocator: allocator, } } } fn ram_read_write(memory_test: &MemoryTest) { let str = "test memory read write"; - let addr = memory_test.alloctor.borrow_mut().alloc(PAGE_SIZE); + let addr = memory_test.allocator.borrow_mut().alloc(PAGE_SIZE); memory_test .state @@ -163,7 +163,7 @@ fn io_region_read_write() { #[test] fn region_priority() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); - let addr = memory_test.alloctor.borrow_mut().alloc(PAGE_SIZE); + let addr = memory_test.allocator.borrow_mut().alloc(PAGE_SIZE); let data = [0x01u8; 8]; // Ram write and read. @@ -257,7 +257,7 @@ fn region_update_exception() { /// 1/2/6/7: Success. /// 4/5: Failed. /// 3: Got [0x02u8; 8] from the device. The read and write behavior is the same as io region. -/// 8: Got [0x00u8; 8] fro the device. The write opration does nothing, and read the original data. +/// 8: Got [0x00u8; 8] fro the device. The write operation does nothing, and read the original data. #[test] fn rom_device_region_readwrite() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index a286d41b8..ad14c94ed 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -80,7 +80,7 @@ pub const VIRTIO_NET_CTRL_RX_NOBCAST: u8 = 5; /// The driver can send control commands for MAC address filtering. pub const VIRTIO_NET_CTRL_MAC: u8 = 1; -/// The driver sets the unicast/multicast addresse table. +/// The driver sets the unicast/multicast address table. pub const VIRTIO_NET_CTRL_MAC_TABLE_SET: u8 = 0; /// The driver sets the default MAC address which rx filtering accepts. pub const VIRTIO_NET_CTRL_MAC_ADDR_SET: u8 = 1; @@ -465,7 +465,7 @@ fn set_up( create_net(id, mq, num_queues, with_mac, false) } -// Set the iothread argument in comand line. +// Set the iothread argument in command line. fn set_up_iothread( id: u8, mq: bool, @@ -1728,7 +1728,7 @@ fn virtio_net_abnormal_rx_tx_test() { test_state.borrow().writew(vqs[0].borrow().avail + 2, 0); net.borrow().set_driver_ok(); - // Test send 256 packet to execeed the handle_tx limitation once. + // Test send 256 packet to exceed the handle_tx limitation once. let request = get_arp_request(id); let length = request.as_bytes().len() as u64; let size = net.borrow().get_queue_size(); diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 08a15f848..d306187ff 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -16,7 +16,7 @@ use mod_test::libdriver::pci::*; use mod_test::libdriver::pci_bus::{PciBusOps, TestPciBus}; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps, VIRTIO_F_VERSION_1}; use mod_test::libdriver::virtio_block::{ - add_blk_request, virtio_blk_defalut_feature, virtio_blk_read, virtio_blk_write, + add_blk_request, virtio_blk_default_feature, virtio_blk_read, virtio_blk_write, VIRTIO_BLK_T_OUT, }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; @@ -555,7 +555,7 @@ fn validate_blk_io_success( test_state: Rc>, alloc: Rc>, ) { - let features = virtio_blk_defalut_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk .borrow_mut() .init_device(test_state.clone(), alloc.clone(), features, 1); @@ -1461,7 +1461,7 @@ fn test_pci_msix_global_ctl() { } /// Test whether the Mask bit in the vector register in msix table works well, -/// which means that when it's set, msix pends notification of the related vecotr, +/// which means that when it's set, msix pends notification of the related vector, /// and starts to notify as soon as the mask bit is cleared by the OS. #[test] fn test_pci_msix_local_ctl() { diff --git a/tests/mod_test/tests/ramfb_test.rs b/tests/mod_test/tests/ramfb_test.rs index 2687b2549..650044126 100644 --- a/tests/mod_test/tests/ramfb_test.rs +++ b/tests/mod_test/tests/ramfb_test.rs @@ -151,7 +151,7 @@ fn test_abnormal_param() { Err(e) => assert!(false, "{}", e), } - // Set frambuffer address is abnormal. + // Set framebuffer address is abnormal. let mut ramfb_config = RamfbConfig::new(ABNORMAL_FB_BASE); ramfb_config.write_to_file(&mut allocator.borrow_mut(), &test_state.borrow(), file_name); diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index cf2cf12d3..a0860ea6d 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -149,7 +149,7 @@ impl VirtioScsiTest { }]; let (cntlr, state, alloc) = scsi_test_init(cntlrcfg, scsi_devices.clone()); - let features = virtio_scsi_defalut_feature(cntlr.clone()); + let features = virtio_scsi_default_feature(cntlr.clone()); let queues = cntlr .borrow_mut() .init_device(state.clone(), alloc.clone(), features, 3); @@ -560,7 +560,7 @@ fn get_sense_bytes(sense: ScsiSense) -> [u8; TEST_VIRTIO_SCSI_SENSE_SIZE] { bytes } -pub fn virtio_scsi_defalut_feature(cntlr: Rc>) -> u64 { +pub fn virtio_scsi_default_feature(cntlr: Rc>) -> u64 { let mut features = cntlr.borrow().get_device_features(); features &= !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX); @@ -697,8 +697,8 @@ fn scsi_hd_basic_test() { }; vst.scsi_cdb_test(cdb_test_args); - // Test 3: scsi command: TESE_UNIT_READY. - // Test 3 Result: Check if scsi command TESE_UNIT_READY was handled successfully. + // Test 3: scsi command: TEST_UNIT_READY. + // Test 3 Result: Check if scsi command TEST_UNIT_READY was handled successfully. let mut test_unit_ready_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; test_unit_ready_cdb[0] = TEST_UNIT_READY; let cdb_test_args = CdbTest { @@ -1358,7 +1358,7 @@ fn scsi_cd_basic_test() { /// 1. Test scsi command REPORT_LUNS. /// 2. Test scsi command INQUIRY. /// 3. Test scsi command REQUEST_SENSE. -/// 4. Test scsi command TESE_UNIT_READY. +/// 4. Test scsi command TEST_UNIT_READY. /// 5. Test other scsi command, e.g. READ_CAPACITY_10. /// 6. Destroy device. /// Expect: @@ -1543,8 +1543,8 @@ fn scsi_target_cdb_test() { }; vst.scsi_cdb_test(cdb_test_args); - // Test 4: scsi command: TESE_UNIT_READY. - // Test 4 Result: Check if scsi command TESE_UNIT_READY was handled successfully. + // Test 4: scsi command: TEST_UNIT_READY. + // Test 4 Result: Check if scsi command TEST_UNIT_READY was handled successfully. let mut test_unit_ready_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; test_unit_ready_cdb[0] = TEST_UNIT_READY; let cdb_test_args = CdbTest { @@ -1600,13 +1600,13 @@ struct VirtioScsiConfig { /// TestStep: /// 1. Init process. /// 2. For every parameter in VirtioScsiConfig, do check just like: -/// Read default value -> Set other value -> Read value again -> Check if value was setted successfully. +/// Read default value -> Set other value -> Read value again -> Check if value was set successfully. /// 3. Destroy device. /// Note: /// 1. sense size and cdb size can not be changed in stratovirt now. So, they are 0 now. /// Expect: /// 1/2/3: success. -/// 2: only sense_size and cdb_size are setted successfully. +/// 2: only sense_size and cdb_size are set successfully. #[test] fn device_config_test() { let target = 0x0; @@ -1883,7 +1883,7 @@ fn aio_model_test() { } let (cntlr, state, alloc) = scsi_test_init(cntlrcfg, device_vec.clone()); - let features = virtio_scsi_defalut_feature(cntlr.clone()); + let features = virtio_scsi_default_feature(cntlr.clone()); let queues = cntlr .borrow_mut() .init_device(state.clone(), alloc.clone(), features, 3); @@ -2266,7 +2266,7 @@ fn send_cd_command_to_hd_test() { /// 1. Init process. /// 2. Send READ_10/WRITE_10 CDB. /// 2.1 READ_10/WRITE_10 transfer length is larger than disk size. -/// 2.2 READ_10/WRITE_10 read/write offset is larget than disk size. +/// 2.2 READ_10/WRITE_10 read/write offset is larger than disk size. /// 3. Wait for return value. /// 4. Destroy device. /// Expect: @@ -2321,7 +2321,7 @@ fn wrong_io_test() { vst.scsi_cdb_test(cdb_test_args); // Test3: scsi command: WRITE_10. - // Write to LBA(logical block address) 2K, transfer length 1 secotr and disk is 1KB size. + // Write to LBA(logical block address) 2K, transfer length 1 sector and disk is 1KB size. // Test Result: Check if scsi command WRITE_10 was failure. let mut write_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; write_cdb[0] = WRITE_10; diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 4bca842ab..c72b130e4 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -482,7 +482,7 @@ fn test_xhci_keyboard_over_ring_limit() { assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0]); } if i == 0 { - // Fake link new addrress. + // Fake link new address. xhci.queue_link_trb( slot_id, HID_DEVICE_ENDPOINT_ID, @@ -1337,7 +1337,7 @@ fn test_xhci_keyboard_device_init_control_command_invalid_order() { xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - // configure agian. + // configure again. xhci.configure_endpoint(slot_id, false); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); @@ -1527,7 +1527,7 @@ fn test_xhci_keyboard_device_init_invalid_request() { index: 10, length: 10, }; - xhci.queue_device_reqeust(slot_id, &device_req); + xhci.queue_device_request(slot_id, &device_req); xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); // Stall Error. let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); @@ -1549,7 +1549,7 @@ fn test_xhci_keyboard_device_init_invalid_request() { index: 2, length: 64, }; - xhci.queue_device_reqeust(slot_id, &device_req); + xhci.queue_device_request(slot_id, &device_req); xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); // Stall Error. let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); @@ -1805,7 +1805,7 @@ fn test_xhci_keyboard_device_init_reset_device() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let slot_id = evt.get_slot_id(); - // Case 1: reset afer enable slot. + // Case 1: reset after enable slot. xhci.reset_device(slot_id); let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); assert!(status & USB_STS_HCE == USB_STS_HCE); @@ -2031,7 +2031,7 @@ fn test_xhci_keyboard_device_init_device_miss_step() { qmp_send_key_event(test_state.borrow_mut(), KEYCODE_SPACE, true); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); - // NOTE: not kick acually, just print error. + // NOTE: not kick actually, just print error. assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); // configure endpoint @@ -2179,7 +2179,7 @@ fn test_xhci_tablet_over_ring_limit() { assert_eq!(buf, [0, 50, 0, 100, 0, 0]); } if i == 0 { - // Fake link new addrress. + // Fake link new address. xhci.queue_link_trb( slot_id, HID_DEVICE_ENDPOINT_ID, @@ -2483,7 +2483,7 @@ fn test_xhci_tablet_invalid_request() { let port_id = 1; let slot_id = xhci.init_device(port_id); - // unsupport report request for tablet. + // unsupported report request for tablet. xhci.test_invalie_device_request(slot_id, USB_INTERFACE_CLASS_OUT_REQUEST, HID_SET_REPORT, 0); // invalid descriptor type. xhci.test_invalie_device_request( diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index 37702f7a0..a2945d13e 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -404,7 +404,7 @@ fn resource_detach_dfx() { let (dpy, gpu) = set_up(&gpu_cfg); gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); - // invlid resource id + // invalid resource id assert_eq!( VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, resource_detach_backing(&gpu, VirtioGpuResourceDetachBacking::new(D_RES_ID),).hdr_type @@ -420,7 +420,7 @@ fn resource_detach_dfx() { .hdr_type ); - // invlid resource id + // invalid resource id assert_eq!( VIRTIO_GPU_RESP_ERR_UNSPEC, resource_detach_backing(&gpu, VirtioGpuResourceDetachBacking::new(D_RES_ID),).hdr_type @@ -440,7 +440,7 @@ fn resource_transfer_dfx() { let (dpy, gpu) = set_up(&gpu_cfg); let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); - // invlid resource id + // invalid resource id assert_eq!( VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, transfer_to_host( @@ -702,7 +702,7 @@ fn crash_dfx() { .request_complete(true, slice, None, None, Some(&mut resp)); assert_eq!(0x1234, resp.header.hdr_type); - // invlid hdr_ctx + // invalid hdr_ctx let mut hdr = VirtioGpuCtrlHdr::default(); hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 8d030edcf..c254d6bbf 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -241,7 +241,7 @@ fn do_event_idx_with_flag(flag: u16) { ); } - // Set avail->used_event to DEFAULT_IO_REQS which the rigth value is DEFAULT_IO_REQS - 1, + // Set avail->used_event to DEFAULT_IO_REQS which the right value is DEFAULT_IO_REQS - 1, // it will not get the interrupt which means event index feature works. vqs[0] .borrow() @@ -556,7 +556,7 @@ fn virtio_feature_event_idx() { do_event_idx_with_flag(0); } -/// Driver just enable these featues: +/// Driver just enable these features: /// VIRTIO_F_VERSION_1 | VIRTIO_RING_F_INDIRECT_DESC | VIRTIO_RING_F_EVENT_IDX /// and succeed to do the I/O request(normal + indirect) which has opened the event idx. /// TestStep: @@ -628,7 +628,7 @@ fn virtio_feature_indirect_and_event_idx() { ); } - // Set avail->used_event to DEFAULT_IO_REQS which the rigth value is DEFAULT_IO_REQS - 1, + // Set avail->used_event to DEFAULT_IO_REQS which the right value is DEFAULT_IO_REQS - 1, // it will not get the interrupt which means event index feature works. vqs[0] .borrow() @@ -775,7 +775,7 @@ fn virtio_init_device_abnormal_status() { /// Setting abnormal feature in device initialization. /// TestStep: /// 1. Init device. -/// negotiate unsupport features: +/// negotiate unsupported features: /// 1) 1 << 63; /// 2) 1 << 63 | 1 << VIRTIO_F_VERSION_1; /// 2. Do the I/O request. @@ -863,7 +863,7 @@ fn virtio_init_device_abnormal_features() { /// 5) set invalid desc/avail/used address: /// 0, 1 << 48, u64::MAX /// 6) set 0 to enable vq; -/// 7) check if the writed queue info is right. +/// 7) check if the written queue info is right. /// 2. Do the I/O request. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. @@ -1008,7 +1008,7 @@ fn virtio_init_device_abnormal_vring_info() { used = value; } blk.borrow().activate_queue(desc, avail, used); - // TEST if the writed queue info is right. + // TEST if the written queue info is right. if err_type == 10 { check_queue(blk.clone(), desc, avail, used); } @@ -1094,7 +1094,7 @@ fn virtio_init_device_abnormal_vring_info() { /// 1.1->1.2->1.3->1.5->1.4->1.6->1.7->1.8 /// 1.1->1.2->1.3->1.4->1.6->1.5->1.7->1.8 /// 1.1->1.2->1.3->1.4->1.7->1.6->1.5->1.8 -/// 2. Noraml init device. +/// 2. Normal init device. /// 3. Write and read. /// 4. Destroy device. /// Expect: @@ -1153,7 +1153,7 @@ fn virtio_init_device_out_of_order_1() { /// 1.1->1.2->1.4->1.5->1.6->1.7->1.8 /// 1.1->1.2->1.3->1.4->1.6->1.7->1.8 /// 1.1->1.2->1.3->1.4->1.5->1.6->1.8 -/// 2. Noraml init device. +/// 2. Normal init device. /// 3. Write and read. /// 4. Destroy device. /// Expect: @@ -1212,7 +1212,7 @@ fn virtio_init_device_out_of_order_2() { /// 1.1->1.2->1.3->1.5->1.8 /// 1.1->1.2->1.3->1.4->1.9(FAILED)->normal init process /// 1.1->1.2->1.3->1.4->1.9(FAILED)->1.2->1.3->1.4->1.5->1.6->1.7->1.8 -/// 2. Noraml init device. +/// 2. Normal init device. /// 3. Write and read. /// 4. Destroy device. /// Expect: @@ -1477,7 +1477,7 @@ fn virtio_io_abnormal_desc_len() { .borrow_mut() .add_indirect(test_state.clone(), indirect_req, true); vqs[0].borrow().update_avail(test_state.clone(), free_head); - // Test invalid lengh of the indirect desc elem. + // Test invalid length of the indirect desc elem. if length == 16 { test_state.borrow().writel( indirect_desc + offset_of!(VringDesc, len) as u64, diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 9e41fe8da..654a5021e 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -98,7 +98,7 @@ fn env_clean(test_dir: String) { Command::new("rm").arg("-rf").arg(test_dir).spawn().unwrap(); } -fn virtio_fs_defalut_feature(dev: Rc>) -> u64 { +fn virtio_fs_default_feature(dev: Rc>) -> u64 { let mut features = dev.borrow().get_device_features(); features &= !(VIRTIO_F_BAD_FEATURE | 1 << VIRTIO_RING_F_INDIRECT_DESC | 1 << VIRTIO_RING_F_EVENT_IDX); @@ -133,7 +133,7 @@ impl VirtioFsTest { let allocator = machine.allocator.clone(); let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); dev.borrow_mut().init(pci_slot, pci_fn); - let features = virtio_fs_defalut_feature(dev.clone()); + let features = virtio_fs_default_feature(dev.clone()); let queues = dev.borrow_mut() .init_device(test_state.clone(), allocator.clone(), features, 2); diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index b1a4e6410..a8e6d6cfa 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -569,7 +569,7 @@ fn test_vnc_kbd_mouse() { for &(button_mask, x, y) in POINTEVENTLIST[1..].iter() { assert!(vnc_client.test_point_event(button_mask, x, y).is_ok()); let msg = input.borrow_mut().read_input_event(); - // After sending the pointer event, the coordinate shoule be changed + // After sending the pointer event, the coordinate should be changed assert!(!(old_msg.button == msg.button && old_msg.x == msg.x && old_msg.y == msg.y)); old_msg = msg; println!("msg: {:?}", msg); @@ -726,7 +726,7 @@ fn test_rfb_version_abnormal(test_state: Rc>, port: u16) -> R Ok(()) } -fn test_unsupport_sec_type(test_state: Rc>, port: u16) -> Result<()> { +fn test_unsupported_sec_type(test_state: Rc>, port: u16) -> Result<()> { let mut buf: Vec = Vec::new(); let mut vnc_client = create_new_client(test_state, port).unwrap(); println!("Connect to server."); @@ -831,8 +831,8 @@ fn test_client_rand_bytes(test_state: Rc>, port: u16) -> Resu /// 3. VNC client send framebuffer request + NotIncremental. /// 4. VNC client check the image data from VNC server. /// Abnormal Situation: -/// 1. Unsupport RFB version -> expect 1 + 2. -/// 2. Unsupport security type -> expect 1 + 2. +/// 1. Unsupported RFB version -> expect 1 + 2. +/// 2. Unsupported security type -> expect 1 + 2. /// 3. The message of set pixel formal is abnormal -> expect 1 + 2. /// 4. Send the rand bytes from the client -> expect 2. /// 5. Send set encoding event: encoding number abnormal -> expect 2. @@ -856,7 +856,7 @@ fn test_rfb_abnormal() { let (gpu_list, input, test_state) = set_up(gpu_list, input_conf, port); assert!(test_rfb_version_abnormal(test_state.clone(), port).is_ok()); - assert!(test_unsupport_sec_type(test_state.clone(), port).is_ok()); + assert!(test_unsupported_sec_type(test_state.clone(), port).is_ok()); assert!(test_set_pixel_format_abnormal(test_state.clone(), port).is_ok()); assert!(test_set_encoding_abnormal(test_state.clone(), port).is_ok()); assert!(test_client_cut_event(test_state.clone(), port).is_ok()); diff --git a/ui/src/console.rs b/ui/src/console.rs index 497f8172b..c656aa985 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -380,7 +380,7 @@ pub fn display_graphic_update( Ok(()) } -/// Update cursor data in dispaly. +/// Update cursor data in display. /// /// # Arguments /// @@ -494,7 +494,7 @@ pub fn unregister_display(dcl: &Option>>) -> R Ok(()) } -/// Create a console and add into a gloabl list. Then returen a console id +/// Create a console and add into a global list. Then returen a console id /// for later finding the assigned console. pub fn console_init(dev_opts: Arc) -> Option>> { let mut locked_consoles = CONSOLES.lock().unwrap(); diff --git a/ui/src/utils.rs b/ui/src/utils.rs index fff54d516..7bb7844d3 100644 --- a/ui/src/utils.rs +++ b/ui/src/utils.rs @@ -85,7 +85,7 @@ impl BuffPool { /// Add data to the bufferpool. If the remaining /// free space is not enough, it will not work. So it is - /// recommended to call is_enouth() before this function. + /// recommended to call is_enough() before this function. /// /// # Example /// ```rust diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index ef2a4499c..118df91bb 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -74,7 +74,7 @@ impl SaslAuth { } } -/// Struct of sasl authentiation. +/// Struct of sasl authentication. #[derive(Debug, Clone)] pub struct SaslConfig { /// State of sasl connection . @@ -289,7 +289,7 @@ impl ClientIoHandler { vnc_flush(&client); return Err(err); } - // Accpet auth. + // Accept auth. buf.append(&mut (0_u32).as_bytes().to_vec()); } diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 2fcdc14d6..79f2e3227 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -20,7 +20,7 @@ use crate::{ pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, vnc::{ - auth_sasl::AuthState, framebuffer_upadate, round_up_div, server_io::VncServer, + auth_sasl::AuthState, framebuffer_update, round_up_div, server_io::VncServer, set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, }, @@ -449,7 +449,7 @@ impl ClientIoHandler { } /// Write a chunk of data to client socket. If there is some - /// error in io channel, then return and break the connnection. + /// error in io channel, then return and break the connection. fn client_handle_write(&mut self) { let client = self.client.clone(); if client.conn_state.lock().unwrap().dis_conn { @@ -1266,7 +1266,7 @@ pub fn desktop_resize( buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut (1_u16).to_be_bytes().to_vec()); - framebuffer_upadate(0, 0, width, height, ENCODING_DESKTOPRESIZE, buf); + framebuffer_update(0, 0, width, height, ENCODING_DESKTOPRESIZE, buf); Ok(()) } @@ -1280,7 +1280,7 @@ pub fn set_color_depth(client: &Arc, buf: &mut Vec) { buf.append(&mut (ServerMsg::FramebufferUpdate as u8).to_be_bytes().to_vec()); buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut (1_u16).to_be_bytes().to_vec()); - framebuffer_upadate(0, 0, client_width, client_height, ENCODING_WMVI, buf); + framebuffer_update(0, 0, client_width, client_height, ENCODING_WMVI, buf); buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); pixel_format_message(client, buf); } else if !locked_dpm.pf.is_default_pixel_format() { @@ -1329,7 +1329,7 @@ pub fn display_cursor_define( buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects - framebuffer_upadate( + framebuffer_update( cursor.hot_x as i32, cursor.hot_y as i32, cursor.width as i32, @@ -1352,7 +1352,7 @@ pub fn display_cursor_define( buf.append(&mut (0_u8).to_be_bytes().to_vec()); // padding buf.append(&mut (1_u16).to_be_bytes().to_vec()); // number of rects - framebuffer_upadate( + framebuffer_update( cursor.hot_x as i32, cursor.hot_y as i32, cursor.width as i32, @@ -1405,7 +1405,7 @@ pub fn vnc_flush(client: &Arc) { .lock() .unwrap() .write(1) - .unwrap_or_else(|e| error!("Error occurrs during data flush:{:?}", e)); + .unwrap_or_else(|e| error!("Error occurs during data flush:{:?}", e)); } /// Disconnect for vnc client. @@ -1415,5 +1415,5 @@ pub fn vnc_disconnect_start(client: &Arc) { .lock() .unwrap() .write(1) - .unwrap_or_else(|e| error!("Error occurrs during disconnection: {:?}", e)); + .unwrap_or_else(|e| error!("Error occurs during disconnection: {:?}", e)); } diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index f105aaef5..c766609e1 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -155,7 +155,7 @@ fn compress_each_tile<'a>( ); } if n_subtiles != 0 { - buf.append(&mut (n_subtiles as u8).to_be_bytes().to_vec()); // Num of SubRectanges. + buf.append(&mut (n_subtiles as u8).to_be_bytes().to_vec()); // Num of SubRectangles. buf.append(&mut tmp_buf); // SubrectsColoured. } } else { diff --git a/ui/src/vnc/encoding/test_hextile_image_data.rs b/ui/src/vnc/encoding/test_hextile_image_data.rs index bfedd8720..4aeadf1e4 100644 --- a/ui/src/vnc/encoding/test_hextile_image_data.rs +++ b/ui/src/vnc/encoding/test_hextile_image_data.rs @@ -274,7 +274,7 @@ pub const IMAGE_DATA_SINGLE_PIXEL: [u8; 4096] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; -/// The data stream obtained afetr the IMAGE_DATA_2 is compressed using the Hextile algorithm. +/// The data stream obtained after the IMAGE_DATA_2 is compressed using the Hextile algorithm. /// Total length is equal to 12. pub const TARGET_DATA_SINGLE_PIXEL: [u8; 12] = [ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 5489ec938..e2d498595 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -303,7 +303,7 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { Some(Arc::downgrade(&dcl)), )); - // Parameter configuation for VncServeer. + // Parameter configuration for VncServeer. make_server_config(&server, vnc_cfg, object)?; // Add an VncServer. @@ -481,7 +481,7 @@ pub fn update_server_surface(server: &Arc) -> Result<()> { ) } -/// Check if the suface for VncClient is need update +/// Check if the surface for VncClient is need update fn check_surface(locked_vnc_surface: &mut VncSurface, surface: &DisplaySurface) -> bool { let guest_width = get_image_width(surface.image); let guest_height = get_image_height(surface.image); @@ -525,7 +525,7 @@ fn check_rect(rect: &mut Rectangle, width: i32, height: i32) -> bool { /// /// * `x` `y` `w` `h` - coordinate, width, height /// * `buf` - send buffer -pub fn framebuffer_upadate(x: i32, y: i32, w: i32, h: i32, encoding: i32, buf: &mut Vec) { +pub fn framebuffer_update(x: i32, y: i32, w: i32, h: i32, encoding: i32, buf: &mut Vec) { buf.append(&mut (x as u16).to_be_bytes().to_vec()); buf.append(&mut (y as u16).to_be_bytes().to_vec()); buf.append(&mut (w as u16).to_be_bytes().to_vec()); @@ -658,11 +658,11 @@ fn send_framebuffer_update( ) -> i32 { match client_dpm.enc { ENCODING_HEXTILE => { - framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_HEXTILE, buf); + framebuffer_update(rect.x, rect.y, rect.w, rect.h, ENCODING_HEXTILE, buf); hextile_send_framebuffer_update(image, rect, client_dpm, buf) } _ => { - framebuffer_upadate(rect.x, rect.y, rect.w, rect.h, ENCODING_RAW, buf); + framebuffer_update(rect.x, rect.y, rect.w, rect.h, ENCODING_RAW, buf); raw_send_framebuffer_update(image, rect, client_dpm, buf) } } diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 593aae623..76cc857c4 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -71,7 +71,7 @@ pub struct VncServer { /// Display Change Listener. pub display_listener: Option>>, /// Saves all image regions that need to be updated. - /// It will be sent to vnc_worker thread, and be transfered into byte stream, + /// It will be sent to vnc_worker thread, and be transferred into byte stream, /// which will be sent to vnc client in main loop. pub rect_jobs: Arc>>, /// Connection limit. @@ -479,7 +479,7 @@ fn set_dirty_for_each_clients(x: usize, y: usize) -> Result<()> { Ok(()) } -/// Accpet client's connection. +/// Accept client's connection. /// /// # Arguments /// diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index a68839ab8..307b9e343 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -75,7 +75,7 @@ pub struct Iovec { trait AioContext { /// Submit IO requests to the OS, the nr submitted is returned. fn submit(&mut self, iocbp: &[*const AioCb]) -> Result; - /// Get the IO events of the requests sumbitted earlier. + /// Get the IO events of the requests submitted earlier. fn get_events(&mut self) -> &[AioEvent]; } @@ -499,7 +499,7 @@ pub fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { Ok(()) } -/// Write buf to iovec and return the writed number of bytes. +/// Write buf to iovec and return the written number of bytes. pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { let mut start: usize = 0; let mut end: usize = 0; @@ -523,7 +523,7 @@ pub fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { Ok(()) } -/// Read iovec to buf and return the readed number of bytes. +/// Read iovec to buf and return the read number of bytes. pub fn iov_to_buf_direct(iovec: &[Iovec], buf: &mut [u8]) -> Result { let mut start: usize = 0; let mut end: usize = 0; diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 0bdfe0867..1673aaa73 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -94,8 +94,8 @@ mod test { assert_eq!(*u32::from_bytes(&bytes).unwrap(), 0x0512_5634); // Convert failed because byte stream's length is not equal to u32's size - let mis_bytes = [0x0_u8, 0x0, 0x12]; - assert!(u32::from_bytes(&mis_bytes).is_none()); + let miss_bytes = [0x0_u8, 0x0, 0x12]; + assert!(u32::from_bytes(&miss_bytes).is_none()); } #[test] diff --git a/util/src/edid.rs b/util/src/edid.rs index 4f7b5aa39..912682589 100644 --- a/util/src/edid.rs +++ b/util/src/edid.rs @@ -254,7 +254,7 @@ impl EdidInfo { // serial LittleEndian::write_u32(&mut edid_array[offset + 5..offset + 9], self.serial); } else { - warn!("Unexpectd desc type"); + warn!("Unexpected desc type"); } } diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 7056564e3..229f09c5e 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -199,7 +199,7 @@ pub struct EventLoopContext { /// Used to avoid unnecessary kick operation when the /// next re-evaluation is performed before next epoll. kick_me: AtomicBool, - /// Used to identify that a kick operation ocurred. + /// Used to identify that a kick operation occurred. kicked: AtomicBool, /// Fds registered to the `EventLoop`. events: Arc>>>, diff --git a/util/src/unix.rs b/util/src/unix.rs index fcf0f3fcb..3ffc3d6ea 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -69,7 +69,7 @@ pub fn parse_unix_uri(uri: &str) -> Result { /// # Arguments /// /// * `file` - Backend file. -/// * `len` - Length of maping. +/// * `len` - Length of mapping. /// * `offset` - Offset in the file (or other object). /// * `read_only` - Allow to write or not. /// * `is_share` - Share the mapping or not. diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 0b273a2a6..e6184cd2b 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -1115,7 +1115,7 @@ impl FileSystem { FUSE_OK } - /// Read the file descriptor by file hander in the management of filesystem. + /// Read the file descriptor by file handler in the management of filesystem. /// /// # Arguments /// @@ -1134,7 +1134,7 @@ impl FileSystem { FUSE_OK } - /// write the file descriptor by file hander in the management of filesystem. + /// write the file descriptor by file handler in the management of filesystem. /// /// # Arguments /// @@ -1284,7 +1284,7 @@ impl FileSystem { } } else { if fchdir(&self.proc_dir) != FUSE_OK { - panic!("setxattr: failed to change process directoy"); + panic!("setxattr: failed to change process directory"); } let ret_ = set_xattr( @@ -1296,7 +1296,7 @@ impl FileSystem { ); if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("setxattr: failed to change directoy of root inode"); + panic!("setxattr: failed to change directory of root inode"); } ret_ @@ -1344,7 +1344,7 @@ impl FileSystem { } } else { if fchdir(&self.proc_dir) != FUSE_OK { - panic!("getxattr: failed to change process directoy"); + panic!("getxattr: failed to change process directory"); } let (buf_opt, ret) = get_xattr( @@ -1354,7 +1354,7 @@ impl FileSystem { ); if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("getxattr: failed to change directoy of root inode"); + panic!("getxattr: failed to change directory of root inode"); } if ret != FUSE_OK { return ret; @@ -1407,7 +1407,7 @@ impl FileSystem { } } else { if fchdir(&self.proc_dir) != FUSE_OK { - panic!("listxattr: failed to change process directoy"); + panic!("listxattr: failed to change process directory"); } let (buf_opt, ret) = list_xattr( @@ -1416,7 +1416,7 @@ impl FileSystem { ); if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("listxattr: failed to change directoy of root inode"); + panic!("listxattr: failed to change directory of root inode"); } if ret != FUSE_OK { return ret; @@ -1465,7 +1465,7 @@ impl FileSystem { } } else { if fchdir(&self.proc_dir) != FUSE_OK { - panic!("removexattr: failed to change process directoy"); + panic!("removexattr: failed to change process directory"); } let ret = remove_xattr( @@ -1474,7 +1474,7 @@ impl FileSystem { ); if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("removexattr: failed to change directoy of root inode"); + panic!("removexattr: failed to change directory of root inode"); } if ret != FUSE_OK { return ret; diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 718480651..084c78504 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -502,7 +502,7 @@ impl<'a> FuseIovec<'a> { /// error number to 0. If it is false, set error number from linux. /// * `body_opt` - The body for replying the fuse message needs to be written /// to fuse buffers. -/// * `body_len` - The length fo body for replying the fuse message. if the body +/// * `body_len` - The length of body for replying the fuse message. if the body /// is none, set the length to 0. pub fn reply_fuse_msg( writer: &mut FuseBuffer, diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs index fd8c529ed..3de2d6125 100644 --- a/vhost_user_fs/src/sandbox.rs +++ b/vhost_user_fs/src/sandbox.rs @@ -103,7 +103,7 @@ impl Sandbox { ) -> Result<()> { // If vhost_user_fs/src/set_signal_handlers do not register SIGTERM. // Child process became orphan process when parent process died. - // Beacuse child process can not receive signal notification from parent process. + // Because child process can not receive signal notification from parent process. // This is the signal that the calling process will get when its parent died. if unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) } == -1 { bail!("prctl fail"); diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 91551c0ec..5878ca377 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -139,7 +139,7 @@ impl BalloonedPageBitmap { } /// Read data segment starting at `iov.iov_base` + `offset` to buffer . -/// Return bufer . +/// Return buffer . /// /// # Arguments /// @@ -574,7 +574,7 @@ impl BalloonIoHandler { let elem = locked_queue .vring .pop_avail(&self.mem_space, self.driver_features) - .with_context(|| "Failed to pop avail ring for process baloon queue")?; + .with_context(|| "Failed to pop avail ring for process balloon queue")?; if elem.desc_num == 0 { break; @@ -921,7 +921,7 @@ impl Balloon { /// /// # Argument /// - /// * `size` - Target momery size. + /// * `size` - Target memory size. pub fn set_guest_memory_size(&mut self, size: u64) -> Result<()> { let host_page_size = host_page_size(); if host_page_size > BALLOON_PAGE_SIZE && !self.mem_info.lock().unwrap().has_huge_page() { diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index f399f6ff6..a5984728c 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -383,7 +383,7 @@ struct GpuIoHandler { cursor_queue: Arc>, /// The address space to which the GPU device belongs. mem_space: Arc, - /// Eventfd for contorl virtqueue. + /// Eventfd for control virtqueue. ctrl_queue_evt: Arc, /// Eventfd for cursor virtqueue. cursor_queue_evt: Arc, @@ -465,7 +465,7 @@ pub fn get_pixman_format(format: u32) -> Result { VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM => Ok(pixman_format_code_t::PIXMAN_r8g8b8a8), VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM => Ok(pixman_format_code_t::PIXMAN_x8b8g8r8), _ => { - bail!("Unsupport pixman format") + bail!("Unsupported pixman format") } } } @@ -476,7 +476,7 @@ pub fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) height as u64 * stride } -fn is_rect_in_resouce(rect: &VirtioGpuRect, res: &GpuResource) -> bool { +fn is_rect_in_resource(rect: &VirtioGpuRect, res: &GpuResource) -> bool { if rect .x_coord .checked_add(rect.width) @@ -553,7 +553,7 @@ impl GpuIoHandler { Ok(()) } else { bail!( - "Expected response length is {}, actual get reponse length {}.", + "Expected response length is {}, actual get response length {}.", size_of::(), size ); @@ -561,7 +561,7 @@ impl GpuIoHandler { }) { error!( "GuestError: An incomplete response will be used instead of the expected, because {:?}. \ - Also, be aware that the virtual machine may suspended if reponse is too short to \ + Also, be aware that the virtual machine may suspended if response is too short to \ carry the necessary information.", e ); @@ -849,7 +849,7 @@ impl GpuIoHandler { let res = &self.resources_list[res_index]; if info_set_scanout.rect.width < 16 || info_set_scanout.rect.height < 16 - || !is_rect_in_resouce(&info_set_scanout.rect, res) + || !is_rect_in_resource(&info_set_scanout.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {}).", @@ -886,7 +886,7 @@ impl GpuIoHandler { .image .is_null() { - error!("HostError: surface image create failed, check pixman libary."); + error!("HostError: surface image create failed, check pixman library."); return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } @@ -906,7 +906,7 @@ impl GpuIoHandler { .image .is_null() { - error!("HostError: surface pixman image create failed, please check pixman libary."); + error!("HostError: surface pixman image create failed, please check pixman library."); return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } } @@ -950,7 +950,7 @@ impl GpuIoHandler { .position(|x| x.resource_id == info_res_flush.resource_id) { let res = &self.resources_list[res_index]; - if !is_rect_in_resouce(&info_res_flush.rect, res) { + if !is_rect_in_resource(&info_res_flush.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, res.width, res.height, @@ -1046,7 +1046,7 @@ impl GpuIoHandler { return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; } - if !is_rect_in_resouce(&info_transfer.rect, res) { + if !is_rect_in_resource(&info_transfer.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {}).", res.resource_id, @@ -1195,7 +1195,7 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_index]; if !res.iov.is_empty() { error!( - "GuestError: The resource_id {} in resource attach backing request allready has iov.", + "GuestError: The resource_id {} in resource attach backing request already has iov.", info_attach_backing.resource_id ); return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); @@ -1349,7 +1349,7 @@ impl GpuIoHandler { Err(e) => { error!( "GuestError: Request will be ignored, because request header is incomplete and {:?}. \ - Also, be aware that the virtual machine may suspended if reponse does not \ + Also, be aware that the virtual machine may suspended if response does not \ carry the necessary information.", e ); diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index ce49d1baf..ab3fbb6b4 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -74,7 +74,7 @@ const CTRL_MAX_VLAN: u16 = 1 << 12; const MAX_MAC_ADDR_NUM: usize = 0xff; /// The header length of virtio net packet. const NET_HDR_LENGTH: usize = mem::size_of::(); -/// The legnth of vlan tag. +/// The length of vlan tag. const VLAN_TAG_LENGTH: usize = 4; /// The offset of vlan tpid for 802.1Q tag. const VLAN_TPID_LENGTH: usize = 2; @@ -768,7 +768,7 @@ impl NetIoHandler { self.rx.queue_full = true; break; } else if elem.in_iovec.is_empty() { - bail!("The lengh of in iovec is 0"); + bail!("The length of in iovec is 0"); } let iovecs = NetIoHandler::get_libc_iovecs( &self.mem_space, @@ -883,7 +883,7 @@ impl NetIoHandler { if elem.desc_num == 0 { break; } else if elem.out_iovec.is_empty() { - bail!("The lengh of out iovec is 0"); + bail!("The length of out iovec is 0"); } let iovecs = NetIoHandler::get_libc_iovecs( diff --git a/virtio/src/device/scsi/bus.rs b/virtio/src/device/scsi/bus.rs index d41be210e..2d6529be7 100644 --- a/virtio/src/device/scsi/bus.rs +++ b/virtio/src/device/scsi/bus.rs @@ -93,7 +93,7 @@ pub const WRITE_LONG_10: u8 = 0x3f; pub const CHANGE_DEFINITION: u8 = 0x40; pub const WRITE_SAME_10: u8 = 0x41; pub const UNMAP: u8 = 0x42; -/// The Read TOC command requests that the Drive read data from a table of contets. +/// The Read TOC command requests that the Drive read data from a table of contexts. pub const READ_TOC: u8 = 0x43; pub const REPORT_DENSITY_SUPPORT: u8 = 0x44; pub const GET_CONFIGURATION: u8 = 0x46; @@ -973,7 +973,7 @@ fn scsi_command_emulate_vpd_page( // Byte[6-7] = 0: Optimal transfer length granularity. // Byte[8-11]: Maximum transfer length. // Byte[12-15] = 0: Optimal Transfer Length. - // Byte[16-19] = 0: Maxium Prefetch Length. + // Byte[16-19] = 0: Maximum Prefetch Length. // Byte[20-23]: Maximum unmap lba count. // Byte[24-27]: Maximum unmap block descriptor count. // Byte[28-31]: Optimal unmap granulatity. @@ -1045,7 +1045,7 @@ fn scsi_command_emulate_target_inquiry(lun: u16, cmd: &ScsiCommand) -> Result Result { let mut start: usize = 0; let mut end: usize = 0; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 98972fcc0..845e66ff8 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -512,7 +512,7 @@ impl SplitVring { VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.actual_size()); // Make sure the event idx read from sys_mem is new. fence(Ordering::SeqCst); - // The GPA of avail_ring_host with avail table lenth has been checked in + // The GPA of avail_ring_host with avail table length has been checked in // is_invalid_memory which must not be overflowed. let used_event_addr = self.addr_cache.avail_ring_host + used_event_offset; let used_event = sys_mem @@ -671,7 +671,7 @@ impl SplitVring { ) -> Result<()> { let index_offset = VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.next_avail.0 % self.actual_size()); - // The GPA of avail_ring_host with avail table lenth has been checked in + // The GPA of avail_ring_host with avail table length has been checked in // is_invalid_memory which must not be overflowed. let desc_index_addr = self.addr_cache.avail_ring_host + index_offset; let desc_index = sys_mem diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index e4e612358..0d816936f 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1257,7 +1257,7 @@ impl PciDevOps for VirtioPciDevice { match self.device.lock().unwrap().device_type() { VIRTIO_TYPE_BLOCK => { // The virtio blk device is identified as a single-channel SCSI device, - // so add scsi controler indetification without channel, scsi-id and lun. + // so add scsi controller identification without channel, scsi-id and lun. let parent_dev_path = self.get_parent_dev_path(parent_bus); let mut dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); dev_path.push_str("/disk@0,0"); diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index e470b4b22..081583a05 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -59,7 +59,7 @@ impl Block { fn delete_event(&mut self) -> Result<()> { self.client .as_ref() - .with_context(|| "Failed to get client when stoping event")? + .with_context(|| "Failed to get client when stopping event")? .lock() .unwrap() .delete_event() diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 8359e9ebb..887b8b2a3 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -799,7 +799,7 @@ impl VhostOps for VhostUserClient { let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( - "The queue index {} is invaild {} for setting vring num", + "The queue index {} is invalid {} for setting vring num", queue_idx, client.max_queue_num ); @@ -925,7 +925,7 @@ impl VhostOps for VhostUserClient { let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( - "The queue index {} is invaild {} for setting vring kick", + "The queue index {} is invalid {} for setting vring kick", queue_idx, client.max_queue_num ); @@ -949,7 +949,7 @@ impl VhostOps for VhostUserClient { let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( - "The queue index {} is invaild {} for setting vring enable", + "The queue index {} is invalid {} for setting vring enable", queue_idx, client.max_queue_num ); diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index a2c823360..405be8977 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -61,7 +61,7 @@ impl Default for VirtioFsConfig { impl ByteCode for VirtioFsConfig {} struct VhostUserFsHandler { - interrup_cb: Arc, + interrupt_cb: Arc, host_notifies: Vec, } @@ -73,7 +73,7 @@ impl EventNotifierHelper for VhostUserFsHandler { read_fd(fd); let locked_vhost_user = vhost_user.lock().unwrap(); for host_notify in locked_vhost_user.host_notifies.iter() { - if let Err(e) = (locked_vhost_user.interrup_cb)( + if let Err(e) = (locked_vhost_user.interrupt_cb)( &VirtioInterruptType::Vring, Some(&host_notify.queue.lock().unwrap()), false, @@ -209,7 +209,7 @@ impl VirtioDevice for Fs { fn activate( &mut self, _mem_space: Arc, - interrup_cb: Arc, + interrupt_cb: Arc, queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { @@ -233,7 +233,7 @@ impl VirtioDevice for Fs { } let handler = VhostUserFsHandler { - interrup_cb, + interrupt_cb, host_notifies, }; @@ -269,7 +269,7 @@ impl VirtioDevice for Fs { self.config = VirtioFsConfig::default(); let client = match &self.client { - None => return Err(anyhow!("Failed to get client when reseting virtio fs")), + None => return Err(anyhow!("Failed to get client when resetting virtio fs")), Some(client_) => client_, }; client diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index d67c40fba..050a2ee71 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -78,7 +78,7 @@ impl Net { .delete_event() .with_context(|| "Failed to delete vhost-user net event")?; } - None => return Err(anyhow!("Failed to get client when stoping event")), + None => return Err(anyhow!("Failed to get client when stopping event")), }; if ((self.state.lock().unwrap().driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq -- Gitee From 68e92688108b24cf00cc08f4d607e92d39ecf6b6 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 6 Apr 2023 17:33:25 +0800 Subject: [PATCH 0954/2187] refactor: usb: rename ctrl to cntlr for controller Rename ctrl to cntlr for usb controller. Signed-off-by: yezengruan --- devices/src/usb/camera.rs | 2 +- devices/src/usb/keyboard.rs | 10 +++++----- devices/src/usb/mod.rs | 6 +++--- devices/src/usb/tablet.rs | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index c63f4300d..acd25be75 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -518,7 +518,7 @@ impl UsbDeviceOps for UsbCamera { self.id.clone() } - fn set_controller(&mut self, _ctrl: Weak>) {} + fn set_controller(&mut self, _cntlr: Weak>) {} fn get_controller(&self) -> Option>> { None diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 8205c9eed..3fc650b10 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -124,7 +124,7 @@ pub struct UsbKeyboard { usb_device: UsbDevice, hid: Hid, /// USB controller used to notify controller to transfer data. - ctrl: Option>>, + cntlr: Option>>, } pub struct UsbKeyboardAdapter { @@ -168,7 +168,7 @@ impl UsbKeyboard { id, usb_device: UsbDevice::new(), hid: Hid::new(HidType::Keyboard), - ctrl: None, + cntlr: None, } } @@ -234,12 +234,12 @@ impl UsbDeviceOps for UsbKeyboard { &mut self.usb_device } - fn set_controller(&mut self, ctrl: Weak>) { - self.ctrl = Some(ctrl); + fn set_controller(&mut self, cntlr: Weak>) { + self.cntlr = Some(cntlr); } fn get_controller(&self) -> Option>> { - self.ctrl.clone() + self.cntlr.clone() } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 3b5d41cf2..62ad1874d 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -296,7 +296,7 @@ pub trait UsbDeviceOps: Send + Sync { /// Set the controller which the USB device attached. /// USB device need to kick controller in some cases. - fn set_controller(&mut self, ctrl: Weak>); + fn set_controller(&mut self, cntlr: Weak>); /// Get the controller which the USB device attached. fn get_controller(&self) -> Option>>; @@ -381,8 +381,8 @@ pub trait UsbDeviceOps: Send + Sync { /// Notify controller to process data request. pub fn notify_controller(dev: &Arc>) -> Result<()> { let locked_dev = dev.lock().unwrap(); - let xhci = if let Some(ctrl) = &locked_dev.get_controller() { - ctrl.upgrade().unwrap() + let xhci = if let Some(cntlr) = &locked_dev.get_controller() { + cntlr.upgrade().unwrap() } else { bail!("USB controller not found"); }; diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index ed6b6aa9d..b7f5ef760 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -116,7 +116,7 @@ pub struct UsbTablet { usb_device: UsbDevice, hid: Hid, /// USB controller used to notify controller to transfer data. - ctrl: Option>>, + cntlr: Option>>, } impl UsbTablet { @@ -125,7 +125,7 @@ impl UsbTablet { id, usb_device: UsbDevice::new(), hid: Hid::new(HidType::Tablet), - ctrl: None, + cntlr: None, } } @@ -221,12 +221,12 @@ impl UsbDeviceOps for UsbTablet { &mut self.usb_device } - fn set_controller(&mut self, ctrl: Weak>) { - self.ctrl = Some(ctrl); + fn set_controller(&mut self, cntlr: Weak>) { + self.cntlr = Some(cntlr); } fn get_controller(&self) -> Option>> { - self.ctrl.clone() + self.cntlr.clone() } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { -- Gitee From 693de8b99b1418e376fdaab36a8431c6f1c9ed75 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 09:30:51 +0800 Subject: [PATCH 0955/2187] usb-storage: add device framework Add framework codes for USB storage device. Signed-off-by: yezengruan Signed-off-by: liuxiangdong --- devices/src/usb/mod.rs | 2 + devices/src/usb/storage.rs | 172 +++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 devices/src/usb/storage.rs diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 62ad1874d..0d679bf91 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -23,6 +23,8 @@ pub mod hid; #[cfg(not(target_env = "musl"))] pub mod keyboard; #[cfg(not(target_env = "musl"))] +pub mod storage; +#[cfg(not(target_env = "musl"))] pub mod tablet; pub mod xhci; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs new file mode 100644 index 000000000..8bbdc4848 --- /dev/null +++ b/devices/src/usb/storage.rs @@ -0,0 +1,172 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Mutex, Weak}; + +use byteorder::{ByteOrder, LittleEndian}; +use log::info; + +use super::xhci::xhci_controller::XhciDevice; +use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket}; + +pub const GET_MAX_LUN: u8 = 0xfe; +pub const MASS_STORAGE_RESET: u8 = 0xff; + +pub const CBW_SIGNATURE: u32 = 0x43425355; +pub const CSW_SIGNATURE: u32 = 0x53425355; +pub const CBW_FLAG_IN: u8 = 1 << 7; +pub const CBW_FLAG_OUT: u8 = 0; +pub const CBW_SIZE: u8 = 31; +pub const CSW_SIZE: u8 = 13; + +#[allow(dead_code)] +struct UsbStorageState { + mode: UsbMsdMode, + cbw: UsbMsdCbw, + csw: UsbMsdCsw, +} + +impl UsbStorageState { + fn new() -> Self { + UsbStorageState { + mode: UsbMsdMode::Cbw, + cbw: UsbMsdCbw::default(), + csw: UsbMsdCsw::new(), + } + } +} + +/// USB storage device. +pub struct UsbStorage { + id: String, + usb_device: UsbDevice, + state: UsbStorageState, + /// USB controller used to notify controller to transfer data. + cntlr: Option>>, +} + +#[allow(dead_code)] +#[derive(Debug)] +enum UsbMsdMode { + Cbw, + DataOut, + DataIn, + Csw, +} + +#[allow(dead_code)] +enum UsbMsdCswStatus { + Passed, + Failed, + PhaseError, +} + +#[allow(dead_code)] +#[derive(Debug, Default)] +struct UsbMsdCbw { + sig: u32, + tag: u32, + data_len: u32, + flags: u8, + lun: u8, + cmd_len: u8, + cmd: [u8; 16], +} + +#[allow(dead_code)] +impl UsbMsdCbw { + fn convert(&mut self, data: &[u8]) { + self.sig = LittleEndian::read_u32(&data[0..4]); + self.tag = LittleEndian::read_u32(&data[4..8]); + self.data_len = LittleEndian::read_u32(&data[8..12]); + self.flags = data[12]; + self.lun = data[13]; + self.cmd_len = data[14]; + self.cmd.copy_from_slice(&data[15..31]); + } +} + +#[allow(dead_code)] +#[derive(Debug)] +struct UsbMsdCsw { + sig: u32, + tag: u32, + residue: u32, + status: u8, +} + +#[allow(dead_code)] +impl UsbMsdCsw { + fn new() -> Self { + UsbMsdCsw { + sig: CSW_SIGNATURE, + tag: 0, + residue: 0, + status: 0, + } + } + + fn convert(&mut self, data: &mut [u8]) { + LittleEndian::write_u32(&mut data[0..4], self.sig); + LittleEndian::write_u32(&mut data[4..8], self.tag); + LittleEndian::write_u32(&mut data[8..12], self.residue); + data[12] = self.status; + } +} + +impl UsbStorage { + pub fn new() -> Self { + Self { + id: "".to_string(), + usb_device: UsbDevice::new(), + state: UsbStorageState::new(), + cntlr: None, + } + } +} + +impl UsbDeviceOps for UsbStorage { + fn reset(&mut self) { + info!("Storage device reset"); + self.usb_device.remote_wakeup = 0; + self.usb_device.addr = 0; + self.state = UsbStorageState::new(); + } + + fn handle_control(&mut self, _packet: &mut UsbPacket, _device_req: &UsbDeviceRequest) {} + + fn handle_data(&mut self, _packet: &mut UsbPacket) {} + + fn device_id(&self) -> String { + self.id.clone() + } + + fn get_usb_device(&self) -> &UsbDevice { + &self.usb_device + } + + fn get_mut_usb_device(&mut self) -> &mut UsbDevice { + &mut self.usb_device + } + + fn set_controller(&mut self, cntlr: Weak>) { + self.cntlr = Some(cntlr); + } + + fn get_controller(&self) -> Option>> { + self.cntlr.clone() + } + + fn get_wakeup_endpoint(&self) -> &UsbEndpoint { + self.usb_device.get_endpoint(true, 1) + } +} -- Gitee From e2f3e67abf657918b4c957bbe2b0bece87f1b7cb Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 09:46:00 +0800 Subject: [PATCH 0956/2187] usb-storage: add device descriptor Add USB storage device descriptor, and init the descriptor in storage device realize. Signed-off-by: yezengruan Signed-off-by: liuxiangdong --- devices/src/usb/config.rs | 1 + devices/src/usb/storage.rs | 118 ++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 964c38ac4..14c1d299b 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -226,5 +226,6 @@ pub const USB_CONFIGURATION_ATTR_REMOTE_WAKEUP: u8 = 1 << 5; // USB Class pub const USB_CLASS_HID: u8 = 3; +pub const USB_CLASS_MASS_STORAGE: u8 = 8; pub const USB_CLASS_VIDEO: u8 = 0xe; pub const USB_CLASS_MISCELLANEOUS: u8 = 0xef; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 8bbdc4848..f8e243b98 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -10,14 +10,119 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Mutex, Weak}; +use std::sync::{Arc, Mutex, Weak}; +use anyhow::Result; use byteorder::{ByteOrder, LittleEndian}; use log::info; +use once_cell::sync::Lazy; +use super::config::*; +use super::descriptor::{ + UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, + UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, +}; use super::xhci::xhci_controller::XhciDevice; use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket}; +// Storage device descriptor +static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDevice { + device_desc: UsbDeviceDescriptor { + bLength: USB_DT_DEVICE_SIZE, + bDescriptorType: USB_DT_DEVICE, + idVendor: USB_STORAGE_VENDOR_ID, + idProduct: 0x0001, + bcdDevice: 0, + iManufacturer: STR_MANUFACTURER_INDEX, + iProduct: STR_PRODUCT_STORAGE_INDEX, + iSerialNumber: STR_SERIAL_STORAGE_INDEX, + bcdUSB: 0x0200, + bDeviceClass: 0, + bDeviceSubClass: 0, + bDeviceProtocol: 0, + bMaxPacketSize0: 64, + bNumConfigurations: 1, + }, + configs: vec![Arc::new(UsbDescConfig { + config_desc: UsbConfigDescriptor { + bLength: USB_DT_CONFIG_SIZE, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: 0, + bNumInterfaces: 1, + bConfigurationValue: 1, + iConfiguration: STR_CONFIG_STORAGE_HIGH_INDEX, + bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, + bMaxPower: 50, + }, + iad_desc: vec![], + interfaces: vec![DESC_IFACE_STORAGE.clone()], + })], + }) +}); + +// Storage interface descriptor +static DESC_IFACE_STORAGE: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0, + bAlternateSetting: 0, + bNumEndpoints: 2, + bInterfaceClass: USB_CLASS_MASS_STORAGE, + bInterfaceSubClass: 0x06, // SCSI + bInterfaceProtocol: 0x50, // Bulk-only + iInterface: 0, + }, + other_desc: vec![], + endpoints: vec![ + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | 0x01, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 512, + bInterval: 0, + }, + extra: None, + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | 0x02, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 512, + bInterval: 0, + }, + extra: None, + }), + ], + }) +}); + +// CRC16 of "STRATOVIRT" +const USB_STORAGE_VENDOR_ID: u16 = 0xB74C; + +// String descriptor index +const STR_MANUFACTURER_INDEX: u8 = 1; +const STR_PRODUCT_STORAGE_INDEX: u8 = 2; +const STR_SERIAL_STORAGE_INDEX: u8 = 3; +const STR_CONFIG_STORAGE_HIGH_INDEX: u8 = 5; + +// String descriptor +const DESC_STRINGS: [&str; 7] = [ + "", + "StratoVirt", + "StratoVirt USB Storage", + "1", + "Full speed config (usb 1.1)", + "High speed config (usb 2.0)", + "Super speed config (usb 3.0)", +]; + pub const GET_MAX_LUN: u8 = 0xfe; pub const MASS_STORAGE_RESET: u8 = 0xff; @@ -132,6 +237,17 @@ impl UsbStorage { cntlr: None, } } + + pub fn realize(mut self) -> Result>> { + self.usb_device.reset_usb_endpoint(); + self.usb_device.speed = USB_SPEED_HIGH; + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; + + let storage: Arc> = Arc::new(Mutex::new(self)); + Ok(storage) + } } impl UsbDeviceOps for UsbStorage { -- Gitee From f0e165ace50289be73ff4d5f2d935aac8ccf4a27 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 10:02:46 +0800 Subject: [PATCH 0957/2187] usb-storage: support handle usb control USB storage needs support for 'Reset' and 'Get Max Lun' control requests. Signed-off-by: yezengruan Signed-off-by: liuxiangdong --- devices/src/usb/config.rs | 4 +++ devices/src/usb/storage.rs | 53 +++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 14c1d299b..a5b592fbd 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -169,6 +169,10 @@ pub const USB_INTERFACE_CLASS_IN_REQUEST: u8 = USB_DIRECTION_DEVICE_TO_HOST | USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE; pub const USB_INTERFACE_CLASS_OUT_REQUEST: u8 = USB_DIRECTION_HOST_TO_DEVICE | USB_TYPE_CLASS | USB_RECIPIENT_INTERFACE; +pub const USB_ENDPOINT_IN_REQUEST: u8 = + USB_DIRECTION_DEVICE_TO_HOST | USB_TYPE_STANDARD | USB_RECIPIENT_ENDPOINT; +pub const USB_ENDPOINT_OUT_REQUEST: u8 = + USB_DIRECTION_HOST_TO_DEVICE | USB_TYPE_STANDARD | USB_RECIPIENT_ENDPOINT; /// USB Standard Request Code. 9.4 Standard Device Requests pub const USB_REQUEST_GET_STATUS: u8 = 0; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index f8e243b98..36cae987c 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -14,7 +14,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use byteorder::{ByteOrder, LittleEndian}; -use log::info; +use log::{debug, error, info}; use once_cell::sync::Lazy; use super::config::*; @@ -23,7 +23,7 @@ use super::descriptor::{ UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use super::xhci::xhci_controller::XhciDevice; -use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket}; +use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; // Storage device descriptor static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { @@ -248,6 +248,35 @@ impl UsbStorage { let storage: Arc> = Arc::new(Mutex::new(self)); Ok(storage) } + + fn handle_control_packet(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + match device_req.request_type { + USB_ENDPOINT_OUT_REQUEST => { + if device_req.request == USB_REQUEST_CLEAR_FEATURE { + return; + } + } + USB_INTERFACE_CLASS_OUT_REQUEST => { + if device_req.request == MASS_STORAGE_RESET { + self.state.mode = UsbMsdMode::Cbw; + return; + } + } + USB_INTERFACE_CLASS_IN_REQUEST => { + if device_req.request == GET_MAX_LUN { + // TODO: Now only supports 1 LUN. + let maxlun = 0; + self.usb_device.data_buf[0] = maxlun; + packet.actual_length = 1; + return; + } + } + _ => {} + } + + error!("Unhandled USB Storage request {}", device_req.request); + packet.status = UsbPacketStatus::Stall; + } } impl UsbDeviceOps for UsbStorage { @@ -258,7 +287,25 @@ impl UsbDeviceOps for UsbStorage { self.state = UsbStorageState::new(); } - fn handle_control(&mut self, _packet: &mut UsbPacket, _device_req: &UsbDeviceRequest) {} + fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + debug!("Storage device handle_control request {:?}, ", device_req); + match self + .usb_device + .handle_control_for_descriptor(packet, device_req) + { + Ok(handled) => { + if handled { + debug!("Storage control handled by descriptor, return directly."); + return; + } + self.handle_control_packet(packet, device_req) + } + Err(e) => { + error!("Storage descriptor error {:?}", e); + packet.status = UsbPacketStatus::Stall; + } + } + } fn handle_data(&mut self, _packet: &mut UsbPacket) {} -- Gitee From 6c11d38cdfc312d4a4870e81d396dbb363b6b4a1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 14:21:55 +0800 Subject: [PATCH 0958/2187] virtio-scsi: separate the scsi controller from the scsi general logic virtio/scsi/controller should only have the logic of the virtio-scsi-pci device. Scsi/Bus and Scsi/Disk should have the common logic for all scsi devices. Split them. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 6 +- machine/src/standard_vm/mod.rs | 4 +- virtio/src/device/scsi/bus.rs | 407 ++++++++++----------- virtio/src/device/scsi/controller.rs | 504 ++++++++++++++------------- virtio/src/device/scsi/disk.rs | 3 +- 5 files changed, 464 insertions(+), 460 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1c6fe7ad1..393e7aff4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -82,8 +82,8 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiBus, - ScsiCntlr, ScsiDisk, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, + balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiCntlr, + ScsiDisk, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; use ScsiDisk::{SCSI_TYPE_DISK, SCSI_TYPE_ROM}; @@ -676,7 +676,7 @@ pub trait MachineOps { let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(device_cfg.clone()))); let bus_name = format!("{}.0", device_cfg.id); - ScsiBus::create_scsi_bus(&bus_name, &device)?; + ScsiCntlr::create_scsi_bus(&bus_name, &device)?; let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 652a716c8..0f8850a66 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -69,7 +69,7 @@ use pci::hotplug::{handle_plug, handle_unplug_request}; use pci::PciBus; use util::byte_code::ByteCode; use virtio::{ - qmp_balloon, qmp_query_balloon, Block, BlockState, ScsiBus, ScsiCntlr, VhostKern, VhostUser, + qmp_balloon, qmp_query_balloon, Block, BlockState, ScsiCntlr, VhostKern, VhostUser, VirtioDevice, VirtioNetState, VirtioPciDevice, }; @@ -820,7 +820,7 @@ impl StdMachine { let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(dev_cfg.clone()))); let bus_name = format!("{}.0", dev_cfg.id); - ScsiBus::create_scsi_bus(&bus_name, &device)?; + ScsiCntlr::create_scsi_bus(&bus_name, &device)?; let virtio_pci_dev = self .add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false) diff --git a/virtio/src/device/scsi/bus.rs b/virtio/src/device/scsi/bus.rs index 2d6529be7..3267095ce 100644 --- a/virtio/src/device/scsi/bus.rs +++ b/virtio/src/device/scsi/bus.rs @@ -13,22 +13,18 @@ use std::cmp; use std::collections::HashMap; use std::io::Write; -use std::sync::{Arc, Mutex, Weak}; +use std::os::unix::io::AsRawFd; +use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; use log::{debug, error, info}; -use crate::ScsiCntlr::{ - ScsiCntlr, ScsiCompleteCb, ScsiXferMode, VirtioScsiCmdReq, VirtioScsiCmdResp, - VirtioScsiRequest, VIRTIO_SCSI_CDB_DEFAULT_SIZE, VIRTIO_SCSI_S_OK, -}; use crate::ScsiDisk::{ ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, SECTOR_SHIFT, }; -use address_space::AddressSpace; use util::aio::{AioCb, Iovec, OpCode}; /// Scsi Operation code. @@ -171,6 +167,7 @@ pub const TASK_ABORTED: u8 = 0x40; pub const STATUS_MASK: u8 = 0x3e; +/// Scsi cdb length will be 6/10/12/16 bytes. pub const SCSI_CMD_BUF_SIZE: usize = 16; pub const SCSI_SENSE_BUF_SIZE: usize = 252; @@ -356,21 +353,28 @@ const GC_FC_CORE: u16 = 0x0001; /// The medium may be removed from the device. const GC_FC_REMOVEABLE_MEDIUM: u16 = 0x0003; +#[derive(Clone, PartialEq)] +pub enum ScsiXferMode { + /// TEST_UNIT_READY, ... + ScsiXferNone, + /// READ, INQUIRY, MODE_SENSE, ... + ScsiXferFromDev, + /// WRITE, MODE_SELECT, ... + ScsiXferToDev, +} + pub struct ScsiBus { /// Bus name. pub name: String, /// Scsi Devices attached to the bus. pub devices: HashMap<(u8, u16), Arc>>, - /// Scsi Controller which the bus orignates from. - pub parent_cntlr: Weak>, } impl ScsiBus { - pub fn new(bus_name: String, parent_cntlr: Weak>) -> ScsiBus { + pub fn new(bus_name: String) -> ScsiBus { ScsiBus { name: bus_name, devices: HashMap::new(), - parent_cntlr, } } @@ -405,47 +409,31 @@ impl ScsiBus { debug!("Can't find scsi device target {} lun {}", target, lun); None } +} - pub fn scsi_bus_parse_req_cdb( - &self, - cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], - dev: Arc>, - ) -> Option { - let buf: [u8; SCSI_CMD_BUF_SIZE] = (cdb[0..SCSI_CMD_BUF_SIZE]) - .try_into() - .expect("incorrect length"); - let command = cdb[0]; - let len = scsi_cdb_length(&cdb); - if len < 0 { - return None; - } - - let xfer = scsi_cdb_xfer(&cdb, dev); - if xfer < 0 { - return None; - } - - let lba = scsi_cdb_lba(&cdb); - if lba < 0 { - return None; - } - - Some(ScsiCommand { - buf, - command, - len: len as u32, - xfer: xfer as u32, - lba: lba as u64, - mode: scsi_cdb_xfer_mode(&cdb), - }) +fn scsi_bus_parse_req_cdb( + cdb: [u8; SCSI_CMD_BUF_SIZE], + dev: Arc>, +) -> Option { + let op = cdb[0]; + let len = scsi_cdb_length(&cdb); + if len < 0 { + return None; } -} -pub fn create_scsi_bus(bus_name: &str, scsi_cntlr: &Arc>) -> Result<()> { - let mut locked_scsi_cntlr = scsi_cntlr.lock().unwrap(); - let bus = ScsiBus::new(bus_name.to_string(), Arc::downgrade(scsi_cntlr)); - locked_scsi_cntlr.bus = Some(Arc::new(Mutex::new(bus))); - Ok(()) + // When CDB's Group Code is vendor specific or reserved, len/xfer/lba will be negative. + // So, don't need to check again afer checking in cdb length. + let xfer = scsi_cdb_xfer(&cdb, dev); + let lba = scsi_cdb_lba(&cdb); + + Some(ScsiCommand { + buf: cdb, + op, + len: len as u32, + xfer: xfer as u32, + lba: lba as u64, + mode: scsi_cdb_xfer_mode(&cdb), + }) } #[derive(Clone)] @@ -453,7 +441,7 @@ pub struct ScsiCommand { /// The Command Descriptor Block(CDB). pub buf: [u8; SCSI_CMD_BUF_SIZE], /// Scsi Operation Code. - pub command: u8, + pub op: u8, /// Length of CDB. pub len: u32, /// Transfer length. @@ -461,101 +449,123 @@ pub struct ScsiCommand { /// Logical Block Address. pub lba: u64, /// Transfer direction. - mode: ScsiXferMode, + pub mode: ScsiXferMode, } #[derive(Clone)] +pub struct ScsiCompleteCb { + pub req: Arc>, +} + +pub fn aio_complete_cb(aiocb: &AioCb, ret: i64) -> Result<()> { + let (status, sense) = if ret < 0 { + (CHECK_CONDITION, Some(SCSI_SENSE_IO_ERROR)) + } else { + (GOOD, None) + }; + + let sreq = &mut aiocb.iocompletecb.req.lock().unwrap(); + sreq.upper_req + .as_mut() + .scsi_request_complete_cb(status, sense)?; + Ok(()) +} + +pub trait ScsiRequestOps { + // Will be called in the end of this scsi instruction execution. + fn scsi_request_complete_cb(&mut self, status: u8, scsisense: Option) -> Result<()>; +} + pub struct ScsiRequest { - cmd: ScsiCommand, - _sense: [u8; SCSI_SENSE_BUF_SIZE], - _sense_size: u32, - _resid: u32, + pub cmd: ScsiCommand, + // Requested lun id for scsi request. It may be not equal to scsi device's lun id when it's a scsi target request. + pub req_lun: u16, pub opstype: u32, - pub virtioscsireq: Arc>>, - dev: Arc>, + // For datain and dataout. Can be empty when it's a ScsiXferMode::ScsiXferNone request. + pub iovec: Vec, + // Provided buffer's length. + pub datalen: u32, + pub dev: Arc>, + // Upper level request which contains this ScsiRequest. + pub upper_req: Box, } impl ScsiRequest { pub fn new( - req: Arc>>, - scsibus: Arc>, + cdb: [u8; SCSI_CMD_BUF_SIZE], + req_lun: u16, + iovec: Vec, + datalen: u32, scsidevice: Arc>, + upper_req: Box, ) -> Result { - let req_lock = req.lock().unwrap(); - let cdb = req_lock.req.cdb; - let req_size = req_lock.data_len; - - if let Some(cmd) = scsibus - .lock() - .unwrap() - .scsi_bus_parse_req_cdb(cdb, scsidevice.clone()) - { - let ops = cmd.command; - let opstype = scsi_operation_type(ops); - let _resid = cmd.xfer; - - if ops == WRITE_10 || ops == READ_10 { - let dev_lock = scsidevice.lock().unwrap(); - let disk_size = dev_lock.disk_sectors << SECTOR_SHIFT; - let disk_type = dev_lock.scsi_type; - drop(dev_lock); - let offset_shift = match disk_type { - SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, - _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, - }; - let offset = if let Some(off) = cmd.lba.checked_shl(offset_shift) { - off - } else { - bail!("Too large offset IO!"); - }; - - if offset - .checked_add(req_size as u64) - .filter(|&off| off <= disk_size) - .is_none() - { - bail!( - "Error CDB! ops {}, read/write length {} from {} is larger than disk size {}", - ops, req_size, offset, disk_size, - ); - } - } - - Ok(ScsiRequest { - cmd, - _sense: [0; SCSI_SENSE_BUF_SIZE], - _sense_size: 0, - _resid, - opstype, - virtioscsireq: req.clone(), - dev: scsidevice, - }) - } else { - bail!("Error CDB!"); + let cmd = scsi_bus_parse_req_cdb(cdb, scsidevice.clone()).with_context(|| "Error cdb!")?; + let op = cmd.op; + let opstype = scsi_operation_type(op); + + if op == WRITE_10 || op == READ_10 { + let dev_lock = scsidevice.lock().unwrap(); + let disk_size = dev_lock.disk_sectors << SECTOR_SHIFT; + let disk_type = dev_lock.scsi_type; + drop(dev_lock); + let offset_shift = match disk_type { + SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, + _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, + }; + let offset = cmd + .lba + .checked_shl(offset_shift) + .with_context(|| "Too large offset IO!")?; + + offset + .checked_add(datalen as u64) + .filter(|&off| off <= disk_size) + .with_context(|| { + format!( + "op 0x{:x} read/write length {} from {} is larger than disk size {}", + op, datalen, offset, disk_size + ) + })?; } + + Ok(ScsiRequest { + cmd, + req_lun, + opstype, + iovec, + datalen, + dev: scsidevice, + upper_req, + }) } - pub fn execute(&self, mut aiocb: AioCb) -> Result { - let dev_lock = self.dev.lock().unwrap(); - let offset = match dev_lock.scsi_type { + pub fn execute(self) -> Result { + let mode = self.cmd.mode.clone(); + let op = self.cmd.op; + let dev = self.dev.clone(); + let locked_dev = dev.lock().unwrap(); + let mut aio = locked_dev.aio.as_ref().unwrap().lock().unwrap(); + + let offset = match locked_dev.scsi_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, }; - aiocb.offset = (self.cmd.lba << offset) as usize; - - let mut aio = dev_lock.aio.as_ref().unwrap().lock().unwrap(); - - for iov in self.virtioscsireq.lock().unwrap().iovec.iter() { - let iovec = Iovec { - iov_base: iov.iov_base, - iov_len: iov.iov_len, - }; - aiocb.iovec.push(iovec); - // Note: total len of each req is no more than DESC_CHAIN_MAX_TOTAL_LEN (1 << 32). - aiocb.nbytes += iov.iov_len; - } + let mut aiocb = AioCb { + direct: locked_dev.config.direct, + req_align: locked_dev.req_align, + buf_align: locked_dev.buf_align, + file_fd: locked_dev.disk_image.as_ref().unwrap().as_raw_fd(), + opcode: OpCode::Noop, + iovec: self.iovec.clone(), + offset: (self.cmd.lba << offset) as usize, + nbytes: self.cmd.xfer as u64, + user_data: 0, + iocompletecb: ScsiCompleteCb { + req: Arc::new(Mutex::new(self)), + }, + }; - if self.cmd.command == SYNCHRONIZE_CACHE { + if op == SYNCHRONIZE_CACHE { aiocb.opcode = OpCode::Fdsync; aio.submit_request(aiocb) .with_context(|| "Failed to process scsi request for flushing")?; @@ -563,7 +573,7 @@ impl ScsiRequest { return Ok(0); } - match self.cmd.mode { + match mode { ScsiXferMode::ScsiXferFromDev => { aiocb.opcode = OpCode::Preadv; aio.submit_request(aiocb) @@ -583,24 +593,21 @@ impl ScsiRequest { Ok(0) } - pub fn emulate_execute( - &self, - iocompletecb: ScsiCompleteCb, - req_lun_id: u16, - found_lun_id: u16, - ) -> Result<()> { - debug!("scsi command is {:#x}", self.cmd.command); + pub fn emulate_execute(&mut self) -> Result<()> { + debug!("scsi command is {:#x}", self.cmd.op); let mut not_supported_flag = false; let mut sense = None; + let mut status = GOOD; + let found_lun = self.dev.lock().unwrap().config.lun; // Requested lun id is not equal to found device id means it may be a target request. // REPORT LUNS is also a target request command. - let result = if req_lun_id != found_lun_id || self.cmd.command == REPORT_LUNS { - match self.cmd.command { + let result = if self.req_lun != found_lun || self.cmd.op == REPORT_LUNS { + match self.cmd.op { REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), - INQUIRY => scsi_command_emulate_target_inquiry(req_lun_id, &self.cmd), + INQUIRY => scsi_command_emulate_target_inquiry(self.req_lun, &self.cmd), REQUEST_SENSE => { - if req_lun_id != 0 { + if self.req_lun != 0 { sense = Some(SCSI_SENSE_LUN_NOT_SUPPORTED); } // Scsi Device does not realize sense buffer now, so just return. @@ -615,7 +622,7 @@ impl ScsiRequest { } } else { // It's not a target request. - match self.cmd.command { + match self.cmd.op { REQUEST_SENSE => { sense = Some(SCSI_SENSE_NO_SENSE); Ok(Vec::new()) @@ -651,94 +658,63 @@ impl ScsiRequest { match result { Ok(outbuf) => { - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - GOOD, - sense, - &outbuf, - )?; + outbuf_to_iov(self.cmd.op, &outbuf, &self.iovec)?; } Err(ref e) => { if not_supported_flag { - info!( - "emulation scsi command {:#x} is no supported", - self.cmd.command - ); - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - CHECK_CONDITION, - Some(SCSI_SENSE_INVALID_OPCODE), - &Vec::new(), - )?; + info!("emulation scsi command {:#x} is no supported", self.cmd.op); + status = CHECK_CONDITION; + sense = Some(SCSI_SENSE_INVALID_OPCODE); } else { error!( "Error in processing scsi command {:#x}, err is {:?}", - self.cmd.command, e + self.cmd.op, e ); - self.cmd_complete( - &iocompletecb.mem_space, - VIRTIO_SCSI_S_OK, - CHECK_CONDITION, - Some(SCSI_SENSE_INVALID_FIELD), - &Vec::new(), - )?; + status = CHECK_CONDITION; + sense = Some(SCSI_SENSE_INVALID_FIELD); } } } - Ok(()) - } + self.upper_req + .as_mut() + .scsi_request_complete_cb(status, sense)?; - fn cmd_complete( - &self, - mem_space: &Arc, - response: u8, - status: u8, - scsisense: Option, - outbuf: &[u8], - ) -> Result<()> { - let mut req = self.virtioscsireq.lock().unwrap(); - - if let Some(sense) = scsisense { - req.resp.set_scsi_sense(sense); - } - req.resp.response = response; - req.resp.status = status; - req.resp.resid = 0; - - if !outbuf.is_empty() { - for (idx, iov) in req.iovec.iter().enumerate() { - if outbuf.len() as u64 > iov.iov_len { - debug!( - "cmd is {:x}, outbuf len is {}, iov_len is {}, idx is {}, iovec size is {}", - self.cmd.command, - outbuf.len(), - iov.iov_len, - idx, - req.iovec.len() - ); - } - - write_buf_mem(outbuf, iov.iov_len, iov.iov_base) - .with_context(|| "Failed to write buf for virtio scsi iov")?; - } - } - - req.complete(mem_space)?; Ok(()) } } -fn write_buf_mem(buf: &[u8], max: u64, hva: u64) -> Result<()> { +fn write_buf_mem(buf: &[u8], max: u64, hva: u64) -> Result { let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, cmp::min(buf.len(), max as usize)) }; - (&mut slice) + let size = (&mut slice) .write(buf) .with_context(|| format!("Failed to write buf(hva:{})", hva))?; + Ok(size) +} + +fn outbuf_to_iov(command: u8, outbuf: &[u8], iovec: &[Iovec]) -> Result<()> { + let mut start = 0; + for (idx, iov) in iovec.iter().enumerate() { + if start >= outbuf.len() { + return Ok(()); + } + + debug!( + "cmd is {:x}, outbuf len is {}, iov_len is {}, idx is {}, iovec size is {}", + command, + outbuf.len(), + iov.iov_len, + idx, + iovec.len() + ); + + start += write_buf_mem(&outbuf[start..], iov.iov_len, iov.iov_base) + .with_context(|| "Failed to write buf for scsi command result iov")?; + } + Ok(()) } @@ -757,14 +733,7 @@ fn scsi_operation_type(op: u8) -> u32 { } } -// lun: [u8, 8] -// | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | -// | 1 | target | lun | 0 | -pub fn virtio_scsi_get_lun(lun: [u8; 8]) -> u16 { - (((lun[2] as u16) << 8) | (lun[3] as u16)) & 0x3FFF -} - -fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { +fn scsi_cdb_length(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i32 { match cdb[0] >> 5 { // CDB[0]: Operation Code Byte. Bits[0-4]: Command Code. Bits[5-7]: Group Code. // Group Code | Meaning | @@ -784,7 +753,7 @@ fn scsi_cdb_length(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i32 { } } -fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc>) -> i32 { +fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i32 { let dev_lock = dev.lock().unwrap(); let block_size = dev_lock.block_size as i32; drop(dev_lock); @@ -827,7 +796,7 @@ fn scsi_cdb_xfer(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], dev: Arc i64 { +fn scsi_cdb_lba(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i64 { match cdb[0] >> 5 { // Group Code | Logical Block Address. | // 000b | Byte[1].bits[0-4]~Byte[3]. | @@ -842,7 +811,7 @@ fn scsi_cdb_lba(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> i64 { } } -fn scsi_cdb_xfer_mode(cdb: &[u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE]) -> ScsiXferMode { +fn scsi_cdb_xfer_mode(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> ScsiXferMode { match cdb[0] { WRITE_6 | WRITE_10 @@ -1200,7 +1169,7 @@ fn scsi_command_emulate_mode_sense( } drop(dev_lock); - if cmd.command == MODE_SENSE { + if cmd.op == MODE_SENSE { outbuf.resize(4, 0); // Device Specific Parameter. outbuf[2] = dev_specific_parameter; @@ -1212,7 +1181,7 @@ fn scsi_command_emulate_mode_sense( } if !dbd && nb_sectors > 0 { - if cmd.command == MODE_SENSE { + if cmd.op == MODE_SENSE { // Block Descriptor Length. outbuf[3] = 8; } else { @@ -1248,7 +1217,7 @@ fn scsi_command_emulate_mode_sense( // that is available to be transferred. The Mode data length does not include the // number of bytes in the Mode Data Length field. let buflen = outbuf.len(); - if cmd.command == MODE_SENSE { + if cmd.op == MODE_SENSE { outbuf[0] = (buflen - 1) as u8; } else { outbuf[0] = (((buflen - 2) >> 8) & 0xff) as u8; diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index de51213a4..9273afd30 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -21,22 +21,22 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use crate::ScsiBus::{ - virtio_scsi_get_lun, ScsiBus, ScsiRequest, ScsiSense, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, - SCSI_SENSE_INVALID_OPCODE, + aio_complete_cb, ScsiBus, ScsiCompleteCb, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + CHECK_CONDITION, EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, }; use crate::{ - report_virtio_error, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, + iov_to_buf, report_virtio_error, ElemIovec, Element, Queue, VirtioDevice, VirtioError, + VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; -use util::aio::{Aio, AioCb, Iovec, OpCode}; +use util::aio::{Aio, Iovec}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -52,11 +52,11 @@ const SCSI_MIN_QUEUE_NUM: usize = 3; /// Default values of the cdb and sense data size configuration fields. Cannot change cdb size /// and sense data size Now. /// To do: support Override CDB/sense data size.(Guest controlled) -pub const VIRTIO_SCSI_CDB_DEFAULT_SIZE: usize = 32; -pub const VIRTIO_SCSI_SENSE_DEFAULT_SIZE: usize = 96; +const VIRTIO_SCSI_CDB_DEFAULT_SIZE: usize = 32; +const VIRTIO_SCSI_SENSE_DEFAULT_SIZE: usize = 96; /// Basic length of fixed format sense data. -pub const SCSI_SENSE_LEN: u32 = 18; +const SCSI_SENSE_LEN: u32 = 18; /// Control type codes. /// Task Management Function. @@ -91,16 +91,6 @@ pub const VIRTIO_SCSI_S_FUNCTION_SUCCEEDED: u8 = 10; pub const VIRTIO_SCSI_S_FUNCTION_REJECTED: u8 = 11; pub const VIRTIO_SCSI_S_INCORRECT_LUN: u8 = 12; -#[derive(Clone)] -pub enum ScsiXferMode { - /// TEST_UNIT_READY, ... - ScsiXferNone, - /// READ, INQUIRY, MODE_SENSE, ... - ScsiXferFromDev, - /// WRITE, MODE_SELECT, ... - ScsiXferToDev, -} - #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] struct VirtioScsiConfig { @@ -398,7 +388,7 @@ pub struct VirtioScsiCmdReq { /// SAM command priority field. prio: u8, crn: u8, - pub cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], + cdb: [u8; VIRTIO_SCSI_CDB_DEFAULT_SIZE], } impl ByteCode for VirtioScsiCmdReq {} @@ -407,17 +397,17 @@ impl ByteCode for VirtioScsiCmdReq {} #[derive(Clone, Copy)] pub struct VirtioScsiCmdResp { /// Sense data length. - pub sense_len: u32, + sense_len: u32, /// Resudual bytes in data buffer. - pub resid: u32, + resid: u32, /// Status qualifier. status_qualifier: u16, /// Command completion status. - pub status: u8, + status: u8, /// Respinse value. - pub response: u8, + response: u8, /// Sense buffer data. - pub sense: [u8; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], + sense: [u8; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], } impl Default for VirtioScsiCmdResp { @@ -434,7 +424,7 @@ impl Default for VirtioScsiCmdResp { } impl VirtioScsiCmdResp { - pub fn set_scsi_sense(&mut self, sense: ScsiSense) { + fn set_scsi_sense(&mut self, sense: ScsiSense) { // Response code: current errors(0x70). self.sense[0] = 0x70; self.sense[2] = sense.key; @@ -449,25 +439,60 @@ impl VirtioScsiCmdResp { impl ByteCode for VirtioScsiCmdResp {} /// T: request; U: response. -pub struct VirtioScsiRequest { +#[derive(Clone)] +struct VirtioScsiRequest { + mem_space: Arc, queue: Arc>, desc_index: u16, /// Read or Write data, HVA, except resp. - pub iovec: Vec, - pub data_len: u32, - _cdb_size: u32, - _sense_size: u32, + iovec: Vec, + data_len: u32, mode: ScsiXferMode, interrupt_cb: Arc, driver_features: u64, /// resp GPA. resp_addr: GuestAddress, - pub req: T, - pub resp: U, + req: T, + resp: U, +} + +// Requests in Command Queue. +type CmdQueueRequest = VirtioScsiRequest; +// TMF Requests in Ctrl Queue. +type CtrlQueueTmfRequest = VirtioScsiRequest; +// An Requests in Command Queue. +type CtrlQueueAnRequest = VirtioScsiRequest; + +/// Convert GPA buffer iovec to HVA buffer iovec. +fn gpa_elemiovec_to_hva_iovec( + iovec: &[ElemIovec], + mem_space: &AddressSpace, + mut skip_size: u32, + iov_size: &mut u32, +) -> Result> { + let mut hva_iovec = Vec::new(); + for elem in iovec.iter() { + if skip_size >= elem.len { + skip_size -= elem.len; + } else { + let hva = mem_space + .get_host_address(elem.addr) + .with_context(|| "Map iov base failed")?; + let len = elem.len - skip_size; + hva_iovec.push(Iovec { + iov_base: hva + skip_size as u64, + iov_len: u64::from(len), + }); + *iov_size += len; + skip_size = 0; + } + } + + Ok(hva_iovec) } /// T: request; U:response. -impl VirtioScsiRequest { +impl VirtioScsiRequest { fn new( mem_space: &Arc, queue: Arc>, @@ -484,130 +509,118 @@ impl VirtioScsiRequest { ); } - let out_iov_elem = elem.out_iovec.get(0).unwrap(); - if out_iov_elem.len < size_of::() as u32 { - bail!( - "Invalid virtio scsi request: get length {}, expected length {}", - out_iov_elem.len, - size_of::(), - ); - } - - let scsi_req = mem_space - .read_object::(out_iov_elem.addr) - .with_context(|| VirtioError::ReadObjectErr("the scsi request", out_iov_elem.addr.0))?; + // Get request from virtqueue Element. + let mut req = T::default(); + iov_to_buf(mem_space, &elem.out_iovec, req.as_mut_bytes()).and_then(|size| { + if size < size_of::() { + bail!( + "Invalid length for request: get {}, expected {}", + size, + size_of::(), + ); + } + Ok(()) + })?; - let in_iov_elem = elem.in_iovec.get(0).unwrap(); - if in_iov_elem.len < size_of::() as u32 { - bail!( - "Invalid virtio scsi response: get length {}, expected length {}", - in_iov_elem.len, - size_of::() - ); - } - let scsi_resp = mem_space - .read_object::(in_iov_elem.addr) - .with_context(|| VirtioError::ReadObjectErr("the scsi response", in_iov_elem.addr.0))?; + // Get response from virtqueue Element. + let mut resp = U::default(); + iov_to_buf(mem_space, &elem.in_iovec, resp.as_mut_bytes()).and_then(|size| { + if size < size_of::() { + bail!( + "Invalid length for response: get {}, expected {}", + size, + size_of::(), + ); + } + Ok(()) + })?; let mut request = VirtioScsiRequest { + mem_space: mem_space.clone(), queue, desc_index: elem.index, iovec: Vec::with_capacity(elem.desc_num as usize), data_len: 0, - _cdb_size: VIRTIO_SCSI_CDB_DEFAULT_SIZE as u32, - _sense_size: VIRTIO_SCSI_SENSE_DEFAULT_SIZE as u32, mode: ScsiXferMode::ScsiXferNone, interrupt_cb, driver_features, - resp_addr: in_iov_elem.addr, - req: scsi_req, - resp: scsi_resp, + // Safety: in_iovec will not be empty since it has been checked after "iov_to_buf". + resp_addr: elem.in_iovec[0].addr, + req, + resp, }; + // Get possible dataout buffer from virtqueue Element. let mut out_len: u32 = 0; - let mut skip_out_size: u32 = size_of::() as u32; - for (_index, elem_iov) in elem.out_iovec.iter().enumerate() { - if skip_out_size >= elem_iov.len { - skip_out_size -= elem_iov.len; - } else if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { - let len = elem_iov.len - skip_out_size; - let iov = Iovec { - iov_base: hva + skip_out_size as u64, - iov_len: u64::from(len), - }; - out_len += len; - skip_out_size = 0; - request.iovec.push(iov); - } - } + let out_iovec = gpa_elemiovec_to_hva_iovec( + &elem.out_iovec, + mem_space, + size_of::() as u32, + &mut out_len, + )?; + // Get possible dataout buffer from virtqueue Element. let mut in_len: u32 = 0; - let mut skip_in_size: u32 = size_of::() as u32; - for (_index, elem_iov) in elem.in_iovec.iter().enumerate() { - if skip_in_size >= elem_iov.len { - skip_in_size -= elem_iov.len; - } else { - if out_len > 0 { - bail!("Wrong scsi request!"); - } - if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { - let len = elem_iov.len - skip_in_size; - let iov = Iovec { - iov_base: hva + skip_in_size as u64, - iov_len: u64::from(len), - }; - in_len += len; - skip_in_size = 0; - request.iovec.push(iov); - } - } + let in_iovec = gpa_elemiovec_to_hva_iovec( + &elem.in_iovec, + mem_space, + size_of::() as u32, + &mut in_len, + )?; + + if out_len > 0 && in_len > 0 { + warn!("Wrong scsi request! Don't support both datain and dataout buffer"); + request.data_len = u32::MAX; + return Ok(request); } if out_len > 0 { request.mode = ScsiXferMode::ScsiXferToDev; request.data_len = out_len; + request.iovec = out_iovec; } else if in_len > 0 { request.mode = ScsiXferMode::ScsiXferFromDev; request.data_len = in_len; + request.iovec = in_iovec; } Ok(request) } - pub fn complete(&self, mem_space: &Arc) -> Result<()> { - if let Err(ref e) = mem_space.write_object(&self.resp, self.resp_addr) { - bail!("Failed to write the scsi response {:?}", e); - } + fn complete(&self) -> Result<()> { + self.mem_space + .write_object(&self.resp, self.resp_addr) + .with_context(|| "Failed to write the scsi response")?; let mut queue_lock = self.queue.lock().unwrap(); // Note: U(response) is the header part of in_iov and self.data_len is the rest part of the in_iov or // the out_iov. in_iov and out_iov total len is no more than DESC_CHAIN_MAX_TOTAL_LEN(1 << 32). So, // it will not overflow here. - if let Err(ref e) = queue_lock.vring.add_used( - mem_space, - self.desc_index, - self.data_len + (size_of::() as u32), - ) { - bail!( - "Failed to add used ring(scsi completion), index {}, len {} {:?}", + queue_lock + .vring + .add_used( + &self.mem_space, self.desc_index, - self.data_len, - e - ); - } + self.data_len + (size_of::() as u32), + ) + .with_context(|| { + format!( + "Failed to add used ring(scsi completion), index {}, len {}", + self.desc_index, self.data_len + ) + })?; if queue_lock .vring - .should_notify(mem_space, self.driver_features) + .should_notify(&self.mem_space, self.driver_features) { - if let Err(e) = - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) - { - bail!( - "Failed to trigger interrupt(aio completion) for scsi controller, error is {:?}", - e - ); - } + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { + VirtioError::InterruptTrigger( + "scsi controller aio completion", + VirtioInterruptType::Vring, + ) + })?; } Ok(()) @@ -662,32 +675,30 @@ impl ScsiCtrlHandler { match ctrl_type { VIRTIO_SCSI_T_TMF => { - let mut tmf = - VirtioScsiRequest::::new( - &self.mem_space, - self.queue.clone(), - self.interrupt_cb.clone(), - self.driver_features, - &elem, - )?; + let mut tmf = CtrlQueueTmfRequest::new( + &self.mem_space, + self.queue.clone(), + self.interrupt_cb.clone(), + self.driver_features, + &elem, + )?; info!("incomplete tmf req, subtype {}!", tmf.req.subtype); // Scsi Task Management Function is not supported. // So, do nothing when stratovirt receives TMF request except responding guest scsi drivers. tmf.resp.response = VIRTIO_SCSI_S_OK; - tmf.complete(&self.mem_space)?; + tmf.complete()?; } VIRTIO_SCSI_T_AN_QUERY | VIRTIO_SCSI_T_AN_SUBSCRIBE => { - let mut an = - VirtioScsiRequest::::new( - &self.mem_space, - self.queue.clone(), - self.interrupt_cb.clone(), - self.driver_features, - &elem, - )?; + let mut an = CtrlQueueAnRequest::new( + &self.mem_space, + self.queue.clone(), + self.interrupt_cb.clone(), + self.driver_features, + &elem, + )?; an.resp.event_actual = 0; an.resp.response = VIRTIO_SCSI_S_OK; - an.complete(&self.mem_space)?; + an.complete()?; } _ => { bail!("Invalid ctrl type {}", ctrl_type); @@ -766,23 +777,25 @@ impl ScsiEventHandler { } } -fn complete_func(aiocb: &AioCb, ret: i64) -> Result<()> { - let complete_cb = &aiocb.iocompletecb; - let request = &aiocb.iocompletecb.req.lock().unwrap(); - let mut virtio_scsi_req = request.virtioscsireq.lock().unwrap(); - - virtio_scsi_req.resp.response = if ret < 0 { - VIRTIO_SCSI_S_FAILURE - } else { - VIRTIO_SCSI_S_OK - }; - - virtio_scsi_req.resp.status = GOOD; - virtio_scsi_req.resp.resid = 0; - virtio_scsi_req.resp.sense_len = 0; - virtio_scsi_req.complete(&complete_cb.mem_space) +impl ScsiRequestOps for CmdQueueRequest { + fn scsi_request_complete_cb(&mut self, status: u8, scsisense: Option) -> Result<()> { + if let Some(sense) = scsisense { + self.resp.set_scsi_sense(sense); + } + self.resp.response = VIRTIO_SCSI_S_OK; + self.resp.status = status; + self.complete()?; + + Ok(()) + } } +// lun: [u8, 8] +// | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | +// | 1 | target | lun | 0 | +fn virtio_scsi_get_lun_id(lun: [u8; 8]) -> u16 { + (((lun[2] as u16) << 8) | (lun[3] as u16)) & 0x3FFF +} pub struct ScsiCmdHandler { /// The scsi controller. scsibus: Arc>, @@ -827,7 +840,7 @@ impl EventNotifierHelper for ScsiCmdHandler { let mut locked_device = device.lock().unwrap(); let aio = if let Ok(engine_aio) = - Aio::new(Arc::new(complete_func), locked_device.config.aio_type) + Aio::new(Arc::new(aio_complete_cb), locked_device.config.aio_type) { engine_aio } else { @@ -885,6 +898,7 @@ impl ScsiCmdHandler { } fn handle_cmd_request(&mut self) -> Result<()> { + let mut sreq_queue = Vec::new(); loop { let mut queue = self.queue.lock().unwrap(); let elem = queue @@ -895,7 +909,7 @@ impl ScsiCmdHandler { } drop(queue); - let mut cmd = VirtioScsiRequest::::new( + let mut cmdq_request = CmdQueueRequest::new( &self.mem_space, self.queue.clone(), self.interrupt_cb.clone(), @@ -903,93 +917,115 @@ impl ScsiCmdHandler { &elem, )?; - let lun = cmd.req.lun; - let scsibus = self.scsibus.lock().unwrap(); - let req_lun_id = virtio_scsi_get_lun(lun); - - let scsidevice = if let Some(scsi_device) = scsibus.get_device(lun[1], req_lun_id) { - scsi_device - } else { - // No such target. Response VIRTIO_SCSI_S_BAD_TARGET to guest scsi drivers. - // It's not an error! - cmd.resp.response = VIRTIO_SCSI_S_BAD_TARGET; - cmd.complete(&self.mem_space)?; - debug!( - "no such scsi device target {}, lun {}", - lun[1], - virtio_scsi_get_lun(lun) - ); + let mut need_handle = false; + self.check_cmd_queue_request(&mut cmdq_request, &mut need_handle)?; + if !need_handle { continue; - }; - drop(scsibus); + } - let cmd_h = Arc::new(Mutex::new(cmd)); - let scsi_req = if let Ok(req) = - ScsiRequest::new(cmd_h.clone(), self.scsibus.clone(), scsidevice.clone()) - { - req - } else { - // Wrong scsi cdb. Response CHECK_CONDITION / SCSI_SENSE_INVALID_OPCODE to guest scsi drivers. - let mut cmd_lock = cmd_h.lock().unwrap(); - cmd_lock.resp.set_scsi_sense(SCSI_SENSE_INVALID_OPCODE); - cmd_lock.resp.status = CHECK_CONDITION; - cmd_lock.complete(&self.mem_space)?; - drop(cmd_lock); - - error!("Failed to create scsi request"); - continue; - }; + self.enqueue_scsi_request(&mut cmdq_request, &mut sreq_queue)?; + } - let scsi_device_lock = scsidevice.lock().unwrap(); - if scsi_req.opstype == EMULATE_SCSI_OPS { - let lun = scsi_device_lock.config.lun; - drop(scsi_device_lock); - let scsicompletecb = ScsiCompleteCb::new( - self.mem_space.clone(), - Arc::new(Mutex::new(scsi_req.clone())), - ); - // If found device's lun id is not equal to request lun id, this request is a target request. - scsi_req.emulate_execute(scsicompletecb, req_lun_id, lun)?; - } else { - let direct = scsi_device_lock.config.direct; - let disk_img = scsi_device_lock.disk_image.as_ref().unwrap().clone(); - let req_align = scsi_device_lock.req_align; - let buf_align = scsi_device_lock.buf_align; - drop(scsi_device_lock); - - let scsicompletecb = ScsiCompleteCb::new( - self.mem_space.clone(), - Arc::new(Mutex::new(scsi_req.clone())), - ); + if sreq_queue.is_empty() { + return Ok(()); + } - let aiocb = AioCb { - direct, - req_align, - buf_align, - file_fd: disk_img.as_raw_fd(), - opcode: OpCode::Noop, - iovec: Vec::new(), - offset: 0, - nbytes: 0, - user_data: 0, - iocompletecb: scsicompletecb, - }; - scsi_req.execute(aiocb)?; - } + for sreq in sreq_queue.into_iter() { + self.handle_scsi_request(sreq)?; } Ok(()) } -} -#[derive(Clone)] -pub struct ScsiCompleteCb { - pub mem_space: Arc, - req: Arc>, -} + fn check_cmd_queue_request( + &mut self, + qrequest: &mut CmdQueueRequest, + need_handle: &mut bool, + ) -> Result<()> { + if qrequest.data_len == u32::MAX && qrequest.mode == ScsiXferMode::ScsiXferNone { + // If neither dataout nor datain is empty, return VIRTIO_SCSI_S_FAILURE immediately. + qrequest.resp.response = VIRTIO_SCSI_S_FAILURE; + qrequest.complete()?; + return Ok(()); + } -impl ScsiCompleteCb { - fn new(mem_space: Arc, req: Arc>) -> Self { - ScsiCompleteCb { mem_space, req } + let target_id = qrequest.req.lun[1]; + let lun_id = virtio_scsi_get_lun_id(qrequest.req.lun); + let bus = self.scsibus.lock().unwrap(); + let device = bus.get_device(target_id, lun_id); + if device.is_none() { + // No such target. Response VIRTIO_SCSI_S_BAD_TARGET to guest scsi drivers. + // It's not an error! + qrequest.resp.response = VIRTIO_SCSI_S_BAD_TARGET; + qrequest.complete()?; + debug!("no such scsi device, target {} lun {}", target_id, lun_id); + return Ok(()); + } + + *need_handle = true; + Ok(()) } + + fn enqueue_scsi_request( + &mut self, + qrequest: &mut CmdQueueRequest, + sreq_queue: &mut Vec, + ) -> Result<()> { + let cdb: [u8; SCSI_CMD_BUF_SIZE] = + qrequest.req.cdb[0..SCSI_CMD_BUF_SIZE].try_into().unwrap(); + + let lun_id = virtio_scsi_get_lun_id(qrequest.req.lun); + let bus = self.scsibus.lock().unwrap(); + // Device will not be None because check_virtio_scsi_request has checked it. + let device = bus.get_device(qrequest.req.lun[1], lun_id).unwrap(); + + let scsi_req = ScsiRequest::new( + cdb, + lun_id, + qrequest.iovec.clone(), + qrequest.data_len, + device, + Box::new(qrequest.clone()), + ); + if scsi_req.is_err() { + // Wrong scsi cdb. Response CHECK_CONDITION / SCSI_SENSE_INVALID_OPCODE to guest scsi drivers. + qrequest.resp.set_scsi_sense(SCSI_SENSE_INVALID_OPCODE); + qrequest.resp.status = CHECK_CONDITION; + qrequest.complete()?; + error!("Failed to create scsi request, error virtio scsi request!"); + return Ok(()); + } + + let sreq = scsi_req.unwrap(); + if sreq.cmd.xfer > sreq.datalen && sreq.cmd.mode != ScsiXferMode::ScsiXferNone { + // Wrong virtio scsi request which doesn't provide enough datain/dataout buffer. + qrequest.resp.response = VIRTIO_SCSI_S_OVERRUN; + qrequest.complete()?; + debug!( + "command {:x} requested data's length({}),provided buffer length({})", + sreq.cmd.op, sreq.cmd.xfer, sreq.datalen + ); + return Ok(()); + } + + sreq_queue.push(sreq); + Ok(()) + } + + fn handle_scsi_request(&mut self, mut sreq: ScsiRequest) -> Result<()> { + if sreq.opstype == EMULATE_SCSI_OPS { + sreq.emulate_execute()?; + } else { + sreq.execute()?; + } + + Ok(()) + } +} + +pub fn create_scsi_bus(bus_name: &str, scsi_cntlr: &Arc>) -> Result<()> { + let mut locked_scsi_cntlr = scsi_cntlr.lock().unwrap(); + let bus = ScsiBus::new(bus_name.to_string()); + locked_scsi_cntlr.bus = Some(Arc::new(Mutex::new(bus))); + Ok(()) } diff --git a/virtio/src/device/scsi/disk.rs b/virtio/src/device/scsi/disk.rs index 86ab4eb20..f6d6da532 100644 --- a/virtio/src/device/scsi/disk.rs +++ b/virtio/src/device/scsi/disk.rs @@ -17,8 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; -use crate::ScsiBus::ScsiBus; -use crate::ScsiCntlr::ScsiCompleteCb; +use crate::ScsiBus::{ScsiBus, ScsiCompleteCb}; use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; use util::aio::Aio; -- Gitee From c06b75724162160566ddea84f37a47e1548e30cb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 16:03:52 +0800 Subject: [PATCH 0959/2187] virtio-scsi: refactoring scsi io processing code Refactoring scsi io processing code. 1) Simplify IO processing function. 2) Add some abnormal situation. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 20 ++--- machine/src/standard_vm/mod.rs | 9 +- virtio/src/device/scsi/bus.rs | 114 ++++++++++++++----------- virtio/src/device/scsi/controller.rs | 121 ++++++++++++++------------- 4 files changed, 143 insertions(+), 121 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 393e7aff4..46e27ee98 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -82,11 +82,12 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiCntlr, - ScsiDisk, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, - VirtioMmioState, VirtioNetState, VirtioPciDevice, + balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, + ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, + ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, + VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, + VirtioNetState, VirtioPciDevice, }; -use ScsiDisk::{SCSI_TYPE_DISK, SCSI_TYPE_ROM}; pub trait MachineOps { /// Calculate the ranges of memory according to architecture. @@ -673,10 +674,10 @@ pub trait MachineOps { MAX_VIRTIO_QUEUE, )); let device_cfg = parse_scsi_controller(cfg_args, queues_auto)?; - let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(device_cfg.clone()))); + let device = Arc::new(Mutex::new(ScsiCntlr::new(device_cfg.clone()))); let bus_name = format!("{}.0", device_cfg.id); - ScsiCntlr::create_scsi_bus(&bus_name, &device)?; + scsi_cntlr_create_scsi_bus(&bus_name, &device)?; let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) @@ -697,7 +698,7 @@ pub trait MachineOps { self.check_bootindex(bootindex) .with_context(|| "Failed to add scsi device for invalid bootindex")?; } - let device = Arc::new(Mutex::new(ScsiDisk::ScsiDevice::new( + let device = Arc::new(Mutex::new(ScsiDevice::new( device_cfg.clone(), scsi_type, self.get_drive_files(), @@ -717,10 +718,7 @@ pub trait MachineOps { .downcast_ref::() .unwrap(); let virtio_device = virtio_pcidev.get_virtio_device().lock().unwrap(); - let cntlr = virtio_device - .as_any() - .downcast_ref::() - .unwrap(); + let cntlr = virtio_device.as_any().downcast_ref::().unwrap(); let bus = cntlr.bus.as_ref().unwrap(); if bus diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 0f8850a66..82991be9b 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -69,8 +69,9 @@ use pci::hotplug::{handle_plug, handle_unplug_request}; use pci::PciBus; use util::byte_code::ByteCode; use virtio::{ - qmp_balloon, qmp_query_balloon, Block, BlockState, ScsiCntlr, VhostKern, VhostUser, - VirtioDevice, VirtioNetState, VirtioPciDevice, + qmp_balloon, qmp_query_balloon, Block, BlockState, + ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, + VhostKern, VhostUser, VirtioDevice, VirtioNetState, VirtioPciDevice, }; #[cfg(target_arch = "aarch64")] @@ -817,10 +818,10 @@ impl StdMachine { }; dev_cfg.check()?; - let device = Arc::new(Mutex::new(ScsiCntlr::ScsiCntlr::new(dev_cfg.clone()))); + let device = Arc::new(Mutex::new(ScsiCntlr::new(dev_cfg.clone()))); let bus_name = format!("{}.0", dev_cfg.id); - ScsiCntlr::create_scsi_bus(&bus_name, &device)?; + scsi_cntlr_create_scsi_bus(&bus_name, &device)?; let virtio_pci_dev = self .add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false) diff --git a/virtio/src/device/scsi/bus.rs b/virtio/src/device/scsi/bus.rs index 3267095ce..7169f542d 100644 --- a/virtio/src/device/scsi/bus.rs +++ b/virtio/src/device/scsi/bus.rs @@ -593,8 +593,69 @@ impl ScsiRequest { Ok(0) } + fn emulate_target_execute( + &self, + not_supported_flag: &mut bool, + sense: &mut Option, + ) -> Result> { + match self.cmd.op { + REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), + INQUIRY => scsi_command_emulate_target_inquiry(self.req_lun, &self.cmd), + REQUEST_SENSE => { + if self.req_lun != 0 { + *sense = Some(SCSI_SENSE_LUN_NOT_SUPPORTED); + } + // Scsi Device does not realize sense buffer now, so just return. + Ok(Vec::new()) + } + TEST_UNIT_READY => Ok(Vec::new()), + _ => { + *not_supported_flag = true; + *sense = Some(SCSI_SENSE_INVALID_OPCODE); + Err(anyhow!("Invalid emulation target scsi command")) + } + } + } + + fn emulate_device_execute( + &self, + not_supported_flag: &mut bool, + sense: &mut Option, + ) -> Result> { + match self.cmd.op { + REQUEST_SENSE => { + *sense = Some(SCSI_SENSE_NO_SENSE); + Ok(Vec::new()) + } + TEST_UNIT_READY => { + let dev_lock = self.dev.lock().unwrap(); + if dev_lock.disk_image.is_none() { + Err(anyhow!("No scsi backend!")) + } else { + Ok(Vec::new()) + } + } + INQUIRY => scsi_command_emulate_inquiry(&self.cmd, &self.dev), + READ_CAPACITY_10 => scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev), + MODE_SENSE | MODE_SENSE_10 => scsi_command_emulate_mode_sense(&self.cmd, &self.dev), + SERVICE_ACTION_IN_16 => scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev), + READ_DISC_INFORMATION => { + scsi_command_emulate_read_disc_information(&self.cmd, &self.dev) + } + GET_EVENT_STATUS_NOTIFICATION => { + scsi_command_emulate_get_event_status_notification(&self.cmd, &self.dev) + } + READ_TOC => scsi_command_emulate_read_toc(&self.cmd, &self.dev), + GET_CONFIGURATION => scsi_command_emulate_get_configuration(&self.cmd, &self.dev), + _ => { + *not_supported_flag = true; + Err(anyhow!("Emulation scsi command is not supported now!")) + } + } + } + pub fn emulate_execute(&mut self) -> Result<()> { - debug!("scsi command is {:#x}", self.cmd.op); + debug!("emulate scsi command is {:#x}", self.cmd.op); let mut not_supported_flag = false; let mut sense = None; let mut status = GOOD; @@ -603,57 +664,10 @@ impl ScsiRequest { // Requested lun id is not equal to found device id means it may be a target request. // REPORT LUNS is also a target request command. let result = if self.req_lun != found_lun || self.cmd.op == REPORT_LUNS { - match self.cmd.op { - REPORT_LUNS => scsi_command_emulate_report_luns(&self.cmd, &self.dev), - INQUIRY => scsi_command_emulate_target_inquiry(self.req_lun, &self.cmd), - REQUEST_SENSE => { - if self.req_lun != 0 { - sense = Some(SCSI_SENSE_LUN_NOT_SUPPORTED); - } - // Scsi Device does not realize sense buffer now, so just return. - Ok(Vec::new()) - } - TEST_UNIT_READY => Ok(Vec::new()), - _ => { - not_supported_flag = true; - sense = Some(SCSI_SENSE_INVALID_OPCODE); - Err(anyhow!("Invalid emulation target scsi command")) - } - } + self.emulate_target_execute(&mut not_supported_flag, &mut sense) } else { // It's not a target request. - match self.cmd.op { - REQUEST_SENSE => { - sense = Some(SCSI_SENSE_NO_SENSE); - Ok(Vec::new()) - } - TEST_UNIT_READY => { - let dev_lock = self.dev.lock().unwrap(); - if dev_lock.disk_image.is_none() { - Err(anyhow!("No scsi backend!")) - } else { - Ok(Vec::new()) - } - } - INQUIRY => scsi_command_emulate_inquiry(&self.cmd, &self.dev), - READ_CAPACITY_10 => scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev), - MODE_SENSE | MODE_SENSE_10 => scsi_command_emulate_mode_sense(&self.cmd, &self.dev), - SERVICE_ACTION_IN_16 => { - scsi_command_emulate_service_action_in_16(&self.cmd, &self.dev) - } - READ_DISC_INFORMATION => { - scsi_command_emulate_read_disc_information(&self.cmd, &self.dev) - } - GET_EVENT_STATUS_NOTIFICATION => { - scsi_command_emulate_get_event_status_notification(&self.cmd, &self.dev) - } - READ_TOC => scsi_command_emulate_read_toc(&self.cmd, &self.dev), - GET_CONFIGURATION => scsi_command_emulate_get_configuration(&self.cmd, &self.dev), - _ => { - not_supported_flag = true; - Err(anyhow!("Emulation scsi command is not supported now!")) - } - } + self.emulate_device_execute(&mut not_supported_flag, &mut sense) }; match result { diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi/controller.rs index 9273afd30..42265fbde 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi/controller.rs @@ -27,7 +27,7 @@ use crate::ScsiBus::{ use crate::{ iov_to_buf, report_virtio_error, ElemIovec, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_SCSI_F_CHANGE, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_TYPE_SCSI, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; use log::{debug, error, info, warn}; @@ -60,11 +60,11 @@ const SCSI_SENSE_LEN: u32 = 18; /// Control type codes. /// Task Management Function. -pub const VIRTIO_SCSI_T_TMF: u32 = 0; +const VIRTIO_SCSI_T_TMF: u32 = 0; /// Asynchronous notification query. -pub const VIRTIO_SCSI_T_AN_QUERY: u32 = 1; +const VIRTIO_SCSI_T_AN_QUERY: u32 = 1; /// Asynchronous notification subscription. -pub const VIRTIO_SCSI_T_AN_SUBSCRIBE: u32 = 2; +const VIRTIO_SCSI_T_AN_SUBSCRIBE: u32 = 2; /// Valid TMF Subtypes. pub const VIRTIO_SCSI_T_TMF_ABORT_TASK: u32 = 0; @@ -76,20 +76,17 @@ pub const VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: u32 = 5; pub const VIRTIO_SCSI_T_TMF_QUERY_TASK: u32 = 6; pub const VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: u32 = 7; -/// Response codes. -pub const VIRTIO_SCSI_S_OK: u8 = 0; -pub const VIRTIO_SCSI_S_OVERRUN: u8 = 1; -pub const VIRTIO_SCSI_S_ABORTED: u8 = 2; -pub const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; -pub const VIRTIO_SCSI_S_RESET: u8 = 4; -pub const VIRTIO_SCSI_S_BUSY: u8 = 5; -pub const VIRTIO_SCSI_S_TRANSPORT_FAILURE: u8 = 6; -pub const VIRTIO_SCSI_S_TARGET_FAILURE: u8 = 7; -pub const VIRTIO_SCSI_S_NEXUS_FAILURE: u8 = 8; -pub const VIRTIO_SCSI_S_FAILURE: u8 = 9; -pub const VIRTIO_SCSI_S_FUNCTION_SUCCEEDED: u8 = 10; -pub const VIRTIO_SCSI_S_FUNCTION_REJECTED: u8 = 11; -pub const VIRTIO_SCSI_S_INCORRECT_LUN: u8 = 12; +/// Command-specific response values. +/// The request was completed and the status byte if filled with a SCSI status code. +const VIRTIO_SCSI_S_OK: u8 = 0; +/// If the content of the CDB(such as the allocation length, parameter length or transfer size) requires +/// more data than is available in the datain and dataout buffers. +const VIRTIO_SCSI_S_OVERRUN: u8 = 1; +/// The request was never processed because the target indicated by lun does not exist. +const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; +/// Other host or driver error. In particular, if neither dataout nor datain is empty, and the VIRTIO_SCSI_F_INOUT +/// feature has not been negotiated, the request will be immediately returned with a response equal to VIRTIO_SCSI_S_FAILURE. +const VIRTIO_SCSI_S_FAILURE: u8 = 9; #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] @@ -171,8 +168,6 @@ impl VirtioDevice for ScsiCntlr { self.state.config_space.num_queues = self.config.queues; self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) - | (1_u64 << VIRTIO_SCSI_F_HOTPLUG) - | (1_u64 << VIRTIO_SCSI_F_CHANGE) | (1_u64 << VIRTIO_F_RING_EVENT_IDX) | (1_u64 << VIRTIO_F_RING_INDIRECT_DESC); @@ -256,14 +251,14 @@ impl VirtioDevice for ScsiCntlr { queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { - let queue_num = queues.len(); - if queue_num < SCSI_MIN_QUEUE_NUM { + if queues.len() < SCSI_MIN_QUEUE_NUM { bail!("virtio scsi controller queues num can not be less than 3!"); } + // Register event notifier for ctrl queue. let ctrl_queue = queues[0].clone(); let ctrl_queue_evt = queue_evts[0].clone(); - let ctrl_handler = ScsiCtrlHandler { + let ctrl_handler = ScsiCtrlQueueHandler { queue: ctrl_queue, queue_evt: ctrl_queue_evt, mem_space: mem_space.clone(), @@ -278,9 +273,10 @@ impl VirtioDevice for ScsiCntlr { &mut self.deactivate_evts, )?; + // Register event notifier for event queue. let event_queue = queues[1].clone(); let event_queue_evt = queue_evts[1].clone(); - let event_handler = ScsiEventHandler { + let event_handler = ScsiEventQueueHandler { _queue: event_queue, queue_evt: event_queue_evt, _mem_space: mem_space.clone(), @@ -296,9 +292,10 @@ impl VirtioDevice for ScsiCntlr { &mut self.deactivate_evts, )?; + // Register event notifier for command queues. for (index, cmd_queue) in queues[2..].iter().enumerate() { let bus = self.bus.as_ref().unwrap(); - let cmd_handler = ScsiCmdHandler { + let cmd_handler = ScsiCmdQueueHandler { scsibus: bus.clone(), queue: cmd_queue.clone(), queue_evt: queue_evts[index + 2].clone(), @@ -311,7 +308,7 @@ impl VirtioDevice for ScsiCntlr { let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))); if notifiers.is_empty() { - bail!("Error in create scsi device aio!"); + bail!("Error in creating scsi device aio!"); } register_event_helper( @@ -341,37 +338,41 @@ fn build_event_notifier(fd: RawFd, handler: Rc) -> EventNotifi } /// Task Managememt Request. +#[allow(unused)] #[derive(Copy, Clone, Debug, Default)] -pub struct VirtioScsiCtrlTmfReq { - pub ctrltype: u32, - pub subtype: u32, - pub lun: [u8; 8], - pub tag: u64, +struct VirtioScsiCtrlTmfReq { + ctrltype: u32, + subtype: u32, + lun: [u8; 8], + tag: u64, } impl ByteCode for VirtioScsiCtrlTmfReq {} +#[allow(unused)] #[derive(Copy, Clone, Debug, Default)] -pub struct VirtioScsiCtrlTmfResp { - pub response: u8, +struct VirtioScsiCtrlTmfResp { + response: u8, } impl ByteCode for VirtioScsiCtrlTmfResp {} /// Asynchronous notification query/subscription. +#[allow(unused)] #[derive(Copy, Clone, Debug, Default)] -pub struct VirtioScsiCtrlAnReq { - pub ctrltype: u32, - pub lun: [u8; 8], - pub event_requested: u32, +struct VirtioScsiCtrlAnReq { + ctrltype: u32, + lun: [u8; 8], + event_requested: u32, } impl ByteCode for VirtioScsiCtrlAnReq {} +#[allow(unused)] #[derive(Copy, Clone, Debug, Default)] -pub struct VirtioScsiCtrlAnResp { - pub event_actual: u32, - pub response: u8, +struct VirtioScsiCtrlAnResp { + event_actual: u32, + response: u8, } impl ByteCode for VirtioScsiCtrlAnResp {} @@ -404,7 +405,7 @@ pub struct VirtioScsiCmdResp { status_qualifier: u16, /// Command completion status. status: u8, - /// Respinse value. + /// Response value. response: u8, /// Sense buffer data. sense: [u8; VIRTIO_SCSI_SENSE_DEFAULT_SIZE], @@ -627,7 +628,7 @@ impl VirtioScsiReq } } -pub struct ScsiCtrlHandler { +pub struct ScsiCtrlQueueHandler { /// The ctrl virtqueue. queue: Arc>, /// EventFd for the ctrl virtqueue. @@ -642,9 +643,9 @@ pub struct ScsiCtrlHandler { device_broken: Arc, } -impl ScsiCtrlHandler { +impl ScsiCtrlQueueHandler { fn handle_ctrl(&mut self) -> Result<()> { - let result = self.handle_ctrl_request(); + let result = self.handle_ctrl_queue_requests(); if result.is_err() { report_virtio_error( self.interrupt_cb.clone(), @@ -656,7 +657,7 @@ impl ScsiCtrlHandler { result } - fn handle_ctrl_request(&mut self) -> Result<()> { + fn handle_ctrl_queue_requests(&mut self) -> Result<()> { loop { let mut queue = self.queue.lock().unwrap(); let elem = queue @@ -667,7 +668,10 @@ impl ScsiCtrlHandler { break; } - let ctrl_desc = elem.out_iovec.get(0).unwrap(); + let ctrl_desc = elem + .out_iovec + .get(0) + .with_context(|| "Error request in ctrl queue. Empty dataout buf!")?; let ctrl_type = self .mem_space .read_object::(ctrl_desc.addr) @@ -710,7 +714,7 @@ impl ScsiCtrlHandler { } } -impl EventNotifierHelper for ScsiCtrlHandler { +impl EventNotifierHelper for ScsiCtrlQueueHandler { fn internal_notifiers(handler: Arc>) -> Vec { let mut notifiers = Vec::new(); @@ -733,7 +737,7 @@ impl EventNotifierHelper for ScsiCtrlHandler { } } -pub struct ScsiEventHandler { +pub struct ScsiEventQueueHandler { /// The Event virtqueue. _queue: Arc>, /// EventFd for the Event virtqueue. @@ -748,7 +752,7 @@ pub struct ScsiEventHandler { device_broken: Arc, } -impl EventNotifierHelper for ScsiEventHandler { +impl EventNotifierHelper for ScsiEventQueueHandler { fn internal_notifiers(handler: Arc>) -> Vec { let mut notifiers = Vec::new(); @@ -771,7 +775,7 @@ impl EventNotifierHelper for ScsiEventHandler { } } -impl ScsiEventHandler { +impl ScsiEventQueueHandler { fn handle_event(&mut self) -> Result<()> { Ok(()) } @@ -796,7 +800,8 @@ impl ScsiRequestOps for CmdQueueRequest { fn virtio_scsi_get_lun_id(lun: [u8; 8]) -> u16 { (((lun[2] as u16) << 8) | (lun[3] as u16)) & 0x3FFF } -pub struct ScsiCmdHandler { + +pub struct ScsiCmdQueueHandler { /// The scsi controller. scsibus: Arc>, /// The Cmd virtqueue. @@ -813,7 +818,7 @@ pub struct ScsiCmdHandler { device_broken: Arc, } -impl EventNotifierHelper for ScsiCmdHandler { +impl EventNotifierHelper for ScsiCmdQueueHandler { fn internal_notifiers(handler: Arc>) -> Vec { let mut notifiers = Vec::new(); @@ -872,7 +877,7 @@ impl EventNotifierHelper for ScsiCmdHandler { } } -impl ScsiCmdHandler { +impl ScsiCmdQueueHandler { fn aio_complete_handler(&mut self, aio: &Arc>>) -> Result { aio.lock().unwrap().handle_complete().map_err(|e| { report_virtio_error( @@ -885,7 +890,7 @@ impl ScsiCmdHandler { } fn handle_cmd(&mut self) -> Result<()> { - let result = self.handle_cmd_request(); + let result = self.handle_cmd_queue_requests(); if result.is_err() { report_virtio_error( self.interrupt_cb.clone(), @@ -897,8 +902,9 @@ impl ScsiCmdHandler { result } - fn handle_cmd_request(&mut self) -> Result<()> { + fn handle_cmd_queue_requests(&mut self) -> Result<()> { let mut sreq_queue = Vec::new(); + loop { let mut queue = self.queue.lock().unwrap(); let elem = queue @@ -1023,7 +1029,10 @@ impl ScsiCmdHandler { } } -pub fn create_scsi_bus(bus_name: &str, scsi_cntlr: &Arc>) -> Result<()> { +pub fn scsi_cntlr_create_scsi_bus( + bus_name: &str, + scsi_cntlr: &Arc>, +) -> Result<()> { let mut locked_scsi_cntlr = scsi_cntlr.lock().unwrap(); let bus = ScsiBus::new(bus_name.to_string()); locked_scsi_cntlr.bus = Some(Arc::new(Mutex::new(bus))); -- Gitee From 2c9f5843f7fe767c8405ec7597b9fc71a31d7676 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 16:30:38 +0800 Subject: [PATCH 0960/2187] scsi: move scsi common logic to devices/ directory We have separated virtio-scsi-pci from scsi general logic. Move scsi common logic to devices/ directory for other devices which will use scsi command. Signed-off-by: liuxiangdong --- devices/src/lib.rs | 3 +++ {virtio/src/device => devices/src}/scsi/bus.rs | 0 {virtio/src/device => devices/src}/scsi/disk.rs | 0 {virtio/src/device => devices/src}/scsi/mod.rs | 1 - machine/src/lib.rs | 2 +- virtio/src/device/mod.rs | 2 +- virtio/src/device/{scsi/controller.rs => scsi_cntlr.rs} | 8 ++++---- virtio/src/lib.rs | 4 +--- 8 files changed, 10 insertions(+), 10 deletions(-) rename {virtio/src/device => devices/src}/scsi/bus.rs (100%) rename {virtio/src/device => devices/src}/scsi/disk.rs (100%) rename {virtio/src/device => devices/src}/scsi/mod.rs (96%) rename virtio/src/device/{scsi/controller.rs => scsi_cntlr.rs} (99%) diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 842511b7d..134be20b0 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -21,6 +21,7 @@ pub mod camera_backend; mod interrupt_controller; pub mod legacy; pub mod misc; +pub mod scsi; pub mod usb; #[cfg(target_arch = "aarch64")] @@ -29,3 +30,5 @@ pub use interrupt_controller::{ GIC_IRQ_MAX, }; pub use legacy::error::LegacyError as LegacyErrs; +pub use scsi::bus as ScsiBus; +pub use scsi::disk as ScsiDisk; diff --git a/virtio/src/device/scsi/bus.rs b/devices/src/scsi/bus.rs similarity index 100% rename from virtio/src/device/scsi/bus.rs rename to devices/src/scsi/bus.rs diff --git a/virtio/src/device/scsi/disk.rs b/devices/src/scsi/disk.rs similarity index 100% rename from virtio/src/device/scsi/disk.rs rename to devices/src/scsi/disk.rs diff --git a/virtio/src/device/scsi/mod.rs b/devices/src/scsi/mod.rs similarity index 96% rename from virtio/src/device/scsi/mod.rs rename to devices/src/scsi/mod.rs index b05f8ea5e..466de4075 100644 --- a/virtio/src/device/scsi/mod.rs +++ b/devices/src/scsi/mod.rs @@ -11,5 +11,4 @@ // See the Mulan PSL v2 for more details. pub mod bus; -pub mod controller; pub mod disk; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 46e27ee98..f6beb41b3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -53,6 +53,7 @@ use devices::usb::{ camera::UsbCamera, keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, }; +use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, @@ -84,7 +85,6 @@ use virtio::Gpu; use virtio::{ balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, - ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, }; diff --git a/virtio/src/device/mod.rs b/virtio/src/device/mod.rs index d2ab84f4d..67f3eede8 100644 --- a/virtio/src/device/mod.rs +++ b/virtio/src/device/mod.rs @@ -17,4 +17,4 @@ pub mod console; pub mod gpu; pub mod net; pub mod rng; -pub mod scsi; +pub mod scsi_cntlr; diff --git a/virtio/src/device/scsi/controller.rs b/virtio/src/device/scsi_cntlr.rs similarity index 99% rename from virtio/src/device/scsi/controller.rs rename to virtio/src/device/scsi_cntlr.rs index 42265fbde..be5b52ffd 100644 --- a/virtio/src/device/scsi/controller.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -20,16 +20,16 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; -use crate::ScsiBus::{ - aio_complete_cb, ScsiBus, ScsiCompleteCb, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, - CHECK_CONDITION, EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, -}; use crate::{ iov_to_buf, report_virtio_error, ElemIovec, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; +use devices::ScsiBus::{ + aio_complete_cb, ScsiBus, ScsiCompleteCb, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + CHECK_CONDITION, EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, +}; use log::{debug, error, info, warn}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index df80ccc04..50e405489 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -52,9 +52,7 @@ pub use device::console::{Console, VirtioConsoleState}; pub use device::gpu::*; pub use device::net::*; pub use device::rng::{Rng, RngState}; -pub use device::scsi::bus as ScsiBus; -pub use device::scsi::controller as ScsiCntlr; -pub use device::scsi::disk as ScsiDisk; +pub use device::scsi_cntlr as ScsiCntlr; pub use error::VirtioError; pub use error::*; pub use queue::*; -- Gitee From afee993c5d5c9237e86fd9fc2d920bbfc275f3e5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 3 Apr 2023 19:54:49 +0800 Subject: [PATCH 0961/2187] MST: add scsi status in scsi test Add scsi status in scsi test. Signed-off-by: liuxiangdong --- tests/mod_test/tests/scsi_test.rs | 94 ++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index a0860ea6d..7005ff916 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -62,7 +62,6 @@ const READ_TOC: u8 = 0x43; const VIRTIO_SCSI_S_OK: u8 = 0; const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; -const VIRTIO_SCSI_S_FAILURE: u8 = 9; /// Mode page codes for mode sense/set. const MODE_PAGE_CACHING: u8 = 0x08; @@ -93,6 +92,9 @@ const READ_TOC_DATA_LEN: u8 = 20; const READ_TOC_MSF_DATA_LEN: u8 = 12; const READ_TOC_FORMAT_DATA_LEN: u8 = 12; +const GOOD: u8 = 0x00; +const CHECK_CONDITION: u8 = 0x02; + struct VirtioScsiTest { cntlr: Rc>, scsi_devices: Vec, @@ -284,6 +286,7 @@ impl VirtioScsiTest { ); assert_eq!(scsi_resp.response, cdb_test.expect_response); + assert_eq!(scsi_resp.status, cdb_test.expect_status); if let Some(result_vec) = cdb_test.expect_result_data { assert_eq!(result_vec, data_in); } @@ -316,23 +319,28 @@ impl VirtioScsiTest { // was failure for scsi CD-ROM. let mut write_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; write_cdb[0] = WRITE_10; - write_cdb[8] = 0x1; // 1 sector. - let data = vec![0x8; 512]; - let write_data = String::from_utf8(data).unwrap(); - let expect_response = if scsi_type == ScsiDeviceType::ScsiHd { - VIRTIO_SCSI_S_OK + write_cdb[8] = 0x1; // 1 logical sector. CD: 2048 Bytes. HD: 512 Bytes. + let (expect_status, expect_sense, data) = if scsi_type == ScsiDeviceType::ScsiHd { + (GOOD, None, vec![0x8; 512]) } else { - VIRTIO_SCSI_S_FAILURE + ( + CHECK_CONDITION, + Some(get_sense_bytes(SCSI_SENSE_IO_ERROR)), + vec![0x8; 2048], + ) }; + let write_data = String::from_utf8(data).unwrap(); + let cdb_test_args = CdbTest { cdb: write_cdb, target, lun, data_out: Some(write_data.clone()), data_in_length: 0, - expect_response, + expect_response: VIRTIO_SCSI_S_OK, + expect_status: expect_status, expect_result_data: None, - expect_sense: None, + expect_sense: expect_sense, }; self.scsi_cdb_test(cdb_test_args); @@ -344,10 +352,10 @@ impl VirtioScsiTest { read_cdb[0] = READ_10; read_cdb[8] = 0x1; // 1 sector. - let (data_in_length, expect_result_data) = if scsi_type == ScsiDeviceType::ScsiHd { - (write_data.len(), Some(write_data.into_bytes())) - } else { - (0, None) + let data_in_length = write_data.len() as u32; + let expect_result_data = match scsi_type { + ScsiDeviceType::ScsiHd => Some(write_data.into_bytes()), + ScsiDeviceType::ScsiCd => None, }; let cdb_test_args = CdbTest { @@ -355,8 +363,9 @@ impl VirtioScsiTest { target, lun, data_out: None, - data_in_length: data_in_length as u32, // Read 1 sector data. + data_in_length, // Read 1 sector data. expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data, expect_sense: None, }; @@ -371,6 +380,7 @@ struct CdbTest { data_out: Option, data_in_length: u32, expect_response: u8, + expect_status: u8, expect_result_data: Option>, expect_sense: Option<[u8; TEST_VIRTIO_SCSI_SENSE_SIZE]>, } @@ -409,6 +419,12 @@ const SCSI_SENSE_NO_SENSE: ScsiSense = ScsiSense { ascq: 0, }; +const SCSI_SENSE_IO_ERROR: ScsiSense = ScsiSense { + key: 0x0b, + asc: 0, + ascq: 0x06, +}; + #[repr(C, packed)] #[derive(Clone, Copy, Debug, Default)] struct TestVirtioScsiCmdReq { @@ -663,6 +679,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_DATA_LEN as u32, expect_response: expect_result, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -692,6 +709,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: REPORT_LUNS_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -708,6 +726,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: 0, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -725,6 +744,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: READ_CAPACITY_10_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -776,6 +796,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -820,6 +841,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: MODE_SENSE_PAGE_ALL_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -842,6 +864,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -865,6 +888,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_SUPPORTED_VPD_PAGES_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -885,6 +909,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_UNIT_SERIAL_NUMBER_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -909,6 +934,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_DEVICE_IDENTIFICATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -930,6 +956,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_BLOCK_LIMITS_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -957,6 +984,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -985,6 +1013,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_LOGICAL_BLOCK_PROVISIONING_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1005,6 +1034,7 @@ fn scsi_hd_basic_test() { data_out: None, data_in_length: INQUIRY_REFERRALS_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(expect_sense), }; @@ -1083,6 +1113,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: MODE_SENSE_LEN_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1101,6 +1132,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: TEST_SCSI_SENSE_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_NO_SENSE)), }; @@ -1143,6 +1175,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: READ_TOC_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1175,6 +1208,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: READ_TOC_MSF_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1198,6 +1232,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: READ_TOC_FORMAT_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1236,6 +1271,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: READ_DISC_INFORMATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1302,6 +1338,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: GET_CONFIGURATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1337,6 +1374,7 @@ fn scsi_cd_basic_test() { data_out: None, data_in_length: GET_EVENT_STATUS_NOTIFICATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1384,6 +1422,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: REPORT_LUNS_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1407,6 +1446,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: INQUIRY_TARGET_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1439,6 +1479,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: INQUIRY_TARGET_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: Some(expect_result_vec), expect_sense: None, }; @@ -1461,6 +1502,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: INQUIRY_TARGET_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -1481,6 +1523,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: INQUIRY_TARGET_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), }; @@ -1501,6 +1544,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: INQUIRY_TARGET_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), }; @@ -1520,6 +1564,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: TEST_SCSI_SENSE_LEN, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_LUN_NOT_SUPPORTED)), }; @@ -1538,6 +1583,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: TEST_SCSI_SENSE_LEN, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -1554,6 +1600,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: 0, expect_response: VIRTIO_SCSI_S_OK, + expect_status: GOOD, expect_result_data: None, expect_sense: None, }; @@ -1571,6 +1618,7 @@ fn scsi_target_cdb_test() { data_out: None, data_in_length: READ_CAPACITY_10_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), }; @@ -2195,8 +2243,9 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: 0, + data_in_length: MODE_SENSE_LEN_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), }; @@ -2213,8 +2262,9 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: 0, + data_in_length: READ_DISC_INFORMATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), }; @@ -2230,8 +2280,9 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: 0, + data_in_length: GET_CONFIGURATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), }; @@ -2247,12 +2298,13 @@ fn send_cd_command_to_hd_test() { get_event_status_notification_cdb[8] = GET_EVENT_STATUS_NOTIFICATION_DATA_LEN; let cdb_test_args = CdbTest { - cdb: get_configuration_cdb, + cdb: get_event_status_notification_cdb, target, lun, data_out: None, - data_in_length: 0, + data_in_length: GET_EVENT_STATUS_NOTIFICATION_DATA_LEN as u32, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_FIELD)), }; @@ -2297,6 +2349,7 @@ fn wrong_io_test() { data_out: Some(write_data), data_in_length: 0, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), }; @@ -2315,6 +2368,7 @@ fn wrong_io_test() { data_out: None, data_in_length: 2048, // Read 2K data. expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), }; @@ -2337,6 +2391,7 @@ fn wrong_io_test() { data_out: Some(write_data), data_in_length: 0, expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), }; @@ -2357,6 +2412,7 @@ fn wrong_io_test() { data_out: None, data_in_length: 512, // 1 sector data. expect_response: VIRTIO_SCSI_S_OK, + expect_status: CHECK_CONDITION, expect_result_data: None, expect_sense: Some(get_sense_bytes(SCSI_SENSE_INVALID_OPCODE)), }; -- Gitee From 4007e7839252ff966d74d4697c092c655506556a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 10:20:22 +0800 Subject: [PATCH 0962/2187] usb-storage: support handle usb data USB storage handle data still has a lot things to do. Signed-off-by: yezengruan Signed-off-by: liuxiangdong --- devices/src/usb/mod.rs | 9 +++ devices/src/usb/storage.rs | 113 ++++++++++++++++++++++++++++++++++--- 2 files changed, 115 insertions(+), 7 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 0d679bf91..1dcea7c7e 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -507,6 +507,15 @@ impl UsbPacket { } self.actual_length = copied as u32; } + + pub fn get_iovecs_size(&mut self) -> usize { + let mut size = 0; + for iov in &self.iovecs { + size += iov.iov_len; + } + + size + } } impl Default for UsbPacket { diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 36cae987c..f67d0989e 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -133,7 +133,6 @@ pub const CBW_FLAG_OUT: u8 = 0; pub const CBW_SIZE: u8 = 31; pub const CSW_SIZE: u8 = 13; -#[allow(dead_code)] struct UsbStorageState { mode: UsbMsdMode, cbw: UsbMsdCbw, @@ -159,7 +158,6 @@ pub struct UsbStorage { cntlr: Option>>, } -#[allow(dead_code)] #[derive(Debug)] enum UsbMsdMode { Cbw, @@ -175,7 +173,6 @@ enum UsbMsdCswStatus { PhaseError, } -#[allow(dead_code)] #[derive(Debug, Default)] struct UsbMsdCbw { sig: u32, @@ -187,7 +184,6 @@ struct UsbMsdCbw { cmd: [u8; 16], } -#[allow(dead_code)] impl UsbMsdCbw { fn convert(&mut self, data: &[u8]) { self.sig = LittleEndian::read_u32(&data[0..4]); @@ -200,7 +196,6 @@ impl UsbMsdCbw { } } -#[allow(dead_code)] #[derive(Debug)] struct UsbMsdCsw { sig: u32, @@ -209,7 +204,6 @@ struct UsbMsdCsw { status: u8, } -#[allow(dead_code)] impl UsbMsdCsw { fn new() -> Self { UsbMsdCsw { @@ -277,6 +271,94 @@ impl UsbStorage { error!("Unhandled USB Storage request {}", device_req.request); packet.status = UsbPacketStatus::Stall; } + + fn handle_token_out(&mut self, packet: &mut UsbPacket) { + if packet.ep_number != 2 { + packet.status = UsbPacketStatus::Stall; + return; + } + + match self.state.mode { + UsbMsdMode::Cbw => { + if packet.get_iovecs_size() != CBW_SIZE as usize { + error!("usb-storage: Bad CBW size {}", packet.get_iovecs_size()); + packet.status = UsbPacketStatus::Stall; + return; + } + let mut cbw_buf = [0_u8; CBW_SIZE as usize]; + packet.transfer_packet(&mut cbw_buf, CBW_SIZE as usize); + self.state.cbw.convert(&cbw_buf); + debug!("Storage cbw {:?}", self.state.cbw); + + if self.state.cbw.sig != CBW_SIGNATURE { + error!("usb-storage: Bad signature {:X}", self.state.cbw.sig); + packet.status = UsbPacketStatus::Stall; + return; + } + + if self.state.cbw.data_len == 0 { + self.state.mode = UsbMsdMode::Csw; + } else if self.state.cbw.flags & CBW_FLAG_IN == CBW_FLAG_IN { + self.state.mode = UsbMsdMode::DataIn; + } else { + self.state.mode = UsbMsdMode::DataOut; + } + // TODO: Convert CBW to SCSI CDB. + self.state.csw = UsbMsdCsw::new(); + } + UsbMsdMode::DataOut => { + // TODO: SCSI data out processing. + if packet.get_iovecs_size() < self.state.cbw.data_len as usize { + packet.status = UsbPacketStatus::Stall; + return; + } + + self.state.mode = UsbMsdMode::Csw; + } + _ => { + packet.status = UsbPacketStatus::Stall; + } + } + } + + fn handle_token_in(&mut self, packet: &mut UsbPacket) { + if packet.ep_number != 1 { + packet.status = UsbPacketStatus::Stall; + return; + } + + match self.state.mode { + UsbMsdMode::DataOut => { + if self.state.cbw.data_len != 0 || packet.get_iovecs_size() < CSW_SIZE as usize { + packet.status = UsbPacketStatus::Stall; + return; + } + + packet.status = UsbPacketStatus::Async; + } + UsbMsdMode::Csw => { + if packet.get_iovecs_size() < CSW_SIZE as usize { + packet.status = UsbPacketStatus::Stall; + return; + } + + // TODO: CSW after SCSI data processing. + let mut csw_buf = [0_u8; CSW_SIZE as usize]; + self.state.csw.tag = self.state.cbw.tag; + self.state.csw.convert(&mut csw_buf); + packet.transfer_packet(&mut csw_buf, CSW_SIZE as usize); + self.state.mode = UsbMsdMode::Cbw; + self.state.csw = UsbMsdCsw::new(); + } + UsbMsdMode::DataIn => { + // TODO: SCSI data in processing. + self.state.mode = UsbMsdMode::Csw; + } + _ => { + packet.status = UsbPacketStatus::Stall; + } + } + } } impl UsbDeviceOps for UsbStorage { @@ -307,7 +389,24 @@ impl UsbDeviceOps for UsbStorage { } } - fn handle_data(&mut self, _packet: &mut UsbPacket) {} + fn handle_data(&mut self, packet: &mut UsbPacket) { + debug!( + "Storage device handle_data endpoint {}, mode {:?}", + packet.ep_number, self.state.mode + ); + + match packet.pid as u8 { + USB_TOKEN_OUT => { + self.handle_token_out(packet); + } + USB_TOKEN_IN => { + self.handle_token_in(packet); + } + _ => { + packet.status = UsbPacketStatus::Stall; + } + } + } fn device_id(&self) -> String { self.id.clone() -- Gitee From 5b1cd3da10a769431c4cbe188fdf929ff131abdd Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 11:01:50 +0800 Subject: [PATCH 0963/2187] usb-storage: add cmdline support Support USB storage cmdline parsing/check. Signed-off-by: yezengruan Signed-off-by: liuxiangdong --- devices/src/usb/storage.rs | 40 ++++++++++++++++-- machine/src/lib.rs | 29 +++++++++++-- machine/src/standard_vm/mod.rs | 1 + machine_manager/src/cmdline.rs | 3 +- machine_manager/src/config/drive.rs | 18 +++++++- machine_manager/src/config/usb.rs | 65 ++++++++++++++++++++++++++++- machine_manager/src/machine.rs | 1 + 7 files changed, 147 insertions(+), 10 deletions(-) diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index f67d0989e..be55c60a5 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -10,13 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Arc, Mutex, Weak}; +use std::{ + collections::HashMap, + fs::File, + sync::{Arc, Mutex, Weak}, +}; use anyhow::Result; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info}; use once_cell::sync::Lazy; +use machine_manager::config::{DriveFile, UsbStorageConfig, VmConfig}; + use super::config::*; use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, @@ -156,6 +162,16 @@ pub struct UsbStorage { state: UsbStorageState, /// USB controller used to notify controller to transfer data. cntlr: Option>>, + /// Configuration of the USB storage device. + pub config: UsbStorageConfig, + /// Image file opened. + pub disk_image: Option>, + /// The align requirement of request(offset/len). + pub req_align: u32, + /// The align requirement of buffer(iova_base). + pub buf_align: u32, + /// Drive backend files. + drive_files: Arc>>, } #[derive(Debug)] @@ -223,12 +239,20 @@ impl UsbMsdCsw { } impl UsbStorage { - pub fn new() -> Self { + pub fn new( + config: UsbStorageConfig, + drive_files: Arc>>, + ) -> Self { Self { - id: "".to_string(), + id: config.id.clone().unwrap(), usb_device: UsbDevice::new(), state: UsbStorageState::new(), cntlr: None, + config, + disk_image: None, + req_align: 1, + buf_align: 1, + drive_files, } } @@ -239,6 +263,16 @@ impl UsbStorage { self.usb_device .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; + if !self.config.path_on_host.is_empty() { + let drive_files = self.drive_files.lock().unwrap(); + let file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; + self.disk_image = Some(Arc::new(file)); + + let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; + self.req_align = alignments.0; + self.buf_align = alignments.1; + } + let storage: Arc> = Arc::new(Mutex::new(self)); Ok(storage) } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c59f7db66..1c6fe7ad1 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -50,8 +50,8 @@ use devices::InterruptController; #[cfg(not(target_env = "musl"))] use devices::usb::{ - camera::UsbCamera, keyboard::UsbKeyboard, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, - UsbDeviceOps, + camera::UsbCamera, keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, + xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, }; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ @@ -65,7 +65,8 @@ use machine_manager::config::{ }; #[cfg(not(target_env = "musl"))] use machine_manager::config::{ - parse_gpu, parse_usb_camera, parse_usb_keyboard, parse_usb_tablet, parse_xhci, + parse_gpu, parse_usb_camera, parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, + parse_xhci, }; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; @@ -1232,6 +1233,24 @@ pub trait MachineOps { Ok(()) } + /// Add usb storage. + /// + /// # Arguments + /// + /// * `cfg_args` - USB Storage Configuration. + #[cfg(not(target_env = "musl"))] + fn add_usb_storage(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let device_cfg = parse_usb_storage(vm_config, cfg_args)?; + let storage = UsbStorage::new(device_cfg, self.get_drive_files()); + let stg = storage + .realize() + .with_context(|| "Failed to realize usb storage device")?; + + self.attach_usb_to_xhci_controller(vm_config, stg as Arc>)?; + + Ok(()) + } + /// Add peripheral devices. /// /// # Arguments @@ -1331,6 +1350,10 @@ pub trait MachineOps { self.add_usb_camera(vm_config, cfg_args)?; } #[cfg(not(target_env = "musl"))] + "usb-storage" => { + self.add_usb_storage(vm_config, cfg_args)?; + } + #[cfg(not(target_env = "musl"))] "virtio-gpu-pci" => { self.add_virtio_pci_gpu(cfg_args)?; } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 8161add8b..652a716c8 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1264,6 +1264,7 @@ impl DeviceInterface for StdMachine { } else { AioEngine::Off }, + media: "disk".to_string(), }; if let Err(e) = config.check() { diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 036e3cc94..0fa9ca6d4 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -245,7 +245,8 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { \n\t\tadd vfio pci: -device vfio-pci,id=,host=<0000:1a:00.3>,bus=,addr=<0x03>[,multifunction=on|off]; \ \n\t\tadd usb controller: -device nec-usb-xhci,id=,bus=,addr=<0xa>; \ \n\t\tadd usb keyboard: -device usb-kbd,id=; \ - \n\t\tadd usb tablet-device usb-tablet,id=; \ + \n\t\tadd usb tablet: -device usb-tablet,id=; \ + \n\t\tadd usb storage: -device usb-storage,id=,drive=; \ \n\t\tadd scsi controller: -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction=on|off][,iothread=][,num-queues=]; \ \n\t\tadd scsi hard disk: -device scsi-hd,scsi-id=<0>,bus=,lun=<0>,drive=,id=; \ \n\t\tadd vhost user fs: -device vhost-user-fs-pci,id=,chardev=,tag=") diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 02e867ac1..00fc96c44 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -108,6 +108,7 @@ pub struct DriveConfig { pub direct: bool, pub iops: Option, pub aio: AioEngine, + pub media: String, } impl Default for DriveConfig { @@ -119,6 +120,7 @@ impl Default for DriveConfig { direct: true, iops: None, aio: AioEngine::Native, + media: "disk".to_string(), } } } @@ -196,6 +198,14 @@ impl ConfigCheck for DriveConfig { "low performance expected when use sync io with \"direct\" on".to_string(), ))); } + + if !["disk", "cdrom"].contains(&self.media.as_str()) { + return Err(anyhow!(ConfigError::InvalidParam( + "media".to_string(), + "media should be \"disk\" or \"cdrom\"".to_string(), + ))); + } + Ok(()) } } @@ -299,6 +309,11 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { AioEngine::Off } }); + + drive.media = cmd_parser + .get_value::("media")? + .unwrap_or_else(|| "disk".to_string()); + drive.check()?; #[cfg(not(test))] drive.check_path()?; @@ -505,7 +520,8 @@ impl VmConfig { .push("format") .push("if") .push("throttling.iops-total") - .push("aio"); + .push("aio") + .push("media"); cmd_parser.parse(block_config)?; let drive_cfg = parse_drive(cmd_parser)?; diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 21565c4ca..886022cd0 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -13,11 +13,11 @@ use std::{path::Path, str::FromStr}; use super::error::ConfigError; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use strum::{EnumCount, IntoEnumIterator}; use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; -use crate::config::{check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck}; +use crate::config::{check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, VmConfig}; /// XHCI controller configuration. #[derive(Debug)] @@ -231,3 +231,64 @@ fn check_camera_path(path: Option) -> Result<()> { Ok(()) } + +#[derive(Clone, Debug)] +pub struct UsbStorageConfig { + /// USB Storage device id. + pub id: Option, + /// The image file path. + pub path_on_host: String, + /// USB Storage device can not do write operation. + pub read_only: bool, +} + +impl UsbStorageConfig { + fn new() -> Self { + UsbStorageConfig { + id: None, + path_on_host: "".to_string(), + read_only: false, + } + } +} + +impl Default for UsbStorageConfig { + fn default() -> Self { + Self::new() + } +} + +impl ConfigCheck for UsbStorageConfig { + fn check(&self) -> Result<()> { + check_id(self.id.clone(), "usb-storage") + } +} + +pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("usb-storage"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("port") + .push("drive"); + + cmd_parser.parse(drive_config)?; + + let mut dev = UsbStorageConfig::new(); + dev.id = cmd_parser.get_value::("id")?; + + let storage_drive = cmd_parser.get_value::("drive")?.with_context(|| { + ConfigError::FieldIsMissing("drive".to_string(), "usb storage device".to_string()) + })?; + + let drive_arg = &vm_config + .drives + .remove(&storage_drive) + .with_context(|| "No drive configured matched for usb storage device")?; + dev.path_on_host = drive_arg.path_on_host.clone(); + dev.read_only = drive_arg.read_only; + + dev.check()?; + Ok(dev) +} diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index c765dd899..1ac72b38a 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -308,6 +308,7 @@ pub trait DeviceInterface { ("nec-usb-xhci", "base-xhci"), ("usb-tablet", "usb-hid"), ("usb-kbd", "usb-hid"), + ("usb-storage", "usb-storage-dev"), ("virtio-gpu-pci", "virtio-gpu"), ]; -- Gitee From 655c187756691583b5d111329e0bc5f92fcbd7ff Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Apr 2023 11:54:31 +0800 Subject: [PATCH 0964/2187] doc: Consolidate usb related docs into a small chapter Signed-off-by: yezengruan --- docs/config_guidebook.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 31a3bf605..b13db996a 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -729,7 +729,10 @@ Five properties can be set for chardev. -chardev file,id=,path= ``` -### 2.13 USB controller +### 2.13 USB +StratoVirt supports XHCI USB controller, you can attach USB devices under XHCI USB controller. + +#### 2.13.1 USB controller USB controller is a pci device which can be attached USB device. Three properties can be set for USB controller. @@ -744,7 +747,7 @@ Three properties can be set for USB controller. Note: Only one USB controller can be configured, USB controller can only support USB keyboard and USB tablet. -### 2.14 USB Keyboard +#### 2.13.2 USB Keyboard The USB keyboard is a keyboard that uses the USB protocol. It should be attached to USB controller. Keypad and led are not supported yet. One property can be set for USB Keyboard. @@ -757,7 +760,7 @@ One property can be set for USB Keyboard. Note: Only one keyboard can be configured. -### 2.15 USB Tablet +#### 2.13.3 USB Tablet Pointer Device which uses alsolute coordinates. It should be attached to USB controller. One property can be set for USB Tablet. @@ -770,7 +773,7 @@ One property can be set for USB Tablet. Note: Only one tablet can be configured. -### 2.16 USB Camera +### 2.13.4 USB Camera Video Camera Device that based on USB video class protocol. It should be attached to USB controller. 3 properties can be set for USB Camera. @@ -786,7 +789,7 @@ Video Camera Device that based on USB video class protocol. It should be attache Note: Only one camera can be configured. -### 2.17 Virtio Scsi Controller +### 2.14 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. Six properties can be set for Virtio-Scsi controller. @@ -800,7 +803,7 @@ Six properties can be set for Virtio-Scsi controller. ```shell -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=][,queue-size=] ``` -### 2.18 Virtio Scsi HardDisk +### 2.15 Virtio Scsi HardDisk Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. Note: Only support using raw image file as backend now. @@ -825,7 +828,7 @@ It determines the order of bootable devices which firmware will use for booting -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,serial=123456,bootindex=1] ``` -### 2.19 VNC +### 2.16 VNC VNC can provide the users with way to login virtual machines remotely. In order to use VNC, the ip and port value must be configured. The IP address can be set to a specified value or `0.0.0.0`, which means that all IP addresses on the host network card are monitored @@ -875,10 +878,10 @@ Sample Configuration: Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. -### 2.20 Virtio-fs +### 2.17 Virtio-fs Virtio-fs is a shared file system that lets virtual machines access a directory tree on the host. Unlike existing approaches, it is designed to offer local file system semantics and performance. -#### 2.20.1 virtio fs device +#### 2.17.1 virtio fs device Three properties can be set for virtio fs device. * chardevid: id for char device * device_id: the unique id for device @@ -889,7 +892,7 @@ Three properties can be set for virtio fs device. -device vhost-user-fs-pci,id=,chardev=,tag= ``` -#### 2.20.2 vhost_user_fs +#### 2.17.2 vhost_user_fs The vhost-user filesystem device contains virtio fs device and the vhost-user server which can be connected with the vhost-user client in StratoVirt through socket. Seven properties are supported for vhost_user_fs. @@ -928,7 +931,7 @@ host# stratovirt \ guest# mount -t virtiofs myfs /mnt ``` -### 2.21 virtio-gpu +### 2.18 virtio-gpu virtio-gpu is an virtualized graphics card that lets virtual machines can display with it. Usually used in conjunction with VNC, the final images is rendered to the VNC client. @@ -947,7 +950,7 @@ Note: 1. Only virtio-gpu 2D supported. 2. Live migration is not supported. -### 2.22 ivshmem-scream +### 2.19 ivshmem-scream ivshmem-scream is a virtual sound card that relies on Intel-VM shared memory to transmit audio data. -- Gitee From ec64101d28b738165641c6f272e2af432007bf17 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 30 Mar 2023 17:04:29 +0800 Subject: [PATCH 0965/2187] doc: add usb storage in config guidebook -device usb-storage,drive=,id= -drive id=,file=[,media={disk|cdrom}] Signed-off-by: yezengruan Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index b13db996a..9d3b8d24a 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -789,6 +789,20 @@ Video Camera Device that based on USB video class protocol. It should be attache Note: Only one camera can be configured. +#### 2.13.5 USB Storage +USB storage device that base on classic bulk-only transport protocol. It should be attached to USB controller. + +Three properties can be set for USB Storage. + +* id: unique device id. +* file: the path of backend image file. +* media: the media type of storage. Possible values are `disk` or `cdrom`. If not set, default is `disk`. + +```shell +-device usb-storage,drive=,id= +-drive id=,file=[,media={disk|cdrom}] +``` + ### 2.14 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. -- Gitee From f1eebace4a1406ef0ca6b2db60399447cceb37ef Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 14:12:14 +0800 Subject: [PATCH 0966/2187] test: refactor usb device test init steps Refactored USB device test initialization steps to support more USB devices. Signed-off-by: yezengruan --- tests/mod_test/src/libdriver/usb.rs | 235 +++++++++++++++++++--------- 1 file changed, 162 insertions(+), 73 deletions(-) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index d6465c0e6..79e67ce8a 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -131,6 +131,12 @@ pub const PRIMARY_INTERRUPTER_ID: usize = 0; pub const XHCI_PCI_SLOT_NUM: u8 = 0x5; pub const XHCI_PCI_FUN_NUM: u8 = 0; +enum UsbDeviceType { + Tablet, + Keyboard, + Other, +} + #[derive(Debug, Default, Copy, Clone)] pub struct TestNormalTRB { parameter: u64, @@ -515,6 +521,7 @@ impl TestXhciPciDevice { } pub fn init_device(&mut self, port_id: u32) -> u32 { + let usb_device_type = self.get_usb_device_type(); // reset usb port self.reset_port(port_id); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); @@ -585,7 +592,14 @@ impl TestXhciPciDevice { let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); let buf = self.get_transfer_data_indirect(evt.ptr - 16, 2); - assert_eq!(buf, [2, 0]); + + match usb_device_type { + UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { + assert_eq!(buf, [2, 0]); + } + _ => {} + } + slot_id } @@ -935,12 +949,34 @@ impl TestXhciPciDevice { // Return the address of input context to allow outside modify. pub fn configure_endpoint(&mut self, slot_id: u32, dc: bool) -> u64 { + let usb_device_type = self.get_usb_device_type(); + let mut endpoint_id: Vec = Vec::new(); + let mut endpoint_type: Vec = Vec::new(); + let mut endpoint_offset: Vec = Vec::new(); + + match usb_device_type { + UsbDeviceType::Keyboard | UsbDeviceType::Tablet => { + endpoint_id.push(HID_DEVICE_ENDPOINT_ID); + endpoint_type.push(7); + endpoint_offset.push(0x80); + } + _ => { + endpoint_id.push(3); + endpoint_type.push(2); + endpoint_offset.push(0x60); + } + } + let output_ctx_addr = self.get_device_context_address(slot_id); // Input context. let input_ctx_addr = self.alloc_input_context(); let mut input_ctx = XhciInputCtrlCtx::default(); - input_ctx.add_flags |= 0x1 | 1 << HID_DEVICE_ENDPOINT_ID; - input_ctx.drop_flags = 1 << HID_DEVICE_ENDPOINT_ID; + + for i in 0..endpoint_id.len() { + input_ctx.add_flags |= 0x1 | 1 << endpoint_id[i]; + input_ctx.drop_flags |= 1 << endpoint_id[i]; + } + self.mem_write_u32(input_ctx_addr, input_ctx.as_dwords()); // Slot context. let mut slot_ctx = XhciSlotCtx::default(); @@ -958,15 +994,18 @@ impl TestXhciPciDevice { } else { TRB_SIZE as u64 * TRANSFER_RING_LEN }; - let ep_tr_ring = self.allocator.borrow_mut().alloc(tr_ring_size); - ep_ctx.set_tr_dequeue_pointer(ep_tr_ring | 1); - ep_ctx.set_interval(10); - ep_ctx.set_ep_state(0); - ep_ctx.set_ep_type(7); - self.mem_write_u32(input_ctx_addr + 0x80, ep_ctx.as_dwords()); - self.xhci.device_slot[slot_id as usize].endpoints[(HID_DEVICE_ENDPOINT_ID - 1) as usize] - .transfer_ring - .init(ep_tr_ring, tr_ring_size); + + for i in 0..endpoint_id.len() { + let ep_tr_ring = self.allocator.borrow_mut().alloc(tr_ring_size); + ep_ctx.set_tr_dequeue_pointer(ep_tr_ring | 1); + ep_ctx.set_interval(10); + ep_ctx.set_ep_state(0); + ep_ctx.set_ep_type(endpoint_type[i]); + self.mem_write_u32(input_ctx_addr + endpoint_offset[i], ep_ctx.as_dwords()); + self.xhci.device_slot[slot_id as usize].endpoints[(endpoint_id[i] - 1) as usize] + .transfer_ring + .init(ep_tr_ring, tr_ring_size); + } let mut trb = TestNormalTRB::default(); trb.parameter = input_ctx_addr; @@ -1506,7 +1545,20 @@ impl TestXhciPciDevice { // Descriptor impl TestXhciPciDevice { + fn get_usb_device_type(&mut self) -> UsbDeviceType { + let usb_device_type = if *self.device_config.get("tablet").unwrap_or(&false) { + UsbDeviceType::Tablet + } else if *self.device_config.get("keyboard").unwrap_or(&false) { + UsbDeviceType::Keyboard + } else { + UsbDeviceType::Other + }; + + usb_device_type + } + pub fn get_usb_descriptor(&mut self, slot_id: u32) { + let usb_device_type = self.get_usb_device_type(); // device descriptor self.get_device_descriptor(slot_id); self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); @@ -1517,7 +1569,12 @@ impl TestXhciPciDevice { // descriptor type assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_DEVICE); // bcdUSB - assert_eq!(buf[3..5], [1, 0]); + match usb_device_type { + UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { + assert_eq!(buf[2..4], [0, 1]); + } + _ => {} + } // config descriptor self.get_config_descriptor(slot_id); self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); @@ -1539,28 +1596,48 @@ impl TestXhciPciDevice { ); // descriptor type assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_INTERFACE); - // hid class - assert_eq!(buf[5], 3); - // hid descriptor - offset += USB_DT_INTERFACE_SIZE as u64; - if *self.device_config.get("tablet").unwrap_or(&false) { - let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); - assert_eq!(buf, [0x9, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0]); - } else if *self.device_config.get("keyboard").unwrap_or(&false) { - let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); - assert_eq!(buf, [0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0]); + // USB class + match usb_device_type { + UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { + assert_eq!(buf[5], USB_CLASS_HID); + } + _ => {} } - offset += 9; + + match usb_device_type { + UsbDeviceType::Tablet => { + // hid descriptor + offset += USB_DT_INTERFACE_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); + assert_eq!(buf, [0x9, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0]); + } + UsbDeviceType::Keyboard => { + // hid descriptor + offset += USB_DT_INTERFACE_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); + assert_eq!(buf, [0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0]); + } + _ => {} + } + + offset += USB_DT_INTERFACE_SIZE as u64; // endpoint descriptor let buf = self.get_transfer_data_indirect_with_offset( addr, USB_DT_ENDPOINT_SIZE as usize, offset, ); - // descriptor type - assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); - // endpoint address - assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x1); + + match usb_device_type { + UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); + // endpoint address + assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x1); + } + _ => {} + } + // string descriptor self.get_string_descriptor(slot_id, 0); self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); @@ -1573,57 +1650,69 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - if *self.device_config.get("tablet").unwrap_or(&false) { - let hid_str = "HID Tablet"; - let len = hid_str.len() * 2 + 2; - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); - for i in 0..hid_str.len() { - assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + + match usb_device_type { + UsbDeviceType::Tablet => { + let hid_str = "HID Tablet"; + let len = hid_str.len() * 2 + 2; + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + for i in 0..hid_str.len() { + assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + } } - } else if *self.device_config.get("keyboard").unwrap_or(&false) { - let hid_str = "HID Keyboard"; - let len = hid_str.len() * 2 + 2; - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); - for i in 0..hid_str.len() { - assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + UsbDeviceType::Keyboard => { + let hid_str = "HID Keyboard"; + let len = hid_str.len() * 2 + 2; + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + for i in 0..hid_str.len() { + assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + } } + _ => {} } } pub fn check_hid_report_descriptor(&mut self, slot_id: u32) { - if *self.device_config.get("keyboard").unwrap_or(&false) { - self.get_hid_report_descriptor(slot_id, 63); - self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); - let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 63); - assert_eq!( - buf, - [ - 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, 0x19, - 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, - 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, - 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, - 0x25, 0xff, 0x05, 0x07, 0x19, 0x00, 0x29, 0xff, 0x81, 0x00, 0xc0 - ] - ); - } else if *self.device_config.get("tablet").unwrap_or(&false) { - self.get_hid_report_descriptor(slot_id, 74); - self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); - let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 74); - assert_eq!( - buf, - [ - 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, - 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, - 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, - 0x00, 0x26, 0xff, 0x7f, 0x35, 0x00, 0x46, 0xff, 0x7f, 0x75, 0x10, 0x95, 0x02, - 0x81, 0x02, 0x05, 0x01, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x35, 0x00, 0x45, - 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0xc0, 0xc0, - ] - ); + let usb_device_type = self.get_usb_device_type(); + match usb_device_type { + UsbDeviceType::Keyboard => { + self.get_hid_report_descriptor(slot_id, 63); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 63); + assert_eq!( + buf, + [ + 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x75, 0x01, 0x95, 0x08, 0x05, 0x07, + 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x95, 0x01, + 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, + 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06, + 0x75, 0x08, 0x15, 0x00, 0x25, 0xff, 0x05, 0x07, 0x19, 0x00, 0x29, 0xff, + 0x81, 0x00, 0xc0 + ] + ); + } + UsbDeviceType::Tablet => { + self.get_hid_report_descriptor(slot_id, 74); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 74); + assert_eq!( + buf, + [ + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, + 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, + 0x09, 0x31, 0x15, 0x00, 0x26, 0xff, 0x7f, 0x35, 0x00, 0x46, 0xff, 0x7f, + 0x75, 0x10, 0x95, 0x02, 0x81, 0x02, 0x05, 0x01, 0x09, 0x38, 0x15, 0x81, + 0x25, 0x7f, 0x35, 0x00, 0x45, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, + 0xc0, 0xc0, + ] + ); + } + _ => {} } } -- Gitee From b18d8b8a1b48351773fee760af50795291bfd6d4 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 31 Mar 2023 14:32:14 +0800 Subject: [PATCH 0967/2187] mod-test: Add usb-storage MST testcases Signed-off-by: yezengruan --- tests/mod_test/src/libdriver/usb.rs | 75 +++ tests/mod_test/tests/usb_storage_test.rs | 816 +++++++++++++++++++++++ 2 files changed, 891 insertions(+) create mode 100644 tests/mod_test/tests/usb_storage_test.rs diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 79e67ce8a..86de9d002 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -126,6 +126,8 @@ pub const TRANSFER_RING_LEN: u64 = 256; pub const TD_TRB_LIMIT: u64 = 0x20000 + 10; // The USB keyboard and tablet intr endpoint id. pub const HID_DEVICE_ENDPOINT_ID: u32 = 3; +pub const STORAGE_DEVICE_IN_ENDPOINT_ID: u32 = 3; +pub const STORAGE_DEVICE_OUT_ENDPOINT_ID: u32 = 4; // Primary Interrupter pub const PRIMARY_INTERRUPTER_ID: usize = 0; pub const XHCI_PCI_SLOT_NUM: u8 = 0x5; @@ -134,6 +136,7 @@ pub const XHCI_PCI_FUN_NUM: u8 = 0; enum UsbDeviceType { Tablet, Keyboard, + Storage, Other, } @@ -597,6 +600,9 @@ impl TestXhciPciDevice { UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { assert_eq!(buf, [2, 0]); } + UsbDeviceType::Storage => { + assert_eq!(buf, [3, 0]); + } _ => {} } @@ -960,6 +966,14 @@ impl TestXhciPciDevice { endpoint_type.push(7); endpoint_offset.push(0x80); } + UsbDeviceType::Storage => { + endpoint_id.push(STORAGE_DEVICE_IN_ENDPOINT_ID); + endpoint_type.push(6); + endpoint_offset.push(0x80); + endpoint_id.push(STORAGE_DEVICE_OUT_ENDPOINT_ID); + endpoint_type.push(2); + endpoint_offset.push(0xa0); + } _ => { endpoint_id.push(3); endpoint_type.push(2); @@ -1550,6 +1564,8 @@ impl TestXhciPciDevice { UsbDeviceType::Tablet } else if *self.device_config.get("keyboard").unwrap_or(&false) { UsbDeviceType::Keyboard + } else if *self.device_config.get("storage").unwrap_or(&false) { + UsbDeviceType::Storage } else { UsbDeviceType::Other }; @@ -1573,6 +1589,9 @@ impl TestXhciPciDevice { UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { assert_eq!(buf[2..4], [0, 1]); } + UsbDeviceType::Storage => { + assert_eq!(buf[2..4], [0, 2]); + } _ => {} } // config descriptor @@ -1601,6 +1620,11 @@ impl TestXhciPciDevice { UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { assert_eq!(buf[5], USB_CLASS_HID); } + UsbDeviceType::Storage => { + assert_eq!(buf[5], USB_CLASS_MASS_STORAGE); + assert_eq!(buf[6], 0x06); + assert_eq!(buf[7], 0x50); + } _ => {} } @@ -1635,6 +1659,23 @@ impl TestXhciPciDevice { // endpoint address assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x1); } + UsbDeviceType::Storage => { + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); + // endpoint address + assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x01); + offset += USB_DT_ENDPOINT_SIZE as u64; + // endpoint descriptor + let buf = self.get_transfer_data_indirect_with_offset( + addr, + USB_DT_ENDPOINT_SIZE as usize, + offset, + ); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); + // endpoint address + assert_eq!(buf[2], USB_DIRECTION_HOST_TO_DEVICE | 0x02); + } _ => {} } @@ -1668,6 +1709,18 @@ impl TestXhciPciDevice { assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); } } + UsbDeviceType::Storage => { + self.get_string_descriptor(slot_id, 2); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let hid_str = "StratoVirt USB Storage"; + let len = hid_str.len() * 2 + 2; + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + for i in 0..hid_str.len() { + assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); + } + } _ => {} } } @@ -2000,6 +2053,10 @@ impl TestUsbBuilder { let machine: Vec<&str> = "-machine virt".split(' ').collect(); let mut arg = machine.into_iter().map(|s| s.to_string()).collect(); args.append(&mut arg); + + let machine: Vec<&str> = "-D /var/log/mst.log".split(' ').collect(); + let mut arg = machine.into_iter().map(|s| s.to_string()).collect(); + args.append(&mut arg); Self { args, config: HashMap::new(), @@ -2045,6 +2102,24 @@ impl TestUsbBuilder { self } + pub fn with_usb_storage(mut self, image_path: &str) -> Self { + let args = format!("-device usb-storage,drive=drive0,id=storage0"); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + + let args = format!( + "-drive if=none,id=drive0,format=raw,media=cdrom,aio=off,direct=false,file={}", + image_path + ); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + + self.config.insert(String::from("storage"), true); + self + } + pub fn with_config(mut self, k: &str, v: bool) -> Self { self.config.insert(k.to_string(), v); self diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs new file mode 100644 index 000000000..45f5a5596 --- /dev/null +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -0,0 +1,816 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cell::RefMut; + +use byteorder::{ByteOrder, LittleEndian}; + +use devices::usb::{ + config::{USB_INTERFACE_CLASS_IN_REQUEST, USB_INTERFACE_CLASS_OUT_REQUEST}, + storage::{ + CBW_FLAG_IN, CBW_FLAG_OUT, CBW_SIGNATURE, CBW_SIZE, CSW_SIGNATURE, CSW_SIZE, GET_MAX_LUN, + MASS_STORAGE_RESET, + }, + xhci::{TRBCCode, TRB_SIZE}, + UsbDeviceRequest, +}; + +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::usb::{ + TestIovec, TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, + STORAGE_DEVICE_IN_ENDPOINT_ID, STORAGE_DEVICE_OUT_ENDPOINT_ID, +}; +use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; + +const READ_10: u8 = 0x28; +const WRITE_10: u8 = 0x2a; + +const CBW_ILLEGAL_SIZE: u8 = CBW_SIZE - 1; +const CSW_ILLEGAL_SIZE: u8 = CSW_SIZE - 1; + +struct Cbw { + sig: u32, + tag: u32, + data_len: u32, + flags: u8, + lun: u8, + cmd_len: u8, + cmd: [u8; 16], +} + +impl Cbw { + fn new() -> Self { + Cbw { + sig: CBW_SIGNATURE, + tag: 123456, + data_len: 0, + flags: 0, + lun: 0, + cmd_len: 0, + cmd: [0; 16], + } + } +} + +fn cbw_phase( + cbw: Cbw, + mut xhci: RefMut, + mut guest_allocator: RefMut, + slot_id: u32, + status: TRBCCode, + len: u8, +) { + let mut cbw_buf: [u8; CBW_SIZE as usize] = [0; CBW_SIZE as usize]; + LittleEndian::write_u32(&mut cbw_buf[0..4], cbw.sig); + LittleEndian::write_u32(&mut cbw_buf[4..8], cbw.tag); + LittleEndian::write_u32(&mut cbw_buf[8..12], cbw.data_len); + cbw_buf[12] = cbw.flags; + cbw_buf[13] = cbw.lun; + cbw_buf[14] = cbw.cmd_len; + for i in 0..16 { + cbw_buf[15 + i] = cbw.cmd[i]; + } + + let mut iovecs = Vec::new(); + let ptr = guest_allocator.alloc(CBW_SIZE as u64); + xhci.mem_write(ptr, &cbw_buf); + + let iovec = TestIovec::new(ptr, len as usize, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, STORAGE_DEVICE_OUT_ENDPOINT_ID, &mut iovecs, false); + xhci.doorbell_write(slot_id, STORAGE_DEVICE_OUT_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, status as u32); +} + +fn data_phase( + mut xhci: RefMut, + mut guest_allocator: RefMut, + slot_id: u32, + buf: &[u8], + to_host: bool, +) { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.alloc(buf.len() as u64); + let iovec = TestIovec::new(ptr, buf.len() as usize, false); + + if !to_host { + xhci.mem_write(ptr, &buf); + } + + iovecs.push(iovec); + + if to_host { + xhci.queue_td_by_iovec(slot_id, STORAGE_DEVICE_IN_ENDPOINT_ID, &mut iovecs, false); + xhci.doorbell_write(slot_id, STORAGE_DEVICE_IN_ENDPOINT_ID); + } else { + xhci.queue_td_by_iovec(slot_id, STORAGE_DEVICE_OUT_ENDPOINT_ID, &mut iovecs, false); + xhci.doorbell_write(slot_id, STORAGE_DEVICE_OUT_ENDPOINT_ID); + } + + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + if to_host { + let data_buf = xhci.mem_read(ptr, buf.len()); + assert_eq!(buf, data_buf); + } +} + +fn csw_phase( + mut xhci: RefMut, + mut guest_allocator: RefMut, + slot_id: u32, + status: TRBCCode, + len: u8, + sig_check: bool, +) -> u64 { + let mut iovecs = Vec::new(); + let ptr = guest_allocator.alloc(len as u64); + + let iovec = TestIovec::new(ptr, len as usize, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, STORAGE_DEVICE_IN_ENDPOINT_ID, &mut iovecs, false); + xhci.doorbell_write(slot_id, STORAGE_DEVICE_IN_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, status as u32); + + if sig_check { + let buf = xhci.mem_read(ptr, len as usize); + assert_eq!(CSW_SIGNATURE, LittleEndian::read_u32(&buf[0..4])); + } + + ptr +} + +/// USB storage device basic IO function test. +/// TestStep: +/// 0. Init process. +/// 1. CBW: write. +/// 2. DataOut: data write from host to device. +/// 3. CSW. +/// 4. CBW: read. +/// 5. DataIn: data read from device to host. +/// 6. Csw. +/// 7. Test ends. Destroy device. +/// Expect: +/// 0/1/2/3/4/5/6/7: success. +#[test] +fn usb_storage_basic() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 4; + cbw.flags = CBW_FLAG_OUT; + cbw.cmd_len = 10; + cbw.cmd[0] = WRITE_10; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: DataOut phase. + let buf = "TEST".as_bytes(); + data_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + buf, + false, + ); + + // Test 3: CSW phase. + csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CSW_SIZE, + true, + ); + + // Test 4: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 4; + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = READ_10; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 5: Datain phase. + let buf = "TEST".as_bytes(); + data_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + buf, + true, + ); + + // Test 6: CSW phase. + csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CSW_SIZE, + true, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device functional 'Reset' test. +/// TestStep: +/// 0. Init process. +/// 1. Reset. +/// 2. Test ends. Destroy device. +/// Expect: +/// 0/1/2: success. +#[test] +fn usb_storage_functional_reset() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let mut xhci = xhci.borrow_mut(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_OUT_REQUEST, + request: MASS_STORAGE_RESET, + value: 0, + index: 0, + length: 0, + }; + + xhci.queue_device_request(slot_id, &device_req); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device functional 'Get Max Lun' test. +/// TestStep: +/// 0. Init process. +/// 1. Get Max Lun. +/// 2. Test ends. Destroy device. +/// Expect: +/// 0/1/2: success. +#[test] +fn usb_storage_functional_get_max_lun() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let mut xhci = xhci.borrow_mut(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_IN_REQUEST, + request: GET_MAX_LUN, + value: 0, + index: 0, + length: 1, + }; + + xhci.queue_device_request(slot_id, &device_req); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 1); + + assert_eq!(buf, [0]); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device illegal request test. +/// TestStep: +/// 0. Init process. +/// 1. Illegal request. +/// 2. Test ends. Destroy device. +/// Expect: +/// 0/2: success. +/// 1: StallError. +#[test] +fn usb_storage_illegal_request() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let mut xhci = xhci.borrow_mut(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + let device_req = UsbDeviceRequest { + request_type: 2, + request: 210, + value: 0, + index: 0, + length: 0, + }; + + xhci.queue_device_request(slot_id, &device_req); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device CBW signature test. +/// TestStep: +/// 0. Init process. +/// 1. CBW: the signature value is abnormal. +/// 2. Test ends. Destroy device. +/// Expect: +/// 0/2: success. +/// 1: CBW StallError. +#[test] +fn usb_storage_cbw_signature() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.sig = 0x123456; + + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CBW_SIZE, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device CBW illegal packet size test. +/// TestStep: +/// 0. Init process. +/// 1. CBW: the packet size is abnormal. +/// 2. Test ends. Destroy device. +/// Expect: +/// 0/2: success. +/// 1: CBW StallError. +#[test] +fn usb_storage_cbw_illegal_size() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let cbw = Cbw::new(); + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CBW_ILLEGAL_SIZE, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device CSW illegal packet size test. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CSW: the packet size is abnormal. +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/3: success. +/// 2: CSW StallError. +#[test] +fn usb_storage_csw_illegal_size() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let cbw = Cbw::new(); + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CSW phase. + csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CSW_ILLEGAL_SIZE, + false, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device abnormal phase (CBW -> CSW) test, skip Data phase. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CSW. +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/3: success. +/// 2: CSW signature verification failed. +#[test] +fn usb_storage_abnormal_phase_01() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 512; + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = READ_10; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CSW phase. + let ptr = csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CSW_SIZE, + false, + ); + + let buf = xhci.borrow_mut().mem_read(ptr, CSW_SIZE as usize); + assert_ne!(CSW_SIGNATURE, LittleEndian::read_u32(&buf[0..4])); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device abnormal phase (CBW -> CSW -> CSW) test. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CSW. +/// 3. CSW. +/// 4. Test ends. Destroy device. +/// Expect: +/// 0/1/2/4: success. +/// 3: CSW StallError. +#[test] +fn usb_storage_abnormal_phase_02() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let cbw = Cbw::new(); + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CSW phase. + csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CSW_SIZE, + true, + ); + + // Test 3: CSW phase. + csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CSW_SIZE, + false, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device abnormal phase (CBW -> CBW) test. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CBW. +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/3: success. +/// 2: CBW StallError. +#[test] +fn usb_storage_abnormal_phase_03() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 512; + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = READ_10; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 512; + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = READ_10; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CBW_SIZE, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device illegal scsi cdb test. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CSW. +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/3: success. +/// 2: CSW StallError. +#[test] +fn usb_storage_illegal_scsi_cdb() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 512; + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = 0xff; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CSW phase. + csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CSW_SIZE, + false, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device CBW phase to invalid endpoint test. +/// TestStep: +/// 0. Init process. +/// 1. CBW: invalid endpoint(not Out endpoint). +/// 2. Test ends. Destroy device. +/// Expect: +/// 0/2: success. +/// 1: CBW StallError. +#[test] +fn usb_storage_cbw_invalid_endpoint() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + // Test 1: CBW phase. + let cbw = Cbw::new(); + + let mut cbw_buf: [u8; CBW_SIZE as usize] = [0; CBW_SIZE as usize]; + LittleEndian::write_u32(&mut cbw_buf[0..4], cbw.sig); + + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(CBW_SIZE as u64); + xhci.mem_write(ptr, &cbw_buf); + + let iovec = TestIovec::new(ptr, CBW_SIZE as usize, false); + iovecs.push(iovec); + xhci.queue_td_by_iovec(slot_id, STORAGE_DEVICE_IN_ENDPOINT_ID, &mut iovecs, false); + xhci.doorbell_write(slot_id, STORAGE_DEVICE_IN_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device CSW phase to invalid endpoint test. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CSW: invalid endpoint(not In endpoint). +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/3: success. +/// 2: CSW StallError. +#[test] +fn usb_storage_csw_invalid_endpoint() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let cbw = Cbw::new(); + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CSW phase. + let mut iovecs = Vec::new(); + let ptr = guest_allocator.borrow_mut().alloc(CSW_SIZE as u64); + + let iovec = TestIovec::new(ptr, CSW_SIZE as usize, false); + iovecs.push(iovec); + xhci.borrow_mut().queue_td_by_iovec( + slot_id, + STORAGE_DEVICE_OUT_ENDPOINT_ID, + &mut iovecs, + false, + ); + xhci.borrow_mut() + .doorbell_write(slot_id, STORAGE_DEVICE_OUT_ENDPOINT_ID); + let evt = xhci + .borrow_mut() + .fetch_event(PRIMARY_INTERRUPTER_ID) + .unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} -- Gitee From 4673a9101f3e46a9833099e3b4ab318e0b8589fc Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sat, 8 Apr 2023 11:15:59 +0800 Subject: [PATCH 0968/2187] pci: config space: adjust bridge device's rom address The ROM address is configured at different offset in PCI config space, 0x30 for endpoint, and 0x38 for bridge. write() didn't treat them respectively. Since ROM bar is not used in StratoVirt now, this has no effect yet. Meanwhile, fix the blurry naming of the const ROM_ADDRESS1. Signed-off-by: Zhang Bo --- pci/src/config.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index f8e248f5a..12fb5602c 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -62,8 +62,8 @@ pub const MEMORY_BASE: u8 = 0x20; pub const PREF_MEMORY_BASE: u8 = 0x24; /// Prefetchable memory limit register. pub const PREF_MEMORY_LIMIT: u8 = 0x26; -pub const ROM_ADDRESS: usize = 0x30; -pub const ROM_ADDRESS1: usize = 0x38; +pub const ROM_ADDRESS_ENDPOINT: usize = 0x30; +pub const ROM_ADDRESS_BRIDGE: usize = 0x38; /// 64-bit prefetchable memory addresses. pub const PREF_MEM_RANGE_64BIT: u8 = 0x01; @@ -564,10 +564,11 @@ impl PciConfig { offset += 1; } - let mut bar_num = BAR_NUM_MAX_FOR_ENDPOINT; - if self.config[HEADER_TYPE as usize] == HEADER_TYPE_BRIDGE { - bar_num = BAR_NUM_MAX_FOR_BRIDGE; - } + let (bar_num, rom_addr) = match self.config[HEADER_TYPE as usize] & HEADER_TYPE_BRIDGE { + HEADER_TYPE_BRIDGE => (BAR_NUM_MAX_FOR_BRIDGE, ROM_ADDRESS_BRIDGE), + _ => (BAR_NUM_MAX_FOR_ENDPOINT, ROM_ADDRESS_ENDPOINT), + }; + if ranges_overlap(old_offset, end, COMMAND as usize, (COMMAND + 1) as usize) || ranges_overlap( old_offset, @@ -575,7 +576,7 @@ impl PciConfig { BAR_0 as usize, BAR_0 as usize + REG_SIZE * bar_num as usize, ) - || ranges_overlap(old_offset, end, ROM_ADDRESS, ROM_ADDRESS + 4) + || ranges_overlap(old_offset, end, rom_addr, rom_addr + 4) { if let Err(e) = self.update_bar_mapping( #[cfg(target_arch = "x86_64")] -- Gitee From d7222a10dbce4a35321ba74adeb4469afcd4a071 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 4 Apr 2023 16:54:17 +0800 Subject: [PATCH 0969/2187] virtio-blk: support feature VIRTIO_BLK_F_DISCARD The feature VIRTIO_BLK_F_DISCARD can discard disk space which is not in use by the filesystem. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 7 +- machine/src/micro_vm/mod.rs | 1 + machine/src/micro_vm/syscall.rs | 9 +- machine/src/standard_vm/aarch64/syscall.rs | 5 +- machine/src/standard_vm/mod.rs | 47 +++++--- machine/src/standard_vm/x86_64/syscall.rs | 5 +- machine_manager/src/config/drive.rs | 33 +++++- machine_manager/src/config/mod.rs | 4 +- machine_manager/src/machine.rs | 20 +++- machine_manager/src/qmp/qmp_schema.rs | 6 +- util/src/aio/mod.rs | 10 ++ util/src/aio/raw.rs | 31 +++++- virtio/src/device/block.rs | 121 +++++++++++++++++++-- virtio/src/lib.rs | 4 + 14 files changed, 250 insertions(+), 53 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 9d3b8d24a..5eff7710a 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -275,7 +275,7 @@ There is only one argument for iothread: Virtio block device is a virtual block device, which process read and write requests in virtio queue from guest. -twelve properties are supported for virtio block device. +thirteen properties are supported for virtio block device. * id: unique device-id in StratoVirt. * file: the path of backend file on host. @@ -284,6 +284,7 @@ twelve properties are supported for virtio block device. * direct: open block device with `O_DIRECT` mode. (optional) If not set, default is true. * iothread: indicate which iothread will be used. (optional) if not set, the main thread will be used. * throttling.iops-total: used to limit IO operations for block device. (optional) +* discard: free up unused disk space. (optional) `unmap/ignore` means `on/off`. If not set, default is `ignore`. * if: drive type, for block drive, it should be `none`. (optional) If not set, default is `none`. * format: the format of block image. (optional) If not set, default is `raw`. NB: currently only `raw` is supported. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. (optional) The max queues number supported is 32. If not set, the default block queue number is the smaller one of vCPU count and the max queues number (e.g, min(vcpu_count, 32)). @@ -304,10 +305,10 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo ```shell # virtio mmio block device. --drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=] +-drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=][,discard={unmap|ignore}] -device virtio-blk-device,drive=,id=[,iothread=][,serial=] # virtio pci block device. --drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=] +-drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=][,discard={unmap|ignore}] -device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,serial=][,num-queues=][,bootindex=][,queue-size=] ``` diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 285f6052e..c36f20197 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1175,6 +1175,7 @@ impl DeviceInterface for LightMachine { AioEngine::Off }, queue_size: DEFAULT_VIRTQUEUE_SIZE, + discard: false, }; if let Err(e) = config.check() { error!("{:?}", e); diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 89ce5c29b..edafa3b53 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -48,10 +48,10 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 48 syscalls -/// * x86_64-unknown-musl: 47 syscalls -/// * aarch64-unknown-gnu: 46 syscalls -/// * aarch64-unknown-musl: 46 syscalls +/// * x86_64-unknown-gnu: 49 syscalls +/// * x86_64-unknown-musl: 48 syscalls +/// * aarch64-unknown-gnu: 47 syscalls +/// * aarch64-unknown-musl: 47 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -128,6 +128,7 @@ pub fn syscall_whitelist() -> Vec { #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_getrandom), + BpfRule::new(libc::SYS_fallocate), madvise_rule(), ] } diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index a6876c743..c3ec2252f 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 86 syscalls -/// * aarch64-unknown-musl: 59 syscalls +/// * aarch64-unknown-gnu: 87 syscalls +/// * aarch64-unknown-musl: 60 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -183,6 +183,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_ftruncate), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_futex), + BpfRule::new(libc::SYS_fallocate), ] } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 82991be9b..03c8262f5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -59,8 +59,8 @@ use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, - DriveConfig, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, - DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, + DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, + VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -769,6 +769,7 @@ impl StdMachine { socket_path: None, aio: conf.aio, queue_size, + discard: conf.discard, }; dev.check()?; dev @@ -1247,27 +1248,35 @@ impl DeviceInterface for StdMachine { } fn blockdev_add(&self, args: Box) -> Response { - let read_only = args.read_only.unwrap_or(false); - let direct = if let Some(cache) = args.cache { - cache.direct.unwrap_or(true) - } else { - true - }; - let config = DriveConfig { + let mut config = DriveConfig { id: args.node_name, path_on_host: args.file.filename.clone(), - read_only, - direct, + read_only: args.read_only.unwrap_or(false), + direct: true, iops: args.iops, // TODO Add aio option by qmp, now we set it based on "direct". - aio: if direct { - AioEngine::Native - } else { - AioEngine::Off - }, + aio: AioEngine::Native, media: "disk".to_string(), + discard: false, }; - + if args.cache.is_some() && !args.cache.unwrap().direct.unwrap_or(true) { + config.direct = false; + config.aio = AioEngine::Off; + } + if let Some(discard) = args.discard { + let ret = discard.as_str().parse::(); + if ret.is_err() { + let err_msg = format!( + "Invalid discard argument '{}', expected 'unwrap' or 'ignore'", + discard + ); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(err_msg), + None, + ); + } + config.discard = ret.unwrap().into(); + } if let Err(e) = config.check() { error!("{:?}", e); return Response::create_error_response( @@ -1284,7 +1293,9 @@ impl DeviceInterface for StdMachine { ); } // Register drive backend file for hotplug drive. - if let Err(e) = self.register_drive_file(&args.file.filename, read_only, direct) { + if let Err(e) = + self.register_drive_file(&args.file.filename, config.read_only, config.direct) + { error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index e37944317..9116aef49 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,8 +56,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 87 syscalls -/// * x86_64-unknown-musl: 62 syscalls +/// * x86_64-unknown-gnu: 88 syscalls +/// * x86_64-unknown-musl: 63 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -191,6 +191,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_ftruncate), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_futex), + BpfRule::new(libc::SYS_fallocate), ] } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 00fc96c44..2f3ac3523 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -68,6 +68,7 @@ pub struct BlkDevConfig { pub socket_path: Option, pub aio: AioEngine, pub queue_size: u16, + pub discard: bool, } #[derive(Debug, Clone)] @@ -93,6 +94,7 @@ impl Default for BlkDevConfig { socket_path: None, aio: AioEngine::Native, queue_size: DEFAULT_VIRTQUEUE_SIZE, + discard: false, } } } @@ -109,6 +111,7 @@ pub struct DriveConfig { pub iops: Option, pub aio: AioEngine, pub media: String, + pub discard: bool, } impl Default for DriveConfig { @@ -121,6 +124,7 @@ impl Default for DriveConfig { iops: None, aio: AioEngine::Native, media: "disk".to_string(), + discard: false, } } } @@ -314,6 +318,10 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { .get_value::("media")? .unwrap_or_else(|| "disk".to_string()); + if let Some(discard) = cmd_parser.get_value::("discard")? { + drive.discard = discard.into(); + } + drive.check()?; #[cfg(not(test))] drive.check_path()?; @@ -387,6 +395,7 @@ pub fn parse_blk( blkdevcfg.direct = drive_arg.direct; blkdevcfg.iops = drive_arg.iops; blkdevcfg.aio = drive_arg.aio; + blkdevcfg.discard = drive_arg.discard; } else { bail!("No drive configured matched for blk device"); } @@ -521,7 +530,8 @@ impl VmConfig { .push("if") .push("throttling.iops-total") .push("aio") - .push("media"); + .push("media") + .push("discard"); cmd_parser.parse(block_config)?; let drive_cfg = parse_drive(cmd_parser)?; @@ -868,4 +878,25 @@ mod tests { assert!(vm_config.drives.get(*id).is_none()); } } + + #[test] + fn test_drive_config_discard() { + let mut vm_config = VmConfig::default(); + let drive_conf = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,discard=ignore") + .unwrap(); + assert_eq!(drive_conf.discard, false); + + let mut vm_config = VmConfig::default(); + let drive_conf = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,discard=unmap") + .unwrap(); + assert_eq!(drive_conf.discard, true); + + let mut vm_config = VmConfig::default(); + let ret = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,discard=invalid") + .is_err(); + assert_eq!(ret, true); + } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index c6dcfdf7e..d4c755ff5 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -551,8 +551,8 @@ impl FromStr for ExBool { fn from_str(s: &str) -> std::result::Result { match s { - "true" | "on" | "yes" => Ok(ExBool { inner: true }), - "false" | "off" | "no" => Ok(ExBool { inner: false }), + "true" | "on" | "yes" | "unmap" => Ok(ExBool { inner: true }), + "false" | "off" | "no" | "ignore" => Ok(ExBool { inner: false }), _ => Err(()), } } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 1ac72b38a..fc709b2d1 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -18,10 +18,10 @@ use strum::VariantNames; use crate::config::ShutdownAction; use crate::qmp::qmp_schema::{ - BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, DeviceAddArgument, - DeviceProps, Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, MachineInfo, - MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, Target, - TypeLists, UpdateRegionArgument, + BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, CmdParameter, + DeviceAddArgument, DeviceProps, Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, + MachineInfo, MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, + QmpEvent, Target, TypeLists, UpdateRegionArgument, }; use crate::qmp::{Response, Version}; @@ -354,7 +354,17 @@ pub trait DeviceInterface { } fn query_command_line_options(&self) -> Response { - let cmd_lines = Vec::::new(); + let mut cmd_lines = Vec::::new(); + let parameters = vec![CmdParameter { + name: "discard".to_string(), + help: "discard operation (ignore/off, unmap/on)".to_string(), + paramter_type: "string".to_string(), + }]; + let cmd_line = CmdLine { + parameters, + option: "drive".to_string(), + }; + cmd_lines.push(cmd_line); Response::create_response(serde_json::to_value(cmd_lines).unwrap(), None) } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index a3a436474..af11fdfd6 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1831,10 +1831,10 @@ pub struct query_command_line_options {} #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct CmdParameter { - name: String, - help: String, + pub name: String, + pub help: String, #[serde(rename = "type")] - paramter_type: String, + pub paramter_type: String, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 307b9e343..760ae376f 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -91,6 +91,7 @@ pub enum OpCode { Preadv = 1, Pwritev = 2, Fdsync = 3, + Discard = 4, } pub struct AioCb { @@ -201,6 +202,7 @@ impl Aio { self.flush_sync(cb) } } + OpCode::Discard => self.discard_sync(cb), OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), } } @@ -488,6 +490,14 @@ impl Aio { } (self.complete_func)(&cb, ret) } + + fn discard_sync(&mut self, cb: AioCb) -> Result<()> { + let ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); + if ret < 0 { + error!("Failed to do sync discard."); + } + (self.complete_func)(&cb, ret) + } } pub fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index c62a62670..c002e2498 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -11,7 +11,10 @@ // See the Mulan PSL v2 for more details. use super::Iovec; -use libc::{c_int, c_void, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t}; +use libc::{ + c_int, c_void, fallocate, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t, + FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE, +}; use log::error; use std::os::unix::io::RawFd; @@ -131,3 +134,29 @@ pub fn raw_datasync(fd: RawFd) -> i64 { } ret } + +pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { + let mut ret; + loop { + // SAFETY: fd is valid. + ret = unsafe { + fallocate( + fd as c_int, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + offset as i64, + size as i64, + ) as i64 + }; + if ret == 0 || errno::errno().0 != libc::EINTR { + break; + } + } + if ret < 0 { + error!( + "Failed to fallocate for {}, errno {}.", + fd, + errno::errno().0, + ); + } + ret +} diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 5baa3a67d..13f45f208 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -26,10 +26,11 @@ use crate::VirtioError; use crate::{ iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, - VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, + VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, + VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; @@ -42,7 +43,9 @@ use migration::{ StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::aio::{iov_from_buf_direct, raw_datasync, Aio, AioCb, AioEngine, Iovec, OpCode}; +use util::aio::{ + iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, AioEngine, Iovec, OpCode, +}; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ @@ -67,6 +70,8 @@ const MAX_NUM_MERGE_IOVS: usize = 1024; const MAX_NUM_MERGE_BYTES: u64 = i32::MAX as u64; /// Max time for every round of process queue. const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; +/// Max number sectors of per request. +const MAX_REQUEST_SECTORS: u32 = u32::MAX >> SECTOR_SHIFT; type SenderConfig = ( Option>, @@ -97,6 +102,20 @@ struct RequestOutHeader { impl ByteCode for RequestOutHeader {} +/// The request of discard and write-zeroes use the same struct. +#[repr(C)] +#[derive(Default, Clone, Copy)] +struct DiscardWriteZeroesSeg { + /// The start sector for discard or write-zeroes. + sector: u64, + /// The number of sectors for discard or write-zeroes. + num_sectors: u32, + /// The flags used for this range. + flags: u32, +} + +impl ByteCode for DiscardWriteZeroesSeg {} + #[derive(Clone)] pub struct AioCompleteCb { queue: Arc>, @@ -230,9 +249,9 @@ impl Request { } match out_header.request_type { - VIRTIO_BLK_T_IN | VIRTIO_BLK_T_GET_ID | VIRTIO_BLK_T_OUT => { + VIRTIO_BLK_T_IN | VIRTIO_BLK_T_GET_ID | VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_DISCARD => { let data_iovec = match out_header.request_type { - VIRTIO_BLK_T_OUT => { + VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_DISCARD => { iov_discard_front(&mut elem.out_iovec, size_of::() as u64) } // Otherwise discard the last "status" byte. @@ -330,12 +349,70 @@ impl Request { ); aiocb.iocompletecb.complete_request(status)?; } + VIRTIO_BLK_T_DISCARD => { + self.handle_discard_request(iohandler, aiocb)?; + } // The illegal request type has been handled in method new(). _ => {} }; Ok(()) } + fn handle_discard_request( + &self, + iohandler: &mut BlockIoHandler, + mut aiocb: AioCb, + ) -> Result<()> { + if !iohandler.discard { + error!("Device does not support discard"); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + } + + let size = size_of::() as u64; + // Just support one segment per request. + if self.data_len > size { + error!("More than one discard or write-zeroes segment is not supported"); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + } + + // Get and check the discard segment. + let mut segment = DiscardWriteZeroesSeg::default(); + iov_to_buf_direct(&self.iovec, segment.as_mut_bytes()).and_then(|v| { + if v as u64 == size { + Ok(()) + } else { + Err(anyhow!("Invalid discard segment size {}", v)) + } + })?; + let sector = LittleEndian::read_u64(segment.sector.as_bytes()); + let num_sectors = LittleEndian::read_u32(segment.num_sectors.as_bytes()); + if sector + .checked_add(num_sectors as u64) + .filter(|&off| off <= iohandler.disk_sectors) + .is_none() + || num_sectors > MAX_REQUEST_SECTORS + { + error!( + "Invalid discard or write zeroes request, sector offset {}, num_sectors {}", + sector, num_sectors + ); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_IOERR); + } + let flags = LittleEndian::read_u32(segment.flags.as_bytes()); + if flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP != 0 { + error!("Discard request must not set unmap flags"); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + } + + aiocb.opcode = OpCode::Discard; + aiocb.offset = (sector as usize) << SECTOR_SHIFT; + aiocb.nbytes = (num_sectors as u64) << SECTOR_SHIFT; + iohandler + .aio + .submit_request(aiocb) + .with_context(|| "Failed to process block request for discard") + } + fn io_range_valid(&self, disk_sectors: u64) -> bool { match self.out_header.request_type { VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT => { @@ -402,6 +479,8 @@ struct BlockIoHandler { iothread: Option, /// Using the leak bucket to implement IO limits leak_bucket: Option, + /// Supporting discard or not. + discard: bool, } impl BlockIoHandler { @@ -956,6 +1035,24 @@ impl Block { self.state.config_space.capacity = num_sectors; // seg_max = queue_size - 2: 32bits self.state.config_space.seg_max = self.queue_size() as u32 - 2; + + if self.blk_cfg.discard { + self.state.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; + // Just support one segment per request. + self.state.config_space.max_discard_seg = 1; + // The default discard alignment is 1 sector. + self.state.config_space.discard_sector_alignment = 1; + self.state.config_space.max_discard_sectors = MAX_REQUEST_SECTORS; + } + } + + fn get_blk_config_size(&self) -> u64 { + // F_WRITE_ZEROES is not supported for now, so related config does not exist. + if virtio_has_feature(self.state.device_features, VIRTIO_BLK_F_DISCARD) { + offset_of!(VirtioBlkConfig, max_write_zeroes_sectors) as u64 + } else { + offset_of!(VirtioBlkConfig, max_discard_sectors) as u64 + } } } @@ -1046,8 +1143,7 @@ impl VirtioDevice for Block { /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - // F_DISCARD is not supported for now, so related config does not exist. - let config_len = offset_of!(VirtioBlkConfig, max_discard_sectors) as u64; + let config_len = self.get_blk_config_size(); let read_end = offset as usize + data.len(); if offset .checked_add(data.len() as u64) @@ -1065,8 +1161,7 @@ impl VirtioDevice for Block { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - // F_DISCARD is not supported for now, so related config does not exist. - let config_len = offset_of!(VirtioBlkConfig, max_discard_sectors) as u64; + let config_len = self.get_blk_config_size(); if offset .checked_add(data.len() as u64) .filter(|&end| end <= config_len) @@ -1100,6 +1195,7 @@ impl VirtioDevice for Block { Arc::new(BlockIoHandler::complete_func), self.blk_cfg.aio, )?); + let driver_features = self.state.driver_features; let handler = BlockIoHandler { queue: queue.clone(), queue_evt: queue_evts[index].clone(), @@ -1111,7 +1207,7 @@ impl VirtioDevice for Block { direct: self.blk_cfg.direct, serial_num: self.blk_cfg.serial_num.clone(), aio, - driver_features: self.state.driver_features, + driver_features, receiver, update_evt: update_evt.clone(), device_broken: self.broken.clone(), @@ -1121,6 +1217,7 @@ impl VirtioDevice for Block { Some(iops) => Some(LeakBucket::new(iops)?), None => None, }, + discard: self.blk_cfg.discard, }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 50e405489..ac4cacf3b 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -153,6 +153,8 @@ pub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10; pub const VIRTIO_BLK_F_DISCARD: u32 = 13; /// WRITE ZEROES is supported. pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; +/// Unmap flags for write zeroes command. +pub const VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP: u32 = 1; /// GPU EDID feature is supported. pub const VIRTIO_GPU_F_EDID: u32 = 1; @@ -221,6 +223,8 @@ pub const VIRTIO_BLK_T_OUT: u32 = 1; pub const VIRTIO_BLK_T_FLUSH: u32 = 4; /// Device id pub const VIRTIO_BLK_T_GET_ID: u32 = 8; +/// Discard command. +pub const VIRTIO_BLK_T_DISCARD: u32 = 11; /// Device id length pub const VIRTIO_BLK_ID_BYTES: u32 = 20; /// Success -- Gitee From 561b0ba607a07322ef4b895f969d0ecd7f524964 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 4 Apr 2023 16:55:23 +0800 Subject: [PATCH 0970/2187] virtio-blk: support feature VIRTIO_BLK_F_WRITE_ZEROES The feature VIRTIO_BLK_F_WRITE_ZEROES use fallocate to zero the disk space. Signed-off-by: Yan Wang --- devices/src/scsi/bus.rs | 5 +- docs/config_guidebook.md | 7 +-- machine/src/micro_vm/mod.rs | 3 +- machine/src/standard_vm/mod.rs | 18 ++++++- machine_manager/src/config/drive.rs | 42 +++++++++++++-- machine_manager/src/machine.rs | 23 ++++---- machine_manager/src/qmp/qmp_schema.rs | 4 +- util/src/aio/mod.rs | 69 ++++++++++++++++++++++++ util/src/aio/raw.rs | 28 +++++++++- virtio/src/device/block.rs | 78 ++++++++++++++++++++------- virtio/src/lib.rs | 2 + 11 files changed, 238 insertions(+), 41 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 7169f542d..f95909598 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -25,7 +25,7 @@ use crate::ScsiDisk::{ SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, SECTOR_SHIFT, }; -use util::aio::{AioCb, Iovec, OpCode}; +use util::aio::{AioCb, Iovec, OpCode, WriteZeroesState}; /// Scsi Operation code. pub const TEST_UNIT_READY: u8 = 0x00; @@ -563,6 +563,9 @@ impl ScsiRequest { iocompletecb: ScsiCompleteCb { req: Arc::new(Mutex::new(self)), }, + discard: false, + write_zeroes: WriteZeroesState::Off, + write_zeroes_unmap: false, }; if op == SYNCHRONIZE_CACHE { diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 5eff7710a..c10f1a0e3 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -275,7 +275,7 @@ There is only one argument for iothread: Virtio block device is a virtual block device, which process read and write requests in virtio queue from guest. -thirteen properties are supported for virtio block device. +fourteen properties are supported for virtio block device. * id: unique device-id in StratoVirt. * file: the path of backend file on host. @@ -285,6 +285,7 @@ thirteen properties are supported for virtio block device. * iothread: indicate which iothread will be used. (optional) if not set, the main thread will be used. * throttling.iops-total: used to limit IO operations for block device. (optional) * discard: free up unused disk space. (optional) `unmap/ignore` means `on/off`. If not set, default is `ignore`. +* detect-zeroes: optimize writing zeroes to disk space. (optional) `unmap` means it can free up disk space when discard is `unmap`. If dicard is `ignore`, `unmap` of detect-zeroes is same as `on`. If not set, default is `off`. * if: drive type, for block drive, it should be `none`. (optional) If not set, default is `none`. * format: the format of block image. (optional) If not set, default is `raw`. NB: currently only `raw` is supported. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. (optional) The max queues number supported is 32. If not set, the default block queue number is the smaller one of vCPU count and the max queues number (e.g, min(vcpu_count, 32)). @@ -305,10 +306,10 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo ```shell # virtio mmio block device. --drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=][,discard={unmap|ignore}] +-drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=][,discard={unmap|ignore}][,detect-zeroes={unmap|on|off}] -device virtio-blk-device,drive=,id=[,iothread=][,serial=] # virtio pci block device. --drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=][,discard={unmap|ignore}] +-drive id=,file=[,readonly={on|off}][,direct={on|off}][,throttling.iops-total=][,discard={unmap|ignore}][,detect-zeroes={unmap|on|off}] -device virtio-blk-pci,id=,drive=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,serial=][,num-queues=][,bootindex=][,queue-size=] ``` diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c36f20197..42a7c6c2d 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -32,7 +32,7 @@ pub mod error; pub use error::MicroVmError; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::UpdateRegionArgument; -use util::aio::AioEngine; +use util::aio::{AioEngine, WriteZeroesState}; mod mem_layout; mod syscall; @@ -1176,6 +1176,7 @@ impl DeviceInterface for LightMachine { }, queue_size: DEFAULT_VIRTQUEUE_SIZE, discard: false, + write_zeroes: WriteZeroesState::Off, }; if let Err(e) = config.check() { error!("{:?}", e); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 03c8262f5..1870fdd16 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -28,7 +28,7 @@ use ui::{ input::{key_event, point_event}, vnc::qmp_query_vnc, }; -use util::aio::AioEngine; +use util::aio::{AioEngine, WriteZeroesState}; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -770,6 +770,7 @@ impl StdMachine { aio: conf.aio, queue_size, discard: conf.discard, + write_zeroes: conf.write_zeroes, }; dev.check()?; dev @@ -1258,6 +1259,7 @@ impl DeviceInterface for StdMachine { aio: AioEngine::Native, media: "disk".to_string(), discard: false, + write_zeroes: WriteZeroesState::Off, }; if args.cache.is_some() && !args.cache.unwrap().direct.unwrap_or(true) { config.direct = false; @@ -1277,6 +1279,20 @@ impl DeviceInterface for StdMachine { } config.discard = ret.unwrap().into(); } + if let Some(detect_zeroes) = args.detect_zeroes { + let state = detect_zeroes.as_str().parse::(); + if state.is_err() { + let err_msg = format!( + "Invalid write-zeroes argument '{}', expected 'on | off | unmap'", + detect_zeroes + ); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(err_msg), + None, + ); + } + config.write_zeroes = state.unwrap(); + } if let Err(e) = config.check() { error!("{:?}", e); return Response::create_error_response( diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 2f3ac3523..ff2e2eb70 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -24,7 +24,7 @@ use crate::config::{ DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::qmp_schema; -use util::aio::{aio_probe, AioEngine}; +use util::aio::{aio_probe, AioEngine, WriteZeroesState}; const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; @@ -69,6 +69,7 @@ pub struct BlkDevConfig { pub aio: AioEngine, pub queue_size: u16, pub discard: bool, + pub write_zeroes: WriteZeroesState, } #[derive(Debug, Clone)] @@ -95,6 +96,7 @@ impl Default for BlkDevConfig { aio: AioEngine::Native, queue_size: DEFAULT_VIRTQUEUE_SIZE, discard: false, + write_zeroes: WriteZeroesState::Off, } } } @@ -112,6 +114,7 @@ pub struct DriveConfig { pub aio: AioEngine, pub media: String, pub discard: bool, + pub write_zeroes: WriteZeroesState, } impl Default for DriveConfig { @@ -125,6 +128,7 @@ impl Default for DriveConfig { aio: AioEngine::Native, media: "disk".to_string(), discard: false, + write_zeroes: WriteZeroesState::Off, } } } @@ -313,14 +317,15 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { AioEngine::Off } }); - drive.media = cmd_parser .get_value::("media")? .unwrap_or_else(|| "disk".to_string()); - if let Some(discard) = cmd_parser.get_value::("discard")? { drive.discard = discard.into(); } + drive.write_zeroes = cmd_parser + .get_value::("detect-zeroes")? + .unwrap_or(WriteZeroesState::Off); drive.check()?; #[cfg(not(test))] @@ -396,6 +401,7 @@ pub fn parse_blk( blkdevcfg.iops = drive_arg.iops; blkdevcfg.aio = drive_arg.aio; blkdevcfg.discard = drive_arg.discard; + blkdevcfg.write_zeroes = drive_arg.write_zeroes; } else { bail!("No drive configured matched for blk device"); } @@ -531,7 +537,8 @@ impl VmConfig { .push("throttling.iops-total") .push("aio") .push("media") - .push("discard"); + .push("discard") + .push("detect-zeroes"); cmd_parser.parse(block_config)?; let drive_cfg = parse_drive(cmd_parser)?; @@ -899,4 +906,31 @@ mod tests { .is_err(); assert_eq!(ret, true); } + + #[test] + fn test_drive_config_write_zeroes() { + let mut vm_config = VmConfig::default(); + let drive_conf = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=off") + .unwrap(); + assert_eq!(drive_conf.write_zeroes, WriteZeroesState::Off); + + let mut vm_config = VmConfig::default(); + let drive_conf = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=on") + .unwrap(); + assert_eq!(drive_conf.write_zeroes, WriteZeroesState::On); + + let mut vm_config = VmConfig::default(); + let drive_conf = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=unmap") + .unwrap(); + assert_eq!(drive_conf.write_zeroes, WriteZeroesState::Unmap); + + let mut vm_config = VmConfig::default(); + let ret = vm_config + .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=invalid") + .is_err(); + assert_eq!(ret, true); + } } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index fc709b2d1..e6ee1d325 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -354,17 +354,22 @@ pub trait DeviceInterface { } fn query_command_line_options(&self) -> Response { - let mut cmd_lines = Vec::::new(); - let parameters = vec![CmdParameter { - name: "discard".to_string(), - help: "discard operation (ignore/off, unmap/on)".to_string(), - paramter_type: "string".to_string(), - }]; - let cmd_line = CmdLine { + let parameters = vec![ + CmdParameter { + name: "discard".to_string(), + help: "discard operation (unmap|ignore)".to_string(), + paramter_type: "string".to_string(), + }, + CmdParameter { + name: "detect-zeroes".to_string(), + help: "optimize zero writes (unmap|on|off)".to_string(), + paramter_type: "string".to_string(), + }, + ]; + let cmd_lines = vec![CmdLine { parameters, option: "drive".to_string(), - }; - cmd_lines.push(cmd_line); + }]; Response::create_response(serde_json::to_value(cmd_lines).unwrap(), None) } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index af11fdfd6..f8b98668c 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -715,8 +715,8 @@ pub struct blockdev_add { pub cache: Option, #[serde(rename = "read-only")] pub read_only: Option, - #[serde(rename = "read-zeros")] - pub read_zeros: Option, + #[serde(rename = "detect-zeroes")] + pub detect_zeroes: Option, pub driver: Option, pub backing: Option, pub discard: Option, diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 760ae376f..8f4c52358 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -65,6 +65,26 @@ impl FromStr for AioEngine { } } +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +pub enum WriteZeroesState { + Off, + On, + Unmap, +} + +impl FromStr for WriteZeroesState { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + match s { + "off" => Ok(WriteZeroesState::Off), + "on" => Ok(WriteZeroesState::On), + "unmap" => Ok(WriteZeroesState::Unmap), + _ => Err(()), + } + } +} + #[derive(Debug, Clone)] pub struct Iovec { pub iov_base: u64, @@ -92,6 +112,7 @@ pub enum OpCode { Pwritev = 2, Fdsync = 3, Discard = 4, + WriteZeroes = 5, } pub struct AioCb { @@ -105,6 +126,9 @@ pub struct AioCb { pub nbytes: u64, pub user_data: u64, pub iocompletecb: T, + pub discard: bool, + pub write_zeroes: WriteZeroesState, + pub write_zeroes_unmap: bool, } pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; @@ -187,6 +211,16 @@ impl Aio { return (self.complete_func)(&cb, res); } + if cb.opcode == OpCode::Pwritev + && cb.write_zeroes != WriteZeroesState::Off + && iovec_is_zero(&cb.iovec) + { + cb.opcode = OpCode::WriteZeroes; + if cb.write_zeroes == WriteZeroesState::Unmap && cb.discard { + cb.write_zeroes_unmap = true; + } + } + match cb.opcode { OpCode::Preadv | OpCode::Pwritev => { if self.ctx.is_some() { @@ -203,6 +237,7 @@ impl Aio { } } OpCode::Discard => self.discard_sync(cb), + OpCode::WriteZeroes => self.write_zeroes_sync(cb), OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), } } @@ -498,6 +533,21 @@ impl Aio { } (self.complete_func)(&cb, ret) } + + fn write_zeroes_sync(&mut self, cb: AioCb) -> Result<()> { + let mut ret; + if cb.write_zeroes_unmap { + ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); + if ret == 0 { + return (self.complete_func)(&cb, ret); + } + } + ret = raw_write_zeroes(cb.file_fd, cb.offset, cb.nbytes); + if ret < 0 { + error!("Failed to do sync write zeroes."); + } + (self.complete_func)(&cb, ret) + } } pub fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { @@ -561,3 +611,22 @@ pub fn iov_discard_front_direct(iovec: &mut [Iovec], mut size: u64) -> Option<&m } None } + +fn iovec_is_zero(iovecs: &[Iovec]) -> bool { + let size = std::mem::size_of::() as u64; + for iov in iovecs { + if iov.iov_len % size != 0 { + return false; + } + // SAFETY: iov_base and iov_len has been checked in pop_avail(). + let slice = unsafe { + std::slice::from_raw_parts(iov.iov_base as *const u64, (iov.iov_len / size) as usize) + }; + for val in slice.iter() { + if *val != 0 { + return false; + } + } + } + true +} diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index c002e2498..36c179c33 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -13,7 +13,7 @@ use super::Iovec; use libc::{ c_int, c_void, fallocate, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t, - FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE, + FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE, FALLOC_FL_ZERO_RANGE, }; use log::error; use std::os::unix::io::RawFd; @@ -160,3 +160,29 @@ pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { } ret } + +pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i64 { + let mut ret; + loop { + // SAFETY: fd is valid. + ret = unsafe { + fallocate( + fd as c_int, + FALLOC_FL_ZERO_RANGE, + offset as i64, + size as i64, + ) as i64 + }; + if ret == 0 || errno::errno().0 != libc::EINTR { + break; + } + } + if ret < 0 { + error!( + "Failed to fallocate zero range for fd {}, errno {}.", + fd, + errno::errno().0, + ); + } + ret +} diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 13f45f208..a4a0048f1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -27,10 +27,11 @@ use crate::{ iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, - VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, - VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, + VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, + VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, + VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Context, Result}; @@ -45,6 +46,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use util::aio::{ iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, AioEngine, Iovec, OpCode, + WriteZeroesState, }; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; @@ -249,9 +251,13 @@ impl Request { } match out_header.request_type { - VIRTIO_BLK_T_IN | VIRTIO_BLK_T_GET_ID | VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_DISCARD => { + VIRTIO_BLK_T_IN + | VIRTIO_BLK_T_GET_ID + | VIRTIO_BLK_T_OUT + | VIRTIO_BLK_T_DISCARD + | VIRTIO_BLK_T_WRITE_ZEROES => { let data_iovec = match out_header.request_type { - VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_DISCARD => { + VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_DISCARD | VIRTIO_BLK_T_WRITE_ZEROES => { iov_discard_front(&mut elem.out_iovec, size_of::() as u64) } // Otherwise discard the last "status" byte. @@ -350,7 +356,20 @@ impl Request { aiocb.iocompletecb.complete_request(status)?; } VIRTIO_BLK_T_DISCARD => { - self.handle_discard_request(iohandler, aiocb)?; + if !iohandler.discard { + error!("Device does not support discard"); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + } + aiocb.opcode = OpCode::Discard; + self.handle_discard_write_zeroes_req(iohandler, aiocb)?; + } + VIRTIO_BLK_T_WRITE_ZEROES => { + if iohandler.write_zeroes == WriteZeroesState::Off { + error!("Device does not support write-zeroes"); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + } + aiocb.opcode = OpCode::WriteZeroes; + self.handle_discard_write_zeroes_req(iohandler, aiocb)?; } // The illegal request type has been handled in method new(). _ => {} @@ -358,16 +377,11 @@ impl Request { Ok(()) } - fn handle_discard_request( + fn handle_discard_write_zeroes_req( &self, iohandler: &mut BlockIoHandler, mut aiocb: AioCb, ) -> Result<()> { - if !iohandler.discard { - error!("Device does not support discard"); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); - } - let size = size_of::() as u64; // Just support one segment per request. if self.data_len > size { @@ -399,18 +413,29 @@ impl Request { return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } let flags = LittleEndian::read_u32(segment.flags.as_bytes()); - if flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP != 0 { - error!("Discard request must not set unmap flags"); + if flags & !VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP != 0 { + error!("Invalid unmap flags 0x{:x}", flags); return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } - aiocb.opcode = OpCode::Discard; + if aiocb.opcode == OpCode::Discard { + if flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP { + error!("Discard request must not set unmap flags"); + return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + } + } else if aiocb.opcode == OpCode::WriteZeroes + && flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP + && iohandler.discard + { + aiocb.write_zeroes_unmap = true; + } + aiocb.offset = (sector as usize) << SECTOR_SHIFT; aiocb.nbytes = (num_sectors as u64) << SECTOR_SHIFT; iohandler .aio .submit_request(aiocb) - .with_context(|| "Failed to process block request for discard") + .with_context(|| "Failed to process block request for discard or write-zeroes") } fn io_range_valid(&self, disk_sectors: u64) -> bool { @@ -481,6 +506,8 @@ struct BlockIoHandler { leak_bucket: Option, /// Supporting discard or not. discard: bool, + /// The write-zeroes state. + write_zeroes: WriteZeroesState, } impl BlockIoHandler { @@ -602,6 +629,9 @@ impl BlockIoHandler { nbytes: 0, user_data: 0, iocompletecb: aiocompletecb, + discard: self.discard, + write_zeroes: self.write_zeroes, + write_zeroes_unmap: false, }; req_rc.execute(self, aiocb)?; } else { @@ -1044,11 +1074,20 @@ impl Block { self.state.config_space.discard_sector_alignment = 1; self.state.config_space.max_discard_sectors = MAX_REQUEST_SECTORS; } + + if self.blk_cfg.write_zeroes != WriteZeroesState::Off { + self.state.device_features |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; + // Just support one segment per request. + self.state.config_space.max_write_zeroes_seg = 1; + self.state.config_space.max_write_zeroes_sectors = MAX_REQUEST_SECTORS; + self.state.config_space.write_zeroes_may_unmap = 1; + } } fn get_blk_config_size(&self) -> u64 { - // F_WRITE_ZEROES is not supported for now, so related config does not exist. - if virtio_has_feature(self.state.device_features, VIRTIO_BLK_F_DISCARD) { + if virtio_has_feature(self.state.device_features, VIRTIO_BLK_F_WRITE_ZEROES) { + offset_of!(VirtioBlkConfig, unused1) as u64 + } else if virtio_has_feature(self.state.device_features, VIRTIO_BLK_F_DISCARD) { offset_of!(VirtioBlkConfig, max_write_zeroes_sectors) as u64 } else { offset_of!(VirtioBlkConfig, max_discard_sectors) as u64 @@ -1218,6 +1257,7 @@ impl VirtioDevice for Block { None => None, }, discard: self.blk_cfg.discard, + write_zeroes: self.blk_cfg.write_zeroes, }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index ac4cacf3b..3e51ba7a7 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -225,6 +225,8 @@ pub const VIRTIO_BLK_T_FLUSH: u32 = 4; pub const VIRTIO_BLK_T_GET_ID: u32 = 8; /// Discard command. pub const VIRTIO_BLK_T_DISCARD: u32 = 11; +/// Write zeroes command. +pub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13; /// Device id length pub const VIRTIO_BLK_ID_BYTES: u32 = 20; /// Success -- Gitee From 7b04603a75480d0b8a9324db14edeb387d5bbae6 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 8 Apr 2023 11:32:02 +0800 Subject: [PATCH 0971/2187] virtio-blk: add mst for feature DISCARD and WRITE_ZEROES Signed-off-by: Yan Wang --- tests/mod_test/src/libdriver/virtio_block.rs | 100 ++++- tests/mod_test/src/libtest.rs | 3 + tests/mod_test/tests/block_test.rs | 380 ++++++++++++++++++- virtio/src/device/block.rs | 12 +- 4 files changed, 472 insertions(+), 23 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index 8150ef708..5bd9e41d9 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -13,6 +13,7 @@ use std::cell::RefCell; use std::mem::size_of; use std::rc::Rc; +use util::byte_code::ByteCode; use util::num_ops::round_up; use super::machine::TestStdMachine; @@ -64,13 +65,21 @@ pub const REQ_STATUS_LEN: u32 = 1; pub const REQ_DATA_OFFSET: u64 = REQ_ADDR_LEN as u64; pub const REQ_STATUS_OFFSET: u64 = (REQ_ADDR_LEN + REQ_DATA_LEN) as u64; -#[allow(unused)] +/// Used to compute the number of sectors. +pub const SECTOR_SHIFT: u8 = 9; +/// Max number sectors of per request. +pub const MAX_REQUEST_SECTORS: u32 = u32::MAX >> SECTOR_SHIFT; + +#[repr(C)] +#[derive(Default, Clone, Copy)] pub struct VirtBlkDiscardWriteZeroes { - sector: u64, - num_sectors: u32, - flags: u32, + pub sector: u64, + pub num_sectors: u32, + pub flags: u32, } +impl ByteCode for VirtBlkDiscardWriteZeroes {} + #[allow(unused)] pub struct TestVirtBlkReq { req_type: u32, @@ -161,12 +170,7 @@ pub fn virtio_blk_request( } VIRTIO_BLK_T_FLUSH => {} VIRTIO_BLK_T_GET_ID => {} - VIRTIO_BLK_T_DISCARD => { - assert_eq!(data_size % (REQ_DATA_LEN as usize), 0) - } - VIRTIO_BLK_T_WRITE_ZEROES => { - assert_eq!(data_size % size_of::(), 0) - } + VIRTIO_BLK_T_DISCARD | VIRTIO_BLK_T_WRITE_ZEROES => {} VIRTIO_BLK_T_ILLGEAL => {} _ => { assert_eq!(data_size, 0) @@ -189,9 +193,19 @@ pub fn virtio_blk_request( test_state.borrow().memwrite(addr, req_bytes.as_slice()); let mut data_bytes = req.data.as_bytes().to_vec(); data_bytes.resize(data_size, 0); - test_state - .borrow() - .memwrite(data_addr, data_bytes.as_slice()); + + // Write data to memory. If the data length is bigger than 4096, the memwrite() + // will return error. So, split it to 512 bytes. + let size = data_bytes.len(); + let mut offset = 0; + while offset < size { + let len = std::cmp::min(512, size - offset); + test_state.borrow().memwrite( + data_addr + offset as u64, + &data_bytes.as_slice()[offset..offset + len], + ); + offset += len; + } test_state .borrow() .memwrite(data_addr + data_size as u64, &status.to_le_bytes()); @@ -341,6 +355,66 @@ pub fn virtio_blk_read( ); } +pub fn virtio_blk_read_write_zeroes( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + vq: Rc>, + req_type: u32, + sector: u64, + data_len: usize, +) { + let mut read = true; + let mut blk_req = TestVirtBlkReq::new(req_type, 1, sector, data_len); + if req_type == VIRTIO_BLK_T_OUT { + unsafe { + blk_req.data.as_mut_vec().append(&mut vec![0; data_len]); + } + read = false; + } + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + let data_addr = req_addr + REQ_ADDR_LEN as u64; + let data_entries: Vec = vec![ + TestVringDescEntry { + data: req_addr, + len: REQ_ADDR_LEN, + write: false, + }, + TestVringDescEntry { + data: data_addr, + len: data_len as u32, + write: read, + }, + TestVringDescEntry { + data: data_addr + data_len as u64, + len: REQ_STATUS_LEN, + write: true, + }, + ]; + let free_head = vq + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + blk.borrow().kick_virtqueue(test_state.clone(), vq.clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + vq.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + let status_addr = req_addr + REQ_ADDR_LEN as u64 + data_len as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + + if read { + assert_eq!( + test_state.borrow().memread(data_addr, data_len as u64), + vec![0; data_len], + ); + } +} + pub fn virtio_blk_default_feature(blk: Rc>) -> u64 { let mut features = blk.borrow().get_device_features(); features &= !(VIRTIO_F_BAD_FEATURE diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 1d47e8e28..d5a04217b 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -25,6 +25,8 @@ use hex; use crate::utils::get_tmp_dir; +const MAX_SOCKET_MSG_LENGTH: usize = 8192; + pub struct StreamHandler { stream: UnixStream, } @@ -35,6 +37,7 @@ impl StreamHandler { } fn write_line(&self, cmd: &str) { + assert!(cmd.len() <= MAX_SOCKET_MSG_LENGTH); self.stream .try_clone() .unwrap() diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 183bd4c06..7c3a58d6b 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -17,28 +17,147 @@ use mod_test::libdriver::virtio::TestVringDescEntry; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; use mod_test::libdriver::virtio_block::{ add_blk_request, create_blk, set_up, tear_down, virtio_blk_default_feature, virtio_blk_read, - virtio_blk_request, virtio_blk_write, TestVirtBlkReq, DEFAULT_IO_REQS, REQ_ADDR_LEN, - REQ_DATA_LEN, REQ_STATUS_LEN, TIMEOUT_US, VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_BLK_SIZE, + virtio_blk_read_write_zeroes, virtio_blk_request, virtio_blk_write, TestVirtBlkReq, + VirtBlkDiscardWriteZeroes, DEFAULT_IO_REQS, MAX_REQUEST_SECTORS, REQ_ADDR_LEN, REQ_DATA_LEN, + REQ_DATA_OFFSET, REQ_STATUS_LEN, TIMEOUT_US, VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_LIFETIME, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, - VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_FLUSH, - VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_ILLGEAL, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, + VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, + VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_ILLGEAL, VIRTIO_BLK_T_IN, + VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::TestState; use mod_test::utils::{create_img, TEST_IMAGE_SIZE}; use std::cell::RefCell; +use std::mem::size_of; +use std::process::Command; use std::rc::Rc; use std::time::{Duration, Instant}; use std::{thread, time}; use util::aio::{aio_probe, AioEngine}; +use util::byte_code::ByteCode; use util::num_ops::round_up; use util::offset_of; const TEST_IMAGE_SIZE_1M: u64 = 1024 * 1024; +fn execute_cmd(cmd: String) -> Vec { + let args = cmd.split(' ').collect::>(); + if args.len() <= 0 { + return vec![]; + } + + let mut cmd_exe = Command::new(args[0]); + for i in 1..args.len() { + cmd_exe.arg(args[i]); + } + + let output = cmd_exe + .output() + .expect(format!("Failed to execute {}", cmd).as_str()); + println!("{:?}, output: {:?}", args, output); + assert!(output.status.success()); + output.stdout +} + +fn virtio_blk_discard_and_write_zeroes( + blk: Rc>, + test_state: Rc>, + alloc: Rc>, + virtqueue: Rc>, + req_data: &[u8], + status: u8, + need_poll_elem: bool, + discard: bool, +) { + let req_len = req_data.len(); + let mut blk_req = if discard { + TestVirtBlkReq::new(VIRTIO_BLK_T_DISCARD, 1, 0, req_len) + } else { + TestVirtBlkReq::new(VIRTIO_BLK_T_WRITE_ZEROES, 1, 0, req_len) + }; + blk_req.data = unsafe { String::from_utf8_unchecked(req_data.to_vec()) }; + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: REQ_ADDR_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + REQ_DATA_OFFSET, + len: req_len as u32, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + REQ_DATA_OFFSET + req_len as u64, + len: REQ_STATUS_LEN, + write: true, + }); + let free_head = virtqueue + .borrow_mut() + .add_chained(test_state.clone(), data_entries); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueue.clone()); + if need_poll_elem { + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } + let status_addr = req_addr + REQ_DATA_OFFSET + req_len as u64; + let read_status = test_state.borrow().readb(status_addr); + assert_eq!(read_status, status); +} + +fn get_disk_size(img_path: Rc) -> u64 { + let out = execute_cmd(format!("du -shk {}", img_path)); + let str_out = std::str::from_utf8(&out) + .unwrap() + .split('\t') + .collect::>(); + serde_json::from_str::(str_out[0]).unwrap() +} + +fn virtio_blk_check_discard_config(blk: Rc>) { + // (offset, expected_value). + let reqs = [ + ( + offset_of!(VirtioBlkConfig, max_discard_sectors), + MAX_REQUEST_SECTORS, + ), + (offset_of!(VirtioBlkConfig, max_discard_seg), 1), + (offset_of!(VirtioBlkConfig, discard_sector_alignment), 1), + ]; + for (offset, expected_value) in reqs { + assert_eq!(blk.borrow().config_readl(offset as u64), expected_value); + } +} + +fn virtio_blk_check_write_zeroes_config(blk: Rc>) { + // (offset, expected_value). + let reqs = [ + ( + offset_of!(VirtioBlkConfig, max_write_zeroes_sectors), + MAX_REQUEST_SECTORS, + ), + (offset_of!(VirtioBlkConfig, max_write_zeroes_seg), 1), + ]; + for (offset, expected_value) in reqs { + assert_eq!(blk.borrow().config_readl(offset as u64), expected_value); + } + let offset = offset_of!(VirtioBlkConfig, write_zeroes_may_unmap); + assert_eq!(blk.borrow().config_readb(offset as u64), 1); +} + fn virtio_blk_get_id( blk: Rc>, test_state: Rc>, @@ -1443,3 +1562,256 @@ fn blk_exceed_capacity() { image_path.clone(), ); } + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_DISCARD'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_DISCARD'. +/// 2. Do the discard request with different arguments. +/// 3. Destroy device. +/// Expect: +/// 1/3: success. +/// 2: success or failure, stratovirt process is normal. +#[test] +fn blk_feature_discard() { + let req_len = std::mem::size_of::(); + // (sector, num_sectors, flags, req_len, enable_feature, discard, status) + let reqs = [ + (0, 2048, 0, req_len, true, "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, req_len, false, "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, 8, true, "unmap", 0xff), + (0, 2048, 0, 32, true, "unmap", VIRTIO_BLK_S_UNSUPP), + (0, 2048, 1, req_len, true, "unmap", VIRTIO_BLK_S_UNSUPP), + (0, 2048, 0xff, req_len, true, "unmap", VIRTIO_BLK_S_UNSUPP), + ( + 0, + (TEST_IMAGE_SIZE >> 9) as u32 + 1, + 0, + req_len, + true, + "unmap", + VIRTIO_BLK_S_IOERR, + ), + ( + 0, + MAX_REQUEST_SECTORS + 1, + 0, + req_len, + true, + "unmap", + VIRTIO_BLK_S_IOERR, + ), + (0, 2048, 0, req_len, false, "ignore", VIRTIO_BLK_S_UNSUPP), + ]; + let mut i = 1; + for (sector, num_sectors, flags, len, enabled, discard, status) in reqs { + println!("blk_feature_discard: request {}", i); + i += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(format!(",discard={},direct=false", discard)); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let mut features = virtio_blk_default_feature(blk.clone()); + if enabled { + features |= 1 << VIRTIO_BLK_F_DISCARD; + } else { + features &= !(1 << VIRTIO_BLK_F_DISCARD); + } + + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + if discard != "ignore" { + virtio_blk_check_discard_config(blk.clone()); + } + + let mut need_poll_elem = true; + let req_data = if len == req_len { + let req = VirtBlkDiscardWriteZeroes { + sector, + num_sectors, + flags, + }; + req.as_bytes().to_vec() + } else { + if len < req_len { + need_poll_elem = false; + } + vec![0; len] + }; + virtio_blk_discard_and_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + &req_data, + status, + need_poll_elem, + true, + ); + if status == VIRTIO_BLK_S_OK { + let image_size = get_disk_size(image_path.clone()); + assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); + } + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } +} + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_WRITE_ZEROES'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_WRITE_ZEROES'. +/// 2. Do the write-zeroes request with different arguments. +/// 3. Destroy device. +/// Expect: +/// 1/3: success. +/// 2: success or failure, stratovirt process is normal. +#[test] +fn blk_feature_write_zeroes() { + let wz_len = size_of::(); + let req_len = size_of::(); + // (sector, num_sectors, flags, req_len, enable_feature, write_zeroes, discard, status) + let reqs = [ + (0, 2048, 0, wz_len, true, "on", "ignore", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, true, "on", "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, false, "on", "ignore", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, false, "on", "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, true, "unmap", "ignore", VIRTIO_BLK_S_OK), + ( + 0, + 2048, + 0, + wz_len, + false, + "unmap", + "ignore", + VIRTIO_BLK_S_OK, + ), + ( + 0, + 2048, + 0, + wz_len, + false, + "off", + "ignore", + VIRTIO_BLK_S_UNSUPP, + ), + ( + 0, + 2048, + 0, + wz_len, + false, + "off", + "unmap", + VIRTIO_BLK_S_UNSUPP, + ), + (0, 2048, 1, wz_len, true, "unmap", "unmap", VIRTIO_BLK_S_OK), + (0, 8, 0, req_len, true, "unmap", "unmap", VIRTIO_BLK_S_OK), + (0, 0, 0, req_len, true, "on", "unmap", VIRTIO_BLK_S_OK), + ]; + let mut i = 1; + for (sector, num_sectors, flags, len, enabled, write_zeroes, discard, status) in reqs { + println!("blk_feature_write_zeroes: request {}", i); + i += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(format!( + ",detect-zeroes={},discard={},direct=false", + write_zeroes, discard + )); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = + create_blk(image_path.clone(), device_args, drive_args, other_args); + + let mut features = virtio_blk_default_feature(blk.clone()); + if discard == "unmap" { + features |= 1 << VIRTIO_BLK_F_DISCARD; + } + if enabled { + features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES; + } else { + features &= !(1 << VIRTIO_BLK_F_WRITE_ZEROES); + } + + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + if enabled { + virtio_blk_check_write_zeroes_config(blk.clone()); + } + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + if len == wz_len { + let req_data = VirtBlkDiscardWriteZeroes { + sector, + num_sectors, + flags, + }; + virtio_blk_discard_and_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + &req_data.as_bytes().to_vec(), + status, + true, + false, + ); + } else { + virtio_blk_read_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + 4096, + ); + } + + if write_zeroes != "off" { + virtio_blk_read_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_IN, + 0, + 512, + ); + } + + if status == VIRTIO_BLK_S_OK + && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) + { + let image_size = get_disk_size(image_path.clone()); + assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); + } + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } +} diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index a4a0048f1..a7c21972a 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -978,17 +978,17 @@ pub struct VirtioBlkConfig { /// Number of virtio queues, only available when `VIRTIO_BLK_F_MQ` is set. pub num_queues: u16, /// The maximum discard sectors for one segment. - max_discard_sectors: u32, + pub max_discard_sectors: u32, /// The maximum number of discard segments in a discard command. - max_discard_seg: u32, + pub max_discard_seg: u32, /// Discard commands must be aligned to this number of sectors. - discard_sector_alignment: u32, + pub discard_sector_alignment: u32, /// The maximum number of write zeros sectors. - max_write_zeroes_sectors: u32, + pub max_write_zeroes_sectors: u32, /// The maximum number of segments in a write zeroes command. - max_write_zeroes_seg: u32, + pub max_write_zeroes_seg: u32, /// Deallocation of one or more of the sectors. - write_zeroes_may_unmap: u8, + pub write_zeroes_may_unmap: u8, /// Reserved data. unused1: [u8; 3], } -- Gitee From a0d35442291b6bedfbbe1c62f7f2b91d2ef066a1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 8 Apr 2023 19:10:43 +0800 Subject: [PATCH 0972/2187] net_test: fix some testcase problem 1. Update the used_event after handling request. 2. Rename br/tap device. 3. Clear br/tap device before the testcase. --- tests/mod_test/tests/net_test.rs | 109 +++++++++++++++++++------------ 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index ad14c94ed..0b33faef3 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -335,8 +335,7 @@ struct VirtioNetHdr { } impl ByteCode for VirtioNetHdr {} -/// Execute cmd used to create br/tap. -fn execute_cmd(cmd: String) { +fn execute_cmd(cmd: String, check: bool) { let args = cmd.split(' ').collect::>(); if args.len() <= 0 { return; @@ -351,24 +350,34 @@ fn execute_cmd(cmd: String) { .output() .expect(format!("Failed to execute {}", cmd).as_str()); println!("{:?}", args); - assert!(output.status.success()); + if check { + assert!(output.status.success()); + } +} + +fn execute_cmd_unchecked(cmd: String) { + execute_cmd(cmd, false); +} + +fn execute_cmd_checked(cmd: String) { + execute_cmd(cmd, true); } fn create_tap(id: u8, mq: bool) { - let br_name = "qbr".to_string() + &id.to_string(); - let tap_name = "qtap".to_string() + &id.to_string(); - execute_cmd("brctl addbr ".to_string() + &br_name); + let br_name = "mst_net_qbr".to_string() + &id.to_string(); + let tap_name = "mst_net_qtap".to_string() + &id.to_string(); + execute_cmd_checked("brctl addbr ".to_string() + &br_name); if mq { - execute_cmd( + execute_cmd_checked( "ip tuntap add ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), ); } else { - execute_cmd("ip tuntap add ".to_string() + &tap_name + &" mode tap".to_string()); + execute_cmd_checked("ip tuntap add ".to_string() + &tap_name + &" mode tap".to_string()); } - execute_cmd("brctl addif ".to_string() + &br_name + &" ".to_string() + &tap_name); - execute_cmd("ip link set ".to_string() + &br_name + &" up".to_string()); - execute_cmd("ip link set ".to_string() + &tap_name + &" up".to_string()); - execute_cmd( + execute_cmd_checked("brctl addif ".to_string() + &br_name + &" ".to_string() + &tap_name); + execute_cmd_checked("ip link set ".to_string() + &br_name + &" up".to_string()); + execute_cmd_checked("ip link set ".to_string() + &tap_name + &" up".to_string()); + execute_cmd_checked( "ip address add ".to_string() + &id.to_string() + &".1.1.".to_string() @@ -379,18 +388,18 @@ fn create_tap(id: u8, mq: bool) { } fn clear_tap(id: u8, mq: bool) { - let br_name = "qbr".to_string() + &id.to_string(); - let tap_name = "qtap".to_string() + &id.to_string(); - execute_cmd("ip link set ".to_string() + &tap_name + &" down".to_string()); - execute_cmd("ip link set ".to_string() + &br_name + &" down".to_string()); + let br_name = "mst_net_qbr".to_string() + &id.to_string(); + let tap_name = "mst_net_qtap".to_string() + &id.to_string(); + execute_cmd_unchecked("ip link set ".to_string() + &tap_name + &" down".to_string()); + execute_cmd_unchecked("ip link set ".to_string() + &br_name + &" down".to_string()); if mq { - execute_cmd( + execute_cmd_unchecked( "ip tuntap del ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), ); } else { - execute_cmd("ip tuntap del ".to_string() + &tap_name + &" mode tap".to_string()); + execute_cmd_unchecked("ip tuntap del ".to_string() + &tap_name + &" mode tap".to_string()); } - execute_cmd("brctl delbr ".to_string() + &br_name); + execute_cmd_unchecked("brctl delbr ".to_string() + &br_name); } #[allow(unused)] @@ -438,7 +447,7 @@ pub fn create_net( extra_args.append(&mut args); let net_args = - String::from("-netdev tap,id=netdev0,ifname=qtap") + &id.to_string() + &mq_queues; + String::from("-netdev tap,id=netdev0,ifname=mst_net_qtap") + &id.to_string() + &mq_queues; args = net_args.split(' ').collect(); extra_args.append(&mut args); @@ -461,6 +470,7 @@ fn set_up( Rc>, Rc>, ) { + clear_tap(id, mq); create_tap(id, mq); create_net(id, mq, num_queues, with_mac, false) } @@ -476,6 +486,7 @@ fn set_up_iothread( Rc>, Rc>, ) { + clear_tap(id, mq); create_tap(id, mq); create_net(id, mq, num_queues, with_mac, true) } @@ -534,30 +545,17 @@ fn init_net_device( vqs } -fn check_arp_mac( - net: Rc>, +fn poll_used_ring( test_state: Rc>, vq: Rc>, arp_request: &[u8], need_reply: bool, -) { +) -> bool { let mut start = 0_u64; - let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_micros(TIMEOUT_US); - let timeout_us_no_reply = time::Duration::from_micros(TIMEOUT_US / 5); - loop { - if need_reply { - assert!(time::Instant::now() - start_time < timeout_us); - if !net.borrow().queue_was_notified(vq.clone()) { - continue; - } - } else if time::Instant::now() - start_time > timeout_us_no_reply { - return; - } - - let idx = test_state - .borrow() - .readw(vq.borrow().used + offset_of!(VringUsed, idx) as u64); + let mut idx = test_state + .borrow() + .readw(vq.borrow().used + offset_of!(VringUsed, idx) as u64); + while start < idx as u64 { for i in start..idx as u64 { let len = test_state.borrow().readw( vq.borrow().used @@ -580,7 +578,7 @@ fn check_arp_mac( == packets[dst_mac_pos..dst_mac_pos + MAC_ADDR_LEN] { if need_reply { - return; + return true; } else { assert!(false); } @@ -588,6 +586,37 @@ fn check_arp_mac( } } start = idx as u64; + vq.borrow().set_used_event(test_state.clone(), start as u16); + idx = test_state + .borrow() + .readw(vq.borrow().used + offset_of!(VringUsed, idx) as u64); + } + false +} + +fn check_arp_mac( + net: Rc>, + test_state: Rc>, + vq: Rc>, + arp_request: &[u8], + need_reply: bool, +) { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + let timeout_us_no_reply = time::Duration::from_micros(TIMEOUT_US / 5); + loop { + if need_reply { + assert!(time::Instant::now() - start_time < timeout_us); + if !net.borrow().queue_was_notified(vq.clone()) { + continue; + } + } else if time::Instant::now() - start_time > timeout_us_no_reply { + return; + } + + if poll_used_ring(test_state.clone(), vq.clone(), arp_request, need_reply) { + return; + } } } -- Gitee From dccc2f6862769c0a03997db070b0e0e7c036f8de Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 8 Apr 2023 18:57:59 +0800 Subject: [PATCH 0973/2187] code_style: Simplify Option type handling Use with_context(), unwrap_or(), map(), map_or() etc. to simplify Option type handling. Signed-off-by: Keqian Zhu --- boot_loader/src/aarch64/mod.rs | 11 +-- boot_loader/src/x86_64/direct_boot/mod.rs | 19 ++-- boot_loader/src/x86_64/mod.rs | 12 +-- boot_loader/src/x86_64/standard_boot/elf.rs | 7 +- cpu/src/aarch64/mod.rs | 6 +- cpu/src/lib.rs | 9 +- cpu/src/x86_64/mod.rs | 6 +- .../src/interrupt_controller/aarch64/state.rs | 12 +-- devices/src/legacy/pflash.rs | 4 +- devices/src/legacy/pl011.rs | 6 +- devices/src/legacy/pl031.rs | 6 +- devices/src/legacy/serial.rs | 6 +- devices/src/usb/descriptor.rs | 62 ++++++------ devices/src/usb/mod.rs | 22 ++--- devices/src/usb/xhci/xhci_controller.rs | 79 +++++++-------- devices/src/usb/xhci/xhci_pci.rs | 8 +- machine/src/lib.rs | 66 ++++++------- machine/src/micro_vm/mod.rs | 12 +-- machine/src/standard_vm/aarch64/mod.rs | 11 +-- machine/src/standard_vm/mod.rs | 57 +++-------- machine/src/vm_state.rs | 6 +- machine_manager/src/cmdline.rs | 9 +- machine_manager/src/config/chardev.rs | 73 +++++--------- machine_manager/src/config/drive.rs | 98 ++++++------------- machine_manager/src/config/fs.rs | 28 ++---- machine_manager/src/config/machine_config.rs | 6 +- machine_manager/src/config/mod.rs | 8 +- machine_manager/src/config/network.rs | 38 +++---- machine_manager/src/config/numa.rs | 11 +-- machine_manager/src/config/pci.rs | 51 ++++------ machine_manager/src/config/rng.rs | 72 +++++--------- machine_manager/src/config/sasl_auth.rs | 17 ++-- machine_manager/src/config/scsi.rs | 35 ++----- machine_manager/src/config/tls_creds.rs | 18 ++-- migration_derive/src/field_parser.rs | 7 +- ozone/src/handler.rs | 12 +-- pci/src/hotplug.rs | 20 ++-- pci/src/msix.rs | 6 +- pci/src/root_port.rs | 6 +- vhost_user_fs/src/fuse_msg.rs | 16 ++- virtio/src/device/balloon.rs | 24 +++-- virtio/src/device/block.rs | 6 +- virtio/src/device/console.rs | 8 +- virtio/src/device/net.rs | 6 +- virtio/src/device/rng.rs | 6 +- virtio/src/transport/virtio_mmio.rs | 6 +- virtio/src/transport/virtio_pci.rs | 6 +- virtio/src/vhost/kernel/vsock.rs | 6 +- 48 files changed, 362 insertions(+), 664 deletions(-) diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index a2a6297ca..76010edde 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -100,19 +100,12 @@ fn load_initrd( File::open(initrd_path).with_context(|| BootLoaderError::BootLoaderOpenInitrd)?; let initrd_size = initrd_image.metadata().unwrap().len(); - let initrd_start = if let Some(addr) = sys_mem + let initrd_start = sys_mem .memory_end_address() .raw_value() .checked_sub(initrd_size) .filter(|addr| addr >= &kernel_end) - { - addr - } else { - return Err(anyhow!(BootLoaderError::InitrdOverflow( - kernel_end, - initrd_size - ))); - }; + .with_context(|| BootLoaderError::InitrdOverflow(kernel_end, initrd_size))?; if let Some(fw_cfg) = fwcfg { let mut initrd_data = Vec::new(); diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 6182a040c..99b019921 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -17,8 +17,10 @@ use std::fs::File; use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; -use address_space::{AddressSpace, GuestAddress}; +use anyhow::{Context, Result}; use log::info; + +use address_space::{AddressSpace, GuestAddress}; use util::byte_code::ByteCode; use self::gdt::setup_gdt; @@ -30,7 +32,6 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::error::BootLoaderError; -use anyhow::{bail, Context, Result}; /// Load bzImage linux kernel to Guest Memory. /// @@ -238,20 +239,16 @@ pub fn load_linux( config: &X86BootLoaderConfig, sys_mem: &Arc, ) -> Result { - if config.kernel.is_none() { - bail!("Kernel is required for direct-boot mode."); - } - + let kernel_path = config + .kernel + .as_ref() + .with_context(|| "Kernel is required for direct-boot mode.")?; let mut boot_loader_layout = X86BootLoader { boot_sp: BOOT_LOADER_SP, zero_page_addr: ZERO_PAGE_START, ..Default::default() }; - let mut boot_header = load_kernel_image( - config.kernel.as_ref().unwrap(), - sys_mem, - &mut boot_loader_layout, - )?; + let mut boot_header = load_kernel_image(kernel_path, sys_mem, &mut boot_loader_layout)?; load_initrd(config, sys_mem, &mut boot_header) .with_context(|| "Failed to load initrd to vm memory")?; diff --git a/boot_loader/src/x86_64/mod.rs b/boot_loader/src/x86_64/mod.rs index 5490b178f..36e971a89 100644 --- a/boot_loader/src/x86_64/mod.rs +++ b/boot_loader/src/x86_64/mod.rs @@ -59,11 +59,11 @@ mod standard_boot; use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use address_space::AddressSpace; -use devices::legacy::FwCfgOps; +use anyhow::{Context, Result}; use kvm_bindings::kvm_segment; -use anyhow::{bail, Result}; +use address_space::AddressSpace; +use devices::legacy::FwCfgOps; const ZERO_PAGE_START: u64 = 0x0000_7000; const PML4_START: u64 = 0x0000_9000; @@ -143,10 +143,8 @@ pub fn load_linux( if config.prot64_mode { direct_boot::load_linux(config, sys_mem) } else { - if fwcfg.is_none() { - bail!("Failed to load linux: No FwCfg provided"); - } - let mut locked_fwcfg = fwcfg.unwrap().lock().unwrap(); + let fwcfg = fwcfg.with_context(|| "Failed to load linux: No FwCfg provided")?; + let mut locked_fwcfg = fwcfg.lock().unwrap(); standard_boot::load_linux(config, sys_mem, &mut *locked_fwcfg)?; Ok(X86BootLoader { diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 1fd6e9860..46f8bc5ca 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -211,13 +211,12 @@ pub fn load_elf_kernel( } } } - if pvh_start_addr.is_none() { - bail!("No Note header contains PVH entry info in ELF kernel image."); - } + let pvh_start_addr = pvh_start_addr + .with_context(|| "No Note header contains PVH entry info in ELF kernel image.")?; fwcfg.add_data_entry( FwCfgEntryType::KernelEntry, - (pvh_start_addr.unwrap() as u32).as_bytes().to_vec(), + (pvh_start_addr as u32).as_bytes().to_vec(), )?; fwcfg.add_data_entry( FwCfgEntryType::KernelAddr, diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 2c123ae55..ab42e2e24 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -344,11 +344,7 @@ impl StateTransfer for CPU { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&ArmCPUState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&ArmCPUState::descriptor().name).unwrap_or(!0) } } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 3e85e0226..9b10e7196 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -471,11 +471,10 @@ impl CPUInterface for CPU { } fn kvm_vcpu_exec(&self) -> Result { - let vm = if let Some(vm) = self.vm.upgrade() { - vm - } else { - return Err(anyhow!(CpuError::NoMachineInterface)); - }; + let vm = self + .vm + .upgrade() + .with_context(|| CpuError::NoMachineInterface)?; match self.fd.run() { Ok(run) => match run { diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index f281e63dd..55c8660af 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -601,11 +601,7 @@ impl StateTransfer for CPU { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&X86CPUState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&X86CPUState::descriptor().name).unwrap_or(!0) } } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index c82198846..3d52d0abe 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -650,11 +650,7 @@ impl StateTransfer for GICv3 { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&GICv3State::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&GICv3State::descriptor().name).unwrap_or(!0) } } @@ -726,11 +722,7 @@ impl StateTransfer for GICv3Its { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&GICv3ItsState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&GICv3ItsState::descriptor().name).unwrap_or(!0) } } diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index abe17601f..4431a8661 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -515,8 +515,8 @@ impl PFlash { } else { self.bank_width * 8 }; - value = if let Some(v) = extract_u32(value, 0, length) { - v + if let Some(v) = extract_u32(value, 0, length) { + value = v; } else { error!("Failed to extract bits from u32 value"); return false; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 23ca96a01..84d5909bf 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -410,11 +410,7 @@ impl StateTransfer for PL011 { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&PL011State::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&PL011State::descriptor().name).unwrap_or(!0) } } diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 437876854..7e4a10fa1 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -224,11 +224,7 @@ impl StateTransfer for PL031 { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&PL031State::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&PL031State::descriptor().name).unwrap_or(!0) } } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 2f972ef9f..21f5c4a92 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -450,11 +450,7 @@ impl StateTransfer for Serial { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&SerialState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&SerialState::descriptor().name).unwrap_or(!0) } } diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 89d40d44d..b03ba5a43 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -12,7 +12,8 @@ use std::sync::Arc; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; + use util::byte_code::ByteCode; use super::config::*; @@ -234,16 +235,14 @@ impl UsbDescriptor { } fn get_config_descriptor(&self, index: u32) -> Result> { - let confs = if let Some(desc) = self.device_desc.as_ref() { - &desc.configs - } else { - bail!("Device descriptor not found"); - }; - let conf = if let Some(conf) = confs.get(index as usize) { - conf - } else { - bail!("Config descriptor index {} is invalid", index); - }; + let confs = self + .device_desc + .as_ref() + .map(|desc| &desc.configs) + .with_context(|| "Device descriptor not found")?; + let conf = confs + .get(index as usize) + .with_context(|| format!("Config descriptor index {} is invalid", index))?; let mut config_desc = conf.config_desc; let mut iads = self.get_iads_descriptor(conf.iad_desc.as_ref())?; let mut ifs = self.get_interfaces_descriptor(conf.interfaces.as_ref())?; @@ -312,11 +311,10 @@ impl UsbDescriptor { let str: [u8; 4] = [4, 3, 9, 4]; return Ok(str.to_vec()); } - let found_str = if let Some(str) = self.strings.get(index as usize) { - str - } else { - bail!("String descriptor index {} is invalid", index); - }; + let found_str = self + .strings + .get(index as usize) + .with_context(|| format!("String descriptor index {} is invalid", index))?; let len = found_str.len() as u8 * 2 + 2; let mut vec = vec![0_u8; len as usize]; vec[0] = len; @@ -421,11 +419,11 @@ impl UsbDescriptorOps for UsbDevice { self.descriptor.interface_number = 0; self.descriptor.configuration_selected = None; } else { - let desc = if let Some(desc) = self.descriptor.device_desc.as_ref() { - desc - } else { - bail!("Device Descriptor not found"); - }; + let desc = self + .descriptor + .device_desc + .as_ref() + .with_context(|| "Device Descriptor not found")?; let num = desc.device_desc.bNumConfigurations; let mut found = false; for i in 0..num as usize { @@ -453,15 +451,12 @@ impl UsbDescriptorOps for UsbDevice { } fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()> { - let iface = if let Some(face) = self.descriptor.find_interface(index, v) { - face - } else { - bail!( + let iface = self.descriptor.find_interface(index, v).with_context(|| { + format!( "Interface descriptor not found. index {} value {}", - index, - v - ); - }; + index, v + ) + })?; self.descriptor.altsetting[index as usize] = v; self.descriptor.interfaces[index as usize] = Some(iface); self.init_endpoint()?; @@ -471,12 +466,11 @@ impl UsbDescriptorOps for UsbDevice { fn init_endpoint(&mut self) -> Result<()> { self.reset_usb_endpoint(); for i in 0..self.descriptor.interface_number { - let iface = if let Some(iface) = self.descriptor.interfaces[i as usize].as_ref() { - iface - } else { + let iface = self.descriptor.interfaces[i as usize].as_ref(); + if iface.is_none() { continue; - }; - let iface = iface.clone(); + } + let iface = iface.unwrap().clone(); for e in 0..iface.interface_desc.bNumEndpoints { let in_direction = iface.endpoints[e as usize].endpoint_desc.bEndpointAddress & USB_DIRECTION_DEVICE_TO_HOST diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 1dcea7c7e..2cfd4eabf 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -31,7 +31,7 @@ pub mod xhci; use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; -use anyhow::bail; +use anyhow::{bail, Context}; use log::{debug, error}; use util::aio::{mem_from_buf, mem_to_buf}; @@ -195,17 +195,15 @@ impl UsbDevice { let conf = if let Some(conf) = &self.descriptor.configuration_selected { conf.clone() } else { - let desc = if let Some(desc) = self.descriptor.device_desc.as_ref() { - desc - } else { - bail!("Device descriptor not found"); - }; - let conf = if let Some(conf) = desc.configs.get(0) { - conf - } else { - bail!("Config descriptor not found"); - }; - conf.clone() + let desc = self + .descriptor + .device_desc + .as_ref() + .with_context(|| "Device descriptor not found")?; + desc.configs + .get(0) + .with_context(|| "Config descriptor not found")? + .clone() }; self.data_buf[0] = 0; if conf.config_desc.bmAttributes & USB_CONFIGURATION_ATTR_SELF_POWER diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a425757d1..d9bd196dc 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -635,12 +635,13 @@ impl XhciDevice { /// Reset xhci port. pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { let mut locked_port = xhci_port.lock().unwrap(); - let usb_dev = if let Some(dev) = locked_port.dev.as_ref() { - dev - } else { + let usb_dev = locked_port.dev.as_ref(); + if usb_dev.is_none() { // No device, no need to reset. return Ok(()); - }; + } + + let usb_dev = usb_dev.unwrap(); usb_dev.lock().unwrap().reset(); let speed = usb_dev.lock().unwrap().speed(); if speed == USB_SPEED_SUPER && warm_reset { @@ -856,13 +857,12 @@ impl XhciDevice { fn address_device(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; - if ictx.checked_add(INPUT_CONTEXT_SIZE).is_none() { - bail!( + ictx.checked_add(INPUT_CONTEXT_SIZE).with_context(|| { + format!( "Input Context access overflow, addr {:x} size {:x}", - ictx, - INPUT_CONTEXT_SIZE - ); - } + ictx, INPUT_CONTEXT_SIZE + ) + })?; let ccode = self.check_input_ctx(ictx)?; if ccode != TRBCCode::Success { return Ok(ccode); @@ -897,13 +897,12 @@ impl XhciDevice { let ctx_addr = self.get_device_context_addr(slot_id)?; let mut octx = 0; dma_read_u64(&self.mem_space, GuestAddress(ctx_addr), &mut octx)?; - if octx.checked_add(DEVICE_CONTEXT_SIZE).is_none() { - bail!( + octx.checked_add(DEVICE_CONTEXT_SIZE).with_context(|| { + format!( "Device Context access overflow, addr {:x} size {:x}", - octx, - DEVICE_CONTEXT_SIZE - ); - } + octx, DEVICE_CONTEXT_SIZE + ) + })?; self.slots[(slot_id - 1) as usize].usb_port = Some(usb_port.clone()); self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; dev.lock().unwrap().reset(); @@ -999,13 +998,12 @@ impl XhciDevice { } fn config_slot_ep(&mut self, slot_id: u32, ictx: u64) -> Result { - if ictx.checked_add(INPUT_CONTEXT_SIZE).is_none() { - bail!( + ictx.checked_add(INPUT_CONTEXT_SIZE).with_context(|| { + format!( "Input Context access overflow, addr {:x} size {:x}", - ictx, - INPUT_CONTEXT_SIZE - ); - } + ictx, INPUT_CONTEXT_SIZE + ) + })?; let mut ictl_ctx = XhciInputCtrlCtx::default(); dma_read_u32( &self.mem_space, @@ -1058,13 +1056,12 @@ impl XhciDevice { return Ok(TRBCCode::ContextStateError); } let ictx = trb.parameter; - if ictx.checked_add(INPUT_CONTEXT_SIZE).is_none() { - bail!( + ictx.checked_add(INPUT_CONTEXT_SIZE).with_context(|| { + format!( "Input Context access overflow, addr {:x} size {:x}", - ictx, - INPUT_CONTEXT_SIZE - ); - } + ictx, INPUT_CONTEXT_SIZE + ) + })?; let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; let mut ictl_ctx = XhciInputCtrlCtx::default(); dma_read_u32( @@ -1553,17 +1550,15 @@ impl XhciDevice { } fn get_usb_dev(&self, slotid: u32, epid: u32) -> Result>> { - let port = if let Some(port) = &self.slots[(slotid - 1) as usize].usb_port { - port - } else { - bail!("USB port not found slotid {} epid {}", slotid, epid); - }; + let port = self.slots[(slotid - 1) as usize] + .usb_port + .as_ref() + .with_context(|| format!("USB port not found slotid {} epid {}", slotid, epid))?; let locked_port = port.lock().unwrap(); - let dev = if let Some(dev) = locked_port.dev.as_ref() { - dev - } else { - bail!("No device found in USB port."); - }; + let dev = locked_port + .dev + .as_ref() + .with_context(|| "No device found in USB port.")?; Ok(dev.clone()) } @@ -1625,11 +1620,9 @@ impl XhciDevice { xfer.status = TRBCCode::ShortPacket; } left -= chunk; - if let Some(v) = edtla.checked_add(chunk) { - edtla = v; - } else { - bail!("Event Data Transfer Length Accumulator overflow, edtla {:x} offset {:x}", edtla, chunk); - } + edtla = edtla.checked_add(chunk).with_context(|| + format!("Event Data Transfer Length Accumulator overflow, edtla {:x} offset {:x}", edtla, chunk) + )?; } TRBType::TrStatus => {} _ => { diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 45389ded5..f04a8911d 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -144,11 +144,9 @@ impl XhciPciDevice { pub fn attach_device(&self, dev: &Arc>) -> Result<()> { let mut locked_xhci = self.xhci.lock().unwrap(); - let usb_port = if let Some(usb_port) = locked_xhci.assign_usb_port(dev) { - usb_port - } else { - bail!("No available USB port."); - }; + let usb_port = locked_xhci + .assign_usb_port(dev) + .with_context(|| "No available USB port.")?; locked_xhci.port_update(&usb_port)?; let mut locked_dev = dev.lock().unwrap(); debug!( diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f6beb41b3..df7f429ac 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -374,11 +374,10 @@ pub trait MachineOps { &device_cfg.id, ); } else { - let virtio_serial_info = if let Some(serial_info) = &vm_config.virtio_serial { - serial_info - } else { - bail!("No virtio-serial-pci device configured for virtconsole"); - }; + let virtio_serial_info = vm_config + .virtio_serial + .as_ref() + .with_context(|| "No virtio-serial-pci device configured for virtconsole")?; // Reasonable, because for virtio-serial-pci device, the bdf has been checked. let bdf = virtio_serial_info.pci_bdf.clone().unwrap(); let multi_func = virtio_serial_info.multifunction; @@ -934,22 +933,18 @@ pub trait MachineOps { fn reset_bus(&mut self, dev_id: &str) -> Result<()> { let pci_host = self.get_pci_host()?; let locked_pci_host = pci_host.lock().unwrap(); - let bus = - if let Some((bus, _)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, dev_id) { - bus - } else { - bail!("Bus not found, dev id {}", dev_id); - }; + let bus = PciBus::find_attached_bus(&locked_pci_host.root_bus, dev_id) + .with_context(|| format!("Bus not found, dev id {}", dev_id))? + .0; let locked_bus = bus.lock().unwrap(); if locked_bus.name == "pcie.0" { // No need to reset root bus return Ok(()); } - let parent_bridge = if let Some(bridge) = locked_bus.parent_bridge.as_ref() { - bridge - } else { - bail!("Parent bridge does not exist, dev id {}", dev_id); - }; + let parent_bridge = locked_bus + .parent_bridge + .as_ref() + .with_context(|| format!("Parent bridge does not exist, dev id {}", dev_id))?; let dev = parent_bridge.upgrade().unwrap(); let locked_dev = dev.lock().unwrap(); let name = locked_dev.name(); @@ -1013,15 +1008,17 @@ pub trait MachineOps { ..Default::default() }; - if let Some(mem_cfg) = vm_config.object.mem_object.remove(&numa_config.mem_dev) - { - numa_node.size = mem_cfg.size; - } else { - bail!( - "Object for memory-backend-ram {} config not found", - numa_config.mem_dev - ); - } + numa_node.size = vm_config + .object + .mem_object + .remove(&numa_config.mem_dev) + .map(|mem_conf| mem_conf.size) + .with_context(|| { + format!( + "Object for memory-backend-ram {} config not found", + numa_config.mem_dev + ) + })?; numa_nodes.insert(numa_config.numa_id, numa_node); } "dist" => { @@ -1162,18 +1159,15 @@ pub trait MachineOps { vm_config: &mut VmConfig, usb_dev: Arc>, ) -> Result<()> { - let parent_dev_op = self.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci"); - if parent_dev_op.is_none() { - bail!("Can not find parent device from pci bus"); - } - let parent_dev = parent_dev_op.unwrap(); + let parent_dev = self + .get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci") + .with_context(|| "Can not find parent device from pci bus")?; let locked_parent_dev = parent_dev.lock().unwrap(); - let xhci_pci = locked_parent_dev.as_any().downcast_ref::(); - if xhci_pci.is_none() { - bail!("PciDevOps can not downcast to XhciPciDevice"); - } - - xhci_pci.unwrap().attach_device(&(usb_dev))?; + let xhci_pci = locked_parent_dev + .as_any() + .downcast_ref::() + .with_context(|| "PciDevOps can not downcast to XhciPciDevice")?; + xhci_pci.attach_device(&(usb_dev))?; Ok(()) } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 42a7c6c2d..afd464df9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1147,14 +1147,10 @@ impl DeviceInterface for LightMachine { fn blockdev_add(&self, args: Box) -> Response { let read_only = args.read_only.unwrap_or(false); - let direct = if let Some(cache) = args.cache { - match cache.direct { - Some(direct) => direct, - _ => true, - } - } else { - true - }; + let mut direct = true; + if args.cache.is_some() && !args.cache.unwrap().direct.unwrap_or(true) { + direct = false; + } let config = BlkDevConfig { id: args.node_name.clone(), diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index d0f3038b0..84c626ed3 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -608,14 +608,13 @@ impl MachineOps for StdMachine { #[cfg(not(target_env = "musl"))] fn add_ramfb(&mut self) -> Result<()> { - let fwcfg_dev = self.get_fwcfg_dev(); - if fwcfg_dev.is_none() { - bail!("Ramfb device must be used UEFI to boot, please add pflash devices"); - } - + let fwcfg_dev = self + .get_fwcfg_dev() + .with_context(|| "Ramfb device must be used UEFI to boot, please add pflash devices")?; let sys_mem = self.get_sys_mem(); let mut ramfb = Ramfb::new(sys_mem.clone()); - ramfb.ramfb_state.setup(&fwcfg_dev.unwrap())?; + + ramfb.ramfb_state.setup(&fwcfg_dev)?; ramfb.realize(&mut self.sysbus)?; Ok(()) } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 1870fdd16..6fdbd638b 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -743,11 +743,7 @@ impl StdMachine { args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { let multifunction = args.multifunction.unwrap_or(false); - let drive = if let Some(drv) = &args.drive { - drv - } else { - bail!("Drive not set"); - }; + let drive = args.drive.as_ref().with_context(|| "Drive not set")?; let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); @@ -841,11 +837,7 @@ impl StdMachine { let multifunction = args.multifunction.unwrap_or(false); let vm_config = self.get_vm_config(); let locked_vmconfig = vm_config.lock().unwrap(); - let chardev = if let Some(dev) = &args.chardev { - dev - } else { - bail!("Chardev not set"); - }; + let chardev = args.chardev.as_ref().with_context(|| "Chardev not set")?; let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let socket_path = self .get_socket_path(&locked_vmconfig, chardev.to_string()) @@ -874,11 +866,10 @@ impl StdMachine { } fn get_socket_path(&self, vm_config: &VmConfig, chardev: String) -> Result> { - let char_dev = if let Some(char_dev) = vm_config.chardev.get(&chardev) { - char_dev - } else { - bail!("Chardev: {:?} not found for character device", &chardev); - }; + let char_dev = vm_config + .chardev + .get(&chardev) + .with_context(|| format!("Chardev: {:?} not found for character device", &chardev))?; let socket_path = match &char_dev.backend { ChardevType::Socket { @@ -908,11 +899,7 @@ impl StdMachine { args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { let multifunction = args.multifunction.unwrap_or(false); - let netdev = if let Some(dev) = &args.netdev { - dev - } else { - bail!("Netdev not set"); - }; + let netdev = args.netdev.as_ref().with_context(|| "Netdev not set")?; let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); @@ -971,36 +958,19 @@ impl StdMachine { bdf: &PciBdf, args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { - let host = if args.host.is_none() { - "" - } else { - args.host.as_ref().unwrap() - }; - - let sysfsdev = if args.sysfsdev.is_none() { - "" - } else { - args.sysfsdev.as_ref().unwrap() - }; - if args.host.is_none() && args.sysfsdev.is_none() { bail!("Neither option \"host\" nor \"sysfsdev\" was not provided."); } - if args.host.is_some() && args.sysfsdev.is_some() { bail!("Both option \"host\" and \"sysfsdev\" was provided."); } - if let Err(e) = self.create_vfio_pci_device( - &args.id, - bdf, - host, - sysfsdev, - args.multifunction.map_or(false, |m| m), - ) { - error!("{:?}", e); - bail!("Failed to plug vfio-pci device."); - } + let host = args.host.as_ref().map_or("", String::as_str); + let sysfsdev = args.sysfsdev.as_ref().map_or("", String::as_str); + let multifunc = args.multifunction.unwrap_or(false); + self.create_vfio_pci_device(&args.id, bdf, host, sysfsdev, multifunc) + .with_context(|| "Failed to plug vfio-pci device.")?; + Ok(()) } } @@ -1170,6 +1140,7 @@ impl DeviceInterface for StdMachine { } "vfio-pci" => { if let Err(e) = self.plug_vfio_pci_device(&pci_bdf, args.as_ref()) { + error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/machine/src/vm_state.rs b/machine/src/vm_state.rs index 493141064..114c08066 100644 --- a/machine/src/vm_state.rs +++ b/machine/src/vm_state.rs @@ -78,11 +78,7 @@ impl StateTransfer for KvmDevice { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&KvmDeviceState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&KvmDeviceState::descriptor().name).unwrap_or(!0) } } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 0fa9ca6d4..99d1521af 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -548,14 +548,11 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< if let Some(mon_config) = args.value_of("mon") { let mut cmd_parser = CmdParser::new("monitor"); cmd_parser.push("id").push("mode").push("chardev"); - cmd_parser.parse(&mon_config)?; - let chardev = if let Some(dev) = cmd_parser.get_value::("chardev")? { - dev - } else { - bail!("Argument \'chardev\' is missing for \'mon\'"); - }; + let chardev = cmd_parser + .get_value::("chardev")? + .with_context(|| "Argument \'chardev\' is missing for \'mon\'")?; if let Some(mode) = cmd_parser.get_value::("mode")? { if mode != *"control" { diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 7edc1b197..4395f4d77 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -109,14 +109,9 @@ fn check_chardev_args(cmd_parser: CmdParser) -> Result<()> { } pub fn parse_chardev(cmd_parser: CmdParser) -> Result { - let chardev_id = if let Some(chardev_id) = cmd_parser.get_value::("id")? { - chardev_id - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "chardev".to_string() - ))); - }; + let chardev_id = cmd_parser + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "chardev".to_string()))?; let backend = cmd_parser.get_value::("")?; let path = cmd_parser.get_value::("path")?; let server = if let Some(server) = cmd_parser.get_value::("server")? { @@ -262,23 +257,15 @@ pub fn parse_virtconsole(vm_config: &mut VmConfig, config_args: &str) -> Result< cmd_parser.push("").push("id").push("chardev"); cmd_parser.parse(config_args)?; - let chardev_name = if let Some(chardev) = cmd_parser.get_value::("chardev")? { - chardev - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "chardev".to_string(), - "virtconsole".to_string() - ))); - }; + let chardev_name = cmd_parser + .get_value::("chardev")? + .with_context(|| { + ConfigError::FieldIsMissing("chardev".to_string(), "virtconsole".to_string()) + })?; - let id = if let Some(chardev_id) = cmd_parser.get_value::("id")? { - chardev_id - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "virtconsole".to_string() - ))); - }; + let id = cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "virtconsole".to_string()) + })?; if let Some(char_dev) = vm_config.chardev.remove(&chardev_name) { return Ok(VirtioConsole { @@ -420,23 +407,13 @@ pub fn parse_vsock(vsock_config: &str) -> Result { .push("vhostfd"); cmd_parser.parse(vsock_config)?; pci_args_check(&cmd_parser)?; - let id = if let Some(vsock_id) = cmd_parser.get_value::("id")? { - vsock_id - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "vsock".to_string() - ))); - }; + let id = cmd_parser + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "vsock".to_string()))?; - let guest_cid = if let Some(cid) = cmd_parser.get_value::("guest-cid")? { - cid - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "guest-cid".to_string(), - "vsock".to_string() - ))); - }; + let guest_cid = cmd_parser.get_value::("guest-cid")?.with_context(|| { + ConfigError::FieldIsMissing("guest-cid".to_string(), "vsock".to_string()) + })?; let vhost_fd = cmd_parser.get_value::("vhostfd")?; let vsock = VsockConfig { @@ -472,16 +449,12 @@ pub fn parse_virtio_serial(vm_config: &mut VmConfig, serial_config: &str) -> Res pci_args_check(&cmd_parser)?; if vm_config.virtio_serial.is_none() { - let id = if let Some(id) = cmd_parser.get_value::("id")? { - id - } else { - "".to_string() - }; - let multifunction = if let Some(switch) = cmd_parser.get_value::("multifunction")? { - switch.into() - } else { - false - }; + let id = cmd_parser + .get_value::("id")? + .unwrap_or_else(|| "".to_string()); + let multifunction = cmd_parser + .get_value::("multifunction")? + .map_or(false, |switch| switch.into()); let virtio_serial = if serial_config.contains("-pci") { let pci_bdf = get_pci_bdf(serial_config)?; VirtioSerialInfo { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index ff2e2eb70..e25803ca8 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -14,7 +14,7 @@ use std::fs::{metadata, File}; use std::os::linux::fs::MetadataExt; use std::path::Path; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use log::error; use serde::{Deserialize, Serialize}; @@ -285,23 +285,12 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { } } - if let Some(id) = cmd_parser.get_value::("id")? { - drive.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "blk".to_string() - ))); - } - - if let Some(file) = cmd_parser.get_value::("file")? { - drive.path_on_host = file; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "file".to_string(), - "blk".to_string() - ))); - } + drive.id = cmd_parser + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "blk".to_string()))?; + drive.path_on_host = cmd_parser + .get_value::("file")? + .with_context(|| ConfigError::FieldIsMissing("file".to_string(), "blk".to_string()))?; if let Some(read_only) = cmd_parser.get_value::("readonly")? { drive.read_only = read_only.into(); @@ -361,14 +350,9 @@ pub fn parse_blk( blkdevcfg.boot_index = Some(boot_index); } - let blkdrive = if let Some(drive) = cmd_parser.get_value::("drive")? { - drive - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "drive".to_string(), - "blk".to_string() - ))); - }; + let blkdrive = cmd_parser + .get_value::("drive")? + .with_context(|| ConfigError::FieldIsMissing("drive".to_string(), "blk".to_string()))?; if let Some(iothread) = cmd_parser.get_value::("iothread")? { blkdevcfg.iothread = Some(iothread); @@ -378,11 +362,9 @@ pub fn parse_blk( blkdevcfg.serial_num = Some(serial); } - if let Some(id) = cmd_parser.get_value::("id")? { - blkdevcfg.id = id; - } else { - bail!("No id configured for blk device"); - } + blkdevcfg.id = cmd_parser + .get_value::("id")? + .with_context(|| "No id configured for blk device")?; if let Some(queues) = cmd_parser.get_value::("num-queues")? { blkdevcfg.queues = queues; @@ -435,20 +417,16 @@ pub fn parse_vhost_user_blk_pci( blkdevcfg.boot_index = Some(boot_index); } - if let Some(chardev) = cmd_parser.get_value::("chardev")? { - blkdevcfg.chardev = Some(chardev); - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "chardev".to_string(), - "vhost-user-blk-pci".to_string() - ))); - }; - - if let Some(id) = cmd_parser.get_value::("id")? { - blkdevcfg.id = id; - } else { - bail!("No id configured for blk device"); - } + blkdevcfg.chardev = cmd_parser + .get_value::("chardev")? + .map(Some) + .with_context(|| { + ConfigError::FieldIsMissing("chardev".to_string(), "vhost-user-blk-pci".to_string()) + })?; + + blkdevcfg.id = cmd_parser + .get_value::("id")? + .with_context(|| "No id configured for blk device")?; if let Some(queues) = cmd_parser.get_value::("num-queues")? { blkdevcfg.queues = queues; @@ -504,11 +482,9 @@ impl VmConfig { cmd_parser.push("if"); cmd_parser.get_parameters(drive_config)?; - let drive_type = if let Some(_type) = cmd_parser.get_value::("if")? { - _type - } else { - "none".to_string() - }; + let drive_type = cmd_parser + .get_value::("if")? + .unwrap_or_else(|| "none".to_string()); match drive_type.as_str() { "none" => { self.add_block_drive(drive_config)?; @@ -658,27 +634,17 @@ impl VmConfig { bail!("Only \'raw\' type of pflash is supported"); } } - if let Some(drive_path) = cmd_parser.get_value::("file")? { - pflash.path_on_host = drive_path; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "file".to_string(), - "pflash".to_string() - ))); - } + pflash.path_on_host = cmd_parser.get_value::("file")?.with_context(|| { + ConfigError::FieldIsMissing("file".to_string(), "pflash".to_string()) + })?; if let Some(read_only) = cmd_parser.get_value::("readonly")? { pflash.read_only = read_only.into(); } - if let Some(unit_id) = cmd_parser.get_value::("unit")? { - pflash.unit = unit_id as usize; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "unit".to_string(), - "pflash".to_string() - ))); - } + pflash.unit = cmd_parser.get_value::("unit")?.with_context(|| { + ConfigError::FieldIsMissing("unit".to_string(), "pflash".to_string()) + })? as usize; pflash.check()?; self.add_flashdev(pflash) diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index 21487e6d1..5b1d6f610 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -15,7 +15,7 @@ use crate::config::{ pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_SOCK_PATH_LENGTH, MAX_STRING_LENGTH, MAX_TAG_LENGTH, }; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; /// Config struct for `fs`. /// Contains fs device's attr. @@ -78,24 +78,16 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { .push("multifunction"); cmd_parser.parse(fs_config)?; pci_args_check(&cmd_parser)?; - let mut fs_cfg = FsConfig::default(); - if let Some(tag) = cmd_parser.get_value::("tag")? { - fs_cfg.tag = tag; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "tag".to_string(), - "virtio-fs".to_string() - ))); - } - if let Some(id) = cmd_parser.get_value::("id")? { - fs_cfg.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "virtio-fs".to_string() - ))); - } + let mut fs_cfg = FsConfig { + tag: cmd_parser.get_value::("tag")?.with_context(|| { + ConfigError::FieldIsMissing("tag".to_string(), "virtio-fs".to_string()) + })?, + id: cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "virtio-fs".to_string()) + })?, + ..Default::default() + }; if let Some(name) = cmd_parser.get_value::("chardev")? { if let Some(char_dev) = vm_config.chardev.remove(&name) { diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 3a50bee96..f9dff4d5b 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -645,11 +645,7 @@ fn memory_unit_conversion(origin_value: &str) -> Result { } fn get_inner(outer: Option) -> Result { - if let Some(x) = outer { - Ok(x) - } else { - Err(anyhow!(ConfigError::IntegerOverflow("-m".to_string()))) - } + outer.with_context(|| ConfigError::IntegerOverflow("-m".to_string())) } #[cfg(test)] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index d4c755ff5..7b500e769 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -181,11 +181,9 @@ impl VmConfig { cmd_params.push(""); cmd_params.get_parameters(object_args)?; - let obj_type = cmd_params.get_value::("")?; - if obj_type.is_none() { - bail!("Object type not specified"); - } - let device_type = obj_type.unwrap(); + let device_type = cmd_params + .get_value::("")? + .with_context(|| "Object type not specified")?; match device_type.as_str() { "iothread" => { self.add_iothread(object_args) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 3396c8108..2a47deac5 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -174,22 +174,15 @@ fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result>> { fn parse_netdev(cmd_parser: CmdParser) -> Result { let mut net = NetDevcfg::default(); - let netdev_type = if let Some(netdev_type) = cmd_parser.get_value::("")? { - netdev_type - } else { - "".to_string() - }; + let netdev_type = cmd_parser + .get_value::("")? + .unwrap_or_else(|| "".to_string()); if netdev_type.ne("tap") && netdev_type.ne("vhost-user") { bail!("Unsupported netdev type: {:?}", &netdev_type); } - if let Some(net_id) = cmd_parser.get_value::("id")? { - net.id = net_id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "netdev".to_string() - ))); - } + net.id = cmd_parser + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "netdev".to_string()))?; if let Some(ifname) = cmd_parser.get_value::("ifname")? { net.ifname = ifname; } @@ -280,19 +273,12 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result("netdev")? { - devname - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "netdev".to_string(), - "net".to_string() - ))); - }; - let netid = if let Some(id) = cmd_parser.get_value::("id")? { - id - } else { - "".to_string() - }; + let netdev = cmd_parser + .get_value::("netdev")? + .with_context(|| ConfigError::FieldIsMissing("netdev".to_string(), "net".to_string()))?; + let netid = cmd_parser + .get_value::("id")? + .unwrap_or_else(|| "".to_string()); if let Some(mq) = cmd_parser.get_value::("mq")? { netdevinterfacecfg.mq = mq.inner; diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 1994c4831..1134c5ce1 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -153,14 +153,9 @@ pub fn parse_numa_mem(numa_config: &str) -> Result { "numa".to_string() ))); } - if let Some(mem_dev) = cmd_parser.get_value::("memdev")? { - config.mem_dev = mem_dev; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "memdev".to_string(), - "numa".to_string() - ))); - } + config.mem_dev = cmd_parser + .get_value::("memdev")? + .with_context(|| ConfigError::FieldIsMissing("memdev".to_string(), "numa".to_string()))?; Ok(config) } diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 8a2258c38..11a01b698 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; @@ -102,12 +102,12 @@ pub fn get_pci_bdf(pci_cfg: &str) -> Result { cmd_parser.push("").push("bus").push("addr"); cmd_parser.get_parameters(pci_cfg)?; - let mut pci_bdf = PciBdf::default(); - if let Some(bus) = cmd_parser.get_value::("bus")? { - pci_bdf.bus = bus; - } else { - bail!("Bus not specified for pci device"); - } + let mut pci_bdf = PciBdf { + bus: cmd_parser + .get_value::("bus")? + .with_context(|| "Bus not specified for pci device")?, + ..Default::default() + }; if let Some(addr) = cmd_parser.get_value::("addr")? { pci_bdf.addr = get_pci_df(&addr).with_context(|| "Failed to get addr")?; } else { @@ -144,34 +144,23 @@ pub fn parse_root_port(rootport_cfg: &str) -> Result { cmd_parser.parse(rootport_cfg)?; let mut root_port = RootPortConfig::default(); - let port = cmd_parser.get_value::("port")?; - if port.is_none() { - return Err(anyhow!(ConfigError::FieldIsMissing( - "port".to_string(), - "rootport".to_string() - ))); - } - // Safety: as port is validated non-none at the previous line, it's safe to unwrap() it - root_port.port = str_to_usize(port.unwrap())? as u8; + + let port = cmd_parser + .get_value::("port")? + .with_context(|| ConfigError::FieldIsMissing("port".to_string(), "rootport".to_string()))?; + root_port.port = str_to_usize(port)? as u8; let _ = cmd_parser.get_value::("chassis")?; - if let Some(id) = cmd_parser.get_value::("id")? { - root_port.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "rootport".to_string() - ))); - } - root_port.multifunction = - if let Some(multi_func) = cmd_parser.get_value::("multifunction")? { - multi_func.into() - } else { - false - }; - root_port.check()?; + root_port.id = cmd_parser + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "rootport".to_string()))?; + root_port.multifunction = cmd_parser + .get_value::("multifunction")? + .map_or(false, bool::from); + + root_port.check()?; Ok(root_port) } diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index a42b3af9c..792123ff3 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; @@ -81,33 +81,22 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result("rng")? { - rng_id - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "rng".to_string(), - "rng".to_string() - ))); - }; + let rng = cmd_parser + .get_value::("rng")? + .with_context(|| ConfigError::FieldIsMissing("rng".to_string(), "rng".to_string()))?; - rng_cfg.id = if let Some(rng_id) = cmd_parser.get_value::("id")? { - rng_id - } else { - "".to_string() - }; + rng_cfg.id = cmd_parser + .get_value::("id")? + .unwrap_or_else(|| "".to_string()); if let Some(max) = cmd_parser.get_value::("max-bytes")? { if let Some(peri) = cmd_parser.get_value::("period")? { - let mul = if let Some(res) = max.checked_mul(1000) { - res - } else { - bail!("Illegal max-bytes arguments: {:?}", max) - }; - let div = if let Some(res) = mul.checked_div(peri) { - res - } else { - bail!("Illegal period arguments: {:?}", peri) - }; + let mul = max + .checked_mul(1000) + .with_context(|| format!("Illegal max-bytes arguments: {:?}", max))?; + let div = mul + .checked_div(peri) + .with_context(|| format!("Illegal period arguments: {:?}", peri))?; rng_cfg.bytes_per_sec = Some(div); } else { bail!("Argument 'period' is missing"); @@ -116,11 +105,12 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result Result { cmd_params.push("").push("id").push("filename"); cmd_params.parse(object_args)?; - let id = if let Some(obj_id) = cmd_params.get_value::("id")? { - obj_id - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "rng-object".to_string() - ))); - }; - let filename = if let Some(name) = cmd_params.get_value::("filename")? { - name - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "filename".to_string(), - "rng-object".to_string() - ))); - }; + let id = cmd_params + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "rng-object".to_string()))?; + let filename = cmd_params + .get_value::("filename")? + .with_context(|| { + ConfigError::FieldIsMissing("filename".to_string(), "rng-object".to_string()) + })?; let rng_obj_cfg = RngObjConfig { id, filename }; Ok(rng_obj_cfg) diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs index 7e74bee9e..c51a19ab1 100644 --- a/machine_manager/src/config/sasl_auth.rs +++ b/machine_manager/src/config/sasl_auth.rs @@ -13,7 +13,7 @@ use crate::config::{ ConfigError, {CmdParser, VmConfig}, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -30,15 +30,12 @@ impl VmConfig { cmd_parser.push("").push("id").push("identity"); cmd_parser.parse(saslauth_config)?; - let mut saslauth = SaslAuthObjConfig::default(); - if let Some(id) = cmd_parser.get_value::("id")? { - saslauth.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "vnc sasl_auth".to_string() - ))); - } + let mut saslauth = SaslAuthObjConfig { + id: cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "vnc sasl_auth".to_string()) + })?, + ..Default::default() + }; if let Some(identity) = cmd_parser.get_value::("identity")? { saslauth.identity = identity; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 908522d85..cc72819d4 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use super::{error::ConfigError, pci_args_check}; use crate::config::{ @@ -122,14 +122,9 @@ pub fn parse_scsi_controller( cntlr_cfg.iothread = Some(iothread); } - if let Some(id) = cmd_parser.get_value::("id")? { - cntlr_cfg.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "virtio scsi pci".to_string() - ))); - } + cntlr_cfg.id = cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "virtio scsi pci".to_string()) + })?; if let Some(queues) = cmd_parser.get_value::("num-queues")? { cntlr_cfg.queues = queues; @@ -203,14 +198,9 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result let mut scsi_dev_cfg = ScsiDevConfig::default(); - let scsi_drive = if let Some(drive) = cmd_parser.get_value::("drive")? { - drive - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "drive".to_string(), - "scsi device".to_string() - ))); - }; + let scsi_drive = cmd_parser.get_value::("drive")?.with_context(|| { + ConfigError::FieldIsMissing("drive".to_string(), "scsi device".to_string()) + })?; if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { scsi_dev_cfg.boot_index = Some(boot_index); @@ -220,14 +210,9 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result scsi_dev_cfg.serial = Some(serial); } - if let Some(id) = cmd_parser.get_value::("id")? { - scsi_dev_cfg.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "scsi device".to_string() - ))); - } + scsi_dev_cfg.id = cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "scsi device".to_string()) + })?; if let Some(bus) = cmd_parser.get_value::("bus")? { // Format "$parent_cntlr_name.0" is required by scsi bus. diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index 8d31ad4c3..b7ed7f2a4 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -13,7 +13,7 @@ use crate::config::{ ConfigError, {CmdParser, VmConfig}, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; use std::path::Path; @@ -36,15 +36,13 @@ impl VmConfig { .push("endpoint") .push("verify-peer"); cmd_parser.parse(tlscred_config)?; - let mut tlscred = TlsCredObjConfig::default(); - if let Some(id) = cmd_parser.get_value::("id")? { - tlscred.id = id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "vnc tls_creds".to_string() - ))); - } + + let mut tlscred = TlsCredObjConfig { + id: cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "vnc tls_creds".to_string()) + })?, + ..Default::default() + }; if let Some(dir) = cmd_parser.get_value::("dir")? { if Path::new(&dir).is_dir() { diff --git a/migration_derive/src/field_parser.rs b/migration_derive/src/field_parser.rs index 9df5aeb8f..3f754cb8c 100644 --- a/migration_derive/src/field_parser.rs +++ b/migration_derive/src/field_parser.rs @@ -41,11 +41,8 @@ fn parse_field( // parse var of field let var_ident = input.value().ident.as_ref().unwrap(); let var_name = var_ident.to_string(); - let alias_name = if let Some(alias) = parse_field_attributes(&input.value().attrs) { - alias - } else { - var_name.clone() - }; + let alias_name = + parse_field_attributes(&input.value().attrs).unwrap_or_else(|| var_name.clone()); // parse type of field let ty = input.value().ty.clone(); diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 50cb8b42d..92ddf70cf 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -163,11 +163,7 @@ impl OzoneHandler { /// /// * `file_path` - args parser. fn bind_mount_file(&self, file_path: &Path) -> Result<()> { - let file_name = if let Some(file) = file_path.file_name() { - file - } else { - bail!("Empty file path"); - }; + let file_name = file_path.file_name().with_context(|| "Empty file path")?; let mut new_root_dir = self.chroot_dir.clone(); new_root_dir.push(file_name); if file_path.is_dir() { @@ -300,11 +296,7 @@ impl OzoneHandler { for source_file_path in self.source_file_paths.clone().into_iter() { let mut chroot_path = self.chroot_dir.clone(); let source_file_name = source_file_path.file_name(); - let file_name = if let Some(file_name) = source_file_name { - file_name - } else { - bail!("Source file is empty") - }; + let file_name = source_file_name.with_context(|| "Source file is empty")?; chroot_path.push(file_name); if chroot_path.exists() { diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index cb536a774..5c2086aca 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex}; use crate::{PciBus, PciDevOps}; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; pub trait HotplugOps: Send { /// Plug device, usually called when hot plug device in device_add. @@ -68,14 +68,16 @@ pub fn handle_unplug_request( dev: &Arc>, ) -> Result<()> { let locked_bus = bus.lock().unwrap(); - let hpc = if let Some(hpc) = locked_bus.hotplug_controller.as_ref() { - hpc.clone() - } else { - bail!( - "No hot plug controller found for bus {} when unplug request", - locked_bus.name - ); - }; + let hpc = locked_bus + .hotplug_controller + .as_ref() + .cloned() + .with_context(|| { + format!( + "No hot plug controller found for bus {} when unplug request", + locked_bus.name + ) + })?; // No need to hold the lock. drop(locked_bus); hpc.upgrade().unwrap().lock().unwrap().unplug_request(dev) diff --git a/pci/src/msix.rs b/pci/src/msix.rs index bb40397e8..de010db28 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -519,11 +519,7 @@ impl StateTransfer for Msix { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&MsixState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&MsixState::descriptor().name).unwrap_or(!0) } } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index dbac18cce..eb0e0a7ba 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -604,11 +604,7 @@ impl StateTransfer for RootPort { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&RootPortState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&RootPortState::descriptor().name).unwrap_or(!0) } } diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 084c78504..e3b808ae5 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -399,11 +399,9 @@ impl FuseBuffer { bail!("{} out of bufs's index", index); } - let buf = if let Some(b) = bufs.get_mut(index) { - b - } else { - bail!("{} out of bufs's bound", index); - }; + let buf = bufs + .get_mut(index) + .with_context(|| format!("{} out of bufs's bound", index))?; let len = if remain_len < buf.len { remain_len @@ -411,11 +409,9 @@ impl FuseBuffer { buf.len }; - let hva = if let Some(hva) = sys_mem.get_host_address(buf.addr) { - hva - } else { - bail!("read file error: get hva failed."); - }; + let hva = sys_mem + .get_host_address(buf.addr) + .with_context(|| "read file error: get hva failed.")?; let iov = vec![libc::iovec { iov_base: hva as *mut libc::c_void, diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 5878ca377..eda6e0007 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -598,12 +598,12 @@ impl BalloonIoHandler { } fn reporting_evt_handler(&mut self) -> Result<()> { - let queue = &self.report_queue; - if queue.is_none() { - return Err(anyhow!(VirtioError::VirtQueueIsNone)); - } - let unwraped_queue = queue.as_ref().unwrap(); - let mut locked_queue = unwraped_queue.lock().unwrap(); + let queue = self + .report_queue + .as_ref() + .with_context(|| VirtioError::VirtQueueIsNone)?; + let mut locked_queue = queue.lock().unwrap(); + loop { let elem = locked_queue .vring @@ -632,13 +632,11 @@ impl BalloonIoHandler { } fn auto_msg_evt_handler(&mut self) -> Result<()> { - let queue = &self.msg_queue; - if queue.is_none() { - return Err(anyhow!(VirtioError::VirtQueueIsNone)); - } - - let unwraped_queue = queue.as_ref().unwrap(); - let mut locked_queue = unwraped_queue.lock().unwrap(); + let queue = self + .msg_queue + .as_ref() + .with_context(|| VirtioError::VirtQueueIsNone)?; + let mut locked_queue = queue.lock().unwrap(); loop { let elem = locked_queue diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index a7c21972a..8033e6f25 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1339,11 +1339,7 @@ impl StateTransfer for Block { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&BlockState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&BlockState::descriptor().name).unwrap_or(!0) } } diff --git a/virtio/src/device/console.rs b/virtio/src/device/console.rs index d552294f6..fd9e36374 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/console.rs @@ -393,13 +393,7 @@ impl StateTransfer for Console { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = - MigrationManager::get_desc_alias(&VirtioConsoleState::descriptor().name) - { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&VirtioConsoleState::descriptor().name).unwrap_or(!0) } } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index ab3fbb6b4..daea4fd15 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1693,11 +1693,7 @@ impl StateTransfer for Net { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&VirtioNetState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&VirtioNetState::descriptor().name).unwrap_or(!0) } } diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 06891b2fb..d4b2823fd 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -371,11 +371,7 @@ impl StateTransfer for Rng { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&RngState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&RngState::descriptor().name).unwrap_or(!0) } } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index c4007a23c..4ed04039c 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -699,11 +699,7 @@ impl StateTransfer for VirtioMmioDevice { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&VirtioMmioState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&VirtioMmioState::descriptor().name).unwrap_or(!0) } } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 0d816936f..98d43ca92 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1375,11 +1375,7 @@ impl StateTransfer for VirtioPciDevice { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&VirtioPciState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&VirtioPciState::descriptor().name).unwrap_or(!0) } } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index c5de9fd56..3b7054f1e 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -365,11 +365,7 @@ impl StateTransfer for Vsock { } fn get_device_alias(&self) -> u64 { - if let Some(alias) = MigrationManager::get_desc_alias(&VsockState::descriptor().name) { - alias - } else { - !0 - } + MigrationManager::get_desc_alias(&VsockState::descriptor().name).unwrap_or(!0) } } -- Gitee From f63becdb6fa7bfd49d419b219267fdd94dd359de Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 10 Apr 2023 10:29:53 +0800 Subject: [PATCH 0974/2187] Makefile: add pulseaudio yum-deps Scream device depends on the pulseaudio rpm package. Signed-off-by: Mingwang Li --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 178864390..2748cea1e 100644 --- a/Makefile +++ b/Makefile @@ -18,3 +18,4 @@ yum-deps: @yum install pixman-devel @yum install libcap-ng-devel @yum install cyrus-sasl-devel + @yum install pulseaudio -- Gitee From 9797d9c3162ab1b8088c23f67100b46217553d3f Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 11 Apr 2023 14:13:13 +0800 Subject: [PATCH 0975/2187] balloon: auto-balloon features modify to 16 Signed-off-by: jiewangqun --- virtio/src/device/balloon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index eda6e0007..c5b882afc 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -53,7 +53,7 @@ use crate::{ const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; const VIRTIO_BALLOON_F_REPORTING: u32 = 5; /// The feature for Auto-balloon -const VIRTIO_BALLOON_F_MESSAGE_VQ: u32 = 6; +const VIRTIO_BALLOON_F_MESSAGE_VQ: u32 = 16; const VIRTIO_BALLOON_PFN_SHIFT: u32 = 12; const QUEUE_NUM_BALLOON: usize = 2; const BALLOON_PAGE_SIZE: u64 = 1 << VIRTIO_BALLOON_PFN_SHIFT; -- Gitee From d005cb4cdc17aed1684237c17ffc27f28685a45b Mon Sep 17 00:00:00 2001 From: mayunlong Date: Mon, 10 Apr 2023 21:45:30 +0800 Subject: [PATCH 0976/2187] vhost-net: use irqfd in vhost-net to improve efficiency original TCP sender: 16.12Gbits/sec original TCP receive: 17.78Gbits/sec now TCP sender: 18.30Gbits/sec (13.5% up) now TCP receive: 18.44Gbits/sec (3.4% up) Signed-off-by: mayunlong --- machine/src/lib.rs | 2 +- machine/src/standard_vm/mod.rs | 4 +--- virtio/src/vhost/kernel/net.rs | 43 ++++++++++++++-------------------- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index df7f429ac..34f9e5b0f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -758,13 +758,13 @@ pub trait MachineOps { let device_cfg = parse_net(vm_config, cfg_args)?; let mut need_irqfd = false; let device: Arc> = if device_cfg.vhost_type.is_some() { + need_irqfd = true; if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { Arc::new(Mutex::new(VhostKern::Net::new( &device_cfg, self.get_sys_mem(), ))) } else { - need_irqfd = true; Arc::new(Mutex::new(VhostUser::Net::new( &device_cfg, self.get_sys_mem(), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6fdbd638b..94f5ab5fa 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -932,15 +932,13 @@ impl StdMachine { drop(locked_vmconfig); if dev.vhost_type.is_some() { - let mut need_irqfd = false; let net: Arc> = if dev.vhost_type == Some(String::from("vhost-kernel")) { Arc::new(Mutex::new(VhostKern::Net::new(&dev, self.get_sys_mem()))) } else { - need_irqfd = true; Arc::new(Mutex::new(VhostUser::Net::new(&dev, self.get_sys_mem()))) }; - self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, need_irqfd) + self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, true) .with_context(|| "Failed to add vhost-kernel/vhost-user net device")?; } else { let net_id = dev.id.clone(); diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 88dffd770..18da527dd 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -28,8 +28,8 @@ use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; +use super::super::VhostOps; +use super::{VhostBackend, VhostVringFile, VHOST_NET_SET_BACKEND}; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, @@ -92,6 +92,8 @@ pub struct Net { deactivate_evts: Vec, /// Device is broken or not. broken: Arc, + /// Save irqfd used for vhost-net. + call_events: Vec>, } impl Net { @@ -105,6 +107,7 @@ impl Net { mem_space: mem_space.clone(), deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), + call_events: Vec::new(), } } } @@ -244,6 +247,14 @@ impl VirtioDevice for Net { Ok(()) } + fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { + for fd in queue_evts.iter() { + self.call_events.push(fd.clone()); + } + + Ok(()) + } + /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( @@ -279,7 +290,6 @@ impl VirtioDevice for Net { let queue_pairs = queue_num / 2; for index in 0..queue_pairs { - let mut host_notifies = Vec::new(); let backend = match &self.backends { None => return Err(anyhow!("Failed to get backend for vhost net")), Some(backends) => backends @@ -333,22 +343,14 @@ impl VirtioDevice for Net { drop(queue); - let host_notify = VhostNotify { - notify_evt: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| VirtioError::EventFdCreate)?, - ), - queue: queue_mutex.clone(), - }; backend - .set_vring_call(queue_index, host_notify.notify_evt.clone()) + .set_vring_call(queue_index, self.call_events[queue_index].clone()) .with_context(|| { format!( "Failed to set vring call for vhost net, index: {}", queue_index, ) })?; - host_notifies.push(host_notify); let tap = match &self.taps { None => bail!("Failed to get tap for vhost net"), @@ -363,19 +365,6 @@ impl VirtioDevice for Net { ) })?; } - - let handler = VhostIoHandler { - interrupt_cb: interrupt_cb.clone(), - host_notifies, - device_broken: self.broken.clone(), - }; - - let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper( - notifiers, - self.net_cfg.iothread.as_ref(), - &mut self.deactivate_evts, - )?; } self.broken.store(false, Ordering::SeqCst); @@ -383,7 +372,9 @@ impl VirtioDevice for Net { } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts) + unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + self.call_events.clear(); + Ok(()) } fn reset(&mut self) -> Result<()> { -- Gitee From cbb42eedab4b3f4ef41f78fcf2c8ccebf40f4812 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 12 Apr 2023 08:38:46 +0800 Subject: [PATCH 0977/2187] pci: Make ranges_overlap() easy to use Use range size instead of range end. Signed-off-by: Keqian Zhu --- machine/src/standard_vm/x86_64/ich9_lpc.rs | 9 +------ machine/src/standard_vm/x86_64/mch.rs | 3 +-- pci/src/config.rs | 29 +++++++------------- pci/src/lib.rs | 21 +++++++-------- pci/src/msix.rs | 21 +++++---------- pci/src/root_port.rs | 31 ++++++---------------- vfio/src/vfio_pci.rs | 17 +++++------- virtio/src/transport/virtio_pci.rs | 4 +-- 8 files changed, 45 insertions(+), 90 deletions(-) diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 706887d60..341f4fdef 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -281,15 +281,8 @@ impl PciDevOps for LPCBridge { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let end = offset + data.len(); - self.config.write(offset, data, 0, None, None); - if ranges_overlap( - offset, - end, - PM_BASE_OFFSET as usize, - PM_BASE_OFFSET as usize + 4, - ) { + if ranges_overlap(offset, data.len(), PM_BASE_OFFSET as usize, 4) { if let Err(e) = self.update_pm_base() { error!("Failed to update PM base addr: {:?}", e); } diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 03a43c31a..0b99f10ce 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -154,11 +154,10 @@ impl PciDevOps for Mch { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let end = offset + data.len(); let old_pciexbar: u64 = le_read_u64(&self.config.config, PCIEXBAR as usize).unwrap(); self.config.write(offset, data, 0, None, None); - if ranges_overlap(offset, end, PCIEXBAR as usize, PCIEXBAR as usize + 8) + if ranges_overlap(offset, data.len(), PCIEXBAR as usize, 8) && self.check_pciexbar_update(old_pciexbar) { if let Err(e) = self.update_pciexbar_mapping() { diff --git a/pci/src/config.rs b/pci/src/config.rs index cde5f043b..e269881d7 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -516,13 +516,10 @@ impl PciConfig { if let Err(err) = self.validate_config_boundary(offset, buf) { warn!("invalid read: {:?}", err); return; - }; - if ranges_overlap( - offset, - offset + buf.len(), - STATUS as usize, - (STATUS + 1) as usize, - ) { + } + + let size = buf.len(); + if ranges_overlap(offset, size, STATUS as usize, 1) { if let Some(intx) = &self.intx { if intx.lock().unwrap().level == 1 { self.config[STATUS as usize] |= STATUS_INTERRUPT; @@ -532,7 +529,6 @@ impl PciConfig { } } - let size = buf.len(); buf[..].copy_from_slice(&self.config[offset..(offset + size)]); } @@ -577,7 +573,6 @@ impl PciConfig { let cloned_data = data.to_vec(); let old_offset = offset; - let end = offset + data.len(); for data in &cloned_data { self.config[offset] = (self.config[offset] & (!self.write_mask[offset])) | (data & self.write_mask[offset]); @@ -586,19 +581,15 @@ impl PciConfig { } let (bar_num, rom_addr) = match self.config[HEADER_TYPE as usize] & HEADER_TYPE_BRIDGE { - HEADER_TYPE_BRIDGE => (BAR_NUM_MAX_FOR_BRIDGE, ROM_ADDRESS_BRIDGE), - _ => (BAR_NUM_MAX_FOR_ENDPOINT, ROM_ADDRESS_ENDPOINT), + HEADER_TYPE_BRIDGE => (BAR_NUM_MAX_FOR_BRIDGE as usize, ROM_ADDRESS_BRIDGE), + _ => (BAR_NUM_MAX_FOR_ENDPOINT as usize, ROM_ADDRESS_ENDPOINT), }; - let cmd_overlap = ranges_overlap(old_offset, end, COMMAND as usize, (COMMAND + 1) as usize); + let size = data.len(); + let cmd_overlap = ranges_overlap(old_offset, size, COMMAND as usize, 1); if cmd_overlap - || ranges_overlap( - old_offset, - end, - BAR_0 as usize, - BAR_0 as usize + REG_SIZE * bar_num as usize, - ) - || ranges_overlap(old_offset, end, rom_addr, rom_addr + 4) + || ranges_overlap(old_offset, size, BAR_0 as usize, REG_SIZE * bar_num) + || ranges_overlap(old_offset, size, rom_addr, 4) { if let Err(e) = self.update_bar_mapping( #[cfg(target_arch = "x86_64")] diff --git a/pci/src/lib.rs b/pci/src/lib.rs index cf510bcca..a5504d36a 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -322,11 +322,10 @@ pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { /// * `end` - End address of the first region. /// * `region_start` - Start address of the second region. /// * `region_end` - End address of the second region. -pub fn ranges_overlap(start: usize, end: usize, range_start: usize, range_end: usize) -> bool { - if start >= range_end || range_start >= end { - return false; - } - true +pub fn ranges_overlap(start: usize, size: usize, range_start: usize, range_size: usize) -> bool { + let end = start + size; + let range_end = range_start + range_size; + !(start >= range_end || range_start >= end) } #[cfg(test)] @@ -374,12 +373,12 @@ mod tests { #[test] fn test_ranges_overlap() { - assert!(ranges_overlap(100, 200, 150, 250)); - assert!(ranges_overlap(100, 200, 150, 200)); - assert!(!ranges_overlap(100, 200, 200, 250)); - assert!(ranges_overlap(100, 200, 100, 150)); - assert!(!ranges_overlap(100, 200, 50, 100)); - assert!(ranges_overlap(100, 200, 50, 150)); + assert!(ranges_overlap(100, 100, 150, 100)); + assert!(ranges_overlap(100, 100, 150, 50)); + assert!(!ranges_overlap(100, 100, 200, 50)); + assert!(ranges_overlap(100, 100, 100, 50)); + assert!(!ranges_overlap(100, 100, 50, 50)); + assert!(ranges_overlap(100, 100, 50, 100)); } #[test] diff --git a/pci/src/msix.rs b/pci/src/msix.rs index de010db28..9104524ef 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -456,12 +456,7 @@ impl Msix { let len = data.len(); let msix_cap_control_off: usize = self.msix_cap_offset as usize + MSIX_CAP_CONTROL as usize; // Only care about the bits Masked(14) & Enabled(15) in msix control register. - if !ranges_overlap( - offset, - offset + len, - msix_cap_control_off + 1, - msix_cap_control_off + 2, - ) { + if !ranges_overlap(offset, len, msix_cap_control_off + 1, 1) { return; } @@ -652,16 +647,12 @@ pub fn init_msix( offset = msix_cap_offset + MSIX_CAP_TABLE as usize; let table_size = vector_nr * MSIX_TABLE_ENTRY_SIZE as u32; let pba_size = ((round_up(vector_nr as u64, 64).unwrap() / 64) * 8) as u32; - let (table_offset, pba_offset) = if let Some((table, pba)) = offset_opt { - (table, pba) - } else { - (0, table_size) - }; + let (table_offset, pba_offset) = offset_opt.unwrap_or((0, table_size)); if ranges_overlap( - table_offset.try_into().unwrap(), - table_size.try_into().unwrap(), - pba_offset.try_into().unwrap(), - pba_size.try_into().unwrap(), + table_offset as usize, + table_size as usize, + pba_offset as usize, + pba_size as usize, ) { bail!("msix table and pba table overlapped."); } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index dff70c5ce..018646033 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -254,14 +254,9 @@ impl RootPort { } fn correct_race_unplug(&mut self, offset: usize, data: &[u8], old_status: u16) { - let end = offset + data.len(); + let size = data.len(); let cap_offset = self.config.pci_express_cap_offset; - if !ranges_overlap( - offset, - end, - (cap_offset + PCI_EXP_SLTSTA) as usize, - (cap_offset + PCI_EXP_SLTSTA + 2) as usize, - ) { + if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTSTA) as usize, 2) { return; } @@ -283,15 +278,10 @@ impl RootPort { fn do_unplug(&mut self, offset: usize, data: &[u8], old_ctl: u16, old_status: u16) { self.correct_race_unplug(offset, data, old_status); - let end = offset + data.len(); + let size = data.len(); let cap_offset = self.config.pci_express_cap_offset; // Only care the write config about slot control - if !ranges_overlap( - offset, - end, - (cap_offset + PCI_EXP_SLTCTL) as usize, - (cap_offset + PCI_EXP_SLTCTL + 2) as usize, - ) { + if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTCTL) as usize, 2) { return; } @@ -466,14 +456,9 @@ impl PciDevOps for RootPort { } } - if ranges_overlap(offset, end, COMMAND as usize, (COMMAND + 1) as usize) - || ranges_overlap(offset, end, IO_BASE as usize, (IO_BASE + 2) as usize) - || ranges_overlap( - offset, - end, - MEMORY_BASE as usize, - (MEMORY_BASE + 20) as usize, - ) + if ranges_overlap(offset, size, COMMAND as usize, 1) + || ranges_overlap(offset, size, IO_BASE as usize, 2) + || ranges_overlap(offset, size, MEMORY_BASE as usize, 20) { self.register_region(); } @@ -481,7 +466,7 @@ impl PciDevOps for RootPort { let mut status = le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); let exp_slot_status = (cap_offset + PCI_EXP_SLTSTA) as usize; - if ranges_overlap(offset, end, exp_slot_status, exp_slot_status + 2) { + if ranges_overlap(offset, size, exp_slot_status, 2) { let new_status = le_read_u16(data, 0).unwrap(); if new_status & !old_status & PCI_EXP_SLOTSTA_EVENTS != 0 { status = (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 1d48e3f7f..aab4a6a6e 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -900,14 +900,11 @@ impl PciDevOps for VfioPciDevice { } // BAR, header_type and extended caps are always controlled by StratoVirt. - if ranges_overlap(offset, end, BAR_0 as usize, (BAR_5 as usize) + REG_SIZE) - || ranges_overlap( - offset, - end, - HEADER_TYPE as usize, - (HEADER_TYPE as usize) + 2, - ) - || ranges_overlap(offset, end, PCI_CONFIG_SPACE_SIZE, PCIE_CONFIG_SPACE_SIZE) + let bars_size = (BAR_5 - BAR_0) as usize + REG_SIZE; + let ext_cfg_size = PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE; + if ranges_overlap(offset, size, BAR_0 as usize, bars_size) + || ranges_overlap(offset, size, HEADER_TYPE as usize, 2) + || ranges_overlap(offset, size, PCI_CONFIG_SPACE_SIZE, ext_cfg_size) { self.pci_config.read(offset, data); return; @@ -970,7 +967,7 @@ impl PciDevOps for VfioPciDevice { Some(&locked_parent_bus.mem_region), ); - if ranges_overlap(offset, end, COMMAND as usize, COMMAND as usize + REG_SIZE) { + if ranges_overlap(offset, size, COMMAND as usize, REG_SIZE) { if le_read_u32(&self.pci_config.config, offset).unwrap() & COMMAND_MEMORY_SPACE as u32 != 0 { @@ -978,7 +975,7 @@ impl PciDevOps for VfioPciDevice { error!("Failed to map bar regions, error is {:?}", e); } } - } else if ranges_overlap(offset, end, cap_offset, cap_offset + MSIX_CAP_SIZE as usize) { + } else if ranges_overlap(offset, size, cap_offset, MSIX_CAP_SIZE as usize) { let is_enable = is_msix_enabled(cap_offset, &self.pci_config.config); if !was_enable && is_enable { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 0435b39f2..873973888 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -940,8 +940,8 @@ impl VirtioPciDevice { fn do_cfg_access(&mut self, start: usize, end: usize, is_write: bool) { let pci_cfg_data_offset = self.cfg_cap_offset + offset_of!(VirtioPciCfgAccessCap, pci_cfg_data); - let pci_cfg_data_end = self.cfg_cap_offset + size_of::(); - if !ranges_overlap(start, end, pci_cfg_data_offset, pci_cfg_data_end) { + let cap_size = size_of::(); + if !ranges_overlap(start, end - start, pci_cfg_data_offset, cap_size) { return; } -- Gitee From e78638cfd6aad3bc6cf265ec1561b69f33376443 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 12 Apr 2023 11:27:12 +0800 Subject: [PATCH 0978/2187] format: optimize partial code In some places unwrap_else_default is used to simplify the code. Signed-off-by: Mingwang Li --- devices/src/scsi/bus.rs | 2 +- machine_manager/src/config/chardev.rs | 4 +--- machine_manager/src/config/network.rs | 8 ++------ machine_manager/src/config/rng.rs | 4 +--- util/src/aio/mod.rs | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index f95909598..d6f0d921e 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -353,7 +353,7 @@ const GC_FC_CORE: u16 = 0x0001; /// The medium may be removed from the device. const GC_FC_REMOVEABLE_MEDIUM: u16 = 0x0003; -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Eq)] pub enum ScsiXferMode { /// TEST_UNIT_READY, ... ScsiXferNone, diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 4395f4d77..a81ce82bc 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -449,9 +449,7 @@ pub fn parse_virtio_serial(vm_config: &mut VmConfig, serial_config: &str) -> Res pci_args_check(&cmd_parser)?; if vm_config.virtio_serial.is_none() { - let id = cmd_parser - .get_value::("id")? - .unwrap_or_else(|| "".to_string()); + let id = cmd_parser.get_value::("id")?.unwrap_or_default(); let multifunction = cmd_parser .get_value::("multifunction")? .map_or(false, |switch| switch.into()); diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 2a47deac5..70d5296b3 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -174,9 +174,7 @@ fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result>> { fn parse_netdev(cmd_parser: CmdParser) -> Result { let mut net = NetDevcfg::default(); - let netdev_type = cmd_parser - .get_value::("")? - .unwrap_or_else(|| "".to_string()); + let netdev_type = cmd_parser.get_value::("")?.unwrap_or_default(); if netdev_type.ne("tap") && netdev_type.ne("vhost-user") { bail!("Unsupported netdev type: {:?}", &netdev_type); } @@ -276,9 +274,7 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result("netdev")? .with_context(|| ConfigError::FieldIsMissing("netdev".to_string(), "net".to_string()))?; - let netid = cmd_parser - .get_value::("id")? - .unwrap_or_else(|| "".to_string()); + let netid = cmd_parser.get_value::("id")?.unwrap_or_default(); if let Some(mq) = cmd_parser.get_value::("mq")? { netdevinterfacecfg.mq = mq.inner; diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 792123ff3..303b3f6f4 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -85,9 +85,7 @@ pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result("rng")? .with_context(|| ConfigError::FieldIsMissing("rng".to_string(), "rng".to_string()))?; - rng_cfg.id = cmd_parser - .get_value::("id")? - .unwrap_or_else(|| "".to_string()); + rng_cfg.id = cmd_parser.get_value::("id")?.unwrap_or_default(); if let Some(max) = cmd_parser.get_value::("max-bytes")? { if let Some(peri) = cmd_parser.get_value::("period")? { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 8f4c52358..1226109cc 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -65,7 +65,7 @@ impl FromStr for AioEngine { } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum WriteZeroesState { Off, On, -- Gitee From d6f31ac6dee7ecb9a690b6806ed93e2a99c0ec2f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 7 Apr 2023 17:13:04 +0800 Subject: [PATCH 0979/2187] usb: delete repetitious Iovec struct struct Iovec is defined in aio. Delete repetitious defination in USB. Signed-off-by: zhouli57 Signed-off-by: liuxiangdong --- devices/src/usb/mod.rs | 24 ++++-------------------- devices/src/usb/storage.rs | 8 ++++---- devices/src/usb/xhci/xhci_controller.rs | 5 +++-- util/src/aio/mod.rs | 9 +++++++++ 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 2cfd4eabf..308cc8c69 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -33,7 +33,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context}; use log::{debug, error}; -use util::aio::{mem_from_buf, mem_to_buf}; +use util::aio::{mem_from_buf, mem_to_buf, Iovec}; use config::*; use descriptor::{UsbDescriptor, UsbDescriptorOps}; @@ -417,22 +417,6 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { Ok(()) } -/// Io vector which save the hva. -#[derive(Debug, Copy, Clone)] -pub struct Iovec { - pub iov_base: u64, - pub iov_len: usize, -} - -impl Iovec { - pub fn new(base: u64, len: usize) -> Self { - Iovec { - iov_base: base, - iov_len: len, - } - } -} - /// Usb packet used for device transfer data. #[derive(Clone)] pub struct UsbPacket { @@ -480,7 +464,7 @@ impl UsbPacket { let mut copied = 0; if to_host { for iov in &self.iovecs { - let cnt = min(iov.iov_len, len - copied); + let cnt = min(iov.iov_len as usize, len - copied); let tmp = &vec[copied..(copied + cnt)]; if let Err(e) = mem_from_buf(tmp, iov.iov_base) { error!("Failed to write mem: {:?}", e); @@ -492,7 +476,7 @@ impl UsbPacket { } } else { for iov in &self.iovecs { - let cnt = min(iov.iov_len, len - copied); + let cnt = min(iov.iov_len as usize, len - copied); let tmp = &mut vec[copied..(copied + cnt)]; if let Err(e) = mem_to_buf(tmp, iov.iov_base) { error!("Failed to read mem {:?}", e); @@ -506,7 +490,7 @@ impl UsbPacket { self.actual_length = copied as u32; } - pub fn get_iovecs_size(&mut self) -> usize { + pub fn get_iovecs_size(&mut self) -> u64 { let mut size = 0; for iov in &self.iovecs { size += iov.iov_len; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index be55c60a5..66a12913c 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -314,7 +314,7 @@ impl UsbStorage { match self.state.mode { UsbMsdMode::Cbw => { - if packet.get_iovecs_size() != CBW_SIZE as usize { + if packet.get_iovecs_size() != CBW_SIZE as u64 { error!("usb-storage: Bad CBW size {}", packet.get_iovecs_size()); packet.status = UsbPacketStatus::Stall; return; @@ -342,7 +342,7 @@ impl UsbStorage { } UsbMsdMode::DataOut => { // TODO: SCSI data out processing. - if packet.get_iovecs_size() < self.state.cbw.data_len as usize { + if packet.get_iovecs_size() < self.state.cbw.data_len as u64 { packet.status = UsbPacketStatus::Stall; return; } @@ -363,7 +363,7 @@ impl UsbStorage { match self.state.mode { UsbMsdMode::DataOut => { - if self.state.cbw.data_len != 0 || packet.get_iovecs_size() < CSW_SIZE as usize { + if self.state.cbw.data_len != 0 || packet.get_iovecs_size() < CSW_SIZE as u64 { packet.status = UsbPacketStatus::Stall; return; } @@ -371,7 +371,7 @@ impl UsbStorage { packet.status = UsbPacketStatus::Async; } UsbMsdMode::Csw => { - if packet.get_iovecs_size() < CSW_SIZE as usize { + if packet.get_iovecs_size() < CSW_SIZE as u64 { packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index d9bd196dc..dcb69ed1c 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -23,12 +23,13 @@ use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, warn}; use machine_manager::config::XhciConfig; +use util::aio::Iovec; use util::num_ops::{read_u32, write_u64_low}; use super::xhci_regs::{XchiOperReg, XhciInterrupter}; use crate::usb::config::*; use crate::usb::UsbError; -use crate::usb::{Iovec, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; +use crate::usb::{UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; use super::xhci_ring::XhciEventRingSeg; use super::xhci_ring::XhciRing; @@ -1537,7 +1538,7 @@ impl XhciDevice { trb.parameter }; if let Some(hva) = self.mem_space.get_host_address(GuestAddress(dma_addr)) { - vec.push(Iovec::new(hva, chunk as usize)); + vec.push(Iovec::new(hva, chunk as u64)); } else { bail!("HVA not existed {:x}", dma_addr); } diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 1226109cc..b41bbd866 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -91,6 +91,15 @@ pub struct Iovec { pub iov_len: u64, } +impl Iovec { + pub fn new(base: u64, len: u64) -> Self { + Iovec { + iov_base: base, + iov_len: len, + } + } +} + /// The trait for Asynchronous IO operation. trait AioContext { /// Submit IO requests to the OS, the nr submitted is returned. -- Gitee From 061707da1dd2577207132544c8db01920153880a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 01:36:20 +0800 Subject: [PATCH 0980/2187] scsi: return ScsiRequest in execute/emulate_execute Return ScsiRequest in execute/emulate_execute. Make preparations for USB-storage. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 27 +++++++++++++++------------ virtio/src/device/scsi_cntlr.rs | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index d6f0d921e..2ea6d9ba4 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -26,6 +26,7 @@ use crate::ScsiDisk::{ SCSI_TYPE_ROM, SECTOR_SHIFT, }; use util::aio::{AioCb, Iovec, OpCode, WriteZeroesState}; +use util::AsAny; /// Scsi Operation code. pub const TEST_UNIT_READY: u8 = 0x00; @@ -471,7 +472,7 @@ pub fn aio_complete_cb(aiocb: &AioCb, ret: i64) -> Result<()> { Ok(()) } -pub trait ScsiRequestOps { +pub trait ScsiRequestOps: AsAny { // Will be called in the end of this scsi instruction execution. fn scsi_request_complete_cb(&mut self, status: u8, scsisense: Option) -> Result<()>; } @@ -539,41 +540,43 @@ impl ScsiRequest { }) } - pub fn execute(self) -> Result { + pub fn execute(self) -> Result>> { let mode = self.cmd.mode.clone(); let op = self.cmd.op; let dev = self.dev.clone(); let locked_dev = dev.lock().unwrap(); let mut aio = locked_dev.aio.as_ref().unwrap().lock().unwrap(); + let s_req = Arc::new(Mutex::new(self)); let offset = match locked_dev.scsi_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, }; + let locked_req = s_req.lock().unwrap(); + let mut aiocb = AioCb { direct: locked_dev.config.direct, req_align: locked_dev.req_align, buf_align: locked_dev.buf_align, file_fd: locked_dev.disk_image.as_ref().unwrap().as_raw_fd(), opcode: OpCode::Noop, - iovec: self.iovec.clone(), - offset: (self.cmd.lba << offset) as usize, - nbytes: self.cmd.xfer as u64, + iovec: locked_req.iovec.clone(), + offset: (locked_req.cmd.lba << offset) as usize, + nbytes: locked_req.cmd.xfer as u64, user_data: 0, - iocompletecb: ScsiCompleteCb { - req: Arc::new(Mutex::new(self)), - }, + iocompletecb: ScsiCompleteCb { req: s_req.clone() }, discard: false, write_zeroes: WriteZeroesState::Off, write_zeroes_unmap: false, }; + drop(locked_req); if op == SYNCHRONIZE_CACHE { aiocb.opcode = OpCode::Fdsync; aio.submit_request(aiocb) .with_context(|| "Failed to process scsi request for flushing")?; aio.flush_request()?; - return Ok(0); + return Ok(s_req); } match mode { @@ -593,7 +596,7 @@ impl ScsiRequest { } aio.flush_request()?; - Ok(0) + Ok(s_req) } fn emulate_target_execute( @@ -657,7 +660,7 @@ impl ScsiRequest { } } - pub fn emulate_execute(&mut self) -> Result<()> { + pub fn emulate_execute(mut self) -> Result>> { debug!("emulate scsi command is {:#x}", self.cmd.op); let mut not_supported_flag = false; let mut sense = None; @@ -697,7 +700,7 @@ impl ScsiRequest { .as_mut() .scsi_request_complete_cb(status, sense)?; - Ok(()) + Ok(Arc::new(Mutex::new(self))) } } diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index be5b52ffd..9a063c735 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -1018,7 +1018,7 @@ impl ScsiCmdQueueHandler { Ok(()) } - fn handle_scsi_request(&mut self, mut sreq: ScsiRequest) -> Result<()> { + fn handle_scsi_request(&mut self, sreq: ScsiRequest) -> Result<()> { if sreq.opstype == EMULATE_SCSI_OPS { sreq.emulate_execute()?; } else { -- Gitee From 2256c7a8a2258e66869d75272813ad61155a0aa7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 03:02:24 +0800 Subject: [PATCH 0981/2187] USB-storage: enable usb-storage Implement dataplane for usb-storage. Signed-off-by: liuxiangdong --- devices/src/usb/storage.rs | 290 +++++++++++++++++++++-------- docs/config_guidebook.md | 4 +- machine_manager/src/config/scsi.rs | 2 +- machine_manager/src/config/usb.rs | 42 +++-- 4 files changed, 247 insertions(+), 91 deletions(-) diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 66a12913c..2008b811a 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -12,16 +12,16 @@ use std::{ collections::HashMap, - fs::File, sync::{Arc, Mutex, Weak}, }; -use anyhow::Result; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use once_cell::sync::Lazy; -use machine_manager::config::{DriveFile, UsbStorageConfig, VmConfig}; +use machine_manager::config::{DriveFile, UsbStorageConfig}; +use util::aio::{Aio, AioEngine}; use super::config::*; use super::descriptor::{ @@ -30,6 +30,13 @@ use super::descriptor::{ }; use super::xhci::xhci_controller::XhciDevice; use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; +use crate::{ + ScsiBus::{ + aio_complete_cb, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + EMULATE_SCSI_OPS, GOOD, SCSI_CMD_BUF_SIZE, + }, + ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, +}; // Storage device descriptor static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { @@ -139,10 +146,24 @@ pub const CBW_FLAG_OUT: u8 = 0; pub const CBW_SIZE: u8 = 31; pub const CSW_SIZE: u8 = 13; +// USB-storage has only target 0 and lun 0. +const USB_STORAGE_SCSI_LUN_ID: u8 = 0; + struct UsbStorageState { mode: UsbMsdMode, cbw: UsbMsdCbw, csw: UsbMsdCsw, + cdb: Option<[u8; SCSI_CMD_BUF_SIZE]>, + iovec_len: u32, +} + +impl ScsiRequestOps for UsbMsdCsw { + fn scsi_request_complete_cb(&mut self, status: u8, _: Option) -> Result<()> { + if status != GOOD { + self.status = UsbMsdCswStatus::Failed as u8; + } + Ok(()) + } } impl UsbStorageState { @@ -151,7 +172,54 @@ impl UsbStorageState { mode: UsbMsdMode::Cbw, cbw: UsbMsdCbw::default(), csw: UsbMsdCsw::new(), + cdb: None, + iovec_len: 0, + } + } + + /// Check if there exists SCSI CDB. + /// + /// # Arguments + /// + /// `exist` - Expected existence status. + /// + /// Return Error if expected existence status is not equal to the actual situation. + fn check_cdb_exist(&self, exist: bool) -> Result<()> { + if exist { + self.cdb.with_context(|| "No scsi CDB can be executed")?; + } else if self.cdb.is_some() { + bail!( + "Another request has not been done! cdb {:x?}", + self.cdb.unwrap() + ); + } + + Ok(()) + } + + /// Check if Iovec is empty. + /// + /// # Arguments + /// + /// `empty` - Expected status. If true, expect empty iovec. + /// + /// Return Error if expected iovec status is not equal to the actual situation. + fn check_iovec_empty(&self, empty: bool) -> Result<()> { + if empty != (self.iovec_len == 0) { + match empty { + true => { + bail!( + "Another request has not been done! Data buffer length {}.", + self.iovec_len + ); + } + false => { + bail!("Missing data buffer!"); + } + }; } + + Ok(()) } } @@ -164,14 +232,14 @@ pub struct UsbStorage { cntlr: Option>>, /// Configuration of the USB storage device. pub config: UsbStorageConfig, - /// Image file opened. - pub disk_image: Option>, - /// The align requirement of request(offset/len). - pub req_align: u32, - /// The align requirement of buffer(iova_base). - pub buf_align: u32, - /// Drive backend files. - drive_files: Arc>>, + /// Scsi bus attached to this usb-storage device. + scsi_bus: Arc>, + /// Effective scsi backend. + // Note: scsi device should attach to scsi bus. Logically, scsi device shoud not be placed in UsbStorage. + // But scsi device is needed in processing scsi request. Because the three (usb-storage/scsi bus/scsi device) + // correspond one-to-one, add scsi device member here for the execution efficiency (No need to find a unique + // device from the hash table of the unique bus). + scsi_dev: Arc>, } #[derive(Debug)] @@ -212,7 +280,7 @@ impl UsbMsdCbw { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] struct UsbMsdCsw { sig: u32, tag: u32, @@ -243,16 +311,23 @@ impl UsbStorage { config: UsbStorageConfig, drive_files: Arc>>, ) -> Self { + let scsi_type = match &config.media as &str { + "disk" => SCSI_TYPE_DISK, + _ => SCSI_TYPE_ROM, + }; + Self { id: config.id.clone().unwrap(), usb_device: UsbDevice::new(), state: UsbStorageState::new(), cntlr: None, - config, - disk_image: None, - req_align: 1, - buf_align: 1, - drive_files, + config: config.clone(), + scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), + scsi_dev: Arc::new(Mutex::new(ScsiDevice::new( + config.scsi_cfg, + scsi_type, + drive_files, + ))), } } @@ -263,15 +338,17 @@ impl UsbStorage { self.usb_device .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; - if !self.config.path_on_host.is_empty() { - let drive_files = self.drive_files.lock().unwrap(); - let file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; - self.disk_image = Some(Arc::new(file)); - - let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; - self.req_align = alignments.0; - self.buf_align = alignments.1; - } + let aio = Aio::new(Arc::new(aio_complete_cb), AioEngine::Off) + .with_context(|| format!("USB-storage {}: aio creation error!", self.id))?; + let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); + locked_scsi_dev.aio = Some(Arc::new(Mutex::new(aio))); + locked_scsi_dev.realize()?; + drop(locked_scsi_dev); + self.scsi_bus + .lock() + .unwrap() + .devices + .insert((0, 0), self.scsi_dev.clone()); let storage: Arc> = Arc::new(Mutex::new(self)); Ok(storage) @@ -293,7 +370,7 @@ impl UsbStorage { USB_INTERFACE_CLASS_IN_REQUEST => { if device_req.request == GET_MAX_LUN { // TODO: Now only supports 1 LUN. - let maxlun = 0; + let maxlun = USB_STORAGE_SCSI_LUN_ID; self.usb_device.data_buf[0] = maxlun; packet.actual_length = 1; return; @@ -306,92 +383,156 @@ impl UsbStorage { packet.status = UsbPacketStatus::Stall; } - fn handle_token_out(&mut self, packet: &mut UsbPacket) { + fn handle_token_out(&mut self, packet: &mut UsbPacket) -> Result<()> { if packet.ep_number != 2 { - packet.status = UsbPacketStatus::Stall; - return; + bail!("Error ep_number {}!", packet.ep_number); } match self.state.mode { UsbMsdMode::Cbw => { - if packet.get_iovecs_size() != CBW_SIZE as u64 { - error!("usb-storage: Bad CBW size {}", packet.get_iovecs_size()); - packet.status = UsbPacketStatus::Stall; - return; + if packet.get_iovecs_size() < CBW_SIZE as u64 { + bail!("Bad CBW size {}", packet.get_iovecs_size()); } + self.state.check_cdb_exist(false)?; + let mut cbw_buf = [0_u8; CBW_SIZE as usize]; packet.transfer_packet(&mut cbw_buf, CBW_SIZE as usize); self.state.cbw.convert(&cbw_buf); debug!("Storage cbw {:?}", self.state.cbw); if self.state.cbw.sig != CBW_SIGNATURE { - error!("usb-storage: Bad signature {:X}", self.state.cbw.sig); - packet.status = UsbPacketStatus::Stall; - return; + bail!("Bad signature {:x}", self.state.cbw.sig); + } + if self.state.cbw.lun != USB_STORAGE_SCSI_LUN_ID { + bail!( + "Bad lun id {:x}. Usb-storage only supports lun id 0!", + self.state.cbw.lun + ); } + self.state.cdb = Some(self.state.cbw.cmd); + if self.state.cbw.data_len == 0 { + self.handle_scsi_request(packet)?; self.state.mode = UsbMsdMode::Csw; } else if self.state.cbw.flags & CBW_FLAG_IN == CBW_FLAG_IN { self.state.mode = UsbMsdMode::DataIn; } else { self.state.mode = UsbMsdMode::DataOut; } - // TODO: Convert CBW to SCSI CDB. - self.state.csw = UsbMsdCsw::new(); } UsbMsdMode::DataOut => { - // TODO: SCSI data out processing. - if packet.get_iovecs_size() < self.state.cbw.data_len as u64 { - packet.status = UsbPacketStatus::Stall; - return; - } - - self.state.mode = UsbMsdMode::Csw; + self.handle_data_inout_packet(packet, UsbMsdMode::DataOut)?; } _ => { - packet.status = UsbPacketStatus::Stall; + bail!( + "Unexpected token out. Expected mode {:?} packet.", + self.state.mode + ); } } + Ok(()) } - fn handle_token_in(&mut self, packet: &mut UsbPacket) { + fn handle_token_in(&mut self, packet: &mut UsbPacket) -> Result<()> { if packet.ep_number != 1 { - packet.status = UsbPacketStatus::Stall; - return; + bail!("Error ep_number {}!", packet.ep_number); } match self.state.mode { UsbMsdMode::DataOut => { - if self.state.cbw.data_len != 0 || packet.get_iovecs_size() < CSW_SIZE as u64 { - packet.status = UsbPacketStatus::Stall; - return; - } - - packet.status = UsbPacketStatus::Async; + bail!("Not supported usb packet(Token_in and data_out)."); } UsbMsdMode::Csw => { if packet.get_iovecs_size() < CSW_SIZE as u64 { - packet.status = UsbPacketStatus::Stall; - return; + bail!("Bad CSW size {}", packet.get_iovecs_size()); } + self.state.check_cdb_exist(true)?; + self.state.check_iovec_empty(self.state.cbw.data_len == 0)?; - // TODO: CSW after SCSI data processing. let mut csw_buf = [0_u8; CSW_SIZE as usize]; self.state.csw.tag = self.state.cbw.tag; self.state.csw.convert(&mut csw_buf); + debug!("Storage csw {:?}", self.state.csw); packet.transfer_packet(&mut csw_buf, CSW_SIZE as usize); - self.state.mode = UsbMsdMode::Cbw; - self.state.csw = UsbMsdCsw::new(); + + // Reset UsbStorageState. + self.state = UsbStorageState::new(); } UsbMsdMode::DataIn => { - // TODO: SCSI data in processing. - self.state.mode = UsbMsdMode::Csw; + self.handle_data_inout_packet(packet, UsbMsdMode::DataIn)?; } _ => { - packet.status = UsbPacketStatus::Stall; + bail!( + "Unexpected token in. Expected mode {:?} packet.", + self.state.mode + ); } } + Ok(()) + } + + fn handle_data_inout_packet(&mut self, packet: &mut UsbPacket, mode: UsbMsdMode) -> Result<()> { + self.state.check_cdb_exist(true)?; + self.state.check_iovec_empty(true)?; + + let iovec_len = packet.get_iovecs_size() as u32; + if iovec_len < self.state.cbw.data_len { + bail!( + "Insufficient transmission buffer, transfer size {}, buffer size {}, MSD mode {:?}!", + self.state.cbw.data_len, + iovec_len, + mode, + ); + } + + self.state.iovec_len = iovec_len; + debug!("Storage: iovec_len {}.", iovec_len); + self.handle_scsi_request(packet)?; + packet.actual_length = iovec_len; + self.state.mode = UsbMsdMode::Csw; + + Ok(()) + } + + // Handle scsi request and save result in self.csw for next CSW packet. + fn handle_scsi_request(&mut self, packet: &mut UsbPacket) -> Result<()> { + self.state + .cdb + .with_context(|| "No scsi CDB can be executed")?; + + let csw = Box::new(UsbMsdCsw::new()); + let sreq = ScsiRequest::new( + self.state.cdb.unwrap(), + 0, + packet.iovecs.clone(), + self.state.iovec_len, + self.scsi_dev.clone(), + csw, + ) + .with_context(|| "Error in creating scsirequest.")?; + + if sreq.cmd.xfer > sreq.datalen && sreq.cmd.mode != ScsiXferMode::ScsiXferNone { + // Wrong USB packet which doesn't provide enough datain/dataout buffer. + bail!( + "command {:x} requested data's length({}), provided buffer length({})", + sreq.cmd.op, + sreq.cmd.xfer, + sreq.datalen + ); + } + + let sreq_h = match sreq.opstype { + EMULATE_SCSI_OPS => sreq.emulate_execute(), + _ => sreq.execute(), + } + .with_context(|| "Error in executing scsi request.")?; + + let csw_h = &sreq_h.lock().unwrap().upper_req; + let csw = csw_h.as_ref().as_any().downcast_ref::().unwrap(); + self.state.csw = *csw; + + Ok(()) } } @@ -429,16 +570,15 @@ impl UsbDeviceOps for UsbStorage { packet.ep_number, self.state.mode ); - match packet.pid as u8 { - USB_TOKEN_OUT => { - self.handle_token_out(packet); - } - USB_TOKEN_IN => { - self.handle_token_in(packet); - } - _ => { - packet.status = UsbPacketStatus::Stall; - } + let result = match packet.pid as u8 { + USB_TOKEN_OUT => self.handle_token_out(packet), + USB_TOKEN_IN => self.handle_token_in(packet), + _ => Err(anyhow!("Bad token!")), + }; + + if let Err(e) = result { + warn!("USB-storage {}: handle data error: {:?}", self.id, e); + packet.status = UsbPacketStatus::Stall; } } diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index c10f1a0e3..f84f5678f 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -802,9 +802,11 @@ Three properties can be set for USB Storage. ```shell -device usb-storage,drive=,id= --drive id=,file=[,media={disk|cdrom}] +-drive id=,file=[,media={disk|cdrom}],aio=off,direct=false ``` +Note: "aio=off,direct=false" must be configured and other aio/direct values are not supported. + ### 2.14 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index cc72819d4..59e6768d0 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -140,7 +140,7 @@ pub fn parse_scsi_controller( Ok(cntlr_cfg) } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ScsiDevConfig { /// Scsi Device id. pub id: String, diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 886022cd0..2e0d6c544 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -12,12 +12,15 @@ use std::{path::Path, str::FromStr}; -use super::error::ConfigError; use anyhow::{anyhow, bail, Context, Result}; use strum::{EnumCount, IntoEnumIterator}; use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; -use crate::config::{check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, VmConfig}; +use super::error::ConfigError; +use crate::config::{ + check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, +}; +use util::aio::AioEngine; /// XHCI controller configuration. #[derive(Debug)] @@ -187,7 +190,9 @@ pub fn parse_usb_camera(conf: &str) -> Result { fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; - check_arg_too_long(&id.unwrap(), "id") + check_arg_too_long(&id.unwrap(), "id")?; + + Ok(()) } #[derive(Clone, Debug)] @@ -236,18 +241,18 @@ fn check_camera_path(path: Option) -> Result<()> { pub struct UsbStorageConfig { /// USB Storage device id. pub id: Option, - /// The image file path. - pub path_on_host: String, - /// USB Storage device can not do write operation. - pub read_only: bool, + /// The scsi backend config. + pub scsi_cfg: ScsiDevConfig, + /// The backend scsi device type(Disk or CD-ROM). + pub media: String, } impl UsbStorageConfig { fn new() -> Self { - UsbStorageConfig { + Self { id: None, - path_on_host: "".to_string(), - read_only: false, + scsi_cfg: ScsiDevConfig::default(), + media: "".to_string(), } } } @@ -260,7 +265,13 @@ impl Default for UsbStorageConfig { impl ConfigCheck for UsbStorageConfig { fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-storage") + check_id(self.id.clone(), "usb-storage")?; + + if self.scsi_cfg.aio_type != AioEngine::Off || self.scsi_cfg.direct { + bail!("USB-storage: \"aio=off,direct=false\" must be configured."); + } + + Ok(()) } } @@ -285,9 +296,12 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result let drive_arg = &vm_config .drives .remove(&storage_drive) - .with_context(|| "No drive configured matched for usb storage device")?; - dev.path_on_host = drive_arg.path_on_host.clone(); - dev.read_only = drive_arg.read_only; + .with_context(|| "No drive configured matched for usb storage device.")?; + dev.scsi_cfg.path_on_host = drive_arg.path_on_host.clone(); + dev.scsi_cfg.read_only = drive_arg.read_only; + dev.scsi_cfg.aio_type = drive_arg.aio; + dev.scsi_cfg.direct = drive_arg.direct; + dev.media = drive_arg.media.clone(); dev.check()?; Ok(dev) -- Gitee From 353acf80d214ddaebe30e55e293ff657bdfc5d2a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 03:05:58 +0800 Subject: [PATCH 0982/2187] scsi-bus: add START_STOP and ALLOW_MEDIUM_REMOVAL COMMAND Do not support SCSI_DISK_F_REMOVABLE and cdrom tray now. Return Ok is enough for START_STOP/ALLOW_MEDIUM_REMOVAL because these two commands can do nothing. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 2ea6d9ba4..a824f92d1 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -641,6 +641,11 @@ impl ScsiRequest { Ok(Vec::new()) } } + // Do not support SCSI_DISK_F_REMOVABLE now. + // Return Ok is enough for START_STOP/ALLOW_MEDIUM_REMOVAL. + // TODO: implement SCSI_DISK_F_REMOVABLE. + START_STOP => Ok(Vec::new()), + ALLOW_MEDIUM_REMOVAL => Ok(Vec::new()), INQUIRY => scsi_command_emulate_inquiry(&self.cmd, &self.dev), READ_CAPACITY_10 => scsi_command_emulate_read_capacity_10(&self.cmd, &self.dev), MODE_SENSE | MODE_SENSE_10 => scsi_command_emulate_mode_sense(&self.cmd, &self.dev), -- Gitee From 58ccd144e6e377ca2bb4c5cf9e411863da68adc7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 03:11:14 +0800 Subject: [PATCH 0983/2187] xhci: delete ep_type check USB-storage will transfer bulk packet. ep_type will be BulkIn/BulkOut. Delete this useless ep_type check now. Signed-off-by: liuxiangdong --- devices/src/usb/xhci/xhci_controller.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index dcb69ed1c..d86e5cbb0 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1494,9 +1494,6 @@ impl XhciDevice { || epctx.ep_type == EpType::IsoIn || epctx.ep_type == EpType::BulkIn || epctx.ep_type == EpType::IntrIn; - if epctx.ep_type != EpType::IntrOut && epctx.ep_type != EpType::IntrIn { - warn!("Unhandled ep_type {:?}", epctx.ep_type); - } if let Err(e) = self.setup_usb_packet(xfer) { error!("Failed to setup packet when transfer data {:?}", e); xfer.status = TRBCCode::TrbError; -- Gitee From f2c859243aa6249cfcc78b800d096cdeb8dfb970 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Apr 2023 00:11:42 +0800 Subject: [PATCH 0984/2187] mst: add some usb_storage test Add some usb_storage test for dataplane is enable. Signed-off-by: liuxiangdong --- devices/src/usb/storage.rs | 3 +- tests/mod_test/src/libdriver/usb.rs | 6 +- tests/mod_test/tests/usb_storage_test.rs | 185 ++++++++++++++++++++--- 3 files changed, 164 insertions(+), 30 deletions(-) diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 2008b811a..9bd3f2939 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -250,8 +250,7 @@ enum UsbMsdMode { Csw, } -#[allow(dead_code)] -enum UsbMsdCswStatus { +pub enum UsbMsdCswStatus { Passed, Failed, PhaseError, diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 86de9d002..9a2fe72fd 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -2102,15 +2102,15 @@ impl TestUsbBuilder { self } - pub fn with_usb_storage(mut self, image_path: &str) -> Self { + pub fn with_usb_storage(mut self, image_path: &str, media: &str) -> Self { let args = format!("-device usb-storage,drive=drive0,id=storage0"); let args: Vec<&str> = args[..].split(' ').collect(); let mut args = args.into_iter().map(|s| s.to_string()).collect(); self.args.append(&mut args); let args = format!( - "-drive if=none,id=drive0,format=raw,media=cdrom,aio=off,direct=false,file={}", - image_path + "-drive if=none,id=drive0,format=raw,media={},aio=off,direct=false,file={}", + media, image_path ); let args: Vec<&str> = args[..].split(' ').collect(); let mut args = args.into_iter().map(|s| s.to_string()).collect(); diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs index 45f5a5596..8393a6c61 100644 --- a/tests/mod_test/tests/usb_storage_test.rs +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -17,8 +17,8 @@ use byteorder::{ByteOrder, LittleEndian}; use devices::usb::{ config::{USB_INTERFACE_CLASS_IN_REQUEST, USB_INTERFACE_CLASS_OUT_REQUEST}, storage::{ - CBW_FLAG_IN, CBW_FLAG_OUT, CBW_SIGNATURE, CBW_SIZE, CSW_SIGNATURE, CSW_SIZE, GET_MAX_LUN, - MASS_STORAGE_RESET, + UsbMsdCswStatus, CBW_FLAG_IN, CBW_FLAG_OUT, CBW_SIGNATURE, CBW_SIZE, CSW_SIGNATURE, + CSW_SIZE, GET_MAX_LUN, MASS_STORAGE_RESET, }, xhci::{TRBCCode, TRB_SIZE}, UsbDeviceRequest, @@ -33,10 +33,13 @@ use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; const READ_10: u8 = 0x28; const WRITE_10: u8 = 0x2a; +const RESERVE: u8 = 0x16; const CBW_ILLEGAL_SIZE: u8 = CBW_SIZE - 1; const CSW_ILLEGAL_SIZE: u8 = CSW_SIZE - 1; +const DISK_SECTOR_SIZE: usize = 512; + struct Cbw { sig: u32, tag: u32, @@ -98,6 +101,7 @@ fn data_phase( slot_id: u32, buf: &[u8], to_host: bool, + if_success: bool, ) { let mut iovecs = Vec::new(); let ptr = guest_allocator.alloc(buf.len() as u64); @@ -118,7 +122,11 @@ fn data_phase( } let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); + if if_success { + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } else { + assert_ne!(evt.ccode, TRBCCode::Success as u32); + } if to_host { let data_buf = xhci.mem_read(ptr, buf.len()); @@ -169,7 +177,7 @@ fn usb_storage_basic() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "disk") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -194,13 +202,15 @@ fn usb_storage_basic() { ); // Test 2: DataOut phase. - let buf = "TEST".as_bytes(); + let mut buf = "TEST".as_bytes().to_vec(); + buf.resize(DISK_SECTOR_SIZE, 0); data_phase( xhci.borrow_mut(), guest_allocator.borrow_mut(), slot_id, - buf, + &buf, false, + true, ); // Test 3: CSW phase. @@ -230,12 +240,12 @@ fn usb_storage_basic() { ); // Test 5: Datain phase. - let buf = "TEST".as_bytes(); data_phase( xhci.borrow_mut(), guest_allocator.borrow_mut(), slot_id, - buf, + &buf, + true, true, ); @@ -265,7 +275,7 @@ fn usb_storage_functional_reset() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, _) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -295,15 +305,17 @@ fn usb_storage_functional_reset() { /// TestStep: /// 0. Init process. /// 1. Get Max Lun. -/// 2. Test ends. Destroy device. +/// 2. Send CBW whose lun is greater than 'MAX LUN'. +/// 3. Test ends. Destroy device. /// Expect: -/// 0/1/2: success. +/// 0/1/3: success. +/// 2: Stallerror. #[test] fn usb_storage_functional_get_max_lun() { let image_path = create_img(TEST_IMAGE_SIZE, 0); - let (xhci, test_state, _) = TestUsbBuilder::new() + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -328,6 +340,23 @@ fn usb_storage_functional_get_max_lun() { assert_eq!(buf, [0]); + // Test: lun > 0 CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 512; + cbw.lun = 8; + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = READ_10; + cbw.cmd[8] = 1; + cbw_phase( + cbw, + xhci, + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::StallError, + CBW_SIZE, + ); + test_state.borrow_mut().stop(); cleanup_img(image_path); } @@ -345,7 +374,7 @@ fn usb_storage_illegal_request() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, _) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -384,7 +413,7 @@ fn usb_storage_cbw_signature() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -422,7 +451,7 @@ fn usb_storage_cbw_illegal_size() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -459,7 +488,7 @@ fn usb_storage_csw_illegal_size() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -500,13 +529,13 @@ fn usb_storage_csw_illegal_size() { /// 3. Test ends. Destroy device. /// Expect: /// 0/1/3: success. -/// 2: CSW signature verification failed. +/// 2: CSW StallError. #[test] fn usb_storage_abnormal_phase_01() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -535,7 +564,7 @@ fn usb_storage_abnormal_phase_01() { xhci.borrow_mut(), guest_allocator.borrow_mut(), slot_id, - TRBCCode::Success, + TRBCCode::StallError, CSW_SIZE, false, ); @@ -562,7 +591,7 @@ fn usb_storage_abnormal_phase_02() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -619,7 +648,7 @@ fn usb_storage_abnormal_phase_03() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -677,7 +706,7 @@ fn usb_storage_illegal_scsi_cdb() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -715,6 +744,112 @@ fn usb_storage_illegal_scsi_cdb() { cleanup_img(image_path); } +/// USB storage device does not provide enough data buffer test. +/// TestStep: +/// 0. Init process. +/// 1. CBW: read. +/// 2. DataIn: data read from device to host. +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/3: success. +/// 2: StallError. +#[test] +fn insufficient_data_buffer_test() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path, "cdrom") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.data_len = 512; // 512 Bytes data buffer. + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = READ_10; + cbw.cmd[8] = 1; // Need 1 logical sector(CD-ROM: 2048Bytes). + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: Datain phase. + let buf = vec![0; 512]; // Provides 512 Bytes datain buffer. + data_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + &buf, + true, + false, + ); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + +/// USB storage device not supported scsi cdb test. +/// TestStep: +/// 0. Init process. +/// 1. CBW. +/// 2. CSW. +/// 3. Test ends. Destroy device. +/// Expect: +/// 0/1/2/3: success. +/// 2: CSW status = UsbMsdCswStatus::Failed. +#[test] +fn usb_storage_not_supported_scsi_cdb() { + let image_path = create_img(TEST_IMAGE_SIZE, 0); + let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_storage(&image_path, "cdrom") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + + let port_id = 1; + let slot_id = xhci.borrow_mut().init_device(port_id); + + // Test 1: CBW phase. + let mut cbw = Cbw::new(); + cbw.flags = CBW_FLAG_IN; + cbw.cmd_len = 10; + cbw.cmd[0] = RESERVE; + cbw_phase( + cbw, + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CBW_SIZE, + ); + + // Test 2: CSW phase. + let csw_addr = csw_phase( + xhci.borrow_mut(), + guest_allocator.borrow_mut(), + slot_id, + TRBCCode::Success, + CSW_SIZE, + true, + ); + + let buf = xhci.borrow_mut().mem_read(csw_addr, CSW_SIZE as usize); + assert_eq!(UsbMsdCswStatus::Failed as u8, buf[12]); + + test_state.borrow_mut().stop(); + cleanup_img(image_path); +} + /// USB storage device CBW phase to invalid endpoint test. /// TestStep: /// 0. Init process. @@ -728,7 +863,7 @@ fn usb_storage_cbw_invalid_endpoint() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); @@ -772,7 +907,7 @@ fn usb_storage_csw_invalid_endpoint() { let image_path = create_img(TEST_IMAGE_SIZE, 0); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") - .with_usb_storage(&image_path) + .with_usb_storage(&image_path, "cdrom") .with_config("auto_run", true) .with_config("command_auto_doorbell", true) .build(); -- Gitee From 03805a19e47feea7c8b52fdb5883f7bfa68b0ec2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 12 Apr 2023 15:15:10 +0800 Subject: [PATCH 0985/2187] scsi: delete useless path_on_host check path_on_host in scsi cannot be 0 length. Delete useless check. Signed-off-by: liuxiangdong --- devices/src/scsi/disk.rs | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index f6d6da532..ac55e1f87 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -46,8 +46,6 @@ pub const SCSI_DISK_F_DPOFUA: u32 = 1; /// Used to compute the number of sectors. pub const SECTOR_SHIFT: u8 = 9; -/// Size of the dummy block device. -const DUMMY_IMG_SIZE: u64 = 0; pub const DEFAULT_SECTOR_SIZE: u32 = 1_u32 << SECTOR_SHIFT; /// Scsi disk's block size is 512 Bytes. @@ -155,26 +153,18 @@ impl ScsiDevice { if let Some(serial) = &self.config.serial { self.state.serial = serial.clone(); } - let mut disk_size = DUMMY_IMG_SIZE; - - if !self.config.path_on_host.is_empty() { - self.disk_image = None; - - let drive_files = self.drive_files.lock().unwrap(); - let mut file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; - disk_size = file - .seek(SeekFrom::End(0)) - .with_context(|| "Failed to seek the end for scsi device")?; - self.disk_image = Some(Arc::new(file)); - - let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; - self.req_align = alignments.0; - self.buf_align = alignments.1; - } else { - self.disk_image = None; - self.req_align = 1; - self.buf_align = 1; - } + + let drive_files = self.drive_files.lock().unwrap(); + // File path can not be empty string. And it has also been checked in CmdParser::parse. + let mut file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; + let disk_size = file + .seek(SeekFrom::End(0)) + .with_context(|| "Failed to seek the end for scsi device")?; + self.disk_image = Some(Arc::new(file)); + + let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; + self.req_align = alignments.0; + self.buf_align = alignments.1; self.disk_sectors = disk_size >> SECTOR_SHIFT; -- Gitee From 59119e402b945f66c3b73f91fccfa64a5720361e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 6 Apr 2023 22:02:00 +0800 Subject: [PATCH 0986/2187] PCI: Support intx interrupt For PCI device, only msix interrupt is supported currently. Add supporting for the traditional intx interrupt. The IRQ numbers corresponding to the PCI interrupt lines are presented to the kernel through the ACPI table. Intx interrupt calls the interface of KVM to register and trigger the interrupt. Signed-off-by: Jinhao Gao --- .../src/interrupt_controller/aarch64/mod.rs | 2 +- devices/src/interrupt_controller/mod.rs | 2 + devices/src/lib.rs | 2 +- devices/src/usb/xhci/xhci_controller.rs | 24 ++- devices/src/usb/xhci/xhci_pci.rs | 35 +++- hypervisor/src/kvm/mod.rs | 9 + machine/src/standard_vm/aarch64/mod.rs | 44 ++++- machine/src/standard_vm/aarch64/syscall.rs | 1 + machine/src/standard_vm/x86_64/ich9_lpc.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 23 ++- pci/src/bus.rs | 13 +- pci/src/config.rs | 59 ++++++- pci/src/host.rs | 69 +++++++- pci/src/intx.rs | 162 ++++++++++++++++++ pci/src/lib.rs | 23 ++- pci/src/root_port.rs | 104 +++++++++-- virtio/src/transport/virtio_pci.rs | 46 +++-- 17 files changed, 557 insertions(+), 63 deletions(-) create mode 100644 pci/src/intx.rs diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index f267bd8df..8114d61d8 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -31,7 +31,7 @@ use util::{ }; // First 32 are private to each CPU (SGIs and PPIs). -pub(crate) const GIC_IRQ_INTERNAL: u32 = 32; +pub const GIC_IRQ_INTERNAL: u32 = 32; // Last usable IRQ on aarch64. pub const GIC_IRQ_MAX: u32 = 192; diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 7eb7dd4d2..0dfaeded7 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -38,6 +38,8 @@ pub use aarch64::GICv3Config as ICGICv3Config; #[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; #[cfg(target_arch = "aarch64")] +pub use aarch64::GIC_IRQ_INTERNAL; +#[cfg(target_arch = "aarch64")] pub use aarch64::GIC_IRQ_MAX; pub use anyhow::Result; pub use error::InterruptError; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 134be20b0..4655644bb 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -27,7 +27,7 @@ pub mod usb; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, InterruptError as IntCtrlErrs, - GIC_IRQ_MAX, + GIC_IRQ_INTERNAL, GIC_IRQ_MAX, }; pub use legacy::error::LegacyError as LegacyErrs; pub use scsi::bus as ScsiBus; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index d9bd196dc..827b3a153 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -541,7 +541,7 @@ pub struct XhciDevice { pub intrs: Vec, pub cmd_ring: XhciRing, mem_space: Arc, - pub send_interrupt_ops: Option>, + pub send_interrupt_ops: Option bool + Send + Sync>>, } impl XhciDevice { @@ -1816,19 +1816,25 @@ impl XhciDevice { } if let Some(intr_ops) = self.send_interrupt_ops.as_ref() { - intr_ops(idx); - self.intrs[idx as usize].iman &= !IMAN_IP; + if intr_ops(idx, 1) { + self.intrs[idx as usize].iman &= !IMAN_IP; + } } } pub fn update_intr(&mut self, v: u32) { - if self.intrs[v as usize].iman & IMAN_IP == IMAN_IP - && self.intrs[v as usize].iman & IMAN_IE == IMAN_IE - && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE - { + let mut level = 0; + if v == 0 { + if self.intrs[0].iman & IMAN_IP == IMAN_IP + && self.intrs[0].iman & IMAN_IE == IMAN_IE + && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE + { + level = 1; + } if let Some(intr_ops) = &self.send_interrupt_ops { - intr_ops(v); - self.intrs[v as usize].iman &= !IMAN_IP; + if intr_ops(0, level) { + self.intrs[0].iman &= !IMAN_IP; + } } } } diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index f04a8911d..2cab3bf8d 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -24,7 +24,7 @@ use pci::config::{ PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::update_dev_id; -use pci::{init_msix, le_write_u16, PciBus, PciDevOps}; +use pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevOps}; use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; use super::xhci_regs::{ @@ -35,7 +35,6 @@ use crate::usb::UsbDeviceOps; /// 5.2 PCI Configuration Registers(USB) const PCI_CLASS_PI: u16 = 0x09; -const PCI_INTERRUPT_PIN: u16 = 0x3d; const PCI_CACHE_LINE_SIZE: u16 = 0x0c; const PCI_SERIAL_BUS_RELEASE_NUMBER: u8 = 0x60; const PCI_FRAME_LENGTH_ADJUSTMENT: u8 = 0x61; @@ -189,7 +188,10 @@ impl PciDevOps for XhciPciDevice { PCI_CLASS_SERIAL_USB, )?; self.pci_config.config[PCI_CLASS_PI as usize] = 0x30; - self.pci_config.config[PCI_INTERRUPT_PIN as usize] = 0x01; + + #[cfg(target_arch = "aarch64")] + self.pci_config.set_interrupt_pin(); + self.pci_config.config[PCI_CACHE_LINE_SIZE as usize] = 0x10; self.pci_config.config[PCI_SERIAL_BUS_RELEASE_NUMBER as usize] = PCI_SERIAL_BUS_RELEASE_VERSION_3_0; @@ -209,6 +211,13 @@ impl PciDevOps for XhciPciDevice { Some((XHCI_MSIX_TABLE_OFFSET, XHCI_MSIX_PBA_OFFSET)), )?; + init_intx( + self.name.clone(), + &mut self.pci_config, + self.parent_bus.clone(), + self.devfn, + )?; + let mut mem_region_size = (XHCI_PCI_CONFIG_LENGTH as u64).next_power_of_two(); mem_region_size = max(mem_region_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); self.pci_config.register_bar( @@ -222,14 +231,22 @@ impl PciDevOps for XhciPciDevice { let devfn = self.devfn; // It is safe to unwrap, because it is initialized in init_msix. let cloned_msix = self.pci_config.msix.as_ref().unwrap().clone(); + let cloned_intx = self.pci_config.intx.as_ref().unwrap().clone(); let cloned_dev_id = self.dev_id.clone(); // Registers the msix to the xhci device for interrupt notification. - self.xhci.lock().unwrap().send_interrupt_ops = Some(Box::new(move |n: u32| { - cloned_msix - .lock() - .unwrap() - .notify(n as u16, cloned_dev_id.load(Ordering::Acquire)); - })); + self.xhci.lock().unwrap().send_interrupt_ops = + Some(Box::new(move |n: u32, level: u8| -> bool { + let mut locked_msix = cloned_msix.lock().unwrap(); + if locked_msix.enabled && level != 0 { + locked_msix.notify(n as u16, cloned_dev_id.load(Ordering::Acquire)); + return true; + } + if n == 0 && !locked_msix.enabled { + cloned_intx.lock().unwrap().notify(level); + } + + false + })); let dev = Arc::new(Mutex::new(self)); // Attach to the PCI bus. let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 12863f3a6..55e6a0bd2 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -93,6 +93,7 @@ ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); +ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] @@ -177,6 +178,14 @@ impl KVMFds { .with_context(|| format!("Failed to unregister irqfd: gsi {}.", gsi)) } + pub fn set_irq_line(&self, irq: u32, level: bool) -> Result<()> { + self.vm_fd + .as_ref() + .unwrap() + .set_irq_line(irq, level) + .with_context(|| format!("Failed to set irq {} level {:?}.", irq, level)) + } + /// Start dirty page tracking in kvm. pub fn start_dirty_log(&self) -> Result<()> { for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 84c626ed3..926fda5ba 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -25,6 +25,8 @@ use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; use vmm_sys_util::eventfd::EventFd; +use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; + use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, @@ -47,7 +49,7 @@ use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; -use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; +use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, @@ -60,9 +62,9 @@ use machine_manager::machine::{ }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; -use pci::{PciDevOps, PciHost}; +use pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use pci_host_root::PciHostRoot; -use sysbus::{SysBus, SysBusDevType, SysRes, IRQ_BASE, IRQ_MAX}; +use sysbus::{SysBus, SysBusDevType, SysRes}; use syscall::syscall_whitelist; #[cfg(not(target_env = "musl"))] use ui::vnc; @@ -114,6 +116,18 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (512 << 30, 512 << 30), // HighPcieMmio ]; +/// The type of Irq entry on aarch64 +enum IrqEntryType { + Sysbus, + Pcie, +} + +/// IRQ MAP of aarch64 +const IRQ_MAP: &[(i32, i32)] = &[ + (5, 15), // Sysbus + (16, 19), // Pcie +]; + /// Standard machine structure. pub struct StdMachine { /// `vCPU` topology, support sockets, cores, threads. @@ -166,7 +180,10 @@ impl StdMachine { .with_context(|| MachineError::CrtIoSpaceErr)?; let sysbus = SysBus::new( &sys_mem, - (IRQ_BASE, IRQ_MAX), + ( + IRQ_MAP[IrqEntryType::Sysbus as usize].0, + IRQ_MAP[IrqEntryType::Sysbus as usize].1, + ), ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, @@ -186,6 +203,7 @@ impl StdMachine { MEM_LAYOUT[LayoutEntryType::PcieMmio as usize], MEM_LAYOUT[LayoutEntryType::PciePio as usize], MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize], + IRQ_MAP[IrqEntryType::Pcie as usize].0, ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), @@ -416,6 +434,24 @@ impl MachineOps for StdMachine { .unwrap() .init_irq_route_table(); KVM_FDS.load().commit_irq_routing()?; + + let root_bus = &self.pci_host.lock().unwrap().root_bus; + let irq_handler = Box::new(move |gsi: u32, level: bool| -> Result<()> { + // The handler is only used to send PCI INTx interrupt. + // PCI INTx interrupt is belong to SPI interrupt type. + let irq = gsi + GIC_IRQ_INTERNAL; + let irqtype = KVM_ARM_IRQ_TYPE_SPI; + let kvm_irq = irqtype << KVM_ARM_IRQ_TYPE_SHIFT | irq; + + KVM_FDS.load().set_irq_line(kvm_irq, level) + }) as InterruptHandler; + + let irq_state = Some(Arc::new(Mutex::new(PciIntxState::new( + IRQ_MAP[IrqEntryType::Pcie as usize].0 as u32, + irq_handler, + )))); + root_bus.lock().unwrap().intx_state = irq_state; + Ok(()) } diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index c3ec2252f..7fede1aac 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -244,6 +244,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) } fn madvise_rule() -> BpfRule { diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 6473b3cd9..706887d60 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -74,7 +74,7 @@ impl LPCBridge { }) } - fn update_pm_base(&self) -> Result<()> { + fn update_pm_base(&mut self) -> Result<()> { let cloned_pmtmr = self.pm_timer.clone(); let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { cloned_pmtmr.lock().unwrap().read(data, addr, offset) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 01227b489..0b1139c31 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -51,7 +51,7 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use pci::{PciDevOps, PciHost}; -use sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; +use sysbus::SysBus; use syscall::syscall_whitelist; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, @@ -94,6 +94,21 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0x1_0000_0000, 0x80_0000_0000), // MemAbove4g ]; +/// The type of Irq entry on aarch64 +enum IrqEntryType { + #[allow(unused)] + Uart, + Sysbus, + Pcie, +} + +/// IRQ MAP of x86_64 +const IRQ_MAP: &[(i32, i32)] = &[ + (4, 4), // Uart + (5, 15), // Sysbus + (16, 19), // Pcie +]; + /// Standard machine structure. pub struct StdMachine { /// `vCPU` topology, support sockets, cores, threads. @@ -144,7 +159,10 @@ impl StdMachine { let sysbus = SysBus::new( &sys_io, &sys_mem, - (IRQ_BASE, IRQ_MAX), + ( + IRQ_MAP[IrqEntryType::Sysbus as usize].0, + IRQ_MAP[IrqEntryType::Sysbus as usize].1, + ), ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, @@ -164,6 +182,7 @@ impl StdMachine { &sys_mem, MEM_LAYOUT[LayoutEntryType::PcieEcam as usize], MEM_LAYOUT[LayoutEntryType::PcieMmio as usize], + IRQ_MAP[IrqEntryType::Pcie as usize].0, ))), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 749e961f5..225b551cc 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -16,11 +16,12 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::Region; use log::debug; -use super::config::{ - BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET, SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM, +use super::{ + config::{BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET, SECONDARY_BUS_NUM, SUBORDINATE_BUS_NUM}, + hotplug::HotplugOps, + PciDevOps, PciIntxState, }; -use super::hotplug::HotplugOps; -use super::PciDevOps; + use anyhow::{bail, Context, Result}; type DeviceBusInfo = (Arc>, Arc>); @@ -42,6 +43,8 @@ pub struct PciBus { pub mem_region: Region, /// Hot Plug controller for obtaining hot plug ops. pub hotplug_controller: Option>>, + /// Interrupt info related to INTx. + pub intx_state: Option>>, } impl PciBus { @@ -66,6 +69,7 @@ impl PciBus { io_region, mem_region, hotplug_controller: None, + intx_state: None, } } @@ -332,6 +336,7 @@ mod tests { (0xF000_0000, 0x1000_0000), #[cfg(target_arch = "aarch64")] (512 << 30, 512 << 30), + 16, ))) } diff --git a/pci/src/config.rs b/pci/src/config.rs index 12fb5602c..cde5f043b 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -16,7 +16,8 @@ use std::sync::{Arc, Mutex}; use address_space::Region; use log::{error, warn}; -use crate::msix::Msix; +use crate::intx::Intx; +use crate::msix::{is_msix_enabled, Msix}; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, @@ -104,6 +105,7 @@ pub const IO_LIMIT: u8 = 0x1d; pub const PREF_MEM_BASE_UPPER: u8 = 0x28; const CAP_LIST: u8 = 0x34; const INTERRUPT_LINE: u8 = 0x3c; +pub const INTERRUPT_PIN: u8 = 0x3d; pub const BRIDGE_CONTROL: u8 = 0x3e; const BRIDGE_CTL_PARITY_ENABLE: u16 = 0x0001; @@ -256,6 +258,7 @@ pub const PCI_EXP_SLOTSTA_EVENTS: u16 = PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC; +pub const PCI_EXP_HP_EV_SPT: u16 = PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_CCIE; // System error on correctable error enable. const PCI_EXP_RTCTL_SECEE: u16 = 0x01; @@ -390,6 +393,8 @@ pub struct PciConfig { pub msix: Option>>, /// Offset of the PCI express capability. pub pci_express_cap_offset: u16, + /// INTx infomation. + pub intx: Option>>, } impl PciConfig { @@ -422,6 +427,7 @@ impl PciConfig { last_ext_cap_end: PCI_CONFIG_SPACE_SIZE as u16, msix: None, pci_express_cap_offset: PCI_CONFIG_HEAD_END as u16, + intx: None, } } @@ -506,11 +512,26 @@ impl PciConfig { /// /// * `offset` - Offset in the configuration space from which to read. /// * `data` - Buffer to put read data. - pub fn read(&self, offset: usize, buf: &mut [u8]) { + pub fn read(&mut self, offset: usize, buf: &mut [u8]) { if let Err(err) = self.validate_config_boundary(offset, buf) { warn!("invalid read: {:?}", err); return; + }; + if ranges_overlap( + offset, + offset + buf.len(), + STATUS as usize, + (STATUS + 1) as usize, + ) { + if let Some(intx) = &self.intx { + if intx.lock().unwrap().level == 1 { + self.config[STATUS as usize] |= STATUS_INTERRUPT; + } else { + self.config[STATUS as usize] &= !STATUS_INTERRUPT; + } + } } + let size = buf.len(); buf[..].copy_from_slice(&self.config[offset..(offset + size)]); } @@ -569,7 +590,8 @@ impl PciConfig { _ => (BAR_NUM_MAX_FOR_ENDPOINT, ROM_ADDRESS_ENDPOINT), }; - if ranges_overlap(old_offset, end, COMMAND as usize, (COMMAND + 1) as usize) + let cmd_overlap = ranges_overlap(old_offset, end, COMMAND as usize, (COMMAND + 1) as usize); + if cmd_overlap || ranges_overlap( old_offset, end, @@ -587,7 +609,30 @@ impl PciConfig { } } + if cmd_overlap { + if let Some(intx) = &self.intx { + let cmd = le_read_u16(self.config.as_slice(), COMMAND as usize).unwrap(); + let enabled = cmd & COMMAND_INTERRUPT_DISABLE == 0; + + let mut locked_intx = intx.lock().unwrap(); + if locked_intx.enabled != enabled { + if !enabled { + locked_intx.change_irq_level(-(locked_intx.level as i8)); + } else { + locked_intx.change_irq_level(locked_intx.level as i8); + } + locked_intx.enabled = enabled; + } + } + } + if let Some(msix) = &mut self.msix { + if is_msix_enabled(msix.lock().unwrap().msix_cap_offset as usize, &self.config) { + if let Some(intx) = &self.intx { + intx.lock().unwrap().notify(0); + } + } + msix.lock() .unwrap() .write_config(&self.config, dev_id, old_offset, data); @@ -627,6 +672,10 @@ impl PciConfig { /// General reset process for pci devices pub fn reset(&mut self) -> Result<()> { + if let Some(intx) = &self.intx { + intx.lock().unwrap().reset(); + } + self.reset_common_regs()?; if let Err(e) = self.update_bar_mapping( @@ -1126,6 +1175,10 @@ impl PciConfig { } Ok(()) } + + pub fn set_interrupt_pin(&mut self) { + self.config[INTERRUPT_PIN as usize] = 0x01; + } } #[cfg(test)] diff --git a/pci/src/host.rs b/pci/src/host.rs index 6ea45b454..03650fa8c 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -13,10 +13,12 @@ use std::sync::{Arc, Mutex}; use acpi::{ - AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlCacheable, AmlCreateDWordField, - AmlDWordDesc, AmlDevice, AmlEisaId, AmlElse, AmlEqual, AmlISARanges, AmlIf, AmlInteger, - AmlLNot, AmlLocal, AmlMethod, AmlName, AmlNameDecl, AmlOr, AmlReadAndWrite, AmlResTemplate, - AmlReturn, AmlScopeBuilder, AmlStore, AmlToUuid, AmlWordDesc, AmlZero, + AmlActiveLevel, AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlCacheable, + AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlElse, + AmlEqual, AmlExtendedInterrupt, AmlISARanges, AmlIf, AmlIntShare, AmlInteger, AmlLNot, + AmlLocal, AmlMethod, AmlName, AmlNameDecl, AmlOr, AmlPackage, AmlReadAndWrite, AmlResTemplate, + AmlResourceUsage, AmlReturn, AmlScopeBuilder, AmlStore, AmlString, AmlToUuid, AmlWordDesc, + AmlZero, }; #[cfg(target_arch = "x86_64")] use acpi::{AmlIoDecode, AmlIoResource}; @@ -26,7 +28,10 @@ use address_space::{AddressSpace, GuestAddress, RegionOps}; use anyhow::Context; use sysbus::SysBusDevOps; -use crate::{bus::PciBus, PciDevOps}; +#[cfg(target_arch = "aarch64")] +use crate::PCI_INTR_BASE; +use crate::{bus::PciBus, PciDevOps, PCI_PIN_NUM, PCI_SLOT_MAX}; + #[cfg(target_arch = "x86_64")] use crate::{le_read_u32, le_write_u32}; @@ -56,6 +61,7 @@ pub struct PciHost { pcie_pio_range: (u64, u64), #[cfg(target_arch = "aarch64")] high_pcie_mmio_range: (u64, u64), + pub intx_gsi_base: i32, } impl PciHost { @@ -69,6 +75,7 @@ impl PciHost { /// * `pcie_mmio_range` - PCIe MMIO base address and length. /// * `pcie_pio_range` - PCIe PIO base addreass and length (only on aarch64). /// * `high_pcie_mmio_range` - PCIe high MMIO base address and length (only on aarch64). + /// * `intx_gsi_base` - PCIe INTx gsi base. pub fn new( #[cfg(target_arch = "x86_64")] sys_io: &Arc, sys_mem: &Arc, @@ -76,6 +83,7 @@ impl PciHost { pcie_mmio_range: (u64, u64), #[cfg(target_arch = "aarch64")] pcie_pio_range: (u64, u64), #[cfg(target_arch = "aarch64")] high_pcie_mmio_range: (u64, u64), + intx_gsi_base: i32, ) -> Self { #[cfg(target_arch = "x86_64")] let io_region = sys_io.root().clone(); @@ -96,6 +104,7 @@ impl PciHost { pcie_pio_range, #[cfg(target_arch = "aarch64")] high_pcie_mmio_range, + intx_gsi_base, } } @@ -361,6 +370,53 @@ fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { pci_host_bridge.append_child(method); } +fn build_prt_for_aml(pci_bus: &mut AmlDevice, irq: i32) { + let mut prt_pkg = AmlPackage::new(PCI_SLOT_MAX * PCI_PIN_NUM); + (0..PCI_SLOT_MAX).for_each(|slot| { + (0..PCI_PIN_NUM).for_each(|pin| { + let gsi = (pin + slot) % PCI_PIN_NUM; + let mut pkg = AmlPackage::new(4); + pkg.append_child(AmlDWord(((slot as u32) << 16) as u32 | 0xFFFF)); + pkg.append_child(AmlDWord(pin as u32)); + pkg.append_child(AmlName(format!("GSI{}", gsi))); + pkg.append_child(AmlZero); + prt_pkg.append_child(pkg); + }); + }); + pci_bus.append_child(AmlNameDecl::new("_PRT", prt_pkg)); + + for i in 0..PCI_PIN_NUM { + #[cfg(target_arch = "x86_64")] + let irqs = irq as u8 + i; + #[cfg(target_arch = "aarch64")] + let irqs = irq as u8 + PCI_INTR_BASE + i; + let mut gsi = AmlDevice::new(format!("GSI{}", i).as_str()); + gsi.append_child(AmlNameDecl::new("_HID", AmlEisaId::new("PNP0C0F"))); + gsi.append_child(AmlNameDecl::new("_UID", AmlString(i.to_string()))); + let mut crs = AmlResTemplate::new(); + crs.append_child(AmlExtendedInterrupt::new( + AmlResourceUsage::Consumer, + AmlEdgeLevel::Level, + AmlActiveLevel::High, + AmlIntShare::Exclusive, + vec![irqs as u32], + )); + gsi.append_child(AmlNameDecl::new("_PRS", crs)); + let mut crs = AmlResTemplate::new(); + crs.append_child(AmlExtendedInterrupt::new( + AmlResourceUsage::Consumer, + AmlEdgeLevel::Level, + AmlActiveLevel::High, + AmlIntShare::Exclusive, + vec![irqs as u32], + )); + gsi.append_child(AmlNameDecl::new("_CRS", crs)); + let method = AmlMethod::new("_SRS", 1, false); + gsi.append_child(method); + pci_bus.append_child(gsi); + } +} + impl AmlBuilder for PciHost { fn aml_bytes(&self) -> Vec { let mut pci_host_bridge = AmlDevice::new("PCI0"); @@ -454,6 +510,8 @@ impl AmlBuilder for PciHost { )); pci_host_bridge.append_child(AmlNameDecl::new("_CRS", crs)); + build_prt_for_aml(&mut pci_host_bridge, self.intx_gsi_base); + pci_host_bridge.aml_bytes() } } @@ -547,6 +605,7 @@ pub mod tests { (0xF000_0000, 0x1000_0000), #[cfg(target_arch = "aarch64")] (512 << 30, 512 << 30), + 16, ))) } diff --git a/pci/src/intx.rs b/pci/src/intx.rs new file mode 100644 index 000000000..2a736347a --- /dev/null +++ b/pci/src/intx.rs @@ -0,0 +1,162 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex, Weak}; + +use anyhow::Result; +use log::error; + +use crate::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_PIN_NUM}; + +pub type InterruptHandler = Box Result<()> + Send + Sync>; + +/// PCI INTx information. +pub struct PciIntxState { + /// Gsi of PCI bus INT#A. + pub gsi_base: u32, + /// INTx IRQ numbers to be asserted of every INTx virtual interrupt line. + pub irq_count: [i8; PCI_PIN_NUM as usize], + /// Handler of asserting the INTx IRQ. + pub irq_handler: InterruptHandler, +} + +impl PciIntxState { + pub fn new(gsi_base: u32, irq_handler: InterruptHandler) -> Self { + Self { + gsi_base, + irq_count: [0; PCI_PIN_NUM as usize], + irq_handler, + } + } +} + +/// INTx structure. +pub struct Intx { + /// Deivce name. + pub device_name: String, + /// Physical interrupt pin. + pub irq_pin: u32, + /// Interrupt level. + pub level: u8, + /// Driver enable status. + pub enabled: bool, + /// Interrupt info related to INTx. + pub intx_state: Option>>, +} + +impl Intx { + pub fn new(name: String, irq_pin: u32, intx_state: Option>>) -> Self { + Self { + device_name: name, + irq_pin, + level: 0, + enabled: true, + intx_state, + } + } + + pub fn notify(&mut self, level: u8) { + assert!(level == 0 || level == 1); + if self.level == level { + return; + }; + + let change: i8 = level as i8 - self.level as i8; + self.level = level; + if !self.enabled { + error!( + "INTx is disabled, failed to set irq INTx interrupt for {}.", + self.device_name + ); + return; + } + + self.change_irq_level(change); + } + + pub fn change_irq_level(&self, change: i8) { + if let Some(intx_state) = &self.intx_state { + let mut locked_intx_state = intx_state.lock().unwrap(); + locked_intx_state.irq_count[self.irq_pin as usize] += change; + if locked_intx_state.irq_count[self.irq_pin as usize] < 0 { + locked_intx_state.irq_count[self.irq_pin as usize] = 0; + } + + let irq = locked_intx_state.gsi_base + self.irq_pin; + let level = locked_intx_state.irq_count[self.irq_pin as usize] != 0; + if let Err(e) = (locked_intx_state.irq_handler)(irq, level) { + error!( + "Failed to set irq {} level {} of device {}: {}.", + irq, level, self.device_name, e + ); + } + } else { + error!( + "Can't set irq pin {} for {}, the INTx handler is not initialized", + self.irq_pin, self.device_name + ); + }; + } + + pub fn reset(&mut self) { + self.notify(0); + self.enabled = true; + } +} + +pub fn init_intx( + name: String, + config: &mut PciConfig, + parent_bus: Weak>, + devfn: u8, +) -> Result<()> { + if config.config[INTERRUPT_PIN as usize] == 0 { + let (irq, intx_state) = (std::u32::MAX, None); + let intx = Arc::new(Mutex::new(Intx::new(name, irq, intx_state))); + config.intx = Some(intx); + return Ok(()); + } + + let (irq, intx_state) = if let Some(pci_bus) = parent_bus.upgrade() { + let locked_pci_bus = pci_bus.lock().unwrap(); + let pin = config.config[INTERRUPT_PIN as usize] - 1; + + let (irq, intx_state) = match &locked_pci_bus.parent_bridge { + Some(parent_bridge) => { + let parent_bridge = parent_bridge.upgrade().unwrap(); + let locked_parent_bridge = parent_bridge.lock().unwrap(); + ( + swizzle_map_irq(locked_parent_bridge.devfn().unwrap(), pin), + locked_parent_bridge.get_intx_state(), + ) + } + None => { + if locked_pci_bus.intx_state.is_some() { + ( + swizzle_map_irq(devfn, pin), + Some(locked_pci_bus.intx_state.as_ref().unwrap().clone()), + ) + } else { + (std::u32::MAX, None) + } + } + }; + (irq, intx_state) + } else { + (std::u32::MAX, None) + }; + + let intx = Arc::new(Mutex::new(Intx::new(name, irq, intx_state))); + + config.intx = Some(intx); + Ok(()) +} diff --git a/pci/src/lib.rs b/pci/src/lib.rs index be3031cb6..cf510bcca 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -15,6 +15,7 @@ pub use error::PciError; pub mod config; pub mod demo_dev; pub mod hotplug; +pub mod intx; pub mod msix; mod bus; @@ -23,8 +24,10 @@ mod host; mod root_port; pub use bus::PciBus; +pub use config::{PciConfig, INTERRUPT_PIN}; pub use host::PciHost; -pub use msix::init_msix; +pub use intx::{init_intx, InterruptHandler, PciIntxState}; +pub use msix::{init_msix, is_msix_enabled}; pub use root_port::RootPort; use util::AsAny; @@ -39,6 +42,9 @@ use byteorder::{ByteOrder, LittleEndian}; use crate::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; const BDF_FUNC_SHIFT: u8 = 3; +pub const PCI_SLOT_MAX: u8 = 32; +pub const PCI_PIN_NUM: u8 = 4; +pub const PCI_INTR_BASE: u8 = 32; /// Macros that write data in little endian. macro_rules! le_write { @@ -221,6 +227,14 @@ pub trait PciDevOps: Send + AsAny { fn get_dev_path(&self) -> Option { None } + + fn change_irq_level(&self, _irq_pin: u32, _level: i8) -> Result<()> { + Ok(()) + } + + fn get_intx_state(&self) -> Option>> { + None + } } /// Init multifunction for pci devices. @@ -293,6 +307,13 @@ pub fn init_multifunction( Ok(()) } +/// 0 <= pin <= 3. 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD. +/// PCI-to-PCI bridge specification 9.1: Interrupt routing. +pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { + let pci_slot = devfn >> 3 & 0x1f; + ((pci_slot + pin) % PCI_PIN_NUM) as u32 +} + /// Check whether two regions overlap with each other. /// /// # Arguments diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index eb0e0a7ba..dff70c5ce 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -28,18 +28,20 @@ use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, - PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_LNKSTA, + PCI_EXP_HP_EV_ABP, PCI_EXP_HP_EV_CCI, PCI_EXP_HP_EV_PDC, PCI_EXP_HP_EV_SPT, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_CLS_2_5GB, PCI_EXP_LNKSTA_DLLLA, PCI_EXP_LNKSTA_NLW_X1, PCI_EXP_SLOTSTA_EVENTS, - PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, PCI_EXP_SLTCTL_PWR_IND_BLINK, - PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, - PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, SUB_CLASS_CODE, VENDOR_ID, + PCI_EXP_SLTCTL, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PIC, + PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PWR_IND_ON, + PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC, PCI_EXP_SLTSTA_PDS, + PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, + SUB_CLASS_CODE, VENDOR_ID, }; use crate::bus::PciBus; use crate::config::{BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET}; use crate::hotplug::HotplugOps; +use crate::intx::init_intx; use crate::msix::init_msix; -use crate::{init_multifunction, PciError}; +use crate::{init_multifunction, PciError, PciIntxState, INTERRUPT_PIN}; use crate::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, PciDevOps, @@ -75,6 +77,7 @@ pub struct RootPort { mem_region: Region, dev_id: Arc, multifunction: bool, + hpev_notified: bool, } impl RootPort { @@ -115,6 +118,7 @@ impl RootPort { mem_region, dev_id: Arc::new(AtomicU16::new(0)), multifunction, + hpev_notified: false, } } @@ -129,13 +133,43 @@ impl RootPort { } } + fn update_hp_event_status(&mut self) { + let cap_offset = self.config.pci_express_cap_offset; + let slot_status = + le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let slot_control = + le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize).unwrap(); + + self.hpev_notified = (slot_control & PCI_EXP_SLTCTL_HPIE != 0) + && (slot_status & slot_control & PCI_EXP_HP_EV_SPT != 0); + } + fn hotplug_event_notify(&mut self) { - if let Some(msix) = self.config.msix.as_mut() { - msix.lock() - .unwrap() - .notify(0, self.dev_id.load(Ordering::Acquire)); - } else { - error!("Failed to send interrupt: msix does not exist"); + let last_event = self.hpev_notified; + self.update_hp_event_status(); + if last_event == self.hpev_notified { + return; + } + + let msix = self.config.msix.as_ref().unwrap(); + let intx = self.config.intx.as_ref().unwrap(); + let mut locked_msix = msix.lock().unwrap(); + if locked_msix.enabled { + locked_msix.notify(0, self.dev_id.load(Ordering::Acquire)); + } else if self.config.config[INTERRUPT_PIN as usize] != 0 { + intx.lock().unwrap().notify(self.hpev_notified as u8); + } + } + + fn hotplug_event_clear(&mut self) { + self.update_hp_event_status(); + + let msix = self.config.msix.as_ref().unwrap(); + let intx = self.config.intx.as_ref().unwrap(); + let locked_msix = msix.lock().unwrap(); + let intr_pin = self.config.config[INTERRUPT_PIN as usize]; + if !locked_msix.enabled && intr_pin != 0 && !self.hpev_notified { + intx.lock().unwrap().notify(0); } } @@ -316,12 +350,17 @@ impl PciDevOps for RootPort { config_space[HEADER_TYPE as usize] = HEADER_TYPE_BRIDGE; config_space[PREF_MEMORY_BASE as usize] = PREF_MEM_RANGE_64BIT; config_space[PREF_MEMORY_LIMIT as usize] = PREF_MEM_RANGE_64BIT; + init_multifunction( self.multifunction, config_space, self.devfn, self.parent_bus.clone(), )?; + + #[cfg(target_arch = "aarch64")] + self.config.set_interrupt_pin(); + self.config .add_pcie_cap(self.devfn, self.port_num, PcieDevType::RootPort as u8)?; @@ -336,6 +375,13 @@ impl PciDevOps for RootPort { None, )?; + init_intx( + self.name.clone(), + &mut self.config, + self.parent_bus.clone(), + self.devfn, + )?; + let parent_bus = self.parent_bus.upgrade().unwrap(); let mut locked_parent_bus = parent_bus.lock().unwrap(); #[cfg(target_arch = "x86_64")] @@ -432,6 +478,23 @@ impl PciDevOps for RootPort { self.register_region(); } + let mut status = + le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let exp_slot_status = (cap_offset + PCI_EXP_SLTSTA) as usize; + if ranges_overlap(offset, end, exp_slot_status, exp_slot_status + 2) { + let new_status = le_read_u16(data, 0).unwrap(); + if new_status & !old_status & PCI_EXP_SLOTSTA_EVENTS != 0 { + status = (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); + if let Err(e) = le_write_u16( + &mut self.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + status, + ) { + error!("Failed to write config: {:?}", e); + } + } + self.hotplug_event_clear(); + } self.do_unplug(offset, data, old_ctl, old_status); } @@ -439,6 +502,10 @@ impl PciDevOps for RootPort { self.name.clone() } + fn devfn(&self) -> Option { + Some(self.devfn) + } + /// Only set slot status to on, and no other device reset actions are implemented. fn reset(&mut self, reset_child_device: bool) -> Result<()> { if reset_child_device { @@ -465,8 +532,9 @@ impl PciDevOps for RootPort { PCI_EXP_LNKSTA_DLLLA, )?; } + self.config.reset_bridge_regs()?; - self.config.reset_common_regs() + self.config.reset() } fn get_dev_path(&self) -> Option { @@ -475,6 +543,16 @@ impl PciDevOps for RootPort { let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/pci-bridge@"); Some(dev_path) } + + fn get_intx_state(&self) -> Option>> { + let intx = self.config.intx.as_ref().unwrap(); + if intx.lock().unwrap().intx_state.is_some() { + let intx_state = intx.lock().unwrap().intx_state.as_ref().unwrap().clone(); + return Some(intx_state); + } + + None + } } impl HotplugOps for RootPort { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 98d43ca92..0435b39f2 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -26,11 +26,12 @@ use pci::config::{ PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; + use pci::msix::{update_dev_id, MsixState, MSIX_TABLE_ENTRY_SIZE}; use pci::Result as PciResult; use pci::{ - config::PciConfig, init_msix, init_multifunction, le_write_u16, le_write_u32, ranges_overlap, - PciBus, PciDevOps, PciError, + config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, + ranges_overlap, PciBus, PciDevOps, PciError, }; use util::byte_code::ByteCode; use util::num_ops::{read_data_u32, write_data_u32}; @@ -644,7 +645,8 @@ impl VirtioPciDevice { fn assign_interrupt_cb(&mut self) { let cloned_common_cfg = self.common_config.clone(); - let cloned_msix = self.config.msix.clone(); + let cloned_msix = self.config.msix.as_ref().unwrap().clone(); + let cloned_intx = self.config.intx.as_ref().unwrap().clone(); let dev_id = self.dev_id.clone(); let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, queue: Option<&Queue>, needs_reset: bool| { @@ -665,17 +667,19 @@ impl VirtioPciDevice { locked_common_cfg.msix_config } VirtioInterruptType::Vring => { + let mut locked_common_cfg = cloned_common_cfg.lock().unwrap(); + locked_common_cfg.interrupt_status |= VIRTIO_MMIO_INT_VRING; queue.map_or(0, |q| q.vring.get_queue_config().vector) } }; - if let Some(msix) = &cloned_msix { - msix.lock() - .unwrap() - .notify(vector, dev_id.load(Ordering::Acquire)); + let mut locked_msix = cloned_msix.lock().unwrap(); + if locked_msix.enabled { + locked_msix.notify(vector, dev_id.load(Ordering::Acquire)); } else { - bail!("Failed to send interrupt, msix does not exist"); + cloned_intx.lock().unwrap().notify(1); } + Ok(()) }, ) as VirtioInterrupt); @@ -866,11 +870,13 @@ impl VirtioPciDevice { // 2. PCI ISR cap sub-region. let cloned_common_cfg = self.common_config.clone(); + let cloned_intx = self.config.intx.as_ref().unwrap().clone(); let isr_read = move |data: &mut [u8], _: GuestAddress, _: u64| -> bool { if let Some(val) = data.get_mut(0) { let mut common_cfg_lock = cloned_common_cfg.lock().unwrap(); *val = common_cfg_lock.interrupt_status as u8; common_cfg_lock.interrupt_status = 0; + cloned_intx.lock().unwrap().notify(0); } true }; @@ -1075,6 +1081,8 @@ impl PciDevOps for VirtioPciDevice { self.devfn, self.parent_bus.clone(), )?; + #[cfg(target_arch = "aarch64")] + self.config.set_interrupt_pin(); let common_cap = VirtioPciCap::new( size_of::() as u8 + PCI_CAP_VNDR_AND_NEXT_SIZE, @@ -1142,6 +1150,13 @@ impl PciDevOps for VirtioPciDevice { None, )?; + init_intx( + self.name.clone(), + &mut self.config, + self.parent_bus.clone(), + self.devfn, + )?; + self.assign_interrupt_cb(); let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) @@ -1738,9 +1753,9 @@ mod tests { Arc::downgrade(&parent_bus), false, ); + #[cfg(target_arch = "aarch64")] + virtio_pci.config.set_interrupt_pin(); - // Prepare msix and interrupt callback - virtio_pci.assign_interrupt_cb(); init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, virtio_pci.device.lock().unwrap().queue_num() as u32 + 1, @@ -1751,6 +1766,17 @@ mod tests { None, ) .unwrap(); + + init_intx( + virtio_pci.name.clone(), + &mut virtio_pci.config, + virtio_pci.parent_bus.clone(), + virtio_pci.devfn, + ) + .unwrap(); + // Prepare msix and interrupt callback + virtio_pci.assign_interrupt_cb(); + // Prepare valid queue config for queue_cfg in virtio_pci .common_config -- Gitee From 0f6118ddb63b221b4a12c5e48fa3cef0678eb23b Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 8 Apr 2023 21:44:33 +0800 Subject: [PATCH 0987/2187] PCI-MST: Add two MST testcases for INTx Add two MST testcases. One testcase is for testing the basic function of INTx. The other testcase is for testing the interrupt disable bit of the command register that is used to control the ability of a Function to generate INTx emulation interrups. Add some functions to simulate interrupt triggering and EOI. Signed-off-by: Jinhao Gao --- machine_manager/src/test_server.rs | 18 +- pci/src/intx.rs | 9 +- tests/mod_test/src/libdriver/pci.rs | 67 +++ tests/mod_test/src/libdriver/virtio.rs | 1 + .../src/libdriver/virtio_pci_modern.rs | 11 +- tests/mod_test/src/libtest.rs | 31 ++ tests/mod_test/tests/pci_test.rs | 485 +++++++++++++++--- util/src/test_helper.rs | 55 ++ 8 files changed, 612 insertions(+), 65 deletions(-) diff --git a/machine_manager/src/test_server.rs b/machine_manager/src/test_server.rs index cb957d37c..73541a7f0 100644 --- a/machine_manager/src/test_server.rs +++ b/machine_manager/src/test_server.rs @@ -20,7 +20,7 @@ use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::sync::{Arc, Mutex}; use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; -use util::test_helper::{get_test_clock, has_msix_msg, set_test_clock}; +use util::test_helper::{eoi_intx, get_test_clock, has_msix_msg, query_intx, set_test_clock}; use vmm_sys_util::epoll::EventSet; pub struct TestSock { @@ -227,6 +227,22 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc handler.send_str("OK FALSE".to_string().as_str()).unwrap(), } } + "query_intx" => { + assert!(cmd.len() == 2); + let irq = cmd[1].parse::().unwrap(); + match query_intx(irq) { + true => handler.send_str("OK TRUE".to_string().as_str()).unwrap(), + false => handler.send_str("OK FALSE".to_string().as_str()).unwrap(), + } + } + "eoi_intx" => { + assert!(cmd.len() == 2); + let irq = cmd[1].parse::().unwrap(); + match eoi_intx(irq) { + true => handler.send_str("OK TRUE".to_string().as_str()).unwrap(), + false => handler.send_str("OK FALSE".to_string().as_str()).unwrap(), + } + } _ => { handler .send_str(format!("Unsupported command: {}", cmd[0]).as_str()) diff --git a/pci/src/intx.rs b/pci/src/intx.rs index 2a736347a..7e545acd7 100644 --- a/pci/src/intx.rs +++ b/pci/src/intx.rs @@ -14,8 +14,9 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use log::error; +use util::test_helper::{is_test_enabled, trigger_intx}; -use crate::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_PIN_NUM}; +use crate::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_INTR_BASE, PCI_PIN_NUM}; pub type InterruptHandler = Box Result<()> + Send + Sync>; @@ -93,6 +94,12 @@ impl Intx { let irq = locked_intx_state.gsi_base + self.irq_pin; let level = locked_intx_state.irq_count[self.irq_pin as usize] != 0; + + if is_test_enabled() { + trigger_intx(irq + PCI_INTR_BASE as u32, change); + return; + } + if let Err(e) = (locked_intx_state.irq_handler)(irq, level) { error!( "Failed to set irq {} level {} of device {}: {}.", diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 9d7ce657d..5aad32b81 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -16,6 +16,7 @@ use std::cell::RefCell; use std::rc::Rc; const BAR_MAP: [u8; 6] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; +const PCI_PIN_NUM: u8 = 4; pub const PCI_VENDOR_ID: u8 = 0x00; pub const PCI_DEVICE_ID: u8 = 0x02; pub const PCI_COMMAND: u8 = 0x04; @@ -23,6 +24,7 @@ pub const PCI_COMMAND: u8 = 0x04; const PCI_COMMAND_IO: u8 = 0x1; pub const PCI_COMMAND_MEMORY: u8 = 0x2; const PCI_COMMAND_MASTER: u8 = 0x4; +pub const PCI_COMMAND_INTX_DISABLE: u16 = 0x400; pub const PCI_STATUS: u8 = 0x06; pub const PCI_STATUS_INTERRUPT: u16 = 0x08; @@ -37,6 +39,7 @@ pub const PCI_SUBSYSTEM_VENDOR_ID: u8 = 0x2c; pub const PCI_SUBSYSTEM_ID: u8 = 0x2e; pub const PCI_CAPABILITY_LIST: u8 = 0x34; +pub const PCI_INTERRUPT_PIN: u8 = 0x3d; pub const PCI_BRIDGE_CONTROL: u8 = 0x3e; pub const BRIDGE_CTL_SEC_BUS_RESET: u8 = 0x40; @@ -73,6 +76,10 @@ pub const PCI_EXP_SLTSTA_CC: u16 = 0x0010; pub const PCI_EXP_SLTSTA_PDS: u16 = 0x0040; pub const PCI_EXP_SLTCTL: u8 = 0x18; +pub const PCI_EXP_SLTCTL_ABPE: u16 = 0x0001; +pub const PCI_EXP_SLTCTL_PDCE: u16 = 0x0008; +pub const PCI_EXP_SLTCTL_CCIE: u16 = 0x0010; +pub const PCI_EXP_SLTCTL_HPIE: u16 = 0x0020; pub const PCI_EXP_SLTCTL_PIC: u16 = 0x0300; pub const PCI_EXP_SLTCTL_PWR_IND_ON: u16 = 0x0100; pub const PCI_EXP_SLTCTL_PWR_IND_BLINK: u16 = 0x0200; @@ -83,6 +90,8 @@ pub const PCI_EXP_SLTCTL_PWR_OFF: u16 = 0x0400; pub type PCIBarAddr = u64; pub const INVALID_BAR_ADDR: u64 = u64::MAX; +const PCI_LINK_GSI: [u32; 4] = [48, 49, 50, 51]; + pub trait PciMsixOps { fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32); } @@ -98,6 +107,7 @@ pub struct TestPciDev { pub msix_table_off: u64, pub msix_pba_off: u64, pub msix_used_vectors: u32, + pub irq_num: u32, } impl TestPciDev { @@ -112,6 +122,7 @@ impl TestPciDev { msix_table_off: 0, msix_pba_off: 0, msix_used_vectors: 0, + irq_num: std::u32::MAX, } } @@ -149,6 +160,27 @@ impl TestPciDev { addr } + pub fn set_intx_irq_num(&mut self, slot: u8) { + let pin = ((self.config_readb(PCI_INTERRUPT_PIN) - 1 + slot) % PCI_PIN_NUM) as usize; + self.irq_num = PCI_LINK_GSI[pin]; + } + + pub fn has_intx(&self) -> bool { + self.pci_bus + .borrow() + .test_state + .borrow() + .query_intx(self.irq_num) + } + + pub fn eoi_intx(&self) { + self.pci_bus + .borrow() + .test_state + .borrow() + .eoi_intx(self.irq_num); + } + /// Enable MSI-X. /// /// # Arguments @@ -207,6 +239,41 @@ impl TestPciDev { (value & PCI_MSIX_MSG_CTL_TSIZE) + 1 } + pub fn init_notification(&self) { + let cap_exp_addr = self.find_capability(PCI_CAP_ID_EXP, 0); + let mut cmd = self.pci_bus.borrow().config_readw( + self.bus_num, + self.devfn, + cap_exp_addr + PCI_EXP_SLTCTL, + ); + cmd |= + PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE; + + self.pci_bus.borrow().config_writew( + self.bus_num, + self.devfn, + cap_exp_addr + PCI_EXP_SLTCTL, + cmd, + ); + } + + pub fn clear_slot_event(&self) { + let cap_exp_addr = self.find_capability(PCI_CAP_ID_EXP, 0); + let mut status = self.pci_bus.borrow().config_readw( + self.bus_num, + self.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + ); + + status &= PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC; + self.pci_bus.borrow().config_writew( + self.bus_num, + self.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + status, + ); + } + pub fn io_readb(&self, bar_addr: PCIBarAddr, offset: u64) -> u8 { let pci_bus = self.pci_bus.borrow_mut(); let value_bytes = pci_bus.memread((bar_addr + offset) as u32, 1); diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 547217bd0..9315f3cfe 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -49,6 +49,7 @@ pub trait VirtioDeviceOps { fn config_writew(&self, addr: u64, value: u16); fn config_writel(&self, addr: u64, value: u32); fn config_writeq(&self, addr: u64, value: u64); + fn isr_readb(&self) -> u8; fn enable_interrupt(&mut self); fn disable_interrupt(&mut self); fn get_device_features(&self) -> u64; diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index c093fbfb7..2783d83e7 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -329,6 +329,10 @@ impl VirtioDeviceOps for TestVirtioPciDev { .io_writeq(self.bar, self.device_base as u64 + addr, value) } + fn isr_readb(&self) -> u8 { + self.pci_dev.io_readb(self.bar, self.isr_base as u64) + } + fn enable_interrupt(&mut self) { self.pci_dev.enable_msix(None); } @@ -526,8 +530,11 @@ impl VirtioDeviceOps for TestVirtioPciDev { } fn queue_was_notified(&self, virtqueue: Rc>) -> bool { - assert!(self.pci_dev.msix_enabled); - return self.has_msix(virtqueue.borrow().msix_addr, virtqueue.borrow().msix_data); + if self.pci_dev.msix_enabled { + return self.has_msix(virtqueue.borrow().msix_addr, virtqueue.borrow().msix_data); + } + + self.pci_dev.has_intx() } fn setup_virtqueue( diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index d5a04217b..22d099b50 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -277,6 +277,37 @@ impl TestState { _ => panic!("Failed to execute {}.", cmd), } } + + pub fn query_intx(&self, irq: u32) -> bool { + let cmd = format!("query_intx {}", irq); + let buf = self.send_test_cmd(&cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + assert_eq!(resp.len(), 2); + + match resp[0] { + "OK" => match resp[1] { + "TRUE" => true, + "FALSE" => false, + _ => panic!("Failed to execute {}.", cmd), + }, + _ => panic!("Failed to execute {}.", cmd), + } + } + + pub fn eoi_intx(&self, irq: u32) -> bool { + let cmd = format!("eoi_intx {}", irq); + let buf = self.send_test_cmd(&cmd); + let resp: Vec<&str> = buf.split(' ').collect(); + assert_eq!(resp.len(), 2); + match resp[0] { + "OK" => match resp[1] { + "TRUE" => true, + "FALSE" => false, + _ => panic!("Failed to execute {}.", cmd), + }, + _ => panic!("Failed to execute {}.", cmd), + } + } } fn init_socket(path: &str) -> UnixListener { diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index d306187ff..75927fe85 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -32,7 +32,7 @@ const VIRTIO_PCI_VENDOR: u16 = 0x1af4; const BLK_DEVICE_ID: u16 = 0x1042; const MAX_DEVICE_NUM_IN_MULTIFUNC: u8 = 248; const MAX_DEVICE_NUM: u8 = 32; -const TIMEOUT_S: u64 = 5; +const TIMEOUT_S: u64 = 1; #[derive(Clone, Copy)] struct DemoDev { @@ -131,6 +131,8 @@ impl RootPort { root_port_msix.msix_addr, root_port_msix.msix_data, ); + root_port.init_notification(); + root_port.clear_slot_event(); Self { rp_dev: root_port, @@ -591,20 +593,20 @@ fn simple_blk_io_req( free_head } -fn wait_msix_timeout( +fn wait_intr_timeout( blk: Rc>, virtqueue: Rc>, - timeout_us: u64, + timeout: u64, ) -> bool { let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_micros(timeout_us); + let timeout = time::Duration::from_secs(timeout); loop { if blk.borrow().queue_was_notified(virtqueue.clone()) { return false; } - if time::Instant::now() - start_time > timeout_us { + if time::Instant::now() - start_time > timeout { return true; } } @@ -635,9 +637,9 @@ fn validate_std_blk_io( ); } -fn wait_root_port_intr(root_port: Rc>) -> bool { +fn wait_root_port_msix(root_port: Rc>) -> bool { let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_secs(TIMEOUT_S); + let timeout = time::Duration::from_secs(TIMEOUT_S); let rp_borrowed = root_port.borrow(); loop { if rp_borrowed.rp_dev.has_msix( @@ -646,29 +648,21 @@ fn wait_root_port_intr(root_port: Rc>) -> bool { ) { return true; } - if (time::Instant::now() - start_time) >= timeout_us { + if (time::Instant::now() - start_time) >= timeout { return false; } } } -fn wait_cci_set(root_port: Rc>) -> bool { +fn wait_root_port_intx(root_port: Rc>) -> bool { let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_secs(TIMEOUT_S); + let timeout = time::Duration::from_secs(TIMEOUT_S); let rp_borrowed = root_port.borrow(); - let cci_mask = PCI_EXP_SLTSTA_CC; - let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); - loop { - if rp_borrowed - .rp_dev - .config_readw(cap_exp_addr + PCI_EXP_SLTSTA) - & cci_mask - == 1 - { + if rp_borrowed.rp_dev.has_intx() { return true; } - if (time::Instant::now() - start_time) >= timeout_us { + if (time::Instant::now() - start_time) >= timeout { return false; } } @@ -793,9 +787,12 @@ fn hotplug_blk( ); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + validate_hotplug(root_port.clone()); + handle_isr(root_port.clone()); power_on_device(root_port.clone()); } @@ -810,10 +807,12 @@ fn hotunplug_blk( let ret = test_state.borrow().qmp(&delete_device_command); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + validate_hotunplug(root_port.clone()); + handle_isr(root_port.clone()); power_off_device(root_port.clone()); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -822,8 +821,12 @@ fn hotunplug_blk( let ret = test_state.borrow().qmp(&delete_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); - wait_cci_set(root_port.clone()); - + assert!( + wait_root_port_msix(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + validate_cmd_complete(root_port.clone()); + handle_isr(root_port.clone()); // Verify the vendor id for the virtio block device. validate_config_value_2byte( blk.borrow().pci_dev.pci_bus.clone(), @@ -835,6 +838,62 @@ fn hotunplug_blk( ); } +fn handle_isr(root_port: Rc>) { + let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); + let mut status = root_port.borrow().rp_dev.pci_bus.borrow().config_readw( + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + ); + + status &= PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC; + root_port.borrow().rp_dev.pci_bus.borrow().config_writew( + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + status, + ); +} + +fn validate_hotplug(root_port: Rc>) { + let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); + let mask = PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PDC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PDC, + mask, + ); +} + +fn validate_hotunplug(root_port: Rc>) { + let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); + let mask = PCI_EXP_SLTSTA_ABP; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP, + mask, + ); +} + +fn validate_cmd_complete(root_port: Rc>) { + let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); + let mask = PCI_EXP_SLTSTA_CC; + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + root_port.borrow().rp_dev.bus_num, + root_port.borrow().rp_dev.devfn, + cap_exp_addr + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC, + mask, + ); +} + /// Query the config of the device which has attached the bus. #[test] fn test_pci_device_discovery_001() { @@ -887,13 +946,21 @@ fn test_pci_device_discovery_002() { set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = Rc::new(RefCell::new(RootPort::new( + let root_port_1 = Rc::new(RefCell::new(RootPort::new( machine.clone(), alloc.clone(), 0, 1 << 3 | 0, ))); + // Create a root port whose bdf is 0:2:0. + let root_port_2 = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 2 << 3 | 0, + ))); + // Create a block device whose bdf is 1:0:0. let blk = create_blk(machine.clone(), 1, 0, 0); @@ -908,12 +975,12 @@ fn test_pci_device_discovery_002() { ); // Hotplug a block device whose id is 0. - hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), 0); + hotunplug_blk(test_state.clone(), blk.clone(), root_port_1.clone(), 0); // Hotplug a block device whose id is 1 and bdf is 2:0:0. hotplug_blk( test_state.clone(), - root_port.clone(), + root_port_2.clone(), &mut image_paths, 1, 2, @@ -1201,10 +1268,14 @@ fn test_pci_type1_reset() { let cmd_memory = command & PCI_COMMAND_MEMORY as u16; // Bitwise inversion of memory space enable. - let write_cmd = command | (command & (!PCI_COMMAND_MEMORY as u16)) | !cmd_memory; + let write_cmd = if cmd_memory != 0 { + command & !PCI_COMMAND_MEMORY as u16 + } else { + command | PCI_COMMAND_MEMORY as u16 + }; root_port.rp_dev.config_writew(PCI_COMMAND, write_cmd); let old_command = root_port.rp_dev.config_readw(PCI_COMMAND); - assert_ne!(old_command, write_cmd); + assert_eq!(old_command, write_cmd); root_port .rp_dev @@ -1413,18 +1484,16 @@ fn test_pci_msix_global_ctl() { ); set_msix_disable(blk.borrow().pci_dev.clone()); - let mut free_head = simple_blk_io_req( + let free_head = simple_blk_io_req( blk.clone(), test_state.clone(), vqs[0].clone(), alloc.clone(), ); + // Verify that the os can not receive msix interrupt when msix is disabled. - assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + assert!(wait_intr_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); - set_msix_enable(blk.borrow().pci_dev.clone()); - // Verify that the os can receive msix interrupt when msix is enabled. - assert!(!wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); blk.borrow().poll_used_elem( test_state.clone(), vqs[0].clone(), @@ -1434,20 +1503,30 @@ fn test_pci_msix_global_ctl() { false, ); + set_msix_enable(blk.borrow().pci_dev.clone()); + let _free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + vqs[0].clone(), + alloc.clone(), + ); + // Verify that the os can receive msix interrupt when msix is enabled. + assert!(!wait_intr_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + mask_msix_global(blk.borrow().pci_dev.clone()); - free_head = simple_blk_io_req( + let free_head = simple_blk_io_req( blk.clone(), test_state.clone(), vqs[0].clone(), alloc.clone(), ); // Verify that the os can not receive msix interrupt when the function of vectors is masked. - assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + assert!(wait_intr_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); unmask_msix_global(blk.borrow().pci_dev.clone()); // Verify that the os can receive msix interrupt when the function of vectors is unmasked. - assert!(!wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + assert!(!wait_intr_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); blk.borrow().poll_used_elem( test_state.clone(), vqs[0].clone(), @@ -1486,11 +1565,11 @@ fn test_pci_msix_local_ctl() { alloc.clone(), ); // Verify that the os can not receive msix interrupt when the vectors of virtqueue is masked. - assert!(wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + assert!(wait_intr_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); unmask_msix_vector(blk.borrow().pci_dev.clone(), 1); // Verify that the os canreceive msix interrupt when the vectors of virtqueue is unmasked. - assert!(!wait_msix_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); + assert!(!wait_intr_timeout(blk.clone(), vqs[0].clone(), TIMEOUT_S)); blk.borrow().poll_used_elem( test_state.clone(), vqs[0].clone(), @@ -1538,7 +1617,151 @@ fn test_alloc_abnormal_vector() { alloc.clone(), ); // Verify that the os can not receive msix interrupt when the vectors of virtqueue is . - assert!(wait_msix_timeout(blk.clone(), virtqueue.clone(), TIMEOUT_S)); + assert!(wait_intr_timeout(blk.clone(), virtqueue.clone(), TIMEOUT_S)); + + blk.borrow_mut() + .cleanup_virtqueue(alloc.clone(), virtqueue.borrow().desc); + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); +} + +#[test] +fn test_intx_basic() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + let blk = create_blk(machine.clone(), 1, 0, 0); + + // 1. Init device. + blk.borrow_mut().reset(); + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_driver(); + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + blk.borrow_mut().set_features_ok(); + + set_msix_disable(blk.borrow().pci_dev.clone()); + blk.borrow_mut().pci_dev.set_intx_irq_num(1 as u8); + + let virtqueue = blk + .borrow() + .setup_virtqueue(test_state.clone(), alloc.clone(), 0 as u16); + blk.borrow().set_driver_ok(); + + let free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + virtqueue.clone(), + alloc.clone(), + ); + // Verify that the os can receive INTx interrupt when msix is disabled. + assert!(!wait_intr_timeout( + blk.clone(), + virtqueue.clone(), + TIMEOUT_S + )); + let mut intr_status = blk.borrow().pci_dev.config_readw(PCI_STATUS) & PCI_STATUS_INTERRUPT != 0; + assert!(intr_status); + + let isr = blk.borrow().isr_readb(); + assert_eq!(isr, 1); + intr_status = blk.borrow().pci_dev.config_readw(PCI_STATUS) & PCI_STATUS_INTERRUPT != 0; + assert!(!intr_status); + + blk.borrow().pci_dev.eoi_intx(); + + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_S, + &mut None, + false, + ); + + blk.borrow_mut() + .cleanup_virtqueue(alloc.clone(), virtqueue.borrow().desc); + tear_down(Some(blk), test_state, alloc, None, Some(image_paths)); +} + +#[test] +fn test_intx_disable() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + let blk = create_blk(machine.clone(), 1, 0, 0); + + // 1. Init device. + blk.borrow_mut().reset(); + blk.borrow_mut().set_acknowledge(); + blk.borrow_mut().set_driver(); + blk.borrow_mut().negotiate_features(1 << VIRTIO_F_VERSION_1); + blk.borrow_mut().set_features_ok(); + + set_msix_disable(blk.borrow().pci_dev.clone()); + blk.borrow_mut().pci_dev.set_intx_irq_num(1 as u8); + + let virtqueue = blk + .borrow() + .setup_virtqueue(test_state.clone(), alloc.clone(), 0 as u16); + blk.borrow().set_driver_ok(); + + // Disable INTx. + let command = blk.borrow().pci_dev.config_readw(PCI_COMMAND); + blk.borrow() + .pci_dev + .config_writew(PCI_COMMAND, command | PCI_COMMAND_INTX_DISABLE); + + let free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + virtqueue.clone(), + alloc.clone(), + ); + // Verify that the os can not receive INTx interrupt when msix is disabled. + assert!(wait_intr_timeout(blk.clone(), virtqueue.clone(), TIMEOUT_S)); + + let isr = blk.borrow().isr_readb(); + assert_eq!(isr, 1); + + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_S, + &mut None, + false, + ); + + // Enable INTx. + blk.borrow() + .pci_dev + .config_writew(PCI_COMMAND, command & !PCI_COMMAND_INTX_DISABLE); + + let _free_head = simple_blk_io_req( + blk.clone(), + test_state.clone(), + virtqueue.clone(), + alloc.clone(), + ); + + // Verify that the os can receive INTx interrupt when msix is disabled. + assert!(!wait_intr_timeout( + blk.clone(), + virtqueue.clone(), + TIMEOUT_S + )); + let isr = blk.borrow().isr_readb(); + assert_eq!(isr, 1); + + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueue.clone(), + free_head, + TIMEOUT_S, + &mut None, + false, + ); blk.borrow_mut() .cleanup_virtqueue(alloc.clone(), virtqueue.borrow().desc); @@ -1553,7 +1776,7 @@ fn test_pci_hotplug_001() { let (test_state, machine, alloc, mut image_paths) = set_up(root_port_nums, blk_nums, true, false); - // Create a root port whose bdf is 0:2:0. + // Create a root port whose bdf is 0:1:0. let root_port = Rc::new(RefCell::new(RootPort::new( machine.clone(), alloc.clone(), @@ -1732,6 +1955,75 @@ fn test_pci_hotplug_006() { tear_down(None, test_state, alloc, None, Some(image_paths)); } +/// Hotplug using INTx interrupt testcase. +#[test] +fn test_pci_hotplug_007() { + let blk_nums = 0; + let root_port_nums = 1; + let (test_state, machine, alloc, mut image_paths) = + set_up(root_port_nums, blk_nums, true, false); + + // Create a root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + set_msix_disable(root_port.borrow().rp_dev.clone()); + root_port.borrow_mut().rp_dev.set_intx_irq_num(1 as u8); + + // Hotplug a block device whose id is 1 and bdf is 1:0:0. + let bus = 1; + let slot = 0; + let func = 0; + let hotplug_blk_id = 0; + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + image_paths.push(hotplug_image_path.clone()); + + // Hotplug a block device whose bdf is 1:0:0. + let (add_blk_command, add_device_command) = + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), bus, slot, 0); + let ret = test_state.borrow().qmp(&add_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + let ret = test_state.borrow().qmp(&add_device_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + // Verify the vendor id for the virtio block device hotplugged. + validate_config_value_2byte( + root_port.borrow().rp_dev.pci_bus.clone(), + bus, + slot << 3 | func, + PCI_VENDOR_ID, + VIRTIO_PCI_VENDOR, + 0xFFFF, + ); + + assert!( + wait_root_port_intx(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + + validate_hotplug(root_port.clone()); + handle_isr(root_port.clone()); + power_on_device(root_port.clone()); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + let vqs = blk.borrow_mut().init_device( + test_state.clone(), + alloc.clone(), + 1 << VIRTIO_F_VERSION_1, + 1, + ); + + validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + + tear_down(Some(blk), test_state, alloc, Some(vqs), Some(image_paths)); +} + /// Basic hotunplug testcase. #[test] fn test_pci_hotunplug_001() { @@ -1796,9 +2088,10 @@ fn test_pci_hotunplug_003() { let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + // The block device will not be unplugged when it is power on. power_on_device(root_port.clone()); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -1817,10 +2110,7 @@ fn test_pci_hotunplug_003() { let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); - assert!( - wait_root_port_intr(root_port.clone()), - "Wait for interrupt of root port timeout" - ); + // The block device will not be unplugged when indicator of power is blinking. power_indicator_blink(root_port.clone()); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -1898,12 +2188,12 @@ fn test_pci_hotunplug_004() { assert_eq!(*ret.get("return").unwrap(), json!({})); assert!( - wait_root_port_intr(root_port_1.clone()), + wait_root_port_msix(root_port_1.clone()), "Wait for interrupt of root port timeout" ); assert!( - wait_root_port_intr(root_port_2.clone()), + wait_root_port_msix(root_port_2.clone()), "Wait for interrupt of root port timeout" ); @@ -2008,7 +2298,7 @@ fn test_pci_hotunplug_007() { let (delete_device_command, _delete_blk_command) = build_hotunplug_blk_cmd(unplug_blk_id); let _ret = test_state.borrow().qmp(&delete_device_command); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); @@ -2032,6 +2322,66 @@ fn test_pci_hotunplug_007() { tear_down(None, test_state, alloc, None, Some(image_paths)); } +/// Hotunplug using INTx interrupt testcase. +#[test] +fn test_pci_hotunplug_008() { + let blk_nums = 1; + let root_port_nums = 1; + let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); + + // Create root port whose bdf is 0:1:0. + let root_port = Rc::new(RefCell::new(RootPort::new( + machine.clone(), + alloc.clone(), + 0, + 1 << 3 | 0, + ))); + + set_msix_disable(root_port.borrow().rp_dev.clone()); + root_port.borrow_mut().rp_dev.set_intx_irq_num(1 as u8); + + // Create a block device whose bdf is 1:0:0. + let blk = create_blk(machine.clone(), 1, 0, 0); + + // Hotunplug the block device whose bdf is 1:0:0. + let hotunplug_blk_id = 0; + let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotunplug_blk_id); + let ret = test_state.borrow().qmp(&delete_device_command); + + assert!( + wait_root_port_intx(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + + validate_hotunplug(root_port.clone()); + handle_isr(root_port.clone()); + power_off_device(root_port.clone()); + + assert_eq!(*ret.get("return").unwrap(), json!({})); + test_state.borrow().wait_qmp_event(); + + let ret = test_state.borrow().qmp(&delete_blk_command); + assert_eq!(*ret.get("return").unwrap(), json!({})); + + assert!( + wait_root_port_intx(root_port.clone()), + "Wait for interrupt of root port timeout" + ); + validate_cmd_complete(root_port.clone()); + handle_isr(root_port.clone()); + // Verify the vendor id for the virtio block device. + validate_config_value_2byte( + blk.borrow().pci_dev.pci_bus.clone(), + blk.borrow().pci_dev.bus_num, + blk.borrow().pci_dev.devfn, + PCI_VENDOR_ID, + 0xFFFF, + 0xFFFF, + ); + + tear_down(None, test_state, alloc, None, Some(image_paths)); +} + /// Hotplug and hotunplug in sequence. #[test] fn test_pci_hotplug_combine_001() { @@ -2061,9 +2411,11 @@ fn test_pci_hotplug_combine_001() { assert_eq!(*ret.get("return").unwrap(), json!({})); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + handle_isr(root_port.clone()); power_on_device(root_port.clone()); // Create a block device whose bdf is 1:0:0. @@ -2080,9 +2432,11 @@ fn test_pci_hotplug_combine_001() { let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + handle_isr(root_port.clone()); power_off_device(root_port.clone()); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -2113,9 +2467,11 @@ fn test_pci_hotplug_combine_001() { assert_eq!(*ret.get("return").unwrap(), json!({})); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + handle_isr(root_port.clone()); power_on_device(root_port.clone()); // Verify the virtio block device has been plugged. @@ -2141,9 +2497,11 @@ fn test_pci_hotplug_combine_001() { let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + handle_isr(root_port.clone()); power_off_device(root_port.clone()); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -2191,6 +2549,7 @@ fn test_pci_hotplug_combine_002() { 0, 0, ); + power_indicator_off(root_port.clone()); // Create a block device whose bdf is 1:0:0. @@ -2204,9 +2563,11 @@ fn test_pci_hotplug_combine_002() { assert_eq!(*ret.get("return").unwrap(), json!({})); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + handle_isr(root_port.clone()); power_indicator_blink(root_port.clone()); let ret = test_state.borrow().qmp(&delete_blk_command); @@ -2227,9 +2588,11 @@ fn test_pci_hotplug_combine_002() { assert!(!(*ret.get("error").unwrap()).is_null()); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); + + handle_isr(root_port.clone()); power_off_device(root_port.clone()); test_state.borrow().wait_qmp_event(); @@ -2270,7 +2633,7 @@ fn test_pci_hotplug_combine_003() { let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotunplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -2447,7 +2810,7 @@ fn test_pci_root_port_exp_cap() { root_port.borrow().rp_dev.bus_num, root_port.borrow().rp_dev.devfn, cap_exp_addr + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_ABP, + 0, abp_mask, ); @@ -2467,7 +2830,7 @@ fn test_pci_root_port_exp_cap() { root_port.borrow().rp_dev.bus_num, root_port.borrow().rp_dev.devfn, cap_exp_addr + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC, + 0, pdc_mask, ); @@ -2505,7 +2868,7 @@ fn test_pci_root_port_exp_cap() { root_port.borrow().rp_dev.bus_num, root_port.borrow().rp_dev.devfn, cap_exp_addr + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_ABP, + 0, abp_mask, ); @@ -2525,7 +2888,7 @@ fn test_pci_root_port_exp_cap() { root_port.borrow().rp_dev.bus_num, root_port.borrow().rp_dev.devfn, cap_exp_addr + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC, + 0, pdc_mask, ); @@ -2637,7 +3000,7 @@ fn test_pci_combine_002() { assert!(test_state.borrow().readb(bar_addr) == 5); assert!( - wait_root_port_intr(root_port.clone()), + wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); power_off_device(root_port.clone()); diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs index cc3c9e6ef..64ac2bba1 100644 --- a/util/src/test_helper.rs +++ b/util/src/test_helper.rs @@ -26,10 +26,23 @@ impl MsixMsg { } } +#[derive(Default, Clone, Copy, Debug)] +struct IntxInfo { + irq: u32, + level: i8, +} + +impl IntxInfo { + fn new(irq: u32, level: i8) -> Self { + IntxInfo { irq, level } + } +} + static TEST_ENABLED: OnceCell = OnceCell::new(); static TEST_BASE_TIME: OnceCell = OnceCell::new(); static mut TEST_CLOCK: Option>> = None; static TEST_MSIX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); +static TEST_INTX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); pub fn set_test_enabled() { #[cfg(target_arch = "x86_64")] @@ -125,3 +138,45 @@ pub fn has_msix_msg(addr: u64, data: u32) -> bool { None => false, } } + +pub fn trigger_intx(irq: u32, change: i8) { + let new_intx = IntxInfo::new(irq, change); + let mut intx_list_lock = TEST_INTX_LIST.lock().unwrap(); + + for intx in intx_list_lock.iter_mut() { + if intx.irq == new_intx.irq { + intx.level += new_intx.level; + return; + } + } + + intx_list_lock.push(new_intx); +} + +pub fn query_intx(irq: u32) -> bool { + let mut intx_list_lock = TEST_INTX_LIST.lock().unwrap(); + for intx in intx_list_lock.iter_mut() { + if intx.irq == irq { + return intx.level > 0; + } + } + + false +} + +pub fn eoi_intx(irq: u32) -> bool { + let mut intx_list_lock = TEST_INTX_LIST.lock().unwrap(); + + for intx in intx_list_lock.iter_mut() { + if intx.irq == irq { + if intx.level == 0 { + return false; + } else { + intx.level -= 1; + return true; + } + } + } + + false +} -- Gitee From dc06344cf6ca9ca9be0c6b9f08c8244259f96e6d Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 25 Mar 2023 14:39:32 +0800 Subject: [PATCH 0988/2187] ACPI-MST: Modify the acpi MST testcases DSDT table is modified because of the supporting INTx. So modify acpi testcase to adapt to the change. Signed-off-by: Jinhao Gao --- tests/mod_test/tests/acpi_test.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index c8def978a..0e263853f 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -23,8 +23,8 @@ use std::{cell::RefCell, mem, rc::Rc}; use mod_test::libdriver::fwcfg::bios_args; use mod_test::libtest::{test_init, TestState}; -// Now dsdt table data length is 877. -const DSDT_TABLE_DATA_LENGTH: u32 = 877; +// Now dsdt table data length is 3482. +const DSDT_TABLE_DATA_LENGTH: u32 = 3482; // Now fadt table data length is 276. const FADT_TABLE_DATA_LENGTH: u32 = 276; // Now madt table data length is 744. @@ -37,10 +37,10 @@ const IORT_TABLE_DATA_LENGTH: u32 = 128; const SPCR_TABLE_DATA_LENGTH: u32 = 80; // Now mcfg table data length is 60. const MCFG_TABLE_DATA_LENGTH: u32 = 60; -// Now acpi tables data length is 3005(cpu number is 8). -const ACPI_TABLES_DATA_LENGTH_8: usize = 3005; -// Now acpi tables data length is 29354(cpu number is 200). -const ACPI_TABLES_DATA_LENGTH_200: usize = 29354; +// Now acpi tables data length is 5610(cpu number is 8). +const ACPI_TABLES_DATA_LENGTH_8: usize = 5610; +// Now acpi tables data length is 31953(cpu number is 200). +const ACPI_TABLES_DATA_LENGTH_200: usize = 31953; fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { let file_name = "etc/acpi/rsdp"; @@ -171,7 +171,7 @@ fn check_spcr(data: &[u8]) { MEM_LAYOUT[LayoutEntryType::Uart as usize].0 ); assert_eq!(data[52], 1_u8 << 3); // Interrupt Type: Arm GIC interrupu - assert_eq!(LittleEndian::read_u32(&data[54..]), 66); // Irq number used by the UART + assert_eq!(LittleEndian::read_u32(&data[54..]), 39); // Irq number used by the UART assert_eq!(data[58], 3); // Set baud rate: 3 = 9600 assert_eq!(data[60], 1); // Stop bit assert_eq!(data[61], 2); // Hardware flow control -- Gitee From 7ccf51a6a4e6413197cd7fc14ba4e5d76be09978 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Thu, 13 Apr 2023 11:25:29 +0800 Subject: [PATCH 0989/2187] vhost-vsock: use irqfd in vhost-vsock Signed-off-by: mayunlong --- machine/src/lib.rs | 3 +- virtio/src/vhost/kernel/mod.rs | 57 ++------------------------------ virtio/src/vhost/kernel/vsock.rs | 40 ++++++++++------------ 3 files changed, 23 insertions(+), 77 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 34f9e5b0f..cdeb02c7f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -278,7 +278,7 @@ pub trait MachineOps { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let virtio_pci_device = VirtioPciDevice::new( + let mut virtio_pci_device = VirtioPciDevice::new( device_cfg.id.clone(), devfn, sys_mem, @@ -286,6 +286,7 @@ pub trait MachineOps { parent_bus, multi_func, ); + virtio_pci_device.enable_need_irqfd(); virtio_pci_device .realize() .with_context(|| "Failed to add virtio pci vsock device")?; diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 19638e1a8..8f819ffd6 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -19,25 +19,19 @@ pub use vsock::{Vsock, VsockState}; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use log::{debug, error}; +use log::debug; use util::byte_code::ByteCode; -use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; -use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; -use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; -use super::{VhostNotify, VhostOps}; +use super::super::QueueConfig; +use super::VhostOps; use crate::VirtioError; use anyhow::{anyhow, Context, Result}; @@ -455,48 +449,3 @@ impl VhostOps for VhostBackend { Ok(()) } } - -pub struct VhostIoHandler { - interrupt_cb: Arc, - host_notifies: Vec, - device_broken: Arc, -} - -impl EventNotifierHelper for VhostIoHandler { - fn internal_notifiers(vhost_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let vhost = vhost_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - let locked_vhost_handler = vhost.lock().unwrap(); - if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { - return None; - } - for host_notify in locked_vhost_handler.host_notifies.iter() { - if let Err(e) = (locked_vhost_handler.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&host_notify.queue.lock().unwrap()), - false, - ) { - error!( - "Failed to trigger interrupt for vhost device, error is {:?}", - e - ); - } - } - None as Option> - }); - for host_notify in vhost_handler.lock().unwrap().host_notifies.iter() { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - host_notify.notify_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler.clone()], - )); - } - - notifiers - } -} diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 3b7054f1e..db0e89a61 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -21,15 +21,14 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use machine_manager::event_loop::unregister_event_helper; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; +use super::super::VhostOps; +use super::{VhostBackend, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_VSOCK, }; @@ -105,6 +104,8 @@ pub struct Vsock { deactivate_evts: Vec, /// Device is broken or not. broken: Arc, + /// Save irqfd used for vhost-vsock + call_events: Vec>, } impl Vsock { @@ -118,6 +119,7 @@ impl Vsock { interrupt_cb: None, deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), + call_events: Vec::new(), } } @@ -243,6 +245,14 @@ impl VirtioDevice for Vsock { Ok(()) } + fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { + for fd in queue_evts.iter() { + self.call_events.push(fd.clone()); + } + + Ok(()) + } + /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( @@ -253,7 +263,6 @@ impl VirtioDevice for Vsock { queue_evts: Vec>, ) -> Result<()> { let cid = self.vsock_cfg.guest_cid; - let mut host_notifies = Vec::new(); // The receive queue and transmit queue will be handled in vhost. let vhost_queues = queues[..2].to_vec(); // This event queue will be handled. @@ -299,38 +308,25 @@ impl VirtioDevice for Vsock { })?; drop(queue); - let host_notify = VhostNotify { - notify_evt: Arc::new( - EventFd::new(libc::EFD_NONBLOCK).with_context(|| VirtioError::EventFdCreate)?, - ), - queue: queue_mutex.clone(), - }; backend - .set_vring_call(queue_index, host_notify.notify_evt.clone()) + .set_vring_call(queue_index, self.call_events[queue_index].clone()) .with_context(|| { format!("Failed to set vring call for vsock, index: {}", queue_index) })?; - host_notifies.push(host_notify); } backend.set_guest_cid(cid)?; backend.set_running(true)?; - let handler = VhostIoHandler { - interrupt_cb, - host_notifies, - device_broken: self.broken.clone(), - }; - - let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; self.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(None, &mut self.deactivate_evts) + unregister_event_helper(None, &mut self.deactivate_evts)?; + self.call_events.clear(); + Ok(()) } fn reset(&mut self) -> Result<()> { -- Gitee From c74012df3c8e7a2be71815db495458e8d81f2964 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 27 Mar 2023 22:00:02 +0800 Subject: [PATCH 0990/2187] xhci: support super speed device Fix the wrong speed mask when init usb port. And check the speed mask match with the device when xhci assign usb port. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 5eaa61f72..42cfb5489 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -355,6 +355,12 @@ impl UsbPort { self.portsc &= !(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); self.portsc |= (pls & PORTSC_PLS_MASK) << PORTSC_PLS_SHIFT; } + + /// Check the speed is supported by the usb port. + pub fn speed_supported(&self, speed: u32) -> bool { + let speed_mask = 1 << speed; + self.speed_mask & speed_mask == speed_mask + } } /// Event usually send to drivers. @@ -583,14 +589,14 @@ impl XhciDevice { ))); locked_xhci.usb_ports.push(usb_port.clone()); let mut locked_port = usb_port.lock().unwrap(); - locked_port.speed_mask = USB_SPEED_LOW | USB_SPEED_HIGH | USB_SPEED_FULL; + locked_port.speed_mask = USB_SPEED_MASK_LOW | USB_SPEED_MASK_HIGH | USB_SPEED_MASK_FULL; } for i in 0..locked_xhci.numports_3 { let idx = i + locked_xhci.numports_2 + 1; let usb_port = Arc::new(Mutex::new(UsbPort::new(&Arc::downgrade(&clone_xhci), idx))); locked_xhci.usb_ports.push(usb_port.clone()); let mut locked_port = usb_port.lock().unwrap(); - locked_port.speed_mask = USB_SPEED_SUPER; + locked_port.speed_mask = USB_SPEED_MASK_SUPER; } xhci } @@ -1842,9 +1848,10 @@ impl XhciDevice { &mut self, dev: &Arc>, ) -> Option>> { + let speed = dev.lock().unwrap().speed(); for port in &self.usb_ports { let mut locked_port = port.lock().unwrap(); - if !locked_port.used { + if locked_port.speed_supported(speed) && !locked_port.used { locked_port.used = true; locked_port.dev = Some(dev.clone()); let mut locked_dev = dev.lock().unwrap(); -- Gitee From 9ed69fb25005c2ba66ce1f3e53451181235e0716 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 27 Mar 2023 21:50:55 +0800 Subject: [PATCH 0991/2187] xhci: change the operational register structure Change the operational cmd and status registers to allow be shared by multiple threads. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 24 +++++----- devices/src/usb/xhci/xhci_regs.rs | 62 +++++++++++++++++-------- 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 42cfb5489..5ca0ce5e0 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -26,7 +26,7 @@ use machine_manager::config::XhciConfig; use util::aio::Iovec; use util::num_ops::{read_u32, write_u64_low}; -use super::xhci_regs::{XchiOperReg, XhciInterrupter}; +use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use crate::usb::config::*; use crate::usb::UsbError; use crate::usb::{UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; @@ -542,7 +542,7 @@ pub trait DwordOrder: Default + Copy + Send + Sync { pub struct XhciDevice { pub numports_2: u8, pub numports_3: u8, - pub oper: XchiOperReg, + pub oper: XhciOperReg, pub usb_ports: Vec>>, pub slots: Vec, pub intrs: Vec, @@ -568,7 +568,7 @@ impl XhciDevice { } } let xhci = XhciDevice { - oper: XchiOperReg::new(), + oper: XhciOperReg::new(), send_interrupt_ops: None, usb_ports: Vec::new(), numports_3: p3, @@ -581,7 +581,7 @@ impl XhciDevice { let xhci = Arc::new(Mutex::new(xhci)); let clone_xhci = xhci.clone(); let mut locked_xhci = clone_xhci.lock().unwrap(); - locked_xhci.oper.usb_status = USB_STS_HCH; + locked_xhci.oper.set_usb_status(USB_STS_HCH); for i in 0..locked_xhci.numports_2 { let usb_port = Arc::new(Mutex::new(UsbPort::new( &Arc::downgrade(&clone_xhci), @@ -602,21 +602,21 @@ impl XhciDevice { } pub fn run(&mut self) { - self.oper.usb_status &= !USB_STS_HCH; + self.oper.unset_usb_status_flag(USB_STS_HCH); } pub fn stop(&mut self) { - self.oper.usb_status |= USB_STS_HCH; + self.oper.set_usb_status_flag(USB_STS_HCH); self.oper.cmd_ring_ctrl &= !(CMD_RING_CTRL_CRR as u64); } pub fn running(&self) -> bool { - self.oper.usb_status & USB_STS_HCH != USB_STS_HCH + self.oper.get_usb_status() & USB_STS_HCH != USB_STS_HCH } pub fn host_controller_error(&mut self) { error!("Xhci host controller error!"); - self.oper.usb_status |= USB_STS_HCE; + self.oper.set_usb_status_flag(USB_STS_HCE) } pub fn reset(&mut self) { @@ -714,7 +714,7 @@ impl XhciDevice { locked_port.portsc, pls ); drop(locked_port); - self.oper.usb_status |= USB_STS_PCD; + self.oper.set_usb_status_flag(USB_STS_PCD); self.port_notify(port, PORTSC_CSC)?; Ok(()) } @@ -1808,14 +1808,14 @@ impl XhciDevice { erdp_low |= ERDP_EHB; self.intrs[idx as usize].erdp = write_u64_low(self.intrs[idx as usize].erdp, erdp_low); self.intrs[idx as usize].iman |= IMAN_IP; - self.oper.usb_status |= USB_STS_EINT; + self.oper.set_usb_status_flag(USB_STS_EINT); if pending { return; } if self.intrs[idx as usize].iman & IMAN_IE != IMAN_IE { return; } - if self.oper.usb_cmd & USB_CMD_INTE != USB_CMD_INTE { + if self.oper.get_usb_cmd() & USB_CMD_INTE != USB_CMD_INTE { return; } @@ -1831,7 +1831,7 @@ impl XhciDevice { if v == 0 { if self.intrs[0].iman & IMAN_IP == IMAN_IP && self.intrs[0].iman & IMAN_IE == IMAN_IE - && self.oper.usb_cmd & USB_CMD_INTE == USB_CMD_INTE + && self.oper.get_usb_cmd() & USB_CMD_INTE == USB_CMD_INTE { level = 1; } diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 0f2a3ce72..e3119cb09 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::Result; @@ -111,12 +112,11 @@ const XHCI_PORTLI: u64 = 0x8; const XHCI_PORTHLPMC: u64 = 0xc; /// XHCI Operation Registers -#[derive(Default, Copy, Clone)] -pub struct XchiOperReg { +pub struct XhciOperReg { /// USB Command - pub usb_cmd: u32, + pub usb_cmd: Arc, /// USB Status - pub usb_status: u32, + pub usb_status: Arc, /// Device Notify Control pub dev_notify_ctrl: u32, /// Command Ring Control @@ -127,11 +127,11 @@ pub struct XchiOperReg { pub config: u32, } -impl XchiOperReg { +impl XhciOperReg { pub fn new() -> Self { Self { - usb_cmd: 0, - usb_status: 0, + usb_cmd: Arc::new(AtomicU32::new(0)), + usb_status: Arc::new(AtomicU32::new(0)), dev_notify_ctrl: 0, cmd_ring_ctrl: 0, dcbaap: 0, @@ -140,8 +140,8 @@ impl XchiOperReg { } pub fn reset(&mut self) { - self.usb_cmd = 0; - self.usb_status = USB_STS_HCH; + self.set_usb_cmd(0); + self.set_usb_status(USB_STS_HCH); self.dev_notify_ctrl = 0; self.cmd_ring_ctrl = 0; self.dcbaap = 0; @@ -152,6 +152,30 @@ impl XchiOperReg { pub fn start_cmd_ring(&mut self) { self.cmd_ring_ctrl |= CMD_RING_CTRL_CRR as u64; } + + pub fn set_usb_cmd(&mut self, value: u32) { + self.usb_cmd.store(value, Ordering::SeqCst) + } + + pub fn get_usb_cmd(&self) -> u32 { + self.usb_cmd.load(Ordering::Acquire) + } + + pub fn set_usb_status(&mut self, value: u32) { + self.usb_status.store(value, Ordering::SeqCst) + } + + pub fn get_usb_status(&self) -> u32 { + self.usb_status.load(Ordering::Acquire) + } + + pub fn set_usb_status_flag(&mut self, value: u32) { + self.usb_status.fetch_or(value, Ordering::SeqCst); + } + + pub fn unset_usb_status_flag(&mut self, value: u32) { + self.usb_status.fetch_and(!value, Ordering::SeqCst); + } } /// XHCI Interrupter @@ -311,8 +335,8 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { debug!("oper read {:x} {:x}", addr.0, offset); let locked_xhci = xhci.lock().unwrap(); let value = match offset { - XHCI_OPER_REG_USBCMD => locked_xhci.oper.usb_cmd, - XHCI_OPER_REG_USBSTS => locked_xhci.oper.usb_status, + XHCI_OPER_REG_USBCMD => locked_xhci.oper.get_usb_cmd(), + XHCI_OPER_REG_USBSTS => locked_xhci.oper.get_usb_status(), XHCI_OPER_REG_PAGESIZE => XHCI_OPER_PAGESIZE, XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl, XHCI_OPER_REG_CMD_RING_CTRL_LO => { @@ -350,23 +374,23 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { match offset { XHCI_OPER_REG_USBCMD => { if (value & USB_CMD_RUN) == USB_CMD_RUN - && (locked_xhci.oper.usb_cmd & USB_CMD_RUN) != USB_CMD_RUN + && (locked_xhci.oper.get_usb_cmd() & USB_CMD_RUN) != USB_CMD_RUN { locked_xhci.run(); } else if (value & USB_CMD_RUN) != USB_CMD_RUN - && (locked_xhci.oper.usb_cmd & USB_CMD_RUN) == USB_CMD_RUN + && (locked_xhci.oper.get_usb_cmd() & USB_CMD_RUN) == USB_CMD_RUN { locked_xhci.stop(); } if value & USB_CMD_CSS == USB_CMD_CSS { - locked_xhci.oper.usb_status &= !USB_STS_SRE; + locked_xhci.oper.unset_usb_status_flag(USB_STS_SRE); } // When the restore command is issued, an error is reported and then // guest OS performs a complete initialization. if value & USB_CMD_CRS == USB_CMD_CRS { - locked_xhci.oper.usb_status |= USB_STS_SRE; + locked_xhci.oper.set_usb_status_flag(USB_STS_SRE); } - locked_xhci.oper.usb_cmd = value & 0xc0f; + locked_xhci.oper.set_usb_cmd(value & 0xc0f); if value & USB_CMD_HCRST == USB_CMD_HCRST { locked_xhci.reset(); } @@ -374,9 +398,9 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { } XHCI_OPER_REG_USBSTS => { // Write 1 to clear. - locked_xhci.oper.usb_status &= - !(value & (USB_STS_HSE | USB_STS_EINT | USB_STS_PCD | USB_STS_SRE)); - locked_xhci.update_intr(0); + locked_xhci.oper.unset_usb_status_flag( + value & (USB_STS_HSE | USB_STS_EINT | USB_STS_PCD | USB_STS_SRE), + ) } XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl = value & XHCI_OPER_NE_MASK, XHCI_OPER_REG_CMD_RING_CTRL_LO => { -- Gitee From 87b7184cc33786d2af4fe9a8e802301361ca633d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 27 Mar 2023 22:00:46 +0800 Subject: [PATCH 0992/2187] xhci: add oper cmd and status registers in interrupter Add oper cmd and status registers in interrupter which is used for send event. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 22 +++++++++++++++++++--- devices/src/usb/xhci/xhci_regs.rs | 23 ++++++++++------------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 5ca0ce5e0..87be5caad 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -567,14 +567,30 @@ impl XhciDevice { p3 = XHCI_MAX_PORT3; } } + let oper = XhciOperReg::default(); + + let mut intrs = Vec::new(); + for _ in 0..MAX_INTRS { + intrs.push(XhciInterrupter::new( + mem_space, + &oper.usb_cmd, + &oper.usb_status, + )); + } + + let mut slots = Vec::new(); + for _ in 0..MAX_SLOTS { + slots.push(XhciSlot::new(mem_space)); + } + let xhci = XhciDevice { - oper: XhciOperReg::new(), + oper: oper, send_interrupt_ops: None, usb_ports: Vec::new(), numports_3: p3, numports_2: p2, - slots: vec![XhciSlot::new(mem_space); MAX_SLOTS as usize], - intrs: vec![XhciInterrupter::new(mem_space); MAX_INTRS as usize], + slots, + intrs, cmd_ring: XhciRing::new(mem_space), mem_space: mem_space.clone(), }; diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index e3119cb09..f07d534df 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -112,6 +112,7 @@ const XHCI_PORTLI: u64 = 0x8; const XHCI_PORTHLPMC: u64 = 0xc; /// XHCI Operation Registers +#[derive(Default)] pub struct XhciOperReg { /// USB Command pub usb_cmd: Arc, @@ -128,17 +129,6 @@ pub struct XhciOperReg { } impl XhciOperReg { - pub fn new() -> Self { - Self { - usb_cmd: Arc::new(AtomicU32::new(0)), - usb_status: Arc::new(AtomicU32::new(0)), - dev_notify_ctrl: 0, - cmd_ring_ctrl: 0, - dcbaap: 0, - config: 0, - } - } - pub fn reset(&mut self) { self.set_usb_cmd(0); self.set_usb_status(USB_STS_HCH); @@ -179,9 +169,10 @@ impl XhciOperReg { } /// XHCI Interrupter -#[derive(Clone)] pub struct XhciInterrupter { mem: Arc, + oper_usb_cmd: Arc, + oper_usb_status: Arc, /// Interrupter Management pub iman: u32, /// Interrupter Morderation @@ -200,9 +191,15 @@ pub struct XhciInterrupter { } impl XhciInterrupter { - pub fn new(mem: &Arc) -> Self { + pub fn new( + mem: &Arc, + oper_usb_cmd: &Arc, + oper_usb_status: &Arc, + ) -> Self { Self { mem: mem.clone(), + oper_usb_cmd: oper_usb_cmd.clone(), + oper_usb_status: oper_usb_status.clone(), iman: 0, imod: 0, erstsz: 0, -- Gitee From c0e89740540610d0c89d9c1b370e6c258f0f2442 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 09:29:54 +0800 Subject: [PATCH 0993/2187] xhci: add transfer ring which can be shared Add transfer ring for endpoint which can be shared by multiple threads. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 74 ++++++++++---------- devices/src/usb/xhci/xhci_ring.rs | 90 ++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 45 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 87be5caad..156598aa8 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -14,7 +14,7 @@ use std::collections::LinkedList; use std::mem::size_of; use std::slice::from_raw_parts; use std::slice::from_raw_parts_mut; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; @@ -27,17 +27,15 @@ use util::aio::Iovec; use util::num_ops::{read_u32, write_u64_low}; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; -use crate::usb::config::*; -use crate::usb::UsbError; -use crate::usb::{UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; - -use super::xhci_ring::XhciEventRingSeg; -use super::xhci_ring::XhciRing; -use super::xhci_ring::XhciTRB; +use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; use super::{ TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_SIZE, TRB_TR_DIR, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TYPE_SHIFT, }; +use crate::usb::config::*; +use crate::usb::{ + UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, +}; pub const MAX_INTRS: u16 = 16; pub const MAX_SLOTS: u32 = 64; @@ -158,9 +156,9 @@ impl XhciTransfer { pub struct XhciEpContext { epid: u32, enabled: bool, - ring: XhciRing, + ring: Arc, ep_type: EpType, - output_ctx_addr: DmaAddr, + output_ctx_addr: Arc, state: u32, interval: u32, transfers: LinkedList, @@ -169,12 +167,13 @@ pub struct XhciEpContext { impl XhciEpContext { pub fn new(mem: &Arc) -> Self { + let addr = Arc::new(AtomicU64::new(0)); Self { epid: 0, enabled: false, - ring: XhciRing::new(mem), + ring: Arc::new(XhciTransferRing::new(mem, &addr)), ep_type: EpType::Invalid, - output_ctx_addr: 0, + output_ctx_addr: addr, state: 0, interval: 0, transfers: LinkedList::new(), @@ -186,25 +185,21 @@ impl XhciEpContext { fn init_ctx(&mut self, output_ctx: DmaAddr, ctx: &XhciEpCtx) { let dequeue: DmaAddr = addr64_from_u32(ctx.deq_lo & !0xf, ctx.deq_hi); self.ep_type = ((ctx.ep_info2 >> EP_TYPE_SHIFT) & EP_TYPE_MASK).into(); - self.output_ctx_addr = output_ctx; + self.output_ctx_addr.store(output_ctx, Ordering::SeqCst); self.ring.init(dequeue); - self.ring.ccs = (ctx.deq_lo & 1) == 1; + self.ring.set_cycle_bit((ctx.deq_lo & 1) == 1); self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); } /// Update the endpoint state and write the state to memory. fn set_state(&mut self, mem: &Arc, state: u32) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32( - mem, - GuestAddress(self.output_ctx_addr), - ep_ctx.as_mut_dwords(), - )?; + let output_addr = self.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= state; - ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; - ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; - dma_write_u32(mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_dwords())?; + self.ring.update_dequeue_to_ctx(&mut ep_ctx); + dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; self.state = state; Ok(()) } @@ -213,18 +208,15 @@ impl XhciEpContext { /// If dequeue is None, only flush the dequeue pointer to memory. fn update_dequeue(&mut self, mem: &Arc, dequeue: Option) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32( - mem, - GuestAddress(self.output_ctx_addr), - ep_ctx.as_mut_dwords(), - )?; + let output_addr = self.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; if let Some(dequeue) = dequeue { self.ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); - self.ring.ccs = (dequeue & EP_CTX_DCS) == EP_CTX_DCS; + self.ring + .set_cycle_bit((dequeue & EP_CTX_DCS) == EP_CTX_DCS); } - ep_ctx.deq_lo = self.ring.dequeue as u32 | self.ring.ccs as u32; - ep_ctx.deq_hi = (self.ring.dequeue >> 32) as u32; - dma_write_u32(mem, GuestAddress(self.output_ctx_addr), ep_ctx.as_dwords())?; + self.ring.update_dequeue_to_ctx(&mut ep_ctx); + dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; Ok(()) } @@ -546,7 +538,7 @@ pub struct XhciDevice { pub usb_ports: Vec>>, pub slots: Vec, pub intrs: Vec, - pub cmd_ring: XhciRing, + pub cmd_ring: XhciCommandRing, mem_space: Arc, pub send_interrupt_ops: Option bool + Send + Sync>>, } @@ -591,7 +583,7 @@ impl XhciDevice { numports_2: p2, slots, intrs, - cmd_ring: XhciRing::new(mem_space), + cmd_ring: XhciCommandRing::new(mem_space), mem_space: mem_space.clone(), }; let xhci = Arc::new(Mutex::new(xhci)); @@ -1330,7 +1322,9 @@ impl XhciDevice { }; debug!( "kick_endpoint slotid {} epid {} dequeue {:x}", - slot_id, ep_id, epctx.ring.dequeue + slot_id, + ep_id, + epctx.ring.get_dequeue_ptr(), ); if self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] .retry @@ -1917,7 +1911,11 @@ fn dma_read_u64(addr_space: &Arc, addr: GuestAddress, data: &mut u Ok(()) } -fn dma_read_u32(addr_space: &Arc, addr: GuestAddress, buf: &mut [u32]) -> Result<()> { +pub fn dma_read_u32( + addr_space: &Arc, + addr: GuestAddress, + buf: &mut [u32], +) -> Result<()> { let vec_len = size_of::() * buf.len(); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); @@ -1928,7 +1926,11 @@ fn dma_read_u32(addr_space: &Arc, addr: GuestAddress, buf: &mut [u Ok(()) } -fn dma_write_u32(addr_space: &Arc, addr: GuestAddress, buf: &[u32]) -> Result<()> { +pub fn dma_write_u32( + addr_space: &Arc, + addr: GuestAddress, + buf: &[u32], +) -> Result<()> { let vec_len = size_of::() * buf.len(); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 713a916be..f5eab6fa8 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::Arc; use anyhow::{bail, Result}; @@ -19,8 +20,9 @@ use byteorder::{ByteOrder, LittleEndian}; use log::debug; use super::super::UsbError; -use super::xhci_controller::dma_read_bytes; +use super::xhci_controller::{dma_read_u32, dma_write_u32, DwordOrder, XhciEpCtx}; use super::{TRBType, TRB_C, TRB_LK_TC, TRB_SIZE, TRB_TR_CH, TRB_TYPE_MASK, TRB_TYPE_SHIFT}; +use crate::usb::xhci::xhci_controller::dma_read_bytes; const TRB_LINK_LIMIT: u32 = 32; /// The max size of a ring segment in bytes is 64k. @@ -52,16 +54,16 @@ impl XhciTRB { } } -/// XHCI Ring +/// XHCI Command Ring #[derive(Clone)] -pub struct XhciRing { +pub struct XhciCommandRing { mem: Arc, pub dequeue: u64, /// Consumer Cycle State pub ccs: bool, } -impl XhciRing { +impl XhciCommandRing { pub fn new(mem: &Arc) -> Self { Self { mem: mem.clone(), @@ -122,14 +124,68 @@ impl XhciRing { }; Ok(trb) } +} + +/// XHCI Transfer Ring +pub struct XhciTransferRing { + mem: Arc, + pub dequeue: AtomicU64, + /// Consumer Cycle State + pub ccs: AtomicBool, + pub output_ctx_addr: Arc, +} + +impl XhciTransferRing { + pub fn new(mem: &Arc, addr: &Arc) -> Self { + Self { + mem: mem.clone(), + dequeue: AtomicU64::new(0), + ccs: AtomicBool::new(true), + output_ctx_addr: addr.clone(), + } + } + + pub fn init(&self, addr: u64) { + self.set_dequeue_ptr(addr); + self.set_cycle_bit(true); + } + + pub fn get_dequeue_ptr(&self) -> u64 { + self.dequeue.load(Ordering::Acquire) + } + + pub fn set_dequeue_ptr(&self, addr: u64) { + self.dequeue.store(addr, Ordering::SeqCst); + } + + pub fn get_cycle_bit(&self) -> bool { + self.ccs.load(Ordering::Acquire) + } + + pub fn set_cycle_bit(&self, v: bool) { + self.ccs.store(v, Ordering::SeqCst); + } + + fn read_trb(&self, addr: u64) -> Result { + let mut buf = [0; TRB_SIZE as usize]; + dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf, TRB_SIZE as u64)?; + let trb = XhciTRB { + parameter: LittleEndian::read_u64(&buf), + status: LittleEndian::read_u32(&buf[8..]), + control: LittleEndian::read_u32(&buf[12..]), + addr: 0, + ccs: true, + }; + Ok(trb) + } /// Get the transfer descriptor which includes one or more TRBs. /// Return None if the td is not ready. /// Return Vec if the td is ok. /// Return Error if read trb failed. - pub fn fetch_td(&mut self) -> Result>> { - let mut dequeue = self.dequeue; - let mut ccs = self.ccs; + pub fn fetch_td(&self) -> Result>> { + let mut dequeue = self.get_dequeue_ptr(); + let mut ccs = self.get_cycle_bit(); let mut ctrl_td = false; let mut link_cnt = 0; let mut td = Vec::new(); @@ -164,14 +220,30 @@ impl XhciRing { } if !ctrl_td && (trb.control & TRB_TR_CH != TRB_TR_CH) { // Update the dequeue pointer and ccs flag. - self.dequeue = dequeue; - self.ccs = ccs; + self.set_dequeue_ptr(dequeue); + self.set_cycle_bit(ccs); return Ok(Some(td)); } } } bail!("Transfer TRB length over limit"); } + + /// Refresh dequeue pointer to output context. + pub fn refresh_dequeue_ptr(&self) -> Result<()> { + let mut ep_ctx = XhciEpCtx::default(); + let output_addr = self.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + self.update_dequeue_to_ctx(&mut ep_ctx); + dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + Ok(()) + } + + pub fn update_dequeue_to_ctx(&self, ep_ctx: &mut XhciEpCtx) { + let dequeue = self.get_dequeue_ptr(); + ep_ctx.deq_lo = dequeue as u32 | self.get_cycle_bit() as u32; + ep_ctx.deq_hi = (dequeue >> 32) as u32; + } } /// Event Ring Segment Table Entry. See in the specs 6.5 Event Ring Segment Table. -- Gitee From bf2a4c9a2bfdd8b5889facdca6a48cb7bc0e871f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 10:58:11 +0800 Subject: [PATCH 0994/2187] xhci: use interrupter to send event Use interrupter to send event instead of controller. And the transfer can be submit directly. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 328 +++++++++--------------- devices/src/usb/xhci/xhci_pci.rs | 6 +- devices/src/usb/xhci/xhci_regs.rs | 161 +++++++++--- 3 files changed, 257 insertions(+), 238 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 156598aa8..396aeff50 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -18,26 +18,25 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; - -use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, warn}; + +use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::XhciConfig; use util::aio::Iovec; -use util::num_ops::{read_u32, write_u64_low}; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; use super::{ - TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_SIZE, TRB_TR_DIR, TRB_TR_IDT, TRB_TR_IOC, - TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TYPE_SHIFT, + TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_TR_DIR, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, + TRB_TR_LEN_MASK, TRB_TYPE_SHIFT, }; use crate::usb::config::*; use crate::usb::{ UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, }; -pub const MAX_INTRS: u16 = 16; +pub const MAX_INTRS: u32 = 16; pub const MAX_SLOTS: u32 = 64; /// Endpoint state pub const EP_STATE_MASK: u32 = 0x7; @@ -62,8 +61,6 @@ pub const SLOT_CONFIGURED: u32 = 3; const TRB_CR_BSR: u32 = 1 << 9; const TRB_CR_EPID_SHIFT: u32 = 16; const TRB_CR_EPID_MASK: u32 = 0x1f; -const TRB_INTR_SHIFT: u32 = 22; -const TRB_INTR_MASK: u32 = 0x3ff; const TRB_CR_DC: u32 = 1 << 9; const TRB_CR_SLOTID_SHIFT: u32 = 24; const TRB_CR_SLOTID_MASK: u32 = 0xff; @@ -118,7 +115,7 @@ type DmaAddr = u64; /// Transfer data between controller and device. #[derive(Clone)] pub struct XhciTransfer { - packet: UsbPacket, + pub packet: UsbPacket, status: TRBCCode, td: Vec, complete: Arc, @@ -126,10 +123,12 @@ pub struct XhciTransfer { epid: u32, in_xfer: bool, running_retry: bool, + interrupter: Arc>, + ep_ring: Arc, } impl XhciTransfer { - fn new() -> Self { + fn new(intr: &Arc>, ring: &Arc) -> Self { XhciTransfer { packet: UsbPacket::default(), status: TRBCCode::Invalid, @@ -139,6 +138,8 @@ impl XhciTransfer { epid: 0, in_xfer: false, running_retry: false, + interrupter: intr.clone(), + ep_ring: ring.clone(), } } @@ -149,10 +150,81 @@ impl XhciTransfer { fn set_comleted(&mut self, v: bool) { self.complete.store(v, Ordering::SeqCst) } + + /// Submit the succeed transfer TRBs. + pub fn submit_transfer(&mut self) -> Result<()> { + // Event Data Transfer Length Accumulator. + let mut edtla: u32 = 0; + let mut left = self.packet.actual_length; + for i in 0..self.td.len() { + let trb = &self.td[i]; + let trb_type = trb.get_type(); + let mut chunk = trb.status & TRB_TR_LEN_MASK; + match trb_type { + TRBType::TrSetup => {} + TRBType::TrData | TRBType::TrNormal | TRBType::TrIsoch => { + if chunk > left { + chunk = left; + self.status = TRBCCode::ShortPacket; + } + left -= chunk; + edtla = edtla.checked_add(chunk).with_context(|| + format!("Event Data Transfer Length Accumulator overflow, edtla {:x} offset {:x}", edtla, chunk) + )?; + } + TRBType::TrStatus => {} + _ => { + debug!("Ignore the TRB, unhandled trb type {:?}", trb.get_type()); + } + } + if (trb.control & TRB_TR_IOC == TRB_TR_IOC) + || (self.status == TRBCCode::ShortPacket + && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) + { + self.send_transfer_event(trb, chunk, &mut edtla)?; + } + } + Ok(()) + } + + fn send_transfer_event(&self, trb: &XhciTRB, transferred: u32, edtla: &mut u32) -> Result<()> { + let trb_type = trb.get_type(); + let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); + evt.slot_id = self.slotid as u8; + evt.ep_id = self.epid as u8; + evt.length = (trb.status & TRB_TR_LEN_MASK) - transferred; + evt.flags = 0; + evt.ptr = trb.addr; + evt.ccode = self.status; + if trb_type == TRBType::TrEvdata { + evt.ptr = trb.parameter; + evt.flags |= TRB_EV_ED; + evt.length = *edtla & TRANSFER_LEN_MASK; + *edtla = 0; + } + self.interrupter.lock().unwrap().send_event(&evt)?; + Ok(()) + } + + fn report_transfer_error(&mut self) -> Result<()> { + // An error occurs in the transfer. The transfer is set to the completed and will not be retried. + self.set_comleted(true); + let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); + evt.slot_id = self.slotid as u8; + evt.ep_id = self.epid as u8; + evt.ccode = self.status; + // According to 4.10.1 Transfer TRBs, the TRB pointer field in a Transfer TRB not + // only references the TRB that generated the event, but it also provides system software + // with the latest value of the xHC Dequeue Pointer for the Transfer Ring. + if let Some(trb) = self.td.last() { + evt.ptr = trb.addr; + } + self.interrupter.lock().unwrap().send_event(&evt)?; + Ok(()) + } } /// Endpoint context which use the ring to transfer data. -#[derive(Clone)] pub struct XhciEpContext { epid: u32, enabled: bool, @@ -262,7 +334,6 @@ impl From for EpType { } /// Device slot, mainly including some endpoint. -#[derive(Clone)] pub struct XhciSlot { pub enabled: bool, pub addressed: bool, @@ -273,12 +344,17 @@ pub struct XhciSlot { impl XhciSlot { fn new(mem: &Arc) -> Self { + let mut eps = Vec::new(); + for _ in 0..MAX_ENDPOINTS { + eps.push(XhciEpContext::new(mem)); + } + XhciSlot { enabled: false, addressed: false, slot_ctx_addr: 0, usb_port: None, - endpoints: vec![XhciEpContext::new(mem); MAX_ENDPOINTS as usize], + endpoints: eps, } } @@ -537,10 +613,9 @@ pub struct XhciDevice { pub oper: XhciOperReg, pub usb_ports: Vec>>, pub slots: Vec, - pub intrs: Vec, + pub intrs: Vec>>, pub cmd_ring: XhciCommandRing, mem_space: Arc, - pub send_interrupt_ops: Option bool + Send + Sync>>, } impl XhciDevice { @@ -562,12 +637,13 @@ impl XhciDevice { let oper = XhciOperReg::default(); let mut intrs = Vec::new(); - for _ in 0..MAX_INTRS { - intrs.push(XhciInterrupter::new( + for i in 0..MAX_INTRS { + intrs.push(Arc::new(Mutex::new(XhciInterrupter::new( mem_space, &oper.usb_cmd, &oper.usb_status, - )); + i, + )))); } let mut slots = Vec::new(); @@ -576,8 +652,7 @@ impl XhciDevice { } let xhci = XhciDevice { - oper: oper, - send_interrupt_ops: None, + oper, usb_ports: Vec::new(), numports_3: p3, numports_2: p2, @@ -609,6 +684,12 @@ impl XhciDevice { xhci } + pub fn set_interrupt_ops(&mut self, cb: Arc bool + Send + Sync>) { + for intr in &self.intrs { + intr.lock().unwrap().set_interrupter(cb.clone()); + } + } + pub fn run(&mut self) { self.oper.unset_usb_status_flag(USB_STS_HCH); } @@ -642,7 +723,7 @@ impl XhciDevice { } } for i in 0..self.intrs.len() { - self.intrs[i].reset(); + self.intrs[i].lock().unwrap().reset(); } self.cmd_ring.init(0); } @@ -689,7 +770,7 @@ impl XhciDevice { } let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); evt.ptr = ((locked_port.port_id as u32) << PORT_EVENT_ID_SHIFT) as u64; - self.send_event(&evt, 0)?; + self.intrs[0].lock().unwrap().send_event(&evt)?; Ok(()) } @@ -843,7 +924,7 @@ impl XhciDevice { } } event.slot_id = slot_id as u8; - self.send_event(&event, 0)?; + self.intrs[0].lock().unwrap().send_event(&event)?; } None => { debug!("No TRB in the cmd ring."); @@ -1343,8 +1424,10 @@ impl XhciDevice { epctx.set_state(&self.mem_space, EP_RUNNING)?; const KICK_LIMIT: u32 = 32; let mut count = 0; + let ring = epctx.ring.clone(); loop { - let mut xfer: XhciTransfer = XhciTransfer::new(); + // NOTE: Only support primary interrupter now. + let mut xfer: XhciTransfer = XhciTransfer::new(&self.intrs[0], &ring); xfer.slotid = slot_id; xfer.epid = ep_id; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; @@ -1352,7 +1435,8 @@ impl XhciDevice { Some(td) => { debug!( "fetch transfer trb {:?} ring dequeue {:?}", - td, epctx.ring.dequeue, + td, + epctx.ring.get_dequeue_ptr(), ); xfer.td = td; } @@ -1459,7 +1543,7 @@ impl XhciDevice { if let Err(e) = self.check_ctrl_transfer(xfer) { error!("Failed to check control transfer {:?}", e); xfer.status = TRBCCode::TrbError; - return self.report_transfer_error(xfer); + return xfer.report_transfer_error(); } let trb_setup = xfer.td[0]; let bm_request_type = trb_setup.parameter as u8; @@ -1468,7 +1552,7 @@ impl XhciDevice { if let Err(e) = self.setup_usb_packet(xfer) { error!("Failed to setup packet when transfer control {:?}", e); xfer.status = TRBCCode::TrbError; - return self.report_transfer_error(xfer); + return xfer.report_transfer_error(); } xfer.packet.parameter = trb_setup.parameter; self.device_handle_packet(xfer); @@ -1476,7 +1560,7 @@ impl XhciDevice { Ok(()) } - fn check_ctrl_transfer(&self, xfer: &mut XhciTransfer) -> Result<()> { + fn check_ctrl_transfer(&self, xfer: &XhciTransfer) -> Result<()> { let trb_setup = xfer.td[0]; let mut trb_status = xfer.td[xfer.td.len() - 1]; let status_type = trb_status.get_type(); @@ -1513,7 +1597,7 @@ impl XhciDevice { if let Err(e) = self.setup_usb_packet(xfer) { error!("Failed to setup packet when transfer data {:?}", e); xfer.status = TRBCCode::TrbError; - return self.report_transfer_error(xfer); + return xfer.report_transfer_error(); } self.device_handle_packet(xfer); self.complete_packet(xfer)?; @@ -1592,7 +1676,7 @@ impl XhciDevice { } if xfer.packet.status == UsbPacketStatus::Success { xfer.status = TRBCCode::Success; - self.submit_transfer(xfer)?; + xfer.submit_transfer()?; return Ok(()); } // Handle packet error status @@ -1610,93 +1694,13 @@ impl XhciDevice { error!("Unhandle status {:?}", xfer.packet.status); } } - self.report_transfer_error(xfer)?; + xfer.report_transfer_error()?; // Set the endpoint state to halted if an error occurs in the packet. let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; epctx.set_state(&self.mem_space, EP_HALTED)?; Ok(()) } - /// Submit the succeed transfer TRBs. - fn submit_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - // Event Data Transfer Length Accumulator - let mut edtla: u32 = 0; - let mut left = xfer.packet.actual_length; - for i in 0..xfer.td.len() { - let trb = &xfer.td[i]; - let trb_type = trb.get_type(); - let mut chunk = trb.status & TRB_TR_LEN_MASK; - match trb_type { - TRBType::TrSetup => {} - TRBType::TrData | TRBType::TrNormal | TRBType::TrIsoch => { - if chunk > left { - chunk = left; - xfer.status = TRBCCode::ShortPacket; - } - left -= chunk; - edtla = edtla.checked_add(chunk).with_context(|| - format!("Event Data Transfer Length Accumulator overflow, edtla {:x} offset {:x}", edtla, chunk) - )?; - } - TRBType::TrStatus => {} - _ => { - debug!("Ignore the TRB, unhandled trb type {:?}", trb.get_type()); - } - } - if (trb.control & TRB_TR_IOC == TRB_TR_IOC) - || (xfer.status == TRBCCode::ShortPacket - && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) - { - self.send_transfer_event(xfer, trb, chunk, &mut edtla)?; - } - } - Ok(()) - } - - fn report_transfer_error(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - // An error occurs in the transfer. The transfer is set to the completed and will not be retried. - xfer.set_comleted(true); - let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); - evt.slot_id = xfer.slotid as u8; - evt.ep_id = xfer.epid as u8; - evt.ccode = xfer.status; - let mut idx = 0; - // According to 4.10.1 Transfer TRBs, the TRB pointer field in a Transfer TRB not - // only references the TRB that generated the event, but it also provides system software - // with the latest value of the xHC Dequeue Pointer for the Transfer Ring. - if let Some(trb) = xfer.td.last() { - evt.ptr = trb.addr; - idx = (trb.status >> TRB_INTR_SHIFT) & TRB_INTR_MASK; - } - self.send_event(&evt, idx) - } - - fn send_transfer_event( - &mut self, - xfer: &XhciTransfer, - trb: &XhciTRB, - transferred: u32, - edtla: &mut u32, - ) -> Result<()> { - let trb_type = trb.get_type(); - let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); - evt.slot_id = xfer.slotid as u8; - evt.ep_id = xfer.epid as u8; - evt.length = (trb.status & TRB_TR_LEN_MASK) - transferred; - evt.flags = 0; - evt.ptr = trb.addr; - evt.ccode = xfer.status; - if trb_type == TRBType::TrEvdata { - evt.ptr = trb.parameter; - evt.flags |= TRB_EV_ED; - evt.length = *edtla & TRANSFER_LEN_MASK; - *edtla = 0; - } - let idx = (trb.status >> TRB_INTR_SHIFT) & TRB_INTR_MASK; - self.send_event(&evt, idx)?; - Ok(()) - } - /// Flush transfer in endpoint in some case such as stop endpoint. fn flush_ep_transfer(&mut self, slotid: u32, epid: u32, report: TRBCCode) -> Result { debug!("flush_ep_transfer slotid {} epid {}", slotid, epid); @@ -1729,7 +1733,7 @@ impl XhciDevice { if xfer.running_retry { if report != TRBCCode::Invalid { xfer.status = report; - self.submit_transfer(xfer)?; + xfer.submit_transfer()?; } let epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(ep_id - 1) as usize]; epctx.retry = None; @@ -1753,106 +1757,24 @@ impl XhciDevice { } pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { - let intr = &mut self.intrs[idx as usize]; - if intr.erstsz == 0 || intr.erstba == 0 { - intr.er_start = 0; - intr.er_size = 0; + let mut locked_intr = self.intrs[idx as usize].lock().unwrap(); + if locked_intr.erstsz == 0 || locked_intr.erstba == 0 { + locked_intr.er_start = 0; + locked_intr.er_size = 0; return Ok(()); } let mut seg = XhciEventRingSeg::new(&self.mem_space); - seg.fetch_event_ring_seg(intr.erstba)?; + seg.fetch_event_ring_seg(locked_intr.erstba)?; if seg.size < 16 || seg.size > 4096 { bail!("Invalid segment size {}", seg.size); } - intr.er_start = addr64_from_u32(seg.addr_lo, seg.addr_hi); - intr.er_size = seg.size; - intr.er_ep_idx = 0; - intr.er_pcs = true; + locked_intr.er_start = addr64_from_u32(seg.addr_lo, seg.addr_hi); + locked_intr.er_size = seg.size; + locked_intr.er_ep_idx = 0; + locked_intr.er_pcs = true; Ok(()) } - /// Send event TRB to driver, first write TRB and then send interrupt. - pub fn send_event(&mut self, evt: &XhciEvent, idx: u32) -> Result<()> { - if idx > self.intrs.len() as u32 { - bail!("Invalid index, out of range {}", idx); - } - let intr = &self.intrs[idx as usize]; - let er_end = intr - .er_start - .checked_add((TRB_SIZE * intr.er_size) as u64) - .ok_or(UsbError::MemoryAccessOverflow( - intr.er_start, - (TRB_SIZE * intr.er_size) as u64, - ))?; - if intr.erdp < intr.er_start || intr.erdp >= er_end { - bail!( - "DMA out of range, erdp {} er_start {:x} er_size {}", - intr.erdp, - intr.er_start, - intr.er_size - ); - } - let dp_idx = (intr.erdp - intr.er_start) / TRB_SIZE as u64; - if ((intr.er_ep_idx + 2) % intr.er_size) as u64 == dp_idx { - error!("Event ring full error, idx {}", idx); - let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); - self.write_event(&event, idx)?; - } else if ((intr.er_ep_idx + 1) % intr.er_size) as u64 == dp_idx { - bail!("Event Ring full, drop Event."); - } else { - self.write_event(evt, idx)?; - } - self.send_intr(idx); - Ok(()) - } - - fn write_event(&mut self, evt: &XhciEvent, idx: u32) -> Result<()> { - let intr = &mut self.intrs[idx as usize]; - intr.write_event(evt)?; - Ok(()) - } - - pub fn send_intr(&mut self, idx: u32) { - let pending = read_u32(self.intrs[idx as usize].erdp, 0) & ERDP_EHB == ERDP_EHB; - let mut erdp_low = read_u32(self.intrs[idx as usize].erdp, 0); - erdp_low |= ERDP_EHB; - self.intrs[idx as usize].erdp = write_u64_low(self.intrs[idx as usize].erdp, erdp_low); - self.intrs[idx as usize].iman |= IMAN_IP; - self.oper.set_usb_status_flag(USB_STS_EINT); - if pending { - return; - } - if self.intrs[idx as usize].iman & IMAN_IE != IMAN_IE { - return; - } - if self.oper.get_usb_cmd() & USB_CMD_INTE != USB_CMD_INTE { - return; - } - - if let Some(intr_ops) = self.send_interrupt_ops.as_ref() { - if intr_ops(idx, 1) { - self.intrs[idx as usize].iman &= !IMAN_IP; - } - } - } - - pub fn update_intr(&mut self, v: u32) { - let mut level = 0; - if v == 0 { - if self.intrs[0].iman & IMAN_IP == IMAN_IP - && self.intrs[0].iman & IMAN_IE == IMAN_IE - && self.oper.get_usb_cmd() & USB_CMD_INTE == USB_CMD_INTE - { - level = 1; - } - if let Some(intr_ops) = &self.send_interrupt_ops { - if intr_ops(0, level) { - self.intrs[0].iman &= !IMAN_IP; - } - } - } - } - /// Assign USB port and attach the device. pub fn assign_usb_port( &mut self, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 2cab3bf8d..c88756d81 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -234,8 +234,10 @@ impl PciDevOps for XhciPciDevice { let cloned_intx = self.pci_config.intx.as_ref().unwrap().clone(); let cloned_dev_id = self.dev_id.clone(); // Registers the msix to the xhci device for interrupt notification. - self.xhci.lock().unwrap().send_interrupt_ops = - Some(Box::new(move |n: u32, level: u8| -> bool { + self.xhci + .lock() + .unwrap() + .set_interrupt_ops(Arc::new(move |n: u32, level: u8| -> bool { let mut locked_msix = cloned_msix.lock().unwrap(); if locked_msix.enabled && level != 0 { locked_msix.notify(n as u16, cloned_dev_id.load(Ordering::Acquire)); diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index f07d534df..9e63f0550 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; -use anyhow::Result; +use anyhow::{bail, Result}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; @@ -173,6 +173,8 @@ pub struct XhciInterrupter { mem: Arc, oper_usb_cmd: Arc, oper_usb_status: Arc, + id: u32, + interrupt_cb: Option bool + Send + Sync>>, /// Interrupter Management pub iman: u32, /// Interrupter Morderation @@ -195,11 +197,14 @@ impl XhciInterrupter { mem: &Arc, oper_usb_cmd: &Arc, oper_usb_status: &Arc, + id: u32, ) -> Self { Self { mem: mem.clone(), oper_usb_cmd: oper_usb_cmd.clone(), oper_usb_status: oper_usb_status.clone(), + id, + interrupt_cb: None, iman: 0, imod: 0, erstsz: 0, @@ -212,6 +217,19 @@ impl XhciInterrupter { } } + pub fn set_interrupter(&mut self, cb: Arc bool + Send + Sync>) { + self.interrupt_cb = Some(cb); + } + + pub fn oper_intr_enabled(&self) -> bool { + self.oper_usb_cmd.load(Ordering::Acquire) & USB_CMD_INTE == USB_CMD_INTE + } + + pub fn enable_intr(&mut self) { + self.oper_usb_status + .fetch_or(USB_STS_EINT, Ordering::SeqCst); + } + pub fn reset(&mut self) { self.iman = 0; self.imod = 0; @@ -224,6 +242,78 @@ impl XhciInterrupter { self.er_ep_idx = 0; } + /// Send event TRB to driver, first write TRB and then send interrupt. + pub fn send_event(&mut self, evt: &XhciEvent) -> Result<()> { + let er_end = self + .er_start + .checked_add((TRB_SIZE * self.er_size) as u64) + .ok_or(UsbError::MemoryAccessOverflow( + self.er_start, + (TRB_SIZE * self.er_size) as u64, + ))?; + if self.erdp < self.er_start || self.erdp >= er_end { + bail!( + "DMA out of range, erdp {} er_start {:x} er_size {}", + self.erdp, + self.er_start, + self.er_size + ); + } + let dp_idx = (self.erdp - self.er_start) / TRB_SIZE as u64; + if ((self.er_ep_idx + 2) % self.er_size) as u64 == dp_idx { + error!("Event ring full error, idx {}", dp_idx); + let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); + self.write_event(&event)?; + } else if ((self.er_ep_idx + 1) % self.er_size) as u64 == dp_idx { + bail!("Event Ring full, drop Event."); + } else { + self.write_event(evt)?; + } + self.send_intr(); + Ok(()) + } + + fn send_intr(&mut self) { + let pending = read_u32(self.erdp, 0) & ERDP_EHB == ERDP_EHB; + let mut erdp_low = read_u32(self.erdp, 0); + erdp_low |= ERDP_EHB; + self.erdp = write_u64_low(self.erdp, erdp_low); + self.iman |= IMAN_IP; + self.enable_intr(); + if pending { + return; + } + if self.iman & IMAN_IE != IMAN_IE { + return; + } + if !self.oper_intr_enabled() { + return; + } + + if let Some(intr_ops) = self.interrupt_cb.as_ref() { + if intr_ops(self.id, 1) { + self.iman &= !IMAN_IP; + } + } + } + + fn update_intr(&mut self) { + if self.id == 0 { + let mut level = 0; + if self.iman & IMAN_IP == IMAN_IP + && self.iman & IMAN_IE == IMAN_IE + && self.oper_intr_enabled() + { + level = 1; + } + if let Some(intr_ops) = &self.interrupt_cb { + if intr_ops(0, level) { + self.iman &= !IMAN_IP; + } + } + } + } + /// Write event to the ring and update index. pub fn write_event(&mut self, evt: &XhciEvent) -> Result<()> { let mut ev_trb = evt.to_trb(); @@ -391,13 +481,14 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { if value & USB_CMD_HCRST == USB_CMD_HCRST { locked_xhci.reset(); } - locked_xhci.update_intr(0); + locked_xhci.intrs[0].lock().unwrap().update_intr(); } XHCI_OPER_REG_USBSTS => { // Write 1 to clear. locked_xhci.oper.unset_usb_status_flag( value & (USB_STS_HSE | USB_STS_EINT | USB_STS_PCD | USB_STS_SRE), - ) + ); + locked_xhci.intrs[0].lock().unwrap().update_intr(); } XHCI_OPER_REG_DNCTRL => locked_xhci.oper.dev_notify_ctrl = value & XHCI_OPER_NE_MASK, XHCI_OPER_REG_CMD_RING_CTRL_LO => { @@ -415,7 +506,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { let event = XhciEvent::new(TRBType::ErCommandComplete, TRBCCode::CommandRingStopped); crc_lo &= !CMD_RING_CTRL_CRR; - if let Err(e) = locked_xhci.send_event(&event, 0) { + if let Err(e) = locked_xhci.intrs[0].lock().unwrap().send_event(&event) { error!("Failed to send event: {:?}", e); } } else { @@ -463,20 +554,20 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } } else { let idx = ((offset - XHCI_INTR_REG_SIZE) >> XHCI_INTR_REG_SHIFT) as usize; - let mut xhci = xhci.lock().unwrap(); + let xhci = xhci.lock().unwrap(); if idx >= xhci.intrs.len() { error!("Invalid interrupter index: {} idx {}", offset, idx); return false; } - let intr = &mut xhci.intrs[idx]; + let locked_intr = xhci.intrs[idx].lock().unwrap(); value = match offset & 0x1f { - XHCI_INTR_REG_IMAN => intr.iman, - XHCI_INTR_REG_IMOD => intr.imod, - XHCI_INTR_REG_ERSTSZ => intr.erstsz, - XHCI_INTR_REG_ERSTBA_LO => read_u32(intr.erstba, 0), - XHCI_INTR_REG_ERSTBA_HI => read_u32(intr.erstba, 1), - XHCI_INTR_REG_ERDP_LO => read_u32(intr.erdp, 0), - XHCI_INTR_REG_ERDP_HI => read_u32(intr.erdp, 1), + XHCI_INTR_REG_IMAN => locked_intr.iman, + XHCI_INTR_REG_IMOD => locked_intr.imod, + XHCI_INTR_REG_ERSTSZ => locked_intr.erstsz, + XHCI_INTR_REG_ERSTBA_LO => read_u32(locked_intr.erstba, 0), + XHCI_INTR_REG_ERSTBA_HI => read_u32(locked_intr.erstba, 1), + XHCI_INTR_REG_ERDP_LO => read_u32(locked_intr.erdp, 0), + XHCI_INTR_REG_ERDP_HI => read_u32(locked_intr.erdp, 1), _ => { error!( "Invalid offset {:x} for reading interrupter registers.", @@ -506,23 +597,24 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { error!("Invalid interrupter index: {} idx {}", offset, idx); return false; } - let intr = &mut xhci.intrs[idx as usize]; + let mut locked_intr = xhci.intrs[idx as usize].lock().unwrap(); match offset & 0x1f { XHCI_INTR_REG_IMAN => { if value & IMAN_IP == IMAN_IP { - intr.iman &= !IMAN_IP; + locked_intr.iman &= !IMAN_IP; } - intr.iman &= !IMAN_IE; - intr.iman |= value & IMAN_IE; - xhci.update_intr(idx); + locked_intr.iman &= !IMAN_IE; + locked_intr.iman |= value & IMAN_IE; + locked_intr.update_intr(); } - XHCI_INTR_REG_IMOD => intr.imod = value, - XHCI_INTR_REG_ERSTSZ => intr.erstsz = value & 0xffff, + XHCI_INTR_REG_IMOD => locked_intr.imod = value, + XHCI_INTR_REG_ERSTSZ => locked_intr.erstsz = value & 0xffff, XHCI_INTR_REG_ERSTBA_LO => { - intr.erstba = write_u64_low(intr.erstba, value & 0xffffffc0); + locked_intr.erstba = write_u64_low(locked_intr.erstba, value & 0xffffffc0); } XHCI_INTR_REG_ERSTBA_HI => { - intr.erstba = write_u64_high(intr.erstba, value); + locked_intr.erstba = write_u64_high(locked_intr.erstba, value); + drop(locked_intr); if let Err(e) = xhci.reset_event_ring(idx) { error!("Failed to reset event ring: {:?}", e); } @@ -531,34 +623,37 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { // ERDP_EHB is write 1 clear. let mut erdp_lo = value & !ERDP_EHB; if value & ERDP_EHB != ERDP_EHB { - let erdp_old = read_u32(intr.erdp, 0); + let erdp_old = read_u32(locked_intr.erdp, 0); erdp_lo |= erdp_old & ERDP_EHB; } - intr.erdp = write_u64_low(intr.erdp, erdp_lo); + locked_intr.erdp = write_u64_low(locked_intr.erdp, erdp_lo); if value & ERDP_EHB == ERDP_EHB { - let erdp = intr.erdp; - let er_end = if let Some(addr) = - intr.er_start.checked_add((TRB_SIZE * intr.er_size) as u64) + let erdp = locked_intr.erdp; + let er_end = if let Some(addr) = locked_intr + .er_start + .checked_add((TRB_SIZE * locked_intr.er_size) as u64) { addr } else { error!( "Memory access overflow, addr {:x} offset {:x}", - intr.er_start, - (TRB_SIZE * intr.er_size) as u64 + locked_intr.er_start, + (TRB_SIZE * locked_intr.er_size) as u64 ); return false; }; - if erdp >= intr.er_start + if erdp >= locked_intr.er_start && erdp < er_end - && (erdp - intr.er_start) / TRB_SIZE as u64 != intr.er_ep_idx as u64 + && (erdp - locked_intr.er_start) / TRB_SIZE as u64 + != locked_intr.er_ep_idx as u64 { - xhci.send_intr(idx); + drop(locked_intr); + xhci.intrs[idx as usize].lock().unwrap().send_intr(); } } } XHCI_INTR_REG_ERDP_HI => { - intr.erdp = write_u64_high(intr.erdp, value); + locked_intr.erdp = write_u64_high(locked_intr.erdp, value); } _ => { error!( -- Gitee From bf00cac6721f238824c34fcaad77bfe5082f7164 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 11:04:55 +0800 Subject: [PATCH 0995/2187] xhci: support setting the ep state when complete transfer Support setting the endpoint state when complete transfer. And endpoint will be halted if transfer failed. Also, use complete_transfer directly in the complete_packet. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 144 +++++++++++++++--------- devices/src/usb/xhci/xhci_pci.rs | 2 +- devices/src/usb/xhci/xhci_ring.rs | 2 +- 3 files changed, 90 insertions(+), 58 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 396aeff50..aefb6d117 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -12,9 +12,8 @@ use std::collections::LinkedList; use std::mem::size_of; -use std::slice::from_raw_parts; -use std::slice::from_raw_parts_mut; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::slice::{from_raw_parts, from_raw_parts_mut}; +use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; @@ -36,7 +35,7 @@ use crate::usb::{ UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, }; -pub const MAX_INTRS: u32 = 16; +pub const MAX_INTRS: u32 = 1; pub const MAX_SLOTS: u32 = 64; /// Endpoint state pub const EP_STATE_MASK: u32 = 0x7; @@ -125,10 +124,15 @@ pub struct XhciTransfer { running_retry: bool, interrupter: Arc>, ep_ring: Arc, + ep_state: Arc, } impl XhciTransfer { - fn new(intr: &Arc>, ring: &Arc) -> Self { + fn new( + intr: &Arc>, + ring: &Arc, + ep_state: &Arc, + ) -> Self { XhciTransfer { packet: UsbPacket::default(), status: TRBCCode::Invalid, @@ -140,6 +144,7 @@ impl XhciTransfer { running_retry: false, interrupter: intr.clone(), ep_ring: ring.clone(), + ep_state: ep_state.clone(), } } @@ -151,6 +156,23 @@ impl XhciTransfer { self.complete.store(v, Ordering::SeqCst) } + pub fn complete_transfer(&mut self) -> Result<()> { + // NOTE: When entry this function, the transfer must be completed. + self.set_comleted(true); + + self.status = usb_packet_status_to_trb_code(self.packet.status)?; + if self.status == TRBCCode::Success { + self.submit_transfer()?; + self.ep_ring.refresh_dequeue_ptr()?; + return Ok(()); + } + + // Set the endpoint state to halted if an error occurs in the packet. + set_ep_state_helper(&self.ep_ring, &self.ep_state, EP_HALTED)?; + self.report_transfer_error()?; + Ok(()) + } + /// Submit the succeed transfer TRBs. pub fn submit_transfer(&mut self) -> Result<()> { // Event Data Transfer Length Accumulator. @@ -231,7 +253,7 @@ pub struct XhciEpContext { ring: Arc, ep_type: EpType, output_ctx_addr: Arc, - state: u32, + state: Arc, interval: u32, transfers: LinkedList, retry: Option, @@ -246,7 +268,7 @@ impl XhciEpContext { ring: Arc::new(XhciTransferRing::new(mem, &addr)), ep_type: EpType::Invalid, output_ctx_addr: addr, - state: 0, + state: Arc::new(AtomicU32::new(0)), interval: 0, transfers: LinkedList::new(), retry: None, @@ -263,17 +285,17 @@ impl XhciEpContext { self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); } + fn get_ep_state(&self) -> u32 { + self.state.load(Ordering::Acquire) + } + + fn set_ep_state(&self, state: u32) { + self.state.store(state, Ordering::SeqCst); + } + /// Update the endpoint state and write the state to memory. - fn set_state(&mut self, mem: &Arc, state: u32) -> Result<()> { - let mut ep_ctx = XhciEpCtx::default(); - let output_addr = self.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; - ep_ctx.ep_info &= !EP_STATE_MASK; - ep_ctx.ep_info |= state; - self.ring.update_dequeue_to_ctx(&mut ep_ctx); - dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; - self.state = state; - Ok(()) + fn set_state(&mut self, state: u32) -> Result<()> { + set_ep_state_helper(&self.ring, &self.state, state) } /// Update the dequeue pointer in endpoint context. @@ -304,6 +326,23 @@ impl XhciEpContext { } } +fn set_ep_state_helper( + ring: &Arc, + ep_state: &Arc, + state: u32, +) -> Result<()> { + let mem = &ring.mem; + let mut ep_ctx = XhciEpCtx::default(); + let output_addr = ring.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + ep_ctx.ep_info &= !EP_STATE_MASK; + ep_ctx.ep_info |= state; + ring.update_dequeue_to_ctx(&mut ep_ctx); + dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + ep_state.store(state, Ordering::SeqCst); + Ok(()) +} + /// Endpoint type, including control, bulk, interrupt and isochronous. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum EpType { @@ -1267,7 +1306,7 @@ impl XhciDevice { epctx.enabled = true; // It is safe to use plus here becuase we previously verify the address on the outer layer. epctx.init_ctx(output_ctx + EP_CTX_OFFSET + entry_offset, &ep_ctx); - epctx.state = EP_RUNNING; + epctx.set_ep_state(EP_RUNNING); ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= EP_RUNNING; dma_write_u32( @@ -1288,7 +1327,7 @@ impl XhciDevice { self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Invalid)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if self.oper.dcbaap != 0 { - epctx.set_state(&self.mem_space, EP_DISABLED)?; + epctx.set_state(EP_DISABLED)?; } epctx.enabled = false; Ok(TRBCCode::Success) @@ -1308,18 +1347,19 @@ impl XhciDevice { error!(" Endpoint is disabled, slotid {} epid {}", slot_id, ep_id); return Ok(TRBCCode::EpNotEnabledError); } - if epctx.state != EP_RUNNING { + if epctx.get_ep_state() != EP_RUNNING { error!( "Endpoint invalid state, slotid {} epid {} state {}", - slot_id, ep_id, epctx.state + slot_id, + ep_id, + epctx.get_ep_state() ); return Ok(TRBCCode::ContextStateError); } if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Stopped)? > 0 { warn!("endpoint stop when xfers running!"); } - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] - .set_state(&self.mem_space, EP_STOPPED)?; + self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].set_state(EP_STOPPED)?; Ok(TRBCCode::Success) } @@ -1338,7 +1378,7 @@ impl XhciDevice { error!("reset_endpoint ep is disabled"); return Ok(TRBCCode::EpNotEnabledError); } - if epctx.state != EP_HALTED { + if epctx.get_ep_state() != EP_HALTED { error!("Endpoint is not halted"); return Ok(TRBCCode::ContextStateError); } @@ -1349,7 +1389,7 @@ impl XhciDevice { let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; if let Some(port) = &slot.usb_port { if port.lock().unwrap().dev.is_some() { - epctx.set_state(&self.mem_space, EP_STOPPED)?; + epctx.set_state(EP_STOPPED)?; } else { error!("Failed to found usb device"); return Ok(TRBCCode::UsbTransactionError); @@ -1380,10 +1420,11 @@ impl XhciDevice { error!("Endpoint is disabled, slotid {} epid {}", slotid, epid); return Ok(TRBCCode::EpNotEnabledError); } - if epctx.state != EP_STOPPED && epctx.state != EP_ERROR { + let ep_state = epctx.get_ep_state(); + if ep_state != EP_STOPPED && ep_state != EP_ERROR { error!( "Endpoint invalid state, slotid {} epid {} state {}", - slotid, epid, epctx.state + slotid, epid, ep_state ); return Ok(TRBCCode::ContextStateError); } @@ -1417,17 +1458,18 @@ impl XhciDevice { } let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - if epctx.state == EP_HALTED { + if epctx.get_ep_state() == EP_HALTED { info!("xhci: endpoint halted"); return Ok(()); } - epctx.set_state(&self.mem_space, EP_RUNNING)?; + epctx.set_state(EP_RUNNING)?; + let ep_state = epctx.state.clone(); const KICK_LIMIT: u32 = 32; let mut count = 0; let ring = epctx.ring.clone(); loop { // NOTE: Only support primary interrupter now. - let mut xfer: XhciTransfer = XhciTransfer::new(&self.intrs[0], &ring); + let mut xfer: XhciTransfer = XhciTransfer::new(&self.intrs[0], &ring, &ep_state); xfer.slotid = slot_id; xfer.epid = ep_id; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; @@ -1453,7 +1495,7 @@ impl XhciDevice { } else { epctx.transfers.push_back(xfer.clone()); } - if epctx.state == EP_HALTED { + if epctx.get_ep_state() == EP_HALTED { break; } // retry @@ -1674,31 +1716,8 @@ impl XhciDevice { xfer.set_comleted(true); xfer.running_retry = false; } - if xfer.packet.status == UsbPacketStatus::Success { - xfer.status = TRBCCode::Success; - xfer.submit_transfer()?; - return Ok(()); - } - // Handle packet error status - match xfer.packet.status { - v if v == UsbPacketStatus::NoDev || v == UsbPacketStatus::IoError => { - xfer.status = TRBCCode::UsbTransactionError; - } - UsbPacketStatus::Stall => { - xfer.status = TRBCCode::StallError; - } - UsbPacketStatus::Babble => { - xfer.status = TRBCCode::BabbleDetected; - } - _ => { - error!("Unhandle status {:?}", xfer.packet.status); - } - } - xfer.report_transfer_error()?; - // Set the endpoint state to halted if an error occurs in the packet. - let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; - epctx.set_state(&self.mem_space, EP_HALTED)?; - Ok(()) + + xfer.complete_transfer() } /// Flush transfer in endpoint in some case such as stop endpoint. @@ -1795,6 +1814,19 @@ impl XhciDevice { } } +fn usb_packet_status_to_trb_code(status: UsbPacketStatus) -> Result { + let code = match status { + UsbPacketStatus::Success => TRBCCode::Success, + UsbPacketStatus::NoDev | UsbPacketStatus::IoError => TRBCCode::UsbTransactionError, + UsbPacketStatus::Stall => TRBCCode::StallError, + UsbPacketStatus::Babble => TRBCCode::BabbleDetected, + _ => { + bail!("Unhandle packet status {:?}", status); + } + }; + Ok(code) +} + // DMA read/write helpers. pub fn dma_read_bytes( addr_space: &Arc, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index c88756d81..d5306c2df 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -48,7 +48,7 @@ const XHCI_PCI_CAP_LENGTH: u32 = XHCI_CAP_LENGTH; const XHCI_PCI_OPER_OFFSET: u32 = XHCI_PCI_CAP_LENGTH; const XHCI_PCI_OPER_LENGTH: u32 = 0x400; const XHCI_PCI_RUNTIME_OFFSET: u32 = XHCI_OFF_RUNTIME; -const XHCI_PCI_RUNTIME_LENGTH: u32 = (MAX_INTRS as u32 + 1) * 0x20; +const XHCI_PCI_RUNTIME_LENGTH: u32 = (MAX_INTRS + 1) * 0x20; const XHCI_PCI_DOORBELL_OFFSET: u32 = XHCI_OFF_DOORBELL; const XHCI_PCI_DOORBELL_LENGTH: u32 = (MAX_SLOTS + 1) * 0x20; const XHCI_PCI_PORT_OFFSET: u32 = XHCI_PCI_OPER_OFFSET + XHCI_PCI_OPER_LENGTH; diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index f5eab6fa8..e1740515f 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -128,7 +128,7 @@ impl XhciCommandRing { /// XHCI Transfer Ring pub struct XhciTransferRing { - mem: Arc, + pub mem: Arc, pub dequeue: AtomicU64, /// Consumer Cycle State pub ccs: AtomicBool, -- Gitee From 84ed2b8f1ae32c257c3e179375014598d62ffff2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 29 Mar 2023 15:50:28 +0800 Subject: [PATCH 0996/2187] xhci: support async when flush endpoint transfer When refresh the endpoint transfer, also submit async transfer. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index aefb6d117..3db726279 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -122,6 +122,7 @@ pub struct XhciTransfer { epid: u32, in_xfer: bool, running_retry: bool, + running_async: bool, interrupter: Arc>, ep_ring: Arc, ep_state: Arc, @@ -142,6 +143,7 @@ impl XhciTransfer { epid: 0, in_xfer: false, running_retry: false, + running_async: false, interrupter: intr.clone(), ep_ring: ring.clone(), ep_state: ep_state.clone(), @@ -1653,13 +1655,12 @@ impl XhciDevice { } else { USB_TOKEN_OUT }; - let in_xfer = dir == USB_TOKEN_IN; // Map dma address to iovec. let mut vec = Vec::new(); for trb in &xfer.td { let trb_type = trb.get_type(); - if trb_type == TRBType::TrData && (trb.control & TRB_TR_DIR == 0) == in_xfer { + if trb_type == TRBType::TrData && (trb.control & TRB_TR_DIR == 0) == xfer.in_xfer { bail!("Direction of data transfer is mismatch"); } @@ -1669,7 +1670,7 @@ impl XhciDevice { { let chunk = trb.status & TRB_TR_LEN_MASK; let dma_addr = if trb.control & TRB_TR_IDT == TRB_TR_IDT { - if chunk > 8 && in_xfer { + if chunk > 8 && xfer.in_xfer { bail!("Invalid immediate data TRB"); } trb.addr @@ -1707,6 +1708,7 @@ impl XhciDevice { if xfer.packet.status == UsbPacketStatus::Async { xfer.set_comleted(false); xfer.running_retry = false; + xfer.running_async = true; return Ok(()); } else if xfer.packet.status == UsbPacketStatus::Nak { xfer.set_comleted(false); @@ -1729,6 +1731,9 @@ impl XhciDevice { .transfers .pop_front() { + if xfer.is_completed() { + continue; + } cnt += self.do_ep_transfer(slotid, epid, &mut xfer, report)?; if cnt != 0 { // Only report once. @@ -1749,6 +1754,16 @@ impl XhciDevice { report: TRBCCode, ) -> Result { let mut killed = 0; + + if xfer.running_async { + if report != TRBCCode::Invalid { + xfer.status = report; + xfer.submit_transfer()?; + } + xfer.running_async = false; + killed = 1; + } + if xfer.running_retry { if report != TRBCCode::Invalid { xfer.status = report; -- Gitee From 69fdfcbbcb8587f16bef0e119789af360e919a05 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 28 Mar 2023 11:01:43 +0800 Subject: [PATCH 0997/2187] usb: let the usb packet shareable Warp the usb packet with Arc> to make it shareable for later used in async transfer. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 9 +++-- devices/src/usb/keyboard.rs | 19 +++++---- devices/src/usb/mod.rs | 52 ++++++++++++++----------- devices/src/usb/storage.rs | 22 ++++++----- devices/src/usb/tablet.rs | 19 +++++---- devices/src/usb/xhci/xhci_controller.rs | 40 ++++++++++--------- 6 files changed, 91 insertions(+), 70 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index acd25be75..547dc628d 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -492,11 +492,12 @@ impl Default for UsbCamera { impl UsbDeviceOps for UsbCamera { fn reset(&mut self) {} - fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { debug!("Into camera handle_control"); + let mut locked_packet = packet.lock().unwrap(); match self .usb_device - .handle_control_for_descriptor(packet, device_req) + .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { if handled { @@ -507,12 +508,12 @@ impl UsbDeviceOps for UsbCamera { } Err(e) => { error!("Camera descriptor error {:?}", e); - packet.status = UsbPacketStatus::Stall; + locked_packet.status = UsbPacketStatus::Stall; } } } - fn handle_data(&mut self, _p: &mut UsbPacket) {} + fn handle_data(&mut self, _p: &Arc>) {} fn device_id(&self) -> String { self.id.clone() diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 3fc650b10..0c71406ed 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -196,11 +196,12 @@ impl UsbDeviceOps for UsbKeyboard { self.hid.reset(); } - fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { debug!("handle_control request {:?}", device_req); + let mut locked_packet = packet.lock().unwrap(); match self .usb_device - .handle_control_for_descriptor(packet, device_req) + .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { if handled { @@ -210,16 +211,20 @@ impl UsbDeviceOps for UsbKeyboard { } Err(e) => { error!("Keyboard descriptor error {:?}", e); - packet.status = UsbPacketStatus::Stall; + locked_packet.status = UsbPacketStatus::Stall; return; } } - self.hid - .handle_control_packet(packet, device_req, &mut self.usb_device.data_buf); + self.hid.handle_control_packet( + &mut locked_packet, + device_req, + &mut self.usb_device.data_buf, + ); } - fn handle_data(&mut self, p: &mut UsbPacket) { - self.hid.handle_data_packet(p); + fn handle_data(&mut self, p: &Arc>) { + let mut locked_p = p.lock().unwrap(); + self.hid.handle_data_packet(&mut locked_p); } fn device_id(&self) -> String { diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 308cc8c69..7e41c94c5 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -52,7 +52,6 @@ pub enum UsbPacketStatus { Stall, Babble, IoError, - Async, } /// USB request used to transfer to USB device. @@ -311,9 +310,11 @@ pub trait UsbDeviceOps: Send + Sync { } /// Handle usb packet, used for controller to deliever packet to device. - fn handle_packet(&mut self, packet: &mut UsbPacket) { - packet.status = UsbPacketStatus::Success; - let ep_nr = packet.ep_number; + fn handle_packet(&mut self, packet: &Arc>) { + let mut locked_packet = packet.lock().unwrap(); + locked_packet.status = UsbPacketStatus::Success; + let ep_nr = locked_packet.ep_number; + drop(locked_packet); debug!("handle packet endpointer number {}", ep_nr); if ep_nr == 0 { if let Err(e) = self.do_parameter(packet) { @@ -325,10 +326,10 @@ pub trait UsbDeviceOps: Send + Sync { } /// Handle control pakcet. - fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest); + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest); /// Handle data pakcet. - fn handle_data(&mut self, packet: &mut UsbPacket); + fn handle_data(&mut self, packet: &Arc>); /// Unique device id. fn device_id(&self) -> String; @@ -345,34 +346,37 @@ pub trait UsbDeviceOps: Send + Sync { usb_dev.speed } - fn do_parameter(&mut self, p: &mut UsbPacket) -> Result<()> { + fn do_parameter(&mut self, packet: &Arc>) -> Result<()> { let usb_dev = self.get_mut_usb_device(); + let mut locked_p = packet.lock().unwrap(); let device_req = UsbDeviceRequest { - request_type: p.parameter as u8, - request: (p.parameter >> 8) as u8, - value: (p.parameter >> 16) as u16, - index: (p.parameter >> 32) as u16, - length: (p.parameter >> 48) as u16, + request_type: locked_p.parameter as u8, + request: (locked_p.parameter >> 8) as u8, + value: (locked_p.parameter >> 16) as u16, + index: (locked_p.parameter >> 32) as u16, + length: (locked_p.parameter >> 48) as u16, }; if device_req.length as usize > usb_dev.data_buf.len() { - p.status = UsbPacketStatus::Stall; + locked_p.status = UsbPacketStatus::Stall; bail!("data buffer small len {}", device_req.length); } - if p.pid as u8 == USB_TOKEN_OUT { - p.transfer_packet(&mut usb_dev.data_buf, device_req.length as usize); + if locked_p.pid as u8 == USB_TOKEN_OUT { + locked_p.transfer_packet(&mut usb_dev.data_buf, device_req.length as usize); } - self.handle_control(p, &device_req); + drop(locked_p); + self.handle_control(packet, &device_req); + let mut locked_p = packet.lock().unwrap(); let usb_dev = self.get_mut_usb_device(); - if p.status == UsbPacketStatus::Async { + if locked_p.is_async { return Ok(()); } let mut len = device_req.length; - if len > p.actual_length as u16 { - len = p.actual_length as u16; + if len > locked_p.actual_length as u16 { + len = locked_p.actual_length as u16; } - if p.pid as u8 == USB_TOKEN_IN { - p.actual_length = 0; - p.transfer_packet(&mut usb_dev.data_buf, len as usize); + if locked_p.pid as u8 == USB_TOKEN_IN { + locked_p.actual_length = 0; + locked_p.transfer_packet(&mut usb_dev.data_buf, len as usize); } Ok(()) } @@ -418,10 +422,10 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { } /// Usb packet used for device transfer data. -#[derive(Clone)] pub struct UsbPacket { /// USB packet id. pub pid: u32, + pub is_async: bool, pub iovecs: Vec, /// control transfer parameter. pub parameter: u64, @@ -446,6 +450,7 @@ impl std::fmt::Display for UsbPacket { impl UsbPacket { pub fn init(&mut self, pid: u32, ep_number: u8) { self.pid = pid; + self.is_async = false; self.status = UsbPacketStatus::Success; self.actual_length = 0; self.parameter = 0; @@ -504,6 +509,7 @@ impl Default for UsbPacket { fn default() -> UsbPacket { UsbPacket { pid: 0, + is_async: false, iovecs: Vec::new(), parameter: 0, status: UsbPacketStatus::NoDev, diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 9bd3f2939..b3c0511d1 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -543,41 +543,43 @@ impl UsbDeviceOps for UsbStorage { self.state = UsbStorageState::new(); } - fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { debug!("Storage device handle_control request {:?}, ", device_req); + let mut locked_packet = packet.lock().unwrap(); match self .usb_device - .handle_control_for_descriptor(packet, device_req) + .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { if handled { debug!("Storage control handled by descriptor, return directly."); return; } - self.handle_control_packet(packet, device_req) + self.handle_control_packet(&mut locked_packet, device_req) } Err(e) => { error!("Storage descriptor error {:?}", e); - packet.status = UsbPacketStatus::Stall; + locked_packet.status = UsbPacketStatus::Stall; } } } - fn handle_data(&mut self, packet: &mut UsbPacket) { + fn handle_data(&mut self, packet: &Arc>) { + let mut locked_packet = packet.lock().unwrap(); debug!( "Storage device handle_data endpoint {}, mode {:?}", - packet.ep_number, self.state.mode + locked_packet.ep_number, self.state.mode ); - let result = match packet.pid as u8 { - USB_TOKEN_OUT => self.handle_token_out(packet), - USB_TOKEN_IN => self.handle_token_in(packet), + let result = match locked_packet.pid as u8 { + USB_TOKEN_OUT => self.handle_token_out(&mut locked_packet), + USB_TOKEN_IN => self.handle_token_in(&mut locked_packet), _ => Err(anyhow!("Bad token!")), }; if let Err(e) = result { warn!("USB-storage {}: handle data error: {:?}", self.id, e); - packet.status = UsbPacketStatus::Stall; + locked_packet.status = UsbPacketStatus::Stall; } } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index b7f5ef760..9ccbe42bf 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -183,11 +183,12 @@ impl UsbDeviceOps for UsbTablet { self.hid.reset(); } - fn handle_control(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { debug!("handle_control request {:?}", device_req); + let mut locked_packet = packet.lock().unwrap(); match self .usb_device - .handle_control_for_descriptor(packet, device_req) + .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { if handled { @@ -197,16 +198,20 @@ impl UsbDeviceOps for UsbTablet { } Err(e) => { error!("Tablet descriptor error {:?}", e); - packet.status = UsbPacketStatus::Stall; + locked_packet.status = UsbPacketStatus::Stall; return; } } - self.hid - .handle_control_packet(packet, device_req, &mut self.usb_device.data_buf); + self.hid.handle_control_packet( + &mut locked_packet, + device_req, + &mut self.usb_device.data_buf, + ); } - fn handle_data(&mut self, p: &mut UsbPacket) { - self.hid.handle_data_packet(p); + fn handle_data(&mut self, p: &Arc>) { + let mut locked_p = p.lock().unwrap(); + self.hid.handle_data_packet(&mut locked_p); } fn device_id(&self) -> String { diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 3db726279..f6a7cca65 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -114,7 +114,7 @@ type DmaAddr = u64; /// Transfer data between controller and device. #[derive(Clone)] pub struct XhciTransfer { - pub packet: UsbPacket, + pub packet: Arc>, status: TRBCCode, td: Vec, complete: Arc, @@ -135,7 +135,7 @@ impl XhciTransfer { ep_state: &Arc, ) -> Self { XhciTransfer { - packet: UsbPacket::default(), + packet: Arc::new(Mutex::new(UsbPacket::default())), status: TRBCCode::Invalid, td: Vec::new(), complete: Arc::new(AtomicBool::new(false)), @@ -154,15 +154,15 @@ impl XhciTransfer { self.complete.load(Ordering::Acquire) } - fn set_comleted(&mut self, v: bool) { + fn set_completed(&mut self, v: bool) { self.complete.store(v, Ordering::SeqCst) } pub fn complete_transfer(&mut self) -> Result<()> { // NOTE: When entry this function, the transfer must be completed. - self.set_comleted(true); + self.set_completed(true); - self.status = usb_packet_status_to_trb_code(self.packet.status)?; + self.status = usb_packet_status_to_trb_code(self.packet.lock().unwrap().status)?; if self.status == TRBCCode::Success { self.submit_transfer()?; self.ep_ring.refresh_dequeue_ptr()?; @@ -179,7 +179,7 @@ impl XhciTransfer { pub fn submit_transfer(&mut self) -> Result<()> { // Event Data Transfer Length Accumulator. let mut edtla: u32 = 0; - let mut left = self.packet.actual_length; + let mut left = self.packet.lock().unwrap().actual_length; for i in 0..self.td.len() { let trb = &self.td[i]; let trb_type = trb.get_type(); @@ -232,7 +232,7 @@ impl XhciTransfer { fn report_transfer_error(&mut self) -> Result<()> { // An error occurs in the transfer. The transfer is set to the completed and will not be retried. - self.set_comleted(true); + self.set_completed(true); let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); evt.slot_id = self.slotid as u8; evt.ep_id = self.epid as u8; @@ -1093,7 +1093,8 @@ impl XhciDevice { index: 0, length: 0, }; - locked_dev.handle_control(&mut p, &device_req); + let p = Arc::new(Mutex::new(p)); + locked_dev.handle_control(&p, &device_req); } fn get_device_context_addr(&self, slot_id: u32) -> Result { @@ -1548,7 +1549,7 @@ impl XhciDevice { .unwrap() .clone(); self.device_handle_packet(xfer); - if xfer.packet.status == UsbPacketStatus::Nak { + if xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { debug!("USB packet status is NAK"); // NAK need to retry again. return Ok(false); @@ -1566,9 +1567,9 @@ impl XhciDevice { fn device_handle_packet(&mut self, xfer: &mut XhciTransfer) { if let Ok(usb_dev) = self.get_usb_dev(xfer.slotid, xfer.epid) { let mut locked_dev = usb_dev.lock().unwrap(); - locked_dev.handle_packet(&mut xfer.packet); + locked_dev.handle_packet(&xfer.packet); } else { - xfer.packet.status = UsbPacketStatus::NoDev; + xfer.packet.lock().unwrap().status = UsbPacketStatus::NoDev; error!("Failed to handle packet, No endpoint found"); } } @@ -1598,7 +1599,7 @@ impl XhciDevice { xfer.status = TRBCCode::TrbError; return xfer.report_transfer_error(); } - xfer.packet.parameter = trb_setup.parameter; + xfer.packet.lock().unwrap().parameter = trb_setup.parameter; self.device_handle_packet(xfer); self.complete_packet(xfer)?; Ok(()) @@ -1685,8 +1686,9 @@ impl XhciDevice { } } let (_, ep_number) = endpoint_id_to_number(xfer.epid as u8); - xfer.packet.init(dir as u32, ep_number); - xfer.packet.iovecs = vec; + let mut locked_packet = xfer.packet.lock().unwrap(); + locked_packet.init(dir as u32, ep_number); + locked_packet.iovecs = vec; Ok(()) } @@ -1705,17 +1707,17 @@ impl XhciDevice { /// Update packet status and then submit transfer. fn complete_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - if xfer.packet.status == UsbPacketStatus::Async { - xfer.set_comleted(false); + if xfer.packet.lock().unwrap().is_async { xfer.running_retry = false; xfer.running_async = true; return Ok(()); - } else if xfer.packet.status == UsbPacketStatus::Nak { - xfer.set_comleted(false); + } + if xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { + xfer.set_completed(false); xfer.running_retry = true; return Ok(()); } else { - xfer.set_comleted(true); + xfer.set_completed(true); xfer.running_retry = false; } -- Gitee From bc4e4f1a609053d7b215366f06d61f1a4e9d4d64 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 8 Apr 2023 20:40:07 +0800 Subject: [PATCH 0998/2187] usb: add transfer ops in the packet to support async submit Add transfer ops in the usb packet to support async submit. And wrap the xhci transfer with Arc> to let it shareable. Signed-off-by: zhouli57 --- devices/src/usb/mod.rs | 31 ++++- devices/src/usb/xhci/xhci_controller.rs | 175 ++++++++++++++---------- 2 files changed, 124 insertions(+), 82 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 7e41c94c5..21e312897 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -421,6 +421,11 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { Ok(()) } +/// Transfer ops for submit callback. +pub trait TransferOps: Send + Sync { + fn submit_transfer(&mut self); +} + /// Usb packet used for device transfer data. pub struct UsbPacket { /// USB packet id. @@ -435,6 +440,8 @@ pub struct UsbPacket { pub actual_length: u32, /// Endpoint number. pub ep_number: u8, + /// Transfer for complete packet. + pub xfer_ops: Option>>, } impl std::fmt::Display for UsbPacket { @@ -448,13 +455,22 @@ impl std::fmt::Display for UsbPacket { } impl UsbPacket { - pub fn init(&mut self, pid: u32, ep_number: u8) { - self.pid = pid; - self.is_async = false; - self.status = UsbPacketStatus::Success; - self.actual_length = 0; - self.parameter = 0; - self.ep_number = ep_number; + pub fn new( + pid: u32, + ep_number: u8, + iovecs: Vec, + xfer_ops: Option>>, + ) -> Self { + Self { + pid, + is_async: false, + iovecs, + parameter: 0, + status: UsbPacketStatus::Success, + actual_length: 0, + ep_number, + xfer_ops, + } } /// Transfer USB packet from host to device or from device to host. @@ -515,6 +531,7 @@ impl Default for UsbPacket { status: UsbPacketStatus::NoDev, actual_length: 0, ep_number: 0, + xfer_ops: None, } } } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index f6a7cca65..e6d360e27 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -13,7 +13,7 @@ use std::collections::LinkedList; use std::mem::size_of; use std::slice::{from_raw_parts, from_raw_parts_mut}; -use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; @@ -30,7 +30,7 @@ use super::{ TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_TR_DIR, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TYPE_SHIFT, }; -use crate::usb::config::*; +use crate::usb::{config::*, TransferOps}; use crate::usb::{ UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, }; @@ -112,12 +112,11 @@ const EP_CONTEXT_EP_TYPE_SHIFT: u32 = 3; type DmaAddr = u64; /// Transfer data between controller and device. -#[derive(Clone)] pub struct XhciTransfer { pub packet: Arc>, status: TRBCCode, td: Vec, - complete: Arc, + complete: bool, slotid: u32, epid: u32, in_xfer: bool, @@ -130,6 +129,10 @@ pub struct XhciTransfer { impl XhciTransfer { fn new( + slotid: u32, + epid: u32, + in_xfer: bool, + td: Vec, intr: &Arc>, ring: &Arc, ep_state: &Arc, @@ -137,11 +140,11 @@ impl XhciTransfer { XhciTransfer { packet: Arc::new(Mutex::new(UsbPacket::default())), status: TRBCCode::Invalid, - td: Vec::new(), - complete: Arc::new(AtomicBool::new(false)), - slotid: 0, - epid: 0, - in_xfer: false, + td, + complete: false, + slotid, + epid, + in_xfer, running_retry: false, running_async: false, interrupter: intr.clone(), @@ -150,17 +153,9 @@ impl XhciTransfer { } } - fn is_completed(&self) -> bool { - self.complete.load(Ordering::Acquire) - } - - fn set_completed(&mut self, v: bool) { - self.complete.store(v, Ordering::SeqCst) - } - pub fn complete_transfer(&mut self) -> Result<()> { // NOTE: When entry this function, the transfer must be completed. - self.set_completed(true); + self.complete = true; self.status = usb_packet_status_to_trb_code(self.packet.lock().unwrap().status)?; if self.status == TRBCCode::Success { @@ -232,7 +227,7 @@ impl XhciTransfer { fn report_transfer_error(&mut self) -> Result<()> { // An error occurs in the transfer. The transfer is set to the completed and will not be retried. - self.set_completed(true); + self.complete = true; let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); evt.slot_id = self.slotid as u8; evt.ep_id = self.epid as u8; @@ -248,6 +243,14 @@ impl XhciTransfer { } } +impl TransferOps for XhciTransfer { + fn submit_transfer(&mut self) { + if let Err(e) = self.complete_transfer() { + error!("Failed to submit transfer, error {:?}", e); + } + } +} + /// Endpoint context which use the ring to transfer data. pub struct XhciEpContext { epid: u32, @@ -257,8 +260,8 @@ pub struct XhciEpContext { output_ctx_addr: Arc, state: Arc, interval: u32, - transfers: LinkedList, - retry: Option, + transfers: LinkedList>>, + retry: Option>>, } impl XhciEpContext { @@ -320,7 +323,7 @@ impl XhciEpContext { fn flush_transfer(&mut self) { let mut undo = LinkedList::new(); while let Some(head) = self.transfers.pop_front() { - if !head.is_completed() { + if !head.lock().unwrap().complete { undo.push_back(head); } } @@ -1083,9 +1086,7 @@ impl XhciDevice { /// Send SET_ADDRESS request to usb device. fn set_device_address(&mut self, dev: &Arc>, addr: u32) { - let mut p = UsbPacket::default(); let mut locked_dev = dev.lock().unwrap(); - p.init(USB_TOKEN_OUT as u32, 0); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, request: USB_REQUEST_SET_ADDRESS, @@ -1093,7 +1094,12 @@ impl XhciDevice { index: 0, length: 0, }; - let p = Arc::new(Mutex::new(p)); + let p = Arc::new(Mutex::new(UsbPacket::new( + USB_TOKEN_OUT as u32, + 0, + Vec::new(), + None, + ))); locked_dev.handle_control(&p, &device_req); } @@ -1471,39 +1477,59 @@ impl XhciDevice { let mut count = 0; let ring = epctx.ring.clone(); loop { - // NOTE: Only support primary interrupter now. - let mut xfer: XhciTransfer = XhciTransfer::new(&self.intrs[0], &ring, &ep_state); - xfer.slotid = slot_id; - xfer.epid = ep_id; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - match epctx.ring.fetch_td()? { + let td = match epctx.ring.fetch_td()? { Some(td) => { debug!( "fetch transfer trb {:?} ring dequeue {:?}", td, epctx.ring.get_dequeue_ptr(), ); - xfer.td = td; + td } None => { debug!("No TD in the transfer ring."); break; } - } - self.endpoint_do_transfer(&mut xfer)?; + }; + let in_xfer = transfer_in_direction(ep_id as u8, &td, epctx.ep_type); + // NOTE: Only support primary interrupter now. + let xfer = Arc::new(Mutex::new(XhciTransfer::new( + slot_id, + ep_id, + in_xfer, + td, + &self.intrs[0], + &ring, + &ep_state, + ))); + let packet = match self.setup_usb_packet(&xfer) { + Ok(pkt) => pkt, + Err(e) => { + error!("Failed to setup packet {:?}", e); + let mut locked_xfer = xfer.lock().unwrap(); + locked_xfer.status = TRBCCode::TrbError; + return locked_xfer.report_transfer_error(); + } + }; + let mut locked_xfer = xfer.lock().unwrap(); + locked_xfer.packet = packet; + self.endpoint_do_transfer(&mut locked_xfer)?; let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - if xfer.is_completed() { + if locked_xfer.complete { epctx.update_dequeue(&self.mem_space, None)?; - epctx.flush_transfer(); } else { epctx.transfers.push_back(xfer.clone()); } + drop(locked_xfer); + epctx.flush_transfer(); if epctx.get_ep_state() == EP_HALTED { break; } // retry - if !xfer.is_completed() && xfer.running_retry { - epctx.retry = Some(xfer); + let locked_xfer = xfer.lock().unwrap(); + if !locked_xfer.complete && locked_xfer.running_retry { + epctx.retry = Some(xfer.clone()); break; } count += 1; @@ -1543,20 +1569,22 @@ impl XhciDevice { fn endpoint_retry_transfer(&mut self, slot_id: u32, ep_id: u32) -> Result { let slot = &mut self.slots[(slot_id - 1) as usize]; // Safe because the retry is checked in the outer function call. - let xfer = &mut slot.endpoints[(ep_id - 1) as usize] + let xfer = slot.endpoints[(ep_id - 1) as usize] .retry .as_ref() .unwrap() .clone(); - self.device_handle_packet(xfer); - if xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { + let mut locked_xfer = xfer.lock().unwrap(); + self.device_handle_packet(&mut locked_xfer); + if locked_xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { debug!("USB packet status is NAK"); // NAK need to retry again. return Ok(false); } - self.complete_packet(xfer)?; + self.complete_packet(&mut locked_xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - if xfer.is_completed() { + if locked_xfer.complete { + drop(locked_xfer); epctx.update_dequeue(&self.mem_space, None)?; epctx.flush_transfer(); } @@ -1591,14 +1619,6 @@ impl XhciDevice { return xfer.report_transfer_error(); } let trb_setup = xfer.td[0]; - let bm_request_type = trb_setup.parameter as u8; - xfer.in_xfer = - bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; - if let Err(e) = self.setup_usb_packet(xfer) { - error!("Failed to setup packet when transfer control {:?}", e); - xfer.status = TRBCCode::TrbError; - return xfer.report_transfer_error(); - } xfer.packet.lock().unwrap().parameter = trb_setup.parameter; self.device_handle_packet(xfer); self.complete_packet(xfer)?; @@ -1634,24 +1654,18 @@ impl XhciDevice { } fn do_data_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; - xfer.in_xfer = epctx.ep_type == EpType::Control - || epctx.ep_type == EpType::IsoIn - || epctx.ep_type == EpType::BulkIn - || epctx.ep_type == EpType::IntrIn; - if let Err(e) = self.setup_usb_packet(xfer) { - error!("Failed to setup packet when transfer data {:?}", e); - xfer.status = TRBCCode::TrbError; - return xfer.report_transfer_error(); - } self.device_handle_packet(xfer); self.complete_packet(xfer)?; Ok(()) } // Setup USB packet, include mapping dma address to iovector. - fn setup_usb_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { - let dir = if xfer.in_xfer { + fn setup_usb_packet( + &mut self, + xfer: &Arc>, + ) -> Result>> { + let locked_xfer = xfer.lock().unwrap(); + let dir = if locked_xfer.in_xfer { USB_TOKEN_IN } else { USB_TOKEN_OUT @@ -1659,9 +1673,10 @@ impl XhciDevice { // Map dma address to iovec. let mut vec = Vec::new(); - for trb in &xfer.td { + for trb in &locked_xfer.td { let trb_type = trb.get_type(); - if trb_type == TRBType::TrData && (trb.control & TRB_TR_DIR == 0) == xfer.in_xfer { + if trb_type == TRBType::TrData && (trb.control & TRB_TR_DIR == 0) == locked_xfer.in_xfer + { bail!("Direction of data transfer is mismatch"); } @@ -1671,7 +1686,7 @@ impl XhciDevice { { let chunk = trb.status & TRB_TR_LEN_MASK; let dma_addr = if trb.control & TRB_TR_IDT == TRB_TR_IDT { - if chunk > 8 && xfer.in_xfer { + if chunk > 8 && locked_xfer.in_xfer { bail!("Invalid immediate data TRB"); } trb.addr @@ -1685,11 +1700,10 @@ impl XhciDevice { } } } - let (_, ep_number) = endpoint_id_to_number(xfer.epid as u8); - let mut locked_packet = xfer.packet.lock().unwrap(); - locked_packet.init(dir as u32, ep_number); - locked_packet.iovecs = vec; - Ok(()) + let (_, ep_number) = endpoint_id_to_number(locked_xfer.epid as u8); + let xfer_ops = Arc::downgrade(xfer) as Weak>; + let packet = UsbPacket::new(dir as u32, ep_number, vec, Some(xfer_ops)); + Ok(Arc::new(Mutex::new(packet))) } fn get_usb_dev(&self, slotid: u32, epid: u32) -> Result>> { @@ -1713,11 +1727,11 @@ impl XhciDevice { return Ok(()); } if xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { - xfer.set_completed(false); + xfer.complete = false; xfer.running_retry = true; return Ok(()); } else { - xfer.set_completed(true); + xfer.complete = true; xfer.running_retry = false; } @@ -1729,14 +1743,15 @@ impl XhciDevice { debug!("flush_ep_transfer slotid {} epid {}", slotid, epid); let mut cnt = 0; let mut report = report; - while let Some(mut xfer) = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] + while let Some(xfer) = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] .transfers .pop_front() { - if xfer.is_completed() { + let mut locked_xfer = xfer.lock().unwrap(); + if locked_xfer.complete { continue; } - cnt += self.do_ep_transfer(slotid, epid, &mut xfer, report)?; + cnt += self.do_ep_transfer(slotid, epid, &mut locked_xfer, report)?; if cnt != 0 { // Only report once. report = TRBCCode::Invalid; @@ -1934,3 +1949,13 @@ fn endpoint_number_to_id(in_direction: bool, ep_number: u8) -> u8 { ep_number * 2 } } + +fn transfer_in_direction(ep_id: u8, td: &[XhciTRB], ep_type: EpType) -> bool { + if ep_id == 1 { + let trb_setup = td[0]; + let bm_request_type = trb_setup.parameter as u8; + bm_request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST + } else { + ep_type == EpType::IsoIn || ep_type == EpType::BulkIn || ep_type == EpType::IntrIn + } +} -- Gitee From e93d4569dfa2de7fc5d2708f2fb8edc65ae41581 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 7 Mar 2023 00:33:52 +0800 Subject: [PATCH 0999/2187] VNC: fix a bug 1. It can ensure that the length of buffer in calling function is greater than 12. 2. Parsing the buffer into UTF-8 format does not guarantee that the length remains unchanged, so is not necessary to use res[0..12]; Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 79f2e3227..1b32410e7 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -501,8 +501,7 @@ impl ClientIoHandler { fn handle_version(&mut self) -> Result<()> { let client = self.client.clone(); let buf = self.read_incoming_msg(); - let res = String::from_utf8_lossy(&buf); - let ver_str = &res[0..12].to_string(); + let ver_str = String::from_utf8_lossy(&buf).to_string(); let ver = match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { Ok(v) => v, Err(_e) => { -- Gitee From 04fdcb352bfed641116052ce29e97f99cfcc9c1b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 4 Mar 2023 10:49:09 +0800 Subject: [PATCH 1000/2187] =?UTF-8?q?Ui:=20Make=20some=20adjustments=20to?= =?UTF-8?q?=20ui=20console=201.=20The=20function=20of=20console=5Fclose=20?= =?UTF-8?q?will=20not=20close=20the=20image=20in=20console=EF=BC=8C=20but?= =?UTF-8?q?=20replace=20the=20image=20in=20console=20to=20the=20place=20ho?= =?UTF-8?q?ld=20image.=202.=20Add=20an=20interface=20to=20obtain=20the=20w?= =?UTF-8?q?eak=20reference=20of=20all=20active=20consoles.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Xiao Ye --- devices/src/legacy/ramfb.rs | 6 +- pci/src/demo_device/gpu_device.rs | 5 +- ui/src/console.rs | 186 ++++++++++++++++-------------- virtio/src/device/gpu.rs | 8 +- 4 files changed, 113 insertions(+), 92 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index d50dc05d5..606a29993 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -21,8 +21,8 @@ use std::mem::size_of; use std::sync::{Arc, Mutex}; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use ui::console::{ - console_init, display_graphic_update, display_replace_surface, DisplayConsole, DisplaySurface, - HardWareOperations, + console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, + DisplaySurface, HardWareOperations, }; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; @@ -188,7 +188,7 @@ impl FwCfgWriteCallback for RamfbState { width: width as i32, height: height as i32, }); - let con = console_init(ramfb_opts); + let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); display_replace_surface(&con, self.surface) .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); } diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 57cedaedb..227175237 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -30,7 +30,8 @@ use std::{ use ui::{ console::{ console_close, console_init, display_cursor_define, display_graphic_update, - display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, + display_replace_surface, ConsoleType, DisplayConsole, DisplayMouse, DisplaySurface, + HardWareOperations, }, pixman::{ create_pixman_image, get_image_data, get_image_format, get_image_stride, ref_pixman_image, @@ -208,7 +209,7 @@ impl DeviceTypeOperation for DemoGpu { fn realize(&mut self) -> Result<()> { let con_opts = Arc::new(HwOpts {}); - self.con = console_init(con_opts); + self.con = console_init("demo-gpu".to_string(), ConsoleType::Graphic, con_opts); // Create Image. self.width = 640; diff --git a/ui/src/console.rs b/ui/src/console.rs index c656aa985..7e934668f 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -34,9 +34,9 @@ const FONT_WIDTH: i32 = 8; /// Height of font. const FONT_HEIGHT: i32 = 16; /// Width of image in surface. -const DEFAULT_SURFACE_WIDTH: i32 = 640; +pub const DEFAULT_SURFACE_WIDTH: i32 = 640; /// Height of image in surface. -const DEFAULT_SURFACE_HEIGHT: i32 = 480; +pub const DEFAULT_SURFACE_HEIGHT: i32 = 480; /// Maximum default window width. pub const MAX_WINDOW_WIDTH: u16 = 2560; /// Maximum default window height. @@ -51,6 +51,11 @@ pub const DISPLAY_UPDATE_INTERVAL_MAX: u64 = 3_000; /// Millisecond to nanosecond. pub const MILLI_PER_SEC: u64 = 1_000_000; +pub enum ConsoleType { + Graphic, + Text, +} + /// Image data defined in display. #[derive(Clone, Copy)] pub struct DisplaySurface { @@ -115,10 +120,10 @@ pub struct DisplayChangeListener { } impl DisplayChangeListener { - pub fn new(dcl_id: Option, dpy_opts: Arc) -> Self { + pub fn new(con_id: Option, dpy_opts: Arc) -> Self { Self { - con_id: None, - dcl_id, + con_id, + dcl_id: None, active: false, update_interval: 0, dpy_opts, @@ -129,27 +134,35 @@ impl DisplayChangeListener { /// Graphic hardware can register a console during initialization /// and store the information of images in this structure. pub struct DisplayConsole { - pub con_id: Option, + pub con_id: usize, + pub dev_name: String, + pub con_type: ConsoleType, pub width: i32, pub height: i32, pub surface: Option, pub console_list: Weak>, dev_opts: Arc, + active: bool, } impl DisplayConsole { pub fn new( - con_id: Option, + con_id: usize, + dev_name: String, + con_type: ConsoleType, console_list: Weak>, dev_opts: Arc, ) -> Self { Self { con_id, - width: DEFAULT_SURFACE_WIDTH, - height: DEFAULT_SURFACE_HEIGHT, + dev_name, + con_type, + width: 0, + height: 0, console_list, surface: None, dev_opts, + active: true, } } } @@ -314,7 +327,7 @@ pub fn display_replace_surface( dcl_id = activate_id; } - if con_id == dcl_id { + if Some(con_id) == dcl_id { related_listeners.push(dcl.clone()); } } @@ -367,7 +380,7 @@ pub fn display_graphic_update( dcl_id = activate_id; } - if con_id == dcl_id { + if Some(con_id) == dcl_id { related_listeners.push(dcl.clone()); } } @@ -404,7 +417,7 @@ pub fn display_cursor_define( dcl_id = activate_id; } - if con_id == dcl_id { + if Some(con_id) == dcl_id { related_listeners.push(dcl.clone()); } } @@ -425,6 +438,19 @@ pub fn graphic_hardware_update(con_id: Option) { } } +/// Get the weak reference of all active consoles from the console lists. +pub fn get_active_console() -> Vec>> { + let mut res: Vec>> = vec![]; + let locked_cons = CONSOLES.lock().unwrap(); + for con in locked_cons.console_list.iter().flatten() { + if con.lock().unwrap().active { + res.push(Arc::downgrade(con)); + } + } + + res +} + /// Register a dcl and return the id. pub fn register_display(dcl: &Arc>) -> Result<()> { let mut dcl_id = 0; @@ -496,38 +522,43 @@ pub fn unregister_display(dcl: &Option>>) -> R /// Create a console and add into a global list. Then returen a console id /// for later finding the assigned console. -pub fn console_init(dev_opts: Arc) -> Option>> { +pub fn console_init( + dev_name: String, + con_type: ConsoleType, + dev_opts: Arc, +) -> Option>> { let mut locked_consoles = CONSOLES.lock().unwrap(); - let len = locked_consoles.console_list.len(); - let mut con_id = len; - for idx in 0..len { - if locked_consoles.console_list[idx].is_none() { - con_id = idx; - break; + for con in locked_consoles.console_list.iter().flatten() { + let mut locked_con = con.lock().unwrap(); + if locked_con.dev_name == dev_name { + locked_con.active = true; + locked_con.dev_opts = dev_opts; + return Some(Arc::downgrade(con)); } } - let mut new_console = - DisplayConsole::new(Some(con_id), Arc::downgrade(&CONSOLES), dev_opts.clone()); - new_console.surface = create_msg_surface( - DEFAULT_SURFACE_WIDTH, - DEFAULT_SURFACE_HEIGHT, - "Guest has not initialized the display yet.".to_string(), + + let con_id = locked_consoles.console_list.len(); + let new_con = DisplayConsole::new( + con_id, + dev_name, + con_type, + Arc::downgrade(&CONSOLES), + dev_opts, ); - new_console.width = DEFAULT_SURFACE_WIDTH; - new_console.height = DEFAULT_SURFACE_HEIGHT; - let console = Arc::new(Mutex::new(new_console)); - if con_id < len { - locked_consoles.console_list[con_id] = Some(console.clone()) - } else { - locked_consoles.console_list.push(Some(console.clone())); - } + let con = Arc::new(Mutex::new(new_con)); + locked_consoles.console_list.push(Some(con.clone())); if locked_consoles.activate_id.is_none() { locked_consoles.activate_id = Some(con_id); } drop(locked_consoles); - let con = Arc::downgrade(&console); - display_replace_surface(&Some(con.clone()), None) + let con = Arc::downgrade(&con); + let surface = create_msg_surface( + DEFAULT_SURFACE_WIDTH, + DEFAULT_SURFACE_HEIGHT, + "Guest has not initialized the display yet.".to_string(), + ); + display_replace_surface(&Some(con.clone()), surface) .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); Some(con) } @@ -538,35 +569,36 @@ pub fn console_close(console: &Option>>) -> Result<() Some(c) => c, None => return Ok(()), }; - let locked_con = con.lock().unwrap(); - let con_id = locked_con.con_id; + let mut locked_con = con.lock().unwrap(); if let Some(surface) = locked_con.surface { unref_pixman_image(surface.image); } + locked_con.active = false; + locked_con.surface = create_msg_surface( + DEFAULT_SURFACE_WIDTH, + DEFAULT_SURFACE_HEIGHT, + "Display is not active.".to_string(), + ); + let con_id = locked_con.con_id; drop(locked_con); + + // If the active console is closed, reset the active console. let mut locked_consoles = CONSOLES.lock().unwrap(); - if con_id.is_none() { - return Ok(()); - } - let len = locked_consoles.console_list.len(); - let id = con_id.unwrap_or(len); - if id >= len { - return Ok(()); - } - locked_consoles.console_list[id] = None; match locked_consoles.activate_id { - Some(activate_id) if id == activate_id => { - locked_consoles.activate_id = None; - for i in 0..len { - if locked_consoles.console_list[i].is_some() { - locked_consoles.activate_id = Some(i); + Some(active_con) if active_con == con_id => { + let mut active_id: Option = None; + for con in locked_consoles.console_list.iter().flatten() { + let locked_con = con.lock().unwrap(); + if locked_con.active { + active_id = Some(locked_con.con_id); break; } } + locked_consoles.activate_id = active_id; } _ => {} } - drop(locked_consoles); + Ok(()) } @@ -582,12 +614,8 @@ pub fn console_select(con_id: Option) -> Result<()> { locked_consoles.activate_id = Some(id); locked_consoles.console_list[id].clone() } - _ => None, + _ => return Ok(()), }; - let activate_id: Option = locked_consoles.activate_id; - if activate_id.is_none() { - return Ok(()); - } drop(locked_consoles); let mut related_listeners: Vec>> = vec![]; @@ -689,34 +717,24 @@ mod tests { #[test] fn test_console_select() { let con_opts = Arc::new(HwOpts {}); - let con_0 = console_init(con_opts.clone()); - assert_eq!( - con_0 - .clone() - .unwrap() - .upgrade() - .unwrap() - .lock() - .unwrap() - .con_id, - Some(0) - ); - let con_1 = console_init(con_opts.clone()); - assert_eq!( - con_1.unwrap().upgrade().unwrap().lock().unwrap().con_id, - Some(1) - ); - let con_2 = console_init(con_opts.clone()); + let dev_name0 = format!("test_device0"); + let con_0 = console_init(dev_name0, ConsoleType::Graphic, con_opts.clone()); + let clone_con = con_0.clone(); assert_eq!( - con_2.unwrap().upgrade().unwrap().lock().unwrap().con_id, - Some(2) + clone_con.unwrap().upgrade().unwrap().lock().unwrap().con_id, + 0 ); + let dev_name1 = format!("test_device1"); + let con_1 = console_init(dev_name1, ConsoleType::Graphic, con_opts.clone()); + assert_eq!(con_1.unwrap().upgrade().unwrap().lock().unwrap().con_id, 1); + let dev_name2 = format!("test_device2"); + let con_2 = console_init(dev_name2, ConsoleType::Graphic, con_opts.clone()); + assert_eq!(con_2.unwrap().upgrade().unwrap().lock().unwrap().con_id, 2); assert!(console_close(&con_0).is_ok()); - let con_3 = console_init(con_opts.clone()); - assert_eq!( - con_3.unwrap().upgrade().unwrap().lock().unwrap().con_id, - Some(0) - ); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(1)); + let dev_name3 = format!("test_device3"); + let con_3 = console_init(dev_name3, ConsoleType::Graphic, con_opts.clone()); + assert_eq!(con_3.unwrap().upgrade().unwrap().lock().unwrap().con_id, 3); assert!(console_select(Some(0)).is_ok()); assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(0)); assert!(console_select(Some(1)).is_ok()); @@ -724,9 +742,9 @@ mod tests { assert!(console_select(Some(2)).is_ok()); assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(2)); assert!(console_select(Some(3)).is_ok()); - assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(2)); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(3)); assert!(console_select(None).is_ok()); - assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(2)); + assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(3)); } #[test] diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a5984728c..da5142fc2 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -38,7 +38,8 @@ use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; use ui::console::{ console_close, console_init, display_cursor_define, display_graphic_update, - display_replace_surface, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, + display_replace_surface, ConsoleType, DisplayConsole, DisplayMouse, DisplaySurface, + HardWareOperations, }; use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; use util::byte_code::ByteCode; @@ -1648,10 +1649,11 @@ impl VirtioDevice for Gpu { self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; let mut scanouts = vec![]; - for _i in 0..VIRTIO_GPU_MAX_SCANOUTS { + for i in 0..VIRTIO_GPU_MAX_SCANOUTS { let mut scanout = GpuScanout::default(); let gpu_opts = Arc::new(GpuOpts::default()); - scanout.con = console_init(gpu_opts); + let dev_name = format!("virtio-gpu{}", i); + scanout.con = console_init(dev_name, ConsoleType::Graphic, gpu_opts); scanouts.push(scanout); } -- Gitee From 688a08115bbaffa4c996cd6430b48809cebc9c3d Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 6 Mar 2023 22:20:33 +0800 Subject: [PATCH 1001/2187] Ramfb: adapt to gtk. Move the operation of console init from device active to device realize. Signed-off-by: Xiao Ye --- devices/src/legacy/ramfb.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 606a29993..24cb8f46c 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -18,7 +18,7 @@ use anyhow::Context; use drm_fourcc::DrmFourcc; use log::error; use std::mem::size_of; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, Weak}; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use ui::console::{ console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, @@ -43,6 +43,7 @@ struct RamfbCfg { #[derive(Clone)] pub struct RamfbState { pub surface: Option, + pub con: Option>>, sys_mem: Arc, } @@ -56,8 +57,11 @@ unsafe impl Send for RamfbState {} impl RamfbState { pub fn new(sys_mem: Arc) -> Self { + let ramfb_opts = Arc::new(RamfbInterface {}); + let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); Self { surface: None, + con, sys_mem, } } @@ -183,24 +187,19 @@ impl FwCfgWriteCallback for RamfbState { }; self.create_display_surface(width, height, format, stride, addr); - - let ramfb_opts = Arc::new(RamfbInterface { - width: width as i32, - height: height as i32, - }); - let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); - display_replace_surface(&con, self.surface) + display_replace_surface(&self.con, self.surface) .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); } } -pub struct RamfbInterface { - width: i32, - height: i32, -} +pub struct RamfbInterface {} impl HardWareOperations for RamfbInterface { fn hw_update(&self, con: Arc>) { - display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, self.width, self.height) + let locked_con = con.lock().unwrap(); + let width = locked_con.width; + let height = locked_con.height; + drop(locked_con); + display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, width, height) .unwrap_or_else(|e| error!("Error occurs during graphic updating: {:?}", e)); } } -- Gitee From 8168e3e4df6373d52911bb6fae07d0a3b54a28ae Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 7 Mar 2023 00:06:01 +0800 Subject: [PATCH 1002/2187] Virtio-gpu: adapt to gtk Move the operation of console init from gpu active to gpu realize. Signed-off-by: Xiao Ye --- virtio/src/device/gpu.rs | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index da5142fc2..c43b6ef8e 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1425,11 +1425,6 @@ impl GpuIoHandler { impl Drop for GpuIoHandler { fn drop(&mut self) { - for scanout in &self.scanouts { - console_close(&scanout.con) - .unwrap_or_else(|e| error!("Error occurs during console closing:{:?}", e)); - } - while !self.resources_list.is_empty() { self.resource_destroy(0); self.resources_list.remove(0); @@ -1508,17 +1503,24 @@ pub struct Gpu { cfg: GpuDevConfig, /// Status of the GPU device. state: GpuState, + /// Each console corresponds to a display. + consoles: Vec>>>, /// Callback to trigger interrupt. interrupt_cb: Option>, /// Eventfd for device deactivate. deactivate_evts: Vec, } +/// SAFETY: The raw pointer in rust doesn't impl Send, all write operations +/// to this memory will be locked. So implement Send safe. +unsafe impl Send for Gpu {} + impl Gpu { pub fn new(cfg: GpuDevConfig) -> Gpu { Self { cfg, state: GpuState::default(), + consoles: Vec::new(), interrupt_cb: None, deactivate_evts: Vec::new(), } @@ -1548,6 +1550,13 @@ impl VirtioDevice for Gpu { self.state.device_features |= 1 << VIRTIO_GPU_F_EDID; } + for i in 0..self.cfg.max_outputs { + let gpu_opts = Arc::new(GpuOpts::default()); + let dev_name = format!("virtio-gpu{}", i); + let con = console_init(dev_name, ConsoleType::Graphic, gpu_opts); + self.consoles.push(con); + } + self.build_device_config_space(); Ok(()) @@ -1555,6 +1564,10 @@ impl VirtioDevice for Gpu { /// Unrealize low level device. fn unrealize(&mut self) -> Result<()> { + for con in &self.consoles { + console_close(con)?; + } + MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.cfg.id); Ok(()) } @@ -1648,12 +1661,13 @@ impl VirtioDevice for Gpu { self.interrupt_cb = Some(interrupt_cb.clone()); let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; + let mut scanouts = vec![]; - for i in 0..VIRTIO_GPU_MAX_SCANOUTS { - let mut scanout = GpuScanout::default(); - let gpu_opts = Arc::new(GpuOpts::default()); - let dev_name = format!("virtio-gpu{}", i); - scanout.con = console_init(dev_name, ConsoleType::Graphic, gpu_opts); + for con in &self.consoles { + let scanout = GpuScanout { + con: con.clone(), + ..Default::default() + }; scanouts.push(scanout); } -- Gitee From 9ce30415ccad930c994cc06d668a0473bce3dfdc Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 15 Apr 2023 19:18:46 +0800 Subject: [PATCH 1003/2187] usb: fix some mst We only support primary interrupter now, fix the testcases. Signed-off-by: zhouli57 --- tests/mod_test/src/libdriver/usb.rs | 2 +- tests/mod_test/tests/usb_test.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 9a2fe72fd..7cc4214e3 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -733,7 +733,7 @@ impl TestXhciPciDevice { let hcsparams1 = self .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x4) as u64); - assert_eq!(hcsparams1, 0x08001040); + assert_eq!(hcsparams1, 0x08000140); // HCSPARAMS2 let hcsparams2 = self .pci_dev diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index c72b130e4..6496fe442 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -734,9 +734,9 @@ fn test_xhci_keyboard_invalid_value() { let mut trb = TestNormalTRB::generate_normal_td(100, HID_KEYBOARD_LEN as u32); xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); - // Host Controller Error + // NOTE: no HCE, only primary interrupter supported now. let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); - assert!(status & USB_STS_HCE == USB_STS_HCE); + assert!(status & USB_STS_HCE != USB_STS_HCE); test_state.borrow_mut().stop(); } -- Gitee From 52d715d8e0e3d3cacbc33934f62767b21be8a817 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 15 Apr 2023 22:21:27 +0800 Subject: [PATCH 1004/2187] aio: Fix handle_misaligned_rw() Under specific situation, the tail block is not loaded, which will make disk corrupted, fix it. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index b41bbd866..ff569fa5c 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -446,6 +446,7 @@ impl Aio { } OpCode::Pwritev => { // Load the head from file before fill iovec to buffer. + let mut head_loaded = false; if cb.offset as u64 > offset_align { let len = raw_read( cb.file_fd, @@ -456,10 +457,11 @@ impl Aio { if len < 0 || len as u32 != cb.req_align { bail!("Failed to load head for misaligned write."); } + head_loaded = true; } // Is head and tail in the same alignment section? - let tail_loaded = (offset_align + cb.req_align as u64) >= high; - let need_tail = !tail_loaded && (high_align > high); + let same_section = (offset_align + cb.req_align as u64) >= high; + let need_tail = !(same_section && head_loaded) && (high_align > high); let mut offset = offset_align; let mut iovecs = &mut cb.iovec[..]; -- Gitee From c9bbbaf3c9f89443b7bea6d7c45a0b7166c3a7e2 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 15 Apr 2023 17:41:06 +0800 Subject: [PATCH 1005/2187] usb: avoid invalid address when fetch trb Use memory barrier to avoid get invalid address when fetch trb. And check the address at the same time. Signed-off-by: zhouli57 --- devices/src/usb/mod.rs | 18 ++++-- devices/src/usb/xhci/xhci_controller.rs | 20 +++++-- devices/src/usb/xhci/xhci_regs.rs | 16 ++++- devices/src/usb/xhci/xhci_ring.rs | 78 +++++++++++-------------- tests/mod_test/tests/usb_test.rs | 33 +++++++++++ 5 files changed, 108 insertions(+), 57 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 21e312897..93f9f3f30 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -485,27 +485,33 @@ impl UsbPacket { let mut copied = 0; if to_host { for iov in &self.iovecs { + if iov.iov_len == 0 { + continue; + } + if len == copied { + break; + } let cnt = min(iov.iov_len as usize, len - copied); let tmp = &vec[copied..(copied + cnt)]; if let Err(e) = mem_from_buf(tmp, iov.iov_base) { error!("Failed to write mem: {:?}", e); } copied += cnt; - if len == copied { - break; - } } } else { for iov in &self.iovecs { + if iov.iov_len == 0 { + continue; + } + if len == copied { + break; + } let cnt = min(iov.iov_len as usize, len - copied); let tmp = &mut vec[copied..(copied + cnt)]; if let Err(e) = mem_to_buf(tmp, iov.iov_base) { error!("Failed to read mem {:?}", e); } copied += cnt; - if len == copied { - break; - } } } self.actual_length = copied as u32; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index e6d360e27..b8c02404d 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1693,6 +1693,16 @@ impl XhciDevice { } else { trb.parameter }; + if !self + .mem_space + .address_in_memory(GuestAddress(dma_addr), chunk as u64) + { + bail!( + "Invalid Address for transfer: base 0x{:X}, size {}", + dma_addr, + chunk + ); + } if let Some(hva) = self.mem_space.get_host_address(GuestAddress(dma_addr)) { vec.push(Iovec::new(hva, chunk as u64)); } else { @@ -1864,8 +1874,8 @@ pub fn dma_read_bytes( addr_space: &Arc, addr: GuestAddress, mut buf: &mut [u8], - len: u64, ) -> Result<()> { + let len = buf.len() as u64; addr_space.read(&mut buf, addr, len).with_context(|| { format!( "Failed to read dma memory at gpa=0x{:x} len=0x{:x}", @@ -1879,8 +1889,8 @@ pub fn dma_write_bytes( addr_space: &Arc, addr: GuestAddress, mut buf: &[u8], - len: u64, ) -> Result<()> { + let len = buf.len() as u64; addr_space.write(&mut buf, addr, len).with_context(|| { format!( "Failed to write dma memory at gpa=0x{:x} len=0x{:x}", @@ -1892,7 +1902,7 @@ pub fn dma_write_bytes( fn dma_read_u64(addr_space: &Arc, addr: GuestAddress, data: &mut u64) -> Result<()> { let mut tmp = [0_u8; 8]; - dma_read_bytes(addr_space, addr, &mut tmp, 8)?; + dma_read_bytes(addr_space, addr, &mut tmp)?; *data = LittleEndian::read_u64(&tmp); Ok(()) } @@ -1905,7 +1915,7 @@ pub fn dma_read_u32( let vec_len = size_of::() * buf.len(); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); - dma_read_bytes(addr_space, addr, tmp, vec_len as u64)?; + dma_read_bytes(addr_space, addr, tmp)?; for i in 0..buf.len() { buf[i] = LittleEndian::read_u32(&tmp[(size_of::() * i)..]); } @@ -1923,7 +1933,7 @@ pub fn dma_write_u32( for i in 0..buf.len() { LittleEndian::write_u32(&mut tmp[(size_of::() * i)..], buf[i]); } - dma_write_bytes(addr_space, addr, tmp, vec_len as u64)?; + dma_write_bytes(addr_space, addr, tmp)?; Ok(()) } diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 9e63f0550..f56278541 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{fence, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{bail, Result}; @@ -338,11 +338,21 @@ impl XhciInterrupter { self.er_start, (TRB_SIZE * self.er_ep_idx) as u64, ))?; + let cycle = trb.control as u8; + // Toggle the cycle bit to avoid driver read it. + let control = if trb.control & TRB_C == TRB_C { + trb.control & !TRB_C + } else { + trb.control | TRB_C + }; let mut buf = [0_u8; TRB_SIZE as usize]; LittleEndian::write_u64(&mut buf, trb.parameter); LittleEndian::write_u32(&mut buf[8..], trb.status); - LittleEndian::write_u32(&mut buf[12..], trb.control); - dma_write_bytes(&self.mem, GuestAddress(addr), &buf, TRB_SIZE as u64)?; + LittleEndian::write_u32(&mut buf[12..], control); + dma_write_bytes(&self.mem, GuestAddress(addr), &buf)?; + // Write the cycle bit at last. + fence(Ordering::SeqCst); + dma_write_bytes(&self.mem, GuestAddress(addr + 12), &[cycle])?; Ok(()) } } diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index e1740515f..f39b72de1 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::atomic::{fence, AtomicBool, AtomicU64, Ordering}; use std::sync::Arc; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; @@ -47,11 +47,6 @@ impl XhciTRB { pub fn get_type(&self) -> TRBType { ((self.control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK).into() } - - // Get Cycle bit - pub fn get_cycle_bit(&self) -> bool { - self.control & TRB_C == TRB_C - } } /// XHCI Command Ring @@ -85,13 +80,14 @@ impl XhciCommandRing { pub fn fetch_trb(&mut self) -> Result> { let mut link_cnt = 0; loop { - let mut trb = self.read_trb(self.dequeue)?; - trb.addr = self.dequeue; - trb.ccs = self.ccs; - if trb.get_cycle_bit() != self.ccs { + if read_cycle_bit(&self.mem, self.dequeue)? != self.ccs { debug!("TRB cycle bit not matched"); return Ok(None); } + fence(Ordering::Acquire); + let mut trb = read_trb(&self.mem, self.dequeue)?; + trb.addr = self.dequeue; + trb.ccs = self.ccs; let trb_type = trb.get_type(); debug!("Fetch TRB: type {:?} trb {:?}", trb_type, trb); if trb_type == TRBType::TrLink { @@ -111,19 +107,6 @@ impl XhciCommandRing { } } } - - fn read_trb(&self, addr: u64) -> Result { - let mut buf = [0; TRB_SIZE as usize]; - dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf, TRB_SIZE as u64)?; - let trb = XhciTRB { - parameter: LittleEndian::read_u64(&buf), - status: LittleEndian::read_u32(&buf[8..]), - control: LittleEndian::read_u32(&buf[12..]), - addr: 0, - ccs: true, - }; - Ok(trb) - } } /// XHCI Transfer Ring @@ -166,19 +149,6 @@ impl XhciTransferRing { self.ccs.store(v, Ordering::SeqCst); } - fn read_trb(&self, addr: u64) -> Result { - let mut buf = [0; TRB_SIZE as usize]; - dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf, TRB_SIZE as u64)?; - let trb = XhciTRB { - parameter: LittleEndian::read_u64(&buf), - status: LittleEndian::read_u32(&buf[8..]), - control: LittleEndian::read_u32(&buf[12..]), - addr: 0, - ccs: true, - }; - Ok(trb) - } - /// Get the transfer descriptor which includes one or more TRBs. /// Return None if the td is not ready. /// Return Vec if the td is ok. @@ -190,14 +160,14 @@ impl XhciTransferRing { let mut link_cnt = 0; let mut td = Vec::new(); for _ in 0..RING_LEN_LIMIT { - let mut trb = self.read_trb(dequeue)?; - trb.addr = dequeue; - trb.ccs = ccs; - if trb.get_cycle_bit() != ccs { - // TRB is not ready. + if read_cycle_bit(&self.mem, dequeue)? != ccs { debug!("TRB cycle bit not matched"); return Ok(None); } + fence(Ordering::Acquire); + let mut trb = read_trb(&self.mem, dequeue)?; + trb.addr = dequeue; + trb.ccs = ccs; let trb_type = trb.get_type(); if trb_type == TRBType::TrLink { link_cnt += 1; @@ -246,6 +216,28 @@ impl XhciTransferRing { } } +fn read_trb(mem: &Arc, addr: u64) -> Result { + let mut buf = [0; TRB_SIZE as usize]; + dma_read_bytes(mem, GuestAddress(addr), &mut buf)?; + let trb = XhciTRB { + parameter: LittleEndian::read_u64(&buf), + status: LittleEndian::read_u32(&buf[8..]), + control: LittleEndian::read_u32(&buf[12..]), + addr: 0, + ccs: true, + }; + Ok(trb) +} + +fn read_cycle_bit(mem: &Arc, addr: u64) -> Result { + let addr = addr + .checked_add(12) + .with_context(|| format!("Ring address overflow, {:x}", addr))?; + let mut buf = [0]; + dma_read_u32(mem, GuestAddress(addr), &mut buf)?; + Ok(buf[0] & TRB_C == TRB_C) +} + /// Event Ring Segment Table Entry. See in the specs 6.5 Event Ring Segment Table. pub struct XhciEventRingSeg { mem: Arc, @@ -269,7 +261,7 @@ impl XhciEventRingSeg { /// Fetch the event ring segment. pub fn fetch_event_ring_seg(&mut self, addr: u64) -> Result<()> { let mut buf = [0_u8; TRB_SIZE as usize]; - dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf, TRB_SIZE as u64)?; + dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf)?; self.addr_lo = LittleEndian::read_u32(&buf); self.addr_hi = LittleEndian::read_u32(&buf[4..]); self.size = LittleEndian::read_u32(&buf[8..]); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 6496fe442..28cabb25b 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -2553,3 +2553,36 @@ fn test_xhci_keyboard_tablet_basic() { xhci.test_pointer_event(slot_id, test_state.clone()); test_state.borrow_mut().stop(); } + +#[test] +fn test_xhci_tablet_invalid_trb() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("tbt") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 1; + let slot_id = xhci.init_device(port_id); + + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + // Invalid address in TRB. + let mut trb = TestNormalTRB::generate_normal_td(0, 6); + trb.set_pointer(0); + trb.set_idt_flag(false); + xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::TrbError as u32); + // Fetch the remaining data. + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + + xhci.test_pointer_event(slot_id, test_state.clone()); + test_state.borrow_mut().stop(); +} -- Gitee From bf8d42b620095c3ca09049d00eeacdecd7f5330b Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 14 Apr 2023 23:36:25 +0800 Subject: [PATCH 1006/2187] Gtk: add syscall white table Add syscall white table for gtk Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/syscall.rs | 24 +++++++++++++++++++++- machine/src/standard_vm/x86_64/syscall.rs | 18 +++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 7fede1aac..c4470b3c1 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 87 syscalls +/// * aarch64-unknown-gnu: 98 syscalls /// * aarch64-unknown-musl: 60 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -184,6 +184,28 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_futex), BpfRule::new(libc::SYS_fallocate), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_getresuid), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_getresgid), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fstatfs), + #[cfg(target_env = "gnu")] + BpfRule::new(223), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_listen), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fchmodat), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmget), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmctl), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmat), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmdt), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_lremovexattr), ] } diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 9116aef49..4b8d6b7d2 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 88 syscalls +/// * x86_64-unknown-gnu: 96 syscalls /// * x86_64-unknown-musl: 63 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -192,6 +192,22 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_futex), BpfRule::new(libc::SYS_fallocate), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_poll), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_access), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_sched_setattr), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_getresuid), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_getresgid), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fstatfs), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fadvise64), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmget), ] } -- Gitee From b7fae999bdae3ceb813cd142d73af33760858df2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 14 Apr 2023 21:45:22 +0800 Subject: [PATCH 1007/2187] GTK: gtk init GTK initialization, you can get a displayable window by cmdline "-display gtk". Signed-off-by: Xiao Ye --- Cargo.lock | 518 ++++++++++++++++++++- machine/src/lib.rs | 4 + machine/src/standard_vm/aarch64/mod.rs | 38 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 3 +- machine/src/standard_vm/x86_64/mod.rs | 49 +- machine_manager/src/cmdline.rs | 9 + machine_manager/src/config/display.rs | 111 +++++ machine_manager/src/config/mod.rs | 3 + machine_manager/src/config/vnc.rs | 5 +- pci/src/config.rs | 2 +- ui/Cargo.toml | 3 +- ui/src/console.rs | 25 +- ui/src/gtk/mod.rs | 52 +++ ui/src/lib.rs | 1 + 14 files changed, 796 insertions(+), 27 deletions(-) create mode 100644 machine_manager/src/config/display.rs create mode 100644 ui/src/gtk/mod.rs diff --git a/Cargo.lock b/Cargo.lock index edcba47c7..379499bfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,30 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" +[[package]] +name = "atk" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a83b21d2aa75e464db56225e1bda2dd5993311ba1095acaa8fa03d1ae67026ba" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "badcf670157c84bb8b1cf6b5f70b650fed78da2033c9eed84c4e49b11cbe83ea" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -138,6 +162,30 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cairo-rs" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b5725979db0c586d98abad2193cdb612dd40ef95cd26bd99851bf93b3cb482" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b448b876970834fda82ba3aeaccadbd760206b75388fc5c1b02f1e343b697570" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "capng" version = "0.2.2" @@ -154,6 +202,15 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +[[package]] +name = "cfg-expr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b412e83326147c2bb881f8b40edfbf9905b9b8abaebd0e47ca190ba62fda8f0e" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -222,8 +279,8 @@ dependencies = [ "pci", "serde", "serial_test", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "sysbus", "thiserror", "ui", @@ -249,6 +306,12 @@ dependencies = [ "shared_child", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "errno" version = "0.2.8" @@ -280,6 +343,125 @@ dependencies = [ "version_check", ] +[[package]] +name = "field-offset" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d749dcfc00d8de0d7c3a289e04a04293eb5ba3d8a4e64d64911d481fa9933b" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534192cb8f01daeb8fab2c8d4baa8f9aae5b7a39130525779f5c2608e235b10f" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f097c0704201fbc8f69c1762dc58c6947c8bb188b8ed0bc7e65259f1894fe590" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e091b3d3d6696949ac3b3fb3c62090e5bfd7bd6850bef5c3c5ea701de1b1f1e" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -297,6 +479,156 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +[[package]] +name = "gio" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "gio-sys", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" +dependencies = [ + "anyhow", + "heck 0.3.3", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c1d60554a212445e2a858e42a0e48cece1bd57b311a19a9468f70376cf554ae" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb51122dd3317e9327ec1e4faa151d1fa0d95664cd8fb8dcfacf4d4d29ac70c" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c14c8d3da0545785a7c5a120345b3abb534010fb8ae0f2ef3f47c027fba303e" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" +dependencies = [ + "anyhow", + "heck 0.3.3", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -343,6 +675,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.3" @@ -477,8 +818,8 @@ dependencies = [ "pci", "serde", "serde_json", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "sysbus", "thiserror", "ui", @@ -501,8 +842,8 @@ dependencies = [ "regex", "serde", "serde_json", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "thiserror", "util", "vmm-sys-util", @@ -514,6 +855,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "migration" version = "2.2.0" @@ -624,6 +974,31 @@ dependencies = [ "util", ] +[[package]] +name = "pango" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581" +dependencies = [ + "bitflags", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2367099ca5e761546ba1d501955079f097caa186bb53ce0f718dca99ac1942fe" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -673,6 +1048,18 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.26" @@ -685,6 +1072,41 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.44" @@ -780,6 +1202,15 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.20.7" @@ -841,6 +1272,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.145" @@ -904,6 +1341,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.9.0" @@ -952,19 +1398,37 @@ dependencies = [ "util", ] +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" + [[package]] name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "strum_macros" version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -995,6 +1459,24 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "system-deps" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" +dependencies = [ + "anyhow", + "cfg-expr", + "heck 0.3.3", + "itertools", + "pkg-config", + "strum 0.21.0", + "strum_macros 0.21.1", + "thiserror", + "toml", + "version-compare", +] + [[package]] name = "thiserror" version = "1.0.36" @@ -1015,12 +1497,22 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "ui" version = "2.2.0" dependencies = [ "anyhow", "bitintr", + "gtk", "libc", "log", "machine_manager", @@ -1041,6 +1533,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -1072,6 +1570,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + [[package]] name = "version_check" version = "0.9.4" diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 89c2b3adb..579fa67b8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1423,6 +1423,10 @@ pub trait MachineOps { bail!("ramfb device is not supported!"); } + fn display_init(&mut self, _vm_config: &mut VmConfig) -> Result<()> { + bail!("Display is not supported."); + } + fn add_demo_dev(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 926fda5ba..26342dded 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -17,12 +17,16 @@ pub use crate::error::MachineError; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; use log::{error, info}; use machine_manager::config::ShutdownAction; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::UiContext; use machine_manager::event_loop::EventLoop; use std::borrow::Borrow; use std::collections::HashMap; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; +#[cfg(not(target_env = "musl"))] +use ui::{gtk::gtk_display_init, vnc::vnc_init}; use vmm_sys_util::eventfd::EventFd; use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; @@ -66,8 +70,6 @@ use pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use pci_host_root::PciHostRoot; use sysbus::{SysBus, SysBusDevType, SysRes}; use syscall::syscall_whitelist; -#[cfg(not(target_env = "musl"))] -use ui::vnc; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -148,7 +150,7 @@ pub struct StdMachine { /// Vm boot_source config. boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. - power_button: Arc, + pub power_button: Arc, /// All configuration information of virtual machine. vm_config: Arc>, /// Reset request, handle VM `Reset` event. @@ -546,9 +548,6 @@ impl MachineOps for StdMachine { .init_pci_host() .with_context(|| StdErrorKind::InitPCIeHostErr)?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; - #[cfg(not(target_env = "musl"))] - vnc::vnc_init(&vm_config.vnc, &vm_config.object) - .with_context(|| "Failed to init VNC server!")?; let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { @@ -607,6 +606,11 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; + #[cfg(not(target_env = "musl"))] + locked_vm + .display_init(vm_config) + .with_context(|| "Fail to init display")?; + MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { @@ -642,6 +646,28 @@ impl MachineOps for StdMachine { Ok(()) } + /// Create display. + #[cfg(not(target_env = "musl"))] + fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { + // GTK display init. + match vm_config.display { + Some(ref ds_cfg) if ds_cfg.gtk => { + let ui_context = UiContext { + vm_name: vm_config.guest_name.clone(), + power_button: self.power_button.clone(), + }; + gtk_display_init(ds_cfg, ui_context) + .with_context(|| "Failed to init GTK display!")?; + } + _ => {} + }; + + // VNC display init. + vnc_init(&vm_config.vnc, &vm_config.object) + .with_context(|| "Failed to init VNC server!")?; + Ok(()) + } + #[cfg(not(target_env = "musl"))] fn add_ramfb(&mut self) -> Result<()> { let fwcfg_dev = self diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 341f4fdef..30ef53a03 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -60,6 +60,7 @@ impl LPCBridge { parent_bus: Weak>, sys_io: Arc, reset_req: Arc, + shutdown_req: Arc, ) -> Result { Ok(Self { config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), @@ -70,7 +71,7 @@ impl LPCBridge { pm_ctrl: Arc::new(Mutex::new(AcpiPmCtrl::new())), rst_ctrl: Arc::new(AtomicU8::new(0)), reset_req, - shutdown_req: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + shutdown_req, }) } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 0b1139c31..d31cd8a40 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -37,6 +37,8 @@ use devices::legacy::{ }; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::UiContext; use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, @@ -63,7 +65,7 @@ use super::{AcpiBuilder, StdMachineOps}; use crate::{vm_state, MachineOps}; use anyhow::{bail, Context, Result}; #[cfg(not(target_env = "musl"))] -use ui::vnc; +use ui::{gtk::gtk_display_init, vnc::vnc_init}; const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -129,6 +131,8 @@ pub struct StdMachine { boot_source: Arc>, /// Reset request, handle VM `Reset` event. reset_req: Arc, + /// Shutdown_req, handle VM 'ShutDown' event. + shutdown_req: Arc, /// All configuration information of virtual machine. vm_config: Arc>, /// List of guest NUMA nodes information. @@ -190,6 +194,11 @@ impl StdMachine { EventFd::new(libc::EFD_NONBLOCK) .with_context(|| MachineError::InitEventFdErr("reset request".to_string()))?, ), + shutdown_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + MachineError::InitEventFdErr("shutdown request".to_string()) + })?, + ), vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -270,7 +279,12 @@ impl StdMachine { fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); - let ich = ich9_lpc::LPCBridge::new(root_bus, self.sys_io.clone(), self.reset_req.clone())?; + let ich = ich9_lpc::LPCBridge::new( + root_bus, + self.sys_io.clone(), + self.reset_req.clone(), + self.shutdown_req.clone(), + )?; self.register_reset_event(self.reset_req.clone(), vm) .with_context(|| "Fail to register reset event in LPC")?; self.register_acpi_shutdown_event(ich.shutdown_req.clone(), clone_vm) @@ -459,9 +473,7 @@ impl MachineOps for StdMachine { .init_ich9_lpc(clone_vm) .with_context(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; - #[cfg(not(target_env = "musl"))] - vnc::vnc_init(&vm_config.vnc, &vm_config.object) - .with_context(|| "Failed to init VNC server!")?; + let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; let migrate = locked_vm.get_migrate_info(); @@ -492,6 +504,11 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; + #[cfg(not(target_env = "musl"))] + locked_vm + .display_init(vm_config) + .with_context(|| "Fail to init display")?; + MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); MigrationManager::register_kvm_instance( @@ -565,6 +582,28 @@ impl MachineOps for StdMachine { Ok(()) } + /// Create display. + #[cfg(not(target_env = "musl"))] + fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { + // GTK display init. + match vm_config.display { + Some(ref ds_cfg) if ds_cfg.gtk => { + let ui_context = UiContext { + vm_name: vm_config.guest_name.clone(), + power_button: self.shutdown_req.clone(), + }; + gtk_display_init(ds_cfg, ui_context) + .with_context(|| "Failed to init GTK display!")?; + } + _ => {} + }; + + // VNC display init. + vnc_init(&vm_config.vnc, &vm_config.object) + .with_context(|| "Failed to init VNC server!")?; + Ok(()) + } + fn run(&self, paused: bool) -> Result<()> { self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 99d1521af..bedb2ae94 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -442,6 +442,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("specify the ip and port for vnc") .takes_value(true), ) + .arg( + Arg::with_name("display") + .multiple(false) + .long("display") + .value_name("gtk") + .help("set display for virtual machine: currently only supports gtk") + .takes_value(true), + ) } /// Create `VmConfig` from `ArgMatches`'s arg. @@ -476,6 +484,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); + add_args_to_config!((args.value_of("display")), vm_cfg, add_display); add_args_to_config!( (args.is_present("no-shutdown")), vm_cfg, diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs new file mode 100644 index 000000000..991ed5886 --- /dev/null +++ b/machine_manager/src/config/display.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::Arc; + +use anyhow::{bail, Result}; +use serde::{Deserialize, Serialize}; +use vmm_sys_util::eventfd::EventFd; + +use crate::config::{CmdParser, ExBool, VmConfig}; + +/// Event fd related to power button in gtk. +pub struct UiContext { + pub vm_name: String, + pub power_button: Arc, +} + +/// GTK related configuration. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct DisplayConfig { + /// Create the GTK thread. + pub gtk: bool, + /// Fix window size. + pub fix_size: bool, + /// Keep the window fill the desktop. + pub full_screen: bool, +} + +impl VmConfig { + pub fn add_display(&mut self, vm_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("display"); + cmd_parser.push("").push("full-screen").push("fix-size"); + cmd_parser.parse(vm_config)?; + let mut display_config = DisplayConfig::default(); + if let Some(str) = cmd_parser.get_value::("")? { + match str.as_str() { + "gtk" => display_config.gtk = true, + _ => bail!("Unsupport device: {}", str), + } + } + if let Some(default) = cmd_parser.get_value::("fix-size")? { + display_config.fix_size = default.into(); + } + if let Some(default) = cmd_parser.get_value::("full-screen")? { + display_config.full_screen = default.into(); + } + + self.display = Some(display_config); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_gtk() { + let mut vm_config = VmConfig::default(); + let config_line = ""; + assert!(vm_config.add_display(config_line).is_err()); + + let config_line = "gtk"; + assert!(vm_config.add_display(config_line).is_ok()); + let display_config = vm_config.display.unwrap(); + assert_eq!(display_config.gtk, true); + assert_eq!(display_config.full_screen, false); + assert_eq!(display_config.fix_size, false); + + let mut vm_config = VmConfig::default(); + let config_line = "gtk,full-screen=on"; + assert!(vm_config.add_display(config_line).is_ok()); + let display_config = vm_config.display.unwrap(); + assert_eq!(display_config.gtk, true); + assert_eq!(display_config.full_screen, true); + assert_eq!(display_config.fix_size, false); + + let mut vm_config = VmConfig::default(); + let config_line = "gtk,full-screen=off"; + assert!(vm_config.add_display(config_line).is_ok()); + let display_config = vm_config.display.unwrap(); + assert_eq!(display_config.gtk, true); + assert_eq!(display_config.full_screen, false); + assert_eq!(display_config.fix_size, false); + + let mut vm_config = VmConfig::default(); + let config_line = "gtk,fix-size=on"; + assert!(vm_config.add_display(config_line).is_ok()); + let display_config = vm_config.display.unwrap(); + assert_eq!(display_config.gtk, true); + assert_eq!(display_config.full_screen, false); + assert_eq!(display_config.fix_size, true); + + let mut vm_config = VmConfig::default(); + let config_line = "gtk,fix-size=off"; + assert!(vm_config.add_display(config_line).is_ok()); + let display_config = vm_config.display.unwrap(); + assert_eq!(display_config.gtk, true); + assert_eq!(display_config.full_screen, false); + assert_eq!(display_config.fix_size, false); + } +} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 7b500e769..e2c53884b 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -15,6 +15,7 @@ pub use boot_source::*; pub use chardev::*; pub use demo_dev::*; pub use devices::*; +pub use display::*; pub use drive::*; pub use error::ConfigError; pub use fs::*; @@ -38,6 +39,7 @@ mod boot_source; mod chardev; mod demo_dev; mod devices; +pub mod display; mod drive; pub mod error; mod fs; @@ -116,6 +118,7 @@ pub struct VmConfig { pub numa_nodes: Vec<(String, String)>, pub incoming: Option, pub vnc: Option, + pub display: Option, } impl VmConfig { diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index c30ffaf1a..63c57964f 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -10,11 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use serde::{Deserialize, Serialize}; +use crate::config::{CmdParser, ConfigError, VmConfig}; -use crate::config::ConfigError; -use crate::config::{CmdParser, VmConfig}; use anyhow::{anyhow, Context, Result}; +use serde::{Deserialize, Serialize}; use std::net::Ipv4Addr; /// Configuration of vnc. diff --git a/pci/src/config.rs b/pci/src/config.rs index e269881d7..28cbd018b 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -1155,7 +1155,7 @@ impl PciConfig { fn validate_bar_size(&self, bar_type: RegionType, size: u64) -> Result<()> { if !size.is_power_of_two() - || (bar_type == RegionType::Io && size < MINMUM_BAR_SIZE_FOR_PIO.try_into().unwrap()) + || (bar_type == RegionType::Io && size < MINMUM_BAR_SIZE_FOR_PIO as u64) || (bar_type == RegionType::Mem32Bit && size > u32::MAX as u64) || (bar_type == RegionType::Io && size > u16::MAX as u64) { diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 348201f34..0d10c3b7e 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -19,5 +19,6 @@ rustls = "0.20.6" rustls-pemfile = "1.0.0" sasl2-sys = "0.1.20" bitintr = "0.2.0" +gtk = "0.14.3" machine_manager = { path = "../machine_manager" } -util = { path = "../util" } \ No newline at end of file +util = { path = "../util" } diff --git a/ui/src/console.rs b/ui/src/console.rs index 7e934668f..836e16122 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -11,8 +11,9 @@ // See the Mulan PSL v2 for more details. use crate::pixman::{ - create_pixman_image, get_image_height, get_image_width, pixman_glyph_from_vgafont, - pixman_glyph_render, unref_pixman_image, ColorNames, COLOR_TABLE_RGB, + create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, + pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, ColorNames, + COLOR_TABLE_RGB, }; use anyhow::Result; use log::error; @@ -74,6 +75,24 @@ impl Default for DisplaySurface { } } +impl DisplaySurface { + pub fn width(&self) -> i32 { + get_image_width(self.image) + } + + pub fn height(&self) -> i32 { + get_image_height(self.image) + } + + pub fn stride(&self) -> i32 { + get_image_stride(self.image) + } + + pub fn data(&self) -> *mut u32 { + get_image_data(self.image) + } +} + /// Cursor data defined in Display. /// hot_x and hot_y indicate the hotspot of the cursor. /// width and height indicate the width of the cursor in pixel. @@ -652,7 +671,7 @@ pub fn console_select(con_id: Option) -> Result<()> { /// * `width` - width of image. /// * `height` - height of image. /// * `msg` - test messages showed in display. -fn create_msg_surface(width: i32, height: i32, msg: String) -> Option { +pub fn create_msg_surface(width: i32, height: i32, msg: String) -> Option { if !(0..MAX_WINDOW_WIDTH as i32).contains(&width) || !(0..MAX_WINDOW_HEIGHT as i32).contains(&height) { diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs new file mode 100644 index 000000000..9c786cd15 --- /dev/null +++ b/ui/src/gtk/mod.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::console::{DEFAULT_SURFACE_HEIGHT, DEFAULT_SURFACE_WIDTH}; +use anyhow::{Context, Result}; +use gtk::{ + prelude::{ApplicationExt, ApplicationExtManual}, + traits::WidgetExt, + Application, ApplicationWindow, +}; +use machine_manager::config::{DisplayConfig, UiContext}; +use std::thread; + +/// Gtk display init. +pub fn gtk_display_init(_ds_cfg: &DisplayConfig, _ui_context: UiContext) -> Result<()> { + let args: Vec = vec![]; + let _handle = thread::Builder::new() + .name("gtk display".to_string()) + .spawn(move || create_gtk_thread(args)) + .with_context(|| "Fail to create gtk display thread!"); + Ok(()) +} + +/// Create a gtk thread. +pub fn create_gtk_thread(gtk_args: Vec) { + let application = Application::builder() + .application_id("stratovirt.gtk") + .build(); + application.connect_activate(build_ui); + application.run_with_args(>k_args); +} + +// Create window. +fn build_ui(app: &Application) { + let window = ApplicationWindow::builder() + .application(app) + .title("Stratovirt") + .default_width(DEFAULT_SURFACE_WIDTH) + .default_height(DEFAULT_SURFACE_HEIGHT) + .build(); + + window.show_all(); +} diff --git a/ui/src/lib.rs b/ui/src/lib.rs index e2dd7ff87..61c8be58f 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -13,6 +13,7 @@ pub mod console; mod data; pub mod error; +pub mod gtk; pub mod input; pub mod pixman; pub mod utils; -- Gitee From 5ba126cdc83b15f7ae66b77f49fce7b69b898933 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 14 Apr 2023 22:55:01 +0800 Subject: [PATCH 1008/2187] Gtk: create a gtk thread Create a gtk display thread that can receive images from the graphic device and display on screen. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 255 ++++++++++++++ ui/src/gtk/menu.rs | 348 +++++++++++++++++++ ui/src/gtk/mod.rs | 810 ++++++++++++++++++++++++++++++++++++++++++++- ui/src/input.rs | 11 + 4 files changed, 1408 insertions(+), 16 deletions(-) create mode 100644 ui/src/gtk/draw.rs create mode 100644 ui/src/gtk/menu.rs diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs new file mode 100644 index 000000000..c3c327faa --- /dev/null +++ b/ui/src/gtk/draw.rs @@ -0,0 +1,255 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, rc::Rc}; + +use anyhow::Result; +use gtk::{ + cairo, + gdk::{self, EventMask, ScrollDirection}, + glib::{self, translate::IntoGlib}, + prelude::WidgetExtManual, + traits::WidgetExt, + DrawingArea, Inhibit, +}; +use log::error; + +use crate::{ + gtk::GtkDisplayScreen, + input::{ + self, point_event, press_mouse, ABS_MAX, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, + INPUT_POINT_RIGHT, + }, +}; + +pub(crate) fn set_callback_for_draw_area( + draw_area: &DrawingArea, + gs: Rc>, +) -> Result<()> { + draw_area.connect_draw( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, cr| { + da_draw_callback(&gs, cr).unwrap_or_else(|e| error!("Draw: {}", e)); + Inhibit(false) + }), + ); + draw_area.connect_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, event| { + da_event_callback(&gs, event).unwrap_or_else(|e| error!("Draw event: {}", e)); + Inhibit(false)}), + ); + draw_area.connect_button_press_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, button_event| { + da_pointer_callback(&gs, button_event).unwrap_or_else(|e| error!("Press event: {}", e)); + Inhibit(false) + }), + ); + draw_area.connect_button_release_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, button_event| { + da_pointer_callback(&gs, button_event).unwrap_or_else(|e| error!("Release event: {}", e)); + Inhibit(false) + }), + ); + draw_area.connect_scroll_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, scroll_event| { + da_scroll_callback(&gs, scroll_event).unwrap_or_else(|e| error!("Scroll event: {}", e)); + Inhibit(false) + }), + ); + draw_area.connect_key_press_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, key_event| { + da_key_callback(&gs,key_event, true).unwrap_or_else(|e|error!("Press event: {}", e)); + Inhibit(false)} + ), + ); + draw_area.connect_key_release_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, key_event| { + da_key_callback(&gs,key_event, false).unwrap_or_else(|e|error!("Key event: {}", e)); + Inhibit(false)} + ), + ); + + let event_mask = EventMask::BUTTON_PRESS_MASK + | EventMask::BUTTON_RELEASE_MASK + | EventMask::BUTTON_MOTION_MASK + | EventMask::SCROLL_MASK + | EventMask::KEY_PRESS_MASK + | EventMask::KEY_RELEASE_MASK + | EventMask::BUTTON1_MOTION_MASK + | EventMask::POINTER_MOTION_MASK; + draw_area.add_events(event_mask); + + Ok(()) +} + +fn da_key_callback( + gs: &Rc>, + key_event: &gdk::EventKey, + press: bool, +) -> Result<()> { + let keysym2keycode = gs.borrow().keysym2keycode.clone(); + let key_value: u16 = key_event.keyval().to_lower().into_glib() as u16; + let keycode: u16 = match keysym2keycode.borrow().get(&(key_value as u16)) { + Some(k) => *k, + None => 0, + }; + input::key_event(keycode, press)?; + Ok(()) +} + +fn da_event_callback(gs: &Rc>, event: &gdk::Event) -> Result<()> { + // Cursor movement. + if event.event_type() == gdk::EventType::MotionNotify { + gd_cursor_move_event(gs, event).unwrap_or_else(|e| error!("Cursor movement: {:?}", e)); + } + Ok(()) +} + +/// Cursor Movement. +fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) -> Result<()> { + let mut borrowed_gs = gs.borrow_mut(); + let (width, height) = match &borrowed_gs.cairo_image { + Some(image) => (image.width() as f64, image.height() as f64), + None => return Ok(()), + }; + + let (x, y) = match event.coords() { + Some(value) => value, + None => return Ok(()), + }; + let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; + let standard_x = (((real_x as u64 * ABS_MAX) / width as u64) as u16) as u16; + let standard_y = (((real_y as u64 * ABS_MAX) / height as u64) as u16) as u16; + + point_event( + borrowed_gs.click_state.button_mask as u32, + standard_x as u32, + standard_y as u32, + ) +} + +fn da_pointer_callback( + gs: &Rc>, + button_event: &gdk::EventButton, +) -> Result<()> { + let mut borrowed_gs = gs.borrow_mut(); + borrowed_gs.click_state.button_mask = match button_event.button() { + 1 => INPUT_POINT_LEFT, + 2 => INPUT_POINT_RIGHT, + 3 => INPUT_POINT_MIDDLE, + _ => return Ok(()), + }; + + let (width, height) = match &borrowed_gs.cairo_image { + Some(image) => (image.width() as f64, image.height() as f64), + None => return Ok(()), + }; + + let (x, y) = button_event.position(); + let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; + + let standard_x = (((real_x as u64 * ABS_MAX) / width as u64) as u16) as u16; + let standard_y = (((real_y as u64 * ABS_MAX) / height as u64) as u16) as u16; + + match button_event.event_type() { + gdk::EventType::ButtonRelease => { + borrowed_gs.click_state.button_mask = 0; + point_event( + borrowed_gs.click_state.button_mask as u32, + standard_x as u32, + standard_y as u32, + ) + } + gdk::EventType::ButtonPress => point_event( + borrowed_gs.click_state.button_mask as u32, + standard_x as u32, + standard_y as u32, + ), + gdk::EventType::DoubleButtonPress => press_mouse( + borrowed_gs.click_state.button_mask as u32, + standard_x as u32, + standard_y as u32, + ), + _ => Ok(()), + } +} + +fn da_scroll_callback( + gs: &Rc>, + scroll_event: &gdk::EventScroll, +) -> Result<()> { + let borrowed_gs = gs.borrow(); + let (width, height) = match &borrowed_gs.cairo_image { + Some(image) => (image.width() as f64, image.height() as f64), + None => return Ok(()), + }; + let button_mask: u8 = match scroll_event.direction() { + ScrollDirection::Up => 0x8, + ScrollDirection::Down => 0x10, + _ => 0x0, + }; + + let standard_x = + (((borrowed_gs.click_state.last_x as u64 * ABS_MAX) / width as u64) as u16) as u16; + let standard_y = + (((borrowed_gs.click_state.last_y as u64 * ABS_MAX) / height as u64) as u16) as u16; + drop(borrowed_gs); + point_event(button_mask as u32, standard_x as u32, standard_y as u32)?; + Ok(()) +} + +/// Draw_area callback func for draw signal. +fn da_draw_callback(gs: &Rc>, cr: &cairo::Context) -> Result<()> { + let mut borrowed_gs = gs.borrow_mut(); + let scale_mode = borrowed_gs.scale_mode.clone(); + let (mut surface_width, mut surface_height) = match &borrowed_gs.cairo_image { + Some(image) => (image.width() as f64, image.height() as f64), + None => return Ok(()), + }; + + if surface_width.le(&0.0) || surface_height.le(&0.0) { + return Ok(()); + } + let (window_width, window_height) = borrowed_gs.get_window_size()?; + + if scale_mode.borrow().is_full_screen() { + borrowed_gs.scale_x = window_width / surface_width; + borrowed_gs.scale_y = window_height / surface_height; + } else if scale_mode.borrow().is_free_scale() { + let scale_x = window_width / surface_width; + let scale_y = window_height / surface_height; + borrowed_gs.scale_x = scale_x.min(scale_y); + borrowed_gs.scale_y = scale_x.min(scale_y); + } + + surface_width = (surface_width * borrowed_gs.scale_x).floor(); + surface_height = (surface_height * borrowed_gs.scale_y).floor(); + + let mut mx: f64 = 0.0; + let mut my: f64 = 0.0; + if window_width > surface_width { + mx = (window_width - surface_width) / (2.0); + } + if window_height > surface_height { + my = (window_height - surface_height) / (2.0); + } + + cr.rectangle(0.0, 0.0, window_width, window_height); + cr.rectangle(mx + surface_width, my, surface_width * -1.0, surface_height); + cr.fill()?; + cr.scale(borrowed_gs.scale_x, borrowed_gs.scale_y); + if let Some(image) = &borrowed_gs.cairo_image { + cr.set_source_surface(image, mx / borrowed_gs.scale_x, my / borrowed_gs.scale_y)?; + } + cr.paint()?; + + Ok(()) +} diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs new file mode 100644 index 000000000..66cfc1f21 --- /dev/null +++ b/ui/src/gtk/menu.rs @@ -0,0 +1,348 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, rc::Rc}; + +use anyhow::{bail, Result}; +use gtk::{ + gdk::{ + self, + ffi::{GDK_KEY_equal, GDK_KEY_minus, GDK_KEY_B, GDK_KEY_F, GDK_KEY_M, GDK_KEY_S}, + ModifierType, + }, + glib, + prelude::{AccelGroupExtManual, NotebookExtManual}, + traits::{ + BoxExt, CheckMenuItemExt, ContainerExt, GtkMenuExt, GtkMenuItemExt, GtkWindowExt, + MenuShellExt, NotebookExt, WidgetExt, + }, + AccelFlags, AccelGroup, ApplicationWindow, CheckMenuItem, Inhibit, Menu, MenuBar, MenuItem, + Orientation, RadioMenuItem, +}; +use log::error; + +use crate::gtk::{ + renew_image, update_window_size, GtkDisplay, ZoomOperate, GTK_SCALE_MIN, GTK_ZOOM_STEP, +}; + +#[derive(Clone)] +pub(crate) struct GtkMenu { + pub(crate) window: ApplicationWindow, + container: gtk::Box, + pub(crate) note_book: gtk::Notebook, + pub(crate) radio_group: Vec, + accel_group: AccelGroup, + menu_bar: MenuBar, + machine_menu: Menu, + machine_item: MenuItem, + shutdown_item: MenuItem, + pub(crate) view_menu: Menu, + view_item: MenuItem, + full_screen_item: MenuItem, + zoom_in_item: MenuItem, + zoom_out_item: MenuItem, + zoom_fit: CheckMenuItem, + best_fit_item: MenuItem, + show_menu_bar: CheckMenuItem, +} + +impl GtkMenu { + pub(crate) fn new(window: ApplicationWindow) -> Self { + Self { + window, + container: gtk::Box::new(Orientation::Vertical, 0), + note_book: gtk::Notebook::default(), + radio_group: vec![], + accel_group: AccelGroup::default(), + menu_bar: MenuBar::new(), + machine_menu: Menu::new(), + machine_item: MenuItem::with_label("Machine"), + shutdown_item: MenuItem::with_label("Shut Down"), + view_menu: Menu::new(), + view_item: MenuItem::with_label("View"), + full_screen_item: MenuItem::with_label("Full Screen"), + zoom_in_item: MenuItem::with_label("Zoom In"), + zoom_out_item: MenuItem::with_label("Zoom Out"), + zoom_fit: CheckMenuItem::with_label("Zoom Fit"), + best_fit_item: MenuItem::with_label("Best Fit"), + show_menu_bar: CheckMenuItem::with_label("Show MenuBar"), + } + } + + /// 1. Setting callback function for button. + /// 2. Set shortcut keys for buttons. + /// Button shortcut key + /// shutdown_item: Ctrl + Alt + S. + /// full_screen_item Ctrl + Alt + F + /// zoom_in_item Ctrl + Alt + + + /// zoom_out_item Ctrl + Alt + - + /// best_fit_item Ctrl + Alt + B + /// show_menu_bar Ctrl + Alt + M + pub(crate) fn set_signal(&mut self, gd: &Rc>) { + let modifier = ModifierType::CONTROL_MASK | ModifierType::MOD1_MASK; + let accel_flags = AccelFlags::VISIBLE; + + self.shutdown_item + .connect_activate(glib::clone!(@weak gd => move |_| { + power_down_callback(&gd).unwrap_or_else(|e| error!("Gtk shutdown failed: {:?}", e)); + })); + self.shutdown_item.add_accelerator( + "activate", + &self.accel_group, + GDK_KEY_S as u32, + modifier, + accel_flags, + ); + + self.full_screen_item + .connect_activate(glib::clone!(@weak gd => move |_| { + full_screen_callback(&gd).unwrap_or_else(|e| error!("Full Screen Item: {:?}", e)); + })); + self.full_screen_item.add_accelerator( + "activate", + &self.accel_group, + GDK_KEY_F as u32, + modifier, + accel_flags, + ); + let full_screen_item = self.full_screen_item.clone(); + self.accel_group.connect_accel_group( + GDK_KEY_F as u32, + modifier, + accel_flags, + glib::clone!(@weak full_screen_item => @default-return false, move |_, _, _, _| { + full_screen_item.activate(); + false + }), + ); + + self.zoom_in_item + .connect_activate(glib::clone!(@weak gd => move |_| { + menu_zoom_callback(&gd, ZoomOperate::ZoomIn).unwrap_or_else(|e| error!("Zoom In Item: {:?}", e)); + })); + self.zoom_in_item.add_accelerator( + "activate", + &self.accel_group, + GDK_KEY_equal as u32, + modifier, + accel_flags, + ); + + self.zoom_out_item + .connect_activate(glib::clone!(@weak gd => move |_| { + menu_zoom_callback(&gd, ZoomOperate::ZoomOut).unwrap_or_else(|e| error!("Zoom Out Item: {:?}", e)); + })); + self.zoom_out_item.add_accelerator( + "activate", + &self.accel_group, + GDK_KEY_minus as u32, + modifier, + accel_flags, + ); + + self.best_fit_item + .connect_activate(glib::clone!(@weak gd => move |_| { + menu_zoom_callback(&gd, ZoomOperate::BestFit).unwrap_or_else(|e| error!("Best Fit Item: {:?}", e)); + })); + self.best_fit_item.add_accelerator( + "activate", + &self.accel_group, + GDK_KEY_B as u32, + modifier, + accel_flags, + ); + + // Set the hiding of menu_bar. + self.show_menu_bar + .connect_activate(glib::clone!(@weak gd => move |_| { + show_menubar_callback(&gd).unwrap_or_else(|e| error!("Shoe Menu Bar: {:?}", e)); + })); + let show_menu_bar = self.show_menu_bar.clone(); + self.show_menu_bar.add_accelerator( + "activate", + &self.accel_group, + GDK_KEY_M as u32, + modifier, + accel_flags, + ); + self.accel_group.connect_accel_group( + GDK_KEY_M as u32, + modifier, + accel_flags, + move |_, _, _, _| { + if !show_menu_bar.is_active() { + show_menu_bar.activate(); + } + true + }, + ); + + // Connect delete for window. + self.window.connect_delete_event( + glib::clone!(@weak gd => @default-return Inhibit(false), move |_, _| { + power_down_callback(&gd).unwrap_or_else(|e| error!("Standard vm write power button failed: {:?}", e)); + Inhibit(false) + }), + ); + + // By confirmation this button, the size of window is fixed and + // can not be changed. + self.zoom_fit + .connect_activate(glib::clone!(@weak gd => move |_| { + zoom_fit_callback(&gd).unwrap_or_else(|e| error!("Zoom fit: {:?}", e)); + })); + } + + pub(crate) fn set_menu(&mut self) { + // Machine menu. + self.machine_menu.set_accel_group(Some(&self.accel_group)); + self.machine_menu.append(&self.shutdown_item); + self.machine_item.set_submenu(Some(&self.machine_menu)); + + // View menu. + self.view_menu.set_accel_group(Some(&self.accel_group)); + self.view_menu.append(&self.full_screen_item); + self.view_menu.append(&self.zoom_in_item); + self.view_menu.append(&self.zoom_out_item); + self.view_menu.append(&self.zoom_fit); + self.view_menu.append(&self.best_fit_item); + self.view_menu.append(&self.show_menu_bar); + self.view_item.set_submenu(Some(&self.view_menu)); + + self.menu_bar.append(&self.machine_item); + self.menu_bar.append(&self.view_item); + + // Set the visible of note_book. + self.note_book.set_show_tabs(false); + self.note_book.set_show_border(true); + + self.window.add_accel_group(&self.accel_group); + self.container.pack_start(&self.menu_bar, false, false, 0); + self.container.pack_start(&self.note_book, true, true, 0); + self.window.add(&self.container); + } + + /// Show window. + pub(crate) fn show_window(&self, is_full_screen: bool) { + if is_full_screen { + self.menu_bar.hide(); + self.window.fullscreen(); + } + self.window.show_all(); + } +} + +/// Fixed the window size. +fn power_down_callback(gd: &Rc>) -> Result<()> { + let power_button = gd.borrow().power_button.clone(); + power_button.write(1)?; + Ok(()) +} + +/// Hid/show title bar. +fn show_menubar_callback(gd: &Rc>) -> Result<()> { + let borrowed_gd = gd.borrow(); + let gtk_menu = borrowed_gd.gtk_menu.clone(); + if borrowed_gd.scale_mode.borrow().is_full_screen() { + return Ok(()); + } + if gtk_menu.show_menu_bar.is_active() { + gtk_menu.menu_bar.show(); + } else { + gtk_menu.menu_bar.hide(); + } + drop(gtk_menu); + + let active_gs = borrowed_gd.get_current_display()?; + drop(borrowed_gd); + update_window_size(&active_gs) +} + +/// Make the window to fill the entir desktop. +fn full_screen_callback(gd: &Rc>) -> Result<()> { + let borrowed_gd = gd.borrow(); + let gtk_menu = borrowed_gd.gtk_menu.clone(); + let gs = borrowed_gd.get_current_display()?; + let scale_mode = borrowed_gd.scale_mode.clone(); + let mut borrowed_scale = scale_mode.borrow_mut(); + drop(borrowed_gd); + if !borrowed_scale.is_full_screen() { + gtk_menu.note_book.set_show_tabs(false); + gtk_menu.menu_bar.hide(); + gs.borrow().draw_area.set_size_request(-1, -1); + if let Some(screen) = gdk::Screen::default() { + let width = screen.width(); + let height = screen.height(); + gs.borrow().window.set_default_size(width, height); + } + gtk_menu.window.fullscreen(); + borrowed_scale.full_screen = true; + } else { + gtk_menu.window.unfullscreen(); + if gtk_menu.show_menu_bar.is_active() { + gtk_menu.menu_bar.show(); + } + borrowed_scale.full_screen = false; + gs.borrow_mut().scale_x = 1.0; + gs.borrow_mut().scale_y = 1.0; + drop(borrowed_scale); + update_window_size(&gs)?; + }; + + Ok(()) +} + +/// Zoom in/out the display. +fn menu_zoom_callback(gd: &Rc>, zoom_opt: ZoomOperate) -> Result<()> { + let borrowed_gd = gd.borrow(); + let page_num = borrowed_gd.gtk_menu.note_book.current_page(); + let gs = match borrowed_gd.get_ds_by_pagenum(page_num) { + Some(ds) => ds, + None => bail!("Display Can not found."), + }; + drop(borrowed_gd); + let mut borrowed_gs = gs.borrow_mut(); + match zoom_opt { + ZoomOperate::ZoomIn => { + borrowed_gs.scale_x += GTK_ZOOM_STEP; + borrowed_gs.scale_y += GTK_ZOOM_STEP; + } + ZoomOperate::ZoomOut => { + borrowed_gs.scale_x -= GTK_ZOOM_STEP; + borrowed_gs.scale_y -= GTK_ZOOM_STEP; + borrowed_gs.scale_x = borrowed_gs.scale_x.max(GTK_SCALE_MIN); + borrowed_gs.scale_y = borrowed_gs.scale_y.max(GTK_SCALE_MIN); + } + ZoomOperate::BestFit => { + borrowed_gs.scale_x = 1.0; + borrowed_gs.scale_y = 1.0; + } + } + drop(borrowed_gs); + update_window_size(&gs) +} + +/// Fixed the window size. +fn zoom_fit_callback(gd: &Rc>) -> Result<()> { + let gtk_menu = gd.borrow().gtk_menu.clone(); + let gs = gd.borrow().get_current_display()?; + if gtk_menu.zoom_fit.is_active() { + gd.borrow_mut().scale_mode.borrow_mut().free_scale = true; + } else { + gd.borrow_mut().scale_mode.borrow_mut().free_scale = false; + gs.borrow_mut().scale_x = 1.0; + gs.borrow_mut().scale_y = 1.0; + } + + update_window_size(&gs)?; + renew_image(&gs) +} diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 9c786cd15..88704272c 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -10,43 +10,821 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::console::{DEFAULT_SURFACE_HEIGHT, DEFAULT_SURFACE_WIDTH}; -use anyhow::{Context, Result}; +mod draw; +mod menu; + +use std::{ + cell::RefCell, + cmp, + collections::HashMap, + ptr, + rc::Rc, + sync::{Arc, Mutex, Weak}, + thread, +}; + +use anyhow::{bail, Context, Result}; use gtk::{ - prelude::{ApplicationExt, ApplicationExtManual}, - traits::WidgetExt, - Application, ApplicationWindow, + cairo::{Format, ImageSurface}, + gdk::{self, Geometry, Gravity, Screen, WindowHints}, + gdk_pixbuf::Colorspace, + glib::{self, Priority, SyncSender}, + prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, + traits::{GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt}, + Application, ApplicationWindow, DrawingArea, RadioMenuItem, +}; +use log::error; + +use crate::{ + console::{ + create_msg_surface, get_active_console, graphic_hardware_update, register_display, + DisplayChangeListener, DisplayChangeListenerOperations, DisplayConsole, DisplayMouse, + DisplaySurface, DEFAULT_SURFACE_HEIGHT, DEFAULT_SURFACE_WIDTH, + }, + data::keycode::KEYSYM2KEYCODE, + gtk::{draw::set_callback_for_draw_area, menu::GtkMenu}, + pixman::{ + create_pixman_image, get_image_data, get_image_height, get_image_width, unref_pixman_image, + }, }; use machine_manager::config::{DisplayConfig, UiContext}; -use std::thread; +use util::pixman::{pixman_format_code_t, pixman_image_composite, pixman_image_t, pixman_op_t}; +use vmm_sys_util::eventfd::EventFd; + +const CHANNEL_BOUND: usize = 1024; +pub(crate) const GTK_SCALE_MIN: f64 = 0.25; +pub(crate) const GTK_ZOOM_STEP: f64 = 0.25; + +/// Gtk window display mode. +#[derive(Clone, PartialEq)] +pub struct ScaleMode { + /// Display fill desktop. + full_screen: bool, + /// Scaling operation does not change the aspect ratio. + free_scale: bool, +} + +impl ScaleMode { + fn is_full_screen(&self) -> bool { + self.full_screen + } + + fn is_free_scale(&self) -> bool { + self.free_scale + } +} + +/// Display zoom operation. +/// Zoom in the display. +/// Zoom out the display. +/// Window adapt to display. +#[derive(PartialEq)] +pub enum ZoomOperate { + ZoomIn, + ZoomOut, + BestFit, +} + +#[derive(Default)] +pub struct ClickState { + pub button_mask: u8, + pub last_x: f64, + pub last_y: f64, +} + +#[derive(Debug, PartialEq)] +enum DisplayEventType { + DisplaySwitch, + DisplayUpdate, + CursorDefine, + DisplayRefresh, +} + +impl Default for DisplayEventType { + fn default() -> Self { + Self::DisplayRefresh + } +} + +#[derive(Default)] +pub struct DisplayChangeEvent { + dev_name: String, + event_type: DisplayEventType, + x: i32, + y: i32, + w: i32, + h: i32, + cursor: Option, +} + +impl DisplayChangeEvent { + fn new(dev_name: String, event_type: DisplayEventType) -> Self { + Self { + dev_name, + event_type, + ..Default::default() + } + } +} + +pub struct GtkInterface { + dev_name: String, + dce_sender: SyncSender, +} + +impl GtkInterface { + pub fn new(dev_name: String, dce_sender: SyncSender) -> Self { + Self { + dev_name, + dce_sender, + } + } +} + +impl DisplayChangeListenerOperations for GtkInterface { + fn dpy_switch(&self, _surface: &crate::console::DisplaySurface) -> Result<()> { + let event = DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplaySwitch); + self.dce_sender.send(event)?; + Ok(()) + } + + fn dpy_refresh( + &self, + dcl: &std::sync::Arc>, + ) -> Result<()> { + let event = + DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplayRefresh); + let con_id = dcl.lock().unwrap().con_id; + graphic_hardware_update(con_id); + self.dce_sender.send(event)?; + Ok(()) + } + + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) -> Result<()> { + let mut event = + DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplayUpdate); + event.x = x; + event.y = y; + event.w = w; + event.h = h; + self.dce_sender.send(event)?; + Ok(()) + } + + fn dpy_cursor_update(&self, cursor_data: &mut DisplayMouse) -> Result<()> { + let mut event = + DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::CursorDefine); + event.cursor = Some(cursor_data.clone()); + self.dce_sender.send(event)?; + Ok(()) + } +} + +pub(crate) struct GtkDisplay { + gtk_menu: GtkMenu, + scale_mode: Rc>, + pagenum2ds: HashMap>>, + power_button: Arc, + keysym2keycode: Rc>>, +} + +impl GtkDisplay { + fn create(gtk_menu: GtkMenu, gtk_cfg: &GtkConfig) -> Self { + // Window scale mode. + let scale_mode = Rc::new(RefCell::new(ScaleMode { + full_screen: gtk_cfg.full_screen, + free_scale: gtk_cfg.zoom_fit, + })); + // Mapping ASCII to keycode. + let keysym2keycode: Rc>> = Rc::new(RefCell::new(HashMap::new())); + let mut borrow_keysym2keycode = keysym2keycode.borrow_mut(); + for &(k, v) in KEYSYM2KEYCODE.iter() { + borrow_keysym2keycode.insert(k, v); + } + drop(borrow_keysym2keycode); + Self { + gtk_menu, + scale_mode, + pagenum2ds: HashMap::new(), + power_button: gtk_cfg.power_button.clone(), + keysym2keycode, + } + } + + // Get the current active drawing_area in note_book. + fn get_current_display(&self) -> Result>> { + let page_num = self.gtk_menu.note_book.current_page(); + let gs = match page_num { + Some(num) if self.pagenum2ds.get(&num).is_some() => self.pagenum2ds.get(&num).unwrap(), + _ => bail!("No active display"), + }; + Ok(gs.clone()) + } + + // Get the displays based on device name. + fn get_ds_by_pagenum(&self, page_num: Option) -> Option>> { + let ds = self.pagenum2ds.get(&page_num?)?; + Some(ds.clone()) + } + + // Get the display base the page number in notebook. + fn get_ds_by_devname(&self, dev_name: &str) -> Option>> { + for ds in self.pagenum2ds.values() { + if ds.borrow().dev_name.eq(dev_name) { + return Some(ds.clone()); + } + } + None + } + + fn set_draw_area(&mut self, gs: Rc>) -> Result<()> { + let draw_area = DrawingArea::new(); + draw_area.set_size_request(DEFAULT_SURFACE_WIDTH, DEFAULT_SURFACE_HEIGHT); + draw_area.set_can_focus(true); + set_callback_for_draw_area(&draw_area, gs.clone())?; + + // Add notebook page. + let active_con = gs.borrow().con.upgrade(); + let con = match active_con { + Some(con) => con, + None => bail!("No active console!"), + }; + let label_name = con.lock().unwrap().dev_name.clone(); + let label = gtk::Label::new(Some(&label_name)); + let page_num = self + .gtk_menu + .note_book + .append_page(&draw_area, Some(&label)); + self.pagenum2ds.insert(page_num, gs.clone()); + draw_area.grab_focus(); + + // Create a radio button. + // Only one screen can be displayed at a time. + let gs_show_menu = RadioMenuItem::with_label(&label_name); + if !self.gtk_menu.radio_group.is_empty() { + let first_radio = &self.gtk_menu.radio_group[0]; + gs_show_menu.join_group(Some(first_radio)); + } + let note_book = self.gtk_menu.note_book.clone(); + gs_show_menu.connect_activate(glib::clone!(@weak gs, @weak note_book => move |_| { + gs_show_menu_callback(&gs, note_book).unwrap_or_else(|e| error!("Display show menu: {:?}", e)); + })); + self.gtk_menu.view_menu.append(&gs_show_menu); + self.gtk_menu.radio_group.push(gs_show_menu); + gs.borrow_mut().draw_area = draw_area; + + Ok(()) + } +} + +pub struct GtkDisplayScreen { + window: ApplicationWindow, + dev_name: String, + draw_area: DrawingArea, + cairo_image: Option, + transfer_image: *mut pixman_image_t, + click_state: ClickState, + con: Weak>, + dcl: Weak>, + scale_mode: Rc>, + scale_x: f64, + scale_y: f64, + keysym2keycode: Rc>>, +} + +/// A displayscreen corresponds to a display area. +impl GtkDisplayScreen { + fn create( + window: ApplicationWindow, + con: Weak>, + dcl: Weak>, + keysym2keycode: Rc>>, + scale_mode: Rc>, + ) -> Self { + let surface = create_msg_surface( + DEFAULT_SURFACE_WIDTH, + DEFAULT_SURFACE_HEIGHT, + "Please wait a moment".to_string(), + ) + .map_or(DisplaySurface::default(), |s| s); + + // SAFETY: The image is created within the function, it can be ensure + // that the data ptr is not nullptr and the image size matchs the image data. + let cairo_image = unsafe { + ImageSurface::create_for_data_unsafe( + surface.data() as *mut u8, + Format::Rgb24, + surface.width(), + surface.height(), + surface.stride(), + ) + } + .ok(); + + let dev_name = match con.upgrade() { + Some(c) => c.lock().unwrap().dev_name.clone(), + None => "default".to_string(), + }; + + Self { + window, + dev_name, + draw_area: DrawingArea::default(), + cairo_image, + transfer_image: surface.image, + click_state: ClickState::default(), + con, + dcl, + scale_mode, + scale_x: 1.0, + scale_y: 1.0, + keysym2keycode, + } + } + + fn get_window_size(&self) -> Result<(f64, f64)> { + let (mut w_width, mut w_height) = (0.0, 0.0); + if let Some(win) = self.draw_area.window() { + w_width = win.width() as f64; + w_height = win.height() as f64; + }; + + if self.scale_mode.borrow().is_full_screen() { + // Get the size of desktop. + if let Some(screen) = Screen::default() { + w_width = screen.width() as f64; + w_height = screen.height() as f64; + } + } + + if w_width.eq(&0.0) || w_height.eq(&0.0) { + bail!("The window size can not be zero!"); + } + Ok((w_width, w_height)) + } + + /// Convert coordinates of the window to relative coordinates of the image. + /// In some situation: + /// 1. Image is scaled. + /// 2. There may be unfilled areas between the window and the image. + /// Input: relative coordinates of window. + /// Output: relative coordinates of images. + fn convert_coord(&mut self, x: f64, y: f64) -> Result<(f64, f64)> { + let (surface_width, surface_height) = match &self.cairo_image { + Some(image) => (image.width(), image.height()), + None => bail!("No display image."), + }; + let (scale_width, scale_height) = ( + (surface_width as f64) * self.scale_x, + (surface_height as f64) * self.scale_y, + ); + + let (window_width, window_height) = self.get_window_size()?; + let scale_factor = match self.draw_area.window() { + Some(window) => window.scale_factor() as f64, + None => bail!("No display window."), + }; + + // There may be unfilled areas between the window and the image. + let (mut mx, mut my) = (0.0, 0.0); + if window_width > scale_width { + mx = (window_width - scale_width) / (2.0); + } + if window_height > scale_height { + my = (window_height - scale_height) / (2.0); + } + let real_x = ((x - mx) / self.scale_x) * scale_factor; + let real_y = ((y - my) / self.scale_y) * scale_factor; + self.click_state.last_x = real_x; + self.click_state.last_y = real_y; + + Ok((real_x, real_y)) + } +} + +/// Args for creating gtk thread. +#[derive(Clone)] +struct GtkConfig { + full_screen: bool, + zoom_fit: bool, + vm_name: String, + power_button: Arc, + gtk_args: Vec, +} /// Gtk display init. -pub fn gtk_display_init(_ds_cfg: &DisplayConfig, _ui_context: UiContext) -> Result<()> { - let args: Vec = vec![]; +pub fn gtk_display_init(ds_cfg: &DisplayConfig, ui_context: UiContext) -> Result<()> { + let gtk_cfg = GtkConfig { + full_screen: ds_cfg.full_screen, + zoom_fit: ds_cfg.fix_size, + vm_name: ui_context.vm_name, + power_button: ui_context.power_button, + gtk_args: vec![], + }; let _handle = thread::Builder::new() .name("gtk display".to_string()) - .spawn(move || create_gtk_thread(args)) - .with_context(|| "Fail to create gtk display thread!"); + .spawn(move || create_gtk_thread(>k_cfg)) + .with_context(|| "Fail to create gtk display thread!")?; Ok(()) } /// Create a gtk thread. -pub fn create_gtk_thread(gtk_args: Vec) { +fn create_gtk_thread(gtk_cfg: &GtkConfig) { let application = Application::builder() .application_id("stratovirt.gtk") .build(); - application.connect_activate(build_ui); - application.run_with_args(>k_args); + let gtk_cfg_clone = gtk_cfg.clone(); + application.connect_activate(move |app| build_ui(app, >k_cfg_clone)); + application.run_with_args(>k_cfg.gtk_args); } // Create window. -fn build_ui(app: &Application) { +fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { let window = ApplicationWindow::builder() .application(app) - .title("Stratovirt") + .title(>k_cfg.vm_name) .default_width(DEFAULT_SURFACE_WIDTH) .default_height(DEFAULT_SURFACE_HEIGHT) .build(); - window.show_all(); + // Create menu. + let mut gtk_menu = GtkMenu::new(window); + let gd = Rc::new(RefCell::new(GtkDisplay::create(gtk_menu.clone(), gtk_cfg))); + gtk_menu.set_menu(); + gtk_menu.set_signal(&gd); + + // Gtk display init. + graphic_display_init(gd) + .with_context(|| "Gtk display init failed!") + .unwrap(); + + gtk_menu.show_window(gtk_cfg.full_screen); +} + +fn graphic_display_init(gd: Rc>) -> Result<()> { + let console_list = get_active_console(); + let mut borrowed_gd = gd.borrow_mut(); + let keysym2keycode = borrowed_gd.keysym2keycode.clone(); + let window = borrowed_gd.gtk_menu.window.clone(); + let scale_mode = borrowed_gd.scale_mode.clone(); + let (dce_sender, dce_receiver) = + glib::MainContext::sync_channel::(Priority::default(), CHANNEL_BOUND); + // Create a display area for each console. + for con in console_list { + let c = match con.upgrade() { + Some(c) => c, + None => continue, + }; + let locked_con = c.lock().unwrap(); + let dev_name = locked_con.dev_name.clone(); + let con_id = locked_con.con_id; + drop(locked_con); + // Register displaychangelistener in the console. + let gtk_opts = Arc::new(GtkInterface::new(dev_name, dce_sender.clone())); + let dcl = Arc::new(Mutex::new(DisplayChangeListener::new( + Some(con_id), + gtk_opts, + ))); + register_display(&dcl)?; + let gs = Rc::new(RefCell::new(GtkDisplayScreen::create( + window.clone(), + con.clone(), + Arc::downgrade(&dcl), + keysym2keycode.clone(), + scale_mode.clone(), + ))); + borrowed_gd.set_draw_area(gs)?; + } + drop(borrowed_gd); + + dce_receiver.attach( + None, + glib::clone!(@strong gd => @default-return Continue(true), move |event| { + gd_handle_event(&gd, event).unwrap_or_else(|e| error!("gd_handle_event: {:?}", e)); + Continue(true) + }), + ); + + Ok(()) +} + +/// Receive display update events from the mainloop of Stratovirt , +/// assigns the event to the corresponding draw display by the field +/// of device name. And then update the specific gtk display. +fn gd_handle_event(gd: &Rc>, event: DisplayChangeEvent) -> Result<()> { + let ds = match gd.borrow().get_ds_by_devname(&event.dev_name) { + Some(display) => display, + None => return Ok(()), + }; + match event.event_type { + DisplayEventType::DisplaySwitch => do_switch_event(&ds), + DisplayEventType::DisplayUpdate => do_update_event(&ds, event), + DisplayEventType::CursorDefine => do_cursor_define(&ds, event), + DisplayEventType::DisplayRefresh => do_refresh_event(&ds), + } +} + +// Select the specified display area. +fn gs_show_menu_callback( + gs: &Rc>, + note_book: gtk::Notebook, +) -> Result<()> { + let page_num = note_book.page_num(&gs.borrow().draw_area); + note_book.set_current_page(page_num); + gs.borrow().draw_area.grab_focus(); + Ok(()) +} + +/// Refresh image. +/// There is a situation: +/// 1. Switch operation 1, the gtk display should change the image from a to b. +/// 2. Switch operation 2, the gtk display should change the image from b to c, but +/// the channel between stratovirt mainloop and gtk mainloop lost the event. +/// 3. The gtk display always show th image a. +/// So, the refresh operation will always check if the image has been switched, if +/// the result is yes, then use the switch operation to switch the latest image. +fn do_refresh_event(gs: &Rc>) -> Result<()> { + let borrowed_gs = gs.borrow(); + let active_con = borrowed_gs.con.upgrade(); + let con = match active_con { + Some(con) => con, + None => return Ok(()), + }; + let locked_con = con.lock().unwrap(); + let surface = match locked_con.surface { + Some(s) => s, + None => return Ok(()), + }; + + if let Some(dcl) = borrowed_gs.dcl.upgrade() { + dcl.lock().unwrap().update_interval = 30; + }; + + let width = get_image_width(borrowed_gs.transfer_image); + let height = get_image_height(borrowed_gs.transfer_image); + let surface_width = surface.width(); + let surface_height = surface.height(); + if width == 0 || height == 0 || width != surface_width || height != surface_height { + drop(locked_con); + drop(borrowed_gs); + do_switch_event(gs)?; + } + Ok(()) +} + +/// Update cursor image. +fn do_cursor_define(gs: &Rc>, event: DisplayChangeEvent) -> Result<()> { + let c: DisplayMouse = match event.cursor { + Some(c) => c, + None => bail!("Invalid Cursor image"), + }; + + if c.data.len() < ((c.width * c.height) as usize) * 4 { + bail!("Invalid Cursor image"); + } + + let borrowed_gs = gs.borrow(); + if !borrowed_gs.draw_area.is_realized() { + bail!("The draw_area is not realized"); + } + let display = borrowed_gs.draw_area.display(); + + let pixbuf = gdk::gdk_pixbuf::Pixbuf::from_mut_slice( + c.data, + Colorspace::Rgb, + true, + 8, + c.width as i32, + c.height as i32, + (c.width as i32) * 4, + ); + let gtk_cursor = gdk::Cursor::from_pixbuf(&display, &pixbuf, c.hot_x as i32, c.hot_y as i32); + if let Some(win) = &borrowed_gs.draw_area.window() { + win.set_cursor(Some(>k_cursor)); + } + Ok(()) +} + +// Update dirty area of image. +fn do_update_event(gs: &Rc>, event: DisplayChangeEvent) -> Result<()> { + let borrowed_gs = gs.borrow(); + let active_con = borrowed_gs.con.upgrade(); + let con = match active_con { + Some(con) => con, + None => return Ok(()), + }; + let locked_con = con.lock().unwrap(); + let surface = match locked_con.surface { + Some(s) => s, + None => return Ok(()), + }; + + // drea_area is hidden behind the screen. + if !borrowed_gs.draw_area.is_realized() { + return Ok(()); + } + + if surface.image.is_null() || borrowed_gs.transfer_image.is_null() { + bail!("Image is null"); + } + + let src_width = get_image_width(surface.image); + let src_height = get_image_height(surface.image); + let dest_width = get_image_width(borrowed_gs.transfer_image); + let dest_height = get_image_height(borrowed_gs.transfer_image); + + let surface_width = cmp::min(src_width, dest_width); + let surface_height = cmp::min(src_height, dest_height); + + let (x, y) = (event.x, event.y); + let x1 = cmp::min(x + event.w, surface_width); + let y1 = cmp::min(y + event.h, surface_height); + let w = (x1 - x).abs(); + let h = (y1 - y).abs(); + + // SAFETY: Verified that the pointer of source image and dest image + // is not empty, and the copied data will not exceed the image area + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + surface.image, + ptr::null_mut(), + borrowed_gs.transfer_image, + x as i16, + y as i16, + 0, + 0, + x as i16, + y as i16, + w as u16, + h as u16, + ) + }; + drop(locked_con); + + // Image scalling. + let x1 = ((x as f64) * borrowed_gs.scale_x).floor(); + let y1 = ((y as f64) * borrowed_gs.scale_y).floor(); + let x2 = ((x as f64) * borrowed_gs.scale_x + (w as f64) * borrowed_gs.scale_x).ceil(); + let y2 = ((y as f64) * borrowed_gs.scale_y + (h as f64) * borrowed_gs.scale_y).ceil(); + + let scale_width = (surface_width as f64) * borrowed_gs.scale_x; + let scale_height = (surface_height as f64) * borrowed_gs.scale_y; + let (window_width, window_height) = borrowed_gs.get_window_size()?; + + let mut mx: f64 = 0.0; + let mut my: f64 = 0.0; + if window_width > scale_width { + mx = ((window_width - scale_width) as f64) / (2.0); + } + if window_height > scale_height { + my = ((window_height - scale_height) as f64) / (2.0); + } + + borrowed_gs.draw_area.queue_draw_area( + (mx + x1) as i32, + (my + y1) as i32, + (x2 - x1) as i32, + (y2 - y1) as i32, + ); + + Ok(()) +} + +/// Switch display image. +fn do_switch_event(gs: &Rc>) -> Result<()> { + let mut borrowed_gs = gs.borrow_mut(); + let active_con = borrowed_gs.con.upgrade(); + let con = match active_con { + Some(con) => con, + None => return Ok(()), + }; + let locked_con = con.lock().unwrap(); + let surface = match locked_con.surface { + Some(s) => s, + None => return Ok(()), + }; + + let mut need_resize: bool = true; + + let width = get_image_width(borrowed_gs.transfer_image); + let height = get_image_height(borrowed_gs.transfer_image); + let surface_width = surface.width(); + let surface_height = surface.height(); + let surface_stride = surface.stride(); + if width != 0 && height != 0 && width == surface_width && height == surface_height { + need_resize = false; + } + + if need_resize { + unref_pixman_image(borrowed_gs.transfer_image); + borrowed_gs.transfer_image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + surface_width, + surface_height, + ptr::null_mut(), + surface_stride, + ); + } + if surface.image.is_null() || borrowed_gs.transfer_image.is_null() { + bail!("Image data is invalid."); + } + + // SAFETY: + // 1. It can be sure that source ptr and dest ptr is not nullptr. + // 2. The copy range will not exceed the image area. + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + surface.image, + ptr::null_mut(), + borrowed_gs.transfer_image, + 0, + 0, + 0, + 0, + 0, + 0, + surface_width as u16, + surface_height as u16, + ) + }; + drop(locked_con); + + if need_resize { + // SAFETY: + // 1. It can be sure that the ptr of data is not nullptr. + // 2. The copy range will not exceed the image data. + let data = get_image_data(borrowed_gs.transfer_image) as *mut u8; + borrowed_gs.cairo_image = unsafe { + ImageSurface::create_for_data_unsafe( + data as *mut u8, + Format::Rgb24, + surface_width, + surface_height, + surface_stride, + ) + } + .ok() + } + drop(borrowed_gs); + + if need_resize { + update_window_size(gs) + } else { + renew_image(gs) + } +} + +pub(crate) fn update_window_size(gs: &Rc>) -> Result<()> { + let borrowed_gs = gs.borrow(); + let scale_mode = borrowed_gs.scale_mode.borrow().clone(); + let (width, height) = match &borrowed_gs.cairo_image { + Some(image) => (image.width() as f64, image.height() as f64), + None => (0.0, 0.0), + }; + let (scale_width, scale_height) = if scale_mode.is_free_scale() { + (width * GTK_SCALE_MIN, height * GTK_SCALE_MIN) + } else { + (width * borrowed_gs.scale_x, height * borrowed_gs.scale_y) + }; + + let geo: Geometry = Geometry { + min_width: scale_width as i32, + min_height: scale_height as i32, + max_width: 0, + max_height: 0, + base_width: 0, + base_height: 0, + width_inc: 0, + height_inc: 0, + min_aspect: 0.0, + max_aspect: 0.0, + win_gravity: Gravity::Center, + }; + let geo_mask = WindowHints::MIN_SIZE; + + borrowed_gs + .draw_area + .set_size_request(geo.min_width, geo.min_height); + if let Some(window) = borrowed_gs.draw_area.window() { + window.set_geometry_hints(&geo, geo_mask) + } + + if !scale_mode.is_full_screen() && !scale_mode.is_free_scale() { + borrowed_gs + .window + .resize(DEFAULT_SURFACE_WIDTH, DEFAULT_SURFACE_HEIGHT); + } + Ok(()) +} + +/// Ask the gtk display to update the display. +pub(crate) fn renew_image(gs: &Rc>) -> Result<()> { + let borrowed_gs = gs.borrow(); + let (width, height) = borrowed_gs.get_window_size()?; + borrowed_gs + .draw_area + .queue_draw_area(0, 0, width as i32, height as i32); + Ok(()) } diff --git a/ui/src/input.rs b/ui/src/input.rs index 6763637ab..878db231b 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -255,6 +255,17 @@ pub fn key_event(keycode: u16, down: bool) -> Result<()> { Ok(()) } +/// A complete mouse click event. +pub fn press_mouse(button: u32, x: u32, y: u32) -> Result<()> { + let mouse = INPUTS.lock().unwrap().get_active_mouse(); + if let Some(m) = mouse { + let mut locked_mouse = m.lock().unwrap(); + locked_mouse.do_point_event(button, x, y)?; + locked_mouse.do_point_event(0, x, y)? + } + Ok(()) +} + pub fn point_event(button: u32, x: u32, y: u32) -> Result<()> { let mouse = INPUTS.lock().unwrap().get_active_mouse(); if let Some(m) = mouse { -- Gitee From 7a5cbe43ff0fe29a44375c0d966f6dea4aef0b04 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 02:23:01 +0800 Subject: [PATCH 1009/2187] GTK: improve the performance of gtk. Reduce one memory copy during GTK operation to improve performance Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 159 +++++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 60 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 88704272c..e4af59d96 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -44,11 +44,12 @@ use crate::{ data::keycode::KEYSYM2KEYCODE, gtk::{draw::set_callback_for_draw_area, menu::GtkMenu}, pixman::{ - create_pixman_image, get_image_data, get_image_height, get_image_width, unref_pixman_image, + create_pixman_image, get_image_data, get_image_height, get_image_width, ref_pixman_image, + unref_pixman_image, }, }; use machine_manager::config::{DisplayConfig, UiContext}; -use util::pixman::{pixman_format_code_t, pixman_image_composite, pixman_image_t, pixman_op_t}; +use util::pixman::{pixman_format_code_t, pixman_image_composite, pixman_op_t}; use vmm_sys_util::eventfd::EventFd; const CHANNEL_BOUND: usize = 1024; @@ -281,8 +282,9 @@ pub struct GtkDisplayScreen { window: ApplicationWindow, dev_name: String, draw_area: DrawingArea, + source_surface: DisplaySurface, + transfer_surface: Option, cairo_image: Option, - transfer_image: *mut pixman_image_t, click_state: ClickState, con: Weak>, dcl: Weak>, @@ -330,8 +332,9 @@ impl GtkDisplayScreen { window, dev_name, draw_area: DrawingArea::default(), + source_surface: surface, + transfer_surface: None, cairo_image, - transfer_image: surface.image, click_state: ClickState::default(), con, dcl, @@ -560,8 +563,8 @@ fn do_refresh_event(gs: &Rc>) -> Result<()> { dcl.lock().unwrap().update_interval = 30; }; - let width = get_image_width(borrowed_gs.transfer_image); - let height = get_image_height(borrowed_gs.transfer_image); + let width = borrowed_gs.source_surface.width(); + let height = borrowed_gs.source_surface.height(); let surface_width = surface.width(); let surface_height = surface.height(); if width == 0 || height == 0 || width != surface_width || height != surface_height { @@ -624,14 +627,14 @@ fn do_update_event(gs: &Rc>, event: DisplayChangeEvent return Ok(()); } - if surface.image.is_null() || borrowed_gs.transfer_image.is_null() { + if surface.image.is_null() { bail!("Image is null"); } let src_width = get_image_width(surface.image); let src_height = get_image_height(surface.image); - let dest_width = get_image_width(borrowed_gs.transfer_image); - let dest_height = get_image_height(borrowed_gs.transfer_image); + let dest_width = get_image_width(borrowed_gs.source_surface.image); + let dest_height = get_image_height(borrowed_gs.source_surface.image); let surface_width = cmp::min(src_width, dest_width); let surface_height = cmp::min(src_height, dest_height); @@ -642,23 +645,31 @@ fn do_update_event(gs: &Rc>, event: DisplayChangeEvent let w = (x1 - x).abs(); let h = (y1 - y).abs(); - // SAFETY: Verified that the pointer of source image and dest image - // is not empty, and the copied data will not exceed the image area - unsafe { - pixman_image_composite( - pixman_op_t::PIXMAN_OP_SRC, - surface.image, - ptr::null_mut(), - borrowed_gs.transfer_image, - x as i16, - y as i16, - 0, - 0, - x as i16, - y as i16, - w as u16, - h as u16, - ) + match borrowed_gs.transfer_surface { + Some(s) if borrowed_gs.source_surface.format != pixman_format_code_t::PIXMAN_x8r8g8b8 => { + if src_width != s.width() || src_height != s.height() { + bail!("Wrong format of image format."); + } + // SAFETY: Verified that the pointer of source image and dest image + // is not empty, and the copied data will not exceed the image area + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + surface.image, + ptr::null_mut(), + s.image, + x as i16, + y as i16, + 0, + 0, + x as i16, + y as i16, + w as u16, + h as u16, + ) + }; + } + _ => {} }; drop(locked_con); @@ -707,8 +718,8 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { let mut need_resize: bool = true; - let width = get_image_width(borrowed_gs.transfer_image); - let height = get_image_height(borrowed_gs.transfer_image); + let width = borrowed_gs.source_surface.width(); + let height = borrowed_gs.source_surface.height(); let surface_width = surface.width(); let surface_height = surface.height(); let surface_stride = surface.stride(); @@ -716,46 +727,27 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { need_resize = false; } - if need_resize { - unref_pixman_image(borrowed_gs.transfer_image); - borrowed_gs.transfer_image = create_pixman_image( - pixman_format_code_t::PIXMAN_x8r8g8b8, - surface_width, - surface_height, - ptr::null_mut(), - surface_stride, - ); - } - if surface.image.is_null() || borrowed_gs.transfer_image.is_null() { + if surface.image.is_null() { bail!("Image data is invalid."); } - // SAFETY: - // 1. It can be sure that source ptr and dest ptr is not nullptr. - // 2. The copy range will not exceed the image area. - unsafe { - pixman_image_composite( - pixman_op_t::PIXMAN_OP_SRC, - surface.image, - ptr::null_mut(), - borrowed_gs.transfer_image, - 0, - 0, - 0, - 0, - 0, - 0, - surface_width as u16, - surface_height as u16, - ) + let source_suface = DisplaySurface { + format: surface.format, + image: ref_pixman_image(surface.image), }; + unref_pixman_image(borrowed_gs.source_surface.image); + borrowed_gs.source_surface = source_suface; + if let Some(s) = borrowed_gs.transfer_surface { + unref_pixman_image(s.image); + borrowed_gs.transfer_surface = None; + } drop(locked_con); - if need_resize { + if borrowed_gs.source_surface.format == pixman_format_code_t::PIXMAN_x8r8g8b8 { + let data = get_image_data(borrowed_gs.source_surface.image) as *mut u8; // SAFETY: // 1. It can be sure that the ptr of data is not nullptr. // 2. The copy range will not exceed the image data. - let data = get_image_data(borrowed_gs.transfer_image) as *mut u8; borrowed_gs.cairo_image = unsafe { ImageSurface::create_for_data_unsafe( data as *mut u8, @@ -766,7 +758,54 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { ) } .ok() - } + } else { + let transfer_image = create_pixman_image( + pixman_format_code_t::PIXMAN_x8r8g8b8, + surface_width, + surface_height, + ptr::null_mut(), + surface_stride, + ); + + // SAFETY: + // 1. It can be sure that the ptr of data is not nullptr. + // 2. The copy range will not exceed the image data. + let data = get_image_data(transfer_image) as *mut u8; + borrowed_gs.cairo_image = unsafe { + ImageSurface::create_for_data_unsafe( + data as *mut u8, + Format::Rgb24, + surface_width, + surface_height, + surface_stride, + ) + } + .ok(); + + // SAFETY: + // 1. It can be sure that source ptr and dest ptr is not nullptr. + // 2. The copy range will not exceed the image area. + unsafe { + pixman_image_composite( + pixman_op_t::PIXMAN_OP_SRC, + borrowed_gs.source_surface.image, + ptr::null_mut(), + transfer_image, + 0, + 0, + 0, + 0, + 0, + 0, + surface_width as u16, + surface_height as u16, + ) + }; + borrowed_gs.transfer_surface = Some(DisplaySurface { + format: pixman_format_code_t::PIXMAN_x8r8g8b8, + image: transfer_image, + }); + }; drop(borrowed_gs); if need_resize { -- Gitee From 9da7ece60b7cd91fcab49861450eeac7386173d1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 16 Apr 2023 18:02:39 +0800 Subject: [PATCH 1010/2187] aio: Add UT for sync io This has good coverage for sync IO, but does not cover async IO. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index ff569fa5c..7ed1241e4 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -641,3 +641,131 @@ fn iovec_is_zero(iovecs: &[Iovec]) -> bool { } true } + +#[cfg(test)] +mod tests { + use super::*; + use std::os::unix::prelude::AsRawFd; + use vmm_sys_util::tempfile::TempFile; + + fn perform_sync_rw( + fsize: usize, + offset: usize, + nbytes: u64, + opcode: OpCode, + direct: bool, + align: u32, + ) { + assert!(opcode == OpCode::Preadv || opcode == OpCode::Pwritev); + // Init a file with special content. + let mut content = vec![0u8; fsize]; + for (index, elem) in content.as_mut_slice().into_iter().enumerate() { + *elem = index as u8; + } + let tmp_file = TempFile::new().unwrap(); + let mut file = tmp_file.into_file(); + file.write_all(&content).unwrap(); + + // Prepare rw buf. + let mut buf = vec![0xEF; nbytes as usize / 3]; + let mut buf2 = vec![0xFE; nbytes as usize - buf.len()]; + let iovec = vec![ + Iovec { + iov_base: buf.as_mut_ptr() as u64, + iov_len: buf.len() as u64, + }, + Iovec { + iov_base: buf2.as_mut_ptr() as u64, + iov_len: buf2.len() as u64, + }, + ]; + + // Perform aio rw. + let file_fd = file.as_raw_fd(); + let aiocb = AioCb { + direct, + req_align: align, + buf_align: align, + file_fd, + opcode, + iovec, + offset, + nbytes, + user_data: 0, + iocompletecb: 0, + discard: false, + write_zeroes: WriteZeroesState::Off, + write_zeroes_unmap: false, + }; + let mut aio = Aio::new( + Arc::new(|_: &AioCb, _: i64| -> Result<()> { Ok(()) }), + AioEngine::Off, + ) + .unwrap(); + aio.submit_request(aiocb).unwrap(); + + // Get actual file content. + let mut new_content = vec![0u8; fsize]; + let ret = raw_read( + file_fd, + new_content.as_mut_ptr() as u64, + new_content.len(), + 0, + ); + assert_eq!(ret, fsize as i64); + if opcode == OpCode::Pwritev { + // The expected file content. + let ret = (&mut content[offset..]).write(&buf).unwrap(); + assert_eq!(ret, buf.len()); + let ret = (&mut content[offset + buf.len()..]).write(&buf2).unwrap(); + assert_eq!(ret, buf2.len()); + for index in 0..fsize { + assert_eq!(new_content[index], content[index]); + } + } else { + for index in 0..buf.len() { + assert_eq!(buf[index], new_content[offset + index]); + } + for index in 0..buf2.len() { + assert_eq!(buf2[index], new_content[offset + buf.len() + index]); + } + } + } + + fn test_sync_rw(opcode: OpCode, direct: bool, align: u32) { + assert!(align >= 512); + let fsize: usize = 2 << 20; + + // perform sync rw in the same alignment section. + let minor_align = align as u64 - 100; + perform_sync_rw(fsize, 0, minor_align, opcode, direct, align); + perform_sync_rw(fsize, 50, minor_align, opcode, direct, align); + perform_sync_rw(fsize, 100, minor_align, opcode, direct, align); + + // perform sync rw across alignment sections. + let minor_size = fsize as u64 - 100; + perform_sync_rw(fsize, 0, minor_size, opcode, direct, align); + perform_sync_rw(fsize, 50, minor_size, opcode, direct, align); + perform_sync_rw(fsize, 100, minor_size, opcode, direct, align); + } + + fn test_sync_rw_all_align(opcode: OpCode, direct: bool) { + let basic_align = 512; + test_sync_rw(opcode, direct, basic_align << 0); + test_sync_rw(opcode, direct, basic_align << 1); + test_sync_rw(opcode, direct, basic_align << 2); + test_sync_rw(opcode, direct, basic_align << 3); + } + + #[test] + fn test_direct_sync_rw() { + test_sync_rw_all_align(OpCode::Preadv, true); + test_sync_rw_all_align(OpCode::Pwritev, true); + } + + #[test] + fn test_indirect_sync_rw() { + test_sync_rw_all_align(OpCode::Preadv, false); + test_sync_rw_all_align(OpCode::Pwritev, false); + } +} -- Gitee From d4ae38a3f4eeaf028a697ffbec2c617b7a7e0e19 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 18:57:15 +0800 Subject: [PATCH 1011/2187] Ui: Optimized code in console.rs. Reduce redundant code in console.rs. Signed-off-by: Xiao Ye --- ui/src/console.rs | 66 ++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index dc3e2e380..a2366619f 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -210,6 +210,25 @@ impl DisplayState { refresh_num: 0, } } + + // Get all related display by con_id. + fn get_related_display(&self, con_id: usize) -> Result>>> { + let mut related_dpys: Vec>> = vec![]; + let active_id = CONSOLES.lock().unwrap().activate_id; + for dcl in self.listeners.iter().flatten() { + match dcl.lock().unwrap().con_id { + Some(id) if con_id == id => { + related_dpys.push(dcl.clone()); + } + None if Some(con_id) == active_id => { + related_dpys.push(dcl.clone()); + } + _ => {} + } + } + + Ok(related_dpys) + } } /// The registered console will be inserted in the console list. @@ -336,21 +355,7 @@ pub fn display_replace_surface( } drop(locked_con); - let mut related_listeners: Vec>> = vec![]; - let activate_id = CONSOLES.lock().unwrap().activate_id; - let locked_state = DISPLAY_STATE.lock().unwrap(); - for dcl in locked_state.listeners.iter().flatten() { - let mut dcl_id = dcl.lock().unwrap().con_id; - if dcl_id.is_none() { - dcl_id = activate_id; - } - - if Some(con_id) == dcl_id { - related_listeners.push(dcl.clone()); - } - } - drop(locked_state); - + let related_listeners = DISPLAY_STATE.lock().unwrap().get_related_display(con_id)?; for dcl in related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); if let Some(s) = &con.lock().unwrap().surface.clone() { @@ -389,21 +394,7 @@ pub fn display_graphic_update( let con_id = locked_con.con_id; drop(locked_con); - let activate_id = CONSOLES.lock().unwrap().activate_id; - let mut related_listeners: Vec>> = vec![]; - let locked_state = DISPLAY_STATE.lock().unwrap(); - for dcl in locked_state.listeners.iter().flatten() { - let mut dcl_id = dcl.lock().unwrap().con_id; - if dcl_id.is_none() { - dcl_id = activate_id; - } - - if Some(con_id) == dcl_id { - related_listeners.push(dcl.clone()); - } - } - drop(locked_state); - + let related_listeners = DISPLAY_STATE.lock().unwrap().get_related_display(con_id)?; for dcl in related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); (*dcl_opts).dpy_image_update(x, y, w, h)?; @@ -425,21 +416,8 @@ pub fn display_cursor_define( Some(c) => c, None => return Ok(()), }; - let activate_id = CONSOLES.lock().unwrap().activate_id; let con_id = con.lock().unwrap().con_id; - let mut related_listeners: Vec>> = vec![]; - let locked_state = DISPLAY_STATE.lock().unwrap(); - for dcl in locked_state.listeners.iter().flatten() { - let mut dcl_id = dcl.lock().unwrap().con_id; - if dcl_id.is_none() { - dcl_id = activate_id; - } - - if Some(con_id) == dcl_id { - related_listeners.push(dcl.clone()); - } - } - drop(locked_state); + let related_listeners = DISPLAY_STATE.lock().unwrap().get_related_display(con_id)?; for dcl in related_listeners.iter() { let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); -- Gitee From ee7747371d140fa452917733728b5e4065545ad7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 20:56:53 +0800 Subject: [PATCH 1012/2187] GTK: add function of update cursor Add function of update cursor, which is used to hide the cursor int host desktop. To prevent the mouse inside the virtual machine from overlapping with the external mouse. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 5 +++-- ui/src/gtk/mod.rs | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 66cfc1f21..3da6762dd 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -31,7 +31,8 @@ use gtk::{ use log::error; use crate::gtk::{ - renew_image, update_window_size, GtkDisplay, ZoomOperate, GTK_SCALE_MIN, GTK_ZOOM_STEP, + renew_image, update_cursor_display, update_window_size, GtkDisplay, ZoomOperate, GTK_SCALE_MIN, + GTK_ZOOM_STEP, }; #[derive(Clone)] @@ -298,7 +299,7 @@ fn full_screen_callback(gd: &Rc>) -> Result<()> { update_window_size(&gs)?; }; - Ok(()) + update_cursor_display(&gs) } /// Zoom in/out the display. diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index e4af59d96..18a3fc1b7 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -535,7 +535,8 @@ fn gs_show_menu_callback( let page_num = note_book.page_num(&gs.borrow().draw_area); note_book.set_current_page(page_num); gs.borrow().draw_area.grab_focus(); - Ok(()) + update_window_size(gs)?; + update_cursor_display(gs) } /// Refresh image. @@ -867,3 +868,19 @@ pub(crate) fn renew_image(gs: &Rc>) -> Result<()> { .queue_draw_area(0, 0, width as i32, height as i32); Ok(()) } + +/// Hide the external Cursor. +pub(crate) fn update_cursor_display(gs: &Rc>) -> Result<()> { + let borrowed_gs = gs.borrow(); + if !borrowed_gs.draw_area.is_realized() { + return Ok(()); + } + + if let Some(win) = borrowed_gs.draw_area.window() { + let dpy = borrowed_gs.window.display(); + let c = gdk::Cursor::for_display(&dpy, gdk::CursorType::BlankCursor); + win.set_cursor(Some(&c)); + } + + Ok(()) +} -- Gitee From 173853b38321a280d319f5545a893be43e95b349 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 01:11:12 +0800 Subject: [PATCH 1013/2187] GTK: fix a mirror error of pointer input Converting f64 to u64 will truncate the accuracy of the data, so the conversion will be done after the calculation is completed Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 8 ++++---- ui/src/gtk/mod.rs | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 4adcd715a..01d816bc4 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -128,8 +128,8 @@ fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) None => return Ok(()), }; let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; - let standard_x = (((real_x as u64 * ABS_MAX) / width as u64) as u16) as u16; - let standard_y = (((real_y as u64 * ABS_MAX) / height as u64) as u16) as u16; + let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; + let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; point_event( borrowed_gs.click_state.button_mask as u32, @@ -158,8 +158,8 @@ fn da_pointer_callback( let (x, y) = button_event.position(); let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; - let standard_x = (((real_x as u64 * ABS_MAX) / width as u64) as u16) as u16; - let standard_y = (((real_y as u64 * ABS_MAX) / height as u64) as u16) as u16; + let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; + let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; match button_event.event_type() { gdk::EventType::ButtonRelease => { diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 18a3fc1b7..e8c73dede 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -388,6 +388,10 @@ impl GtkDisplayScreen { None => bail!("No display window."), }; + if x.lt(&0.0) || x.gt(&window_width) || y.lt(&0.0) || y.gt(&window_height) { + bail!("The coordinate of pointer exceeds limit") + } + // There may be unfilled areas between the window and the image. let (mut mx, mut my) = (0.0, 0.0); if window_width > scale_width { -- Gitee From de05782372eb859305fdfaf991fb68dfd0e4c745 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 02:23:54 +0800 Subject: [PATCH 1014/2187] GTK: Optimize refresh interval If virtio-gpu is currently used, the refresh rate will be minimized. Otherwise if ramfb is currently used, the refresh interval will be reduced. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 18a3fc1b7..462f27997 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -262,15 +262,19 @@ impl GtkDisplay { // Create a radio button. // Only one screen can be displayed at a time. let gs_show_menu = RadioMenuItem::with_label(&label_name); - if !self.gtk_menu.radio_group.is_empty() { - let first_radio = &self.gtk_menu.radio_group[0]; - gs_show_menu.join_group(Some(first_radio)); - } let note_book = self.gtk_menu.note_book.clone(); gs_show_menu.connect_activate(glib::clone!(@weak gs, @weak note_book => move |_| { gs_show_menu_callback(&gs, note_book).unwrap_or_else(|e| error!("Display show menu: {:?}", e)); })); self.gtk_menu.view_menu.append(&gs_show_menu); + + if !self.gtk_menu.radio_group.is_empty() { + let first_radio = &self.gtk_menu.radio_group[0]; + gs_show_menu.join_group(Some(first_radio)); + } else { + gs_show_menu.activate(); + } + self.gtk_menu.radio_group.push(gs_show_menu); gs.borrow_mut().draw_area = draw_area; @@ -532,9 +536,20 @@ fn gs_show_menu_callback( gs: &Rc>, note_book: gtk::Notebook, ) -> Result<()> { - let page_num = note_book.page_num(&gs.borrow().draw_area); + let borrowed_gs = gs.borrow(); + let page_num = note_book.page_num(&borrowed_gs.draw_area); note_book.set_current_page(page_num); - gs.borrow().draw_area.grab_focus(); + + if let Some(dcl) = borrowed_gs.dcl.upgrade() { + if borrowed_gs.dev_name == "ramfb" { + dcl.lock().unwrap().update_interval = 30; + } else { + dcl.lock().unwrap().update_interval = 0; + } + } + + borrowed_gs.draw_area.grab_focus(); + drop(borrowed_gs); update_window_size(gs)?; update_cursor_display(gs) } @@ -560,10 +575,6 @@ fn do_refresh_event(gs: &Rc>) -> Result<()> { None => return Ok(()), }; - if let Some(dcl) = borrowed_gs.dcl.upgrade() { - dcl.lock().unwrap().update_interval = 30; - }; - let width = borrowed_gs.source_surface.width(); let height = borrowed_gs.source_surface.height(); let surface_width = surface.width(); -- Gitee From 98af569e525e87dd572699a4152dda5b6f652d47 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Apr 2023 14:57:49 +0800 Subject: [PATCH 1015/2187] Boot: switch from ramfb to virtio-gpu automatically During the boot process of virtual machine, switch from ramfb to virtio-gpu automatically. Signed-off-by: Xiao Ye --- devices/src/legacy/ramfb.rs | 5 ++-- ui/src/console.rs | 59 +++++++++++++++++++++++++++++++++++++ ui/src/gtk/mod.rs | 20 ++++++++++++- virtio/src/device/gpu.rs | 22 ++++++++++++-- 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 536623b83..b04130dbe 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -26,8 +26,8 @@ use address_space::{AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use ui::console::{ - console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, - DisplaySurface, HardWareOperations, + console_init, display_graphic_update, display_replace_surface, set_run_stage, ConsoleType, + DisplayConsole, DisplaySurface, HardWareOperations, VmRunningStage, }; use ui::input::{key_event, KEYCODE_RET}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; @@ -69,6 +69,7 @@ impl RamfbState { pub fn new(sys_mem: Arc, install: bool) -> Self { let ramfb_opts = Arc::new(RamfbInterface {}); let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); + set_run_stage(VmRunningStage::Bios); Self { surface: None, con, diff --git a/ui/src/console.rs b/ui/src/console.rs index a2366619f..485e45122 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -56,6 +56,14 @@ pub enum ConsoleType { Text, } +/// Run stage of virtual machine. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum VmRunningStage { + Init, + Bios, + Os, +} + /// Image data defined in display. #[derive(Clone, Copy)] pub struct DisplaySurface { @@ -119,6 +127,10 @@ pub trait DisplayChangeListenerOperations { fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) -> Result<()>; /// Update the cursor data. fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) -> Result<()>; + /// Set the current display as major. + fn dpy_set_major(&self) -> Result<()> { + Ok(()) + } } /// Callback functions registered by graphic hardware. @@ -187,6 +199,8 @@ impl DisplayConsole { /// The state of console layer. pub struct DisplayState { + /// Running stage. + pub run_stage: VmRunningStage, /// Refresh interval, which can be dynamic changed. pub interval: u64, /// Whether there is a refresh task. @@ -204,6 +218,7 @@ unsafe impl Send for DisplayState {} impl DisplayState { fn new() -> Self { Self { + run_stage: VmRunningStage::Init, interval: DISPLAY_UPDATE_INTERVAL_DEFAULT, is_refresh: false, listeners: Vec::new(), @@ -253,6 +268,19 @@ impl ConsoleList { } } + // Get console by device name. + fn get_console_by_dev_name(&mut self, dev_name: String) -> Option>> { + let mut target: Option>> = None; + for con in self.console_list.iter().flatten() { + let locked_con = con.lock().unwrap(); + if locked_con.dev_name == dev_name { + target = Some(con.clone()); + break; + } + } + target + } + /// Get the console by id. fn get_console_by_id(&mut self, con_id: Option) -> Option>> { if con_id.is_none() && self.activate_id.is_none() { @@ -270,6 +298,16 @@ impl ConsoleList { } } +/// Set currently running stage for virtual machine. +pub fn set_run_stage(run_stage: VmRunningStage) { + DISPLAY_STATE.lock().unwrap().run_stage = run_stage +} + +/// Get currently running stage. +pub fn get_run_stage() -> VmRunningStage { + DISPLAY_STATE.lock().unwrap().run_stage +} + /// Refresh display image. pub fn display_refresh() { let mut dcl_interval: u64; @@ -426,6 +464,27 @@ pub fn display_cursor_define( Ok(()) } +/// Set specific screen as the main display screen. +pub fn display_set_major_screen(dev_name: &str) -> Result<()> { + let con = match CONSOLES + .lock() + .unwrap() + .get_console_by_dev_name(dev_name.to_string()) + { + Some(c) => c, + None => return Ok(()), + }; + let con_id = con.lock().unwrap().con_id; + console_select(Some(con_id))?; + let related_listeners = DISPLAY_STATE.lock().unwrap().get_related_display(con_id)?; + + for dcl in related_listeners.iter() { + let dcl_opts = dcl.lock().unwrap().dpy_opts.clone(); + (*dcl_opts).dpy_set_major()?; + } + Ok(()) +} + pub fn graphic_hardware_update(con_id: Option) { let console = CONSOLES.lock().unwrap().get_console_by_id(con_id); if let Some(con) = console { diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 175fa736a..9a8b0d750 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -99,6 +99,7 @@ enum DisplayEventType { DisplayUpdate, CursorDefine, DisplayRefresh, + DisplaySetMajor, } impl Default for DisplayEventType { @@ -179,6 +180,13 @@ impl DisplayChangeListenerOperations for GtkInterface { self.dce_sender.send(event)?; Ok(()) } + + fn dpy_set_major(&self) -> Result<()> { + let event = + DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplaySetMajor); + self.dce_sender.send(event)?; + Ok(()) + } } pub(crate) struct GtkDisplay { @@ -275,7 +283,8 @@ impl GtkDisplay { gs_show_menu.activate(); } - self.gtk_menu.radio_group.push(gs_show_menu); + self.gtk_menu.radio_group.push(gs_show_menu.clone()); + gs.borrow_mut().show_menu = gs_show_menu; gs.borrow_mut().draw_area = draw_area; Ok(()) @@ -285,6 +294,7 @@ impl GtkDisplay { pub struct GtkDisplayScreen { window: ApplicationWindow, dev_name: String, + show_menu: RadioMenuItem, draw_area: DrawingArea, source_surface: DisplaySurface, transfer_surface: Option, @@ -336,6 +346,7 @@ impl GtkDisplayScreen { window, dev_name, draw_area: DrawingArea::default(), + show_menu: RadioMenuItem::default(), source_surface: surface, transfer_surface: None, cairo_image, @@ -532,6 +543,7 @@ fn gd_handle_event(gd: &Rc>, event: DisplayChangeEvent) -> R DisplayEventType::DisplayUpdate => do_update_event(&ds, event), DisplayEventType::CursorDefine => do_cursor_define(&ds, event), DisplayEventType::DisplayRefresh => do_refresh_event(&ds), + DisplayEventType::DisplaySetMajor => do_set_major_event(&ds), } } @@ -831,6 +843,12 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { } } +/// Activate the current screen. +fn do_set_major_event(gs: &Rc>) -> Result<()> { + gs.borrow().show_menu.activate(); + Ok(()) +} + pub(crate) fn update_window_size(gs: &Rc>) -> Result<()> { let borrowed_gs = gs.borrow(); let scale_mode = borrowed_gs.scale_mode.borrow().clone(); diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a39f76d7e..2446a0cbb 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -38,8 +38,8 @@ use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; use ui::console::{ console_close, console_init, display_cursor_define, display_graphic_update, - display_replace_surface, ConsoleType, DisplayConsole, DisplayMouse, DisplaySurface, - HardWareOperations, + display_replace_surface, display_set_major_screen, get_run_stage, set_run_stage, ConsoleType, + DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, VmRunningStage, }; use ui::pixman::unref_pixman_image; use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; @@ -1350,7 +1350,19 @@ impl GpuIoHandler { VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self.cmd_transfer_to_host_2d(req), VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self.cmd_resource_attach_backing(req), VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self.cmd_resource_detach_backing(req), - VIRTIO_GPU_CMD_GET_EDID => self.cmd_get_edid(req), + VIRTIO_GPU_CMD_GET_EDID => { + if get_run_stage() == VmRunningStage::Bios && !self.scanouts.is_empty() { + match &self.scanouts[0].con.as_ref().and_then(|c| c.upgrade()) { + Some(con) => { + let dev_name = con.lock().unwrap().dev_name.clone(); + display_set_major_screen(&dev_name)?; + set_run_stage(VmRunningStage::Os); + } + None => {} + }; + } + self.cmd_get_edid(req) + } _ => self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), } { error!("Fail to handle GPU request, {:?}.", e); @@ -1729,6 +1741,10 @@ impl VirtioDevice for Gpu { } fn deactivate(&mut self) -> Result<()> { + if get_run_stage() == VmRunningStage::Os { + display_set_major_screen("ramfb")?; + set_run_stage(VmRunningStage::Bios); + } unregister_event_helper(None, &mut self.deactivate_evts) } } -- Gitee From 1e8398bd589736d5b2af48231cead727182306d1 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 11:52:15 +0800 Subject: [PATCH 1016/2187] GTK: Optimize refresh interval For ramfb, if it is not activated, adjust its refresh rate to the lowest Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 175fa736a..536577d46 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -30,7 +30,9 @@ use gtk::{ gdk_pixbuf::Colorspace, glib::{self, Priority, SyncSender}, prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, - traits::{GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt}, + traits::{ + CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt, + }, Application, ApplicationWindow, DrawingArea, RadioMenuItem, }; use log::error; @@ -263,8 +265,8 @@ impl GtkDisplay { // Only one screen can be displayed at a time. let gs_show_menu = RadioMenuItem::with_label(&label_name); let note_book = self.gtk_menu.note_book.clone(); - gs_show_menu.connect_activate(glib::clone!(@weak gs, @weak note_book => move |_| { - gs_show_menu_callback(&gs, note_book).unwrap_or_else(|e| error!("Display show menu: {:?}", e)); + gs_show_menu.connect_activate(glib::clone!(@weak gs, @weak note_book => move |show_menu| { + gs_show_menu_callback(&gs, note_book, show_menu).unwrap_or_else(|e| error!("Display show menu: {:?}", e)); })); self.gtk_menu.view_menu.append(&gs_show_menu); @@ -539,16 +541,17 @@ fn gd_handle_event(gd: &Rc>, event: DisplayChangeEvent) -> R fn gs_show_menu_callback( gs: &Rc>, note_book: gtk::Notebook, + show_menu: &RadioMenuItem, ) -> Result<()> { let borrowed_gs = gs.borrow(); let page_num = note_book.page_num(&borrowed_gs.draw_area); note_book.set_current_page(page_num); - if let Some(dcl) = borrowed_gs.dcl.upgrade() { - if borrowed_gs.dev_name == "ramfb" { - dcl.lock().unwrap().update_interval = 30; - } else { - dcl.lock().unwrap().update_interval = 0; + if borrowed_gs.dev_name == "ramfb" { + match borrowed_gs.dcl.upgrade() { + Some(dcl) if show_menu.is_active() => dcl.lock().unwrap().update_interval = 30, + Some(dcl) if !show_menu.is_active() => dcl.lock().unwrap().update_interval = 0, + _ => {} } } -- Gitee From 273037f640a7dd5d7f0107ab9dc1d89e99562d5c Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 13:14:41 +0800 Subject: [PATCH 1017/2187] GTK: Virtual machine shutdown When the system cannot shut down gracefully, fill out a pop-up window to remind the user Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/mod.rs | 3 +- machine/src/standard_vm/x86_64/mod.rs | 3 +- machine_manager/src/config/display.rs | 6 ++- ui/src/gtk/menu.rs | 61 +++++++++++++++++++++----- ui/src/gtk/mod.rs | 32 ++++++++++++-- 5 files changed, 86 insertions(+), 19 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 66d42cc07..34e799c60 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -662,7 +662,8 @@ impl MachineOps for StdMachine { Some(ref ds_cfg) if ds_cfg.gtk => { let ui_context = UiContext { vm_name: vm_config.guest_name.clone(), - power_button: self.power_button.clone(), + power_button: Some(self.power_button.clone()), + shutdown_req: Some(self.shutdown_req.clone()), }; gtk_display_init(ds_cfg, ui_context) .with_context(|| "Failed to init GTK display!")?; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 3168f9ac5..621e45931 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -577,7 +577,8 @@ impl MachineOps for StdMachine { Some(ref ds_cfg) if ds_cfg.gtk => { let ui_context = UiContext { vm_name: vm_config.guest_name.clone(), - power_button: self.shutdown_req.clone(), + power_button: None, + shutdown_req: Some(self.shutdown_req.clone()), }; gtk_display_init(ds_cfg, ui_context) .with_context(|| "Failed to init GTK display!")?; diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 991ed5886..a21d53e9a 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -20,8 +20,12 @@ use crate::config::{CmdParser, ExBool, VmConfig}; /// Event fd related to power button in gtk. pub struct UiContext { + /// Name of virtual machine. pub vm_name: String, - pub power_button: Arc, + /// Gracefully Shutdown. + pub power_button: Option>, + /// Forced Shutdown. + pub shutdown_req: Option>, } /// GTK related configuration. diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 3da6762dd..b1b778ce2 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -20,19 +20,22 @@ use gtk::{ ModifierType, }, glib, - prelude::{AccelGroupExtManual, NotebookExtManual}, + prelude::{AccelGroupExtManual, NotebookExtManual, WidgetExtManual}, traits::{ - BoxExt, CheckMenuItemExt, ContainerExt, GtkMenuExt, GtkMenuItemExt, GtkWindowExt, - MenuShellExt, NotebookExt, WidgetExt, + BoxExt, CheckMenuItemExt, ContainerExt, DialogExt, GtkMenuExt, GtkMenuItemExt, + GtkWindowExt, MenuShellExt, NotebookExt, WidgetExt, }, - AccelFlags, AccelGroup, ApplicationWindow, CheckMenuItem, Inhibit, Menu, MenuBar, MenuItem, - Orientation, RadioMenuItem, + AccelFlags, AccelGroup, ApplicationWindow, ButtonsType, CheckMenuItem, DialogFlags, Inhibit, + Menu, MenuBar, MenuItem, MessageDialog, MessageType, Orientation, RadioMenuItem, }; use log::error; -use crate::gtk::{ - renew_image, update_cursor_display, update_window_size, GtkDisplay, ZoomOperate, GTK_SCALE_MIN, - GTK_ZOOM_STEP, +use crate::{ + console::{get_run_stage, VmRunningStage}, + gtk::{ + renew_image, update_cursor_display, update_window_size, GtkDisplay, ZoomOperate, + GTK_SCALE_MIN, GTK_ZOOM_STEP, + }, }; #[derive(Clone)] @@ -190,8 +193,8 @@ impl GtkMenu { // Connect delete for window. self.window.connect_delete_event( glib::clone!(@weak gd => @default-return Inhibit(false), move |_, _| { - power_down_callback(&gd).unwrap_or_else(|e| error!("Standard vm write power button failed: {:?}", e)); - Inhibit(false) + window_close_callback(&gd).unwrap_or_else(|e| error!("Standard vm shut down failed: {:?}", e)); + Inhibit(true) }), ); @@ -244,8 +247,13 @@ impl GtkMenu { /// Fixed the window size. fn power_down_callback(gd: &Rc>) -> Result<()> { - let power_button = gd.borrow().power_button.clone(); - power_button.write(1)?; + let borrowed_gd = gd.borrow(); + if borrowed_gd.powerdown_button.is_some() { + borrowed_gd.vm_powerdown(); + } else { + drop(borrowed_gd); + window_close_callback(gd)?; + } Ok(()) } @@ -347,3 +355,32 @@ fn zoom_fit_callback(gd: &Rc>) -> Result<()> { update_window_size(&gs)?; renew_image(&gs) } + +/// Close window. +fn window_close_callback(gd: &Rc>) -> Result<()> { + let borrowed_gd = gd.borrow(); + if get_run_stage() != VmRunningStage::Os || borrowed_gd.powerdown_button.is_none() { + let dialog = MessageDialog::new( + Some(&borrowed_gd.gtk_menu.window), + DialogFlags::DESTROY_WITH_PARENT, + MessageType::Question, + ButtonsType::YesNo, + "Forced shutdown may cause installation failure, blue screen, unusable and other abnormalities.", + ); + dialog.set_title("Please confirm whether to exit the virtual machine"); + let answer = dialog.run(); + // SAFETY: Dialog is created in the current function and can be guaranteed not to be empty. + unsafe { dialog.destroy() }; + if answer != gtk::ResponseType::Yes { + return Ok(()); + } + } + + if get_run_stage() == VmRunningStage::Os && borrowed_gd.powerdown_button.is_some() { + borrowed_gd.vm_powerdown(); + } else { + borrowed_gd.vm_shutdown(); + } + + Ok(()) +} diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index f48003a50..415ba52b7 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -195,7 +195,8 @@ pub(crate) struct GtkDisplay { gtk_menu: GtkMenu, scale_mode: Rc>, pagenum2ds: HashMap>>, - power_button: Arc, + powerdown_button: Option>, + shutdown_button: Option>, keysym2keycode: Rc>>, } @@ -217,7 +218,8 @@ impl GtkDisplay { gtk_menu, scale_mode, pagenum2ds: HashMap::new(), - power_button: gtk_cfg.power_button.clone(), + powerdown_button: gtk_cfg.powerdown_button.clone(), + shutdown_button: gtk_cfg.shutdown_button.clone(), keysym2keycode, } } @@ -291,6 +293,24 @@ impl GtkDisplay { Ok(()) } + + /// Gracefully Shutdown. + pub(crate) fn vm_powerdown(&self) { + if let Some(button) = &self.powerdown_button { + button + .write(1) + .unwrap_or_else(|e| error!("Vm power down failed: {:?}", e)); + } + } + + /// Forced Shutdown. + pub(crate) fn vm_shutdown(&self) { + if let Some(button) = &self.shutdown_button { + button + .write(1) + .unwrap_or_else(|e| error!("Vm shut down failed: {:?}", e)); + } + } } pub struct GtkDisplayScreen { @@ -432,7 +452,10 @@ struct GtkConfig { full_screen: bool, zoom_fit: bool, vm_name: String, - power_button: Arc, + /// Gracefully Shutdown. + powerdown_button: Option>, + /// Forced Shutdown. + shutdown_button: Option>, gtk_args: Vec, } @@ -442,7 +465,8 @@ pub fn gtk_display_init(ds_cfg: &DisplayConfig, ui_context: UiContext) -> Result full_screen: ds_cfg.full_screen, zoom_fit: ds_cfg.fix_size, vm_name: ui_context.vm_name, - power_button: ui_context.power_button, + powerdown_button: ui_context.power_button, + shutdown_button: ui_context.shutdown_req, gtk_args: vec![], }; let _handle = thread::Builder::new() -- Gitee From d6f646857e8625e60df201d033efc6b41e3ef5d2 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 19 Apr 2023 11:19:03 +0800 Subject: [PATCH 1018/2187] qmp: add package message add package message in the return of "query-version" command Signed-off-by: mayunlong --- machine_manager/src/qmp/mod.rs | 12 ++---------- machine_manager/src/qmp/qmp_schema.rs | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index e1f0acbe7..b7954b776 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -197,16 +197,8 @@ impl QmpGreeting { /// * `minor` - Minor version number. /// * `major` - Major version number. pub fn create_greeting(micro: u8, minor: u8, major: u8) -> Self { - let version_number = VersionNumber { - micro, - minor, - major, - }; + let version = Version::new(micro, minor, major); let cap: Vec = Default::default(); - let version = Version { - application: version_number, - package: "".to_string(), - }; let greeting = Greeting { version, capabilities: cap, @@ -609,7 +601,7 @@ mod tests { "minor": 0, "major": 5 }, - "package": "" + "package": "StratoVirt-2.2.0" }, "capabilities": [] } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index f8b98668c..bc6459af2 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1541,7 +1541,7 @@ impl Command for balloon { /// /// ```text /// -> { "execute": "query-version" } -/// <- {"return":{"package":"StratoVirt-0.3.0","qemu":{"major":4,"micro":0,"minor":1}}} +/// <- {"return":{"version":{"qemu":{"minor":1,"micro":0,"major":5},"package":"StratoVirt-2.2.0"},"capabilities":[]}} /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_version {} -- Gitee From 7ba60d2dd7bfd9bced8dd6f2add802570e423820 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Wed, 19 Apr 2023 22:30:37 +0800 Subject: [PATCH 1019/2187] virtio-gpu: fix memory leak bug Fixed a bug where images that were not on the screen were not released correctly. Signed-off-by: wubinfeng --- tests/mod_test/tests/virtio_gpu_test.rs | 26 +++++++++++++++++++++++++ virtio/src/device/gpu.rs | 21 +++++++++----------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index a2945d13e..e48df4281 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -318,11 +318,37 @@ fn resource_destroy_dfx() { gpu_cfg.max_hostmem = image_size; let (dpy, gpu) = set_up(&gpu_cfg); + // release resource which doesn't exist assert_eq!( VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, resource_unref(&gpu, VirtioGpuResourceUnref::new(D_RES_ID)).hdr_type ); + // release and check + // create resource first + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + // release it + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_unref(&gpu, VirtioGpuResourceUnref::new(D_RES_ID)).hdr_type + ); + // check if it release, expect can create again + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_create( + &gpu, + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + ) + .hdr_type + ); + tear_down(dpy, gpu); } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index c43b6ef8e..a3763f280 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -41,6 +41,7 @@ use ui::console::{ display_replace_surface, ConsoleType, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, }; +use ui::pixman::unref_pixman_image; use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; use util::byte_code::ByteCode; use util::loop_context::{ @@ -775,21 +776,17 @@ impl GpuIoHandler { fn resource_destroy(&mut self, res_index: usize) { let res = &mut self.resources_list[res_index]; - if res.scanouts_bitmask == 0 { - return; - } - - for i in 0..self.num_scanouts { - if (res.scanouts_bitmask & (1 << i)) != 0 { - let scanout = &mut self.scanouts[i as usize]; - res.scanouts_bitmask &= !(1 << i); - disable_scanout(scanout); + if res.scanouts_bitmask != 0 { + for i in 0..self.num_scanouts { + if (res.scanouts_bitmask & (1 << i)) != 0 { + let scanout = &mut self.scanouts[i as usize]; + res.scanouts_bitmask &= !(1 << i); + disable_scanout(scanout); + } } } - unsafe { - pixman_image_unref(res.pixman_image); - } + unref_pixman_image(res.pixman_image); self.used_hostmem -= res.host_mem; res.iov.clear(); } -- Gitee From de63f29f2f1195152c230971e56ebe0b386c92da Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 17 Apr 2023 21:36:19 +0800 Subject: [PATCH 1020/2187] usb: support usb keyboard and tablet hotplug Support usb keyboard and tablet hotplug. Signed-off-by: Yan Wang --- devices/src/usb/camera.rs | 30 ++++----- devices/src/usb/keyboard.rs | 16 +++-- devices/src/usb/mod.rs | 19 ++++++ devices/src/usb/storage.rs | 46 ++++++------- devices/src/usb/tablet.rs | 36 ++++++----- devices/src/usb/xhci/xhci_controller.rs | 85 ++++++++++++++++++------- devices/src/usb/xhci/xhci_pci.rs | 27 +++++++- machine/src/lib.rs | 55 ++++++++++++++-- machine/src/standard_vm/mod.rs | 63 ++++++++++++++++-- machine_manager/src/qmp/mod.rs | 13 ++++ machine_manager/src/qmp/qmp_schema.rs | 1 + pci/src/hotplug.rs | 2 +- pci/src/root_port.rs | 11 +--- ui/src/input.rs | 64 +++++++++++++------ 14 files changed, 346 insertions(+), 122 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 547dc628d..3e0322b85 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -440,21 +440,6 @@ impl UsbCamera { } } - pub fn realize(mut self) -> Result>> { - self.set_hostdev()?; - - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_HIGH; - let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); - self.usb_device - .init_descriptor(DESC_DEVICE_CAMERA.clone(), s)?; - self.usb_device - .init_device_qualifier_descriptor(DESC_DEVICE_QUALIFIER_CAMERA.clone())?; - let camera = Arc::new(Mutex::new(self)); - - Ok(camera) - } - fn set_hostdev(&mut self) -> Result<()> { match self.backend_type { CamBackendType::V4l2 => { @@ -490,6 +475,21 @@ impl Default for UsbCamera { } impl UsbDeviceOps for UsbCamera { + fn realize(mut self) -> Result>> { + self.set_hostdev()?; + + self.usb_device.reset_usb_endpoint(); + self.usb_device.speed = USB_SPEED_HIGH; + let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_CAMERA.clone(), s)?; + self.usb_device + .init_device_qualifier_descriptor(DESC_DEVICE_QUALIFIER_CAMERA.clone())?; + let camera = Arc::new(Mutex::new(self)); + + Ok(camera) + } + fn reset(&mut self) {} fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 0c71406ed..b3c19dcf7 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -27,7 +27,7 @@ use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use ui::input::{register_keyboard, KeyboardOpts}; +use ui::input::{register_keyboard, unregister_keyboard, KeyboardOpts}; /// Keyboard device descriptor static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { @@ -171,24 +171,30 @@ impl UsbKeyboard { cntlr: None, } } +} - pub fn realize(mut self) -> Result>> { +impl UsbDeviceOps for UsbKeyboard { + fn realize(mut self) -> Result>> { self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_FULL; let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); self.usb_device .init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; + let id = self.id.clone(); let kbd = Arc::new(Mutex::new(self)); let kbd_adapter = Arc::new(Mutex::new(UsbKeyboardAdapter { usb_kbd: kbd.clone(), })); - register_keyboard("UsbKeyboard", kbd_adapter); + register_keyboard(&id, kbd_adapter); Ok(kbd) } -} -impl UsbDeviceOps for UsbKeyboard { + fn unrealize(&mut self) -> Result<()> { + unregister_keyboard(&self.id.clone()); + Ok(()) + } + fn reset(&mut self) { info!("Keyboard device reset"); self.usb_device.remote_wakeup = 0; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 93f9f3f30..b9a249808 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -37,6 +37,7 @@ use util::aio::{mem_from_buf, mem_to_buf, Iovec}; use config::*; use descriptor::{UsbDescriptor, UsbDescriptorOps}; +use machine_manager::qmp::send_device_deleted_msg; use xhci::xhci_controller::{UsbPort, XhciDevice}; const USB_MAX_ENDPOINTS: u32 = 15; @@ -95,6 +96,8 @@ pub struct UsbDevice { pub ep_out: Vec, /// USB descriptor pub descriptor: UsbDescriptor, + /// The usb device id which is hot unplugged. + pub unplugged_id: Option, } impl UsbDevice { @@ -109,6 +112,7 @@ impl UsbDevice { data_buf: vec![0_u8; 4096], remote_wakeup: 0, descriptor: UsbDescriptor::new(), + unplugged_id: None, }; for i in 0..USB_MAX_ENDPOINTS as u8 { @@ -280,9 +284,24 @@ impl Default for UsbDevice { } } +impl Drop for UsbDevice { + fn drop(&mut self) { + if let Some(id) = &self.unplugged_id { + send_device_deleted_msg(id); + } + } +} + /// UsbDeviceOps is the interface for USB device. /// Include device handle attach/detach and the transfer between controller and device. pub trait UsbDeviceOps: Send + Sync { + /// Realize the USB device. + fn realize(self) -> Result>>; + + /// Unrealize the USB device. + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } /// Handle the attach ops when attach device to controller. fn handle_attach(&mut self) -> Result<()> { let usb_dev = self.get_mut_usb_device(); diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index b3c0511d1..404dba25e 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -330,29 +330,6 @@ impl UsbStorage { } } - pub fn realize(mut self) -> Result>> { - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_HIGH; - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); - self.usb_device - .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; - - let aio = Aio::new(Arc::new(aio_complete_cb), AioEngine::Off) - .with_context(|| format!("USB-storage {}: aio creation error!", self.id))?; - let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); - locked_scsi_dev.aio = Some(Arc::new(Mutex::new(aio))); - locked_scsi_dev.realize()?; - drop(locked_scsi_dev); - self.scsi_bus - .lock() - .unwrap() - .devices - .insert((0, 0), self.scsi_dev.clone()); - - let storage: Arc> = Arc::new(Mutex::new(self)); - Ok(storage) - } - fn handle_control_packet(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { match device_req.request_type { USB_ENDPOINT_OUT_REQUEST => { @@ -536,6 +513,29 @@ impl UsbStorage { } impl UsbDeviceOps for UsbStorage { + fn realize(mut self) -> Result>> { + self.usb_device.reset_usb_endpoint(); + self.usb_device.speed = USB_SPEED_HIGH; + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; + + let aio = Aio::new(Arc::new(aio_complete_cb), AioEngine::Off) + .with_context(|| format!("USB-storage {}: aio creation error!", self.id))?; + let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); + locked_scsi_dev.aio = Some(Arc::new(Mutex::new(aio))); + locked_scsi_dev.realize()?; + drop(locked_scsi_dev); + self.scsi_bus + .lock() + .unwrap() + .devices + .insert((0, 0), self.scsi_dev.clone()); + + let storage: Arc> = Arc::new(Mutex::new(self)); + Ok(storage) + } + fn reset(&mut self) { info!("Storage device reset"); self.usb_device.remote_wakeup = 0; diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 9ccbe42bf..b0b660183 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -28,7 +28,7 @@ use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use ui::input::{register_pointer, PointerOpts}; +use ui::input::{register_pointer, unregister_pointer, PointerOpts}; const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; @@ -128,20 +128,6 @@ impl UsbTablet { cntlr: None, } } - - pub fn realize(mut self) -> Result>> { - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_FULL; - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); - self.usb_device - .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; - let tablet = Arc::new(Mutex::new(self)); - let tablet_adapter = Arc::new(Mutex::new(UsbTabletAdapter { - tablet: tablet.clone(), - })); - register_pointer("UsbTablet", tablet_adapter); - Ok(tablet) - } } pub struct UsbTabletAdapter { @@ -176,6 +162,26 @@ impl PointerOpts for UsbTabletAdapter { } impl UsbDeviceOps for UsbTablet { + fn realize(mut self) -> Result>> { + self.usb_device.reset_usb_endpoint(); + self.usb_device.speed = USB_SPEED_FULL; + let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + self.usb_device + .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; + let id = self.id.clone(); + let tablet = Arc::new(Mutex::new(self)); + let tablet_adapter = Arc::new(Mutex::new(UsbTabletAdapter { + tablet: tablet.clone(), + })); + register_pointer(&id, tablet_adapter); + Ok(tablet) + } + + fn unrealize(&mut self) -> Result<()> { + unregister_pointer(&self.id.clone()); + Ok(()) + } + fn reset(&mut self) { info!("Tablet device reset"); self.usb_device.remote_wakeup = 0; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b8c02404d..df46a0643 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -35,6 +35,7 @@ use crate::usb::{ UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, }; +const INVALID_SLOT_ID: u32 = 0; pub const MAX_INTRS: u32 = 1; pub const MAX_SLOTS: u32 = 64; /// Endpoint state @@ -443,6 +444,7 @@ pub struct UsbPort { pub speed_mask: u32, pub dev: Option>>, pub used: bool, + pub slot_id: u32, } impl UsbPort { @@ -454,6 +456,7 @@ impl UsbPort { speed_mask: 0, dev: None, used: false, + slot_id: INVALID_SLOT_ID, } } @@ -762,7 +765,7 @@ impl XhciDevice { } for i in 0..self.usb_ports.len() { let port = self.usb_ports[i].clone(); - if let Err(e) = self.port_update(&port) { + if let Err(e) = self.port_update(&port, false) { error!("Failed to update port: {:?}", e); } } @@ -819,26 +822,28 @@ impl XhciDevice { } /// Update the xhci port status and then notify the driver. - pub fn port_update(&mut self, port: &Arc>) -> Result<()> { + pub fn port_update(&mut self, port: &Arc>, detach: bool) -> Result<()> { let mut locked_port = port.lock().unwrap(); locked_port.portsc = PORTSC_PP; let mut pls = PLS_RX_DETECT; - if let Some(dev) = &locked_port.dev { - let speed = dev.lock().unwrap().speed(); - locked_port.portsc |= PORTSC_CCS; - if speed == USB_SPEED_SUPER { - locked_port.portsc |= PORTSC_SPEED_SUPER; - locked_port.portsc |= PORTSC_PED; - pls = PLS_U0; - } else if speed == USB_SPEED_FULL { - locked_port.portsc |= PORTSC_SPEED_FULL; - pls = PLS_POLLING; - } else if speed == USB_SPEED_HIGH { - locked_port.portsc |= PORTSC_SPEED_HIGH; - pls = PLS_POLLING; - } else if speed == USB_SPEED_LOW { - locked_port.portsc |= PORTSC_SPEED_LOW; - pls = PLS_POLLING; + if !detach { + if let Some(dev) = &locked_port.dev { + let speed = dev.lock().unwrap().speed(); + locked_port.portsc |= PORTSC_CCS; + if speed == USB_SPEED_SUPER { + locked_port.portsc |= PORTSC_SPEED_SUPER; + locked_port.portsc |= PORTSC_PED; + pls = PLS_U0; + } else if speed == USB_SPEED_FULL { + locked_port.portsc |= PORTSC_SPEED_FULL; + pls = PLS_POLLING; + } else if speed == USB_SPEED_HIGH { + locked_port.portsc |= PORTSC_SPEED_HIGH; + pls = PLS_POLLING; + } else if speed == USB_SPEED_LOW { + locked_port.portsc |= PORTSC_SPEED_LOW; + pls = PLS_POLLING; + } } } locked_port.set_port_link_state(pls); @@ -995,6 +1000,20 @@ impl XhciDevice { Ok(TRBCCode::Success) } + pub fn detach_slot(&mut self, slot_id: u32) -> Result<()> { + if slot_id < 1 || slot_id > self.slots.len() as u32 { + bail!("Invalid slot id {} while detaching slot", slot_id); + } + for i in 1..=self.slots[(slot_id - 1) as usize].endpoints.len() as u32 { + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(i - 1) as usize]; + if epctx.enabled { + self.flush_ep_transfer(slot_id, i, TRBCCode::Invalid)?; + } + } + self.slots[(slot_id - 1) as usize].usb_port = None; + Ok(()) + } + fn address_device(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { let ictx = trb.parameter; ictx.checked_add(INPUT_CONTEXT_SIZE).with_context(|| { @@ -1027,10 +1046,7 @@ impl XhciDevice { error!("Failed to found usb port"); return Ok(TRBCCode::TrbError); }; - let lock_port = usb_port.lock().unwrap(); - let dev = if let Some(dev) = lock_port.dev.as_ref() { - dev - } else { + if usb_port.lock().unwrap().dev.is_none() { error!("No device found in usb port."); return Ok(TRBCCode::UsbTransactionError); }; @@ -1043,8 +1059,11 @@ impl XhciDevice { octx, DEVICE_CONTEXT_SIZE ) })?; + let mut locked_port = usb_port.lock().unwrap(); + locked_port.slot_id = slot_id; self.slots[(slot_id - 1) as usize].usb_port = Some(usb_port.clone()); self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; + let dev = locked_port.dev.as_ref().unwrap(); dev.lock().unwrap().reset(); if bsr { slot_ctx.dev_state = SLOT_DEFAULT << SLOT_STATE_SHIFT; @@ -1854,6 +1873,28 @@ impl XhciDevice { } None } + + pub fn discharge_usb_port(&mut self, port: &mut UsbPort) { + if port.used { + port.used = false; + port.dev = None; + port.slot_id = INVALID_SLOT_ID; + } + } + + pub fn find_usb_port_by_id(&mut self, id: &str) -> Option>> { + for port in &self.usb_ports { + let locked_port = port.lock().unwrap(); + if !locked_port.used || locked_port.dev.is_none() { + continue; + } + let dev = locked_port.dev.as_ref().unwrap(); + if dev.lock().unwrap().device_id() == id { + return Some(port.clone()); + } + } + None + } } fn usb_packet_status_to_trb_code(status: UsbPacketStatus) -> Result { diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index d5306c2df..becd8ab6c 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -64,7 +64,7 @@ const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; pub struct XhciPciDevice { pci_config: PciConfig, devfn: u8, - xhci: Arc>, + pub xhci: Arc>, dev_id: Arc, name: String, parent_bus: Weak>, @@ -146,7 +146,7 @@ impl XhciPciDevice { let usb_port = locked_xhci .assign_usb_port(dev) .with_context(|| "No available USB port.")?; - locked_xhci.port_update(&usb_port)?; + locked_xhci.port_update(&usb_port, false)?; let mut locked_dev = dev.lock().unwrap(); debug!( "Attach usb device: xhci port id {} device id {}", @@ -157,6 +157,29 @@ impl XhciPciDevice { locked_dev.set_controller(Arc::downgrade(&self.xhci)); Ok(()) } + + pub fn detach_device(&self, id: String) -> Result<()> { + let mut locked_xhci = self.xhci.lock().unwrap(); + let usb_port = locked_xhci.find_usb_port_by_id(&id); + if usb_port.is_none() { + bail!("Failed to detach device: id {} not found", id); + } + let usb_port = usb_port.unwrap(); + let slot_id = usb_port.lock().unwrap().slot_id; + locked_xhci.detach_slot(slot_id as u32)?; + locked_xhci.port_update(&usb_port, true)?; + + // Unrealize device and discharge usb port. + let mut locked_port = usb_port.lock().unwrap(); + let dev = locked_port.dev.as_ref().unwrap(); + let mut locked_dev = dev.lock().unwrap(); + locked_dev.get_mut_usb_device().unplugged_id = Some(id); + locked_dev.unrealize()?; + drop(locked_dev); + locked_xhci.discharge_usb_port(&mut locked_port); + + Ok(()) + } } impl PciDevOps for XhciPciDevice { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index cdeb02c7f..89c2b3adb 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -529,6 +529,23 @@ pub trait MachineOps { Ok(()) } + #[cfg(not(target_env = "musl"))] + fn check_id_existed_in_xhci(&mut self, id: &str) -> Result { + let vm_config = self.get_vm_config(); + let mut locked_vmconfig = vm_config.lock().unwrap(); + let parent_dev = self + .get_pci_dev_by_id_and_type(&mut locked_vmconfig, None, "nec-usb-xhci") + .with_context(|| "Can not find parent device from pci bus")?; + let locked_parent_dev = parent_dev.lock().unwrap(); + let xhci_pci = locked_parent_dev + .as_any() + .downcast_ref::() + .with_context(|| "PciDevOps can not downcast to XhciPciDevice")?; + let mut locked_xhci = xhci_pci.xhci.lock().unwrap(); + let port = locked_xhci.find_usb_port_by_id(id); + Ok(port.is_some()) + } + fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { @@ -539,6 +556,10 @@ pub trait MachineOps { if PciBus::find_attached_bus(&pci_host.lock().unwrap().root_bus, name).is_some() { bail!("Device id {} existed", name); } + #[cfg(not(target_env = "musl"))] + if self.check_id_existed_in_xhci(name).unwrap_or_default() { + bail!("Device id {} existed in xhci", name); + } } Ok(()) } @@ -1173,6 +1194,31 @@ pub trait MachineOps { Ok(()) } + /// Detach usb device from xhci controller. + /// + /// # Arguments + /// + /// * `vm_config` - VM configuration. + /// * `id` - id of the usb device. + #[cfg(not(target_env = "musl"))] + fn detach_usb_from_xhci_controller( + &mut self, + vm_config: &mut VmConfig, + id: String, + ) -> Result<()> { + let parent_dev = self + .get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci") + .with_context(|| "Can not find parent device from pci bus")?; + let locked_parent_dev = parent_dev.lock().unwrap(); + let xhci_pci = locked_parent_dev + .as_any() + .downcast_ref::() + .with_context(|| "PciDevOps can not downcast to XhciPciDevice")?; + xhci_pci.detach_device(id)?; + + Ok(()) + } + /// Add usb keyboard. /// /// # Arguments @@ -1186,8 +1232,7 @@ pub trait MachineOps { let kbd = keyboard .realize() .with_context(|| "Failed to realize usb keyboard device")?; - - self.attach_usb_to_xhci_controller(vm_config, kbd as Arc>)?; + self.attach_usb_to_xhci_controller(vm_config, kbd)?; Ok(()) } @@ -1205,7 +1250,7 @@ pub trait MachineOps { .realize() .with_context(|| "Failed to realize usb tablet device")?; - self.attach_usb_to_xhci_controller(vm_config, tbt as Arc>)?; + self.attach_usb_to_xhci_controller(vm_config, tbt)?; Ok(()) } @@ -1221,7 +1266,7 @@ pub trait MachineOps { let camera = UsbCamera::new(device_cfg); let camera = camera.realize()?; - self.attach_usb_to_xhci_controller(vm_config, camera as Arc>)?; + self.attach_usb_to_xhci_controller(vm_config, camera)?; Ok(()) } @@ -1239,7 +1284,7 @@ pub trait MachineOps { .realize() .with_context(|| "Failed to realize usb storage device")?; - self.attach_usb_to_xhci_controller(vm_config, stg as Arc>)?; + self.attach_usb_to_xhci_controller(vm_config, stg)?; Ok(()) } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 94f5ab5fa..a808087cd 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -65,7 +65,7 @@ use machine_manager::config::{ use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::MigrationManager; -use pci::hotplug::{handle_plug, handle_unplug_request}; +use pci::hotplug::{handle_plug, handle_unplug_pci_request}; use pci::PciBus; use util::byte_code::ByteCode; use virtio::{ @@ -951,6 +951,36 @@ impl StdMachine { Ok(()) } + #[cfg(not(target_env = "musl"))] + fn plug_usb_device(&mut self, args: &qmp_schema::DeviceAddArgument) -> Result<()> { + let driver = args.driver.as_str(); + let vm_config = self.get_vm_config(); + let mut locked_vmconfig = vm_config.lock().unwrap(); + let cfg_args = format!("id={}", args.id); + match driver { + "usb-kbd" => { + self.add_usb_keyboard(&mut locked_vmconfig, &cfg_args)?; + } + "usb-tablet" => { + self.add_usb_tablet(&mut locked_vmconfig, &cfg_args)?; + } + _ => { + bail!("Invalid usb device driver '{}'", driver); + } + }; + + Ok(()) + } + + #[cfg(not(target_env = "musl"))] + fn handle_unplug_usb_request(&mut self, id: String) -> Result<()> { + let vm_config = self.get_vm_config(); + let mut locked_vmconfig = vm_config.lock().unwrap(); + self.detach_usb_from_xhci_controller(&mut locked_vmconfig, id)?; + + Ok(()) + } + fn plug_vfio_pci_device( &mut self, bdf: &PciBdf, @@ -1145,6 +1175,17 @@ impl DeviceInterface for StdMachine { ); } } + #[cfg(not(target_env = "musl"))] + "usb-kbd" | "usb-tablet" => { + if let Err(e) = self.plug_usb_device(args.as_ref()) { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + return Response::create_empty_response(); + } _ => { let err_str = format!("Failed to add device: Driver {} is not support", driver); return Response::create_error_response( @@ -1194,7 +1235,7 @@ impl DeviceInterface for StdMachine { let locked_pci_host = pci_host.lock().unwrap(); if let Some((bus, dev)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &device_id) { - match handle_unplug_request(&bus, &dev) { + return match handle_unplug_pci_request(&bus, &dev) { Ok(()) => { let locked_dev = dev.lock().unwrap(); let dev_id = locked_dev.name(); @@ -1210,8 +1251,22 @@ impl DeviceInterface for StdMachine { qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, ), - } - } else { + }; + } + drop(locked_pci_host); + + // The device is not a pci device, assume it is a usb device. + #[cfg(not(target_env = "musl"))] + return match self.handle_unplug_usb_request(device_id) { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + }; + + #[cfg(target_env = "musl")] + { let err_str = format!("Failed to remove device: id {} not found", &device_id); Response::create_error_response(qmp_schema::QmpErrorClass::GenericError(err_str), None) } diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index e1f0acbe7..8aebbace2 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -590,6 +590,19 @@ impl QmpChannel { } } +/// Send device deleted message to qmp client. +pub fn send_device_deleted_msg(id: &str) { + if QmpChannel::is_connected() { + let deleted_event = schema::DeviceDeleted { + device: Some(id.to_string()), + path: format!("/machine/peripheral/{}", id), + }; + event!(DeviceDeleted; deleted_event); + } else { + warn!("Qmp channel is not connected while sending device deleted message"); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index f8b98668c..a360ab2f8 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -601,6 +601,7 @@ pub struct device_add { pub sysfsdev: Option, #[serde(rename = "queue-size")] pub queue_size: Option, + pub port: Option, } pub type DeviceAddArgument = device_add; diff --git a/pci/src/hotplug.rs b/pci/src/hotplug.rs index 5c2086aca..87658f6bd 100644 --- a/pci/src/hotplug.rs +++ b/pci/src/hotplug.rs @@ -63,7 +63,7 @@ pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> /// Return Error if /// * No hot plug controller found. /// * Device unplug request failed. -pub fn handle_unplug_request( +pub fn handle_unplug_pci_request( bus: &Arc>, dev: &Arc>, ) -> Result<()> { diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 018646033..ab2470ef3 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -16,8 +16,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::Region; use anyhow::{anyhow, bail, Context, Result}; use log::{error, info}; -use machine_manager::event; -use machine_manager::qmp::{qmp_schema as schema, QmpChannel}; +use machine_manager::qmp::send_device_deleted_msg; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; @@ -209,13 +208,7 @@ impl RootPort { info!("Device {} unplug from {}", locked_dev.name(), self.name); // Send QMP event for successful hot unplugging. - if QmpChannel::is_connected() { - let device_del = schema::DeviceDeleted { - device: Some(locked_dev.name()), - path: format!("/machine/peripheral/{}", &locked_dev.name()), - }; - event!(DeviceDeleted; device_del); - } + send_device_deleted_msg(&locked_dev.name()); } self.sec_bus.lock().unwrap().devices.clear(); } diff --git a/ui/src/input.rs b/ui/src/input.rs index 9cc8e1344..6763637ab 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -173,46 +173,60 @@ impl KeyBoardState { } #[derive(Default)] struct Inputs { - active_kbd: Option, - active_tablet: Option, + kbd_ids: Vec, kbd_lists: HashMap>>, + tablet_ids: Vec, tablet_lists: HashMap>>, } impl Inputs { fn register_kbd(&mut self, device: &str, kbd: Arc>) { - if self.active_kbd.is_none() { - self.active_kbd = Some(device.to_string()); - } - + self.kbd_ids.insert(0, device.to_string()); self.kbd_lists.insert(device.to_string(), kbd); } - fn register_mouse(&mut self, device: &str, tablet: Arc>) { - if self.active_tablet.is_none() { - self.active_tablet = Some(device.to_string()); + fn unregister_kbd(&mut self, device: &str) { + self.kbd_lists.remove(&device.to_string()); + let len = self.kbd_ids.len(); + for i in 0..len { + if self.kbd_ids[i] == device { + self.kbd_ids.remove(i); + break; + } } + } + fn register_mouse(&mut self, device: &str, tablet: Arc>) { + self.tablet_ids.insert(0, device.to_string()); self.tablet_lists.insert(device.to_string(), tablet); } - fn get_active_kbd(&mut self) -> Option>> { - match &self.active_kbd { - Some(active_kbd) => { - let kbd = self.kbd_lists.get(active_kbd)?.clone(); - Some(kbd) + fn unregister_mouse(&mut self, device: &str) { + self.tablet_lists.remove(&device.to_string()); + let len = self.tablet_ids.len(); + for i in 0..len { + if self.tablet_ids[i] == device { + self.tablet_ids.remove(i); + break; } - None => None, + } + } + + fn get_active_kbd(&mut self) -> Option>> { + if !self.kbd_ids.is_empty() { + let kbd = self.kbd_lists.get(&self.kbd_ids[0])?.clone(); + Some(kbd) + } else { + None } } fn get_active_mouse(&mut self) -> Option>> { - match &self.active_tablet { - Some(active_mouse) => { - let mouse = self.tablet_lists.get(active_mouse)?.clone(); - Some(mouse) - } - None => None, + if !self.tablet_ids.is_empty() { + let mouse = self.tablet_lists.get(&self.tablet_ids[0])?.clone(); + Some(mouse) + } else { + None } } } @@ -221,10 +235,18 @@ pub fn register_keyboard(device: &str, kbd: Arc>) { INPUTS.lock().unwrap().register_kbd(device, kbd); } +pub fn unregister_keyboard(device: &str) { + INPUTS.lock().unwrap().unregister_kbd(device); +} + pub fn register_pointer(device: &str, tablet: Arc>) { INPUTS.lock().unwrap().register_mouse(device, tablet); } +pub fn unregister_pointer(device: &str) { + INPUTS.lock().unwrap().unregister_mouse(device); +} + pub fn key_event(keycode: u16, down: bool) -> Result<()> { let kbd = INPUTS.lock().unwrap().get_active_kbd(); if let Some(k) = kbd { -- Gitee From 7e9b7f32997e952114a257cf8be323862b7d5062 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 21 Apr 2023 09:40:05 +0800 Subject: [PATCH 1021/2187] usb: ignore unimportant logs Ignore the error logs printed when the devices is not activated. Signed-off-by: zhouli57 --- devices/src/usb/hid.rs | 4 ++-- devices/src/usb/xhci/xhci_controller.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 97944c11d..ac6fce7ab 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -12,7 +12,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use log::{debug, error, warn}; +use log::{debug, error}; use super::config::*; use super::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; @@ -457,7 +457,7 @@ impl Hid { match device_req.request { HID_SET_REPORT => match self.kind { HidType::Keyboard => { - warn!("Keyboard set report not implemented"); + debug!("Keyboard set report not implemented"); } _ => { error!("Unsupported to set report"); diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index df46a0643..998338b67 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1385,7 +1385,7 @@ impl XhciDevice { return Ok(TRBCCode::ContextStateError); } if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Stopped)? > 0 { - warn!("endpoint stop when xfers running!"); + debug!("endpoint stop when xfers running!"); } self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].set_state(EP_STOPPED)?; Ok(TRBCCode::Success) @@ -1826,6 +1826,10 @@ impl XhciDevice { /// Used for device to wakeup endpoint pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &UsbEndpoint) -> Result<()> { + if slot_id == INVALID_SLOT_ID { + debug!("Invalid slot id, maybe device not activated."); + return Ok(()); + } let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); self.kick_endpoint(slot_id, ep_id as u32)?; Ok(()) -- Gitee From e2ae5114ae362044d5f230b3f55ae97ff4c5afbf Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 22 Apr 2023 15:58:40 +0800 Subject: [PATCH 1022/2187] ramfb: Automatically enter into windows intallation instead of manually pressing key Add an install parameter of ramfb to decide if automatic enterring windows is needed. The install parameter is only valid when the VM enters windows for the first time. Signed-off-by: Jinhao Gao --- devices/src/legacy/ramfb.rs | 63 ++++++++++++++++++++++---- docs/config_guidebook.md | 14 ++++++ machine/src/lib.rs | 4 +- machine/src/standard_vm/aarch64/mod.rs | 9 ++-- machine_manager/src/config/mod.rs | 2 + machine_manager/src/config/ramfb.rs | 23 ++++++++++ ui/src/console.rs | 5 +- ui/src/input.rs | 1 + util/src/leak_bucket.rs | 6 ++- util/src/loop_context.rs | 11 ++--- virtio/src/vhost/user/client.rs | 4 +- 11 files changed, 114 insertions(+), 28 deletions(-) create mode 100644 machine_manager/src/config/ramfb.rs diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 24cb8f46c..536623b83 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -10,25 +10,34 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::mem::size_of; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, Weak}; +use std::time::Duration; + +use anyhow::Context; +use drm_fourcc::DrmFourcc; +use log::error; + use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; use crate::legacy::Result; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; -use anyhow::Context; -use drm_fourcc::DrmFourcc; -use log::error; -use std::mem::size_of; -use std::sync::{Arc, Mutex, Weak}; +use machine_manager::event_loop::EventLoop; use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; use ui::console::{ console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, DisplaySurface, HardWareOperations, }; +use ui::input::{key_event, KEYCODE_RET}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; const HEIGHT_MAX: u32 = 12_000; +const INSTALL_CHECK_INTERVEL_MS: u64 = 500; +const INSTALL_RELEASE_INTERVEL_MS: u64 = 200; +const INSTALL_PRESS_INTERVEL_MS: u64 = 100; #[repr(packed)] struct RamfbCfg { @@ -45,6 +54,7 @@ pub struct RamfbState { pub surface: Option, pub con: Option>>, sys_mem: Arc, + install: Arc, } // SAFETY: The type of image, the field of the struct DisplaySurface @@ -56,13 +66,14 @@ unsafe impl Sync for RamfbState {} unsafe impl Send for RamfbState {} impl RamfbState { - pub fn new(sys_mem: Arc) -> Self { + pub fn new(sys_mem: Arc, install: bool) -> Self { let ramfb_opts = Arc::new(RamfbInterface {}); let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); Self { surface: None, con, sys_mem, + install: Arc::new(AtomicBool::new(install)), } } @@ -130,6 +141,8 @@ impl RamfbState { } self.surface = Some(ds); + + set_press_event(self.install.clone(), fb_addr as *const u8); } fn reset_ramfb_state(&mut self) { @@ -209,9 +222,9 @@ pub struct Ramfb { } impl Ramfb { - pub fn new(sys_mem: Arc) -> Self { + pub fn new(sys_mem: Arc, install: bool) -> Self { Ramfb { - ramfb_state: RamfbState::new(sys_mem), + ramfb_state: RamfbState::new(sys_mem, install), } } @@ -248,3 +261,37 @@ impl AmlBuilder for Ramfb { Vec::new() } } + +fn set_press_event(install: Arc, data: *const u8) { + // SAFETY: data is the raw pointer of framebuffer. EDKII has malloc the memory of + // the framebuffer. So dereference the data is safe. + let black_screen = + unsafe { !data.is_null() && *data == 0 && *data.offset(1) == 0 && *data.offset(2) == 0 }; + if install.load(Ordering::Acquire) && black_screen { + let set_press_func = Box::new(move || { + set_press_event(install.clone(), data); + }); + let press_func = Box::new(move || { + key_event(KEYCODE_RET, true) + .unwrap_or_else(|e| error!("Ramfb couldn't press return key: {:?}", e)); + }); + let release_func = Box::new(move || { + key_event(KEYCODE_RET, false) + .unwrap_or_else(|e| error!("Ramfb couldn't release return key: {:?}.", e)); + }); + + if let Some(ctx) = EventLoop::get_ctx(None) { + ctx.delay_call( + set_press_func, + Duration::from_millis(INSTALL_CHECK_INTERVEL_MS), + ); + ctx.delay_call(press_func, Duration::from_millis(INSTALL_PRESS_INTERVEL_MS)); + ctx.delay_call( + release_func, + Duration::from_millis(INSTALL_RELEASE_INTERVEL_MS), + ); + } + } else { + install.store(false, Ordering::Release); + } +} diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index f84f5678f..42a465947 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -990,6 +990,20 @@ Sample Configuration: -object memory-backend-ram,id=,share=on,size=2M ``` +### 2.20 ramfb +Ramfb is a simple display device. It is used in the Windows system on aarch64. + +Two properties are supported for ramfb device. +* id: unique device id. +* install: when install the Windows system, setting true will automatically press enter key to skip the stage which needs to manually press any key boot from cd or dvd. + +Sample Configuration: +```shell +-device ramfb,id=[,install=true|false] +``` + +Note: Only supported on aarch64. + ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 89c2b3adb..461b4dc9c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1397,7 +1397,7 @@ pub trait MachineOps { } #[cfg(not(target_env = "musl"))] "ramfb" => { - self.add_ramfb()?; + self.add_ramfb(cfg_args)?; } "pcie-demo-dev" => { self.add_demo_dev(vm_config, cfg_args)?; @@ -1419,7 +1419,7 @@ pub trait MachineOps { bail!("Pflash device is not supported!"); } - fn add_ramfb(&mut self) -> Result<()> { + fn add_ramfb(&mut self, _cfg_args: &str) -> Result<()> { bail!("ramfb device is not supported!"); } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 926fda5ba..c6d411f18 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -52,8 +52,8 @@ use devices::legacy::{ use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, - NumaNodes, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, parse_ramfb, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, + NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -643,12 +643,13 @@ impl MachineOps for StdMachine { } #[cfg(not(target_env = "musl"))] - fn add_ramfb(&mut self) -> Result<()> { + fn add_ramfb(&mut self, cfg_args: &str) -> Result<()> { + let install = parse_ramfb(cfg_args)?; let fwcfg_dev = self .get_fwcfg_dev() .with_context(|| "Ramfb device must be used UEFI to boot, please add pflash devices")?; let sys_mem = self.get_sys_mem(); - let mut ramfb = Ramfb::new(sys_mem.clone()); + let mut ramfb = Ramfb::new(sys_mem.clone(), install); ramfb.ramfb_state.setup(&fwcfg_dev)?; ramfb.realize(&mut self.sysbus)?; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 7b500e769..b347be647 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -25,6 +25,7 @@ pub use machine_config::*; pub use network::*; pub use numa::*; pub use pci::*; +pub use ramfb::*; pub use rng::*; pub use sasl_auth::*; pub use scsi::*; @@ -48,6 +49,7 @@ mod machine_config; mod network; mod numa; mod pci; +mod ramfb; mod rng; mod sasl_auth; pub mod scream; diff --git a/machine_manager/src/config/ramfb.rs b/machine_manager/src/config/ramfb.rs new file mode 100644 index 000000000..7eb5c5e19 --- /dev/null +++ b/machine_manager/src/config/ramfb.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::config::CmdParser; +use anyhow::Result; + +pub fn parse_ramfb(cfg_args: &str) -> Result { + let mut cmd_parser = CmdParser::new("ramfb"); + cmd_parser.push("").push("install").push("id"); + cmd_parser.parse(cfg_args)?; + + let install = cmd_parser.get_value::("install")?.unwrap_or(false); + Ok(install) +} diff --git a/ui/src/console.rs b/ui/src/console.rs index 7e934668f..aada6c9b4 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -21,6 +21,7 @@ use once_cell::sync::Lazy; use std::{ cmp, ptr, sync::{Arc, Mutex, Weak}, + time::Duration, }; use util::pixman::{pixman_format_code_t, pixman_image_t}; @@ -48,8 +49,6 @@ pub const DISPLAY_UPDATE_INTERVAL_DEFAULT: u64 = 30; pub const DISPLAY_UPDATE_INTERVAL_INC: u64 = 50; /// Maximum refresh interval in ms. pub const DISPLAY_UPDATE_INTERVAL_MAX: u64 = 3_000; -/// Millisecond to nanosecond. -pub const MILLI_PER_SEC: u64 = 1_000_000; pub enum ConsoleType { Graphic, @@ -280,7 +279,7 @@ pub fn setup_refresh(update_interval: u64) { if update_interval != 0 { if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.delay_call(func, update_interval * MILLI_PER_SEC); + ctx.delay_call(func, Duration::from_millis(update_interval)); } } } diff --git a/ui/src/input.rs b/ui/src/input.rs index 6763637ab..729b1c89e 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -34,6 +34,7 @@ const BIT_PER_BYTE: u32 = 8; pub const KEYCODE_1: u16 = 2; pub const KEYCODE_9: u16 = 10; const KEYCODE_CTRL: u16 = 29; +pub const KEYCODE_RET: u16 = 38; const KEYCODE_SHIFT: u16 = 42; const KEYCODE_SHIFT_R: u16 = 54; const KEYCODE_ALT: u16 = 56; diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index e355cae90..6691d1f7a 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -13,7 +13,7 @@ /// We use Leaky Bucket Algorithm to limit iops of block device and qmp. use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::Arc; -use std::time::Instant; +use std::time::{Duration, Instant}; use log::error; use vmm_sys_util::eventfd::EventFd; @@ -93,7 +93,9 @@ impl LeakBucket { loop_context.delay_call( func, - (self.level - self.capacity) * NANOSECONDS_PER_SECOND / self.capacity, + Duration::from_nanos( + (self.level - self.capacity) * NANOSECONDS_PER_SECOND / self.capacity, + ), ); self.timer_started = true; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 229f09c5e..de2065e42 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -23,7 +23,6 @@ use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; use crate::test_helper::{get_test_time, is_test_enabled}; -use crate::time::NANOSECONDS_PER_SECOND; use crate::UtilError; use anyhow::{anyhow, Context, Result}; use std::fmt; @@ -178,10 +177,8 @@ impl Timer { /// /// * `func` - the function will be called later. /// * `nsec` - delay time in nanosecond. - pub fn new(func: Box, nsec: u64) -> Self { - let secs = nsec / NANOSECONDS_PER_SECOND; - let nsecs = (nsec % NANOSECONDS_PER_SECOND) as u32; - let expire_time = get_current_time() + Duration::new(secs, nsecs); + pub fn new(func: Box, delay: Duration) -> Self { + let expire_time = get_current_time() + delay; Timer { func, expire_time } } @@ -508,8 +505,8 @@ impl EventLoopContext { /// /// * `func` - the function will be called later. /// * `nsec` - delay time in nanoseconds. - pub fn delay_call(&mut self, func: Box, nsec: u64) { - let timer = Timer::new(func, nsec); + pub fn delay_call(&mut self, func: Box, delay: Duration) { + let timer = Timer::new(func, delay); // insert in order of expire_time let mut timers = self.timers.lock().unwrap(); diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 887b8b2a3..1f5b74733 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -16,6 +16,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::rc::Rc; use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; +use std::time::Duration; use anyhow::{bail, Context, Result}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -28,7 +29,6 @@ use machine_manager::event_loop::{register_event_helper, unregister_event_helper use util::loop_context::{ gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::time::NANOSECONDS_PER_SECOND; use util::unix::do_mmap; use super::super::VhostOps; @@ -114,7 +114,7 @@ fn vhost_user_reconnect(client: &Arc>) { { if let Some(ctx) = EventLoop::get_ctx(None) { // Default reconnecting time: 3s. - ctx.delay_call(func, 3 * NANOSECONDS_PER_SECOND); + ctx.delay_call(func, Duration::from_secs(3)); } else { error!("Failed to get ctx to delay vhost-user reconnecting"); } -- Gitee From 453232eeb64371844521b845c07283f6e2e925aa Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 22 Apr 2023 21:08:49 +0800 Subject: [PATCH 1023/2187] bugfix: the VM pause resume stuck After the VM is paused, obtain the timer in time and set the timer before the VM resumes. Otherwise, after the host is hibernated and woken up, the VM freezes because the timer interval is too long. Signed-off-by: Mingwang Li --- cpu/src/aarch64/mod.rs | 32 +++++++++++++++++++++++++++++++- cpu/src/lib.rs | 12 ++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index ab42e2e24..e7ab9618c 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -50,9 +50,14 @@ const PSR_D_BIT: u64 = 0x0000_0200; // MPIDR is Multiprocessor Affinity Register // [40:63] bit reserved on AArch64 Architecture, const UNINIT_MPIDR: u64 = 0xFFFF_FF00_0000_0000; -// MPIDR - Multiprocessor Affinity Register. + // See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h#L130 +// MPIDR - Multiprocessor Affinity Register. const SYS_MPIDR_EL1: u64 = 0x6030_0000_0013_c005; +// Counter-timer Virtual Count register: Due to the API interface problem, the encode of +// this register is SYS_CNTV_CVAL_EL0. +const SYS_CNTV_CNT_EL0: u64 = 0x6030_0000_0013_df1a; + const KVM_MAX_CPREG_ENTRIES: usize = 500; /// Interrupt ID for pmu. @@ -112,6 +117,8 @@ pub struct ArmCPUState { cpreg_list: [CpregListEntry; 512], /// Vcpu features features: ArmCPUFeatures, + /// Virtual timer count. + vtimer_cnt: u64, } impl ArmCPUState { @@ -257,6 +264,29 @@ impl ArmCPUState { self.core_regs.regs.pc = boot_config.boot_pc; } } + + /// Set virtual timer Count register value to `Kvm` with `ArmCPUState`. + /// + /// # Arguments + /// + /// * `vcpu_fd` - Vcpu file descriptor in kvm. + pub fn set_virtual_timer_cnt(&self, vcpu_fd: &Arc) -> Result<()> { + vcpu_fd + .set_one_reg(SYS_CNTV_CNT_EL0, self.vtimer_cnt as u128) + .with_context(|| "Failed to set virtual timer count") + } + + /// Get virtual timer Count register value from `Kvm` with `ArmCPUState`. + /// + /// # Arguments + /// + /// * `vcpu_fd` - Vcpu file descriptor in kvm. + pub fn get_virtual_timer_cnt(&mut self, vcpu_fd: &Arc) -> Result<()> { + self.vtimer_cnt = vcpu_fd + .get_one_reg(SYS_CNTV_CNT_EL0) + .with_context(|| "Failed to get virtual timer count")? as u64; + Ok(()) + } } impl CPU { diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 9b10e7196..ffec6eec9 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -302,6 +302,12 @@ impl CPUInterface for CPU { } fn resume(&self) -> Result<()> { + #[cfg(target_arch = "aarch64")] + self.arch() + .lock() + .unwrap() + .set_virtual_timer_cnt(self.fd())?; + let (cpu_state_locked, cvar) = &*self.state; let mut cpu_state = cpu_state_locked.lock().unwrap(); if *cpu_state == CpuLifecycleState::Running { @@ -400,6 +406,12 @@ impl CPUInterface for CPU { } } + #[cfg(target_arch = "aarch64")] + self.arch() + .lock() + .unwrap() + .get_virtual_timer_cnt(self.fd())?; + Ok(()) } -- Gitee From 9cfda3f0f0b603b6f4d20c875db48744b4b296e2 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 26 Apr 2023 16:52:43 +0800 Subject: [PATCH 1024/2187] bugfix: windows shutdown freeezes When a windows VM is shutdown, a command is issued to stop the endpoint corresponding to the USB device. However, the VM does not receive a response to the command because the information reported by the XHCI. As a result, the VM stops and freezes for several seconds. Signed-off-by: Mingwang Li --- devices/src/usb/xhci/xhci_controller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 998338b67..a1872ae94 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1813,7 +1813,7 @@ impl XhciDevice { if xfer.running_retry { if report != TRBCCode::Invalid { xfer.status = report; - xfer.submit_transfer()?; + xfer.report_transfer_error()?; } let epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(ep_id - 1) as usize]; epctx.retry = None; -- Gitee From 3bba7302c9abfbbfbcf1cfbc60e101ce76189fb6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 23 Apr 2023 20:16:35 +0800 Subject: [PATCH 1025/2187] ui: sync cap lock and num lock Sync the cap lock and num lock state to guest. Signed-off-by: zhouli57 --- devices/src/usb/hid.rs | 8 ++- devices/src/usb/mod.rs | 1 + ui/src/gtk/draw.rs | 6 ++- ui/src/input.rs | 112 ++++++++++++++++++++++++++++++++++++++-- ui/src/vnc/client_io.rs | 17 +++--- ui/src/vnc/mod.rs | 10 ---- ui/src/vnc/server_io.rs | 5 -- 7 files changed, 129 insertions(+), 30 deletions(-) diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index ac6fce7ab..62d6b8b24 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -14,6 +14,8 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use log::{debug, error}; +use ui::input::set_kbd_led_state; + use super::config::*; use super::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; @@ -366,7 +368,7 @@ impl Hid { self.do_interface_class_in_request(packet, device_req, data); } USB_INTERFACE_CLASS_OUT_REQUEST => { - self.do_interface_class_out_request(packet, device_req); + self.do_interface_class_out_request(packet, device_req, data); } _ => { error!("Unhandled request {}", device_req.request); @@ -453,11 +455,13 @@ impl Hid { &mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest, + data: &[u8], ) { match device_req.request { HID_SET_REPORT => match self.kind { HidType::Keyboard => { - debug!("Keyboard set report not implemented"); + debug!("Keyboard set report {}", data[0]); + set_kbd_led_state(data[0]); } _ => { error!("Unsupported to set report"); diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index b9a249808..ae291ab8a 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -18,6 +18,7 @@ pub use error::UsbError; pub mod camera; pub mod config; mod descriptor; +#[cfg(not(target_env = "musl"))] pub mod hid; #[cfg(not(target_env = "musl"))] diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index c3c327faa..4adcd715a 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -26,8 +26,8 @@ use log::error; use crate::{ gtk::GtkDisplayScreen, input::{ - self, point_event, press_mouse, ABS_MAX, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, - INPUT_POINT_RIGHT, + self, point_event, press_mouse, update_key_state, ABS_MAX, INPUT_POINT_LEFT, + INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, }, }; @@ -96,11 +96,13 @@ fn da_key_callback( press: bool, ) -> Result<()> { let keysym2keycode = gs.borrow().keysym2keycode.clone(); + let org_key_value = key_event.keyval().into_glib() as i32; let key_value: u16 = key_event.keyval().to_lower().into_glib() as u16; let keycode: u16 = match keysym2keycode.borrow().get(&(key_value as u16)) { Some(k) => *k, None => 0, }; + update_key_state(press, org_key_value, keycode)?; input::key_event(keycode, press)?; Ok(()) } diff --git a/ui/src/input.rs b/ui/src/input.rs index 1397eef23..eecbd8c2e 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use anyhow::Result; +use log::debug; use once_cell::sync::Lazy; use std::{ collections::HashMap, @@ -18,6 +19,8 @@ use std::{ }; use util::bitmap::Bitmap; +use crate::data::keycode::KEYSYM2KEYCODE; + // Logical window size for mouse. pub const ABS_MAX: u64 = 0x7fff; // Event type of Point. @@ -28,6 +31,8 @@ pub const INPUT_POINT_RIGHT: u8 = 0x04; pub const ASCII_A: i32 = 65; pub const ASCII_Z: i32 = 90; pub const UPPERCASE_TO_LOWERCASE: i32 = 32; +const ASCII_A_LOWCASE: i32 = 97; +const ASCII_Z_LOWCASE: i32 = 122; const BIT_PER_BYTE: u32 = 8; // Keycode. @@ -42,9 +47,22 @@ const KEYCODE_CAPS_LOCK: u16 = 58; const KEYCODE_NUM_LOCK: u16 = 69; const KEYCODE_CTRL_R: u16 = 157; const KEYCODE_ALT_R: u16 = 184; +const KEYPAD_1: u16 = 0xffb0; +const KEYPAD_9: u16 = 0xffb9; +const KEYPAD_SEPARATOR: u16 = 0xffac; +const KEYPAD_DECIMAL: u16 = 0xffae; +const KEYCODE_KP_7: u16 = 0x47; +const KEYCODE_KP_DECIMAL: u16 = 0x53; +// Led (HID) +pub const NUM_LOCK_LED: u8 = 0x1; +pub const CAPS_LOCK_LED: u8 = 0x2; +pub const SCROLL_LOCK_LED: u8 = 0x4; static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); +static LED_STATE: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new(LedState::default()))); + // Keyboard Modifier State pub enum KeyboardModifier { KeyModNone = 0, @@ -67,6 +85,16 @@ pub struct KeyBoardState { pub keymods: Bitmap, } +impl Default for KeyBoardState { + fn default() -> Self { + let mut max_keycode: u16 = 0; + for &(_, v) in KEYSYM2KEYCODE.iter() { + max_keycode = std::cmp::max(max_keycode, v); + } + KeyBoardState::new(max_keycode as usize) + } +} + impl KeyBoardState { pub fn new(key_num: usize) -> Self { Self { @@ -78,7 +106,7 @@ impl KeyBoardState { } /// Get the corresponding keyboard modifier. - pub fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool { + fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool { match self.keymods.contain(key_mod as usize) { Ok(res) => res, Err(_e) => false, @@ -86,12 +114,12 @@ impl KeyBoardState { } /// Reset all keyboard modifier state. - pub fn keyboard_state_reset(&mut self) { + fn keyboard_state_reset(&mut self) { self.keymods.clear_all(); } /// Record the press and up state in the keyboard. - pub fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> { + fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> { // Key is not pressed and the incoming key action is up. if !down && !self.keystate.contain(keycode as usize)? { return Ok(()); @@ -172,12 +200,19 @@ impl KeyBoardState { Ok(()) } } + +#[derive(Default)] +struct LedState { + kbd_led: u8, +} + #[derive(Default)] struct Inputs { kbd_ids: Vec, kbd_lists: HashMap>>, tablet_ids: Vec, tablet_lists: HashMap>>, + keyboard_state: KeyBoardState, } impl Inputs { @@ -230,6 +265,19 @@ impl Inputs { None } } + + fn press_key(&mut self, keycode: u16) -> Result<()> { + self.keyboard_state.keyboard_state_update(keycode, true)?; + let kbd = self.get_active_kbd(); + if let Some(k) = kbd.as_ref() { + k.lock().unwrap().do_key_event(keycode, true)?; + } + self.keyboard_state.keyboard_state_update(keycode, false)?; + if let Some(k) = kbd.as_ref() { + k.lock().unwrap().do_key_event(keycode, false)?; + } + Ok(()) + } } pub fn register_keyboard(device: &str, kbd: Arc>) { @@ -275,6 +323,64 @@ pub fn point_event(button: u32, x: u32, y: u32) -> Result<()> { Ok(()) } +/// 1. Keep the key state in keyboard_state. +/// 2. Sync the caps lock and num lock state to guest. +pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { + let mut locked_input = INPUTS.lock().unwrap(); + let upper = (ASCII_A..=ASCII_Z).contains(&keysym); + let is_letter = upper || (ASCII_A_LOWCASE..=ASCII_Z_LOWCASE).contains(&keysym); + let in_keypad = (KEYCODE_KP_7..=KEYCODE_KP_DECIMAL).contains(&keycode); + + if down && is_letter { + let shift = locked_input + .keyboard_state + .keyboard_modifier_get(KeyboardModifier::KeyModShift); + let in_upper = get_kbd_led_state(CAPS_LOCK_LED); + if (shift && upper == in_upper) || (!shift && upper != in_upper) { + debug!("Correct caps lock {} inside {}", upper, in_upper); + locked_input.press_key(KEYCODE_CAPS_LOCK)?; + } + } else if down && in_keypad { + let numlock = keysym_is_num_lock(keysym); + let in_numlock = get_kbd_led_state(NUM_LOCK_LED); + if in_numlock != numlock { + debug!("Correct num lock {} inside {}", numlock, in_numlock); + locked_input.press_key(KEYCODE_NUM_LOCK)?; + } + } + + locked_input + .keyboard_state + .keyboard_state_update(keycode, down) +} + +pub fn get_kbd_led_state(state: u8) -> bool { + LED_STATE.lock().unwrap().kbd_led & state == state +} + +pub fn set_kbd_led_state(state: u8) { + LED_STATE.lock().unwrap().kbd_led = state; +} + +pub fn keyboard_modifier_get(key_mod: KeyboardModifier) -> bool { + INPUTS + .lock() + .unwrap() + .keyboard_state + .keyboard_modifier_get(key_mod) +} + +pub fn keyboard_state_reset() { + INPUTS.lock().unwrap().keyboard_state.keyboard_state_reset(); +} + +fn keysym_is_num_lock(sym: i32) -> bool { + matches!( + (sym & 0xffff) as u16, + KEYPAD_1..=KEYPAD_9 | KEYPAD_SEPARATOR | KEYPAD_DECIMAL + ) +} + pub trait KeyboardOpts: Send { fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()>; } diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 1b32410e7..3bdcdc20d 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -14,8 +14,9 @@ use crate::{ console::{console_select, DisplayMouse}, error::VncError, input::{ - key_event, point_event, KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, INPUT_POINT_LEFT, - INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, UPPERCASE_TO_LOWERCASE, + key_event, keyboard_modifier_get, keyboard_state_reset, point_event, update_key_state, + KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, + INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, UPPERCASE_TO_LOWERCASE, }, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, @@ -910,14 +911,14 @@ impl ClientIoHandler { } let buf = self.read_incoming_msg(); let down: bool = buf[1] != 0; - let mut keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); + let org_keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); + let mut keysym = org_keysym; let server = self.server.clone(); // Uppercase -> Lowercase. if (ASCII_A..=ASCII_Z).contains(&keysym) { keysym += UPPERCASE_TO_LOWERCASE; } - let mut kbd_state = server.keyboard_state.borrow_mut(); let keycode: u16 = match server.keysym2keycode.get(&(keysym as u16)) { Some(k) => *k, @@ -929,14 +930,14 @@ impl ClientIoHandler { if (KEYCODE_1..KEYCODE_9 + 1).contains(&keycode) && down && self.server.display_listener.is_some() - && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModCtrl) - && kbd_state.keyboard_modifier_get(KeyboardModifier::KeyModAlt) + && keyboard_modifier_get(KeyboardModifier::KeyModCtrl) + && keyboard_modifier_get(KeyboardModifier::KeyModAlt) { - kbd_state.keyboard_state_reset(); + keyboard_state_reset(); console_select(Some((keycode - KEYCODE_1) as usize))?; } - kbd_state.keyboard_state_update(keycode, down)?; + update_key_state(down, org_keysym, keycode)?; key_event(keycode, down)?; self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index e2d498595..17b6ad419 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -24,7 +24,6 @@ use crate::{ }, data::keycode::KEYSYM2KEYCODE, error::VncError, - input::KeyBoardState, pixman::{ bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, ref_pixman_image, unref_pixman_image, @@ -48,12 +47,10 @@ use machine_manager::{ }; use once_cell::sync::Lazy; use std::{ - cell::RefCell, cmp, collections::HashMap, net::TcpListener, ptr, - rc::Rc, sync::{Arc, Mutex}, thread, }; @@ -282,23 +279,16 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { .expect("Set noblocking for vnc socket failed"); let mut keysym2keycode: HashMap = HashMap::new(); - - let mut max_keycode: u16 = 0; // Mapping ASCII to keycode. for &(k, v) in KEYSYM2KEYCODE.iter() { - max_keycode = cmp::max(max_keycode, v); keysym2keycode.insert(k, v); } - // Record keyboard state. - let keyboard_state: Rc> = - Rc::new(RefCell::new(KeyBoardState::new(max_keycode as usize))); let vnc_opts = Arc::new(VncInterface::default()); let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, vnc_opts))); let server = Arc::new(VncServer::new( get_client_image(), - keyboard_state, keysym2keycode, Some(Arc::downgrade(&dcl)), )); diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 76cc857c4..304056479 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -13,7 +13,6 @@ use crate::{ console::{DisplayChangeListener, DisplayMouse}, error::VncError, - input::KeyBoardState, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, get_image_width, pixman_image_linebuf_create, pixman_image_linebuf_fill, @@ -60,8 +59,6 @@ pub struct VncServer { pub client_handlers: Arc>>>, /// Security Type for connection. pub security_type: Rc>, - /// keyboard status. - pub keyboard_state: Rc>, /// Mapping ASCII to keycode. pub keysym2keycode: HashMap, /// Image data of surface. @@ -89,14 +86,12 @@ impl VncServer { /// Create a new VncServer. pub fn new( guest_image: *mut pixman_image_t, - keyboard_state: Rc>, keysym2keycode: HashMap, display_listener: Option>>, ) -> Self { VncServer { client_handlers: Arc::new(Mutex::new(HashMap::new())), security_type: Rc::new(RefCell::new(SecurityType::default())), - keyboard_state, keysym2keycode, vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), vnc_cursor: Arc::new(Mutex::new(VncCursor::default())), -- Gitee From 52916a539356f11d3256280d27acd0cf8b8502f5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 3 Apr 2023 21:08:24 +0800 Subject: [PATCH 1026/2187] usb: add bos descriptor 1. Add bos descriptor for super speed device. 2. Support SET_SEL and SET_ISOCH_DELAY for super speed device. 3. Support extra info for endpoint descriptor. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 4 +- devices/src/usb/config.rs | 7 ++++ devices/src/usb/descriptor.rs | 70 ++++++++++++++++++++++++++++++++++- devices/src/usb/keyboard.rs | 2 +- devices/src/usb/mod.rs | 10 +++++ devices/src/usb/storage.rs | 4 +- devices/src/usb/tablet.rs | 2 +- 7 files changed, 91 insertions(+), 8 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 3e0322b85..a32edd5cc 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -220,7 +220,7 @@ static DESC_INTERFACE_CAMERA_VC: Lazy> = Lazy::new(|| { wMaxPacketSize: 0x40, bInterval: 0x20, }, - extra: None, + extra: Vec::new(), })], }) }); @@ -348,7 +348,7 @@ static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { wMaxPacketSize: 0x400, bInterval: 0x20, }, - extra: None, + extra: Vec::new(), })], }) }); diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index a5b592fbd..92db56025 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -208,11 +208,18 @@ pub const USB_DT_BOS: u8 = 15; pub const USB_DT_DEVICE_CAPABILITY: u8 = 16; pub const USB_DT_ENDPOINT_COMPANION: u8 = 48; +/// USB SuperSpeed Device Capability. +pub const USB_SS_DEVICE_CAP: u8 = 0x3; +pub const USB_SS_DEVICE_SPEED_SUPPORTED_SUPER: u16 = 1 << 3; +pub const USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_SUPER: u8 = 3; + /// USB Descriptor size pub const USB_DT_DEVICE_SIZE: u8 = 18; pub const USB_DT_CONFIG_SIZE: u8 = 9; pub const USB_DT_INTERFACE_SIZE: u8 = 9; pub const USB_DT_ENDPOINT_SIZE: u8 = 7; +pub const USB_DT_BOS_SIZE: u8 = 5; +pub const USB_DT_SS_CAP_SIZE: u8 = 10; /// USB Endpoint Descriptor pub const USB_ENDPOINT_ATTR_CONTROL: u8 = 0; diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index b03ba5a43..7be662f63 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -143,6 +143,36 @@ struct UsbStringDescriptor { impl ByteCode for UsbStringDescriptor {} +/// USB binary device object store descriptor for transfer. +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UsbBOSDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub wTotalLength: u16, + pub bNumDeviceCaps: u8, +} + +impl ByteCode for UsbBOSDescriptor {} + +/// USB super speed capability descriptor for transfer. +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UsbSuperSpeedCapDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDevCapabilityType: u8, + pub bmAttributes: u8, + pub wSpeedsSupported: u16, + pub bFunctionalitySupport: u8, + pub bU1DevExitLat: u8, + pub wU2DevExitLat: u16, +} + +impl ByteCode for UsbSuperSpeedCapDescriptor {} + /// USB device descriptor. pub struct UsbDescDevice { pub device_desc: UsbDeviceDescriptor, @@ -199,7 +229,7 @@ pub struct UsbDescOther { /// USB endpoint descriptor. pub struct UsbDescEndpoint { pub endpoint_desc: UsbEndpointDescriptor, - pub extra: Option>, + pub extra: Vec, } /// USB Descriptor. @@ -302,7 +332,9 @@ impl UsbDescriptor { fn get_endpoint_descriptor(&self, ep: &UsbDescEndpoint) -> Result> { let desc = ep.endpoint_desc; - Ok(desc.as_bytes().to_vec()) + let mut buf = desc.as_bytes().to_vec(); + buf.append(&mut ep.extra.clone()); + Ok(buf) } fn get_string_descriptor(&self, index: u32) -> Result> { @@ -342,6 +374,39 @@ impl UsbDescriptor { Ok(vec![]) } + fn get_bos_descriptor(&self, speed: u32) -> Result> { + let mut total = USB_DT_BOS_SIZE as u16; + let mut cap = Vec::new(); + let mut cap_num = 0; + + if speed == USB_SPEED_SUPER { + let super_cap = UsbSuperSpeedCapDescriptor { + bLength: USB_DT_SS_CAP_SIZE, + bDescriptorType: USB_DT_DEVICE_CAPABILITY, + bDevCapabilityType: USB_SS_DEVICE_CAP, + bmAttributes: 0, + wSpeedsSupported: USB_SS_DEVICE_SPEED_SUPPORTED_SUPER, + bFunctionalitySupport: USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_SUPER, + bU1DevExitLat: 0xa, + wU2DevExitLat: 0x20, + }; + let mut super_buf = super_cap.as_bytes().to_vec(); + cap_num += 1; + total += super_buf.len() as u16; + cap.append(&mut super_buf); + } + + let bos = UsbBOSDescriptor { + bLength: USB_DT_BOS_SIZE, + bDescriptorType: USB_DT_BOS, + wTotalLength: total, + bNumDeviceCaps: cap_num, + }; + let mut buf = bos.as_bytes().to_vec(); + buf.append(&mut cap); + Ok(buf) + } + fn find_interface(&self, nif: u32, alt: u32) -> Option> { let conf = self.configuration_selected.as_ref()?; @@ -407,6 +472,7 @@ impl UsbDescriptorOps for UsbDevice { USB_DT_STRING => self.descriptor.get_string_descriptor(index)?, USB_DT_DEVICE_QUALIFIER => self.descriptor.get_device_qualifier_descriptor()?, USB_DT_DEBUG => self.descriptor.get_debug_descriptor()?, + USB_DT_BOS => self.descriptor.get_bos_descriptor(self.speed)?, _ => { bail!("Unknown descriptor type {}", desc_type); } diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index b3c19dcf7..bbce82b92 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -91,7 +91,7 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { wMaxPacketSize: 8, bInterval: 0xa, }, - extra: None, + extra: Vec::new(), })], }) }); diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index ae291ab8a..970892bf8 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -248,6 +248,16 @@ impl UsbDevice { self.remote_wakeup = 1; } } + USB_REQUEST_SET_SEL => { + if self.speed == USB_SPEED_SUPER { + return Ok(true); + } + } + USB_REQUEST_SET_ISOCH_DELAY => { + if self.speed == USB_SPEED_SUPER { + return Ok(true); + } + } _ => { return Ok(false); } diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 404dba25e..4f6329bbf 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -99,7 +99,7 @@ static DESC_IFACE_STORAGE: Lazy> = Lazy::new(|| { wMaxPacketSize: 512, bInterval: 0, }, - extra: None, + extra: Vec::new(), }), Arc::new(UsbDescEndpoint { endpoint_desc: UsbEndpointDescriptor { @@ -110,7 +110,7 @@ static DESC_IFACE_STORAGE: Lazy> = Lazy::new(|| { wMaxPacketSize: 512, bInterval: 0, }, - extra: None, + extra: Vec::new(), }), ], }) diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index b0b660183..3be4b9830 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -97,7 +97,7 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { wMaxPacketSize: 8, bInterval: 0xa, }, - extra: None, + extra: Vec::new(), })], }) }); -- Gitee From 169b35fa0d1dbc823942c190e32ac8bdda940496 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 22:13:54 +0800 Subject: [PATCH 1027/2187] usb: add super speed comp descriptor Add super speed comp descriptor for super speed device. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 11 ++++++++++- devices/src/usb/config.rs | 1 + devices/src/usb/descriptor.rs | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index a32edd5cc..2fd4fb76a 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -15,6 +15,7 @@ use anyhow::{bail, Result}; use log::{debug, error}; use once_cell::sync::Lazy; +use util::byte_code::ByteCode; use std::sync::{Arc, Mutex, Weak}; use strum::EnumCount; @@ -348,7 +349,15 @@ static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { wMaxPacketSize: 0x400, bInterval: 0x20, }, - extra: Vec::new(), + extra: UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 0, + bmAttributes: 0, + wBytesPerInterval: 0, + } + .as_bytes() + .to_vec(), })], }) }); diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 92db56025..aa61ac556 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -220,6 +220,7 @@ pub const USB_DT_INTERFACE_SIZE: u8 = 9; pub const USB_DT_ENDPOINT_SIZE: u8 = 7; pub const USB_DT_BOS_SIZE: u8 = 5; pub const USB_DT_SS_CAP_SIZE: u8 = 10; +pub const USB_DT_SS_EP_COMP_SIZE: u8 = 6; /// USB Endpoint Descriptor pub const USB_ENDPOINT_ATTR_CONTROL: u8 = 0; diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 7be662f63..ac8b36171 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -173,6 +173,20 @@ struct UsbSuperSpeedCapDescriptor { impl ByteCode for UsbSuperSpeedCapDescriptor {} +/// USB super speed endpoint companion descriptor for transfer. +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct UsbSuperSpeedEndpointCompDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bMaxBurst: u8, + pub bmAttributes: u8, + pub wBytesPerInterval: u16, +} + +impl ByteCode for UsbSuperSpeedEndpointCompDescriptor {} + /// USB device descriptor. pub struct UsbDescDevice { pub device_desc: UsbDeviceDescriptor, -- Gitee From dbad99d82dd0515a70d2f99b0703f830d23ea6b6 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 16 Apr 2023 21:33:40 +0800 Subject: [PATCH 1028/2187] usb: add some camera ops Add some camera ops which used for usb camera later. Signed-off-by: zhouli57 --- devices/src/camera_backend/mod.rs | 58 +++++++++++++++++++++++++----- devices/src/camera_backend/v4l2.rs | 45 +++++++++++++++++++---- 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 9b8574b0c..48ddce76b 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -16,8 +16,12 @@ pub mod demo; pub mod v4l2; +use std::sync::Arc; + use anyhow::Result; +use util::aio::Iovec; + #[allow(dead_code)] #[derive(Default)] pub struct CamFmt { @@ -37,12 +41,11 @@ impl CamFmt { } } -#[allow(dead_code)] #[derive(Default)] pub struct CamBasicFmt { - width: u16, - height: u16, - fps: u16, + width: u32, + height: u32, + fps: u32, fmttype: FmtType, } @@ -64,8 +67,7 @@ pub struct CamLensFmt { // TODO: to be extended. } -#[allow(dead_code)] -enum FmtType { +pub enum FmtType { Uncompressed = 0, Mjpg, } @@ -76,17 +78,55 @@ impl Default for FmtType { } } +pub struct CameraFrame { + pub width: u32, + pub height: u32, + pub interval: u32, +} + +pub struct CameraFormatList { + pub format: FmtType, + pub frame: Vec, +} + +/// Callback function which is called when frame data is coming. +pub type CameraNotifyCallback = Arc; + +/// Callback function which is called when backend is broken. +pub type CameraBrokenCallback = Arc; + pub trait CameraHostdevOps: Send + Sync { fn init(&self) -> Result<()>; fn is_camera(&self) -> Result; fn get_fmt(&self) -> Result<()>; - fn set_fmt(&self, fmt: u64) -> Result<()>; + /// Set a specific format. + fn set_fmt(&mut self, fmt: &CamBasicFmt) -> Result<()>; fn set_ctl(&self) -> Result<()>; // Turn stream on to start to receive frame buffer. fn video_stream_on(&self) -> Result<()>; - // The callback function used to poll on backend video devices, such as /dev/video0. - fn video_stream_run(&self) -> Result<()>; + // Turn stream off to end receiving frame buffer. fn video_stream_off(&self) -> Result<()>; + + /// List all formats supported by backend. + fn list_format(&mut self) -> Result>; + + /// Reset the device. + fn reset(&mut self); + + /// Get the total size of current frame. + fn get_frame_size(&self) -> usize; + + /// Copy frame data to iovecs. + fn get_frame(&self, iovecs: &[Iovec], frame_offset: usize, len: usize) -> Result; + + /// Get next frame when current frame is read complete. + fn next_frame(&mut self) -> Result<()>; + + /// Register notify callback which is called when data is coming. + fn register_notify_cb(&mut self, cb: CameraNotifyCallback); + + /// Register broken callback which is called when backend is broken. + fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index f27129fcc..a6cf2404a 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -16,8 +16,10 @@ use anyhow::{anyhow, Context, Result}; use std::fs::File; use std::os::unix::io::{IntoRawFd, RawFd}; -use super::CamFmt; -use super::CameraHostdevOps; +use super::{ + CamBasicFmt, CamFmt, CameraBrokenCallback, CameraFormatList, CameraHostdevOps, + CameraNotifyCallback, +}; #[allow(dead_code)] pub struct V4l2HostDev { @@ -101,7 +103,7 @@ impl CameraHostdevOps for V4l2HostDev { fn get_fmt(&self) -> Result<()> { Ok(()) } - fn set_fmt(&self, _fmt: u64) -> Result<()> { + fn set_fmt(&mut self, _fmt: &CamBasicFmt) -> Result<()> { Ok(()) } fn set_ctl(&self) -> Result<()> { @@ -111,10 +113,41 @@ impl CameraHostdevOps for V4l2HostDev { fn video_stream_on(&self) -> Result<()> { Ok(()) } - fn video_stream_run(&self) -> Result<()> { - Ok(()) - } + fn video_stream_off(&self) -> Result<()> { Ok(()) } + + fn list_format(&mut self) -> Result> { + todo!() + } + + fn reset(&mut self) { + todo!() + } + + fn get_frame_size(&self) -> usize { + todo!() + } + + fn get_frame( + &self, + _iovecs: &[util::aio::Iovec], + _frame_offset: usize, + _len: usize, + ) -> Result { + todo!() + } + + fn next_frame(&mut self) -> Result<()> { + todo!() + } + + fn register_notify_cb(&mut self, _cb: CameraNotifyCallback) { + todo!() + } + + fn register_broken_cb(&mut self, _cb: CameraBrokenCallback) { + todo!() + } } -- Gitee From a049fd9b255f32cb4e64b0bf2336feaf67e69b4e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 18:11:02 +0800 Subject: [PATCH 1029/2187] camera: add v4l2 backend api Add v4l2 backend api to get image from host. Signed-off-by: zhouli57 --- Cargo.lock | 220 +++++++++++++++++++++++++++++++++ Makefile | 1 + util/Cargo.toml | 1 + util/src/aio/mod.rs | 4 + util/src/lib.rs | 1 + util/src/v4l2.rs | 295 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 522 insertions(+) create mode 100644 util/src/v4l2.rs diff --git a/Cargo.lock b/Cargo.lock index 379499bfd..bdd74cf1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.65" @@ -95,6 +104,17 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -122,6 +142,29 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "bindgen" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -202,6 +245,15 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + [[package]] name = "cfg-expr" version = "0.8.1" @@ -217,6 +269,32 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "const_format" version = "0.2.26" @@ -312,6 +390,19 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "errno" version = "0.2.8" @@ -553,6 +644,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gobject-sys" version = "0.14.0" @@ -635,12 +732,27 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hypervisor" version = "2.2.0" @@ -725,12 +837,28 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libpulse-binding" version = "2.27.1" @@ -919,6 +1047,16 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -1048,6 +1186,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1202,6 +1346,12 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1341,6 +1491,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "slab" version = "0.4.8" @@ -1398,6 +1554,12 @@ dependencies = [ "util", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strum" version = "0.21.0" @@ -1477,6 +1639,24 @@ dependencies = [ "version-compare", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.36" @@ -1539,6 +1719,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -1567,9 +1753,25 @@ dependencies = [ "once_cell", "serde", "thiserror", + "v4l2-sys-mit", "vmm-sys-util", ] +[[package]] +name = "v4l2-sys-mit" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c932c06df4af1dfb229f604214f2a87993784596ff33ffdadcba1b5519254e" +dependencies = [ + "bindgen", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version-compare" version = "0.0.11" @@ -1749,6 +1951,15 @@ dependencies = [ "untrusted", ] +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1765,6 +1976,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Makefile b/Makefile index 2748cea1e..1057d9ecf 100644 --- a/Makefile +++ b/Makefile @@ -19,3 +19,4 @@ yum-deps: @yum install libcap-ng-devel @yum install cyrus-sasl-devel @yum install pulseaudio + @yum install clang diff --git a/util/Cargo.toml b/util/Cargo.toml index 1e7c96611..cb3982ed3 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -21,3 +21,4 @@ once_cell = "1.13.0" io-uring = "0.5.7" errno = "0.2.8" serde = { version = "1.0", features = ["derive"] } +v4l2-sys-mit = "0.2.0" \ No newline at end of file diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 7ed1241e4..fd6e3f916 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -98,6 +98,10 @@ impl Iovec { iov_len: len, } } + + pub fn is_none(&self) -> bool { + self.iov_base == 0 && self.iov_len == 0 + } } /// The trait for Asynchronous IO operation. diff --git a/util/src/lib.rs b/util/src/lib.rs index c39f055cd..fe7626f34 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -37,6 +37,7 @@ pub mod test_helper; pub mod time; pub mod trace; pub mod unix; +pub mod v4l2; pub use anyhow::Result; pub use error::UtilError; use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; diff --git a/util/src/v4l2.rs b/util/src/v4l2.rs new file mode 100644 index 000000000..68374956b --- /dev/null +++ b/util/src/v4l2.rs @@ -0,0 +1,295 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::fs::{File, OpenOptions}; +use std::io::ErrorKind; +use std::os::unix::prelude::{AsRawFd, OpenOptionsExt, RawFd}; +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; +use log::{debug, error}; +use v4l2_sys_mit::{ + v4l2_buffer, v4l2_capability, v4l2_fmtdesc, v4l2_format, v4l2_frmivalenum, v4l2_frmsizeenum, + v4l2_requestbuffers, v4l2_streamparm, +}; +use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; + +use crate::aio::Iovec; + +macro_rules! fourcc_code { + ($a: expr, $b: expr, $c: expr, $d: expr) => { + $a as u32 | (($b as u32) << 8) | (($c as u32) << 16) | (($d as u32) << 24) + }; +} + +pub const V4L2_FORMAT_YUYV: u32 = fourcc_code!('Y', 'U', 'Y', 'V'); +pub const V4L2_FORMAT_MJPG: u32 = fourcc_code!('M', 'J', 'P', 'G'); + +const VIDEO: u32 = 86; + +ioctl_ior_nr!(VIDIOC_QUERYCAP, VIDEO, 0, v4l2_capability); +ioctl_iowr_nr!(VIDIOC_ENUM_FMT, VIDEO, 2, v4l2_fmtdesc); +ioctl_iowr_nr!(VIDIOC_G_FMT, VIDEO, 4, v4l2_format); +ioctl_iowr_nr!(VIDIOC_S_FMT, VIDEO, 5, v4l2_format); +ioctl_iowr_nr!(VIDIOC_REQBUFS, VIDEO, 8, v4l2_requestbuffers); +ioctl_iowr_nr!(VIDIOC_QUERYBUF, VIDEO, 9, v4l2_buffer); +ioctl_iowr_nr!(VIDIOC_QBUF, VIDEO, 15, v4l2_buffer); +ioctl_iowr_nr!(VIDIOC_DQBUF, VIDEO, 17, v4l2_buffer); +ioctl_iow_nr!(VIDIOC_STREAMON, VIDEO, 18, std::os::raw::c_int); +ioctl_iow_nr!(VIDIOC_STREAMOFF, VIDEO, 19, std::os::raw::c_int); +ioctl_iowr_nr!(VIDIOC_S_PARM, VIDEO, 22, v4l2_streamparm); +ioctl_iowr_nr!(VIDIOC_ENUM_FRAMESIZES, VIDEO, 74, v4l2_frmsizeenum); +ioctl_iowr_nr!(VIDIOC_ENUM_FRAMEINTERVALS, VIDEO, 75, v4l2_frmivalenum); + +pub struct V4l2Backend { + /// V4L2 backend path, such as /dev/video0. + path: String, + /// V4L2 backend device fd. + fd: File, + /// V4L2 image buffer. + pub buffer: Arc>>, +} + +impl Drop for V4l2Backend { + fn drop(&mut self) { + debug!("Drop v4l2 backend fd {}", self.as_raw_fd()); + if let Err(e) = self.release_buffers() { + error!("Failed to release buffer for {}, {:?}", self.path, e); + } + } +} + +impl V4l2Backend { + pub fn new(path: String, buf_cnt: usize) -> Result { + let fd = OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) + .open(&path) + .with_context(|| format!("Failed to open v4l2 backend {}.", &path))?; + Ok(Self { + path, + fd, + buffer: Arc::new(Mutex::new(vec![Iovec::new(0, 0); buf_cnt])), + }) + } + + pub fn query_cap(&self) -> Result { + let mut cap = new_init::(); + let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_QUERYCAP(), &mut cap) }; + if ret < 0 { + bail!( + "Failed to query cap, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(cap) + } + + pub fn set_format(&self, fmt: &v4l2_format) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, VIDIOC_S_FMT(), fmt) }; + if ret < 0 { + bail!( + "Failed to set format, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } + + pub fn request_buffers(&self, bufs: &mut v4l2_requestbuffers) -> Result<()> { + // Ensure that there are no residual buffers. + self.release_buffers()?; + let mut locked_buf = self.buffer.lock().unwrap(); + let cnt = locked_buf.len() as u32; + // Ensure the count is equal to the length of buffer. + bufs.count = cnt; + let ret = unsafe { ioctl_with_ref(self, VIDIOC_REQBUFS(), bufs) }; + if ret < 0 { + bail!( + "Failed to request buffers, error {:?}", + std::io::Error::last_os_error() + ); + } + + for i in 0..cnt { + let mut buf = new_init::(); + buf.index = i; + buf.type_ = bufs.type_; + buf.memory = bufs.memory; + let ret = unsafe { ioctl_with_ref(self, VIDIOC_QUERYBUF(), &buf) }; + if ret < 0 { + bail!( + "Failed to query buffer {}, error {:?}", + i, + std::io::Error::last_os_error() + ); + } + + let ret = unsafe { + libc::mmap( + std::ptr::null_mut() as *mut libc::c_void, + buf.length as libc::size_t, + libc::PROT_WRITE | libc::PROT_READ, + libc::MAP_SHARED, + self.as_raw_fd(), + buf.m.offset.into(), + ) + }; + if ret == libc::MAP_FAILED { + bail!( + "Failed to mmap for buffer {}, error {:?}", + i, + std::io::Error::last_os_error() + ); + } + locked_buf[i as usize].iov_base = ret as u64; + locked_buf[i as usize].iov_len = buf.length as u64; + // Queue buffer to get data. + self.queue_buffer(&buf)?; + } + Ok(()) + } + + pub fn release_buffers(&self) -> Result<()> { + let mut locked_buf = self.buffer.lock().unwrap(); + for buf in locked_buf.iter_mut() { + if buf.is_none() { + continue; + } + let ret = unsafe { + libc::munmap( + buf.iov_base as *mut libc::c_void, + buf.iov_len as libc::size_t, + ) + }; + if ret < 0 { + bail!( + "Failed to release buffers, error {:?}", + std::io::Error::last_os_error() + ); + } + buf.iov_base = 0; + buf.iov_len = 0; + } + Ok(()) + } + + pub fn stream_on(&self, vtype: std::os::raw::c_int) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, VIDIOC_STREAMON(), &vtype) }; + if ret < 0 { + bail!( + "Failed to stream on, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } + + pub fn stream_off(&self, vtype: std::os::raw::c_int) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, VIDIOC_STREAMOFF(), &vtype) }; + if ret < 0 { + bail!( + "Failed to stream off, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } + + pub fn queue_buffer(&self, buf: &v4l2_buffer) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, VIDIOC_QBUF(), buf) }; + if ret < 0 { + bail!( + "Failed to queue buffer, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } + + pub fn dequeue_buffer(&self, buf: &v4l2_buffer) -> Result { + let ret = unsafe { ioctl_with_ref(self, VIDIOC_DQBUF(), buf) }; + if ret < 0 { + if errno::errno().0 == libc::EAGAIN { + return Ok(false); + } + bail!( + "Failed to dequeue buffer, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(true) + } + + pub fn enum_format(&self, desc: &mut v4l2_fmtdesc) -> Result { + let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_ENUM_FMT(), desc) }; + if ret < 0 { + let err = std::io::Error::last_os_error(); + if err.kind() == ErrorKind::InvalidInput { + return Ok(true); + } + bail!("Failed to enumerate format, error {:?}", err); + } + Ok(false) + } + + pub fn enum_frame_size(&self, frmsize: &mut v4l2_frmsizeenum) -> Result { + let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_ENUM_FRAMESIZES(), frmsize) }; + if ret < 0 { + let err = std::io::Error::last_os_error(); + if err.kind() == ErrorKind::InvalidInput { + return Ok(true); + } + bail!("Failed to enumerate frame size, error {:?}", err); + } + Ok(false) + } + + pub fn enum_frame_interval(&self, frame_val: &mut v4l2_frmivalenum) -> Result { + let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_ENUM_FRAMEINTERVALS(), frame_val) }; + if ret < 0 { + let err = std::io::Error::last_os_error(); + if err.kind() == ErrorKind::InvalidInput { + return Ok(true); + } + bail!("Failed to enumerate frame interval, error {:?}", err); + } + Ok(false) + } + + pub fn set_stream_parameter(&self, parm: &v4l2_streamparm) -> Result<()> { + let ret = unsafe { ioctl_with_ref(self, VIDIOC_S_PARM(), parm) }; + if ret < 0 { + bail!( + "Failed to set stream parameter, error {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } +} + +impl AsRawFd for V4l2Backend { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +pub fn new_init() -> T { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } +} -- Gitee From 24d24f9771f19c54a4e956022c7af422f163bc59 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 14:55:36 +0800 Subject: [PATCH 1030/2187] usb-host: Add the basic framework The combination of the hostbus and hostaddr, hostport or vendorid and productid is used to configure the USB-HOST device. Add a abstract USB device of the host USB device. Signed-off-by: Mingwang Li --- devices/src/usb/mod.rs | 2 + devices/src/usb/usbhost/mod.rs | 73 +++++++++++++++++++++++++++++++ docs/config_guidebook.md | 34 ++++++++++++++ machine/src/lib.rs | 29 ++++++++++-- machine_manager/src/config/mod.rs | 15 +++++++ machine_manager/src/config/pci.rs | 30 ++++++------- machine_manager/src/config/usb.rs | 68 +++++++++++++++++++++++++++- 7 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 devices/src/usb/usbhost/mod.rs diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 27156d42b..0367e4998 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -28,6 +28,8 @@ pub mod keyboard; pub mod storage; #[cfg(not(target_env = "musl"))] pub mod tablet; +#[cfg(not(target_env = "musl"))] +pub mod usbhost; pub mod xhci; use std::cmp::min; diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs new file mode 100644 index 000000000..a3ab4f6b5 --- /dev/null +++ b/devices/src/usb/usbhost/mod.rs @@ -0,0 +1,73 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex, Weak}; + +use anyhow::{Ok, Result}; + +use super::{ + xhci::xhci_controller::XhciDevice, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, + UsbPacket, +}; +use machine_manager::config::UsbHostConfig; + +/// Abstract object of the host USB device. +pub struct UsbHost { + id: String, + _config: UsbHostConfig, + usb_device: UsbDevice, +} + +impl UsbHost { + pub fn new(config: UsbHostConfig) -> Self { + Self { + id: config.id.clone().unwrap(), + _config: config, + usb_device: UsbDevice::new(), + } + } +} + +impl UsbDeviceOps for UsbHost { + fn realize(self) -> Result>> { + let usbhost = Arc::new(Mutex::new(self)); + Ok(usbhost) + } + + fn reset(&mut self) {} + + fn set_controller(&mut self, _cntlr: std::sync::Weak>) {} + + fn get_controller(&self) -> Option>> { + None + } + + fn get_wakeup_endpoint(&self) -> &UsbEndpoint { + self.usb_device.get_endpoint(true, 1) + } + + fn handle_control(&mut self, _packet: &Arc>, _device_req: &UsbDeviceRequest) {} + + fn handle_data(&mut self, _packet: &Arc>) {} + + fn device_id(&self) -> String { + self.id.clone() + } + + fn get_usb_device(&self) -> &UsbDevice { + &self.usb_device + } + + fn get_mut_usb_device(&mut self) -> &mut UsbDevice { + &mut self.usb_device + } +} diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 42a465947..29dad5f2e 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -807,6 +807,40 @@ Three properties can be set for USB Storage. Note: "aio=off,direct=false" must be configured and other aio/direct values are not supported. +### 2.13.6 USB Host +USB Host Device that based on USB protocol. It should be attached to USB controller. + +Six properties can be set for USB Host. + +* id: unique device id. +* hostbus: the bus number of the usb host device. +* hostaddr: the addr number of the usb host device. +* hostport: the physical number of the usb host device. +* vendorid: the vendor ID of the usb host device. +* productid: the product ID of the usb host device. + +Pass through the host device identified by bus and addr: + +```shell +-device usb-host,id=,hostbus=,hostaddr= +``` + +Pass through the host device identified by bus and physical port: + +```shell +-device usb-host,id=,hostbus=,hostport= +``` + +Pass through the host device identified by the vendor and product ID: + +```shell +-device usb-host,id=,vendorid=,productid= +``` + +Note: +1. The combination of vendor and product ID takes precedence over the combination of bus number and physical port number. +2. The combination of bus and physical port takes precedence over the combination of bus number and addr number. + ### 2.14 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 934144648..d29d552ca 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -51,7 +51,7 @@ use devices::InterruptController; #[cfg(not(target_env = "musl"))] use devices::usb::{ camera::UsbCamera, keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, - xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, + usbhost::UsbHost, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, }; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; @@ -66,8 +66,8 @@ use machine_manager::config::{ }; #[cfg(not(target_env = "musl"))] use machine_manager::config::{ - parse_gpu, parse_usb_camera, parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, - parse_xhci, + parse_gpu, parse_usb_camera, parse_usb_host, parse_usb_keyboard, parse_usb_storage, + parse_usb_tablet, parse_xhci, }; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; @@ -1289,6 +1289,25 @@ pub trait MachineOps { Ok(()) } + /// Add usb host. + /// + /// # Arguments + /// + /// * `cfg_args` - USB Host Configuration. + #[cfg(not(target_env = "musl"))] + fn add_usb_host(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let device_cfg = parse_usb_host(cfg_args)?; + let usbhost = UsbHost::new(device_cfg); + + let usbhost = usbhost + .realize() + .with_context(|| "Failed to realize usb host device")?; + + self.attach_usb_to_xhci_controller(vm_config, usbhost)?; + + Ok(()) + } + /// Add peripheral devices. /// /// # Arguments @@ -1392,6 +1411,10 @@ pub trait MachineOps { self.add_usb_storage(vm_config, cfg_args)?; } #[cfg(not(target_env = "musl"))] + "usb-host" => { + self.add_usb_host(vm_config, cfg_args)?; + } + #[cfg(not(target_env = "musl"))] "virtio-gpu-pci" => { self.add_virtio_pci_gpu(cfg_args)?; } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 11d2cb2e4..72b6f8ac6 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -76,6 +76,7 @@ use log::error; use util::device_tree::{self, FdtBuilder}; use util::{ file::{get_file_alignment, open_file}, + num_ops::str_to_usize, test_helper::is_test_enabled, trace::enable_trace_events, AsAny, @@ -582,6 +583,20 @@ pub fn add_trace_events(config: &str) -> Result<()> { bail!("trace: events file must be set."); } +/// This struct is a wrapper for `usize`. +/// Hexadecimal string can be converted to integers by this structure method. +pub struct UnsignedInteger(pub usize); + +impl FromStr for UnsignedInteger { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + let value = str_to_usize(s.to_string()) + .map_err(|e| error!("Invalid value {}, error is {:?}", s, e))?; + Ok(UnsignedInteger(value)) + } +} + pub struct IntegerList(pub Vec); impl FromStr for IntegerList { diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 11a01b698..b580e2402 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -14,7 +14,7 @@ use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; -use super::{CmdParser, ConfigCheck}; +use super::{CmdParser, ConfigCheck, UnsignedInteger}; use crate::config::{check_arg_too_long, ExBool}; use util::num_ops::str_to_usize; @@ -143,23 +143,23 @@ pub fn parse_root_port(rootport_cfg: &str) -> Result { .push("id"); cmd_parser.parse(rootport_cfg)?; - let mut root_port = RootPortConfig::default(); - - let port = cmd_parser - .get_value::("port")? - .with_context(|| ConfigError::FieldIsMissing("port".to_string(), "rootport".to_string()))?; - root_port.port = str_to_usize(port)? as u8; + let root_port = RootPortConfig { + port: cmd_parser + .get_value::("port")? + .with_context(|| { + ConfigError::FieldIsMissing("port".to_string(), "rootport".to_string()) + })? + .0 as u8, + id: cmd_parser.get_value::("id")?.with_context(|| { + ConfigError::FieldIsMissing("id".to_string(), "rootport".to_string()) + })?, + multifunction: cmd_parser + .get_value::("multifunction")? + .map_or(false, bool::from), + }; let _ = cmd_parser.get_value::("chassis")?; - root_port.id = cmd_parser - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "rootport".to_string()))?; - - root_port.multifunction = cmd_parser - .get_value::("multifunction")? - .map_or(false, bool::from); - root_port.check()?; Ok(root_port) } diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 9ad3fd2cb..e4381229c 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -12,13 +12,15 @@ use anyhow::{anyhow, bail, Context, Result}; -use super::{error::ConfigError, get_cameradev_by_id}; +use super::{error::ConfigError, get_cameradev_by_id, UnsignedInteger}; use crate::config::{ check_arg_nonexist, check_arg_too_long, CamBackendType, CameraDevConfig, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, }; use util::aio::AioEngine; +const USBHOST_ADDR_MAX: u8 = 127; + /// XHCI controller configuration. #[derive(Debug)] pub struct XhciConfig { @@ -292,3 +294,67 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result dev.check()?; Ok(dev) } + +#[derive(Clone, Debug, Default)] +pub struct UsbHostConfig { + /// USB Host device id. + pub id: Option, + /// The bus number of the USB Host device. + pub hostbus: u8, + /// The addr number of the USB Host device. + pub hostaddr: u8, + /// The physical port number of the USB host device. + pub hostport: Option, + /// The vendor id of the USB Host device. + pub vendorid: u16, + /// The product id of the USB Host device. + pub productid: u16, +} + +impl UsbHostConfig { + fn check_range(&self) -> Result<()> { + if self.hostaddr > USBHOST_ADDR_MAX { + bail!("USB Host hostaddr out of range"); + } + Ok(()) + } +} + +impl ConfigCheck for UsbHostConfig { + fn check(&self) -> Result<()> { + check_id(self.id.clone(), "usb-host")?; + self.check_range() + } +} + +pub fn parse_usb_host(cfg_args: &str) -> Result { + let mut cmd_parser = CmdParser::new("usb-host"); + cmd_parser + .push("") + .push("id") + .push("hostbus") + .push("hostaddr") + .push("hostport") + .push("vendorid") + .push("productid"); + + cmd_parser.parse(cfg_args)?; + + let dev = UsbHostConfig { + id: cmd_parser.get_value::("id")?, + hostbus: cmd_parser.get_value::("hostbus")?.unwrap_or(0), + hostaddr: cmd_parser.get_value::("hostaddr")?.unwrap_or(0), + hostport: cmd_parser.get_value::("hostport")?, + vendorid: cmd_parser + .get_value::("vendorid")? + .unwrap_or(UnsignedInteger(0)) + .0 as u16, + productid: cmd_parser + .get_value::("productid")? + .unwrap_or(UnsignedInteger(0)) + .0 as u16, + }; + + dev.check()?; + Ok(dev) +} -- Gitee From 97bd6cf6b73a368a1716c1e4fe3374aa9f123ebd Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 15:56:57 +0800 Subject: [PATCH 1031/2187] usb-host: Add realize, unrealize and related methods. Locate the device from host based on one of the parameter combination of hostbus and hostaddr, hostport and vendorid and productid. And detach the USB device from host in the realizing process. Release the interfaces of the host USB device and attach the USB device to host in the unrealizing process. Signed-off-by: Mingwang Li --- Cargo.lock | 29 +++ Makefile | 1 + devices/Cargo.toml | 1 + devices/src/usb/descriptor.rs | 2 +- devices/src/usb/mod.rs | 7 + devices/src/usb/usbhost/mod.rs | 312 +++++++++++++++++++++++++++++++-- machine/src/lib.rs | 2 +- 7 files changed, 342 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c207da074..3ed094626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,6 +355,7 @@ dependencies = [ "migration_derive", "once_cell", "pci", + "rusb", "serde", "serial_test", "strum 0.24.1", @@ -908,6 +909,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "libusb1-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -1341,6 +1354,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "rusb" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44a8c36914f9b1a3be712c1dfa48c9b397131f9a75707e570a391735f785c5d1" +dependencies = [ + "libc", + "libusb1-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -1767,6 +1790,12 @@ dependencies = [ "bindgen", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Makefile b/Makefile index 8e1ca4121..54b8b763a 100644 --- a/Makefile +++ b/Makefile @@ -21,3 +21,4 @@ yum-deps: @yum install pulseaudio @yum install clang @yum install gtk3-devel + @yum install libusbx diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 7ebaf3695..d29b85b4b 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -36,6 +36,7 @@ ui = { path = "../ui" } pci = { path = "../pci" } pulse = { version = "2.0", package = "libpulse-binding" } psimple = { version = "2.0", package = "libpulse-simple-binding" } +rusb = "0.9" [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 8faf26501..1376b6773 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -19,7 +19,7 @@ use util::byte_code::ByteCode; use super::config::*; use super::UsbDevice; -const USB_MAX_INTERFACES: u32 = 16; +pub const USB_MAX_INTERFACES: u32 = 16; const USB_DESCRIPTOR_TYPE_SHIFT: u32 = 8; const USB_DESCRIPTOR_INDEX_MASK: u32 = 0xff; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 0367e4998..b927d149b 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -39,6 +39,7 @@ use anyhow::{bail, Context}; use log::{debug, error}; use util::aio::{mem_from_buf, mem_to_buf, Iovec}; +use self::descriptor::USB_MAX_INTERFACES; use config::*; use descriptor::{UsbDescriptor, UsbDescriptorOps}; use machine_manager::qmp::send_device_deleted_msg; @@ -76,6 +77,8 @@ pub struct UsbEndpoint { pub ep_number: u8, pub in_direction: bool, pub ep_type: u8, + pub ifnum: u8, + pub halted: bool, } impl UsbEndpoint { @@ -84,6 +87,7 @@ impl UsbEndpoint { ep_number, in_direction, ep_type, + ..Default::default() } } } @@ -102,6 +106,8 @@ pub struct UsbDevice { pub descriptor: UsbDescriptor, /// The usb device id which is hot unplugged. pub unplugged_id: Option, + /// The index of the interfaces. + pub altsetting: [u32; USB_MAX_INTERFACES as usize], } impl UsbDevice { @@ -117,6 +123,7 @@ impl UsbDevice { remote_wakeup: 0, descriptor: UsbDescriptor::new(), unplugged_id: None, + altsetting: [0_u32; USB_MAX_INTERFACES as usize], }; for i in 0..USB_MAX_ENDPOINTS as u8 { diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index a3ab4f6b5..59c2efa15 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -12,38 +12,319 @@ use std::sync::{Arc, Mutex, Weak}; -use anyhow::{Ok, Result}; +use anyhow::{bail, Result}; +use log::{error, info}; +use rusb::{ + constants::LIBUSB_CLASS_HUB, Context, Device, DeviceDescriptor, DeviceHandle, Direction, + UsbContext, +}; -use super::{ - xhci::xhci_controller::XhciDevice, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, - UsbPacket, +use crate::usb::{ + config::{USB_ENDPOINT_ATTR_INVALID, USB_TOKEN_IN, USB_TOKEN_OUT}, + descriptor::USB_MAX_INTERFACES, + xhci::xhci_controller::XhciDevice, + UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, }; use machine_manager::config::UsbHostConfig; +#[derive(Default, Copy, Clone)] +struct InterfaceStatus { + detached: bool, + claimed: bool, +} + /// Abstract object of the host USB device. pub struct UsbHost { id: String, - _config: UsbHostConfig, + config: UsbHostConfig, + /// Libusb context. + context: Context, + /// A reference to a USB device. + libdev: Option>, + /// A handle to an open USB device. + handle: Option>, + /// Describes a device. + ddesc: Option, + /// Configuration interface number. + ifs_num: u8, + ifs: [InterfaceStatus; USB_MAX_INTERFACES as usize], usb_device: UsbDevice, } impl UsbHost { - pub fn new(config: UsbHostConfig) -> Self { - Self { + pub fn new(config: UsbHostConfig) -> Result { + let mut context = Context::new()?; + context.set_log_level(rusb::LogLevel::Warning); + Ok(Self { id: config.id.clone().unwrap(), - _config: config, + config, + context, + libdev: None, + handle: None, + ddesc: None, + ifs_num: 0, + ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], usb_device: UsbDevice::new(), + }) + } + + fn find_libdev(&self) -> Option> { + if self.config.vendorid != 0 && self.config.productid != 0 { + self.find_dev_by_vendor_product() + } else if self.config.hostport.is_some() { + self.find_dev_by_bus_port() + } else if self.config.hostbus != 0 && self.config.hostaddr != 0 { + self.find_dev_by_bus_addr() + } else { + None + } + } + + fn find_dev_by_bus_addr(&self) -> Option> { + self.context + .devices() + .ok() + .map(|devices| { + devices.iter().find(|device| { + if check_device_valid(device) { + return device.bus_number() == self.config.hostbus + && device.address() == self.config.hostaddr; + } + false + }) + }) + .unwrap_or_else(|| None) + } + + fn find_dev_by_vendor_product(&self) -> Option> { + self.context + .devices() + .ok() + .map(|devices| { + devices.iter().find(|device| { + if check_device_valid(device) { + let ddesc = device.device_descriptor().unwrap(); + return ddesc.vendor_id() == self.config.vendorid + && ddesc.product_id() == self.config.productid; + } + false + }) + }) + .unwrap_or_else(|| None) + } + + fn find_dev_by_bus_port(&self) -> Option> { + let hostport: Vec<&str> = self.config.hostport.as_ref().unwrap().split('.').collect(); + let mut port: Vec = Vec::new(); + for elem in hostport { + let elem = elem.to_string().parse::(); + if elem.is_err() { + return None; + } + port.push(elem.unwrap()); + } + + if port.is_empty() { + return None; + } + + self.context + .devices() + .ok() + .map(|devices| { + devices.iter().find(|device| { + if check_device_valid(device) { + return device.bus_number() == self.config.hostbus + && port.eq(device.port_numbers().as_ref().unwrap()); + } + false + }) + }) + .unwrap_or_else(|| None) + } + + fn detach_kernel(&mut self) -> Result<()> { + let conf = self.libdev.as_ref().unwrap().active_config_descriptor()?; + + self.ifs_num = conf.num_interfaces(); + + for i in 0..self.ifs_num { + if !match self.handle.as_ref().unwrap().kernel_driver_active(i as u8) { + Ok(rc) => { + if !rc { + self.ifs[i as usize].detached = true; + } + rc + } + Err(e) => { + error!("Failed to kernel driver active: {:?}", e); + false + } + } { + continue; + } + self.handle + .as_mut() + .unwrap() + .detach_kernel_driver(i as u8) + .unwrap_or_else(|e| error!("Failed to detach kernel driver: {:?}", e)); + self.ifs[i as usize].detached = true; + } + + Ok(()) + } + + fn attach_kernel(&mut self) { + if self + .libdev + .as_ref() + .unwrap() + .active_config_descriptor() + .is_err() + { + return; + } + for i in 0..self.ifs_num { + if !self.ifs[i as usize].detached { + continue; + } + self.handle + .as_mut() + .unwrap() + .attach_kernel_driver(i as u8) + .unwrap_or_else(|e| error!("Failed to attach kernel driver: {:?}", e)); + self.ifs[i as usize].detached = false; + } + } + + fn ep_update(&mut self) { + self.usb_device.reset_usb_endpoint(); + let conf = match self.libdev.as_ref().unwrap().active_config_descriptor() { + Ok(conf) => conf, + Err(_) => return, + }; + + for (i, intf) in conf.interfaces().into_iter().enumerate() { + // The usb_deviec.altsetting indexs alternate settings by the interface number. + // Get the 0th alternate setting first so that we can grap the interface number, + // and then correct the alternate setting value if necessary. + let mut intf_desc = intf.descriptors().next(); + if intf_desc.is_none() { + continue; + } + let alt = + self.usb_device.altsetting[intf_desc.as_ref().unwrap().interface_number() as usize]; + if alt != 0 { + if alt >= intf.descriptors().count() as u32 { + error!( + "Interface index {} exceeds max counts {}", + alt, + intf.descriptors().count() + ); + return; + } + intf_desc = intf.descriptors().nth(alt as usize); + } + + for ep in intf_desc.as_ref().unwrap().endpoint_descriptors() { + let addr = ep.address(); + let pid = match ep.direction() { + Direction::In => USB_TOKEN_IN, + Direction::Out => USB_TOKEN_OUT, + }; + let ep_num = ep.number(); + let ep_type = ep.transfer_type() as u8; + if ep_num == 0 { + error!("Invalid endpoint address {}", addr); + return; + } + let in_direction = pid == USB_TOKEN_IN; + if self.usb_device.get_endpoint(in_direction, ep_num).ep_type + != USB_ENDPOINT_ATTR_INVALID + { + error!("duplicate endpoint address") + } + let usb_ep = self.usb_device.get_mut_endpoint(in_direction, ep_num); + usb_ep.ep_type = ep_type; + usb_ep.ifnum = i as u8; + usb_ep.halted = false; + } + } + } + + fn open_and_init(&mut self) -> Result<()> { + self.handle = Some(self.libdev.as_ref().unwrap().open()?); + + self.detach_kernel()?; + + self.ddesc = self.libdev.as_ref().unwrap().device_descriptor().ok(); + + self.ep_update(); + + self.usb_device.speed = self.libdev.as_ref().unwrap().speed() as u32 - 1; + Ok(()) + } + + fn release_interfaces(&mut self) { + for i in 0..self.ifs_num { + if !self.ifs[i as usize].claimed { + continue; + } + self.handle + .as_mut() + .unwrap() + .release_interface(i as u8) + .unwrap_or_else(|e| error!("Failed to release interface: {:?}", e)); + self.ifs[i as usize].claimed = false; } } + + fn release_dev_to_host(&mut self) { + if self.handle.is_none() { + return; + } + + self.release_interfaces(); + self.handle.as_mut().unwrap().reset().unwrap_or_else(|e| { + error!( + "Failed to reset the handle of UsbHost device {}: {:?}", + self.id, e + ) + }); + self.attach_kernel(); + } } impl UsbDeviceOps for UsbHost { - fn realize(self) -> Result>> { + fn realize(mut self) -> Result>> { + self.libdev = self.find_libdev(); + if self.libdev.is_none() { + bail!("Invalid USB host config: {:?}", self.config); + } + + self.open_and_init()?; + let usbhost = Arc::new(Mutex::new(self)); Ok(usbhost) } - fn reset(&mut self) {} + fn unrealize(&mut self) -> Result<()> { + self.release_dev_to_host(); + Ok(()) + } + + fn reset(&mut self) { + info!("Usb Host device {} reset", self.id); + if self.handle.is_none() { + return; + } + + self.handle + .as_mut() + .unwrap() + .reset() + .unwrap_or_else(|e| error!("Failed to reset device handle:{:?}", e)); + } fn set_controller(&mut self, _cntlr: std::sync::Weak>) {} @@ -71,3 +352,14 @@ impl UsbDeviceOps for UsbHost { &mut self.usb_device } } + +fn check_device_valid(device: &Device) -> bool { + let ddesc = match device.device_descriptor() { + Ok(ddesc) => ddesc, + Err(_) => return false, + }; + if ddesc.class_code() == LIBUSB_CLASS_HUB { + return false; + } + true +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d29d552ca..c0ae5226a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1297,7 +1297,7 @@ pub trait MachineOps { #[cfg(not(target_env = "musl"))] fn add_usb_host(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_host(cfg_args)?; - let usbhost = UsbHost::new(device_cfg); + let usbhost = UsbHost::new(device_cfg)?; let usbhost = usbhost .realize() -- Gitee From 0e4f377bd231f75b3d40455d2b200393e54de640 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 17:18:58 +0800 Subject: [PATCH 1032/2187] usb-host: Implement the handling methods of control transfer Use synchronous interfaces of rusb library to handle control transfers. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 194 ++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 6 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 59c2efa15..14b394fd7 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -10,20 +10,27 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Arc, Mutex, Weak}; +use std::{ + sync::{Arc, Mutex, Weak}, + time::Duration, +}; use anyhow::{bail, Result}; -use log::{error, info}; +use log::{error, info, warn}; use rusb::{ - constants::LIBUSB_CLASS_HUB, Context, Device, DeviceDescriptor, DeviceHandle, Direction, + constants::LIBUSB_CLASS_HUB, Context, Device, DeviceDescriptor, DeviceHandle, Direction, Error, UsbContext, }; use crate::usb::{ - config::{USB_ENDPOINT_ATTR_INVALID, USB_TOKEN_IN, USB_TOKEN_OUT}, + config::{ + USB_DEVICE_OUT_REQUEST, USB_ENDPOINT_ATTR_INVALID, USB_ENDPOINT_OUT_REQUEST, + USB_INTERFACE_OUT_REQUEST, USB_REQUEST_CLEAR_FEATURE, USB_REQUEST_SET_ADDRESS, + USB_REQUEST_SET_CONFIGURATION, USB_REQUEST_SET_INTERFACE, USB_TOKEN_IN, USB_TOKEN_OUT, + }, descriptor::USB_MAX_INTERFACES, xhci::xhci_controller::XhciDevice, - UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, + UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; use machine_manager::config::UsbHostConfig; @@ -279,6 +286,150 @@ impl UsbHost { } } + fn claim_interfaces(&mut self) -> UsbPacketStatus { + self.usb_device.altsetting = [0; USB_MAX_INTERFACES as usize]; + if self.detach_kernel().is_err() { + return UsbPacketStatus::Stall; + } + + let conf = match self.libdev.as_ref().unwrap().active_config_descriptor() { + Ok(conf) => conf, + Err(e) => { + if e == Error::NotFound { + // Ignore address state + return UsbPacketStatus::Success; + } + return UsbPacketStatus::Stall; + } + }; + + let mut claimed = 0; + for i in 0..self.ifs_num { + if self + .handle + .as_mut() + .unwrap() + .claim_interface(i as u8) + .is_ok() + { + self.ifs[i as usize].claimed = true; + claimed += 1; + if claimed == conf.num_interfaces() { + break; + } + } + } + + if claimed != conf.num_interfaces() { + return UsbPacketStatus::Stall; + } + + UsbPacketStatus::Success + } + + fn set_config(&mut self, config: u8, packet: &mut UsbPacket) { + self.release_interfaces(); + + if self.ddesc.is_some() && self.ddesc.as_ref().unwrap().num_configurations() != 1 { + if let Err(e) = self + .handle + .as_mut() + .unwrap() + .set_active_configuration(config) + { + error!("Failed to set active configuration: {:?}", e); + if e == Error::NoDevice { + packet.status = UsbPacketStatus::NoDev + } else { + packet.status = UsbPacketStatus::Stall; + } + return; + } + } + + packet.status = self.claim_interfaces(); + if packet.status == UsbPacketStatus::Success { + self.ep_update(); + } + } + + fn set_interface(&mut self, iface: u16, alt: u16, packet: &mut UsbPacket) { + if iface > USB_MAX_INTERFACES as u16 { + packet.status = UsbPacketStatus::Stall; + return; + } + match self + .handle + .as_mut() + .unwrap() + .set_alternate_setting(iface as u8, alt as u8) + { + Ok(_) => { + self.usb_device.altsetting[iface as usize] = alt as u32; + self.ep_update(); + } + Err(e) => { + if e == Error::NoDevice { + packet.status = UsbPacketStatus::NoDev + } else { + packet.status = UsbPacketStatus::Stall; + } + } + } + } + + fn clear_halt(&mut self, pid: u8, index: u8) { + if self + .handle + .as_mut() + .unwrap() + .clear_halt(index as u8) + .is_err() + { + warn!("Failed to clear halt"); + } + self.usb_device + .get_mut_endpoint(pid == USB_TOKEN_IN, index & 0x0f) + .halted = false; + } + + fn control_transfer_pass_through( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + ) { + if packet.pid as u8 == USB_TOKEN_OUT { + if let Err(e) = self.handle.as_ref().unwrap().write_control( + device_req.request_type, + device_req.request, + device_req.value, + device_req.index, + &self.usb_device.data_buf[..device_req.length as usize], + Duration::from_millis(10), + ) { + error!("Failed to write control by usb host: {:?}", e); + packet.status = UsbPacketStatus::Stall; + return; + } + } else { + packet.actual_length = match self.handle.as_ref().unwrap().read_control( + device_req.request_type, + device_req.request, + device_req.value, + device_req.index, + &mut self.usb_device.data_buf[..device_req.length as usize], + Duration::from_millis(10), + ) { + Ok(n) => n as u32, + Err(e) => { + error!("Failed to read control by usb host: {:?}", e); + 0 + } + }; + }; + packet.status = UsbPacketStatus::Success; + } + fn release_dev_to_host(&mut self) { if self.handle.is_none() { return; @@ -336,7 +487,38 @@ impl UsbDeviceOps for UsbHost { self.usb_device.get_endpoint(true, 1) } - fn handle_control(&mut self, _packet: &Arc>, _device_req: &UsbDeviceRequest) {} + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { + let mut locked_packet = packet.lock().unwrap(); + if self.handle.is_none() { + locked_packet.status = UsbPacketStatus::NoDev; + return; + } + match device_req.request_type { + USB_DEVICE_OUT_REQUEST => { + if device_req.request == USB_REQUEST_SET_ADDRESS { + self.usb_device.addr = device_req.value as u8; + return; + } else if device_req.request == USB_REQUEST_SET_CONFIGURATION { + self.set_config(device_req.value as u8, &mut locked_packet); + return; + } + } + USB_INTERFACE_OUT_REQUEST => { + if device_req.request == USB_REQUEST_SET_INTERFACE { + self.set_interface(device_req.index, device_req.value, &mut locked_packet); + return; + } + } + USB_ENDPOINT_OUT_REQUEST => { + if device_req.request == USB_REQUEST_CLEAR_FEATURE && device_req.value == 0 { + self.clear_halt(locked_packet.pid as u8, device_req.index as u8); + return; + } + } + _ => {} + } + self.control_transfer_pass_through(&mut locked_packet, device_req); + } fn handle_data(&mut self, _packet: &Arc>) {} -- Gitee From 42aabfd1257aaada007ce8cf05347137430486c8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 25 Apr 2023 17:35:50 +0800 Subject: [PATCH 1033/2187] usb-host: Add the cleaning resources notifiers when vm exits Some resources need to be released when the process exits unexpectedly. Otherwise, resources cannot be released to hosts. For example, usb device passthrough. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 25 ++++++++- machine_manager/src/temp_cleaner.rs | 78 +++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 14b394fd7..6a1fe143e 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -32,7 +32,10 @@ use crate::usb::{ xhci::xhci_controller::XhciDevice, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use machine_manager::config::UsbHostConfig; +use machine_manager::{ + config::UsbHostConfig, + temp_cleaner::{ExitNotifier, TempCleaner}, +}; #[derive(Default, Copy, Clone)] struct InterfaceStatus { @@ -56,6 +59,8 @@ pub struct UsbHost { ifs_num: u8, ifs: [InterfaceStatus; USB_MAX_INTERFACES as usize], usb_device: UsbDevice, + /// Callback for release dev to Host afer the vm exited. + exit: Option>, } impl UsbHost { @@ -72,6 +77,7 @@ impl UsbHost { ifs_num: 0, ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], usb_device: UsbDevice::new(), + exit: None, }) } @@ -272,6 +278,19 @@ impl UsbHost { Ok(()) } + fn register_exit(&mut self) { + let exit = self as *const Self as u64; + let exit_notifier = Arc::new(move || { + // SAFETY: This callback is deleted after the device hot-unplug, so it is called only + // when the vm exits abnormally. + let usb_host = + &mut unsafe { std::slice::from_raw_parts_mut(exit as *mut UsbHost, 1) }[0]; + usb_host.release_dev_to_host(); + }) as Arc; + self.exit = Some(exit_notifier.clone()); + TempCleaner::add_exit_notifier(self.id.clone(), exit_notifier); + } + fn release_interfaces(&mut self) { for i in 0..self.ifs_num { if !self.ifs[i as usize].claimed { @@ -456,10 +475,14 @@ impl UsbDeviceOps for UsbHost { self.open_and_init()?; let usbhost = Arc::new(Mutex::new(self)); + // UsbHost addr is changed after Arc::new, so so the registration must be here. + usbhost.lock().unwrap().register_exit(); + Ok(usbhost) } fn unrealize(&mut self) -> Result<()> { + TempCleaner::remove_exit_notifier(&self.id); self.release_dev_to_host(); Ok(()) } diff --git a/machine_manager/src/temp_cleaner.rs b/machine_manager/src/temp_cleaner.rs index bda0cfb8d..c9d7ae63c 100644 --- a/machine_manager/src/temp_cleaner.rs +++ b/machine_manager/src/temp_cleaner.rs @@ -10,23 +10,32 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::HashMap; use std::fs; use std::io::Write; +use std::sync::Arc; static mut GLOBAL_TEMP_CLEANER: Option = None; +pub type ExitNotifier = dyn Fn() + Send + Sync; + /// This structure used to keep temporary file which was created by program, and would be deleted /// when Vm exit. pub struct TempCleaner { /// Path of files that should be removed after exiting the vm. paths: Vec, + /// Notifiers are used to release residual resources after exiting the vm. + notifiers: HashMap>, } impl TempCleaner { pub fn object_init() { unsafe { if GLOBAL_TEMP_CLEANER.is_none() { - GLOBAL_TEMP_CLEANER = Some(TempCleaner { paths: Vec::new() }); + GLOBAL_TEMP_CLEANER = Some(TempCleaner { + paths: Vec::new(), + notifiers: HashMap::new(), + }); } } } @@ -40,28 +49,57 @@ impl TempCleaner { } } - /// Clean the temporary files + /// Add exit notifier. + pub fn add_exit_notifier(id: String, exit: Arc) { + unsafe { + if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { + tmp.notifiers.insert(id, exit); + } + } + } + + /// Remove exit notifier by id. + pub fn remove_exit_notifier(id: &str) { + unsafe { + if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { + tmp.notifiers.remove(id); + } + } + } + + fn clean_files(&mut self) { + while let Some(path) = self.paths.pop() { + if let Err(ref e) = fs::remove_file(&path) { + write!( + &mut std::io::stderr(), + "Failed to delete console / socket file:{} :{} \r\n", + &path, + e + ) + .expect("Failed to write to stderr"); + } else { + write!( + &mut std::io::stdout(), + "Delete file: {} successfully.\r\n", + &path + ) + .expect("Failed to write to stdout"); + } + } + } + + fn exit_notifier(&mut self) { + for (_id, exit) in self.notifiers.iter() { + exit(); + } + } + + /// Clean the resources pub fn clean() { unsafe { if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { - while let Some(path) = tmp.paths.pop() { - if let Err(ref e) = fs::remove_file(&path) { - write!( - &mut std::io::stderr(), - "Failed to delete console / socket file:{} :{} \r\n", - &path, - e - ) - .expect("Failed to write to stderr"); - } else { - write!( - &mut std::io::stdout(), - "Delete file: {} successfully.\r\n", - &path - ) - .expect("Failed to write to stdout"); - } - } + tmp.clean_files(); + tmp.exit_notifier(); } } } -- Gitee From 41f0d86df6963ddb19a1ac154411e2c32b050e56 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 25 Apr 2023 17:44:08 +0800 Subject: [PATCH 1034/2187] usb-host: Add the asynchronous handling data method and a related wrapper lib Handling date submit asynchronously needs to call funtions of libusb1_sys which is a C lib. Calling the libusb1_sys lib functions must use unsafe expression. So wrap all related unsafe functions in a lib. Signed-off-by: Jinhao Gao --- Cargo.lock | 1 + devices/Cargo.toml | 1 + devices/src/usb/usbhost/host_usblib.rs | 287 ++++++++++++++++++ devices/src/usb/usbhost/mod.rs | 252 ++++++++++++++- devices/src/usb/xhci/xhci_controller.rs | 1 + ...Third_Party_Open_Source_Software_Notice.md | 6 + 6 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 devices/src/usb/usbhost/host_usblib.rs diff --git a/Cargo.lock b/Cargo.lock index 3ed094626..1d4b5dae8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,7 @@ dependencies = [ "libc", "libpulse-binding", "libpulse-simple-binding", + "libusb1-sys", "log", "machine_manager", "migration", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index d29b85b4b..91d7304d4 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -37,6 +37,7 @@ pci = { path = "../pci" } pulse = { version = "2.0", package = "libpulse-binding" } psimple = { version = "2.0", package = "libpulse-simple-binding" } rusb = "0.9" +libusb1-sys = "0.6.4" [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs new file mode 100644 index 000000000..b750bf895 --- /dev/null +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -0,0 +1,287 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + rc::Rc, + sync::{Arc, Mutex}, +}; + +use libc::{c_int, EPOLLIN, EPOLLOUT}; +use libusb1_sys::{ + constants::{ + LIBUSB_ERROR_ACCESS, LIBUSB_ERROR_BUSY, LIBUSB_ERROR_INTERRUPTED, + LIBUSB_ERROR_INVALID_PARAM, LIBUSB_ERROR_IO, LIBUSB_ERROR_NOT_FOUND, + LIBUSB_ERROR_NOT_SUPPORTED, LIBUSB_ERROR_NO_DEVICE, LIBUSB_ERROR_NO_MEM, + LIBUSB_ERROR_OVERFLOW, LIBUSB_ERROR_PIPE, LIBUSB_ERROR_TIMEOUT, LIBUSB_TRANSFER_CANCELLED, + LIBUSB_TRANSFER_COMPLETED, LIBUSB_TRANSFER_ERROR, LIBUSB_TRANSFER_NO_DEVICE, + LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_TIMED_OUT, + }, + libusb_get_pollfds, libusb_pollfd, libusb_transfer, +}; +use log::error; +use rusb::{Context, DeviceHandle, Error, Result, UsbContext}; +use vmm_sys_util::epoll::EventSet; + +use super::{UsbHost, UsbHostRequest}; +use crate::usb::{UsbPacketStatus, USB_TOKEN_IN}; +use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; + +const BULK_TIMEOUT: u32 = 0; +const INTERRUPT_TIMEOUT: u32 = 0; + +fn from_libusb(err: i32) -> Error { + match err { + LIBUSB_ERROR_IO => Error::Io, + LIBUSB_ERROR_INVALID_PARAM => Error::InvalidParam, + LIBUSB_ERROR_ACCESS => Error::Access, + LIBUSB_ERROR_NO_DEVICE => Error::NoDevice, + LIBUSB_ERROR_NOT_FOUND => Error::NotFound, + LIBUSB_ERROR_BUSY => Error::Busy, + LIBUSB_ERROR_TIMEOUT => Error::Timeout, + LIBUSB_ERROR_OVERFLOW => Error::Overflow, + LIBUSB_ERROR_PIPE => Error::Pipe, + LIBUSB_ERROR_INTERRUPTED => Error::Interrupted, + LIBUSB_ERROR_NO_MEM => Error::NoMem, + LIBUSB_ERROR_NOT_SUPPORTED => Error::NotSupported, + _ => Error::Other, + } +} + +macro_rules! try_unsafe { + ($x:expr) => { + // SAFETY: expression is calling C library of libusb. + match unsafe { $x } { + 0 => (), + err => return Err(from_libusb(err)), + } + }; +} + +pub fn get_request_from_transfer(transfer: *mut libusb_transfer) -> Arc> { + // Safety: cast the raw pointer of transfer's user_data to the + // Arc>. + unsafe { Arc::from_raw((*transfer).user_data.cast::>()) } +} + +pub fn get_buffer_from_transfer(transfer: *mut libusb_transfer) -> &'static mut [u8] { + // SAFETY: cast the raw pointer of transfer's buffer which is transformed + // from a slice with actual_length to a mutable slice. + unsafe { + std::slice::from_raw_parts_mut((*transfer).buffer, (*transfer).actual_length as usize) + } +} + +pub fn get_length_from_transfer(transfer: *mut libusb_transfer) -> i32 { + // SAFETY: cast the raw pointer of transfer's actual_length to a integer. + unsafe { (*transfer).actual_length } +} + +pub fn get_status_from_transfer(transfer: *mut libusb_transfer) -> i32 { + // SAFETY: cast the raw pointer of transfer's status which is to a integer. + unsafe { (*transfer).status } +} + +pub fn map_packet_status(status: i32) -> UsbPacketStatus { + match status { + LIBUSB_TRANSFER_COMPLETED => UsbPacketStatus::Success, + LIBUSB_TRANSFER_ERROR => UsbPacketStatus::IoError, + LIBUSB_TRANSFER_TIMED_OUT => UsbPacketStatus::IoError, + LIBUSB_TRANSFER_CANCELLED => UsbPacketStatus::IoError, + LIBUSB_TRANSFER_STALL => UsbPacketStatus::Stall, + LIBUSB_TRANSFER_NO_DEVICE => UsbPacketStatus::NoDev, + _ => UsbPacketStatus::Babble, + } +} + +pub fn get_libusb_pollfds(usbhost: Arc>) -> *const *mut libusb_pollfd { + // SAFETY: call C library of libusb to get pointer of poll fd. + unsafe { libusb_get_pollfds(usbhost.lock().unwrap().context.as_raw()) } +} + +pub fn set_pollfd_notifiers( + poll: *const *mut libusb_pollfd, + notifiers: &mut Vec, + handler: Rc, +) { + let mut i = 0; + // SAFETY: have checked whether the pointer is null before dereference it. + unsafe { + loop { + if (*poll.offset(i)).is_null() { + break; + }; + if (*(*poll.offset(i))).events as c_int == EPOLLIN { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + (*(*poll.offset(i))).fd, + None, + EventSet::IN, + vec![handler.clone()], + )); + } else if (*(*poll.offset(i))).events as c_int == EPOLLOUT { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + (*(*poll.offset(i))).fd, + None, + EventSet::OUT, + vec![handler.clone()], + )); + } + i += 1; + } + } +} + +pub fn alloc_host_transfer(iso_packets: c_int) -> *mut libusb_transfer { + if iso_packets < 0 { + error!( + "The number of iso packets cannot be less than 0, it is {}", + iso_packets + ); + return std::ptr::null_mut(); + } + + // SAFETY: have checked the validity of iso_packets before call C + // library of libusb to get the pointer of transfer. + unsafe { libusb1_sys::libusb_alloc_transfer(iso_packets) } +} + +extern "system" fn req_complete_data(host_transfer: *mut libusb_transfer) { + // SAFETY: transfer is still valid because libusb just completed it + // but we haven't told anyone yet. user_data remains valid because + // it is dropped only when the request is completed and removed from + // requests linked list. + let request = get_request_from_transfer(host_transfer); + let mut locked_request = request.lock().unwrap(); + let packet = locked_request.packet.clone(); + let mut locked_packet = packet.lock().unwrap(); + + if !locked_packet.is_async { + locked_request.complete(); + return; + } + + let actual_length = get_length_from_transfer(host_transfer); + let transfer_status = get_status_from_transfer(host_transfer); + locked_packet.status = map_packet_status(transfer_status); + + if locked_packet.pid as u8 == USB_TOKEN_IN && actual_length != 0 { + let data = get_buffer_from_transfer(host_transfer); + locked_packet.transfer_packet(data, actual_length as usize); + } + + if let Some(transfer) = locked_packet.xfer_ops.as_ref() { + if let Some(ops) = transfer.clone().upgrade() { + drop(locked_packet); + ops.lock().unwrap().submit_transfer(); + } + } + + locked_request.complete(); +} + +pub fn fill_bulk_transfer( + transfer: *mut libusb_transfer, + handle: Option<&mut DeviceHandle>, + ep_number: u8, + request: Arc>, +) { + let packet = request.lock().unwrap().packet.clone(); + let size = packet.lock().unwrap().get_iovecs_size(); + let buffer_ptr = request.lock().unwrap().buffer.as_mut_ptr(); + + if handle.is_none() { + error!("Failed to fill bulk transfer, handle is none"); + return; + } + + if transfer.is_null() { + error!("Failed to fill bulk transfer, transfer is none"); + return; + } + + // SAFETY: have checked the validity of parameters of libusb_fill_bulk_transfer + // before call libusb_fill_bulk_transfer. + unsafe { + libusb1_sys::libusb_fill_bulk_transfer( + transfer, + handle.unwrap().as_raw(), + ep_number, + buffer_ptr, + size as i32, + req_complete_data, + (Arc::into_raw(request) as *mut Mutex).cast::(), + BULK_TIMEOUT, + ); + } +} + +pub fn fill_interrupt_transfer( + transfer: *mut libusb_transfer, + handle: Option<&mut DeviceHandle>, + ep_number: u8, + request: Arc>, +) { + let packet = request.lock().unwrap().packet.clone(); + let size = packet.lock().unwrap().get_iovecs_size(); + let buffer_ptr = request.lock().unwrap().buffer.as_mut_ptr(); + + if handle.is_none() { + error!("Failed to fill interrupt transfer, handle is none"); + return; + } + + if transfer.is_null() { + error!("Failed to fill interrupt transfer, transfer is a null pointer"); + return; + } + + // SAFETY: have checked the validity of parameters of libusb_fill_interrupt_transfer + // before call libusb_fill_interrupt_transfer. + unsafe { + libusb1_sys::libusb_fill_interrupt_transfer( + transfer, + handle.unwrap().as_raw(), + ep_number, + buffer_ptr, + size as i32, + req_complete_data, + (Arc::into_raw(request) as *mut Mutex).cast::(), + INTERRUPT_TIMEOUT, + ); + } +} + +pub fn submit_host_transfer(transfer: *mut libusb_transfer) -> Result<()> { + if transfer.is_null() { + return Err(Error::NoMem); + } + try_unsafe!(libusb1_sys::libusb_submit_transfer(transfer)); + Ok(()) +} + +pub fn cancel_host_transfer(transfer: *mut libusb_transfer) -> Result<()> { + if transfer.is_null() { + return Ok(()); + } + try_unsafe!(libusb1_sys::libusb_cancel_transfer(transfer)); + Ok(()) +} + +pub fn free_host_transfer(transfer: *mut libusb_transfer) { + if transfer.is_null() { + return; + } + + // SAFETY: have checked the validity of transfer before call libusb_free_transfer. + unsafe { libusb1_sys::libusb_free_transfer(transfer) }; +} diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 6a1fe143e..3123636a8 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -11,11 +11,16 @@ // See the Mulan PSL v2 for more details. use std::{ + collections::LinkedList, + os::unix::io::RawFd, + rc::Rc, sync::{Arc, Mutex, Weak}, time::Duration, }; use anyhow::{bail, Result}; +use libc::c_int; +use libusb1_sys::libusb_transfer; use log::{error, info, warn}; use rusb::{ constants::LIBUSB_CLASS_HUB, Context, Device, DeviceDescriptor, DeviceHandle, Direction, Error, @@ -24,7 +29,8 @@ use rusb::{ use crate::usb::{ config::{ - USB_DEVICE_OUT_REQUEST, USB_ENDPOINT_ATTR_INVALID, USB_ENDPOINT_OUT_REQUEST, + USB_DEVICE_OUT_REQUEST, USB_DIRECTION_DEVICE_TO_HOST, USB_ENDPOINT_ATTR_BULK, + USB_ENDPOINT_ATTR_INT, USB_ENDPOINT_ATTR_INVALID, USB_ENDPOINT_OUT_REQUEST, USB_INTERFACE_OUT_REQUEST, USB_REQUEST_CLEAR_FEATURE, USB_REQUEST_SET_ADDRESS, USB_REQUEST_SET_CONFIGURATION, USB_REQUEST_SET_INTERFACE, USB_TOKEN_IN, USB_TOKEN_OUT, }, @@ -32,10 +38,19 @@ use crate::usb::{ xhci::xhci_controller::XhciDevice, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; +use host_usblib::*; use machine_manager::{ config::UsbHostConfig, + event_loop::{register_event_helper, unregister_event_helper}, temp_cleaner::{ExitNotifier, TempCleaner}, }; +use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}; + +mod host_usblib; + +const NON_ISO_PACKETS_NUMS: c_int = 0; +const COMPLETE_LIMIT: u32 = 200; +const HANDLE_TIMEOUT_MS: u64 = 2; #[derive(Default, Copy, Clone)] struct InterfaceStatus { @@ -43,6 +58,67 @@ struct InterfaceStatus { claimed: bool, } +pub struct UsbHostRequest { + pub packet: Arc>, + pub host_transfer: *mut libusb_transfer, + /// Async data buffer. + pub buffer: Vec, + pub completed: Arc>, +} + +impl UsbHostRequest { + pub fn new( + packet: Arc>, + host_transfer: *mut libusb_transfer, + completed: Arc>, + ) -> Self { + Self { + packet, + host_transfer, + buffer: Vec::new(), + completed, + } + } + + pub fn setup_buffer(&mut self) { + let mut locked_packet = self.packet.lock().unwrap(); + let size = locked_packet.get_iovecs_size(); + self.buffer = vec![0; size as usize]; + if locked_packet.pid as u8 == USB_TOKEN_OUT { + locked_packet.transfer_packet(self.buffer.as_mut(), size as usize); + } + } + + pub fn complete(&mut self) { + free_host_transfer(self.host_transfer); + self.buffer.clear(); + self.host_transfer = std::ptr::null_mut(); + let mut completed = self.completed.lock().unwrap(); + *completed += 1; + } + + pub fn abort_req(&mut self) -> Result<()> { + let mut locked_packet = self.packet.lock().unwrap(); + if locked_packet.is_async { + locked_packet.status = UsbPacketStatus::NoDev; + locked_packet.is_async = false; + cancel_host_transfer(self.host_transfer)?; + + if let Some(transfer) = locked_packet.xfer_ops.as_ref() { + if let Some(ops) = transfer.clone().upgrade() { + drop(locked_packet); + ops.lock().unwrap().submit_transfer(); + } + } + } + + Ok(()) + } +} + +unsafe impl Sync for UsbHostRequest {} +unsafe impl Send for UsbHostRequest {} + /// Abstract object of the host USB device. pub struct UsbHost { id: String, @@ -55,12 +131,17 @@ pub struct UsbHost { handle: Option>, /// Describes a device. ddesc: Option, + /// /// EventFd for libusb. + libevt: Vec, /// Configuration interface number. ifs_num: u8, ifs: [InterfaceStatus; USB_MAX_INTERFACES as usize], usb_device: UsbDevice, /// Callback for release dev to Host afer the vm exited. exit: Option>, + /// All pending asynchronous usb request. + requests: Arc>>>>, + completed: Arc>, } impl UsbHost { @@ -74,10 +155,13 @@ impl UsbHost { libdev: None, handle: None, ddesc: None, + libevt: Vec::new(), ifs_num: 0, ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], usb_device: UsbDevice::new(), exit: None, + requests: Arc::new(Mutex::new(LinkedList::new())), + completed: Arc::new(Mutex::new(0)), }) } @@ -454,6 +538,8 @@ impl UsbHost { return; } + self.abort_host_transfers() + .unwrap_or_else(|e| error!("Failed to abort all libusb transfers: {:?}", e)); self.release_interfaces(); self.handle.as_mut().unwrap().reset().unwrap_or_else(|e| { error!( @@ -463,6 +549,86 @@ impl UsbHost { }); self.attach_kernel(); } + + pub fn clear_succ_requests(&mut self) { + let mut updated_requests: LinkedList>> = LinkedList::new(); + let mut locked_request = self.requests.lock().unwrap(); + let mut completed = self.completed.lock().unwrap(); + + while !locked_request.is_empty() { + let request = locked_request.front(); + if let Some(request) = request { + if request.lock().unwrap().host_transfer.is_null() { + locked_request.pop_front(); + *completed -= 1; + } else { + break; + } + } else { + break; + } + } + + if *completed > COMPLETE_LIMIT { + loop { + let request = locked_request.pop_front(); + if let Some(request) = request { + if request.lock().unwrap().host_transfer.is_null() { + continue; + } + updated_requests.push_back(request); + } else { + break; + } + } + } + *locked_request = updated_requests; + *completed = 0; + } + + pub fn abort_host_transfers(&mut self) -> Result<()> { + // Max counts of uncompleted request to be handled. + let mut limit = 100; + for request in self.requests.lock().unwrap().iter_mut() { + request.lock().unwrap().abort_req()?; + } + + loop { + if self.requests.lock().unwrap().is_empty() { + return Ok(()); + } + let timeout = Some(Duration::from_millis(HANDLE_TIMEOUT_MS)); + self.context.handle_events(timeout)?; + if limit == 0 { + self.requests.lock().unwrap().clear(); + return Ok(()); + } + limit -= 1; + } + } +} + +impl EventNotifierHelper for UsbHost { + fn internal_notifiers(usbhost: Arc>) -> Vec { + let cloned_usbhost = usbhost.clone(); + let mut notifiers = Vec::new(); + + let poll = get_libusb_pollfds(usbhost); + let timeout = Some(Duration::from_micros(500)); + let handler: Rc = Rc::new(move |_, _fd: RawFd| { + cloned_usbhost + .lock() + .unwrap() + .context + .handle_events(timeout) + .unwrap_or_else(|e| error!("Failed to handle event: {:?}", e)); + None + }); + + set_pollfd_notifiers(poll, &mut notifiers, handler); + + notifiers + } } impl UsbDeviceOps for UsbHost { @@ -475,6 +641,8 @@ impl UsbDeviceOps for UsbHost { self.open_and_init()?; let usbhost = Arc::new(Mutex::new(self)); + let notifiers = EventNotifierHelper::internal_notifiers(usbhost.clone()); + register_event_helper(notifiers, None, &mut usbhost.lock().unwrap().libevt)?; // UsbHost addr is changed after Arc::new, so so the registration must be here. usbhost.lock().unwrap().register_exit(); @@ -484,6 +652,8 @@ impl UsbDeviceOps for UsbHost { fn unrealize(&mut self) -> Result<()> { TempCleaner::remove_exit_notifier(&self.id); self.release_dev_to_host(); + unregister_event_helper(None, &mut self.libevt)?; + info!("Usb Host device {} is unrealized", self.id); Ok(()) } @@ -492,6 +662,8 @@ impl UsbDeviceOps for UsbHost { if self.handle.is_none() { return; } + self.abort_host_transfers() + .unwrap_or_else(|e| error!("Failed to abort all libusb transfers: {:?}", e)); self.handle .as_mut() @@ -543,7 +715,83 @@ impl UsbDeviceOps for UsbHost { self.control_transfer_pass_through(&mut locked_packet, device_req); } - fn handle_data(&mut self, _packet: &Arc>) {} + fn handle_data(&mut self, packet: &Arc>) { + let cloned_packet = packet.clone(); + let mut locked_packet = packet.lock().unwrap(); + + if self.handle.is_none() { + locked_packet.status = UsbPacketStatus::NoDev; + return; + } + let in_direction = locked_packet.pid as u8 == USB_TOKEN_IN; + if self + .usb_device + .get_endpoint(in_direction, locked_packet.ep_number) + .halted + { + locked_packet.status = UsbPacketStatus::Stall; + return; + } + + drop(locked_packet); + let mut ep_number = packet.lock().unwrap().ep_number; + let host_transfer: *mut libusb_transfer; + + match self + .usb_device + .get_endpoint(in_direction, ep_number) + .ep_type + { + USB_ENDPOINT_ATTR_BULK => { + host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); + let request = Arc::new(Mutex::new(UsbHostRequest::new( + cloned_packet, + host_transfer, + self.completed.clone(), + ))); + request.lock().unwrap().setup_buffer(); + self.requests.lock().unwrap().push_back(request.clone()); + if packet.lock().unwrap().pid as u8 != USB_TOKEN_OUT { + ep_number |= USB_DIRECTION_DEVICE_TO_HOST; + } + fill_bulk_transfer(host_transfer, self.handle.as_mut(), ep_number, request); + } + USB_ENDPOINT_ATTR_INT => { + host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); + let request = Arc::new(Mutex::new(UsbHostRequest::new( + cloned_packet, + host_transfer, + self.completed.clone(), + ))); + request.lock().unwrap().setup_buffer(); + self.requests.lock().unwrap().push_back(request.clone()); + if packet.lock().unwrap().pid as u8 != USB_TOKEN_OUT { + ep_number |= USB_DIRECTION_DEVICE_TO_HOST; + } + fill_interrupt_transfer(host_transfer, self.handle.as_mut(), ep_number, request); + } + _ => { + error!("Isochronous transmission is not supported by host USB passthrough."); + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + }; + + match submit_host_transfer(host_transfer) { + Ok(()) => {} + Err(Error::NoDevice) => { + packet.lock().unwrap().status = UsbPacketStatus::NoDev; + return; + } + _ => { + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + }; + + packet.lock().unwrap().is_async = true; + self.clear_succ_requests(); + } fn device_id(&self) -> String { self.id.clone() diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a1872ae94..6886fbe82 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -155,6 +155,7 @@ impl XhciTransfer { } pub fn complete_transfer(&mut self) -> Result<()> { + self.packet.lock().unwrap().is_async = false; // NOTE: When entry this function, the transfer must be completed. self.complete = true; diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 5a6561f93..b676011d1 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -321,6 +321,12 @@ Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: Apache License Version 2.0 or BSD 3-Clause Please see above. +Software: libusb1_sys 0.6.4 +Copyright notice: +Copyright (c) 2015 David Cuddeback +License: MIT +Please see above. + ---------------------------------------------------------------- Copyright (c) . All rights reserved. -- Gitee From 655bfcd604220891628ffad731363dd47dc958da Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 25 Apr 2023 19:26:29 +0800 Subject: [PATCH 1035/2187] usb-host: Support hotplug of the UsbHost device Add the supporting of hotplug for the UsbHost device Signed-off-by: Jinhao Gao --- machine/src/standard_vm/mod.rs | 17 ++++++++++++++++- machine_manager/src/qmp/qmp_schema.rs | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 312270c18..601949a39 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -41,6 +41,7 @@ use std::ops::Deref; use std::os::unix::io::RawFd; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; +use std::string::String; use std::sync::{Arc, Mutex}; use super::Result as MachineResult; @@ -974,6 +975,20 @@ impl StdMachine { } self.add_usb_camera(&mut locked_vmconfig, &cfg_args)?; } + "usb-host" => { + let default_value = "0".to_string(); + let hostbus = args.hostbus.as_ref().unwrap_or(&default_value); + let hostaddr = args.hostaddr.as_ref().unwrap_or(&default_value); + let hostport = args.hostport.as_ref().unwrap_or(&default_value); + let vendorid = args.vendorid.as_ref().unwrap_or(&default_value); + let productid = args.productid.as_ref().unwrap_or(&default_value); + cfg_args = format!( + "{},hostbus={},hostaddr={},hostport={},vendorid={},productid={}", + cfg_args, hostbus, hostaddr, hostport, vendorid, productid + ); + + self.add_usb_host(&mut locked_vmconfig, &cfg_args)?; + } _ => { bail!("Invalid usb device driver '{}'", driver); } @@ -1186,7 +1201,7 @@ impl DeviceInterface for StdMachine { } } #[cfg(not(target_env = "musl"))] - "usb-kbd" | "usb-tablet" | "usb-camera" => { + "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" => { if let Err(e) = self.plug_usb_device(args.as_ref()) { error!("{:?}", e); return Response::create_error_response( diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index e219b5eea..3921d81f8 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -615,6 +615,11 @@ pub struct device_add { pub backend: Option, pub path: Option, pub cameradev: Option, + pub hostbus: Option, + pub hostaddr: Option, + pub hostport: Option, + pub vendorid: Option, + pub productid: Option, } pub type DeviceAddArgument = device_add; -- Gitee From ca26d17e9d8b0e7e2709ef5812e5ff33990722de Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 19:19:29 +0800 Subject: [PATCH 1036/2187] camera: add v4l2 camera Add v4l2 camera to support get camera image from v4l2 backend. Signed-off-by: zhouli57 --- Cargo.lock | 1 + devices/Cargo.toml | 1 + devices/src/camera_backend/mod.rs | 22 +- devices/src/camera_backend/v4l2.rs | 493 ++++++++++++++++++++++++----- devices/src/usb/camera.rs | 6 +- 5 files changed, 437 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bdd74cf1c..c207da074 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,6 +363,7 @@ dependencies = [ "thiserror", "ui", "util", + "v4l2-sys-mit", "vmm-sys-util", ] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 287976fae..7ebaf3695 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -19,6 +19,7 @@ drm-fourcc = ">=2.2.0" once_cell = "1.9.0" strum = "0.24.1" strum_macros = "0.24.3" +v4l2-sys-mit = "0.2.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 48ddce76b..210e1829c 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -18,10 +18,13 @@ pub mod v4l2; use std::sync::Arc; -use anyhow::Result; +use anyhow::{bail, Result}; use util::aio::Iovec; +/// Frame interval in 100ns units. +pub const INTERVALS_PER_SEC: u32 = 10_000_000; + #[allow(dead_code)] #[derive(Default)] pub struct CamFmt { @@ -41,7 +44,7 @@ impl CamFmt { } } -#[derive(Default)] +#[derive(Default, Debug)] pub struct CamBasicFmt { width: u32, height: u32, @@ -49,6 +52,15 @@ pub struct CamBasicFmt { fmttype: FmtType, } +impl CamBasicFmt { + pub fn get_frame_intervals(&self) -> Result { + if self.fps == 0 { + bail!("Invalid fps!"); + } + Ok(INTERVALS_PER_SEC / self.fps) + } +} + #[allow(dead_code)] #[derive(Default)] pub struct CamPUFmt { @@ -67,6 +79,7 @@ pub struct CamLensFmt { // TODO: to be extended. } +#[derive(Debug)] pub enum FmtType { Uncompressed = 0, Mjpg, @@ -78,6 +91,7 @@ impl Default for FmtType { } } +#[derive(Debug)] pub struct CameraFrame { pub width: u32, pub height: u32, @@ -104,10 +118,10 @@ pub trait CameraHostdevOps: Send + Sync { fn set_ctl(&self) -> Result<()>; // Turn stream on to start to receive frame buffer. - fn video_stream_on(&self) -> Result<()>; + fn video_stream_on(&mut self) -> Result<()>; // Turn stream off to end receiving frame buffer. - fn video_stream_off(&self) -> Result<()>; + fn video_stream_off(&mut self) -> Result<()>; /// List all formats supported by backend. fn list_format(&mut self) -> Result>; diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index a6cf2404a..b82397dd2 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -12,88 +12,211 @@ //! V4L2 backend for vCamera device. /dev/videoX and VIDIOC_XX ioctls are used. -use anyhow::{anyhow, Context, Result}; -use std::fs::File; -use std::os::unix::io::{IntoRawFd, RawFd}; +use std::os::unix::prelude::{AsRawFd, RawFd}; +use std::rc::Rc; +use std::sync::{Arc, Mutex}; -use super::{ - CamBasicFmt, CamFmt, CameraBrokenCallback, CameraFormatList, CameraHostdevOps, - CameraNotifyCallback, +use anyhow::{bail, Context, Result}; +use log::{debug, error, info, warn}; +use v4l2_sys_mit::{ + v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE, v4l2_buffer, v4l2_fmtdesc, v4l2_format, + v4l2_frmivalenum, v4l2_frmsizeenum, v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE, + v4l2_memory_V4L2_MEMORY_MMAP, v4l2_requestbuffers, v4l2_streamparm, V4L2_CAP_STREAMING, + V4L2_CAP_VIDEO_CAPTURE, }; +use vmm_sys_util::epoll::EventSet; -#[allow(dead_code)] -pub struct V4l2HostDev { - device: String, // backend host device, eg. "/dev/video0" - pub fd: RawFd, // the fd for "device" +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::aio::Iovec; +use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; - buffer: Vec, // buffer that stores video frame - buf_addr: u64, // video buffer related hpa - buf_len: u64, // video buffer size +use crate::camera_backend::{ + CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, + CameraNotifyCallback, FmtType, INTERVALS_PER_SEC, +}; +use util::v4l2::{new_init, V4l2Backend, V4L2_FORMAT_MJPG, V4L2_FORMAT_YUYV}; + +const BUFFER_CNT: usize = 4; - hostfmt: CamFmt, // the combination of video formats that the hardware supports - pub cur_fmt: CamFmt, // the combination of video formats that we negotiated with the hardware +#[derive(Default)] +pub struct Sample { + /// Sample address. + addr: u64, + /// Sample used length. + used_len: u64, + /// Sample in which buffer. + buf_index: u32, } -#[allow(dead_code)] -impl V4l2HostDev { - pub fn new(device: String) -> Self { - V4l2HostDev { - device, - fd: -1, - buffer: vec![], - buf_addr: 0, - buf_len: 0, - hostfmt: CamFmt::new(), - cur_fmt: CamFmt::new(), - } +impl Sample { + fn reset(&mut self) { + self.addr = 0; + self.used_len = 0; + self.buf_index = 0; } +} - pub fn realize(&mut self) -> Result<()> { - // open /dev/videoX - self.fd = File::open(self.device.clone()) - .with_context(|| anyhow!("failed to open video device"))? - .into_raw_fd(); +pub struct V4l2CameraBackend { + id: String, + dev_path: String, + /// Sample info. + sample: Arc>, + /// V4l2 backend used to get frame. + backend: Option>, + /// Callback to used to notify when data is coming. + notify_cb: Option, + /// Callback to used to notify the broken. + broken_cb: Option, + /// If the video stream is on or not. + running: bool, + /// If the backend fd is listening or not. + listening: bool, + iothread: Option, + delete_evts: Vec, +} - Ok(()) +impl V4l2CameraBackend { + pub fn new(id: String, path: String, iothread: Option) -> Result { + let backend = V4l2Backend::new(path.clone(), BUFFER_CNT)?; + let cam = V4l2CameraBackend { + id, + dev_path: path, + sample: Arc::new(Mutex::new(Sample::default())), + backend: Some(Arc::new(backend)), + running: false, + listening: false, + notify_cb: None, + broken_cb: None, + iothread, + delete_evts: Vec::new(), + }; + cam.check_cap()?; + Ok(cam) } - // Below funcs are just encapsulation for v4l2 ioctls. - fn map_buffer() -> Result<()> { - Ok(()) - } - fn query_cap() -> Result<()> { - Ok(()) - } - fn query_buffer() -> Result<()> { - Ok(()) - } - fn g_fmt() -> Result<()> { - Ok(()) - } - fn s_fmt() -> Result<()> { - Ok(()) - } - fn require_buf() -> Result<()> { - Ok(()) - } - fn query_buf() -> Result<()> { + fn check_cap(&self) -> Result<()> { + // SAFETY: backend is inited in outside function. + let cap = self.backend.as_ref().unwrap().query_cap()?; + if cap.capabilities & V4L2_CAP_VIDEO_CAPTURE != V4L2_CAP_VIDEO_CAPTURE { + bail!( + "Device {} not support capture capability {}", + self.id, + cap.capabilities + ); + } + if cap.device_caps & V4L2_CAP_VIDEO_CAPTURE != V4L2_CAP_VIDEO_CAPTURE { + bail!( + "Device caps {} not support capture capability {}", + self.id, + cap.device_caps + ); + } + if cap.capabilities & V4L2_CAP_STREAMING != V4L2_CAP_STREAMING { + bail!( + "Device {} not support streaming capability {}", + self.id, + cap.capabilities + ); + } Ok(()) } - fn qbuf() -> Result<()> { + + fn register_fd(&mut self) -> Result<()> { + if self.listening { + self.unregister_fd()?; + } + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + debug!("Camera {} register fd {}", self.id, backend.as_raw_fd()); + // Register event notifier for /dev/videoX. + let handler = Arc::new(Mutex::new(V4l2IoHander::new( + &self.sample, + backend, + self.notify_cb.clone(), + self.broken_cb.clone(), + ))); + register_event_helper( + EventNotifierHelper::internal_notifiers(handler), + self.iothread.as_ref(), + &mut self.delete_evts, + )?; + self.listening = true; Ok(()) } - fn dqbuf() -> Result<()> { + + fn unregister_fd(&mut self) -> Result<()> { + if !self.listening { + warn!("Camera {} is not listening", self.id); + return Ok(()); + } + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + debug!("Camera {} unregister fd {}", self.id, backend.as_raw_fd()); + unregister_event_helper(self.iothread.as_ref(), &mut self.delete_evts)?; + self.listening = false; Ok(()) } - fn stream_on() -> Result<()> { - Ok(()) + + fn list_frame_size(&self, pixfmt: u32) -> Result> { + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + let mut list = Vec::new(); + let mut frmsize = new_init::(); + frmsize.pixel_format = pixfmt; + const FRAME_SIZE_LIMIT: u32 = 1000; + for i in 0..FRAME_SIZE_LIMIT { + frmsize.index = i; + let frame_size_end = backend.enum_frame_size(&mut frmsize)?; + if frame_size_end { + break; + } + // NOTE: Only support discrete now. + if (frmsize.type_) != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE as u32 { + continue; + } + let width = unsafe { frmsize.__bindgen_anon_1.discrete.width }; + let height = unsafe { frmsize.__bindgen_anon_1.discrete.height }; + let interval_list = self.list_frame_interval(pixfmt, width, height)?; + for interval in interval_list { + list.push(CameraFrame { + width, + height, + interval, + }); + } + } + Ok(list) } - fn stream_off() -> Result<()> { - Ok(()) + + fn list_frame_interval(&self, pixfmt: u32, width: u32, height: u32) -> Result> { + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + let mut list = Vec::new(); + let mut frame_val = new_init::(); + frame_val.pixel_format = pixfmt; + frame_val.width = width; + frame_val.height = height; + const FRAME_INTERVAL_LIMIT: u32 = 1000; + for i in 0..FRAME_INTERVAL_LIMIT { + frame_val.index = i; + let interval_end = backend.enum_frame_interval(&mut frame_val)?; + if interval_end { + break; + } + let numerator = unsafe { frame_val.__bindgen_anon_1.discrete.numerator }; + let denominator = unsafe { frame_val.__bindgen_anon_1.discrete.denominator }; + if denominator == 0 { + warn!( + "Invalid denominator ignore it, format {} width {} height {}", + frame_val.pixel_format, frame_val.width, frame_val.height + ); + continue; + } + let interval = + (numerator as u64 * INTERVALS_PER_SEC as u64 / denominator as u64) as u32; + list.push(interval); + } + Ok(list) } } -impl CameraHostdevOps for V4l2HostDev { +impl CameraHostdevOps for V4l2CameraBackend { fn init(&self) -> Result<()> { Ok(()) } @@ -103,51 +226,263 @@ impl CameraHostdevOps for V4l2HostDev { fn get_fmt(&self) -> Result<()> { Ok(()) } - fn set_fmt(&mut self, _fmt: &CamBasicFmt) -> Result<()> { + fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { + info!("Camera {} set format {:?}", self.id, cam_fmt); + if self.listening { + self.unregister_fd()?; + } + + // NOTE: Reopen backend to avoid Device or Resource busy. + let backend = V4l2Backend::new(self.dev_path.clone(), BUFFER_CNT)?; + debug!( + "Camera {} set format open fd {}", + self.id, + backend.as_raw_fd() + ); + self.backend = Some(Arc::new(backend)); + + let mut fmt = new_init::(); + fmt.type_ = V4L2_CAP_VIDEO_CAPTURE; + fmt.fmt.pix.width = cam_fmt.width; + fmt.fmt.pix.height = cam_fmt.height; + fmt.fmt.pix.pixelformat = cam_fmt_to_v4l2(&cam_fmt.fmttype); + fmt.fmt.pix.field = 4; + // SAFETY: backend is inited before. + let backend = self.backend.as_ref().unwrap(); + backend.set_format(&fmt)?; + + let mut parm = new_init::(); + parm.type_ = v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; + let interval = cam_fmt.get_frame_intervals()?; + unsafe { + parm.parm.capture.timeperframe.numerator = 30; + parm.parm.capture.timeperframe.denominator = + parm.parm.capture.timeperframe.numerator * INTERVALS_PER_SEC / interval; + } + backend.set_stream_parameter(&parm)?; Ok(()) } fn set_ctl(&self) -> Result<()> { Ok(()) } - fn video_stream_on(&self) -> Result<()> { + fn video_stream_on(&mut self) -> Result<()> { + if self.running { + warn!("Camera {} already running", self.id); + return Ok(()); + } + info!("Camera {} stream on", self.id); + let mut bufs = new_init::(); + bufs.type_ = v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufs.memory = v4l2_memory_V4L2_MEMORY_MMAP; + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + backend.request_buffers(&mut bufs)?; + backend.stream_on(V4L2_CAP_VIDEO_CAPTURE as std::os::raw::c_int)?; + self.register_fd()?; + self.running = true; Ok(()) } - fn video_stream_off(&self) -> Result<()> { + fn video_stream_off(&mut self) -> Result<()> { + info!("Camera {} stream off", self.id); + self.unregister_fd()?; + if let Some(backend) = self.backend.as_ref() { + backend.stream_off(V4L2_CAP_VIDEO_CAPTURE as std::os::raw::c_int)?; + backend.release_buffers()?; + self.backend = None; + } + self.running = false; Ok(()) } fn list_format(&mut self) -> Result> { - todo!() + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + let mut list = Vec::new(); + let mut desc = new_init::(); + desc.type_ = V4L2_CAP_VIDEO_CAPTURE; + const FORMAT_LIMIT: u32 = 1000; + for i in 0..FORMAT_LIMIT { + desc.index = i; + let format_end = backend.enum_format(&mut desc)?; + if format_end { + break; + } + list.push(CameraFormatList { + format: cam_fmt_from_v4l2(desc.pixelformat)?, + frame: self.list_frame_size(desc.pixelformat)?, + }); + } + Ok(list) } fn reset(&mut self) { - todo!() + info!("device {} reset", self.id); + if self.running { + if let Err(e) = self.unregister_fd() { + warn!("Failed to unregister fd when reset {:?}", e); + } + if let Some(backend) = self.backend.as_ref() { + if let Err(e) = backend.stream_off(V4L2_CAP_VIDEO_CAPTURE as std::os::raw::c_int) { + warn!("Failed to stream off when reset {:?}", e); + } + if let Err(e) = backend.release_buffers() { + warn!("Failed to release buffer when reset {:?}", e); + } + self.backend = None; + } + } + self.listening = false; + self.running = false; + self.sample.lock().unwrap().reset(); } fn get_frame_size(&self) -> usize { - todo!() + self.sample.lock().unwrap().used_len as usize } - fn get_frame( - &self, - _iovecs: &[util::aio::Iovec], - _frame_offset: usize, - _len: usize, - ) -> Result { - todo!() + fn next_frame(&mut self) -> Result<()> { + let mut locked_sample = self.sample.lock().unwrap(); + locked_sample.used_len = 0; + let backend = self.backend.as_ref().with_context(|| "Backend is none")?; + let mut buf = new_init::(); + buf.type_ = v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = v4l2_memory_V4L2_MEMORY_MMAP; + buf.index = locked_sample.buf_index; + backend.queue_buffer(&buf)?; + Ok(()) } - fn next_frame(&mut self) -> Result<()> { - todo!() + fn get_frame(&self, iovecs: &[Iovec], frame_offset: usize, len: usize) -> Result { + let locked_sample = self.sample.lock().unwrap(); + if frame_offset + len > locked_sample.used_len as usize { + bail!("Invalid frame offset {} or len {}", frame_offset, len); + } + let mut copyed = 0; + for iov in iovecs { + if len == copyed { + break; + } + let cnt = std::cmp::min(iov.iov_len as usize, len - copyed); + let src_ptr = locked_sample.addr + frame_offset as u64 + copyed as u64; + // SAFETY: the address is not out of range. + unsafe { + std::ptr::copy(src_ptr as *const u8, iov.iov_base as *mut u8, cnt); + } + copyed += cnt; + } + Ok(copyed) + } + + fn register_notify_cb(&mut self, cb: CameraNotifyCallback) { + self.notify_cb = Some(cb); + } + + fn register_broken_cb(&mut self, cb: CameraBrokenCallback) { + self.broken_cb = Some(cb); } +} - fn register_notify_cb(&mut self, _cb: CameraNotifyCallback) { - todo!() +fn cam_fmt_to_v4l2(t: &FmtType) -> u32 { + match t { + FmtType::Uncompressed => V4L2_FORMAT_YUYV, + FmtType::Mjpg => V4L2_FORMAT_MJPG, } +} + +fn cam_fmt_from_v4l2(t: u32) -> Result { + let fmt = match t { + V4L2_FORMAT_YUYV => FmtType::Uncompressed, + V4L2_FORMAT_MJPG => FmtType::Mjpg, + _ => bail!("Invalid v4l2 type {}", t), + }; + Ok(fmt) +} + +pub struct V4l2IoHander { + sample: Arc>, + backend: Arc, + notify_cb: Option, + broken_cb: Option, +} + +impl V4l2IoHander { + pub fn new( + sample: &Arc>, + backend: &Arc, + cb: Option, + broken_cb: Option, + ) -> Self { + V4l2IoHander { + sample: sample.clone(), + backend: backend.clone(), + notify_cb: cb, + broken_cb, + } + } + + fn handle_sample(&mut self) -> Result<()> { + let mut buf = new_init::(); + buf.type_ = v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = v4l2_memory_V4L2_MEMORY_MMAP; + if !self.backend.dequeue_buffer(&buf)? { + // Not ready. + return Ok(()); + } + if buf.bytesused > buf.length { + bail!("Invalid buf used {} length {}", buf.bytesused, buf.length); + } + let locked_buf = self.backend.buffer.lock().unwrap(); + let mut locked_sample = self.sample.lock().unwrap(); + if locked_sample.used_len == 0 { + let iov = locked_buf + .get(buf.index as usize) + .with_context(|| "Buffer index overflow")?; + if buf.bytesused as u64 > iov.iov_len { + bail!( + "Buffer overflow, bytesused {} iov len {}", + buf.bytesused, + iov.iov_len + ); + } + locked_sample.addr = iov.iov_base; + locked_sample.used_len = buf.bytesused as u64; + locked_sample.buf_index = buf.index; + drop(locked_sample); + // Notify the camera to deal with request. + if let Some(notify_cb) = &self.notify_cb { + notify_cb(); + } + } else { + self.backend + .queue_buffer(&buf) + .with_context(|| "Failed to queue buffer when handle sample")?; + } + Ok(()) + } +} + +impl EventNotifierHelper for V4l2IoHander { + fn internal_notifiers(v4l2_handler: Arc>) -> Vec { + let cloend_v4l2_handler = v4l2_handler.clone(); + let handler: Rc = Rc::new(move |event, _fd: RawFd| { + let mut locked_handler = cloend_v4l2_handler.lock().unwrap(); + if event & EventSet::HANG_UP == EventSet::HANG_UP { + if let Some(broken_cb) = &locked_handler.broken_cb { + // Backend is broken. + broken_cb(); + } + } else if let Err(e) = locked_handler.handle_sample() { + error!("Failed to handle sample {:?}", e); + } + None + }); - fn register_broken_cb(&mut self, _cb: CameraBrokenCallback) { - todo!() + vec![EventNotifier::new( + NotifierOperation::AddShared, + v4l2_handler.lock().unwrap().backend.as_raw_fd(), + None, + EventSet::IN | EventSet::EDGE_TRIGGERED | EventSet::HANG_UP, // For unexpected device removal. + vec![handler], + )] } } diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 2fd4fb76a..d4f0668e6 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -23,7 +23,7 @@ use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use super::config::USB_SPEED_HIGH; use super::xhci::xhci_controller::XhciDevice; -use crate::camera_backend::{v4l2::V4l2HostDev, CameraHostdevOps}; +use crate::camera_backend::{v4l2::V4l2CameraBackend, CameraHostdevOps}; use crate::usb::config::*; use crate::usb::descriptor::*; use crate::usb::{ @@ -452,8 +452,8 @@ impl UsbCamera { fn set_hostdev(&mut self) -> Result<()> { match self.backend_type { CamBackendType::V4l2 => { - let mut hostdev = V4l2HostDev::new(self.backend_path.clone()); - hostdev.realize()?; + let hostdev = + V4l2CameraBackend::new(self.id.clone(), self.backend_path.clone(), None)?; self.hostdev = Some(Box::new(hostdev)); } _ => { -- Gitee From b121e1adf427f30503092bf4bb629b63d93f7e5b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 22:29:45 +0800 Subject: [PATCH 1037/2187] usb: implement control plane for usb camera Implement control plane for usb camera. Signed-off-by: zhouli57 --- devices/src/camera_backend/mod.rs | 28 +++ devices/src/usb/camera.rs | 358 ++++++++++++++++++++++++------ machine/src/lib.rs | 2 +- machine_manager/src/config/usb.rs | 16 +- 4 files changed, 332 insertions(+), 72 deletions(-) diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 210e1829c..7a419ced2 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -144,3 +144,31 @@ pub trait CameraHostdevOps: Send + Sync { /// Register broken callback which is called when backend is broken. fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } + +pub fn get_format_by_index(format_index: u8, frame_index: u8) -> Result { + let fmttype = if format_index == 1 { + FmtType::Mjpg + } else if format_index == 2 { + FmtType::Uncompressed + } else { + bail!("Invalid format index {}", format_index); + }; + + let width_height_list = [(960, 540), (1280, 720), (640, 480)]; + let fps_list = [5, 10, 30]; + if width_height_list.get((frame_index - 1) as usize).is_none() { + bail!("Invalid frame index {}", frame_index); + } + let fps = if format_index == 1 { + 30 + } else { + fps_list[(frame_index - 1) as usize] + }; + + Ok(CamBasicFmt { + width: width_height_list[(frame_index - 1) as usize].0, + height: width_height_list[(frame_index - 1) as usize].1, + fmttype, + fps, + }) +} diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index d4f0668e6..c77ed1485 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -12,24 +12,37 @@ //! Emulated camera device that based on UVC(USB video class) protocol. -use anyhow::{bail, Result}; -use log::{debug, error}; -use once_cell::sync::Lazy; -use util::byte_code::ByteCode; - +use std::collections::LinkedList; +use std::os::unix::prelude::{AsRawFd, RawFd}; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; + +use anyhow::{bail, Context, Result}; +use log::{debug, error, info}; +use once_cell::sync::Lazy; use strum::EnumCount; use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; + +use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; -use super::config::USB_SPEED_HIGH; use super::xhci::xhci_controller::XhciDevice; -use crate::camera_backend::{v4l2::V4l2CameraBackend, CameraHostdevOps}; +use crate::camera_backend::{ + get_format_by_index, v4l2::V4l2CameraBackend, CamBasicFmt, CameraBrokenCallback, + CameraHostdevOps, CameraNotifyCallback, +}; use crate::usb::config::*; use crate::usb::descriptor::*; use crate::usb::{ UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use machine_manager::config::{CamBackendType, UsbCameraConfig}; // CRC16 of "STRATOVIRT" const UVC_VENDOR_ID: u16 = 0xB74C; @@ -46,6 +59,7 @@ const UNIT_ID_PROCESSING_UNIT: u8 = 5; const ENDPOINT_ID_CONTROL: u8 = 0x1; const ENDPOINT_ID_STREAMING: u8 = 0x2; +const VS_INTERFACE_NUM: u8 = 1; // According to UVC specification 1.5 // A.2. Video Interface Subclass Codes @@ -68,17 +82,34 @@ const VS_INPUT_HEADER: u8 = 0x01; const VS_FORMAT_UNCOMPRESSED: u8 = 0x04; const VS_FRAME_UNCOMPRESSED: u8 = 0x05; const VS_COLORFORMAT: u8 = 0x0D; +// A.8. Video Class-Specific Request Codes +const SET_CUR: u8 = 0x1; +const GET_CUR: u8 = 0x81; +const GET_MIN: u8 = 0x82; +const GET_MAX: u8 = 0x83; +const GET_INFO: u8 = 0x86; +const GET_DEF: u8 = 0x87; +const UVC_FID: u8 = 1; +// A.9.8. VideoStreaming Interface Control Selectors +const VS_PROBE_CONTROL: u8 = 1; +const VS_COMMIT_CONTROL: u8 = 2; + +const MAX_PAYLOAD: u32 = 1024 * 3 - 2; +const FPS_30_INTERVAL: u32 = 333333; +const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; -#[allow(dead_code)] pub struct UsbCamera { - id: String, // uniq device id - usb_device: UsbDevice, // general usb device object - frame: Vec, // video frame data - max_pkt_size: u32, // the max size of the packet that can be separated - vs_eps: Vec, // the endpoints that the VS uses - backend_type: CamBackendType, // backend interface, eg. v4l2 - backend_path: String, // backend interface file, eg. /dev/video0 - hostdev: Option>, // backend device, eg. v4l2, demo, etc. + id: String, // uniq device id + usb_device: UsbDevice, // general usb device object + vs_control: VideoStreamingControl, // video stream control info + camera_fd: Arc, // camera io fd + camera_dev: Arc>, // backend device + packet_list: Arc>>>>, // packet to be processed + payload: Arc>, // uvc payload + listening: bool, // if the camera is listening or not + broken: Arc, // if the device broken or not + iothread: Option, + delete_evts: Vec, } #[derive(Debug, EnumCountMacro, EnumIter)] @@ -418,91 +449,251 @@ static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { }) }); -static DESC_DEVICE_QUALIFIER_CAMERA: Lazy> = Lazy::new(|| { - Arc::new(UsbDescDeviceQualifier { - qualifier_desc: UsbDeviceQualifierDescriptor { - bLength: 0xa, - bDescriptorType: USB_DT_DEVICE_QUALIFIER, - bcdUSB: DESC_DEVICE_CAMERA.device_desc.bcdUSB, - bDeviceClass: DESC_DEVICE_CAMERA.device_desc.bDeviceClass, - bDeviceSubClass: DESC_DEVICE_CAMERA.device_desc.bDeviceSubClass, - bDeviceProtocol: DESC_DEVICE_CAMERA.device_desc.bDeviceProtocol, - bMaxPacketSize0: DESC_DEVICE_CAMERA.device_desc.bMaxPacketSize0, - bNumConfigurations: DESC_DEVICE_CAMERA.device_desc.bNumConfigurations, - bReserved: 0, - }, - }) -}); +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct VideoStreamingControl { + pub bmHint: u16, + pub bFormatIndex: u8, + pub bFrameIndex: u8, + pub dwFrameInterval: u32, + pub wKeyFrameRate: u16, + pub wPFrameRate: u16, + pub wCompQuality: u16, + pub wCompWindowSize: u16, + pub wDelay: u16, + pub dwMaxVideoFrameSize: u32, + pub dwMaxPayloadTransferSize: u32, +} + +impl ByteCode for VideoStreamingControl {} + +impl VideoStreamingControl { + fn reset(&mut self) { + self.bFormatIndex = 1; + self.bFormatIndex = 2; + self.dwFrameInterval = FPS_30_INTERVAL; + self.dwMaxVideoFrameSize = FRAME_SIZE_1280_720; + self.dwMaxPayloadTransferSize = MAX_PAYLOAD; + } +} -#[allow(dead_code)] impl UsbCamera { - pub fn new(config: UsbCameraConfig) -> Self { - UsbCamera { + pub fn new(config: UsbCameraConfig) -> Result { + let cam = match config.backend { + CamBackendType::V4l2 => V4l2CameraBackend::new( + config.id.clone().unwrap(), + config.path.clone().with_context(|| { + ConfigError::FieldIsMissing("path".to_string(), "V4L2".to_string()) + })?, + config.iothread.clone(), + )?, + CamBackendType::Demo => bail!("Not supported type"), + }; + let camera = Arc::new(Mutex::new(cam)); + Ok(Self { id: config.id.unwrap(), usb_device: UsbDevice::new(), - frame: Vec::new(), - max_pkt_size: 0, - vs_eps: Vec::new(), - backend_type: config.backend, - backend_path: config.path.unwrap(), - hostdev: None, - } + vs_control: VideoStreamingControl::default(), + camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + camera_dev: camera, + packet_list: Arc::new(Mutex::new(LinkedList::new())), + payload: Arc::new(Mutex::new(UvcPayload::new())), + listening: false, + broken: Arc::new(AtomicBool::new(false)), + iothread: config.iothread, + delete_evts: Vec::new(), + }) } - fn set_hostdev(&mut self) -> Result<()> { - match self.backend_type { - CamBackendType::V4l2 => { - let hostdev = - V4l2CameraBackend::new(self.id.clone(), self.backend_path.clone(), None)?; - self.hostdev = Some(Box::new(hostdev)); - } - _ => { - bail!("Unsupported backend yet."); + fn register_cb(&mut self) { + let clone_fd = self.camera_fd.clone(); + let notify_cb: CameraNotifyCallback = Arc::new(move || { + if let Err(e) = clone_fd.write(1) { + error!( + "Failed to write camera device fd {} {:?}", + clone_fd.as_raw_fd(), + e + ); } - } + }); + let clone_broken = self.broken.clone(); + let clone_id = self.id.clone(); + let broken_cb: CameraBrokenCallback = Arc::new(move || { + clone_broken.store(true, Ordering::SeqCst); + error!("USB Camera {} device broken", clone_id); + }); + let mut locked_camera = self.camera_dev.lock().unwrap(); + locked_camera.register_notify_cb(notify_cb); + locked_camera.register_broken_cb(broken_cb); + } + fn activate(&mut self, fmt: &CamBasicFmt) -> Result<()> { + info!("USB Camera {} activate", self.id); + self.camera_dev.lock().unwrap().reset(); + self.payload.lock().unwrap().reset(); + let mut locked_camera = self.camera_dev.lock().unwrap(); + locked_camera.set_fmt(fmt)?; + locked_camera.video_stream_on()?; + drop(locked_camera); Ok(()) } - fn read_backend_video_frame() -> Result<()> { + fn deactivate(&mut self) -> Result<()> { + info!("USB Camera {} deactivate", self.id); + if self.broken.load(Ordering::Acquire) { + info!("USB Camera {} broken when deactivate, reset it.", self.id); + self.camera_dev.lock().unwrap().reset(); + self.broken.store(false, Ordering::SeqCst); + } else { + self.camera_dev.lock().unwrap().video_stream_off()?; + } + self.packet_list.lock().unwrap().clear(); Ok(()) } - fn send_video_frame_to_guest() -> Result<()> { + fn handle_uvc_request( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + ) -> Result<()> { + let inter_num = (device_req.index & 0xff) as u8; + match device_req.request_type { + USB_INTERFACE_IN_REQUEST => { + if device_req.request == USB_REQUEST_GET_STATUS { + self.usb_device.data_buf[0] = 0; + self.usb_device.data_buf[1] = 0; + packet.actual_length = 2; + return Ok(()); + } + } + USB_INTERFACE_OUT_REQUEST => { + if device_req.request == USB_REQUEST_SET_FEATURE { + return Ok(()); + } + } + USB_INTERFACE_CLASS_IN_REQUEST => { + if inter_num == VS_INTERFACE_NUM { + return self.do_vs_interface_in_request(packet, device_req); + } + } + USB_INTERFACE_CLASS_OUT_REQUEST => { + if inter_num == VS_INTERFACE_NUM { + return self.do_vs_interface_out_request(device_req); + } + } + USB_ENDPOINT_OUT_REQUEST => { + if device_req.request == USB_REQUEST_CLEAR_FEATURE { + return self + .deactivate() + .with_context(|| "Failed to deactivate device"); + } + } + _ => (), + } + bail!("Unknown UVC request {:?}", device_req.request); + } + + fn do_vs_interface_in_request( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + ) -> Result<()> { + match device_req.request { + GET_INFO => { + self.usb_device.data_buf[0] = 1 | 2; + packet.actual_length = 1; + } + GET_CUR | GET_MIN | GET_MAX | GET_DEF => { + self.video_stream_in_request(packet, device_req)?; + } + _ => { + bail!( + "Unsupported VS interface in request {:?}", + device_req.request + ); + } + } Ok(()) } - fn set_control() -> Result<()> { + fn video_stream_in_request( + &mut self, + packet: &mut UsbPacket, + device_req: &UsbDeviceRequest, + ) -> Result<()> { + let cs = (device_req.value >> 8) as u8; + if cs != VS_PROBE_CONTROL { + bail!("Invalid VS Control Selector {}", cs); + } + let len = self.vs_control.as_bytes().len(); + self.usb_device.data_buf[0..len].copy_from_slice(self.vs_control.as_bytes()); + packet.actual_length = len as u32; Ok(()) } -} -impl Default for UsbCamera { - fn default() -> Self { - Self::new(UsbCameraConfig::new()) + fn do_vs_interface_out_request(&mut self, device_req: &UsbDeviceRequest) -> Result<()> { + let mut vs_control = VideoStreamingControl::default(); + let len = vs_control.as_mut_bytes().len(); + vs_control + .as_mut_bytes() + .copy_from_slice(&self.usb_device.data_buf[0..len]); + let cs = (device_req.value >> 8) as u8; + debug!("VideoStreamingControl {} {:?}", cs, vs_control); + match device_req.request { + SET_CUR => match cs { + VS_PROBE_CONTROL => { + self.vs_control.bFormatIndex = vs_control.bFormatIndex; + self.vs_control.bFrameIndex = vs_control.bFrameIndex; + self.vs_control.dwMaxVideoFrameSize = vs_control.dwMaxVideoFrameSize; + self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; + } + VS_COMMIT_CONTROL => { + self.vs_control.bFormatIndex = vs_control.bFormatIndex; + self.vs_control.bFrameIndex = vs_control.bFrameIndex; + self.vs_control.dwMaxVideoFrameSize = vs_control.dwMaxVideoFrameSize; + self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; + let fmt = get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; + self.activate(&fmt) + .with_context(|| "Failed to activate device")?; + } + _ => { + bail!("Invalid VS control selector {}", cs); + } + }, + _ => { + bail!("Unsupported VS interface out request {:?}", device_req); + } + } + Ok(()) } } impl UsbDeviceOps for UsbCamera { fn realize(mut self) -> Result>> { - self.set_hostdev()?; - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_HIGH; + self.usb_device.speed = USB_SPEED_SUPER; let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); self.usb_device .init_descriptor(DESC_DEVICE_CAMERA.clone(), s)?; - self.usb_device - .init_device_qualifier_descriptor(DESC_DEVICE_QUALIFIER_CAMERA.clone())?; + // TODO: list format to construct descriptor. + self.camera_dev.lock().unwrap().list_format()?; + self.register_cb(); let camera = Arc::new(Mutex::new(self)); - Ok(camera) } - fn reset(&mut self) {} + fn reset(&mut self) { + info!("Camera {} device reset", self.id); + self.usb_device.addr = 0; + self.vs_control.reset(); + self.payload.lock().unwrap().reset(); + self.camera_dev.lock().unwrap().reset(); + self.packet_list.lock().unwrap().clear(); + self.broken.store(false, Ordering::SeqCst); + } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { - debug!("Into camera handle_control"); let mut locked_packet = packet.lock().unwrap(); match self .usb_device @@ -511,15 +702,20 @@ impl UsbDeviceOps for UsbCamera { Ok(handled) => { if handled { debug!("Camera control handled by descriptor, return directly."); - } else { - error!("Camera: unhandled control msg: {}", device_req.request_type); + return; } } Err(e) => { error!("Camera descriptor error {:?}", e); locked_packet.status = UsbPacketStatus::Stall; + return; } } + + if let Err(e) = self.handle_uvc_request(&mut locked_packet, device_req) { + error!("Camera uvc descriptor error {:?}", e); + locked_packet.status = UsbPacketStatus::Stall; + } } fn handle_data(&mut self, _p: &Arc>) {} @@ -547,6 +743,30 @@ impl UsbDeviceOps for UsbCamera { } } +/// UVC payload +struct UvcPayload { + header: Vec, + frame_offset: usize, + payload_offset: usize, +} + +impl UvcPayload { + fn new() -> Self { + Self { + header: vec![2, 0], + frame_offset: 0, + payload_offset: 0, + } + } + + fn reset(&mut self) { + self.header[0] = 2; + self.header[1] = 0; + self.frame_offset = 0; + self.payload_offset = 0; + } +} + #[cfg(test)] mod test { use super::*; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e7077d14d..e6a81e87e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1263,7 +1263,7 @@ pub trait MachineOps { #[cfg(not(target_env = "musl"))] fn add_usb_camera(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_camera(cfg_args)?; - let camera = UsbCamera::new(device_cfg); + let camera = UsbCamera::new(device_cfg)?; let camera = camera.realize()?; self.attach_usb_to_xhci_controller(vm_config, camera)?; diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 2e0d6c544..12f497950 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -176,13 +176,19 @@ pub const CAM_OPT_STR_BACKEND_TYPES: [&str; CamBackendType::COUNT] = ["v4l2", "d pub fn parse_usb_camera(conf: &str) -> Result { let mut cmd_parser = CmdParser::new("usb-camera"); - cmd_parser.push("").push("id").push("backend").push("path"); + cmd_parser + .push("") + .push("id") + .push("backend") + .push("path") + .push("iothread"); cmd_parser.parse(conf)?; let mut dev = UsbCameraConfig::new(); dev.id = cmd_parser.get_value::("id")?; dev.backend = cmd_parser.get_value::("backend")?.unwrap(); dev.path = cmd_parser.get_value::("path")?; + dev.iothread = cmd_parser.get_value::("iothread")?; dev.check()?; Ok(dev) @@ -200,6 +206,7 @@ pub struct UsbCameraConfig { pub id: Option, pub backend: CamBackendType, pub path: Option, + pub iothread: Option, } impl UsbCameraConfig { @@ -208,6 +215,7 @@ impl UsbCameraConfig { id: None, backend: CamBackendType::Demo, path: None, + iothread: None, } } } @@ -222,7 +230,11 @@ impl ConfigCheck for UsbCameraConfig { fn check(&self) -> Result<()> { // Note: backend has already been checked during args parsing. check_id(self.id.clone(), "usb-camera")?; - check_camera_path(self.path.clone()) + check_camera_path(self.path.clone())?; + if self.iothread.is_some() { + check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; + } + Ok(()) } } -- Gitee From 59b8eb1c6f2e1cb4d4b6129b1a4f3d3e3978315b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Apr 2023 22:26:31 +0800 Subject: [PATCH 1038/2187] usb: implement data plane for usb camera Implement data plane for usb camera. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 215 +++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index c77ed1485..fbdd72a22 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -28,6 +28,7 @@ use vmm_sys_util::eventfd::EventFd; use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::aio::{iov_discard_front_direct, Iovec}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -536,6 +537,26 @@ impl UsbCamera { locked_camera.set_fmt(fmt)?; locked_camera.video_stream_on()?; drop(locked_camera); + self.register_camera_fd()?; + Ok(()) + } + + fn register_camera_fd(&mut self) -> Result<()> { + if self.listening { + return Ok(()); + } + let cam_handler = Arc::new(Mutex::new(CameraIoHander::new( + &self.camera_fd, + &self.packet_list, + &self.camera_dev, + &self.payload, + ))); + register_event_helper( + EventNotifierHelper::internal_notifiers(cam_handler), + self.iothread.as_ref(), + &mut self.delete_evts, + )?; + self.listening = true; Ok(()) } @@ -548,10 +569,20 @@ impl UsbCamera { } else { self.camera_dev.lock().unwrap().video_stream_off()?; } + self.unregister_camera_fd()?; self.packet_list.lock().unwrap().clear(); Ok(()) } + fn unregister_camera_fd(&mut self) -> Result<()> { + if !self.listening { + return Ok(()); + } + unregister_event_helper(self.iothread.as_ref(), &mut self.delete_evts)?; + self.listening = false; + Ok(()) + } + fn handle_uvc_request( &mut self, packet: &mut UsbPacket, @@ -686,6 +717,9 @@ impl UsbDeviceOps for UsbCamera { fn reset(&mut self) { info!("Camera {} device reset", self.id); self.usb_device.addr = 0; + if let Err(e) = self.unregister_camera_fd() { + error!("Failed to unregister fd when reset {:?}", e); + } self.vs_control.reset(); self.payload.lock().unwrap().reset(); self.camera_dev.lock().unwrap().reset(); @@ -718,7 +752,29 @@ impl UsbDeviceOps for UsbCamera { } } - fn handle_data(&mut self, _p: &Arc>) {} + fn handle_data(&mut self, packet: &Arc>) { + if packet.lock().unwrap().ep_number == ENDPOINT_ID_STREAMING { + packet.lock().unwrap().is_async = true; + let mut locked_list = self.packet_list.lock().unwrap(); + locked_list.push_back(packet.clone()); + // Notify the camera to deal with the request. + if let Err(e) = self.camera_fd.write(1) { + error!( + "Failed to write fd when handle data for {} {:?}", + self.id, e + ); + // SAFETY: packet is push before, and no other thread modify the list. + let p = locked_list.pop_back().unwrap(); + let mut locked_p = p.lock().unwrap(); + locked_p.status = UsbPacketStatus::Stall; + // Async request failed, let controller report the error. + locked_p.is_async = false; + } + } else { + error!("Invalid ep number {}", packet.lock().unwrap().ep_number); + packet.lock().unwrap().status = UsbPacketStatus::Stall; + } + } fn device_id(&self) -> String { self.id.clone() @@ -765,6 +821,163 @@ impl UvcPayload { self.frame_offset = 0; self.payload_offset = 0; } + + fn get_frame_data_size(&self, current_frame_size: usize, iov_size: u64) -> Result { + let mut frame_data_size = iov_size; + let header_len = self.header.len(); + // Within the scope of the frame. + if self.frame_offset + frame_data_size as usize >= current_frame_size { + if self.frame_offset > current_frame_size { + bail!( + "Invalid frame offset {} {}", + self.frame_offset, + current_frame_size + ); + } + frame_data_size = (current_frame_size - self.frame_offset) as u64; + } + // Within the scope of the payload. + if self.payload_offset + frame_data_size as usize >= MAX_PAYLOAD as usize { + if self.payload_offset > MAX_PAYLOAD as usize { + bail!( + "Invalid payload offset {} {}", + self.payload_offset, + MAX_PAYLOAD + ); + } + frame_data_size = MAX_PAYLOAD as u64 - self.payload_offset as u64; + } + // payload start, reserve the header. + if self.payload_offset == 0 && frame_data_size + header_len as u64 > iov_size { + if iov_size <= header_len as u64 { + bail!("Invalid iov size {}", iov_size); + } + frame_data_size = iov_size as u64 - header_len as u64; + } + Ok(frame_data_size) + } + + fn next_frame(&mut self) { + self.frame_offset = 0; + self.payload_offset = 0; + self.header[1] ^= UVC_FID; + } +} + +/// Camere handler for copying frame data to usb packet. +struct CameraIoHander { + camera: Arc>, + fd: Arc, + packet_list: Arc>>>>, + payload: Arc>, +} + +impl CameraIoHander { + fn new( + fd: &Arc, + list: &Arc>>>>, + camera: &Arc>, + payload: &Arc>, + ) -> Self { + CameraIoHander { + camera: camera.clone(), + fd: fd.clone(), + packet_list: list.clone(), + payload: payload.clone(), + } + } + + fn handle_io(&mut self) { + const REQUEST_LIMIT: u32 = 100; + for _ in 0..REQUEST_LIMIT { + let len = self.camera.lock().unwrap().get_frame_size(); + if len == 0 { + break; + } + let mut locked_list = self.packet_list.lock().unwrap(); + if locked_list.is_empty() { + break; + } + // SAFETY: packet list is not empty. + let p = locked_list.pop_front().unwrap(); + drop(locked_list); + let mut locked_p = p.lock().unwrap(); + if let Err(e) = self.handle_payload(&mut locked_p) { + error!("Failed handle uvc data {:?}", e); + locked_p.status = UsbPacketStatus::IoError; + } + if let Some(transfer) = locked_p.xfer_ops.as_ref() { + if let Some(ops) = transfer.clone().upgrade() { + drop(locked_p); + ops.lock().unwrap().submit_transfer(); + } + } + } + } + + fn handle_payload(&mut self, pkt: &mut UsbPacket) -> Result<()> { + let mut locked_camera = self.camera.lock().unwrap(); + let current_frame_size = locked_camera.get_frame_size(); + let mut locked_payload = self.payload.lock().unwrap(); + let header_len = locked_payload.header.len(); + let pkt_size = pkt.get_iovecs_size(); + let frame_data_size = locked_payload.get_frame_data_size(current_frame_size, pkt_size)?; + if frame_data_size == 0 { + bail!( + "Invalid frame data size, frame offset {} payload offset {} packet size {}", + locked_payload.frame_offset, + locked_payload.payload_offset, + pkt.get_iovecs_size(), + ); + } + let mut iovecs: &mut [Iovec] = &mut pkt.iovecs; + if locked_payload.payload_offset == 0 { + // Payload start, add header. + pkt.transfer_packet(&mut locked_payload.header, header_len); + locked_payload.payload_offset += header_len as usize; + iovecs = iov_discard_front_direct(&mut pkt.iovecs, pkt.actual_length as u64) + .with_context(|| format!("Invalid iov size {}", pkt_size))?; + } + let copyed = locked_camera.get_frame( + iovecs, + locked_payload.frame_offset, + frame_data_size as usize, + )?; + pkt.actual_length += copyed as u32; + debug!( + "Camera handle payload, frame_offset {} payloadoffset {} data_size {} copyed {}", + locked_payload.frame_offset, locked_payload.payload_offset, frame_data_size, copyed + ); + locked_payload.frame_offset += frame_data_size as usize; + locked_payload.payload_offset += frame_data_size as usize; + + if locked_payload.payload_offset >= MAX_PAYLOAD as usize { + locked_payload.payload_offset = 0; + } + if locked_payload.frame_offset >= current_frame_size { + locked_payload.next_frame(); + locked_camera.next_frame()?; + } + Ok(()) + } +} + +impl EventNotifierHelper for CameraIoHander { + fn internal_notifiers(io_handler: Arc>) -> Vec { + let cloned_io_handler = io_handler.clone(); + let handler: Rc = Rc::new(move |_event, fd: RawFd| { + read_fd(fd); + cloned_io_handler.lock().unwrap().handle_io(); + None + }); + vec![EventNotifier::new( + NotifierOperation::AddShared, + io_handler.lock().unwrap().fd.as_raw_fd(), + None, + EventSet::IN, + vec![handler], + )] + } } #[cfg(test)] -- Gitee From 9448edab6d6c122d932676242b0ad04337379ff7 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 21 Apr 2023 09:28:57 +0800 Subject: [PATCH 1039/2187] camera: support hotplug Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 6 ++++++ machine/src/standard_vm/mod.rs | 18 +++++++++++++++++- machine_manager/src/config/usb.rs | 6 +++--- machine_manager/src/qmp/qmp_schema.rs | 2 ++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index fbdd72a22..18d27112f 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -714,6 +714,12 @@ impl UsbDeviceOps for UsbCamera { Ok(camera) } + fn unrealize(&mut self) -> Result<()> { + info!("Camera {} unrealize", self.id); + self.camera_dev.lock().unwrap().reset(); + Ok(()) + } + fn reset(&mut self) { info!("Camera {} device reset", self.id); self.usb_device.addr = 0; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index a808087cd..f21ae68ba 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -964,6 +964,22 @@ impl StdMachine { "usb-tablet" => { self.add_usb_tablet(&mut locked_vmconfig, &cfg_args)?; } + "usb-camera" => { + let mut cfg_args = format!( + "id={},backend={},path={}", + args.id, + args.backend + .as_ref() + .with_context(|| "No backend for usb camera.")?, + args.path + .as_ref() + .with_context(|| "No path for usb camera.")?, + ); + if let Some(iothread) = args.iothread.as_ref() { + cfg_args = format!("{},iothread={}", cfg_args, iothread); + }; + self.add_usb_camera(&mut locked_vmconfig, &cfg_args)?; + } _ => { bail!("Invalid usb device driver '{}'", driver); } @@ -1176,7 +1192,7 @@ impl DeviceInterface for StdMachine { } } #[cfg(not(target_env = "musl"))] - "usb-kbd" | "usb-tablet" => { + "usb-kbd" | "usb-tablet" | "usb-camera" => { if let Err(e) = self.plug_usb_device(args.as_ref()) { error!("{:?}", e); return Response::create_error_response( diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 12f497950..41a0279ca 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -152,14 +152,14 @@ pub fn parse_usb_tablet(conf: &str) -> Result { Ok(dev) } -#[derive(Clone, Copy, Debug, EnumCountMacro, EnumIter)] +#[derive(Clone, Copy, Debug, EnumCountMacro, EnumIter, PartialEq, Eq)] pub enum CamBackendType { V4l2, Demo, } impl FromStr for CamBackendType { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { for i in CamBackendType::iter() { @@ -168,7 +168,7 @@ impl FromStr for CamBackendType { } } - Err(()) + Err(anyhow!("Unknown camera backend type")) } } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 6c630d0f0..968ac7a9a 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -602,6 +602,8 @@ pub struct device_add { #[serde(rename = "queue-size")] pub queue_size: Option, pub port: Option, + pub backend: Option, + pub path: Option, } pub type DeviceAddArgument = device_add; -- Gitee From aea60cd879fa1406332a95cc19de1fc9afd18d8f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 3 May 2023 15:13:58 +0800 Subject: [PATCH 1040/2187] bug-fix: Cleanup existing socket files It will cause a failure if bind a existing socket file. So remove it before bind a socket path. Signed-off-by: Jinhao Gao --- devices/src/legacy/chardev.rs | 2 ++ machine/src/lib.rs | 3 ++- machine_manager/src/cmdline.rs | 2 ++ machine_manager/src/temp_cleaner.rs | 28 +++++++++++++++++++--------- util/src/file.rs | 14 ++++++++++++-- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 7cd35b375..961d7b3ce 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -26,6 +26,7 @@ use machine_manager::{ config::{ChardevConfig, ChardevType}, temp_cleaner::TempCleaner, }; +use util::file::clear_file; use util::loop_context::{ gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -124,6 +125,7 @@ impl Chardev { path ); } + clear_file(path.clone())?; let sock = UnixListener::bind(path.clone()) .with_context(|| format!("Failed to bind socket for chardev, path:{}", path))?; self.listener = Some(sock); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 583b0888f..38c448763 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -36,7 +36,7 @@ use machine_manager::config::scream::parse_scream; use machine_manager::event_loop::EventLoop; #[cfg(not(target_env = "musl"))] use ui::console::{get_run_stage, VmRunningStage}; -use util::file::{lock_file, unlock_file}; +use util::file::{clear_file, lock_file, unlock_file}; #[cfg(not(target_env = "musl"))] use vmm_sys_util::eventfd::EventFd; @@ -1836,6 +1836,7 @@ fn start_incoming_migration(vm: &Arc>) -> Re .with_context(|| "Failed to start VM.")?; } MigrateMode::Unix => { + clear_file(path.clone())?; let listener = UnixListener::bind(&path)?; let (mut sock, _) = listener.accept()?; remove_file(&path)?; diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 2b28694b1..61f343047 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -14,6 +14,7 @@ use std::os::unix::net::UnixListener; use anyhow::{bail, Context, Result}; use util::arg_parser::{Arg, ArgMatches, ArgParser}; +use util::file::clear_file; use util::unix::{limit_permission, parse_unix_uri}; use crate::{ @@ -630,6 +631,7 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< } fn bind_socket(path: String) -> Result { + clear_file(path.clone())?; let listener = UnixListener::bind(&path) .with_context(|| format!("Failed to bind socket file {}", &path))?; // Add file to temporary pool, so it could be cleaned when vm exits. diff --git a/machine_manager/src/temp_cleaner.rs b/machine_manager/src/temp_cleaner.rs index c9d7ae63c..0198808f0 100644 --- a/machine_manager/src/temp_cleaner.rs +++ b/machine_manager/src/temp_cleaner.rs @@ -13,6 +13,7 @@ use std::collections::HashMap; use std::fs; use std::io::Write; +use std::path::Path; use std::sync::Arc; static mut GLOBAL_TEMP_CLEANER: Option = None; @@ -69,18 +70,27 @@ impl TempCleaner { fn clean_files(&mut self) { while let Some(path) = self.paths.pop() { - if let Err(ref e) = fs::remove_file(&path) { - write!( - &mut std::io::stderr(), - "Failed to delete console / socket file:{} :{} \r\n", - &path, - e - ) - .expect("Failed to write to stderr"); + if Path::new(&path).exists() { + if let Err(ref e) = fs::remove_file(&path) { + write!( + &mut std::io::stderr(), + "Failed to delete console / socket file:{} :{} \r\n", + &path, + e + ) + .expect("Failed to write to stderr"); + } else { + write!( + &mut std::io::stdout(), + "Delete file: {} successfully.\r\n", + &path + ) + .expect("Failed to write to stdout"); + } } else { write!( &mut std::io::stdout(), - "Delete file: {} successfully.\r\n", + "file: {} has been removed \r\n", &path ) .expect("Failed to write to stdout"); diff --git a/util/src/file.rs b/util/src/file.rs index a0855eaba..1c29ab7bb 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::fs::{File, OpenOptions}; +use std::fs::{remove_file, File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::AsRawFd; +use std::path::Path; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Context, Ok, Result}; const MIN_FILE_ALIGN: u32 = 512; const MAX_FILE_ALIGN: u32 = 4096; @@ -127,3 +128,12 @@ pub fn unlock_file(file: &File, path: &str) -> Result<()> { Ok(()) } + +pub fn clear_file(path: String) -> Result<()> { + if Path::new(&path).exists() { + remove_file(&path) + .with_context(|| format!("File {} exists, but failed to remove it.", &path))?; + } + + Ok(()) +} -- Gitee From 790a21d93536ecdef1f3f195ac7e757c3f1f4bd2 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Thu, 4 May 2023 21:22:56 +0800 Subject: [PATCH 1041/2187] Fixed compilation issues caused by missing gtk-devel libraries Signed-off-by: Yan Wen --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 1057d9ecf..8e1ca4121 100644 --- a/Makefile +++ b/Makefile @@ -20,3 +20,4 @@ yum-deps: @yum install cyrus-sasl-devel @yum install pulseaudio @yum install clang + @yum install gtk3-devel -- Gitee From e9a44246063e4ae48e51e883b5d6019aa8aa3630 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 10 Apr 2023 09:31:02 +0800 Subject: [PATCH 1042/2187] camera: support video format negotiation Generate the video format/frame descriptors to guest with the result got from v4l2 query result. Signed-off-by: Zhang Bo --- devices/src/camera_backend/mod.rs | 72 +- devices/src/camera_backend/v4l2.rs | 74 ++- devices/src/usb/camera.rs | 759 +++++++++++++++------- devices/src/usb/camera_media_type_guid.rs | 42 ++ devices/src/usb/descriptor.rs | 14 - devices/src/usb/mod.rs | 1 + util/src/v4l2.rs | 9 - 7 files changed, 671 insertions(+), 300 deletions(-) create mode 100644 devices/src/usb/camera_media_type_guid.rs diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 7a419ced2..72e19cb97 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -16,9 +16,8 @@ pub mod demo; pub mod v4l2; -use std::sync::Arc; - use anyhow::{bail, Result}; +use std::sync::Arc; use util::aio::Iovec; @@ -26,7 +25,7 @@ use util::aio::Iovec; pub const INTERVALS_PER_SEC: u32 = 10_000_000; #[allow(dead_code)] -#[derive(Default)] +#[derive(Default, Clone)] pub struct CamFmt { // Basic 3 configurations: frame size, format, frame frequency. basic_fmt: CamBasicFmt, @@ -44,7 +43,7 @@ impl CamFmt { } } -#[derive(Default, Debug)] +#[derive(Clone, Copy, Default, Debug)] pub struct CamBasicFmt { width: u32, height: u32, @@ -62,7 +61,7 @@ impl CamBasicFmt { } #[allow(dead_code)] -#[derive(Default)] +#[derive(Default, Clone)] pub struct CamPUFmt { bright: u64, contrast: u64, @@ -72,37 +71,52 @@ pub struct CamPUFmt { } #[allow(dead_code)] -#[derive(Default)] +#[derive(Default, Clone)] pub struct CamLensFmt { focus: u64, zoom: u64, // TODO: to be extended. } -#[derive(Debug)] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] pub enum FmtType { - Uncompressed = 0, + Yuy2 = 0, + Rgb565, Mjpg, } impl Default for FmtType { fn default() -> Self { - FmtType::Uncompressed + FmtType::Yuy2 } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct CameraFrame { pub width: u32, pub height: u32, + pub index: u8, pub interval: u32, } +#[derive(Clone)] pub struct CameraFormatList { pub format: FmtType, + pub fmt_index: u8, pub frame: Vec, } +#[macro_export] +macro_rules! video_fourcc { + ($a:expr, $b:expr, $c:expr, $d:expr) => { + $a as u32 | (($b as u32) << 8) | (($c as u32) << 16) | (($d as u32) << 24) + }; +} + +pub const PIXFMT_RGB565: u32 = video_fourcc!('R', 'G', 'B', 'P'); +pub const PIXFMT_YUYV: u32 = video_fourcc!('Y', 'U', 'Y', 'V'); +pub const PIXFMT_MJPG: u32 = video_fourcc!('M', 'J', 'P', 'G'); + /// Callback function which is called when frame data is coming. pub type CameraNotifyCallback = Arc; @@ -112,9 +126,14 @@ pub type CameraBrokenCallback = Arc; pub trait CameraHostdevOps: Send + Sync { fn init(&self) -> Result<()>; fn is_camera(&self) -> Result; - fn get_fmt(&self) -> Result<()>; + + /// Get format list. + fn get_fmt(&self) -> Result>; + /// Set a specific format. fn set_fmt(&mut self, fmt: &CamBasicFmt) -> Result<()>; + + /// Set control capabilities and properties. fn set_ctl(&self) -> Result<()>; // Turn stream on to start to receive frame buffer. @@ -135,6 +154,9 @@ pub trait CameraHostdevOps: Send + Sync { /// Copy frame data to iovecs. fn get_frame(&self, iovecs: &[Iovec], frame_offset: usize, len: usize) -> Result; + /// Get format/frame info including width/height/interval/fmt according to format/frame index. + fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result; + /// Get next frame when current frame is read complete. fn next_frame(&mut self) -> Result<()>; @@ -144,31 +166,3 @@ pub trait CameraHostdevOps: Send + Sync { /// Register broken callback which is called when backend is broken. fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } - -pub fn get_format_by_index(format_index: u8, frame_index: u8) -> Result { - let fmttype = if format_index == 1 { - FmtType::Mjpg - } else if format_index == 2 { - FmtType::Uncompressed - } else { - bail!("Invalid format index {}", format_index); - }; - - let width_height_list = [(960, 540), (1280, 720), (640, 480)]; - let fps_list = [5, 10, 30]; - if width_height_list.get((frame_index - 1) as usize).is_none() { - bail!("Invalid frame index {}", frame_index); - } - let fps = if format_index == 1 { - 30 - } else { - fps_list[(frame_index - 1) as usize] - }; - - Ok(CamBasicFmt { - width: width_height_list[(frame_index - 1) as usize].0, - height: width_height_list[(frame_index - 1) as usize].1, - fmttype, - fps, - }) -} diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index b82397dd2..f35f2fb46 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -34,7 +34,9 @@ use crate::camera_backend::{ CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, INTERVALS_PER_SEC, }; -use util::v4l2::{new_init, V4l2Backend, V4L2_FORMAT_MJPG, V4L2_FORMAT_YUYV}; +use util::v4l2::{new_init, V4l2Backend}; + +use super::{PIXFMT_MJPG, PIXFMT_RGB565, PIXFMT_YUYV}; const BUFFER_CNT: usize = 4; @@ -56,6 +58,7 @@ impl Sample { } } +#[derive(Clone)] pub struct V4l2CameraBackend { id: String, dev_path: String, @@ -73,6 +76,7 @@ pub struct V4l2CameraBackend { listening: bool, iothread: Option, delete_evts: Vec, + fmt_list: Vec, } impl V4l2CameraBackend { @@ -89,6 +93,7 @@ impl V4l2CameraBackend { broken_cb: None, iothread, delete_evts: Vec::new(), + fmt_list: vec![], }; cam.check_cap()?; Ok(cam) @@ -159,6 +164,7 @@ impl V4l2CameraBackend { let backend = self.backend.as_ref().with_context(|| "Backend is none")?; let mut list = Vec::new(); let mut frmsize = new_init::(); + let mut frm_idx = 1; frmsize.pixel_format = pixfmt; const FRAME_SIZE_LIMIT: u32 = 1000; for i in 0..FRAME_SIZE_LIMIT { @@ -179,8 +185,10 @@ impl V4l2CameraBackend { width, height, interval, + index: frm_idx, }); } + frm_idx += 1; } Ok(list) } @@ -214,6 +222,10 @@ impl V4l2CameraBackend { } Ok(list) } + + fn is_pixfmt_supported(&self, pixelformat: u32) -> bool { + pixelformat == PIXFMT_MJPG || pixelformat == PIXFMT_RGB565 || pixelformat == PIXFMT_YUYV + } } impl CameraHostdevOps for V4l2CameraBackend { @@ -223,9 +235,11 @@ impl CameraHostdevOps for V4l2CameraBackend { fn is_camera(&self) -> Result { Ok(true) } - fn get_fmt(&self) -> Result<()> { - Ok(()) + + fn get_fmt(&self) -> Result> { + Ok(self.fmt_list.clone()) } + fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { info!("Camera {} set format {:?}", self.id, cam_fmt); if self.listening { @@ -262,6 +276,7 @@ impl CameraHostdevOps for V4l2CameraBackend { backend.set_stream_parameter(&parm)?; Ok(()) } + fn set_ctl(&self) -> Result<()> { Ok(()) } @@ -301,17 +316,26 @@ impl CameraHostdevOps for V4l2CameraBackend { let mut desc = new_init::(); desc.type_ = V4L2_CAP_VIDEO_CAPTURE; const FORMAT_LIMIT: u32 = 1000; + let mut fmt_index = 1; for i in 0..FORMAT_LIMIT { desc.index = i; let format_end = backend.enum_format(&mut desc)?; if format_end { break; } + if !self.is_pixfmt_supported(desc.pixelformat) { + continue; + } list.push(CameraFormatList { format: cam_fmt_from_v4l2(desc.pixelformat)?, frame: self.list_frame_size(desc.pixelformat)?, + fmt_index, }); + fmt_index += 1; } + + self.fmt_list = list.clone(); + Ok(list) } @@ -336,6 +360,40 @@ impl CameraHostdevOps for V4l2CameraBackend { self.sample.lock().unwrap().reset(); } + fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result { + let mut out = CamBasicFmt::default(); + for fmt in &self.fmt_list { + if fmt.fmt_index != format_index { + continue; + } + out.fmttype = fmt.format; + for frm in &fmt.frame { + if frm.index != frame_index { + continue; + } + out.width = frm.width; + out.height = frm.height; + out.fps = 10000000_u32.checked_div(frm.interval).with_context(|| { + format!( + "Invalied interval {} for format/frame {}:{}", + frm.interval, format_index, frame_index + ) + })?; + debug!( + "v4l2 get_format_by_index fmt {}, frm {}, info {:?}", + format_index, frame_index, out + ); + return Ok(out); + } + } + + bail!( + "format/frame with idx {}/{} is not found", + format_index, + frame_index + ); + } + fn get_frame_size(&self) -> usize { self.sample.lock().unwrap().used_len as usize } @@ -384,15 +442,17 @@ impl CameraHostdevOps for V4l2CameraBackend { fn cam_fmt_to_v4l2(t: &FmtType) -> u32 { match t { - FmtType::Uncompressed => V4L2_FORMAT_YUYV, - FmtType::Mjpg => V4L2_FORMAT_MJPG, + FmtType::Yuy2 => PIXFMT_YUYV, + FmtType::Rgb565 => PIXFMT_RGB565, + FmtType::Mjpg => PIXFMT_MJPG, } } fn cam_fmt_from_v4l2(t: u32) -> Result { let fmt = match t { - V4L2_FORMAT_YUYV => FmtType::Uncompressed, - V4L2_FORMAT_MJPG => FmtType::Mjpg, + PIXFMT_YUYV => FmtType::Yuy2, + PIXFMT_RGB565 => FmtType::Rgb565, + PIXFMT_MJPG => FmtType::Mjpg, _ => bail!("Invalid v4l2 type {}", t), }; Ok(fmt) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 18d27112f..e34d7350b 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -20,7 +20,6 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; use log::{debug, error, info}; -use once_cell::sync::Lazy; use strum::EnumCount; use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use vmm_sys_util::epoll::EventSet; @@ -34,11 +33,13 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; +use super::camera_media_type_guid::MEDIA_TYPE_GUID_HASHMAP; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{ - get_format_by_index, v4l2::V4l2CameraBackend, CamBasicFmt, CameraBrokenCallback, - CameraHostdevOps, CameraNotifyCallback, + v4l2::V4l2CameraBackend, CamBasicFmt, CameraBrokenCallback, CameraHostdevOps, + CameraNotifyCallback, }; +use crate::camera_backend::{CameraFormatList, CameraFrame, FmtType}; use crate::usb::config::*; use crate::usb::descriptor::*; use crate::usb::{ @@ -54,12 +55,9 @@ const INTERFACE_ID_CONTROL: u8 = 0; const INTERFACE_ID_STREAMING: u8 = 1; const TERMINAL_ID_INPUT_TERMINAL: u8 = 1; -const TERMINAL_ID_OUTPUT_TERMINAL: u8 = 3; -const UNIT_ID_SELECTOR_UNIT: u8 = 4; -const UNIT_ID_PROCESSING_UNIT: u8 = 5; +const TERMINAL_ID_OUTPUT_TERMINAL: u8 = 2; -const ENDPOINT_ID_CONTROL: u8 = 0x1; -const ENDPOINT_ID_STREAMING: u8 = 0x2; +const ENDPOINT_ID_STREAMING: u8 = 0x1; const VS_INTERFACE_NUM: u8 = 1; // According to UVC specification 1.5 @@ -69,19 +67,18 @@ const SC_VIDEOSTREAMING: u8 = 0x02; const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; // A.3. Video Interface Protocol Codes const PC_PROTOCOL_UNDEFINED: u8 = 0x0; -const PC_PROTOCOL_15: u8 = 0x1; // A.4. Video Class-Specific Descriptor Types const CS_INTERFACE: u8 = 0x24; // A.5. Video Class-Specific VC Interface Descriptor Subtypes const VC_HEADER: u8 = 0x01; const VC_INPUT_TERMINAL: u8 = 0x02; const VC_OUTPUT_TERMINAL: u8 = 0x03; -const VC_SELECTOR_UNIT: u8 = 0x04; -const VC_PROCESSING_UNIT: u8 = 0x05; // A.6 Video Class-Specific VS Interface Descriptor Subtypes const VS_INPUT_HEADER: u8 = 0x01; const VS_FORMAT_UNCOMPRESSED: u8 = 0x04; const VS_FRAME_UNCOMPRESSED: u8 = 0x05; +const VS_FORMAT_MJPEG: u8 = 0x06; +const VS_FRAME_MJPEG: u8 = 0x07; const VS_COLORFORMAT: u8 = 0x0D; // A.8. Video Class-Specific Request Codes const SET_CUR: u8 = 0x1; @@ -144,123 +141,341 @@ const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ "Video Streaming", ]; -static DESC_INTERFACE_CAMERA_VC: Lazy> = Lazy::new(|| { +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct VCHeaderDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bcdUVC: u16, + pub wTotalLength: u16, + pub dwClockFrequency: u32, + pub bInCollection: u8, + pub baInterfaceNr: u8, +} + +impl ByteCode for VCHeaderDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct InputTerminalDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bTerminalID: u8, + pub wTerminalType: u16, + pub bAssocTerminal: u8, + pub iTerminal: u8, + pub wObjectiveFocalLengthMin: u16, + pub wObjectiveFocalLengthMax: u16, + pub wOcularFocalLength: u16, + pub bControlSize: u8, + pub bmControls: [u8; 3], +} + +impl ByteCode for InputTerminalDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct OutputTerminalDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bTerminalID: u8, + pub wTerminalType: u16, + pub bAssocTerminal: u8, + pub bSourceID: u8, + pub iTerminal: u8, +} + +impl ByteCode for OutputTerminalDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct VSInputHeaderDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bNumFormats: u8, + pub wTotalLength: u16, + pub bEndpointAddress: u8, + pub bmInfo: u8, + pub bTerminalLink: u8, + pub bStillCaptureMethod: u8, + pub bTriggerSupport: u8, + pub bTriggerUsage: u8, + pub bControlSize: u8, + pub bmaControls: [u8; 2], +} + +impl ByteCode for VSInputHeaderDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct MjpgFormatDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bFormatIndex: u8, + pub bNumFrameDescriptors: u8, + pub bmFlags: u8, + pub bDefaultFrameIndex: u8, + pub bAspectRatioX: u8, + pub bAspectRatioY: u8, + pub bmInterfaceFlags: u8, + pub bCopyProtect: u8, +} + +impl ByteCode for MjpgFormatDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct MjpgFrameDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bFrameIndex: u8, + pub bmCapabilities: u8, + pub wWidth: u16, + pub wHeight: u16, + pub dwMinBitRate: u32, + pub dwMaxBitRate: u32, + pub dwMaxVideoFrameBufferSize: u32, + pub dwDefaultFrameInterval: u32, + pub bFrameIntervalType: u8, + pub dwFrameInterval: u32, +} + +impl ByteCode for MjpgFrameDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct ColorMatchingDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bColorPrimaries: u8, + pub bTransferCharacteristics: u8, + pub bMatrixCoefficients: u8, +} + +impl ByteCode for ColorMatchingDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UncompressedFormatDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bFormatIndex: u8, + pub bNumFrameDescriptors: u8, + pub guidFormat: [u8; 16], + pub bBitsPerPixel: u8, + pub bDefaultFrameIndex: u8, + pub bAspectRatioX: u8, + pub bAspectRatioY: u8, + pub bmInterfaceFlags: u8, + pub bCopyProtect: u8, +} + +impl ByteCode for UncompressedFormatDescriptor {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UncompressedFrameDescriptor { + pub bLength: u8, + pub bDescriptorType: u8, + pub bDescriptorSubType: u8, + pub bFrameIndex: u8, + pub bmCapabilities: u8, + pub wWidth: u16, + pub wHeight: u16, + pub dwMinBitRate: u32, + pub dwMaxBitRate: u32, + pub dwMaxVideoFrameBufferSize: u32, + pub dwDefaultFrameInterval: u32, + pub bFrameIntervalType: u8, + pub dwFrameInterval: u32, +} + +impl ByteCode for UncompressedFrameDescriptor {} + +fn gen_desc_interface_camera_vc() -> Result> { // VideoControl Interface Descriptor - Arc::new(UsbDescIface { + let desc = Arc::new(UsbDescIface { interface_desc: UsbInterfaceDescriptor { - bLength: 0x9, + bLength: USB_DT_INTERFACE_SIZE, bDescriptorType: USB_DT_INTERFACE, bInterfaceNumber: INTERFACE_ID_CONTROL, bAlternateSetting: 0, - bNumEndpoints: 1, + bNumEndpoints: 0, bInterfaceClass: USB_CLASS_VIDEO, bInterfaceSubClass: SC_VIDEOCONTROL, - bInterfaceProtocol: PC_PROTOCOL_15, + bInterfaceProtocol: 0, iInterface: UsbCameraStringIDs::VideoControl as u8, }, other_desc: vec![ Arc::new(UsbDescOther { - // Class-specific VS Interface Input Header Descriptor - data: vec![ - 0xd, // bLength - CS_INTERFACE, // bDescriptorType - VC_HEADER, // bDescriptorSubtype - 0x10, // bcdADC - 0x01, - 0x3b, //wTotalLength - 0x00, - 0x80, //dwClockFrequency, 6MHz - 0x8D, - 0x5B, - 0x00, - 0x01, // bInCollection - 0x01, // baInterfaceNr - ], + data: VCHeaderDescriptor { + bLength: 0x0d, + bDescriptorType: CS_INTERFACE, + bDescriptorSubType: VC_HEADER, + bcdUVC: 0x100, + wTotalLength: 40, + dwClockFrequency: 0x02dc6c00, + bInCollection: 0x1, + baInterfaceNr: 0x1, + } + .as_bytes() + .to_vec(), }), // Input Terminal Descriptor Arc::new(UsbDescOther { - data: vec![ - 0x11, // bLength - CS_INTERFACE, // bDescriptorType - VC_INPUT_TERMINAL, // bDescriptorSubtype - TERMINAL_ID_INPUT_TERMINAL, // bTerminalID - 0x01, // Fixme, wTerminalType, ITT_CAMERA, 0x0201 - 0x02, - 0x00, // bAssocTerminal - UsbCameraStringIDs::InputTerminal as u8, - 0x00, // wObjectiveFocalLengthMin - 0x00, - 0x00, // wObjectiveFocalLengthMax - 0x00, - 0x00, // wOcularFocalLength - 0x00, - 0x02, // bControlSize - 0x00, // bmControls - 0x00, - ], + data: InputTerminalDescriptor { + bLength: 0x12, + bDescriptorType: CS_INTERFACE, + bDescriptorSubType: VC_INPUT_TERMINAL, + bTerminalID: TERMINAL_ID_INPUT_TERMINAL, + wTerminalType: 0x0201, + bAssocTerminal: 0, + iTerminal: UsbCameraStringIDs::InputTerminal as u8, + wObjectiveFocalLengthMin: 0, + wObjectiveFocalLengthMax: 0, + wOcularFocalLength: 0, + bControlSize: 0x3, + bmControls: [0; 3], + } + .as_bytes() + .to_vec(), }), // Output Terminal Descriptor Arc::new(UsbDescOther { - data: vec![ - 0x09, // bLength - CS_INTERFACE, // bDescriptorType - VC_OUTPUT_TERMINAL, // bDescriptorSubtype - TERMINAL_ID_OUTPUT_TERMINAL, // bTerminalID - 0x01, // wTerminalType, TT_STREAMING, 0x0101 - 0x01, - 0x00, // bAssocTerminal - UNIT_ID_PROCESSING_UNIT, // bSourceID - UsbCameraStringIDs::OutputTerminal as u8, // iTerminal - ], - }), - // Selector Unit Descriptor - Arc::new(UsbDescOther { - data: vec![ - 0x07, // bLength - CS_INTERFACE, //bDescriptorType - VC_SELECTOR_UNIT, //bDescriptorSubtype - UNIT_ID_SELECTOR_UNIT, // bUnitID - 0x01, // bNrInPins - TERMINAL_ID_INPUT_TERMINAL, // baSourceID(1) - UsbCameraStringIDs::SelectUnit as u8, // iSelector - ], - }), - // Processing Unit Descriptor - Arc::new(UsbDescOther { - data: vec![ - 0x0d, // bLength - CS_INTERFACE, // bDescriptorType - VC_PROCESSING_UNIT, // bDescriptorSubtype - UNIT_ID_PROCESSING_UNIT, // bUnitID - UNIT_ID_SELECTOR_UNIT, // bSourceID - 0x00, // u16 wMaxMultiplier - 0x00, - 0x03, // bControlSize - 0x00, // u24 bmControls - 0x00, - 0x00, - UsbCameraStringIDs::ProcessingUnit as u8, // iProcessing - 0x00, // bmVideoStandards - ], + data: OutputTerminalDescriptor { + bLength: 0x9, + bDescriptorType: CS_INTERFACE, + bDescriptorSubType: VC_OUTPUT_TERMINAL, + bTerminalID: TERMINAL_ID_OUTPUT_TERMINAL, + wTerminalType: 0x0101, + bAssocTerminal: 0, + bSourceID: 1, + iTerminal: UsbCameraStringIDs::OutputTerminal as u8, + } + .as_bytes() + .to_vec(), }), ], - endpoints: vec![Arc::new(UsbDescEndpoint { - endpoint_desc: UsbEndpointDescriptor { - bLength: USB_DT_ENDPOINT_SIZE, - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | ENDPOINT_ID_CONTROL, - bmAttributes: USB_ENDPOINT_ATTR_INT, - wMaxPacketSize: 0x40, - bInterval: 0x20, - }, - extra: Vec::new(), - })], - }) -}); + endpoints: vec![], + }); -static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { - // VideoStreaming Interface Descriptor - Arc::new(UsbDescIface { + Ok(desc) +} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct VsDescInputHeader { + bLength: u8, + bDescriptorType: u8, + bDescriptorSubtype: u8, + bNumFormats: u8, + wTotalLength: u16, + bEndpointAddress: u8, + bmInfo: u8, + bTerminalLink: u8, + bStillCaptureMethod: u8, + bTriggerSupport: u8, + bTriggerUsage: u8, + bControlSize: u8, + bmaControls: u16, +} + +impl ByteCode for VsDescInputHeader {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct VsDescUncompressedFmt { + bLength: u8, + bDescriptorType: u8, + bDescriptorSubtype: u8, + bFormatIndex: u8, + bNumFrameDescriptors: u8, + guidFormat: [u8; 16], + bBitsPerPixel: u8, + bDefaultFrameIndex: u8, + bAspectRatioX: u8, + bAspectRatioY: u8, + bmInterlaceFlags: u8, + bCopyProtect: u8, +} + +impl ByteCode for VsDescUncompressedFmt {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct VsDescMjpgFmt { + bLength: u8, + bDescriptorType: u8, + bDescriptorSubtype: u8, + bFormatIndex: u8, + bNumFrameDescriptors: u8, + bmFlags: u8, + bDefaultFrameIndex: u8, + bAspectRatioX: u8, + bAspectRatioY: u8, + bmInterlaceFlags: u8, + bCopyProtect: u8, +} + +impl ByteCode for VsDescMjpgFmt {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct VsDescFrm { + bLength: u8, + bDescriptorType: u8, + bDescriptorSubtype: u8, + bFrameIndex: u8, + bmCapabilities: u8, + wWidth: u16, + wHeight: u16, + dwMinBitRate: u32, + dwMaxBitRate: u32, + dwMaxVideoFrameBufSize: u32, + dwDefaultFrameInterval: u32, + bFrameIntervalType: u8, + dwIntervalVals: u32, +} + +impl ByteCode for VsDescFrm {} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Clone, Copy, Default)] +struct VsDescColorMatching { + bLength: u8, + bDescriptorType: u8, + bDescriptorSubtype: u8, + bColorPrimaries: u8, + bTransferCharacteristics: u8, + bMatrixCoefficients: u8, +} + +impl ByteCode for VsDescColorMatching {} + +fn gen_desc_interface_camera_vs(fmt_list: Vec) -> Result> { + let desc = Arc::new(UsbDescIface { interface_desc: UsbInterfaceDescriptor { bLength: USB_DT_INTERFACE_SIZE, bDescriptorType: USB_DT_INTERFACE, @@ -269,109 +484,10 @@ static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { bNumEndpoints: 1, bInterfaceClass: USB_CLASS_VIDEO, bInterfaceSubClass: SC_VIDEOSTREAMING, - bInterfaceProtocol: PC_PROTOCOL_15, + bInterfaceProtocol: 0, iInterface: UsbCameraStringIDs::VideoStreaming as u8, }, - other_desc: vec![ - // VC-Specific VS Video Input Header Descriptor - Arc::new(UsbDescOther { - data: vec![ - 0xf, // bLength - CS_INTERFACE, // bDescriptorType - VS_INPUT_HEADER, // bDescriptorSubtype - 0x1, // bNumFormats - 0x4E, //wTotalLength - 0x00, - 0x81, // bEndpointAddress, EP 1 IN - 0x00, // bmInfo - UsbCameraStringIDs::OutputTerminal as u8, // bTerminalLink - 0x00, // bStillCaptureMethod - 0x00, // bTriggerSupport - 0x00, // bTriggerUsage - 0x01, // bControlSize - 0x00, // bmaControls(0) - 0x00, // bmaControls(1) - ], - }), - // VS Uncompressed Format Type Descriptor - Arc::new(UsbDescOther { - data: vec![ - 0x1B, // bLength - CS_INTERFACE, // bDescriptorType - VS_FORMAT_UNCOMPRESSED, //bDescriptorSubtype - 0x01, // bFormatIndex - 0x01, // bNumFrameDescriptors - 0x59, // guidFormat {32595559-0000-0010-8000-00AA00389B71} (YUY2) - 0x55, - 0x59, - 0x32, - 0x00, - 0x00, - 0x10, - 0x00, - 0x80, - 0x00, - 0x00, - 0xaa, - 0x00, - 0x38, - 0x9b, - 0x71, - 0x10, // bBitsPerPixel (16 bits per pixel) - 0x01, // bDefaultFrameIndex (Index 1) - 0x00, // bAspectRatioX - 0x00, // bAspectRatioY - 0x00, // bmInterlaceFlags - 0x00, // bCopyProtect - ], - }), - // VS Uncompressed Frame Type Descriptor - Arc::new(UsbDescOther { - data: vec![ - 0x1E, // bLength - CS_INTERFACE, // bDescriptorType - VS_FRAME_UNCOMPRESSED, // bDescriptorSubtype - 0x01, // bFrameIndex - 0x00, // bmCapabilities (Still image unsupported) - 0x00, // wWidth, 1280 - 0x05, // - 0xD0, // wHeight, 720 - 0x02, // - 0x00, // dwMinBitRate, (147456000 bps -> 18.432 MB/s) - 0x00, // - 0xCA, // - 0x08, // - 0x00, // dwMaxBitRate, (147456000 bps -> 18.432 MB/s) - 0x00, // - 0xCA, // - 0x08, // - 0x00, // dwMaxVideoFrameBufferSize, (1843200 bytes) - 0x20, // - 0x1C, // - 0x00, // - 0x40, // dwDefaultFrameInterval, (100.0000 ms -> 10.0000 fps) - 0x42, // - 0x0F, // - 0x00, // - 0x01, // bFrameIntervalType, (1 discrete frame interval supported) - 0x40, // adwFrameInterval,(100.0000 ms -> 10.0000 fps) - 0x42, // - 0x0F, // - 0x00, // - ], - }), - // VS Color Matching Descriptor Descriptor - Arc::new(UsbDescOther { - data: vec![ - 0x06, // bLength - CS_INTERFACE, // bDescriptorType - VS_COLORFORMAT, // bDescriptorSubtype - 0x01, // bColorPrimaries (BT.709,sRGB) - 0x01, // bTransferCharacteristics (BT.709) - 0x04, // bMatrixCoefficients (SMPTE 170M (BT.601)) - ], - }), - ], + other_desc: gen_fmt_desc(fmt_list)?, endpoints: vec![Arc::new(UsbDescEndpoint { endpoint_desc: UsbEndpointDescriptor { bLength: USB_DT_ENDPOINT_SIZE, @@ -379,7 +495,7 @@ static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | ENDPOINT_ID_STREAMING, bmAttributes: USB_ENDPOINT_ATTR_BULK, wMaxPacketSize: 0x400, - bInterval: 0x20, + bInterval: 0x0, }, extra: UsbSuperSpeedEndpointCompDescriptor { bLength: USB_DT_SS_EP_COMP_SIZE, @@ -391,11 +507,13 @@ static DESC_INTERFACE_CAMERA_VS: Lazy> = Lazy::new(|| { .as_bytes() .to_vec(), })], - }) -}); + }); -static DESC_IAD_CAMERA: Lazy> = Lazy::new(|| { - Arc::new(UsbDescIAD { + Ok(desc) +} + +fn gen_desc_iad_camera(fmt_list: Vec) -> Result> { + let desc = Arc::new(UsbDescIAD { iad_desc: UsbIadDescriptor { bLength: 0x8, bDescriptorType: USB_DT_INTERFACE_ASSOCIATION, @@ -407,15 +525,17 @@ static DESC_IAD_CAMERA: Lazy> = Lazy::new(|| { iFunction: UsbCameraStringIDs::Iad as u8, }, itfs: vec![ - DESC_INTERFACE_CAMERA_VC.clone(), - DESC_INTERFACE_CAMERA_VS.clone(), + gen_desc_interface_camera_vc()?, + gen_desc_interface_camera_vs(fmt_list)?, ], - }) -}); + }); + + Ok(desc) +} /// UVC Camera device descriptor -static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { - Arc::new(UsbDescDevice { +fn gen_desc_device_camera(fmt_list: Vec) -> Result> { + let desc = Arc::new(UsbDescDevice { device_desc: UsbDeviceDescriptor { bLength: USB_DT_DEVICE_SIZE, bDescriptorType: USB_DT_DEVICE, @@ -425,7 +545,7 @@ static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { iManufacturer: UsbCameraStringIDs::Manufacture as u8, iProduct: UsbCameraStringIDs::Product as u8, iSerialNumber: UsbCameraStringIDs::SerialNumber as u8, - bcdUSB: 0x0200, // TODO: support 3.0 Super + bcdUSB: 0x0300, bDeviceClass: USB_CLASS_MISCELLANEOUS, // Refer to https://www.usb.org/defined-class-codes for details. bDeviceSubClass: 2, @@ -444,11 +564,13 @@ static DESC_DEVICE_CAMERA: Lazy> = Lazy::new(|| { bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, bMaxPower: 50, }, - iad_desc: vec![DESC_IAD_CAMERA.clone()], + iad_desc: vec![gen_desc_iad_camera(fmt_list)?], interfaces: vec![], })], - }) -}); + }); + + Ok(desc) +} #[allow(non_snake_case)] #[repr(C, packed)] @@ -684,7 +806,12 @@ impl UsbCamera { self.vs_control.bFrameIndex = vs_control.bFrameIndex; self.vs_control.dwMaxVideoFrameSize = vs_control.dwMaxVideoFrameSize; self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; - let fmt = get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; + let fmt = self + .camera_dev + .lock() + .unwrap() + .get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; + self.activate(&fmt) .with_context(|| "Failed to activate device")?; } @@ -702,14 +829,16 @@ impl UsbCamera { impl UsbDeviceOps for UsbCamera { fn realize(mut self) -> Result>> { + self.camera_dev.lock().unwrap().list_format()?; + let fmt_list = self.camera_dev.lock().unwrap().get_fmt()?; + self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_SUPER; let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); - self.usb_device - .init_descriptor(DESC_DEVICE_CAMERA.clone(), s)?; - // TODO: list format to construct descriptor. - self.camera_dev.lock().unwrap().list_format()?; + let device_desc = gen_desc_device_camera(fmt_list)?; + self.usb_device.init_descriptor(device_desc, s)?; self.register_cb(); + let camera = Arc::new(Mutex::new(self)); Ok(camera) } @@ -986,8 +1115,135 @@ impl EventNotifierHelper for CameraIoHander { } } +fn gen_fmt_desc(fmt_list: Vec) -> Result>> { + let mut body = vec![]; + let mut buf = vec![]; + + let mut header_struct = gen_intface_header_desc(fmt_list.len() as u8); + + for fmt in fmt_list { + let data = gen_fmt_header(&fmt)?; + body.push(Arc::new(UsbDescOther { data: data.clone() })); + + for frm in &fmt.frame { + let data = gen_frm_desc(fmt.format, frm)?; + body.push(Arc::new(UsbDescOther { data: data.clone() })); + } + let data = gen_color_matching_desc()?; + body.push(Arc::new(UsbDescOther { data })); + } + + header_struct.wTotalLength = header_struct.bLength as u16 + + body.clone().iter().fold(0, |len, x| len + x.data.len()) as u16; + + buf.push(Arc::new(UsbDescOther { + data: header_struct.as_bytes().to_vec(), + })); + buf.append(&mut body); + + Ok(buf) +} + +fn gen_intface_header_desc(fmt_num: u8) -> VsDescInputHeader { + VsDescInputHeader { + bLength: 0xf, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: VS_INPUT_HEADER, + bNumFormats: fmt_num, + wTotalLength: 0x00, // Shall be filled later after all members joined together. + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | ENDPOINT_ID_STREAMING, // EP 1 IN + bmInfo: 0x00, + bTerminalLink: 2, + bStillCaptureMethod: 0x00, + bTriggerSupport: 0x00, + bTriggerUsage: 0x00, + bControlSize: 0x01, + bmaControls: 0x00, + } +} + +fn gen_fmt_header(fmt: &CameraFormatList) -> Result> { + let header = match fmt.format { + FmtType::Yuy2 | FmtType::Rgb565 => VsDescUncompressedFmt { + bLength: 0x1B, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: VS_FORMAT_UNCOMPRESSED, + bFormatIndex: fmt.fmt_index, + bNumFrameDescriptors: fmt.frame.len() as u8, + guidFormat: *MEDIA_TYPE_GUID_HASHMAP + .get(&fmt.format) + .with_context(|| "unsupported video format.")?, + bBitsPerPixel: 0x10, + bDefaultFrameIndex: 1, + bAspectRatioX: 0, + bAspectRatioY: 0, + bmInterlaceFlags: 0, + bCopyProtect: 0, + } + .as_bytes() + .to_vec(), + FmtType::Mjpg => VsDescMjpgFmt { + bLength: 0xb, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: VS_FORMAT_MJPEG, + bFormatIndex: fmt.fmt_index, + bNumFrameDescriptors: fmt.frame.len() as u8, + bmFlags: 0x01, + bDefaultFrameIndex: 0x01, + bAspectRatioX: 0x00, + bAspectRatioY: 0x00, + bmInterlaceFlags: 0x00, + bCopyProtect: 0x00, + } + .as_bytes() + .to_vec(), + }; + + Ok(header) +} + +fn gen_frm_desc(pixfmt: FmtType, frm: &CameraFrame) -> Result> { + let desc = VsDescFrm { + bLength: 0x1e, // TODO: vary with interval number. + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: match pixfmt { + FmtType::Rgb565 | FmtType::Yuy2 => VS_FRAME_UNCOMPRESSED, + FmtType::Mjpg => VS_FRAME_MJPEG, + }, + bFrameIndex: frm.index, + bmCapabilities: 0x00, + wWidth: frm.width as u16, + wHeight: frm.height as u16, + dwMinBitRate: 442368000, + dwMaxBitRate: 442368000, + dwMaxVideoFrameBufSize: frm.width as u32 * frm.height as u32 * 2 + 589, + dwDefaultFrameInterval: frm.interval, + bFrameIntervalType: 1, + dwIntervalVals: frm.interval, + } + .as_bytes() + .to_vec(); + + Ok(desc) +} + +fn gen_color_matching_desc() -> Result> { + Ok(VsDescColorMatching { + bLength: 0x06, + bDescriptorType: CS_INTERFACE, + bDescriptorSubtype: VS_COLORFORMAT, + bColorPrimaries: 0x01, // BT.709,sRGB + bTransferCharacteristics: 0x01, // BT.709 + bMatrixCoefficients: 0x04, // SMPTE 170M (BT.601) + } + .as_bytes() + .to_vec()) +} + #[cfg(test)] mod test { + use crate::camera_backend::{CameraFormatList, CameraFrame, FmtType}; + use super::*; fn test_interface_table_data_len(interface: Arc, size_offset: usize) { @@ -1009,11 +1265,52 @@ mod test { assert_eq!(total_len_set, total_len_exact); } + fn list_format() -> Vec { + vec![ + CameraFormatList { + format: FmtType::Yuy2, + fmt_index: 1, + frame: vec![ + CameraFrame { + width: 1980, + height: 720, + interval: 30, + index: 1, + }, + CameraFrame { + width: 640, + height: 480, + interval: 30, + index: 2, + }, + ], + }, + CameraFormatList { + format: FmtType::Mjpg, + frame: vec![ + CameraFrame { + width: 1980, + height: 720, + interval: 30, + index: 1, + }, + CameraFrame { + width: 640, + height: 680, + interval: 20, + index: 2, + }, + ], + fmt_index: 2, + }, + ] + } + #[test] fn test_interfaces_table_data_len() { // VC and VS's header differents, their wTotalSize field's offset are the bit 5 and 4 respectively in their data[0] vector. // the rest datas follow the same principle that the 1st element is the very data vector's length. - test_interface_table_data_len(DESC_INTERFACE_CAMERA_VC.clone(), 5); - test_interface_table_data_len(DESC_INTERFACE_CAMERA_VS.clone(), 4); + test_interface_table_data_len(gen_desc_interface_camera_vc().unwrap(), 5); + test_interface_table_data_len(gen_desc_interface_camera_vs(list_format()).unwrap(), 4); } } diff --git a/devices/src/usb/camera_media_type_guid.rs b/devices/src/usb/camera_media_type_guid.rs new file mode 100644 index 000000000..b03c5ca0d --- /dev/null +++ b/devices/src/usb/camera_media_type_guid.rs @@ -0,0 +1,42 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! Media Type GUID. refered with uvc.h in linux kernel. + +use once_cell::sync::Lazy; +use std::collections::HashMap; + +use crate::camera_backend::FmtType; + +pub const MEDIA_TYPE_GUID: [(FmtType, [u8; 16]); 2] = [ + ( + FmtType::Yuy2, + [ + b'Y', b'U', b'Y', b'2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, + 0x9b, 0x71, + ], + ), + ( + FmtType::Rgb565, + [ + b'R', b'G', b'B', b'P', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, + 0x9b, 0x71, + ], + ), +]; + +pub static MEDIA_TYPE_GUID_HASHMAP: Lazy> = + Lazy::new(gen_mediatype_hashmap); + +fn gen_mediatype_hashmap() -> HashMap { + HashMap::from(MEDIA_TYPE_GUID) +} diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index ac8b36171..8faf26501 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -468,12 +468,6 @@ pub trait UsbDescriptorOps { /// Init descriptor with the device descriptor and string descriptors. fn init_descriptor(&mut self, desc: Arc, str: Vec) -> Result<()>; - - /// Init device qualifier descriptor. - fn init_device_qualifier_descriptor( - &mut self, - device_qualifier_desc: Arc, - ) -> Result<()>; } impl UsbDescriptorOps for UsbDevice { @@ -571,12 +565,4 @@ impl UsbDescriptorOps for UsbDevice { self.set_config_descriptor(0)?; Ok(()) } - - fn init_device_qualifier_descriptor( - &mut self, - device_qualifier_desc: Arc, - ) -> Result<()> { - self.descriptor.device_qualifier_desc = Some(device_qualifier_desc); - Ok(()) - } } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 970892bf8..27156d42b 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -16,6 +16,7 @@ pub use error::UsbError; #[cfg(not(target_env = "musl"))] pub mod camera; +pub mod camera_media_type_guid; pub mod config; mod descriptor; #[cfg(not(target_env = "musl"))] diff --git a/util/src/v4l2.rs b/util/src/v4l2.rs index 68374956b..dfeb0d6e9 100644 --- a/util/src/v4l2.rs +++ b/util/src/v4l2.rs @@ -26,15 +26,6 @@ use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; use crate::aio::Iovec; -macro_rules! fourcc_code { - ($a: expr, $b: expr, $c: expr, $d: expr) => { - $a as u32 | (($b as u32) << 8) | (($c as u32) << 16) | (($d as u32) << 24) - }; -} - -pub const V4L2_FORMAT_YUYV: u32 = fourcc_code!('Y', 'U', 'Y', 'V'); -pub const V4L2_FORMAT_MJPG: u32 = fourcc_code!('M', 'J', 'P', 'G'); - const VIDEO: u32 = 86; ioctl_ior_nr!(VIDIOC_QUERYCAP, VIDEO, 0, v4l2_capability); -- Gitee From 6eb6752227852def50eb874c09a9341710dd4922 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Sun, 23 Apr 2023 09:58:42 +0800 Subject: [PATCH 1043/2187] camera: performance: enlarge MAX_PAYLOAD to reduce datacopy times According to USB video FAQ, "MaxPayloadTransferSize should be set to a value comprised between 10ms worth of data and dwMaxVideoFrameSize.", thus we set the MAX_PAYLOAD to typical uncompressed video frame size, which is 1024 * 768 * 2. Note that MJPG do not have fixed frame size, thus this number shall be according to expierience. 1024 * 768 * 2 seems a sensible trade-off. Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index e34d7350b..2befcb23b 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -92,7 +92,7 @@ const UVC_FID: u8 = 1; const VS_PROBE_CONTROL: u8 = 1; const VS_COMMIT_CONTROL: u8 = 2; -const MAX_PAYLOAD: u32 = 1024 * 3 - 2; +const MAX_PAYLOAD: u32 = FRAME_SIZE_1280_720; const FPS_30_INTERVAL: u32 = 333333; const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; -- Gitee From 3bada48648ff4b2bedb762c52b0bcebf4646d10b Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Mon, 24 Apr 2023 14:45:55 +0800 Subject: [PATCH 1044/2187] camera: seccomp: add VIDIOC_ ioctrls into whitelist Signed-off-by: Zhang Bo --- machine/src/standard_vm/aarch64/syscall.rs | 18 ++++++++++++++++++ machine/src/standard_vm/x86_64/syscall.rs | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index c4470b3c1..c8600834a 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -13,6 +13,11 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::v4l2::{ + VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, + VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, + VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, +}; use vfio::{ VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, @@ -267,6 +272,19 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_G_FMT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_FMT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_REQBUFS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYBUF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QBUF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_DQBUF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMON() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMOFF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_PARM() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMESIZES() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32) } fn madvise_rule() -> BpfRule { diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 4b8d6b7d2..a0087b2f5 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -19,6 +19,11 @@ use vfio::{ VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, }; +use util::v4l2::{ + VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, + VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, + VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, +}; use virtio::VhostKern::*; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/futex.h @@ -286,6 +291,19 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_G_FMT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_FMT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_REQBUFS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYBUF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QBUF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_DQBUF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMON() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMOFF() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_PARM() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMESIZES() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32) } fn madvise_rule() -> BpfRule { -- Gitee From 642f74c0b9d57a3b64d8ad0cb073b20569092db5 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 26 Apr 2023 22:35:53 +0800 Subject: [PATCH 1045/2187] camera: change cmdline style Seperate it to cameradev and device, thus make it more feasible and extendable. Example: -device nec-usb-xhci,id=usb,bus=pcie.0,addr=0x7 \ -cameradev v4l2,id=cambackend,path=/dev/video0 \ -device usb-camera,cameradev=cambackend,id=cam \ Signed-off-by: Zhang Bo --- devices/src/camera_backend/mod.rs | 22 ++- devices/src/usb/camera.rs | 19 +-- docs/qmp.md | 42 ++++++ machine/src/lib.rs | 2 +- machine/src/micro_vm/mod.rs | 18 +++ machine/src/standard_vm/mod.rs | 58 ++++++-- machine/src/standard_vm/x86_64/syscall.rs | 10 +- machine_manager/src/cmdline.rs | 9 ++ machine_manager/src/config/camera.rs | 165 ++++++++++++++++++++++ machine_manager/src/config/mod.rs | 3 + machine_manager/src/config/network.rs | 1 + machine_manager/src/config/usb.rs | 68 +++------ machine_manager/src/machine.rs | 14 +- machine_manager/src/qmp/mod.rs | 2 + machine_manager/src/qmp/qmp_schema.rs | 96 ++++++++++++- 15 files changed, 437 insertions(+), 92 deletions(-) create mode 100644 machine_manager/src/config/camera.rs diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 72e19cb97..b4adddb19 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -16,11 +16,14 @@ pub mod demo; pub mod v4l2; -use anyhow::{bail, Result}; -use std::sync::Arc; +use anyhow::{bail, Context, Result}; +use std::sync::{Arc, Mutex}; +use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; use util::aio::Iovec; +use self::v4l2::V4l2CameraBackend; + /// Frame interval in 100ns units. pub const INTERVALS_PER_SEC: u32 = 10_000_000; @@ -166,3 +169,18 @@ pub trait CameraHostdevOps: Send + Sync { /// Register broken callback which is called when backend is broken. fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } + +pub fn camera_ops(config: UsbCameraConfig) -> Result>> { + let cam = match config.backend { + CamBackendType::V4l2 => V4l2CameraBackend::new( + config.drive.id.clone().unwrap(), + config.drive.path.clone().with_context(|| { + ConfigError::FieldIsMissing("path".to_string(), "V4L2".to_string()) + })?, + config.iothread, + )?, + CamBackendType::Demo => bail!("Not supported type"), + }; + + Ok(Arc::new(Mutex::new(cam))) +} diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 2befcb23b..67c8a6e25 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -25,7 +25,7 @@ use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; +use machine_manager::config::UsbCameraConfig; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::aio::{iov_discard_front_direct, Iovec}; use util::byte_code::ByteCode; @@ -36,10 +36,9 @@ use util::loop_context::{ use super::camera_media_type_guid::MEDIA_TYPE_GUID_HASHMAP; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{ - v4l2::V4l2CameraBackend, CamBasicFmt, CameraBrokenCallback, CameraHostdevOps, - CameraNotifyCallback, + camera_ops, CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, + CameraNotifyCallback, FmtType, }; -use crate::camera_backend::{CameraFormatList, CameraFrame, FmtType}; use crate::usb::config::*; use crate::usb::descriptor::*; use crate::usb::{ @@ -603,17 +602,7 @@ impl VideoStreamingControl { impl UsbCamera { pub fn new(config: UsbCameraConfig) -> Result { - let cam = match config.backend { - CamBackendType::V4l2 => V4l2CameraBackend::new( - config.id.clone().unwrap(), - config.path.clone().with_context(|| { - ConfigError::FieldIsMissing("path".to_string(), "V4L2".to_string()) - })?, - config.iothread.clone(), - )?, - CamBackendType::Demo => bail!("Not supported type"), - }; - let camera = Arc::new(Mutex::new(cam)); + let camera = camera_ops(config.clone())?; Ok(Self { id: config.id.unwrap(), usb_device: UsbDevice::new(), diff --git a/docs/qmp.md b/docs/qmp.md index 7051d82ef..326817977 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -146,6 +146,48 @@ Remove a network backend. -> {"return": {}} ``` +## Camera device backend management + +### cameradev_add + +Add a camera backend. + +#### Arguments + +* `id` : the device's ID, must be unique. +* `driver` : the backend camera type, eg. v4l2 or demo. +* `path` : the backend camera file's path, eg. /dev/video0 + +#### Notes + +* MicroVM is not supported. + +#### Example + +```json +<- {"execute":"cameradev_add", "arguments":{"id":"cam-0", "driver": "v4l2", "path":"/dev/video0"}} +-> {"return": {}} +``` + +### cameradev_del + +Remove a camera backend. + +#### Arguments + +* `id` : the device's ID. + +#### Notes + +* MicroVM is not supported. + +#### Example + +```json +<- {"execute": "cameradev_del", "arguments": {"id": "cam-0"}} +-> {"return": {}} +``` + ## Character device backend management Currently, It only supports Standard VM. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e6a81e87e..934144648 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1262,7 +1262,7 @@ pub trait MachineOps { /// * `cfg_args` - Camera Configuration. #[cfg(not(target_env = "musl"))] fn add_usb_camera(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_camera(cfg_args)?; + let device_cfg = parse_usb_camera(vm_config, cfg_args)?; let camera = UsbCamera::new(device_cfg)?; let camera = camera.realize()?; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index afd464df9..220555632 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1303,6 +1303,24 @@ impl DeviceInterface for LightMachine { ) } + fn cameradev_add(&mut self, _args: qmp_schema::CameraDevAddArgument) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "cameradev_add not supported for MicroVM".to_string(), + ), + None, + ) + } + + fn cameradev_del(&mut self, _id: String) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "cameradev_del not supported for MicroVM".to_string(), + ), + None, + ) + } + fn getfd(&self, fd_name: String, if_fd: Option) -> Response { if let Some(fd) = if_fd { QmpChannel::set_fd(fd_name, fd); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index f21ae68ba..312270c18 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -21,6 +21,7 @@ pub use error::StandardVmError; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; use log::error; +use machine_manager::config::get_cameradev_config; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::UpdateRegionArgument; #[cfg(not(target_env = "musl"))] @@ -956,7 +957,7 @@ impl StdMachine { let driver = args.driver.as_str(); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); - let cfg_args = format!("id={}", args.id); + let mut cfg_args = format!("id={}", args.id); match driver { "usb-kbd" => { self.add_usb_keyboard(&mut locked_vmconfig, &cfg_args)?; @@ -965,19 +966,12 @@ impl StdMachine { self.add_usb_tablet(&mut locked_vmconfig, &cfg_args)?; } "usb-camera" => { - let mut cfg_args = format!( - "id={},backend={},path={}", - args.id, - args.backend - .as_ref() - .with_context(|| "No backend for usb camera.")?, - args.path - .as_ref() - .with_context(|| "No path for usb camera.")?, - ); + if let Some(cameradev) = &args.cameradev { + cfg_args = format!("{},cameradev={}", cfg_args, cameradev); + } if let Some(iothread) = args.iothread.as_ref() { cfg_args = format!("{},iothread={}", cfg_args, iothread); - }; + } self.add_usb_camera(&mut locked_vmconfig, &cfg_args)?; } _ => { @@ -1473,6 +1467,46 @@ impl DeviceInterface for StdMachine { } } + fn cameradev_add(&mut self, args: qmp_schema::CameraDevAddArgument) -> Response { + let config = match get_cameradev_config(args) { + Ok(conf) => conf, + Err(e) => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + }; + + match self + .get_vm_config() + .lock() + .unwrap() + .add_cameradev_with_config(config) + { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + } + } + + fn cameradev_del(&mut self, id: String) -> Response { + match self + .get_vm_config() + .lock() + .unwrap() + .del_cameradev_by_id(&id) + { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + } + } + fn getfd(&self, fd_name: String, if_fd: Option) -> Response { if let Some(fd) = if_fd { QmpChannel::set_fd(fd_name, fd); diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index a0087b2f5..c88303228 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -13,17 +13,17 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::v4l2::{ + VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, + VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, + VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, +}; use vfio::{ VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, }; -use util::v4l2::{ - VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, - VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, - VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, -}; use virtio::VhostKern::*; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/futex.h diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index bedb2ae94..8dfb5c606 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -157,6 +157,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { \n\t\tset numa distance: -numa dist,src=<0>,dst=<1>,val=<20> ") .takes_values(true), ) + .arg( + Arg::with_name("cameradev") + .multiple(true) + .long("cameradev") + .value_name("") + .help("set cameradev: -cameradev v4l2,id=,path=") + .takes_values(true), + ) .arg( Arg::with_name("kernel") .long("kernel") @@ -510,6 +518,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config_multi!((args.values_of("device")), vm_cfg, add_device); add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); + add_args_to_config_multi!((args.values_of("cameradev")), vm_cfg, add_camera_backend); if let Some(s) = args.value_of("trace") { add_trace_events(&s)?; diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs new file mode 100644 index 000000000..6603a4f93 --- /dev/null +++ b/machine_manager/src/config/camera.rs @@ -0,0 +1,165 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::Path; +use std::str::FromStr; + +use anyhow::{anyhow, bail, Context, Result}; +use serde::{Deserialize, Serialize}; +use strum::{EnumCount, IntoEnumIterator}; +use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; + +use super::error::ConfigError; +use crate::{ + config::{check_arg_nonexist, check_id, CmdParser, ConfigCheck, VmConfig}, + qmp::qmp_schema, +}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CameraDevConfig { + pub id: Option, + pub path: Option, + pub backend: CamBackendType, +} + +#[derive(Clone, Copy, Debug, PartialEq, EnumCountMacro, EnumIter, Eq, Serialize, Deserialize)] +pub enum CamBackendType { + V4l2, + Demo, +} + +impl FromStr for CamBackendType { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + for i in CamBackendType::iter() { + if s == CAM_OPT_STR_BACKEND_TYPES[i as usize] { + return Ok(i); + } + } + + Err(anyhow!("Unknown camera backend type")) + } +} + +pub const CAM_OPT_STR_BACKEND_TYPES: [&str; CamBackendType::COUNT] = ["v4l2", "demo"]; + +impl CameraDevConfig { + pub fn new() -> CameraDevConfig { + CameraDevConfig { + id: None, + path: None, + backend: CamBackendType::V4l2, + } + } +} + +impl Default for CameraDevConfig { + fn default() -> Self { + Self::new() + } +} + +impl VmConfig { + pub fn add_camera_backend(&mut self, camera_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("cameradev"); + cmd_parser.push("").push("id").push("path"); + cmd_parser.get_parameters(camera_config)?; + + let mut camera_backend = CameraDevConfig::new(); + camera_backend.backend = cmd_parser.get_value::("")?.unwrap(); + camera_backend.id = cmd_parser.get_value::("id")?; + camera_backend.path = cmd_parser.get_value::("path")?; + + camera_backend.check()?; + + self.add_cameradev_with_config(camera_backend) + } + + fn camera_backend_repeated(&self, id: &str, path: &str, backend: CamBackendType) -> bool { + for (key, cam) in self.camera_backend.iter() { + if key != id && cam.backend == backend && cam.path == Some(path.to_string()) { + return true; + } + } + + false + } + + pub fn add_cameradev_with_config(&mut self, conf: CameraDevConfig) -> Result<()> { + let cameradev_id = conf + .id + .clone() + .with_context(|| "no id configured for cameradev")?; + let cameradev_path = conf + .path + .clone() + .with_context(|| "no path configured for cameradev")?; + let cameradev_backend = conf.backend; + + let cam = self.camera_backend.get(&cameradev_id); + + if cam.is_some() { + bail!("cameradev with id {:?} has already existed", cameradev_id); + } + + if self.camera_backend_repeated(&cameradev_id, &cameradev_path, cameradev_backend) { + bail!("another cameradev has the same backend device"); + } + + self.camera_backend.insert(cameradev_id, conf); + + Ok(()) + } + + pub fn del_cameradev_by_id(&mut self, id: &str) -> Result<()> { + if self.camera_backend.get(&id.to_string()).is_none() { + bail!("no cameradev with id {}", id); + } + self.camera_backend.remove(&id.to_string()); + + Ok(()) + } +} + +impl ConfigCheck for CameraDevConfig { + fn check(&self) -> Result<()> { + // Note: backend has already been checked during args parsing. + check_id(self.id.clone(), "cameradev")?; + check_camera_path(self.path.clone()) + } +} + +fn check_camera_path(path: Option) -> Result<()> { + check_arg_nonexist(path.clone(), "path", "cameradev")?; + + let path = path.unwrap(); + if !Path::new(&path).exists() { + bail!(ConfigError::FileNotExist(path)); + } + + Ok(()) +} + +pub fn get_cameradev_config(args: qmp_schema::CameraDevAddArgument) -> Result { + let config = CameraDevConfig { + id: Some(args.id), + path: args.path, + backend: CamBackendType::from_str(&args.driver)?, + }; + + Ok(config) +} + +pub fn get_cameradev_by_id(vm_config: &mut VmConfig, id: String) -> Option { + vm_config.camera_backend.get(&id).cloned() +} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 58a1e656d..11d2cb2e4 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -12,6 +12,7 @@ pub use balloon::*; pub use boot_source::*; +pub use camera::*; pub use chardev::*; pub use demo_dev::*; pub use devices::*; @@ -37,6 +38,7 @@ pub use vnc::*; mod balloon; mod boot_source; +pub mod camera; mod chardev; mod demo_dev; mod devices; @@ -121,6 +123,7 @@ pub struct VmConfig { pub incoming: Option, pub vnc: Option, pub display: Option, + pub camera_backend: HashMap, } impl VmConfig { diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 70d5296b3..526945c4a 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -454,6 +454,7 @@ impl VmConfig { } Ok(()) } + /// Add 'net devices' to `VmConfig devices`. pub fn add_net_device_config(&mut self, args: &qmp_schema::DeviceAddArgument) { let mut device_info = args.driver.clone(); diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 41a0279ca..9ad3fd2cb 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -10,15 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{path::Path, str::FromStr}; - use anyhow::{anyhow, bail, Context, Result}; -use strum::{EnumCount, IntoEnumIterator}; -use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; -use super::error::ConfigError; +use super::{error::ConfigError, get_cameradev_by_id}; use crate::config::{ - check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, + check_arg_nonexist, check_arg_too_long, CamBackendType, CameraDevConfig, CmdParser, + ConfigCheck, ScsiDevConfig, VmConfig, }; use util::aio::AioEngine; @@ -152,49 +149,37 @@ pub fn parse_usb_tablet(conf: &str) -> Result { Ok(dev) } -#[derive(Clone, Copy, Debug, EnumCountMacro, EnumIter, PartialEq, Eq)] -pub enum CamBackendType { - V4l2, - Demo, -} - -impl FromStr for CamBackendType { - type Err = anyhow::Error; - - fn from_str(s: &str) -> std::result::Result { - for i in CamBackendType::iter() { - if s == CAM_OPT_STR_BACKEND_TYPES[i as usize] { - return Ok(i); - } - } - - Err(anyhow!("Unknown camera backend type")) - } -} - -pub const CAM_OPT_STR_BACKEND_TYPES: [&str; CamBackendType::COUNT] = ["v4l2", "demo"]; - -pub fn parse_usb_camera(conf: &str) -> Result { +pub fn parse_usb_camera(vm_config: &mut VmConfig, conf: &str) -> Result { let mut cmd_parser = CmdParser::new("usb-camera"); cmd_parser .push("") .push("id") - .push("backend") - .push("path") + .push("cameradev") .push("iothread"); cmd_parser.parse(conf)?; let mut dev = UsbCameraConfig::new(); + let drive = cmd_parser + .get_value::("cameradev") + .with_context(|| "`cameradev` is missing for usb-camera")?; + let cameradev = get_cameradev_by_id(vm_config, drive.clone().unwrap()).with_context(|| { + format!( + "no cameradev found with id {:?} for usb-camera", + drive.unwrap() + ) + })?; + dev.id = cmd_parser.get_value::("id")?; - dev.backend = cmd_parser.get_value::("backend")?.unwrap(); - dev.path = cmd_parser.get_value::("path")?; + dev.backend = cameradev.backend; + dev.path = cameradev.path.clone(); + dev.drive = cameradev; dev.iothread = cmd_parser.get_value::("iothread")?; dev.check()?; Ok(dev) } -fn check_id(id: Option, device: &str) -> Result<()> { +pub fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; check_arg_too_long(&id.unwrap(), "id")?; @@ -207,6 +192,7 @@ pub struct UsbCameraConfig { pub backend: CamBackendType, pub path: Option, pub iothread: Option, + pub drive: CameraDevConfig, } impl UsbCameraConfig { @@ -216,6 +202,7 @@ impl UsbCameraConfig { backend: CamBackendType::Demo, path: None, iothread: None, + drive: CameraDevConfig::new(), } } } @@ -228,9 +215,7 @@ impl Default for UsbCameraConfig { impl ConfigCheck for UsbCameraConfig { fn check(&self) -> Result<()> { - // Note: backend has already been checked during args parsing. check_id(self.id.clone(), "usb-camera")?; - check_camera_path(self.path.clone())?; if self.iothread.is_some() { check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; } @@ -238,17 +223,6 @@ impl ConfigCheck for UsbCameraConfig { } } -fn check_camera_path(path: Option) -> Result<()> { - check_arg_nonexist(path.clone(), "path", "usb-camera")?; - - let path = path.unwrap(); - if !Path::new(&path).exists() { - bail!(ConfigError::FileNotExist(path)); - } - - Ok(()) -} - #[derive(Clone, Debug)] pub struct UsbStorageConfig { /// USB Storage device id. diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index e6ee1d325..eadeeb74e 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -18,10 +18,10 @@ use strum::VariantNames; use crate::config::ShutdownAction; use crate::qmp::qmp_schema::{ - BlockDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, CmdParameter, - DeviceAddArgument, DeviceProps, Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, - MachineInfo, MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, - QmpEvent, Target, TypeLists, UpdateRegionArgument, + BlockDevAddArgument, CameraDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, + CmdParameter, DeviceAddArgument, DeviceProps, Events, GicCap, HumanMonitorCmdArgument, + IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, NetDevAddArgument, PropList, + QmpCommand, QmpErrorClass, QmpEvent, Target, TypeLists, UpdateRegionArgument, }; use crate::qmp::{Response, Version}; @@ -181,6 +181,12 @@ pub trait DeviceInterface { /// Remove a chardev device. fn chardev_remove(&mut self, _id: String) -> Response; + /// Creates a new camera device. + fn cameradev_add(&mut self, args: CameraDevAddArgument) -> Response; + + /// Delete a camera device. + fn cameradev_del(&mut self, id: String) -> Response; + /// Receive a file descriptor via SCM rights and assign it a name. fn getfd(&self, fd_name: String, if_fd: Option) -> Response; diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index c6cb47c42..b036c961d 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -452,12 +452,14 @@ fn qmp_command_exec( (blockdev_del, blockdev_del, node_name), (netdev_del, netdev_del, id), (chardev_remove, chardev_remove, id), + (cameradev_del, cameradev_del,id), (balloon, balloon, value), (migrate, migrate, uri); (device_add, device_add), (blockdev_add, blockdev_add), (netdev_add, netdev_add), (chardev_add, chardev_add), + (cameradev_add, cameradev_add), (update_region, update_region), (human_monitor_command, human_monitor_command) ); diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 968ac7a9a..e219b5eea 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -124,6 +124,16 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + cameradev_add { + arguments: cameradev_add, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, + cameradev_del { + arguments: cameradev_del, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, #[serde(rename = "query-hotpluggable-cpus")] #[strum(serialize = "query-hotpluggable-cpus")] query_hotpluggable_cpus { @@ -604,6 +614,7 @@ pub struct device_add { pub port: Option, pub backend: Option, pub path: Option, + pub cameradev: Option, } pub type DeviceAddArgument = device_add; @@ -786,6 +797,40 @@ impl Command for netdev_add { } } +/// cameradev_add +/// +/// # Arguments +/// +/// * `id` - the device's ID, must be unique. +/// * `path` - the backend camera file, eg. /dev/video0. +/// * `driver` - the backend type, eg. v4l2. +/// +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "cameradev_add", +/// "arguments": {"id": "cam0", "driver": "v4l2", "path": "/dev/video0" }} +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct cameradev_add { + pub id: String, + pub path: Option, + pub driver: String, +} + +pub type CameraDevAddArgument = cameradev_add; + +impl Command for cameradev_add { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct AddrDataOptions { @@ -973,6 +1018,38 @@ impl Command for netdev_del { } } +/// cameradev_del +/// +/// Remove a camera backend. +/// +/// # Arguments +/// +/// * `id` - The name of the camera backend to remove. +/// +/// # Errors +/// +/// If `id` is not a valid camera backend, DeviceNotFound +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "cameradev_del", "arguments": { "id": "cam0" } } +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct cameradev_del { + pub id: String, +} + +impl Command for cameradev_del { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + /// query-hotpluggable-cpus: /// /// # Returns @@ -1565,13 +1642,20 @@ impl Command for query_version { /// /// ```text /// -> { "execute": "query-commands" } -/// <- {"return":[{"name":"qmp_capabilities"},{"name":"quit"},{"name":"stop"}, -/// {"name":"cont"},{"name":"system_powerdown"},{"name":"system_reset"},{"name":"device_add"}, -/// {"name":"device_del"},{"name":"netdev_add"},{"name":"netdev_del"},{"name":"query-hotpluggable-cpus"}, +/// <- {"return":[{"name":"qmp_capabilities"},{"name":"quit"},{"name":"stop"},{"name":"cont"}, +/// {"name":"system_powerdown"},{"name":"system_reset"},{"name":"device_add"},{"name":"device_del"}, +/// {"name":"chardev_add"},{"name":"chardev_remove"},{"name":"netdev_add"},{"name":"netdev_del"}, +/// {"name":"cameradev_add"},{"name":"cameradev_del"},{"name":"query-hotpluggable-cpus"}, /// {"name":"query-cpus"},{"name":"query_status"},{"name":"getfd"},{"name":"blockdev_add"}, -/// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"},{"name":"query_vnc"}, -/// {"name":"migrate"},{"name":"query_migrate"},{"name":"query_version"}, -/// {"name":"query_target"},{"name":"query_commands"}]} +/// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"},{"name":"query-vnc"}, +/// {"name":"migrate"},{"name":"query_migrate"},{"name":"cancel_migrate"},{"name":"query_version"}, +/// {"name":"query_commands"},{"name":"query_target"},{"name":"query_kvm"},{"name":"query_machines"}, +/// {"name":"query-events"},{"name":"list_type"},{"name":"device_list_properties"},{"name":"block-commit"}, +/// {"name":"query_tpm_models"},{"name":"query_tpm_types"},{"name":"query_command_line_options"}, +/// {"name":"query_migrate_capabilities"},{"name":"query_qmp_schema"},{"name":"query_sev_capabilities"}, +/// {"name":"query-chardev"},{"name":"qom-list"},{"name":"qom_get"},{"name":"query-block"},{"name":"query-named-block-nodes"}, +/// {"name":"query-blockstats"},{"name":"query-block-jobs"},{"name":"query-gic-capabilities"},{"name":"query-iothreads"}, +/// {"name":"update_region"},{"name":"input_event"},{"name":"human_monitor_command"}]} /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_commands {} -- Gitee From 9c7be0328cd553b34acff6556e8b8120655fd43e Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 24 Apr 2023 17:40:46 +0800 Subject: [PATCH 1046/2187] virtio-gpu: set curosr img invisible if needed When front driver choose to display cursor img in scanout img, we set cursor img invisable to avoid imgs overlapping. Signed-off-by: wubinfeng --- virtio/src/device/gpu.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a3763f280..0ef02701b 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -366,6 +366,8 @@ struct GpuScanout { resource_id: u32, // Unused with vnc backend, work in others. cursor: VirtioGpuUpdateCursor, + // Cursor visable + cursor_visible: bool, } impl GpuScanout { @@ -374,6 +376,7 @@ impl GpuScanout { self.surface = None; self.width = 0; self.height = 0; + self.cursor_visible = false; } } @@ -602,6 +605,27 @@ impl GpuIoHandler { if req.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { scanout.cursor.pos.x_coord = info_cursor.hot_x; scanout.cursor.pos.y_coord = info_cursor.hot_y; + if info_cursor.resource_id == 0 && scanout.cursor_visible && scanout.mouse.is_some() { + let data = &mut scanout.mouse.as_mut().unwrap().data; + // In order to improve performance, displaying cursor by virtio-gpu. + // But we have to displaying it in guest img if virtio-gpu can't do display job. + // In this case, to avoid overlapping displaying two cursor imgs, change + // cursor (render by virtio-gpu) color to transparent. + // + // Only A or X byte in RGBA\X needs to be set. + // We sure that the data is assembled in format like RGBA and the minimum unit + // is byte, so there is no size end problem. + // + // TODO: How much impact does it have on performance? + for (i, item) in data.iter_mut().enumerate() { + if i % 4 == 3 { + *item = 0_u8; + } + } + + display_cursor_define(&scanout.con, scanout.mouse.as_mut().unwrap())?; + scanout.cursor_visible = false; + } } else if req.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { if scanout.mouse.is_none() { let tmp_mouse = DisplayMouse { @@ -637,10 +661,12 @@ impl GpuIoHandler { ptr::copy(res_data_ptr, con.as_mut_ptr(), mouse_data_size as usize); mse.data.clear(); mse.data.append(&mut con); + scanout.cursor_visible = true; } } } } + if let Some(mouse) = &mut scanout.mouse { display_cursor_define(&scanout.con, mouse)?; } -- Gitee From 8cf394854d8565336183082475974654761c9535 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 27 Apr 2023 22:49:05 +0800 Subject: [PATCH 1047/2187] virtio-gpu: fix cursor color bug in backend Fix the bug which cursor color is not as expected when using windows as guest. In addition, the cursor img data array is populated in a different way. Signed-off-by: wubinfeng --- virtio/src/device/gpu.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 0ef02701b..a39f76d7e 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -654,13 +654,22 @@ impl GpuIoHandler { let mse = scanout.mouse.as_mut().unwrap(); if res_width as u32 == mse.width && res_height as u32 == mse.height { - let pixels = mse.width * mse.height; - let mouse_data_size = pixels * (size_of::() as u32); - let mut con = vec![0u8; 64 * 64 * 4]; let res_data_ptr = pixman_image_get_data(res.pixman_image) as *mut u8; - ptr::copy(res_data_ptr, con.as_mut_ptr(), mouse_data_size as usize); - mse.data.clear(); - mse.data.append(&mut con); + let mse_data_size = mse.data.len(); + ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); + // Front-end drvier does not deliver data in format sequence. + // So we fix it in back-end. + // + // TODO: Fix front-end driver is a better solution. + if res.format == VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM + || res.format == VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM + { + let mut i = 0; + while i < mse_data_size { + mse.data.swap(i, i + 2); + i += 4; + } + } scanout.cursor_visible = true; } } -- Gitee From c2e7a3f660492a638b64c55c669936e6db113051 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 5 May 2023 06:23:12 +0800 Subject: [PATCH 1048/2187] virtio: convert gpa iovec to hva iovec by using function Using functions gpa_elemiovec_to_hva_iovec to Convert gpa elemiovec to hva iovec. Signed-off-by: liuxiangdong --- virtio/src/device/block.rs | 27 +++++--------- virtio/src/device/gpu.rs | 62 +++++++++++---------------------- virtio/src/device/scsi_cntlr.rs | 58 ++++++------------------------ virtio/src/lib.rs | 28 ++++++++++++++- 4 files changed, 67 insertions(+), 108 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 8033e6f25..2cb0df390 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -24,9 +24,9 @@ use std::time::Instant; use crate::VirtioError; use crate::{ - iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, virtio_has_feature, - Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, + virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + VirtioTrace, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, @@ -262,23 +262,12 @@ impl Request { } // Otherwise discard the last "status" byte. _ => iov_discard_back(&mut elem.in_iovec, 1), - }; - if data_iovec.is_none() { - bail!("Empty data for block request"); - } - for elem_iov in data_iovec.unwrap() { - if let Some(hva) = handler.mem_space.get_host_address(elem_iov.addr) { - let iov = Iovec { - iov_base: hva, - iov_len: u64::from(elem_iov.len), - }; - request.iovec.push(iov); - // Note: elem_iov total len is no more than 1<<32. - request.data_len += u64::from(elem_iov.len); - } else { - bail!("Map desc base {:?} failed", elem_iov.addr); - } } + .with_context(|| "Empty data for block request")?; + + let (data_len, iovec) = gpa_hva_iovec_map(data_iovec, &handler.mem_space)?; + request.data_len = data_len; + request.iovec = iovec; } VIRTIO_BLK_T_FLUSH => (), others => { diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a39f76d7e..43be62ae6 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -11,17 +11,17 @@ // See the Mulan PSL v2 for more details. use crate::{ - iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, - VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, - VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_F_EDID, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, - VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, + VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, + VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, + VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Result}; @@ -295,37 +295,17 @@ impl VirtioGpuRequest { }; let mut out_iovec = elem.out_iovec.clone(); - // Size of out_iovec no less than sizeo of VirtioGpuCtrlHdr, so + // Size of out_iovec is no less than size of VirtioGpuCtrlHdr, so // it is possible to get none back. - if let Some(data_iovec) = - iov_discard_front(&mut out_iovec, size_of::() as u64) - { - for elem_iov in data_iovec { - if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { - let iov = Iovec { - iov_base: hva, - iov_len: u64::from(elem_iov.len), - }; - request.out_iovec.push(iov); - request.out_len += elem_iov.len; - } else { - bail!("Map desc base {:?} failed.", elem_iov.addr); - } - } - } - - for elem_iov in elem.in_iovec.iter() { - if let Some(hva) = mem_space.get_host_address(elem_iov.addr) { - let iov = Iovec { - iov_base: hva, - iov_len: u64::from(elem_iov.len), - }; - request.in_iovec.push(iov); - request.in_len += elem_iov.len; - } else { - bail!("Map desc base {:?} failed.", elem_iov.addr); - } - } + let data_iovec = iov_discard_front(&mut out_iovec, size_of::() as u64) + .unwrap_or_default(); + let (data_len, iovec) = gpa_hva_iovec_map(data_iovec, mem_space)?; + request.out_len = data_len as u32; + request.out_iovec = iovec; + + let (data_len, iovec) = gpa_hva_iovec_map(&elem.in_iovec, mem_space)?; + request.in_len = data_len as u32; + request.in_iovec = iovec; Ok(request) } diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 9a063c735..c159db874 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -21,9 +21,9 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use crate::{ - iov_to_buf, report_virtio_error, ElemIovec, Element, Queue, VirtioDevice, VirtioError, - VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, + VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; use devices::ScsiBus::{ @@ -464,34 +464,6 @@ type CtrlQueueTmfRequest = VirtioScsiRequest; -/// Convert GPA buffer iovec to HVA buffer iovec. -fn gpa_elemiovec_to_hva_iovec( - iovec: &[ElemIovec], - mem_space: &AddressSpace, - mut skip_size: u32, - iov_size: &mut u32, -) -> Result> { - let mut hva_iovec = Vec::new(); - for elem in iovec.iter() { - if skip_size >= elem.len { - skip_size -= elem.len; - } else { - let hva = mem_space - .get_host_address(elem.addr) - .with_context(|| "Map iov base failed")?; - let len = elem.len - skip_size; - hva_iovec.push(Iovec { - iov_base: hva + skip_size as u64, - iov_len: u64::from(len), - }); - *iov_size += len; - skip_size = 0; - } - } - - Ok(hva_iovec) -} - /// T: request; U:response. impl VirtioScsiRequest { fn new( @@ -552,22 +524,14 @@ impl VirtioScsiReq }; // Get possible dataout buffer from virtqueue Element. - let mut out_len: u32 = 0; - let out_iovec = gpa_elemiovec_to_hva_iovec( - &elem.out_iovec, - mem_space, - size_of::() as u32, - &mut out_len, - )?; + let mut iovec = elem.out_iovec.clone(); + let elemiov = iov_discard_front(&mut iovec, size_of::() as u64).unwrap_or_default(); + let (out_len, out_iovec) = gpa_hva_iovec_map(elemiov, mem_space)?; // Get possible dataout buffer from virtqueue Element. - let mut in_len: u32 = 0; - let in_iovec = gpa_elemiovec_to_hva_iovec( - &elem.in_iovec, - mem_space, - size_of::() as u32, - &mut in_len, - )?; + let mut iovec = elem.in_iovec.clone(); + let elemiov = iov_discard_front(&mut iovec, size_of::() as u64).unwrap_or_default(); + let (in_len, in_iovec) = gpa_hva_iovec_map(elemiov, mem_space)?; if out_len > 0 && in_len > 0 { warn!("Wrong scsi request! Don't support both datain and dataout buffer"); @@ -577,11 +541,11 @@ impl VirtioScsiReq if out_len > 0 { request.mode = ScsiXferMode::ScsiXferToDev; - request.data_len = out_len; + request.data_len = out_len as u32; request.iovec = out_iovec; } else if in_len > 0 { request.mode = ScsiXferMode::ScsiXferFromDev; - request.data_len = in_len; + request.data_len = in_len as u32; request.iovec = in_iovec; } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 3e51ba7a7..1e733fa33 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -41,7 +41,7 @@ use vmm_sys_util::eventfd::EventFd; use address_space::AddressSpace; use machine_manager::config::ConfigCheck; -use util::aio::mem_to_buf; +use util::aio::{mem_to_buf, Iovec}; use util::num_ops::write_u32; use util::AsAny; @@ -503,3 +503,29 @@ pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ } None } + +/// Convert GPA buffer iovec to HVA buffer iovec. +/// If don't need the entire iovec, use iov_discard_front/iov_discard_back firstly. +fn gpa_hva_iovec_map( + gpa_elemiovec: &[ElemIovec], + mem_space: &AddressSpace, +) -> Result<(u64, Vec)> { + let mut iov_size = 0; + let mut hva_iovec = Vec::new(); + + for elem in gpa_elemiovec.iter() { + let hva = mem_space.get_host_address(elem.addr).with_context(|| { + format!( + "Map iov base {:x?}, iov len {:?} failed", + elem.addr, elem.len + ) + })?; + hva_iovec.push(Iovec { + iov_base: hva as u64, + iov_len: u64::from(elem.len), + }); + iov_size += elem.len as u64; + } + + Ok((iov_size, hva_iovec)) +} -- Gitee From 0a4494c956a7701d0a3eb4b45dcd32c16432f63d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 5 May 2023 15:37:50 +0800 Subject: [PATCH 1049/2187] camera: use the default value from the format list Use the default interval and frame size from the format list. Signed-off-by: zhouli57 --- devices/src/camera_backend/mod.rs | 12 +++++++++-- devices/src/usb/camera.rs | 34 +++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index b4adddb19..e37fb26b5 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -48,8 +48,8 @@ impl CamFmt { #[derive(Clone, Copy, Default, Debug)] pub struct CamBasicFmt { - width: u32, - height: u32, + pub width: u32, + pub height: u32, fps: u32, fmttype: FmtType, } @@ -109,6 +109,14 @@ pub struct CameraFormatList { pub frame: Vec, } +pub fn get_video_frame_size(width: u32, height: u32) -> Result { + width + .checked_mul(height) + .with_context(|| format!("Invalid width {} or height {}", width, height))? + .checked_mul(2) + .with_context(|| format!("Invalid width {} or height {}", width, height)) +} + #[macro_export] macro_rules! video_fourcc { ($a:expr, $b:expr, $c:expr, $d:expr) => { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 67c8a6e25..ccc7f931e 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -36,8 +36,8 @@ use util::loop_context::{ use super::camera_media_type_guid::MEDIA_TYPE_GUID_HASHMAP; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{ - camera_ops, CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, - CameraNotifyCallback, FmtType, + camera_ops, get_video_frame_size, CamBasicFmt, CameraBrokenCallback, CameraFormatList, + CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, }; use crate::usb::config::*; use crate::usb::descriptor::*; @@ -92,7 +92,6 @@ const VS_PROBE_CONTROL: u8 = 1; const VS_COMMIT_CONTROL: u8 = 2; const MAX_PAYLOAD: u32 = FRAME_SIZE_1280_720; -const FPS_30_INTERVAL: u32 = 333333; const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; pub struct UsbCamera { @@ -591,11 +590,18 @@ pub struct VideoStreamingControl { impl ByteCode for VideoStreamingControl {} impl VideoStreamingControl { - fn reset(&mut self) { + fn reset(&mut self, fmt: &CamBasicFmt) { self.bFormatIndex = 1; - self.bFormatIndex = 2; - self.dwFrameInterval = FPS_30_INTERVAL; - self.dwMaxVideoFrameSize = FRAME_SIZE_1280_720; + self.bFrameIndex = 1; + self.dwFrameInterval = fmt.get_frame_intervals().unwrap_or_else(|e| { + error!("Invalid interval {:?}", e); + 0 + }); + self.dwMaxVideoFrameSize = + get_video_frame_size(fmt.width, fmt.height).unwrap_or_else(|e| { + error!("Invalid frame size {:?}", e); + 0 + }); self.dwMaxPayloadTransferSize = MAX_PAYLOAD; } } @@ -814,6 +820,16 @@ impl UsbCamera { } Ok(()) } + + fn reset_vs_control(&mut self) { + let default_fmt = self + .camera_dev + .lock() + .unwrap() + .get_format_by_index(1, 1) + .unwrap_or_default(); + self.vs_control.reset(&default_fmt); + } } impl UsbDeviceOps for UsbCamera { @@ -844,7 +860,7 @@ impl UsbDeviceOps for UsbCamera { if let Err(e) = self.unregister_camera_fd() { error!("Failed to unregister fd when reset {:?}", e); } - self.vs_control.reset(); + self.reset_vs_control(); self.payload.lock().unwrap().reset(); self.camera_dev.lock().unwrap().reset(); self.packet_list.lock().unwrap().clear(); @@ -1205,7 +1221,7 @@ fn gen_frm_desc(pixfmt: FmtType, frm: &CameraFrame) -> Result> { wHeight: frm.height as u16, dwMinBitRate: 442368000, dwMaxBitRate: 442368000, - dwMaxVideoFrameBufSize: frm.width as u32 * frm.height as u32 * 2 + 589, + dwMaxVideoFrameBufSize: get_video_frame_size(frm.width, frm.height)?, dwDefaultFrameInterval: frm.interval, bFrameIntervalType: 1, dwIntervalVals: frm.interval, -- Gitee From 37151745693c63e9e4577001a8aeb96e0bbe36c1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 8 May 2023 08:51:46 +0800 Subject: [PATCH 1050/2187] std_machine: Add shutdown_req support for aarch64 Log the vm state tranfer err message and use destroy() for aarch64 and x86. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/mod.rs | 9 ++++++--- machine/src/standard_vm/aarch64/mod.rs | 21 ++++++++++++++++----- machine/src/standard_vm/mod.rs | 12 +++++++----- machine/src/standard_vm/x86_64/mod.rs | 24 ++++++++---------------- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 220555632..6ccafea23 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -916,15 +916,18 @@ impl MachineLifecycle for LightMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - self.vm_state_transfer( + if let Err(e) = self.vm_state_transfer( &self.cpus, #[cfg(target_arch = "aarch64")] &self.irq_chip, &mut self.vm_state.0.lock().unwrap(), old, new, - ) - .is_ok() + ) { + error!("VM state transfer failed: {:?}", e); + return false; + } + true } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 00a5a7743..66d42cc07 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -153,6 +153,8 @@ pub struct StdMachine { pub power_button: Arc, /// All configuration information of virtual machine. vm_config: Arc>, + /// Shutdown request, handle VM `shutdown` event. + shutdown_req: Arc, /// Reset request, handle VM `Reset` event. reset_req: Arc, /// Device Tree Blob. @@ -214,6 +216,10 @@ impl StdMachine { .with_context(|| MachineError::InitEventFdErr("power_button".to_string()))?, ), vm_config: Arc::new(Mutex::new(vm_config.clone())), + shutdown_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("shutdown_req".to_string()))?, + ), reset_req: Arc::new( EventFd::new(libc::EFD_NONBLOCK) .with_context(|| MachineError::InitEventFdErr("reset_req".to_string()))?, @@ -531,11 +537,13 @@ impl MachineOps for StdMachine { use super::error::StandardVmError as StdErrorKind; let nr_cpus = vm_config.machine_config.nr_cpus; - let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm - .register_reset_event(locked_vm.reset_req.clone(), clone_vm) + .register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) + .with_context(|| "Fail to register shutdown event")?; + locked_vm + .register_reset_event(locked_vm.reset_req.clone(), vm.clone()) .with_context(|| "Fail to register reset event")?; locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( @@ -1073,14 +1081,17 @@ impl MachineLifecycle for StdMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - self.vm_state_transfer( + if let Err(e) = self.vm_state_transfer( &self.cpus, &self.irq_chip, &mut self.vm_state.0.lock().unwrap(), old, new, - ) - .is_ok() + ) { + error!("VM state transfer failed: {:?}", e); + return false; + } + true } } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 312270c18..7c2e49fce 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -21,9 +21,9 @@ pub use error::StandardVmError; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; use log::error; -use machine_manager::config::get_cameradev_config; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::UpdateRegionArgument; +use machine_manager::{config::get_cameradev_config, machine::MachineLifecycle}; #[cfg(not(target_env = "musl"))] use ui::{ input::{key_event, point_event}, @@ -223,8 +223,7 @@ trait StdMachineOps: AcpiBuilder { Ok(()) } - #[cfg(target_arch = "x86_64")] - fn register_acpi_shutdown_event( + fn register_shutdown_event( &self, shutdown_req: Arc, clone_vm: Arc>, @@ -234,8 +233,11 @@ trait StdMachineOps: AcpiBuilder { let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { let _ret = shutdown_req.read().unwrap(); - StdMachine::handle_shutdown_request(&clone_vm); - Some(gen_delete_notifiers(&[shutdown_req_fd])) + if clone_vm.lock().unwrap().destroy() { + Some(gen_delete_notifiers(&[shutdown_req_fd])) + } else { + None + } }); let notifier = EventNotifier::new( NotifierOperation::AddShared, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index d31cd8a40..3168f9ac5 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -239,19 +239,6 @@ impl StdMachine { Ok(()) } - pub fn handle_shutdown_request(vm: &Arc>) -> bool { - let locked_vm = vm.lock().unwrap(); - for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { - if let Err(e) = cpu.destroy() { - error!("Failed to destroy vcpu{}, error is {:?}", cpu_index, e); - } - } - - let mut vmstate = locked_vm.vm_state.0.lock().unwrap(); - *vmstate = KvmVmState::Shutdown; - true - } - fn arch_init() -> Result<()> { let kvm_fds = KVM_FDS.load(); let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); @@ -287,7 +274,7 @@ impl StdMachine { )?; self.register_reset_event(self.reset_req.clone(), vm) .with_context(|| "Fail to register reset event in LPC")?; - self.register_acpi_shutdown_event(ich.shutdown_req.clone(), clone_vm) + self.register_shutdown_event(ich.shutdown_req.clone(), clone_vm) .with_context(|| "Fail to register shutdown event in LPC")?; ich.realize()?; Ok(()) @@ -884,8 +871,13 @@ impl MachineLifecycle for StdMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - self.vm_state_transfer(&self.cpus, &mut self.vm_state.0.lock().unwrap(), old, new) - .is_ok() + if let Err(e) = + self.vm_state_transfer(&self.cpus, &mut self.vm_state.0.lock().unwrap(), old, new) + { + error!("VM state transfer failed: {:?}", e); + return false; + } + true } } -- Gitee From 46327e98c02049966fed02b7dae0b9f7e992fff8 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Fri, 28 Apr 2023 17:30:16 +0800 Subject: [PATCH 1051/2187] mst: add usb hotplug mst Add the mst testcases of the hotplug function. Signed-off-by: mayunlong --- tests/mod_test/src/libdriver/usb.rs | 50 ++++++- tests/mod_test/tests/usb_test.rs | 218 +++++++++++++++++++++++++++- 2 files changed, 259 insertions(+), 9 deletions(-) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 7cc4214e3..bed53c8f6 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -19,6 +19,7 @@ use std::{ }; use byteorder::{ByteOrder, LittleEndian}; +use serde_json::Value; use super::{ machine::TestStdMachine, @@ -733,7 +734,7 @@ impl TestXhciPciDevice { let hcsparams1 = self .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x4) as u64); - assert_eq!(hcsparams1, 0x08000140); + assert_eq!(hcsparams1 & 0xffffff, 0x000140); // HCSPARAMS2 let hcsparams2 = self .pci_dev @@ -777,7 +778,7 @@ impl TestXhciPciDevice { let usb2_port = self .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x28) as u64); - assert!(usb2_port & 0x400 == 0x400); + let usb2_port_num = (usb2_port >> 8) & 0xff; // extend capability end let end = self .pci_dev @@ -795,12 +796,17 @@ impl TestXhciPciDevice { let usb3_port = self .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x38) as u64); - assert!(usb3_port & 0x400 == 0x400); + let usb3_port_num = (usb3_port >> 8) & 0xff; // extend capability end let end = self .pci_dev .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x3c) as u64); assert_eq!(end, 0); + // Max ports + let hcsparams1 = self + .pci_dev + .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x4) as u64); + assert_eq!(hcsparams1 >> 24, usb2_port_num + usb3_port_num); } pub fn init_max_device_slot_enabled(&mut self) { @@ -2188,6 +2194,44 @@ pub fn qmp_send_pointer_event(test_state: RefMut, x: i32, y: i32, btn test_state.qmp(&str); } +pub fn qmp_plug_keyboard_event(test_state: RefMut, num: u32) -> Value { + let num_str = format!("{}", num); + let mut str = + "{\"execute\":\"device_add\",\"arguments\":{\"driver\":\"usb-kbd\",\"id\":\"input" + .to_string(); + str += &num_str; + str += "\",\"bus\":\"usb.0\",\"port\":\"1\"}}"; + + let value = test_state.qmp(&str); + value +} + +pub fn qmp_plug_tablet_event(test_state: RefMut, num: u32) -> Value { + let num_str = format!("{}", num); + let mut str = + "{\"execute\":\"device_add\",\"arguments\":{\"driver\":\"usb-tablet\",\"id\":\"input" + .to_string(); + str += &num_str; + str += "\",\"bus\":\"usb.0\",\"port\":\"2\"}}"; + + let value = test_state.qmp(&str); + value +} + +pub fn qmp_unplug_usb_event(test_state: RefMut, num: u32) -> Value { + let num_str = format!("{}", num); + let mut str = "{\"execute\":\"device_del\",\"arguments\":{\"id\":\"input".to_string(); + str += &num_str; + str += "\"}}"; + + let value = test_state.qmp(&str); + value +} + +pub fn qmp_event_read(test_state: RefMut) { + test_state.qmp_read(); +} + pub fn clear_iovec(test_state: RefMut, iovecs: &Vec) { for iov in iovecs.iter() { test_state.memwrite(iov.io_base, &vec![0; iov.io_len as usize]); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 28cabb25b..1761b9c98 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -23,12 +23,13 @@ use devices::usb::xhci::{TRBCCode, TRBType, TRB_SIZE}; use devices::usb::UsbDeviceRequest; use mod_test::libdriver::pci::{PCI_DEVICE_ID, PCI_VENDOR_ID}; use mod_test::libdriver::usb::{ - clear_iovec, qmp_send_key_event, qmp_send_multi_key_event, qmp_send_pointer_event, TestIovec, - TestNormalTRB, TestUsbBuilder, CONTROL_ENDPOINT_ID, HID_DEVICE_ENDPOINT_ID, HID_KEYBOARD_LEN, - HID_POINTER_LEN, KEYCODE_NUM1, KEYCODE_SPACE, PCI_CLASS_PI, PRIMARY_INTERRUPTER_ID, - TD_TRB_LIMIT, XHCI_PCI_CAP_OFFSET, XHCI_PCI_DOORBELL_OFFSET, XHCI_PCI_FUN_NUM, - XHCI_PCI_OPER_OFFSET, XHCI_PCI_PORT_OFFSET, XHCI_PCI_RUNTIME_OFFSET, XHCI_PCI_SLOT_NUM, - XHCI_PORTSC_OFFSET, + clear_iovec, qmp_event_read, qmp_plug_keyboard_event, qmp_plug_tablet_event, + qmp_send_key_event, qmp_send_multi_key_event, qmp_send_pointer_event, qmp_unplug_usb_event, + TestIovec, TestNormalTRB, TestUsbBuilder, CONTROL_ENDPOINT_ID, HID_DEVICE_ENDPOINT_ID, + HID_KEYBOARD_LEN, HID_POINTER_LEN, KEYCODE_NUM1, KEYCODE_SPACE, PCI_CLASS_PI, + PRIMARY_INTERRUPTER_ID, TD_TRB_LIMIT, XHCI_PCI_CAP_OFFSET, XHCI_PCI_DOORBELL_OFFSET, + XHCI_PCI_FUN_NUM, XHCI_PCI_OPER_OFFSET, XHCI_PCI_PORT_OFFSET, XHCI_PCI_RUNTIME_OFFSET, + XHCI_PCI_SLOT_NUM, XHCI_PORTSC_OFFSET, }; #[test] @@ -2586,3 +2587,208 @@ fn test_xhci_tablet_invalid_trb() { xhci.test_pointer_event(slot_id, test_state.clone()); test_state.borrow_mut().stop(); } + +/// Test basic hotplug function. +/// TestStep: +/// 1. Start a domain without tablet and keyboard. +/// 2. Hotplug tablet and keyboard. +/// 3. Test the tablet and keyboard functions. +/// 4. Unplug tablet and keyboard. +/// Except: +/// 1/2/3/4: success. +#[test] +fn test_plug_usb_basic() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + qmp_plug_keyboard_event(test_state.borrow_mut(), 1); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let port_id = 1; + xhci.device_config.insert(String::from("keyboard"), true); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + + qmp_plug_tablet_event(test_state.borrow_mut(), 2); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let port_id = 2; + xhci.device_config.insert(String::from("tablet"), true); + let slot_id = xhci.init_device(port_id); + xhci.test_pointer_event(slot_id, test_state.clone()); + + qmp_unplug_usb_event(test_state.borrow_mut(), 1); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + qmp_unplug_usb_event(test_state.borrow_mut(), 2); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + test_state.borrow_mut().stop(); +} + +/// Test basic hotplug functions +/// TestStep: +/// 1. Start a domain with tablet and keyboard. +/// 2. Hotplug the new tablet and keyboard, unplug the old. +/// 3. Test the tablet and keyboard functions. +/// 4. Unplug tablet and keyboard. +/// Except: +/// 1/2/3/4: success. +#[test] +fn test_tablet_keyboard_plug() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_tablet("input1") + .with_usb_keyboard("input2") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let port_id = 1; + xhci.device_config.insert(String::from("keyboard"), false); + xhci.device_config.insert(String::from("tablet"), true); + let slot_id = xhci.init_device(port_id); + xhci.test_pointer_event(slot_id, test_state.clone()); + + let port_id = 2; + xhci.device_config.insert(String::from("keyboard"), true); + xhci.device_config.insert(String::from("tablet"), false); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + + qmp_plug_tablet_event(test_state.borrow_mut(), 3); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let port_id = 3; + xhci.device_config.insert(String::from("keyboard"), false); + xhci.device_config.insert(String::from("tablet"), true); + let slot_id_tbt = xhci.init_device(port_id); + + qmp_plug_keyboard_event(test_state.borrow_mut(), 4); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let port_id = 4; + xhci.device_config.insert(String::from("keyboard"), true); + xhci.device_config.insert(String::from("tablet"), false); + let slot_id_kbd = xhci.init_device(port_id); + + qmp_unplug_usb_event(test_state.borrow_mut(), 1); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + qmp_unplug_usb_event(test_state.borrow_mut(), 2); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + xhci.test_pointer_event(slot_id_tbt, test_state.clone()); + xhci.test_keyboard_event(slot_id_kbd, test_state.clone()); + + qmp_unplug_usb_event(test_state.borrow_mut(), 3); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + qmp_unplug_usb_event(test_state.borrow_mut(), 4); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + test_state.borrow_mut().stop(); +} + +/// Test stratovirt maximum specification +/// TestStep: +/// 1. Start a domain without tablet and keyboard. +/// 2. Plug 15 tablets and keyboards. +/// 3. Continue to hotplug tablet and keyboard, exceptation is failure. +/// 4. Unplug all tablets and keyboards. +/// Except: +/// 1/2/4: success. +/// 3: No available USB port. +#[test] +fn test_max_number_of_device() { + let max_num_of_port2 = 15; + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci_config("xhci", max_num_of_port2, 0) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let mut i = 1; + while i <= max_num_of_port2 { + qmp_plug_keyboard_event(test_state.borrow_mut(), i); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + i += 1; + } + + let value = qmp_plug_keyboard_event(test_state.borrow_mut(), 16); + let status = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!(status, "No available USB port.".to_string()); + + let value = qmp_plug_tablet_event(test_state.borrow_mut(), 16); + let status = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!(status, "No available USB port.".to_string()); + + i = 1; + while i <= max_num_of_port2 { + qmp_unplug_usb_event(test_state.borrow_mut(), i); + i += 1; + } + test_state.borrow_mut().stop(); +} + +/// Test abnormal hotplug operation +/// TestStep: +/// 1. Start a domain without tablet and keyboard. +/// 2. Unplug tablet and keyboard, exceptation is failure. +/// 3. Hotplug tablet and keyboard. +/// 4. Unplug tablet and keyboard, exceptation is success. +/// 5. Repeat the unplug operation, exceptation is failure. +/// Except: +/// 1/3/4: success. +/// 2/5: Failed to detach device: id input1 not found. +#[test] +fn test_unplug_usb_device() { + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + + let value = qmp_unplug_usb_event(test_state.borrow_mut(), 1); + let status = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!( + status, + "Failed to detach device: id input1 not found".to_string() + ); + + qmp_plug_keyboard_event(test_state.borrow_mut(), 1); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let port_id = 1; + xhci.device_config.insert(String::from("keyboard"), true); + xhci.device_config.insert(String::from("tablet"), false); + let slot_id = xhci.init_device(port_id); + xhci.test_keyboard_event(slot_id, test_state.clone()); + + qmp_unplug_usb_event(test_state.borrow_mut(), 1); + qmp_event_read(test_state.borrow_mut()); + + let value = qmp_unplug_usb_event(test_state.borrow_mut(), 1); + let status = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!( + status, + "Failed to detach device: id input1 not found".to_string() + ); + test_state.borrow_mut().stop(); +} -- Gitee From 2e1a36c58cb1c7a90fd3a79b73dc2997cfe369c6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 8 May 2023 17:23:32 +0800 Subject: [PATCH 1052/2187] Emu: Keep lifetime same with external emu Watching on the external emu pid path periodically. When emu exits, stratovirt will exit too. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 37 ++++++++++++++++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 13 +++++++-- machine/src/standard_vm/mod.rs | 20 ++++++++++++++ machine/src/standard_vm/x86_64/mod.rs | 7 +++++ machine_manager/src/cmdline.rs | 13 +++++++++ machine_manager/src/config/mod.rs | 14 ++++++++++ 6 files changed, 102 insertions(+), 2 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c0ae5226a..ea66fc34e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -24,13 +24,21 @@ use std::ops::Deref; use std::os::unix::net::UnixListener; use std::path::Path; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; +#[cfg(not(target_env = "musl"))] +use std::time::Duration; #[cfg(not(target_env = "musl"))] use devices::misc::scream::Scream; use log::warn; #[cfg(not(target_env = "musl"))] use machine_manager::config::scream::parse_scream; +#[cfg(not(target_env = "musl"))] +use machine_manager::event_loop::EventLoop; +#[cfg(not(target_env = "musl"))] +use ui::console::{get_run_stage, VmRunningStage}; use util::file::{lock_file, unlock_file}; +#[cfg(not(target_env = "musl"))] +use vmm_sys_util::eventfd::EventFd; pub use micro_vm::LightMachine; @@ -1806,3 +1814,32 @@ fn coverage_allow_list(syscall_allow_list: &mut Vec) { BpfRule::new(libc::SYS_ftruncate), ]) } + +#[cfg(not(target_env = "musl"))] +fn check_windows_emu_pid( + pid_path: String, + powerdown_req: Arc, + shutdown_req: Arc, +) { + if !Path::new(&pid_path).exists() { + log::info!("Detect windows emu exited, let VM exits now"); + if get_run_stage() == VmRunningStage::Os { + if let Err(e) = powerdown_req.write(1) { + log::error!("Failed to send powerdown request after emu exits: {:?}", e); + } + } else if let Err(e) = shutdown_req.write(1) { + log::error!("Failed to send shutdown request after emu exits: {:?}", e); + } + } else { + let check_emu_alive = Box::new(move || { + check_windows_emu_pid( + pid_path.clone(), + powerdown_req.clone(), + shutdown_req.clone(), + ); + }); + if let Some(ctx) = EventLoop::get_ctx(None) { + ctx.delay_call(check_emu_alive, Duration::from_millis(4000)); + } + } +} diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 66d42cc07..bdb5818fd 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -55,9 +55,11 @@ use devices::legacy::{ use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::parse_ramfb; use machine_manager::config::{ - parse_incoming_uri, parse_ramfb, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, - NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, + NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -619,6 +621,13 @@ impl MachineOps for StdMachine { .display_init(vm_config) .with_context(|| "Fail to init display")?; + #[cfg(not(target_env = "musl"))] + locked_vm.watch_windows_emu_pid( + vm_config, + locked_vm.power_button.clone(), + locked_vm.shutdown_req.clone(), + ); + MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index b77632087..ca16d8a2d 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1028,6 +1028,26 @@ impl StdMachine { Ok(()) } + + /// When windows emu exits, stratovirt should exits too. + #[cfg(not(target_env = "musl"))] + fn watch_windows_emu_pid( + &self, + vm_config: &VmConfig, + power_button: Arc, + shutdown_req: Arc, + ) { + let emu_pid = vm_config.windows_emu_pid.as_ref(); + if emu_pid.is_none() { + return; + } + log::info!("Watching on windows emu lifetime"); + crate::check_windows_emu_pid( + "/proc/".to_owned() + emu_pid.unwrap(), + power_button, + shutdown_req, + ); + } } impl DeviceInterface for StdMachine { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 3168f9ac5..ac0d6d584 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -496,6 +496,13 @@ impl MachineOps for StdMachine { .display_init(vm_config) .with_context(|| "Fail to init display")?; + #[cfg(not(target_env = "musl"))] + locked_vm.watch_windows_emu_pid( + vm_config, + locked_vm.shutdown_req.clone(), + locked_vm.shutdown_req.clone(), + ); + MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); MigrationManager::register_kvm_instance( diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 8dfb5c606..2b28694b1 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -458,6 +458,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("set display for virtual machine: currently only supports gtk") .takes_value(true), ) + .arg( + Arg::with_name("windows_emu_pid") + .multiple(false) + .long("windows_emu_pid") + .value_name("pid") + .help("watch on the external windows emu pid") + .takes_value(true), + ) } /// Create `VmConfig` from `ArgMatches`'s arg. @@ -493,6 +501,11 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); add_args_to_config!((args.value_of("display")), vm_cfg, add_display); + add_args_to_config!( + (args.value_of("windows_emu_pid")), + vm_cfg, + add_windows_emu_pid + ); add_args_to_config!( (args.is_present("no-shutdown")), vm_cfg, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 72b6f8ac6..d6bdf6d96 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -125,6 +125,7 @@ pub struct VmConfig { pub vnc: Option, pub display: Option, pub camera_backend: HashMap, + pub windows_emu_pid: Option, } impl VmConfig { @@ -257,6 +258,19 @@ impl VmConfig { Ok(()) } + /// Add argument `windows_emu_pid` to `VmConfig`. + /// + /// # Arguments + /// + /// * `windows_emu_pid` - The args of windows_emu_pid. + pub fn add_windows_emu_pid(&mut self, windows_emu_pid: &str) -> Result<()> { + if windows_emu_pid.is_empty() { + bail!("The arg of windows_emu_pid is empty!"); + } + self.windows_emu_pid = Some(windows_emu_pid.to_string()); + Ok(()) + } + /// Add a file to drive file store. pub fn add_drive_file( drive_files: &mut HashMap, -- Gitee From ba722324b7ea7f337440d2e88cb7fce6faa78f39 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 21:23:52 +0800 Subject: [PATCH 1053/2187] virtio-console: add class id for virtio console device Add VIRTIO_PCI_CLASS_ID_COMMUNICATION_OTHER for console device. Signed-off-by: liuxiangdong --- virtio/src/transport/virtio_pci.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 873973888..bdcaf90f7 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -46,8 +46,8 @@ use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, - VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_FS, - VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, + VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_CONSOLE, + VIRTIO_TYPE_FS, VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -58,6 +58,7 @@ const VIRTIO_PCI_ABI_VERSION: u8 = 1; const VIRTIO_PCI_CLASS_ID_NET: u16 = 0x0280; const VIRTIO_PCI_CLASS_ID_BLOCK: u16 = 0x0100; const VIRTIO_PCI_CLASS_ID_STORAGE_OTHER: u16 = 0x0180; +const VIRTIO_PCI_CLASS_ID_COMMUNICATION_OTHER: u16 = 0x0780; #[cfg(target_arch = "aarch64")] const VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER: u16 = 0x0380; #[cfg(target_arch = "x86_64")] @@ -136,6 +137,7 @@ fn get_virtio_class_id(device_type: u32) -> u16 { VIRTIO_TYPE_SCSI => VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_FS => VIRTIO_PCI_CLASS_ID_STORAGE_OTHER, VIRTIO_TYPE_NET => VIRTIO_PCI_CLASS_ID_NET, + VIRTIO_TYPE_CONSOLE => VIRTIO_PCI_CLASS_ID_COMMUNICATION_OTHER, #[cfg(target_arch = "x86_64")] VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_VGA, #[cfg(target_arch = "aarch64")] -- Gitee From df82726413765f48bcecef0a6586d2c520b4fe8d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 9 May 2023 04:38:52 +0800 Subject: [PATCH 1054/2187] timer: Add timer delete interface Sometimes we want to revoke already existed timer. Signed-off-by: Keqian Zhu --- devices/src/legacy/ramfb.rs | 6 +++--- machine/src/lib.rs | 2 +- ui/src/console.rs | 2 +- util/src/leak_bucket.rs | 2 +- util/src/loop_context.rs | 29 ++++++++++++++++++++--------- virtio/src/vhost/user/client.rs | 2 +- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index b04130dbe..cc26d945c 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -282,12 +282,12 @@ fn set_press_event(install: Arc, data: *const u8) { }); if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.delay_call( + ctx.timer_add( set_press_func, Duration::from_millis(INSTALL_CHECK_INTERVEL_MS), ); - ctx.delay_call(press_func, Duration::from_millis(INSTALL_PRESS_INTERVEL_MS)); - ctx.delay_call( + ctx.timer_add(press_func, Duration::from_millis(INSTALL_PRESS_INTERVEL_MS)); + ctx.timer_add( release_func, Duration::from_millis(INSTALL_RELEASE_INTERVEL_MS), ); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 583b0888f..f436c18ad 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1908,7 +1908,7 @@ fn check_windows_emu_pid( ); }); if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.delay_call(check_emu_alive, Duration::from_millis(4000)); + ctx.timer_add(check_emu_alive, Duration::from_millis(4000)); } } } diff --git a/ui/src/console.rs b/ui/src/console.rs index 485e45122..bc2f2a0a4 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -355,7 +355,7 @@ pub fn setup_refresh(update_interval: u64) { if update_interval != 0 { if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.delay_call(func, Duration::from_millis(update_interval)); + ctx.timer_add(func, Duration::from_millis(update_interval)); } } } diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 6691d1f7a..cf1d2490c 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -91,7 +91,7 @@ impl LeakBucket { .unwrap_or_else(|e| error!("LeakBucket send event to device failed {:?}", e)); }); - loop_context.delay_call( + loop_context.timer_add( func, Duration::from_nanos( (self.level - self.capacity) * NANOSECONDS_PER_SECOND / self.capacity, diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index de2065e42..06a9b0cdb 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -176,10 +176,9 @@ impl Timer { /// # Arguments /// /// * `func` - the function will be called later. - /// * `nsec` - delay time in nanosecond. + /// * `delay` - delay time to call the function. pub fn new(func: Box, delay: Duration) -> Self { let expire_time = get_current_time() + delay; - Timer { func, expire_time } } } @@ -205,7 +204,7 @@ pub struct EventLoopContext { /// Temp events vector, store wait returned events. ready_events: Vec, /// Timer list - timers: Arc>>, + timers: Arc>>>, } // SAFETY: The closure in EventNotifier and Timer doesn't impl Send, they're @@ -499,14 +498,15 @@ impl EventLoopContext { self.epoll_wait_manager(timeout) } - /// Call the function given by `func` after `nsec` nanoseconds. + /// Call the function given by `func` after `delay` time. /// /// # Arguments /// /// * `func` - the function will be called later. - /// * `nsec` - delay time in nanoseconds. - pub fn delay_call(&mut self, func: Box, delay: Duration) { - let timer = Timer::new(func, delay); + /// * `delay` - delay time. + pub fn timer_add(&mut self, func: Box, delay: Duration) -> u64 { + let timer = Box::new(Timer::new(func, delay)); + let timer_id = timer.as_ref() as *const _ as u64; // insert in order of expire_time let mut timers = self.timers.lock().unwrap(); @@ -520,6 +520,18 @@ impl EventLoopContext { timers.insert(index, timer); drop(timers); self.kick(); + timer_id + } + + /// Remove timer with specific timer id. + pub fn timer_del(&mut self, timer_id: u64) { + let mut timers = self.timers.lock().unwrap(); + for (i, t) in timers.iter().enumerate() { + if timer_id == t.as_ref() as *const _ as u64 { + timers.remove(i); + break; + } + } } /// Get the expire_time of the soonest Timer, and then translate it to duration. @@ -576,11 +588,10 @@ impl EventLoopContext { if timer.expire_time > now { break; } - expired_nr += 1; } - let expired_timers: Vec = timers.drain(0..expired_nr).collect(); + let expired_timers: Vec> = timers.drain(0..expired_nr).collect(); drop(timers); for timer in expired_timers { (timer.func)(); diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 1f5b74733..470fa0ca9 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -114,7 +114,7 @@ fn vhost_user_reconnect(client: &Arc>) { { if let Some(ctx) = EventLoop::get_ctx(None) { // Default reconnecting time: 3s. - ctx.delay_call(func, Duration::from_secs(3)); + ctx.timer_add(func, Duration::from_secs(3)); } else { error!("Failed to get ctx to delay vhost-user reconnecting"); } -- Gitee From 6c05bbc684001bb19f099946c76b85f706489aa3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 May 2023 15:07:17 +0800 Subject: [PATCH 1055/2187] add KVM_SET_ONE_REG in ioctl white list Fix bad system call (number 29) when VM reset. Fixes: 453232eeb643 ("bugfix: the VM pause resume stuck") Signed-off-by: yezengruan --- machine/src/standard_vm/aarch64/syscall.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index c8600834a..9986cde3a 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -272,6 +272,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_G_FMT() as u32) -- Gitee From 2af494b92e93e282a03c67f6b38dc1afafeaa388 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 16:43:17 +0800 Subject: [PATCH 1056/2187] GTK: Hide menu bar default Hide menu bar default. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index b1b778ce2..5b78c6ab5 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -238,10 +238,10 @@ impl GtkMenu { /// Show window. pub(crate) fn show_window(&self, is_full_screen: bool) { if is_full_screen { - self.menu_bar.hide(); self.window.fullscreen(); } self.window.show_all(); + self.menu_bar.hide(); } } -- Gitee From e379936f69ae2f7b0a6144c2c893570d1c421925 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 17 Apr 2023 15:52:29 +0800 Subject: [PATCH 1057/2187] GTK: set program name Set program name of desktopappengine Signed-off-by: Xiao Ye --- machine_manager/src/config/display.rs | 23 ++++++++++++++++++++++- ui/src/gtk/mod.rs | 9 ++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index a21d53e9a..1889aeabb 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -33,6 +33,8 @@ pub struct UiContext { pub struct DisplayConfig { /// Create the GTK thread. pub gtk: bool, + /// App name if configured. + pub app_name: Option, /// Fix window size. pub fix_size: bool, /// Keep the window fill the desktop. @@ -42,7 +44,11 @@ pub struct DisplayConfig { impl VmConfig { pub fn add_display(&mut self, vm_config: &str) -> Result<()> { let mut cmd_parser = CmdParser::new("display"); - cmd_parser.push("").push("full-screen").push("fix-size"); + cmd_parser + .push("") + .push("full-screen") + .push("fix-size") + .push("app-name"); cmd_parser.parse(vm_config)?; let mut display_config = DisplayConfig::default(); if let Some(str) = cmd_parser.get_value::("")? { @@ -51,6 +57,9 @@ impl VmConfig { _ => bail!("Unsupport device: {}", str), } } + if let Some(name) = cmd_parser.get_value::("app-name")? { + display_config.app_name = Some(name); + } if let Some(default) = cmd_parser.get_value::("fix-size")? { display_config.fix_size = default.into(); } @@ -111,5 +120,17 @@ mod tests { assert_eq!(display_config.gtk, true); assert_eq!(display_config.full_screen, false); assert_eq!(display_config.fix_size, false); + + let mut vm_config = VmConfig::default(); + let config_line = "gtk,app-name=desktopappengine"; + assert!(vm_config.add_display(config_line).is_ok()); + let display_config = vm_config.display.unwrap(); + assert_eq!(display_config.gtk, true); + assert_eq!(display_config.full_screen, false); + assert_eq!(display_config.fix_size, false); + assert_eq!( + display_config.app_name, + Some("desktopappengine".to_string()) + ); } } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 415ba52b7..ee72569b5 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -28,7 +28,7 @@ use gtk::{ cairo::{Format, ImageSurface}, gdk::{self, Geometry, Gravity, Screen, WindowHints}, gdk_pixbuf::Colorspace, - glib::{self, Priority, SyncSender}, + glib::{self, set_program_name, Priority, SyncSender}, prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, traits::{ CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt, @@ -451,6 +451,7 @@ impl GtkDisplayScreen { struct GtkConfig { full_screen: bool, zoom_fit: bool, + app_name: Option, vm_name: String, /// Gracefully Shutdown. powerdown_button: Option>, @@ -464,6 +465,7 @@ pub fn gtk_display_init(ds_cfg: &DisplayConfig, ui_context: UiContext) -> Result let gtk_cfg = GtkConfig { full_screen: ds_cfg.full_screen, zoom_fit: ds_cfg.fix_size, + app_name: ds_cfg.app_name.clone(), vm_name: ui_context.vm_name, powerdown_button: ui_context.power_button, shutdown_button: ui_context.shutdown_req, @@ -495,6 +497,11 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { .default_height(DEFAULT_SURFACE_HEIGHT) .build(); + // Set app name if configured. + if let Some(name) = >k_cfg.app_name { + set_program_name(Some(name)); + } + // Create menu. let mut gtk_menu = GtkMenu::new(window); let gd = Rc::new(RefCell::new(GtkDisplay::create(gtk_menu.clone(), gtk_cfg))); -- Gitee From 209b2782be642322d720b7bbfb628518a6dbf146 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 4 May 2023 11:26:06 +0800 Subject: [PATCH 1058/2187] virtio-serial: add max_ports config parameter Add max_ports config parameter. Make praperation for virtserialport. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 8 +++++--- machine_manager/src/config/chardev.rs | 29 ++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 29dad5f2e..3a64f4e6b 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -516,10 +516,12 @@ Two properties can be set for virtconsole. * id: unique device-id. * chardev: char device of virtio console device. -For virtio-serial-pci, two more properties are required. +For virtio-serial-pci, Four more properties are required. * bus: bus number of virtio console. * addr: including slot number and function number. The first number represents slot number of device and the second one represents function number of it. +* multifunction: whether to open multi-function for device. (optional) If not set, default is false. +* max_ports: max number of ports we can have for a virtio-serial device. Configuration range is [1, 31]. (optional) If not set, default is 31. ```shell # virtio mmio device @@ -528,12 +530,12 @@ of device and the second one represents function number of it. -device virtconsole,id=,chardev= # virtio pci device --device virtio-serial-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}] +-device virtio-serial-pci,id=,bus=,addr=<0x3>[,multifunction={on|off},max_ports=] -chardev socket,path=,id=,server,nowait -device virtconsole,id=,chardev= ``` NB: -Currently, only one virtio console device is supported in standard machine. +Currently, only one virtio console device is supported. Only one port is supported in microvm. ### 2.5 Virtio-vsock diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index a81ce82bc..e35ac9eaf 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -23,6 +23,9 @@ use crate::qmp::qmp_schema; const MAX_GUEST_CID: u64 = 4_294_967_295; const MIN_GUEST_CID: u64 = 3; +/// Default value of max ports for virtio-serial. +const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31; + /// Character device options. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ChardevType { @@ -424,16 +427,29 @@ pub fn parse_vsock(vsock_config: &str) -> Result { Ok(vsock) } -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct VirtioSerialInfo { pub id: String, pub pci_bdf: Option, pub multifunction: bool, + pub max_ports: u32, } impl ConfigCheck for VirtioSerialInfo { fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "virtio-serial id") + check_arg_too_long(&self.id, "virtio-serial id")?; + + if self.max_ports < 1 || self.max_ports > DEFAULT_SERIAL_PORTS_NUMBER { + return Err(anyhow!(ConfigError::IllegalValue( + "Virtio-serial max_ports".to_string(), + 1, + true, + DEFAULT_SERIAL_PORTS_NUMBER as u64, + true + ))); + } + + Ok(()) } } @@ -444,7 +460,8 @@ pub fn parse_virtio_serial(vm_config: &mut VmConfig, serial_config: &str) -> Res .push("id") .push("bus") .push("addr") - .push("multifunction"); + .push("multifunction") + .push("max_ports"); cmd_parser.parse(serial_config)?; pci_args_check(&cmd_parser)?; @@ -453,18 +470,24 @@ pub fn parse_virtio_serial(vm_config: &mut VmConfig, serial_config: &str) -> Res let multifunction = cmd_parser .get_value::("multifunction")? .map_or(false, |switch| switch.into()); + let max_ports = cmd_parser + .get_value::("max_ports")? + .unwrap_or(DEFAULT_SERIAL_PORTS_NUMBER); let virtio_serial = if serial_config.contains("-pci") { let pci_bdf = get_pci_bdf(serial_config)?; VirtioSerialInfo { id, pci_bdf: Some(pci_bdf), multifunction, + max_ports, } } else { VirtioSerialInfo { id, pci_bdf: None, multifunction, + // Micro_vm does not support multi-ports in virtio-serial-device. + max_ports: 1, } }; virtio_serial.check()?; -- Gitee From b8b5a5a3a809f55613ef08d989ad7ae256f0551b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 6 May 2023 08:16:49 +0800 Subject: [PATCH 1059/2187] util: add as_any_mut Add as_any_mut to cast trait object to mutable reference struct. Signed-off-by: liuxiangdong --- util/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/src/lib.rs b/util/src/lib.rs index fe7626f34..c490d0d5c 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -91,10 +91,15 @@ pub fn set_termi_canon_mode() -> std::io::Result<()> { /// This trait is to cast trait object to struct. pub trait AsAny { fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; } impl AsAny for T { fn as_any(&self) -> &dyn Any { self } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } -- Gitee From dad0d95703b1940c44c8dfb666de78caacdb1799 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 14:47:31 +0800 Subject: [PATCH 1060/2187] virtio-serial: add basic virtio-serial/virtio-serial-port frame Delete existing virtio-console code in stratovirt. Add basic virtio-serial/virtio-serial-port frame. We can configure it by using command line just like: -device virtio-serial-pci,id=virtio-serial0,bus=pcie0,addr=0x3,max_ports=31 \ -chardev socket,path=/home/sock1,id=virtserial1,server,nowait \ -device virtserialport,chardev=virtserial1,id=port1,nr=1 Meanwhile, virtio-console is also supported and it's command line is: -device virtio-serial-pci,id=virtio-serial0,bus=pcie0,addr=0x3,max_ports=31 \ -chardev socket,path=/home/sock1,id=virtserial1,server,nowait \ -device virtconsole,chardev=virtserial1,id=port1,nr=1 Signed-off-by: liuxiangdong Signed-off-by: wangyan --- Cargo.lock | 1 + machine/src/lib.rs | 172 ++++-- machine_manager/src/config/chardev.rs | 120 ++-- sysbus/Cargo.toml | 1 + sysbus/src/lib.rs | 7 +- virtio/src/device/mod.rs | 2 +- virtio/src/device/{console.rs => serial.rs} | 632 +++++++++----------- virtio/src/lib.rs | 8 +- 8 files changed, 496 insertions(+), 447 deletions(-) rename virtio/src/device/{console.rs => serial.rs} (36%) diff --git a/Cargo.lock b/Cargo.lock index 1d4b5dae8..ffc3b3c04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,6 +1643,7 @@ dependencies = [ "hypervisor", "kvm-ioctls", "thiserror", + "util", "vmm-sys-util", ] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index ea66fc34e..43edace06 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -67,9 +67,9 @@ use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, - parse_vhost_user_blk_pci, parse_virtconsole, parse_virtio_serial, parse_vsock, BootIndexInfo, - DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, - NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + parse_vhost_user_blk_pci, parse_virtio_serial, parse_virtserialport, parse_vsock, + BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, + NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; #[cfg(not(target_env = "musl"))] @@ -82,7 +82,7 @@ use migration::MigrationManager; use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; -use sysbus::{SysBus, SysBusDevOps}; +use sysbus::{SysBus, SysBusDevOps, SysBusDevType}; use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, @@ -91,10 +91,10 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, vhost, Balloon, Block, BlockState, Console, Rng, RngState, + balloon_allow_list, find_port_by_nr, vhost, Balloon, Block, BlockState, Rng, RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, - VhostKern, VhostUser, VirtioConsoleState, VirtioDevice, VirtioMmioDevice, VirtioMmioState, - VirtioNetState, VirtioPciDevice, + Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, + VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; pub trait MachineOps { @@ -363,61 +363,124 @@ pub trait MachineOps { Ok(()) } - /// Add console device. + /// Add virtio serial device. /// /// # Arguments /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. - fn add_virtio_console(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_virtconsole(vm_config, cfg_args)?; - let sys_mem = self.get_sys_mem(); - let console = Arc::new(Mutex::new(Console::new(device_cfg.clone()))); - if let Some(serial) = &vm_config.virtio_serial { - if serial.pci_bdf.is_none() { - let device = VirtioMmioDevice::new(sys_mem, console.clone()); - MigrationManager::register_device_instance( - VirtioMmioState::descriptor(), - self.realize_virtio_mmio_device(device) - .with_context(|| MachineError::RlzVirtioMmioErr)?, - &device_cfg.id, - ); - } else { - let virtio_serial_info = vm_config - .virtio_serial - .as_ref() - .with_context(|| "No virtio-serial-pci device configured for virtconsole")?; - // Reasonable, because for virtio-serial-pci device, the bdf has been checked. - let bdf = virtio_serial_info.pci_bdf.clone().unwrap(); - let multi_func = virtio_serial_info.multifunction; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let sys_mem = self.get_sys_mem().clone(); - let virtio_pci_device = VirtioPciDevice::new( - device_cfg.id.clone(), - devfn, - sys_mem, - console.clone(), - parent_bus, - multi_func, - ); - virtio_pci_device - .realize() - .with_context(|| "Failed to add virtio pci console device")?; - } + fn add_virtio_serial(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let serial_cfg = parse_virtio_serial(vm_config, cfg_args)?; + let sys_mem = self.get_sys_mem().clone(); + let serial = Arc::new(Mutex::new(Serial::new(serial_cfg.clone()))); + + if serial_cfg.pci_bdf.is_none() { + let device = VirtioMmioDevice::new(&sys_mem, serial.clone()); + MigrationManager::register_device_instance( + VirtioMmioState::descriptor(), + self.realize_virtio_mmio_device(device) + .with_context(|| MachineError::RlzVirtioMmioErr)?, + &serial_cfg.id, + ); } else { - bail!("No virtio-serial-bus specified"); + let bdf = serial_cfg.pci_bdf.unwrap(); + let multi_func = serial_cfg.multifunction; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let virtio_pci_device = VirtioPciDevice::new( + serial_cfg.id.clone(), + devfn, + sys_mem, + serial.clone(), + parent_bus, + multi_func, + ); + virtio_pci_device + .realize() + .with_context(|| "Failed to add virtio pci serial device")?; } + MigrationManager::register_device_instance( - VirtioConsoleState::descriptor(), - console, - &device_cfg.id, + VirtioSerialState::descriptor(), + serial, + &serial_cfg.id, ); Ok(()) } - fn add_virtio_serial(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - parse_virtio_serial(vm_config, cfg_args)?; + /// Add virtio serial port. + /// + /// # Arguments + /// + /// * `vm_config` - VM configuration. + /// * `cfg_args` - Device configuration args. + /// * `is_console` - Whether this virtio serial port is a console port. + fn add_virtio_serial_port( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + is_console: bool, + ) -> Result<()> { + let serialport_cfg = parse_virtserialport(vm_config, cfg_args, is_console)?; + let serial_cfg = vm_config + .virtio_serial + .as_ref() + .with_context(|| "No virtio serial device specified")?; + + let mut virtio_device = None; + if serial_cfg.pci_bdf.is_none() { + // Micro_vm. + for dev in self.get_sys_bus().devices.iter() { + let locked_busdev = dev.lock().unwrap(); + if locked_busdev.get_type() == SysBusDevType::VirtioMmio { + let virtio_mmio_dev = locked_busdev + .as_any() + .downcast_ref::() + .unwrap(); + if virtio_mmio_dev.device.lock().unwrap().device_type() == VIRTIO_TYPE_CONSOLE { + virtio_device = Some(virtio_mmio_dev.device.clone()); + break; + } + } + } + } else { + // Standard_vm. + let pci_dev = self + .get_pci_dev_by_id_and_type(vm_config, Some(&serial_cfg.id), "virtio-serial-pci") + .with_context(|| { + format!( + "Can not find virtio serial pci device {} from pci bus", + serial_cfg.id + ) + })?; + let locked_pcidev = pci_dev.lock().unwrap(); + let virtio_pcidev = locked_pcidev + .as_any() + .downcast_ref::() + .unwrap(); + virtio_device = Some(virtio_pcidev.get_virtio_device().clone()); + } + + let virtio_dev = virtio_device.with_context(|| "No virtio serial device found")?; + let mut virtio_dev_h = virtio_dev.lock().unwrap(); + let serial = virtio_dev_h.as_any_mut().downcast_mut::().unwrap(); + + if serialport_cfg.nr >= serial.max_nr_ports { + bail!( + "virtio serial port nr {} should be less than virtio serial's max_nr_ports {}", + serialport_cfg.nr, + serial.max_nr_ports + ); + } + if find_port_by_nr(&serial.ports, serialport_cfg.nr).is_some() { + bail!("Repetitive virtio serial port nr {}.", serialport_cfg.nr,); + } + + let mut serial_port = SerialPort::new(serialport_cfg); + let port = Arc::new(Mutex::new(serial_port.clone())); + serial_port.realize()?; + serial.ports.lock().unwrap().push(port); + Ok(()) } @@ -540,9 +603,9 @@ pub trait MachineOps { #[cfg(not(target_env = "musl"))] fn check_id_existed_in_xhci(&mut self, id: &str) -> Result { let vm_config = self.get_vm_config(); - let mut locked_vmconfig = vm_config.lock().unwrap(); + let locked_vmconfig = vm_config.lock().unwrap(); let parent_dev = self - .get_pci_dev_by_id_and_type(&mut locked_vmconfig, None, "nec-usb-xhci") + .get_pci_dev_by_id_and_type(&locked_vmconfig, None, "nec-usb-xhci") .with_context(|| "Can not find parent device from pci bus")?; let locked_parent_dev = parent_dev.lock().unwrap(); let xhci_pci = locked_parent_dev @@ -1148,7 +1211,7 @@ pub trait MachineOps { /// * `dev_type` - Device type name. fn get_pci_dev_by_id_and_type( &mut self, - vm_config: &mut VmConfig, + vm_config: &VmConfig, id: Option<&str>, dev_type: &str, ) -> Option>> { @@ -1384,7 +1447,10 @@ pub trait MachineOps { self.add_virtio_serial(vm_config, cfg_args)?; } "virtconsole" => { - self.add_virtio_console(vm_config, cfg_args)?; + self.add_virtio_serial_port(vm_config, cfg_args, true)?; + } + "virtserialport" => { + self.add_virtio_serial_port(vm_config, cfg_args, false)?; } "virtio-rng-device" | "virtio-rng-pci" => { self.add_virtio_rng(vm_config, cfg_args)?; diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index e35ac9eaf..6312c82b8 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -39,11 +39,19 @@ pub enum ChardevType { File(String), } -/// Config structure for virtio-console. +/// Config structure for virtio-serial-port. #[derive(Debug, Clone)] -pub struct VirtioConsole { +pub struct VirtioSerialPort { pub id: String, pub chardev: ChardevConfig, + pub nr: u32, + pub is_console: bool, +} + +impl ConfigCheck for VirtioSerialPort { + fn check(&self) -> Result<()> { + check_arg_too_long(&self.id, "chardev id") + } } /// Config structure for character device. @@ -255,26 +263,36 @@ pub fn get_chardev_socket_path(chardev: &str, vm_config: &mut VmConfig) -> Resul } } -pub fn parse_virtconsole(vm_config: &mut VmConfig, config_args: &str) -> Result { - let mut cmd_parser = CmdParser::new("virtconsole"); - cmd_parser.push("").push("id").push("chardev"); +pub fn parse_virtserialport( + vm_config: &mut VmConfig, + config_args: &str, + is_console: bool, +) -> Result { + let mut cmd_parser = CmdParser::new("virtserialport"); + cmd_parser.push("").push("id").push("chardev").push("nr"); cmd_parser.parse(config_args)?; let chardev_name = cmd_parser .get_value::("chardev")? .with_context(|| { - ConfigError::FieldIsMissing("chardev".to_string(), "virtconsole".to_string()) + ConfigError::FieldIsMissing("chardev".to_string(), "virtserialport".to_string()) })?; - let id = cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "virtconsole".to_string()) + ConfigError::FieldIsMissing("id".to_string(), "virtserialport".to_string()) + })?; + let nr = cmd_parser.get_value::("nr")?.with_context(|| { + ConfigError::FieldIsMissing("nr".to_string(), "virtserialport".to_string()) })?; - if let Some(char_dev) = vm_config.chardev.remove(&chardev_name) { - return Ok(VirtioConsole { + if let Some(chardev) = vm_config.chardev.remove(&chardev_name) { + let port_cfg = VirtioSerialPort { id, - chardev: char_dev, - }); + chardev, + nr, + is_console, + }; + port_cfg.check()?; + return Ok(port_cfg); } bail!("Chardev {:?} not found or is in use", &chardev_name); } @@ -453,7 +471,10 @@ impl ConfigCheck for VirtioSerialInfo { } } -pub fn parse_virtio_serial(vm_config: &mut VmConfig, serial_config: &str) -> Result<()> { +pub fn parse_virtio_serial( + vm_config: &mut VmConfig, + serial_config: &str, +) -> Result { let mut cmd_parser = CmdParser::new("virtio-serial"); cmd_parser .push("") @@ -465,38 +486,38 @@ pub fn parse_virtio_serial(vm_config: &mut VmConfig, serial_config: &str) -> Res cmd_parser.parse(serial_config)?; pci_args_check(&cmd_parser)?; - if vm_config.virtio_serial.is_none() { - let id = cmd_parser.get_value::("id")?.unwrap_or_default(); - let multifunction = cmd_parser - .get_value::("multifunction")? - .map_or(false, |switch| switch.into()); - let max_ports = cmd_parser - .get_value::("max_ports")? - .unwrap_or(DEFAULT_SERIAL_PORTS_NUMBER); - let virtio_serial = if serial_config.contains("-pci") { - let pci_bdf = get_pci_bdf(serial_config)?; - VirtioSerialInfo { - id, - pci_bdf: Some(pci_bdf), - multifunction, - max_ports, - } - } else { - VirtioSerialInfo { - id, - pci_bdf: None, - multifunction, - // Micro_vm does not support multi-ports in virtio-serial-device. - max_ports: 1, - } - }; - virtio_serial.check()?; - vm_config.virtio_serial = Some(virtio_serial); - } else { + if vm_config.virtio_serial.is_some() { bail!("Only one virtio serial device is supported"); } - Ok(()) + let id = cmd_parser.get_value::("id")?.unwrap_or_default(); + let multifunction = cmd_parser + .get_value::("multifunction")? + .map_or(false, |switch| switch.into()); + let max_ports = cmd_parser + .get_value::("max_ports")? + .unwrap_or(DEFAULT_SERIAL_PORTS_NUMBER); + let virtio_serial = if serial_config.contains("-pci") { + let pci_bdf = get_pci_bdf(serial_config)?; + VirtioSerialInfo { + id, + pci_bdf: Some(pci_bdf), + multifunction, + max_ports, + } + } else { + VirtioSerialInfo { + id, + pci_bdf: None, + multifunction, + // Micro_vm does not support multi-ports in virtio-serial-device. + max_ports: 1, + } + }; + virtio_serial.check()?; + vm_config.virtio_serial = Some(virtio_serial.clone()); + + Ok(virtio_serial) } #[cfg(test)] @@ -511,9 +532,10 @@ mod tests { assert!(vm_config .add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait") .is_ok()); - let virt_console = parse_virtconsole( + let virt_console = parse_virtserialport( &mut vm_config, - "virtconsole,chardev=test_console,id=console1", + "virtconsole,chardev=test_console,id=console1,nr=1", + true, ); assert!(virt_console.is_ok()); let console_cfg = virt_console.unwrap(); @@ -541,9 +563,10 @@ mod tests { assert!(vm_config .add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait") .is_ok()); - let virt_console = parse_virtconsole( + let virt_console = parse_virtserialport( &mut vm_config, - "virtconsole,chardev=test_console1,id=console1", + "virtconsole,chardev=test_console1,id=console1,nr=1", + true, ); // test_console1 does not exist. assert!(virt_console.is_err()); @@ -559,9 +582,10 @@ mod tests { assert!(vm_config .add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait") .is_ok()); - let virt_console = parse_virtconsole( + let virt_console = parse_virtserialport( &mut vm_config, - "virtconsole,chardev=test_console,id=console1", + "virtconsole,chardev=test_console,id=console1,nr=1", + true, ); assert!(virt_console.is_ok()); let console_cfg = virt_console.unwrap(); diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml index 59e97288d..2280f9fb5 100644 --- a/sysbus/Cargo.toml +++ b/sysbus/Cargo.toml @@ -14,3 +14,4 @@ vmm-sys-util = "0.11.0" acpi = { path = "../acpi" } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } +util = {path = "../util"} \ No newline at end of file diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 0882282fe..018a56665 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -11,14 +11,17 @@ // See the Mulan PSL v2 for more details. pub mod error; + +pub use anyhow::{bail, Context, Result}; pub use error::SysBusError; + use std::fmt; use std::sync::{Arc, Mutex}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; -pub use anyhow::{bail, Context, Result}; use hypervisor::kvm::KVM_FDS; +use util::AsAny; use vmm_sys_util::eventfd::EventFd; // Now that the serial device use a hardcoded IRQ number (4), and the starting @@ -211,7 +214,7 @@ pub enum SysBusDevType { } /// Operations for sysbus devices. -pub trait SysBusDevOps: Send + AmlBuilder { +pub trait SysBusDevOps: Send + AmlBuilder + AsAny { /// Read function of device. /// /// # Arguments diff --git a/virtio/src/device/mod.rs b/virtio/src/device/mod.rs index 67f3eede8..26eee300a 100644 --- a/virtio/src/device/mod.rs +++ b/virtio/src/device/mod.rs @@ -12,9 +12,9 @@ pub mod balloon; pub mod block; -pub mod console; #[cfg(not(target_env = "musl"))] pub mod gpu; pub mod net; pub mod rng; pub mod scsi_cntlr; +pub mod serial; diff --git a/virtio/src/device/console.rs b/virtio/src/device/serial.rs similarity index 36% rename from virtio/src/device/console.rs rename to virtio/src/device/serial.rs index fd9e36374..98a50f566 100644 --- a/virtio/src/device/console.rs +++ b/virtio/src/device/serial.rs @@ -13,19 +13,22 @@ use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; -use std::sync::{Arc, Mutex}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, Weak}; use std::{cmp, usize}; +use anyhow::{anyhow, bail, Context, Result}; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; + use crate::{ - Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + Queue, VirtioDevice, VirtioError, VirtioInterrupt, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; -use anyhow::{anyhow, bail, Context, Result}; use devices::legacy::{Chardev, InputReceiver}; -use log::{debug, error}; use machine_manager::{ - config::{VirtioConsole, DEFAULT_VIRTQUEUE_SIZE}, + config::{VirtioSerialInfo, VirtioSerialPort, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, event_loop::{register_event_helper, unregister_event_helper}, }; @@ -36,262 +39,137 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::num_ops::read_u32; -use vmm_sys_util::epoll::EventSet; -use vmm_sys_util::eventfd::EventFd; - -/// Number of virtqueues. -const QUEUE_NUM_CONSOLE: usize = 2; -const BUFF_SIZE: usize = 4096; +/// Buffer size for chardev backend. +const BUF_SIZE: usize = 4096; -#[derive(Copy, Clone, Debug, Default)] #[repr(C)] +#[derive(Copy, Clone, Debug, Default)] struct VirtioConsoleConfig { + // The size of the console is supplied if VIRTIO_CONSOLE_F_SIZE feature is set. cols: u16, rows: u16, + // The maximum number of ports supported by the device can be fetched + // if VIRTIO_CONSOLE_F_MULTIPORT feature is set. max_nr_ports: u32, + // The driver can use emergency write to output a single character without + // initializing virtio queues if VIRTIO_CONSOLE_F_EMERG_WRITE is set. emerg_wr: u32, } impl ByteCode for VirtioConsoleConfig {} impl VirtioConsoleConfig { - /// Create configuration of virtio-console devices. - pub fn new() -> Self { + /// Create configuration of virtio-serial devices. + pub fn new(max_nr_ports: u32) -> Self { VirtioConsoleConfig { cols: 0_u16, rows: 0_u16, - max_nr_ports: 1_u32, + max_nr_ports, emerg_wr: 0_u32, } } } -struct ConsoleHandler { - input_queue: Arc>, - output_queue: Arc>, - output_queue_evt: Arc, - mem_space: Arc, - interrupt_cb: Arc, - driver_features: u64, - chardev: Arc>, -} - -impl InputReceiver for ConsoleHandler { - #[allow(clippy::useless_asref)] - fn input_handle(&mut self, buffer: &[u8]) { - let mut queue_lock = self.input_queue.lock().unwrap(); - - let count = buffer.len(); - if count == 0 { - return; - } - - while let Ok(elem) = queue_lock - .vring - .pop_avail(&self.mem_space, self.driver_features) - { - if elem.desc_num == 0 { - break; - } - let mut write_count = 0_usize; - for elem_iov in elem.in_iovec.iter() { - let allow_write_count = cmp::min(write_count + elem_iov.len as usize, count); - let source_slice = &buffer[write_count..allow_write_count]; - - let write_result = self.mem_space.write( - &mut source_slice.as_ref(), - elem_iov.addr, - source_slice.len() as u64, - ); - match write_result { - Ok(_) => { - write_count = allow_write_count; - } - Err(ref e) => { - error!( - "Failed to write slice for input console: addr {:X} len {} {:?}", - elem_iov.addr.0, - source_slice.len(), - e - ); - break; - } - } - } - - if let Err(ref e) = - queue_lock - .vring - .add_used(&self.mem_space, elem.index, write_count as u32) - { - error!( - "Failed to add used ring for input console, index: {} len: {} {:?}", - elem.index, write_count, e - ); - break; - } - - if write_count >= count { - break; - } - } - - if let Err(ref e) = - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) - { - error!( - "Failed to trigger interrupt for console, int-type {:?} {:?}", - VirtioInterruptType::Vring, - e - ) - } - } - - fn get_remain_space_size(&mut self) -> usize { - BUFF_SIZE - } -} - -impl ConsoleHandler { - fn output_handle(&mut self) { - self.trace_request("Console".to_string(), "to IO".to_string()); - let mut queue_lock = self.output_queue.lock().unwrap(); - let mut buffer = [0_u8; 4096]; - - while let Ok(elem) = queue_lock - .vring - .pop_avail(&self.mem_space, self.driver_features) - { - if elem.desc_num == 0 { - break; - } - let mut read_count = 0_usize; - for elem_iov in elem.out_iovec.iter() { - let allow_read_count = cmp::min(read_count + elem_iov.len as usize, buffer.len()); - let mut slice = &mut buffer[read_count..allow_read_count]; - - let read_result = self.mem_space.read( - &mut slice, - elem_iov.addr, - (allow_read_count - read_count) as u64, - ); - match read_result { - Ok(_) => { - read_count = allow_read_count; - } - Err(ref e) => { - error!( - "Failed to read buffer for output console: addr: {:X}, len: {} {:?}", - elem_iov.addr.0, - allow_read_count - read_count, - e - ); - break; - } - }; - } - if let Some(output) = &mut self.chardev.lock().unwrap().output { - let mut locked_output = output.lock().unwrap(); - if let Err(e) = locked_output.write_all(&buffer[..read_count]) { - error!("Failed to write to console output: {:?}", e); - } - if let Err(e) = locked_output.flush() { - error!("Failed to flush console output: {:?}", e); - } - } else { - debug!("Failed to get output fd"); - } - - if let Err(ref e) = queue_lock.vring.add_used(&self.mem_space, elem.index, 0) { - error!( - "Failed to add used ring for output console, index: {} len: {} {:?}", - elem.index, 0, e - ); - break; - } - } - } -} - -impl EventNotifierHelper for ConsoleHandler { - fn internal_notifiers(console_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let cloned_cls = console_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - cloned_cls.lock().unwrap().output_handle(); - None - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - console_handler.lock().unwrap().output_queue_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler], - )); - - notifiers - } -} - -/// Status of console device. +/// Status of serial device. #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct VirtioConsoleState { +pub struct VirtioSerialState { /// Bit mask of features supported by the backend. device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, - /// Virtio Console config space. + /// Virtio serial config space. config_space: VirtioConsoleConfig, } -/// Virtio console device structure. -pub struct Console { - /// Status of console device. - state: VirtioConsoleState, +/// Virtio serial device structure. +#[allow(dead_code)] +pub struct Serial { + /// Status of virtio serial device. + state: VirtioSerialState, /// EventFd for device deactivate. deactivate_evts: Vec, - /// Character device for redirection. - chardev: Arc>, + /// Max serial ports number. + pub max_nr_ports: u32, + /// Serial port vector for serialport. + pub ports: Arc>>>>, + /// Device is broken or not. + device_broken: Arc, } -impl Console { - /// Create a virtio-console device. +impl Serial { + /// Create a virtio-serial device. /// /// # Arguments /// - /// * `console_cfg` - Device configuration set by user. - pub fn new(console_cfg: VirtioConsole) -> Self { - Console { - state: VirtioConsoleState { + /// * `serial_cfg` - Device configuration set by user. + pub fn new(serial_cfg: VirtioSerialInfo) -> Self { + Serial { + state: VirtioSerialState { device_features: 0_u64, driver_features: 0_u64, - config_space: VirtioConsoleConfig::new(), + config_space: VirtioConsoleConfig::new(serial_cfg.max_ports), }, deactivate_evts: Vec::new(), - chardev: Arc::new(Mutex::new(Chardev::new(console_cfg.chardev))), + max_nr_ports: serial_cfg.max_ports, + ports: Arc::new(Mutex::new(Vec::new())), + device_broken: Arc::new(AtomicBool::new(false)), + } + } + + fn control_queues_activate( + &mut self, + mem_space: Arc, + interrupt_cb: Arc, + queues: &[Arc>], + queue_evts: Vec>, + device_broken: Arc, + ) -> Result<()> { + // queue[2]: control receiveq(host to guest). + // queue[3]: control transmitq(guest to host). + let handler = SerialControlHandler { + input_queue: queues[2].clone(), + output_queue: queues[3].clone(), + output_queue_evt: queue_evts[3].clone(), + mem_space, + interrupt_cb, + driver_features: self.state.driver_features, + device_broken, + ports: self.ports.clone(), + }; + + let handler_h = Arc::new(Mutex::new(handler)); + for port in self.ports.lock().unwrap().iter_mut() { + port.lock().unwrap().ctrl_handler = Some(Arc::downgrade(&handler_h.clone())); } + let notifiers = EventNotifierHelper::internal_notifiers(handler_h); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + + Ok(()) } } -impl VirtioDevice for Console { - /// Realize virtio console device. +pub fn find_port_by_nr( + ports: &Arc>>>>, + nr: u32, +) -> Option>> { + for port in ports.lock().unwrap().iter() { + if port.lock().unwrap().nr == nr { + return Some(port.clone()); + } + } + None +} + +impl VirtioDevice for Serial { + /// Realize virtio serial device. fn realize(&mut self) -> Result<()> { - self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE; - self.chardev - .lock() - .unwrap() - .realize() - .with_context(|| "Failed to realize chardev")?; - self.chardev.lock().unwrap().deactivated = true; - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(self.chardev.clone()), - None, - )?; + self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 + | 1_u64 << VIRTIO_CONSOLE_F_SIZE + | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT; + Ok(()) } @@ -302,7 +180,9 @@ impl VirtioDevice for Console { /// Get the count of virtio device queues. fn queue_num(&self) -> usize { - QUEUE_NUM_CONSOLE + // Each port has 2 queues(receiveq/transmitq). + // And there exist 2 control queues(control receiveq/control transmitq). + self.max_nr_ports as usize * 2 + 2 } /// Get the queue size of virtio device. @@ -342,7 +222,7 @@ impl VirtioDevice for Console { /// Write data to config from guest. fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { - bail!("Device config space for console is not supported") + bail!("Writing device config space for virtio serial is not supported.") } /// Activate the virtio device, this function is called by vcpu thread when frontend @@ -354,162 +234,230 @@ impl VirtioDevice for Console { queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { - let handler = ConsoleHandler { - input_queue: queues[0].clone(), - output_queue: queues[1].clone(), - // input_queue_evt never used - output_queue_evt: queue_evts[1].clone(), + if queues.len() != self.queue_num() { + return Err(anyhow!(VirtioError::IncorrectQueueNum( + self.queue_num(), + queues.len() + ))); + } + + for queue_id in 0..queues.len() / 2 { + // queues[i * 2] (note: i != 1): receiveq(host to guest). + // queues[i * 2 + 1] (note: i != 1): transmitq(guest to host). + let nr = match queue_id { + 0 => 0, + 1 => continue, + _ => queue_id - 1, + }; + let port = find_port_by_nr(&self.ports, nr as u32); + let handler = SerialPortHandler { + input_queue: queues[queue_id * 2].clone(), + output_queue: queues[queue_id * 2 + 1].clone(), + output_queue_evt: queue_evts[queue_id * 2 + 1].clone(), + mem_space: mem_space.clone(), + interrupt_cb: interrupt_cb.clone(), + driver_features: self.state.driver_features, + device_broken: self.device_broken.clone(), + port: port.clone(), + }; + let handler_h = Arc::new(Mutex::new(handler)); + let notifiers = EventNotifierHelper::internal_notifiers(handler_h.clone()); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + + if let Some(port_h) = port { + port_h.lock().unwrap().activate(&handler_h); + } + } + + self.control_queues_activate( mem_space, interrupt_cb, - driver_features: self.state.driver_features, - chardev: self.chardev.clone(), - }; - - let dev = Arc::new(Mutex::new(handler)); - let notifiers = EventNotifierHelper::internal_notifiers(dev.clone()); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + queues, + queue_evts, + self.device_broken.clone(), + )?; - self.chardev.lock().unwrap().set_input_callback(&dev); - self.chardev.lock().unwrap().deactivated = false; Ok(()) } fn deactivate(&mut self) -> Result<()> { - self.chardev.lock().unwrap().deactivated = true; - unregister_event_helper(None, &mut self.deactivate_evts) + for port in self.ports.lock().unwrap().iter_mut() { + port.lock().unwrap().deactivate(); + } + unregister_event_helper(None, &mut self.deactivate_evts)?; + + Ok(()) } } -impl StateTransfer for Console { +impl StateTransfer for Serial { fn get_state_vec(&self) -> migration::Result> { Ok(self.state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *VirtioConsoleState::from_bytes(state) - .with_context(|| migration::error::MigrationError::FromBytesError("CONSOLE"))?; + self.state = *VirtioSerialState::from_bytes(state) + .with_context(|| migration::error::MigrationError::FromBytesError("SERIAL"))?; Ok(()) } fn get_device_alias(&self) -> u64 { - MigrationManager::get_desc_alias(&VirtioConsoleState::descriptor().name).unwrap_or(!0) + MigrationManager::get_desc_alias(&VirtioSerialState::descriptor().name).unwrap_or(!0) } } -impl MigrationHook for Console {} +impl MigrationHook for Serial {} + +/// Virtio serial port structure. +#[allow(dead_code)] +#[derive(Clone)] +pub struct SerialPort { + name: Option, + /// Chardev vector for serialport. + pub chardev: Arc>, + /// Number id. + nr: u32, + /// Whether the port is a console port. + is_console: bool, + /// Whether the guest open the serial port. + guest_connected: bool, + /// Whether the host open the serial socket. + host_connected: bool, + /// The handler used to send control event to guest. + ctrl_handler: Option>>, +} -impl VirtioTrace for ConsoleHandler {} +impl SerialPort { + pub fn new(port_cfg: VirtioSerialPort) -> Self { + SerialPort { + name: Some(port_cfg.id), + chardev: Arc::new(Mutex::new(Chardev::new(port_cfg.chardev))), + nr: port_cfg.nr, + is_console: port_cfg.is_console, + guest_connected: false, + host_connected: false, + ctrl_handler: None, + } + } -#[cfg(test)] -mod tests { - pub use super::super::*; - pub use super::*; - use std::mem::size_of; + pub fn realize(&mut self) -> Result<()> { + self.chardev + .lock() + .unwrap() + .realize() + .with_context(|| "Failed to realize chardev")?; + self.chardev.lock().unwrap().deactivated = true; + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(self.chardev.clone()), + None, + )?; + Ok(()) + } - use machine_manager::config::{ChardevConfig, ChardevType}; + fn activate(&mut self, handler: &Arc>) { + self.chardev.lock().unwrap().set_input_callback(handler); + self.chardev.lock().unwrap().deactivated = false; + } - #[test] - fn test_set_driver_features() { - let chardev_cfg = ChardevConfig { - id: "chardev".to_string(), - backend: ChardevType::Stdio, - }; - let mut console = Console::new(VirtioConsole { - id: "console".to_string(), - chardev: chardev_cfg.clone(), + fn deactivate(&mut self) { + self.chardev.lock().unwrap().deactivated = true; + self.guest_connected = false; + } +} + +/// Handler for queues which are used for port. +#[allow(dead_code)] +struct SerialPortHandler { + input_queue: Arc>, + output_queue: Arc>, + output_queue_evt: Arc, + mem_space: Arc, + interrupt_cb: Arc, + driver_features: u64, + /// Virtio serial device is broken or not. + device_broken: Arc, + port: Option>>, +} + +/// Handler for queues which are used for control. +#[allow(dead_code)] +struct SerialControlHandler { + input_queue: Arc>, + output_queue: Arc>, + output_queue_evt: Arc, + mem_space: Arc, + interrupt_cb: Arc, + driver_features: u64, + /// Virtio serial device is broken or not. + device_broken: Arc, + ports: Arc>>>>, +} + +impl SerialPortHandler { + fn output_handle(&mut self) {} +} + +impl EventNotifierHelper for SerialPortHandler { + fn internal_notifiers(serial_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let cloned_cls = serial_handler.clone(); + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let mut h_lock = cloned_cls.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock.output_handle(); + None }); - let mut chardev = Chardev::new(chardev_cfg); - chardev.output = Some(Arc::new(Mutex::new(std::io::stdout()))); - console.chardev = Arc::new(Mutex::new(chardev)); - - //If the device feature is 0, all driver features are not supported. - console.state.device_features = 0; - let driver_feature: u32 = 0xFF; - let page = 0_u32; - console.set_driver_features(page, driver_feature); - assert_eq!(console.state.driver_features, 0_u64); - assert_eq!(console.get_driver_features(page) as u64, 0_u64); - - let driver_feature: u32 = 0xFF; - let page = 1_u32; - console.set_driver_features(page, driver_feature); - assert_eq!(console.state.driver_features, 0_u64); - assert_eq!(console.get_driver_features(page) as u64, 0_u64); - - //If both the device feature bit and the front-end driver feature bit are - //supported at the same time, this driver feature bit is supported. - console.state.device_features = - 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE; - let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; - let page = 0_u32; - console.set_driver_features(page, driver_feature); - assert_eq!( - console.state.driver_features, - (1_u64 << VIRTIO_CONSOLE_F_SIZE) - ); - assert_eq!( - console.get_driver_features(page) as u64, - (1_u64 << VIRTIO_CONSOLE_F_SIZE) - ); - console.state.driver_features = 0; - - console.state.device_features = 1_u64 << VIRTIO_F_VERSION_1; - let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; - let page = 0_u32; - console.set_driver_features(page, driver_feature); - assert_eq!(console.state.driver_features, 0); - console.state.driver_features = 0; - - console.state.device_features = - 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE; - let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; - let page = 0_u32; - console.set_driver_features(page, driver_feature); - assert_eq!( - console.state.driver_features, - (1_u64 << VIRTIO_CONSOLE_F_SIZE) - ); - - let driver_feature: u32 = ((1_u64 << VIRTIO_F_VERSION_1) >> 32) as u32; - let page = 1_u32; - console.set_driver_features(page, driver_feature); - assert_eq!( - console.state.driver_features, - (1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE) - ); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + serial_handler.lock().unwrap().output_queue_evt.as_raw_fd(), + None, + EventSet::IN, + vec![handler], + )); + + notifiers } +} - #[test] - fn test_read_config() { - let chardev_cfg = ChardevConfig { - id: "chardev".to_string(), - backend: ChardevType::Stdio, - }; - let mut console = Console::new(VirtioConsole { - id: "console".to_string(), - chardev: chardev_cfg.clone(), +impl InputReceiver for SerialPortHandler { + fn input_handle(&mut self, _buffer: &[u8]) {} + + fn get_remain_space_size(&mut self) -> usize { + BUF_SIZE + } +} + +impl SerialControlHandler { + fn output_control(&mut self) {} +} + +impl EventNotifierHelper for SerialControlHandler { + fn internal_notifiers(serial_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let cloned_cls = serial_handler.clone(); + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let mut h_lock = cloned_cls.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock.output_control(); + None }); - let mut chardev = Chardev::new(chardev_cfg); - chardev.output = Some(Arc::new(Mutex::new(std::io::stdout()))); - console.chardev = Arc::new(Mutex::new(chardev)); - - //The offset of configuration that needs to be read exceeds the maximum - let offset = size_of::() as u64; - let mut read_data: Vec = vec![0; 8]; - assert_eq!(console.read_config(offset, &mut read_data).is_ok(), false); - - //Check the configuration that needs to be read - let offset = 0_u64; - let mut read_data: Vec = vec![0; 12]; - let expect_data: Vec = vec![0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]; - assert_eq!(console.read_config(offset, &mut read_data).is_ok(), true); - assert_eq!(read_data, expect_data); - - let offset = 4_u64; - let mut read_data: Vec = vec![0; 1]; - let expect_data: Vec = vec![1]; - assert_eq!(console.read_config(offset, &mut read_data).is_ok(), true); - assert_eq!(read_data, expect_data); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + serial_handler.lock().unwrap().output_queue_evt.as_raw_fd(), + None, + EventSet::IN, + vec![handler], + )); + + notifiers } } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 1e733fa33..b43aee969 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -47,12 +47,12 @@ use util::AsAny; pub use device::balloon::*; pub use device::block::{Block, BlockState}; -pub use device::console::{Console, VirtioConsoleState}; #[cfg(not(target_env = "musl"))] pub use device::gpu::*; pub use device::net::*; pub use device::rng::{Rng, RngState}; pub use device::scsi_cntlr as ScsiCntlr; +pub use device::serial::{find_port_by_nr, Serial, SerialPort, VirtioSerialState}; pub use error::VirtioError; pub use error::*; pub use queue::*; @@ -135,6 +135,12 @@ pub const VIRTIO_NET_F_MQ: u32 = 22; pub const VIRTIO_NET_F_CTRL_MAC_ADDR: u32 = 23; /// Configuration cols and rows are valid. pub const VIRTIO_CONSOLE_F_SIZE: u64 = 0; +/// Device has support for multiple ports. +/// max_nr_ports is valid and control virtqueues will be used. +pub const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 1; +/// Device has support for emergency write. +/// Configuration field emerg_wr is valid. +pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 2; /// Maximum size of any single segment is in size_max. pub const VIRTIO_BLK_F_SIZE_MAX: u32 = 1; /// Maximum number of segments in a request is in seg_max. -- Gitee From 168d0407966c7096d4af918e7a8858da3b19b63e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 16:39:17 +0800 Subject: [PATCH 1061/2187] Chardev: add ChardevNotifyDevice trait to notify device Add ChardevNotifyDevice trait for chardev to notify device. Signed-off-by: liuxiangdong Signed-off-by: wangyan --- devices/src/legacy/chardev.rs | 24 ++++++++++++++++++++++++ devices/src/legacy/mod.rs | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index ec3cc6f6d..7cd35b375 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -40,6 +40,16 @@ pub trait InputReceiver: Send { fn get_remain_space_size(&mut self) -> usize; } +/// Provide the trait that notifies device the socket is opened or closed. +pub trait ChardevNotifyDevice: Send { + fn chardev_notify(&mut self, status: ChardevStatus); +} + +pub enum ChardevStatus { + Close, + Open, +} + type ReceFn = Option>; /// Character device structure. @@ -62,6 +72,8 @@ pub struct Chardev { receive: ReceFn, /// Return the remain space size of receiver buffer. get_remain_space_size: Option usize + Send + Sync>>, + /// Used to notify device the socket is opened or closed. + dev: Option>>, } impl Chardev { @@ -76,6 +88,7 @@ impl Chardev { deactivated: false, receive: None, get_remain_space_size: None, + dev: None, } } @@ -147,6 +160,10 @@ impl Chardev { cloned_dev.lock().unwrap().get_remain_space_size() })); } + + pub fn set_device(&mut self, dev: Arc>) { + self.dev = Some(dev.clone()); + } } fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { @@ -237,6 +254,10 @@ fn get_notifier_handler( locked_chardev.input = Some(stream_arc.clone()); locked_chardev.output = Some(stream_arc); + if let Some(dev) = &locked_chardev.dev { + dev.lock().unwrap().chardev_notify(ChardevStatus::Open); + } + let cloned_chardev = chardev.clone(); let inner_handler: Rc = Rc::new(move |event, _| { let mut locked_chardev = cloned_chardev.lock().unwrap(); @@ -258,6 +279,9 @@ fn get_notifier_handler( None } else if event & EventSet::HANG_UP == EventSet::HANG_UP { // Always allow disconnect even if has deactivated. + if let Some(dev) = &locked_chardev.dev { + dev.lock().unwrap().chardev_notify(ChardevStatus::Close); + } locked_chardev.input = None; locked_chardev.output = None; locked_chardev.stream_fd = None; diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index e6b0ae7c6..0b2e541cb 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -41,7 +41,7 @@ mod serial; #[cfg(target_arch = "x86_64")] pub use self::rtc::{RTC, RTC_PORT_INDEX}; pub use anyhow::Result; -pub use chardev::{Chardev, InputReceiver}; +pub use chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; pub use error::LegacyError; #[cfg(target_arch = "x86_64")] pub use fwcfg::FwCfgIO; -- Gitee From 7b50b15a18e336a4f386ae570b98cea98253a0e6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 19:04:41 +0800 Subject: [PATCH 1062/2187] virtio-serial: implement virtio-serial dataplane Implement virtio-serial port input/output handle function. Signed-off-by: liuxiangdong Signed-off-by: wangyan --- virtio/src/device/serial.rs | 182 +++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 5 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 98a50f566..30b00708b 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -18,11 +18,13 @@ use std::sync::{Arc, Mutex, Weak}; use std::{cmp, usize}; use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, error}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use crate::{ - Queue, VirtioDevice, VirtioError, VirtioInterrupt, VIRTIO_CONSOLE_F_MULTIPORT, + iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, VirtioDevice, VirtioError, + VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; @@ -40,7 +42,7 @@ use util::loop_context::{ }; use util::num_ops::read_u32; -/// Buffer size for chardev backend. +// Buffer size for chardev backend. const BUF_SIZE: usize = 4096; #[repr(C)] @@ -395,9 +397,170 @@ struct SerialControlHandler { } impl SerialPortHandler { - fn output_handle(&mut self) {} + fn output_handle(&mut self) { + self.trace_request("Serial".to_string(), "to IO".to_string()); + + self.output_handle_internal().unwrap_or_else(|e| { + error!("Port handle output error: {:?}", e); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + }); + } + + fn output_handle_internal(&mut self) -> Result<()> { + let mut queue_lock = self.output_queue.lock().unwrap(); + + loop { + let elem = queue_lock + .vring + .pop_avail(&self.mem_space, self.driver_features)?; + if elem.desc_num == 0 { + break; + } + debug!("elem desc_unm: {}", elem.desc_num); + + // Discard requests when there is no port using this queue or this port's socket is not connected. + // Popping elements without processing means discarding the request. + if self.port.is_some() && self.port.as_ref().unwrap().lock().unwrap().host_connected { + let mut iovec = elem.out_iovec; + let mut iovec_size = Element::iovec_size(&iovec); + while iovec_size > 0 { + let mut buffer = [0_u8; BUF_SIZE]; + let size = iov_to_buf(&self.mem_space, &iovec, &mut buffer)?; + + self.write_chardev_msg(&buffer, size); + + iovec = iov_discard_front(&mut iovec, size as u64) + .unwrap_or_default() + .to_vec(); + // Safety: iovec follows the iov_discard_front operation and + // iovec_size always equals Element::iovec_size(&iovec). + iovec_size -= size as u64; + debug!("iovec size {}, write size {}", iovec_size, size); + } + } + + queue_lock + .vring + .add_used(&self.mem_space, elem.index, 0) + .with_context(|| { + format!( + "Failed to add used ring for virtio serial port output, index: {} len: {}", + elem.index, 0, + ) + })?; + } + + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { + VirtioError::InterruptTrigger( + "serial port output queue", + VirtioInterruptType::Vring, + ) + })?; + } + + Ok(()) + } + + fn write_chardev_msg(&self, buffer: &[u8], write_len: usize) { + let chardev = self.port.as_ref().unwrap().lock().unwrap(); + if let Some(output) = &mut chardev.chardev.lock().unwrap().output { + let mut locked_output = output.lock().unwrap(); + // To do: + // If the buffer is not fully written to chardev, the incomplete part will be discarded. + // This may occur when chardev is abnormal. Consider optimizing this logic in the future. + if let Err(e) = locked_output.write_all(&buffer[..write_len]) { + error!("Failed to write msg to chardev: {:?}", e); + } + if let Err(e) = locked_output.flush() { + error!("Failed to flush msg to chardev: {:?}", e); + } + } else { + error!("Failed to get output fd"); + }; + } + + fn input_handle_internal(&mut self, buffer: &[u8]) -> Result<()> { + let mut queue_lock = self.input_queue.lock().unwrap(); + + let count = buffer.len(); + if count == 0 + || self.port.is_some() && !self.port.as_ref().unwrap().lock().unwrap().guest_connected + { + return Ok(()); + } + + loop { + let elem = queue_lock + .vring + .pop_avail(&self.mem_space, self.driver_features)?; + if elem.desc_num == 0 { + break; + } + + let mut written_count = 0_usize; + for elem_iov in elem.in_iovec.iter() { + let allow_write_count = cmp::min(written_count + elem_iov.len as usize, count); + let mut source_slice = &buffer[written_count..allow_write_count]; + let len = source_slice.len(); + + self.mem_space + .write(&mut source_slice, elem_iov.addr, len as u64) + .with_context(|| { + format!( + "Failed to write slice for virtio serial port input: addr {:X} len {}", + elem_iov.addr.0, len + ) + })?; + + written_count = allow_write_count; + if written_count >= count { + break; + } + } + + queue_lock + .vring + .add_used(&self.mem_space, elem.index, written_count as u32) + .with_context(|| { + format!( + "Failed to add used ring for virtio serial port input: index {} len {}", + elem.index, written_count + ) + })?; + + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { + VirtioError::InterruptTrigger( + "serial port input queue", + VirtioInterruptType::Vring, + ) + })?; + } + + if written_count >= count { + break; + } + } + + Ok(()) + } } +impl VirtioTrace for SerialPortHandler {} + impl EventNotifierHelper for SerialPortHandler { fn internal_notifiers(serial_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); @@ -425,7 +588,16 @@ impl EventNotifierHelper for SerialPortHandler { } impl InputReceiver for SerialPortHandler { - fn input_handle(&mut self, _buffer: &[u8]) {} + fn input_handle(&mut self, buffer: &[u8]) { + self.input_handle_internal(buffer).unwrap_or_else(|e| { + error!("Port handle input error: {:?}", e); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + }); + } fn get_remain_space_size(&mut self) -> usize { BUF_SIZE -- Gitee From 8d06acbe1e7c2fd29c990d764d6a248e37e7c57f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 19:55:20 +0800 Subject: [PATCH 1063/2187] virtio-serial: implement virtio-serial control plane Implement virtio-serial port input/output control function. Signed-off-by: liuxiangdong Signed-off-by: wangyan --- machine/src/lib.rs | 3 + virtio/src/device/serial.rs | 303 ++++++++++++++++++++++++++++++++++-- 2 files changed, 294 insertions(+), 12 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 43edace06..583b0888f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -479,6 +479,9 @@ pub trait MachineOps { let mut serial_port = SerialPort::new(serialport_cfg); let port = Arc::new(Mutex::new(serial_port.clone())); serial_port.realize()?; + if !is_console { + serial_port.chardev.lock().unwrap().set_device(port.clone()); + } serial.ports.lock().unwrap().push(port); Ok(()) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 30b00708b..3664fd170 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::io::Write; +use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -18,17 +19,18 @@ use std::sync::{Arc, Mutex, Weak}; use std::{cmp, usize}; use anyhow::{anyhow, bail, Context, Result}; -use log::{debug, error}; +use byteorder::{ByteOrder, LittleEndian}; +use log::{debug, error, info, warn}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use crate::{ - iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, VirtioDevice, VirtioError, - VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_MULTIPORT, - VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, + VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; -use devices::legacy::{Chardev, InputReceiver}; +use devices::legacy::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; use machine_manager::{ config::{VirtioSerialInfo, VirtioSerialPort, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, @@ -36,6 +38,7 @@ use machine_manager::{ }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; +use util::aio::iov_from_buf_direct; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -45,6 +48,44 @@ use util::num_ops::read_u32; // Buffer size for chardev backend. const BUF_SIZE: usize = 4096; +// The values for event. +// Sent by the driver at initialization to indicate that it is ready to receive control message. +const VIRTIO_CONSOLE_DEVICE_READY: u16 = 0; +// Sent by the device, to create a new port. +const VIRTIO_CONSOLE_PORT_ADD: u16 = 1; +// Sent by the device, to remove an existing port. +#[allow(unused)] +const VIRTIO_CONSOLE_PORT_REMOVE: u16 = 2; +// Sent by the driver in response to the device's VIRTIO_CONSOLE_PORT_ADD message. +// To indicate that the port is ready to be used. +const VIRTIO_CONSOLE_PORT_READY: u16 = 3; +// Sent by the device to nominate a port as a console port. +// There may be more than one console port. +const VIRTIO_CONSOLE_CONSOLE_PORT: u16 = 4; +// Sent by the device to indicate a console size change. +#[allow(unused)] +const VIRTIO_CONSOLE_RESIZE: u16 = 5; +// This message is sent by both the device and the driver. This allows for ports to be used +// directly by guest and host processes to communicate in an application-defined manner. +const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6; +// Sent by the device to give a tag to the port. +const VIRTIO_CONSOLE_PORT_NAME: u16 = 7; + +/// If the driver negotiated the VIRTIO_CONSOLE_F_MULTIPORT, the two control queues are used. +/// The layout of the control message is VirtioConsoleControl. +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioConsoleControl { + // Port number. + id: u32, + // The kind of control event. + event: u16, + // Extra information for event. + value: u16, +} + +impl ByteCode for VirtioConsoleControl {} + #[repr(C)] #[derive(Copy, Clone, Debug, Default)] struct VirtioConsoleConfig { @@ -87,7 +128,6 @@ pub struct VirtioSerialState { } /// Virtio serial device structure. -#[allow(dead_code)] pub struct Serial { /// Status of virtio serial device. state: VirtioSerialState, @@ -312,7 +352,6 @@ impl StateTransfer for Serial { impl MigrationHook for Serial {} /// Virtio serial port structure. -#[allow(dead_code)] #[derive(Clone)] pub struct SerialPort { name: Option, @@ -321,7 +360,7 @@ pub struct SerialPort { /// Number id. nr: u32, /// Whether the port is a console port. - is_console: bool, + pub is_console: bool, /// Whether the guest open the serial port. guest_connected: bool, /// Whether the host open the serial socket. @@ -338,7 +377,8 @@ impl SerialPort { nr: port_cfg.nr, is_console: port_cfg.is_console, guest_connected: false, - host_connected: false, + // Console is default host connected. + host_connected: port_cfg.is_console, ctrl_handler: None, } } @@ -369,7 +409,6 @@ impl SerialPort { } /// Handler for queues which are used for port. -#[allow(dead_code)] struct SerialPortHandler { input_queue: Arc>, output_queue: Arc>, @@ -383,7 +422,6 @@ struct SerialPortHandler { } /// Handler for queues which are used for control. -#[allow(dead_code)] struct SerialControlHandler { input_queue: Arc>, output_queue: Arc>, @@ -605,7 +643,227 @@ impl InputReceiver for SerialPortHandler { } impl SerialControlHandler { - fn output_control(&mut self) {} + fn output_control(&mut self) { + self.output_control_internal().unwrap_or_else(|e| { + error!("handle output control error: {:?}", e); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + }); + } + + fn output_control_internal(&mut self) -> Result<()> { + let output_queue = self.output_queue.clone(); + let mut queue_lock = output_queue.lock().unwrap(); + + loop { + let elem = queue_lock + .vring + .pop_avail(&self.mem_space, self.driver_features)?; + if elem.desc_num == 0 { + break; + } + + let mut req = VirtioConsoleControl::default(); + iov_to_buf(&self.mem_space, &elem.out_iovec, req.as_mut_bytes()).and_then(|size| { + if size < size_of::() { + bail!( + "Invalid length for request: get {}, expected {}", + size, + size_of::(), + ); + } + Ok(()) + })?; + req.id = LittleEndian::read_u32(req.id.as_bytes()); + req.event = LittleEndian::read_u16(req.event.as_bytes()); + req.value = LittleEndian::read_u16(req.value.as_bytes()); + + info!( + "Serial port {} handle control message: event({}), value({})", + req.id, req.event, req.value + ); + self.handle_control_message(&mut req); + + queue_lock + .vring + .add_used(&self.mem_space, elem.index, 0) + .with_context(|| { + format!( + "Failed to add used ring for control port, index: {} len: {}.", + elem.index, 0 + ) + })?; + } + + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { + VirtioError::InterruptTrigger( + "serial input control queue", + VirtioInterruptType::Vring, + ) + })?; + } + + Ok(()) + } + + fn handle_control_message(&mut self, ctrl: &mut VirtioConsoleControl) { + if ctrl.event == VIRTIO_CONSOLE_DEVICE_READY { + if ctrl.value == 0 { + error!("Guest is not ready to receive contorl message."); + return; + } + + let cloned_ports = self.ports.clone(); + let mut locked_ports = cloned_ports.lock().unwrap(); + for port in locked_ports.iter_mut() { + self.send_control_event(port.lock().unwrap().nr, VIRTIO_CONSOLE_PORT_ADD, 1); + } + return; + } + + let port = if let Some(port) = find_port_by_nr(&self.ports, ctrl.id) { + port + } else { + error!("Invalid port id {}", ctrl.id); + return; + }; + + match ctrl.event { + VIRTIO_CONSOLE_PORT_READY => { + if ctrl.value == 0 { + error!("Driver failed to add port {}", ctrl.id); + return; + } + + let locked_port = port.lock().unwrap(); + if locked_port.is_console { + self.send_control_event(locked_port.nr, VIRTIO_CONSOLE_CONSOLE_PORT, 1); + } + + if let Some(name) = &locked_port.name { + let mut extra_data: Vec = Vec::new(); + extra_data.extend(name.as_bytes()); + extra_data.push(0); + self.send_input_control_msg( + locked_port.nr, + VIRTIO_CONSOLE_PORT_NAME, + 1, + &extra_data, + ) + .unwrap_or_else(|e| { + error!("Send input control message error: {:?}", e); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + }); + } + + if locked_port.host_connected { + self.send_control_event(locked_port.nr, VIRTIO_CONSOLE_PORT_OPEN, 1); + } + } + VIRTIO_CONSOLE_PORT_OPEN => { + port.lock().unwrap().guest_connected = ctrl.value != 0; + } + _ => (), + } + } + + fn send_control_event(&mut self, id: u32, event: u16, value: u16) { + info!( + "Serial port {} send control message: event({}), value({})", + id, event, value + ); + self.send_input_control_msg(id, event, value, &[]) + .unwrap_or_else(|e| { + error!("send input control message error: {:?}", e); + report_virtio_error( + self.interrupt_cb.clone(), + self.driver_features, + &self.device_broken, + ); + }); + } + + fn send_input_control_msg( + &mut self, + id: u32, + event: u16, + value: u16, + extra: &[u8], + ) -> Result<()> { + let mut queue_lock = self.input_queue.lock().unwrap(); + let elem = queue_lock + .vring + .pop_avail(&self.mem_space, self.driver_features)?; + if elem.desc_num == 0 { + warn!("empty input queue buffer!"); + return Ok(()); + } + + let (in_size, ctrl_vec) = gpa_hva_iovec_map(&elem.in_iovec, &self.mem_space)?; + let len = size_of::() + extra.len(); + if in_size < len as u64 { + bail!( + "Invalid length for input control msg: get {}, expected {}", + in_size, + len, + ); + } + + let ctrl_msg = VirtioConsoleControl { id, event, value }; + let mut msg_data: Vec = Vec::new(); + msg_data.extend(ctrl_msg.as_bytes()); + if !extra.is_empty() { + msg_data.extend(extra); + } + + iov_from_buf_direct(&ctrl_vec, &msg_data).and_then(|size| { + if size != len { + bail!( + "Expected send msg length is {}, actual send length {}.", + len, + size + ); + } + Ok(()) + })?; + + queue_lock + .vring + .add_used(&self.mem_space, elem.index, len as u32) + .with_context(|| { + format!( + "Failed to add used ring(serial input control queue), index {}, len {}", + elem.index, len, + ) + })?; + + if queue_lock + .vring + .should_notify(&self.mem_space, self.driver_features) + { + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| { + VirtioError::InterruptTrigger( + "serial input control queue", + VirtioInterruptType::Vring, + ) + })?; + } + + Ok(()) + } } impl EventNotifierHelper for SerialControlHandler { @@ -633,3 +891,24 @@ impl EventNotifierHelper for SerialControlHandler { notifiers } } + +impl ChardevNotifyDevice for SerialPort { + fn chardev_notify(&mut self, status: ChardevStatus) { + match (&status, self.host_connected) { + (ChardevStatus::Close, _) => self.host_connected = false, + (ChardevStatus::Open, false) => self.host_connected = true, + (ChardevStatus::Open, true) => return, + } + + if let Some(handler) = &self.ctrl_handler { + let handler = handler.upgrade().unwrap(); + handler.lock().unwrap().send_control_event( + self.nr, + VIRTIO_CONSOLE_PORT_OPEN, + status as u16, + ); + } else { + error!("Control handler for port {} is None", self.nr); + } + } +} -- Gitee From 8c942d700f8b9a22c2ae64e2b7bfaca9f3a3a7c0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 8 May 2023 20:20:56 +0800 Subject: [PATCH 1064/2187] docs: add virtio-serial/virtserialport docs Add virtio-serial/virtserialport docs. Signed-off-by: liuxiangdong Signed-off-by: wangyan --- docs/config_guidebook.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 3a64f4e6b..5fca3810c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -508,31 +508,44 @@ $ ovs-vsctl set Interface port2 options:n_rxq=num,n_txq=num ### 2.4 Virtio-console -Virtio console is a general-purpose serial device for data transfer between the guest and host. -Character devices at /dev/hvc0 to /dev/hvc7 in guest will be created once setting it. -To set the virtio console, chardev for redirection will be required. See [section 2.12 Chardev](#212-chardev) for details. +Virtio console device is a simple device for data transfer between the guest and host. A console device may have +one or more ports. These ports could be generic ports or console ports. Character devices /dev/vport\*p\* in linux +guest will be created once setting a port (Whether it is a console port or not). Character devices at /dev/hvc0 to +/dev/hvc7 in linux guest will be created once setting console port. To set the virtio console, chardev for +redirection will be required. See [section 2.12 Chardev](#212-chardev) for details. -Two properties can be set for virtconsole. +Three properties can be set for virtconsole(console port) and virtserialport(generic port). * id: unique device-id. -* chardev: char device of virtio console device. +* chardev: char device of this console/generic port. +* nr: unique port number for this port. For virtio-serial-pci, Four more properties are required. * bus: bus number of virtio console. -* addr: including slot number and function number. The first number represents slot number -of device and the second one represents function number of it. +* addr: including slot number and function number. The first number represents slot number of device and the second one represents function number of it. * multifunction: whether to open multi-function for device. (optional) If not set, default is false. * max_ports: max number of ports we can have for a virtio-serial device. Configuration range is [1, 31]. (optional) If not set, default is 31. +For virtio-serial-device, Two more properties are required. +* bus: bus number of virtio console. +* addr: including slot number and function number. The first number represents slot number of device and the second one represents function number of it. + ```shell -# virtio mmio device +# virtio mmio device using console port -device virtio-serial-device[,id=] -chardev socket,path=,id=,server,nowait --device virtconsole,id=,chardev= +-device virtconsole,id=,chardev=,nr=0 + +# virtio mmio device using generic port +-device virtio-serial-device[,id=] +-chardev socket,path=,id=,server,nowait +-device virtserialport,id=,chardev=,nr=0 # virtio pci device -device virtio-serial-pci,id=,bus=,addr=<0x3>[,multifunction={on|off},max_ports=] --chardev socket,path=,id=,server,nowait --device virtconsole,id=,chardev= +-chardev socket,path=,id=,server,nowait +-device virtconsole,id=,chardev=,nr=0 +-chardev socket,path=,id=,server,nowait +-device virtserialport,id=,chardev=,nr=1 ``` NB: Currently, only one virtio console device is supported. Only one port is supported in microvm. -- Gitee From fbfc902d2ab964cf9293919034620b7acc3c8447 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 10 May 2023 09:45:05 +0800 Subject: [PATCH 1065/2187] virtio-serial: add ut for serial Add ut for serial. Signed-off-by: liuxiangdong --- virtio/src/device/serial.rs | 102 ++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 3664fd170..d9ba4d74b 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -912,3 +912,105 @@ impl ChardevNotifyDevice for SerialPort { } } } + +#[cfg(test)] +mod tests { + pub use super::super::*; + pub use super::*; + + use machine_manager::config::PciBdf; + + #[test] + fn test_set_driver_features() { + let mut serial = Serial::new(VirtioSerialInfo { + id: "serial".to_string(), + pci_bdf: Some(PciBdf { + bus: "pcie.0".to_string(), + addr: (0, 0), + }), + multifunction: false, + max_ports: 31, + }); + + // If the device feature is 0, all driver features are not supported. + serial.state.device_features = 0; + let driver_feature: u32 = 0xFF; + let page = 0_u32; + serial.set_driver_features(page, driver_feature); + assert_eq!(serial.state.driver_features, 0_u64); + assert_eq!(serial.get_driver_features(page) as u64, 0_u64); + + let driver_feature: u32 = 0xFF; + let page = 1_u32; + serial.set_driver_features(page, driver_feature); + assert_eq!(serial.state.driver_features, 0_u64); + assert_eq!(serial.get_driver_features(page) as u64, 0_u64); + + // If both the device feature bit and the front-end driver feature bit are + // supported at the same time, this driver feature bit is supported. + serial.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE; + let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; + let page = 0_u32; + serial.set_driver_features(page, driver_feature); + assert_eq!( + serial.state.driver_features, + (1_u64 << VIRTIO_CONSOLE_F_SIZE) + ); + assert_eq!( + serial.get_driver_features(page) as u64, + (1_u64 << VIRTIO_CONSOLE_F_SIZE) + ); + serial.state.driver_features = 0; + + serial.state.device_features = 1_u64 << VIRTIO_F_VERSION_1; + let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; + let page = 0_u32; + serial.set_driver_features(page, driver_feature); + assert_eq!(serial.state.driver_features, 0); + serial.state.driver_features = 0; + + serial.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 + | 1_u64 << VIRTIO_CONSOLE_F_SIZE + | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT; + let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_MULTIPORT) as u32; + let page = 0_u32; + serial.set_driver_features(page, driver_feature); + assert_eq!( + serial.state.driver_features, + (1_u64 << VIRTIO_CONSOLE_F_MULTIPORT) + ); + let driver_feature: u32 = ((1_u64 << VIRTIO_F_VERSION_1) >> 32) as u32; + let page = 1_u32; + serial.set_driver_features(page, driver_feature); + assert_eq!( + serial.state.driver_features, + (1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT) + ); + } + + #[test] + fn test_read_config() { + let max_ports: u8 = 31; + let serial = Serial::new(VirtioSerialInfo { + id: "serial".to_string(), + pci_bdf: Some(PciBdf { + bus: "pcie.0".to_string(), + addr: (0, 0), + }), + multifunction: false, + max_ports: max_ports as u32, + }); + + // The offset of configuration that needs to be read exceeds the maximum. + let offset = size_of::() as u64; + let mut read_data: Vec = vec![0; 8]; + assert_eq!(serial.read_config(offset, &mut read_data).is_ok(), false); + + // Check the configuration that needs to be read. + let offset = 0_u64; + let mut read_data: Vec = vec![0; 12]; + let expect_data: Vec = vec![0, 0, 0, 0, max_ports, 0, 0, 0, 0, 0, 0, 0]; + assert_eq!(serial.read_config(offset, &mut read_data).is_ok(), true); + assert_eq!(read_data, expect_data); + } +} -- Gitee From 9ddb297b6634219bfc4d35fe403ac79429f37bab Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 10 May 2023 11:30:34 +0800 Subject: [PATCH 1066/2187] virtio-serial: adapt console mst Adapt console mst. Note: This is a temporary plan. Serial mst will be restructured later on! We will add virtserialport and modify the Port Enabling Process in the furture. Signed-off-by: liuxiangdong Signed-off-by: wangyan --- .../mod_test/src/libdriver/virtio_console.rs | 2 +- tests/mod_test/tests/console_test.rs | 124 +++++++++++++++--- 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio_console.rs b/tests/mod_test/src/libdriver/virtio_console.rs index 2336b6bb7..0f115bd6a 100644 --- a/tests/mod_test/src/libdriver/virtio_console.rs +++ b/tests/mod_test/src/libdriver/virtio_console.rs @@ -74,7 +74,7 @@ pub fn create_console( }; extra_args.append(&mut chardev_args.split(' ').collect()); - let console_args = String::from("-device virtconsole,chardev=charconsole0,id=console0"); + let console_args = String::from("-device virtconsole,chardev=charconsole0,id=console0,nr=0"); extra_args.append(&mut console_args.split(' ').collect()); let test_state = Rc::new(RefCell::new(test_init(extra_args))); diff --git a/tests/mod_test/tests/console_test.rs b/tests/mod_test/tests/console_test.rs index d26135f34..b204ba56d 100644 --- a/tests/mod_test/tests/console_test.rs +++ b/tests/mod_test/tests/console_test.rs @@ -14,12 +14,15 @@ use serde_json::json; use std::cell::RefCell; use std::fs::{self, File}; use std::io::prelude::*; +use std::mem::size_of; use std::net::Shutdown; use std::os::unix::net::UnixStream; use std::path::Path; use std::rc::Rc; use std::time; +use util::byte_code::ByteCode; + use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; use mod_test::libdriver::virtio_console::{create_console, ChardevType}; @@ -35,15 +38,40 @@ const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 1; const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 2; const BUFFER_LEN: usize = 96; +// Default 31 serial ports. +const DEFAULT_SERIAL_VIRTQUEUES: usize = 64; + +const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioConsoleControl { + // Port number. + id: u32, + // The kind of control event. + event: u16, + // Extra information for event. + value: u16, +} + +impl VirtioConsoleControl { + fn new(id: u32, event: u16, value: u16) -> VirtioConsoleControl { + VirtioConsoleControl { id, event, value } + } +} + +impl ByteCode for VirtioConsoleControl {} + fn console_setup( console: Rc>, test_state: Rc>, alloc: Rc>, ) -> Vec>> { let features = console.borrow().get_device_features(); - let vqs = console - .borrow_mut() - .init_device(test_state, alloc, features, 2); + let vqs = + console + .borrow_mut() + .init_device(test_state, alloc, features, DEFAULT_SERIAL_VIRTQUEUES); vqs } @@ -87,6 +115,32 @@ fn get_pty_path(test_state: Rc>) -> String { } } +// Send control message by output control queue. +fn out_control_event( + test_state: Rc>, + alloc: Rc>, + console: Rc>, + ctrl_msg: VirtioConsoleControl, + vqs: &Vec>>, +) { + let out_control_queue = vqs[3].clone(); + + let addr = alloc + .borrow_mut() + .alloc(size_of::() as u64); + test_state.borrow().memwrite(addr, ctrl_msg.as_bytes()); + let _ = out_control_queue.borrow_mut().add( + test_state.clone(), + addr, + size_of::() as u32, + false, + ); + + console + .borrow() + .kick_virtqueue(test_state.clone(), out_control_queue.clone()); +} + fn verify_pty_io( test_state: Rc>, alloc: Rc>, @@ -99,6 +153,23 @@ fn verify_pty_io( let pty_path = get_pty_path(test_state.clone()); assert_ne!(pty_path, String::from("")); + // Connect Guest. + let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); + out_control_event( + test_state.clone(), + alloc.clone(), + console.clone(), + open_msg, + &vqs, + ); + + // Connect Host. + let mut input: Option = None; + match File::open(&pty_path) { + Ok(file) => input = Some(file), + Err(e) => assert!(false, "{}", e), + } + let test_data = String::from("Test\n"); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); test_state.borrow().memwrite(addr, test_data.as_bytes()); @@ -121,12 +192,6 @@ fn verify_pty_io( false, ); - let mut input: Option = None; - match File::open(&pty_path) { - Ok(file) => input = Some(file), - Err(e) => assert!(false, "{}", e), - } - verify_input_data(&mut input.unwrap(), &test_data); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); @@ -217,21 +282,11 @@ fn console_features_negotiate() { let (console, test_state, alloc) = create_console(chardev, pci_slot, pci_fn); let mut features = console.borrow().get_device_features(); - features |= 1 << VIRTIO_CONSOLE_F_SIZE; + features |= 1 << VIRTIO_CONSOLE_F_SIZE | 1 << VIRTIO_CONSOLE_F_MULTIPORT; console.borrow_mut().negotiate_features(features); console.borrow_mut().set_features_ok(); assert_eq!(features, console.borrow_mut().get_guest_features()); - let unsupported_features = 1 << VIRTIO_CONSOLE_F_MULTIPORT; - features |= unsupported_features; - console.borrow_mut().negotiate_features(features); - console.borrow_mut().set_features_ok(); - assert_ne!(features, console.borrow_mut().get_guest_features()); - assert_eq!( - unsupported_features & console.borrow_mut().get_guest_features(), - 0 - ); - let unsupported_features = 1 << VIRTIO_CONSOLE_F_EMERG_WRITE; features |= unsupported_features; console.borrow_mut().negotiate_features(features); @@ -279,6 +334,16 @@ fn console_socket_basic() { let input_queue = vqs[0].clone(); let output_queue = vqs[1].clone(); + // Connect Guest. + let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); + out_control_event( + test_state.clone(), + alloc.clone(), + console.clone(), + open_msg, + &vqs, + ); + let mut stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); stream .set_nonblocking(true) @@ -370,6 +435,16 @@ fn console_parallel_req() { let input_queue = vqs[0].clone(); let output_queue = vqs[1].clone(); + // Connect Guest. + let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); + out_control_event( + test_state.clone(), + alloc.clone(), + console.clone(), + open_msg, + &vqs, + ); + let mut stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); stream .set_nonblocking(true) @@ -444,6 +519,15 @@ fn console_parallel_req() { let input_queue = vqs[0].clone(); let output_queue = vqs[1].clone(); + let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); + out_control_event( + test_state.clone(), + alloc.clone(), + console.clone(), + open_msg, + &vqs, + ); + let test_data = String::from("Test\n"); let addr = alloc.borrow_mut().alloc(test_data.len() as u64); test_state.borrow().memwrite(addr, test_data.as_bytes()); -- Gitee From 5f36bd62a35260181518b017185af2e67650d1ef Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Wed, 10 May 2023 17:02:30 +0800 Subject: [PATCH 1067/2187] virtio-gpu: fix driver needs to be installed manually Viogpu needs to be installed manually because it checks subsystem_id. But this is a particular behavior of the driver, so we modified here for compatibility with front-end drivers only. Signed-off-by: wubinfeng --- pci/src/config.rs | 2 ++ virtio/src/transport/virtio_pci.rs | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 28cbd018b..b28977e42 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -90,6 +90,8 @@ pub const HEADER_TYPE_MULTIFUNC: u8 = 0x80; pub const PCI_VENDOR_ID_REDHAT_QUMRANET: u16 = 0x1af4; /// The vendor ID for PCI devices other than virtio. pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; +/// The sub device ID for Red Hat / Qumranet. +pub const PCI_SUBDEVICE_ID_QEMU: u16 = 0x1100; const PCI_CONFIG_HEAD_END: u8 = 64; const NEXT_CAP_OFFSET: u8 = 0x01; diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 873973888..ea56721d7 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -23,8 +23,8 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use pci::config::{ RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, - PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, - SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, + PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, + STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::{update_dev_id, MsixState, MSIX_TABLE_ENTRY_SIZE}; @@ -1070,11 +1070,14 @@ impl PciDevOps for VirtioPciDevice { SUBSYSTEM_VENDOR_ID, VIRTIO_PCI_VENDOR_ID, )?; - le_write_u16( - &mut self.config.config, - SUBSYSTEM_ID, - 0x40 + device_type as u16, - )?; + // For compatibility with windows viogpu as front-end drivers. + let subsysid = if device_type == VIRTIO_TYPE_GPU { + PCI_SUBDEVICE_ID_QEMU + } else { + 0x40 + device_type as u16 + }; + le_write_u16(&mut self.config.config, SUBSYSTEM_ID, subsysid)?; + init_multifunction( self.multi_func, &mut self.config.config, -- Gitee From 588345233ed9cee3ba91dd26d71f4a7cc58f81b4 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 11 May 2023 11:04:49 +0800 Subject: [PATCH 1068/2187] gtk: fix button event may be lost When pointer is out of window, the x or y is out of range and button event may be lost. Signed-off-by: zhouli57 --- ui/src/gtk/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index ee72569b5..5cbeb41e6 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -35,7 +35,7 @@ use gtk::{ }, Application, ApplicationWindow, DrawingArea, RadioMenuItem, }; -use log::error; +use log::{debug, error}; use crate::{ console::{ @@ -426,7 +426,8 @@ impl GtkDisplayScreen { }; if x.lt(&0.0) || x.gt(&window_width) || y.lt(&0.0) || y.gt(&window_height) { - bail!("The coordinate of pointer exceeds limit") + debug!("x {} or y {} out of range, use last value.", x, y); + return Ok((self.click_state.last_x, self.click_state.last_y)); } // There may be unfilled areas between the window and the image. -- Gitee From b3776fcf464d077da10d7d3ccfd3780eb8e671e9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 11 May 2023 16:37:44 +0800 Subject: [PATCH 1069/2187] gtk: disable the default F10 menu shortcut Signed-off-by: zhouli57 --- ui/src/gtk/menu.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 5b78c6ab5..741817b85 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -19,8 +19,8 @@ use gtk::{ ffi::{GDK_KEY_equal, GDK_KEY_minus, GDK_KEY_B, GDK_KEY_F, GDK_KEY_M, GDK_KEY_S}, ModifierType, }, - glib, - prelude::{AccelGroupExtManual, NotebookExtManual, WidgetExtManual}, + glib::{self, object::GObject, translate::ToGlibPtr}, + prelude::{AccelGroupExtManual, NotebookExtManual, ObjectType, WidgetExtManual}, traits::{ BoxExt, CheckMenuItemExt, ContainerExt, DialogExt, GtkMenuExt, GtkMenuItemExt, GtkWindowExt, MenuShellExt, NotebookExt, WidgetExt, @@ -233,6 +233,17 @@ impl GtkMenu { self.container.pack_start(&self.menu_bar, false, false, 0); self.container.pack_start(&self.note_book, true, true, 0); self.window.add(&self.container); + + // Disable the default F10 menu shortcut. + if let Some(setting) = self.window.settings() { + unsafe { + gtk::glib::gobject_ffi::g_object_set_property( + setting.as_ptr() as *mut GObject, + "gtk-menu-bar-accel".to_glib_none().0, + glib::Value::from("").to_glib_none().0, + ); + } + } } /// Show window. -- Gitee From cd4842f01879d406503496e824f5ce46fa78f24d Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 10 May 2023 14:58:01 +0800 Subject: [PATCH 1070/2187] GTK: Change default window size Change default window size to 1080 * 720. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 5cbeb41e6..1645b09bf 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -55,6 +55,10 @@ use util::pixman::{pixman_format_code_t, pixman_image_composite, pixman_op_t}; use vmm_sys_util::eventfd::EventFd; const CHANNEL_BOUND: usize = 1024; +/// Width of default window. +const DEFAULT_WINDOW_WIDTH: i32 = 1080; +/// Height of default window. +const DEFAULT_WINDOW_HEIGHT: i32 = 768; pub(crate) const GTK_SCALE_MIN: f64 = 0.25; pub(crate) const GTK_ZOOM_STEP: f64 = 0.25; @@ -494,8 +498,8 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { let window = ApplicationWindow::builder() .application(app) .title(>k_cfg.vm_name) - .default_width(DEFAULT_SURFACE_WIDTH) - .default_height(DEFAULT_SURFACE_HEIGHT) + .default_width(DEFAULT_WINDOW_WIDTH) + .default_height(DEFAULT_WINDOW_HEIGHT) .build(); // Set app name if configured. -- Gitee From e1c069c045907feeb4ad25407549eb76c4a81734 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 11 May 2023 20:24:48 +0800 Subject: [PATCH 1071/2187] GTK: delete command line configuration of fix-size Delete command line configuration of fix-size and activate zoom-fit button by default. Signed-off-by: Xiao Ye --- machine_manager/src/config/display.rs | 31 +-------------------------- ui/src/gtk/menu.rs | 13 ++++++++--- ui/src/gtk/mod.rs | 7 +++--- 3 files changed, 14 insertions(+), 37 deletions(-) diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 1889aeabb..74ac7d08e 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -35,8 +35,6 @@ pub struct DisplayConfig { pub gtk: bool, /// App name if configured. pub app_name: Option, - /// Fix window size. - pub fix_size: bool, /// Keep the window fill the desktop. pub full_screen: bool, } @@ -44,11 +42,7 @@ pub struct DisplayConfig { impl VmConfig { pub fn add_display(&mut self, vm_config: &str) -> Result<()> { let mut cmd_parser = CmdParser::new("display"); - cmd_parser - .push("") - .push("full-screen") - .push("fix-size") - .push("app-name"); + cmd_parser.push("").push("full-screen").push("app-name"); cmd_parser.parse(vm_config)?; let mut display_config = DisplayConfig::default(); if let Some(str) = cmd_parser.get_value::("")? { @@ -60,9 +54,6 @@ impl VmConfig { if let Some(name) = cmd_parser.get_value::("app-name")? { display_config.app_name = Some(name); } - if let Some(default) = cmd_parser.get_value::("fix-size")? { - display_config.fix_size = default.into(); - } if let Some(default) = cmd_parser.get_value::("full-screen")? { display_config.full_screen = default.into(); } @@ -87,7 +78,6 @@ mod tests { let display_config = vm_config.display.unwrap(); assert_eq!(display_config.gtk, true); assert_eq!(display_config.full_screen, false); - assert_eq!(display_config.fix_size, false); let mut vm_config = VmConfig::default(); let config_line = "gtk,full-screen=on"; @@ -95,7 +85,6 @@ mod tests { let display_config = vm_config.display.unwrap(); assert_eq!(display_config.gtk, true); assert_eq!(display_config.full_screen, true); - assert_eq!(display_config.fix_size, false); let mut vm_config = VmConfig::default(); let config_line = "gtk,full-screen=off"; @@ -103,23 +92,6 @@ mod tests { let display_config = vm_config.display.unwrap(); assert_eq!(display_config.gtk, true); assert_eq!(display_config.full_screen, false); - assert_eq!(display_config.fix_size, false); - - let mut vm_config = VmConfig::default(); - let config_line = "gtk,fix-size=on"; - assert!(vm_config.add_display(config_line).is_ok()); - let display_config = vm_config.display.unwrap(); - assert_eq!(display_config.gtk, true); - assert_eq!(display_config.full_screen, false); - assert_eq!(display_config.fix_size, true); - - let mut vm_config = VmConfig::default(); - let config_line = "gtk,fix-size=off"; - assert!(vm_config.add_display(config_line).is_ok()); - let display_config = vm_config.display.unwrap(); - assert_eq!(display_config.gtk, true); - assert_eq!(display_config.full_screen, false); - assert_eq!(display_config.fix_size, false); let mut vm_config = VmConfig::default(); let config_line = "gtk,app-name=desktopappengine"; @@ -127,7 +99,6 @@ mod tests { let display_config = vm_config.display.unwrap(); assert_eq!(display_config.gtk, true); assert_eq!(display_config.full_screen, false); - assert_eq!(display_config.fix_size, false); assert_eq!( display_config.app_name, Some("desktopappengine".to_string()) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 741817b85..4dffb5c6b 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -38,6 +38,8 @@ use crate::{ }, }; +use super::ScaleMode; + #[derive(Clone)] pub(crate) struct GtkMenu { pub(crate) window: ApplicationWindow, @@ -247,10 +249,15 @@ impl GtkMenu { } /// Show window. - pub(crate) fn show_window(&self, is_full_screen: bool) { - if is_full_screen { - self.window.fullscreen(); + pub(crate) fn show_window(&self, scale_mode: Rc>) { + if scale_mode.borrow().full_screen { + self.full_screen_item.activate(); + } + + if scale_mode.borrow().free_scale { + self.zoom_fit.activate(); } + self.window.show_all(); self.menu_bar.hide(); } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 1645b09bf..9ecbca34f 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -209,7 +209,7 @@ impl GtkDisplay { // Window scale mode. let scale_mode = Rc::new(RefCell::new(ScaleMode { full_screen: gtk_cfg.full_screen, - free_scale: gtk_cfg.zoom_fit, + free_scale: true, })); // Mapping ASCII to keycode. let keysym2keycode: Rc>> = Rc::new(RefCell::new(HashMap::new())); @@ -455,7 +455,6 @@ impl GtkDisplayScreen { #[derive(Clone)] struct GtkConfig { full_screen: bool, - zoom_fit: bool, app_name: Option, vm_name: String, /// Gracefully Shutdown. @@ -469,7 +468,6 @@ struct GtkConfig { pub fn gtk_display_init(ds_cfg: &DisplayConfig, ui_context: UiContext) -> Result<()> { let gtk_cfg = GtkConfig { full_screen: ds_cfg.full_screen, - zoom_fit: ds_cfg.fix_size, app_name: ds_cfg.app_name.clone(), vm_name: ui_context.vm_name, powerdown_button: ui_context.power_button, @@ -513,12 +511,13 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { gtk_menu.set_menu(); gtk_menu.set_signal(&gd); + let scale_mode = gd.borrow().scale_mode.clone(); // Gtk display init. graphic_display_init(gd) .with_context(|| "Gtk display init failed!") .unwrap(); - gtk_menu.show_window(gtk_cfg.full_screen); + gtk_menu.show_window(scale_mode); } fn graphic_display_init(gd: Rc>) -> Result<()> { -- Gitee From 57c20019466d59c0dfebdc1fade4a8af934e2337 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 11 May 2023 21:32:31 +0800 Subject: [PATCH 1072/2187] GTK: Change default window size Change default window size to 1024 * 768 Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 9ecbca34f..cbc9ce08d 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -56,7 +56,7 @@ use vmm_sys_util::eventfd::EventFd; const CHANNEL_BOUND: usize = 1024; /// Width of default window. -const DEFAULT_WINDOW_WIDTH: i32 = 1080; +const DEFAULT_WINDOW_WIDTH: i32 = 1024; /// Height of default window. const DEFAULT_WINDOW_HEIGHT: i32 = 768; pub(crate) const GTK_SCALE_MIN: f64 = 0.25; -- Gitee From 46291eb58f681b54badc688c615a6a2ddcd420ef Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 22:04:20 +0800 Subject: [PATCH 1073/2187] refactor: uniform name of scanout and output Output means the emulated physical screen, and scanout means the screen information seen by software. Signed-off-by: wubinfeng --- machine_manager/src/config/gpu.rs | 8 ++++---- virtio/src/device/gpu.rs | 30 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 151dd17aa..186a1adbb 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -15,8 +15,8 @@ use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; use anyhow::{anyhow, Result}; use log::warn; -/// The maximum number of scanouts. -pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; +/// The maximum number of outputs. +pub const VIRTIO_GPU_MAX_OUTPUTS: usize = 16; pub const VIRTIO_GPU_MAX_HOSTMEM: u64 = 256 * M; @@ -46,12 +46,12 @@ impl Default for GpuDevConfig { impl ConfigCheck for GpuDevConfig { fn check(&self) -> Result<()> { check_arg_too_long(&self.id, "id")?; - if self.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 || self.max_outputs == 0 { + if self.max_outputs > VIRTIO_GPU_MAX_OUTPUTS as u32 || self.max_outputs == 0 { return Err(anyhow!(ConfigError::IllegalValue( "max_outputs".to_string(), 0, false, - VIRTIO_GPU_MAX_SCANOUTS as u64, + VIRTIO_GPU_MAX_OUTPUTS as u64, true ))); } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index fac155498..d7af82483 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -26,7 +26,7 @@ use crate::{ use address_space::{AddressSpace, GuestAddress}; use anyhow::{anyhow, bail, Result}; use log::{error, warn}; -use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_SCANOUTS}; +use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationManager}; use migration_derive::{ByteCode, Desc}; @@ -91,7 +91,7 @@ impl Default for GpuResource { #[allow(unused)] #[derive(Default, Clone, Copy)] -struct VirtioGpuReqState { +struct VirtioGpuOutputState { width: u32, height: u32, x_coor: i32, @@ -133,7 +133,7 @@ impl ByteCode for VirtioGpuDisplayOne {} #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, - pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS], + pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_OUTPUTS], } impl ByteCode for VirtioGpuDisplayInfo {} @@ -382,8 +382,8 @@ struct GpuIoHandler { enable_output_bitmask: u32, /// The number of scanouts num_scanouts: u32, - /// States of all request in scanout. - req_states: [VirtioGpuReqState; VIRTIO_GPU_MAX_SCANOUTS], + /// States of all output_states. + output_states: [VirtioGpuOutputState; VIRTIO_GPU_MAX_OUTPUTS], /// Scanouts of gpu, mouse doesn't realize copy trait, so it is a vector. scanouts: Vec, /// Max host mem for resource. @@ -674,8 +674,8 @@ impl GpuIoHandler { if (self.enable_output_bitmask & (1 << i)) != 0 { let i = i as usize; display_info.pmodes[i].enabled = 1; - display_info.pmodes[i].rect.width = self.req_states[i].width; - display_info.pmodes[i].rect.height = self.req_states[i].height; + display_info.pmodes[i].rect.width = self.output_states[i].width; + display_info.pmodes[i].rect.height = self.output_states[i].height; display_info.pmodes[i].flags = 0; } } @@ -712,8 +712,8 @@ impl GpuIoHandler { "HWV", "STRA Monitor", 100, - self.req_states[edid_req.scanouts as usize].width, - self.req_states[edid_req.scanouts as usize].height, + self.output_states[edid_req.scanouts as usize].width, + self.output_states[edid_req.scanouts as usize].height, ); edid_info.edid_array_fulfill(&mut edid_resp.edid.to_vec()); edid_resp.size = edid_resp.edid.len() as u32; @@ -1559,11 +1559,11 @@ impl Gpu { impl VirtioDevice for Gpu { /// Realize virtio gpu device. fn realize(&mut self) -> Result<()> { - if self.cfg.max_outputs > VIRTIO_GPU_MAX_SCANOUTS as u32 { + if self.cfg.max_outputs > VIRTIO_GPU_MAX_OUTPUTS as u32 { bail!( "Invalid max_outputs {} which is bigger than {}", self.cfg.max_outputs, - VIRTIO_GPU_MAX_SCANOUTS + VIRTIO_GPU_MAX_OUTPUTS ); } @@ -1684,7 +1684,7 @@ impl VirtioDevice for Gpu { } self.interrupt_cb = Some(interrupt_cb.clone()); - let req_states = [VirtioGpuReqState::default(); VIRTIO_GPU_MAX_SCANOUTS]; + let output_states = [VirtioGpuOutputState::default(); VIRTIO_GPU_MAX_OUTPUTS]; let mut scanouts = vec![]; for con in &self.consoles { @@ -1706,13 +1706,13 @@ impl VirtioDevice for Gpu { resources_list: Vec::new(), enable_output_bitmask: 1, num_scanouts: self.cfg.max_outputs, - req_states, + output_states, scanouts, max_hostmem: self.cfg.max_hostmem, used_hostmem: 0, }; - handler.req_states[0].width = self.cfg.xres; - handler.req_states[0].height = self.cfg.yres; + handler.output_states[0].width = self.cfg.xres; + handler.output_states[0].height = self.cfg.yres; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); register_event_helper(notifiers, None, &mut self.deactivate_evts)?; -- Gitee From f2d131278a3daea96ea7cedb4f9a44010f3a31c1 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 22:41:39 +0800 Subject: [PATCH 1074/2187] gtk: add event_configure event Event_configure event will be triggered when the window is stretched. This event will be used in automatic resolution adjustment on the virtio gpu. Signed-off-by: wubinfeng --- ui/src/console.rs | 27 ++++++++++++++++++++++++++- ui/src/gtk/draw.rs | 25 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index bc2f2a0a4..19942d3b1 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -137,6 +137,8 @@ pub trait DisplayChangeListenerOperations { pub trait HardWareOperations { /// Update image. fn hw_update(&self, _con: Arc>) {} + /// Ui configuration changed. + fn hw_ui_info(&self, _con: Arc>, _width: u32, _height: u32) {} } /// Listen to the change of image and call the related @@ -171,7 +173,8 @@ pub struct DisplayConsole { pub height: i32, pub surface: Option, pub console_list: Weak>, - dev_opts: Arc, + pub dev_opts: Arc, + pub timer_id: Option, active: bool, } @@ -192,6 +195,7 @@ impl DisplayConsole { console_list, surface: None, dev_opts, + timer_id: None, active: true, } } @@ -493,6 +497,27 @@ pub fn graphic_hardware_update(con_id: Option) { } } +pub fn graphic_hardware_ui_info(con_id: Option, width: u32, height: u32) { + let console = CONSOLES.lock().unwrap().get_console_by_id(con_id); + + if let Some(con) = console { + let con_opts = con.lock().unwrap().dev_opts.clone(); + let con_clone = con.clone(); + let func = Box::new(move || { + (*con_opts).hw_ui_info(con_clone.clone(), width, height); + }); + + if let Some(ctx) = EventLoop::get_ctx(None) { + let mut con_locked = con.lock().unwrap(); + if let Some(timer_id) = con_locked.timer_id { + ctx.timer_del(timer_id); + } + + con_locked.timer_id = Some(ctx.timer_add(func, Duration::from_millis(500))); + } + } +} + /// Get the weak reference of all active consoles from the console lists. pub fn get_active_console() -> Vec>> { let mut res: Vec>> = vec![]; diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 01d816bc4..6a3fc32a3 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -24,6 +24,7 @@ use gtk::{ use log::error; use crate::{ + console::graphic_hardware_ui_info, gtk::GtkDisplayScreen, input::{ self, point_event, press_mouse, update_key_state, ABS_MAX, INPUT_POINT_LEFT, @@ -76,6 +77,12 @@ pub(crate) fn set_callback_for_draw_area( Inhibit(false)} ), ); + draw_area.connect_configure_event( + glib::clone!(@weak gs => @default-return false, move |_, event_configure| { + da_configure_callback(&gs, event_configure).unwrap_or_else(|e|error!("Configure event: {}", e)); + false} + ), + ); let event_mask = EventMask::BUTTON_PRESS_MASK | EventMask::BUTTON_RELEASE_MASK @@ -90,6 +97,24 @@ pub(crate) fn set_callback_for_draw_area( Ok(()) } +/// When the window size changes, +/// the image resolution adapts to the window. +fn da_configure_callback( + gs: &Rc>, + event_configure: &gdk::EventConfigure, +) -> Result<()> { + let borrowed_gs = gs.borrow(); + let con = match borrowed_gs.con.upgrade() { + Some(c) => c, + None => return Ok(()), + }; + let con_id = con.lock().unwrap().con_id; + let (width, height) = event_configure.size(); + + graphic_hardware_ui_info(Some(con_id), width, height); + Ok(()) +} + fn da_key_callback( gs: &Rc>, key_event: &gdk::EventKey, -- Gitee From c99e1f80b28a8c595ca7de6e773c94c1bf9b86eb Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 23:15:03 +0800 Subject: [PATCH 1075/2187] virtio-gpu: modify the configuration space to be shared between process To support resolution auto-adaptation, the configuration space needs to be set to be shared between processes. Because each resolution change needs to be notified to the front end via event_read of the configuration space. Signed-off-by: wubinfeng --- virtio/src/device/gpu.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index d7af82483..7166758f2 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -28,8 +28,7 @@ use anyhow::{anyhow, bail, Result}; use log::{error, warn}; use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use migration::{DeviceStateDesc, FieldDesc, MigrationManager}; -use migration_derive::{ByteCode, Desc}; +use migration_derive::ByteCode; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; @@ -1509,15 +1508,24 @@ pub struct VirtioGpuConfig { /// State of gpu device. #[repr(C)] -#[derive(Clone, Copy, Desc, ByteCode)] -#[desc_version(compat_version = "0.1.0")] +#[derive(Clone)] pub struct GpuState { /// Bitmask of features supported by the backend. device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, /// Config space of the GPU device. - config_space: VirtioGpuConfig, + config_space: Arc>, +} + +impl Default for GpuState { + fn default() -> Self { + GpuState { + device_features: 0, + driver_features: 0, + config_space: Arc::new(Mutex::new(VirtioGpuConfig::default())), + } + } } /// GPU device structure. @@ -1551,8 +1559,9 @@ impl Gpu { } fn build_device_config_space(&mut self) { - self.state.config_space.num_scanouts = self.cfg.max_outputs; - self.state.config_space.reserved = 0; + let mut config_space = self.state.config_space.lock().unwrap(); + config_space.num_scanouts = self.cfg.max_outputs; + config_space.reserved = 0; } } @@ -1592,7 +1601,7 @@ impl VirtioDevice for Gpu { console_close(con)?; } - MigrationManager::unregister_device_instance(GpuState::descriptor(), &self.cfg.id); + // TODO: support migration Ok(()) } @@ -1628,7 +1637,8 @@ impl VirtioDevice for Gpu { /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.state.config_space.as_bytes(); + let config_space = *self.state.config_space.lock().unwrap(); + let config_slice = config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset @@ -1647,7 +1657,8 @@ impl VirtioDevice for Gpu { /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let mut config_cpy = self.state.config_space; + let mut config_space = self.state.config_space.lock().unwrap(); + let mut config_cpy = *config_space; let config_cpy_slice = config_cpy.as_mut_bytes(); let config_len = config_cpy_slice.len() as u64; @@ -1660,8 +1671,8 @@ impl VirtioDevice for Gpu { } config_cpy_slice[(offset as usize)..(offset as usize + data.len())].copy_from_slice(data); - if self.state.config_space.events_clear != 0 { - self.state.config_space.events_read &= !config_cpy.events_clear; + if config_space.events_clear != 0 { + config_space.events_read &= !config_cpy.events_clear; } Ok(()) -- Gitee From bd961a075d5d4abcec400aa578df3930285b7748 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 11 May 2023 14:41:24 +0800 Subject: [PATCH 1076/2187] virtio-gpu: fix layout of struct and write to real edid 1. Data structure used in front and back end communication need to specify an explicit layout. 2. Expected update edid info, but to_vec() will generate a array fill of copy_obj. Signed-off-by: wubinfeng --- tests/mod_test/src/libdriver/virtio_gpu.rs | 3 +++ util/src/edid.rs | 2 +- virtio/src/device/gpu.rs | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index e5f44ca15..6e96f12f9 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -68,6 +68,7 @@ impl VirtioGpuRect { impl ByteCode for VirtioGpuRect {} +#[repr(C)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuDisplayOne { pub rect: VirtioGpuRect, @@ -77,6 +78,7 @@ pub struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} +#[repr(C)] #[allow(unused)] #[derive(Default, Clone, Copy)] pub struct VirtioGpuDisplayInfo { @@ -103,6 +105,7 @@ impl VirtioGpuGetEdid { impl ByteCode for VirtioGpuGetEdid {} +#[repr(C)] #[allow(unused)] #[derive(Clone, Copy)] pub struct VirtioGpuRespEdid { diff --git a/util/src/edid.rs b/util/src/edid.rs index 912682589..17080d70b 100644 --- a/util/src/edid.rs +++ b/util/src/edid.rs @@ -50,7 +50,7 @@ impl EdidInfo { } } - pub fn edid_array_fulfill(&mut self, edid_array: &mut Vec) { + pub fn edid_array_fulfill(&mut self, edid_array: &mut [u8; 1024]) { // The format follows VESA ENHANCED EXTENDED DISPLAY IDENTIFICATION DATA STANDARD if self.vendor.len() != 3 { // HWV for 'HUAWEI TECHNOLOGIES CO., INC.' diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 7166758f2..63dd0b51c 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -120,6 +120,7 @@ struct VirtioGpuRect { impl ByteCode for VirtioGpuRect {} +#[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayOne { rect: VirtioGpuRect, @@ -129,6 +130,7 @@ struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} +#[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, @@ -144,6 +146,7 @@ struct VirtioGpuGetEdid { } impl ByteCode for VirtioGpuGetEdid {} +#[repr(C)] #[allow(unused)] // data which transfer to frontend need padding #[derive(Clone, Copy)] @@ -714,7 +717,7 @@ impl GpuIoHandler { self.output_states[edid_req.scanouts as usize].width, self.output_states[edid_req.scanouts as usize].height, ); - edid_info.edid_array_fulfill(&mut edid_resp.edid.to_vec()); + edid_info.edid_array_fulfill(&mut edid_resp.edid); edid_resp.size = edid_resp.edid.len() as u32; self.send_response(req, &edid_resp)?; -- Gitee From 0775d34d9a6a07bcbc618e251a62bfd6c1ef0509 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Tue, 9 May 2023 23:48:25 +0800 Subject: [PATCH 1077/2187] virtio-gpu: support automatic resolution adjustment By sharing device configuration space and outputs states, data synchronization between ui process and io process is realized to achieve the purpose of resolution automatic adaptation. Signed-off-by: wubinfeng --- virtio/src/device/gpu.rs | 98 ++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 63dd0b51c..6207bd90d 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -58,8 +58,10 @@ use util::pixman::{ use util::{aio::Iovec, edid::EdidInfo}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -// number of virtqueues +/// Number of virtqueues const QUEUE_NUM_GPU: usize = 2; +/// Display changed event +const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0; #[derive(Debug)] struct GpuResource { @@ -91,6 +93,7 @@ impl Default for GpuResource { #[allow(unused)] #[derive(Default, Clone, Copy)] struct VirtioGpuOutputState { + con_id: usize, width: u32, height: u32, x_coor: i32, @@ -249,9 +252,45 @@ struct VirtioGpuResourceDetachBacking { impl ByteCode for VirtioGpuResourceDetachBacking {} -#[derive(Default)] -pub struct GpuOpts {} -impl HardWareOperations for GpuOpts {} +pub struct GpuOpts { + /// Status of the emulated physical outputs. + output_states: Arc>, + /// Config space of the GPU device. + config_space: Arc>, + /// Callback to trigger interrupt. + interrupt_cb: Option>, +} + +impl HardWareOperations for GpuOpts { + fn hw_ui_info(&self, con: Arc>, width: u32, height: u32) { + let con_id = con.lock().unwrap().con_id; + + // Update output size. + for output_state in self.output_states.lock().unwrap().iter_mut() { + if output_state.con_id == con_id { + output_state.width = width; + output_state.height = height; + break; + } + } + + // Update events_read in config sapce. + let mut config_space = self.config_space.lock().unwrap(); + config_space.events_read |= VIRTIO_GPU_EVENT_DISPLAY; + + if self.interrupt_cb.is_none() { + return; + } + let interrup_cb = self.interrupt_cb.as_ref().unwrap(); + if let Err(e) = (interrup_cb)(&VirtioInterruptType::Config, None, false) { + error!( + "{:?}. {:?}", + VirtioError::InterruptTrigger("gpu", VirtioInterruptType::Config), + e + ); + } + } +} #[allow(unused)] #[derive(Default, Clone)] @@ -385,7 +424,7 @@ struct GpuIoHandler { /// The number of scanouts num_scanouts: u32, /// States of all output_states. - output_states: [VirtioGpuOutputState; VIRTIO_GPU_MAX_OUTPUTS], + output_states: Arc>, /// Scanouts of gpu, mouse doesn't realize copy trait, so it is a vector. scanouts: Vec, /// Max host mem for resource. @@ -672,15 +711,18 @@ impl GpuIoHandler { fn cmd_get_display_info(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut display_info = VirtioGpuDisplayInfo::default(); display_info.header.hdr_type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO; + + let output_states_lock = self.output_states.lock().unwrap(); for i in 0..self.num_scanouts { if (self.enable_output_bitmask & (1 << i)) != 0 { let i = i as usize; display_info.pmodes[i].enabled = 1; - display_info.pmodes[i].rect.width = self.output_states[i].width; - display_info.pmodes[i].rect.height = self.output_states[i].height; + display_info.pmodes[i].rect.width = output_states_lock[i].width; + display_info.pmodes[i].rect.height = output_states_lock[i].height; display_info.pmodes[i].flags = 0; } } + drop(output_states_lock); if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { display_info.header.flags |= VIRTIO_GPU_FLAG_FENCE; @@ -710,13 +752,15 @@ impl GpuIoHandler { edid_resp.header.ctx_id = req.header.ctx_id; } + let output_states_lock = self.output_states.lock().unwrap(); let mut edid_info = EdidInfo::new( "HWV", "STRA Monitor", 100, - self.output_states[edid_req.scanouts as usize].width, - self.output_states[edid_req.scanouts as usize].height, + output_states_lock[edid_req.scanouts as usize].width, + output_states_lock[edid_req.scanouts as usize].height, ); + drop(output_states_lock); edid_info.edid_array_fulfill(&mut edid_resp.edid); edid_resp.size = edid_resp.edid.len() as u32; @@ -1538,6 +1582,8 @@ pub struct Gpu { cfg: GpuDevConfig, /// Status of the GPU device. state: GpuState, + /// Status of the emulated physical outputs. + output_states: Arc>, /// Each console corresponds to a display. consoles: Vec>>>, /// Callback to trigger interrupt. @@ -1555,6 +1601,9 @@ impl Gpu { Self { cfg, state: GpuState::default(), + output_states: Arc::new(Mutex::new( + [VirtioGpuOutputState::default(); VIRTIO_GPU_MAX_OUTPUTS], + )), consoles: Vec::new(), interrupt_cb: None, deactivate_evts: Vec::new(), @@ -1586,12 +1635,20 @@ impl VirtioDevice for Gpu { self.state.device_features |= 1 << VIRTIO_GPU_F_EDID; } + let mut output_states = self.output_states.lock().unwrap(); for i in 0..self.cfg.max_outputs { - let gpu_opts = Arc::new(GpuOpts::default()); + let gpu_opts = Arc::new(GpuOpts { + output_states: self.output_states.clone(), + config_space: self.state.config_space.clone(), + interrupt_cb: self.interrupt_cb.clone(), + }); let dev_name = format!("virtio-gpu{}", i); let con = console_init(dev_name, ConsoleType::Graphic, gpu_opts); + let con_ref = con.as_ref().unwrap().upgrade().unwrap(); + output_states[i as usize].con_id = con_ref.lock().unwrap().con_id; self.consoles.push(con); } + drop(output_states); self.build_device_config_space(); @@ -1698,10 +1755,23 @@ impl VirtioDevice for Gpu { } self.interrupt_cb = Some(interrupt_cb.clone()); - let output_states = [VirtioGpuOutputState::default(); VIRTIO_GPU_MAX_OUTPUTS]; + + let mut output_states = self.output_states.lock().unwrap(); + output_states[0].width = self.cfg.xres; + output_states[0].height = self.cfg.yres; + drop(output_states); let mut scanouts = vec![]; for con in &self.consoles { + let gpu_opts = Arc::new(GpuOpts { + output_states: self.output_states.clone(), + config_space: self.state.config_space.clone(), + interrupt_cb: self.interrupt_cb.clone(), + }); + + let con_ref = con.as_ref().unwrap().upgrade().unwrap(); + con_ref.lock().unwrap().dev_opts = gpu_opts; + let scanout = GpuScanout { con: con.clone(), ..Default::default() @@ -1709,7 +1779,7 @@ impl VirtioDevice for Gpu { scanouts.push(scanout); } - let mut handler = GpuIoHandler { + let handler = GpuIoHandler { ctrl_queue: queues[0].clone(), cursor_queue: queues[1].clone(), mem_space, @@ -1720,13 +1790,11 @@ impl VirtioDevice for Gpu { resources_list: Vec::new(), enable_output_bitmask: 1, num_scanouts: self.cfg.max_outputs, - output_states, + output_states: self.output_states.clone(), scanouts, max_hostmem: self.cfg.max_hostmem, used_hostmem: 0, }; - handler.output_states[0].width = self.cfg.xres; - handler.output_states[0].height = self.cfg.yres; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); register_event_helper(notifiers, None, &mut self.deactivate_evts)?; -- Gitee From d469f11c15d2f2de8e693bccafc7b1ebdbd14534 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 May 2023 07:32:24 +0800 Subject: [PATCH 1078/2187] Fix some warnings in rustc v1.60.0 clippy There exists some warnings when using "cargo clippy -- -D warnings" in rustc v1.60.0. Fix it. Signed-off-by: liuxiangdong --- pci/src/config.rs | 2 +- ui/src/vnc/client_io.rs | 20 +++++++------------- ui/src/vnc/mod.rs | 9 +++------ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index b28977e42..d0249da0a 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -825,7 +825,7 @@ impl PciConfig { } fn is_bar_region_empty( - &mut self, + &self, id: usize, #[cfg(target_arch = "x86_64")] io_region: Option<&Region>, mem_region: Option<&Region>, diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 3bdcdc20d..b0e357122 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::{ - console::{console_select, DisplayMouse}, + console::console_select, error::VncError, input::{ key_event, keyboard_modifier_get, keyboard_state_reset, point_event, update_key_state, @@ -1294,25 +1294,19 @@ pub fn display_cursor_define( server: &Arc, buf: &mut Vec, ) { - let mut cursor: DisplayMouse; - let mut mask: Vec; let locked_cursor = server.vnc_cursor.lock().unwrap(); - match &locked_cursor.cursor { - Some(c) => { - cursor = c.clone(); - } + let mut cursor = match &locked_cursor.cursor { + Some(c) => c.clone(), None => { return; } - } - match &locked_cursor.mask { - Some(m) => { - mask = m.clone(); - } + }; + let mut mask = match &locked_cursor.mask { + Some(m) => m.clone(), None => { return; } - } + }; drop(locked_cursor); if cursor.data.is_empty() || cursor.data.len() != ((cursor.width * cursor.height) as usize) * bytes_per_pixel() diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 17b6ad419..50af7db0e 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -324,16 +324,13 @@ fn start_vnc_thread() -> Result<()> { continue; } - let mut rect_info; - match rect_jobs.lock().unwrap().get_mut(0) { - Some(rect) => { - rect_info = rect.clone(); - } + let mut rect_info = match rect_jobs.lock().unwrap().get_mut(0) { + Some(rect) => rect.clone(), None => { thread::sleep(time::Duration::from_millis(interval)); continue; } - } + }; rect_jobs.lock().unwrap().remove(0); let mut num_rects: i32 = 0; -- Gitee From 124faa3dbd7ae82a9077d09a335e36c7ca5f81cd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 11 May 2023 21:21:42 +0800 Subject: [PATCH 1079/2187] log: delete useless log Signed-off-by: zhouli57 --- devices/src/scsi/bus.rs | 2 +- virtio/src/transport/virtio_pci.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index a824f92d1..1f95d5499 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -687,7 +687,7 @@ impl ScsiRequest { } Err(ref e) => { if not_supported_flag { - info!("emulation scsi command {:#x} is no supported", self.cmd.op); + debug!("emulation scsi command {:#x} is no supported", self.cmd.op); status = CHECK_CONDITION; sense = Some(SCSI_SENSE_INVALID_OPCODE); } else { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index b53a7f15c..5420ee961 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use anyhow::{anyhow, bail, Context}; use byteorder::{ByteOrder, LittleEndian}; -use log::{error, warn}; +use log::{debug, error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use pci::config::{ @@ -728,7 +728,7 @@ impl VirtioPciDevice { let mut locked_queues = self.queues.lock().unwrap(); for q_config in queues_config.iter_mut() { if !q_config.ready { - warn!("queue is not ready, please check your init process"); + debug!("queue is not ready, please check your init process"); } else { q_config.addr_cache.desc_table_host = self .sys_mem @@ -957,11 +957,11 @@ impl VirtioPciDevice { } let bar_base = self.config.get_bar_address(bar as usize); if bar_base == BAR_SPACE_UNMAPPED { - warn!("The bar {} of VirtioPciCfgAccessCap is not mapped", bar); + debug!("The bar {} of VirtioPciCfgAccessCap is not mapped", bar); return; } if ![1, 2, 4].contains(&len) { - warn!("The length {} of VirtioPciCfgAccessCap is illegal", len); + debug!("The length {} of VirtioPciCfgAccessCap is illegal", len); return; } if off & (len - 1) != 0 { -- Gitee From ee3a22215399bd3f02cfbe1ff84fbbff9cbb4373 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 May 2023 14:29:36 +0800 Subject: [PATCH 1080/2187] virtio-serial: set pty chardev default host connected. Pty chardev has opened by default in realize() function. Set it default host connected. Signed-off-by: liuxiangdong --- virtio/src/device/serial.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index d9ba4d74b..5899f58bc 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -32,7 +32,7 @@ use crate::{ use address_space::AddressSpace; use devices::legacy::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; use machine_manager::{ - config::{VirtioSerialInfo, VirtioSerialPort, DEFAULT_VIRTQUEUE_SIZE}, + config::{ChardevType, VirtioSerialInfo, VirtioSerialPort, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, event_loop::{register_event_helper, unregister_event_helper}, }; @@ -371,14 +371,16 @@ pub struct SerialPort { impl SerialPort { pub fn new(port_cfg: VirtioSerialPort) -> Self { + // Console is default host connected. And pty chardev has opened by default in realize() function. + let host_connected = port_cfg.is_console || port_cfg.chardev.backend == ChardevType::Pty; + SerialPort { name: Some(port_cfg.id), chardev: Arc::new(Mutex::new(Chardev::new(port_cfg.chardev))), nr: port_cfg.nr, is_console: port_cfg.is_console, guest_connected: false, - // Console is default host connected. - host_connected: port_cfg.is_console, + host_connected, ctrl_handler: None, } } -- Gitee From c193ac559d447f08c1fc17ef5b77e0d59543b2fa Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 May 2023 06:45:59 +0800 Subject: [PATCH 1081/2187] mst: add virtio-serial mst Add virtio-serial mst. Signed-off-by: liuxiangdong --- tests/mod_test/src/libdriver/mod.rs | 1 - .../mod_test/src/libdriver/virtio_console.rs | 89 -- tests/mod_test/tests/console_test.rs | 595 ------------- tests/mod_test/tests/serial_test.rs | 795 ++++++++++++++++++ 4 files changed, 795 insertions(+), 685 deletions(-) delete mode 100644 tests/mod_test/src/libdriver/virtio_console.rs delete mode 100644 tests/mod_test/tests/console_test.rs create mode 100644 tests/mod_test/tests/serial_test.rs diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 48a8badd8..8064c9a6d 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -19,7 +19,6 @@ pub mod pci_bus; pub mod usb; pub mod virtio; pub mod virtio_block; -pub mod virtio_console; pub mod virtio_gpu; pub mod virtio_pci_modern; pub mod virtio_rng; diff --git a/tests/mod_test/src/libdriver/virtio_console.rs b/tests/mod_test/src/libdriver/virtio_console.rs deleted file mode 100644 index 0f115bd6a..000000000 --- a/tests/mod_test/src/libdriver/virtio_console.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use super::machine::TestStdMachine; -use super::malloc::GuestAllocator; -use super::virtio_pci_modern::TestVirtioPciDev; -use crate::libtest::{test_init, TestState}; -use std::cell::RefCell; -use std::rc::Rc; - -pub enum ChardevType { - Stdio, - Pty, - Socket { - path: String, - server: bool, - nowait: bool, - }, - File { - path: String, - }, -} - -pub fn create_console( - chardev_type: ChardevType, - pci_slot: u8, - pci_fn: u8, -) -> ( - Rc>, - Rc>, - Rc>, -) { - let mut extra_args: Vec<&str> = Vec::new(); - - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); - extra_args.append(&mut args); - - let serial_pci_args = format!( - "-device {},id=serial0,bus=pcie.0,addr={}.0", - "virtio-serial-pci", pci_slot - ); - extra_args.append(&mut serial_pci_args[..].split(' ').collect()); - - let chardev_args = match chardev_type { - ChardevType::Stdio => String::from("-chardev stdio,id=charconsole0"), - ChardevType::Pty => String::from("-chardev pty,id=charconsole0"), - ChardevType::Socket { - path, - server, - nowait, - } => { - let mut args = format!("-chardev socket,id=charconsole0,path={}", path); - if server { - args.push_str(",server") - } - if nowait { - args.push_str(",nowait") - } - args - } - ChardevType::File { path } => { - let args = format!("-chardev file,id=charconsole0,path={}", path); - args - } - }; - extra_args.append(&mut chardev_args.split(' ').collect()); - - let console_args = String::from("-device virtconsole,chardev=charconsole0,id=console0,nr=0"); - extra_args.append(&mut console_args.split(' ').collect()); - - let test_state = Rc::new(RefCell::new(test_init(extra_args))); - let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); - - let virtio_console = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); - - virtio_console.borrow_mut().init(pci_slot, pci_fn); - - (virtio_console, test_state, allocator) -} diff --git a/tests/mod_test/tests/console_test.rs b/tests/mod_test/tests/console_test.rs deleted file mode 100644 index b204ba56d..000000000 --- a/tests/mod_test/tests/console_test.rs +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use serde_json::json; -use std::cell::RefCell; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::mem::size_of; -use std::net::Shutdown; -use std::os::unix::net::UnixStream; -use std::path::Path; -use std::rc::Rc; -use std::time; - -use util::byte_code::ByteCode; - -use mod_test::libdriver::malloc::GuestAllocator; -use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; -use mod_test::libdriver::virtio_console::{create_console, ChardevType}; -use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; -use mod_test::libtest::TestState; - -const TIMEOUT_US: u64 = 15 * 1000 * 1000; -const ROWS_DEFAULT: u16 = 0; -const COLS_DEFAULT: u16 = 0; -const EMERG_WR_DEFAULT: u32 = 0; -const VIRTIO_CONSOLE_F_SIZE: u64 = 0; -const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 1; -const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 2; -const BUFFER_LEN: usize = 96; - -// Default 31 serial ports. -const DEFAULT_SERIAL_VIRTQUEUES: usize = 64; - -const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6; - -#[repr(C)] -#[derive(Copy, Clone, Debug, Default)] -struct VirtioConsoleControl { - // Port number. - id: u32, - // The kind of control event. - event: u16, - // Extra information for event. - value: u16, -} - -impl VirtioConsoleControl { - fn new(id: u32, event: u16, value: u16) -> VirtioConsoleControl { - VirtioConsoleControl { id, event, value } - } -} - -impl ByteCode for VirtioConsoleControl {} - -fn console_setup( - console: Rc>, - test_state: Rc>, - alloc: Rc>, -) -> Vec>> { - let features = console.borrow().get_device_features(); - let vqs = - console - .borrow_mut() - .init_device(test_state, alloc, features, DEFAULT_SERIAL_VIRTQUEUES); - vqs -} - -fn verify_output_data(test_state: Rc>, addr: u64, len: u32, test_data: &String) { - let mut data_buf: Vec = Vec::with_capacity(len.try_into().unwrap()); - data_buf.append( - test_state - .borrow() - .memread(addr, len.try_into().unwrap()) - .as_mut(), - ); - let data = String::from_utf8(data_buf).unwrap(); - assert_eq!(data, *test_data); -} - -fn verify_input_data(input: &mut dyn Read, test_data: &String) { - let mut buffer = [0; BUFFER_LEN]; - match input.read(&mut buffer[0..test_data.len()]) { - Ok(size) => { - let response = String::from_utf8_lossy(&buffer[0..size]).to_string(); - assert_eq!(response, *test_data); - } - Err(e) => assert!(false, "Failed to read contents from socket: {}", e), - } -} - -fn get_pty_path(test_state: Rc>) -> String { - let ret = test_state.borrow().qmp("{\"execute\": \"query-chardev\"}"); - if (*ret.get("return").unwrap()).as_array().unwrap().len() != 0 - && (*ret.get("return").unwrap())[0].get("filename").is_some() - { - let filename = (*ret.get("return").unwrap())[0] - .get("filename") - .unwrap() - .to_string() - .replace('"', ""); - let mut file_path: Vec<&str> = filename.split("pty:").collect(); - return file_path.pop().unwrap().to_string(); - } else { - return String::from(""); - } -} - -// Send control message by output control queue. -fn out_control_event( - test_state: Rc>, - alloc: Rc>, - console: Rc>, - ctrl_msg: VirtioConsoleControl, - vqs: &Vec>>, -) { - let out_control_queue = vqs[3].clone(); - - let addr = alloc - .borrow_mut() - .alloc(size_of::() as u64); - test_state.borrow().memwrite(addr, ctrl_msg.as_bytes()); - let _ = out_control_queue.borrow_mut().add( - test_state.clone(), - addr, - size_of::() as u32, - false, - ); - - console - .borrow() - .kick_virtqueue(test_state.clone(), out_control_queue.clone()); -} - -fn verify_pty_io( - test_state: Rc>, - alloc: Rc>, - console: Rc>, -) { - let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); - let input_queue = vqs[0].clone(); - let output_queue = vqs[1].clone(); - - let pty_path = get_pty_path(test_state.clone()); - assert_ne!(pty_path, String::from("")); - - // Connect Guest. - let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); - out_control_event( - test_state.clone(), - alloc.clone(), - console.clone(), - open_msg, - &vqs, - ); - - // Connect Host. - let mut input: Option = None; - match File::open(&pty_path) { - Ok(file) => input = Some(file), - Err(e) => assert!(false, "{}", e), - } - - let test_data = String::from("Test\n"); - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite(addr, test_data.as_bytes()); - let free_head = output_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - false, - ); - - console - .borrow() - .kick_virtqueue(test_state.clone(), output_queue.clone()); - console.borrow().poll_used_elem( - test_state.clone(), - output_queue.clone(), - free_head, - TIMEOUT_US, - &mut None, - false, - ); - - verify_input_data(&mut input.unwrap(), &test_data); - - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - let free_head = input_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - true, - ); - console - .borrow() - .kick_virtqueue(test_state.clone(), input_queue.clone()); - - let mut output: Option = None; - match File::create(&pty_path) { - Ok(file) => output = Some(file), - Err(e) => assert!(false, "{}", e), - } - match output.unwrap().write(&test_data.as_bytes()) { - Ok(_num) => { - let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_micros(TIMEOUT_US); - loop { - let mut len: Option = Some(0); - console.borrow().poll_used_elem( - test_state.clone(), - input_queue.clone(), - free_head, - TIMEOUT_US, - &mut len, - false, - ); - if len.unwrap() != 0 { - verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); - break; - } - assert!(time::Instant::now() - start_time < timeout_us); - } - } - Err(e) => assert!(false, "Failed to write contents to socket: {}", e), - } - - console.borrow_mut().destroy_device(alloc, vqs); -} - -#[test] -fn console_rw_conifg() { - let chardev = ChardevType::Pty; - let pci_slot = 0x04; - let pci_fn = 0x0; - let (console, test_state, alloc) = create_console(chardev, pci_slot, pci_fn); - - assert_eq!( - console.borrow().config_readw(0), - ROWS_DEFAULT, - "The rows of the console config is incorrect or the testcase parament is out of date!" - ); - - assert_eq!( - console.borrow().config_readw(2), - COLS_DEFAULT, - "The cols of the console config is incorrect or the testcase parament is out of date!" - ); - - assert_eq!( - console.borrow().config_readl(8), - EMERG_WR_DEFAULT, - "The emerg_wr of the console config is incorrect or the testcase parament is out of date!" - ); - - console.borrow().config_writew(0, 1); - assert_eq!( - console.borrow().config_readw(0), - ROWS_DEFAULT, - "The console device doesn't support writing config. But config was written!" - ); - - verify_pty_io(test_state.clone(), alloc.clone(), console.clone()); - - test_state.borrow_mut().stop(); -} - -#[test] -fn console_features_negotiate() { - let chardev = ChardevType::Pty; - let pci_slot = 0x04; - let pci_fn = 0x0; - let (console, test_state, alloc) = create_console(chardev, pci_slot, pci_fn); - - let mut features = console.borrow().get_device_features(); - features |= 1 << VIRTIO_CONSOLE_F_SIZE | 1 << VIRTIO_CONSOLE_F_MULTIPORT; - console.borrow_mut().negotiate_features(features); - console.borrow_mut().set_features_ok(); - assert_eq!(features, console.borrow_mut().get_guest_features()); - - let unsupported_features = 1 << VIRTIO_CONSOLE_F_EMERG_WRITE; - features |= unsupported_features; - console.borrow_mut().negotiate_features(features); - console.borrow_mut().set_features_ok(); - assert_ne!(features, console.borrow_mut().get_guest_features()); - assert_eq!( - unsupported_features & console.borrow_mut().get_guest_features(), - 0 - ); - - verify_pty_io(test_state.clone(), alloc.clone(), console.clone()); - - test_state.borrow_mut().stop(); -} - -#[test] -fn console_pty_basic() { - let pty = ChardevType::Pty; - let pci_slot = 0x04; - let pci_fn = 0x0; - let (console, test_state, alloc) = create_console(pty, pci_slot, pci_fn); - - verify_pty_io(test_state.clone(), alloc.clone(), console.clone()); - - test_state.borrow_mut().stop(); -} - -#[test] -fn console_socket_basic() { - let socket_path = "/tmp/test-console0.sock"; - if Path::new(socket_path).exists() { - fs::remove_file(socket_path).unwrap(); - } - let socket = ChardevType::Socket { - path: String::from(socket_path), - server: true, - nowait: true, - }; - - let pci_slot = 0x4; - let pci_fn = 0x0; - let (console, test_state, alloc) = create_console(socket, pci_slot, pci_fn); - - let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); - let input_queue = vqs[0].clone(); - let output_queue = vqs[1].clone(); - - // Connect Guest. - let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); - out_control_event( - test_state.clone(), - alloc.clone(), - console.clone(), - open_msg, - &vqs, - ); - - let mut stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); - stream - .set_nonblocking(true) - .expect("Couldn't set nonblocking"); - - let test_data = String::from("Test\n"); - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite(addr, test_data.as_bytes()); - let free_head = output_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - false, - ); - - console - .borrow() - .kick_virtqueue(test_state.clone(), output_queue.clone()); - console.borrow().poll_used_elem( - test_state.clone(), - output_queue.clone(), - free_head, - TIMEOUT_US, - &mut None, - false, - ); - - verify_input_data(&mut stream, &test_data); - - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - let free_head = input_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - true, - ); - console - .borrow() - .kick_virtqueue(test_state.clone(), input_queue.clone()); - - match stream.write(&test_data.as_bytes()) { - Ok(_num) => { - let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_micros(TIMEOUT_US); - loop { - let mut len: Option = Some(0); - console.borrow().poll_used_elem( - test_state.clone(), - input_queue.clone(), - free_head, - TIMEOUT_US, - &mut len, - false, - ); - if len.unwrap() != 0 { - verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); - break; - } - assert!(time::Instant::now() - start_time < timeout_us); - } - } - Err(e) => assert!(false, "Failed to write contents to socket: {}", e), - } - - stream - .shutdown(Shutdown::Both) - .expect("shutdown function failed"); - console.borrow_mut().destroy_device(alloc, vqs); - test_state.borrow_mut().stop(); -} - -#[test] -fn console_parallel_req() { - let socket_path = "/tmp/test-console1.sock"; - if Path::new(socket_path).exists() { - fs::remove_file(socket_path).unwrap(); - } - let socket = ChardevType::Socket { - path: String::from(socket_path), - server: true, - nowait: true, - }; - - let pci_slot = 0x4; - let pci_fn = 0x0; - let (console, test_state, alloc) = create_console(socket, pci_slot, pci_fn); - - let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); - let input_queue = vqs[0].clone(); - let output_queue = vqs[1].clone(); - - // Connect Guest. - let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); - out_control_event( - test_state.clone(), - alloc.clone(), - console.clone(), - open_msg, - &vqs, - ); - - let mut stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); - stream - .set_nonblocking(true) - .expect("Couldn't set nonblocking"); - - let test_data = String::from("Test\n"); - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite(addr, test_data.as_bytes()); - let free_head = output_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - false, - ); - - console - .borrow() - .kick_virtqueue(test_state.clone(), output_queue.clone()); - let mut len: Option = Some(0); - console.borrow().poll_used_elem( - test_state.clone(), - output_queue.clone(), - free_head, - TIMEOUT_US, - &mut len, - false, - ); - - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - let free_head = input_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - true, - ); - console - .borrow() - .kick_virtqueue(test_state.clone(), input_queue.clone()); - - verify_input_data(&mut stream, &test_data); - - match stream.write(&test_data.as_bytes()) { - Ok(_num) => { - let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_micros(TIMEOUT_US); - loop { - let mut len: Option = Some(0); - console.borrow().poll_used_elem( - test_state.clone(), - input_queue.clone(), - free_head, - TIMEOUT_US, - &mut len, - false, - ); - if len.unwrap() != 0 { - verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); - break; - } - assert!(time::Instant::now() - start_time < timeout_us); - } - } - Err(e) => assert!(false, "Failed to write contents to socket: {}", e), - } - - let ret = test_state.borrow().qmp("{\"execute\": \"system_reset\"}"); - assert_eq!(*ret.get("return").unwrap(), json!({})); - - console.borrow_mut().init(pci_slot, pci_fn); - - let vqs = console_setup(console.clone(), test_state.clone(), alloc.clone()); - let input_queue = vqs[0].clone(); - let output_queue = vqs[1].clone(); - - let open_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_OPEN, 1); - out_control_event( - test_state.clone(), - alloc.clone(), - console.clone(), - open_msg, - &vqs, - ); - - let test_data = String::from("Test\n"); - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - test_state.borrow().memwrite(addr, test_data.as_bytes()); - let free_head = output_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - false, - ); - - console - .borrow() - .kick_virtqueue(test_state.clone(), output_queue.clone()); - console.borrow().poll_used_elem( - test_state.clone(), - output_queue.clone(), - free_head, - TIMEOUT_US, - &mut None, - false, - ); - - verify_input_data(&mut stream, &test_data); - - let addr = alloc.borrow_mut().alloc(test_data.len() as u64); - let free_head = input_queue.borrow_mut().add( - test_state.clone(), - addr, - test_data.len().try_into().unwrap(), - true, - ); - console - .borrow() - .kick_virtqueue(test_state.clone(), input_queue.clone()); - - match stream.write(&test_data.as_bytes()) { - Ok(_num) => { - let start_time = time::Instant::now(); - let timeout_us = time::Duration::from_micros(TIMEOUT_US); - loop { - let mut len: Option = Some(0); - console.borrow().poll_used_elem( - test_state.clone(), - input_queue.clone(), - free_head, - TIMEOUT_US, - &mut len, - false, - ); - if len.unwrap() != 0 { - verify_output_data(test_state.clone(), addr, len.unwrap(), &test_data); - break; - } - assert!(time::Instant::now() - start_time < timeout_us); - } - } - Err(e) => assert!(false, "Failed to write contents to socket: {}", e), - } - - stream - .shutdown(Shutdown::Both) - .expect("shutdown function failed"); - console.borrow_mut().destroy_device(alloc, vqs); - test_state.borrow_mut().stop(); -} diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs new file mode 100644 index 000000000..6a69f477d --- /dev/null +++ b/tests/mod_test/tests/serial_test.rs @@ -0,0 +1,795 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::mem::size_of; +use std::net::Shutdown; +use std::os::unix::net::UnixStream; +use std::path::Path; +use std::rc::Rc; +use std::{thread, time}; + +use byteorder::{ByteOrder, LittleEndian}; + +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps, VIRTIO_CONFIG_S_NEEDS_RESET}; +use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; +use mod_test::libtest::{test_init, TestState}; +use util::byte_code::ByteCode; + +const TIMEOUT_US: u64 = 15 * 1000 * 1000; +const ROWS_DEFAULT: u16 = 0; +const COLS_DEFAULT: u16 = 0; +const EMERG_WR_DEFAULT: u32 = 0; +// Default 31 serial ports. +const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31; +const BUFFER_LEN: usize = 96; +// Each port has 2 virtqueues and there exist 2 control virtqueues. +const DEFAULT_SERIAL_VIRTQUEUES: usize = DEFAULT_SERIAL_PORTS_NUMBER as usize * 2 + 2; + +const VIRTIO_CONSOLE_F_SIZE: u64 = 0; +const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 1; +const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 2; + +const VIRTIO_CONSOLE_DEVICE_READY: u16 = 0; +const VIRTIO_CONSOLE_PORT_ADD: u16 = 1; +const VIRTIO_CONSOLE_PORT_READY: u16 = 3; +const VIRTIO_CONSOLE_CONSOLE_PORT: u16 = 4; +const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6; +const VIRTIO_CONSOLE_PORT_NAME: u16 = 7; + +const IN_CONTROL_QUEUE_ID: usize = 2; +const OUT_CONTROL_QUEUE_ID: usize = 3; + +#[derive(Clone)] +enum ChardevType { + Pty, + Socket { + path: String, + server: bool, + nowait: bool, + }, +} + +#[derive(Clone)] +struct PortConfig { + chardev_type: ChardevType, + nr: u8, + is_console: bool, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +struct VirtioConsoleControl { + // Port number. + id: u32, + // The kind of control event. + event: u16, + // Extra information for event. + value: u16, +} + +impl VirtioConsoleControl { + fn new(id: u32, event: u16, value: u16) -> Self { + VirtioConsoleControl { id, event, value } + } +} + +impl ByteCode for VirtioConsoleControl {} + +struct SerialTest { + pub serial: Rc>, + pub state: Rc>, + pub alloc: Rc>, + vqs: Vec>>, + ports: HashMap, +} + +impl SerialTest { + fn virtqueue_setup(&mut self, num_queues: usize) { + let features = self.serial.borrow().get_device_features(); + self.vqs = self.serial.borrow_mut().init_device( + self.state.clone(), + self.alloc.clone(), + features, + num_queues, + ); + } + + fn get_pty_path(&mut self) -> String { + let ret = self.state.borrow().qmp("{\"execute\": \"query-chardev\"}"); + if (*ret.get("return").unwrap()).as_array().unwrap().len() != 0 + && (*ret.get("return").unwrap())[0].get("filename").is_some() + { + let filename = (*ret.get("return").unwrap())[0] + .get("filename") + .unwrap() + .to_string() + .replace('"', ""); + let mut file_path: Vec<&str> = filename.split("pty:").collect(); + return file_path.pop().unwrap().to_string(); + } else { + return String::from(""); + } + } + + // Send control message by output control queue. + fn out_control_event(&mut self, ctrl_msg: VirtioConsoleControl) { + self.virtqueue_add_element( + OUT_CONTROL_QUEUE_ID, + Some(ctrl_msg.as_bytes()), + size_of::() as u64, + ); + } + + fn virtqueue_add_element( + &mut self, + queue_id: usize, + data: Option<&[u8]>, + buffer_len: u64, + ) -> (u64, u32) { + let queue = self.vqs[queue_id].clone(); + let addr = self.alloc.borrow_mut().alloc(buffer_len); + let mut write = true; + + if let Some(buffer) = data { + self.state.borrow().memwrite(addr, buffer); + write = false; + } + let free_head = queue + .borrow_mut() + .add(self.state.clone(), addr, buffer_len as u32, write); + + self.serial + .borrow() + .kick_virtqueue(self.state.clone(), queue.clone()); + + (addr, free_head) + } + + // Fill a batch of buffers elements in queues[$queue_id]. + fn fill_buffer_in_vq(&mut self, queue_id: usize) -> (Vec, Vec) { + // Note: limited by the MST framework, we only allocate 32 * 1K sized buffers. It's enough for test. + let mut buf_addrs = Vec::with_capacity(32); + let mut free_heads = Vec::with_capacity(32); + for _ in 0..32 { + let (buf_addr, free_head) = self.virtqueue_add_element(queue_id, None, 1024); + buf_addrs.push(buf_addr); + free_heads.push(free_head); + } + + (free_heads, buf_addrs) + } + + // Init serial device. + fn serial_init(&mut self) { + let control_msg_len = size_of::(); + let mut in_msg = 0; + + // Init virtqueues. + self.virtqueue_setup(DEFAULT_SERIAL_VIRTQUEUES); + + // Prepare control input buffer. + let (free_heads, control_outs) = self.fill_buffer_in_vq(IN_CONTROL_QUEUE_ID); + + // Device ready. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 1); + self.out_control_event(ready_msg); + + // Port add. + self.serial.borrow().poll_used_elem( + self.state.clone(), + self.vqs[IN_CONTROL_QUEUE_ID].clone(), + free_heads[in_msg], + TIMEOUT_US, + &mut None, + true, + ); + for _ in self.ports.iter() { + let in_control_msg = self + .state + .borrow() + .memread(control_outs[in_msg], control_msg_len as u64); + in_msg += 1; + assert_eq!( + LittleEndian::read_u16(&in_control_msg[4..6]), + VIRTIO_CONSOLE_PORT_ADD + ); + assert_eq!(LittleEndian::read_u16(&in_control_msg[6..8]), 1); + } + + // Port Ready. + for port in self.ports.clone().iter() { + let ready_msg = VirtioConsoleControl::new(*port.0 as u32, VIRTIO_CONSOLE_PORT_READY, 1); + self.out_control_event(ready_msg); + + // If it's a console port. + if *port.1 { + let in_control_msg = self + .state + .borrow() + .memread(control_outs[in_msg], control_msg_len as u64); + in_msg += 1; + assert_eq!( + LittleEndian::read_u16(&in_control_msg[4..6]), + VIRTIO_CONSOLE_CONSOLE_PORT + ); + assert_eq!(LittleEndian::read_u16(&in_control_msg[6..8]), 1); + } + + // Port name. + let in_control_msg = self + .state + .borrow() + .memread(control_outs[in_msg], control_msg_len as u64); + in_msg += 1; + assert_eq!( + LittleEndian::read_u16(&in_control_msg[4..6]), + VIRTIO_CONSOLE_PORT_NAME + ); + assert_eq!(LittleEndian::read_u16(&in_control_msg[6..8]), 1); + + // Virtconsole is default host connected. + if *port.1 { + let in_control_msg = self + .state + .borrow() + .memread(control_outs[in_msg], control_msg_len as u64); + in_msg += 1; + assert_eq!( + LittleEndian::read_u16(&in_control_msg[4..6]), + VIRTIO_CONSOLE_PORT_OPEN + ); + assert_eq!(LittleEndian::read_u16(&in_control_msg[6..8]), 1); + + // driver -> device: port open. + let open_msg: VirtioConsoleControl = + VirtioConsoleControl::new(*port.0 as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); + self.out_control_event(open_msg); + } + } + } + + fn connect_pty_host(&mut self, new: bool) -> Option { + let pty_path = self.get_pty_path(); + assert_ne!(pty_path, String::from("")); + + let pty = match new { + true => File::create(&pty_path), + false => File::open(&pty_path), + }; + + // Connect pty host. + let mut host: Option = None; + match pty { + Ok(file) => host = Some(file), + Err(e) => assert!(false, "{}", e), + } + + host + } + + fn connect_socket_host(&mut self, socket_path: &str) -> Option { + let stream = UnixStream::connect(socket_path).expect("Couldn't connect socket"); + stream + .set_nonblocking(true) + .expect("Couldn't set nonblocking"); + + Some(stream) + } + + fn verify_port_io(&mut self, port: PortConfig) { + // queue[2]: control receiveq(host to guest). + // queue[3]: control transmitq(guest to host). + let input_queue_id = match port.nr { + 0 => 0, + _ => 2 * port.nr + 2, + } as usize; + let output_queue_id = input_queue_id + 1; + + let mut stream = None; + let mut host = None; + + // Connect Host. + match port.chardev_type { + ChardevType::Pty => { + host = self.connect_pty_host(false); + } + ChardevType::Socket { + ref path, + server: _, + nowait: _, + } => { + stream = self.connect_socket_host(&path); + } + } + + // Connect Guest. + // driver -> device: port open. + let open_msg: VirtioConsoleControl = + VirtioConsoleControl::new(port.nr as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); + self.out_control_event(open_msg); + + // IO: Guest -> Host. + let test_data = String::from("Test\n"); + let (_, free_head) = self.virtqueue_add_element( + output_queue_id, + Some(test_data.as_bytes()), + test_data.len() as u64, + ); + self.serial.borrow().poll_used_elem( + self.state.clone(), + self.vqs[output_queue_id].clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + match port.chardev_type { + ChardevType::Pty => { + verify_input_data(&mut host.unwrap(), &test_data); + } + _ => { + verify_input_data(&mut stream.as_ref().unwrap(), &test_data); + } + } + + // IO: Host -> Guest. + let (addr, free_head) = + self.virtqueue_add_element(input_queue_id, None, test_data.len() as u64); + let result = match port.chardev_type { + ChardevType::Pty => { + let output = self.connect_pty_host(true); + output.unwrap().write(&test_data.as_bytes()) + } + _ => stream.as_ref().unwrap().write(&test_data.as_bytes()), + }; + match result { + Ok(_num) => { + let start_time = time::Instant::now(); + let timeout_us = time::Duration::from_micros(TIMEOUT_US); + loop { + let mut len: Option = Some(0); + self.serial.borrow().poll_used_elem( + self.state.clone(), + self.vqs[input_queue_id].clone(), + free_head, + TIMEOUT_US, + &mut len, + false, + ); + if len.unwrap() != 0 { + verify_output_data(self.state.clone(), addr, len.unwrap(), &test_data); + break; + } + assert!(time::Instant::now() - start_time < timeout_us); + } + } + Err(e) => assert!(false, "Failed to write contents to socket: {}", e), + } + + // Clean. + match port.chardev_type { + ChardevType::Pty => {} + _ => stream + .unwrap() + .shutdown(Shutdown::Both) + .expect("shutdown function failed"), + }; + } + + fn test_end(&mut self) { + self.serial + .borrow_mut() + .destroy_device(self.alloc.clone(), self.vqs.clone()); + + self.state.borrow_mut().stop(); + } +} + +fn create_serial(ports_config: Vec, pci_slot: u8, pci_fn: u8) -> SerialTest { + let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let serial_pci_args = format!( + "-device {},id=serial0,bus=pcie.0,addr={}.0", + "virtio-serial-pci", pci_slot + ); + let mut ports = HashMap::new(); + args.append(&mut serial_pci_args[..].split(' ').collect()); + + let mut ports_args = String::new(); + for port in ports_config { + let chardev_args = match port.chardev_type { + ChardevType::Pty => format!("-chardev pty,id=charserial{}", port.nr), + ChardevType::Socket { + path, + server, + nowait, + } => { + let mut args = format!("-chardev socket,id=charserial{},path={}", port.nr, path); + if server { + args.push_str(",server") + } + if nowait { + args.push_str(",nowait") + } + args + } + }; + ports_args.push_str(&chardev_args); + + let device_type = match port.is_console { + true => "virtconsole", + false => "virtserialport", + }; + let port_args = format!( + " -device {},chardev=charserial{},id=serialport{},nr={} ", + device_type, port.nr, port.nr, port.nr + ); + ports_args.push_str(&port_args); + ports.insert(port.nr, port.is_console); + } + args.append(&mut ports_args.trim().split(' ').collect()); + + let state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(state.clone()); + let alloc = machine.allocator.clone(); + let serial = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); + serial.borrow_mut().init(pci_slot, pci_fn); + + SerialTest { + serial, + state, + alloc, + vqs: Vec::new(), + ports, + } +} + +fn verify_output_data(test_state: Rc>, addr: u64, len: u32, test_data: &String) { + let mut data_buf: Vec = Vec::with_capacity(len.try_into().unwrap()); + data_buf.append( + test_state + .borrow() + .memread(addr, len.try_into().unwrap()) + .as_mut(), + ); + let data = String::from_utf8(data_buf).unwrap(); + assert_eq!(data, *test_data); +} + +fn verify_input_data(input: &mut dyn Read, test_data: &String) { + let mut buffer = [0; BUFFER_LEN]; + match input.read(&mut buffer[0..test_data.len()]) { + Ok(size) => { + let response = String::from_utf8_lossy(&buffer[0..size]).to_string(); + assert_eq!(response, *test_data); + } + Err(e) => assert!(false, "Failed to read contents from socket: {}", e), + } +} + +/// Virtio serial pci device config space operation. +/// TestStep: +/// 1. Init virtio serial device(1 virtconsole, pty backend chardev). +/// 2. Read/write pci device config space. +/// 3. IO function test. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn serial_config_rw_conifg() { + let port = PortConfig { + chardev_type: ChardevType::Pty, + nr: 0, + is_console: true, + }; + let pci_slot = 0x04; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + // Cross boundary reading. Stratovirt should not be abnormal. + st.serial.borrow().config_readl(32); + + // Read normally. + assert_eq!( + st.serial.borrow().config_readw(0), + ROWS_DEFAULT, + "The rows of the serial config is incorrect or the testcase parament is out of date!" + ); + + assert_eq!( + st.serial.borrow().config_readw(2), + COLS_DEFAULT, + "The cols of the serial config is incorrect or the testcase parament is out of date!" + ); + + assert_eq!( + st.serial.borrow().config_readl(4), + DEFAULT_SERIAL_PORTS_NUMBER, + "The max_nr_ports of the serial config is incorrect or the testcase parament is out of date!" + ); + + assert_eq!( + st.serial.borrow().config_readl(8), + EMERG_WR_DEFAULT, + "The emerg_wr of the serial config is incorrect or the testcase parament is out of date!" + ); + + // Write config. + st.serial.borrow().config_writew(0, 1); + assert_eq!( + st.serial.borrow().config_readw(0), + ROWS_DEFAULT, + "The serial device doesn't support writing config. But config was written!" + ); + + st.serial_init(); + st.verify_port_io(port); + st.test_end(); +} + +/// Virtio serial pci device features negotiate operation. +/// TestStep: +/// 1. Init virtio serial device(1 virtconsole, pty backend chardev). +/// 2. Negotiate supported features(VIRTIO_CONSOLE_F_SIZE/VIRTIO_CONSOLE_F_MULTIPORT). +/// 3. Negotiate unsupported feature(VIRTIO_CONSOLE_F_EMERG_WRITE). +/// 4. IO function test. +/// 5. Destroy device. +/// Expect: +/// 1/2/4/5: success. +/// 3: unsupported feature can't be negotiated. +#[test] +fn serial_features_negotiate() { + let port = PortConfig { + chardev_type: ChardevType::Pty, + nr: 0, + is_console: true, + }; + let pci_slot = 0x04; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + let mut features = st.serial.borrow().get_device_features(); + features |= 1 << VIRTIO_CONSOLE_F_SIZE | 1 << VIRTIO_CONSOLE_F_MULTIPORT; + st.serial.borrow_mut().negotiate_features(features); + st.serial.borrow_mut().set_features_ok(); + assert_eq!(features, st.serial.borrow_mut().get_guest_features()); + + let unsupported_features = 1 << VIRTIO_CONSOLE_F_EMERG_WRITE; + features |= unsupported_features; + st.serial.borrow_mut().negotiate_features(features); + st.serial.borrow_mut().set_features_ok(); + assert_ne!(features, st.serial.borrow_mut().get_guest_features()); + assert_eq!( + unsupported_features & st.serial.borrow_mut().get_guest_features(), + 0 + ); + + st.serial_init(); + st.verify_port_io(port); + st.test_end(); +} + +/// Virtio serial pci device basic function(socket backend chardev). +/// TestStep: +/// 1. Init virtio serial device(1 virtserialport, socket backend chardev). +/// 2. IO function test. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtserialport_socket_basic() { + let socket_path = "/tmp/test-virtserialport0.sock"; + if Path::new(socket_path).exists() { + fs::remove_file(socket_path).unwrap(); + } + let socket = ChardevType::Socket { + path: String::from(socket_path), + server: true, + nowait: true, + }; + let port = PortConfig { + chardev_type: socket.clone(), + nr: 1, + is_console: false, + }; + + let pci_slot = 0x4; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + st.serial_init(); + st.verify_port_io(port); + st.test_end(); +} + +/// Virtio serial pci device basic function(pty backend chardev). +/// TestStep: +/// 1. Init virtio serial device(1 virtserialport, pty backend chardev). +/// 2. IO function test. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn virtserialport_pty_basic() { + let port = PortConfig { + chardev_type: ChardevType::Pty, + nr: 0, + is_console: false, + }; + let pci_slot = 0x04; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + st.serial_init(); + st.verify_port_io(port); + st.test_end(); +} + +/// Virtio serial pci device error control message test. +/// TestStep: +/// 1. Init virtio serial device(1 virtconsole, pty backend chardev). +/// 2. Send out control message which has invalid event. +/// 3. Send out control message which has non-existed port id. +/// 4. Send out control message which size is illegal. +/// 5. Destroy device. +/// Expect: +/// 1/5: success. +/// 2/3: Just discard this invalid msg. Nothing happend. +/// 4: report virtio error. +#[test] +fn virtconsole_pty_err_out_control_msg() { + let nr = 0; + let port = PortConfig { + chardev_type: ChardevType::Pty, + nr, + is_console: true, + }; + let pci_slot = 0x04; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + st.serial_init(); + + // Error out control msg which has invalid event. Just discard this invalid msg. Nothing happend. + let invalid_event_msg = VirtioConsoleControl::new(nr as u32, VIRTIO_CONSOLE_PORT_NAME, 1); + st.out_control_event(invalid_event_msg); + + // Error out control msg which has non-existed port id. Just discard this invalid msg. Nothing happend. + let invalid_event_msg = VirtioConsoleControl::new((nr + 5) as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); + st.out_control_event(invalid_event_msg); + + // Error out control msg which size is illegal. + let error_control_msg = vec![0]; + st.virtqueue_add_element(OUT_CONTROL_QUEUE_ID, Some(&error_control_msg), 1); + + thread::sleep(time::Duration::from_secs(1)); + assert!(st.serial.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET != 0); + + // Send a random control message. Check stratovirt is working. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 1); + st.out_control_event(ready_msg); + + st.test_end(); +} + +/// Virtio serial pci device invalid input control message buffer test. +/// TestStep: +/// 1. Init virtio serial device(1 virtconsole, pty backend chardev). +/// 2. Don't provide buffer in input_control_queue. Send a message which should response in input_control_queue. +/// 3. Provide 1 byte buffer in input_control_queue. Send a message which should response in input_control_queue. +/// 4. Destroy device. +/// Expect: +/// 1/4: success. +/// 2: Just discard this invalid msg. Nothing happend. +/// 3: report virtio error. +#[test] +fn virtconsole_pty_invalid_in_control_buffer() { + let port = PortConfig { + chardev_type: ChardevType::Pty, + nr: 0, + is_console: true, + }; + let pci_slot = 0x04; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + // Init virtqueues. + st.virtqueue_setup(DEFAULT_SERIAL_VIRTQUEUES); + + // No buffer in input_control_queue. Will discard all requests sent by input_control_queue. Nothing else happened. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 1); + st.out_control_event(ready_msg); + + // Provide size_of::() buffer for input_control_queue. + st.virtqueue_add_element( + IN_CONTROL_QUEUE_ID, + None, + size_of::() as u64, + ); + + // Error control msg: Guest is not ready. It will do nothing. Buffer in input_control_queue will not be used. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 0); + st.out_control_event(ready_msg); + + // Should response VIRTIO_CONSOLE_PORT_ADD msg when guest is ready. Buffer will be used. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 1); + st.out_control_event(ready_msg); + + // Give only 1 byte for input control message which will result virtio error. + st.virtqueue_add_element(IN_CONTROL_QUEUE_ID, None, 1); + + // Error control msg: Port is not ready. It will do nothing. Buffer in input_control_queue will not be used. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_READY, 0); + st.out_control_event(ready_msg); + + // Console is default host connected. Should response VIRTIO_CONSOLE_CONSOLE_PORT msg. 1 byte Buffer will be used. + let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_READY, 1); + st.out_control_event(ready_msg); + + // Little buffer for VIRTIO_CONSOLE_CONSOLE_PORT message. + thread::sleep(time::Duration::from_secs(1)); + assert!(st.serial.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET != 0); + + st.test_end(); +} + +/// Virtio serial pci device IO test when host is not connected. +/// TestStep: +/// 1. Init virtio serial device(1 virtserialport, socket backend chardev, don't connect in host). +/// 2. IO test in this port. +/// 3. IO test in virtqueues which have no port. +/// 4. basic IO test.(port is connected) +/// 5. Destroy device. +/// Expect: +/// 1/4/5: success. +/// 2/3: Just discard these requests. Nothing happend. +#[test] +fn virtserialport_socket_not_connect() { + let nr = 1; + let socket_path = "/tmp/test-virtserialport1.sock"; + if Path::new(socket_path).exists() { + fs::remove_file(socket_path).unwrap(); + } + let socket = ChardevType::Socket { + path: String::from(socket_path), + server: true, + nowait: true, + }; + let port = PortConfig { + chardev_type: socket.clone(), + nr, + is_console: false, + }; + + let pci_slot = 0x4; + let pci_fn = 0x0; + let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + + st.serial_init(); + + // Requests will be discarded when host (port 1, output queue id: 5) is not connected. Nothing happend. + let test_data = String::from("Test\n"); + st.virtqueue_add_element(5, Some(test_data.as_bytes()), test_data.len() as u64); + + // Requests will be discarded when it is sent in virtqueue which has no port(port 2, output queue id: 7). Nothing happend. + let test_data = String::from("Test\n"); + st.virtqueue_add_element(7, Some(test_data.as_bytes()), test_data.len() as u64); + + // Virtio-serial is working normally after these steps. + st.verify_port_io(port); + st.test_end(); +} -- Gitee From 74e8e1637bd53dee8ec0144b2fd562d74e808e30 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 14 May 2023 18:07:00 +0800 Subject: [PATCH 1082/2187] Fix: Optimize a dangerous code Optimize a dangerous code. Signed-off-by: Xiao Ye --- pci/src/demo_device/gpu_device.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 227175237..96b41d855 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -137,7 +137,10 @@ impl DemoGpu { /// Change the pixels of the specified area in the image. pub fn update_image_area(&mut self, x: u32, y: u32, w: u32, h: u32) -> Result<()> { - let image = self.surface.unwrap().image; + let image = match self.surface { + Some(s) => s.image, + None => bail!("Surface is null"), + }; let image_ptr = get_image_data(image) as *mut u8; let stride = get_image_stride(image); for i in y..y + h { -- Gitee From c39e0c052ff40bd68bde040af5c40d6b88cc5211 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 14 May 2023 16:57:50 +0800 Subject: [PATCH 1083/2187] GTK: Optimize adaptive resolution feature of GPU 1. Optimize some code to reduce the number of mutex locking. 2. Add judgment condition before adaptive resolution of virio-gpu, if the current size is the same as the previous size, it will return. Signed-off-by: Xiao Ye --- ui/src/console.rs | 44 +++++++++++++++++++++++++++++--------------- ui/src/gtk/draw.rs | 5 ++--- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index 19942d3b1..e9441d7bf 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -64,6 +64,12 @@ pub enum VmRunningStage { Os, } +#[derive(Default)] +pub struct UiInfo { + pub last_width: u32, + pub last_height: u32, +} + /// Image data defined in display. #[derive(Clone, Copy)] pub struct DisplaySurface { @@ -171,6 +177,7 @@ pub struct DisplayConsole { pub con_type: ConsoleType, pub width: i32, pub height: i32, + pub ui_info: UiInfo, pub surface: Option, pub console_list: Weak>, pub dev_opts: Arc, @@ -192,6 +199,7 @@ impl DisplayConsole { con_type, width: 0, height: 0, + ui_info: UiInfo::default(), console_list, surface: None, dev_opts, @@ -497,25 +505,31 @@ pub fn graphic_hardware_update(con_id: Option) { } } -pub fn graphic_hardware_ui_info(con_id: Option, width: u32, height: u32) { - let console = CONSOLES.lock().unwrap().get_console_by_id(con_id); - - if let Some(con) = console { - let con_opts = con.lock().unwrap().dev_opts.clone(); - let con_clone = con.clone(); - let func = Box::new(move || { - (*con_opts).hw_ui_info(con_clone.clone(), width, height); - }); +pub fn graphic_hardware_ui_info( + con: Arc>, + width: u32, + height: u32, +) -> Result<()> { + let mut locked_con = con.lock().unwrap(); + if locked_con.ui_info.last_width == width && locked_con.ui_info.last_height == height { + return Ok(()); + } + locked_con.ui_info.last_width = width; + locked_con.ui_info.last_height = height; - if let Some(ctx) = EventLoop::get_ctx(None) { - let mut con_locked = con.lock().unwrap(); - if let Some(timer_id) = con_locked.timer_id { - ctx.timer_del(timer_id); - } + let clone_con = con.clone(); + let con_opts = locked_con.dev_opts.clone(); + let func = Box::new(move || { + (*con_opts).hw_ui_info(clone_con.clone(), width, height); + }); - con_locked.timer_id = Some(ctx.timer_add(func, Duration::from_millis(500))); + if let Some(ctx) = EventLoop::get_ctx(None) { + if let Some(timer_id) = locked_con.timer_id { + ctx.timer_del(timer_id); } + locked_con.timer_id = Some(ctx.timer_add(func, Duration::from_millis(500))); } + Ok(()) } /// Get the weak reference of all active consoles from the console lists. diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 6a3fc32a3..bcd83cb1d 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -108,11 +108,10 @@ fn da_configure_callback( Some(c) => c, None => return Ok(()), }; - let con_id = con.lock().unwrap().con_id; + drop(borrowed_gs); let (width, height) = event_configure.size(); - graphic_hardware_ui_info(Some(con_id), width, height); - Ok(()) + graphic_hardware_ui_info(con, width, height) } fn da_key_callback( -- Gitee From 6042ec20f507eee08d5640a9d566721ac630f879 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 14 May 2023 17:29:18 +0800 Subject: [PATCH 1084/2187] GTK: Disable adaptive resolution of gpu in non zoom fit mode Disable adaptive resolution of gpu in non zoom fit mode. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index bcd83cb1d..3ace3e9ea 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -104,6 +104,10 @@ fn da_configure_callback( event_configure: &gdk::EventConfigure, ) -> Result<()> { let borrowed_gs = gs.borrow(); + if !borrowed_gs.scale_mode.borrow().is_free_scale() { + return Ok(()); + } + let con = match borrowed_gs.con.upgrade() { Some(c) => c, None => return Ok(()), -- Gitee From 5319b2220448a941fad44103f63d05718376435e Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 11 May 2023 22:16:22 +0800 Subject: [PATCH 1085/2187] GTK: Set the text attribute. Set the text attribute to support multiple languages. Signed-off-by: Xiao Ye --- Cargo.lock | 84 ++++++++++++++++++++++++++++++++++++++++++++++ ui/Cargo.toml | 1 + ui/src/gtk/menu.rs | 25 ++++++++------ ui/src/gtk/mod.rs | 29 +++++++++++++--- 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffc3b3c04..cb0b904fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f80c754495ef07b4554134bbfb23fc1e31a775113b49acccbe35076d57cba145" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "boot_loader" version = "2.2.0" @@ -567,6 +573,26 @@ dependencies = [ "wasi", ] +[[package]] +name = "gettext-rs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49ea8a8fad198aaa1f9655a2524b64b70eb06b2f3ff37da407566c93054f364" +dependencies = [ + "gettext-sys", + "locale_config", +] + +[[package]] +name = "gettext-sys" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c63ce2e00f56a206778276704bbe38564c8695249fdc8f354b4ef71c57c3839d" +dependencies = [ + "cc", + "temp-dir", +] + [[package]] name = "gimli" version = "0.26.2" @@ -922,6 +948,19 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "locale_config" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -992,6 +1031,15 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1092,6 +1140,35 @@ dependencies = [ "autocfg", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.29.0" @@ -1665,6 +1742,12 @@ dependencies = [ "version-compare", ] +[[package]] +name = "temp-dir" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab" + [[package]] name = "termcolor" version = "1.2.0" @@ -1718,6 +1801,7 @@ version = "2.2.0" dependencies = [ "anyhow", "bitintr", + "gettext-rs", "gtk", "libc", "log", diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 0d10c3b7e..e1a731a66 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -20,5 +20,6 @@ rustls-pemfile = "1.0.0" sasl2-sys = "0.1.20" bitintr = "0.2.0" gtk = "0.14.3" +gettext-rs = "0.7.0" machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 4dffb5c6b..50b13f54d 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -13,6 +13,7 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::{bail, Result}; +use gettextrs::gettext; use gtk::{ gdk::{ self, @@ -71,16 +72,16 @@ impl GtkMenu { accel_group: AccelGroup::default(), menu_bar: MenuBar::new(), machine_menu: Menu::new(), - machine_item: MenuItem::with_label("Machine"), - shutdown_item: MenuItem::with_label("Shut Down"), + machine_item: MenuItem::with_mnemonic(&gettext("_Machine")), + shutdown_item: MenuItem::with_mnemonic(&gettext("Power _Down")), view_menu: Menu::new(), - view_item: MenuItem::with_label("View"), - full_screen_item: MenuItem::with_label("Full Screen"), - zoom_in_item: MenuItem::with_label("Zoom In"), - zoom_out_item: MenuItem::with_label("Zoom Out"), - zoom_fit: CheckMenuItem::with_label("Zoom Fit"), - best_fit_item: MenuItem::with_label("Best Fit"), - show_menu_bar: CheckMenuItem::with_label("Show MenuBar"), + view_item: MenuItem::with_mnemonic(&gettext("_View")), + full_screen_item: MenuItem::with_mnemonic(&gettext("_Fullscreen")), + zoom_in_item: MenuItem::with_mnemonic(&gettext("Zoom _In")), + zoom_out_item: MenuItem::with_mnemonic(&gettext("Zoom _Out")), + zoom_fit: CheckMenuItem::with_mnemonic(&gettext("Zoom To _Fit")), + best_fit_item: MenuItem::with_mnemonic(&gettext("Best _Fit")), + show_menu_bar: CheckMenuItem::with_mnemonic(&gettext("Show Menubar")), } } @@ -383,9 +384,11 @@ fn window_close_callback(gd: &Rc>) -> Result<()> { DialogFlags::DESTROY_WITH_PARENT, MessageType::Question, ButtonsType::YesNo, - "Forced shutdown may cause installation failure, blue screen, unusable and other abnormalities.", + &gettext("Forced shutdown may cause installation failure, blue screen, unusable and other abnormalities."), ); - dialog.set_title("Please confirm whether to exit the virtual machine"); + dialog.set_title(&gettext( + "Please confirm whether to exit the virtual machine", + )); let answer = dialog.run(); // SAFETY: Dialog is created in the current function and can be guaranteed not to be empty. unsafe { dialog.destroy() }; diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index cbc9ce08d..66c45270d 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -24,6 +24,7 @@ use std::{ }; use anyhow::{bail, Context, Result}; +use gettextrs::LocaleCategory; use gtk::{ cairo::{Format, ImageSurface}, gdk::{self, Geometry, Gravity, Screen, WindowHints}, @@ -61,6 +62,10 @@ const DEFAULT_WINDOW_WIDTH: i32 = 1024; const DEFAULT_WINDOW_HEIGHT: i32 = 768; pub(crate) const GTK_SCALE_MIN: f64 = 0.25; pub(crate) const GTK_ZOOM_STEP: f64 = 0.25; +/// Domain name. +const DOMAIN_NAME: &str = "desktop-app-engine"; +/// The path of message information is located. +const LOCALE_PATH: &str = "/usr/share/locale"; /// Gtk window display mode. #[derive(Clone, PartialEq)] @@ -487,6 +492,7 @@ fn create_gtk_thread(gtk_cfg: &GtkConfig) { .application_id("stratovirt.gtk") .build(); let gtk_cfg_clone = gtk_cfg.clone(); + application.connect_activate(move |app| build_ui(app, >k_cfg_clone)); application.run_with_args(>k_cfg.gtk_args); } @@ -500,10 +506,9 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { .default_height(DEFAULT_WINDOW_HEIGHT) .build(); - // Set app name if configured. - if let Some(name) = >k_cfg.app_name { - set_program_name(Some(name)); - } + set_program_attribute(gtk_cfg) + .with_context(|| "Failed to set properties for program") + .unwrap(); // Create menu. let mut gtk_menu = GtkMenu::new(window); @@ -520,6 +525,22 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { gtk_menu.show_window(scale_mode); } +fn set_program_attribute(gtk_cfg: &GtkConfig) -> Result<()> { + // Set the program name. + if let Some(name) = >k_cfg.app_name { + set_program_name(Some(name)); + } + + // Set text attributes for the program. + gettextrs::setlocale(LocaleCategory::LcMessages, ""); + gettextrs::setlocale(LocaleCategory::LcCType, "C.UTF-8"); + gettextrs::bindtextdomain(DOMAIN_NAME, LOCALE_PATH)?; + gettextrs::bind_textdomain_codeset(DOMAIN_NAME, "UTF-8")?; + gettextrs::textdomain(DOMAIN_NAME)?; + + Ok(()) +} + fn graphic_display_init(gd: Rc>) -> Result<()> { let console_list = get_active_console(); let mut borrowed_gd = gd.borrow_mut(); -- Gitee From 830c8dc8d61c05967a9ff6a22365a55083111f94 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 06:36:27 +0800 Subject: [PATCH 1086/2187] GTK: set show border to false Set show border to false. In some specific environments and state of full screen, it will cause different size of image and dreaing area. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 50b13f54d..9b255a660 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -230,7 +230,7 @@ impl GtkMenu { // Set the visible of note_book. self.note_book.set_show_tabs(false); - self.note_book.set_show_border(true); + self.note_book.set_show_border(false); self.window.add_accel_group(&self.accel_group); self.container.pack_start(&self.menu_bar, false, false, 0); -- Gitee From 8cc32255809fa1677a9d50c0a95eb1272d657b38 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 06:46:39 +0800 Subject: [PATCH 1087/2187] GTK: delete Screen widget. There is no need to determine the size of the Desktop in full screen mode, as the expected size of drawing area is the same as that of Desktop. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 6 ------ ui/src/gtk/mod.rs | 10 +--------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 9b255a660..e346bc001 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -16,7 +16,6 @@ use anyhow::{bail, Result}; use gettextrs::gettext; use gtk::{ gdk::{ - self, ffi::{GDK_KEY_equal, GDK_KEY_minus, GDK_KEY_B, GDK_KEY_F, GDK_KEY_M, GDK_KEY_S}, ModifierType, }, @@ -307,11 +306,6 @@ fn full_screen_callback(gd: &Rc>) -> Result<()> { gtk_menu.note_book.set_show_tabs(false); gtk_menu.menu_bar.hide(); gs.borrow().draw_area.set_size_request(-1, -1); - if let Some(screen) = gdk::Screen::default() { - let width = screen.width(); - let height = screen.height(); - gs.borrow().window.set_default_size(width, height); - } gtk_menu.window.fullscreen(); borrowed_scale.full_screen = true; } else { diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 66c45270d..105c53fe3 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -27,7 +27,7 @@ use anyhow::{bail, Context, Result}; use gettextrs::LocaleCategory; use gtk::{ cairo::{Format, ImageSurface}, - gdk::{self, Geometry, Gravity, Screen, WindowHints}, + gdk::{self, Geometry, Gravity, WindowHints}, gdk_pixbuf::Colorspace, glib::{self, set_program_name, Priority, SyncSender}, prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, @@ -398,14 +398,6 @@ impl GtkDisplayScreen { w_height = win.height() as f64; }; - if self.scale_mode.borrow().is_full_screen() { - // Get the size of desktop. - if let Some(screen) = Screen::default() { - w_width = screen.width() as f64; - w_height = screen.height() as f64; - } - } - if w_width.eq(&0.0) || w_height.eq(&0.0) { bail!("The window size can not be zero!"); } -- Gitee From 09f196220477a49a85aa77b9ad0bc3785e9f3048 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 06:47:03 +0800 Subject: [PATCH 1088/2187] GTK: delete floor in drawing Using floor may result in loss of accuracy. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 3ace3e9ea..4ea8bfc15 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -260,15 +260,15 @@ fn da_draw_callback(gs: &Rc>, cr: &cairo::Context) -> borrowed_gs.scale_y = scale_x.min(scale_y); } - surface_width = (surface_width * borrowed_gs.scale_x).floor(); - surface_height = (surface_height * borrowed_gs.scale_y).floor(); + surface_width *= borrowed_gs.scale_x; + surface_height *= borrowed_gs.scale_y; let mut mx: f64 = 0.0; let mut my: f64 = 0.0; - if window_width > surface_width { + if window_width.gt(&surface_width) { mx = (window_width - surface_width) / (2.0); } - if window_height > surface_height { + if window_height.gt(&surface_height) { my = (window_height - surface_height) / (2.0); } -- Gitee From 5a9205b1438366a05a6d5b1ffe62d8ac43b651fc Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 11:19:51 +0800 Subject: [PATCH 1089/2187] VNC: fix set_color_depth Delete excess information in set_color_depth. Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index b0e357122..2d596fb9a 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -1281,7 +1281,6 @@ pub fn set_color_depth(client: &Arc, buf: &mut Vec) { buf.append(&mut (0_u8).to_be_bytes().to_vec()); buf.append(&mut (1_u16).to_be_bytes().to_vec()); framebuffer_update(0, 0, client_width, client_height, ENCODING_WMVI, buf); - buf.append(&mut (ENCODING_RAW as u32).to_be_bytes().to_vec()); pixel_format_message(client, buf); } else if !locked_dpm.pf.is_default_pixel_format() { locked_dpm.convert = true; -- Gitee From c8de6b42421fd09502111c6e9d140fea7499962b Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 13:55:20 +0800 Subject: [PATCH 1090/2187] GTK: set title bar for window Set title bar for window. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 105c53fe3..e40948376 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -32,9 +32,9 @@ use gtk::{ glib::{self, set_program_name, Priority, SyncSender}, prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, traits::{ - CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt, + CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt, HeaderBarExt, }, - Application, ApplicationWindow, DrawingArea, RadioMenuItem, + Application, ApplicationWindow, DrawingArea, RadioMenuItem, HeaderBar, }; use log::{debug, error}; @@ -493,12 +493,11 @@ fn create_gtk_thread(gtk_cfg: &GtkConfig) { fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { let window = ApplicationWindow::builder() .application(app) - .title(>k_cfg.vm_name) .default_width(DEFAULT_WINDOW_WIDTH) .default_height(DEFAULT_WINDOW_HEIGHT) .build(); - set_program_attribute(gtk_cfg) + set_program_attribute(gtk_cfg, &window) .with_context(|| "Failed to set properties for program") .unwrap(); @@ -517,7 +516,13 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { gtk_menu.show_window(scale_mode); } -fn set_program_attribute(gtk_cfg: &GtkConfig) -> Result<()> { +fn set_program_attribute(gtk_cfg: &GtkConfig, window: &ApplicationWindow) -> Result<()> { + // Set title bar. + let header = HeaderBar::new(); + header.set_show_close_button(true); + header.set_title(Some(>k_cfg.vm_name)); + window.set_titlebar(Some(&header)); + // Set the program name. if let Some(name) = >k_cfg.app_name { set_program_name(Some(name)); -- Gitee From d2a1557fcab2f2cd3bbc2a58f2b0412b949bc822 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 16:46:53 +0800 Subject: [PATCH 1091/2187] GTK: Remove cursor Remove function update_cursor_display. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 7 ++----- ui/src/gtk/mod.rs | 19 +------------------ 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index e346bc001..13b4bbdb7 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -32,10 +32,7 @@ use log::error; use crate::{ console::{get_run_stage, VmRunningStage}, - gtk::{ - renew_image, update_cursor_display, update_window_size, GtkDisplay, ZoomOperate, - GTK_SCALE_MIN, GTK_ZOOM_STEP, - }, + gtk::{renew_image, update_window_size, GtkDisplay, ZoomOperate, GTK_SCALE_MIN, GTK_ZOOM_STEP}, }; use super::ScaleMode; @@ -320,7 +317,7 @@ fn full_screen_callback(gd: &Rc>) -> Result<()> { update_window_size(&gs)?; }; - update_cursor_display(&gs) + Ok(()) } /// Zoom in/out the display. diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 105c53fe3..7f0305e8e 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -617,8 +617,7 @@ fn gs_show_menu_callback( borrowed_gs.draw_area.grab_focus(); drop(borrowed_gs); - update_window_size(gs)?; - update_cursor_display(gs) + update_window_size(gs) } /// Refresh image. @@ -952,19 +951,3 @@ pub(crate) fn renew_image(gs: &Rc>) -> Result<()> { .queue_draw_area(0, 0, width as i32, height as i32); Ok(()) } - -/// Hide the external Cursor. -pub(crate) fn update_cursor_display(gs: &Rc>) -> Result<()> { - let borrowed_gs = gs.borrow(); - if !borrowed_gs.draw_area.is_realized() { - return Ok(()); - } - - if let Some(win) = borrowed_gs.draw_area.window() { - let dpy = borrowed_gs.window.display(); - let c = gdk::Cursor::for_display(&dpy, gdk::CursorType::BlankCursor); - win.set_cursor(Some(&c)); - } - - Ok(()) -} -- Gitee From 817737fee8611d1b2b77666950564da15ea64128 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 May 2023 15:00:02 +0800 Subject: [PATCH 1092/2187] GTK: Set gtk icons 1. The args[0] of application run set the program name, which supports configuring the program name of gtk in the command line. 2. Add function set_icon_name, which set the default name of icon picture in task bar. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index e40948376..f07533d38 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -29,12 +29,13 @@ use gtk::{ cairo::{Format, ImageSurface}, gdk::{self, Geometry, Gravity, WindowHints}, gdk_pixbuf::Colorspace, - glib::{self, set_program_name, Priority, SyncSender}, + glib::{self, Priority, SyncSender}, prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, traits::{ - CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, MenuShellExt, RadioMenuItemExt, WidgetExt, HeaderBarExt, + CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, HeaderBarExt, MenuShellExt, + RadioMenuItemExt, WidgetExt, }, - Application, ApplicationWindow, DrawingArea, RadioMenuItem, HeaderBar, + Application, ApplicationWindow, DrawingArea, HeaderBar, RadioMenuItem, }; use log::{debug, error}; @@ -463,13 +464,17 @@ struct GtkConfig { /// Gtk display init. pub fn gtk_display_init(ds_cfg: &DisplayConfig, ui_context: UiContext) -> Result<()> { + let mut gtk_args: Vec = vec![]; + if let Some(app_name) = &ds_cfg.app_name { + gtk_args.push(app_name.clone()); + } let gtk_cfg = GtkConfig { full_screen: ds_cfg.full_screen, app_name: ds_cfg.app_name.clone(), vm_name: ui_context.vm_name, powerdown_button: ui_context.power_button, shutdown_button: ui_context.shutdown_req, - gtk_args: vec![], + gtk_args, }; let _handle = thread::Builder::new() .name("gtk display".to_string()) @@ -523,9 +528,9 @@ fn set_program_attribute(gtk_cfg: &GtkConfig, window: &ApplicationWindow) -> Res header.set_title(Some(>k_cfg.vm_name)); window.set_titlebar(Some(&header)); - // Set the program name. - if let Some(name) = >k_cfg.app_name { - set_program_name(Some(name)); + // Set default icon. + if let Some(app_name) = >k_cfg.app_name { + window.set_icon_name(Some(app_name)); } // Set text attributes for the program. -- Gitee From a559fb7a55e8b5514837efc1e8ef799e085bc0cd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 15 May 2023 19:17:24 +0800 Subject: [PATCH 1093/2187] virtio_pci: Avoid locking common_cfg in interrupt_cb There is a AB deadlock situation(common_cfg lock and event_status lock): vCPU: write common_cfg(lock common_cfg) -> deactivate device(lock virito_device) -> rm_event(lock event_status). IO thread: execute event callback(lock event_status) -> execute iohandler(lock iohandler) -> send interrupt(lock common_cfg). Signed-off-by: Keqian Zhu --- virtio/src/transport/virtio_pci.rs | 121 ++++++++++++++++------------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 5420ee961..0df4b0127 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -12,7 +12,7 @@ use std::cmp::{max, min}; use std::mem::size_of; -use std::sync::atomic::{AtomicBool, AtomicU16, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU8, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; @@ -150,22 +150,22 @@ fn get_virtio_class_id(device_type: u32) -> u16 { } /// The configuration of virtio-pci device, the fields refer to Virtio Spec. -#[derive(Clone)] +#[derive(Default)] struct VirtioPciCommonConfig { /// Bitmask of the features supported by the device (host)(32 bits per set) features_select: u32, /// Device (host) feature-setting selector. acked_features_select: u32, /// Interrupt status. - interrupt_status: u32, + interrupt_status: Arc, /// Device status. - device_status: u32, + device_status: Arc, /// Configuration atomicity value. - config_generation: u8, + config_generation: Arc, /// Queue selector. queue_select: u16, /// The MSI-X vector for config change notification. - msix_config: u16, + msix_config: Arc, /// The configuration of queues. queues_config: Vec, /// The type of queue, split-vring or packed-vring. @@ -175,32 +175,27 @@ struct VirtioPciCommonConfig { impl VirtioPciCommonConfig { fn new(queue_size: u16, queue_num: usize) -> Self { VirtioPciCommonConfig { - features_select: 0, - acked_features_select: 0, - interrupt_status: 0, - device_status: 0, - config_generation: 0, - queue_select: 0, - msix_config: INVALID_VECTOR_NUM, + msix_config: Arc::new(AtomicU16::new(INVALID_VECTOR_NUM)), queues_config: vec![QueueConfig::new(queue_size); queue_num], queue_type: QUEUE_TYPE_SPLIT_VRING, + ..Default::default() } } fn reset(&mut self) { self.features_select = 0; self.acked_features_select = 0; - self.interrupt_status = 0; - self.device_status = 0; - self.config_generation = 0; + self.interrupt_status.store(0, Ordering::SeqCst); + self.device_status.store(0, Ordering::SeqCst); + self.config_generation.store(0, Ordering::SeqCst); self.queue_select = 0; - self.msix_config = INVALID_VECTOR_NUM; + self.msix_config.store(INVALID_VECTOR_NUM, Ordering::SeqCst); self.queue_type = QUEUE_TYPE_SPLIT_VRING; self.queues_config.iter_mut().for_each(|q| q.reset()); } fn check_device_status(&self, set: u32, clr: u32) -> bool { - self.device_status & (set | clr) == set + self.device_status.load(Ordering::Acquire) & (set | clr) == set } fn get_mut_queue_config(&mut self, need_check: bool) -> PciResult<&mut QueueConfig> { @@ -218,7 +213,9 @@ impl VirtioPciCommonConfig { .get_mut(self.queue_select as usize) .with_context(|| "pci-reg queue_select overflows") } else { - Err(anyhow!(PciError::DeviceStatus(self.device_status))) + Err(anyhow!(PciError::DeviceStatus( + self.device_status.load(Ordering::Acquire) + ))) } } @@ -277,10 +274,10 @@ impl VirtioPciCommonConfig { 0 } } - COMMON_MSIX_REG => self.msix_config as u32, + COMMON_MSIX_REG => self.msix_config.load(Ordering::Acquire) as u32, COMMON_NUMQ_REG => self.queues_config.len() as u32, - COMMON_STATUS_REG => self.device_status, - COMMON_CFGGENERATION_REG => self.config_generation as u32, + COMMON_STATUS_REG => self.device_status.load(Ordering::Acquire), + COMMON_CFGGENERATION_REG => self.config_generation.load(Ordering::Acquire) as u32, COMMON_Q_SELECT_REG => self.queue_select as u32, COMMON_Q_SIZE_REG => self .get_queue_config() @@ -342,7 +339,7 @@ impl VirtioPciCommonConfig { self.acked_features_select = value; } COMMON_GF_REG => { - if self.device_status & CONFIG_STATUS_FEATURES_OK != 0 { + if self.device_status.load(Ordering::Acquire) & CONFIG_STATUS_FEATURES_OK != 0 { error!("it's not allowed to set features after having been negoiated"); return Ok(()); } @@ -367,8 +364,8 @@ impl VirtioPciCommonConfig { } COMMON_MSIX_REG => { let val = self.revise_queue_vector(value, virtio_pci_dev); - self.msix_config = val as u16; - self.interrupt_status = 0; + self.msix_config.store(val as u16, Ordering::SeqCst); + self.interrupt_status.store(0, Ordering::SeqCst); } COMMON_STATUS_REG => { if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { @@ -380,13 +377,13 @@ impl VirtioPciCommonConfig { return Ok(()); } } - if value != 0 && (self.device_status & !value) != 0 { + if value != 0 && (self.device_status.load(Ordering::Acquire) & !value) != 0 { error!("Driver must not clear a device status bit"); return Ok(()); } - let old_status = self.device_status; - self.device_status = value; + let old_status = self.device_status.load(Ordering::Acquire); + self.device_status.store(value, Ordering::SeqCst); if self.check_device_status( CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER @@ -395,7 +392,7 @@ impl VirtioPciCommonConfig { CONFIG_STATUS_FAILED, ) { virtio_pci_dev.activate_device(self); - } else if old_status != 0 && self.device_status == 0 { + } else if old_status != 0 && self.device_status.load(Ordering::Acquire) == 0 { self.reset(); virtio_pci_dev.deactivate_device(); } @@ -421,7 +418,7 @@ impl VirtioPciCommonConfig { // It should not check device status when detaching device which // will set vector to INVALID_VECTOR_NUM. let mut need_check = true; - if self.device_status == 0 { + if self.device_status.load(Ordering::Acquire) == 0 { need_check = false; } self.get_mut_queue_config(need_check) @@ -646,7 +643,11 @@ impl VirtioPciDevice { } fn assign_interrupt_cb(&mut self) { - let cloned_common_cfg = self.common_config.clone(); + let common_cfg = self.common_config.lock().unwrap(); + let device_status = common_cfg.device_status.clone(); + let interrupt_status = common_cfg.interrupt_status.clone(); + let msix_config = common_cfg.msix_config.clone(); + let config_generation = common_cfg.config_generation.clone(); let cloned_msix = self.config.msix.as_ref().unwrap().clone(); let cloned_intx = self.config.intx.as_ref().unwrap().clone(); let dev_id = self.dev_id.clone(); @@ -654,23 +655,24 @@ impl VirtioPciDevice { move |int_type: &VirtioInterruptType, queue: Option<&Queue>, needs_reset: bool| { let vector = match int_type { VirtioInterruptType::Config => { - let mut locked_common_cfg = cloned_common_cfg.lock().unwrap(); if needs_reset { - locked_common_cfg.device_status |= CONFIG_STATUS_NEEDS_RESET; - if locked_common_cfg.device_status & CONFIG_STATUS_DRIVER_OK == 0 { + device_status.fetch_or(CONFIG_STATUS_NEEDS_RESET, Ordering::SeqCst); + if device_status.load(Ordering::Acquire) & CONFIG_STATUS_DRIVER_OK == 0 + { return Ok(()); } } // Use (CONFIG | VRING) instead of CONFIG, it can be used to solve the // IO stuck problem by change the device configure. - locked_common_cfg.interrupt_status |= - VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING; - locked_common_cfg.config_generation += 1; - locked_common_cfg.msix_config + interrupt_status.fetch_or( + VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING, + Ordering::SeqCst, + ); + config_generation.fetch_add(1, Ordering::SeqCst); + msix_config.load(Ordering::Acquire) } VirtioInterruptType::Vring => { - let mut locked_common_cfg = cloned_common_cfg.lock().unwrap(); - locked_common_cfg.interrupt_status |= VIRTIO_MMIO_INT_VRING; + interrupt_status.fetch_or(VIRTIO_MMIO_INT_VRING, Ordering::SeqCst); queue.map_or(0, |q| q.vring.get_queue_config().vector) } }; @@ -875,9 +877,8 @@ impl VirtioPciDevice { let cloned_intx = self.config.intx.as_ref().unwrap().clone(); let isr_read = move |data: &mut [u8], _: GuestAddress, _: u64| -> bool { if let Some(val) = data.get_mut(0) { - let mut common_cfg_lock = cloned_common_cfg.lock().unwrap(); - *val = common_cfg_lock.interrupt_status as u8; - common_cfg_lock.interrupt_status = 0; + let common_cfg_lock = cloned_common_cfg.lock().unwrap(); + *val = common_cfg_lock.interrupt_status.swap(0, Ordering::SeqCst) as u8; cloned_intx.lock().unwrap().notify(0); } true @@ -1319,12 +1320,12 @@ impl StateTransfer for VirtioPciDevice { // Save virtio pci common config state. { let common_config = self.common_config.lock().unwrap(); - state.interrupt_status = common_config.interrupt_status; - state.msix_config = common_config.msix_config; + state.interrupt_status = common_config.interrupt_status.load(Ordering::Acquire); + state.msix_config = common_config.msix_config.load(Ordering::Acquire); state.features_select = common_config.features_select; state.acked_features_select = common_config.acked_features_select; - state.device_status = common_config.device_status; - state.config_generation = common_config.config_generation; + state.device_status = common_config.device_status.load(Ordering::Acquire); + state.config_generation = common_config.config_generation.load(Ordering::Acquire); state.queue_select = common_config.queue_select; } @@ -1358,12 +1359,20 @@ impl StateTransfer for VirtioPciDevice { // Set virtio pci common config state. { let mut common_config = self.common_config.lock().unwrap(); - common_config.interrupt_status = pci_state.interrupt_status; - common_config.msix_config = pci_state.msix_config; + common_config + .interrupt_status + .store(pci_state.interrupt_status, Ordering::SeqCst); + common_config + .msix_config + .store(pci_state.msix_config, Ordering::SeqCst); common_config.features_select = pci_state.features_select; common_config.acked_features_select = pci_state.acked_features_select; - common_config.device_status = pci_state.device_status; - common_config.config_generation = pci_state.config_generation; + common_config + .device_status + .store(pci_state.device_status, Ordering::SeqCst); + common_config + .config_generation + .store(pci_state.config_generation, Ordering::SeqCst); common_config.queue_select = pci_state.queue_select; } @@ -1656,13 +1665,19 @@ mod tests { .is_err()); // Test Queue ready register - cmn_cfg.device_status = CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER; + cmn_cfg.device_status.store( + CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER, + Ordering::SeqCst, + ); cmn_cfg.queue_select = 0; com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_Q_ENABLE_REG, 0x1_u32); assert!(cmn_cfg.queues_config.get(0).unwrap().ready); // Failed to set Queue relevant register if device is no ready - cmn_cfg.device_status = CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER_OK; + cmn_cfg.device_status.store( + CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER_OK, + Ordering::SeqCst, + ); cmn_cfg.queue_select = 1; assert!(cmn_cfg .write_common_config(&virtio_pci, COMMON_Q_MSIX_REG, 0x4_u32) -- Gitee From 7fac1c302f6bc79cac8e569f0f465fa427d15965 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 16 May 2023 19:54:02 +0800 Subject: [PATCH 1094/2187] VNC: Modify Demo device. Fix vnc mst test_switch_display_device. The demo gpu device used for mst needs to update the registered process in ui console and add a field to identify the unique device. Signed-off-by: Xiao Ye --- pci/src/demo_dev.rs | 2 +- pci/src/demo_device/gpu_device.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index fb92d04a8..0a5f38052 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -76,7 +76,7 @@ impl DemoDev { // You can choose different device function based on the parameter of device_type. let device: Arc> = match cfg.device_type.as_str() { #[cfg(not(target_env = "musl"))] - "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem))), + "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem, cfg.id.clone()))), #[cfg(not(target_env = "musl"))] "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), #[cfg(not(target_env = "musl"))] diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 96b41d855..53d98bf9d 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -63,6 +63,7 @@ impl From for GpuEvent { } pub struct DemoGpu { + dev_id: String, sys_mem: Arc, con: Option>>, width: u32, @@ -73,8 +74,9 @@ pub struct DemoGpu { unsafe impl Send for DemoGpu {} impl DemoGpu { - pub fn new(sys_mem: Arc) -> Self { + pub fn new(sys_mem: Arc, dev_id: String) -> Self { Self { + dev_id, sys_mem, con: None, width: 0, @@ -212,7 +214,7 @@ impl DeviceTypeOperation for DemoGpu { fn realize(&mut self) -> Result<()> { let con_opts = Arc::new(HwOpts {}); - self.con = console_init("demo-gpu".to_string(), ConsoleType::Graphic, con_opts); + self.con = console_init(self.dev_id.clone(), ConsoleType::Graphic, con_opts); // Create Image. self.width = 640; -- Gitee From aaabe9e4ed97709fe7dc30ad3f75103566c0ac19 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 16 May 2023 17:15:38 +0800 Subject: [PATCH 1095/2187] GTK: add pause and resume button Pause the virtual machine when running the dailog. Signed-off-by: Xiao Ye --- machine/src/standard_vm/aarch64/mod.rs | 21 ++++++++++ machine/src/standard_vm/mod.rs | 54 +++++++++++++++++++++++++- machine/src/standard_vm/x86_64/mod.rs | 2 + machine_manager/src/config/display.rs | 4 ++ ui/src/gtk/menu.rs | 3 ++ ui/src/gtk/mod.rs | 28 +++++++++++++ 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 597979a17..f3ab6f8a2 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -159,6 +159,10 @@ pub struct StdMachine { shutdown_req: Arc, /// Reset request, handle VM `Reset` event. reset_req: Arc, + /// Pause request, handle VM `Pause` event. + pause_req: Arc, + /// Resume request, handle VM `Resume` event. + resume_req: Arc, /// Device Tree Blob. dtb_vec: Vec, /// List of guest NUMA nodes information. @@ -226,6 +230,14 @@ impl StdMachine { EventFd::new(libc::EFD_NONBLOCK) .with_context(|| MachineError::InitEventFdErr("reset_req".to_string()))?, ), + pause_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("pause_req".to_string()))?, + ), + resume_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("resume_req".to_string()))?, + ), dtb_vec: Vec::new(), numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -549,6 +561,13 @@ impl MachineOps for StdMachine { locked_vm .register_reset_event(locked_vm.reset_req.clone(), vm.clone()) .with_context(|| "Fail to register reset event")?; + locked_vm + .register_pause_event(locked_vm.pause_req.clone(), vm.clone()) + .with_context(|| "Fail to register pause event")?; + locked_vm + .register_resume_event(locked_vm.resume_req.clone(), vm.clone()) + .with_context(|| "Fail to register resume event")?; + locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, @@ -680,6 +699,8 @@ impl MachineOps for StdMachine { vm_name: vm_config.guest_name.clone(), power_button: Some(self.power_button.clone()), shutdown_req: Some(self.shutdown_req.clone()), + pause_req: Some(self.pause_req.clone()), + resume_req: Some(self.resume_req.clone()), }; gtk_display_init(ds_cfg, ui_context) .with_context(|| "Failed to init GTK display!")?; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index ca16d8a2d..7f197bda3 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -224,6 +224,58 @@ trait StdMachineOps: AcpiBuilder { Ok(()) } + fn register_pause_event( + &self, + pause_req: Arc, + clone_vm: Arc>, + ) -> MachineResult<()> { + let pause_req_fd = pause_req.as_raw_fd(); + let pause_req_handler: Rc = Rc::new(move |_, _| { + let _ret = pause_req.read(); + if !clone_vm.lock().unwrap().pause() { + error!("VM pause failed"); + } + None + }); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + pause_req_fd, + None, + EventSet::IN, + vec![pause_req_handler], + ); + EventLoop::update_event(vec![notifier], None) + .with_context(|| "Failed to register event notifier.")?; + Ok(()) + } + + fn register_resume_event( + &self, + resume_req: Arc, + clone_vm: Arc>, + ) -> MachineResult<()> { + let resume_req_fd = resume_req.as_raw_fd(); + let resume_req_handler: Rc = Rc::new(move |_, _| { + let _ret = resume_req.read(); + if !clone_vm.lock().unwrap().resume() { + error!("VM resume failed!"); + } + None + }); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + resume_req_fd, + None, + EventSet::IN, + vec![resume_req_handler], + ); + EventLoop::update_event(vec![notifier], None) + .with_context(|| "Failed to register event notifier.")?; + Ok(()) + } + fn register_shutdown_event( &self, shutdown_req: Arc, @@ -233,7 +285,7 @@ trait StdMachineOps: AcpiBuilder { let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { - let _ret = shutdown_req.read().unwrap(); + let _ret = shutdown_req.read(); if clone_vm.lock().unwrap().destroy() { Some(gen_delete_notifiers(&[shutdown_req_fd])) } else { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index a649204e8..452d49bce 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -591,6 +591,8 @@ impl MachineOps for StdMachine { vm_name: vm_config.guest_name.clone(), power_button: None, shutdown_req: Some(self.shutdown_req.clone()), + pause_req: None, + resume_req: None, }; gtk_display_init(ds_cfg, ui_context) .with_context(|| "Failed to init GTK display!")?; diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 74ac7d08e..4f89f749d 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -26,6 +26,10 @@ pub struct UiContext { pub power_button: Option>, /// Forced Shutdown. pub shutdown_req: Option>, + /// Pause Virtual Machine. + pub pause_req: Option>, + /// Resume Virtual Machine. + pub resume_req: Option>, } /// GTK related configuration. diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 13b4bbdb7..06ad42fcd 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -380,10 +380,13 @@ fn window_close_callback(gd: &Rc>) -> Result<()> { dialog.set_title(&gettext( "Please confirm whether to exit the virtual machine", )); + borrowed_gd.vm_pause(); let answer = dialog.run(); // SAFETY: Dialog is created in the current function and can be guaranteed not to be empty. unsafe { dialog.destroy() }; + if answer != gtk::ResponseType::Yes { + borrowed_gd.vm_resume(); return Ok(()); } } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index fdc5af243..3e593848b 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -207,6 +207,8 @@ pub(crate) struct GtkDisplay { pagenum2ds: HashMap>>, powerdown_button: Option>, shutdown_button: Option>, + pause_button: Option>, + resume_button: Option>, keysym2keycode: Rc>>, } @@ -230,6 +232,8 @@ impl GtkDisplay { pagenum2ds: HashMap::new(), powerdown_button: gtk_cfg.powerdown_button.clone(), shutdown_button: gtk_cfg.shutdown_button.clone(), + pause_button: gtk_cfg.pause_button.clone(), + resume_button: gtk_cfg.resume_button.clone(), keysym2keycode, } } @@ -321,6 +325,24 @@ impl GtkDisplay { .unwrap_or_else(|e| error!("Vm shut down failed: {:?}", e)); } } + + /// Pause Virtual Machine. + pub(crate) fn vm_pause(&self) { + if let Some(button) = &self.pause_button { + button + .write(1) + .unwrap_or_else(|e| error!("Vm pause failed: {:?}", e)); + } + } + + /// Resume Virtual Machine. + pub(crate) fn vm_resume(&self) { + if let Some(button) = &self.resume_button { + button + .write(1) + .unwrap_or_else(|e| error!("Vm resume failed: {:?}", e)); + } + } } pub struct GtkDisplayScreen { @@ -459,6 +481,10 @@ struct GtkConfig { powerdown_button: Option>, /// Forced Shutdown. shutdown_button: Option>, + /// Pause Virtual Machine. + pause_button: Option>, + /// Resume Virtual Machine. + resume_button: Option>, gtk_args: Vec, } @@ -474,6 +500,8 @@ pub fn gtk_display_init(ds_cfg: &DisplayConfig, ui_context: UiContext) -> Result vm_name: ui_context.vm_name, powerdown_button: ui_context.power_button, shutdown_button: ui_context.shutdown_req, + pause_button: ui_context.pause_req, + resume_button: ui_context.resume_req, gtk_args, }; let _handle = thread::Builder::new() -- Gitee From 624b9051abcfa60b1c5e2d00f256233a0a70bb56 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 17 May 2023 18:59:06 +0800 Subject: [PATCH 1096/2187] version: Append git commit id to bin VERSION Signed-off-by: Keqian Zhu --- machine_manager/src/cmdline.rs | 5 +--- util/build.rs | 44 ++++++++++++++++++++++++++++++++++ util/src/lib.rs | 7 ++++++ vhost_user_fs/src/cmdline.rs | 4 +--- 4 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 util/build.rs diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 61f343047..fa0e8c383 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -22,9 +22,6 @@ use crate::{ temp_cleaner::TempCleaner, }; -// Read the programe version in `Cargo.toml`. -const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); - /// This macro is to run struct $z 's function $s whose arg is $x 's inner member. /// There is a multi-macro-cast in cases of vec and bool. /// @@ -74,7 +71,7 @@ macro_rules! add_args_to_config_multi { /// This function is to define all commandline arguments. pub fn create_args_parser<'a>() -> ArgParser<'a> { ArgParser::new("StratoVirt") - .version(VERSION.unwrap_or("unknown")) + .version(util::VERSION) .author("The StratoVirt Project Developers") .about("A light kvm-based hypervisor.") .arg( diff --git a/util/build.rs b/util/build.rs new file mode 100644 index 000000000..8bc2d6095 --- /dev/null +++ b/util/build.rs @@ -0,0 +1,44 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{env, fs::File, io::Write, path::Path, process::Command}; + +fn get_git_commit() -> String { + println!("cargo:rerun-if-changed=../.git/HEAD"); + println!("cargo:rerun-if-changed=../.git/refs"); + println!("cargo:rerun-if-changed=build.rs"); + + let output = Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output(); + match output { + Ok(o) if o.status.success() => { + String::from_utf8(o.stdout).expect("Failed to read git commit id") + } + Ok(o) => { + println!("Get git commit id failed with status: {}", o.status); + String::from("unknown") + } + Err(e) => { + println!("Get git commit id failed: {:?}", e); + String::from("unknown") + } + } +} + +fn main() { + let commit = get_git_commit(); + // Save commit id to pkg build out directory. + let path = Path::new(&env::var("OUT_DIR").unwrap()).join("GIT_COMMIT"); + let mut file = File::create(path).unwrap(); + file.write_all(commit.as_bytes()).unwrap(); +} diff --git a/util/src/lib.rs b/util/src/lib.rs index c490d0d5c..0c7027875 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -46,6 +46,13 @@ use once_cell::sync::Lazy; use std::{any::Any, sync::Mutex}; use vmm_sys_util::terminal::Terminal; +/// Read the program version in `Cargo.toml` and concat with git commit id. +pub const VERSION: &str = concat!( + env!("CARGO_PKG_VERSION"), + " commit-id ", + include_str!(concat!(env!("OUT_DIR"), "/GIT_COMMIT")) +); + pub static TERMINAL_MODE: Lazy>> = Lazy::new(|| Mutex::new(None)); pub fn set_termi_raw_mode() -> std::io::Result<()> { diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index a8293f786..a8745bbff 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -20,8 +20,6 @@ use crate::fs_ops::open; use crate::fuse_msg::FUSE_OK; use util::arg_parser::{Arg, ArgMatches, ArgParser}; -// Read the programe version in `Cargo.toml`. -const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const MAX_PATH_LENGTH: usize = 4096; // Maximum length of the socket path is restricted by linux. const MAX_SOCK_PATH_LENGTH: usize = 108; @@ -29,7 +27,7 @@ const MAX_SOCK_PATH_LENGTH: usize = 108; /// This function is to define all command line arguments. pub fn create_args_parser<'a>() -> ArgParser<'a> { ArgParser::new("VhostUserFs") - .version(VERSION.unwrap_or("unknown")) + .version(util::VERSION) .author("Huawei Technologies Co., Ltd") .about("The process of Virtio fs for StratoVirt.") .arg( -- Gitee From 7a56f2b4c953d1de9eae17d02e81cd3097276fcd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 18 May 2023 00:06:02 +0800 Subject: [PATCH 1097/2187] Emu: Continue checking to prevent exit failed There is case powerdown or shutdown failed, try again util succeed. Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a6c995919..1eb36c4a9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1891,6 +1891,7 @@ fn check_windows_emu_pid( powerdown_req: Arc, shutdown_req: Arc, ) { + let mut check_delay = Duration::from_millis(4000); if !Path::new(&pid_path).exists() { log::info!("Detect windows emu exited, let VM exits now"); if get_run_stage() == VmRunningStage::Os { @@ -1900,16 +1901,18 @@ fn check_windows_emu_pid( } else if let Err(e) = shutdown_req.write(1) { log::error!("Failed to send shutdown request after emu exits: {:?}", e); } - } else { - let check_emu_alive = Box::new(move || { - check_windows_emu_pid( - pid_path.clone(), - powerdown_req.clone(), - shutdown_req.clone(), - ); - }); - if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.timer_add(check_emu_alive, Duration::from_millis(4000)); - } + // Continue checking to prevent exit failed. + check_delay = Duration::from_millis(1000); + } + + let check_emu_alive = Box::new(move || { + check_windows_emu_pid( + pid_path.clone(), + powerdown_req.clone(), + shutdown_req.clone(), + ); + }); + if let Some(ctx) = EventLoop::get_ctx(None) { + ctx.timer_add(check_emu_alive, check_delay); } } -- Gitee From 77acb765e114c7aed0c2e35808f3731e47de2522 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 04:39:20 +0800 Subject: [PATCH 1098/2187] Gtk: Some optimizations. Before active show_menu, Check the status first. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 41e2bbda0..ed83dd2f1 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -933,7 +933,11 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { /// Activate the current screen. fn do_set_major_event(gs: &Rc>) -> Result<()> { - gs.borrow().show_menu.activate(); + let borrowed_gs = gs.borrow(); + if borrowed_gs.show_menu.is_active() { + return Ok(()); + } + borrowed_gs.show_menu.activate(); Ok(()) } -- Gitee From ffdb4fa824fa85a34af2e5b1e20a30d3ed7bdd02 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 06:12:41 +0800 Subject: [PATCH 1099/2187] GTK: update window size update window size. Signed-off-by: Xiao Ye --- ui/src/console.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index e9441d7bf..ce5a1bd13 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -36,9 +36,9 @@ const FONT_WIDTH: i32 = 8; /// Height of font. const FONT_HEIGHT: i32 = 16; /// Width of image in surface. -pub const DEFAULT_SURFACE_WIDTH: i32 = 640; +pub const DEFAULT_SURFACE_WIDTH: i32 = 800; /// Height of image in surface. -pub const DEFAULT_SURFACE_HEIGHT: i32 = 480; +pub const DEFAULT_SURFACE_HEIGHT: i32 = 600; /// Maximum default window width. pub const MAX_WINDOW_WIDTH: u16 = 2560; /// Maximum default window height. -- Gitee From 12f2c9fe4fa972e44249423565f66c10e8ce8185 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 05:49:32 +0800 Subject: [PATCH 1100/2187] GTK: Optimize exit pop-up button display Optimize exit pop-up button display. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 06ad42fcd..53f96c5c7 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -15,6 +15,7 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::{bail, Result}; use gettextrs::gettext; use gtk::{ + ffi::{gtk_button_set_label, GtkButton, GtkWidget}, gdk::{ ffi::{GDK_KEY_equal, GDK_KEY_minus, GDK_KEY_B, GDK_KEY_F, GDK_KEY_M, GDK_KEY_S}, ModifierType, @@ -380,6 +381,23 @@ fn window_close_callback(gd: &Rc>) -> Result<()> { dialog.set_title(&gettext( "Please confirm whether to exit the virtual machine", )); + if let Some(button_yes) = &dialog.widget_for_response(gtk::ResponseType::Yes) { + let label: &str = &gettext("Yes"); + // SAFETY: it can be ensure that the pointer is not empty. + unsafe { + let button: *mut GtkWidget = button_yes.as_ptr(); + gtk_button_set_label(button as *mut GtkButton, label.to_glib_none().0); + } + } + if let Some(button_no) = dialog.widget_for_response(gtk::ResponseType::No) { + let label: &str = &gettext("No"); + // SAFETY: it can be ensure that the pointer is not empty. + unsafe { + let button: *mut GtkWidget = button_no.as_ptr(); + gtk_button_set_label(button as *mut GtkButton, label.to_glib_none().0); + } + } + borrowed_gd.vm_pause(); let answer = dialog.run(); // SAFETY: Dialog is created in the current function and can be guaranteed not to be empty. -- Gitee From 1db325c985180a24fd6419dde4b8f78cb6032f50 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 18 May 2023 07:10:59 +0800 Subject: [PATCH 1101/2187] GTK: update default window size Update default Minimum window size. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 41e2bbda0..0cbc2bdfb 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -944,11 +944,13 @@ pub(crate) fn update_window_size(gs: &Rc>) -> Result<( Some(image) => (image.width() as f64, image.height() as f64), None => (0.0, 0.0), }; - let (scale_width, scale_height) = if scale_mode.is_free_scale() { + let (mut scale_width, mut scale_height) = if scale_mode.is_free_scale() { (width * GTK_SCALE_MIN, height * GTK_SCALE_MIN) } else { (width * borrowed_gs.scale_x, height * borrowed_gs.scale_y) }; + scale_width = scale_width.max(DEFAULT_SURFACE_WIDTH as f64); + scale_height = scale_height.max(DEFAULT_SURFACE_HEIGHT as f64); let geo: Geometry = Geometry { min_width: scale_width as i32, -- Gitee From 5df69488ebeae7f451561a86d2f8c41dc97e8db1 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 16 May 2023 19:08:46 +0800 Subject: [PATCH 1102/2187] usb: support horizontal scroll wheel Add AC Pan in USB tablet report descriptor to support horizontal wheel. Signed-off-by: zhouli57 --- devices/src/usb/hid.rs | 18 ++++++++++++++---- devices/src/usb/tablet.rs | 26 +++++++++++++++++--------- ui/src/gtk/draw.rs | 11 +++++++---- ui/src/input.rs | 5 +++++ 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 62d6b8b24..61e678868 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -72,7 +72,7 @@ const HID_CODE: [u8; 0x100] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; /// Tablet report descriptor -const TABLET_REPORT_DESCRIPTOR: [u8; 74] = [ +const TABLET_REPORT_DESCRIPTOR: [u8; 89] = [ 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xa1, 0x01, // Collection (Application) @@ -108,6 +108,13 @@ const TABLET_REPORT_DESCRIPTOR: [u8; 74] = [ 0x75, 0x08, // Report Size (8) 0x95, 0x01, // Report Count (1) 0x81, 0x06, // Input (Data, Variable, Relative) + 0x05, 0x0c, // Usage Page (Consumer Device) + 0x0a, 0x38, 0x02, // Usage (AC Pan) + 0x15, 0x81, // Logical Minimum (-0x7f) + 0x25, 0x7f, // Logical Maximum (0x7f) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x06, // Input (Data, Variable, Relative) 0xc0, 0xc0, // End Collection ]; /// Keyboard report descriptor @@ -190,8 +197,10 @@ pub struct HidPointerEvent { pub pos_x: u32, /// Direction: up to down. pub pos_y: u32, - /// Wheel up or down. - pub pos_z: i32, + /// Vertical scroll wheel. + pub v_wheel: i32, + /// Horizontal scroll wheel. + pub h_wheel: i32, pub button_state: u32, } @@ -341,7 +350,8 @@ impl Hid { (evt.pos_x >> 8) as u8, evt.pos_y as u8, (evt.pos_y >> 8) as u8, - evt.pos_z as u8, + evt.v_wheel as u8, + evt.h_wheel as u8, ] } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 3be4b9830..0f4a4b018 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -28,10 +28,11 @@ use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use ui::input::{register_pointer, unregister_pointer, PointerOpts}; +use ui::input::{ + register_pointer, unregister_pointer, PointerOpts, INPUT_BUTTON_WHEEL_DOWN, + INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, +}; -const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; -const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; const INPUT_BUTTON_MASK: u32 = 0x7; const INPUT_COORDINATES_MAX: u32 = 0x7fff; @@ -86,7 +87,7 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { }, other_desc: vec![Arc::new(UsbDescOther { /// HID descriptor - data: vec![0x09, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0], + data: vec![0x09, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 89, 0x0], })], endpoints: vec![Arc::new(UsbDescEndpoint { endpoint_desc: UsbEndpointDescriptor { @@ -144,12 +145,19 @@ impl PointerOpts for UsbTabletAdapter { } let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; let mut evt = &mut locked_tablet.hid.pointer.queue[index]; - if button == INPUT_BUTTON_WHEEL_UP { - evt.pos_z = 1; - } else if button == INPUT_BUTTON_WHEEL_DOWN { - evt.pos_z = -1; + if button & INPUT_BUTTON_WHEEL_UP == INPUT_BUTTON_WHEEL_UP { + evt.v_wheel = 1; + } else if button & INPUT_BUTTON_WHEEL_DOWN == INPUT_BUTTON_WHEEL_DOWN { + evt.v_wheel = -1; + } else { + evt.v_wheel = 0; + } + if button & INPUT_BUTTON_WHEEL_LEFT == INPUT_BUTTON_WHEEL_LEFT { + evt.h_wheel = -1; + } else if button & INPUT_BUTTON_WHEEL_RIGHT == INPUT_BUTTON_WHEEL_RIGHT { + evt.h_wheel = 1; } else { - evt.pos_z = 0; + evt.h_wheel = 0; } evt.button_state = button & INPUT_BUTTON_MASK; evt.pos_x = min(x, INPUT_COORDINATES_MAX); diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 3ace3e9ea..4cf8600b5 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -27,7 +27,8 @@ use crate::{ console::graphic_hardware_ui_info, gtk::GtkDisplayScreen, input::{ - self, point_event, press_mouse, update_key_state, ABS_MAX, INPUT_POINT_LEFT, + self, point_event, press_mouse, update_key_state, ABS_MAX, INPUT_BUTTON_WHEEL_DOWN, + INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, }, }; @@ -221,9 +222,11 @@ fn da_scroll_callback( Some(image) => (image.width() as f64, image.height() as f64), None => return Ok(()), }; - let button_mask: u8 = match scroll_event.direction() { - ScrollDirection::Up => 0x8, - ScrollDirection::Down => 0x10, + let button_mask = match scroll_event.direction() { + ScrollDirection::Up => INPUT_BUTTON_WHEEL_UP, + ScrollDirection::Down => INPUT_BUTTON_WHEEL_DOWN, + ScrollDirection::Left => INPUT_BUTTON_WHEEL_LEFT, + ScrollDirection::Right => INPUT_BUTTON_WHEEL_RIGHT, _ => 0x0, }; diff --git a/ui/src/input.rs b/ui/src/input.rs index eecbd8c2e..23d964f86 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -57,6 +57,11 @@ const KEYCODE_KP_DECIMAL: u16 = 0x53; pub const NUM_LOCK_LED: u8 = 0x1; pub const CAPS_LOCK_LED: u8 = 0x2; pub const SCROLL_LOCK_LED: u8 = 0x4; +/// Input button state. +pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; +pub const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; +pub const INPUT_BUTTON_WHEEL_LEFT: u32 = 0x20; +pub const INPUT_BUTTON_WHEEL_RIGHT: u32 = 0x40; static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); -- Gitee From 554591e4c08d0c1df12d8d673d613ab7bcafcf48 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 18 May 2023 21:12:49 +0800 Subject: [PATCH 1103/2187] input: correct the update logic for the shift modifier state The shift modifier state should be set when either left shift or right shift is pressed. Signed-off-by: zhouli57 --- ui/src/input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/input.rs b/ui/src/input.rs index 23d964f86..f9ee706c9 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -142,7 +142,7 @@ impl KeyBoardState { KEYCODE_SHIFT | KEYCODE_SHIFT_R => { self.keyboard_modstate_update( KEYCODE_SHIFT, - KEYCODE_SHIFT, + KEYCODE_SHIFT_R, KeyboardModifier::KeyModShift, )?; } -- Gitee From d3c57098c93c31554254a9bfbf68dea5de23c4af Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 18:57:58 +0800 Subject: [PATCH 1104/2187] virtio-gpu: Optimize construction of gpu_opts gpu_opts can be shared. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index d04b2c271..a02fa2222 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1586,8 +1586,6 @@ pub struct Gpu { output_states: Arc>, /// Each console corresponds to a display. consoles: Vec>>>, - /// Callback to trigger interrupt. - interrupt_cb: Option>, /// Eventfd for device deactivate. deactivate_evts: Vec, } @@ -1605,7 +1603,6 @@ impl Gpu { [VirtioGpuOutputState::default(); VIRTIO_GPU_MAX_OUTPUTS], )), consoles: Vec::new(), - interrupt_cb: None, deactivate_evts: Vec::new(), } } @@ -1638,14 +1635,15 @@ impl VirtioDevice for Gpu { let mut output_states = self.output_states.lock().unwrap(); output_states[0].width = self.cfg.xres; output_states[0].height = self.cfg.yres; + + let gpu_opts = Arc::new(GpuOpts { + output_states: self.output_states.clone(), + config_space: self.state.config_space.clone(), + interrupt_cb: None, + }); for i in 0..self.cfg.max_outputs { - let gpu_opts = Arc::new(GpuOpts { - output_states: self.output_states.clone(), - config_space: self.state.config_space.clone(), - interrupt_cb: self.interrupt_cb.clone(), - }); let dev_name = format!("virtio-gpu{}", i); - let con = console_init(dev_name, ConsoleType::Graphic, gpu_opts); + let con = console_init(dev_name, ConsoleType::Graphic, gpu_opts.clone()); let con_ref = con.as_ref().unwrap().upgrade().unwrap(); output_states[i as usize].con_id = con_ref.lock().unwrap().con_id; self.consoles.push(con); @@ -1756,17 +1754,15 @@ impl VirtioDevice for Gpu { ))); } - self.interrupt_cb = Some(interrupt_cb.clone()); let mut scanouts = vec![]; + let gpu_opts = Arc::new(GpuOpts { + output_states: self.output_states.clone(), + config_space: self.state.config_space.clone(), + interrupt_cb: Some(interrupt_cb.clone()), + }); for con in &self.consoles { - let gpu_opts = Arc::new(GpuOpts { - output_states: self.output_states.clone(), - config_space: self.state.config_space.clone(), - interrupt_cb: self.interrupt_cb.clone(), - }); - let con_ref = con.as_ref().unwrap().upgrade().unwrap(); - con_ref.lock().unwrap().dev_opts = gpu_opts; + con_ref.lock().unwrap().dev_opts = gpu_opts.clone(); let scanout = GpuScanout { con: con.clone(), -- Gitee From b75e2f2bb67c93e79b278fd5003025103c9e695c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 19:11:29 +0800 Subject: [PATCH 1105/2187] virtio-gpu: Derive Default for GpuState No need to implement Default by hand. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a02fa2222..def431966 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1550,12 +1550,12 @@ pub struct VirtioGpuConfig { events_read: u32, events_clear: u32, num_scanouts: u32, - reserved: u32, + _reserved: u32, } /// State of gpu device. #[repr(C)] -#[derive(Clone)] +#[derive(Clone, Default)] pub struct GpuState { /// Bitmask of features supported by the backend. device_features: u64, @@ -1565,16 +1565,6 @@ pub struct GpuState { config_space: Arc>, } -impl Default for GpuState { - fn default() -> Self { - GpuState { - device_features: 0, - driver_features: 0, - config_space: Arc::new(Mutex::new(VirtioGpuConfig::default())), - } - } -} - /// GPU device structure. #[derive(Default)] pub struct Gpu { @@ -1610,7 +1600,6 @@ impl Gpu { fn build_device_config_space(&mut self) { let mut config_space = self.state.config_space.lock().unwrap(); config_space.num_scanouts = self.cfg.max_outputs; - config_space.reserved = 0; } } -- Gitee From 6df00f13c06b528c238781bef7bf63e51f5b4422 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 19:39:40 +0800 Subject: [PATCH 1106/2187] virtio-gpu: Optimize constuction of VirtioGpuRequest No need to clone element out_iovec, as it's unused after request parsing. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 54 +++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index def431966..b42577b4c 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -150,7 +150,6 @@ struct VirtioGpuGetEdid { impl ByteCode for VirtioGpuGetEdid {} #[repr(C)] -#[allow(unused)] // data which transfer to frontend need padding #[derive(Clone, Copy)] struct VirtioGpuRespEdid { @@ -292,7 +291,6 @@ impl HardWareOperations for GpuOpts { } } -#[allow(unused)] #[derive(Default, Clone)] struct VirtioGpuRequest { header: VirtioGpuCtrlHdr, @@ -300,11 +298,11 @@ struct VirtioGpuRequest { out_iovec: Vec, out_len: u32, in_iovec: Vec, - in_len: u32, + _in_len: u32, } impl VirtioGpuRequest { - fn new(mem_space: &Arc, elem: &Element) -> Result { + fn new(mem_space: &Arc, elem: &mut Element) -> Result { // Report errors for out_iovec invalid here, deal with in_iovec // error in cmd process. if elem.out_iovec.is_empty() { @@ -324,31 +322,25 @@ impl VirtioGpuRequest { Ok(()) })?; - // Note: in_iov and out_iov total len is no more than 1<<32, and - // out_iov is more than 1, so in_len and out_len will not overflow. - let mut request = VirtioGpuRequest { - header, - index: elem.index, - out_iovec: Vec::with_capacity(elem.desc_num as usize), - out_len: 0, - in_iovec: Vec::with_capacity(elem.desc_num as usize), - in_len: 0, - }; - - let mut out_iovec = elem.out_iovec.clone(); // Size of out_iovec is no less than size of VirtioGpuCtrlHdr, so // it is possible to get none back. - let data_iovec = iov_discard_front(&mut out_iovec, size_of::() as u64) - .unwrap_or_default(); - let (data_len, iovec) = gpa_hva_iovec_map(data_iovec, mem_space)?; - request.out_len = data_len as u32; - request.out_iovec = iovec; + let data_iovec = + iov_discard_front(&mut elem.out_iovec, size_of::() as u64) + .unwrap_or_default(); - let (data_len, iovec) = gpa_hva_iovec_map(&elem.in_iovec, mem_space)?; - request.in_len = data_len as u32; - request.in_iovec = iovec; + let (out_len, out_iovec) = gpa_hva_iovec_map(data_iovec, mem_space)?; + let (in_len, in_iovec) = gpa_hva_iovec_map(&elem.in_iovec, mem_space)?; - Ok(request) + // Note: in_iov and out_iov total len is no more than 1<<32, and + // out_iov is more than 1, so in_len and out_len will not overflow. + Ok(VirtioGpuRequest { + header, + index: elem.index, + out_iovec, + out_len: out_len as u32, + in_iovec, + _in_len: in_len as u32, + }) } } @@ -1406,14 +1398,14 @@ impl GpuIoHandler { let mut invalid_elem_index = 0; loop { - let elem = queue + let mut elem = queue .vring .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { break; } - match VirtioGpuRequest::new(&self.mem_space, &elem) { + match VirtioGpuRequest::new(&self.mem_space, &mut elem) { Ok(req) => { req_queue.push(req); } @@ -1445,14 +1437,14 @@ impl GpuIoHandler { let mut queue = cursor_queue.lock().unwrap(); loop { - let elem = queue + let mut elem = queue .vring .pop_avail(&self.mem_space, self.driver_features)?; if elem.desc_num == 0 { break; } - match VirtioGpuRequest::new(&self.mem_space, &elem) { + match VirtioGpuRequest::new(&self.mem_space, &mut elem) { Ok(req) => match self.cmd_update_cursor(&req) { Ok(_) => {} Err(e) => { @@ -1512,7 +1504,7 @@ impl EventNotifierHelper for GpuIoHandler { let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = handler_clone.lock().unwrap().ctrl_queue_evt_handler() { - error!("Failed to process queue for virtio gpu, err: {:?}", e,); + error!("Failed to process ctrlq for virtio gpu, err: {:?}", e); } None }); @@ -1529,7 +1521,7 @@ impl EventNotifierHelper for GpuIoHandler { let h: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); if let Err(e) = handler_clone.lock().unwrap().cursor_queue_evt_handler() { - error!("Failed to process queue for virtio gpu, err: {:?}", e,); + error!("Failed to process cursorq for virtio gpu, err: {:?}", e); } None }); -- Gitee From 05236f82d4175d6ef74fdb2aa25a61d4a4fb528a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 02:34:05 +0800 Subject: [PATCH 1107/2187] virtio-gpu: Refactor cmd_set_scanout() Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 240 ++++++++++++++++++++------------------- 1 file changed, 121 insertions(+), 119 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index b42577b4c..61a3ec4a9 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -511,17 +511,6 @@ fn is_rect_in_resource(rect: &VirtioGpuRect, res: &GpuResource) -> bool { false } -// Mask resource's scanout bit before disable a scanout. -fn disable_scanout(scanout: &mut GpuScanout) { - if scanout.resource_id == 0 { - return; - } - // TODO: present 'Guest disabled display.' in surface. - display_replace_surface(&scanout.con, None) - .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); - scanout.clear(); -} - impl GpuIoHandler { fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { if header.out_len < size_of::() as u32 { @@ -602,6 +591,55 @@ impl GpuIoHandler { self.send_response(req, &resp) } + // Mask resource's scanout bit before disable a scanout. + fn disable_scanout(&mut self, scanout_id: usize) { + let resource_id = self.scanouts[scanout_id].resource_id; + if resource_id == 0 { + return; + } + + if let Some(res_idx) = self.get_resource_idx(resource_id) { + let res = &mut self.resources_list[res_idx]; + res.scanouts_bitmask &= !(1 << scanout_id); + } + + // TODO: present 'Guest disabled display.' in surface. + let scanout = &mut self.scanouts[scanout_id]; + display_replace_surface(&scanout.con, None) + .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); + scanout.clear(); + } + + fn get_resource_idx(&self, resource_id: u32) -> Option { + self.resources_list + .iter() + .position(|x| x.resource_id == resource_id) + } + + fn get_backed_resource_idx(&self, res_id: u32, caller: &str) -> (Option, u32) { + match self.get_resource_idx(res_id) { + None => { + error!( + "GuestError: The resource_id {} in {} request does not existed", + res_id, caller, + ); + (None, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID) + } + Some(res_idx) => { + let res = &self.resources_list[res_idx]; + if res.iov.is_empty() || res.pixman_image.is_null() { + error!( + "GuestError: The resource_id {} in {} request has no backing storage.", + res_id, caller, + ); + (None, VIRTIO_GPU_RESP_ERR_UNSPEC) + } else { + (Some(res_idx), 0) + } + } + } + } + fn cmd_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_cursor = VirtioGpuUpdateCursor::default(); self.get_request(req, &mut info_cursor)?; @@ -827,18 +865,16 @@ impl GpuIoHandler { } fn resource_destroy(&mut self, res_index: usize) { - let res = &mut self.resources_list[res_index]; - - if res.scanouts_bitmask != 0 { + let scanouts_bitmask = self.resources_list[res_index].scanouts_bitmask; + if scanouts_bitmask != 0 { for i in 0..self.num_scanouts { - if (res.scanouts_bitmask & (1 << i)) != 0 { - let scanout = &mut self.scanouts[i as usize]; - res.scanouts_bitmask &= !(1 << i); - disable_scanout(scanout); + if (scanouts_bitmask & (1 << i)) != 0 { + self.disable_scanout(i as usize); } } } + let res = &mut self.resources_list[res_index]; unref_pixman_image(res.pixman_image); self.used_hostmem -= res.host_mem; res.iov.clear(); @@ -877,118 +913,84 @@ impl GpuIoHandler { return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, req); } - let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; if info_set_scanout.resource_id == 0 { // Set resource_id to 0 means disable the scanout. - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == scanout.resource_id) - { - let res = &mut self.resources_list[res_index]; - res.scanouts_bitmask &= !(1 << info_set_scanout.scanout_id); - } - disable_scanout(scanout); + self.disable_scanout(info_set_scanout.scanout_id as usize); return self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req); } - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_set_scanout.resource_id) + // Check if resource is valid. + let (res_idx, error) = + self.get_backed_resource_idx(info_set_scanout.resource_id, "cmd_set_scanout"); + if res_idx.is_none() { + return self.response_nodata(error, req); + } + + let res = &mut self.resources_list[res_idx.unwrap()]; + if info_set_scanout.rect.width < 16 + || info_set_scanout.rect.height < 16 + || !is_rect_in_resource(&info_set_scanout.rect, res) { - let res = &self.resources_list[res_index]; - if info_set_scanout.rect.width < 16 - || info_set_scanout.rect.height < 16 - || !is_rect_in_resource(&info_set_scanout.rect, res) - { - error!( - "GuestError: The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {}).", - res.resource_id, - res.width, - res.height, - info_set_scanout.scanout_id, - info_set_scanout.rect.width, - info_set_scanout.rect.height, - info_set_scanout.rect.x_coord, - info_set_scanout.rect.y_coord, - ); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); - } + error!( + "GuestError: The resource (id: {} width: {} height: {}) is outfit for scanout (id: {} width: {} height: {} x_coord: {} y_coord: {}).", + res.resource_id, + res.width, + res.height, + info_set_scanout.scanout_id, + info_set_scanout.rect.width, + info_set_scanout.rect.height, + info_set_scanout.rect.x_coord, + info_set_scanout.rect.y_coord, + ); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); + } - let pixman_format = unsafe { pixman_image_get_format(res.pixman_image) }; - let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; - let pixman_stride = unsafe { pixman_image_get_stride(res.pixman_image) }; - let offset = info_set_scanout.rect.x_coord * bpp - + info_set_scanout.rect.y_coord * pixman_stride as u32; - let res_data = unsafe { pixman_image_get_data(res.pixman_image) }; - let res_data_offset = unsafe { res_data.offset(offset as isize) }; - - match scanout.surface { - None => { - if create_surface( - scanout, - info_set_scanout, - res, - pixman_format, - pixman_stride, - res_data_offset, - ) - .image - .is_null() - { - error!("HostError: surface image create failed, check pixman library."); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } - } - Some(sur) => { - let scanout_data = unsafe { pixman_image_get_data(sur.image) }; - if (res_data_offset != scanout_data - || scanout.width != info_set_scanout.rect.width - || scanout.height != info_set_scanout.rect.height) - && create_surface( - scanout, - info_set_scanout, - res, - pixman_format, - pixman_stride, - res_data_offset, - ) - .image - .is_null() - { - error!("HostError: surface pixman image create failed, please check pixman library."); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } - } - } + let pixman_format = unsafe { pixman_image_get_format(res.pixman_image) }; + let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + let pixman_stride = unsafe { pixman_image_get_stride(res.pixman_image) }; + let offset = info_set_scanout.rect.x_coord * bpp + + info_set_scanout.rect.y_coord * pixman_stride as u32; + let res_data = unsafe { pixman_image_get_data(res.pixman_image) }; + let res_data_offset = unsafe { res_data.offset(offset as isize) }; - if let Some(old_res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == scanout.resource_id) - { - // Update old resource scanout bitmask. - self.resources_list[old_res_index].scanouts_bitmask &= - !(1 << info_set_scanout.scanout_id); + // Create surface for the scanout. + let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; + if scanout.surface.is_none() + || unsafe { pixman_image_get_data(scanout.surface.unwrap().image) } != res_data_offset + || scanout.width != info_set_scanout.rect.width + || scanout.height != info_set_scanout.rect.height + { + let surface = create_surface( + scanout, + info_set_scanout, + res, + pixman_format, + pixman_stride, + res_data_offset, + ); + if surface.image.is_null() { + error!("HostError: surface image create failed, check pixman library."); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } - // Update new resource scanout bitmask. - self.resources_list[res_index].scanouts_bitmask |= 1 << info_set_scanout.scanout_id; - // Update scanout configure. - scanout.resource_id = info_set_scanout.resource_id; - scanout.x = info_set_scanout.rect.x_coord; - scanout.y = info_set_scanout.rect.y_coord; - scanout.width = info_set_scanout.rect.width; - scanout.height = info_set_scanout.rect.height; + } - self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) - } else { - error!( - "GuestError: The resource_id {} in set_scanout {} request is not existed.", - info_set_scanout.resource_id, info_set_scanout.scanout_id - ); - self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + // Unlink old resource. + let old_res_id = scanout.resource_id; + if let Some(old_res_idx) = self.get_resource_idx(old_res_id) { + let old_res = &mut self.resources_list[old_res_idx]; + old_res.scanouts_bitmask &= !(1 << info_set_scanout.scanout_id); } + // Link new resource. + let res = &mut self.resources_list[res_idx.unwrap()]; + res.scanouts_bitmask |= 1 << info_set_scanout.scanout_id; + let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; + scanout.resource_id = info_set_scanout.resource_id; + scanout.x = info_set_scanout.rect.x_coord; + scanout.y = info_set_scanout.rect.y_coord; + scanout.width = info_set_scanout.rect.width; + scanout.height = info_set_scanout.rect.height; + + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } fn cmd_resource_flush(&mut self, req: &VirtioGpuRequest) -> Result<()> { -- Gitee From f5600e485d9d41c08e3e4a3a5ef76e3bbbe8f985 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 22:50:23 +0800 Subject: [PATCH 1108/2187] virtio-gpu: Refactor send_responce() Fill fence id in send_responce() to avoid repeat code. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 112 ++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 61a3ec4a9..5e9a965f6 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -24,7 +24,7 @@ use crate::{ VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -100,6 +100,10 @@ struct VirtioGpuOutputState { y_coor: i32, } +trait CtrlHdr { + fn mut_ctrl_hdr(&mut self) -> &mut VirtioGpuCtrlHdr; +} + #[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuCtrlHdr { @@ -112,6 +116,12 @@ struct VirtioGpuCtrlHdr { impl ByteCode for VirtioGpuCtrlHdr {} +impl CtrlHdr for VirtioGpuCtrlHdr { + fn mut_ctrl_hdr(&mut self) -> &mut VirtioGpuCtrlHdr { + self + } +} + #[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuRect { @@ -139,8 +149,15 @@ struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_OUTPUTS], } + impl ByteCode for VirtioGpuDisplayInfo {} +impl CtrlHdr for VirtioGpuDisplayInfo { + fn mut_ctrl_hdr(&mut self) -> &mut VirtioGpuCtrlHdr { + &mut self.header + } +} + #[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuGetEdid { @@ -159,6 +176,14 @@ struct VirtioGpuRespEdid { edid: [u8; 1024], } +impl ByteCode for VirtioGpuRespEdid {} + +impl CtrlHdr for VirtioGpuRespEdid { + fn mut_ctrl_hdr(&mut self) -> &mut VirtioGpuCtrlHdr { + &mut self.header + } +} + impl Default for VirtioGpuRespEdid { fn default() -> Self { VirtioGpuRespEdid { @@ -170,8 +195,6 @@ impl Default for VirtioGpuRespEdid { } } -impl ByteCode for VirtioGpuRespEdid {} - #[repr(C)] #[derive(Default, Clone, Copy)] struct VirtioGpuResourceCreate2d { @@ -529,48 +552,47 @@ impl GpuIoHandler { fn complete_one_request(&mut self, index: u16, len: u32) -> Result<()> { let mut queue_lock = self.ctrl_queue.lock().unwrap(); - if let Err(e) = queue_lock.vring.add_used(&self.mem_space, index, len) { - bail!( - "Failed to add used ring(gpu ctrl), index {}, len {} {:?}.", - index, - len, - e, - ); - } + queue_lock + .vring + .add_used(&self.mem_space, index, len) + .with_context(|| { + format!( + "Failed to add used ring(gpu ctrl), index {}, len {}", + index, len, + ) + })?; if queue_lock .vring .should_notify(&self.mem_space, self.driver_features) { - if let Err(e) = - (*self.interrupt_cb.as_ref())(&VirtioInterruptType::Vring, Some(&queue_lock), false) - { - bail!("Failed to trigger interrupt(gpu ctrl), error is {:?}.", e); - } + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) + .with_context(|| "Failed to trigger interrupt(gpu ctrl)")?; } Ok(()) } - fn send_response(&mut self, req: &VirtioGpuRequest, resp: &T) -> Result<()> { - let mut len = 0; - if let Err(e) = iov_from_buf_direct(&req.in_iovec, resp.as_bytes()).and_then(|size| { - len = size; - if size == size_of::() { - Ok(()) - } else { - bail!( - "Expected response length is {}, actual get response length {}.", - size_of::(), - size - ); - } - }) { + fn send_response( + &mut self, + req: &VirtioGpuRequest, + resp: &mut T, + ) -> Result<()> { + if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { + let mut header = resp.mut_ctrl_hdr(); + header.flags |= VIRTIO_GPU_FLAG_FENCE; + header.fence_id = req.header.fence_id; + header.ctx_id = req.header.ctx_id; + } + + let len = iov_from_buf_direct(&req.in_iovec, resp.as_bytes())?; + if len != size_of::() { error!( - "GuestError: An incomplete response will be used instead of the expected, because {:?}. \ + "GuestError: An incomplete response will be used instead of the expected: expected \ + length is {}, actual length is {}. \ Also, be aware that the virtual machine may suspended if response is too short to \ carry the necessary information.", - e + size_of::(), len, ); } self.complete_one_request(req.index, len as u32) @@ -581,14 +603,7 @@ impl GpuIoHandler { hdr_type: resp_head_type, ..Default::default() }; - - if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { - resp.flags |= VIRTIO_GPU_FLAG_FENCE; - resp.fence_id = req.header.fence_id; - resp.ctx_id = req.header.ctx_id; - } - - self.send_response(req, &resp) + self.send_response(req, &mut resp) } // Mask resource's scanout bit before disable a scanout. @@ -753,13 +768,7 @@ impl GpuIoHandler { } } drop(output_states_lock); - - if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { - display_info.header.flags |= VIRTIO_GPU_FLAG_FENCE; - display_info.header.fence_id = req.header.fence_id; - display_info.header.ctx_id = req.header.ctx_id; - } - self.send_response(req, &display_info) + self.send_response(req, &mut display_info) } fn cmd_get_edid(&mut self, req: &VirtioGpuRequest) -> Result<()> { @@ -776,11 +785,6 @@ impl GpuIoHandler { let mut edid_resp = VirtioGpuRespEdid::default(); edid_resp.header.hdr_type = VIRTIO_GPU_RESP_OK_EDID; - if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { - edid_resp.header.flags |= VIRTIO_GPU_FLAG_FENCE; - edid_resp.header.fence_id = req.header.fence_id; - edid_resp.header.ctx_id = req.header.ctx_id; - } let output_states_lock = self.output_states.lock().unwrap(); let mut edid_info = EdidInfo::new( @@ -794,9 +798,7 @@ impl GpuIoHandler { edid_info.edid_array_fulfill(&mut edid_resp.edid); edid_resp.size = edid_resp.edid.len() as u32; - self.send_response(req, &edid_resp)?; - - Ok(()) + self.send_response(req, &mut edid_resp) } fn cmd_resource_create_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { -- Gitee From bf4cc0f6fc6575aad22b8064dcbf662266045a36 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 23:16:10 +0800 Subject: [PATCH 1109/2187] virtio-gpu: Refactor cmd_resource_unref() ... to remove duplicate code. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 5e9a965f6..b0b89fe93 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -880,19 +880,15 @@ impl GpuIoHandler { unref_pixman_image(res.pixman_image); self.used_hostmem -= res.host_mem; res.iov.clear(); + self.resources_list.remove(res_index); } fn cmd_resource_unref(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_resource_unref = VirtioGpuResourceUnref::default(); self.get_request(req, &mut info_resource_unref)?; - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_resource_unref.resource_id) - { + if let Some(res_index) = self.get_resource_idx(info_resource_unref.resource_id) { self.resource_destroy(res_index); - self.resources_list.remove(res_index); self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } else { error!( @@ -1493,7 +1489,6 @@ impl Drop for GpuIoHandler { fn drop(&mut self) { while !self.resources_list.is_empty() { self.resource_destroy(0); - self.resources_list.remove(0); } } } -- Gitee From 30466ecdb95a70077ae114d6bd53ddaad157a9c1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 20 May 2023 23:55:48 +0800 Subject: [PATCH 1110/2187] virtio-gpu: Refactor cmd_resource_flush() Reduce indent. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 149 ++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 80 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index b0b89fe93..9aed5418f 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -518,20 +518,17 @@ pub fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) } fn is_rect_in_resource(rect: &VirtioGpuRect, res: &GpuResource) -> bool { - if rect + let x_in = rect .x_coord .checked_add(rect.width) .filter(|&sum| sum <= res.width) - .is_some() - && rect - .y_coord - .checked_add(rect.height) - .filter(|&sum| sum <= res.height) - .is_some() - { - return true; - } - false + .is_some(); + let y_in = rect + .y_coord + .checked_add(rect.height) + .filter(|&sum| sum <= res.height) + .is_some(); + x_in && y_in } impl GpuIoHandler { @@ -995,80 +992,72 @@ impl GpuIoHandler { let mut info_res_flush = VirtioGpuResourceFlush::default(); self.get_request(req, &mut info_res_flush)?; - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_res_flush.resource_id) - { - let res = &self.resources_list[res_index]; - if !is_rect_in_resource(&info_res_flush.rect, res) { - error!( - "GuestError: The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {}).", - res.resource_id, res.width, res.height, - info_res_flush.rect.width, info_res_flush.rect.height, - info_res_flush.rect.x_coord, info_res_flush.rect.y_coord, - ); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); - } - - unsafe { - let mut flush_reg = pixman_region16_t::default(); - let flush_reg_ptr: *mut pixman_region16_t = - &mut flush_reg as *mut pixman_region16_t; - pixman_region_init_rect( - flush_reg_ptr, - info_res_flush.rect.x_coord as i32, - info_res_flush.rect.y_coord as i32, - info_res_flush.rect.width, - info_res_flush.rect.height, - ); - for i in 0..self.num_scanouts { - // Flushes any scanouts the resource is being used on. - if res.scanouts_bitmask & (1 << i) != 0 { - let scanout = &self.scanouts[i as usize]; - let mut rect_reg = pixman_region16_t::default(); - let mut final_reg = pixman_region16_t::default(); - let rect_reg_ptr: *mut pixman_region16_t = - &mut rect_reg as *mut pixman_region16_t; - let final_reg_ptr: *mut pixman_region16_t = - &mut final_reg as *mut pixman_region16_t; - - pixman_region_init(final_reg_ptr); - pixman_region_init_rect( - rect_reg_ptr, - scanout.x as i32, - scanout.y as i32, - scanout.width, - scanout.height, - ); - pixman_region_intersect(final_reg_ptr, flush_reg_ptr, rect_reg_ptr); - pixman_region_translate( - final_reg_ptr, - -(scanout.x as i32), - -(scanout.y as i32), - ); - let extents = pixman_region_extents(final_reg_ptr); - display_graphic_update( - &scanout.con, - (*extents).x1 as i32, - (*extents).y1 as i32, - ((*extents).x2 - (*extents).x1) as i32, - ((*extents).y2 - (*extents).y1) as i32, - )?; - pixman_region_fini(rect_reg_ptr); - pixman_region_fini(final_reg_ptr); - } - } - pixman_region_fini(flush_reg_ptr); - } - self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) - } else { + let res_index = self.get_resource_idx(info_res_flush.resource_id); + if res_index.is_none() { error!( "GuestError: The resource_id {} in resource flush request is not existed.", info_res_flush.resource_id ); - self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + } + + let res = &self.resources_list[res_index.unwrap()]; + if !is_rect_in_resource(&info_res_flush.rect, res) { + error!( + "GuestError: The resource (id: {} width: {} height: {}) is outfit for flush rectangle (width: {} height: {} x_coord: {} y_coord: {}).", + res.resource_id, res.width, res.height, + info_res_flush.rect.width, info_res_flush.rect.height, + info_res_flush.rect.x_coord, info_res_flush.rect.y_coord, + ); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); + } + + unsafe { + let mut flush_reg = pixman_region16_t::default(); + let flush_reg_ptr = &mut flush_reg as *mut pixman_region16_t; + pixman_region_init_rect( + flush_reg_ptr, + info_res_flush.rect.x_coord as i32, + info_res_flush.rect.y_coord as i32, + info_res_flush.rect.width, + info_res_flush.rect.height, + ); + for i in 0..self.num_scanouts { + // Flushes any scanouts the resource is being used on. + if res.scanouts_bitmask & (1 << i) == 0 { + continue; + } + let scanout = &self.scanouts[i as usize]; + + let mut rect_reg = pixman_region16_t::default(); + let mut final_reg = pixman_region16_t::default(); + let rect_reg_ptr = &mut rect_reg as *mut pixman_region16_t; + let final_reg_ptr = &mut final_reg as *mut pixman_region16_t; + pixman_region_init(final_reg_ptr); + pixman_region_init_rect( + rect_reg_ptr, + scanout.x as i32, + scanout.y as i32, + scanout.width, + scanout.height, + ); + + pixman_region_intersect(final_reg_ptr, flush_reg_ptr, rect_reg_ptr); + pixman_region_translate(final_reg_ptr, -(scanout.x as i32), -(scanout.y as i32)); + let extents = pixman_region_extents(final_reg_ptr); + display_graphic_update( + &scanout.con, + (*extents).x1 as i32, + (*extents).y1 as i32, + ((*extents).x2 - (*extents).x1) as i32, + ((*extents).y2 - (*extents).y1) as i32, + )?; + pixman_region_fini(rect_reg_ptr); + pixman_region_fini(final_reg_ptr); + } + pixman_region_fini(flush_reg_ptr); } + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } fn cmd_transfer_to_host_2d_params_check( -- Gitee From 663b3f1500b49c2e2b0ce1c4459f7f0122f75bee Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 00:10:45 +0800 Subject: [PATCH 1111/2187] virtio-gpu: Refactor cmd_transfer_to_host_2d() Add more check and fix error msg. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 45 ++++++++++++---------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 9aed5418f..e641a7560 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1063,29 +1063,14 @@ impl GpuIoHandler { fn cmd_transfer_to_host_2d_params_check( &mut self, info_transfer: &VirtioGpuTransferToHost2d, - ) -> u32 { - let res_idx = self - .resources_list - .iter() - .position(|x| x.resource_id == info_transfer.resource_id); - + ) -> (Option, u32) { + let (res_idx, error) = + self.get_backed_resource_idx(info_transfer.resource_id, "cmd_transfer_to_host_2d"); if res_idx.is_none() { - error!( - "GuestError: The resource_id {} in transfer to host 2d request is not existed.", - info_transfer.resource_id - ); - return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return (None, error); } let res = &self.resources_list[res_idx.unwrap()]; - if res.iov.is_empty() { - error!( - "GuestError: The resource_id {} in transfer to host 2d request don't have iov.", - info_transfer.resource_id - ); - return VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - } - if !is_rect_in_resource(&info_transfer.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {}).", @@ -1098,22 +1083,18 @@ impl GpuIoHandler { info_transfer.rect.x_coord, info_transfer.rect.y_coord, ); - return VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + (None, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER) + } else { + (res_idx, 0) } - - 0 } fn cmd_transfer_to_host_2d_update_resource( &mut self, info_transfer: &VirtioGpuTransferToHost2d, + res_idx: usize, ) { - // SAFETY: unwrap is safe because it has been checked in params check. - let res = self - .resources_list - .iter() - .find(|&x| x.resource_id == info_transfer.resource_id) - .unwrap(); + let res = &self.resources_list[res_idx]; let pixman_format; let bpp; let stride; @@ -1214,12 +1195,12 @@ impl GpuIoHandler { let mut info_transfer = VirtioGpuTransferToHost2d::default(); self.get_request(req, &mut info_transfer)?; - let errcode = self.cmd_transfer_to_host_2d_params_check(&info_transfer); - if errcode != 0 { - return self.response_nodata(errcode, req); + let (res_idx, error) = self.cmd_transfer_to_host_2d_params_check(&info_transfer); + if res_idx.is_none() { + return self.response_nodata(error, req); } - self.cmd_transfer_to_host_2d_update_resource(&info_transfer); + self.cmd_transfer_to_host_2d_update_resource(&info_transfer, res_idx.unwrap()); self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } -- Gitee From b8d0a6e5a47992194e2378e5abcd9dc49f1087fb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 04:31:12 +0800 Subject: [PATCH 1112/2187] virtio-gpu: Refactor cmd_resource_attach_backing() Get res backing ents in one go. Signed-off-by: Keqian Zhu --- util/src/aio/mod.rs | 29 ++++++- virtio/src/device/block.rs | 2 +- virtio/src/device/gpu.rs | 150 +++++++++++++++---------------------- 3 files changed, 87 insertions(+), 94 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index fd6e3f916..dc9de2e54 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -496,7 +496,7 @@ impl Aio { real_nbytes as usize, ) }; - iov_to_buf_direct(iovecs, dst).and_then(|v| { + iov_to_buf_direct(iovecs, 0, dst).and_then(|v| { if v == real_nbytes as usize { Ok(()) } else { @@ -599,11 +599,34 @@ pub fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { } /// Read iovec to buf and return the read number of bytes. -pub fn iov_to_buf_direct(iovec: &[Iovec], buf: &mut [u8]) -> Result { +pub fn iov_to_buf_direct(iovec: &[Iovec], offset: u64, buf: &mut [u8]) -> Result { + let mut iovec2: Option<&[Iovec]> = None; let mut start: usize = 0; let mut end: usize = 0; - for iov in iovec { + if offset == 0 { + iovec2 = Some(iovec); + } else { + let mut offset = offset; + for (index, iov) in iovec.iter().enumerate() { + if iov.iov_len > offset { + end = cmp::min((iov.iov_len - offset) as usize, buf.len()); + mem_to_buf(&mut buf[..end], iov.iov_base + offset)?; + if end >= buf.len() || index >= (iovec.len() - 1) { + return Ok(end); + } + start = end; + iovec2 = Some(&iovec[index + 1..]); + break; + } + offset -= iov.iov_len; + } + if iovec2.is_none() { + return Ok(0); + } + } + + for iov in iovec2.unwrap() { end = cmp::min(start + iov.iov_len as usize, buf.len()); mem_to_buf(&mut buf[start..end], iov.iov_base)?; if end >= buf.len() { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 2cb0df390..0d993ff6b 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -380,7 +380,7 @@ impl Request { // Get and check the discard segment. let mut segment = DiscardWriteZeroesSeg::default(); - iov_to_buf_direct(&self.iovec, segment.as_mut_bytes()).and_then(|v| { + iov_to_buf_direct(&self.iovec, 0, segment.as_mut_bytes()).and_then(|v| { if v as u64 == size { Ok(()) } else { diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index e641a7560..cf7a8c79f 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -33,6 +33,7 @@ use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; +use std::slice::from_raw_parts_mut; use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; use ui::console::{ @@ -41,7 +42,7 @@ use ui::console::{ DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, VmRunningStage, }; use ui::pixman::unref_pixman_image; -use util::aio::{iov_discard_front_direct, iov_from_buf_direct, iov_to_buf_direct}; +use util::aio::{iov_from_buf_direct, iov_to_buf_direct}; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -533,15 +534,11 @@ fn is_rect_in_resource(rect: &VirtioGpuRect, res: &GpuResource) -> bool { impl GpuIoHandler { fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { - if header.out_len < size_of::() as u32 { - bail!("Invalid header for gpu request: len {}.", header.out_len) - } - - iov_to_buf_direct(&header.out_iovec, req.as_mut_bytes()).and_then(|size| { + iov_to_buf_direct(&header.out_iovec, 0, req.as_mut_bytes()).and_then(|size| { if size == size_of::() { Ok(()) } else { - bail!("Invalid header for gpu request: len {}.", size) + Err(anyhow!("Invalid header for gpu request: len {}.", size)) } }) } @@ -1208,95 +1205,68 @@ impl GpuIoHandler { let mut info_attach_backing = VirtioGpuResourceAttachBacking::default(); self.get_request(req, &mut info_attach_backing)?; - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_attach_backing.resource_id) - { - let res = &mut self.resources_list[res_index]; - if !res.iov.is_empty() { - error!( - "GuestError: The resource_id {} in resource attach backing request already has iov.", - info_attach_backing.resource_id - ); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } - - if info_attach_backing.nr_entries > 16384 { - error!( - "GuestError: The nr_entries in resource attach backing request is too large ( {} > 16384).", - info_attach_backing.nr_entries - ); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } - - let esize = - size_of::() as u64 * info_attach_backing.nr_entries as u64; - if esize > req.out_len as u64 { - error!( - "GuestError: The nr_entries {} in resource attach backing request is larger than total len {}.", - info_attach_backing.nr_entries, req.out_len, - ); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } + let res_idx = self.get_resource_idx(info_attach_backing.resource_id); + if res_idx.is_none() { + error!( + "The resource_id {} in attach backing request request is not existed.", + info_attach_backing.resource_id + ); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); + } - let mut data_iovec = req.out_iovec.clone(); - // Move to entries part first. - data_iovec = iov_discard_front_direct( - &mut data_iovec, - size_of::() as u64, - ) - .unwrap() - .to_vec(); - - for i in 0..info_attach_backing.nr_entries { - if i != 0 { - data_iovec = iov_discard_front_direct( - &mut data_iovec, - size_of::() as u64, - ) - .unwrap() - .to_vec(); - } + let res = &mut self.resources_list[res_idx.unwrap()]; + if !res.iov.is_empty() { + error!( + "GuestError: The resource_id {} in resource attach backing request already has iov.", + info_attach_backing.resource_id + ); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } - let mut entry = VirtioGpuMemEntry::default(); - if let Err(e) = - iov_to_buf_direct(&data_iovec, entry.as_mut_bytes()).and_then(|size| { - if size == size_of::() { - Ok(()) - } else { - bail!( - "GuestError: Invalid size of gpu request data: len {}.", - size - ); - } - }) - { - res.iov.clear(); - error!("{:?}", e); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } + if info_attach_backing.nr_entries > 16384 { + error!( + "GuestError: The nr_entries in resource attach backing request is too large ( {} > 16384).", + info_attach_backing.nr_entries + ); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } - if let Some(iov_base) = self.mem_space.get_host_address(GuestAddress(entry.addr)) { - let iov_item = Iovec { - iov_base, - iov_len: entry.length as u64, - }; - res.iov.push(iov_item); - } else { - res.iov.clear(); - error!("GuestError: Map desc base {:?} failed.", entry.addr); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } - } - self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) - } else { + let entries = info_attach_backing.nr_entries; + let ents_size = size_of::() as u64 * entries as u64; + let head_size = size_of::() as u64; + if (req.out_len as u64) < (ents_size + head_size) { error!( - "The resource_id {} in attach backing request request is not existed.", - info_attach_backing.resource_id + "GuestError: The nr_entries {} in resource attach backing request is larger than total len {}.", + info_attach_backing.nr_entries, req.out_len, ); - self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } + + // Start reading and parsing. + let mut ents = Vec::::new(); + ents.resize(entries as usize, VirtioGpuMemEntry::default()); + let ents_buf = + unsafe { from_raw_parts_mut(ents.as_mut_ptr() as *mut u8, ents_size as usize) }; + iov_to_buf_direct(&req.out_iovec, head_size, ents_buf).and_then(|v| { + if v as u64 == ents_size { + Ok(()) + } else { + Err(anyhow!("Load no enough ents when attach backing")) + } + })?; + + for entry in ents.iter() { + let iov_base = self + .mem_space + .get_host_address(GuestAddress(entry.addr)) + .with_context(|| format!("Map entry base {:?} failed", entry.addr))?; + let iov_item = Iovec { + iov_base, + iov_len: entry.length as u64, + }; + res.iov.push(iov_item); + } + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } fn cmd_resource_detach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { -- Gitee From e0310d67ba2a22873fc9eb258f35d2ec654599e0 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 04:39:28 +0800 Subject: [PATCH 1113/2187] virtio-gpu: Refactor cmd_resource_detach_backing() Use helper function to reduce code. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index cf7a8c79f..d264b152c 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1273,28 +1273,16 @@ impl GpuIoHandler { let mut info_detach_backing = VirtioGpuResourceDetachBacking::default(); self.get_request(req, &mut info_detach_backing)?; - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_detach_backing.resource_id) - { - let res = &mut self.resources_list[res_index]; - if res.iov.is_empty() { - error!( - "GuestError: The resource_id {} in resource detach backing request don't have iov.", - info_detach_backing.resource_id - ); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } - res.iov.clear(); - self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) - } else { - error!( - "GuestError: The resource_id {} in detach backing request request is not existed.", - info_detach_backing.resource_id - ); - self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req) + let (res_idx, error) = self.get_backed_resource_idx( + info_detach_backing.resource_id, + "cmd_resource_detach_backing", + ); + if res_idx.is_none() { + return self.response_nodata(error, req); } + + self.resources_list[res_idx.unwrap()].iov.clear(); + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } fn process_control_queue(&mut self, mut req_queue: Vec) -> Result<()> { -- Gitee From 575bf9d25c57ad6620040740ea6882dd0052f1fe Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 22 May 2023 13:47:18 +0800 Subject: [PATCH 1114/2187] virtio-gpu: keep resolution same after reboot After reboot, use the same resolution set by the user as before. Signed-off-by: wubinfeng --- virtio/src/device/gpu.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 6207bd90d..d04b2c271 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1636,6 +1636,8 @@ impl VirtioDevice for Gpu { } let mut output_states = self.output_states.lock().unwrap(); + output_states[0].width = self.cfg.xres; + output_states[0].height = self.cfg.yres; for i in 0..self.cfg.max_outputs { let gpu_opts = Arc::new(GpuOpts { output_states: self.output_states.clone(), @@ -1755,12 +1757,6 @@ impl VirtioDevice for Gpu { } self.interrupt_cb = Some(interrupt_cb.clone()); - - let mut output_states = self.output_states.lock().unwrap(); - output_states[0].width = self.cfg.xres; - output_states[0].height = self.cfg.yres; - drop(output_states); - let mut scanouts = vec![]; for con in &self.consoles { let gpu_opts = Arc::new(GpuOpts { -- Gitee From 7e97dec3e00792ac118a33b410c1ee10b1b0d30b Mon Sep 17 00:00:00 2001 From: mayunlong Date: Mon, 22 May 2023 17:05:35 +0800 Subject: [PATCH 1115/2187] vsock: fix mircovm vhost_vsock. in commit 7ccf51a6, we used irqfd instead of eventfd, as a result, the vsock of the microvm is unavailable. Now the old event notification is revert to microvm. Signed-off-by: mayunlong --- machine/src/lib.rs | 1 + virtio/src/vhost/kernel/mod.rs | 57 ++++++++++++++++++++++++++++++-- virtio/src/vhost/kernel/vsock.rs | 46 +++++++++++++++++++++++--- 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1eb36c4a9..36a488666 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -276,6 +276,7 @@ pub trait MachineOps { let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); if cfg_args.contains("vhost-vsock-device") { let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); + vsock.lock().unwrap().disable_irqfd = true; MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 8f819ffd6..19638e1a8 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -19,19 +19,25 @@ pub use vsock::{Vsock, VsockState}; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use log::debug; +use log::{debug, error}; use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; +use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; -use super::super::QueueConfig; -use super::VhostOps; +use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; +use super::{VhostNotify, VhostOps}; use crate::VirtioError; use anyhow::{anyhow, Context, Result}; @@ -449,3 +455,48 @@ impl VhostOps for VhostBackend { Ok(()) } } + +pub struct VhostIoHandler { + interrupt_cb: Arc, + host_notifies: Vec, + device_broken: Arc, +} + +impl EventNotifierHelper for VhostIoHandler { + fn internal_notifiers(vhost_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let vhost = vhost_handler.clone(); + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let locked_vhost_handler = vhost.lock().unwrap(); + if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { + return None; + } + for host_notify in locked_vhost_handler.host_notifies.iter() { + if let Err(e) = (locked_vhost_handler.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&host_notify.queue.lock().unwrap()), + false, + ) { + error!( + "Failed to trigger interrupt for vhost device, error is {:?}", + e + ); + } + } + None as Option> + }); + for host_notify in vhost_handler.lock().unwrap().host_notifies.iter() { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + host_notify.notify_evt.as_raw_fd(), + None, + EventSet::IN, + vec![handler.clone()], + )); + } + + notifiers + } +} diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index db0e89a61..811e134d6 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -21,14 +21,15 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use address_space::AddressSpace; use byteorder::{ByteOrder, LittleEndian}; use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; -use machine_manager::event_loop::unregister_event_helper; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::loop_context::EventNotifierHelper; use util::num_ops::read_u32; -use super::super::VhostOps; -use super::{VhostBackend, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; +use super::super::{VhostNotify, VhostOps}; +use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_VSOCK, }; @@ -106,6 +107,8 @@ pub struct Vsock { broken: Arc, /// Save irqfd used for vhost-vsock call_events: Vec>, + /// Whether irqfd can be used. + pub disable_irqfd: bool, } impl Vsock { @@ -120,6 +123,7 @@ impl Vsock { deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), call_events: Vec::new(), + disable_irqfd: false, } } @@ -246,6 +250,10 @@ impl VirtioDevice for Vsock { } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { + if self.disable_irqfd { + return Err(anyhow!("The irqfd cannot be used on the current machine.")); + } + for fd in queue_evts.iter() { self.call_events.push(fd.clone()); } @@ -265,6 +273,7 @@ impl VirtioDevice for Vsock { let cid = self.vsock_cfg.guest_cid; // The receive queue and transmit queue will be handled in vhost. let vhost_queues = queues[..2].to_vec(); + let mut host_notifies = Vec::new(); // This event queue will be handled. self.event_queue = Some(queues[2].clone()); self.interrupt_cb = Some(interrupt_cb.clone()); @@ -308,8 +317,22 @@ impl VirtioDevice for Vsock { })?; drop(queue); + let event = if self.disable_irqfd { + let host_notify = VhostNotify { + notify_evt: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| VirtioError::EventFdCreate)?, + ), + queue: queue_mutex.clone(), + }; + let event = host_notify.notify_evt.clone(); + host_notifies.push(host_notify); + event + } else { + self.call_events[queue_index].clone() + }; backend - .set_vring_call(queue_index, self.call_events[queue_index].clone()) + .set_vring_call(queue_index, event) .with_context(|| { format!("Failed to set vring call for vsock, index: {}", queue_index) })?; @@ -318,6 +341,16 @@ impl VirtioDevice for Vsock { backend.set_guest_cid(cid)?; backend.set_running(true)?; + if self.disable_irqfd { + let handler = VhostIoHandler { + interrupt_cb: interrupt_cb.clone(), + host_notifies, + device_broken: self.broken.clone(), + }; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + } + self.broken.store(false, Ordering::SeqCst); Ok(()) @@ -325,7 +358,10 @@ impl VirtioDevice for Vsock { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.deactivate_evts)?; - self.call_events.clear(); + if !self.disable_irqfd { + self.call_events.clear(); + } + Ok(()) } -- Gitee From fda92e615ea8c083bb0de66676adaddaafb28eef Mon Sep 17 00:00:00 2001 From: mayunlong Date: Mon, 22 May 2023 20:56:23 +0800 Subject: [PATCH 1116/2187] vhost-net: fix microvm vhost_net in commit d005cb4,we used irqfd instead of eventfd, as a result, the vhost-net of the microvm is unavailable. Now the old event notification is revert to microvm. Signed-off-by: mayunlong --- machine/src/micro_vm/mod.rs | 1 + virtio/src/vhost/kernel/net.rs | 48 +++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 6ccafea23..178467043 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -710,6 +710,7 @@ impl MachineOps for LightMachine { let device_cfg = parse_net(vm_config, cfg_args)?; if device_cfg.vhost_type.is_some() { let net = Arc::new(Mutex::new(VhostKern::Net::new(&device_cfg, &self.sys_mem))); + net.lock().unwrap().disable_irqfd = true; let device = VirtioMmioDevice::new(&self.sys_mem, net); self.realize_virtio_mmio_device(device)?; } else { diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 18da527dd..0e4efd246 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -28,8 +28,8 @@ use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::VhostOps; -use super::{VhostBackend, VhostVringFile, VHOST_NET_SET_BACKEND}; +use super::super::{VhostNotify, VhostOps}; +use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, @@ -94,6 +94,8 @@ pub struct Net { broken: Arc, /// Save irqfd used for vhost-net. call_events: Vec>, + /// Whether irqfd can be used. + pub disable_irqfd: bool, } impl Net { @@ -108,6 +110,7 @@ impl Net { deactivate_evts: Vec::new(), broken: Arc::new(AtomicBool::new(false)), call_events: Vec::new(), + disable_irqfd: false, } } } @@ -248,6 +251,10 @@ impl VirtioDevice for Net { } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { + if self.disable_irqfd { + return Err(anyhow!("The irqfd cannot be used on the current machine.")); + } + for fd in queue_evts.iter() { self.call_events.push(fd.clone()); } @@ -290,6 +297,7 @@ impl VirtioDevice for Net { let queue_pairs = queue_num / 2; for index in 0..queue_pairs { + let mut host_notifies = Vec::new(); let backend = match &self.backends { None => return Err(anyhow!("Failed to get backend for vhost net")), Some(backends) => backends @@ -343,8 +351,22 @@ impl VirtioDevice for Net { drop(queue); + let event = if self.disable_irqfd { + let host_notify = VhostNotify { + notify_evt: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| VirtioError::EventFdCreate)?, + ), + queue: queue_mutex.clone(), + }; + let event = host_notify.notify_evt.clone(); + host_notifies.push(host_notify); + event + } else { + self.call_events[queue_index].clone() + }; backend - .set_vring_call(queue_index, self.call_events[queue_index].clone()) + .set_vring_call(queue_index, event) .with_context(|| { format!( "Failed to set vring call for vhost net, index: {}", @@ -365,6 +387,21 @@ impl VirtioDevice for Net { ) })?; } + + if self.disable_irqfd { + let handler = VhostIoHandler { + interrupt_cb: interrupt_cb.clone(), + host_notifies, + device_broken: self.broken.clone(), + }; + let notifiers = + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper( + notifiers, + self.net_cfg.iothread.as_ref(), + &mut self.deactivate_evts, + )?; + } } self.broken.store(false, Ordering::SeqCst); @@ -373,7 +410,10 @@ impl VirtioDevice for Net { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; - self.call_events.clear(); + if !self.disable_irqfd { + self.call_events.clear(); + } + Ok(()) } -- Gitee From 6ccfbba9d6611046fd98ace9bd7f6e846621fb0f Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 10 May 2023 10:15:50 +0800 Subject: [PATCH 1117/2187] camera: set dwMaxVideoFrameSize by width and height According to 4.3.1.1, for an IN endpoint, the dwMaxVideoFrameSize is set by the devices. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index ccc7f931e..bccd8fc01 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -791,22 +791,20 @@ impl UsbCamera { match device_req.request { SET_CUR => match cs { VS_PROBE_CONTROL => { - self.vs_control.bFormatIndex = vs_control.bFormatIndex; - self.vs_control.bFrameIndex = vs_control.bFrameIndex; - self.vs_control.dwMaxVideoFrameSize = vs_control.dwMaxVideoFrameSize; - self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; + let fmt = self + .camera_dev + .lock() + .unwrap() + .get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; + self.update_vs_control(&fmt, &vs_control)?; } VS_COMMIT_CONTROL => { - self.vs_control.bFormatIndex = vs_control.bFormatIndex; - self.vs_control.bFrameIndex = vs_control.bFrameIndex; - self.vs_control.dwMaxVideoFrameSize = vs_control.dwMaxVideoFrameSize; - self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; let fmt = self .camera_dev .lock() .unwrap() .get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; - + self.update_vs_control(&fmt, &vs_control)?; self.activate(&fmt) .with_context(|| "Failed to activate device")?; } @@ -821,6 +819,18 @@ impl UsbCamera { Ok(()) } + fn update_vs_control( + &mut self, + fmt: &CamBasicFmt, + vs_control: &VideoStreamingControl, + ) -> Result<()> { + self.vs_control.bFormatIndex = vs_control.bFormatIndex; + self.vs_control.bFrameIndex = vs_control.bFrameIndex; + self.vs_control.dwMaxVideoFrameSize = get_video_frame_size(fmt.width, fmt.height)?; + self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; + Ok(()) + } + fn reset_vs_control(&mut self) { let default_fmt = self .camera_dev -- Gitee From e926e5f0b3437e4875b92b4a3a5d988cfbf161ca Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 10 May 2023 10:20:49 +0800 Subject: [PATCH 1118/2187] camera: add demo camera backend Add Demo camera backend which is used in MST. Signed-off-by: zhouli57 --- Cargo.lock | 3 + devices/Cargo.toml | 3 + devices/src/camera_backend/demo.rs | 580 ++++++++++++++++++++++++++++- devices/src/camera_backend/mod.rs | 17 +- devices/src/lib.rs | 1 + devices/src/usb/mod.rs | 1 + 6 files changed, 593 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb0b904fa..7b7de6694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -348,6 +348,7 @@ dependencies = [ "address_space", "anyhow", "byteorder", + "cairo-rs", "drm-fourcc", "hypervisor", "kvm-bindings", @@ -362,8 +363,10 @@ dependencies = [ "migration_derive", "once_cell", "pci", + "rand", "rusb", "serde", + "serde_json", "serial_test", "strum 0.24.1", "strum_macros 0.24.3", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 91d7304d4..2ed596caf 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -20,6 +20,8 @@ once_cell = "1.9.0" strum = "0.24.1" strum_macros = "0.24.3" v4l2-sys-mit = "0.2.0" +serde_json = "1.0" +rand = "0.8.5" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } @@ -38,6 +40,7 @@ pulse = { version = "2.0", package = "libpulse-binding" } psimple = { version = "2.0", package = "libpulse-simple-binding" } rusb = "0.9" libusb1-sys = "0.6.4" +cairo-rs = "0.14.9" [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index fd17f7f54..10df2ac38 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -12,12 +12,580 @@ //! Demo backend for vCamera device, that helps for testing. -use super::CamFmt; +use std::fs::read_to_string; +use std::ops::Deref; +use std::sync::{Arc, Mutex}; -#[allow(dead_code)] -pub struct DemoHostDev { - buffer: Vec, // buffer that stores video frame +use anyhow::{bail, Context, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use cairo::{Format, ImageSurface}; +use log::{debug, error, info}; +use rand::{thread_rng, Rng}; +use serde::{Deserialize, Serialize}; - hostfmt: CamFmt, // the combination of video formats that the hardware supports - pub cur_fmt: CamFmt, // the combination of video formats that we negotiated with the hardware +use util::aio::{mem_from_buf, Iovec}; + +use super::INTERVALS_PER_SEC; +use crate::camera_backend::{ + CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, + CameraNotifyCallback, FmtType, +}; + +#[derive(Debug)] +enum RgbColor { + Red, + Orange, + Yellow, + Green, + Blue, + Indigo, + Violet, + White, + Black, +} + +fn get_rgb_color(color: &RgbColor) -> (u8, u8, u8) { + match color { + RgbColor::Red => (0xff, 0x0, 0x0), + RgbColor::Orange => (0xff, 0x80, 0x0), + RgbColor::Yellow => (0xff, 0xff, 0x0), + RgbColor::Green => (0x0, 0xff, 0x0), + RgbColor::Blue => (0x0, 0x0, 0xff), + RgbColor::Indigo => (0x4b, 0x0, 0x82), + RgbColor::Violet => (0xee, 0x82, 0xee), + RgbColor::White => (0xff, 0xff, 0xff), + RgbColor::Black => (0x0, 0x0, 0x0), + } +} + +impl From for RgbColor { + fn from(t: u8) -> Self { + match t { + 0 => RgbColor::Red, + 1 => RgbColor::Orange, + 2 => RgbColor::Yellow, + 3 => RgbColor::Green, + 4 => RgbColor::Blue, + 5 => RgbColor::Indigo, + 6 => RgbColor::Violet, + 7 => RgbColor::White, + _ => RgbColor::Black, + } + } +} + +#[derive(Default)] +struct FrameImage { + image: Vec, + used_len: u64, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum DeviceState { + Uninitialized, + Running, + Exit, +} + +enum ImageMode { + Default, + Random, +} + +impl From<&str> for ImageMode { + fn from(t: &str) -> Self { + match t { + "default" => ImageMode::Default, + "random" => ImageMode::Random, + _ => ImageMode::Default, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct DeviceConfig { + check_interval: u64, + image_mode: String, + force_frame_len: Option, +} + +impl Default for DeviceConfig { + fn default() -> Self { + Self { + check_interval: 50, + image_mode: String::from("default"), + force_frame_len: None, + } + } +} + +/// Demo camera backend used for test. +pub struct DemoCamera { + id: String, + /// Device config path. + config_path: String, + /// Frame image data. + frame_image: Arc>, + /// Callback to used to notify when data is coming. + notify_cb: Option, + /// Callback to used to notify the broken. + broken_cb: Option, + /// Current format. + cur_format: Arc>, + /// Format list supported by the device. + format_list: Vec, + /// Device state. + state: Arc>, +} + +impl DemoCamera { + pub fn new(id: String, config_path: String) -> Result { + Ok(DemoCamera { + id, + config_path, + frame_image: Arc::new(Mutex::new(FrameImage::default())), + notify_cb: None, + broken_cb: None, + cur_format: Arc::new(Mutex::new(CamBasicFmt::default())), + format_list: build_format_list(), + state: Arc::new(Mutex::new(DeviceState::Uninitialized)), + }) + } + + fn start_worker(&mut self) -> Result<()> { + let cloned_fmt = self.cur_format.clone(); + let cloned_frame = self.frame_image.clone(); + let cloned_notify = self.notify_cb.clone(); + let cloned_state = self.state.clone(); + let cloned_path = self.config_path.clone(); + + std::thread::Builder::new() + .name("demo camera worker".to_string()) + .spawn(move || { + let mut image_frame = ImageFrame::default(); + let config = read_config(&cloned_path).unwrap_or_else(|_| DeviceConfig::default()); + info!("Demo device config {:?}", config); + loop { + let locked_state = cloned_state.lock().unwrap(); + match *locked_state { + DeviceState::Uninitialized => { + std::thread::sleep(std::time::Duration::from_millis( + config.check_interval, + )); + continue; + } + DeviceState::Running => (), + DeviceState::Exit => break, + } + drop(locked_state); + let mut locked_frame = cloned_frame.lock().unwrap(); + if locked_frame.used_len == 0 { + // Build next frame. + let locked_fmt = cloned_fmt.lock().unwrap(); + if let Some(len) = config.force_frame_len { + locked_frame.used_len = len; + locked_frame.image = vec![0xfe; len as usize]; + debug!("Demo camera force used_len {}", locked_frame.used_len); + } else { + locked_frame.image = match image_frame.build_image( + &ImageMode::from(config.image_mode.as_str()), + &locked_fmt.fmttype, + locked_fmt.width, + locked_fmt.height, + ) { + Ok(img) => img, + Err(e) => { + error!("Failed to build image {:?}", e); + break; + } + }; + locked_frame.used_len = locked_frame.image.len() as u64; + debug!("Demo camera used_len {}", locked_frame.used_len); + } + if let Some(notify) = cloned_notify.as_ref() { + notify(); + } + let interval = if locked_fmt.fps != 0 { + 1000 / locked_fmt.fps as u64 + } else { + 20 + }; + drop(locked_frame); + std::thread::sleep(std::time::Duration::from_millis(interval)); + } + } + })?; + Ok(()) + } +} + +#[derive(Default)] +struct ImageFrame { + frame_idx: u64, +} + +impl ImageFrame { + fn build_image( + &mut self, + image_mode: &ImageMode, + format: &FmtType, + width: u32, + height: u32, + ) -> Result> { + const FRAME_IDX_LIMIT: u64 = 1000; + let color = match image_mode { + ImageMode::Default => RgbColor::Red, + ImageMode::Random => RgbColor::from(self.frame_idx as u8 % 8), + }; + debug!("Demo Image color {:?}", color); + let mut surface = ImageSurface::create(Format::Rgb24, width as i32, height as i32)?; + let cr = cairo::Context::new(&surface)?; + let (r, g, b) = get_rgb_color(&color); + cr.set_source_rgb(r as f64, g as f64, b as f64); + cr.rectangle(0.0, 0.0, width as f64, height as f64); + cr.fill()?; + cr.paint()?; + drop(cr); + let data = surface.data()?; + let image = match format { + FmtType::Mjpg => build_fake_mjpg(width, height), + FmtType::Yuy2 => convert_to_yuy2(data.deref(), width, height), + FmtType::Rgb565 => data.deref().to_vec(), + }; + self.frame_idx += 1; + if self.frame_idx > FRAME_IDX_LIMIT { + self.frame_idx = 0; + } + Ok(image) + } +} + +fn read_config(path: &str) -> Result { + let str = read_to_string(&path)?; + let conf = serde_json::from_str::(&str)?; + Ok(conf) +} + +fn build_format_list() -> Vec { + vec![build_yuy2_list(), build_mjpg_list(), build_rgb565_list()] +} + +fn build_yuy2_list() -> CameraFormatList { + CameraFormatList { + format: FmtType::Yuy2, + fmt_index: 1, + frame: vec![ + CameraFrame { + width: 1280, + height: 720, + interval: INTERVALS_PER_SEC / 10, + index: 1, + }, + CameraFrame { + width: 1920, + height: 1280, + interval: INTERVALS_PER_SEC / 5, + index: 2, + }, + CameraFrame { + width: 960, + height: 540, + interval: INTERVALS_PER_SEC / 30, + index: 3, + }, + CameraFrame { + width: 640, + height: 480, + interval: INTERVALS_PER_SEC / 30, + index: 4, + }, + CameraFrame { + width: 480, + height: 240, + interval: INTERVALS_PER_SEC / 30, + index: 5, + }, + CameraFrame { + width: 160, + height: 120, + interval: INTERVALS_PER_SEC / 60, + index: 6, + }, + ], + } +} + +fn build_mjpg_list() -> CameraFormatList { + CameraFormatList { + format: FmtType::Mjpg, + fmt_index: 2, + frame: vec![ + CameraFrame { + width: 1920, + height: 1080, + interval: INTERVALS_PER_SEC / 30, + index: 1, + }, + CameraFrame { + width: 1280, + height: 720, + interval: INTERVALS_PER_SEC / 30, + index: 2, + }, + CameraFrame { + width: 960, + height: 540, + interval: INTERVALS_PER_SEC / 30, + index: 3, + }, + CameraFrame { + width: 480, + height: 240, + interval: INTERVALS_PER_SEC / 30, + index: 4, + }, + ], + } +} + +fn build_rgb565_list() -> CameraFormatList { + CameraFormatList { + format: FmtType::Rgb565, + fmt_index: 3, + frame: vec![ + CameraFrame { + width: 1280, + height: 720, + interval: INTERVALS_PER_SEC / 10, + index: 1, + }, + CameraFrame { + width: 640, + height: 480, + interval: INTERVALS_PER_SEC / 30, + index: 2, + }, + CameraFrame { + width: 480, + height: 240, + interval: INTERVALS_PER_SEC / 30, + index: 3, + }, + ], + } +} + +impl CameraHostdevOps for DemoCamera { + fn init(&self) -> Result<()> { + Ok(()) + } + + fn is_camera(&self) -> Result { + Ok(true) + } + + fn get_fmt(&self) -> Result> { + Ok(self.format_list.clone()) + } + + fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { + *self.cur_format.lock().unwrap() = *cam_fmt; + info!("Demo camera backend set format {:?}", cam_fmt); + Ok(()) + } + + fn set_ctl(&self) -> Result<()> { + Ok(()) + } + + fn video_stream_on(&mut self) -> Result<()> { + if *self.state.lock().unwrap() == DeviceState::Running { + return Ok(()); + } + info!("Demo camera backend {} stream on", self.id); + let mut locked_state = self.state.lock().unwrap(); + *locked_state = DeviceState::Running; + drop(locked_state); + self.start_worker() + } + + fn video_stream_off(&mut self) -> Result<()> { + if *self.state.lock().unwrap() == DeviceState::Exit { + return Ok(()); + } + info!("Demo camera backend {} stream off", self.id); + let mut locked_state = self.state.lock().unwrap(); + *locked_state = DeviceState::Exit; + Ok(()) + } + + fn list_format(&mut self) -> Result> { + Ok(self.format_list.clone()) + } + + fn reset(&mut self) { + info!("Demo camera backend {} reset", self.id); + let mut locked_state = self.state.lock().unwrap(); + *locked_state = DeviceState::Exit; + let mut locked_frame = self.frame_image.lock().unwrap(); + locked_frame.used_len = 0; + } + + fn get_frame_size(&self) -> usize { + self.frame_image.lock().unwrap().used_len as usize + } + + fn next_frame(&mut self) -> Result<()> { + let mut locked_frame = self.frame_image.lock().unwrap(); + locked_frame.used_len = 0; + Ok(()) + } + + fn get_frame(&self, iovecs: &[Iovec], frame_offset: usize, len: usize) -> Result { + let locked_frame = self.frame_image.lock().unwrap(); + if frame_offset + len > locked_frame.used_len as usize { + bail!("Invalid frame offset {} or len {}", frame_offset, len); + } + let mut copyed = 0; + for iov in iovecs { + if len == copyed { + break; + } + let cnt = std::cmp::min(iov.iov_len as usize, len - copyed); + let start = frame_offset + copyed; + let end = start + cnt; + let tmp = &locked_frame.image[start..end]; + mem_from_buf(tmp, iov.iov_base) + .with_context(|| format!("Failed to write data to {:x}", iov.iov_base))?; + copyed += cnt; + } + Ok(copyed) + } + + fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result { + let mut out = CamBasicFmt::default(); + for fmt in &self.format_list { + if fmt.fmt_index != format_index { + continue; + } + out.fmttype = fmt.format; + for frm in &fmt.frame { + if frm.index != frame_index { + continue; + } + out.width = frm.width; + out.height = frm.height; + out.fps = INTERVALS_PER_SEC + .checked_div(frm.interval) + .with_context(|| { + format!( + "Invalid interval {} for format/frame {}:{}", + frm.interval, format_index, frame_index + ) + })?; + return Ok(out); + } + } + bail!( + "format/frame with idx {}/{} is not found", + format_index, + frame_index + ); + } + + fn register_notify_cb(&mut self, cb: CameraNotifyCallback) { + self.notify_cb = Some(cb); + } + + fn register_broken_cb(&mut self, cb: CameraBrokenCallback) { + self.broken_cb = Some(cb); + } +} + +fn clip(x: i32) -> u8 { + if x > 255 { + 255 + } else if x < 0 { + 0 + } else { + x as u8 + } +} + +fn convert_to_yuy2(source: &[u8], width: u32, height: u32) -> Vec { + let pixbytes = 4; + let sz = width * height * 2; + let mut yuv = vec![0; sz as usize]; + for x in 0..height { + for y in 0..(width / 2) { + let offset = x * width * pixbytes + y * pixbytes * 2; + let src = &source[offset as usize..]; + let val = LittleEndian::read_i32(src); + let r1 = (val >> 16) & 0xff; + let g1 = (val >> 8) & 0xff; + let b1 = val & 0xff; + let src = &source[pixbytes as usize..]; + let val = LittleEndian::read_i32(src); + let r2 = (val >> 16) & 0xff; + let g2 = (val >> 8) & 0xff; + let b2 = val & 0xff; + + let y1 = clip(((66 * r1 + 129 * g1 + 25 * b1 + 128) >> 8) + 16); + let u1 = clip( + (((-38 * r1 - 74 * g1 + 112 * b1 + 128) >> 8) + + ((-38 * r2 - 74 * g2 + 112 * b2 + 128) >> 8)) + / 2 + + 128, + ); + let y2 = clip(((66 * r2 + 129 * g2 + 25 * b2 + 128) >> 8) + 16); + let v1 = clip( + (((112 * r1 - 94 * g1 - 18 * b1 + 128) >> 8) + + ((112 * r2 - 94 * g2 - 18 * b2 + 128) >> 8)) + / 2 + + 128, + ); + let mut dst = (x * width * 2 + y * 4) as usize; + yuv[dst] = y1; + dst += 1; + yuv[dst] = u1; + dst += 1; + yuv[dst] = y2; + dst += 1; + yuv[dst] = v1; + } + } + yuv +} + +// NOTE: Fake mjpg data, which is used to simulate frame data of different lengths. +fn build_fake_mjpg(width: u32, height: u32) -> Vec { + let mut rng = thread_rng(); + let len = rng.gen_range((width * height / 20)..(width * height / 4)); + let start = vec![0xff, 0xd8, 0xff, 0xe0]; + let data = vec![0xfc; len as usize]; + let end = vec![0xff, 0xf9]; + [start, data, end].concat() +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_yuy2() { + let mut frame = ImageFrame::default(); + let buf = frame + .build_image(&ImageMode::Default, &FmtType::Yuy2, 2, 2) + .unwrap(); + assert_eq!(buf, [82, 90, 82, 240, 82, 90, 82, 240]); + } + + #[test] + fn test_rgb() { + let mut frame = ImageFrame::default(); + let buf = frame + .build_image(&ImageMode::Default, &FmtType::Rgb565, 1, 1) + .unwrap(); + assert_eq!(buf, [0, 0, 255, 255]); + } } diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index e37fb26b5..64da5dbd6 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -22,7 +22,7 @@ use std::sync::{Arc, Mutex}; use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; use util::aio::Iovec; -use self::v4l2::V4l2CameraBackend; +use self::{demo::DemoCamera, v4l2::V4l2CameraBackend}; /// Frame interval in 100ns units. pub const INTERVALS_PER_SEC: u32 = 10_000_000; @@ -179,16 +179,21 @@ pub trait CameraHostdevOps: Send + Sync { } pub fn camera_ops(config: UsbCameraConfig) -> Result>> { - let cam = match config.backend { - CamBackendType::V4l2 => V4l2CameraBackend::new( + let cam: Arc> = match config.backend { + CamBackendType::V4l2 => Arc::new(Mutex::new(V4l2CameraBackend::new( config.drive.id.clone().unwrap(), config.drive.path.clone().with_context(|| { ConfigError::FieldIsMissing("path".to_string(), "V4L2".to_string()) })?, config.iothread, - )?, - CamBackendType::Demo => bail!("Not supported type"), + )?)), + CamBackendType::Demo => Arc::new(Mutex::new(DemoCamera::new( + config.id.clone().unwrap(), + config.path.with_context(|| { + ConfigError::FieldIsMissing("path".to_string(), "Demo".to_string()) + })?, + )?)), }; - Ok(Arc::new(Mutex::new(cam))) + Ok(cam) } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 4655644bb..eb7fbb7b9 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -17,6 +17,7 @@ //! - legacy devices, such as serial devices pub mod acpi; +#[cfg(not(target_env = "musl"))] pub mod camera_backend; mod interrupt_controller; pub mod legacy; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index b927d149b..e2387cba3 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -16,6 +16,7 @@ pub use error::UsbError; #[cfg(not(target_env = "musl"))] pub mod camera; +#[cfg(not(target_env = "musl"))] pub mod camera_media_type_guid; pub mod config; mod descriptor; -- Gitee From 54844053c394e0f1b4651b748235e29efa9c8e12 Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Wed, 26 Apr 2023 22:37:37 +0800 Subject: [PATCH 1119/2187] camera: test: add basic testcase for descriptor Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 12 +- tests/mod_test/src/libdriver/usb.rs | 231 ++++++++++++++++++++-------- 2 files changed, 170 insertions(+), 73 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index bccd8fc01..ffe541e20 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -50,20 +50,20 @@ const UVC_VENDOR_ID: u16 = 0xB74C; // The first 4 chars of "VIDEO", 5 substitutes V. const UVC_PRODUCT_ID: u16 = 0x51DE; -const INTERFACE_ID_CONTROL: u8 = 0; -const INTERFACE_ID_STREAMING: u8 = 1; +pub const INTERFACE_ID_CONTROL: u8 = 0; +pub const INTERFACE_ID_STREAMING: u8 = 1; const TERMINAL_ID_INPUT_TERMINAL: u8 = 1; const TERMINAL_ID_OUTPUT_TERMINAL: u8 = 2; -const ENDPOINT_ID_STREAMING: u8 = 0x1; +pub const ENDPOINT_ID_STREAMING: u8 = 0x1; const VS_INTERFACE_NUM: u8 = 1; // According to UVC specification 1.5 // A.2. Video Interface Subclass Codes -const SC_VIDEOCONTROL: u8 = 0x01; -const SC_VIDEOSTREAMING: u8 = 0x02; -const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; +pub const SC_VIDEOCONTROL: u8 = 0x01; +pub const SC_VIDEOSTREAMING: u8 = 0x02; +pub const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; // A.3. Video Interface Protocol Codes const PC_PROTOCOL_UNDEFINED: u8 = 0x0; // A.4. Video Class-Specific Descriptor Types diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index bed53c8f6..0cb512887 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -30,6 +30,9 @@ use super::{ use crate::libdriver::pci::{PciMsixOps, PCI_DEVICE_ID}; use crate::libtest::{test_init, TestState}; use devices::usb::{ + camera::{ + INTERFACE_ID_CONTROL, SC_VIDEOCONTROL, SC_VIDEOSTREAMING, SC_VIDEO_INTERFACE_COLLECTION, + }, config::*, hid::{ HID_GET_IDLE, HID_GET_PROTOCOL, HID_GET_REPORT, HID_SET_IDLE, HID_SET_PROTOCOL, @@ -134,10 +137,12 @@ pub const PRIMARY_INTERRUPTER_ID: usize = 0; pub const XHCI_PCI_SLOT_NUM: u8 = 0x5; pub const XHCI_PCI_FUN_NUM: u8 = 0; +#[derive(Eq, PartialEq)] enum UsbDeviceType { Tablet, Keyboard, Storage, + Camera, Other, } @@ -604,6 +609,9 @@ impl TestXhciPciDevice { UsbDeviceType::Storage => { assert_eq!(buf, [3, 0]); } + UsbDeviceType::Camera => { + assert_eq!(buf, [3, 0]); + } _ => {} } @@ -980,6 +988,11 @@ impl TestXhciPciDevice { endpoint_type.push(2); endpoint_offset.push(0xa0); } + UsbDeviceType::Camera => { + endpoint_id.push(3); + endpoint_type.push(6); + endpoint_offset.push(0x80); + } _ => { endpoint_id.push(3); endpoint_type.push(2); @@ -1572,6 +1585,8 @@ impl TestXhciPciDevice { UsbDeviceType::Keyboard } else if *self.device_config.get("storage").unwrap_or(&false) { UsbDeviceType::Storage + } else if *self.device_config.get("camera").unwrap_or(&false) { + UsbDeviceType::Camera } else { UsbDeviceType::Other }; @@ -1579,45 +1594,79 @@ impl TestXhciPciDevice { usb_device_type } - pub fn get_usb_descriptor(&mut self, slot_id: u32) { + fn get_iad_desc(&mut self, offset: &mut u64, addr: u64) { let usb_device_type = self.get_usb_device_type(); - // device descriptor - self.get_device_descriptor(slot_id); - self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); - let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = - self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, USB_DT_DEVICE_SIZE as u64); - // descriptor type - assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_DEVICE); - // bcdUSB - match usb_device_type { - UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { - assert_eq!(buf[2..4], [0, 1]); - } - UsbDeviceType::Storage => { - assert_eq!(buf[2..4], [0, 2]); - } - _ => {} + if usb_device_type != UsbDeviceType::Camera { + return; } - // config descriptor - self.get_config_descriptor(slot_id); - self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); - let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let addr = evt.ptr - TRB_SIZE as u64; - let mut offset = 0; - let buf = - self.get_transfer_data_indirect_with_offset(addr, USB_DT_CONFIG_SIZE as usize, offset); + + // 1. IAD header descriptor + *offset += USB_DT_CONFIG_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 8 as usize, *offset); + // descriptor type - assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_CONFIG); - // configure value - assert_eq!(buf[5], 1); - offset += USB_DT_CONFIG_SIZE as u64; + assert_eq!(buf[1], USB_DT_INTERFACE_ASSOCIATION); + // class + assert_eq!(buf[4], USB_CLASS_VIDEO); + // subclass + assert_eq!(buf[5], SC_VIDEO_INTERFACE_COLLECTION); + + //2. VC interface + *offset += 8; + let buf = self.get_transfer_data_indirect_with_offset( + addr, + USB_DT_INTERFACE_SIZE as usize, + *offset, + ); + + assert_eq!(buf[1], USB_DT_INTERFACE); + assert_eq!(buf[2], INTERFACE_ID_CONTROL); + assert_eq!(buf[5], USB_CLASS_VIDEO); + assert_eq!(buf[6], SC_VIDEOCONTROL); + + // get total vc length from its header descriptor + *offset += USB_DT_INTERFACE_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 0xd as usize, *offset); + + let total = u16::from_le_bytes(buf[5..7].try_into().unwrap()); + let remained = total - 0xd; + + *offset += 0xd; + let _buf = self.get_transfer_data_indirect_with_offset(addr, remained as usize, *offset); + + //3. VS interface + *offset += remained as u64; + let buf = self.get_transfer_data_indirect_with_offset( + addr, + USB_DT_INTERFACE_SIZE as usize, + *offset, + ); + + assert_eq!(buf[1], USB_DT_INTERFACE); + assert_eq!(buf[5], USB_CLASS_VIDEO); + assert_eq!(buf[6], SC_VIDEOSTREAMING); + + // get total vs length from its header descriptor + *offset += USB_DT_INTERFACE_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 0xf as usize, *offset); + let total = u16::from_le_bytes(buf[4..6].try_into().unwrap()); + let remained = total - 0xf; + + *offset += 0xf; + let _buf = self.get_transfer_data_indirect_with_offset(addr, remained as usize, *offset); + } + + fn get_interfaces(&mut self, offset: &mut u64, addr: u64) { + let usb_device_type = self.get_usb_device_type(); + if usb_device_type == UsbDeviceType::Camera { + return; + } + + *offset += USB_DT_CONFIG_SIZE as u64; let buf = self.get_transfer_data_indirect_with_offset( addr, USB_DT_INTERFACE_SIZE as usize, - offset, + *offset, ); // descriptor type assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_INTERFACE); @@ -1637,25 +1686,25 @@ impl TestXhciPciDevice { match usb_device_type { UsbDeviceType::Tablet => { // hid descriptor - offset += USB_DT_INTERFACE_SIZE as u64; - let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); + *offset += USB_DT_INTERFACE_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 9, *offset); assert_eq!(buf, [0x9, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0]); } UsbDeviceType::Keyboard => { // hid descriptor - offset += USB_DT_INTERFACE_SIZE as u64; - let buf = self.get_transfer_data_indirect_with_offset(addr, 9, offset); + *offset += USB_DT_INTERFACE_SIZE as u64; + let buf = self.get_transfer_data_indirect_with_offset(addr, 9, *offset); assert_eq!(buf, [0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0]); } _ => {} } - offset += USB_DT_INTERFACE_SIZE as u64; + *offset += USB_DT_INTERFACE_SIZE as u64; // endpoint descriptor let buf = self.get_transfer_data_indirect_with_offset( addr, USB_DT_ENDPOINT_SIZE as usize, - offset, + *offset, ); match usb_device_type { @@ -1670,12 +1719,12 @@ impl TestXhciPciDevice { assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); // endpoint address assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x01); - offset += USB_DT_ENDPOINT_SIZE as u64; + *offset += USB_DT_ENDPOINT_SIZE as u64; // endpoint descriptor let buf = self.get_transfer_data_indirect_with_offset( addr, USB_DT_ENDPOINT_SIZE as usize, - offset, + *offset, ); // descriptor type assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); @@ -1684,6 +1733,61 @@ impl TestXhciPciDevice { } _ => {} } + } + + fn check_string_descriptor(&mut self, slot_id: u32, name_idx: u16, name: &str) { + self.get_string_descriptor(slot_id, name_idx); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + + let len = name.len() * 2 + 2; + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + for i in 0..name.len() { + assert_eq!(buf[2 * i + 2], name.as_bytes()[i]); + } + } + + pub fn get_usb_descriptor(&mut self, slot_id: u32) { + let usb_device_type = self.get_usb_device_type(); + // device descriptor + self.get_device_descriptor(slot_id); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let buf = + self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, USB_DT_DEVICE_SIZE as u64); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_DEVICE); + // bcdUSB + match usb_device_type { + UsbDeviceType::Tablet | UsbDeviceType::Keyboard => { + assert_eq!(buf[2..4], [0, 1]); + } + UsbDeviceType::Storage => { + assert_eq!(buf[2..4], [0, 2]); + } + UsbDeviceType::Camera => { + assert_eq!(buf[2..4], [0, 3]); + } + _ => {} + } + // config descriptor + self.get_config_descriptor(slot_id); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + let addr = evt.ptr - TRB_SIZE as u64; + let mut offset = 0; + let buf = + self.get_transfer_data_indirect_with_offset(addr, USB_DT_CONFIG_SIZE as usize, offset); + // descriptor type + assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_CONFIG); + // configure value + assert_eq!(buf[5], 1); + + self.get_iad_desc(&mut offset, addr); + self.get_interfaces(&mut offset, addr); // string descriptor self.get_string_descriptor(slot_id, 0); @@ -1693,39 +1797,19 @@ impl TestXhciPciDevice { let buf = self.get_transfer_data_indirect(evt.ptr - 16, 4); // Language ID assert_eq!(buf, [4, 3, 9, 4]); - self.get_string_descriptor(slot_id, 3); - self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); - let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); match usb_device_type { UsbDeviceType::Tablet => { - let hid_str = "HID Tablet"; - let len = hid_str.len() * 2 + 2; - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); - for i in 0..hid_str.len() { - assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); - } + self.check_string_descriptor(slot_id, 3, "HID Tablet"); } UsbDeviceType::Keyboard => { - let hid_str = "HID Keyboard"; - let len = hid_str.len() * 2 + 2; - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); - for i in 0..hid_str.len() { - assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); - } + self.check_string_descriptor(slot_id, 3, "HID Keyboard"); } UsbDeviceType::Storage => { - self.get_string_descriptor(slot_id, 2); - self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); - let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let hid_str = "StratoVirt USB Storage"; - let len = hid_str.len() * 2 + 2; - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); - for i in 0..hid_str.len() { - assert_eq!(buf[2 * i + 2], hid_str.as_bytes()[i]); - } + self.check_string_descriptor(slot_id, 2, "StratoVirt USB Storage"); + } + UsbDeviceType::Camera => { + self.check_string_descriptor(slot_id, 2, "USB Camera"); } _ => {} } @@ -2126,6 +2210,19 @@ impl TestUsbBuilder { self } + pub fn with_usb_camera(mut self, id: &str, path: &str) -> Self { + let args = format!("-cameradev demo,id=camdev0,path={}", path); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + let args = format!("-device usb-camera,id={},cameradev=camdev0", id); + let args: Vec<&str> = args[..].split(' ').collect(); + let mut args = args.into_iter().map(|s| s.to_string()).collect(); + self.args.append(&mut args); + self.config.insert(String::from("camera"), true); + self + } + pub fn with_config(mut self, k: &str, v: bool) -> Self { self.config.insert(k.to_string(), v); self -- Gitee From e326fac00af3ceb0418618e7092ed665cfdd5a1e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 10 May 2023 10:21:44 +0800 Subject: [PATCH 1120/2187] test: add MST for usb camera Add MST for usb camera which used demo camera backend. And fix some usb MST. Signed-off-by: zhouli57 --- Cargo.lock | 1 + tests/mod_test/Cargo.toml | 1 + tests/mod_test/src/libdriver/usb.rs | 199 ++++++++- tests/mod_test/tests/usb_camera_test.rs | 549 ++++++++++++++++++++++++ tests/mod_test/tests/usb_test.rs | 25 +- 5 files changed, 753 insertions(+), 22 deletions(-) create mode 100644 tests/mod_test/tests/usb_camera_test.rs diff --git a/Cargo.lock b/Cargo.lock index 7b7de6694..68a6fff28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1107,6 +1107,7 @@ dependencies = [ "hex", "machine", "rand", + "serde", "serde_json", "util", "virtio", diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 9702f5452..033220a27 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -12,6 +12,7 @@ vmm-sys-util = "0.11.0" anyhow = "1.0" serde_json = "1.0" byteorder = "1.4.3" +serde = { version = "1.0", features = ["derive"] } devices = { path = "../../devices" } util = { path = "../../util" } acpi = { path = "../../acpi" } diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 0cb512887..3ec3dc893 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -21,6 +21,8 @@ use std::{ use byteorder::{ByteOrder, LittleEndian}; use serde_json::Value; +use util::byte_code::ByteCode; + use super::{ machine::TestStdMachine, malloc::GuestAllocator, @@ -89,7 +91,7 @@ const TRB_BSR_MASK: u32 = 0x1; const TRB_TD_SIZE_SHIFT: u32 = 9; const TRB_TD_SIZE_MASK: u32 = 0x1; const TRB_TRANSFER_LENGTH_SHIFT: u32 = 0; -const TRB_TRANSFER_LENGTH_MASK: u32 = 0xffff; +const TRB_TRANSFER_LENGTH_MASK: u32 = 0x1ffff; const TRB_IOC_SHIFT: u32 = 5; const TRB_IOC_MASK: u32 = 0x1; const TRB_CH_SHIFT: u32 = 4; @@ -110,9 +112,10 @@ const DEVICE_CONTEXT_SIZE: u64 = 0x400; const INPUT_CONTEXT_SIZE: u64 = 0x420; pub const CONTROL_ENDPOINT_ID: u32 = 1; pub const HID_KEYBOARD_LEN: u64 = 8; -pub const HID_POINTER_LEN: u64 = 6; +pub const HID_POINTER_LEN: u64 = 7; pub const KEYCODE_SPACE: u32 = 57; pub const KEYCODE_NUM1: u32 = 2; +const HID_POINTER_REPORT_LEN: u8 = 89; // Descriptor type pub const USB_DESCRIPTOR_TYPE_DEVICE: u8 = 1; pub const USB_DESCRIPTOR_TYPE_CONFIG: u8 = 2; @@ -1146,7 +1149,7 @@ impl TestXhciPciDevice { self.event_list.pop_front() } - pub fn queue_device_request(&mut self, slot_id: u32, device_req: &UsbDeviceRequest) { + pub fn queue_device_request(&mut self, slot_id: u32, device_req: &UsbDeviceRequest) -> u64 { // Setup Stage. let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); @@ -1159,6 +1162,7 @@ impl TestXhciPciDevice { // Status Stage. let mut status_trb = TestNormalTRB::generate_status_td(false); self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut status_trb); + ptr } // Queue TD with multi-TRB. @@ -1688,7 +1692,20 @@ impl TestXhciPciDevice { // hid descriptor *offset += USB_DT_INTERFACE_SIZE as u64; let buf = self.get_transfer_data_indirect_with_offset(addr, 9, *offset); - assert_eq!(buf, [0x9, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 74, 0x0]); + assert_eq!( + buf, + [ + 0x9, + 0x21, + 0x01, + 0x0, + 0x0, + 0x01, + 0x22, + HID_POINTER_REPORT_LEN, + 0x0 + ] + ); } UsbDeviceType::Keyboard => { // hid descriptor @@ -1837,11 +1854,14 @@ impl TestXhciPciDevice { ); } UsbDeviceType::Tablet => { - self.get_hid_report_descriptor(slot_id, 74); + self.get_hid_report_descriptor(slot_id, HID_POINTER_REPORT_LEN as u16); self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 74); + let buf = self.get_transfer_data_indirect( + evt.ptr - TRB_SIZE as u64, + HID_POINTER_REPORT_LEN as u64, + ); assert_eq!( buf, [ @@ -1851,7 +1871,8 @@ impl TestXhciPciDevice { 0x09, 0x31, 0x15, 0x00, 0x26, 0xff, 0x7f, 0x35, 0x00, 0x46, 0xff, 0x7f, 0x75, 0x10, 0x95, 0x02, 0x81, 0x02, 0x05, 0x01, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x35, 0x00, 0x45, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, - 0xc0, 0xc0, + 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, + 0x01, 0x81, 0x06, 0xc0, 0xc0, ] ); } @@ -1872,7 +1893,7 @@ impl TestXhciPciDevice { } pub fn get_config_descriptor(&mut self, slot_id: u32) { - let buf_len = 64; + let buf_len = 4096; let device_req = UsbDeviceRequest { request_type: USB_DEVICE_IN_REQUEST, request: USB_REQUEST_GET_DESCRIPTOR, @@ -2085,11 +2106,11 @@ impl TestXhciPciDevice { let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = self.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + assert_eq!(buf, [0, 100, 0, 200, 0, 0, 0]); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = self.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [1, 200, 0, 100, 0, 0]); + assert_eq!(buf, [1, 200, 0, 100, 0, 0, 0]); } } @@ -2132,6 +2153,164 @@ impl TestXhciPciDevice { } } +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +pub struct VideoStreamingControl { + pub bmHint: u16, + pub bFormatIndex: u8, + pub bFrameIndex: u8, + pub dwFrameInterval: u32, + pub wKeyFrameRate: u16, + pub wPFrameRate: u16, + pub wCompQuality: u16, + pub wCompWindowSize: u16, + pub wDelay: u16, + pub dwMaxVideoFrameSize: u32, + pub dwMaxPayloadTransferSize: u32, +} + +impl ByteCode for VideoStreamingControl {} +const SET_CUR: u8 = 0x1; +const VS_PROBE_CONTROL: u8 = 1; +const VS_COMMIT_CONTROL: u8 = 2; +const VS_INTERFACE_NUM: u16 = 1; +const GET_CUR: u8 = 0x81; +const GET_INFO: u8 = 0x86; + +const TRB_MAX_LEN: u32 = 64 * 1024; +const FRAME_WAIT_MS: u64 = 20; + +// USB Camera +impl TestXhciPciDevice { + pub fn vs_get_info(&mut self, slot_id: u32) -> u8 { + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_IN_REQUEST, + request: GET_INFO, + value: (VS_PROBE_CONTROL as u16) << 8, + index: VS_INTERFACE_NUM, + length: 1, + }; + self.queue_device_request(slot_id, &device_req); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 1); + buf[0] + } + + pub fn vs_get_cur(&mut self, slot_id: u32) -> VideoStreamingControl { + let len = std::mem::size_of::() as u16; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_IN_REQUEST, + request: GET_CUR, + value: (VS_PROBE_CONTROL as u16) << 8, + index: VS_INTERFACE_NUM, + length: len, + }; + self.queue_device_request(slot_id, &device_req); + self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + let mut vs_control = VideoStreamingControl::default(); + vs_control.as_mut_bytes().copy_from_slice(&buf); + vs_control + } + + pub fn vs_probe_control(&mut self, slot_id: u32, fmt_idx: u8, frm_idx: u8) { + let mut vs: VideoStreamingControl = VideoStreamingControl::default(); + let len = std::mem::size_of::() as u16; + vs.bFormatIndex = fmt_idx; + vs.bFrameIndex = frm_idx; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_OUT_REQUEST, + request: SET_CUR, + value: (VS_PROBE_CONTROL as u16) << 8, + index: VS_INTERFACE_NUM, + length: len, + }; + let data_ptr = self.queue_device_request(slot_id, &device_req); + self.mem_write(data_ptr, vs.as_bytes()); + } + + pub fn vs_commit_control(&mut self, slot_id: u32, fmt_idx: u8, frm_idx: u8) { + let mut vs = VideoStreamingControl::default(); + vs.bFormatIndex = fmt_idx; + vs.bFrameIndex = frm_idx; + let device_req = UsbDeviceRequest { + request_type: USB_INTERFACE_CLASS_OUT_REQUEST, + request: SET_CUR, + value: (VS_COMMIT_CONTROL as u16) << 8, + index: VS_INTERFACE_NUM, + length: 0, + }; + let data_ptr = self.queue_device_request(slot_id, &device_req); + self.mem_write(data_ptr, vs.as_bytes()); + } + + pub fn vs_clear_feature(&mut self, slot_id: u32) { + let device_req = UsbDeviceRequest { + request_type: USB_ENDPOINT_OUT_REQUEST, + request: USB_REQUEST_CLEAR_FEATURE, + value: 0, + index: 0, + length: 0, + }; + self.queue_device_request(slot_id, &device_req); + } + + pub fn get_payload( + &mut self, + slot_id: u32, + ep_id: u32, + frame_len: u32, + header_len: u32, + max_payload: u32, + ) -> Vec> { + let sz = max_payload - header_len; + let payload_cnt = frame_len + sz - 1 / sz; + let mut image = Vec::new(); + for _ in 0..payload_cnt { + let (done, buf) = self.do_payload_transfer(slot_id, ep_id, max_payload); + image.push(buf); + if done { + break; + } + } + image + } + + fn do_payload_transfer(&mut self, slot_id: u32, ep_id: u32, total: u32) -> (bool, Vec) { + let cnt = (total + TRB_MAX_LEN - 1) / TRB_MAX_LEN; + let mut data = Vec::new(); + for _ in 0..cnt { + self.queue_indirect_td(slot_id, ep_id, TRB_MAX_LEN as u64); + self.doorbell_write(slot_id, ep_id); + // wait for frame done. + std::thread::sleep(std::time::Duration::from_millis(FRAME_WAIT_MS)); + let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + if evt.ccode == TRBCCode::Success as u32 { + let mut buf = self.get_transfer_data_indirect(evt.ptr, TRB_MAX_LEN as u64); + data.append(&mut buf); + } else if evt.ccode == TRBCCode::ShortPacket as u32 { + let copyed = (TRB_MAX_LEN - evt.length) as u64; + let mut buf = self.get_transfer_data_indirect(evt.ptr, copyed); + data.append(&mut buf); + if total == data.len() as u32 { + return (false, data); + } else { + return (true, data); + } + } else { + assert_eq!(evt.ccode, 0); + return (false, Vec::new()); + } + } + (false, data) + } +} + pub struct TestUsbBuilder { args: Vec, config: HashMap, diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs new file mode 100644 index 000000000..321ac7b58 --- /dev/null +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -0,0 +1,549 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cell::{RefCell, RefMut}; +use std::rc::Rc; +use std::{fs::remove_file, fs::File, io::Write}; + +use mod_test::libtest::TestState; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use devices::camera_backend::FmtType; +use devices::usb::xhci::TRBCCode; +use mod_test::libdriver::usb::{ + TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, +}; + +const UVC_FID: u8 = 1; +const UVC_HEADER_LEN: u8 = 2; +const VS_ENDPOINT_ID: u32 = 3; + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct DeviceConfig { + check_interval: u64, + image_mode: String, + force_frame_len: Option, +} + +impl Default for DeviceConfig { + fn default() -> Self { + Self { + check_interval: 10, + image_mode: String::from("default"), + force_frame_len: None, + } + } +} + +struct TestCameraConfig { + path: String, + conf: DeviceConfig, +} + +impl TestCameraConfig { + fn new(name: &str) -> Self { + let path = format!("/tmp/camera_config_{}.json", name); + let mut config = Self { + path, + conf: DeviceConfig::default(), + }; + config.write_config(); + config + } + + fn write_config(&mut self) { + let conf = serde_json::to_string(&self.conf).unwrap(); + let mut file = File::create(&self.path).unwrap(); + file.set_len(0).unwrap(); + file.write_all(conf.as_bytes()).unwrap(); + file.flush().unwrap(); + } +} + +impl Drop for TestCameraConfig { + fn drop(&mut self) { + if let Err(e) = remove_file(&self.path) { + println!("Failed to remove config, {:?}", e); + } + } +} + +fn check_frame( + xhci: &mut RefMut, + slot_id: u32, + format_idx: u8, + frame_idx: u8, + cnt: u32, +) { + start_capture(xhci, slot_id, format_idx, frame_idx); + // Check current setting. + let cur = xhci.vs_get_cur(slot_id); + assert_eq!(cur.bFormatIndex, format_idx); + assert_eq!(cur.bFrameIndex, frame_idx); + // Get frame. + let fmt = format_index_to_fmt(format_idx); + check_multi_frames( + xhci, + slot_id, + &fmt, + cur.dwMaxVideoFrameSize, + cur.dwMaxPayloadTransferSize, + cnt, + ); + stop_capture(xhci, slot_id); +} + +fn format_index_to_fmt(idx: u8) -> FmtType { + if idx == 1 { + FmtType::Yuy2 + } else if idx == 2 { + FmtType::Mjpg + } else { + FmtType::Rgb565 + } +} + +fn start_capture(xhci: &mut RefMut, slot_id: u32, fmt_idx: u8, frm_idx: u8) { + xhci.vs_probe_control(slot_id, fmt_idx, frm_idx); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + xhci.vs_commit_control(slot_id, fmt_idx, frm_idx); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); +} + +fn stop_capture(xhci: &mut RefMut, slot_id: u32) { + xhci.vs_clear_feature(slot_id); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); +} + +fn check_multi_frames( + xhci: &mut RefMut, + slot_id: u32, + fmt: &FmtType, + frame_len: u32, + max_payload: u32, + cnt: u32, +) { + let mut fid = 0; + for _ in 0..cnt { + let payload_list = xhci.get_payload( + slot_id, + VS_ENDPOINT_ID, + frame_len, + UVC_HEADER_LEN as u32, + max_payload, + ); + for buf in &payload_list { + assert_eq!(buf[0], UVC_HEADER_LEN); + assert_eq!(buf[1] & UVC_FID, fid); + } + fid ^= UVC_FID; + let frame = payload_to_frame(payload_list); + check_frame_data(fmt, &frame); + } +} + +fn payload_to_frame(list: Vec>) -> Vec { + let mut frame = Vec::new(); + for buf in list { + frame.append(&mut buf[UVC_HEADER_LEN as usize..].to_vec()) + } + frame +} + +fn check_frame_data(fmt: &FmtType, data: &[u8]) { + let sz = data.len(); + match fmt { + FmtType::Yuy2 => { + assert_eq!(sz % 4, 0); + for i in 0..(sz / 4) { + assert_eq!(data[4 * i..(4 * i + 4)], [82, 90, 82, 240]); + } + } + FmtType::Rgb565 => { + assert_eq!(sz % 4, 0); + for i in 0..(sz / 4) { + assert_eq!(data[4 * i..(4 * i + 4)], [0, 0, 255, 255]); + } + } + FmtType::Mjpg => { + assert_eq!(data[0..4], [0xff, 0xd8, 0xff, 0xe0]); + let pos = data.len() - 2; + assert_eq!(data[pos..], [0xff, 0xf9]); + } + } +} + +fn qmp_cameradev_add( + test_state: &Rc>, + id: &str, + driver: &str, + path: &str, +) -> Value { + let test_state = test_state.borrow_mut(); + let cmd: &str = r#"{"execute": "cameradev_add", "arguments": {"id": "ID", "driver": "DRIVER", "path": "PATH"}}"#; + let cmd = cmd.replace("ID", id); + let cmd = cmd.replace("DRIVER", driver); + let cmd = cmd.replace("PATH", path); + test_state.qmp(&cmd) +} + +fn qmp_cameradev_del(test_state: &Rc>, id: &str) -> Value { + let test_state = test_state.borrow_mut(); + let cmd = r#"{"execute": "cameradev_del", "arguments": {"id": "ID"}}"#; + let cmd = cmd.replace("ID", id); + test_state.qmp(&cmd) +} + +fn qmp_plug_camera(test_state: &Rc>, id: &str, camdev: &str) -> Value { + let test_state = test_state.borrow_mut(); + let cmd = r#"{"execute": "device_add", "arguments": {"id": "ID", "driver": "usb-camera", "cameradev": "CAMDEV"}}"#; + let cmd = cmd.replace("ID", id); + let cmd = cmd.replace("CAMDEV", &camdev); + test_state.qmp(&cmd) +} + +fn qmp_unplug_camera(test_state: &Rc>, id: &str) -> Value { + let test_state = test_state.borrow_mut(); + let cmd = r#"{"execute": "device_del", "arguments": {"id": "ID"}}"#; + let cmd = cmd.replace("ID", id); + test_state.qmp(&cmd) +} + +/// USB camera basic capture. +/// TestStep: +/// 1. Init camera device. +/// 2. Query control capabilities. +/// 3. Start capture. +/// 4. Check Frame data. +/// 5. Stop capture. +/// Expect: +/// 1/2/3/4/5: success. +#[test] +fn test_xhci_camera_basic() { + let config = TestCameraConfig::new("test_xhci_camera_basic"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_camera("cam", &config.path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 5; // super speed + let slot_id = xhci.init_device(port_id); + + // Query control capabilities. + let info = xhci.vs_get_info(slot_id); + assert_eq!(info, 2 | 1); + // Yuy2 + check_frame(&mut xhci, slot_id, 1, 2, 3); + // Mjpg + check_frame(&mut xhci, slot_id, 2, 2, 3); + // Rgb + check_frame(&mut xhci, slot_id, 3, 3, 3); + + test_state.borrow_mut().stop(); +} + +/// USB camera capture with invalid frame length. +/// TestStep: +/// 1. Init camera device with invalid frame length. +/// 2. Start capture. +/// 3. Check Frame data. +/// 4. Stop capture. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn test_xhci_camera_invalid_frame_len() { + let mut config: TestCameraConfig = TestCameraConfig::new("test_xhci_camera_invalid_frame_len"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_camera("cam", &config.path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .with_config("over_transfer_ring", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 5; // super speed + let slot_id = xhci.init_device(port_id); + // Oversized frame. + let len = 7680 * 4320; + config.conf.force_frame_len = Some(len); + config.write_config(); + start_capture(&mut xhci, slot_id, 1, 1); + let cur = xhci.vs_get_cur(slot_id); + // Get frame. + let payload_list = xhci.get_payload( + slot_id, + VS_ENDPOINT_ID, + len as u32, + UVC_HEADER_LEN as u32, + cur.dwMaxPayloadTransferSize, + ); + for item in payload_list { + assert_eq!(item[0], UVC_HEADER_LEN); + } + stop_capture(&mut xhci, slot_id); + // Zero size frame. + config.conf.force_frame_len = Some(0); + config.write_config(); + start_capture(&mut xhci, slot_id, 1, 1); + // Get frame. + xhci.queue_indirect_td(slot_id, VS_ENDPOINT_ID, 10); + xhci.doorbell_write(slot_id, VS_ENDPOINT_ID); + // Wait enough time. + std::thread::sleep(std::time::Duration::from_millis(200)); + assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); + stop_capture(&mut xhci, slot_id); + + test_state.borrow_mut().stop(); +} + +/// USB camera capture with invalid frame index. +/// TestStep: +/// 1. Init camera device. +/// 2. Start capture with invalid frame index. +/// 3. Reset endpoint. +/// 4. Start capture. +/// 5. Stop capture. +/// Expect: +/// 1/3/4/5: success. +/// 2: failure. +#[test] +fn test_xhci_camera_invalid_config() { + let config = TestCameraConfig::new("test_xhci_camera_invalid_config"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_camera("cam", &config.path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 5; // super speed + let slot_id = xhci.init_device(port_id); + + start_capture(&mut xhci, slot_id, 1, 1); + // Check current setting. + let cur = xhci.vs_get_cur(slot_id); + assert_eq!(cur.bFormatIndex, 1); + assert_eq!(cur.bFrameIndex, 1); + // Get frame. + let fmt = format_index_to_fmt(1); + check_multi_frames( + &mut xhci, + slot_id, + &fmt, + cur.dwMaxVideoFrameSize, + cur.dwMaxPayloadTransferSize, + 2, + ); + // Set invalid index. + xhci.vs_probe_control(slot_id, 99, 99); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + // Reset endpoint. + xhci.reset_endpoint(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + // Set invalid index. + xhci.vs_commit_control(slot_id, 99, 99); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::StallError as u32); + // Reset endpoint. + xhci.reset_endpoint(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + check_multi_frames( + &mut xhci, + slot_id, + &fmt, + cur.dwMaxVideoFrameSize, + cur.dwMaxPayloadTransferSize, + 2, + ); + + test_state.borrow_mut().stop(); +} + +/// USB camera capture multiple times. +/// TestStep: +/// 1. Init camera device. +/// 2. Start/Stop capture for multiple times. +/// Expect: +/// 1/2: success. +#[test] +fn test_xhci_camera_repeat_openclose() { + let config = TestCameraConfig::new("test_xhci_camera_repeat_openclose"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_camera("cam", &config.path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 5; // super speed + let slot_id = xhci.init_device(port_id); + + let cnt = 3; + for _ in 0..cnt { + check_frame(&mut xhci, slot_id, 1, 1, 3); + } + test_state.borrow_mut().stop(); +} + +/// USB camera capture with different config. +/// TestStep: +/// 1. Init camera device. +/// 2. Capture with different config. +/// Expect: +/// 1/2: success. +#[test] +fn test_xhci_camera_repeat_config() { + let config = TestCameraConfig::new("test_xhci_camera_repeat_config"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_camera("cam", &config.path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 5; // super speed + let slot_id = xhci.init_device(port_id); + check_frame(&mut xhci, slot_id, 1, 1, 3); + check_frame(&mut xhci, slot_id, 1, 2, 3); + check_frame(&mut xhci, slot_id, 1, 3, 3); + check_frame(&mut xhci, slot_id, 1, 4, 3); + check_frame(&mut xhci, slot_id, 3, 2, 3); + + test_state.borrow_mut().stop(); +} + +/// USB camera capture with invalid control order. +/// TestStep: +/// 1. Init camera device. +/// 2. Capture with invalid control order. +/// Expect: +/// 1/2: success. +#[test] +fn test_xhci_camera_invalid_control() { + let config = TestCameraConfig::new("test_xhci_camera_invalid_control"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_usb_camera("cam", &config.path) + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + let port_id = 5; // super speed + let slot_id = xhci.init_device(port_id); + + start_capture(&mut xhci, slot_id, 1, 1); + let cur = xhci.vs_get_cur(slot_id); + let fmt = format_index_to_fmt(1); + let cnt = 2; + for _ in 0..cnt { + xhci.vs_probe_control(slot_id, 1, 1); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } + for _ in 0..cnt { + xhci.vs_commit_control(slot_id, 1, 1); + xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + } + + check_multi_frames( + &mut xhci, + slot_id, + &fmt, + cur.dwMaxVideoFrameSize, + cur.dwMaxPayloadTransferSize, + 2, + ); + + test_state.borrow_mut().stop(); +} + +/// USB camera hot plug/unplug. +/// TestStep: +/// 1. Hot plug camera device. +/// 2. Test camera start/stop capture. +/// 3. Hot unplug device. +/// Expect: +/// 1/2: success. +#[test] +fn test_xhci_camera_hotplug() { + let config = TestCameraConfig::new("test_xhci_camera_hotplug"); + let (xhci, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_config("auto_run", true) + .with_config("command_auto_doorbell", true) + .build(); + let mut xhci = xhci.borrow_mut(); + qmp_cameradev_add(&test_state, "camdev0", "demo", &config.path); + qmp_plug_camera(&test_state, "cam0", "camdev0"); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + let port_id = 5; // super speed + xhci.device_config.insert(String::from("camera"), true); + let slot_id = xhci.init_device(port_id); + // Yuy2 + check_frame(&mut xhci, slot_id, 1, 4, 3); + + qmp_unplug_camera(&test_state, "cam0"); + qmp_cameradev_del(&test_state, "camdev0"); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + + test_state.borrow_mut().stop(); +} + +/// USB camera hot plug/unplug with invalid config. +/// TestStep: +/// 1. Hot plug camera device with invalid config. +/// 2. Hot unplug camera device with invalid config. +/// Expect: +/// 1/2: failure. +#[test] +fn test_xhci_camera_hotplug_invalid() { + let (_, test_state, _) = TestUsbBuilder::new() + .with_xhci("xhci") + .with_config("auto_run", true) + .build(); + + qmp_cameradev_add(&test_state, "camdev0", "v4l2", "/tmp/not-existed"); + // Invalid cameradev. + let value = qmp_plug_camera(&test_state, "usbcam0", "camdev0"); + let desc = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!(desc, "Failed to open v4l2 backend /tmp/not-existed."); + // Invalid device id. + let value = qmp_unplug_camera(&test_state.clone(), "usbcam0"); + let desc = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!(desc, "Failed to detach device: id usbcam0 not found"); + // Invalid cameradev id. + let value = qmp_cameradev_del(&test_state, "camdev1"); + let desc = value["error"]["desc"].as_str().unwrap().to_string(); + assert_eq!(desc, "no cameradev with id camdev1"); + + test_state.borrow_mut().stop(); +} diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 1761b9c98..34265c7c5 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -2081,13 +2081,14 @@ fn test_xhci_tablet_basic() { (i * 10 >> 8) as u8, (i * 20) as u8, (i * 20 >> 8) as u8, + 0, 0 ] ); } for _ in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x8); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x28); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); @@ -2095,11 +2096,11 @@ fn test_xhci_tablet_basic() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 10, 0, 20, 0, 1]); + assert_eq!(buf, [0, 10, 0, 20, 0, 1, 255]); } for _ in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x10); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x50); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); @@ -2107,7 +2108,7 @@ fn test_xhci_tablet_basic() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 10, 0, 20, 0, 255]); + assert_eq!(buf, [0, 10, 0, 20, 0, 255, 1]); } test_state.borrow_mut().stop(); } @@ -2142,7 +2143,7 @@ fn test_xhci_tablet_over_hid_buffer() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, i as u8, 0, (i + 100) as u8, 0, 0]); + assert_eq!(buf, [0, i as u8, 0, (i + 100) as u8, 0, 0, 0]); } else { // event lost. assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); @@ -2177,7 +2178,7 @@ fn test_xhci_tablet_over_ring_limit() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 50, 0, 100, 0, 0]); + assert_eq!(buf, [0, 50, 0, 100, 0, 0, 0]); } if i == 0 { // Fake link new address. @@ -2219,7 +2220,7 @@ fn test_xhci_tablet_invalid_value() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [7, 255, 127, 255, 127, 0]); + assert_eq!(buf, [7, 255, 127, 255, 127, 1, 255]); xhci.test_pointer_event(slot_id, test_state.clone()); test_state.borrow_mut().stop(); @@ -2252,7 +2253,7 @@ fn test_xhci_tablet_device_init_control_command() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, HID_POINTER_LEN); - assert_eq!(buf, [0, 0, 0, 0, 0, 0]); + assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0]); xhci.test_pointer_event(slot_id, test_state.clone()); test_state.borrow_mut().stop(); @@ -2367,7 +2368,7 @@ fn test_xhci_tablet_flush() { xhci.stop_endpoint(slot_id, HID_DEVICE_ENDPOINT_ID); // No data, the xhci report short packet. let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); + assert_eq!(evt.ccode, TRBCCode::Stopped as u32); // Stop command return success. let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); @@ -2443,7 +2444,7 @@ fn test_xhci_disable_interrupt() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_direct(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + assert_eq!(buf, [0, 100, 0, 200, 0, 0, 0]); // Case: disable IMAN_IE qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); @@ -2466,7 +2467,7 @@ fn test_xhci_disable_interrupt() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_direct(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + assert_eq!(buf, [0, 100, 0, 200, 0, 0, 0]); test_state.borrow_mut().stop(); } @@ -2582,7 +2583,7 @@ fn test_xhci_tablet_invalid_trb() { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 100, 0, 200, 0, 0]); + assert_eq!(buf, [0, 100, 0, 200, 0, 0, 0]); xhci.test_pointer_event(slot_id, test_state.clone()); test_state.borrow_mut().stop(); -- Gitee From 0a4625960bcf015db460661f2687e0f6b459a95d Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 16 May 2023 17:10:27 +0800 Subject: [PATCH 1121/2187] smbios: smbios table support SMBIOS specification defines the data structures and information that will enter the data structures associated with the system. Having these fields populate the data associated with each system enables system administrators to identify and manage these systems remotely Signed-off-by: jiewangqun --- Cargo.lock | 11 + docs/config_guidebook.md | 9 + machine/Cargo.toml | 1 + machine/src/lib.rs | 20 ++ machine/src/standard_vm/aarch64/mod.rs | 13 +- machine/src/standard_vm/x86_64/mod.rs | 13 +- machine_manager/src/cmdline.rs | 10 + machine_manager/src/config/mod.rs | 3 + machine_manager/src/config/smbios.rs | 205 +++++++++++++++ smbios/Cargo.toml | 14 ++ smbios/src/lib.rs | 18 ++ smbios/src/smbios_table.rs | 335 +++++++++++++++++++++++++ 12 files changed, 644 insertions(+), 8 deletions(-) create mode 100644 machine_manager/src/config/smbios.rs create mode 100644 smbios/Cargo.toml create mode 100644 smbios/src/lib.rs create mode 100644 smbios/src/smbios_table.rs diff --git a/Cargo.lock b/Cargo.lock index cb0b904fa..c0b0528bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1000,6 +1000,7 @@ dependencies = [ "pci", "serde", "serde_json", + "smbios", "strum 0.24.1", "strum_macros 0.24.3", "sysbus", @@ -1614,6 +1615,16 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "smbios" +version = "2.2.0" +dependencies = [ + "anyhow", + "byteorder", + "machine_manager", + "util", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 5fca3810c..e088790e3 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -247,6 +247,15 @@ And you can also restore StratoVirt's **pid number** to a file by: -pidfile ``` +### 1.11 Smbios +The SMBIOS specification defines the data structures and information that will enter the data structures associated with the system. Having these fields populate the data associated with each system enables system administrators to identify and manage these systems remotely. + +```shell +# cmdline +-smbios type=0[,vendor=str][,version=str][,date=str] +-smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str] +``` + ## 2. Device Configuration For machine type "microvm", only virtio-mmio and legacy devices are supported. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 5640312aa..c745aac51 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -19,6 +19,7 @@ anyhow = "1.0" strum = "0.24.1" strum_macros = "0.24.3" acpi = { path = "../acpi" } +smbios = { path = "../smbios" } address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } cpu = { path = "../cpu" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1eb36c4a9..e7bf3e1f3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -80,6 +80,8 @@ use machine_manager::config::{ use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; +use smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; +use smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; use sysbus::{SysBus, SysBusDevOps, SysBusDevType}; @@ -110,6 +112,24 @@ pub trait MachineOps { /// On x86_64, there is a gap ranged from (4G - 768M) to 4G, which will be skipped. fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)>; + fn build_smbios(&self, fw_cfg: &Arc>) -> Result<()> { + let smbioscfg = self.get_vm_config().lock().unwrap().smbios.clone(); + + let mut smbios = SmbiosTable::new(); + let table = smbios.build_smbios_tables(smbioscfg); + let ep = build_smbios_ep30(table.len() as u32); + + let mut locked_fw_cfg = fw_cfg.lock().unwrap(); + locked_fw_cfg + .add_file_entry(SMBIOS_TABLE_FILE, table) + .with_context(|| "Failed to add smbios table file entry")?; + locked_fw_cfg + .add_file_entry(SMBIOS_ANCHOR_FILE, ep) + .with_context(|| "Failed to add smbios anchor file entry")?; + + Ok(()) + } + fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result; #[cfg(target_arch = "aarch64")] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index df29d5620..a77be4dd2 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -606,10 +606,15 @@ impl MachineOps for StdMachine { } // If it is direct kernel boot mode, the ACPI can not be enabled. - if migrate.0 == MigrateMode::Unknown && fwcfg.is_some() { - locked_vm - .build_acpi_tables(&fwcfg.unwrap()) - .with_context(|| "Failed to create ACPI tables")?; + if migrate.0 == MigrateMode::Unknown { + if let Some(fw_cfg) = fwcfg { + locked_vm + .build_acpi_tables(&fw_cfg) + .with_context(|| "Failed to create ACPI tables")?; + locked_vm + .build_smbios(&fw_cfg) + .with_context(|| "Failed to create smbios tables")?; + } } locked_vm diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index b63530732..a649204e8 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -481,10 +481,15 @@ impl MachineOps for StdMachine { &boot_config, )?); - if migrate.0 == MigrateMode::Unknown && fwcfg.is_some() { - locked_vm - .build_acpi_tables(&fwcfg.unwrap()) - .with_context(|| "Failed to create ACPI tables")?; + if migrate.0 == MigrateMode::Unknown { + if let Some(fw_cfg) = fwcfg { + locked_vm + .build_acpi_tables(&fw_cfg) + .with_context(|| "Failed to create ACPI tables")?; + locked_vm + .build_smbios(&fw_cfg) + .with_context(|| "Failed to create smbios tables")?; + } } locked_vm diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index fa0e8c383..691e790e7 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -464,6 +464,15 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("watch on the external windows emu pid") .takes_value(true), ) + .arg( + Arg::with_name("smbios") + .multiple(true) + .long("smbios") + .value_name("") + .help("\n\t\tadd type0 table: -smbios type=0[,vendor=str][,version=str][,date=str]; \ + \n\t\tadd type1 table: -smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str];") + .takes_values(true), + ) } /// Create `VmConfig` from `ArgMatches`'s arg. @@ -530,6 +539,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); add_args_to_config_multi!((args.values_of("cameradev")), vm_cfg, add_camera_backend); + add_args_to_config_multi!((args.values_of("smbios")), vm_cfg, add_smbios); if let Some(s) = args.value_of("trace") { add_trace_events(&s)?; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index d6bdf6d96..2b080990c 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -31,6 +31,7 @@ pub use ramfb::*; pub use rng::*; pub use sasl_auth::*; pub use scsi::*; +pub use smbios::*; pub use tls_creds::*; pub use usb::*; pub use vfio::*; @@ -58,6 +59,7 @@ mod rng; mod sasl_auth; pub mod scream; mod scsi; +mod smbios; mod tls_creds; mod usb; mod vfio; @@ -126,6 +128,7 @@ pub struct VmConfig { pub display: Option, pub camera_backend: HashMap, pub windows_emu_pid: Option, + pub smbios: SmbiosConfig, } impl VmConfig { diff --git a/machine_manager/src/config/smbios.rs b/machine_manager/src/config/smbios.rs new file mode 100644 index 000000000..e0ef354ec --- /dev/null +++ b/machine_manager/src/config/smbios.rs @@ -0,0 +1,205 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Context, Result}; + +use crate::config::{CmdParser, VmConfig}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosType0Config { + pub vender: Option, + pub version: Option, + pub date: Option, + pub added: bool, +} + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosType1Config { + pub manufacturer: Option, + pub product: Option, + pub version: Option, + pub serial: Option, + pub sku: Option, + pub family: Option, + pub uuid: Option, + pub added: bool, +} + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosConfig { + pub type0: SmbiosType0Config, + pub type1: SmbiosType1Config, +} + +/// Check if the uuid is valid. +fn check_valid_uuid(uuid: &str) -> bool { + if uuid.len() != 36 { + return false; + } + + // Char located at 8, 13, 18, 23 should be `-` + let indexs = &[8, 13, 18, 23]; + for i in indexs { + if uuid.chars().nth(*i).unwrap() != '-' { + return false; + } + } + + for ch in uuid.chars() { + if ch != '-' && (!ch.is_ascii_hexdigit()) { + return false; + } + } + + true +} + +/// Convert an ASCII string to a 128-bit buffer. +/// format: 33DB4D5E-1FF7-401C-9657-7441C03DD766 +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct Uuid { + pub name: Vec, +} + +impl FromStr for Uuid { + type Err = (); + + fn from_str(str: &str) -> std::result::Result { + let name = str.to_string(); + + if !check_valid_uuid(&name) { + return Err(()); + } + + let mut uuid_bytes = Vec::new(); + // If the UUID is "aabbccdd-eeff-gghh-iijj-kkllmmnnoopp", then the encoded order is: + // dd cc bb aa ff ee hh gg ii jj kk ll mm nn oo pp + let index = &[6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34]; + + for i in index { + let mut chars = name.chars(); + uuid_bytes.push( + (chars.nth(*i).unwrap().to_digit(16).unwrap() as u8) << 4 + | chars.next().unwrap().to_digit(16).unwrap() as u8, + ); + } + Ok(Uuid { name: uuid_bytes }) + } +} + +impl VmConfig { + /// # Arguments + /// + /// * `type0` - The type0 cmdline string. + fn add_smbios_type0(&mut self, type0: &str) -> Result<()> { + if self.smbios.type0.added { + bail!("smbios type0 has been added"); + } + + let mut cmd_parser = CmdParser::new("smbios"); + cmd_parser + .push("") + .push("type") + .push("vendor") + .push("version") + .push("date"); + cmd_parser.parse(type0)?; + + self.smbios.type0.vender = cmd_parser.get_value::("vendor")?; + self.smbios.type0.version = cmd_parser.get_value::("version")?; + self.smbios.type0.date = cmd_parser.get_value::("date")?; + self.smbios.type0.added = true; + + Ok(()) + } + + /// # Arguments + /// + /// * `type1` - The type1 cmdline string. + fn add_smbios_type1(&mut self, type1: &str) -> Result<()> { + if self.smbios.type1.added { + bail!("smbios type1 has been added"); + } + + let mut cmd_parser = CmdParser::new("smbios"); + cmd_parser + .push("") + .push("type") + .push("manufacturer") + .push("product") + .push("version") + .push("serial") + .push("sku") + .push("uuid") + .push("family"); + cmd_parser.parse(type1)?; + + self.smbios.type1.manufacturer = cmd_parser.get_value::("manufacturer")?; + self.smbios.type1.product = cmd_parser.get_value::("product")?; + self.smbios.type1.version = cmd_parser.get_value::("version")?; + self.smbios.type1.serial = cmd_parser.get_value::("serial")?; + self.smbios.type1.sku = cmd_parser.get_value::("sku")?; + self.smbios.type1.family = cmd_parser.get_value::("family")?; + self.smbios.type1.uuid = cmd_parser.get_value::("uuid")?; + self.smbios.type1.added = true; + + Ok(()) + } + + /// Add argument `smbios_args` to `VmConfig`. + /// + /// # Arguments + /// + /// * `smbios_args` - The args of object. + pub fn add_smbios(&mut self, smbios_args: &str) -> Result<()> { + let mut cmd_params = CmdParser::new("smbios"); + cmd_params.push("").push("type"); + + cmd_params.get_parameters(smbios_args)?; + let smbios_type = cmd_params + .get_value::("type")? + .with_context(|| "smbios type not specified")?; + match smbios_type.as_str() { + "0" => { + self.add_smbios_type0(smbios_args)?; + } + "1" => { + self.add_smbios_type1(smbios_args)?; + } + _ => { + bail!("Unknow smbios type: {:?}", &smbios_type); + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_smbios_uuid() { + let uuid = Uuid::from_str("33DB4D5E-1FF7-401C-9657-7441C03DD766").unwrap(); + + assert_eq!( + uuid.name.to_vec(), + &[ + 0x5E, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, + 0xD7, 0x66 + ] + ); + } +} diff --git a/smbios/Cargo.toml b/smbios/Cargo.toml new file mode 100644 index 000000000..62d539053 --- /dev/null +++ b/smbios/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "smbios" +version = "2.2.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "smbios module" + +[dependencies] +byteorder = "1.4.3" +anyhow = "1.0" + +util = {path = "../util"} +machine_manager = { path = "../machine_manager" } diff --git a/smbios/src/lib.rs b/smbios/src/lib.rs new file mode 100644 index 000000000..f5aa812ee --- /dev/null +++ b/smbios/src/lib.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod smbios_table; + +// The name of corresponding file-entry in FwCfg device that represents smbios table data. +pub const SMBIOS_TABLE_FILE: &str = "etc/smbios/smbios-tables"; +// The name of corresponding file-entry in FwCfg device that represents smbios table anchor. +pub const SMBIOS_ANCHOR_FILE: &str = "etc/smbios/smbios-anchor"; diff --git a/smbios/src/smbios_table.rs b/smbios/src/smbios_table.rs new file mode 100644 index 000000000..c08b48700 --- /dev/null +++ b/smbios/src/smbios_table.rs @@ -0,0 +1,335 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use machine_manager::config::{SmbiosConfig, SmbiosType0Config, SmbiosType1Config}; +use std::mem::size_of; +use util::byte_code::ByteCode; + +const TYPE0_HANDLE: u16 = 0x0; +const TYPE1_HANDLE: u16 = 0x100; +const TYPE127_HANDLE: u16 = 0x7F00; + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosHeader { + type_num: u8, + len: u8, + handle: u16, +} + +impl SmbiosHeader { + pub fn new(type_num: u8, len: u8, handle: u16) -> SmbiosHeader { + SmbiosHeader { + type_num, + len, + handle, + } + } +} + +/// Type0: BIOS information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType0 { + header: SmbiosHeader, + pub vendor_idx: u8, + pub bios_version_idx: u8, + bios_starting_addr_seg: [u8; 2], + bios_release_date_idx: u8, + bios_rom_size: u8, + bios_characteristics: [u8; 8], + bios_characteristics_ext: [u8; 2], + system_bios_major_release: u8, + system_bios_minor_release: u8, + embedded_controller_major_release: u8, + embedded_controller_minor_release: u8, +} + +impl ByteCode for SmbiosType0 {} + +impl SmbiosType0 { + pub fn new() -> SmbiosType0 { + SmbiosType0 { + header: SmbiosHeader::new(0_u8, size_of::() as u8, TYPE0_HANDLE), + bios_starting_addr_seg: 0xE800_u16.to_le_bytes(), + bios_rom_size: 0_u8, + bios_characteristics: 0x08_u64.to_le_bytes(), + bios_characteristics_ext: [0, 0x1C], + embedded_controller_major_release: 0xFF, + embedded_controller_minor_release: 0xFF, + ..Default::default() + } + } +} +#[derive(Default, Clone)] +struct SmbiosType0Table { + header: SmbiosType0, + body: Vec, + str_index: u8, +} + +impl SmbiosType0Table { + pub fn new() -> SmbiosType0Table { + SmbiosType0Table { + header: SmbiosType0::new(), + body: Vec::new(), + str_index: 0_u8, + } + } + + pub fn set_str(&mut self, str: String) { + self.str_index += 1; + self.body.append(&mut str.as_bytes().to_vec()); + self.body.append(&mut vec![0]); + } + + pub fn finish(&mut self) { + if self.str_index == 0 { + self.body.append(&mut vec![0; 2]); + } else { + self.body.append(&mut vec![0]); + } + } +} + +/// Type1: System information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType1 { + header: SmbiosHeader, + pub manufacturer: u8, + pub product_name: u8, + pub version: u8, + serial_num: u8, + uuid: [u8; 16], + wake_up_type: u8, + sku_num: u8, + family: u8, +} + +impl ByteCode for SmbiosType1 {} + +impl SmbiosType1 { + pub fn new() -> SmbiosType1 { + SmbiosType1 { + header: SmbiosHeader::new(1_u8, size_of::() as u8, TYPE1_HANDLE), + wake_up_type: 0x6_u8, + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType1Table { + header: SmbiosType1, + body: Vec, + str_index: u8, +} + +impl SmbiosType1Table { + pub fn new() -> SmbiosType1Table { + SmbiosType1Table { + header: SmbiosType1::new(), + body: Vec::new(), + str_index: 0_u8, + } + } + + pub fn set_str(&mut self, str: String) { + self.str_index += 1; + self.body.append(&mut str.as_bytes().to_vec()); + self.body.append(&mut vec![0]); + } + + pub fn finish(&mut self) { + if self.str_index == 0 { + self.body.append(&mut vec![0; 2]); + } else { + self.body.append(&mut vec![0]); + } + } +} + +/// Type127: End of table +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType127 { + header: SmbiosHeader, +} + +impl SmbiosType127 { + pub fn new() -> SmbiosType127 { + SmbiosType127 { + header: SmbiosHeader::new(127_u8, size_of::() as u8, TYPE127_HANDLE), + } + } +} + +impl ByteCode for SmbiosType127 {} + +#[derive(Default, Clone)] +struct SmbiosType127Table { + header: SmbiosType127, + body: Vec, +} + +impl SmbiosType127Table { + pub fn new() -> SmbiosType127Table { + SmbiosType127Table { + header: SmbiosType127::new(), + body: Vec::new(), + } + } + + pub fn finish(&mut self) { + self.body.append(&mut vec![0; 2]); + } +} + +#[derive(Default)] +pub struct SmbiosTable { + entries: Vec, +} + +impl SmbiosTable { + pub fn new() -> SmbiosTable { + SmbiosTable { + entries: Vec::new(), + } + } + + fn build_type0(&mut self, type0: SmbiosType0Config) { + let mut table0: SmbiosType0Table = SmbiosType0Table::new(); + + if let Some(vender) = type0.vender { + table0.header.vendor_idx = table0.str_index + 1; + table0.set_str(vender); + } + + if let Some(version) = type0.version { + table0.header.bios_version_idx = table0.str_index + 1; + table0.set_str(version); + } + + if let Some(date) = type0.date { + table0.header.bios_release_date_idx = table0.str_index + 1; + table0.set_str(date); + } + table0.finish(); + + self.entries.append(&mut table0.header.as_bytes().to_vec()); + self.entries.append(&mut table0.body); + } + + fn build_type1(&mut self, type1: SmbiosType1Config) { + let mut table1: SmbiosType1Table = SmbiosType1Table::new(); + + table1.header.manufacturer = table1.str_index + 1; + if let Some(manufacturer) = type1.manufacturer { + table1.set_str(manufacturer); + } else { + table1.set_str(String::from("Stratovirt")); + } + + table1.header.product_name = table1.str_index + 1; + if let Some(product) = type1.product { + table1.set_str(product); + } else { + table1.set_str(String::from("Virtual Machine")); + } + + if let Some(version) = type1.version { + table1.header.version = table1.str_index + 1; + table1.set_str(version); + } + + if let Some(serial) = type1.serial { + table1.header.serial_num = table1.str_index + 1; + table1.set_str(serial); + } + + if let Some(sku) = type1.sku { + table1.header.sku_num = table1.str_index + 1; + table1.set_str(sku); + } + + if let Some(family) = type1.family { + table1.header.family = table1.str_index + 1; + table1.set_str(family); + } + + if let Some(uuid) = type1.uuid { + for (idx, data) in uuid.name.iter().enumerate() { + table1.header.uuid[idx] = *data; + } + } + table1.finish(); + + self.entries.append(&mut table1.header.as_bytes().to_vec()); + self.entries.append(&mut table1.body); + } + + fn build_type127(&mut self) { + let mut table127 = SmbiosType127Table::new(); + + table127.finish(); + + self.entries + .append(&mut table127.header.as_bytes().to_vec()); + self.entries.append(&mut table127.body); + } + + pub fn build_smbios_tables(&mut self, smbios: SmbiosConfig) -> Vec { + self.build_type0(smbios.type0); + self.build_type1(smbios.type1); + self.build_type127(); + + self.entries.clone() + } +} + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosEntryPoint30 { + anchor_str: [u8; 5], + checksum: u8, + len: u8, + smbios_major_version: u8, + smbios_minor_version: u8, + smbios_doc_rev: u8, + entry_point_revision: u8, + reserved: u8, + structure_table_max_size: [u8; 4], + structure_table_address: u64, +} + +impl ByteCode for SmbiosEntryPoint30 {} +impl SmbiosEntryPoint30 { + pub fn new(table_len: u32) -> SmbiosEntryPoint30 { + let anchor: [u8; 5] = [b'_', b'S', b'M', b'3', b'_']; + SmbiosEntryPoint30 { + anchor_str: anchor, + len: size_of::() as u8, + entry_point_revision: 1_u8, + smbios_major_version: 3_u8, + smbios_minor_version: 0_u8, + structure_table_max_size: table_len.to_le_bytes(), + ..Default::default() + } + } +} + +pub fn build_smbios_ep30(table_len: u32) -> Vec { + let ep = SmbiosEntryPoint30::new(table_len); + + ep.as_bytes().to_vec() +} -- Gitee From 89ed4c7288328372ff79bd26f342f51383dadb64 Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 29 May 2023 14:44:14 +0800 Subject: [PATCH 1122/2187] Fixed a typo while gicv3::set_redist() Instead of calling access_gic_redistributor(GICR_ISENABLER0, redist.vcpu, &mut !0, true)?; (which _enables_ every possible interrupt on GIC), we must call self.access_gic_redistributor(GICR_ICENABLER0, redist.vcpu, &mut !0, true)?; Signed-off-by: Dmitry Skorodumov --- devices/src/interrupt_controller/aarch64/state.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 3d52d0abe..4be30ef6e 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -52,6 +52,7 @@ const NR_GICR_IPRIORITYR: usize = 8; /// SGI and PPI Redistributor registers, offsets from RD_base const GICR_IGROUPR0: u64 = 0x1_0080; const GICR_ISENABLER0: u64 = 0x1_0100; +const GICR_ICENABLER0: u64 = 0x1_0180; const GICR_ISPENDR0: u64 = 0x1_0200; const GICR_ICPENDR0: u64 = 0x1_0280; const GICR_ISACTIVER0: u64 = 0x1_0300; @@ -244,7 +245,7 @@ impl GICv3 { self.access_gic_redistributor(GICR_STATUSR, redist.vcpu, &mut redist.gicr_statusr, true)?; self.access_gic_redistributor(GICR_WAKER, redist.vcpu, &mut redist.gicr_waker, true)?; self.access_gic_redistributor(GICR_IGROUPR0, redist.vcpu, &mut redist.gicr_igroupr0, true)?; - self.access_gic_redistributor(GICR_ISENABLER0, redist.vcpu, &mut !0, true)?; + self.access_gic_redistributor(GICR_ICENABLER0, redist.vcpu, &mut !0, true)?; self.access_gic_redistributor( GICR_ISENABLER0, redist.vcpu, -- Gitee From 28b02bf279270bf7395eb4f61ea4884ee75870c4 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 30 May 2023 15:23:43 +0800 Subject: [PATCH 1123/2187] send qmp event when guest killed by signal Add sending shutdown qmp event when processing SIGTERM, SIGINT or SIGHUP signals, and return 0. Signed-off-by: yezengruan --- machine_manager/src/signal_handler.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 138a576ed..c2977e396 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -9,13 +9,20 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::temp_cleaner::TempCleaner; + use std::io::Write; use libc::{c_int, c_void, siginfo_t}; use util::set_termi_canon_mode; use vmm_sys_util::signal::register_signal_handler; +use crate::{ + event, + qmp::{qmp_schema, QmpChannel}, + temp_cleaner::TempCleaner, +}; + +const VM_EXIT_SUCCESS: i32 = 0; pub const VM_EXIT_GENE_ERR: i32 = 1; const SYSTEMCALL_OFFSET: isize = 6; @@ -34,6 +41,14 @@ pub fn exit_with_code(code: i32) { } extern "C" fn handle_signal_kill(num: c_int, _: *mut siginfo_t, _: *mut c_void) { + if QmpChannel::is_connected() { + let shutdown_msg = qmp_schema::Shutdown { + guest: false, + reason: "Guest shutdown by signal ".to_string() + &num.to_string(), + }; + event!(Shutdown; shutdown_msg); + } + basic_clean(); write!( &mut std::io::stderr(), @@ -41,7 +56,7 @@ extern "C" fn handle_signal_kill(num: c_int, _: *mut siginfo_t, _: *mut c_void) num ) .expect("Failed to write to stderr"); - exit_with_code(VM_EXIT_GENE_ERR); + exit_with_code(VM_EXIT_SUCCESS); } extern "C" fn handle_signal_sys(_: c_int, info: *mut siginfo_t, _: *mut c_void) { @@ -64,4 +79,6 @@ pub fn register_kill_signal() { .expect("Register signal handler for SIGSYS failed!"); register_signal_handler(libc::SIGINT, handle_signal_kill) .expect("Register signal handler for SIGINT failed!"); + register_signal_handler(libc::SIGHUP, handle_signal_kill) + .expect("Register signal handler for SIGHUP failed!"); } -- Gitee From 3db25ccb194040768bc44c3cd22972b8263a529d Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 25 May 2023 23:35:14 +0800 Subject: [PATCH 1124/2187] migrate: use serde instead of unsafe call during snapshot restore As byte_order in MigrationHeader is set to Niche if it's 0 in snapshot file, due to Discriminant Elision, and could not be deferenced. Thus using from_bytes() to convert bytes to MigrationHeader will deference it, and cause an error. Using Ok() to wrap it it will change Ok() into Error (on Rust 1.67 and above), As Ok() and Err() share the same memory for their members, the ErrorVTable in Error is overriden by MigrationHeader which is wrapped by Ok(). To avoid such unsafe behavior, remove the unsafe as_ref()/from_bytes() to remove the problem from the root, and use serde to help to avoid further problems. Signed-off-by: Zhang Bo --- migration/src/general.rs | 50 ++++++++++++++++++++++++++++++++++----- migration/src/protocol.rs | 6 ++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/migration/src/general.rs b/migration/src/general.rs index d4ba732be..8cbc6d445 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -21,11 +21,13 @@ use crate::protocol::{ }; use crate::{MigrationError, MigrationManager}; use anyhow::{anyhow, Context, Result}; -use util::{byte_code::ByteCode, unix::host_page_size}; +use util::unix::host_page_size; impl MigrationManager { /// Write `MigrationHeader` to `Write` trait object as bytes. /// `MigrationHeader` will occupy the first 4096 bytes in snapshot file. + /// bytes 0-8: the length of the header that's in serde style from struct MigrationHeader. + /// bytes 8-4096: the header that's in serde style from struct MigrationHeader, and tailing 0s. /// /// # Arguments /// @@ -43,8 +45,17 @@ impl MigrationManager { header.desc_len = Self::desc_db_len()?; } + let header_serde = serde_json::to_vec(&header)?; + if header_serde.len() > HEADER_LENGTH - 8 { + return Err(anyhow!(MigrationError::SaveVmMemoryErr( + "header too long".to_string() + ))); + } + let header_len = header_serde.len().to_le_bytes(); let mut input_slice = [0u8; HEADER_LENGTH]; - input_slice[0..size_of::()].copy_from_slice(header.as_bytes()); + input_slice[0..8].copy_from_slice(&header_len); + input_slice[8..header_serde.len() + 8].copy_from_slice(&header_serde); + fd.write(&input_slice) .with_context(|| "Failed to save migration header")?; @@ -57,14 +68,41 @@ impl MigrationManager { /// /// * `fd` - The `Read` trait object to read header message. pub fn restore_header(fd: &mut dyn Read) -> Result { - let mut header_bytes = [0u8; size_of::()]; + // 1. reader header length + let mut header_len = [0u8; 8]; + fd.read_exact(&mut header_len)?; + let header_len = u64::from_le_bytes(header_len); + if header_len > HEADER_LENGTH as u64 - 8 { + return Err(anyhow!(MigrationError::FromBytesError( + "migration header length too large" + ))); + } + + // 2. read header according to its length + let mut header_bytes = Vec::new(); + header_bytes.resize(header_len as usize, 0); fd.read_exact(&mut header_bytes)?; - let mut place_holder = [0u8; HEADER_LENGTH - size_of::()]; + // 3. change the binary format header into struct + let deserializer = serde_json::Deserializer::from_slice(&header_bytes); + let mut migration_header: Option = None; + for header in deserializer.into_iter::() { + migration_header = match header { + Ok(h) => Some(h), + Err(_) => { + return Err(anyhow!(MigrationError::FromBytesError( + "Invalid migration header" + ))) + } + }; + } + + // 4. read the extra bits + let mut place_holder = vec![0u8; HEADER_LENGTH - 8 - header_len as usize]; fd.read_exact(&mut place_holder)?; - Ok(*MigrationHeader::from_bytes(&header_bytes) - .with_context(|| MigrationError::FromBytesError("HEADER"))?) + // SAFETY: migration_header is Some here. + Ok(migration_header.unwrap()) } /// Write all `DeviceStateDesc` in `desc_db` hashmap to `Write` trait object. diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 7bbc87403..0fd1ec59a 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -297,14 +297,14 @@ pub const HEADER_LENGTH: usize = 4096; /// Format type for migration. /// Different file format will have different file layout. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] pub enum FileFormat { Device, MemoryFull, } /// The endianness of byte order. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)] enum EndianType { Little = 1, Big = 2, @@ -345,7 +345,7 @@ fn cpu_model() -> [u8; 16] { } /// Structure used to mark some message in migration. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub struct MigrationHeader { /// Magic number for migration file/stream. magic_num: [u8; 16], -- Gitee From 34e08b7aaf9cf90b2662d3d4b987132c84ef45e5 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 30 May 2023 20:05:02 +0800 Subject: [PATCH 1125/2187] smbios: add smbios MST test smbios table0(bios message) and table1(system message) MTS test, fw_cfg device data check. Signed-off-by: jiewangqun --- Cargo.lock | 1 + tests/mod_test/Cargo.toml | 1 + tests/mod_test/tests/fwcfg_test.rs | 161 +++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 611c3a695..8786f2419 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1110,6 +1110,7 @@ dependencies = [ "rand", "serde", "serde_json", + "smbios", "util", "virtio", "vmm-sys-util", diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 033220a27..560176e78 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -16,5 +16,6 @@ serde = { version = "1.0", features = ["derive"] } devices = { path = "../../devices" } util = { path = "../../util" } acpi = { path = "../../acpi" } +smbios = { path = "../../smbios" } machine = { path = "../../machine" } virtio = { path = "../../virtio"} diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index 07712f0fe..fedc98359 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -227,6 +227,167 @@ fn test_boot_index() { } } +#[test] +fn test_smbios_tyep0() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + let mut extra_args = "-smbios type=0,vendor=vendor0,version=version0,date=date0" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let anchor_file = "etc/smbios/smbios-anchor"; + let tables_file = "etc/smbios/smbios-tables"; + let mut read_data: Vec = Vec::with_capacity(24); + + // Select FileDir entry and read it. + let anchor_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + anchor_file, + &mut read_data, + 24 as u32, + ); + + assert_eq!(anchor_size, 24 as u32); + assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); + assert_eq!(read_data[6], 24 as u8); + let talble_len = LittleEndian::read_u32(&read_data[12..]); + assert_eq!(talble_len, 109); + + let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); + let talbles_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + tables_file, + &mut read_table_date, + talble_len, + ); + assert_eq!(talbles_size, talble_len); + let talbe_type0_len = 24; + assert_eq!( + String::from_utf8_lossy(&read_table_date[talbe_type0_len..talbe_type0_len + 7]), + "vendor0" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[talbe_type0_len + 8..talbe_type0_len + 16]), + "version0" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[talbe_type0_len + 17..talbe_type0_len + 22]), + "date0" + ); + + test_state.borrow_mut().stop(); +} + +#[test] +fn test_smbios_tyep1() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + let mut extra_args = "-smbios type=0,vendor=vendor0,version=version0,date=date0" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let mut extra_args = "-smbios type=1,manufacturer=manufacturer1,product=product1,\ + version=12.2.2,serial=181a6bdf-ff98-4c5e-97ec-bff35fe41f6c,uuid=181a6bdf-ff98-4c5e-97ec-bff35fe41f6c,\ + family=Virtual,sku=sku1" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let anchor_file = "etc/smbios/smbios-anchor"; + let tables_file = "etc/smbios/smbios-tables"; + let mut read_data: Vec = Vec::with_capacity(24); + + // Select FileDir entry and read it. + let anchor_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + anchor_file, + &mut read_data, + 24 as u32, + ); + + assert_eq!(anchor_size, 24 as u32); + assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); + assert_eq!(read_data[6], 24 as u8); + let talble_len = LittleEndian::read_u32(&read_data[12..]); + assert_eq!(talble_len, 162); + + let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); + let talbles_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + tables_file, + &mut read_table_date, + talble_len, + ); + assert_eq!(talbles_size, talble_len); + let talbe_type0_len = 24; + assert_eq!( + String::from_utf8_lossy(&read_table_date[talbe_type0_len..talbe_type0_len + 7]), + "vendor0" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[talbe_type0_len + 8..talbe_type0_len + 16]), + "version0" + ); + assert_eq!(read_table_date[48], 1); + assert_eq!(read_table_date[49], 27 as u8); + let handle1 = LittleEndian::read_u16(&read_table_date[50..]); + assert_eq!(handle1, 0x100); + + assert_eq!( + String::from_utf8_lossy(&read_table_date[75..88]), + "manufacturer1" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[89..97]), + "product1" + ); + assert_eq!(String::from_utf8_lossy(&read_table_date[98..104]), "12.2.2"); + assert_eq!( + String::from_utf8_lossy(&read_table_date[105..141]), + "181a6bdf-ff98-4c5e-97ec-bff35fe41f6c" + ); + assert_eq!(String::from_utf8_lossy(&read_table_date[142..146]), "sku1"); + assert_eq!( + String::from_utf8_lossy(&read_table_date[147..154]), + "Virtual" + ); + // check uuid + assert_eq!(read_table_date[56], 0xdf); + assert_eq!(read_table_date[57], 0x6b); + assert_eq!(read_table_date[58], 0x1a); + assert_eq!(read_table_date[59], 0x18); + + assert_eq!(read_table_date[60], 0x98); + assert_eq!(read_table_date[61], 0xff); + + assert_eq!(read_table_date[62], 0x5e); + assert_eq!(read_table_date[63], 0x4c); + + assert_eq!(read_table_date[64], 0x97); + assert_eq!(read_table_date[65], 0xec); + + assert_eq!(read_table_date[66], 0xbf); + assert_eq!(read_table_date[67], 0xf3); + assert_eq!(read_table_date[68], 0x5f); + assert_eq!(read_table_date[69], 0xe4); + assert_eq!(read_table_date[70], 0x1f); + assert_eq!(read_table_date[71], 0x6c); + + test_state.borrow_mut().stop(); +} + #[test] fn test_exception_by_ctrl_reg() { let mut args = Vec::new(); -- Gitee From d435fd8f63a5b53c3139e8a03bcafa346419e989 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 2 Jun 2023 15:22:37 +0800 Subject: [PATCH 1126/2187] PL011: Fix A-B deadlock Thread 1: get_notifier_handler (lock chardev) get_remain_space_size self.get_remain_space_size = Some(Arc::new(move || { cloned_dev.lock().unwrap().get_remain_space_size() })); (lock PL011) Thread 2: AddressSpace::write fr.owner.write write_ops(&slice, base, offset) let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { cloned_dev.lock().unwrap().write(data, addr, offset) }; (lock PL011) write if let Some(output) = &mut self.chardev.lock().unwrap().output (lock chardev) Fix this A-B deadlock. Signed-off-by: liuxiangdong --- devices/src/legacy/chardev.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 961d7b3ce..7a8f1d6ac 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -223,11 +223,18 @@ fn get_notifier_handler( ) -> Rc { match backend { ChardevType::Stdio | ChardevType::Pty => Rc::new(move |_, _| { + let locked_chardev = chardev.lock().unwrap(); + let get_remain_space_size = locked_chardev + .get_remain_space_size + .as_ref() + .unwrap() + .clone(); + drop(locked_chardev); + let buff_size = get_remain_space_size(); let locked_chardev = chardev.lock().unwrap(); if locked_chardev.deactivated { return None; } - let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); let mut buffer = vec![0_u8; buff_size]; let input_h = locked_chardev.input.clone(); let receive = locked_chardev.receive.clone(); @@ -264,10 +271,17 @@ fn get_notifier_handler( let inner_handler: Rc = Rc::new(move |event, _| { let mut locked_chardev = cloned_chardev.lock().unwrap(); if event == EventSet::IN { + let get_remain_space_size = locked_chardev + .get_remain_space_size + .as_ref() + .unwrap() + .clone(); + drop(locked_chardev); + let buff_size = get_remain_space_size(); + let locked_chardev = cloned_chardev.lock().unwrap(); if locked_chardev.deactivated { return None; } - let buff_size = locked_chardev.get_remain_space_size.as_ref().unwrap()(); let mut buffer = vec![0_u8; buff_size]; if let Some(input) = locked_chardev.input.clone() { if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { -- Gitee From 9d7c23440843ad2d7a5ce61f6f2e8fd8da18d0bf Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 29 May 2023 14:49:38 +0800 Subject: [PATCH 1127/2187] Implemented function to reset gicv3 reset is not implemented for gicv2 yet. The reset for irq_chip is explicitly called while handle_reset_request(). We asks StateTransfer for GICv3 create a reset_state for us and then load the state into GICv3. We also must explicitly reset ITS Signed-off-by: Dmitry Skorodumov --- .../src/interrupt_controller/aarch64/gicv3.rs | 36 +++++++++++++++++- .../src/interrupt_controller/aarch64/mod.rs | 10 +++++ .../src/interrupt_controller/aarch64/state.rs | 37 +++++++++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 2 + 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index e22f90f0d..79bf884e8 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -22,10 +22,11 @@ use anyhow::{anyhow, Context, Result}; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::DeviceFd; use log::error; +use log::info; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use migration::{ snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, - MigrationManager, + MigrationManager, StateTransfer, }; use util::device_tree::{self, FdtBuilder}; @@ -245,6 +246,20 @@ impl GICv3 { fn device_fd(&self) -> &DeviceFd { &self.fd } + + fn reset_its_state(&self) -> Result<()> { + if let Some(its) = &self.its_dev { + its.reset()?; + } + + Ok(()) + } + + fn reset_gic_state(&self) -> Result<()> { + let reset_state = self.create_reset_state()?; + self.set_state(&reset_state) + .with_context(|| "Failed to reset gic") + } } impl MachineLifecycle for GICv3 { @@ -442,6 +457,13 @@ impl GICDevice for GICv3 { Ok(()) } + fn reset(&self) -> Result<()> { + info!("Reset gicv3"); + + self.reset_its_state()?; + self.reset_gic_state() + } + fn get_redist_count(&self) -> u8 { self.redist_regions.len() as u8 } @@ -539,6 +561,18 @@ impl GICv3Its { true, ) } + + pub(crate) fn reset(&self) -> Result<()> { + info!("Reset gicv3 its"); + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_ITS_CTRL_RESET), + std::ptr::null::() as u64, + true, + ) + .with_context(|| "Failed to reset ITS") + } } #[cfg(test)] diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 8114d61d8..543e55bfb 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -125,6 +125,11 @@ pub trait GICDevice: MachineLifecycle { /// Realize function for kvm_based `GIC` device. fn realize(&self) -> Result<()>; + /// Reset 'GIC' + fn reset(&self) -> Result<()> { + Ok(()) + } + /// Constructs `fdt` node for `GIC`. /// /// # Arguments @@ -170,6 +175,11 @@ impl InterruptController { Ok(()) } + /// Reset the InterruptController + pub fn reset(&self) -> Result<()> { + self.gic.reset().with_context(|| "Failed to reset GIC") + } + /// Change `InterruptController` lifecycle state to `Stopped`. pub fn stop(&self) { self.gic diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 4be30ef6e..63652fa62 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -549,6 +549,43 @@ impl GICv3 { Ok(()) } + + pub(crate) fn create_reset_state(&self) -> Result> { + let mut gic_state = GICv3State::default(); + + self.access_gic_redistributor(GICR_TYPER, 0, &mut gic_state.redist_typer_l, false) + .with_context(|| "create_reset_state: redist_typer_l")?; + self.access_gic_redistributor(GICR_TYPER + 4, 0, &mut gic_state.redist_typer_h, false) + .with_context(|| "create_reset_state: redist_typer_h")?; + + // process cpu-state and redistriburor + gic_state.iccr_len = self.vcpu_count as usize; + gic_state.redist_len = self.vcpu_count as usize; + for cpu in 0..self.vcpu_count { + let mut gic_cpu = GICv3CPUState { + vcpu: cpu as usize, + ..Default::default() + }; + + gic_cpu.icc_sre_el1 = 0x7; + + // initialize to hardware supported configuration + self.access_gic_cpu(ICC_CTLR_EL1, cpu as usize, &mut gic_cpu.icc_ctlr_el1, false) + .with_context(|| format!("create_reset_state: VCPU-{} icc_ctlr_el1", cpu))?; + + gic_state.vcpu_iccr[cpu as usize] = gic_cpu; + // setup redist state + gic_state.vcpu_redist[cpu as usize] = GICv3RedistState { + vcpu: cpu as usize, + ..Default::default() + } + } + + // process distributor + gic_state.dist_len = (self.nr_irqs / 32) as usize; + + Ok(gic_state.as_bytes().to_vec()) + } } /// The status of GICv3 interrupt controller. diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index a77be4dd2..597979a17 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -272,6 +272,8 @@ impl StdMachine { event!(Reset; reset_msg); } + locked_vm.irq_chip.as_ref().unwrap().reset()?; + for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { cpu.resume() .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; -- Gitee From b26e4e9e31d1f5b0e3ac17979819b0727dfa1682 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 5 Jun 2023 16:14:45 +0800 Subject: [PATCH 1128/2187] bugfix: increase the timeout for usb-host reading the control For some old USB devices, it may take more than 10 ms to read control data. Therefore, increase the timeout to prevent data reading from timeout. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 3123636a8..4dce3f05b 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -508,7 +508,7 @@ impl UsbHost { device_req.value, device_req.index, &self.usb_device.data_buf[..device_req.length as usize], - Duration::from_millis(10), + Duration::from_millis(1000), ) { error!("Failed to write control by usb host: {:?}", e); packet.status = UsbPacketStatus::Stall; @@ -521,7 +521,7 @@ impl UsbHost { device_req.value, device_req.index, &mut self.usb_device.data_buf[..device_req.length as usize], - Duration::from_millis(10), + Duration::from_millis(1000), ) { Ok(n) => n as u32, Err(e) => { -- Gitee From a33a70e82d3ab6b27319218166322a3de550d7ed Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 31 May 2023 16:08:09 +0800 Subject: [PATCH 1129/2187] gtk: release all key when out of focus Signed-off-by: zhouli57 --- ui/src/gtk/draw.rs | 18 +++++++++++++++--- ui/src/input.rs | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 58f4beeba..c7494e4d0 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -27,9 +27,9 @@ use crate::{ console::graphic_hardware_ui_info, gtk::GtkDisplayScreen, input::{ - self, point_event, press_mouse, update_key_state, ABS_MAX, INPUT_BUTTON_WHEEL_DOWN, - INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_LEFT, - INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, + self, point_event, press_mouse, release_all_key, update_key_state, ABS_MAX, + INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, + INPUT_BUTTON_WHEEL_UP, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, }, }; @@ -85,6 +85,13 @@ pub(crate) fn set_callback_for_draw_area( ), ); + draw_area.connect_focus_out_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, _| { + da_focus_out_callback().unwrap_or_else(|e|error!("Focus out event: {:?}", e)); + Inhibit(false)} + ), + ); + let event_mask = EventMask::BUTTON_PRESS_MASK | EventMask::BUTTON_RELEASE_MASK | EventMask::BUTTON_MOTION_MASK @@ -92,6 +99,7 @@ pub(crate) fn set_callback_for_draw_area( | EventMask::KEY_PRESS_MASK | EventMask::KEY_RELEASE_MASK | EventMask::BUTTON1_MOTION_MASK + | EventMask::FOCUS_CHANGE_MASK | EventMask::POINTER_MOTION_MASK; draw_area.add_events(event_mask); @@ -119,6 +127,10 @@ fn da_configure_callback( graphic_hardware_ui_info(con, width, height) } +fn da_focus_out_callback() -> Result<()> { + release_all_key() +} + fn da_key_callback( gs: &Rc>, key_event: &gdk::EventKey, diff --git a/ui/src/input.rs b/ui/src/input.rs index f9ee706c9..078de4f0c 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -359,6 +359,26 @@ pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { .keyboard_state_update(keycode, down) } +/// Release all pressed key. +pub fn release_all_key() -> Result<()> { + let mut locked_input = INPUTS.lock().unwrap(); + for &(_, keycode) in KEYSYM2KEYCODE.iter() { + if locked_input + .keyboard_state + .keystate + .contain(keycode as usize)? + { + locked_input + .keyboard_state + .keyboard_state_update(keycode, false)?; + if let Some(k) = locked_input.get_active_kbd().as_ref() { + k.lock().unwrap().do_key_event(keycode, false)?; + } + } + } + Ok(()) +} + pub fn get_kbd_led_state(state: u8) -> bool { LED_STATE.lock().unwrap().kbd_led & state == state } -- Gitee From f8f9502c63ef6a943e2259c12efb546147a3efc4 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 30 May 2023 12:06:19 +0800 Subject: [PATCH 1130/2187] Scream: Optimize the playback and capture process Add a destroy method for the AudioInterface trait. Destroy the stream of playback or capture when it ends. Persistently send or receive chunks until the stream ends instead of cyclically send or receive a chunk after checking whether the stream is ready. The benefit of modification is playing/capture the data as more as possible. It avoids checking whether the stream is ready after play/ capture a chunk of data every time. Signed-off-by: Jinhao Gao --- devices/src/misc/scream/audio_demo.rs | 2 + devices/src/misc/scream/mod.rs | 72 ++++++++++++++------------- devices/src/misc/scream/pulseaudio.rs | 14 ++++++ 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/devices/src/misc/scream/audio_demo.rs b/devices/src/misc/scream/audio_demo.rs index 9ea063f34..02f0435f6 100644 --- a/devices/src/misc/scream/audio_demo.rs +++ b/devices/src/misc/scream/audio_demo.rs @@ -78,4 +78,6 @@ impl AudioInterface for AudioDemo { size == data.len() } + + fn destroy(&mut self) {} } diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 02fb6f712..18f54029b 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -166,6 +166,7 @@ impl StreamData { fn wait_for_ready( &mut self, + interface: Arc>, dir: ScreamDirection, poll_delay_us: u64, hva: u64, @@ -182,6 +183,7 @@ impl StreamData { loop { if header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { + interface.lock().unwrap().destroy(); while header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { thread::sleep(time::Duration::from_millis(10)); header = @@ -218,24 +220,6 @@ impl StreamData { } } - fn update_play_buffer(&mut self, hva: u64) { - // SAFETY: hva is the shared memory base address. It already verifies the validity - // of the address range during the header check. - let header = &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; - let play = header.play; - - self.chunk_idx = (self.chunk_idx + 1) % play.max_chunks; - - // If the difference between the currently processed chunk_idx and the chunk_idx in - // the shared memory is greater than 3, the processing of the backend device is too - // slow and the backward data is skipped. - if (play.chunk_idx + play.max_chunks - self.chunk_idx) % play.max_chunks > 3 { - self.chunk_idx = (play.chunk_idx + play.max_chunks - 1) % play.max_chunks; - } - - self.update_buffer_by_chunk_idx(hva, &play); - } - fn update_buffer_by_chunk_idx(&mut self, hva: u64, stream_header: &ShmemStreamHeader) { self.audio_size = stream_header.chunk_size; self.audio_base = hva @@ -243,25 +227,46 @@ impl StreamData { + (stream_header.chunk_size as u64) * (self.chunk_idx as u64); } - fn update_capt_buffer(&mut self, hva: u64) { + fn playback_trans(&mut self, hva: u64, interface: Arc>) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; + let play = &header.play; + + while play.fmt.fmt_generation == self.fmt.fmt_generation && self.chunk_idx != play.chunk_idx + { + // If the difference between the currently processed chunk_idx and the chunk_idx in + // the shared memory is greater than 4, the processing of the backend device is too + // slow and the backward data is skipped. + if (play.chunk_idx + play.max_chunks - self.chunk_idx) % play.max_chunks > 4 { + self.chunk_idx = (play.chunk_idx + play.max_chunks - 1) % play.max_chunks; + } else { + self.chunk_idx = (self.chunk_idx + 1) % play.max_chunks; + } - self.update_buffer_by_chunk_idx(hva, &header.capt); + self.update_buffer_by_chunk_idx(hva, play); + interface.lock().unwrap().send(self); + } } - fn update_capt_idx(&mut self, hva: u64) { + fn capture_trans(&mut self, hva: u64, interface: Arc>) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; + let capt = &mut header.capt; - self.chunk_idx = (self.chunk_idx + 1) % header.capt.max_chunks; + while capt.is_started != 0 { + self.update_buffer_by_chunk_idx(hva, capt); - // Make sure chunk_idx write does not bypass audio chunk write. - fence(Ordering::SeqCst); + if interface.lock().unwrap().receive(self) { + self.chunk_idx = (self.chunk_idx + 1) % capt.max_chunks; - header.capt.chunk_idx = self.chunk_idx; + // Make sure chunk_idx write does not bypass audio chunk write. + fence(Ordering::SeqCst); + + capt.chunk_idx = self.chunk_idx; + } + } } } @@ -306,23 +311,23 @@ impl Scream { fn start_play_thread_fn(&self) -> Result<()> { let hva = self.hva; let shmem_size = self.size; - let interface = self.interface_init("Scream", ScreamDirection::Playback); + let interface = self.interface_init("ScreamPlay", ScreamDirection::Playback); thread::Builder::new() .name("scream audio play worker".to_string()) .spawn(move || { - let mut interface_locked = interface.lock().unwrap(); + let clone_interface = interface.clone(); let mut play_data = StreamData::default(); loop { play_data.wait_for_ready( + clone_interface.clone(), ScreamDirection::Playback, POLL_DELAY_US, hva, shmem_size, ); - play_data.update_play_buffer(hva); - interface_locked.send(&play_data); + play_data.playback_trans(hva, clone_interface.clone()); } }) .with_context(|| "Failed to create thread scream")?; @@ -336,21 +341,19 @@ impl Scream { thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { - let mut interface_locked = interface.lock().unwrap(); + let clone_interface = interface.clone(); let mut capt_data = StreamData::default(); loop { capt_data.wait_for_ready( + clone_interface.clone(), ScreamDirection::Record, POLL_DELAY_US, hva, shmem_size, ); - capt_data.update_capt_buffer(hva); - if interface_locked.receive(&capt_data) { - capt_data.update_capt_idx(hva); - } + capt_data.capture_trans(hva, clone_interface.clone()); } }) .with_context(|| "Failed to create thread scream")?; @@ -391,4 +394,5 @@ impl Scream { pub trait AudioInterface: Send { fn send(&mut self, recv_data: &StreamData); fn receive(&mut self, recv_data: &StreamData) -> bool; + fn destroy(&mut self); } diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 7eb3201fa..1c04c2088 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -280,6 +280,20 @@ impl AudioInterface for PulseStreamData { true } + + fn destroy(&mut self) { + if self.simple.is_none() { + return; + } + if self.dir == Direction::Playback { + if let Err(e) = self.simple.as_ref().unwrap().drain() { + error!("Failed to drain Playback stream: {:?}", e); + } + } else if let Err(e) = self.simple.as_ref().unwrap().flush() { + error!("Failed to flush Capture stream: {:?}", e); + } + self.simple = None; + } } #[cfg(test)] -- Gitee From a1e73c803fc54dcd195c6ef769d03a57bd92d0be Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 30 May 2023 12:11:31 +0800 Subject: [PATCH 1131/2187] Scream: Extend scream to support ALSA PulseAudio does not support usb headset. So add the support for ALSA. Signed-off-by: Mingwang Li Signed-off-by: Jinhao Gao --- Cargo.lock | 34 ++++ Makefile | 1 + devices/Cargo.toml | 1 + devices/src/misc/scream/alsa.rs | 274 ++++++++++++++++++++++++++ devices/src/misc/scream/mod.rs | 13 +- devices/src/misc/scream/pulseaudio.rs | 8 +- docs/config_guidebook.md | 3 +- machine_manager/src/config/scream.rs | 2 +- 8 files changed, 326 insertions(+), 10 deletions(-) create mode 100644 devices/src/misc/scream/alsa.rs diff --git a/Cargo.lock b/Cargo.lock index 611c3a695..49375acad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "alsa" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44" +dependencies = [ + "alsa-sys", + "bitflags", + "libc", + "nix", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -346,6 +368,7 @@ version = "2.2.0" dependencies = [ "acpi", "address_space", + "alsa", "anyhow", "byteorder", "cairo-rs", @@ -1115,6 +1138,17 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "5.1.2" diff --git a/Makefile b/Makefile index 54b8b763a..a1eb80233 100644 --- a/Makefile +++ b/Makefile @@ -22,3 +22,4 @@ yum-deps: @yum install clang @yum install gtk3-devel @yum install libusbx + @yum install alsa-lib-devel diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 2ed596caf..eb022f2f5 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -41,6 +41,7 @@ psimple = { version = "2.0", package = "libpulse-simple-binding" } rusb = "0.9" libusb1-sys = "0.6.4" cairo-rs = "0.14.9" +alsa = "0.7.0" [dev-dependencies] serial_test = "0.5.1" diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs new file mode 100644 index 000000000..4c830637c --- /dev/null +++ b/devices/src/misc/scream/alsa.rs @@ -0,0 +1,274 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + cmp::min, + io::{Read, Write}, + sync::atomic::{fence, Ordering}, +}; + +use alsa::{ + pcm::{Access, Format, HwParams}, + Direction, ValueOr, PCM, +}; +use anyhow::Result; +use log::{debug, error, warn}; + +use super::{ + pulseaudio::TAGET_LATENCY_MS, AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, + AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, +}; + +const MAX_CHANNELS: u8 = 8; +const MIN_CHANNELS: u8 = 1; +const MAX_FRAME_NUM: u32 = 240; + +pub struct AlsaStreamData { + pcm: Option, + dir: Direction, + format: Format, + bytes_per_sample: u32, + stream_fmt: ShmemStreamFmt, + rate: u32, + latency: u32, + app_name: String, + init: bool, +} + +impl ScreamDirection { + fn trans_to_alsa(&self) -> Direction { + match self { + Self::Playback => Direction::Playback, + Self::Record => Direction::Capture, + } + } +} + +impl AlsaStreamData { + pub fn init(name: &str, dir: ScreamDirection) -> Self { + // Init receiver format to track changes. + let stream_fmt = ShmemStreamFmt::default(); + + let alsa_dir = dir.trans_to_alsa(); + + Self { + pcm: None, + dir: alsa_dir, + format: Format::S16LE, + bytes_per_sample: 0, + stream_fmt, + rate: AUDIO_SAMPLE_RATE_44KHZ, + latency: TAGET_LATENCY_MS, + app_name: name.to_string(), + init: false, + } + } + + fn setup(&mut self, channels: u8) -> Result<()> { + let pcm = PCM::new("default", self.dir, false)?; + { + // Set hardware parameters of the stream. + let hwp = HwParams::any(&pcm)?; + hwp.set_rate_resample(true)?; + hwp.set_access(Access::RWInterleaved)?; + hwp.set_format(self.format)?; + hwp.set_channels(channels as u32)?; + hwp.set_rate(self.rate, ValueOr::Nearest)?; + // Set the latency in microseconds. + hwp.set_buffer_time_near(self.latency * 1000, ValueOr::Nearest)?; + pcm.hw_params(&hwp)?; + + // Set software parameters of the stream. + let hwp = pcm.hw_params_current()?; + let swp = pcm.sw_params_current()?; + swp.set_start_threshold(hwp.get_buffer_size().unwrap())?; + pcm.sw_params(&swp)?; + } + self.pcm = Some(pcm); + Ok(()) + } + + fn check_fmt_update(&mut self, recv_data: &StreamData) -> bool { + if self.init && self.stream_fmt.fmt_generation == recv_data.fmt.fmt_generation { + return true; + } + + self.destroy(); + + // If audio format changed, reconfigure. + self.stream_fmt = recv_data.fmt; + self.rate = if recv_data.fmt.rate >= WINDOWS_SAMPLE_BASE_RATE { + AUDIO_SAMPLE_RATE_44KHZ + } else { + AUDIO_SAMPLE_RATE_48KHZ + } * (recv_data.fmt.rate % WINDOWS_SAMPLE_BASE_RATE) as u32; + + match recv_data.fmt.size { + 16 => { + self.format = Format::S16LE; + self.bytes_per_sample = 2; + } + 24 => { + self.format = Format::S243LE; + self.bytes_per_sample = 3; + } + 32 => { + self.format = Format::S32LE; + self.bytes_per_sample = 4; + } + _ => { + warn!( + "Unsupported sample size {} for {}, wait next format switch", + self.app_name, recv_data.fmt.size + ); + self.rate = 0; + } + } + + if self.rate == 0 { + self.init = false; + warn!("Configure wrong rate {} for {}", self.app_name, self.rate); + return false; + } + + if recv_data.fmt.channels < MIN_CHANNELS || recv_data.fmt.channels > MAX_CHANNELS { + self.init = false; + warn!( + "Configure wrong channels {} for {}", + self.app_name, recv_data.fmt.channels + ); + return false; + } + + match self.setup(recv_data.fmt.channels) { + Err(e) => { + error!( + "Failed to set up ALSA HW parameters and SW parameters for {}: {:?}", + self.app_name, e + ); + self.init = false; + } + Ok(_) => self.init = true, + } + self.init + } +} + +impl AudioInterface for AlsaStreamData { + fn send(&mut self, recv_data: &StreamData) { + if !self.check_fmt_update(recv_data) { + self.destroy(); + return; + } + + let mut frames = 0; + let mut io = self.pcm.as_ref().unwrap().io_bytes(); + + // Make sure audio read does not bypass chunk_idx read. + fence(Ordering::Acquire); + + // SAFETY: audio_base is the shared memory. It already verifies the validity + // of the address range during the header check. + let data = unsafe { + std::slice::from_raw_parts( + recv_data.audio_base as *const u8, + recv_data.audio_size as usize, + ) + }; + + let samples = + recv_data.audio_size / (self.bytes_per_sample * recv_data.fmt.channels as u32); + while frames < samples { + let send_frame_num = min(samples - frames, MAX_FRAME_NUM); + let offset = (frames * self.bytes_per_sample * recv_data.fmt.channels as u32) as usize; + let end = offset + + (send_frame_num * self.bytes_per_sample * recv_data.fmt.channels as u32) as usize; + match io.write(&data[offset..end]) { + Err(e) => { + debug!("Failed to write data to ALSA buffer: {:?}", e); + match self.pcm.as_ref().unwrap().prepare() { + Err(e) => { + error!("Can't recovery from underrun for playback: {:?}", e); + self.init = false; + } + Ok(_) => continue, + }; + } + Ok(n) => { + frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); + } + } + } + } + + fn receive(&mut self, recv_data: &StreamData) -> bool { + if !self.check_fmt_update(recv_data) { + self.destroy(); + return false; + } + + let mut frames = 0; + let mut io = self.pcm.as_ref().unwrap().io_bytes(); + + // Make sure audio read does not bypass chunk_idx read. + fence(Ordering::Acquire); + + // SAFETY: audio_base is the shared memory. It already verifies the validity + // of the address range during the header check. + let data = unsafe { + std::slice::from_raw_parts_mut( + recv_data.audio_base as *mut u8, + recv_data.audio_size as usize, + ) + }; + + let samples = + recv_data.audio_size / (self.bytes_per_sample * recv_data.fmt.channels as u32); + while frames < samples { + let offset = (frames * self.bytes_per_sample * recv_data.fmt.channels as u32) as usize; + let end = offset + + ((samples - frames) * self.bytes_per_sample * recv_data.fmt.channels as u32) + as usize; + match io.read(&mut data[offset..end]) { + Err(e) => { + debug!("Failed to read data from ALSA buffer: {:?}", e); + match self.pcm.as_ref().unwrap().prepare() { + Err(e) => { + error!("Can't recovery from overrun for capture: {:?}", e); + self.init = false; + } + Ok(_) => continue, + }; + } + Ok(n) => { + frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); + } + } + } + true + } + + fn destroy(&mut self) { + if self.pcm.is_some() { + if self.dir == Direction::Playback { + self.pcm + .as_ref() + .unwrap() + .drain() + .unwrap_or_else(|e| error!("Failed to drain: {:?}", e)); + } + self.pcm = None; + } + + self.init = false; + } +} diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 18f54029b..610928d56 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod alsa; mod audio_demo; mod pulseaudio; @@ -27,12 +28,17 @@ use anyhow::{bail, Context, Result}; use core::time; use log::{error, warn}; -use self::audio_demo::AudioDemo; +use self::{alsa::AlsaStreamData, audio_demo::AudioDemo}; use super::ivshmem::Ivshmem; use machine_manager::config::scream::ScreamConfig; use pci::{PciBus, PciDevOps}; use pulseaudio::{PulseStreamData, TAGET_LATENCY_MS}; +pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; +pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; + +pub const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; + // A frame of back-end audio data is 50ms, and the next frame of audio data needs // to be trained in polling within 50ms. Theoretically, the shorter the polling time, // the better. However, if the value is too small, the overhead is high. So take a @@ -292,6 +298,7 @@ impl Scream { fn interface_init(&self, name: &str, dir: ScreamDirection) -> Arc> { match self.interface.as_str() { + "ALSA" => Arc::new(Mutex::new(AlsaStreamData::init(name, dir))), "PulseAudio" => Arc::new(Mutex::new(PulseStreamData::init(name, dir))), "Demo" => Arc::new(Mutex::new(AudioDemo::init( dir, @@ -300,10 +307,10 @@ impl Scream { ))), _ => { error!( - "Unsupported audio interface {}, falling back to Pulseaudio", + "Unsupported audio interface {}, falling back to ALSA", self.interface ); - Arc::new(Mutex::new(PulseStreamData::init(name, dir))) + Arc::new(Mutex::new(AlsaStreamData::init(name, dir))) } } } diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 1c04c2088..6bc089ede 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -22,13 +22,11 @@ use pulse::{ time::MicroSeconds, }; -use super::AudioInterface; +use super::{ + AudioInterface, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, +}; use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData}; -const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; -const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; -const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; - pub const TAGET_LATENCY_MS: u32 = 50; const MAX_LATENCY_MS: u32 = 100; diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index e088790e3..9fe66784d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1033,7 +1033,8 @@ ivshmem-scream is a virtual sound card that relies on Intel-VM shared memory to Nine properties are supported for ivshmem-scream device. * id: unique device id. * memdev: configuration of the back-end memory device used by the ivshmem. -* interface: configuring audio playback and recording interfaces, currently can be set to `PulseAudio` or `Demo`. +* interface: configuring audio playback and recording interfaces, currently can be set to `ALSA`, `PulseAudio` or `Demo`. +`ALSA` is used by default. * playback: Path for storing audio. When interface is set to Demo, playback is mandatory. * record: Path for obtaining audio. When interface is set to Demo, record is mandatory. * bus: bus number of the device. diff --git a/machine_manager/src/config/scream.rs b/machine_manager/src/config/scream.rs index 24f994bb5..ea715c11b 100644 --- a/machine_manager/src/config/scream.rs +++ b/machine_manager/src/config/scream.rs @@ -25,7 +25,7 @@ impl ScreamConfig { pub fn new() -> Self { Self { memdev: "".to_string(), - interface: "PulseAudio".to_string(), + interface: "ALSA".to_string(), playback: "".to_string(), record: "".to_string(), } -- Gitee From 5ce9fb7d3db0cf41faa1e50188e1cf492c0674ca Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Jun 2023 20:48:14 +0800 Subject: [PATCH 1132/2187] virtio-pci: Not send config change irq when device deactivated Signed-off-by: Keqian Zhu --- virtio/src/transport/virtio_pci.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 0df4b0127..e991904e1 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -657,11 +657,11 @@ impl VirtioPciDevice { VirtioInterruptType::Config => { if needs_reset { device_status.fetch_or(CONFIG_STATUS_NEEDS_RESET, Ordering::SeqCst); - if device_status.load(Ordering::Acquire) & CONFIG_STATUS_DRIVER_OK == 0 - { - return Ok(()); - } } + if device_status.load(Ordering::Acquire) & CONFIG_STATUS_DRIVER_OK == 0 { + return Ok(()); + } + // Use (CONFIG | VRING) instead of CONFIG, it can be used to solve the // IO stuck problem by change the device configure. interrupt_status.fetch_or( -- Gitee From 1619579b2e804e1f3913ab7735ce29f6a81fd85a Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 8 Jun 2023 13:08:56 +0800 Subject: [PATCH 1133/2187] bugfix: Resolves a high re-memory usage issue of balloon device The original configuration is still used after the system restarts, the numpages need set 0 when reset. Signed-off-by: jiewangqun --- virtio/src/device/balloon.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index c5b882afc..5a0ecc4ba 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1147,6 +1147,13 @@ impl VirtioDevice for Balloon { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.deactivate_evts) } + + fn reset(&mut self) -> Result<()> { + if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + self.num_pages = 0; + } + Ok(()) + } } pub fn qmp_balloon(target: u64) -> bool { -- Gitee From b9a79a59e563a0d088388fc0e8324d7730a7eebc Mon Sep 17 00:00:00 2001 From: Zhang Bo Date: Thu, 8 Jun 2023 20:26:07 +0800 Subject: [PATCH 1134/2187] usb camera: make device name more readable in windows guest In the Device Manager of a Windows OS, the usb camera's name is from the string ID of IAD descriptor, rather than the Product field. Thus, we should make IAD string more readable, change it to "StratoVirt Camera". Signed-off-by: Zhang Bo --- devices/src/usb/camera.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index ffe541e20..e5286190b 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -130,7 +130,7 @@ const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ "USB Camera", "1", "USB Camera Configuration", - "USB Camera Interface Associated Description", + "StratoVirt Camera", "Video Control", "Input Terminal", "Output Terminal", -- Gitee From 3576531ec8dd581c7c8688b98a1e2035995f6fa7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 04:46:06 +0800 Subject: [PATCH 1135/2187] virtio-gpu: Refactor of some request handlers Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 53 +++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index d264b152c..59603b167 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -533,6 +533,20 @@ fn is_rect_in_resource(rect: &VirtioGpuRect, res: &GpuResource) -> bool { } impl GpuIoHandler { + fn change_run_stage(&self) -> Result<()> { + if get_run_stage() == VmRunningStage::Bios && !self.scanouts.is_empty() { + match &self.scanouts[0].con.as_ref().and_then(|c| c.upgrade()) { + Some(con) => { + let dev_name = con.lock().unwrap().dev_name.clone(); + display_set_major_screen(&dev_name)?; + set_run_stage(VmRunningStage::Os); + } + None => {} + }; + } + Ok(()) + } + fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { iov_to_buf_direct(&header.out_iovec, 0, req.as_mut_bytes()).and_then(|size| { if size == size_of::() { @@ -767,6 +781,7 @@ impl GpuIoHandler { fn cmd_get_edid(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut edid_req = VirtioGpuGetEdid::default(); + self.change_run_stage()?; self.get_request(req, &mut edid_req)?; if edid_req.scanouts >= self.num_scanouts { @@ -804,12 +819,11 @@ impl GpuIoHandler { return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } - if let Some(res) = self - .resources_list - .iter() - .find(|&x| x.resource_id == info_create_2d.resource_id) - { - error!("GuestError: resource {} already exists.", res.resource_id); + if self.get_resource_idx(info_create_2d.resource_id).is_some() { + error!( + "GuestError: resource {} already exists.", + info_create_2d.resource_id + ); return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, req); } @@ -1296,23 +1310,10 @@ impl GpuIoHandler { VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => self.cmd_transfer_to_host_2d(req), VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self.cmd_resource_attach_backing(req), VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self.cmd_resource_detach_backing(req), - VIRTIO_GPU_CMD_GET_EDID => { - if get_run_stage() == VmRunningStage::Bios && !self.scanouts.is_empty() { - match &self.scanouts[0].con.as_ref().and_then(|c| c.upgrade()) { - Some(con) => { - let dev_name = con.lock().unwrap().dev_name.clone(); - display_set_major_screen(&dev_name)?; - set_run_stage(VmRunningStage::Os); - } - None => {} - }; - } - self.cmd_get_edid(req) - } + VIRTIO_GPU_CMD_GET_EDID => self.cmd_get_edid(req), _ => self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), } { error!("Fail to handle GPU request, {:?}.", e); - self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req)?; } } @@ -1322,8 +1323,6 @@ impl GpuIoHandler { fn ctrl_queue_evt_handler(&mut self) -> Result<()> { let mut queue = self.ctrl_queue.lock().unwrap(); let mut req_queue = Vec::new(); - let mut need_break = false; - let mut invalid_elem_index = 0; loop { let mut elem = queue @@ -1340,23 +1339,15 @@ impl GpuIoHandler { Err(e) => { error!( "GuestError: Request will be ignored, because request header is incomplete and {:?}. \ - Also, be aware that the virtual machine may suspended if response does not \ - carry the necessary information.", + Also, be aware that the virtual machine may suspended as response is not sent.", e ); - need_break = true; - invalid_elem_index = elem.index; - break; } } } - drop(queue); self.process_control_queue(req_queue)?; - if need_break { - self.complete_one_request(invalid_elem_index, 0)?; - } Ok(()) } -- Gitee From 4147e4fca8ee327f97e66d34b701e8ef806165d5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 05:30:29 +0800 Subject: [PATCH 1136/2187] virtio-gpu: Refactor cmd_update_cursor() factor out update_cursor_image(). Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 129 +++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 59603b167..a51747398 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -663,6 +663,42 @@ impl GpuIoHandler { } } + fn update_cursor_image(&mut self, info_cursor: &VirtioGpuUpdateCursor) { + let res_idx = self.get_resource_idx(info_cursor.resource_id); + if res_idx.is_none() { + return; + } + + let res = &self.resources_list[res_idx.unwrap()]; + let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; + let mse = scanout.mouse.as_mut().unwrap(); + let mse_data_size = mse.data.len(); + unsafe { + let res_width = pixman_image_get_width(res.pixman_image); + let res_height = pixman_image_get_height(res.pixman_image); + if res_width as u32 != mse.width || res_height as u32 != mse.height { + return; + } + + let res_data_ptr = pixman_image_get_data(res.pixman_image) as *mut u8; + ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); + } + + // Windows front-end drvier does not deliver data in format sequence. + // So we fix it in back-end. + // TODO: Fix front-end driver is a better solution. + if res.format == VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM + || res.format == VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM + { + let mut i = 0; + while i < mse_data_size { + mse.data.swap(i, i + 2); + i += 4; + } + } + scanout.cursor_visible = true; + } + fn cmd_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_cursor = VirtioGpuUpdateCursor::default(); self.get_request(req, &mut info_cursor)?; @@ -696,63 +732,32 @@ impl GpuIoHandler { *item = 0_u8; } } - display_cursor_define(&scanout.con, scanout.mouse.as_mut().unwrap())?; scanout.cursor_visible = false; } } else if req.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { - if scanout.mouse.is_none() { - let tmp_mouse = DisplayMouse { - height: 64, - width: 64, - hot_x: info_cursor.hot_x, - hot_y: info_cursor.hot_y, - data: vec![0_u8; 64 * 64 * size_of::()], - }; - scanout.mouse = Some(tmp_mouse); - } else { - let mut mse = scanout.mouse.as_mut().unwrap(); - mse.hot_x = info_cursor.hot_x; - mse.hot_y = info_cursor.hot_y; - } - if info_cursor.resource_id != 0 { - if let Some(res_index) = self - .resources_list - .iter() - .position(|x| x.resource_id == info_cursor.resource_id) - { - let res = &self.resources_list[res_index]; - unsafe { - let res_width = pixman_image_get_width(res.pixman_image); - let res_height = pixman_image_get_height(res.pixman_image); - let mse = scanout.mouse.as_mut().unwrap(); - - if res_width as u32 == mse.width && res_height as u32 == mse.height { - let res_data_ptr = pixman_image_get_data(res.pixman_image) as *mut u8; - let mse_data_size = mse.data.len(); - ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); - // Front-end drvier does not deliver data in format sequence. - // So we fix it in back-end. - // - // TODO: Fix front-end driver is a better solution. - if res.format == VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM - || res.format == VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM - { - let mut i = 0; - while i < mse_data_size { - mse.data.swap(i, i + 2); - i += 4; - } - } - scanout.cursor_visible = true; - } - } + match &mut scanout.mouse { + None => { + let tmp_mouse = DisplayMouse { + height: 64, + width: 64, + hot_x: info_cursor.hot_x, + hot_y: info_cursor.hot_y, + data: vec![0_u8; 64 * 64 * size_of::()], + }; + scanout.mouse = Some(tmp_mouse); + } + Some(mouse) => { + mouse.hot_x = info_cursor.hot_x; + mouse.hot_y = info_cursor.hot_y; } } - if let Some(mouse) = &mut scanout.mouse { - display_cursor_define(&scanout.con, mouse)?; + if info_cursor.resource_id > 0 { + self.update_cursor_image(&info_cursor); } + let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; + display_cursor_define(&scanout.con, scanout.mouse.as_mut().unwrap())?; scanout.cursor = info_cursor; } else { bail!("Wrong header type for cursor queue"); @@ -1376,27 +1381,21 @@ impl GpuIoHandler { } }; - if let Err(e) = queue.vring.add_used(&self.mem_space, elem.index, 0) { - bail!( - "Failed to add used ring(cursor), index {} {:?}", - elem.index, - e - ); - } + queue + .vring + .add_used(&self.mem_space, elem.index, 0) + .with_context(|| { + format!("Failed to add used ring(cursor), index {}", elem.index) + })?; if queue .vring .should_notify(&self.mem_space, self.driver_features) { - if let Err(e) = - (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) - { - error!("{:?}", e); - return Err(anyhow!(VirtioError::InterruptTrigger( - "gpu cursor", - VirtioInterruptType::Vring - ))); - } + (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) + .with_context(|| { + VirtioError::InterruptTrigger("gpu cursor", VirtioInterruptType::Vring) + })?; } } -- Gitee From 21ed82306250cc52af4be3de95eb4b726c7b44b5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 21 May 2023 07:39:27 +0800 Subject: [PATCH 1137/2187] virtio-gpu: Adjust import order of module Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 47 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index a51747398..309e0fca0 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -10,25 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, - VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, - VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, - VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, - VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, - VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, -}; -use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, bail, Context, Result}; -use log::{error, warn}; -use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use migration_derive::ByteCode; use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; @@ -36,14 +17,24 @@ use std::rc::Rc; use std::slice::from_raw_parts_mut; use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; + +use anyhow::{anyhow, bail, Context, Result}; +use log::{error, warn}; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +use address_space::{AddressSpace, GuestAddress}; +use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use migration_derive::ByteCode; use ui::console::{ console_close, console_init, display_cursor_define, display_graphic_update, display_replace_surface, display_set_major_screen, get_run_stage, set_run_stage, ConsoleType, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, VmRunningStage, }; use ui::pixman::unref_pixman_image; -use util::aio::{iov_from_buf_direct, iov_to_buf_direct}; +use util::aio::{iov_from_buf_direct, iov_to_buf_direct, Iovec}; use util::byte_code::ByteCode; +use util::edid::EdidInfo; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -56,8 +47,20 @@ use util::pixman::{ pixman_region_init, pixman_region_init_rect, pixman_region_intersect, pixman_region_translate, virtio_gpu_unref_resource_callback, }; -use util::{aio::Iovec, edid::EdidInfo}; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +use crate::{ + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, + VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, + VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, + VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, +}; /// Number of virtqueues const QUEUE_NUM_GPU: usize = 2; -- Gitee From 7aed6cacbbfa2d0958158e3d26e8e31baadf62e4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Jun 2023 03:53:30 +0800 Subject: [PATCH 1138/2187] virtio-gpu: Refactor logic of data transfer 2D Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 125 ++++++++++++--------------------------- 1 file changed, 39 insertions(+), 86 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 309e0fca0..6ea8448c8 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1110,104 +1110,57 @@ impl GpuIoHandler { fn cmd_transfer_to_host_2d_update_resource( &mut self, - info_transfer: &VirtioGpuTransferToHost2d, + trans_info: &VirtioGpuTransferToHost2d, res_idx: usize, - ) { + ) -> Result<()> { let res = &self.resources_list[res_idx]; let pixman_format; + let width; let bpp; let stride; let data; + // SAFETY: res.pixman_image has been checked valid. unsafe { pixman_format = pixman_image_get_format(res.pixman_image); + width = pixman_image_get_width(res.pixman_image) as u32; bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; - stride = pixman_image_get_stride(res.pixman_image); - data = pixman_image_get_data(res.pixman_image); - } - let data_cast: *mut u8 = data.cast(); - let mut dst_ofs: usize = (info_transfer.rect.y_coord * stride as u32 - + info_transfer.rect.x_coord * bpp) as usize; - // It can be considered that PARTIAL or complete image data is stored in - // the res.iov[]. And info_transfer.offset is the offset from res.iov[0].base - // to the start position of the resource we really want to update. - let mut src_ofs: usize = info_transfer.offset as usize; - // current iov's index - let mut iov_idx: usize = 0; - // current iov's offset - let mut iov_ofs: usize = 0; - let mut iov_len_sum: usize = 0; - - // move to correct iov - loop { - if iov_len_sum == src_ofs || iov_idx >= res.iov.len() { - break; - } - - if res.iov[iov_idx].iov_len as usize + iov_len_sum <= src_ofs { - iov_len_sum += res.iov[iov_idx].iov_len as usize; - iov_idx += 1; - } else { - iov_ofs = src_ofs - iov_len_sum; - break; - } - } - - if iov_idx >= res.iov.len() { - warn!("GuestWarn: the start pos of transfer data from guest is longer than resource's len."); - return; + stride = pixman_image_get_stride(res.pixman_image) as u32; + data = pixman_image_get_data(res.pixman_image).cast() as *mut u8; + } + + // When the dedicated area is continous. + if trans_info.rect.x_coord == 0 && trans_info.rect.width == width { + let offset_dst = (trans_info.rect.y_coord * stride) as usize; + let trans_size = (trans_info.rect.height * stride) as usize; + // SAFETY: offset_dst and trans_size do not exceeds data size. + let dst = unsafe { from_raw_parts_mut(data.add(offset_dst), trans_size) }; + iov_to_buf_direct(&res.iov, trans_info.offset, dst).map(|v| { + if v < trans_size { + warn!("No enough data is copied for transfer_to_host_2d"); + } + v + })?; + return Ok(()); } - // We divide regions into that need to be copied and can be skipped. - let src_cpy_section: usize = (info_transfer.rect.width * bpp) as usize; - let src_expected: usize = info_transfer.offset as usize - + ((info_transfer.rect.height - 1) * stride as u32) as usize - + (info_transfer.rect.width * bpp) as usize; - - loop { - if src_ofs >= src_expected || iov_idx >= res.iov.len() { - break; - } - - let iov_left = res.iov[iov_idx].iov_len as usize - iov_ofs; - - let pos = (src_ofs - info_transfer.offset as usize) % (stride as usize); - if pos >= src_cpy_section { - if pos + iov_left <= stride as usize { - src_ofs += iov_left; - dst_ofs += iov_left; - iov_idx += 1; - iov_ofs = 0; - } else { - src_ofs += stride as usize - pos; - dst_ofs += stride as usize - pos; - iov_ofs += stride as usize - pos; + // Otherwise transfer data line by line. + let mut offset_src = trans_info.offset as usize; + let mut offset_dst = + (trans_info.rect.y_coord * stride + trans_info.rect.x_coord * bpp) as usize; + let line_size = (trans_info.rect.width * bpp) as usize; + for _ in 0..trans_info.rect.height { + // SAFETY: offset_dst and line_size do not exceeds data size. + let dst = unsafe { from_raw_parts_mut(data.add(offset_dst), line_size) }; + iov_to_buf_direct(&res.iov, offset_src as u64, dst).map(|v| { + if v < line_size { + warn!("No enough data is copied for transfer_to_host_2d"); } - } else if pos + iov_left <= src_cpy_section { - unsafe { - ptr::copy( - (res.iov[iov_idx].iov_base as *const u8).add(iov_ofs), - data_cast.add(dst_ofs), - iov_left, - ); - } - src_ofs += iov_left; - dst_ofs += iov_left; - iov_idx += 1; - iov_ofs = 0; - } else { - // pos + iov_left > src_cpy_section - unsafe { - ptr::copy( - (res.iov[iov_idx].iov_base as *const u8).add(iov_ofs), - data_cast.add(dst_ofs), - src_cpy_section - pos, - ); - } - src_ofs += src_cpy_section - pos; - dst_ofs += src_cpy_section - pos; - iov_ofs += src_cpy_section - pos; - } + v + })?; + offset_src += stride as usize; + offset_dst += stride as usize; } + Ok(()) } fn cmd_transfer_to_host_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { @@ -1219,7 +1172,7 @@ impl GpuIoHandler { return self.response_nodata(error, req); } - self.cmd_transfer_to_host_2d_update_resource(&info_transfer, res_idx.unwrap()); + self.cmd_transfer_to_host_2d_update_resource(&info_transfer, res_idx.unwrap())?; self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } -- Gitee From 04b80efa7f2b7de711e06bfce300ddc75c9549d4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 11 Jun 2023 17:48:43 +0800 Subject: [PATCH 1139/2187] code_style: Fix clippy warning of newer rust version This makes preparation for upgrading rust version for stratovirt. Signed-off-by: Keqian Zhu --- boot_loader/src/x86_64/direct_boot/mod.rs | 2 +- cpu/src/aarch64/core_regs.rs | 4 +- devices/src/camera_backend/demo.rs | 2 +- devices/src/camera_backend/mod.rs | 9 +--- devices/src/camera_backend/v4l2.rs | 2 +- devices/src/usb/camera.rs | 4 +- devices/src/usb/usbhost/mod.rs | 26 +++------ devices/src/usb/xhci/xhci_pci.rs | 2 +- machine/src/standard_vm/mod.rs | 4 +- machine_manager/src/config/machine_config.rs | 18 ++----- machine_manager/src/machine.rs | 4 +- machine_manager/src/qmp/qmp_schema.rs | 9 +--- machine_manager/src/socket.rs | 9 ++-- pci/src/demo_device/dpy_device.rs | 4 +- pci/src/demo_device/kbd_pointer_device.rs | 11 ++-- pci/src/host.rs | 8 +-- ui/src/gtk/draw.rs | 10 ++-- ui/src/gtk/mod.rs | 8 +-- ui/src/vnc/client_io.rs | 4 +- util/src/aio/mod.rs | 6 +-- util/src/device_tree.rs | 3 +- util/src/unix.rs | 56 +++++++++----------- virtio/src/lib.rs | 2 +- virtio/src/vhost/user/client.rs | 2 +- 24 files changed, 82 insertions(+), 127 deletions(-) diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 99b019921..a68f3757f 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -88,7 +88,7 @@ fn load_bzimage(kernel_image: &mut File) -> Result { /// /// * Write image to guest memory failed. fn load_image(image: &mut File, start_addr: u64, sys_mem: &Arc) -> Result<()> { - let curr_loc = image.seek(SeekFrom::Current(0))?; + let curr_loc = image.stream_position()?; let len = image.seek(SeekFrom::End(0))?; image.seek(SeekFrom::Start(curr_loc))?; diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index 8ca13cbd4..eb53ddb4d 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -118,8 +118,8 @@ impl From for u64 { // KVM_REG_SIZE_* => KVM_REG_SIZE_U32/KVM_REG_SIZE_U64/KVM_REG_SIZE_U128 // calculate reg_id - KVM_REG_ARM64 as u64 - | register_size as u64 + KVM_REG_ARM64 + | register_size | u64::from(KVM_REG_ARM_CORE) | (reg_offset / size_of::()) as u64 } diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 10df2ac38..2c57f96ab 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -261,7 +261,7 @@ impl ImageFrame { } fn read_config(path: &str) -> Result { - let str = read_to_string(&path)?; + let str = read_to_string(path)?; let conf = serde_json::from_str::(&str)?; Ok(conf) } diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 64da5dbd6..1d6b34105 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -81,19 +81,14 @@ pub struct CamLensFmt { // TODO: to be extended. } -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Default)] pub enum FmtType { + #[default] Yuy2 = 0, Rgb565, Mjpg, } -impl Default for FmtType { - fn default() -> Self { - FmtType::Yuy2 - } -} - #[derive(Clone, Debug)] pub struct CameraFrame { pub width: u32, diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index f35f2fb46..b09605441 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -174,7 +174,7 @@ impl V4l2CameraBackend { break; } // NOTE: Only support discrete now. - if (frmsize.type_) != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE as u32 { + if (frmsize.type_) != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { continue; } let width = unsafe { frmsize.__bindgen_anon_1.discrete.width }; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index e5286190b..3a41817c9 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -1002,7 +1002,7 @@ impl UvcPayload { if iov_size <= header_len as u64 { bail!("Invalid iov size {}", iov_size); } - frame_data_size = iov_size as u64 - header_len as u64; + frame_data_size = iov_size - header_len as u64; } Ok(frame_data_size) } @@ -1084,7 +1084,7 @@ impl CameraIoHander { if locked_payload.payload_offset == 0 { // Payload start, add header. pkt.transfer_packet(&mut locked_payload.header, header_len); - locked_payload.payload_offset += header_len as usize; + locked_payload.payload_offset += header_len; iovecs = iov_discard_front_direct(&mut pkt.iovecs, pkt.actual_length as u64) .with_context(|| format!("Invalid iov size {}", pkt_size))?; } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 4dce3f05b..dc8134de8 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -246,7 +246,7 @@ impl UsbHost { self.ifs_num = conf.num_interfaces(); for i in 0..self.ifs_num { - if !match self.handle.as_ref().unwrap().kernel_driver_active(i as u8) { + if !match self.handle.as_ref().unwrap().kernel_driver_active(i) { Ok(rc) => { if !rc { self.ifs[i as usize].detached = true; @@ -263,7 +263,7 @@ impl UsbHost { self.handle .as_mut() .unwrap() - .detach_kernel_driver(i as u8) + .detach_kernel_driver(i) .unwrap_or_else(|e| error!("Failed to detach kernel driver: {:?}", e)); self.ifs[i as usize].detached = true; } @@ -288,7 +288,7 @@ impl UsbHost { self.handle .as_mut() .unwrap() - .attach_kernel_driver(i as u8) + .attach_kernel_driver(i) .unwrap_or_else(|e| error!("Failed to attach kernel driver: {:?}", e)); self.ifs[i as usize].detached = false; } @@ -301,7 +301,7 @@ impl UsbHost { Err(_) => return, }; - for (i, intf) in conf.interfaces().into_iter().enumerate() { + for (i, intf) in conf.interfaces().enumerate() { // The usb_deviec.altsetting indexs alternate settings by the interface number. // Get the 0th alternate setting first so that we can grap the interface number, // and then correct the alternate setting value if necessary. @@ -383,7 +383,7 @@ impl UsbHost { self.handle .as_mut() .unwrap() - .release_interface(i as u8) + .release_interface(i) .unwrap_or_else(|e| error!("Failed to release interface: {:?}", e)); self.ifs[i as usize].claimed = false; } @@ -408,13 +408,7 @@ impl UsbHost { let mut claimed = 0; for i in 0..self.ifs_num { - if self - .handle - .as_mut() - .unwrap() - .claim_interface(i as u8) - .is_ok() - { + if self.handle.as_mut().unwrap().claim_interface(i).is_ok() { self.ifs[i as usize].claimed = true; claimed += 1; if claimed == conf.num_interfaces() { @@ -482,13 +476,7 @@ impl UsbHost { } fn clear_halt(&mut self, pid: u8, index: u8) { - if self - .handle - .as_mut() - .unwrap() - .clear_halt(index as u8) - .is_err() - { + if self.handle.as_mut().unwrap().clear_halt(index).is_err() { warn!("Failed to clear halt"); } self.usb_device diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index becd8ab6c..7d5d43eb0 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -166,7 +166,7 @@ impl XhciPciDevice { } let usb_port = usb_port.unwrap(); let slot_id = usb_port.lock().unwrap().slot_id; - locked_xhci.detach_slot(slot_id as u32)?; + locked_xhci.detach_slot(slot_id)?; locked_xhci.port_update(&usb_port, true)?; // Unrealize device and discharge usb port. diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 7f197bda3..2c7c1d949 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1194,7 +1194,7 @@ impl DeviceInterface for StdMachine { fn query_vnc(&self) -> Response { #[cfg(not(target_env = "musl"))] if let Some(vnc_info) = qmp_query_vnc() { - return Response::create_response(serde_json::to_value(&vnc_info).unwrap(), None); + return Response::create_response(serde_json::to_value(vnc_info).unwrap(), None); } Response::create_error_response( qmp_schema::QmpErrorClass::GenericError( @@ -1661,7 +1661,7 @@ impl DeviceInterface for StdMachine { std::fs::OpenOptions::new() .read(true) .write(true) - .open(&file_name) + .open(file_name) .unwrap(), ); } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index f9dff4d5b..66982999f 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -130,26 +130,16 @@ pub struct CpuConfig { pub pmu: PmuConfig, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] pub enum PmuConfig { On, + #[default] Off, } -impl Default for PmuConfig { - fn default() -> Self { - PmuConfig::Off - } -} - -impl Default for ShutdownAction { - fn default() -> Self { - ShutdownAction::ShutdownActionPoweroff - } -} - -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ShutdownAction { + #[default] ShutdownActionPoweroff, ShutdownActionPause, } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index eadeeb74e..849f92d69 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -227,7 +227,7 @@ pub trait DeviceInterface { let target = Target { arch: "aarch64".to_string(), }; - Response::create_response(serde_json::to_value(&target).unwrap(), None) + Response::create_response(serde_json::to_value(target).unwrap(), None) } /// Query all events of StratoVirt. @@ -248,7 +248,7 @@ pub trait DeviceInterface { enabled: true, present: true, }; - Response::create_response(serde_json::to_value(&kvm).unwrap(), None) + Response::create_response(serde_json::to_value(kvm).unwrap(), None) } /// Query machine types supported by StratoVirt. diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 3921d81f8..ca3cec8aa 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1254,9 +1254,10 @@ pub struct StatusInfo { pub status: RunState, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub enum RunState { #[serde(rename = "debug")] + #[default] debug, #[serde(rename = "inmigrate")] inmigrate, @@ -1292,12 +1293,6 @@ pub enum RunState { preconfig, } -impl Default for RunState { - fn default() -> Self { - RunState::debug - } -} - /// migrate /// /// Migrates the current running guest to another VM or file. diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 81bd5bd67..90b4d1a44 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -353,8 +353,8 @@ impl SocketRWHandler { fn parse_fd(&mut self, mhdr: &msghdr) { // At least it should has one RawFd. - let min_cmsg_len = unsafe { CMSG_LEN(size_of::() as u32) as usize }; - if (mhdr.msg_controllen as usize) < min_cmsg_len { + let min_cmsg_len = unsafe { CMSG_LEN(size_of::() as u32) as u64 }; + if (mhdr.msg_controllen as u64) < min_cmsg_len { return; } @@ -363,9 +363,12 @@ impl SocketRWHandler { let scm = cmsg_hdr.unwrap(); if scm.cmsg_level == SOL_SOCKET && scm.cmsg_type == SCM_RIGHTS - && scm.cmsg_len as usize >= min_cmsg_len + && scm.cmsg_len as u64 >= min_cmsg_len { let fds = unsafe { + #[cfg(not(target_env = "musl"))] + let fd_num = (scm.cmsg_len - CMSG_LEN(0) as usize) / size_of::(); + #[cfg(target_env = "musl")] let fd_num = (scm.cmsg_len as usize - CMSG_LEN(0) as usize) / size_of::(); std::slice::from_raw_parts(CMSG_DATA(scm) as *const RawFd, fd_num) diff --git a/pci/src/demo_device/dpy_device.rs b/pci/src/demo_device/dpy_device.rs index 3de96d644..452b592e4 100644 --- a/pci/src/demo_device/dpy_device.rs +++ b/pci/src/demo_device/dpy_device.rs @@ -121,7 +121,7 @@ impl DisplayChangeListenerOperations for DpyInterface { } let mut i = 0; - let mut offset = y * stride as i32 + x * bpp as i32 / 8; + let mut offset = y * stride + x * bpp as i32 / 8; let count = w * bpp as i32 / 8; while i < h { error!( @@ -144,7 +144,7 @@ impl DisplayChangeListenerOperations for DpyInterface { offset + count, ds.image[offset as usize] ); - offset += stride as i32; + offset += stride; i += 1; } Ok(()) diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/pci/src/demo_device/kbd_pointer_device.rs index 0211a51c3..6637f2529 100644 --- a/pci/src/demo_device/kbd_pointer_device.rs +++ b/pci/src/demo_device/kbd_pointer_device.rs @@ -99,7 +99,7 @@ impl PointerOpts for TestPciPointer { fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { let msg = PointerMessage { event_type: InputEvent::PointerEvent, - button: button as u32, + button, x, y, ..Default::default() @@ -108,19 +108,14 @@ impl PointerOpts for TestPciPointer { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum InputEvent { KbdEvent = 0, PointerEvent = 1, + #[default] InvalidEvent = 2, } -impl Default for InputEvent { - fn default() -> Self { - InputEvent::InvalidEvent - } -} - impl From for InputEvent { fn from(v: u8) -> Self { match v { diff --git a/pci/src/host.rs b/pci/src/host.rs index 03650fa8c..d45d73c8d 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -376,7 +376,7 @@ fn build_prt_for_aml(pci_bus: &mut AmlDevice, irq: i32) { (0..PCI_PIN_NUM).for_each(|pin| { let gsi = (pin + slot) % PCI_PIN_NUM; let mut pkg = AmlPackage::new(4); - pkg.append_child(AmlDWord(((slot as u32) << 16) as u32 | 0xFFFF)); + pkg.append_child(AmlDWord((slot as u32) << 16 | 0xFFFF)); pkg.append_child(AmlDWord(pin as u32)); pkg.append_child(AmlName(format!("GSI{}", gsi))); pkg.append_child(AmlZero); @@ -492,10 +492,10 @@ impl AmlBuilder for PciHost { AmlCacheable::NonCacheable, AmlReadAndWrite::ReadWrite, 0, - high_pcie_mmio.0 as u64, - (high_pcie_mmio.0 + high_pcie_mmio.1) as u64 - 1, + high_pcie_mmio.0, + high_pcie_mmio.0 + high_pcie_mmio.1 - 1, 0, - high_pcie_mmio.1 as u64, + high_pcie_mmio.1, )); } crs.append_child(AmlDWordDesc::new_memory( diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index c7494e4d0..48a85444f 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -139,7 +139,7 @@ fn da_key_callback( let keysym2keycode = gs.borrow().keysym2keycode.clone(); let org_key_value = key_event.keyval().into_glib() as i32; let key_value: u16 = key_event.keyval().to_lower().into_glib() as u16; - let keycode: u16 = match keysym2keycode.borrow().get(&(key_value as u16)) { + let keycode: u16 = match keysym2keycode.borrow().get(&key_value) { Some(k) => *k, None => 0, }; @@ -242,12 +242,10 @@ fn da_scroll_callback( _ => 0x0, }; - let standard_x = - (((borrowed_gs.click_state.last_x as u64 * ABS_MAX) / width as u64) as u16) as u16; - let standard_y = - (((borrowed_gs.click_state.last_y as u64 * ABS_MAX) / height as u64) as u16) as u16; + let standard_x = ((borrowed_gs.click_state.last_x as u64 * ABS_MAX) / width as u64) as u16; + let standard_y = ((borrowed_gs.click_state.last_y as u64 * ABS_MAX) / height as u64) as u16; drop(borrowed_gs); - point_event(button_mask as u32, standard_x as u32, standard_y as u32)?; + point_event(button_mask, standard_x as u32, standard_y as u32)?; Ok(()) } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 3e593848b..41e2bbda0 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -69,7 +69,7 @@ const DOMAIN_NAME: &str = "desktop-app-engine"; const LOCALE_PATH: &str = "/usr/share/locale"; /// Gtk window display mode. -#[derive(Clone, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct ScaleMode { /// Display fill desktop. full_screen: bool, @@ -91,7 +91,7 @@ impl ScaleMode { /// Zoom in the display. /// Zoom out the display. /// Window adapt to display. -#[derive(PartialEq)] +#[derive(Eq, PartialEq)] pub enum ZoomOperate { ZoomIn, ZoomOut, @@ -802,10 +802,10 @@ fn do_update_event(gs: &Rc>, event: DisplayChangeEvent let mut mx: f64 = 0.0; let mut my: f64 = 0.0; if window_width > scale_width { - mx = ((window_width - scale_width) as f64) / (2.0); + mx = (window_width - scale_width) / (2.0); } if window_height > scale_height { - my = ((window_height - scale_height) as f64) / (2.0); + my = (window_height - scale_height) / (2.0); } borrowed_gs.draw_area.queue_draw_area( diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 2d596fb9a..86fe26e4c 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -1246,9 +1246,7 @@ pub fn desktop_resize( let locked_surface = server.vnc_surface.lock().unwrap(); let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); - if !(0..=MAX_IMAGE_SIZE as i32).contains(&width) - || !(0..=MAX_IMAGE_SIZE as i32).contains(&height) - { + if !(0..=MAX_IMAGE_SIZE).contains(&width) || !(0..=MAX_IMAGE_SIZE).contains(&height) { return Err(anyhow!(VncError::InvalidImageSize(width, height))); } drop(locked_surface); diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index fd6e3f916..7368ec19e 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -617,12 +617,12 @@ pub fn iov_to_buf_direct(iovec: &[Iovec], buf: &mut [u8]) -> Result { /// Discard "size" bytes of the front of iovec. pub fn iov_discard_front_direct(iovec: &mut [Iovec], mut size: u64) -> Option<&mut [Iovec]> { for (index, iov) in iovec.iter_mut().enumerate() { - if iov.iov_len as u64 > size { + if iov.iov_len > size { iov.iov_base += size; - iov.iov_len -= size as u64; + iov.iov_len -= size; return Some(&mut iovec[index..]); } - size -= iov.iov_len as u64; + size -= iov.iov_len; } None } diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index a280136e0..3895dcc43 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -285,8 +285,7 @@ impl FdtBuilder { fn align_structure_blk(&mut self, alignment: usize) { let remainder = self.structure_blk.len() % alignment; if remainder != 0 { - self.structure_blk - .extend(vec![0_u8; (alignment - remainder) as usize]); + self.structure_blk.extend(vec![0_u8; alignment - remainder]); } } } diff --git a/util/src/unix.rs b/util/src/unix.rs index 3ffc3d6ea..938bf8de8 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -244,13 +244,10 @@ impl UnixSock { .wrapping_add(unsafe { CMSG_LEN(cmsg.cmsg_len as u32) } as usize) as *mut cmsghdr; // Safe to get msg_control because the parameter is valid. - let nex_cmsg_pos = - (next_cmsg as *mut u8).wrapping_sub(msghdr.msg_control as usize) as usize; + let nex_cmsg_pos = (next_cmsg as *mut u8).wrapping_sub(msghdr.msg_control as usize) as u64; // Safe as parameter is zero. - if nex_cmsg_pos.wrapping_add(unsafe { CMSG_LEN(0) } as usize) - > msghdr.msg_controllen as usize - { + if nex_cmsg_pos.wrapping_add(unsafe { CMSG_LEN(0) } as u64) > msghdr.msg_controllen as u64 { null_mut() } else { next_cmsg @@ -268,24 +265,20 @@ impl UnixSock { /// /// The socket file descriptor is broken. pub fn send_msg(&self, iovecs: &mut [iovec], out_fds: &[RawFd]) -> std::io::Result { - // It is safe because we check the iovecs lens before. - #[cfg(not(target_env = "musl"))] + // SAFETY: we checked the iovecs lens before. let iovecs_len = iovecs.len(); + // SATETY: we checked the out_fds lens before. + let cmsg_len = unsafe { CMSG_LEN((size_of::() * out_fds.len()) as u32) }; + // SAFETY: we checked the out_fds lens before. + let cmsg_capacity = unsafe { CMSG_SPACE((size_of::() * out_fds.len()) as u32) }; + let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; + #[cfg(target_env = "musl")] - let iovecs_len = iovecs.len() as i32; - // It is safe because we check the out_fds lens before. + let iovecs_len = iovecs_len as i32; #[cfg(not(target_env = "musl"))] - let cmsg_len = unsafe { CMSG_LEN((size_of::() * out_fds.len()) as u32) } as usize; - #[cfg(target_env = "musl")] - let cmsg_len = unsafe { CMSG_LEN((size_of::() * out_fds.len()) as u32) } as u32; - // It is safe because we check the out_fds lens before. + let cmsg_len = cmsg_len as usize; #[cfg(not(target_env = "musl"))] - let cmsg_capacity = - unsafe { CMSG_SPACE((size_of::() * out_fds.len()) as u32) } as usize; - #[cfg(target_env = "musl")] - let cmsg_capacity = - unsafe { CMSG_SPACE((size_of::() * out_fds.len()) as u32) } as u32; - let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; + let cmsg_capacity = cmsg_capacity as usize; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. @@ -352,19 +345,16 @@ impl UnixSock { iovecs: &mut [iovec], in_fds: &mut [RawFd], ) -> std::io::Result<(usize, usize)> { - // It is safe because we check the iovecs lens before. - #[cfg(not(target_env = "musl"))] + // SAFETY: we check the iovecs lens before. let iovecs_len = iovecs.len(); + // SAFETY: we check the in_fds lens before. + let cmsg_capacity = unsafe { CMSG_SPACE((size_of::() * in_fds.len()) as u32) }; + let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; + #[cfg(target_env = "musl")] - let iovecs_len = iovecs.len() as i32; - // It is safe because we check the in_fds lens before. + let iovecs_len = iovecs_len as i32; #[cfg(not(target_env = "musl"))] - let cmsg_capacity = - unsafe { CMSG_SPACE((size_of::() * in_fds.len()) as u32) } as usize; - #[cfg(target_env = "musl")] - let cmsg_capacity = - unsafe { CMSG_SPACE((size_of::() * in_fds.len()) as u32) } as u32; - let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; + let cmsg_capacity = cmsg_capacity as usize; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. @@ -400,7 +390,7 @@ impl UnixSock { ), )); } - if total_read == 0 && (msg.msg_controllen as usize) < size_of::() { + if total_read == 0 && (msg.msg_controllen as u64) < size_of::() as u64 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!( @@ -417,6 +407,10 @@ impl UnixSock { let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() }; if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS { + #[cfg(not(target_env = "musl"))] + let fd_count = + (cmsg.cmsg_len - unsafe { CMSG_LEN(0) } as usize) / size_of::(); + #[cfg(target_env = "musl")] let fd_count = (cmsg.cmsg_len as usize - unsafe { CMSG_LEN(0) } as usize) / size_of::(); unsafe { @@ -431,7 +425,7 @@ impl UnixSock { cmsg_ptr = self.get_next_cmsg(&msg, &cmsg, cmsg_ptr); } - Ok((total_read as usize, in_fds_count as usize)) + Ok((total_read as usize, in_fds_count)) } } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b43aee969..57a41e48d 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -527,7 +527,7 @@ fn gpa_hva_iovec_map( ) })?; hva_iovec.push(Iovec { - iov_base: hva as u64, + iov_base: hva, iov_len: u64::from(elem.len), }); iov_size += elem.len as u64; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 470fa0ca9..050f1cb0d 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -453,7 +453,7 @@ impl VhostUserClient { )?; let inflight = VhostInflight { file, - addr: hva as u64, + addr: hva, inner: vhost_user_inflight, }; self.inflight = Some(inflight); -- Gitee From a590d630395b2c31d67d3504b0a445ef92e6e9ca Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 12 Jun 2023 05:27:13 +0800 Subject: [PATCH 1140/2187] virtio-gpu: Adjust error handling and mst testcase 1. Some error needs response. 2. Asjuest expected behavior of some testcases. Signed-off-by: Keqian Zhu --- tests/mod_test/src/libdriver/virtio_gpu.rs | 50 ++++++++++------ tests/mod_test/tests/virtio_gpu_test.rs | 66 ++++++++++++++++++---- virtio/src/device/gpu.rs | 38 +++++++------ 3 files changed, 110 insertions(+), 44 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index 6e96f12f9..3bf004514 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -436,13 +436,14 @@ impl TestVirtioGpu { self.device.borrow_mut().set_driver_ok(); } - pub fn request_complete( + pub fn submit_request( &mut self, ctrl_q: bool, hdr: &[u8], hdr_ctx: Option<&[u8]>, ctx: Option<&[u8]>, resp: Option<&mut T>, + wait_resp: bool, ) { let mut offset = 0; let mut vec = Vec::new(); @@ -519,14 +520,16 @@ impl TestVirtioGpu { .borrow_mut() .kick_virtqueue(self.state.clone(), self.ctrl_q.clone()); - self.device.borrow_mut().poll_used_elem( - self.state.clone(), - self.ctrl_q.clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + if wait_resp { + self.device.borrow_mut().poll_used_elem( + self.state.clone(), + self.ctrl_q.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } } else { let free_head = self .cursor_q @@ -537,14 +540,16 @@ impl TestVirtioGpu { .borrow_mut() .kick_virtqueue(self.state.clone(), self.cursor_q.clone()); - self.device.borrow_mut().poll_used_elem( - self.state.clone(), - self.cursor_q.clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + if wait_resp { + self.device.borrow_mut().poll_used_elem( + self.state.clone(), + self.cursor_q.clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); + } } if resp.is_some() { @@ -559,6 +564,17 @@ impl TestVirtioGpu { *resp.unwrap() = slice[0].clone(); } } + + pub fn request_complete( + &mut self, + ctrl_q: bool, + hdr: &[u8], + hdr_ctx: Option<&[u8]>, + ctx: Option<&[u8]>, + resp: Option<&mut T>, + ) { + self.submit_request(ctrl_q, hdr, hdr_ctx, ctx, resp, true); + } } #[derive(Clone, Debug)] diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index e48df4281..c2ae38911 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use mod_test::libdriver::virtio::VirtioDeviceOps; use mod_test::libdriver::virtio_gpu::{ current_curosr_check, current_surface_check, get_display_info, get_edid, invalid_cmd_test, resource_attach_backing, resource_attach_backing_with_invalid_ctx_len, resource_create, @@ -20,6 +21,7 @@ use mod_test::libdriver::virtio_gpu::{ VirtioGpuSetScanout, VirtioGpuTransferToHost2d, }; use mod_test::libdriver::virtio_gpu::{set_up, tear_down}; +use std::time::{Duration, Instant}; use std::vec; use util::byte_code::ByteCode; use virtio::{ @@ -492,7 +494,7 @@ fn resource_transfer_dfx() { // have not attach any data source assert_eq!( - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_UNSPEC, transfer_to_host( &gpu, VirtioGpuTransferToHost2d::new( @@ -541,7 +543,7 @@ fn scanout_set_dfx() { gpu_cfg.max_hostmem = image_size; let (dpy, gpu) = set_up(&gpu_cfg); - gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); + let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); // invalid scanout id assert_eq!( @@ -581,6 +583,17 @@ fn scanout_set_dfx() { .hdr_type ); + // attach backing + assert_eq!( + VIRTIO_GPU_RESP_OK_NODATA, + resource_attach_backing( + &gpu, + VirtioGpuResourceAttachBacking::new(D_RES_ID, 1), + vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] + ) + .hdr_type + ); + // invalid rect region assert_eq!( VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, @@ -718,30 +731,61 @@ fn crash_dfx() { // invalid request header length let mut hdr = VirtioGpuCtrlHdr::default(); hdr.hdr_type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; - let mut resp = VirtioGpuDisplayInfo::default(); resp.header.hdr_type = 0x1234; // will not change because req has been ignored let temp = hdr.as_bytes(); let slice = &temp[4..]; gpu.borrow_mut() - .request_complete(true, slice, None, None, Some(&mut resp)); - assert_eq!(0x1234, resp.header.hdr_type); + .submit_request(true, slice, None, None, Some(&mut resp), false); + + // expect has no resp from backend. + let time_out = Instant::now() + Duration::from_secs(2); + loop { + gpu.borrow_mut().state.borrow().clock_step(); + assert!(!gpu + .borrow() + .device + .borrow() + .queue_was_notified(gpu.borrow().ctrl_q.clone())); + if Instant::now() > time_out { + assert_eq!(0x1234, resp.header.hdr_type); + break; + } + } // invalid hdr_ctx let mut hdr = VirtioGpuCtrlHdr::default(); hdr.hdr_type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; - let hdr_ctx = VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT); - let mut resp = VirtioGpuCtrlHdr::default(); + resp.hdr_type = 0x1234; // will not change because req has been ignored let temp = hdr_ctx.as_bytes(); let slice = &temp[4..]; - - gpu.borrow_mut() - .request_complete(true, hdr.as_bytes(), Some(slice), None, Some(&mut resp)); - assert_eq!(VIRTIO_GPU_RESP_ERR_UNSPEC, resp.hdr_type); + gpu.borrow_mut().submit_request( + true, + hdr.as_bytes(), + Some(slice), + None, + Some(&mut resp), + false, + ); + + // expect has no resp from backend. + let time_out = Instant::now() + Duration::from_secs(2); + loop { + gpu.borrow_mut().state.borrow().clock_step(); + assert!(!gpu + .borrow() + .device + .borrow() + .queue_was_notified(gpu.borrow().ctrl_q.clone())); + if Instant::now() > time_out { + assert_eq!(0x1234, resp.hdr_type); + break; + } + } tear_down(dpy, gpu); } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 6ea8448c8..76b57f495 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1222,24 +1222,30 @@ impl GpuIoHandler { ents.resize(entries as usize, VirtioGpuMemEntry::default()); let ents_buf = unsafe { from_raw_parts_mut(ents.as_mut_ptr() as *mut u8, ents_size as usize) }; - iov_to_buf_direct(&req.out_iovec, head_size, ents_buf).and_then(|v| { - if v as u64 == ents_size { - Ok(()) - } else { - Err(anyhow!("Load no enough ents when attach backing")) - } - })?; + let v = iov_to_buf_direct(&req.out_iovec, head_size, ents_buf)?; + if v as u64 != ents_size { + error!( + "Virtio-GPU: Load no enough ents buf when attach backing, {} vs {}", + v, ents_size + ); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } for entry in ents.iter() { - let iov_base = self - .mem_space - .get_host_address(GuestAddress(entry.addr)) - .with_context(|| format!("Map entry base {:?} failed", entry.addr))?; - let iov_item = Iovec { - iov_base, - iov_len: entry.length as u64, - }; - res.iov.push(iov_item); + match self.mem_space.get_host_address(GuestAddress(entry.addr)) { + Some(iov_base) => { + let iov_item = Iovec { + iov_base, + iov_len: entry.length as u64, + }; + res.iov.push(iov_item); + } + None => { + error!("Virtio-GPU: Map entry base {:?} failed", entry.addr); + res.iov.clear(); + return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); + } + } } self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } -- Gitee From deca4ff7518fd686bd441144f71f465955e10ff9 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Tue, 13 Jun 2023 23:06:48 +0800 Subject: [PATCH 1141/2187] Upgrade crate to the latest version Signed-off-by: Yan Wen --- Cargo.lock | 745 +++++++++++------- address_space/Cargo.toml | 8 +- boot_loader/Cargo.toml | 4 +- cpu/Cargo.toml | 8 +- devices/Cargo.toml | 16 +- hypervisor/Cargo.toml | 8 +- ...Third_Party_Open_Source_Software_Notice.md | 28 +- machine/Cargo.toml | 2 +- machine_manager/Cargo.toml | 4 +- migration/Cargo.toml | 4 +- migration_derive/Cargo.toml | 2 +- migration_derive/src/attr_parser.rs | 96 ++- pci/Cargo.toml | 6 +- sysbus/Cargo.toml | 6 +- tests/mod_test/Cargo.toml | 2 +- ui/Cargo.toml | 18 +- ui/src/gtk/menu.rs | 2 +- ui/src/gtk/mod.rs | 29 +- ui/src/vnc/auth_vencrypt.rs | 6 +- util/Cargo.toml | 14 +- util/src/aio/uring.rs | 2 +- vfio/Cargo.toml | 6 +- vhost_user_fs/Cargo.toml | 8 +- virtio/Cargo.toml | 6 +- 24 files changed, 600 insertions(+), 430 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c29151ac5..891e6855f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,32 +81,23 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "arc-swap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "atk" -version = "0.14.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a83b21d2aa75e464db56225e1bda2dd5993311ba1095acaa8fa03d1ae67026ba" +checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" dependencies = [ "atk-sys", "bitflags", @@ -116,9 +107,9 @@ dependencies = [ [[package]] name = "atk-sys" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "badcf670157c84bb8b1cf6b5f70b650fed78da2033c9eed84c4e49b11cbe83ea" +checksum = "e3bf0a7ca572fbd5762fd8f8cd65a581e06767bc1234913fe1f43e370cff6e90" dependencies = [ "glib-sys", "gobject-sys", @@ -126,17 +117,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -160,30 +140,30 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bindgen" -version = "0.56.0" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ "bitflags", "cexpr", "clang-sys", - "clap", - "env_logger", "lazy_static", "lazycell", "log", "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", + "syn 2.0.18", "which", ] @@ -195,9 +175,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitintr" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80c754495ef07b4554134bbfb23fc1e31a775113b49acccbe35076d57cba145" +checksum = "7ba5a5c4df8ac8673f22698f443ef1ce3853d7f22d5a15ebf66b9a7553b173dd" [[package]] name = "block" @@ -235,22 +215,23 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cairo-rs" -version = "0.14.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b5725979db0c586d98abad2193cdb612dd40ef95cd26bd99851bf93b3cb482" +checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" dependencies = [ "bitflags", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.14.9" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b448b876970834fda82ba3aeaccadbd760206b75388fc5c1b02f1e343b697570" +checksum = "f55382a01d30e5e53f185eee269124f5e21ab526595b872751278dfbb463594e" dependencies = [ "glib-sys", "libc", @@ -259,9 +240,9 @@ dependencies = [ [[package]] name = "capng" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f8e9448233603643e42606121d95f5f8d4e015b3e7619a51593864dd902575" +checksum = "7a26766f93f07f7e8b8309ed2824fa2a68f5d12d219de855e24688e9fbe89e85" dependencies = [ "bitflags", "libc", @@ -275,20 +256,21 @@ checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cexpr" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-expr" -version = "0.8.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b412e83326147c2bb881f8b40edfbf9905b9b8abaebd0e47ca190ba62fda8f0e" +checksum = "e70d3ad08698a0568b0562f22710fe6bfc1f4a61a367c77d0398c562eadd453a" dependencies = [ "smallvec", + "target-lexicon", ] [[package]] @@ -308,21 +290,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "const_format" version = "0.2.26" @@ -343,6 +310,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cpu" version = "2.2.0" @@ -362,6 +338,19 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "devices" version = "2.2.0" @@ -391,8 +380,8 @@ dependencies = [ "serde", "serde_json", "serial_test", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum", + "strum_macros", "sysbus", "thiserror", "ui", @@ -425,28 +414,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -479,6 +455,21 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -486,6 +477,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -511,6 +503,23 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "futures-task" version = "0.3.28" @@ -523,8 +532,13 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -532,9 +546,9 @@ dependencies = [ [[package]] name = "gdk" -version = "0.14.3" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d749dcfc00d8de0d7c3a289e04a04293eb5ba3d8a4e64d64911d481fa9933b" +checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" dependencies = [ "bitflags", "cairo-rs", @@ -548,21 +562,23 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534192cb8f01daeb8fab2c8d4baa8f9aae5b7a39130525779f5c2608e235b10f" +checksum = "b023fbe0c6b407bd3d9805d107d9800da3829dc5a676653210f1d5f16d7f59bf" dependencies = [ + "bitflags", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f097c0704201fbc8f69c1762dc58c6947c8bb188b8ed0bc7e65259f1894fe590" +checksum = "7b41bd2b44ed49d99277d3925652a163038bd5ed943ec9809338ffb2f4391e3b" dependencies = [ "gio-sys", "glib-sys", @@ -573,9 +589,9 @@ dependencies = [ [[package]] name = "gdk-sys" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e091b3d3d6696949ac3b3fb3c62090e5bfd7bd6850bef5c3c5ea701de1b1f1e" +checksum = "2152de9d38bc67a17b3fe49dc0823af5bf874df59ea088c5f28f31cf103de703" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -627,26 +643,29 @@ checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "gio" -version = "0.14.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0" +checksum = "d14522e56c6bcb6f7a3aebc25cbcfb06776af4c0c25232b601b4383252d7cb92" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror", ] [[package]] name = "gio-sys" -version = "0.14.0" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa" +checksum = "6b1d43b0d7968b48455244ecafe41192871257f5740aa6b095eb19db78e362a5" dependencies = [ "glib-sys", "gobject-sys", @@ -657,43 +676,47 @@ dependencies = [ [[package]] name = "glib" -version = "0.14.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4" +checksum = "a7f1de7cbde31ea4f0a919453a2dcece5d54d5b70e08f8ad254dc4840f5f09b6" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", + "memchr", "once_cell", "smallvec", + "thiserror", ] [[package]] name = "glib-macros" -version = "0.14.1" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" +checksum = "0a7206c5c03851ef126ea1444990e81fdd6765fb799d5bc694e4897ca01bb97f" dependencies = [ "anyhow", - "heck 0.3.3", + "heck", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "glib-sys" -version = "0.14.0" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1d60554a212445e2a858e42a0e48cece1bd57b311a19a9468f70376cf554ae" +checksum = "49f00ad0a1bf548e61adfff15d83430941d9e1bb620e334f779edd1c745680a5" dependencies = [ "libc", "system-deps", @@ -707,9 +730,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gobject-sys" -version = "0.14.0" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5" +checksum = "15e75b0000a64632b2d8ca3cf856af9308e3a970844f6e9659bd197f026793d0" dependencies = [ "glib-sys", "libc", @@ -718,9 +741,9 @@ dependencies = [ [[package]] name = "gtk" -version = "0.14.3" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb51122dd3317e9327ec1e4faa151d1fa0d95664cd8fb8dcfacf4d4d29ac70c" +checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" dependencies = [ "atk", "bitflags", @@ -741,9 +764,9 @@ dependencies = [ [[package]] name = "gtk-sys" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c14c8d3da0545785a7c5a120345b3abb534010fb8ae0f2ef3f47c027fba303e" +checksum = "4d8eb6a4b93e5a7e6980f7348d08c1cd93d31fae07cf97f20678c5ec41de3d7e" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -759,27 +782,23 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.14.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" +checksum = "3efb84d682c9a39c10bd9f24f5a4b9c15cc8c7edc45c19cb2ca2c4fc38b2d95e" dependencies = [ "anyhow", - "heck 0.3.3", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] -name = "heck" -version = "0.3.3" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -787,27 +806,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hypervisor" version = "2.2.0" @@ -824,33 +828,25 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "cfg-if", + "autocfg", + "hashbrown", ] [[package]] name = "io-uring" -version = "0.5.7" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d32c9c053ad47572e11da8bce622ed4c9ae9dedac5b7f678a2e876d1494d4c4" +checksum = "8b7b36074613a723279637061b40db993208908a94f10ccb14436ce735bc0f57" dependencies = [ "bitflags", "libc", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.3" @@ -877,9 +873,9 @@ dependencies = [ [[package]] name = "kvm-ioctls" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4" +checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b" dependencies = [ "kvm-bindings", "libc", @@ -900,9 +896,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.133" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libloading" @@ -999,12 +995,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "machine" @@ -1027,8 +1020,8 @@ dependencies = [ "serde", "serde_json", "smbios", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum", + "strum_macros", "sysbus", "thiserror", "ui", @@ -1051,8 +1044,8 @@ dependencies = [ "regex", "serde", "serde_json", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum", + "strum_macros", "thiserror", "util", "vmm-sys-util", @@ -1107,10 +1100,16 @@ dependencies = [ "migration", "proc-macro2", "quote", - "syn", + "syn 2.0.18", "util", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.5.4" @@ -1152,12 +1151,12 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", - "version_check", + "minimal-lexical", ] [[package]] @@ -1168,7 +1167,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1220,9 +1219,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "os_pipe" @@ -1246,11 +1245,12 @@ dependencies = [ [[package]] name = "pango" -version = "0.14.8" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581" +checksum = "52c280b82a881e4208afb3359a8e7fde27a1b272280981f1f34610bed5770d37" dependencies = [ "bitflags", + "gio", "glib", "libc", "once_cell", @@ -1259,9 +1259,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2367099ca5e761546ba1d501955079f097caa186bb53ce0f718dca99ac1942fe" +checksum = "4293d0f0b5525eb5c24734d30b0ed02cd02aa734f216883f376b54de49625de8" dependencies = [ "glib-sys", "gobject-sys", @@ -1271,27 +1271,25 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", - "instant", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -1338,9 +1336,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" @@ -1348,15 +1346,24 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" +dependencies = [ + "proc-macro2", + "syn 2.0.18", +] + [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -1368,7 +1375,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1385,18 +1392,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.44" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -1505,25 +1512,35 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ "base64", ] +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -1572,55 +1589,67 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + [[package]] name = "serial_test" -version = "0.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ + "dashmap", + "futures", "lazy_static", + "log", "parking_lot", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "0.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -1635,9 +1664,9 @@ dependencies = [ [[package]] name = "shlex" -version = "0.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "slab" @@ -1650,9 +1679,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smbios" @@ -1672,9 +1701,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "sscanf" -version = "0.2.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40f3891ecd1b4d716fd674bf238ee743d2cd948c9aeb3b42b1dd9d8f56d0154b" +checksum = "c713ebd15ce561dd4a13ed62bc2a0368e16806fc30dcaf66ecf1256b2a3fdde6" dependencies = [ "const_format", "lazy_static", @@ -1684,14 +1713,17 @@ dependencies = [ [[package]] name = "sscanf_macro" -version = "0.2.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d18335301db6e21d35b955cfb58327f3ede99f831a84cad5a07a59c6b1ec5d" +checksum = "84955aa74a157e5834d58a07be11af7f0ab923f0194a0bb2ea6b3db8b5d1611d" dependencies = [ + "convert_case", "proc-macro2", "quote", "regex-syntax", - "syn", + "strsim", + "syn 2.0.18", + "unicode-width", ] [[package]] @@ -1708,15 +1740,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strum" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" @@ -1726,34 +1752,33 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" [[package]] name = "strum_macros" -version = "0.21.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck 0.3.3", + "heck", "proc-macro2", "quote", - "syn", + "rustversion", + "syn 1.0.109", ] [[package]] -name = "strum_macros" -version = "0.24.3" +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "heck 0.4.1", "proc-macro2", "quote", - "rustversion", - "syn", + "unicode-ident", ] [[package]] name = "syn" -version = "1.0.100" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -1776,22 +1801,23 @@ dependencies = [ [[package]] name = "system-deps" -version = "3.2.0" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" +checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2" dependencies = [ - "anyhow", "cfg-expr", - "heck 0.3.3", - "itertools", + "heck", "pkg-config", - "strum 0.21.0", - "strum_macros 0.21.1", - "thiserror", "toml", "version-compare", ] +[[package]] +name = "target-lexicon" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" + [[package]] name = "temp-dir" version = "0.1.11" @@ -1799,50 +1825,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab" [[package]] -name = "termcolor" -version = "1.2.0" +name = "thiserror" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ - "winapi-util", + "thiserror-impl", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "thiserror-impl" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "unicode-width", + "proc-macro2", + "quote", + "syn 2.0.18", ] [[package]] -name = "thiserror" -version = "1.0.36" +name = "toml" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" dependencies = [ - "thiserror-impl", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "thiserror-impl" -version = "1.0.36" +name = "toml_datetime" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "serde", ] [[package]] -name = "toml" -version = "0.5.11" +name = "toml_edit" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ + "indexmap", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -1869,9 +1902,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-segmentation" @@ -1919,9 +1952,9 @@ dependencies = [ [[package]] name = "v4l2-sys-mit" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c932c06df4af1dfb229f604214f2a87993784596ff33ffdadcba1b5519254e" +checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b" dependencies = [ "bindgen", ] @@ -1932,17 +1965,11 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version-compare" -version = "0.0.11" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "version_check" @@ -2029,9 +2056,9 @@ dependencies = [ [[package]] name = "vmm-sys-util" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc06a16ee8ebf0d9269aed304030b0d20a866b8b3dd3d4ce532596ac567a0d24" +checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" dependencies = [ "bitflags", "libc", @@ -2064,7 +2091,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -2086,7 +2113,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2107,23 +2134,15 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "which" -version = "3.1.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ + "either", "libc", + "once_cell", ] [[package]] @@ -2143,16 +2162,148 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "winapi", + "windows-targets 0.42.2", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 465d546f4..a3974f31a 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -10,9 +10,9 @@ description = "provide memory management for VM" libc = "0.2" log = "0.4" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" -vmm-sys-util = "0.11.0" -arc-swap = "1.5.0" +kvm-ioctls = "0.13.0" +vmm-sys-util = "0.11.1" +arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" hypervisor = { path = "../hypervisor" } @@ -22,4 +22,4 @@ migration_derive = { path = "../migration_derive" } util = { path = "../util" } [dev-dependencies] -serial_test = "0.5.1" +serial_test = "2.0.0" diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index 0665e51c0..8dec57f78 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -9,10 +9,10 @@ license = "Mulan PSL v2" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } devices = { path = "../devices" } util = { path = "../util" } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 7aa86f7c3..418410138 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -10,10 +10,10 @@ description = "CPU emulation" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" log = "0.4" libc = "0.2" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } @@ -21,8 +21,8 @@ migration_derive = { path = "../migration_derive" } util = { path = "../util" } [dev-dependencies] -serial_test = "0.5.1" +serial_test = "2.0.0" [features] default = [] -boot_time = [] \ No newline at end of file +boot_time = [] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index eb022f2f5..3502da1d1 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -11,15 +11,15 @@ thiserror = "1.0" anyhow = "1.0" libc = "0.2" log = "0.4" -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" serde = { version = "1.0", features = ["derive"] } -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" -once_cell = "1.9.0" +once_cell = "1.18.0" strum = "0.24.1" strum_macros = "0.24.3" -v4l2-sys-mit = "0.2.0" +v4l2-sys-mit = "0.3.0" serde_json = "1.0" rand = "0.8.5" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } @@ -36,15 +36,15 @@ acpi = { path = "../acpi" } [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } pci = { path = "../pci" } -pulse = { version = "2.0", package = "libpulse-binding" } -psimple = { version = "2.0", package = "libpulse-simple-binding" } +pulse = { version = "2.27", package = "libpulse-binding" } +psimple = { version = "2.27", package = "libpulse-simple-binding" } rusb = "0.9" libusb1-sys = "0.6.4" -cairo-rs = "0.14.9" +cairo-rs = "0.17.10" alsa = "0.7.0" [dev-dependencies] -serial_test = "0.5.1" +serial_test = "2.0.0" [features] default = [] diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 89c79d1c7..587d36945 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -6,12 +6,12 @@ edition = "2021" license = "Mulan PSL v2" [dependencies] -arc-swap = "1.5.0" +arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" log = "0.4" -vmm-sys-util = "0.11.0" -once_cell = "1.13.0" +vmm-sys-util = "0.11.1" +once_cell = "1.18.0" util = { path = "../util" } diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index b676011d1..e0fae0721 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -241,7 +241,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -Software: log 0.4.17 +Software: log 0.4.18 Copyright notice: Copyright (c) 2014 The Rust Project Developers Copyright 2014-2015 The Rust Project Developers @@ -289,7 +289,7 @@ Copyright (c) Erick Tryzelaar License: MIT or Apache License Version 2.0 Please see above. -Software: serde_json 1.0.145 +Software: serde_json 1.0.96 Copyright notice: Copyright (c) David Tolnay Copyright (c) Erick Tryzelaar @@ -308,7 +308,7 @@ Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: vmm-sys-util 0.11.0 +Software: vmm-sys-util 0.11.1 Copyright notice: Copyright 2019 Intel Corporation. All Rights Reserved. Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -356,7 +356,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Software: kvm-ioctls 0.12.0 +Software: kvm-ioctls kvm-ioctls Copyright notice: Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. Portions Copyright 2017 The Chromium OS Authors. All rights reserved. @@ -371,44 +371,44 @@ Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: The APACHE 2.0 License Please see above. -Software: arc-swap 1.5.1 +Software: arc-swap 1.6.0 Copyright notice: Copyright (c) 2017 arc-swap developers License: MIT or Apache License Version 2.0 Please see above. -Software: serial_test 0.5.1 +Software: serial_test 2.0.0 Copyright notice: Copyright (c) 2018 Tom Parker-Shemilt License: MIT Please see above. -Software: syn 1.0.100 +Software: syn 2.0.18 Copyright notice: Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: quote 1.0.21 +Software: quote 1.0.28 Copyright notice: Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: proc-macro2 1.0.44 +Software: proc-macro2 1.0.59 Copyright notice: Copyright (c) David Tolnay Copyright (c) Alex Crichton License: MIT or Apache License Version 2.0 Please see above. -Software: strum 0.20.0 +Software: strum 0.24.1 Copyright notice: Copyright (c) 2019 Peter Glotfelty License: MIT Please see above. -Software: strum_macros 0.20.1 +Software: strum_macros 0.24.3 Copyright notice: Copyright (c) 2019 Peter Glotfelty License: MIT @@ -420,19 +420,19 @@ Copyright (c) 2019 Intel Corporation. All Rights Reserved. License: Apache License Version 2.0 or BSD 3-Clause License Please see above. -Software: once_cell 1.15.0 +Software: once_cell 1.17.2 Copyright notice: Copyright (c) Aleksey Kladov License: MIT OR Apache-2.0 Please see above. -Software: io-uring 0.5.7 +Software: io-uring 0.6.0 Copyright notice: Copyright (c) tokio-rs License: MIT OR Apache-2.0 Please see above. -Software: capng 0.2.2 +Software: capng 0.2.3 Copyright notice: Copyright (C) 2020 Red Hat, Inc. All rights reserved. License: Apache License Version 2.0 or BSD 3-Clause License diff --git a/machine/Cargo.toml b/machine/Cargo.toml index c745aac51..fa413a0ec 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -12,7 +12,7 @@ log = "0.4" libc = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" vfio-bindings = "0.3" thiserror = "1.0" anyhow = "1.0" diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 4548df300..de62997d1 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -12,12 +12,12 @@ regex = "1" log = "0.4" libc = "0.2" serde_json = "1.0" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" hex = "0.4.3" serde = { version = "1.0", features = ["derive"] } strum = "0.24.1" strum_macros = "0.24.3" -once_cell = "1.13.0" +once_cell = "1.18.0" thiserror = "1.0" anyhow = "1.0" util = { path = "../util" } diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 103994a64..6af23c651 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -5,10 +5,10 @@ authors = ["Huawei StratoVirt Team"] edition = "2021" [dependencies] -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -once_cell = "1.13.0" +once_cell = "1.18.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } log = "0.4" thiserror = "1.0" diff --git a/migration_derive/Cargo.toml b/migration_derive/Cargo.toml index 5f889d136..6fdd65b9b 100644 --- a/migration_derive/Cargo.toml +++ b/migration_derive/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Mulan PSL v2" [dependencies] -syn = { version = "1.0.72", features = ["full", "extra-traits"] } +syn = { version = "2.0.18", features = ["full", "extra-traits"] } quote = "1.0" proc-macro2 = "1.0" diff --git a/migration_derive/src/attr_parser.rs b/migration_derive/src/attr_parser.rs index 96b26af9d..d86e4271c 100644 --- a/migration_derive/src/attr_parser.rs +++ b/migration_derive/src/attr_parser.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use syn::{MetaList, MetaNameValue}; +use syn::Lit; // Read the program version in `Cargo.toml`. const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); @@ -39,10 +39,34 @@ const FIELD_ATTRIBUTE_NAME: &str = "alias"; pub fn parse_struct_attributes(attributes: &[syn::Attribute]) -> (u32, u32) { let (mut current_version, mut compat_version) = (0, 0); for attribute in attributes { - if attribute.path.is_ident(ATTRIBUTE_NAME) { - if let Ok(syn::Meta::List(meta)) = attribute.parse_meta() { - get_attr_version(meta, &mut current_version, &mut compat_version); - } + if attribute.path().is_ident(ATTRIBUTE_NAME) { + let _ = attribute.parse_nested_meta(|meta| { + if meta.path.is_ident(CURRENT_VERSION) { + let value = meta.value()?; + let lit = value.parse::()?; + current_version = match lit { + syn::Lit::Int(lit_int) => lit_int.base10_parse().unwrap(), + syn::Lit::Str(lit_str) => version_to_u32(&lit_str.value()), + _ => panic!("Unsupported version number."), + }; + + return Ok(()); + } + + if meta.path.is_ident(COMPAT_VERSION) { + let value = meta.value()?; + let lit = value.parse::()?; + compat_version = match lit { + syn::Lit::Int(lit_int) => lit_int.base10_parse().unwrap(), + syn::Lit::Str(lit_str) => version_to_u32(&lit_str.value()), + _ => panic!("Unsupported version number."), + }; + + return Ok(()); + } + + Err(meta.error("unrecognized repr")) + }); } } @@ -55,7 +79,7 @@ pub fn parse_field_attributes(attributes: &[syn::Attribute]) -> Option { let mut field_alias = None; for attribute in attributes { - if attribute.path.is_ident(FIELD_ATTRIBUTE_NAME) { + if attribute.path().is_ident(FIELD_ATTRIBUTE_NAME) { let content: proc_macro2::TokenStream = attribute.parse_args().unwrap(); field_alias = Some(content.to_string()); } @@ -64,31 +88,6 @@ pub fn parse_field_attributes(attributes: &[syn::Attribute]) -> Option { field_alias } -fn get_attr_version(meta_list: MetaList, current_version: &mut u32, compat_version: &mut u32) { - for meta in meta_list.nested.iter() { - if let syn::NestedMeta::Meta(syn::Meta::NameValue(attr_name_value)) = meta { - if let Some(version) = meta_name_parse(attr_name_value, CURRENT_VERSION) { - *current_version = version; - } - if let Some(version) = meta_name_parse(attr_name_value, COMPAT_VERSION) { - *compat_version = version; - } - } - } -} - -fn meta_name_parse(meta_name: &MetaNameValue, name_str: &str) -> Option { - if *meta_name.path.get_ident().unwrap() == name_str { - Some(match &meta_name.lit { - syn::Lit::Int(lit_int) => lit_int.base10_parse().unwrap(), - syn::Lit::Str(lit_str) => version_to_u32(&lit_str.value()), - _ => panic!("Unsupported version number."), - }) - } else { - None - } -} - /// Check current version and compat version. /// /// # Check rules @@ -133,14 +132,33 @@ fn version_to_u32(version_str: &str) -> u32 { (version_vec[2] as u32) + ((version_vec[1] as u32) << 8) + ((version_vec[0] as u32) << 16) } -#[test] -fn test_version_to_u32() { - let version_str_01 = "0.1.0"; - assert_eq!(version_to_u32(version_str_01), 256); +#[cfg(test)] +mod test { + use super::*; + use syn::{parse_quote, ItemStruct}; - let version_str_02 = "1.18.0"; - assert_eq!(version_to_u32(version_str_02), 70_144); + #[test] + fn test_version_to_u32() { + let version_str_01 = "0.1.0"; + assert_eq!(version_to_u32(version_str_01), 256); - let version_str_03 = "255.255.255"; - assert_eq!(version_to_u32(version_str_03), 16_777_215); + let version_str_02 = "1.18.0"; + assert_eq!(version_to_u32(version_str_02), 70_144); + + let version_str_03 = "255.255.255"; + assert_eq!(version_to_u32(version_str_03), 16_777_215); + } + + #[test] + fn test_parse_attribute() { + let input: ItemStruct = parse_quote! { + #[desc_version(current_version = 1, compat_version = "0.1.0")] + pub struct MyStruct(u16, u32); + }; + + let (current_version, compat_version) = parse_struct_attributes(input.attrs.as_slice()); + + assert_eq!(current_version, 1); + assert_eq!(compat_version, 256); + } } diff --git a/pci/Cargo.toml b/pci/Cargo.toml index 3834ce9e5..f2612bd25 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -11,11 +11,11 @@ byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.0" -once_cell = "1.13.0" +vmm-sys-util = "0.11.1" +once_cell = "1.18.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml index 2280f9fb5..30ec46911 100644 --- a/sysbus/Cargo.toml +++ b/sysbus/Cargo.toml @@ -9,9 +9,9 @@ description = "Emulate system bus" [dependencies] thiserror = "1.0" anyhow = "1.0" -kvm-ioctls = "0.12.0" -vmm-sys-util = "0.11.0" +kvm-ioctls = "0.13.0" +vmm-sys-util = "0.11.1" acpi = { path = "../acpi" } address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } -util = {path = "../util"} \ No newline at end of file +util = {path = "../util"} diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 560176e78..3dcb2b6a8 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -8,7 +8,7 @@ license = "Mulan PSL v2" [dependencies] rand = "0.8.5" hex = "0.4.3" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" anyhow = "1.0" serde_json = "1.0" byteorder = "1.4.3" diff --git a/ui/Cargo.toml b/ui/Cargo.toml index e1a731a66..730d57657 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -10,16 +10,16 @@ description = "User Interface" thiserror = "1.0" anyhow = "1.0" libc = "0.2" -log = "0.4.8" -serde_json = "1.0.55" -vmm-sys-util = "0.11.0" -once_cell = "1.9.0" -sscanf = "0.2.1" -rustls = "0.20.6" -rustls-pemfile = "1.0.0" +log = "0.4" +serde_json = "1.0" +vmm-sys-util = "0.11.1" +once_cell = "1.18.0" +sscanf = "0.4.1" +rustls = "0.21.1" +rustls-pemfile = "1.0.2" sasl2-sys = "0.1.20" -bitintr = "0.2.0" -gtk = "0.14.3" +bitintr = "0.3.0" +gtk = "0.17.1" gettext-rs = "0.7.0" machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 06ad42fcd..6c348c8d4 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -19,7 +19,7 @@ use gtk::{ ffi::{GDK_KEY_equal, GDK_KEY_minus, GDK_KEY_B, GDK_KEY_F, GDK_KEY_M, GDK_KEY_S}, ModifierType, }, - glib::{self, object::GObject, translate::ToGlibPtr}, + glib::{self, gobject_ffi::GObject, translate::ToGlibPtr}, prelude::{AccelGroupExtManual, NotebookExtManual, ObjectType, WidgetExtManual}, traits::{ BoxExt, CheckMenuItemExt, ContainerExt, DialogExt, GtkMenuExt, GtkMenuItemExt, diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 41e2bbda0..bae128804 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -950,24 +950,25 @@ pub(crate) fn update_window_size(gs: &Rc>) -> Result<( (width * borrowed_gs.scale_x, height * borrowed_gs.scale_y) }; - let geo: Geometry = Geometry { - min_width: scale_width as i32, - min_height: scale_height as i32, - max_width: 0, - max_height: 0, - base_width: 0, - base_height: 0, - width_inc: 0, - height_inc: 0, - min_aspect: 0.0, - max_aspect: 0.0, - win_gravity: Gravity::Center, - }; + let geo: Geometry = Geometry::new( + scale_width as i32, + scale_height as i32, + 0, + 0, + 0, + 0, + 0, + 0, + 0.0, + 0.0, + Gravity::Center, + ); + let geo_mask = WindowHints::MIN_SIZE; borrowed_gs .draw_area - .set_size_request(geo.min_width, geo.min_height); + .set_size_request(geo.min_width(), geo.min_height()); if let Some(window) = borrowed_gs.draw_area.window() { window.set_geometry_hints(&geo, geo_mask) } diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index f3dc6497e..06e848cec 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -276,12 +276,12 @@ pub fn make_vencrypt_config(args: &TlsCreds) -> Result> { client_auth_roots.add(&root)?; } if CLIENT_REQUIRE_AUTH { - AllowAnyAuthenticatedClient::new(client_auth_roots) + AllowAnyAuthenticatedClient::new(client_auth_roots).boxed() } else { - AllowAnyAnonymousOrAuthenticatedClient::new(client_auth_roots) + AllowAnyAnonymousOrAuthenticatedClient::new(client_auth_roots).boxed() } } else { - NoClientAuth::new() + NoClientAuth::boxed() }; // Cipher suiter. diff --git a/util/Cargo.toml b/util/Cargo.toml index cb3982ed3..3bb52fbf4 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -8,17 +8,17 @@ license = "Mulan PSL v2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -arc-swap = "1.5.0" +arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" libc = "0.2" log = { version = "0.4", features = ["std"]} -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" byteorder = "1.4.3" -once_cell = "1.13.0" -io-uring = "0.5.7" -errno = "0.2.8" +once_cell = "1.18.0" +io-uring = "0.6.0" +errno = "0.3.1" serde = { version = "1.0", features = ["derive"] } -v4l2-sys-mit = "0.2.0" \ No newline at end of file +v4l2-sys-mit = "0.3.0" diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index fb247cd01..b413641a4 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -51,7 +51,7 @@ impl AioContext for IoUringContext { for iocb in iocbp.iter() { // SAFETY: iocb is valid until request is finished. let cb = unsafe { &*(*iocb) }; - let offset = cb.offset as libc::off_t; + let offset = cb.offset as u64; let data = cb.user_data; let len = cb.iovec.len(); let iovs = cb.iovec.as_ptr(); diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index 705d98772..d4c8a42e1 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -11,12 +11,12 @@ byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" vfio-bindings = "0.3" -once_cell = "1.13.0" +once_cell = "1.18.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } util = { path = "../util" } diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 730eb6b4b..b2bf2b962 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -7,14 +7,14 @@ license = "Mulan PSL v2" description = "Provide virtio fs for VM" [dependencies] -capng = "0.2.2" -errno = "0.2.8" -log = "0.4.8" +capng = "0.2.3" +errno = "0.3.1" +log = "0.4" libc = "0.2" thiserror = "1.0" anyhow = "1.0" error-chain = "0.12.4" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 58cc24cd4..cd15d7d66 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -10,12 +10,12 @@ description = "Virtio devices emulation" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-ioctls = "0.12.0" +kvm-ioctls = "0.13.0" libc = "0.2" log = "0.4" serde_json = "1.0" -vmm-sys-util = "0.11.0" -once_cell = "1.13.0" +vmm-sys-util = "0.11.1" +once_cell = "1.18.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } -- Gitee From 65ede24e1e3d7025824cadeb247cb280db0fea38 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 10 Jun 2023 17:42:57 +0800 Subject: [PATCH 1142/2187] CPU: add cpu cache topology Add the cache topology for the ARM CPU through the PPTT table of the ACPI. Signed-off-by: Mingwang Li --- acpi/src/acpi_table.rs | 129 ++++++++++++++++++++++--- machine/src/standard_vm/aarch64/mod.rs | 48 ++++++--- 2 files changed, 151 insertions(+), 26 deletions(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 508b807e0..41ea28fb7 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -160,7 +160,7 @@ impl AcpiTable { /// `new_value` - The new value that will be set in the field. pub fn set_field(&mut self, byte_index: usize, new_value: T) { let value_len = std::mem::size_of::(); - if byte_index >= self.entries.len() || byte_index + value_len >= self.entries.len() { + if byte_index >= self.entries.len() || byte_index + value_len > self.entries.len() { panic!("Set field in table failed: overflow occurs."); } self.entries[byte_index..(byte_index + value_len)].copy_from_slice(new_value.as_bytes()); @@ -184,13 +184,13 @@ impl AmlBuilder for AcpiTable { #[repr(C, packed)] #[derive(Default, Copy, Clone)] pub struct ProcessorHierarchyNode { - pub r#type: u8, - pub length: u8, - pub reserved: u16, - pub flags: u32, - pub parent: u32, - pub acpi_processor_id: u32, - pub num_private_resources: u32, + r#type: u8, + length: u8, + reserved: u16, + flags: u32, + parent: u32, + acpi_processor_id: u32, + num_private_resources: u32, } impl ByteCode for ProcessorHierarchyNode {} @@ -202,15 +202,120 @@ impl AmlBuilder for ProcessorHierarchyNode { } impl ProcessorHierarchyNode { - pub fn new(r#type: u8, flags: u32, parent: u32, acpi_processor_id: u32) -> Self { + pub fn new( + flags: u32, + parent: u32, + acpi_processor_id: u32, + num_private_resources: u32, + ) -> Self { Self { - r#type, - length: 20, + r#type: 0, + length: 20 + num_private_resources as u8 * 4, reserved: 0, flags, parent, acpi_processor_id, - num_private_resources: 0, + num_private_resources, + } + } +} + +pub fn processor_append_priv_res(pptt: &mut AcpiTable, priv_resources: Vec) { + let start = pptt.table_len(); + pptt.set_table_len(start + priv_resources.len() * 4); + for (i, priv_res) in priv_resources.iter().enumerate() { + pptt.set_field(start + i * 4, *priv_res); + } +} + +/// The Type of the hardcoded cache info +pub enum CacheType { + L1D, + L1I, + L2, + L3, +} + +struct CacheNode { + size: u32, + sets: u32, + associativity: u8, + attributes: u8, + line_size: u16, +} + +const CACHE_NODES: [CacheNode; CacheType::L3 as usize + 1] = [ + // L1 data cache + CacheNode { + size: 65536, + sets: 256, + associativity: 4, + attributes: 2, + line_size: 64, + }, + // L1 instruction cache + CacheNode { + size: 65536, + sets: 256, + associativity: 4, + attributes: 4, + line_size: 64, + }, + // L2 unified cache + CacheNode { + size: 524228, + sets: 1024, + associativity: 8, + attributes: 10, + line_size: 64, + }, + // L3 unified cache + CacheNode { + size: 33554432, + sets: 2048, + associativity: 15, + attributes: 10, + line_size: 128, + }, +]; + +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +pub struct CacheHierarchyNode { + r#type: u8, + length: u8, + reserved: u16, + flags: u32, + next_level: u32, + size: u32, + number_sets: u32, + associativity: u8, + attributes: u8, + line_size: u16, +} + +impl ByteCode for CacheHierarchyNode {} + +impl AmlBuilder for CacheHierarchyNode { + fn aml_bytes(&self) -> Vec { + self.as_bytes().to_vec() + } +} + +impl CacheHierarchyNode { + pub fn new(next_level: u32, cache_type: CacheType) -> Self { + let cache_node = &CACHE_NODES[cache_type as usize]; + Self { + r#type: 1, + length: 24, + reserved: 0, + flags: 127, + next_level, + size: cache_node.size, + number_sets: cache_node.sets, + associativity: cache_node.associativity, + attributes: cache_node.attributes, + line_size: cache_node.line_size, } } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index f3ab6f8a2..08b27ba6a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -32,15 +32,16 @@ use vmm_sys_util::eventfd::EventFd; use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; use acpi::{ - AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, - AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlScope, - AmlScopeBuilder, AmlString, ProcessorHierarchyNode, TableLoader, - ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, - ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, ACPI_GTDT_CAP_ALWAYS_ON, - ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, ACPI_IORT_NODE_PCI_ROOT_COMPLEX, - ACPI_MADT_GENERIC_CPU_INTERFACE, ACPI_MADT_GENERIC_DISTRIBUTOR, - ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, ARCH_GIC_MAINT_IRQ, - ID_MAPPING_ENTRY_SIZE, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, ROOT_COMPLEX_ENTRY_SIZE, + processor_append_priv_res, AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, + AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, + AmlNameDecl, AmlScope, AmlScopeBuilder, AmlString, CacheHierarchyNode, CacheType, + ProcessorHierarchyNode, TableLoader, ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ, + ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ, ACPI_GTDT_ARCH_TIMER_VIRT_IRQ, + ACPI_GTDT_CAP_ALWAYS_ON, ACPI_GTDT_INTERRUPT_MODE_LEVEL, ACPI_IORT_NODE_ITS_GROUP, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX, ACPI_MADT_GENERIC_CPU_INTERFACE, + ACPI_MADT_GENERIC_DISTRIBUTOR, ACPI_MADT_GENERIC_REDISTRIBUTOR, ACPI_MADT_GENERIC_TRANSLATOR, + ARCH_GIC_MAINT_IRQ, ID_MAPPING_ENTRY_SIZE, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, + ROOT_COMPLEX_ENTRY_SIZE, }; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -296,21 +297,34 @@ impl StdMachine { fn build_pptt_cores(&self, pptt: &mut AcpiTable, cluster_offset: u32, uid: &mut u32) { for core in 0..self.cpu_topo.cores { + let mut priv_resources = vec![0; 3]; + priv_resources[0] = pptt.table_len() as u32; + let mut cache_hierarchy_node = CacheHierarchyNode::new(0, CacheType::L2); + pptt.append_child(&cache_hierarchy_node.aml_bytes()); + priv_resources[1] = pptt.table_len() as u32; + cache_hierarchy_node = CacheHierarchyNode::new(priv_resources[0], CacheType::L1D); + pptt.append_child(&cache_hierarchy_node.aml_bytes()); + priv_resources[2] = pptt.table_len() as u32; + cache_hierarchy_node = CacheHierarchyNode::new(priv_resources[0], CacheType::L1I); + pptt.append_child(&cache_hierarchy_node.aml_bytes()); + if self.cpu_topo.threads > 1 { let core_offset = pptt.table_len(); let core_hierarchy_node = - ProcessorHierarchyNode::new(0, 0x0, cluster_offset, core as u32); + ProcessorHierarchyNode::new(0x0, cluster_offset, core as u32, 3); pptt.append_child(&core_hierarchy_node.aml_bytes()); + processor_append_priv_res(pptt, priv_resources); for _thread in 0..self.cpu_topo.threads { let thread_hierarchy_node = - ProcessorHierarchyNode::new(0, 0xE, core_offset as u32, *uid); + ProcessorHierarchyNode::new(0xE, core_offset as u32, *uid, 0); pptt.append_child(&thread_hierarchy_node.aml_bytes()); (*uid) += 1; } } else { - let core_hierarchy_node = ProcessorHierarchyNode::new(0, 0xA, cluster_offset, *uid); + let core_hierarchy_node = ProcessorHierarchyNode::new(0xA, cluster_offset, *uid, 3); pptt.append_child(&core_hierarchy_node.aml_bytes()); (*uid) += 1; + processor_append_priv_res(pptt, priv_resources); } } } @@ -319,7 +333,7 @@ impl StdMachine { for cluster in 0..self.cpu_topo.clusters { let cluster_offset = pptt.table_len(); let cluster_hierarchy_node = - ProcessorHierarchyNode::new(0, 0x0, socket_offset, cluster as u32); + ProcessorHierarchyNode::new(0x0, socket_offset, cluster as u32, 0); pptt.append_child(&cluster_hierarchy_node.aml_bytes()); self.build_pptt_cores(pptt, cluster_offset as u32, uid); } @@ -327,9 +341,15 @@ impl StdMachine { fn build_pptt_sockets(&self, pptt: &mut AcpiTable, uid: &mut u32) { for socket in 0..self.cpu_topo.sockets { + let priv_resources = vec![pptt.table_len() as u32]; + let cache_hierarchy_node = CacheHierarchyNode::new(0, CacheType::L3); + pptt.append_child(&cache_hierarchy_node.aml_bytes()); + let socket_offset = pptt.table_len(); - let socket_hierarchy_node = ProcessorHierarchyNode::new(0, 0x1, 0, socket as u32); + let socket_hierarchy_node = ProcessorHierarchyNode::new(0x1, 0, socket as u32, 1); pptt.append_child(&socket_hierarchy_node.aml_bytes()); + processor_append_priv_res(pptt, priv_resources); + self.build_pptt_clusters(pptt, socket_offset as u32, uid); } } -- Gitee From a494d97672a1754b0ec3cc1f4479483300da5c72 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 16 Jun 2023 12:20:21 +0800 Subject: [PATCH 1143/2187] MST: add mst for cpu cache Signed-off-by: Mingwang Li --- tests/mod_test/tests/acpi_test.rs | 59 +++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index 0e263853f..ee29acfb3 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -12,7 +12,7 @@ use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicRedistributor, AcpiRsdp, AcpiSratGiccAffinity, - AcpiSratMemoryAffinity, AcpiTableHeader, ProcessorHierarchyNode, + AcpiSratMemoryAffinity, AcpiTableHeader, CacheHierarchyNode, ProcessorHierarchyNode, }; use byteorder::{ByteOrder, LittleEndian}; use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; @@ -37,10 +37,10 @@ const IORT_TABLE_DATA_LENGTH: u32 = 128; const SPCR_TABLE_DATA_LENGTH: u32 = 80; // Now mcfg table data length is 60. const MCFG_TABLE_DATA_LENGTH: u32 = 60; -// Now acpi tables data length is 5610(cpu number is 8). -const ACPI_TABLES_DATA_LENGTH_8: usize = 5610; -// Now acpi tables data length is 31953(cpu number is 200). -const ACPI_TABLES_DATA_LENGTH_200: usize = 31953; +// Now acpi tables data length is 5974(cpu number is 8). +const ACPI_TABLES_DATA_LENGTH_8: usize = 5974; +// Now acpi tables data length is 40415(cpu number is 200). +const ACPI_TABLES_DATA_LENGTH_200: usize = 40415; fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { let file_name = "etc/acpi/rsdp"; @@ -103,7 +103,7 @@ fn check_madt(data: &[u8], cpu: u8) { assert_eq!(data[offset + 20], 3); // Check GIC CPU - offset = offset + mem::size_of::(); + offset += mem::size_of::(); for i in 0..cpu { assert_eq!(data[offset + 1], 80); // The length of this structure assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), i as u32); // CPU interface number @@ -112,7 +112,7 @@ fn check_madt(data: &[u8], cpu: u8) { assert_eq!(LittleEndian::read_u32(&data[(offset + 20)..]), 23); // Performance monitoring interrupts assert_eq!(LittleEndian::read_u64(&data[(offset + 56)..]), 25); // Virtual GIC maintenance interrupt assert_eq!(LittleEndian::read_u64(&data[(offset + 68)..]), i as u64); // MPIDR - offset = offset + mem::size_of::(); + offset += mem::size_of::(); } // Check GIC Redistributor @@ -120,7 +120,7 @@ fn check_madt(data: &[u8], cpu: u8) { assert_eq!(MEM_LAYOUT[LayoutEntryType::GicRedist as usize].0, addr); // Check GIC Its - offset = offset + mem::size_of::(); + offset += mem::size_of::(); addr = LittleEndian::read_u64(&data[(offset + 8)..]); assert_eq!(MEM_LAYOUT[LayoutEntryType::GicIts as usize].0, addr); } @@ -209,13 +209,13 @@ fn check_srat(data: &[u8]) { assert_eq!(proximity_domain, i); let process_uid = LittleEndian::read_u32(&data[(offset + 6)..]); assert_eq!(process_uid, (i * 4) + j); - offset = offset + mem::size_of::(); + offset += mem::size_of::(); } assert_eq!(LittleEndian::read_u64(&data[(offset + 8)..]), base_addr); let size = LittleEndian::read_u64(&data[(offset + 16)..]); assert_eq!(size, 0x8000_0000); base_addr = base_addr + size; - offset = offset + mem::size_of::(); + offset += mem::size_of::(); } } @@ -233,7 +233,7 @@ fn check_slit(data: &[u8]) { } else { assert_eq!(data[offset], 30); } - offset = offset + 1; + offset += 1; } } } @@ -244,23 +244,52 @@ fn check_pptt(data: &[u8]) { // offset = AcpiTable.len = 36 let mut offset = 36; // sockets = 1, clusters = 1, cores = 4, threads = 2 + // Check L3 cache type, next_level and attributes. + assert_eq!(data[offset], 1); + assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), 0); + assert_eq!(data[offset + 21], 10); + // Check sockets flags and processor_id. + offset += mem::size_of::(); assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 1); assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), 0); // Check clusters flags and processor_id. - offset = offset + mem::size_of::(); + // Sockets have an L3 cache, so it's offset to add 4. + offset += mem::size_of::() + 4; assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 0); assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), 0); - // Check cores flags and processor_id. for i in 0..4 { - offset = offset + mem::size_of::(); + // Check L2 cache type, next_level and attributes. + offset += mem::size_of::(); + assert_eq!(data[offset], 1); + assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), 0); + assert_eq!(data[offset + 21], 10); + + // Check L1D cache type, next_level and attributes. + let next_level = offset as u32; + offset += mem::size_of::(); + assert_eq!(data[offset], 1); + assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), next_level); + assert_eq!(data[offset + 21], 2); + + // Check L1I cache type, next_level and attributes. + offset += mem::size_of::(); + assert_eq!(data[offset], 1); + assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), next_level); + assert_eq!(data[offset + 21], 4); + + // Check cores flags and processor_id. + offset += mem::size_of::(); assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 0); assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), i); + + // Cores have L2, L1D, L1I cache, so it'3 offset to add 3 * 4; + offset += 3 * 4; for j in 0..2 { // Check threads flags and processor_id. - offset = offset + mem::size_of::(); + offset += mem::size_of::(); assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), 0xE); assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), i * 2 + j); } -- Gitee From 261e26c5ce442529a4f55859b7ab426bad076ac4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Jun 2023 03:09:28 +0800 Subject: [PATCH 1144/2187] ui/gtk: Add smooth scroll support Signed-off-by: Keqian Zhu --- ui/src/gtk/draw.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 788a88654..a9422654a 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -109,6 +109,7 @@ pub(crate) fn set_callback_for_draw_area( | EventMask::BUTTON_RELEASE_MASK | EventMask::BUTTON_MOTION_MASK | EventMask::SCROLL_MASK + | EventMask::SMOOTH_SCROLL_MASK | EventMask::KEY_PRESS_MASK | EventMask::KEY_RELEASE_MASK | EventMask::BUTTON1_MOTION_MASK @@ -284,6 +285,19 @@ fn da_scroll_callback( ScrollDirection::Down => INPUT_BUTTON_WHEEL_DOWN, ScrollDirection::Left => INPUT_BUTTON_WHEEL_LEFT, ScrollDirection::Right => INPUT_BUTTON_WHEEL_RIGHT, + ScrollDirection::Smooth => match scroll_event.scroll_deltas() { + Some((_, delta_y)) => { + if delta_y == 0.0 { + return Ok(()); + } + if delta_y > 0.0 { + INPUT_BUTTON_WHEEL_DOWN + } else { + INPUT_BUTTON_WHEEL_UP + } + } + None => return Ok(()), + }, _ => 0x0, }; -- Gitee From aba7e6b180c73356e2903b3c050d78ee6149f663 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 19 Jun 2023 16:44:22 +0800 Subject: [PATCH 1145/2187] VCPU: destroy guest again If the vcpu cannot be kicked out, run the destroy command to kick the vcpu again. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 08b27ba6a..53a95aa8a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -15,7 +15,7 @@ mod syscall; pub use crate::error::MachineError; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; -use log::{error, info}; +use log::{error, info, warn}; use machine_manager::config::ShutdownAction; #[cfg(not(target_env = "musl"))] use machine_manager::config::UiContext; @@ -1104,6 +1104,10 @@ impl MachineLifecycle for StdMachine { }; if !self.notify_lifecycle(vmstate, KvmVmState::Shutdown) { + warn!("Failed to destroy guest, destroy continue."); + if self.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request.") + } return false; } -- Gitee From 328308113b7daa0f9bb6defe17b0a7782b55992a Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 19 Jun 2023 22:20:56 +0800 Subject: [PATCH 1146/2187] GPU: support framebuf in virtio-gpu If there is a frambuffer(resources with special flag in id), wo do periodic display update, as it is required for dumb frambuffer. Signed-off-by: Mingwang Li --- virtio/src/device/gpu.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 76b57f495..d5268b471 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -67,6 +67,9 @@ const QUEUE_NUM_GPU: usize = 2; /// Display changed event const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0; +/// The flag indicates that the frame buffer only used in windows. +const VIRTIO_GPU_RES_WIN_FRAMEBUF: u32 = 0x80000000; + #[derive(Debug)] struct GpuResource { resource_id: u32, @@ -964,7 +967,11 @@ impl GpuIoHandler { let pixman_stride = unsafe { pixman_image_get_stride(res.pixman_image) }; let offset = info_set_scanout.rect.x_coord * bpp + info_set_scanout.rect.y_coord * pixman_stride as u32; - let res_data = unsafe { pixman_image_get_data(res.pixman_image) }; + let res_data = if info_set_scanout.resource_id & VIRTIO_GPU_RES_WIN_FRAMEBUF != 0 { + res.iov[0].iov_base as *mut u32 + } else { + unsafe { pixman_image_get_data(res.pixman_image) } + }; let res_data_offset = unsafe { res_data.offset(offset as isize) }; // Create surface for the scanout. @@ -1090,6 +1097,9 @@ impl GpuIoHandler { } let res = &self.resources_list[res_idx.unwrap()]; + if res.resource_id & VIRTIO_GPU_RES_WIN_FRAMEBUF != 0 { + return (None, VIRTIO_GPU_RESP_OK_NODATA); + } if !is_rect_in_resource(&info_transfer.rect, res) { error!( "GuestError: The resource (id: {} width: {} height: {}) is outfit for transfer rectangle (offset: {} width: {} height: {} x_coord: {} y_coord: {}).", -- Gitee From 345c757406a6b82d9d4bb9ad314166e1ba069d89 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 20 Jun 2023 11:05:37 +0800 Subject: [PATCH 1147/2187] Scream-PA: flush left data when format changed When the audio format changes, the old data is refreshed to aviod interference with the new audio data. Signed-off-by: Mingwang Li --- devices/src/misc/scream/pulseaudio.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 6bc089ede..035823932 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -159,6 +159,10 @@ impl PulseStreamData { if self.stream_fmt == recv_data.fmt { return; } + + // Flush left data when audio format changed. + self.destroy(); + // If audio format changed, reconfigure self.stream_fmt = recv_data.fmt; self.ss.channels = recv_data.fmt.channels; -- Gitee From 77e38ffaa6d52629d19f4e3e190a916a65762cc9 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 19 Jun 2023 20:37:58 +0800 Subject: [PATCH 1148/2187] usb: increase the buffer size for usb camera Some USB camera devices have many frame descriptors, and the buffer size is increased so that control transfers can be transferred normally. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 3 ++- devices/src/usb/keyboard.rs | 4 ++-- devices/src/usb/mod.rs | 12 ++++-------- devices/src/usb/storage.rs | 4 ++-- devices/src/usb/tablet.rs | 4 ++-- devices/src/usb/usbhost/mod.rs | 3 ++- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 3a41817c9..776b986a0 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -93,6 +93,7 @@ const VS_COMMIT_CONTROL: u8 = 2; const MAX_PAYLOAD: u32 = FRAME_SIZE_1280_720; const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; +const USB_CAMERA_BUFFER_LEN: usize = 12 * 1024; pub struct UsbCamera { id: String, // uniq device id @@ -611,7 +612,7 @@ impl UsbCamera { let camera = camera_ops(config.clone())?; Ok(Self { id: config.id.unwrap(), - usb_device: UsbDevice::new(), + usb_device: UsbDevice::new(USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), camera_dev: camera, diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index bbce82b92..339e23f92 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -16,13 +16,13 @@ use anyhow::Result; use log::{debug, error, info}; use once_cell::sync::Lazy; -use super::config::*; use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; use super::xhci::xhci_controller::XhciDevice; +use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, @@ -166,7 +166,7 @@ impl UsbKeyboard { pub fn new(id: String) -> Self { Self { id, - usb_device: UsbDevice::new(), + usb_device: UsbDevice::new(USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Keyboard), cntlr: None, } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index e2387cba3..c37d5c135 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -49,6 +49,8 @@ use xhci::xhci_controller::{UsbPort, XhciDevice}; const USB_MAX_ENDPOINTS: u32 = 15; /// USB max address. const USB_MAX_ADDRESS: u8 = 127; +/// USB device default buffer length. +pub const USB_DEVICE_BUFFER_DEFAULT_LEN: usize = 4096; /// USB packet return status. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -112,7 +114,7 @@ pub struct UsbDevice { } impl UsbDevice { - pub fn new() -> Self { + pub fn new(data_buf_len: usize) -> Self { let mut dev = UsbDevice { port: None, speed: 0, @@ -120,7 +122,7 @@ impl UsbDevice { ep_ctl: UsbEndpoint::new(0, false, USB_ENDPOINT_ATTR_CONTROL), ep_in: Vec::new(), ep_out: Vec::new(), - data_buf: vec![0_u8; 4096], + data_buf: vec![0_u8; data_buf_len], remote_wakeup: 0, descriptor: UsbDescriptor::new(), unplugged_id: None, @@ -300,12 +302,6 @@ impl UsbDevice { } } -impl Default for UsbDevice { - fn default() -> Self { - Self::new() - } -} - impl Drop for UsbDevice { fn drop(&mut self) { if let Some(id) = &self.unplugged_id { diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 4f6329bbf..97167d200 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -23,12 +23,12 @@ use once_cell::sync::Lazy; use machine_manager::config::{DriveFile, UsbStorageConfig}; use util::aio::{Aio, AioEngine}; -use super::config::*; use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use super::xhci::xhci_controller::XhciDevice; +use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; use crate::{ ScsiBus::{ @@ -317,7 +317,7 @@ impl UsbStorage { Self { id: config.id.clone().unwrap(), - usb_device: UsbDevice::new(), + usb_device: UsbDevice::new(USB_DEVICE_BUFFER_DEFAULT_LEN), state: UsbStorageState::new(), cntlr: None, config: config.clone(), diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 0f4a4b018..562e1b798 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -17,13 +17,13 @@ use anyhow::Result; use log::{debug, error, info}; use once_cell::sync::Lazy; -use super::config::*; use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescOther, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; use super::xhci::xhci_controller::XhciDevice; +use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{ notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, @@ -124,7 +124,7 @@ impl UsbTablet { pub fn new(id: String) -> Self { Self { id, - usb_device: UsbDevice::new(), + usb_device: UsbDevice::new(USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Tablet), cntlr: None, } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index dc8134de8..5c4ce4560 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -51,6 +51,7 @@ mod host_usblib; const NON_ISO_PACKETS_NUMS: c_int = 0; const COMPLETE_LIMIT: u32 = 200; const HANDLE_TIMEOUT_MS: u64 = 2; +const USB_HOST_BUFFER_LEN: usize = 12 * 1024; #[derive(Default, Copy, Clone)] struct InterfaceStatus { @@ -158,7 +159,7 @@ impl UsbHost { libevt: Vec::new(), ifs_num: 0, ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], - usb_device: UsbDevice::new(), + usb_device: UsbDevice::new(USB_HOST_BUFFER_LEN), exit: None, requests: Arc::new(Mutex::new(LinkedList::new())), completed: Arc::new(Mutex::new(0)), -- Gitee From 27bc9efbd5ac9bebe41f5d6cf4ea0689a19e0981 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 21 Jun 2023 12:04:52 +0800 Subject: [PATCH 1149/2187] XHCI: add check before TRB processed If the device has been detached, the TRB processing must stop. Otherwise, the XHCI reports an abnormal error, causing the XHCI to be reset. Signed-off-by: Mingwang Li --- devices/src/usb/xhci/xhci_controller.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 6886fbe82..dd77e8c4a 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1471,6 +1471,14 @@ impl XhciDevice { return Ok(()); } }; + + // If the device has been detached, but the guest has not been notified. + // In this case, the Transaction Error is reported when the TRB processed. + // Therefore, don't continue here. + if self.get_usb_dev(slot_id, ep_id).is_err() { + return Ok(()); + } + debug!( "kick_endpoint slotid {} epid {} dequeue {:x}", slot_id, -- Gitee From 7a3571820b1dc9d2831af6e3099ee29ae811d380 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 22 Jun 2023 15:58:24 +0800 Subject: [PATCH 1150/2187] VNC: Fix regular matching for RFB version Fix regular matching for RFB version Signed-off-by: Xiao Ye --- ui/src/vnc/client_io.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 86fe26e4c..bf48e8bb7 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -501,9 +501,14 @@ impl ClientIoHandler { /// Exchange RFB protocol version with client. fn handle_version(&mut self) -> Result<()> { let client = self.client.clone(); - let buf = self.read_incoming_msg(); + let mut buf = self.read_incoming_msg(); + // The last character should be '\n' + let lf_char = buf.pop().ok_or(VncError::UnsupportRFBProtocolVersion)?; + if !lf_char.eq(&10) { + return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); + } let ver_str = String::from_utf8_lossy(&buf).to_string(); - let ver = match scanf!(ver_str, "RFB {usize:/\\d\\{3\\}/}.{usize:/\\d\\{3\\}/}\n") { + let ver = match scanf!(ver_str, "RFB {usize:/\\d{3}/}.{usize:/\\d{3}/}") { Ok(v) => v, Err(_e) => { return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); -- Gitee From b2ddd09bd048f5aa05d2ce73e91aa8d0011573df Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Jun 2023 14:45:30 +0800 Subject: [PATCH 1151/2187] virtio-gpu: Optimize cmd_update_cursor() 1. No need to save VirtioGpuUpdateCursor, as it's used in migration and virtio-gpu doesn't support migration for now. 2. Add constructor for DisplayMouse. Signed-off-by: Keqian Zhu --- pci/src/demo_device/gpu_device.rs | 9 +------- ui/src/console.rs | 38 +++++++++++++++++++++++-------- virtio/src/device/gpu.rs | 19 ++++------------ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index 53d98bf9d..f53a88cb9 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -233,14 +233,7 @@ impl DeviceTypeOperation for DemoGpu { self.surface = Some(surface); // Create image. - let mouse = DisplayMouse { - width: 64_u32, - height: 64_u32, - hot_x: 4_u32, - hot_y: 4_u32, - data: vec![0_u8; 64 * 64 * 4], - }; - self.mouse = Some(mouse); + self.mouse = Some(DisplayMouse::new(64, 64, 4, 4)); Ok(()) } diff --git a/ui/src/console.rs b/ui/src/console.rs index ce5a1bd13..64809c269 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -10,22 +10,27 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::pixman::{ - create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, - pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, ColorNames, - COLOR_TABLE_RGB, -}; -use anyhow::Result; -use log::error; -use machine_manager::event_loop::EventLoop; -use once_cell::sync::Lazy; use std::{ - cmp, ptr, + cmp, + mem::size_of, + ptr, sync::{Arc, Mutex, Weak}, time::Duration, }; + +use anyhow::Result; +use log::error; +use once_cell::sync::Lazy; + +use machine_manager::event_loop::EventLoop; use util::pixman::{pixman_format_code_t, pixman_image_t}; +use crate::pixman::{ + create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, + pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, ColorNames, + COLOR_TABLE_RGB, +}; + static CONSOLES: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(ConsoleList::new()))); static DISPLAY_STATE: Lazy>> = @@ -121,6 +126,19 @@ pub struct DisplayMouse { pub data: Vec, } +impl DisplayMouse { + pub fn new(width: u32, height: u32, hot_x: u32, hot_y: u32) -> Self { + let data_size = (width * height) as usize * size_of::(); + DisplayMouse { + width, + height, + hot_x, + hot_y, + data: vec![0_u8; data_size], + } + } +} + /// UIs (such as VNC) can register interfaces related to image display. /// After the graphic hardware processes images, these interfaces can be /// called to display images on the user's desktop. diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index d5268b471..d2ba7c03a 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -407,8 +407,6 @@ struct GpuScanout { x: u32, y: u32, resource_id: u32, - // Unused with vnc backend, work in others. - cursor: VirtioGpuUpdateCursor, // Cursor visable cursor_visible: bool, } @@ -670,8 +668,10 @@ impl GpuIoHandler { } fn update_cursor_image(&mut self, info_cursor: &VirtioGpuUpdateCursor) { - let res_idx = self.get_resource_idx(info_cursor.resource_id); + let (res_idx, error) = + self.get_backed_resource_idx(info_cursor.resource_id, "cmd_update_cursor"); if res_idx.is_none() { + error!("Failed to update cursor image, errcode: {}", error); return; } @@ -719,8 +719,6 @@ impl GpuIoHandler { let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; if req.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { - scanout.cursor.pos.x_coord = info_cursor.hot_x; - scanout.cursor.pos.y_coord = info_cursor.hot_y; if info_cursor.resource_id == 0 && scanout.cursor_visible && scanout.mouse.is_some() { let data = &mut scanout.mouse.as_mut().unwrap().data; // In order to improve performance, displaying cursor by virtio-gpu. @@ -744,14 +742,8 @@ impl GpuIoHandler { } else if req.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { match &mut scanout.mouse { None => { - let tmp_mouse = DisplayMouse { - height: 64, - width: 64, - hot_x: info_cursor.hot_x, - hot_y: info_cursor.hot_y, - data: vec![0_u8; 64 * 64 * size_of::()], - }; - scanout.mouse = Some(tmp_mouse); + let mouse = DisplayMouse::new(64, 64, info_cursor.hot_x, info_cursor.hot_y); + scanout.mouse = Some(mouse); } Some(mouse) => { mouse.hot_x = info_cursor.hot_x; @@ -764,7 +756,6 @@ impl GpuIoHandler { } let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; display_cursor_define(&scanout.con, scanout.mouse.as_mut().unwrap())?; - scanout.cursor = info_cursor; } else { bail!("Wrong header type for cursor queue"); } -- Gitee From 35c6174ac3c5fdba9b0f14eaed87ad51be3bd85d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Jun 2023 15:05:55 +0800 Subject: [PATCH 1152/2187] ui/console: Cursor no need to be mut in dpy_cursor_update Signed-off-by: Keqian Zhu --- pci/src/demo_device/dpy_device.rs | 2 +- pci/src/demo_device/gpu_device.rs | 6 +++--- ui/src/console.rs | 6 +++--- ui/src/gtk/mod.rs | 2 +- ui/src/vnc/mod.rs | 2 +- virtio/src/device/gpu.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pci/src/demo_device/dpy_device.rs b/pci/src/demo_device/dpy_device.rs index 452b592e4..ada4952c7 100644 --- a/pci/src/demo_device/dpy_device.rs +++ b/pci/src/demo_device/dpy_device.rs @@ -150,7 +150,7 @@ impl DisplayChangeListenerOperations for DpyInterface { Ok(()) } - fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) -> Result<()> { + fn dpy_cursor_update(&self, cursor: &DisplayMouse) -> Result<()> { if DISPLAY.lock().unwrap().is_empty() { error!("Demo Display is empty, check initialize"); return Ok(()); diff --git a/pci/src/demo_device/gpu_device.rs b/pci/src/demo_device/gpu_device.rs index f53a88cb9..6d52f9a22 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/pci/src/demo_device/gpu_device.rs @@ -125,14 +125,14 @@ impl DemoGpu { hot_y: u32, mouse_data: u32, ) -> Result<()> { - let mut mouse = DisplayMouse { + let mouse = DisplayMouse { width, height, hot_x, hot_y, data: vec![0_u8; mouse_data as usize], }; - display_cursor_define(&self.con, &mut mouse)?; + display_cursor_define(&self.con, &mouse)?; self.mouse = Some(mouse); Ok(()) } @@ -170,7 +170,7 @@ impl DemoGpu { /// Update the cursor image. pub fn graphic_cursor_define(&mut self) -> Result<()> { - if let Some(mouse) = &mut self.mouse { + if let Some(mouse) = &self.mouse { display_cursor_define(&self.con, mouse)?; } Ok(()) diff --git a/ui/src/console.rs b/ui/src/console.rs index 64809c269..7632c072d 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -150,7 +150,7 @@ pub trait DisplayChangeListenerOperations { /// Update image. fn dpy_image_update(&self, _x: i32, _y: i32, _w: i32, _h: i32) -> Result<()>; /// Update the cursor data. - fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) -> Result<()>; + fn dpy_cursor_update(&self, _cursor: &DisplayMouse) -> Result<()>; /// Set the current display as major. fn dpy_set_major(&self) -> Result<()> { Ok(()) @@ -478,7 +478,7 @@ pub fn display_graphic_update( /// * `cursor` - data of curosr image. pub fn display_cursor_define( console: &Option>>, - cursor: &mut DisplayMouse, + cursor: &DisplayMouse, ) -> Result<()> { let con = match console.as_ref().and_then(|c| c.upgrade()) { Some(c) => c, @@ -819,7 +819,7 @@ mod tests { Ok(()) } - fn dpy_cursor_update(&self, _cursor: &mut DisplayMouse) -> Result<()> { + fn dpy_cursor_update(&self, _cursor: &DisplayMouse) -> Result<()> { Ok(()) } } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 19131e5d6..8c9f11f11 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -185,7 +185,7 @@ impl DisplayChangeListenerOperations for GtkInterface { Ok(()) } - fn dpy_cursor_update(&self, cursor_data: &mut DisplayMouse) -> Result<()> { + fn dpy_cursor_update(&self, cursor_data: &DisplayMouse) -> Result<()> { let mut event = DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::CursorDefine); event.cursor = Some(cursor_data.clone()); diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 50af7db0e..a0d72d569 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -205,7 +205,7 @@ impl DisplayChangeListenerOperations for VncInterface { Ok(()) } - fn dpy_cursor_update(&self, cursor: &mut DisplayMouse) -> Result<()> { + fn dpy_cursor_update(&self, cursor: &DisplayMouse) -> Result<()> { if VNC_SERVERS.lock().unwrap().is_empty() { return Ok(()); } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index d2ba7c03a..7053da04c 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -736,7 +736,7 @@ impl GpuIoHandler { *item = 0_u8; } } - display_cursor_define(&scanout.con, scanout.mouse.as_mut().unwrap())?; + display_cursor_define(&scanout.con, scanout.mouse.as_ref().unwrap())?; scanout.cursor_visible = false; } } else if req.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { @@ -755,7 +755,7 @@ impl GpuIoHandler { self.update_cursor_image(&info_cursor); } let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; - display_cursor_define(&scanout.con, scanout.mouse.as_mut().unwrap())?; + display_cursor_define(&scanout.con, scanout.mouse.as_ref().unwrap())?; } else { bail!("Wrong header type for cursor queue"); } -- Gitee From 0f7c671285c0fbeaa51188367c0f31eefaaade57 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sun, 25 Jun 2023 10:45:34 +0800 Subject: [PATCH 1153/2187] gtk: grab keyboard when pointer enter draw area Grab keyboard when pointer enter draw area, and ungrab keyboard when pointer leave draw area. Signed-off-by: zhouli57 --- ui/src/gtk/draw.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 48a85444f..788a88654 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -13,6 +13,7 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::Result; +use gdk::{prelude::SeatExt, SeatCapabilities}; use gtk::{ cairo, gdk::{self, EventMask, ScrollDirection}, @@ -91,6 +92,18 @@ pub(crate) fn set_callback_for_draw_area( Inhibit(false)} ), ); + draw_area.connect_enter_notify_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_,enter_event| { + da_enter_callback(&gs, enter_event).unwrap_or_else(|e|error!("Enter event: {:?}", e)); + Inhibit(false)} + ), + ); + draw_area.connect_leave_notify_event( + glib::clone!(@weak gs => @default-return Inhibit(false), move |_, leave_event| { + da_leave_callback(&gs, leave_event).unwrap_or_else(|e|error!("Leave event: {:?}", e)); + Inhibit(false)} + ), + ); let event_mask = EventMask::BUTTON_PRESS_MASK | EventMask::BUTTON_RELEASE_MASK @@ -100,12 +113,44 @@ pub(crate) fn set_callback_for_draw_area( | EventMask::KEY_RELEASE_MASK | EventMask::BUTTON1_MOTION_MASK | EventMask::FOCUS_CHANGE_MASK + | EventMask::ENTER_NOTIFY_MASK + | EventMask::LEAVE_NOTIFY_MASK | EventMask::POINTER_MOTION_MASK; draw_area.add_events(event_mask); Ok(()) } +fn da_enter_callback( + gs: &Rc>, + _event: &gdk::EventCrossing, +) -> Result<()> { + update_keyboard_grab(gs, true); + Ok(()) +} + +fn da_leave_callback( + gs: &Rc>, + _event: &gdk::EventCrossing, +) -> Result<()> { + update_keyboard_grab(gs, false); + Ok(()) +} + +fn update_keyboard_grab(gs: &Rc>, grab: bool) { + let borrowed_gs = gs.borrow(); + let display = borrowed_gs.draw_area.display(); + if let Some(seat) = display.default_seat() { + if grab { + if let Some(w) = borrowed_gs.draw_area.window() { + seat.grab(&w, SeatCapabilities::KEYBOARD, false, None, None, None); + } + } else { + seat.ungrab(); + } + } +} + /// When the window size changes, /// the image resolution adapts to the window. fn da_configure_callback( -- Gitee From d087146d4983cebe8bd145c521fc3627ded02cf6 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 25 Jun 2023 14:15:01 +0800 Subject: [PATCH 1154/2187] USB: Change the usb-host control plane to asynchronous In the USB passthrough scenario, continuous hot plug/unplug may cause the control plane information to be read for more than 1s. Therefore, the control plane information to be read in asynchronous mode. Signed-off-by: Mingwang Li --- devices/src/usb/mod.rs | 3 + devices/src/usb/usbhost/host_usblib.rs | 107 ++++++++--------- devices/src/usb/usbhost/mod.rs | 155 +++++++++++++++---------- 3 files changed, 147 insertions(+), 118 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index c37d5c135..eef474b6e 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -13,6 +13,7 @@ pub mod error; pub use anyhow::Result; pub use error::UsbError; +use util::byte_code::ByteCode; #[cfg(not(target_env = "musl"))] pub mod camera; @@ -74,6 +75,8 @@ pub struct UsbDeviceRequest { pub length: u16, } +impl ByteCode for UsbDeviceRequest {} + /// The data transmission channel. #[derive(Default, Clone)] pub struct UsbEndpoint { diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index b750bf895..511c442db 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -28,13 +28,14 @@ use libusb1_sys::{ libusb_get_pollfds, libusb_pollfd, libusb_transfer, }; use log::error; -use rusb::{Context, DeviceHandle, Error, Result, UsbContext}; +use rusb::{Context, DeviceHandle, Error, Result, TransferType, UsbContext}; use vmm_sys_util::epoll::EventSet; use super::{UsbHost, UsbHostRequest}; use crate::usb::{UsbPacketStatus, USB_TOKEN_IN}; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; +const CONTROL_TIMEOUT: u32 = 10000; // 10s const BULK_TIMEOUT: u32 = 0; const INTERRUPT_TIMEOUT: u32 = 0; @@ -72,12 +73,10 @@ pub fn get_request_from_transfer(transfer: *mut libusb_transfer) -> Arc>()) } } -pub fn get_buffer_from_transfer(transfer: *mut libusb_transfer) -> &'static mut [u8] { +pub fn get_buffer_from_transfer(transfer: *mut libusb_transfer, len: usize) -> &'static mut [u8] { // SAFETY: cast the raw pointer of transfer's buffer which is transformed // from a slice with actual_length to a mutable slice. - unsafe { - std::slice::from_raw_parts_mut((*transfer).buffer, (*transfer).actual_length as usize) - } + unsafe { std::slice::from_raw_parts_mut((*transfer).buffer, len) } } pub fn get_length_from_transfer(transfer: *mut libusb_transfer) -> i32 { @@ -155,7 +154,7 @@ pub fn alloc_host_transfer(iso_packets: c_int) -> *mut libusb_transfer { unsafe { libusb1_sys::libusb_alloc_transfer(iso_packets) } } -extern "system" fn req_complete_data(host_transfer: *mut libusb_transfer) { +extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { // SAFETY: transfer is still valid because libusb just completed it // but we haven't told anyone yet. user_data remains valid because // it is dropped only when the request is completed and removed from @@ -174,8 +173,10 @@ extern "system" fn req_complete_data(host_transfer: *mut libusb_transfer) { let transfer_status = get_status_from_transfer(host_transfer); locked_packet.status = map_packet_status(transfer_status); - if locked_packet.pid as u8 == USB_TOKEN_IN && actual_length != 0 { - let data = get_buffer_from_transfer(host_transfer); + if locked_request.is_control { + locked_request.ctrl_transfer_packet(&mut locked_packet, actual_length as usize); + } else if locked_packet.pid as u8 == USB_TOKEN_IN && actual_length != 0 { + let data = get_buffer_from_transfer(host_transfer, actual_length as usize); locked_packet.transfer_packet(data, actual_length as usize); } @@ -189,11 +190,12 @@ extern "system" fn req_complete_data(host_transfer: *mut libusb_transfer) { locked_request.complete(); } -pub fn fill_bulk_transfer( +pub fn fill_transfer_by_type( transfer: *mut libusb_transfer, handle: Option<&mut DeviceHandle>, ep_number: u8, request: Arc>, + transfer_type: TransferType, ) { let packet = request.lock().unwrap().packet.clone(); let size = packet.lock().unwrap().get_iovecs_size(); @@ -209,55 +211,44 @@ pub fn fill_bulk_transfer( return; } - // SAFETY: have checked the validity of parameters of libusb_fill_bulk_transfer - // before call libusb_fill_bulk_transfer. - unsafe { - libusb1_sys::libusb_fill_bulk_transfer( - transfer, - handle.unwrap().as_raw(), - ep_number, - buffer_ptr, - size as i32, - req_complete_data, - (Arc::into_raw(request) as *mut Mutex).cast::(), - BULK_TIMEOUT, - ); - } -} - -pub fn fill_interrupt_transfer( - transfer: *mut libusb_transfer, - handle: Option<&mut DeviceHandle>, - ep_number: u8, - request: Arc>, -) { - let packet = request.lock().unwrap().packet.clone(); - let size = packet.lock().unwrap().get_iovecs_size(); - let buffer_ptr = request.lock().unwrap().buffer.as_mut_ptr(); - - if handle.is_none() { - error!("Failed to fill interrupt transfer, handle is none"); - return; - } - - if transfer.is_null() { - error!("Failed to fill interrupt transfer, transfer is a null pointer"); - return; - } - - // SAFETY: have checked the validity of parameters of libusb_fill_interrupt_transfer - // before call libusb_fill_interrupt_transfer. - unsafe { - libusb1_sys::libusb_fill_interrupt_transfer( - transfer, - handle.unwrap().as_raw(), - ep_number, - buffer_ptr, - size as i32, - req_complete_data, - (Arc::into_raw(request) as *mut Mutex).cast::(), - INTERRUPT_TIMEOUT, - ); + // SAFETY: have checked the validity of parameters of libusb_fill_*_transfer + // before call libusb_fill_*_transfer. + match transfer_type { + TransferType::Control => unsafe { + libusb1_sys::libusb_fill_control_transfer( + transfer, + handle.unwrap().as_raw(), + buffer_ptr, + req_complete, + (Arc::into_raw(request) as *mut Mutex).cast::(), + CONTROL_TIMEOUT, + ); + }, + TransferType::Bulk => unsafe { + libusb1_sys::libusb_fill_bulk_transfer( + transfer, + handle.unwrap().as_raw(), + ep_number, + buffer_ptr, + size as i32, + req_complete, + (Arc::into_raw(request) as *mut Mutex).cast::(), + BULK_TIMEOUT, + ); + }, + TransferType::Interrupt => unsafe { + libusb1_sys::libusb_fill_interrupt_transfer( + transfer, + handle.unwrap().as_raw(), + ep_number, + buffer_ptr, + size as i32, + req_complete, + (Arc::into_raw(request) as *mut Mutex).cast::(), + INTERRUPT_TIMEOUT, + ); + }, + _ => error!("Unsupport transfer type: {:?}", transfer_type), } } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 5c4ce4560..7723a14c3 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -24,7 +24,7 @@ use libusb1_sys::libusb_transfer; use log::{error, info, warn}; use rusb::{ constants::LIBUSB_CLASS_HUB, Context, Device, DeviceDescriptor, DeviceHandle, Direction, Error, - UsbContext, + TransferType, UsbContext, }; use crate::usb::{ @@ -44,7 +44,10 @@ use machine_manager::{ event_loop::{register_event_helper, unregister_event_helper}, temp_cleaner::{ExitNotifier, TempCleaner}, }; -use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}; +use util::{ + byte_code::ByteCode, + loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}, +}; mod host_usblib; @@ -65,6 +68,7 @@ pub struct UsbHostRequest { /// Async data buffer. pub buffer: Vec, pub completed: Arc>, + pub is_control: bool, } impl UsbHostRequest { @@ -72,16 +76,18 @@ impl UsbHostRequest { packet: Arc>, host_transfer: *mut libusb_transfer, completed: Arc>, + is_control: bool, ) -> Self { Self { packet, host_transfer, buffer: Vec::new(), completed, + is_control, } } - pub fn setup_buffer(&mut self) { + pub fn setup_data_buffer(&mut self) { let mut locked_packet = self.packet.lock().unwrap(); let size = locked_packet.get_iovecs_size(); self.buffer = vec![0; size as usize]; @@ -90,6 +96,14 @@ impl UsbHostRequest { } } + pub fn setup_ctrl_buffer(&mut self, data_buf: Vec, device_req: &UsbDeviceRequest) { + self.buffer = vec![0; (device_req.length + 8) as usize]; + self.buffer[..8].copy_from_slice(device_req.as_bytes()); + if self.packet.lock().unwrap().pid as u8 == USB_TOKEN_OUT { + self.buffer[8..].clone_from_slice(&data_buf); + } + } + pub fn complete(&mut self) { free_host_transfer(self.host_transfer); self.buffer.clear(); @@ -107,6 +121,9 @@ impl UsbHostRequest { if let Some(transfer) = locked_packet.xfer_ops.as_ref() { if let Some(ops) = transfer.clone().upgrade() { + if self.is_control { + self.ctrl_transfer_packet(&mut locked_packet, 0); + } drop(locked_packet); ops.lock().unwrap().submit_transfer(); } @@ -115,6 +132,19 @@ impl UsbHostRequest { Ok(()) } + + pub fn ctrl_transfer_packet(&self, packet: &mut UsbPacket, actual_length: usize) { + let setup_buf = get_buffer_from_transfer(self.host_transfer, 8); + let mut len = (setup_buf[7] as usize) << 8 | setup_buf[6] as usize; + if len > actual_length { + len = actual_length; + } + + if packet.pid as u8 == USB_TOKEN_IN && actual_length != 0 { + let data = get_buffer_from_transfer(self.host_transfer, len + 8); + packet.transfer_packet(&mut data[8..], len); + } + } } unsafe impl Sync for UsbHostRequest {} @@ -485,43 +515,6 @@ impl UsbHost { .halted = false; } - fn control_transfer_pass_through( - &mut self, - packet: &mut UsbPacket, - device_req: &UsbDeviceRequest, - ) { - if packet.pid as u8 == USB_TOKEN_OUT { - if let Err(e) = self.handle.as_ref().unwrap().write_control( - device_req.request_type, - device_req.request, - device_req.value, - device_req.index, - &self.usb_device.data_buf[..device_req.length as usize], - Duration::from_millis(1000), - ) { - error!("Failed to write control by usb host: {:?}", e); - packet.status = UsbPacketStatus::Stall; - return; - } - } else { - packet.actual_length = match self.handle.as_ref().unwrap().read_control( - device_req.request_type, - device_req.request, - device_req.value, - device_req.index, - &mut self.usb_device.data_buf[..device_req.length as usize], - Duration::from_millis(1000), - ) { - Ok(n) => n as u32, - Err(e) => { - error!("Failed to read control by usb host: {:?}", e); - 0 - } - }; - }; - packet.status = UsbPacketStatus::Success; - } - fn release_dev_to_host(&mut self) { if self.handle.is_none() { return; @@ -595,6 +588,27 @@ impl UsbHost { limit -= 1; } } + + fn submit_host_transfer( + &mut self, + host_transfer: *mut libusb_transfer, + packet: &Arc>, + ) { + match submit_host_transfer(host_transfer) { + Ok(()) => {} + Err(Error::NoDevice) => { + packet.lock().unwrap().status = UsbPacketStatus::NoDev; + return; + } + _ => { + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + }; + + packet.lock().unwrap().is_async = true; + self.clear_succ_requests(); + } } impl EventNotifierHelper for UsbHost { @@ -701,7 +715,28 @@ impl UsbDeviceOps for UsbHost { } _ => {} } - self.control_transfer_pass_through(&mut locked_packet, device_req); + drop(locked_packet); + + let host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); + let request = Arc::new(Mutex::new(UsbHostRequest::new( + packet.clone(), + host_transfer, + self.completed.clone(), + true, + ))); + request + .lock() + .unwrap() + .setup_ctrl_buffer(self.usb_device.data_buf.clone(), device_req); + fill_transfer_by_type( + host_transfer, + self.handle.as_mut(), + 0, + request, + TransferType::Control, + ); + + self.submit_host_transfer(host_transfer, packet); } fn handle_data(&mut self, packet: &Arc>) { @@ -737,13 +772,20 @@ impl UsbDeviceOps for UsbHost { cloned_packet, host_transfer, self.completed.clone(), + false, ))); - request.lock().unwrap().setup_buffer(); + request.lock().unwrap().setup_data_buffer(); self.requests.lock().unwrap().push_back(request.clone()); if packet.lock().unwrap().pid as u8 != USB_TOKEN_OUT { ep_number |= USB_DIRECTION_DEVICE_TO_HOST; } - fill_bulk_transfer(host_transfer, self.handle.as_mut(), ep_number, request); + fill_transfer_by_type( + host_transfer, + self.handle.as_mut(), + ep_number, + request, + TransferType::Bulk, + ); } USB_ENDPOINT_ATTR_INT => { host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); @@ -751,13 +793,20 @@ impl UsbDeviceOps for UsbHost { cloned_packet, host_transfer, self.completed.clone(), + false, ))); - request.lock().unwrap().setup_buffer(); + request.lock().unwrap().setup_data_buffer(); self.requests.lock().unwrap().push_back(request.clone()); if packet.lock().unwrap().pid as u8 != USB_TOKEN_OUT { ep_number |= USB_DIRECTION_DEVICE_TO_HOST; } - fill_interrupt_transfer(host_transfer, self.handle.as_mut(), ep_number, request); + fill_transfer_by_type( + host_transfer, + self.handle.as_mut(), + ep_number, + request, + TransferType::Interrupt, + ); } _ => { error!("Isochronous transmission is not supported by host USB passthrough."); @@ -765,21 +814,7 @@ impl UsbDeviceOps for UsbHost { return; } }; - - match submit_host_transfer(host_transfer) { - Ok(()) => {} - Err(Error::NoDevice) => { - packet.lock().unwrap().status = UsbPacketStatus::NoDev; - return; - } - _ => { - packet.lock().unwrap().status = UsbPacketStatus::Stall; - return; - } - }; - - packet.lock().unwrap().is_async = true; - self.clear_succ_requests(); + self.submit_host_transfer(host_transfer, packet); } fn device_id(&self) -> String { -- Gitee From f33fb43193ca618b65037d79dc00383f69f33abd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 28 Jun 2023 22:25:17 +0800 Subject: [PATCH 1155/2187] aio: Move write_zeros_unmap into opcode ... which makes code more readable and reduce memory foot print. Signed-off-by: Keqian Zhu --- devices/src/scsi/bus.rs | 5 ++--- util/src/aio/mod.rs | 17 ++++++++--------- virtio/src/device/block.rs | 7 +++---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 1f95d5499..c4810ba69 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -558,6 +558,8 @@ impl ScsiRequest { direct: locked_dev.config.direct, req_align: locked_dev.req_align, buf_align: locked_dev.buf_align, + discard: false, + write_zeroes: WriteZeroesState::Off, file_fd: locked_dev.disk_image.as_ref().unwrap().as_raw_fd(), opcode: OpCode::Noop, iovec: locked_req.iovec.clone(), @@ -565,9 +567,6 @@ impl ScsiRequest { nbytes: locked_req.cmd.xfer as u64, user_data: 0, iocompletecb: ScsiCompleteCb { req: s_req.clone() }, - discard: false, - write_zeroes: WriteZeroesState::Off, - write_zeroes_unmap: false, }; drop(locked_req); diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index f64f92323..9ce8c3237 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -126,12 +126,15 @@ pub enum OpCode { Fdsync = 3, Discard = 4, WriteZeroes = 5, + WriteZeroesUnmap = 6, } pub struct AioCb { pub direct: bool, pub req_align: u32, pub buf_align: u32, + pub discard: bool, + pub write_zeroes: WriteZeroesState, pub file_fd: RawFd, pub opcode: OpCode, pub iovec: Vec, @@ -139,9 +142,6 @@ pub struct AioCb { pub nbytes: u64, pub user_data: u64, pub iocompletecb: T, - pub discard: bool, - pub write_zeroes: WriteZeroesState, - pub write_zeroes_unmap: bool, } pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; @@ -230,7 +230,7 @@ impl Aio { { cb.opcode = OpCode::WriteZeroes; if cb.write_zeroes == WriteZeroesState::Unmap && cb.discard { - cb.write_zeroes_unmap = true; + cb.opcode = OpCode::WriteZeroesUnmap; } } @@ -250,7 +250,7 @@ impl Aio { } } OpCode::Discard => self.discard_sync(cb), - OpCode::WriteZeroes => self.write_zeroes_sync(cb), + OpCode::WriteZeroes | OpCode::WriteZeroesUnmap => self.write_zeroes_sync(cb), OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), } } @@ -551,7 +551,7 @@ impl Aio { fn write_zeroes_sync(&mut self, cb: AioCb) -> Result<()> { let mut ret; - if cb.write_zeroes_unmap { + if cb.opcode == OpCode::WriteZeroesUnmap { ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); if ret == 0 { return (self.complete_func)(&cb, ret); @@ -713,6 +713,8 @@ mod tests { direct, req_align: align, buf_align: align, + discard: false, + write_zeroes: WriteZeroesState::Off, file_fd, opcode, iovec, @@ -720,9 +722,6 @@ mod tests { nbytes, user_data: 0, iocompletecb: 0, - discard: false, - write_zeroes: WriteZeroesState::Off, - write_zeroes_unmap: false, }; let mut aio = Aio::new( Arc::new(|_: &AioCb, _: i64| -> Result<()> { Ok(()) }), diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 0d993ff6b..3cf18f0a5 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -416,7 +416,7 @@ impl Request { && flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP && iohandler.discard { - aiocb.write_zeroes_unmap = true; + aiocb.opcode = OpCode::WriteZeroesUnmap; } aiocb.offset = (sector as usize) << SECTOR_SHIFT; @@ -611,6 +611,8 @@ impl BlockIoHandler { direct: self.direct, req_align: self.req_align, buf_align: self.buf_align, + discard: self.discard, + write_zeroes: self.write_zeroes, file_fd: disk_img.as_raw_fd(), opcode: OpCode::Noop, iovec: Vec::new(), @@ -618,9 +620,6 @@ impl BlockIoHandler { nbytes: 0, user_data: 0, iocompletecb: aiocompletecb, - discard: self.discard, - write_zeroes: self.write_zeroes, - write_zeroes_unmap: false, }; req_rc.execute(self, aiocb)?; } else { -- Gitee From ed4617f9a3e0edbbe1c5407d5016a5b1ba9af2c8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 28 Jun 2023 20:31:14 +0800 Subject: [PATCH 1156/2187] virtio-gpu: Use gpa_hva_iovec_map to parse backing This common interface will be adapted for memory system change. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 7053da04c..e7b776ddc 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -49,10 +49,10 @@ use util::pixman::{ }; use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, Element, Queue, VirtioDevice, VirtioError, - VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, - VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, ElemIovec, Element, Queue, VirtioDevice, + VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, + VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, @@ -1232,23 +1232,23 @@ impl GpuIoHandler { return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); } - for entry in ents.iter() { - match self.mem_space.get_host_address(GuestAddress(entry.addr)) { - Some(iov_base) => { - let iov_item = Iovec { - iov_base, - iov_len: entry.length as u64, - }; - res.iov.push(iov_item); - } - None => { - error!("Virtio-GPU: Map entry base {:?} failed", entry.addr); - res.iov.clear(); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req); - } + let mut elemiovec = Vec::with_capacity(ents.len()); + for ent in ents.iter() { + elemiovec.push(ElemIovec { + addr: GuestAddress(ent.addr), + len: ent.length, + }); + } + match gpa_hva_iovec_map(&elemiovec, &self.mem_space) { + Ok((_, iov)) => { + res.iov = iov; + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) + } + Err(e) => { + error!("Virtio-GPU: Map entry base failed, {:?}", e); + self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req) } } - self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } fn cmd_resource_detach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { -- Gitee From 2410c58244480f30d84837f0f7cf0d4a0210c050 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 16 May 2023 05:13:37 +0800 Subject: [PATCH 1157/2187] virtio-blk: Drain incomplete IO when deactivate device Add drain_request() in BlockDriverOps. When device is deactivated, stop receiving virtqueue requests and drain incomplete IO. Signed-off-by: Keqian Zhu --- block_backend/src/file.rs | 10 +++++++++- block_backend/src/lib.rs | 2 ++ block_backend/src/raw.rs | 4 ++++ util/src/aio/mod.rs | 14 +++++++++++--- virtio/src/device/block.rs | 6 +++++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index ccad30414..027303370 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -17,7 +17,7 @@ use std::{ os::unix::prelude::{AsRawFd, RawFd}, rc::Rc, sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, Mutex, }, }; @@ -38,6 +38,7 @@ use util::{ pub struct FileDriver { file: File, aio: Rc>>, + incomplete: Arc, delete_evts: Vec, block_prop: BlockProperty, } @@ -46,6 +47,7 @@ impl FileDriver { pub fn new(file: File, aio: Aio, block_prop: BlockProperty) -> Self { Self { file, + incomplete: aio.incomplete_cnt.clone(), aio: Rc::new(RefCell::new(aio)), delete_evts: Vec::new(), block_prop, @@ -128,6 +130,12 @@ impl FileDriver { self.aio.borrow_mut().flush_request() } + pub fn drain_request(&self) { + while self.incomplete.load(Ordering::Acquire) != 0 { + continue; + } + } + pub fn register_io_event( &mut self, broken: Arc, diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index a30fce306..6e75ab03c 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -58,6 +58,8 @@ pub trait BlockDriverOps: Send { fn flush_request(&mut self) -> Result<()>; + fn drain_request(&self); + fn register_io_event( &mut self, device_broken: Arc, diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 8d50a7430..d9ca06120 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -68,6 +68,10 @@ impl BlockDriverOps for RawDriver { self.driver.flush_request() } + fn drain_request(&self) { + self.driver.drain_request(); + } + fn register_io_event( &mut self, broken: Arc, diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 3aee000a3..9e1711cfe 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -17,6 +17,7 @@ mod uring; use std::clone::Clone; use std::io::Write; use std::os::unix::io::RawFd; +use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::{cmp, str::FromStr}; @@ -158,8 +159,10 @@ pub struct Aio { ctx: Option>>, engine: AioEngine, pub fd: EventFd, - pub aio_in_queue: CbList, - pub aio_in_flight: CbList, + aio_in_queue: CbList, + aio_in_flight: CbList, + /// IO in aio_in_queue and aio_in_flight. + pub incomplete_cnt: Arc, max_events: usize, complete_func: Arc>, } @@ -195,6 +198,7 @@ impl Aio { fd, aio_in_queue: List::new(), aio_in_flight: List::new(), + incomplete_cnt: Arc::new(AtomicU64::new(0)), max_events, complete_func: func, }) @@ -292,10 +296,12 @@ impl Aio { -1 }; - (self.complete_func)(&(*node).value, res)?; + let res = (self.complete_func)(&(*node).value, res); self.aio_in_flight.unlink(&(*node)); + self.incomplete_cnt.fetch_sub(1, Ordering::SeqCst); // Construct Box to free mem automatically. drop(Box::from_raw(node)); + res?; } } self.process_list()?; @@ -342,6 +348,7 @@ impl Aio { if is_err { // Fail one request, retry the rest. if let Some(node) = self.aio_in_queue.pop_tail() { + self.incomplete_cnt.fetch_sub(1, Ordering::SeqCst); (self.complete_func)(&(node).value, -1)?; } } else if nr == 0 { @@ -358,6 +365,7 @@ impl Aio { node.value.user_data = (&mut (*node) as *mut CbNode) as u64; self.aio_in_queue.add_head(node); + self.incomplete_cnt.fetch_add(1, Ordering::SeqCst); if self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { self.process_list()?; } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 56a7ecbfd..10fec545b 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1208,9 +1208,13 @@ impl VirtioDevice for Block { } fn deactivate(&mut self) -> Result<()> { + // Stop receiving virtqueue requests and drain incomplete IO. unregister_event_helper(self.blk_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; if let Some(block_backend) = self.block_backend.as_ref() { - block_backend.lock().unwrap().unregister_io_event()?; + let mut block_backend = block_backend.lock().unwrap(); + // Must drain requests before unregister. + block_backend.drain_request(); + block_backend.unregister_io_event()?; } self.update_evts.clear(); self.senders.clear(); -- Gitee From 5a23b8c3ed1a923d81577440c2914a051ee1105a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 29 Jun 2023 02:05:46 +0800 Subject: [PATCH 1158/2187] virtio-blk: unwrap block_backend when handle discard_write_zeroes Signed-off-by: Keqian Zhu --- virtio/src/device/block.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 10fec545b..c9fc31aec 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -412,10 +412,8 @@ impl Request { return iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } - let block_backend = iohandler - .block_backend - .as_ref() - .with_context(|| "No disk image when handle write zeroes")?; + // The block_backend is not None here. + let block_backend = iohandler.block_backend.as_ref().unwrap(); let mut locked_backend = block_backend.lock().unwrap(); let offset = (sector as usize) << SECTOR_SHIFT; let nbytes = (num_sectors as u64) << SECTOR_SHIFT; -- Gitee From 485ee7b66a892652627f2833a98760b14d8e3539 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 29 Jun 2023 09:30:19 +0800 Subject: [PATCH 1159/2187] migration: Move migration_derive into migration package Signed-off-by: Keqian Zhu --- address_space/Cargo.toml | 2 +- cpu/Cargo.toml | 2 +- devices/Cargo.toml | 2 +- machine/Cargo.toml | 2 +- migration/Cargo.toml | 2 +- {migration_derive => migration/migration_derive}/Cargo.toml | 4 ++-- .../migration_derive}/src/attr_parser.rs | 0 .../migration_derive}/src/field_parser.rs | 0 {migration_derive => migration/migration_derive}/src/lib.rs | 0 .../migration_derive}/src/struct_parser.rs | 0 pci/Cargo.toml | 2 +- vhost_user_fs/Cargo.toml | 2 +- virtio/Cargo.toml | 2 +- 13 files changed, 10 insertions(+), 10 deletions(-) rename {migration_derive => migration/migration_derive}/Cargo.toml (81%) rename {migration_derive => migration/migration_derive}/src/attr_parser.rs (100%) rename {migration_derive => migration/migration_derive}/src/field_parser.rs (100%) rename {migration_derive => migration/migration_derive}/src/lib.rs (100%) rename {migration_derive => migration/migration_derive}/src/struct_parser.rs (100%) diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index a3974f31a..b8cfb7540 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -18,7 +18,7 @@ anyhow = "1.0" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } [dev-dependencies] diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 418410138..2a4787c5e 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -17,7 +17,7 @@ vmm-sys-util = "0.11.1" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } [dev-dependencies] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 9e9d612ae..ddfb3bb8f 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -27,7 +27,7 @@ address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } sysbus = { path = "../sysbus" } pci = { path = "../pci" } util = { path = "../util" } diff --git a/machine/Cargo.toml b/machine/Cargo.toml index fa413a0ec..1f9de313b 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -27,7 +27,7 @@ devices = { path = "../devices" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } pci = { path = "../pci" } sysbus = { path = "../sysbus" } util = { path = "../util" } diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 6af23c651..feec2bab7 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -18,4 +18,4 @@ hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } [dev-dependencies] -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "migration_derive" } diff --git a/migration_derive/Cargo.toml b/migration/migration_derive/Cargo.toml similarity index 81% rename from migration_derive/Cargo.toml rename to migration/migration_derive/Cargo.toml index 6fdd65b9b..418b10ecd 100644 --- a/migration_derive/Cargo.toml +++ b/migration/migration_derive/Cargo.toml @@ -11,8 +11,8 @@ quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] -migration = { path = "../migration" } -util = { path = "../util" } +migration = { path = "../../migration" } +util = { path = "../../util" } [lib] name = "migration_derive" diff --git a/migration_derive/src/attr_parser.rs b/migration/migration_derive/src/attr_parser.rs similarity index 100% rename from migration_derive/src/attr_parser.rs rename to migration/migration_derive/src/attr_parser.rs diff --git a/migration_derive/src/field_parser.rs b/migration/migration_derive/src/field_parser.rs similarity index 100% rename from migration_derive/src/field_parser.rs rename to migration/migration_derive/src/field_parser.rs diff --git a/migration_derive/src/lib.rs b/migration/migration_derive/src/lib.rs similarity index 100% rename from migration_derive/src/lib.rs rename to migration/migration_derive/src/lib.rs diff --git a/migration_derive/src/struct_parser.rs b/migration/migration_derive/src/struct_parser.rs similarity index 100% rename from migration_derive/src/struct_parser.rs rename to migration/migration_derive/src/struct_parser.rs diff --git a/pci/Cargo.toml b/pci/Cargo.toml index f2612bd25..9fada9c4b 100644 --- a/pci/Cargo.toml +++ b/pci/Cargo.toml @@ -20,7 +20,7 @@ address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } acpi = { path = "../acpi" } diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index b2bf2b962..e10ec36da 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -19,7 +19,7 @@ address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } pci = { path = "../pci" } diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index d75d218dd..6f86b7f6e 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -20,7 +20,7 @@ address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } -migration_derive = { path = "../migration_derive" } +migration_derive = { path = "../migration/migration_derive" } sysbus = { path = "../sysbus" } util = { path = "../util" } pci = { path = "../pci" } -- Gitee From 63281f5001bac89b226fc01a7a097b9d92d344bf Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Jun 2023 10:25:02 +0800 Subject: [PATCH 1160/2187] block_backend: add raw driver Add block driver ops to provide an block interface, and implemented by the raw driver, which will be used later. Signed-off-by: zhouli57 Signed-off-by: liuxiangdong --- Cargo.lock | 14 +++ block_backend/Cargo.toml | 15 +++ block_backend/src/file.rs | 251 ++++++++++++++++++++++++++++++++++++++ block_backend/src/lib.rs | 64 ++++++++++ block_backend/src/raw.rs | 86 +++++++++++++ devices/Cargo.toml | 1 + util/src/aio/mod.rs | 8 ++ 7 files changed, 439 insertions(+) create mode 100644 block_backend/Cargo.toml create mode 100644 block_backend/src/file.rs create mode 100644 block_backend/src/lib.rs create mode 100644 block_backend/src/raw.rs diff --git a/Cargo.lock b/Cargo.lock index 891e6855f..b63410374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,19 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block_backend" +version = "2.2.0" +dependencies = [ + "anyhow", + "byteorder", + "log", + "machine_manager", + "thiserror", + "util", + "vmm-sys-util", +] + [[package]] name = "boot_loader" version = "2.2.0" @@ -359,6 +372,7 @@ dependencies = [ "address_space", "alsa", "anyhow", + "block_backend", "byteorder", "cairo-rs", "drm-fourcc", diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml new file mode 100644 index 000000000..5e89086cb --- /dev/null +++ b/block_backend/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "block_backend" +version = "2.2.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" + +[dependencies] +thiserror = "1.0" +vmm-sys-util = "0.11.0" +anyhow = "1.0" +log = "0.4" +byteorder = "1.4.3" +machine_manager = { path = "../machine_manager" } +util = { path = "../util" } diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs new file mode 100644 index 000000000..ccad30414 --- /dev/null +++ b/block_backend/src/file.rs @@ -0,0 +1,251 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + cell::RefCell, + fs::File, + io::{Seek, SeekFrom}, + os::unix::prelude::{AsRawFd, RawFd}, + rc::Rc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, +}; + +use anyhow::{Context, Result}; +use log::error; +use vmm_sys_util::epoll::EventSet; + +use crate::{BlockIoErrorCallback, BlockProperty}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::{ + aio::{get_iov_size, Aio, AioCb, AioEngine, Iovec, OpCode}, + loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + }, +}; + +pub struct FileDriver { + file: File, + aio: Rc>>, + delete_evts: Vec, + block_prop: BlockProperty, +} + +impl FileDriver { + pub fn new(file: File, aio: Aio, block_prop: BlockProperty) -> Self { + Self { + file, + aio: Rc::new(RefCell::new(aio)), + delete_evts: Vec::new(), + block_prop, + } + } + + fn package_aiocb( + &self, + opcode: OpCode, + iovec: Vec, + offset: usize, + nbytes: u64, + iocompletecb: T, + ) -> AioCb { + AioCb { + direct: self.block_prop.direct, + req_align: self.block_prop.req_align, + buf_align: self.block_prop.buf_align, + file_fd: self.file.as_raw_fd(), + opcode, + iovec, + offset, + nbytes, + user_data: 0, + iocompletecb, + discard: self.block_prop.discard, + write_zeroes: self.block_prop.write_zeroes, + } + } + + pub fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + let aiocb = self.package_aiocb( + OpCode::Preadv, + iovec.to_vec(), + offset, + get_iov_size(iovec), + completecb, + ); + self.aio.borrow_mut().submit_request(aiocb) + } + + pub fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + let aiocb = self.package_aiocb( + OpCode::Pwritev, + iovec.to_vec(), + offset, + get_iov_size(iovec), + completecb, + ); + self.aio.borrow_mut().submit_request(aiocb) + } + + pub fn write_zeroes( + &mut self, + offset: usize, + nbytes: u64, + completecb: T, + unmap: bool, + ) -> Result<()> { + let opcode = if unmap { + OpCode::WriteZeroesUnmap + } else { + OpCode::WriteZeroes + }; + let aiocb = self.package_aiocb(opcode, Vec::new(), offset, nbytes, completecb); + self.aio.borrow_mut().submit_request(aiocb) + } + + pub fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { + let aiocb = self.package_aiocb(OpCode::Discard, Vec::new(), offset, nbytes, completecb); + self.aio.borrow_mut().submit_request(aiocb) + } + + pub fn datasync(&mut self, completecb: T) -> Result<()> { + let aiocb = self.package_aiocb(OpCode::Fdsync, Vec::new(), 0, 0, completecb); + self.aio.borrow_mut().submit_request(aiocb) + } + + pub fn flush_request(&mut self) -> Result<()> { + self.aio.borrow_mut().flush_request() + } + + pub fn register_io_event( + &mut self, + broken: Arc, + error_cb: BlockIoErrorCallback, + ) -> Result<()> { + let handler = FileIoHandler::new(self.aio.clone(), broken, error_cb); + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper( + notifiers, + self.block_prop.iothread.as_ref(), + &mut self.delete_evts, + ) + } + + pub fn unregister_io_event(&mut self) -> Result<()> { + unregister_event_helper(self.block_prop.iothread.as_ref(), &mut self.delete_evts) + } + + pub fn disk_size(&mut self) -> Result { + let disk_size = self + .file + .seek(SeekFrom::End(0)) + .with_context(|| "Failed to seek the end for file")?; + Ok(disk_size) + } +} + +struct FileIoHandler { + aio: Rc>>, + broken: Arc, + error_cb: BlockIoErrorCallback, +} + +impl FileIoHandler { + pub fn new( + aio: Rc>>, + broken: Arc, + error_cb: BlockIoErrorCallback, + ) -> Self { + Self { + aio, + broken, + error_cb, + } + } + + fn aio_complete_handler(&mut self) -> Result { + let error_cb = self.error_cb.clone(); + self.aio.borrow_mut().handle_complete().map_err(|e| { + error_cb(); + e + }) + } +} + +impl EventNotifierHelper for FileIoHandler { + fn internal_notifiers(handler: Arc>) -> Vec { + let handler_raw = handler.lock().unwrap(); + let mut notifiers = Vec::new(); + + // Register event notifier for aio. + let h_clone = handler.clone(); + let h: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.broken.load(Ordering::SeqCst) { + return None; + } + if let Err(ref e) = h_lock.aio_complete_handler() { + error!("Failed to handle aio {:?}", e); + } + None + }); + let h_clone = handler.clone(); + let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { + let mut h_lock = h_clone.lock().unwrap(); + if h_lock.broken.load(Ordering::SeqCst) { + return None; + } + if h_lock.aio.borrow_mut().get_engine() == AioEngine::Off { + return None; + } + match h_lock.aio_complete_handler() { + Ok(done) => { + if done { + Some(Vec::new()) + } else { + None + } + } + Err(e) => { + error!("Failed to handle aio {:?}", e); + None + } + } + }); + notifiers.push(build_event_notifier( + handler_raw.aio.borrow_mut().fd.as_raw_fd(), + vec![h], + Some(handler_iopoll), + )); + + notifiers + } +} + +fn build_event_notifier( + fd: RawFd, + handlers: Vec>, + handler_poll: Option>, +) -> EventNotifier { + let mut notifier = EventNotifier::new( + NotifierOperation::AddShared, + fd, + None, + EventSet::IN, + handlers, + ); + notifier.handler_poll = handler_poll; + notifier +} diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs new file mode 100644 index 000000000..5d67cdda1 --- /dev/null +++ b/block_backend/src/lib.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod raw; + +mod file; + +use std::sync::{atomic::AtomicBool, Arc}; + +use anyhow::Result; + +use util::aio::{Iovec, WriteZeroesState}; + +/// Callback function which is called when aio handle failed. +pub type BlockIoErrorCallback = Arc; + +#[derive(Debug, Clone)] +pub struct BlockProperty { + pub iothread: Option, + pub direct: bool, + pub req_align: u32, + pub buf_align: u32, + pub discard: bool, + pub write_zeroes: WriteZeroesState, +} + +pub trait BlockDriverOps: Send { + fn disk_size(&mut self) -> Result; + + fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()>; + + fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()>; + + fn datasync(&mut self, completecb: T) -> Result<()>; + + fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()>; + + fn write_zeroes( + &mut self, + offset: usize, + nbytes: u64, + completecb: T, + unmap: bool, + ) -> Result<()>; + + fn flush_request(&mut self) -> Result<()>; + + fn register_io_event( + &mut self, + device_broken: Arc, + error_cb: BlockIoErrorCallback, + ) -> Result<()>; + + fn unregister_io_event(&mut self) -> Result<()>; +} diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs new file mode 100644 index 000000000..8d50a7430 --- /dev/null +++ b/block_backend/src/raw.rs @@ -0,0 +1,86 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::File, + sync::{atomic::AtomicBool, Arc}, +}; + +use anyhow::Result; + +use crate::{file::FileDriver, BlockDriverOps, BlockIoErrorCallback, BlockProperty}; +use util::aio::{Aio, Iovec}; + +pub struct RawDriver { + driver: FileDriver, +} + +// SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. +// We use Arc>> to allow used in multi-threading. +unsafe impl Send for RawDriver {} +unsafe impl Sync for RawDriver {} + +impl RawDriver { + pub fn new(file: File, aio: Aio, prop: BlockProperty) -> Self { + Self { + driver: FileDriver::new(file, aio, prop), + } + } +} + +impl BlockDriverOps for RawDriver { + fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + self.driver.read_vectored(iovec, offset, completecb) + } + + fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + self.driver.write_vectored(iovec, offset, completecb) + } + + fn write_zeroes( + &mut self, + offset: usize, + nbytes: u64, + completecb: T, + unmap: bool, + ) -> Result<()> { + self.driver.write_zeroes(offset, nbytes, completecb, unmap) + } + + fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { + self.driver.discard(offset, nbytes, completecb) + } + + fn datasync(&mut self, completecb: T) -> Result<()> { + self.driver.datasync(completecb) + } + + fn flush_request(&mut self) -> Result<()> { + self.driver.flush_request() + } + + fn register_io_event( + &mut self, + broken: Arc, + error_cb: BlockIoErrorCallback, + ) -> Result<()> { + self.driver.register_io_event(broken, error_cb) + } + + fn unregister_io_event(&mut self) -> Result<()> { + self.driver.unregister_io_event() + } + + fn disk_size(&mut self) -> Result { + self.driver.disk_size() + } +} diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 3502da1d1..9e9d612ae 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -32,6 +32,7 @@ sysbus = { path = "../sysbus" } pci = { path = "../pci" } util = { path = "../util" } acpi = { path = "../acpi" } +block_backend = { path = "../block_backend"} [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 9ce8c3237..3aee000a3 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -104,6 +104,14 @@ impl Iovec { } } +pub fn get_iov_size(iovecs: &[Iovec]) -> u64 { + let mut sum = 0; + for iov in iovecs { + sum += iov.iov_len; + } + sum +} + /// The trait for Asynchronous IO operation. trait AioContext { /// Submit IO requests to the OS, the nr submitted is returned. -- Gitee From 4f9d2fd437d7078393d3dd1797a724f104909bcd Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Jun 2023 10:38:38 +0800 Subject: [PATCH 1161/2187] block_backend: use raw driver for block device Use raw driver as the backend for virtio block, scsi device and usb-storage. Signed-off-by: zhouli57 Signed-off-by: liuxiangdong --- Cargo.lock | 1 + block_backend/src/lib.rs | 18 +- devices/src/scsi/bus.rs | 50 ++--- devices/src/scsi/disk.rs | 35 ++-- devices/src/usb/storage.rs | 11 +- machine/src/lib.rs | 4 +- virtio/Cargo.toml | 1 + virtio/src/device/block.rs | 328 +++++++++++++++----------------- virtio/src/device/scsi_cntlr.rs | 90 ++++----- 9 files changed, 257 insertions(+), 281 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b63410374..acaa77342 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2049,6 +2049,7 @@ dependencies = [ "acpi", "address_space", "anyhow", + "block_backend", "byteorder", "devices", "hypervisor", diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 5d67cdda1..a30fce306 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -14,11 +14,15 @@ pub mod raw; mod file; -use std::sync::{atomic::AtomicBool, Arc}; +use std::{ + fs::File, + sync::{atomic::AtomicBool, Arc, Mutex}, +}; use anyhow::Result; -use util::aio::{Iovec, WriteZeroesState}; +use raw::RawDriver; +use util::aio::{Aio, Iovec, WriteZeroesState}; /// Callback function which is called when aio handle failed. pub type BlockIoErrorCallback = Arc; @@ -62,3 +66,13 @@ pub trait BlockDriverOps: Send { fn unregister_io_event(&mut self) -> Result<()>; } + +pub fn create_block_backend( + file: File, + aio: Aio, + prop: BlockProperty, +) -> Result>>> { + // NOTE: we only support file backend for raw format now. + let raw_file = RawDriver::new(file, aio, prop); + Ok(Arc::new(Mutex::new(raw_file))) +} diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index c4810ba69..c1daca99d 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -13,7 +13,6 @@ use std::cmp; use std::collections::HashMap; use std::io::Write; -use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -25,7 +24,7 @@ use crate::ScsiDisk::{ SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, SECTOR_SHIFT, }; -use util::aio::{AioCb, Iovec, OpCode, WriteZeroesState}; +use util::aio::{AioCb, Iovec}; use util::AsAny; /// Scsi Operation code. @@ -472,7 +471,7 @@ pub fn aio_complete_cb(aiocb: &AioCb, ret: i64) -> Result<()> { Ok(()) } -pub trait ScsiRequestOps: AsAny { +pub trait ScsiRequestOps: Send + Sync + AsAny { // Will be called in the end of this scsi instruction execution. fn scsi_request_complete_cb(&mut self, status: u8, scsisense: Option) -> Result<()>; } @@ -545,56 +544,47 @@ impl ScsiRequest { let op = self.cmd.op; let dev = self.dev.clone(); let locked_dev = dev.lock().unwrap(); - let mut aio = locked_dev.aio.as_ref().unwrap().lock().unwrap(); + // SAFETY: the block_backend is assigned after device realized. + let block_backend = locked_dev.block_backend.as_ref().unwrap(); + let mut locked_backend = block_backend.lock().unwrap(); let s_req = Arc::new(Mutex::new(self)); - let offset = match locked_dev.scsi_type { + let scsicompletecb = ScsiCompleteCb { req: s_req.clone() }; + let offset_bits = match locked_dev.scsi_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, }; let locked_req = s_req.lock().unwrap(); - - let mut aiocb = AioCb { - direct: locked_dev.config.direct, - req_align: locked_dev.req_align, - buf_align: locked_dev.buf_align, - discard: false, - write_zeroes: WriteZeroesState::Off, - file_fd: locked_dev.disk_image.as_ref().unwrap().as_raw_fd(), - opcode: OpCode::Noop, - iovec: locked_req.iovec.clone(), - offset: (locked_req.cmd.lba << offset) as usize, - nbytes: locked_req.cmd.xfer as u64, - user_data: 0, - iocompletecb: ScsiCompleteCb { req: s_req.clone() }, - }; + let iovecs = locked_req.iovec.clone(); + let offset = (locked_req.cmd.lba << offset_bits) as usize; drop(locked_req); if op == SYNCHRONIZE_CACHE { - aiocb.opcode = OpCode::Fdsync; - aio.submit_request(aiocb) + locked_backend + .datasync(scsicompletecb) .with_context(|| "Failed to process scsi request for flushing")?; - aio.flush_request()?; + locked_backend.flush_request()?; + return Ok(s_req); } match mode { ScsiXferMode::ScsiXferFromDev => { - aiocb.opcode = OpCode::Preadv; - aio.submit_request(aiocb) + locked_backend + .read_vectored(&iovecs, offset, scsicompletecb) .with_context(|| "Failed to process scsi request for reading")?; } ScsiXferMode::ScsiXferToDev => { - aiocb.opcode = OpCode::Pwritev; - aio.submit_request(aiocb) - .with_context(|| "Failed to process block request for writing")?; + locked_backend + .write_vectored(&iovecs, offset, scsicompletecb) + .with_context(|| "Failed to process scsi request for writing")?; } _ => { info!("xfer none"); } } - aio.flush_request()?; + locked_backend.flush_request()?; Ok(s_req) } @@ -634,7 +624,7 @@ impl ScsiRequest { } TEST_UNIT_READY => { let dev_lock = self.dev.lock().unwrap(); - if dev_lock.disk_image.is_none() { + if dev_lock.block_backend.is_none() { Err(anyhow!("No scsi backend!")) } else { Ok(Vec::new()) diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index ac55e1f87..f96a66ab0 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -11,15 +11,14 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::fs::File; -use std::io::{Seek, SeekFrom}; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; -use crate::ScsiBus::{ScsiBus, ScsiCompleteCb}; +use crate::ScsiBus::{aio_complete_cb, ScsiBus, ScsiCompleteCb}; +use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; -use util::aio::Aio; +use util::aio::{Aio, WriteZeroesState}; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -90,8 +89,8 @@ pub struct ScsiDevice { pub config: ScsiDevConfig, /// State of the scsi device. pub state: ScsiDevState, - /// Image file opened. - pub disk_image: Option>, + /// Block backend opened by scsi device. + pub block_backend: Option>>>, /// The align requirement of request(offset/len). pub req_align: u32, /// The align requirement of buffer(iova_base). @@ -123,7 +122,7 @@ impl ScsiDevice { ScsiDevice { config, state: ScsiDevState::new(), - disk_image: None, + block_backend: None, req_align: 1, buf_align: 1, disk_sectors: 0, @@ -135,7 +134,7 @@ impl ScsiDevice { } } - pub fn realize(&mut self) -> Result<()> { + pub fn realize(&mut self, iothread: Option) -> Result<()> { match self.scsi_type { SCSI_TYPE_DISK => { self.block_size = SCSI_DISK_DEFAULT_BLOCK_SIZE; @@ -156,16 +155,24 @@ impl ScsiDevice { let drive_files = self.drive_files.lock().unwrap(); // File path can not be empty string. And it has also been checked in CmdParser::parse. - let mut file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; - let disk_size = file - .seek(SeekFrom::End(0)) - .with_context(|| "Failed to seek the end for scsi device")?; - self.disk_image = Some(Arc::new(file)); + let file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; self.req_align = alignments.0; self.buf_align = alignments.1; + let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type)?; + let conf = BlockProperty { + iothread, + direct: self.config.direct, + req_align: self.req_align, + buf_align: self.buf_align, + discard: false, + write_zeroes: WriteZeroesState::Off, + }; + let backend = create_block_backend(file, aio, conf)?; + let disk_size = backend.lock().unwrap().disk_size()?; + self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; Ok(()) diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 97167d200..dccb9be2c 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -21,7 +21,6 @@ use log::{debug, error, info, warn}; use once_cell::sync::Lazy; use machine_manager::config::{DriveFile, UsbStorageConfig}; -use util::aio::{Aio, AioEngine}; use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, @@ -32,8 +31,8 @@ use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; use crate::{ ScsiBus::{ - aio_complete_cb, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, - EMULATE_SCSI_OPS, GOOD, SCSI_CMD_BUF_SIZE, + ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD, + SCSI_CMD_BUF_SIZE, }, ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, }; @@ -520,11 +519,9 @@ impl UsbDeviceOps for UsbStorage { self.usb_device .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; - let aio = Aio::new(Arc::new(aio_complete_cb), AioEngine::Off) - .with_context(|| format!("USB-storage {}: aio creation error!", self.id))?; + // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not supported. let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); - locked_scsi_dev.aio = Some(Arc::new(Mutex::new(aio))); - locked_scsi_dev.realize()?; + locked_scsi_dev.realize(None)?; drop(locked_scsi_dev); self.scsi_bus .lock() diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 029a2ec50..5b71970c0 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -845,14 +845,14 @@ pub trait MachineOps { { bail!("Wrong! Two scsi devices have the same scsi-id and lun"); } + let iothread = cntlr.config.iothread.clone(); + device.lock().unwrap().realize(iothread)?; bus.lock() .unwrap() .devices .insert((device_cfg.target, device_cfg.lun), device.clone()); device.lock().unwrap().parent_bus = Arc::downgrade(bus); - device.lock().unwrap().realize()?; - if let Some(bootindex) = device_cfg.boot_index { // Eg: OpenFirmware device path(virtio-scsi disk): // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3 diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index cd15d7d66..d75d218dd 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -26,6 +26,7 @@ util = { path = "../util" } pci = { path = "../pci" } acpi = { path = "../acpi" } devices = {path = "../devices"} +block_backend = {path = "../block_backend"} [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 3cf18f0a5..56a7ecbfd 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -12,8 +12,7 @@ use std::cmp; use std::collections::HashMap; -use std::fs::File; -use std::io::{Seek, SeekFrom, Write}; +use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -22,6 +21,11 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::time::Instant; +use anyhow::{anyhow, bail, Context, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::{error, warn}; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + use crate::VirtioError; use crate::{ gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, @@ -34,9 +38,7 @@ use crate::{ VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, bail, Context, Result}; -use byteorder::{ByteOrder, LittleEndian}; -use log::{error, warn}; +use block_backend::{create_block_backend, BlockDriverOps, BlockIoErrorCallback, BlockProperty}; use machine_manager::config::{BlkDevConfig, ConfigCheck, DriveFile, VmConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use migration::{ @@ -45,7 +47,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::aio::{ - iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, AioEngine, Iovec, OpCode, + iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, Iovec, OpCode, WriteZeroesState, }; use util::byte_code::ByteCode; @@ -55,7 +57,7 @@ use util::loop_context::{ }; use util::num_ops::read_u32; use util::offset_of; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + /// Number of virtqueues. const QUEUE_NUM_BLK: usize = 1; /// Used to compute the number of sectors. @@ -76,13 +78,12 @@ const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; const MAX_REQUEST_SECTORS: u32 = u32::MAX >> SECTOR_SHIFT; type SenderConfig = ( - Option>, + Option>>>, u32, u32, u64, Option, bool, - AioEngine, ); fn get_serial_num_config(serial_num: &str) -> Vec { @@ -123,7 +124,7 @@ pub struct AioCompleteCb { queue: Arc>, mem_space: Arc, /// The head of merged Request list. - req: Rc, + req: Arc, interrupt_cb: Arc, driver_features: u64, } @@ -132,7 +133,7 @@ impl AioCompleteCb { fn new( queue: Arc>, mem_space: Arc, - req: Rc, + req: Arc, interrupt_cb: Arc, driver_features: u64, ) -> Self { @@ -286,50 +287,49 @@ impl Request { fn execute( &self, iohandler: &mut BlockIoHandler, - mut aiocb: AioCb, + block_backend: Arc>>, + aiocompletecb: AioCompleteCb, ) -> Result<()> { let mut req = Some(self); + let mut iovecs = Vec::new(); while let Some(req_raw) = req { for iov in req_raw.iovec.iter() { let iovec = Iovec { iov_base: iov.iov_base, iov_len: iov.iov_len, }; - aiocb.iovec.push(iovec); - // Note: total len of each req is no more than 1<<32, - // and reqs count is no more than 1024. - aiocb.nbytes += iov.iov_len; + iovecs.push(iovec); } req = req_raw.next.as_ref().as_ref(); } - + let offset = (aiocompletecb.req.out_header.sector << SECTOR_SHIFT) as usize; let request_type = self.out_header.request_type; if MigrationManager::is_active() && (request_type == VIRTIO_BLK_T_IN || request_type == VIRTIO_BLK_T_GET_ID) { // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. - for iov in aiocb.iovec.iter() { + for iov in iovecs.iter() { // Mark vmm dirty page manually if live migration is active. MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); } } - let aio = &mut iohandler.aio; let serial_num = &iohandler.serial_num; + let mut locked_backend = block_backend.lock().unwrap(); match request_type { VIRTIO_BLK_T_IN => { - aiocb.opcode = OpCode::Preadv; - aio.submit_request(aiocb) + locked_backend + .read_vectored(&iovecs, offset, aiocompletecb) .with_context(|| "Failed to process block request for reading")?; } VIRTIO_BLK_T_OUT => { - aiocb.opcode = OpCode::Pwritev; - aio.submit_request(aiocb) + locked_backend + .write_vectored(&iovecs, offset, aiocompletecb) .with_context(|| "Failed to process block request for writing")?; } VIRTIO_BLK_T_FLUSH => { - aiocb.opcode = OpCode::Fdsync; - aio.submit_request(aiocb) + locked_backend + .datasync(aiocompletecb) .with_context(|| "Failed to process block request for flushing")?; } VIRTIO_BLK_T_GET_ID => { @@ -342,23 +342,27 @@ impl Request { }, |_| VIRTIO_BLK_S_OK, ); - aiocb.iocompletecb.complete_request(status)?; + aiocompletecb.complete_request(status)?; } VIRTIO_BLK_T_DISCARD => { if !iohandler.discard { error!("Device does not support discard"); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + return aiocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } - aiocb.opcode = OpCode::Discard; - self.handle_discard_write_zeroes_req(iohandler, aiocb)?; + drop(locked_backend); + self.handle_discard_write_zeroes_req(iohandler, aiocompletecb, OpCode::Discard)?; } VIRTIO_BLK_T_WRITE_ZEROES => { if iohandler.write_zeroes == WriteZeroesState::Off { error!("Device does not support write-zeroes"); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + return aiocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } - aiocb.opcode = OpCode::WriteZeroes; - self.handle_discard_write_zeroes_req(iohandler, aiocb)?; + drop(locked_backend); + self.handle_discard_write_zeroes_req( + iohandler, + aiocompletecb, + OpCode::WriteZeroes, + )?; } // The illegal request type has been handled in method new(). _ => {} @@ -369,13 +373,14 @@ impl Request { fn handle_discard_write_zeroes_req( &self, iohandler: &mut BlockIoHandler, - mut aiocb: AioCb, + iocompletecb: AioCompleteCb, + opcode: OpCode, ) -> Result<()> { let size = size_of::() as u64; // Just support one segment per request. if self.data_len > size { error!("More than one discard or write-zeroes segment is not supported"); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + return iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } // Get and check the discard segment. @@ -399,32 +404,36 @@ impl Request { "Invalid discard or write zeroes request, sector offset {}, num_sectors {}", sector, num_sectors ); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_IOERR); + return iocompletecb.complete_request(VIRTIO_BLK_S_IOERR); } let flags = LittleEndian::read_u32(segment.flags.as_bytes()); if flags & !VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP != 0 { error!("Invalid unmap flags 0x{:x}", flags); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + return iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } - if aiocb.opcode == OpCode::Discard { + let block_backend = iohandler + .block_backend + .as_ref() + .with_context(|| "No disk image when handle write zeroes")?; + let mut locked_backend = block_backend.lock().unwrap(); + let offset = (sector as usize) << SECTOR_SHIFT; + let nbytes = (num_sectors as u64) << SECTOR_SHIFT; + if opcode == OpCode::Discard { if flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP { error!("Discard request must not set unmap flags"); - return aiocb.iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); + return iocompletecb.complete_request(VIRTIO_BLK_S_UNSUPP); } - } else if aiocb.opcode == OpCode::WriteZeroes - && flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP - && iohandler.discard - { - aiocb.opcode = OpCode::WriteZeroesUnmap; + locked_backend + .discard(offset, nbytes, iocompletecb) + .with_context(|| "Failed to process block request for discard")?; + } else if opcode == OpCode::WriteZeroes { + let unmap = flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP && iohandler.discard; + locked_backend + .write_zeroes(offset, nbytes, iocompletecb, unmap) + .with_context(|| "Failed to process block request for write-zeroes")?; } - - aiocb.offset = (sector as usize) << SECTOR_SHIFT; - aiocb.nbytes = (num_sectors as u64) << SECTOR_SHIFT; - iohandler - .aio - .submit_request(aiocb) - .with_context(|| "Failed to process block request for discard or write-zeroes") + Ok(()) } fn io_range_valid(&self, disk_sectors: u64) -> bool { @@ -465,8 +474,8 @@ struct BlockIoHandler { queue_evt: Arc, /// The address space to which the block device belongs. mem_space: Arc, - /// The image file opened by the block device. - disk_image: Option>, + /// The block backend opened by the block device. + block_backend: Option>>>, /// The align requirement of request(offset/len). pub req_align: u32, /// The align requirement of buffer(iova_base). @@ -477,8 +486,6 @@ struct BlockIoHandler { serial_num: Option, /// If use direct access io. direct: bool, - /// Aio context. - aio: Box>, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, /// The receiving half of Rust's channel to receive the image file. @@ -575,7 +582,7 @@ impl BlockIoHandler { let aiocompletecb = AioCompleteCb::new( self.queue.clone(), self.mem_space.clone(), - Rc::new(req), + Arc::new(req), self.interrupt_cb.clone(), self.driver_features, ); @@ -598,7 +605,7 @@ impl BlockIoHandler { let merge_req_queue = self.merge_req_queue(req_queue); for req in merge_req_queue.into_iter() { - let req_rc = Rc::new(req); + let req_rc = Arc::new(req); let aiocompletecb = AioCompleteCb::new( self.queue.clone(), self.mem_space.clone(), @@ -606,29 +613,16 @@ impl BlockIoHandler { self.interrupt_cb.clone(), self.driver_features, ); - if let Some(disk_img) = self.disk_image.as_ref() { - let aiocb = AioCb { - direct: self.direct, - req_align: self.req_align, - buf_align: self.buf_align, - discard: self.discard, - write_zeroes: self.write_zeroes, - file_fd: disk_img.as_raw_fd(), - opcode: OpCode::Noop, - iovec: Vec::new(), - offset: (req_rc.out_header.sector << SECTOR_SHIFT) as usize, - nbytes: 0, - user_data: 0, - iocompletecb: aiocompletecb, - }; - req_rc.execute(self, aiocb)?; + if let Some(block_backend) = self.block_backend.as_ref() { + req_rc.execute(self, block_backend.clone(), aiocompletecb)?; } else { - warn!("Failed to execute block request, disk_img not specified"); + warn!("Failed to execute block request, block_backend not specified"); aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR)?; } } - self.aio.flush_request()?; - + if let Some(block_backend) = self.block_backend.as_ref() { + block_backend.lock().unwrap().flush_request()?; + } Ok(done) } @@ -713,58 +707,27 @@ impl BlockIoHandler { complete_cb.complete_request(status) } - fn aio_complete_handler(&mut self) -> Result { - self.aio.handle_complete().map_err(|e| { - report_virtio_error( - self.interrupt_cb.clone(), - self.driver_features, - &self.device_broken, - ); - e - }) - } - fn update_evt_handler(&mut self) { - let aio_engine; match self.receiver.recv() { - Ok((image, req_align, buf_align, disk_sectors, serial_num, direct, aio)) => { + Ok((image, req_align, buf_align, disk_sectors, serial_num, direct)) => { self.disk_sectors = disk_sectors; - self.disk_image = image; + self.block_backend = image; self.req_align = req_align; self.buf_align = buf_align; self.serial_num = serial_num; self.direct = direct; - aio_engine = aio; } Err(e) => { error!("Failed to receive config in updating handler {:?}", e); self.disk_sectors = 0; - self.disk_image = None; + self.block_backend = None; self.req_align = 1; self.buf_align = 1; self.serial_num = None; self.direct = true; - aio_engine = AioEngine::Native; } }; - if self.aio.get_engine() != aio_engine { - match Aio::new(Arc::new(Self::complete_func), aio_engine) { - Ok(aio) => { - self.aio = Box::new(aio); - } - Err(e) => { - error!("{:?}", e); - report_virtio_error( - self.interrupt_cb.clone(), - self.driver_features, - &self.device_broken, - ); - return; - } - } - } - if let Err(e) = (self.interrupt_cb)(&VirtioInterruptType::Config, None, false) { error!( "{:?}. {:?}", @@ -882,48 +845,6 @@ impl EventNotifierHelper for BlockIoHandler { notifiers.push(build_event_notifier(lb.as_raw_fd(), vec![h], None)); } - // Register event notifier for aio. - let h_clone = handler.clone(); - let h: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - let mut h_lock = h_clone.lock().unwrap(); - if h_lock.device_broken.load(Ordering::SeqCst) { - return None; - } - if let Err(ref e) = h_lock.aio_complete_handler() { - error!("Failed to handle aio {:?}", e); - } - None - }); - let h_clone = handler.clone(); - let handler_iopoll: Box = Box::new(move |_, _fd: RawFd| { - let mut h_lock = h_clone.lock().unwrap(); - if h_lock.device_broken.load(Ordering::SeqCst) { - return None; - } - if h_lock.aio.get_engine() == AioEngine::Off { - return None; - } - match h_lock.aio_complete_handler() { - Ok(done) => { - if done { - Some(Vec::new()) - } else { - None - } - } - Err(e) => { - error!("Failed to handle aio {:?}", e); - None - } - } - }); - notifiers.push(build_event_notifier( - handler_raw.aio.fd.as_raw_fd(), - vec![h], - Some(handler_iopoll), - )); - notifiers } } @@ -1002,8 +923,8 @@ pub struct BlockState { pub struct Block { /// Configuration of the block device. blk_cfg: BlkDevConfig, - /// Image file opened. - disk_image: Option>, + /// BLock backend opened by the block device. + block_backend: Option>>>, /// The align requirement of request(offset/len). pub req_align: u32, /// The align requirement of buffer(iova_base). @@ -1033,7 +954,7 @@ impl Block { ) -> Block { Self { blk_cfg, - disk_image: None, + block_backend: None, req_align: 1, buf_align: 1, disk_sectors: 0, @@ -1081,6 +1002,14 @@ impl Block { offset_of!(VirtioBlkConfig, max_discard_sectors) as u64 } } + + fn gen_error_cb(&self, interrupt_cb: Arc) -> BlockIoErrorCallback { + let cloned_features = self.state.driver_features; + let clone_broken = self.broken.clone(); + Arc::new(move || { + report_virtio_error(interrupt_cb.clone(), cloned_features, &clone_broken); + }) + } } impl VirtioDevice for Block { @@ -1111,22 +1040,30 @@ impl VirtioDevice for Block { self.state.config_space.num_queues = self.blk_cfg.queues; } - self.disk_image = None; + self.block_backend = None; self.disk_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; self.req_align = 1; self.buf_align = 1; if !self.blk_cfg.path_on_host.is_empty() { let drive_files = self.drive_files.lock().unwrap(); - let mut file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; + let file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; let alignments = VmConfig::fetch_drive_align(&drive_files, &self.blk_cfg.path_on_host)?; - let disk_size = file - .seek(SeekFrom::End(0)) - .with_context(|| "Failed to seek the end for block")?; - - self.disk_image = Some(Arc::new(file)); - self.disk_sectors = disk_size >> SECTOR_SHIFT; self.req_align = alignments.0; self.buf_align = alignments.1; + + let aio = Aio::new(Arc::new(BlockIoHandler::complete_func), self.blk_cfg.aio)?; + let conf = BlockProperty { + iothread: self.blk_cfg.iothread.clone(), + direct: self.blk_cfg.direct, + req_align: self.req_align, + buf_align: self.buf_align, + discard: self.blk_cfg.discard, + write_zeroes: self.blk_cfg.write_zeroes, + }; + let backend = create_block_backend(file, aio, conf)?; + let disk_size = backend.lock().unwrap().disk_size()?; + self.block_backend = Some(backend); + self.disk_sectors = disk_size >> SECTOR_SHIFT; } self.state.config_space.capacity = self.disk_sectors; @@ -1218,22 +1155,17 @@ impl VirtioDevice for Block { } let (sender, receiver) = channel(); let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); - let aio = Box::new(Aio::new( - Arc::new(BlockIoHandler::complete_func), - self.blk_cfg.aio, - )?); let driver_features = self.state.driver_features; let handler = BlockIoHandler { queue: queue.clone(), queue_evt: queue_evts[index].clone(), mem_space: mem_space.clone(), - disk_image: self.disk_image.clone(), + block_backend: self.block_backend.clone(), req_align: self.req_align, buf_align: self.buf_align, disk_sectors: self.disk_sectors, direct: self.blk_cfg.direct, serial_num: self.blk_cfg.serial_num.clone(), - aio, driver_features, receiver, update_evt: update_evt.clone(), @@ -1257,6 +1189,19 @@ impl VirtioDevice for Block { self.update_evts.push(update_evt); self.senders.push(sender); } + + if let Some(block_backend) = self.block_backend.as_ref() { + let err_cb = self.gen_error_cb(interrupt_cb.clone()); + block_backend + .lock() + .unwrap() + .register_io_event(self.broken.clone(), err_cb)?; + } else { + warn!( + "No disk image when block device {} activate", + self.blk_cfg.id + ); + } self.broken.store(false, Ordering::SeqCst); Ok(()) @@ -1264,12 +1209,16 @@ impl VirtioDevice for Block { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(self.blk_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + if let Some(block_backend) = self.block_backend.as_ref() { + block_backend.lock().unwrap().unregister_io_event()?; + } self.update_evts.clear(); self.senders.clear(); Ok(()) } fn update_config(&mut self, dev_config: Option>) -> Result<()> { + let is_plug = dev_config.is_some(); if let Some(conf) = dev_config { self.blk_cfg = conf .as_any() @@ -1282,18 +1231,53 @@ impl VirtioDevice for Block { self.blk_cfg = Default::default(); } + if !is_plug { + // If it is an unplug operation, the block backend is set to none. Unregister aio before it. + if let Some(block_backend) = self.block_backend.as_ref() { + block_backend.lock().unwrap().unregister_io_event()?; + } else { + bail!( + "No block backend when block device {} unplug", + self.blk_cfg.id + ); + } + } + self.realize()?; + if is_plug { + // Block backend is set after device realized. + if let Some(cb) = self.interrupt_cb.as_ref() { + // NOTE: interrupter_cb may be is none for replaceable device. + let err_cb = self.gen_error_cb(cb.clone()); + self.block_backend + .as_ref() + .with_context(|| { + format!( + "No block backend when block device {} plug", + self.blk_cfg.id + ) + })? + .lock() + .unwrap() + .register_io_event(self.broken.clone(), err_cb)?; + } else { + warn!( + "No interrupter cb, may be device {} is not activated", + self.blk_cfg.id + ); + } + } + for sender in &self.senders { sender .send(( - self.disk_image.clone(), + self.block_backend.clone(), self.req_align, self.buf_align, self.disk_sectors, self.blk_cfg.serial_num.clone(), self.blk_cfg.direct, - self.blk_cfg.aio, )) .with_context(|| VirtioError::ChannelSend("image fd".to_string()))?; } @@ -1356,7 +1340,7 @@ mod tests { fn default() -> Self { Block { blk_cfg: Default::default(), - disk_image: None, + block_backend: None, req_align: 1, buf_align: 1, disk_sectors: 0, @@ -1406,7 +1390,7 @@ mod tests { assert_eq!(block.state.device_features, 0); assert_eq!(block.state.driver_features, 0); assert_eq!(block.state.config_space.as_bytes().len(), CONFIG_SPACE_SIZE); - assert!(block.disk_image.is_none()); + assert!(block.block_backend.is_none()); assert!(block.interrupt_cb.is_none()); assert!(block.senders.is_empty()); diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index c159db874..9385dfe44 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -19,6 +19,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, error, info, warn}; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, @@ -26,23 +28,22 @@ use crate::{ VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; +use block_backend::BlockIoErrorCallback; use devices::ScsiBus::{ - aio_complete_cb, ScsiBus, ScsiCompleteCb, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, - CHECK_CONDITION, EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, + ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, + EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, }; -use log::{debug, error, info, warn}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, event_loop::EventLoop, }; -use util::aio::{Aio, Iovec}; +use util::aio::Iovec; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::num_ops::read_u32; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; /// Virtio Scsi Controller has 1 ctrl queue, 1 event queue and at least 1 cmd queue. const SCSI_CTRL_QUEUE_NUM: usize = 1; @@ -140,6 +141,14 @@ impl ScsiCntlr { broken: Arc::new(AtomicBool::new(false)), } } + + fn gen_error_cb(&self, interrupt_cb: Arc) -> BlockIoErrorCallback { + let cloned_features = self.state.driver_features; + let clone_broken = self.broken.clone(); + Arc::new(move || { + report_virtio_error(interrupt_cb.clone(), cloned_features, &clone_broken); + }) + } } impl VirtioDevice for ScsiCntlr { @@ -307,9 +316,6 @@ impl VirtioDevice for ScsiCntlr { let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(cmd_handler))); - if notifiers.is_empty() { - bail!("Error in creating scsi device aio!"); - } register_event_helper( notifiers, @@ -319,11 +325,32 @@ impl VirtioDevice for ScsiCntlr { } self.broken.store(false, Ordering::SeqCst); + // Register event notifier for device aio. + let bus = self.bus.as_ref().unwrap(); + let locked_bus = bus.lock().unwrap(); + for device in locked_bus.devices.values() { + let locked_device = device.lock().unwrap(); + let err_cb = self.gen_error_cb(interrupt_cb.clone()); + // SAFETY: the disk_image is assigned after device realized. + let disk_image = locked_device.block_backend.as_ref().unwrap(); + let mut locked_backend = disk_image.lock().unwrap(); + locked_backend.register_io_event(self.broken.clone(), err_cb)?; + } Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(self.config.iothread.as_ref(), &mut self.deactivate_evts) + unregister_event_helper(self.config.iothread.as_ref(), &mut self.deactivate_evts)?; + let bus = self.bus.as_ref().unwrap(); + let locked_bus = bus.lock().unwrap(); + for device in locked_bus.devices.values() { + let locked_dev = device.lock().unwrap(); + // SAFETY: the disk_image is assigned after device realized. + let disk_image = locked_dev.block_backend.as_ref().unwrap(); + let mut locked_backend = disk_image.lock().unwrap(); + locked_backend.unregister_io_event()?; + } + Ok(()) } } @@ -803,56 +830,11 @@ impl EventNotifierHelper for ScsiCmdQueueHandler { }); notifiers.push(build_event_notifier(h_locked.queue_evt.as_raw_fd(), h)); - // Register event notifier for device aio. - let locked_bus = h_locked.scsibus.lock().unwrap(); - for device in locked_bus.devices.values() { - let mut locked_device = device.lock().unwrap(); - - let aio = if let Ok(engine_aio) = - Aio::new(Arc::new(aio_complete_cb), locked_device.config.aio_type) - { - engine_aio - } else { - return Vec::new(); - }; - let dev_aio = Arc::new(Mutex::new(aio)); - let dev_aio_h = dev_aio.clone(); - locked_device.aio = Some(dev_aio.clone()); - - let h_clone = handler.clone(); - let h: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - let mut h_lock = h_clone.lock().unwrap(); - if h_lock.device_broken.load(Ordering::SeqCst) { - return None; - } - if let Err(ref e) = h_lock.aio_complete_handler(&dev_aio_h) { - error!("Failed to handle aio {:?}", e); - } - None - }); - notifiers.push(build_event_notifier( - (*dev_aio).lock().unwrap().fd.as_raw_fd(), - h, - )); - } - notifiers } } impl ScsiCmdQueueHandler { - fn aio_complete_handler(&mut self, aio: &Arc>>) -> Result { - aio.lock().unwrap().handle_complete().map_err(|e| { - report_virtio_error( - self.interrupt_cb.clone(), - self.driver_features, - &self.device_broken, - ); - e - }) - } - fn handle_cmd(&mut self) -> Result<()> { let result = self.handle_cmd_queue_requests(); if result.is_err() { -- Gitee From 9307c8183f2f03d023538091e4f13bf761213762 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 29 Jun 2023 19:33:26 +0800 Subject: [PATCH 1162/2187] USB: bugfix the usb device hot unplug failed After a USB device is hot plug, the device is hot unplug immediately. At this time, the device is not mounted to the slot by guest. The hot plug judgment is incorrect, so device hot unplug failed. Signed-off-by: Mingwang Li --- devices/src/usb/xhci/xhci_controller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index dd77e8c4a..b365fae77 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1003,7 +1003,7 @@ impl XhciDevice { pub fn detach_slot(&mut self, slot_id: u32) -> Result<()> { if slot_id < 1 || slot_id > self.slots.len() as u32 { - bail!("Invalid slot id {} while detaching slot", slot_id); + return Ok(()); } for i in 1..=self.slots[(slot_id - 1) as usize].endpoints.len() as u32 { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(i - 1) as usize]; -- Gitee From ca427ee93d2d500d90046db603ddae3ef36c9b60 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 29 Jun 2023 21:44:15 +0800 Subject: [PATCH 1163/2187] usb-host: bugfix data copy out-of-bounds The length of data_buf is 12 * 1024. The control plane data must be copied based on the device_req.length. Otherwise, process will panic! Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 7723a14c3..22a09e55a 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -96,11 +96,11 @@ impl UsbHostRequest { } } - pub fn setup_ctrl_buffer(&mut self, data_buf: Vec, device_req: &UsbDeviceRequest) { + pub fn setup_ctrl_buffer(&mut self, data_buf: &[u8], device_req: &UsbDeviceRequest) { self.buffer = vec![0; (device_req.length + 8) as usize]; self.buffer[..8].copy_from_slice(device_req.as_bytes()); if self.packet.lock().unwrap().pid as u8 == USB_TOKEN_OUT { - self.buffer[8..].clone_from_slice(&data_buf); + self.buffer[8..].copy_from_slice(data_buf); } } @@ -724,10 +724,10 @@ impl UsbDeviceOps for UsbHost { self.completed.clone(), true, ))); - request - .lock() - .unwrap() - .setup_ctrl_buffer(self.usb_device.data_buf.clone(), device_req); + request.lock().unwrap().setup_ctrl_buffer( + &self.usb_device.data_buf[..device_req.length as usize], + device_req, + ); fill_transfer_by_type( host_transfer, self.handle.as_mut(), -- Gitee From c4ea775930e777fefb2b6111814849c73c3ef39d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 3 Jul 2023 15:35:20 +0800 Subject: [PATCH 1164/2187] add make to build dependencies Package gettext-sys build depends on make, let's add it to build dependencies. Signed-off-by: yezengruan --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index a1eb80233..e39a3acbe 100644 --- a/Makefile +++ b/Makefile @@ -23,3 +23,4 @@ yum-deps: @yum install gtk3-devel @yum install libusbx @yum install alsa-lib-devel + @yum install make -- Gitee From ce70ba96448cfceb37a296ff84dff74799a946b7 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 10 May 2023 20:06:04 +0800 Subject: [PATCH 1165/2187] fix some typos Signed-off-by: yezengruan --- acpi/src/aml_compiler.rs | 4 ++-- devices/src/camera_backend/demo.rs | 12 +++++----- devices/src/camera_backend/v4l2.rs | 22 +++++++++---------- devices/src/misc/scream/alsa.rs | 4 ++-- devices/src/misc/scream/mod.rs | 4 ++-- devices/src/misc/scream/pulseaudio.rs | 6 ++--- devices/src/scsi/bus.rs | 6 ++--- devices/src/usb/camera.rs | 22 +++++++++---------- devices/src/usb/camera_media_type_guid.rs | 2 +- devices/src/usb/config.rs | 2 +- devices/src/usb/storage.rs | 2 +- devices/src/usb/usbhost/host_usblib.rs | 2 +- devices/src/usb/usbhost/mod.rs | 4 ++-- devices/src/usb/xhci/xhci_pci.rs | 4 ++-- docs/config_guidebook.md | 6 ++--- machine_manager/src/config/display.rs | 2 +- machine_manager/src/config/smbios.rs | 4 ++-- machine_manager/src/machine.rs | 4 ++-- machine_manager/src/qmp/qmp_schema.rs | 2 +- pci/src/config.rs | 8 +++---- pci/src/intx.rs | 2 +- pci/src/msix.rs | 4 ++-- pci/src/root_port.rs | 2 +- .../functional/test_microvm_virtio_blk.py | 4 ++-- tests/mod_test/src/libdriver/usb.rs | 4 ++-- tests/mod_test/src/libdriver/vnc.rs | 4 ++-- tests/mod_test/tests/fwcfg_test.rs | 18 +++++++-------- tests/mod_test/tests/pci_test.rs | 16 +++++++------- tests/mod_test/tests/serial_test.rs | 14 ++++++------ tests/mod_test/tests/usb_test.rs | 8 +++---- tests/mod_test/tests/virtio_gpu_test.rs | 8 +++---- ui/src/error.rs | 2 +- ui/src/gtk/mod.rs | 10 ++++----- ui/src/input.rs | 6 ++--- ui/src/vnc/auth_vencrypt.rs | 2 +- ui/src/vnc/client_io.rs | 8 +++---- util/src/loop_context.rs | 12 +++++----- virtio/src/device/gpu.rs | 9 ++++---- virtio/src/device/serial.rs | 2 +- virtio/src/transport/virtio_pci.rs | 4 ++-- 40 files changed, 130 insertions(+), 131 deletions(-) diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index b07a0ce5b..a366941ea 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -262,8 +262,8 @@ impl AmlToUuid { } // Char located at 8, 13, 18, 23 should be `-` - let indexs = &[8, 13, 18, 23]; - for i in indexs { + let indexes = &[8, 13, 18, 23]; + for i in indexes { if uuid.chars().nth(*i).unwrap() != '-' { return false; } diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 2c57f96ab..e797cbf7d 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -446,20 +446,20 @@ impl CameraHostdevOps for DemoCamera { if frame_offset + len > locked_frame.used_len as usize { bail!("Invalid frame offset {} or len {}", frame_offset, len); } - let mut copyed = 0; + let mut copied = 0; for iov in iovecs { - if len == copyed { + if len == copied { break; } - let cnt = std::cmp::min(iov.iov_len as usize, len - copyed); - let start = frame_offset + copyed; + let cnt = std::cmp::min(iov.iov_len as usize, len - copied); + let start = frame_offset + copied; let end = start + cnt; let tmp = &locked_frame.image[start..end]; mem_from_buf(tmp, iov.iov_base) .with_context(|| format!("Failed to write data to {:x}", iov.iov_base))?; - copyed += cnt; + copied += cnt; } - Ok(copyed) + Ok(copied) } fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result { diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index b09605441..03ff3b654 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -133,7 +133,7 @@ impl V4l2CameraBackend { let backend = self.backend.as_ref().with_context(|| "Backend is none")?; debug!("Camera {} register fd {}", self.id, backend.as_raw_fd()); // Register event notifier for /dev/videoX. - let handler = Arc::new(Mutex::new(V4l2IoHander::new( + let handler = Arc::new(Mutex::new(V4l2IoHandler::new( &self.sample, backend, self.notify_cb.clone(), @@ -415,20 +415,20 @@ impl CameraHostdevOps for V4l2CameraBackend { if frame_offset + len > locked_sample.used_len as usize { bail!("Invalid frame offset {} or len {}", frame_offset, len); } - let mut copyed = 0; + let mut copied = 0; for iov in iovecs { - if len == copyed { + if len == copied { break; } - let cnt = std::cmp::min(iov.iov_len as usize, len - copyed); - let src_ptr = locked_sample.addr + frame_offset as u64 + copyed as u64; + let cnt = std::cmp::min(iov.iov_len as usize, len - copied); + let src_ptr = locked_sample.addr + frame_offset as u64 + copied as u64; // SAFETY: the address is not out of range. unsafe { std::ptr::copy(src_ptr as *const u8, iov.iov_base as *mut u8, cnt); } - copyed += cnt; + copied += cnt; } - Ok(copyed) + Ok(copied) } fn register_notify_cb(&mut self, cb: CameraNotifyCallback) { @@ -458,21 +458,21 @@ fn cam_fmt_from_v4l2(t: u32) -> Result { Ok(fmt) } -pub struct V4l2IoHander { +pub struct V4l2IoHandler { sample: Arc>, backend: Arc, notify_cb: Option, broken_cb: Option, } -impl V4l2IoHander { +impl V4l2IoHandler { pub fn new( sample: &Arc>, backend: &Arc, cb: Option, broken_cb: Option, ) -> Self { - V4l2IoHander { + V4l2IoHandler { sample: sample.clone(), backend: backend.clone(), notify_cb: cb, @@ -521,7 +521,7 @@ impl V4l2IoHander { } } -impl EventNotifierHelper for V4l2IoHander { +impl EventNotifierHelper for V4l2IoHandler { fn internal_notifiers(v4l2_handler: Arc>) -> Vec { let cloend_v4l2_handler = v4l2_handler.clone(); let handler: Rc = Rc::new(move |event, _fd: RawFd| { diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index 4c830637c..251878142 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -24,7 +24,7 @@ use anyhow::Result; use log::{debug, error, warn}; use super::{ - pulseaudio::TAGET_LATENCY_MS, AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, + pulseaudio::TARGET_LATENCY_MS, AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, }; @@ -67,7 +67,7 @@ impl AlsaStreamData { bytes_per_sample: 0, stream_fmt, rate: AUDIO_SAMPLE_RATE_44KHZ, - latency: TAGET_LATENCY_MS, + latency: TARGET_LATENCY_MS, app_name: name.to_string(), init: false, } diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 610928d56..9c1344f04 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -32,7 +32,7 @@ use self::{alsa::AlsaStreamData, audio_demo::AudioDemo}; use super::ivshmem::Ivshmem; use machine_manager::config::scream::ScreamConfig; use pci::{PciBus, PciDevOps}; -use pulseaudio::{PulseStreamData, TAGET_LATENCY_MS}; +use pulseaudio::{PulseStreamData, TARGET_LATENCY_MS}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; @@ -43,7 +43,7 @@ pub const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; // to be trained in polling within 50ms. Theoretically, the shorter the polling time, // the better. However, if the value is too small, the overhead is high. So take a // compromise: 50 * 1000 / 8 us. -const POLL_DELAY_US: u64 = (TAGET_LATENCY_MS as u64) * 1000 / 8; +const POLL_DELAY_US: u64 = (TARGET_LATENCY_MS as u64) * 1000 / 8; pub const SCREAM_MAGIC: u64 = 0x02032023; diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 035823932..5aa88fbec 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -27,7 +27,7 @@ use super::{ }; use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData}; -pub const TAGET_LATENCY_MS: u32 = 50; +pub const TARGET_LATENCY_MS: u32 = 50; const MAX_LATENCY_MS: u32 = 100; const STREAM_NAME: &str = "Audio"; @@ -88,7 +88,7 @@ impl PulseStreamData { // Set buffer size for requested latency. let buffer_attr = BufferAttr { maxlength: ss.usec_to_bytes(MicroSeconds(MAX_LATENCY_MS as u64 * 1000)) as u32, - tlength: ss.usec_to_bytes(MicroSeconds(TAGET_LATENCY_MS as u64 * 1000)) as u32, + tlength: ss.usec_to_bytes(MicroSeconds(TARGET_LATENCY_MS as u64 * 1000)) as u32, prebuf: std::u32::MAX, minreq: std::u32::MAX, fragsize: std::u32::MAX, @@ -119,7 +119,7 @@ impl PulseStreamData { channel_map, buffer_attr, stream_fmt, - latency: TAGET_LATENCY_MS, + latency: TARGET_LATENCY_MS, app_name: name.to_string(), stream_name: STREAM_NAME.to_string(), dir: pa_dir, diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index c1daca99d..2e4780b5f 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -351,7 +351,7 @@ const GC_FC_PROFILE_LIST: u16 = 0x0000; /// Mandatory behavior for all devices. const GC_FC_CORE: u16 = 0x0001; /// The medium may be removed from the device. -const GC_FC_REMOVEABLE_MEDIUM: u16 = 0x0003; +const GC_FC_REMOVABLE_MEDIUM: u16 = 0x0003; #[derive(Clone, PartialEq, Eq)] pub enum ScsiXferMode { @@ -422,7 +422,7 @@ fn scsi_bus_parse_req_cdb( } // When CDB's Group Code is vendor specific or reserved, len/xfer/lba will be negative. - // So, don't need to check again afer checking in cdb length. + // So, don't need to check again after checking in cdb length. let xfer = scsi_cdb_xfer(&cdb, dev); let lba = scsi_cdb_lba(&cdb); @@ -1597,7 +1597,7 @@ fn scsi_command_emulate_get_configuration( // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). Bit 2: Pvnt Jmpr. // Bit 1: DBML. Bit 0: Lock(1). // Byte[37-39]: Reserved. - BigEndian::write_u16(&mut outbuf[32..34], GC_FC_REMOVEABLE_MEDIUM); + BigEndian::write_u16(&mut outbuf[32..34], GC_FC_REMOVABLE_MEDIUM); outbuf[34] = 0x0b; outbuf[35] = 4; outbuf[36] = 0x39; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 776b986a0..bbe0d2589 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -663,7 +663,7 @@ impl UsbCamera { if self.listening { return Ok(()); } - let cam_handler = Arc::new(Mutex::new(CameraIoHander::new( + let cam_handler = Arc::new(Mutex::new(CameraIoHandler::new( &self.camera_fd, &self.packet_list, &self.camera_dev, @@ -1016,21 +1016,21 @@ impl UvcPayload { } /// Camere handler for copying frame data to usb packet. -struct CameraIoHander { +struct CameraIoHandler { camera: Arc>, fd: Arc, packet_list: Arc>>>>, payload: Arc>, } -impl CameraIoHander { +impl CameraIoHandler { fn new( fd: &Arc, list: &Arc>>>>, camera: &Arc>, payload: &Arc>, ) -> Self { - CameraIoHander { + CameraIoHandler { camera: camera.clone(), fd: fd.clone(), packet_list: list.clone(), @@ -1089,15 +1089,15 @@ impl CameraIoHander { iovecs = iov_discard_front_direct(&mut pkt.iovecs, pkt.actual_length as u64) .with_context(|| format!("Invalid iov size {}", pkt_size))?; } - let copyed = locked_camera.get_frame( + let copied = locked_camera.get_frame( iovecs, locked_payload.frame_offset, frame_data_size as usize, )?; - pkt.actual_length += copyed as u32; + pkt.actual_length += copied as u32; debug!( - "Camera handle payload, frame_offset {} payloadoffset {} data_size {} copyed {}", - locked_payload.frame_offset, locked_payload.payload_offset, frame_data_size, copyed + "Camera handle payload, frame_offset {} payloadoffset {} data_size {} copied {}", + locked_payload.frame_offset, locked_payload.payload_offset, frame_data_size, copied ); locked_payload.frame_offset += frame_data_size as usize; locked_payload.payload_offset += frame_data_size as usize; @@ -1113,7 +1113,7 @@ impl CameraIoHander { } } -impl EventNotifierHelper for CameraIoHander { +impl EventNotifierHelper for CameraIoHandler { fn internal_notifiers(io_handler: Arc>) -> Vec { let cloned_io_handler = io_handler.clone(); let handler: Rc = Rc::new(move |_event, fd: RawFd| { @@ -1324,8 +1324,8 @@ mod test { #[test] fn test_interfaces_table_data_len() { - // VC and VS's header differents, their wTotalSize field's offset are the bit 5 and 4 respectively in their data[0] vector. - // the rest datas follow the same principle that the 1st element is the very data vector's length. + // VC and VS's header difference, their wTotalSize field's offset are the bit 5 and 4 respectively in their data[0] vector. + // the rest data follow the same principle that the 1st element is the very data vector's length. test_interface_table_data_len(gen_desc_interface_camera_vc().unwrap(), 5); test_interface_table_data_len(gen_desc_interface_camera_vs(list_format()).unwrap(), 4); } diff --git a/devices/src/usb/camera_media_type_guid.rs b/devices/src/usb/camera_media_type_guid.rs index b03c5ca0d..f58b0c96c 100644 --- a/devices/src/usb/camera_media_type_guid.rs +++ b/devices/src/usb/camera_media_type_guid.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -//! Media Type GUID. refered with uvc.h in linux kernel. +//! Media Type GUID. referred with uvc.h in linux kernel. use once_cell::sync::Lazy; use std::collections::HashMap; diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index aa61ac556..3dda38b45 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -125,7 +125,7 @@ pub const PLS_INACTIVE: u32 = 6; pub const PLS_POLLING: u32 = 7; pub const PLS_RECOVERY: u32 = 8; pub const PLS_HOT_RESET: u32 = 9; -pub const PLS_COMPILANCE_MODE: u32 = 10; +pub const PLS_COMPLIANCE_MODE: u32 = 10; pub const PLS_TEST_MODE: u32 = 11; pub const PLS_RESUME: u32 = 15; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index dccb9be2c..f46374ffb 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -234,7 +234,7 @@ pub struct UsbStorage { /// Scsi bus attached to this usb-storage device. scsi_bus: Arc>, /// Effective scsi backend. - // Note: scsi device should attach to scsi bus. Logically, scsi device shoud not be placed in UsbStorage. + // Note: scsi device should attach to scsi bus. Logically, scsi device should not be placed in UsbStorage. // But scsi device is needed in processing scsi request. Because the three (usb-storage/scsi bus/scsi device) // correspond one-to-one, add scsi device member here for the execution efficiency (No need to find a unique // device from the hash table of the unique bus). diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 511c442db..299889a48 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -248,7 +248,7 @@ pub fn fill_transfer_by_type( INTERRUPT_TIMEOUT, ); }, - _ => error!("Unsupport transfer type: {:?}", transfer_type), + _ => error!("Unsupported transfer type: {:?}", transfer_type), } } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 22a09e55a..2d7471add 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -168,7 +168,7 @@ pub struct UsbHost { ifs_num: u8, ifs: [InterfaceStatus; USB_MAX_INTERFACES as usize], usb_device: UsbDevice, - /// Callback for release dev to Host afer the vm exited. + /// Callback for release dev to Host after the vm exited. exit: Option>, /// All pending asynchronous usb request. requests: Arc>>>>, @@ -333,7 +333,7 @@ impl UsbHost { }; for (i, intf) in conf.interfaces().enumerate() { - // The usb_deviec.altsetting indexs alternate settings by the interface number. + // The usb_deviec.altsetting indexes alternate settings by the interface number. // Get the 0th alternate setting first so that we can grap the interface number, // and then correct the alternate setting value if necessary. let mut intf_desc = intf.descriptors().next(); diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 7d5d43eb0..ae598a162 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -20,7 +20,7 @@ use address_space::{AddressSpace, Region}; use log::debug; use machine_manager::config::XhciConfig; use pci::config::{ - PciConfig, RegionType, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, + PciConfig, RegionType, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::update_dev_id; @@ -242,7 +242,7 @@ impl PciDevOps for XhciPciDevice { )?; let mut mem_region_size = (XHCI_PCI_CONFIG_LENGTH as u64).next_power_of_two(); - mem_region_size = max(mem_region_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); + mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); self.pci_config.register_bar( 0_usize, self.mem_region.clone(), diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 9fe66784d..50b78c836 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -294,7 +294,7 @@ fourteen properties are supported for virtio block device. * iothread: indicate which iothread will be used. (optional) if not set, the main thread will be used. * throttling.iops-total: used to limit IO operations for block device. (optional) * discard: free up unused disk space. (optional) `unmap/ignore` means `on/off`. If not set, default is `ignore`. -* detect-zeroes: optimize writing zeroes to disk space. (optional) `unmap` means it can free up disk space when discard is `unmap`. If dicard is `ignore`, `unmap` of detect-zeroes is same as `on`. If not set, default is `off`. +* detect-zeroes: optimize writing zeroes to disk space. (optional) `unmap` means it can free up disk space when discard is `unmap`. If discard is `ignore`, `unmap` of detect-zeroes is same as `on`. If not set, default is `off`. * if: drive type, for block drive, it should be `none`. (optional) If not set, default is `none`. * format: the format of block image. (optional) If not set, default is `raw`. NB: currently only `raw` is supported. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. (optional) The max queues number supported is 32. If not set, the default block queue number is the smaller one of vCPU count and the max queues number (e.g, min(vcpu_count, 32)). @@ -799,7 +799,7 @@ One property can be set for USB Tablet. Note: Only one tablet can be configured. -### 2.13.4 USB Camera +#### 2.13.4 USB Camera Video Camera Device that based on USB video class protocol. It should be attached to USB controller. 3 properties can be set for USB Camera. @@ -831,7 +831,7 @@ Three properties can be set for USB Storage. Note: "aio=off,direct=false" must be configured and other aio/direct values are not supported. -### 2.13.6 USB Host +#### 2.13.6 USB Host USB Host Device that based on USB protocol. It should be attached to USB controller. Six properties can be set for USB Host. diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 4f89f749d..bb09501a5 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -52,7 +52,7 @@ impl VmConfig { if let Some(str) = cmd_parser.get_value::("")? { match str.as_str() { "gtk" => display_config.gtk = true, - _ => bail!("Unsupport device: {}", str), + _ => bail!("Unsupported device: {}", str), } } if let Some(name) = cmd_parser.get_value::("app-name")? { diff --git a/machine_manager/src/config/smbios.rs b/machine_manager/src/config/smbios.rs index e0ef354ec..38f120489 100644 --- a/machine_manager/src/config/smbios.rs +++ b/machine_manager/src/config/smbios.rs @@ -49,8 +49,8 @@ fn check_valid_uuid(uuid: &str) -> bool { } // Char located at 8, 13, 18, 23 should be `-` - let indexs = &[8, 13, 18, 23]; - for i in indexs { + let indexes = &[8, 13, 18, 23]; + for i in indexes { if uuid.chars().nth(*i).unwrap() != '-' { return false; } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 849f92d69..c09f4213b 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -364,12 +364,12 @@ pub trait DeviceInterface { CmdParameter { name: "discard".to_string(), help: "discard operation (unmap|ignore)".to_string(), - paramter_type: "string".to_string(), + parameter_type: "string".to_string(), }, CmdParameter { name: "detect-zeroes".to_string(), help: "optimize zero writes (unmap|on|off)".to_string(), - paramter_type: "string".to_string(), + parameter_type: "string".to_string(), }, ]; let cmd_lines = vec![CmdLine { diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index ca3cec8aa..017e699d5 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1921,7 +1921,7 @@ pub struct CmdParameter { pub name: String, pub help: String, #[serde(rename = "type")] - pub paramter_type: String, + pub parameter_type: String, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] diff --git a/pci/src/config.rs b/pci/src/config.rs index d0249da0a..8f2ec6134 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -146,9 +146,9 @@ pub const BAR_NUM_MAX_FOR_ENDPOINT: u8 = 6; /// The maximum Bar ID numbers of a Type 1 device pub const BAR_NUM_MAX_FOR_BRIDGE: u8 = 2; /// mmio bar's minimum size shall be 4KB -pub const MINMUM_BAR_SIZE_FOR_MMIO: usize = 0x1000; +pub const MINIMUM_BAR_SIZE_FOR_MMIO: usize = 0x1000; /// pio bar's minimum size shall be 4B -pub const MINMUM_BAR_SIZE_FOR_PIO: usize = 0x4; +pub const MINIMUM_BAR_SIZE_FOR_PIO: usize = 0x4; /// PCI Express capability registers, same as kernel defines @@ -395,7 +395,7 @@ pub struct PciConfig { pub msix: Option>>, /// Offset of the PCI express capability. pub pci_express_cap_offset: u16, - /// INTx infomation. + /// INTx information. pub intx: Option>>, } @@ -1157,7 +1157,7 @@ impl PciConfig { fn validate_bar_size(&self, bar_type: RegionType, size: u64) -> Result<()> { if !size.is_power_of_two() - || (bar_type == RegionType::Io && size < MINMUM_BAR_SIZE_FOR_PIO as u64) + || (bar_type == RegionType::Io && size < MINIMUM_BAR_SIZE_FOR_PIO as u64) || (bar_type == RegionType::Mem32Bit && size > u32::MAX as u64) || (bar_type == RegionType::Io && size > u16::MAX as u64) { diff --git a/pci/src/intx.rs b/pci/src/intx.rs index 7e545acd7..50f727051 100644 --- a/pci/src/intx.rs +++ b/pci/src/intx.rs @@ -42,7 +42,7 @@ impl PciIntxState { /// INTx structure. pub struct Intx { - /// Deivce name. + /// Device name. pub device_name: String, /// Physical interrupt pin. pub irq_pin: u32, diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 9104524ef..d6c283830 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -31,7 +31,7 @@ use util::{ test_helper::{add_msix_msg, is_test_enabled}, }; -use crate::config::{CapId, PciConfig, RegionType, MINMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; +use crate::config::{CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, ranges_overlap, PciBus, @@ -676,7 +676,7 @@ pub fn init_msix( )?; } else { let mut bar_size = ((table_size + pba_size) as u64).next_power_of_two(); - bar_size = max(bar_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); + bar_size = max(bar_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); let region = Region::init_container_region(bar_size); Msix::register_memory_region( msix.clone(), diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index ab2470ef3..7d4ae4c75 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -570,7 +570,7 @@ impl HotplugOps for RootPort { .unwrap(); if (sltctl & PCI_EXP_SLTCTL_PIC) == PCI_EXP_SLTCTL_PWR_IND_BLINK { - bail!("Guest is still on the fly of another (un)pluging"); + bail!("Guest is still on the fly of another (un)plugging"); } let devfn = dev diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py b/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py index 220581c8c..690ee9f0a 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py @@ -142,8 +142,8 @@ def test_microvm_virtio_blk_md5(test_session_root_path, microvm): mount_cmd = "mount /dev/vdb /mnt" test_vm.serial_cmd(mount_cmd) - wirte_cmd = "touch /mnt/test_virtioblk.c" - test_vm.serial_cmd(wirte_cmd) + write_cmd = "touch /mnt/test_virtioblk.c" + test_vm.serial_cmd(write_cmd) _cmd = "md5sum /mnt/test_virtioblk.c" _, md5 = test_vm.serial_cmd(_cmd) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 3ec3dc893..a9606b3a4 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -2294,8 +2294,8 @@ impl TestXhciPciDevice { let mut buf = self.get_transfer_data_indirect(evt.ptr, TRB_MAX_LEN as u64); data.append(&mut buf); } else if evt.ccode == TRBCCode::ShortPacket as u32 { - let copyed = (TRB_MAX_LEN - evt.length) as u64; - let mut buf = self.get_transfer_data_indirect(evt.ptr, copyed); + let copied = (TRB_MAX_LEN - evt.length) as u64; + let mut buf = self.get_transfer_data_indirect(evt.ptr, copied); data.append(&mut buf); if total == data.len() as u32 { return (false, data); diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index abab4890e..f8f660959 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -824,9 +824,9 @@ impl VncClient { } /// Send point event to VncServer. - pub fn test_point_event(&mut self, buttom_mask: u8, x: u16, y: u16) -> Result<()> { + pub fn test_point_event(&mut self, button_mask: u8, x: u16, y: u16) -> Result<()> { println!("Test point event."); - let test_event = TestPointEvent::new(buttom_mask, x, y); + let test_event = TestPointEvent::new(button_mask, x, y); self.write_msg(&mut test_event.to_be_bytes())?; Ok(()) } diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index fedc98359..609fcb6a0 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -228,7 +228,7 @@ fn test_boot_index() { } #[test] -fn test_smbios_tyep0() { +fn test_smbios_type0() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); @@ -267,17 +267,17 @@ fn test_smbios_tyep0() { talble_len, ); assert_eq!(talbles_size, talble_len); - let talbe_type0_len = 24; + let table_type0_len = 24; assert_eq!( - String::from_utf8_lossy(&read_table_date[talbe_type0_len..talbe_type0_len + 7]), + String::from_utf8_lossy(&read_table_date[table_type0_len..table_type0_len + 7]), "vendor0" ); assert_eq!( - String::from_utf8_lossy(&read_table_date[talbe_type0_len + 8..talbe_type0_len + 16]), + String::from_utf8_lossy(&read_table_date[table_type0_len + 8..table_type0_len + 16]), "version0" ); assert_eq!( - String::from_utf8_lossy(&read_table_date[talbe_type0_len + 17..talbe_type0_len + 22]), + String::from_utf8_lossy(&read_table_date[table_type0_len + 17..table_type0_len + 22]), "date0" ); @@ -285,7 +285,7 @@ fn test_smbios_tyep0() { } #[test] -fn test_smbios_tyep1() { +fn test_smbios_type1() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); @@ -331,13 +331,13 @@ fn test_smbios_tyep1() { talble_len, ); assert_eq!(talbles_size, talble_len); - let talbe_type0_len = 24; + let table_type0_len = 24; assert_eq!( - String::from_utf8_lossy(&read_table_date[talbe_type0_len..talbe_type0_len + 7]), + String::from_utf8_lossy(&read_table_date[table_type0_len..table_type0_len + 7]), "vendor0" ); assert_eq!( - String::from_utf8_lossy(&read_table_date[talbe_type0_len + 8..talbe_type0_len + 16]), + String::from_utf8_lossy(&read_table_date[table_type0_len + 8..table_type0_len + 16]), "version0" ); assert_eq!(read_table_date[48], 1); diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 75927fe85..114275c05 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -433,7 +433,7 @@ fn validate_config_perm_1byte( pci_dev: TestPciDev, offset: u8, expected_value: u8, - writed_value: u8, + written_value: u8, mask: u8, ) { let config_value = @@ -446,7 +446,7 @@ fn validate_config_perm_1byte( pci_dev .pci_bus .borrow() - .config_writeb(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + .config_writeb(pci_dev.bus_num, pci_dev.devfn, offset, written_value); let config_value = pci_dev @@ -460,13 +460,13 @@ fn validate_config_perm_2byte( pci_dev: TestPciDev, offset: u8, expected_value: u16, - writed_value: u16, + written_value: u16, mask: u16, ) { pci_dev .pci_bus .borrow() - .config_writew(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + .config_writew(pci_dev.bus_num, pci_dev.devfn, offset, written_value); let config_value = pci_dev .pci_bus @@ -479,7 +479,7 @@ fn validate_config_perm_4byte( pci_dev: TestPciDev, offset: u8, expected_value: u32, - writed_value: u32, + written_value: u32, mask: u32, ) { let config_value = @@ -492,7 +492,7 @@ fn validate_config_perm_4byte( pci_dev .pci_bus .borrow() - .config_writel(pci_dev.bus_num, pci_dev.devfn, offset, writed_value); + .config_writel(pci_dev.bus_num, pci_dev.devfn, offset, written_value); let config_value = pci_dev @@ -1294,7 +1294,7 @@ fn test_pci_type1_reset() { /// Verify that out-of-bounds access to the configuration space #[test] -fn test_out_boundry_config_access() { +fn test_out_boundary_config_access() { let blk_nums = 0; let root_port_nums = 1; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); @@ -1338,7 +1338,7 @@ fn test_out_size_config_access() { /// Verify that out-of-bounds access to the msix bar space. #[test] -fn test_out_boundry_msix_access() { +fn test_out_boundary_msix_access() { let blk_nums = 0; let root_port_nums = 1; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index 6a69f477d..096857a9d 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -648,7 +648,7 @@ fn virtserialport_pty_basic() { /// 5. Destroy device. /// Expect: /// 1/5: success. -/// 2/3: Just discard this invalid msg. Nothing happend. +/// 2/3: Just discard this invalid msg. Nothing happened. /// 4: report virtio error. #[test] fn virtconsole_pty_err_out_control_msg() { @@ -664,11 +664,11 @@ fn virtconsole_pty_err_out_control_msg() { st.serial_init(); - // Error out control msg which has invalid event. Just discard this invalid msg. Nothing happend. + // Error out control msg which has invalid event. Just discard this invalid msg. Nothing happened. let invalid_event_msg = VirtioConsoleControl::new(nr as u32, VIRTIO_CONSOLE_PORT_NAME, 1); st.out_control_event(invalid_event_msg); - // Error out control msg which has non-existed port id. Just discard this invalid msg. Nothing happend. + // Error out control msg which has non-existed port id. Just discard this invalid msg. Nothing happened. let invalid_event_msg = VirtioConsoleControl::new((nr + 5) as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); st.out_control_event(invalid_event_msg); @@ -694,7 +694,7 @@ fn virtconsole_pty_err_out_control_msg() { /// 4. Destroy device. /// Expect: /// 1/4: success. -/// 2: Just discard this invalid msg. Nothing happend. +/// 2: Just discard this invalid msg. Nothing happened. /// 3: report virtio error. #[test] fn virtconsole_pty_invalid_in_control_buffer() { @@ -756,7 +756,7 @@ fn virtconsole_pty_invalid_in_control_buffer() { /// 5. Destroy device. /// Expect: /// 1/4/5: success. -/// 2/3: Just discard these requests. Nothing happend. +/// 2/3: Just discard these requests. Nothing happened. #[test] fn virtserialport_socket_not_connect() { let nr = 1; @@ -781,11 +781,11 @@ fn virtserialport_socket_not_connect() { st.serial_init(); - // Requests will be discarded when host (port 1, output queue id: 5) is not connected. Nothing happend. + // Requests will be discarded when host (port 1, output queue id: 5) is not connected. Nothing happened. let test_data = String::from("Test\n"); st.virtqueue_add_element(5, Some(test_data.as_bytes()), test_data.len() as u64); - // Requests will be discarded when it is sent in virtqueue which has no port(port 2, output queue id: 7). Nothing happend. + // Requests will be discarded when it is sent in virtqueue which has no port(port 2, output queue id: 7). Nothing happened. let test_data = String::from("Test\n"); st.virtqueue_add_element(7, Some(test_data.as_bytes()), test_data.len() as u64); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 34265c7c5..83bac57bc 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -2707,7 +2707,7 @@ fn test_tablet_keyboard_plug() { /// TestStep: /// 1. Start a domain without tablet and keyboard. /// 2. Plug 15 tablets and keyboards. -/// 3. Continue to hotplug tablet and keyboard, exceptation is failure. +/// 3. Continue to hotplug tablet and keyboard, expectation is failure. /// 4. Unplug all tablets and keyboards. /// Except: /// 1/2/4: success. @@ -2749,10 +2749,10 @@ fn test_max_number_of_device() { /// Test abnormal hotplug operation /// TestStep: /// 1. Start a domain without tablet and keyboard. -/// 2. Unplug tablet and keyboard, exceptation is failure. +/// 2. Unplug tablet and keyboard, expectation is failure. /// 3. Hotplug tablet and keyboard. -/// 4. Unplug tablet and keyboard, exceptation is success. -/// 5. Repeat the unplug operation, exceptation is failure. +/// 4. Unplug tablet and keyboard, expectation is success. +/// 5. Repeat the unplug operation, expectation is failure. /// Except: /// 1/3/4: success. /// 2/5: Failed to detach device: id input1 not found. diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index c2ae38911..5915cc9c6 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -37,7 +37,7 @@ const D_RES_ID: u32 = 1; const D_SCANOUT_ID: u32 = 0; const D_INVALID_SCANOUT_ID: u32 = 100; const D_FMT: u32 = 2; -const D_INVALD_FMT: u32 = VIRTIO_GPU_FORMAT_INVALID_UNORM; +const D_INVALID_FMT: u32 = VIRTIO_GPU_FORMAT_INVALID_UNORM; const D_WIDTH: u32 = 64; const D_HEIGHT: u32 = 64; const D_BYTE_PER_PIXEL: u32 = 4; @@ -45,7 +45,7 @@ const D_IMG_SIZE: u32 = D_WIDTH * D_HEIGHT * D_BYTE_PER_PIXEL; const D_OFFSET: u64 = 0; const D_X_COORD: u32 = 0; const D_Y_COORD: u32 = 0; -const D_INVALD_NR_ENTRIES: u32 = 1 + 16384; +const D_INVALID_NR_ENTRIES: u32 = 1 + 16384; #[test] fn image_display_fun() { @@ -276,7 +276,7 @@ fn resource_create_dfx() { VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, resource_create( &gpu, - VirtioGpuResourceCreate2d::new(D_RES_ID, D_INVALD_FMT, D_WIDTH, D_HEIGHT) + VirtioGpuResourceCreate2d::new(D_RES_ID, D_INVALID_FMT, D_WIDTH, D_HEIGHT) ) .hdr_type ); @@ -391,7 +391,7 @@ fn resource_attach_dfx() { VIRTIO_GPU_RESP_ERR_UNSPEC, resource_attach_backing( &gpu, - VirtioGpuResourceAttachBacking::new(D_RES_ID, D_INVALD_NR_ENTRIES), + VirtioGpuResourceAttachBacking::new(D_RES_ID, D_INVALID_NR_ENTRIES), vec![VirtioGpuMemEntry::new(image_addr, image_size as u32)] ) .hdr_type diff --git a/ui/src/error.rs b/ui/src/error.rs index dce0857cb..d8bb09d57 100644 --- a/ui/src/error.rs +++ b/ui/src/error.rs @@ -20,7 +20,7 @@ pub enum VncError { source: util::error::UtilError, }, #[error("Unsupported RFB Protocol Version!")] - UnsupportRFBProtocolVersion, + UnsupportedRFBProtocolVersion, #[error("Invalid Image Size: width: {0}, height: {0}")] InvalidImageSize(i32, i32), #[error("Tcp bind failed: {0}")] diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 39b89d16b..45a1b65ff 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -379,7 +379,7 @@ impl GtkDisplayScreen { .map_or(DisplaySurface::default(), |s| s); // SAFETY: The image is created within the function, it can be ensure - // that the data ptr is not nullptr and the image size matchs the image data. + // that the data ptr is not nullptr and the image size matches the image data. let cairo_image = unsafe { ImageSurface::create_for_data_unsafe( surface.data() as *mut u8, @@ -663,9 +663,9 @@ fn gs_show_menu_callback( /// 1. Switch operation 1, the gtk display should change the image from a to b. /// 2. Switch operation 2, the gtk display should change the image from b to c, but /// the channel between stratovirt mainloop and gtk mainloop lost the event. -/// 3. The gtk display always show th image a. +/// 3. The gtk display always show the image. /// So, the refresh operation will always check if the image has been switched, if -/// the result is yes, then use the switch operation to switch the latest image. +/// the result is yes, then use the switch operation to switch the latest image. fn do_refresh_event(gs: &Rc>) -> Result<()> { let borrowed_gs = gs.borrow(); let active_con = borrowed_gs.con.upgrade(); @@ -847,12 +847,12 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { bail!("Image data is invalid."); } - let source_suface = DisplaySurface { + let source_surface = DisplaySurface { format: surface.format, image: ref_pixman_image(surface.image), }; unref_pixman_image(borrowed_gs.source_surface.image); - borrowed_gs.source_surface = source_suface; + borrowed_gs.source_surface = source_surface; if let Some(s) = borrowed_gs.transfer_surface { unref_pixman_image(s.image); borrowed_gs.transfer_surface = None; diff --git a/ui/src/input.rs b/ui/src/input.rs index 078de4f0c..4841f07be 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -31,8 +31,8 @@ pub const INPUT_POINT_RIGHT: u8 = 0x04; pub const ASCII_A: i32 = 65; pub const ASCII_Z: i32 = 90; pub const UPPERCASE_TO_LOWERCASE: i32 = 32; -const ASCII_A_LOWCASE: i32 = 97; -const ASCII_Z_LOWCASE: i32 = 122; +const ASCII_A_LOWERCASE: i32 = 97; +const ASCII_Z_LOWERCASE: i32 = 122; const BIT_PER_BYTE: u32 = 8; // Keycode. @@ -333,7 +333,7 @@ pub fn point_event(button: u32, x: u32, y: u32) -> Result<()> { pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { let mut locked_input = INPUTS.lock().unwrap(); let upper = (ASCII_A..=ASCII_Z).contains(&keysym); - let is_letter = upper || (ASCII_A_LOWCASE..=ASCII_Z_LOWCASE).contains(&keysym); + let is_letter = upper || (ASCII_A_LOWERCASE..=ASCII_Z_LOWERCASE).contains(&keysym); let in_keypad = (KEYCODE_KP_7..=KEYCODE_KP_DECIMAL).contains(&keycode); if down && is_letter { diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index 06e848cec..b2f59eeb9 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -99,7 +99,7 @@ impl ClientIoHandler { buf.append(&mut (0_u8).to_be_bytes().to_vec()); vnc_write(&client, buf); vnc_flush(&client); - return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); + return Err(anyhow!(VncError::UnsupportedRFBProtocolVersion)); } else { let mut buf = Vec::new(); // Accept version. diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index bf48e8bb7..cb12872e5 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -503,15 +503,15 @@ impl ClientIoHandler { let client = self.client.clone(); let mut buf = self.read_incoming_msg(); // The last character should be '\n' - let lf_char = buf.pop().ok_or(VncError::UnsupportRFBProtocolVersion)?; + let lf_char = buf.pop().ok_or(VncError::UnsupportedRFBProtocolVersion)?; if !lf_char.eq(&10) { - return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); + return Err(anyhow!(VncError::UnsupportedRFBProtocolVersion)); } let ver_str = String::from_utf8_lossy(&buf).to_string(); let ver = match scanf!(ver_str, "RFB {usize:/\\d{3}/}.{usize:/\\d{3}/}") { Ok(v) => v, Err(_e) => { - return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); + return Err(anyhow!(VncError::UnsupportedRFBProtocolVersion)); } }; @@ -521,7 +521,7 @@ impl ClientIoHandler { buf.append(&mut (AuthState::Invalid as u32).to_be_bytes().to_vec()); vnc_write(&client, buf); vnc_flush(&client); - return Err(anyhow!(VncError::UnsupportRFBProtocolVersion)); + return Err(anyhow!(VncError::UnsupportedRFBProtocolVersion)); } if [4, 5].contains(&version.minor) { diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 06a9b0cdb..2ff98cbed 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -271,7 +271,7 @@ impl EventLoopContext { break; } // SAFETY: We will stop removing when reach max_cnt and no other place - // removes element of gc. This is to avoid infinite poping if other + // removes element of gc. This is to avoid infinite popping if other // thread continuously adds element to gc. self.gc.write().unwrap().remove(0); pop_cnt += 1; @@ -482,13 +482,13 @@ impl EventLoopContext { let timeout = self.timers_min_timeout_ms(); if timeout == -1 { for _i in 0..AIO_PRFETCH_CYCLE_TIME { - for notifer in self.events.read().unwrap().values() { - let status_locked = notifer.status.lock().unwrap(); - if *status_locked != EventStatus::Alive || notifer.handler_poll.is_none() { + for notifier in self.events.read().unwrap().values() { + let status_locked = notifier.status.lock().unwrap(); + if *status_locked != EventStatus::Alive || notifier.handler_poll.is_none() { continue; } - let handler_poll = notifer.handler_poll.as_ref().unwrap(); - if handler_poll(EventSet::empty(), notifer.raw_fd).is_some() { + let handler_poll = notifier.handler_poll.as_ref().unwrap(); + if handler_poll(EventSet::empty(), notifier.raw_fd).is_some() { break; } } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index e7b776ddc..027feb4ec 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -310,8 +310,8 @@ impl HardWareOperations for GpuOpts { if self.interrupt_cb.is_none() { return; } - let interrup_cb = self.interrupt_cb.as_ref().unwrap(); - if let Err(e) = (interrup_cb)(&VirtioInterruptType::Config, None, false) { + let interrupt_cb = self.interrupt_cb.as_ref().unwrap(); + if let Err(e) = (interrupt_cb)(&VirtioInterruptType::Config, None, false) { error!( "{:?}. {:?}", VirtioError::InterruptTrigger("gpu", VirtioInterruptType::Config), @@ -407,7 +407,6 @@ struct GpuScanout { x: u32, y: u32, resource_id: u32, - // Cursor visable cursor_visible: bool, } @@ -690,7 +689,7 @@ impl GpuIoHandler { ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); } - // Windows front-end drvier does not deliver data in format sequence. + // Windows front-end driver does not deliver data in format sequence. // So we fix it in back-end. // TODO: Fix front-end driver is a better solution. if res.format == VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM @@ -1129,7 +1128,7 @@ impl GpuIoHandler { data = pixman_image_get_data(res.pixman_image).cast() as *mut u8; } - // When the dedicated area is continous. + // When the dedicated area is continuous. if trans_info.rect.x_coord == 0 && trans_info.rect.width == width { let offset_dst = (trans_info.rect.y_coord * stride) as usize; let trans_size = (trans_info.rect.height * stride) as usize; diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 5899f58bc..2bd7c6646 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -719,7 +719,7 @@ impl SerialControlHandler { fn handle_control_message(&mut self, ctrl: &mut VirtioConsoleControl) { if ctrl.event == VIRTIO_CONSOLE_DEVICE_READY { if ctrl.value == 0 { - error!("Guest is not ready to receive contorl message."); + error!("Guest is not ready to receive control message."); return; } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index e991904e1..9ffa05eef 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -22,7 +22,7 @@ use log::{debug, error, warn}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use pci::config::{ - RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, + RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; @@ -1168,7 +1168,7 @@ impl PciDevOps for VirtioPciDevice { let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) as u64) .next_power_of_two(); - mem_region_size = max(mem_region_size, MINMUM_BAR_SIZE_FOR_MMIO as u64); + mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); let modern_mem_region = Region::init_container_region(mem_region_size); self.modern_mem_region_init(&modern_mem_region)?; -- Gitee From e9e36f81e206bf127951dd494544dc46a4765acc Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 3 Jul 2023 22:10:37 +0800 Subject: [PATCH 1166/2187] usb: fix test_max_number_of_device After usb device unplugged, read event from xhci. Signed-off-by: zhouli57 --- tests/mod_test/tests/usb_test.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 34265c7c5..59a952aff 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -2741,6 +2741,9 @@ fn test_max_number_of_device() { i = 1; while i <= max_num_of_port2 { qmp_unplug_usb_event(test_state.borrow_mut(), i); + let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(evt.ccode, TRBCCode::Success as u32); + qmp_event_read(test_state.borrow_mut()); i += 1; } test_state.borrow_mut().stop(); -- Gitee From 5d84ff37466955b851c3a5e68e6f65e2b2d573e8 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 3 Jul 2023 22:26:07 +0800 Subject: [PATCH 1167/2187] Qcow2: add discard Add discard. Signed-off-by: Xiao Ye --- Cargo.lock | 1 + block_backend/Cargo.toml | 1 + block_backend/src/file.rs | 33 +-- block_backend/src/qcow2/mod.rs | 346 ++++++++++++++++++++++++---- block_backend/src/qcow2/refcount.rs | 170 +++++++++++--- block_backend/src/qcow2/table.rs | 8 +- block_backend/src/raw.rs | 13 +- util/src/aio/mod.rs | 2 +- 8 files changed, 478 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 093f85f0b..bd86834e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,6 +195,7 @@ dependencies = [ "log", "machine_manager", "once_cell", + "serde_json", "thiserror", "util", "vmm-sys-util", diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index 15d4a089a..a20e5381b 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -9,6 +9,7 @@ license = "Mulan PSL v2" thiserror = "1.0" vmm-sys-util = "0.11.0" anyhow = "1.0" +serde_json = "1.0" log = "0.4" byteorder = "1.4.3" once_cell = "1.13.0" diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 9a88f50a3..5dbec86fa 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -29,7 +29,7 @@ use vmm_sys_util::epoll::EventSet; use crate::{BlockIoErrorCallback, BlockProperty}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::{ - aio::{get_iov_size, Aio, AioCb, AioEngine, Iovec, OpCode}, + aio::{Aio, AioCb, AioEngine, Iovec, OpCode}, loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }, @@ -38,11 +38,16 @@ use util::{ pub struct CombineRequest { pub iov: Vec, pub offset: u64, + pub nbytes: u64, } impl CombineRequest { - pub fn new(iov: Vec, offset: u64) -> Self { - Self { iov, offset } + pub fn new(iov: Vec, offset: u64, nbytes: u64) -> Self { + Self { + iov, + offset, + nbytes, + } } } @@ -90,22 +95,24 @@ impl FileDriver { } } - fn rw_vectored( + fn process_request( &mut self, opcode: OpCode, req_list: Vec, completecb: T, ) -> Result<()> { + if req_list.is_empty() { + return self.complete_request(opcode, &Vec::new(), 0, 0, completecb); + } let single_req = req_list.len() == 1; let cnt = Arc::new(AtomicU32::new(req_list.len() as u32)); let res = Arc::new(AtomicI64::new(0)); for req in req_list { - let nbytes = get_iov_size(&req.iov); let mut aiocb = self.package_aiocb( opcode, req.iov, req.offset as usize, - nbytes, + req.nbytes, completecb.clone(), ); if !single_req { @@ -117,22 +124,23 @@ impl FileDriver { } pub fn read_vectored(&mut self, req_list: Vec, completecb: T) -> Result<()> { - self.rw_vectored(OpCode::Preadv, req_list, completecb) + self.process_request(OpCode::Preadv, req_list, completecb) } - pub fn complete_read_request( + pub fn complete_request( &mut self, + opcode: OpCode, iovec: &[Iovec], offset: usize, nbytes: u64, completecb: T, ) -> Result<()> { - let aiocb = self.package_aiocb(OpCode::Preadv, iovec.to_vec(), offset, nbytes, completecb); + let aiocb = self.package_aiocb(opcode, iovec.to_vec(), offset, nbytes, completecb); (self.aio.borrow_mut().complete_func)(&aiocb, nbytes as i64) } pub fn write_vectored(&mut self, req_list: Vec, completecb: T) -> Result<()> { - self.rw_vectored(OpCode::Pwritev, req_list, completecb) + self.process_request(OpCode::Pwritev, req_list, completecb) } pub fn write_zeroes( @@ -151,9 +159,8 @@ impl FileDriver { self.aio.borrow_mut().submit_request(aiocb) } - pub fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { - let aiocb = self.package_aiocb(OpCode::Discard, Vec::new(), offset, nbytes, completecb); - self.aio.borrow_mut().submit_request(aiocb) + pub fn discard(&mut self, req_list: Vec, completecb: T) -> Result<()> { + self.process_request(OpCode::Discard, req_list, completecb) } pub fn datasync(&mut self, completecb: T) -> Result<()> { diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 12cd81071..8c2d4fed5 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -31,7 +31,7 @@ use byteorder::{BigEndian, ByteOrder}; use log::{debug, error, info}; use once_cell::sync::Lazy; -use self::cache::ENTRY_SIZE_U64; +use self::{cache::ENTRY_SIZE_U64, refcount::Qcow2DiscardType}; use crate::{ file::{CombineRequest, FileDriver}, qcow2::{ @@ -204,7 +204,7 @@ impl Qcow2Driver { let fd = file.as_raw_fd(); let sync_aio = Rc::new(RefCell::new(SyncAioInfo::new(fd, conf.clone())?)); let mut qcow2 = Self { - driver: FileDriver::new(file, aio, conf), + driver: FileDriver::new(file, aio, conf.clone()), sync_aio: sync_aio.clone(), header: QcowHeader::default(), table: Qcow2Table::new(sync_aio.clone()), @@ -220,7 +220,7 @@ impl Qcow2Driver { .table .init_table(&qcow2.header) .with_context(|| "Failed to create qcow2 table")?; - qcow2.refcount.init_refcount_info(&qcow2.header); + qcow2.refcount.init_refcount_info(&qcow2.header, conf); qcow2 .load_refcount_table() .with_context(|| "Failed to load refcount table")?; @@ -316,7 +316,7 @@ impl Qcow2Driver { .borrow_mut() .write_buffer(new_data_addr, &data)?; self.refcount - .update_refcount(cluster_addr, 1, false, 1, true)?; + .update_refcount(cluster_addr, 1, -1, true, &Qcow2DiscardType::Other)?; l2_entry = new_data_addr | QCOW2_OFFSET_COPIED; cluster_addr = new_data_addr & L2_TABLE_OFFSET_MASK; } @@ -382,8 +382,13 @@ impl Qcow2Driver { // Decrease the refcount of the old table. if old_l2_offset != 0 { - self.refcount - .update_refcount(old_l2_offset, 1, false, 1, true)?; + self.refcount.update_refcount( + old_l2_offset, + 1, + -1, + true, + &Qcow2DiscardType::Other, + )?; } // Get the offset of the newly-allocated l2 table. l2_address = new_l2_offset; @@ -404,6 +409,77 @@ impl Qcow2Driver { Ok(l2_table_entry) } + /// Discard the data as many as possibale, and return the total number of cluster. + /// Note: the guest_offset should align to cluster size. + fn discard_in_l2_slice( + &mut self, + guest_offset: u64, + nb_cluster: u64, + discard_type: &Qcow2DiscardType, + ) -> Result { + let l2_index = self.table.get_l2_table_index(guest_offset); + let l2_slice_size = self.header.cluster_size() >> ENTRY_BITS; + let nb_cluster = std::cmp::min(nb_cluster, l2_slice_size - l2_index); + let table_entry = self.get_table_cluster(guest_offset)?; + for i in 0..nb_cluster { + let new_l2_index = l2_index + i; + let old_l2_entry = table_entry + .borrow_mut() + .get_entry_map(new_l2_index as usize)?; + let entry_type = Qcow2ClusterType::get_cluster_type(old_l2_entry); + let mut new_l2_entry = old_l2_entry; + + if entry_type.is_allocated() { + new_l2_entry = if self.header.version >= 3 { + QCOW2_OFLAG_ZERO + } else { + 0 + }; + } + if new_l2_entry == old_l2_entry { + continue; + } + + // Update l2 entry. + table_entry + .borrow_mut() + .set_entry_map(new_l2_index as usize, new_l2_entry)?; + // Decrease the refcount. + self.qcow2_free_cluster(old_l2_entry, discard_type)?; + } + Ok(nb_cluster) + } + + /// Update refount of cluster, if the value is equal to 0, + /// then clear the cluster. + pub fn qcow2_free_cluster( + &mut self, + l2_entry: u64, + discard_type: &Qcow2DiscardType, + ) -> Result<()> { + let cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); + match cluster_type { + Qcow2ClusterType::ZeroAlloc | Qcow2ClusterType::Normal => { + let offset = l2_entry & L2_TABLE_OFFSET_MASK; + let nbytes = self.header.cluster_size(); + // Align to cluster size. + if !is_aligned(nbytes, offset) { + bail!( + "Host offset {} is unaligned to cluster size {}", + offset, + nbytes + ); + } + self.free_cluster(offset, 1, discard_type)?; + } + Qcow2ClusterType::Compressed => { + bail!("Compressed is not supported"); + } + _ => {} + } + Ok(()) + } + fn offset_into_cluster(&self, guest_offset: u64) -> u64 { guest_offset & (self.header.cluster_size() - 1) } @@ -451,9 +527,14 @@ impl Qcow2Driver { Ok(()) } - fn free_cluster(&mut self, addr: u64, clusters: u64) -> Result<()> { + fn free_cluster( + &mut self, + addr: u64, + clusters: u64, + discard_type: &Qcow2DiscardType, + ) -> Result<()> { self.refcount - .update_refcount(addr, clusters, false, 1, true) + .update_refcount(addr, clusters, -1, true, discard_type) } fn get_snapshot_by_name(&mut self, name: &String) -> i32 { @@ -509,9 +590,9 @@ impl Qcow2Driver { if let Err(e) = self.refcount.update_refcount( snap.l1_table_offset, l1_table_clusters, + -1, false, - 1, - false, + &Qcow2DiscardType::Snapshot, ) { err_msg = format!("{:?}", e); error_stage = 3; @@ -525,10 +606,13 @@ impl Qcow2Driver { break; } - if let Err(e) = - self.refcount - .update_refcount(self.header.snapshots_offset, 1, false, 1, true) - { + if let Err(e) = self.refcount.update_refcount( + self.header.snapshots_offset, + 1, + -1, + true, + &Qcow2DiscardType::Snapshot, + ) { err_msg = format!("{:?}", e); error_stage = 5; break; @@ -568,16 +652,21 @@ impl Qcow2Driver { // Error handling, to revert some operation. if error_stage >= 6 { - self.refcount - .update_refcount(self.header.snapshots_offset, 1, true, 1, true)?; + self.refcount.update_refcount( + self.header.snapshots_offset, + 1, + 1, + true, + &Qcow2DiscardType::Never, + )?; } if error_stage >= 4 { self.refcount.update_refcount( snap.l1_table_offset, l1_table_clusters, - true, 1, true, + &Qcow2DiscardType::Never, )?; } if error_stage >= 3 { @@ -587,7 +676,11 @@ impl Qcow2Driver { } } if error_stage >= 2 { - self.free_cluster(new_snapshot_table_offset, new_snapshots_table_clusters)?; + self.free_cluster( + new_snapshot_table_offset, + new_snapshots_table_clusters, + &Qcow2DiscardType::Never, + )?; } if error_stage >= 1 { self.snapshot.insert_snapshot(snap, snapshot_idx as usize) @@ -697,9 +790,9 @@ impl Qcow2Driver { if let Err(e) = self.refcount.update_refcount( self.header.snapshots_offset, old_snapshots_table_clusters, - false, - 1, + -1, true, + &Qcow2DiscardType::Snapshot, ) { error_stage = 4; err_msg = format!("{:?}", e); @@ -731,9 +824,9 @@ impl Qcow2Driver { self.refcount.update_refcount( self.header.snapshots_offset, old_snapshots_table_clusters, - true, 1, true, + &Qcow2DiscardType::Never, )?; } @@ -746,13 +839,21 @@ impl Qcow2Driver { bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); self.snapshot .del_snapshot(self.snapshot.snapshots_number() - 1); - self.free_cluster(new_snapshot_table_offset, num_clusters)?; + self.free_cluster( + new_snapshot_table_offset, + num_clusters, + &Qcow2DiscardType::Snapshot, + )?; } if error_stage >= 2 { self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, -1)?; } if error_stage >= 1 { - self.free_cluster(new_l1_table_offset, l1_table_clusters)?; + self.free_cluster( + new_l1_table_offset, + l1_table_clusters, + &Qcow2DiscardType::Snapshot, + )?; } bail!("{}", err_msg); @@ -778,8 +879,6 @@ impl Qcow2Driver { .read_ctrl_cluster(l1_table_offset, l1_table_size as u64)?; } - let is_add = added > 0; - let num = added.unsigned_abs() as u16; let mut old_l2_table_offset: u64; for (i, l1_entry) in l1_table.iter_mut().enumerate().take(l1_table_size) { let mut l2_table_offset = *l1_entry; @@ -833,10 +932,15 @@ impl Qcow2Driver { ); } - if num != 0 { + if added != 0 { // Update Data Cluster refcount. - self.refcount - .update_refcount(data_cluster_offset, 1, is_add, num, false)?; + self.refcount.update_refcount( + data_cluster_offset, + 1, + added, + false, + &Qcow2DiscardType::Snapshot, + )?; } let refcount = self.refcount.get_refcount(data_cluster_offset)?; @@ -852,10 +956,15 @@ impl Qcow2Driver { .write_buffer(l2_table_offset, borrowed_table.get_value())?; drop(borrowed_table); - if num != 0 { + if added != 0 { // Update L2 table cluster refcount. - self.refcount - .update_refcount(l2_table_offset, 1, is_add, num, false)?; + self.refcount.update_refcount( + l2_table_offset, + 1, + added, + false, + &Qcow2DiscardType::Snapshot, + )?; } let refcount = self.refcount.get_refcount(l2_table_offset)?; @@ -870,6 +979,9 @@ impl Qcow2Driver { } } } + self.refcount + .sync_process_discards(OpCode::Discard) + .unwrap_or_else(|e| error!("Snapshot discard failed: {:?}", e)); self.refcount.flush_refcount_block_cache()?; if l1_changed { self.sync_aio @@ -1066,6 +1178,54 @@ impl InternalSnapshotOps for Qcow2Driver { unsafe impl Send for Qcow2Driver {} unsafe impl Sync for Qcow2Driver {} +impl Qcow2Driver { + fn qcow2_cluster_discard(&mut self, offset: u64, nbytes: u64, args: T) -> Result<()> { + let cluster_bits = self.header.cluster_bits; + let cluster_size = self.header.cluster_size(); + let mut nb_cluster = nbytes >> cluster_bits; + let mut host_offset = offset; + + while nb_cluster > 0 { + match self.discard_in_l2_slice(host_offset, nb_cluster, &Qcow2DiscardType::Request) { + Ok(cleared) => { + nb_cluster -= cleared; + host_offset += cleared * cluster_size; + } + Err(e) => { + error!("Discard in l2 slice: {:?}", e); + break; + } + } + } + + self.table + .flush_l2_table_cache() + .unwrap_or_else(|e| error!("Flush l2 table cache failed: {:?}", e)); + self.process_discards(args, OpCode::Discard, false) + } + + fn process_discards(&mut self, completecb: T, opcode: OpCode, _unmap: bool) -> Result<()> { + let mut req_list = Vec::new(); + for task in self.refcount.discard_list.iter() { + req_list.push(CombineRequest { + iov: Vec::new(), + offset: task.offset, + nbytes: task.nbytes, + }) + } + match opcode { + OpCode::Discard => { + self.driver.discard(req_list, completecb)?; + } + _ => { + bail!("Unsuppoerted opcode: {:?}", opcode); + } + } + self.refcount.discard_list.clear(); + Ok(()) + } +} + impl BlockDriverOps for Qcow2Driver { fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { let nbytes = get_iov_size(iovec); @@ -1082,22 +1242,19 @@ impl BlockDriverOps for Qcow2Driver { let (begin, end) = iovecs_split(left, count); left = end; if let HostOffset::DataAddress(host_offset) = self.host_offset_for_read(pos)? { + let nbytes = get_iov_size(&begin); req_list.push(CombineRequest { iov: begin, offset: host_offset, + nbytes, }); } else { iov_from_buf_direct(&begin, &vec![0_u8; count as usize])?; } copyed += count; } - if req_list.is_empty() { - // Not submitting an AIO request, call callback directly. - self.driver - .complete_read_request(iovec, offset, nbytes, completecb) - } else { - self.driver.read_vectored(req_list, completecb) - } + + self.driver.read_vectored(req_list, completecb) } fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { @@ -1115,9 +1272,11 @@ impl BlockDriverOps for Qcow2Driver { let (begin, end) = iovecs_split(left, count); left = end; if let HostOffset::DataAddress(host_offset) = self.host_offset_for_write(pos)? { + let nbytes = get_iov_size(&begin); req_list.push(CombineRequest { iov: begin, offset: host_offset, + nbytes, }); copyed += count; } @@ -1137,8 +1296,26 @@ impl BlockDriverOps for Qcow2Driver { Ok(self.virtual_disk_size()) } - fn discard(&mut self, _offset: usize, _nbytes: u64, _completecb: T) -> Result<()> { - bail!("discard not supported now"); + fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { + // Align to cluster_size. + let file_size = self.header.size; + let align_size = self.header.cluster_size(); + let mut offset_start = std::cmp::min(offset as u64, file_size); + let offset_end = std::cmp::min(offset as u64 + nbytes, file_size); + let mut bytes = offset_end + .checked_sub(offset_start) + .with_context(|| format!("Discard :{} out of range: {}", offset_end, file_size))?; + let head_align = (align_size - offset_start % align_size) % align_size; + let tail_align = offset_end % align_size; + if head_align + tail_align >= bytes { + bytes = 0; + } else { + bytes -= head_align; + bytes -= tail_align; + } + offset_start += head_align; + + self.qcow2_cluster_discard(offset_start, bytes, completecb) } fn write_zeroes( @@ -1187,6 +1364,7 @@ mod test { fs::remove_file, io::{Seek, SeekFrom, Write}, os::unix::fs::OpenOptionsExt, + process::Command, }; use machine_manager::config::DiskFormat; @@ -1200,22 +1378,27 @@ mod test { const CLUSTER_SIZE: u64 = 64 * 1024; struct TestImage { + pub img_bits: u64, + pub cluster_bits: u64, pub path: String, pub file: File, } impl TestImage { - fn new(path: &str, img_bits: u32, cluster_bits: u32) -> TestImage { - let cluster_sz = 1 << cluster_bits; + fn new(path: &str, img_bits: u64, cluster_bits: u64) -> TestImage { + let cluster_sz: u64 = 1 << cluster_bits; + let img_size: u64 = 1 << img_bits; + let l1_entry_size: u64 = 1 << (cluster_bits * 2 - 3); + let l1_size = (img_size + l1_entry_size - 1) / l1_entry_size; let header = QcowHeader { magic: crate::qcow2::header::QCOW_MAGIC, version: 3, backing_file_offset: 0, backing_file_size: 0, - cluster_bits, + cluster_bits: cluster_bits as u32, size: 1 << img_bits, crypt_method: 0, - l1_size: 1 << (img_bits - (cluster_bits * 2 - 3)), + l1_size: l1_size as u32, l1_table_offset: 3 * cluster_sz, refcount_table_offset: cluster_sz, refcount_table_clusters: 1, @@ -1227,6 +1410,7 @@ mod test { refcount_order: 4, header_length: std::mem::size_of::() as u32, }; + let mut file = std::fs::OpenOptions::new() .read(true) .write(true) @@ -1255,11 +1439,27 @@ mod test { } file.seek(SeekFrom::Start(cluster_sz * 2)).unwrap(); file.write_all(&refcount_block).unwrap(); + TestImage { + img_bits, + cluster_bits, path: path.to_string(), file, } } + + fn create_qcow2_driver(&self, conf: BlockProperty) -> Qcow2Driver<()> { + fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { + Ok(()) + } + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&self.path) + .unwrap(); + let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); + Qcow2Driver::new(file, aio, conf).unwrap() + } } impl Drop for TestImage { @@ -1268,6 +1468,34 @@ mod test { } } + fn execute_cmd(cmd: String) -> Vec { + let args = cmd.split(' ').collect::>(); + if args.len() <= 0 { + return vec![]; + } + + let mut cmd_exe = Command::new(args[0]); + for i in 1..args.len() { + cmd_exe.arg(args[i]); + } + + let output = cmd_exe + .output() + .expect(format!("Failed to execute {}", cmd).as_str()); + println!("{:?}, output: {:?}", args, output); + assert!(output.status.success()); + output.stdout + } + + fn get_disk_size(img_path: Rc) -> u64 { + let out = execute_cmd(format!("du -shk {}", img_path)); + let str_out = std::str::from_utf8(&out) + .unwrap() + .split('\t') + .collect::>(); + serde_json::from_str::(str_out[0]).unwrap() + } + struct TestData { data: u8, sz: usize, @@ -1566,4 +1794,34 @@ mod test { } } } + + #[test] + fn test_discard_basic() { + let path = "/tmp/discard.qcow2"; + // Create a new image, with size = 16M, cluster_size = 64K. + let image_bits = 24; + let cluster_bits = 16; + let conf = BlockProperty { + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align: 512, + buf_align: 512, + discard: true, + write_zeroes: WriteZeroesState::Off, + }; + let image = TestImage::new(path, image_bits, cluster_bits); + let mut qcow2_driver = image.create_qcow2_driver(conf); + // Full the disk. + let offset_start = 0; + for i in 0..1 << (image.img_bits - image.cluster_bits) { + let offset = offset_start + i * (1 << image.cluster_bits); + let buf = vec![0_u8; 1 << image.cluster_bits]; + assert!(qcow2_write(&mut qcow2_driver, &buf, offset as usize).is_ok()); + } + let image_size_1 = get_disk_size(Rc::new(path.to_string())); + assert!(qcow2_driver.discard(0, 1 << image_bits, ()).is_ok()); + let image_size_2 = get_disk_size(Rc::new(path.to_string())); + assert_eq!(image_size_1, image_size_2 + (1 << image_bits) / 1024); + } } diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 67cdad305..ab47e9d86 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -14,22 +14,58 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::{bail, Context, Result}; use log::{error, info}; - -use crate::qcow2::{ - bytes_to_clusters, - cache::{CacheTable, Qcow2Cache, ENTRY_SIZE_U16}, - header::QcowHeader, - is_aligned, SyncAioInfo, ENTRY_SIZE, +use util::aio::OpCode; + +use crate::{ + qcow2::{ + bytes_to_clusters, + cache::{CacheTable, Qcow2Cache, ENTRY_SIZE_U16}, + header::QcowHeader, + is_aligned, SyncAioInfo, ENTRY_SIZE, + }, + BlockProperty, }; // The max refcount table size default is 4 clusters; const MAX_REFTABLE_NUM: u32 = 4; +#[derive(Eq, PartialEq, Clone)] +pub enum Qcow2DiscardType { + Never, + Always, + Request, + Snapshot, + Other, +} + +#[derive(Clone, Default)] +pub struct DiscardTask { + pub offset: u64, + pub nbytes: u64, +} + +impl DiscardTask { + pub fn is_overlap(&self, task: &DiscardTask) -> bool { + !(self.offset > task.offset + task.nbytes || task.offset > self.offset + self.nbytes) + } + + pub fn merge_task(&mut self, task: &DiscardTask) { + let offset = std::cmp::min(self.offset, task.offset); + let end_offset = std::cmp::max(self.offset + self.nbytes, task.offset + task.nbytes); + let nbytes = end_offset - offset; + self.offset = offset; + self.nbytes = nbytes; + } +} + #[derive(Clone)] pub struct RefCount { pub refcount_table: Vec, sync_aio: Rc>, refcount_blk_cache: Qcow2Cache, + pub discard_list: Vec, + /// Pass the discard operation if refcount of cluster decrease to 0. + pub discard_passthrough: Vec, free_cluster_index: u64, refcount_table_offset: u64, refcount_table_clusters: u32, @@ -50,6 +86,8 @@ impl RefCount { refcount_table: Vec::new(), sync_aio, refcount_blk_cache: Qcow2Cache::new(MAX_REFTABLE_NUM as usize), + discard_list: Vec::new(), + discard_passthrough: Vec::new(), free_cluster_index: 0, refcount_table_offset: 0, refcount_table_clusters: 0, @@ -62,7 +100,14 @@ impl RefCount { } } - pub fn init_refcount_info(&mut self, header: &QcowHeader) { + pub fn init_refcount_info(&mut self, header: &QcowHeader, conf: BlockProperty) { + // Update discard_pass_through depend on config. + self.discard_passthrough.push(Qcow2DiscardType::Always); + if conf.discard { + self.discard_passthrough.push(Qcow2DiscardType::Request); + self.discard_passthrough.push(Qcow2DiscardType::Snapshot); + } + self.refcount_table_offset = header.refcount_table_offset; self.refcount_table_clusters = header.refcount_table_clusters; self.refcount_table_size = @@ -139,7 +184,13 @@ impl RefCount { // Free the old cluster of refcount table. let clusters = bytes_to_clusters(old_rct_size, self.cluster_size).unwrap(); - self.update_refcount(old_rct_offset, clusters, false, 1, true)?; + self.update_refcount( + old_rct_offset, + clusters, + -1, + true, + &Qcow2DiscardType::Snapshot, + )?; info!( "Qcow2 extends refcount table success, offset 0x{:x} -> 0x{:x}", old_rct_offset, self.refcount_table_offset @@ -152,9 +203,9 @@ impl RefCount { &mut self, offset: u64, clusters: u64, - is_add: bool, - addend: u16, + added: i32, flush: bool, + discard_type: &Qcow2DiscardType, ) -> Result<()> { if self.offset_into_cluster(offset) != 0 { bail!("Failed to update refcount, offset is not aligned to cluster"); @@ -184,10 +235,10 @@ impl RefCount { i += num as u64; } - let idx = self.set_refcount_blocks(&rc_vec, is_add, addend); + let idx = self.set_refcount_blocks(&rc_vec, added, discard_type); if idx != rc_vec.len() { // Revert the updating operation for refount block. - let rev_idx = self.set_refcount_blocks(&rc_vec[..idx], !is_add, addend); + let rev_idx = self.set_refcount_blocks(&rc_vec[..idx], -added, discard_type); let status = if rev_idx == idx { "success" } else { "failed" }; bail!("Failed to set refcounts, recover {}", status); } @@ -200,14 +251,20 @@ impl RefCount { fn set_refcount_blocks( &mut self, rc_vec: &[(u64, u64, usize)], - is_add: bool, - addend: u16, + added: i32, + discard_type: &Qcow2DiscardType, ) -> usize { for (i, (rt_idx, rb_idx, num)) in rc_vec.iter().enumerate() { - let ret = self.set_refcount(*rt_idx, *rb_idx, *num, is_add, addend); + let ret = self.set_refcount(*rt_idx, *rb_idx, *num, added, discard_type); if let Err(err) = ret { - error!("Set refcount failed, rt_idx {}, rb_idx {}, clusters {}, is_add {}, addend {}, {}", - rt_idx, rb_idx, num, is_add, addend, err.to_string()); + error!( + "Set refcount failed, rt_idx {}, rb_idx {}, clusters {}, added {}, {}", + rt_idx, + rb_idx, + num, + added, + err.to_string() + ); return i; } } @@ -240,9 +297,11 @@ impl RefCount { rt_idx: u64, rb_idx: u64, clusters: usize, - is_add: bool, - addend: u16, + added: i32, + discard_type: &Qcow2DiscardType, ) -> Result<()> { + let is_add = added > 0; + let added_value = added.unsigned_abs() as u16; if !self.refcount_blk_cache.contains_keys(rt_idx) { self.load_refcount_block(rt_idx).with_context(|| { format!("Failed to get refcount block cache, index is {}", rt_idx) @@ -260,25 +319,34 @@ impl RefCount { let mut rc_value = borrowed_entry.get_entry_map(rb_idx as usize + i)? as u16; rc_value = if is_add { rc_value - .checked_add(addend) + .checked_add(added_value) .filter(|&v| v <= self.refcount_max as u16) .with_context(|| { format!( "Refcount {} add {} cause overflows, index is {}", - rc_value, addend, i + rc_value, added_value, i ) })? } else { - rc_value.checked_sub(addend).with_context(|| { + rc_value.checked_sub(added_value).with_context(|| { format!( "Refcount {} sub {} cause overflows, index is {}", - rc_value, addend, i + rc_value, added_value, i ) })? }; let cluster_idx = rt_idx * self.refcount_blk_size as u64 + rb_idx + i as u64; - if rc_value == 0 && cluster_idx < self.free_cluster_index { - self.free_cluster_index = cluster_idx; + if rc_value == 0 { + if self.discard_passthrough.contains(discard_type) { + // update refcount discard. + let offset = cluster_idx * self.cluster_size; + let nbytes = self.cluster_size; + self.update_discard_list(offset, nbytes)?; + } + + if cluster_idx < self.free_cluster_index { + self.free_cluster_index = cluster_idx; + } } rb_vec.push(rc_value); } @@ -327,6 +395,36 @@ impl RefCount { Ok(rc_value as u16) } + /// Add discard task to the list. + fn update_discard_list(&mut self, offset: u64, nbytes: u64) -> Result<()> { + let mut discard_task = DiscardTask { offset, nbytes }; + let mut discard_list: Vec = Vec::new(); + for task in self.discard_list.iter() { + if discard_task.is_overlap(task) { + discard_task.merge_task(task); + } else { + discard_list.push(task.clone()); + } + } + discard_list.push(discard_task); + self.discard_list = discard_list; + Ok(()) + } + + /// Process discards task by sync aio. + pub fn sync_process_discards(&mut self, opcode: OpCode) -> Result<()> { + for task in self.discard_list.iter() { + let offset = task.offset; + let nbytes = task.nbytes; + let mut borrowed_sync_aio = self.sync_aio.borrow_mut(); + let discard_aio = + borrowed_sync_aio.package_sync_aiocb(opcode, Vec::new(), offset as usize, nbytes); + borrowed_sync_aio.aio.submit_request(discard_aio)?; + } + self.discard_list.clear(); + Ok(()) + } + pub fn offset_into_cluster(&self, offset: u64) -> u64 { offset & (self.cluster_size - 1) } @@ -430,7 +528,7 @@ impl RefCount { pub fn alloc_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { let addr = self.find_free_cluster(header, size)?; let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); - self.update_refcount(addr, clusters, true, 1, true)?; + self.update_refcount(addr, clusters, 1, true, &Qcow2DiscardType::Other)?; Ok(addr) } @@ -497,8 +595,8 @@ mod test { use anyhow::Result; use byteorder::{BigEndian, ByteOrder}; - use crate::qcow2::header::*; use crate::qcow2::*; + use crate::qcow2::{header::*, refcount::Qcow2DiscardType}; use machine_manager::config::DiskFormat; use util::aio::{Aio, AioCb, WriteZeroesState}; @@ -671,11 +769,11 @@ mod test { let mut refcount = qcow2.refcount.clone(); // Add refcount for the first cluster. - let ret = refcount.update_refcount(0, 1, true, 1, true); + let ret = refcount.update_refcount(0, 1, 1, true, &Qcow2DiscardType::Never); assert!(ret.is_ok()); // Test invalid cluster offset. - let ret = refcount.update_refcount(1 << 63, 1, true, 1, true); + let ret = refcount.update_refcount(1 << 63, 1, 1, true, &Qcow2DiscardType::Never); if let Err(err) = ret { // 16 bit is cluster bits, 15 is refcount block bits. let err_msg = format!("Invalid refcount table index {}", 1_u64 << (63 - 15 - 16)); @@ -685,7 +783,13 @@ mod test { } // Test refcount block not in cache. - let ret = refcount.update_refcount(1 << (cluster_bits * 2), 1, true, 1, true); + let ret = refcount.update_refcount( + 1 << (cluster_bits * 2), + 1, + 1, + true, + &Qcow2DiscardType::Never, + ); if let Err(err) = ret { let err_msg = format!("Invalid refcount block address 0x0, index is 2"); assert_eq!(err.to_string(), err_msg); @@ -705,11 +809,11 @@ mod test { let mut refcount = qcow2.refcount.clone(); // Add refcount for the first cluster. - let ret = refcount.set_refcount(0, 0, 1, true, 1); + let ret = refcount.set_refcount(0, 0, 1, 1, &Qcow2DiscardType::Never); assert!(ret.is_ok()); // Test refcount overflow. - let ret = refcount.set_refcount(0, 0, 1, true, 65535); + let ret = refcount.set_refcount(0, 0, 1, 65535, &Qcow2DiscardType::Never); if let Err(err) = ret { let err_msg = format!("Refcount 2 add 65535 cause overflows, index is 0"); assert_eq!(err.to_string(), err_msg); @@ -718,7 +822,7 @@ mod test { } // Test refcount underflow. - let ret = refcount.set_refcount(0, 0, 1, false, 65535); + let ret = refcount.set_refcount(0, 0, 1, -65535, &Qcow2DiscardType::Never); if let Err(err) = ret { let err_msg = format!("Refcount 2 sub 65535 cause overflows, index is 0"); assert_eq!(err.to_string(), err_msg); diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index d8b76c403..edfeda032 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -41,6 +41,12 @@ pub enum Qcow2ClusterType { } impl Qcow2ClusterType { + pub fn is_allocated(&self) -> bool { + self.eq(&Qcow2ClusterType::Compressed) + || self.eq(&Qcow2ClusterType::Normal) + || self.eq(&Qcow2ClusterType::ZeroAlloc) + } + pub fn is_read_zero(&self) -> bool { if self.eq(&Qcow2ClusterType::Unallocated) || self.eq(&Qcow2ClusterType::ZeroAlloc) @@ -188,7 +194,7 @@ impl Qcow2Table { Ok(()) } - fn flush_l2_table_cache(&self) -> Result<()> { + pub fn flush_l2_table_cache(&self) -> Result<()> { for (_idx, entry) in self.l2_table_cache.iter() { let mut borrowed_entry = entry.borrow_mut(); if !borrowed_entry.dirty_info.is_dirty { diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 07f64ebb0..3000fdcc8 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -21,7 +21,7 @@ use crate::{ file::{CombineRequest, FileDriver}, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, }; -use util::aio::{Aio, Iovec}; +use util::aio::{get_iov_size, Aio, Iovec}; pub struct RawDriver { driver: FileDriver, @@ -44,15 +44,17 @@ impl RawDriver { impl BlockDriverOps for RawDriver { fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + let nbytes = get_iov_size(iovec); self.driver.read_vectored( - vec![CombineRequest::new(iovec.to_vec(), offset as u64)], + vec![CombineRequest::new(iovec.to_vec(), offset as u64, nbytes)], completecb, ) } fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + let nbytes = get_iov_size(iovec); self.driver.write_vectored( - vec![CombineRequest::new(iovec.to_vec(), offset as u64)], + vec![CombineRequest::new(iovec.to_vec(), offset as u64, nbytes)], completecb, ) } @@ -68,7 +70,10 @@ impl BlockDriverOps for RawDriver { } fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { - self.driver.discard(offset, nbytes, completecb) + self.driver.discard( + vec![CombineRequest::new(Vec::new(), offset as u64, nbytes)], + completecb, + ) } fn datasync(&mut self, completecb: T) -> Result<()> { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 197bf6f84..11e281e87 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -127,7 +127,7 @@ pub struct AioEvent { pub res: i64, } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum OpCode { Noop = 0, Preadv = 1, -- Gitee From 7ece2538fa7b810bb79bc4872e03be47316edadb Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 02:27:49 +0800 Subject: [PATCH 1168/2187] Qcow2: add write zero Add write zero. Signed-off-by: Xiao Ye --- block_backend/src/file.rs | 6 +- block_backend/src/qcow2/mod.rs | 218 +++++++++++++++++++++++++++++++-- block_backend/src/raw.rs | 6 +- 3 files changed, 218 insertions(+), 12 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 5dbec86fa..ac498512e 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -145,8 +145,7 @@ impl FileDriver { pub fn write_zeroes( &mut self, - offset: usize, - nbytes: u64, + req_list: Vec, completecb: T, unmap: bool, ) -> Result<()> { @@ -155,8 +154,7 @@ impl FileDriver { } else { OpCode::WriteZeroes }; - let aiocb = self.package_aiocb(opcode, Vec::new(), offset, nbytes, completecb); - self.aio.borrow_mut().submit_request(aiocb) + self.process_request(opcode, req_list, completecb) } pub fn discard(&mut self, req_list: Vec, completecb: T) -> Result<()> { diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 8c2d4fed5..c3f5703b1 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -409,6 +409,61 @@ impl Qcow2Driver { Ok(l2_table_entry) } + /// Write back to disk synchronously, with a range no greater than cluster size. + fn sync_write_bytes(&mut self, guest_offset: u64, buf: &[u8]) -> Result<()> { + if buf.len() > self.header.cluster_size() as usize + || guest_offset as usize + buf.len() > self.virtual_disk_size() as usize + { + bail!("Buffer size: is out of range",); + } + // Return if the address is not allocated. + if let HostOffset::DataAddress(host_offset) = self.host_offset_for_write(guest_offset)? { + self.sync_aio.borrow_mut().write_buffer(host_offset, buf)?; + } + Ok(()) + } + + /// Write zero data to cluster data as many as possible, and return the total number of + /// cluster. + /// Note: the guest offset should align to cluster size. + fn zero_in_l2_slice(&mut self, guest_offset: u64, nb_cluster: u64) -> Result { + // Zero flag is only support by version 3. + // If this flag is not supported, then transfer write_zero to discard. + if self.header.version < 3 { + return self.discard_in_l2_slice(guest_offset, nb_cluster, &Qcow2DiscardType::Request); + } + + let l2_index = self.table.get_l2_table_index(guest_offset); + let l2_slice_size = self.header.cluster_size() >> ENTRY_BITS; + let nb_cluster = std::cmp::min(nb_cluster, l2_slice_size - l2_index); + let table_entry = self.get_table_cluster(guest_offset)?; + for i in 0..nb_cluster { + let new_l2_index = l2_index + i; + let old_l2_entry = table_entry + .borrow_mut() + .get_entry_map(new_l2_index as usize)?; + let entry_type = Qcow2ClusterType::get_cluster_type(old_l2_entry); + let mut new_l2_entry = old_l2_entry; + let unmap: bool = entry_type.is_allocated(); + if unmap { + new_l2_entry = 0; + } + new_l2_entry |= QCOW2_OFLAG_ZERO; + + if new_l2_entry == old_l2_entry { + continue; + } + + table_entry + .borrow_mut() + .set_entry_map(new_l2_index as usize, new_l2_entry)?; + if unmap { + self.qcow2_free_cluster(old_l2_entry, &Qcow2DiscardType::Request)?; + } + } + Ok(nb_cluster) + } + /// Discard the data as many as possibale, and return the total number of cluster. /// Note: the guest_offset should align to cluster size. fn discard_in_l2_slice( @@ -1204,7 +1259,35 @@ impl Qcow2Driver { self.process_discards(args, OpCode::Discard, false) } - fn process_discards(&mut self, completecb: T, opcode: OpCode, _unmap: bool) -> Result<()> { + /// Align to cluster size and write zeroes. + fn qcow2_cluster_write_zeroes(&mut self, offset: u64, nbytes: u64) -> Result<()> { + // Offset and offset + nbytes should align to cluster size. + if !is_aligned(self.header.cluster_size(), offset | nbytes) { + return Ok(()); + } + + let mut nb_cluster = bytes_to_clusters(nbytes, self.header.cluster_size())?; + let mut guest_offset = offset; + while nb_cluster > 0 { + match self.zero_in_l2_slice(guest_offset, nb_cluster) { + Ok(cleared) => { + nb_cluster -= cleared; + guest_offset += cleared * self.header.cluster_size(); + } + Err(e) => { + error!("Write zero: {:?}", e); + break; + } + } + } + + self.table + .flush_l2_table_cache() + .unwrap_or_else(|e| error!("Flush l2 table cache failed: {:?}", e)); + Ok(()) + } + + fn process_discards(&mut self, completecb: T, opcode: OpCode, unmap: bool) -> Result<()> { let mut req_list = Vec::new(); for task in self.refcount.discard_list.iter() { req_list.push(CombineRequest { @@ -1213,9 +1296,17 @@ impl Qcow2Driver { nbytes: task.nbytes, }) } + match opcode { OpCode::Discard => { - self.driver.discard(req_list, completecb)?; + self.driver + .discard(req_list, completecb) + .unwrap_or_else(|e| error!("Discard failed: {}", e)); + } + OpCode::WriteZeroes => { + self.driver + .write_zeroes(req_list, completecb, unmap) + .unwrap_or_else(|e| error!("Write zero failed: {}", e)); } _ => { bail!("Unsuppoerted opcode: {:?}", opcode); @@ -1320,12 +1411,51 @@ impl BlockDriverOps for Qcow2Driver { fn write_zeroes( &mut self, - _offset: usize, - _nbytes: u64, - _completecb: T, - _unmap: bool, + offset: usize, + nbytes: u64, + completecb: T, + unmap: bool, ) -> Result<()> { - bail!("write zero not supported now"); + let file_size = self.header.size; + let align_size = self.header.cluster_size(); + let mut offset_start = std::cmp::min(offset as u64, file_size); + let offset_end = std::cmp::min(offset_start + nbytes, file_size); + let mut total_bytes = offset_end.checked_sub(offset_start).with_context(|| { + format!( + "Write zeroes: ofset: {} nbytes: {} out of range", + offset, nbytes + ) + })?; + let mut head = offset_start % align_size; + let tail = offset_end % align_size; + + while total_bytes > 0 { + let mut num = total_bytes; + if head != 0 { + num = std::cmp::min(num, align_size - head); + head = (head + num) % align_size; + } else if tail != 0 && num > align_size { + num -= tail; + } + + // Writing buffer with zero to disk for the addr that + // is not aligned with cluster size. + // The write order is: head -> offset align to cluster size -> tail. + if !is_aligned(self.header.cluster_size(), offset_start | num) { + let buf: Vec = vec![0; num as usize]; + if let Err(e) = self.sync_write_bytes(offset_start, &buf) { + error!("Write zero failed: {:?}", e); + break; + } + } else if let Err(e) = self.qcow2_cluster_write_zeroes(offset_start, num) { + error!("Write zero failed: {:?}", e); + break; + } + + total_bytes -= num; + offset_start += num; + } + self.process_discards(completecb, OpCode::WriteZeroes, unmap) } fn flush_request(&mut self) -> Result<()> { @@ -1496,6 +1626,15 @@ mod test { serde_json::from_str::(str_out[0]).unwrap() } + fn vec_is_zero(vec: &[u8]) -> bool { + for elem in vec { + if elem != &0 { + return false; + } + } + true + } + struct TestData { data: u8, sz: usize, @@ -1824,4 +1963,69 @@ mod test { let image_size_2 = get_disk_size(Rc::new(path.to_string())); assert_eq!(image_size_1, image_size_2 + (1 << image_bits) / 1024); } + + #[test] + fn test_write_zero_basic() { + // Create a new image, with size = 16M, cluster_size = 64K. + let path = "/tmp/discard_write_zero.qcow2"; + let image_bits = 24; + let cluster_bits = 16; + let conf = BlockProperty { + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align: 512, + buf_align: 512, + discard: true, + write_zeroes: WriteZeroesState::On, + }; + let image = TestImage::new(path, image_bits, cluster_bits); + let mut qcow2_driver = image.create_qcow2_driver(conf); + + // Test 1. + let mut test_buf: Vec = vec![1_u8; 65536 * 6]; + let test_data = vec![ + TestData::new(0, 65536), + TestData::new(0, 65536 + 32768), + TestData::new(0, 65536 * 2), + TestData::new(0, 65536 + 32768), + ]; + let offset_start = 0; + let mut guest_offset = offset_start; + assert!(qcow2_write(&mut qcow2_driver, &test_buf, offset_start).is_ok()); + for data in test_data.iter() { + assert!(qcow2_driver + .write_zeroes(guest_offset, data.sz as u64, (), true) + .is_ok()); + let mut tmp_buf = vec![1_u8; data.sz]; + assert!(qcow2_read(&mut qcow2_driver, &mut tmp_buf, guest_offset).is_ok()); + assert!(vec_is_zero(&tmp_buf)); + guest_offset += data.sz; + } + assert!(qcow2_read(&mut qcow2_driver, &mut test_buf, offset_start).is_ok()); + assert!(vec_is_zero(&test_buf)); + + // Test 2. + let mut test_buf: Vec = vec![1_u8; 65536 * 6]; + let test_data = vec![ + TestData::new(0, 65536), + TestData::new(0, 65536 + 32768), + TestData::new(0, 65536 * 2), + TestData::new(0, 65536 + 32768), + ]; + let offset_start = 459752; + let mut guest_offset = offset_start; + assert!(qcow2_write(&mut qcow2_driver, &test_buf, offset_start).is_ok()); + for data in test_data.iter() { + assert!(qcow2_driver + .write_zeroes(guest_offset, data.sz as u64, (), true) + .is_ok()); + let mut tmp_buf = vec![1_u8; data.sz]; + assert!(qcow2_read(&mut qcow2_driver, &mut tmp_buf, guest_offset).is_ok()); + assert!(vec_is_zero(&tmp_buf)); + guest_offset += data.sz; + } + assert!(qcow2_read(&mut qcow2_driver, &mut test_buf, offset_start).is_ok()); + assert!(vec_is_zero(&test_buf)); + } } diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 3000fdcc8..896f00d6c 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -66,7 +66,11 @@ impl BlockDriverOps for RawDriver { completecb: T, unmap: bool, ) -> Result<()> { - self.driver.write_zeroes(offset, nbytes, completecb, unmap) + self.driver.write_zeroes( + vec![CombineRequest::new(Vec::new(), offset as u64, nbytes)], + completecb, + unmap, + ) } fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { -- Gitee From d3d9d8a269815ff7c0a0872e148ed6a308847aaa Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 2 Jul 2023 23:33:53 +0800 Subject: [PATCH 1169/2187] Qcow2: add qcow2 mst for virtio block Add qcow2 mst mst for virtio block. Signed-off-by: Xiao Ye --- Cargo.lock | 1 + tests/mod_test/Cargo.toml | 1 + tests/mod_test/src/libdriver/mod.rs | 1 + tests/mod_test/src/libdriver/qcow2.rs | 301 +++ tests/mod_test/src/libdriver/virtio_block.rs | 25 +- tests/mod_test/src/utils.rs | 28 +- tests/mod_test/tests/block_test.rs | 2532 +++++++++--------- tests/mod_test/tests/fwcfg_test.rs | 4 +- tests/mod_test/tests/pci_test.rs | 28 +- tests/mod_test/tests/scsi_test.rs | 12 +- tests/mod_test/tests/usb_storage_test.rs | 32 +- tests/mod_test/tests/virtio_test.rs | 58 +- 12 files changed, 1753 insertions(+), 1270 deletions(-) create mode 100644 tests/mod_test/src/libdriver/qcow2.rs diff --git a/Cargo.lock b/Cargo.lock index bd86834e2..c689ceda7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1146,6 +1146,7 @@ dependencies = [ "byteorder", "devices", "hex", + "libc", "machine", "rand", "serde", diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 3dcb2b6a8..2df3f4bd4 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -11,6 +11,7 @@ hex = "0.4.3" vmm-sys-util = "0.11.1" anyhow = "1.0" serde_json = "1.0" +libc = "0.2" byteorder = "1.4.3" serde = { version = "1.0", features = ["derive"] } devices = { path = "../../devices" } diff --git a/tests/mod_test/src/libdriver/mod.rs b/tests/mod_test/src/libdriver/mod.rs index 8064c9a6d..de696bc0b 100644 --- a/tests/mod_test/src/libdriver/mod.rs +++ b/tests/mod_test/src/libdriver/mod.rs @@ -16,6 +16,7 @@ pub mod machine; pub mod malloc; pub mod pci; pub mod pci_bus; +pub mod qcow2; pub mod usb; pub mod virtio; pub mod virtio_block; diff --git a/tests/mod_test/src/libdriver/qcow2.rs b/tests/mod_test/src/libdriver/qcow2.rs new file mode 100644 index 000000000..8b3fa81d5 --- /dev/null +++ b/tests/mod_test/src/libdriver/qcow2.rs @@ -0,0 +1,301 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Result}; +use byteorder::{BigEndian, ByteOrder}; +use libc::iovec; +use libc::{c_int, off_t, preadv}; +use std::{ + fs::File, + io::{Seek, SeekFrom, Write}, + os::unix::prelude::{AsRawFd, OpenOptionsExt}, +}; +use util::aio::Iovec; + +const QCOW_MAGIC: u32 = 0x514649fb; +const ENTRY_SIZE: u64 = 8; +const QCOW_VERSION_2_MIN_LEN: usize = 72; +const QCOW_VERSION_3_MIN_LEN: usize = 104; +const QCOW2_OFFSET_COPIED: u64 = 1 << 63; +const CLUSTER_BITS: u64 = 16; +pub const CLUSTER_SIZE: u64 = 1 << CLUSTER_BITS; + +#[derive(Debug)] +pub struct Qcow2Driver { + header: QcowHeader, + file: File, +} + +impl Qcow2Driver { + fn new(image_path: String) -> Self { + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(image_path) + .unwrap(); + + let mut qcow2 = Qcow2Driver { + header: QcowHeader::default(), + file, + }; + qcow2.load_header(); + qcow2 + } + + fn load_header(&mut self) { + let mut buf = vec![0; QcowHeader::len()]; + let ret = self.raw_read(0, &mut buf); + assert_eq!(ret, buf.len() as i64); + self.header = QcowHeader::from_vec(&buf).unwrap(); + } + + fn raw_read(&self, offset: u64, buf: &mut [u8]) -> i64 { + let ptr = buf.as_mut_ptr() as u64; + let cnt = buf.len() as u64; + let iovec = vec![Iovec::new(ptr, cnt)]; + let ret = unsafe { + preadv( + self.file.as_raw_fd() as c_int, + iovec.as_ptr() as *const iovec, + iovec.len() as c_int, + offset as off_t, + ) as i64 + }; + ret + } + + fn raw_write(&mut self, offset: u64, buf: &mut [u8]) { + self.file.seek(SeekFrom::Start(offset)).unwrap(); + self.file.write_all(&buf).unwrap(); + } +} + +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct QcowHeader { + pub magic: u32, + pub version: u32, + pub backing_file_offset: u64, + pub backing_file_size: u32, + pub cluster_bits: u32, + pub size: u64, + pub crypt_method: u32, + pub l1_size: u32, + pub l1_table_offset: u64, + pub refcount_table_offset: u64, + pub refcount_table_clusters: u32, + pub nb_snapshots: u32, + pub snapshots_offset: u64, + // version >= v3 + pub incompatible_features: u64, + pub compatible_features: u64, + pub autoclear_features: u64, + pub refcount_order: u32, + pub header_length: u32, +} + +impl QcowHeader { + pub fn from_vec(buf: &[u8]) -> Result { + if buf.len() < QCOW_VERSION_2_MIN_LEN { + bail!( + "Invalid header len {}, the min len {}", + buf.len(), + QCOW_VERSION_2_MIN_LEN + ); + } + let mut header = QcowHeader { + magic: BigEndian::read_u32(&buf[0..4]), + version: BigEndian::read_u32(&buf[4..8]), + backing_file_offset: BigEndian::read_u64(&buf[8..16]), + backing_file_size: BigEndian::read_u32(&buf[16..20]), + cluster_bits: BigEndian::read_u32(&buf[20..24]), + size: BigEndian::read_u64(&buf[24..32]), + crypt_method: BigEndian::read_u32(&buf[32..36]), + l1_size: BigEndian::read_u32(&buf[36..40]), + l1_table_offset: BigEndian::read_u64(&buf[40..48]), + refcount_table_offset: BigEndian::read_u64(&buf[48..56]), + refcount_table_clusters: BigEndian::read_u32(&buf[56..60]), + nb_snapshots: BigEndian::read_u32(&buf[60..64]), + snapshots_offset: BigEndian::read_u64(&buf[64..72]), + ..Default::default() + }; + if header.magic != QCOW_MAGIC { + bail!("Invalid format {}", header.magic); + } + if header.version == 2 { + header.refcount_order = 4; + header.header_length = QCOW_VERSION_2_MIN_LEN as u32; + } else if header.version == 3 { + if buf.len() < QCOW_VERSION_3_MIN_LEN { + bail!("Invalid header len for version 3 {}", buf.len()); + } + header.incompatible_features = BigEndian::read_u64(&buf[72..80]); + header.compatible_features = BigEndian::read_u64(&buf[80..88]); + header.autoclear_features = BigEndian::read_u64(&buf[88..96]); + header.refcount_order = BigEndian::read_u32(&buf[96..100]); + header.header_length = BigEndian::read_u32(&buf[100..104]); + } else { + bail!("Invalid version {}", header.version); + } + Ok(header) + } + + pub fn to_vec(&self) -> Vec { + let sz = if self.version == 2 { + QCOW_VERSION_2_MIN_LEN + } else { + QcowHeader::len() + }; + let mut buf = vec![0; sz]; + BigEndian::write_u32(&mut buf[0..4], self.magic); + BigEndian::write_u32(&mut buf[4..8], self.version); + BigEndian::write_u64(&mut buf[8..16], self.backing_file_offset); + BigEndian::write_u32(&mut buf[16..20], self.backing_file_size); + BigEndian::write_u32(&mut buf[20..24], self.cluster_bits); + BigEndian::write_u64(&mut buf[24..32], self.size); + BigEndian::write_u32(&mut buf[32..36], self.crypt_method); + BigEndian::write_u32(&mut buf[36..40], self.l1_size); + BigEndian::write_u64(&mut buf[40..48], self.l1_table_offset); + BigEndian::write_u64(&mut buf[48..56], self.refcount_table_offset); + BigEndian::write_u32(&mut buf[56..60], self.refcount_table_clusters); + BigEndian::write_u32(&mut buf[60..64], self.nb_snapshots); + BigEndian::write_u64(&mut buf[64..72], self.snapshots_offset); + if self.version >= 3 { + BigEndian::write_u64(&mut buf[72..80], self.incompatible_features); + BigEndian::write_u64(&mut buf[80..88], self.compatible_features); + BigEndian::write_u64(&mut buf[88..96], self.autoclear_features); + BigEndian::write_u32(&mut buf[96..100], self.refcount_order); + BigEndian::write_u32(&mut buf[100..104], self.header_length); + } + buf + } + + #[inline] + pub fn len() -> usize { + std::mem::size_of::() + } +} + +// From size to bits. +fn size_to_bits(size: u64) -> Option { + for i in 0..63 { + if size >> i == 1 { + return Some(i); + } + } + return None; +} + +/// Create a qcow2 format image for test. +pub fn create_qcow2_img(image_path: String, image_size: u64) { + let img_bits = size_to_bits(image_size).unwrap(); + let img_size = image_size; + let cluster_bits = CLUSTER_BITS; + let cluster_sz = 1 << cluster_bits; + + let l1_entry_size: u64 = 1 << (cluster_bits * 2 - 3); + let l1_size = (img_size + l1_entry_size - 1) / l1_entry_size; + let header = QcowHeader { + magic: QCOW_MAGIC, + version: 3, + backing_file_offset: 0, + backing_file_size: 0, + cluster_bits: cluster_bits as u32, + size: 1 << img_bits, + crypt_method: 0, + l1_size: l1_size as u32, + l1_table_offset: 3 * cluster_sz, + refcount_table_offset: cluster_sz, + refcount_table_clusters: 1, + nb_snapshots: 0, + snapshots_offset: 0, + incompatible_features: 0, + compatible_features: 0, + autoclear_features: 0, + refcount_order: 4, + header_length: std::mem::size_of::() as u32, + }; + + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_CREAT | libc::O_TRUNC) + .open(image_path.clone()) + .unwrap(); + file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) + .unwrap(); + file.write_all(&header.to_vec()).unwrap(); + + // Cluster 1 is the refcount table. + assert_eq!(header.refcount_table_offset, cluster_sz * 1); + let mut refcount_table = [0_u8; ENTRY_SIZE as usize]; + BigEndian::write_u64(&mut refcount_table, cluster_sz * 2); + file.seek(SeekFrom::Start(cluster_sz * 1)).unwrap(); + file.write_all(&refcount_table).unwrap(); + + // Clusters which has been allocated. + assert_eq!(header.refcount_order, 4); + let clusters = + 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); + let mut refcount_block = Vec::new(); + for _ in 0..clusters { + refcount_block.push(0x00); + refcount_block.push(0x01); + } + file.seek(SeekFrom::Start(cluster_sz * 2)).unwrap(); + file.write_all(&refcount_block).unwrap(); + + // Full the disk. + write_full_disk(image_path); +} + +/// Full the disk(this function is only used for test). +/// By default, the data occupied by the l2 table and refcount table should not exceed one cluster. +/// If the defined disk is too large, it may result in incorrect data format for. +/// For example. +/// If you defined cluster size = 1 << 16, the max disk size cannout exceed the 1 << (16 * 2 - 3) = 512M. +fn write_full_disk(image_path: String) { + let mut qcow2 = Qcow2Driver::new(image_path); + let cluster_bits = qcow2.header.cluster_bits; + let cluster_size = 1 << cluster_bits; + let image_size = qcow2.header.size; + + let n_cluster = image_size / cluster_size; + // Header + refcount table + refcount block + l1 table + l2 table = 5 cluster. + qcow2.file.set_len((5 + n_cluster) * cluster_size).unwrap(); + // Write l2 table. + let mut refcount_block: Vec = Vec::new(); + let mut l1_table = [0_u8; ENTRY_SIZE as usize]; + BigEndian::write_u64(&mut l1_table, cluster_size * 4 | QCOW2_OFFSET_COPIED); + let mut l2_table: Vec = Vec::new(); + for _ in 0..5 { + refcount_block.push(0x00); + refcount_block.push(0x01); + } + let offset_start = 5 * cluster_size; + for i in 0..n_cluster { + let addr = offset_start + i * cluster_size; + let l2_table_value = addr | QCOW2_OFFSET_COPIED; + + let mut tmp_buf = vec![0_u8; ENTRY_SIZE as usize]; + BigEndian::write_u64(&mut tmp_buf, l2_table_value); + l2_table.append(&mut tmp_buf); + refcount_block.push(0x00); + refcount_block.push(0x01); + + let mut cluster_buff = vec![0_u8; cluster_size as usize]; + qcow2.raw_write(addr, &mut cluster_buff); + } + qcow2.raw_write(cluster_size * 2, &mut refcount_block); + qcow2.raw_write(cluster_size * 3, &mut l1_table); + qcow2.raw_write(cluster_size * 4, &mut l2_table); +} diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index 5bd9e41d9..ccf97add2 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -25,6 +25,7 @@ use crate::libdriver::virtio::{ TestVringDescEntry, VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, }; use crate::libtest::{test_init, TestState}; +use crate::utils::ImageType; use crate::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; pub const VIRTIO_BLK_F_BARRIER: u64 = 0; @@ -110,6 +111,7 @@ impl TestVirtBlkReq { } pub fn create_blk( + image_type: &ImageType, image_path: Rc, device_args: Rc, drive_args: Rc, @@ -122,6 +124,10 @@ pub fn create_blk( let pci_slot: u8 = 0x4; let pci_fn: u8 = 0x0; let mut extra_args: Vec<&str> = Vec::new(); + let img_type = match image_type { + &ImageType::Raw => "raw", + &ImageType::Qcow2 => "qcow2", + }; let mut args: Vec<&str> = "-machine virt".split(' ').collect(); extra_args.append(&mut args); @@ -133,8 +139,8 @@ pub fn create_blk( args = blk_pci_args[..].split(' ').collect(); extra_args.append(&mut args); let blk_args = format!( - "-drive if=none,id=drive0,file={},format=raw{}", - image_path, drive_args, + "-drive if=none,id=drive0,file={},format={}{}", + image_path, img_type, drive_args, ); args = blk_args.split(' ').collect(); extra_args.append(&mut args); @@ -425,18 +431,25 @@ pub fn virtio_blk_default_feature(blk: Rc>) -> u64 { features } -pub fn set_up() -> ( +pub fn set_up( + image_type: &ImageType, +) -> ( Rc>, Rc>, Rc>, Rc, ) { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, image_type)); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=false")); let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); + let (blk, test_state, alloc) = create_blk( + image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); (blk, test_state, alloc, image_path) } diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index 77a8672b6..7641e8f71 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -16,6 +16,8 @@ use std::fs; use std::path::Path; use std::process::Command; +use crate::libdriver::qcow2::create_qcow2_img; + pub fn get_rand_str(size: usize) -> String { thread_rng() .sample_iter(&Alphanumeric) @@ -68,9 +70,21 @@ pub fn swap_u64(value: u64) -> u64 { lower_u32 << 32 | higher_u32 } -pub const TEST_IMAGE_SIZE: u64 = 64 * 1024 * 1024; +pub const TEST_IMAGE_BITS: u64 = 26; +pub const TEST_IMAGE_SIZE: u64 = 1 << TEST_IMAGE_BITS; + +#[derive(Debug, PartialEq, Eq)] +pub enum ImageType { + Raw, + Qcow2, +} + +impl ImageType { + pub const IMAGE_TYPE: [Self; 2] = [ImageType::Raw, ImageType::Qcow2]; +} + /// Create image file. -pub fn create_img(size: u64, flag: u8) -> String { +pub fn create_img(image_size: u64, flag: u8, image_type: &ImageType) -> String { let rng_name: String = get_rand_str(8); assert!(cfg!(target_os = "linux")); @@ -80,6 +94,15 @@ pub fn create_img(size: u64, flag: u8) -> String { image_path = format!("/var/log/stratovirt-{}.img", rng_name); } + match image_type { + &ImageType::Raw => create_raw_img(image_path.clone(), image_size), + &ImageType::Qcow2 => create_qcow2_img(image_path.clone(), image_size), + } + + image_path +} + +fn create_raw_img(image_path: String, size: u64) { let image_path_of = format!("of={}", &image_path); let image_size_of = format!("bs={}", size); let output = Command::new("dd") @@ -90,7 +113,6 @@ pub fn create_img(size: u64, flag: u8) -> String { .output() .expect("failed to create image"); assert!(output.status.success()); - image_path } /// Delete image file. diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 7c3a58d6b..145b6b833 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use mod_test::libdriver::qcow2::CLUSTER_SIZE; use virtio::device::block::VirtioBlkConfig; use mod_test::libdriver::malloc::GuestAllocator; @@ -29,7 +30,7 @@ use mod_test::libdriver::virtio_block::{ }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::TestState; -use mod_test::utils::{create_img, TEST_IMAGE_SIZE}; +use mod_test::utils::{create_img, ImageType, TEST_IMAGE_SIZE}; use std::cell::RefCell; use std::mem::size_of; @@ -268,42 +269,45 @@ fn virtio_blk_illegal_req( /// 1/2/3: success. #[test] fn blk_basic() { - let (blk, test_state, alloc, image_path) = set_up(); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let features = virtio_blk_default_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device negotiate different features. @@ -316,52 +320,60 @@ fn blk_basic() { /// 1/2/4: success, 3: failed. #[test] fn blk_features_negotiate() { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); - let device_args = Rc::new(String::from(",num-queues=4")); - let drive_args = Rc::new(String::from(",direct=false,readonly=on")); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); - - blk.borrow_mut().pci_dev.enable_msix(None); - blk.borrow_mut() - .setup_msix_configuration_vector(alloc.clone(), 0); - - let mut features = blk.borrow().get_device_features(); - features |= 1 << VIRTIO_BLK_F_SEG_MAX - | 1 << VIRTIO_BLK_F_RO - | 1 << VIRTIO_BLK_F_FLUSH - | 1 << VIRTIO_BLK_F_MQ; - blk.borrow_mut().negotiate_features(features); - blk.borrow_mut().set_features_ok(); - assert_eq!(features, blk.borrow_mut().get_guest_features()); - - let unsupported_features = 1 << VIRTIO_BLK_F_BARRIER - | 1 << VIRTIO_BLK_F_SIZE_MAX - | 1 << VIRTIO_BLK_F_GEOMETRY - | 1 << VIRTIO_BLK_F_BLK_SIZE - | 1 << VIRTIO_BLK_F_TOPOLOGY - | 1 << VIRTIO_BLK_F_CONFIG_WCE - | 1 << VIRTIO_BLK_F_DISCARD - | 1 << VIRTIO_BLK_F_WRITE_ZEROES - | 1 << VIRTIO_BLK_F_LIFETIME - | 1 << VIRTIO_BLK_F_SECURE_ERASE; - features |= unsupported_features; - blk.borrow_mut().negotiate_features(features); - blk.borrow_mut().set_features_ok(); - assert_ne!(features, blk.borrow_mut().get_guest_features()); - assert_eq!( - unsupported_features & blk.borrow_mut().get_guest_features(), - 0 - ); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &image_type)); + let device_args = Rc::new(String::from(",num-queues=4")); + let drive_args = Rc::new(String::from(",direct=false,readonly=on")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - Vec::new(), - image_path.clone(), - ); + blk.borrow_mut().pci_dev.enable_msix(None); + blk.borrow_mut() + .setup_msix_configuration_vector(alloc.clone(), 0); + + let mut features = blk.borrow().get_device_features(); + features |= 1 << VIRTIO_BLK_F_SEG_MAX + | 1 << VIRTIO_BLK_F_RO + | 1 << VIRTIO_BLK_F_FLUSH + | 1 << VIRTIO_BLK_F_MQ; + blk.borrow_mut().negotiate_features(features); + blk.borrow_mut().set_features_ok(); + assert_eq!(features, blk.borrow_mut().get_guest_features()); + + let unsupported_features = 1 << VIRTIO_BLK_F_BARRIER + | 1 << VIRTIO_BLK_F_SIZE_MAX + | 1 << VIRTIO_BLK_F_GEOMETRY + | 1 << VIRTIO_BLK_F_BLK_SIZE + | 1 << VIRTIO_BLK_F_TOPOLOGY + | 1 << VIRTIO_BLK_F_CONFIG_WCE + | 1 << VIRTIO_BLK_F_DISCARD + | 1 << VIRTIO_BLK_F_WRITE_ZEROES + | 1 << VIRTIO_BLK_F_LIFETIME + | 1 << VIRTIO_BLK_F_SECURE_ERASE; + features |= unsupported_features; + blk.borrow_mut().negotiate_features(features); + blk.borrow_mut().set_features_ok(); + assert_ne!(features, blk.borrow_mut().get_guest_features()); + assert_eq!( + unsupported_features & blk.borrow_mut().get_guest_features(), + 0 + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + Vec::new(), + image_path.clone(), + ); + } } /// Block device sends I/O request with feature 'VIRTIO_BLK_F_SEG_MAX'. @@ -373,46 +385,49 @@ fn blk_features_negotiate() { /// 1/2/3: success. #[test] fn blk_feature_seg_max() { - let (blk, test_state, alloc, image_path) = set_up(); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let mut features = virtio_blk_default_feature(blk.clone()); - features |= 1 << VIRTIO_BLK_F_SEG_MAX; + let mut features = virtio_blk_default_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_SEG_MAX; - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let seg_max = blk - .borrow() - .config_readl(offset_of!(VirtioBlkConfig, seg_max) as u64); - let queue_size = virtqueues[0].borrow_mut().size; - assert_eq!(seg_max, (queue_size - 2)); + let seg_max = blk + .borrow() + .config_readl(offset_of!(VirtioBlkConfig, seg_max) as u64); + let queue_size = virtqueues[0].borrow_mut().size; + assert_eq!(seg_max, (queue_size - 2)); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request with feature 'VIRTIO_BLK_F_RO'. @@ -425,87 +440,96 @@ fn blk_feature_seg_max() { /// 1/2/4: success, failed: 3. #[test] fn blk_feature_ro() { - let (blk, test_state, alloc, image_path) = set_up(); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let mut features = virtio_blk_default_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - Rc::new("".to_string()), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + Rc::new("".to_string()), + ); - let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(String::from(",direct=false,readonly=on")); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false,readonly=on")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - features |= 1 << VIRTIO_BLK_F_RO; + features |= 1 << VIRTIO_BLK_F_RO; - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_OUT, - 0, - true, - ); - blk.borrow().virtqueue_notify(virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + true, + ); + blk.borrow().virtqueue_notify(virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_IOERR); + let status_addr = + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request with feature 'VIRTIO_BLK_F_FLUSH'. @@ -517,48 +541,51 @@ fn blk_feature_ro() { /// 1/2/3: success. #[test] fn blk_feature_flush() { - let (blk, test_state, alloc, image_path) = set_up(); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let mut features = virtio_blk_default_feature(blk.clone()); - features |= 1 << VIRTIO_BLK_F_FLUSH; + let mut features = virtio_blk_default_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_FLUSH; - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - virtio_blk_flush( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 10, - ); + virtio_blk_flush( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 10, + ); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request with feature 'VIRTIO_BLK_F_MQ'. @@ -570,106 +597,115 @@ fn blk_feature_flush() { /// 1/2/3: success. #[test] fn blk_feature_mq() { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); - let device_args = Rc::new(String::from(",num-queues=4")); - let drive_args = Rc::new(String::from(",direct=false")); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); - - let mut features = virtio_blk_default_feature(blk.clone()); - features |= 1 << VIRTIO_BLK_F_MQ; - - let num_queues = 4; - let virtqueues = - blk.borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, num_queues); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &image_type)); + let device_args = Rc::new(String::from(",num-queues=4")); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let cfg_num_queues = blk - .borrow() - .config_readw(offset_of!(VirtioBlkConfig, num_queues) as u64); - assert_eq!(num_queues as u16, cfg_num_queues); + let mut features = virtio_blk_default_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_MQ; - let mut free_head: Vec = Vec::with_capacity(num_queues); - let mut req_addr: Vec = Vec::with_capacity(num_queues); - for i in 0..num_queues { - let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, i as u64, REQ_DATA_LEN as usize); - blk_req.data.push_str("TEST"); + let num_queues = 4; + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, num_queues); - req_addr.push(virtio_blk_request( - test_state.clone(), - alloc.clone(), - blk_req, - true, - )); + let cfg_num_queues = blk + .borrow() + .config_readw(offset_of!(VirtioBlkConfig, num_queues) as u64); + assert_eq!(num_queues as u16, cfg_num_queues); - let data_addr = round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap(); + let mut free_head: Vec = Vec::with_capacity(num_queues); + let mut req_addr: Vec = Vec::with_capacity(num_queues); + for i in 0..num_queues { + let mut blk_req = + TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, i as u64, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr[i], - len: REQ_ADDR_LEN, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: data_addr, - len: REQ_DATA_LEN, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: data_addr + REQ_DATA_LEN as u64, - len: REQ_STATUS_LEN, - write: true, - }); + req_addr.push(virtio_blk_request( + test_state.clone(), + alloc.clone(), + blk_req, + true, + )); + + let data_addr = round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap(); + + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr[i], + len: REQ_ADDR_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr, + len: REQ_DATA_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr + REQ_DATA_LEN as u64, + len: REQ_STATUS_LEN, + write: true, + }); + + free_head.push( + virtqueues[i] + .borrow_mut() + .add_chained(test_state.clone(), data_entries), + ); + } - free_head.push( - virtqueues[i] - .borrow_mut() - .add_chained(test_state.clone(), data_entries), - ); - } + for i in 0..num_queues { + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[i].clone()); + } - for i in 0..num_queues { - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[i].clone()); - } + for i in 0..num_queues { + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[i].clone(), + free_head[i], + TIMEOUT_US, + &mut None, + true, + ); + } - for i in 0..num_queues { - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[i].clone(), - free_head[i], - TIMEOUT_US, - &mut None, - true, - ); - } + for i in 0..num_queues { + let status_addr = + round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + } - for i in 0..num_queues { - let status_addr = - round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_OK); - } + for i in 0..num_queues { + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[i].clone(), + i as u64, + true, + ); + } - for i in 0..num_queues { - virtio_blk_read( + tear_down( blk.clone(), test_state.clone(), alloc.clone(), - virtqueues[i].clone(), - i as u64, - true, + virtqueues, + image_path.clone(), ); } - - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); } /// Block device sends I/O request, configure all parameters. @@ -681,56 +717,64 @@ fn blk_feature_mq() { /// 1/2/3: success. #[test] fn blk_all_features() { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1)); - let device_args = Rc::new(String::from( - ",multifunction=on,serial=111111,num-queues=4,bootindex=1,iothread=iothread1", - )); - let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { - Rc::new(String::from( - ",direct=on,aio=io_uring,readonly=off,throttling.iops-total=1024", - )) - } else { - Rc::new(String::from( - ",direct=false,readonly=off,throttling.iops-total=1024", - )) - }; - let other_args = Rc::new(String::from("-object iothread,id=iothread1")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1, &image_type)); + let device_args = Rc::new(String::from( + ",multifunction=on,serial=111111,num-queues=4,bootindex=1,iothread=iothread1", + )); + let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { + Rc::new(String::from( + ",direct=on,aio=io_uring,readonly=off,throttling.iops-total=1024", + )) + } else { + Rc::new(String::from( + ",direct=false,readonly=off,throttling.iops-total=1024", + )) + }; + let other_args = Rc::new(String::from("-object iothread,id=iothread1")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let mut features = virtio_blk_default_feature(blk.clone()); - features |= 1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_FLUSH | 1 << VIRTIO_BLK_F_MQ; + let mut features = virtio_blk_default_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_SEG_MAX | 1 << VIRTIO_BLK_F_FLUSH | 1 << VIRTIO_BLK_F_MQ; - let num_queues = 4; - let virtqueues = - blk.borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, num_queues); + let num_queues = 4; + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, num_queues); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request to a file with a size of 511b. @@ -742,95 +786,105 @@ fn blk_all_features() { /// 1/3: success, 2: failed. #[test] fn blk_small_file_511b() { - let size = 511; - let image_path = Rc::new(create_img(size, 1)); - let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(String::from(",direct=false")); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let size = 511; + let image_path = Rc::new(create_img(size, 1, &image_type)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let features = virtio_blk_default_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, size / REQ_DATA_LEN as u64); + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, size / REQ_DATA_LEN as u64); - let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); - blk_req.data.push_str("TEST"); + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); - let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, true); - let data_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap(); + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, true); + let data_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap(); - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: REQ_ADDR_LEN, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: data_addr, - len: REQ_DATA_LEN, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: data_addr + REQ_DATA_LEN as u64, - len: REQ_STATUS_LEN, - write: true, - }); - let free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: REQ_ADDR_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr, + len: REQ_DATA_LEN, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: data_addr + REQ_DATA_LEN as u64, + len: REQ_STATUS_LEN, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_IOERR); + let status_addr = + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_IN, - 0, - true, - ); + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_IN, + 0, + true, + ); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_IOERR); + let status_addr = + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request, configured as 'serial=11111111111111111111'. @@ -842,134 +896,152 @@ fn blk_small_file_511b() { /// 1/2/3: success. #[test] fn blk_serial() { - let serial_num = String::from("11111111111111111111"); - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); - let device_args = Rc::new(format!(",serial={}", serial_num)); - let drive_args = Rc::new(String::from(",direct=false")); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); - - let features = virtio_blk_default_feature(blk.clone()); - - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); - - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); - - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); - - virtio_blk_get_id( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - serial_num, - ); - - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); -} - -/// Block device sends I/O request, configured as 'throttling.iops-total=1'. -/// TestStep: -/// 1. Init block device, configured as 'throttling.iops-total=1'. -/// 2. Do the I/O request, check iops. -/// 3. Destroy device. -/// Expect: -/// 1/2/3: success. -#[test] -fn blk_iops() { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); - let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(String::from(",direct=false,throttling.iops-total=1")); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); - - let features = virtio_blk_default_feature(blk.clone()); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let serial_num = String::from("11111111111111111111"); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &image_type)); + let device_args = Rc::new(format!(",serial={}", serial_num)); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let features = virtio_blk_default_feature(blk.clone()); - let mut free_head = 0_u32; - let mut req_addr = 0_u64; + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - for i in 0..DEFAULT_IO_REQS { - (free_head, req_addr) = add_blk_request( + virtio_blk_write( + blk.clone(), test_state.clone(), alloc.clone(), virtqueues[0].clone(), - VIRTIO_BLK_T_OUT, - i, + 0, true, ); - } - - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - loop { - test_state.borrow().clock_step_ns(100); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - if blk.borrow().queue_was_notified(virtqueues[0].clone()) - && virtqueues[0].borrow_mut().get_buf(test_state.clone()) - { - assert!(!virtqueues[0].borrow().desc_len.contains_key(&free_head)); - break; - } + virtio_blk_get_id( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + serial_num, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); } +} - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_ne!(status, VIRTIO_BLK_S_OK); +/// Block device sends I/O request, configured as 'throttling.iops-total=1'. +/// TestStep: +/// 1. Init block device, configured as 'throttling.iops-total=1'. +/// 2. Do the I/O request, check iops. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn blk_iops() { + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &image_type)); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false,throttling.iops-total=1")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); + + let features = virtio_blk_default_feature(blk.clone()); + + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + let mut free_head = 0_u32; + let mut req_addr = 0_u64; + + for i in 0..DEFAULT_IO_REQS { + (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + i, + true, + ); + } - let time_out = Instant::now() + Duration::from_micros(TIMEOUT_US); - loop { - test_state.borrow().clock_step(); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + + loop { + test_state.borrow().clock_step_ns(100); - if blk.borrow().queue_was_notified(virtqueues[0].clone()) - && virtqueues[0].borrow_mut().get_buf(test_state.clone()) - { - if virtqueues[0].borrow().desc_len.contains_key(&free_head) { + if blk.borrow().queue_was_notified(virtqueues[0].clone()) + && virtqueues[0].borrow_mut().get_buf(test_state.clone()) + { + assert!(!virtqueues[0].borrow().desc_len.contains_key(&free_head)); break; } } - assert!(Instant::now() <= time_out); - } - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_OK); + let status_addr = + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_ne!(status, VIRTIO_BLK_S_OK); + + let time_out = Instant::now() + Duration::from_micros(TIMEOUT_US); + loop { + test_state.borrow().clock_step(); + + if blk.borrow().queue_was_notified(virtqueues[0].clone()) + && virtqueues[0].borrow_mut().get_buf(test_state.clone()) + { + if virtqueues[0].borrow().desc_len.contains_key(&free_head) { + break; + } + } + assert!(Instant::now() <= time_out); + } - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + let status_addr = + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request, configured as 'aio=native'. @@ -981,48 +1053,56 @@ fn blk_iops() { /// 1/2/3: success. #[test] fn blk_aio_native() { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1)); - let device_args = Rc::new(String::from("")); - let drive_args = if aio_probe(AioEngine::Native).is_ok() { - Rc::new(String::from(",direct=on,aio=native")) - } else { - Rc::new(String::from(",direct=false")) - }; - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1, &image_type)); + let device_args = Rc::new(String::from("")); + let drive_args = if aio_probe(AioEngine::Native).is_ok() { + Rc::new(String::from(",direct=on,aio=native")) + } else { + Rc::new(String::from(",direct=false")) + }; + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let features = virtio_blk_default_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends I/O request, configured as 'aio=io_uring'. @@ -1034,48 +1114,56 @@ fn blk_aio_native() { /// 1/2/3: success. #[test] fn blk_aio_io_uring() { - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1)); - let device_args = Rc::new(String::from("")); - let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { - Rc::new(String::from(",direct=on,aio=io_uring")) - } else { - Rc::new(String::from(",direct=false")) - }; - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1, &image_type)); + let device_args = Rc::new(String::from("")); + let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { + Rc::new(String::from(",direct=on,aio=io_uring")) + } else { + Rc::new(String::from(",direct=false")) + }; + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let features = virtio_blk_default_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - false, - ); + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + false, + ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - false, - ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + false, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device sends an illegal type of I/O request. @@ -1087,29 +1175,32 @@ fn blk_aio_io_uring() { /// 1/3: success, 2: failed. #[test] fn blk_illegal_req_type() { - let (blk, test_state, alloc, image_path) = set_up(); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let features = virtio_blk_default_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - virtio_blk_illegal_req( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_ILLGEAL, - ); + virtio_blk_illegal_req( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_ILLGEAL, + ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device configuration space read and write. @@ -1122,33 +1213,36 @@ fn blk_illegal_req_type() { /// 1/2/4: success, 3: failed. #[test] fn blk_rw_config() { - let (blk, test_state, alloc, image_path) = set_up(); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let features = virtio_blk_default_feature(blk.clone()); + let features = virtio_blk_default_feature(blk.clone()); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); - blk.borrow().config_writeq(0, 1024); - let capacity = blk.borrow().config_readq(0); - assert_ne!(capacity, 1024); + blk.borrow().config_writeq(0, 1024); + let capacity = blk.borrow().config_readq(0); + assert_ne!(capacity, 1024); - let discard_sector_alignment = blk.borrow().config_readl(40); - blk.borrow().config_writel(40, 1024); - assert_eq!(blk.borrow().config_readl(40), discard_sector_alignment); - assert_ne!(blk.borrow().config_readl(40), 1024); + let discard_sector_alignment = blk.borrow().config_readl(40); + blk.borrow().config_writel(40, 1024); + assert_eq!(blk.borrow().config_readl(40), discard_sector_alignment); + assert_ne!(blk.borrow().config_readl(40), 1024); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } /// Block device send I/O requests in an abnormal format. @@ -1160,501 +1254,408 @@ fn blk_rw_config() { /// 1/3: success, 2: failed. #[test] fn blk_abnormal_req() { - let (blk, test_state, alloc, image_path) = set_up(); - - let features = virtio_blk_default_feature(blk.clone()); + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); - - let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); - blk_req.data.push_str("TEST"); - - let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + let features = virtio_blk_default_feature(blk.clone()); - // Desc: req_hdr length 8, data length 256. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 8, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 8, - len: 256, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 264, - len: 1, - write: true, - }); - let free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); - - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); - - let status = test_state.borrow().readb(req_addr + 264); - assert_ne!(status, VIRTIO_BLK_S_OK); - - // Desc: req_hdr length 32. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 32, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 32, - len: 512, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 544, - len: 1, - write: true, - }); - let free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); - - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); - - let status = test_state.borrow().readb(req_addr + 544); - assert_ne!(status, VIRTIO_BLK_S_OK); - - // Desc: data length 256. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 16, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 16, - len: 256, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 272, - len: 1, - write: true, - }); - let free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); - - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); - - let status = test_state.borrow().readb(req_addr + 272); - assert_ne!(status, VIRTIO_BLK_S_OK); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - // Desc: data length 4, small size desc. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 16, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 16, - len: 4, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 20, - len: 1, - write: true, - }); - let free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); + blk_req.data.push_str("TEST"); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); - let status = test_state.borrow().readb(req_addr + 20); - assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: req_hdr length 8, data length 256. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 8, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 8, + len: 256, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 264, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - // Desc: miss data. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 16, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 16, - len: 1, - write: true, - }); - let _free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - thread::sleep(time::Duration::from_secs(1)); + let status = test_state.borrow().readb(req_addr + 264); + assert_ne!(status, VIRTIO_BLK_S_OK); - let status = test_state.borrow().readb(req_addr + 16); - assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: req_hdr length 32. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 32, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 32, + len: 512, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 544, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - // Desc: all 'out' desc. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 16, - write: true, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 16, - len: 512, - write: true, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 528, - len: 1, - write: true, - }); - let _free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - thread::sleep(time::Duration::from_secs(1)); + let status = test_state.borrow().readb(req_addr + 544); + assert_ne!(status, VIRTIO_BLK_S_OK); - let status = test_state.borrow().readb(req_addr + 528); - assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: data length 256. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 256, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 272, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - // Desc: data length 0. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 16, - write: false, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 16, - len: 0, - write: true, - }); - data_entries.push(TestVringDescEntry { - data: req_addr + 20, - len: 1, - write: true, - }); - let _free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - thread::sleep(time::Duration::from_secs(1)); + let status = test_state.borrow().readb(req_addr + 272); + assert_ne!(status, VIRTIO_BLK_S_OK); - let status = test_state.borrow().readb(req_addr + 20); - assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: data length 4, small size desc. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 4, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 20, + len: 1, + write: true, + }); + let free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - // Desc: only status desc. - let mut data_entries: Vec = Vec::with_capacity(3); - data_entries.push(TestVringDescEntry { - data: req_addr, - len: 1, - write: true, - }); - let _free_head = virtqueues[0] - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, + true, + ); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - thread::sleep(time::Duration::from_secs(1)); + let status = test_state.borrow().readb(req_addr + 20); + assert_ne!(status, VIRTIO_BLK_S_OK); - let status = test_state.borrow().readb(req_addr); - assert_ne!(status, VIRTIO_BLK_S_OK); + // Desc: miss data. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); -} + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); -/// Block device send different types of I/O requests in parallel. -/// TestStep: -/// 1. Init block device. -/// 2. Do the different types I/O requests in parallel. -/// 3. Destroy device. -/// Expect: -/// 1/2/3: success. -#[test] -fn blk_parallel_req() { - let (blk, test_state, alloc, image_path) = set_up(); + let status = test_state.borrow().readb(req_addr + 16); + assert_ne!(status, VIRTIO_BLK_S_OK); - let mut features = virtio_blk_default_feature(blk.clone()); - features |= 1 << VIRTIO_BLK_F_FLUSH; + // Desc: all 'out' desc. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: true, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 512, + write: true, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 528, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); - let mut free_head_vec: Vec = Vec::with_capacity(4); - let mut req_addr_vec: Vec = Vec::with_capacity(4); + let status = test_state.borrow().readb(req_addr + 528); + assert_ne!(status, VIRTIO_BLK_S_OK); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_OUT, - 0, - true, - ); - free_head_vec.push(free_head); - req_addr_vec.push(req_addr); + // Desc: data length 0. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 16, + write: false, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 16, + len: 0, + write: true, + }); + data_entries.push(TestVringDescEntry { + data: req_addr + 20, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_IN, - 0, - true, - ); - free_head_vec.push(free_head); - req_addr_vec.push(req_addr); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_FLUSH, - 0, - true, - ); - free_head_vec.push(free_head); - req_addr_vec.push(req_addr); + let status = test_state.borrow().readb(req_addr + 20); + assert_ne!(status, VIRTIO_BLK_S_OK); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_GET_ID, - 0, - true, - ); - free_head_vec.push(free_head); - req_addr_vec.push(req_addr); + // Desc: only status desc. + let mut data_entries: Vec = Vec::with_capacity(3); + data_entries.push(TestVringDescEntry { + data: req_addr, + len: 1, + write: true, + }); + let _free_head = virtqueues[0] + .borrow_mut() + .add_chained(test_state.clone(), data_entries); - blk.borrow() - .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head_vec[3], - TIMEOUT_US, - &mut None, - true, - ); + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + thread::sleep(time::Duration::from_secs(1)); - for i in 0..4 { - let status_addr = - round_up(req_addr_vec[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_OK); - } + let status = test_state.borrow().readb(req_addr); + assert_ne!(status, VIRTIO_BLK_S_OK); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } -/// Block device sends an I/O request that exceeds the capacity range. +/// Block device send different types of I/O requests in parallel. /// TestStep: /// 1. Init block device. -/// 2. Do the I/O request that exceeds the capacity range. +/// 2. Do the different types I/O requests in parallel. /// 3. Destroy device. /// Expect: -/// 1/3: success, 2: failed. +/// 1/2/3: success. #[test] -fn blk_exceed_capacity() { - let (blk, test_state, alloc, image_path) = set_up(); +fn blk_parallel_req() { + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let features = virtio_blk_default_feature(blk.clone()); + let mut features = virtio_blk_default_feature(blk.clone()); + features |= 1 << VIRTIO_BLK_F_FLUSH; - let virtqueues = blk - .borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); - let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + let mut free_head_vec: Vec = Vec::with_capacity(4); + let mut req_addr_vec: Vec = Vec::with_capacity(4); - let (free_head, req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_OUT, - capacity + 1, - true, - ); + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); - blk.borrow().virtqueue_notify(virtqueues[0].clone()); - blk.borrow().poll_used_elem( - test_state.clone(), - virtqueues[0].clone(), - free_head, - TIMEOUT_US, - &mut None, - true, - ); + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_IN, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; - let status = test_state.borrow().readb(status_addr); - assert_eq!(status, VIRTIO_BLK_S_IOERR); + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_FLUSH, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + let (free_head, req_addr) = add_blk_request( + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_GET_ID, + 0, + true, + ); + free_head_vec.push(free_head); + req_addr_vec.push(req_addr); + + blk.borrow() + .kick_virtqueue(test_state.clone(), virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head_vec[3], + TIMEOUT_US, + &mut None, + true, + ); + + for i in 0..4 { + let status_addr = + round_up(req_addr_vec[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_OK); + } + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } -/// Block device sends I/O request with feature 'VIRTIO_BLK_F_DISCARD'. +/// Block device sends an I/O request that exceeds the capacity range. /// TestStep: -/// 1. Init block device with feature 'VIRTIO_BLK_F_DISCARD'. -/// 2. Do the discard request with different arguments. +/// 1. Init block device. +/// 2. Do the I/O request that exceeds the capacity range. /// 3. Destroy device. /// Expect: -/// 1/3: success. -/// 2: success or failure, stratovirt process is normal. +/// 1/3: success, 2: failed. #[test] -fn blk_feature_discard() { - let req_len = std::mem::size_of::(); - // (sector, num_sectors, flags, req_len, enable_feature, discard, status) - let reqs = [ - (0, 2048, 0, req_len, true, "unmap", VIRTIO_BLK_S_OK), - (0, 2048, 0, req_len, false, "unmap", VIRTIO_BLK_S_OK), - (0, 2048, 0, 8, true, "unmap", 0xff), - (0, 2048, 0, 32, true, "unmap", VIRTIO_BLK_S_UNSUPP), - (0, 2048, 1, req_len, true, "unmap", VIRTIO_BLK_S_UNSUPP), - (0, 2048, 0xff, req_len, true, "unmap", VIRTIO_BLK_S_UNSUPP), - ( - 0, - (TEST_IMAGE_SIZE >> 9) as u32 + 1, - 0, - req_len, - true, - "unmap", - VIRTIO_BLK_S_IOERR, - ), - ( - 0, - MAX_REQUEST_SECTORS + 1, - 0, - req_len, - true, - "unmap", - VIRTIO_BLK_S_IOERR, - ), - (0, 2048, 0, req_len, false, "ignore", VIRTIO_BLK_S_UNSUPP), - ]; - let mut i = 1; - for (sector, num_sectors, flags, len, enabled, discard, status) in reqs { - println!("blk_feature_discard: request {}", i); - i += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); - let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(format!(",discard={},direct=false", discard)); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); +fn blk_exceed_capacity() { + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let (blk, test_state, alloc, image_path) = set_up(&image_type); - let mut features = virtio_blk_default_feature(blk.clone()); - if enabled { - features |= 1 << VIRTIO_BLK_F_DISCARD; - } else { - features &= !(1 << VIRTIO_BLK_F_DISCARD); - } + let features = virtio_blk_default_feature(blk.clone()); let virtqueues = blk.borrow_mut() .init_device(test_state.clone(), alloc.clone(), features, 1); - if discard != "ignore" { - virtio_blk_check_discard_config(blk.clone()); - } - let mut need_poll_elem = true; - let req_data = if len == req_len { - let req = VirtBlkDiscardWriteZeroes { - sector, - num_sectors, - flags, - }; - req.as_bytes().to_vec() - } else { - if len < req_len { - need_poll_elem = false; - } - vec![0; len] - }; - virtio_blk_discard_and_write_zeroes( - blk.clone(), + let capacity = blk.borrow().config_readq(0); + assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + + let (free_head, req_addr) = add_blk_request( test_state.clone(), alloc.clone(), virtqueues[0].clone(), - &req_data, - status, - need_poll_elem, + VIRTIO_BLK_T_OUT, + capacity + 1, + true, + ); + + blk.borrow().virtqueue_notify(virtqueues[0].clone()); + blk.borrow().poll_used_elem( + test_state.clone(), + virtqueues[0].clone(), + free_head, + TIMEOUT_US, + &mut None, true, ); - if status == VIRTIO_BLK_S_OK { - let image_size = get_disk_size(image_path.clone()); - assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); - } + + let status_addr = + round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status = test_state.borrow().readb(status_addr); + assert_eq!(status, VIRTIO_BLK_S_IOERR); tear_down( blk.clone(), @@ -1666,152 +1667,295 @@ fn blk_feature_discard() { } } -/// Block device sends I/O request with feature 'VIRTIO_BLK_F_WRITE_ZEROES'. +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_DISCARD'. /// TestStep: -/// 1. Init block device with feature 'VIRTIO_BLK_F_WRITE_ZEROES'. -/// 2. Do the write-zeroes request with different arguments. +/// 1. Init block device with feature 'VIRTIO_BLK_F_DISCARD'. +/// 2. Do the discard request with different arguments. /// 3. Destroy device. /// Expect: /// 1/3: success. /// 2: success or failure, stratovirt process is normal. #[test] -fn blk_feature_write_zeroes() { - let wz_len = size_of::(); - let req_len = size_of::(); - // (sector, num_sectors, flags, req_len, enable_feature, write_zeroes, discard, status) - let reqs = [ - (0, 2048, 0, wz_len, true, "on", "ignore", VIRTIO_BLK_S_OK), - (0, 2048, 0, wz_len, true, "on", "unmap", VIRTIO_BLK_S_OK), - (0, 2048, 0, wz_len, false, "on", "ignore", VIRTIO_BLK_S_OK), - (0, 2048, 0, wz_len, false, "on", "unmap", VIRTIO_BLK_S_OK), - (0, 2048, 0, wz_len, true, "unmap", "ignore", VIRTIO_BLK_S_OK), - ( - 0, - 2048, - 0, - wz_len, - false, - "unmap", - "ignore", - VIRTIO_BLK_S_OK, - ), - ( - 0, - 2048, - 0, - wz_len, - false, - "off", - "ignore", - VIRTIO_BLK_S_UNSUPP, - ), - ( - 0, - 2048, - 0, - wz_len, - false, - "off", - "unmap", - VIRTIO_BLK_S_UNSUPP, - ), - (0, 2048, 1, wz_len, true, "unmap", "unmap", VIRTIO_BLK_S_OK), - (0, 8, 0, req_len, true, "unmap", "unmap", VIRTIO_BLK_S_OK), - (0, 0, 0, req_len, true, "on", "unmap", VIRTIO_BLK_S_OK), - ]; - let mut i = 1; - for (sector, num_sectors, flags, len, enabled, write_zeroes, discard, status) in reqs { - println!("blk_feature_write_zeroes: request {}", i); - i += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); - let device_args = Rc::new(String::from("")); - let drive_args = Rc::new(format!( - ",detect-zeroes={},discard={},direct=false", - write_zeroes, discard - )); - let other_args = Rc::new(String::from("")); - let (blk, test_state, alloc) = - create_blk(image_path.clone(), device_args, drive_args, other_args); - - let mut features = virtio_blk_default_feature(blk.clone()); - if discard == "unmap" { - features |= 1 << VIRTIO_BLK_F_DISCARD; - } - if enabled { - features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES; - } else { - features &= !(1 << VIRTIO_BLK_F_WRITE_ZEROES); - } +fn blk_feature_discard() { + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let req_len = std::mem::size_of::(); + // (sector, num_sectors, flags, req_len, enable_feature, discard, status) + let reqs = [ + (0, 2048, 0, req_len, true, "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, req_len, false, "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, 8, true, "unmap", 0xff), + (0, 2048, 0, 32, true, "unmap", VIRTIO_BLK_S_UNSUPP), + (0, 2048, 1, req_len, true, "unmap", VIRTIO_BLK_S_UNSUPP), + (0, 2048, 0xff, req_len, true, "unmap", VIRTIO_BLK_S_UNSUPP), + ( + 0, + (TEST_IMAGE_SIZE >> 9) as u32 + 1, + 0, + req_len, + true, + "unmap", + VIRTIO_BLK_S_IOERR, + ), + ( + 0, + MAX_REQUEST_SECTORS + 1, + 0, + req_len, + true, + "unmap", + VIRTIO_BLK_S_IOERR, + ), + (0, 2048, 0, req_len, false, "ignore", VIRTIO_BLK_S_UNSUPP), + ]; + let mut i = 1; + for (sector, num_sectors, flags, len, enabled, discard, status) in reqs { + println!("blk_feature_discard: request {}", i); + i += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &image_type)); + let full_disk_size = get_disk_size(image_path.clone()); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(format!(",discard={},direct=false", discard)); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); - let virtqueues = - blk.borrow_mut() - .init_device(test_state.clone(), alloc.clone(), features, 1); - if enabled { - virtio_blk_check_write_zeroes_config(blk.clone()); - } + let mut features = virtio_blk_default_feature(blk.clone()); + if enabled { + features |= 1 << VIRTIO_BLK_F_DISCARD; + } else { + features &= !(1 << VIRTIO_BLK_F_DISCARD); + } - virtio_blk_write( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - true, - ); + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + if discard != "ignore" { + virtio_blk_check_discard_config(blk.clone()); + } - if len == wz_len { - let req_data = VirtBlkDiscardWriteZeroes { - sector, - num_sectors, - flags, + let mut need_poll_elem = true; + let req_data = if len == req_len { + let req = VirtBlkDiscardWriteZeroes { + sector, + num_sectors, + flags, + }; + req.as_bytes().to_vec() + } else { + if len < req_len { + need_poll_elem = false; + } + vec![0; len] }; virtio_blk_discard_and_write_zeroes( blk.clone(), test_state.clone(), alloc.clone(), virtqueues[0].clone(), - &req_data.as_bytes().to_vec(), + &req_data, status, + need_poll_elem, true, - false, ); - } else { - virtio_blk_read_write_zeroes( + if image_type == ImageType::Raw && status == VIRTIO_BLK_S_OK { + let image_size = get_disk_size(image_path.clone()); + assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); + } else if image_type == ImageType::Qcow2 + && status == VIRTIO_BLK_S_OK + && (num_sectors as u64 * 512 & CLUSTER_SIZE - 1) == 0 + { + // If the disk format is equal to Qcow2. + // the length of the num sectors needs to be aligned with the cluster size, + // otherwise the calculated file size is not accurate. + let image_size = get_disk_size(image_path.clone()); + let delete_num = (num_sectors as u64 * 512) >> 10; + assert_eq!(image_size, full_disk_size - delete_num); + } + + tear_down( blk.clone(), test_state.clone(), alloc.clone(), - virtqueues[0].clone(), - VIRTIO_BLK_T_OUT, - 0, - 4096, + virtqueues, + image_path.clone(), ); } + } +} + +/// Block device sends I/O request with feature 'VIRTIO_BLK_F_WRITE_ZEROES'. +/// TestStep: +/// 1. Init block device with feature 'VIRTIO_BLK_F_WRITE_ZEROES'. +/// 2. Do the write-zeroes request with different arguments. +/// 3. Destroy device. +/// Expect: +/// 1/3: success. +/// 2: success or failure, stratovirt process is normal. +#[test] +fn blk_feature_write_zeroes() { + for image_type in ImageType::IMAGE_TYPE { + println!("Image type: {:?}", image_type); + let wz_len = size_of::(); + let req_len = size_of::(); + // (sector, num_sectors, flags, req_len, enable_feature, write_zeroes, discard, status) + let reqs = [ + (0, 2048, 0, wz_len, true, "on", "ignore", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, true, "on", "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, false, "on", "ignore", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, false, "on", "unmap", VIRTIO_BLK_S_OK), + (0, 2048, 0, wz_len, true, "unmap", "ignore", VIRTIO_BLK_S_OK), + ( + 0, + 2048, + 0, + wz_len, + false, + "unmap", + "ignore", + VIRTIO_BLK_S_OK, + ), + ( + 0, + 2048, + 0, + wz_len, + false, + "off", + "ignore", + VIRTIO_BLK_S_UNSUPP, + ), + ( + 0, + 2048, + 0, + wz_len, + false, + "off", + "unmap", + VIRTIO_BLK_S_UNSUPP, + ), + (0, 2048, 1, wz_len, true, "unmap", "unmap", VIRTIO_BLK_S_OK), + (0, 8, 0, req_len, true, "unmap", "unmap", VIRTIO_BLK_S_OK), + (0, 0, 0, req_len, true, "on", "unmap", VIRTIO_BLK_S_OK), + ]; + let mut i = 1; + for (sector, num_sectors, flags, len, enabled, write_zeroes, discard, status) in reqs { + println!("blk_feature_write_zeroes: request {}", i); + i += 1; + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1, &image_type)); + let full_disk_size = get_disk_size(image_path.clone()); + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(format!( + ",detect-zeroes={},discard={},direct=false", + write_zeroes, discard + )); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &image_type, + image_path.clone(), + device_args, + drive_args, + other_args, + ); + + let mut features = virtio_blk_default_feature(blk.clone()); + if discard == "unmap" { + features |= 1 << VIRTIO_BLK_F_DISCARD; + } + if enabled { + features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES; + } else { + features &= !(1 << VIRTIO_BLK_F_WRITE_ZEROES); + } + + let virtqueues = + blk.borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + if enabled { + virtio_blk_check_write_zeroes_config(blk.clone()); + } - if write_zeroes != "off" { - virtio_blk_read_write_zeroes( + virtio_blk_write( blk.clone(), test_state.clone(), alloc.clone(), virtqueues[0].clone(), - VIRTIO_BLK_T_IN, 0, - 512, + true, ); - } - if status == VIRTIO_BLK_S_OK - && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) - { - let image_size = get_disk_size(image_path.clone()); - assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); - } + if len == wz_len { + let req_data = VirtBlkDiscardWriteZeroes { + sector, + num_sectors, + flags, + }; + virtio_blk_discard_and_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + &req_data.as_bytes().to_vec(), + status, + true, + false, + ); + } else { + virtio_blk_read_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_OUT, + 0, + 4096, + ); + } - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + if write_zeroes != "off" { + virtio_blk_read_write_zeroes( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + VIRTIO_BLK_T_IN, + 0, + 512, + ); + } + + if image_type == ImageType::Raw + && status == VIRTIO_BLK_S_OK + && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) + { + let image_size = get_disk_size(image_path.clone()); + assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); + } else if image_type == ImageType::Qcow2 + && status == VIRTIO_BLK_S_OK + && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) + && (num_sectors as u64 * 512 & CLUSTER_SIZE - 1) == 0 + { + // If the disk format is equal to Qcow2. + // the length of the num sectors needs to be aligned with the cluster size, + // otherwise the calculated file size is not accurate. + let image_size = get_disk_size(image_path.clone()); + let delete_num = (num_sectors as u64 * 512) >> 10; + assert_eq!(image_size, full_disk_size - delete_num); + } + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); + } } } diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index 609fcb6a0..977e4c6b5 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -15,7 +15,7 @@ use devices::legacy::FwCfgEntryType; use mod_test::libdriver::fwcfg::{bios_args, FW_CFG_BASE}; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libtest::test_init; -use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; +use mod_test::utils::{cleanup_img, create_img, ImageType, TEST_IMAGE_SIZE}; use mod_test::utils::{swap_u16, swap_u32}; use std::cell::RefCell; @@ -187,7 +187,7 @@ fn test_boot_index() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let dev_path = "/pci@ffffffffffffffff/scsi@1/disk@0,0\n\0".to_string(); diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 114275c05..6c7a544f1 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -21,7 +21,7 @@ use mod_test::libdriver::virtio_block::{ }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::{test_init, TestState}; -use mod_test::utils::{cleanup_img, create_img, read_le_u16, TEST_IMAGE_SIZE}; +use mod_test::utils::{cleanup_img, create_img, read_le_u16, ImageType, TEST_IMAGE_SIZE}; use serde_json::json; use std::cell::RefCell; @@ -226,7 +226,7 @@ fn build_blk_driver_args(blk_nums: u8) -> (Vec, Vec) { let mut image_paths: Vec = Vec::new(); for i in 0..blk_nums { - let image_path = create_img(TEST_IMAGE_SIZE, 1); + let image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(image_path.clone()); let driver_arg_str = format!( "-drive if=none,id=drive-{},file={},format=raw,direct=false", @@ -764,7 +764,7 @@ fn hotplug_blk( slot: u8, func: u8, ) { - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 2:0:0. @@ -1030,7 +1030,7 @@ fn test_pci_device_discovery_003() { ); let blk_id = 1; - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. @@ -1071,7 +1071,7 @@ fn test_pci_device_discovery_004() { ))); let blk_id = 0; - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose id is 0 and bdf is 1:0:0. @@ -1871,7 +1871,7 @@ fn test_pci_hotplug_003() { let (test_state, _machine, alloc, mut image_paths) = set_up(root_port_nums, blk_nums, true, false); - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose id is 0, bdf is 1:1:0. @@ -1894,7 +1894,7 @@ fn test_pci_hotplug_004() { let (test_state, _machine, alloc, mut image_paths) = set_up(root_port_nums, blk_nums, true, false); - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); let hotplug_blk_id = 1; @@ -1916,7 +1916,7 @@ fn test_pci_hotplug_005() { let (test_state, _machine, alloc, mut image_paths) = set_up(root_port_nums, blk_nums, true, false); - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); let hotplug_blk_id = 0; @@ -1939,7 +1939,7 @@ fn test_pci_hotplug_006() { let (test_state, _machine, alloc, mut image_paths) = set_up(root_port_nums, blk_nums, true, false); - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); let hotplug_blk_id = 0; @@ -1979,7 +1979,7 @@ fn test_pci_hotplug_007() { let slot = 0; let func = 0; let hotplug_blk_id = 0; - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. @@ -2399,7 +2399,7 @@ fn test_pci_hotplug_combine_001() { ))); let hotplug_blk_id = 0; - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. @@ -2455,7 +2455,7 @@ fn test_pci_hotplug_combine_001() { ); let hotplug_blk_id = 1; - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. @@ -2640,7 +2640,7 @@ fn test_pci_hotplug_combine_003() { let ret = test_state.borrow().qmp(&delete_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. @@ -2654,7 +2654,7 @@ fn test_pci_hotplug_combine_003() { power_off_device(root_port.clone()); test_state.borrow().wait_qmp_event(); - let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1); + let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 7005ff916..743d33ddc 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -29,7 +29,7 @@ use mod_test::libdriver::virtio::{ }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::{test_init, TestState}; -use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; +use mod_test::utils::{cleanup_img, create_img, ImageType, TEST_IMAGE_SIZE}; const TEST_VIRTIO_SCSI_CDB_SIZE: usize = 32; const TEST_VIRTIO_SCSI_SENSE_SIZE: usize = 96; @@ -126,7 +126,7 @@ impl VirtioScsiTest { image_size: u64, iothread: bool, ) -> VirtioScsiTest { - let image_path = Rc::new(create_img(image_size, 1)); + let image_path = Rc::new(create_img(image_size, 1, &ImageType::Raw)); let cntlrcfg = CntlrConfig { id: 0, @@ -1864,7 +1864,7 @@ fn aio_model_test() { if aio_probe(AioEngine::IoUring).is_ok() { // Scsi Disk 1. AIO io_uring. Direct false. - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw)); device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, @@ -1879,7 +1879,7 @@ fn aio_model_test() { // Scsi Disk 2. AIO io_uring. Direct true. lun += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw)); device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, @@ -1898,7 +1898,7 @@ fn aio_model_test() { //Scsi Disk 4. AIO OFF. Direct false. lun += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw)); device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, @@ -1916,7 +1916,7 @@ fn aio_model_test() { if aio_probe(AioEngine::Native).is_ok() { // Scsi Disk 6. AIO native. Direct true. lun += 1; - let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1)); + let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw)); device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs index 8393a6c61..15c74da6f 100644 --- a/tests/mod_test/tests/usb_storage_test.rs +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -24,12 +24,12 @@ use devices::usb::{ UsbDeviceRequest, }; -use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::usb::{ TestIovec, TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, STORAGE_DEVICE_IN_ENDPOINT_ID, STORAGE_DEVICE_OUT_ENDPOINT_ID, }; use mod_test::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; +use mod_test::{libdriver::malloc::GuestAllocator, utils::ImageType}; const READ_10: u8 = 0x28; const WRITE_10: u8 = 0x2a; @@ -174,7 +174,7 @@ fn csw_phase( /// 0/1/2/3/4/5/6/7: success. #[test] fn usb_storage_basic() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "disk") @@ -272,7 +272,7 @@ fn usb_storage_basic() { /// 0/1/2: success. #[test] fn usb_storage_functional_reset() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, _) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -312,7 +312,7 @@ fn usb_storage_functional_reset() { /// 2: Stallerror. #[test] fn usb_storage_functional_get_max_lun() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -371,7 +371,7 @@ fn usb_storage_functional_get_max_lun() { /// 1: StallError. #[test] fn usb_storage_illegal_request() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, _) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -410,7 +410,7 @@ fn usb_storage_illegal_request() { /// 1: CBW StallError. #[test] fn usb_storage_cbw_signature() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -448,7 +448,7 @@ fn usb_storage_cbw_signature() { /// 1: CBW StallError. #[test] fn usb_storage_cbw_illegal_size() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -485,7 +485,7 @@ fn usb_storage_cbw_illegal_size() { /// 2: CSW StallError. #[test] fn usb_storage_csw_illegal_size() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -532,7 +532,7 @@ fn usb_storage_csw_illegal_size() { /// 2: CSW StallError. #[test] fn usb_storage_abnormal_phase_01() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -588,7 +588,7 @@ fn usb_storage_abnormal_phase_01() { /// 3: CSW StallError. #[test] fn usb_storage_abnormal_phase_02() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -645,7 +645,7 @@ fn usb_storage_abnormal_phase_02() { /// 2: CBW StallError. #[test] fn usb_storage_abnormal_phase_03() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -703,7 +703,7 @@ fn usb_storage_abnormal_phase_03() { /// 2: CSW StallError. #[test] fn usb_storage_illegal_scsi_cdb() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -755,7 +755,7 @@ fn usb_storage_illegal_scsi_cdb() { /// 2: StallError. #[test] fn insufficient_data_buffer_test() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -808,7 +808,7 @@ fn insufficient_data_buffer_test() { /// 2: CSW status = UsbMsdCswStatus::Failed. #[test] fn usb_storage_not_supported_scsi_cdb() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -860,7 +860,7 @@ fn usb_storage_not_supported_scsi_cdb() { /// 1: CBW StallError. #[test] fn usb_storage_cbw_invalid_endpoint() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") @@ -904,7 +904,7 @@ fn usb_storage_cbw_invalid_endpoint() { /// 2: CSW StallError. #[test] fn usb_storage_csw_invalid_endpoint() { - let image_path = create_img(TEST_IMAGE_SIZE, 0); + let image_path = create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw); let (xhci, test_state, guest_allocator) = TestUsbBuilder::new() .with_xhci("xhci") .with_usb_storage(&image_path, "cdrom") diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index c254d6bbf..2920dcc8a 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -31,7 +31,7 @@ use mod_test::libdriver::virtio_block::{ }; use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; use mod_test::libtest::TestState; -use mod_test::utils::TEST_IMAGE_SIZE; +use mod_test::utils::{ImageType, TEST_IMAGE_SIZE}; fn add_request( test_state: Rc>, @@ -198,7 +198,7 @@ fn check_queue(blk: Rc>, desc: u64, avail: u64, used: } fn do_event_idx_with_flag(flag: u16) { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -310,7 +310,7 @@ fn do_event_idx_with_flag(flag: u16) { /// 2: device can't handle the io request. #[test] fn virtio_feature_none() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk .borrow_mut() @@ -357,7 +357,7 @@ fn virtio_feature_none() { /// 1/2/3/4: success. #[test] fn virtio_feature_vertion_1() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -434,7 +434,7 @@ fn virtio_feature_vertion_1() { /// 1/2/3: success. #[test] fn virtio_feature_indirect() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -572,7 +572,7 @@ fn virtio_feature_event_idx() { /// 1/2/3: success. #[test] fn virtio_feature_indirect_and_event_idx() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -701,7 +701,7 @@ fn virtio_feature_indirect_and_event_idx() { /// 3/4: success. #[test] fn virtio_init_device_abnormal_status() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); // Test some special status. let status = [31, 0, 2, 16, 31, 0, 1, 16, 31, 0, 7, 16, 31, 64, 128]; @@ -787,7 +787,7 @@ fn virtio_init_device_abnormal_status() { #[test] fn virtio_init_device_abnormal_features() { for i in 0..2 { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); // 1. Init device. blk.borrow_mut().reset(); @@ -898,7 +898,7 @@ fn virtio_init_device_abnormal_vring_info() { ]; for (err_type, value, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); // 1. Init device. blk.borrow_mut().reset(); @@ -1102,7 +1102,7 @@ fn virtio_init_device_abnormal_vring_info() { /// 3/4: success. #[test] fn virtio_init_device_out_of_order_1() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let tests = vec![ [1, 3, 2, 4, 5, 6, 7, 8], @@ -1161,7 +1161,7 @@ fn virtio_init_device_out_of_order_1() { /// 3/4: success. #[test] fn virtio_init_device_out_of_order_2() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let tests = vec![ [1, 2, 3, 4, 8, 6, 7, 5], @@ -1220,7 +1220,7 @@ fn virtio_init_device_out_of_order_2() { /// 3/4: success. #[test] fn virtio_init_device_out_of_order_3() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let tests = vec![ [1, 2, 3, 4, 5, 6, 7, 0], @@ -1279,7 +1279,7 @@ fn virtio_init_device_out_of_order_3() { /// 3/4: success. #[test] fn virtio_init_device_repeat() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); // Reset virtio device twice. blk.borrow_mut().reset(); @@ -1355,7 +1355,7 @@ fn virtio_io_abnormal_desc_addr() { (u64::MAX, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), ]; for (mut addr, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1423,7 +1423,7 @@ fn virtio_io_abnormal_desc_len() { (16, 65, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), ]; for (length, io_num, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1529,7 +1529,7 @@ fn virtio_io_abnormal_desc_flags_1() { (16, 0, 0), ]; for (flag, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1580,7 +1580,7 @@ fn virtio_io_abnormal_desc_flags_1() { /// 1/3/4: success. #[test] fn virtio_io_abnormal_desc_flags_2() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1654,7 +1654,7 @@ fn virtio_io_abnormal_desc_flags_3() { (VRING_DESC_F_NEXT, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), ]; for (flag, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1740,7 +1740,7 @@ fn virtio_io_abnormal_desc_next() { (u16::MAX, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), ]; for (next, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1798,7 +1798,7 @@ fn virtio_io_abnormal_desc_next() { /// 1/3/4: success. #[test] fn virtio_io_abnormal_desc_elem_place() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1856,7 +1856,7 @@ fn virtio_io_abnormal_desc_elem_place() { /// 2: success or failure. #[test] fn virtio_io_abnormal_indirect_desc_elem_num() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1958,7 +1958,7 @@ fn virtio_io_abnormal_avail_flags() { fn virtio_io_abnormal_avail_idx() { let idxs = [16, u16::MAX]; for idx in idxs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -2009,7 +2009,7 @@ fn virtio_io_abnormal_avail_ring() { // (ring[i], ack, device_status) let reqs = [(u16::MAX, 0xff, VIRTIO_CONFIG_S_NEEDS_RESET), (0, 0xff, 0)]; for (value, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -2074,7 +2074,7 @@ fn virtio_io_abnormal_used_event() { (VIRTIO_RING_F_EVENT_IDX, 0, 0, 0), ]; for (feature, used_event, ack, device_status) in reqs { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -2154,7 +2154,7 @@ fn virtio_io_abnormal_used_event() { /// 1/2/3: success. #[test] fn virtio_io_abnormal_used_idx() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -2210,7 +2210,7 @@ fn virtio_io_abnormal_used_idx() { /// 3/4: success or failure. #[test] fn virtio_test_out_of_order_1() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -2278,7 +2278,7 @@ fn virtio_test_out_of_order_1() { /// 1/2/3/4/5: success. #[test] fn virtio_test_out_of_order_2() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -2294,7 +2294,7 @@ fn virtio_test_out_of_order_2() { image_path.clone(), ); - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -2339,7 +2339,7 @@ fn virtio_test_out_of_order_2() { /// 1/2/3/4/5/6/7: success. #[test] fn virtio_test_repeat() { - let (blk, test_state, alloc, image_path) = set_up(); + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); blk.borrow_mut().init_device( test_state.clone(), -- Gitee From 916215ab8193e06caeb9b00980af9e93d9afcaa5 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 8 Jul 2023 10:51:01 +0800 Subject: [PATCH 1170/2187] qcow2: flush when process exit Flush cache and try to drain io request when process exit. Signed-off-by: zhouli57 --- block_backend/src/lib.rs | 23 +++++++++++++++++++++-- block_backend/src/qcow2/mod.rs | 2 +- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index a72f33ad4..28474b881 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -21,8 +21,12 @@ use std::{ }; use anyhow::{bail, Context, Result}; +use log::{error, info}; -use machine_manager::config::DiskFormat; +use machine_manager::{ + config::DiskFormat, + temp_cleaner::{ExitNotifier, TempCleaner}, +}; use qcow2::{Qcow2Driver, QCOW2_LIST}; use raw::RawDriver; use util::aio::{Aio, Iovec, WriteZeroesState}; @@ -110,7 +114,22 @@ pub fn create_block_backend( QCOW2_LIST .lock() .unwrap() - .insert(drive_id, new_qcow2.clone()); + .insert(drive_id.clone(), new_qcow2.clone()); + let cloned_qcow2 = new_qcow2.clone(); + // NOTE: we can drain request when request in io thread. + let drain = prop.iothread.is_some(); + let cloned_drive_id = drive_id.clone(); + let exit_notifier = Arc::new(move || { + let mut locked_qcow2 = cloned_qcow2.lock().unwrap(); + info!("clean up qcow2 {:?} resources.", cloned_drive_id); + if let Err(e) = locked_qcow2.flush() { + error!("Failed to flush qcow2 {:?}", e); + } + if drain { + locked_qcow2.drain_request(); + } + }) as Arc; + TempCleaner::add_exit_notifier(drive_id, exit_notifier); Ok(new_qcow2) } } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index c3f5703b1..42f8c03b5 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -242,7 +242,7 @@ impl Qcow2Driver { Ok(()) } - fn flush(&mut self) -> Result<()> { + pub fn flush(&mut self) -> Result<()> { self.table.flush() } -- Gitee From 4299b9288c8363acf2f622503d0bdb4c965dc603 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 14:38:24 +0800 Subject: [PATCH 1171/2187] Qcow2: fix some error 1. The data in the qcow2 cache may not be consistent with the disk data, so it is necessary to first obtain the data from the cache. 2. Add discard task processing after updating the discard list. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 22 ++++++++++++++-------- block_backend/src/qcow2/refcount.rs | 8 +++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 5de7c00e6..abdf0b803 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -353,11 +353,14 @@ impl Qcow2Driver { // Alloc a new l2_table. let old_l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; let new_l2_offset = self.alloc_cluster(1, true)?; - let l2_cluster = if old_l2_offset != 0 { - self.load_cluster(l2_address)? - } else { - vec![0_u8; self.header.cluster_size() as usize] - }; + let l2_cluster: Vec = + if let Some(entry) = self.table.get_l2_table_cache_entry(guest_offset) { + entry.borrow().get_value().to_vec() + } else if old_l2_offset != 0 { + self.load_cluster(l2_address)? + } else { + vec![0_u8; self.header.cluster_size() as usize] + }; self.sync_aio .borrow_mut() .write_buffer(new_l2_offset, &l2_cluster)?; @@ -551,6 +554,10 @@ impl Qcow2Driver { } fn alloc_cluster(&mut self, clusters: u64, write_zero: bool) -> Result { + if !self.refcount.discard_list.is_empty() { + self.refcount.sync_process_discards(OpCode::Discard); + } + let size = clusters * self.header.cluster_size(); let addr = self.refcount.alloc_cluster(&mut self.header, size)?; if write_zero && addr < self.driver.disk_size()? { @@ -686,6 +693,7 @@ impl Qcow2Driver { } self.header.snapshots_offset = new_header.snapshots_offset; self.header.nb_snapshots = new_header.nb_snapshots; + self.refcount.sync_process_discards(OpCode::Discard); return Ok(SnapshotInfo { id: snap.id.to_string(), @@ -863,6 +871,7 @@ impl Qcow2Driver { } self.header.snapshots_offset = new_header.snapshots_offset; self.header.nb_snapshots = new_header.nb_snapshots; + self.refcount.sync_process_discards(OpCode::Discard); return Ok(()); } @@ -1027,9 +1036,6 @@ impl Qcow2Driver { } } } - self.refcount - .sync_process_discards(OpCode::Discard) - .unwrap_or_else(|e| error!("Snapshot discard failed: {:?}", e)); self.refcount.flush_refcount_block_cache()?; if l1_changed { self.sync_aio diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index ab47e9d86..635d2bd3e 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -412,17 +412,19 @@ impl RefCount { } /// Process discards task by sync aio. - pub fn sync_process_discards(&mut self, opcode: OpCode) -> Result<()> { + pub fn sync_process_discards(&mut self, opcode: OpCode) { for task in self.discard_list.iter() { let offset = task.offset; let nbytes = task.nbytes; let mut borrowed_sync_aio = self.sync_aio.borrow_mut(); let discard_aio = borrowed_sync_aio.package_sync_aiocb(opcode, Vec::new(), offset as usize, nbytes); - borrowed_sync_aio.aio.submit_request(discard_aio)?; + borrowed_sync_aio + .aio + .submit_request(discard_aio) + .unwrap_or_else(|e| error!("Discard failed: {:?}", e)); } self.discard_list.clear(); - Ok(()) } pub fn offset_into_cluster(&self, offset: u64) -> u64 { -- Gitee From 9645ed82d30ee80fe5471b46ec46152f74cf09df Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 18:06:24 +0800 Subject: [PATCH 1172/2187] Qcow2: Optimize the discard process 1. Reduce the refresh times of the refcount block and place it outside for one refresh. 2. Optimize the merging process for discard Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 11 +++++++++-- block_backend/src/qcow2/refcount.rs | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index abdf0b803..37e1c0144 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -521,7 +521,7 @@ impl Qcow2Driver { nbytes ); } - self.free_cluster(offset, 1, discard_type)?; + self.free_cluster(offset, 1, false, discard_type)?; } Qcow2ClusterType::Compressed => { bail!("Compressed is not supported"); @@ -586,10 +586,11 @@ impl Qcow2Driver { &mut self, addr: u64, clusters: u64, + flush: bool, discard_type: &Qcow2DiscardType, ) -> Result<()> { self.refcount - .update_refcount(addr, clusters, -1, true, discard_type) + .update_refcount(addr, clusters, -1, flush, discard_type) } fn get_snapshot_by_name(&mut self, name: &String) -> i32 { @@ -735,6 +736,7 @@ impl Qcow2Driver { self.free_cluster( new_snapshot_table_offset, new_snapshots_table_clusters, + true, &Qcow2DiscardType::Never, )?; } @@ -899,6 +901,7 @@ impl Qcow2Driver { self.free_cluster( new_snapshot_table_offset, num_clusters, + true, &Qcow2DiscardType::Snapshot, )?; } @@ -909,6 +912,7 @@ impl Qcow2Driver { self.free_cluster( new_l1_table_offset, l1_table_clusters, + true, &Qcow2DiscardType::Snapshot, )?; } @@ -1252,6 +1256,9 @@ impl Qcow2Driver { } } + self.refcount + .flush_refcount_block_cache() + .unwrap_or_else(|e| error!("Flush refcount block failed: {:?}", e)); self.table .flush_l2_table_cache() .unwrap_or_else(|e| error!("Flush l2 table cache failed: {:?}", e)); diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 635d2bd3e..229e5a197 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -398,7 +398,8 @@ impl RefCount { /// Add discard task to the list. fn update_discard_list(&mut self, offset: u64, nbytes: u64) -> Result<()> { let mut discard_task = DiscardTask { offset, nbytes }; - let mut discard_list: Vec = Vec::new(); + let len = self.discard_list.len(); + let mut discard_list: Vec = Vec::with_capacity(len + 1); for task in self.discard_list.iter() { if discard_task.is_overlap(task) { discard_task.merge_task(task); -- Gitee From 197a3d8257cba6c3f4675ab1d368cc6c9fe55eff Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 18:15:23 +0800 Subject: [PATCH 1173/2187] Ui: enable gettext-system feature for gettext-rs crate Enable gettext-system feature for gettext-rs crate, which can reduce compilation time. Suggested-by: Yan Wen Signed-off-by: Xiao Ye --- ui/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 730d57657..a9676619a 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -20,6 +20,6 @@ rustls-pemfile = "1.0.2" sasl2-sys = "0.1.20" bitintr = "0.3.0" gtk = "0.17.1" -gettext-rs = "0.7.0" +gettext-rs = { version = "0.7.0", features = ["gettext-system"]} machine_manager = { path = "../machine_manager" } util = { path = "../util" } -- Gitee From 6ee7efd5fc8a44cf642263f2c6746fb6f03dd778 Mon Sep 17 00:00:00 2001 From: Andrey Kazmin Date: Tue, 13 Jun 2023 15:42:34 +0300 Subject: [PATCH 1174/2187] Added ACPI ACAD and battery devices Signed-off-by: Andrey Kazmin --- devices/src/acpi/ged.rs | 57 ++- devices/src/acpi/mod.rs | 1 + devices/src/acpi/power.rs | 418 +++++++++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 29 +- machine_manager/src/cmdline.rs | 9 + machine_manager/src/config/machine_config.rs | 8 + 6 files changed, 503 insertions(+), 19 deletions(-) create mode 100644 devices/src/acpi/power.rs diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index ca8a6f182..95741c2b5 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -37,9 +37,12 @@ use std::sync::{Arc, Mutex}; use vmm_sys_util::eventfd::EventFd; #[derive(Clone, Copy)] -enum AcpiEvent { +pub enum AcpiEvent { Nothing = 0, PowerDown = 1, + AcadSt = 2, + BatteryInf = 4, + BatterySt = 8, } const AML_GED_EVT_REG: &str = "EREG"; @@ -49,6 +52,7 @@ const AML_GED_EVT_SEL: &str = "ESEL"; pub struct Ged { interrupt_evt: Arc>, notification_type: Arc, + battery_present: bool, /// System resource. res: SysRes, } @@ -58,6 +62,7 @@ impl Default for Ged { Self { interrupt_evt: Arc::new(None), notification_type: Arc::new(AtomicU32::new(AcpiEvent::Nothing as u32)), + battery_present: false, res: SysRes::default(), } } @@ -68,18 +73,22 @@ impl Ged { mut self, sysbus: &mut SysBus, power_button: Arc, + battery_present: bool, region_base: u64, region_size: u64, - ) -> Result<()> { + ) -> Result>> { self.interrupt_evt = Arc::new(Some(EventFd::new(libc::EFD_NONBLOCK)?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; + self.battery_present = battery_present; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size)?; let ged = dev.lock().unwrap(); ged.register_acpi_powerdown_event(power_button) + .with_context(|| "Failed to register ACPI powerdown event.")?; + Ok(dev.clone()) } fn register_acpi_powerdown_event(&self, power_button: Arc) -> Result<()> { @@ -110,6 +119,12 @@ impl Ged { Ok(()) } + pub fn inject_acpi_event(&self, evt: AcpiEvent) { + self.notification_type + .fetch_or(evt as u32, Ordering::SeqCst); + self.inject_interrupt(); + } + fn inject_interrupt(&self) { if let Some(evt_fd) = self.interrupt_evt() { evt_fd @@ -127,7 +142,6 @@ impl SysBusDevOps for Ged { let value = self .notification_type .swap(AcpiEvent::Nothing as u32, Ordering::SeqCst); - write_data_u32(data, value) } @@ -184,15 +198,34 @@ impl AmlBuilder for Ged { let mut method = AmlMethod::new("_EVT", 1, true); let store = AmlStore::new(AmlName(AML_GED_EVT_SEL.to_string()), AmlLocal(0)); method.append_child(store); - let mut if_scope = AmlIf::new(AmlEqual::new( - AmlAnd::new(AmlLocal(0), AmlInteger(1), AmlLocal(0)), - AmlInteger(1), - )); - if_scope.append_child(AmlNotify::new( - AmlName("PWRB".to_string()), - AmlInteger(0x80), - )); - method.append_child(if_scope); + + struct PowerDevEvent(AcpiEvent, &'static str, u64); + let events: [PowerDevEvent; 4] = [ + PowerDevEvent(AcpiEvent::PowerDown, "PWRB", 0x80), + PowerDevEvent(AcpiEvent::AcadSt, "ACAD", 0x80), + PowerDevEvent(AcpiEvent::BatteryInf, "BAT0", 0x81), + PowerDevEvent(AcpiEvent::BatterySt, "BAT0", 0x80), + ]; + + for event in events.into_iter() { + let evt = event.0 as u64; + let dev = event.1; + let notify = event.2; + + if !self.battery_present + && (evt > AcpiEvent::PowerDown as u64 && evt <= AcpiEvent::BatterySt as u64) + { + break; + } + + let mut if_scope = AmlIf::new(AmlEqual::new( + AmlAnd::new(AmlLocal(0), AmlInteger(evt), AmlLocal(1)), + AmlInteger(evt), + )); + if_scope.append_child(AmlNotify::new(AmlName(dev.to_string()), AmlInteger(notify))); + method.append_child(if_scope); + } + acpi_dev.append_child(method); acpi_dev.aml_bytes() diff --git a/devices/src/acpi/mod.rs b/devices/src/acpi/mod.rs index 54c1644a4..563ce1fa5 100644 --- a/devices/src/acpi/mod.rs +++ b/devices/src/acpi/mod.rs @@ -11,3 +11,4 @@ // See the Mulan PSL v2 for more details. pub mod ged; +pub mod power; diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs new file mode 100644 index 000000000..731dfef43 --- /dev/null +++ b/devices/src/acpi/power.rs @@ -0,0 +1,418 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::Path; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use anyhow::{Context, Result}; +use log::{error, info}; +use util::byte_code::ByteCode; +use util::num_ops::write_data_u32; + +use crate::acpi::ged::{AcpiEvent, Ged}; +use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; +use acpi::{ + AmlAddressSpaceType, AmlBuilder, AmlDevice, AmlField, AmlFieldUnit, AmlIndex, AmlInteger, + AmlMethod, AmlName, AmlNameDecl, AmlOpRegion, AmlPackage, AmlReturn, AmlScopeBuilder, AmlStore, + AmlString, AmlZero, +}; +use address_space::GuestAddress; +use machine_manager::event_loop::EventLoop; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use sysbus::{SysBus, SysBusDevOps, SysRes}; + +const AML_ACAD_REG: &str = "ADPM"; +const AML_ACAD_ONLINE: &str = "ADPO"; + +const AML_ACAD_REG_SZ: u64 = 4; + +const AML_BAT0_REG: &str = "BATM"; +const AML_BAT0_DESIGN_CAP: &str = "DCAP"; +const AML_BAT0_LAST_FULL_CAP: &str = "LFC"; +const AML_BAT0_DESIGN_VOLTAGE: &str = "DV"; +const AML_BAT0_STATE: &str = "ST"; +const AML_BAT0_PRESENT_RATE: &str = "PRT"; +const AML_BAT0_REM_CAP: &str = "RCAP"; +const AML_BAT0_PRES_VOLT: &str = "PV"; + +const POWERDEV_REGS_SIZE: usize = 8; +const REG_IDX_ACAD_ON: usize = 0; +const REG_IDX_BAT_DCAP: usize = 1; +const REG_IDX_BAT_FCAP: usize = 2; +const REG_IDX_BAT_DVOLT: usize = 3; +const REG_IDX_BAT_STATE: usize = 4; +const REG_IDX_BAT_PRATE: usize = 5; +const REG_IDX_BAT_RCAP: usize = 6; +const REG_IDX_BAT_PVOLT: usize = 7; + +const ACPI_BATTERY_STATE_DISCHARGING: u32 = 0x1; +const ACPI_BATTERY_STATE_CHARGING: u32 = 0x2; + +const ACAD_SYSFS_DIR: &str = "/sys/class/power_supply/Mains"; +const BAT_SYSFS_DIR: &str = "/sys/class/power_supply/Battery"; + +#[repr(C)] +#[derive(Copy, Clone, Desc, ByteCode)] +#[desc_version(compat_version = "0.1.0")] +pub struct PowerDevState { + last_acad_st: u32, + last_bat_st: u32, + last_bat_lvl: u32, +} + +#[derive(Clone)] +pub struct PowerDev { + regs: Vec, + state: PowerDevState, + ged: Arc>, + /// System resource. + res: SysRes, +} + +impl PowerDev { + pub fn new(ged_dev: Arc>) -> Self { + Self { + regs: vec![0; POWERDEV_REGS_SIZE], + state: PowerDevState { + last_acad_st: 1, + last_bat_st: ACPI_BATTERY_STATE_CHARGING, + last_bat_lvl: 0xffffffff, + }, + ged: ged_dev, + res: SysRes::default(), + } + } + + fn read_sysfs_power_props( + &self, + dir_name: &str, + sysfs_props: &Vec<&str>, + pdev_props: &mut [u32], + ) -> Result<()> { + for i in 0..sysfs_props.len() { + let df = format!("{}/{}", dir_name, sysfs_props[i]); + let path = Path::new(&df); + let sprop = std::fs::read_to_string(path).with_context(|| { + format!("Can't read power device property: {}", path.display(),) + })?; + let prop = sprop[..sprop.len() - 1].parse::().with_context(|| { + format!( + "Can't parse power device property: {} value: {}", + path.display(), + sprop + ) + })?; + /* All the values except "online" property is multiplicated by 1000 + * Only "online" property starts with 'o' character + */ + pdev_props[i] = if sysfs_props[i].starts_with('o') { + prop.unsigned_abs() as u32 + } else { + (prop.abs() / 1000) as u32 + }; + } + Ok(()) + } + + fn power_battery_init_info(&mut self) -> Result<()> { + let bat_sysfs_props = vec!["energy_full_design", "energy_full", "voltage_max_design"]; + let mut props: Vec = vec![0; bat_sysfs_props.len()]; + self.read_sysfs_power_props(BAT_SYSFS_DIR, &bat_sysfs_props, &mut props)?; + self.regs[REG_IDX_BAT_DCAP] = props[0]; + self.regs[REG_IDX_BAT_FCAP] = props[1]; + self.regs[REG_IDX_BAT_DVOLT] = props[2]; + Ok(()) + } + + pub fn power_status_read(&mut self) -> Result<()> { + let acad_props = vec!["online"]; + let bat_sysfs_props = vec!["online", "current_now", "energy_now", "voltage_now"]; + let mut props: Vec = vec![0; bat_sysfs_props.len()]; + + self.read_sysfs_power_props(ACAD_SYSFS_DIR, &acad_props, &mut props)?; + self.regs[REG_IDX_ACAD_ON] = props[0]; + + self.read_sysfs_power_props(BAT_SYSFS_DIR, &bat_sysfs_props, &mut props)?; + self.regs[REG_IDX_BAT_STATE] = if props[0] == 1 { + ACPI_BATTERY_STATE_CHARGING + } else { + ACPI_BATTERY_STATE_DISCHARGING + }; + // unit: mA + self.regs[REG_IDX_BAT_PRATE] = props[1]; + self.regs[REG_IDX_BAT_RCAP] = props[2]; + self.regs[REG_IDX_BAT_PVOLT] = props[3]; + // unit: mW + self.regs[REG_IDX_BAT_PRATE] = + (self.regs[REG_IDX_BAT_PRATE] * self.regs[REG_IDX_BAT_PVOLT]) / 1000; + Ok(()) + } + + pub fn power_load_static_status(&mut self) { + info!("Load static power devices status"); + self.regs[REG_IDX_ACAD_ON] = 1; + self.regs[REG_IDX_BAT_DCAP] = 0xffffffff; + self.regs[REG_IDX_BAT_FCAP] = 0xffffffff; + self.regs[REG_IDX_BAT_DVOLT] = 0xffffffff; + self.regs[REG_IDX_BAT_STATE] = ACPI_BATTERY_STATE_CHARGING; + self.regs[REG_IDX_BAT_PRATE] = 0; + self.regs[REG_IDX_BAT_RCAP] = 0xffffffff; + self.regs[REG_IDX_BAT_PVOLT] = 0xffffffff; + } + + fn send_power_event(&self, evt: AcpiEvent) { + self.ged.lock().unwrap().inject_acpi_event(evt); + } +} + +impl PowerDev { + pub fn realize( + mut self, + sysbus: &mut SysBus, + region_base: u64, + region_size: u64, + ) -> Result<()> { + self.set_sys_resource(sysbus, region_base, region_size) + .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; + + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev, region_base, region_size)?; + + let pdev_availiable: bool; + { + let mut pdev = dev.lock().unwrap(); + pdev_availiable = pdev.power_battery_init_info().is_ok(); + if pdev_availiable { + pdev.send_power_event(AcpiEvent::BatteryInf); + } + } + if pdev_availiable { + power_status_update(&dev.clone()); + } else { + let mut pdev = dev.lock().unwrap(); + pdev.power_load_static_status(); + } + Ok(()) + } +} + +impl StateTransfer for PowerDev { + fn get_state_vec(&self) -> migration::Result> { + Ok(self.state.as_bytes().to_vec()) + } + + fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + self.state.as_mut_bytes().copy_from_slice(state); + Ok(()) + } + + fn get_device_alias(&self) -> u64 { + MigrationManager::get_desc_alias(&PowerDevState::descriptor().name).unwrap_or(!0) + } +} + +impl MigrationHook for PowerDev { + fn resume(&mut self) -> migration::Result<()> { + self.send_power_event(AcpiEvent::AcadSt); + self.send_power_event(AcpiEvent::BatterySt); + Ok(()) + } +} + +impl SysBusDevOps for PowerDev { + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { + let reg_idx: u64 = offset / 4; + if reg_idx >= self.regs.len() as u64 { + return false; + } + let value = self.regs[reg_idx as usize]; + write_data_u32(data, value) + } + + fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { + true + } + + fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + Some(&mut self.res) + } +} + +impl AmlBuilder for PowerDev { + fn aml_bytes(&self) -> Vec { + let mut acpi_acad_dev = AmlDevice::new("ACAD"); + acpi_acad_dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0003".to_string()))); + + acpi_acad_dev.append_child(AmlOpRegion::new( + AML_ACAD_REG, + AmlAddressSpaceType::SystemMemory, + self.res.region_base, + AML_ACAD_REG_SZ, + )); + + let mut field = AmlField::new( + AML_ACAD_REG, + AmlFieldAccessType::DWord, + AmlFieldLockRule::NoLock, + AmlFieldUpdateRule::WriteAsZeros, + ); + + field.append_child(AmlFieldUnit::new(Some(AML_ACAD_ONLINE), 32)); + acpi_acad_dev.append_child(field); + + let mut pcl_pkg = AmlPackage::new(1); + pcl_pkg.append_child(AmlName("\\_SB".to_string())); + acpi_acad_dev.append_child(AmlNameDecl::new("_PCL", pcl_pkg)); + + let mut method = AmlMethod::new("_STA", 0, false); + method.append_child(AmlReturn::with_value(AmlInteger(0x0F))); + + acpi_acad_dev.append_child(method); + + method = AmlMethod::new("_PSR", 0, false); + method.append_child(AmlReturn::with_value(AmlName(AML_ACAD_ONLINE.to_string()))); + acpi_acad_dev.append_child(method); + + let mut acpi_bat_dev = AmlDevice::new("BAT0"); + acpi_bat_dev.append_child(AmlNameDecl::new("_HID", AmlString("PNP0C0A".to_string()))); + + acpi_bat_dev.append_child(AmlOpRegion::new( + AML_BAT0_REG, + AmlAddressSpaceType::SystemMemory, + self.res.region_base + AML_ACAD_REG_SZ, + self.res.region_size - AML_ACAD_REG_SZ, + )); + + field = AmlField::new( + AML_BAT0_REG, + AmlFieldAccessType::DWord, + AmlFieldLockRule::NoLock, + AmlFieldUpdateRule::WriteAsZeros, + ); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_DESIGN_CAP), 32)); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_LAST_FULL_CAP), 32)); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_DESIGN_VOLTAGE), 32)); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_STATE), 32)); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_PRESENT_RATE), 32)); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_REM_CAP), 32)); + field.append_child(AmlFieldUnit::new(Some(AML_BAT0_PRES_VOLT), 32)); + acpi_bat_dev.append_child(field); + + pcl_pkg = AmlPackage::new(1); + pcl_pkg.append_child(AmlName("\\_SB".to_string())); + acpi_bat_dev.append_child(AmlNameDecl::new("_PCL", pcl_pkg)); + + method = AmlMethod::new("_STA", 0, false); + method.append_child(AmlInteger(0x1F)); + acpi_bat_dev.append_child(method); + + let mut bif_pkg = AmlPackage::new(13); + bif_pkg.append_child(AmlInteger(0x0)); + bif_pkg.append_child(AmlInteger(0xFFFFFFFF)); + bif_pkg.append_child(AmlInteger(0xFFFFFFFF)); + bif_pkg.append_child(AmlInteger(0x1)); + bif_pkg.append_child(AmlInteger(0xFFFFFFFF)); + bif_pkg.append_child(AmlInteger(0x00000100)); + bif_pkg.append_child(AmlInteger(0x00000050)); + bif_pkg.append_child(AmlInteger(1)); + bif_pkg.append_child(AmlInteger(1)); + bif_pkg.append_child(AmlString("SVBATM1".to_string())); + bif_pkg.append_child(AmlString("000001".to_string())); + bif_pkg.append_child(AmlString("LI-ON".to_string())); + bif_pkg.append_child(AmlString("SVIRT".to_string())); + acpi_bat_dev.append_child(AmlNameDecl::new("PBIF", bif_pkg)); + + method = AmlMethod::new("_BIF", 0, false); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_DESIGN_CAP.to_string()), + AmlIndex::new(AmlName("PBIF".to_string()), AmlInteger(1), AmlZero), + )); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_LAST_FULL_CAP.to_string()), + AmlIndex::new(AmlName("PBIF".to_string()), AmlInteger(2), AmlZero), + )); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_DESIGN_VOLTAGE.to_string()), + AmlIndex::new(AmlName("PBIF".to_string()), AmlInteger(4), AmlZero), + )); + method.append_child(AmlReturn::with_value(AmlName("PBIF".to_string()))); + acpi_bat_dev.append_child(method); + + let mut bst_pkg = AmlPackage::new(4); + bst_pkg.append_child(AmlInteger(ACPI_BATTERY_STATE_CHARGING as u64)); + bst_pkg.append_child(AmlInteger(0xFFFFFFFF)); + bst_pkg.append_child(AmlInteger(0xFFFFFFFF)); + bst_pkg.append_child(AmlInteger(0xFFFFFFFF)); + acpi_bat_dev.append_child(AmlNameDecl::new("PBST", bst_pkg)); + + method = AmlMethod::new("_BST", 0, false); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_STATE.to_string()), + AmlIndex::new(AmlName("PBST".to_string()), AmlInteger(0), AmlZero), + )); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_PRESENT_RATE.to_string()), + AmlIndex::new(AmlName("PBST".to_string()), AmlInteger(1), AmlZero), + )); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_REM_CAP.to_string()), + AmlIndex::new(AmlName("PBST".to_string()), AmlInteger(2), AmlZero), + )); + method.append_child(AmlStore::new( + AmlName(AML_BAT0_PRES_VOLT.to_string()), + AmlIndex::new(AmlName("PBST".to_string()), AmlInteger(3), AmlZero), + )); + method.append_child(AmlReturn::with_value(AmlName("PBST".to_string()))); + acpi_bat_dev.append_child(method); + + acpi_acad_dev + .aml_bytes() + .into_iter() + .chain(acpi_bat_dev.aml_bytes().into_iter()) + .collect() + } +} + +pub fn power_status_update(dev: &Arc>) { + let cdev = dev.clone(); + let update_func = Box::new(move || { + power_status_update(&cdev); + }); + + let mut pdev = dev.lock().unwrap(); + + if pdev.power_status_read().is_ok() { + let step2notify: u32 = pdev.regs[REG_IDX_BAT_FCAP] / 100; + let bdiff: u32 = pdev.regs[REG_IDX_BAT_RCAP].abs_diff(pdev.state.last_bat_lvl); + + if pdev.state.last_acad_st != pdev.regs[REG_IDX_ACAD_ON] { + pdev.send_power_event(AcpiEvent::AcadSt); + pdev.state.last_acad_st = pdev.regs[REG_IDX_ACAD_ON]; + } + if pdev.state.last_bat_st != pdev.regs[REG_IDX_BAT_STATE] || bdiff >= step2notify { + pdev.send_power_event(AcpiEvent::BatterySt); + pdev.state.last_bat_st = pdev.regs[REG_IDX_BAT_STATE]; + pdev.state.last_bat_lvl = pdev.regs[REG_IDX_BAT_RCAP]; + } + + match EventLoop::get_ctx(None) { + Some(ctx) => { + ctx.timer_add(update_func, Duration::from_secs(5)); + } + None => error!("Failed to get ctx to update power devices status"), + } + } else { + pdev.power_load_static_status(); + } +} diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 53a95aa8a..252f4458a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -15,6 +15,7 @@ mod syscall; pub use crate::error::MachineError; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; +use devices::acpi::power::PowerDev; use log::{error, info, warn}; use machine_manager::config::ShutdownAction; #[cfg(not(target_env = "musl"))] @@ -93,6 +94,7 @@ pub enum LayoutEntryType { Rtc, FwCfg, Ged, + PowerDev, Mmio, PcieMmio, PciePio, @@ -112,6 +114,7 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0x0901_0000, 0x0000_1000), // Rtc (0x0902_0000, 0x0000_0018), // FwCfg (0x0908_0000, 0x0000_0004), // Ged + (0x0909_0000, 0x0000_1000), // PowerDev (0x0A00_0000, 0x0000_0200), // Mmio (0x1000_0000, 0x2EFF_0000), // PcieMmio (0x3EFF_0000, 0x0001_0000), // PciePio @@ -534,14 +537,26 @@ impl MachineOps for StdMachine { } fn add_ged_device(&mut self) -> Result<()> { + let battery_present = self.vm_config.lock().unwrap().machine_config.battery; let ged = Ged::default(); - ged.realize( - &mut self.sysbus, - self.power_button.clone(), - MEM_LAYOUT[LayoutEntryType::Ged as usize].0, - MEM_LAYOUT[LayoutEntryType::Ged as usize].1, - ) - .with_context(|| "Failed to realize Ged")?; + let ged_dev = ged + .realize( + &mut self.sysbus, + self.power_button.clone(), + battery_present, + MEM_LAYOUT[LayoutEntryType::Ged as usize].0, + MEM_LAYOUT[LayoutEntryType::Ged as usize].1, + ) + .with_context(|| "Failed to realize Ged")?; + if battery_present { + let pdev = PowerDev::new(ged_dev); + pdev.realize( + &mut self.sysbus, + MEM_LAYOUT[LayoutEntryType::PowerDev as usize].0, + MEM_LAYOUT[LayoutEntryType::PowerDev as usize].1, + ) + .with_context(|| "Failed to realize PowerDev")?; + } Ok(()) } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 691e790e7..2c1ba19f7 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -385,6 +385,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .can_no_value(true) .takes_value(true), ) + .arg( + Arg::with_name("battery") + .long("battery") + .value_name("") + .help("enable battery and power adapter devices") + .takes_value(false) + .required(false), + ) .arg( Arg::with_name("boot") .long("boot") @@ -519,6 +527,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_no_shutdown, bool ); + add_args_to_config!((args.is_present("battery")), vm_cfg, add_battery, bool); add_args_to_config!( (args.is_present("mem-prealloc")), vm_cfg, diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 66982999f..4b6d72dda 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -159,6 +159,7 @@ pub struct MachineConfig { pub mem_config: MachineMemConfig, pub cpu_config: CpuConfig, pub shutdown_action: ShutdownAction, + pub battery: bool, } impl Default for MachineConfig { @@ -176,6 +177,7 @@ impl Default for MachineConfig { mem_config: MachineMemConfig::default(), cpu_config: CpuConfig::default(), shutdown_action: ShutdownAction::default(), + battery: false, } } } @@ -413,6 +415,11 @@ impl VmConfig { self.machine_config.shutdown_action = ShutdownAction::ShutdownActionPause; true } + + pub fn add_battery(&mut self) -> bool { + self.machine_config.battery = true; + true + } } impl VmConfig { @@ -664,6 +671,7 @@ mod tests { mem_config: memory_config, cpu_config: CpuConfig::default(), shutdown_action: ShutdownAction::default(), + battery: false, }; assert!(machine_config.check().is_ok()); -- Gitee From 1694fb67ed12b3c3b38b83af56263c178441c41b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 4 Jul 2023 17:07:27 +0800 Subject: [PATCH 1175/2187] fix VCPU destroy failed in Nothing state Caused by: 0: Failed to destroy vcpu0 1: Failed to destroy kvm vcpu: VCPU still in Nothing state! Signed-off-by: yezengruan --- cpu/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index ffec6eec9..47ca6741c 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -107,8 +107,6 @@ const MAGIC_VALUE_SIGNAL_GUEST_BOOT_COMPLETE: u8 = 0x02; /// State for `CPU` lifecycle. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CpuLifecycleState { - /// `CPU` structure is only be initialized, but nothing set. - Nothing = 0, /// `CPU` structure's property is set with configuration. Created = 1, /// `CPU` start to be running. @@ -423,7 +421,6 @@ impl CPUInterface for CPU { } else if *cpu_state == CpuLifecycleState::Stopped || *cpu_state == CpuLifecycleState::Paused { - *cpu_state = CpuLifecycleState::Nothing; return Ok(()); } @@ -434,7 +431,6 @@ impl CPUInterface for CPU { .0; if *cpu_state == CpuLifecycleState::Stopped { - *cpu_state = CpuLifecycleState::Nothing; Ok(()) } else { Err(anyhow!(CpuError::DestroyVcpu(format!( @@ -1018,7 +1014,7 @@ mod tests { // Wait for CPU finish state change. std::thread::sleep(Duration::from_millis(50)); let (cpu_state, _) = &*cpu_arc.state; - assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Nothing); + assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Stopped); drop(cpu_state); } -- Gitee From 9da7a068c261c8765f49fe142f2348e3e944904c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 17 Jun 2023 11:08:02 +0800 Subject: [PATCH 1176/2187] block_backend: support qcow2 format in command line Command line parsing supports the qcow2 format. Signed-off-by: zhouli57 --- block_backend/src/lib.rs | 16 ++++++--- devices/src/scsi/disk.rs | 1 + machine/src/micro_vm/mod.rs | 2 ++ machine/src/standard_vm/mod.rs | 18 ++++++++-- machine_manager/src/config/drive.rs | 56 ++++++++++++++++++++--------- machine_manager/src/config/scsi.rs | 19 ++++++---- virtio/src/device/block.rs | 1 + 7 files changed, 83 insertions(+), 30 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 6e75ab03c..f20ddb7fb 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -19,8 +19,9 @@ use std::{ sync::{atomic::AtomicBool, Arc, Mutex}, }; -use anyhow::Result; +use anyhow::{bail, Result}; +use machine_manager::config::DiskFormat; use raw::RawDriver; use util::aio::{Aio, Iovec, WriteZeroesState}; @@ -29,6 +30,7 @@ pub type BlockIoErrorCallback = Arc; #[derive(Debug, Clone)] pub struct BlockProperty { + pub format: DiskFormat, pub iothread: Option, pub direct: bool, pub req_align: u32, @@ -74,7 +76,13 @@ pub fn create_block_backend( aio: Aio, prop: BlockProperty, ) -> Result>>> { - // NOTE: we only support file backend for raw format now. - let raw_file = RawDriver::new(file, aio, prop); - Ok(Arc::new(Mutex::new(raw_file))) + match prop.format { + DiskFormat::Raw => { + let raw_file = RawDriver::new(file, aio, prop); + Ok(Arc::new(Mutex::new(raw_file))) + } + DiskFormat::Qcow2 => { + bail!("Disk format qcow2 not supported"); + } + } } diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index f96a66ab0..27da0fa68 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -163,6 +163,7 @@ impl ScsiDevice { let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type)?; let conf = BlockProperty { + format: self.config.format, iothread, direct: self.config.direct, req_align: self.req_align, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 178467043..fb2f80477 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -30,6 +30,7 @@ pub mod error; pub use error::MicroVmError; +use machine_manager::config::DiskFormat; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::UpdateRegionArgument; use util::aio::{AioEngine, WriteZeroesState}; @@ -1177,6 +1178,7 @@ impl DeviceInterface for LightMachine { queue_size: DEFAULT_VIRTQUEUE_SIZE, discard: false, write_zeroes: WriteZeroesState::Off, + format: DiskFormat::Raw, }; if let Err(e) = config.check() { error!("{:?}", e); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 2c7c1d949..e80ae95f1 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -61,8 +61,8 @@ use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, - DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, - VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, + DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, + ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -823,6 +823,7 @@ impl StdMachine { queue_size, discard: conf.discard, write_zeroes: conf.write_zeroes, + format: conf.format, }; dev.check()?; dev @@ -1383,6 +1384,7 @@ impl DeviceInterface for StdMachine { media: "disk".to_string(), discard: false, write_zeroes: WriteZeroesState::Off, + format: DiskFormat::Raw, }; if args.cache.is_some() && !args.cache.unwrap().direct.unwrap_or(true) { config.direct = false; @@ -1416,6 +1418,18 @@ impl DeviceInterface for StdMachine { } config.write_zeroes = state.unwrap(); } + if let Some(format) = args.driver { + match format.as_str().parse::() { + Ok(fmt) => config.format = fmt, + Err(e) => { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + } + } if let Err(e) = config.check() { error!("{:?}", e); return Response::create_error_response( diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index e25803ca8..e15e9b9e9 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -13,6 +13,7 @@ use std::fs::{metadata, File}; use std::os::linux::fs::MetadataExt; use std::path::Path; +use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; use log::error; @@ -70,6 +71,7 @@ pub struct BlkDevConfig { pub queue_size: u16, pub discard: bool, pub write_zeroes: WriteZeroesState, + pub format: DiskFormat, } #[derive(Debug, Clone)] @@ -97,6 +99,25 @@ impl Default for BlkDevConfig { queue_size: DEFAULT_VIRTQUEUE_SIZE, discard: false, write_zeroes: WriteZeroesState::Off, + format: DiskFormat::Raw, + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +pub enum DiskFormat { + Raw, + Qcow2, +} + +impl FromStr for DiskFormat { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + "raw" => Ok(DiskFormat::Raw), + "qcow2" => Ok(DiskFormat::Qcow2), + _ => Err(anyhow!("Unknown format type")), } } } @@ -115,6 +136,7 @@ pub struct DriveConfig { pub media: String, pub discard: bool, pub write_zeroes: WriteZeroesState, + pub format: DiskFormat, } impl Default for DriveConfig { @@ -129,6 +151,7 @@ impl Default for DriveConfig { media: "disk".to_string(), discard: false, write_zeroes: WriteZeroesState::Off, + format: DiskFormat::Raw, } } } @@ -278,11 +301,8 @@ impl ConfigCheck for BlkDevConfig { fn parse_drive(cmd_parser: CmdParser) -> Result { let mut drive = DriveConfig::default(); - - if let Some(format) = cmd_parser.get_value::("format")? { - if format.ne("raw") { - bail!("Only \'raw\' type of block is supported"); - } + if let Some(fmt) = cmd_parser.get_value::("format")? { + drive.format = fmt; } drive.id = cmd_parser @@ -376,17 +396,18 @@ pub fn parse_blk( blkdevcfg.queue_size = queue_size; } - if let Some(drive_arg) = &vm_config.drives.remove(&blkdrive) { - blkdevcfg.path_on_host = drive_arg.path_on_host.clone(); - blkdevcfg.read_only = drive_arg.read_only; - blkdevcfg.direct = drive_arg.direct; - blkdevcfg.iops = drive_arg.iops; - blkdevcfg.aio = drive_arg.aio; - blkdevcfg.discard = drive_arg.discard; - blkdevcfg.write_zeroes = drive_arg.write_zeroes; - } else { - bail!("No drive configured matched for blk device"); - } + let drive_arg = &vm_config + .drives + .remove(&blkdrive) + .with_context(|| "No drive configured matched for blk device")?; + blkdevcfg.path_on_host = drive_arg.path_on_host.clone(); + blkdevcfg.read_only = drive_arg.read_only; + blkdevcfg.direct = drive_arg.direct; + blkdevcfg.iops = drive_arg.iops; + blkdevcfg.aio = drive_arg.aio; + blkdevcfg.discard = drive_arg.discard; + blkdevcfg.write_zeroes = drive_arg.write_zeroes; + blkdevcfg.format = drive_arg.format; blkdevcfg.check()?; Ok(blkdevcfg) } @@ -514,7 +535,8 @@ impl VmConfig { .push("aio") .push("media") .push("discard") - .push("detect-zeroes"); + .push("detect-zeroes") + .push("format"); cmd_parser.parse(block_config)?; let drive_cfg = parse_drive(cmd_parser)?; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index 59e6768d0..f3116c507 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -12,7 +12,7 @@ use anyhow::{anyhow, bail, Context, Result}; -use super::{error::ConfigError, pci_args_check}; +use super::{error::ConfigError, pci_args_check, DiskFormat}; use crate::config::{ check_arg_too_long, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; @@ -162,6 +162,7 @@ pub struct ScsiDevConfig { pub channel: u8, pub target: u8, pub lun: u16, + pub format: DiskFormat, } impl Default for ScsiDevConfig { @@ -178,6 +179,7 @@ impl Default for ScsiDevConfig { channel: 0, target: 0, lun: 0, + format: DiskFormat::Raw, } } } @@ -257,12 +259,15 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result scsi_dev_cfg.lun = lun; } - if let Some(drive_arg) = &vm_config.drives.remove(&scsi_drive) { - scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); - scsi_dev_cfg.read_only = drive_arg.read_only; - scsi_dev_cfg.direct = drive_arg.direct; - scsi_dev_cfg.aio_type = drive_arg.aio; - } + let drive_arg = &vm_config + .drives + .remove(&scsi_drive) + .with_context(|| "No drive configured matched for scsi device")?; + scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); + scsi_dev_cfg.read_only = drive_arg.read_only; + scsi_dev_cfg.direct = drive_arg.direct; + scsi_dev_cfg.aio_type = drive_arg.aio; + scsi_dev_cfg.format = drive_arg.format; Ok(scsi_dev_cfg) } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index c9fc31aec..eef400693 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1051,6 +1051,7 @@ impl VirtioDevice for Block { let aio = Aio::new(Arc::new(BlockIoHandler::complete_func), self.blk_cfg.aio)?; let conf = BlockProperty { + format: self.blk_cfg.format, iothread: self.blk_cfg.iothread.clone(), direct: self.blk_cfg.direct, req_align: self.req_align, -- Gitee From 51bf9f86a4490173aaf4b0a205398d91a7c3c051 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 21 Jun 2023 17:20:35 +0800 Subject: [PATCH 1177/2187] qcow2: add basic qcow2 driver Add basic qcow2 driver and sync aio which used by other modules. Signed-off-by: zhouli57 Signed-off-by: Yan Wang Signed-off-by: liuxiangdong Signed-off-by: Xiao Ye --- Cargo.lock | 1 + block_backend/Cargo.toml | 1 + block_backend/src/lib.rs | 11 +- block_backend/src/qcow2/mod.rs | 211 +++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 block_backend/src/qcow2/mod.rs diff --git a/Cargo.lock b/Cargo.lock index acaa77342..161650026 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,6 +191,7 @@ version = "2.2.0" dependencies = [ "anyhow", "byteorder", + "libc", "log", "machine_manager", "thiserror", diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index 5e89086cb..b5ed3db6a 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -11,5 +11,6 @@ vmm-sys-util = "0.11.0" anyhow = "1.0" log = "0.4" byteorder = "1.4.3" +libc = "0.2" machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index f20ddb7fb..cd3423804 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -10,18 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod raw; - mod file; +mod qcow2; +mod raw; use std::{ fs::File, sync::{atomic::AtomicBool, Arc, Mutex}, }; -use anyhow::{bail, Result}; +use anyhow::{Context, Result}; use machine_manager::config::DiskFormat; +use qcow2::Qcow2Driver; use raw::RawDriver; use util::aio::{Aio, Iovec, WriteZeroesState}; @@ -82,7 +83,9 @@ pub fn create_block_backend( Ok(Arc::new(Mutex::new(raw_file))) } DiskFormat::Qcow2 => { - bail!("Disk format qcow2 not supported"); + let qcow2 = Qcow2Driver::new(file, aio, prop) + .with_context(|| "Failed to create qcow2 driver")?; + Ok(Arc::new(Mutex::new(qcow2))) } } } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs new file mode 100644 index 000000000..8d17ff141 --- /dev/null +++ b/block_backend/src/qcow2/mod.rs @@ -0,0 +1,211 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + cell::RefCell, + fs::File, + mem::size_of, + os::unix::io::{AsRawFd, RawFd}, + rc::Rc, + sync::{atomic::AtomicBool, Arc}, +}; + +use anyhow::{Context, Result}; +use byteorder::{BigEndian, ByteOrder}; + +use super::BlockDriverOps; +use crate::{file::FileDriver, BlockIoErrorCallback, BlockProperty}; +use util::{ + aio::{Aio, AioCb, AioEngine, Iovec, OpCode}, + num_ops::{round_down, round_up}, +}; + +// The L1/L2/Refcount table entry size. +const ENTRY_SIZE: u64 = 1 << ENTRY_BITS; +const ENTRY_BITS: u64 = 3; +const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; +const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; +const QCOW2_OFFSET_COPIED: u64 = 1 << 63; +const DEFAULT_SECTOR_SIZE: u64 = 512; + +pub enum HostOffset { + DataNotInit, + DataAddress(u64), +} + +pub struct SyncAioInfo { + /// Aio for sync read/write metadata. + aio: Aio<()>, + fd: RawFd, + prop: BlockProperty, +} + +impl SyncAioInfo { + fn new(fd: RawFd, prop: BlockProperty) -> Result { + fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { + Ok(()) + } + Ok(Self { + aio: Aio::new(Arc::new(stub_func), AioEngine::Off)?, + fd, + prop, + }) + } + + fn package_sync_aiocb( + &self, + opcode: OpCode, + iovec: Vec, + offset: usize, + nbytes: u64, + ) -> AioCb<()> { + AioCb { + direct: self.prop.direct, + req_align: self.prop.req_align, + buf_align: self.prop.buf_align, + file_fd: self.fd, + opcode, + iovec, + offset, + nbytes, + user_data: 0, + iocompletecb: (), + discard: self.prop.discard, + write_zeroes: self.prop.write_zeroes, + } + } + + fn read_buffer(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> { + let ptr = buf.as_mut_ptr() as u64; + let cnt = buf.len() as u64; + let aiocb = self.package_sync_aiocb( + OpCode::Preadv, + vec![Iovec::new(ptr, cnt)], + offset as usize, + cnt, + ); + self.aio.submit_request(aiocb) + } + + fn write_buffer(&mut self, offset: u64, buf: &[u8]) -> Result<()> { + let ptr = buf.as_ptr() as u64; + let cnt = buf.len() as u64; + let aiocb = self.package_sync_aiocb( + OpCode::Pwritev, + vec![Iovec::new(ptr, cnt)], + offset as usize, + cnt, + ); + self.aio.submit_request(aiocb) + } + + fn write_ctrl_cluster(&mut self, addr: u64, buf: &[u64]) -> Result<()> { + let output: Vec = buf.iter().flat_map(|val| val.to_be_bytes()).collect(); + self.write_buffer(addr, &output) + } + + fn read_ctrl_cluster(&mut self, addr: u64, sz: u64) -> Result> { + let mut buf = vec![0; sz as usize]; + let vec_len = size_of::() * sz as usize; + let mut vec = vec![0_u8; vec_len]; + self.read_buffer(addr, vec.as_mut_slice())?; + for i in 0..buf.len() { + buf[i] = BigEndian::read_u64(&vec[(size_of::() * i)..]); + } + Ok(buf) + } + + fn write_dirty_info(&mut self, addr: u64, buf: &[u8], start: u64, end: u64) -> Result<()> { + let start = round_down(start, DEFAULT_SECTOR_SIZE) + .with_context(|| format!("Round down failed, value is {}", start))?; + let end = round_up(end, DEFAULT_SECTOR_SIZE) + .with_context(|| format!("Round up failed, value is {}", end))?; + self.write_buffer(addr + start, &buf[start as usize..end as usize]) + } +} + +pub struct Qcow2Driver { + driver: FileDriver, + sync_aio: Rc>, +} + +impl Qcow2Driver { + pub fn new(file: File, aio: Aio, conf: BlockProperty) -> Result { + let fd = file.as_raw_fd(); + let qcow2 = Self { + driver: FileDriver::new(file, aio, conf.clone()), + sync_aio: Rc::new(RefCell::new(SyncAioInfo::new(fd, conf)?)), + }; + Ok(qcow2) + } +} + +// SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. +// We use Arc>> to allow used in multi-threading. +unsafe impl Send for Qcow2Driver {} +unsafe impl Sync for Qcow2Driver {} + +impl BlockDriverOps for Qcow2Driver { + fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + todo!() + } + + fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { + todo!() + } + + fn datasync(&mut self, args: T) -> Result<()> { + todo!() + } + + fn disk_size(&mut self) -> Result { + todo!() + } + + fn discard(&mut self, _offset: usize, _nbytes: u64, _completecb: T) -> Result<()> { + todo!() + } + + fn write_zeroes( + &mut self, + _offset: usize, + _nbytes: u64, + _completecb: T, + _unmap: bool, + ) -> Result<()> { + todo!() + } + + fn flush_request(&mut self) -> Result<()> { + todo!() + } + + fn drain_request(&self) { + todo!() + } + + fn register_io_event( + &mut self, + broken: Arc, + error_cb: BlockIoErrorCallback, + ) -> Result<()> { + todo!() + } + + fn unregister_io_event(&mut self) -> Result<()> { + todo!() + } +} + +pub fn is_aligned(cluster_sz: u64, offset: u64) -> bool { + offset & (cluster_sz - 1) == 0 +} -- Gitee From 915d87c620473c22ff96a6038265830eb6a96755 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 21 Jun 2023 16:21:19 +0800 Subject: [PATCH 1178/2187] qcow2: add qcow2 header Add qcow2 header structure definition, and buffer conversion function. Signed-off-by: zhouli57 Signed-off-by: Yan Wang --- block_backend/src/qcow2/header.rs | 435 ++++++++++++++++++++++++++++++ block_backend/src/qcow2/mod.rs | 6 + util/src/num_ops.rs | 41 +++ 3 files changed, 482 insertions(+) create mode 100644 block_backend/src/qcow2/header.rs diff --git a/block_backend/src/qcow2/header.rs b/block_backend/src/qcow2/header.rs new file mode 100644 index 000000000..8fbf9a9bb --- /dev/null +++ b/block_backend/src/qcow2/header.rs @@ -0,0 +1,435 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Context, Result}; +use byteorder::{BigEndian, ByteOrder}; +use log::warn; + +use super::ENTRY_SIZE; +use util::num_ops::div_round_up; + +pub const QCOW_MAGIC: u32 = 0x514649fb; +const QCOW_VERSION_2_MIN_LEN: usize = 72; +const QCOW_VERSION_3_MIN_LEN: usize = 104; +const MIN_CLUSTER_BIT: u32 = 9; +const MAX_CLUSTER_BIT: u32 = 21; +const MAX_REFTABLE_SIZE: u64 = 8 * (1 << 20); +const MAX_L1TABLE_SIZE: u64 = 32 * (1 << 20); + +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct QcowHeader { + pub magic: u32, + pub version: u32, + pub backing_file_offset: u64, + pub backing_file_size: u32, + pub cluster_bits: u32, + pub size: u64, + pub crypt_method: u32, + pub l1_size: u32, + pub l1_table_offset: u64, + pub refcount_table_offset: u64, + pub refcount_table_clusters: u32, + pub nb_snapshots: u32, + pub snapshots_offset: u64, + // version >= v3 + pub incompatible_features: u64, + pub compatible_features: u64, + pub autoclear_features: u64, + pub refcount_order: u32, + pub header_length: u32, +} + +impl QcowHeader { + pub fn from_vec(buf: &[u8]) -> Result { + if buf.len() < QCOW_VERSION_2_MIN_LEN { + bail!( + "Invalid header len {}, the min len {}", + buf.len(), + QCOW_VERSION_2_MIN_LEN + ); + } + let mut header = QcowHeader { + magic: BigEndian::read_u32(&buf[0..4]), + version: BigEndian::read_u32(&buf[4..8]), + backing_file_offset: BigEndian::read_u64(&buf[8..16]), + backing_file_size: BigEndian::read_u32(&buf[16..20]), + cluster_bits: BigEndian::read_u32(&buf[20..24]), + size: BigEndian::read_u64(&buf[24..32]), + crypt_method: BigEndian::read_u32(&buf[32..36]), + l1_size: BigEndian::read_u32(&buf[36..40]), + l1_table_offset: BigEndian::read_u64(&buf[40..48]), + refcount_table_offset: BigEndian::read_u64(&buf[48..56]), + refcount_table_clusters: BigEndian::read_u32(&buf[56..60]), + nb_snapshots: BigEndian::read_u32(&buf[60..64]), + snapshots_offset: BigEndian::read_u64(&buf[64..72]), + ..Default::default() + }; + if header.magic != QCOW_MAGIC { + bail!("Invalid format {}", header.magic); + } + if header.version == 2 { + header.refcount_order = 4; + header.header_length = QCOW_VERSION_2_MIN_LEN as u32; + } else if header.version == 3 { + if buf.len() < QCOW_VERSION_3_MIN_LEN { + bail!("Invalid header len for version 3 {}", buf.len()); + } + header.incompatible_features = BigEndian::read_u64(&buf[72..80]); + header.compatible_features = BigEndian::read_u64(&buf[80..88]); + header.autoclear_features = BigEndian::read_u64(&buf[88..96]); + header.refcount_order = BigEndian::read_u32(&buf[96..100]); + header.header_length = BigEndian::read_u32(&buf[100..104]); + } else { + bail!("Invalid version {}", header.version); + } + Ok(header) + } + + pub fn to_vec(&self) -> Vec { + let sz = if self.version == 2 { + QCOW_VERSION_2_MIN_LEN + } else { + QcowHeader::len() + }; + let mut buf = vec![0; sz]; + BigEndian::write_u32(&mut buf[0..4], self.magic); + BigEndian::write_u32(&mut buf[4..8], self.version); + BigEndian::write_u64(&mut buf[8..16], self.backing_file_offset); + BigEndian::write_u32(&mut buf[16..20], self.backing_file_size); + BigEndian::write_u32(&mut buf[20..24], self.cluster_bits); + BigEndian::write_u64(&mut buf[24..32], self.size); + BigEndian::write_u32(&mut buf[32..36], self.crypt_method); + BigEndian::write_u32(&mut buf[36..40], self.l1_size); + BigEndian::write_u64(&mut buf[40..48], self.l1_table_offset); + BigEndian::write_u64(&mut buf[48..56], self.refcount_table_offset); + BigEndian::write_u32(&mut buf[56..60], self.refcount_table_clusters); + BigEndian::write_u32(&mut buf[60..64], self.nb_snapshots); + BigEndian::write_u64(&mut buf[64..72], self.snapshots_offset); + if self.version >= 3 { + BigEndian::write_u64(&mut buf[72..80], self.incompatible_features); + BigEndian::write_u64(&mut buf[80..88], self.compatible_features); + BigEndian::write_u64(&mut buf[88..96], self.autoclear_features); + BigEndian::write_u32(&mut buf[96..100], self.refcount_order); + BigEndian::write_u32(&mut buf[100..104], self.header_length); + } + buf + } + + #[inline] + pub fn len() -> usize { + std::mem::size_of::() + } + + #[inline] + pub fn cluster_size(&self) -> u64 { + 0x1 << self.cluster_bits + } + + pub fn check(&self, file_sz: u64) -> Result<()> { + if !(MIN_CLUSTER_BIT..=MAX_CLUSTER_BIT).contains(&self.cluster_bits) { + bail!("Invalid cluster bits {}", self.cluster_bits); + } + if self.header_length as u64 > self.cluster_size() { + bail!( + "Header length {} over cluster size {}", + self.header_length, + self.cluster_size() + ); + } + // NOTE: not support backing file now. + if self.backing_file_offset != 0 { + bail!( + "Don't support backing file offset, {}", + self.backing_file_offset + ); + } + // NOTE: only support refcount_order == 4. + if self.refcount_order != 4 { + bail!( + "Invalid refcount order {}, only support 4 now", + self.refcount_order + ); + } + if self.size > file_sz { + warn!( + "Header size {} is large than the file size {}", + self.size, file_sz + ); + } + self.check_refcount_table()?; + self.check_l1_table()?; + Ok(()) + } + + fn check_refcount_table(&self) -> Result<()> { + if self.refcount_table_clusters == 0 { + bail!("Refcount table clusters is zero"); + } + if self.refcount_table_clusters as u64 > MAX_REFTABLE_SIZE / self.cluster_size() { + bail!( + "Refcount table size over limit {}", + self.refcount_table_clusters + ); + } + if !self.cluster_aligned(self.refcount_table_offset) { + bail!( + "Refcount table offset not aligned {}", + self.refcount_table_offset + ); + } + self.refcount_table_offset + .checked_add(self.refcount_table_clusters as u64 * self.cluster_size()) + .with_context(|| { + format!( + "Invalid offset {} or refcount table clusters {}", + self.refcount_table_offset, self.refcount_table_clusters + ) + })?; + Ok(()) + } + + fn check_l1_table(&self) -> Result<()> { + if self.l1_size as u64 > MAX_L1TABLE_SIZE / ENTRY_SIZE { + bail!("L1 table size over limit {}", self.l1_size); + } + if !self.cluster_aligned(self.l1_table_offset) { + bail!("L1 table offset not aligned {}", self.l1_table_offset); + } + let size_per_l1_entry = self.cluster_size() * self.cluster_size() / ENTRY_SIZE; + let l1_need_sz = + div_round_up(self.size, size_per_l1_entry).with_context(|| "Failed to get l1 size")?; + if (self.l1_size as u64) < l1_need_sz { + bail!( + "L1 table is too small, l1 size {} expect {}", + self.l1_size, + l1_need_sz + ); + } + self.l1_table_offset + .checked_add(self.l1_size as u64 * ENTRY_SIZE) + .with_context(|| { + format!( + "Invalid offset {} or entry size {}", + self.l1_table_offset, self.l1_size + ) + })?; + Ok(()) + } + + #[inline] + fn cluster_aligned(&self, offset: u64) -> bool { + offset & (self.cluster_size() - 1) == 0 + } +} + +#[cfg(test)] +mod test { + use crate::qcow2::header::*; + + const DEFUALT_CLUSTER_SIZE: u64 = 64 * 1024; + + fn valid_header_v3() -> Vec { + // 10G + vec![ + 0x51, 0x46, 0x49, 0xfb, // magic + 0x00, 0x00, 0x00, 0x03, // version + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset + 0x00, 0x00, 0x00, 0x00, // backing file size + 0x00, 0x00, 0x00, 0x10, // cluster bits + 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, // size + 0x00, 0x00, 0x00, 0x00, // crypt method + 0x00, 0x00, 0x00, 0x14, // l1 size + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // l1 table offset + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset + 0x00, 0x00, 0x00, 0x01, // refcount table clusters + 0x00, 0x00, 0x00, 0x00, // nb snapshots + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // snapshots offset + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // incompatible features + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compatible features + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // autoclear features + 0x00, 0x00, 0x00, 0x04, // refcount order + 0x00, 0x00, 0x00, 0x68, // header length + ] + } + + fn extended_header_v3() -> Vec { + // 10G + let mut buf = valid_header_v3(); + buf.append(&mut vec![0_u8; 8]); + BigEndian::write_u32(&mut buf[100..104], 112); + buf + } + + fn valid_header_v2() -> Vec { + // 5G + vec![ + 0x51, 0x46, 0x49, 0xfb, // magic + 0x00, 0x00, 0x00, 0x02, // version + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // backing file offset + 0x00, 0x00, 0x00, 0x00, // backing file size + 0x00, 0x00, 0x00, 0x10, // cluster bits + 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, // size + 0x00, 0x00, 0x00, 0x00, // crypt method + 0x00, 0x00, 0x00, 0x0a, // l1 size + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // l1 table offset + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // refcount table offset + 0x00, 0x00, 0x00, 0x01, // refcount table clusters + 0x00, 0x00, 0x00, 0x00, // nb snapshots + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // snapshots offset + ] + } + + #[test] + fn test_header_align() { + // 8 bytes alignments + let sz = std::mem::size_of::(); + assert_eq!(sz % 8, 0); + } + + #[test] + fn test_valid_header() { + let buf = valid_header_v2(); + let header = QcowHeader::from_vec(&buf).unwrap(); + assert_eq!(header.magic, QCOW_MAGIC); + assert_eq!(header.version, 2); + assert_eq!(header.cluster_size(), DEFUALT_CLUSTER_SIZE); + assert_eq!(header.header_length, QCOW_VERSION_2_MIN_LEN as u32); + assert_eq!(buf, header.to_vec()); + + let buf = valid_header_v3(); + let header = QcowHeader::from_vec(&buf).unwrap(); + assert_eq!(header.magic, QCOW_MAGIC); + assert_eq!(header.version, 3); + assert_eq!(header.cluster_size(), DEFUALT_CLUSTER_SIZE); + assert_eq!(header.header_length, QCOW_VERSION_3_MIN_LEN as u32); + assert_eq!(buf, header.to_vec()); + + let buf = extended_header_v3(); + let header = QcowHeader::from_vec(&buf).unwrap(); + assert_eq!(header.magic, QCOW_MAGIC); + assert_eq!(header.version, 3); + assert_eq!(header.cluster_size(), DEFUALT_CLUSTER_SIZE); + assert_eq!(header.header_length, 112); + // NOTE: only care the length we supported. + assert_eq!(buf[0..QcowHeader::len()], header.to_vec()); + } + + fn invalid_header_list() -> Vec<(Vec, String)> { + let mut list = Vec::new(); + // Invalid buffer length. + list.push((vec![0_u8; 16], format!("Invalid header len"))); + // Invalid buffer length for v3. + let buf = valid_header_v3(); + list.push(( + buf[0..90].to_vec(), + format!("Invalid header len for version 3"), + )); + // Invalid magic. + let mut buf = valid_header_v2(); + BigEndian::write_u32(&mut buf[0..4], 1234); + list.push((buf, format!("Invalid format"))); + // Invalid version. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[4..8], 1); + list.push((buf, format!("Invalid version"))); + // Large header length. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[100..104], 0x10000000_u32); + list.push(( + buf, + format!("Header length {} over cluster size", 0x10000000_u32), + )); + // Small cluster bit. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[20..24], 0); + list.push((buf, format!("Invalid cluster bit"))); + // Large cluster bit. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[20..24], 65); + list.push((buf, format!("Invalid cluster bit"))); + // Invalid backing file offset. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[8..16], 0x2000); + list.push((buf, format!("Don't support backing file offset"))); + // Invalid refcount order. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[96..100], 5); + list.push((buf, format!("Invalid refcount order"))); + // Refcount table offset is not aligned. + let mut buf = valid_header_v3(); + BigEndian::write_u64(&mut buf[48..56], 0x1234); + list.push((buf, format!("Refcount table offset not aligned"))); + // Refcount table offset is large. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[36..40], 4 * 1024 * 1024); + BigEndian::write_u64(&mut buf[48..56], 0xffff_ffff_ffff_0000_u64); + BigEndian::write_u32(&mut buf[56..60], 128); + list.push(( + buf, + format!( + "Invalid offset {} or refcount table clusters {}", + 0xffff_ffff_ffff_0000_u64, 128 + ), + )); + // Invalid refcount table cluster. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[56..60], 256); + list.push((buf, format!("Refcount table size over limit"))); + // Refcount table cluster is 0. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[56..60], 0); + list.push((buf, format!("Refcount table clusters is zero"))); + // L1 table offset is not aligned. + let mut buf = valid_header_v3(); + BigEndian::write_u64(&mut buf[40..48], 0x123456); + list.push((buf, format!("L1 table offset not aligned"))); + // L1 table offset is large. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[36..40], 4 * 1024 * 1024); + BigEndian::write_u64(&mut buf[40..48], 0xffff_ffff_ffff_0000_u64); + list.push(( + buf, + format!( + "Invalid offset {} or entry size {}", + 0xffff_ffff_ffff_0000_u64, + 4 * 1024 * 1024 + ), + )); + // Invalid l1 table size. + let mut buf = valid_header_v3(); + BigEndian::write_u32(&mut buf[36..40], 0xffff_0000_u32); + list.push((buf, format!("L1 table size over limit"))); + // File size is large than l1 table size. + let mut buf = valid_header_v3(); + BigEndian::write_u64(&mut buf[24..32], 0xffff_ffff_ffff_0000_u64); + BigEndian::write_u32(&mut buf[36..40], 10); + list.push((buf, format!("L1 table is too small"))); + list + } + + #[test] + fn test_invalid_header() { + let list = invalid_header_list(); + for (buf, err) in list { + match QcowHeader::from_vec(&buf) { + Ok(header) => { + let e = header.check(0).err().unwrap(); + assert!(e.to_string().contains(&err)); + } + Err(e) => { + assert!(e.to_string().contains(&err)); + } + } + } + } +} diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 8d17ff141..3da94136e 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod header; + use std::{ cell::RefCell, fs::File, @@ -22,6 +24,8 @@ use std::{ use anyhow::{Context, Result}; use byteorder::{BigEndian, ByteOrder}; +use self::header::QcowHeader; + use super::BlockDriverOps; use crate::{file::FileDriver, BlockIoErrorCallback, BlockProperty}; use util::{ @@ -136,6 +140,7 @@ impl SyncAioInfo { pub struct Qcow2Driver { driver: FileDriver, sync_aio: Rc>, + header: QcowHeader, } impl Qcow2Driver { @@ -144,6 +149,7 @@ impl Qcow2Driver { let qcow2 = Self { driver: FileDriver::new(file, aio, conf.clone()), sync_aio: Rc::new(RefCell::new(SyncAioInfo::new(fd, conf)?)), + header: QcowHeader::default(), }; Ok(qcow2) } diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 0cbcb038f..3a42533cc 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -62,6 +62,33 @@ pub fn round_down(origin: u64, align: u64) -> Option { } } +/// Division rounded up. +/// +/// # Arguments +/// +/// * `dividend` - dividend. +/// * `divisor` - divisor. +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::div_round_up; +/// +/// let value = div_round_up(10 as u64, 4 as u64); +/// assert!(value == Some(3)); +/// ``` +pub fn div_round_up(dividend: u64, divisor: u64) -> Option { + if let Some(res) = dividend.checked_div(divisor) { + if dividend % divisor == 0 { + return Some(res); + } else { + return Some(res + 1); + } + } + None +} + /// Get the first half or second half of u64. /// /// # Arguments @@ -431,6 +458,20 @@ mod test { assert_eq!(result, Some(10000)); } + #[test] + fn test_div_round_up() { + let res = div_round_up(10, 4); + assert_eq!(res, Some(3)); + let res = div_round_up(10, 2); + assert_eq!(res, Some(5)); + let res = div_round_up(2, 10); + assert_eq!(res, Some(1)); + let res = div_round_up(10, 0); + assert_eq!(res, None); + let res = div_round_up(0xffff_ffff_ffff_ffff_u64, 1); + assert_eq!(res, Some(0xffff_ffff_ffff_ffff_u64)); + } + #[test] fn test_read_u32_from_u64() { let value = 0x1234_5678_9012_3456u64; -- Gitee From c8256851cfdf563fbcfda4ffec81ab4c06bb6b04 Mon Sep 17 00:00:00 2001 From: Xiao Ye Date: Wed, 21 Jun 2023 16:39:05 +0800 Subject: [PATCH 1179/2187] qcow2: add cache Add cache for l2 table and refcount block. Signed-off-by: Xiao Ye Signed-off-by: Yan Wang --- block_backend/src/qcow2/cache.rs | 271 +++++++++++++++++++++++++++++++ block_backend/src/qcow2/mod.rs | 1 + 2 files changed, 272 insertions(+) create mode 100644 block_backend/src/qcow2/cache.rs diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs new file mode 100644 index 000000000..34b23ab00 --- /dev/null +++ b/block_backend/src/qcow2/cache.rs @@ -0,0 +1,271 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + cell::RefCell, + collections::{hash_map::Iter, HashMap}, + rc::Rc, +}; + +use anyhow::{bail, Result}; +use byteorder::{BigEndian, ByteOrder}; +use log::warn; + +const CACHE_DEFAULT_SIZE: usize = 1; +pub const ENTRY_SIZE_U16: usize = 2; +pub const ENTRY_SIZE_U64: usize = 8; + +#[derive(Clone)] +pub struct DirtyInfo { + /// If the entry is marked dirty, it needs to be rewritten back to the disk. + pub is_dirty: bool, + /// The start of the dirty area. + pub start: u64, + /// The end of the dirty area. + pub end: u64, +} + +impl Default for DirtyInfo { + fn default() -> Self { + Self { + is_dirty: false, + start: u64::MAX, + end: 0, + } + } +} + +impl DirtyInfo { + pub fn clear(&mut self) { + self.is_dirty = false; + self.start = u64::MAX; + self.end = 0; + } +} + +#[derive(Clone, Default)] +pub struct CacheTable { + /// If the table is marked dirty, it needs to be rewritten back to the disk. + pub dirty_info: DirtyInfo, + /// Lru hit count. + pub lru_count: u64, + /// Host offset of cached table. + pub addr: u64, + /// The size of an entry in bytes. + entry_size: usize, + /// Buffer of table data. + table_data: Vec, +} + +impl CacheTable { + pub fn new(addr: u64, table_data: Vec, entry_size: usize) -> Result { + if entry_size == 0 { + bail!("Invalid entry size"); + } + Ok(Self { + dirty_info: Default::default(), + lru_count: 0, + addr, + entry_size, + table_data, + }) + } + + fn be_read(&self, idx: usize) -> Result { + let start = idx * self.entry_size; + let end = start + self.entry_size; + if end > self.table_data.len() { + bail!("Invalid idx {}", idx); + } + let v = match self.entry_size { + ENTRY_SIZE_U16 => BigEndian::read_u16(&self.table_data[start..end]) as u64, + ENTRY_SIZE_U64 => BigEndian::read_u64(&self.table_data[start..end]), + _ => bail!("Unsupported entry size {}", self.entry_size), + }; + Ok(v) + } + + #[inline(always)] + pub fn get_entry_map(&mut self, idx: usize) -> Result { + self.be_read(idx) + } + + #[inline(always)] + pub fn set_entry_map(&mut self, idx: usize, value: u64) -> Result<()> { + let start = idx * self.entry_size; + let end = start + self.entry_size; + if end > self.table_data.len() { + bail!("Invalid idx {}", idx); + } + match self.entry_size { + ENTRY_SIZE_U16 => BigEndian::write_u16(&mut self.table_data[start..end], value as u16), + ENTRY_SIZE_U64 => BigEndian::write_u64(&mut self.table_data[start..end], value), + _ => bail!("Unsupported entry size {}", self.entry_size), + } + + let mut dirty_info = &mut self.dirty_info; + dirty_info.start = std::cmp::min(dirty_info.start, start as u64); + dirty_info.end = std::cmp::max(dirty_info.end, end as u64); + dirty_info.is_dirty = true; + Ok(()) + } + + pub fn find_empty_entry(&self, start: usize) -> Result { + let len = self.table_data.len() / self.entry_size; + for i in start..len { + if self.be_read(i)? == 0 { + return Ok(i); + } + } + Ok(len) + } + + pub fn get_value(&self) -> &[u8] { + &self.table_data + } +} + +#[derive(Clone, Default)] +pub struct Qcow2Cache { + /// Max size of the cache map. + pub max_size: usize, + /// LRU count which record the latest count and increased when cache is accessed. + pub lru_count: u64, + pub cache_map: HashMap>>, +} + +impl Qcow2Cache { + pub fn new(mut max_size: usize) -> Self { + if max_size == 0 { + max_size = CACHE_DEFAULT_SIZE; + warn!( + "The cache max size is 0, use the default value {}", + CACHE_DEFAULT_SIZE + ); + } + Self { + max_size, + lru_count: 0, + cache_map: HashMap::with_capacity(max_size), + } + } + + fn check_refcount(&mut self) { + if self.lru_count < u64::MAX { + return; + } + warn!("refcount reaches the max limit and is reset to 0"); + for (_, entry) in self.cache_map.iter() { + entry.borrow_mut().lru_count = 0; + } + } + + pub fn contains_keys(&self, key: u64) -> bool { + self.cache_map.contains_key(&key) + } + + pub fn get(&mut self, key: u64) -> Option<&Rc>> { + self.check_refcount(); + let entry = self.cache_map.get(&key)?; + // LRU replace algorithm. + entry.borrow_mut().lru_count = self.lru_count; + self.lru_count += 1; + Some(entry) + } + + pub fn iter(&self) -> Iter>> { + self.cache_map.iter() + } + + pub fn lru_replace( + &mut self, + key: u64, + entry: Rc>, + ) -> Option>> { + let mut replaced_entry: Option>> = None; + let mut lru_count = u64::MAX; + let mut target_idx = 0; + self.check_refcount(); + entry.borrow_mut().lru_count = self.lru_count; + self.lru_count += 1; + + if self.cache_map.len() < self.max_size { + self.cache_map.insert(key, entry); + return replaced_entry; + } + + for (key, entry) in self.cache_map.iter() { + let borrowed_entry = entry.borrow(); + if borrowed_entry.lru_count < lru_count { + lru_count = borrowed_entry.lru_count; + replaced_entry = Some(entry.clone()); + target_idx = *key; + } + } + self.cache_map.remove(&target_idx); + self.cache_map.insert(key, entry); + replaced_entry + } +} + +#[cfg(test)] +mod test { + use super::{CacheTable, Qcow2Cache}; + use std::{cell::RefCell, rc::Rc}; + + #[test] + fn test_cache_entry() { + let buf: Vec = vec![0x00, 0x01, 0x02, 0x03, 0x04]; + let mut vec = Vec::new(); + for i in 0..buf.len() { + vec.append(&mut buf[i].to_be_bytes().to_vec()); + } + let mut entry = CacheTable::new(0x00 as u64, vec, 8).unwrap(); + assert_eq!(entry.get_entry_map(0).unwrap(), 0x00); + assert_eq!(entry.get_entry_map(3).unwrap(), 0x03); + assert_eq!(entry.get_entry_map(4).unwrap(), 0x04); + + entry.set_entry_map(0x02, 0x09).unwrap(); + assert_eq!(entry.get_entry_map(2).unwrap(), 0x09); + } + + #[test] + fn test_qcow2_cache() { + let buf: Vec = vec![0x00, 0x01, 0x02, 0x03, 0x04]; + let mut vec = Vec::new(); + for i in 0..buf.len() { + vec.append(&mut buf[i].to_be_bytes().to_vec()); + } + let entry_0 = Rc::new(RefCell::new( + CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + )); + entry_0.borrow_mut().lru_count = 0; + let entry_1 = Rc::new(RefCell::new( + CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + )); + entry_1.borrow_mut().lru_count = 1; + let entry_2 = Rc::new(RefCell::new( + CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + )); + entry_2.borrow_mut().lru_count = 2; + let entry_3 = Rc::new(RefCell::new( + CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + )); + entry_3.borrow_mut().lru_count = 3; + + let mut qcow2_cache: Qcow2Cache = Qcow2Cache::new(3); + assert!(qcow2_cache.lru_replace(0x00, entry_0).is_none()); + assert!(qcow2_cache.lru_replace(0x01, entry_1).is_none()); + assert!(qcow2_cache.lru_replace(0x02, entry_2).is_none()); + assert!(qcow2_cache.lru_replace(0x03, entry_3).is_some()); + } +} diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 3da94136e..8809113fd 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod cache; mod header; use std::{ -- Gitee From 697c4c9aafe152ca12950654460b2491c12bd9ed Mon Sep 17 00:00:00 2001 From: Xiao Ye Date: Wed, 28 Jun 2023 14:08:04 +0800 Subject: [PATCH 1180/2187] qcow2: add l1 and l2 table Add qcow2 table to manage the data cluster address. Signed-off-by: Xiao Ye Signed-off-by: zhouli57 --- block_backend/src/qcow2/mod.rs | 12 +- block_backend/src/qcow2/table.rs | 217 +++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 block_backend/src/qcow2/table.rs diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 8809113fd..5e562cdb7 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -12,6 +12,7 @@ mod cache; mod header; +mod table; use std::{ cell::RefCell, @@ -25,7 +26,7 @@ use std::{ use anyhow::{Context, Result}; use byteorder::{BigEndian, ByteOrder}; -use self::header::QcowHeader; +use self::{header::QcowHeader, table::Qcow2Table}; use super::BlockDriverOps; use crate::{file::FileDriver, BlockIoErrorCallback, BlockProperty}; @@ -39,6 +40,8 @@ const ENTRY_SIZE: u64 = 1 << ENTRY_BITS; const ENTRY_BITS: u64 = 3; const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; +const QCOW2_OFLAG_ZERO: u64 = 1 << 0; +const QCOW2_OFFSET_COMPRESSED: u64 = 1 << 62; const QCOW2_OFFSET_COPIED: u64 = 1 << 63; const DEFAULT_SECTOR_SIZE: u64 = 512; @@ -142,15 +145,18 @@ pub struct Qcow2Driver { driver: FileDriver, sync_aio: Rc>, header: QcowHeader, + table: Qcow2Table, } impl Qcow2Driver { pub fn new(file: File, aio: Aio, conf: BlockProperty) -> Result { let fd = file.as_raw_fd(); + let sync_aio = Rc::new(RefCell::new(SyncAioInfo::new(fd, conf.clone())?)); let qcow2 = Self { - driver: FileDriver::new(file, aio, conf.clone()), - sync_aio: Rc::new(RefCell::new(SyncAioInfo::new(fd, conf)?)), + driver: FileDriver::new(file, aio, conf), + sync_aio: sync_aio.clone(), header: QcowHeader::default(), + table: Qcow2Table::new(sync_aio), }; Ok(qcow2) } diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs new file mode 100644 index 000000000..1ff253730 --- /dev/null +++ b/block_backend/src/qcow2/table.rs @@ -0,0 +1,217 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, rc::Rc}; + +use anyhow::{Context, Result}; + +use super::ENTRY_BITS; +use crate::qcow2::{ + cache::{CacheTable, Qcow2Cache}, + header::QcowHeader, + SyncAioInfo, ENTRY_SIZE, L1_TABLE_OFFSET_MASK, L2_TABLE_OFFSET_MASK, QCOW2_OFFSET_COMPRESSED, + QCOW2_OFLAG_ZERO, +}; +use util::num_ops::div_round_up; + +// L2 Cache max size is 32M. +const MAX_L2_CACHE_SIZE: u64 = 32 * (1 << 20); + +#[derive(PartialEq, Eq, Debug)] +pub enum Qcow2ClusterType { + /// Cluster is unallocated. + Unallocated, + /// Cluster is zero and not allocated. + ZeroPlain, + /// cluster is zero and allocated. + ZeroAlloc, + /// Cluster is allocated. + Normal, + /// Cluster is compressed. + Compressed, +} + +impl Qcow2ClusterType { + pub fn is_read_zero(&self) -> bool { + if self.eq(&Qcow2ClusterType::Unallocated) + || self.eq(&Qcow2ClusterType::ZeroAlloc) + || self.eq(&Qcow2ClusterType::ZeroPlain) + { + return true; + } + false + } + + /// Get cluster type of l2 table entry. + pub fn get_cluster_type(l2_entry: u64) -> Self { + if l2_entry & QCOW2_OFFSET_COMPRESSED != 0 { + return Qcow2ClusterType::Compressed; + } + if l2_entry & QCOW2_OFLAG_ZERO != 0 { + if l2_entry & L2_TABLE_OFFSET_MASK != 0 { + return Qcow2ClusterType::ZeroAlloc; + } + return Qcow2ClusterType::ZeroPlain; + } + if l2_entry & L2_TABLE_OFFSET_MASK == 0 { + return Qcow2ClusterType::Unallocated; + } + Qcow2ClusterType::Normal + } +} + +pub struct Qcow2Table { + pub cluster_bits: u64, + pub cluster_size: u64, + pub l1_table: Vec, + pub l2_table_cache: Qcow2Cache, + sync_aio: Rc>, + l2_bits: u64, + l2_size: u64, +} + +impl Qcow2Table { + pub fn new(sync_aio: Rc>) -> Self { + Self { + sync_aio, + cluster_bits: 0, + cluster_size: 0, + l1_table: Vec::new(), + l2_table_cache: Qcow2Cache::default(), + l2_bits: 0, + l2_size: 0, + } + } + + pub fn init_table(&mut self, header: &QcowHeader) -> Result<()> { + let max_l2_entries = + div_round_up(header.size, header.cluster_size()).with_context(|| { + format!( + "Invalid size {} or cluster size {}", + header.size, + header.cluster_size() + ) + })?; + let max_l2_cache = div_round_up(max_l2_entries * ENTRY_SIZE, header.cluster_size()) + .with_context(|| { + format!( + "Invalid l2 entries {} or cluster size {}", + max_l2_entries * ENTRY_SIZE, + header.cluster_size() + ) + })?; + let cache_size = std::cmp::min(max_l2_cache, MAX_L2_CACHE_SIZE / header.cluster_size()); + let l2_table_cache: Qcow2Cache = Qcow2Cache::new(cache_size as usize); + self.cluster_bits = header.cluster_bits as u64; + self.cluster_size = header.cluster_size(); + self.l2_bits = header.cluster_bits as u64 - ENTRY_BITS; + self.l2_size = header.cluster_size() / ENTRY_SIZE; + self.l2_table_cache = l2_table_cache; + self.load_l1_table(header) + .with_context(|| "Failed to load l1 table")?; + Ok(()) + } + + fn load_l1_table(&mut self, header: &QcowHeader) -> Result<()> { + self.l1_table = self + .sync_aio + .borrow_mut() + .read_ctrl_cluster(header.l1_table_offset, header.l1_size as u64)?; + Ok(()) + } + + fn save_l1_table(&mut self, header: &QcowHeader) -> Result<()> { + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(header.l1_table_offset, &self.l1_table) + } + + pub fn get_l1_table_index(&self, guest_offset: u64) -> u64 { + guest_offset >> (self.cluster_bits + self.l2_bits) + } + + pub fn get_l2_table_index(&self, guest_offset: u64) -> u64 { + (guest_offset >> self.cluster_bits) & (self.l2_size - 1) + } + + pub fn get_l1_table_entry(&self, guest_offset: u64) -> u64 { + let l1_idx = self.get_l1_table_index(guest_offset); + self.l1_table[l1_idx as usize] + } + + pub fn get_l2_table_cache_entry( + &mut self, + guest_offset: u64, + ) -> Option<&Rc>> { + let l1_entry = self.get_l1_table_entry(guest_offset); + let l2_entry_addr = l1_entry & L1_TABLE_OFFSET_MASK; + if l2_entry_addr == 0 { + None + } else { + self.l2_table_cache.get(l2_entry_addr) + } + } + + pub fn update_l1_table( + &mut self, + l1_index: usize, + l2_address: u64, + header: &QcowHeader, + ) -> Result<()> { + self.l1_table[l1_index] = l2_address; + self.save_l1_table(header) + } + + pub fn update_l2_table(&mut self, l2_table_entry: Rc>) -> Result<()> { + let l2_entry_addr = l2_table_entry.borrow().addr; + if self.l2_table_cache.contains_keys(l2_entry_addr as u64) { + return Ok(()); + } + if let Some(replaced_entry) = self + .l2_table_cache + .lru_replace(l2_entry_addr as u64, l2_table_entry) + { + let borrowed_entry = replaced_entry.borrow(); + // Flush the dirty entry. + if borrowed_entry.dirty_info.is_dirty { + self.sync_aio.borrow_mut().write_dirty_info( + borrowed_entry.addr, + borrowed_entry.get_value(), + borrowed_entry.dirty_info.start, + borrowed_entry.dirty_info.end, + )?; + } + } + Ok(()) + } + + fn flush_l2_table_cache(&self) -> Result<()> { + for (_idx, entry) in self.l2_table_cache.iter() { + let mut borrowed_entry = entry.borrow_mut(); + if !borrowed_entry.dirty_info.is_dirty { + continue; + } + self.sync_aio.borrow_mut().write_dirty_info( + borrowed_entry.addr, + borrowed_entry.get_value(), + borrowed_entry.dirty_info.start, + borrowed_entry.dirty_info.end, + )?; + borrowed_entry.dirty_info.clear(); + } + Ok(()) + } + + pub fn flush(&mut self) -> Result<()> { + self.flush_l2_table_cache() + } +} -- Gitee From 93b11b6a87101c1c140e4649b9e9fa836d11d901 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 24 Jun 2023 12:08:19 +0800 Subject: [PATCH 1181/2187] qcow2: add refcount table and block Add refcount table and block to manage the cluster counts. Signed-off-by: Yan Wang --- block_backend/src/qcow2/mod.rs | 7 +- block_backend/src/qcow2/refcount.rs | 441 ++++++++++++++++++++++++++++ 2 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 block_backend/src/qcow2/refcount.rs diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 5e562cdb7..83da8debf 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -12,6 +12,7 @@ mod cache; mod header; +mod refcount; mod table; use std::{ @@ -26,7 +27,7 @@ use std::{ use anyhow::{Context, Result}; use byteorder::{BigEndian, ByteOrder}; -use self::{header::QcowHeader, table::Qcow2Table}; +use self::{header::QcowHeader, refcount::RefCount, table::Qcow2Table}; use super::BlockDriverOps; use crate::{file::FileDriver, BlockIoErrorCallback, BlockProperty}; @@ -146,6 +147,7 @@ pub struct Qcow2Driver { sync_aio: Rc>, header: QcowHeader, table: Qcow2Table, + refcount: RefCount, } impl Qcow2Driver { @@ -156,7 +158,8 @@ impl Qcow2Driver { driver: FileDriver::new(file, aio, conf), sync_aio: sync_aio.clone(), header: QcowHeader::default(), - table: Qcow2Table::new(sync_aio), + table: Qcow2Table::new(sync_aio.clone()), + refcount: RefCount::new(sync_aio), }; Ok(qcow2) } diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs new file mode 100644 index 000000000..61abe1021 --- /dev/null +++ b/block_backend/src/qcow2/refcount.rs @@ -0,0 +1,441 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, rc::Rc}; + +use anyhow::{bail, Context, Result}; +use log::{error, info}; + +use crate::qcow2::{ + cache::{CacheTable, Qcow2Cache, ENTRY_SIZE_U16}, + header::QcowHeader, + is_aligned, SyncAioInfo, ENTRY_SIZE, +}; + +// The max refcount table size default is 4 clusters; +const MAX_REFTABLE_NUM: u32 = 4; + +#[derive(Clone)] +pub struct RefCount { + pub refcount_table: Vec, + sync_aio: Rc>, + refcount_blk_cache: Qcow2Cache, + free_cluster_index: u64, + refcount_table_offset: u64, + refcount_table_clusters: u32, + /// Number of refcount table entries. + refcount_table_size: u64, + refcount_blk_bits: u32, + /// Number of refcount block entries. + refcount_blk_size: u32, + refcount_max: u64, + /// Cluster size in bytes. + cluster_size: u64, + cluster_bits: u32, +} + +impl RefCount { + pub fn new(sync_aio: Rc>) -> Self { + RefCount { + refcount_table: Vec::new(), + sync_aio, + refcount_blk_cache: Qcow2Cache::new(MAX_REFTABLE_NUM as usize), + free_cluster_index: 0, + refcount_table_offset: 0, + refcount_table_clusters: 0, + refcount_table_size: 0, + refcount_blk_bits: 0, + refcount_blk_size: 0, + refcount_max: 0, + cluster_size: 0, + cluster_bits: 0, + } + } + + pub fn init_refcount_info(&mut self, header: &QcowHeader) { + self.refcount_table_offset = header.refcount_table_offset; + self.refcount_table_clusters = header.refcount_table_clusters; + self.refcount_table_size = + header.refcount_table_clusters as u64 * header.cluster_size() / ENTRY_SIZE; + self.refcount_blk_bits = header.cluster_bits + 3 - header.refcount_order; + self.refcount_blk_size = 1 << self.refcount_blk_bits; + self.cluster_bits = header.cluster_bits; + self.cluster_size = header.cluster_size(); + let refcount_bits = 1 << header.refcount_order; + self.refcount_max = 1 << (refcount_bits - 1); + self.refcount_max += self.refcount_max - 1; + } + + fn bytes_to_clusters(&self, size: u64) -> u64 { + (size + self.cluster_size - 1) >> self.cluster_bits + } + + fn cluster_in_rc_block(&self, cluster_index: u64) -> u64 { + cluster_index & (self.refcount_blk_size - 1) as u64 + } + + fn extend_refcount_table(&mut self, header: &mut QcowHeader, cluster_index: u64) -> Result<()> { + info!("Qcow2 needs to extend the refcount table"); + // Alloc space for new refcount table. + let mut offset = cluster_index << self.cluster_bits; + let rcb_offset = self.cluster_in_rc_block(cluster_index); + let rc_block = vec![0_u8; self.cluster_size as usize]; + let cache_entry = Rc::new(RefCell::new(CacheTable::new( + offset, + rc_block, + ENTRY_SIZE_U16, + )?)); + let mut borrow_entry = cache_entry.borrow_mut(); + for i in rcb_offset..rcb_offset + self.refcount_table_clusters as u64 + 2 { + borrow_entry.set_entry_map(i as usize, 1)?; + } + self.sync_aio.borrow_mut().write_dirty_info( + borrow_entry.addr, + borrow_entry.get_value(), + 0, + self.cluster_size, + )?; + + // Write new extended refcount table to disk. + let size = self.refcount_table.len() + (self.cluster_size / ENTRY_SIZE) as usize; + let mut new_rc_table = self.refcount_table.clone(); + new_rc_table.resize(size, 0); + new_rc_table[(cluster_index >> self.refcount_blk_bits) as usize] = offset; + offset += self.cluster_size; + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(offset, &new_rc_table)?; + + // Update and save qcow2 header to disk. + let mut new_header = header.clone(); + new_header.refcount_table_offset = offset; + new_header.refcount_table_clusters += 1; + self.sync_aio + .borrow_mut() + .write_buffer(0, &new_header.to_vec())?; + + // Update qcow2 header in memory. + header.refcount_table_offset = new_header.refcount_table_offset; + header.refcount_table_clusters = new_header.refcount_table_clusters; + + // Update refcount information. + let old_rct_offset = self.refcount_table_offset; + let old_rct_size = self.refcount_table_size; + self.refcount_table = new_rc_table; + self.refcount_table_offset = header.refcount_table_offset; + self.refcount_table_clusters = header.refcount_table_clusters; + self.refcount_table_size = + (self.refcount_table_clusters << self.cluster_bits) as u64 / ENTRY_SIZE; + + // Free the old cluster of refcount table. + let clusters = self.bytes_to_clusters(old_rct_size as u64); + self.update_refcount(old_rct_offset, clusters, false, 1)?; + info!( + "Qcow2 extends refcount table success, offset 0x{:x} -> 0x{:x}", + old_rct_offset, self.refcount_table_offset + ); + + Ok(()) + } + + pub fn update_refcount( + &mut self, + offset: u64, + clusters: u64, + is_add: bool, + addend: u16, + ) -> Result<()> { + let first_cluster = self.bytes_to_clusters(offset); + let mut rc_vec = Vec::new(); + let mut i = 0; + while i < clusters { + let rt_idx = (first_cluster + i) >> self.refcount_blk_bits; + if rt_idx >= self.refcount_table_size as u64 { + bail!("Invalid refcount table index {}", rt_idx); + } + let rb_addr = self.refcount_table[rt_idx as usize]; + if rb_addr == 0 || self.offset_into_cluster(rb_addr) != 0 { + bail!( + "Invalid refcount block address 0x{:x}, index is {}", + rb_addr, + rt_idx + ); + } + let rb_idx = self.cluster_in_rc_block(i + first_cluster) as usize; + let num = std::cmp::min( + self.refcount_blk_size as usize - rb_idx, + (clusters - i) as usize, + ); + rc_vec.push((rt_idx, rb_idx as u64, num)); + i += num as u64; + } + + let idx = self.set_refcount_blocks(&rc_vec, is_add, addend); + if idx != rc_vec.len() { + // Revert the updating operation for refount block. + let rev_idx = self.set_refcount_blocks(&rc_vec[..idx], !is_add, addend); + let status = if rev_idx == idx { "success" } else { "failed" }; + bail!("Failed to set refcounts, recover {}", status); + } + self.flush_reftount_block_cache() + } + + fn set_refcount_blocks( + &mut self, + rc_vec: &[(u64, u64, usize)], + is_add: bool, + addend: u16, + ) -> usize { + for (i, (rt_idx, rb_idx, num)) in rc_vec.iter().enumerate() { + let ret = self.set_refcount(*rt_idx, *rb_idx, *num, is_add, addend); + if let Err(err) = ret { + error!("Set refcount failed, rt_idx {}, rb_idx {}, clusters {}, is_add {}, addend {}, {}", + rt_idx, rb_idx, num, is_add, addend, err.to_string()); + return i; + } + } + + rc_vec.len() + } + + fn flush_reftount_block_cache(&self) -> Result<()> { + for (_, entry) in self.refcount_blk_cache.iter() { + let mut borrowed_entry = entry.borrow_mut(); + if !borrowed_entry.dirty_info.is_dirty { + continue; + } + let ret = self.sync_aio.borrow_mut().write_dirty_info( + borrowed_entry.addr, + borrowed_entry.get_value(), + borrowed_entry.dirty_info.start, + borrowed_entry.dirty_info.end, + ); + if let Err(err) = ret { + error!("Flush refcount table cache failed, {}", err.to_string()); + } + borrowed_entry.dirty_info.clear(); + } + Ok(()) + } + + fn set_refcount( + &mut self, + rt_idx: u64, + rb_idx: u64, + clusters: usize, + is_add: bool, + addend: u16, + ) -> Result<()> { + if !self.refcount_blk_cache.contains_keys(rt_idx) { + self.load_refcount_block(rt_idx).with_context(|| { + format!("Failed to get refcount block cache, index is {}", rt_idx) + })?; + } + let cache_entry = self + .refcount_blk_cache + .get(rt_idx) + .with_context(|| format!("Not found refcount block cache, index is {}", rt_idx))? + .clone(); + + let mut rb_vec = Vec::new(); + let mut borrowed_entry = cache_entry.borrow_mut(); + for i in 0..clusters { + let mut rc_value = borrowed_entry.get_entry_map(rb_idx as usize + i)? as u16; + rc_value = if is_add { + rc_value + .checked_add(addend) + .filter(|&v| v <= self.refcount_max as u16) + .with_context(|| { + format!( + "Refcount {} add {} cause overflows, index is {}", + rc_value, addend, i + ) + })? + } else { + rc_value.checked_sub(addend).with_context(|| { + format!( + "Refcount {} sub {} cause overflows, index is {}", + rc_value, addend, i + ) + })? + }; + let cluster_idx = rt_idx * self.refcount_blk_size as u64 + rb_idx + i as u64; + if rc_value == 0 && cluster_idx < self.free_cluster_index { + self.free_cluster_index = cluster_idx; + } + rb_vec.push(rc_value); + } + + for (idx, rc_value) in rb_vec.iter().enumerate() { + borrowed_entry.set_entry_map(rb_idx as usize + idx, *rc_value as u64)?; + } + + Ok(()) + } + + fn offset_into_cluster(&self, offset: u64) -> u64 { + offset & (self.cluster_size - 1) + } + + fn alloc_refcount_block(&mut self, rt_idx: u64, free_idx: u64) -> Result<()> { + let rb_addr = free_idx << self.cluster_bits; + let rc_block = vec![0_u8; self.cluster_size as usize]; + + // Update refcount table. + self.refcount_table[rt_idx as usize] = rb_addr; + let start = rt_idx * ENTRY_SIZE; + let ret = self.save_refcount_table(start, start + ENTRY_SIZE); + if ret.is_err() { + self.refcount_table[rt_idx as usize] = 0; + ret?; + } + + // Create recount block cache. + let cache_entry = Rc::new(RefCell::new(CacheTable::new( + rb_addr, + rc_block, + ENTRY_SIZE_U16, + )?)); + // Update and save refcount block. + let mut borrow_entry = cache_entry.borrow_mut(); + borrow_entry.set_entry_map(self.cluster_in_rc_block(free_idx) as usize, 1)?; + self.sync_aio.borrow_mut().write_dirty_info( + borrow_entry.addr, + borrow_entry.get_value(), + 0, + self.cluster_size, + )?; + drop(borrow_entry); + if let Some(replaced_entry) = self.refcount_blk_cache.lru_replace(rt_idx, cache_entry) { + self.save_refcount_block(&replaced_entry)?; + } + + Ok(()) + } + + fn find_free_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { + let clusters = self.bytes_to_clusters(size); + let mut current_index = self.free_cluster_index; + let mut i = 0; + while i < clusters { + // Check if it needs to extend refcount table. + let rt_idx = current_index >> self.refcount_blk_bits; + if rt_idx >= self.refcount_table_size as u64 { + self.extend_refcount_table(header, current_index)?; + if current_index > self.free_cluster_index { + current_index = self.free_cluster_index; + } else { + current_index += 1; + } + i = 0; + continue; + } + + // Check if it needs to alloc refcount block. + let rb_addr = self.refcount_table[rt_idx as usize]; + if rb_addr == 0 { + // Need to alloc refcount block. + self.alloc_refcount_block(rt_idx as u64, current_index)?; + current_index += 1; + i = 0; + continue; + } else if self.offset_into_cluster(rb_addr) != 0 { + bail!( + "Invalid refcount block address 0x{:x}, index is {}", + rb_addr, + rt_idx + ); + } + + // Load refcount block from disk which is not in cache. + if !self.refcount_blk_cache.contains_keys(rt_idx) { + self.load_refcount_block(rt_idx).with_context(|| { + format!("Failed to get refcount block cache, index is {}", rt_idx) + })?; + } + + // Check if the cluster of current_index is free. + let idx = self.cluster_in_rc_block(current_index) as usize; + let cache_entry = self.refcount_blk_cache.get(rt_idx).unwrap(); + let borrowed_entry = cache_entry.borrow(); + + let find_idx = borrowed_entry.find_empty_entry(idx)?; + if find_idx != idx { + current_index += (find_idx - idx) as u64; + i = 0; + continue; + } + + i += 1; + current_index += 1; + } + self.free_cluster_index = current_index; + Ok((current_index - clusters) << self.cluster_bits) + } + + pub fn alloc_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { + let addr = self.find_free_cluster(header, size)?; + let clusters = self.bytes_to_clusters(size as u64); + self.update_refcount(addr, clusters, true, 1)?; + Ok(addr) + } + + fn load_refcount_block(&mut self, rt_idx: u64) -> Result<()> { + let rb_addr = self.refcount_table[rt_idx as usize]; + if !is_aligned(self.cluster_size, rb_addr) { + bail!("Refcount block address not aligned {}", rb_addr); + } + let mut rc_block = vec![0_u8; self.cluster_size as usize]; + self.sync_aio + .borrow_mut() + .read_buffer(rb_addr, &mut rc_block)?; + let cache_entry = Rc::new(RefCell::new(CacheTable::new( + rb_addr, + rc_block, + ENTRY_SIZE_U16, + )?)); + if let Some(replaced_entry) = self.refcount_blk_cache.lru_replace(rt_idx, cache_entry) { + self.save_refcount_block(&replaced_entry)?; + } + Ok(()) + } + + fn save_refcount_table(&mut self, start: u64, end: u64) -> Result<()> { + let vec: Vec = self + .refcount_table + .iter() + .flat_map(|val| val.to_be_bytes()) + .collect(); + self.sync_aio + .borrow_mut() + .write_dirty_info(self.refcount_table_offset, &vec, start, end) + } + + fn save_refcount_block(&mut self, entry: &Rc>) -> Result<()> { + let borrowed_entry = entry.borrow(); + if !borrowed_entry.dirty_info.is_dirty { + return Ok(()); + } + if !is_aligned(self.cluster_size, borrowed_entry.addr) { + bail!( + "Refcount block address is not aligned {}", + borrowed_entry.addr + ); + } + self.sync_aio.borrow_mut().write_dirty_info( + borrowed_entry.addr, + borrowed_entry.get_value(), + borrowed_entry.dirty_info.start, + borrowed_entry.dirty_info.end, + ) + } +} -- Gitee From b6fc4261a8c3b02b6c4baaa370e24e54f3f2ab4d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 24 Jun 2023 14:45:24 +0800 Subject: [PATCH 1182/2187] qcow2: support read/write for qcow2 driver Support read/write for qcow2 driver, discard and write zeroes are not supported now. Signed-off-by: zhouli57 Signed-off-by: Yan Wang Signed-off-by: liuxiangdong Signed-off-by: Xiao Ye --- block_backend/src/file.rs | 85 ++++++-- block_backend/src/lib.rs | 17 +- block_backend/src/qcow2/mod.rs | 315 ++++++++++++++++++++++++++++-- block_backend/src/raw.rs | 15 +- devices/src/scsi/bus.rs | 10 +- machine_manager/src/config/mod.rs | 7 +- util/src/aio/mod.rs | 93 ++++++++- virtio/src/device/block.rs | 9 +- 8 files changed, 492 insertions(+), 59 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 027303370..9a88f50a3 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -17,7 +17,7 @@ use std::{ os::unix::prelude::{AsRawFd, RawFd}, rc::Rc, sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, + atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering}, Arc, Mutex, }, }; @@ -35,6 +35,17 @@ use util::{ }, }; +pub struct CombineRequest { + pub iov: Vec, + pub offset: u64, +} + +impl CombineRequest { + pub fn new(iov: Vec, offset: u64) -> Self { + Self { iov, offset } + } +} + pub struct FileDriver { file: File, aio: Rc>>, @@ -75,29 +86,53 @@ impl FileDriver { iocompletecb, discard: self.block_prop.discard, write_zeroes: self.block_prop.write_zeroes, + combine_req: None, } } - pub fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - let aiocb = self.package_aiocb( - OpCode::Preadv, - iovec.to_vec(), - offset, - get_iov_size(iovec), - completecb, - ); - self.aio.borrow_mut().submit_request(aiocb) + fn rw_vectored( + &mut self, + opcode: OpCode, + req_list: Vec, + completecb: T, + ) -> Result<()> { + let single_req = req_list.len() == 1; + let cnt = Arc::new(AtomicU32::new(req_list.len() as u32)); + let res = Arc::new(AtomicI64::new(0)); + for req in req_list { + let nbytes = get_iov_size(&req.iov); + let mut aiocb = self.package_aiocb( + opcode, + req.iov, + req.offset as usize, + nbytes, + completecb.clone(), + ); + if !single_req { + aiocb.combine_req = Some((cnt.clone(), res.clone())); + } + self.aio.borrow_mut().submit_request(aiocb)?; + } + Ok(()) } - pub fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - let aiocb = self.package_aiocb( - OpCode::Pwritev, - iovec.to_vec(), - offset, - get_iov_size(iovec), - completecb, - ); - self.aio.borrow_mut().submit_request(aiocb) + pub fn read_vectored(&mut self, req_list: Vec, completecb: T) -> Result<()> { + self.rw_vectored(OpCode::Preadv, req_list, completecb) + } + + pub fn complete_read_request( + &mut self, + iovec: &[Iovec], + offset: usize, + nbytes: u64, + completecb: T, + ) -> Result<()> { + let aiocb = self.package_aiocb(OpCode::Preadv, iovec.to_vec(), offset, nbytes, completecb); + (self.aio.borrow_mut().complete_func)(&aiocb, nbytes as i64) + } + + pub fn write_vectored(&mut self, req_list: Vec, completecb: T) -> Result<()> { + self.rw_vectored(OpCode::Pwritev, req_list, completecb) } pub fn write_zeroes( @@ -161,6 +196,18 @@ impl FileDriver { .with_context(|| "Failed to seek the end for file")?; Ok(disk_size) } + + pub fn extend_len(&mut self, len: u64) -> Result<()> { + let file_end = self.file.seek(SeekFrom::End(0))?; + if len > file_end { + self.file.set_len(len)?; + } + Ok(()) + } + + pub fn meta_len(&self) -> Result { + Ok(self.file.metadata()?.len()) + } } struct FileIoHandler { diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index cd3423804..e117db3b1 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -19,7 +19,7 @@ use std::{ sync::{atomic::AtomicBool, Arc, Mutex}, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use machine_manager::config::DiskFormat; use qcow2::Qcow2Driver; @@ -79,12 +79,23 @@ pub fn create_block_backend( ) -> Result>>> { match prop.format { DiskFormat::Raw => { - let raw_file = RawDriver::new(file, aio, prop); + let mut raw_file = RawDriver::new(file, aio, prop.clone()); + let file_size = raw_file.disk_size()?; + if file_size & (prop.req_align as u64 - 1) != 0 { + bail!("The size of raw file is not aligned to {}.", prop.req_align); + } Ok(Arc::new(Mutex::new(raw_file))) } DiskFormat::Qcow2 => { - let qcow2 = Qcow2Driver::new(file, aio, prop) + let mut qcow2 = Qcow2Driver::new(file, aio, prop.clone()) .with_context(|| "Failed to create qcow2 driver")?; + let file_size = qcow2.disk_size()?; + if file_size & (prop.req_align as u64 - 1) != 0 { + bail!( + "The size of qcow2 file is not aligned to {}.", + prop.req_align + ); + } Ok(Arc::new(Mutex::new(qcow2))) } } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 83da8debf..81eecefe8 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -24,17 +24,25 @@ use std::{ sync::{atomic::AtomicBool, Arc}, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; +use log::error; -use self::{header::QcowHeader, refcount::RefCount, table::Qcow2Table}; - -use super::BlockDriverOps; -use crate::{file::FileDriver, BlockIoErrorCallback, BlockProperty}; -use util::{ - aio::{Aio, AioCb, AioEngine, Iovec, OpCode}, - num_ops::{round_down, round_up}, +use self::cache::ENTRY_SIZE_U64; +use crate::{ + file::{CombineRequest, FileDriver}, + qcow2::{ + cache::CacheTable, + header::QcowHeader, + refcount::RefCount, + table::{Qcow2ClusterType, Qcow2Table}, + }, + BlockDriverOps, BlockIoErrorCallback, BlockProperty, +}; +use util::aio::{ + get_iov_size, iov_from_buf_direct, iovecs_split, Aio, AioCb, AioEngine, Iovec, OpCode, }; +use util::num_ops::{round_down, round_up}; // The L1/L2/Refcount table entry size. const ENTRY_SIZE: u64 = 1 << ENTRY_BITS; @@ -90,6 +98,7 @@ impl SyncAioInfo { iocompletecb: (), discard: self.prop.discard, write_zeroes: self.prop.write_zeroes, + combine_req: None, } } @@ -150,19 +159,230 @@ pub struct Qcow2Driver { refcount: RefCount, } +impl Drop for Qcow2Driver { + fn drop(&mut self) { + self.flush() + .unwrap_or_else(|e| error!("Flush failed: {:?}", e)); + } +} + impl Qcow2Driver { pub fn new(file: File, aio: Aio, conf: BlockProperty) -> Result { let fd = file.as_raw_fd(); let sync_aio = Rc::new(RefCell::new(SyncAioInfo::new(fd, conf.clone())?)); - let qcow2 = Self { + let mut qcow2 = Self { driver: FileDriver::new(file, aio, conf), sync_aio: sync_aio.clone(), header: QcowHeader::default(), table: Qcow2Table::new(sync_aio.clone()), refcount: RefCount::new(sync_aio), }; + qcow2 + .load_header() + .with_context(|| "Failed to load header")?; + qcow2.check().with_context(|| "Invalid header")?; + qcow2 + .table + .init_table(&qcow2.header) + .with_context(|| "Failed to create qcow2 table")?; + qcow2.refcount.init_refcount_info(&qcow2.header); + qcow2 + .load_refcount_table() + .with_context(|| "Failed to load refcount table")?; Ok(qcow2) } + + fn check(&self) -> Result<()> { + let file_sz = self + .driver + .meta_len() + .with_context(|| "Failed to get metadata len")?; + self.header.check(file_sz)?; + Ok(()) + } + + fn flush(&mut self) -> Result<()> { + self.table.flush() + } + + fn load_header(&mut self) -> Result<()> { + let mut buf = vec![0; QcowHeader::len()]; + self.sync_aio.borrow_mut().read_buffer(0, &mut buf)?; + self.header = QcowHeader::from_vec(&buf)?; + Ok(()) + } + + fn load_refcount_table(&mut self) -> Result<()> { + let sz = self.header.refcount_table_clusters as u64 + * (self.header.cluster_size() / ENTRY_SIZE as u64); + self.refcount.refcount_table = self + .sync_aio + .borrow_mut() + .read_ctrl_cluster(self.header.refcount_table_offset, sz)?; + Ok(()) + } + + fn host_offset_for_read(&mut self, guest_offset: u64) -> Result { + let l2_address = self.table.get_l1_table_entry(guest_offset) & L1_TABLE_OFFSET_MASK; + if l2_address == 0 { + return Ok(HostOffset::DataNotInit); + } + + let cluster_addr: u64; + let cluster_type: Qcow2ClusterType; + let l2_index = self.table.get_l2_table_index(guest_offset); + if let Some(entry) = self.table.get_l2_table_cache_entry(guest_offset) { + let l2_entry = entry.borrow_mut().get_entry_map(l2_index as usize)?; + cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); + cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; + } else { + let l2_cluster = self.load_l2_cluster(l2_address)?; + let l2_table = Rc::new(RefCell::new(CacheTable::new( + l2_address, + l2_cluster, + ENTRY_SIZE_U64, + )?)); + let l2_entry = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; + cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); + cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; + self.table.update_l2_table(l2_table)?; + } + + if cluster_addr == 0 || cluster_type.is_read_zero() { + Ok(HostOffset::DataNotInit) + } else { + Ok(HostOffset::DataAddress( + cluster_addr + self.offset_into_cluster(guest_offset), + )) + } + } + + fn host_offset_for_write(&mut self, guest_offset: u64) -> Result { + let l2_index = self.table.get_l2_table_index(guest_offset); + let l2_table = self.get_table_cluster(guest_offset)?; + let mut l2_entry = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; + l2_entry &= !QCOW2_OFLAG_ZERO; + let mut cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; + if cluster_addr == 0 { + let new_addr = self.alloc_cluster(1, true)?; + l2_entry = new_addr | QCOW2_OFFSET_COPIED; + cluster_addr = new_addr & L2_TABLE_OFFSET_MASK; + } + l2_table + .borrow_mut() + .set_entry_map(l2_index as usize, l2_entry)?; + Ok(HostOffset::DataAddress( + cluster_addr + self.offset_into_cluster(guest_offset), + )) + } + + /// Obtaining the target entry for guest offset. + /// If the corresponding entry didn't cache, it will be read from the disk synchronously. + /// Input: guest offset. + /// Output: target entry. + fn get_table_cluster(&mut self, guest_offset: u64) -> Result>> { + let l1_index = self.table.get_l1_table_index(guest_offset); + if l1_index >= self.header.l1_size as u64 { + bail!("Need to grow l1 table size."); + } + + let l1_entry = self.table.get_l1_table_entry(guest_offset); + let mut l2_address = l1_entry & L1_TABLE_OFFSET_MASK; + // Align to cluster size. + if (l2_address & (self.header.cluster_size() - 1)) != 0 { + bail!( + "L2 table offset {} unaligned(L1 index: {})", + l2_address, + l1_index + ); + } + + if l1_entry & QCOW2_OFFSET_COPIED == 0 { + // Step 1: Alloc a new l2_table. + let old_l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; + let new_l2_offset = self.alloc_cluster(1, true)?; + // Step 2: Update l1_table and l2 table cache. + self.table.update_l1_table( + l1_index as usize, + new_l2_offset | QCOW2_OFFSET_COPIED, + &self.header, + )?; + let zero_cluster: Vec = vec![0_u8; self.header.cluster_size() as usize]; + let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( + new_l2_offset, + zero_cluster, + ENTRY_SIZE_U64, + )?)); + self.table.update_l2_table(l2_table_entry)?; + // Step 3: Decrease the refcount of the old table. + if old_l2_offset != 0 { + self.refcount.update_refcount(old_l2_offset, 1, false, 1)?; + } + // Step 4. Get the offset of the newly-allocated l2 table. + l2_address = new_l2_offset; + } + + // Cache hit. + if let Some(entry) = self.table.l2_table_cache.get(l2_address) { + return Ok(entry.clone()); + } + // Cache miss. + let l2_cluster = self.load_l2_cluster(l2_address)?; + let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( + l2_address, + l2_cluster, + ENTRY_SIZE_U64, + )?)); + self.table.update_l2_table(l2_table_entry.clone())?; + Ok(l2_table_entry) + } + + fn offset_into_cluster(&self, guest_offset: u64) -> u64 { + guest_offset & (self.header.cluster_size() - 1) + } + + fn load_l2_cluster(&mut self, addr: u64) -> Result> { + if !is_aligned(self.header.cluster_size(), addr) { + bail!("L2 cluster address not aligned {}", addr); + } + let mut buf = vec![0_u8; self.header.cluster_size() as usize]; + self.sync_aio.borrow_mut().read_buffer(addr, &mut buf)?; + Ok(buf) + } + + fn virtual_disk_size(&self) -> u64 { + self.header.size + } + + fn cluster_aligned_bytes(&self, addr: u64, cnt: u64) -> u64 { + let offset = self.offset_into_cluster(addr); + std::cmp::min(cnt, self.header.cluster_size() - offset) + } + + fn alloc_cluster(&mut self, clusters: u64, write_zero: bool) -> Result { + let size = clusters * self.header.cluster_size(); + let addr = self.refcount.alloc_cluster(&mut self.header, size)?; + if write_zero && addr < self.driver.disk_size()? { + // Clean the cluster. + let zero = vec![0_u8; self.header.cluster_size() as usize]; + self.sync_aio.borrow_mut().write_buffer(addr, &zero)?; + } + self.driver.extend_len(addr + size)?; + Ok(addr) + } + + fn check_request(&self, offset: usize, nbytes: u64) -> Result<()> { + if offset as u64 > self.virtual_disk_size() { + bail!("Invalid offset {}", offset); + } + let end = (offset as u64) + .checked_add(nbytes) + .with_context(|| format!("Invalid offset {} or size {}", offset, nbytes))?; + if end > self.virtual_disk_size() { + bail!("Request over limit {}", end); + } + Ok(()) + } } // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. @@ -172,23 +392,77 @@ unsafe impl Sync for Qcow2Driver {} impl BlockDriverOps for Qcow2Driver { fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - todo!() + let nbytes = get_iov_size(iovec); + self.check_request(offset, nbytes) + .with_context(|| " Invalid read request")?; + + let mut left = iovec.to_vec(); + let total = std::cmp::min(nbytes, self.virtual_disk_size() - offset as u64); + let mut req_list = Vec::new(); + let mut copyed = 0; + while copyed < total { + let pos = offset as u64 + copyed; + let count = self.cluster_aligned_bytes(pos, total - copyed); + let (begin, end) = iovecs_split(left, count); + left = end; + if let HostOffset::DataAddress(host_offset) = self.host_offset_for_read(pos)? { + req_list.push(CombineRequest { + iov: begin, + offset: host_offset, + }); + } else { + iov_from_buf_direct(&begin, &vec![0_u8; count as usize])?; + } + copyed += count; + } + if req_list.is_empty() { + // Not submitting an AIO request, call callback directly. + self.driver + .complete_read_request(iovec, offset, nbytes, completecb) + } else { + self.driver.read_vectored(req_list, completecb) + } } fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - todo!() + let nbytes = get_iov_size(iovec); + self.check_request(offset, nbytes) + .with_context(|| " Invalid write request")?; + + let mut left = iovec.to_vec(); + let total = std::cmp::min(nbytes, self.virtual_disk_size() - offset as u64); + let mut req_list = Vec::new(); + let mut copyed = 0; + while copyed < total { + let pos = offset as u64 + copyed; + let count = self.cluster_aligned_bytes(pos, total - copyed); + let (begin, end) = iovecs_split(left, count); + left = end; + if let HostOffset::DataAddress(host_offset) = self.host_offset_for_write(pos)? { + req_list.push(CombineRequest { + iov: begin, + offset: host_offset, + }); + copyed += count; + } + } + + if req_list.is_empty() { + bail!("Request list is empty!"); + } + self.driver.write_vectored(req_list, completecb) } - fn datasync(&mut self, args: T) -> Result<()> { - todo!() + fn datasync(&mut self, completecb: T) -> Result<()> { + self.driver.datasync(completecb) } fn disk_size(&mut self) -> Result { - todo!() + Ok(self.virtual_disk_size()) } fn discard(&mut self, _offset: usize, _nbytes: u64, _completecb: T) -> Result<()> { - todo!() + bail!("discard not supported now"); } fn write_zeroes( @@ -198,15 +472,16 @@ impl BlockDriverOps for Qcow2Driver { _completecb: T, _unmap: bool, ) -> Result<()> { - todo!() + bail!("write zero not supported now"); } fn flush_request(&mut self) -> Result<()> { - todo!() + self.flush()?; + self.driver.flush_request() } fn drain_request(&self) { - todo!() + self.driver.drain_request(); } fn register_io_event( @@ -214,11 +489,11 @@ impl BlockDriverOps for Qcow2Driver { broken: Arc, error_cb: BlockIoErrorCallback, ) -> Result<()> { - todo!() + self.driver.register_io_event(broken, error_cb) } fn unregister_io_event(&mut self) -> Result<()> { - todo!() + self.driver.unregister_io_event() } } diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index d9ca06120..584ded815 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -17,7 +17,10 @@ use std::{ use anyhow::Result; -use crate::{file::FileDriver, BlockDriverOps, BlockIoErrorCallback, BlockProperty}; +use crate::{ + file::{CombineRequest, FileDriver}, + BlockDriverOps, BlockIoErrorCallback, BlockProperty, +}; use util::aio::{Aio, Iovec}; pub struct RawDriver { @@ -39,11 +42,17 @@ impl RawDriver { impl BlockDriverOps for RawDriver { fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - self.driver.read_vectored(iovec, offset, completecb) + self.driver.read_vectored( + vec![CombineRequest::new(iovec.to_vec(), offset as u64)], + completecb, + ) } fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - self.driver.write_vectored(iovec, offset, completecb) + self.driver.write_vectored( + vec![CombineRequest::new(iovec.to_vec(), offset as u64)], + completecb, + ) } fn write_zeroes( diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 2e4780b5f..1304fee12 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -24,7 +24,7 @@ use crate::ScsiDisk::{ SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, SECTOR_SHIFT, }; -use util::aio::{AioCb, Iovec}; +use util::aio::{AioCb, AioReqResult, Iovec}; use util::AsAny; /// Scsi Operation code. @@ -457,7 +457,13 @@ pub struct ScsiCompleteCb { pub req: Arc>, } -pub fn aio_complete_cb(aiocb: &AioCb, ret: i64) -> Result<()> { +pub fn aio_complete_cb(aiocb: &AioCb, mut ret: i64) -> Result<()> { + match aiocb.req_is_completed(ret) { + AioReqResult::Inflight => return Ok(()), + AioReqResult::Error(v) => ret = v, + AioReqResult::Done => (), + } + let (status, sense) = if ret < 0 { (CHECK_CONDITION, Some(SCSI_SENSE_IO_ERROR)) } else { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 2b080990c..075eab18a 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -67,7 +67,6 @@ pub mod vnc; use std::collections::HashMap; use std::fs::File; -use std::io::{Seek, SeekFrom}; use std::str::FromStr; use serde::{Deserialize, Serialize}; @@ -294,7 +293,7 @@ impl VmConfig { )); } } - let mut file = open_file(path, read_only, direct)?; + let file = open_file(path, read_only, direct)?; let (req_align, buf_align) = get_file_alignment(&file, direct); if req_align == 0 || buf_align == 0 { bail!( @@ -302,10 +301,6 @@ impl VmConfig { path ); } - let file_size = file.seek(SeekFrom::End(0))?; - if file_size & (req_align as u64 - 1) != 0 { - bail!("The size of file {} is not aligned to {}.", path, req_align); - } let drive_file = DriveFile { file, count: 1, diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 9e1711cfe..197bf6f84 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -17,7 +17,7 @@ mod uring; use std::clone::Clone; use std::io::Write; use std::os::unix::io::RawFd; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::atomic::{AtomicI64, AtomicU32, AtomicU64, Ordering}; use std::sync::Arc; use std::{cmp, str::FromStr}; @@ -86,7 +86,7 @@ impl FromStr for WriteZeroesState { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Iovec { pub iov_base: u64, pub iov_len: u64, @@ -151,6 +151,35 @@ pub struct AioCb { pub nbytes: u64, pub user_data: u64, pub iocompletecb: T, + pub combine_req: Option<(Arc, Arc)>, +} + +pub enum AioReqResult { + Inflight, + Error(i64), + Done, +} + +impl AioCb { + pub fn req_is_completed(&self, ret: i64) -> AioReqResult { + if let Some((cnt, res)) = self.combine_req.as_ref() { + if ret < 0 { + // Store error code in res. + if let Err(v) = res.compare_exchange(0, ret, Ordering::SeqCst, Ordering::SeqCst) { + warn!("Error already existed, old {} new {}", v, ret); + } + } + if cnt.fetch_sub(1, Ordering::SeqCst) > 1 { + // Request is not completed. + return AioReqResult::Inflight; + } + let v = res.load(Ordering::SeqCst); + if v < 0 { + return AioReqResult::Error(v); + } + } + AioReqResult::Done + } } pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; @@ -164,7 +193,7 @@ pub struct Aio { /// IO in aio_in_queue and aio_in_flight. pub incomplete_cnt: Arc, max_events: usize, - complete_func: Arc>, + pub complete_func: Arc>, } pub fn aio_probe(engine: AioEngine) -> Result<()> { @@ -430,13 +459,20 @@ impl Aio { nbytes as usize, offset as usize, ); - if len < 0 || len as u64 != nbytes { + if len < 0 { bail!("Failed to do raw read for misaligned read."); } let real_offset = cmp::max(offset, cb.offset as u64); let real_high = cmp::min(offset + nbytes, high); let real_nbytes = real_high - real_offset; + if (len as u64) < real_high - offset { + bail!( + "misaligned read len {} less than the nbytes {}", + len, + real_high - offset + ); + } // SAFETY: the memory is allocated by us. let src = unsafe { std::slice::from_raw_parts( @@ -685,6 +721,26 @@ fn iovec_is_zero(iovecs: &[Iovec]) -> bool { true } +pub fn iovecs_split(iovecs: Vec, mut size: u64) -> (Vec, Vec) { + let mut begin = Vec::new(); + let mut end = Vec::new(); + for iov in iovecs { + if size == 0 { + end.push(iov); + continue; + } + if iov.iov_len as u64 > size { + begin.push(Iovec::new(iov.iov_base, size)); + end.push(Iovec::new(iov.iov_base + size, iov.iov_len - size)); + size = 0; + } else { + size -= iov.iov_len as u64; + begin.push(iov); + } + } + (begin, end) +} + #[cfg(test)] mod tests { use super::*; @@ -738,6 +794,7 @@ mod tests { nbytes, user_data: 0, iocompletecb: 0, + combine_req: None, }; let mut aio = Aio::new( Arc::new(|_: &AioCb, _: i64| -> Result<()> { Ok(()) }), @@ -810,4 +867,32 @@ mod tests { test_sync_rw_all_align(OpCode::Preadv, false); test_sync_rw_all_align(OpCode::Pwritev, false); } + + #[test] + fn test_iovecs_split() { + let iovecs = vec![Iovec::new(0, 100), Iovec::new(200, 100)]; + let (left, right) = iovecs_split(iovecs, 0); + assert_eq!(left, vec![]); + assert_eq!(right, vec![Iovec::new(0, 100), Iovec::new(200, 100)]); + + let iovecs = vec![Iovec::new(0, 100), Iovec::new(200, 100)]; + let (left, right) = iovecs_split(iovecs, 50); + assert_eq!(left, vec![Iovec::new(0, 50)]); + assert_eq!(right, vec![Iovec::new(50, 50), Iovec::new(200, 100)]); + + let iovecs = vec![Iovec::new(0, 100), Iovec::new(200, 100)]; + let (left, right) = iovecs_split(iovecs, 100); + assert_eq!(left, vec![Iovec::new(0, 100)]); + assert_eq!(right, vec![Iovec::new(200, 100)]); + + let iovecs = vec![Iovec::new(0, 100), Iovec::new(200, 100)]; + let (left, right) = iovecs_split(iovecs, 150); + assert_eq!(left, vec![Iovec::new(0, 100), Iovec::new(200, 50)]); + assert_eq!(right, vec![Iovec::new(250, 50)]); + + let iovecs = vec![Iovec::new(0, 100), Iovec::new(200, 100)]; + let (left, right) = iovecs_split(iovecs, 300); + assert_eq!(left, vec![Iovec::new(0, 100), Iovec::new(200, 100)]); + assert_eq!(right, vec![]); + } } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index eef400693..4777fcd2e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -47,7 +47,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::aio::{ - iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, Iovec, OpCode, + iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, AioReqResult, Iovec, OpCode, WriteZeroesState, }; use util::byte_code::ByteCode; @@ -683,7 +683,12 @@ impl BlockIoHandler { result } - fn complete_func(aiocb: &AioCb, ret: i64) -> Result<()> { + fn complete_func(aiocb: &AioCb, mut ret: i64) -> Result<()> { + match aiocb.req_is_completed(ret) { + AioReqResult::Inflight => return Ok(()), + AioReqResult::Error(v) => ret = v, + AioReqResult::Done => (), + } let mut status = if ret < 0 { VIRTIO_BLK_S_IOERR } else { -- Gitee From 0c95152bf41c4e32146481d0116c9a4e3d5b2568 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 3 Jul 2023 14:54:11 +0800 Subject: [PATCH 1183/2187] qcow2: add some UT Signed-off-by: zhouli57 Signed-off-by: Yan Wang --- block_backend/src/qcow2/mod.rs | 388 ++++++++++++++++++++++++++++ block_backend/src/qcow2/refcount.rs | 277 ++++++++++++++++++++ 2 files changed, 665 insertions(+) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 81eecefe8..ea3635014 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -500,3 +500,391 @@ impl BlockDriverOps for Qcow2Driver { pub fn is_aligned(cluster_sz: u64, offset: u64) -> bool { offset & (cluster_sz - 1) == 0 } + +#[cfg(test)] +mod test { + use std::{ + fs::remove_file, + io::{Seek, SeekFrom, Write}, + os::unix::fs::OpenOptionsExt, + }; + + use machine_manager::config::DiskFormat; + use util::{ + aio::{iov_to_buf_direct, WriteZeroesState}, + file::get_file_alignment, + }; + + use super::*; + + const CLUSTER_SIZE: u64 = 64 * 1024; + + struct TestImage { + pub path: String, + pub file: File, + } + + impl TestImage { + fn new(path: &str, img_bits: u32, cluster_bits: u32) -> TestImage { + let cluster_sz = 1 << cluster_bits; + let header = QcowHeader { + magic: crate::qcow2::header::QCOW_MAGIC, + version: 3, + backing_file_offset: 0, + backing_file_size: 0, + cluster_bits, + size: 1 << img_bits, + crypt_method: 0, + l1_size: 1 << (img_bits - (cluster_bits * 2 - 3)), + l1_table_offset: 3 * cluster_sz, + refcount_table_offset: cluster_sz, + refcount_table_clusters: 1, + nb_snapshots: 0, + snapshots_offset: 0, + incompatible_features: 0, + compatible_features: 0, + autoclear_features: 0, + refcount_order: 4, + header_length: std::mem::size_of::() as u32, + }; + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_CREAT | libc::O_TRUNC) + .open(path) + .unwrap(); + file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) + .unwrap(); + file.write_all(&header.to_vec()).unwrap(); + + // Cluster 1 is the refcount table. + assert_eq!(header.refcount_table_offset, cluster_sz * 1); + let mut refcount_table = [0_u8; ENTRY_SIZE as usize]; + BigEndian::write_u64(&mut refcount_table, cluster_sz * 2); + file.seek(SeekFrom::Start(cluster_sz * 1)).unwrap(); + file.write_all(&refcount_table).unwrap(); + + // Clusters which has been allocated. + assert_eq!(header.refcount_order, 4); + let clusters = + 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); + let mut refcount_block = Vec::new(); + for _ in 0..clusters { + refcount_block.push(0x00); + refcount_block.push(0x01); + } + file.seek(SeekFrom::Start(cluster_sz * 2)).unwrap(); + file.write_all(&refcount_block).unwrap(); + TestImage { + path: path.to_string(), + file, + } + } + } + + impl Drop for TestImage { + fn drop(&mut self) { + remove_file(&self.path).unwrap() + } + } + + struct TestData { + data: u8, + sz: usize, + } + + impl TestData { + fn new(data: u8, sz: usize) -> Self { + Self { data, sz } + } + } + + struct TestRwCase { + riovec: Vec, + wiovec: Vec, + data: Vec, + offset: usize, + sz: u64, + } + + fn create_qcow2(path: &str) -> (TestImage, Qcow2Driver<()>) { + let mut image = TestImage::new(path, 30, 16); + fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { + Ok(()) + } + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_DIRECT) + .open(path) + .unwrap(); + let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); + let (req_align, buf_align) = get_file_alignment(&image.file, true); + let conf = BlockProperty { + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align, + buf_align, + discard: false, + write_zeroes: WriteZeroesState::Off, + }; + image.file = file.try_clone().unwrap(); + (image, Qcow2Driver::new(file, aio, conf).unwrap()) + } + + fn qcow2_read(qcow2: &mut Qcow2Driver<()>, buf: &mut [u8], offset: usize) -> Result<()> { + qcow2.read_vectored( + &[Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + offset, + (), + ) + } + + fn qcow2_write(qcow2: &mut Qcow2Driver<()>, buf: &[u8], offset: usize) -> Result<()> { + qcow2.write_vectored( + &[Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + offset, + (), + ) + } + + #[test] + fn test_read_zero() { + let path = "/tmp/block_backend_test_read_zero.qcow2"; + let (mut image, mut qcow2) = create_qcow2(path); + let org_len = image.file.seek(SeekFrom::End(0)).unwrap(); + + let mut buf = vec![1_u8; 128]; + qcow2_read(&mut qcow2, &mut buf, 40).unwrap(); + assert_eq!(buf, vec![0; 128]); + let mut buf = vec![2_u8; 512]; + qcow2_read(&mut qcow2, &mut buf, 65536).unwrap(); + assert_eq!(buf, vec![0; 512]); + let mut buf = vec![3_u8; 600]; + qcow2_read(&mut qcow2, &mut buf, 655350).unwrap(); + assert_eq!(buf, vec![0; 600]); + + let len = image.file.seek(SeekFrom::End(0)).unwrap(); + assert_eq!(org_len, len); + } + + #[test] + fn test_write_single_cluster() { + let path = "/tmp/block_backend_test_write_single_cluster.qcow2"; + let (_, mut qcow2) = create_qcow2(path); + + let wbuf = vec![7_u8; CLUSTER_SIZE as usize]; + qcow2_write(&mut qcow2, &wbuf, 0).unwrap(); + let mut rbuf = vec![0_u8; CLUSTER_SIZE as usize]; + qcow2_read(&mut qcow2, &mut rbuf, 0).unwrap(); + assert_eq!(rbuf, wbuf); + + let wbuf = vec![5_u8; 1000]; + qcow2_write(&mut qcow2, &wbuf, 2000).unwrap(); + let mut rbuf = vec![0_u8; 1000]; + qcow2_read(&mut qcow2, &mut rbuf, 2000).unwrap(); + assert_eq!(rbuf, wbuf); + } + + #[test] + fn test_write_multi_cluster() { + let path = "/tmp/block_backend_test_write_multi_cluster.qcow2"; + let (_, mut qcow2) = create_qcow2(path); + + let mut offset = 0; + let cnt: u8 = 2; + let sz = 100 * 1000; + for i in 0..cnt { + let buf = vec![i + 1; sz]; + qcow2_write(&mut qcow2, &buf, offset).unwrap(); + offset += buf.len(); + } + let mut offset = 0; + for i in 0..cnt { + let mut buf = vec![i + 1; sz]; + qcow2_read(&mut qcow2, &mut buf, offset).unwrap(); + for (_, item) in buf.iter().enumerate() { + assert_eq!(item, &(i + 1)); + } + offset += buf.len(); + } + } + + #[test] + fn test_invalid_read_write() { + let path = "/tmp/block_backend_test_invalid_read_write.qcow2"; + let (_, mut qcow2) = create_qcow2(path); + + let mut buf = vec![0_u8; 100]; + let disk_size = qcow2.disk_size().unwrap(); + let res = qcow2_write(&mut qcow2, &buf, disk_size as usize + 1); + assert!(res.is_err()); + + let res = qcow2_read(&mut qcow2, &mut buf, disk_size as usize + 100); + assert!(res.is_err()); + } + + fn generate_iovecs( + buf_list: &mut Vec>, + list: &Vec, + ) -> (Vec, Vec) { + let mut riovec = Vec::new(); + let mut wiovec = Vec::new(); + for item in list { + let buf = vec![0_u8; item.sz]; + riovec.push(Iovec::new(buf.as_ptr() as u64, buf.len() as u64)); + buf_list.push(buf); + let buf = vec![item.data; item.sz]; + wiovec.push(Iovec::new(buf.as_ptr() as u64, buf.len() as u64)); + buf_list.push(buf); + } + (riovec, wiovec) + } + + fn generate_rw_case_list() -> (Vec, Vec>) { + let mut list = Vec::new(); + let mut buf_list = Vec::new(); + let test_data = vec![ + TestData::new(1, 100_000), + TestData::new(2, 100_000), + TestData::new(3, 100_000), + ]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 12590, + sz: 100_000 * 3, + }); + + let test_data = vec![ + TestData::new(1, 1_000), + TestData::new(2, 100_000), + TestData::new(3, 10_000), + TestData::new(4, 20_000), + TestData::new(5, 80_000), + ]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 8935201, + sz: 211_000, + }); + + (list, buf_list) + } + + #[test] + fn test_read_write_vectored() { + let path = "/tmp/block_backend_test_read_write_vectored.qcow2"; + let (_, mut qcow2) = create_qcow2(path); + let (case_list, _buf_list) = generate_rw_case_list(); + for case in &case_list { + qcow2.write_vectored(&case.wiovec, case.offset, ()).unwrap(); + qcow2.read_vectored(&case.riovec, case.offset, ()).unwrap(); + + let mut wbuf = vec![0; case.sz as usize]; + let mut rbuf = vec![0; case.sz as usize]; + let wsz = iov_to_buf_direct(&case.wiovec, 0, &mut wbuf).unwrap(); + let rsz = iov_to_buf_direct(&case.riovec, 0, &mut rbuf).unwrap(); + assert_eq!(wsz, case.sz as usize); + assert_eq!(rsz, case.sz as usize); + assert_eq!(wbuf, rbuf); + } + } + + fn generate_rw_random_list() -> (Vec, Vec>) { + let mut list = Vec::new(); + let mut buf_list = Vec::new(); + let test_data = vec![TestData::new(1, CLUSTER_SIZE as usize)]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 0, + sz: CLUSTER_SIZE, + }); + let test_data = vec![TestData::new(2, CLUSTER_SIZE as usize)]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 2 * CLUSTER_SIZE as usize, + sz: CLUSTER_SIZE, + }); + let test_data = vec![TestData::new(3, CLUSTER_SIZE as usize)]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 4 * CLUSTER_SIZE as usize, + sz: CLUSTER_SIZE, + }); + let test_data = vec![TestData::new(4, CLUSTER_SIZE as usize)]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 1 * CLUSTER_SIZE as usize, + sz: CLUSTER_SIZE, + }); + let test_data = vec![TestData::new(5, CLUSTER_SIZE as usize)]; + let (riovec, wiovec) = generate_iovecs(&mut buf_list, &test_data); + list.push(TestRwCase { + riovec, + wiovec, + data: test_data, + offset: 3 * CLUSTER_SIZE as usize, + sz: CLUSTER_SIZE, + }); + + (list, buf_list) + } + + #[test] + fn test_read_write_random() { + let path = "/tmp/block_backend_test_read_write_random.qcow2"; + let (_, mut qcow2) = create_qcow2(path); + let (mut case_list, _buf_list) = generate_rw_random_list(); + for case in &case_list { + qcow2.write_vectored(&case.wiovec, case.offset, ()).unwrap(); + qcow2.read_vectored(&case.riovec, case.offset, ()).unwrap(); + + let mut wbuf = vec![0; case.sz as usize]; + let mut rbuf = vec![0; case.sz as usize]; + let wsz = iov_to_buf_direct(&case.wiovec, 0, &mut wbuf).unwrap(); + let rsz = iov_to_buf_direct(&case.riovec, 0, &mut rbuf).unwrap(); + assert_eq!(wsz, case.sz as usize); + assert_eq!(rsz, case.sz as usize); + assert_eq!(wbuf, rbuf); + } + + // read all write data once. + let buf = vec![0_u8; 5 * CLUSTER_SIZE as usize]; + let riovecs = vec![Iovec::new(buf.as_ptr() as u64, 5 * CLUSTER_SIZE)]; + qcow2.read_vectored(&riovecs, 0, ()).unwrap(); + + case_list.sort_by(|a, b| a.offset.cmp(&b.offset)); + let mut idx = 0; + for case in case_list.iter() { + for item in case.data.iter() { + assert_eq!(buf[idx..(idx + item.sz)].to_vec(), vec![item.data; item.sz]); + idx += item.sz; + } + } + } +} diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 61abe1021..5c3137a2b 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -439,3 +439,280 @@ impl RefCount { ) } } + +#[cfg(test)] +mod test { + use std::{ + fs::{remove_file, File}, + io::{Seek, SeekFrom, Write}, + os::unix::fs::{FileExt, OpenOptionsExt}, + sync::Arc, + }; + + use anyhow::Result; + use byteorder::{BigEndian, ByteOrder}; + + use crate::qcow2::header::*; + use crate::qcow2::*; + use machine_manager::config::DiskFormat; + use util::aio::{Aio, AioCb, WriteZeroesState}; + + fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { + Ok(()) + } + + fn image_create(path: &str, img_bits: u32, cluster_bits: u32) -> File { + let cluster_sz = 1 << cluster_bits; + let header = QcowHeader { + magic: QCOW_MAGIC, + version: 3, + backing_file_offset: 0, + backing_file_size: 0, + cluster_bits: cluster_bits, + size: 1 << img_bits, + crypt_method: 0, + l1_size: 1 << (img_bits - (cluster_bits * 2 - 3)), + l1_table_offset: 3 * cluster_sz, + refcount_table_offset: cluster_sz, + refcount_table_clusters: 1, + nb_snapshots: 0, + snapshots_offset: 0, + incompatible_features: 0, + compatible_features: 0, + autoclear_features: 0, + refcount_order: 4, + header_length: std::mem::size_of::() as u32, + }; + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_CREAT | libc::O_TRUNC) + .open(path) + .unwrap(); + file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) + .unwrap(); + file.write_all(&header.to_vec()).unwrap(); + + // Cluster 1 is the refcount table. + assert_eq!(header.refcount_table_offset, cluster_sz * 1); + let mut refcount_table = [0_u8; ENTRY_SIZE as usize]; + BigEndian::write_u64(&mut refcount_table, cluster_sz * 2); + file.seek(SeekFrom::Start(cluster_sz * 1)).unwrap(); + file.write_all(&refcount_table).unwrap(); + + // Clusters which has been allocated. + assert_eq!(header.refcount_order, 4); + let clusters = + 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); + let mut refcount_block = Vec::new(); + for _ in 0..clusters { + refcount_block.push(0x00); + refcount_block.push(0x01); + } + file.seek(SeekFrom::Start(cluster_sz * 2)).unwrap(); + file.write_all(&refcount_block).unwrap(); + + file + } + + fn create_qcow2_driver( + path: &str, + img_bits: u32, + cluster_bits: u32, + ) -> (Qcow2Driver<()>, File) { + let file = image_create(path, img_bits, cluster_bits); + let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); + let conf = BlockProperty { + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align: 512, + buf_align: 512, + discard: false, + write_zeroes: WriteZeroesState::Off, + }; + let cloned_file = file.try_clone().unwrap(); + (Qcow2Driver::new(file, aio, conf).unwrap(), cloned_file) + } + + #[test] + fn test_alloc_cluster() { + let path = "/tmp/refcount_case1.qcow2"; + let image_bits = 30; + let cluster_bits = 16; + let (mut qcow2, cloned_file) = create_qcow2_driver(path, image_bits, cluster_bits); + let header = qcow2.header.clone(); + + // Alloc one free clusters + let cluster_sz = 1 << cluster_bits; + let free_cluster_index = + 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); + let addr = qcow2.alloc_cluster(1, true).unwrap(); + assert_eq!(addr, cluster_sz * free_cluster_index as u64); + // Check if the refcount of the cluster is updated to the disk. + let mut rc_value = [0_u8; 2]; + cloned_file + .read_at( + &mut rc_value, + cluster_sz * 2 + 2 * free_cluster_index as u64, + ) + .unwrap(); + assert_eq!(1, BigEndian::read_u16(&rc_value)); + remove_file(path).unwrap(); + } + + #[test] + fn test_extend_refcount_table() { + let path = "/tmp/refcount_case2.qcow2"; + // Image size is 128MB. + let image_bits = 27; + // Cluster size is 1KB. + let cluster_bits = 10; + let (mut qcow2, cloned_file) = create_qcow2_driver(path, image_bits, cluster_bits); + let header = &qcow2.header; + let rct_offset = header.refcount_table_offset; + let rct_clusters = header.refcount_table_clusters; + + // Extend refcount table which can not mark all clusters. + let cluster_sz = 1 << cluster_bits; + // 3 bit means refcount table entry size(8 Byte) + // 1 bit means refcount block entry size(2 Byte). + let mut clusters = 1 << (cluster_bits - 3 + cluster_bits - 1); + clusters /= 2; + // Alloc 2 cluster once for all clusters which will cause extending refcount table. + for _ in 0..clusters + 1 { + qcow2.alloc_cluster(2, true).unwrap(); + } + let new_rct_offset = qcow2.header.refcount_table_offset; + let new_rct_clusters = qcow2.header.refcount_table_clusters; + assert_ne!(new_rct_offset, rct_offset); + assert_eq!(qcow2.header.refcount_table_clusters, rct_clusters + 1); + + // Check if the new refcount table contains the old refcount table. + let old_rct_size = cluster_sz as usize * rct_clusters as usize; + let mut old_rc_table = vec![0_u8; old_rct_size]; + cloned_file.read_at(&mut old_rc_table, rct_offset).unwrap(); + let mut new_rc_table = vec![0_u8; old_rct_size]; + cloned_file.read_at(&mut new_rc_table, rct_offset).unwrap(); + for i in 0..old_rct_size { + assert_eq!(old_rc_table[i], new_rc_table[i]); + } + + // Read the first refcount table entry in the extended cluster of the refcount table. + let mut rct_entry = vec![0_u8; ENTRY_SIZE as usize]; + cloned_file + .read_at(&mut rct_entry, new_rct_offset + old_rct_size as u64) + .unwrap(); + let rcb_offset = BigEndian::read_u64(&rct_entry); + + // Check the refcount block in the extended cluster of the refcount table. + // It will include the cluster of refcount table and itself. + let mut rc_table = vec![0_u8; cluster_sz as usize]; + cloned_file.read_at(&mut rc_table, rcb_offset).unwrap(); + for i in 0..new_rct_clusters as usize + 1 { + let offset = 2 * i; + assert_eq!(1, BigEndian::read_u16(&rc_table[offset..offset + 2])); + } + + remove_file(path).unwrap(); + } + + #[test] + fn test_update_refcount() { + let path = "/tmp/refcount_case3.qcow2"; + let image_bits = 30; + let cluster_bits = 16; + let (qcow2, _) = create_qcow2_driver(path, image_bits, cluster_bits); + let mut refcount = qcow2.refcount.clone(); + + // Add refcount for the first cluster. + let ret = refcount.update_refcount(0, 1, true, 1); + assert!(ret.is_ok()); + + // Test invalid cluster offset. + let ret = refcount.update_refcount(1 << 63, 1, true, 1); + if let Err(err) = ret { + // 16 bit is cluster bits, 15 is refcount block bits. + let err_msg = format!("Invalid refcount table index {}", 1_u64 << (63 - 15 - 16)); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + + // Test refcount block not in cache. + let ret = refcount.update_refcount(1 << (cluster_bits * 2), 1, true, 1); + if let Err(err) = ret { + let err_msg = format!("Invalid refcount block address 0x0, index is 2"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + + remove_file(path).unwrap(); + } + + #[test] + fn test_set_refcount() { + let path = "/tmp/refcount_case4.qcow2"; + let image_bits = 30; + let cluster_bits = 16; + let (qcow2, _) = create_qcow2_driver(path, image_bits, cluster_bits); + let mut refcount = qcow2.refcount.clone(); + + // Add refcount for the first cluster. + let ret = refcount.set_refcount(0, 0, 1, true, 1); + assert!(ret.is_ok()); + + // Test refcount overflow. + let ret = refcount.set_refcount(0, 0, 1, true, 65535); + if let Err(err) = ret { + let err_msg = format!("Refcount 2 add 65535 cause overflows, index is 0"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + + // Test refcount underflow. + let ret = refcount.set_refcount(0, 0, 1, false, 65535); + if let Err(err) = ret { + let err_msg = format!("Refcount 2 sub 65535 cause overflows, index is 0"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + + remove_file(path).unwrap(); + } + + #[test] + fn test_find_free_cluster() { + let path = "/tmp/refcount_case5.qcow2"; + let image_bits = 30; + let cluster_bits = 16; + let cluster_sz = 1 << 16; + let (qcow2, _) = create_qcow2_driver(path, image_bits, cluster_bits); + let mut refcount = qcow2.refcount.clone(); + let mut header = qcow2.header.clone(); + + // Test find 1 free cluster. + let ret = refcount.find_free_cluster(&mut header, cluster_sz); + assert!(ret.is_ok()); + + // Test find 10 continuous free cluster. + let ret = refcount.find_free_cluster(&mut header, 10 * cluster_sz); + assert!(ret.is_ok()); + + // Test invalid refcount table entry. + refcount.refcount_table[0] |= 0x1; + let ret = refcount.find_free_cluster(&mut header, 1 << cluster_bits); + if let Err(err) = ret { + let err_msg = format!("Invalid refcount block address 0x20001, index is 0"); + assert_eq!(err.to_string(), err_msg); + } else { + assert!(false); + } + refcount.refcount_table[0] &= !0x1; + + remove_file(path).unwrap(); + } +} -- Gitee From f3699859dd05c3b33868cdef6204a68c7c775f02 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 5 Jul 2023 10:34:33 +0800 Subject: [PATCH 1184/2187] qcow2: add docs Signed-off-by: zhouli57 --- docs/config_guidebook.md | 4 +--- docs/qmp.md | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 50b78c836..1c9fa1fd3 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -296,7 +296,7 @@ fourteen properties are supported for virtio block device. * discard: free up unused disk space. (optional) `unmap/ignore` means `on/off`. If not set, default is `ignore`. * detect-zeroes: optimize writing zeroes to disk space. (optional) `unmap` means it can free up disk space when discard is `unmap`. If discard is `ignore`, `unmap` of detect-zeroes is same as `on`. If not set, default is `off`. * if: drive type, for block drive, it should be `none`. (optional) If not set, default is `none`. -* format: the format of block image. (optional) If not set, default is `raw`. NB: currently only `raw` is supported. +* format: the format of block image. (optional) Possible values are `raw` or `qcow2`. If not set, default is `raw`. NB: currently only `raw` is supported for microvm. * num-queues: the optional num-queues attribute controls the number of queues to be used for block device. (optional) The max queues number supported is 32. If not set, the default block queue number is the smaller one of vCPU count and the max queues number (e.g, min(vcpu_count, 32)). * bootindex: the boot order of block device. (optional) If not set, the priority is lowest. The number ranges from 0 to 255, the smaller the number, the higher the priority. @@ -882,8 +882,6 @@ Six properties can be set for Virtio-Scsi controller. ### 2.15 Virtio Scsi HardDisk Virtio Scsi HardDisk is a virtual block device, which process read and write requests in virtio queue from guest. -Note: Only support using raw image file as backend now. - Ten properties can be set for virtio-scsi hd. * file: the path of backend image file. diff --git a/docs/qmp.md b/docs/qmp.md index 326817977..10870f790 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -64,6 +64,7 @@ Add a block backend. * `file` : the backend file information. * `cache` : if use direct io. * `read-only` : if readonly. +* `driver` : the block image format. Possible values are `raw` or `qcow2`. If not set, default is `raw`. #### Notes @@ -74,6 +75,8 @@ Add a block backend. * For `addr`, it start at `0x0` mapping in guest with `vda` on x86_64 platform, and start at `0x1` mapping in guest with `vdb` on aarch64 platform. + * For `driver`, only `raw` is supported. + #### Example ```json -- Gitee From 5673b2248d83e5e996094f2d2c78bec9385f189a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 4 Jul 2023 21:50:19 +0800 Subject: [PATCH 1185/2187] camera: enhance descriptor 1. Dynamically calculating the bit rate of the frame. 2. Filtering unsupported frame. 3. Dynamically generate the bmaControls field in the input header. 4. Increment the frame index for different frame descriptors. Signed-off-by: zhouli57 --- devices/src/camera_backend/mod.rs | 9 +++++++++ devices/src/camera_backend/v4l2.rs | 14 ++++++++++---- devices/src/usb/camera.rs | 23 +++++++++++------------ 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 1d6b34105..73e12784c 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -112,6 +112,15 @@ pub fn get_video_frame_size(width: u32, height: u32) -> Result { .with_context(|| format!("Invalid width {} or height {}", width, height)) } +pub fn get_bit_rate(width: u32, height: u32, interval: u32) -> Result { + let fm_size = get_video_frame_size(width, height)?; + let size_in_bit = fm_size as u64 * INTERVALS_PER_SEC as u64 * 8; + let rate = size_in_bit + .checked_div(interval as u64) + .with_context(|| format!("Invalid size {} or interval {}", size_in_bit, interval))?; + Ok(rate as u32) +} + #[macro_export] macro_rules! video_fourcc { ($a:expr, $b:expr, $c:expr, $d:expr) => { diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 03ff3b654..ffa5d7f92 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -22,7 +22,7 @@ use v4l2_sys_mit::{ v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE, v4l2_buffer, v4l2_fmtdesc, v4l2_format, v4l2_frmivalenum, v4l2_frmsizeenum, v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE, v4l2_memory_V4L2_MEMORY_MMAP, v4l2_requestbuffers, v4l2_streamparm, V4L2_CAP_STREAMING, - V4L2_CAP_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_CAPTURE, V4L2_FMT_FLAG_EMULATED, }; use vmm_sys_util::epoll::EventSet; @@ -174,7 +174,7 @@ impl V4l2CameraBackend { break; } // NOTE: Only support discrete now. - if (frmsize.type_) != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { + if frmsize.type_ != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { continue; } let width = unsafe { frmsize.__bindgen_anon_1.discrete.width }; @@ -187,8 +187,8 @@ impl V4l2CameraBackend { interval, index: frm_idx, }); + frm_idx += 1; } - frm_idx += 1; } Ok(list) } @@ -207,6 +207,10 @@ impl V4l2CameraBackend { if interval_end { break; } + // NOTE: Only support discrete now. + if frame_val.type_ != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { + continue; + } let numerator = unsafe { frame_val.__bindgen_anon_1.discrete.numerator }; let denominator = unsafe { frame_val.__bindgen_anon_1.discrete.denominator }; if denominator == 0 { @@ -323,7 +327,9 @@ impl CameraHostdevOps for V4l2CameraBackend { if format_end { break; } - if !self.is_pixfmt_supported(desc.pixelformat) { + if desc.flags & V4L2_FMT_FLAG_EMULATED != 0 + || !self.is_pixfmt_supported(desc.pixelformat) + { continue; } list.push(CameraFormatList { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index bbe0d2589..1441244b6 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -36,8 +36,8 @@ use util::loop_context::{ use super::camera_media_type_guid::MEDIA_TYPE_GUID_HASHMAP; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{ - camera_ops, get_video_frame_size, CamBasicFmt, CameraBrokenCallback, CameraFormatList, - CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, + camera_ops, get_bit_rate, get_video_frame_size, CamBasicFmt, CameraBrokenCallback, + CameraFormatList, CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, }; use crate::usb::config::*; use crate::usb::descriptor::*; @@ -394,7 +394,6 @@ struct VsDescInputHeader { bTriggerSupport: u8, bTriggerUsage: u8, bControlSize: u8, - bmaControls: u16, } impl ByteCode for VsDescInputHeader {} @@ -560,7 +559,7 @@ fn gen_desc_device_camera(fmt_list: Vec) -> Result) -> Result header_struct.wTotalLength = header_struct.bLength as u16 + body.clone().iter().fold(0, |len, x| len + x.data.len()) as u16; - buf.push(Arc::new(UsbDescOther { - data: header_struct.as_bytes().to_vec(), - })); + let mut vec = header_struct.as_bytes().to_vec(); + vec.resize(header_struct.bLength as usize, 0); + buf.push(Arc::new(UsbDescOther { data: vec })); buf.append(&mut body); Ok(buf) @@ -1162,7 +1161,7 @@ fn gen_fmt_desc(fmt_list: Vec) -> Result fn gen_intface_header_desc(fmt_num: u8) -> VsDescInputHeader { VsDescInputHeader { - bLength: 0xf, + bLength: 0xd + fmt_num, bDescriptorType: CS_INTERFACE, bDescriptorSubtype: VS_INPUT_HEADER, bNumFormats: fmt_num, @@ -1174,7 +1173,6 @@ fn gen_intface_header_desc(fmt_num: u8) -> VsDescInputHeader { bTriggerSupport: 0x00, bTriggerUsage: 0x00, bControlSize: 0x01, - bmaControls: 0x00, } } @@ -1219,6 +1217,7 @@ fn gen_fmt_header(fmt: &CameraFormatList) -> Result> { } fn gen_frm_desc(pixfmt: FmtType, frm: &CameraFrame) -> Result> { + let bitrate = get_bit_rate(frm.width, frm.height, frm.interval)?; let desc = VsDescFrm { bLength: 0x1e, // TODO: vary with interval number. bDescriptorType: CS_INTERFACE, @@ -1227,11 +1226,11 @@ fn gen_frm_desc(pixfmt: FmtType, frm: &CameraFrame) -> Result> { FmtType::Mjpg => VS_FRAME_MJPEG, }, bFrameIndex: frm.index, - bmCapabilities: 0x00, + bmCapabilities: 0x1, wWidth: frm.width as u16, wHeight: frm.height as u16, - dwMinBitRate: 442368000, - dwMaxBitRate: 442368000, + dwMinBitRate: bitrate, + dwMaxBitRate: bitrate, dwMaxVideoFrameBufSize: get_video_frame_size(frm.width, frm.height)?, dwDefaultFrameInterval: frm.interval, bFrameIntervalType: 1, -- Gitee From 6cded3b17be93dbbab0f30c43592747a177cc65d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 26 Jun 2023 08:37:02 +0800 Subject: [PATCH 1186/2187] time: add function to get wall time and format unix time Add function gettime() to get wall time. Add function get_format_time() to convert unix time to Year/Month/Day/Hour/Minute/Second format. Signed-off-by: liuxiangdong --- util/src/logger.rs | 30 ++++++++++++------------------ util/src/time.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/util/src/logger.rs b/util/src/logger.rs index a58c485fa..80629a608 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -13,30 +13,24 @@ use std::io::prelude::*; use std::sync::Mutex; -use crate::unix::gettid; use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; -fn format_now() -> String { - let mut ts = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; +use crate::time::{get_format_time, gettime}; +use crate::unix::gettid; - let mut ti: libc::tm = unsafe { std::mem::zeroed() }; - unsafe { - libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts); - libc::localtime_r(&ts.tv_sec, &mut ti); - } +fn format_now() -> String { + let (sec, nsec) = gettime(); + let format_time = get_format_time(sec as i64); format!( "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}", - ti.tm_year + 1900, - ti.tm_mon + 1, - ti.tm_mday, - ti.tm_hour, - ti.tm_min, - ti.tm_sec, - ts.tv_nsec + format_time[0], + format_time[1], + format_time[2], + format_time[3], + format_time[4], + format_time[5], + nsec ) } diff --git a/util/src/time.rs b/util/src/time.rs index ad391ab44..7c8f5c0b9 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -28,3 +28,34 @@ pub fn mktime64(year: u64, mon: u64, day: u64, hour: u64, min: u64, sec: u64) -> * 60 + sec } + +/// Get wall time. +pub fn gettime() -> (u32, u32) { + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + + unsafe { + libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts); + } + + (ts.tv_sec as u32, ts.tv_nsec as u32) +} + +/// Convert wall time to year/month/day/hour/minute/second format. +pub fn get_format_time(sec: i64) -> [i32; 6] { + let mut ti: libc::tm = unsafe { std::mem::zeroed() }; + unsafe { + libc::localtime_r(&sec, &mut ti); + } + + [ + ti.tm_year + 1900, + ti.tm_mon + 1, + ti.tm_mday, + ti.tm_hour, + ti.tm_min, + ti.tm_sec, + ] +} -- Gitee From eeaf96195236bd44eab9effac47d750f69c81003 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 26 Jun 2023 11:45:32 +0800 Subject: [PATCH 1187/2187] config: record drive id in DriveFile Record drive id in DriveFile. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 10 ++++++++-- machine/src/micro_vm/mod.rs | 3 ++- machine/src/standard_vm/mod.rs | 10 +++++++--- machine_manager/src/config/drive.rs | 2 ++ machine_manager/src/config/mod.rs | 12 ++++++++++++ virtio/src/device/block.rs | 2 ++ 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5b71970c0..fc5ea3ae2 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1598,10 +1598,16 @@ pub trait MachineOps { } /// Register a new drive backend file. - fn register_drive_file(&self, path: &str, read_only: bool, direct: bool) -> Result<()> { + fn register_drive_file( + &self, + id: &str, + path: &str, + read_only: bool, + direct: bool, + ) -> Result<()> { let files = self.get_drive_files(); let mut drive_files = files.lock().unwrap(); - VmConfig::add_drive_file(&mut drive_files, path, read_only, direct)?; + VmConfig::add_drive_file(&mut drive_files, id, path, read_only, direct)?; // Lock the added file if VM is running. let drive_file = drive_files.get_mut(path).unwrap(); diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index fb2f80477..d35e3cf74 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1188,7 +1188,8 @@ impl DeviceInterface for LightMachine { ); } // Register drive backend file for hotplugged drive. - if let Err(e) = self.register_drive_file(&args.file.filename, read_only, direct) { + if let Err(e) = self.register_drive_file(&config.id, &args.file.filename, read_only, direct) + { error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index e80ae95f1..65a579fa6 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1446,9 +1446,12 @@ impl DeviceInterface for StdMachine { ); } // Register drive backend file for hotplug drive. - if let Err(e) = - self.register_drive_file(&args.file.filename, config.read_only, config.direct) - { + if let Err(e) = self.register_drive_file( + &config.id, + &args.file.filename, + config.read_only, + config.direct, + ) { error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), @@ -1822,6 +1825,7 @@ impl DeviceInterface for StdMachine { } }; if let Err(e) = self.register_drive_file( + &drive_cfg.id, &drive_cfg.path_on_host, drive_cfg.read_only, drive_cfg.direct, diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index e15e9b9e9..1b05901d8 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -37,6 +37,8 @@ const MAX_QUEUE_SIZE_BLK: u16 = 1024; /// Represent a single drive backend file. pub struct DriveFile { + /// Drive id. + pub id: String, /// The opened file. pub file: File, /// The num of drives share same file. diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 075eab18a..cc677dfa9 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -276,6 +276,7 @@ impl VmConfig { /// Add a file to drive file store. pub fn add_drive_file( drive_files: &mut HashMap, + id: &str, path: &str, read_only: bool, direct: bool, @@ -302,6 +303,7 @@ impl VmConfig { ); } let drive_file = DriveFile { + id: id.to_string(), file, count: 1, read_only, @@ -344,6 +346,14 @@ impl VmConfig { } } + /// Get drive id from drive file store. + pub fn get_drive_id(drive_files: &HashMap, path: &str) -> Result { + match drive_files.get(path) { + Some(drive_file) => Ok(drive_file.id.clone()), + None => Err(anyhow!("The file {} is not in drive backend", path)), + } + } + /// Get alignment requirement from drive file store. pub fn fetch_drive_align( drive_files: &HashMap, @@ -361,6 +371,7 @@ impl VmConfig { for drive in self.drives.values() { Self::add_drive_file( &mut drive_files, + &drive.id, &drive.path_on_host, drive.read_only, drive.direct, @@ -370,6 +381,7 @@ impl VmConfig { for pflash in pflashs { Self::add_drive_file( &mut drive_files, + "", &pflash.path_on_host, pflash.read_only, false, diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 4777fcd2e..23813aa8b 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1409,6 +1409,7 @@ mod tests { block.blk_cfg.path_on_host = f.as_path().to_str().unwrap().to_string(); VmConfig::add_drive_file( &mut block.drive_files.lock().unwrap(), + "", &block.blk_cfg.path_on_host, block.blk_cfg.read_only, block.blk_cfg.direct, @@ -1547,6 +1548,7 @@ mod tests { VmConfig::add_drive_file( &mut block.drive_files.lock().unwrap(), + "", &block.blk_cfg.path_on_host, block.blk_cfg.read_only, block.blk_cfg.direct, -- Gitee From fec741dd1ae2cadb0b17dfdacda5aed3c549b33d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 7 Jul 2023 13:18:37 +0800 Subject: [PATCH 1188/2187] snapshot: support qcow2 internal snapshot Support qcow2 internal snapshot. Signed-off-by: liuxiangdong Signed-off-by: Yan Wang --- Cargo.lock | 1 + block_backend/Cargo.toml | 1 + block_backend/src/lib.rs | 10 +- block_backend/src/qcow2/cache.rs | 4 + block_backend/src/qcow2/mod.rs | 710 +++++++++++++++++++++++++- block_backend/src/qcow2/refcount.rs | 73 ++- block_backend/src/qcow2/snapshot.rs | 302 +++++++++++ block_backend/src/qcow2/table.rs | 10 +- devices/src/scsi/disk.rs | 3 +- machine_manager/src/qmp/qmp_schema.rs | 18 + virtio/src/device/block.rs | 3 +- 11 files changed, 1088 insertions(+), 47 deletions(-) create mode 100644 block_backend/src/qcow2/snapshot.rs diff --git a/Cargo.lock b/Cargo.lock index 161650026..7c206ca54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,6 +194,7 @@ dependencies = [ "libc", "log", "machine_manager", + "once_cell", "thiserror", "util", "vmm-sys-util", diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index b5ed3db6a..15d4a089a 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -11,6 +11,7 @@ vmm-sys-util = "0.11.0" anyhow = "1.0" log = "0.4" byteorder = "1.4.3" +once_cell = "1.13.0" libc = "0.2" machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index e117db3b1..3c83e4b29 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -22,7 +22,7 @@ use std::{ use anyhow::{bail, Context, Result}; use machine_manager::config::DiskFormat; -use qcow2::Qcow2Driver; +use qcow2::{Qcow2Driver, QCOW2_LIST}; use raw::RawDriver; use util::aio::{Aio, Iovec, WriteZeroesState}; @@ -75,6 +75,7 @@ pub trait BlockDriverOps: Send { pub fn create_block_backend( file: File, aio: Aio, + drive_id: String, prop: BlockProperty, ) -> Result>>> { match prop.format { @@ -96,7 +97,12 @@ pub fn create_block_backend( prop.req_align ); } - Ok(Arc::new(Mutex::new(qcow2))) + let new_qcow2 = Arc::new(Mutex::new(qcow2)); + QCOW2_LIST + .lock() + .unwrap() + .insert(drive_id, new_qcow2.clone()); + Ok(new_qcow2) } } } diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 34b23ab00..3971b9176 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -94,6 +94,10 @@ impl CacheTable { Ok(v) } + pub fn get_entry_num(&self) -> usize { + self.table_data.len() / self.entry_size + } + #[inline(always)] pub fn get_entry_map(&mut self, idx: usize) -> Result { self.be_read(idx) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index ea3635014..8d2617845 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -13,20 +13,23 @@ mod cache; mod header; mod refcount; +mod snapshot; mod table; use std::{ cell::RefCell, + collections::HashMap, fs::File, mem::size_of, os::unix::io::{AsRawFd, RawFd}, rc::Rc, - sync::{atomic::AtomicBool, Arc}, + sync::{atomic::AtomicBool, Arc, Mutex}, }; use anyhow::{bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; -use log::error; +use log::{debug, error, info}; +use once_cell::sync::Lazy; use self::cache::ENTRY_SIZE_U64; use crate::{ @@ -35,25 +38,53 @@ use crate::{ cache::CacheTable, header::QcowHeader, refcount::RefCount, + snapshot::{InternalSnapshot, QcowSnapshot, QcowSnapshotExtraData, QCOW2_MAX_SNAPSHOTS}, table::{Qcow2ClusterType, Qcow2Table}, }, BlockDriverOps, BlockIoErrorCallback, BlockProperty, }; -use util::aio::{ - get_iov_size, iov_from_buf_direct, iovecs_split, Aio, AioCb, AioEngine, Iovec, OpCode, +use machine_manager::qmp::qmp_schema::SnapshotInfo; +use util::{ + aio::{get_iov_size, iov_from_buf_direct, iovecs_split, Aio, AioCb, AioEngine, Iovec, OpCode}, + num_ops::{div_round_up, round_down, round_up}, + time::{get_format_time, gettime}, }; -use util::num_ops::{round_down, round_up}; // The L1/L2/Refcount table entry size. const ENTRY_SIZE: u64 = 1 << ENTRY_BITS; const ENTRY_BITS: u64 = 3; const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; +const REFCOUNT_TABLE_OFFSET_MASK: u64 = 0xffff_ffff_ffff_fe00; const QCOW2_OFLAG_ZERO: u64 = 1 << 0; const QCOW2_OFFSET_COMPRESSED: u64 = 1 << 62; const QCOW2_OFFSET_COPIED: u64 = 1 << 63; const DEFAULT_SECTOR_SIZE: u64 = 512; +const METADATA_OVERLAP_CHECK_MAINHEADER: u64 = 1 << 0; +const METADATA_OVERLAP_CHECK_ACTIVEL1: u64 = 1 << 1; +const METADATA_OVERLAP_CHECK_ACTIVEL2: u64 = 1 << 2; +const METADATA_OVERLAP_CHECK_REFCOUNTTABLE: u64 = 1 << 3; +const METADATA_OVERLAP_CHECK_REFCOUNTBLOCK: u64 = 1 << 4; +const METADATA_OVERLAP_CHECK_SNAPSHOTTABLE: u64 = 1 << 5; +const METADATA_OVERLAP_CHECK_INACTIVEL1: u64 = 1 << 6; +#[allow(unused)] +const METADATA_OVERLAP_CHECK_INACTIVEL2: u64 = 1 << 7; +#[allow(unused)] +const METADATA_OVERLAP_CHECK_BITMAPDIRECTORY: u64 = 1 << 8; + +const DEFAULT_QCOW2_METADATA_OVERLAP_CHECK: u64 = METADATA_OVERLAP_CHECK_MAINHEADER + | METADATA_OVERLAP_CHECK_ACTIVEL1 + | METADATA_OVERLAP_CHECK_ACTIVEL2 + | METADATA_OVERLAP_CHECK_REFCOUNTTABLE + | METADATA_OVERLAP_CHECK_REFCOUNTBLOCK + | METADATA_OVERLAP_CHECK_SNAPSHOTTABLE + | METADATA_OVERLAP_CHECK_INACTIVEL1; + +type Qcow2ListType = Lazy>>>>>; +/// Record the correspondence between disk drive ID and the qcow2 struct. +pub static QCOW2_LIST: Qcow2ListType = Lazy::new(|| Arc::new(Mutex::new(HashMap::new()))); + pub enum HostOffset { DataNotInit, DataAddress(u64), @@ -157,6 +188,7 @@ pub struct Qcow2Driver { header: QcowHeader, table: Qcow2Table, refcount: RefCount, + snapshot: InternalSnapshot, } impl Drop for Qcow2Driver { @@ -175,7 +207,8 @@ impl Qcow2Driver { sync_aio: sync_aio.clone(), header: QcowHeader::default(), table: Qcow2Table::new(sync_aio.clone()), - refcount: RefCount::new(sync_aio), + refcount: RefCount::new(sync_aio.clone()), + snapshot: InternalSnapshot::new(sync_aio), }; qcow2 .load_header() @@ -189,6 +222,12 @@ impl Qcow2Driver { qcow2 .load_refcount_table() .with_context(|| "Failed to load refcount table")?; + qcow2.snapshot.set_cluster_size(qcow2.header.cluster_size()); + qcow2 + .snapshot + .load_snapshot_table(qcow2.header.snapshots_offset, qcow2.header.nb_snapshots) + .with_context(|| "Failed to load snapshot table")?; + Ok(qcow2) } @@ -236,7 +275,7 @@ impl Qcow2Driver { cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; } else { - let l2_cluster = self.load_l2_cluster(l2_address)?; + let l2_cluster = self.load_cluster(l2_address)?; let l2_table = Rc::new(RefCell::new(CacheTable::new( l2_address, l2_cluster, @@ -267,6 +306,17 @@ impl Qcow2Driver { let new_addr = self.alloc_cluster(1, true)?; l2_entry = new_addr | QCOW2_OFFSET_COPIED; cluster_addr = new_addr & L2_TABLE_OFFSET_MASK; + } else if l2_entry & QCOW2_OFFSET_COPIED == 0 { + // Copy on write for data cluster. + let new_data_addr = self.alloc_cluster(1, false)?; + let data = self.load_cluster(cluster_addr)?; + self.sync_aio + .borrow_mut() + .write_buffer(new_data_addr, &data)?; + self.refcount + .update_refcount(cluster_addr, 1, false, 1, true)?; + l2_entry = new_data_addr | QCOW2_OFFSET_COPIED; + cluster_addr = new_data_addr & L2_TABLE_OFFSET_MASK; } l2_table .borrow_mut() @@ -298,15 +348,28 @@ impl Qcow2Driver { } if l1_entry & QCOW2_OFFSET_COPIED == 0 { - // Step 1: Alloc a new l2_table. + // Alloc a new l2_table. let old_l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; let new_l2_offset = self.alloc_cluster(1, true)?; - // Step 2: Update l1_table and l2 table cache. - self.table.update_l1_table( - l1_index as usize, - new_l2_offset | QCOW2_OFFSET_COPIED, - &self.header, - )?; + let l2_cluster = if old_l2_offset != 0 { + self.load_cluster(l2_address)? + } else { + vec![0_u8; self.header.cluster_size() as usize] + }; + self.sync_aio + .borrow_mut() + .write_buffer(new_l2_offset, &l2_cluster)?; + let l2_cache_entry = Rc::new(RefCell::new(CacheTable::new( + new_l2_offset, + l2_cluster, + ENTRY_SIZE_U64, + )?)); + self.table.update_l2_table(l2_cache_entry)?; + + // Update l1_table and l2 table cache. + self.table + .update_l1_table(l1_index as usize, new_l2_offset | QCOW2_OFFSET_COPIED); + self.table.save_l1_table(&self.header)?; let zero_cluster: Vec = vec![0_u8; self.header.cluster_size() as usize]; let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( new_l2_offset, @@ -314,11 +377,13 @@ impl Qcow2Driver { ENTRY_SIZE_U64, )?)); self.table.update_l2_table(l2_table_entry)?; - // Step 3: Decrease the refcount of the old table. + + // Decrease the refcount of the old table. if old_l2_offset != 0 { - self.refcount.update_refcount(old_l2_offset, 1, false, 1)?; + self.refcount + .update_refcount(old_l2_offset, 1, false, 1, true)?; } - // Step 4. Get the offset of the newly-allocated l2 table. + // Get the offset of the newly-allocated l2 table. l2_address = new_l2_offset; } @@ -327,7 +392,7 @@ impl Qcow2Driver { return Ok(entry.clone()); } // Cache miss. - let l2_cluster = self.load_l2_cluster(l2_address)?; + let l2_cluster = self.load_cluster(l2_address)?; let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( l2_address, l2_cluster, @@ -341,9 +406,9 @@ impl Qcow2Driver { guest_offset & (self.header.cluster_size() - 1) } - fn load_l2_cluster(&mut self, addr: u64) -> Result> { + fn load_cluster(&mut self, addr: u64) -> Result> { if !is_aligned(self.header.cluster_size(), addr) { - bail!("L2 cluster address not aligned {}", addr); + bail!("Cluster address not aligned {}", addr); } let mut buf = vec![0_u8; self.header.cluster_size() as usize]; self.sync_aio.borrow_mut().read_buffer(addr, &mut buf)?; @@ -383,6 +448,610 @@ impl Qcow2Driver { } Ok(()) } + + fn free_cluster(&mut self, addr: u64, clusters: u64) -> Result<()> { + self.refcount + .update_refcount(addr, clusters, false, 1, true) + } + + fn get_snapshot_by_name(&mut self, name: &String) -> i32 { + self.snapshot.find_snapshot(name) + } + + fn qcow2_delete_snapshot(&mut self, name: String) -> Result { + let snapshot_idx = self.get_snapshot_by_name(&name); + if snapshot_idx < 0 { + bail!("Snapshot with name {} does not exist", name); + } + + // Alloc new snapshot table. Delete the new snapshot from the snapshot table + // and write new snapshot table to file. + let cluster_size = self.header.cluster_size(); + let snap = self.snapshot.del_snapshot(snapshot_idx as usize); + let new_snapshots_table_clusters = + bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); + let l1_table_clusters = + bytes_to_clusters(snap.l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); + let new_snapshot_table_offset: u64; + let mut err_msg: String = "".to_string(); + let mut error_stage = 0; + + // Using loop for error handling. + #[allow(clippy::never_loop)] + loop { + new_snapshot_table_offset = self + .alloc_cluster(new_snapshots_table_clusters, true) + .unwrap_or_else(|e| { + err_msg = format!("{:?}", e); + error_stage = 1; + 0 + }); + if new_snapshot_table_offset == 0 { + break; + } + + if let Err(e) = self.snapshot.save_snapshot_table(new_snapshot_table_offset) { + err_msg = format!("{:?}", e); + error_stage = 2; + break; + } + + // Decrease the refcounts of clusters referenced by the snapshot. + if let Err(e) = self.qcow2_update_snapshot_refcount(snap.l1_table_offset, -1) { + err_msg = format!("{:?}", e); + error_stage = 2; + break; + } + + // Free the snaphshot L1 table. + if let Err(e) = self.refcount.update_refcount( + snap.l1_table_offset, + l1_table_clusters, + false, + 1, + false, + ) { + err_msg = format!("{:?}", e); + error_stage = 3; + break; + } + + // Update the copied flag on the current cluster offsets. + if let Err(e) = self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0) { + err_msg = format!("{:?}", e); + error_stage = 4; + break; + } + + if let Err(e) = + self.refcount + .update_refcount(self.header.snapshots_offset, 1, false, 1, true) + { + err_msg = format!("{:?}", e); + error_stage = 5; + break; + } + + if let Err(e) = self.refcount.flush_refcount_block_cache() { + err_msg = format!("{:?}", e); + error_stage = 5; + break; + } + + let mut new_header = self.header.clone(); + new_header.snapshots_offset = new_snapshot_table_offset; + new_header.nb_snapshots -= 1; + if let Err(e) = self + .sync_aio + .borrow_mut() + .write_buffer(0, &new_header.to_vec()) + { + err_msg = format!("{:?}", e); + error_stage = 6; + break; + } + self.header.snapshots_offset = new_header.snapshots_offset; + self.header.nb_snapshots = new_header.nb_snapshots; + + return Ok(SnapshotInfo { + id: snap.id.to_string(), + name: snap.name.clone(), + vm_state_size: snap.vm_state_size as u64, + date_sec: snap.date_sec, + date_nsec: snap.date_nsec, + vm_clock_nsec: snap.vm_clock_nsec, + icount: snap.icount, + }); + } + + // Error handling, to revert some operation. + if error_stage >= 6 { + self.refcount + .update_refcount(self.header.snapshots_offset, 1, true, 1, true)?; + } + if error_stage >= 4 { + self.refcount.update_refcount( + snap.l1_table_offset, + l1_table_clusters, + true, + 1, + true, + )?; + } + if error_stage >= 3 { + self.qcow2_update_snapshot_refcount(snap.l1_table_offset, 1)?; + if error_stage >= 5 { + self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; + } + } + if error_stage >= 2 { + self.free_cluster(new_snapshot_table_offset, new_snapshots_table_clusters)?; + } + if error_stage >= 1 { + self.snapshot.insert_snapshot(snap, snapshot_idx as usize) + } + + bail!("{}", err_msg); + } + + fn qcow2_create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()> { + if self.get_snapshot_by_name(&name) >= 0 { + bail!("Snapshot {} exists!", name); + } + if self.snapshot.snapshots_number() > QCOW2_MAX_SNAPSHOTS { + bail!( + "The number of snapshots exceed the maximum limit {}", + QCOW2_MAX_SNAPSHOTS + ); + } + + // Alloc cluster and copy L1 table for snapshot. + let l1_table_len = self.header.l1_size as u64 * ENTRY_SIZE; + let cluster_size = self.header.cluster_size(); + let l1_table_clusters = bytes_to_clusters(l1_table_len, cluster_size).unwrap(); + let new_l1_table_offset = self.alloc_cluster(l1_table_clusters, true)?; + + let old_snapshots_table_len = self.snapshot.snapshot_size; + let old_snapshots_table_clusters = + bytes_to_clusters(old_snapshots_table_len, cluster_size).unwrap(); + let mut err_msg: String = "".to_string(); + let mut new_snapshot_table_offset = 0_u64; + let mut error_stage = 0; + + // Using loop for error handling. + #[allow(clippy::never_loop)] + loop { + // Check if l1 table offset is overlap. + if self.qcow2_pre_write_overlap_check(0, new_l1_table_offset, l1_table_len) != 0 { + err_msg = format!( + "Allocated snapshot L1 table addr {:x} is illegal!", + new_l1_table_offset + ); + error_stage = 1; + break; + } + + // Write L1 table to file. + if let Err(e) = self + .sync_aio + .borrow_mut() + .write_ctrl_cluster(new_l1_table_offset, &self.table.l1_table) + { + error_stage = 1; + err_msg = format!("{:?}", e); + break; + } + + // Increase the refcounts of all clusters searched by L1 table. + if let Err(e) = self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 1) { + error_stage = 1; + err_msg = format!("{:?}", e); + break; + } + + // Alloc new snapshot table. + let (date_sec, date_nsec) = gettime(); + let snap = QcowSnapshot { + l1_table_offset: new_l1_table_offset, + l1_size: self.header.l1_size, + id: self.snapshot.find_new_snapshot_id(), + name, + disk_size: self.virtual_disk_size(), + vm_state_size: 0, + date_sec, + date_nsec, + vm_clock_nsec, + icount: u64::MAX, + extra_data_size: size_of::() as u32, + }; + let new_snapshots_table_clusters = + bytes_to_clusters(old_snapshots_table_len + snap.get_size(), cluster_size).unwrap(); + new_snapshot_table_offset = self + .alloc_cluster(new_snapshots_table_clusters, true) + .unwrap_or_else(|e| { + error_stage = 2; + err_msg = format!("{:?}", e); + 0 + }); + if new_snapshot_table_offset == 0 { + break; + } + info!( + "Snapshot table offset: old(0x{:x}) -> new(0x{:x})", + self.header.snapshots_offset, new_snapshot_table_offset, + ); + + // Append the new snapshot to the snapshot table and write new snapshot table to file. + self.snapshot.add_snapshot(snap); + if let Err(e) = self.snapshot.save_snapshot_table(new_snapshot_table_offset) { + error_stage = 3; + err_msg = format!("{:?}", e); + break; + } + self.snapshot.snapshot_table_offset = new_snapshot_table_offset; + + // Free the old snapshot table cluster if snapshot exists. + if self.header.snapshots_offset != 0 { + if let Err(e) = self.refcount.update_refcount( + self.header.snapshots_offset, + old_snapshots_table_clusters, + false, + 1, + true, + ) { + error_stage = 4; + err_msg = format!("{:?}", e); + break; + } + } + + // Update snapshot offset and num in qcow2 header. + let mut new_header = self.header.clone(); + new_header.snapshots_offset = new_snapshot_table_offset; + new_header.nb_snapshots += 1; + if let Err(e) = self + .sync_aio + .borrow_mut() + .write_buffer(0, &new_header.to_vec()) + { + error_stage = 5; + err_msg = format!("{:?}", e); + break; + } + self.header.snapshots_offset = new_header.snapshots_offset; + self.header.nb_snapshots = new_header.nb_snapshots; + + return Ok(()); + } + + // Error handling, to revert some operation. + if error_stage >= 5 && self.header.snapshots_offset != 0 { + self.refcount.update_refcount( + self.header.snapshots_offset, + old_snapshots_table_clusters, + true, + 1, + true, + )?; + } + + if error_stage >= 4 { + self.snapshot.snapshot_table_offset = self.header.snapshots_offset; + } + if error_stage >= 3 { + // Delete new added snapshot. + let num_clusters = + bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); + self.snapshot + .del_snapshot(self.snapshot.snapshots_number() - 1); + self.free_cluster(new_snapshot_table_offset, num_clusters)?; + } + if error_stage >= 2 { + self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, -1)?; + } + if error_stage >= 1 { + self.free_cluster(new_l1_table_offset, l1_table_clusters)?; + } + + bail!("{}", err_msg); + } + + /// Update the refcounts of all clusters searched by l1_table_offset. + fn qcow2_update_snapshot_refcount(&mut self, l1_table_offset: u64, added: i32) -> Result<()> { + let l1_table_size = self.header.l1_size as usize; + let mut l1_table = self.table.l1_table.clone(); + let mut l1_changed = false; + debug!( + "Update snapshot refcount: l1 table offset {:x}, active header l1 table addr {:x}, add {}", + l1_table_offset, + self.header.l1_table_offset, + added + ); + + if l1_table_offset != self.header.l1_table_offset { + // Read snapshot l1 table from qcow2 file. + l1_table = self + .sync_aio + .borrow_mut() + .read_ctrl_cluster(l1_table_offset, l1_table_size as u64)?; + } + + let is_add = added > 0; + let num = added.unsigned_abs() as u16; + let mut old_l2_table_offset: u64; + for (i, l1_entry) in l1_table.iter_mut().enumerate().take(l1_table_size) { + let mut l2_table_offset = *l1_entry; + log::info!("Develop: l1 table idx {}, addr {:x}", i, l2_table_offset); + if l2_table_offset == 0 { + // No l2 table. + continue; + } + old_l2_table_offset = l2_table_offset; + l2_table_offset &= L1_TABLE_OFFSET_MASK; + + if self.refcount.offset_into_cluster(l2_table_offset) != 0 { + bail!( + "L2 table offset {:x} unaligned (L1 index {})!", + l2_table_offset, + i + ); + } + + if !self + .table + .l2_table_cache + .contains_keys(l2_table_offset as u64) + { + let l2_cluster = self.load_cluster(l2_table_offset)?; + let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( + l2_table_offset, + l2_cluster, + ENTRY_SIZE_U64, + )?)); + self.table.update_l2_table(l2_table_entry)?; + } + + let cached_l2_table = self.table.l2_table_cache.get(l2_table_offset).unwrap(); + let mut borrowed_table = cached_l2_table.borrow_mut(); + + for idx in 0..borrowed_table.get_entry_num() { + let l2_entry = borrowed_table.get_entry_map(idx)?; + let mut new_l2_entry = l2_entry & !QCOW2_OFFSET_COPIED; + let data_cluster_offset = new_l2_entry & L2_TABLE_OFFSET_MASK; + if data_cluster_offset == 0 { + // Unallocated data cluster. + continue; + } + if self.refcount.offset_into_cluster(data_cluster_offset) != 0 { + bail!( + "Cluster offset 0x{:x} unaligned, (L2 table offset 0x{:x}, L2 index {})!", + data_cluster_offset, + l2_table_offset, + idx + ); + } + + if num != 0 { + // Update Data Cluster refcount. + self.refcount + .update_refcount(data_cluster_offset, 1, is_add, num, false)?; + } + + let refcount = self.refcount.get_refcount(data_cluster_offset)?; + if refcount == 1 { + new_l2_entry |= QCOW2_OFFSET_COPIED; + } + if l2_entry != new_l2_entry { + borrowed_table.set_entry_map(idx, new_l2_entry)?; + } + } + self.sync_aio + .borrow_mut() + .write_buffer(l2_table_offset, borrowed_table.get_value())?; + drop(borrowed_table); + + if num != 0 { + // Update L2 table cluster refcount. + self.refcount + .update_refcount(l2_table_offset, 1, is_add, num, false)?; + } + + let refcount = self.refcount.get_refcount(l2_table_offset)?; + if refcount == 1 { + l2_table_offset |= QCOW2_OFFSET_COPIED; + } + if l2_table_offset != old_l2_table_offset { + *l1_entry = l2_table_offset; + l1_changed = true; + if l1_table_offset == self.header.l1_table_offset { + self.table.update_l1_table(i, l2_table_offset); + } + } + } + self.refcount.flush_refcount_block_cache()?; + if l1_changed { + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(l1_table_offset, &l1_table)?; + } + + Ok(()) + } + + fn qcow2_list_snapshots(&self) -> String { + let mut snap_strs = format!( + "{:<10}{:<17}{:>8}{:>20}{:>13}{:>11}\r\n", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT" + ); + for snap in &self.snapshot.snapshots { + let id_str = snap.id.to_string(); + let name_str = snap.name.clone(); + // Note: vm state size is not needed in disk snapshot, so it's "0 B". + let vm_size_str = snap.vm_state_size.to_string(); + let icount_str = match snap.icount { + u64::MAX => "".to_string(), + _ => snap.icount.to_string(), + }; + + let date = get_format_time(snap.date_sec as i64); + let date_str = format!( + "{:04}-{:02}-{:02} {:02}-{:02}-{:02}", + date[0], date[1], date[2], date[3], date[4], date[5] + ); + + let vm_clock_secs = snap.vm_clock_nsec / 1_000_000_000; + let vm_clock_str = format!( + "{:02}:{:02}:{:02}.{:02}", + vm_clock_secs / 3600, + (vm_clock_secs / 3600) % 60, + vm_clock_secs % 60, + (vm_clock_secs / 1_000_000) % 1000 + ); + + let snap_str = format!( + "{:<9} {:<16} {:>6} B{:>20}{:>13}{:>11}\r\n", + id_str, name_str, vm_size_str, date_str, vm_clock_str, icount_str + ); + snap_strs += &snap_str; + } + snap_strs + } + + // Check if there exist intersection between given address range and qcow2 mede data. + fn qcow2_pre_write_overlap_check(&self, ignore: u64, offset: u64, size: u64) -> i64 { + let check = DEFAULT_QCOW2_METADATA_OVERLAP_CHECK | !ignore; + if check == 0 { + return 0; + } + + if check & METADATA_OVERLAP_CHECK_MAINHEADER != 0 && offset < self.header.cluster_size() { + return METADATA_OVERLAP_CHECK_MAINHEADER as i64; + } + + let size = round_up( + self.refcount.offset_into_cluster(offset) + size, + self.header.cluster_size(), + ) + .unwrap(); + let offset = self.refcount.start_of_cluster(offset); + if u64::MAX - offset < size { + // Ensure there exist no overflow. + return -1; + } + + if check & METADATA_OVERLAP_CHECK_ACTIVEL1 != 0 + && self.header.l1_size != 0 + && check_overlaps( + offset, + size, + self.header.l1_table_offset, + self.header.l1_size as u64 * ENTRY_SIZE, + ) + { + return METADATA_OVERLAP_CHECK_ACTIVEL1 as i64; + } + + if check & METADATA_OVERLAP_CHECK_ACTIVEL2 != 0 { + for l1_entry in &self.table.l1_table { + if check_overlaps( + offset, + size, + l1_entry & L1_TABLE_OFFSET_MASK, + self.header.cluster_size(), + ) { + return METADATA_OVERLAP_CHECK_ACTIVEL2 as i64; + } + } + } + + if check & METADATA_OVERLAP_CHECK_REFCOUNTTABLE != 0 + && check_overlaps( + offset, + size, + self.header.refcount_table_offset, + self.header.refcount_table_clusters as u64 * self.header.cluster_size(), + ) + { + return METADATA_OVERLAP_CHECK_REFCOUNTTABLE as i64; + } + + if check & METADATA_OVERLAP_CHECK_REFCOUNTBLOCK != 0 { + for block_offset in &self.refcount.refcount_table { + if check_overlaps( + offset, + size, + block_offset & REFCOUNT_TABLE_OFFSET_MASK, + self.header.cluster_size(), + ) { + return METADATA_OVERLAP_CHECK_REFCOUNTBLOCK as i64; + } + } + } + + if check & METADATA_OVERLAP_CHECK_SNAPSHOTTABLE != 0 + && check_overlaps( + offset, + size, + self.snapshot.snapshot_table_offset, + self.snapshot.snapshot_size, + ) + { + return METADATA_OVERLAP_CHECK_SNAPSHOTTABLE as i64; + } + + if check & METADATA_OVERLAP_CHECK_INACTIVEL1 != 0 { + for snap in &self.snapshot.snapshots { + if check_overlaps( + offset, + size, + snap.l1_table_offset, + snap.l1_size as u64 * ENTRY_SIZE, + ) { + return METADATA_OVERLAP_CHECK_INACTIVEL1 as i64; + } + } + } + + 0 + } +} + +pub fn bytes_to_clusters(size: u64, cluster_sz: u64) -> Result { + div_round_up(size, cluster_sz) + .with_context(|| format!("Failed to div round up, size is {}", size)) +} + +// Check if there is an intersection between two addresses. Return true if there is a crossover. +fn check_overlaps(addr1: u64, size1: u64, addr2: u64, size2: u64) -> bool { + // Note: ensure that there is no overflow at the calling function. + if addr1 + size1 == 0 || addr2 + size2 == 0 { + return false; + } + + let last1 = addr1 + size1 - 1; + let last2 = addr2 + size2 - 1; + + !(last1 < addr2 || last2 < addr1) +} + +pub trait InternalSnapshotOps: Send + Sync { + fn create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()>; + fn delete_snapshot(&mut self, name: String) -> Result; + fn list_snapshots(&self) -> String; +} + +impl InternalSnapshotOps for Qcow2Driver { + fn create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()> { + self.qcow2_create_snapshot(name, vm_clock_nsec) + } + + fn delete_snapshot(&mut self, name: String) -> Result { + self.qcow2_delete_snapshot(name) + } + + fn list_snapshots(&self) -> String { + self.qcow2_list_snapshots() + } } // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. @@ -615,7 +1284,6 @@ mod test { let file = std::fs::OpenOptions::new() .read(true) .write(true) - .custom_flags(libc::O_DIRECT) .open(path) .unwrap(); let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 5c3137a2b..67cdad305 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -16,6 +16,7 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use crate::qcow2::{ + bytes_to_clusters, cache::{CacheTable, Qcow2Cache, ENTRY_SIZE_U16}, header::QcowHeader, is_aligned, SyncAioInfo, ENTRY_SIZE, @@ -75,8 +76,8 @@ impl RefCount { self.refcount_max += self.refcount_max - 1; } - fn bytes_to_clusters(&self, size: u64) -> u64 { - (size + self.cluster_size - 1) >> self.cluster_bits + pub fn start_of_cluster(&self, offset: u64) -> u64 { + offset & !(self.cluster_size - 1) } fn cluster_in_rc_block(&self, cluster_index: u64) -> u64 { @@ -137,8 +138,8 @@ impl RefCount { (self.refcount_table_clusters << self.cluster_bits) as u64 / ENTRY_SIZE; // Free the old cluster of refcount table. - let clusters = self.bytes_to_clusters(old_rct_size as u64); - self.update_refcount(old_rct_offset, clusters, false, 1)?; + let clusters = bytes_to_clusters(old_rct_size, self.cluster_size).unwrap(); + self.update_refcount(old_rct_offset, clusters, false, 1, true)?; info!( "Qcow2 extends refcount table success, offset 0x{:x} -> 0x{:x}", old_rct_offset, self.refcount_table_offset @@ -153,8 +154,12 @@ impl RefCount { clusters: u64, is_add: bool, addend: u16, + flush: bool, ) -> Result<()> { - let first_cluster = self.bytes_to_clusters(offset); + if self.offset_into_cluster(offset) != 0 { + bail!("Failed to update refcount, offset is not aligned to cluster"); + } + let first_cluster = bytes_to_clusters(offset, self.cluster_size).unwrap(); let mut rc_vec = Vec::new(); let mut i = 0; while i < clusters { @@ -186,7 +191,10 @@ impl RefCount { let status = if rev_idx == idx { "success" } else { "failed" }; bail!("Failed to set refcounts, recover {}", status); } - self.flush_reftount_block_cache() + if flush { + self.flush_refcount_block_cache()?; + } + Ok(()) } fn set_refcount_blocks( @@ -207,7 +215,7 @@ impl RefCount { rc_vec.len() } - fn flush_reftount_block_cache(&self) -> Result<()> { + pub fn flush_refcount_block_cache(&self) -> Result<()> { for (_, entry) in self.refcount_blk_cache.iter() { let mut borrowed_entry = entry.borrow_mut(); if !borrowed_entry.dirty_info.is_dirty { @@ -282,7 +290,44 @@ impl RefCount { Ok(()) } - fn offset_into_cluster(&self, offset: u64) -> u64 { + pub fn get_refcount(&mut self, offset: u64) -> Result { + let cluster = offset >> self.cluster_bits; + let rt_idx = cluster >> self.refcount_blk_bits; + if rt_idx >= self.refcount_table_size as u64 { + bail!( + "Invalid refcount table index {}, refcount table size {}", + rt_idx, + self.refcount_table_size + ); + } + + let rb_addr = self.refcount_table[rt_idx as usize]; + if rb_addr == 0 || self.offset_into_cluster(rb_addr) != 0 { + bail!( + "Invalid refcount block address 0x{:x}, index is {}", + rb_addr, + rt_idx + ); + } + + if !self.refcount_blk_cache.contains_keys(rt_idx) { + self.load_refcount_block(rt_idx).with_context(|| { + format!("Failed to get refcount block cache, index is {}", rt_idx) + })?; + } + let cache_entry = self + .refcount_blk_cache + .get(rt_idx) + .with_context(|| format!("Not found refcount block cache, index is {}", rt_idx))? + .clone(); + + let rb_idx = self.cluster_in_rc_block(cluster) as usize; + let rc_value = cache_entry.borrow_mut().get_entry_map(rb_idx).unwrap(); + + Ok(rc_value as u16) + } + + pub fn offset_into_cluster(&self, offset: u64) -> u64 { offset & (self.cluster_size - 1) } @@ -323,7 +368,7 @@ impl RefCount { } fn find_free_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { - let clusters = self.bytes_to_clusters(size); + let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); let mut current_index = self.free_cluster_index; let mut i = 0; while i < clusters { @@ -384,8 +429,8 @@ impl RefCount { pub fn alloc_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { let addr = self.find_free_cluster(header, size)?; - let clusters = self.bytes_to_clusters(size as u64); - self.update_refcount(addr, clusters, true, 1)?; + let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); + self.update_refcount(addr, clusters, true, 1, true)?; Ok(addr) } @@ -626,11 +671,11 @@ mod test { let mut refcount = qcow2.refcount.clone(); // Add refcount for the first cluster. - let ret = refcount.update_refcount(0, 1, true, 1); + let ret = refcount.update_refcount(0, 1, true, 1, true); assert!(ret.is_ok()); // Test invalid cluster offset. - let ret = refcount.update_refcount(1 << 63, 1, true, 1); + let ret = refcount.update_refcount(1 << 63, 1, true, 1, true); if let Err(err) = ret { // 16 bit is cluster bits, 15 is refcount block bits. let err_msg = format!("Invalid refcount table index {}", 1_u64 << (63 - 15 - 16)); @@ -640,7 +685,7 @@ mod test { } // Test refcount block not in cache. - let ret = refcount.update_refcount(1 << (cluster_bits * 2), 1, true, 1); + let ret = refcount.update_refcount(1 << (cluster_bits * 2), 1, true, 1, true); if let Err(err) = ret { let err_msg = format!("Invalid refcount block address 0x0, index is 2"); assert_eq!(err.to_string(), err_msg); diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs new file mode 100644 index 000000000..abcc98a29 --- /dev/null +++ b/block_backend/src/qcow2/snapshot.rs @@ -0,0 +1,302 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, rc::Rc}; +use std::{mem::size_of, str::from_utf8}; + +use anyhow::{bail, Context, Result}; +use byteorder::{BigEndian, ByteOrder}; + +use super::{is_aligned, SyncAioInfo}; +use util::num_ops::round_up; + +/// Maximum number of snapshots. +pub const QCOW2_MAX_SNAPSHOTS: usize = 65536; + +#[derive(Clone)] +pub struct InternalSnapshot { + pub snapshots: Vec, + sync_aio: Rc>, + cluster_size: u64, + // Total snapshot table size in bytes. + pub snapshot_size: u64, + pub snapshot_table_offset: u64, + // Number of snapshot table entry. + nb_snapshots: u32, +} + +impl InternalSnapshot { + pub fn new(sync_aio: Rc>) -> Self { + Self { + snapshots: Vec::new(), + sync_aio, + cluster_size: 0, + snapshot_size: 0, + snapshot_table_offset: 0, + nb_snapshots: 0, + } + } + + pub fn snapshots_number(&self) -> usize { + self.nb_snapshots as usize + } + + pub fn find_snapshot(&self, name: &String) -> i32 { + for (idx, snap) in self.snapshots.iter().enumerate() { + if snap.name.eq(name) { + return idx as i32; + } + } + -1 + } + + pub fn set_cluster_size(&mut self, cluster_size: u64) { + self.cluster_size = cluster_size; + } + + pub fn add_snapshot(&mut self, snap: QcowSnapshot) { + let size = snap.get_size(); + self.snapshots.push(snap); + self.snapshot_size += size; + self.nb_snapshots += 1; + } + + pub fn del_snapshot(&mut self, index: usize) -> QcowSnapshot { + let snap = self.snapshots.remove(index); + self.nb_snapshots -= 1; + self.snapshot_size -= snap.get_size(); + + snap + } + + pub fn insert_snapshot(&mut self, snap: QcowSnapshot, index: usize) { + let size = snap.get_size(); + self.snapshots.insert(index, snap); + self.snapshot_size += size; + self.nb_snapshots += 1; + } + + pub fn find_new_snapshot_id(&self) -> u64 { + let mut id_max = 0; + for snap in &self.snapshots { + if id_max < snap.id { + id_max = snap.id; + } + } + + id_max + 1 + } + + pub fn save_snapshot_table(&self, addr: u64) -> Result<()> { + let mut buf = Vec::new(); + for snap in &self.snapshots { + buf.append(&mut snap.gen_snapshot_table_entry()); + } + self.sync_aio.borrow_mut().write_buffer(addr, &buf) + } + + pub fn load_snapshot_table(&mut self, addr: u64, nb_snapshots: u32) -> Result<()> { + if !is_aligned(self.cluster_size, addr) { + bail!("snapshot table address not aligned {}", addr); + } + + for _ in 0..nb_snapshots { + let offset = addr + self.snapshot_size; + + let mut pos = 0; + let header_size = size_of::(); + let mut header_buf = vec![0_u8; header_size]; + self.sync_aio + .borrow_mut() + .read_buffer(offset, &mut header_buf) + .with_context(|| format!("read snapshot header error(addr {:x}).", offset))?; + let header = QcowSnapshotHeader::from_vec(&header_buf)?; + pos += header_size; + + let extra_size = size_of::(); + let mut extra_buf = vec![0_u8; extra_size]; + self.sync_aio + .borrow_mut() + .read_buffer(offset + pos as u64, &mut extra_buf) + .with_context(|| { + format!( + "read snapshot extra data error(addr {:x}).", + offset + pos as u64 + ) + })?; + let extra = QcowSnapshotExtraData::from_vec(&extra_buf)?; + pos += extra_size; + + if header.id_str_size == 0 { + bail!("Invalid snapshot id size: 0"); + } + let mut id_buf = vec![0_u8; header.id_str_size as usize]; + self.sync_aio + .borrow_mut() + .read_buffer(offset + pos as u64, &mut id_buf) + .with_context(|| { + format!("read snapshot ID error(addr {:x}).", offset + pos as u64) + })?; + let id = from_utf8(&id_buf).unwrap().parse::().unwrap(); + pos += header.id_str_size as usize; + + let mut name_buf = vec![0_u8; header.name_size as usize]; + self.sync_aio + .borrow_mut() + .read_buffer(offset + pos as u64, &mut name_buf) + .with_context(|| { + format!("read snapshot name error(addr {:x}).", offset + pos as u64) + })?; + let name = from_utf8(&name_buf).unwrap(); + + let snap = QcowSnapshot { + l1_table_offset: header.l1_table_offset, + l1_size: header.l1_size, + id, + name: name.to_string(), + disk_size: extra.disk_size, + vm_state_size: header.vm_state_size, + date_sec: header.date_sec, + date_nsec: header.date_nsec, + vm_clock_nsec: header.vm_clock_nsec, + icount: extra.icount, + extra_data_size: header.extra_date_size, + }; + + self.add_snapshot(snap); + } + + Ok(()) + } +} + +#[derive(Clone)] +pub struct QcowSnapshot { + pub l1_table_offset: u64, + pub l1_size: u32, + pub id: u64, + pub name: String, + pub disk_size: u64, + // VM state info size, used for vm snapshot. + // Set to 0 for disk internal snapshot. + pub vm_state_size: u32, + pub date_sec: u32, + pub date_nsec: u32, + pub vm_clock_nsec: u64, + // Icount value which corresponds to the record/replay instruction count when the snapshots was token. + // Sed to -1 which means icount was disabled. + pub icount: u64, + pub extra_data_size: u32, +} + +impl QcowSnapshot { + pub fn get_size(&self) -> u64 { + let tmp_size = size_of::() + + self.extra_data_size as usize + + self.id.to_string().len() + + self.name.len(); + + round_up(tmp_size as u64, 8).unwrap() + } + + fn gen_snapshot_table_entry(&self) -> Vec { + let id_str = self.id.to_string(); + let entry_size = size_of::() + size_of::(); + let mut buf = vec![0_u8; entry_size]; + + // Snapshot Header. + BigEndian::write_u64(&mut buf[0..8], self.l1_table_offset); + BigEndian::write_u32(&mut buf[8..12], self.l1_size); + BigEndian::write_u16(&mut buf[12..14], id_str.len() as u16); + BigEndian::write_u16(&mut buf[14..16], self.name.len() as u16); + BigEndian::write_u32(&mut buf[16..20], self.date_sec); + BigEndian::write_u32(&mut buf[20..24], self.date_nsec); + BigEndian::write_u64(&mut buf[24..32], self.vm_clock_nsec); + BigEndian::write_u32(&mut buf[32..36], self.vm_state_size); + BigEndian::write_u32(&mut buf[36..40], size_of::() as u32); + + // Snapshot Extra data. + // vm_state_size_large is used for vm snapshot. + // It's equal to vm_state_size which is also 0 in disk snapshot. + BigEndian::write_u64(&mut buf[40..48], self.vm_state_size as u64); + BigEndian::write_u64(&mut buf[48..56], self.disk_size); + BigEndian::write_u64(&mut buf[56..64], self.icount); + + // Snapshot ID. + let mut id_vec = id_str.as_bytes().to_vec(); + buf.append(&mut id_vec); + + // Snapshot Name. + let mut name_vec = self.name.as_bytes().to_vec(); + buf.append(&mut name_vec); + + // 8 bytes Alignment. + let tmp_size = buf.len(); + let size = round_up(tmp_size as u64, 8).unwrap(); + buf.resize(size as usize, 0); + + buf + } +} + +pub struct QcowSnapshotHeader { + l1_table_offset: u64, + l1_size: u32, + id_str_size: u16, + name_size: u16, + date_sec: u32, + date_nsec: u32, + vm_clock_nsec: u64, + vm_state_size: u32, + extra_date_size: u32, +} + +impl QcowSnapshotHeader { + fn from_vec(buf: &[u8]) -> Result { + if buf.len() < size_of::() { + bail!("Invalid qcow2 snapshot header length {}.", buf.len()); + } + + Ok(QcowSnapshotHeader { + l1_table_offset: BigEndian::read_u64(&buf[0..8]), + l1_size: BigEndian::read_u32(&buf[8..12]), + id_str_size: BigEndian::read_u16(&buf[12..14]), + name_size: BigEndian::read_u16(&buf[14..16]), + date_sec: BigEndian::read_u32(&buf[16..20]), + date_nsec: BigEndian::read_u32(&buf[20..24]), + vm_clock_nsec: BigEndian::read_u64(&buf[24..32]), + vm_state_size: BigEndian::read_u32(&buf[32..36]), + extra_date_size: BigEndian::read_u32(&buf[36..40]), + }) + } +} + +pub struct QcowSnapshotExtraData { + _vm_state_size_large: u64, + disk_size: u64, + icount: u64, +} + +impl QcowSnapshotExtraData { + fn from_vec(buf: &[u8]) -> Result { + let extra_len = size_of::(); + if buf.len() != extra_len { + bail!("Only support snapshot extra data length {}", extra_len); + } + + Ok(QcowSnapshotExtraData { + _vm_state_size_large: BigEndian::read_u64(&buf[0..8]), + disk_size: BigEndian::read_u64(&buf[8..16]), + icount: BigEndian::read_u64(&buf[16..24]), + }) + } +} diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 1ff253730..d8b76c403 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -129,7 +129,7 @@ impl Qcow2Table { Ok(()) } - fn save_l1_table(&mut self, header: &QcowHeader) -> Result<()> { + pub fn save_l1_table(&mut self, header: &QcowHeader) -> Result<()> { self.sync_aio .borrow_mut() .write_ctrl_cluster(header.l1_table_offset, &self.l1_table) @@ -161,14 +161,8 @@ impl Qcow2Table { } } - pub fn update_l1_table( - &mut self, - l1_index: usize, - l2_address: u64, - header: &QcowHeader, - ) -> Result<()> { + pub fn update_l1_table(&mut self, l1_index: usize, l2_address: u64) { self.l1_table[l1_index] = l2_address; - self.save_l1_table(header) } pub fn update_l2_table(&mut self, l2_table_entry: Rc>) -> Result<()> { diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 27da0fa68..9caa8287c 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -160,6 +160,7 @@ impl ScsiDevice { let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; self.req_align = alignments.0; self.buf_align = alignments.1; + let drive_id = VmConfig::get_drive_id(&drive_files, &self.config.path_on_host)?; let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type)?; let conf = BlockProperty { @@ -171,7 +172,7 @@ impl ScsiDevice { discard: false, write_zeroes: WriteZeroesState::Off, }; - let backend = create_block_backend(file, aio, conf)?; + let backend = create_block_backend(file, aio, drive_id, conf)?; let disk_size = backend.lock().unwrap().disk_size()?; self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 017e699d5..6260bce90 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -2256,6 +2256,24 @@ pub struct human_monitor_command { } pub type HumanMonitorCmdArgument = human_monitor_command; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SnapshotInfo { + #[serde(rename = "id")] + pub id: String, + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "vm-state-size")] + pub vm_state_size: u64, + #[serde(rename = "date-sec")] + pub date_sec: u32, + #[serde(rename = "date-nsec")] + pub date_nsec: u32, + #[serde(rename = "vm-clock-nsec")] + pub vm_clock_nsec: u64, + #[serde(rename = "icount")] + pub icount: u64, +} + #[cfg(test)] mod tests { use super::*; diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 23813aa8b..758978d1f 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1053,6 +1053,7 @@ impl VirtioDevice for Block { let alignments = VmConfig::fetch_drive_align(&drive_files, &self.blk_cfg.path_on_host)?; self.req_align = alignments.0; self.buf_align = alignments.1; + let drive_id = VmConfig::get_drive_id(&drive_files, &self.blk_cfg.path_on_host)?; let aio = Aio::new(Arc::new(BlockIoHandler::complete_func), self.blk_cfg.aio)?; let conf = BlockProperty { @@ -1064,7 +1065,7 @@ impl VirtioDevice for Block { discard: self.blk_cfg.discard, write_zeroes: self.blk_cfg.write_zeroes, }; - let backend = create_block_backend(file, aio, conf)?; + let backend = create_block_backend(file, aio, drive_id, conf)?; let disk_size = backend.lock().unwrap().disk_size()?; self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; -- Gitee From 54a5d581c05c17c7e59d1f1d2a54c72c0066d1bf Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 7 Jul 2023 13:37:53 +0800 Subject: [PATCH 1189/2187] snapshot: add qcow2 internal snapshot qmp/hmp command Add qcow2 internal snapshot qmp/hmp command. Eg: Create snapshot: { "execute": "blockdev-snapshot-internal-sync", "arguments": { "device": "disk0", "name": "snapshot1" }} { "return": {} } Delete snapshot: { "execute": "blockdev-snapshot-delete-internal-sync", "arguments": { "device": "disk0", "name": "snapshot1" }} { "return": { "id": "1", "name": "snapshot0", "vm-state-size": 0, "date-sec": 1000012, "date-nsec": 10, "vm-clock-sec": 100, "vm-clock-nsec": 20, "icount": 220414 } } Query snapshot: {"execute":"human-monitor-command","arguments":"{"command-line":"info snapshots"}} Signed-off-by: liuxiangdong Signed-off-by: Yan Wang --- Cargo.lock | 1 + block_backend/src/lib.rs | 3 +- machine/Cargo.toml | 1 + machine/src/standard_vm/mod.rs | 117 ++++++++++++++++++++++++++ machine_manager/src/machine.rs | 20 ++++- machine_manager/src/qmp/mod.rs | 4 +- machine_manager/src/qmp/qmp_schema.rs | 64 ++++++++++++++ 7 files changed, 204 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c206ca54..093f85f0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,7 @@ dependencies = [ "acpi", "address_space", "anyhow", + "block_backend", "boot_loader", "cpu", "devices", diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 3c83e4b29..22547154b 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -10,8 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod qcow2; + mod file; -mod qcow2; mod raw; use std::{ diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 1f9de313b..ddaf0b59e 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -33,6 +33,7 @@ sysbus = { path = "../sysbus" } util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } +block_backend = { path = "../block_backend" } [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 65a579fa6..7d7521dc6 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -57,6 +57,7 @@ use address_space::{ }; pub use anyhow::Result; use anyhow::{bail, Context}; +use block_backend::qcow2::QCOW2_LIST; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ @@ -1850,6 +1851,51 @@ impl DeviceInterface for StdMachine { } return self.blockdev_del(cmd_args[1].to_string()); } + "info" => { + // Only support to query snapshots information by: + // "info snapshots" + if cmd_args.len() != 2 { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "Invalid number of arguments".to_string(), + ), + None, + ); + } + if cmd_args[1] != "snapshots" { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!( + "Unsupported command: {} {}", + cmd_args[0], cmd_args[1] + )), + None, + ); + } + + let qcow2_list = QCOW2_LIST.lock().unwrap(); + if qcow2_list.len() == 0 { + return Response::create_response( + serde_json::to_value("There is no snapshot available.\r\n").unwrap(), + None, + ); + } + + let mut info_str = "List of snapshots present on all disks:\r\n".to_string(); + // Note: VM state is "None" in disk snapshots. It's used for vm snapshots which we don't support. + let vmstate_str = "None\r\n".to_string(); + info_str += &vmstate_str; + + for (drive_name, qcow2driver) in qcow2_list.iter() { + let dev_str = format!( + "\r\nList of partial (non-loadable) snapshots on \'{}\':\r\n", + drive_name + ); + let snap_infos = qcow2driver.lock().unwrap().list_snapshots(); + info_str += &(dev_str + &snap_infos); + } + + return Response::create_response(serde_json::to_value(info_str).unwrap(), None); + } _ => { return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(format!( @@ -1862,6 +1908,77 @@ impl DeviceInterface for StdMachine { } Response::create_empty_response() } + + fn blockdev_snapshot_internal_sync( + &self, + args: qmp_schema::BlockdevSnapshotInternalArgument, + ) -> Response { + let qcow2_list = QCOW2_LIST.lock().unwrap(); + let qcow2driver = qcow2_list.get(&args.device); + if qcow2driver.is_none() { + return Response::create_error_response( + qmp_schema::QmpErrorClass::DeviceNotFound(format!( + "No device drive named {} while creating snapshot {}", + args.device, args.name + )), + None, + ); + } + + // TODO: Add a method for getting guest clock. It's useless now so we can use a fake time(0). + let vm_clock_nsec = 0; + if let Err(e) = qcow2driver + .unwrap() + .lock() + .unwrap() + .create_snapshot(args.name.clone(), vm_clock_nsec) + { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!( + "Device {} Creates snapshot {} error: {}.", + args.device, args.name, e + )), + None, + ); + } + + Response::create_empty_response() + } + + fn blockdev_snapshot_delete_internal_sync( + &self, + args: qmp_schema::BlockdevSnapshotInternalArgument, + ) -> Response { + let qcow2_list = QCOW2_LIST.lock().unwrap(); + let qcow2driver = qcow2_list.get(&args.device); + if qcow2driver.is_none() { + return Response::create_error_response( + qmp_schema::QmpErrorClass::DeviceNotFound(format!( + "No device drive named {} while deleting snapshot {}", + args.device, args.name + )), + None, + ); + } + + let result = qcow2driver + .unwrap() + .lock() + .unwrap() + .delete_snapshot(args.name.clone()); + match result { + Ok(snap_info) => { + Response::create_response(serde_json::to_value(&snap_info).unwrap(), None) + } + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!( + "Device {} deletes snapshot {} error! {}", + args.device, args.name, e + )), + None, + ), + } + } } #[cfg(not(target_env = "musl"))] diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index c09f4213b..cec244617 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -18,10 +18,11 @@ use strum::VariantNames; use crate::config::ShutdownAction; use crate::qmp::qmp_schema::{ - BlockDevAddArgument, CameraDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, - CmdParameter, DeviceAddArgument, DeviceProps, Events, GicCap, HumanMonitorCmdArgument, - IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, NetDevAddArgument, PropList, - QmpCommand, QmpErrorClass, QmpEvent, Target, TypeLists, UpdateRegionArgument, + BlockDevAddArgument, BlockdevSnapshotInternalArgument, CameraDevAddArgument, + CharDevAddArgument, ChardevInfo, Cmd, CmdLine, CmdParameter, DeviceAddArgument, DeviceProps, + Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, MachineInfo, + MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, Target, + TypeLists, UpdateRegionArgument, }; use crate::qmp::{Response, Version}; @@ -466,6 +467,17 @@ pub trait DeviceInterface { None, ) } + + fn blockdev_snapshot_internal_sync(&self, _args: BlockdevSnapshotInternalArgument) -> Response { + Response::create_empty_response() + } + + fn blockdev_snapshot_delete_internal_sync( + &self, + _args: BlockdevSnapshotInternalArgument, + ) -> Response { + Response::create_empty_response() + } } /// Migrate external api diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index b036c961d..3aa8f3fb1 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -461,7 +461,9 @@ fn qmp_command_exec( (chardev_add, chardev_add), (cameradev_add, cameradev_add), (update_region, update_region), - (human_monitor_command, human_monitor_command) + (human_monitor_command, human_monitor_command), + (blockdev_snapshot_internal_sync, blockdev_snapshot_internal_sync), + (blockdev_snapshot_delete_internal_sync, blockdev_snapshot_delete_internal_sync) ); // Handle the Qmp command which macro can't cover diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 6260bce90..abdcc5235 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -415,6 +415,18 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "blockdev-snapshot-internal-sync")] + blockdev_snapshot_internal_sync { + arguments: blockdev_snapshot_internal, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, + #[serde(rename = "blockdev-snapshot-delete-internal-sync")] + blockdev_snapshot_delete_internal_sync { + arguments: blockdev_snapshot_internal, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, } /// qmp_capabilities @@ -2256,6 +2268,58 @@ pub struct human_monitor_command { } pub type HumanMonitorCmdArgument = human_monitor_command; +/// blockdev-snapshot-internal-sync +/// +/// Create disk internal snapshot. +/// +/// # Arguments +/// +/// * `device` - the valid block device. +/// * `name` - the snapshot name. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "blockdev-snapshot-internal-sync", +/// "arguments": { "device": "disk0", +/// "name": "snapshot1" }} +/// <- { "return": {} } +/// ``` +/// +/// blockdev-snapshot-delete-internal-sync +/// +/// Delete disk internal snapshot. +/// +/// # Arguments +/// +/// * `device` - the valid block device. +/// * `name` - the snapshot name. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "blockdev-snapshot-delete-internal-sync", +/// "arguments": { "device": "disk0", +/// "name": "snapshot1" }} +/// <- { "return": { +/// "id": "1", +/// "name": "snapshot0", +/// "vm-state-size": 0, +/// "date-sec": 1000012, +/// "date-nsec": 10, +/// "vm-clock-sec": 100, +/// "vm-clock-nsec": 20, +/// "icount": 220414 +/// } } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct blockdev_snapshot_internal { + pub device: String, + pub name: String, +} +pub type BlockdevSnapshotInternalArgument = blockdev_snapshot_internal; + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SnapshotInfo { #[serde(rename = "id")] -- Gitee From b5174c46074b0b579166d37f76fb20c763258952 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 8 Jul 2023 03:28:09 +0800 Subject: [PATCH 1190/2187] snapshot: add lock between io handling and snapshot operation Add lock between io handling and snapshot operation to ensure that all requests in virtio-queues have been processed. Signed-off-by: Yan Wang Signed-off-by: liuxiangdong --- block_backend/src/lib.rs | 8 ++++++++ block_backend/src/qcow2/mod.rs | 13 ++++++++++++- block_backend/src/raw.rs | 10 ++++++++-- machine/src/standard_vm/mod.rs | 12 +++++++++++- virtio/src/device/block.rs | 24 +++++++++++++++++++++++- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 22547154b..a72f33ad4 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -30,6 +30,12 @@ use util::aio::{Aio, Iovec, WriteZeroesState}; /// Callback function which is called when aio handle failed. pub type BlockIoErrorCallback = Arc; +pub enum BlockStatus { + Init, + NormalIO, + Snapshot, +} + #[derive(Debug, Clone)] pub struct BlockProperty { pub format: DiskFormat, @@ -71,6 +77,8 @@ pub trait BlockDriverOps: Send { ) -> Result<()>; fn unregister_io_event(&mut self) -> Result<()>; + + fn get_status(&mut self) -> Arc>; } pub fn create_block_backend( diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 8d2617845..12cd81071 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -41,7 +41,7 @@ use crate::{ snapshot::{InternalSnapshot, QcowSnapshot, QcowSnapshotExtraData, QCOW2_MAX_SNAPSHOTS}, table::{Qcow2ClusterType, Qcow2Table}, }, - BlockDriverOps, BlockIoErrorCallback, BlockProperty, + BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, }; use machine_manager::qmp::qmp_schema::SnapshotInfo; use util::{ @@ -189,6 +189,7 @@ pub struct Qcow2Driver { table: Qcow2Table, refcount: RefCount, snapshot: InternalSnapshot, + status: Arc>, } impl Drop for Qcow2Driver { @@ -209,6 +210,7 @@ impl Qcow2Driver { table: Qcow2Table::new(sync_aio.clone()), refcount: RefCount::new(sync_aio.clone()), snapshot: InternalSnapshot::new(sync_aio), + status: Arc::new(Mutex::new(BlockStatus::Init)), }; qcow2 .load_header() @@ -1038,6 +1040,7 @@ pub trait InternalSnapshotOps: Send + Sync { fn create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()>; fn delete_snapshot(&mut self, name: String) -> Result; fn list_snapshots(&self) -> String; + fn get_status(&self) -> Arc>; } impl InternalSnapshotOps for Qcow2Driver { @@ -1052,6 +1055,10 @@ impl InternalSnapshotOps for Qcow2Driver { fn list_snapshots(&self) -> String { self.qcow2_list_snapshots() } + + fn get_status(&self) -> Arc> { + self.status.clone() + } } // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. @@ -1164,6 +1171,10 @@ impl BlockDriverOps for Qcow2Driver { fn unregister_io_event(&mut self) -> Result<()> { self.driver.unregister_io_event() } + + fn get_status(&mut self) -> Arc> { + self.status.clone() + } } pub fn is_aligned(cluster_sz: u64, offset: u64) -> bool { diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 584ded815..07f64ebb0 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -12,19 +12,20 @@ use std::{ fs::File, - sync::{atomic::AtomicBool, Arc}, + sync::{atomic::AtomicBool, Arc, Mutex}, }; use anyhow::Result; use crate::{ file::{CombineRequest, FileDriver}, - BlockDriverOps, BlockIoErrorCallback, BlockProperty, + BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, }; use util::aio::{Aio, Iovec}; pub struct RawDriver { driver: FileDriver, + status: Arc>, } // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. @@ -36,6 +37,7 @@ impl RawDriver { pub fn new(file: File, aio: Aio, prop: BlockProperty) -> Self { Self { driver: FileDriver::new(file, aio, prop), + status: Arc::new(Mutex::new(BlockStatus::Init)), } } } @@ -96,4 +98,8 @@ impl BlockDriverOps for RawDriver { fn disk_size(&mut self) -> Result { self.driver.disk_size() } + + fn get_status(&mut self) -> Arc> { + self.status.clone() + } } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 7d7521dc6..e07a04ea1 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -57,7 +57,7 @@ use address_space::{ }; pub use anyhow::Result; use anyhow::{bail, Context}; -use block_backend::qcow2::QCOW2_LIST; +use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ @@ -1925,6 +1925,11 @@ impl DeviceInterface for StdMachine { ); } + // Do not unlock or drop the locked_status in this function. + let status = qcow2driver.unwrap().lock().unwrap().get_status(); + let mut locked_status = status.lock().unwrap(); + *locked_status = BlockStatus::Snapshot; + // TODO: Add a method for getting guest clock. It's useless now so we can use a fake time(0). let vm_clock_nsec = 0; if let Err(e) = qcow2driver @@ -1961,6 +1966,11 @@ impl DeviceInterface for StdMachine { ); } + // Do not unlock or drop the locked_status in this function. + let status = qcow2driver.unwrap().lock().unwrap().get_status(); + let mut locked_status = status.lock().unwrap(); + *locked_status = BlockStatus::Snapshot; + let result = qcow2driver .unwrap() .lock() diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 758978d1f..14a951f4a 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -38,7 +38,9 @@ use crate::{ VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::{AddressSpace, GuestAddress}; -use block_backend::{create_block_backend, BlockDriverOps, BlockIoErrorCallback, BlockProperty}; +use block_backend::{ + create_block_backend, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, +}; use machine_manager::config::{BlkDevConfig, ConfigCheck, DriveFile, VmConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use migration::{ @@ -625,6 +627,26 @@ impl BlockIoHandler { } fn process_queue_suppress_notify(&mut self) -> Result { + // Note: locked_status has two function: + // 1) set the status of the block device. + // 2) as a mutex lock which is mutual exclusive with snapshot operations. + // Do not unlock or drop the locked_status in this function. + let status; + let mut locked_status; + let len = self + .queue + .lock() + .unwrap() + .vring + .avail_ring_len(&self.mem_space)?; + if len > 0 { + if let Some(block_backend) = self.block_backend.as_ref() { + status = block_backend.lock().unwrap().get_status(); + locked_status = status.lock().unwrap(); + *locked_status = BlockStatus::NormalIO; + } + } + let mut done = false; let start_time = Instant::now(); -- Gitee From 121e5626ecf1486e55ff0a9769742ceffd368851 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Sat, 8 Jul 2023 16:21:58 +0800 Subject: [PATCH 1191/2187] usb: fix camera mst After commit 5673b224, we remove self power in bmAttributes, fix the camera mst. Signed-off-by: zhouli57 --- tests/mod_test/src/libdriver/usb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index a9606b3a4..07c65c768 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -613,7 +613,7 @@ impl TestXhciPciDevice { assert_eq!(buf, [3, 0]); } UsbDeviceType::Camera => { - assert_eq!(buf, [3, 0]); + assert_eq!(buf, [2, 0]); } _ => {} } -- Gitee From 23ceab4dc623f3c6c8aacacde8b155eb1d8c5bce Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Jul 2023 16:37:22 +0800 Subject: [PATCH 1192/2187] qcow2: remove duplicate update l2 table Remove duplicate update l2 table when l2 table do COW. Signed-off-by: zhouli57 --- block_backend/src/qcow2/mod.rs | 9 +-------- block_backend/src/qcow2/table.rs | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 42f8c03b5..5de7c00e6 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -368,17 +368,10 @@ impl Qcow2Driver { )?)); self.table.update_l2_table(l2_cache_entry)?; - // Update l1_table and l2 table cache. + // Update l1_table. self.table .update_l1_table(l1_index as usize, new_l2_offset | QCOW2_OFFSET_COPIED); self.table.save_l1_table(&self.header)?; - let zero_cluster: Vec = vec![0_u8; self.header.cluster_size() as usize]; - let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( - new_l2_offset, - zero_cluster, - ENTRY_SIZE_U64, - )?)); - self.table.update_l2_table(l2_table_entry)?; // Decrease the refcount of the old table. if old_l2_offset != 0 { diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index edfeda032..8eb46879a 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -174,7 +174,7 @@ impl Qcow2Table { pub fn update_l2_table(&mut self, l2_table_entry: Rc>) -> Result<()> { let l2_entry_addr = l2_table_entry.borrow().addr; if self.l2_table_cache.contains_keys(l2_entry_addr as u64) { - return Ok(()); + self.l2_table_cache.cache_map.remove(&l2_entry_addr); } if let Some(replaced_entry) = self .l2_table_cache -- Gitee From 8ea73ed6985a2b0e9d38cb99b0176658f18336d7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 11 Jul 2023 01:48:00 +0800 Subject: [PATCH 1193/2187] snapshot: delete useless log Delete useless log. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index abdf0b803..9ffff300c 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -939,7 +939,6 @@ impl Qcow2Driver { let mut old_l2_table_offset: u64; for (i, l1_entry) in l1_table.iter_mut().enumerate().take(l1_table_size) { let mut l2_table_offset = *l1_entry; - log::info!("Develop: l1 table idx {}, addr {:x}", i, l2_table_offset); if l2_table_offset == 0 { // No l2 table. continue; -- Gitee From 87e9ab1e0b41c617e60612b4c4babe1c8413417c Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 10:23:59 +0800 Subject: [PATCH 1194/2187] memory: add memory object use memory-backend-memfd and memory-backend-file Signed-off-by: jiewangqun --- docs/config_guidebook.md | 11 ++- machine_manager/src/cmdline.rs | 7 +- machine_manager/src/config/machine_config.rs | 86 ++++++++++++++++++-- machine_manager/src/config/mod.rs | 20 +++-- 4 files changed, 107 insertions(+), 17 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1c9fa1fd3..72101972e 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -129,9 +129,12 @@ $ cat /proc/meminfo ### 1.5 NUMA node The optional NUMA node element gives the opportunity to create a virtual machine with non-uniform memory accesses. The application of NUMA node is that one region of memory can be set as fast memory, another can be set as slow memory. +The configuration items(mem-path, mem-prealloc) here will cause the global configuration to be invalidated Each NUMA node is given a list of command lines option, there will be described in detail below. -1. -object memory-backend-ram,size=2G,id=mem0,[policy=bind,host-nodes=0] +1. -object memory-backend-ram,size=,id=[,policy=][,host-nodes=<0>][,mem-prealloc=][,dump-guest-core=][,share=] + -object memory-backend-file,size=,id=[,host-nodes=<0-1>][,policy=bind][,mem-path=][,dump-guest-core=][,mem-prealloc=][,share=] + -object memory-backend-memfd,size=,id=[,host-nodes=0-1][,policy=bind][,mem-prealloc=][,dump-guest-core=][,share=] It describes the size and id of each memory zone, the policy of binding to host memory node. you should choose `G` or `M` as unit for each memory zone. The host-nodes id must exist on host OS. The optional policies are default, preferred, bind and interleave. If it is not configured, `default` is used. @@ -154,6 +157,10 @@ The following command shows how to set NUMA node: -object memory-backend-ram,size=2G,id=mem0,host-nodes=0-1,policy=bind -object memory-backend-ram,size=2G,id=mem1,host-nodes=0-1,policy=bind +or +-object memory-backend-file,size=2G,id=mem0,host-nodes=0-1,policy=bind,mem-path=/path/to/file +-object memory-backend-memfd,size=2G,id=mem1,host-nodes=0-1,policy=bind,mem-prealloc=true + -numa node,nodeid=0,cpus=0-1:4-5,memdev=mem0 -numa node,nodeid=1,cpus=2-3:6-7,memdev=mem1 [-numa dist,src=0,dst=0,val=10] @@ -165,6 +172,8 @@ The following command shows how to set NUMA node: Detailed configuration instructions: ``` -object memory-backend-ram,size=,id=,policy={bind|default|preferred|interleave},host-nodes= +-object memory-backend-file,size=,id=,policy={bind|default|preferred|interleave},host-nodes=,mem-path=[,dump-guest-core=] +-object memory-backend-memfd,size=,id=[,host-nodes=0-1][,policy=bind][,mem-prealloc=true][,dump-guest-core=false] -numa node[,nodeid=][,cpus=[-][:[-]]][,memdev=] -numa dist,src=,dst=,val= ``` diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 2c1ba19f7..facb3de94 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -310,7 +310,12 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .multiple(true) .long("object") .value_name("") - .help("\n\t\tadd memory backend ram object: -object memory-backend-ram,id=,size=<2G>,host-nodes=<0-1>,policy=; \ + .help("\n\t\tadd memory backend ram object: -object memory-backend-ram,size=,id=[,policy=] + [,host-nodes=<0>][,mem-prealloc=][,dump-guest-core=][,share=]; \ + \n\t\tadd memory backend file object: -object memory-backend-file,size=,id=[,host-nodes=<0-1>] \ + [,policy=bind][,mem-path=][,dump-guest-core=][,mem-prealloc=][,share=] \ + \n\t\tadd memory backend memfd object: -object memory-backend-memfd,size=,id=[,host-nodes=0-1][,policy=bind] \ + [,mem-prealloc=][,dump-guest-core=][,share=]; \ \n\t\tadd iothread object: -object iothread,id=; \ \n\t\tadd rng object: -object rng-random,id=,filename=; \ \n\t\tadd vnc tls object: -object tls-creds-x509,id=,dir=; \ diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 4b6d72dda..8b80a9a8e 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -17,7 +17,8 @@ use serde::{Deserialize, Serialize}; use super::error::ConfigError; use crate::config::{ - check_arg_too_long, CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES, + check_arg_too_long, check_path_too_long, CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, + MAX_NODES, }; const DEFAULT_CPUS: u8 = 1; @@ -86,7 +87,11 @@ pub struct MemZoneConfig { pub size: u64, pub host_numa_nodes: Option>, pub policy: String, + pub mem_path: Option, + pub dump_guest_core: bool, pub share: bool, + pub prealloc: bool, + pub memfd: bool, } impl Default for MemZoneConfig { @@ -96,7 +101,11 @@ impl Default for MemZoneConfig { size: 0, host_numa_nodes: None, policy: String::from("bind"), + mem_path: None, + dump_guest_core: true, share: false, + prealloc: false, + memfd: false, } } } @@ -435,6 +444,14 @@ impl VmConfig { } } + fn get_mem_path(&self, cmd_parser: &CmdParser) -> Result> { + if let Some(path) = cmd_parser.get_value::("mem-path")? { + check_path_too_long(&path, "mem-path")?; + return Ok(Some(path)); + } + Ok(None) + } + fn get_mem_zone_size(&self, cmd_parser: &CmdParser) -> Result { if let Some(mem) = cmd_parser.get_value::("size")? { let size = memory_unit_conversion(&mem)?; @@ -499,12 +516,27 @@ impl VmConfig { } } + fn get_mem_dump(&self, cmd_parser: &CmdParser) -> Result { + if let Some(dump_guest) = cmd_parser.get_value::("dump-guest-core")? { + return Ok(dump_guest.into()); + } + Ok(true) + } + + fn get_mem_prealloc(&self, cmd_parser: &CmdParser) -> Result { + if let Some(mem_prealloc) = cmd_parser.get_value::("mem-prealloc")? { + return Ok(mem_prealloc.into()); + } + Ok(false) + } + /// Convert memory zone cmdline to VM config /// /// # Arguments /// /// * `mem_zone` - The memory zone cmdline string. - pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { + /// * `mem_type` - The memory zone type + pub fn add_mem_zone(&mut self, mem_zone: &str, mem_type: String) -> Result { let mut cmd_parser = CmdParser::new("mem_zone"); cmd_parser .push("") @@ -512,7 +544,10 @@ impl VmConfig { .push("size") .push("host-nodes") .push("policy") - .push("share"); + .push("share") + .push("mem-path") + .push("dump-guest-core") + .push("mem-prealloc"); cmd_parser.parse(mem_zone)?; let zone_config = MemZoneConfig { @@ -520,9 +555,27 @@ impl VmConfig { size: self.get_mem_zone_size(&cmd_parser)?, host_numa_nodes: self.get_mem_zone_host_nodes(&cmd_parser)?, policy: self.get_mem_zone_policy(&cmd_parser)?, + dump_guest_core: self.get_mem_dump(&cmd_parser)?, share: self.get_mem_share(&cmd_parser)?, + mem_path: self.get_mem_path(&cmd_parser)?, + prealloc: self.get_mem_prealloc(&cmd_parser)?, + memfd: mem_type.eq("memory-backend-memfd"), }; + if (zone_config.mem_path.is_none() && mem_type.eq("memory-backend-file")) + || (zone_config.mem_path.is_some() && mem_type.ne("memory-backend-file")) + { + bail!("Object type: {} config path err", mem_type); + } + + if self.object.mem_object.get(&zone_config.id).is_none() { + self.object + .mem_object + .insert(zone_config.id.clone(), zone_config.clone()); + } else { + bail!("Object: {} has been added", zone_config.id); + } + if zone_config.host_numa_nodes.is_none() { return Ok(zone_config); } @@ -993,7 +1046,10 @@ mod tests { fn test_add_mem_zone() { let mut vm_config = VmConfig::default(); let zone_config_1 = vm_config - .add_mem_zone("-object memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind") + .add_mem_zone( + "-object memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind", + String::from("memory-backend-ram"), + ) .unwrap(); assert_eq!(zone_config_1.id, "mem1"); assert_eq!(zone_config_1.size, 2147483648); @@ -1002,21 +1058,37 @@ mod tests { let zone_config_2 = vm_config .add_mem_zone( - "-object memory-backend-ram,size=2G,id=mem1,host-nodes=1-2,policy=default", + "-object memory-backend-ram,size=2G,id=mem2,host-nodes=1-2,policy=default", + String::from("memory-backend-ram"), ) .unwrap(); assert_eq!(zone_config_2.host_numa_nodes, Some(vec![1, 2])); let zone_config_3 = vm_config - .add_mem_zone("-object memory-backend-ram,size=2M,id=mem1,share=on") + .add_mem_zone( + "-object memory-backend-ram,size=2M,id=mem3,share=on", + String::from("memory-backend-ram"), + ) .unwrap(); assert_eq!(zone_config_3.size, 2 * 1024 * 1024); assert_eq!(zone_config_3.share, true); let zone_config_4 = vm_config - .add_mem_zone("-object memory-backend-ram,size=2M,id=mem1") + .add_mem_zone( + "-object memory-backend-ram,size=2M,id=mem4", + String::from("memory-backend-ram"), + ) .unwrap(); assert_eq!(zone_config_4.share, false); + assert_eq!(zone_config_4.memfd, false); + + let zone_config_5 = vm_config + .add_mem_zone( + "-object memory-backend-memfd,size=2M,id=mem5", + String::from("memory-backend-memfd"), + ) + .unwrap(); + assert_eq!(zone_config_5.memfd, true); } #[test] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index cc677dfa9..cc4345dbc 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -210,14 +210,8 @@ impl VmConfig { bail!("Object: {} has been added", id); } } - "memory-backend-ram" => { - let config = self.add_mem_zone(object_args)?; - let id = config.id.clone(); - if self.object.mem_object.get(&id).is_none() { - self.object.mem_object.insert(id, config); - } else { - bail!("Object: {} has been added", id); - } + "memory-backend-ram" | "memory-backend-file" | "memory-backend-memfd" => { + self.add_mem_zone(object_args, device_type)?; } "tls-creds-x509" => { self.add_tlscred(object_args)?; @@ -671,6 +665,16 @@ pub fn check_arg_too_long(arg: &str, name: &str) -> Result<()> { Ok(()) } +pub fn check_path_too_long(arg: &str, name: &str) -> Result<()> { + if arg.len() > MAX_PATH_LENGTH { + bail!(ConfigError::StringLengthTooLong( + name.to_string(), + MAX_PATH_LENGTH + )); + } + Ok(()) +} + pub fn check_arg_nonexist(arg: Option, name: &str, device: &str) -> Result<()> { arg.with_context(|| ConfigError::FieldIsMissing(name.to_string(), device.to_string()))?; -- Gitee From 4b00897cac6815d49a43f09bf0d0be0e81472631 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 4 Jul 2023 15:52:27 +0800 Subject: [PATCH 1195/2187] memory: region tree reconstitution 1.memory region and address_space add name message 2.add alias region type to contact the memory of the front and back view of the front and back ends Signed-off-by: jiewangqun --- address_space/src/address.rs | 13 +- address_space/src/address_space.rs | 58 ++--- address_space/src/host_mmap.rs | 235 ++++++++++----------- address_space/src/lib.rs | 8 +- address_space/src/listener.rs | 2 +- address_space/src/region.rs | 175 ++++++++++----- address_space/src/state.rs | 2 +- boot_loader/src/lib.rs | 4 +- boot_loader/src/x86_64/bootparam.rs | 6 +- boot_loader/src/x86_64/direct_boot/mod.rs | 6 +- devices/src/acpi/ged.rs | 2 +- devices/src/acpi/power.rs | 2 +- devices/src/legacy/fwcfg.rs | 19 +- devices/src/legacy/pflash.rs | 14 +- devices/src/legacy/pl011.rs | 2 +- devices/src/legacy/pl031.rs | 2 +- devices/src/legacy/rtc.rs | 2 +- devices/src/legacy/serial.rs | 2 +- devices/src/misc/ivshmem.rs | 2 +- devices/src/misc/scream/mod.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 28 ++- machine/src/lib.rs | 68 +++--- machine/src/micro_vm/mod.rs | 74 +++++-- machine/src/standard_vm/aarch64/mod.rs | 40 +++- machine/src/standard_vm/mod.rs | 34 +-- machine/src/standard_vm/x86_64/ich9_lpc.rs | 10 +- machine/src/standard_vm/x86_64/mch.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 64 ++++-- machine_manager/src/config/numa.rs | 8 + pci/src/bus.rs | 9 +- pci/src/config.rs | 21 +- pci/src/demo_dev.rs | 4 +- pci/src/host.rs | 9 +- pci/src/msix.rs | 6 +- pci/src/root_port.rs | 4 +- sysbus/src/lib.rs | 3 +- vfio/src/vfio_pci.rs | 17 +- vhost_user_fs/src/virtio_fs.rs | 9 +- virtio/src/device/balloon.rs | 8 +- virtio/src/device/block.rs | 6 +- virtio/src/device/rng.rs | 6 +- virtio/src/queue/split.rs | 6 +- virtio/src/transport/virtio_mmio.rs | 8 +- virtio/src/transport/virtio_pci.rs | 81 +++++-- virtio/src/vhost/kernel/net.rs | 6 +- virtio/src/vhost/kernel/vsock.rs | 4 +- 46 files changed, 691 insertions(+), 402 deletions(-) diff --git a/address_space/src/address.rs b/address_space/src/address.rs index e7760c3e1..a5ace6f03 100644 --- a/address_space/src/address.rs +++ b/address_space/src/address.rs @@ -166,17 +166,20 @@ impl AddressRange { /// /// * `other` - Other AddressRange. pub fn find_intersection(&self, other: AddressRange) -> Option { - let end = self.base.checked_add(self.size)?; - let other_end = other.base.checked_add(other.size)?; + let begin = self.base.raw_value() as u128; + let end = self.size as u128 + begin; + let other_begin = other.base.raw_value() as u128; + let other_end = other.size as u128 + other_begin; - if end <= other.base || other_end <= self.base { + if end <= other_begin || other_end <= begin { return None; } - let start = std::cmp::max(self.base, other.base); + let size_inter = (std::cmp::min(end, other_end) - start.0 as u128) as u64; + Some(AddressRange { base: start, - size: std::cmp::min(end, other_end).offset_from(start), + size: size_inter, }) } diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 7a604bc38..7a9457677 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use arc_swap::ArcSwap; use std::fmt; use std::fmt::Debug; @@ -53,6 +53,8 @@ type ListenerObj = Arc>; /// Address Space of memory. #[derive(Clone)] pub struct AddressSpace { + /// the name of AddressSpace. + name: String, /// Root Region of this AddressSpace. root: Region, /// `flat_view` is the output of rendering all regions in parent `address-space`, @@ -80,8 +82,10 @@ impl AddressSpace { /// # Arguments /// /// * `root` - Root region of address space. - pub fn new(root: Region) -> Result> { + /// * `name` - the name of AddressSpace. + pub fn new(root: Region, name: &str) -> Result> { let space = Arc::new(AddressSpace { + name: String::from(name), root: root.clone(), flat_view: Arc::new(ArcSwap::new(Arc::new(FlatView::default()))), listeners: Arc::new(Mutex::new(Vec::new())), @@ -757,8 +761,8 @@ mod test { } } - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(8000, "root"); + let space = AddressSpace::new(root, "space").unwrap(); let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener3 = Arc::new(Mutex::new(ListenerPrior3::default())); @@ -808,8 +812,8 @@ mod test { } } - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(8000, "root"); + let space = AddressSpace::new(root, "space").unwrap(); let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); space.register_listener(listener1.clone()).unwrap(); @@ -824,8 +828,8 @@ mod test { #[test] fn test_update_topology() { - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(8000, "root"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let listener = Arc::new(Mutex::new(TestListener::default())); space.register_listener(listener.clone()).unwrap(); @@ -842,8 +846,8 @@ mod test { // // the flat_view is as follows, region-b is container which will not appear in the flat-view // [CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC] - let region_b = Region::init_container_region(4000); - let region_c = Region::init_io_region(6000, default_ops.clone()); + let region_b = Region::init_container_region(4000, "region_b"); + let region_c = Region::init_io_region(6000, default_ops.clone(), "region_c"); region_b.set_priority(2); region_c.set_priority(1); root.add_subregion(region_b.clone(), 2000).unwrap(); @@ -873,7 +877,7 @@ mod test { // D: [DDDDDD] // the flat_view is as follows, // [CCCCCCCCCCCC][DDDDDD][CCCCCCCCCCCCCCCCCCC] - let region_d = Region::init_io_region(1000, default_ops); + let region_d = Region::init_io_region(1000, default_ops, "region_d"); region_b.add_subregion(region_d.clone(), 0).unwrap(); let locked_listener = listener.lock().unwrap(); @@ -922,15 +926,15 @@ mod test { // c: [CCCCCCCCCCCCC] // the flat_view is as follows, // [BBBBBBBBBBBBB][CCCCC] - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(8000, "region"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let listener = Arc::new(Mutex::new(TestListener::default())); space.register_listener(listener.clone()).unwrap(); - let region_b = Region::init_io_region(2000, default_ops.clone()); + let region_b = Region::init_io_region(2000, default_ops.clone(), "region_b"); region_b.set_priority(1); region_b.set_ioeventfds(&ioeventfds); - let region_c = Region::init_io_region(2000, default_ops); + let region_c = Region::init_io_region(2000, default_ops, "region_c"); region_c.set_ioeventfds(&ioeventfds); root.add_subregion(region_c, 2000).unwrap(); @@ -984,13 +988,13 @@ mod test { // c: [CCCCCC] // the flat_view is as follows, // [CCCCCC] - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(8000, "root"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let listener = Arc::new(Mutex::new(TestListener::default())); space.register_listener(listener.clone()).unwrap(); - let region_b = Region::init_container_region(5000); - let region_c = Region::init_io_region(1000, default_ops); + let region_b = Region::init_container_region(5000, "root"); + let region_c = Region::init_io_region(1000, default_ops, "region_c"); region_c.set_ioeventfds(&ioeventfds); region_b.add_subregion(region_c, 1000).unwrap(); @@ -1006,8 +1010,8 @@ mod test { #[test] fn test_get_ram_info() { - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(8000, "root"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let default_ops = RegionOps { read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }), write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }), @@ -1019,8 +1023,8 @@ mod test { let ram2 = Arc::new( HostMemMapping::new(GuestAddress(2000), None, 1000, None, false, false, false).unwrap(), ); - let region_a = Region::init_ram_region(ram1.clone()); - let region_b = Region::init_ram_region(ram2.clone()); + let region_a = Region::init_ram_region(ram1.clone(), "region_a"); + let region_b = Region::init_ram_region(ram2.clone(), "region_b"); root.add_subregion(region_a, ram1.start_address().raw_value()) .unwrap(); root.add_subregion(region_b, ram2.start_address().raw_value()) @@ -1052,7 +1056,7 @@ mod test { // c: [CCCCCCCCC] // the flat_view is as follows, // [AAAAAA][CCCCCCCCC][BB] - let region_c = Region::init_io_region(1500, default_ops); + let region_c = Region::init_io_region(1500, default_ops, "region_c"); region_c.set_priority(1); root.add_subregion(region_c, 1000).unwrap(); @@ -1079,12 +1083,12 @@ mod test { #[test] fn test_write_and_read_object() { - let root = Region::init_container_region(8000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(8000, "root"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let ram1 = Arc::new( HostMemMapping::new(GuestAddress(0), None, 1000, None, false, false, false).unwrap(), ); - let region_a = Region::init_ram_region(ram1.clone()); + let region_a = Region::init_ram_region(ram1.clone(), "region_a"); root.add_subregion(region_a, ram1.start_address().raw_value()) .unwrap(); diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 13f3364f5..6ad148fae 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -24,7 +24,7 @@ use util::{ unix::{do_mmap, host_page_size}, }; -use crate::{AddressRange, GuestAddress}; +use crate::{AddressRange, GuestAddress, Region}; const MAX_PREALLOC_THREAD: u8 = 16; /// Verify existing pages in the mapping. @@ -204,7 +204,7 @@ fn touch_pages(start: u64, page_size: u64, nr_pages: u64) { /// * `host_addr` - The start host address to pre allocate. /// * `size` - Size of memory. /// * `nr_vcpus` - Number of vcpus. -fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { +pub fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { let page_size = host_page_size(); let threads = max_nr_threads(nr_vcpus); let nr_pages = (size + page_size - 1) / page_size; @@ -232,27 +232,16 @@ fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { } } -/// Create HostMemMappings according to address ranges. +/// If the memory is not configured numa, use this /// /// # Arguments /// -/// * `ranges` - The guest address range that will be mapped. -/// * `mem_config` - Machine memory config. -pub fn create_host_mmaps( - ranges: &[(u64, u64)], - mem_config: &MachineMemConfig, - nr_vcpus: u8, -) -> Result>> { +/// * `mem_config` - The config of default memory. +/// * `thread_num` - The num of mem preallocv threads, typically the number of vCPUs. +pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Result { let mut f_back: Option = None; - if let Some(path) = &mem_config.mem_path { - let file_len = ranges.iter().fold(0, |acc, x| acc + x.1); - f_back = Some( - FileBackend::new_mem(path, file_len) - .with_context(|| "Failed to create file that backs memory")?, - ); - } else if mem_config.mem_share { - let file_len = ranges.iter().fold(0, |acc, x| acc + x.1); + if mem_config.mem_share { let anon_mem_name = String::from("stratovirt_anon_mem"); let anon_fd = @@ -263,7 +252,7 @@ pub fn create_host_mmaps( let anon_file = unsafe { File::from_raw_fd(anon_fd) }; anon_file - .set_len(file_len) + .set_len(mem_config.mem_size) .with_context(|| "Failed to set the length of anonymous file that backs memory")?; f_back = Some(FileBackend { @@ -271,39 +260,80 @@ pub fn create_host_mmaps( offset: 0, page_size: host_page_size(), }); + } else if let Some(path) = &mem_config.mem_path { + f_back = Some( + FileBackend::new_mem(path, mem_config.mem_size) + .with_context(|| "Failed to create file that backs memory")?, + ); } - - let backend = f_back.as_ref(); - let mut host_addr = do_mmap( - &backend.map(|fb| fb.file.as_ref()), + let block = Arc::new(HostMemMapping::new( + GuestAddress(0), + None, mem_config.mem_size, - backend.map_or(0, |fb| fb.offset), - false, - mem_config.mem_share, + f_back, mem_config.dump_guest_core, - )?; + mem_config.mem_share, + false, + )?); + if mem_config.mem_prealloc { - mem_prealloc(host_addr, mem_config.mem_size, nr_vcpus); + mem_prealloc(block.host_address(), mem_config.mem_size, thread_num); } - let mut mappings = Vec::new(); - for range in ranges.iter() { - mappings.push(Arc::new(HostMemMapping::new( - GuestAddress(range.0), - Some(host_addr), - range.1, - f_back.clone(), - mem_config.dump_guest_core, - mem_config.mem_share, - false, - )?)); - host_addr += range.1; + let region = Region::init_ram_region(block, "DefaultRam"); - if let Some(mut fb) = f_back.as_mut() { - fb.offset += range.1 + Ok(region) +} + +/// If the memory is configured numa, use this +/// +/// # Arguments +/// +/// * `mem_config` - The config of default memory. +/// * `thread_num` - The num of mem preallocv threads, typically the number of vCPUs. +pub fn create_backend_mem(mem_config: &MemZoneConfig, thread_num: u8) -> Result { + let mut f_back: Option = None; + + if mem_config.memfd { + let anon_mem_name = String::from("stratovirt_anon_mem"); + + let anon_fd = + unsafe { libc::syscall(libc::SYS_memfd_create, anon_mem_name.as_ptr(), 0) } as RawFd; + if anon_fd < 0 { + return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } + + let anon_file = unsafe { File::from_raw_fd(anon_fd) }; + anon_file + .set_len(mem_config.size) + .with_context(|| "Failed to set the length of anonymous file that backs memory")?; + + f_back = Some(FileBackend { + file: Arc::new(anon_file), + offset: 0, + page_size: host_page_size(), + }); + } else if let Some(path) = &mem_config.mem_path { + f_back = Some( + FileBackend::new_mem(path, mem_config.size) + .with_context(|| "Failed to create file that backs memory")?, + ); } + let block = Arc::new(HostMemMapping::new( + GuestAddress(0), + None, + mem_config.size, + f_back, + mem_config.dump_guest_core, + mem_config.share, + false, + )?); + if mem_config.prealloc { + mem_prealloc(block.host_address(), mem_config.size, thread_num); + } + set_host_memory_policy(&block, mem_config)?; - Ok(mappings) + let region = Region::init_ram_region(block, mem_config.id.as_str()); + Ok(region) } /// Set host memory backend numa policy. @@ -311,50 +341,42 @@ pub fn create_host_mmaps( /// # Arguments /// /// * `mem_mappings` - The host virtual address of mapped memory information. -/// * `mem_zones` - Memory zone config. +/// * `zone` - Memory zone config info. pub fn set_host_memory_policy( - mem_mappings: &[Arc], - mem_zones: &Option>, + mem_mappings: &Arc, + zone: &MemZoneConfig, ) -> Result<()> { - if mem_zones.is_none() || mem_mappings.is_empty() { + if zone.host_numa_nodes.is_none() { return Ok(()); } - - let mut host_addr_start = mem_mappings.get(0).map(|m| m.host_address()).unwrap(); - for zone in mem_zones.as_ref().unwrap() { - if zone.host_numa_nodes.is_none() { - continue; - } - - let nodes = zone.host_numa_nodes.as_ref().unwrap(); - let mut max_node = nodes[nodes.len() - 1] as usize; - - let mut nmask: Vec = Vec::new(); - nmask.resize(max_node / 64 + 1, 0); - for node in nodes.iter() { - nmask[(*node / 64) as usize] |= 1_u64 << (*node % 64); - } - // We need to pass node_id + 1 as mbind() max_node argument. - // It is kind of linux bug or feature which will cut off the last node. - max_node += 1; - - let policy = HostMemPolicy::from(zone.policy.clone()); - if policy == HostMemPolicy::Default { - max_node = 0; - nmask = vec![0_u64; max_node]; - } - - mbind( - host_addr_start, - zone.size, - policy as u32, - nmask, - max_node as u64, - MPOL_MF_STRICT | MPOL_MF_MOVE, - ) - .with_context(|| "Failed to call mbind")?; - host_addr_start += zone.size; + let host_addr_start = mem_mappings.host_address(); + let nodes = zone.host_numa_nodes.as_ref().unwrap(); + let mut max_node = nodes[nodes.len() - 1] as usize; + + let mut nmask: Vec = Vec::new(); + nmask.resize(max_node / 64 + 1, 0); + for node in nodes.iter() { + nmask[(*node / 64) as usize] |= 1_u64 << (*node % 64); } + // We need to pass node_id + 1 as mbind() max_node argument. + // It is kind of linux bug or feature which will cut off the last node. + max_node += 1; + + let policy = HostMemPolicy::from(zone.policy.clone()); + if policy == HostMemPolicy::Default { + max_node = 0; + nmask = vec![0_u64; max_node]; + } + + mbind( + host_addr_start, + zone.size, + policy as u32, + nmask, + max_node as u64, + MPOL_MF_STRICT | MPOL_MF_MOVE, + ) + .with_context(|| "Failed to call mbind")?; Ok(()) } @@ -368,6 +390,8 @@ pub struct HostMemMapping { host_addr: *mut u8, /// Represents file and offset-in-file that backs this mapping. file_back: Option, + /// share mem flag + is_share: bool, } // Send and Sync is not auto-implemented for raw pointer type @@ -418,6 +442,7 @@ impl HostMemMapping { }, host_addr: host_addr as *mut u8, file_back, + is_share, }) } @@ -442,6 +467,10 @@ impl HostMemMapping { pub fn file_backend(&self) -> Option { self.file_back.clone() } + + pub fn mem_shared(&self) -> bool { + self.is_share + } } impl Drop for HostMemMapping { @@ -557,48 +586,6 @@ mod test { std::fs::remove_file(file_path).unwrap(); } - #[test] - fn test_create_host_mmaps() { - let addr_ranges = [(0x0, 0x10_0000), (0x100000, 0x10_0000)]; - let mem_path = std::env::current_dir() - .unwrap() - .as_path() - .to_str() - .unwrap() - .to_string(); - let mem_config = MachineMemConfig { - mem_size: 0x20_0000, - mem_path: Some(mem_path), - dump_guest_core: false, - mem_share: false, - mem_prealloc: false, - mem_zones: None, - }; - - let host_mmaps = create_host_mmaps(&addr_ranges, &mem_config, 1).unwrap(); - assert_eq!(host_mmaps.len(), 2); - - // check the start address and size of HostMemMapping - for (index, mmap) in host_mmaps.iter().enumerate() { - assert_eq!(mmap.start_address().raw_value(), addr_ranges[index].0); - assert_eq!(mmap.size(), addr_ranges[index].1); - assert!(mmap.file_backend().is_some()); - } - - // check the file backends' total size, should equal to mem_size in config. - let total_file_size = host_mmaps[0] - .file_backend() - .unwrap() - .file - .metadata() - .unwrap() - .len(); - let total_mem_size = addr_ranges.iter().fold(0_u64, |acc, x| acc + x.1); - let total_mmaps_size = host_mmaps.iter().fold(0_u64, |acc, x| acc + x.size()); - assert_eq!(total_mem_size, total_file_size); - assert_eq!(total_mem_size, total_mmaps_size); - } - #[test] fn test_memory_prealloc() { // Mmap and prealloc with anonymous memory. diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 047f86934..233a037af 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -34,7 +34,7 @@ //! //! fn main() { //! // 1. create address_space -//! let space = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); +//! let space = AddressSpace::new(Region::init_container_region(u64::max_value(), "space"), "space").unwrap(); //! //! // 2. create an Ram-type Region, and set it's priority //! let mem_mapping = Arc::new(HostMemMapping::new( @@ -46,7 +46,7 @@ //! false, //! false, //! ).unwrap()); -//! let ram_region = Region::init_ram_region(mem_mapping.clone()); +//! let ram_region = Region::init_ram_region(mem_mapping.clone(), "ram"); //! ram_region.set_priority(10); //! //! // 3. create a IO-type Region @@ -66,7 +66,7 @@ //! write: Arc::new(write_ops), //! }; //! -//! let io_region = Region::init_io_region(0x1000, dev_ops); +//! let io_region = Region::init_io_region(0x1000, dev_ops, "io_region"); //! //! // 4. add sub_region to address_space's root region //! space.root().add_subregion(ram_region, mem_mapping.start_address().raw_value()); @@ -89,7 +89,7 @@ pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; pub use anyhow::Result; pub use error::AddressSpaceError; -pub use host_mmap::{create_host_mmaps, set_host_memory_policy, FileBackend, HostMemMapping}; +pub use host_mmap::{create_backend_mem, create_default_mem, FileBackend, HostMemMapping}; #[cfg(target_arch = "x86_64")] pub use listener::KvmIoListener; pub use listener::KvmMemoryListener; diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index aa5a755c2..2bbd167a3 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -642,7 +642,7 @@ mod test { mem_mapping.start_address().unchecked_add(offset_in_region), mem_mapping.size() - offset_in_region, ), - owner: Region::init_ram_region(mem_mapping.clone()), + owner: Region::init_ram_region(mem_mapping.clone(), "ram"), offset_in_region, rom_dev_romd: None, } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 628e9dfd2..927864890 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -40,11 +40,15 @@ pub enum RegionType { RomDevice, /// RamDevice type. RamDevice, + /// Alias type + Alias, } /// Represents a memory region, used by mem-mapped IO, Ram or Rom. #[derive(Clone)] pub struct Region { + /// The name of Region + pub name: String, /// Type of Region, won't be changed once initialized. region_type: RegionType, /// The priority of Region, only valid in parent Container-type Region. @@ -67,6 +71,10 @@ pub struct Region { rom_dev_romd: Arc, /// Max access size supported by the device. max_access_size: Option, + /// Point to entity memory region + alias: Option>, + /// Offset in parent Alias-type region. + alias_offset: u64, } impl fmt::Debug for Region { @@ -232,12 +240,14 @@ impl Region { /// * `mem_mapping` - Mapped memory. /// * `ops` - Region operations. fn init_region_internal( + name: &str, size: u64, region_type: RegionType, mem_mapping: Option>, ops: Option, ) -> Region { Region { + name: String::from(name), region_type, priority: Arc::new(AtomicI32::new(0)), offset: Arc::new(Mutex::new(GuestAddress(0))), @@ -249,6 +259,8 @@ impl Region { subregions: Arc::new(RwLock::new(Vec::new())), rom_dev_romd: Arc::new(AtomicBool::new(false)), max_access_size: None, + alias: None, + alias_offset: 0_u64, } } @@ -257,8 +269,14 @@ impl Region { /// # Arguments /// /// * `mem_mapping` - Mapped memory of this Ram region. - pub fn init_ram_region(mem_mapping: Arc) -> Region { - Region::init_region_internal(mem_mapping.size(), RegionType::Ram, Some(mem_mapping), None) + pub fn init_ram_region(mem_mapping: Arc, name: &str) -> Region { + Region::init_region_internal( + name, + mem_mapping.size(), + RegionType::Ram, + Some(mem_mapping), + None, + ) } /// Initialize IO-type region. @@ -267,8 +285,8 @@ impl Region { /// /// * `size` - Size of IO region. /// * `ops` - Operation of Region. - pub fn init_io_region(size: u64, ops: RegionOps) -> Region { - Region::init_region_internal(size, RegionType::IO, None, Some(ops)) + pub fn init_io_region(size: u64, ops: RegionOps, name: &str) -> Region { + Region::init_region_internal(name, size, RegionType::IO, None, Some(ops)) } /// Set the access size limit of the IO region. @@ -285,8 +303,8 @@ impl Region { /// # Arguments /// /// * `size` - Size of container region. - pub fn init_container_region(size: u64) -> Region { - Region::init_region_internal(size, RegionType::Container, None, None) + pub fn init_container_region(size: u64, name: &str) -> Region { + Region::init_region_internal(name, size, RegionType::Container, None, None) } /// Initialize RomDevice-type region. @@ -295,8 +313,13 @@ impl Region { /// /// * `mem_mapping` - Mapped memory of this region. /// * `ops` - Operation functions of this region. - pub fn init_rom_device_region(mem_mapping: Arc, ops: RegionOps) -> Region { + pub fn init_rom_device_region( + mem_mapping: Arc, + ops: RegionOps, + name: &str, + ) -> Region { let mut region = Region::init_region_internal( + name, mem_mapping.size(), RegionType::RomDevice, Some(mem_mapping), @@ -312,8 +335,9 @@ impl Region { /// # Arguments /// /// * `mem_mapping` - Mapped memory of this region. - pub fn init_ram_device_region(mem_mapping: Arc) -> Region { + pub fn init_ram_device_region(mem_mapping: Arc, name: &str) -> Region { Region::init_region_internal( + name, mem_mapping.size(), RegionType::RamDevice, Some(mem_mapping), @@ -321,6 +345,26 @@ impl Region { ) } + /// Initialize alias-type region. + /// + /// # Arguments + /// + /// * `alias` - alias to region. + /// * `alias_offset` - offset of alias + /// * `size` - region size + /// * `name` - alias name + pub fn init_alias_region( + alias: Arc, + alias_offset: u64, + size: u64, + name: &str, + ) -> Region { + let mut region = Region::init_region_internal(name, size, RegionType::Alias, None, None); + region.alias = Some(alias); + region.alias_offset = alias_offset; + region + } + /// Get the type of this region. pub fn region_type(&self) -> RegionType { self.region_type @@ -345,6 +389,16 @@ impl Region { self.size.load(Ordering::SeqCst) } + /// Get offset of this region. + pub fn alias_offset(&self) -> u64 { + self.alias_offset + } + + /// Get name of this alias region. + pub fn alias_name(&self) -> Option { + self.alias.as_ref().map(|mr| mr.name.clone()) + } + /// Get the offset of this region. /// The offset is within its parent region or belonged address space. pub fn offset(&self) -> GuestAddress { @@ -415,7 +469,10 @@ impl Region { /// Get the host address if this region is backed by host-memory, /// Return `None` if it is not a Ram-type region. pub fn get_host_address(&self) -> Option { - if self.region_type == RegionType::IO || self.region_type == RegionType::Container { + if self.region_type != RegionType::Ram + && self.region_type != RegionType::RamDevice + && self.region_type != RegionType::RomDevice + { return None; } self.mem_mapping.as_ref().map(|r| r.host_address()) @@ -658,21 +715,13 @@ impl Region { self.io_evtfds.lock().unwrap().to_vec() } - /// Add sub-region to this region. + /// Add sub-region to this region, but not produce flat view /// /// # Arguments /// /// * `child` - Subregion of this region. - /// * `offset` - Offset of subregion. - /// - /// # Errors - /// - /// Return Error if - /// * This region is not a Container. - /// * The argument `offset` plus child region's size overflows or exceed this region's size. - /// * The child-region already exists in sub-regions array. - /// * Failed to generate flat view (topology changed after adding sub-region). - pub fn add_subregion(&self, child: Region, offset: u64) -> Result<()> { + /// * `offset` - Offset of subregion + pub fn add_subregion_not_update(&self, child: Region, offset: u64) -> Result<()> { // check parent Region's property, and check if child Region's offset is valid or not if self.region_type() != RegionType::Container { return Err(anyhow!(AddressSpaceError::RegionType(self.region_type()))); @@ -705,6 +754,26 @@ impl Region { sub_regions.insert(index, child); drop(sub_regions); + Ok(()) + } + + /// Add sub-region to this region and production flat view + /// + /// # Arguments + /// + /// * `child` - Subregion of this region. + /// * `offset` - Offset of subregion. + /// + /// # Errors + /// + /// Return Error if + /// * This region is not a Container. + /// * The argument `offset` plus child region's size overflows or exceed this region's size. + /// * The child-region already exists in sub-regions array. + /// * Failed to generate flat view (topology changed after adding sub-region). + pub fn add_subregion(&self, child: Region, offset: u64) -> Result<()> { + self.add_subregion_not_update(child, offset)?; + if let Some(space) = self.space.read().unwrap().upgrade() { space .update_topology() @@ -781,20 +850,14 @@ impl Region { addr_range: AddressRange, flat_view: &mut FlatView, ) -> Result<()> { + let region_base = base.unchecked_add(self.offset().raw_value()); + let region_range = AddressRange::new(region_base, self.size()); + let intersect = match region_range.find_intersection(addr_range) { + Some(r) => r, + None => return Ok(()), + }; match self.region_type { RegionType::Container => { - let region_base = base.unchecked_add(self.offset().raw_value()); - let region_range = AddressRange::new(region_base, self.size()); - let intersect = match region_range.find_intersection(addr_range) { - Some(r) => r, - None => bail!( - "Generate flat view failed: region_addr 0x{:X} exceeds parent region range (0x{:X}, 0x{:X})", - region_base.raw_value(), - addr_range.base.raw_value(), - addr_range.size - ), - }; - for sub_r in self.subregions.read().unwrap().iter() { sub_r .render_region_pass(region_base, intersect, flat_view) @@ -808,6 +871,22 @@ impl Region { })?; } } + RegionType::Alias => { + if let Some(alias_region) = &self.alias { + let alias_base = region_base + .unchecked_sub(self.alias_offset) + .unchecked_sub(alias_region.offset().raw_value()); + alias_region.render_region_pass(alias_base, intersect, flat_view).with_context(|| { + format!( + "Failed to render subregion, alias_base 0x{:X}, intersect (0x{:X}, 0x{:X})", + alias_base.raw_value(), + intersect.base.raw_value(), + intersect.size + ) + })?; + } + } + RegionType::Ram | RegionType::IO | RegionType::RomDevice | RegionType::RamDevice => { self.render_terminate_region(base, addr_range, flat_view) .with_context(|| @@ -980,7 +1059,7 @@ mod test { let mem_mapping = Arc::new( HostMemMapping::new(GuestAddress(0), None, 1024, None, false, false, false).unwrap(), ); - let ram_region = Region::init_ram_region(mem_mapping.clone()); + let ram_region = Region::init_ram_region(mem_mapping.clone(), "mem_mapping"); let data: [u8; 10] = [10; 10]; let mut res_data: [u8; 10] = [0; 10]; let count = data.len() as u64; @@ -1016,7 +1095,7 @@ mod test { let host_mmap = Arc::new( HostMemMapping::new(GuestAddress(0), None, 1024, None, false, false, false).unwrap(), ); - let ram_region = Region::init_ram_region(host_mmap); + let ram_region = Region::init_ram_region(host_mmap, "mem_mapping"); let file = TempFile::new().unwrap(); let mut file_read = std::fs::File::open(file.as_path()).unwrap(); @@ -1065,7 +1144,7 @@ mod test { write: Arc::new(write_ops), }; - let io_region = Region::init_io_region(16, test_dev_ops.clone()); + let io_region = Region::init_io_region(16, test_dev_ops.clone(), "io_region"); let data = [0x01u8; 8]; let mut data_res = [0x0u8; 8]; let count = data.len() as u64; @@ -1135,7 +1214,7 @@ mod test { // test add/del sub-region to container-region, and check priority #[test] fn test_add_del_subregion() { - let container = Region::init_container_region(1 << 10); + let container = Region::init_container_region(1 << 10, "root"); assert_eq!(container.region_type(), RegionType::Container); assert_eq!(container.priority(), 0); @@ -1144,8 +1223,8 @@ mod test { write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }), }; - let io_region = Region::init_io_region(1 << 4, default_ops.clone()); - let io_region2 = Region::init_io_region(1 << 4, default_ops.clone()); + let io_region = Region::init_io_region(1 << 4, default_ops.clone(), "io1"); + let io_region2 = Region::init_io_region(1 << 4, default_ops.clone(), "io2"); io_region2.set_priority(10); // add duplicate io-region or ram-region will fail @@ -1201,11 +1280,11 @@ mod test { // the flat_view is as follows // [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] { - let region_a = Region::init_container_region(8000); - let region_b = Region::init_container_region(4000); - let region_c = Region::init_io_region(6000, default_ops.clone()); - let region_d = Region::init_io_region(1000, default_ops.clone()); - let region_e = Region::init_io_region(1000, default_ops.clone()); + let region_a = Region::init_container_region(8000, "region_a"); + let region_b = Region::init_container_region(4000, "region_b"); + let region_c = Region::init_io_region(6000, default_ops.clone(), "region_c"); + let region_d = Region::init_io_region(1000, default_ops.clone(), "region_d"); + let region_e = Region::init_io_region(1000, default_ops.clone(), "region_e"); region_b.set_priority(2); region_c.set_priority(1); @@ -1247,11 +1326,11 @@ mod test { // the flat_view is as follows // [CCCCCC] [DDDDDDDDDDDD][EEEEEEEEEEEEE] { - let region_a = Region::init_container_region(8000); - let region_b = Region::init_container_region(5000); - let region_c = Region::init_io_region(1000, default_ops.clone()); - let region_d = Region::init_io_region(3000, default_ops.clone()); - let region_e = Region::init_io_region(2000, default_ops.clone()); + let region_a = Region::init_container_region(8000, "region_a"); + let region_b = Region::init_container_region(5000, "region_b"); + let region_c = Region::init_io_region(1000, default_ops.clone(), "regionc"); + let region_d = Region::init_io_region(3000, default_ops.clone(), "region_d"); + let region_e = Region::init_io_region(2000, default_ops.clone(), "region_e"); region_a.add_subregion(region_b.clone(), 2000).unwrap(); region_a.add_subregion(region_c.clone(), 0).unwrap(); diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 1d9422a85..71099a83c 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -128,7 +128,7 @@ impl MigrationHook for AddressSpace { ); self.root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "HostMem"), host_mmap.start_address().raw_value(), ) .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?; diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index 6dfb2e876..c2513ab13 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -40,7 +40,7 @@ //! //! #[cfg(target_arch="x86_64")] //! fn main() { -//! let guest_mem = AddressSpace::new(Region::init_container_region(std::u64::MAX)).unwrap(); +//! let guest_mem = AddressSpace::new(Region::init_container_region(std::u64::MAX, "guest_mem"), "guest_mem").unwrap(); //! let kernel_file = std::path::PathBuf::from("/path/to/my/kernel"); //! let bootloader_config = BootLoaderConfig { //! kernel: Some(kernel_file), @@ -60,7 +60,7 @@ //! //! #[cfg(target_arch="aarch64")] //! fn main() { -//! let guest_mem = AddressSpace::new(Region::init_container_region(u64::MAX)).unwrap(); +//! let guest_mem = AddressSpace::new(Region::init_container_region(u64::MAX, "guest_mem"), "guest_mem").unwrap(); //! let kernel_file = std::path::PathBuf::from("/path/to/my/kernel"); //! let bootloader_config = BootLoaderConfig { //! kernel: Some(kernel_file), diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs index ce023680f..5d944b62d 100644 --- a/boot_loader/src/x86_64/bootparam.rs +++ b/boot_loader/src/x86_64/bootparam.rs @@ -233,8 +233,8 @@ mod test { #[test] fn test_boot_param() { - let root = Region::init_container_region(0x2000_0000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(0x2000_0000, "root"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let ram1 = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -247,7 +247,7 @@ mod test { ) .unwrap(), ); - let region_a = Region::init_ram_region(ram1.clone()); + let region_a = Region::init_ram_region(ram1.clone(), "region_a"); root.add_subregion(region_a, ram1.start_address().raw_value()) .unwrap(); diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index a68f3757f..f16afe4f2 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -286,8 +286,8 @@ mod test { #[test] fn test_x86_bootloader_and_kernel_cmdline() { - let root = Region::init_container_region(0x2000_0000); - let space = AddressSpace::new(root.clone()).unwrap(); + let root = Region::init_container_region(0x2000_0000, "root"); + let space = AddressSpace::new(root.clone(), "space").unwrap(); let ram1 = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -300,7 +300,7 @@ mod test { ) .unwrap(), ); - let region_a = Region::init_ram_region(ram1.clone()); + let region_a = Region::init_ram_region(ram1.clone(), "region_a"); root.add_subregion(region_a, ram1.start_address().raw_value()) .unwrap(); assert_eq!(setup_page_table(&space).unwrap(), 0x0000_9000); diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 95741c2b5..1e868b2fd 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -83,7 +83,7 @@ impl Ged { self.battery_present = battery_present; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size)?; + sysbus.attach_device(&dev, region_base, region_size, "Ged")?; let ged = dev.lock().unwrap(); ged.register_acpi_powerdown_event(power_button) diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 731dfef43..ce469f8eb 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -187,7 +187,7 @@ impl PowerDev { .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size)?; + sysbus.attach_device(&dev, region_base, region_size, "PowerDev")?; let pdev_availiable: bool; { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 66a57f93c..432128528 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -853,7 +853,7 @@ impl FwCfgMem { let dev = Arc::new(Mutex::new(self)); sysbus - .attach_device(&dev, region_base, region_size) + .attach_device(&dev, region_base, region_size, "FwCfgMem") .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } @@ -1019,7 +1019,7 @@ impl FwCfgIO { let dev = Arc::new(Mutex::new(self)); sysbus - .attach_device(&dev, region_base, region_size) + .attach_device(&dev, region_base, region_size, "FwCfgIO") .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } @@ -1274,9 +1274,14 @@ mod test { use sysbus::{IRQ_BASE, IRQ_MAX}; fn sysbus_init() -> SysBus { - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sys_mem"), + "sys_mem", + ) + .unwrap(); #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); + let sys_io = + AddressSpace::new(Region::init_container_region(1 << 16, "sys_io"), "sys_io").unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( @@ -1289,8 +1294,8 @@ mod test { } fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "root"); + let sys_space = AddressSpace::new(root, "sys_space").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -1306,7 +1311,7 @@ mod test { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "region_1"), host_mmap.start_address().raw_value(), ) .unwrap(); diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 4431a8661..51281f398 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -214,7 +214,8 @@ impl PFlash { let dev = Arc::new(Mutex::new(self)); let region_ops = sysbus.build_region_ops(&dev); - let rom_region = Region::init_rom_device_region(host_mmap, region_ops); + + let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "PflashRom"); dev.lock().unwrap().rom = Some(rom_region.clone()); sysbus .sys_mem @@ -868,9 +869,14 @@ mod test { use sysbus::{IRQ_BASE, IRQ_MAX}; fn sysbus_init() -> SysBus { - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sys_mem"), + "sys_mem", + ) + .unwrap(); #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); + let sys_io = + AddressSpace::new(Region::init_container_region(1 << 16, "sys_io"), "sys_io").unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( @@ -916,7 +922,7 @@ mod test { .unwrap(), ); - let rom_region = Region::init_rom_device_region(host_mmap, region_ops); + let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "pflash-dev"); dev.lock().unwrap().rom = Some(rom_region); dev } diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 84d5909bf..07345f331 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -170,7 +170,7 @@ impl PL011 { let dev = Arc::new(Mutex::new(self)); sysbus - .attach_device(&dev, region_base, region_size) + .attach_device(&dev, region_base, region_size, "PL011") .with_context(|| "Failed to attach PL011 to system bus.")?; bs.lock().unwrap().kernel_cmdline.push(Param { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 7e4a10fa1..5603b1222 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -108,7 +108,7 @@ impl PL031 { .with_context(|| LegacyError::SetSysResErr)?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size)?; + sysbus.attach_device(&dev, region_base, region_size, "PL031")?; MigrationManager::register_device_instance( PL031State::descriptor(), diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index a2098eb1f..c545b7579 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -267,7 +267,7 @@ impl RTC { self.set_sys_resource(sysbus, region_base, region_size)?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size)?; + sysbus.attach_device(&dev, region_base, region_size, "RTC")?; Ok(()) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 21f5c4a92..82d4593ae 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -152,7 +152,7 @@ impl Serial { .with_context(|| LegacyError::SetSysResErr)?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size)?; + sysbus.attach_device(&dev, region_base, region_size, "Serial")?; MigrationManager::register_device_instance( SerialState::descriptor(), diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 89e9ea1fb..e76e7b787 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -74,7 +74,7 @@ impl Ivshmem { // bar0: mmio register self.config.register_bar( 0, - Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops), + Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops, "IvshmemIo"), RegionType::Mem64Bit, false, IVSHMEM_REG_BAR_SIZE, diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 9c1344f04..99479a74c 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -388,7 +388,7 @@ impl Scream { )?); self.hva = host_mmap.host_address(); - let mem_region = Region::init_ram_region(host_mmap); + let mem_region = Region::init_ram_region(host_mmap, "ivshmem_ram"); let ivshmem = Ivshmem::new("ivshmem".to_string(), devfn, parent_bus, mem_region); ivshmem.realize()?; diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index ae598a162..d33106c01 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -85,21 +85,30 @@ impl XhciPciDevice { dev_id: Arc::new(AtomicU16::new(0)), name: config.id.clone().unwrap(), parent_bus, - mem_region: Region::init_container_region(XHCI_PCI_CONFIG_LENGTH as u64), + mem_region: Region::init_container_region( + XHCI_PCI_CONFIG_LENGTH as u64, + "XhciPciContainer", + ), } } fn mem_region_init(&mut self) -> pci::Result<()> { - let cap_region = - Region::init_io_region(XHCI_PCI_CAP_LENGTH as u64, build_cap_ops(&self.xhci)); + let cap_region = Region::init_io_region( + XHCI_PCI_CAP_LENGTH as u64, + build_cap_ops(&self.xhci), + "XhciPciCapRegion", + ); pci::Result::with_context( self.mem_region .add_subregion(cap_region, XHCI_PCI_CAP_OFFSET as u64), || "Failed to register cap region.", )?; - let mut oper_region = - Region::init_io_region(XHCI_PCI_OPER_LENGTH as u64, build_oper_ops(&self.xhci)); + let mut oper_region = Region::init_io_region( + XHCI_PCI_OPER_LENGTH as u64, + build_oper_ops(&self.xhci), + "XhciPciOperRegion", + ); oper_region.set_access_size(4); pci::Result::with_context( self.mem_region @@ -110,8 +119,11 @@ impl XhciPciDevice { let port_num = self.xhci.lock().unwrap().usb_ports.len(); for i in 0..port_num { let port = &self.xhci.lock().unwrap().usb_ports[i]; - let port_region = - Region::init_io_region(XHCI_PCI_PORT_LENGTH as u64, build_port_ops(port)); + let port_region = Region::init_io_region( + XHCI_PCI_PORT_LENGTH as u64, + build_port_ops(port), + "XhciPciPortRegion", + ); let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i as u32) as u64; pci::Result::with_context(self.mem_region.add_subregion(port_region, offset), || { "Failed to register port region." @@ -121,6 +133,7 @@ impl XhciPciDevice { let mut runtime_region = Region::init_io_region( XHCI_PCI_RUNTIME_LENGTH as u64, build_runtime_ops(&self.xhci), + "XhciPciRuntimeRegion", ); runtime_region.set_access_size(4); pci::Result::with_context( @@ -132,6 +145,7 @@ impl XhciPciDevice { let doorbell_region = Region::init_io_region( XHCI_PCI_DOORBELL_LENGTH as u64, build_doorbell_ops(&self.xhci), + "XhciPciDoorbellRegion", ); pci::Result::with_context( self.mem_region diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fc5ea3ae2..6d93a6844 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -45,7 +45,7 @@ pub use micro_vm::LightMachine; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{ - create_host_mmaps, set_host_memory_policy, AddressSpace, KvmMemoryListener, Region, + create_backend_mem, create_default_mem, AddressSpace, KvmMemoryListener, Region, }; pub use anyhow::Result; use anyhow::{anyhow, bail, Context}; @@ -100,18 +100,6 @@ use virtio::{ }; pub trait MachineOps { - /// Calculate the ranges of memory according to architecture. - /// - /// # Arguments - /// - /// * `mem_size` - memory size of VM. - /// - /// # Returns - /// - /// A array of ranges, it's element represents (start_addr, size). - /// On x86_64, there is a gap ranged from (4G - 768M) to 4G, which will be skipped. - fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)>; - fn build_smbios(&self, fw_cfg: &Arc>) -> Result<()> { let smbioscfg = self.get_vm_config().lock().unwrap().smbios.clone(); @@ -137,6 +125,37 @@ pub trait MachineOps { Ok((&vmcfg.machine_config.cpu_config).into()) } + /// Init memory of vm to architecture. + /// + /// # Arguments + /// + /// * `mem_size` - memory size of VM. + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()>; + + fn create_machine_ram(&self, mem_config: &MachineMemConfig, thread_num: u8) -> Result<()> { + let root = self.get_vm_ram(); + let numa_nodes = self.get_numa_nodes(); + + if numa_nodes.is_none() || mem_config.mem_zones.is_none() { + let default_mem = create_default_mem(mem_config, thread_num)?; + root.add_subregion_not_update(default_mem, 0_u64)?; + return Ok(()); + } + let zones = mem_config.mem_zones.as_ref().unwrap(); + let mut offset = 0_u64; + for (_, node) in numa_nodes.as_ref().unwrap().iter().enumerate() { + for zone in zones.iter() { + if zone.id.eq(&node.1.mem_dev) { + let ram = create_backend_mem(zone, thread_num)?; + root.add_subregion_not_update(ram, offset)?; + offset += zone.size; + break; + } + } + } + Ok(()) + } + /// Init I/O & memory address space and mmap guest memory. /// /// # Arguments @@ -155,14 +174,9 @@ pub trait MachineOps { // call registers some notifier functions in the KVM, which are frequently triggered when // doing memory prealloc.To avoid affecting memory prealloc performance, create_host_mmaps // needs to be invoked first. - let mut mem_mappings = Vec::new(); let migrate_info = self.get_migrate_info(); if migrate_info.0 != MigrateMode::File { - let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); - mem_mappings = create_host_mmaps(&ram_ranges, mem_config, nr_cpus) - .with_context(|| "Failed to mmap guest ram.")?; - set_host_memory_policy(&mem_mappings, &mem_config.mem_zones) - .with_context(|| "Failed to set host memory NUMA policy.")?; + self.create_machine_ram(mem_config, nr_cpus)?; } sys_mem @@ -176,14 +190,7 @@ pub trait MachineOps { .with_context(|| "Failed to register KVM listener for I/O address space.")?; if migrate_info.0 != MigrateMode::File { - for mmap in mem_mappings.iter() { - let base = mmap.start_address().raw_value(); - let size = mmap.size(); - sys_mem - .root() - .add_subregion(Region::init_ram_region(mmap.clone()), base) - .with_context(|| MachineError::RegMemRegionErr(base, size))?; - } + self.init_machine_ram(sys_mem, mem_config.mem_size)?; } MigrationManager::register_memory_instance(sys_mem.clone()); @@ -342,6 +349,10 @@ pub trait MachineOps { fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)>; + fn get_vm_ram(&self) -> &Arc; + + fn get_numa_nodes(&self) -> &Option; + /// Get migration mode and path from VM config. There are four modes in total: /// Tcp, Unix, File and Unknown. fn get_migrate_info(&self) -> Incoming; @@ -1122,6 +1133,7 @@ pub trait MachineOps { } let mut numa_node = NumaNode { cpus: numa_config.cpus, + mem_dev: numa_config.mem_dev.clone(), ..Default::default() }; @@ -1132,7 +1144,7 @@ pub trait MachineOps { .map(|mem_conf| mem_conf.size) .with_context(|| { format!( - "Object for memory-backend-ram {} config not found", + "Object for memory-backend {} config not found", numa_config.mem_dev ) })?; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d35e3cf74..e45ef8b05 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -69,7 +69,7 @@ use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use machine_manager::{ config::{ parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DriveFile, - Incoming, MigrateMode, NetworkInterfaceConfig, SerialConfig, VmConfig, + Incoming, MigrateMode, NetworkInterfaceConfig, NumaNodes, SerialConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }, event, @@ -185,8 +185,12 @@ pub struct LightMachine { boot_source: Arc>, // All configuration information of virtual machine. vm_config: Arc>, + // List of guest NUMA nodes information. + numa_nodes: Option, // Drive backend files. drive_files: Arc>>, + // All backend memory region tree. + machine_ram: Arc, } impl LightMachine { @@ -196,10 +200,13 @@ impl LightMachine { /// /// * `vm_config` - Represents the configuration for VM. pub fn new(vm_config: &VmConfig) -> MachineResult { - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .with_context(|| MachineError::CrtMemSpaceErr)?; + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "SysMem"), + "sys_mem", + ) + .with_context(|| MachineError::CrtMemSpaceErr)?; #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) + let sys_io = AddressSpace::new(Region::init_container_region(1 << 16, "SysIo"), "SysIo") .with_context(|| MachineError::CrtIoSpaceErr)?; let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = ( @@ -240,7 +247,9 @@ impl LightMachine { boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_state, vm_config: Arc::new(Mutex::new(vm_config.clone())), + numa_nodes: None, drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), + machine_ram: Arc::new(Region::init_container_region(u64::max_value(), "pc.ram")), }) } @@ -482,26 +491,49 @@ impl LightMachine { } impl MachineOps for LightMachine { - fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)> { - #[allow(unused_mut)] - let mut ranges: Vec<(u64, u64)>; + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { + let vm_ram = self.get_vm_ram(); #[cfg(target_arch = "aarch64")] { - let mem_start = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - ranges = vec![(mem_start, mem_size)]; + let layout_size = MEM_LAYOUT[LayoutEntryType::Mem as usize].1; + let ram = Region::init_alias_region( + vm_ram.clone(), + 0, + std::cmp::min(layout_size, mem_size), + "pc_ram", + ); + sys_mem + .root() + .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?; } #[cfg(target_arch = "x86_64")] { - let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 - + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; - ranges = vec![(0, std::cmp::min(gap_start, mem_size))]; - if mem_size > gap_start { - let gap_end = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; - ranges.push((gap_end, mem_size - gap_start)); + let below4g_size = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + + let below4g_ram = Region::init_alias_region( + vm_ram.clone(), + 0, + std::cmp::min(below4g_size, mem_size), + "below4g_ram", + ); + sys_mem.root().add_subregion( + below4g_ram, + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, + )?; + + if mem_size > below4g_size { + let above4g_ram = Region::init_alias_region( + vm_ram.clone(), + below4g_size, + mem_size - below4g_size, + "above4g_ram", + ); + let above4g_start = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; + sys_mem.root().add_subregion(above4g_ram, above4g_start)?; } } - ranges + Ok(()) } #[cfg(target_arch = "x86_64")] @@ -658,6 +690,14 @@ impl MachineOps for LightMachine { &self.sysbus } + fn get_vm_ram(&self) -> &Arc { + &self.machine_ram + } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } + #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { PL031::realize( @@ -767,7 +807,7 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_dies, )); trace_cpu_topo(&topology); - + locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, #[cfg(target_arch = "x86_64")] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 252f4458a..c4cf61c4f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -177,6 +177,8 @@ pub struct StdMachine { fwcfg_dev: Option>>, /// Drive backend files. drive_files: Arc>>, + /// machine all backend memory region tree + machine_ram: Arc, } impl StdMachine { @@ -190,8 +192,11 @@ impl StdMachine { vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .with_context(|| MachineError::CrtIoSpaceErr)?; + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "SysMem"), + "sys_mem", + ) + .with_context(|| MachineError::CrtIoSpaceErr)?; let sysbus = SysBus::new( &sys_mem, ( @@ -247,6 +252,10 @@ impl StdMachine { boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), + machine_ram: Arc::new(Region::init_container_region( + u64::max_value(), + "MachineRam", + )), }) } @@ -376,6 +385,7 @@ impl StdMachineOps for StdMachine { let mmconfig_region = Region::init_io_region( MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1, mmconfig_region_ops, + "PcieEcamIo", ); self.sys_mem .root() @@ -444,14 +454,26 @@ impl StdMachineOps for StdMachine { &self.cpus } - fn get_numa_nodes(&self) -> &Option { + fn get_guest_numa(&self) -> &Option { &self.numa_nodes } } impl MachineOps for StdMachine { - fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)> { - vec![(MEM_LAYOUT[LayoutEntryType::Mem as usize].0, mem_size)] + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { + let vm_ram = self.get_vm_ram(); + + let layout_size = MEM_LAYOUT[LayoutEntryType::Mem as usize].1; + let ram = Region::init_alias_region( + vm_ram.clone(), + 0, + std::cmp::min(layout_size, mem_size), + "pc_ram", + ); + sys_mem + .root() + .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?; + Ok(()) } fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { @@ -795,6 +817,14 @@ impl MachineOps for StdMachine { &self.sysbus } + fn get_vm_ram(&self) -> &Arc { + &self.machine_ram + } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } + fn get_fwcfg_dev(&mut self) -> Option>> { if let Some(fwcfg_dev) = &self.fwcfg_dev { return Some(fwcfg_dev.clone()); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index e07a04ea1..1458623e3 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -144,7 +144,7 @@ trait StdMachineOps: AcpiBuilder { .with_context(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); - if let Some(numa_nodes) = self.get_numa_nodes() { + if let Some(numa_nodes) = self.get_guest_numa() { let srat_addr = self .build_srat_table(&acpi_tables, &mut loader) .with_context(|| "Failed to build ACPI SRAT table")?; @@ -191,7 +191,7 @@ trait StdMachineOps: AcpiBuilder { fn get_cpus(&self) -> &Vec>; - fn get_numa_nodes(&self) -> &Option; + fn get_guest_numa(&self) -> &Option; /// Register event notifier for reset of standard machine. /// @@ -1688,7 +1688,7 @@ impl DeviceInterface for StdMachine { let region; match args.region_type.as_str() { "io_region" => { - region = Region::init_io_region(args.size, dummy_dev_ops); + region = Region::init_io_region(args.size, dummy_dev_ops, "UpdateRegionTest"); if args.ioeventfd.is_some() && args.ioeventfd.unwrap() { let ioeventfds = vec![RegionIoEventFd { fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), @@ -1717,21 +1717,25 @@ impl DeviceInterface for StdMachine { .unwrap(), ), dummy_dev_ops, + "RomDeviceRegionTest", ); } "ram_device_region" => { - region = Region::init_ram_device_region(Arc::new( - HostMemMapping::new( - GuestAddress(args.offset), - None, - args.size, - fd.map(FileBackend::new_common), - false, - true, - false, - ) - .unwrap(), - )); + region = Region::init_ram_device_region( + Arc::new( + HostMemMapping::new( + GuestAddress(args.offset), + None, + args.size, + fd.map(FileBackend::new_common), + false, + true, + false, + ) + .unwrap(), + ), + "RamdeviceregionTest", + ); } _ => { return Response::create_error_response( diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 30ef53a03..634c7aeaa 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -85,7 +85,7 @@ impl LPCBridge { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let pmtmr_region = Region::init_io_region(0x8, ops); + let pmtmr_region = Region::init_io_region(0x8, ops, "PmtmrRegion"); let mut pm_base_addr = 0_u32; self.config @@ -136,7 +136,7 @@ impl LPCBridge { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let rst_ctrl_region = Region::init_io_region(0x1, ops); + let rst_ctrl_region = Region::init_io_region(0x1, ops, "RstCtrlRegion"); self.sys_io .root() .add_subregion(rst_ctrl_region, RST_CTRL_OFFSET as u64)?; @@ -163,7 +163,7 @@ impl LPCBridge { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let sleep_reg_region = Region::init_io_region(0x1, ops); + let sleep_reg_region = Region::init_io_region(0x1, ops, "SleepReg"); self.sys_io .root() .add_subregion(sleep_reg_region, SLEEP_CTRL_OFFSET as u64)?; @@ -185,7 +185,7 @@ impl LPCBridge { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let pm_evt_region = Region::init_io_region(0x4, ops); + let pm_evt_region = Region::init_io_region(0x4, ops, "PmEvtRegion"); self.sys_io .root() .add_subregion(pm_evt_region, PM_EVENT_OFFSET as u64)?; @@ -215,7 +215,7 @@ impl LPCBridge { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let pm_ctrl_region = Region::init_io_region(0x4, ops); + let pm_ctrl_region = Region::init_io_region(0x4, ops, "PmCtrl"); self.sys_io .root() .add_subregion(pm_ctrl_region, PM_CTRL_OFFSET as u64)?; diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 0b99f10ce..3f7e29d65 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -90,7 +90,7 @@ impl Mch { self.mmconfig_region = None; } if enable == 0x1 { - let region = Region::init_io_region(length, self.mmconfig_ops.clone()); + let region = Region::init_io_region(length, self.mmconfig_ops.clone(), "PcieXBar"); let base_addr: u64 = pciexbar & addr_mask; self.parent_bus .upgrade() diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 452d49bce..f12e4a7d5 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -143,6 +143,8 @@ pub struct StdMachine { fwcfg_dev: Option>>, /// Drive backend files. drive_files: Arc>>, + /// All backend memory region tree + machine_ram: Arc, } impl StdMachine { @@ -156,10 +158,13 @@ impl StdMachine { vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ); - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)) - .with_context(|| MachineError::CrtMemSpaceErr)?; - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) + let sys_io = AddressSpace::new(Region::init_container_region(1 << 16, "SysIo"), "SysIo") .with_context(|| MachineError::CrtIoSpaceErr)?; + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "SysMem"), + "SysMem", + ) + .with_context(|| MachineError::CrtMemSpaceErr)?; let sysbus = SysBus::new( &sys_io, &sys_mem, @@ -204,6 +209,10 @@ impl StdMachine { boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), + machine_ram: Arc::new(Region::init_container_region( + u64::max_value(), + "MachineRam", + )), }) } @@ -288,6 +297,7 @@ impl StdMachineOps for StdMachine { let mmconfig_region = Region::init_io_region( MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].1, mmconfig_region_ops.clone(), + "PcieEcamSpace", ); self.sys_mem .root() @@ -298,13 +308,13 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to register ECAM in memory space.")?; let pio_addr_ops = PciHost::build_pio_addr_ops(self.pci_host.clone()); - let pio_addr_region = Region::init_io_region(4, pio_addr_ops); + let pio_addr_region = Region::init_io_region(4, pio_addr_ops, "PioAddr"); self.sys_io .root() .add_subregion(pio_addr_region, 0xcf8) .with_context(|| "Failed to register CONFIG_ADDR port in I/O space.")?; let pio_data_ops = PciHost::build_pio_data_ops(self.pci_host.clone()); - let pio_data_region = Region::init_io_region(4, pio_data_ops); + let pio_data_region = Region::init_io_region(4, pio_data_ops, "PioData"); self.sys_io .root() .add_subregion(pio_data_region, 0xcfc) @@ -341,22 +351,38 @@ impl StdMachineOps for StdMachine { &self.cpus } - fn get_numa_nodes(&self) -> &Option { + fn get_guest_numa(&self) -> &Option { &self.numa_nodes } } impl MachineOps for StdMachine { - fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)> { - let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 - + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { + let ram = self.get_vm_ram(); + let below4g_size = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + + let below4g_ram = Region::init_alias_region( + ram.clone(), + 0, + std::cmp::min(below4g_size, mem_size), + "below4g_ram", + ); + sys_mem.root().add_subregion( + below4g_ram, + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, + )?; - let mut ranges = vec![(0, std::cmp::min(gap_start, mem_size))]; - if mem_size > gap_start { - let gap_end = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; - ranges.push((gap_end, mem_size - gap_start)); + if mem_size > below4g_size { + let above4g_ram = Region::init_alias_region( + ram.clone(), + below4g_size, + mem_size - below4g_size, + "above4g_ram", + ); + let above4g_start = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; + sys_mem.root().add_subregion(above4g_ram, above4g_start)?; } - ranges + Ok(()) } fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { @@ -548,7 +574,7 @@ impl MachineOps for StdMachine { false, false, )?); - let rom_region = Region::init_ram_region(ram1); + let rom_region = Region::init_ram_region(ram1, "PflashRam"); rom_region.write(&mut fd, GuestAddress(rom_base), 0, rom_size)?; rom_region.set_priority(10); self.sys_mem.root().add_subregion(rom_region, rom_base)?; @@ -638,6 +664,14 @@ impl MachineOps for StdMachine { &self.sysbus } + fn get_vm_ram(&self) -> &Arc { + &self.machine_ram + } + + fn get_numa_nodes(&self) -> &Option { + &self.numa_nodes + } + fn get_fwcfg_dev(&mut self) -> Option>> { if let Some(fwcfg_dev) = &self.fwcfg_dev { return Some(fwcfg_dev.clone()); diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 1134c5ce1..a9a0bfa3c 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -40,6 +40,7 @@ pub struct NumaNode { pub cpus: Vec, pub distances: BTreeMap, pub size: u64, + pub mem_dev: String, } pub type NumaNodes = BTreeMap; @@ -321,11 +322,13 @@ mod tests { cpus: vec![0, 1], distances: Default::default(), size: 1073741824, + mem_dev: String::from("numa_node1"), }; let numa_node2 = NumaNode { cpus: vec![2, 3], distances: Default::default(), size: 1073741824, + mem_dev: String::from("numa_node2"), }; let mut numa_nodes = BTreeMap::new(); @@ -337,6 +340,7 @@ mod tests { cpus: vec![2], distances: Default::default(), size: 1073741824, + mem_dev: String::from("numa_node3"), }; numa_nodes.remove(&1); numa_nodes.insert(2, numa_node3); @@ -346,6 +350,7 @@ mod tests { cpus: vec![2, 3, 4], distances: Default::default(), size: 1073741824, + mem_dev: String::from("numa_node4"), }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node4); @@ -355,6 +360,7 @@ mod tests { cpus: vec![3, 4], distances: Default::default(), size: 1073741824, + mem_dev: String::from("numa_node5"), }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node5); @@ -364,6 +370,7 @@ mod tests { cpus: vec![0, 1], distances: Default::default(), size: 1073741824, + mem_dev: String::from("numa_node6"), }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node6); @@ -373,6 +380,7 @@ mod tests { cpus: vec![2, 3], distances: Default::default(), size: 2147483648, + mem_dev: String::from("numa_node7"), }; numa_nodes.remove(&1); numa_nodes.insert(1, numa_node7); diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 225b551cc..b0f0f34b9 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -324,8 +324,13 @@ mod tests { pub fn create_pci_host() -> Arc> { #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_io = + AddressSpace::new(Region::init_container_region(1 << 16, "sysio"), "sysio").unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); Arc::new(Mutex::new(PciHost::new( #[cfg(target_arch = "x86_64")] &sys_io, diff --git a/pci/src/config.rs b/pci/src/config.rs index 8f2ec6134..2bf9b0800 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -1214,7 +1214,7 @@ mod tests { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let region = Region::init_io_region(8192, region_ops.clone()); + let region = Region::init_io_region(8192, region_ops.clone(), "io"); let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); #[cfg(target_arch = "x86_64")] @@ -1232,7 +1232,7 @@ mod tests { .register_bar(7, region, RegionType::Mem64Bit, true, 8192) .is_err()); // test when bar size is incorrect(not power of 2) - let region_size_not_pow_2 = Region::init_io_region(4238, region_ops); + let region_size_not_pow_2 = Region::init_io_region(4238, region_ops, "io2"); assert!(pci_config .register_bar(4, region_size_not_pow_2, RegionType::Mem64Bit, true, 4238) .is_err()); @@ -1291,7 +1291,7 @@ mod tests { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let region = Region::init_io_region(8192, region_ops); + let region = Region::init_io_region(8192, region_ops, "io"); let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 6); #[cfg(target_arch = "x86_64")] @@ -1327,8 +1327,13 @@ mod tests { .unwrap(); #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_io = + AddressSpace::new(Region::init_container_region(1 << 16, "sysio"), "sysio").unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); assert_eq!(pci_config.bars[1].address, BAR_SPACE_UNMAPPED); assert_eq!(pci_config.bars[2].address, BAR_SPACE_UNMAPPED); pci_config @@ -1454,7 +1459,7 @@ mod tests { read: Arc::new(read_ops), write: Arc::new(write_ops), }; - let region = Region::init_io_region(4096, region_ops); + let region = Region::init_io_region(4096, region_ops, "io"); let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); // bar is unmapped @@ -1470,8 +1475,8 @@ mod tests { .is_ok()); #[cfg(target_arch = "x86_64")] - let io_region = Region::init_container_region(1 << 16); - let mem_region = Region::init_container_region(u64::max_value()); + let io_region = Region::init_container_region(1 << 16, "iocon"); + let mem_region = Region::init_container_region(u64::max_value(), "mem"); let bus = Arc::new(Mutex::new(PciBus::new( String::from("bus"), #[cfg(target_arch = "x86_64")] diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 0a5f38052..ca56a45dd 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -87,7 +87,7 @@ impl DemoDev { name: cfg.id.clone(), cmd_cfg: cfg.clone(), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), - mem_region: Region::init_container_region(u32::MAX as u64), + mem_region: Region::init_container_region(u32::MAX as u64, "DemoDev"), devfn, parent_bus, dev_id: Arc::new(AtomicU16::new(0)), @@ -147,7 +147,7 @@ impl DemoDev { write: Arc::new(write_ops), }; - let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops); + let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops, "DemoRegion"); self.mem_region.add_subregion(region, 0)?; self.config.register_bar( diff --git a/pci/src/host.rs b/pci/src/host.rs index d45d73c8d..42f04e41c 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -593,8 +593,13 @@ pub mod tests { pub fn create_pci_host() -> Arc> { #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16)).unwrap(); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_io = + AddressSpace::new(Region::init_container_region(1 << 16, "sysio"), "sysio").unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); Arc::new(Mutex::new(PciHost::new( #[cfg(target_arch = "x86_64")] &sys_io, diff --git a/pci/src/msix.rs b/pci/src/msix.rs index d6c283830..9978b6f78 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -390,7 +390,7 @@ impl Msix { read: Arc::new(table_read), write: Arc::new(table_write), }; - let table_region = Region::init_io_region(table_size, table_region_ops); + let table_region = Region::init_io_region(table_size, table_region_ops, "MsixTable"); region .add_subregion(table_region, table_offset) .with_context(|| "Failed to register MSI-X table region.")?; @@ -414,7 +414,7 @@ impl Msix { read: Arc::new(pba_read), write: Arc::new(pba_write), }; - let pba_region = Region::init_io_region(pba_size, pba_region_ops); + let pba_region = Region::init_io_region(pba_size, pba_region_ops, "MsixPba"); region .add_subregion(pba_region, pba_offset) .with_context(|| "Failed to register MSI-X PBA region.")?; @@ -677,7 +677,7 @@ pub fn init_msix( } else { let mut bar_size = ((table_size + pba_size) as u64).next_power_of_two(); bar_size = max(bar_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); - let region = Region::init_container_region(bar_size); + let region = Region::init_container_region(bar_size, "Msix_region"); Msix::register_memory_region( msix.clone(), ®ion, diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 7d4ae4c75..1d5344fa5 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -96,8 +96,8 @@ impl RootPort { multifunction: bool, ) -> Self { #[cfg(target_arch = "x86_64")] - let io_region = Region::init_container_region(1 << 16); - let mem_region = Region::init_container_region(u64::max_value()); + let io_region = Region::init_container_region(1 << 16, "RootPortIo"); + let mem_region = Region::init_container_region(u64::max_value(), "RootPortMem"); let sec_bus = Arc::new(Mutex::new(PciBus::new( name.clone(), #[cfg(target_arch = "x86_64")] diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 018a56665..b8b75d41f 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -114,9 +114,10 @@ impl SysBus { dev: &Arc>, region_base: u64, region_size: u64, + region_name: &str, ) -> Result<()> { let region_ops = self.build_region_ops(dev); - let region = Region::init_io_region(region_size, region_ops); + let region = Region::init_io_region(region_size, region_ops, region_name); let locked_dev = dev.lock().unwrap(); region.set_ioeventfds(&locked_dev.ioeventfds()); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index aab4a6a6e..bdf5f929b 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -385,18 +385,21 @@ impl VfioPciDevice { .with_context(|| "Failed to get vfio bar info")?; let size = vfio_bar.size; - let region = Region::init_container_region(size); + let region = Region::init_container_region(size, "VfioPci"); let bar_region = if i == table_bar { region .add_subregion( - Region::init_io_region(table_size, table_ops.clone()), + Region::init_io_region(table_size, table_ops.clone(), "VfioBar"), table_offset, ) .with_context(|| VfioError::AddRegBar(i as usize))?; if table_offset > 0 { region - .add_subregion(Region::init_io_region(table_offset, bar_ops.clone()), 0) + .add_subregion( + Region::init_io_region(table_offset, bar_ops.clone(), "VfioRegion"), + 0, + ) .with_context(|| VfioError::AddRegBar(i as usize))?; } @@ -406,6 +409,7 @@ impl VfioPciDevice { Region::init_io_region( size - table_offset - table_size, bar_ops.clone(), + "vfio_io_region2", ), table_offset + table_size, ) @@ -414,7 +418,10 @@ impl VfioPciDevice { region } else { region - .add_subregion(Region::init_io_region(size, bar_ops.clone()), 0) + .add_subregion( + Region::init_io_region(size, bar_ops.clone(), "vfio_io_region"), + 0, + ) .with_context(|| VfioError::AddRegBar(i as usize))?; region }; @@ -695,7 +702,7 @@ impl VfioPciDevice { read_only, )?; - let ram_device = Region::init_ram_device_region(Arc::new(host_mmap)); + let ram_device = Region::init_ram_device_region(Arc::new(host_mmap), "VfioRam"); let bar = self .pci_config .bars diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 0b4b36a2c..3fbc2d1c0 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -213,8 +213,11 @@ impl VirtioFs { /// /// * `source_dir` - The path of source directory shared in host. pub fn new(fs_config: FsConfig) -> Result { - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) - .with_context(|| "Failed to create address space")?; + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "VirtioFsMem"), + "VirtioFsMem", + ) + .with_context(|| "Failed to create address space")?; let mut fs_handlers = Vec::new(); for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { @@ -296,7 +299,7 @@ impl VhostUserReqHandler for VirtioFs { )? ); - let region = Region::init_ram_region(mmap.clone()); + let region = Region::init_ram_region(mmap.clone(), "VirtioFsRam"); self.sys_mem .root() .add_subregion(region.clone(), mmap.start_address().raw_value()) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 5a0ecc4ba..cfa5c1c5a 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1206,8 +1206,8 @@ mod tests { const QUEUE_SIZE: u16 = 256; fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "space"); + let sys_space = AddressSpace::new(root, "space").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -1223,7 +1223,7 @@ mod tests { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "space"), host_mmap.start_address().raw_value(), ) .unwrap(); @@ -1239,7 +1239,7 @@ mod tests { mem_mapping.start_address().unchecked_add(offset_in_region), mem_mapping.size() - offset_in_region, ), - owner: Region::init_ram_region(mem_mapping.clone()), + owner: Region::init_ram_region(mem_mapping.clone(), "mem"), offset_in_region, rom_dev_romd: None, } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 14a951f4a..e3191f229 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1388,8 +1388,8 @@ mod tests { // build dummy address space of vm fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "sysmem"); + let sys_space = AddressSpace::new(root, "sysmem").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -1405,7 +1405,7 @@ mod tests { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "sysmem"), host_mmap.start_address().raw_value(), ) .unwrap(); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index d4b2823fd..3e4a33186 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -399,8 +399,8 @@ mod tests { // build dummy address space of vm fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "sysmem"); + let sys_space = AddressSpace::new(root, "sysmem").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -416,7 +416,7 @@ mod tests { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "sysmem"), host_mmap.start_address().raw_value(), ) .unwrap(); diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 845e66ff8..a2a5657d4 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -845,8 +845,8 @@ mod tests { use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "sysmem"); + let sys_space = AddressSpace::new(root, "sysmem").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -862,7 +862,7 @@ mod tests { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "sysmem"), host_mmap.start_address().raw_value(), ) .unwrap(); diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 4ed04039c..3407ddd3f 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -384,7 +384,7 @@ impl VirtioMmioDevice { } self.set_sys_resource(sysbus, region_base, region_size)?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size)?; + sysbus.attach_device(&dev, region_base, region_size, "VirtioMmio")?; #[cfg(target_arch = "x86_64")] bs.lock().unwrap().kernel_cmdline.push(Param { @@ -740,8 +740,8 @@ mod tests { use crate::VIRTIO_TYPE_BLOCK; fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "sysmem"); + let sys_space = AddressSpace::new(root, "sysmem").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -757,7 +757,7 @@ mod tests { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "sysmem"), host_mmap.start_address().raw_value(), ) .unwrap(); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 9ffa05eef..758ecf71f 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -866,8 +866,11 @@ impl VirtioPciDevice { fn modern_mem_region_init(&mut self, modern_mem_region: &Region) -> PciResult<()> { // 1. PCI common cap sub-region. let common_region_ops = self.build_common_cfg_ops(); - let common_region = - Region::init_io_region(u64::from(VIRTIO_PCI_CAP_COMMON_LENGTH), common_region_ops); + let common_region = Region::init_io_region( + u64::from(VIRTIO_PCI_CAP_COMMON_LENGTH), + common_region_ops, + "VirtioPciCommon", + ); modern_mem_region .add_subregion(common_region, u64::from(VIRTIO_PCI_CAP_COMMON_OFFSET)) .with_context(|| "Failed to register pci-common-cap region.")?; @@ -888,8 +891,11 @@ impl VirtioPciDevice { read: Arc::new(isr_read), write: Arc::new(isr_write), }; - let isr_region = - Region::init_io_region(u64::from(VIRTIO_PCI_CAP_ISR_LENGTH), isr_region_ops); + let isr_region = Region::init_io_region( + u64::from(VIRTIO_PCI_CAP_ISR_LENGTH), + isr_region_ops, + "VirtioIsr", + ); modern_mem_region .add_subregion(isr_region, u64::from(VIRTIO_PCI_CAP_ISR_OFFSET)) .with_context(|| "Failed to register pci-isr-cap region.")?; @@ -916,8 +922,11 @@ impl VirtioPciDevice { read: Arc::new(device_read), write: Arc::new(device_write), }; - let device_region = - Region::init_io_region(u64::from(VIRTIO_PCI_CAP_DEVICE_LENGTH), device_region_ops); + let device_region = Region::init_io_region( + u64::from(VIRTIO_PCI_CAP_DEVICE_LENGTH), + device_region_ops, + "VirtioDevice", + ); modern_mem_region .add_subregion(device_region, u64::from(VIRTIO_PCI_CAP_DEVICE_OFFSET)) .with_context(|| "Failed to register pci-dev-cap region.")?; @@ -929,8 +938,11 @@ impl VirtioPciDevice { read: Arc::new(notify_read), write: Arc::new(notify_write), }; - let notify_region = - Region::init_io_region(u64::from(VIRTIO_PCI_CAP_NOTIFY_LENGTH), notify_region_ops); + let notify_region = Region::init_io_region( + u64::from(VIRTIO_PCI_CAP_NOTIFY_LENGTH), + notify_region_ops, + "VirtioNotify", + ); notify_region.set_ioeventfds(&self.ioeventfds()); modern_mem_region .add_subregion(notify_region, u64::from(VIRTIO_PCI_CAP_NOTIFY_OFFSET)) @@ -1169,7 +1181,8 @@ impl PciDevOps for VirtioPciDevice { as u64) .next_power_of_two(); mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); - let modern_mem_region = Region::init_container_region(mem_region_size); + let modern_mem_region = + Region::init_container_region(mem_region_size, "VirtioPciModernMem"); self.modern_mem_region_init(&modern_mem_region)?; self.config.register_bar( @@ -1540,11 +1553,15 @@ mod tests { fn test_common_config_dev_feature() { let dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); let virtio_dev = dev.clone() as Arc>; - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16), + Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); let cloned_virtio_dev = virtio_dev.clone(); @@ -1627,11 +1644,15 @@ mod tests { let queue_size = virtio_dev.lock().unwrap().queue_size(); let queue_num = virtio_dev.lock().unwrap().queue_num(); let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16), + Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); let cloned_virtio_dev = virtio_dev.clone(); @@ -1688,11 +1709,15 @@ mod tests { fn test_virtio_pci_config_access() { let virtio_dev: Arc> = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16), + Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); let mut virtio_pci = VirtioPciDevice::new( @@ -1723,11 +1748,15 @@ mod tests { fn test_virtio_pci_realize() { let virtio_dev: Arc> = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16), + Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); let virtio_pci = VirtioPciDevice::new( @@ -1743,7 +1772,11 @@ mod tests { #[test] fn test_device_activate() { - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); let mem_size: u64 = 1024 * 1024; let host_mmap = Arc::new( HostMemMapping::new(GuestAddress(0), None, mem_size, None, false, false, false) @@ -1752,7 +1785,7 @@ mod tests { sys_mem .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "sysmem"), host_mmap.start_address().raw_value(), ) .unwrap(); @@ -1762,7 +1795,7 @@ mod tests { let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16), + Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); let mut virtio_pci = VirtioPciDevice::new( @@ -1844,11 +1877,15 @@ mod tests { fn test_multifunction() { let virtio_dev: Arc> = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap(); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16), + Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); let mut virtio_pci = VirtioPciDevice::new( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 0e4efd246..1f4db547d 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -447,8 +447,8 @@ mod tests { const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; fn vhost_address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36); - let sys_space = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(1 << 36, "sysmem"); + let sys_space = AddressSpace::new(root, "sysmem").unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), @@ -464,7 +464,7 @@ mod tests { sys_space .root() .add_subregion( - Region::init_ram_region(host_mmap.clone()), + Region::init_ram_region(host_mmap.clone(), "sysmem"), host_mmap.start_address().raw_value(), ) .unwrap(); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 811e134d6..e8aeb8edb 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -419,8 +419,8 @@ mod tests { pub use address_space::*; fn vsock_address_space_init() -> Arc { - let root = Region::init_container_region(u64::max_value()); - let sys_mem = AddressSpace::new(root).unwrap(); + let root = Region::init_container_region(u64::max_value(), "sysmem"); + let sys_mem = AddressSpace::new(root, "sysmem").unwrap(); sys_mem } -- Gitee From 2ba70167ccd8d0fbca2d3251a7bcc1bcbe9a3bbe Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 12:15:33 +0800 Subject: [PATCH 1196/2187] memory: memory virtual address discontinuity adaptation read and write API Signed-off-by: jiewangqun --- address_space/src/address_space.rs | 117 ++++++++++++++++++----------- address_space/src/region.rs | 1 - 2 files changed, 73 insertions(+), 45 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 7a9457677..705f1e1db 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -38,6 +38,73 @@ impl FlatView { _ => None, } } + + fn read(&self, dst: &mut dyn std::io::Write, addr: GuestAddress, count: u64) -> Result<()> { + let mut len = count; + let mut l = count; + let mut start = addr; + + loop { + if let Some(fr) = self.find_flatrange(start) { + let fr_offset = start.offset_from(fr.addr_range.base); + let region_offset = fr.offset_in_region + fr_offset; + let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); + let fr_remain = fr.addr_range.size - fr_offset; + + if fr.owner.region_type() == RegionType::Ram + || fr.owner.region_type() == RegionType::RamDevice + { + l = std::cmp::min(l, fr_remain); + } + fr.owner.read(dst, region_base, region_offset, l)?; + } else { + return Err(anyhow!(AddressSpaceError::RegionNotFound( + start.raw_value() + ))); + } + + len -= l; + if len == 0 { + return Ok(()); + } + start = start.unchecked_add(l); + l = len; + } + } + + fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { + let mut l = count; + let mut len = count; + let mut start = addr; + + loop { + if let Some(fr) = self.find_flatrange(start) { + let fr_offset = start.offset_from(fr.addr_range.base); + let region_offset = fr.offset_in_region + fr_offset; + let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); + let fr_remain = fr.addr_range.size - fr_offset; + if fr.owner.region_type() == RegionType::Ram + || fr.owner.region_type() == RegionType::RamDevice + { + l = std::cmp::min(l, fr_remain); + } + fr.owner.write(src, region_base, region_offset, l)?; + } else { + return Err(anyhow!(AddressSpaceError::RegionNotFound( + start.raw_value() + ))); + } + + len -= l; + if len == 0 { + break; + } + start = start.unchecked_add(l); + l = len; + } + + Ok(()) + } } #[derive(Clone, Copy)] @@ -437,25 +504,10 @@ impl AddressSpace { /// /// Return Error if the `addr` is not mapped. pub fn read(&self, dst: &mut dyn std::io::Write, addr: GuestAddress, count: u64) -> Result<()> { - let view = &self.flat_view.load(); + let view = self.flat_view.load(); - let (fr, offset) = view - .find_flatrange(addr) - .map(|fr| (fr, addr.offset_from(fr.addr_range.base))) - .with_context(|| AddressSpaceError::RegionNotFound(addr.raw_value()))?; - - let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); - let offset_in_region = fr.offset_in_region + offset; - fr.owner - .read(dst, region_base, offset_in_region, count) - .with_context(|| { - format!( - "Failed to read region, region base 0x{:X}, offset in region 0x{:X}, size 0x{:X}", - region_base.raw_value(), - offset_in_region, - count - ) - }) + view.read(dst, addr, count)?; + Ok(()) } /// Write data to specified guest address. @@ -471,13 +523,6 @@ impl AddressSpace { /// Return Error if the `addr` is not mapped. pub fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { let view = self.flat_view.load(); - let (fr, offset) = view - .find_flatrange(addr) - .map(|fr| (fr, addr.offset_from(fr.addr_range.base))) - .with_context(|| AddressSpaceError::RegionNotFound(addr.raw_value()))?; - - let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); - let offset_in_region = fr.offset_in_region + offset; if is_test_enabled() { for evtfd in self.ioeventfds.lock().unwrap().iter() { @@ -499,28 +544,12 @@ impl AddressSpace { return Ok(()); } } - - return fr.owner - .write(&mut buf.as_slice(), region_base, offset_in_region, count) - .with_context(|| - format!( - "Failed to write region, region base 0x{:X}, offset in region 0x{:X}, size 0x{:X}", - region_base.raw_value(), - offset_in_region, - count - )); + view.write(&mut buf.as_slice(), addr, count)?; } } - fr.owner - .write(src, region_base, offset_in_region, count) - .with_context(|| - format!( - "Failed to write region, region base 0x{:X}, offset in region 0x{:X}, size 0x{:X}", - region_base.raw_value(), - offset_in_region, - count - )) + view.write(src, addr, count)?; + Ok(()) } /// Write an object to memory. diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 927864890..192f101ca 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -886,7 +886,6 @@ impl Region { })?; } } - RegionType::Ram | RegionType::IO | RegionType::RomDevice | RegionType::RamDevice => { self.render_terminate_region(base, addr_range, flat_view) .with_context(|| -- Gitee From 62aa3e9599ad8c2aa8e980a46d7b5662e8f1695e Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 10 Jul 2023 11:08:57 +0800 Subject: [PATCH 1197/2187] qmp: use query-mem qmp commands to query memory informations Signed-off-by: jiewangqun --- address_space/src/address_space.rs | 21 +++++++++ address_space/src/region.rs | 60 ++++++++++++++++++++++++++ machine/src/micro_vm/mod.rs | 14 ++++++ machine/src/standard_vm/aarch64/mod.rs | 6 +++ machine/src/standard_vm/mod.rs | 5 +++ machine/src/standard_vm/x86_64/mod.rs | 7 +++ machine_manager/src/machine.rs | 3 ++ machine_manager/src/qmp/mod.rs | 1 + machine_manager/src/qmp/qmp_schema.rs | 28 ++++++++++++ 9 files changed, 145 insertions(+) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 705f1e1db..fb6acd988 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -174,6 +174,27 @@ impl AddressSpace { &self.root } + pub fn memspace_show(&self) { + let view = self.flat_view.load(); + + println!("----- address-space flat: {} -----", self.name); + for fr in view.0.iter() { + println!( + " 0x{:X} - 0x{:X}, (pri {}, {:?}) Region {} @ offset 0x{:X}", + fr.addr_range.base.raw_value(), + fr.addr_range.base.raw_value() + fr.addr_range.size, + fr.owner.priority(), + fr.owner.region_type(), + fr.owner.name, + fr.offset_in_region + ); + } + + println!("------ regions show: {} --------------", self.root().name); + self.root().mtree(0_u32); + println!("--------------------------------------"); + } + /// Register the listener to the `AddressSpace`. /// /// # Arguments diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 192f101ca..769ed9bc6 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1012,6 +1012,66 @@ impl Region { } Ok(flat_view) } + + fn get_region_type_name(&self) -> String { + match self.region_type() { + RegionType::Ram => String::from("ram"), + RegionType::IO => String::from("i/o"), + RegionType::RomDevice => String::from("romd"), + RegionType::RamDevice => String::from("ramd"), + _ => String::from("err type"), + } + } + + pub fn mtree(&self, level: u32) { + let mut tab = String::new(); + let mut num = 0_u32; + + loop { + if num == level { + break; + } + tab.push_str(" "); + num += 1; + } + match self.region_type() { + RegionType::Container => { + println!( + "{}0x{:X} - 0x{:X}, (Prio {}, Container) : {}", + tab, + self.offset().raw_value(), + self.offset().raw_value() + self.size(), + self.priority(), + self.name + ); + for sub_r in self.subregions().iter() { + sub_r.mtree(level + 1); + } + } + RegionType::Ram | RegionType::IO | RegionType::RomDevice | RegionType::RamDevice => { + println!( + "{}0x{:X} - 0x{:X}, (Prio {}, {}) : {}", + tab, + self.offset().raw_value(), + self.offset().raw_value() + self.size(), + self.priority(), + self.get_region_type_name(), + self.name + ); + } + RegionType::Alias => { + println!( + "{}0x{:X} - 0x{:X}, (Prio {}, alias) : {} @alias name: {}", + tab, + self.alias_offset(), + self.alias_offset() + self.size(), + self.priority(), + self.name, + self.alias_name().unwrap(), + ); + } + } + } } #[cfg(test)] diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index e45ef8b05..1076e6892 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -272,6 +272,15 @@ impl LightMachine { Ok(()) } + pub fn mem_show(&self) { + self.sys_mem.memspace_show(); + #[cfg(target_arch = "x86_64")] + self.sys_io.memspace_show(); + + let machine_ram = self.get_vm_ram(); + machine_ram.mtree(0_u32); + } + fn create_replaceable_devices(&mut self) -> Result<()> { let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { @@ -1127,6 +1136,11 @@ impl DeviceInterface for LightMachine { ) } + fn query_mem(&self) -> Response { + self.mem_show(); + Response::create_empty_response() + } + /// VNC is not supported by light machine currently. fn query_vnc(&self) -> Response { Response::create_error_response( diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index c4cf61c4f..ee739cd7f 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -376,6 +376,12 @@ impl StdMachine { } Ok(()) } + + pub fn mem_show(&self) { + self.sys_mem.memspace_show(); + let machine_ram = self.get_vm_ram(); + machine_ram.mtree(0_u32); + } } impl StdMachineOps for StdMachine { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 1458623e3..6298a9091 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1193,6 +1193,11 @@ impl DeviceInterface for StdMachine { ) } + fn query_mem(&self) -> Response { + self.mem_show(); + Response::create_empty_response() + } + fn query_vnc(&self) -> Response { #[cfg(not(target_env = "musl"))] if let Some(vnc_info) = qmp_query_vnc() { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index f12e4a7d5..e911a65e5 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -288,6 +288,13 @@ impl StdMachine { ich.realize()?; Ok(()) } + + pub fn mem_show(&self) { + self.sys_mem.memspace_show(); + self.sys_io.memspace_show(); + let machine_ram = self.get_vm_ram(); + machine_ram.mtree(0_u32); + } } impl StdMachineOps for StdMachine { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index cec244617..39cda6448 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -194,6 +194,9 @@ pub trait DeviceInterface { /// Query balloon's size. fn query_balloon(&self) -> Response; + /// Query machine mem size. + fn query_mem(&self) -> Response; + /// Query the info of vnc server. fn query_vnc(&self) -> Response; diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 3aa8f3fb1..ff4e191f9 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -443,6 +443,7 @@ fn qmp_command_exec( (cancel_migrate, cancel_migrate), (query_cpus, query_cpus), (query_balloon, query_balloon), + (query_mem, query_mem), (query_vnc, query_vnc), (list_type, list_type), (query_hotpluggable_cpus, query_hotpluggable_cpus); diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index abdcc5235..eaee8a544 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -181,6 +181,13 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "query-mem")] + query_mem { + #[serde(default)] + arguments: query_mem, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, #[serde(rename = "query-balloon")] query_balloon { #[serde(default)] @@ -2338,6 +2345,27 @@ pub struct SnapshotInfo { pub icount: u64, } +/// query-mem +/// +/// This command +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "query-mem" } +/// <- { "return": {}} +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct query_mem {} +impl Command for query_mem { + type Res = Empty; + + fn back(self) -> Empty { + Default::default() + } +} + #[cfg(test)] mod tests { use super::*; -- Gitee From c110d1de47ff0a72c811bd4cd2d2ffdd04e3fd0a Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 15:12:36 +0800 Subject: [PATCH 1198/2187] devices: Added address continuity protection 1.Increase protection against address discontinuities in the virtio queue. 2.Replace get_host_address functions with safe functions Signed-off-by: jiewangqun --- address_space/src/address_space.rs | 71 +++++++++++++++++++++-- devices/src/legacy/ramfb.rs | 10 +++- devices/src/usb/xhci/xhci_controller.rs | 19 ++---- vhost_user_fs/src/fuse_msg.rs | 16 ++--- virtio/src/device/balloon.rs | 4 ++ virtio/src/device/block.rs | 4 ++ virtio/src/device/gpu.rs | 8 +++ virtio/src/device/net.rs | 29 +++++++--- virtio/src/device/rng.rs | 8 +++ virtio/src/device/scsi_cntlr.rs | 4 ++ virtio/src/device/serial.rs | 4 ++ virtio/src/lib.rs | 33 +++++------ virtio/src/queue/split.rs | 77 ++++++++++++++++++++++++- virtio/src/transport/virtio_mmio.rs | 55 ++++++++++-------- virtio/src/transport/virtio_pci.rs | 50 +++++++++------- virtio/src/vhost/kernel/net.rs | 4 ++ virtio/src/vhost/kernel/vsock.rs | 4 ++ virtio/src/vhost/user/block.rs | 8 +++ virtio/src/vhost/user/fs.rs | 8 +++ virtio/src/vhost/user/net.rs | 4 ++ 20 files changed, 315 insertions(+), 105 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index fb6acd988..d39b2e2a8 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -18,6 +18,7 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use migration::{migration::Migratable, MigrationManager}; +use util::aio::Iovec; use util::byte_code::ByteCode; use util::test_helper::is_test_enabled; @@ -448,6 +449,65 @@ impl AddressSpace { }) } + /// Return the available size and hva to the given `GuestAddress` from flat_view. + /// + /// # Arguments + /// + /// * `addr` - Guest address. + /// Return Error if the `addr` is not mapped. + /// or return the HVA address and available mem length + pub fn addr_cache_init(&self, addr: GuestAddress) -> Option<(u64, u64)> { + let view = self.flat_view.load(); + + if let Some(flat_range) = view.find_flatrange(addr) { + let fr_offset = addr.offset_from(flat_range.addr_range.base); + let region_offset = flat_range.offset_in_region + fr_offset; + + let region_remain = flat_range.owner.size() - region_offset; + let fr_remain = flat_range.addr_range.size - fr_offset; + + return flat_range.owner.get_host_address().map(|host| { + ( + host + region_offset, + std::cmp::min(fr_remain, region_remain), + ) + }); + } + + None + } + + /// Convert GPA buffer iovec to HVA buffer iovec. + /// + /// # Arguments + /// + /// * `addr` - Guest address. + /// * `count` - Memory needed length + pub fn get_address_map(&self, addr: GuestAddress, count: u64) -> Result> { + let mut len = count; + let mut start = addr; + let mut hva_iovec = Vec::new(); + + loop { + let io_vec = self + .addr_cache_init(start) + .map(|(hva, fr_len)| Iovec { + iov_base: hva, + iov_len: std::cmp::min(len, fr_len), + }) + .with_context(|| format!("Map iov base {:x?}, iov len {:?} failed", addr, count))?; + start = start.unchecked_add(io_vec.iov_len); + len -= io_vec.iov_len; + hva_iovec.push(io_vec); + + if len == 0 { + break; + } + } + + Ok(hva_iovec) + } + /// Return the host address according to the given `GuestAddress` from cache. /// /// # Arguments @@ -458,15 +518,18 @@ impl AddressSpace { &self, addr: GuestAddress, cache: &Option, - ) -> Option { + ) -> Option<(u64, u64)> { if cache.is_none() { - return self.get_host_address(addr); + return self.addr_cache_init(addr); } let region_cache = cache.unwrap(); if addr.0 >= region_cache.start && addr.0 < region_cache.end { - Some(region_cache.host_base + addr.0 - region_cache.start) + Some(( + region_cache.host_base + addr.0 - region_cache.start, + region_cache.end - addr.0, + )) } else { - self.get_host_address(addr) + self.addr_cache_init(addr) } } diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index cc26d945c..b4d310e84 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -111,8 +111,14 @@ impl RamfbState { stride = linesize; } - let fb_addr = match self.sys_mem.get_host_address(GuestAddress(addr)) { - Some(addr) => addr, + let fb_addr = match self.sys_mem.addr_cache_init(GuestAddress(addr)) { + Some((hva, len)) => { + if len < stride as u64 { + error!("Insufficient contiguous memory length"); + return; + } + hva + } None => { error!("Failed to get the host address of the framebuffer"); return; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b365fae77..8091310ff 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -22,7 +22,6 @@ use log::{debug, error, info, warn}; use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::XhciConfig; -use util::aio::Iovec; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; @@ -1721,21 +1720,11 @@ impl XhciDevice { } else { trb.parameter }; - if !self + + let mut hvas = self .mem_space - .address_in_memory(GuestAddress(dma_addr), chunk as u64) - { - bail!( - "Invalid Address for transfer: base 0x{:X}, size {}", - dma_addr, - chunk - ); - } - if let Some(hva) = self.mem_space.get_host_address(GuestAddress(dma_addr)) { - vec.push(Iovec::new(hva, chunk as u64)); - } else { - bail!("HVA not existed {:x}", dma_addr); - } + .get_address_map(GuestAddress(dma_addr), chunk as u64)?; + vec.append(&mut hvas); } } let (_, ep_number) = endpoint_id_to_number(locked_xfer.epid as u8); diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index e3b808ae5..12fc122d9 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -409,14 +409,14 @@ impl FuseBuffer { buf.len }; - let hva = sys_mem - .get_host_address(buf.addr) - .with_context(|| "read file error: get hva failed.")?; - - let iov = vec![libc::iovec { - iov_base: hva as *mut libc::c_void, - iov_len: len as usize, - }]; + let mut iov = Vec::new(); + let hvas = sys_mem.get_address_map(buf.addr, len as u64)?; + for addr in hvas.into_iter() { + iov.push(libc::iovec { + iov_base: addr.iov_base as *mut libc::c_void, + iov_len: addr.iov_len as usize, + }) + } let ret = unsafe { if is_read { diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index cfa5c1c5a..4c6db8897 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1154,6 +1154,10 @@ impl VirtioDevice for Balloon { } Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } pub fn qmp_balloon(target: u64) -> bool { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index e3191f229..86fbf772d 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1320,6 +1320,10 @@ impl VirtioDevice for Block { Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } // SAFETY: Send and Sync is not auto-implemented for `Sender` type. diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 027feb4ec..e77d38953 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -15,6 +15,7 @@ use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::slice::from_raw_parts_mut; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; @@ -1449,6 +1450,8 @@ pub struct Gpu { consoles: Vec>>>, /// Eventfd for device deactivate. deactivate_evts: Vec, + /// Device is broken or not. + broken: Arc, } /// SAFETY: The raw pointer in rust doesn't impl Send, all write operations @@ -1465,6 +1468,7 @@ impl Gpu { )), consoles: Vec::new(), deactivate_evts: Vec::new(), + broken: Arc::new(AtomicBool::new(false)), } } @@ -1661,4 +1665,8 @@ impl VirtioDevice for Gpu { } unregister_event_helper(None, &mut self.deactivate_evts) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index daea4fd15..113cef937 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -742,14 +742,23 @@ impl NetIoHandler { let mut iovecs = Vec::new(); for elem_iov in elem_iovecs.iter() { // elem_iov.addr has been checked in pop_avail(). - let host_addr = mem_space - .get_host_address_from_cache(elem_iov.addr, cache) - .unwrap(); - let iovec = libc::iovec { - iov_base: host_addr as *mut libc::c_void, - iov_len: elem_iov.len as libc::size_t, - }; - iovecs.push(iovec); + let mut len = elem_iov.len; + let mut start = elem_iov.addr; + loop { + let io_vec = mem_space + .get_host_address_from_cache(start, cache) + .map(|(hva, fr_len)| libc::iovec { + iov_base: hva as *mut libc::c_void, + iov_len: std::cmp::min(elem_iov.len, fr_len as u32) as libc::size_t, + }) + .unwrap(); + start = start.unchecked_add(io_vec.iov_len as u64); + len -= io_vec.iov_len as u32; + iovecs.push(io_vec); + if len == 0 { + break; + } + } } iovecs } @@ -1667,6 +1676,10 @@ impl VirtioDevice for Net { self.ctrl_info = None; Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } // SAFETY: Send and Sync is not auto-implemented for `Sender` type. diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 3e4a33186..145ecd3b8 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -16,6 +16,7 @@ use std::os::unix::fs::FileTypeExt; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::rc::Rc; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -226,6 +227,8 @@ pub struct Rng { state: RngState, /// Eventfd for device deactivate deactivate_evts: Vec, + /// Device is broken or not. + broken: Arc, } impl Rng { @@ -238,6 +241,7 @@ impl Rng { driver_features: 0, }, deactivate_evts: Vec::new(), + broken: Arc::new(AtomicBool::new(false)), } } @@ -356,6 +360,10 @@ impl VirtioDevice for Rng { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.deactivate_evts) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } impl StateTransfer for Rng { diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 9385dfe44..6705bc94f 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -352,6 +352,10 @@ impl VirtioDevice for ScsiCntlr { } Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } fn build_event_notifier(fd: RawFd, handler: Rc) -> EventNotifier { diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 2bd7c6646..123f1ce24 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -330,6 +330,10 @@ impl VirtioDevice for Serial { Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.device_broken + } } impl StateTransfer for Serial { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 57a41e48d..96af6d175 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -35,7 +35,7 @@ use std::cmp; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; @@ -424,6 +424,8 @@ pub trait VirtioDevice: Send + AsAny { fn has_control_queue(&mut self) -> bool { false } + + fn get_device_broken(&self) -> &Arc; } /// The trait for trace descriptions of virtio device interactions @@ -468,18 +470,17 @@ pub fn report_virtio_error( pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) -> Result { let mut start: usize = 0; let mut end: usize = 0; - let mut hva; for iov in iovec { - end = cmp::min(start + iov.len as usize, buf.len()); - hva = mem_space - .get_host_address(iov.addr) - .with_context(|| "Map iov base failed")?; - mem_to_buf(&mut buf[start..end], hva)?; - if end >= buf.len() { - break; + let addr_map = mem_space.get_address_map(iov.addr, iov.len as u64)?; + for addr in addr_map.into_iter() { + end = cmp::min(start + addr.iov_len as usize, buf.len()); + mem_to_buf(&mut buf[start..end], addr.iov_base)?; + if end >= buf.len() { + return Ok(end); + } + start = end; } - start = end; } Ok(end) } @@ -520,16 +521,8 @@ fn gpa_hva_iovec_map( let mut hva_iovec = Vec::new(); for elem in gpa_elemiovec.iter() { - let hva = mem_space.get_host_address(elem.addr).with_context(|| { - format!( - "Map iov base {:x?}, iov len {:?} failed", - elem.addr, elem.len - ) - })?; - hva_iovec.push(Iovec { - iov_base: hva, - iov_len: u64::from(elem.len), - }); + let mut hva_vec = mem_space.get_address_map(elem.addr, elem.len as u64)?; + hva_iovec.append(&mut hva_vec); iov_size += elem.len as u64; } diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index a2a5657d4..34f9e7a82 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -14,7 +14,7 @@ use std::cmp::min; use std::mem::size_of; use std::num::Wrapping; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{fence, Ordering}; +use std::sync::atomic::{fence, AtomicBool, Ordering}; use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; @@ -26,7 +26,9 @@ use super::{ checked_offset_mem, ElemIovec, Element, VringOps, INVALID_VECTOR_NUM, VIRTQ_DESC_F_INDIRECT, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE, }; -use crate::{virtio_has_feature, VirtioError, VIRTIO_F_RING_EVENT_IDX}; +use crate::{ + report_virtio_error, virtio_has_feature, VirtioError, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, +}; /// When host consumes a buffer, don't interrupt the guest. const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; @@ -114,9 +116,78 @@ impl QueueConfig { } } + pub fn get_desc_size(&self) -> u64 { + min(self.size, self.max_size) as u64 * DESCRIPTOR_LEN + } + + pub fn get_used_size(&self, features: u64) -> u64 { + let size = if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { + 2_u64 + } else { + 0_u64 + }; + + size + VRING_FLAGS_AND_IDX_LEN + (min(self.size, self.max_size) as u64) * USEDELEM_LEN + } + + pub fn get_avail_size(&self, features: u64) -> u64 { + let size = if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { + 2_u64 + } else { + 0_u64 + }; + + size + VRING_FLAGS_AND_IDX_LEN + + (min(self.size, self.max_size) as u64) * (size_of::() as u64) + } + pub fn reset(&mut self) { *self = Self::new(self.max_size); } + + pub fn set_addr_cache( + &mut self, + mem_space: Arc, + interrupt_cb: Arc, + features: u64, + broken: &Arc, + ) { + self.addr_cache.desc_table_host = + if let Some((addr, size)) = mem_space.addr_cache_init(self.desc_table) { + if size < self.get_desc_size() { + report_virtio_error(interrupt_cb.clone(), features, broken); + 0_u64 + } else { + addr + } + } else { + 0_u64 + }; + + self.addr_cache.avail_ring_host = + if let Some((addr, size)) = mem_space.addr_cache_init(self.avail_ring) { + if size < self.get_avail_size(features) { + report_virtio_error(interrupt_cb.clone(), features, broken); + 0_u64 + } else { + addr + } + } else { + 0_u64 + }; + + self.addr_cache.used_ring_host = + if let Some((addr, size)) = mem_space.addr_cache_init(self.used_ring) { + if size < self.get_used_size(features) { + report_virtio_error(interrupt_cb.clone(), features, broken); + 0_u64 + } else { + addr + } + } else { + 0_u64 + }; + } } /// Virtio used element. @@ -338,7 +409,7 @@ impl SplitVringDesc { } else { bail!("Found two indirect descriptor elem in one request"); } - desc_table_host = sys_mem + (desc_table_host, _) = sys_mem .get_host_address_from_cache(desc.addr, cache) .with_context(|| "Failed to get descriptor table entry host address")?; queue_size = desc.get_desc_num(); diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 3407ddd3f..26f527896 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -406,23 +406,26 @@ impl VirtioMmioDevice { let queue_num = locked_state.config_space.queue_num; let queue_type = locked_state.config_space.queue_type; let queues_config = &mut locked_state.config_space.queues_config[0..queue_num]; - let cloned_mem_space = self.mem_space.clone(); + let dev_lock = self.device.lock().unwrap(); + let features = + (dev_lock.get_driver_features(1) as u64) << 32 | dev_lock.get_driver_features(0) as u64; + let broken = dev_lock.get_device_broken(); + for q_config in queues_config.iter_mut() { - q_config.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(q_config.desc_table) - .unwrap_or(0); - q_config.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(q_config.avail_ring) - .unwrap_or(0); - q_config.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(q_config.used_ring) - .unwrap_or(0); + q_config.set_addr_cache( + self.mem_space.clone(), + self.interrupt_cb.clone().unwrap(), + features, + broken, + ); + let queue = Queue::new(*q_config, queue_type)?; if !queue.is_valid(&self.mem_space) { bail!("Invalid queue"); } self.queues.push(Arc::new(Mutex::new(queue))); } + drop(dev_lock); drop(locked_state); let mut queue_evts = Vec::>::new(); @@ -671,22 +674,22 @@ impl StateTransfer for VirtioMmioDevice { } let mut locked_state = self.state.lock().unwrap(); locked_state.as_mut_bytes().copy_from_slice(state); - let cloned_mem_space = self.mem_space.clone(); let mut queue_states = locked_state.config_space.queues_config [0..locked_state.config_space.queue_num] .to_vec(); + let dev_lock = self.device.lock().unwrap(); + let features = + (dev_lock.get_driver_features(1) as u64) << 32 | dev_lock.get_driver_features(0) as u64; + let broken = dev_lock.get_device_broken(); self.queues = queue_states .iter_mut() .map(|queue_state| { - queue_state.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(queue_state.desc_table) - .unwrap_or(0); - queue_state.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(queue_state.avail_ring) - .unwrap_or(0); - queue_state.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(queue_state.used_ring) - .unwrap_or(0); + queue_state.set_addr_cache( + self.mem_space.clone(), + self.interrupt_cb.clone().unwrap(), + features, + broken, + ); Arc::new(Mutex::new( Queue::new(*queue_state, locked_state.config_space.queue_type).unwrap(), )) @@ -733,11 +736,11 @@ impl MigrationHook for VirtioMmioDevice { mod tests { use std::io::Write; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use util::num_ops::read_u32; - use super::*; use crate::VIRTIO_TYPE_BLOCK; + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + use std::sync::atomic::AtomicBool; + use util::num_ops::read_u32; fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); @@ -775,6 +778,7 @@ mod tests { pub config_space: Vec, pub b_active: bool, pub b_realized: bool, + pub broken: Arc, } impl VirtioDeviceTest { @@ -790,6 +794,7 @@ mod tests { b_active: false, b_realized: false, config_space, + broken: Arc::new(AtomicBool::new(false)), } } } @@ -870,6 +875,10 @@ mod tests { self.b_active = true; Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } #[test] diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 758ecf71f..5daa96885 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -42,6 +42,7 @@ use crate::{ virtio_has_feature, NotifyEventFds, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, VirtioInterruptType, }; + use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, @@ -728,22 +729,20 @@ impl VirtioPciDevice { let queue_type = common_cfg_lock.queue_type; let queues_config = &mut common_cfg_lock.queues_config; let mut locked_queues = self.queues.lock().unwrap(); + let dev_lock = self.device.lock().unwrap(); + let features = + (dev_lock.get_driver_features(1) as u64) << 32 | dev_lock.get_driver_features(0) as u64; + let broken = dev_lock.get_device_broken(); for q_config in queues_config.iter_mut() { if !q_config.ready { debug!("queue is not ready, please check your init process"); } else { - q_config.addr_cache.desc_table_host = self - .sys_mem - .get_host_address(q_config.desc_table) - .unwrap_or(0); - q_config.addr_cache.avail_ring_host = self - .sys_mem - .get_host_address(q_config.avail_ring) - .unwrap_or(0); - q_config.addr_cache.used_ring_host = self - .sys_mem - .get_host_address(q_config.used_ring) - .unwrap_or(0); + q_config.set_addr_cache( + self.sys_mem.clone(), + self.interrupt_cb.clone().unwrap(), + features, + broken, + ); } let queue = Queue::new(*q_config, queue_type).unwrap(); if q_config.ready && !queue.is_valid(&self.sys_mem) { @@ -753,6 +752,7 @@ impl VirtioPciDevice { let arc_queue = Arc::new(Mutex::new(queue)); locked_queues.push(arc_queue.clone()); } + drop(dev_lock); drop(locked_queues); update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); @@ -1396,17 +1396,17 @@ impl StateTransfer for VirtioPciDevice { { let queue_type = self.common_config.lock().unwrap().queue_type; let mut locked_queues = self.queues.lock().unwrap(); - let cloned_mem_space = self.sys_mem.clone(); + let dev_lock = self.device.lock().unwrap(); + let features = (dev_lock.get_driver_features(1) as u64) << 32 + | dev_lock.get_driver_features(0) as u64; + let broken = dev_lock.get_device_broken(); for queue_state in pci_state.queues_config[0..pci_state.queue_num].iter_mut() { - queue_state.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(queue_state.desc_table) - .unwrap_or(0); - queue_state.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(queue_state.avail_ring) - .unwrap_or(0); - queue_state.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(queue_state.used_ring) - .unwrap_or(0); + queue_state.set_addr_cache( + self.sys_mem.clone(), + self.interrupt_cb.clone().unwrap(), + features, + broken, + ); locked_queues.push(Arc::new(Mutex::new( Queue::new(*queue_state, queue_type).unwrap(), ))) @@ -1477,6 +1477,7 @@ mod tests { pub device_features: u64, pub driver_features: u64, pub is_activated: bool, + pub broken: Arc, } impl VirtioDeviceTest { @@ -1485,6 +1486,7 @@ mod tests { device_features: 0xFFFF_FFF0, driver_features: 0, is_activated: false, + broken: Arc::new(AtomicBool::new(false)), } } } @@ -1536,6 +1538,10 @@ mod tests { self.is_activated = true; Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } macro_rules! com_cfg_read_test { diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 1f4db547d..de06c3890 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -435,6 +435,10 @@ impl VirtioDevice for Net { Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } #[cfg(test)] diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index e8aeb8edb..7b486defa 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -368,6 +368,10 @@ impl VirtioDevice for Vsock { fn reset(&mut self) -> Result<()> { self.backend.as_ref().unwrap().set_running(false) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } impl StateTransfer for Vsock { diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 081583a05..c161958e6 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -14,6 +14,7 @@ use crate::VirtioError; use anyhow::{anyhow, bail, Context, Result}; use std::cmp; use std::io::Write; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -44,6 +45,8 @@ pub struct Block { state: BlockState, /// Vhost user client client: Option>>, + /// Device is broken or not. + broken: Arc, } impl Block { @@ -53,6 +56,7 @@ impl Block { state: BlockState::default(), mem_space: mem_space.clone(), client: None, + broken: Arc::new(AtomicBool::new(false)), } } @@ -297,4 +301,8 @@ impl VirtioDevice for Block { Ok(()) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 405be8977..8fc6d691c 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -22,6 +22,7 @@ use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use log::error; @@ -111,6 +112,8 @@ pub struct Fs { call_events: Vec>, deactivate_evts: Vec, enable_irqfd: bool, + /// Device is broken or not. + broken: Arc, } impl Fs { @@ -132,6 +135,7 @@ impl Fs { call_events: Vec::>::new(), deactivate_evts: Vec::new(), enable_irqfd, + broken: Arc::new(AtomicBool::new(false)), } } } @@ -281,4 +285,8 @@ impl VirtioDevice for Fs { self.realize() } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 050a2ee71..a70991c00 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -316,4 +316,8 @@ impl VirtioDevice for Net { VIRTIO_NET_F_CTRL_VQ, ) } + + fn get_device_broken(&self) -> &Arc { + &self.broken + } } -- Gitee From b9ff44d2e1585867b994c4ef9d0f52d4e11cb463 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Wed, 28 Jun 2023 17:15:42 +0800 Subject: [PATCH 1199/2187] balloon: balloon device adapt to different memory regions has different share Signed-off-by: jiewangqun --- address_space/src/region.rs | 10 ++ machine/src/lib.rs | 6 +- virtio/src/device/balloon.rs | 258 +++++++++++++++++++++-------------- 3 files changed, 163 insertions(+), 111 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 769ed9bc6..be32ede70 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -478,6 +478,16 @@ impl Region { self.mem_mapping.as_ref().map(|r| r.host_address()) } + pub fn get_host_share(&self) -> Option { + if self.region_type != RegionType::Ram + && self.region_type != RegionType::RamDevice + && self.region_type != RegionType::RomDevice + { + return None; + } + self.mem_mapping.as_ref().map(|r| r.mem_shared()) + } + /// Get the file information if this region is backed by host-memory. /// Return `None` if it is not a Ram-type region. pub fn get_file_backend(&self) -> Option { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6d93a6844..9d02fc052 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -370,11 +370,7 @@ pub trait MachineOps { fn add_virtio_balloon(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_balloon(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); - let balloon = Arc::new(Mutex::new(Balloon::new( - &device_cfg, - sys_mem.clone(), - vm_config.machine_config.mem_config.mem_share, - ))); + let balloon = Arc::new(Mutex::new(Balloon::new(&device_cfg, sys_mem.clone()))); Balloon::object_init(balloon.clone()); if cfg_args.contains("virtio-balloon-device") { let device = VirtioMmioDevice::new(sys_mem, balloon); diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 4c6db8897..1170cae67 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -221,6 +221,38 @@ impl Request { Ok(request) } + fn balloon_deflate_page(&self, hvaset: &mut Vec<(u64, bool)>) { + let mut free_len: u64 = 0; + let mut start_addr: u64 = 0; + let mut last_addr: u64 = 0; + + while let Some((hva, _)) = hvaset.pop() { + if last_addr == 0 { + free_len += 1; + start_addr = hva; + } else if hva == last_addr + BALLOON_PAGE_SIZE { + free_len += 1; + } else { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + libc::MADV_WILLNEED, + ); + free_len = 1; + start_addr = hva; + } + + last_addr = hva; + } + + if free_len != 0 { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + libc::MADV_WILLNEED, + ); + } + } /// Mark balloon page with `MADV_DONTNEED` or `MADV_WILLNEED`. /// /// # Arguments @@ -233,104 +265,117 @@ impl Request { address_space: &Arc, mem: &Arc>, ) { - let mem_share = mem.lock().unwrap().mem_share(); - let advice = if req_type && !mem_share { - libc::MADV_DONTNEED - } else if req_type { - libc::MADV_REMOVE - } else { - libc::MADV_WILLNEED - }; let mut last_addr: u64 = 0; - let mut count_iov: u64 = 4; + let mut last_share = false; let mut free_len: u64 = 0; let mut start_addr: u64 = 0; + let mut hvaset = Vec::new(); for iov in self.iovec.iter() { let mut offset = 0; - let mut hvaset = Vec::new(); + while let Some(pfn) = iov_to_buf::(address_space, iov, offset) { offset += std::mem::size_of::() as u64; let gpa: GuestAddress = GuestAddress((pfn as u64) << VIRTIO_BALLOON_PFN_SHIFT); - let hva = match mem.lock().unwrap().get_host_address(gpa) { - Some(addr) => addr, + let (hva, shared) = match mem.lock().unwrap().get_host_address(gpa) { + Some((addr, mem_share)) => (addr, mem_share), None => { // Windows OS will populate the address with PA of 0 continue; } }; - hvaset.push(hva); + hvaset.push((hva, shared)); } - hvaset.sort_by_key(|&b| Reverse(b)); - let host_page_size = host_page_size(); - // If host_page_size equals BALLOON_PAGE_SIZE, we can directly call the - // madvise function without any problem. And if the advice is MADV_WILLNEED, - // we just hint the whole host page it lives on, since we can't do anything - // smaller. - if host_page_size == BALLOON_PAGE_SIZE || advice == libc::MADV_WILLNEED { - while let Some(hva) = hvaset.pop() { - if last_addr == 0 { - free_len += 1; - start_addr = hva; - } else if hva == last_addr + BALLOON_PAGE_SIZE { - free_len += 1; + } + hvaset.sort_by_key(|&b| Reverse(b.0)); + + if req_type == BALLOON_DEFLATE_EVENT { + self.balloon_deflate_page(&mut hvaset); + return; + } + + let host_page_size = host_page_size(); + let mut advice = 0; + // If host_page_size equals BALLOON_PAGE_SIZE and have the same share properties, + // we can directly call the madvise function without any problem. And if the advice is MADV_WILLNEED, + // we just hint the whole host page it lives on, since we can't do anything + // smaller. + if host_page_size == BALLOON_PAGE_SIZE { + while let Some((hva, share)) = hvaset.pop() { + if last_addr == 0 { + free_len += 1; + start_addr = hva; + last_share = share; + if share { + advice = libc::MADV_REMOVE; } else { - memory_advise( - start_addr as *const libc::c_void as *mut _, - (free_len * BALLOON_PAGE_SIZE) as usize, - advice, - ); - free_len = 1; - start_addr = hva; + advice = libc::MADV_DONTNEED; } - - if count_iov == iov.iov_len { - memory_advise( - start_addr as *const libc::c_void as *mut _, - (free_len * BALLOON_PAGE_SIZE) as usize, - advice, - ); + } else if hva == last_addr + BALLOON_PAGE_SIZE && last_share == share { + free_len += 1; + } else { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + advice, + ); + free_len = 1; + start_addr = hva; + last_share = share; + if share { + advice = libc::MADV_REMOVE; + } else { + advice = libc::MADV_DONTNEED; } - count_iov += std::mem::size_of::() as u64; - last_addr = hva; } - } else { - let mut host_page_bitmap = - BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); - while let Some(hva) = hvaset.pop() { - if host_page_bitmap.base_address == 0 { - if let Some(base_addr) = round_down(hva, host_page_size) { - host_page_bitmap.base_address = base_addr; - } else { - error!( - "Failed to round_down, hva: {}, align: {}", - hva, host_page_size - ); - } - } else if host_page_bitmap.base_address + host_page_size < hva { - host_page_bitmap = - BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); - continue; - } - if let Err(ref e) = - host_page_bitmap.set_bit((hva % host_page_size) / BALLOON_PAGE_SIZE) - { + last_addr = hva; + } + if free_len != 0 { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + advice, + ); + } + } else { + let mut host_page_bitmap = BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); + while let Some((hva, share)) = hvaset.pop() { + if host_page_bitmap.base_address == 0 { + if let Some(base_addr) = round_down(hva, host_page_size) { + host_page_bitmap.base_address = base_addr; + } else { error!( - "Failed to set bit with index: {} :{:?}", - (hva % host_page_size) / BALLOON_PAGE_SIZE, - e + "Failed to round_down, hva: {}, align: {}", + hva, host_page_size ); } - if host_page_bitmap.is_full(host_page_size / BALLOON_PAGE_SIZE) { - memory_advise( - host_page_bitmap.base_address as *const libc::c_void as *mut _, - host_page_size as usize, - advice, - ); - host_page_bitmap = - BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); + } else if host_page_bitmap.base_address + host_page_size < hva { + host_page_bitmap = BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); + continue; + } + + if let Err(ref e) = + host_page_bitmap.set_bit((hva % host_page_size) / BALLOON_PAGE_SIZE) + { + error!( + "Failed to set bit with index: {} :{:?}", + (hva % host_page_size) / BALLOON_PAGE_SIZE, + e + ); + } + if host_page_bitmap.is_full(host_page_size / BALLOON_PAGE_SIZE) { + if share { + advice = libc::MADV_REMOVE; + } else { + advice = libc::MADV_DONTNEED; } + memory_advise( + host_page_bitmap.base_address as *const libc::c_void as *mut _, + host_page_size as usize, + advice, + ); + host_page_bitmap = BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); } } } @@ -338,19 +383,19 @@ impl Request { fn release_pages(&self, mem: &Arc>) { for iov in self.iovec.iter() { - let advice = if mem.lock().unwrap().mem_share() { - libc::MADV_REMOVE - } else { - libc::MADV_DONTNEED - }; let gpa: GuestAddress = iov.iov_base; - let hva = match mem.lock().unwrap().get_host_address(gpa) { - Some(addr) => addr, + let (hva, shared) = match mem.lock().unwrap().get_host_address(gpa) { + Some((hva, shared)) => (hva, shared), None => { error!("Can not get host address, gpa: {}", gpa.raw_value()); continue; } }; + let advice = if shared { + libc::MADV_REMOVE + } else { + libc::MADV_DONTNEED + }; memory_advise( hva as *const libc::c_void as *mut _, iov.iov_len as usize, @@ -372,33 +417,34 @@ struct BlnMemoryRegion { flags_padding: u64, /// Region Page size reg_page_size: Option, + /// Region shared or not + mem_share: bool, } struct BlnMemInfo { regions: Mutex>, enabled: bool, - mem_share: bool, } impl BlnMemInfo { - fn new(mem_share: bool) -> BlnMemInfo { + fn new() -> BlnMemInfo { BlnMemInfo { regions: Mutex::new(Vec::new()), enabled: false, - mem_share, } } - fn get_host_address(&self, addr: GuestAddress) -> Option { + fn get_host_address(&self, addr: GuestAddress) -> Option<(u64, bool)> { let all_regions = self.regions.lock().unwrap(); for i in 0..all_regions.len() { if addr.raw_value() < all_regions[i].guest_phys_addr + all_regions[i].memory_size && addr.raw_value() >= all_regions[i].guest_phys_addr { - return Some( + return Some(( all_regions[i].userspace_addr + addr.raw_value() - all_regions[i].guest_phys_addr, - ); + all_regions[i].mem_share, + )); } } None @@ -428,6 +474,7 @@ impl BlnMemInfo { userspace_addr, flags_padding: 0_u64, reg_page_size, + mem_share: fr.owner.get_host_share().unwrap_or(false), }); } else { error!("Failed to get host address!"); @@ -444,6 +491,7 @@ impl BlnMemInfo { userspace_addr: host_addr + fr.offset_in_region, flags_padding: 0_u64, reg_page_size, + mem_share: false, }; for (index, mr) in mem_regions.iter().enumerate() { if mr.guest_phys_addr == target.guest_phys_addr @@ -470,11 +518,6 @@ impl BlnMemInfo { } size } - - /// Get Balloon memory type, shared or private. - fn mem_share(&self) -> bool { - self.mem_share - } } impl Listener for BlnMemInfo { @@ -862,7 +905,7 @@ impl Balloon { /// # Arguments /// /// * `bln_cfg` - Balloon configuration. - pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc, mem_share: bool) -> Balloon { + pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc) -> Balloon { let mut device_features = 1u64 << VIRTIO_F_VERSION_1; if bln_cfg.deflate_on_oom { device_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM; @@ -880,7 +923,7 @@ impl Balloon { actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, interrupt_cb: None, - mem_info: Arc::new(Mutex::new(BlnMemInfo::new(mem_share))), + mem_info: Arc::new(Mutex::new(BlnMemInfo::new())), mem_space, event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), deactivate_evts: Vec::new(), @@ -1261,7 +1304,7 @@ mod tests { }; let mem_space = address_space_init(); - let mut bln = Balloon::new(&bln_cfg, mem_space, false); + let mut bln = Balloon::new(&bln_cfg, mem_space); assert_eq!(bln.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); @@ -1310,7 +1353,7 @@ mod tests { }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space, false); + let balloon = Balloon::new(&bln_cfg, mem_space); let ret_data = [0, 0, 0, 0, 1, 0, 0, 0]; let mut read_data: Vec = vec![0; 8]; let addr = 0x00; @@ -1332,7 +1375,7 @@ mod tests { }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space, false); + let balloon = Balloon::new(&bln_cfg, mem_space); let ret_data = [1, 0, 0, 0, 0, 0, 0, 0]; let mut read_data: Vec = vec![0; 8]; let addr = 0x4; @@ -1354,7 +1397,7 @@ mod tests { }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space, false); + let balloon = Balloon::new(&bln_cfg, mem_space); let mut read_data: Vec = vec![0; 8]; let addr: u64 = 0xffff_ffff_ffff_ffff; assert_eq!(balloon.get_balloon_memory_size(), 0); @@ -1375,7 +1418,7 @@ mod tests { }; let mem_space = address_space_init(); - let mut balloon = Balloon::new(&bln_cfg, mem_space, false); + let mut balloon = Balloon::new(&bln_cfg, mem_space); let write_data = [1, 0, 0, 0]; let addr = 0x00; assert_eq!(balloon.get_balloon_memory_size(), 0); @@ -1394,10 +1437,10 @@ mod tests { membuf_percent: 0, monitor_interval: 0, }; - let mut bln = Balloon::new(&bln_cfg, mem_space.clone(), false); + let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); bln.realize().unwrap(); let ram_fr1 = create_flat_range(0, MEMORY_SIZE, 0); - let blninfo = BlnMemInfo::new(false); + let blninfo = BlnMemInfo::new(); assert!(blninfo .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) .is_ok()); @@ -1578,7 +1621,7 @@ mod tests { membuf_percent: 0, monitor_interval: 0, }; - let mut bln = Balloon::new(&bln_cfg, mem_space.clone(), false); + let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); assert!(bln .activate(mem_space, interrupt_cb, &queues, queue_evts) .is_err()); @@ -1592,16 +1635,19 @@ mod tests { blndef.memory_size = 0x8000; blndef.userspace_addr = 0; - let blninfo = BlnMemInfo::new(false); + let blninfo = BlnMemInfo::new(); assert_eq!(blninfo.priority(), 0); blninfo.regions.lock().unwrap().push(blndef); assert_eq!(blninfo.get_host_address(GuestAddress(0x200)), None); - assert_eq!(blninfo.get_host_address(GuestAddress(0x420)), Some(0x20)); + assert_eq!( + blninfo.get_host_address(GuestAddress(0x420)), + Some((0x20, false)) + ); let ram_size = 0x800; let ram_fr1 = create_flat_range(0, ram_size, 0); - let blninfo = BlnMemInfo::new(false); + let blninfo = BlnMemInfo::new(); assert!(blninfo .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) .is_ok()); @@ -1646,7 +1692,7 @@ mod tests { monitor_interval: 0, }; let mem_space = address_space_init(); - let mut bln = Balloon::new(&bln_cfg, mem_space, false); + let mut bln = Balloon::new(&bln_cfg, mem_space); assert_eq!(bln.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); -- Gitee From 4c0e7ab151df80b1a359ce6cf284f08fffcbdfd8 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 4 May 2023 15:56:43 +0800 Subject: [PATCH 1200/2187] mst/memory: Add more test cases add test case of 2 numa nodes , memory read/write baloon address across nodes Signed-off-by: jiewangqun --- tests/mod_test/tests/balloon_test.rs | 157 +++++++++++++++++++++++++++ tests/mod_test/tests/memory_test.rs | 78 ++++++++++++- 2 files changed, 234 insertions(+), 1 deletion(-) diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index bee1af2f1..b39f5058d 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -32,6 +32,7 @@ const TIMEOUT_US: u64 = 15 * 1000 * 1000; const MBSIZE: u64 = 1024 * 1024; const MEM_BUFFER_PERCENT_DEFAULT: u32 = 50; const MONITOR_INTERVAL_SECOND_DEFAULT: u32 = 10; +const ADDRESS_BASE: u64 = 0x4000_0000; fn read_lines(filename: String) -> io::Lines> { let file = File::open(filename).unwrap(); @@ -162,6 +163,73 @@ impl VirtioBalloonTest { auto_queue, } } + + pub fn numa_node_new() -> Self { + let mut args: Vec<&str> = Vec::new(); + let mut extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + args.append(&mut extra_args); + + let cpu = 8; + let cpu_args = format!( + "-smp {},sockets=1,cores=4,threads=2 -cpu host,pmu=on -m 2G", + cpu + ); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-object memory-backend-file,size=1G,id=mem0,host-nodes=0-1,policy=bind,share=on,mem-path=test.fd" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = + "-object memory-backend-memfd,size=1G,id=mem1,host-nodes=0-1,policy=bind,mem-prealloc=true" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=0,cpus=0-3,memdev=mem0" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=1,cpus=4-7,memdev=mem1" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=0,dst=1,val=30".split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=1,dst=0,val=30".split(' ').collect(); + args.append(&mut extra_args); + + extra_args = "-device virtio-balloon-pci,id=drv0,bus=pcie.0,addr=0x4.0" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new_bymem(test_state.clone(), 2 * MBSIZE, 4096); + let allocator = machine.allocator.clone(); + + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + dev.borrow_mut().init(4, 0); + + let features = dev.borrow_mut().get_device_features(); + let inf_queue; + let def_queue; + + let ques = dev + .borrow_mut() + .init_device(test_state.clone(), allocator.clone(), features, 2); + inf_queue = ques[0].clone(); + def_queue = ques[1].clone(); + + VirtioBalloonTest { + device: dev, + state: test_state, + allocator, + inf_queue, + def_queue, + fpr_queue: None, + auto_queue: None, + } + } } fn inflate_fun(shared: bool) { @@ -977,3 +1045,92 @@ fn auto_balloon_test_001() { .config_readl(offset_of!(VirtioBalloonConfig, actual) as u64); assert_eq!(actual, 131070); } + +#[test] +/// balloon device deactive config test +/// TestStep: +/// 1.Init device +/// 2.geust send msg to host by auto balloon +/// Expect: +/// 1/2.Success +fn balloon_numa1() { + let page_num = 255_u32; + let mut idx = 0_u32; + let balloon = VirtioBalloonTest::numa_node_new(); + + let free_page = 0x4000_0000 + ADDRESS_BASE - 100 * PAGE_SIZE_UNIT; + let pfn = (free_page >> 12) as u32; + let pfn_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); + while idx < page_num { + balloon + .state + .borrow_mut() + .writel(pfn_addr + 4 * idx as u64, pfn + idx); + balloon + .state + .borrow_mut() + .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + idx += 1; + } + + // begin inflate addresses + let mut loop_num = 0_u32; + let mut msg = Vec::new(); + + while loop_num < page_num { + let entry = TestVringDescEntry { + data: pfn_addr + (loop_num as u64 * 4), + len: 4, + write: false, + }; + msg.push(entry); + loop_num += 1; + } + let free_head = balloon + .inf_queue + .borrow_mut() + .add_chained(balloon.state.clone(), msg); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.inf_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.inf_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + // begin deflate addresses + let mut loop_num = 0_u32; + let mut msg = Vec::new(); + + while loop_num < page_num { + let entry = TestVringDescEntry { + data: pfn_addr + (loop_num as u64 * 4), + len: 4, + write: false, + }; + msg.push(entry); + loop_num += 1; + } + let free_head = balloon + .def_queue + .borrow_mut() + .add_chained(balloon.state.clone(), msg); + balloon + .device + .borrow_mut() + .kick_virtqueue(balloon.state.clone(), balloon.def_queue.clone()); + balloon.device.borrow_mut().poll_used_elem( + balloon.state.clone(), + balloon.def_queue.clone(), + free_head, + TIMEOUT_US, + &mut None, + false, + ); + + balloon.state.borrow_mut().stop(); +} diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index ae566b739..c9b553986 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -378,7 +378,9 @@ fn ram_device_region_readwrite() { .state .borrow_mut() .memread(addr + PAGE_SIZE - 1, std::mem::size_of::() as u64); - assert_eq!(ret, [0x00u8; 8]); + let mut data_err = [0x00u8; 8]; + data_err[0] = 0x01u8; + assert_eq!(ret, data_err); memory_test .state @@ -647,3 +649,77 @@ fn ram_readwrite_numa() { test_state.borrow_mut().stop(); } + +/// Ram read and write Test. +/// TestStep: +/// 1. Start device. +/// 2. Write some data("test memory read write") to the address. +/// And the read/write will across numa. +/// 3. Read data from the address and check it. +/// 4. Destroy device. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn ram_readwrite_numa1() { + let mut args: Vec<&str> = Vec::new(); + let mut extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + args.append(&mut extra_args); + + let cpu = 8; + let cpu_args = format!( + "-smp {},sockets=1,cores=4,threads=2 -cpu host,pmu=on -m 2G", + cpu + ); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-object memory-backend-file,size=1G,id=mem0,host-nodes=0-1,policy=bind,share=on,mem-path=test.fd" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = + "-object memory-backend-memfd,size=1G,id=mem1,host-nodes=0-1,policy=bind,mem-prealloc=true" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=0,cpus=0-3,memdev=mem0" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa node,nodeid=1,cpus=4-7,memdev=mem1" + .split(' ') + .collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=0,dst=1,val=30".split(' ').collect(); + args.append(&mut extra_args); + extra_args = "-numa dist,src=1,dst=0,val=30".split(' ').collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + + let str = "test memory read write"; + let start_base = ADDRESS_BASE + MEM_SIZE * 1024 * 1024 / 2 - 4; + test_state.borrow_mut().memwrite(start_base, str.as_bytes()); + let ret = test_state + .borrow_mut() + .memread(start_base, str.len() as u64); + assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); + test_state.borrow_mut().qmp("{\"execute\": \"query-mem\"}"); + + let file = File::create(&RAM_DEV_PATH).unwrap(); + file.set_len(PAGE_SIZE).unwrap(); + let qmp_str = format!( + "{{ \"execute\": \"update_region\", + \"arguments\": {{ \"update_type\": \"add\", + \"region_type\": \"ram_device_region\", + \"offset\": 1099511627776, + \"size\": 4096, + \"priority\": 99, + \"device_fd_path\": {:?} }} }}", + RAM_DEV_PATH + ); + test_state.borrow_mut().qmp(&qmp_str); + + test_state.borrow_mut().qmp("{\"execute\": \"query-mem\"}"); + remove_file(RAM_DEV_PATH.to_string()); + test_state.borrow_mut().stop(); +} -- Gitee From e0ae72469467f9dbfbbda61bb850aebc42995b79 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Tue, 6 Jun 2023 19:23:44 +0800 Subject: [PATCH 1201/2187] log: Add log rotate function The log rotate function is added. When the size of VM logs reaches 100MB or the next day, VM logs are dumped. Signed-off-by: mayunlong Signed-off-by: Keqian Zhu --- machine/src/micro_vm/syscall.rs | 1 + machine/src/standard_vm/aarch64/syscall.rs | 1 + machine/src/standard_vm/x86_64/syscall.rs | 1 + src/main.rs | 21 +-- util/src/logger.rs | 171 +++++++++++++++++---- vhost_user_fs/src/main.rs | 27 +--- 6 files changed, 148 insertions(+), 74 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index edafa3b53..8dd40361a 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -121,6 +121,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_unlink), #[cfg(target_arch = "aarch64")] BpfRule::new(libc::SYS_unlinkat), + BpfRule::new(libc::SYS_renameat), #[cfg(target_arch = "x86_64")] BpfRule::new(libc::SYS_mkdir), #[cfg(target_arch = "aarch64")] diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 9986cde3a..b1f340bf9 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -129,6 +129,7 @@ pub fn syscall_whitelist() -> Vec { madvise_rule(), BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), + BpfRule::new(libc::SYS_renameat), BpfRule::new(libc::SYS_socket), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_bind), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index c88303228..6a04f2653 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -134,6 +134,7 @@ pub fn syscall_whitelist() -> Vec { madvise_rule(), BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), + BpfRule::new(libc::SYS_renameat), BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_socket), #[cfg(target_env = "gnu")] diff --git a/src/main.rs b/src/main.rs index 5dc3f1f8d..95c85f6d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::io::Write; -use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -94,24 +93,8 @@ fn run() -> Result<()> { set_test_enabled(); } - match cmd_args.value_of("display log") { - Some(ref logfile_path) if !logfile_path.is_empty() => { - let logfile = std::fs::OpenOptions::new() - .read(false) - .write(true) - .append(true) - .create(true) - .mode(0o640) - .open(logfile_path) - .with_context(|| "Failed to open log file")?; - logger::init_logger_with_env(Some(Box::new(logfile))) - .with_context(|| "Failed to init logger.")?; - } - _ => { - logger::init_logger_with_env(Some(Box::new(std::io::stderr()))) - .with_context(|| "Failed to init logger.")?; - } - } + let logfile_path = cmd_args.value_of("display log").unwrap_or_default(); + logger::init_log(logfile_path)?; std::panic::set_hook(Box::new(|panic_msg| { set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); diff --git a/util/src/logger.rs b/util/src/logger.rs index 80629a608..7cb5e8e3d 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -10,14 +10,25 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::io::prelude::*; +use std::fs::File; +use std::io::Write; +use std::num::Wrapping; +use std::os::unix::fs::OpenOptionsExt; +use std::path::Path; use std::sync::Mutex; +use std::time::UNIX_EPOCH; -use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; +use anyhow::{Context, Result}; +use log::{Level, LevelFilter, Log, Metadata, Record}; use crate::time::{get_format_time, gettime}; use crate::unix::gettid; +// Max size of the log file is 100MB. +const LOG_ROTATE_SIZE_MAX: usize = 100 * 1024 * 1024; +// Logs are retained for seven days. +const LOG_ROTATE_COUNT_MAX: u32 = 7; + fn format_now() -> String { let (sec, nsec) = gettime(); let format_time = get_format_time(sec as i64); @@ -34,34 +45,96 @@ fn format_now() -> String { ) } +struct FileRotate { + handler: Box, + path: String, + current_size: Wrapping, + create_day: i32, +} + +impl FileRotate { + fn rotate_file(&mut self, size_inc: usize) -> Result<()> { + if self.path.is_empty() { + return Ok(()); + } + + self.current_size += Wrapping(size_inc); + let sec = gettime().0; + let today = get_format_time(sec as i64)[2]; + if self.current_size < Wrapping(LOG_ROTATE_SIZE_MAX) && self.create_day == today { + return Ok(()); + } + + // Remove the oldest log file. + let mut rotate_count = LOG_ROTATE_COUNT_MAX - 1; + let old_name = format!("{}{}", self.path, rotate_count); + if Path::new(&old_name).exists() { + std::fs::remove_file(&old_name) + .with_context(|| format! {"Failed to remove log file {}", old_name})?; + } + + // Rename files to older file name. + let mut path_from; + let mut path_to = old_name; + while rotate_count != 0 { + rotate_count -= 1; + path_from = self.path.clone(); + if rotate_count != 0 { + path_from += &rotate_count.to_string(); + } + if Path::new(&path_from).exists() { + std::fs::rename(&path_from, &path_to).with_context( + || format! {"Failed to rename log file from {} to {}", path_from, path_to}, + )?; + } + path_to = path_from; + } + + // Update log file. + self.handler = Box::new(open_log_file(&self.path)?); + self.current_size = Wrapping(0); + self.create_day = today; + Ok(()) + } +} + /// Format like "%year-%mon-%dayT%hour:%min:%sec.%nsec struct VmLogger { - handler: Option>>, + rotate: Mutex, level: Level, } impl Log for VmLogger { fn enabled(&self, metadata: &Metadata) -> bool { - self.handler.is_some() && metadata.level() <= self.level + metadata.level() <= self.level } fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - let pid = unsafe { libc::getpid() }; - let tid = gettid(); - - self.handler.as_ref().map(|writer| { - writer.lock().unwrap().write_fmt(format_args!( - "{:<5}: [{}][{}][{}: {}]:{}: {}\n", - format_now(), - pid, - tid, - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.level(), - record.args() - )) - }); + if !self.enabled(record.metadata()) { + return; + } + + let pid = unsafe { libc::getpid() }; + let tid = gettid(); + let formatmsg = format_args!( + "{:<5}: [{}][{}][{}: {}]:{}: {}\n", + format_now(), + pid, + tid, + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.level(), + record.args() + ) + .to_string(); + + let mut rotate = self.rotate.lock().unwrap(); + if let Err(e) = rotate.handler.write_all(formatmsg.as_bytes()) { + println!("Failed to log message {:?}", e); + return; + } + if let Err(e) = rotate.rotate_file(formatmsg.as_bytes().len()) { + println!("Failed to rotate log files {:?}", e); } } @@ -69,19 +142,35 @@ impl Log for VmLogger { } fn init_vm_logger( - level: Option, - logfile: Option>, -) -> Result<(), log::SetLoggerError> { - let buffer = logfile.map(Mutex::new); - let logger = VmLogger { - level: level.unwrap_or(Level::Info), - handler: buffer, + level: Level, + logfile: Box, + logfile_path: String, +) -> Result<()> { + let current_size; + let create_day; + if logfile_path.is_empty() { + current_size = Wrapping(0); + create_day = 0; + } else { + let metadata = File::open(&logfile_path)?.metadata()?; + current_size = Wrapping(metadata.len() as usize); + let mod_time = metadata.modified()?; + let sec = mod_time.duration_since(UNIX_EPOCH)?.as_secs(); + create_day = get_format_time(sec as i64)[2]; }; + let rotate = Mutex::new(FileRotate { + handler: logfile, + path: logfile_path, + current_size, + create_day, + }); - log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(LevelFilter::Trace)) + let logger = VmLogger { rotate, level }; + log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(LevelFilter::Trace))?; + Ok(()) } -pub fn init_logger_with_env(logfile: Option>) -> Result<(), SetLoggerError> { +fn init_logger_with_env(logfile: Box, logfile_path: String) -> Result<()> { let level = match std::env::var("STRATOVIRT_LOG_LEVEL") { Ok(l) => match l.to_lowercase().as_str() { "error" => Level::Error, @@ -94,7 +183,27 @@ pub fn init_logger_with_env(logfile: Option>) -> Result<() _ => Level::Info, }; - init_vm_logger(Some(level), logfile)?; - + init_vm_logger(level, logfile, logfile_path)?; Ok(()) } + +fn open_log_file(path: &str) -> Result { + std::fs::OpenOptions::new() + .read(false) + .write(true) + .append(true) + .create(true) + .mode(0o640) + .open(path) + .with_context(|| format!("Failed to open log file {}", path)) +} + +pub fn init_log(path: String) -> Result<()> { + let logfile: Box = if path.is_empty() { + Box::new(std::io::stderr()) + } else { + Box::new(open_log_file(&path)?) + }; + init_logger_with_env(logfile, path.clone()) + .with_context(|| format!("Failed to init logger: {}", path)) +} diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 6189562d2..310e46163 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -25,7 +25,6 @@ pub mod virtio_fs; use std::collections::HashSet; use std::io::Write; -use std::os::unix::fs::OpenOptionsExt; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -132,9 +131,9 @@ fn parse_capabilities(cmd_args: &arg_parser::ArgMatches) -> Result Result<()> { let cmd_args = create_args_parser().get_matches()?; - if let Some(logfile_path) = cmd_args.value_of("display log") { - init_log(logfile_path)?; - } + let logfile_path = cmd_args.value_of("display log").unwrap_or_default(); + logger::init_log(logfile_path)?; + signal_handler::register_kill_signal(); set_panic_hook(); match real_main(&cmd_args) { @@ -207,26 +206,6 @@ fn update_capabilities(cmd_args: &ArgMatches) -> Result<()> { Ok(()) } -fn init_log(logfile_path: String) -> Result<()> { - if logfile_path.is_empty() { - logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) - .with_context(|| "Failed to init logger")?; - } else { - let logfile = std::fs::OpenOptions::new() - .read(false) - .write(true) - .append(true) - .create(true) - .mode(0o640) - .open(logfile_path.clone()) - .with_context(|| format!("Failed to open log file {}", logfile_path))?; - logger::init_logger_with_env(Some(Box::new(logfile))) - .with_context(|| format!("Failed to init logger {}", logfile_path))?; - } - - Ok(()) -} - fn set_panic_hook() { std::panic::set_hook(Box::new(|panic_msg| { TempCleaner::clean(); -- Gitee From b1ca9bbd8879f44d81e8bf0102a948e7dd5dba1d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 13 Jul 2023 23:46:56 +0800 Subject: [PATCH 1202/2187] snapshot: check snapshot size before allocating snapshot table in snapshot deleting If snapshot table is empty, we should not alloc any cluster for snapshot table. Check it before deleting. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 35 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 9ffff300c..97d56af0c 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -610,28 +610,31 @@ impl Qcow2Driver { bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); let l1_table_clusters = bytes_to_clusters(snap.l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); - let new_snapshot_table_offset: u64; + let mut new_snapshot_table_offset = 0_u64; let mut err_msg: String = "".to_string(); let mut error_stage = 0; // Using loop for error handling. #[allow(clippy::never_loop)] loop { - new_snapshot_table_offset = self - .alloc_cluster(new_snapshots_table_clusters, true) - .unwrap_or_else(|e| { - err_msg = format!("{:?}", e); - error_stage = 1; - 0 - }); - if new_snapshot_table_offset == 0 { - break; - } + if new_snapshots_table_clusters != 0 { + new_snapshot_table_offset = self + .alloc_cluster(new_snapshots_table_clusters, true) + .unwrap_or_else(|e| { + err_msg = format!("{:?}", e); + error_stage = 1; + 0 + }); + + if new_snapshot_table_offset == 0 { + break; + } - if let Err(e) = self.snapshot.save_snapshot_table(new_snapshot_table_offset) { - err_msg = format!("{:?}", e); - error_stage = 2; - break; + if let Err(e) = self.snapshot.save_snapshot_table(new_snapshot_table_offset) { + err_msg = format!("{:?}", e); + error_stage = 2; + break; + } } // Decrease the refcounts of clusters referenced by the snapshot. @@ -731,7 +734,7 @@ impl Qcow2Driver { self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; } } - if error_stage >= 2 { + if error_stage >= 2 && new_snapshots_table_clusters != 0 { self.free_cluster( new_snapshot_table_offset, new_snapshots_table_clusters, -- Gitee From d6583c3c3b620087602248022a33cf487027c5ff Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 14 Jul 2023 00:34:04 +0800 Subject: [PATCH 1203/2187] refcount: add check before allocating cluster Don't allow to alloc 0 size cluster. Check before allocating. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/refcount.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 635d2bd3e..b7a9c871f 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -528,6 +528,9 @@ impl RefCount { } pub fn alloc_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { + if size == 0 { + bail!("Don't allow to alloc 0 size cluster!"); + } let addr = self.find_free_cluster(header, size)?; let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); self.update_refcount(addr, clusters, 1, true, &Qcow2DiscardType::Other)?; -- Gitee From ecf7da0bd669755c6510ebf66ac34bf00af700d8 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 11 Jul 2023 15:18:55 +0800 Subject: [PATCH 1204/2187] qcow2: support config cache size Support config l2 cache size and refcount table size on command line. Signed-off-by: zhouli57 --- block_backend/src/lib.rs | 10 +- block_backend/src/qcow2/mod.rs | 13 +- block_backend/src/qcow2/refcount.rs | 16 +- block_backend/src/qcow2/table.rs | 23 ++- devices/src/scsi/disk.rs | 5 +- machine/src/micro_vm/mod.rs | 2 + machine/src/standard_vm/mod.rs | 146 ++++++++++--------- machine_manager/src/config/drive.rs | 29 +++- machine_manager/src/config/machine_config.rs | 2 +- machine_manager/src/config/mod.rs | 4 +- machine_manager/src/config/scsi.rs | 6 + machine_manager/src/config/usb.rs | 3 + machine_manager/src/qmp/qmp_schema.rs | 4 + util/src/aio/mod.rs | 4 +- virtio/src/device/block.rs | 5 +- 15 files changed, 174 insertions(+), 98 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 28474b881..b824c50a5 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -42,6 +42,7 @@ pub enum BlockStatus { #[derive(Debug, Clone)] pub struct BlockProperty { + pub id: String, pub format: DiskFormat, pub iothread: Option, pub direct: bool, @@ -49,6 +50,8 @@ pub struct BlockProperty { pub buf_align: u32, pub discard: bool, pub write_zeroes: WriteZeroesState, + pub l2_cache_size: Option, + pub refcount_cache_size: Option, } pub trait BlockDriverOps: Send { @@ -88,7 +91,6 @@ pub trait BlockDriverOps: Send { pub fn create_block_backend( file: File, aio: Aio, - drive_id: String, prop: BlockProperty, ) -> Result>>> { match prop.format { @@ -114,11 +116,11 @@ pub fn create_block_backend( QCOW2_LIST .lock() .unwrap() - .insert(drive_id.clone(), new_qcow2.clone()); + .insert(prop.id.clone(), new_qcow2.clone()); let cloned_qcow2 = new_qcow2.clone(); // NOTE: we can drain request when request in io thread. let drain = prop.iothread.is_some(); - let cloned_drive_id = drive_id.clone(); + let cloned_drive_id = prop.id.clone(); let exit_notifier = Arc::new(move || { let mut locked_qcow2 = cloned_qcow2.lock().unwrap(); info!("clean up qcow2 {:?} resources.", cloned_drive_id); @@ -129,7 +131,7 @@ pub fn create_block_backend( locked_qcow2.drain_request(); } }) as Arc; - TempCleaner::add_exit_notifier(drive_id, exit_notifier); + TempCleaner::add_exit_notifier(prop.id, exit_notifier); Ok(new_qcow2) } } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 9ffff300c..91ca22601 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -218,9 +218,9 @@ impl Qcow2Driver { qcow2.check().with_context(|| "Invalid header")?; qcow2 .table - .init_table(&qcow2.header) + .init_table(&qcow2.header, &conf) .with_context(|| "Failed to create qcow2 table")?; - qcow2.refcount.init_refcount_info(&qcow2.header, conf); + qcow2.refcount.init_refcount_info(&qcow2.header, &conf); qcow2 .load_refcount_table() .with_context(|| "Failed to load refcount table")?; @@ -1665,6 +1665,7 @@ mod test { let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); let (req_align, buf_align) = get_file_alignment(&image.file, true); let conf = BlockProperty { + id: path.to_string(), format: DiskFormat::Qcow2, iothread: None, direct: true, @@ -1672,6 +1673,8 @@ mod test { buf_align, discard: false, write_zeroes: WriteZeroesState::Off, + l2_cache_size: None, + refcount_cache_size: None, }; image.file = file.try_clone().unwrap(); (image, Qcow2Driver::new(file, aio, conf).unwrap()) @@ -1939,6 +1942,7 @@ mod test { let image_bits = 24; let cluster_bits = 16; let conf = BlockProperty { + id: path.to_string(), format: DiskFormat::Qcow2, iothread: None, direct: true, @@ -1946,6 +1950,8 @@ mod test { buf_align: 512, discard: true, write_zeroes: WriteZeroesState::Off, + l2_cache_size: None, + refcount_cache_size: None, }; let image = TestImage::new(path, image_bits, cluster_bits); let mut qcow2_driver = image.create_qcow2_driver(conf); @@ -1969,6 +1975,7 @@ mod test { let image_bits = 24; let cluster_bits = 16; let conf = BlockProperty { + id: path.to_string(), format: DiskFormat::Qcow2, iothread: None, direct: true, @@ -1976,6 +1983,8 @@ mod test { buf_align: 512, discard: true, write_zeroes: WriteZeroesState::On, + l2_cache_size: None, + refcount_cache_size: None, }; let image = TestImage::new(path, image_bits, cluster_bits); let mut qcow2_driver = image.create_qcow2_driver(conf); diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 635d2bd3e..3be1b8ac7 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -27,7 +27,7 @@ use crate::{ }; // The max refcount table size default is 4 clusters; -const MAX_REFTABLE_NUM: u32 = 4; +const MAX_REFTABLE_NUM: u64 = 4; #[derive(Eq, PartialEq, Clone)] pub enum Qcow2DiscardType { @@ -85,7 +85,7 @@ impl RefCount { RefCount { refcount_table: Vec::new(), sync_aio, - refcount_blk_cache: Qcow2Cache::new(MAX_REFTABLE_NUM as usize), + refcount_blk_cache: Qcow2Cache::default(), discard_list: Vec::new(), discard_passthrough: Vec::new(), free_cluster_index: 0, @@ -100,7 +100,7 @@ impl RefCount { } } - pub fn init_refcount_info(&mut self, header: &QcowHeader, conf: BlockProperty) { + pub fn init_refcount_info(&mut self, header: &QcowHeader, conf: &BlockProperty) { // Update discard_pass_through depend on config. self.discard_passthrough.push(Qcow2DiscardType::Always); if conf.discard { @@ -119,6 +119,13 @@ impl RefCount { let refcount_bits = 1 << header.refcount_order; self.refcount_max = 1 << (refcount_bits - 1); self.refcount_max += self.refcount_max - 1; + let sz = if let Some(rc_size) = conf.refcount_cache_size { + rc_size / header.cluster_size() + } else { + MAX_REFTABLE_NUM + }; + info!("Driver {} refcount cache size {}", conf.id, sz); + self.refcount_blk_cache = Qcow2Cache::new(sz as usize); } pub fn start_of_cluster(&self, offset: u64) -> u64 { @@ -668,6 +675,7 @@ mod test { let file = image_create(path, img_bits, cluster_bits); let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); let conf = BlockProperty { + id: path.to_string(), format: DiskFormat::Qcow2, iothread: None, direct: true, @@ -675,6 +683,8 @@ mod test { buf_align: 512, discard: false, write_zeroes: WriteZeroesState::Off, + l2_cache_size: None, + refcount_cache_size: None, }; let cloned_file = file.try_clone().unwrap(); (Qcow2Driver::new(file, aio, conf).unwrap(), cloned_file) diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 8eb46879a..3bdf71771 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -13,13 +13,17 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::{Context, Result}; +use log::info; use super::ENTRY_BITS; -use crate::qcow2::{ - cache::{CacheTable, Qcow2Cache}, - header::QcowHeader, - SyncAioInfo, ENTRY_SIZE, L1_TABLE_OFFSET_MASK, L2_TABLE_OFFSET_MASK, QCOW2_OFFSET_COMPRESSED, - QCOW2_OFLAG_ZERO, +use crate::{ + qcow2::{ + cache::{CacheTable, Qcow2Cache}, + header::QcowHeader, + SyncAioInfo, ENTRY_SIZE, L1_TABLE_OFFSET_MASK, L2_TABLE_OFFSET_MASK, + QCOW2_OFFSET_COMPRESSED, QCOW2_OFLAG_ZERO, + }, + BlockProperty, }; use util::num_ops::div_round_up; @@ -98,7 +102,7 @@ impl Qcow2Table { } } - pub fn init_table(&mut self, header: &QcowHeader) -> Result<()> { + pub fn init_table(&mut self, header: &QcowHeader, conf: &BlockProperty) -> Result<()> { let max_l2_entries = div_round_up(header.size, header.cluster_size()).with_context(|| { format!( @@ -115,7 +119,12 @@ impl Qcow2Table { header.cluster_size() ) })?; - let cache_size = std::cmp::min(max_l2_cache, MAX_L2_CACHE_SIZE / header.cluster_size()); + let cache_size = if let Some(l2_cache) = conf.l2_cache_size { + l2_cache / header.cluster_size() + } else { + std::cmp::min(max_l2_cache, MAX_L2_CACHE_SIZE / header.cluster_size()) + }; + info!("Driver {} l2 cache size {}", conf.id, cache_size); let l2_table_cache: Qcow2Cache = Qcow2Cache::new(cache_size as usize); self.cluster_bits = header.cluster_bits as u64; self.cluster_size = header.cluster_size(); diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 9caa8287c..854423be4 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -164,6 +164,7 @@ impl ScsiDevice { let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type)?; let conf = BlockProperty { + id: drive_id, format: self.config.format, iothread, direct: self.config.direct, @@ -171,8 +172,10 @@ impl ScsiDevice { buf_align: self.buf_align, discard: false, write_zeroes: WriteZeroesState::Off, + l2_cache_size: self.config.l2_cache_size, + refcount_cache_size: self.config.refcount_cache_size, }; - let backend = create_block_backend(file, aio, drive_id, conf)?; + let backend = create_block_backend(file, aio, conf)?; let disk_size = backend.lock().unwrap().disk_size()?; self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 1076e6892..afc75d5e9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1233,6 +1233,8 @@ impl DeviceInterface for LightMachine { discard: false, write_zeroes: WriteZeroesState::Off, format: DiskFormat::Raw, + l2_cache_size: None, + refcount_cache_size: None, }; if let Err(e) = config.check() { error!("{:?}", e); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6298a9091..2afa53e48 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -22,7 +22,7 @@ pub use error::StandardVmError; pub use aarch64::StdMachine; use log::error; use machine_manager::event_loop::EventLoop; -use machine_manager::qmp::qmp_schema::UpdateRegionArgument; +use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::{config::get_cameradev_config, machine::MachineLifecycle}; #[cfg(not(target_env = "musl"))] use ui::{ @@ -61,9 +61,9 @@ use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use machine_manager::config::{ - get_chardev_config, get_netdev_config, get_pci_df, BlkDevConfig, ChardevType, ConfigCheck, - DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, - ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, + get_chardev_config, get_netdev_config, get_pci_df, memory_unit_conversion, BlkDevConfig, + ChardevType, ConfigCheck, DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, + NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; @@ -825,6 +825,8 @@ impl StdMachine { discard: conf.discard, write_zeroes: conf.write_zeroes, format: conf.format, + l2_cache_size: conf.l2_cache_size, + refcount_cache_size: conf.refcount_cache_size, }; dev.check()?; dev @@ -1379,78 +1381,17 @@ impl DeviceInterface for StdMachine { } fn blockdev_add(&self, args: Box) -> Response { - let mut config = DriveConfig { - id: args.node_name, - path_on_host: args.file.filename.clone(), - read_only: args.read_only.unwrap_or(false), - direct: true, - iops: args.iops, - // TODO Add aio option by qmp, now we set it based on "direct". - aio: AioEngine::Native, - media: "disk".to_string(), - discard: false, - write_zeroes: WriteZeroesState::Off, - format: DiskFormat::Raw, - }; - if args.cache.is_some() && !args.cache.unwrap().direct.unwrap_or(true) { - config.direct = false; - config.aio = AioEngine::Off; - } - if let Some(discard) = args.discard { - let ret = discard.as_str().parse::(); - if ret.is_err() { - let err_msg = format!( - "Invalid discard argument '{}', expected 'unwrap' or 'ignore'", - discard - ); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(err_msg), - None, - ); - } - config.discard = ret.unwrap().into(); - } - if let Some(detect_zeroes) = args.detect_zeroes { - let state = detect_zeroes.as_str().parse::(); - if state.is_err() { - let err_msg = format!( - "Invalid write-zeroes argument '{}', expected 'on | off | unmap'", - detect_zeroes - ); + let config = match parse_blockdev(&args) { + Ok(config) => config, + Err(e) => { + error!("{:?}", e); return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(err_msg), + qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, ); } - config.write_zeroes = state.unwrap(); - } - if let Some(format) = args.driver { - match format.as_str().parse::() { - Ok(fmt) => config.format = fmt, - Err(e) => { - error!("{:?}", e); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - } - } - if let Err(e) = config.check() { - error!("{:?}", e); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } - // Check whether path is valid after configuration check - if let Err(e) = config.check_path() { - error!("{:?}", e); - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ); - } + }; + // Register drive backend file for hotplug drive. if let Err(e) = self.register_drive_file( &config.id, @@ -2000,6 +1941,67 @@ impl DeviceInterface for StdMachine { } } +fn parse_blockdev(args: &BlockDevAddArgument) -> Result { + let mut config = DriveConfig { + id: args.node_name.clone(), + path_on_host: args.file.filename.clone(), + read_only: args.read_only.unwrap_or(false), + direct: true, + iops: args.iops, + // TODO Add aio option by qmp, now we set it based on "direct". + aio: AioEngine::Native, + media: "disk".to_string(), + discard: false, + write_zeroes: WriteZeroesState::Off, + format: DiskFormat::Raw, + l2_cache_size: None, + refcount_cache_size: None, + }; + if args.cache.is_some() && !args.cache.as_ref().unwrap().direct.unwrap_or(true) { + config.direct = false; + config.aio = AioEngine::Off; + } + if let Some(discard) = args.discard.as_ref() { + config.discard = discard + .as_str() + .parse::() + .with_context(|| { + format!( + "Invalid discard argument '{}', expected 'unwrap' or 'ignore'", + discard + ) + })? + .into(); + } + if let Some(detect_zeroes) = args.detect_zeroes.as_ref() { + config.write_zeroes = detect_zeroes + .as_str() + .parse::() + .with_context(|| { + format!( + "Invalid write-zeroes argument '{}', expected 'on | off | unmap'", + detect_zeroes + ) + })?; + } + if let Some(format) = args.driver.as_ref() { + config.format = format.as_str().parse::()?; + } + if let Some(l2_cache) = args.l2_cache_size.as_ref() { + let sz = memory_unit_conversion(l2_cache) + .with_context(|| format!("Invalid l2 cache size: {}", l2_cache))?; + config.l2_cache_size = Some(sz); + } + if let Some(rc_cache) = args.refcount_cache_size.as_ref() { + let sz = memory_unit_conversion(rc_cache) + .with_context(|| format!("Invalid refcount cache size: {}", rc_cache))?; + config.refcount_cache_size = Some(sz); + } + config.check()?; + config.check_path()?; + Ok(config) +} + #[cfg(not(target_env = "musl"))] fn send_input_event(key: String, value: String) -> Result<()> { match key.as_str() { diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 1b05901d8..fc055ed5e 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -21,8 +21,8 @@ use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check}; use crate::config::{ - check_arg_too_long, get_chardev_socket_path, CmdParser, ConfigCheck, ExBool, VmConfig, - DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, CmdParser, ConfigCheck, + ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use crate::qmp::qmp_schema; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; @@ -74,6 +74,8 @@ pub struct BlkDevConfig { pub discard: bool, pub write_zeroes: WriteZeroesState, pub format: DiskFormat, + pub l2_cache_size: Option, + pub refcount_cache_size: Option, } #[derive(Debug, Clone)] @@ -102,6 +104,8 @@ impl Default for BlkDevConfig { discard: false, write_zeroes: WriteZeroesState::Off, format: DiskFormat::Raw, + l2_cache_size: None, + refcount_cache_size: None, } } } @@ -139,6 +143,8 @@ pub struct DriveConfig { pub discard: bool, pub write_zeroes: WriteZeroesState, pub format: DiskFormat, + pub l2_cache_size: Option, + pub refcount_cache_size: Option, } impl Default for DriveConfig { @@ -154,6 +160,8 @@ impl Default for DriveConfig { discard: false, write_zeroes: WriteZeroesState::Off, format: DiskFormat::Raw, + l2_cache_size: None, + refcount_cache_size: None, } } } @@ -338,6 +346,17 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { .get_value::("detect-zeroes")? .unwrap_or(WriteZeroesState::Off); + if let Some(l2_cache) = cmd_parser.get_value::("l2-cache-size")? { + let sz = memory_unit_conversion(&l2_cache) + .with_context(|| format!("Invalid l2 cache size: {}", l2_cache))?; + drive.l2_cache_size = Some(sz); + } + if let Some(rc_cache) = cmd_parser.get_value::("refcount-cache-size")? { + let sz = memory_unit_conversion(&rc_cache) + .with_context(|| format!("Invalid refcount cache size: {}", rc_cache))?; + drive.refcount_cache_size = Some(sz); + } + drive.check()?; #[cfg(not(test))] drive.check_path()?; @@ -410,6 +429,8 @@ pub fn parse_blk( blkdevcfg.discard = drive_arg.discard; blkdevcfg.write_zeroes = drive_arg.write_zeroes; blkdevcfg.format = drive_arg.format; + blkdevcfg.l2_cache_size = drive_arg.l2_cache_size; + blkdevcfg.refcount_cache_size = drive_arg.refcount_cache_size; blkdevcfg.check()?; Ok(blkdevcfg) } @@ -538,7 +559,9 @@ impl VmConfig { .push("media") .push("discard") .push("detect-zeroes") - .push("format"); + .push("format") + .push("l2-cache-size") + .push("refcount-cache-size"); cmd_parser.parse(block_config)?; let drive_cfg = parse_drive(cmd_parser)?; diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 8b80a9a8e..1b2f41746 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -656,7 +656,7 @@ fn adjust_topology( /// # Arguments /// /// * `origin_value` - The origin memory value from user. -fn memory_unit_conversion(origin_value: &str) -> Result { +pub fn memory_unit_conversion(origin_value: &str) -> Result { if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index cc4345dbc..7b1267652 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -572,13 +572,13 @@ pub struct ExBool { } impl FromStr for ExBool { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { match s { "true" | "on" | "yes" | "unmap" => Ok(ExBool { inner: true }), "false" | "off" | "no" | "ignore" => Ok(ExBool { inner: false }), - _ => Err(()), + _ => Err(anyhow!("Unknown Exbool value {}", s)), } } } diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index f3116c507..bb2defb98 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -163,6 +163,8 @@ pub struct ScsiDevConfig { pub target: u8, pub lun: u16, pub format: DiskFormat, + pub l2_cache_size: Option, + pub refcount_cache_size: Option, } impl Default for ScsiDevConfig { @@ -180,6 +182,8 @@ impl Default for ScsiDevConfig { target: 0, lun: 0, format: DiskFormat::Raw, + l2_cache_size: None, + refcount_cache_size: None, } } } @@ -268,6 +272,8 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result scsi_dev_cfg.direct = drive_arg.direct; scsi_dev_cfg.aio_type = drive_arg.aio; scsi_dev_cfg.format = drive_arg.format; + scsi_dev_cfg.l2_cache_size = drive_arg.l2_cache_size; + scsi_dev_cfg.refcount_cache_size = drive_arg.refcount_cache_size; Ok(scsi_dev_cfg) } diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index e4381229c..86e6b5c37 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -289,6 +289,9 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result dev.scsi_cfg.read_only = drive_arg.read_only; dev.scsi_cfg.aio_type = drive_arg.aio; dev.scsi_cfg.direct = drive_arg.direct; + dev.scsi_cfg.format = drive_arg.format; + dev.scsi_cfg.l2_cache_size = drive_arg.l2_cache_size; + dev.scsi_cfg.refcount_cache_size = drive_arg.refcount_cache_size; dev.media = drive_arg.media.clone(); dev.check()?; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index eaee8a544..5136c1c35 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -762,6 +762,10 @@ pub struct blockdev_add { pub options: Option, #[serde(rename = "throttling.iops-total")] pub iops: Option, + #[serde(rename = "l2-cache-size")] + pub l2_cache_size: Option, + #[serde(rename = "refcount-cache-size")] + pub refcount_cache_size: Option, } pub type BlockDevAddArgument = blockdev_add; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 11e281e87..f6c3b5e19 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -74,14 +74,14 @@ pub enum WriteZeroesState { } impl FromStr for WriteZeroesState { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { match s { "off" => Ok(WriteZeroesState::Off), "on" => Ok(WriteZeroesState::On), "unmap" => Ok(WriteZeroesState::Unmap), - _ => Err(()), + _ => Err(anyhow!("Unknown write zeroes state {}", s)), } } } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 86fbf772d..b47467f75 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1079,6 +1079,7 @@ impl VirtioDevice for Block { let aio = Aio::new(Arc::new(BlockIoHandler::complete_func), self.blk_cfg.aio)?; let conf = BlockProperty { + id: drive_id, format: self.blk_cfg.format, iothread: self.blk_cfg.iothread.clone(), direct: self.blk_cfg.direct, @@ -1086,8 +1087,10 @@ impl VirtioDevice for Block { buf_align: self.buf_align, discard: self.blk_cfg.discard, write_zeroes: self.blk_cfg.write_zeroes, + l2_cache_size: self.blk_cfg.l2_cache_size, + refcount_cache_size: self.blk_cfg.refcount_cache_size, }; - let backend = create_block_backend(file, aio, drive_id, conf)?; + let backend = create_block_backend(file, aio, conf)?; let disk_size = backend.lock().unwrap().disk_size()?; self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; -- Gitee From 466479590b7a6eeec89b7738e0f56e8bc9bb8671 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 28 Jun 2023 09:23:39 +0800 Subject: [PATCH 1205/2187] loop-context: Support ppoll for the more precise timing The current epoll lib only supports timing in milliseconds. USB Isochronous Transfer needs timing in nanoseconds. Use ppoll to replace the timing function of epoll. Signed-off-by: Jinhao Gao --- Cargo.lock | 34 ++++++++++++++- machine_manager/src/test_server.rs | 22 ++++++++-- util/Cargo.toml | 1 + util/src/loop_context.rs | 70 ++++++++++++++---------------- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c689ceda7..7b7abf7d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ dependencies = [ "alsa-sys", "bitflags", "libc", - "nix", + "nix 0.24.3", ] [[package]] @@ -468,7 +468,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" dependencies = [ - "memoffset", + "memoffset 0.8.0", "rustc_version", ] @@ -1084,6 +1084,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.8.0" @@ -1168,6 +1177,20 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset 0.7.1", + "pin-utils", + "static_assertions", +] + [[package]] name = "nom" version = "7.1.3" @@ -1745,6 +1768,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stratovirt" version = "2.2.0" @@ -1962,6 +1991,7 @@ dependencies = [ "kvm-ioctls", "libc", "log", + "nix 0.26.2", "once_cell", "serde", "thiserror", diff --git a/machine_manager/src/test_server.rs b/machine_manager/src/test_server.rs index 73541a7f0..4c83e6587 100644 --- a/machine_manager/src/test_server.rs +++ b/machine_manager/src/test_server.rs @@ -67,17 +67,31 @@ impl EventNotifierHelper for TestSock { } fn get_min_timeout() -> i64 { - let mut min_timeout = EventLoop::get_ctx(None).unwrap().timers_min_timeout_ns(); + let mut min_timeout = EventLoop::get_ctx(None).unwrap().timers_min_duration(); for thread in IOTHREADS.lock().unwrap().iter() { let timeout = EventLoop::get_ctx(Some(&thread.id)) .unwrap() - .timers_min_timeout_ns(); - if timeout >= 0 && (timeout < min_timeout || min_timeout < 0) { + .timers_min_duration(); + if timeout.is_some() + && (min_timeout.is_none() + || (min_timeout.is_some() + && timeout.as_ref().unwrap() < min_timeout.as_ref().unwrap())) + { min_timeout = timeout; } } - min_timeout + match min_timeout { + Some(d) => { + let timeout = d.as_nanos(); + if timeout >= i64::MAX as u128 { + i64::MAX + } else { + timeout as i64 + } + } + None => -1, + } } fn update_clock(target: u64) { diff --git a/util/Cargo.toml b/util/Cargo.toml index 3bb52fbf4..5f9a84ea9 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -22,3 +22,4 @@ io-uring = "0.6.0" errno = "0.3.1" serde = { version = "1.0", features = ["derive"] } v4l2-sys-mit = "0.3.0" +nix = "0.26.2" diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 2ff98cbed..9a6a776bb 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -19,6 +19,11 @@ use std::time::{Duration, Instant}; use libc::{c_void, read, EFD_NONBLOCK}; use log::warn; +use nix::errno::Errno; +use nix::{ + poll::{ppoll, PollFd, PollFlags}, + sys::time::TimeSpec, +}; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; @@ -468,7 +473,7 @@ impl EventLoopContext { } } - self.epoll_wait_manager(self.timers_min_timeout_ms()) + self.epoll_wait_manager(self.timers_min_duration()) } pub fn iothread_run(&mut self) -> Result { @@ -479,8 +484,8 @@ impl EventLoopContext { } } - let timeout = self.timers_min_timeout_ms(); - if timeout == -1 { + let min_timeout_ns = self.timers_min_duration(); + if min_timeout_ns.is_none() { for _i in 0..AIO_PRFETCH_CYCLE_TIME { for notifier in self.events.read().unwrap().values() { let status_locked = notifier.status.lock().unwrap(); @@ -494,8 +499,7 @@ impl EventLoopContext { } } } - - self.epoll_wait_manager(timeout) + self.epoll_wait_manager(min_timeout_ns) } /// Call the function given by `func` after `delay` time. @@ -535,7 +539,7 @@ impl EventLoopContext { } /// Get the expire_time of the soonest Timer, and then translate it to duration. - fn timers_min_duration(&self) -> Option { + pub fn timers_min_duration(&self) -> Option { // The kick event happens before re-evaluate can be ignored. self.kicked.store(false, Ordering::SeqCst); let timers = self.timers.lock().unwrap(); @@ -550,34 +554,6 @@ impl EventLoopContext { ) } - fn timers_min_timeout_ms(&self) -> i32 { - match self.timers_min_duration() { - Some(d) => { - let timeout = d.as_millis(); - if timeout >= i32::MAX as u128 { - i32::MAX - 1 - } else { - timeout as i32 - } - } - None => -1, - } - } - - pub fn timers_min_timeout_ns(&self) -> i64 { - match self.timers_min_duration() { - Some(d) => { - let timeout = d.as_nanos(); - if timeout >= i64::MAX as u128 { - i64::MAX - } else { - timeout as i64 - } - } - None => -1, - } - } - /// Call function of the timers which have already expired. pub fn run_timers(&mut self) { let now = get_current_time(); @@ -598,15 +574,33 @@ impl EventLoopContext { } } - fn epoll_wait_manager(&mut self, mut time_out: i32) -> Result { - let need_kick = time_out != 0; + fn epoll_wait_manager(&mut self, mut time_out: Option) -> Result { + let need_kick = !(time_out.is_some() && *time_out.as_ref().unwrap() == Duration::ZERO); if need_kick { self.kick_me.store(true, Ordering::SeqCst); if self.kicked.load(Ordering::SeqCst) { - time_out = 0; + time_out = Some(Duration::ZERO); } } - let ev_count = match self.epoll.wait(time_out, &mut self.ready_events[..]) { + + // When time_out greater then zero, use ppoll as a more precise timer. + if time_out.is_some() && *time_out.as_ref().unwrap() != Duration::ZERO { + let time_out_spec = Some(TimeSpec::from_duration(*time_out.as_ref().unwrap())); + let pollflags = PollFlags::POLLIN | PollFlags::POLLOUT | PollFlags::POLLHUP; + let mut pollfds: [PollFd; 1] = [PollFd::new(self.epoll.as_raw_fd(), pollflags)]; + + match ppoll(&mut pollfds, time_out_spec, None) { + Ok(_) => time_out = Some(Duration::ZERO), + Err(e) if e == Errno::EINTR => time_out = Some(Duration::ZERO), + Err(e) => return Err(anyhow!(UtilError::EpollWait(e.into()))), + }; + } + + let time_out_ms = match time_out { + Some(t) => t.as_millis() as i32, + None => -1, + }; + let ev_count = match self.epoll.wait(time_out_ms, &mut self.ready_events[..]) { Ok(ev_count) => ev_count, Err(e) if e.raw_os_error() == Some(libc::EINTR) => 0, Err(e) => return Err(anyhow!(UtilError::EpollWait(e))), -- Gitee From 6411d2b516de890ca5f6c56e1ea188075c4c5be9 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 4 Jul 2023 17:08:43 +0800 Subject: [PATCH 1206/2187] usb-host: Fix hostport default bug The hostport parameter of the usbhost device is set to zero string by default if it is not configured when hotplug a usbhost device. It will result that the usb device can not be found. Signed-off-by: Jinhao Gao --- machine/src/standard_vm/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 6298a9091..1c3f44752 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1035,13 +1035,15 @@ impl StdMachine { let default_value = "0".to_string(); let hostbus = args.hostbus.as_ref().unwrap_or(&default_value); let hostaddr = args.hostaddr.as_ref().unwrap_or(&default_value); - let hostport = args.hostport.as_ref().unwrap_or(&default_value); let vendorid = args.vendorid.as_ref().unwrap_or(&default_value); let productid = args.productid.as_ref().unwrap_or(&default_value); cfg_args = format!( - "{},hostbus={},hostaddr={},hostport={},vendorid={},productid={}", - cfg_args, hostbus, hostaddr, hostport, vendorid, productid + "{},hostbus={},hostaddr={},vendorid={},productid={}", + cfg_args, hostbus, hostaddr, vendorid, productid ); + if args.hostport.is_some() { + cfg_args = format!("{},hostport={}", cfg_args, args.hostport.as_ref().unwrap()); + } self.add_usb_host(&mut locked_vmconfig, &cfg_args)?; } -- Gitee From 75d9ada0466cba8794585fa9aaaf9ec6e2e092df Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 1 Jul 2023 10:08:05 +0800 Subject: [PATCH 1207/2187] usb-host: Add two parameters of USB Isochronous Transfers Add two parameters, isobufs and isosize, which is used to setup the number and size of Isochronous Transfers buffer. Signed-off-by: Jinhao Gao --- docs/config_guidebook.md | 11 ++++++++--- machine/src/standard_vm/mod.rs | 7 +++++++ machine_manager/src/config/usb.rs | 8 +++++++- machine_manager/src/qmp/qmp_schema.rs | 2 ++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 72101972e..12057d411 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -851,23 +851,28 @@ Six properties can be set for USB Host. * hostport: the physical number of the usb host device. * vendorid: the vendor ID of the usb host device. * productid: the product ID of the usb host device. +* isobufs: the number of Isochronous Transfers buffer. If not set, default is 4. +* isobsize: the size of Isochronous Transfers buffer. If not set, default is 32. Pass through the host device identified by bus and addr: ```shell --device usb-host,id=,hostbus=,hostaddr= +-device usb-host,id=,hostbus=,hostaddr=[,isobufs=][,isobsize=] + ``` Pass through the host device identified by bus and physical port: ```shell --device usb-host,id=,hostbus=,hostport= +-device usb-host,id=,hostbus=,hostport=[,isobufs=][,isobsize=] + ``` Pass through the host device identified by the vendor and product ID: ```shell --device usb-host,id=,vendorid=,productid= +-device usb-host,id=,vendorid=,productid=[,isobufs=][,isobsize=] + ``` Note: diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 1c3f44752..a78246dbd 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1037,6 +1037,7 @@ impl StdMachine { let hostaddr = args.hostaddr.as_ref().unwrap_or(&default_value); let vendorid = args.vendorid.as_ref().unwrap_or(&default_value); let productid = args.productid.as_ref().unwrap_or(&default_value); + cfg_args = format!( "{},hostbus={},hostaddr={},vendorid={},productid={}", cfg_args, hostbus, hostaddr, vendorid, productid @@ -1044,6 +1045,12 @@ impl StdMachine { if args.hostport.is_some() { cfg_args = format!("{},hostport={}", cfg_args, args.hostport.as_ref().unwrap()); } + if args.isobufs.is_some() { + cfg_args = format!("{},isobufs={}", cfg_args, args.isobufs.as_ref().unwrap()); + } + if args.isobsize.is_some() { + cfg_args = format!("{},isobsize={}", cfg_args, args.isobsize.as_ref().unwrap()); + } self.add_usb_host(&mut locked_vmconfig, &cfg_args)?; } diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index e4381229c..539b31925 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -309,6 +309,8 @@ pub struct UsbHostConfig { pub vendorid: u16, /// The product id of the USB Host device. pub productid: u16, + pub iso_urb_frames: u32, + pub iso_urb_count: u32, } impl UsbHostConfig { @@ -336,7 +338,9 @@ pub fn parse_usb_host(cfg_args: &str) -> Result { .push("hostaddr") .push("hostport") .push("vendorid") - .push("productid"); + .push("productid") + .push("isobsize") + .push("isobufs"); cmd_parser.parse(cfg_args)?; @@ -353,6 +357,8 @@ pub fn parse_usb_host(cfg_args: &str) -> Result { .get_value::("productid")? .unwrap_or(UnsignedInteger(0)) .0 as u16, + iso_urb_frames: cmd_parser.get_value::("isobsize")?.unwrap_or(32), + iso_urb_count: cmd_parser.get_value::("isobufs")?.unwrap_or(4), }; dev.check()?; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index eaee8a544..86883cf28 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -639,6 +639,8 @@ pub struct device_add { pub hostport: Option, pub vendorid: Option, pub productid: Option, + pub isobufs: Option, + pub isobsize: Option, } pub type DeviceAddArgument = device_add; -- Gitee From 6240ea0e53b25389ecc2eb6f969f9661a641c849 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 8 May 2023 16:46:14 +0800 Subject: [PATCH 1208/2187] xhci: support iso trb transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the TRB type is ISO, a timer is created to obtain data from the device. Signed-off-by: Mingwang Li Signed-off-by: Jinhao Gao 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 --- devices/src/usb/xhci/mod.rs | 6 + devices/src/usb/xhci/xhci_controller.rs | 213 ++++++++++++++++++++++-- devices/src/usb/xhci/xhci_regs.rs | 7 +- 3 files changed, 207 insertions(+), 19 deletions(-) diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index 3f839eebc..20e205f8a 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -35,6 +35,12 @@ pub const TRB_TR_IOC: u32 = 1 << 5; pub const TRB_TR_IDT: u32 = 1 << 6; /// Direction of the data transfer. pub const TRB_TR_DIR: u32 = 1 << 16; +/// Frame ID shift. +pub const TRB_TR_FRAMEID_SHIFT: u32 = 20; +/// Frame ID mask. +pub const TRB_TR_FRAMEID_MASK: u32 = 0x7ff; +/// Start Isoch ASAP. +pub const TRB_TR_SIA: u32 = 1 << 31; /// TRB Transfer Length Mask pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; /// Setup Stage TRB Length always 8 diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 8091310ff..0929f899a 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -15,6 +15,7 @@ use std::mem::size_of; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; +use std::time::{Duration, Instant}; use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; @@ -22,12 +23,14 @@ use log::{debug, error, info, warn}; use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::XhciConfig; +use machine_manager::event_loop::EventLoop; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; use super::{ - TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_TR_DIR, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, - TRB_TR_LEN_MASK, TRB_TYPE_SHIFT, + TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_TR_DIR, TRB_TR_FRAMEID_MASK, + TRB_TR_FRAMEID_SHIFT, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TR_SIA, + TRB_TYPE_SHIFT, }; use crate::usb::{config::*, TransferOps}; use crate::usb::{ @@ -108,6 +111,8 @@ const EP_CONTEXT_EP_STATE_MASK: u32 = 0x7; const EP_CONTEXT_EP_STATE_SHIFT: u32 = 0; const EP_CONTEXT_EP_TYPE_MASK: u32 = 0x7; const EP_CONTEXT_EP_TYPE_SHIFT: u32 = 3; +const ISO_BASE_TIME_INTERVAL: u64 = 125000; +const MFINDEX_WRAP_NUM: u64 = 0x4000; type DmaAddr = u64; @@ -120,17 +125,20 @@ pub struct XhciTransfer { slotid: u32, epid: u32, in_xfer: bool, + iso_xfer: bool, + timed_xfer: bool, running_retry: bool, running_async: bool, interrupter: Arc>, ep_ring: Arc, + ep_type: EpType, ep_state: Arc, + mfindex_kick: u64, } impl XhciTransfer { fn new( - slotid: u32, - epid: u32, + ep_info: (u32, u32, EpType), in_xfer: bool, td: Vec, intr: &Arc>, @@ -142,14 +150,18 @@ impl XhciTransfer { status: TRBCCode::Invalid, td, complete: false, - slotid, - epid, + slotid: ep_info.0, + epid: ep_info.1, in_xfer, + iso_xfer: false, + timed_xfer: false, running_retry: false, running_async: false, interrupter: intr.clone(), ep_ring: ring.clone(), + ep_type: ep_info.2, ep_state: ep_state.clone(), + mfindex_kick: 0, } } @@ -165,9 +177,14 @@ impl XhciTransfer { return Ok(()); } + self.report_transfer_error()?; + + if self.ep_type == EpType::IsoIn || self.ep_type == EpType::IsoOut { + return Ok(()); + } // Set the endpoint state to halted if an error occurs in the packet. set_ep_state_helper(&self.ep_ring, &self.ep_state, EP_HALTED)?; - self.report_transfer_error()?; + Ok(()) } @@ -261,6 +278,7 @@ pub struct XhciEpContext { output_ctx_addr: Arc, state: Arc, interval: u32, + mfindex_last: u64, transfers: LinkedList>>, retry: Option>>, } @@ -276,6 +294,7 @@ impl XhciEpContext { output_ctx_addr: addr, state: Arc::new(AtomicU32::new(0)), interval: 0, + mfindex_last: 0, transfers: LinkedList::new(), retry: None, } @@ -663,6 +682,9 @@ pub struct XhciDevice { pub intrs: Vec>>, pub cmd_ring: XhciCommandRing, mem_space: Arc, + /// Runtime Register. + mfindex_start: Instant, + timer_id: Option, } impl XhciDevice { @@ -707,6 +729,8 @@ impl XhciDevice { intrs, cmd_ring: XhciCommandRing::new(mem_space), mem_space: mem_space.clone(), + mfindex_start: Instant::now(), + timer_id: None, }; let xhci = Arc::new(Mutex::new(xhci)); let clone_xhci = xhci.clone(); @@ -739,6 +763,39 @@ impl XhciDevice { pub fn run(&mut self) { self.oper.unset_usb_status_flag(USB_STS_HCH); + self.mfindex_start = Instant::now(); + } + + pub fn mfindex(&mut self) -> u64 { + let now = Instant::now(); + now.duration_since(self.mfindex_start).as_nanos() as u64 / ISO_BASE_TIME_INTERVAL + } + + pub fn mfwrap_update(&mut self) { + let bits = USB_CMD_RUN | USB_CMD_EWE; + if self.oper.get_usb_cmd() & bits == bits { + let mfindex = self.mfindex() & (MFINDEX_WRAP_NUM - 1); + let left = MFINDEX_WRAP_NUM - mfindex; + let weak_xhci = self.usb_ports[0].lock().unwrap().xhci.clone(); + + let xhci_mfwrap_timer = Box::new(move || { + let xhci = weak_xhci.upgrade().unwrap(); + let mut locked_xhci = xhci.lock().unwrap(); + + let evt = XhciEvent::new(TRBType::ErMfindexWrap, TRBCCode::Success); + if let Err(e) = locked_xhci.intrs[0].lock().unwrap().send_event(&evt) { + error!("Failed to send event: {:?}", e); + } + + locked_xhci.mfwrap_update(); + }); + if let Some(ctx) = EventLoop::get_ctx(None) { + self.timer_id = Some(ctx.timer_add( + xhci_mfwrap_timer, + Duration::from_nanos(left * ISO_BASE_TIME_INTERVAL), + )); + } + } } pub fn stop(&mut self) { @@ -773,6 +830,10 @@ impl XhciDevice { self.intrs[i].lock().unwrap().reset(); } self.cmd_ring.init(0); + + self.mfindex_start = Instant::now(); + + self.mfwrap_update(); } /// Reset xhci port. @@ -1343,6 +1404,9 @@ impl XhciDevice { GuestAddress(output_ctx + EP_CTX_OFFSET + entry_offset), ep_ctx.as_dwords(), )?; + + epctx.mfindex_last = 0; + Ok(TRBCCode::Success) } @@ -1500,7 +1564,7 @@ impl XhciDevice { } epctx.set_state(EP_RUNNING)?; let ep_state = epctx.state.clone(); - const KICK_LIMIT: u32 = 32; + const KICK_LIMIT: u32 = 256; let mut count = 0; let ring = epctx.ring.clone(); loop { @@ -1515,6 +1579,19 @@ impl XhciDevice { td } None => { + if epctx.ep_type == EpType::IsoIn || epctx.ep_type == EpType::IsoOut { + let ccode = match epctx.ep_type { + EpType::IsoIn => TRBCCode::RingOverrun, + _ => TRBCCode::RingUnderrun, + }; + let mut evt = XhciEvent::new(TRBType::ErTransfer, ccode); + evt.slot_id = slot_id as u8; + evt.ep_id = ep_id as u8; + evt.ptr = epctx.ring.dequeue.load(Ordering::Acquire); + if let Err(e) = self.intrs[0].lock().unwrap().send_event(&evt) { + error!("Failed to send event: {:?}", e); + } + } debug!("No TD in the transfer ring."); break; } @@ -1522,8 +1599,7 @@ impl XhciDevice { let in_xfer = transfer_in_direction(ep_id as u8, &td, epctx.ep_type); // NOTE: Only support primary interrupter now. let xfer = Arc::new(Mutex::new(XhciTransfer::new( - slot_id, - ep_id, + (slot_id, ep_id, epctx.ep_type), in_xfer, td, &self.intrs[0], @@ -1602,13 +1678,26 @@ impl XhciDevice { .unwrap() .clone(); let mut locked_xfer = xfer.lock().unwrap(); + if locked_xfer.timed_xfer { + let mfindex = self.mfindex(); + self.check_intr_iso_kick(&mut locked_xfer, mfindex); + if locked_xfer.running_retry { + return Ok(false); + } + locked_xfer.timed_xfer = false; + locked_xfer.running_retry = true; + } + self.device_handle_packet(&mut locked_xfer); - if locked_xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { + if !locked_xfer.iso_xfer + && locked_xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak + { debug!("USB packet status is NAK"); // NAK need to retry again. return Ok(false); } self.complete_packet(&mut locked_xfer)?; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if locked_xfer.complete { drop(locked_xfer); @@ -1680,7 +1769,104 @@ impl XhciDevice { Ok(()) } + fn calc_iso_kick(&mut self, xfer: &mut XhciTransfer, mfindex: u64) { + let epctx = &self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; + + if xfer.td[0].control & TRB_TR_SIA != 0 { + let asap = ((mfindex as u32 + epctx.interval - 1) & !(epctx.interval - 1)) as u64; + if asap >= epctx.mfindex_last && asap <= epctx.mfindex_last + epctx.interval as u64 * 4 + { + xfer.mfindex_kick = epctx.mfindex_last + epctx.interval as u64; + } else { + xfer.mfindex_kick = asap; + } + } else { + xfer.mfindex_kick = + (((xfer.td[0].control >> TRB_TR_FRAMEID_SHIFT) & TRB_TR_FRAMEID_MASK) as u64) << 3; + xfer.mfindex_kick |= mfindex & !(MFINDEX_WRAP_NUM - 1); + if xfer.mfindex_kick + 0x100 < mfindex { + xfer.mfindex_kick += MFINDEX_WRAP_NUM; + } + } + } + + fn check_intr_iso_kick(&mut self, xfer: &mut XhciTransfer, mfindex: u64) { + let epctx = &mut self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; + if xfer.mfindex_kick > mfindex { + let weak_xhci = self.usb_ports[0].lock().unwrap().xhci.clone(); + let slotid = xfer.slotid; + let epid = xfer.epid; + let xhci_ep_kick_timer = Box::new(move || { + let xhci = weak_xhci.upgrade().unwrap(); + let mut locked_xhci = xhci.lock().unwrap(); + let epctx = match locked_xhci.get_endpoint_ctx(slotid, epid) { + Ok(epctx) => epctx, + Err(e) => { + error!("Kick endpoint error: {:?}", e); + return; + } + }; + let ep_state = epctx.get_ep_state(); + if ep_state == EP_STOPPED && ep_state == EP_ERROR { + return; + } + if let Err(e) = locked_xhci.kick_endpoint(slotid, epid) { + error!("Failed to kick endpoint: {:?}", e); + } + }); + if let Some(ctx) = EventLoop::get_ctx(None) { + if self.timer_id.is_some() { + ctx.timer_del(self.timer_id.unwrap()); + } + self.timer_id = Some(ctx.timer_add( + xhci_ep_kick_timer, + Duration::from_nanos((xfer.mfindex_kick - mfindex) * ISO_BASE_TIME_INTERVAL), + )); + } + xfer.running_retry = true; + } else { + epctx.mfindex_last = xfer.mfindex_kick; + if let Some(ctx) = EventLoop::get_ctx(None) { + if self.timer_id.is_some() { + ctx.timer_del(self.timer_id.unwrap()); + self.timer_id = None; + } + } + xfer.running_retry = false; + } + } + fn do_data_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + let epctx = &self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; + match epctx.ep_type { + EpType::IntrOut | EpType::IntrIn => { + xfer.iso_xfer = false; + xfer.timed_xfer = false; + if xfer.running_retry { + return Ok(()); + } + } + EpType::BulkOut | EpType::BulkIn => { + xfer.iso_xfer = false; + xfer.timed_xfer = false; + } + EpType::IsoOut | EpType::IsoIn => { + xfer.iso_xfer = true; + xfer.timed_xfer = true; + let mfindex = self.mfindex(); + self.calc_iso_kick(xfer, mfindex); + self.check_intr_iso_kick(xfer, mfindex); + if xfer.running_retry { + return Ok(()); + } + } + _ => { + bail!( + "endpoint type: {:?} is unsupported by data transfer", + epctx.ep_type + ); + } + } self.device_handle_packet(xfer); self.complete_packet(xfer)?; Ok(()) @@ -1833,11 +2019,6 @@ impl XhciDevice { Ok(()) } - /// Get microframe index - pub fn get_mf_index(&self) -> u64 { - 0 - } - pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { let mut locked_intr = self.intrs[idx as usize].lock().unwrap(); if locked_intr.erstsz == 0 || locked_intr.erstba == 0 { diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index f56278541..b668ec944 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -261,11 +261,11 @@ impl XhciInterrupter { } let dp_idx = (self.erdp - self.er_start) / TRB_SIZE as u64; if ((self.er_ep_idx + 2) % self.er_size) as u64 == dp_idx { - error!("Event ring full error, idx {}", dp_idx); + debug!("Event ring full error, idx {}", dp_idx); let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); self.write_event(&event)?; } else if ((self.er_ep_idx + 1) % self.er_size) as u64 == dp_idx { - bail!("Event Ring full, drop Event."); + debug!("Event Ring full, drop Event."); } else { self.write_event(evt)?; } @@ -488,6 +488,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { locked_xhci.oper.set_usb_status_flag(USB_STS_SRE); } locked_xhci.oper.set_usb_cmd(value & 0xc0f); + locked_xhci.mfwrap_update(); if value & USB_CMD_HCRST == USB_CMD_HCRST { locked_xhci.reset(); } @@ -558,7 +559,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { let mut value = 0; if offset < 0x20 { if offset == 0x0 { - value = (xhci.lock().unwrap().get_mf_index() & 0x3fff) as u32; + value = (xhci.lock().unwrap().mfindex() & 0x3fff) as u32; } else { error!("Failed to read runtime registers, offset is {:x}", offset); } -- Gitee From 35bf3d2a2984d48a28da7e4473f90fea166d609c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 9 May 2023 13:22:24 +0800 Subject: [PATCH 1209/2187] usb-host: Support Isochronous Transfer. Some USB devices like camera use the isochronous transfer method to transfer data. Add the support for Isochronous Transfer from the USB device to guest. Signed-off-by: Jinhao Gao --- devices/src/usb/mod.rs | 12 + devices/src/usb/usbhost/host_usblib.rs | 86 ++++++- devices/src/usb/usbhost/mod.rs | 326 ++++++++++++++++++++++++- 3 files changed, 413 insertions(+), 11 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index eef474b6e..1edf81e34 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -85,6 +85,7 @@ pub struct UsbEndpoint { pub ep_type: u8, pub ifnum: u8, pub halted: bool, + pub max_packet_size: u32, } impl UsbEndpoint { @@ -96,6 +97,17 @@ impl UsbEndpoint { ..Default::default() } } + + fn set_max_packet_size(&mut self, raw: u16) { + let size = raw & 0x7ff; + let micro_frames: u32 = match (raw >> 11) & 3 { + 1 => 2, + 2 => 3, + _ => 1, + }; + + self.max_packet_size = size as u32 * micro_frames; + } } /// USB device common structure. diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 299889a48..135a94d72 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -15,7 +15,7 @@ use std::{ sync::{Arc, Mutex}, }; -use libc::{c_int, EPOLLIN, EPOLLOUT}; +use libc::{c_int, c_uint, c_void, EPOLLIN, EPOLLOUT}; use libusb1_sys::{ constants::{ LIBUSB_ERROR_ACCESS, LIBUSB_ERROR_BUSY, LIBUSB_ERROR_INTERRUPTED, @@ -23,15 +23,15 @@ use libusb1_sys::{ LIBUSB_ERROR_NOT_SUPPORTED, LIBUSB_ERROR_NO_DEVICE, LIBUSB_ERROR_NO_MEM, LIBUSB_ERROR_OVERFLOW, LIBUSB_ERROR_PIPE, LIBUSB_ERROR_TIMEOUT, LIBUSB_TRANSFER_CANCELLED, LIBUSB_TRANSFER_COMPLETED, LIBUSB_TRANSFER_ERROR, LIBUSB_TRANSFER_NO_DEVICE, - LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_TIMED_OUT, + LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_TIMED_OUT, LIBUSB_TRANSFER_TYPE_ISOCHRONOUS, }, - libusb_get_pollfds, libusb_pollfd, libusb_transfer, + libusb_get_pollfds, libusb_iso_packet_descriptor, libusb_pollfd, libusb_transfer, }; use log::error; use rusb::{Context, DeviceHandle, Error, Result, TransferType, UsbContext}; use vmm_sys_util::epoll::EventSet; -use super::{UsbHost, UsbHostRequest}; +use super::{IsoTransfer, UsbHost, UsbHostRequest}; use crate::usb::{UsbPacketStatus, USB_TOKEN_IN}; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; @@ -68,11 +68,21 @@ macro_rules! try_unsafe { } pub fn get_request_from_transfer(transfer: *mut libusb_transfer) -> Arc> { - // Safety: cast the raw pointer of transfer's user_data to the + // SAFETY: cast the raw pointer of transfer's user_data to the // Arc>. unsafe { Arc::from_raw((*transfer).user_data.cast::>()) } } +pub fn get_iso_transfer_from_transfer(transfer: *mut libusb_transfer) -> Arc> { + // SAFETY: cast the raw pointer of transfer's user_data to the + // Arc>. + unsafe { + let ptr = (*transfer).user_data.cast::>(); + Arc::increment_strong_count(ptr); + Arc::from_raw(ptr) + } +} + pub fn get_buffer_from_transfer(transfer: *mut libusb_transfer, len: usize) -> &'static mut [u8] { // SAFETY: cast the raw pointer of transfer's buffer which is transformed // from a slice with actual_length to a mutable slice. @@ -140,6 +150,33 @@ pub fn set_pollfd_notifiers( } } +pub fn get_iso_packet_nums(host_transfer: *mut libusb_transfer) -> u32 { + // SAFETY: host_transfer is guaranteed to be valid once created. + unsafe { (*host_transfer).num_iso_packets as u32 } +} + +pub fn set_iso_packet_length( + host_transfer: *mut libusb_transfer, + packet: u32, + max_packet_size: u32, +) { + let iso_packet_desc: *mut libusb_iso_packet_descriptor; + // SAFETY: host_transfer is guaranteed to be valid once created. + unsafe { iso_packet_desc = (*host_transfer).iso_packet_desc.as_mut_ptr() } + // SAFETY: iso_packet_desc is guaranteed to be valid once host_transfer is created + // and packet is guaranteed to be not out of boundry. + unsafe { (*iso_packet_desc.offset(packet as isize)).length = max_packet_size as c_uint } +} + +pub fn get_iso_packet_acl_length(host_transfer: *mut libusb_transfer, packet: u32) -> u32 { + let iso_packet_desc: *mut libusb_iso_packet_descriptor; + // SAFETY: host_transfer is guaranteed to be valid once created. + unsafe { iso_packet_desc = (*host_transfer).iso_packet_desc.as_mut_ptr() } + // SAFETY: iso_packet_desc is guaranteed to be valid once host_transfer is created + // and packet is guaranteed to be not out of boundry. + unsafe { (*iso_packet_desc.offset(packet as isize)).actual_length } +} + pub fn alloc_host_transfer(iso_packets: c_int) -> *mut libusb_transfer { if iso_packets < 0 { error!( @@ -190,6 +227,23 @@ extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { locked_request.complete(); } +extern "system" fn req_complete_iso(host_transfer: *mut libusb_transfer) { + if unsafe { (*host_transfer).user_data.is_null() } { + free_host_transfer(host_transfer); + return; + } + + let iso_transfer = get_iso_transfer_from_transfer(host_transfer); + let locketd_iso_transfer = iso_transfer.lock().unwrap(); + + if let Some(iso_queue) = locketd_iso_transfer.iso_queue.clone().upgrade() { + drop(locketd_iso_transfer); + let mut locked_iso_queue = iso_queue.lock().unwrap(); + let iso_transfer = locked_iso_queue.inflight.pop_front().unwrap(); + locked_iso_queue.copy.push_back(iso_transfer); + } +} + pub fn fill_transfer_by_type( transfer: *mut libusb_transfer, handle: Option<&mut DeviceHandle>, @@ -252,6 +306,28 @@ pub fn fill_transfer_by_type( } } +pub fn fill_iso_transfer( + transfer: *mut libusb_transfer, + handle: &mut DeviceHandle, + ep_number: u8, + user_data: *mut c_void, + packets: u32, + length: u32, + buffer: &mut Vec, +) { + // SAFETY: have checked the validity of transfer before call fill_iso_transfer. + unsafe { + (*transfer).dev_handle = handle.as_raw(); + (*transfer).transfer_type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + (*transfer).endpoint = ep_number; + (*transfer).callback = req_complete_iso; + (*transfer).user_data = user_data; + (*transfer).num_iso_packets = packets as c_int; + (*transfer).length = length as c_int; + (*transfer).buffer = buffer.as_mut_ptr(); + } +} + pub fn submit_host_transfer(transfer: *mut libusb_transfer) -> Result<()> { if transfer.is_null() { return Err(Error::NoMem); diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 2d7471add..24a44ba2b 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -18,9 +18,11 @@ use std::{ time::Duration, }; -use anyhow::{bail, Result}; +use anyhow::{anyhow, bail, Result}; use libc::c_int; -use libusb1_sys::libusb_transfer; +use libusb1_sys::{ + libusb_get_iso_packet_buffer_simple, libusb_set_iso_packet_lengths, libusb_transfer, +}; use log::{error, info, warn}; use rusb::{ constants::LIBUSB_CLASS_HUB, Context, Device, DeviceDescriptor, DeviceHandle, Direction, Error, @@ -30,9 +32,10 @@ use rusb::{ use crate::usb::{ config::{ USB_DEVICE_OUT_REQUEST, USB_DIRECTION_DEVICE_TO_HOST, USB_ENDPOINT_ATTR_BULK, - USB_ENDPOINT_ATTR_INT, USB_ENDPOINT_ATTR_INVALID, USB_ENDPOINT_OUT_REQUEST, - USB_INTERFACE_OUT_REQUEST, USB_REQUEST_CLEAR_FEATURE, USB_REQUEST_SET_ADDRESS, - USB_REQUEST_SET_CONFIGURATION, USB_REQUEST_SET_INTERFACE, USB_TOKEN_IN, USB_TOKEN_OUT, + USB_ENDPOINT_ATTR_INT, USB_ENDPOINT_ATTR_INVALID, USB_ENDPOINT_ATTR_ISOC, + USB_ENDPOINT_OUT_REQUEST, USB_INTERFACE_OUT_REQUEST, USB_REQUEST_CLEAR_FEATURE, + USB_REQUEST_SET_ADDRESS, USB_REQUEST_SET_CONFIGURATION, USB_REQUEST_SET_INTERFACE, + USB_TOKEN_IN, USB_TOKEN_OUT, }, descriptor::USB_MAX_INTERFACES, xhci::xhci_controller::XhciDevice, @@ -150,6 +153,186 @@ impl UsbHostRequest { unsafe impl Sync for UsbHostRequest {} unsafe impl Send for UsbHostRequest {} +pub struct IsoTransfer { + host_transfer: *mut libusb_transfer, + copy_completed: bool, + packet: u32, + buffer: Vec, + iso_queue: Weak>, +} + +impl IsoTransfer { + pub fn new(packets: u32, iso_queue: Weak>) -> Self { + let host_transfer = alloc_host_transfer(packets as i32); + Self { + host_transfer, + copy_completed: false, + packet: 0, + buffer: Vec::new(), + iso_queue, + } + } + + pub fn realize( + &mut self, + handle: &mut DeviceHandle, + packets: u32, + pid: u8, + ep_number: u8, + ep_max_packet_size: u32, + user_data: *mut libc::c_void, + ) { + let mut ep = ep_number; + let length = ep_max_packet_size * packets; + if pid == USB_TOKEN_IN { + ep |= USB_DIRECTION_DEVICE_TO_HOST; + } + self.buffer = vec![0; length as usize]; + fill_iso_transfer( + self.host_transfer, + handle, + ep, + user_data, + packets, + length, + &mut self.buffer, + ); + } + + pub fn reset(&mut self, max_packet_size: u32) { + // SAFETY: host_transfer is guaranteed to be valid once created. + unsafe { libusb_set_iso_packet_lengths(self.host_transfer, max_packet_size) }; + self.packet = 0; + self.copy_completed = false; + } + + pub fn clear(&mut self, inflight: bool) { + if inflight { + // SAFETY: host_transfer is guaranteed to be valid once created. + unsafe { + (*self.host_transfer).user_data = std::ptr::null_mut(); + } + } else { + self.buffer.clear(); + free_host_transfer(self.host_transfer); + } + } + + pub fn copy_data(&mut self, packet: Arc>, ep_max_packet_size: u32) -> bool { + let mut lockecd_packet = packet.lock().unwrap(); + let mut size: usize; + if lockecd_packet.pid == USB_TOKEN_OUT as u32 { + size = lockecd_packet.get_iovecs_size() as usize; + if size > ep_max_packet_size as usize { + size = ep_max_packet_size as usize; + } + set_iso_packet_length(self.host_transfer, self.packet, size as u32); + } else { + size = get_iso_packet_acl_length(self.host_transfer, self.packet) as usize; + if size > lockecd_packet.get_iovecs_size() as usize { + size = lockecd_packet.get_iovecs_size() as usize; + } + } + let buffer = + // SAFETY: host_transfer is guaranteed to be valid once created + // and packet is guaranteed to be not out of boundry. + unsafe { libusb_get_iso_packet_buffer_simple(self.host_transfer, self.packet) }; + + // SAFETY: buffer is already allocated and size will not be exceed + // the size of buffer. + lockecd_packet.transfer_packet( + unsafe { std::slice::from_raw_parts_mut(buffer, size) }, + size, + ); + + self.packet += 1; + self.copy_completed = self.packet == get_iso_packet_nums(self.host_transfer); + self.copy_completed + } +} + +unsafe impl Sync for IsoTransfer {} +unsafe impl Send for IsoTransfer {} + +pub struct IsoQueue { + ep_number: u8, + unused: LinkedList>>, + inflight: LinkedList>>, + copy: LinkedList>>, +} + +impl IsoQueue { + pub fn new(ep_number: u8) -> Self { + Self { + ep_number, + unused: LinkedList::new(), + inflight: LinkedList::new(), + copy: LinkedList::new(), + } + } + + pub fn realize( + &mut self, + id: &str, + handle: &mut DeviceHandle, + iso_urb_count: u32, + iso_urb_frames: u32, + ep: &UsbEndpoint, + iso_queue: Arc>, + ) -> Result<()> { + let packets: u32 = iso_urb_frames; + let pid = if ep.in_direction { + USB_TOKEN_IN + } else { + USB_TOKEN_OUT + }; + let ep_number = ep.ep_number; + let max_packet_size = ep.max_packet_size; + + for i in 0..iso_urb_count { + let iso_xfer = Arc::new(Mutex::new(IsoTransfer::new( + packets, + Arc::downgrade(&iso_queue), + ))); + + if iso_xfer.lock().unwrap().host_transfer.is_null() { + return Err(anyhow!( + "Failed to allocate host transfer for {}th iso urb of device {} ep {}", + i, + id, + ep_number + )); + } + + let cloned_iso_xfer = iso_xfer.clone(); + iso_xfer.lock().unwrap().realize( + handle, + packets, + pid, + ep_number, + max_packet_size, + (Arc::into_raw(cloned_iso_xfer) as *mut Mutex).cast::(), + ); + self.unused.push_back(iso_xfer); + } + Ok(()) + } + + pub fn clear(&mut self) { + for xfer in self.unused.iter_mut() { + xfer.lock().unwrap().clear(false); + } + + for xfer in self.inflight.iter_mut() { + xfer.lock().unwrap().clear(true); + } + + for xfer in self.copy.iter_mut() { + xfer.lock().unwrap().clear(false); + } + } +} + /// Abstract object of the host USB device. pub struct UsbHost { id: String, @@ -173,12 +356,18 @@ pub struct UsbHost { /// All pending asynchronous usb request. requests: Arc>>>>, completed: Arc>, + /// ISO queues corresponding to all endpoints. + iso_queues: Arc>>>>, + iso_urb_frames: u32, + iso_urb_count: u32, } impl UsbHost { pub fn new(config: UsbHostConfig) -> Result { let mut context = Context::new()?; context.set_log_level(rusb::LogLevel::Warning); + let iso_urb_frames = config.iso_urb_frames; + let iso_urb_count = config.iso_urb_count; Ok(Self { id: config.id.clone().unwrap(), config, @@ -193,6 +382,9 @@ impl UsbHost { exit: None, requests: Arc::new(Mutex::new(LinkedList::new())), completed: Arc::new(Mutex::new(0)), + iso_queues: Arc::new(Mutex::new(LinkedList::new())), + iso_urb_frames, + iso_urb_count, }) } @@ -373,6 +565,7 @@ impl UsbHost { error!("duplicate endpoint address") } let usb_ep = self.usb_device.get_mut_endpoint(in_direction, ep_num); + usb_ep.set_max_packet_size(ep.max_packet_size()); usb_ep.ep_type = ep_type; usb_ep.ifnum = i as u8; usb_ep.halted = false; @@ -482,6 +675,8 @@ impl UsbHost { } fn set_interface(&mut self, iface: u16, alt: u16, packet: &mut UsbPacket) { + self.clear_iso_queues(); + if iface > USB_MAX_INTERFACES as u16 { packet.status = UsbPacketStatus::Stall; return; @@ -568,6 +763,15 @@ impl UsbHost { *completed = 0; } + fn clear_iso_queues(&mut self) { + let mut locked_iso_queues = self.iso_queues.lock().unwrap(); + for queue in locked_iso_queues.iter() { + (*queue).lock().unwrap().clear(); + } + locked_iso_queues.clear(); + drop(locked_iso_queues); + } + pub fn abort_host_transfers(&mut self) -> Result<()> { // Max counts of uncompleted request to be handled. let mut limit = 100; @@ -589,6 +793,106 @@ impl UsbHost { } } + pub fn find_iso_queue(&self, ep_number: u8) -> Option>> { + for queue in self.iso_queues.lock().unwrap().iter() { + if (*queue).lock().unwrap().ep_number == ep_number { + return Some(queue.clone()); + } + } + None + } + + pub fn handle_iso_data_in(&mut self, packet: Arc>) { + let mut disconnect: bool = false; + let cloned_packet = packet.clone(); + let locked_packet = packet.lock().unwrap(); + let in_direction = locked_packet.pid == USB_TOKEN_IN as u32; + let iso_queue = if self.find_iso_queue(locked_packet.ep_number).is_some() { + self.find_iso_queue(locked_packet.ep_number).unwrap() + } else { + let iso_queue = Arc::new(Mutex::new(IsoQueue::new(locked_packet.ep_number))); + let cloned_iso_queue = iso_queue.clone(); + let ep = self + .usb_device + .get_endpoint(in_direction, locked_packet.ep_number); + match iso_queue.lock().unwrap().realize( + &self.id, + self.handle.as_mut().unwrap(), + self.iso_urb_count, + self.iso_urb_frames, + ep, + cloned_iso_queue, + ) { + Ok(()) => { + self.iso_queues.lock().unwrap().push_back(iso_queue.clone()); + } + Err(_e) => { + return; + } + }; + iso_queue + }; + + let mut locked_iso_queue = iso_queue.lock().unwrap(); + + let in_direction = locked_packet.pid == USB_TOKEN_IN as u32; + let ep = self + .usb_device + .get_endpoint(in_direction, locked_packet.ep_number); + drop(locked_packet); + + let iso_transfer = locked_iso_queue.copy.front_mut(); + if iso_transfer.is_some() + && iso_transfer + .unwrap() + .lock() + .unwrap() + .copy_data(cloned_packet, ep.max_packet_size) + { + let iso_transfer = locked_iso_queue.copy.pop_front().unwrap(); + locked_iso_queue.unused.push_back(iso_transfer); + } + drop(locked_iso_queue); + + loop { + let mut iso_transfer = iso_queue.lock().unwrap().unused.pop_front(); + if iso_transfer.is_none() { + break; + } + iso_transfer + .as_mut() + .unwrap() + .lock() + .unwrap() + .reset(ep.max_packet_size); + let host_transfer = iso_transfer.as_ref().unwrap().lock().unwrap().host_transfer; + let mut locked_iso_queue = iso_queue.lock().unwrap(); + match submit_host_transfer(host_transfer) { + Ok(()) => { + locked_iso_queue + .inflight + .push_back(iso_transfer.unwrap().clone()); + } + Err(e) => { + locked_iso_queue.unused.push_back(iso_transfer.unwrap()); + if e == Error::NoDevice { + disconnect = true; + }; + break; + } + }; + } + + if disconnect { + self.reset(); + } + } + + pub fn handle_iso_data_out(&mut self, _packet: Arc>) { + // TODO + error!("USBHost device Unsupport Isochronous Transfer from guest to device."); + } + fn submit_host_transfer( &mut self, host_transfer: *mut libusb_transfer, @@ -602,6 +906,7 @@ impl UsbHost { } _ => { packet.lock().unwrap().status = UsbPacketStatus::Stall; + self.reset(); return; } }; @@ -668,6 +973,8 @@ impl UsbDeviceOps for UsbHost { self.abort_host_transfers() .unwrap_or_else(|e| error!("Failed to abort all libusb transfers: {:?}", e)); + self.clear_iso_queues(); + self.handle .as_mut() .unwrap() @@ -808,8 +1115,15 @@ impl UsbDeviceOps for UsbHost { TransferType::Interrupt, ); } + USB_ENDPOINT_ATTR_ISOC => { + if packet.lock().unwrap().pid as u8 == USB_TOKEN_IN { + self.handle_iso_data_in(packet.clone()); + } else { + self.handle_iso_data_out(packet.clone()); + } + return; + } _ => { - error!("Isochronous transmission is not supported by host USB passthrough."); packet.lock().unwrap().status = UsbPacketStatus::Stall; return; } -- Gitee From bc36ae38ac2e2c77be6052631fdb1254c5f42977 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 13 Jul 2023 10:37:54 +0800 Subject: [PATCH 1210/2187] USB-MST: Modify some testcase to adapting the modification of xhci controller Modify the judgement of mfindex value because the function of getting mfindex is implemented. Delete the testcase of test_xhci_keyboard_over_event_ring as the incomming event will be droped when event ring is full currently. Signed-off-by: Jinhao Gao --- tests/mod_test/tests/usb_test.rs | 58 +------------------------------- 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index ea971839d..98742e184 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -865,62 +865,6 @@ fn test_xhci_keyboard_over_transfer_ring() { test_state.borrow_mut().stop(); } -#[test] -fn test_xhci_keyboard_over_event_ring() { - let (xhci, test_state, _) = TestUsbBuilder::new() - .with_xhci("xhci") - .with_usb_keyboard("kbd") - .with_config("auto_run", true) - .with_config("command_auto_doorbell", true) - .build(); - let mut xhci = xhci.borrow_mut(); - // reset event ring. - let evt_ring_sz = 16; - xhci.init_event_ring(0, 1, evt_ring_sz); - xhci.init_msix(); - let port_id = 1; - let slot_id = xhci.init_device(port_id); - - // only one trb left in event ring it will report ring full error. - for i in 0..evt_ring_sz - 1 { - qmp_send_key_event(test_state.borrow_mut(), 2 + i, true); - } - xhci.queue_multi_indirect_td( - slot_id, - HID_DEVICE_ENDPOINT_ID, - HID_KEYBOARD_LEN, - evt_ring_sz as usize - 1, - ); - xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); - // NOTE: the last event is lost for the current implementation. - for _ in 0..evt_ring_sz - 2 { - let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - } - let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::EventRingFullError as u32); - - for i in 0..evt_ring_sz { - qmp_send_key_event(test_state.borrow_mut(), 2 + i, false); - } - - xhci.queue_multi_indirect_td( - slot_id, - HID_DEVICE_ENDPOINT_ID, - HID_KEYBOARD_LEN, - evt_ring_sz as usize, - ); - - xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); - assert!(status & USB_STS_HCE == USB_STS_HCE); - - xhci.reset_controller(true); - let slot_id = xhci.init_device(port_id); - xhci.test_keyboard_event(slot_id, test_state.clone()); - test_state.borrow_mut().stop(); -} - #[test] fn test_xhci_keyboard_invalid_doorbell() { let (xhci, test_state, _) = TestUsbBuilder::new() @@ -1070,7 +1014,7 @@ fn test_xhci_keyboard_controller_init_invalid_register() { let mf_index = xhci .pci_dev .io_readl(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64); - assert_eq!(mf_index, 0); + assert!(mf_index <= 0x3fff); // invalid offset xhci.pci_dev .io_writel(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64 + 0x1008, 0xf); -- Gitee From 8efbb297de10181be0d063a43eca89dbe4d14676 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 05:59:26 +0800 Subject: [PATCH 1211/2187] virtio-serial: allow to use default nr for virtconsole/virtserialport Allow to use default nr for virtconsole/virtserialport. Note: all virtconsoles and virtserialports should set nr if one sets. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- machine/src/lib.rs | 7 +++++-- machine_manager/src/config/chardev.rs | 11 ++++++++--- virtio/src/device/serial.rs | 11 +++++++++++ virtio/src/lib.rs | 2 +- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 12057d411..57d905fcc 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -535,7 +535,7 @@ redirection will be required. See [section 2.12 Chardev](#212-chardev) for detai Three properties can be set for virtconsole(console port) and virtserialport(generic port). * id: unique device-id. * chardev: char device of this console/generic port. -* nr: unique port number for this port. +* nr: unique port number for this port. (optional) If set, all virtserialports and virtconsoles should set. nr = 0 is only allowed for virtconsole. For virtio-serial-pci, Four more properties are required. * bus: bus number of virtio console. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9d02fc052..495a85318 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -93,7 +93,8 @@ use vfio::{VfioDevice, VfioPciDevice}; #[cfg(not(target_env = "musl"))] use virtio::Gpu; use virtio::{ - balloon_allow_list, find_port_by_nr, vhost, Balloon, Block, BlockState, Rng, RngState, + balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, Block, BlockState, Rng, + RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, @@ -449,7 +450,6 @@ pub trait MachineOps { cfg_args: &str, is_console: bool, ) -> Result<()> { - let serialport_cfg = parse_virtserialport(vm_config, cfg_args, is_console)?; let serial_cfg = vm_config .virtio_serial .as_ref() @@ -493,6 +493,9 @@ pub trait MachineOps { let mut virtio_dev_h = virtio_dev.lock().unwrap(); let serial = virtio_dev_h.as_any_mut().downcast_mut::().unwrap(); + // Note: port 0 is reserved for a virtconsole. "nr=0" should be specified to configure. + let free_nr = get_max_nr(&serial.ports) + 1; + let serialport_cfg = parse_virtserialport(vm_config, cfg_args, is_console, free_nr)?; if serialport_cfg.nr >= serial.max_nr_ports { bail!( "virtio serial port nr {} should be less than virtio serial's max_nr_ports {}", diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 6312c82b8..5bdd70d2d 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -267,6 +267,7 @@ pub fn parse_virtserialport( vm_config: &mut VmConfig, config_args: &str, is_console: bool, + free_nr: u32, ) -> Result { let mut cmd_parser = CmdParser::new("virtserialport"); cmd_parser.push("").push("id").push("chardev").push("nr"); @@ -280,9 +281,10 @@ pub fn parse_virtserialport( let id = cmd_parser.get_value::("id")?.with_context(|| { ConfigError::FieldIsMissing("id".to_string(), "virtserialport".to_string()) })?; - let nr = cmd_parser.get_value::("nr")?.with_context(|| { - ConfigError::FieldIsMissing("nr".to_string(), "virtserialport".to_string()) - })?; + let nr = cmd_parser.get_value::("nr")?.unwrap_or(free_nr); + if nr == 0 && !is_console { + bail!("Port number 0 on virtio-serial devices reserved for virtconsole device."); + } if let Some(chardev) = vm_config.chardev.remove(&chardev_name) { let port_cfg = VirtioSerialPort { @@ -536,6 +538,7 @@ mod tests { &mut vm_config, "virtconsole,chardev=test_console,id=console1,nr=1", true, + 0, ); assert!(virt_console.is_ok()); let console_cfg = virt_console.unwrap(); @@ -567,6 +570,7 @@ mod tests { &mut vm_config, "virtconsole,chardev=test_console1,id=console1,nr=1", true, + 0, ); // test_console1 does not exist. assert!(virt_console.is_err()); @@ -586,6 +590,7 @@ mod tests { &mut vm_config, "virtconsole,chardev=test_console,id=console1,nr=1", true, + 0, ); assert!(virt_console.is_ok()); let console_cfg = virt_console.unwrap(); diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 123f1ce24..6b652e81d 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -193,6 +193,17 @@ impl Serial { } } +pub fn get_max_nr(ports: &Arc>>>>) -> u32 { + let mut max = 0; + for port in ports.lock().unwrap().iter() { + let nr = port.lock().unwrap().nr; + if nr > max { + max = nr; + } + } + max +} + pub fn find_port_by_nr( ports: &Arc>>>>, nr: u32, diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 96af6d175..c4ea76f2e 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -52,7 +52,7 @@ pub use device::gpu::*; pub use device::net::*; pub use device::rng::{Rng, RngState}; pub use device::scsi_cntlr as ScsiCntlr; -pub use device::serial::{find_port_by_nr, Serial, SerialPort, VirtioSerialState}; +pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; pub use error::VirtioError; pub use error::*; pub use queue::*; -- Gitee From c8138162dec871ec061c72335edeabe52f29ba17 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 10:51:33 +0800 Subject: [PATCH 1212/2187] serial_test: don't set serialport nr to 0 nr=0 is only allowed for virtconsole. Change serialport's nr from 0 to 1. Signed-off-by: liuxiangdong --- tests/mod_test/tests/serial_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index 096857a9d..beea92a67 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -627,7 +627,7 @@ fn virtserialport_socket_basic() { fn virtserialport_pty_basic() { let port = PortConfig { chardev_type: ChardevType::Pty, - nr: 0, + nr: 1, is_console: false, }; let pci_slot = 0x04; -- Gitee From c2804e585ac1f0e841cc056c78b19b4d67b59351 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 16:44:08 +0800 Subject: [PATCH 1213/2187] snapshot: modify snapshot host time format Change snapshot host time format from %H-%M-%S to %H:%M:%S Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 7b2defa07..c618a7c08 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1069,7 +1069,7 @@ impl Qcow2Driver { let date = get_format_time(snap.date_sec as i64); let date_str = format!( - "{:04}-{:02}-{:02} {:02}-{:02}-{:02}", + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}", date[0], date[1], date[2], date[3], date[4], date[5] ); -- Gitee From e1a9670dea9b04ec8e7717f9d363b2a9abb7fafe Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 17:53:28 +0800 Subject: [PATCH 1214/2187] snapshot: add vm clock in creating snapshot Add vm clock in creating snapshot. Signed-off-by: liuxiangdong --- machine/src/standard_vm/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 615b02ac5..490d66a47 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1889,8 +1889,10 @@ impl DeviceInterface for StdMachine { let mut locked_status = status.lock().unwrap(); *locked_status = BlockStatus::Snapshot; - // TODO: Add a method for getting guest clock. It's useless now so we can use a fake time(0). - let vm_clock_nsec = 0; + let vm_clock_nsec = EventLoop::get_ctx(None) + .unwrap() + .get_virtual_clock() + .as_nanos() as u64; if let Err(e) = qcow2driver .unwrap() .lock() -- Gitee From d87b8e6bc06175684f8019e376705e45ef1c5dd9 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 10 Jul 2023 13:10:46 +0800 Subject: [PATCH 1215/2187] Snapshot: add qcow2 snapshot tests. Add qcow2 snapshot tests. Signed-off-by: Yan Wang Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 212 ++++++++++++++++++++++++++ tests/mod_test/src/libdriver/qcow2.rs | 46 +++++- tests/mod_test/tests/block_test.rs | 162 ++++++++++++++++++++ 3 files changed, 416 insertions(+), 4 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index c618a7c08..d8c6069eb 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -2045,4 +2045,216 @@ mod test { assert!(qcow2_read(&mut qcow2_driver, &mut test_buf, offset_start).is_ok()); assert!(vec_is_zero(&test_buf)); } + + #[test] + fn test_snapshot_basic() { + // TODO: + // 1) add check step when stratovirt-img works. + // 2) add snapshot apply step to check function. + let path = "/tmp/snashot_test.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let (_, mut qcow2) = create_qcow2(path); + + let guest_offsets = [ + cluster_size * 0, + cluster_size * 10, + cluster_size * 100, + cluster_size * 1000, + cluster_size * 10000, + ]; + + let wbuf = vec![1_u8; CLUSTER_SIZE as usize]; + // Write data and create snapshot 'snap1'. + for offset in guest_offsets { + qcow2_write(&mut qcow2, &wbuf, offset).unwrap(); + } + qcow2.qcow2_create_snapshot("snap1".to_string(), 0).unwrap(); + + let wbuf = vec![2_u8; CLUSTER_SIZE as usize]; + // Write data and create snapshot 'snap2'. + for offset in guest_offsets { + qcow2_write(&mut qcow2, &wbuf, offset).unwrap(); + } + qcow2.qcow2_create_snapshot("snap2".to_string(), 0).unwrap(); + + // Read 1 byte for checking. Add more checks after implementing snapshot restore. + let mut rbuf = vec![0_u8; 1]; + for offset in guest_offsets { + qcow2_read(&mut qcow2, &mut rbuf, offset).unwrap(); + assert_eq!(rbuf, [2]); + } + + // Delete snapshot 'snap2'. + qcow2.qcow2_delete_snapshot("snap2".to_string()).unwrap(); + + // Delete snapshot 'snap1'. + qcow2.qcow2_delete_snapshot("snap1".to_string()).unwrap(); + } + + fn get_host_offset(qcow2_driver: &mut Qcow2Driver<()>, guest_offset: u64) -> u64 { + let l2_index = qcow2_driver.table.get_l2_table_index(guest_offset); + // All used l2 table will be cached for it's little data size in these tests. + let l2_table = qcow2_driver + .table + .get_l2_table_cache_entry(guest_offset) + .unwrap(); + let l2_entry = l2_table + .borrow_mut() + .get_entry_map(l2_index as usize) + .unwrap(); + let host_offset = l2_entry & L2_TABLE_OFFSET_MASK; + + host_offset + } + + // Change snapshot table offset to unaligned address which will lead to error in refcount update process. + #[test] + fn simulate_revert_snapshot_creation() { + let path = "/tmp/revert_create.qcow2"; + let (_image, mut qcow2_driver) = create_qcow2(path); + + // Write some random data. + let (case_list, _buf_list) = generate_rw_random_list(); + for case in &case_list { + qcow2_driver + .write_vectored(&case.wiovec, case.offset, ()) + .unwrap(); + } + + // Change snapshot table offset to a fake address which is not align to cluster size and + // it will fail in update_refcount. + qcow2_driver.header.snapshots_offset = 0x1111; + let result = qcow2_driver.create_snapshot("snapshot1".to_string(), 0); + assert!(result.is_err()); + + // Check + // 1) No snapshot. + assert_eq!(qcow2_driver.header.nb_snapshots, 0); + // 2) Refcount is right. + for case in &case_list { + let host_offset = get_host_offset(&mut qcow2_driver, case.offset as u64); + assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 1); + } + // 3) L1 table refcount is right. + assert_eq!( + qcow2_driver + .refcount + .get_refcount(qcow2_driver.header.l1_table_offset) + .unwrap(), + 1 + ); + // 4) L2 table refcount is right. + let mut l1_table = qcow2_driver.table.l1_table.clone(); + for l1_entry in l1_table.iter_mut() { + if *l1_entry == 0 { + // No l2 table. + continue; + } + assert_eq!( + qcow2_driver + .refcount + .get_refcount(*l1_entry & L1_TABLE_OFFSET_MASK) + .unwrap(), + 1 + ); + } + } + + // Change snapshot table offset to unaligned address which will lead to error in refcount update process. + #[test] + fn simulate_revert_snapshot_deletion() { + let path = "/tmp/revert_delete.qcow2"; + let (_image, mut qcow2_driver) = create_qcow2(path); + + // Write some random data. + let (case_list, _buf_list) = generate_rw_random_list(); + for case in &case_list { + qcow2_driver + .write_vectored(&case.wiovec, case.offset, ()) + .unwrap(); + } + + // Create two new snapshots. + qcow2_driver + .qcow2_create_snapshot("snaptest1".to_string(), 0) + .unwrap(); + qcow2_driver + .qcow2_create_snapshot("snaptest2".to_string(), 0) + .unwrap(); + + // Check. + // 1) 2 snapshots: snaptest1, snaptest2. + assert_eq!(qcow2_driver.header.nb_snapshots, 2); + assert_eq!(qcow2_driver.snapshot.snapshots[0].name, "snaptest1"); + assert_eq!(qcow2_driver.snapshot.snapshots[1].name, "snaptest2"); + // 2) Data cluster refcount is right. + for case in &case_list { + let host_offset = get_host_offset(&mut qcow2_driver, case.offset as u64); + assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 3); + } + // 3) L1 table refcount is right. + assert_eq!( + qcow2_driver + .refcount + .get_refcount(qcow2_driver.header.l1_table_offset) + .unwrap(), + 1 + ); + // 4) L2 table refcount is right. + let mut l1_table = qcow2_driver.table.l1_table.clone(); + for l1_entry in l1_table.iter_mut() { + if *l1_entry == 0 { + // No l2 table. + continue; + } + assert_eq!( + qcow2_driver + .refcount + .get_refcount(*l1_entry & L1_TABLE_OFFSET_MASK) + .unwrap(), + 3 + ); + } + + // Change snapshot table offset to a fake address which is not align to cluster size and + // it will fail in update_refcount. + qcow2_driver.header.snapshots_offset = 0x1111; + let result = qcow2_driver.delete_snapshot("snapshot1".to_string()); + assert!(result.is_err()); + + // Check again. + // 1) 2 snapshots: snaptest1, snaptest2. + assert_eq!(qcow2_driver.header.nb_snapshots, 2); + assert_eq!(qcow2_driver.snapshot.snapshots[0].name, "snaptest1"); + assert_eq!(qcow2_driver.snapshot.snapshots[1].name, "snaptest2"); + // 2) Data cluster refcount is right. + for case in &case_list { + let host_offset = get_host_offset(&mut qcow2_driver, case.offset as u64); + assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 3); + } + // 3) L1 table refcount is right. + assert_eq!( + qcow2_driver + .refcount + .get_refcount(qcow2_driver.header.l1_table_offset) + .unwrap(), + 1 + ); + // 4) L2 table refcount is right. + let mut l1_table = qcow2_driver.table.l1_table.clone(); + for l1_entry in l1_table.iter_mut() { + if *l1_entry == 0 { + // No l2 table. + continue; + } + assert_eq!( + qcow2_driver + .refcount + .get_refcount(*l1_entry & L1_TABLE_OFFSET_MASK) + .unwrap(), + 3 + ); + } + } } diff --git a/tests/mod_test/src/libdriver/qcow2.rs b/tests/mod_test/src/libdriver/qcow2.rs index 8b3fa81d5..6511a381d 100644 --- a/tests/mod_test/src/libdriver/qcow2.rs +++ b/tests/mod_test/src/libdriver/qcow2.rs @@ -10,15 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{bail, Result}; -use byteorder::{BigEndian, ByteOrder}; -use libc::iovec; -use libc::{c_int, off_t, preadv}; +use std::cell::RefCell; +use std::rc::Rc; use std::{ fs::File, io::{Seek, SeekFrom, Write}, os::unix::prelude::{AsRawFd, OpenOptionsExt}, }; + +use anyhow::{bail, Result}; +use byteorder::{BigEndian, ByteOrder}; +use libc::{c_int, iovec, off_t, preadv}; +use serde_json::Value; + +use crate::libtest::TestState; use util::aio::Iovec; const QCOW_MAGIC: u32 = 0x514649fb; @@ -299,3 +304,36 @@ fn write_full_disk(image_path: String) { qcow2.raw_write(cluster_size * 3, &mut l1_table); qcow2.raw_write(cluster_size * 4, &mut l2_table); } + +pub fn create_snapshot(state: Rc>, device: &str, snap: &str) { + let qmp_str = format!("{{\"execute\":\"blockdev-snapshot-internal-sync\",\"arguments\":{{\"device\":\"{}\",\"name\":\"{}\"}}}}", device, snap); + state.borrow_mut().qmp(&qmp_str); +} + +pub fn delete_snapshot(state: Rc>, device: &str, snap: &str) { + let qmp_str = format!("{{\"execute\":\"blockdev-snapshot-delete-internal-sync\",\"arguments\":{{\"device\":\"{}\",\"name\":\"{}\"}}}}", device, snap); + state.borrow_mut().qmp(&qmp_str); +} + +pub fn query_snapshot(state: Rc>) -> Value { + let qmp_str = + format!("{{\"execute\":\"human-monitor-command\",\"arguments\":{{\"command-line\":\"info snapshots\"}}}}"); + let value = state.borrow_mut().qmp(&qmp_str); + + value +} + +// Check if there exists snapshot with the specified name. +pub fn check_snapshot(state: Rc>, snap: &str) -> bool { + let value = query_snapshot(state.clone()); + let str = (*value.get("return").unwrap()).as_str().unwrap(); + let lines: Vec<&str> = str.split("\r\n").collect(); + for line in lines { + let buf: Vec<&str> = line.split_whitespace().collect(); + if buf.len() > 2 && buf[1] == snap { + return true; + } + } + + false +} diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 145b6b833..333aff9d8 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -14,6 +14,7 @@ use mod_test::libdriver::qcow2::CLUSTER_SIZE; use virtio::device::block::VirtioBlkConfig; use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::qcow2::{check_snapshot, create_snapshot, delete_snapshot}; use mod_test::libdriver::virtio::TestVringDescEntry; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; use mod_test::libdriver::virtio_block::{ @@ -1959,3 +1960,164 @@ fn blk_feature_write_zeroes() { } } } + +/// Block device using snapshot sends I/O request. +/// TestStep: +/// 1. Init block device. Create internal snapshot. +/// 2. Do the I/O request. +/// 3. Delete internal snapshot. +/// 4. Do the I/O request. +/// 5. Destroy device. +/// Expect: +/// 1/2/3/4/5: success. +#[test] +fn blk_snapshot_basic() { + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Qcow2); + let features = virtio_blk_default_feature(blk.clone()); + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + create_snapshot(test_state.clone(), "drive0", "snap0"); + assert_eq!(check_snapshot(test_state.clone(), "snap0"), true); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + delete_snapshot(test_state.clone(), "drive0", "snap0"); + assert_eq!(check_snapshot(test_state.clone(), "snap0"), false); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} + +/// Block device whose backend file has snapshot sends I/O request. +/// TestStep: +/// 1. Create snapshot snap0 in qcow2 backend file. +/// 2. Init device. +/// 3. Do the I/O request. +/// 4. Create internal snapshot snap1. Delete internal snapshot snap0. +/// 5. Do the I/O request. +/// 6. Destroy device. +/// Expect: +/// 1/2/3/4/5/6: success. +#[test] +fn blk_snapshot_basic2() { + // Note: We can not use stratovirt-img to create snapshot now. + // So, we use qmp to create snapshot in existed qcow2 file. + // TODO: use stratovirt-img instead of qmp in the future. + let (blk, test_state, alloc, image_path) = set_up(&ImageType::Qcow2); + let features = virtio_blk_default_feature(blk.clone()); + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + create_snapshot(test_state.clone(), "drive0", "snap0"); + assert_eq!(check_snapshot(test_state.clone(), "snap0"), true); + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + Rc::new("".to_string()), + ); + + let device_args = Rc::new(String::from("")); + let drive_args = Rc::new(String::from(",direct=false")); + let other_args = Rc::new(String::from("")); + let (blk, test_state, alloc) = create_blk( + &ImageType::Qcow2, + image_path.clone(), + device_args, + drive_args, + other_args, + ); + + let features = virtio_blk_default_feature(blk.clone()); + let virtqueues = blk + .borrow_mut() + .init_device(test_state.clone(), alloc.clone(), features, 1); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + create_snapshot(test_state.clone(), "drive0", "snap1"); + assert_eq!(check_snapshot(test_state.clone(), "snap1"), true); + + delete_snapshot(test_state.clone(), "drive0", "snap0"); + assert_eq!(check_snapshot(test_state.clone(), "snap0"), false); + + virtio_blk_write( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + virtio_blk_read( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues[0].clone(), + 0, + true, + ); + + tear_down( + blk.clone(), + test_state.clone(), + alloc.clone(), + virtqueues, + image_path.clone(), + ); +} -- Gitee From 3497ac4531a8583b0b9319c15e4508584ddadd8b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jul 2023 13:40:50 +0800 Subject: [PATCH 1216/2187] snapshot: don't need to flush refcount cache again We have flushed refcount cache after updating refount in update_refcount. Don't need to flush refcount cache again. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index d8c6069eb..30b92ea15 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -677,12 +677,6 @@ impl Qcow2Driver { break; } - if let Err(e) = self.refcount.flush_refcount_block_cache() { - err_msg = format!("{:?}", e); - error_stage = 5; - break; - } - let mut new_header = self.header.clone(); new_header.snapshots_offset = new_snapshot_table_offset; new_header.nb_snapshots -= 1; @@ -711,6 +705,7 @@ impl Qcow2Driver { } // Error handling, to revert some operation. + self.refcount.discard_list.clear(); if error_stage >= 6 { self.refcount.update_refcount( self.header.snapshots_offset, @@ -882,6 +877,7 @@ impl Qcow2Driver { } // Error handling, to revert some operation. + self.refcount.discard_list.clear(); if error_stage >= 5 && self.header.snapshots_offset != 0 { self.refcount.update_refcount( self.header.snapshots_offset, -- Gitee From 5aee3bd4d145e6aa6957098a777211c6d075c98d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 01:55:59 +0800 Subject: [PATCH 1217/2187] vhost-vsock: activate vsock in micro-vm Activate vsock in micro vm. Fix: 7e97dec(vsock: fix mircovm vhost_vsock) Signed-off-by: liuxiangdong --- virtio/src/vhost/kernel/vsock.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 7b486defa..1233d7075 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -250,12 +250,10 @@ impl VirtioDevice for Vsock { } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { - if self.disable_irqfd { - return Err(anyhow!("The irqfd cannot be used on the current machine.")); - } - - for fd in queue_evts.iter() { - self.call_events.push(fd.clone()); + if !self.disable_irqfd { + for fd in queue_evts.iter() { + self.call_events.push(fd.clone()); + } } Ok(()) -- Gitee From ac4bdd65509a24e324e130bf0b90aa8f43d31e46 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 16 Jul 2023 16:36:12 +0800 Subject: [PATCH 1218/2187] Ui: change the strategy of free scale Dont fix the length and width ratio of the image in scale free mode. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index a9422654a..90ba14387 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -322,16 +322,10 @@ fn da_draw_callback(gs: &Rc>, cr: &cairo::Context) -> } let (window_width, window_height) = borrowed_gs.get_window_size()?; - if scale_mode.borrow().is_full_screen() { + if scale_mode.borrow().is_full_screen() || scale_mode.borrow().is_free_scale() { borrowed_gs.scale_x = window_width / surface_width; borrowed_gs.scale_y = window_height / surface_height; - } else if scale_mode.borrow().is_free_scale() { - let scale_x = window_width / surface_width; - let scale_y = window_height / surface_height; - borrowed_gs.scale_x = scale_x.min(scale_y); - borrowed_gs.scale_y = scale_x.min(scale_y); } - surface_width *= borrowed_gs.scale_x; surface_height *= borrowed_gs.scale_y; -- Gitee From 1653a23fee975cf3fce484a551fa4a7d62a1d837 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 13 Jul 2023 16:48:28 +0800 Subject: [PATCH 1219/2187] Clock: add get_virtual_clock interface In scenarious such as USB passthrough, when the VM is paused, the vCPU clock must stops ticking. This prevents inaccurate timers from being trigered. In addtion, the EventLopp::get_ctx call is optimized. Signed-off-by: Mingwang Li --- devices/src/acpi/power.rs | 11 +-- devices/src/legacy/ramfb.rs | 21 +++-- devices/src/usb/xhci/xhci_controller.rs | 49 +++++----- machine/src/lib.rs | 12 ++- machine/src/micro_vm/mod.rs | 7 +- machine/src/standard_vm/aarch64/mod.rs | 7 +- machine/src/standard_vm/x86_64/mod.rs | 7 +- ui/src/console.rs | 16 ++-- util/src/clock.rs | 115 ++++++++++++++++++++++++ util/src/leak_bucket.rs | 3 +- util/src/lib.rs | 1 + util/src/loop_context.rs | 13 +-- virtio/src/device/rng.rs | 10 +-- virtio/src/vhost/user/client.rs | 10 +-- 14 files changed, 191 insertions(+), 91 deletions(-) create mode 100644 util/src/clock.rs diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index ce469f8eb..a50ec7e20 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use anyhow::{Context, Result}; -use log::{error, info}; +use log::info; use util::byte_code::ByteCode; use util::num_ops::write_data_u32; @@ -406,12 +406,9 @@ pub fn power_status_update(dev: &Arc>) { pdev.state.last_bat_lvl = pdev.regs[REG_IDX_BAT_RCAP]; } - match EventLoop::get_ctx(None) { - Some(ctx) => { - ctx.timer_add(update_func, Duration::from_secs(5)); - } - None => error!("Failed to get ctx to update power devices status"), - } + EventLoop::get_ctx(None) + .unwrap() + .timer_add(update_func, Duration::from_secs(5)); } else { pdev.power_load_static_status(); } diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index b4d310e84..3181aaa74 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -287,17 +287,16 @@ fn set_press_event(install: Arc, data: *const u8) { .unwrap_or_else(|e| error!("Ramfb couldn't release return key: {:?}.", e)); }); - if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.timer_add( - set_press_func, - Duration::from_millis(INSTALL_CHECK_INTERVEL_MS), - ); - ctx.timer_add(press_func, Duration::from_millis(INSTALL_PRESS_INTERVEL_MS)); - ctx.timer_add( - release_func, - Duration::from_millis(INSTALL_RELEASE_INTERVEL_MS), - ); - } + let ctx = EventLoop::get_ctx(None).unwrap(); + ctx.timer_add( + set_press_func, + Duration::from_millis(INSTALL_CHECK_INTERVEL_MS), + ); + ctx.timer_add(press_func, Duration::from_millis(INSTALL_PRESS_INTERVEL_MS)); + ctx.timer_add( + release_func, + Duration::from_millis(INSTALL_RELEASE_INTERVEL_MS), + ); } else { install.store(false, Ordering::Release); } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 0929f899a..b7c82a2dc 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -15,7 +15,7 @@ use std::mem::size_of; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use std::time::{Duration, Instant}; +use std::time::Duration; use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; @@ -683,7 +683,7 @@ pub struct XhciDevice { pub cmd_ring: XhciCommandRing, mem_space: Arc, /// Runtime Register. - mfindex_start: Instant, + mfindex_start: Duration, timer_id: Option, } @@ -729,7 +729,7 @@ impl XhciDevice { intrs, cmd_ring: XhciCommandRing::new(mem_space), mem_space: mem_space.clone(), - mfindex_start: Instant::now(), + mfindex_start: EventLoop::get_ctx(None).unwrap().get_virtual_clock(), timer_id: None, }; let xhci = Arc::new(Mutex::new(xhci)); @@ -763,12 +763,12 @@ impl XhciDevice { pub fn run(&mut self) { self.oper.unset_usb_status_flag(USB_STS_HCH); - self.mfindex_start = Instant::now(); + self.mfindex_start = EventLoop::get_ctx(None).unwrap().get_virtual_clock(); } pub fn mfindex(&mut self) -> u64 { - let now = Instant::now(); - now.duration_since(self.mfindex_start).as_nanos() as u64 / ISO_BASE_TIME_INTERVAL + let now = EventLoop::get_ctx(None).unwrap().get_virtual_clock(); + (now - self.mfindex_start).as_nanos() as u64 / ISO_BASE_TIME_INTERVAL } pub fn mfwrap_update(&mut self) { @@ -789,12 +789,10 @@ impl XhciDevice { locked_xhci.mfwrap_update(); }); - if let Some(ctx) = EventLoop::get_ctx(None) { - self.timer_id = Some(ctx.timer_add( - xhci_mfwrap_timer, - Duration::from_nanos(left * ISO_BASE_TIME_INTERVAL), - )); - } + self.timer_id = Some(EventLoop::get_ctx(None).unwrap().timer_add( + xhci_mfwrap_timer, + Duration::from_nanos(left * ISO_BASE_TIME_INTERVAL), + )); } } @@ -831,7 +829,7 @@ impl XhciDevice { } self.cmd_ring.init(0); - self.mfindex_start = Instant::now(); + self.mfindex_start = EventLoop::get_ctx(None).unwrap().get_virtual_clock(); self.mfwrap_update(); } @@ -1814,23 +1812,22 @@ impl XhciDevice { error!("Failed to kick endpoint: {:?}", e); } }); - if let Some(ctx) = EventLoop::get_ctx(None) { - if self.timer_id.is_some() { - ctx.timer_del(self.timer_id.unwrap()); - } - self.timer_id = Some(ctx.timer_add( - xhci_ep_kick_timer, - Duration::from_nanos((xfer.mfindex_kick - mfindex) * ISO_BASE_TIME_INTERVAL), - )); + let ctx = EventLoop::get_ctx(None).unwrap(); + if self.timer_id.is_some() { + ctx.timer_del(self.timer_id.unwrap()); } + self.timer_id = Some(ctx.timer_add( + xhci_ep_kick_timer, + Duration::from_nanos((xfer.mfindex_kick - mfindex) * ISO_BASE_TIME_INTERVAL), + )); xfer.running_retry = true; } else { epctx.mfindex_last = xfer.mfindex_kick; - if let Some(ctx) = EventLoop::get_ctx(None) { - if self.timer_id.is_some() { - ctx.timer_del(self.timer_id.unwrap()); - self.timer_id = None; - } + if self.timer_id.is_some() { + EventLoop::get_ctx(None) + .unwrap() + .timer_del(self.timer_id.unwrap()); + self.timer_id = None; } xfer.running_retry = false; } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9d02fc052..1f6c7db71 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -32,7 +32,6 @@ use devices::misc::scream::Scream; use log::warn; #[cfg(not(target_env = "musl"))] use machine_manager::config::scream::parse_scream; -#[cfg(not(target_env = "musl"))] use machine_manager::event_loop::EventLoop; #[cfg(not(target_env = "musl"))] use ui::console::{get_run_stage, VmRunningStage}; @@ -1687,6 +1686,7 @@ pub trait MachineOps { /// * `vm_state` - Vm kvm vm state. fn vm_start(&self, paused: bool, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { if !paused { + EventLoop::get_ctx(None).unwrap().enable_clock(); self.active_drive_files()?; } @@ -1722,6 +1722,8 @@ pub trait MachineOps { #[cfg(target_arch = "aarch64")] irq_chip: &Option>, vm_state: &mut KvmVmState, ) -> Result<()> { + EventLoop::get_ctx(None).unwrap().disable_clock(); + self.deactive_drive_files()?; for (cpu_index, cpu) in cpus.iter().enumerate() { @@ -1747,6 +1749,8 @@ pub trait MachineOps { /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_resume(&self, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { + EventLoop::get_ctx(None).unwrap().enable_clock(); + self.active_drive_files()?; for (cpu_index, cpu) in cpus.iter().enumerate() { @@ -1947,7 +1951,7 @@ fn check_windows_emu_pid( shutdown_req.clone(), ); }); - if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.timer_add(check_emu_alive, check_delay); - } + EventLoop::get_ctx(None) + .unwrap() + .timer_add(check_emu_alive, check_delay); } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index afc75d5e9..4d644112c 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -949,10 +949,9 @@ impl MachineLifecycle for LightMachine { return false; } - if let Some(ctx) = EventLoop::get_ctx(None) { - info!("vm destroy"); - ctx.kick(); - } + info!("vm destroy"); + EventLoop::get_ctx(None).unwrap().kick(); + true } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index ee739cd7f..38bfb2327 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -1162,10 +1162,9 @@ impl MachineLifecycle for StdMachine { return false; } - if let Some(ctx) = EventLoop::get_ctx(None) { - info!("vm destroy"); - ctx.kick(); - } + info!("vm destroy"); + EventLoop::get_ctx(None).unwrap().kick(); + true } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index e911a65e5..d9b107e1f 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -911,10 +911,9 @@ impl MachineLifecycle for StdMachine { return false; } - if let Some(ctx) = EventLoop::get_ctx(None) { - info!("vm destroy"); - ctx.kick(); - } + info!("vm destroy"); + EventLoop::get_ctx(None).unwrap().kick(); + true } diff --git a/ui/src/console.rs b/ui/src/console.rs index 7632c072d..c91810664 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -384,9 +384,9 @@ pub fn setup_refresh(update_interval: u64) { }); if update_interval != 0 { - if let Some(ctx) = EventLoop::get_ctx(None) { - ctx.timer_add(func, Duration::from_millis(update_interval)); - } + EventLoop::get_ctx(None) + .unwrap() + .timer_add(func, Duration::from_millis(update_interval)); } } @@ -541,12 +541,12 @@ pub fn graphic_hardware_ui_info( (*con_opts).hw_ui_info(clone_con.clone(), width, height); }); - if let Some(ctx) = EventLoop::get_ctx(None) { - if let Some(timer_id) = locked_con.timer_id { - ctx.timer_del(timer_id); - } - locked_con.timer_id = Some(ctx.timer_add(func, Duration::from_millis(500))); + let ctx = EventLoop::get_ctx(None).unwrap(); + if let Some(timer_id) = locked_con.timer_id { + ctx.timer_del(timer_id); } + locked_con.timer_id = Some(ctx.timer_add(func, Duration::from_millis(500))); + Ok(()) } diff --git a/util/src/clock.rs b/util/src/clock.rs new file mode 100644 index 000000000..68a043e1c --- /dev/null +++ b/util/src/clock.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::time::{Duration, Instant}; + +use crate::{ + loop_context::EventLoopContext, + test_helper::{get_test_time, is_test_enabled}, +}; + +pub fn get_current_time() -> Instant { + if is_test_enabled() { + get_test_time() + } else { + Instant::now() + } +} + +/// Recording VM timer state. +pub struct ClockState { + enable: bool, + offset: Instant, + paused: Duration, + elapsed: Duration, +} + +impl Default for ClockState { + fn default() -> Self { + Self { + enable: false, + offset: Instant::now(), + paused: Duration::default(), + elapsed: Duration::default(), + } + } +} + +impl ClockState { + pub fn get_virtual_clock(&mut self) -> Duration { + let mut time = self.paused; + if self.enable { + time = self.offset.elapsed() - self.elapsed; + } + time + } + + pub fn enable(&mut self) { + self.elapsed = self.offset.elapsed() - self.paused; + self.enable = true; + } + + pub fn disable(&mut self) { + self.paused = self.offset.elapsed() - self.elapsed; + self.enable = false; + } +} + +impl EventLoopContext { + /// Returns the clock based on the type. + pub fn get_virtual_clock(&self) -> Duration { + self.clock_state.lock().unwrap().get_virtual_clock() + } + + /// The clock running when VCPU in running. + pub fn enable_clock(&self) { + self.clock_state.lock().unwrap().enable(); + } + + /// The clock is stopped when VCPU in paused. + pub fn disable_clock(&self) { + self.clock_state.lock().unwrap().disable(); + } +} + +#[cfg(test)] +mod test { + use std::{thread, time::Duration}; + + use super::ClockState; + + #[test] + fn test_virtual_clock() { + let mut clock = ClockState::default(); + clock.enable(); + thread::sleep(Duration::from_secs(5)); + let virtual_clock = clock.get_virtual_clock(); + assert_eq!(virtual_clock.as_secs(), 5); + clock.disable(); + thread::sleep(Duration::from_secs(10)); + let virtual_clock = clock.get_virtual_clock(); + assert_eq!(virtual_clock.as_secs(), 5); + clock.enable(); + thread::sleep(Duration::from_secs(5)); + let virtual_clock = clock.get_virtual_clock(); + assert_eq!(virtual_clock.as_secs(), 10); + + clock.disable(); + thread::sleep(Duration::from_secs(10)); + let virtual_clock = clock.get_virtual_clock(); + assert_eq!(virtual_clock.as_secs(), 10); + clock.enable(); + thread::sleep(Duration::from_secs(5)); + let virtual_clock = clock.get_virtual_clock(); + assert_eq!(virtual_clock.as_secs(), 15); + } +} diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index cf1d2490c..c2196c357 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -18,7 +18,8 @@ use std::time::{Duration, Instant}; use log::error; use vmm_sys_util::eventfd::EventFd; -use crate::loop_context::{get_current_time, EventLoopContext}; +use crate::clock::get_current_time; +use crate::loop_context::EventLoopContext; use crate::time::NANOSECONDS_PER_SECOND; use anyhow::Result; diff --git a/util/src/lib.rs b/util/src/lib.rs index 0c7027875..8e34aa4d6 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -15,6 +15,7 @@ pub mod arg_parser; pub mod bitmap; pub mod byte_code; pub mod checksum; +pub mod clock; pub mod daemonize; #[cfg(target_arch = "aarch64")] pub mod device_tree; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 9a6a776bb..5c478d730 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -27,7 +27,7 @@ use nix::{ use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; -use crate::test_helper::{get_test_time, is_test_enabled}; +use crate::clock::{get_current_time, ClockState}; use crate::UtilError; use anyhow::{anyhow, Context, Result}; use std::fmt; @@ -153,14 +153,6 @@ pub fn gen_delete_notifiers(fds: &[RawFd]) -> Vec { notifiers } -pub fn get_current_time() -> Instant { - if is_test_enabled() { - get_test_time() - } else { - Instant::now() - } -} - /// EventLoop manager, advise continue running or stop running pub trait EventLoopManager: Send + Sync { fn loop_should_exit(&self) -> bool; @@ -210,6 +202,8 @@ pub struct EventLoopContext { ready_events: Vec, /// Timer list timers: Arc>>>, + /// Record VM clock state. + pub clock_state: Arc>, } // SAFETY: The closure in EventNotifier and Timer doesn't impl Send, they're @@ -229,6 +223,7 @@ impl EventLoopContext { gc: Arc::new(RwLock::new(Vec::new())), ready_events: vec![EpollEvent::default(); READY_EVENT_MAX], timers: Arc::new(Mutex::new(Vec::new())), + clock_state: Arc::new(Mutex::new(ClockState::default())), }; ctx.init_kick(); ctx diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 145ecd3b8..70ee34aa9 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -110,13 +110,9 @@ impl RngHandler { get_req_data_size(&elem.in_iovec).with_context(|| "Failed to get request size")?; if let Some(leak_bucket) = self.leak_bucket.as_mut() { - if let Some(ctx) = EventLoop::get_ctx(None) { - if leak_bucket.throttled(ctx, size as u64) { - queue_lock.vring.push_back(); - break; - } - } else { - bail!("Failed to get ctx in event loop context for virtio rng"); + if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), size as u64) { + queue_lock.vring.push_back(); + break; } } diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 050f1cb0d..3a68bf4f9 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -112,12 +112,10 @@ fn vhost_user_reconnect(client: &Arc>) { .domain .connect() { - if let Some(ctx) = EventLoop::get_ctx(None) { - // Default reconnecting time: 3s. - ctx.timer_add(func, Duration::from_secs(3)); - } else { - error!("Failed to get ctx to delay vhost-user reconnecting"); - } + // Default reconnecting time: 3s. + EventLoop::get_ctx(None) + .unwrap() + .timer_add(func, Duration::from_secs(3)); return; } -- Gitee From fea445482283fc4d895609684e0da031dc94a6ce Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 15 Jul 2023 16:00:14 +0800 Subject: [PATCH 1220/2187] USB: report USB stall when submit transfer error After the host wakes up from hibernation, the transfer fails to be submitted. The guest needs to re-initialize the device. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 24a44ba2b..752d89c0b 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -803,7 +803,6 @@ impl UsbHost { } pub fn handle_iso_data_in(&mut self, packet: Arc>) { - let mut disconnect: bool = false; let cloned_packet = packet.clone(); let locked_packet = packet.lock().unwrap(); let in_direction = locked_packet.pid == USB_TOKEN_IN as u32; @@ -875,17 +874,15 @@ impl UsbHost { } Err(e) => { locked_iso_queue.unused.push_back(iso_transfer.unwrap()); - if e == Error::NoDevice { - disconnect = true; + if e == Error::NoDevice || e == Error::Io { + // When the USB device reports the preceding error, XHCI notifies the guest of the + // error through packet status. The guest initiallizes the device again. + packet.lock().unwrap().status = UsbPacketStatus::Stall; }; break; } }; } - - if disconnect { - self.reset(); - } } pub fn handle_iso_data_out(&mut self, _packet: Arc>) { -- Gitee From e091a86c96a64924e624b117abeee9905187fd62 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 18 Jul 2023 16:05:25 +0800 Subject: [PATCH 1221/2187] mst: fix usb unplug mst Two results are returned when qmp hot unplug command is executed. When the qmp command is executed, two results may be read at a time. As a result, subsequent read commands cannot obtain data. Therefore, the remaining characters are stored in the buffer for the next read. Signed-off-by: zhouli57 --- tests/mod_test/src/libtest.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 22d099b50..151b2aa9d 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use serde_json::Value; +use std::cell::RefCell; use std::io::Read; use std::io::Write; use std::os::unix::net::{UnixListener, UnixStream}; @@ -22,6 +22,7 @@ use std::time::Instant; use std::{env, fs}; use hex; +use serde_json::Value; use crate::utils::get_tmp_dir; @@ -29,11 +30,15 @@ const MAX_SOCKET_MSG_LENGTH: usize = 8192; pub struct StreamHandler { stream: UnixStream, + read_buffer: RefCell, } impl StreamHandler { fn new(stream: UnixStream) -> Self { - StreamHandler { stream } + StreamHandler { + stream, + read_buffer: RefCell::new(String::new()), + } } fn write_line(&self, cmd: &str) { @@ -47,7 +52,7 @@ impl StreamHandler { fn read_line(&self, timeout: Duration) -> String { let start = Instant::now(); - let mut resp = String::new(); + let mut resp = self.read_buffer.borrow_mut(); let mut stream = self.stream.try_clone().unwrap(); stream.set_nonblocking(true).unwrap(); @@ -62,8 +67,11 @@ impl StreamHandler { } }; - let (line, _) = resp.split_at(pos.unwrap()); - line.trim().to_string() + let (line, left) = resp.split_at(pos.unwrap()); + let line = line.trim().to_string(); + // Save the remaining strings to the buffer, except the prefix '\n'. + *resp = left[1..].to_string(); + line } } -- Gitee From 34cda22487b80e3ce018f485f4e1804955588a18 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 17 Jul 2023 10:23:30 +0800 Subject: [PATCH 1222/2187] gtk: fix tab not totally work in wayland Inhibits the propagation of key event to avoid tab not totally work in wayland. --- ui/src/gtk/draw.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index a9422654a..74e985aae 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -68,15 +68,15 @@ pub(crate) fn set_callback_for_draw_area( }), ); draw_area.connect_key_press_event( - glib::clone!(@weak gs => @default-return Inhibit(false), move |_, key_event| { + glib::clone!(@weak gs => @default-return Inhibit(true), move |_, key_event| { da_key_callback(&gs,key_event, true).unwrap_or_else(|e|error!("Press event: {}", e)); - Inhibit(false)} + Inhibit(true)} ), ); draw_area.connect_key_release_event( - glib::clone!(@weak gs => @default-return Inhibit(false), move |_, key_event| { + glib::clone!(@weak gs => @default-return Inhibit(true), move |_, key_event| { da_key_callback(&gs,key_event, false).unwrap_or_else(|e|error!("Key event: {}", e)); - Inhibit(false)} + Inhibit(true)} ), ); draw_area.connect_configure_event( -- Gitee From 60f104f45660d97fb58a44f1d8b51a6ff38ae1fa Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 19 Jul 2023 19:56:34 +0800 Subject: [PATCH 1223/2187] usb-host: bugfix cann't abort request The request is deleted incorrectly. As a result, the request cancellation operation is not performed. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 752d89c0b..c1b0f0096 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -728,7 +728,6 @@ impl UsbHost { } pub fn clear_succ_requests(&mut self) { - let mut updated_requests: LinkedList>> = LinkedList::new(); let mut locked_request = self.requests.lock().unwrap(); let mut completed = self.completed.lock().unwrap(); @@ -747,6 +746,7 @@ impl UsbHost { } if *completed > COMPLETE_LIMIT { + let mut updated_requests: LinkedList>> = LinkedList::new(); loop { let request = locked_request.pop_front(); if let Some(request) = request { @@ -758,8 +758,8 @@ impl UsbHost { break; } } + *locked_request = updated_requests; } - *locked_request = updated_requests; *completed = 0; } @@ -1032,6 +1032,7 @@ impl UsbDeviceOps for UsbHost { &self.usb_device.data_buf[..device_req.length as usize], device_req, ); + self.requests.lock().unwrap().push_back(request.clone()); fill_transfer_by_type( host_transfer, self.handle.as_mut(), -- Gitee From a2603eb690a01e65a6ee57b209a882318a69da80 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 17:39:52 +0800 Subject: [PATCH 1224/2187] seccomp: Optimize epbf filter length Only need to reload arg when arg idx changes. Signed-off-by: Keqian Zhu --- util/src/seccomp.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 47d14d626..2defd2a23 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -289,6 +289,8 @@ fn handle_process(opt: SeccompOpt) -> Vec { pub struct BpfRule { /// The first bpf_filter to compare syscall number. header_rule: SockFilter, + /// The last args index. + args_idx_last: Option, /// The inner rules to limit the arguments of syscall. inner_rules: Vec, /// The last bpf_filter to allow syscall. @@ -303,6 +305,7 @@ impl BpfRule { pub fn new(syscall_num: i64) -> BpfRule { BpfRule { header_rule: bpf_jump(BPF_JMP + BPF_JEQ + BPF_K, syscall_num as u32, 0, 1), + args_idx_last: None, inner_rules: Vec::new(), tail_rule: bpf_stmt(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), } @@ -312,16 +315,23 @@ impl BpfRule { /// /// # Arguments /// * `cmp` - Compare operator for given args_value and the raw args_value. - /// * `args_num` - The index number of system call's arguments. + /// * `args_idx` - The index number of system call's arguments. /// * `args_value` - The value of args_num you want to limit. This value /// used with `cmp` together. - pub fn add_constraint(mut self, cmp: SeccompCmpOpt, args_num: u32, args_value: u32) -> BpfRule { + pub fn add_constraint(mut self, cmp: SeccompCmpOpt, args_idx: u32, args_value: u32) -> BpfRule { if self.inner_rules.is_empty() { self.tail_rule = bpf_stmt(BPF_LD + BPF_W + BPF_ABS, SeccompData::nr()); } - // Create a bpf_filter to get args in `SeccompData`. - let args_filter = bpf_stmt(BPF_LD + BPF_W + BPF_ABS, SeccompData::args(args_num)); + let mut inner_append = Vec::new(); + + // Reload new args if idx changes. + if self.args_idx_last.ne(&Some(args_idx)) { + // Create a bpf_filter to get args in `SeccompData`. + let args_filter = bpf_stmt(BPF_LD + BPF_W + BPF_ABS, SeccompData::args(args_idx)); + inner_append.push(args_filter); + self.args_idx_last = Some(args_idx); + } // Create a bpf_filter to limit args in syscall. let constraint_filter = match cmp { @@ -332,12 +342,10 @@ impl BpfRule { SeccompCmpOpt::Le => bpf_jump(BPF_JMP + BPF_JGE + BPF_K, args_value, 1, 0), SeccompCmpOpt::Lt => bpf_jump(BPF_JMP + BPF_JGT + BPF_K, args_value, 1, 0), }; + inner_append.push(constraint_filter); + inner_append.push(bpf_stmt(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)); - self.append(&mut vec![ - args_filter, - constraint_filter, - bpf_stmt(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), - ]); + self.append(&mut inner_append); self } -- Gitee From 9663848744db8159029823f76b0637a501c6a6a3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 21 Jul 2023 21:49:02 +0800 Subject: [PATCH 1225/2187] seccomp: Stage bpf rules when jump is about to overflow The jump is type of U8, so when inner rules is too many, the jump will overflow. Based on the fact that bpfrules for a specific syscall can be split, this introduces stages_rules to stage current rules and start new session for building the rules for the syscall. Signed-off-by: Keqian Zhu --- util/src/seccomp.rs | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 2defd2a23..d15e44a90 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -287,6 +287,8 @@ fn handle_process(opt: SeccompOpt) -> Vec { /// A wrapper structure of a list of bpf_filters for a syscall's rule. #[derive(Debug)] pub struct BpfRule { + /// The staged rules to avoid jump offset overflow. + staged_rules: Vec, /// The first bpf_filter to compare syscall number. header_rule: SockFilter, /// The last args index. @@ -304,6 +306,7 @@ impl BpfRule { /// * `syscall_num` - the number of system call. pub fn new(syscall_num: i64) -> BpfRule { BpfRule { + staged_rules: Vec::new(), header_rule: bpf_jump(BPF_JMP + BPF_JEQ + BPF_K, syscall_num as u32, 0, 1), args_idx_last: None, inner_rules: Vec::new(), @@ -345,25 +348,47 @@ impl BpfRule { inner_append.push(constraint_filter); inner_append.push(bpf_stmt(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)); - self.append(&mut inner_append); - self + if !self.append(&mut inner_append) { + self.start_new_session(); + self.add_constraint(cmp, args_idx, args_value) + } else { + self + } } /// Change `BpfRules` to a list of `SockFilter`. It will be used when /// seccomp taking effect. - fn as_vec(&mut self) -> Vec { - let mut bpf_filters = vec![self.header_rule]; - bpf_filters.append(&mut self.inner_rules); + fn as_vec(&self) -> Vec { + let mut bpf_filters = self.staged_rules.clone(); + bpf_filters.push(self.header_rule); + bpf_filters.append(&mut self.inner_rules.clone()); bpf_filters.push(self.tail_rule); bpf_filters } + /// Stage current rules and start new session. Used when header rule jump + /// is about to overflow. + fn start_new_session(&mut self) { + // Save current rules to staged. + self.staged_rules.push(self.header_rule); + self.staged_rules.append(&mut self.inner_rules); + self.staged_rules.push(self.tail_rule); + + self.header_rule.jf = 1; + self.args_idx_last = None; + } + /// Add bpf_filters to `inner_rules`. - fn append(&mut self, bpf_filters: &mut Vec) { + fn append(&mut self, bpf_filters: &mut Vec) -> bool { let offset = bpf_filters.len() as u8; - self.header_rule.jf += offset; - self.inner_rules.append(bpf_filters); + if let Some(jf_added) = self.header_rule.jf.checked_add(offset) { + self.header_rule.jf = jf_added; + self.inner_rules.append(bpf_filters); + true + } else { + false + } } } -- Gitee From 9bbd1e8d6c758862ea85231732141716dccfa24f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 03:29:59 +0800 Subject: [PATCH 1226/2187] seccomp: Update unit test 1. Add comment for each sockfilter 2. Modify test cast to verify jump does not overflow. Signed-off-by: Keqian Zhu --- util/src/seccomp.rs | 128 ++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 39 deletions(-) diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index d15e44a90..7a0992df9 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -476,52 +476,48 @@ mod tests { // a list of bpf_filter to allow `read` syscall and forbidden others // in x86_64. let bpf_vec = vec![ + // Load arch SockFilter { code: 0x20, jt: 0, jf: 0, k: 4, }, - #[cfg(target_arch = "x86_64")] + // Verify arch SockFilter { code: 0x15, jt: 1, jf: 0, + #[cfg(target_arch = "x86_64")] k: 0xC000_003E, - }, - #[cfg(target_arch = "aarch64")] - SockFilter { - code: 0x15, - jt: 1, - jf: 0, + #[cfg(target_arch = "aarch64")] k: 0xC000_00B7, }, + // Ret kill SockFilter { code: 0x06, jt: 0, jf: 0, k: 0, }, + // Load syscall nr SockFilter { code: 0x20, jt: 0, jf: 0, k: 0, }, - #[cfg(target_arch = "x86_64")] + // Verify syscall nr SockFilter { code: 0x15, jt: 0, jf: 1, + #[cfg(target_arch = "x86_64")] k: 0, - }, - #[cfg(target_arch = "aarch64")] - SockFilter { - code: 0x15, - jt: 0, - jf: 1, + #[cfg(target_arch = "aarch64")] k: 63, }, + // Ret allow SockFilter { code: 0x06, jt: 0, @@ -540,85 +536,139 @@ mod tests { fn test_enable_syscall_extra() { // a list of bpf_filter to allow read `1024` bytes in x86_64 and // forbidden others - let bpf_vec = vec![ + let mut bpf_vec = vec![ + // Load arch SockFilter { code: 0x20, jt: 0, jf: 0, k: 4, }, - #[cfg(target_arch = "x86_64")] + // Verify arch SockFilter { code: 0x15, jt: 1, jf: 0, + #[cfg(target_arch = "x86_64")] k: 0xC000_003E, - }, - #[cfg(target_arch = "aarch64")] - SockFilter { - code: 0x15, - jt: 1, - jf: 0, + #[cfg(target_arch = "aarch64")] k: 0xC000_00B7, }, + // Ret kill SockFilter { code: 0x06, jt: 0, jf: 0, k: 0, }, + // Load syscall nr SockFilter { code: 0x20, jt: 0, jf: 0, k: 0, }, - #[cfg(target_arch = "x86_64")] + // Verify syscall nr SockFilter { code: 0x15, jt: 0, - jf: 4, + jf: 254, + #[cfg(target_arch = "x86_64")] k: 0, + #[cfg(target_arch = "aarch64")] + k: 63, }, - #[cfg(target_arch = "aarch64")] + // Load arg SockFilter { - code: 0x15, + code: 0x20, jt: 0, - jf: 4, - k: 63, + jf: 0, + k: 0x20, }, + ]; + for _ in 0..126 { + bpf_vec.append(&mut vec![ + // Verify arg + SockFilter { + code: 0x15, + jt: 0, + jf: 1, + k: 1024, + }, + // Ret allow + SockFilter { + code: 0x06, + jt: 0, + jf: 0, + k: 0x7fff_0000, + }, + ]); + } + bpf_vec.push( + // Load syscall nr SockFilter { code: 0x20, jt: 0, jf: 0, - k: 0x20, + k: 0, }, + ); + + // Start new session. + bpf_vec.append(&mut vec![ + // Verify syscall nr SockFilter { code: 0x15, jt: 0, - jf: 1, - k: 1024, + jf: 150, + #[cfg(target_arch = "x86_64")] + k: 0, + #[cfg(target_arch = "aarch64")] + k: 63, }, + // Load arg SockFilter { - code: 0x06, + code: 0x20, jt: 0, jf: 0, - k: 0x7fff_0000, + k: 0x20, }, + ]); + for _ in 126..200 { + bpf_vec.append(&mut vec![ + // Verify arg + SockFilter { + code: 0x15, + jt: 0, + jf: 1, + k: 1024, + }, + // Ret allow + SockFilter { + code: 0x06, + jt: 0, + jf: 0, + k: 0x7fff_0000, + }, + ]); + } + bpf_vec.push( + // Load syscall nr SockFilter { code: 0x20, jt: 0, jf: 0, k: 0, }, - ]; + ); let mut seccomp_filter = SyscallFilter::new(SeccompOpt::Trap); - seccomp_filter.push(&mut BpfRule::new(libc::SYS_read).add_constraint( - SeccompCmpOpt::Eq, - 2, - 1024, - )); + let mut read_rules = BpfRule::new(libc::SYS_read); + // Add enough constraint to verify that jump does not overflow. + for _ in 0..200 { + read_rules = read_rules.add_constraint(SeccompCmpOpt::Eq, 2, 1024); + } + seccomp_filter.push(&mut read_rules); assert_eq!(seccomp_filter.sock_filters, bpf_vec); } -- Gitee From 48da9466e70dbc10b4e579b6d16824c5127afb5e Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 5 Jul 2023 04:55:37 +0800 Subject: [PATCH 1227/2187] Qcow2: judge the errno when doing a fallocate Additional judgment on EOPNOTSUPP to reduce the error logs. Signed-off-by: Xiao Ye --- util/src/aio/mod.rs | 12 +++++++--- util/src/aio/raw.rs | 54 ++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index f6c3b5e19..65c64ab0a 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -43,7 +43,7 @@ const AIO_OFF: &str = "off"; const AIO_NATIVE: &str = "native"; /// Io-uring aio type. const AIO_IOURING: &str = "io_uring"; -/// Max bytes of bounce buffer for misaligned IO. +/// Max bytes of bounce buffer for IO. const MAX_LEN_BOUNCE_BUFF: u64 = 1 << 20; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] @@ -595,13 +595,13 @@ impl Aio { fn discard_sync(&mut self, cb: AioCb) -> Result<()> { let ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); - if ret < 0 { + if ret < 0 && ret != -libc::ENOTSUP as i64 { error!("Failed to do sync discard."); } (self.complete_func)(&cb, ret) } - fn write_zeroes_sync(&mut self, cb: AioCb) -> Result<()> { + fn write_zeroes_sync(&mut self, mut cb: AioCb) -> Result<()> { let mut ret; if cb.opcode == OpCode::WriteZeroesUnmap { ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); @@ -610,6 +610,12 @@ impl Aio { } } ret = raw_write_zeroes(cb.file_fd, cb.offset, cb.nbytes); + if ret == -libc::ENOTSUP as i64 && !cb.iovec.is_empty() { + cb.opcode = OpCode::Pwritev; + cb.write_zeroes = WriteZeroesState::Off; + return self.submit_request(cb); + } + if ret < 0 { error!("Failed to do sync write zeroes."); } diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 0808a44ae..fa2f0185a 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -140,22 +140,8 @@ pub fn raw_datasync(fd: RawFd) -> i64 { } pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { - let mut ret; - loop { - // SAFETY: fd is valid. - ret = unsafe { - fallocate( - fd as c_int, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - offset as i64, - size as i64, - ) as i64 - }; - if ret == 0 || nix::errno::errno() != libc::EINTR { - break; - } - } - if ret < 0 { + let ret = do_fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, size); + if ret < 0 && ret != -libc::ENOTSUP as i64 { error!( "Failed to fallocate for {}, errno {}.", fd, @@ -166,22 +152,8 @@ pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { } pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i64 { - let mut ret; - loop { - // SAFETY: fd is valid. - ret = unsafe { - fallocate( - fd as c_int, - FALLOC_FL_ZERO_RANGE, - offset as i64, - size as i64, - ) as i64 - }; - if ret == 0 || nix::errno::errno() != libc::EINTR { - break; - } - } - if ret < 0 { + let ret = do_fallocate(fd, FALLOC_FL_ZERO_RANGE, offset, size); + if ret < 0 && ret != -libc::ENOTSUP as i64 { error!( "Failed to fallocate zero range for fd {}, errno {}.", fd, @@ -190,3 +162,21 @@ pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i64 { } ret } + +fn do_fallocate(fd: RawFd, mode: i32, offset: usize, size: u64) -> i64 { + loop { + // SAFETY: fd is valid. + let ret = unsafe { fallocate(fd as c_int, mode, offset as i64, size as i64) as i64 }; + if ret == 0 { + return 0; + } + if nix::errno::errno() != libc::EINTR { + break; + } + } + + if [libc::ENODEV, libc::ENOSYS, libc::EOPNOTSUPP, libc::ENOTTY].contains(&nix::errno::errno()) { + return -libc::ENOTSUP as i64; + } + -nix::errno::errno() as i64 +} -- Gitee From 27d408b97d3c30f045c034db90e9aec1c949fdf0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 20 Jul 2023 08:46:05 +0800 Subject: [PATCH 1228/2187] Qcow2: fix some error in cluster allocation 1. The length of buf needs to multiplied by cluster_size 2. When the file is expanded, addr will exceed file length. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 6765d06c0..e1a7000d3 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -45,7 +45,10 @@ use crate::{ }; use machine_manager::qmp::qmp_schema::SnapshotInfo; use util::{ - aio::{get_iov_size, iov_from_buf_direct, iovecs_split, Aio, AioCb, AioEngine, Iovec, OpCode}, + aio::{ + get_iov_size, iov_from_buf_direct, iovecs_split, raw_write_zeroes, Aio, AioCb, AioEngine, + Iovec, OpCode, + }, num_ops::{div_round_up, round_down, round_up}, time::{get_format_time, gettime}, }; @@ -561,9 +564,14 @@ impl Qcow2Driver { let size = clusters * self.header.cluster_size(); let addr = self.refcount.alloc_cluster(&mut self.header, size)?; if write_zero && addr < self.driver.disk_size()? { - // Clean the cluster. - let zero = vec![0_u8; self.header.cluster_size() as usize]; - self.sync_aio.borrow_mut().write_buffer(addr, &zero)?; + let ret = raw_write_zeroes(self.sync_aio.borrow_mut().fd, addr as usize, size); + if ret < 0 { + let zero_buf = vec![0_u8; self.header.cluster_size() as usize]; + for i in 0..clusters { + let offset = addr + i * self.header.cluster_size(); + self.sync_aio.borrow_mut().write_buffer(offset, &zero_buf)?; + } + } } self.driver.extend_len(addr + size)?; Ok(addr) -- Gitee From fd3d8fd047a379927641d547dc31cfc506aa77ee Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 4 Jul 2023 16:44:44 +0800 Subject: [PATCH 1229/2187] Qcow2: add test for discard Add test for discard Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 217 ++++++++++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 18 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index e1a7000d3..032cf3e03 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1505,13 +1505,13 @@ mod test { use std::{ fs::remove_file, io::{Seek, SeekFrom, Write}, - os::unix::fs::OpenOptionsExt, + os::unix::{fs::OpenOptionsExt, prelude::FileExt}, process::Command, }; use machine_manager::config::DiskFormat; use util::{ - aio::{iov_to_buf_direct, WriteZeroesState}, + aio::{iov_to_buf_direct, Iovec, WriteZeroesState}, file::get_file_alignment, }; @@ -1561,6 +1561,10 @@ mod test { .unwrap(); file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) .unwrap(); + let zero_buf = + vec![0_u8; (cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) as usize]; + file.write_all(&zero_buf).unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); file.write_all(&header.to_vec()).unwrap(); // Cluster 1 is the refcount table. @@ -1602,6 +1606,22 @@ mod test { let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); Qcow2Driver::new(file, aio, conf).unwrap() } + + /// Write full the disk with value disorderly. + fn write_full_disk(&self, qcow2_driver: &mut Qcow2Driver<()>, value: u8) -> Result<()> { + let buf = vec![value; 1 << self.cluster_bits]; + // Simulate discontinuity of host offset. + let mod_range = 2; + for mod_value in 0..mod_range { + for i in 0..1 << (self.img_bits - self.cluster_bits) { + if i % mod_range == mod_value { + let offset: u64 = i * (1 << self.cluster_bits); + qcow2_write(qcow2_driver, &buf, offset as usize)?; + } + } + } + Ok(()) + } } impl Drop for TestImage { @@ -1629,7 +1649,7 @@ mod test { output.stdout } - fn get_disk_size(img_path: Rc) -> u64 { + fn get_disk_size(img_path: String) -> u64 { let out = execute_cmd(format!("du -shk {}", img_path)); let str_out = std::str::from_utf8(&out) .unwrap() @@ -1949,13 +1969,70 @@ mod test { } } + /// Test the basic functions of alloc cluster. + /// TestStep: + /// 1. Init qcow2 file driver with property of discard and write zero. + /// 2. Write full of disk and then send discard command to recycle space. + /// 3. Call the function for alloc_cluster with args of write zero + /// and read data from the corresponding address of the file. + /// Expect: + /// Newly allocated data is full of zero. + #[test] + fn test_alloc_cluster_with_zero() { + let path = "/tmp/alloc_cluster_with_zero.qcow2"; + // Create a new image, with size = 16M, cluster_size = 64K. + let image_bits = 24; + let cluster_bits = 16; + let alloc_clusters: Vec = vec![1, 2, 4, 8, 16, 32]; + + for n_clusters in alloc_clusters { + let image = TestImage::new(path, image_bits, cluster_bits); + let (req_align, buf_align) = get_file_alignment(&image.file, true); + let conf = BlockProperty { + id: path.to_string(), + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align, + buf_align, + discard: true, + write_zeroes: WriteZeroesState::On, + l2_cache_size: None, + refcount_cache_size: None, + }; + let mut qcow2_driver = image.create_qcow2_driver(conf.clone()); + + assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); + assert!(qcow2_driver.discard(0, 1 << image_bits, ()).is_ok()); + + let times: u64 = (1 << (image_bits - cluster_bits)) / n_clusters; + for _time in 0..times { + let addr = qcow2_driver.alloc_cluster(n_clusters, true).unwrap(); + for i in 0..n_clusters { + let mut buf = vec![1_u8; qcow2_driver.header.cluster_size() as usize]; + let offset = addr + i * qcow2_driver.header.cluster_size(); + assert!(image.file.read_at(&mut buf, offset).is_ok()); + assert!(vec_is_zero(&buf)); + } + } + } + } + + /// Test the basic functions of discard. + /// TestStep: + /// 1. Init qcow2 file driver with property of discard. + /// 2. Create a new qcow2 image, and then write full disk. + /// 3. Send discard command. + /// Expect: + /// The size of disk space has been reduced. #[test] fn test_discard_basic() { - let path = "/tmp/discard.qcow2"; + let path = "/tmp/discard_basic.qcow2"; // Create a new image, with size = 16M, cluster_size = 64K. let image_bits = 24; let cluster_bits = 16; - let conf = BlockProperty { + let cluster_size = 1 << cluster_bits; + let mut conf = BlockProperty { id: path.to_string(), format: DiskFormat::Qcow2, iothread: None, @@ -1967,27 +2044,132 @@ mod test { l2_cache_size: None, refcount_cache_size: None, }; - let image = TestImage::new(path, image_bits, cluster_bits); - let mut qcow2_driver = image.create_qcow2_driver(conf); - // Full the disk. - let offset_start = 0; - for i in 0..1 << (image.img_bits - image.cluster_bits) { - let offset = offset_start + i * (1 << image.cluster_bits); - let buf = vec![0_u8; 1 << image.cluster_bits]; - assert!(qcow2_write(&mut qcow2_driver, &buf, offset as usize).is_ok()); + + // (offset_begin, offset_end) + let test_data: Vec<(u64, u64)> = vec![ + (0, cluster_size * 5), + (cluster_size * 5, cluster_size * 10), + (cluster_size * 5 + 32768, cluster_size * 10), + (cluster_size * 5, cluster_size * 10 + 32768), + (cluster_size * 5, cluster_size * 5 + 32768), + (cluster_size * 5 + 32768, cluster_size * 5 + 32768), + (cluster_size * 5 + 32768, cluster_size * 5 + 49152), + (cluster_size * 5 + 32768, cluster_size * 6), + (cluster_size * 5 + 32768, cluster_size * 10 + 32768), + (0, 1 << image_bits), + ]; + + // Qcow2 driver will align the offset of requests according to the cluster size, + // and then use the aligned interval for recying disk. + for (offset_begin, offset_end) in test_data { + let image = TestImage::new(path, image_bits, cluster_bits); + let (req_align, buf_align) = get_file_alignment(&image.file, true); + conf.req_align = req_align; + conf.buf_align = buf_align; + let mut qcow2_driver = image.create_qcow2_driver(conf.clone()); + + assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); + let offset_begin_algn = round_up(offset_begin, cluster_size).unwrap(); + let offset_end_align = round_down(offset_end, cluster_size).unwrap(); + let expect_discard_space = if offset_end_align <= offset_begin_algn { + 0 + } else { + (offset_end_align - offset_begin_algn) / 1024 + }; + let full_image_size = get_disk_size(path.to_string()); + assert!(qcow2_driver + .discard(offset_begin as usize, offset_end - offset_begin, ()) + .is_ok()); + assert!(qcow2_driver.flush().is_ok()); + + let discard_image_size = get_disk_size(path.to_string()); + assert_eq!(full_image_size, discard_image_size + expect_discard_space); + // TODO: Check the metadata for qcow2 image. } - let image_size_1 = get_disk_size(Rc::new(path.to_string())); - assert!(qcow2_driver.discard(0, 1 << image_bits, ()).is_ok()); - let image_size_2 = get_disk_size(Rc::new(path.to_string())); - assert_eq!(image_size_1, image_size_2 + (1 << image_bits) / 1024); } + /// Test the discard during the delete snapshot. + /// TestStep: + /// 1. Init qcow2 file driver with property of discard. + /// 2. Create a new qcow2 image, and then write full disk. + /// 3. Create a new snapshot, and then rewrite the disk, which will result in copy on write. + /// 4. Delete snapshot, which will result in discard. + /// Expect: + /// The size of disk space has been reduced. + #[test] + fn test_snapshot_with_discard() { + let path = "/tmp/snapshot_with_discard.qcow2"; + // Create a new image, with size = 1G, cluster_size = 64K. + let image_bits = 24; + let cluster_bits = 16; + let image = TestImage::new(path, image_bits, cluster_bits); + let (req_align, buf_align) = get_file_alignment(&image.file, true); + let conf = BlockProperty { + id: path.to_string(), + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align, + buf_align, + discard: true, + write_zeroes: WriteZeroesState::Off, + l2_cache_size: None, + refcount_cache_size: None, + }; + + let mut qcow2_driver = image.create_qcow2_driver(conf); + assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); + + let disk_size_1 = get_disk_size(path.to_string()); + // Create a snapshot and write full disk again, which will result in copy on write. + // Delete the snapshot, which will result in discard, and recycle disk size. + assert!(qcow2_driver + .create_snapshot("test_snapshot_1".to_string(), 1000) + .is_ok()); + assert!(image.write_full_disk(&mut qcow2_driver, 2).is_ok()); + assert!(qcow2_driver.flush().is_ok()); + let disk_size_2 = get_disk_size(path.to_string()); + // Data cluster + 1 snapshot table + l1 table(cow) + l2 table(cow) + // But, the cluster of snapshots may not be fully allocated + assert!(disk_size_1 < disk_size_2); + + assert!(qcow2_driver + .create_snapshot("test_snapshot_2".to_string(), 1000) + .is_ok()); + assert!(image.write_full_disk(&mut qcow2_driver, 2).is_ok()); + assert!(qcow2_driver.flush().is_ok()); + let disk_size_3 = get_disk_size(path.to_string()); + // Data cluster + l1 table(cow) + l2 table(cow) + assert!(disk_size_2 < disk_size_3); + + // Snapshot delete will result in discard, which will recycle the disk space. + assert!(qcow2_driver + .delete_snapshot("test_snapshot_2".to_string()) + .is_ok()); + let disk_size_4 = get_disk_size(path.to_string()); + assert_eq!(disk_size_4, disk_size_2); + + assert!(qcow2_driver + .delete_snapshot("test_snapshot_1".to_string()) + .is_ok()); + let disk_size_5 = get_disk_size(path.to_string()); + assert_eq!(disk_size_5, disk_size_1); + } + + /// Test the basic functions of write zero. + /// TestStep: + /// 1. Init qcow2 file driver with property of write zero. + /// 2. Create a new qcow2 image, and then write full disk with value of 1. + /// 3. Send write zero command with (offset, nbytes). + /// Expect: + /// 1. The data read from disk of the specified interval is zero. #[test] fn test_write_zero_basic() { // Create a new image, with size = 16M, cluster_size = 64K. let path = "/tmp/discard_write_zero.qcow2"; let image_bits = 24; let cluster_bits = 16; + let image = TestImage::new(path, image_bits, cluster_bits); let conf = BlockProperty { id: path.to_string(), format: DiskFormat::Qcow2, @@ -2000,7 +2182,6 @@ mod test { l2_cache_size: None, refcount_cache_size: None, }; - let image = TestImage::new(path, image_bits, cluster_bits); let mut qcow2_driver = image.create_qcow2_driver(conf); // Test 1. -- Gitee From 3e979defbf7687d499b70a8e3b9c5397fa9186b9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 23 Jul 2023 06:37:23 +0800 Subject: [PATCH 1230/2187] Qcow2: optimize ut of qcow2 The actual size of file may change in different file systems. So only check the actual capacity of the file after discard. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 032cf3e03..2feeececc 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -2147,13 +2147,16 @@ mod test { .delete_snapshot("test_snapshot_2".to_string()) .is_ok()); let disk_size_4 = get_disk_size(path.to_string()); - assert_eq!(disk_size_4, disk_size_2); + // The actual size of the file should not exceed 1 cluster. + assert!(disk_size_4 > disk_size_2 - 32); + assert!(disk_size_4 < disk_size_2 + 32); assert!(qcow2_driver .delete_snapshot("test_snapshot_1".to_string()) .is_ok()); let disk_size_5 = get_disk_size(path.to_string()); - assert_eq!(disk_size_5, disk_size_1); + assert!(disk_size_5 > disk_size_1 - 32); + assert!(disk_size_5 < disk_size_1 + 32); } /// Test the basic functions of write zero. -- Gitee From b9655c6ef01b9570d9e2171f0dc84680d5b7e65c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 20 May 2023 17:00:05 +0800 Subject: [PATCH 1231/2187] virtio-net: support attach/detach queue for tap device Support attach/detach queue for tap device when modifing queue num in guest. Signed-off-by: Yan Wang --- machine/src/micro_vm/syscall.rs | 3 +- machine/src/standard_vm/aarch64/syscall.rs | 3 +- machine/src/standard_vm/x86_64/syscall.rs | 3 +- util/src/tap.rs | 36 ++++++++++++++++++++-- virtio/src/device/net.rs | 31 ++++++++++++++----- virtio/src/vhost/kernel/net.rs | 1 + virtio/src/vhost/user/net.rs | 1 + 7 files changed, 64 insertions(+), 14 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index edafa3b53..5f92e0883 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -12,7 +12,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; use virtio::VhostKern::*; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/futex.h @@ -159,6 +159,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32); diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 9986cde3a..7a730510c 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -12,7 +12,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; use util::v4l2::{ VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, @@ -247,6 +247,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_SET_IRQS() as u32) diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index c88303228..8b3ec381f 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -12,7 +12,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETVNETHDRSZ}; +use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; use util::v4l2::{ VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, @@ -249,6 +249,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_SET_IRQS() as u32) diff --git a/util/src/tap.rs b/util/src/tap.rs index 189e8010d..c386e88b1 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -10,15 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Context}; use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use anyhow::{anyhow, bail, Context, Result}; +use log::error; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; -use anyhow::Result; +const IFF_ATTACH_QUEUE: u16 = 0x0200; +const IFF_DETACH_QUEUE: u16 = 0x0400; pub const TUN_F_CSUM: u32 = 1; pub const TUN_F_TSO4: u32 = 2; @@ -38,6 +41,7 @@ ioctl_iow_nr!(TUNSETIFF, 84, 202, ::std::os::raw::c_int); ioctl_ior_nr!(TUNGETFEATURES, 84, 207, ::std::os::raw::c_uint); ioctl_iow_nr!(TUNSETOFFLOAD, 84, 208, ::std::os::raw::c_int); ioctl_iow_nr!(TUNSETVNETHDRSZ, 84, 216, ::std::os::raw::c_int); +ioctl_iow_nr!(TUNSETQUEUE, 84, 217, ::std::os::raw::c_int); #[repr(C)] pub struct IfReq { @@ -47,6 +51,7 @@ pub struct IfReq { pub struct Tap { pub file: File, + pub enabled: bool, } impl Tap { @@ -112,7 +117,10 @@ impl Tap { bail!("Needs multiqueue, but no kernel support for IFF_MULTI_QUEUE available"); } - Ok(Tap { file }) + Ok(Tap { + file, + enabled: true, + }) } pub fn set_offload(&self, flags: u32) -> Result<()> { @@ -138,6 +146,27 @@ impl Tap { (unsafe { ioctl_with_val(&self.file, TUNSETOFFLOAD(), flags as libc::c_ulong) }) >= 0 } + pub fn set_queue(&self, enable: bool) -> i32 { + let ifr_flags = if enable { + IFF_ATTACH_QUEUE + } else { + IFF_DETACH_QUEUE + }; + let mut if_req = IfReq { + ifr_name: [0_u8; IFNAME_SIZE], + ifr_flags, + }; + let ret = unsafe { ioctl_with_mut_ref(&self.file, TUNSETQUEUE(), &mut if_req) }; + if ret < 0 { + error!( + "Failed to set queue, flags is {}, error is {}", + ifr_flags, + std::io::Error::last_os_error() + ); + } + ret + } + pub fn read(&mut self, buf: &mut [u8]) -> IoResult { self.file.read(buf) } @@ -155,6 +184,7 @@ impl Clone for Tap { fn clone(&self) -> Self { Tap { file: self.file.try_clone().unwrap(), + enabled: self.enabled, } } } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index daea4fd15..4f2f9b3a0 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use once_cell::sync::Lazy; use std::collections::HashMap; use std::io::{ErrorKind, Write}; use std::os::unix::io::{AsRawFd, RawFd}; @@ -21,6 +20,12 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; +use anyhow::{anyhow, bail, Context, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::{error, warn}; +use once_cell::sync::Lazy; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, @@ -38,8 +43,6 @@ use crate::{ VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use address_space::{AddressSpace, RegionCache}; -use anyhow::{anyhow, bail, Context, Result}; -use log::{error, warn}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ config::{ConfigCheck, NetworkInterfaceConfig}, @@ -59,7 +62,7 @@ use util::num_ops::{read_u32, str_to_usize}; use util::tap::{ Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, }; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + /// Number of virtqueues(rx/tx/ctrl). const QUEUE_NUM_NET: usize = 3; /// The Mac Address length. @@ -360,6 +363,7 @@ impl CtrlInfo { fn handle_mq( &mut self, mem_space: &AddressSpace, + taps: Option<&mut Vec>, cmd: u8, data_iovec: &mut Vec, ) -> u8 { @@ -376,11 +380,19 @@ impl CtrlInfo { return ack; } - if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) - .contains(&queue_pairs) - { + queue_pairs = LittleEndian::read_u16(queue_pairs.as_bytes()); + let max_pairs = self.state.lock().unwrap().config_space.max_virtqueue_pairs; + if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=max_pairs).contains(&queue_pairs) { error!("Invalid queue pairs {}", queue_pairs); - ack = VIRTIO_NET_ERR; + return VIRTIO_NET_ERR; + } + if let Some(taps) = taps { + for (index, tap) in taps.iter_mut().enumerate() { + if tap.set_queue(index < queue_pairs as usize) != 0 { + error!("Failed to set queue, index is {}", index); + return VIRTIO_NET_ERR; + } + } } } else { error!( @@ -509,6 +521,7 @@ pub struct NetCtrlHandler { pub driver_features: u64, /// Device is broken or not. pub device_broken: Arc, + pub taps: Option>, } #[repr(C, packed)] @@ -585,6 +598,7 @@ impl NetCtrlHandler { VIRTIO_NET_CTRL_MQ => { ack = self.ctrl.ctrl_info.lock().unwrap().handle_mq( &self.mem_space, + self.taps.as_mut(), ctrl_hdr.cmd, &mut data_iovec, ); @@ -1544,6 +1558,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features, device_broken: self.broken.clone(), + taps: self.taps.clone(), }; let notifiers = diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 18da527dd..3853af4c8 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -277,6 +277,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features, device_broken: self.broken.clone(), + taps: None, }; let notifiers = diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 050a2ee71..bcadb2d29 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -252,6 +252,7 @@ impl VirtioDevice for Net { interrupt_cb: interrupt_cb.clone(), driver_features, device_broken: self.broken.clone(), + taps: None, }; let notifiers = -- Gitee From dc81c059851df327f9fcfe28d2e7a94881a4ed37 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 20 May 2023 17:03:57 +0800 Subject: [PATCH 1232/2187] virtio-net: decrease the fd num for tap device Using Arc to decrease the fd num for tap device Signed-off-by: Yan Wang --- util/src/tap.rs | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/util/src/tap.rs b/util/src/tap.rs index c386e88b1..a6480542b 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -14,6 +14,7 @@ use std::fs::{File, OpenOptions}; use std::io::{Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use log::error; @@ -49,8 +50,9 @@ pub struct IfReq { ifr_flags: u16, } +#[derive(Clone)] pub struct Tap { - pub file: File, + pub file: Arc, pub enabled: bool, } @@ -118,13 +120,14 @@ impl Tap { } Ok(Tap { - file, + file: Arc::new(file), enabled: true, }) } pub fn set_offload(&self, flags: u32) -> Result<()> { - let ret = unsafe { ioctl_with_val(&self.file, TUNSETOFFLOAD(), flags as libc::c_ulong) }; + let ret = + unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) }; if ret < 0 { return Err(anyhow!("ioctl TUNSETOFFLOAD failed.".to_string())); } @@ -133,7 +136,7 @@ impl Tap { } pub fn set_hdr_size(&self, len: u32) -> Result<()> { - let ret = unsafe { ioctl_with_ref(&self.file, TUNSETVNETHDRSZ(), &len) }; + let ret = unsafe { ioctl_with_ref(self.file.as_ref(), TUNSETVNETHDRSZ(), &len) }; if ret < 0 { return Err(anyhow!("ioctl TUNSETVNETHDRSZ failed.".to_string())); } @@ -143,10 +146,14 @@ impl Tap { pub fn has_ufo(&self) -> bool { let flags = TUN_F_CSUM | TUN_F_UFO; - (unsafe { ioctl_with_val(&self.file, TUNSETOFFLOAD(), flags as libc::c_ulong) }) >= 0 + (unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) }) + >= 0 } - pub fn set_queue(&self, enable: bool) -> i32 { + pub fn set_queue(&mut self, enable: bool) -> i32 { + if enable == self.enabled { + return 0; + } let ifr_flags = if enable { IFF_ATTACH_QUEUE } else { @@ -156,8 +163,11 @@ impl Tap { ifr_name: [0_u8; IFNAME_SIZE], ifr_flags, }; - let ret = unsafe { ioctl_with_mut_ref(&self.file, TUNSETQUEUE(), &mut if_req) }; - if ret < 0 { + + let ret = unsafe { ioctl_with_mut_ref(self.file.as_ref(), TUNSETQUEUE(), &mut if_req) }; + if ret == 0 { + self.enabled = enable; + } else { error!( "Failed to set queue, flags is {}, error is {}", ifr_flags, @@ -168,23 +178,14 @@ impl Tap { } pub fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.file.read(buf) + self.file.as_ref().read(buf) } pub fn write(&mut self, buf: &[u8]) -> IoResult { - self.file.write(buf) + self.file.as_ref().write(buf) } pub fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() } } - -impl Clone for Tap { - fn clone(&self) -> Self { - Tap { - file: self.file.try_clone().unwrap(), - enabled: self.enabled, - } - } -} -- Gitee From 3ffd1eaedff239fd6df453a2aa8832086be2bb8c Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 20 May 2023 17:08:04 +0800 Subject: [PATCH 1233/2187] virtio-net: optimize some code for virtio-net 1. convert the vlan id to little endian. 2. using loop instead of while for handle_rx(). Signed-off-by: Yan Wang --- virtio/src/device/net.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 4f2f9b3a0..64cd3b02e 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -335,6 +335,7 @@ impl CtrlInfo { if ack == VIRTIO_NET_ERR { return ack; } + vid = LittleEndian::read_u16(vid.as_bytes()); if vid >= CTRL_MAX_VLAN { return VIRTIO_NET_ERR; } @@ -770,10 +771,13 @@ impl NetIoHandler { fn handle_rx(&mut self) -> Result<()> { self.trace_request("Net".to_string(), "to rx".to_string()); - let mut queue = self.rx.queue.lock().unwrap(); + if self.tap.is_none() { + return Ok(()); + } + let mut queue = self.rx.queue.lock().unwrap(); let mut rx_packets = 0; - while let Some(tap) = self.tap.as_mut() { + loop { let elem = queue .vring .pop_avail(&self.mem_space, self.driver_features) @@ -799,7 +803,7 @@ impl NetIoHandler { } // Read the data from the tap device. - let size = NetIoHandler::read_from_tap(&iovecs, tap); + let size = NetIoHandler::read_from_tap(&iovecs, self.tap.as_mut().unwrap()); if size < (NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH) as i32 { queue.vring.push_back(); break; -- Gitee From 0ab6b5a27681fe30012c6f174a24dfcc05792d3a Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 26 Jul 2023 10:17:29 +0800 Subject: [PATCH 1234/2187] usb: move id to UsbDevice Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 40 ++++++++++++++++------------------ devices/src/usb/keyboard.rs | 12 +++------- devices/src/usb/mod.rs | 8 +++++-- devices/src/usb/storage.rs | 14 +++++------- devices/src/usb/tablet.rs | 12 +++------- devices/src/usb/usbhost/mod.rs | 23 +++++++++---------- 6 files changed, 47 insertions(+), 62 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 1441244b6..34e2a325a 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -96,15 +96,14 @@ const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; const USB_CAMERA_BUFFER_LEN: usize = 12 * 1024; pub struct UsbCamera { - id: String, // uniq device id - usb_device: UsbDevice, // general usb device object - vs_control: VideoStreamingControl, // video stream control info - camera_fd: Arc, // camera io fd - camera_dev: Arc>, // backend device + usb_device: UsbDevice, // general usb device object + vs_control: VideoStreamingControl, // video stream control info + camera_fd: Arc, // camera io fd + camera_dev: Arc>, // backend device packet_list: Arc>>>>, // packet to be processed - payload: Arc>, // uvc payload - listening: bool, // if the camera is listening or not - broken: Arc, // if the device broken or not + payload: Arc>, // uvc payload + listening: bool, // if the camera is listening or not + broken: Arc, // if the device broken or not iothread: Option, delete_evts: Vec, } @@ -610,8 +609,7 @@ impl UsbCamera { pub fn new(config: UsbCameraConfig) -> Result { let camera = camera_ops(config.clone())?; Ok(Self { - id: config.id.unwrap(), - usb_device: UsbDevice::new(USB_CAMERA_BUFFER_LEN), + usb_device: UsbDevice::new(config.id.unwrap(), USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), camera_dev: camera, @@ -636,7 +634,7 @@ impl UsbCamera { } }); let clone_broken = self.broken.clone(); - let clone_id = self.id.clone(); + let clone_id = self.device_id().to_string(); let broken_cb: CameraBrokenCallback = Arc::new(move || { clone_broken.store(true, Ordering::SeqCst); error!("USB Camera {} device broken", clone_id); @@ -647,7 +645,7 @@ impl UsbCamera { } fn activate(&mut self, fmt: &CamBasicFmt) -> Result<()> { - info!("USB Camera {} activate", self.id); + info!("USB Camera {} activate", self.device_id()); self.camera_dev.lock().unwrap().reset(); self.payload.lock().unwrap().reset(); let mut locked_camera = self.camera_dev.lock().unwrap(); @@ -678,9 +676,12 @@ impl UsbCamera { } fn deactivate(&mut self) -> Result<()> { - info!("USB Camera {} deactivate", self.id); + info!("USB Camera {} deactivate", self.device_id()); if self.broken.load(Ordering::Acquire) { - info!("USB Camera {} broken when deactivate, reset it.", self.id); + info!( + "USB Camera {} broken when deactivate, reset it.", + self.device_id() + ); self.camera_dev.lock().unwrap().reset(); self.broken.store(false, Ordering::SeqCst); } else { @@ -859,13 +860,13 @@ impl UsbDeviceOps for UsbCamera { } fn unrealize(&mut self) -> Result<()> { - info!("Camera {} unrealize", self.id); + info!("Camera {} unrealize", self.device_id()); self.camera_dev.lock().unwrap().reset(); Ok(()) } fn reset(&mut self) { - info!("Camera {} device reset", self.id); + info!("Camera {} device reset", self.device_id()); self.usb_device.addr = 0; if let Err(e) = self.unregister_camera_fd() { error!("Failed to unregister fd when reset {:?}", e); @@ -911,7 +912,8 @@ impl UsbDeviceOps for UsbCamera { if let Err(e) = self.camera_fd.write(1) { error!( "Failed to write fd when handle data for {} {:?}", - self.id, e + self.device_id(), + e ); // SAFETY: packet is push before, and no other thread modify the list. let p = locked_list.pop_back().unwrap(); @@ -926,10 +928,6 @@ impl UsbDeviceOps for UsbCamera { } } - fn device_id(&self) -> String { - self.id.clone() - } - fn set_controller(&mut self, _cntlr: Weak>) {} fn get_controller(&self) -> Option>> { diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 339e23f92..9e6f2bbd3 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -120,7 +120,6 @@ const DESC_STRINGS: [&str; 5] = [ /// USB keyboard device. pub struct UsbKeyboard { - id: String, usb_device: UsbDevice, hid: Hid, /// USB controller used to notify controller to transfer data. @@ -165,8 +164,7 @@ impl KeyboardOpts for UsbKeyboardAdapter { impl UsbKeyboard { pub fn new(id: String) -> Self { Self { - id, - usb_device: UsbDevice::new(USB_DEVICE_BUFFER_DEFAULT_LEN), + usb_device: UsbDevice::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Keyboard), cntlr: None, } @@ -180,7 +178,7 @@ impl UsbDeviceOps for UsbKeyboard { let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); self.usb_device .init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; - let id = self.id.clone(); + let id = self.device_id().to_string(); let kbd = Arc::new(Mutex::new(self)); let kbd_adapter = Arc::new(Mutex::new(UsbKeyboardAdapter { usb_kbd: kbd.clone(), @@ -191,7 +189,7 @@ impl UsbDeviceOps for UsbKeyboard { } fn unrealize(&mut self) -> Result<()> { - unregister_keyboard(&self.id.clone()); + unregister_keyboard(self.device_id()); Ok(()) } @@ -233,10 +231,6 @@ impl UsbDeviceOps for UsbKeyboard { self.hid.handle_data_packet(&mut locked_p); } - fn device_id(&self) -> String { - self.id.clone() - } - fn get_usb_device(&self) -> &UsbDevice { &self.usb_device } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 1edf81e34..1ad6d90f2 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -112,6 +112,7 @@ impl UsbEndpoint { /// USB device common structure. pub struct UsbDevice { + pub id: String, pub port: Option>>, pub speed: u32, pub addr: u8, @@ -129,8 +130,9 @@ pub struct UsbDevice { } impl UsbDevice { - pub fn new(data_buf_len: usize) -> Self { + pub fn new(id: String, data_buf_len: usize) -> Self { let mut dev = UsbDevice { + id, port: None, speed: 0, addr: 0, @@ -384,7 +386,9 @@ pub trait UsbDeviceOps: Send + Sync { fn handle_data(&mut self, packet: &Arc>); /// Unique device id. - fn device_id(&self) -> String; + fn device_id(&self) -> &str { + &self.get_usb_device().id + } /// Get the UsbDevice. fn get_usb_device(&self) -> &UsbDevice; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index f46374ffb..71e71a2d1 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -224,7 +224,6 @@ impl UsbStorageState { /// USB storage device. pub struct UsbStorage { - id: String, usb_device: UsbDevice, state: UsbStorageState, /// USB controller used to notify controller to transfer data. @@ -315,8 +314,7 @@ impl UsbStorage { }; Self { - id: config.id.clone().unwrap(), - usb_device: UsbDevice::new(USB_DEVICE_BUFFER_DEFAULT_LEN), + usb_device: UsbDevice::new(config.id.clone().unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN), state: UsbStorageState::new(), cntlr: None, config: config.clone(), @@ -575,15 +573,15 @@ impl UsbDeviceOps for UsbStorage { }; if let Err(e) = result { - warn!("USB-storage {}: handle data error: {:?}", self.id, e); + warn!( + "USB-storage {}: handle data error: {:?}", + self.device_id(), + e + ); locked_packet.status = UsbPacketStatus::Stall; } } - fn device_id(&self) -> String { - self.id.clone() - } - fn get_usb_device(&self) -> &UsbDevice { &self.usb_device } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 562e1b798..4bd8640bf 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -113,7 +113,6 @@ const STR_SERIAL_TABLET_INDEX: u8 = 4; const DESC_STRINGS: [&str; 5] = ["", "StratoVirt", "StratoVirt USB Tablet", "HID Tablet", "2"]; /// USB tablet device. pub struct UsbTablet { - id: String, usb_device: UsbDevice, hid: Hid, /// USB controller used to notify controller to transfer data. @@ -123,8 +122,7 @@ pub struct UsbTablet { impl UsbTablet { pub fn new(id: String) -> Self { Self { - id, - usb_device: UsbDevice::new(USB_DEVICE_BUFFER_DEFAULT_LEN), + usb_device: UsbDevice::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Tablet), cntlr: None, } @@ -176,7 +174,7 @@ impl UsbDeviceOps for UsbTablet { let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); self.usb_device .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; - let id = self.id.clone(); + let id = self.device_id().to_string(); let tablet = Arc::new(Mutex::new(self)); let tablet_adapter = Arc::new(Mutex::new(UsbTabletAdapter { tablet: tablet.clone(), @@ -186,7 +184,7 @@ impl UsbDeviceOps for UsbTablet { } fn unrealize(&mut self) -> Result<()> { - unregister_pointer(&self.id.clone()); + unregister_pointer(self.device_id()); Ok(()) } @@ -228,10 +226,6 @@ impl UsbDeviceOps for UsbTablet { self.hid.handle_data_packet(&mut locked_p); } - fn device_id(&self) -> String { - self.id.clone() - } - fn get_usb_device(&self) -> &UsbDevice { &self.usb_device } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index c1b0f0096..8ca6caa2b 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -335,7 +335,6 @@ impl IsoQueue { /// Abstract object of the host USB device. pub struct UsbHost { - id: String, config: UsbHostConfig, /// Libusb context. context: Context, @@ -368,8 +367,8 @@ impl UsbHost { context.set_log_level(rusb::LogLevel::Warning); let iso_urb_frames = config.iso_urb_frames; let iso_urb_count = config.iso_urb_count; + let id = config.id.clone().unwrap(); Ok(Self { - id: config.id.clone().unwrap(), config, context, libdev: None, @@ -378,7 +377,7 @@ impl UsbHost { libevt: Vec::new(), ifs_num: 0, ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], - usb_device: UsbDevice::new(USB_HOST_BUFFER_LEN), + usb_device: UsbDevice::new(id, USB_HOST_BUFFER_LEN), exit: None, requests: Arc::new(Mutex::new(LinkedList::new())), completed: Arc::new(Mutex::new(0)), @@ -596,7 +595,7 @@ impl UsbHost { usb_host.release_dev_to_host(); }) as Arc; self.exit = Some(exit_notifier.clone()); - TempCleaner::add_exit_notifier(self.id.clone(), exit_notifier); + TempCleaner::add_exit_notifier(self.device_id().to_string(), exit_notifier); } fn release_interfaces(&mut self) { @@ -721,7 +720,8 @@ impl UsbHost { self.handle.as_mut().unwrap().reset().unwrap_or_else(|e| { error!( "Failed to reset the handle of UsbHost device {}: {:?}", - self.id, e + self.device_id(), + e ) }); self.attach_kernel(); @@ -814,8 +814,9 @@ impl UsbHost { let ep = self .usb_device .get_endpoint(in_direction, locked_packet.ep_number); + let id = self.device_id().to_string(); match iso_queue.lock().unwrap().realize( - &self.id, + &id, self.handle.as_mut().unwrap(), self.iso_urb_count, self.iso_urb_frames, @@ -955,15 +956,15 @@ impl UsbDeviceOps for UsbHost { } fn unrealize(&mut self) -> Result<()> { - TempCleaner::remove_exit_notifier(&self.id); + TempCleaner::remove_exit_notifier(self.device_id()); self.release_dev_to_host(); unregister_event_helper(None, &mut self.libevt)?; - info!("Usb Host device {} is unrealized", self.id); + info!("Usb Host device {} is unrealized", self.device_id()); Ok(()) } fn reset(&mut self) { - info!("Usb Host device {} reset", self.id); + info!("Usb Host device {} reset", self.device_id()); if self.handle.is_none() { return; } @@ -1129,10 +1130,6 @@ impl UsbDeviceOps for UsbHost { self.submit_host_transfer(host_transfer, packet); } - fn device_id(&self) -> String { - self.id.clone() - } - fn get_usb_device(&self) -> &UsbDevice { &self.usb_device } -- Gitee From 8b85b3f509a36d3dab6d627b71f8d59634f32c7e Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 26 Jul 2023 10:18:07 +0800 Subject: [PATCH 1235/2187] usb: add serial number for usb device Add serial number for usb device to distinguish different devices. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 7 +++++-- devices/src/usb/keyboard.rs | 4 +++- devices/src/usb/mod.rs | 4 ++++ devices/src/usb/storage.rs | 6 ++++-- devices/src/usb/tablet.rs | 4 +++- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 34e2a325a..57f89f37d 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -128,7 +128,7 @@ const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ "", "StratoVirt", "USB Camera", - "1", + "4", "USB Camera Configuration", "StratoVirt Camera", "Video Control", @@ -850,7 +850,10 @@ impl UsbDeviceOps for UsbCamera { self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_SUPER; - let s = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); + let mut s: Vec = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); + let prefix = &s[UsbCameraStringIDs::SerialNumber as usize]; + s[UsbCameraStringIDs::SerialNumber as usize] = + self.usb_device.generate_serial_number(prefix); let device_desc = gen_desc_device_camera(fmt_list)?; self.usb_device.init_descriptor(device_desc, s)?; self.register_cb(); diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 9e6f2bbd3..37cefbb0c 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -175,7 +175,9 @@ impl UsbDeviceOps for UsbKeyboard { fn realize(mut self) -> Result>> { self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_FULL; - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let prefix = &s[STR_SERIAL_KEYBOARD_INDEX as usize]; + s[STR_SERIAL_KEYBOARD_INDEX as usize] = self.usb_device.generate_serial_number(prefix); self.usb_device .init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; let id = self.device_id().to_string(); diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 1ad6d90f2..e31a6f088 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -190,6 +190,10 @@ impl UsbDevice { } } + pub fn generate_serial_number(&self, prefix: &str) -> String { + format!("{}-{}", prefix, self.id) + } + /// Handle USB control request which is for descriptor. /// /// # Arguments diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 71e71a2d1..114f74a3d 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -129,7 +129,7 @@ const DESC_STRINGS: [&str; 7] = [ "", "StratoVirt", "StratoVirt USB Storage", - "1", + "3", "Full speed config (usb 1.1)", "High speed config (usb 2.0)", "Super speed config (usb 3.0)", @@ -513,7 +513,9 @@ impl UsbDeviceOps for UsbStorage { fn realize(mut self) -> Result>> { self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_HIGH; - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let prefix = &s[STR_SERIAL_STORAGE_INDEX as usize]; + s[STR_SERIAL_STORAGE_INDEX as usize] = self.usb_device.generate_serial_number(prefix); self.usb_device .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 4bd8640bf..97a58bdb8 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -171,7 +171,9 @@ impl UsbDeviceOps for UsbTablet { fn realize(mut self) -> Result>> { self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_FULL; - let s = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let prefix = &s[STR_SERIAL_TABLET_INDEX as usize]; + s[STR_SERIAL_TABLET_INDEX as usize] = self.usb_device.generate_serial_number(prefix); self.usb_device .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; let id = self.device_id().to_string(); -- Gitee From 66a5b78b6c6b655b26566dca9abb9a8d060ca600 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 25 Jul 2023 19:48:46 +0800 Subject: [PATCH 1236/2187] camera: add id in iad The id is added so that the driver can display different camera names. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 8 +++++++- devices/src/usb/descriptor.rs | 8 ++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 57f89f37d..1b41678ea 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -130,7 +130,7 @@ const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ "USB Camera", "4", "USB Camera Configuration", - "StratoVirt Camera", + "STRATO CAM", "Video Control", "Input Terminal", "Output Terminal", @@ -841,6 +841,10 @@ impl UsbCamera { .unwrap_or_default(); self.vs_control.reset(&default_fmt); } + + fn generate_iad(&self, prefix: &str) -> String { + format!("{} ({})", prefix, self.device_id()) + } } impl UsbDeviceOps for UsbCamera { @@ -854,6 +858,8 @@ impl UsbDeviceOps for UsbCamera { let prefix = &s[UsbCameraStringIDs::SerialNumber as usize]; s[UsbCameraStringIDs::SerialNumber as usize] = self.usb_device.generate_serial_number(prefix); + let iad = &s[UsbCameraStringIDs::Iad as usize]; + s[UsbCameraStringIDs::Iad as usize] = self.generate_iad(iad); let device_desc = gen_desc_device_camera(fmt_list)?; self.usb_device.init_descriptor(device_desc, s)?; self.register_cb(); diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 1376b6773..9cbe8cebf 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -22,6 +22,9 @@ use super::UsbDevice; pub const USB_MAX_INTERFACES: u32 = 16; const USB_DESCRIPTOR_TYPE_SHIFT: u32 = 8; const USB_DESCRIPTOR_INDEX_MASK: u32 = 0xff; +// The max length of the string descriptor is 255. +// And the header occupies 2 bytes, and each character occupies 2 bytes. +const USB_STRING_MAX_LEN: usize = 126; /// USB device descriptor for transfer #[allow(non_snake_case)] @@ -361,13 +364,14 @@ impl UsbDescriptor { .strings .get(index as usize) .with_context(|| format!("String descriptor index {} is invalid", index))?; - let len = found_str.len() as u8 * 2 + 2; + let str_max_len = std::cmp::min(USB_STRING_MAX_LEN, found_str.len()); + let len = str_max_len as u8 * 2 + 2; let mut vec = vec![0_u8; len as usize]; vec[0] = len; vec[1] = USB_DT_STRING; let mut pos = 2; - for i in 0..found_str.len() { + for i in 0..str_max_len { vec[pos] = found_str.as_bytes()[i]; vec[pos + 1] = 0; pos += 2; -- Gitee From f988a327359a9cd71ed56b43817047f5fcd0907b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 23 Jul 2023 04:27:29 +0800 Subject: [PATCH 1237/2187] Qcow2: modify test for qcow2 The file system of tmpfs does not support write zero. So, place the test file path from /tmp to ./ Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 72 ++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 5ae402652..0e0fe9a3f 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1977,40 +1977,44 @@ mod test { /// Newly allocated data is full of zero. #[test] fn test_alloc_cluster_with_zero() { - let path = "/tmp/alloc_cluster_with_zero.qcow2"; // Create a new image, with size = 16M, cluster_size = 64K. let image_bits = 24; let cluster_bits = 16; - let alloc_clusters: Vec = vec![1, 2, 4, 8, 16, 32]; - - for n_clusters in alloc_clusters { - let image = TestImage::new(path, image_bits, cluster_bits); - let (req_align, buf_align) = get_file_alignment(&image.file, true); - let conf = BlockProperty { - id: path.to_string(), - format: DiskFormat::Qcow2, - iothread: None, - direct: true, - req_align, - buf_align, - discard: true, - write_zeroes: WriteZeroesState::On, - l2_cache_size: None, - refcount_cache_size: None, - }; - let mut qcow2_driver = image.create_qcow2_driver(conf.clone()); - - assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); - assert!(qcow2_driver.discard(0, 1 << image_bits, ()).is_ok()); - - let times: u64 = (1 << (image_bits - cluster_bits)) / n_clusters; - for _time in 0..times { - let addr = qcow2_driver.alloc_cluster(n_clusters, true).unwrap(); - for i in 0..n_clusters { - let mut buf = vec![1_u8; qcow2_driver.header.cluster_size() as usize]; - let offset = addr + i * qcow2_driver.header.cluster_size(); - assert!(image.file.read_at(&mut buf, offset).is_ok()); - assert!(vec_is_zero(&buf)); + let paths = [ + "/tmp/alloc_cluster_with_zero.qcow2", + "./alloc_cluster_with_zero.qcow2", + ]; + for path in paths { + let alloc_clusters: Vec = vec![1, 2, 4, 8, 16, 32]; + for n_clusters in alloc_clusters { + let image = TestImage::new(path, image_bits, cluster_bits); + let (req_align, buf_align) = get_file_alignment(&image.file, true); + let conf = BlockProperty { + id: path.to_string(), + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align, + buf_align, + discard: true, + write_zeroes: WriteZeroesState::On, + l2_cache_size: None, + refcount_cache_size: None, + }; + let mut qcow2_driver = image.create_qcow2_driver(conf.clone()); + + assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); + assert!(qcow2_driver.discard(0, 1 << image_bits, ()).is_ok()); + + let times: u64 = (1 << (image_bits - cluster_bits)) / n_clusters; + for _time in 0..times { + let addr = qcow2_driver.alloc_cluster(n_clusters, true).unwrap(); + for i in 0..n_clusters { + let mut buf = vec![1_u8; qcow2_driver.header.cluster_size() as usize]; + let offset = addr + i * qcow2_driver.header.cluster_size(); + assert!(image.file.read_at(&mut buf, offset).is_ok()); + assert!(vec_is_zero(&buf)); + } } } } @@ -2025,7 +2029,7 @@ mod test { /// The size of disk space has been reduced. #[test] fn test_discard_basic() { - let path = "/tmp/discard_basic.qcow2"; + let path = "./discard_basic.qcow2"; // Create a new image, with size = 16M, cluster_size = 64K. let image_bits = 24; let cluster_bits = 16; @@ -2096,7 +2100,7 @@ mod test { /// The size of disk space has been reduced. #[test] fn test_snapshot_with_discard() { - let path = "/tmp/snapshot_with_discard.qcow2"; + let path = "./snapshot_with_discard.qcow2"; // Create a new image, with size = 1G, cluster_size = 64K. let image_bits = 24; let cluster_bits = 16; @@ -2167,7 +2171,7 @@ mod test { #[test] fn test_write_zero_basic() { // Create a new image, with size = 16M, cluster_size = 64K. - let path = "/tmp/discard_write_zero.qcow2"; + let path = "./discard_write_zero.qcow2"; let image_bits = 24; let cluster_bits = 16; let image = TestImage::new(path, image_bits, cluster_bits); -- Gitee From e1fc0861f1c7f3ad760cf8058d0d8e1e32eaedbe Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 23 Jul 2023 05:02:12 +0800 Subject: [PATCH 1238/2187] Qcow2: modify test_write_zero_basic Modify test_write_zero_basic, reduce duplicate code. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 103 +++++++++++++++------------------ 1 file changed, 47 insertions(+), 56 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 0e0fe9a3f..79eab12a2 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -2174,66 +2174,57 @@ mod test { let path = "./discard_write_zero.qcow2"; let image_bits = 24; let cluster_bits = 16; - let image = TestImage::new(path, image_bits, cluster_bits); - let conf = BlockProperty { - id: path.to_string(), - format: DiskFormat::Qcow2, - iothread: None, - direct: true, - req_align: 512, - buf_align: 512, - discard: true, - write_zeroes: WriteZeroesState::On, - l2_cache_size: None, - refcount_cache_size: None, - }; - let mut qcow2_driver = image.create_qcow2_driver(conf); - // Test 1. - let mut test_buf: Vec = vec![1_u8; 65536 * 6]; - let test_data = vec![ - TestData::new(0, 65536), - TestData::new(0, 65536 + 32768), - TestData::new(0, 65536 * 2), - TestData::new(0, 65536 + 32768), + // let test_data = vec![65536, 65536 + 32768, 65536 * 2, 65536 + 32768]; + let cluster_size: u64 = 1 << cluster_bits; + // (offset_begin, offset_end) + let test_data: Vec<(u64, u64)> = vec![ + (0, cluster_size * 5), + (cluster_size * 5, cluster_size * 10), + (cluster_size * 5 + 32768, cluster_size * 10), + (cluster_size * 5, cluster_size * 10 + 32768), + (cluster_size * 5, cluster_size * 5 + 32768), + (cluster_size * 5 + 32768, cluster_size * 5 + 32768), + (cluster_size * 5 + 32768, cluster_size * 5 + 49152), + (cluster_size * 5 + 32768, cluster_size * 6), + (cluster_size * 5 + 32768, cluster_size * 10 + 32768), + (0, 1 << image_bits), ]; - let offset_start = 0; - let mut guest_offset = offset_start; - assert!(qcow2_write(&mut qcow2_driver, &test_buf, offset_start).is_ok()); - for data in test_data.iter() { - assert!(qcow2_driver - .write_zeroes(guest_offset, data.sz as u64, (), true) - .is_ok()); - let mut tmp_buf = vec![1_u8; data.sz]; - assert!(qcow2_read(&mut qcow2_driver, &mut tmp_buf, guest_offset).is_ok()); - assert!(vec_is_zero(&tmp_buf)); - guest_offset += data.sz; - } - assert!(qcow2_read(&mut qcow2_driver, &mut test_buf, offset_start).is_ok()); - assert!(vec_is_zero(&test_buf)); + for (offset_start, offset_end) in test_data { + for discard in [true, false] { + let image = TestImage::new(path, image_bits, cluster_bits); + let conf = BlockProperty { + id: path.to_string(), + format: DiskFormat::Qcow2, + iothread: None, + direct: true, + req_align: 512, + buf_align: 512, + discard, + write_zeroes: WriteZeroesState::On, + l2_cache_size: None, + refcount_cache_size: None, + }; - // Test 2. - let mut test_buf: Vec = vec![1_u8; 65536 * 6]; - let test_data = vec![ - TestData::new(0, 65536), - TestData::new(0, 65536 + 32768), - TestData::new(0, 65536 * 2), - TestData::new(0, 65536 + 32768), - ]; - let offset_start = 459752; - let mut guest_offset = offset_start; - assert!(qcow2_write(&mut qcow2_driver, &test_buf, offset_start).is_ok()); - for data in test_data.iter() { - assert!(qcow2_driver - .write_zeroes(guest_offset, data.sz as u64, (), true) - .is_ok()); - let mut tmp_buf = vec![1_u8; data.sz]; - assert!(qcow2_read(&mut qcow2_driver, &mut tmp_buf, guest_offset).is_ok()); - assert!(vec_is_zero(&tmp_buf)); - guest_offset += data.sz; + let mut qcow2_driver = image.create_qcow2_driver(conf); + assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); + + assert!(qcow2_driver + .write_zeroes( + offset_start as usize, + offset_end - offset_start, + (), + discard + ) + .is_ok()); + + let mut read_buf = vec![1_u8; (offset_end - offset_start) as usize]; + assert!( + qcow2_read(&mut qcow2_driver, &mut read_buf, offset_start as usize).is_ok() + ); + assert!(vec_is_zero(&read_buf)); + } } - assert!(qcow2_read(&mut qcow2_driver, &mut test_buf, offset_start).is_ok()); - assert!(vec_is_zero(&test_buf)); } #[test] -- Gitee From 8cee6180a75943e612f4ff32813be02d7b87e860 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 26 Jul 2023 09:42:52 +0800 Subject: [PATCH 1239/2187] Qcow2: modify unit test for discard Optimize the calculation method for file size. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 72 +++++++++++++--------------------- 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 79eab12a2..bc2534558 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1479,8 +1479,10 @@ mod test { use std::{ fs::remove_file, io::{Seek, SeekFrom, Write}, - os::unix::{fs::OpenOptionsExt, prelude::FileExt}, - process::Command, + os::{ + linux::fs::MetadataExt, + unix::{fs::OpenOptionsExt, prelude::FileExt}, + }, }; use super::*; @@ -1595,6 +1597,12 @@ mod test { } Ok(()) } + + fn get_disk_size(&mut self) -> Result { + let meta_data = self.file.metadata()?; + let blk_size = meta_data.st_blocks() * DEFAULT_SECTOR_SIZE; + Ok(blk_size) + } } impl Drop for TestImage { @@ -1603,34 +1611,6 @@ mod test { } } - fn execute_cmd(cmd: String) -> Vec { - let args = cmd.split(' ').collect::>(); - if args.len() <= 0 { - return vec![]; - } - - let mut cmd_exe = Command::new(args[0]); - for i in 1..args.len() { - cmd_exe.arg(args[i]); - } - - let output = cmd_exe - .output() - .expect(format!("Failed to execute {}", cmd).as_str()); - println!("{:?}, output: {:?}", args, output); - assert!(output.status.success()); - output.stdout - } - - fn get_disk_size(img_path: String) -> u64 { - let out = execute_cmd(format!("du -shk {}", img_path)); - let str_out = std::str::from_utf8(&out) - .unwrap() - .split('\t') - .collect::>(); - str_out[0].parse::().unwrap() - } - fn vec_is_zero(vec: &[u8]) -> bool { for elem in vec { if elem != &0 { @@ -2064,7 +2044,7 @@ mod test { // Qcow2 driver will align the offset of requests according to the cluster size, // and then use the aligned interval for recying disk. for (offset_begin, offset_end) in test_data { - let image = TestImage::new(path, image_bits, cluster_bits); + let mut image = TestImage::new(path, image_bits, cluster_bits); let (req_align, buf_align) = get_file_alignment(&image.file, true); conf.req_align = req_align; conf.buf_align = buf_align; @@ -2076,16 +2056,17 @@ mod test { let expect_discard_space = if offset_end_align <= offset_begin_algn { 0 } else { - (offset_end_align - offset_begin_algn) / 1024 + offset_end_align - offset_begin_algn }; - let full_image_size = get_disk_size(path.to_string()); + let full_image_size = image.get_disk_size().unwrap(); assert!(qcow2_driver .discard(offset_begin as usize, offset_end - offset_begin, ()) .is_ok()); assert!(qcow2_driver.flush().is_ok()); - let discard_image_size = get_disk_size(path.to_string()); - assert_eq!(full_image_size, discard_image_size + expect_discard_space); + let discard_image_size = image.get_disk_size().unwrap(); + assert!(full_image_size < discard_image_size + expect_discard_space + cluster_size / 2); + assert!(full_image_size > discard_image_size + expect_discard_space - cluster_size / 2); // TODO: Check the metadata for qcow2 image. } } @@ -2104,7 +2085,8 @@ mod test { // Create a new image, with size = 1G, cluster_size = 64K. let image_bits = 24; let cluster_bits = 16; - let image = TestImage::new(path, image_bits, cluster_bits); + let cluster_size = 1 << cluster_bits; + let mut image = TestImage::new(path, image_bits, cluster_bits); let (req_align, buf_align) = get_file_alignment(&image.file, true); let conf = BlockProperty { id: path.to_string(), @@ -2122,7 +2104,7 @@ mod test { let mut qcow2_driver = image.create_qcow2_driver(conf); assert!(image.write_full_disk(&mut qcow2_driver, 1).is_ok()); - let disk_size_1 = get_disk_size(path.to_string()); + let disk_size_1 = image.get_disk_size().unwrap(); // Create a snapshot and write full disk again, which will result in copy on write. // Delete the snapshot, which will result in discard, and recycle disk size. assert!(qcow2_driver @@ -2130,7 +2112,7 @@ mod test { .is_ok()); assert!(image.write_full_disk(&mut qcow2_driver, 2).is_ok()); assert!(qcow2_driver.flush().is_ok()); - let disk_size_2 = get_disk_size(path.to_string()); + let disk_size_2 = image.get_disk_size().unwrap(); // Data cluster + 1 snapshot table + l1 table(cow) + l2 table(cow) // But, the cluster of snapshots may not be fully allocated assert!(disk_size_1 < disk_size_2); @@ -2140,7 +2122,7 @@ mod test { .is_ok()); assert!(image.write_full_disk(&mut qcow2_driver, 2).is_ok()); assert!(qcow2_driver.flush().is_ok()); - let disk_size_3 = get_disk_size(path.to_string()); + let disk_size_3 = image.get_disk_size().unwrap(); // Data cluster + l1 table(cow) + l2 table(cow) assert!(disk_size_2 < disk_size_3); @@ -2148,17 +2130,17 @@ mod test { assert!(qcow2_driver .delete_snapshot("test_snapshot_2".to_string()) .is_ok()); - let disk_size_4 = get_disk_size(path.to_string()); + let disk_size_4 = image.get_disk_size().unwrap(); // The actual size of the file should not exceed 1 cluster. - assert!(disk_size_4 > disk_size_2 - 32); - assert!(disk_size_4 < disk_size_2 + 32); + assert!(disk_size_4 > disk_size_2 - cluster_size / 2); + assert!(disk_size_4 < disk_size_2 + cluster_size / 2); assert!(qcow2_driver .delete_snapshot("test_snapshot_1".to_string()) .is_ok()); - let disk_size_5 = get_disk_size(path.to_string()); - assert!(disk_size_5 > disk_size_1 - 32); - assert!(disk_size_5 < disk_size_1 + 32); + let disk_size_5 = image.get_disk_size().unwrap(); + assert!(disk_size_5 > disk_size_1 - cluster_size / 2); + assert!(disk_size_5 < disk_size_1 + cluster_size / 2); } /// Test the basic functions of write zero. -- Gitee From a0379557bd40d012b36612fb0feb66c51d970e4b Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Mon, 24 Jul 2023 16:58:16 +0800 Subject: [PATCH 1240/2187] =?UTF-8?q?smbios:=20Add=20smbios=20table=202?= =?UTF-8?q?=E3=80=813=E3=80=814=E3=80=8116=E3=80=8117=E3=80=8132=20support?= =?UTF-8?q?=20SMBIOS=20type=20message:=20type=202-Baseboard=20infomation?= =?UTF-8?q?=E3=80=81type=203:=20System=20=20Enclosure=20infomation?= =?UTF-8?q?=E3=80=81=20type=204:=20Processor=20infomation=E3=80=81Type16-P?= =?UTF-8?q?hysical=20memory=20array=20informationtype=E3=80=81=20=2017:=20?= =?UTF-8?q?Memory=20Device=E3=80=81Type19:=20Memory=20device=20information?= =?UTF-8?q?=E3=80=81Type32-=20boot=20information?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jiewangqun --- docs/config_guidebook.md | 19 + machine/src/lib.rs | 15 +- machine/src/standard_vm/aarch64/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 17 +- machine_manager/src/cmdline.rs | 6 +- machine_manager/src/config/smbios.rs | 194 +++++++ smbios/src/smbios_table.rs | 735 ++++++++++++++++++++++++- tests/mod_test/tests/fwcfg_test.rs | 321 ++++++++++- 8 files changed, 1296 insertions(+), 13 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 57d905fcc..7c82b39e5 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -261,8 +261,27 @@ The SMBIOS specification defines the data structures and information that will e ```shell # cmdline +# type 0: BIOS infomation,Support Version and Release Date string -smbios type=0[,vendor=str][,version=str][,date=str] +# type 1: System infomation,The information in this structure defines attributes of +# the overall system and is intended to be associated with the Component ID group of the system’s MIF. -smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str] +# type 2: Baseboard infomation,the information in this structure defines attributes of a system baseboard +# (for example, a motherboard, planar, server blade, or other standard system module). +-smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str][,asset=str][,location=str] +# type 3: System Enclosure infomation,defines attributes of the system’s mechanical enclosure(s). +# For example, if a system included a separate enclosure for its peripheral devices, +# two structures would be returned: one for the main system enclosure and the second for the peripheral device enclosure. +-smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str] +# type 4: Processor infomation,defines the attributes of a single processor; +# a separate structure instance is provided for each system processor socket/slot. +# For example, a system with an IntelDX2 processor would have a single structure instance +# while a system with an IntelSX2 processor would have a structure to describe the main CPU +# and a second structure to describe the 80487 co-processor +-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,max-speed=%d][,current-speed=%d] +# type 17: Memory Device,this structure describes a single memory device. +-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str][,asset=str][,part=str][,speed=%d] + ``` ## 2. Device Configuration diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5562b3c97..369d86070 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -100,11 +100,20 @@ use virtio::{ }; pub trait MachineOps { - fn build_smbios(&self, fw_cfg: &Arc>) -> Result<()> { - let smbioscfg = self.get_vm_config().lock().unwrap().smbios.clone(); + fn build_smbios( + &self, + fw_cfg: &Arc>, + mem_array: Vec<(u64, u64)>, + ) -> Result<()> { + let vm_config = self.get_vm_config(); + let vmcfg_lock = vm_config.lock().unwrap(); let mut smbios = SmbiosTable::new(); - let table = smbios.build_smbios_tables(smbioscfg); + let table = smbios.build_smbios_tables( + vmcfg_lock.smbios.clone(), + &vmcfg_lock.machine_config, + mem_array, + ); let ep = build_smbios_ep30(table.len() as u32); let mut locked_fw_cfg = fw_cfg.lock().unwrap(); diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 38bfb2327..ee5d55d5c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -696,7 +696,7 @@ impl MachineOps for StdMachine { .build_acpi_tables(&fw_cfg) .with_context(|| "Failed to create ACPI tables")?; locked_vm - .build_smbios(&fw_cfg) + .build_smbios(&fw_cfg, Vec::new()) .with_context(|| "Failed to create smbios tables")?; } } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index d9b107e1f..17f0e3af9 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -519,8 +519,23 @@ impl MachineOps for StdMachine { locked_vm .build_acpi_tables(&fw_cfg) .with_context(|| "Failed to create ACPI tables")?; + let mut mem_array = Vec::new(); + let mem_size = vm_config.machine_config.mem_config.mem_size; + let below_size = + std::cmp::min(MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, mem_size); + mem_array.push(( + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, + below_size, + )); + if mem_size > below_size { + mem_array.push(( + MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0, + mem_size - below_size, + )); + } + locked_vm - .build_smbios(&fw_cfg) + .build_smbios(&fw_cfg, mem_array) .with_context(|| "Failed to create smbios tables")?; } } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index facb3de94..b13b4fcc2 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -483,7 +483,11 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .long("smbios") .value_name("") .help("\n\t\tadd type0 table: -smbios type=0[,vendor=str][,version=str][,date=str]; \ - \n\t\tadd type1 table: -smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str];") + \n\t\tadd type1 table: -smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str]; \ + \n\t\tadd type2 table: -smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str][,asset=str][,location=str]; \ + \n\t\tadd type3 table: -smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str]; \ + \n\t\tadd type4 table: -smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,max-speed=%d][,current-speed=%d]; \ + \n\t\tadd type17 table: -smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str][,asset=str][,part=str][,speed=%d]") .takes_values(true), ) } diff --git a/machine_manager/src/config/smbios.rs b/machine_manager/src/config/smbios.rs index 38f120489..1d229891f 100644 --- a/machine_manager/src/config/smbios.rs +++ b/machine_manager/src/config/smbios.rs @@ -36,10 +36,60 @@ pub struct SmbiosType1Config { pub added: bool, } +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosType2Config { + pub manufacturer: Option, + pub product: Option, + pub version: Option, + pub serial: Option, + pub asset: Option, + pub location: Option, + pub added: bool, +} + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosType3Config { + pub manufacturer: Option, + pub version: Option, + pub serial: Option, + pub sku: Option, + pub asset: Option, + pub added: bool, +} + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosType4Config { + pub manufacturer: Option, + pub version: Option, + pub serial: Option, + pub asset: Option, + pub sock_pfx: Option, + pub part: Option, + pub max_speed: Option, + pub current_speed: Option, + pub added: bool, +} + +#[derive(Clone, Default, Debug, Serialize, Deserialize)] +pub struct SmbiosType17Config { + pub manufacturer: Option, + pub serial: Option, + pub asset: Option, + pub loc_pfx: Option, + pub part: Option, + pub speed: u16, + pub bank: Option, + pub added: bool, +} + #[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct SmbiosConfig { pub type0: SmbiosType0Config, pub type1: SmbiosType1Config, + pub type2: SmbiosType2Config, + pub type3: SmbiosType3Config, + pub type4: SmbiosType4Config, + pub type17: SmbiosType17Config, } /// Check if the uuid is valid. @@ -157,6 +207,138 @@ impl VmConfig { Ok(()) } + /// # Arguments + /// + /// * `type2` - The type2 cmdline string. + fn add_smbios_type2(&mut self, type2: &str) -> Result<()> { + if self.smbios.type2.added { + bail!("smbios type2 has been added"); + } + + let mut cmd_parser = CmdParser::new("smbios"); + cmd_parser + .push("") + .push("type") + .push("manufacturer") + .push("product") + .push("version") + .push("serial") + .push("asset") + .push("location"); + cmd_parser.parse(type2)?; + + self.smbios.type2.manufacturer = cmd_parser.get_value::("manufacturer")?; + self.smbios.type2.product = cmd_parser.get_value::("product")?; + self.smbios.type2.version = cmd_parser.get_value::("version")?; + self.smbios.type2.serial = cmd_parser.get_value::("serial")?; + self.smbios.type2.asset = cmd_parser.get_value::("asset")?; + self.smbios.type2.location = cmd_parser.get_value::("location")?; + self.smbios.type2.added = true; + + Ok(()) + } + + /// # Arguments + /// + /// * `type3` - The type3 cmdline string. + fn add_smbios_type3(&mut self, type3: &str) -> Result<()> { + if self.smbios.type3.added { + bail!("smbios type3 has been added"); + } + + let mut cmd_parser = CmdParser::new("smbios"); + cmd_parser + .push("") + .push("type") + .push("manufacturer") + .push("version") + .push("serial") + .push("sku") + .push("asset"); + cmd_parser.parse(type3)?; + + self.smbios.type3.manufacturer = cmd_parser.get_value::("manufacturer")?; + self.smbios.type3.version = cmd_parser.get_value::("version")?; + self.smbios.type3.serial = cmd_parser.get_value::("serial")?; + self.smbios.type3.sku = cmd_parser.get_value::("sku")?; + self.smbios.type3.asset = cmd_parser.get_value::("asset")?; + self.smbios.type3.added = true; + + Ok(()) + } + + /// # Arguments + /// + /// * `type4` - The type4 cmdline string. + fn add_smbios_type4(&mut self, type4: &str) -> Result<()> { + if self.smbios.type4.added { + bail!("smbios type4 has been added"); + } + + let mut cmd_parser = CmdParser::new("smbios"); + cmd_parser + .push("") + .push("type") + .push("manufacturer") + .push("version") + .push("serial") + .push("sock_pfx") + .push("max-speed") + .push("current-speed") + .push("part") + .push("asset"); + cmd_parser.parse(type4)?; + + self.smbios.type4.manufacturer = cmd_parser.get_value::("manufacturer")?; + self.smbios.type4.version = cmd_parser.get_value::("version")?; + self.smbios.type4.serial = cmd_parser.get_value::("serial")?; + self.smbios.type4.asset = cmd_parser.get_value::("asset")?; + self.smbios.type4.part = cmd_parser.get_value::("part")?; + self.smbios.type4.sock_pfx = cmd_parser.get_value::("sock_pfx")?; + self.smbios.type4.max_speed = cmd_parser.get_value::("max-speed")?; + self.smbios.type4.current_speed = cmd_parser.get_value::("current-speed")?; + self.smbios.type4.added = true; + + Ok(()) + } + + /// # Arguments + /// + /// * `type17` - The type17 cmdline string. + fn add_smbios_type17(&mut self, type17: &str) -> Result<()> { + if self.smbios.type17.added { + bail!("smbios type17 has been added"); + } + + let mut cmd_parser = CmdParser::new("smbios"); + cmd_parser + .push("") + .push("type") + .push("loc_pfx") + .push("bank") + .push("manufacturer") + .push("serial") + .push("speed") + .push("part") + .push("asset"); + cmd_parser.parse(type17)?; + + self.smbios.type17.manufacturer = cmd_parser.get_value::("manufacturer")?; + self.smbios.type17.loc_pfx = cmd_parser.get_value::("loc_pfx")?; + self.smbios.type17.serial = cmd_parser.get_value::("serial")?; + self.smbios.type17.asset = cmd_parser.get_value::("asset")?; + self.smbios.type17.part = cmd_parser.get_value::("part")?; + self.smbios.type17.speed = if let Some(speed) = cmd_parser.get_value::("speed")? { + speed + } else { + 0 + }; + self.smbios.type17.bank = cmd_parser.get_value::("bank")?; + self.smbios.type17.added = true; + + Ok(()) + } + /// Add argument `smbios_args` to `VmConfig`. /// /// # Arguments @@ -177,6 +359,18 @@ impl VmConfig { "1" => { self.add_smbios_type1(smbios_args)?; } + "2" => { + self.add_smbios_type2(smbios_args)?; + } + "3" => { + self.add_smbios_type3(smbios_args)?; + } + "4" => { + self.add_smbios_type4(smbios_args)?; + } + "17" => { + self.add_smbios_type17(smbios_args)?; + } _ => { bail!("Unknow smbios type: {:?}", &smbios_type); } diff --git a/smbios/src/smbios_table.rs b/smbios/src/smbios_table.rs index c08b48700..c87308f6d 100644 --- a/smbios/src/smbios_table.rs +++ b/smbios/src/smbios_table.rs @@ -10,14 +10,28 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use machine_manager::config::{SmbiosConfig, SmbiosType0Config, SmbiosType1Config}; +use machine_manager::config::{ + MachineConfig, SmbiosConfig, SmbiosType0Config, SmbiosType17Config, SmbiosType1Config, + SmbiosType2Config, SmbiosType3Config, SmbiosType4Config, +}; use std::mem::size_of; use util::byte_code::ByteCode; const TYPE0_HANDLE: u16 = 0x0; const TYPE1_HANDLE: u16 = 0x100; +const TYPE2_HANDLE: u16 = 0x200; +const TYPE3_HANDLE: u16 = 0x300; +const TYPE4_HANDLE: u16 = 0x400; +const TYPE16_HANDLE: u16 = 0x1000; +const TYPE17_HANDLE: u16 = 0x1100; +const TYPE19_HANDLE: u16 = 0x1300; +const TYPE32_HANDLE: u16 = 0x2000; const TYPE127_HANDLE: u16 = 0x7F00; +const GB_SIZE: u64 = 1_u64 << 30; +const KB_2T_SIZE: u32 = 0x80000000; +const HYPERVISOR_STR: &str = "StratoVirt"; + #[repr(C, packed)] #[derive(Default, Copy, Clone)] struct SmbiosHeader { @@ -159,6 +173,437 @@ impl SmbiosType1Table { } } +/// Type2: Baseboard information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType2 { + header: SmbiosHeader, + manufacturer: u8, + product_name: u8, + version: u8, + serial_num: u8, + asset_tag_num: u8, + feature_flags: u8, + location: u8, + chassis_handle: [u8; 2], + board_type: u8, + contained_element_count: u8, +} + +impl ByteCode for SmbiosType2 {} + +impl SmbiosType2 { + pub fn new() -> SmbiosType2 { + SmbiosType2 { + header: SmbiosHeader::new(2_u8, size_of::() as u8, TYPE2_HANDLE), + feature_flags: 1_u8, + chassis_handle: 0x300_u16.to_le_bytes(), + board_type: 0x0A_u8, + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType2Table { + header: SmbiosType2, + body: Vec, + str_index: u8, +} + +impl SmbiosType2Table { + pub fn new() -> SmbiosType2Table { + SmbiosType2Table { + header: SmbiosType2::new(), + body: Vec::new(), + str_index: 0_u8, + } + } + + pub fn set_str(&mut self, str: String) { + self.str_index += 1; + self.body.append(&mut str.as_bytes().to_vec()); + self.body.append(&mut vec![0]); + } + + pub fn finish(&mut self) { + if self.str_index == 0 { + self.body.append(&mut vec![0; 2]); + } else { + self.body.append(&mut vec![0]); + } + } +} + +/// Type3: System enclosure information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType3 { + header: SmbiosHeader, + manufacturer: u8, + type_id: u8, + version: u8, + serial_num: u8, + asset_tag_num: u8, + boot_up_state: u8, + power_supply_state: u8, + thermal_state: u8, + security_status: u8, + oem_defined: [u8; 4], + height: u8, + number_of_power_cords: u8, + contained_element_count: u8, + contained_element_record_length: u8, + sku_num: u8, +} + +impl ByteCode for SmbiosType3 {} + +impl SmbiosType3 { + pub fn new() -> SmbiosType3 { + SmbiosType3 { + header: SmbiosHeader::new(3_u8, size_of::() as u8, TYPE3_HANDLE), + type_id: 0x1_u8, + boot_up_state: 0x03_u8, + power_supply_state: 0x03_u8, + thermal_state: 0x03_u8, + security_status: 0x02_u8, + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType3Table { + header: SmbiosType3, + body: Vec, + str_index: u8, +} + +impl SmbiosType3Table { + pub fn new() -> SmbiosType3Table { + SmbiosType3Table { + header: SmbiosType3::new(), + body: Vec::new(), + str_index: 0_u8, + } + } + + pub fn set_str(&mut self, str: String) { + self.str_index += 1; + self.body.append(&mut str.as_bytes().to_vec()); + self.body.append(&mut vec![0]); + } + + pub fn finish(&mut self) { + if self.str_index == 0 { + self.body.append(&mut vec![0; 2]); + } else { + self.body.append(&mut vec![0]); + } + } +} + +/// Type4: Processor information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType4 { + header: SmbiosHeader, + socket_design: u8, + processor_type: u8, + processor_family: u8, + processor_manufacturer: u8, + processor_id0: [u8; 4], + processor_id1: [u8; 4], + processor_version: u8, + voltage: u8, + external_clock: [u8; 2], + max_speed: [u8; 2], + current_speed: [u8; 2], + status: u8, + processor_upgrade: u8, + l1_cache_handle: [u8; 2], + l2_cache_handle: [u8; 2], + l3_cache_handle: [u8; 2], + serial_num: u8, + asset_tag_num: u8, + part_num: u8, + core_count: u8, + core_enabled: u8, + thread_count: u8, + processor_characteristics: [u8; 2], + processor_family2: [u8; 2], + core_count2: [u8; 2], + core_enabled2: [u8; 2], + thread_count2: [u8; 2], +} + +impl ByteCode for SmbiosType4 {} + +impl SmbiosType4 { + pub fn new(instance: u16) -> SmbiosType4 { + SmbiosType4 { + header: SmbiosHeader::new( + 4_u8, + size_of::() as u8, + TYPE4_HANDLE + instance, + ), + processor_type: 0x03_u8, + processor_family: 0x01_u8, + status: 0x41_u8, + processor_upgrade: 0x01_u8, + l1_cache_handle: 0xFFFF_u16.to_le_bytes(), + l2_cache_handle: 0xFFFF_u16.to_le_bytes(), + l3_cache_handle: 0xFFFF_u16.to_le_bytes(), + processor_characteristics: 0x02_u16.to_le_bytes(), + processor_family2: 0x01_u16.to_le_bytes(), + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType4Table { + header: SmbiosType4, + body: Vec, + str_index: u8, +} + +impl SmbiosType4Table { + pub fn new(instance: u16) -> SmbiosType4Table { + SmbiosType4Table { + header: SmbiosType4::new(instance), + body: Vec::new(), + str_index: 0_u8, + } + } + + pub fn set_str(&mut self, str: String) { + self.str_index += 1; + self.body.append(&mut str.as_bytes().to_vec()); + self.body.append(&mut vec![0]); + } + + pub fn finish(&mut self) { + if self.str_index == 0 { + self.body.append(&mut vec![0; 2]); + } else { + self.body.append(&mut vec![0]); + } + } +} + +/// Type16: Physical memory array information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType16 { + header: SmbiosHeader, + location: u8, + used: u8, + error_correction: u8, + maximum_capacity: [u8; 4], + memory_error_information_handle: [u8; 2], + number_of_memory_devices: [u8; 2], + extended_maximum_capacity: [u8; 8], +} + +impl ByteCode for SmbiosType16 {} + +impl SmbiosType16 { + pub fn new(cnt: u16) -> SmbiosType16 { + SmbiosType16 { + header: SmbiosHeader::new(16_u8, size_of::() as u8, TYPE16_HANDLE), + location: 0x01, + used: 0x03, + error_correction: 0x06, + memory_error_information_handle: 0xFFFE_u16.to_le_bytes(), + number_of_memory_devices: cnt.to_le_bytes(), + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType16Table { + header: SmbiosType16, + body: Vec, +} + +impl SmbiosType16Table { + pub fn new(cnt: u16) -> SmbiosType16Table { + SmbiosType16Table { + header: SmbiosType16::new(cnt), + body: Vec::new(), + } + } + + pub fn finish(&mut self) { + self.body.append(&mut vec![0; 2]); + } +} + +/// Type17: memory device +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType17 { + header: SmbiosHeader, + physical_memory_array_handle: [u8; 2], + memory_error_information_handle: [u8; 2], + total_width: [u8; 2], + data_width: [u8; 2], + size: [u8; 2], + form_factor: u8, + device_set: u8, + device_locator_str: u8, + bank_locator_str: u8, + memory_type: u8, + type_detail: [u8; 2], + speed: [u8; 2], + manufacturer_str: u8, + serial_number_str: u8, + asset_tag_number_str: u8, + part_number_str: u8, + attributes: u8, + extended_size: [u8; 4], + configured_clock_speed: [u8; 2], + minimum_voltage: [u8; 2], + maximum_voltage: [u8; 2], + configured_voltage: [u8; 2], +} + +impl ByteCode for SmbiosType17 {} + +impl SmbiosType17 { + pub fn new(ins: u16) -> SmbiosType17 { + SmbiosType17 { + header: SmbiosHeader::new(17_u8, size_of::() as u8, TYPE17_HANDLE + ins), + physical_memory_array_handle: 0x1000_u16.to_le_bytes(), + memory_error_information_handle: 0xFFFE_u16.to_le_bytes(), + total_width: 0xFFFF_u16.to_le_bytes(), + data_width: 0xFFFF_u16.to_le_bytes(), + form_factor: 0x09, + memory_type: 0x07, + type_detail: 0x02_u16.to_le_bytes(), + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType17Table { + header: SmbiosType17, + body: Vec, + str_index: u8, +} + +impl SmbiosType17Table { + pub fn new(ins: u16) -> SmbiosType17Table { + SmbiosType17Table { + header: SmbiosType17::new(ins), + body: Vec::new(), + str_index: 0_u8, + } + } + + pub fn set_str(&mut self, str: String) { + self.str_index += 1; + self.body.append(&mut str.as_bytes().to_vec()); + self.body.append(&mut vec![0]); + } + + pub fn finish(&mut self) { + if self.str_index == 0 { + self.body.append(&mut vec![0; 2]); + } else { + self.body.append(&mut vec![0]); + } + } +} + +/// Type19: Memory device information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType19 { + header: SmbiosHeader, + starting_address: [u8; 4], + ending_address: [u8; 4], + memory_array_handle: [u8; 2], + partition_width: u8, + extended_starting_address: [u8; 8], + extended_ending_address: [u8; 8], +} + +impl ByteCode for SmbiosType19 {} + +impl SmbiosType19 { + pub fn new(ins: u16) -> SmbiosType19 { + SmbiosType19 { + header: SmbiosHeader::new(19_u8, size_of::() as u8, TYPE19_HANDLE + ins), + memory_array_handle: 0x1000_u16.to_le_bytes(), + partition_width: 1, + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType19Table { + header: SmbiosType19, + body: Vec, +} + +impl SmbiosType19Table { + pub fn new(ins: u16) -> SmbiosType19Table { + SmbiosType19Table { + header: SmbiosType19::new(ins), + body: Vec::new(), + } + } + + pub fn finish(&mut self) { + self.body.append(&mut vec![0; 2]); + } +} + +/// Type32: boot information +#[repr(C, packed)] +#[derive(Default, Copy, Clone)] +struct SmbiosType32 { + header: SmbiosHeader, + reserved: [u8; 6], + boot_status: u8, +} + +impl ByteCode for SmbiosType32 {} + +impl SmbiosType32 { + pub fn new() -> SmbiosType32 { + SmbiosType32 { + header: SmbiosHeader::new(32_u8, size_of::() as u8, TYPE32_HANDLE), + ..Default::default() + } + } +} + +#[derive(Default, Clone)] +struct SmbiosType32Table { + header: SmbiosType32, + body: Vec, +} + +impl SmbiosType32Table { + pub fn new() -> SmbiosType32Table { + SmbiosType32Table { + header: SmbiosType32::new(), + body: Vec::new(), + } + } + + pub fn finish(&mut self) { + self.body.append(&mut vec![0; 2]); + } +} + /// Type127: End of table #[repr(C, packed)] #[derive(Default, Copy, Clone)] @@ -237,19 +682,21 @@ impl SmbiosTable { if let Some(manufacturer) = type1.manufacturer { table1.set_str(manufacturer); } else { - table1.set_str(String::from("Stratovirt")); + table1.set_str(String::from(HYPERVISOR_STR)); } table1.header.product_name = table1.str_index + 1; if let Some(product) = type1.product { table1.set_str(product); } else { - table1.set_str(String::from("Virtual Machine")); + table1.set_str(String::from(HYPERVISOR_STR)); } + table1.header.version = table1.str_index + 1; if let Some(version) = type1.version { - table1.header.version = table1.str_index + 1; table1.set_str(version); + } else { + table1.set_str(String::from(HYPERVISOR_STR)); } if let Some(serial) = type1.serial { @@ -278,6 +725,251 @@ impl SmbiosTable { self.entries.append(&mut table1.body); } + fn build_type2(&mut self, type2: SmbiosType2Config) { + if !type2.added { + return; + } + let mut table2 = SmbiosType2Table::new(); + + table2.header.manufacturer = table2.str_index + 1; + if let Some(manufacturer) = type2.manufacturer { + table2.set_str(manufacturer); + } else { + table2.set_str(String::from(HYPERVISOR_STR)); + } + + table2.header.product_name = table2.str_index + 1; + if let Some(product) = type2.product { + table2.set_str(product); + } else { + table2.set_str(String::from("Virtual Machine")); + } + + table2.header.version = table2.str_index + 1; + if let Some(version) = type2.version { + table2.set_str(version); + } else { + table2.set_str(String::from(HYPERVISOR_STR)); + } + + if let Some(serial) = type2.serial { + table2.header.serial_num = table2.str_index + 1; + table2.set_str(serial); + } + + if let Some(location) = type2.location { + table2.header.location = table2.str_index + 1; + table2.set_str(location); + } + + if let Some(asset) = type2.asset { + table2.header.asset_tag_num = table2.str_index + 1; + table2.set_str(asset); + } + + table2.finish(); + + self.entries.append(&mut table2.header.as_bytes().to_vec()); + self.entries.append(&mut table2.body); + } + + fn build_type3(&mut self, type3: SmbiosType3Config) { + let mut table3 = SmbiosType3Table::new(); + + table3.header.manufacturer = table3.str_index + 1; + if let Some(manufacturer) = type3.manufacturer { + table3.set_str(manufacturer); + } else { + table3.set_str(String::from(HYPERVISOR_STR)); + } + + table3.header.version = table3.str_index + 1; + if let Some(version) = type3.version { + table3.set_str(version); + } else { + table3.set_str(String::from(HYPERVISOR_STR)); + } + + if let Some(serial) = type3.serial { + table3.header.serial_num = table3.str_index + 1; + table3.set_str(serial); + } + + if let Some(sku) = type3.sku { + table3.header.sku_num = table3.str_index + 1; + table3.set_str(sku); + } + + if let Some(asset) = type3.asset { + table3.header.asset_tag_num = table3.str_index + 1; + table3.set_str(asset); + } + + table3.finish(); + + self.entries.append(&mut table3.header.as_bytes().to_vec()); + self.entries.append(&mut table3.body); + } + + fn build_type4(&mut self, type4: SmbiosType4Config, instance: u16, mach_cfg: &MachineConfig) { + let mut table4 = SmbiosType4Table::new(instance); + + table4.header.socket_design = table4.str_index + 1; + if let Some(sock_str) = type4.sock_pfx { + table4.set_str(std::format!("{}{:2x}", sock_str, instance)); + } else { + table4.set_str(std::format!("CPU{:2x}", instance)); + } + + table4.header.processor_manufacturer = table4.str_index + 1; + if let Some(manufacturer) = type4.manufacturer { + table4.set_str(manufacturer); + } else { + table4.set_str(String::from(HYPERVISOR_STR)); + } + + table4.header.processor_version = table4.str_index + 1; + if let Some(version) = type4.version { + table4.set_str(version); + } else { + table4.set_str(String::from(HYPERVISOR_STR)); + } + + if let Some(serial) = type4.serial { + table4.header.serial_num = table4.str_index + 1; + table4.set_str(serial); + } + + if let Some(asset) = type4.asset { + table4.header.asset_tag_num = table4.str_index + 1; + table4.set_str(asset); + } + + if let Some(part) = type4.part { + table4.header.part_num = table4.str_index + 1; + table4.set_str(part); + } + + if let Some(max_speed) = type4.max_speed { + table4.header.max_speed = (max_speed as u16).to_le_bytes(); + } else { + table4.header.max_speed = 2000_u16.to_le_bytes(); + } + + if let Some(current_speed) = type4.current_speed { + table4.header.current_speed = (current_speed as u16).to_le_bytes(); + } else { + table4.header.current_speed = 2000_u16.to_le_bytes(); + } + + table4.header.core_count = mach_cfg.nr_cores; + table4.header.core_enabled = mach_cfg.nr_cores; + + table4.header.core_count2 = (mach_cfg.nr_cores as u16).to_le_bytes(); + table4.header.core_enabled2 = (mach_cfg.nr_cores as u16).to_le_bytes(); + + table4.header.thread_count = mach_cfg.nr_threads; + table4.header.thread_count2 = (mach_cfg.nr_threads as u16).to_le_bytes(); + table4.finish(); + + self.entries.append(&mut table4.header.as_bytes().to_vec()); + self.entries.append(&mut table4.body); + } + + fn build_type16(&mut self, size: u64, number_device: u16) { + let mut table16 = SmbiosType16Table::new(1); + + let size_kb = (size / 1024) as u32; + if size_kb < KB_2T_SIZE { + table16.header.maximum_capacity = size_kb.to_le_bytes(); + } else { + table16.header.maximum_capacity = KB_2T_SIZE.to_le_bytes(); + table16.header.extended_maximum_capacity = size.to_le_bytes(); + } + table16.header.number_of_memory_devices = number_device.to_le_bytes(); + table16.finish(); + + self.entries.append(&mut table16.header.as_bytes().to_vec()); + self.entries.append(&mut table16.body); + } + + fn build_type17(&mut self, type17: SmbiosType17Config, ins: u16, size: u64) { + let mut table17 = SmbiosType17Table::new(ins); + + let size_mb = (size / 1024 / 1024) as u16; + table17.header.size = size_mb.to_le_bytes(); + + table17.header.manufacturer_str = table17.str_index + 1; + if let Some(manufacturer) = type17.manufacturer { + table17.set_str(manufacturer); + } else { + table17.set_str(String::from(HYPERVISOR_STR)); + } + table17.header.device_locator_str = table17.str_index + 1; + if let Some(loc_pfx) = type17.loc_pfx { + table17.set_str(std::format!("{} {}", loc_pfx, ins)); + } else { + table17.set_str(std::format!("DIMM {}", ins)); + } + + if let Some(bank) = type17.bank { + table17.header.bank_locator_str = table17.str_index + 1; + table17.set_str(bank); + } + + if let Some(serial) = type17.serial { + table17.header.serial_number_str = table17.str_index + 1; + table17.set_str(serial); + } + + if let Some(part) = type17.part { + table17.header.part_number_str = table17.str_index + 1; + table17.set_str(part); + } + + if let Some(asset) = type17.asset { + table17.header.asset_tag_number_str = table17.str_index + 1; + table17.set_str(asset); + } + table17.header.speed = type17.speed.to_le_bytes(); + table17.header.configured_clock_speed = type17.speed.to_le_bytes(); + table17.finish(); + + self.entries.append(&mut table17.header.as_bytes().to_vec()); + self.entries.append(&mut table17.body); + } + + fn build_type19(&mut self, ins: u16, start: u64, size: u64) { + let mut table19 = SmbiosType19Table::new(ins); + + let start_kb = start / 1024; + let end_kb = (start + size - 1) / 1024; + + if start_kb < u32::MAX as u64 && end_kb < u32::MAX as u64 { + table19.header.starting_address = (start_kb as u32).to_le_bytes(); + table19.header.ending_address = (end_kb as u32).to_le_bytes(); + } else { + table19.header.starting_address = u32::MAX.to_le_bytes(); + table19.header.ending_address = u32::MAX.to_le_bytes(); + table19.header.extended_starting_address = start.to_le_bytes(); + table19.header.extended_ending_address = (start + size - 1).to_le_bytes(); + } + + table19.finish(); + + self.entries.append(&mut table19.header.as_bytes().to_vec()); + self.entries.append(&mut table19.body); + } + + fn build_type32(&mut self) { + let mut table32 = SmbiosType32Table::new(); + + table32.finish(); + + self.entries.append(&mut table32.header.as_bytes().to_vec()); + self.entries.append(&mut table32.body); + } + fn build_type127(&mut self) { let mut table127 = SmbiosType127Table::new(); @@ -288,9 +980,42 @@ impl SmbiosTable { self.entries.append(&mut table127.body); } - pub fn build_smbios_tables(&mut self, smbios: SmbiosConfig) -> Vec { + pub fn build_smbios_tables( + &mut self, + smbios: SmbiosConfig, + mach_cfg: &MachineConfig, + mem_arr: Vec<(u64, u64)>, + ) -> Vec { self.build_type0(smbios.type0); self.build_type1(smbios.type1); + self.build_type2(smbios.type2); + self.build_type3(smbios.type3); + + let smbios_sockets = mach_cfg.nr_cpus / (mach_cfg.nr_cores * mach_cfg.nr_threads); + for i in 0..smbios_sockets { + self.build_type4(smbios.type4.clone(), i as u16, mach_cfg); + } + let mem_num = ((mach_cfg.mem_config.mem_size + 16 * GB_SIZE - 1) / (16 * GB_SIZE)) as u16; + self.build_type16(mach_cfg.mem_config.mem_size, mem_num); + + for i in 0..mem_num { + let memdev_size = if i < mem_num - 1 { + 16 * GB_SIZE + } else { + (mach_cfg.mem_config.mem_size - 1) % (16 * GB_SIZE) + 1 + }; + self.build_type17(smbios.type17.clone(), i, memdev_size); + } + + let offset = if mem_num > (TYPE19_HANDLE - TYPE17_HANDLE) { + mem_num - (TYPE19_HANDLE - TYPE17_HANDLE) + } else { + 0_u16 + }; + for (index, (start, size)) in mem_arr.iter().enumerate() { + self.build_type19(offset + index as u16, *start, *size); + } + self.build_type32(); self.build_type127(); self.entries.clone() diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index 977e4c6b5..46500bba1 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -257,7 +257,7 @@ fn test_smbios_type0() { assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); assert_eq!(read_data[6], 24 as u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); - assert_eq!(talble_len, 109); + assert_eq!(talble_len, 339); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); let talbles_size = test_state.borrow().fw_cfg_read_file( @@ -321,7 +321,7 @@ fn test_smbios_type1() { assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); assert_eq!(read_data[6], 24 as u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); - assert_eq!(talble_len, 162); + assert_eq!(talble_len, 381); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); let talbles_size = test_state.borrow().fw_cfg_read_file( @@ -388,6 +388,323 @@ fn test_smbios_type1() { test_state.borrow_mut().stop(); } +/// smbios table2 test +/// TestStep: +/// 1.Init device +/// 2.config type2 message +/// Expect: +/// 1.Success +/// 2.Verify that the data in the table is as expected +#[test] +fn test_smbios_type2() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + let mut extra_args = "-smbios type=2,manufacturer=manufacturer2,product=product2,\ + version=version2,serial=serial2,asset=asset2,location=location2" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let anchor_file = "etc/smbios/smbios-anchor"; + let tables_file = "etc/smbios/smbios-tables"; + let mut read_data: Vec = Vec::with_capacity(24); + + // Select FileDir entry and read it. + let anchor_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + anchor_file, + &mut read_data, + 24 as u32, + ); + + assert_eq!(anchor_size, 24 as u32); + assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); + assert_eq!(read_data[6], 24 as u8); + let talble_len = LittleEndian::read_u32(&read_data[12..]); + + let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); + let talbles_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + tables_file, + &mut read_table_date, + talble_len, + ); + assert_eq!(talbles_size, talble_len); + let table_type2_len = 107; + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len..table_type2_len + 13]), + "manufacturer2" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 14..table_type2_len + 22]), + "product2" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 23..table_type2_len + 31]), + "version2" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 32..table_type2_len + 39]), + "serial2" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 40..table_type2_len + 49]), + "location2" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 50..table_type2_len + 56]), + "asset2" + ); + + test_state.borrow_mut().stop(); +} + +/// TestStep: +/// 1.Init device +/// 2.config type3 message +/// Expect: +/// 1.Success +/// 2.Verify that the data in the table is as expected +#[test] +fn test_smbios_type3() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + let mut extra_args = "-smbios type=3,manufacturer=manufacturer3,version=version3,\ + serial=serial3,asset=asset3,sku=sku3" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let anchor_file = "etc/smbios/smbios-anchor"; + let tables_file = "etc/smbios/smbios-tables"; + let mut read_data: Vec = Vec::with_capacity(24); + + // Select FileDir entry and read it. + let anchor_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + anchor_file, + &mut read_data, + 24 as u32, + ); + + assert_eq!(anchor_size, 24 as u32); + assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); + assert_eq!(read_data[6], 24 as u8); + let talble_len = LittleEndian::read_u32(&read_data[12..]); + + let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); + let talbles_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + tables_file, + &mut read_table_date, + talble_len, + ); + assert_eq!(talbles_size, talble_len); + let table_type3_len = 114; + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type3_len..table_type3_len + 13]), + "manufacturer3" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type3_len + 14..table_type3_len + 22]), + "version3" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type3_len + 23..table_type3_len + 30]), + "serial3" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type3_len + 31..table_type3_len + 35]), + "sku3" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type3_len + 36..table_type3_len + 42]), + "asset3" + ); + + test_state.borrow_mut().stop(); +} + +/// TestStep: +/// 1.Init device +/// 2.config type4 message +/// Expect: +/// 1.Success +/// 2.Verify that the data in the table is as expected +#[test] +fn test_smbios_type4() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + let cpu_args = format!("-smp 8,maxcpus=8,sockets=2,cores=2,threads=2"); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + + let mut extra_args = "-smbios type=4,sock_pfx=sock_pfx4,manufacturer=manufacturer4,\ + version=version4,serial=serial4,asset=asset4,part=part4,max-speed=65534,current-speed=65534" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let anchor_file = "etc/smbios/smbios-anchor"; + let tables_file = "etc/smbios/smbios-tables"; + let mut read_data: Vec = Vec::with_capacity(24); + + // Select FileDir entry and read it. + let anchor_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + anchor_file, + &mut read_data, + 24 as u32, + ); + + assert_eq!(anchor_size, 24 as u32); + assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); + assert_eq!(read_data[6], 24 as u8); + let talble_len = LittleEndian::read_u32(&read_data[12..]); + + let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); + let talbles_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + tables_file, + &mut read_table_date, + talble_len, + ); + assert_eq!(talbles_size, talble_len); + // check speed + assert_eq!(read_table_date[157], 0xFE); + assert_eq!(read_table_date[158], 0xFF); + assert_eq!(read_table_date[159], 0xFE); + assert_eq!(read_table_date[160], 0xFF); + + let table_type4_len = 185; + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type4_len..table_type4_len + 11]), + "sock_pfx4 0" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type4_len + 12..table_type4_len + 25]), + "manufacturer4" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type4_len + 26..table_type4_len + 34]), + "version4" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type4_len + 35..table_type4_len + 42]), + "serial4" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type4_len + 43..table_type4_len + 49]), + "asset4" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type4_len + 50..table_type4_len + 55]), + "part4" + ); + test_state.borrow_mut().stop(); +} + +/// TestStep: +/// 1.Init device +/// 2.config type17 message +/// Expect: +/// 1.Success +/// 2.Verify that the data in the table is as expected +#[test] +fn test_smbios_type17() { + let mut args: Vec<&str> = Vec::new(); + bios_args(&mut args); + + let cpu_args = format!("-smp 8,maxcpus=8,sockets=2,cores=2,threads=2"); + let mut extra_args = cpu_args.split(' ').collect(); + args.append(&mut extra_args); + + let mut extra_args = + "-smbios type=17,loc_pfx=loc_pfx17,bank=bank17,manufacturer=manufacturer17,\ + serial=serial17,asset=asset17,part=part17,speed=65534" + .split(' ') + .collect(); + args.append(&mut extra_args); + + let test_state = Rc::new(RefCell::new(test_init(args))); + let machine = TestStdMachine::new(test_state.clone()); + let allocator = machine.allocator.clone(); + + let anchor_file = "etc/smbios/smbios-anchor"; + let tables_file = "etc/smbios/smbios-tables"; + let mut read_data: Vec = Vec::with_capacity(24); + + // Select FileDir entry and read it. + let anchor_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + anchor_file, + &mut read_data, + 24 as u32, + ); + + assert_eq!(anchor_size, 24 as u32); + assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); + assert_eq!(read_data[6], 24 as u8); + let talble_len = LittleEndian::read_u32(&read_data[12..]); + assert_eq!(talble_len, 434); + + let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); + let talbles_size = test_state.borrow().fw_cfg_read_file( + &mut allocator.borrow_mut(), + tables_file, + &mut read_table_date, + talble_len, + ); + assert_eq!(talbles_size, talble_len); + // check speed + assert_eq!(read_table_date[337], 0xFE); + assert_eq!(read_table_date[338], 0xFF); + + let table_type2_len = 356; + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len..table_type2_len + 14]), + "manufacturer17" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 15..table_type2_len + 26]), + "loc_pfx17 0" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 27..table_type2_len + 33]), + "bank17" + ); + + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 34..table_type2_len + 42]), + "serial17" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 43..table_type2_len + 49]), + "part17" + ); + assert_eq!( + String::from_utf8_lossy(&read_table_date[table_type2_len + 50..table_type2_len + 57]), + "asset17" + ); + + test_state.borrow_mut().stop(); +} + #[test] fn test_exception_by_ctrl_reg() { let mut args = Vec::new(); -- Gitee From 04e62d7840d3ddf7dff103d9db90a265d4d31beb Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Fri, 28 Jul 2023 14:25:12 +0800 Subject: [PATCH 1241/2187] smbios: fix mst test Signed-off-by: jiewangqun --- smbios/src/smbios_table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smbios/src/smbios_table.rs b/smbios/src/smbios_table.rs index c87308f6d..1935c8718 100644 --- a/smbios/src/smbios_table.rs +++ b/smbios/src/smbios_table.rs @@ -689,7 +689,7 @@ impl SmbiosTable { if let Some(product) = type1.product { table1.set_str(product); } else { - table1.set_str(String::from(HYPERVISOR_STR)); + table1.set_str(String::from("Virtual Machine")); } table1.header.version = table1.str_index + 1; -- Gitee From 4246ccea98ba4c42b7b992a7fff3f3c8c2787e47 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 27 Jul 2023 20:17:04 +0800 Subject: [PATCH 1242/2187] usb: optimize the UsbDevice structure Change unplugged_id to bool after id already existed in UsbDevice. Signed-off-by: zhouli57 --- devices/src/usb/mod.rs | 10 +++++----- devices/src/usb/xhci/xhci_pci.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index e31a6f088..4e31a89b0 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -123,8 +123,8 @@ pub struct UsbDevice { pub ep_out: Vec, /// USB descriptor pub descriptor: UsbDescriptor, - /// The usb device id which is hot unplugged. - pub unplugged_id: Option, + /// Check whether the usb device is hot unplugged. + pub unplugged: bool, /// The index of the interfaces. pub altsetting: [u32; USB_MAX_INTERFACES as usize], } @@ -142,7 +142,7 @@ impl UsbDevice { data_buf: vec![0_u8; data_buf_len], remote_wakeup: 0, descriptor: UsbDescriptor::new(), - unplugged_id: None, + unplugged: false, altsetting: [0_u32; USB_MAX_INTERFACES as usize], }; @@ -325,8 +325,8 @@ impl UsbDevice { impl Drop for UsbDevice { fn drop(&mut self) { - if let Some(id) = &self.unplugged_id { - send_device_deleted_msg(id); + if self.unplugged { + send_device_deleted_msg(&self.id); } } } diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index d33106c01..47c4f5d9c 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -187,7 +187,7 @@ impl XhciPciDevice { let mut locked_port = usb_port.lock().unwrap(); let dev = locked_port.dev.as_ref().unwrap(); let mut locked_dev = dev.lock().unwrap(); - locked_dev.get_mut_usb_device().unplugged_id = Some(id); + locked_dev.get_mut_usb_device().unplugged = true; locked_dev.unrealize()?; drop(locked_dev); locked_xhci.discharge_usb_port(&mut locked_port); -- Gitee From 4cb8074e713940389d0fb5a494ec4d5d6580bc6b Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 28 Jul 2023 18:06:23 +0800 Subject: [PATCH 1243/2187] camera: remove useless code Signed-off-by: zhouli57 --- devices/src/camera_backend/demo.rs | 12 --- devices/src/camera_backend/mod.rs | 6 -- devices/src/camera_backend/v4l2.rs | 11 --- devices/src/usb/camera.rs | 122 +---------------------------- 4 files changed, 2 insertions(+), 149 deletions(-) diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index e797cbf7d..073773c98 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -376,18 +376,6 @@ fn build_rgb565_list() -> CameraFormatList { } impl CameraHostdevOps for DemoCamera { - fn init(&self) -> Result<()> { - Ok(()) - } - - fn is_camera(&self) -> Result { - Ok(true) - } - - fn get_fmt(&self) -> Result> { - Ok(self.format_list.clone()) - } - fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { *self.cur_format.lock().unwrap() = *cam_fmt; info!("Demo camera backend set format {:?}", cam_fmt); diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 73e12784c..61cf32c9b 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -139,12 +139,6 @@ pub type CameraNotifyCallback = Arc; pub type CameraBrokenCallback = Arc; pub trait CameraHostdevOps: Send + Sync { - fn init(&self) -> Result<()>; - fn is_camera(&self) -> Result; - - /// Get format list. - fn get_fmt(&self) -> Result>; - /// Set a specific format. fn set_fmt(&mut self, fmt: &CamBasicFmt) -> Result<()>; diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index ffa5d7f92..b422f07f3 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -233,17 +233,6 @@ impl V4l2CameraBackend { } impl CameraHostdevOps for V4l2CameraBackend { - fn init(&self) -> Result<()> { - Ok(()) - } - fn is_camera(&self) -> Result { - Ok(true) - } - - fn get_fmt(&self) -> Result> { - Ok(self.fmt_list.clone()) - } - fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { info!("Camera {} set format {:?}", self.id, cam_fmt); if self.listening { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 1b41678ea..7a6f4ee06 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -56,7 +56,7 @@ pub const INTERFACE_ID_STREAMING: u8 = 1; const TERMINAL_ID_INPUT_TERMINAL: u8 = 1; const TERMINAL_ID_OUTPUT_TERMINAL: u8 = 2; -pub const ENDPOINT_ID_STREAMING: u8 = 0x1; +const ENDPOINT_ID_STREAMING: u8 = 0x1; const VS_INTERFACE_NUM: u8 = 1; // According to UVC specification 1.5 @@ -191,122 +191,6 @@ struct OutputTerminalDescriptor { impl ByteCode for OutputTerminalDescriptor {} -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct VSInputHeaderDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bDescriptorSubType: u8, - pub bNumFormats: u8, - pub wTotalLength: u16, - pub bEndpointAddress: u8, - pub bmInfo: u8, - pub bTerminalLink: u8, - pub bStillCaptureMethod: u8, - pub bTriggerSupport: u8, - pub bTriggerUsage: u8, - pub bControlSize: u8, - pub bmaControls: [u8; 2], -} - -impl ByteCode for VSInputHeaderDescriptor {} - -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct MjpgFormatDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bDescriptorSubType: u8, - pub bFormatIndex: u8, - pub bNumFrameDescriptors: u8, - pub bmFlags: u8, - pub bDefaultFrameIndex: u8, - pub bAspectRatioX: u8, - pub bAspectRatioY: u8, - pub bmInterfaceFlags: u8, - pub bCopyProtect: u8, -} - -impl ByteCode for MjpgFormatDescriptor {} - -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct MjpgFrameDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bDescriptorSubType: u8, - pub bFrameIndex: u8, - pub bmCapabilities: u8, - pub wWidth: u16, - pub wHeight: u16, - pub dwMinBitRate: u32, - pub dwMaxBitRate: u32, - pub dwMaxVideoFrameBufferSize: u32, - pub dwDefaultFrameInterval: u32, - pub bFrameIntervalType: u8, - pub dwFrameInterval: u32, -} - -impl ByteCode for MjpgFrameDescriptor {} - -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct ColorMatchingDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bDescriptorSubType: u8, - pub bColorPrimaries: u8, - pub bTransferCharacteristics: u8, - pub bMatrixCoefficients: u8, -} - -impl ByteCode for ColorMatchingDescriptor {} - -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct UncompressedFormatDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bDescriptorSubType: u8, - pub bFormatIndex: u8, - pub bNumFrameDescriptors: u8, - pub guidFormat: [u8; 16], - pub bBitsPerPixel: u8, - pub bDefaultFrameIndex: u8, - pub bAspectRatioX: u8, - pub bAspectRatioY: u8, - pub bmInterfaceFlags: u8, - pub bCopyProtect: u8, -} - -impl ByteCode for UncompressedFormatDescriptor {} - -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct UncompressedFrameDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bDescriptorSubType: u8, - pub bFrameIndex: u8, - pub bmCapabilities: u8, - pub wWidth: u16, - pub wHeight: u16, - pub dwMinBitRate: u32, - pub dwMaxBitRate: u32, - pub dwMaxVideoFrameBufferSize: u32, - pub dwDefaultFrameInterval: u32, - pub bFrameIntervalType: u8, - pub dwFrameInterval: u32, -} - -impl ByteCode for UncompressedFrameDescriptor {} - fn gen_desc_interface_camera_vc() -> Result> { // VideoControl Interface Descriptor let desc = Arc::new(UsbDescIface { @@ -849,9 +733,7 @@ impl UsbCamera { impl UsbDeviceOps for UsbCamera { fn realize(mut self) -> Result>> { - self.camera_dev.lock().unwrap().list_format()?; - let fmt_list = self.camera_dev.lock().unwrap().get_fmt()?; - + let fmt_list = self.camera_dev.lock().unwrap().list_format()?; self.usb_device.reset_usb_endpoint(); self.usb_device.speed = USB_SPEED_SUPER; let mut s: Vec = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); -- Gitee From f77564564c17385402555810e007f9809b97f36d Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 27 Jul 2023 17:07:34 +0800 Subject: [PATCH 1244/2187] camera: report error when backend is broken When the camera backend is broken we should set the packet to an error state, so that the driver can sence the error. Signed-off-by: zhouli57 --- devices/src/usb/camera.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 7a6f4ee06..b147221ba 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -519,9 +519,14 @@ impl UsbCamera { }); let clone_broken = self.broken.clone(); let clone_id = self.device_id().to_string(); + let clone_fd = self.camera_fd.clone(); let broken_cb: CameraBrokenCallback = Arc::new(move || { clone_broken.store(true, Ordering::SeqCst); error!("USB Camera {} device broken", clone_id); + // Notify the camera to process the packet. + if let Err(e) = clone_fd.write(1) { + error!("Failed to notify camera fd {:?}", e); + } }); let mut locked_camera = self.camera_dev.lock().unwrap(); locked_camera.register_notify_cb(notify_cb); @@ -549,6 +554,7 @@ impl UsbCamera { &self.packet_list, &self.camera_dev, &self.payload, + &self.broken, ))); register_event_helper( EventNotifierHelper::internal_notifiers(cam_handler), @@ -909,6 +915,7 @@ struct CameraIoHandler { fd: Arc, packet_list: Arc>>>>, payload: Arc>, + broken: Arc, } impl CameraIoHandler { @@ -917,12 +924,14 @@ impl CameraIoHandler { list: &Arc>>>>, camera: &Arc>, payload: &Arc>, + broken: &Arc, ) -> Self { CameraIoHandler { camera: camera.clone(), fd: fd.clone(), packet_list: list.clone(), payload: payload.clone(), + broken: broken.clone(), } } @@ -1006,7 +1015,22 @@ impl EventNotifierHelper for CameraIoHandler { let cloned_io_handler = io_handler.clone(); let handler: Rc = Rc::new(move |_event, fd: RawFd| { read_fd(fd); - cloned_io_handler.lock().unwrap().handle_io(); + let mut locked_handler = cloned_io_handler.lock().unwrap(); + if locked_handler.broken.load(Ordering::Acquire) { + let mut locked_list = locked_handler.packet_list.lock().unwrap(); + while let Some(p) = locked_list.pop_front() { + let mut locked_p = p.lock().unwrap(); + locked_p.status = UsbPacketStatus::IoError; + if let Some(transfer) = locked_p.xfer_ops.as_ref() { + if let Some(ops) = transfer.clone().upgrade() { + drop(locked_p); + ops.lock().unwrap().submit_transfer(); + } + } + } + return None; + } + locked_handler.handle_io(); None }); vec![EventNotifier::new( -- Gitee From f269564aa0ae80402fc4ee03cd3d64c1450a4b7a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 19:45:21 +0800 Subject: [PATCH 1245/2187] Refactory: Delete useless comments Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 32 -------------------------------- virtio/src/device/block.rs | 11 ----------- virtio/src/device/gpu.rs | 12 ------------ virtio/src/device/net.rs | 11 ----------- virtio/src/device/rng.rs | 11 ----------- virtio/src/device/scsi_cntlr.rs | 11 ----------- virtio/src/device/serial.rs | 11 ----------- virtio/src/vhost/kernel/net.rs | 11 ----------- virtio/src/vhost/kernel/vsock.rs | 11 ----------- virtio/src/vhost/user/block.rs | 13 ------------- virtio/src/vhost/user/fs.rs | 2 -- virtio/src/vhost/user/net.rs | 12 ------------ 12 files changed, 148 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 1170cae67..957a8154d 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -998,7 +998,6 @@ impl Balloon { } impl VirtioDevice for Balloon { - /// Realize a balloon device. fn realize(&mut self) -> Result<()> { self.mem_space .register_listener(self.mem_info.clone()) @@ -1006,12 +1005,10 @@ impl VirtioDevice for Balloon { Ok(()) } - /// Get the type of balloon. fn device_type(&self) -> u32 { VIRTIO_TYPE_BALLOON } - /// Get the number of balloon-device queues. fn queue_num(&self) -> usize { let mut queue_num = QUEUE_NUM_BALLOON; if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { @@ -1023,37 +1020,22 @@ impl VirtioDevice for Balloon { queue_num } - /// Get the zise of balloon queue. fn queue_size(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - /// Get the feature of `balloon` device. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.device_features, features_select) } - /// Set feature for device. - /// - /// # Arguments - /// - /// * `page` - Selector of feature. - /// * `value` - Value to be set. fn set_driver_features(&mut self, page: u32, value: u32) { self.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.driver_features, features_select) } - /// Read configuration. - /// - /// # Arguments - /// - /// * `offset` - Offset from base address. - /// * `data` - Read data to `data`. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let new_config = VirtioBalloonConfig { num_pages: self.num_pages, @@ -1084,11 +1066,6 @@ impl VirtioDevice for Balloon { Ok(()) } - /// Write configuration. - /// - /// # Argument - /// - /// * `_offset` - Offset from base address. fn write_config(&mut self, _offset: u64, data: &[u8]) -> Result<()> { // Guest update actual balloon size // Safe, because the results will be checked. @@ -1114,15 +1091,6 @@ impl VirtioDevice for Balloon { Ok(()) } - /// Active balloon device. - /// - /// # Arguments - /// - /// * `mem_space` - Address space. - /// * `interrupt_evt` - Interrupt EventFd. - /// * `interrupt_stats` - Statistics interrupt. - /// * `queues` - Different virtio queues. - /// * `queue_evts` Different EventFd. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index b47467f75..1a772d05e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1038,7 +1038,6 @@ impl Block { } impl VirtioDevice for Block { - /// Realize virtio block device. fn realize(&mut self) -> Result<()> { // if iothread not found, return err if self.blk_cfg.iothread.is_some() @@ -1105,37 +1104,30 @@ impl VirtioDevice for Block { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_BLOCK } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { self.blk_cfg.queues as usize } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { self.blk_cfg.queue_size } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_len = self.get_blk_config_size(); let read_end = offset as usize + data.len(); @@ -1153,7 +1145,6 @@ impl VirtioDevice for Block { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let config_len = self.get_blk_config_size(); if offset @@ -1169,8 +1160,6 @@ impl VirtioDevice for Block { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index e77d38953..e33bea30c 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1479,7 +1479,6 @@ impl Gpu { } impl VirtioDevice for Gpu { - /// Realize virtio gpu device. fn realize(&mut self) -> Result<()> { if self.cfg.max_outputs > VIRTIO_GPU_MAX_OUTPUTS as u32 { bail!( @@ -1519,7 +1518,6 @@ impl VirtioDevice for Gpu { Ok(()) } - /// Unrealize low level device. fn unrealize(&mut self) -> Result<()> { for con in &self.consoles { console_close(con)?; @@ -1529,37 +1527,30 @@ impl VirtioDevice for Gpu { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_GPU } - /// Get the count of virtio gpu queues. fn queue_num(&self) -> usize { QUEUE_NUM_GPU } - /// Get the queue size of virtio gpu. fn queue_size(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_space = *self.state.config_space.lock().unwrap(); let config_slice = config_space.as_bytes(); @@ -1579,7 +1570,6 @@ impl VirtioDevice for Gpu { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let mut config_space = self.state.config_space.lock().unwrap(); let mut config_cpy = *config_space; @@ -1602,8 +1592,6 @@ impl VirtioDevice for Gpu { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 545a7e32c..a7837aca6 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1374,7 +1374,6 @@ fn get_tap_offload_flags(features: u64) -> u32 { } impl VirtioDevice for Net { - /// Realize virtio network device. fn realize(&mut self) -> Result<()> { // if iothread not found, return err if self.net_cfg.iothread.is_some() @@ -1469,12 +1468,10 @@ impl VirtioDevice for Net { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_NET } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { if self.net_cfg.mq { (self.net_cfg.queues + 1) as usize @@ -1483,27 +1480,22 @@ impl VirtioDevice for Net { } } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { self.net_cfg.queue_size } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.lock().unwrap().device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.lock().unwrap().driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let locked_state = self.state.lock().unwrap(); let config_slice = locked_state.config_space.as_bytes(); @@ -1520,7 +1512,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let mut locked_state = self.state.lock().unwrap(); @@ -1548,8 +1539,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 70ee34aa9..54beac046 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -262,7 +262,6 @@ impl Rng { } impl VirtioDevice for Rng { - /// Realize virtio rng device. fn realize(&mut self) -> Result<()> { self.check_random_file() .with_context(|| "Failed to check random file")?; @@ -274,37 +273,30 @@ impl VirtioDevice for Rng { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_RNG } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { QUEUE_NUM_RNG } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, _data: &mut [u8]) -> Result<()> { bail!( "Reading device config space for rng is not supported, offset: {}", @@ -312,7 +304,6 @@ impl VirtioDevice for Rng { ); } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, _data: &[u8]) -> Result<()> { bail!( "Writing device config space for rng is not supported, offset: {}", @@ -320,8 +311,6 @@ impl VirtioDevice for Rng { ); } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 6705bc94f..0dd6e2708 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -152,7 +152,6 @@ impl ScsiCntlr { } impl VirtioDevice for ScsiCntlr { - /// Realize virtio scsi controller, which is a pci device. fn realize(&mut self) -> Result<()> { // If iothread not found, return err. if self.config.iothread.is_some() @@ -187,38 +186,31 @@ impl VirtioDevice for ScsiCntlr { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_SCSI } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { // Note: self.config.queues <= MAX_VIRTIO_QUEUE(32). self.config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { self.config.queue_size } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len() as u64; @@ -232,7 +224,6 @@ impl VirtioDevice for ScsiCntlr { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let config_slice = self.state.config_space.as_mut_bytes(); let config_len = config_slice.len() as u64; @@ -251,8 +242,6 @@ impl VirtioDevice for ScsiCntlr { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 6b652e81d..9744a61c2 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -217,7 +217,6 @@ pub fn find_port_by_nr( } impl VirtioDevice for Serial { - /// Realize virtio serial device. fn realize(&mut self) -> Result<()> { self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE @@ -226,39 +225,32 @@ impl VirtioDevice for Serial { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_CONSOLE } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { // Each port has 2 queues(receiveq/transmitq). // And there exist 2 control queues(control receiveq/control transmitq). self.max_nr_ports as usize * 2 + 2 } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.state.config_space.as_bytes(); let config_len = config_slice.len() as u64; @@ -273,13 +265,10 @@ impl VirtioDevice for Serial { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { bail!("Writing device config space for virtio serial is not supported.") } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 5ec5c4b91..be468684c 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -116,7 +116,6 @@ impl Net { } impl VirtioDevice for Net { - /// Realize vhost virtio network device. fn realize(&mut self) -> Result<()> { let queue_pairs = self.net_cfg.queues / 2; let mut backends = Vec::with_capacity(queue_pairs as usize); @@ -182,12 +181,10 @@ impl VirtioDevice for Net { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_NET } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { if self.net_cfg.mq { (self.net_cfg.queues + 1) as usize @@ -196,27 +193,22 @@ impl VirtioDevice for Net { } } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { self.net_cfg.queue_size } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.lock().unwrap().device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.lock().unwrap().driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let locked_state = self.state.lock().unwrap(); let config_slice = locked_state.config_space.as_bytes(); @@ -231,7 +223,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let mut locked_state = self.state.lock().unwrap(); @@ -262,8 +253,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 1233d7075..9761345c4 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -171,7 +171,6 @@ impl Vsock { } impl VirtioDevice for Vsock { - /// Realize vhost virtio vsock device. fn realize(&mut self) -> Result<()> { let vhost_fd: Option = self.vsock_cfg.vhost_fd; let backend = VhostBackend::new(&self.mem_space, VHOST_PATH, vhost_fd) @@ -187,37 +186,30 @@ impl VirtioDevice for Vsock { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_VSOCK } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { QUEUE_NUM_VSOCK } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { match offset { 0 if data.len() == 8 => LittleEndian::write_u64(data, self.vsock_cfg.guest_cid), @@ -233,7 +225,6 @@ impl VirtioDevice for Vsock { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let config_len = self.state.config_space.len(); @@ -259,8 +250,6 @@ impl VirtioDevice for Vsock { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, _: Arc, diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index c161958e6..8136a8391 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -169,7 +169,6 @@ impl Block { } impl VirtioDevice for Block { - /// Realize vhost user blk pci device. fn realize(&mut self) -> Result<()> { self.init_client()?; self.negotiate_features()?; @@ -177,37 +176,30 @@ impl VirtioDevice for Block { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_BLOCK } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { self.blk_cfg.queues as usize } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { self.blk_cfg.queue_size } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let offset = offset as usize; let config_slice = self.state.config_space.as_bytes(); @@ -227,7 +219,6 @@ impl VirtioDevice for Block { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let offset = offset as usize; let config_slice = self.state.config_space.as_mut_bytes(); @@ -255,7 +246,6 @@ impl VirtioDevice for Block { Ok(()) } - /// Activate device. fn activate( &mut self, _mem_space: Arc, @@ -274,7 +264,6 @@ impl VirtioDevice for Block { Ok(()) } - /// Deactivate device. fn deactivate(&mut self) -> Result<()> { self.client .as_ref() @@ -285,14 +274,12 @@ impl VirtioDevice for Block { self.delete_event() } - /// Unrealize device. fn unrealize(&mut self) -> Result<()> { self.delete_event()?; self.client = None; Ok(()) } - /// Set guest notifiers for notifying the guest. fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 8fc6d691c..a45e8fe4e 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -188,7 +188,6 @@ impl VirtioDevice for Fs { self.acked_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.acked_features, features_select) } @@ -248,7 +247,6 @@ impl VirtioDevice for Fs { Ok(()) } - /// Set guest notifiers for notifying the guest. fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { for fd in queue_evts.iter() { let cloned_evt_fd = fd.clone(); diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 2a79f1d0e..9a7a3c5e5 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -102,7 +102,6 @@ impl Net { } impl VirtioDevice for Net { - /// Realize vhost user network device. fn realize(&mut self) -> Result<()> { let socket_path = self .net_cfg @@ -159,12 +158,10 @@ impl VirtioDevice for Net { Ok(()) } - /// Get the virtio device type, refer to Virtio Spec. fn device_type(&self) -> u32 { VIRTIO_TYPE_NET } - /// Get the count of virtio device queues. fn queue_num(&self) -> usize { if self.net_cfg.mq { // If support multi-queue, it should add 1 control queue. @@ -174,27 +171,22 @@ impl VirtioDevice for Net { } } - /// Get the queue size of virtio device. fn queue_size(&self) -> u16 { self.net_cfg.queue_size } - /// Get device features from host. fn get_device_features(&self, features_select: u32) -> u32 { read_u32(self.state.lock().unwrap().device_features, features_select) } - /// Set driver features by guest. fn set_driver_features(&mut self, page: u32, value: u32) { self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); } - /// Get driver features by guest. fn get_driver_features(&self, features_select: u32) -> u32 { read_u32(self.state.lock().unwrap().driver_features, features_select) } - /// Read data of config from guest. fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let locked_state = self.state.lock().unwrap(); let config_slice = locked_state.config_space.as_bytes(); @@ -209,7 +201,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Write data to config from guest. fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); let mut locked_state = self.state.lock().unwrap(); @@ -228,8 +219,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Activate the virtio device, this function is called by vcpu thread when frontend - /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate( &mut self, mem_space: Arc, @@ -284,7 +273,6 @@ impl VirtioDevice for Net { Ok(()) } - /// Set guest notifiers for notifying the guest. fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), -- Gitee From 4fd6ba09b1a74b12269373e3a30ba6d44c4c83be Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 17:41:15 +0800 Subject: [PATCH 1246/2187] Refactory: Introduce VirtioBase for virtio ... to share common device info. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/mod.rs | 2 +- virtio/src/device/balloon.rs | 85 +++++++------ virtio/src/device/block.rs | 179 +++++++++++++--------------- virtio/src/device/gpu.rs | 66 ++++------ virtio/src/device/net.rs | 176 +++++++++++++-------------- virtio/src/device/rng.rs | 68 +++++------ virtio/src/device/scsi_cntlr.rs | 90 ++++++-------- virtio/src/device/serial.rs | 86 ++++++------- virtio/src/lib.rs | 15 ++- virtio/src/transport/virtio_mmio.rs | 28 ++--- virtio/src/transport/virtio_pci.rs | 30 +++-- virtio/src/vhost/kernel/net.rs | 88 +++++++------- virtio/src/vhost/kernel/vsock.rs | 85 +++++++------ virtio/src/vhost/user/block.rs | 49 ++++---- virtio/src/vhost/user/fs.rs | 45 +++---- virtio/src/vhost/user/net.rs | 92 +++++++------- 16 files changed, 558 insertions(+), 626 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 4d644112c..a4685cb7a 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -298,7 +298,7 @@ impl LightMachine { ); } for id in 0..MMIO_REPLACEABLE_NET_NR { - let net = Arc::new(Mutex::new(Net::default())); + let net = Arc::new(Mutex::new(Net::new(NetworkInterfaceConfig::default()))); let virtio_mmio = VirtioMmioDevice::new(&self.sys_mem, net.clone()); rpl_devs.push(virtio_mmio); diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 957a8154d..fef8ea4c7 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -46,7 +46,7 @@ use util::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use crate::{ - error::*, report_virtio_error, virtio_has_feature, Element, Queue, VirtioDevice, + error::*, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; @@ -874,10 +874,8 @@ impl EventNotifierHelper for BalloonIoHandler { /// A balloon device with some necessary information. pub struct Balloon { - /// Balloon device features. - device_features: u64, - /// Driver features. - driver_features: u64, + /// Virtio device base property. + base: VirtioBase, /// Actual memory pages of balloon device. actual: Arc, /// Target memory pages of balloon device. @@ -890,10 +888,6 @@ pub struct Balloon { mem_space: Arc, /// Event timer for BALLOON_CHANGED event. event_timer: Arc>, - /// EventFd for device deactivate. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, /// For auto balloon membuf_percent: u32, monitor_interval: u32, @@ -918,16 +912,16 @@ impl Balloon { } Balloon { - device_features, - driver_features: 0u64, + base: VirtioBase { + device_features, + ..Default::default() + }, actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, interrupt_cb: None, mem_info: Arc::new(Mutex::new(BlnMemInfo::new())), mem_space, event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), membuf_percent: bln_cfg.membuf_percent, monitor_interval: bln_cfg.monitor_interval, } @@ -1011,10 +1005,10 @@ impl VirtioDevice for Balloon { fn queue_num(&self) -> usize { let mut queue_num = QUEUE_NUM_BALLOON; - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { + if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_REPORTING) { queue_num += 1; } - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { queue_num += 1; } queue_num @@ -1025,15 +1019,15 @@ impl VirtioDevice for Balloon { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { @@ -1046,11 +1040,12 @@ impl VirtioDevice for Balloon { monitor_interval: self.monitor_interval, }; - let config_len = if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { - size_of::() as u64 - } else { - offset_of!(VirtioBalloonConfig, _reserved) as u64 - }; + let config_len = + if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + size_of::() as u64 + } else { + offset_of!(VirtioBalloonConfig, _reserved) as u64 + }; let data_len = data.len() as u64; if offset >= config_len { @@ -1114,7 +1109,7 @@ impl VirtioDevice for Balloon { let mut queue_index = 2; let mut report_queue = None; let mut report_evt = None; - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_REPORTING) { + if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_REPORTING) { report_queue = Some(queues[queue_index].clone()); report_evt = Some(queue_evts[queue_index].clone()); queue_index += 1; @@ -1123,14 +1118,14 @@ impl VirtioDevice for Balloon { // Get msg queue and eventfd. let mut msg_queue = None; let mut msg_evt = None; - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { msg_queue = Some(queues[queue_index].clone()); msg_evt = Some(queue_evts[queue_index].clone()); } self.interrupt_cb = Some(interrupt_cb.clone()); let handler = BalloonIoHandler { - driver_features: self.driver_features, + driver_features: self.base.driver_features, mem_space, inf_queue, inf_evt, @@ -1140,7 +1135,7 @@ impl VirtioDevice for Balloon { report_evt, msg_queue, msg_evt, - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), interrupt_cb, mem_info: self.mem_info.clone(), event_timer: self.event_timer.clone(), @@ -1148,26 +1143,26 @@ impl VirtioDevice for Balloon { }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.deactivate_evts) + register_event_helper(notifiers, None, &mut self.base.deactivate_evts) .with_context(|| "Failed to register balloon event notifier to MainLoop")?; - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(None, &mut self.deactivate_evts) + unregister_event_helper(None, &mut self.base.deactivate_evts) } fn reset(&mut self) -> Result<()> { - if virtio_has_feature(self.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { + if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { self.num_pages = 0; } Ok(()) } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } @@ -1273,27 +1268,27 @@ mod tests { let mem_space = address_space_init(); let mut bln = Balloon::new(&bln_cfg, mem_space); - assert_eq!(bln.driver_features, 0); + assert_eq!(bln.base.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); assert!(bln.interrupt_cb.is_none()); let feature = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM); - assert_eq!(bln.device_features, feature); + assert_eq!(bln.base.device_features, feature); let fts = bln.get_device_features(0); assert_eq!(fts, feature as u32); let fts = bln.get_device_features(1); assert_eq!(fts, (feature >> 32) as u32); - bln.driver_features = 0; - bln.device_features = 1 | 1 << 32; + bln.base.driver_features = 0; + bln.base.device_features = 1 | 1 << 32; bln.set_driver_features(0, 1); - assert_eq!(bln.driver_features, 1); - assert_eq!(bln.driver_features, bln.get_driver_features(0) as u64); - bln.driver_features = 1 << 32; + assert_eq!(bln.base.driver_features, 1); + assert_eq!(bln.base.driver_features, bln.get_driver_features(0) as u64); + bln.base.driver_features = 1 << 32; bln.set_driver_features(1, 1); - assert_eq!(bln.driver_features, 1 << 32); + assert_eq!(bln.base.driver_features, 1 << 32); assert_eq!( - bln.driver_features, + bln.base.driver_features, (bln.get_driver_features(1) as u64) << 32 ); @@ -1471,7 +1466,7 @@ mod tests { let event_def = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); let mut handler = BalloonIoHandler { - driver_features: bln.driver_features, + driver_features: bln.base.driver_features, mem_space: mem_space.clone(), inf_queue: queue1, inf_evt: event_inf.clone(), @@ -1481,7 +1476,7 @@ mod tests { report_evt: None, msg_queue: None, msg_evt: None, - device_broken: bln.broken.clone(), + device_broken: bln.base.broken.clone(), interrupt_cb: cb.clone(), mem_info: bln.mem_info.clone(), event_timer: bln.event_timer.clone(), @@ -1661,13 +1656,13 @@ mod tests { }; let mem_space = address_space_init(); let mut bln = Balloon::new(&bln_cfg, mem_space); - assert_eq!(bln.driver_features, 0); + assert_eq!(bln.base.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); assert!(bln.interrupt_cb.is_none()); let feature = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM | 1u64 << VIRTIO_BALLOON_F_REPORTING); - assert_eq!(bln.device_features, feature); + assert_eq!(bln.base.device_features, feature); let fts = bln.get_device_features(0); assert_eq!(fts, feature as u32); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 1a772d05e..5913ef872 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -26,16 +26,15 @@ use byteorder::{ByteOrder, LittleEndian}; use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; -use crate::VirtioError; use crate::{ gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, - virtio_has_feature, Element, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, - VirtioTrace, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, - VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, - VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, - VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + virtio_has_feature, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, + VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, + VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, + VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, + VIRTIO_BLK_T_WRITE_ZEROES, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::{AddressSpace, GuestAddress}; use block_backend::{ @@ -945,9 +944,14 @@ pub struct BlockState { } /// Block device structure. +#[derive(Default)] pub struct Block { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the block device. blk_cfg: BlkDevConfig, + /// Config space of the block device. + config_space: VirtioBlkConfig, /// BLock backend opened by the block device. block_backend: Option>>>, /// The align requirement of request(offset/len). @@ -956,18 +960,12 @@ pub struct Block { pub buf_align: u32, /// Number of sectors of the image file. disk_sectors: u64, - /// Status of block device. - state: BlockState, /// Callback to trigger interrupt. interrupt_cb: Option>, /// The sending half of Rust's channel to send the image file. senders: Vec>, /// Eventfd for config space update. update_evts: Vec>, - /// Eventfd for device deactivate. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, /// Drive backend files. drive_files: Arc>>, } @@ -979,49 +977,42 @@ impl Block { ) -> Block { Self { blk_cfg, - block_backend: None, req_align: 1, buf_align: 1, - disk_sectors: 0, - state: BlockState::default(), - interrupt_cb: None, - senders: Vec::new(), - update_evts: Vec::new(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), drive_files, + ..Default::default() } } fn build_device_config_space(&mut self) { // capacity: 64bits let num_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; - self.state.config_space.capacity = num_sectors; + self.config_space.capacity = num_sectors; // seg_max = queue_size - 2: 32bits - self.state.config_space.seg_max = self.queue_size() as u32 - 2; + self.config_space.seg_max = self.queue_size() as u32 - 2; if self.blk_cfg.discard { - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; // Just support one segment per request. - self.state.config_space.max_discard_seg = 1; + self.config_space.max_discard_seg = 1; // The default discard alignment is 1 sector. - self.state.config_space.discard_sector_alignment = 1; - self.state.config_space.max_discard_sectors = MAX_REQUEST_SECTORS; + self.config_space.discard_sector_alignment = 1; + self.config_space.max_discard_sectors = MAX_REQUEST_SECTORS; } if self.blk_cfg.write_zeroes != WriteZeroesState::Off { - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; // Just support one segment per request. - self.state.config_space.max_write_zeroes_seg = 1; - self.state.config_space.max_write_zeroes_sectors = MAX_REQUEST_SECTORS; - self.state.config_space.write_zeroes_may_unmap = 1; + self.config_space.max_write_zeroes_seg = 1; + self.config_space.max_write_zeroes_sectors = MAX_REQUEST_SECTORS; + self.config_space.write_zeroes_may_unmap = 1; } } fn get_blk_config_size(&self) -> u64 { - if virtio_has_feature(self.state.device_features, VIRTIO_BLK_F_WRITE_ZEROES) { + if virtio_has_feature(self.base.device_features, VIRTIO_BLK_F_WRITE_ZEROES) { offset_of!(VirtioBlkConfig, unused1) as u64 - } else if virtio_has_feature(self.state.device_features, VIRTIO_BLK_F_DISCARD) { + } else if virtio_has_feature(self.base.device_features, VIRTIO_BLK_F_DISCARD) { offset_of!(VirtioBlkConfig, max_write_zeroes_sectors) as u64 } else { offset_of!(VirtioBlkConfig, max_discard_sectors) as u64 @@ -1029,8 +1020,8 @@ impl Block { } fn gen_error_cb(&self, interrupt_cb: Arc) -> BlockIoErrorCallback { - let cloned_features = self.state.driver_features; - let clone_broken = self.broken.clone(); + let cloned_features = self.base.driver_features; + let clone_broken = self.base.broken.clone(); Arc::new(move || { report_virtio_error(interrupt_cb.clone(), cloned_features, &clone_broken); }) @@ -1049,19 +1040,19 @@ impl VirtioDevice for Block { ); } - self.state.device_features = (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_BLK_F_FLUSH); + self.base.device_features = (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_BLK_F_FLUSH); if self.blk_cfg.read_only { - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_RO; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_RO; }; - self.state.device_features |= 1_u64 << VIRTIO_F_RING_INDIRECT_DESC; - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_SEG_MAX; - self.state.device_features |= 1_u64 << VIRTIO_F_RING_EVENT_IDX; + self.base.device_features |= 1_u64 << VIRTIO_F_RING_INDIRECT_DESC; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_SEG_MAX; + self.base.device_features |= 1_u64 << VIRTIO_F_RING_EVENT_IDX; self.build_device_config_space(); if self.blk_cfg.queues > 1 { - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; - self.state.config_space.num_queues = self.blk_cfg.queues; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; + self.config_space.num_queues = self.blk_cfg.queues; } self.block_backend = None; @@ -1094,7 +1085,7 @@ impl VirtioDevice for Block { self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; } - self.state.config_space.capacity = self.disk_sectors; + self.config_space.capacity = self.disk_sectors; Ok(()) } @@ -1117,15 +1108,15 @@ impl VirtioDevice for Block { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { @@ -1139,7 +1130,7 @@ impl VirtioDevice for Block { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); } - let config_slice = self.state.config_space.as_bytes(); + let config_slice = self.config_space.as_bytes(); data.write_all(&config_slice[(offset as usize)..read_end])?; Ok(()) @@ -1174,7 +1165,7 @@ impl VirtioDevice for Block { } let (sender, receiver) = channel(); let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); - let driver_features = self.state.driver_features; + let driver_features = self.base.driver_features; let handler = BlockIoHandler { queue: queue.clone(), queue_evt: queue_evts[index].clone(), @@ -1188,7 +1179,7 @@ impl VirtioDevice for Block { driver_features, receiver, update_evt: update_evt.clone(), - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), leak_bucket: match self.blk_cfg.iops { @@ -1203,7 +1194,7 @@ impl VirtioDevice for Block { register_event_helper( notifiers, self.blk_cfg.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; self.update_evts.push(update_evt); self.senders.push(sender); @@ -1214,21 +1205,24 @@ impl VirtioDevice for Block { block_backend .lock() .unwrap() - .register_io_event(self.broken.clone(), err_cb)?; + .register_io_event(self.base.broken.clone(), err_cb)?; } else { warn!( "No disk image when block device {} activate", self.blk_cfg.id ); } - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { // Stop receiving virtqueue requests and drain incomplete IO. - unregister_event_helper(self.blk_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + unregister_event_helper( + self.blk_cfg.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; if let Some(block_backend) = self.block_backend.as_ref() { let mut block_backend = block_backend.lock().unwrap(); // Must drain requests before unregister. @@ -1283,7 +1277,7 @@ impl VirtioDevice for Block { })? .lock() .unwrap() - .register_io_event(self.broken.clone(), err_cb)?; + .register_io_event(self.base.broken.clone(), err_cb)?; } else { warn!( "No interrupter cb, may be device {} is not activated", @@ -1314,7 +1308,7 @@ impl VirtioDevice for Block { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } @@ -1325,15 +1319,22 @@ unsafe impl Sync for Block {} impl StateTransfer for Block { fn get_state_vec(&self) -> migration::Result> { - let mut state = self.state; - state.broken = self.broken.load(Ordering::SeqCst); + let state = BlockState { + device_features: self.base.device_features, + driver_features: self.base.driver_features, + config_space: self.config_space, + broken: self.base.broken.load(Ordering::SeqCst), + }; Ok(state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *BlockState::from_bytes(state) + let state = BlockState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("BLOCK"))?; - self.broken.store(self.state.broken, Ordering::SeqCst); + self.base.device_features = state.device_features; + self.base.driver_features = state.driver_features; + self.base.broken.store(state.broken, Ordering::SeqCst); + self.config_space = state.config_space; Ok(()) } @@ -1363,25 +1364,6 @@ mod tests { const VIRTQ_DESC_F_WRITE: u16 = 0x02; const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; - impl Default for Block { - fn default() -> Self { - Block { - blk_cfg: Default::default(), - block_backend: None, - req_align: 1, - buf_align: 1, - disk_sectors: 0, - state: BlockState::default(), - interrupt_cb: None, - senders: Vec::new(), - update_evts: Vec::new(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), - drive_files: Arc::new(Mutex::new(HashMap::new())), - } - } - } - // build dummy address space of vm fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); @@ -1408,15 +1390,22 @@ mod tests { sys_space } + fn init_default_block() -> Block { + Block::new( + BlkDevConfig::default(), + Arc::new(Mutex::new(HashMap::new())), + ) + } + // Use different input parameters to verify block `new()` and `realize()` functionality. #[test] fn test_block_init() { // New block device - let mut block = Block::default(); + let mut block = init_default_block(); assert_eq!(block.disk_sectors, 0); - assert_eq!(block.state.device_features, 0); - assert_eq!(block.state.driver_features, 0); - assert_eq!(block.state.config_space.as_bytes().len(), CONFIG_SPACE_SIZE); + assert_eq!(block.base.device_features, 0); + assert_eq!(block.base.driver_features, 0); + assert_eq!(block.config_space.as_bytes().len(), CONFIG_SPACE_SIZE); assert!(block.block_backend.is_none()); assert!(block.interrupt_cb.is_none()); assert!(block.senders.is_empty()); @@ -1445,7 +1434,7 @@ mod tests { // read data are not same; Input invalid offset or data length, it will failed. #[test] fn test_read_write_config() { - let mut block = Block::default(); + let mut block = init_default_block(); block.realize().unwrap(); let expect_config_space: [u8; 8] = [0x00, 020, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00]; @@ -1473,33 +1462,33 @@ mod tests { // bit is supported. #[test] fn test_block_features() { - let mut block = Block::default(); + let mut block = init_default_block(); // If the device feature is 0, all driver features are not supported. - block.state.device_features = 0; + block.base.device_features = 0; let driver_feature: u32 = 0xFF; let page = 0_u32; block.set_driver_features(page, driver_feature); - assert_eq!(block.state.driver_features, 0_u64); + assert_eq!(block.base.driver_features, 0_u64); assert_eq!(block.get_driver_features(page) as u64, 0_u64); assert_eq!(block.get_device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; block.set_driver_features(page, driver_feature); - assert_eq!(block.state.driver_features, 0_u64); + assert_eq!(block.base.driver_features, 0_u64); assert_eq!(block.get_driver_features(page) as u64, 0_u64); assert_eq!(block.get_device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. - block.state.device_features = + block.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC; let driver_feature: u32 = (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) as u32; let page = 0_u32; block.set_driver_features(page, driver_feature); assert_eq!( - block.state.driver_features, + block.base.driver_features, (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) ); assert_eq!( @@ -1510,16 +1499,16 @@ mod tests { block.get_device_features(page), (1_u32 << VIRTIO_F_RING_INDIRECT_DESC) ); - block.state.driver_features = 0; + block.base.driver_features = 0; - block.state.device_features = 1_u64 << VIRTIO_F_VERSION_1; + block.base.device_features = 1_u64 << VIRTIO_F_VERSION_1; let driver_feature: u32 = (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) as u32; let page = 0_u32; block.set_driver_features(page, driver_feature); - assert_eq!(block.state.driver_features, 0); + assert_eq!(block.base.driver_features, 0); assert_eq!(block.get_driver_features(page), 0); assert_eq!(block.get_device_features(page), 0_u32); - block.state.driver_features = 0; + block.base.driver_features = 0; } // Test `get_serial_num_config`. The function will output the shorter length between 20 @@ -1556,7 +1545,7 @@ mod tests { }; EventLoop::object_init(&Some(vec![io_conf])).unwrap(); - let mut block = Block::default(); + let mut block = init_default_block(); let file = TempFile::new().unwrap(); block.blk_cfg.path_on_host = file.as_path().to_str().unwrap().to_string(); block.blk_cfg.direct = false; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index e33bea30c..63d30811a 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -50,8 +50,8 @@ use util::pixman::{ }; use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, ElemIovec, Element, Queue, VirtioDevice, - VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, ElemIovec, Element, Queue, VirtioBase, + VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, @@ -1425,33 +1425,19 @@ pub struct VirtioGpuConfig { _reserved: u32, } -/// State of gpu device. -#[repr(C)] -#[derive(Clone, Default)] -pub struct GpuState { - /// Bitmask of features supported by the backend. - device_features: u64, - /// Bit mask of features negotiated by the backend and the frontend. - driver_features: u64, - /// Config space of the GPU device. - config_space: Arc>, -} - /// GPU device structure. #[derive(Default)] pub struct Gpu { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the GPU device. cfg: GpuDevConfig, - /// Status of the GPU device. - state: GpuState, + /// Config space of the GPU device. + config_space: Arc>, /// Status of the emulated physical outputs. output_states: Arc>, /// Each console corresponds to a display. consoles: Vec>>>, - /// Eventfd for device deactivate. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, } /// SAFETY: The raw pointer in rust doesn't impl Send, all write operations @@ -1462,18 +1448,12 @@ impl Gpu { pub fn new(cfg: GpuDevConfig) -> Gpu { Self { cfg, - state: GpuState::default(), - output_states: Arc::new(Mutex::new( - [VirtioGpuOutputState::default(); VIRTIO_GPU_MAX_OUTPUTS], - )), - consoles: Vec::new(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), + ..Default::default() } } fn build_device_config_space(&mut self) { - let mut config_space = self.state.config_space.lock().unwrap(); + let mut config_space = self.config_space.lock().unwrap(); config_space.num_scanouts = self.cfg.max_outputs; } } @@ -1488,11 +1468,11 @@ impl VirtioDevice for Gpu { ); } - self.state.device_features = 1u64 << VIRTIO_F_VERSION_1; - self.state.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; - self.state.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; + self.base.device_features = 1u64 << VIRTIO_F_VERSION_1; + self.base.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; + self.base.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; if self.cfg.edid { - self.state.device_features |= 1 << VIRTIO_GPU_F_EDID; + self.base.device_features |= 1 << VIRTIO_GPU_F_EDID; } let mut output_states = self.output_states.lock().unwrap(); @@ -1501,7 +1481,7 @@ impl VirtioDevice for Gpu { let gpu_opts = Arc::new(GpuOpts { output_states: self.output_states.clone(), - config_space: self.state.config_space.clone(), + config_space: self.config_space.clone(), interrupt_cb: None, }); for i in 0..self.cfg.max_outputs { @@ -1540,19 +1520,19 @@ impl VirtioDevice for Gpu { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_space = *self.state.config_space.lock().unwrap(); + let config_space = *self.config_space.lock().unwrap(); let config_slice = config_space.as_bytes(); let config_len = config_slice.len() as u64; @@ -1571,7 +1551,7 @@ impl VirtioDevice for Gpu { } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let mut config_space = self.state.config_space.lock().unwrap(); + let mut config_space = self.config_space.lock().unwrap(); let mut config_cpy = *config_space; let config_cpy_slice = config_cpy.as_mut_bytes(); let config_len = config_cpy_slice.len() as u64; @@ -1609,7 +1589,7 @@ impl VirtioDevice for Gpu { let mut scanouts = vec![]; let gpu_opts = Arc::new(GpuOpts { output_states: self.output_states.clone(), - config_space: self.state.config_space.clone(), + config_space: self.config_space.clone(), interrupt_cb: Some(interrupt_cb.clone()), }); for con in &self.consoles { @@ -1630,7 +1610,7 @@ impl VirtioDevice for Gpu { ctrl_queue_evt: queue_evts[0].clone(), cursor_queue_evt: queue_evts[1].clone(), interrupt_cb, - driver_features: self.state.driver_features, + driver_features: self.base.driver_features, resources_list: Vec::new(), enable_output_bitmask: 1, num_scanouts: self.cfg.max_outputs, @@ -1641,7 +1621,7 @@ impl VirtioDevice for Gpu { }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; Ok(()) } @@ -1651,10 +1631,10 @@ impl VirtioDevice for Gpu { display_set_major_screen("ramfb")?; set_run_stage(VmRunningStage::Bios); } - unregister_event_helper(None, &mut self.deactivate_evts) + unregister_event_helper(None, &mut self.base.deactivate_evts) } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index a7837aca6..1997563d5 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -28,19 +28,20 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, - Element, Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, - VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, - VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, - VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, - VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_CTRL_VLAN, - VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, - VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, - VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, + Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, + VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, + VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, + VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, + VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, + VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use address_space::{AddressSpace, RegionCache}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -168,16 +169,16 @@ pub struct CtrlInfo { /// The map of all the vlan ids. vlan_map: HashMap, /// The net device status. - state: Arc>, + config: Arc>, } impl CtrlInfo { - pub fn new(state: Arc>) -> Self { + pub fn new(config: Arc>) -> Self { CtrlInfo { rx_mode: CtrlRxMode::default(), mac_info: CtrlMacInfo::default(), vlan_map: HashMap::new(), - state, + config, } } @@ -293,12 +294,7 @@ impl CtrlInfo { if ack == VIRTIO_NET_ERR { return VIRTIO_NET_ERR; } - self.state - .lock() - .unwrap() - .config_space - .mac - .copy_from_slice(&mac); + self.config.lock().unwrap().mac.copy_from_slice(&mac); } VIRTIO_NET_CTRL_MAC_TABLE_SET => { ack = self @@ -382,7 +378,7 @@ impl CtrlInfo { } queue_pairs = LittleEndian::read_u16(queue_pairs.as_bytes()); - let max_pairs = self.state.lock().unwrap().config_space.max_virtqueue_pairs; + let max_pairs = self.config.lock().unwrap().max_virtqueue_pairs; if !(VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=max_pairs).contains(&queue_pairs) { error!("Invalid queue pairs {}", queue_pairs); return VIRTIO_NET_ERR; @@ -451,7 +447,7 @@ impl CtrlInfo { } if self.rx_mode.all_uni || self.mac_info.uni_mac_of - || buf[..MAC_ADDR_LEN] == self.state.lock().unwrap().config_space.mac + || buf[..MAC_ADDR_LEN] == self.config.lock().unwrap().mac { return false; } @@ -1166,51 +1162,29 @@ pub struct VirtioNetState { } /// Network device structure. +#[derive(Default)] pub struct Net { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the network device. net_cfg: NetworkInterfaceConfig, + /// Virtio net configurations. + config_space: Arc>, /// Tap device opened. taps: Option>, - /// The status of net device. - state: Arc>, /// The send half of Rust's channel to send tap information. senders: Option>>, /// Eventfd for config space update. update_evts: Vec>, - /// Eventfd for device deactivate. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, /// The information about control command. ctrl_info: Option>>, } -impl Default for Net { - fn default() -> Self { - Self { - net_cfg: Default::default(), - taps: None, - state: Arc::new(Mutex::new(VirtioNetState::default())), - senders: None, - update_evts: Vec::new(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), - ctrl_info: None, - } - } -} - impl Net { pub fn new(net_cfg: NetworkInterfaceConfig) -> Self { Self { net_cfg, - taps: None, - state: Arc::new(Mutex::new(VirtioNetState::default())), - senders: None, - update_evts: Vec::new(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), - ctrl_info: None, + ..Default::default() } } } @@ -1385,8 +1359,7 @@ impl VirtioDevice for Net { ); } - let mut locked_state = self.state.lock().unwrap(); - locked_state.device_features = 1 << VIRTIO_F_VERSION_1 + self.base.device_features = 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GUEST_CSUM | 1 << VIRTIO_NET_F_GUEST_TSO4 @@ -1403,13 +1376,15 @@ impl VirtioDevice for Net { | 1 << VIRTIO_F_RING_INDIRECT_DESC | 1 << VIRTIO_F_RING_EVENT_IDX; + let mut locked_config = self.config_space.lock().unwrap(); + let queue_pairs = self.net_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { - locked_state.device_features |= 1 << VIRTIO_NET_F_MQ; - locked_state.config_space.max_virtqueue_pairs = queue_pairs; + self.base.device_features |= 1 << VIRTIO_NET_F_MQ; + locked_config.max_virtqueue_pairs = queue_pairs; } if !self.net_cfg.host_dev_name.is_empty() { @@ -1437,30 +1412,29 @@ impl VirtioDevice for Net { // Using the first tap to test if all the taps have ufo. if let Some(tap) = self.taps.as_ref().map(|t| &t[0]) { if !tap.has_ufo() { - locked_state.device_features &= + self.base.device_features &= !(1 << VIRTIO_NET_F_GUEST_UFO | 1 << VIRTIO_NET_F_HOST_UFO); } } if let Some(mac) = &self.net_cfg.mac { - locked_state.device_features |= - build_device_config_space(&mut locked_state.config_space, mac); - mark_mac_table(&locked_state.config_space.mac, true); - } else if locked_state.config_space.mac == [0; MAC_ADDR_LEN] { + self.base.device_features |= build_device_config_space(&mut locked_config, mac); + mark_mac_table(&locked_config.mac, true); + } else if locked_config.mac == [0; MAC_ADDR_LEN] { let mac = get_default_mac_addr().with_context(|| "Failed to get a default mac address")?; - locked_state.config_space.mac.copy_from_slice(&mac); - locked_state.device_features |= 1 << VIRTIO_NET_F_MAC; + locked_config.mac.copy_from_slice(&mac); + self.base.device_features |= 1 << VIRTIO_NET_F_MAC; } else { // For microvm which will call realize() twice for one virtio-net-device. - locked_state.device_features |= 1 << VIRTIO_NET_F_MAC; + self.base.device_features |= 1 << VIRTIO_NET_F_MAC; } Ok(()) } fn unrealize(&mut self) -> Result<()> { - mark_mac_table(&self.state.lock().unwrap().config_space.mac, false); + mark_mac_table(&self.config_space.lock().unwrap().mac, false); MigrationManager::unregister_device_instance( VirtioNetState::descriptor(), &self.net_cfg.id, @@ -1485,20 +1459,20 @@ impl VirtioDevice for Net { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.lock().unwrap().device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.lock().unwrap().driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let locked_state = self.state.lock().unwrap(); - let config_slice = locked_state.config_space.as_bytes(); + let config_space = self.config_space.lock().unwrap(); + let config_slice = config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset .checked_add(data.len() as u64) @@ -1514,9 +1488,9 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let mut locked_state = self.state.lock().unwrap(); - let driver_features = locked_state.driver_features; - let config_slice = locked_state.config_space.as_mut_bytes(); + let driver_features = self.base.driver_features; + let mut config_space = self.config_space.lock().unwrap(); + let config_slice = config_space.as_mut_bytes(); if offset .checked_add(data_len as u64) @@ -1547,9 +1521,9 @@ impl VirtioDevice for Net { queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); - let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); + let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.config_space.clone()))); self.ctrl_info = Some(ctrl_info.clone()); - let driver_features = self.state.lock().unwrap().driver_features; + let driver_features = self.base.driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); @@ -1559,7 +1533,7 @@ impl VirtioDevice for Net { mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features, - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), taps: self.taps.clone(), }; @@ -1568,7 +1542,7 @@ impl VirtioDevice for Net { register_event_helper( notifiers, self.net_cfg.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; } @@ -1603,7 +1577,7 @@ impl VirtioDevice for Net { driver_features, receiver, update_evt: update_evt.clone(), - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), queue_size: self.queue_size(), @@ -1616,12 +1590,12 @@ impl VirtioDevice for Net { register_event_helper( notifiers, self.net_cfg.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; self.update_evts.push(update_evt); } self.senders = Some(senders); - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); Ok(()) } @@ -1679,14 +1653,17 @@ impl VirtioDevice for Net { } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + unregister_event_helper( + self.net_cfg.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; self.update_evts.clear(); self.ctrl_info = None; Ok(()) } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } @@ -1697,8 +1674,13 @@ unsafe impl Sync for Net {} impl StateTransfer for Net { fn get_state_vec(&self) -> migration::Result> { - self.state.lock().unwrap().broken = self.broken.load(Ordering::SeqCst); - Ok(self.state.lock().unwrap().as_bytes().to_vec()) + let state = VirtioNetState { + device_features: self.base.device_features, + driver_features: self.base.driver_features, + config_space: *self.config_space.lock().unwrap(), + broken: self.base.broken.load(Ordering::SeqCst), + }; + Ok(state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { @@ -1706,10 +1688,12 @@ impl StateTransfer for Net { if state.len() != s_len { bail!("Invalid state length {}, expected {}", state.len(), s_len); } - let mut locked_state = self.state.lock().unwrap(); - locked_state.as_mut_bytes().copy_from_slice(state); - self.broken.store(locked_state.broken, Ordering::SeqCst); - + let state = VirtioNetState::from_bytes(state) + .with_context(|| migration::error::MigrationError::FromBytesError("NET"))?; + self.base.device_features = state.device_features; + self.base.driver_features = state.driver_features; + self.base.broken.store(state.broken, Ordering::SeqCst); + *self.config_space.lock().unwrap() = state.config_space; Ok(()) } @@ -1730,9 +1714,9 @@ mod tests { #[test] fn test_net_init() { // test net new method - let mut net = Net::default(); - assert_eq!(net.state.lock().unwrap().device_features, 0); - assert_eq!(net.state.lock().unwrap().driver_features, 0); + let mut net = Net::new(NetworkInterfaceConfig::default()); + assert_eq!(net.base.device_features, 0); + assert_eq!(net.base.driver_features, 0); assert_eq!(net.taps.is_none(), true); assert_eq!(net.senders.is_none(), true); @@ -1760,10 +1744,10 @@ mod tests { net.write_config(0x00, &origin_data).unwrap(); // test boundary condition of offset and data parameters - let locked_state = net.state.lock().unwrap(); - let device_config = locked_state.config_space.as_bytes(); + let config_space = net.config_space.lock().unwrap(); + let device_config = config_space.as_bytes(); let len = device_config.len() as u64; - drop(locked_state); + drop(config_space); let mut data: Vec = vec![0; 10]; let offset: u64 = len + 1; @@ -1823,7 +1807,7 @@ mod tests { #[test] fn test_net_filter_vlan() { - let mut ctrl_info = CtrlInfo::new(Arc::new(Mutex::new(VirtioNetState::default()))); + let mut ctrl_info = CtrlInfo::new(Arc::new(Mutex::new(VirtioNetConfig::default()))); ctrl_info.rx_mode.promisc = false; let mut buf = [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x00, @@ -1891,7 +1875,7 @@ mod tests { #[test] fn test_iothread() { - let mut net = Net::default(); + let mut net = Net::new(NetworkInterfaceConfig::default()); net.net_cfg.iothread = Some("iothread".to_string()); if let Err(err) = net.realize() { let err_msg = format!( diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 54beac046..9e9b7a684 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -41,7 +41,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::error::VirtioError; use crate::{ - ElemIovec, Queue, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + ElemIovec, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; use anyhow::{bail, Context, Result}; @@ -214,30 +214,21 @@ pub struct RngState { } /// Random number generator device structure +#[derive(Default)] pub struct Rng { + /// Virtio device base property. + base: VirtioBase, /// Configuration of virtio rng device rng_cfg: RngConfig, /// The file descriptor of random number generator random_file: Option, - /// The state of Rng device. - state: RngState, - /// Eventfd for device deactivate - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, } impl Rng { pub fn new(rng_cfg: RngConfig) -> Self { Rng { rng_cfg, - random_file: None, - state: RngState { - device_features: 0, - driver_features: 0, - }, - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), + ..Default::default() } } @@ -269,7 +260,7 @@ impl VirtioDevice for Rng { .with_context(|| "Failed to open file of random number generator")?; self.random_file = Some(file); - self.state.device_features = 1 << VIRTIO_F_VERSION_1 as u64; + self.base.device_features = 1 << VIRTIO_F_VERSION_1 as u64; Ok(()) } @@ -286,15 +277,15 @@ impl VirtioDevice for Rng { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, _data: &mut [u8]) -> Result<()> { @@ -322,7 +313,7 @@ impl VirtioDevice for Rng { queue: queues[0].clone(), queue_evt: queue_evts[0].clone(), interrupt_cb, - driver_features: self.state.driver_features, + driver_features: self.base.driver_features, mem_space, random_file: self .random_file @@ -337,29 +328,34 @@ impl VirtioDevice for Rng { }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(None, &mut self.deactivate_evts) + unregister_event_helper(None, &mut self.base.deactivate_evts) } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } impl StateTransfer for Rng { fn get_state_vec(&self) -> migration::Result> { - Ok(self.state.as_bytes().to_vec()) + let state = RngState { + device_features: self.base.device_features, + driver_features: self.base.driver_features, + }; + Ok(state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *RngState::from_bytes(state) + let state = RngState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("RNG"))?; - + self.base.device_features = state.device_features; + self.base.driver_features = state.driver_features; Ok(()) } @@ -427,8 +423,8 @@ mod tests { }; let rng = Rng::new(rng_config); assert!(rng.random_file.is_none()); - assert_eq!(rng.state.driver_features, 0_u64); - assert_eq!(rng.state.device_features, 0_u64); + assert_eq!(rng.base.driver_features, 0_u64); + assert_eq!(rng.base.device_features, 0_u64); assert_eq!(rng.rng_cfg.random_file, random_file); assert_eq!(rng.rng_cfg.bytes_per_sec, Some(64)); @@ -453,30 +449,30 @@ mod tests { let mut rng = Rng::new(rng_config); // If the device feature is 0, all driver features are not supported. - rng.state.device_features = 0; + rng.base.device_features = 0; let driver_feature: u32 = 0xFF; let page = 0_u32; rng.set_driver_features(page, driver_feature); - assert_eq!(rng.state.driver_features, 0_u64); + assert_eq!(rng.base.driver_features, 0_u64); assert_eq!(rng.get_driver_features(page) as u64, 0_u64); assert_eq!(rng.get_device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; rng.set_driver_features(page, driver_feature); - assert_eq!(rng.state.driver_features, 0_u64); + assert_eq!(rng.base.driver_features, 0_u64); assert_eq!(rng.get_driver_features(page) as u64, 0_u64); assert_eq!(rng.get_device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. - rng.state.device_features = + rng.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64; let driver_feature: u32 = 1_u32 << VIRTIO_F_RING_INDIRECT_DESC; let page = 0_u32; rng.set_driver_features(page, driver_feature); assert_eq!( - rng.state.driver_features, + rng.base.driver_features, (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) ); assert_eq!( @@ -487,15 +483,15 @@ mod tests { rng.get_device_features(page), (1_u32 << VIRTIO_F_RING_INDIRECT_DESC) ); - rng.state.driver_features = 0; + rng.base.driver_features = 0; - rng.state.device_features = 1_u64 << VIRTIO_F_VERSION_1; + rng.base.device_features = 1_u64 << VIRTIO_F_VERSION_1; let driver_feature: u32 = 1_u32 << VIRTIO_F_RING_INDIRECT_DESC; let page = 0_u32; rng.set_driver_features(page, driver_feature); - assert_eq!(rng.state.driver_features, 0); + assert_eq!(rng.base.driver_features, 0); assert_eq!(rng.get_device_features(page), 0_u32); - rng.state.driver_features = 0; + rng.base.driver_features = 0; } #[test] diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 0dd6e2708..e6d729db7 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -24,8 +24,8 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, - VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, + VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; use block_backend::BlockIoErrorCallback; @@ -106,45 +106,30 @@ struct VirtioScsiConfig { impl ByteCode for VirtioScsiConfig {} -/// State of virtio scsi controller. -#[derive(Clone, Copy, Default)] -pub struct ScsiCntlrState { - /// Bitmask of features supported by the backend. - device_features: u64, - /// Bit mask of features negotiated by the backend and the frontend. - driver_features: u64, - /// Config space of the virtio scsi controller. - config_space: VirtioScsiConfig, -} - /// Virtio Scsi Controller device structure. +#[derive(Default)] pub struct ScsiCntlr { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the virtio scsi controller. pub config: ScsiCntlrConfig, - /// Status of virtio scsi controller. - state: ScsiCntlrState, + /// Config space of the virtio scsi controller. + config_space: VirtioScsiConfig, /// Scsi bus. pub bus: Option>>, - /// Eventfd for Scsi Controller deactivates. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, } impl ScsiCntlr { pub fn new(config: ScsiCntlrConfig) -> ScsiCntlr { Self { config, - state: ScsiCntlrState::default(), - bus: None, - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), + ..Default::default() } } fn gen_error_cb(&self, interrupt_cb: Arc) -> BlockIoErrorCallback { - let cloned_features = self.state.driver_features; - let clone_broken = self.broken.clone(); + let cloned_features = self.base.driver_features; + let clone_broken = self.base.broken.clone(); Arc::new(move || { report_virtio_error(interrupt_cb.clone(), cloned_features, &clone_broken); }) @@ -163,19 +148,19 @@ impl VirtioDevice for ScsiCntlr { ); } - self.state.config_space.num_queues = self.config.queues; + self.config_space.num_queues = self.config.queues; - self.state.config_space.max_sectors = 0xFFFF_u32; + self.config_space.max_sectors = 0xFFFF_u32; // cmd_per_lun: maximum number of linked commands can be sent to one LUN. 32bit. - self.state.config_space.cmd_per_lun = 128; + self.config_space.cmd_per_lun = 128; // seg_max: queue size - 2, 32 bit. - self.state.config_space.seg_max = self.queue_size() as u32 - 2; - self.state.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; - self.state.config_space.max_lun = VIRTIO_SCSI_MAX_LUN as u32; + self.config_space.seg_max = self.queue_size() as u32 - 2; + self.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; + self.config_space.max_lun = VIRTIO_SCSI_MAX_LUN as u32; // num_queues: request queues number. - self.state.config_space.num_queues = self.config.queues; + self.config_space.num_queues = self.config.queues; - self.state.device_features |= (1_u64 << VIRTIO_F_VERSION_1) + self.base.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_F_RING_EVENT_IDX) | (1_u64 << VIRTIO_F_RING_INDIRECT_DESC); @@ -200,19 +185,19 @@ impl VirtioDevice for ScsiCntlr { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.state.config_space.as_bytes(); + let config_slice = self.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); @@ -225,7 +210,7 @@ impl VirtioDevice for ScsiCntlr { } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let config_slice = self.state.config_space.as_mut_bytes(); + let config_slice = self.config_space.as_mut_bytes(); let config_len = config_slice.len() as u64; if offset @@ -261,14 +246,14 @@ impl VirtioDevice for ScsiCntlr { queue_evt: ctrl_queue_evt, mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, - device_broken: self.broken.clone(), + driver_features: self.base.driver_features, + device_broken: self.base.broken.clone(), }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(ctrl_handler))); register_event_helper( notifiers, self.config.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; // Register event notifier for event queue. @@ -279,15 +264,15 @@ impl VirtioDevice for ScsiCntlr { queue_evt: event_queue_evt, _mem_space: mem_space.clone(), _interrupt_cb: interrupt_cb.clone(), - _driver_features: self.state.driver_features, - device_broken: self.broken.clone(), + _driver_features: self.base.driver_features, + device_broken: self.base.broken.clone(), }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(event_handler))); register_event_helper( notifiers, self.config.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; // Register event notifier for command queues. @@ -299,8 +284,8 @@ impl VirtioDevice for ScsiCntlr { queue_evt: queue_evts[index + 2].clone(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, - device_broken: self.broken.clone(), + driver_features: self.base.driver_features, + device_broken: self.base.broken.clone(), }; let notifiers = @@ -309,10 +294,10 @@ impl VirtioDevice for ScsiCntlr { register_event_helper( notifiers, self.config.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; } - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); // Register event notifier for device aio. let bus = self.bus.as_ref().unwrap(); @@ -323,13 +308,16 @@ impl VirtioDevice for ScsiCntlr { // SAFETY: the disk_image is assigned after device realized. let disk_image = locked_device.block_backend.as_ref().unwrap(); let mut locked_backend = disk_image.lock().unwrap(); - locked_backend.register_io_event(self.broken.clone(), err_cb)?; + locked_backend.register_io_event(self.base.broken.clone(), err_cb)?; } Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(self.config.iothread.as_ref(), &mut self.deactivate_evts)?; + unregister_event_helper( + self.config.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; let bus = self.bus.as_ref().unwrap(); let locked_bus = bus.lock().unwrap(); for device in locked_bus.devices.values() { @@ -343,7 +331,7 @@ impl VirtioDevice for ScsiCntlr { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 9744a61c2..690ed2e1b 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -26,7 +26,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::{ gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, - VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; @@ -128,17 +128,16 @@ pub struct VirtioSerialState { } /// Virtio serial device structure. +#[derive(Default)] pub struct Serial { - /// Status of virtio serial device. - state: VirtioSerialState, - /// EventFd for device deactivate. - deactivate_evts: Vec, + /// Virtio device base property. + base: VirtioBase, + /// Virtio serial config space. + config_space: VirtioConsoleConfig, /// Max serial ports number. pub max_nr_ports: u32, /// Serial port vector for serialport. pub ports: Arc>>>>, - /// Device is broken or not. - device_broken: Arc, } impl Serial { @@ -149,15 +148,9 @@ impl Serial { /// * `serial_cfg` - Device configuration set by user. pub fn new(serial_cfg: VirtioSerialInfo) -> Self { Serial { - state: VirtioSerialState { - device_features: 0_u64, - driver_features: 0_u64, - config_space: VirtioConsoleConfig::new(serial_cfg.max_ports), - }, - deactivate_evts: Vec::new(), + config_space: VirtioConsoleConfig::new(serial_cfg.max_ports), max_nr_ports: serial_cfg.max_ports, - ports: Arc::new(Mutex::new(Vec::new())), - device_broken: Arc::new(AtomicBool::new(false)), + ..Default::default() } } @@ -177,7 +170,7 @@ impl Serial { output_queue_evt: queue_evts[3].clone(), mem_space, interrupt_cb, - driver_features: self.state.driver_features, + driver_features: self.base.driver_features, device_broken, ports: self.ports.clone(), }; @@ -187,7 +180,7 @@ impl Serial { port.lock().unwrap().ctrl_handler = Some(Arc::downgrade(&handler_h.clone())); } let notifiers = EventNotifierHelper::internal_notifiers(handler_h); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; Ok(()) } @@ -218,7 +211,7 @@ pub fn find_port_by_nr( impl VirtioDevice for Serial { fn realize(&mut self) -> Result<()> { - self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 + self.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT; @@ -240,19 +233,19 @@ impl VirtioDevice for Serial { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.state.config_space.as_bytes(); + let config_slice = self.config_space.as_bytes(); let config_len = config_slice.len() as u64; if offset >= config_len { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); @@ -298,13 +291,13 @@ impl VirtioDevice for Serial { output_queue_evt: queue_evts[queue_id * 2 + 1].clone(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), - driver_features: self.state.driver_features, - device_broken: self.device_broken.clone(), + driver_features: self.base.driver_features, + device_broken: self.base.broken.clone(), port: port.clone(), }; let handler_h = Arc::new(Mutex::new(handler)); let notifiers = EventNotifierHelper::internal_notifiers(handler_h.clone()); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; if let Some(port_h) = port { port_h.lock().unwrap().activate(&handler_h); @@ -316,7 +309,7 @@ impl VirtioDevice for Serial { interrupt_cb, queues, queue_evts, - self.device_broken.clone(), + self.base.broken.clone(), )?; Ok(()) @@ -326,25 +319,32 @@ impl VirtioDevice for Serial { for port in self.ports.lock().unwrap().iter_mut() { port.lock().unwrap().deactivate(); } - unregister_event_helper(None, &mut self.deactivate_evts)?; + unregister_event_helper(None, &mut self.base.deactivate_evts)?; Ok(()) } fn get_device_broken(&self) -> &Arc { - &self.device_broken + &self.base.broken } } impl StateTransfer for Serial { fn get_state_vec(&self) -> migration::Result> { - Ok(self.state.as_bytes().to_vec()) + let state = VirtioSerialState { + device_features: self.base.device_features, + driver_features: self.base.driver_features, + config_space: self.config_space, + }; + Ok(state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *VirtioSerialState::from_bytes(state) + let state = VirtioSerialState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("SERIAL"))?; - + self.base.device_features = state.device_features; + self.base.driver_features = state.driver_features; + self.config_space = state.config_space; Ok(()) } @@ -939,57 +939,57 @@ mod tests { }); // If the device feature is 0, all driver features are not supported. - serial.state.device_features = 0; + serial.base.device_features = 0; let driver_feature: u32 = 0xFF; let page = 0_u32; serial.set_driver_features(page, driver_feature); - assert_eq!(serial.state.driver_features, 0_u64); + assert_eq!(serial.base.driver_features, 0_u64); assert_eq!(serial.get_driver_features(page) as u64, 0_u64); let driver_feature: u32 = 0xFF; let page = 1_u32; serial.set_driver_features(page, driver_feature); - assert_eq!(serial.state.driver_features, 0_u64); + assert_eq!(serial.base.driver_features, 0_u64); assert_eq!(serial.get_driver_features(page) as u64, 0_u64); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. - serial.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE; + serial.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE; let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; let page = 0_u32; serial.set_driver_features(page, driver_feature); assert_eq!( - serial.state.driver_features, + serial.base.driver_features, (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); assert_eq!( serial.get_driver_features(page) as u64, (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); - serial.state.driver_features = 0; + serial.base.driver_features = 0; - serial.state.device_features = 1_u64 << VIRTIO_F_VERSION_1; + serial.base.device_features = 1_u64 << VIRTIO_F_VERSION_1; let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_SIZE) as u32; let page = 0_u32; serial.set_driver_features(page, driver_feature); - assert_eq!(serial.state.driver_features, 0); - serial.state.driver_features = 0; + assert_eq!(serial.base.driver_features, 0); + serial.base.driver_features = 0; - serial.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 + serial.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT; let driver_feature: u32 = (1_u64 << VIRTIO_CONSOLE_F_MULTIPORT) as u32; let page = 0_u32; serial.set_driver_features(page, driver_feature); assert_eq!( - serial.state.driver_features, + serial.base.driver_features, (1_u64 << VIRTIO_CONSOLE_F_MULTIPORT) ); let driver_feature: u32 = ((1_u64 << VIRTIO_F_VERSION_1) >> 32) as u32; let page = 1_u32; serial.set_driver_features(page, driver_feature); assert_eq!( - serial.state.driver_features, + serial.base.driver_features, (1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT) ); } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index c4ea76f2e..eef3066eb 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -32,6 +32,7 @@ mod transport; pub mod vhost; use std::cmp; +use std::os::unix::prelude::RawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -46,7 +47,7 @@ use util::num_ops::write_u32; use util::AsAny; pub use device::balloon::*; -pub use device::block::{Block, BlockState}; +pub use device::block::{Block, BlockState, VirtioBlkConfig}; #[cfg(not(target_env = "musl"))] pub use device::gpu::*; pub use device::net::*; @@ -316,6 +317,18 @@ pub enum VirtioInterruptType { pub type VirtioInterrupt = Box, bool) -> Result<()> + Send + Sync>; +#[derive(Default)] +struct VirtioBase { + /// Bit mask of features supported by the backend. + device_features: u64, + /// Bit mask of features negotiated by the backend and the frontend. + driver_features: u64, + /// Eventfd for device deactivate. + deactivate_evts: Vec, + /// Device is broken or not. + broken: Arc, +} + /// The trait for virtio device operations. pub trait VirtioDevice: Send + AsAny { /// Realize low level device. diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 26f527896..33230d287 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -737,7 +737,7 @@ mod tests { use std::io::Write; use super::*; - use crate::VIRTIO_TYPE_BLOCK; + use crate::{VirtioBase, VIRTIO_TYPE_BLOCK}; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use std::sync::atomic::AtomicBool; use util::num_ops::read_u32; @@ -773,12 +773,10 @@ mod tests { const QUEUE_SIZE: u16 = 256; pub struct VirtioDeviceTest { - pub device_features: u64, - pub driver_features: u64, + base: VirtioBase, pub config_space: Vec, pub b_active: bool, pub b_realized: bool, - pub broken: Arc, } impl VirtioDeviceTest { @@ -789,12 +787,10 @@ mod tests { } VirtioDeviceTest { - device_features: 0, - driver_features: 0, + base: Default::default(), b_active: false, b_realized: false, config_space, - broken: Arc::new(AtomicBool::new(false)), } } } @@ -818,15 +814,15 @@ mod tests { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { @@ -877,7 +873,7 @@ mod tests { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } @@ -955,7 +951,7 @@ mod tests { .unwrap() .config_space .features_select = 0; - virtio_device_clone.lock().unwrap().device_features = 0x0000_00f8_0000_00fe; + virtio_device_clone.lock().unwrap().base.device_features = 0x0000_00f8_0000_00fe; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), true @@ -1204,13 +1200,13 @@ mod tests { locked_state.config_space.acked_features_select = 0; drop(locked_state); LittleEndian::write_u32(&mut buf[..], 0x0000_00fe); - virtio_device_clone.lock().unwrap().device_features = 0x0000_00fe; + virtio_device_clone.lock().unwrap().base.device_features = 0x0000_00fe; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), true ); assert_eq!( - virtio_device_clone.lock().unwrap().driver_features as u32, + virtio_device_clone.lock().unwrap().base.driver_features as u32, 0x0000_00fe ); // it is ok to write the high 32bit of device features @@ -1222,7 +1218,7 @@ mod tests { .config_space .acked_features_select = 1; LittleEndian::write_u32(&mut buf[..], 0x0000_00ff); - virtio_device_clone.lock().unwrap().device_features = 0x0000_00ff_0000_0000; + virtio_device_clone.lock().unwrap().base.device_features = 0x0000_00ff_0000_0000; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), true @@ -1237,7 +1233,7 @@ mod tests { QUEUE_TYPE_PACKED_VRING ); assert_eq!( - virtio_device_clone.lock().unwrap().driver_features >> 32 as u32, + virtio_device_clone.lock().unwrap().base.driver_features >> 32 as u32, 0x0000_00ff ); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 5daa96885..39547e6db 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1467,26 +1467,24 @@ mod tests { use vmm_sys_util::eventfd::EventFd; use super::*; - use crate::Result as VirtioResult; + use crate::{Result as VirtioResult, VirtioBase}; const VIRTIO_DEVICE_TEST_TYPE: u32 = 1; const VIRTIO_DEVICE_QUEUE_NUM: usize = 2; const VIRTIO_DEVICE_QUEUE_SIZE: u16 = 256; pub struct VirtioDeviceTest { - pub device_features: u64, - pub driver_features: u64, + base: VirtioBase, pub is_activated: bool, - pub broken: Arc, } impl VirtioDeviceTest { pub fn new() -> Self { + let mut base = VirtioBase::default(); + base.device_features = 0xFFFF_FFF0; VirtioDeviceTest { - device_features: 0xFFFF_FFF0, - driver_features: 0, + base, is_activated: false, - broken: Arc::new(AtomicBool::new(false)), } } } @@ -1509,15 +1507,15 @@ mod tests { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, _offset: u64, mut _data: &mut [u8]) -> VirtioResult<()> { @@ -1540,7 +1538,7 @@ mod tests { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } @@ -1595,22 +1593,22 @@ mod tests { cmn_cfg.acked_features_select = 1_u32; com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, 0xFF); // The feature is not supported by this virtio device, and is masked - assert_eq!(dev.lock().unwrap().driver_features, 0_u64); + assert_eq!(dev.lock().unwrap().base.driver_features, 0_u64); cmn_cfg.acked_features_select = 0_u32; com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, 0xCF); // The feature is partially supported by this virtio device, and is partially masked - assert_eq!(dev.lock().unwrap().driver_features, 0xC0_u64); + assert_eq!(dev.lock().unwrap().base.driver_features, 0xC0_u64); // Set the feature of the Queue type cmn_cfg.acked_features_select = 1_u32; - dev.lock().unwrap().driver_features = 0_u64; - dev.lock().unwrap().device_features = 0xFFFF_FFFF_0000_0000_u64; + dev.lock().unwrap().base.driver_features = 0_u64; + dev.lock().unwrap().base.device_features = 0xFFFF_FFFF_0000_0000_u64; let driver_features = 1_u32 << (VIRTIO_F_RING_PACKED - 32); com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, driver_features); assert_eq!(cmn_cfg.queue_type, QUEUE_TYPE_PACKED_VRING); assert_eq!( - dev.lock().unwrap().driver_features, + dev.lock().unwrap().base.driver_features, 1_u64 << VIRTIO_F_RING_PACKED ); } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index be468684c..6a3c7a6c1 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -16,7 +16,6 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use crate::error::VirtioError; use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; use machine_manager::config::NetworkInterfaceConfig; @@ -31,12 +30,14 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; use crate::{ - device::net::{build_device_config_space, create_tap, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, - virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, - VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, - VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, + device::net::{build_device_config_space, create_tap, CtrlInfo, MAC_ADDR_LEN}, + error::VirtioError, + virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioBase, VirtioDevice, + VirtioInterrupt, VirtioNetConfig, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; /// Number of virtqueues. @@ -76,22 +77,20 @@ impl VhostNetBackend for VhostBackend { /// Network device structure. pub struct Net { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the network device. net_cfg: NetworkInterfaceConfig, + /// Virtio net configurations. + config_space: Arc>, /// Tap device opened. taps: Option>, - /// The status of net device. - state: Arc>, /// Related vhost-net kernel device. backends: Option>, /// Bit mask of features supported by the vhost-net kernel. vhost_features: u64, /// System address space. mem_space: Arc, - /// EventFd for device deactivate. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, /// Save irqfd used for vhost-net. call_events: Vec>, /// Whether irqfd can be used. @@ -101,14 +100,13 @@ pub struct Net { impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { Net { + base: Default::default(), net_cfg: cfg.clone(), + config_space: Default::default(), taps: None, - state: Arc::new(Mutex::new(VirtioNetState::default())), backends: None, vhost_features: 0_u64, mem_space: mem_space.clone(), - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), call_events: Vec::new(), disable_irqfd: false, } @@ -149,18 +147,19 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_TSO4 | 1 << VIRTIO_NET_F_HOST_UFO; - let mut locked_state = self.state.lock().unwrap(); + let mut locked_config = self.config_space.lock().unwrap(); + if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; device_features |= 1 << VIRTIO_NET_F_MQ; - locked_state.config_space.max_virtqueue_pairs = queue_pairs; + locked_config.max_virtqueue_pairs = queue_pairs; } if let Some(mac) = &self.net_cfg.mac { - device_features |= build_device_config_space(&mut locked_state.config_space, mac); + device_features |= build_device_config_space(&mut locked_config, mac); } let host_dev_name = match self.net_cfg.host_dev_name.as_str() { @@ -171,7 +170,7 @@ impl VirtioDevice for Net { self.taps = create_tap(self.net_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) .with_context(|| "Failed to create tap for vhost net")?; self.backends = Some(backends); - locked_state.device_features = device_features; + self.base.device_features = device_features; self.vhost_features = vhost_features; Ok(()) @@ -198,20 +197,20 @@ impl VirtioDevice for Net { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.lock().unwrap().device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.lock().unwrap().driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let locked_state = self.state.lock().unwrap(); - let config_slice = locked_state.config_space.as_bytes(); + let config_space = self.config_space.lock().unwrap(); + let config_slice = config_space.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); @@ -225,9 +224,9 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let mut locked_state = self.state.lock().unwrap(); - let driver_features = locked_state.driver_features; - let config_slice = locked_state.config_space.as_mut_bytes(); + let driver_features = self.base.driver_features; + let mut config_space = self.config_space.lock().unwrap(); + let config_slice = config_space.as_mut_bytes(); if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) @@ -261,18 +260,18 @@ impl VirtioDevice for Net { queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); - let driver_features = self.state.lock().unwrap().driver_features; + let driver_features = self.base.driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); - let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); + let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.config_space.clone()))); let ctrl_handler = NetCtrlHandler { ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, ctrl_info), mem_space, interrupt_cb: interrupt_cb.clone(), driver_features, - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), taps: None, }; @@ -281,7 +280,7 @@ impl VirtioDevice for Net { register_event_helper( notifiers, self.net_cfg.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; } @@ -382,24 +381,27 @@ impl VirtioDevice for Net { let handler = VhostIoHandler { interrupt_cb: interrupt_cb.clone(), host_notifies, - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); register_event_helper( notifiers, self.net_cfg.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; } } - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + unregister_event_helper( + self.net_cfg.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; if !self.disable_irqfd { self.call_events.clear(); } @@ -427,7 +429,7 @@ impl VirtioDevice for Net { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } @@ -517,7 +519,7 @@ mod tests { assert_eq!(vhost_net.realize().is_ok(), true); // test for get/set_driver_features - vhost_net.state.lock().unwrap().device_features = 0; + vhost_net.base.device_features = 0; let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); @@ -525,7 +527,7 @@ mod tests { let new_page = vhost_net.get_device_features(page); assert_eq!(new_page, page); - vhost_net.state.lock().unwrap().device_features = 0xffff_ffff_ffff_ffff; + vhost_net.base.device_features = 0xffff_ffff_ffff_ffff; let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); @@ -534,13 +536,7 @@ mod tests { assert_ne!(new_page, page); // test for read/write_config - let len = vhost_net - .state - .lock() - .unwrap() - .config_space - .as_bytes() - .len() as u64; + let len = vhost_net.config_space.lock().unwrap().as_bytes().len() as u64; let offset: u64 = 0; let data: Vec = vec![1; len as usize]; assert_eq!(vhost_net.write_config(offset, &data).is_ok(), true); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 9761345c4..dfbc3514e 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -31,7 +31,8 @@ use util::num_ops::read_u32; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ - Queue, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_TYPE_VSOCK, + Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VIRTIO_TYPE_VSOCK, }; /// Number of virtqueues. @@ -89,22 +90,22 @@ pub struct VsockState { /// Vsock device structure. pub struct Vsock { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the vsock device. vsock_cfg: VsockConfig, + /// Configuration of virtio vsock. + config_space: [u8; 8], /// Related vhost-vsock kernel device. backend: Option, - /// The status of vsock. - state: VsockState, + /// Last avail idx in vsock backend queue. + last_avail_idx: [u16; 2], /// System address space. mem_space: Arc, /// Event queue for vsock. event_queue: Option>>, /// Callback to trigger interrupt. interrupt_cb: Option>, - /// EventFd for device deactivate. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, /// Save irqfd used for vhost-vsock call_events: Vec>, /// Whether irqfd can be used. @@ -114,14 +115,14 @@ pub struct Vsock { impl Vsock { pub fn new(cfg: &VsockConfig, mem_space: &Arc) -> Self { Vsock { + base: Default::default(), vsock_cfg: cfg.clone(), backend: None, - state: VsockState::default(), + config_space: Default::default(), + last_avail_idx: Default::default(), mem_space: mem_space.clone(), event_queue: None, interrupt_cb: None, - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), call_events: Vec::new(), disable_irqfd: false, } @@ -135,7 +136,7 @@ impl Vsock { let mut event_queue_locked = evt_queue.lock().unwrap(); let element = event_queue_locked .vring - .pop_avail(&self.mem_space, self.state.driver_features) + .pop_avail(&self.mem_space, self.base.driver_features) .with_context(|| "Failed to get avail ring element.")?; if element.desc_num == 0 { return Ok(()); @@ -178,7 +179,7 @@ impl VirtioDevice for Vsock { backend .set_owner() .with_context(|| "Failed to set owner for vsock")?; - self.state.device_features = backend + self.base.device_features = backend .get_features() .with_context(|| "Failed to get features for vsock")?; self.backend = Some(backend); @@ -199,15 +200,15 @@ impl VirtioDevice for Vsock { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { @@ -227,7 +228,7 @@ impl VirtioDevice for Vsock { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let config_len = self.state.config_space.len(); + let config_len = self.config_space.len(); if offset as usize + data_len > config_len { return Err(anyhow!(VirtioError::DevConfigOverflow( offset, @@ -235,8 +236,7 @@ impl VirtioDevice for Vsock { ))); } - self.state.config_space[(offset as usize)..(offset as usize + data_len)] - .copy_from_slice(data); + self.config_space[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); Ok(()) } @@ -271,7 +271,7 @@ impl VirtioDevice for Vsock { Some(backend_) => backend_, }; backend - .set_features(self.state.driver_features) + .set_features(self.base.driver_features) .with_context(|| "Failed to set features for vsock")?; backend .set_mem_table() @@ -293,7 +293,7 @@ impl VirtioDevice for Vsock { format!("Failed to set vring addr for vsock, index: {}", queue_index) })?; backend - .set_vring_base(queue_index, self.state.last_avail_idx[queue_index]) + .set_vring_base(queue_index, self.last_avail_idx[queue_index]) .with_context(|| { format!("Failed to set vring base for vsock, index: {}", queue_index) })?; @@ -332,19 +332,19 @@ impl VirtioDevice for Vsock { let handler = VhostIoHandler { interrupt_cb: interrupt_cb.clone(), host_notifies, - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; } - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); Ok(()) } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(None, &mut self.deactivate_evts)?; + unregister_event_helper(None, &mut self.base.deactivate_evts)?; if !self.disable_irqfd { self.call_events.clear(); } @@ -357,19 +357,26 @@ impl VirtioDevice for Vsock { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } impl StateTransfer for Vsock { fn get_state_vec(&self) -> migration::Result> { - let mut state = self.state; migration::Result::with_context(self.backend.as_ref().unwrap().set_running(false), || { "Failed to set vsock backend stopping" })?; - state.last_avail_idx[0] = self.backend.as_ref().unwrap().get_vring_base(0).unwrap(); - state.last_avail_idx[1] = self.backend.as_ref().unwrap().get_vring_base(1).unwrap(); - state.broken = self.broken.load(Ordering::SeqCst); + + let last_avail_idx_0 = self.backend.as_ref().unwrap().get_vring_base(0).unwrap(); + let last_avail_idx_1 = self.backend.as_ref().unwrap().get_vring_base(1).unwrap(); + let state = VsockState { + device_features: self.base.device_features, + driver_features: self.base.driver_features, + config_space: self.config_space, + last_avail_idx: [last_avail_idx_0, last_avail_idx_1], + broken: self.base.broken.load(Ordering::SeqCst), + }; + migration::Result::with_context(self.backend.as_ref().unwrap().set_running(true), || { "Failed to set vsock backend running" })?; @@ -381,9 +388,13 @@ impl StateTransfer for Vsock { } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - self.state = *VsockState::from_bytes(state) + let state = VsockState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("VSOCK"))?; - self.broken.store(self.state.broken, Ordering::SeqCst); + self.base.device_features = state.device_features; + self.base.driver_features = state.driver_features; + self.base.broken.store(state.broken, Ordering::SeqCst); + self.config_space = state.config_space; + self.last_avail_idx = state.last_avail_idx; Ok(()) } @@ -431,8 +442,8 @@ mod tests { // test vsock new method let mut vsock = vsock_create_instance(); - assert_eq!(vsock.state.device_features, 0); - assert_eq!(vsock.state.driver_features, 0); + assert_eq!(vsock.base.device_features, 0); + assert_eq!(vsock.base.driver_features, 0); assert!(vsock.backend.is_none()); assert_eq!(vsock.device_type(), VIRTIO_TYPE_VSOCK); @@ -440,7 +451,7 @@ mod tests { assert_eq!(vsock.queue_size(), DEFAULT_VIRTQUEUE_SIZE); // test vsock get_device_features - vsock.state.device_features = 0x0123_4567_89ab_cdef; + vsock.base.device_features = 0x0123_4567_89ab_cdef; let features = vsock.get_device_features(0); assert_eq!(features, 0x89ab_cdef); let features = vsock.get_device_features(1); @@ -449,15 +460,15 @@ mod tests { assert_eq!(features, 0); // test vsock set_driver_features - vsock.state.device_features = 0x0123_4567_89ab_cdef; + vsock.base.device_features = 0x0123_4567_89ab_cdef; // check for unsupported feature vsock.set_driver_features(0, 0x7000_0000); assert_eq!(vsock.get_driver_features(0) as u64, 0_u64); - assert_eq!(vsock.state.device_features, 0x0123_4567_89ab_cdef); + assert_eq!(vsock.base.device_features, 0x0123_4567_89ab_cdef); // check for supported feature vsock.set_driver_features(0, 0x8000_0000); assert_eq!(vsock.get_driver_features(0) as u64, 0x8000_0000_u64); - assert_eq!(vsock.state.device_features, 0x0123_4567_89ab_cdef); + assert_eq!(vsock.base.device_features, 0x0123_4567_89ab_cdef); // test vsock read_config let mut buf: [u8; 8] = [0; 8]; diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 8136a8391..cc7853abe 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::VirtioError; use anyhow::{anyhow, bail, Context, Result}; use std::cmp; use std::io::Write; @@ -30,33 +29,33 @@ use crate::VhostUser::client::{ }; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; use crate::{ - virtio_has_feature, BlockState, VirtioDevice, VirtioInterrupt, VIRTIO_BLK_F_BLK_SIZE, - VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + virtio_has_feature, VirtioBase, VirtioBlkConfig, VirtioDevice, VirtioError, VirtioInterrupt, + VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, + VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, + VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; pub struct Block { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the block device. blk_cfg: BlkDevConfig, + /// Config space of the block device. + config_space: VirtioBlkConfig, /// System address space. mem_space: Arc, - /// Status of block device. - state: BlockState, /// Vhost user client client: Option>>, - /// Device is broken or not. - broken: Arc, } impl Block { pub fn new(cfg: &BlkDevConfig, mem_space: &Arc) -> Self { Block { + base: Default::default(), blk_cfg: cfg.clone(), - state: BlockState::default(), + config_space: Default::default(), mem_space: mem_space.clone(), client: None, - broken: Arc::new(AtomicBool::new(false)), } } @@ -115,7 +114,7 @@ impl Block { let config = locked_client .get_virtio_blk_config() .with_context(|| "Failed to get config for vhost-user blk")?; - self.state.config_space = config; + self.config_space = config; } else { bail!( "Failed to get config, spdk doesn't support, spdk protocol features: {:#b}", @@ -135,7 +134,7 @@ impl Block { } if self.blk_cfg.queues > 1 { - self.state.config_space.num_queues = self.blk_cfg.queues; + self.config_space.num_queues = self.blk_cfg.queues; } } else if self.blk_cfg.queues > 1 { bail!( @@ -148,7 +147,7 @@ impl Block { } drop(locked_client); - self.state.device_features = 1_u64 << VIRTIO_F_VERSION_1 + self.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_BLK_F_SIZE_MAX | 1_u64 << VIRTIO_BLK_F_TOPOLOGY | 1_u64 << VIRTIO_BLK_F_BLK_SIZE @@ -157,12 +156,12 @@ impl Block { | 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES | 1_u64 << VIRTIO_BLK_F_SEG_MAX; if self.blk_cfg.read_only { - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_RO; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_RO; }; if self.blk_cfg.queues > 1 { - self.state.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; } - self.state.device_features &= features; + self.base.device_features &= features; Ok(()) } @@ -189,20 +188,20 @@ impl VirtioDevice for Block { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let offset = offset as usize; - let config_slice = self.state.config_space.as_bytes(); + let config_slice = self.config_space.as_bytes(); let config_len = config_slice.len(); if offset >= config_len { return Err(anyhow!(VirtioError::DevConfigOverflow( @@ -221,7 +220,7 @@ impl VirtioDevice for Block { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let offset = offset as usize; - let config_slice = self.state.config_space.as_mut_bytes(); + let config_slice = self.config_space.as_mut_bytes(); let config_len = config_slice.len(); if let Some(end) = offset.checked_add(data.len()) { if end > config_len { @@ -240,7 +239,7 @@ impl VirtioDevice for Block { .with_context(|| "Failed to get client when writing config")? .lock() .unwrap() - .set_virtio_blk_config(self.state.config_space) + .set_virtio_blk_config(self.config_space) .with_context(|| "Failed to set config for vhost-user blk")?; Ok(()) @@ -257,7 +256,7 @@ impl VirtioDevice for Block { Some(client) => client.lock().unwrap(), None => return Err(anyhow!("Failed to get client for vhost-user blk")), }; - client.features = self.state.driver_features; + client.features = self.base.driver_features; client.set_queues(queues); client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; @@ -290,6 +289,6 @@ impl VirtioDevice for Block { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index a45e8fe4e..0dbb97784 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -17,7 +17,7 @@ const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; // The size of queue for virtio fs const VIRTIO_FS_QUEUE_SIZE: u16 = 128; -use crate::VirtioError; +use crate::{VirtioBase, VirtioError}; use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; @@ -102,18 +102,14 @@ impl EventNotifierHelper for VhostUserFsHandler { } pub struct Fs { + base: VirtioBase, fs_cfg: FsConfig, - config: VirtioFsConfig, + config_space: VirtioFsConfig, client: Option>>, - avail_features: u64, - acked_features: u64, mem_space: Arc, /// The notifier events from host. call_events: Vec>, - deactivate_evts: Vec, enable_irqfd: bool, - /// Device is broken or not. - broken: Arc, } impl Fs { @@ -126,16 +122,13 @@ impl Fs { /// `enable_irqfd` - Whether irqfd is enabled on this Fs device. pub fn new(fs_cfg: FsConfig, mem_space: Arc, enable_irqfd: bool) -> Self { Fs { + base: Default::default(), fs_cfg, - config: VirtioFsConfig::default(), + config_space: VirtioFsConfig::default(), client: None, - avail_features: 0_u64, - acked_features: 0_u64, mem_space, call_events: Vec::>::new(), - deactivate_evts: Vec::new(), enable_irqfd, - broken: Arc::new(AtomicBool::new(false)), } } } @@ -143,8 +136,8 @@ impl Fs { impl VirtioDevice for Fs { fn realize(&mut self) -> Result<()> { let tag_bytes_vec = self.fs_cfg.tag.clone().into_bytes(); - self.config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); - self.config.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; + self.config_space.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); + self.config_space.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; let client = VhostUserClient::new( @@ -158,7 +151,7 @@ impl VirtioDevice for Fs { })?; let client = Arc::new(Mutex::new(client)); VhostUserClient::add_event(&client)?; - self.avail_features = client + self.base.device_features = client .lock() .unwrap() .get_features() @@ -181,19 +174,19 @@ impl VirtioDevice for Fs { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.avail_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.acked_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.acked_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.config.as_bytes(); + let config_slice = self.config_space.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); @@ -221,7 +214,7 @@ impl VirtioDevice for Fs { Some(client) => client.lock().unwrap(), None => return Err(anyhow!("Failed to get client for virtio fs")), }; - client.features = self.acked_features; + client.features = self.base.driver_features; client.set_queues(queues); client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; @@ -241,7 +234,7 @@ impl VirtioDevice for Fs { }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.deactivate_evts)?; + register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; } Ok(()) @@ -260,15 +253,15 @@ impl VirtioDevice for Fs { } fn deactivate(&mut self) -> Result<()> { - unregister_event_helper(None, &mut self.deactivate_evts)?; + unregister_event_helper(None, &mut self.base.deactivate_evts)?; self.call_events.clear(); Ok(()) } fn reset(&mut self) -> Result<()> { - self.avail_features = 0_u64; - self.acked_features = 0_u64; - self.config = VirtioFsConfig::default(); + self.base.device_features = 0_u64; + self.base.driver_features = 0_u64; + self.config_space = VirtioFsConfig::default(); let client = match &self.client { None => return Err(anyhow!("Failed to get client when resetting virtio fs")), @@ -285,6 +278,6 @@ impl VirtioDevice for Fs { } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 9a7a3c5e5..0e7382cc7 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -12,7 +12,6 @@ use std::cmp; use std::io::Write; -use std::os::unix::prelude::RawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -28,11 +27,11 @@ use super::super::VhostOps; use super::{VhostBackendType, VhostUserClient}; use crate::error::VirtioError; use crate::{ - device::net::{build_device_config_space, CtrlInfo, VirtioNetState, MAC_ADDR_LEN}, - virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioDevice, VirtioInterrupt, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + device::net::{build_device_config_space, CtrlInfo, MAC_ADDR_LEN}, + virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioBase, VirtioDevice, + VirtioInterrupt, VirtioNetConfig, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; @@ -43,29 +42,26 @@ const QUEUE_NUM_NET: usize = 2; /// Network device structure. pub struct Net { + /// Virtio device base property. + base: VirtioBase, /// Configuration of the vhost user network device. net_cfg: NetworkInterfaceConfig, - /// The status of net device. - state: Arc>, + /// Virtio net configurations. + config_space: Arc>, /// System address space. mem_space: Arc, /// Vhost user client client: Option>>, - /// EventFd for deactivate control Queue. - deactivate_evts: Vec, - /// Device is broken or not. - broken: Arc, } impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { Net { + base: Default::default(), net_cfg: cfg.clone(), - state: Arc::new(Mutex::new(VirtioNetState::default())), + config_space: Default::default(), mem_space: mem_space.clone(), client: None, - deactivate_evts: Vec::new(), - broken: Arc::new(AtomicBool::new(false)), } } @@ -80,10 +76,11 @@ impl Net { } None => return Err(anyhow!("Failed to get client when stopping event")), }; - if ((self.state.lock().unwrap().driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) - && self.net_cfg.mq - { - unregister_event_helper(self.net_cfg.iothread.as_ref(), &mut self.deactivate_evts)?; + if ((self.base.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq { + unregister_event_helper( + self.net_cfg.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; } Ok(()) @@ -91,10 +88,10 @@ impl Net { fn clean_up(&mut self) -> Result<()> { self.delete_event()?; - let mut locked_state = self.state.lock().unwrap(); - locked_state - .as_mut_bytes() - .copy_from_slice(&[0_u8; std::mem::size_of::()]); + self.base.device_features = 0; + self.base.driver_features = 0; + self.base.broken.store(false, Ordering::SeqCst); + self.config_space = Default::default(); self.client = None; Ok(()) @@ -121,8 +118,7 @@ impl VirtioDevice for Net { let client = Arc::new(Mutex::new(client)); VhostUserClient::add_event(&client)?; - let mut locked_state = self.state.lock().unwrap(); - locked_state.device_features = client + self.base.device_features = client .lock() .unwrap() .get_features() @@ -136,23 +132,24 @@ impl VirtioDevice for Net { | 1 << VIRTIO_NET_F_HOST_UFO | 1 << VIRTIO_NET_F_MRG_RXBUF | 1 << VIRTIO_F_RING_EVENT_IDX; - locked_state.device_features &= features; + self.base.device_features &= features; + + let mut locked_config = self.config_space.lock().unwrap(); let queue_pairs = self.net_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) { - locked_state.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; - locked_state.device_features |= 1 << VIRTIO_NET_F_MQ; - locked_state.config_space.max_virtqueue_pairs = queue_pairs; + self.base.device_features |= 1 << VIRTIO_NET_F_CTRL_VQ; + self.base.device_features |= 1 << VIRTIO_NET_F_MQ; + locked_config.max_virtqueue_pairs = queue_pairs; } self.client = Some(client); if let Some(mac) = &self.net_cfg.mac { - locked_state.device_features |= - build_device_config_space(&mut locked_state.config_space, mac); + self.base.device_features |= build_device_config_space(&mut locked_config, mac); } Ok(()) @@ -176,20 +173,20 @@ impl VirtioDevice for Net { } fn get_device_features(&self, features_select: u32) -> u32 { - read_u32(self.state.lock().unwrap().device_features, features_select) + read_u32(self.base.device_features, features_select) } fn set_driver_features(&mut self, page: u32, value: u32) { - self.state.lock().unwrap().driver_features = self.checked_driver_features(page, value); + self.base.driver_features = self.checked_driver_features(page, value); } fn get_driver_features(&self, features_select: u32) -> u32 { - read_u32(self.state.lock().unwrap().driver_features, features_select) + read_u32(self.base.driver_features, features_select) } fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let locked_state = self.state.lock().unwrap(); - let config_slice = locked_state.config_space.as_bytes(); + let config_space = self.config_space.lock().unwrap(); + let config_slice = config_space.as_bytes(); let config_size = config_slice.len() as u64; if offset >= config_size { return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); @@ -203,9 +200,9 @@ impl VirtioDevice for Net { fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let data_len = data.len(); - let mut locked_state = self.state.lock().unwrap(); - let driver_features = locked_state.driver_features; - let config_slice = locked_state.config_space.as_mut_bytes(); + let mut config_space = self.config_space.lock().unwrap(); + let driver_features = self.base.driver_features; + let config_slice = config_space.as_mut_bytes(); if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) @@ -227,20 +224,20 @@ impl VirtioDevice for Net { queue_evts: Vec>, ) -> Result<()> { let queue_num = queues.len(); - let driver_features = self.state.lock().unwrap().driver_features; + let driver_features = self.base.driver_features; let has_control_queue = (driver_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0) && (queue_num % 2 != 0); if has_control_queue { let ctrl_queue = queues[queue_num - 1].clone(); let ctrl_queue_evt = queue_evts[queue_num - 1].clone(); - let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.state.clone()))); + let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.config_space.clone()))); let ctrl_handler = NetCtrlHandler { ctrl: CtrlVirtio::new(ctrl_queue, ctrl_queue_evt, ctrl_info), mem_space, interrupt_cb: interrupt_cb.clone(), driver_features, - device_broken: self.broken.clone(), + device_broken: self.base.broken.clone(), taps: None, }; @@ -249,7 +246,7 @@ impl VirtioDevice for Net { register_event_helper( notifiers, self.net_cfg.iothread.as_ref(), - &mut self.deactivate_evts, + &mut self.base.deactivate_evts, )?; } @@ -268,7 +265,7 @@ impl VirtioDevice for Net { client.set_queue_evts(&queue_evts); } client.activate_vhost_user()?; - self.broken.store(false, Ordering::SeqCst); + self.base.broken.store(false, Ordering::SeqCst); Ok(()) } @@ -300,13 +297,10 @@ impl VirtioDevice for Net { } fn has_control_queue(&mut self) -> bool { - virtio_has_feature( - self.state.lock().unwrap().device_features, - VIRTIO_NET_F_CTRL_VQ, - ) + virtio_has_feature(self.base.device_features, VIRTIO_NET_F_CTRL_VQ) } fn get_device_broken(&self) -> &Arc { - &self.broken + &self.base.broken } } -- Gitee From c16b6734daf7c6486fef34761da1944c9b19bea9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 10:19:22 +0800 Subject: [PATCH 1247/2187] Refactory: Rename some methods of VirtioDevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、get_ prefix is not necessary when get the member. 2、append max to queue_size. Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 22 ++++++++++---------- virtio/src/device/block.rs | 26 ++++++++++++------------ virtio/src/device/gpu.rs | 6 +++--- virtio/src/device/net.rs | 14 ++++++------- virtio/src/device/rng.rs | 22 ++++++++++---------- virtio/src/device/scsi_cntlr.rs | 8 ++++---- virtio/src/device/serial.rs | 12 +++++------ virtio/src/lib.rs | 12 +++++------ virtio/src/transport/virtio_mmio.rs | 17 +++++++--------- virtio/src/transport/virtio_pci.rs | 31 +++++++++++++---------------- virtio/src/vhost/kernel/net.rs | 14 ++++++------- virtio/src/vhost/kernel/vsock.rs | 18 ++++++++--------- virtio/src/vhost/user/block.rs | 6 +++--- virtio/src/vhost/user/fs.rs | 6 +++--- virtio/src/vhost/user/net.rs | 6 +++--- 15 files changed, 107 insertions(+), 113 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index fef8ea4c7..a35a25b4a 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1014,11 +1014,11 @@ impl VirtioDevice for Balloon { queue_num } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -1026,7 +1026,7 @@ impl VirtioDevice for Balloon { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -1275,28 +1275,28 @@ mod tests { let feature = (1u64 << VIRTIO_F_VERSION_1) | (1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM); assert_eq!(bln.base.device_features, feature); - let fts = bln.get_device_features(0); + let fts = bln.device_features(0); assert_eq!(fts, feature as u32); - let fts = bln.get_device_features(1); + let fts = bln.device_features(1); assert_eq!(fts, (feature >> 32) as u32); bln.base.driver_features = 0; bln.base.device_features = 1 | 1 << 32; bln.set_driver_features(0, 1); assert_eq!(bln.base.driver_features, 1); - assert_eq!(bln.base.driver_features, bln.get_driver_features(0) as u64); + assert_eq!(bln.base.driver_features, bln.driver_features(0) as u64); bln.base.driver_features = 1 << 32; bln.set_driver_features(1, 1); assert_eq!(bln.base.driver_features, 1 << 32); assert_eq!( bln.base.driver_features, - (bln.get_driver_features(1) as u64) << 32 + (bln.driver_features(1) as u64) << 32 ); // Test realize function. bln.realize().unwrap(); assert_eq!(bln.device_type(), 5); assert_eq!(bln.queue_num(), 2); - assert_eq!(bln.queue_size(), QUEUE_SIZE); + assert_eq!(bln.queue_size_max(), QUEUE_SIZE); // Test methods of balloon. let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); @@ -1664,16 +1664,16 @@ mod tests { | (1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM | 1u64 << VIRTIO_BALLOON_F_REPORTING); assert_eq!(bln.base.device_features, feature); - let fts = bln.get_device_features(0); + let fts = bln.device_features(0); assert_eq!(fts, feature as u32); - let fts = bln.get_device_features(1); + let fts = bln.device_features(1); assert_eq!(fts, (feature >> 32) as u32); // Test realize function. bln.realize().unwrap(); assert_eq!(bln.device_type(), 5); assert_eq!(bln.queue_num(), 3); - assert_eq!(bln.queue_size(), QUEUE_SIZE); + assert_eq!(bln.queue_size_max(), QUEUE_SIZE); // Test methods of balloon. let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 5913ef872..1c6b927b1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -989,7 +989,7 @@ impl Block { let num_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; self.config_space.capacity = num_sectors; // seg_max = queue_size - 2: 32bits - self.config_space.seg_max = self.queue_size() as u32 - 2; + self.config_space.seg_max = self.queue_size_max() as u32 - 2; if self.blk_cfg.discard { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; @@ -1103,11 +1103,11 @@ impl VirtioDevice for Block { self.blk_cfg.queues as usize } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { self.blk_cfg.queue_size } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -1115,7 +1115,7 @@ impl VirtioDevice for Block { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -1427,7 +1427,7 @@ mod tests { assert_eq!(block.device_type(), VIRTIO_TYPE_BLOCK); assert_eq!(block.queue_num(), QUEUE_NUM_BLK); - assert_eq!(block.queue_size(), DEFAULT_VIRTQUEUE_SIZE); + assert_eq!(block.queue_size_max(), DEFAULT_VIRTQUEUE_SIZE); } // Test `write_config` and `read_config`. The main contests include: compare expect data and @@ -1470,15 +1470,15 @@ mod tests { let page = 0_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.base.driver_features, 0_u64); - assert_eq!(block.get_driver_features(page) as u64, 0_u64); - assert_eq!(block.get_device_features(0_u32), 0_u32); + assert_eq!(block.driver_features(page) as u64, 0_u64); + assert_eq!(block.device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.base.driver_features, 0_u64); - assert_eq!(block.get_driver_features(page) as u64, 0_u64); - assert_eq!(block.get_device_features(1_u32), 0_u32); + assert_eq!(block.driver_features(page) as u64, 0_u64); + assert_eq!(block.device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. @@ -1492,11 +1492,11 @@ mod tests { (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) ); assert_eq!( - block.get_driver_features(page) as u64, + block.driver_features(page) as u64, (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) ); assert_eq!( - block.get_device_features(page), + block.device_features(page), (1_u32 << VIRTIO_F_RING_INDIRECT_DESC) ); block.base.driver_features = 0; @@ -1506,8 +1506,8 @@ mod tests { let page = 0_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.base.driver_features, 0); - assert_eq!(block.get_driver_features(page), 0); - assert_eq!(block.get_device_features(page), 0_u32); + assert_eq!(block.driver_features(page), 0); + assert_eq!(block.device_features(page), 0_u32); block.base.driver_features = 0; } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 63d30811a..25c7101f2 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1515,11 +1515,11 @@ impl VirtioDevice for Gpu { QUEUE_NUM_GPU } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -1527,7 +1527,7 @@ impl VirtioDevice for Gpu { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 1997563d5..e194a62f1 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1454,11 +1454,11 @@ impl VirtioDevice for Net { } } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { self.net_cfg.queue_size } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -1466,7 +1466,7 @@ impl VirtioDevice for Net { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -1547,7 +1547,7 @@ impl VirtioDevice for Net { } // The features about offload is included in bits 0 to 31. - let features = self.get_driver_features(0_u32); + let features = self.driver_features(0_u32); let flags = get_tap_offload_flags(features as u64); let mut senders = Vec::new(); @@ -1580,7 +1580,7 @@ impl VirtioDevice for Net { device_broken: self.base.broken.clone(), is_listening: true, ctrl_info: ctrl_info.clone(), - queue_size: self.queue_size(), + queue_size: self.queue_size_max(), }; if let Some(tap) = &handler.tap { handler.tap_fd = tap.as_raw_fd(); @@ -1610,7 +1610,7 @@ impl VirtioDevice for Net { // Set tap offload. // The features about offload is included in bits 0 to 31. - let features = self.get_driver_features(0_u32); + let features = self.driver_features(0_u32); let flags = get_tap_offload_flags(features as u64); if let Some(taps) = &self.taps { for (_, tap) in taps.iter().enumerate() { @@ -1729,7 +1729,7 @@ mod tests { net.realize().unwrap(); assert_eq!(net.device_type(), 1); assert_eq!(net.queue_num(), 3); - assert_eq!(net.queue_size(), 256); + assert_eq!(net.queue_size_max(), 256); // test read_config and write_config method let write_data: Vec = vec![7; 4]; diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 9e9b7a684..1fd2603f5 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -272,11 +272,11 @@ impl VirtioDevice for Rng { QUEUE_NUM_RNG } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -284,7 +284,7 @@ impl VirtioDevice for Rng { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -429,7 +429,7 @@ mod tests { assert_eq!(rng.rng_cfg.bytes_per_sec, Some(64)); assert_eq!(rng.queue_num(), QUEUE_NUM_RNG); - assert_eq!(rng.queue_size(), DEFAULT_VIRTQUEUE_SIZE); + assert_eq!(rng.queue_size_max(), DEFAULT_VIRTQUEUE_SIZE); assert_eq!(rng.device_type(), VIRTIO_TYPE_RNG); } @@ -454,15 +454,15 @@ mod tests { let page = 0_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.base.driver_features, 0_u64); - assert_eq!(rng.get_driver_features(page) as u64, 0_u64); - assert_eq!(rng.get_device_features(0_u32), 0_u32); + assert_eq!(rng.driver_features(page) as u64, 0_u64); + assert_eq!(rng.device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.base.driver_features, 0_u64); - assert_eq!(rng.get_driver_features(page) as u64, 0_u64); - assert_eq!(rng.get_device_features(1_u32), 0_u32); + assert_eq!(rng.driver_features(page) as u64, 0_u64); + assert_eq!(rng.device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. @@ -476,11 +476,11 @@ mod tests { (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) ); assert_eq!( - rng.get_driver_features(page) as u64, + rng.driver_features(page) as u64, (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) ); assert_eq!( - rng.get_device_features(page), + rng.device_features(page), (1_u32 << VIRTIO_F_RING_INDIRECT_DESC) ); rng.base.driver_features = 0; @@ -490,7 +490,7 @@ mod tests { let page = 0_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.base.driver_features, 0); - assert_eq!(rng.get_device_features(page), 0_u32); + assert_eq!(rng.device_features(page), 0_u32); rng.base.driver_features = 0; } diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index e6d729db7..bc3987009 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -154,7 +154,7 @@ impl VirtioDevice for ScsiCntlr { // cmd_per_lun: maximum number of linked commands can be sent to one LUN. 32bit. self.config_space.cmd_per_lun = 128; // seg_max: queue size - 2, 32 bit. - self.config_space.seg_max = self.queue_size() as u32 - 2; + self.config_space.seg_max = self.queue_size_max() as u32 - 2; self.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; self.config_space.max_lun = VIRTIO_SCSI_MAX_LUN as u32; // num_queues: request queues number. @@ -180,11 +180,11 @@ impl VirtioDevice for ScsiCntlr { self.config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { self.config.queue_size } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -192,7 +192,7 @@ impl VirtioDevice for ScsiCntlr { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 690ed2e1b..ee5626c28 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -228,11 +228,11 @@ impl VirtioDevice for Serial { self.max_nr_ports as usize * 2 + 2 } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -240,7 +240,7 @@ impl VirtioDevice for Serial { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -944,13 +944,13 @@ mod tests { let page = 0_u32; serial.set_driver_features(page, driver_feature); assert_eq!(serial.base.driver_features, 0_u64); - assert_eq!(serial.get_driver_features(page) as u64, 0_u64); + assert_eq!(serial.driver_features(page) as u64, 0_u64); let driver_feature: u32 = 0xFF; let page = 1_u32; serial.set_driver_features(page, driver_feature); assert_eq!(serial.base.driver_features, 0_u64); - assert_eq!(serial.get_driver_features(page) as u64, 0_u64); + assert_eq!(serial.driver_features(page) as u64, 0_u64); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. @@ -963,7 +963,7 @@ mod tests { (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); assert_eq!( - serial.get_driver_features(page) as u64, + serial.driver_features(page) as u64, (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); serial.base.driver_features = 0; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index eef3066eb..b03a79b80 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -346,15 +346,15 @@ pub trait VirtioDevice: Send + AsAny { fn queue_num(&self) -> usize; /// Get the queue size of virtio device. - fn queue_size(&self) -> u16; + fn queue_size_max(&self) -> u16; /// Get device features from host. - fn get_device_features(&self, features_select: u32) -> u32; + fn device_features(&self, features_select: u32) -> u32; /// Get checked driver features before set the value at the page. fn checked_driver_features(&mut self, page: u32, value: u32) -> u64 { let mut v = value; - let unsupported_features = value & !self.get_device_features(page); + let unsupported_features = value & !self.device_features(page); if unsupported_features != 0 { warn!( "Receive acknowledge request with unknown feature: {:x}", @@ -363,9 +363,9 @@ pub trait VirtioDevice: Send + AsAny { v &= !unsupported_features; } if page == 0 { - (self.get_driver_features(1) as u64) << 32 | (v as u64) + (self.driver_features(1) as u64) << 32 | (v as u64) } else { - (v as u64) << 32 | (self.get_driver_features(0) as u64) + (v as u64) << 32 | (self.driver_features(0) as u64) } } @@ -373,7 +373,7 @@ pub trait VirtioDevice: Send + AsAny { fn set_driver_features(&mut self, page: u32, value: u32); /// Get driver features by guest. - fn get_driver_features(&self, features_select: u32) -> u32; + fn driver_features(&self, features_select: u32) -> u32; /// Read data of config from guest. fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()>; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 33230d287..6a38fbc71 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -142,7 +142,7 @@ impl VirtioMmioCommonConfig { pub fn new(device: &Arc>) -> Self { let locked_device = device.lock().unwrap(); let mut queues_config = [QueueConfig::default(); 8]; - let queue_size = locked_device.queue_size(); + let queue_size = locked_device.queue_size_max(); let queue_num = locked_device.queue_num(); for queue_config in queues_config.iter_mut().take(queue_num) { *queue_config = QueueConfig::new(queue_size); @@ -217,10 +217,7 @@ impl VirtioMmioCommonConfig { DEVICE_ID_REG => device.lock().unwrap().device_type(), VENDOR_ID_REG => VENDOR_ID, DEVICE_FEATURES_REG => { - let mut features = device - .lock() - .unwrap() - .get_device_features(self.features_select); + let mut features = device.lock().unwrap().device_features(self.features_select); if self.features_select == 1 { features |= 0x1; // enable support of VirtIO Version 1 } @@ -408,7 +405,7 @@ impl VirtioMmioDevice { let queues_config = &mut locked_state.config_space.queues_config[0..queue_num]; let dev_lock = self.device.lock().unwrap(); let features = - (dev_lock.get_driver_features(1) as u64) << 32 | dev_lock.get_driver_features(0) as u64; + (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = dev_lock.get_device_broken(); for q_config in queues_config.iter_mut() { @@ -679,7 +676,7 @@ impl StateTransfer for VirtioMmioDevice { .to_vec(); let dev_lock = self.device.lock().unwrap(); let features = - (dev_lock.get_driver_features(1) as u64) << 32 | dev_lock.get_driver_features(0) as u64; + (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = dev_lock.get_device_broken(); self.queues = queue_states .iter_mut() @@ -809,11 +806,11 @@ mod tests { QUEUE_NUM } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { QUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -821,7 +818,7 @@ mod tests { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 39547e6db..9361f4bd7 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -256,10 +256,7 @@ impl VirtioPciCommonConfig { COMMON_DFSELECT_REG => self.features_select, COMMON_DF_REG => { if self.features_select < MAX_FEATURES_SELECT_NUM { - device - .lock() - .unwrap() - .get_device_features(self.features_select) + device.lock().unwrap().device_features(self.features_select) } else { 0 } @@ -270,7 +267,7 @@ impl VirtioPciCommonConfig { device .lock() .unwrap() - .get_driver_features(self.acked_features_select) + .driver_features(self.acked_features_select) } else { 0 } @@ -355,7 +352,7 @@ impl VirtioPciCommonConfig { .set_driver_features(self.acked_features_select, value); if self.acked_features_select == 1 { - let features = (device.lock().unwrap().get_driver_features(1) as u64) << 32; + let features = (device.lock().unwrap().driver_features(1) as u64) << 32; if virtio_has_feature(features, VIRTIO_F_RING_PACKED) { self.queue_type = QUEUE_TYPE_PACKED_VRING; } else { @@ -370,7 +367,7 @@ impl VirtioPciCommonConfig { } COMMON_STATUS_REG => { if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { - let features = (device.lock().unwrap().get_driver_features(1) as u64) << 32; + let features = (device.lock().unwrap().driver_features(1) as u64) << 32; if !virtio_has_feature(features, VIRTIO_F_VERSION_1) { error!( "Device is modern only, but the driver not support VIRTIO_F_VERSION_1" @@ -616,7 +613,7 @@ impl VirtioPciDevice { multi_func: bool, ) -> Self { let queue_num = device.lock().unwrap().queue_num(); - let queue_size = device.lock().unwrap().queue_size(); + let queue_size = device.lock().unwrap().queue_size_max(); VirtioPciDevice { name, @@ -731,7 +728,7 @@ impl VirtioPciDevice { let mut locked_queues = self.queues.lock().unwrap(); let dev_lock = self.device.lock().unwrap(); let features = - (dev_lock.get_driver_features(1) as u64) << 32 | dev_lock.get_driver_features(0) as u64; + (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = dev_lock.get_device_broken(); for q_config in queues_config.iter_mut() { if !q_config.ready { @@ -1397,8 +1394,8 @@ impl StateTransfer for VirtioPciDevice { let queue_type = self.common_config.lock().unwrap().queue_type; let mut locked_queues = self.queues.lock().unwrap(); let dev_lock = self.device.lock().unwrap(); - let features = (dev_lock.get_driver_features(1) as u64) << 32 - | dev_lock.get_driver_features(0) as u64; + let features = + (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = dev_lock.get_device_broken(); for queue_state in pci_state.queues_config[0..pci_state.queue_num].iter_mut() { queue_state.set_addr_cache( @@ -1502,11 +1499,11 @@ mod tests { VIRTIO_DEVICE_QUEUE_NUM } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { VIRTIO_DEVICE_QUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -1514,7 +1511,7 @@ mod tests { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -1578,7 +1575,7 @@ mod tests { false, ); - let queue_size = dev.lock().unwrap().queue_size(); + let queue_size = dev.lock().unwrap().queue_size_max(); let queue_num = dev.lock().unwrap().queue_num(); let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); @@ -1617,7 +1614,7 @@ mod tests { fn test_common_config_queue() { let virtio_dev: Arc> = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let queue_size = virtio_dev.lock().unwrap().queue_size(); + let queue_size = virtio_dev.lock().unwrap().queue_size_max(); let queue_num = virtio_dev.lock().unwrap().queue_num(); let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); @@ -1645,7 +1642,7 @@ mod tests { fn test_common_config_queue_error() { let virtio_dev: Arc> = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let queue_size = virtio_dev.lock().unwrap().queue_size(); + let queue_size = virtio_dev.lock().unwrap().queue_size_max(); let queue_num = virtio_dev.lock().unwrap().queue_num(); let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); let sys_mem = AddressSpace::new( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 6a3c7a6c1..e29300369 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -192,11 +192,11 @@ impl VirtioDevice for Net { } } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { self.net_cfg.queue_size } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -204,7 +204,7 @@ impl VirtioDevice for Net { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -523,16 +523,16 @@ mod tests { let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); - assert_eq!(vhost_net.get_driver_features(page) as u64, 0_u64); - let new_page = vhost_net.get_device_features(page); + assert_eq!(vhost_net.driver_features(page) as u64, 0_u64); + let new_page = vhost_net.device_features(page); assert_eq!(new_page, page); vhost_net.base.device_features = 0xffff_ffff_ffff_ffff; let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); - assert_eq!(vhost_net.get_driver_features(page) as u64, 0xff_u64); - let new_page = vhost_net.get_device_features(page); + assert_eq!(vhost_net.driver_features(page) as u64, 0xff_u64); + let new_page = vhost_net.device_features(page); assert_ne!(new_page, page); // test for read/write_config diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index dfbc3514e..59b8cf354 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -195,11 +195,11 @@ impl VirtioDevice for Vsock { QUEUE_NUM_VSOCK } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { DEFAULT_VIRTQUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -207,7 +207,7 @@ impl VirtioDevice for Vsock { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } @@ -448,26 +448,26 @@ mod tests { assert_eq!(vsock.device_type(), VIRTIO_TYPE_VSOCK); assert_eq!(vsock.queue_num(), QUEUE_NUM_VSOCK); - assert_eq!(vsock.queue_size(), DEFAULT_VIRTQUEUE_SIZE); + assert_eq!(vsock.queue_size_max(), DEFAULT_VIRTQUEUE_SIZE); // test vsock get_device_features vsock.base.device_features = 0x0123_4567_89ab_cdef; - let features = vsock.get_device_features(0); + let features = vsock.device_features(0); assert_eq!(features, 0x89ab_cdef); - let features = vsock.get_device_features(1); + let features = vsock.device_features(1); assert_eq!(features, 0x0123_4567); - let features = vsock.get_device_features(3); + let features = vsock.device_features(3); assert_eq!(features, 0); // test vsock set_driver_features vsock.base.device_features = 0x0123_4567_89ab_cdef; // check for unsupported feature vsock.set_driver_features(0, 0x7000_0000); - assert_eq!(vsock.get_driver_features(0) as u64, 0_u64); + assert_eq!(vsock.driver_features(0) as u64, 0_u64); assert_eq!(vsock.base.device_features, 0x0123_4567_89ab_cdef); // check for supported feature vsock.set_driver_features(0, 0x8000_0000); - assert_eq!(vsock.get_driver_features(0) as u64, 0x8000_0000_u64); + assert_eq!(vsock.driver_features(0) as u64, 0x8000_0000_u64); assert_eq!(vsock.base.device_features, 0x0123_4567_89ab_cdef); // test vsock read_config diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index cc7853abe..9f568a41c 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -183,11 +183,11 @@ impl VirtioDevice for Block { self.blk_cfg.queues as usize } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { self.blk_cfg.queue_size } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -195,7 +195,7 @@ impl VirtioDevice for Block { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 0dbb97784..601379d41 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -169,11 +169,11 @@ impl VirtioDevice for Fs { VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { VIRTIO_FS_QUEUE_SIZE } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -181,7 +181,7 @@ impl VirtioDevice for Fs { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 0e7382cc7..5bd417505 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -168,11 +168,11 @@ impl VirtioDevice for Net { } } - fn queue_size(&self) -> u16 { + fn queue_size_max(&self) -> u16 { self.net_cfg.queue_size } - fn get_device_features(&self, features_select: u32) -> u32 { + fn device_features(&self, features_select: u32) -> u32 { read_u32(self.base.device_features, features_select) } @@ -180,7 +180,7 @@ impl VirtioDevice for Net { self.base.driver_features = self.checked_driver_features(page, value); } - fn get_driver_features(&self, features_select: u32) -> u32 { + fn driver_features(&self, features_select: u32) -> u32 { read_u32(self.base.driver_features, features_select) } -- Gitee From da6adef7a9897042e225e9d0a40522762aae82e3 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 20:05:25 +0800 Subject: [PATCH 1248/2187] Refactory: Inherint methods of VirtioDevice Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 38 ++++++++-------------- virtio/src/device/block.rs | 30 ++++++----------- virtio/src/device/gpu.rs | 31 ++++++------------ virtio/src/device/net.rs | 31 ++++++------------ virtio/src/device/rng.rs | 31 ++++++------------ virtio/src/device/scsi_cntlr.rs | 31 ++++++------------ virtio/src/device/serial.rs | 30 ++++++----------- virtio/src/lib.rs | 50 +++++++++++++++++++++-------- virtio/src/transport/virtio_mmio.rs | 35 +++++++------------- virtio/src/transport/virtio_pci.rs | 31 ++++++------------ virtio/src/vhost/kernel/net.rs | 33 ++++++------------- virtio/src/vhost/kernel/vsock.rs | 33 ++++++------------- virtio/src/vhost/user/block.rs | 32 ++++++------------ virtio/src/vhost/user/fs.rs | 32 ++++++------------ virtio/src/vhost/user/net.rs | 33 ++++++------------- 15 files changed, 173 insertions(+), 328 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index a35a25b4a..f72a56693 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -38,7 +38,7 @@ use util::{ loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }, - num_ops::{read_u32, round_down}, + num_ops::round_down, offset_of, seccomp::BpfRule, unix::host_page_size, @@ -911,11 +911,11 @@ impl Balloon { device_features |= 1u64 << VIRTIO_BALLOON_F_MESSAGE_VQ; } + let mut base = VirtioBase::new(VIRTIO_TYPE_BALLOON); + base.device_features = device_features; + Balloon { - base: VirtioBase { - device_features, - ..Default::default() - }, + base, actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, interrupt_cb: None, @@ -992,6 +992,14 @@ impl Balloon { } impl VirtioDevice for Balloon { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { self.mem_space .register_listener(self.mem_info.clone()) @@ -999,10 +1007,6 @@ impl VirtioDevice for Balloon { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_BALLOON - } - fn queue_num(&self) -> usize { let mut queue_num = QUEUE_NUM_BALLOON; if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_REPORTING) { @@ -1018,18 +1022,6 @@ impl VirtioDevice for Balloon { DEFAULT_VIRTQUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let new_config = VirtioBalloonConfig { num_pages: self.num_pages, @@ -1160,10 +1152,6 @@ impl VirtioDevice for Balloon { } Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } pub fn qmp_balloon(target: u64) -> bool { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 1c6b927b1..0d60ebb28 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -56,7 +56,6 @@ use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; use util::offset_of; /// Number of virtqueues. @@ -976,6 +975,7 @@ impl Block { drive_files: Arc>>, ) -> Block { Self { + base: VirtioBase::new(VIRTIO_TYPE_BLOCK), blk_cfg, req_align: 1, buf_align: 1, @@ -1029,6 +1029,14 @@ impl Block { } impl VirtioDevice for Block { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { // if iothread not found, return err if self.blk_cfg.iothread.is_some() @@ -1095,10 +1103,6 @@ impl VirtioDevice for Block { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_BLOCK - } - fn queue_num(&self) -> usize { self.blk_cfg.queues as usize } @@ -1107,18 +1111,6 @@ impl VirtioDevice for Block { self.blk_cfg.queue_size } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_len = self.get_blk_config_size(); let read_end = offset as usize + data.len(); @@ -1306,10 +1298,6 @@ impl VirtioDevice for Block { Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } // SAFETY: Send and Sync is not auto-implemented for `Sender` type. diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 25c7101f2..c2f355751 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -15,7 +15,6 @@ use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::slice::from_raw_parts_mut; -use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; @@ -39,7 +38,6 @@ use util::edid::EdidInfo; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; use util::pixman::{ pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits, pixman_image_get_data, pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, @@ -1447,6 +1445,7 @@ unsafe impl Send for Gpu {} impl Gpu { pub fn new(cfg: GpuDevConfig) -> Gpu { Self { + base: VirtioBase::new(VIRTIO_TYPE_GPU), cfg, ..Default::default() } @@ -1459,6 +1458,14 @@ impl Gpu { } impl VirtioDevice for Gpu { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { if self.cfg.max_outputs > VIRTIO_GPU_MAX_OUTPUTS as u32 { bail!( @@ -1507,10 +1514,6 @@ impl VirtioDevice for Gpu { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_GPU - } - fn queue_num(&self) -> usize { QUEUE_NUM_GPU } @@ -1519,18 +1522,6 @@ impl VirtioDevice for Gpu { DEFAULT_VIRTQUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_space = *self.config_space.lock().unwrap(); let config_slice = config_space.as_bytes(); @@ -1633,8 +1624,4 @@ impl VirtioDevice for Gpu { } unregister_event_helper(None, &mut self.base.deactivate_evts) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index e194a62f1..425141286 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -59,7 +59,7 @@ use util::loop_context::gen_delete_notifiers; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::{read_u32, str_to_usize}; +use util::num_ops::str_to_usize; use util::tap::{ Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, }; @@ -1183,6 +1183,7 @@ pub struct Net { impl Net { pub fn new(net_cfg: NetworkInterfaceConfig) -> Self { Self { + base: VirtioBase::new(VIRTIO_TYPE_NET), net_cfg, ..Default::default() } @@ -1348,6 +1349,14 @@ fn get_tap_offload_flags(features: u64) -> u32 { } impl VirtioDevice for Net { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { // if iothread not found, return err if self.net_cfg.iothread.is_some() @@ -1442,10 +1451,6 @@ impl VirtioDevice for Net { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_NET - } - fn queue_num(&self) -> usize { if self.net_cfg.mq { (self.net_cfg.queues + 1) as usize @@ -1458,18 +1463,6 @@ impl VirtioDevice for Net { self.net_cfg.queue_size } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); let config_slice = config_space.as_bytes(); @@ -1661,10 +1654,6 @@ impl VirtioDevice for Net { self.ctrl_info = None; Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } // SAFETY: Send and Sync is not auto-implemented for `Sender` type. diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 1fd2603f5..2d681ca27 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -16,7 +16,6 @@ use std::os::unix::fs::FileTypeExt; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::rc::Rc; -use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -32,7 +31,6 @@ use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; use log::error; use migration_derive::{ByteCode, Desc}; @@ -227,6 +225,7 @@ pub struct Rng { impl Rng { pub fn new(rng_cfg: RngConfig) -> Self { Rng { + base: VirtioBase::new(VIRTIO_TYPE_RNG), rng_cfg, ..Default::default() } @@ -253,6 +252,14 @@ impl Rng { } impl VirtioDevice for Rng { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { self.check_random_file() .with_context(|| "Failed to check random file")?; @@ -264,10 +271,6 @@ impl VirtioDevice for Rng { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_RNG - } - fn queue_num(&self) -> usize { QUEUE_NUM_RNG } @@ -276,18 +279,6 @@ impl VirtioDevice for Rng { DEFAULT_VIRTQUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, _data: &mut [u8]) -> Result<()> { bail!( "Reading device config space for rng is not supported, offset: {}", @@ -336,10 +327,6 @@ impl VirtioDevice for Rng { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.base.deactivate_evts) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } impl StateTransfer for Rng { diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index bc3987009..35ce774c1 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -43,7 +43,6 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; /// Virtio Scsi Controller has 1 ctrl queue, 1 event queue and at least 1 cmd queue. const SCSI_CTRL_QUEUE_NUM: usize = 1; @@ -122,6 +121,7 @@ pub struct ScsiCntlr { impl ScsiCntlr { pub fn new(config: ScsiCntlrConfig) -> ScsiCntlr { Self { + base: VirtioBase::new(VIRTIO_TYPE_SCSI), config, ..Default::default() } @@ -137,6 +137,14 @@ impl ScsiCntlr { } impl VirtioDevice for ScsiCntlr { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { // If iothread not found, return err. if self.config.iothread.is_some() @@ -149,7 +157,6 @@ impl VirtioDevice for ScsiCntlr { } self.config_space.num_queues = self.config.queues; - self.config_space.max_sectors = 0xFFFF_u32; // cmd_per_lun: maximum number of linked commands can be sent to one LUN. 32bit. self.config_space.cmd_per_lun = 128; @@ -171,10 +178,6 @@ impl VirtioDevice for ScsiCntlr { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_SCSI - } - fn queue_num(&self) -> usize { // Note: self.config.queues <= MAX_VIRTIO_QUEUE(32). self.config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM @@ -184,18 +187,6 @@ impl VirtioDevice for ScsiCntlr { self.config.queue_size } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.config_space.as_bytes(); let config_len = config_slice.len() as u64; @@ -329,10 +320,6 @@ impl VirtioDevice for ScsiCntlr { } Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } fn build_event_notifier(fd: RawFd, handler: Rc) -> EventNotifier { diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index ee5626c28..a5ca8784a 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -43,7 +43,6 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; // Buffer size for chardev backend. const BUF_SIZE: usize = 4096; @@ -148,6 +147,7 @@ impl Serial { /// * `serial_cfg` - Device configuration set by user. pub fn new(serial_cfg: VirtioSerialInfo) -> Self { Serial { + base: VirtioBase::new(VIRTIO_TYPE_CONSOLE), config_space: VirtioConsoleConfig::new(serial_cfg.max_ports), max_nr_ports: serial_cfg.max_ports, ..Default::default() @@ -210,6 +210,14 @@ pub fn find_port_by_nr( } impl VirtioDevice for Serial { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { self.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE @@ -218,10 +226,6 @@ impl VirtioDevice for Serial { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_CONSOLE - } - fn queue_num(&self) -> usize { // Each port has 2 queues(receiveq/transmitq). // And there exist 2 control queues(control receiveq/control transmitq). @@ -232,18 +236,6 @@ impl VirtioDevice for Serial { DEFAULT_VIRTQUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.config_space.as_bytes(); let config_len = config_slice.len() as u64; @@ -323,10 +315,6 @@ impl VirtioDevice for Serial { Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } impl StateTransfer for Serial { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b03a79b80..bfa7ffe95 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -43,7 +43,7 @@ use vmm_sys_util::eventfd::EventFd; use address_space::AddressSpace; use machine_manager::config::ConfigCheck; use util::aio::{mem_to_buf, Iovec}; -use util::num_ops::write_u32; +use util::num_ops::{read_u32, write_u32}; use util::AsAny; pub use device::balloon::*; @@ -318,7 +318,9 @@ pub type VirtioInterrupt = Box, bool) -> Result<()> + Send + Sync>; #[derive(Default)] -struct VirtioBase { +pub struct VirtioBase { + /// Device type + device_type: u32, /// Bit mask of features supported by the backend. device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. @@ -329,8 +331,23 @@ struct VirtioBase { broken: Arc, } +impl VirtioBase { + fn new(device_type: u32) -> Self { + Self { + device_type, + ..Default::default() + } + } +} + /// The trait for virtio device operations. pub trait VirtioDevice: Send + AsAny { + /// Get base property of virtio device. + fn virtio_base(&self) -> &VirtioBase; + + /// Get mutable base property virtio device. + fn virtio_base_mut(&mut self) -> &mut VirtioBase; + /// Realize low level device. fn realize(&mut self) -> Result<()>; @@ -340,7 +357,9 @@ pub trait VirtioDevice: Send + AsAny { } /// Get the virtio device type, refer to Virtio Spec. - fn device_type(&self) -> u32; + fn device_type(&self) -> u32 { + self.virtio_base().device_type + } /// Get the count of virtio device queues. fn queue_num(&self) -> usize; @@ -349,10 +368,12 @@ pub trait VirtioDevice: Send + AsAny { fn queue_size_max(&self) -> u16; /// Get device features from host. - fn device_features(&self, features_select: u32) -> u32; + fn device_features(&self, features_select: u32) -> u32 { + read_u32(self.virtio_base().device_features, features_select) + } - /// Get checked driver features before set the value at the page. - fn checked_driver_features(&mut self, page: u32, value: u32) -> u64 { + /// Set driver features by guest. + fn set_driver_features(&mut self, page: u32, value: u32) { let mut v = value; let unsupported_features = value & !self.device_features(page); if unsupported_features != 0 { @@ -362,18 +383,19 @@ pub trait VirtioDevice: Send + AsAny { ); v &= !unsupported_features; } - if page == 0 { + + let features = if page == 0 { (self.driver_features(1) as u64) << 32 | (v as u64) } else { (v as u64) << 32 | (self.driver_features(0) as u64) - } + }; + self.virtio_base_mut().driver_features = features; } - /// Set driver features by guest. - fn set_driver_features(&mut self, page: u32, value: u32); - /// Get driver features by guest. - fn driver_features(&self, features_select: u32) -> u32; + fn driver_features(&self, features_select: u32) -> u32 { + read_u32(self.virtio_base().driver_features, features_select) + } /// Read data of config from guest. fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()>; @@ -438,7 +460,9 @@ pub trait VirtioDevice: Send + AsAny { false } - fn get_device_broken(&self) -> &Arc; + fn get_device_broken(&self) -> &Arc { + &self.virtio_base().broken + } } /// The trait for trace descriptions of virtio device interactions diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 6a38fbc71..987c4315f 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -733,11 +733,10 @@ impl MigrationHook for VirtioMmioDevice { mod tests { use std::io::Write; + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + use super::*; use crate::{VirtioBase, VIRTIO_TYPE_BLOCK}; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use std::sync::atomic::AtomicBool; - use util::num_ops::read_u32; fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); @@ -784,7 +783,7 @@ mod tests { } VirtioDeviceTest { - base: Default::default(), + base: VirtioBase::new(VIRTIO_TYPE_BLOCK), b_active: false, b_realized: false, config_space, @@ -793,15 +792,19 @@ mod tests { } impl VirtioDevice for VirtioDeviceTest { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { self.b_realized = true; Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_BLOCK - } - fn queue_num(&self) -> usize { QUEUE_NUM } @@ -810,18 +813,6 @@ mod tests { QUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_len = self.config_space.len() as u64; if offset >= config_len { @@ -868,10 +859,6 @@ mod tests { self.b_active = true; Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } #[test] diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 9361f4bd7..8d0fe6dd4 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1460,7 +1460,6 @@ mod tests { config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC}, le_read_u16, }; - use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::*; @@ -1477,7 +1476,7 @@ mod tests { impl VirtioDeviceTest { pub fn new() -> Self { - let mut base = VirtioBase::default(); + let mut base = VirtioBase::new(VIRTIO_DEVICE_TEST_TYPE); base.device_features = 0xFFFF_FFF0; VirtioDeviceTest { base, @@ -1487,12 +1486,16 @@ mod tests { } impl VirtioDevice for VirtioDeviceTest { - fn realize(&mut self) -> VirtioResult<()> { - Ok(()) + fn virtio_base(&self) -> &VirtioBase { + &self.base } - fn device_type(&self) -> u32 { - VIRTIO_DEVICE_TEST_TYPE + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + + fn realize(&mut self) -> VirtioResult<()> { + Ok(()) } fn queue_num(&self) -> usize { @@ -1503,18 +1506,6 @@ mod tests { VIRTIO_DEVICE_QUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, _offset: u64, mut _data: &mut [u8]) -> VirtioResult<()> { Ok(()) } @@ -1533,10 +1524,6 @@ mod tests { self.is_activated = true; Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } macro_rules! com_cfg_read_test { diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index e29300369..21875dae4 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -13,7 +13,7 @@ use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -22,7 +22,6 @@ use machine_manager::config::NetworkInterfaceConfig; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::read_u32; use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; @@ -100,7 +99,7 @@ pub struct Net { impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { Net { - base: Default::default(), + base: VirtioBase::new(VIRTIO_TYPE_NET), net_cfg: cfg.clone(), config_space: Default::default(), taps: None, @@ -114,6 +113,14 @@ impl Net { } impl VirtioDevice for Net { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { let queue_pairs = self.net_cfg.queues / 2; let mut backends = Vec::with_capacity(queue_pairs as usize); @@ -180,10 +187,6 @@ impl VirtioDevice for Net { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_NET - } - fn queue_num(&self) -> usize { if self.net_cfg.mq { (self.net_cfg.queues + 1) as usize @@ -196,18 +199,6 @@ impl VirtioDevice for Net { self.net_cfg.queue_size } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); let config_slice = config_space.as_bytes(); @@ -427,10 +418,6 @@ impl VirtioDevice for Net { Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } #[cfg(test)] diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 59b8cf354..0cdf54f11 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::os::unix::io::RawFd; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -26,7 +26,6 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::read_u32; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; @@ -115,7 +114,7 @@ pub struct Vsock { impl Vsock { pub fn new(cfg: &VsockConfig, mem_space: &Arc) -> Self { Vsock { - base: Default::default(), + base: VirtioBase::new(VIRTIO_TYPE_VSOCK), vsock_cfg: cfg.clone(), backend: None, config_space: Default::default(), @@ -172,6 +171,14 @@ impl Vsock { } impl VirtioDevice for Vsock { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { let vhost_fd: Option = self.vsock_cfg.vhost_fd; let backend = VhostBackend::new(&self.mem_space, VHOST_PATH, vhost_fd) @@ -187,10 +194,6 @@ impl VirtioDevice for Vsock { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_VSOCK - } - fn queue_num(&self) -> usize { QUEUE_NUM_VSOCK } @@ -199,18 +202,6 @@ impl VirtioDevice for Vsock { DEFAULT_VIRTQUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { match offset { 0 if data.len() == 8 => LittleEndian::write_u64(data, self.vsock_cfg.guest_cid), @@ -355,10 +346,6 @@ impl VirtioDevice for Vsock { fn reset(&mut self) -> Result<()> { self.backend.as_ref().unwrap().set_running(false) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } impl StateTransfer for Vsock { diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 9f568a41c..acb0041a1 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -13,13 +13,11 @@ use anyhow::{anyhow, bail, Context, Result}; use std::cmp; use std::io::Write; -use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; use machine_manager::config::BlkDevConfig; use util::byte_code::ByteCode; -use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; @@ -51,7 +49,7 @@ pub struct Block { impl Block { pub fn new(cfg: &BlkDevConfig, mem_space: &Arc) -> Self { Block { - base: Default::default(), + base: VirtioBase::new(VIRTIO_TYPE_BLOCK), blk_cfg: cfg.clone(), config_space: Default::default(), mem_space: mem_space.clone(), @@ -168,6 +166,14 @@ impl Block { } impl VirtioDevice for Block { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { self.init_client()?; self.negotiate_features()?; @@ -175,10 +181,6 @@ impl VirtioDevice for Block { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_BLOCK - } - fn queue_num(&self) -> usize { self.blk_cfg.queues as usize } @@ -187,18 +189,6 @@ impl VirtioDevice for Block { self.blk_cfg.queue_size } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let offset = offset as usize; let config_slice = self.config_space.as_bytes(); @@ -287,8 +277,4 @@ impl VirtioDevice for Block { Ok(()) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 601379d41..0fcc80404 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -22,7 +22,6 @@ use std::cmp; use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; -use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use log::error; @@ -35,7 +34,6 @@ use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::read_u32; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; use super::super::{VhostNotify, VhostOps}; @@ -122,7 +120,7 @@ impl Fs { /// `enable_irqfd` - Whether irqfd is enabled on this Fs device. pub fn new(fs_cfg: FsConfig, mem_space: Arc, enable_irqfd: bool) -> Self { Fs { - base: Default::default(), + base: VirtioBase::new(VIRTIO_TYPE_FS), fs_cfg, config_space: VirtioFsConfig::default(), client: None, @@ -134,6 +132,14 @@ impl Fs { } impl VirtioDevice for Fs { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { let tag_bytes_vec = self.fs_cfg.tag.clone().into_bytes(); self.config_space.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); @@ -161,10 +167,6 @@ impl VirtioDevice for Fs { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_FS - } - fn queue_num(&self) -> usize { VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM } @@ -173,18 +175,6 @@ impl VirtioDevice for Fs { VIRTIO_FS_QUEUE_SIZE } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_slice = self.config_space.as_bytes(); let config_size = config_slice.len() as u64; @@ -276,8 +266,4 @@ impl VirtioDevice for Fs { self.realize() } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 5bd417505..85749ad3c 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -12,7 +12,7 @@ use std::cmp; use std::io::Write; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -20,7 +20,6 @@ use machine_manager::config::NetworkInterfaceConfig; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use util::num_ops::read_u32; use vmm_sys_util::eventfd::EventFd; use super::super::VhostOps; @@ -57,7 +56,7 @@ pub struct Net { impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { Net { - base: Default::default(), + base: VirtioBase::new(VIRTIO_TYPE_NET), net_cfg: cfg.clone(), config_space: Default::default(), mem_space: mem_space.clone(), @@ -99,6 +98,14 @@ impl Net { } impl VirtioDevice for Net { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + fn realize(&mut self) -> Result<()> { let socket_path = self .net_cfg @@ -155,10 +162,6 @@ impl VirtioDevice for Net { Ok(()) } - fn device_type(&self) -> u32 { - VIRTIO_TYPE_NET - } - fn queue_num(&self) -> usize { if self.net_cfg.mq { // If support multi-queue, it should add 1 control queue. @@ -172,18 +175,6 @@ impl VirtioDevice for Net { self.net_cfg.queue_size } - fn device_features(&self, features_select: u32) -> u32 { - read_u32(self.base.device_features, features_select) - } - - fn set_driver_features(&mut self, page: u32, value: u32) { - self.base.driver_features = self.checked_driver_features(page, value); - } - - fn driver_features(&self, features_select: u32) -> u32 { - read_u32(self.base.driver_features, features_select) - } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); let config_slice = config_space.as_bytes(); @@ -299,8 +290,4 @@ impl VirtioDevice for Net { fn has_control_queue(&mut self) -> bool { virtio_has_feature(self.base.device_features, VIRTIO_NET_F_CTRL_VQ) } - - fn get_device_broken(&self) -> &Arc { - &self.base.broken - } } -- Gitee From ddd7b6e4a2cc6cbb89663b6f936ac6c57b7b3e3f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 21 Jul 2023 17:46:18 +0800 Subject: [PATCH 1249/2187] Refactory: Remove get_device_broken method Signed-off-by: Keqian Zhu --- virtio/src/lib.rs | 4 ---- virtio/src/transport/virtio_mmio.rs | 4 ++-- virtio/src/transport/virtio_pci.rs | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index bfa7ffe95..b67b61bfa 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -459,10 +459,6 @@ pub trait VirtioDevice: Send + AsAny { fn has_control_queue(&mut self) -> bool { false } - - fn get_device_broken(&self) -> &Arc { - &self.virtio_base().broken - } } /// The trait for trace descriptions of virtio device interactions diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 987c4315f..eae99e94a 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -406,7 +406,7 @@ impl VirtioMmioDevice { let dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = dev_lock.get_device_broken(); + let broken = &dev_lock.virtio_base().broken; for q_config in queues_config.iter_mut() { q_config.set_addr_cache( @@ -677,7 +677,7 @@ impl StateTransfer for VirtioMmioDevice { let dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = dev_lock.get_device_broken(); + let broken = &dev_lock.virtio_base().broken; self.queues = queue_states .iter_mut() .map(|queue_state| { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 8d0fe6dd4..e52d32ecd 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -729,7 +729,7 @@ impl VirtioPciDevice { let dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = dev_lock.get_device_broken(); + let broken = &dev_lock.virtio_base().broken; for q_config in queues_config.iter_mut() { if !q_config.ready { debug!("queue is not ready, please check your init process"); @@ -1396,7 +1396,7 @@ impl StateTransfer for VirtioPciDevice { let dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = dev_lock.get_device_broken(); + let broken = &dev_lock.virtio_base().broken; for queue_state in pci_state.queues_config[0..pci_state.queue_num].iter_mut() { queue_state.set_addr_cache( self.sys_mem.clone(), -- Gitee From 68b7c7a78f229cc331fa9b8cddb32f6a3f11aa97 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 9 Jul 2023 22:06:41 +0800 Subject: [PATCH 1250/2187] Refactory: Supply default implementation for read_config Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 31 +++++-------- virtio/src/device/block.rs | 53 ++++++++--------------- virtio/src/device/gpu.rs | 55 ++++++++--------------- virtio/src/device/net.rs | 67 ++++++++++------------------- virtio/src/device/scsi_cntlr.rs | 36 ++++------------ virtio/src/device/serial.rs | 22 +++------- virtio/src/error.rs | 4 +- virtio/src/lib.rs | 22 +++++++++- virtio/src/transport/virtio_mmio.rs | 34 ++------------- virtio/src/vhost/kernel/net.rs | 18 ++------ virtio/src/vhost/kernel/vsock.rs | 13 ++---- virtio/src/vhost/user/block.rs | 45 +++++-------------- virtio/src/vhost/user/fs.rs | 20 ++------- virtio/src/vhost/user/net.rs | 20 ++------- 14 files changed, 135 insertions(+), 305 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index f72a56693..91aedc0d5 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -9,7 +9,7 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::io::Write; + use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -46,8 +46,9 @@ use util::{ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use crate::{ - error::*, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, VirtioDevice, - VirtioInterrupt, VirtioInterruptType, VirtioTrace, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, + error::*, read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, + VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, }; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; @@ -1022,7 +1023,7 @@ impl VirtioDevice for Balloon { DEFAULT_VIRTQUEUE_SIZE } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let new_config = VirtioBalloonConfig { num_pages: self.num_pages, actual: self.actual.load(Ordering::Acquire), @@ -1034,23 +1035,13 @@ impl VirtioDevice for Balloon { let config_len = if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { - size_of::() as u64 + size_of::() } else { - offset_of!(VirtioBalloonConfig, _reserved) as u64 + offset_of!(VirtioBalloonConfig, _reserved) }; - let data_len = data.len() as u64; - if offset >= config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - - if let Some(end) = offset.checked_add(data_len) { - data.write_all( - &new_config.as_bytes()[offset as usize..cmp::min(end, config_len) as usize], - )?; - } - - Ok(()) + let config = &new_config.as_bytes()[..config_len]; + read_config_default(config, offset, data) } fn write_config(&mut self, _offset: u64, data: &[u8]) -> Result<()> { @@ -1332,8 +1323,8 @@ mod tests { let addr = 0x4; assert_eq!(balloon.get_balloon_memory_size(), 0); balloon.actual.store(1, Ordering::Release); - balloon.read_config(addr, &mut read_data).unwrap(); - assert_eq!(read_data, ret_data); + assert!(balloon.read_config(addr, &mut read_data).is_err()); + assert_ne!(read_data, ret_data); } #[test] diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 0d60ebb28..0e5b14ef2 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -12,7 +12,6 @@ use std::cmp; use std::collections::HashMap; -use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -27,14 +26,15 @@ use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, report_virtio_error, - virtio_has_feature, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VirtioTrace, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, - VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, - VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, - VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, - VIRTIO_BLK_T_WRITE_ZEROES, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, + read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, + VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, + VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, + VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, + VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::{AddressSpace, GuestAddress}; use block_backend::{ @@ -1009,13 +1009,13 @@ impl Block { } } - fn get_blk_config_size(&self) -> u64 { + fn get_blk_config_size(&self) -> usize { if virtio_has_feature(self.base.device_features, VIRTIO_BLK_F_WRITE_ZEROES) { - offset_of!(VirtioBlkConfig, unused1) as u64 + offset_of!(VirtioBlkConfig, unused1) } else if virtio_has_feature(self.base.device_features, VIRTIO_BLK_F_DISCARD) { - offset_of!(VirtioBlkConfig, max_write_zeroes_sectors) as u64 + offset_of!(VirtioBlkConfig, max_write_zeroes_sectors) } else { - offset_of!(VirtioBlkConfig, max_discard_sectors) as u64 + offset_of!(VirtioBlkConfig, max_discard_sectors) } } @@ -1111,35 +1111,18 @@ impl VirtioDevice for Block { self.blk_cfg.queue_size } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_len = self.get_blk_config_size(); - let read_end = offset as usize + data.len(); - if offset - .checked_add(data.len() as u64) - .filter(|&end| end <= config_len) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - - let config_slice = self.config_space.as_bytes(); - data.write_all(&config_slice[(offset as usize)..read_end])?; - - Ok(()) + let config = &self.config_space.as_bytes()[..config_len]; + read_config_default(config, offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let config_len = self.get_blk_config_size(); - if offset - .checked_add(data.len() as u64) - .filter(|&end| end <= config_len) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } + let config = &self.config_space.as_bytes()[..config_len]; + check_config_space_rw(config, offset, data)?; // The only writable field is "writeback", but it's not supported for now, // so do nothing here. - Ok(()) } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index c2f355751..e7eb1b8cc 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -48,17 +47,18 @@ use util::pixman::{ }; use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, ElemIovec, Element, Queue, VirtioBase, - VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, - VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, - VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, - VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, - VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, - VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, - VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, - VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, + ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_F_EDID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, + VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; /// Number of virtqueues @@ -1522,38 +1522,17 @@ impl VirtioDevice for Gpu { DEFAULT_VIRTQUEUE_SIZE } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_space = *self.config_space.lock().unwrap(); - let config_slice = config_space.as_bytes(); - let config_len = config_slice.len() as u64; - - if offset - .checked_add(data.len() as u64) - .filter(|&end| end <= config_len) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - - let read_end: usize = offset as usize + data.len(); - data.write_all(&config_slice[offset as usize..read_end])?; - - Ok(()) + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { + let config_space = self.config_space.lock().unwrap(); + read_config_default(config_space.as_bytes(), offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let mut config_space = self.config_space.lock().unwrap(); + check_config_space_rw(config_space.as_bytes(), offset, data)?; + let mut config_cpy = *config_space; let config_cpy_slice = config_cpy.as_mut_bytes(); - let config_len = config_cpy_slice.len() as u64; - - if offset - .checked_add(data.len() as u64) - .filter(|&end| end <= config_len) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } config_cpy_slice[(offset as usize)..(offset as usize + data.len())].copy_from_slice(data); if config_space.events_clear != 0 { diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 425141286..cd94f174a 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::io::{ErrorKind, Write}; +use std::io::ErrorKind; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::rc::Rc; @@ -20,28 +20,28 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::{cmp, fs, mem}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, warn}; use once_cell::sync::Lazy; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - iov_discard_front, iov_to_buf, mem_to_buf, report_virtio_error, virtio_has_feature, ElemIovec, - Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, - VirtioNetHdr, VirtioTrace, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, - VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, - VIRTIO_NET_CTRL_RX_ALLMULTI, VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, - VIRTIO_NET_CTRL_RX_NOMULTI, VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, - VIRTIO_NET_CTRL_VLAN, VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, - VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, - VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, - VIRTIO_NET_OK, VIRTIO_TYPE_NET, + check_config_space_rw, iov_discard_front, iov_to_buf, mem_to_buf, read_config_default, + report_virtio_error, virtio_has_feature, ElemIovec, Element, Queue, VirtioBase, VirtioDevice, + VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, + VIRTIO_NET_CTRL_RX_ALLUNI, VIRTIO_NET_CTRL_RX_NOBCAST, VIRTIO_NET_CTRL_RX_NOMULTI, + VIRTIO_NET_CTRL_RX_NOUNI, VIRTIO_NET_CTRL_RX_PROMISC, VIRTIO_NET_CTRL_VLAN, + VIRTIO_NET_CTRL_VLAN_ADD, VIRTIO_NET_CTRL_VLAN_DEL, VIRTIO_NET_ERR, VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, + VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, + VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; use address_space::{AddressSpace, RegionCache}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -1463,39 +1463,18 @@ impl VirtioDevice for Net { self.net_cfg.queue_size } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); - let config_slice = config_space.as_bytes(); - let config_len = config_slice.len() as u64; - if offset - .checked_add(data.len() as u64) - .filter(|&end| end <= config_len) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - data.write_all(&config_slice[offset as usize..(offset as usize + data.len())])?; - - Ok(()) + read_config_default(config_space.as_bytes(), offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let data_len = data.len(); - let driver_features = self.base.driver_features; let mut config_space = self.config_space.lock().unwrap(); - let config_slice = config_space.as_mut_bytes(); - - if offset - .checked_add(data_len as u64) - .filter(|&end| end <= MAC_ADDR_LEN as u64) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset, - config_slice.len() as u64 - ))); - } + let config_slice = &mut config_space.as_mut_bytes()[..MAC_ADDR_LEN]; + check_config_space_rw(config_slice, offset, data)?; + let data_len = data.len(); + let driver_features = self.base.driver_features; if !virtio_has_feature(driver_features, VIRTIO_NET_F_CTRL_MAC_ADDR) && !virtio_has_feature(driver_features, VIRTIO_F_VERSION_1) && *data != config_slice[offset as usize..(offset as usize + data_len)] diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 35ce774c1..eec76fc79 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -10,22 +10,21 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::cmp; -use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use log::{debug, error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, - VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, + report_virtio_error, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; use block_backend::BlockIoErrorCallback; @@ -187,31 +186,12 @@ impl VirtioDevice for ScsiCntlr { self.config.queue_size } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.config_space.as_bytes(); - let config_len = config_slice.len() as u64; - if offset >= config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; - } - - Ok(()) + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { + read_config_default(self.config_space.as_bytes(), offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - let config_slice = self.config_space.as_mut_bytes(); - let config_len = config_slice.len() as u64; - - if offset - .checked_add(data.len() as u64) - .filter(|&end| end <= config_len) - .is_none() - { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - + check_config_space_rw(self.config_space.as_bytes(), offset, data)?; // Guest can only set sense_size and cdb_size, which are fixed default values // (VIRTIO_SCSI_CDB_DEFAULT_SIZE; VIRTIO_SCSI_SENSE_DEFAULT_SIZE) and cannot be // changed in stratovirt now. So, do nothing when guest writes config. diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index a5ca8784a..75482d294 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::io::Write; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -25,9 +24,10 @@ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, report_virtio_error, Element, Queue, - VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, + gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, report_virtio_error, + Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VirtioTrace, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; use devices::legacy::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; @@ -236,18 +236,8 @@ impl VirtioDevice for Serial { DEFAULT_VIRTQUEUE_SIZE } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.config_space.as_bytes(); - let config_len = config_slice.len() as u64; - if offset >= config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_len))); - } - - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_len) as usize])?; - } - - Ok(()) + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { + read_config_default(self.config_space.as_bytes(), offset, data) } fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { diff --git a/virtio/src/error.rs b/virtio/src/error.rs index a53c1d6f4..7535c938e 100644 --- a/virtio/src/error.rs +++ b/virtio/src/error.rs @@ -48,8 +48,8 @@ pub enum VirtioError { QueueDescInvalid, #[error("Address overflows for {0}, address: 0x{1:x}, offset: {2}")] AddressOverflow(&'static str, u64, u64), - #[error("Failed to r/w dev config space: overflows, offset {0}, space size {1}")] - DevConfigOverflow(u64, u64), + #[error("Failed to r/w dev config space: overflows, offset {0}, len {1}, space size {2}")] + DevConfigOverflow(u64, u64, u64), #[error("Failed to trigger interrupt for {0}, int-type {1:#?}")] InterruptTrigger(&'static str, super::VirtioInterruptType), #[error("Vhost ioctl failed: {0}")] diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index b67b61bfa..ac7915ef3 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -32,11 +32,12 @@ mod transport; pub mod vhost; use std::cmp; +use std::io::Write; use std::os::unix::prelude::RawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; @@ -461,6 +462,25 @@ pub trait VirtioDevice: Send + AsAny { } } +/// Check boundary for config space rw. +fn check_config_space_rw(config: &[u8], offset: u64, data: &[u8]) -> Result<()> { + let config_len = config.len() as u64; + let data_len = data.len() as u64; + offset + .checked_add(data_len) + .filter(|&end| end <= config_len) + .with_context(|| VirtioError::DevConfigOverflow(offset, data_len, config_len))?; + Ok(()) +} + +/// Default implementation for config space read. +fn read_config_default(config: &[u8], offset: u64, mut data: &mut [u8]) -> Result<()> { + check_config_space_rw(config, offset, data)?; + let read_end = offset as usize + data.len(); + data.write_all(&config[offset as usize..read_end])?; + Ok(()) +} + /// The trait for trace descriptions of virtio device interactions /// on the front and back ends. pub trait VirtioTrace { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index eae99e94a..c40e645da 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -731,12 +731,10 @@ impl MigrationHook for VirtioMmioDevice { #[cfg(test)] mod tests { - use std::io::Write; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use super::*; - use crate::{VirtioBase, VIRTIO_TYPE_BLOCK}; + use crate::{check_config_space_rw, read_config_default, VirtioBase, VIRTIO_TYPE_BLOCK}; fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); @@ -813,39 +811,15 @@ mod tests { QUEUE_SIZE } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_len = self.config_space.len() as u64; - if offset >= config_len { - bail!( - "The offset{} for reading is more than the length{} of configuration", - offset, - config_len - ); - } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all( - &self.config_space[offset as usize..std::cmp::min(end, config_len) as usize], - )?; - } - - Ok(()) + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { + read_config_default(&self.config_space, offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + check_config_space_rw(&self.config_space, offset, data)?; let data_len = data.len(); - let config_len = self.config_space.len(); - if offset as usize + data_len > config_len { - bail!( - "The offset{} {}for writing is more than the length{} of configuration", - offset, - data_len, - config_len - ); - } - self.config_space[(offset as usize)..(offset as usize + data_len)] .copy_from_slice(&data[..]); - Ok(()) } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 21875dae4..74f6a06e6 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::cmp; -use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; @@ -28,6 +26,7 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; +use crate::read_config_default; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, MAC_ADDR_LEN}, error::VirtioError, @@ -199,18 +198,9 @@ impl VirtioDevice for Net { self.net_cfg.queue_size } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); - let config_slice = config_space.as_bytes(); - let config_size = config_slice.len() as u64; - if offset >= config_size { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); - } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; - } - - Ok(()) + read_config_default(config_space.as_bytes(), offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { @@ -542,6 +532,6 @@ mod tests { let offset: u64 = len - 1; let mut read_data: Vec = vec![0; len as usize]; - assert_eq!(vhost_net.read_config(offset, &mut read_data).is_ok(), true); + assert_eq!(vhost_net.read_config(offset, &mut read_data).is_ok(), false); } } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 0cdf54f11..4943f3bba 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -30,8 +30,8 @@ use util::loop_context::EventNotifierHelper; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ - Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, - VIRTIO_TYPE_VSOCK, + check_config_space_rw, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_TYPE_VSOCK, }; /// Number of virtqueues. @@ -218,15 +218,8 @@ impl VirtioDevice for Vsock { } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + check_config_space_rw(&self.config_space, offset, data)?; let data_len = data.len(); - let config_len = self.config_space.len(); - if offset as usize + data_len > config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset, - config_len as u64 - ))); - } - self.config_space[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); Ok(()) } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index acb0041a1..110824c7f 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -11,8 +11,6 @@ // See the Mulan PSL v2 for more details. use anyhow::{anyhow, bail, Context, Result}; -use std::cmp; -use std::io::Write; use std::sync::{Arc, Mutex}; use address_space::AddressSpace; @@ -27,10 +25,10 @@ use crate::VhostUser::client::{ }; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; use crate::{ - virtio_has_feature, VirtioBase, VirtioBlkConfig, VirtioDevice, VirtioError, VirtioInterrupt, - VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, - VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, - VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, + check_config_space_rw, read_config_default, virtio_has_feature, VirtioBase, VirtioBlkConfig, + VirtioDevice, VirtioInterrupt, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, + VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, + VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; pub struct Block { @@ -189,40 +187,17 @@ impl VirtioDevice for Block { self.blk_cfg.queue_size } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let offset = offset as usize; - let config_slice = self.config_space.as_bytes(); - let config_len = config_slice.len(); - if offset >= config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset as u64, - config_len as u64 - ))); - } - if let Some(end) = offset.checked_add(data.len()) { - data.write_all(&config_slice[offset..cmp::min(end, config_len)])?; - } else { - bail!("Failed to read config from guest for vhost user blk pci, config space address overflow.") - } - - Ok(()) + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { + read_config_default(self.config_space.as_bytes(), offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + check_config_space_rw(self.config_space.as_bytes(), offset, data)?; + let offset = offset as usize; + let end = offset + data.len(); let config_slice = self.config_space.as_mut_bytes(); - let config_len = config_slice.len(); - if let Some(end) = offset.checked_add(data.len()) { - if end > config_len { - return Err(anyhow!(VirtioError::DevConfigOverflow( - offset as u64, - config_len as u64 - ))); - } - config_slice[offset..end].copy_from_slice(data); - } else { - bail!("Failed to write config to guest for vhost user blk pci, config space address overflow.") - } + config_slice[offset..end].copy_from_slice(data); self.client .as_ref() diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 0fcc80404..5399b69c9 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -17,13 +17,11 @@ const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; // The size of queue for virtio fs const VIRTIO_FS_QUEUE_SIZE: u16 = 128; -use crate::{VirtioBase, VirtioError}; -use std::cmp; -use std::io::Write; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, Context, Result}; use log::error; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -38,8 +36,7 @@ use util::loop_context::{ use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackendType, VhostUserClient}; -use crate::{VirtioInterrupt, VirtioInterruptType}; -use anyhow::{anyhow, Context, Result}; +use crate::{read_config_default, VirtioBase, VirtioInterrupt, VirtioInterruptType}; #[derive(Copy, Clone)] #[repr(C, packed)] @@ -175,17 +172,8 @@ impl VirtioDevice for Fs { VIRTIO_FS_QUEUE_SIZE } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { - let config_slice = self.config_space.as_bytes(); - let config_size = config_slice.len() as u64; - if offset >= config_size { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); - } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; - } - - Ok(()) + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { + read_config_default(self.config_space.as_bytes(), offset, data) } fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 85749ad3c..4f3f9d8db 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::cmp; -use std::io::Write; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; @@ -24,11 +22,10 @@ use vmm_sys_util::eventfd::EventFd; use super::super::VhostOps; use super::{VhostBackendType, VhostUserClient}; -use crate::error::VirtioError; use crate::{ device::net::{build_device_config_space, CtrlInfo, MAC_ADDR_LEN}, - virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioBase, VirtioDevice, - VirtioInterrupt, VirtioNetConfig, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, + read_config_default, virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioBase, + VirtioDevice, VirtioInterrupt, VirtioNetConfig, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, @@ -175,18 +172,9 @@ impl VirtioDevice for Net { self.net_cfg.queue_size } - fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); - let config_slice = config_space.as_bytes(); - let config_size = config_slice.len() as u64; - if offset >= config_size { - return Err(anyhow!(VirtioError::DevConfigOverflow(offset, config_size))); - } - if let Some(end) = offset.checked_add(data.len() as u64) { - data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; - } - - Ok(()) + read_config_default(config_space.as_bytes(), offset, data) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { -- Gitee From eecd34b1ac9d26023e76e3c053166a480dea1d9f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 20:40:28 +0800 Subject: [PATCH 1251/2187] Refactory: Factor out config and feature initilization ... which makes realize() more readable. Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 67 +++++++++++++++-------------- virtio/src/device/block.rs | 59 ++++++++++++++----------- virtio/src/device/gpu.rs | 20 +++++---- virtio/src/device/net.rs | 50 +++++++++++---------- virtio/src/device/rng.rs | 6 ++- virtio/src/device/scsi_cntlr.rs | 4 ++ virtio/src/device/serial.rs | 6 ++- virtio/src/lib.rs | 3 ++ virtio/src/transport/virtio_mmio.rs | 5 +++ virtio/src/transport/virtio_pci.rs | 5 +++ virtio/src/vhost/kernel/net.rs | 26 +++++++---- virtio/src/vhost/kernel/vsock.rs | 11 ++++- virtio/src/vhost/user/block.rs | 36 ++++++++-------- virtio/src/vhost/user/fs.rs | 18 +++++--- virtio/src/vhost/user/net.rs | 13 ++++-- 15 files changed, 202 insertions(+), 127 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 91aedc0d5..d2a8ad1ea 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -877,6 +877,8 @@ impl EventNotifierHelper for BalloonIoHandler { pub struct Balloon { /// Virtio device base property. base: VirtioBase, + /// Configuration of the balloon device. + bln_cfg: BalloonConfig, /// Actual memory pages of balloon device. actual: Arc, /// Target memory pages of balloon device. @@ -889,9 +891,6 @@ pub struct Balloon { mem_space: Arc, /// Event timer for BALLOON_CHANGED event. event_timer: Arc>, - /// For auto balloon - membuf_percent: u32, - monitor_interval: u32, } impl Balloon { @@ -901,30 +900,15 @@ impl Balloon { /// /// * `bln_cfg` - Balloon configuration. pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc) -> Balloon { - let mut device_features = 1u64 << VIRTIO_F_VERSION_1; - if bln_cfg.deflate_on_oom { - device_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM; - } - if bln_cfg.free_page_reporting { - device_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING; - } - if bln_cfg.auto_balloon { - device_features |= 1u64 << VIRTIO_BALLOON_F_MESSAGE_VQ; - } - - let mut base = VirtioBase::new(VIRTIO_TYPE_BALLOON); - base.device_features = device_features; - Balloon { - base, + base: VirtioBase::new(VIRTIO_TYPE_BALLOON), + bln_cfg: bln_cfg.clone(), actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, interrupt_cb: None, mem_info: Arc::new(Mutex::new(BlnMemInfo::new())), mem_space, event_timer: Arc::new(Mutex::new(TimerFd::new().unwrap())), - membuf_percent: bln_cfg.membuf_percent, - monitor_interval: bln_cfg.monitor_interval, } } @@ -1005,6 +989,21 @@ impl VirtioDevice for Balloon { self.mem_space .register_listener(self.mem_info.clone()) .with_context(|| "Failed to register memory listener defined by balloon device.")?; + self.init_config_features()?; + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { + self.base.device_features = 1u64 << VIRTIO_F_VERSION_1; + if self.bln_cfg.deflate_on_oom { + self.base.device_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM; + } + if self.bln_cfg.free_page_reporting { + self.base.device_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING; + } + if self.bln_cfg.auto_balloon { + self.base.device_features |= 1u64 << VIRTIO_BALLOON_F_MESSAGE_VQ; + } Ok(()) } @@ -1029,8 +1028,8 @@ impl VirtioDevice for Balloon { actual: self.actual.load(Ordering::Acquire), _reserved: 0_u32, _reserved1: 0_u32, - membuf_percent: self.membuf_percent, - monitor_interval: self.monitor_interval, + membuf_percent: self.bln_cfg.membuf_percent, + monitor_interval: self.bln_cfg.monitor_interval, }; let config_len = @@ -1247,6 +1246,13 @@ mod tests { let mem_space = address_space_init(); let mut bln = Balloon::new(&bln_cfg, mem_space); + + // Test realize function. + bln.realize().unwrap(); + assert_eq!(bln.device_type(), 5); + assert_eq!(bln.queue_num(), 2); + assert_eq!(bln.queue_size_max(), QUEUE_SIZE); + assert_eq!(bln.base.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); @@ -1271,11 +1277,6 @@ mod tests { (bln.driver_features(1) as u64) << 32 ); - // Test realize function. - bln.realize().unwrap(); - assert_eq!(bln.device_type(), 5); - assert_eq!(bln.queue_num(), 2); - assert_eq!(bln.queue_size_max(), QUEUE_SIZE); // Test methods of balloon. let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); @@ -1635,6 +1636,13 @@ mod tests { }; let mem_space = address_space_init(); let mut bln = Balloon::new(&bln_cfg, mem_space); + + // Test realize function. + bln.realize().unwrap(); + assert_eq!(bln.device_type(), 5); + assert_eq!(bln.queue_num(), 3); + assert_eq!(bln.queue_size_max(), QUEUE_SIZE); + assert_eq!(bln.base.driver_features, 0); assert_eq!(bln.actual.load(Ordering::Acquire), 0); assert_eq!(bln.num_pages, 0); @@ -1648,11 +1656,6 @@ mod tests { let fts = bln.device_features(1); assert_eq!(fts, (feature >> 32) as u32); - // Test realize function. - bln.realize().unwrap(); - assert_eq!(bln.device_type(), 5); - assert_eq!(bln.queue_num(), 3); - assert_eq!(bln.queue_size_max(), QUEUE_SIZE); // Test methods of balloon. let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 0e5b14ef2..1fad6dc5d 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -986,13 +986,15 @@ impl Block { fn build_device_config_space(&mut self) { // capacity: 64bits - let num_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; - self.config_space.capacity = num_sectors; + self.config_space.capacity = self.disk_sectors; // seg_max = queue_size - 2: 32bits self.config_space.seg_max = self.queue_size_max() as u32 - 2; + if self.blk_cfg.queues > 1 { + self.config_space.num_queues = self.blk_cfg.queues; + } + if self.blk_cfg.discard { - self.base.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; // Just support one segment per request. self.config_space.max_discard_seg = 1; // The default discard alignment is 1 sector. @@ -1001,7 +1003,6 @@ impl Block { } if self.blk_cfg.write_zeroes != WriteZeroesState::Off { - self.base.device_features |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; // Just support one segment per request. self.config_space.max_write_zeroes_seg = 1; self.config_space.max_write_zeroes_sectors = MAX_REQUEST_SECTORS; @@ -1048,25 +1049,6 @@ impl VirtioDevice for Block { ); } - self.base.device_features = (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_BLK_F_FLUSH); - if self.blk_cfg.read_only { - self.base.device_features |= 1_u64 << VIRTIO_BLK_F_RO; - }; - self.base.device_features |= 1_u64 << VIRTIO_F_RING_INDIRECT_DESC; - self.base.device_features |= 1_u64 << VIRTIO_BLK_F_SEG_MAX; - self.base.device_features |= 1_u64 << VIRTIO_F_RING_EVENT_IDX; - - self.build_device_config_space(); - - if self.blk_cfg.queues > 1 { - self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; - self.config_space.num_queues = self.blk_cfg.queues; - } - - self.block_backend = None; - self.disk_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; - self.req_align = 1; - self.buf_align = 1; if !self.blk_cfg.path_on_host.is_empty() { let drive_files = self.drive_files.lock().unwrap(); let file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; @@ -1092,8 +1074,37 @@ impl VirtioDevice for Block { let disk_size = backend.lock().unwrap().disk_size()?; self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; + } else { + self.req_align = 1; + self.buf_align = 1; + self.block_backend = None; + self.disk_sectors = DUMMY_IMG_SIZE >> SECTOR_SHIFT; } - self.config_space.capacity = self.disk_sectors; + + self.init_config_features()?; + + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { + self.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 + | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC + | 1_u64 << VIRTIO_F_RING_EVENT_IDX + | 1_u64 << VIRTIO_BLK_F_FLUSH + | 1_u64 << VIRTIO_BLK_F_SEG_MAX; + if self.blk_cfg.read_only { + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_RO; + }; + if self.blk_cfg.queues > 1 { + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; + } + if self.blk_cfg.discard { + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; + } + if self.blk_cfg.write_zeroes != WriteZeroesState::Off { + self.base.device_features |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; + } + self.build_device_config_space(); Ok(()) } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index e7eb1b8cc..90c8fad46 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1475,13 +1475,6 @@ impl VirtioDevice for Gpu { ); } - self.base.device_features = 1u64 << VIRTIO_F_VERSION_1; - self.base.device_features |= 1u64 << VIRTIO_F_RING_EVENT_IDX; - self.base.device_features |= 1u64 << VIRTIO_F_RING_INDIRECT_DESC; - if self.cfg.edid { - self.base.device_features |= 1 << VIRTIO_GPU_F_EDID; - } - let mut output_states = self.output_states.lock().unwrap(); output_states[0].width = self.cfg.xres; output_states[0].height = self.cfg.yres; @@ -1500,8 +1493,19 @@ impl VirtioDevice for Gpu { } drop(output_states); - self.build_device_config_space(); + self.init_config_features()?; + + Ok(()) + } + fn init_config_features(&mut self) -> Result<()> { + self.base.device_features = 1u64 << VIRTIO_F_VERSION_1 + | 1u64 << VIRTIO_F_RING_INDIRECT_DESC + | 1u64 << VIRTIO_F_RING_EVENT_IDX; + if self.cfg.edid { + self.base.device_features |= 1 << VIRTIO_GPU_F_EDID; + } + self.build_device_config_space(); Ok(()) } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index cd94f174a..f45bdc4d6 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1368,6 +1368,34 @@ impl VirtioDevice for Net { ); } + let queue_pairs = self.net_cfg.queues / 2; + if !self.net_cfg.host_dev_name.is_empty() { + self.taps = create_tap(None, Some(&self.net_cfg.host_dev_name), queue_pairs) + .with_context(|| "Failed to open tap with file path")?; + } else if let Some(fds) = self.net_cfg.tap_fds.as_mut() { + let mut created_fds = 0; + if let Some(taps) = &self.taps { + for (index, tap) in taps.iter().enumerate() { + if fds.get(index).map_or(-1, |fd| *fd as RawFd) == tap.as_raw_fd() { + created_fds += 1; + } + } + } + + if created_fds != fds.len() { + self.taps = create_tap(Some(fds), None, queue_pairs) + .with_context(|| "Failed to open tap")?; + } + } else { + self.taps = None; + } + + self.init_config_features()?; + + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { self.base.device_features = 1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GUEST_CSUM @@ -1396,28 +1424,6 @@ impl VirtioDevice for Net { locked_config.max_virtqueue_pairs = queue_pairs; } - if !self.net_cfg.host_dev_name.is_empty() { - self.taps = None; - self.taps = create_tap(None, Some(&self.net_cfg.host_dev_name), queue_pairs) - .with_context(|| "Failed to open tap with file path")?; - } else if let Some(fds) = self.net_cfg.tap_fds.as_mut() { - let mut created_fds = 0; - if let Some(taps) = &self.taps { - for (index, tap) in taps.iter().enumerate() { - if fds.get(index).map_or(-1, |fd| *fd as RawFd) == tap.as_raw_fd() { - created_fds += 1; - } - } - } - - if created_fds != fds.len() { - self.taps = create_tap(Some(fds), None, queue_pairs) - .with_context(|| "Failed to open tap")?; - } - } else { - self.taps = None; - } - // Using the first tap to test if all the taps have ufo. if let Some(tap) = self.taps.as_ref().map(|t| &t[0]) { if !tap.has_ufo() { diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 2d681ca27..e0af2d8ce 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -265,8 +265,12 @@ impl VirtioDevice for Rng { .with_context(|| "Failed to check random file")?; let file = File::open(&self.rng_cfg.random_file) .with_context(|| "Failed to open file of random number generator")?; - self.random_file = Some(file); + self.init_config_features()?; + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { self.base.device_features = 1 << VIRTIO_F_VERSION_1 as u64; Ok(()) } diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index eec76fc79..2885b7f39 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -154,7 +154,11 @@ impl VirtioDevice for ScsiCntlr { self.config.iothread, ); } + self.init_config_features()?; + Ok(()) + } + fn init_config_features(&mut self) -> Result<()> { self.config_space.num_queues = self.config.queues; self.config_space.max_sectors = 0xFFFF_u32; // cmd_per_lun: maximum number of linked commands can be sent to one LUN. 32bit. diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 75482d294..b4712c3b4 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -219,10 +219,14 @@ impl VirtioDevice for Serial { } fn realize(&mut self) -> Result<()> { + self.init_config_features()?; + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { self.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_CONSOLE_F_SIZE | 1_u64 << VIRTIO_CONSOLE_F_MULTIPORT; - Ok(()) } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index ac7915ef3..5243e92b9 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -368,6 +368,9 @@ pub trait VirtioDevice: Send + AsAny { /// Get the queue size of virtio device. fn queue_size_max(&self) -> u16; + /// Init device configure space and features. + fn init_config_features(&mut self) -> Result<()>; + /// Get device features from host. fn device_features(&self, features_select: u32) -> u32 { read_u32(self.virtio_base().device_features, features_select) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index c40e645da..8bde1d5a0 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -800,6 +800,11 @@ mod tests { fn realize(&mut self) -> Result<()> { self.b_realized = true; + self.init_config_features()?; + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { Ok(()) } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index e52d32ecd..e83218a98 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1495,6 +1495,11 @@ mod tests { } fn realize(&mut self) -> VirtioResult<()> { + self.init_config_features()?; + Ok(()) + } + + fn init_config_features(&mut self) -> VirtioResult<()> { Ok(()) } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 74f6a06e6..212341a69 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -138,7 +138,22 @@ impl VirtioDevice for Net { backends.push(backend); } - let mut vhost_features = backends[0] + let host_dev_name = match self.net_cfg.host_dev_name.as_str() { + "" => None, + _ => Some(self.net_cfg.host_dev_name.as_str()), + }; + + self.taps = create_tap(self.net_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) + .with_context(|| "Failed to create tap for vhost net")?; + self.backends = Some(backends); + + self.init_config_features()?; + + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { + let mut vhost_features = self.backends.as_ref().unwrap()[0] .get_features() .with_context(|| "Failed to get features for vhost net")?; vhost_features &= !(1_u64 << VHOST_NET_F_VIRTIO_NET_HDR); @@ -155,6 +170,7 @@ impl VirtioDevice for Net { let mut locked_config = self.config_space.lock().unwrap(); + let queue_pairs = self.net_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) @@ -168,14 +184,6 @@ impl VirtioDevice for Net { device_features |= build_device_config_space(&mut locked_config, mac); } - let host_dev_name = match self.net_cfg.host_dev_name.as_str() { - "" => None, - _ => Some(self.net_cfg.host_dev_name.as_str()), - }; - - self.taps = create_tap(self.net_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) - .with_context(|| "Failed to create tap for vhost net")?; - self.backends = Some(backends); self.base.device_features = device_features; self.vhost_features = vhost_features; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 4943f3bba..f4c7226da 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -186,11 +186,18 @@ impl VirtioDevice for Vsock { backend .set_owner() .with_context(|| "Failed to set owner for vsock")?; + self.backend = Some(backend); + + self.init_config_features()?; + + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { + let backend = self.backend.as_ref().unwrap(); self.base.device_features = backend .get_features() .with_context(|| "Failed to get features for vsock")?; - self.backend = Some(backend); - Ok(()) } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 110824c7f..1056b3d06 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -88,9 +88,24 @@ impl Block { self.client = Some(client); Ok(()) } +} + +impl VirtioDevice for Block { + fn virtio_base(&self) -> &VirtioBase { + &self.base + } + + fn virtio_base_mut(&mut self) -> &mut VirtioBase { + &mut self.base + } + + fn realize(&mut self) -> Result<()> { + self.init_client()?; + self.init_config_features()?; + Ok(()) + } - /// Negotiate features with spdk. - fn negotiate_features(&mut self) -> Result<()> { + fn init_config_features(&mut self) -> Result<()> { let locked_client = self.client.as_ref().unwrap().lock().unwrap(); let features = locked_client .get_features() @@ -161,23 +176,6 @@ impl Block { Ok(()) } -} - -impl VirtioDevice for Block { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } - - fn realize(&mut self) -> Result<()> { - self.init_client()?; - self.negotiate_features()?; - - Ok(()) - } fn queue_num(&self) -> usize { self.blk_cfg.queues as usize diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 5399b69c9..bd102139c 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -138,10 +138,6 @@ impl VirtioDevice for Fs { } fn realize(&mut self) -> Result<()> { - let tag_bytes_vec = self.fs_cfg.tag.clone().into_bytes(); - self.config_space.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); - self.config_space.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; - let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; let client = VhostUserClient::new( &self.mem_space, @@ -154,12 +150,24 @@ impl VirtioDevice for Fs { })?; let client = Arc::new(Mutex::new(client)); VhostUserClient::add_event(&client)?; + self.client = Some(client); + + self.init_config_features()?; + + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { + let tag_bytes_vec = self.fs_cfg.tag.clone().into_bytes(); + self.config_space.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); + self.config_space.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; + + let client = self.client.as_ref().unwrap(); self.base.device_features = client .lock() .unwrap() .get_features() .with_context(|| "Failed to get features for virtio fs")?; - self.client = Some(client); Ok(()) } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 4f3f9d8db..83bd7a5ea 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -108,11 +108,10 @@ impl VirtioDevice for Net { .net_cfg .socket_path .as_ref() - .map(|path| path.to_string()) .with_context(|| "vhost-user: socket path is not found")?; let client = VhostUserClient::new( &self.mem_space, - &socket_path, + socket_path, self.queue_num() as u64, VhostBackendType::TypeNet, ) @@ -121,7 +120,15 @@ impl VirtioDevice for Net { })?; let client = Arc::new(Mutex::new(client)); VhostUserClient::add_event(&client)?; + self.client = Some(client); + + self.init_config_features()?; + Ok(()) + } + + fn init_config_features(&mut self) -> Result<()> { + let client = self.client.as_ref().unwrap(); self.base.device_features = client .lock() .unwrap() @@ -150,8 +157,6 @@ impl VirtioDevice for Net { locked_config.max_virtqueue_pairs = queue_pairs; } - self.client = Some(client); - if let Some(mac) = &self.net_cfg.mac { self.base.device_features |= build_device_config_space(&mut locked_config, mac); } -- Gitee From 6b1267d678c02040c994b4d357d2ad0650e574cb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 21 Jul 2023 18:13:15 +0800 Subject: [PATCH 1252/2187] Refactory: Move queue_num and queue_size_max into VirtioBase Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 25 +++++++++---------------- virtio/src/device/block.rs | 12 +++--------- virtio/src/device/gpu.rs | 10 +--------- virtio/src/device/net.rs | 21 ++++++++------------- virtio/src/device/rng.rs | 10 +--------- virtio/src/device/scsi_cntlr.rs | 15 +++++---------- virtio/src/device/serial.rs | 17 ++++++----------- virtio/src/lib.rs | 16 +++++++++++++--- virtio/src/transport/virtio_mmio.rs | 10 +--------- virtio/src/transport/virtio_pci.rs | 14 +++++--------- virtio/src/vhost/kernel/net.rs | 21 ++++++++------------- virtio/src/vhost/kernel/vsock.rs | 10 +--------- virtio/src/vhost/user/block.rs | 13 ++++--------- virtio/src/vhost/user/fs.rs | 13 ++++--------- virtio/src/vhost/user/net.rs | 23 +++++++++-------------- 15 files changed, 78 insertions(+), 152 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index d2a8ad1ea..cb7865a7a 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -900,8 +900,16 @@ impl Balloon { /// /// * `bln_cfg` - Balloon configuration. pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc) -> Balloon { + let mut queue_num = QUEUE_NUM_BALLOON; + if bln_cfg.free_page_reporting { + queue_num += 1; + } + if bln_cfg.auto_balloon { + queue_num += 1; + } + Balloon { - base: VirtioBase::new(VIRTIO_TYPE_BALLOON), + base: VirtioBase::new(VIRTIO_TYPE_BALLOON, queue_num, DEFAULT_VIRTQUEUE_SIZE), bln_cfg: bln_cfg.clone(), actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, @@ -1007,21 +1015,6 @@ impl VirtioDevice for Balloon { Ok(()) } - fn queue_num(&self) -> usize { - let mut queue_num = QUEUE_NUM_BALLOON; - if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_REPORTING) { - queue_num += 1; - } - if virtio_has_feature(self.base.device_features, VIRTIO_BALLOON_F_MESSAGE_VQ) { - queue_num += 1; - } - queue_num - } - - fn queue_size_max(&self) -> u16 { - DEFAULT_VIRTQUEUE_SIZE - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let new_config = VirtioBalloonConfig { num_pages: self.num_pages, diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 1fad6dc5d..2d54e3138 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -974,8 +974,10 @@ impl Block { blk_cfg: BlkDevConfig, drive_files: Arc>>, ) -> Block { + let queue_num = blk_cfg.queues as usize; + let queue_size = blk_cfg.queue_size; Self { - base: VirtioBase::new(VIRTIO_TYPE_BLOCK), + base: VirtioBase::new(VIRTIO_TYPE_BLOCK, queue_num, queue_size), blk_cfg, req_align: 1, buf_align: 1, @@ -1114,14 +1116,6 @@ impl VirtioDevice for Block { Ok(()) } - fn queue_num(&self) -> usize { - self.blk_cfg.queues as usize - } - - fn queue_size_max(&self) -> u16 { - self.blk_cfg.queue_size - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_len = self.get_blk_config_size(); let config = &self.config_space.as_bytes()[..config_len]; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 90c8fad46..ca17159c0 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1445,7 +1445,7 @@ unsafe impl Send for Gpu {} impl Gpu { pub fn new(cfg: GpuDevConfig) -> Gpu { Self { - base: VirtioBase::new(VIRTIO_TYPE_GPU), + base: VirtioBase::new(VIRTIO_TYPE_GPU, QUEUE_NUM_GPU, DEFAULT_VIRTQUEUE_SIZE), cfg, ..Default::default() } @@ -1518,14 +1518,6 @@ impl VirtioDevice for Gpu { Ok(()) } - fn queue_num(&self) -> usize { - QUEUE_NUM_GPU - } - - fn queue_size_max(&self) -> u16 { - DEFAULT_VIRTQUEUE_SIZE - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); read_config_default(config_space.as_bytes(), offset, data) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index f45bdc4d6..24916c472 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1182,8 +1182,15 @@ pub struct Net { impl Net { pub fn new(net_cfg: NetworkInterfaceConfig) -> Self { + let queue_num = if net_cfg.mq { + (net_cfg.queues + 1) as usize + } else { + QUEUE_NUM_NET + }; + let queue_size = net_cfg.queue_size; + Self { - base: VirtioBase::new(VIRTIO_TYPE_NET), + base: VirtioBase::new(VIRTIO_TYPE_NET, queue_num, queue_size), net_cfg, ..Default::default() } @@ -1457,18 +1464,6 @@ impl VirtioDevice for Net { Ok(()) } - fn queue_num(&self) -> usize { - if self.net_cfg.mq { - (self.net_cfg.queues + 1) as usize - } else { - QUEUE_NUM_NET - } - } - - fn queue_size_max(&self) -> u16 { - self.net_cfg.queue_size - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); read_config_default(config_space.as_bytes(), offset, data) diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index e0af2d8ce..a39ce9c27 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -225,7 +225,7 @@ pub struct Rng { impl Rng { pub fn new(rng_cfg: RngConfig) -> Self { Rng { - base: VirtioBase::new(VIRTIO_TYPE_RNG), + base: VirtioBase::new(VIRTIO_TYPE_RNG, QUEUE_NUM_RNG, DEFAULT_VIRTQUEUE_SIZE), rng_cfg, ..Default::default() } @@ -275,14 +275,6 @@ impl VirtioDevice for Rng { Ok(()) } - fn queue_num(&self) -> usize { - QUEUE_NUM_RNG - } - - fn queue_size_max(&self) -> u16 { - DEFAULT_VIRTQUEUE_SIZE - } - fn read_config(&self, offset: u64, _data: &mut [u8]) -> Result<()> { bail!( "Reading device config space for rng is not supported, offset: {}", diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 2885b7f39..b1f1f29ec 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -119,8 +119,12 @@ pub struct ScsiCntlr { impl ScsiCntlr { pub fn new(config: ScsiCntlrConfig) -> ScsiCntlr { + // Note: config.queues <= MAX_VIRTIO_QUEUE(32). + let queue_num = config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM; + let queue_size = config.queue_size; + Self { - base: VirtioBase::new(VIRTIO_TYPE_SCSI), + base: VirtioBase::new(VIRTIO_TYPE_SCSI, queue_num, queue_size), config, ..Default::default() } @@ -181,15 +185,6 @@ impl VirtioDevice for ScsiCntlr { Ok(()) } - fn queue_num(&self) -> usize { - // Note: self.config.queues <= MAX_VIRTIO_QUEUE(32). - self.config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM - } - - fn queue_size_max(&self) -> u16 { - self.config.queue_size - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { read_config_default(self.config_space.as_bytes(), offset, data) } diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index b4712c3b4..25f1d3794 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -146,8 +146,13 @@ impl Serial { /// /// * `serial_cfg` - Device configuration set by user. pub fn new(serial_cfg: VirtioSerialInfo) -> Self { + // Each port has 2 queues(receiveq/transmitq). + // And there exist 2 control queues(control receiveq/control transmitq). + let queue_num = serial_cfg.max_ports as usize * 2 + 2; + let queue_size = DEFAULT_VIRTQUEUE_SIZE; + Serial { - base: VirtioBase::new(VIRTIO_TYPE_CONSOLE), + base: VirtioBase::new(VIRTIO_TYPE_CONSOLE, queue_num, queue_size), config_space: VirtioConsoleConfig::new(serial_cfg.max_ports), max_nr_ports: serial_cfg.max_ports, ..Default::default() @@ -230,16 +235,6 @@ impl VirtioDevice for Serial { Ok(()) } - fn queue_num(&self) -> usize { - // Each port has 2 queues(receiveq/transmitq). - // And there exist 2 control queues(control receiveq/control transmitq). - self.max_nr_ports as usize * 2 + 2 - } - - fn queue_size_max(&self) -> u16 { - DEFAULT_VIRTQUEUE_SIZE - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { read_config_default(self.config_space.as_bytes(), offset, data) } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 5243e92b9..f769f6af5 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -326,6 +326,10 @@ pub struct VirtioBase { device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, + /// The number of device queues. + queue_num: usize, + /// The max size of each queue. + queue_size_max: u16, /// Eventfd for device deactivate. deactivate_evts: Vec, /// Device is broken or not. @@ -333,9 +337,11 @@ pub struct VirtioBase { } impl VirtioBase { - fn new(device_type: u32) -> Self { + fn new(device_type: u32, queue_num: usize, queue_size_max: u16) -> Self { Self { device_type, + queue_num, + queue_size_max, ..Default::default() } } @@ -363,10 +369,14 @@ pub trait VirtioDevice: Send + AsAny { } /// Get the count of virtio device queues. - fn queue_num(&self) -> usize; + fn queue_num(&self) -> usize { + self.virtio_base().queue_num + } /// Get the queue size of virtio device. - fn queue_size_max(&self) -> u16; + fn queue_size_max(&self) -> u16 { + self.virtio_base().queue_size_max + } /// Init device configure space and features. fn init_config_features(&mut self) -> Result<()>; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 8bde1d5a0..8ca0b30ee 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -781,7 +781,7 @@ mod tests { } VirtioDeviceTest { - base: VirtioBase::new(VIRTIO_TYPE_BLOCK), + base: VirtioBase::new(VIRTIO_TYPE_BLOCK, QUEUE_NUM, QUEUE_SIZE), b_active: false, b_realized: false, config_space, @@ -808,14 +808,6 @@ mod tests { Ok(()) } - fn queue_num(&self) -> usize { - QUEUE_NUM - } - - fn queue_size_max(&self) -> u16 { - QUEUE_SIZE - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { read_config_default(&self.config_space, offset, data) } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index e83218a98..2358bc3a3 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1476,7 +1476,11 @@ mod tests { impl VirtioDeviceTest { pub fn new() -> Self { - let mut base = VirtioBase::new(VIRTIO_DEVICE_TEST_TYPE); + let mut base = VirtioBase::new( + VIRTIO_DEVICE_TEST_TYPE, + VIRTIO_DEVICE_QUEUE_NUM, + VIRTIO_DEVICE_QUEUE_SIZE, + ); base.device_features = 0xFFFF_FFF0; VirtioDeviceTest { base, @@ -1503,14 +1507,6 @@ mod tests { Ok(()) } - fn queue_num(&self) -> usize { - VIRTIO_DEVICE_QUEUE_NUM - } - - fn queue_size_max(&self) -> u16 { - VIRTIO_DEVICE_QUEUE_SIZE - } - fn read_config(&self, _offset: u64, mut _data: &mut [u8]) -> VirtioResult<()> { Ok(()) } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 212341a69..36b1d1e8b 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -97,8 +97,15 @@ pub struct Net { impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { + let queue_num = if cfg.mq { + (cfg.queues + 1) as usize + } else { + QUEUE_NUM_NET + }; + let queue_size = cfg.queue_size; + Net { - base: VirtioBase::new(VIRTIO_TYPE_NET), + base: VirtioBase::new(VIRTIO_TYPE_NET, queue_num, queue_size), net_cfg: cfg.clone(), config_space: Default::default(), taps: None, @@ -194,18 +201,6 @@ impl VirtioDevice for Net { Ok(()) } - fn queue_num(&self) -> usize { - if self.net_cfg.mq { - (self.net_cfg.queues + 1) as usize - } else { - QUEUE_NUM_NET - } - } - - fn queue_size_max(&self) -> u16 { - self.net_cfg.queue_size - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); read_config_default(config_space.as_bytes(), offset, data) diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index f4c7226da..0920bbb38 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -114,7 +114,7 @@ pub struct Vsock { impl Vsock { pub fn new(cfg: &VsockConfig, mem_space: &Arc) -> Self { Vsock { - base: VirtioBase::new(VIRTIO_TYPE_VSOCK), + base: VirtioBase::new(VIRTIO_TYPE_VSOCK, QUEUE_NUM_VSOCK, DEFAULT_VIRTQUEUE_SIZE), vsock_cfg: cfg.clone(), backend: None, config_space: Default::default(), @@ -201,14 +201,6 @@ impl VirtioDevice for Vsock { Ok(()) } - fn queue_num(&self) -> usize { - QUEUE_NUM_VSOCK - } - - fn queue_size_max(&self) -> u16 { - DEFAULT_VIRTQUEUE_SIZE - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { match offset { 0 if data.len() == 8 => LittleEndian::write_u64(data, self.vsock_cfg.guest_cid), diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 1056b3d06..fb68d72ea 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -46,8 +46,11 @@ pub struct Block { impl Block { pub fn new(cfg: &BlkDevConfig, mem_space: &Arc) -> Self { + let queue_num = cfg.queues as usize; + let queue_size = cfg.queue_size; + Block { - base: VirtioBase::new(VIRTIO_TYPE_BLOCK), + base: VirtioBase::new(VIRTIO_TYPE_BLOCK, queue_num, queue_size), blk_cfg: cfg.clone(), config_space: Default::default(), mem_space: mem_space.clone(), @@ -177,14 +180,6 @@ impl VirtioDevice for Block { Ok(()) } - fn queue_num(&self) -> usize { - self.blk_cfg.queues as usize - } - - fn queue_size_max(&self) -> u16 { - self.blk_cfg.queue_size - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { read_config_default(self.config_space.as_bytes(), offset, data) } diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index bd102139c..0b5334d67 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -116,8 +116,11 @@ impl Fs { /// `mem_space` - The address space of this Fs device. /// `enable_irqfd` - Whether irqfd is enabled on this Fs device. pub fn new(fs_cfg: FsConfig, mem_space: Arc, enable_irqfd: bool) -> Self { + let queue_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; + let queue_size = VIRTIO_FS_QUEUE_SIZE; + Fs { - base: VirtioBase::new(VIRTIO_TYPE_FS), + base: VirtioBase::new(VIRTIO_TYPE_FS, queue_num, queue_size), fs_cfg, config_space: VirtioFsConfig::default(), client: None, @@ -172,14 +175,6 @@ impl VirtioDevice for Fs { Ok(()) } - fn queue_num(&self) -> usize { - VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM - } - - fn queue_size_max(&self) -> u16 { - VIRTIO_FS_QUEUE_SIZE - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { read_config_default(self.config_space.as_bytes(), offset, data) } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 83bd7a5ea..e1a16851a 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -52,8 +52,16 @@ pub struct Net { impl Net { pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { + let queue_num = if cfg.mq { + // If support multi-queue, it should add 1 control queue. + (cfg.queues + 1) as usize + } else { + QUEUE_NUM_NET + }; + let queue_size = cfg.queue_size; + Net { - base: VirtioBase::new(VIRTIO_TYPE_NET), + base: VirtioBase::new(VIRTIO_TYPE_NET, queue_num, queue_size), net_cfg: cfg.clone(), config_space: Default::default(), mem_space: mem_space.clone(), @@ -164,19 +172,6 @@ impl VirtioDevice for Net { Ok(()) } - fn queue_num(&self) -> usize { - if self.net_cfg.mq { - // If support multi-queue, it should add 1 control queue. - (self.net_cfg.queues + 1) as usize - } else { - QUEUE_NUM_NET - } - } - - fn queue_size_max(&self) -> u16 { - self.net_cfg.queue_size - } - fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_space = self.config_space.lock().unwrap(); read_config_default(config_space.as_bytes(), offset, data) -- Gitee From 3d59b507dc0bf11b619317f20b143ef8c4039e8f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 11:13:10 +0800 Subject: [PATCH 1253/2187] Refactory: Move queues into VirtioBase Signed-off-by: Keqian Zhu --- virtio/src/device/balloon.rs | 7 ++-- virtio/src/device/block.rs | 12 ++----- virtio/src/device/gpu.rs | 2 +- virtio/src/device/net.rs | 2 +- virtio/src/device/rng.rs | 2 +- virtio/src/device/scsi_cntlr.rs | 2 +- virtio/src/device/serial.rs | 4 +-- virtio/src/lib.rs | 5 +-- virtio/src/transport/virtio_mmio.rs | 39 ++++++++++---------- virtio/src/transport/virtio_pci.rs | 56 ++++++++++++++--------------- virtio/src/vhost/kernel/net.rs | 13 ++++--- virtio/src/vhost/kernel/vsock.rs | 2 +- virtio/src/vhost/user/block.rs | 3 +- virtio/src/vhost/user/fs.rs | 4 +-- virtio/src/vhost/user/net.rs | 10 +++--- 15 files changed, 75 insertions(+), 88 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index cb7865a7a..7e902cabf 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1065,9 +1065,9 @@ impl VirtioDevice for Balloon { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = &self.base.queues; if queues.len() != self.queue_num() { return Err(anyhow!(VirtioError::IncorrectQueueNum( self.queue_num(), @@ -1558,9 +1558,8 @@ mod tests { monitor_interval: 0, }; let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); - assert!(bln - .activate(mem_space, interrupt_cb, &queues, queue_evts) - .is_err()); + bln.base.queues = queues; + assert!(bln.activate(mem_space, interrupt_cb, queue_evts).is_err()); } #[test] diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 2d54e3138..c9dab508e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1135,10 +1135,10 @@ impl VirtioDevice for Block { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { self.interrupt_cb = Some(interrupt_cb.clone()); + let queues = self.base.queues.clone(); for (index, queue) in queues.iter().enumerate() { if !queue.lock().unwrap().is_enabled() { continue; @@ -1570,18 +1570,12 @@ mod tests { queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; - let queues: Vec>> = - vec![Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap()))]; + block.base.queues = vec![Arc::new(Mutex::new(Queue::new(queue_config, 1).unwrap()))]; let event = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); // activate block device block - .activate( - mem_space.clone(), - interrupt_cb, - &queues, - vec![event.clone()], - ) + .activate(mem_space.clone(), interrupt_cb, vec![event.clone()]) .unwrap(); // make first descriptor entry diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index ca17159c0..710a32845 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1542,9 +1542,9 @@ impl VirtioDevice for Gpu { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = &self.base.queues; if queues.len() != QUEUE_NUM_GPU { return Err(anyhow!(VirtioError::IncorrectQueueNum( QUEUE_NUM_GPU, diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 24916c472..16360ac3e 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1490,9 +1490,9 @@ impl VirtioDevice for Net { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = self.base.queues.clone(); let queue_num = queues.len(); let ctrl_info = Arc::new(Mutex::new(CtrlInfo::new(self.config_space.clone()))); self.ctrl_info = Some(ctrl_info.clone()); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index a39ce9c27..b45c9bad6 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -293,9 +293,9 @@ impl VirtioDevice for Rng { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = &self.base.queues; let handler = RngHandler { queue: queues[0].clone(), queue_evt: queue_evts[0].clone(), diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index b1f1f29ec..58ae68fb6 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -201,9 +201,9 @@ impl VirtioDevice for ScsiCntlr { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = self.base.queues.clone(); if queues.len() < SCSI_MIN_QUEUE_NUM { bail!("virtio scsi controller queues num can not be less than 3!"); } diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 25f1d3794..a61b6205a 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -247,9 +247,9 @@ impl VirtioDevice for Serial { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = self.base.queues.clone(); if queues.len() != self.queue_num() { return Err(anyhow!(VirtioError::IncorrectQueueNum( self.queue_num(), @@ -288,7 +288,7 @@ impl VirtioDevice for Serial { self.control_queues_activate( mem_space, interrupt_cb, - queues, + &queues, queue_evts, self.base.broken.clone(), )?; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index f769f6af5..9e11b53ba 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -330,6 +330,8 @@ pub struct VirtioBase { queue_num: usize, /// The max size of each queue. queue_size_max: u16, + /// Virtio queues. + queues: Vec>>, /// Eventfd for device deactivate. deactivate_evts: Vec, /// Device is broken or not. @@ -430,7 +432,6 @@ pub trait VirtioDevice: Send + AsAny { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()>; @@ -470,7 +471,7 @@ pub trait VirtioDevice: Send + AsAny { /// Get whether the virtio device has a control queue, /// devices with a control queue should override this function. - fn has_control_queue(&mut self) -> bool { + fn has_control_queue(&self) -> bool { false } } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 8ca0b30ee..e2ea98147 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -333,8 +333,6 @@ pub struct VirtioMmioDevice { state: Arc>, // System address space. mem_space: Arc, - // Virtio queues. - queues: Vec>>, // System Resource of device. res: SysRes, /// The function for interrupt triggering. @@ -356,7 +354,6 @@ impl VirtioMmioDevice { config_space: VirtioMmioCommonConfig::new(&device_clone), })), mem_space: mem_space.clone(), - queues: Vec::new(), res: SysRes::default(), interrupt_cb: None, } @@ -403,11 +400,12 @@ impl VirtioMmioDevice { let queue_num = locked_state.config_space.queue_num; let queue_type = locked_state.config_space.queue_type; let queues_config = &mut locked_state.config_space.queues_config[0..queue_num]; - let dev_lock = self.device.lock().unwrap(); + let mut dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = &dev_lock.virtio_base().broken; + let mut queues = Vec::with_capacity(queue_num); for q_config in queues_config.iter_mut() { q_config.set_addr_cache( self.mem_space.clone(), @@ -420,8 +418,9 @@ impl VirtioMmioDevice { if !queue.is_valid(&self.mem_space) { bail!("Invalid queue"); } - self.queues.push(Arc::new(Mutex::new(queue))); + queues.push(Arc::new(Mutex::new(queue))); } + dev_lock.virtio_base_mut().queues = queues; drop(dev_lock); drop(locked_state); @@ -438,12 +437,10 @@ impl VirtioMmioDevice { self.device.lock().unwrap().set_guest_notifiers(&events)?; if let Some(cb) = self.interrupt_cb.clone() { - self.device.lock().unwrap().activate( - self.mem_space.clone(), - cb, - &self.queues, - queue_evts, - )?; + self.device + .lock() + .unwrap() + .activate(self.mem_space.clone(), cb, queue_evts)?; } else { bail!("Failed to activate device: No interrupt callback"); } @@ -655,7 +652,8 @@ impl StateTransfer for VirtioMmioDevice { fn get_state_vec(&self) -> migration::Result> { let mut state = self.state.lock().unwrap(); - for (index, queue) in self.queues.iter().enumerate() { + let locked_dev = self.device.lock().unwrap(); + for (index, queue) in locked_dev.virtio_base().queues.iter().enumerate() { state.config_space.queues_config[index] = queue.lock().unwrap().vring.get_queue_config(); } @@ -674,11 +672,11 @@ impl StateTransfer for VirtioMmioDevice { let mut queue_states = locked_state.config_space.queues_config [0..locked_state.config_space.queue_num] .to_vec(); - let dev_lock = self.device.lock().unwrap(); + let mut dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = &dev_lock.virtio_base().broken; - self.queues = queue_states + dev_lock.virtio_base_mut().queues = queue_states .iter_mut() .map(|queue_state| { queue_state.set_addr_cache( @@ -712,12 +710,12 @@ impl MigrationHook for VirtioMmioDevice { } if let Some(cb) = self.interrupt_cb.clone() { - if let Err(e) = self.device.lock().unwrap().activate( - self.mem_space.clone(), - cb, - &self.queues, - queue_evts, - ) { + if let Err(e) = + self.device + .lock() + .unwrap() + .activate(self.mem_space.clone(), cb, queue_evts) + { bail!("Failed to resume virtio mmio device: {}", e); } } else { @@ -824,7 +822,6 @@ mod tests { &mut self, _mem_space: Arc, _interrupt_cb: Arc, - _queues: &[Arc>], mut _queue_evts: Vec>, ) -> Result<()> { self.b_active = true; diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 2358bc3a3..466e5883b 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -595,8 +595,6 @@ pub struct VirtioPciDevice { notify_eventfds: Arc, /// The function for interrupt triggering interrupt_cb: Option>, - /// Virtio queues. The vector and Queue will be shared acrossing thread, so all with Arc> wrapper. - queues: Arc>>>>, /// Multi-Function flag. multi_func: bool, /// If the device need to register irqfd to kvm. @@ -630,7 +628,6 @@ impl VirtioPciDevice { parent_bus, notify_eventfds: Arc::new(NotifyEventFds::new(queue_num)), interrupt_cb: None, - queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))), multi_func, need_irqfd: false, } @@ -725,8 +722,8 @@ impl VirtioPciDevice { let queue_type = common_cfg_lock.queue_type; let queues_config = &mut common_cfg_lock.queues_config; - let mut locked_queues = self.queues.lock().unwrap(); - let dev_lock = self.device.lock().unwrap(); + let mut queues = Vec::new(); + let mut dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; let broken = &dev_lock.virtio_base().broken; @@ -747,10 +744,10 @@ impl VirtioPciDevice { return false; } let arc_queue = Arc::new(Mutex::new(queue)); - locked_queues.push(arc_queue.clone()); + queues.push(arc_queue.clone()); } + dev_lock.virtio_base_mut().queues = queues; drop(dev_lock); - drop(locked_queues); update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); if self.need_irqfd { @@ -780,7 +777,6 @@ impl VirtioPciDevice { if let Err(e) = self.device.lock().unwrap().activate( self.sys_mem.clone(), self.interrupt_cb.clone().unwrap(), - &self.queues.lock().unwrap(), queue_evts, ) { error!("Failed to activate device, error is {:?}", e); @@ -799,7 +795,7 @@ impl VirtioPciDevice { } } - self.queues.lock().unwrap().clear(); + self.device.lock().unwrap().virtio_base_mut().queues.clear(); if self.device_activated.load(Ordering::Acquire) { self.device_activated.store(false, Ordering::Release); if let Err(e) = self.device.lock().unwrap().deactivate() { @@ -1013,18 +1009,19 @@ impl VirtioPciDevice { } fn queues_register_irqfd(&self, call_fds: &[Arc]) -> bool { - let mut locked_msix = if let Some(msix) = &self.config.msix { - msix.lock().unwrap() - } else { + if self.config.msix.is_none() { error!("Failed to get msix in virtio pci device configure"); return false; - }; + } + + let locked_dev = self.device.lock().unwrap(); + let mut locked_msix = self.config.msix.as_ref().unwrap().lock().unwrap(); - let locked_queues = self.queues.lock().unwrap(); - for (queue_index, queue_mutex) in locked_queues.iter().enumerate() { - if self.device.lock().unwrap().has_control_queue() - && queue_index + 1 == locked_queues.len() - && locked_queues.len() % 2 != 0 + let queues = &locked_dev.virtio_base().queues; + for (queue_index, queue_mutex) in queues.iter().enumerate() { + if locked_dev.has_control_queue() + && queue_index + 1 == queues.len() + && queues.len() % 2 != 0 { break; } @@ -1343,8 +1340,9 @@ impl StateTransfer for VirtioPciDevice { state.activated = self.device_activated.load(Ordering::Relaxed); state.dev_id = self.dev_id.load(Ordering::Acquire); { - let locked_queues = self.queues.lock().unwrap(); - for (index, queue) in locked_queues.iter().enumerate() { + let locked_dev = self.device.lock().unwrap(); + let queues = &locked_dev.virtio_base().queues; + for (index, queue) in queues.iter().enumerate() { state.queues_config[index] = queue.lock().unwrap().vring.get_queue_config(); state.queue_num += 1; } @@ -1392,7 +1390,8 @@ impl StateTransfer for VirtioPciDevice { self.dev_id.store(pci_state.dev_id, Ordering::Release); { let queue_type = self.common_config.lock().unwrap().queue_type; - let mut locked_queues = self.queues.lock().unwrap(); + let mut locked_dev = self.device.lock().unwrap(); + let queues = &mut locked_dev.virtio_base_mut().queues; let dev_lock = self.device.lock().unwrap(); let features = (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; @@ -1404,7 +1403,7 @@ impl StateTransfer for VirtioPciDevice { features, broken, ); - locked_queues.push(Arc::new(Mutex::new( + queues.push(Arc::new(Mutex::new( Queue::new(*queue_state, queue_type).unwrap(), ))) } @@ -1434,12 +1433,12 @@ impl MigrationHook for VirtioPciDevice { let queue_evts = (*self.notify_eventfds).clone().events; if let Some(cb) = self.interrupt_cb.clone() { - if let Err(e) = self.device.lock().unwrap().activate( - self.sys_mem.clone(), - cb, - &self.queues.lock().unwrap(), - queue_evts, - ) { + if let Err(e) = + self.device + .lock() + .unwrap() + .activate(self.sys_mem.clone(), cb, queue_evts) + { error!("Failed to resume device, error is {:?}", e); } } else { @@ -1519,7 +1518,6 @@ mod tests { &mut self, _mem_space: Arc, _interrupt_cb: Arc, - _queues: &[Arc>], _queue_evts: Vec>, ) -> VirtioResult<()> { self.is_activated = true; diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 36b1d1e8b..08decb276 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -30,12 +30,11 @@ use crate::read_config_default; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, MAC_ADDR_LEN}, error::VirtioError, - virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioBase, VirtioDevice, - VirtioInterrupt, VirtioNetConfig, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, + virtio_has_feature, CtrlVirtio, NetCtrlHandler, VirtioBase, VirtioDevice, VirtioInterrupt, + VirtioNetConfig, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, + VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; /// Number of virtqueues. @@ -240,9 +239,9 @@ impl VirtioDevice for Net { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = self.base.queues.clone(); let queue_num = queues.len(); let driver_features = self.base.driver_features; if (driver_features & 1 << VIRTIO_NET_F_CTRL_VQ != 0) && (queue_num % 2 != 0) { diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 0920bbb38..fb0e21c4c 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -237,10 +237,10 @@ impl VirtioDevice for Vsock { &mut self, _: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { let cid = self.vsock_cfg.guest_cid; + let queues = &self.base.queues; // The receive queue and transmit queue will be handled in vhost. let vhost_queues = queues[..2].to_vec(); let mut host_notifies = Vec::new(); diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index fb68d72ea..95e427298 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -207,7 +207,6 @@ impl VirtioDevice for Block { &mut self, _mem_space: Arc, _interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { let mut client = match &self.client { @@ -215,7 +214,7 @@ impl VirtioDevice for Block { None => return Err(anyhow!("Failed to get client for vhost-user blk")), }; client.features = self.base.driver_features; - client.set_queues(queues); + client.set_queues(&self.base.queues); client.set_queue_evts(&queue_evts); client.activate_vhost_user()?; Ok(()) diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 0b5334d67..d11bc2777 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -33,7 +33,7 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; +use super::super::super::{VirtioDevice, VIRTIO_TYPE_FS}; use super::super::{VhostNotify, VhostOps}; use super::{VhostBackendType, VhostUserClient}; use crate::{read_config_default, VirtioBase, VirtioInterrupt, VirtioInterruptType}; @@ -187,9 +187,9 @@ impl VirtioDevice for Fs { &mut self, _mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = &self.base.queues; let mut host_notifies = Vec::new(); let mut client = match &self.client { Some(client) => client.lock().unwrap(), diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index e1a16851a..53f57809c 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -24,8 +24,8 @@ use super::super::VhostOps; use super::{VhostBackendType, VhostUserClient}; use crate::{ device::net::{build_device_config_space, CtrlInfo, MAC_ADDR_LEN}, - read_config_default, virtio_has_feature, CtrlVirtio, NetCtrlHandler, Queue, VirtioBase, - VirtioDevice, VirtioInterrupt, VirtioNetConfig, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, + read_config_default, virtio_has_feature, CtrlVirtio, NetCtrlHandler, VirtioBase, VirtioDevice, + VirtioInterrupt, VirtioNetConfig, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_F_CTRL_MAC_ADDR, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, @@ -199,9 +199,9 @@ impl VirtioDevice for Net { &mut self, mem_space: Arc, interrupt_cb: Arc, - queues: &[Arc>], queue_evts: Vec>, ) -> Result<()> { + let queues = self.base.queues.clone(); let queue_num = queues.len(); let driver_features = self.base.driver_features; let has_control_queue = @@ -240,7 +240,7 @@ impl VirtioDevice for Net { client.set_queues(&queues[..(queue_num - 1)]); client.set_queue_evts(&queue_evts[..(queue_num - 1)]); } else { - client.set_queues(queues); + client.set_queues(&queues); client.set_queue_evts(&queue_evts); } client.activate_vhost_user()?; @@ -275,7 +275,7 @@ impl VirtioDevice for Net { Ok(()) } - fn has_control_queue(&mut self) -> bool { + fn has_control_queue(&self) -> bool { virtio_has_feature(self.base.device_features, VIRTIO_NET_F_CTRL_VQ) } } -- Gitee From e486397aaa7ee057e6ac40b49122cd3c8af0f627 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 22 Jul 2023 11:13:34 +0800 Subject: [PATCH 1254/2187] Refactory: Move some property from virtio transport to base Signed-off-by: Keqian Zhu --- pci/src/config.rs | 12 +- virtio/src/lib.rs | 264 +++++++- virtio/src/transport/virtio_mmio.rs | 883 +++++++++---------------- virtio/src/transport/virtio_pci.rs | 978 ++++++++++++---------------- 4 files changed, 1001 insertions(+), 1136 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 2bf9b0800..8ed5e1e57 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -17,7 +17,7 @@ use address_space::Region; use log::{error, warn}; use crate::intx::Intx; -use crate::msix::{is_msix_enabled, Msix}; +use crate::msix::{is_msix_enabled, Msix, MSIX_TABLE_ENTRY_SIZE}; use crate::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, @@ -1172,6 +1172,16 @@ impl PciConfig { pub fn set_interrupt_pin(&mut self) { self.config[INTERRUPT_PIN as usize] = 0x01; } + + pub fn revise_msix_vector(&self, vector_nr: u32) -> bool { + if self.msix.is_none() { + return false; + } + + let table_len = self.msix.as_ref().unwrap().lock().unwrap().table.len(); + let max_vector = table_len / MSIX_TABLE_ENTRY_SIZE as usize; + vector_nr < max_vector as u32 + } } #[cfg(test)] diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9e11b53ba..818f20d1b 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -34,15 +34,16 @@ pub mod vhost; use std::cmp; use std::io::Write; use std::os::unix::prelude::RawFd; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU8, Ordering}; use std::sync::{Arc, Mutex}; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; use address_space::AddressSpace; use machine_manager::config::ConfigCheck; +use migration_derive::ByteCode; use util::aio::{mem_to_buf, Iovec}; use util::num_ops::{read_u32, write_u32}; use util::AsAny; @@ -326,10 +327,30 @@ pub struct VirtioBase { device_features: u64, /// Bit mask of features negotiated by the backend and the frontend. driver_features: u64, + /// Device (host) feature-setting selector. + hfeatures_sel: u32, + /// Driver (guest) feature-setting selector. + gfeatures_sel: u32, + /// Interrupt status. + interrupt_status: Arc, + /// Device status. + device_status: Arc, + /// If this device is activated or not. + device_activated: Arc, + /// Configuration atomicity value. + config_generation: Arc, + /// The MSI-X vector for config change notification. + config_vector: Arc, + /// The type of queue, split-vring or packed-vring. + queue_type: u16, /// The number of device queues. queue_num: usize, /// The max size of each queue. queue_size_max: u16, + /// Queue selector. + queue_select: u16, + /// The configuration of queues. + queues_config: Vec, /// Virtio queues. queues: Vec>>, /// Eventfd for device deactivate. @@ -338,15 +359,122 @@ pub struct VirtioBase { broken: Arc, } +#[derive(Copy, Clone, ByteCode)] +struct VirtioBaseState { + device_activated: bool, + hfeatures_sel: u32, + gfeatures_sel: u32, + interrupt_status: u32, + device_status: u32, + config_generation: u8, + queue_select: u16, + config_vector: u16, + queues_config: [QueueConfig; 32], + /// The number of activated queues. + queue_num: usize, + queue_type: u16, +} + impl VirtioBase { fn new(device_type: u32, queue_num: usize, queue_size_max: u16) -> Self { Self { device_type, + config_vector: Arc::new(AtomicU16::new(INVALID_VECTOR_NUM)), queue_num, queue_size_max, + queue_type: QUEUE_TYPE_SPLIT_VRING, + queues_config: vec![QueueConfig::new(queue_size_max); queue_num], ..Default::default() } } + + fn reset(&mut self) { + // device_type, device_features, queue_num and queue_size_max + // is not mutable, thus no need to reset. + self.driver_features = 0; + self.hfeatures_sel = 0; + self.gfeatures_sel = 0; + self.interrupt_status.store(0, Ordering::SeqCst); + self.device_status.store(0, Ordering::SeqCst); + self.device_activated.store(false, Ordering::SeqCst); + self.config_generation.store(0, Ordering::SeqCst); + self.config_vector + .store(INVALID_VECTOR_NUM, Ordering::SeqCst); + self.queue_type = QUEUE_TYPE_SPLIT_VRING; + self.queue_select = 0; + self.queues_config.iter_mut().for_each(|q| q.reset()); + self.queues.clear(); + self.broken.store(false, Ordering::SeqCst); + } + + fn get_state(&self) -> VirtioBaseState { + let mut state = VirtioBaseState { + device_activated: self.device_activated.load(Ordering::Acquire), + hfeatures_sel: self.hfeatures_sel, + gfeatures_sel: self.gfeatures_sel, + interrupt_status: self.interrupt_status.load(Ordering::Acquire), + device_status: self.device_status.load(Ordering::Acquire), + config_generation: self.config_generation.load(Ordering::Acquire), + queue_select: self.queue_select, + config_vector: self.config_vector.load(Ordering::Acquire), + queues_config: [QueueConfig::default(); 32], + queue_num: 0, + queue_type: self.queue_type, + }; + + for (index, queue) in self.queues_config.iter().enumerate() { + state.queues_config[index] = *queue; + } + for (index, queue) in self.queues.iter().enumerate() { + state.queues_config[index] = queue.lock().unwrap().vring.get_queue_config(); + state.queue_num += 1; + } + + state + } + + fn set_state( + &mut self, + state: &VirtioBaseState, + mem_space: Arc, + interrupt_cb: Arc, + ) { + self.device_activated + .store(state.device_activated, Ordering::SeqCst); + self.hfeatures_sel = state.hfeatures_sel; + self.gfeatures_sel = state.gfeatures_sel; + self.interrupt_status + .store(state.interrupt_status, Ordering::SeqCst); + self.device_status + .store(state.device_status, Ordering::SeqCst); + self.config_generation + .store(state.config_generation, Ordering::SeqCst); + self.queue_select = state.queue_select; + self.config_vector + .store(state.config_vector, Ordering::SeqCst); + self.queues_config = state.queues_config[..self.queue_num].to_vec(); + self.queue_type = state.queue_type; + + if state.queue_num == 0 { + return; + } + + let mut queues = Vec::with_capacity(self.queue_num); + for queue_config in self.queues_config.iter_mut().take(state.queue_num) { + if queue_config.ready { + queue_config.set_addr_cache( + mem_space.clone(), + interrupt_cb.clone(), + self.driver_features, + &self.broken, + ); + } + queues.push(Arc::new(Mutex::new( + Queue::new(*queue_config, self.queue_type).unwrap(), + ))); + } + self.queues = queues; + } } /// The trait for virtio device operations. @@ -413,6 +541,138 @@ pub trait VirtioDevice: Send + AsAny { read_u32(self.virtio_base().driver_features, features_select) } + /// Get host feature selector. + fn hfeatures_sel(&self) -> u32 { + self.virtio_base().hfeatures_sel + } + + /// Set host feature selector. + fn set_hfeatures_sel(&mut self, val: u32) { + self.virtio_base_mut().hfeatures_sel = val; + } + + /// Get guest feature selector. + fn gfeatures_sel(&self) -> u32 { + self.virtio_base().gfeatures_sel + } + + /// Set guest feature selector. + fn set_gfeatures_sel(&mut self, val: u32) { + self.virtio_base_mut().gfeatures_sel = val; + } + + /// Check whether virtio device status is as expected. + fn check_device_status(&self, set: u32, clr: u32) -> bool { + self.device_status() & (set | clr) == set + } + + /// Get the status of virtio device. + fn device_status(&self) -> u32 { + self.virtio_base().device_status.load(Ordering::Acquire) + } + + /// Set the status of virtio device. + fn set_device_status(&mut self, val: u32) { + self.virtio_base_mut() + .device_status + .store(val, Ordering::SeqCst) + } + + /// Check device is activated or not. + fn device_activated(&self) -> bool { + self.virtio_base().device_activated.load(Ordering::Acquire) + } + + /// Set device activate status. + fn set_device_activated(&mut self, val: bool) { + self.virtio_base_mut() + .device_activated + .store(val, Ordering::SeqCst) + } + + /// Get config generation. + fn config_generation(&self) -> u8 { + self.virtio_base().config_generation.load(Ordering::Acquire) + } + + /// Set config generation. + fn set_config_generation(&mut self, val: u8) { + self.virtio_base_mut() + .config_generation + .store(val, Ordering::SeqCst); + } + + /// Get msix vector of config change interrupt. + fn config_vector(&self) -> u16 { + self.virtio_base().config_vector.load(Ordering::Acquire) + } + + /// Set msix vector of config change interrupt. + fn set_config_vector(&mut self, val: u16) { + self.virtio_base_mut() + .config_vector + .store(val, Ordering::SeqCst); + } + + /// Get virtqueue type. + fn queue_type(&self) -> u16 { + self.virtio_base().queue_type + } + + /// Set virtqueue type. + fn set_queue_type(&mut self, val: u16) { + self.virtio_base_mut().queue_type = val; + } + + /// Get virtqueue selector. + fn queue_select(&self) -> u16 { + self.virtio_base().queue_select + } + + /// Set virtqueue selector. + fn set_queue_select(&mut self, val: u16) { + self.virtio_base_mut().queue_select = val; + } + + /// Get virtqueue config. + fn queue_config(&self) -> Result<&QueueConfig> { + let queues_config = &self.virtio_base().queues_config; + let queue_select = self.virtio_base().queue_select; + queues_config + .get(queue_select as usize) + .with_context(|| "queue_select overflows") + } + + /// Get mutable virtqueue config. + fn queue_config_mut(&mut self, need_check: bool) -> Result<&mut QueueConfig> { + if need_check + && !self.check_device_status( + CONFIG_STATUS_FEATURES_OK, + CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FAILED, + ) + { + return Err(anyhow!(VirtioError::DevStatErr(self.device_status()))); + } + + let queue_select = self.virtio_base().queue_select; + let queues_config = &mut self.virtio_base_mut().queues_config; + return queues_config + .get_mut(queue_select as usize) + .with_context(|| "queue_select overflows"); + } + + /// Get ISR register. + fn interrupt_status(&self) -> u32 { + self.virtio_base().interrupt_status.load(Ordering::Acquire) + } + + /// Set ISR register. + fn set_interrupt_status(&mut self, val: u32) { + self.virtio_base_mut() + .interrupt_status + .store(val, Ordering::SeqCst) + } + /// Read data of config from guest. fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()>; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index e2ea98147..8e0c10dae 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use crate::error::VirtioError; @@ -26,11 +26,10 @@ use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; use crate::{ - virtio_has_feature, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, VirtioInterruptType, + virtio_has_feature, Queue, VirtioBaseState, VirtioDevice, VirtioInterrupt, VirtioInterruptType, CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, NOTIFY_REG_OFFSET, - QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, - VIRTIO_MMIO_INT_VRING, + QUEUE_TYPE_PACKED_VRING, VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, }; use anyhow::{anyhow, bail, Context, Result}; @@ -84,9 +83,6 @@ const VENDOR_ID: u32 = 0; const MMIO_MAGIC_VALUE: u32 = 0x7472_6976; const MMIO_VERSION: u32 = 2; -/// The maximum of virtio queue within a virtio device. -const MAXIMUM_NR_QUEUES: usize = 8; - /// HostNotifyInfo includes the info needed for notifying backend from guest. pub struct HostNotifyInfo { /// Eventfds which notify backend to use the avail ring. @@ -109,214 +105,7 @@ impl HostNotifyInfo { #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] pub struct VirtioMmioState { - /// Identify if this device is activated by frontend driver. - activated: bool, - /// Config space of virtio mmio device. - config_space: VirtioMmioCommonConfig, -} - -/// The configuration of virtio-mmio device, the fields refer to Virtio Spec. -#[derive(Copy, Clone, Default)] -pub struct VirtioMmioCommonConfig { - /// Bitmask of the features supported by the device (host)(32 bits per set). - features_select: u32, - /// Device (host) feature-setting selector. - acked_features_select: u32, - /// Interrupt status value. - interrupt_status: u32, - /// Device status. - device_status: u32, - /// Configuration atomicity value. - config_generation: u32, - /// Queue selector. - queue_select: u32, - /// The configuration of queues. - queues_config: [QueueConfig; MAXIMUM_NR_QUEUES], - /// The number of queues. - queue_num: usize, - /// The type of queue, either be split ring or packed ring. - queue_type: u16, -} - -impl VirtioMmioCommonConfig { - pub fn new(device: &Arc>) -> Self { - let locked_device = device.lock().unwrap(); - let mut queues_config = [QueueConfig::default(); 8]; - let queue_size = locked_device.queue_size_max(); - let queue_num = locked_device.queue_num(); - for queue_config in queues_config.iter_mut().take(queue_num) { - *queue_config = QueueConfig::new(queue_size); - } - - VirtioMmioCommonConfig { - queues_config, - queue_num, - queue_type: QUEUE_TYPE_SPLIT_VRING, - ..Default::default() - } - } - - /// Check whether virtio device status is as expected. - fn check_device_status(&self, set: u32, clr: u32) -> bool { - self.device_status & (set | clr) == set - } - - /// Get the status of virtio device - fn get_device_status(&self) -> u32 { - self.device_status - } - - /// Get mutable QueueConfig structure of virtio device. - fn get_mut_queue_config(&mut self) -> Result<&mut QueueConfig> { - if self.check_device_status( - CONFIG_STATUS_FEATURES_OK, - CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FAILED, - ) { - let queue_select = self.queue_select; - self.queues_config - .get_mut(queue_select as usize) - .with_context(|| { - format!( - "Mmio-reg queue_select {} overflows for mutable queue config", - queue_select, - ) - }) - } else { - Err(anyhow!(VirtioError::DevStatErr(self.device_status))) - } - } - - /// Get immutable QueueConfig structure of virtio device. - fn get_queue_config(&self) -> Result<&QueueConfig> { - let queue_select = self.queue_select; - self.queues_config - .get(queue_select as usize) - .with_context(|| { - format!( - "Mmio-reg queue_select overflows {} for immutable queue config", - queue_select, - ) - }) - } - - /// Read data from the common config of virtio device. - /// Return the config value in u32. - /// # Arguments - /// - /// * `device` - Virtio device entity. - /// * `offset` - The offset of common config. - fn read_common_config( - &mut self, - device: &Arc>, - interrupt_status: &Arc, - offset: u64, - ) -> Result { - let value = match offset { - MAGIC_VALUE_REG => MMIO_MAGIC_VALUE, - VERSION_REG => MMIO_VERSION, - DEVICE_ID_REG => device.lock().unwrap().device_type(), - VENDOR_ID_REG => VENDOR_ID, - DEVICE_FEATURES_REG => { - let mut features = device.lock().unwrap().device_features(self.features_select); - if self.features_select == 1 { - features |= 0x1; // enable support of VirtIO Version 1 - } - features - } - QUEUE_NUM_MAX_REG => self - .get_queue_config() - .map(|config| u32::from(config.max_size))?, - QUEUE_READY_REG => self.get_queue_config().map(|config| config.ready as u32)?, - INTERRUPT_STATUS_REG => { - self.interrupt_status = interrupt_status.load(Ordering::SeqCst); - self.interrupt_status - } - STATUS_REG => self.device_status, - CONFIG_GENERATION_REG => self.config_generation, - _ => { - return Err(anyhow!(VirtioError::MmioRegErr(offset))); - } - }; - - Ok(value) - } - - /// Write data to the common config of virtio device. - /// - /// # Arguments - /// - /// * `device` - Virtio device entity. - /// * `offset` - The offset of common config. - /// * `value` - The value to write. - /// - /// # Errors - /// - /// Returns Error if the offset is out of bound. - fn write_common_config( - &mut self, - device: &Arc>, - interrupt_status: &Arc, - offset: u64, - value: u32, - ) -> Result<()> { - match offset { - DEVICE_FEATURES_SEL_REG => self.features_select = value, - DRIVER_FEATURES_REG => { - if self.check_device_status( - CONFIG_STATUS_DRIVER, - CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_FAILED, - ) { - device - .lock() - .unwrap() - .set_driver_features(self.acked_features_select, value); - if self.acked_features_select == 1 - && virtio_has_feature(u64::from(value) << 32, VIRTIO_F_RING_PACKED) - { - self.queue_type = QUEUE_TYPE_PACKED_VRING; - } - } else { - return Err(anyhow!(VirtioError::DevStatErr(self.device_status))); - } - } - DRIVER_FEATURES_SEL_REG => self.acked_features_select = value, - QUEUE_SEL_REG => self.queue_select = value, - QUEUE_NUM_REG => self - .get_mut_queue_config() - .map(|config| config.size = value as u16)?, - QUEUE_READY_REG => self - .get_mut_queue_config() - .map(|config| config.ready = value == 1)?, - INTERRUPT_ACK_REG => { - if self.check_device_status(CONFIG_STATUS_DRIVER_OK, 0) { - self.interrupt_status = interrupt_status.fetch_and(!value, Ordering::SeqCst); - } - } - STATUS_REG => self.device_status = value, - QUEUE_DESC_LOW_REG => self.get_mut_queue_config().map(|config| { - config.desc_table = GuestAddress(config.desc_table.0 | u64::from(value)); - })?, - QUEUE_DESC_HIGH_REG => self.get_mut_queue_config().map(|config| { - config.desc_table = GuestAddress(config.desc_table.0 | (u64::from(value) << 32)); - })?, - QUEUE_AVAIL_LOW_REG => self.get_mut_queue_config().map(|config| { - config.avail_ring = GuestAddress(config.avail_ring.0 | u64::from(value)); - })?, - QUEUE_AVAIL_HIGH_REG => self.get_mut_queue_config().map(|config| { - config.avail_ring = GuestAddress(config.avail_ring.0 | (u64::from(value) << 32)); - })?, - QUEUE_USED_LOW_REG => self.get_mut_queue_config().map(|config| { - config.used_ring = GuestAddress(config.used_ring.0 | u64::from(value)); - })?, - QUEUE_USED_HIGH_REG => self.get_mut_queue_config().map(|config| { - config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); - })?, - _ => { - return Err(anyhow!(VirtioError::MmioRegErr(offset))); - } - }; - Ok(()) - } + virtio_base: VirtioBaseState, } /// virtio-mmio device structure. @@ -325,12 +114,8 @@ pub struct VirtioMmioDevice { pub device: Arc>, // EventFd used to send interrupt to VM interrupt_evt: Arc, - // Interrupt status. - interrupt_status: Arc, // HostNotifyInfo used for guest notifier host_notify_info: HostNotifyInfo, - // The state of virtio mmio device. - state: Arc>, // System address space. mem_space: Arc, // System Resource of device. @@ -347,12 +132,7 @@ impl VirtioMmioDevice { VirtioMmioDevice { device, interrupt_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - interrupt_status: Arc::new(AtomicU32::new(0)), host_notify_info: HostNotifyInfo::new(queue_num), - state: Arc::new(Mutex::new(VirtioMmioState { - activated: false, - config_space: VirtioMmioCommonConfig::new(&device_clone), - })), mem_space: mem_space.clone(), res: SysRes::default(), interrupt_cb: None, @@ -396,14 +176,12 @@ impl VirtioMmioDevice { /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate(&mut self) -> Result<()> { - let mut locked_state = self.state.lock().unwrap(); - let queue_num = locked_state.config_space.queue_num; - let queue_type = locked_state.config_space.queue_type; - let queues_config = &mut locked_state.config_space.queues_config[0..queue_num]; - let mut dev_lock = self.device.lock().unwrap(); - let features = - (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = &dev_lock.virtio_base().broken; + let mut locked_dev = self.device.lock().unwrap(); + let queue_num = locked_dev.queue_num(); + let queue_type = locked_dev.queue_type(); + let features = locked_dev.virtio_base().driver_features; + let broken = locked_dev.virtio_base().broken.clone(); + let queues_config = &mut locked_dev.virtio_base_mut().queues_config; let mut queues = Vec::with_capacity(queue_num); for q_config in queues_config.iter_mut() { @@ -411,7 +189,7 @@ impl VirtioMmioDevice { self.mem_space.clone(), self.interrupt_cb.clone().unwrap(), features, - broken, + &broken, ); let queue = Queue::new(*q_config, queue_type)?; @@ -420,9 +198,7 @@ impl VirtioMmioDevice { } queues.push(Arc::new(Mutex::new(queue))); } - dev_lock.virtio_base_mut().queues = queues; - drop(dev_lock); - drop(locked_state); + locked_dev.virtio_base_mut().queues = queues; let mut queue_evts = Vec::>::new(); for fd in self.host_notify_info.events.iter() { @@ -430,17 +206,14 @@ impl VirtioMmioDevice { } let mut events = Vec::new(); - for _i in 0..self.device.lock().unwrap().queue_num() { + for _i in 0..locked_dev.queue_num() { events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); } - self.device.lock().unwrap().set_guest_notifiers(&events)?; + locked_dev.set_guest_notifiers(&events)?; if let Some(cb) = self.interrupt_cb.clone() { - self.device - .lock() - .unwrap() - .activate(self.mem_space.clone(), cb, queue_evts)?; + locked_dev.activate(self.mem_space.clone(), cb, queue_evts)?; } else { bail!("Failed to activate device: No interrupt callback"); } @@ -449,23 +222,25 @@ impl VirtioMmioDevice { } fn assign_interrupt_cb(&mut self) { - let interrupt_status = self.interrupt_status.clone(); let interrupt_evt = self.interrupt_evt.clone(); - let cloned_state = self.state.clone(); + + let locked_dev = self.device.lock().unwrap(); + let virtio_base = locked_dev.virtio_base(); + let device_status = virtio_base.device_status.clone(); + let config_generation = virtio_base.config_generation.clone(); + let interrupt_status = virtio_base.interrupt_status.clone(); + let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, _queue: Option<&Queue>, needs_reset: bool| { let status = match int_type { VirtioInterruptType::Config => { - let mut locked_state = cloned_state.lock().unwrap(); if needs_reset { - locked_state.config_space.device_status |= CONFIG_STATUS_NEEDS_RESET; - if locked_state.config_space.device_status & CONFIG_STATUS_DRIVER_OK - == 0 - { - return Ok(()); - } + device_status.fetch_or(CONFIG_STATUS_NEEDS_RESET, Ordering::SeqCst); } - locked_state.config_space.config_generation += 1; + if device_status.load(Ordering::Acquire) & CONFIG_STATUS_DRIVER_OK == 0 { + return Ok(()); + } + config_generation.fetch_add(1, Ordering::SeqCst); // Use (CONFIG | VRING) instead of CONFIG, it can be used to solve the // IO stuck problem by change the device configure. VIRTIO_MMIO_INT_CONFIG | VIRTIO_MMIO_INT_VRING @@ -483,6 +258,115 @@ impl VirtioMmioDevice { self.interrupt_cb = Some(cb); } + + /// Read data from the common config of virtio device. + /// Return the config value in u32. + /// # Arguments + /// + /// * `offset` - The offset of common config. + fn read_common_config(&mut self, offset: u64) -> Result { + let locked_device = self.device.lock().unwrap(); + let value = match offset { + MAGIC_VALUE_REG => MMIO_MAGIC_VALUE, + VERSION_REG => MMIO_VERSION, + DEVICE_ID_REG => locked_device.device_type(), + VENDOR_ID_REG => VENDOR_ID, + DEVICE_FEATURES_REG => { + let hfeatures_sel = locked_device.hfeatures_sel(); + let mut features = locked_device.device_features(hfeatures_sel); + if hfeatures_sel == 1 { + features |= 0x1; // enable support of VirtIO Version 1 + } + features + } + QUEUE_NUM_MAX_REG => locked_device + .queue_config() + .map(|config| u32::from(config.max_size))?, + QUEUE_READY_REG => locked_device + .queue_config() + .map(|config| config.ready as u32)?, + INTERRUPT_STATUS_REG => locked_device.interrupt_status(), + STATUS_REG => locked_device.device_status(), + CONFIG_GENERATION_REG => locked_device.config_generation() as u32, + _ => { + return Err(anyhow!(VirtioError::MmioRegErr(offset))); + } + }; + + Ok(value) + } + + /// Write data to the common config of virtio device. + /// + /// # Arguments + /// + /// * `offset` - The offset of common config. + /// * `value` - The value to write. + /// + /// # Errors + /// + /// Returns Error if the offset is out of bound. + fn write_common_config(&mut self, offset: u64, value: u32) -> Result<()> { + let mut locked_device = self.device.lock().unwrap(); + match offset { + DEVICE_FEATURES_SEL_REG => locked_device.set_hfeatures_sel(value), + DRIVER_FEATURES_REG => { + if locked_device.check_device_status( + CONFIG_STATUS_DRIVER, + CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_FAILED, + ) { + let gfeatures_sel = locked_device.gfeatures_sel(); + locked_device.set_driver_features(gfeatures_sel, value); + if gfeatures_sel == 1 + && virtio_has_feature(u64::from(value) << 32, VIRTIO_F_RING_PACKED) + { + locked_device.set_queue_type(QUEUE_TYPE_PACKED_VRING); + } + } else { + return Err(anyhow!(VirtioError::DevStatErr( + locked_device.device_status() + ))); + } + } + DRIVER_FEATURES_SEL_REG => locked_device.set_gfeatures_sel(value), + QUEUE_SEL_REG => locked_device.set_queue_select(value as u16), + QUEUE_NUM_REG => locked_device + .queue_config_mut(true) + .map(|config| config.size = value as u16)?, + QUEUE_READY_REG => locked_device + .queue_config_mut(true) + .map(|config| config.ready = value == 1)?, + INTERRUPT_ACK_REG => { + if locked_device.check_device_status(CONFIG_STATUS_DRIVER_OK, 0) { + let isr = &locked_device.virtio_base_mut().interrupt_status; + isr.fetch_and(!value, Ordering::SeqCst); + } + } + STATUS_REG => locked_device.set_device_status(value), + QUEUE_DESC_LOW_REG => locked_device.queue_config_mut(true).map(|config| { + config.desc_table = GuestAddress(config.desc_table.0 | u64::from(value)); + })?, + QUEUE_DESC_HIGH_REG => locked_device.queue_config_mut(true).map(|config| { + config.desc_table = GuestAddress(config.desc_table.0 | (u64::from(value) << 32)); + })?, + QUEUE_AVAIL_LOW_REG => locked_device.queue_config_mut(true).map(|config| { + config.avail_ring = GuestAddress(config.avail_ring.0 | u64::from(value)); + })?, + QUEUE_AVAIL_HIGH_REG => locked_device.queue_config_mut(true).map(|config| { + config.avail_ring = GuestAddress(config.avail_ring.0 | (u64::from(value) << 32)); + })?, + QUEUE_USED_LOW_REG => locked_device.queue_config_mut(true).map(|config| { + config.used_ring = GuestAddress(config.used_ring.0 | u64::from(value)); + })?, + QUEUE_USED_HIGH_REG => locked_device.queue_config_mut(true).map(|config| { + config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); + })?, + _ => { + return Err(anyhow!(VirtioError::MmioRegErr(offset))); + } + }; + Ok(()) + } } impl SysBusDevOps for VirtioMmioDevice { @@ -490,11 +374,7 @@ impl SysBusDevOps for VirtioMmioDevice { fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { match offset { 0x00..=0xff if data.len() == 4 => { - let value = match self.state.lock().unwrap().config_space.read_common_config( - &self.device, - &self.interrupt_status, - offset, - ) { + let value = match self.read_common_config(offset) { Ok(v) => v, Err(ref e) => { error!( @@ -537,16 +417,10 @@ impl SysBusDevOps for VirtioMmioDevice { /// Write data by virtio driver from VM. fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { - let mut locked_state = self.state.lock().unwrap(); match offset { 0x00..=0xff if data.len() == 4 => { let value = LittleEndian::read_u32(data); - if let Err(ref e) = locked_state.config_space.write_common_config( - &self.device, - &self.interrupt_status, - offset, - value, - ) { + if let Err(ref e) = self.write_common_config(offset, value) { error!( "Failed to write mmio register {}, type: {}, {:?}", offset, @@ -556,15 +430,16 @@ impl SysBusDevOps for VirtioMmioDevice { return false; } - if locked_state.config_space.check_device_status( + let locked_dev = self.device.lock().unwrap(); + if locked_dev.check_device_status( CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER | CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_FAILED, - ) && !locked_state.activated + ) && !locked_dev.device_activated() { - drop(locked_state); + drop(locked_dev); if let Err(ref e) = self.activate() { error!( "Failed to activate dev, type: {}, {:?}", @@ -573,32 +448,25 @@ impl SysBusDevOps for VirtioMmioDevice { ); return false; } - self.state.lock().unwrap().activated = true; + self.device.lock().unwrap().set_device_activated(true); } } 0x100..=0xfff => { - if locked_state - .config_space - .check_device_status(CONFIG_STATUS_DRIVER, CONFIG_STATUS_FAILED) - { - if let Err(ref e) = self - .device - .lock() - .unwrap() - .write_config(offset - 0x100, data) - { + let mut locked_device = self.device.lock().unwrap(); + if locked_device.check_device_status(CONFIG_STATUS_DRIVER, CONFIG_STATUS_FAILED) { + if let Err(ref e) = locked_device.write_config(offset - 0x100, data) { error!( "Failed to write virtio-dev config space {}, type: {}, {:?}", offset - 0x100, - self.device.lock().unwrap().device_type(), + locked_device.device_type(), e, ); return false; } } else { error!("Failed to write virtio-dev config space: driver is not ready 0x{:X}, type: {}", - locked_state.config_space.get_device_status(), - self.device.lock().unwrap().device_type(), + locked_device.device_status(), + locked_device.device_type(), ); return false; } @@ -650,15 +518,9 @@ impl acpi::AmlBuilder for VirtioMmioDevice { impl StateTransfer for VirtioMmioDevice { fn get_state_vec(&self) -> migration::Result> { - let mut state = self.state.lock().unwrap(); - - let locked_dev = self.device.lock().unwrap(); - for (index, queue) in locked_dev.virtio_base().queues.iter().enumerate() { - state.config_space.queues_config[index] = - queue.lock().unwrap().vring.get_queue_config(); - } - state.config_space.interrupt_status = self.interrupt_status.load(Ordering::Relaxed); - + let state = VirtioMmioState { + virtio_base: self.device.lock().unwrap().virtio_base().get_state(), + }; Ok(state.as_bytes().to_vec()) } @@ -667,32 +529,16 @@ impl StateTransfer for VirtioMmioDevice { if state.len() != s_len { bail!("Invalid state length {}, expected {}", state.len(), s_len); } - let mut locked_state = self.state.lock().unwrap(); - locked_state.as_mut_bytes().copy_from_slice(state); - let mut queue_states = locked_state.config_space.queues_config - [0..locked_state.config_space.queue_num] - .to_vec(); - let mut dev_lock = self.device.lock().unwrap(); - let features = - (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = &dev_lock.virtio_base().broken; - dev_lock.virtio_base_mut().queues = queue_states - .iter_mut() - .map(|queue_state| { - queue_state.set_addr_cache( - self.mem_space.clone(), - self.interrupt_cb.clone().unwrap(), - features, - broken, - ); - Arc::new(Mutex::new( - Queue::new(*queue_state, locked_state.config_space.queue_type).unwrap(), - )) - }) - .collect(); - self.interrupt_status - .store(locked_state.config_space.interrupt_status, Ordering::SeqCst); + let mut mmio_state = VirtioMmioState::default(); + mmio_state.as_mut_bytes().copy_from_slice(state); + + let mut locked_dev = self.device.lock().unwrap(); + locked_dev.virtio_base_mut().set_state( + &mmio_state.virtio_base, + self.mem_space.clone(), + self.interrupt_cb.clone().unwrap(), + ); Ok(()) } @@ -703,24 +549,22 @@ impl StateTransfer for VirtioMmioDevice { impl MigrationHook for VirtioMmioDevice { fn resume(&mut self) -> migration::Result<()> { - if self.state.lock().unwrap().activated { - let mut queue_evts = Vec::>::new(); - for fd in self.host_notify_info.events.iter() { - queue_evts.push(fd.clone()); - } + let mut locked_dev = self.device.lock().unwrap(); + if !locked_dev.device_activated() { + return Ok(()); + } - if let Some(cb) = self.interrupt_cb.clone() { - if let Err(e) = - self.device - .lock() - .unwrap() - .activate(self.mem_space.clone(), cb, queue_evts) - { - bail!("Failed to resume virtio mmio device: {}", e); - } - } else { - bail!("Failed to resume device: No interrupt callback"); + let mut queue_evts = Vec::>::new(); + for fd in self.host_notify_info.events.iter() { + queue_evts.push(fd.clone()); + } + + if let Some(cb) = self.interrupt_cb.clone() { + if let Err(e) = locked_dev.activate(self.mem_space.clone(), cb, queue_evts) { + bail!("Failed to resume virtio mmio device: {}", e); } + } else { + bail!("Failed to resume device: No interrupt callback"); } Ok(()) @@ -732,7 +576,10 @@ mod tests { use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use super::*; - use crate::{check_config_space_rw, read_config_default, VirtioBase, VIRTIO_TYPE_BLOCK}; + use crate::{ + check_config_space_rw, read_config_default, VirtioBase, QUEUE_TYPE_SPLIT_VRING, + VIRTIO_TYPE_BLOCK, + }; fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); @@ -832,34 +679,28 @@ mod tests { #[test] fn test_virtio_mmio_device_new() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let virtio_device_clone = virtio_device.clone(); let sys_space = address_space_init(); + let virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); - let virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); - assert_eq!(virtio_mmio_device.state.lock().unwrap().activated, false); + let locked_device = virtio_device.lock().unwrap(); + assert_eq!(locked_device.device_activated(), false); assert_eq!( virtio_mmio_device.host_notify_info.events.len(), - virtio_device_clone.lock().unwrap().queue_num() - ); - let config_space = virtio_mmio_device.state.lock().unwrap().config_space; - assert_eq!(config_space.features_select, 0); - assert_eq!(config_space.acked_features_select, 0); - assert_eq!(config_space.device_status, 0); - assert_eq!(config_space.config_generation, 0); - assert_eq!(config_space.queue_select, 0); - assert_eq!( - config_space.queue_num, - virtio_device_clone.lock().unwrap().queue_num() + locked_device.queue_num() ); - assert_eq!(config_space.queue_type, QUEUE_TYPE_SPLIT_VRING); + assert_eq!(locked_device.hfeatures_sel(), 0); + assert_eq!(locked_device.gfeatures_sel(), 0); + assert_eq!(locked_device.device_status(), 0); + assert_eq!(locked_device.config_generation(), 0); + assert_eq!(locked_device.queue_select(), 0); + assert_eq!(locked_device.queue_type(), QUEUE_TYPE_SPLIT_VRING); } #[test] fn test_virtio_mmio_device_read_01() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let virtio_device_clone = virtio_device.clone(); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); // read the register of magic value @@ -897,13 +738,8 @@ mod tests { // read the register of the features // get low 32bit of the features let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .features_select = 0; - virtio_device_clone.lock().unwrap().base.device_features = 0x0000_00f8_0000_00fe; + virtio_device.lock().unwrap().set_hfeatures_sel(0); + virtio_device.lock().unwrap().base.device_features = 0x0000_00f8_0000_00fe; assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), true @@ -911,12 +747,7 @@ mod tests { assert_eq!(LittleEndian::read_u32(&buf[..]), 0x0000_00fe); // get high 32bit of the features for device which supports VirtIO Version 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .features_select = 1; + virtio_device.lock().unwrap().set_hfeatures_sel(1); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), true @@ -928,18 +759,13 @@ mod tests { fn test_virtio_mmio_device_read_02() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); // read the register representing max size of the queue // for queue_select as 0 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .queue_select = 0; + virtio_device.lock().unwrap().set_queue_select(0); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), true @@ -947,12 +773,7 @@ mod tests { assert_eq!(LittleEndian::read_u32(&buf[..]), QUEUE_SIZE as u32); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .queue_select = 1; + virtio_device.lock().unwrap().set_queue_select(1); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), true @@ -962,10 +783,11 @@ mod tests { // read the register representing the status of queue // for queue_select as 0 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 1); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), @@ -979,10 +801,11 @@ mod tests { assert_eq!(LittleEndian::read_u32(&data[..]), 1); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 1; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(1); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, QUEUE_READY_REG), true @@ -997,9 +820,10 @@ mod tests { ); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .interrupt_status - .store(0b10_1111, Ordering::Relaxed); + virtio_device + .lock() + .unwrap() + .set_interrupt_status(0b10_1111); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, INTERRUPT_STATUS_REG), true @@ -1008,24 +832,14 @@ mod tests { // read the register representing the status of device let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .device_status = 0; + virtio_device.lock().unwrap().set_device_status(0); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG), true ); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .device_status = 5; + virtio_device.lock().unwrap().set_device_status(5); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG), true @@ -1036,9 +850,8 @@ mod tests { #[test] fn test_virtio_mmio_device_read_03() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let virtio_device_clone = virtio_device.clone(); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); // read the configuration atomic value @@ -1049,12 +862,7 @@ mod tests { ); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .config_generation = 10; + virtio_device.lock().unwrap().set_config_generation(10); assert_eq!( virtio_mmio_device.read(&mut buf[..], addr, CONFIG_GENERATION_REG), true @@ -1070,12 +878,12 @@ mod tests { // read the configuration space of virtio device // write something let result: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - virtio_device_clone + virtio_device .lock() .unwrap() .config_space .as_mut_slice() - .copy_from_slice(&result[..]); + .copy_from_slice(&result); let mut data: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; assert_eq!(virtio_mmio_device.read(&mut data[..], addr, 0x100), true); @@ -1090,9 +898,8 @@ mod tests { #[test] fn test_virtio_mmio_device_write_01() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let virtio_device_clone = virtio_device.clone(); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); // write the selector for device features @@ -1102,90 +909,65 @@ mod tests { virtio_mmio_device.write(&buf[..], addr, DEVICE_FEATURES_SEL_REG), true ); - assert_eq!( - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .features_select, - 2 - ); + assert_eq!(virtio_device.lock().unwrap().hfeatures_sel(), 2); // write the device features // false when the device status is CONFIG_STATUS_FEATURES_OK or CONFIG_STATUS_FAILED isn't CONFIG_STATUS_DRIVER - virtio_mmio_device - .state + virtio_device .lock() .unwrap() - .config_space - .device_status = CONFIG_STATUS_FEATURES_OK; + .set_device_status(CONFIG_STATUS_FEATURES_OK); assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), false ); - virtio_mmio_device - .state + virtio_device .lock() .unwrap() - .config_space - .device_status = CONFIG_STATUS_FAILED; + .set_device_status(CONFIG_STATUS_FAILED); assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), false ); - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .device_status = - CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_FAILED | CONFIG_STATUS_DRIVER; + virtio_device.lock().unwrap().set_device_status( + CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_FAILED | CONFIG_STATUS_DRIVER, + ); assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), false ); // it is ok to write the low 32bit of device features - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.device_status = CONFIG_STATUS_DRIVER; + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_DRIVER); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - locked_state.config_space.acked_features_select = 0; - drop(locked_state); + virtio_device.lock().unwrap().set_gfeatures_sel(0); LittleEndian::write_u32(&mut buf[..], 0x0000_00fe); - virtio_device_clone.lock().unwrap().base.device_features = 0x0000_00fe; + virtio_device.lock().unwrap().base.device_features = 0x0000_00fe; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), true ); assert_eq!( - virtio_device_clone.lock().unwrap().base.driver_features as u32, + virtio_device.lock().unwrap().base.driver_features as u32, 0x0000_00fe ); // it is ok to write the high 32bit of device features let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .acked_features_select = 1; + virtio_device.lock().unwrap().set_gfeatures_sel(1); LittleEndian::write_u32(&mut buf[..], 0x0000_00ff); - virtio_device_clone.lock().unwrap().base.device_features = 0x0000_00ff_0000_0000; + virtio_device.lock().unwrap().base.device_features = 0x0000_00ff_0000_0000; assert_eq!( virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), true ); assert_eq!( - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .queue_type, + virtio_device.lock().unwrap().queue_type(), QUEUE_TYPE_PACKED_VRING ); assert_eq!( - virtio_device_clone.lock().unwrap().base.driver_features >> 32 as u32, + virtio_device.lock().unwrap().base.driver_features >> 32 as u32, 0x0000_00ff ); @@ -1196,15 +978,7 @@ mod tests { virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_SEL_REG), true ); - assert_eq!( - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .acked_features_select, - 0x00ff_0000 - ); + assert_eq!(virtio_device.lock().unwrap().gfeatures_sel(), 0x00ff_0000); // write the selector of queue let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; @@ -1213,48 +987,41 @@ mod tests { virtio_mmio_device.write(&buf[..], addr, QUEUE_SEL_REG), true ); - assert_eq!( - virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .queue_select, - 0x0000_ff00 - ); + assert_eq!(virtio_device.lock().unwrap().queue_select(), 0x0000_ff00); // write the size of queue let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 128); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_NUM_REG), true ); - let locked_state = virtio_mmio_device.state.lock().unwrap(); - if let Ok(config) = locked_state.config_space.get_queue_config() { + if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!(config.size, 128); } else { assert!(false); - } + }; } #[test] fn test_virtio_mmio_device_write_02() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); // write the ready status of queue let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 1); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), @@ -1268,10 +1035,11 @@ mod tests { assert_eq!(LittleEndian::read_u32(&data[..]), 1); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 2); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), @@ -1286,15 +1054,14 @@ mod tests { // write the interrupt status let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - virtio_mmio_device - .state + virtio_device .lock() .unwrap() - .config_space - .device_status = CONFIG_STATUS_DRIVER_OK; - virtio_mmio_device - .interrupt_status - .store(0b10_1111, Ordering::Relaxed); + .set_device_status(CONFIG_STATUS_DRIVER_OK); + virtio_device + .lock() + .unwrap() + .set_interrupt_status(0b10_1111); LittleEndian::write_u32(&mut buf[..], 0b111); assert_eq!( virtio_mmio_device.write(&buf[..], addr, INTERRUPT_ACK_REG), @@ -1312,141 +1079,116 @@ mod tests { fn test_virtio_mmio_device_write_03() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); // write the low 32bit of queue's descriptor table address - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xffff_fefe); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_LOW_REG), true ); - if let Ok(config) = virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .get_queue_config() - { + if let Ok(config) = virtio_mmio_device.device.lock().unwrap().queue_config() { assert_eq!(config.desc_table.0 as u32, 0xffff_fefe) } else { assert!(false); } // write the high 32bit of queue's descriptor table address - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xfcfc_ffff); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_HIGH_REG), true ); - if let Ok(config) = virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .get_queue_config() - { + if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!((config.desc_table.0 >> 32) as u32, 0xfcfc_ffff) } else { assert!(false); } // write the low 32bit of queue's available ring address - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xfcfc_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_LOW_REG), true ); - if let Ok(config) = virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .get_queue_config() - { + if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!(config.avail_ring.0 as u32, 0xfcfc_fafa) } else { assert!(false); } // write the high 32bit of queue's available ring address - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xecec_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_HIGH_REG), true ); - if let Ok(config) = virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .get_queue_config() - { + if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!((config.avail_ring.0 >> 32) as u32, 0xecec_fafa) } else { assert!(false); } // write the low 32bit of queue's used ring address - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xacac_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_LOW_REG), true ); - if let Ok(config) = virtio_mmio_device - .state - .lock() - .unwrap() - .config_space - .get_queue_config() - { + if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!(config.used_ring.0 as u32, 0xacac_fafa) } else { assert!(false); } // write the high 32bit of queue's used ring address - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - drop(locked_state); + virtio_device.lock().unwrap().set_queue_select(0); + virtio_device + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xcccc_fafa); assert_eq!( virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_HIGH_REG), true ); - let locked_state = virtio_mmio_device.state.lock().unwrap(); - if let Ok(config) = locked_state.config_space.get_queue_config() { + if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!((config.used_ring.0 >> 32) as u32, 0xcccc_fafa) } else { assert!(false); - } + }; } fn align(size: u64, alignment: u64) -> u64 { @@ -1461,16 +1203,15 @@ mod tests { #[test] fn test_virtio_mmio_device_write_04() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let virtio_device_clone = virtio_device.clone(); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device); + let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); let addr = GuestAddress(0); virtio_mmio_device.assign_interrupt_cb(); - let mut locked_state = virtio_mmio_device.state.lock().unwrap(); - locked_state.config_space.queue_select = 0; - locked_state.config_space.device_status = CONFIG_STATUS_FEATURES_OK; - if let Ok(config) = locked_state.config_space.get_mut_queue_config() { + let mut locked_device = virtio_device.lock().unwrap(); + locked_device.set_queue_select(0); + locked_device.set_device_status(CONFIG_STATUS_FEATURES_OK); + if let Ok(config) = locked_device.queue_config_mut(true) { config.desc_table = GuestAddress(0); config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * 16); config.used_ring = GuestAddress(align( @@ -1480,8 +1221,8 @@ mod tests { config.size = QUEUE_SIZE; config.ready = true; } - locked_state.config_space.queue_select = 1; - if let Ok(config) = locked_state.config_space.get_mut_queue_config() { + locked_device.set_queue_select(1); + if let Ok(config) = locked_device.queue_config_mut(true) { config.desc_table = GuestAddress(0); config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * 16); config.used_ring = GuestAddress(align( @@ -1491,13 +1232,13 @@ mod tests { config.size = QUEUE_SIZE / 2; config.ready = true; } - drop(locked_state); + drop(locked_device); // write the device status let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], CONFIG_STATUS_ACKNOWLEDGE); assert_eq!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG), true); - assert_eq!(virtio_mmio_device.state.lock().unwrap().activated, false); + assert_eq!(virtio_device.lock().unwrap().device_activated(), false); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; assert_eq!( virtio_mmio_device.read(&mut data[..], addr, STATUS_REG), @@ -1513,10 +1254,10 @@ mod tests { | CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FEATURES_OK, ); - assert_eq!(virtio_device_clone.lock().unwrap().b_active, false); + assert_eq!(virtio_device.lock().unwrap().b_active, false); assert_eq!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG), true); - assert_eq!(virtio_mmio_device.state.lock().unwrap().activated, true); - assert_eq!(virtio_device_clone.lock().unwrap().b_active, true); + assert_eq!(virtio_device.lock().unwrap().device_activated(), true); + assert_eq!(virtio_device.lock().unwrap().b_active, true); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; assert_eq!( virtio_mmio_device.read(&mut data[..], addr, STATUS_REG), diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 466e5883b..559c55f50 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -12,7 +12,7 @@ use std::cmp::{max, min}; use std::mem::size_of; -use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; @@ -27,7 +27,7 @@ use pci::config::{ STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::msix::{update_dev_id, MsixState, MSIX_TABLE_ENTRY_SIZE}; +use pci::msix::{update_dev_id, MsixState}; use pci::Result as PciResult; use pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, @@ -39,7 +39,7 @@ use util::offset_of; use vmm_sys_util::eventfd::EventFd; use crate::{ - virtio_has_feature, NotifyEventFds, Queue, QueueConfig, VirtioDevice, VirtioInterrupt, + virtio_has_feature, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, VirtioInterrupt, VirtioInterruptType, }; @@ -150,305 +150,6 @@ fn get_virtio_class_id(device_type: u32) -> u16 { } } -/// The configuration of virtio-pci device, the fields refer to Virtio Spec. -#[derive(Default)] -struct VirtioPciCommonConfig { - /// Bitmask of the features supported by the device (host)(32 bits per set) - features_select: u32, - /// Device (host) feature-setting selector. - acked_features_select: u32, - /// Interrupt status. - interrupt_status: Arc, - /// Device status. - device_status: Arc, - /// Configuration atomicity value. - config_generation: Arc, - /// Queue selector. - queue_select: u16, - /// The MSI-X vector for config change notification. - msix_config: Arc, - /// The configuration of queues. - queues_config: Vec, - /// The type of queue, split-vring or packed-vring. - queue_type: u16, -} - -impl VirtioPciCommonConfig { - fn new(queue_size: u16, queue_num: usize) -> Self { - VirtioPciCommonConfig { - msix_config: Arc::new(AtomicU16::new(INVALID_VECTOR_NUM)), - queues_config: vec![QueueConfig::new(queue_size); queue_num], - queue_type: QUEUE_TYPE_SPLIT_VRING, - ..Default::default() - } - } - - fn reset(&mut self) { - self.features_select = 0; - self.acked_features_select = 0; - self.interrupt_status.store(0, Ordering::SeqCst); - self.device_status.store(0, Ordering::SeqCst); - self.config_generation.store(0, Ordering::SeqCst); - self.queue_select = 0; - self.msix_config.store(INVALID_VECTOR_NUM, Ordering::SeqCst); - self.queue_type = QUEUE_TYPE_SPLIT_VRING; - self.queues_config.iter_mut().for_each(|q| q.reset()); - } - - fn check_device_status(&self, set: u32, clr: u32) -> bool { - self.device_status.load(Ordering::Acquire) & (set | clr) == set - } - - fn get_mut_queue_config(&mut self, need_check: bool) -> PciResult<&mut QueueConfig> { - if !need_check { - return self - .queues_config - .get_mut(self.queue_select as usize) - .with_context(|| "pci-reg queue_select overflows"); - } - if self.check_device_status( - CONFIG_STATUS_FEATURES_OK, - CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FAILED, - ) { - self.queues_config - .get_mut(self.queue_select as usize) - .with_context(|| "pci-reg queue_select overflows") - } else { - Err(anyhow!(PciError::DeviceStatus( - self.device_status.load(Ordering::Acquire) - ))) - } - } - - fn get_queue_config(&self) -> PciResult<&QueueConfig> { - self.queues_config - .get(self.queue_select as usize) - .with_context(|| "pci-reg queue_select overflows") - } - - fn revise_queue_vector(&self, vector_nr: u32, virtio_pci_dev: &VirtioPciDevice) -> u32 { - let msix = &virtio_pci_dev.config.msix; - if msix.is_none() { - return INVALID_VECTOR_NUM as u32; - } - let max_vector = - msix.as_ref().unwrap().lock().unwrap().table.len() / MSIX_TABLE_ENTRY_SIZE as usize; - if vector_nr >= max_vector as u32 { - INVALID_VECTOR_NUM as u32 - } else { - vector_nr - } - } - - /// Read data from the common config of virtio device. - /// Return the config value in u32. - /// - /// # Arguments - /// - /// * `device` - Virtio device entity. - /// * `offset` - The offset of common config. - fn read_common_config( - &self, - device: &Arc>, - offset: u64, - ) -> PciResult { - let value = match offset { - COMMON_DFSELECT_REG => self.features_select, - COMMON_DF_REG => { - if self.features_select < MAX_FEATURES_SELECT_NUM { - device.lock().unwrap().device_features(self.features_select) - } else { - 0 - } - } - COMMON_GFSELECT_REG => self.acked_features_select, - COMMON_GF_REG => { - if self.acked_features_select < MAX_FEATURES_SELECT_NUM { - device - .lock() - .unwrap() - .driver_features(self.acked_features_select) - } else { - 0 - } - } - COMMON_MSIX_REG => self.msix_config.load(Ordering::Acquire) as u32, - COMMON_NUMQ_REG => self.queues_config.len() as u32, - COMMON_STATUS_REG => self.device_status.load(Ordering::Acquire), - COMMON_CFGGENERATION_REG => self.config_generation.load(Ordering::Acquire) as u32, - COMMON_Q_SELECT_REG => self.queue_select as u32, - COMMON_Q_SIZE_REG => self - .get_queue_config() - .map(|config| u32::from(config.size))?, - COMMON_Q_MSIX_REG => self - .get_queue_config() - .map(|config| u32::from(config.vector))?, - COMMON_Q_ENABLE_REG => self - .get_queue_config() - .map(|config| u32::from(config.ready))?, - COMMON_Q_NOFF_REG => self.queue_select as u32, - COMMON_Q_DESCLO_REG => self - .get_queue_config() - .map(|config| config.desc_table.0 as u32)?, - COMMON_Q_DESCHI_REG => self - .get_queue_config() - .map(|config| (config.desc_table.0 >> 32) as u32)?, - COMMON_Q_AVAILLO_REG => self - .get_queue_config() - .map(|config| config.avail_ring.0 as u32)?, - COMMON_Q_AVAILHI_REG => self - .get_queue_config() - .map(|config| (config.avail_ring.0 >> 32) as u32)?, - COMMON_Q_USEDLO_REG => self - .get_queue_config() - .map(|config| config.used_ring.0 as u32)?, - COMMON_Q_USEDHI_REG => self - .get_queue_config() - .map(|config| (config.used_ring.0 >> 32) as u32)?, - _ => 0, - }; - - Ok(value) - } - - /// Write data to the common config of virtio device. - /// - /// # Arguments - /// - /// * `device` - Virtio device entity. - /// * `offset` - The offset of common config. - /// * `value` - The value to write. - /// - /// # Errors - /// - /// Returns Error if the offset is out of bound. - fn write_common_config( - &mut self, - virtio_pci_dev: &VirtioPciDevice, - offset: u64, - value: u32, - ) -> PciResult<()> { - let device = virtio_pci_dev.device.clone(); - match offset { - COMMON_DFSELECT_REG => { - self.features_select = value; - } - COMMON_GFSELECT_REG => { - self.acked_features_select = value; - } - COMMON_GF_REG => { - if self.device_status.load(Ordering::Acquire) & CONFIG_STATUS_FEATURES_OK != 0 { - error!("it's not allowed to set features after having been negoiated"); - return Ok(()); - } - if self.acked_features_select >= MAX_FEATURES_SELECT_NUM { - return Err(anyhow!(PciError::FeaturesSelect( - self.acked_features_select - ))); - } - device - .lock() - .unwrap() - .set_driver_features(self.acked_features_select, value); - - if self.acked_features_select == 1 { - let features = (device.lock().unwrap().driver_features(1) as u64) << 32; - if virtio_has_feature(features, VIRTIO_F_RING_PACKED) { - self.queue_type = QUEUE_TYPE_PACKED_VRING; - } else { - self.queue_type = QUEUE_TYPE_SPLIT_VRING; - } - } - } - COMMON_MSIX_REG => { - let val = self.revise_queue_vector(value, virtio_pci_dev); - self.msix_config.store(val as u16, Ordering::SeqCst); - self.interrupt_status.store(0, Ordering::SeqCst); - } - COMMON_STATUS_REG => { - if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { - let features = (device.lock().unwrap().driver_features(1) as u64) << 32; - if !virtio_has_feature(features, VIRTIO_F_VERSION_1) { - error!( - "Device is modern only, but the driver not support VIRTIO_F_VERSION_1" - ); - return Ok(()); - } - } - if value != 0 && (self.device_status.load(Ordering::Acquire) & !value) != 0 { - error!("Driver must not clear a device status bit"); - return Ok(()); - } - - let old_status = self.device_status.load(Ordering::Acquire); - self.device_status.store(value, Ordering::SeqCst); - if self.check_device_status( - CONFIG_STATUS_ACKNOWLEDGE - | CONFIG_STATUS_DRIVER - | CONFIG_STATUS_DRIVER_OK - | CONFIG_STATUS_FEATURES_OK, - CONFIG_STATUS_FAILED, - ) { - virtio_pci_dev.activate_device(self); - } else if old_status != 0 && self.device_status.load(Ordering::Acquire) == 0 { - self.reset(); - virtio_pci_dev.deactivate_device(); - } - } - COMMON_Q_SELECT_REG => { - if value < VIRTIO_QUEUE_MAX { - self.queue_select = value as u16; - } - } - COMMON_Q_SIZE_REG => self - .get_mut_queue_config(true) - .map(|config| config.size = value as u16)?, - COMMON_Q_ENABLE_REG => { - if value != 1 { - error!("Driver set illegal value for queue_enable {}", value); - return Err(anyhow!(PciError::QueueEnable(value))); - } - self.get_mut_queue_config(true) - .map(|config| config.ready = true)?; - } - COMMON_Q_MSIX_REG => { - let val = self.revise_queue_vector(value, virtio_pci_dev); - // It should not check device status when detaching device which - // will set vector to INVALID_VECTOR_NUM. - let mut need_check = true; - if self.device_status.load(Ordering::Acquire) == 0 { - need_check = false; - } - self.get_mut_queue_config(need_check) - .map(|config| config.vector = val as u16)?; - } - COMMON_Q_DESCLO_REG => self.get_mut_queue_config(true).map(|config| { - config.desc_table = GuestAddress(config.desc_table.0 | u64::from(value)); - })?, - COMMON_Q_DESCHI_REG => self.get_mut_queue_config(true).map(|config| { - config.desc_table = GuestAddress(config.desc_table.0 | (u64::from(value) << 32)); - })?, - COMMON_Q_AVAILLO_REG => self.get_mut_queue_config(true).map(|config| { - config.avail_ring = GuestAddress(config.avail_ring.0 | u64::from(value)); - })?, - COMMON_Q_AVAILHI_REG => self.get_mut_queue_config(true).map(|config| { - config.avail_ring = GuestAddress(config.avail_ring.0 | (u64::from(value) << 32)); - })?, - COMMON_Q_USEDLO_REG => self.get_mut_queue_config(true).map(|config| { - config.used_ring = GuestAddress(config.used_ring.0 | u64::from(value)); - })?, - COMMON_Q_USEDHI_REG => self.get_mut_queue_config(true).map(|config| { - config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); - })?, - _ => { - return Err(anyhow!(PciError::PciRegister(offset))); - } - }; - - Ok(()) - } -} - #[allow(clippy::upper_case_acronyms)] #[repr(u8)] enum VirtioPciCapType { @@ -546,7 +247,6 @@ impl VirtioPciNotifyCap { #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] pub struct VirtioPciState { - activated: bool, dev_id: u16, /// Max length of config_space is 4096. config_space: [u8; 4096], @@ -555,17 +255,7 @@ pub struct VirtioPciState { last_cap_end: u16, last_ext_cap_offset: u16, last_ext_cap_end: u16, - features_select: u32, - acked_features_select: u32, - interrupt_status: u32, - device_status: u32, - config_generation: u8, - queue_select: u16, - msix_config: u16, - /// The configuration of queues. Max number of queues is 32(equals to MAX_VIRTIO_QUEUE). - queues_config: [QueueConfig; 32], - /// The number of queues. - queue_num: usize, + virtio_base: VirtioBaseState, } /// Virtio-PCI device structure @@ -579,16 +269,12 @@ pub struct VirtioPciDevice { dev_id: Arc, /// Devfn devfn: u8, - /// If this device is activated or not. - device_activated: Arc, /// Memory AddressSpace sys_mem: Arc, /// Pci config space. config: PciConfig, /// Offset of VirtioPciCfgAccessCap in Pci config space. cfg_cap_offset: usize, - /// Virtio common config refer to Virtio Spec. - common_config: Arc>, /// Primary Bus parent_bus: Weak>, /// Eventfds used for guest notify the Device. @@ -611,20 +297,14 @@ impl VirtioPciDevice { multi_func: bool, ) -> Self { let queue_num = device.lock().unwrap().queue_num(); - let queue_size = device.lock().unwrap().queue_size_max(); - VirtioPciDevice { name, device, dev_id: Arc::new(AtomicU16::new(0)), devfn, - device_activated: Arc::new(AtomicBool::new(false)), sys_mem, config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), cfg_cap_offset: 0, - common_config: Arc::new(Mutex::new(VirtioPciCommonConfig::new( - queue_size, queue_num, - ))), parent_bus, notify_eventfds: Arc::new(NotifyEventFds::new(queue_num)), interrupt_cb: None, @@ -638,14 +318,17 @@ impl VirtioPciDevice { } fn assign_interrupt_cb(&mut self) { - let common_cfg = self.common_config.lock().unwrap(); - let device_status = common_cfg.device_status.clone(); - let interrupt_status = common_cfg.interrupt_status.clone(); - let msix_config = common_cfg.msix_config.clone(); - let config_generation = common_cfg.config_generation.clone(); + let locked_dev = self.device.lock().unwrap(); + let virtio_base = locked_dev.virtio_base(); + let device_status = virtio_base.device_status.clone(); + let interrupt_status = virtio_base.interrupt_status.clone(); + let msix_config = virtio_base.config_vector.clone(); + let config_generation = virtio_base.config_generation.clone(); + let cloned_msix = self.config.msix.as_ref().unwrap().clone(); let cloned_intx = self.config.intx.as_ref().unwrap().clone(); let dev_id = self.dev_id.clone(); + let cb = Arc::new(Box::new( move |int_type: &VirtioInterruptType, queue: Option<&Queue>, needs_reset: bool| { let vector = match int_type { @@ -715,18 +398,18 @@ impl VirtioPciDevice { Ok(write_start) } - fn activate_device(&self, common_cfg_lock: &mut VirtioPciCommonConfig) -> bool { - if self.device_activated.load(Ordering::Acquire) { + fn activate_device(&self) -> bool { + let mut locked_dev = self.device.lock().unwrap(); + if locked_dev.device_activated() { return true; } - let queue_type = common_cfg_lock.queue_type; - let queues_config = &mut common_cfg_lock.queues_config; + let queue_type = locked_dev.queue_type(); + let features = locked_dev.virtio_base().driver_features; + let broken = locked_dev.virtio_base().broken.clone(); + let mut queues = Vec::new(); - let mut dev_lock = self.device.lock().unwrap(); - let features = - (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = &dev_lock.virtio_base().broken; + let queues_config = &mut locked_dev.virtio_base_mut().queues_config; for q_config in queues_config.iter_mut() { if !q_config.ready { debug!("queue is not ready, please check your init process"); @@ -735,7 +418,7 @@ impl VirtioPciDevice { self.sys_mem.clone(), self.interrupt_cb.clone().unwrap(), features, - broken, + &broken, ); } let queue = Queue::new(*q_config, queue_type).unwrap(); @@ -746,35 +429,31 @@ impl VirtioPciDevice { let arc_queue = Arc::new(Mutex::new(queue)); queues.push(arc_queue.clone()); } - dev_lock.virtio_base_mut().queues = queues; - drop(dev_lock); + locked_dev.virtio_base_mut().queues = queues; update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); if self.need_irqfd { - let mut queue_num = self.device.lock().unwrap().queue_num(); + let mut queue_num = locked_dev.queue_num(); // No need to create call event for control queue. // It will be polled in StratoVirt when activating the device. - if self.device.lock().unwrap().has_control_queue() && queue_num % 2 != 0 { + if locked_dev.has_control_queue() && queue_num % 2 != 0 { queue_num -= 1; } let call_evts = NotifyEventFds::new(queue_num); - if let Err(e) = self - .device - .lock() - .unwrap() - .set_guest_notifiers(&call_evts.events) - { + if let Err(e) = locked_dev.set_guest_notifiers(&call_evts.events) { error!("Failed to set guest notifiers, error is {:?}", e); return false; } + drop(locked_dev); if !self.queues_register_irqfd(&call_evts.events) { error!("Failed to register queues irqfd."); return false; } + locked_dev = self.device.lock().unwrap(); } let queue_evts = (*self.notify_eventfds).clone().events; - if let Err(e) = self.device.lock().unwrap().activate( + if let Err(e) = locked_dev.activate( self.sys_mem.clone(), self.interrupt_cb.clone().unwrap(), queue_evts, @@ -783,7 +462,7 @@ impl VirtioPciDevice { return false; } - self.device_activated.store(true, Ordering::Release); + locked_dev.set_device_activated(true); true } @@ -795,26 +474,221 @@ impl VirtioPciDevice { } } - self.device.lock().unwrap().virtio_base_mut().queues.clear(); - if self.device_activated.load(Ordering::Acquire) { - self.device_activated.store(false, Ordering::Release); - if let Err(e) = self.device.lock().unwrap().deactivate() { + let mut locked_dev = self.device.lock().unwrap(); + if locked_dev.device_activated() { + if let Err(e) = locked_dev.deactivate() { error!("Failed to deactivate virtio device, error is {:?}", e); return false; } + locked_dev.virtio_base_mut().reset(); } true } - fn build_common_cfg_ops(&mut self) -> RegionOps { - let cloned_virtio_dev = self.device.clone(); - let cloned_common_cfg = self.common_config.clone(); + /// Read data from the common config of virtio device. + /// Return the config value in u32. + /// + /// # Arguments + /// + /// * `offset` - The offset of common config. + fn read_common_config(&self, offset: u64) -> PciResult { + let locked_device = self.device.lock().unwrap(); + let value = match offset { + COMMON_DFSELECT_REG => locked_device.hfeatures_sel(), + COMMON_DF_REG => { + let dfeatures_sel = locked_device.hfeatures_sel(); + if dfeatures_sel < MAX_FEATURES_SELECT_NUM { + locked_device.device_features(dfeatures_sel) + } else { + 0 + } + } + COMMON_GFSELECT_REG => locked_device.gfeatures_sel(), + COMMON_GF_REG => { + let gfeatures_sel = locked_device.gfeatures_sel(); + if gfeatures_sel < MAX_FEATURES_SELECT_NUM { + locked_device.driver_features(gfeatures_sel) + } else { + 0 + } + } + COMMON_MSIX_REG => locked_device.config_vector() as u32, + COMMON_NUMQ_REG => locked_device.virtio_base().queues_config.len() as u32, + COMMON_STATUS_REG => locked_device.device_status(), + COMMON_CFGGENERATION_REG => locked_device.config_generation() as u32, + COMMON_Q_SELECT_REG => locked_device.queue_select() as u32, + COMMON_Q_SIZE_REG => locked_device + .queue_config() + .map(|config| u32::from(config.size))?, + COMMON_Q_MSIX_REG => locked_device + .queue_config() + .map(|config| u32::from(config.vector))?, + COMMON_Q_ENABLE_REG => locked_device + .queue_config() + .map(|config| u32::from(config.ready))?, + COMMON_Q_NOFF_REG => locked_device.queue_select() as u32, + COMMON_Q_DESCLO_REG => locked_device + .queue_config() + .map(|config| config.desc_table.0 as u32)?, + COMMON_Q_DESCHI_REG => locked_device + .queue_config() + .map(|config| (config.desc_table.0 >> 32) as u32)?, + COMMON_Q_AVAILLO_REG => locked_device + .queue_config() + .map(|config| config.avail_ring.0 as u32)?, + COMMON_Q_AVAILHI_REG => locked_device + .queue_config() + .map(|config| (config.avail_ring.0 >> 32) as u32)?, + COMMON_Q_USEDLO_REG => locked_device + .queue_config() + .map(|config| config.used_ring.0 as u32)?, + COMMON_Q_USEDHI_REG => locked_device + .queue_config() + .map(|config| (config.used_ring.0 >> 32) as u32)?, + _ => 0, + }; + + Ok(value) + } + + /// Write data to the common config of virtio device. + /// + /// # Arguments + /// + /// * `offset` - The offset of common config. + /// * `value` - The value to write. + /// + /// # Errors + /// + /// Returns Error if the offset is out of bound. + fn write_common_config(&mut self, offset: u64, value: u32) -> PciResult<()> { + let mut locked_device = self.device.lock().unwrap(); + match offset { + COMMON_DFSELECT_REG => { + locked_device.set_hfeatures_sel(value); + } + COMMON_GFSELECT_REG => { + locked_device.set_gfeatures_sel(value); + } + COMMON_GF_REG => { + if locked_device.device_status() & CONFIG_STATUS_FEATURES_OK != 0 { + error!("it's not allowed to set features after having been negoiated"); + return Ok(()); + } + let gfeatures_sel = locked_device.gfeatures_sel(); + if gfeatures_sel >= MAX_FEATURES_SELECT_NUM { + return Err(anyhow!(PciError::FeaturesSelect(gfeatures_sel))); + } + locked_device.set_driver_features(gfeatures_sel, value); + + if gfeatures_sel == 1 { + let features = (locked_device.driver_features(1) as u64) << 32; + if virtio_has_feature(features, VIRTIO_F_RING_PACKED) { + locked_device.set_queue_type(QUEUE_TYPE_PACKED_VRING); + } else { + locked_device.set_queue_type(QUEUE_TYPE_SPLIT_VRING); + } + } + } + COMMON_MSIX_REG => { + if self.config.revise_msix_vector(value) { + locked_device.set_config_vector(value as u16); + } else { + locked_device.set_config_vector(INVALID_VECTOR_NUM); + } + locked_device.set_interrupt_status(0); + } + COMMON_STATUS_REG => { + if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { + let features = (locked_device.driver_features(1) as u64) << 32; + if !virtio_has_feature(features, VIRTIO_F_VERSION_1) { + error!( + "Device is modern only, but the driver not support VIRTIO_F_VERSION_1" + ); + return Ok(()); + } + } + if value != 0 && (locked_device.device_status() & !value) != 0 { + error!("Driver must not clear a device status bit"); + return Ok(()); + } + + let old_status = locked_device.device_status(); + locked_device.set_device_status(value); + if locked_device.check_device_status( + CONFIG_STATUS_ACKNOWLEDGE + | CONFIG_STATUS_DRIVER + | CONFIG_STATUS_DRIVER_OK + | CONFIG_STATUS_FEATURES_OK, + CONFIG_STATUS_FAILED, + ) { + drop(locked_device); + self.activate_device(); + } else if old_status != 0 && locked_device.device_status() == 0 { + drop(locked_device); + self.deactivate_device(); + } + } + COMMON_Q_SELECT_REG => { + if value < VIRTIO_QUEUE_MAX { + locked_device.set_queue_select(value as u16); + } + } + COMMON_Q_SIZE_REG => locked_device + .queue_config_mut(true) + .map(|config| config.size = value as u16)?, + COMMON_Q_ENABLE_REG => { + if value != 1 { + error!("Driver set illegal value for queue_enable {}", value); + return Err(anyhow!(PciError::QueueEnable(value))); + } + locked_device + .queue_config_mut(true) + .map(|config| config.ready = true)?; + } + COMMON_Q_MSIX_REG => { + let val = if self.config.revise_msix_vector(value) { + value as u16 + } else { + INVALID_VECTOR_NUM + }; + // It should not check device status when detaching device which + // will set vector to INVALID_VECTOR_NUM. + let need_check = locked_device.device_status() != 0; + locked_device + .queue_config_mut(need_check) + .map(|config| config.vector = val)?; + } + COMMON_Q_DESCLO_REG => locked_device.queue_config_mut(true).map(|config| { + config.desc_table = GuestAddress(config.desc_table.0 | u64::from(value)); + })?, + COMMON_Q_DESCHI_REG => locked_device.queue_config_mut(true).map(|config| { + config.desc_table = GuestAddress(config.desc_table.0 | (u64::from(value) << 32)); + })?, + COMMON_Q_AVAILLO_REG => locked_device.queue_config_mut(true).map(|config| { + config.avail_ring = GuestAddress(config.avail_ring.0 | u64::from(value)); + })?, + COMMON_Q_AVAILHI_REG => locked_device.queue_config_mut(true).map(|config| { + config.avail_ring = GuestAddress(config.avail_ring.0 | (u64::from(value) << 32)); + })?, + COMMON_Q_USEDLO_REG => locked_device.queue_config_mut(true).map(|config| { + config.used_ring = GuestAddress(config.used_ring.0 | u64::from(value)); + })?, + COMMON_Q_USEDHI_REG => locked_device.queue_config_mut(true).map(|config| { + config.used_ring = GuestAddress(config.used_ring.0 | (u64::from(value) << 32)); + })?, + _ => { + return Err(anyhow!(PciError::PciRegister(offset))); + } + }; + + Ok(()) + } + + fn build_common_cfg_ops(virtio_pci: Arc>) -> RegionOps { + let cloned_virtio_pci = virtio_pci.clone(); let common_read = move |data: &mut [u8], _addr: GuestAddress, offset: u64| -> bool { - let value = match cloned_common_cfg - .lock() - .unwrap() - .read_common_config(&cloned_virtio_dev, offset) - { + let value = match cloned_virtio_pci.lock().unwrap().read_common_config(offset) { Ok(v) => v, Err(e) => { error!( @@ -828,18 +702,16 @@ impl VirtioPciDevice { write_data_u32(data, value) }; - let cloned_pci_device = self.clone(); let common_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { let mut value = 0; if !read_data_u32(data, &mut value) { return false; } - if let Err(e) = cloned_pci_device - .common_config + if let Err(e) = virtio_pci .lock() .unwrap() - .write_common_config(&cloned_pci_device, offset, value) + .write_common_config(offset, value) { error!( "Failed to write common config of virtio-pci device, error is {:?}", @@ -856,9 +728,12 @@ impl VirtioPciDevice { } } - fn modern_mem_region_init(&mut self, modern_mem_region: &Region) -> PciResult<()> { + fn modern_mem_region_init( + virtio_pci: Arc>, + modern_mem_region: &Region, + ) -> PciResult<()> { // 1. PCI common cap sub-region. - let common_region_ops = self.build_common_cfg_ops(); + let common_region_ops = Self::build_common_cfg_ops(virtio_pci.clone()); let common_region = Region::init_io_region( u64::from(VIRTIO_PCI_CAP_COMMON_LENGTH), common_region_ops, @@ -869,12 +744,15 @@ impl VirtioPciDevice { .with_context(|| "Failed to register pci-common-cap region.")?; // 2. PCI ISR cap sub-region. - let cloned_common_cfg = self.common_config.clone(); - let cloned_intx = self.config.intx.as_ref().unwrap().clone(); + let cloned_device = virtio_pci.lock().unwrap().device.clone(); + let cloned_intx = virtio_pci.lock().unwrap().config.intx.clone().unwrap(); let isr_read = move |data: &mut [u8], _: GuestAddress, _: u64| -> bool { if let Some(val) = data.get_mut(0) { - let common_cfg_lock = cloned_common_cfg.lock().unwrap(); - *val = common_cfg_lock.interrupt_status.swap(0, Ordering::SeqCst) as u8; + let device_lock = cloned_device.lock().unwrap(); + *val = device_lock + .virtio_base() + .interrupt_status + .swap(0, Ordering::SeqCst) as u8; cloned_intx.lock().unwrap().notify(0); } true @@ -894,7 +772,7 @@ impl VirtioPciDevice { .with_context(|| "Failed to register pci-isr-cap region.")?; // 3. PCI dev cap sub-region. - let cloned_virtio_dev = self.device.clone(); + let cloned_virtio_dev = virtio_pci.lock().unwrap().device.clone(); let device_read = move |data: &mut [u8], _addr: GuestAddress, offset: u64| -> bool { if let Err(e) = cloned_virtio_dev.lock().unwrap().read_config(offset, data) { error!("Failed to read virtio-dev config space, error is {:?}", e); @@ -903,7 +781,7 @@ impl VirtioPciDevice { true }; - let cloned_virtio_dev = self.device.clone(); + let cloned_virtio_dev = virtio_pci.lock().unwrap().device.clone(); let device_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { if let Err(e) = cloned_virtio_dev.lock().unwrap().write_config(offset, data) { error!("Failed to write virtio-dev config space, error is {:?}", e); @@ -936,7 +814,8 @@ impl VirtioPciDevice { notify_region_ops, "VirtioNotify", ); - notify_region.set_ioeventfds(&self.ioeventfds()); + notify_region.set_ioeventfds(&virtio_pci.lock().unwrap().ioeventfds()); + modern_mem_region .add_subregion(notify_region, u64::from(VIRTIO_PCI_CAP_NOTIFY_OFFSET)) .with_context(|| "Failed to register pci-notify-cap region.")?; @@ -1171,15 +1050,25 @@ impl PciDevOps for VirtioPciDevice { self.assign_interrupt_cb(); + self.device + .lock() + .unwrap() + .realize() + .with_context(|| "Failed to realize virtio device")?; + + let name = self.name.clone(); + let devfn = self.devfn; + let dev = Arc::new(Mutex::new(self)); + let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) as u64) .next_power_of_two(); mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); let modern_mem_region = Region::init_container_region(mem_region_size, "VirtioPciModernMem"); - self.modern_mem_region_init(&modern_mem_region)?; + Self::modern_mem_region_init(dev.clone(), &modern_mem_region)?; - self.config.register_bar( + dev.lock().unwrap().config.register_bar( VIRTIO_PCI_MEM_BAR_IDX as usize, modern_mem_region, RegionType::Mem64Bit, @@ -1187,15 +1076,7 @@ impl PciDevOps for VirtioPciDevice { mem_region_size, )?; - self.device - .lock() - .unwrap() - .realize() - .with_context(|| "Failed to realize virtio device")?; - - let name = self.name.clone(); - let devfn = self.devfn; - let dev = Arc::new(Mutex::new(self)); + // Register device to pci bus. let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); let mut locked_pci_bus = pci_bus.lock().unwrap(); let pci_device = locked_pci_bus.devices.get(&devfn); @@ -1208,6 +1089,7 @@ impl PciDevOps for VirtioPciDevice { pci_device.unwrap().lock().unwrap().name() ); } + MigrationManager::register_transport_instance(VirtioPciState::descriptor(), dev, &name); Ok(()) @@ -1273,10 +1155,7 @@ impl PciDevOps for VirtioPciDevice { .unwrap() .reset() .with_context(|| "Failed to reset virtio device")?; - self.common_config.lock().unwrap().reset(); - self.config.reset()?; - Ok(()) } @@ -1307,7 +1186,10 @@ impl PciDevOps for VirtioPciDevice { impl StateTransfer for VirtioPciDevice { fn get_state_vec(&self) -> migration::Result> { - let mut state = VirtioPciState::default(); + let mut state = VirtioPciState { + dev_id: self.dev_id.load(Ordering::Acquire), + ..Default::default() + }; // Save virtio pci config state. for idx in 0..self.config.config.len() { @@ -1325,37 +1207,17 @@ impl StateTransfer for VirtioPciDevice { state.last_ext_cap_end = self.config.last_ext_cap_end; // Save virtio pci common config state. - { - let common_config = self.common_config.lock().unwrap(); - state.interrupt_status = common_config.interrupt_status.load(Ordering::Acquire); - state.msix_config = common_config.msix_config.load(Ordering::Acquire); - state.features_select = common_config.features_select; - state.acked_features_select = common_config.acked_features_select; - state.device_status = common_config.device_status.load(Ordering::Acquire); - state.config_generation = common_config.config_generation.load(Ordering::Acquire); - state.queue_select = common_config.queue_select; - } - - // Save virtio pci state. - state.activated = self.device_activated.load(Ordering::Relaxed); - state.dev_id = self.dev_id.load(Ordering::Acquire); - { - let locked_dev = self.device.lock().unwrap(); - let queues = &locked_dev.virtio_base().queues; - for (index, queue) in queues.iter().enumerate() { - state.queues_config[index] = queue.lock().unwrap().vring.get_queue_config(); - state.queue_num += 1; - } - } + state.virtio_base = self.device.lock().unwrap().virtio_base().get_state(); Ok(state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { - let mut pci_state = *VirtioPciState::from_bytes(state) + let pci_state = VirtioPciState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("PCI_DEVICE"))?; // Set virtio pci config state. + self.dev_id.store(pci_state.dev_id, Ordering::Release); let config_length = self.config.config.len(); self.config.config = pci_state.config_space[..config_length].to_vec(); self.config.write_mask = pci_state.write_mask[..config_length].to_vec(); @@ -1365,49 +1227,12 @@ impl StateTransfer for VirtioPciDevice { self.config.last_ext_cap_offset = pci_state.last_ext_cap_offset; // Set virtio pci common config state. - { - let mut common_config = self.common_config.lock().unwrap(); - common_config - .interrupt_status - .store(pci_state.interrupt_status, Ordering::SeqCst); - common_config - .msix_config - .store(pci_state.msix_config, Ordering::SeqCst); - common_config.features_select = pci_state.features_select; - common_config.acked_features_select = pci_state.acked_features_select; - common_config - .device_status - .store(pci_state.device_status, Ordering::SeqCst); - common_config - .config_generation - .store(pci_state.config_generation, Ordering::SeqCst); - common_config.queue_select = pci_state.queue_select; - } - - // Set virtio pci state. - self.device_activated - .store(pci_state.activated, Ordering::Relaxed); - self.dev_id.store(pci_state.dev_id, Ordering::Release); - { - let queue_type = self.common_config.lock().unwrap().queue_type; - let mut locked_dev = self.device.lock().unwrap(); - let queues = &mut locked_dev.virtio_base_mut().queues; - let dev_lock = self.device.lock().unwrap(); - let features = - (dev_lock.driver_features(1) as u64) << 32 | dev_lock.driver_features(0) as u64; - let broken = &dev_lock.virtio_base().broken; - for queue_state in pci_state.queues_config[0..pci_state.queue_num].iter_mut() { - queue_state.set_addr_cache( - self.sys_mem.clone(), - self.interrupt_cb.clone().unwrap(), - features, - broken, - ); - queues.push(Arc::new(Mutex::new( - Queue::new(*queue_state, queue_type).unwrap(), - ))) - } - } + let mut locked_device = self.device.lock().unwrap(); + locked_device.virtio_base_mut().set_state( + &pci_state.virtio_base, + self.sys_mem.clone(), + self.interrupt_cb.clone().unwrap(), + ); Ok(()) } @@ -1419,31 +1244,33 @@ impl StateTransfer for VirtioPciDevice { impl MigrationHook for VirtioPciDevice { fn resume(&mut self) -> migration::Result<()> { - if self.device_activated.load(Ordering::Relaxed) { - // Reregister ioevents for notifies. - let parent_bus = self.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - if let Err(e) = self.config.update_bar_mapping( - #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), - ) { - bail!("Failed to update bar, error is {:?}", e); - } + if !self.device.lock().unwrap().device_activated() { + return Ok(()); + } - let queue_evts = (*self.notify_eventfds).clone().events; - if let Some(cb) = self.interrupt_cb.clone() { - if let Err(e) = - self.device - .lock() - .unwrap() - .activate(self.sys_mem.clone(), cb, queue_evts) - { - error!("Failed to resume device, error is {:?}", e); - } - } else { - error!("Failed to resume device: No interrupt callback"); + // Reregister ioevents for notifies. + let parent_bus = self.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + if let Err(e) = self.config.update_bar_mapping( + #[cfg(target_arch = "x86_64")] + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), + ) { + bail!("Failed to update bar, error is {:?}", e); + } + + let queue_evts = (*self.notify_eventfds).clone().events; + if let Some(cb) = self.interrupt_cb.clone() { + if let Err(e) = + self.device + .lock() + .unwrap() + .activate(self.sys_mem.clone(), cb, queue_evts) + { + error!("Failed to resume device, error is {:?}", e); } + } else { + error!("Failed to resume device: No interrupt callback"); } Ok(()) @@ -1523,23 +1350,26 @@ mod tests { self.is_activated = true; Ok(()) } + + fn deactivate(&mut self) -> VirtioResult<()> { + Ok(()) + } } macro_rules! com_cfg_read_test { - ($cfg: ident, $dev: ident, $reg: ident, $expect: expr) => { - assert_eq!($cfg.read_common_config(&$dev, $reg).unwrap(), $expect) + ($pci_dev: ident, $reg: ident, $expect: expr) => { + assert_eq!($pci_dev.read_common_config($reg).unwrap(), $expect) }; } macro_rules! com_cfg_write_test { - ($cfg: ident, $dev: ident, $reg: ident, $val: expr) => { - assert!($cfg.write_common_config(&$dev, $reg, $val).is_ok()) + ($pci_dev: ident, $reg: ident, $val: expr) => { + assert!($pci_dev.write_common_config($reg, $val).is_ok()) }; } #[test] fn test_common_config_dev_feature() { - let dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let virtio_dev = dev.clone() as Arc>; + let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", @@ -1551,86 +1381,100 @@ mod tests { Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); - let cloned_virtio_dev = virtio_dev.clone(); - let virtio_pci = VirtioPciDevice::new( + let mut virtio_pci = VirtioPciDevice::new( String::from("test device"), 0, sys_mem, - cloned_virtio_dev, + virtio_dev.clone(), Arc::downgrade(&parent_bus), false, ); - let queue_size = dev.lock().unwrap().queue_size_max(); - let queue_num = dev.lock().unwrap().queue_num(); - - let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); - // Read virtio device features - cmn_cfg.features_select = 0_u32; - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_DF_REG, 0xFFFF_FFF0_u32); - cmn_cfg.features_select = 1_u32; - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_DF_REG, 0_u32); + virtio_dev.lock().unwrap().set_hfeatures_sel(0_u32); + com_cfg_read_test!(virtio_pci, COMMON_DF_REG, 0xFFFF_FFF0_u32); + virtio_dev.lock().unwrap().set_hfeatures_sel(1_u32); + com_cfg_read_test!(virtio_pci, COMMON_DF_REG, 0_u32); // Write virtio device features - cmn_cfg.acked_features_select = 1_u32; - com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, 0xFF); + virtio_dev.lock().unwrap().set_gfeatures_sel(1_u32); + com_cfg_write_test!(virtio_pci, COMMON_GF_REG, 0xFF); // The feature is not supported by this virtio device, and is masked - assert_eq!(dev.lock().unwrap().base.driver_features, 0_u64); + assert_eq!(virtio_dev.lock().unwrap().base.driver_features, 0_u64); - cmn_cfg.acked_features_select = 0_u32; - com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, 0xCF); + virtio_dev.lock().unwrap().set_gfeatures_sel(0_u32); + com_cfg_write_test!(virtio_pci, COMMON_GF_REG, 0xCF); // The feature is partially supported by this virtio device, and is partially masked - assert_eq!(dev.lock().unwrap().base.driver_features, 0xC0_u64); + assert_eq!(virtio_dev.lock().unwrap().base.driver_features, 0xC0_u64); // Set the feature of the Queue type - cmn_cfg.acked_features_select = 1_u32; - dev.lock().unwrap().base.driver_features = 0_u64; - dev.lock().unwrap().base.device_features = 0xFFFF_FFFF_0000_0000_u64; + virtio_dev.lock().unwrap().set_gfeatures_sel(1_u32); + virtio_dev.lock().unwrap().base.driver_features = 0_u64; + virtio_dev.lock().unwrap().base.device_features = 0xFFFF_FFFF_0000_0000_u64; let driver_features = 1_u32 << (VIRTIO_F_RING_PACKED - 32); - com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_GF_REG, driver_features); - assert_eq!(cmn_cfg.queue_type, QUEUE_TYPE_PACKED_VRING); + com_cfg_write_test!(virtio_pci, COMMON_GF_REG, driver_features); + assert_eq!( + virtio_dev.lock().unwrap().queue_type(), + QUEUE_TYPE_PACKED_VRING + ); assert_eq!( - dev.lock().unwrap().base.driver_features, + virtio_dev.lock().unwrap().base.driver_features, 1_u64 << VIRTIO_F_RING_PACKED ); } #[test] fn test_common_config_queue() { - let virtio_dev: Arc> = - Arc::new(Mutex::new(VirtioDeviceTest::new())); - let queue_size = virtio_dev.lock().unwrap().queue_size_max(); - let queue_num = virtio_dev.lock().unwrap().queue_num(); - let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); + let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); + let parent_bus = Arc::new(Mutex::new(PciBus::new( + String::from("test bus"), + #[cfg(target_arch = "x86_64")] + Region::init_container_region(1 << 16, "parent_bus"), + sys_mem.root().clone(), + ))); + let virtio_pci = VirtioPciDevice::new( + String::from("test device"), + 0, + sys_mem, + virtio_dev.clone(), + Arc::downgrade(&parent_bus), + false, + ); // Read Queue's Descriptor Table address - cmn_cfg.queue_select = VIRTIO_DEVICE_QUEUE_NUM as u16 - 1; - cmn_cfg.queues_config[cmn_cfg.queue_select as usize].desc_table = - GuestAddress(0xAABBCCDD_FFEEDDAA); - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_Q_DESCLO_REG, 0xFFEEDDAA_u32); - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_Q_DESCHI_REG, 0xAABBCCDD_u32); + virtio_dev + .lock() + .unwrap() + .set_queue_select(VIRTIO_DEVICE_QUEUE_NUM as u16 - 1); + let queue_select = virtio_dev.lock().unwrap().queue_select(); + virtio_dev.lock().unwrap().virtio_base_mut().queues_config[queue_select as usize] + .desc_table = GuestAddress(0xAABBCCDD_FFEEDDAA); + com_cfg_read_test!(virtio_pci, COMMON_Q_DESCLO_REG, 0xFFEEDDAA_u32); + com_cfg_read_test!(virtio_pci, COMMON_Q_DESCHI_REG, 0xAABBCCDD_u32); // Read Queue's Available Ring address - cmn_cfg.queue_select = 0; - cmn_cfg.queues_config[0].avail_ring = GuestAddress(0x11223344_55667788); - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_Q_AVAILLO_REG, 0x55667788_u32); - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_Q_AVAILHI_REG, 0x11223344_u32); + virtio_dev.lock().unwrap().set_queue_select(0); + virtio_dev.lock().unwrap().virtio_base_mut().queues_config[0].avail_ring = + GuestAddress(0x11223344_55667788); + com_cfg_read_test!(virtio_pci, COMMON_Q_AVAILLO_REG, 0x55667788_u32); + com_cfg_read_test!(virtio_pci, COMMON_Q_AVAILHI_REG, 0x11223344_u32); // Read Queue's Used Ring address - cmn_cfg.queue_select = 0; - cmn_cfg.queues_config[0].used_ring = GuestAddress(0x55667788_99AABBCC); - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_Q_USEDLO_REG, 0x99AABBCC_u32); - com_cfg_read_test!(cmn_cfg, virtio_dev, COMMON_Q_USEDHI_REG, 0x55667788_u32); + virtio_dev.lock().unwrap().set_queue_select(0); + virtio_dev.lock().unwrap().virtio_base_mut().queues_config[0].used_ring = + GuestAddress(0x55667788_99AABBCC); + com_cfg_read_test!(virtio_pci, COMMON_Q_USEDLO_REG, 0x99AABBCC_u32); + com_cfg_read_test!(virtio_pci, COMMON_Q_USEDHI_REG, 0x55667788_u32); } #[test] fn test_common_config_queue_error() { - let virtio_dev: Arc> = - Arc::new(Mutex::new(VirtioDeviceTest::new())); - let queue_size = virtio_dev.lock().unwrap().queue_size_max(); - let queue_num = virtio_dev.lock().unwrap().queue_num(); - let mut cmn_cfg = VirtioPciCommonConfig::new(queue_size, queue_num); + let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", @@ -1654,7 +1498,7 @@ mod tests { assert!(init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, - (queue_num + 1) as u32, + (virtio_dev.lock().unwrap().queue_num() + 1) as u32, &mut virtio_pci.config, virtio_pci.dev_id.clone(), &virtio_pci.name, @@ -1664,31 +1508,41 @@ mod tests { .is_ok()); // Error occurs when queue selector exceeds queue num - cmn_cfg.queue_select = VIRTIO_DEVICE_QUEUE_NUM as u16; - assert!(cmn_cfg - .read_common_config(&virtio_dev, COMMON_Q_SIZE_REG) - .is_err()); - assert!(cmn_cfg - .write_common_config(&virtio_pci, COMMON_Q_SIZE_REG, 128) + virtio_dev + .lock() + .unwrap() + .set_queue_select(VIRTIO_DEVICE_QUEUE_NUM as u16); + assert!(virtio_pci.read_common_config(COMMON_Q_SIZE_REG).is_err()); + assert!(virtio_pci + .write_common_config(COMMON_Q_SIZE_REG, 128) .is_err()); // Test Queue ready register - cmn_cfg.device_status.store( - CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER, - Ordering::SeqCst, + virtio_dev + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER); + virtio_dev.lock().unwrap().set_queue_select(0); + com_cfg_write_test!(virtio_pci, COMMON_Q_ENABLE_REG, 0x1_u32); + assert!( + virtio_dev + .lock() + .unwrap() + .virtio_base() + .queues_config + .get(0) + .unwrap() + .ready ); - cmn_cfg.queue_select = 0; - com_cfg_write_test!(cmn_cfg, virtio_pci, COMMON_Q_ENABLE_REG, 0x1_u32); - assert!(cmn_cfg.queues_config.get(0).unwrap().ready); // Failed to set Queue relevant register if device is no ready - cmn_cfg.device_status.store( - CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER_OK, - Ordering::SeqCst, - ); - cmn_cfg.queue_select = 1; - assert!(cmn_cfg - .write_common_config(&virtio_pci, COMMON_Q_MSIX_REG, 0x4_u32) + virtio_dev + .lock() + .unwrap() + .set_device_status(CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_DRIVER_OK); + virtio_dev.lock().unwrap().set_queue_select(1); + assert!(virtio_pci + .write_common_config(COMMON_Q_MSIX_REG, 0x4_u32) .is_err()); } @@ -1777,8 +1631,7 @@ mod tests { ) .unwrap(); - let virtio_dev: Arc> = - Arc::new(Mutex::new(VirtioDeviceTest::new())); + let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] @@ -1789,7 +1642,7 @@ mod tests { String::from("test device"), 0, sys_mem, - virtio_dev, + virtio_dev.clone(), Arc::downgrade(&parent_bus), false, ); @@ -1818,10 +1671,10 @@ mod tests { virtio_pci.assign_interrupt_cb(); // Prepare valid queue config - for queue_cfg in virtio_pci - .common_config + for queue_cfg in virtio_dev .lock() .unwrap() + .virtio_base_mut() .queues_config .iter_mut() { @@ -1831,13 +1684,14 @@ mod tests { queue_cfg.ready = true; queue_cfg.size = VIRTIO_DEVICE_QUEUE_SIZE; } - let common_cfg_ops = virtio_pci.build_common_cfg_ops(); + let common_cfg_ops = + VirtioPciDevice::build_common_cfg_ops(Arc::new(Mutex::new(virtio_pci))); // Device status is not ok, failed to activate virtio device let status = (CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER | CONFIG_STATUS_FEATURES_OK) .as_bytes(); (common_cfg_ops.write)(status, GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_pci.device_activated.load(Ordering::Relaxed), false); + assert_eq!(virtio_dev.lock().unwrap().device_activated(), false); // Device status is not ok, failed to activate virtio device let status = (CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER @@ -1845,7 +1699,7 @@ mod tests { | CONFIG_STATUS_FEATURES_OK) .as_bytes(); (common_cfg_ops.write)(status, GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_pci.device_activated.load(Ordering::Relaxed), false); + assert_eq!(virtio_dev.lock().unwrap().device_activated(), false); // Status is ok, virtio device is activated. let status = (CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER @@ -1853,11 +1707,11 @@ mod tests { | CONFIG_STATUS_FEATURES_OK) .as_bytes(); (common_cfg_ops.write)(status, GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_pci.device_activated.load(Ordering::Relaxed), true); + assert_eq!(virtio_dev.lock().unwrap().device_activated(), true); // If device status(not zero) is set to zero, reset the device (common_cfg_ops.write)(0_u32.as_bytes(), GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_pci.device_activated.load(Ordering::Relaxed), false); + assert_eq!(virtio_dev.lock().unwrap().device_activated(), false); } #[test] -- Gitee From f0bcf7d116d5b2038e1c3470181e62766d7d3a6f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 31 Jul 2023 16:48:43 +0800 Subject: [PATCH 1255/2187] add _typos.toml to declare some valid spellings reference https://crates.io/crates/typos Signed-off-by: yezengruan --- _typos.toml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 _typos.toml diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 000000000..1d161a7f2 --- /dev/null +++ b/_typos.toml @@ -0,0 +1,32 @@ +[files] +extend-exclude = ["docs/kernel_config/*", "_typos.toml"] + +[default.extend-identifiers] +APIC_MODE_EXTINT = "APIC_MODE_EXTINT" +baInterfaceNr = "baInterfaceNr" +BARs = "BARs" +DESCRIPTION_HEADERs = "DESCRIPTION_HEADERs" +E6GgSyMd0oQtUGFyNf5pRHlYqlx3s7PMPVUtRJP0bBnNd5eDwWAotInu33h6UI0zfKgckAxeVdEROKAExx5xWK = "E6GgSyMd0oQtUGFyNf5pRHlYqlx3s7PMPVUtRJP0bBnNd5eDwWAotInu33h6UI0zfKgckAxeVdEROKAExx5xWK" +fldXlNNdCeqMvoIfEFogBxlL = "fldXlNNdCeqMvoIfEFogBxlL" +INTERRUPT_TYPE_EXTINT = "INTERRUPT_TYPE_EXTINT" +ist_info = "ist_info" +KVM_CPUID_FLAG_SIGNIFCANT_INDEX = "KVM_CPUID_FLAG_SIGNIFCANT_INDEX" +MODE_PAGE_ALLS = "MODE_PAGE_ALLS" +n_subtiles = "n_subtiles" +O_WRONLY = "O_WRONLY" +RTC_MIS = "RTC_MIS" +SECCOMP_FILETER_FLAG_TSYNC = "SECCOMP_FILETER_FLAG_TSYNC" +test_ths = "test_ths" +UART_LSR_THRE = "UART_LSR_THRE" + +[default.extend-words] +ba = "ba" +deactive = "deactive" +Deactive = "Deactive" +fpr = "fpr" +fullfill = "fullfill" +hda = "hda" +inout = "inout" +IST = "IST" +NCE = "NCE" +parm = "parm" -- Gitee From 83636613e76f142d53d44c5a2bbe61e867b23a15 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 31 Jul 2023 16:48:27 +0800 Subject: [PATCH 1256/2187] fix some typos fix some typos by tool: https://crates.io/crates/typos Signed-off-by: yezengruan --- block_backend/src/qcow2/header.rs | 8 ++++---- block_backend/src/qcow2/mod.rs | 20 +++++++++---------- devices/src/acpi/ged.rs | 4 ++-- devices/src/acpi/power.rs | 8 ++++---- devices/src/camera_backend/v4l2.rs | 2 +- devices/src/scsi/bus.rs | 2 +- devices/src/usb/descriptor.rs | 2 +- devices/src/usb/mod.rs | 2 +- devices/src/usb/usbhost/host_usblib.rs | 4 ++-- devices/src/usb/usbhost/mod.rs | 4 ++-- docs/config_guidebook.md | 10 +++++----- machine/src/standard_vm/mod.rs | 4 ++-- machine_manager/src/config/incoming.rs | 2 +- pci/src/bus.rs | 2 +- .../functional/test_microvm_cmdline.py | 2 +- tests/mod_test/tests/virtio_test.rs | 2 +- util/src/daemonize.rs | 2 +- vfio/src/vfio_pci.rs | 18 ++++++++--------- virtio/src/device/block.rs | 2 +- 19 files changed, 50 insertions(+), 50 deletions(-) diff --git a/block_backend/src/qcow2/header.rs b/block_backend/src/qcow2/header.rs index 8fbf9a9bb..11a7c467d 100644 --- a/block_backend/src/qcow2/header.rs +++ b/block_backend/src/qcow2/header.rs @@ -236,7 +236,7 @@ impl QcowHeader { mod test { use crate::qcow2::header::*; - const DEFUALT_CLUSTER_SIZE: u64 = 64 * 1024; + const DEFAULT_CLUSTER_SIZE: u64 = 64 * 1024; fn valid_header_v3() -> Vec { // 10G @@ -302,7 +302,7 @@ mod test { let header = QcowHeader::from_vec(&buf).unwrap(); assert_eq!(header.magic, QCOW_MAGIC); assert_eq!(header.version, 2); - assert_eq!(header.cluster_size(), DEFUALT_CLUSTER_SIZE); + assert_eq!(header.cluster_size(), DEFAULT_CLUSTER_SIZE); assert_eq!(header.header_length, QCOW_VERSION_2_MIN_LEN as u32); assert_eq!(buf, header.to_vec()); @@ -310,7 +310,7 @@ mod test { let header = QcowHeader::from_vec(&buf).unwrap(); assert_eq!(header.magic, QCOW_MAGIC); assert_eq!(header.version, 3); - assert_eq!(header.cluster_size(), DEFUALT_CLUSTER_SIZE); + assert_eq!(header.cluster_size(), DEFAULT_CLUSTER_SIZE); assert_eq!(header.header_length, QCOW_VERSION_3_MIN_LEN as u32); assert_eq!(buf, header.to_vec()); @@ -318,7 +318,7 @@ mod test { let header = QcowHeader::from_vec(&buf).unwrap(); assert_eq!(header.magic, QCOW_MAGIC); assert_eq!(header.version, 3); - assert_eq!(header.cluster_size(), DEFUALT_CLUSTER_SIZE); + assert_eq!(header.cluster_size(), DEFAULT_CLUSTER_SIZE); assert_eq!(header.header_length, 112); // NOTE: only care the length we supported. assert_eq!(buf[0..QcowHeader::len()], header.to_vec()); diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 30b92ea15..e45c68f18 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1330,10 +1330,10 @@ impl BlockDriverOps for Qcow2Driver { let mut left = iovec.to_vec(); let total = std::cmp::min(nbytes, self.virtual_disk_size() - offset as u64); let mut req_list = Vec::new(); - let mut copyed = 0; - while copyed < total { - let pos = offset as u64 + copyed; - let count = self.cluster_aligned_bytes(pos, total - copyed); + let mut copied = 0; + while copied < total { + let pos = offset as u64 + copied; + let count = self.cluster_aligned_bytes(pos, total - copied); let (begin, end) = iovecs_split(left, count); left = end; if let HostOffset::DataAddress(host_offset) = self.host_offset_for_read(pos)? { @@ -1346,7 +1346,7 @@ impl BlockDriverOps for Qcow2Driver { } else { iov_from_buf_direct(&begin, &vec![0_u8; count as usize])?; } - copyed += count; + copied += count; } self.driver.read_vectored(req_list, completecb) @@ -1360,10 +1360,10 @@ impl BlockDriverOps for Qcow2Driver { let mut left = iovec.to_vec(); let total = std::cmp::min(nbytes, self.virtual_disk_size() - offset as u64); let mut req_list = Vec::new(); - let mut copyed = 0; - while copyed < total { - let pos = offset as u64 + copyed; - let count = self.cluster_aligned_bytes(pos, total - copyed); + let mut copied = 0; + while copied < total { + let pos = offset as u64 + copied; + let count = self.cluster_aligned_bytes(pos, total - copied); let (begin, end) = iovecs_split(left, count); left = end; if let HostOffset::DataAddress(host_offset) = self.host_offset_for_write(pos)? { @@ -1373,7 +1373,7 @@ impl BlockDriverOps for Qcow2Driver { offset: host_offset, nbytes, }); - copyed += count; + copied += count; } } diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 1e868b2fd..48532122f 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -191,8 +191,8 @@ impl AmlBuilder for Ged { AmlFieldUpdateRule::WriteAsZeros, ); - let elemt = AmlFieldUnit::new(Some(AML_GED_EVT_SEL), 32); - field.append_child(elemt); + let element = AmlFieldUnit::new(Some(AML_GED_EVT_SEL), 32); + field.append_child(element); acpi_dev.append_child(field); let mut method = AmlMethod::new("_EVT", 1, true); diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index a50ec7e20..d07aa69ac 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -189,15 +189,15 @@ impl PowerDev { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size, "PowerDev")?; - let pdev_availiable: bool; + let pdev_available: bool; { let mut pdev = dev.lock().unwrap(); - pdev_availiable = pdev.power_battery_init_info().is_ok(); - if pdev_availiable { + pdev_available = pdev.power_battery_init_info().is_ok(); + if pdev_available { pdev.send_power_event(AcpiEvent::BatteryInf); } } - if pdev_availiable { + if pdev_available { power_status_update(&dev.clone()); } else { let mut pdev = dev.lock().unwrap(); diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index b422f07f3..1335cf139 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -370,7 +370,7 @@ impl CameraHostdevOps for V4l2CameraBackend { out.height = frm.height; out.fps = 10000000_u32.checked_div(frm.interval).with_context(|| { format!( - "Invalied interval {} for format/frame {}:{}", + "Invalid interval {} for format/frame {}:{}", frm.interval, format_index, frame_index ) })?; diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 1304fee12..c2fc65fbb 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -278,7 +278,7 @@ const TYPE_INACTIVE: u8 = 0x20; const TYPE_NO_LUN: u8 = 0x7f; /// Notification Classes for GET EVENT STATUS NOTIFICATION. -/// 000b: No requested Event Clases are supported. +/// 000b: No requested Event Classes are supported. pub const GESN_NO_REQUESTED_EVENT: u8 = 0; /// 001b: Operational Change Request/Notification. pub const GESN_OPERATIONAL_CHANGE: u8 = 1; diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 9cbe8cebf..26003e558 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -464,7 +464,7 @@ pub trait UsbDescriptorOps { /// Set configuration descriptor with the Configuration Value. fn set_config_descriptor(&mut self, v: u8) -> Result<()>; - /// Set interface descriptor with the Interface and Alernate Setting. + /// Set interface descriptor with the Interface and Alternate Setting. fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()>; /// Init all endpoint descriptors and reset the USB endpoint. diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 4e31a89b0..b0c2ee80f 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -367,7 +367,7 @@ pub trait UsbDeviceOps: Send + Sync { usb_dev.port = port; } - /// Handle usb packet, used for controller to deliever packet to device. + /// Handle usb packet, used for controller to deliver packet to device. fn handle_packet(&mut self, packet: &Arc>) { let mut locked_packet = packet.lock().unwrap(); locked_packet.status = UsbPacketStatus::Success; diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 135a94d72..18ed414b0 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -164,7 +164,7 @@ pub fn set_iso_packet_length( // SAFETY: host_transfer is guaranteed to be valid once created. unsafe { iso_packet_desc = (*host_transfer).iso_packet_desc.as_mut_ptr() } // SAFETY: iso_packet_desc is guaranteed to be valid once host_transfer is created - // and packet is guaranteed to be not out of boundry. + // and packet is guaranteed to be not out of boundary. unsafe { (*iso_packet_desc.offset(packet as isize)).length = max_packet_size as c_uint } } @@ -173,7 +173,7 @@ pub fn get_iso_packet_acl_length(host_transfer: *mut libusb_transfer, packet: u3 // SAFETY: host_transfer is guaranteed to be valid once created. unsafe { iso_packet_desc = (*host_transfer).iso_packet_desc.as_mut_ptr() } // SAFETY: iso_packet_desc is guaranteed to be valid once host_transfer is created - // and packet is guaranteed to be not out of boundry. + // and packet is guaranteed to be not out of boundary. unsafe { (*iso_packet_desc.offset(packet as isize)).actual_length } } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 8ca6caa2b..d706993ba 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -235,7 +235,7 @@ impl IsoTransfer { } let buffer = // SAFETY: host_transfer is guaranteed to be valid once created - // and packet is guaranteed to be not out of boundry. + // and packet is guaranteed to be not out of boundary. unsafe { libusb_get_iso_packet_buffer_simple(self.host_transfer, self.packet) }; // SAFETY: buffer is already allocated and size will not be exceed @@ -888,7 +888,7 @@ impl UsbHost { pub fn handle_iso_data_out(&mut self, _packet: Arc>) { // TODO - error!("USBHost device Unsupport Isochronous Transfer from guest to device."); + error!("USBHost device Unsupported Isochronous Transfer from guest to device."); } fn submit_host_transfer( diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 7c82b39e5..3032b6142 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -261,19 +261,19 @@ The SMBIOS specification defines the data structures and information that will e ```shell # cmdline -# type 0: BIOS infomation,Support Version and Release Date string +# type 0: BIOS information, support version and release date string. -smbios type=0[,vendor=str][,version=str][,date=str] -# type 1: System infomation,The information in this structure defines attributes of +# type 1: System information, the information in this structure defines attributes of # the overall system and is intended to be associated with the Component ID group of the system’s MIF. -smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str] -# type 2: Baseboard infomation,the information in this structure defines attributes of a system baseboard +# type 2: Baseboard information, the information in this structure defines attributes of a system baseboard # (for example, a motherboard, planar, server blade, or other standard system module). -smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str][,asset=str][,location=str] -# type 3: System Enclosure infomation,defines attributes of the system’s mechanical enclosure(s). +# type 3: System Enclosure information, defines attributes of the system’s mechanical enclosure(s). # For example, if a system included a separate enclosure for its peripheral devices, # two structures would be returned: one for the main system enclosure and the second for the peripheral device enclosure. -smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str] -# type 4: Processor infomation,defines the attributes of a single processor; +# type 4: Processor information, defines the attributes of a single processor; # a separate structure instance is provided for each system processor socket/slot. # For example, a system with an IntelDX2 processor would have a single structure instance # while a system with an IntelSX2 processor would have a structure to describe the main CPU diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 490d66a47..f4311fd37 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1759,7 +1759,7 @@ impl DeviceInterface for StdMachine { let cmd_args: Vec<&str> = args.command_line.split(' ').collect(); match cmd_args[0] { "drive_add" => { - // The drive_add command has three arguments splited by space: + // The drive_add command has three arguments split by space: // "drive_add dummy file=/path/to/file,format=raw,if=none,id=drive-id..." // The 'dummy' here is a placeholder for pci address which is not needed for drive. if cmd_args.len() != 3 { @@ -1798,7 +1798,7 @@ impl DeviceInterface for StdMachine { } } "drive_del" => { - // The drive_del command has two arguments splited by space: + // The drive_del command has two arguments split by space: // "drive_del drive-id" if cmd_args.len() != 2 { return Response::create_error_response( diff --git a/machine_manager/src/config/incoming.rs b/machine_manager/src/config/incoming.rs index e967617b8..98c3abf08 100644 --- a/machine_manager/src/config/incoming.rs +++ b/machine_manager/src/config/incoming.rs @@ -148,6 +148,6 @@ mod tests { ); let mut vm_config_case2 = VmConfig::default(); - assert!(vm_config_case2.add_incoming("unkonw:/tmp/").is_err()); + assert!(vm_config_case2.add_incoming("unknown:/tmp/").is_err()); } } diff --git a/pci/src/bus.rs b/pci/src/bus.rs index b0f0f34b9..3f89613c7 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -34,7 +34,7 @@ pub struct PciBus { pub devices: HashMap>>, /// Child buses of the bus. pub child_buses: Vec>>, - /// Pci bridge which the bus orignates from. + /// Pci bridge which the bus originates from. pub parent_bridge: Option>>, /// IO region which the parent bridge manages. #[cfg(target_arch = "x86_64")] diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py b/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py index 4eb3d0e56..7eaabafb6 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_cmdline.py @@ -58,7 +58,7 @@ def test_microvm_with_unsupported_param(): 1) Launch microvm with a unsupported param. 2) Expect run with error code, but not panic. """ - _cmd = "%s --unsupport" % CONFIG.stratovirt_microvm_bin + _cmd = "%s --unsupported" % CONFIG.stratovirt_microvm_bin try: _result = run(_cmd, shell=True, capture_output=True, check=False) except TypeError: diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 2920dcc8a..9ad787d2b 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -2057,7 +2057,7 @@ fn virtio_io_abnormal_avail_ring() { /// TestStep: /// 1. Init device with or with not EVENT_IDX feature. /// 2. Do the I/O request with avail->used_event: -/// 1) without EVENT_IDX, set valud to used_event. +/// 1) without EVENT_IDX, set valid to used_event. /// 2) with EVENT_IDX, set u16::MAX to used_event. /// 3) with EVENT_IDX, do not modify used_event. /// 3. Send qmp to StratoVirt. diff --git a/util/src/daemonize.rs b/util/src/daemonize.rs index e7671869e..3453913eb 100644 --- a/util/src/daemonize.rs +++ b/util/src/daemonize.rs @@ -84,7 +84,7 @@ fn fork() -> Result<()> { /// process also becomes the process group leader or a new process group in the /// session. /// The calling process will be the only process in the new process group and in -/// the new session. New session has no controlling termimal. +/// the new session. New session has no controlling terminal. /// /// # Errors /// diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index bdf5f929b..428a323f1 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -59,8 +59,8 @@ struct MsixTable { struct VfioMsixInfo { // Table bar, table offset and table size info. table: MsixTable, - // Msix enteries. - enteries: u16, + // Msix entries. + entries: u16, // Vfio device irq info #[allow(dead_code)] vfio_irq: HashMap, @@ -248,12 +248,12 @@ impl VfioPciDevice { &self.pci_config.config, cap_offset + MSIX_CAP_CONTROL as usize, )?; - let enteries = (ctrl & MSIX_TABLE_SIZE_MAX) + 1; - // Make sure that if enteries less than 1 or greater than (0x7ff + 1) is error value. - if !(1..=(MSIX_TABLE_SIZE_MAX + 1)).contains(&enteries) { + let entries = (ctrl & MSIX_TABLE_SIZE_MAX) + 1; + // Make sure that if entries less than 1 or greater than (0x7ff + 1) is error value. + if !(1..=(MSIX_TABLE_SIZE_MAX + 1)).contains(&entries) { bail!( "The number of MSI-X vectors is invalid, MSI-X vectors are {}", - enteries, + entries, ); } @@ -261,9 +261,9 @@ impl VfioPciDevice { table: MsixTable { table_bar: (table as u16 & MSIX_TABLE_BIR) as u8, table_offset: (table & MSIX_TABLE_OFFSET) as u64, - table_size: (enteries * MSIX_TABLE_ENTRY_SIZE) as u64, + table_size: (entries * MSIX_TABLE_ENTRY_SIZE) as u64, }, - enteries, + entries, vfio_irq, }) } @@ -729,7 +729,7 @@ impl VfioPciDevice { }; gsi_routes.push(gsi_route); - let entries = self.msix_info.as_ref().unwrap().enteries; + let entries = self.msix_info.as_ref().unwrap().entries; for i in 1..entries { let gsi_route = GsiMsiRoute { irq_fd: None, diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index c9dab508e..3d44ae57e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -889,7 +889,7 @@ pub struct VirtioBlkConfig { capacity: u64, /// The maximum segment size. size_max: u32, - /// Tne maximum number of segments. + /// The maximum number of segments. pub seg_max: u32, /// Geometry of the block device. geometry: VirtioBlkGeometry, -- Gitee From 80b4737688739d232b44009fd2651b7adb37a77b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 1 Aug 2023 19:34:47 +0800 Subject: [PATCH 1257/2187] package: Reduce rust package dependency Signed-off-by: Keqian Zhu --- Cargo.lock | 417 ++---------------- acpi/Cargo.toml | 2 - address_space/Cargo.toml | 3 - address_space/src/listener.rs | 7 - block_backend/Cargo.toml | 3 +- block_backend/src/qcow2/mod.rs | 2 +- boot_loader/Cargo.toml | 3 - cpu/Cargo.toml | 3 - cpu/src/lib.rs | 2 - cpu/src/x86_64/mod.rs | 2 - devices/Cargo.toml | 5 - .../src/interrupt_controller/aarch64/gicv2.rs | 2 - .../src/interrupt_controller/aarch64/gicv3.rs | 4 - devices/src/usb/camera.rs | 9 +- ...Third_Party_Open_Source_Software_Notice.md | 6 - machine/Cargo.toml | 4 - machine_manager/src/config/camera.rs | 16 +- util/Cargo.toml | 1 - util/src/aio/libaio.rs | 2 +- util/src/aio/raw.rs | 30 +- util/src/file.rs | 2 +- util/src/v4l2.rs | 2 +- vhost_user_fs/Cargo.toml | 3 +- vhost_user_fs/src/fs_ops.rs | 148 +++---- 24 files changed, 140 insertions(+), 538 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b7abf7d8..a47cf3d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,22 +8,12 @@ version = "2.2.0" dependencies = [ "address_space", "anyhow", - "byteorder", "log", "machine_manager", "thiserror", "util", ] -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - [[package]] name = "address_space" version = "2.2.0" @@ -38,18 +28,11 @@ dependencies = [ "machine_manager", "migration", "migration_derive", - "serial_test", "thiserror", "util", "vmm-sys-util", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aho-corasick" version = "0.7.19" @@ -123,21 +106,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.21.2" @@ -195,7 +163,6 @@ dependencies = [ "log", "machine_manager", "once_cell", - "serde_json", "thiserror", "util", "vmm-sys-util", @@ -209,19 +176,16 @@ dependencies = [ "anyhow", "devices", "kvm-bindings", - "kvm-ioctls", - "libc", "log", "thiserror", "util", - "vmm-sys-util", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -308,18 +272,18 @@ dependencies = [ [[package]] name = "const_format" -version = "0.2.26" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.22" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" dependencies = [ "proc-macro2", "quote", @@ -348,25 +312,11 @@ dependencies = [ "machine_manager", "migration", "migration_derive", - "serial_test", "thiserror", "util", "vmm-sys-util", ] -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "devices" version = "2.2.0" @@ -396,9 +346,6 @@ dependencies = [ "rusb", "serde", "serde_json", - "serial_test", - "strum", - "strum_macros", "sysbus", "thiserror", "ui", @@ -431,37 +378,6 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "backtrace", - "version_check", -] - [[package]] name = "field-offset" version = "0.3.5" @@ -472,21 +388,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.28" @@ -494,7 +395,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -531,12 +431,6 @@ dependencies = [ "syn 2.0.18", ] -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - [[package]] name = "futures-task" version = "0.3.28" @@ -549,13 +443,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", - "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", @@ -623,9 +513,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -652,12 +542,6 @@ dependencies = [ "temp-dir", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" - [[package]] name = "gio" version = "0.17.9" @@ -872,9 +756,9 @@ checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1000,16 +884,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.18" @@ -1035,17 +909,13 @@ dependencies = [ "migration", "migration_derive", "pci", - "serde", "serde_json", "smbios", - "strum", - "strum_macros", "sysbus", "thiserror", "ui", "util", "vfio", - "vfio-bindings", "virtio", "vmm-sys-util", ] @@ -1137,15 +1007,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - [[package]] name = "mod_test" version = "2.2.0" @@ -1250,15 +1111,6 @@ dependencies = [ "objc", ] -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.18.0" @@ -1311,29 +1163,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - [[package]] name = "pci" version = "2.2.0" @@ -1480,15 +1309,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.6.0" @@ -1531,12 +1351,6 @@ dependencies = [ "libusb1-sys", ] -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "rustc-hash" version = "1.1.0" @@ -1554,9 +1368,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.1" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", @@ -1566,18 +1380,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" dependencies = [ "ring", "untrusted", @@ -1585,9 +1399,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" @@ -1607,12 +1421,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "sct" version = "0.7.0" @@ -1669,31 +1477,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serial_test" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" -dependencies = [ - "dashmap", - "futures", - "lazy_static", - "log", - "parking_lot", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", -] - [[package]] name = "shared_child" version = "0.3.5" @@ -1985,7 +1768,6 @@ dependencies = [ "anyhow", "arc-swap", "byteorder", - "errno", "io-uring", "kvm-bindings", "kvm-ioctls", @@ -2061,14 +1843,13 @@ dependencies = [ "anyhow", "capng", "devices", - "errno", - "error-chain", "hypervisor", "libc", "log", "machine_manager", "migration", "migration_derive", + "nix 0.26.2", "pci", "sysbus", "thiserror", @@ -2122,9 +1903,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2132,24 +1913,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2157,28 +1938,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -2217,138 +1998,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - [[package]] name = "winnow" version = "0.4.6" diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml index faa035c07..bbe9e5ace 100644 --- a/acpi/Cargo.toml +++ b/acpi/Cargo.toml @@ -8,10 +8,8 @@ description = "acpi module" [dependencies] log = "0.4" -byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" - address_space = { path = "../address_space" } util = {path = "../util"} machine_manager = {path = "../machine_manager"} diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index b8cfb7540..82e4f730b 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -20,6 +20,3 @@ machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } - -[dev-dependencies] -serial_test = "2.0.0" diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 2bbd167a3..cc01a278e 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -617,7 +617,6 @@ impl Listener for KvmIoListener { mod test { use hypervisor::kvm::KVMFds; use libc::EFD_NONBLOCK; - use serial_test::serial; use vmm_sys_util::eventfd::EventFd; use super::*; @@ -649,7 +648,6 @@ mod test { } #[test] - #[serial] fn test_alloc_slot() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -675,7 +673,6 @@ mod test { } #[test] - #[serial] fn test_add_del_ram_region() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -703,7 +700,6 @@ mod test { } #[test] - #[serial] fn test_add_region_align() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -727,7 +723,6 @@ mod test { } #[test] - #[serial] fn test_add_del_ioeventfd() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -774,7 +769,6 @@ mod test { } #[test] - #[serial] fn test_ioeventfd_with_data_match() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -829,7 +823,6 @@ mod test { } #[test] - #[serial] #[cfg(target_arch = "x86_64")] fn test_kvm_io_listener() { let kvm_fds = KVMFds::new(); diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index a20e5381b..f997c6b8d 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -9,10 +9,9 @@ license = "Mulan PSL v2" thiserror = "1.0" vmm-sys-util = "0.11.0" anyhow = "1.0" -serde_json = "1.0" log = "0.4" byteorder = "1.4.3" -once_cell = "1.13.0" +once_cell = "1.18.0" libc = "0.2" machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index e45c68f18..6765d06c0 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1627,7 +1627,7 @@ mod test { .unwrap() .split('\t') .collect::>(); - serde_json::from_str::(str_out[0]).unwrap() + str_out[0].parse::().unwrap() } fn vec_is_zero(vec: &[u8]) -> bool { diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index 8dec57f78..2d14d2b08 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -9,10 +9,7 @@ license = "Mulan PSL v2" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" -libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } devices = { path = "../devices" } util = { path = "../util" } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 2a4787c5e..88dfe2175 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -20,9 +20,6 @@ migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } -[dev-dependencies] -serial_test = "2.0.0" - [features] default = [] boot_time = [] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 47ca6741c..c90d8c4e6 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -866,7 +866,6 @@ mod tests { use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineInterface, MachineLifecycle, }; - use serial_test::serial; use super::*; @@ -932,7 +931,6 @@ mod tests { impl MachineInterface for TestVm {} #[test] - #[serial] #[allow(unused)] fn test_cpu_lifecycle() { let kvm_fds = KVMFds::new(); diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 55c8660af..89b879f0a 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -612,11 +612,9 @@ mod test { use super::*; use hypervisor::kvm::{KVMFds, KVM_FDS}; use kvm_bindings::kvm_segment; - use serial_test::serial; use std::sync::Arc; #[test] - #[serial] fn test_x86_64_cpu() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { diff --git a/devices/Cargo.toml b/devices/Cargo.toml index ddfb3bb8f..48c7f4e05 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -17,8 +17,6 @@ vmm-sys-util = "0.11.1" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" once_cell = "1.18.0" -strum = "0.24.1" -strum_macros = "0.24.3" v4l2-sys-mit = "0.3.0" serde_json = "1.0" rand = "0.8.5" @@ -44,8 +42,5 @@ libusb1-sys = "0.6.4" cairo-rs = "0.17.10" alsa = "0.7.0" -[dev-dependencies] -serial_test = "2.0.0" - [features] default = [] diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index bf44049f4..def1df699 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -296,7 +296,6 @@ impl GICDevice for GICv2 { #[cfg(test)] mod tests { use hypervisor::kvm::KVMFds; - use serial_test::serial; use super::super::GICVersion; use super::super::GICv2Config; @@ -305,7 +304,6 @@ mod tests { use crate::GIC_IRQ_MAX; #[test] - #[serial] fn test_create_gicv2() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 79bf884e8..2444e1658 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -578,7 +578,6 @@ impl GICv3Its { #[cfg(test)] mod tests { use hypervisor::kvm::KVMFds; - use serial_test::serial; use super::super::GICVersion; use super::super::GICv3Config; @@ -587,7 +586,6 @@ mod tests { use crate::GIC_IRQ_MAX; #[test] - #[serial] fn test_create_gicv3() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -611,7 +609,6 @@ mod tests { } #[test] - #[serial] fn test_create_gic_device() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { @@ -637,7 +634,6 @@ mod tests { } #[test] - #[serial] fn test_gic_redist_regions() { let kvm_fds = KVMFds::new(); if kvm_fds.vm_fd.is_none() { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index b147221ba..4402cc1c6 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -20,8 +20,6 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; use log::{debug, error, info}; -use strum::EnumCount; -use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -108,8 +106,9 @@ pub struct UsbCamera { delete_evts: Vec, } -#[derive(Debug, EnumCountMacro, EnumIter)] +#[derive(Debug)] enum UsbCameraStringIDs { + #[allow(unused)] Invalid = 0, Manufacture, Product, @@ -119,12 +118,14 @@ enum UsbCameraStringIDs { VideoControl, InputTerminal, OutputTerminal, + #[allow(unused)] SelectUnit, + #[allow(unused)] ProcessingUnit, VideoStreaming, } -const UVC_CAMERA_STRINGS: [&str; UsbCameraStringIDs::COUNT] = [ +const UVC_CAMERA_STRINGS: [&str; 12] = [ "", "StratoVirt", "USB Camera", diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index e0fae0721..746f6d334 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -377,12 +377,6 @@ Copyright (c) 2017 arc-swap developers License: MIT or Apache License Version 2.0 Please see above. -Software: serial_test 2.0.0 -Copyright notice: -Copyright (c) 2018 Tom Parker-Shemilt -License: MIT -Please see above. - Software: syn 2.0.18 Copyright notice: Copyright (c) David Tolnay diff --git a/machine/Cargo.toml b/machine/Cargo.toml index ddaf0b59e..4c87bf794 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -10,14 +10,10 @@ description = "Emulation machines" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } log = "0.4" libc = "0.2" -serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" vmm-sys-util = "0.11.1" -vfio-bindings = "0.3" thiserror = "1.0" anyhow = "1.0" -strum = "0.24.1" -strum_macros = "0.24.3" acpi = { path = "../acpi" } smbios = { path = "../smbios" } address_space = { path = "../address_space" } diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index 6603a4f93..ee17aaf5f 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -15,8 +15,6 @@ use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; use serde::{Deserialize, Serialize}; -use strum::{EnumCount, IntoEnumIterator}; -use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; use super::error::ConfigError; use crate::{ @@ -31,7 +29,7 @@ pub struct CameraDevConfig { pub backend: CamBackendType, } -#[derive(Clone, Copy, Debug, PartialEq, EnumCountMacro, EnumIter, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum CamBackendType { V4l2, Demo, @@ -41,18 +39,14 @@ impl FromStr for CamBackendType { type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { - for i in CamBackendType::iter() { - if s == CAM_OPT_STR_BACKEND_TYPES[i as usize] { - return Ok(i); - } + match s { + "v4l2" => Ok(CamBackendType::V4l2), + "demo" => Ok(CamBackendType::Demo), + _ => Err(anyhow!("Unknown camera backend type")), } - - Err(anyhow!("Unknown camera backend type")) } } -pub const CAM_OPT_STR_BACKEND_TYPES: [&str; CamBackendType::COUNT] = ["v4l2", "demo"]; - impl CameraDevConfig { pub fn new() -> CameraDevConfig { CameraDevConfig { diff --git a/util/Cargo.toml b/util/Cargo.toml index 5f9a84ea9..10c1d70c7 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -19,7 +19,6 @@ vmm-sys-util = "0.11.1" byteorder = "1.4.3" once_cell = "1.18.0" io-uring = "0.6.0" -errno = "0.3.1" serde = { version = "1.0", features = ["derive"] } v4l2-sys-mit = "0.3.0" nix = "0.26.2" diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 560bc0a5d..2407fcd3a 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -160,7 +160,7 @@ impl AioContext for LibaioContext { if ret >= 0 { return Ok(ret as usize); } - if errno::errno().0 != libc::EAGAIN { + if nix::errno::errno() != libc::EAGAIN { bail!("Failed to submit aio, return {}.", ret); } Ok(0) diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 36c179c33..0808a44ae 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -30,7 +30,8 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { offset as off_t, ) as i64 }; - if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) + { break; } } @@ -40,7 +41,7 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { buf, size, offset, - errno::errno().0 + nix::errno::errno() ); } ret @@ -58,7 +59,8 @@ pub fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { offset as off_t, ) as i64 }; - if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) + { break; } } @@ -66,7 +68,7 @@ pub fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { error!( "Failed to preadv: offset{}, errno{}.", offset, - errno::errno().0, + nix::errno::errno(), ); } ret @@ -84,7 +86,8 @@ pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { offset as off_t, ) as i64 }; - if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) + { break; } } @@ -94,7 +97,7 @@ pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { buf, size, offset, - errno::errno().0, + nix::errno::errno(), ); } ret @@ -112,7 +115,8 @@ pub fn raw_writev(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { offset as off_t, ) as i64 }; - if !(ret < 0 && (errno::errno().0 == libc::EINTR || errno::errno().0 == libc::EAGAIN)) { + if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) + { break; } } @@ -120,7 +124,7 @@ pub fn raw_writev(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { error!( "Failed to pwritev: offset{}, errno{}.", offset, - errno::errno().0, + nix::errno::errno(), ); } ret @@ -130,7 +134,7 @@ pub fn raw_datasync(fd: RawFd) -> i64 { // SAFETY: fd is valid. let ret = unsafe { i64::from(fdatasync(fd)) }; if ret < 0 { - error!("Failed to fdatasync: errno{}.", errno::errno().0); + error!("Failed to fdatasync: errno{}.", nix::errno::errno()); } ret } @@ -147,7 +151,7 @@ pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { size as i64, ) as i64 }; - if ret == 0 || errno::errno().0 != libc::EINTR { + if ret == 0 || nix::errno::errno() != libc::EINTR { break; } } @@ -155,7 +159,7 @@ pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { error!( "Failed to fallocate for {}, errno {}.", fd, - errno::errno().0, + nix::errno::errno(), ); } ret @@ -173,7 +177,7 @@ pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i64 { size as i64, ) as i64 }; - if ret == 0 || errno::errno().0 != libc::EINTR { + if ret == 0 || nix::errno::errno() != libc::EINTR { break; } } @@ -181,7 +185,7 @@ pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i64 { error!( "Failed to fallocate zero range for fd {}, errno {}.", fd, - errno::errno().0, + nix::errno::errno(), ); } ret diff --git a/util/src/file.rs b/util/src/file.rs index 1c29ab7bb..942edb2e9 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -47,7 +47,7 @@ fn is_io_aligned(file: &File, buf: u64, size: usize) -> bool { 0, ) }; - ret >= 0 || errno::errno().0 != libc::EINVAL + ret >= 0 || nix::errno::errno() != libc::EINVAL } pub fn get_file_alignment(file: &File, direct: bool) -> (u32, u32) { diff --git a/util/src/v4l2.rs b/util/src/v4l2.rs index dfeb0d6e9..be3f86084 100644 --- a/util/src/v4l2.rs +++ b/util/src/v4l2.rs @@ -212,7 +212,7 @@ impl V4l2Backend { pub fn dequeue_buffer(&self, buf: &v4l2_buffer) -> Result { let ret = unsafe { ioctl_with_ref(self, VIDIOC_DQBUF(), buf) }; if ret < 0 { - if errno::errno().0 == libc::EAGAIN { + if nix::errno::errno() == libc::EAGAIN { return Ok(false); } bail!( diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index e10ec36da..70a40a25b 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -8,12 +8,11 @@ description = "Provide virtio fs for VM" [dependencies] capng = "0.2.3" -errno = "0.3.1" log = "0.4" libc = "0.2" +nix = "0.26.2" thiserror = "1.0" anyhow = "1.0" -error-chain = "0.12.4" vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index e914ce020..a9e265291 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -34,9 +34,9 @@ pub type DirentPtr = *mut libc::dirent; pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { let mut stat: libc::stat = unsafe { std::mem::zeroed() }; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::fstatat(file.as_raw_fd(), name.as_ptr(), &mut stat, flags) } < 0 { - return (stat, errno::errno().0); + return (stat, nix::errno::errno()); } (stat, FUSE_OK) @@ -49,10 +49,10 @@ pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { /// * `name` - The path name in the host filesystem. /// * `flags` - The flags used to open a file. pub fn open(name: CString, flags: i32) -> (Option, i32) { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let fd = unsafe { libc::open(name.as_ptr(), flags) }; if fd < 0 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } let file = unsafe { File::from_raw_fd(fd) }; @@ -68,11 +68,11 @@ pub fn open(name: CString, flags: i32) -> (Option, i32) { /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode used to open a file. pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option, i32) { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let fd = unsafe { libc::openat(file.as_raw_fd(), name.as_ptr(), flags, mode) }; if fd < 0 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } let file = unsafe { File::from_raw_fd(fd) }; @@ -87,9 +87,9 @@ pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::fchmod(file.as_raw_fd(), mode) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -104,9 +104,9 @@ pub fn fchmod(file: &File, mode: u32) -> i32 { /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode indicates the permissions of the file will be set. pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::fchmodat(file.as_raw_fd(), name.as_ptr(), mode, 0) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -123,9 +123,9 @@ pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { /// * `gid` - The group id will be set. /// * `flags` - The flags indicates the action of file will be set. pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::fchownat(file.as_raw_fd(), name.as_ptr(), uid, gid, flags) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -138,9 +138,9 @@ pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> /// * `file` - The file handler saves the open file descriptor. /// * `size` - The size of truncating file. pub fn ftruncate(file: &File, size: u64) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::ftruncate(file.as_raw_fd(), size as i64) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -167,9 +167,9 @@ pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) - }, ]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::futimens(file.as_raw_fd(), tv.as_ptr()) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -206,9 +206,9 @@ pub fn utimensat( }, ]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::utimensat(file.as_raw_fd(), name.as_ptr(), tv.as_ptr(), flags) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -224,7 +224,7 @@ pub fn utimensat( pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { let mut buf = vec![0; MAX_PATH_LEN + 1]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::readlinkat( file.as_raw_fd(), @@ -235,7 +235,7 @@ pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { }; if ret == -1 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } buf.resize(ret as usize, 0); @@ -252,12 +252,12 @@ pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { /// * `name` - The name indicates the file path is relative to the starting directory. /// * `link_name` - The link name is new path name for the target path name. pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::symlinkat(link_name.as_ptr(), file.as_raw_fd(), name.as_ptr()) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -275,18 +275,18 @@ pub fn change_uid_gid(new_uid: u32, new_gid: u32, old_uid: &mut u32, old_gid: &m let current_uid = unsafe { libc::geteuid() }; let current_gid = unsafe { libc::getegid() }; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, new_gid, -1) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, new_uid, -1) }; if ret == -1 { unsafe { libc::syscall(libc::SYS_setresgid, -1, current_gid, -1) }; - return errno::errno().0; + return nix::errno::errno(); } *old_uid = current_uid; @@ -324,12 +324,12 @@ pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { /// * `mode` - The mode indicates both the file mode to use and the type of node to be created. /// * `rdev` - The rdev indicates the major and minor numbers of the special file. pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::mknodat(file.as_raw_fd(), name.as_ptr(), mode, rdev as u64) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -344,9 +344,9 @@ pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode indicates the permissions of the new directory. pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::mkdirat(file.as_raw_fd(), name.as_ptr(), mode) } < 0 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -361,12 +361,12 @@ pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { /// * `name` - The name indicates the file path is relative to the starting directory. /// * `flags` - The flags indicates the operation of deleting a name. pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::unlinkat(file.as_raw_fd(), name.as_ptr(), flags) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -383,7 +383,7 @@ pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { /// new file. /// * `newname` - The name indicates the file path is relative to the starting of new directory. pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::renameat( @@ -395,7 +395,7 @@ pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> }; if ret != FUSE_OK { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -419,7 +419,7 @@ pub fn linkat( new_name: CString, flags: i32, ) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::linkat( @@ -432,7 +432,7 @@ pub fn linkat( }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -446,9 +446,9 @@ pub fn linkat( pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { let mut stat: libc::statvfs = unsafe { std::mem::zeroed() }; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); if unsafe { libc::fstatvfs(file.as_raw_fd(), &mut stat) } < 0 { - return (stat, errno::errno().0); + return (stat, nix::errno::errno()); } (stat, FUSE_OK) @@ -462,7 +462,7 @@ pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { /// * `datasync` - The datasync indicates whether to use the fdatasync /// or fsync interface. pub fn fsync(file: &File, datasync: bool) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = if datasync { unsafe { libc::fdatasync(file.as_raw_fd()) } @@ -471,7 +471,7 @@ pub fn fsync(file: &File, datasync: bool) -> i32 { }; if ret != FUSE_OK { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -483,10 +483,10 @@ pub fn fsync(file: &File, datasync: bool) -> i32 { /// /// * `file` - The file handler saves the open directory descriptor. pub fn fchdir(file: &File) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::fchdir(file.as_raw_fd()) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -502,7 +502,7 @@ pub fn fchdir(file: &File) -> i32 { /// * `size` - The size of the string of value. /// * `flags` - The flags indicates the attribute will be set. pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: u32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::setxattr( path.as_ptr(), @@ -514,7 +514,7 @@ pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -530,7 +530,7 @@ pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: /// * `size` - The size of the string of value. /// * `flags` - The flags indicates the attribute will be set. pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: u32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::fsetxattr( file.as_raw_fd(), @@ -542,7 +542,7 @@ pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -558,7 +558,7 @@ pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::getxattr( path.as_ptr(), @@ -568,7 +568,7 @@ pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, ) }; if ret == -1 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } buf.resize(ret as usize, 0); @@ -586,7 +586,7 @@ pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::fgetxattr( file.as_raw_fd(), @@ -596,7 +596,7 @@ pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, ) }; if ret == -1 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } buf.resize(ret as usize, 0); @@ -613,11 +613,11 @@ pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::listxattr(path.as_ptr(), buf.as_mut_ptr() as *mut libc::c_char, size) }; if ret == -1 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } buf.resize(ret as usize, 0); @@ -634,7 +634,7 @@ pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::flistxattr( file.as_raw_fd(), @@ -643,7 +643,7 @@ pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { ) }; if ret == -1 { - return (None, errno::errno().0); + return (None, nix::errno::errno()); } buf.resize(ret as usize, 0); @@ -658,10 +658,10 @@ pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { /// * `path` - The path in the host filesystem. /// * `name` - The name of the extended attribute value. pub fn remove_xattr(path: CString, name: CString) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -674,10 +674,10 @@ pub fn remove_xattr(path: CString, name: CString) -> i32 { /// * `file` - The file handler saves the open file descriptor. /// * `name` - The name of the extended attribute value. pub fn fremove_xattr(file: &File, name: CString) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::fremovexattr(file.as_raw_fd(), name.as_ptr()) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -698,11 +698,11 @@ pub fn umask(umask: u32) { /// /// * `fd` - The open file descriptor used to open a directory stream. pub fn fdopen_dir(fd: RawFd) -> (Option, i32) { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let dirp = unsafe { libc::fdopendir(fd) }; - if errno::errno().0 != 0 { - return (None, errno::errno().0); + if nix::errno::errno() != 0 { + return (None, nix::errno::errno()); } (Some(dirp), FUSE_OK) @@ -726,11 +726,11 @@ pub fn seek_dir(dirp: &mut DirPtr, offset: u64) { /// /// * `dirp` - The pointer to open a directory. pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let direntp = unsafe { libc::readdir(*dirp) }; - if errno::errno().0 != 0 { - return (None, errno::errno().0); + if nix::errno::errno() != 0 { + return (None, nix::errno::errno()); } (Some(direntp), FUSE_OK) @@ -743,10 +743,10 @@ pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { /// * `file` - The file handler saves the open file descriptor. /// * `operation` - The operation of lock type. pub fn flock(file: &File, operation: i32) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -779,11 +779,11 @@ pub fn fcntl_flock( flock.l_len = file_lock.end as i64 - file_lock.start as i64 + 1; } - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &mut flock) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } file_lock_out.lock_type = flock.l_type as u32; @@ -810,13 +810,13 @@ pub fn fcntl_flock( /// * `offset` - The offset in the file. /// * `length` - The length that needs to be allocated. pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::fallocate(file.as_raw_fd(), mode as i32, offset as i64, length as i64) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK @@ -830,11 +830,11 @@ pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { /// * `offset` - The offset in the file used together with the whence. /// * `whence` - The length that needs to be allocated. pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::lseek(file.as_raw_fd(), offset as i64, whence as i32) }; if ret == -1 { - return (0, errno::errno().0); + return (0, nix::errno::errno()); } (ret as u64, FUSE_OK) @@ -849,11 +849,11 @@ pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { pub fn set_rlimit(rlim_cur: u64, rlim_max: u64) -> i32 { let limit = libc::rlimit { rlim_cur, rlim_max }; - errno::set_errno(errno::Errno(0)); + nix::errno::Errno::clear(); let ret = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &limit) }; if ret == -1 { - return errno::errno().0; + return nix::errno::errno(); } FUSE_OK -- Gitee From a9f7dadaf2ee559ddcca94ab2769805c8268b018 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 1 Aug 2023 19:51:08 +0800 Subject: [PATCH 1258/2187] package: Update package version in Notice Signed-off-by: Keqian Zhu --- license/Third_Party_Open_Source_Software_Notice.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 746f6d334..71a4f7778 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -5,7 +5,7 @@ Warranty Disclaimer THE OPEN SOURCE SOFTWARE IN THIS SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. Copyright Notice and License Texts -Software: libc 0.2.133 +Software: libc 0.2.146 Copyright notice: Copyright (c) 2014-2020 The Rust Project Developers License: MIT or Apache License Version 2.0 @@ -282,7 +282,7 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to -Software: serde 1.0.145 +Software: serde 1.0.163 Copyright notice: Copyright (c) David Tolnay Copyright (c) Erick Tryzelaar @@ -296,7 +296,7 @@ Copyright (c) Erick Tryzelaar License: MIT or Apache License Version 2.0 Please see above. -Software: anyhow 1.0 +Software: anyhow 1.0.71 Copyright notice: Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 @@ -321,7 +321,7 @@ Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: Apache License Version 2.0 or BSD 3-Clause Please see above. -Software: libusb1_sys 0.6.4 +Software: libusb1-sys 0.6.4 Copyright notice: Copyright (c) 2015 David Cuddeback License: MIT @@ -356,7 +356,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Software: kvm-ioctls kvm-ioctls +Software: kvm-ioctls 0.13.0 Copyright notice: Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. Portions Copyright 2017 The Chromium OS Authors. All rights reserved. @@ -414,7 +414,7 @@ Copyright (c) 2019 Intel Corporation. All Rights Reserved. License: Apache License Version 2.0 or BSD 3-Clause License Please see above. -Software: once_cell 1.17.2 +Software: once_cell 1.18.0 Copyright notice: Copyright (c) Aleksey Kladov License: MIT OR Apache-2.0 -- Gitee From 59ab4880c6d4fc0f72efe2ce043e6493acc21b58 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 29 Jul 2023 11:27:58 +0800 Subject: [PATCH 1259/2187] usb-host: optimized hot plug/unplug performance 1. Use List instead of LinkedList to O(1) delete elements from the linked list. 2. Unnecessary invokking of libusb_reset_device is deleted because libusb_reset_device is time-consuming. 3. The usbhost release process is placed in the drop to prevent libusb_reset_device from blocking the main thread. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/host_usblib.rs | 56 ++++++----- devices/src/usb/usbhost/mod.rs | 130 ++++++++++--------------- util/src/lib.rs | 2 +- 3 files changed, 83 insertions(+), 105 deletions(-) diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 18ed414b0..83507513b 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -33,7 +33,10 @@ use vmm_sys_util::epoll::EventSet; use super::{IsoTransfer, UsbHost, UsbHostRequest}; use crate::usb::{UsbPacketStatus, USB_TOKEN_IN}; -use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; +use util::{ + link_list::Node, + loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, +}; const CONTROL_TIMEOUT: u32 = 10000; // 10s const BULK_TIMEOUT: u32 = 0; @@ -67,10 +70,10 @@ macro_rules! try_unsafe { }; } -pub fn get_request_from_transfer(transfer: *mut libusb_transfer) -> Arc> { +pub fn get_node_from_transfer(transfer: *mut libusb_transfer) -> Box> { // SAFETY: cast the raw pointer of transfer's user_data to the - // Arc>. - unsafe { Arc::from_raw((*transfer).user_data.cast::>()) } + // Box>. + unsafe { Box::from_raw((*transfer).user_data.cast::>()) } } pub fn get_iso_transfer_from_transfer(transfer: *mut libusb_transfer) -> Arc> { @@ -194,15 +197,23 @@ pub fn alloc_host_transfer(iso_packets: c_int) -> *mut libusb_transfer { extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { // SAFETY: transfer is still valid because libusb just completed it // but we haven't told anyone yet. user_data remains valid because - // it is dropped only when the request is completed and removed from - // requests linked list. - let request = get_request_from_transfer(host_transfer); - let mut locked_request = request.lock().unwrap(); - let packet = locked_request.packet.clone(); + // it is dropped only when the request is completed and removed here. + let mut node = get_node_from_transfer(host_transfer); + let request = &mut node.value; + let requests = match request.requests.upgrade() { + Some(requests) => requests, + None => return, + }; + + // Before operating a node, lock requests to prevent multiple threads from operating + // the node at the same time. + let mut locked_requests = requests.lock().unwrap(); + let packet = request.packet.clone(); let mut locked_packet = packet.lock().unwrap(); if !locked_packet.is_async { - locked_request.complete(); + request.free(); + locked_requests.unlink(&node); return; } @@ -210,8 +221,8 @@ extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { let transfer_status = get_status_from_transfer(host_transfer); locked_packet.status = map_packet_status(transfer_status); - if locked_request.is_control { - locked_request.ctrl_transfer_packet(&mut locked_packet, actual_length as usize); + if request.is_control { + request.ctrl_transfer_packet(&mut locked_packet, actual_length as usize); } else if locked_packet.pid as u8 == USB_TOKEN_IN && actual_length != 0 { let data = get_buffer_from_transfer(host_transfer, actual_length as usize); locked_packet.transfer_packet(data, actual_length as usize); @@ -224,7 +235,8 @@ extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { } } - locked_request.complete(); + request.free(); + locked_requests.unlink(&node); } extern "system" fn req_complete_iso(host_transfer: *mut libusb_transfer) { @@ -248,17 +260,13 @@ pub fn fill_transfer_by_type( transfer: *mut libusb_transfer, handle: Option<&mut DeviceHandle>, ep_number: u8, - request: Arc>, + node: *mut Node, transfer_type: TransferType, ) { - let packet = request.lock().unwrap().packet.clone(); + // SAFETY: node only deleted when request completed. + let packet = unsafe { (*node).value.packet.clone() }; + let buffer_ptr = unsafe { (*node).value.buffer.as_mut_ptr() }; let size = packet.lock().unwrap().get_iovecs_size(); - let buffer_ptr = request.lock().unwrap().buffer.as_mut_ptr(); - - if handle.is_none() { - error!("Failed to fill bulk transfer, handle is none"); - return; - } if transfer.is_null() { error!("Failed to fill bulk transfer, transfer is none"); @@ -274,7 +282,7 @@ pub fn fill_transfer_by_type( handle.unwrap().as_raw(), buffer_ptr, req_complete, - (Arc::into_raw(request) as *mut Mutex).cast::(), + node.cast::(), CONTROL_TIMEOUT, ); }, @@ -286,7 +294,7 @@ pub fn fill_transfer_by_type( buffer_ptr, size as i32, req_complete, - (Arc::into_raw(request) as *mut Mutex).cast::(), + node.cast::(), BULK_TIMEOUT, ); }, @@ -298,7 +306,7 @@ pub fn fill_transfer_by_type( buffer_ptr, size as i32, req_complete, - (Arc::into_raw(request) as *mut Mutex).cast::(), + node.cast::(), INTERRUPT_TIMEOUT, ); }, diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index d706993ba..01461e286 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -49,13 +49,13 @@ use machine_manager::{ }; use util::{ byte_code::ByteCode, + link_list::{List, Node}, loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}, }; mod host_usblib; const NON_ISO_PACKETS_NUMS: c_int = 0; -const COMPLETE_LIMIT: u32 = 200; const HANDLE_TIMEOUT_MS: u64 = 2; const USB_HOST_BUFFER_LEN: usize = 12 * 1024; @@ -66,26 +66,26 @@ struct InterfaceStatus { } pub struct UsbHostRequest { + pub requests: Weak>>, pub packet: Arc>, pub host_transfer: *mut libusb_transfer, /// Async data buffer. pub buffer: Vec, - pub completed: Arc>, pub is_control: bool, } impl UsbHostRequest { pub fn new( + requests: Weak>>, packet: Arc>, host_transfer: *mut libusb_transfer, - completed: Arc>, is_control: bool, ) -> Self { Self { + requests, packet, host_transfer, buffer: Vec::new(), - completed, is_control, } } @@ -107,20 +107,19 @@ impl UsbHostRequest { } } - pub fn complete(&mut self) { + pub fn free(&mut self) { free_host_transfer(self.host_transfer); self.buffer.clear(); self.host_transfer = std::ptr::null_mut(); - let mut completed = self.completed.lock().unwrap(); - *completed += 1; } - pub fn abort_req(&mut self) -> Result<()> { + pub fn abort_req(&mut self) { let mut locked_packet = self.packet.lock().unwrap(); if locked_packet.is_async { locked_packet.status = UsbPacketStatus::NoDev; locked_packet.is_async = false; - cancel_host_transfer(self.host_transfer)?; + cancel_host_transfer(self.host_transfer) + .unwrap_or_else(|e| warn!("usb-host cancel host transfer is error: {:?}", e)); if let Some(transfer) = locked_packet.xfer_ops.as_ref() { if let Some(ops) = transfer.clone().upgrade() { @@ -132,8 +131,6 @@ impl UsbHostRequest { } } } - - Ok(()) } pub fn ctrl_transfer_packet(&self, packet: &mut UsbPacket, actual_length: usize) { @@ -353,14 +350,18 @@ pub struct UsbHost { /// Callback for release dev to Host after the vm exited. exit: Option>, /// All pending asynchronous usb request. - requests: Arc>>>>, - completed: Arc>, + requests: Arc>>, /// ISO queues corresponding to all endpoints. iso_queues: Arc>>>>, iso_urb_frames: u32, iso_urb_count: u32, } +// SAFETY: Send and Sync is not auto-implemented for util::link_list::List. +// Implementing them is safe because List add Mutex. +unsafe impl Sync for UsbHost {} +unsafe impl Send for UsbHost {} + impl UsbHost { pub fn new(config: UsbHostConfig) -> Result { let mut context = Context::new()?; @@ -379,8 +380,7 @@ impl UsbHost { ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], usb_device: UsbDevice::new(id, USB_HOST_BUFFER_LEN), exit: None, - requests: Arc::new(Mutex::new(LinkedList::new())), - completed: Arc::new(Mutex::new(0)), + requests: Arc::new(Mutex::new(List::new())), iso_queues: Arc::new(Mutex::new(LinkedList::new())), iso_urb_frames, iso_urb_count, @@ -727,42 +727,6 @@ impl UsbHost { self.attach_kernel(); } - pub fn clear_succ_requests(&mut self) { - let mut locked_request = self.requests.lock().unwrap(); - let mut completed = self.completed.lock().unwrap(); - - while !locked_request.is_empty() { - let request = locked_request.front(); - if let Some(request) = request { - if request.lock().unwrap().host_transfer.is_null() { - locked_request.pop_front(); - *completed -= 1; - } else { - break; - } - } else { - break; - } - } - - if *completed > COMPLETE_LIMIT { - let mut updated_requests: LinkedList>> = LinkedList::new(); - loop { - let request = locked_request.pop_front(); - if let Some(request) = request { - if request.lock().unwrap().host_transfer.is_null() { - continue; - } - updated_requests.push_back(request); - } else { - break; - } - } - *locked_request = updated_requests; - } - *completed = 0; - } - fn clear_iso_queues(&mut self) { let mut locked_iso_queues = self.iso_queues.lock().unwrap(); for queue in locked_iso_queues.iter() { @@ -773,20 +737,24 @@ impl UsbHost { } pub fn abort_host_transfers(&mut self) -> Result<()> { - // Max counts of uncompleted request to be handled. - let mut limit = 100; - for request in self.requests.lock().unwrap().iter_mut() { - request.lock().unwrap().abort_req()?; + let mut locked_requests = self.requests.lock().unwrap(); + for _i in 0..locked_requests.len { + let mut node = locked_requests.pop_head().unwrap(); + node.value.abort_req(); + locked_requests.add_tail(node); } + drop(locked_requests); + // Max counts of uncompleted request to be handled. + let mut limit = 100; loop { - if self.requests.lock().unwrap().is_empty() { + if self.requests.lock().unwrap().len == 0 { return Ok(()); } let timeout = Some(Duration::from_millis(HANDLE_TIMEOUT_MS)); self.context.handle_events(timeout)?; if limit == 0 { - self.requests.lock().unwrap().clear(); + self.requests = Arc::new(Mutex::new(List::new())); return Ok(()); } limit -= 1; @@ -910,7 +878,12 @@ impl UsbHost { }; packet.lock().unwrap().is_async = true; - self.clear_succ_requests(); + } +} + +impl Drop for UsbHost { + fn drop(&mut self) { + self.release_dev_to_host(); } } @@ -957,7 +930,6 @@ impl UsbDeviceOps for UsbHost { fn unrealize(&mut self) -> Result<()> { TempCleaner::remove_exit_notifier(self.device_id()); - self.release_dev_to_host(); unregister_event_helper(None, &mut self.libevt)?; info!("Usb Host device {} is unrealized", self.device_id()); Ok(()) @@ -972,12 +944,6 @@ impl UsbDeviceOps for UsbHost { .unwrap_or_else(|e| error!("Failed to abort all libusb transfers: {:?}", e)); self.clear_iso_queues(); - - self.handle - .as_mut() - .unwrap() - .reset() - .unwrap_or_else(|e| error!("Failed to reset device handle:{:?}", e)); } fn set_controller(&mut self, _cntlr: std::sync::Weak>) {} @@ -1023,25 +989,27 @@ impl UsbDeviceOps for UsbHost { drop(locked_packet); let host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); - let request = Arc::new(Mutex::new(UsbHostRequest::new( + let mut node = Box::new(Node::new(UsbHostRequest::new( + Arc::downgrade(&self.requests), packet.clone(), host_transfer, - self.completed.clone(), true, ))); - request.lock().unwrap().setup_ctrl_buffer( + node.value.setup_ctrl_buffer( &self.usb_device.data_buf[..device_req.length as usize], device_req, ); - self.requests.lock().unwrap().push_back(request.clone()); + fill_transfer_by_type( host_transfer, self.handle.as_mut(), 0, - request, + &mut (*node) as *mut Node, TransferType::Control, ); + self.requests.lock().unwrap().add_tail(node); + self.submit_host_transfer(host_transfer, packet); } @@ -1074,14 +1042,14 @@ impl UsbDeviceOps for UsbHost { { USB_ENDPOINT_ATTR_BULK => { host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); - let request = Arc::new(Mutex::new(UsbHostRequest::new( + let mut node = Box::new(Node::new(UsbHostRequest::new( + Arc::downgrade(&self.requests), cloned_packet, host_transfer, - self.completed.clone(), false, ))); - request.lock().unwrap().setup_data_buffer(); - self.requests.lock().unwrap().push_back(request.clone()); + node.value.setup_data_buffer(); + if packet.lock().unwrap().pid as u8 != USB_TOKEN_OUT { ep_number |= USB_DIRECTION_DEVICE_TO_HOST; } @@ -1089,20 +1057,21 @@ impl UsbDeviceOps for UsbHost { host_transfer, self.handle.as_mut(), ep_number, - request, + &mut (*node) as *mut Node, TransferType::Bulk, ); + self.requests.lock().unwrap().add_tail(node); } USB_ENDPOINT_ATTR_INT => { host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); - let request = Arc::new(Mutex::new(UsbHostRequest::new( + let mut node = Box::new(Node::new(UsbHostRequest::new( + Arc::downgrade(&self.requests), cloned_packet, host_transfer, - self.completed.clone(), false, ))); - request.lock().unwrap().setup_data_buffer(); - self.requests.lock().unwrap().push_back(request.clone()); + node.value.setup_data_buffer(); + if packet.lock().unwrap().pid as u8 != USB_TOKEN_OUT { ep_number |= USB_DIRECTION_DEVICE_TO_HOST; } @@ -1110,9 +1079,10 @@ impl UsbDeviceOps for UsbHost { host_transfer, self.handle.as_mut(), ep_number, - request, + &mut (*node) as *mut Node, TransferType::Interrupt, ); + self.requests.lock().unwrap().add_tail(node); } USB_ENDPOINT_ATTR_ISOC => { if packet.lock().unwrap().pid as u8 == USB_TOKEN_IN { diff --git a/util/src/lib.rs b/util/src/lib.rs index 8e34aa4d6..4d637a171 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -23,7 +23,7 @@ pub mod edid; pub mod error; pub mod file; pub mod leak_bucket; -mod link_list; +pub mod link_list; pub mod logger; pub mod loop_context; pub mod num_ops; -- Gitee From 1620c1353c7df54115b7160dfa979f4d3f5cfcaa Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Wed, 2 Aug 2023 11:55:50 +0800 Subject: [PATCH 1260/2187] devices: Remove redundant pub declarations Signed-off-by: jiewangqun --- devices/src/acpi/power.rs | 8 ++++---- devices/src/camera_backend/v4l2.rs | 4 ++-- devices/src/legacy/chardev.rs | 10 +++++----- devices/src/legacy/pl011.rs | 2 +- devices/src/legacy/pl031.rs | 2 +- devices/src/legacy/serial.rs | 2 +- devices/src/usb/mod.rs | 2 +- pci/src/demo_device/kbd_pointer_device.rs | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index d07aa69ac..ed45fe7ca 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -65,7 +65,7 @@ const BAT_SYSFS_DIR: &str = "/sys/class/power_supply/Battery"; #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct PowerDevState { +struct PowerDevState { last_acad_st: u32, last_bat_st: u32, last_bat_lvl: u32, @@ -135,7 +135,7 @@ impl PowerDev { Ok(()) } - pub fn power_status_read(&mut self) -> Result<()> { + fn power_status_read(&mut self) -> Result<()> { let acad_props = vec!["online"]; let bat_sysfs_props = vec!["online", "current_now", "energy_now", "voltage_now"]; let mut props: Vec = vec![0; bat_sysfs_props.len()]; @@ -159,7 +159,7 @@ impl PowerDev { Ok(()) } - pub fn power_load_static_status(&mut self) { + fn power_load_static_status(&mut self) { info!("Load static power devices status"); self.regs[REG_IDX_ACAD_ON] = 1; self.regs[REG_IDX_BAT_DCAP] = 0xffffffff; @@ -384,7 +384,7 @@ impl AmlBuilder for PowerDev { } } -pub fn power_status_update(dev: &Arc>) { +fn power_status_update(dev: &Arc>) { let cdev = dev.clone(); let update_func = Box::new(move || { power_status_update(&cdev); diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 1335cf139..dd392b9f0 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -453,7 +453,7 @@ fn cam_fmt_from_v4l2(t: u32) -> Result { Ok(fmt) } -pub struct V4l2IoHandler { +struct V4l2IoHandler { sample: Arc>, backend: Arc, notify_cb: Option, @@ -461,7 +461,7 @@ pub struct V4l2IoHandler { } impl V4l2IoHandler { - pub fn new( + fn new( sample: &Arc>, backend: &Arc, cb: Option, diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index 7a8f1d6ac..a198113aa 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -56,17 +56,17 @@ type ReceFn = Option>; /// Character device structure. pub struct Chardev { /// Id of chardev. - pub id: String, + id: String, /// Type of backend device. - pub backend: ChardevType, + backend: ChardevType, /// UnixListener for socket-type chardev. - pub listener: Option, + listener: Option, /// Chardev input. - pub input: Option>>, + input: Option>>, /// Chardev output. pub output: Option>>, /// Fd of socket stream. - pub stream_fd: Option, + stream_fd: Option, /// Device is deactivated or not. pub deactivated: bool, /// Handle the input data and trigger interrupt if necessary. diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 07345f331..6f4e964b0 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -60,7 +60,7 @@ const PL011_FIFO_SIZE: usize = 16; #[repr(C)] #[derive(Clone, Copy, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct PL011State { +struct PL011State { /// Read FIFO. PL011_FIFO_SIZE is 16. rfifo: [u32; 16], /// Flag Register. diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 5603b1222..b61025a9c 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -54,7 +54,7 @@ const RTC_PERIPHERAL_ID: [u8; 8] = [0x31, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0x #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct PL031State { +struct PL031State { /// Match register value. mr: u32, /// Load register value. diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 82d4593ae..4d34515be 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -68,7 +68,7 @@ const RECEIVER_BUFF_SIZE: usize = 1024; #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct SerialState { +struct SerialState { /// Receiver buffer state. rbr_value: [u8; 1024], /// Length of rbr. diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index b0c2ee80f..4c32fe6b1 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -98,7 +98,7 @@ impl UsbEndpoint { } } - fn set_max_packet_size(&mut self, raw: u16) { + pub fn set_max_packet_size(&mut self, raw: u16) { let size = raw & 0x7ff; let micro_frames: u32 = match (raw >> 11) & 3 { 1 => 2, diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/pci/src/demo_device/kbd_pointer_device.rs index 6637f2529..2ab772cee 100644 --- a/pci/src/demo_device/kbd_pointer_device.rs +++ b/pci/src/demo_device/kbd_pointer_device.rs @@ -34,7 +34,7 @@ pub struct MemSpace { } impl MemSpace { - pub fn send_kbdmouse_message(&mut self, msg: &PointerMessage) -> Result<()> { + fn send_kbdmouse_message(&mut self, msg: &PointerMessage) -> Result<()> { let sys_mem = match &self.sys_mem { Some(m) => m, None => { -- Gitee From 27a32bcddebd5d54c6ab29f8105f21109b54f984 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 1 Aug 2023 16:37:06 +0800 Subject: [PATCH 1261/2187] acpi/devices/machine_manager: Remove redundant code remove dead code that pub struct/enum data not be used. Signed-off-by: jiewangqun --- acpi/src/aml_compiler.rs | 56 +++---------------------------- devices/src/camera_backend/mod.rs | 37 -------------------- machine_manager/src/machine.rs | 8 ----- 3 files changed, 5 insertions(+), 96 deletions(-) diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index a366941ea..b5e49c100 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -1186,49 +1186,6 @@ impl AmlBuilder for AmlRelease { } } -/// Create arbitrary-length field of Buffer. -pub struct AmlCreateField { - /// The name of this field. - name: String, - /// The source Buffer, which has been converted to bytes. - src: Vec, - /// the start index in the Buffer, which has been converted to bytes. - /// `bit_index` has to be an Integer. - bit_index: Vec, - /// the length of this bit range, which has been converted to bytes. - /// `bit_count` has to be an Integer and must not be zero. - /// Note that the bit range (bit_index, bit_index + bit_count) must not exceed the bound of Buffer. - bit_count: Vec, -} - -impl AmlCreateField { - pub fn new( - src: S, - bit_index: T, - bit_count: C, - name: &str, - ) -> AmlCreateField { - AmlCreateField { - name: name.to_string(), - src: src.aml_bytes(), - bit_index: bit_index.aml_bytes(), - bit_count: bit_count.aml_bytes(), - } - } -} - -impl AmlBuilder for AmlCreateField { - fn aml_bytes(&self) -> Vec { - let mut bytes = vec![0x5B, 0x13]; - bytes.extend(self.src.clone()); - bytes.extend(self.bit_index.clone()); - bytes.extend(self.bit_count.clone()); - bytes.extend(build_name_string(self.name.as_ref())); - - bytes - } -} - /// Macro helps to define CreateWordField/CreateDWordField/CreateQWordField. macro_rules! create_word_field_define { ($name: ident, $op: expr) => { @@ -2233,12 +2190,10 @@ mod test { let size = AmlCreateDWordField::new(AmlArg(0), AmlInteger(4), "SIZE"); let minv = AmlCreateWordField::new(AmlArg(0), AmlInteger(8), "MINV"); let maxv = AmlCreateQWordField::new(AmlArg(0), AmlInteger(10), "MAXV"); - let temp = AmlCreateField::new(AmlArg(0), AmlInteger(64), AmlInteger(8), "TEMP"); method.append_child(revs); method.append_child(size); method.append_child(minv); method.append_child(maxv); - method.append_child(temp); let store = AmlOr::new( AmlName("MINV".to_string()), @@ -2256,12 +2211,11 @@ mod test { method.append_child(AmlReturn::with_value(AmlLocal(0))); let method_bytes = vec![ - 0x14, 0x4A, 0x04, 0x4D, 0x54, 0x44, 0x31, 0x01, 0x8A, 0x68, 0x00, 0x52, 0x45, 0x56, - 0x53, 0x8A, 0x68, 0x0A, 0x04, 0x53, 0x49, 0x5A, 0x45, 0x8B, 0x68, 0x0A, 0x08, 0x4D, - 0x49, 0x4E, 0x56, 0x8F, 0x68, 0x0A, 0x0A, 0x4D, 0x41, 0x58, 0x56, 0x5B, 0x13, 0x68, - 0x0A, 0x40, 0x0A, 0x08, 0x54, 0x45, 0x4D, 0x50, 0x7D, 0x4D, 0x49, 0x4E, 0x56, 0x4D, - 0x41, 0x58, 0x56, 0x54, 0x45, 0x4D, 0x50, 0x73, 0x52, 0x45, 0x56, 0x53, 0x53, 0x49, - 0x5A, 0x45, 0x60, 0xA4, 0x60, + 0x14, 0x3E, 0x4D, 0x54, 0x44, 0x31, 0x01, 0x8A, 0x68, 0x00, 0x52, 0x45, 0x56, 0x53, + 0x8A, 0x68, 0x0A, 0x04, 0x53, 0x49, 0x5A, 0x45, 0x8B, 0x68, 0x0A, 0x08, 0x4D, 0x49, + 0x4E, 0x56, 0x8F, 0x68, 0x0A, 0x0A, 0x4D, 0x41, 0x58, 0x56, 0x7D, 0x4D, 0x49, 0x4E, + 0x56, 0x4D, 0x41, 0x58, 0x56, 0x54, 0x45, 0x4D, 0x50, 0x73, 0x52, 0x45, 0x56, 0x53, + 0x53, 0x49, 0x5A, 0x45, 0x60, 0xA4, 0x60, ]; assert_eq!(method.aml_bytes(), method_bytes); } diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 61cf32c9b..a65cf4f3d 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -27,25 +27,6 @@ use self::{demo::DemoCamera, v4l2::V4l2CameraBackend}; /// Frame interval in 100ns units. pub const INTERVALS_PER_SEC: u32 = 10_000_000; -#[allow(dead_code)] -#[derive(Default, Clone)] -pub struct CamFmt { - // Basic 3 configurations: frame size, format, frame frequency. - basic_fmt: CamBasicFmt, - // Processing Unit Configuration: brightness, hue, etc. - pu_fmt: CamPUFmt, - // Camera Terminal Configuration: focus, exposure time, iris, etc. - lens_fmt: CamLensFmt, -} - -impl CamFmt { - pub fn new() -> Self { - Self { - ..Default::default() - } - } -} - #[derive(Clone, Copy, Default, Debug)] pub struct CamBasicFmt { pub width: u32, @@ -63,24 +44,6 @@ impl CamBasicFmt { } } -#[allow(dead_code)] -#[derive(Default, Clone)] -pub struct CamPUFmt { - bright: u64, - contrast: u64, - hue: u64, - saturatio: u64, - // TODO: to be extended. -} - -#[allow(dead_code)] -#[derive(Default, Clone)] -pub struct CamLensFmt { - focus: u64, - zoom: u64, - // TODO: to be extended. -} - #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Default)] pub enum FmtType { #[default] diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 39cda6448..2541e0f8b 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -43,14 +43,6 @@ pub enum KvmVmState { Shutdown = 6, } -/// Event over StratoVirt lifetime. -pub enum VmEvent { - ShutdownCauseGuestReset, - ShutdownCauseGuestCrash, - ShutdownCauseFailEntry, - ShutdownCauseInternalError, -} - /// Trait to handle virtual machine lifecycle. /// /// # Notes -- Gitee From 2242639bcffe200ef094570ab4fe44fe476b653c Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 1 Aug 2023 16:37:06 +0800 Subject: [PATCH 1262/2187] acpi/address_space/boot_loader/CPU: Remove redundant pub declarations Signed-off-by: jiewangqun --- acpi/src/acpi_device.rs | 2 +- acpi/src/acpi_table.rs | 18 +++++++++--------- address_space/src/address_space.rs | 2 +- address_space/src/host_mmap.rs | 7 ++----- boot_loader/src/x86_64/direct_boot/gdt.rs | 4 ++-- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index 9c6c2674f..966e6472e 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -22,7 +22,7 @@ use util::{ // Frequency of PM Timer in HZ. const PM_TIMER_FREQUENCY: u128 = 3_579_545; -pub const ACPI_BITMASK_SLEEP_ENABLE: u16 = 0x2000; +const ACPI_BITMASK_SLEEP_ENABLE: u16 = 0x2000; /// ACPI Power Management Timer #[allow(clippy::upper_case_acronyms)] diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index 41ea28fb7..d91ee3119 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -72,23 +72,23 @@ impl AmlBuilder for AcpiGenericAddress { #[derive(Default, Copy, Clone)] pub struct AcpiTableHeader { /// Signature of this table. - pub signature: [u8; 4], + signature: [u8; 4], /// The total length of this table, including this header. - pub length: u32, + length: u32, /// The revision of this table. - pub revision: u8, + revision: u8, /// The checksum of this table, including this header. - pub checksum: u8, + checksum: u8, /// OEM ID. - pub oem_id: [u8; 6], + oem_id: [u8; 6], /// OEM table ID. - pub oem_table_id: [u8; 8], + oem_table_id: [u8; 8], /// OEM revision of this table. - pub oem_revision: u32, + oem_revision: u32, /// Vendor ID for the ASL Compiler, default zero. - pub asl_compiler_id: [u8; 4], + asl_compiler_id: [u8; 4], /// Revision number of the ASL Compiler, default zero. - pub asl_compiler_revision: u32, + asl_compiler_revision: u32, } impl ByteCode for AcpiTableHeader {} diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index d39b2e2a8..49e8117a5 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -29,7 +29,7 @@ use crate::{ /// Contains an array of `FlatRange`. #[derive(Default, Clone, Debug)] -pub(crate) struct FlatView(pub Vec); +pub(crate) struct FlatView(pub(crate) Vec); impl FlatView { fn find_flatrange(&self, addr: GuestAddress) -> Option<&FlatRange> { diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 6ad148fae..00fde9d59 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -204,7 +204,7 @@ fn touch_pages(start: u64, page_size: u64, nr_pages: u64) { /// * `host_addr` - The start host address to pre allocate. /// * `size` - Size of memory. /// * `nr_vcpus` - Number of vcpus. -pub fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { +fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { let page_size = host_page_size(); let threads = max_nr_threads(nr_vcpus); let nr_pages = (size + page_size - 1) / page_size; @@ -342,10 +342,7 @@ pub fn create_backend_mem(mem_config: &MemZoneConfig, thread_num: u8) -> Result< /// /// * `mem_mappings` - The host virtual address of mapped memory information. /// * `zone` - Memory zone config info. -pub fn set_host_memory_policy( - mem_mappings: &Arc, - zone: &MemZoneConfig, -) -> Result<()> { +fn set_host_memory_policy(mem_mappings: &Arc, zone: &MemZoneConfig) -> Result<()> { if zone.host_numa_nodes.is_none() { return Ok(()); } diff --git a/boot_loader/src/x86_64/direct_boot/gdt.rs b/boot_loader/src/x86_64/direct_boot/gdt.rs index b229947ea..c1ea257ed 100644 --- a/boot_loader/src/x86_64/direct_boot/gdt.rs +++ b/boot_loader/src/x86_64/direct_boot/gdt.rs @@ -30,10 +30,10 @@ use kvm_bindings::kvm_segment; // (((base) & _AC(0x00ffffff,ULL)) << 16) | \ // (((limit) & _AC(0x0000ffff,ULL)))) // -pub struct GdtEntry(pub u64); +struct GdtEntry(pub u64); impl GdtEntry { - pub fn new(flags: u64, base: u64, limit: u64) -> Self { + fn new(flags: u64, base: u64, limit: u64) -> Self { let base = (base & 0xff00_0000) << (56 - 24) | (base & 0x00ff_ffff) << 16; let limit = (limit & 0x000f_0000) << (48 - 16) | (limit & 0x0000_ffff); let flags = (flags & 0x0000_f0ff) << 40; -- Gitee From 8f8f9d3f2a9db4251f42a3bc1655182fffe2cbdd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 2 Aug 2023 15:43:05 +0800 Subject: [PATCH 1263/2187] building: Remove unnecessary mut tag Signed-off-by: Keqian Zhu --- block_backend/src/qcow2/cache.rs | 2 +- devices/src/legacy/fwcfg.rs | 2 +- devices/src/legacy/pflash.rs | 2 +- devices/src/usb/descriptor.rs | 2 +- devices/src/usb/tablet.rs | 2 +- devices/src/usb/xhci/xhci_controller.rs | 4 ++-- hypervisor/src/kvm/mod.rs | 2 +- machine/src/standard_vm/aarch64/mod.rs | 3 +-- vfio/src/vfio_pci.rs | 2 +- virtio/src/device/gpu.rs | 2 +- 10 files changed, 11 insertions(+), 12 deletions(-) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 3971b9176..e2ab01e48 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -116,7 +116,7 @@ impl CacheTable { _ => bail!("Unsupported entry size {}", self.entry_size), } - let mut dirty_info = &mut self.dirty_info; + let dirty_info = &mut self.dirty_info; dirty_info.start = std::cmp::min(dirty_info.start, start as u64); dirty_info.end = std::cmp::max(dirty_info.end, end as u64); dirty_info.is_dirty = true; diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 432128528..a323ef70c 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -972,7 +972,7 @@ impl SysBusDevOps for FwCfgMem { region_base: u64, region_size: u64, ) -> sysbus::Result<()> { - let mut res = self.get_sys_resource().unwrap(); + let res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 51281f398..94b9de87c 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -832,7 +832,7 @@ impl SysBusDevOps for PFlash { region_base: u64, region_size: u64, ) -> sysbus::Result<()> { - let mut res = self.get_sys_resource().unwrap(); + let res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; res.irq = 0; diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 26003e558..95253fdc0 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -555,7 +555,7 @@ impl UsbDescriptorOps for UsbDevice { == USB_DIRECTION_DEVICE_TO_HOST; let ep = iface.endpoints[e as usize].endpoint_desc.bEndpointAddress & USB_ENDPOINT_ADDRESS_NUMBER_MASK; - let mut usb_ep = self.get_mut_endpoint(in_direction, ep); + let usb_ep = self.get_mut_endpoint(in_direction, ep); usb_ep.ep_type = iface.endpoints[e as usize].endpoint_desc.bmAttributes & USB_ENDPOINT_ATTR_TRANSFER_TYPE_MASK; } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 97a58bdb8..2650a0ed9 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -142,7 +142,7 @@ impl PointerOpts for UsbTabletAdapter { return Ok(()); } let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; - let mut evt = &mut locked_tablet.hid.pointer.queue[index]; + let evt = &mut locked_tablet.hid.pointer.queue[index]; if button & INPUT_BUTTON_WHEEL_UP == INPUT_BUTTON_WHEEL_UP { evt.v_wheel = 1; } else if button & INPUT_BUTTON_WHEEL_DOWN == INPUT_BUTTON_WHEEL_DOWN { diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b7c82a2dc..eaa5d0e21 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1388,7 +1388,7 @@ impl XhciDevice { ep_ctx.as_mut_dwords(), )?; self.disable_endpoint(slot_id, ep_id)?; - let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; epctx.epid = ep_id; epctx.enabled = true; // It is safe to use plus here becuase we previously verify the address on the outer layer. @@ -1616,7 +1616,7 @@ impl XhciDevice { let mut locked_xfer = xfer.lock().unwrap(); locked_xfer.packet = packet; self.endpoint_do_transfer(&mut locked_xfer)?; - let mut epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if locked_xfer.complete { epctx.update_dequeue(&self.mem_space, None)?; } else { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 55e6a0bd2..8abd3ee2b 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -141,7 +141,7 @@ impl KVMFds { // Safe because data in `routes` is reliable. unsafe { - let mut irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; + let irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; if irq_routing.is_null() { bail!("Failed to alloc irq routing"); } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index ee5d55d5c..a4d6587e6 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -21,7 +21,6 @@ use machine_manager::config::ShutdownAction; #[cfg(not(target_env = "musl"))] use machine_manager::config::UiContext; use machine_manager::event_loop::EventLoop; -use std::borrow::Borrow; use std::collections::HashMap; use std::mem::size_of; use std::ops::Deref; @@ -212,7 +211,7 @@ impl StdMachine { Ok(StdMachine { cpu_topo, cpus: Vec::new(), - cpu_features: vm_config.machine_config.cpu_config.borrow().into(), + cpu_features: (&vm_config.machine_config.cpu_config).into(), irq_chip: None, sys_mem: sys_mem.clone(), sysbus, diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 428a323f1..cbef6e1f8 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -509,7 +509,7 @@ impl VfioPciDevice { }; let mut locked_gsi_routes = cloned_gsi_routes.lock().unwrap(); - let mut gsi_route = locked_gsi_routes.get_mut(vector as usize).unwrap(); + let gsi_route = locked_gsi_routes.get_mut(vector as usize).unwrap(); if gsi_route.irq_fd.is_none() { let irq_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); gsi_route.irq_fd = Some(Arc::new(irq_fd)); diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 710a32845..52870c8ff 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -589,7 +589,7 @@ impl GpuIoHandler { resp: &mut T, ) -> Result<()> { if (req.header.flags & VIRTIO_GPU_FLAG_FENCE) != 0 { - let mut header = resp.mut_ctrl_hdr(); + let header = resp.mut_ctrl_hdr(); header.flags |= VIRTIO_GPU_FLAG_FENCE; header.fence_id = req.header.fence_id; header.ctx_id = req.header.ctx_id; -- Gitee From a79bcb81f89f16ebb53437bfd1d18afdc2398f39 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 2 Aug 2023 11:08:11 +0800 Subject: [PATCH 1264/2187] ignore EPERM error for epoll event delete operation When the notifier's raw_fd is released, epoll ctl may fail, so just print these errors on the main thread instead of crashing the main thread. Signed-off-by: yezengruan --- util/src/loop_context.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 5c478d730..50b3110a2 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; use libc::{c_void, read, EFD_NONBLOCK}; -use log::warn; +use log::{error, warn}; use nix::errno::Errno; use nix::{ poll::{ppoll, PollFd, PollFlags}, @@ -339,8 +339,13 @@ impl EventLoopContext { EpollEvent::default(), ) { let error_num = error.raw_os_error().unwrap(); - if error_num != libc::EBADF && error_num != libc::ENOENT { + if error_num != libc::EBADF + && error_num != libc::ENOENT + && error_num != libc::EPERM + { return Err(anyhow!(UtilError::BadSyscall(error))); + } else { + warn!("epoll ctl failed: {}", error); } } } @@ -624,7 +629,9 @@ impl EventLoopContext { } } drop(status_locked); - self.update_events(notifiers)?; + if let Err(e) = self.update_events(notifiers) { + error!("update event failed: {}", e); + } } self.run_timers(); -- Gitee From 2a0aed8d816be85821c8cca2efee89ac4c9cd581 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 3 Aug 2023 10:25:26 +0800 Subject: [PATCH 1265/2187] pci/smbios/ui: Remove redundant pub declarations Signed-off-by: jiewangqun --- pci/src/config.rs | 20 ++++----- pci/src/msix.rs | 2 +- pci/src/root_port.rs | 2 +- smbios/src/smbios_table.rs | 86 ++++++++++++++++++------------------- src/main.rs | 4 +- ui/src/console.rs | 18 ++++---- ui/src/gtk/mod.rs | 6 +-- ui/src/input.rs | 4 +- ui/src/vnc/auth_sasl.rs | 16 +++---- ui/src/vnc/auth_vencrypt.rs | 18 ++++---- ui/src/vnc/client_io.rs | 28 ++++++------ ui/src/vnc/mod.rs | 6 +-- ui/src/vnc/server_io.rs | 2 +- 13 files changed, 106 insertions(+), 106 deletions(-) diff --git a/pci/src/config.rs b/pci/src/config.rs index 8ed5e1e57..276070ae0 100644 --- a/pci/src/config.rs +++ b/pci/src/config.rs @@ -63,8 +63,8 @@ pub const MEMORY_BASE: u8 = 0x20; pub const PREF_MEMORY_BASE: u8 = 0x24; /// Prefetchable memory limit register. pub const PREF_MEMORY_LIMIT: u8 = 0x26; -pub const ROM_ADDRESS_ENDPOINT: usize = 0x30; -pub const ROM_ADDRESS_BRIDGE: usize = 0x38; +const ROM_ADDRESS_ENDPOINT: usize = 0x30; +const ROM_ADDRESS_BRIDGE: usize = 0x38; /// 64-bit prefetchable memory addresses. pub const PREF_MEM_RANGE_64BIT: u8 = 0x01; @@ -102,9 +102,9 @@ pub const STATUS: u8 = 0x06; /// PCI Interrupt Status. pub const STATUS_INTERRUPT: u8 = 0x08; const CACHE_LINE_SIZE: u8 = 0x0c; -pub const PRIMARY_BUS_NUM: u8 = 0x18; -pub const IO_LIMIT: u8 = 0x1d; -pub const PREF_MEM_BASE_UPPER: u8 = 0x28; +const PRIMARY_BUS_NUM: u8 = 0x18; +const IO_LIMIT: u8 = 0x1d; +const PREF_MEM_BASE_UPPER: u8 = 0x28; const CAP_LIST: u8 = 0x34; const INTERRUPT_LINE: u8 = 0x3c; pub const INTERRUPT_PIN: u8 = 0x3d; @@ -142,13 +142,13 @@ pub const BAR_MEM_64BIT: u8 = 0x04; const BAR_PREFETCH: u8 = 0x08; pub const BAR_SPACE_UNMAPPED: u64 = 0xffff_ffff_ffff_ffff; /// The maximum Bar ID numbers of a Type 0 device -pub const BAR_NUM_MAX_FOR_ENDPOINT: u8 = 6; +const BAR_NUM_MAX_FOR_ENDPOINT: u8 = 6; /// The maximum Bar ID numbers of a Type 1 device -pub const BAR_NUM_MAX_FOR_BRIDGE: u8 = 2; +const BAR_NUM_MAX_FOR_BRIDGE: u8 = 2; /// mmio bar's minimum size shall be 4KB pub const MINIMUM_BAR_SIZE_FOR_MMIO: usize = 0x1000; /// pio bar's minimum size shall be 4B -pub const MINIMUM_BAR_SIZE_FOR_PIO: usize = 0x4; +const MINIMUM_BAR_SIZE_FOR_PIO: usize = 0x4; /// PCI Express capability registers, same as kernel defines @@ -246,9 +246,9 @@ pub const PCI_EXP_SLTSTA: u16 = 26; /// Attention Button Pressed pub const PCI_EXP_SLTSTA_ABP: u16 = 0x0001; /// Power Fault Detected -pub const PCI_EXP_SLTSTA_PFD: u16 = 0x0002; +const PCI_EXP_SLTSTA_PFD: u16 = 0x0002; /// MRL Sensor Changed -pub const PCI_EXP_SLTSTA_MRLSC: u16 = 0x0004; +const PCI_EXP_SLTSTA_MRLSC: u16 = 0x0004; /// Presence Detect Changed pub const PCI_EXP_SLTSTA_PDC: u16 = 0x0008; /// Command Completed diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 9978b6f78..2935e8ffb 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -52,7 +52,7 @@ pub const MSIX_CAP_FUNC_MASK: u16 = 0x4000; pub const MSIX_CAP_SIZE: u8 = 12; pub const MSIX_CAP_ID: u8 = 0x11; pub const MSIX_CAP_TABLE: u8 = 0x04; -pub const MSIX_CAP_PBA: u8 = 0x08; +const MSIX_CAP_PBA: u8 = 0x08; /// MSI-X message structure. #[derive(Copy, Clone)] diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 1d5344fa5..5996be629 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -54,7 +54,7 @@ static FAST_UNPLUG_FEATURE: OnceCell = OnceCell::new(); #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct RootPortState { +struct RootPortState { /// Max length of config_space is 4096. config_space: [u8; 4096], write_mask: [u8; 4096], diff --git a/smbios/src/smbios_table.rs b/smbios/src/smbios_table.rs index 1935c8718..3857b46ac 100644 --- a/smbios/src/smbios_table.rs +++ b/smbios/src/smbios_table.rs @@ -41,7 +41,7 @@ struct SmbiosHeader { } impl SmbiosHeader { - pub fn new(type_num: u8, len: u8, handle: u16) -> SmbiosHeader { + fn new(type_num: u8, len: u8, handle: u16) -> SmbiosHeader { SmbiosHeader { type_num, len, @@ -55,8 +55,8 @@ impl SmbiosHeader { #[derive(Default, Copy, Clone)] struct SmbiosType0 { header: SmbiosHeader, - pub vendor_idx: u8, - pub bios_version_idx: u8, + vendor_idx: u8, + bios_version_idx: u8, bios_starting_addr_seg: [u8; 2], bios_release_date_idx: u8, bios_rom_size: u8, @@ -71,7 +71,7 @@ struct SmbiosType0 { impl ByteCode for SmbiosType0 {} impl SmbiosType0 { - pub fn new() -> SmbiosType0 { + fn new() -> SmbiosType0 { SmbiosType0 { header: SmbiosHeader::new(0_u8, size_of::() as u8, TYPE0_HANDLE), bios_starting_addr_seg: 0xE800_u16.to_le_bytes(), @@ -92,7 +92,7 @@ struct SmbiosType0Table { } impl SmbiosType0Table { - pub fn new() -> SmbiosType0Table { + fn new() -> SmbiosType0Table { SmbiosType0Table { header: SmbiosType0::new(), body: Vec::new(), @@ -100,13 +100,13 @@ impl SmbiosType0Table { } } - pub fn set_str(&mut self, str: String) { + fn set_str(&mut self, str: String) { self.str_index += 1; self.body.append(&mut str.as_bytes().to_vec()); self.body.append(&mut vec![0]); } - pub fn finish(&mut self) { + fn finish(&mut self) { if self.str_index == 0 { self.body.append(&mut vec![0; 2]); } else { @@ -120,9 +120,9 @@ impl SmbiosType0Table { #[derive(Default, Copy, Clone)] struct SmbiosType1 { header: SmbiosHeader, - pub manufacturer: u8, - pub product_name: u8, - pub version: u8, + manufacturer: u8, + product_name: u8, + version: u8, serial_num: u8, uuid: [u8; 16], wake_up_type: u8, @@ -133,7 +133,7 @@ struct SmbiosType1 { impl ByteCode for SmbiosType1 {} impl SmbiosType1 { - pub fn new() -> SmbiosType1 { + fn new() -> SmbiosType1 { SmbiosType1 { header: SmbiosHeader::new(1_u8, size_of::() as u8, TYPE1_HANDLE), wake_up_type: 0x6_u8, @@ -150,7 +150,7 @@ struct SmbiosType1Table { } impl SmbiosType1Table { - pub fn new() -> SmbiosType1Table { + fn new() -> SmbiosType1Table { SmbiosType1Table { header: SmbiosType1::new(), body: Vec::new(), @@ -158,13 +158,13 @@ impl SmbiosType1Table { } } - pub fn set_str(&mut self, str: String) { + fn set_str(&mut self, str: String) { self.str_index += 1; self.body.append(&mut str.as_bytes().to_vec()); self.body.append(&mut vec![0]); } - pub fn finish(&mut self) { + fn finish(&mut self) { if self.str_index == 0 { self.body.append(&mut vec![0; 2]); } else { @@ -193,7 +193,7 @@ struct SmbiosType2 { impl ByteCode for SmbiosType2 {} impl SmbiosType2 { - pub fn new() -> SmbiosType2 { + fn new() -> SmbiosType2 { SmbiosType2 { header: SmbiosHeader::new(2_u8, size_of::() as u8, TYPE2_HANDLE), feature_flags: 1_u8, @@ -212,7 +212,7 @@ struct SmbiosType2Table { } impl SmbiosType2Table { - pub fn new() -> SmbiosType2Table { + fn new() -> SmbiosType2Table { SmbiosType2Table { header: SmbiosType2::new(), body: Vec::new(), @@ -220,13 +220,13 @@ impl SmbiosType2Table { } } - pub fn set_str(&mut self, str: String) { + fn set_str(&mut self, str: String) { self.str_index += 1; self.body.append(&mut str.as_bytes().to_vec()); self.body.append(&mut vec![0]); } - pub fn finish(&mut self) { + fn finish(&mut self) { if self.str_index == 0 { self.body.append(&mut vec![0; 2]); } else { @@ -260,7 +260,7 @@ struct SmbiosType3 { impl ByteCode for SmbiosType3 {} impl SmbiosType3 { - pub fn new() -> SmbiosType3 { + fn new() -> SmbiosType3 { SmbiosType3 { header: SmbiosHeader::new(3_u8, size_of::() as u8, TYPE3_HANDLE), type_id: 0x1_u8, @@ -281,7 +281,7 @@ struct SmbiosType3Table { } impl SmbiosType3Table { - pub fn new() -> SmbiosType3Table { + fn new() -> SmbiosType3Table { SmbiosType3Table { header: SmbiosType3::new(), body: Vec::new(), @@ -289,13 +289,13 @@ impl SmbiosType3Table { } } - pub fn set_str(&mut self, str: String) { + fn set_str(&mut self, str: String) { self.str_index += 1; self.body.append(&mut str.as_bytes().to_vec()); self.body.append(&mut vec![0]); } - pub fn finish(&mut self) { + fn finish(&mut self) { if self.str_index == 0 { self.body.append(&mut vec![0; 2]); } else { @@ -341,7 +341,7 @@ struct SmbiosType4 { impl ByteCode for SmbiosType4 {} impl SmbiosType4 { - pub fn new(instance: u16) -> SmbiosType4 { + fn new(instance: u16) -> SmbiosType4 { SmbiosType4 { header: SmbiosHeader::new( 4_u8, @@ -370,7 +370,7 @@ struct SmbiosType4Table { } impl SmbiosType4Table { - pub fn new(instance: u16) -> SmbiosType4Table { + fn new(instance: u16) -> SmbiosType4Table { SmbiosType4Table { header: SmbiosType4::new(instance), body: Vec::new(), @@ -378,13 +378,13 @@ impl SmbiosType4Table { } } - pub fn set_str(&mut self, str: String) { + fn set_str(&mut self, str: String) { self.str_index += 1; self.body.append(&mut str.as_bytes().to_vec()); self.body.append(&mut vec![0]); } - pub fn finish(&mut self) { + fn finish(&mut self) { if self.str_index == 0 { self.body.append(&mut vec![0; 2]); } else { @@ -410,7 +410,7 @@ struct SmbiosType16 { impl ByteCode for SmbiosType16 {} impl SmbiosType16 { - pub fn new(cnt: u16) -> SmbiosType16 { + fn new(cnt: u16) -> SmbiosType16 { SmbiosType16 { header: SmbiosHeader::new(16_u8, size_of::() as u8, TYPE16_HANDLE), location: 0x01, @@ -430,14 +430,14 @@ struct SmbiosType16Table { } impl SmbiosType16Table { - pub fn new(cnt: u16) -> SmbiosType16Table { + fn new(cnt: u16) -> SmbiosType16Table { SmbiosType16Table { header: SmbiosType16::new(cnt), body: Vec::new(), } } - pub fn finish(&mut self) { + fn finish(&mut self) { self.body.append(&mut vec![0; 2]); } } @@ -474,7 +474,7 @@ struct SmbiosType17 { impl ByteCode for SmbiosType17 {} impl SmbiosType17 { - pub fn new(ins: u16) -> SmbiosType17 { + fn new(ins: u16) -> SmbiosType17 { SmbiosType17 { header: SmbiosHeader::new(17_u8, size_of::() as u8, TYPE17_HANDLE + ins), physical_memory_array_handle: 0x1000_u16.to_le_bytes(), @@ -497,7 +497,7 @@ struct SmbiosType17Table { } impl SmbiosType17Table { - pub fn new(ins: u16) -> SmbiosType17Table { + fn new(ins: u16) -> SmbiosType17Table { SmbiosType17Table { header: SmbiosType17::new(ins), body: Vec::new(), @@ -505,13 +505,13 @@ impl SmbiosType17Table { } } - pub fn set_str(&mut self, str: String) { + fn set_str(&mut self, str: String) { self.str_index += 1; self.body.append(&mut str.as_bytes().to_vec()); self.body.append(&mut vec![0]); } - pub fn finish(&mut self) { + fn finish(&mut self) { if self.str_index == 0 { self.body.append(&mut vec![0; 2]); } else { @@ -536,7 +536,7 @@ struct SmbiosType19 { impl ByteCode for SmbiosType19 {} impl SmbiosType19 { - pub fn new(ins: u16) -> SmbiosType19 { + fn new(ins: u16) -> SmbiosType19 { SmbiosType19 { header: SmbiosHeader::new(19_u8, size_of::() as u8, TYPE19_HANDLE + ins), memory_array_handle: 0x1000_u16.to_le_bytes(), @@ -553,14 +553,14 @@ struct SmbiosType19Table { } impl SmbiosType19Table { - pub fn new(ins: u16) -> SmbiosType19Table { + fn new(ins: u16) -> SmbiosType19Table { SmbiosType19Table { header: SmbiosType19::new(ins), body: Vec::new(), } } - pub fn finish(&mut self) { + fn finish(&mut self) { self.body.append(&mut vec![0; 2]); } } @@ -577,7 +577,7 @@ struct SmbiosType32 { impl ByteCode for SmbiosType32 {} impl SmbiosType32 { - pub fn new() -> SmbiosType32 { + fn new() -> SmbiosType32 { SmbiosType32 { header: SmbiosHeader::new(32_u8, size_of::() as u8, TYPE32_HANDLE), ..Default::default() @@ -592,14 +592,14 @@ struct SmbiosType32Table { } impl SmbiosType32Table { - pub fn new() -> SmbiosType32Table { + fn new() -> SmbiosType32Table { SmbiosType32Table { header: SmbiosType32::new(), body: Vec::new(), } } - pub fn finish(&mut self) { + fn finish(&mut self) { self.body.append(&mut vec![0; 2]); } } @@ -612,7 +612,7 @@ struct SmbiosType127 { } impl SmbiosType127 { - pub fn new() -> SmbiosType127 { + fn new() -> SmbiosType127 { SmbiosType127 { header: SmbiosHeader::new(127_u8, size_of::() as u8, TYPE127_HANDLE), } @@ -628,14 +628,14 @@ struct SmbiosType127Table { } impl SmbiosType127Table { - pub fn new() -> SmbiosType127Table { + fn new() -> SmbiosType127Table { SmbiosType127Table { header: SmbiosType127::new(), body: Vec::new(), } } - pub fn finish(&mut self) { + fn finish(&mut self) { self.body.append(&mut vec![0; 2]); } } @@ -1039,7 +1039,7 @@ struct SmbiosEntryPoint30 { impl ByteCode for SmbiosEntryPoint30 {} impl SmbiosEntryPoint30 { - pub fn new(table_len: u32) -> SmbiosEntryPoint30 { + fn new(table_len: u32) -> SmbiosEntryPoint30 { let anchor: [u8; 5] = [b'_', b'S', b'M', b'3', b'_']; SmbiosEntryPoint30 { anchor_str: anchor, diff --git a/src/main.rs b/src/main.rs index 95c85f6d0..73e4077b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ use util::{arg_parser, daemonize::daemonize, logger, set_termi_canon_mode}; use thiserror::Error; #[derive(Error, Debug)] -pub enum MainError { +enum MainError { #[error("Manager")] Manager { #[from] @@ -57,7 +57,7 @@ pub enum MainError { }, } -pub trait ExitCode { +trait ExitCode { /// Returns the value to use as the exit status. fn code(self) -> i32; } diff --git a/ui/src/console.rs b/ui/src/console.rs index c91810664..a976058b3 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -70,9 +70,9 @@ pub enum VmRunningStage { } #[derive(Default)] -pub struct UiInfo { - pub last_width: u32, - pub last_height: u32, +struct UiInfo { + last_width: u32, + last_height: u32, } /// Image data defined in display. @@ -195,7 +195,7 @@ pub struct DisplayConsole { pub con_type: ConsoleType, pub width: i32, pub height: i32, - pub ui_info: UiInfo, + ui_info: UiInfo, pub surface: Option, pub console_list: Weak>, pub dev_opts: Arc, @@ -228,11 +228,11 @@ impl DisplayConsole { } /// The state of console layer. -pub struct DisplayState { +struct DisplayState { /// Running stage. - pub run_stage: VmRunningStage, + run_stage: VmRunningStage, /// Refresh interval, which can be dynamic changed. - pub interval: u64, + interval: u64, /// Whether there is a refresh task. is_refresh: bool, /// A list of DisplayChangeListeners. @@ -339,7 +339,7 @@ pub fn get_run_stage() -> VmRunningStage { } /// Refresh display image. -pub fn display_refresh() { +fn display_refresh() { let mut dcl_interval: u64; let mut interval: u64 = DISPLAY_UPDATE_INTERVAL_MAX; @@ -378,7 +378,7 @@ pub fn display_refresh() { /// Register the timer to execute the scheduled /// refresh task. -pub fn setup_refresh(update_interval: u64) { +fn setup_refresh(update_interval: u64) { let func = Box::new(move || { display_refresh(); }); diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 45a1b65ff..e857255fe 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -121,7 +121,7 @@ impl Default for DisplayEventType { } #[derive(Default)] -pub struct DisplayChangeEvent { +struct DisplayChangeEvent { dev_name: String, event_type: DisplayEventType, x: i32, @@ -141,13 +141,13 @@ impl DisplayChangeEvent { } } -pub struct GtkInterface { +struct GtkInterface { dev_name: String, dce_sender: SyncSender, } impl GtkInterface { - pub fn new(dev_name: String, dce_sender: SyncSender) -> Self { + fn new(dev_name: String, dce_sender: SyncSender) -> Self { Self { dev_name, dce_sender, diff --git a/ui/src/input.rs b/ui/src/input.rs index 4841f07be..1be74dd1c 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -54,8 +54,8 @@ const KEYPAD_DECIMAL: u16 = 0xffae; const KEYCODE_KP_7: u16 = 0x47; const KEYCODE_KP_DECIMAL: u16 = 0x53; // Led (HID) -pub const NUM_LOCK_LED: u8 = 0x1; -pub const CAPS_LOCK_LED: u8 = 0x2; +const NUM_LOCK_LED: u8 = 0x1; +const CAPS_LOCK_LED: u8 = 0x2; pub const SCROLL_LOCK_LED: u8 = 0x4; /// Input button state. pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 118df91bb..1de8a7759 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -78,17 +78,17 @@ impl SaslAuth { #[derive(Debug, Clone)] pub struct SaslConfig { /// State of sasl connection . - pub sasl_conn: *mut sasl_conn_t, + sasl_conn: *mut sasl_conn_t, /// Mech list server support. - pub mech_list: String, + mech_list: String, /// Authentication mechanism currently in use. - pub mech_name: String, + mech_name: String, /// State of auth. - pub sasl_stage: SaslStage, + sasl_stage: SaslStage, /// Security layer in sasl. - pub want_ssf: bool, + want_ssf: bool, /// Strength of ssf. - pub run_ssf: u32, + run_ssf: u32, } impl Default for SaslConfig { @@ -168,7 +168,7 @@ impl ClientIoHandler { } /// Length of client authentication message. - pub fn get_authmessage_length(&mut self) -> Result<()> { + fn get_authmessage_length(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); let buf = [buf[0], buf[1], buf[2], buf[3]]; let len = u32::from_be_bytes(buf); @@ -188,7 +188,7 @@ impl ClientIoHandler { } /// Receive the authentication information from client and return the result. - pub fn client_sasl_auth(&mut self) -> Result<()> { + fn client_sasl_auth(&mut self) -> Result<()> { info!("Sasl Authentication"); let buf = self.read_incoming_msg(); diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index b2f59eeb9..ec274d4fb 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -60,7 +60,7 @@ const CLIENT_REQUIRE_AUTH: bool = true; const MAXIMUM_SESSION_STORAGE: usize = 256; /// Cipher suites supported by server. -pub static TLS_CIPHER_SUITES: &[SupportedCipherSuite] = &[ +static TLS_CIPHER_SUITES: &[SupportedCipherSuite] = &[ TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, @@ -69,9 +69,9 @@ pub static TLS_CIPHER_SUITES: &[SupportedCipherSuite] = &[ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, ]; /// Tls version supported by server. -pub static TLS_VERSIONS: &[&SupportedProtocolVersion] = &[&TLS13, &TLS12]; +static TLS_VERSIONS: &[&SupportedProtocolVersion] = &[&TLS13, &TLS12]; /// Key exchange groups supported by server. -pub static TLS_KX_GROUPS: [&SupportedKxGroup; 3] = [&X25519, &SECP256R1, &SECP384R1]; +static TLS_KX_GROUPS: [&SupportedKxGroup; 3] = [&X25519, &SECP256R1, &SECP384R1]; /// Configuration for tls. #[derive(Debug, Clone, Default)] @@ -204,7 +204,7 @@ impl ClientIoHandler { Ok(()) } - pub fn tls_handshake_done(&mut self) -> Result<()> { + fn tls_handshake_done(&mut self) -> Result<()> { let handler = self.handlers.get("vnc_client_io").unwrap().clone(); let handlers = vec![handler]; EventLoop::update_event( @@ -352,19 +352,19 @@ fn load_certs(filepath: &str) -> Result> { Ok(certs) } -pub struct TlsIoChannel { +struct TlsIoChannel { /// TcpStream connected with client. - pub stream: TcpStream, + stream: TcpStream, /// Tls server connection. - pub tls_conn: ServerConnection, + tls_conn: ServerConnection, } impl TlsIoChannel { - pub fn new(stream: TcpStream, tls_conn: ServerConnection) -> Self { + fn new(stream: TcpStream, tls_conn: ServerConnection) -> Self { Self { stream, tls_conn } } - pub fn tls_handshake(&mut self) -> Result<()> { + fn tls_handshake(&mut self) -> Result<()> { if self.tls_conn.read_tls(&mut self.stream)? == 0 { bail!("Tls hand shake failed: EOF"); } diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index cb12872e5..4c3124607 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -60,11 +60,11 @@ const ENCODING_TIGHT: i32 = 7; const ENCODING_ZRLE: i32 = 16; const ENCODING_ZYWRLE: i32 = 17; const ENCODING_DESKTOPRESIZE: i32 = -223; -pub const ENCODING_RICH_CURSOR: i32 = -239; +const ENCODING_RICH_CURSOR: i32 = -239; const ENCODING_POINTER_TYPE_CHANGE: i32 = -257; const ENCODING_LED_STATE: i32 = -261; const ENCODING_DESKTOP_RESIZE_EXT: i32 = -308; -pub const ENCODING_ALPHA_CURSOR: i32 = -314; +const ENCODING_ALPHA_CURSOR: i32 = -314; const ENCODING_WMVI: i32 = 1464686185; /// This trait is used to send bytes, @@ -75,7 +75,7 @@ pub trait IoOperations { } /// Image display feature. -pub enum VncFeatures { +enum VncFeatures { VncFeatureResize, VncFeatureResizeExt, VncFeatureHextile, @@ -85,16 +85,16 @@ pub enum VncFeatures { VncFeatureZlib, VncFeatureRichCursor, VncFeatureAlphaCursor, - VncFeatureTightPng, + _VncFeatureTightPng, VncFeatureZrle, VncFeatureZywrle, VncFeatureLedState, - VncFeatureXvp, - VncFeatureClipboardExt, + _VncFeatureXvp, + _VncFeatureClipboardExt, } /// Client to server message in Remote Framebuffer Protocol. -pub enum ClientMsg { +enum ClientMsg { SetPixelFormat = 0, SetEncodings = 2, FramebufferUpdateRequest = 3, @@ -132,7 +132,7 @@ pub struct VncVersion { } impl VncVersion { - pub fn new(major: u16, minor: u16) -> Self { + fn new(major: u16, minor: u16) -> Self { VncVersion { major, minor } } } @@ -197,7 +197,7 @@ impl DisplayMode { } } - pub fn has_feature(&self, feature: VncFeatures) -> bool { + fn has_feature(&self, feature: VncFeatures) -> bool { self.feature & (1 << feature as usize) != 0 } } @@ -325,12 +325,12 @@ impl Default for ConnState { } impl ConnState { - pub fn is_disconnect(&mut self) -> bool { + fn is_disconnect(&mut self) -> bool { self.dis_conn } /// Whether the client's image data needs to be updated. - pub fn is_need_update(&mut self) -> bool { + fn is_need_update(&mut self) -> bool { if self.is_disconnect() { return false; } @@ -342,7 +342,7 @@ impl ConnState { } } - pub fn clear_update_state(&mut self) { + fn clear_update_state(&mut self) { self.dirty_num = 0; self.update_state = UpdateState::No; } @@ -909,7 +909,7 @@ impl ClientIoHandler { } /// Keyboard event. - pub fn key_envent(&mut self) -> Result<()> { + fn key_envent(&mut self) -> Result<()> { if self.expect == 1 { self.expect = 8; return Ok(()); @@ -982,7 +982,7 @@ impl ClientIoHandler { } /// Client cut text. - pub fn client_cut_event(&mut self) { + fn client_cut_event(&mut self) { let buf = self.read_incoming_msg(); if self.expect == 1 { self.expect = 8; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index a0d72d569..53b1bc056 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -421,7 +421,7 @@ pub fn set_area_dirty( } /// Get the width of image. -pub fn vnc_width(width: i32) -> i32 { +fn vnc_width(width: i32) -> i32 { cmp::min( MAX_WINDOW_WIDTH as i32, round_up(width as u64, DIRTY_PIXELS_NUM as u64) as i32, @@ -560,7 +560,7 @@ pub fn write_pixel( /// * `client_dpm` - Output mod of client display. /// * `buf` - send buffer. /// * `color` - the pixel value need to be convert. -pub fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { +fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { let mut ret = [0u8; 4]; let r = ((color & 0x00ff0000) >> 16) << client_dpm.pf.red.bits >> 8; let g = ((color & 0x0000ff00) >> 8) << client_dpm.pf.green.bits >> 8; @@ -607,7 +607,7 @@ pub fn convert_pixel(client_dpm: &DisplayMode, buf: &mut Vec, color: u32) { /// * `rect` - dirty area of image. /// * `client_dpm` - Output mod information of client display. /// * `buf` - send buffer. -pub fn raw_send_framebuffer_update( +fn raw_send_framebuffer_update( image: *mut pixman_image_t, rect: &Rectangle, client_dpm: &DisplayMode, diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 304056479..e6c0b7d3a 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -147,7 +147,7 @@ impl EventNotifierHelper for VncConnHandler { /// Info of image. /// stride is not always equal to stride because of memory alignment. -pub struct ImageInfo { +struct ImageInfo { /// The start pointer to image. data: *mut u8, /// The memory size of each line for image. -- Gitee From 1d0288da077d80abbd445dea1671894a94bf8396 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 4 Aug 2023 09:50:52 +0800 Subject: [PATCH 1266/2187] fix some whitespace and comment typos Signed-off-by: yezengruan --- devices/src/camera_backend/v4l2.rs | 2 +- devices/src/usb/usbhost/mod.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 2 +- docs/boot.md | 11 +++++------ docs/config_guidebook.md | 12 ++++++------ docs/migration.md | 4 ++-- docs/qmp.md | 2 +- machine/src/lib.rs | 2 +- machine_manager/src/event_loop.rs | 2 +- machine_manager/src/qmp/qmp_schema.rs | 4 ++-- ozone/src/handler.rs | 2 +- .../microvm/functional/test_microvm_vhost_vsock.py | 2 +- .../microvm/functional/test_microvm_virtio_blk.py | 2 +- .../standvm/functional/test_standvm_vhost_vsock.py | 2 +- tests/hydropper/utils/utils_qmp.py | 2 +- tests/mod_test/src/libdriver/usb.rs | 2 +- tests/mod_test/tests/pci_test.rs | 4 ++-- tests/mod_test/tests/virtio_test.rs | 4 ++-- ui/src/vnc/encoding/enc_hextile.rs | 6 +++--- ui/src/vnc/mod.rs | 2 +- util/src/loop_context.rs | 2 +- virtio/src/device/block.rs | 4 ++-- virtio/src/device/gpu.rs | 2 +- virtio/src/device/rng.rs | 2 +- virtio/src/lib.rs | 2 +- virtio/src/queue/split.rs | 6 +++--- virtio/src/vhost/kernel/net.rs | 4 ++-- 27 files changed, 46 insertions(+), 47 deletions(-) diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index dd392b9f0..1e9d41125 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -70,7 +70,7 @@ pub struct V4l2CameraBackend { notify_cb: Option, /// Callback to used to notify the broken. broken_cb: Option, - /// If the video stream is on or not. + /// If the video stream is on or not. running: bool, /// If the backend fd is listening or not. listening: bool, diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 01461e286..7f216d694 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -341,7 +341,7 @@ pub struct UsbHost { handle: Option>, /// Describes a device. ddesc: Option, - /// /// EventFd for libusb. + /// EventFd for libusb. libevt: Vec, /// Configuration interface number. ifs_num: u8, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 47c4f5d9c..c46726a38 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -58,7 +58,7 @@ const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; /// Registers offset. /// 0x0 0x40 0x440 0x1000 0x2000 0x3000 0x4000 -/// | cap | oper | port | runtime | doorbell | MSIX | +/// | cap | oper | port | runtime | doorbell | MSIX | /// XHCI pci device which can be attached to PCI bus. pub struct XhciPciDevice { diff --git a/docs/boot.md b/docs/boot.md index ac7dcd715..4cec5dc6a 100644 --- a/docs/boot.md +++ b/docs/boot.md @@ -45,7 +45,7 @@ and copy it to `kernel` path as `.config`. You can also modify config options by ### 2. Build rootfs -Rootfs image is a file system image. An EXT4-format image with `/sbin/init` can +Rootfs image is a file system image. An EXT4-format image with `/sbin/init` can be mounted at boot time in StratoVirt. You can check [Appendix](#2Appendix). ### 3. Boot command line sample @@ -202,10 +202,9 @@ $ qemu-img convert -f qcow2 -O raw openEuler-21.03-x86_64.qcow2 openEuler-21.03- Now the available raw image is obtained. -### 4. Boot with kernel directly +### 4. Boot with kernel directly -It can directly boot from kernel. In this mode, UEFI and ACPI will not be used. And VM will skip the UEFI, directly start the kernel to reduce boot -up time. +It can directly boot from kernel. In this mode, UEFI and ACPI will not be used. And VM will skip the UEFI, directly start the kernel to reduce boot up time. Run the following commands to direct boot VM from kernel: @@ -325,7 +324,7 @@ Below is a simple way to make a EXT4 rootfs image: **Notice: alpine is an example. You can use any open rootfs filesystem with init/systemd as rootfs image.** -5. Unmount rootfs image: +5. Unmount rootfs image: ```shell $ cd ~ && umount /mnt/rootfs @@ -333,6 +332,6 @@ Below is a simple way to make a EXT4 rootfs image: ## Links -- [EDK II wiki](https://github.com/tianocore/tianocore.github.io/wiki/EDK-II) +- [EDK II wiki](https://github.com/tianocore/tianocore.github.io/wiki/EDK-II) - [OVMF wiki](https://github.com/tianocore/tianocore.github.io/wiki/OVMF) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 3032b6142..7efd39381 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -351,7 +351,7 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo ``` -StratoVirt also supports vhost-user-blk-pci to get a higher performance in storage, but only standard vm supports it. +StratoVirt also supports vhost-user-blk-pci to get a higher performance in storage, but only standard vm supports it. You can use it by adding a new device, one more property is supported by vhost-user-blk-pci device than virtio-blk-pci. @@ -546,7 +546,7 @@ $ ovs-vsctl set Interface port2 options:n_rxq=num,n_txq=num ### 2.4 Virtio-console Virtio console device is a simple device for data transfer between the guest and host. A console device may have -one or more ports. These ports could be generic ports or console ports. Character devices /dev/vport\*p\* in linux +one or more ports. These ports could be generic ports or console ports. Character devices /dev/vport\*p\* in linux guest will be created once setting a port (Whether it is a console port or not). Character devices at /dev/hvc0 to /dev/hvc7 in linux guest will be created once setting console port. To set the virtio console, chardev for redirection will be required. See [section 2.12 Chardev](#212-chardev) for details. @@ -1016,7 +1016,7 @@ Seven properties are supported for vhost_user_fs. - **chroot**: The program invokes `chroot(2)` to make the shared directory tree its root when it does not have permission to create namespaces itself. - **namespace**: The program invodes `pivot_root(2)` to make the shared directory tree its root. * modcaps: Add/delete capabilities, For example, `--modcaps=-LEASE,+KILL` stands for delete CAP_LEASE, add CAP_KILL. Capabilityes list do not need prefix `CAP_`. - + *How to start vhost_user_fs process?* ```shell @@ -1034,12 +1034,12 @@ host# stratovirt \ -device virtio-blk-pci,drive=drive_id,bug=pcie.0,addr=1,id=blk -serial stdio -disable-seccomp \ -chardev socket,id=virtio_fs,path=/tmp/shared/virtio_fs.sock,server,nowait \ -device vhost-user-fs-pci,id=device_id,chardev=virtio_fs,tag=myfs,bus=pcie.0,addr=0x7 - + guest# mount -t virtiofs myfs /mnt ``` ### 2.18 virtio-gpu -virtio-gpu is an virtualized graphics card that lets virtual machines can display with it. +virtio-gpu is an virtualized graphics card that lets virtual machines can display with it. Usually used in conjunction with VNC, the final images is rendered to the VNC client. Sample Configuration: @@ -1245,7 +1245,7 @@ and 'bootindex' for virtio-blk; 'chassis' for pcie-root-port; 'sockets', 'cores' and 'threads' for smp; 'accel' and 'usb' for machine; "format" for pflash device. ## 8. Debug boot time -Currently, measurement of guest boot up time is supported. The guest kernel writes different +Currently, measurement of guest boot up time is supported. The guest kernel writes different values to specific IO/MMIO regions, and it will trap to `stratovirt`, we can record the timestamp of kernel start or kernel boot complete. diff --git a/docs/migration.md b/docs/migration.md index f09be99b7..f47d8d13a 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -15,7 +15,7 @@ The migration stream can be passed over any transport as following: - TCP mode migration: using tcp sockets to do the migration. - UNIX mode migration: using unix sockets to do the migration. -Note: UNIX mode only supports migrate two VMs on the same host OS. TCP mode supports migrate both on the same or +Note: UNIX mode only supports migrate two VMs on the same host OS. TCP mode supports migrate both on the same or different host OS. ## Migration @@ -45,7 +45,7 @@ Launch the destination VM: -incoming tcp:192.168.0.1:4446 \ ``` -Note: +Note: - The destination VM command line parameter needs to be consistent with the source VM. - If it is necessary to change the data transmission from tcp network protocol to unix socket, the parameter `-incoming tcp:192.168.0.1:4446` needs to be replaced with `-incoming unix:/tmp/stratovirt-migrate.socket`. diff --git a/docs/qmp.md b/docs/qmp.md index 10870f790..fbaaf7353 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -168,7 +168,7 @@ Add a camera backend. #### Example ```json -<- {"execute":"cameradev_add", "arguments":{"id":"cam-0", "driver": "v4l2", "path":"/dev/video0"}} +<- {"execute":"cameradev_add", "arguments":{"id":"cam-0", "driver": "v4l2", "path":"/dev/video0"}} -> {"return": {}} ``` diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 369d86070..fa24b5f9c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1851,7 +1851,7 @@ pub trait MachineOps { } } -/// Normal run or resume virtual machine from migration/snapshot . +/// Normal run or resume virtual machine from migration/snapshot. /// /// # Arguments /// diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 1a43822ab..bf7b27c58 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -122,7 +122,7 @@ impl EventLoop { } } - /// Update event notifiers to event loop + /// Update event notifiers to event loop /// /// # Arguments /// diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 230737e88..274303821 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -462,7 +462,7 @@ impl Command for qmp_capabilities { /// /// This command will cause the StratoVirt process to exit gracefully. While every /// attempt is made to send the QMP response before terminating, this is not -/// guaranteed. When using this interface, a premature EOF would not be +/// guaranteed. When using this interface, a premature EOF would not be /// unexpected. /// /// # Examples @@ -2353,7 +2353,7 @@ pub struct SnapshotInfo { /// query-mem /// -/// This command +/// This command /// /// # Examples /// diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index 92ddf70cf..a2797b1fb 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -191,7 +191,7 @@ impl OzoneHandler { Ok(()) } - /// Get exec file name. + /// Get exec file name. fn exec_file_name(&self) -> Result { if let Some(file_name) = self.exec_file_path.file_name() { return Ok(file_name.to_string_lossy().into()); diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py b/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py index c7866e4d5..16bf01b59 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_vhost_vsock.py @@ -37,7 +37,7 @@ def _check_vsock_enable(microvm): return False status, _ = microvm.serial_cmd(_cmd) - assert status == 0 + assert status == 0 return True diff --git a/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py b/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py index 690ee9f0a..34760a107 100644 --- a/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py +++ b/tests/hydropper/testcases/microvm/functional/test_microvm_virtio_blk.py @@ -119,7 +119,7 @@ def test_microvm_virtio_blk_md5(test_session_root_path, microvm): """ Test data consistency by md5sum: - 1) Generate a temp disk for test_vm and launch. + 1) Generate a temp disk for test_vm and launch. 2) Mount the temp disk 3) Touch a file and compute it md5sum. 4) Umount the temp disk diff --git a/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py b/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py index ba88e1ba2..365144125 100644 --- a/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py +++ b/tests/hydropper/testcases/standvm/functional/test_standvm_vhost_vsock.py @@ -37,7 +37,7 @@ def _check_vsock_enable(standvm): return False status, _ = standvm.serial_cmd(_cmd) - assert status == 0 + assert status == 0 return True diff --git a/tests/hydropper/utils/utils_qmp.py b/tests/hydropper/utils/utils_qmp.py index 7d0527dcf..aa4d2e7a6 100644 --- a/tests/hydropper/utils/utils_qmp.py +++ b/tests/hydropper/utils/utils_qmp.py @@ -48,7 +48,7 @@ def assert_qmp_absent(dictionary, path): def assert_qmp(dictionary, path, value): """ Assert that the value for a specific path in a QMP dict - matches. When given a list of values, assert that any of + matches. When given a list of values, assert that any of them matches. """ result = dictpath(dictionary, path) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 07c65c768..28a6833c4 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -1207,7 +1207,7 @@ impl TestXhciPciDevice { self.queue_trb(slot_id, ep_id, &mut trb); } - // Queue multi-TD with IDT=1 + // Queue multi-TD with IDT=1 pub fn queue_multi_direct_td(&mut self, slot_id: u32, ep_id: u32, sz: u64, num: usize) { for _ in 0..num { self.queue_direct_td(slot_id, ep_id, sz); diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 6c7a544f1..e53bb8c8f 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -2031,7 +2031,7 @@ fn test_pci_hotunplug_001() { let root_port_nums = 1; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); - // Create root port whose bdf is 0:1:0. + // Create root port whose bdf is 0:1:0. let root_port = Rc::new(RefCell::new(RootPort::new( machine.clone(), alloc.clone(), @@ -2329,7 +2329,7 @@ fn test_pci_hotunplug_008() { let root_port_nums = 1; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); - // Create root port whose bdf is 0:1:0. + // Create root port whose bdf is 0:1:0. let root_port = Rc::new(RefCell::new(RootPort::new( machine.clone(), alloc.clone(), diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 9ad787d2b..0162b3b85 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -428,7 +428,7 @@ fn virtio_feature_vertion_1() { /// and succeed to do the I/O request. /// TestStep: /// 1. Init device. -/// 2. Do the I/O request(indirect and indirect + normal). +/// 2. Do the I/O request(indirect and indirect + normal). /// 3. Destroy device. /// Expect: /// 1/2/3: success. @@ -561,7 +561,7 @@ fn virtio_feature_event_idx() { /// and succeed to do the I/O request(normal + indirect) which has opened the event idx. /// TestStep: /// 1. Init device. -/// 2. Do the I/O request(indirect and indirect + normal). +/// 2. Do the I/O request(indirect and indirect + normal). /// 1) create 5 request(with indirect), and modify avail->used_event to 5. /// 2) If the event idx works, we will not get the interrupt from device. /// 3) create 5 request, and use the right avail->used_event. diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index c766609e1..7281f1abf 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -175,7 +175,7 @@ fn compress_each_tile<'a>( /// * `data_ptr` - pointer to the data of image. /// * `bg` - background of current tile. /// * `fg` - foreground of current tile. -/// * `stride` - stride of image. +/// * `stride` - stride of image. /// * `buf` - send buffer. fn subrectangle_of_foreground( sub_rect: &Rectangle, @@ -213,10 +213,10 @@ fn subrectangle_of_foreground( /// # Arguments /// /// * `sub_rect` - area of tile. -/// * `data_ptr` - pointer to the data of image. +/// * `data_ptr` - pointer to the data of image. /// * `bg` - background of current tile. /// * `stride` - stride of image. -/// * `client_dpm` - Output mode information of client display. +/// * `client_dpm` - Output mode information of client display. /// * `buf` - send buffer. fn subrectangle_with_pixel_value( sub_rect: &Rectangle, diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 53b1bc056..a0cf1670e 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -552,7 +552,7 @@ pub fn write_pixel( } } -/// Convert the sent information to a format supported +/// Convert the sent information to a format supported /// by the client depend on byte arrangement /// /// # Arguments diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 50b3110a2..fa5547f91 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -821,7 +821,7 @@ mod test { mainloop.run().unwrap(); // Firstly, event1 with handler1 would be added. Then, event1's handlers would append - // handler1_update, which would register fd1_related_update in mainloop. + // handler1_update, which would register fd1_related_update in mainloop. assert!(mainloop.check_existence(fd1_related.as_raw_fd()).unwrap()); assert!(mainloop .check_existence(fd1_related_update.as_raw_fd()) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 3d44ae57e..b19c2a6b6 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1434,7 +1434,7 @@ mod tests { // Test `get_device_features` and `set_driver_features`. The main contests include: If the // device feature is 0, all driver features are not supported; If both the device feature bit - // and the front-end driver feature bit are supported at the same time, this driver feature + // and the front-end driver feature bit are supported at the same time, this driver feature // bit is supported. #[test] fn test_block_features() { @@ -1457,7 +1457,7 @@ mod tests { assert_eq!(block.device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are - // supported at the same time, this driver feature bit is supported. + // supported at the same time, this driver feature bit is supported. block.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC; let driver_feature: u32 = (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) as u32; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 52870c8ff..e11eef4b4 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -600,7 +600,7 @@ impl GpuIoHandler { error!( "GuestError: An incomplete response will be used instead of the expected: expected \ length is {}, actual length is {}. \ - Also, be aware that the virtual machine may suspended if response is too short to \ + Also, be aware that the virtual machine may suspended if response is too short to \ carry the necessary information.", size_of::(), len, ); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index b45c9bad6..3cb41c18c 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -448,7 +448,7 @@ mod tests { assert_eq!(rng.device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are - // supported at the same time, this driver feature bit is supported. + // supported at the same time, this driver feature bit is supported. rng.base.device_features = 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64; let driver_feature: u32 = 1_u32 << VIRTIO_F_RING_INDIRECT_DESC; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 818f20d1b..50fa0165a 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -263,7 +263,7 @@ pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105; pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106; /// Detach backing pages from a resource. pub const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107; -//// Retrieve the EDID data for a given scanout. +/// Retrieve the EDID data for a given scanout. pub const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x010a; /// update cursor pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 34f9e7a82..1f3f509f2 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -613,7 +613,7 @@ impl SplitVring { let new = match self.get_used_idx(sys_mem) { Ok(used_idx) => Wrapping(used_idx), Err(ref e) => { - error!("Failed to get the status for notifying used vring {:?}", e); + error!("Failed to get the status for notifying used vring: {:?}", e); return false; } }; @@ -621,7 +621,7 @@ impl SplitVring { let used_event_idx = match self.get_used_event(sys_mem) { Ok(idx) => Wrapping(idx), Err(ref e) => { - error!("Failed to get the status for notifying used vring {:?}", e); + error!("Failed to get the status for notifying used vring: {:?}", e); return false; } }; @@ -1948,7 +1948,7 @@ mod tests { let mut vring = SplitVring::new(queue_config); assert_eq!(vring.is_valid(&sys_space), true); - // it's true when the feature of event idx and no interrupt for the avail ring is closed + // it's true when the feature of event idx and no interrupt for the avail ring is closed let features = 0 as u64; assert!(vring.set_avail_ring_flags(&sys_space, 0).is_ok()); assert_eq!(vring.should_notify(&sys_space, features), true); diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 08decb276..98b56646f 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -45,8 +45,8 @@ const VHOST_NET_F_VIRTIO_NET_HDR: u32 = 27; trait VhostNetBackend { /// Attach virtio net ring to a raw socket, or tap device. /// The socket must be already bound to an ethernet device, this device will be - /// used for transmit. Pass fd -1 to unbind from the socket and the transmit - /// device. This can be used to stop the ring (e.g. for migration). + /// used for transmit. Pass fd -1 to unbind from the socket and the transmit + /// device. This can be used to stop the ring (e.g. for migration). /// /// # Arguments /// * `queue_index` - Index of the queue to modify. -- Gitee From 26074f6c75cd70d4be786315b9e4cbd92eff16b3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 4 Aug 2023 09:46:58 +0800 Subject: [PATCH 1267/2187] add copyright to the head of some files Some files lack copyright, let's add it. Signed-off-by: yezengruan --- pci/src/demo_device/kbd_pointer_device.rs | 3 +++ pci/src/demo_device/mod.rs | 12 ++++++++++++ util/src/trace.rs | 12 ++++++++++++ virtio/src/vhost/user/net.rs | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/pci/src/demo_device/kbd_pointer_device.rs index 2ab772cee..991af7551 100644 --- a/pci/src/demo_device/kbd_pointer_device.rs +++ b/pci/src/demo_device/kbd_pointer_device.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: diff --git a/pci/src/demo_device/mod.rs b/pci/src/demo_device/mod.rs index 128a25148..10a3c8273 100644 --- a/pci/src/demo_device/mod.rs +++ b/pci/src/demo_device/mod.rs @@ -1,3 +1,15 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + pub mod base_device; #[cfg(not(target_env = "musl"))] pub mod dpy_device; diff --git a/util/src/trace.rs b/util/src/trace.rs index 043d85cb1..36b5e3ca2 100644 --- a/util/src/trace.rs +++ b/util/src/trace.rs @@ -1,3 +1,15 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + use std::collections::HashSet; use std::fs::{File, OpenOptions}; use std::io::{prelude::Write, BufRead, BufReader}; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 53f57809c..4bf04b07c 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -1,4 +1,4 @@ -// Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan -- Gitee From 721a74a3d68de60e51987080c3f756eddd7723fe Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Fri, 4 Aug 2023 15:12:10 +0800 Subject: [PATCH 1268/2187] vitio: Remove redundant pub declarations Signed-off-by: jiewangqun --- vhost_user_fs/src/main.rs | 4 ++-- virtio/src/device/balloon.rs | 19 ++++++++++--------- virtio/src/device/block.rs | 4 ++-- virtio/src/device/gpu.rs | 22 +++++++++++----------- virtio/src/device/net.rs | 4 ++-- virtio/src/device/scsi_cntlr.rs | 10 +++++----- virtio/src/device/serial.rs | 2 +- virtio/src/queue/split.rs | 6 +++--- virtio/src/transport/virtio_mmio.rs | 4 ++-- virtio/src/transport/virtio_pci.rs | 2 +- virtio/src/vhost/user/client.rs | 12 ++++++------ 11 files changed, 45 insertions(+), 44 deletions(-) diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs index 310e46163..de575d160 100644 --- a/vhost_user_fs/src/main.rs +++ b/vhost_user_fs/src/main.rs @@ -42,7 +42,7 @@ use util::arg_parser::ArgMatches; use util::{arg_parser, logger, seccomp::SeccompOpt}; #[derive(Error, Debug)] -pub enum MainError { +enum MainError { #[error("VhostUserFs")] VhostUserFs { #[from] @@ -60,7 +60,7 @@ pub enum MainError { }, } -pub trait ExitCode { +trait ExitCode { /// Returns the value to use as the exit status. fn code(self) -> i32; } diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 7e902cabf..dbb581602 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -88,19 +88,19 @@ struct BalloonStat { #[allow(dead_code)] struct VirtioBalloonConfig { /// The target page numbers of balloon device. - pub num_pages: u32, + num_pages: u32, /// Number of pages we've actually got in balloon device. - pub actual: u32, - pub _reserved: u32, - pub _reserved1: u32, + actual: u32, + _reserved: u32, + _reserved1: u32, /// Buffer percent is a percentage of memory actually needed by /// the applications and services running inside the virtual machine. /// This parameter takes effect only when VIRTIO_BALLOON_F_MESSAGE_VQ is supported. /// Recommended value range: [20, 80] and default is 50. - pub membuf_percent: u32, + membuf_percent: u32, /// Monitor interval(second) host wants to adjust VM memory size. /// Recommended value range: [5, 300] and default is 10. - pub monitor_interval: u32, + monitor_interval: u32, } impl ByteCode for BalloonStat {} @@ -950,7 +950,7 @@ impl Balloon { /// # Argument /// /// * `size` - Target memory size. - pub fn set_guest_memory_size(&mut self, size: u64) -> Result<()> { + fn set_guest_memory_size(&mut self, size: u64) -> Result<()> { let host_page_size = host_page_size(); if host_page_size > BALLOON_PAGE_SIZE && !self.mem_info.lock().unwrap().has_huge_page() { warn!("Balloon used with backing page size > 4kiB, this may not be reliable"); @@ -976,10 +976,11 @@ impl Balloon { } /// Get the actual memory size of guest. - pub fn get_guest_memory_size(&self) -> u64 { + fn get_guest_memory_size(&self) -> u64 { self.mem_info.lock().unwrap().get_ram_size() - self.get_balloon_memory_size() } - pub fn set_num_pages(&mut self, target: u32) { + + fn set_num_pages(&mut self, target: u32) { self.num_pages = target; } } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index b19c2a6b6..f15418fd1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -475,9 +475,9 @@ struct BlockIoHandler { /// The block backend opened by the block device. block_backend: Option>>>, /// The align requirement of request(offset/len). - pub req_align: u32, + req_align: u32, /// The align requirement of buffer(iova_base). - pub buf_align: u32, + buf_align: u32, /// The number of sectors of the disk image. disk_sectors: u64, /// Serial number of the block device. diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index e11eef4b4..f11da1a9b 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -280,7 +280,7 @@ struct VirtioGpuResourceDetachBacking { impl ByteCode for VirtioGpuResourceDetachBacking {} -pub struct GpuOpts { +struct GpuOpts { /// Status of the emulated physical outputs. output_states: Arc>, /// Config space of the GPU device. @@ -488,15 +488,15 @@ fn create_surface( } // simple formats for fbcon/X use -pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; -pub const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; -pub const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3; -pub const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4; -pub const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; -pub const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; -pub const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; -pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; -pub const VIRTIO_GPU_FORMAT_INVALID_UNORM: u32 = 135; +const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1; +const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2; +const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3; +const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4; +const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; +const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; +const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; +const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; +const _VIRTIO_GPU_FORMAT_INVALID_UNORM: u32 = 135; pub fn get_pixman_format(format: u32) -> Result { match format { @@ -1416,7 +1416,7 @@ impl EventNotifierHelper for GpuIoHandler { } #[derive(Clone, Copy, Debug, ByteCode)] -pub struct VirtioGpuConfig { +struct VirtioGpuConfig { events_read: u32, events_clear: u32, num_scanouts: u32, diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 16360ac3e..067ec47e4 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -113,7 +113,7 @@ pub struct VirtioNetConfig { impl ByteCode for VirtioNetConfig {} /// The control mode used for packet receive filtering. -pub struct CtrlRxMode { +struct CtrlRxMode { /// If the device should receive all incoming packets. promisc: bool, /// If the device should allow all incoming multicast packets. @@ -145,7 +145,7 @@ impl Default for CtrlRxMode { #[derive(Default, Clone)] struct MacAddress { - pub address: [u8; MAC_ADDR_LEN], + address: [u8; MAC_ADDR_LEN], } /// The Mac information used to filter incoming packet. diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 58ae68fb6..486a7f57f 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -353,7 +353,7 @@ impl ByteCode for VirtioScsiCtrlAnResp {} #[repr(C, packed)] #[derive(Default, Clone, Copy)] -pub struct VirtioScsiCmdReq { +struct VirtioScsiCmdReq { /// Logical Unit Number. lun: [u8; 8], /// Command identifier. @@ -370,7 +370,7 @@ impl ByteCode for VirtioScsiCmdReq {} #[repr(C)] #[derive(Clone, Copy)] -pub struct VirtioScsiCmdResp { +struct VirtioScsiCmdResp { /// Sense data length. sense_len: u32, /// Resudual bytes in data buffer. @@ -566,7 +566,7 @@ impl VirtioScsiReq } } -pub struct ScsiCtrlQueueHandler { +struct ScsiCtrlQueueHandler { /// The ctrl virtqueue. queue: Arc>, /// EventFd for the ctrl virtqueue. @@ -675,7 +675,7 @@ impl EventNotifierHelper for ScsiCtrlQueueHandler { } } -pub struct ScsiEventQueueHandler { +struct ScsiEventQueueHandler { /// The Event virtqueue. _queue: Arc>, /// EventFd for the Event virtqueue. @@ -739,7 +739,7 @@ fn virtio_scsi_get_lun_id(lun: [u8; 8]) -> u16 { (((lun[2] as u16) << 8) | (lun[3] as u16)) & 0x3FFF } -pub struct ScsiCmdQueueHandler { +struct ScsiCmdQueueHandler { /// The scsi controller. scsibus: Arc>, /// The Cmd virtqueue. diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index a61b6205a..37f4a4245 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -103,7 +103,7 @@ impl ByteCode for VirtioConsoleConfig {} impl VirtioConsoleConfig { /// Create configuration of virtio-serial devices. - pub fn new(max_nr_ports: u32) -> Self { + fn new(max_nr_ports: u32) -> Self { VirtioConsoleConfig { cols: 0_u16, rows: 0_u16, diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 1f3f509f2..88b68e072 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -116,11 +116,11 @@ impl QueueConfig { } } - pub fn get_desc_size(&self) -> u64 { + fn get_desc_size(&self) -> u64 { min(self.size, self.max_size) as u64 * DESCRIPTOR_LEN } - pub fn get_used_size(&self, features: u64) -> u64 { + fn get_used_size(&self, features: u64) -> u64 { let size = if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { 2_u64 } else { @@ -130,7 +130,7 @@ impl QueueConfig { size + VRING_FLAGS_AND_IDX_LEN + (min(self.size, self.max_size) as u64) * USEDELEM_LEN } - pub fn get_avail_size(&self, features: u64) -> u64 { + fn get_avail_size(&self, features: u64) -> u64 { let size = if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { 2_u64 } else { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 8e0c10dae..087811aec 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -84,13 +84,13 @@ const MMIO_MAGIC_VALUE: u32 = 0x7472_6976; const MMIO_VERSION: u32 = 2; /// HostNotifyInfo includes the info needed for notifying backend from guest. -pub struct HostNotifyInfo { +struct HostNotifyInfo { /// Eventfds which notify backend to use the avail ring. events: Vec>, } impl HostNotifyInfo { - pub fn new(queue_num: usize) -> Self { + fn new(queue_num: usize) -> Self { let mut events = Vec::new(); for _i in 0..queue_num { events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 559c55f50..ba553ebae 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -246,7 +246,7 @@ impl VirtioPciNotifyCap { #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] -pub struct VirtioPciState { +struct VirtioPciState { dev_id: u16, /// Max length of config_space is 4096. config_space: [u8; 4096], diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 3a68bf4f9..48e87350f 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -46,7 +46,7 @@ pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; /// Vhost supports `VHOST_USER_SET_CONFIG` and `VHOST_USER_GET_CONFIG` msg. pub const VHOST_USER_PROTOCOL_F_CONFIG: u8 = 9; /// Vhost supports `VHOST_USER_SET_INFLIGHT_FD` and `VHOST_USER_GET_INFLIGHT_FD` msg. -pub const VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD: u8 = 12; +const VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD: u8 = 12; struct ClientInternal { // Used to send requests to the vhost user backend in userspace. @@ -336,12 +336,12 @@ pub struct VhostUserInflight { /// Struct for saving inflight info, create this struct to save inflight info when /// vhost client start, use this struct to set inflight fd when vhost client reconnect. #[derive(Debug)] -pub struct VhostInflight { +struct VhostInflight { // The inflight file. - pub file: Arc, + file: Arc, // Fd mmap addr, used for migration. - pub addr: u64, - pub inner: VhostUserInflight, + _addr: u64, + inner: VhostUserInflight, } #[derive(PartialEq, Eq)] @@ -451,7 +451,7 @@ impl VhostUserClient { )?; let inflight = VhostInflight { file, - addr: hva, + _addr: hva, inner: vhost_user_inflight, }; self.inflight = Some(inflight); -- Gitee From 6dce76583e227ccffe60e7d184535d4d98428eee Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 10 Jul 2023 21:33:32 +0800 Subject: [PATCH 1269/2187] qcow2: support get continous host offset 1. Support get continous host offset for read. 2. Merge continous host offset for write. 3. Try to reduce the number of clones of the vector by passing Vec directly. Signed-off-by: zhouli57 --- block_backend/src/file.rs | 2 +- block_backend/src/lib.rs | 4 +- block_backend/src/qcow2/mod.rs | 213 ++++++++++++++++++++----------- block_backend/src/qcow2/table.rs | 6 + block_backend/src/raw.rs | 12 +- devices/src/scsi/bus.rs | 4 +- util/src/aio/mod.rs | 23 ++++ virtio/src/device/block.rs | 4 +- 8 files changed, 182 insertions(+), 86 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index ac498512e..451e18114 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -127,7 +127,7 @@ impl FileDriver { self.process_request(OpCode::Preadv, req_list, completecb) } - pub fn complete_request( + fn complete_request( &mut self, opcode: OpCode, iovec: &[Iovec], diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index b824c50a5..580ef7956 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -57,9 +57,9 @@ pub struct BlockProperty { pub trait BlockDriverOps: Send { fn disk_size(&mut self) -> Result; - fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()>; + fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()>; - fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()>; + fn write_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()>; fn datasync(&mut self, completecb: T) -> Result<()>; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 032cf3e03..7fb4413bd 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -46,7 +46,7 @@ use crate::{ use machine_manager::qmp::qmp_schema::SnapshotInfo; use util::{ aio::{ - get_iov_size, iov_from_buf_direct, iovecs_split, raw_write_zeroes, Aio, AioCb, AioEngine, + get_iov_size, iovec_write_zero, iovecs_split, raw_write_zeroes, Aio, AioCb, AioEngine, Iovec, OpCode, }, num_ops::{div_round_up, round_down, round_up}, @@ -88,9 +88,12 @@ type Qcow2ListType = Lazy Qcow2Driver { Ok(()) } - fn host_offset_for_read(&mut self, guest_offset: u64) -> Result { - let l2_address = self.table.get_l1_table_entry(guest_offset) & L1_TABLE_OFFSET_MASK; - if l2_address == 0 { - return Ok(HostOffset::DataNotInit); - } - - let cluster_addr: u64; - let cluster_type: Qcow2ClusterType; + // NOTE: L2 table must be allocated. + fn get_l2_entry(&mut self, guest_offset: u64) -> Result { let l2_index = self.table.get_l2_table_index(guest_offset); if let Some(entry) = self.table.get_l2_table_cache_entry(guest_offset) { - let l2_entry = entry.borrow_mut().get_entry_map(l2_index as usize)?; - cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); - cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; + entry.borrow_mut().get_entry_map(l2_index as usize) } else { + let l2_address = self.table.get_l1_table_entry(guest_offset) & L1_TABLE_OFFSET_MASK; + if l2_address == 0 { + bail!("L2 table is unallocated when get l2 cache"); + } let l2_cluster = self.load_cluster(l2_address)?; let l2_table = Rc::new(RefCell::new(CacheTable::new( l2_address, l2_cluster, ENTRY_SIZE_U64, )?)); - let l2_entry = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; - cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); - cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; + let res = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; self.table.update_l2_table(l2_table)?; + Ok(res) + } + } + + fn get_continuous_address( + &mut self, + guest_offset: u64, + expect_len: u64, + ) -> Result<(Qcow2ClusterType, u64, u64)> { + let begin = round_down(guest_offset, self.header.cluster_size()) + .with_context(|| format!("invalid offset {}", guest_offset))?; + let end = round_up(guest_offset + expect_len, self.header.cluster_size()) + .with_context(|| format!("invalid offset {} len {}", guest_offset, expect_len))?; + let clusters = (end - begin) / self.header.cluster_size(); + if clusters == 0 { + bail!( + "Failed to get continuous address offset {} len {}", + guest_offset, + expect_len + ); } + let mut host_start = 0; + let mut first_cluster_type = Qcow2ClusterType::Unallocated; + let mut cnt = 0; + while cnt < clusters { + let offset = cnt * self.header.cluster_size(); + let l2_entry = self.get_l2_entry(begin + offset)?; + let cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); + let cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; + if cnt == 0 { + host_start = cluster_addr; + first_cluster_type = cluster_type; + } else if cluster_addr != host_start + offset || cluster_type != first_cluster_type { + break; + } + cnt += 1; + } + let sz = cnt * self.header.cluster_size() - self.offset_into_cluster(guest_offset); + let actual_len = std::cmp::min(expect_len, sz); + Ok(( + first_cluster_type, + host_start + self.offset_into_cluster(guest_offset), + actual_len, + )) + } - if cluster_addr == 0 || cluster_type.is_read_zero() { - Ok(HostOffset::DataNotInit) + fn host_offset_for_read(&mut self, guest_offset: u64, req_len: u64) -> Result { + // Request not support cross l2 table. + let l2_max_len = self + .table + .get_l2_table_max_remain_size(guest_offset, self.offset_into_cluster(guest_offset)); + let size = std::cmp::min(req_len, l2_max_len); + let l2_address = self.table.get_l1_table_entry(guest_offset) & L1_TABLE_OFFSET_MASK; + if l2_address == 0 { + return Ok(HostRange::DataNotInit(size)); + } + let (cluster_type, host_start, bytes) = self.get_continuous_address(guest_offset, size)?; + if cluster_type.is_read_zero() { + Ok(HostRange::DataNotInit(bytes)) } else { - Ok(HostOffset::DataAddress( - cluster_addr + self.offset_into_cluster(guest_offset), - )) + Ok(HostRange::DataAddress(host_start, bytes)) } } - fn host_offset_for_write(&mut self, guest_offset: u64) -> Result { + fn host_offset_for_write(&mut self, guest_offset: u64) -> Result { let l2_index = self.table.get_l2_table_index(guest_offset); let l2_table = self.get_table_cluster(guest_offset)?; let mut l2_entry = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; @@ -326,9 +376,7 @@ impl Qcow2Driver { l2_table .borrow_mut() .set_entry_map(l2_index as usize, l2_entry)?; - Ok(HostOffset::DataAddress( - cluster_addr + self.offset_into_cluster(guest_offset), - )) + Ok(cluster_addr + self.offset_into_cluster(guest_offset)) } /// Obtaining the target entry for guest offset. @@ -416,9 +464,8 @@ impl Qcow2Driver { bail!("Buffer size: is out of range",); } // Return if the address is not allocated. - if let HostOffset::DataAddress(host_offset) = self.host_offset_for_write(guest_offset)? { - self.sync_aio.borrow_mut().write_buffer(host_offset, buf)?; - } + let host_offset = self.host_offset_for_write(guest_offset)?; + self.sync_aio.borrow_mut().write_buffer(host_offset, buf)?; Ok(()) } @@ -1330,64 +1377,76 @@ impl Qcow2Driver { } impl BlockDriverOps for Qcow2Driver { - fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - let nbytes = get_iov_size(iovec); + fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { + let nbytes = get_iov_size(&iovec); self.check_request(offset, nbytes) .with_context(|| " Invalid read request")?; - let mut left = iovec.to_vec(); - let total = std::cmp::min(nbytes, self.virtual_disk_size() - offset as u64); - let mut req_list = Vec::new(); + let mut left = iovec; + let mut req_list: Vec = Vec::new(); let mut copied = 0; - while copied < total { + while copied < nbytes { let pos = offset as u64 + copied; - let count = self.cluster_aligned_bytes(pos, total - copied); - let (begin, end) = iovecs_split(left, count); - left = end; - if let HostOffset::DataAddress(host_offset) = self.host_offset_for_read(pos)? { - let nbytes = get_iov_size(&begin); - req_list.push(CombineRequest { - iov: begin, - offset: host_offset, - nbytes, - }); - } else { - iov_from_buf_direct(&begin, &vec![0_u8; count as usize])?; + match self.host_offset_for_read(pos, nbytes - copied)? { + HostRange::DataAddress(host_offset, cnt) => { + let (begin, end) = iovecs_split(left, cnt); + left = end; + req_list.push(CombineRequest { + iov: begin, + offset: host_offset, + nbytes: cnt, + }); + copied += cnt; + } + HostRange::DataNotInit(cnt) => { + let (begin, end) = iovecs_split(left, cnt); + left = end; + iovec_write_zero(&begin); + copied += cnt; + } } - copied += count; } self.driver.read_vectored(req_list, completecb) } - fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - let nbytes = get_iov_size(iovec); + fn write_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { + let nbytes = get_iov_size(&iovec); self.check_request(offset, nbytes) .with_context(|| " Invalid write request")?; - let mut left = iovec.to_vec(); - let total = std::cmp::min(nbytes, self.virtual_disk_size() - offset as u64); - let mut req_list = Vec::new(); + let mut req_list: Vec = Vec::new(); let mut copied = 0; - while copied < total { + while copied < nbytes { let pos = offset as u64 + copied; - let count = self.cluster_aligned_bytes(pos, total - copied); - let (begin, end) = iovecs_split(left, count); - left = end; - if let HostOffset::DataAddress(host_offset) = self.host_offset_for_write(pos)? { - let nbytes = get_iov_size(&begin); - req_list.push(CombineRequest { - iov: begin, - offset: host_offset, - nbytes, - }); - copied += count; + let count = self.cluster_aligned_bytes(pos, nbytes - copied); + let host_offset = self.host_offset_for_write(pos)?; + if let Some(end) = req_list.last_mut() { + if end.offset + end.nbytes == host_offset { + end.nbytes += count; + copied += count; + continue; + } } + req_list.push(CombineRequest { + iov: Vec::new(), + offset: host_offset, + nbytes: count, + }); + copied += count; } if req_list.is_empty() { bail!("Request list is empty!"); } + + let mut left = iovec; + for req in req_list.iter_mut() { + let (begin, end) = iovecs_split(left, req.nbytes); + req.iov = begin; + left = end; + } + self.driver.write_vectored(req_list, completecb) } @@ -1716,7 +1775,7 @@ mod test { fn qcow2_read(qcow2: &mut Qcow2Driver<()>, buf: &mut [u8], offset: usize) -> Result<()> { qcow2.read_vectored( - &[Iovec { + vec![Iovec { iov_base: buf.as_ptr() as u64, iov_len: buf.len() as u64, }], @@ -1727,7 +1786,7 @@ mod test { fn qcow2_write(qcow2: &mut Qcow2Driver<()>, buf: &[u8], offset: usize) -> Result<()> { qcow2.write_vectored( - &[Iovec { + vec![Iovec { iov_base: buf.as_ptr() as u64, iov_len: buf.len() as u64, }], @@ -1871,8 +1930,12 @@ mod test { let (_, mut qcow2) = create_qcow2(path); let (case_list, _buf_list) = generate_rw_case_list(); for case in &case_list { - qcow2.write_vectored(&case.wiovec, case.offset, ()).unwrap(); - qcow2.read_vectored(&case.riovec, case.offset, ()).unwrap(); + qcow2 + .write_vectored(case.wiovec.clone(), case.offset, ()) + .unwrap(); + qcow2 + .read_vectored(case.riovec.clone(), case.offset, ()) + .unwrap(); let mut wbuf = vec![0; case.sz as usize]; let mut rbuf = vec![0; case.sz as usize]; @@ -1942,8 +2005,12 @@ mod test { let (_, mut qcow2) = create_qcow2(path); let (mut case_list, _buf_list) = generate_rw_random_list(); for case in &case_list { - qcow2.write_vectored(&case.wiovec, case.offset, ()).unwrap(); - qcow2.read_vectored(&case.riovec, case.offset, ()).unwrap(); + qcow2 + .write_vectored(case.wiovec.clone(), case.offset, ()) + .unwrap(); + qcow2 + .read_vectored(case.riovec.clone(), case.offset, ()) + .unwrap(); let mut wbuf = vec![0; case.sz as usize]; let mut rbuf = vec![0; case.sz as usize]; @@ -1957,7 +2024,7 @@ mod test { // read all write data once. let buf = vec![0_u8; 5 * CLUSTER_SIZE as usize]; let riovecs = vec![Iovec::new(buf.as_ptr() as u64, 5 * CLUSTER_SIZE)]; - qcow2.read_vectored(&riovecs, 0, ()).unwrap(); + qcow2.read_vectored(riovecs, 0, ()).unwrap(); case_list.sort_by(|a, b| a.offset.cmp(&b.offset)); let mut idx = 0; diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 3bdf71771..140bba040 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -176,6 +176,12 @@ impl Qcow2Table { } } + /// Get max data remaining size after the offset which indexed by l2 table. + pub fn get_l2_table_max_remain_size(&self, guest_offset: u64, offset_in_cluster: u64) -> u64 { + (self.l2_size - self.get_l2_table_index(guest_offset)) * self.cluster_size + - offset_in_cluster + } + pub fn update_l1_table(&mut self, l1_index: usize, l2_address: u64) { self.l1_table[l1_index] = l2_address; } diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 896f00d6c..cd351027b 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -43,18 +43,18 @@ impl RawDriver { } impl BlockDriverOps for RawDriver { - fn read_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - let nbytes = get_iov_size(iovec); + fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { + let nbytes = get_iov_size(&iovec); self.driver.read_vectored( - vec![CombineRequest::new(iovec.to_vec(), offset as u64, nbytes)], + vec![CombineRequest::new(iovec, offset as u64, nbytes)], completecb, ) } - fn write_vectored(&mut self, iovec: &[Iovec], offset: usize, completecb: T) -> Result<()> { - let nbytes = get_iov_size(iovec); + fn write_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { + let nbytes = get_iov_size(&iovec); self.driver.write_vectored( - vec![CombineRequest::new(iovec.to_vec(), offset as u64, nbytes)], + vec![CombineRequest::new(iovec, offset as u64, nbytes)], completecb, ) } diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index c2fc65fbb..19d6542d1 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -577,12 +577,12 @@ impl ScsiRequest { match mode { ScsiXferMode::ScsiXferFromDev => { locked_backend - .read_vectored(&iovecs, offset, scsicompletecb) + .read_vectored(iovecs, offset, scsicompletecb) .with_context(|| "Failed to process scsi request for reading")?; } ScsiXferMode::ScsiXferToDev => { locked_backend - .write_vectored(&iovecs, offset, scsicompletecb) + .write_vectored(iovecs, offset, scsicompletecb) .with_context(|| "Failed to process scsi request for writing")?; } _ => { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 65c64ab0a..3b1eb3837 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -747,6 +747,15 @@ pub fn iovecs_split(iovecs: Vec, mut size: u64) -> (Vec, Vec { locked_backend - .read_vectored(&iovecs, offset, aiocompletecb) + .read_vectored(iovecs, offset, aiocompletecb) .with_context(|| "Failed to process block request for reading")?; } VIRTIO_BLK_T_OUT => { locked_backend - .write_vectored(&iovecs, offset, aiocompletecb) + .write_vectored(iovecs, offset, aiocompletecb) .with_context(|| "Failed to process block request for writing")?; } VIRTIO_BLK_T_FLUSH => { -- Gitee From 088926117df7ff0519b9101a969856f069009c24 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 7 Aug 2023 14:29:02 +0800 Subject: [PATCH 1270/2187] qcow2: avoid residual resources Remove the reference in the QCOW2_LIST to release resources when hot unplug. And use Weak instead of Arc in exit_notifier. Signed-off-by: zhouli57 --- block_backend/src/lib.rs | 22 ++++++++++++++-------- virtio/src/device/block.rs | 6 +++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 580ef7956..d3f078fb6 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -117,18 +117,20 @@ pub fn create_block_backend( .lock() .unwrap() .insert(prop.id.clone(), new_qcow2.clone()); - let cloned_qcow2 = new_qcow2.clone(); + let cloned_qcow2 = Arc::downgrade(&new_qcow2); // NOTE: we can drain request when request in io thread. let drain = prop.iothread.is_some(); let cloned_drive_id = prop.id.clone(); let exit_notifier = Arc::new(move || { - let mut locked_qcow2 = cloned_qcow2.lock().unwrap(); - info!("clean up qcow2 {:?} resources.", cloned_drive_id); - if let Err(e) = locked_qcow2.flush() { - error!("Failed to flush qcow2 {:?}", e); - } - if drain { - locked_qcow2.drain_request(); + if let Some(qcow2) = cloned_qcow2.upgrade() { + let mut locked_qcow2 = qcow2.lock().unwrap(); + info!("clean up qcow2 {:?} resources.", cloned_drive_id); + if let Err(e) = locked_qcow2.flush() { + error!("Failed to flush qcow2 {:?}", e); + } + if drain { + locked_qcow2.drain_request(); + } } }) as Arc; TempCleaner::add_exit_notifier(prop.id, exit_notifier); @@ -136,3 +138,7 @@ pub fn create_block_backend( } } } + +pub fn remove_block_backend(id: &str) { + QCOW2_LIST.lock().unwrap().remove(id); +} diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 12a22072d..92941d6fb 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -38,7 +38,8 @@ use crate::{ }; use address_space::{AddressSpace, GuestAddress}; use block_backend::{ - create_block_backend, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, + create_block_backend, remove_block_backend, BlockDriverOps, BlockIoErrorCallback, + BlockProperty, BlockStatus, }; use machine_manager::config::{BlkDevConfig, ConfigCheck, DriveFile, VmConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; @@ -1113,6 +1114,9 @@ impl VirtioDevice for Block { fn unrealize(&mut self) -> Result<()> { MigrationManager::unregister_device_instance(BlockState::descriptor(), &self.blk_cfg.id); + let drive_files = self.drive_files.lock().unwrap(); + let drive_id = VmConfig::get_drive_id(&drive_files, &self.blk_cfg.path_on_host)?; + remove_block_backend(&drive_id); Ok(()) } -- Gitee From 5341cb6153c51b074febf2df0ebe0530289df154 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Mon, 7 Aug 2023 11:40:44 +0800 Subject: [PATCH 1271/2187] qcow2: reset lru_count if overflow Signed-off-by: zhouli57 --- block_backend/src/qcow2/cache.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index e2ab01e48..9ea34da96 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -168,6 +168,7 @@ impl Qcow2Cache { return; } warn!("refcount reaches the max limit and is reset to 0"); + self.lru_count = 0; for (_, entry) in self.cache_map.iter() { entry.borrow_mut().lru_count = 0; } -- Gitee From fd483e21d8a3311deb49216f03d3b68e05a58096 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Fri, 14 Jul 2023 15:48:24 +0800 Subject: [PATCH 1272/2187] qcow2: add some UT Signed-off-by: zhouli57 --- block_backend/src/qcow2/cache.rs | 44 +++++++++++++++++++---- block_backend/src/qcow2/mod.rs | 53 +++++++++++++++++---------- block_backend/src/qcow2/table.rs | 61 ++++++++++++++++++++++++++++++-- 3 files changed, 132 insertions(+), 26 deletions(-) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 9ea34da96..097bb02a4 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -224,7 +224,7 @@ impl Qcow2Cache { #[cfg(test)] mod test { - use super::{CacheTable, Qcow2Cache}; + use super::{CacheTable, Qcow2Cache, ENTRY_SIZE_U64}; use std::{cell::RefCell, rc::Rc}; #[test] @@ -234,7 +234,7 @@ mod test { for i in 0..buf.len() { vec.append(&mut buf[i].to_be_bytes().to_vec()); } - let mut entry = CacheTable::new(0x00 as u64, vec, 8).unwrap(); + let mut entry = CacheTable::new(0x00 as u64, vec, ENTRY_SIZE_U64).unwrap(); assert_eq!(entry.get_entry_map(0).unwrap(), 0x00); assert_eq!(entry.get_entry_map(3).unwrap(), 0x03); assert_eq!(entry.get_entry_map(4).unwrap(), 0x04); @@ -251,19 +251,19 @@ mod test { vec.append(&mut buf[i].to_be_bytes().to_vec()); } let entry_0 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_0.borrow_mut().lru_count = 0; let entry_1 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_1.borrow_mut().lru_count = 1; let entry_2 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_2.borrow_mut().lru_count = 2; let entry_3 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), 8).unwrap(), + CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_3.borrow_mut().lru_count = 3; @@ -273,4 +273,36 @@ mod test { assert!(qcow2_cache.lru_replace(0x02, entry_2).is_none()); assert!(qcow2_cache.lru_replace(0x03, entry_3).is_some()); } + + #[test] + fn test_get_cache() { + let cnt = 200_u64; + let mut vec = Vec::new(); + for i in 0..cnt { + vec.append(&mut i.to_be_bytes().to_vec()); + } + let addr = 12345678; + let entry = Rc::new(RefCell::new( + CacheTable::new(addr, vec, ENTRY_SIZE_U64).unwrap(), + )); + + let mut qcow2_cache = Qcow2Cache::new(2); + qcow2_cache.lru_replace(addr, entry.clone()); + qcow2_cache.lru_count = u64::MAX - cnt / 2; + // Not in cache. + assert!(qcow2_cache.get(0).is_none()); + assert!(qcow2_cache.get(addr + 10).is_none()); + + for i in 0..cnt { + let value = qcow2_cache.get(addr).unwrap(); + let v = value.borrow_mut().get_entry_map(i as usize).unwrap(); + assert_eq!(v, i); + } + assert_eq!(qcow2_cache.lru_count, cnt / 2); + + // Entry index invalid. + let value = qcow2_cache.get(addr).unwrap(); + let v = value.borrow_mut().get_entry_map(cnt as usize + 1); + assert!(v.err().unwrap().to_string().contains("Invalid idx")); + } } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 7fb4413bd..c4c915b7a 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1578,7 +1578,7 @@ mod test { const CLUSTER_SIZE: u64 = 64 * 1024; - struct TestImage { + pub struct TestImage { pub img_bits: u64, pub cluster_bits: u64, pub path: String, @@ -1745,7 +1745,7 @@ mod test { sz: u64, } - fn create_qcow2(path: &str) -> (TestImage, Qcow2Driver<()>) { + pub fn create_qcow2(path: &str) -> (TestImage, Qcow2Driver<()>) { let mut image = TestImage::new(path, 30, 16); fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { Ok(()) @@ -1807,9 +1807,12 @@ mod test { let mut buf = vec![2_u8; 512]; qcow2_read(&mut qcow2, &mut buf, 65536).unwrap(); assert_eq!(buf, vec![0; 512]); - let mut buf = vec![3_u8; 600]; - qcow2_read(&mut qcow2, &mut buf, 655350).unwrap(); - assert_eq!(buf, vec![0; 600]); + for i in 0..100 { + let sz = 100_000; + let mut buf = vec![3_u8; sz]; + qcow2_read(&mut qcow2, &mut buf, 655350 + i * sz).unwrap(); + assert_eq!(buf, vec![0; 100_000]); + } let len = image.file.seek(SeekFrom::End(0)).unwrap(); assert_eq!(org_len, len); @@ -1825,31 +1828,34 @@ mod test { let mut rbuf = vec![0_u8; CLUSTER_SIZE as usize]; qcow2_read(&mut qcow2, &mut rbuf, 0).unwrap(); assert_eq!(rbuf, wbuf); + let cnt = qcow2.refcount.get_refcount(0).unwrap(); + assert_eq!(cnt, 1); let wbuf = vec![5_u8; 1000]; qcow2_write(&mut qcow2, &wbuf, 2000).unwrap(); let mut rbuf = vec![0_u8; 1000]; qcow2_read(&mut qcow2, &mut rbuf, 2000).unwrap(); assert_eq!(rbuf, wbuf); + let cnt = qcow2.refcount.get_refcount(2000).unwrap(); + assert_eq!(cnt, 1); } - #[test] - fn test_write_multi_cluster() { - let path = "/tmp/block_backend_test_write_multi_cluster.qcow2"; - let (_, mut qcow2) = create_qcow2(path); - - let mut offset = 0; - let cnt: u8 = 2; - let sz = 100 * 1000; + fn test_write_multi_cluster_helper( + qcow2: &mut Qcow2Driver<()>, + off: usize, + sz: usize, + cnt: u8, + ) { + let mut offset = off; for i in 0..cnt { let buf = vec![i + 1; sz]; - qcow2_write(&mut qcow2, &buf, offset).unwrap(); + qcow2_write(qcow2, &buf, offset).unwrap(); offset += buf.len(); } - let mut offset = 0; + let mut offset = off; for i in 0..cnt { let mut buf = vec![i + 1; sz]; - qcow2_read(&mut qcow2, &mut buf, offset).unwrap(); + qcow2_read(qcow2, &mut buf, offset).unwrap(); for (_, item) in buf.iter().enumerate() { assert_eq!(item, &(i + 1)); } @@ -1857,6 +1863,17 @@ mod test { } } + #[test] + fn test_write_multi_cluster() { + let path = "/tmp/block_backend_test_write_multi_cluster.qcow2"; + let (_, mut qcow2) = create_qcow2(path); + + test_write_multi_cluster_helper(&mut qcow2, 832574, 100_000, 200); + test_write_multi_cluster_helper(&mut qcow2, 0, 16, 250); + test_write_multi_cluster_helper(&mut qcow2, 7689, 512, 99); + test_write_multi_cluster_helper(&mut qcow2, 56285351, 4096, 123); + } + #[test] fn test_invalid_read_write() { let path = "/tmp/block_backend_test_invalid_read_write.qcow2"; @@ -2370,7 +2387,7 @@ mod test { let (case_list, _buf_list) = generate_rw_random_list(); for case in &case_list { qcow2_driver - .write_vectored(&case.wiovec, case.offset, ()) + .write_vectored(case.wiovec.clone(), case.offset, ()) .unwrap(); } @@ -2423,7 +2440,7 @@ mod test { let (case_list, _buf_list) = generate_rw_random_list(); for case in &case_list { qcow2_driver - .write_vectored(&case.wiovec, case.offset, ()) + .write_vectored(case.wiovec.clone(), case.offset, ()) .unwrap(); } diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 140bba040..90357cd75 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -80,8 +80,8 @@ impl Qcow2ClusterType { } pub struct Qcow2Table { - pub cluster_bits: u64, - pub cluster_size: u64, + cluster_bits: u64, + cluster_size: u64, pub l1_table: Vec, pub l2_table_cache: Qcow2Cache, sync_aio: Rc>, @@ -230,3 +230,60 @@ impl Qcow2Table { self.flush_l2_table_cache() } } + +#[cfg(test)] +mod test { + use std::{ + cell::RefCell, + io::{Read, Seek, SeekFrom}, + rc::Rc, + }; + + use crate::qcow2::{ + cache::{CacheTable, ENTRY_SIZE_U64}, + test::create_qcow2, + }; + + #[test] + fn test_update_l2_table() { + let path = "/tmp/block_backend_test_update_l2_table.qcow2"; + let (mut image, mut qcow2) = create_qcow2(path); + let cluster_size = qcow2.header.cluster_size() as usize; + let addr = qcow2.alloc_cluster(1, true).unwrap(); + let l2_cluster: Vec = vec![0_u8; cluster_size]; + let l2_table = Rc::new(RefCell::new( + CacheTable::new(addr, l2_cluster.clone(), ENTRY_SIZE_U64).unwrap(), + )); + qcow2.table.update_l2_table(l2_table.clone()).unwrap(); + + let test_val1 = 0xff00ff00_u64; + l2_table.borrow_mut().set_entry_map(0, test_val1).unwrap(); + let res = l2_table.borrow_mut().get_entry_map(0).unwrap(); + assert_eq!(res, test_val1); + + image.file.seek(SeekFrom::Start(addr)).unwrap(); + let mut buf = vec![0_u8; ENTRY_SIZE_U64]; + image.file.read_exact(&mut buf).unwrap(); + assert_eq!(buf, [0_u8; ENTRY_SIZE_U64]); + + let test_val2 = 0x00ff00ff_u64; + l2_table + .borrow_mut() + .set_entry_map(8191, test_val2) + .unwrap(); + let res = l2_table.borrow_mut().get_entry_map(8191).unwrap(); + assert_eq!(res, test_val2); + + qcow2.table.flush_l2_table_cache().unwrap(); + image.file.seek(SeekFrom::Start(addr)).unwrap(); + let mut buf = vec![0_u8; ENTRY_SIZE_U64]; + image.file.read_exact(&mut buf).unwrap(); + assert_eq!(buf, test_val1.to_be_bytes()); + + let offset = addr + ENTRY_SIZE_U64 as u64 * 8191; + image.file.seek(SeekFrom::Start(offset)).unwrap(); + let mut buf = vec![0_u8; ENTRY_SIZE_U64]; + image.file.read_exact(&mut buf).unwrap(); + assert_eq!(buf, test_val2.to_be_bytes()); + } +} -- Gitee From ba48c5b4a4116a5ab6f9d176b56af9ed9123ef32 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 12:38:16 +0800 Subject: [PATCH 1273/2187] Refactory: Introduce PciDevBase for all pci device Introduce PciDevBase for all pci device to share common pci device info. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 49 ++-- devices/src/usb/xhci/xhci_pci.rs | 77 +++--- .../src/standard_vm/aarch64/pci_host_root.rs | 35 +-- machine/src/standard_vm/x86_64/ich9_lpc.rs | 52 ++-- machine/src/standard_vm/x86_64/mch.rs | 45 ++-- pci/src/bus.rs | 46 ++-- pci/src/demo_dev.rs | 49 ++-- pci/src/host.rs | 28 +- pci/src/lib.rs | 37 ++- pci/src/root_port.rs | 239 ++++++++++-------- vfio/src/vfio_pci.rs | 97 +++---- virtio/src/transport/virtio_pci.rs | 199 ++++++++------- 12 files changed, 521 insertions(+), 432 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index e76e7b787..86b818c35 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -23,7 +23,7 @@ use pci::{ PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }, - le_write_u16, PciBus, PciDevOps, + le_write_u16, PciBus, PciDevBase, PciDevOps, }; const PCI_VENDOR_ID_IVSHMEM: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; @@ -36,11 +36,8 @@ const IVSHMEM_REG_BAR_SIZE: u64 = 0x100; /// Intel-VM shared memory device structure. pub struct Ivshmem { - config: PciConfig, - devfn: u8, + base: PciDevBase, dev_id: Arc, - name: String, - parent_bus: Weak>, ram_mem_region: Region, } @@ -52,11 +49,13 @@ impl Ivshmem { ram_mem_region: Region, ) -> Self { Self { - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), - devfn, + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), + devfn, + name, + parent_bus, + }, dev_id: Arc::new(AtomicU16::new(0)), - name, - parent_bus, ram_mem_region, } } @@ -72,7 +71,7 @@ impl Ivshmem { }; // bar0: mmio register - self.config.register_bar( + self.base.config.register_bar( 0, Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops, "IvshmemIo"), RegionType::Mem64Bit, @@ -81,7 +80,7 @@ impl Ivshmem { )?; // bar2: ram - self.config.register_bar( + self.base.config.register_bar( 2, self.ram_mem_region.clone(), RegionType::Mem64Bit, @@ -96,19 +95,19 @@ impl PciDevOps for Ivshmem { self.init_write_mask()?; self.init_write_clear_mask()?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, VENDOR_ID as usize, PCI_VENDOR_ID_IVSHMEM, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, DEVICE_ID as usize, PCI_DEVICE_ID_IVSHMEM, )?; - self.config.config[REVISION_ID] = PCI_REVIRSION_ID_IVSHMEM; + self.base.config.config[REVISION_ID] = PCI_REVIRSION_ID_IVSHMEM; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, SUB_CLASS_CODE as usize, PCI_CLASS_MEMORY_RAM, )?; @@ -116,39 +115,39 @@ impl PciDevOps for Ivshmem { self.register_bars()?; // Attach to the PCI bus. - let pci_bus = self.parent_bus.upgrade().unwrap(); + let pci_bus = self.base.parent_bus.upgrade().unwrap(); let mut locked_pci_bus = pci_bus.lock().unwrap(); - let pci_device = locked_pci_bus.devices.get(&self.devfn); + let pci_device = locked_pci_bus.devices.get(&self.base.devfn); match pci_device { Some(device) => bail!( "Devfn {:?} has been used by {:?}", - &self.devfn, + &self.base.devfn, device.lock().unwrap().name() ), None => locked_pci_bus .devices - .insert(self.devfn, Arc::new(Mutex::new(self))), + .insert(self.base.devfn, Arc::new(Mutex::new(self))), }; Ok(()) } fn init_write_mask(&mut self) -> pci::Result<()> { - self.config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> pci::Result<()> { - self.config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); - self.config.write( + self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), @@ -159,6 +158,6 @@ impl PciDevOps for Ivshmem { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } } diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index c46726a38..3df658167 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -24,7 +24,7 @@ use pci::config::{ PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use pci::msix::update_dev_id; -use pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevOps}; +use pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; use super::xhci_regs::{ @@ -62,12 +62,9 @@ const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; /// XHCI pci device which can be attached to PCI bus. pub struct XhciPciDevice { - pci_config: PciConfig, - devfn: u8, + base: PciDevBase, pub xhci: Arc>, dev_id: Arc, - name: String, - parent_bus: Weak>, mem_region: Region, } @@ -79,12 +76,14 @@ impl XhciPciDevice { mem_space: &Arc, ) -> Self { Self { - pci_config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), - devfn, + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), + devfn, + name: config.id.clone().unwrap(), + parent_bus, + }, xhci: XhciDevice::new(mem_space, config), dev_id: Arc::new(AtomicU16::new(0)), - name: config.id.clone().unwrap(), - parent_bus, mem_region: Region::init_container_region( XHCI_PCI_CONFIG_LENGTH as u64, "XhciPciContainer", @@ -198,66 +197,66 @@ impl XhciPciDevice { impl PciDevOps for XhciPciDevice { fn init_write_mask(&mut self) -> pci::Result<()> { - self.pci_config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> pci::Result<()> { - self.pci_config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn realize(mut self) -> pci::Result<()> { self.init_write_mask()?; self.init_write_clear_mask()?; le_write_u16( - &mut self.pci_config.config, + &mut self.base.config.config, VENDOR_ID as usize, PCI_VENDOR_ID_REDHAT, )?; le_write_u16( - &mut self.pci_config.config, + &mut self.base.config.config, DEVICE_ID as usize, PCI_DEVICE_ID_REDHAT_XHCI, )?; - le_write_u16(&mut self.pci_config.config, REVISION_ID, 0x3_u16)?; + le_write_u16(&mut self.base.config.config, REVISION_ID, 0x3_u16)?; le_write_u16( - &mut self.pci_config.config, + &mut self.base.config.config, SUB_CLASS_CODE as usize, PCI_CLASS_SERIAL_USB, )?; - self.pci_config.config[PCI_CLASS_PI as usize] = 0x30; + self.base.config.config[PCI_CLASS_PI as usize] = 0x30; #[cfg(target_arch = "aarch64")] - self.pci_config.set_interrupt_pin(); + self.base.config.set_interrupt_pin(); - self.pci_config.config[PCI_CACHE_LINE_SIZE as usize] = 0x10; - self.pci_config.config[PCI_SERIAL_BUS_RELEASE_NUMBER as usize] = + self.base.config.config[PCI_CACHE_LINE_SIZE as usize] = 0x10; + self.base.config.config[PCI_SERIAL_BUS_RELEASE_NUMBER as usize] = PCI_SERIAL_BUS_RELEASE_VERSION_3_0; - self.pci_config.config[PCI_FRAME_LENGTH_ADJUSTMENT as usize] = + self.base.config.config[PCI_FRAME_LENGTH_ADJUSTMENT as usize] = PCI_NO_FRAME_LENGTH_TIMING_CAP; - self.dev_id.store(self.devfn as u16, Ordering::SeqCst); + self.dev_id.store(self.base.devfn as u16, Ordering::SeqCst); self.mem_region_init()?; let intrs_num = self.xhci.lock().unwrap().intrs.len() as u32; init_msix( 0_usize, intrs_num, - &mut self.pci_config, + &mut self.base.config, self.dev_id.clone(), - &self.name, + &self.base.name, Some(&self.mem_region), Some((XHCI_MSIX_TABLE_OFFSET, XHCI_MSIX_PBA_OFFSET)), )?; init_intx( - self.name.clone(), - &mut self.pci_config, - self.parent_bus.clone(), - self.devfn, + self.base.name.clone(), + &mut self.base.config, + self.base.parent_bus.clone(), + self.base.devfn, )?; let mut mem_region_size = (XHCI_PCI_CONFIG_LENGTH as u64).next_power_of_two(); mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); - self.pci_config.register_bar( + self.base.config.register_bar( 0_usize, self.mem_region.clone(), RegionType::Mem64Bit, @@ -265,10 +264,10 @@ impl PciDevOps for XhciPciDevice { mem_region_size, )?; - let devfn = self.devfn; + let devfn = self.base.devfn; // It is safe to unwrap, because it is initialized in init_msix. - let cloned_msix = self.pci_config.msix.as_ref().unwrap().clone(); - let cloned_intx = self.pci_config.intx.as_ref().unwrap().clone(); + let cloned_msix = self.base.config.msix.as_ref().unwrap().clone(); + let cloned_intx = self.base.config.intx.as_ref().unwrap().clone(); let cloned_dev_id = self.dev_id.clone(); // Registers the msix to the xhci device for interrupt notification. self.xhci @@ -288,7 +287,7 @@ impl PciDevOps for XhciPciDevice { })); let dev = Arc::new(Mutex::new(self)); // Attach to the PCI bus. - let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); + let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); let mut locked_pci_bus = pci_bus.lock().unwrap(); let pci_device = locked_pci_bus.devices.get(&devfn); if pci_device.is_none() { @@ -308,19 +307,19 @@ impl PciDevOps for XhciPciDevice { } fn devfn(&self) -> Option { - Some(self.devfn) + Some(self.base.devfn) } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.pci_config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { - update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); - let parent_bus = self.parent_bus.upgrade().unwrap(); + update_dev_id(&self.base.parent_bus, self.base.devfn, &self.dev_id); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); - self.pci_config.write( + self.base.config.write( offset, data, self.dev_id.clone().load(Ordering::Acquire), @@ -331,13 +330,13 @@ impl PciDevOps for XhciPciDevice { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { self.xhci.lock().unwrap().reset(); - self.pci_config.reset()?; + self.base.config.reset()?; Ok(()) } diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index cffcf4a98..6af66488f 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -17,35 +17,36 @@ use pci::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }, - le_write_u16, PciBus, PciDevOps, Result as PciResult, + le_write_u16, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; /// PciHost root (Device 0:Function 0). pub struct PciHostRoot { - /// Pci config space. - config: PciConfig, - /// Primary Bus. - parent_bus: Weak>, + base: PciDevBase, } impl PciHostRoot { pub fn new(parent_bus: Weak>) -> Self { Self { - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus, + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + parent_bus, + name: "PCI Host Root".to_string(), + devfn: 0, + }, } } } impl PciDevOps for PciHostRoot { fn init_write_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn realize(mut self) -> PciResult<()> { @@ -53,23 +54,23 @@ impl PciDevOps for PciHostRoot { self.init_write_clear_mask()?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, VENDOR_ID as usize, PCI_VENDOR_ID_REDHAT, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, DEVICE_ID as usize, DEVICE_ID_PCIE_HOST, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, SUB_CLASS_CODE as usize, CLASS_CODE_HOST_BRIDGE, )?; - le_write_u16(&mut self.config.config, REVISION_ID, 0)?; + le_write_u16(&mut self.base.config.config, REVISION_ID, 0)?; - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); parent_bus .lock() .unwrap() @@ -79,14 +80,14 @@ impl PciDevOps for PciHostRoot { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { - self.config.write(offset, data, 0, None); + self.base.config.write(offset, data, 0, None); } fn name(&self) -> String { - "PCI Host Root".to_string() + self.base.name.clone() } } diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 634c7aeaa..4b533fccf 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -21,13 +21,13 @@ use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use anyhow::Context; use log::error; -use pci::config::CLASS_CODE_ISA_BRIDGE; use pci::config::{ - PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, - PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, + PciConfig, CLASS_CODE_ISA_BRIDGE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, + HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, +}; +use pci::{ + le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; -use pci::Result as PciResult; -use pci::{le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevOps}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; @@ -43,8 +43,7 @@ pub const RST_CTRL_OFFSET: u16 = 0xCF9; /// LPC bridge of ICH9 (IO controller hub 9), Device 1F : Function 0 #[allow(clippy::upper_case_acronyms)] pub struct LPCBridge { - config: PciConfig, - parent_bus: Weak>, + base: PciDevBase, sys_io: Arc, pm_timer: Arc>, rst_ctrl: Arc, @@ -63,8 +62,12 @@ impl LPCBridge { shutdown_req: Arc, ) -> Result { Ok(Self { - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus, + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn: 0x1F << 3, + name: "ICH9 LPC bridge".to_string(), + parent_bus, + }, sys_io, pm_timer: Arc::new(Mutex::new(AcpiPMTimer::new())), pm_evt: Arc::new(Mutex::new(AcpiPmEvent::new())), @@ -88,7 +91,8 @@ impl LPCBridge { let pmtmr_region = Region::init_io_region(0x8, ops, "PmtmrRegion"); let mut pm_base_addr = 0_u32; - self.config + self.base + .config .read(PM_BASE_OFFSET as usize, pm_base_addr.as_mut_bytes()); self.sys_io .root() @@ -226,31 +230,39 @@ impl LPCBridge { impl PciDevOps for LPCBridge { fn init_write_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn realize(mut self) -> PciResult<()> { self.init_write_mask()?; self.init_write_clear_mask()?; - le_write_u16(&mut self.config.config, VENDOR_ID as usize, VENDOR_ID_INTEL)?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, + VENDOR_ID as usize, + VENDOR_ID_INTEL, + )?; + le_write_u16( + &mut self.base.config.config, DEVICE_ID as usize, DEVICE_ID_INTEL_ICH9, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, SUB_CLASS_CODE as usize, CLASS_CODE_ISA_BRIDGE, )?; - le_write_u32(&mut self.config.write_mask, PM_BASE_OFFSET as usize, 0xff80)?; + le_write_u32( + &mut self.base.config.write_mask, + PM_BASE_OFFSET as usize, + 0xff80, + )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, HEADER_TYPE as usize, (HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC) as u16, )?; @@ -266,7 +278,7 @@ impl PciDevOps for LPCBridge { self.init_pm_ctrl_reg() .with_context(|| "Fail to init IO region for PM control register")?; - let parent_bus = self.parent_bus.clone(); + let parent_bus = self.base.parent_bus.clone(); parent_bus .upgrade() .unwrap() @@ -278,11 +290,11 @@ impl PciDevOps for LPCBridge { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { - self.config.write(offset, data, 0, None, None); + self.base.config.write(offset, data, 0, None, None); if ranges_overlap(offset, data.len(), PM_BASE_OFFSET as usize, 4) { if let Err(e) = self.update_pm_base() { error!("Failed to update PM base addr: {:?}", e); diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 3f7e29d65..71c042a6c 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -20,7 +20,7 @@ use pci::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }, - le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevOps, Result as PciResult, + le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; use super::VENDOR_ID_INTEL; @@ -41,8 +41,7 @@ const PCIEXBAR_RESERVED_MASK: u64 = 0x3ff_fff8; /// Memory controller hub (Device 0:Function 0) pub struct Mch { - config: PciConfig, - parent_bus: Weak>, + base: PciDevBase, mmconfig_region: Option, mmconfig_ops: RegionOps, } @@ -54,15 +53,19 @@ impl Mch { mmconfig_ops: RegionOps, ) -> Self { Self { - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus, + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn: 0, + name: "Memory Controller Hub".to_string(), + parent_bus, + }, mmconfig_region: Some(mmconfig_region), mmconfig_ops, } } fn update_pciexbar_mapping(&mut self) -> Result<()> { - let pciexbar: u64 = le_read_u64(&self.config.config, PCIEXBAR as usize)?; + let pciexbar: u64 = le_read_u64(&self.base.config.config, PCIEXBAR as usize)?; let enable = pciexbar & PCIEXBAR_ENABLE_MASK; let length: u64; let mut addr_mask: u64 = PCIEXBAR_ADDR_MASK; @@ -80,7 +83,8 @@ impl Mch { } if let Some(region) = self.mmconfig_region.as_ref() { - self.parent_bus + self.base + .parent_bus .upgrade() .unwrap() .lock() @@ -92,7 +96,8 @@ impl Mch { if enable == 0x1 { let region = Region::init_io_region(length, self.mmconfig_ops.clone(), "PcieXBar"); let base_addr: u64 = pciexbar & addr_mask; - self.parent_bus + self.base + .parent_bus .upgrade() .unwrap() .lock() @@ -104,7 +109,7 @@ impl Mch { } fn check_pciexbar_update(&self, old_pciexbar: u64) -> bool { - let cur_pciexbar: u64 = le_read_u64(&self.config.config, PCIEXBAR as usize).unwrap(); + let cur_pciexbar: u64 = le_read_u64(&self.base.config.config, PCIEXBAR as usize).unwrap(); if (cur_pciexbar & !PCIEXBAR_RESERVED_MASK) == (old_pciexbar & !PCIEXBAR_RESERVED_MASK) { return false; @@ -115,30 +120,34 @@ impl Mch { impl PciDevOps for Mch { fn init_write_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn realize(mut self) -> PciResult<()> { self.init_write_mask()?; self.init_write_clear_mask()?; - le_write_u16(&mut self.config.config, VENDOR_ID as usize, VENDOR_ID_INTEL)?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, + VENDOR_ID as usize, + VENDOR_ID_INTEL, + )?; + le_write_u16( + &mut self.base.config.config, DEVICE_ID as usize, DEVICE_ID_INTEL_Q35_MCH, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, SUB_CLASS_CODE as usize, CLASS_CODE_HOST_BRIDGE, )?; - let parent_bus = self.parent_bus.clone(); + let parent_bus = self.base.parent_bus.clone(); parent_bus .upgrade() .unwrap() @@ -150,12 +159,12 @@ impl PciDevOps for Mch { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { - let old_pciexbar: u64 = le_read_u64(&self.config.config, PCIEXBAR as usize).unwrap(); - self.config.write(offset, data, 0, None, None); + let old_pciexbar: u64 = le_read_u64(&self.base.config.config, PCIEXBAR as usize).unwrap(); + self.base.config.write(offset, data, 0, None, None); if ranges_overlap(offset, data.len(), PCIEXBAR as usize, 8) && self.check_pciexbar_update(old_pciexbar) diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 3f89613c7..af2020657 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -255,15 +255,12 @@ mod tests { use crate::bus::PciBus; use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; use crate::root_port::RootPort; - use crate::PciHost; + use crate::{PciDevBase, PciHost}; use anyhow::Result; #[derive(Clone)] struct PciDevice { - name: String, - devfn: u8, - config: PciConfig, - parent_bus: Weak>, + base: PciDevBase, } impl PciDevOps for PciDevice { @@ -276,12 +273,12 @@ mod tests { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { #[allow(unused_variables)] - self.config.write( + self.base.config.write( offset, data, 0, @@ -292,17 +289,18 @@ mod tests { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } fn realize(mut self) -> Result<()> { - let devfn = self.devfn; + let devfn = self.base.devfn; self.init_write_mask()?; self.init_write_clear_mask()?; let dev = Arc::new(Mutex::new(self)); dev.lock() .unwrap() + .base .parent_bus .upgrade() .unwrap() @@ -356,20 +354,24 @@ mod tests { // Test device is attached to the root bus. let pci_dev = PciDevice { - name: String::from("test1"), - devfn: 10, - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus: root_bus.clone(), + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn: 10, + name: String::from("test1"), + parent_bus: root_bus.clone(), + }, }; pci_dev.realize().unwrap(); // Test device is attached to the root port. let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = PciDevice { - name: String::from("test2"), - devfn: 12, - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus: Arc::downgrade(&bus), + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn: 12, + name: String::from("test2"), + parent_bus: Arc::downgrade(&bus), + }, }; pci_dev.realize().unwrap(); @@ -400,10 +402,12 @@ mod tests { let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = PciDevice { - name: String::from("test1"), - devfn: 0, - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus: Arc::downgrade(&bus), + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn: 0, + name: String::from("test1"), + parent_bus: Arc::downgrade(&bus), + }, }; let dev = Arc::new(Mutex::new(pci_dev.clone())); let dev_ops: Arc> = dev; diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index ca56a45dd..7ec812aa5 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -41,7 +41,6 @@ use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use log::error; use machine_manager::config::DemoDevConfig; -use crate::demo_device::base_device::BaseDevice; #[cfg(not(target_env = "musl"))] use crate::demo_device::{ dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, @@ -53,15 +52,13 @@ use crate::{ }, init_msix, le_write_u16, PciBus, PciDevOps, }; +use crate::{demo_device::base_device::BaseDevice, PciDevBase}; pub use anyhow::{bail, Result}; pub struct DemoDev { - name: String, + base: PciDevBase, cmd_cfg: DemoDevConfig, - config: PciConfig, mem_region: Region, - devfn: u8, - parent_bus: Weak>, dev_id: Arc, device: Arc>, } @@ -84,12 +81,14 @@ impl DemoDev { _ => Arc::new(Mutex::new(BaseDevice::new())), }; DemoDev { - name: cfg.id.clone(), - cmd_cfg: cfg.clone(), - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), + base: PciDevBase { + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), + devfn, + name: cfg.id.clone(), + parent_bus, + }, + cmd_cfg: cfg, mem_region: Region::init_container_region(u32::MAX as u64, "DemoDev"), - devfn, - parent_bus, dev_id: Arc::new(AtomicU16::new(0)), device, } @@ -99,7 +98,7 @@ impl DemoDev { self.init_write_mask()?; self.init_write_clear_mask()?; - let config = &mut self.config.config; + let config = &mut self.base.config.config; le_write_u16(config, DEVICE_ID as usize, DEVICE_ID_DEMO)?; le_write_u16(config, VENDOR_ID as usize, VENDOR_ID_DEMO)?; le_write_u16(config, SUB_CLASS_CODE as usize, CLASS_CODE_DEMO)?; @@ -109,12 +108,12 @@ impl DemoDev { } fn attach_to_parent_bus(self) -> Result<()> { - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let mut locked_parent_bus = parent_bus.lock().unwrap(); - if locked_parent_bus.devices.get(&self.devfn).is_some() { + if locked_parent_bus.devices.get(&self.base.devfn).is_some() { bail!("device already existed"); } - let devfn = self.devfn; + let devfn = self.base.devfn; let demo_pci_dev = Arc::new(Mutex::new(self)); locked_parent_bus.devices.insert(devfn, demo_pci_dev); @@ -150,7 +149,7 @@ impl DemoDev { let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops, "DemoRegion"); self.mem_region.add_subregion(region, 0)?; - self.config.register_bar( + self.base.config.register_bar( 0, self.mem_region.clone(), crate::config::RegionType::Mem64Bit, @@ -171,11 +170,11 @@ const CLASS_CODE_DEMO: u16 = 0xEE; impl PciDevOps for DemoDev { fn init_write_mask(&mut self) -> Result<()> { - self.config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> Result<()> { - self.config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } /// Realize PCI/PCIe device. @@ -186,9 +185,9 @@ impl PciDevOps for DemoDev { init_msix( 0, 1, - &mut self.config, + &mut self.base.config, self.dev_id.clone(), - &self.name, + &self.base.name, None, None, )?; @@ -208,15 +207,15 @@ impl PciDevOps for DemoDev { /// read the pci configuration space fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let parent_bus_locked = parent_bus.lock().unwrap(); - self.config.write( + self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), @@ -227,17 +226,17 @@ impl PciDevOps for DemoDev { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } /// Reset device fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - self.config.reset_common_regs() + self.base.config.reset_common_regs() } /// Get device devfn fn devfn(&self) -> Option { - Some(self.devfn) + Some(self.base.devfn) } } diff --git a/pci/src/host.rs b/pci/src/host.rs index 42f04e41c..344749763 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -518,8 +518,6 @@ impl AmlBuilder for PciHost { #[cfg(test)] pub mod tests { - use std::sync::Weak; - use address_space::Region; use byteorder::{ByteOrder, LittleEndian}; @@ -527,20 +525,18 @@ pub mod tests { use crate::bus::PciBus; use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; use crate::root_port::RootPort; - use crate::Result; + use crate::{PciDevBase, Result}; struct PciDevice { - devfn: u8, - config: PciConfig, - parent_bus: Weak>, + base: PciDevBase, } impl PciDevOps for PciDevice { fn init_write_mask(&mut self) -> Result<()> { let mut offset = 0_usize; - while offset < self.config.config.len() { + while offset < self.base.config.config.len() { LittleEndian::write_u32( - &mut self.config.write_mask[offset..offset + 4], + &mut self.base.config.write_mask[offset..offset + 4], 0xffff_ffff, ); offset += 4; @@ -553,12 +549,12 @@ pub mod tests { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { #[allow(unused_variables)] - self.config.write( + self.base.config.write( offset, data, 0, @@ -573,13 +569,14 @@ pub mod tests { } fn realize(mut self) -> Result<()> { - let devfn = self.devfn; + let devfn = self.base.devfn; self.init_write_mask()?; self.init_write_clear_mask()?; let dev = Arc::new(Mutex::new(self)); dev.lock() .unwrap() + .base .parent_bus .upgrade() .unwrap() @@ -707,9 +704,12 @@ pub mod tests { let bus = PciBus::find_bus_by_name(&pci_host.lock().unwrap().root_bus, "pcie.2").unwrap(); let pci_dev = PciDevice { - devfn: 8, - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus: Arc::downgrade(&bus), + base: PciDevBase { + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn: 8, + name: "PCI device".to_string(), + parent_bus: Arc::downgrade(&bus), + }, }; pci_dev.realize().unwrap(); diff --git a/pci/src/lib.rs b/pci/src/lib.rs index a5504d36a..7b397ab32 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -127,6 +127,18 @@ pub fn pci_ext_cap_next(header: u32) -> usize { ((header >> 20) & 0xffc) as usize } +#[derive(Clone)] +pub struct PciDevBase { + /// Pci config space. + pub config: PciConfig, + /// Devfn. + pub devfn: u8, + /// Name of this device. + pub name: String, + /// Primary Bus. + pub parent_bus: Weak>, +} + pub trait PciDevOps: Send + AsAny { /// Init writable bit mask. fn init_write_mask(&mut self) -> Result<()>; @@ -330,6 +342,8 @@ pub fn ranges_overlap(start: usize, size: usize, range_start: usize, range_size: #[cfg(test)] mod tests { + use address_space::{AddressSpace, Region}; + use super::*; #[test] @@ -384,7 +398,7 @@ mod tests { #[test] fn set_dev_id() { struct PciDev { - name: String, + base: PciDevBase, } impl PciDevOps for PciDev { @@ -401,7 +415,7 @@ mod tests { fn write_config(&mut self, _offset: usize, _data: &[u8]) {} fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } fn realize(self) -> Result<()> { @@ -409,8 +423,25 @@ mod tests { } } + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sysmem"), + "sysmem", + ) + .unwrap(); + let parent_bus: Arc> = Arc::new(Mutex::new(PciBus::new( + String::from("test bus"), + #[cfg(target_arch = "x86_64")] + Region::init_container_region(1 << 16, "parent_bus"), + sys_mem.root().clone(), + ))); + let dev = PciDev { - name: "PCI device".to_string(), + base: PciDevBase { + config: PciConfig::new(1, 1), + devfn: 0, + name: "PCI device".to_string(), + parent_bus: Arc::downgrade(&parent_bus), + }, }; assert_eq!(dev.set_dev_id(1, 2), 258); } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 5996be629..c0b314aa7 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -40,7 +40,7 @@ use crate::config::{BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET}; use crate::hotplug::HotplugOps; use crate::intx::init_intx; use crate::msix::init_msix; -use crate::{init_multifunction, PciError, PciIntxState, INTERRUPT_PIN}; +use crate::{init_multifunction, PciDevBase, PciError, PciIntxState, INTERRUPT_PIN}; use crate::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, PciDevOps, @@ -65,11 +65,8 @@ struct RootPortState { } pub struct RootPort { - name: String, - devfn: u8, + base: PciDevBase, port_num: u8, - config: PciConfig, - parent_bus: Weak>, sec_bus: Arc>, #[cfg(target_arch = "x86_64")] io_region: Region, @@ -106,11 +103,13 @@ impl RootPort { ))); Self { - name, - devfn, + base: PciDevBase { + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), + devfn, + name, + parent_bus, + }, port_num, - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), - parent_bus, sec_bus, #[cfg(target_arch = "x86_64")] io_region, @@ -123,8 +122,8 @@ impl RootPort { fn hotplug_command_completed(&mut self) { if let Err(e) = le_write_set_value_u16( - &mut self.config.config, - (self.config.pci_express_cap_offset + PCI_EXP_SLTSTA) as usize, + &mut self.base.config.config, + (self.base.config.pci_express_cap_offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_HP_EV_CCI, ) { error!("{}", format!("{:?}", e)); @@ -133,11 +132,17 @@ impl RootPort { } fn update_hp_event_status(&mut self) { - let cap_offset = self.config.pci_express_cap_offset; - let slot_status = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); - let slot_control = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize).unwrap(); + let cap_offset = self.base.config.pci_express_cap_offset; + let slot_status = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + ) + .unwrap(); + let slot_control = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTCTL) as usize, + ) + .unwrap(); self.hpev_notified = (slot_control & PCI_EXP_SLTCTL_HPIE != 0) && (slot_status & slot_control & PCI_EXP_HP_EV_SPT != 0); @@ -150,12 +155,12 @@ impl RootPort { return; } - let msix = self.config.msix.as_ref().unwrap(); - let intx = self.config.intx.as_ref().unwrap(); + let msix = self.base.config.msix.as_ref().unwrap(); + let intx = self.base.config.intx.as_ref().unwrap(); let mut locked_msix = msix.lock().unwrap(); if locked_msix.enabled { locked_msix.notify(0, self.dev_id.load(Ordering::Acquire)); - } else if self.config.config[INTERRUPT_PIN as usize] != 0 { + } else if self.base.config.config[INTERRUPT_PIN as usize] != 0 { intx.lock().unwrap().notify(self.hpev_notified as u8); } } @@ -163,10 +168,10 @@ impl RootPort { fn hotplug_event_clear(&mut self) { self.update_hp_event_status(); - let msix = self.config.msix.as_ref().unwrap(); - let intx = self.config.intx.as_ref().unwrap(); + let msix = self.base.config.msix.as_ref().unwrap(); + let intx = self.base.config.intx.as_ref().unwrap(); let locked_msix = msix.lock().unwrap(); - let intr_pin = self.config.config[INTERRUPT_PIN as usize]; + let intr_pin = self.base.config.config[INTERRUPT_PIN as usize]; if !locked_msix.enabled && intr_pin != 0 && !self.hpev_notified { intx.lock().unwrap().notify(0); } @@ -174,19 +179,19 @@ impl RootPort { /// Update register when the guest OS trigger the removal of the device. fn update_register_status(&mut self) -> Result<()> { - let cap_offset = self.config.pci_express_cap_offset; + let cap_offset = self.base.config.pci_express_cap_offset; le_write_clear_value_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_SLTSTA_PDS, )?; le_write_clear_value_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_LNKSTA) as usize, PCI_EXP_LNKSTA_DLLLA, )?; le_write_set_value_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_SLTSTA_PDC, )?; @@ -205,7 +210,11 @@ impl RootPort { error!("{}", format!("{:?}", e)); error!("Failed to unrealize device {}.", locked_dev.name()); } - info!("Device {} unplug from {}", locked_dev.name(), self.name); + info!( + "Device {} unplug from {}", + locked_dev.name(), + self.base.name + ); // Send QMP event for successful hot unplugging. send_device_deleted_msg(&locked_dev.name()); @@ -214,10 +223,11 @@ impl RootPort { } fn register_region(&mut self) { - let command: u16 = le_read_u16(&self.config.config, COMMAND as usize).unwrap(); + let command: u16 = le_read_u16(&self.base.config.config, COMMAND as usize).unwrap(); if command & COMMAND_IO_SPACE != 0 { #[cfg(target_arch = "x86_64")] if let Err(e) = self + .base .parent_bus .upgrade() .unwrap() @@ -232,6 +242,7 @@ impl RootPort { } if command & COMMAND_MEMORY_SPACE != 0 { if let Err(e) = self + .base .parent_bus .upgrade() .unwrap() @@ -248,19 +259,22 @@ impl RootPort { fn correct_race_unplug(&mut self, offset: usize, data: &[u8], old_status: u16) { let size = data.len(); - let cap_offset = self.config.pci_express_cap_offset; + let cap_offset = self.base.config.pci_express_cap_offset; if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTSTA) as usize, 2) { return; } - let status = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let status = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + ) + .unwrap(); let val: u16 = data[0] as u16 + ((data[1] as u16) << 8); if (val & !old_status & PCI_EXP_SLOTSTA_EVENTS) != 0 { let tmpstat = (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); le_write_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, tmpstat, ) @@ -272,15 +286,18 @@ impl RootPort { self.correct_race_unplug(offset, data, old_status); let size = data.len(); - let cap_offset = self.config.pci_express_cap_offset; + let cap_offset = self.base.config.pci_express_cap_offset; // Only care the write config about slot control if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTCTL) as usize, 2) { return; } - let status = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); - let val = le_read_u16(&self.config.config, offset).unwrap(); + let status = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + ) + .unwrap(); + let val = le_read_u16(&self.base.config.config, offset).unwrap(); // Only unplug device when the slot is on // Don't unplug when slot is off for guest OS overwrite the off status before slot on. if (status & PCI_EXP_SLTSTA_PDS != 0) @@ -313,20 +330,20 @@ impl RootPort { impl PciDevOps for RootPort { fn init_write_mask(&mut self) -> Result<()> { - self.config.init_common_write_mask()?; - self.config.init_bridge_write_mask() + self.base.config.init_common_write_mask()?; + self.base.config.init_bridge_write_mask() } fn init_write_clear_mask(&mut self) -> Result<()> { - self.config.init_common_write_clear_mask()?; - self.config.init_bridge_write_clear_mask() + self.base.config.init_common_write_clear_mask()?; + self.base.config.init_bridge_write_clear_mask() } fn realize(mut self) -> Result<()> { self.init_write_mask()?; self.init_write_clear_mask()?; - let config_space = &mut self.config.config; + let config_space = &mut self.base.config.config; le_write_u16(config_space, VENDOR_ID as usize, PCI_VENDOR_ID_REDHAT)?; le_write_u16(config_space, DEVICE_ID as usize, DEVICE_ID_RP)?; le_write_u16(config_space, SUB_CLASS_CODE as usize, CLASS_CODE_PCI_BRIDGE)?; @@ -337,35 +354,38 @@ impl PciDevOps for RootPort { init_multifunction( self.multifunction, config_space, - self.devfn, - self.parent_bus.clone(), + self.base.devfn, + self.base.parent_bus.clone(), )?; #[cfg(target_arch = "aarch64")] - self.config.set_interrupt_pin(); + self.base.config.set_interrupt_pin(); - self.config - .add_pcie_cap(self.devfn, self.port_num, PcieDevType::RootPort as u8)?; + self.base.config.add_pcie_cap( + self.base.devfn, + self.port_num, + PcieDevType::RootPort as u8, + )?; - self.dev_id.store(self.devfn as u16, Ordering::SeqCst); + self.dev_id.store(self.base.devfn as u16, Ordering::SeqCst); init_msix( 0, 1, - &mut self.config, + &mut self.base.config, self.dev_id.clone(), - &self.name, + &self.base.name, None, None, )?; init_intx( - self.name.clone(), - &mut self.config, - self.parent_bus.clone(), - self.devfn, + self.base.name.clone(), + &mut self.base.config, + self.base.parent_bus.clone(), + self.base.devfn, )?; - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let mut locked_parent_bus = parent_bus.lock().unwrap(); #[cfg(target_arch = "x86_64")] locked_parent_bus @@ -377,7 +397,7 @@ impl PciDevOps for RootPort { .add_subregion(self.sec_bus.lock().unwrap().mem_region.clone(), 0) .with_context(|| "Failed to register subregion in memory space.")?; - let name = self.name.clone(); + let name = self.base.name.clone(); let root_port = Arc::new(Mutex::new(self)); #[allow(unused_mut)] let mut locked_root_port = root_port.lock().unwrap(); @@ -385,18 +405,18 @@ impl PciDevOps for RootPort { Some(Arc::downgrade(&root_port) as Weak>); locked_root_port.sec_bus.lock().unwrap().hotplug_controller = Some(Arc::downgrade(&root_port) as Weak>); - let pci_device = locked_parent_bus.devices.get(&locked_root_port.devfn); + let pci_device = locked_parent_bus.devices.get(&locked_root_port.base.devfn); if pci_device.is_none() { locked_parent_bus .child_buses .push(locked_root_port.sec_bus.clone()); locked_parent_bus .devices - .insert(locked_root_port.devfn, root_port.clone()); + .insert(locked_root_port.base.devfn, root_port.clone()); } else { bail!( "Devfn {:?} has been used by {:?}", - locked_root_port.devfn, + locked_root_port.base.devfn, pci_device.unwrap().lock().unwrap().name() ); } @@ -408,7 +428,7 @@ impl PciDevOps for RootPort { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { @@ -422,15 +442,21 @@ impl PciDevOps for RootPort { return; } - let cap_offset = self.config.pci_express_cap_offset; - let old_ctl = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize).unwrap(); - let old_status = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let cap_offset = self.base.config.pci_express_cap_offset; + let old_ctl = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTCTL) as usize, + ) + .unwrap(); + let old_status = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + ) + .unwrap(); - let old_br_ctl = le_read_u16(&self.config.config, BRIDGE_CONTROL.into()).unwrap(); + let old_br_ctl = le_read_u16(&self.base.config.config, BRIDGE_CONTROL.into()).unwrap(); - self.config.write( + self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), @@ -439,12 +465,12 @@ impl PciDevOps for RootPort { Some(&self.mem_region), ); - let new_br_ctl = le_read_u16(&self.config.config, BRIDGE_CONTROL.into()).unwrap(); + let new_br_ctl = le_read_u16(&self.base.config.config, BRIDGE_CONTROL.into()).unwrap(); if (!old_br_ctl & new_br_ctl & BRIDGE_CTL_SEC_BUS_RESET) != 0 { if let Err(e) = self.reset(true) { error!( "Failed to reset child devices under root port {}: {:?}", - self.name, e + self.base.name, e ) } } @@ -456,15 +482,18 @@ impl PciDevOps for RootPort { self.register_region(); } - let mut status = - le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap(); + let mut status = le_read_u16( + &self.base.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + ) + .unwrap(); let exp_slot_status = (cap_offset + PCI_EXP_SLTSTA) as usize; if ranges_overlap(offset, size, exp_slot_status, 2) { let new_status = le_read_u16(data, 0).unwrap(); if new_status & !old_status & PCI_EXP_SLOTSTA_EVENTS != 0 { status = (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); if let Err(e) = le_write_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, status, ) { @@ -477,11 +506,11 @@ impl PciDevOps for RootPort { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } fn devfn(&self) -> Option { - Some(self.devfn) + Some(self.base.devfn) } /// Only set slot status to on, and no other device reset actions are implemented. @@ -493,37 +522,37 @@ impl PciDevOps for RootPort { .reset() .with_context(|| "Fail to reset sec_bus in root port")?; } else { - let cap_offset = self.config.pci_express_cap_offset; + let cap_offset = self.base.config.pci_express_cap_offset; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_SLTSTA_PDS, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_SLTCTL) as usize, !PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PWR_IND_ON, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, (cap_offset + PCI_EXP_LNKSTA) as usize, PCI_EXP_LNKSTA_DLLLA, )?; } - self.config.reset_bridge_regs()?; - self.config.reset() + self.base.config.reset_bridge_regs()?; + self.base.config.reset() } fn get_dev_path(&self) -> Option { - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let parent_dev_path = self.get_parent_dev_path(parent_bus); - let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/pci-bridge@"); + let dev_path = self.populate_dev_path(parent_dev_path, self.base.devfn, "/pci-bridge@"); Some(dev_path) } fn get_intx_state(&self) -> Option>> { - let intx = self.config.intx.as_ref().unwrap(); + let intx = self.base.config.intx.as_ref().unwrap(); if intx.lock().unwrap().intx_state.is_some() { let intx_state = intx.lock().unwrap().intx_state.as_ref().unwrap().clone(); return Some(intx_state); @@ -545,14 +574,14 @@ impl HotplugOps for RootPort { return Err(anyhow!(PciError::HotplugUnsupported(devfn))); } - let offset = self.config.pci_express_cap_offset; + let offset = self.base.config.pci_express_cap_offset; le_write_set_value_u16( - &mut self.config.config, + &mut self.base.config.config, (offset + PCI_EXP_SLTSTA) as usize, PCI_EXP_SLTSTA_PDS | PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP, )?; le_write_set_value_u16( - &mut self.config.config, + &mut self.base.config.config, (offset + PCI_EXP_LNKSTA) as usize, PCI_EXP_LNKSTA_CLS_2_5GB | PCI_EXP_LNKSTA_NLW_X1 | PCI_EXP_LNKSTA_DLLLA, )?; @@ -562,9 +591,9 @@ impl HotplugOps for RootPort { } fn unplug_request(&mut self, dev: &Arc>) -> Result<()> { - let pcie_cap_offset = self.config.pci_express_cap_offset; + let pcie_cap_offset = self.base.config.pci_express_cap_offset; let sltctl = le_read_u16( - &self.config.config, + &self.base.config.config, (pcie_cap_offset + PCI_EXP_SLTCTL) as usize, ) .unwrap(); @@ -582,9 +611,9 @@ impl HotplugOps for RootPort { return self.unplug(dev); } - let offset = self.config.pci_express_cap_offset; + let offset = self.base.config.pci_express_cap_offset; le_write_clear_value_u16( - &mut self.config.config, + &mut self.base.config.config, (offset + PCI_EXP_LNKSTA) as usize, PCI_EXP_LNKSTA_DLLLA, )?; @@ -594,7 +623,7 @@ impl HotplugOps for RootPort { slot_status |= PCI_EXP_HP_EV_PDC; } le_write_set_value_u16( - &mut self.config.config, + &mut self.base.config.config, (offset + PCI_EXP_SLTSTA) as usize, slot_status, )?; @@ -607,7 +636,7 @@ impl HotplugOps for RootPort { } le_write_set_value_u16( - &mut self.config.config, + &mut self.base.config.config, (offset + PCI_EXP_SLTSTA) as usize, slot_status | PCI_EXP_HP_EV_ABP, )?; @@ -632,14 +661,14 @@ impl StateTransfer for RootPort { fn get_state_vec(&self) -> Result> { let mut state = RootPortState::default(); - for idx in 0..self.config.config.len() { - state.config_space[idx] = self.config.config[idx]; - state.write_mask[idx] = self.config.write_mask[idx]; - state.write_clear_mask[idx] = self.config.write_clear_mask[idx]; + for idx in 0..self.base.config.config.len() { + state.config_space[idx] = self.base.config.config[idx]; + state.write_mask[idx] = self.base.config.write_mask[idx]; + state.write_clear_mask[idx] = self.base.config.write_clear_mask[idx]; } - state.last_cap_end = self.config.last_cap_end; - state.last_ext_cap_end = self.config.last_ext_cap_end; - state.last_ext_cap_offset = self.config.last_ext_cap_offset; + state.last_cap_end = self.base.config.last_cap_end; + state.last_ext_cap_end = self.base.config.last_ext_cap_end; + state.last_ext_cap_offset = self.base.config.last_ext_cap_offset; Ok(state.as_bytes().to_vec()) } @@ -648,13 +677,13 @@ impl StateTransfer for RootPort { let root_port_state = *RootPortState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("ROOT_PORT"))?; - let length = self.config.config.len(); - self.config.config = root_port_state.config_space[..length].to_vec(); - self.config.write_mask = root_port_state.write_mask[..length].to_vec(); - self.config.write_clear_mask = root_port_state.write_clear_mask[..length].to_vec(); - self.config.last_cap_end = root_port_state.last_cap_end; - self.config.last_ext_cap_end = root_port_state.last_ext_cap_end; - self.config.last_ext_cap_offset = root_port_state.last_ext_cap_offset; + let length = self.base.config.config.len(); + self.base.config.config = root_port_state.config_space[..length].to_vec(); + self.base.config.write_mask = root_port_state.write_mask[..length].to_vec(); + self.base.config.write_clear_mask = root_port_state.write_clear_mask[..length].to_vec(); + self.base.config.last_cap_end = root_port_state.last_cap_end; + self.base.config.last_ext_cap_end = root_port_state.last_ext_cap_end; + self.base.config.last_ext_cap_offset = root_port_state.last_ext_cap_offset; Ok(()) } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index cbef6e1f8..702248dc1 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -37,7 +37,7 @@ use pci::msix::{ }; use pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, - pci_ext_cap_next, pci_ext_cap_ver, ranges_overlap, PciBus, PciDevOps, + pci_ext_cap_next, pci_ext_cap_ver, ranges_overlap, PciBus, PciDevBase, PciDevOps, }; use util::unix::host_page_size; use vfio_bindings::bindings::vfio; @@ -81,7 +81,7 @@ struct GsiMsiRoute { /// VfioPciDevice is a VFIO PCI device. It implements PciDevOps trait for a PCI device. /// And it is bound to a VFIO device. pub struct VfioPciDevice { - pci_config: PciConfig, + base: PciDevBase, config_size: u64, // Offset of pci config space region within vfio device fd. config_offset: u64, @@ -93,10 +93,7 @@ pub struct VfioPciDevice { vfio_bars: Arc>>, // Maintains a list of GSI with irqfds that are registered to kvm. gsi_msi_routes: Arc>>, - devfn: u8, dev_id: Arc, - name: String, - parent_bus: Weak>, // Multi-Function flag. multi_func: bool, mem_as: Arc, @@ -114,17 +111,19 @@ impl VfioPciDevice { ) -> Self { Self { // Unknown PCI or PCIe type here, allocate enough space to match the two types. - pci_config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), + base: PciDevBase { + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), + devfn, + name, + parent_bus, + }, config_size: 0, config_offset: 0, vfio_device, msix_info: None, vfio_bars: Arc::new(Mutex::new(Vec::with_capacity(PCI_NUM_BARS as usize))), gsi_msi_routes: Arc::new(Mutex::new(Vec::new())), - devfn, dev_id: Arc::new(AtomicU16::new(0)), - name, - parent_bus, multi_func, mem_as, } @@ -156,7 +155,7 @@ impl VfioPciDevice { self.config_offset = info.offset; let mut config_data = vec![0_u8; self.config_size as usize]; locked_dev.read_region(config_data.as_mut_slice(), self.config_offset, 0)?; - self.pci_config.config[..PCI_CONFIG_SPACE_SIZE] + self.base.config.config[..PCI_CONFIG_SPACE_SIZE] .copy_from_slice(&config_data[..PCI_CONFIG_SPACE_SIZE]); // If guest OS can not see extended caps, just ignore them. @@ -186,9 +185,10 @@ impl VfioPciDevice { continue; } let offset = self - .pci_config + .base + .config .add_pcie_ext_cap(cap_id, size, cap_version)?; - self.pci_config.config[offset..offset + size] + self.base.config.config[offset..offset + size] .copy_from_slice(&config.config[old_next..old_next + size]); } @@ -198,12 +198,12 @@ impl VfioPciDevice { /// Disable I/O, MMIO, bus master and INTx states, And clear host device bar size information. /// Guest OS can get residual addresses from the host if not clear bar size. fn pci_config_reset(&mut self) -> Result<()> { - let mut cmd = le_read_u16(&self.pci_config.config, COMMAND as usize)?; + let mut cmd = le_read_u16(&self.base.config.config, COMMAND as usize)?; cmd &= !(COMMAND_IO_SPACE | COMMAND_MEMORY_SPACE | COMMAND_BUS_MASTER | COMMAND_INTERRUPT_DISABLE); - le_write_u16(&mut self.pci_config.config, COMMAND as usize, cmd)?; + le_write_u16(&mut self.base.config.config, COMMAND as usize, cmd)?; let mut data = vec![0u8; 2]; LittleEndian::write_u16(&mut data, cmd); @@ -215,12 +215,12 @@ impl VfioPciDevice { for i in 0..PCI_ROM_SLOT { let offset = BAR_0 as usize + REG_SIZE * i as usize; - let v = le_read_u32(&self.pci_config.config, offset)?; + let v = le_read_u32(&self.base.config.config, offset)?; if v & BAR_IO_SPACE as u32 != 0 { - le_write_u32(&mut self.pci_config.config, offset, v & !IO_BASE_ADDR_MASK)?; + le_write_u32(&mut self.base.config.config, offset, v & !IO_BASE_ADDR_MASK)?; } else { le_write_u32( - &mut self.pci_config.config, + &mut self.base.config.config, offset, v & !MEM_BASE_ADDR_MASK as u32, )?; @@ -238,14 +238,14 @@ impl VfioPciDevice { .get_irqs_info(n) .with_context(|| "Failed to get vfio irqs info")?; - let cap_offset = self.pci_config.find_pci_cap(MSIX_CAP_ID); + let cap_offset = self.base.config.find_pci_cap(MSIX_CAP_ID); let table = le_read_u32( - &self.pci_config.config, + &self.base.config.config, cap_offset + MSIX_CAP_TABLE as usize, )?; let ctrl = le_read_u16( - &self.pci_config.config, + &self.base.config.config, cap_offset + MSIX_CAP_CONTROL as usize, )?; let entries = (ctrl & MSIX_TABLE_SIZE_MAX) + 1; @@ -426,7 +426,7 @@ impl VfioPciDevice { region }; - self.pci_config.register_bar( + self.base.config.register_bar( i as usize, bar_region, vfio_bar.region_type, @@ -439,8 +439,8 @@ impl VfioPciDevice { } fn unregister_bars(&mut self) -> Result<()> { - let bus = self.parent_bus.upgrade().unwrap(); - self.pci_config.unregister_bars(&bus)?; + let bus = self.base.parent_bus.upgrade().unwrap(); + self.base.config.unregister_bars(&bus)?; Ok(()) } @@ -451,11 +451,11 @@ impl VfioPciDevice { .as_ref() .with_context(|| "Failed to get MSIX info")?; let table_size = msix_info.table.table_size as u32; - let cap_offset = self.pci_config.find_pci_cap(MSIX_CAP_ID); + let cap_offset = self.base.config.find_pci_cap(MSIX_CAP_ID); let offset: usize = cap_offset + MSIX_CAP_CONTROL as usize; le_write_u16( - &mut self.pci_config.write_mask, + &mut self.base.config.write_mask, offset, MSIX_CAP_FUNC_MASK | MSIX_CAP_ENABLE, )?; @@ -465,7 +465,7 @@ impl VfioPciDevice { cap_offset as u16, self.dev_id.clone(), ))); - self.pci_config.msix = Some(msix.clone()); + self.base.config.msix = Some(msix.clone()); let cloned_msix = msix.clone(); let read = move |data: &mut [u8], _: GuestAddress, offset: u64| -> bool { @@ -485,9 +485,9 @@ impl VfioPciDevice { let cloned_dev = self.vfio_device.clone(); let cloned_gsi_routes = self.gsi_msi_routes.clone(); - let parent_bus = self.parent_bus.clone(); + let parent_bus = self.base.parent_bus.clone(); let dev_id = self.dev_id.clone(); - let devfn = self.devfn; + let devfn = self.base.devfn; let write = move |data: &[u8], _: GuestAddress, offset: u64| -> bool { let mut locked_msix = msix.lock().unwrap(); locked_msix.table[offset as usize..(offset as usize + data.len())] @@ -660,7 +660,7 @@ impl VfioPciDevice { /// the guest OS. fn setup_bars_mmap(&mut self) -> Result<()> { for i in vfio::VFIO_PCI_BAR0_REGION_INDEX..vfio::VFIO_PCI_ROM_REGION_INDEX { - let gpa = self.pci_config.get_bar_address(i as usize); + let gpa = self.base.config.get_bar_address(i as usize); if gpa == BAR_SPACE_UNMAPPED || gpa == 0 { continue; } @@ -704,7 +704,8 @@ impl VfioPciDevice { let ram_device = Region::init_ram_device_region(Arc::new(host_mmap), "VfioRam"); let bar = self - .pci_config + .base + .config .bars .get_mut(i as usize) .with_context(|| "Failed to get pci bar info")?; @@ -813,11 +814,11 @@ impl VfioPciDevice { impl PciDevOps for VfioPciDevice { fn init_write_mask(&mut self) -> pci::Result<()> { - self.pci_config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> pci::Result<()> { - self.pci_config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn realize(mut self) -> pci::Result<()> { @@ -836,9 +837,9 @@ impl PciDevOps for VfioPciDevice { pci::Result::with_context( init_multifunction( self.multi_func, - &mut self.pci_config.config, - self.devfn, - self.parent_bus.clone(), + &mut self.base.config.config, + self.base.devfn, + self.base.parent_bus.clone(), ), || "Failed to init vfio device multifunction.", )?; @@ -846,13 +847,14 @@ impl PciDevOps for VfioPciDevice { #[cfg(target_arch = "aarch64")] { let bus_num = self + .base .parent_bus .upgrade() .unwrap() .lock() .unwrap() .number(SECONDARY_BUS_NUM as usize); - self.dev_id = Arc::new(AtomicU16::new(self.set_dev_id(bus_num, self.devfn))); + self.dev_id = Arc::new(AtomicU16::new(self.set_dev_id(bus_num, self.base.devfn))); } self.msix_info = Some(pci::Result::with_context(self.get_msix_info(), || { @@ -864,9 +866,9 @@ impl PciDevOps for VfioPciDevice { )?)); pci::Result::with_context(self.register_bars(), || "Failed to register bars")?; - let devfn = self.devfn; + let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); - let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); + let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); let mut locked_pci_bus = pci_bus.lock().unwrap(); let pci_device = locked_pci_bus.devices.get(&devfn); if pci_device.is_none() { @@ -891,7 +893,7 @@ impl PciDevOps for VfioPciDevice { } fn devfn(&self) -> Option { - Some(self.devfn) + Some(self.base.devfn) } /// Read pci data from pci config if it emulate, otherwise read from vfio device. @@ -913,7 +915,7 @@ impl PciDevOps for VfioPciDevice { || ranges_overlap(offset, size, HEADER_TYPE as usize, 2) || ranges_overlap(offset, size, PCI_CONFIG_SPACE_SIZE, ext_cfg_size) { - self.pci_config.read(offset, data); + self.base.config.read(offset, data); return; } @@ -958,14 +960,15 @@ impl PciDevOps for VfioPciDevice { } let cap_offset = self - .pci_config + .base + .config .msix .as_ref() .map_or(0, |m| m.lock().unwrap().msix_cap_offset as usize); - let was_enable = is_msix_enabled(cap_offset, &self.pci_config.config); - let parent_bus = self.parent_bus.upgrade().unwrap(); + let was_enable = is_msix_enabled(cap_offset, &self.base.config.config); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); - self.pci_config.write( + self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), @@ -975,7 +978,7 @@ impl PciDevOps for VfioPciDevice { ); if ranges_overlap(offset, size, COMMAND as usize, REG_SIZE) { - if le_read_u32(&self.pci_config.config, offset).unwrap() & COMMAND_MEMORY_SPACE as u32 + if le_read_u32(&self.base.config.config, offset).unwrap() & COMMAND_MEMORY_SPACE as u32 != 0 { if let Err(e) = self.setup_bars_mmap() { @@ -983,7 +986,7 @@ impl PciDevOps for VfioPciDevice { } } } else if ranges_overlap(offset, size, cap_offset, MSIX_CAP_SIZE as usize) { - let is_enable = is_msix_enabled(cap_offset, &self.pci_config.config); + let is_enable = is_msix_enabled(cap_offset, &self.base.config.config); if !was_enable && is_enable { if let Err(e) = self.vfio_enable_msix() { @@ -998,7 +1001,7 @@ impl PciDevOps for VfioPciDevice { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index ba553ebae..4ed1922d0 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -28,10 +28,9 @@ use pci::config::{ }; use pci::msix::{update_dev_id, MsixState}; -use pci::Result as PciResult; use pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, - ranges_overlap, PciBus, PciDevOps, PciError, + ranges_overlap, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, }; use util::byte_code::ByteCode; use util::num_ops::{read_data_u32, write_data_u32}; @@ -261,22 +260,15 @@ struct VirtioPciState { /// Virtio-PCI device structure #[derive(Clone)] pub struct VirtioPciDevice { - /// Name of this device - name: String, + base: PciDevBase, /// The entity of virtio device device: Arc>, /// Device id dev_id: Arc, - /// Devfn - devfn: u8, /// Memory AddressSpace sys_mem: Arc, - /// Pci config space. - config: PciConfig, /// Offset of VirtioPciCfgAccessCap in Pci config space. cfg_cap_offset: usize, - /// Primary Bus - parent_bus: Weak>, /// Eventfds used for guest notify the Device. notify_eventfds: Arc, /// The function for interrupt triggering @@ -298,14 +290,16 @@ impl VirtioPciDevice { ) -> Self { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { - name, + base: PciDevBase { + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), + devfn, + name, + parent_bus, + }, device, dev_id: Arc::new(AtomicU16::new(0)), - devfn, sys_mem, - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), cfg_cap_offset: 0, - parent_bus, notify_eventfds: Arc::new(NotifyEventFds::new(queue_num)), interrupt_cb: None, multi_func, @@ -325,8 +319,8 @@ impl VirtioPciDevice { let msix_config = virtio_base.config_vector.clone(); let config_generation = virtio_base.config_generation.clone(); - let cloned_msix = self.config.msix.as_ref().unwrap().clone(); - let cloned_intx = self.config.intx.as_ref().unwrap().clone(); + let cloned_msix = self.base.config.msix.as_ref().unwrap().clone(); + let cloned_intx = self.base.config.intx.as_ref().unwrap().clone(); let dev_id = self.dev_id.clone(); let cb = Arc::new(Box::new( @@ -386,13 +380,13 @@ impl VirtioPciDevice { } fn modern_mem_region_map(&mut self, data: T) -> PciResult { - let cap_offset = self.config.add_pci_cap( + let cap_offset = self.base.config.add_pci_cap( PCI_CAP_ID_VNDR, size_of::() + PCI_CAP_VNDR_AND_NEXT_SIZE as usize, )?; let write_start = cap_offset + PCI_CAP_VNDR_AND_NEXT_SIZE as usize; - self.config.config[write_start..(write_start + size_of::())] + self.base.config.config[write_start..(write_start + size_of::())] .copy_from_slice(data.as_bytes()); Ok(write_start) @@ -431,7 +425,7 @@ impl VirtioPciDevice { } locked_dev.virtio_base_mut().queues = queues; - update_dev_id(&self.parent_bus, self.devfn, &self.dev_id); + update_dev_id(&self.base.parent_bus, self.base.devfn, &self.dev_id); if self.need_irqfd { let mut queue_num = locked_dev.queue_num(); // No need to create call event for control queue. @@ -467,8 +461,8 @@ impl VirtioPciDevice { } fn deactivate_device(&self) -> bool { - if self.need_irqfd && self.config.msix.is_some() { - let msix = self.config.msix.as_ref().unwrap(); + if self.need_irqfd && self.base.config.msix.is_some() { + let msix = self.base.config.msix.as_ref().unwrap(); if msix.lock().unwrap().unregister_irqfd().is_err() { return false; } @@ -591,7 +585,7 @@ impl VirtioPciDevice { } } COMMON_MSIX_REG => { - if self.config.revise_msix_vector(value) { + if self.base.config.revise_msix_vector(value) { locked_device.set_config_vector(value as u16); } else { locked_device.set_config_vector(INVALID_VECTOR_NUM); @@ -647,7 +641,7 @@ impl VirtioPciDevice { .map(|config| config.ready = true)?; } COMMON_Q_MSIX_REG => { - let val = if self.config.revise_msix_vector(value) { + let val = if self.base.config.revise_msix_vector(value) { value as u16 } else { INVALID_VECTOR_NUM @@ -745,7 +739,7 @@ impl VirtioPciDevice { // 2. PCI ISR cap sub-region. let cloned_device = virtio_pci.lock().unwrap().device.clone(); - let cloned_intx = virtio_pci.lock().unwrap().config.intx.clone().unwrap(); + let cloned_intx = virtio_pci.lock().unwrap().base.config.intx.clone().unwrap(); let isr_read = move |data: &mut [u8], _: GuestAddress, _: u64| -> bool { if let Some(val) = data.get_mut(0) { let device_lock = cloned_device.lock().unwrap(); @@ -832,7 +826,7 @@ impl VirtioPciDevice { return; } - let config = &self.config.config[self.cfg_cap_offset..]; + let config = &self.base.config.config[self.cfg_cap_offset..]; let bar = config[offset_of!(VirtioPciCap, bar_id)]; let off = LittleEndian::read_u32(&config[offset_of!(VirtioPciCap, offset)..]); let len = LittleEndian::read_u32(&config[offset_of!(VirtioPciCap, length)..]); @@ -840,7 +834,7 @@ impl VirtioPciDevice { warn!("The bar_id {} of VirtioPciCfgAccessCap exceeds max", bar); return; } - let bar_base = self.config.get_bar_address(bar as usize); + let bar_base = self.base.config.get_bar_address(bar as usize); if bar_base == BAR_SPACE_UNMAPPED { debug!("The bar {} of VirtioPciCfgAccessCap is not mapped", bar); return; @@ -855,7 +849,7 @@ impl VirtioPciDevice { } if (off as u64) .checked_add(len as u64) - .filter(|&end| end <= self.config.bars[bar as usize].size) + .filter(|&end| end <= self.base.config.bars[bar as usize].size) .is_none() { warn!("The access range of VirtioPciCfgAccessCap exceeds bar size"); @@ -863,11 +857,11 @@ impl VirtioPciDevice { } let result = if is_write { - let mut data = self.config.config[pci_cfg_data_offset..].as_ref(); + let mut data = self.base.config.config[pci_cfg_data_offset..].as_ref(); self.sys_mem .write(&mut data, GuestAddress(bar_base + off as u64), len as u64) } else { - let mut data = self.config.config[pci_cfg_data_offset..].as_mut(); + let mut data = self.base.config.config[pci_cfg_data_offset..].as_mut(); self.sys_mem .read(&mut data, GuestAddress(bar_base + off as u64), len as u64) }; @@ -888,13 +882,13 @@ impl VirtioPciDevice { } fn queues_register_irqfd(&self, call_fds: &[Arc]) -> bool { - if self.config.msix.is_none() { + if self.base.config.msix.is_none() { error!("Failed to get msix in virtio pci device configure"); return false; } let locked_dev = self.device.lock().unwrap(); - let mut locked_msix = self.config.msix.as_ref().unwrap().lock().unwrap(); + let mut locked_msix = self.base.config.msix.as_ref().unwrap().lock().unwrap(); let queues = &locked_dev.virtio_base().queues; for (queue_index, queue_mutex) in queues.iter().enumerate() { @@ -928,11 +922,11 @@ impl VirtioPciDevice { impl PciDevOps for VirtioPciDevice { fn init_write_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_mask() + self.base.config.init_common_write_mask() } fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.config.init_common_write_clear_mask() + self.base.config.init_common_write_clear_mask() } fn realize(mut self) -> PciResult<()> { @@ -941,20 +935,24 @@ impl PciDevOps for VirtioPciDevice { let device_type = self.device.lock().unwrap().device_type(); le_write_u16( - &mut self.config.config, + &mut self.base.config.config, VENDOR_ID as usize, VIRTIO_PCI_VENDOR_ID, )?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, DEVICE_ID as usize, VIRTIO_PCI_DEVICE_ID_BASE + device_type as u16, )?; - self.config.config[REVISION_ID] = VIRTIO_PCI_ABI_VERSION; + self.base.config.config[REVISION_ID] = VIRTIO_PCI_ABI_VERSION; let class_id = get_virtio_class_id(device_type); - le_write_u16(&mut self.config.config, SUB_CLASS_CODE as usize, class_id)?; le_write_u16( - &mut self.config.config, + &mut self.base.config.config, + SUB_CLASS_CODE as usize, + class_id, + )?; + le_write_u16( + &mut self.base.config.config, SUBSYSTEM_VENDOR_ID, VIRTIO_PCI_VENDOR_ID, )?; @@ -964,16 +962,16 @@ impl PciDevOps for VirtioPciDevice { } else { 0x40 + device_type as u16 }; - le_write_u16(&mut self.config.config, SUBSYSTEM_ID, subsysid)?; + le_write_u16(&mut self.base.config.config, SUBSYSTEM_ID, subsysid)?; init_multifunction( self.multi_func, - &mut self.config.config, - self.devfn, - self.parent_bus.clone(), + &mut self.base.config.config, + self.base.devfn, + self.base.parent_bus.clone(), )?; #[cfg(target_arch = "aarch64")] - self.config.set_interrupt_pin(); + self.base.config.set_interrupt_pin(); let common_cap = VirtioPciCap::new( size_of::() as u8 + PCI_CAP_VNDR_AND_NEXT_SIZE, @@ -1019,7 +1017,7 @@ impl PciDevOps for VirtioPciDevice { self.cfg_cap_offset = self.modern_mem_region_map(cfg_cap)?; // Make related fields of PCI config writable for VirtioPciCfgAccessCap. - let write_mask = &mut self.config.write_mask[self.cfg_cap_offset..]; + let write_mask = &mut self.base.config.write_mask[self.cfg_cap_offset..]; write_mask[offset_of!(VirtioPciCap, bar_id)] = !0; le_write_u32(write_mask, offset_of!(VirtioPciCap, offset), !0)?; le_write_u32(write_mask, offset_of!(VirtioPciCap, length), !0)?; @@ -1034,18 +1032,18 @@ impl PciDevOps for VirtioPciDevice { init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, nvectors as u32, - &mut self.config, + &mut self.base.config, self.dev_id.clone(), - &self.name, + &self.base.name, None, None, )?; init_intx( - self.name.clone(), - &mut self.config, - self.parent_bus.clone(), - self.devfn, + self.base.name.clone(), + &mut self.base.config, + self.base.parent_bus.clone(), + self.base.devfn, )?; self.assign_interrupt_cb(); @@ -1056,10 +1054,9 @@ impl PciDevOps for VirtioPciDevice { .realize() .with_context(|| "Failed to realize virtio device")?; - let name = self.name.clone(); - let devfn = self.devfn; + let name = self.base.name.clone(); + let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); - let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) as u64) .next_power_of_two(); @@ -1068,7 +1065,7 @@ impl PciDevOps for VirtioPciDevice { Region::init_container_region(mem_region_size, "VirtioPciModernMem"); Self::modern_mem_region_init(dev.clone(), &modern_mem_region)?; - dev.lock().unwrap().config.register_bar( + dev.lock().unwrap().base.config.register_bar( VIRTIO_PCI_MEM_BAR_IDX as usize, modern_mem_region, RegionType::Mem64Bit, @@ -1077,7 +1074,7 @@ impl PciDevOps for VirtioPciDevice { )?; // Register device to pci bus. - let pci_bus = dev.lock().unwrap().parent_bus.upgrade().unwrap(); + let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); let mut locked_pci_bus = pci_bus.lock().unwrap(); let pci_device = locked_pci_bus.devices.get(&devfn); if pci_device.is_none() { @@ -1102,18 +1099,21 @@ impl PciDevOps for VirtioPciDevice { .unrealize() .with_context(|| "Failed to unrealize the virtio device")?; - let bus = self.parent_bus.upgrade().unwrap(); - self.config.unregister_bars(&bus)?; + let bus = self.base.parent_bus.upgrade().unwrap(); + self.base.config.unregister_bars(&bus)?; - MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name); - MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name); + MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.base.name); + MigrationManager::unregister_transport_instance( + VirtioPciState::descriptor(), + &self.base.name, + ); Ok(()) } fn read_config(&mut self, offset: usize, data: &mut [u8]) { self.do_cfg_access(offset, offset + data.len(), false); - self.config.read(offset, data); + self.base.config.read(offset, data); } fn write_config(&mut self, offset: usize, data: &[u8]) { @@ -1127,9 +1127,9 @@ impl PciDevOps for VirtioPciDevice { return; } - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); - self.config.write( + self.base.config.write( offset, data, self.dev_id.clone().load(Ordering::Acquire), @@ -1141,11 +1141,11 @@ impl PciDevOps for VirtioPciDevice { } fn name(&self) -> String { - self.name.clone() + self.base.name.clone() } fn devfn(&self) -> Option { - Some(self.devfn) + Some(self.base.devfn) } fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { @@ -1155,18 +1155,20 @@ impl PciDevOps for VirtioPciDevice { .unwrap() .reset() .with_context(|| "Failed to reset virtio device")?; - self.config.reset()?; + self.base.config.reset()?; + Ok(()) } fn get_dev_path(&self) -> Option { - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); match self.device.lock().unwrap().device_type() { VIRTIO_TYPE_BLOCK => { // The virtio blk device is identified as a single-channel SCSI device, // so add scsi controller identification without channel, scsi-id and lun. let parent_dev_path = self.get_parent_dev_path(parent_bus); - let mut dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); + let mut dev_path = + self.populate_dev_path(parent_dev_path, self.base.devfn, "/scsi@"); dev_path.push_str("/disk@0,0"); Some(dev_path) } @@ -1176,7 +1178,7 @@ impl PciDevOps for VirtioPciDevice { // (eg: /pci@XXXXX/scsi@$slot_id[,function_id]). And every scsi device has it's // own boot path("/channel@0/disk@$target_id,$lun_id"); let parent_dev_path = self.get_parent_dev_path(parent_bus); - let dev_path = self.populate_dev_path(parent_dev_path, self.devfn, "/scsi@"); + let dev_path = self.populate_dev_path(parent_dev_path, self.base.devfn, "/scsi@"); Some(dev_path) } _ => None, @@ -1192,19 +1194,19 @@ impl StateTransfer for VirtioPciDevice { }; // Save virtio pci config state. - for idx in 0..self.config.config.len() { + for idx in 0..self.base.config.config.len() { // Clean interrupt status bit. if (idx as u8) == STATUS { - state.config_space[idx] = self.config.config[idx] & (!STATUS_INTERRUPT); + state.config_space[idx] = self.base.config.config[idx] & (!STATUS_INTERRUPT); } else { - state.config_space[idx] = self.config.config[idx]; + state.config_space[idx] = self.base.config.config[idx]; } - state.write_mask[idx] = self.config.write_mask[idx]; - state.write_clear_mask[idx] = self.config.write_clear_mask[idx]; + state.write_mask[idx] = self.base.config.write_mask[idx]; + state.write_clear_mask[idx] = self.base.config.write_clear_mask[idx]; } - state.last_cap_end = self.config.last_cap_end; - state.last_ext_cap_offset = self.config.last_ext_cap_offset; - state.last_ext_cap_end = self.config.last_ext_cap_end; + state.last_cap_end = self.base.config.last_cap_end; + state.last_ext_cap_offset = self.base.config.last_ext_cap_offset; + state.last_ext_cap_end = self.base.config.last_ext_cap_end; // Save virtio pci common config state. state.virtio_base = self.device.lock().unwrap().virtio_base().get_state(); @@ -1218,13 +1220,13 @@ impl StateTransfer for VirtioPciDevice { // Set virtio pci config state. self.dev_id.store(pci_state.dev_id, Ordering::Release); - let config_length = self.config.config.len(); - self.config.config = pci_state.config_space[..config_length].to_vec(); - self.config.write_mask = pci_state.write_mask[..config_length].to_vec(); - self.config.write_clear_mask = pci_state.write_clear_mask[..config_length].to_vec(); - self.config.last_cap_end = pci_state.last_cap_end; - self.config.last_ext_cap_end = pci_state.last_ext_cap_end; - self.config.last_ext_cap_offset = pci_state.last_ext_cap_offset; + let config_length = self.base.config.config.len(); + self.base.config.config = pci_state.config_space[..config_length].to_vec(); + self.base.config.write_mask = pci_state.write_mask[..config_length].to_vec(); + self.base.config.write_clear_mask = pci_state.write_clear_mask[..config_length].to_vec(); + self.base.config.last_cap_end = pci_state.last_cap_end; + self.base.config.last_ext_cap_end = pci_state.last_ext_cap_end; + self.base.config.last_ext_cap_offset = pci_state.last_ext_cap_offset; // Set virtio pci common config state. let mut locked_device = self.device.lock().unwrap(); @@ -1249,9 +1251,9 @@ impl MigrationHook for VirtioPciDevice { } // Reregister ioevents for notifies. - let parent_bus = self.parent_bus.upgrade().unwrap(); + let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); - if let Err(e) = self.config.update_bar_mapping( + if let Err(e) = self.base.config.update_bar_mapping( #[cfg(target_arch = "x86_64")] Some(&locked_parent_bus.io_region), Some(&locked_parent_bus.mem_region), @@ -1499,9 +1501,9 @@ mod tests { assert!(init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, (virtio_dev.lock().unwrap().queue_num() + 1) as u32, - &mut virtio_pci.config, + &mut virtio_pci.base.config, virtio_pci.dev_id.clone(), - &virtio_pci.name, + &virtio_pci.base.name, None, None, ) @@ -1647,24 +1649,24 @@ mod tests { false, ); #[cfg(target_arch = "aarch64")] - virtio_pci.config.set_interrupt_pin(); + virtio_pci.base.config.set_interrupt_pin(); init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, virtio_pci.device.lock().unwrap().queue_num() as u32 + 1, - &mut virtio_pci.config, + &mut virtio_pci.base.config, virtio_pci.dev_id.clone(), - &virtio_pci.name, + &virtio_pci.base.name, None, None, ) .unwrap(); init_intx( - virtio_pci.name.clone(), - &mut virtio_pci.config, - virtio_pci.parent_bus.clone(), - virtio_pci.devfn, + virtio_pci.base.name.clone(), + &mut virtio_pci.base.config, + virtio_pci.base.parent_bus.clone(), + virtio_pci.base.devfn, ) .unwrap(); // Prepare msix and interrupt callback @@ -1740,12 +1742,13 @@ mod tests { assert!(init_multifunction( virtio_pci.multi_func, - &mut virtio_pci.config.config, - virtio_pci.devfn, - virtio_pci.parent_bus.clone() + &mut virtio_pci.base.config.config, + virtio_pci.base.devfn, + virtio_pci.base.parent_bus.clone() ) .is_ok()); - let header_type = le_read_u16(&virtio_pci.config.config, HEADER_TYPE as usize).unwrap(); + let header_type = + le_read_u16(&virtio_pci.base.config.config, HEADER_TYPE as usize).unwrap(); assert_eq!(header_type, HEADER_TYPE_MULTIFUNC as u16); } } -- Gitee From f5d36646595cbb11f99a88a04ea88a90272c7fe9 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 14:17:41 +0800 Subject: [PATCH 1274/2187] Refactory: Inherited methods of pci device 1.delete function name() from PciDevOps trait. 2.set default init_write_mask() and init_write_clear_mask() and remove them from pci device. 3.add pcidev_base() and pcidev_base_mut() function to get basic pci device propeties. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 26 ++++++------- devices/src/usb/xhci/xhci_pci.rs | 18 ++++----- machine/src/lib.rs | 4 +- .../src/standard_vm/aarch64/pci_host_root.rs | 16 +++----- machine/src/standard_vm/mod.rs | 4 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 16 +++----- machine/src/standard_vm/x86_64/mch.rs | 16 +++----- pci/src/bus.rs | 37 ++++++++---------- pci/src/demo_dev.rs | 16 +++----- pci/src/host.rs | 20 ++++++---- pci/src/lib.rs | 39 ++++++++++++------- pci/src/root_port.rs | 26 +++++-------- vfio/src/vfio_pci.rs | 18 ++++----- virtio/src/transport/virtio_pci.rs | 22 +++++------ 14 files changed, 126 insertions(+), 152 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 86b818c35..089534057 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -91,9 +91,17 @@ impl Ivshmem { } impl PciDevOps for Ivshmem { + fn pci_base(&self) -> &PciDevBase { + &self.base + } + + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base + } + fn realize(mut self) -> pci::Result<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; le_write_u16( &mut self.base.config.config, VENDOR_ID as usize, @@ -122,7 +130,7 @@ impl PciDevOps for Ivshmem { Some(device) => bail!( "Devfn {:?} has been used by {:?}", &self.base.devfn, - device.lock().unwrap().name() + device.lock().unwrap().pci_base().name ), None => locked_pci_bus .devices @@ -131,14 +139,6 @@ impl PciDevOps for Ivshmem { Ok(()) } - fn init_write_mask(&mut self) -> pci::Result<()> { - self.base.config.init_common_write_mask() - } - - fn init_write_clear_mask(&mut self) -> pci::Result<()> { - self.base.config.init_common_write_clear_mask() - } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { self.base.config.read(offset, data); } @@ -156,8 +156,4 @@ impl PciDevOps for Ivshmem { Some(&locked_parent_bus.mem_region), ); } - - fn name(&self) -> String { - self.base.name.clone() - } } diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 3df658167..83ba8383a 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -196,17 +196,17 @@ impl XhciPciDevice { } impl PciDevOps for XhciPciDevice { - fn init_write_mask(&mut self) -> pci::Result<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> pci::Result<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> pci::Result<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; le_write_u16( &mut self.base.config.config, VENDOR_ID as usize, @@ -296,7 +296,7 @@ impl PciDevOps for XhciPciDevice { bail!( "Devfn {:?} has been used by {:?}", &devfn, - pci_device.unwrap().lock().unwrap().name() + pci_device.unwrap().lock().unwrap().pci_base().name ); } Ok(()) @@ -329,10 +329,6 @@ impl PciDevOps for XhciPciDevice { ); } - fn name(&self) -> String { - self.base.name.clone() - } - fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { self.xhci.lock().unwrap().reset(); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fa24b5f9c..eecbf8f5b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1082,12 +1082,12 @@ pub trait MachineOps { .with_context(|| format!("Parent bridge does not exist, dev id {}", dev_id))?; let dev = parent_bridge.upgrade().unwrap(); let locked_dev = dev.lock().unwrap(); - let name = locked_dev.name(); + let name = locked_dev.pci_base().name.clone(); drop(locked_dev); let mut devfn = None; let locked_bus = locked_pci_host.root_bus.lock().unwrap(); for (id, dev) in &locked_bus.devices { - if dev.lock().unwrap().name() == name { + if dev.lock().unwrap().pci_base().name == name { devfn = Some(*id); break; } diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 6af66488f..c1e96f5cd 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -41,17 +41,17 @@ impl PciHostRoot { } impl PciDevOps for PciHostRoot { - fn init_write_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> PciResult<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; le_write_u16( &mut self.base.config.config, @@ -86,8 +86,4 @@ impl PciDevOps for PciHostRoot { fn write_config(&mut self, offset: usize, data: &[u8]) { self.base.config.write(offset, data, 0, None); } - - fn name(&self) -> String { - self.base.name.clone() - } } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index ad583dae3..144f899b9 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1355,9 +1355,9 @@ impl DeviceInterface for StdMachine { return match handle_unplug_pci_request(&bus, &dev) { Ok(()) => { let locked_dev = dev.lock().unwrap(); - let dev_id = locked_dev.name(); + let dev_id = &locked_dev.pci_base().name; drop(locked_pci_host); - self.del_bootindex_devices(&dev_id); + self.del_bootindex_devices(dev_id); let vm_config = self.get_vm_config(); let mut locked_config = vm_config.lock().unwrap(); locked_config.del_device_by_id(device_id); diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 4b533fccf..66ef5618c 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -229,17 +229,17 @@ impl LPCBridge { } impl PciDevOps for LPCBridge { - fn init_write_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> PciResult<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; le_write_u16( &mut self.base.config.config, @@ -301,8 +301,4 @@ impl PciDevOps for LPCBridge { } } } - - fn name(&self) -> String { - "ICH9 LPC bridge".to_string() - } } diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 71c042a6c..dad70ecdc 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -119,17 +119,17 @@ impl Mch { } impl PciDevOps for Mch { - fn init_write_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> PciResult<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; le_write_u16( &mut self.base.config.config, @@ -174,8 +174,4 @@ impl PciDevOps for Mch { } } } - - fn name(&self) -> String { - "Memory Controller Hub".to_string() - } } diff --git a/pci/src/bus.rs b/pci/src/bus.rs index af2020657..14c76bcb0 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -163,7 +163,7 @@ impl PciBus { // Device is attached in pci_bus. let locked_bus = pci_bus.lock().unwrap(); for dev in locked_bus.devices.values() { - if dev.lock().unwrap().name() == name { + if dev.lock().unwrap().pci_base().name == name { return Some((pci_bus.clone(), dev.clone())); } } @@ -184,19 +184,18 @@ impl PciBus { /// * `dev` - Device attached to the bus. pub fn detach_device(bus: &Arc>, dev: &Arc>) -> Result<()> { let mut dev_locked = dev.lock().unwrap(); - dev_locked - .unrealize() - .with_context(|| format!("Failed to unrealize device {}", dev_locked.name()))?; - - let devfn = dev_locked - .devfn() - .with_context(|| format!("Failed to get devfn: device {}", dev_locked.name()))?; + dev_locked.unrealize().with_context(|| { + format!("Failed to unrealize device {}", dev_locked.pci_base().name) + })?; + let devfn = dev_locked.devfn().with_context(|| { + format!("Failed to get devfn: device {}", dev_locked.pci_base().name) + })?; let mut locked_bus = bus.lock().unwrap(); if locked_bus.devices.get(&devfn).is_some() { locked_bus.devices.remove(&devfn); } else { - bail!("Device {} not found in the bus", dev_locked.name()); + bail!("Device {} not found in the bus", dev_locked.pci_base().name); } Ok(()) @@ -264,12 +263,12 @@ mod tests { } impl PciDevOps for PciDevice { - fn init_write_mask(&mut self) -> Result<()> { - Ok(()) + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> Result<()> { - Ok(()) + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn read_config(&mut self, offset: usize, data: &mut [u8]) { @@ -288,14 +287,10 @@ mod tests { ); } - fn name(&self) -> String { - self.base.name.clone() - } - fn realize(mut self) -> Result<()> { let devfn = self.base.devfn; - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; let dev = Arc::new(Mutex::new(self)); dev.lock() @@ -382,13 +377,13 @@ mod tests { assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name, "pcie.0"); - assert_eq!(dev.lock().unwrap().name(), "test1"); + assert_eq!(dev.lock().unwrap().pci_base().name, "test1"); let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test2"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name, "pcie.1"); - assert_eq!(dev.lock().unwrap().name(), "test2"); + assert_eq!(dev.lock().unwrap().pci_base().name, "test2"); } #[test] diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 7ec812aa5..fc2128c9b 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -95,8 +95,8 @@ impl DemoDev { } fn init_pci_config(&mut self) -> Result<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; let config = &mut self.base.config.config; le_write_u16(config, DEVICE_ID as usize, DEVICE_ID_DEMO)?; @@ -169,12 +169,12 @@ const DEVICE_ID_DEMO: u16 = 0xBEEF; const CLASS_CODE_DEMO: u16 = 0xEE; impl PciDevOps for DemoDev { - fn init_write_mask(&mut self) -> Result<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> Result<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } /// Realize PCI/PCIe device. @@ -225,10 +225,6 @@ impl PciDevOps for DemoDev { ); } - fn name(&self) -> String { - self.base.name.clone() - } - /// Reset device fn reset(&mut self, _reset_child_device: bool) -> Result<()> { self.base.config.reset_common_regs() diff --git a/pci/src/host.rs b/pci/src/host.rs index 344749763..05282bff9 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -532,7 +532,15 @@ pub mod tests { } impl PciDevOps for PciDevice { - fn init_write_mask(&mut self) -> Result<()> { + fn pci_base(&self) -> &PciDevBase { + &self.base + } + + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base + } + + fn init_write_mask(&mut self, _is_bridge: bool) -> Result<()> { let mut offset = 0_usize; while offset < self.base.config.config.len() { LittleEndian::write_u32( @@ -544,7 +552,7 @@ pub mod tests { Ok(()) } - fn init_write_clear_mask(&mut self) -> Result<()> { + fn init_write_clear_mask(&mut self, _is_bridge: bool) -> Result<()> { Ok(()) } @@ -564,14 +572,10 @@ pub mod tests { ); } - fn name(&self) -> String { - "PCI device".to_string() - } - fn realize(mut self) -> Result<()> { let devfn = self.base.devfn; - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; let dev = Arc::new(Mutex::new(self)); dev.lock() diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 7b397ab32..b3b1a756f 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -140,11 +140,31 @@ pub struct PciDevBase { } pub trait PciDevOps: Send + AsAny { + /// Get base property of pci device. + fn pci_base(&self) -> &PciDevBase; + + /// Get mutable base property of pci device. + fn pci_base_mut(&mut self) -> &mut PciDevBase; + /// Init writable bit mask. - fn init_write_mask(&mut self) -> Result<()>; + fn init_write_mask(&mut self, is_bridge: bool) -> Result<()> { + self.pci_base_mut().config.init_common_write_mask()?; + if is_bridge { + self.pci_base_mut().config.init_bridge_write_mask()?; + } + + Ok(()) + } /// Init write-and-clear bit mask. - fn init_write_clear_mask(&mut self) -> Result<()>; + fn init_write_clear_mask(&mut self, is_bridge: bool) -> Result<()> { + self.pci_base_mut().config.init_common_write_clear_mask()?; + if is_bridge { + self.pci_base_mut().config.init_bridge_write_clear_mask()?; + } + + Ok(()) + } /// Realize PCI/PCIe device. fn realize(self) -> Result<()>; @@ -185,9 +205,6 @@ pub trait PciDevOps: Send + AsAny { ((bus_num as u16) << bus_shift) | (devfn as u16) } - /// Get device name. - fn name(&self) -> String; - /// Reset device fn reset(&mut self, _reset_child_device: bool) -> Result<()> { Ok(()) @@ -402,22 +419,18 @@ mod tests { } impl PciDevOps for PciDev { - fn init_write_mask(&mut self) -> Result<()> { - Ok(()) + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> Result<()> { - Ok(()) + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn read_config(&mut self, _offset: usize, _data: &mut [u8]) {} fn write_config(&mut self, _offset: usize, _data: &[u8]) {} - fn name(&self) -> String { - self.base.name.clone() - } - fn realize(self) -> Result<()> { Ok(()) } diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index c0b314aa7..537818075 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -208,16 +208,16 @@ impl RootPort { let mut locked_dev = dev.lock().unwrap(); if let Err(e) = locked_dev.unrealize() { error!("{}", format!("{:?}", e)); - error!("Failed to unrealize device {}.", locked_dev.name()); + error!("Failed to unrealize device {}.", locked_dev.pci_base().name); } info!( "Device {} unplug from {}", - locked_dev.name(), + locked_dev.pci_base().name, self.base.name ); // Send QMP event for successful hot unplugging. - send_device_deleted_msg(&locked_dev.name()); + send_device_deleted_msg(&locked_dev.pci_base().name); } self.sec_bus.lock().unwrap().devices.clear(); } @@ -329,19 +329,17 @@ impl RootPort { } impl PciDevOps for RootPort { - fn init_write_mask(&mut self) -> Result<()> { - self.base.config.init_common_write_mask()?; - self.base.config.init_bridge_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> Result<()> { - self.base.config.init_common_write_clear_mask()?; - self.base.config.init_bridge_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> Result<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(true)?; + self.init_write_clear_mask(true)?; let config_space = &mut self.base.config.config; le_write_u16(config_space, VENDOR_ID as usize, PCI_VENDOR_ID_REDHAT)?; @@ -417,7 +415,7 @@ impl PciDevOps for RootPort { bail!( "Devfn {:?} has been used by {:?}", locked_root_port.base.devfn, - pci_device.unwrap().lock().unwrap().name() + pci_device.unwrap().lock().unwrap().pci_base().name ); } // Need to drop locked_root_port in order to register root_port instance. @@ -505,10 +503,6 @@ impl PciDevOps for RootPort { self.do_unplug(offset, data, old_ctl, old_status); } - fn name(&self) -> String { - self.base.name.clone() - } - fn devfn(&self) -> Option { Some(self.base.devfn) } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 702248dc1..01341ea3e 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -813,17 +813,17 @@ impl VfioPciDevice { } impl PciDevOps for VfioPciDevice { - fn init_write_mask(&mut self) -> pci::Result<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> pci::Result<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> pci::Result<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Failed to reset vfio device" })?; @@ -877,7 +877,7 @@ impl PciDevOps for VfioPciDevice { bail!( "Devfn {:?} has been used by {:?}", &devfn, - pci_device.unwrap().lock().unwrap().name() + pci_device.unwrap().lock().unwrap().pci_base().name ); } @@ -1000,10 +1000,6 @@ impl PciDevOps for VfioPciDevice { } } - fn name(&self) -> String { - self.base.name.clone() - } - fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Fail to reset vfio dev" diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 4ed1922d0..ba8536f54 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -921,17 +921,17 @@ impl VirtioPciDevice { } impl PciDevOps for VirtioPciDevice { - fn init_write_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_mask() + fn pci_base(&self) -> &PciDevBase { + &self.base } - fn init_write_clear_mask(&mut self) -> PciResult<()> { - self.base.config.init_common_write_clear_mask() + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base } fn realize(mut self) -> PciResult<()> { - self.init_write_mask()?; - self.init_write_clear_mask()?; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; let device_type = self.device.lock().unwrap().device_type(); le_write_u16( @@ -1083,7 +1083,7 @@ impl PciDevOps for VirtioPciDevice { bail!( "Devfn {:?} has been used by {:?}", &devfn, - pci_device.unwrap().lock().unwrap().name() + pci_device.unwrap().lock().unwrap().pci_base().name ); } @@ -1140,10 +1140,6 @@ impl PciDevOps for VirtioPciDevice { self.do_cfg_access(offset, end, true); } - fn name(&self) -> String { - self.base.name.clone() - } - fn devfn(&self) -> Option { Some(self.base.devfn) } @@ -1571,8 +1567,8 @@ mod tests { Arc::downgrade(&parent_bus), false, ); - virtio_pci.init_write_mask().unwrap(); - virtio_pci.init_write_clear_mask().unwrap(); + virtio_pci.init_write_mask(false).unwrap(); + virtio_pci.init_write_clear_mask(false).unwrap(); // Overflows, exceeds size of pcie config space let mut data = vec![0_u8; 4]; -- Gitee From 440eb31504f555bb56822a167452c6959e69a90c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 23:02:38 +0800 Subject: [PATCH 1275/2187] Refactory: Introduce SysBusDevBase for all system bus device Introduce SysBusDevBase for all system bus device to share common device info. Signed-off-by: liuxiangdong --- devices/src/acpi/ged.rs | 23 +++++++--------- devices/src/acpi/power.rs | 15 +++++------ devices/src/legacy/fwcfg.rs | 42 +++++++++++++++-------------- devices/src/legacy/pflash.rs | 9 +++---- devices/src/legacy/pl011.rs | 27 ++++++++++--------- devices/src/legacy/pl031.rs | 18 +++++-------- devices/src/legacy/ramfb.rs | 4 ++- devices/src/legacy/rtc.rs | 40 +++++++++++++-------------- devices/src/legacy/serial.rs | 28 +++++++++---------- pci/src/host.rs | 4 ++- sysbus/src/lib.rs | 36 ++++++++++++++++++++++--- virtio/src/transport/virtio_mmio.rs | 27 ++++++++++--------- 12 files changed, 149 insertions(+), 124 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 48532122f..13246d4d7 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -20,7 +20,7 @@ use machine_manager::qmp::QmpChannel; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; -use sysbus::{SysBus, SysBusDevOps, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; use vmm_sys_util::epoll::EventSet; @@ -50,20 +50,17 @@ const AML_GED_EVT_SEL: &str = "ESEL"; #[derive(Clone)] pub struct Ged { - interrupt_evt: Arc>, + base: SysBusDevBase, notification_type: Arc, battery_present: bool, - /// System resource. - res: SysRes, } impl Default for Ged { fn default() -> Self { Self { - interrupt_evt: Arc::new(None), + base: SysBusDevBase::default(), notification_type: Arc::new(AtomicU32::new(AcpiEvent::Nothing as u32)), battery_present: false, - res: SysRes::default(), } } } @@ -77,7 +74,7 @@ impl Ged { region_base: u64, region_size: u64, ) -> Result>> { - self.interrupt_evt = Arc::new(Some(EventFd::new(libc::EFD_NONBLOCK)?)); + self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; self.battery_present = battery_present; @@ -149,12 +146,12 @@ impl SysBusDevOps for Ged { true } - fn interrupt_evt(&self) -> Option<&EventFd> { - self.interrupt_evt.as_ref().as_ref() + fn interrupt_evt(&self) -> Option> { + self.base.interrupt_evt.clone() } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } } @@ -173,15 +170,15 @@ impl AmlBuilder for Ged { AmlEdgeLevel::Edge, AmlActiveLevel::High, AmlIntShare::Exclusive, - vec![self.res.irq as u32 + irq_base], + vec![self.base.res.irq as u32 + irq_base], )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); acpi_dev.append_child(AmlOpRegion::new( "EREG", AmlAddressSpaceType::SystemMemory, - self.res.region_base, - self.res.region_size, + self.base.res.region_base, + self.base.res.region_size, )); let mut field = AmlField::new( diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index ed45fe7ca..aee74d5d0 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -30,7 +30,7 @@ use address_space::GuestAddress; use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevOps, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; const AML_ACAD_REG: &str = "ADPM"; const AML_ACAD_ONLINE: &str = "ADPO"; @@ -73,16 +73,16 @@ struct PowerDevState { #[derive(Clone)] pub struct PowerDev { + base: SysBusDevBase, regs: Vec, state: PowerDevState, ged: Arc>, - /// System resource. - res: SysRes, } impl PowerDev { pub fn new(ged_dev: Arc>) -> Self { Self { + base: SysBusDevBase::default(), regs: vec![0; POWERDEV_REGS_SIZE], state: PowerDevState { last_acad_st: 1, @@ -90,7 +90,6 @@ impl PowerDev { last_bat_lvl: 0xffffffff, }, ged: ged_dev, - res: SysRes::default(), } } @@ -245,7 +244,7 @@ impl SysBusDevOps for PowerDev { } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } } @@ -257,7 +256,7 @@ impl AmlBuilder for PowerDev { acpi_acad_dev.append_child(AmlOpRegion::new( AML_ACAD_REG, AmlAddressSpaceType::SystemMemory, - self.res.region_base, + self.base.res.region_base, AML_ACAD_REG_SZ, )); @@ -290,8 +289,8 @@ impl AmlBuilder for PowerDev { acpi_bat_dev.append_child(AmlOpRegion::new( AML_BAT0_REG, AmlAddressSpaceType::SystemMemory, - self.res.region_base + AML_ACAD_REG_SZ, - self.res.region_size - AML_ACAD_REG_SZ, + self.base.res.region_base + AML_ACAD_REG_SZ, + self.base.res.region_size - AML_ACAD_REG_SZ, )); field = AmlField::new( diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 6f3d1fe9e..4b2952dc6 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -26,7 +26,7 @@ use anyhow::{anyhow, bail, Context, Result}; use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; use log::{error, warn}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; use util::offset_of; @@ -827,17 +827,16 @@ fn common_read( /// FwCfg MMIO Device use for AArch64 platform #[cfg(target_arch = "aarch64")] pub struct FwCfgMem { + base: SysBusDevBase, fwcfg: FwCfgCommon, - /// System Resource of device. - res: SysRes, } #[cfg(target_arch = "aarch64")] impl FwCfgMem { pub fn new(sys_mem: Arc) -> Self { FwCfgMem { + base: SysBusDevBase::new(SysBusDevType::FwCfg), fwcfg: FwCfgCommon::new(sys_mem), - res: SysRes::default(), } } @@ -963,7 +962,7 @@ impl SysBusDevOps for FwCfgMem { } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn set_sys_resource( @@ -992,28 +991,31 @@ impl SysBusDevOps for FwCfgMem { #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "x86_64")] pub struct FwCfgIO { + base: SysBusDevBase, fwcfg: FwCfgCommon, - /// System Resource of device. - res: SysRes, } #[cfg(target_arch = "x86_64")] impl FwCfgIO { pub fn new(sys_mem: Arc) -> Self { FwCfgIO { - fwcfg: FwCfgCommon::new(sys_mem), - res: SysRes { - region_base: FW_CFG_IO_BASE, - region_size: FW_CFG_IO_SIZE, - irq: -1, + base: SysBusDevBase { + dev_type: SysBusDevType::FwCfg, + res: SysRes { + region_base: FW_CFG_IO_BASE, + region_size: FW_CFG_IO_SIZE, + irq: -1, + }, + ..Default::default() }, + fwcfg: FwCfgCommon::new(sys_mem), } } pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { self.fwcfg.common_realize()?; - let region_base = self.res.region_base; - let region_size = self.res.region_size; + let region_base = self.base.res.region_base; + let region_size = self.base.res.region_size; self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| "Failed to allocate system resource for FwCfg.")?; @@ -1130,7 +1132,7 @@ impl SysBusDevOps for FwCfgIO { } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn set_sys_resource( @@ -1237,8 +1239,8 @@ impl AmlBuilder for FwCfgMem { let mut res = AmlResTemplate::new(); res.append_child(AmlMemory32Fixed::new( AmlReadAndWrite::ReadWrite, - self.res.region_base as u32, - self.res.region_size as u32, + self.base.res.region_base as u32, + self.base.res.region_size as u32, )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); @@ -1256,10 +1258,10 @@ impl AmlBuilder for FwCfgIO { let mut res = AmlResTemplate::new(); res.append_child(AmlIoResource::new( AmlIoDecode::Decode16, - self.res.region_base as u16, - self.res.region_base as u16, + self.base.res.region_base as u16, + self.base.res.region_base as u16, 0x01, - self.res.region_size as u8, + self.base.res.region_size as u8, )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 94b9de87c..a5f8c62a7 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -19,9 +19,10 @@ use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use anyhow::{anyhow, bail, Context, Result}; use log::{debug, error, warn}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; pub struct PFlash { + base: SysBusDevBase, /// Has backend file or not. has_backend: bool, /// Length of block. @@ -52,8 +53,6 @@ pub struct PFlash { write_blk_size: u32, /// ROM region of PFlash. rom: Option, - /// System Resource of device. - res: SysRes, } impl PFlash { @@ -173,6 +172,7 @@ impl PFlash { cfi_table[0x3f] = 0x01; Ok(PFlash { + base: SysBusDevBase::new(SysBusDevType::Flash), has_backend: backend.is_some(), block_len, bank_width, @@ -188,7 +188,6 @@ impl PFlash { counter: 0, write_blk_size, rom: None, - res: SysRes::default(), }) } @@ -823,7 +822,7 @@ impl SysBusDevOps for PFlash { } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn set_sys_resource( diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 2c15bc815..0780daca2 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -31,7 +31,7 @@ use migration::{ MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_data_u32; @@ -118,12 +118,9 @@ impl PL011State { #[allow(clippy::upper_case_acronyms)] pub struct PL011 { + base: SysBusDevBase, /// Device state. state: PL011State, - /// Interrupt event file descriptor. - interrupt_evt: EventFd, - /// System Resource of device. - res: SysRes, /// Character device for redirection. chardev: Arc>, } @@ -132,9 +129,13 @@ impl PL011 { /// Create a new `PL011` instance with default parameters. pub fn new(cfg: SerialConfig) -> Result { Ok(PL011 { + base: SysBusDevBase { + dev_type: SysBusDevType::PL011, + res: SysRes::default(), + interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), + }, + base: SysBusDevBase::new(SysBusDevType::PL011), state: PL011State::new(), - interrupt_evt: EventFd::new(libc::EFD_NONBLOCK)?, - res: SysRes::default(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), }) } @@ -384,12 +385,12 @@ impl SysBusDevOps for PL011 { true } - fn interrupt_evt(&self) -> Option<&EventFd> { - Some(&self.interrupt_evt) + fn interrupt_evt(&self) -> Option> { + self.base.interrupt_evt.clone() } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn get_type(&self) -> SysBusDevType { @@ -425,8 +426,8 @@ impl AmlBuilder for PL011 { let mut res = AmlResTemplate::new(); res.append_child(AmlMemory32Fixed::new( AmlReadAndWrite::ReadWrite, - self.res.region_base as u32, - self.res.region_size as u32, + self.base.res.region_base as u32, + self.base.res.region_size as u32, )); // SPI start at interrupt number 32 on aarch64 platform. let irq_base = INTERRUPT_PPIS_COUNT + INTERRUPT_SGIS_COUNT; @@ -435,7 +436,7 @@ impl AmlBuilder for PL011 { AmlEdgeLevel::Edge, AmlActiveLevel::High, AmlIntShare::Exclusive, - vec![self.res.irq as u32 + irq_base], + vec![self.base.res.irq as u32 + irq_base], )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index b61025a9c..9557b4043 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -24,7 +24,7 @@ use migration::{ MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::num_ops::write_data_u32; use vmm_sys_util::eventfd::EventFd; @@ -68,21 +68,19 @@ struct PL031State { #[allow(clippy::upper_case_acronyms)] /// PL031 structure. pub struct PL031 { + base: SysBusDevBase, /// State of device PL031. state: PL031State, /// The duplicate of Load register value. tick_offset: u32, /// Record the real time. base_time: Instant, - /// Interrupt eventfd. - interrupt_evt: Option, - /// System resource. - res: SysRes, } impl Default for PL031 { fn default() -> Self { Self { + base: SysBusDevBase::new(SysBusDevType::Rtc), state: PL031State::default(), // since 1970-01-01 00:00:00,it never cause overflow. tick_offset: SystemTime::now() @@ -90,8 +88,6 @@ impl Default for PL031 { .expect("time wrong") .as_secs() as u32, base_time: Instant::now(), - interrupt_evt: None, - res: SysRes::default(), } } } @@ -103,7 +99,7 @@ impl PL031 { region_base: u64, region_size: u64, ) -> Result<()> { - self.interrupt_evt = Some(EventFd::new(libc::EFD_NONBLOCK)?); + self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| LegacyError::SetSysResErr)?; @@ -190,12 +186,12 @@ impl SysBusDevOps for PL031 { true } - fn interrupt_evt(&self) -> Option<&EventFd> { - self.interrupt_evt.as_ref() + fn interrupt_evt(&self) -> Option> { + self.base.interrupt_evt.clone() } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn get_type(&self) -> SysBusDevType { diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 3181aaa74..c37df313c 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -24,7 +24,7 @@ use crate::legacy::Result; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; -use sysbus::{Result as SysBusResult, SysBus, SysBusDevOps, SysBusDevType}; +use sysbus::{Result as SysBusResult, SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use ui::console::{ console_init, display_graphic_update, display_replace_surface, set_run_stage, ConsoleType, DisplayConsole, DisplaySurface, HardWareOperations, VmRunningStage, @@ -225,12 +225,14 @@ impl HardWareOperations for RamfbInterface { } pub struct Ramfb { + base: SysBusDevBase, pub ramfb_state: RamfbState, } impl Ramfb { pub fn new(sys_mem: Arc, install: bool) -> Self { Ramfb { + base: SysBusDevBase::new(SysBusDevType::Ramfb), ramfb_state: RamfbState::new(sys_mem, install), } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index c545b7579..4707ddd10 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -20,7 +20,7 @@ use acpi::{ use address_space::GuestAddress; use anyhow::Result; use log::{debug, error, warn}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use vmm_sys_util::eventfd::EventFd; use util::time::{mktime64, NANOSECONDS_PER_SECOND}; @@ -99,14 +99,11 @@ fn bcd_to_bin(src: u8) -> u64 { #[allow(clippy::upper_case_acronyms)] /// RTC device. pub struct RTC { + base: SysBusDevBase, /// Static CMOS RAM. cmos_data: [u8; 128], /// Index of Selected register. cur_index: u8, - /// Interrupt eventfd. - interrupt_evt: Option, - /// Resource of RTC. - res: SysRes, /// Guest memory size. mem_size: u64, /// The start address of gap. @@ -121,14 +118,17 @@ impl RTC { /// Construct function of RTC device. pub fn new() -> Result { let mut rtc = RTC { + base: SysBusDevBase { + dev_type: SysBusDevType::Rtc, + res: SysRes { + region_base: RTC_PORT_INDEX, + region_size: 8, + irq: -1, + }, + interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), + }, cmos_data: [0_u8; 128], cur_index: 0_u8, - interrupt_evt: Some(EventFd::new(libc::EFD_NONBLOCK)?), - res: SysRes { - region_base: RTC_PORT_INDEX, - region_size: 8, - irq: -1, - }, mem_size: 0, gap_start: 0, // Since 1970-01-01 00:00:00, it never cause overflow. @@ -262,8 +262,8 @@ impl RTC { } pub fn realize(mut self, sysbus: &mut SysBus) -> Result<()> { - let region_base = self.res.region_base; - let region_size = self.res.region_size; + let region_base = self.base.res.region_base; + let region_size = self.base.res.region_size; self.set_sys_resource(sysbus, region_base, region_size)?; let dev = Arc::new(Mutex::new(self)); @@ -378,12 +378,12 @@ impl SysBusDevOps for RTC { } } - fn interrupt_evt(&self) -> Option<&EventFd> { - self.interrupt_evt.as_ref() + fn interrupt_evt(&self) -> Option> { + self.base.interrupt_evt.clone() } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn get_type(&self) -> SysBusDevType { @@ -406,12 +406,12 @@ impl AmlBuilder for RTC { let mut res = AmlResTemplate::new(); res.append_child(AmlIoResource::new( AmlIoDecode::Decode16, - self.res.region_base as u16, - self.res.region_base as u16, + self.base.res.region_base as u16, + self.base.res.region_base as u16, 0x01, - self.res.region_size as u8, + self.base.res.region_size as u8, )); - res.append_child(AmlIrqNoFlags::new(self.res.irq as u8)); + res.append_child(AmlIrqNoFlags::new(self.base.res.irq as u8)); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); acpi_dev.aml_bytes() diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 4d34515be..2df673bc4 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -29,7 +29,7 @@ use migration::{ MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use vmm_sys_util::eventfd::EventFd; @@ -113,14 +113,11 @@ impl SerialState { /// Contain registers status and operation methods of serial. pub struct Serial { + base: SysBusDevBase, /// Receiver buffer register. rbr: VecDeque, /// State of Device Serial. state: SerialState, - /// Interrupt event file descriptor. - interrupt_evt: Option, - /// System resource. - res: SysRes, /// Character device for redirection. chardev: Arc>, } @@ -128,10 +125,9 @@ pub struct Serial { impl Serial { pub fn new(cfg: SerialConfig) -> Self { Serial { + base: SysBusDevBase::new(SysBusDevType::Serial), rbr: VecDeque::new(), state: SerialState::new(), - interrupt_evt: None, - res: SysRes::default(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), } } @@ -147,7 +143,7 @@ impl Serial { .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; - self.interrupt_evt = Some(EventFd::new(libc::EFD_NONBLOCK)?); + self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| LegacyError::SetSysResErr)?; @@ -376,21 +372,21 @@ impl SysBusDevOps for Serial { self.write_internal(offset, data[0]).is_ok() } - fn interrupt_evt(&self) -> Option<&EventFd> { - self.interrupt_evt.as_ref() + fn interrupt_evt(&self) -> Option> { + self.base.interrupt_evt.clone() } fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { let mut irq: i32 = -1; if let Some(e) = self.interrupt_evt() { irq = UART_IRQ; - KVM_FDS.load().register_irqfd(e, irq as u32)?; + KVM_FDS.load().register_irqfd(&e, irq as u32)?; } Ok(irq) } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn get_type(&self) -> SysBusDevType { @@ -408,17 +404,17 @@ impl AmlBuilder for Serial { let mut res = AmlResTemplate::new(); res.append_child(AmlIoResource::new( AmlIoDecode::Decode16, - self.res.region_base as u16, - self.res.region_base as u16, + self.base.res.region_base as u16, + self.base.res.region_base as u16, 0x00, - self.res.region_size as u8, + self.base.res.region_size as u8, )); res.append_child(AmlExtendedInterrupt::new( AmlResourceUsage::Consumer, AmlEdgeLevel::Edge, AmlActiveLevel::High, AmlIntShare::Exclusive, - vec![self.res.irq as u32], + vec![self.base.res.irq as u32], )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); diff --git a/pci/src/host.rs b/pci/src/host.rs index 05282bff9..8e03b708f 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -26,7 +26,7 @@ use acpi::{AmlIoDecode, AmlIoResource}; use acpi::{AmlOne, AmlQWordDesc}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use anyhow::Context; -use sysbus::SysBusDevOps; +use sysbus::{SysBusDevBase, SysBusDevOps}; #[cfg(target_arch = "aarch64")] use crate::PCI_INTR_BASE; @@ -52,6 +52,7 @@ const ECAM_OFFSET_MASK: u64 = 0xfff; #[derive(Clone)] pub struct PciHost { + _base: SysBusDevBase, pub root_bus: Arc>, #[cfg(target_arch = "x86_64")] config_addr: u32, @@ -95,6 +96,7 @@ impl PciHost { mem_region, ); PciHost { + _base: SysBusDevBase::default(), root_bus: Arc::new(Mutex::new(root_bus)), #[cfg(target_arch = "x86_64")] config_addr: 0, diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index b8b75d41f..e1c54333d 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -201,7 +201,7 @@ impl Default for SysRes { } #[allow(clippy::upper_case_acronyms)] -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Clone)] pub enum SysBusDevType { Serial, Rtc, @@ -214,6 +214,36 @@ pub enum SysBusDevType { Others, } +#[derive(Clone)] +pub struct SysBusDevBase { + /// System bus device type. + pub dev_type: SysBusDevType, + /// System resource. + pub res: SysRes, + /// Interrupt event file descriptor. + pub interrupt_evt: Option>, +} + +impl Default for SysBusDevBase { + fn default() -> Self { + SysBusDevBase { + dev_type: SysBusDevType::Others, + res: SysRes::default(), + interrupt_evt: None, + } + } +} + +impl SysBusDevBase { + pub fn new(dev_type: SysBusDevType) -> SysBusDevBase { + Self { + dev_type, + res: SysRes::default(), + interrupt_evt: None, + } + } +} + /// Operations for sysbus devices. pub trait SysBusDevOps: Send + AmlBuilder + AsAny { /// Read function of device. @@ -238,7 +268,7 @@ pub trait SysBusDevOps: Send + AmlBuilder + AsAny { Vec::new() } - fn interrupt_evt(&self) -> Option<&EventFd> { + fn interrupt_evt(&self) -> Option> { None } @@ -251,7 +281,7 @@ pub trait SysBusDevOps: Send + AmlBuilder + AsAny { match self.interrupt_evt() { None => Ok(-1_i32), Some(evt) => { - KVM_FDS.load().register_irqfd(evt, irq as u32)?; + KVM_FDS.load().register_irqfd(&evt, irq as u32)?; sysbus.min_free_irq = irq + 1; Ok(irq) } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 087811aec..c78da59e0 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -21,7 +21,7 @@ use log::{error, warn}; use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType, SysRes}; +use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; @@ -110,16 +110,13 @@ pub struct VirtioMmioState { /// virtio-mmio device structure. pub struct VirtioMmioDevice { + base: SysBusDevBase, // The entity of low level device. pub device: Arc>, - // EventFd used to send interrupt to VM - interrupt_evt: Arc, // HostNotifyInfo used for guest notifier host_notify_info: HostNotifyInfo, // System address space. mem_space: Arc, - // System Resource of device. - res: SysRes, /// The function for interrupt triggering. interrupt_cb: Option>, } @@ -130,11 +127,14 @@ impl VirtioMmioDevice { let queue_num = device_clone.lock().unwrap().queue_num(); VirtioMmioDevice { + base: SysBusDevBase { + dev_type: SysBusDevType::VirtioMmio, + res: SysRes::default(), + interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), + }, device, - interrupt_evt: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), host_notify_info: HostNotifyInfo::new(queue_num), mem_space: mem_space.clone(), - res: SysRes::default(), interrupt_cb: None, } } @@ -167,7 +167,7 @@ impl VirtioMmioDevice { "{}@0x{:08x}:{}", region_size, region_base, - dev.lock().unwrap().res.irq + dev.lock().unwrap().base.res.irq ), }); Ok(dev) @@ -222,7 +222,7 @@ impl VirtioMmioDevice { } fn assign_interrupt_cb(&mut self) { - let interrupt_evt = self.interrupt_evt.clone(); + let interrupt_evt = self.base.interrupt_evt.clone(); let locked_dev = self.device.lock().unwrap(); let virtio_base = locked_dev.virtio_base(); @@ -248,7 +248,8 @@ impl VirtioMmioDevice { VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status, Ordering::SeqCst); - interrupt_evt + let interrupt = interrupt_evt.as_ref().unwrap(); + interrupt .write(1) .with_context(|| VirtioError::EventFdWrite)?; @@ -497,12 +498,12 @@ impl SysBusDevOps for VirtioMmioDevice { ret } - fn interrupt_evt(&self) -> Option<&EventFd> { - Some(self.interrupt_evt.as_ref()) + fn interrupt_evt(&self) -> Option> { + self.base.interrupt_evt.clone() } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { - Some(&mut self.res) + Some(&mut self.base.res) } fn get_type(&self) -> SysBusDevType { -- Gitee From 65174d99188867f7626f3727231baef55a861527 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 17 Jul 2023 04:59:40 +0800 Subject: [PATCH 1276/2187] Refactory: Inherited methods of sysbus device 1. Add sysbusdev_base() and sysbusdev_base_mut() to get common property of system bus device. 2. Set default interrupt_evt() function in sysbusdevops trait. Signed-off-by: liuxiangdong --- devices/src/acpi/ged.rs | 12 +++-- devices/src/acpi/power.rs | 8 ++++ devices/src/legacy/fwcfg.rs | 25 +++++++---- devices/src/legacy/pflash.rs | 12 +++-- devices/src/legacy/pl011.rs | 19 ++++---- devices/src/legacy/pl031.rs | 16 +++---- devices/src/legacy/ramfb.rs | 12 +++-- devices/src/legacy/rtc.rs | 16 +++---- devices/src/legacy/serial.rs | 16 +++---- machine/src/lib.rs | 2 +- machine/src/micro_vm/mod.rs | 12 ++--- machine/src/standard_vm/aarch64/mod.rs | 18 ++++---- pci/src/host.rs | 12 ++++- sysbus/src/lib.rs | 62 +++++++++++++++----------- virtio/src/transport/virtio_mmio.rs | 17 ++++--- 15 files changed, 150 insertions(+), 109 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 13246d4d7..ba2f26edb 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -132,6 +132,14 @@ impl Ged { } impl SysBusDevOps for Ged { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if offset != 0 { return false; @@ -146,10 +154,6 @@ impl SysBusDevOps for Ged { true } - fn interrupt_evt(&self) -> Option> { - self.base.interrupt_evt.clone() - } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index aee74d5d0..581657996 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -230,6 +230,14 @@ impl MigrationHook for PowerDev { } impl SysBusDevOps for PowerDev { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { let reg_idx: u64 = offset / 4; if reg_idx >= self.regs.len() as u64 { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 4b2952dc6..b64d7b12c 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -922,6 +922,14 @@ impl FwCfgOps for FwCfgMem { #[cfg(target_arch = "aarch64")] impl SysBusDevOps for FwCfgMem { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { common_read(self, data, base, offset) } @@ -977,11 +985,6 @@ impl SysBusDevOps for FwCfgMem { Ok(()) } - /// Get device type. - fn get_type(&self) -> SysBusDevType { - SysBusDevType::FwCfg - } - fn reset(&mut self) -> sysbus::Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) @@ -1089,6 +1092,14 @@ impl FwCfgOps for FwCfgIO { #[cfg(target_arch = "x86_64")] impl SysBusDevOps for FwCfgIO { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { common_read(self, data, base, offset) } @@ -1147,10 +1158,6 @@ impl SysBusDevOps for FwCfgIO { Ok(()) } - fn get_type(&self) -> SysBusDevType { - SysBusDevType::FwCfg - } - fn reset(&mut self) -> sysbus::Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index a5f8c62a7..58b6c424b 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -646,6 +646,14 @@ impl PFlash { } impl SysBusDevOps for PFlash { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { let mut index: u64; let mut ret: u32 = 0; @@ -838,10 +846,6 @@ impl SysBusDevOps for PFlash { Ok(()) } - fn get_type(&self) -> SysBusDevType { - SysBusDevType::Flash - } - fn reset(&mut self) -> sysbus::Result<()> { sysbus::Result::with_context(self.rom.as_ref().unwrap().set_rom_device_romd(true), || { "Fail to set PFlash rom region read only" diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 0780daca2..5883e9606 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -134,7 +134,6 @@ impl PL011 { res: SysRes::default(), interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), }, - base: SysBusDevBase::new(SysBusDevType::PL011), state: PL011State::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), }) @@ -145,7 +144,7 @@ impl PL011 { let flag = self.state.int_level & self.state.int_enabled; if flag & irq_mask != 0 { - if let Err(e) = self.interrupt_evt.write(1) { + if let Err(e) = self.interrupt_evt().unwrap().write(1) { error!( "Failed to trigger interrupt for PL011, flag is 0x{:x}, error is {:?}", flag, e, @@ -222,6 +221,14 @@ impl InputReceiver for PL011 { } impl SysBusDevOps for PL011 { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if data.len() > 4 { error!("Fail to read PL011, illegal data length {}", data.len()); @@ -385,17 +392,9 @@ impl SysBusDevOps for PL011 { true } - fn interrupt_evt(&self) -> Option> { - self.base.interrupt_evt.clone() - } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } - - fn get_type(&self) -> SysBusDevType { - SysBusDevType::PL011 - } } impl StateTransfer for PL011 { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 9557b4043..9b633fab0 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -132,6 +132,14 @@ impl PL031 { } impl SysBusDevOps for PL031 { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + /// Read data from registers by guest. fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if (0xFE0..0x1000).contains(&offset) { @@ -186,17 +194,9 @@ impl SysBusDevOps for PL031 { true } - fn interrupt_evt(&self) -> Option> { - self.base.interrupt_evt.clone() - } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } - - fn get_type(&self) -> SysBusDevType { - SysBusDevType::Rtc - } } impl AmlBuilder for PL031 { diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index c37df313c..25099a166 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -245,6 +245,14 @@ impl Ramfb { } impl SysBusDevOps for Ramfb { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, _data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool { error!("Ramfb can not be read!"); false @@ -255,10 +263,6 @@ impl SysBusDevOps for Ramfb { false } - fn get_type(&self) -> SysBusDevType { - SysBusDevType::Ramfb - } - fn reset(&mut self) -> SysBusResult<()> { self.ramfb_state.reset_ramfb_state(); Ok(()) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 4707ddd10..729045fa9 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -356,6 +356,14 @@ impl RTC { } impl SysBusDevOps for RTC { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { if offset == 0 { debug!( @@ -378,18 +386,10 @@ impl SysBusDevOps for RTC { } } - fn interrupt_evt(&self) -> Option> { - self.base.interrupt_evt.clone() - } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } - fn get_type(&self) -> SysBusDevType { - SysBusDevType::Rtc - } - fn reset(&mut self) -> sysbus::Result<()> { self.cmos_data.fill(0); self.init_rtc_reg(); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 2df673bc4..d518554fe 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -363,6 +363,14 @@ impl InputReceiver for Serial { } impl SysBusDevOps for Serial { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { data[0] = self.read_internal(offset); true @@ -372,10 +380,6 @@ impl SysBusDevOps for Serial { self.write_internal(offset, data[0]).is_ok() } - fn interrupt_evt(&self) -> Option> { - self.base.interrupt_evt.clone() - } - fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { let mut irq: i32 = -1; if let Some(e) = self.interrupt_evt() { @@ -388,10 +392,6 @@ impl SysBusDevOps for Serial { fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } - - fn get_type(&self) -> SysBusDevType { - SysBusDevType::Serial - } } impl AmlBuilder for Serial { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index eecbf8f5b..4d177c0ac 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -468,7 +468,7 @@ pub trait MachineOps { // Micro_vm. for dev in self.get_sys_bus().devices.iter() { let locked_busdev = dev.lock().unwrap(); - if locked_busdev.get_type() == SysBusDevType::VirtioMmio { + if locked_busdev.sysbusdev_base().dev_type == SysBusDevType::VirtioMmio { let virtio_mmio_dev = locked_busdev .as_any() .downcast_ref::() diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index b260732cd..0961f3be0 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1673,13 +1673,13 @@ impl CompileFDTHelper for LightMachine { fdt.end_node(psci_node_dep)?; for dev in self.sysbus.devices.iter() { - let mut locked_dev = dev.lock().unwrap(); - let dev_type = locked_dev.get_type(); - let sys_res = locked_dev.get_sys_resource().unwrap(); + let locked_dev = dev.lock().unwrap(); + let dev_type = locked_dev.sysbusdev_base().dev_type; + let sys_res = locked_dev.sysbusdev_base().res; match dev_type { - SysBusDevType::Serial => generate_serial_device_node(fdt, sys_res)?, - SysBusDevType::Rtc => generate_rtc_device_node(fdt, sys_res)?, - SysBusDevType::VirtioMmio => generate_virtio_devices_node(fdt, sys_res)?, + SysBusDevType::Serial => generate_serial_device_node(fdt, &sys_res)?, + SysBusDevType::Rtc => generate_rtc_device_node(fdt, &sys_res)?, + SysBusDevType::VirtioMmio => generate_virtio_devices_node(fdt, &sys_res)?, _ => (), } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index b4c7d35c1..779f7aa01 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -940,9 +940,9 @@ impl AcpiBuilder for StdMachine { // Irq number used by the UART let mut uart_irq: u32 = 0; for dev in self.sysbus.devices.iter() { - let mut locked_dev = dev.lock().unwrap(); - if locked_dev.get_type() == SysBusDevType::PL011 { - uart_irq = locked_dev.get_sys_resource().unwrap().irq as u32; + let locked_dev = dev.lock().unwrap(); + if locked_dev.sysbusdev_base().dev_type == SysBusDevType::PL011 { + uart_irq = locked_dev.sysbusdev_base().res.irq as u32; break; } } @@ -1626,23 +1626,23 @@ impl CompileFDTHelper for StdMachine { fdt.end_node(psci_node_dep)?; for dev in self.sysbus.devices.iter() { - let mut locked_dev = dev.lock().unwrap(); - match locked_dev.get_type() { + let locked_dev = dev.lock().unwrap(); + match locked_dev.sysbusdev_base().dev_type { SysBusDevType::PL011 => { // SAFETY: Legacy devices guarantee is not empty. - generate_serial_device_node(fdt, locked_dev.get_sys_resource().unwrap())? + generate_serial_device_node(fdt, &locked_dev.sysbusdev_base().res)? } SysBusDevType::Rtc => { // SAFETY: Legacy devices guarantee is not empty. - generate_rtc_device_node(fdt, locked_dev.get_sys_resource().unwrap())? + generate_rtc_device_node(fdt, &locked_dev.sysbusdev_base().res)? } SysBusDevType::VirtioMmio => { // SAFETY: Legacy devices guarantee is not empty. - generate_virtio_devices_node(fdt, locked_dev.get_sys_resource().unwrap())? + generate_virtio_devices_node(fdt, &locked_dev.sysbusdev_base().res)? } SysBusDevType::FwCfg => { // SAFETY: Legacy devices guarantee is not empty. - generate_fwcfg_device_node(fdt, locked_dev.get_sys_resource().unwrap())?; + generate_fwcfg_device_node(fdt, &locked_dev.sysbusdev_base().res)?; } _ => (), } diff --git a/pci/src/host.rs b/pci/src/host.rs index 8e03b708f..a398614dd 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -52,7 +52,7 @@ const ECAM_OFFSET_MASK: u64 = 0xfff; #[derive(Clone)] pub struct PciHost { - _base: SysBusDevBase, + base: SysBusDevBase, pub root_bus: Arc>, #[cfg(target_arch = "x86_64")] config_addr: u32, @@ -96,7 +96,7 @@ impl PciHost { mem_region, ); PciHost { - _base: SysBusDevBase::default(), + base: SysBusDevBase::default(), root_bus: Arc::new(Mutex::new(root_bus)), #[cfg(target_arch = "x86_64")] config_addr: 0, @@ -231,6 +231,14 @@ impl PciHost { } impl SysBusDevOps for PciHost { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { let bus_num = ((offset as u32 >> ECAM_BUS_SHIFT) & CONFIG_BUS_MASK) as u8; let devfn = ((offset as u32 >> ECAM_DEVFN_SHIFT) & CONFIG_DEVFN_MASK) as u8; diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index e1c54333d..834a124ef 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -121,7 +121,7 @@ impl SysBus { let locked_dev = dev.lock().unwrap(); region.set_ioeventfds(&locked_dev.ioeventfds()); - match locked_dev.get_type() { + match locked_dev.sysbusdev_base().dev_type { SysBusDevType::Serial if cfg!(target_arch = "x86_64") => { #[cfg(target_arch = "x86_64")] self.sys_io @@ -201,7 +201,7 @@ impl Default for SysRes { } #[allow(clippy::upper_case_acronyms)] -#[derive(Eq, PartialEq, Clone)] +#[derive(Eq, PartialEq, Clone, Copy)] pub enum SysBusDevType { Serial, Rtc, @@ -242,10 +242,36 @@ impl SysBusDevBase { interrupt_evt: None, } } + + fn set_irq(&mut self, sysbus: &mut SysBus) -> Result { + let irq = sysbus.min_free_irq; + if irq > sysbus.free_irqs.1 { + bail!("IRQ number exhausted."); + } + + match &self.interrupt_evt { + None => Ok(-1_i32), + Some(evt) => { + KVM_FDS.load().register_irqfd(evt, irq as u32)?; + sysbus.min_free_irq = irq + 1; + Ok(irq) + } + } + } + + pub fn set_sys(&mut self, irq: i32, region_base: u64, region_size: u64) { + self.res.irq = irq; + self.res.region_base = region_base; + self.res.region_size = region_size; + } } /// Operations for sysbus devices. pub trait SysBusDevOps: Send + AmlBuilder + AsAny { + fn sysbusdev_base(&self) -> &SysBusDevBase; + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase; + /// Read function of device. /// /// # Arguments @@ -269,23 +295,11 @@ pub trait SysBusDevOps: Send + AmlBuilder + AsAny { } fn interrupt_evt(&self) -> Option> { - None + self.sysbusdev_base().interrupt_evt.clone() } fn set_irq(&mut self, sysbus: &mut SysBus) -> Result { - let irq = sysbus.min_free_irq; - if irq > sysbus.free_irqs.1 { - bail!("IRQ number exhausted."); - } - - match self.interrupt_evt() { - None => Ok(-1_i32), - Some(evt) => { - KVM_FDS.load().register_irqfd(&evt, irq as u32)?; - sysbus.min_free_irq = irq + 1; - Ok(irq) - } - } + self.sysbusdev_base_mut().set_irq(sysbus) } fn get_sys_resource(&mut self) -> Option<&mut SysRes> { @@ -299,17 +313,11 @@ pub trait SysBusDevOps: Send + AmlBuilder + AsAny { region_size: u64, ) -> Result<()> { let irq = self.set_irq(sysbus)?; - if let Some(res) = self.get_sys_resource() { - res.region_base = region_base; - res.region_size = region_size; - res.irq = irq; - return Ok(()); - } - bail!("Failed to get sys resource."); - } - - fn get_type(&self) -> SysBusDevType { - SysBusDevType::Others + self.get_sys_resource() + .with_context(|| "Failed to get sys resource.")?; + self.sysbusdev_base_mut() + .set_sys(irq, region_base, region_size); + Ok(()) } fn reset(&mut self) -> Result<()> { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index c78da59e0..7366eba80 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -223,7 +223,6 @@ impl VirtioMmioDevice { fn assign_interrupt_cb(&mut self) { let interrupt_evt = self.base.interrupt_evt.clone(); - let locked_dev = self.device.lock().unwrap(); let virtio_base = locked_dev.virtio_base(); let device_status = virtio_base.device_status.clone(); @@ -371,6 +370,14 @@ impl VirtioMmioDevice { } impl SysBusDevOps for VirtioMmioDevice { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + /// Read data by virtio driver from VM. fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { match offset { @@ -498,17 +505,9 @@ impl SysBusDevOps for VirtioMmioDevice { ret } - fn interrupt_evt(&self) -> Option> { - self.base.interrupt_evt.clone() - } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } - - fn get_type(&self) -> SysBusDevType { - SysBusDevType::VirtioMmio - } } impl acpi::AmlBuilder for VirtioMmioDevice { -- Gitee From 1cc24abed14571d57fc26bdef0386380502e8b85 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 1 Aug 2023 17:10:04 +0800 Subject: [PATCH 1277/2187] Refactory: Introduce DeviceBase struct and device trait Introduce DeviceBase struct and device trait. Signed-off-by: liuxiangdong --- devices/src/acpi/ged.rs | 11 +++++++ devices/src/acpi/power.rs | 11 +++++++ devices/src/legacy/fwcfg.rs | 24 +++++++++++++++ devices/src/legacy/pflash.rs | 12 ++++++++ devices/src/legacy/pl011.rs | 12 ++++++++ devices/src/legacy/pl031.rs | 11 +++++++ devices/src/legacy/ramfb.rs | 11 +++++++ devices/src/legacy/rtc.rs | 12 ++++++++ devices/src/legacy/serial.rs | 11 +++++++ devices/src/misc/ivshmem.rs | 12 ++++++++ devices/src/scsi/disk.rs | 13 ++++++++ devices/src/usb/mod.rs | 13 ++++++++ devices/src/usb/xhci/xhci_pci.rs | 12 ++++++++ .../src/standard_vm/aarch64/pci_host_root.rs | 12 ++++++++ machine/src/standard_vm/x86_64/ich9_lpc.rs | 12 ++++++++ machine/src/standard_vm/x86_64/mch.rs | 12 ++++++++ pci/src/bus.rs | 14 +++++++++ pci/src/demo_dev.rs | 12 ++++++++ pci/src/host.rs | 23 ++++++++++++++ pci/src/lib.rs | 30 +++++++++++++++++-- pci/src/root_port.rs | 12 ++++++++ sysbus/src/lib.rs | 16 +++++++++- util/src/device.rs | 29 ++++++++++++++++++ util/src/lib.rs | 1 + vfio/src/vfio_pci.rs | 12 ++++++++ virtio/src/transport/virtio_mmio.rs | 12 ++++++++ virtio/src/transport/virtio_pci.rs | 12 ++++++++ 27 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 util/src/device.rs diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index ba2f26edb..0ca53aef7 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -21,6 +21,7 @@ use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; +use util::device::{Device, DeviceBase}; use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; use vmm_sys_util::epoll::EventSet; @@ -131,6 +132,16 @@ impl Ged { } } +impl Device for Ged { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for Ged { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 581657996..a15abb015 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -17,6 +17,7 @@ use std::time::Duration; use anyhow::{Context, Result}; use log::info; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use util::num_ops::write_data_u32; use crate::acpi::ged::{AcpiEvent, Ged}; @@ -229,6 +230,16 @@ impl MigrationHook for PowerDev { } } +impl Device for PowerDev { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for PowerDev { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index b64d7b12c..8d007ff2a 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -28,6 +28,7 @@ use byteorder::{BigEndian, ByteOrder}; use log::{error, warn}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use util::num_ops::extract_u64; use util::offset_of; #[cfg(target_arch = "x86_64")] @@ -920,6 +921,17 @@ impl FwCfgOps for FwCfgMem { } } +#[cfg(target_arch = "aarch64")] +impl Device for FwCfgMem { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + #[cfg(target_arch = "aarch64")] impl SysBusDevOps for FwCfgMem { fn sysbusdev_base(&self) -> &SysBusDevBase { @@ -1003,6 +1015,7 @@ impl FwCfgIO { pub fn new(sys_mem: Arc) -> Self { FwCfgIO { base: SysBusDevBase { + base: DeviceBase::default(), dev_type: SysBusDevType::FwCfg, res: SysRes { region_base: FW_CFG_IO_BASE, @@ -1090,6 +1103,17 @@ impl FwCfgOps for FwCfgIO { } } +#[cfg(target_arch = "x86_64")] +impl Device for FwCfgIO { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + #[cfg(target_arch = "x86_64")] impl SysBusDevOps for FwCfgIO { fn sysbusdev_base(&self) -> &SysBusDevBase { diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 58b6c424b..eb4dc81fa 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -20,7 +20,9 @@ use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use anyhow::{anyhow, bail, Context, Result}; use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use util::device::{Device, DeviceBase}; use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; + pub struct PFlash { base: SysBusDevBase, /// Has backend file or not. @@ -645,6 +647,16 @@ impl PFlash { } } +impl Device for PFlash { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for PFlash { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 5883e9606..35a48b077 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -33,6 +33,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use util::loop_context::EventNotifierHelper; use util::num_ops::read_data_u32; use vmm_sys_util::eventfd::EventFd; @@ -130,6 +131,7 @@ impl PL011 { pub fn new(cfg: SerialConfig) -> Result { Ok(PL011 { base: SysBusDevBase { + base: DeviceBase::default(), dev_type: SysBusDevType::PL011, res: SysRes::default(), interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), @@ -220,6 +222,16 @@ impl InputReceiver for PL011 { } } +impl Device for PL011 { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for PL011 { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 9b633fab0..5f09a2022 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -26,6 +26,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use util::num_ops::write_data_u32; use vmm_sys_util::eventfd::EventFd; @@ -131,6 +132,16 @@ impl PL031 { } } +impl Device for PL031 { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for PL031 { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 25099a166..776d76884 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -30,6 +30,7 @@ use ui::console::{ DisplayConsole, DisplaySurface, HardWareOperations, VmRunningStage, }; use ui::input::{key_event, KEYCODE_RET}; +use util::device::{Device, DeviceBase}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; const BYTES_PER_PIXELS: u32 = 8; @@ -244,6 +245,16 @@ impl Ramfb { } } +impl Device for Ramfb { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for Ramfb { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 729045fa9..de0f4f0b2 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -21,6 +21,7 @@ use address_space::GuestAddress; use anyhow::Result; use log::{debug, error, warn}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use util::device::{Device, DeviceBase}; use vmm_sys_util::eventfd::EventFd; use util::time::{mktime64, NANOSECONDS_PER_SECOND}; @@ -119,6 +120,7 @@ impl RTC { pub fn new() -> Result { let mut rtc = RTC { base: SysBusDevBase { + base: DeviceBase::default(), dev_type: SysBusDevType::Rtc, res: SysRes { region_base: RTC_PORT_INDEX, @@ -355,6 +357,16 @@ impl RTC { } } +impl Device for RTC { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for RTC { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index d518554fe..2070bafb4 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -31,6 +31,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use util::loop_context::EventNotifierHelper; use vmm_sys_util::eventfd::EventFd; @@ -362,6 +363,16 @@ impl InputReceiver for Serial { } } +impl Device for Serial { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for Serial { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 089534057..133909e51 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -25,6 +25,7 @@ use pci::{ }, le_write_u16, PciBus, PciDevBase, PciDevOps, }; +use util::device::{Device, DeviceBase}; const PCI_VENDOR_ID_IVSHMEM: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; const PCI_DEVICE_ID_IVSHMEM: u16 = 0x1110; @@ -50,6 +51,7 @@ impl Ivshmem { ) -> Self { Self { base: PciDevBase { + base: DeviceBase::new(name.clone()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), devfn, name, @@ -90,6 +92,16 @@ impl Ivshmem { } } +impl Device for Ivshmem { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for Ivshmem { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 854423be4..9736b56fe 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -14,6 +14,7 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; +use util::device::{Device, DeviceBase}; use crate::ScsiBus::{aio_complete_cb, ScsiBus, ScsiCompleteCb}; use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; @@ -84,7 +85,18 @@ impl ScsiDevState { } } +impl Device for ScsiDevice { + fn device_base(&self) -> &DeviceBase { + &self.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base + } +} + pub struct ScsiDevice { + pub base: DeviceBase, /// Configuration of the scsi device. pub config: ScsiDevConfig, /// State of the scsi device. @@ -120,6 +132,7 @@ impl ScsiDevice { drive_files: Arc>>, ) -> ScsiDevice { ScsiDevice { + base: DeviceBase::new(config.id.clone()), config, state: ScsiDevState::new(), block_backend: None, diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 4c32fe6b1..f51ba5819 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -14,6 +14,7 @@ pub mod error; pub use anyhow::Result; pub use error::UsbError; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; #[cfg(not(target_env = "musl"))] pub mod camera; @@ -112,6 +113,7 @@ impl UsbEndpoint { /// USB device common structure. pub struct UsbDevice { + pub base: DeviceBase, pub id: String, pub port: Option>>, pub speed: u32, @@ -129,9 +131,20 @@ pub struct UsbDevice { pub altsetting: [u32; USB_MAX_INTERFACES as usize], } +impl Device for UsbDevice { + fn device_base(&self) -> &DeviceBase { + &self.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base + } +} + impl UsbDevice { pub fn new(id: String, data_buf_len: usize) -> Self { let mut dev = UsbDevice { + base: DeviceBase::new(id.clone()), id, port: None, speed: 0, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 83ba8383a..4060dec91 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -25,6 +25,7 @@ use pci::config::{ }; use pci::msix::update_dev_id; use pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; +use util::device::{Device, DeviceBase}; use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; use super::xhci_regs::{ @@ -77,6 +78,7 @@ impl XhciPciDevice { ) -> Self { Self { base: PciDevBase { + base: DeviceBase::new(config.id.clone().unwrap()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, name: config.id.clone().unwrap(), @@ -195,6 +197,16 @@ impl XhciPciDevice { } } +impl Device for XhciPciDevice { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for XhciPciDevice { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index c1e96f5cd..a44e7e96a 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -19,6 +19,7 @@ use pci::{ }, le_write_u16, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; +use util::device::{Device, DeviceBase}; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; @@ -31,6 +32,7 @@ impl PciHostRoot { pub fn new(parent_bus: Weak>) -> Self { Self { base: PciDevBase { + base: DeviceBase::new("PCI Host Root".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), parent_bus, name: "PCI Host Root".to_string(), @@ -40,6 +42,16 @@ impl PciHostRoot { } } +impl Device for PciHostRoot { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for PciHostRoot { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 66ef5618c..29091761e 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -29,6 +29,7 @@ use pci::{ le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use vmm_sys_util::eventfd::EventFd; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; @@ -63,6 +64,7 @@ impl LPCBridge { ) -> Result { Ok(Self { base: PciDevBase { + base: DeviceBase::new("ICH9 LPC bridge".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0x1F << 3, name: "ICH9 LPC bridge".to_string(), @@ -228,6 +230,16 @@ impl LPCBridge { } } +impl Device for LPCBridge { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for LPCBridge { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index dad70ecdc..3cb232bb4 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -22,6 +22,7 @@ use pci::{ }, le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; +use util::device::{Device, DeviceBase}; use super::VENDOR_ID_INTEL; @@ -54,6 +55,7 @@ impl Mch { ) -> Self { Self { base: PciDevBase { + base: DeviceBase::new("Memory Controller Hub".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, name: "Memory Controller Hub".to_string(), @@ -118,6 +120,16 @@ impl Mch { } } +impl Device for Mch { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for Mch { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 14c76bcb0..ed6f84c64 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -256,12 +256,23 @@ mod tests { use crate::root_port::RootPort; use crate::{PciDevBase, PciHost}; use anyhow::Result; + use util::device::{Device, DeviceBase}; #[derive(Clone)] struct PciDevice { base: PciDevBase, } + impl Device for PciDevice { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } + } + impl PciDevOps for PciDevice { fn pci_base(&self) -> &PciDevBase { &self.base @@ -350,6 +361,7 @@ mod tests { // Test device is attached to the root bus. let pci_dev = PciDevice { base: PciDevBase { + base: DeviceBase::new("test1".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 10, name: String::from("test1"), @@ -362,6 +374,7 @@ mod tests { let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = PciDevice { base: PciDevBase { + base: DeviceBase::new("test2".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 12, name: String::from("test2"), @@ -398,6 +411,7 @@ mod tests { let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = PciDevice { base: PciDevBase { + base: DeviceBase::new("test1".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, name: String::from("test1"), diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index fc2128c9b..8f8ca6617 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -54,6 +54,7 @@ use crate::{ }; use crate::{demo_device::base_device::BaseDevice, PciDevBase}; pub use anyhow::{bail, Result}; +use util::device::{Device, DeviceBase}; pub struct DemoDev { base: PciDevBase, @@ -82,6 +83,7 @@ impl DemoDev { }; DemoDev { base: PciDevBase { + base: DeviceBase::new(cfg.id.clone()), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), devfn, name: cfg.id.clone(), @@ -168,6 +170,16 @@ const DEVICE_ID_DEMO: u16 = 0xBEEF; // reference to https://pci-ids.ucw.cz/read/PD/ const CLASS_CODE_DEMO: u16 = 0xEE; +impl Device for DemoDev { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for DemoDev { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/pci/src/host.rs b/pci/src/host.rs index a398614dd..e3a6db2f1 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -27,6 +27,7 @@ use acpi::{AmlOne, AmlQWordDesc}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use anyhow::Context; use sysbus::{SysBusDevBase, SysBusDevOps}; +use util::device::{Device, DeviceBase}; #[cfg(target_arch = "aarch64")] use crate::PCI_INTR_BASE; @@ -230,6 +231,16 @@ impl PciHost { } } +impl Device for PciHost { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for PciHost { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base @@ -536,11 +547,22 @@ pub mod tests { use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; use crate::root_port::RootPort; use crate::{PciDevBase, Result}; + use util::device::{Device, DeviceBase}; struct PciDevice { base: PciDevBase, } + impl Device for PciDevice { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } + } + impl PciDevOps for PciDevice { fn pci_base(&self) -> &PciDevBase { &self.base @@ -719,6 +741,7 @@ pub mod tests { let bus = PciBus::find_bus_by_name(&pci_host.lock().unwrap().root_bus, "pcie.2").unwrap(); let pci_dev = PciDevice { base: PciDevBase { + base: DeviceBase::new("PCI device".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 8, name: "PCI device".to_string(), diff --git a/pci/src/lib.rs b/pci/src/lib.rs index b3b1a756f..655e5b038 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -29,7 +29,10 @@ pub use host::PciHost; pub use intx::{init_intx, InterruptHandler, PciIntxState}; pub use msix::{init_msix, is_msix_enabled}; pub use root_port::RootPort; -use util::AsAny; +use util::{ + device::{Device, DeviceBase}, + AsAny, +}; use std::{ mem::size_of, @@ -129,6 +132,7 @@ pub fn pci_ext_cap_next(header: u32) -> usize { #[derive(Clone)] pub struct PciDevBase { + pub base: DeviceBase, /// Pci config space. pub config: PciConfig, /// Devfn. @@ -139,7 +143,17 @@ pub struct PciDevBase { pub parent_bus: Weak>, } -pub trait PciDevOps: Send + AsAny { +impl Device for PciDevBase { + fn device_base(&self) -> &DeviceBase { + &self.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base + } +} + +pub trait PciDevOps: Device + Send + AsAny { /// Get base property of pci device. fn pci_base(&self) -> &PciDevBase; @@ -360,6 +374,7 @@ pub fn ranges_overlap(start: usize, size: usize, range_start: usize, range_size: #[cfg(test)] mod tests { use address_space::{AddressSpace, Region}; + use util::device::DeviceBase; use super::*; @@ -418,6 +433,16 @@ mod tests { base: PciDevBase, } + impl Device for PciDev { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } + } + impl PciDevOps for PciDev { fn pci_base(&self) -> &PciDevBase { &self.base @@ -450,6 +475,7 @@ mod tests { let dev = PciDev { base: PciDevBase { + base: DeviceBase::new("PCI device".to_string()), config: PciConfig::new(1, 1), devfn: 0, name: "PCI device".to_string(), diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 537818075..50c10f517 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -23,6 +23,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use once_cell::sync::OnceCell; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use super::config::{ PciConfig, PcieDevType, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, @@ -104,6 +105,7 @@ impl RootPort { Self { base: PciDevBase { + base: DeviceBase::new(name.clone()), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, name, @@ -328,6 +330,16 @@ impl RootPort { } } +impl Device for RootPort { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for RootPort { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/sysbus/src/lib.rs b/sysbus/src/lib.rs index 834a124ef..0ed8e572c 100644 --- a/sysbus/src/lib.rs +++ b/sysbus/src/lib.rs @@ -14,6 +14,7 @@ pub mod error; pub use anyhow::{bail, Context, Result}; pub use error::SysBusError; +use util::device::{Device, DeviceBase}; use std::fmt; use std::sync::{Arc, Mutex}; @@ -216,6 +217,7 @@ pub enum SysBusDevType { #[derive(Clone)] pub struct SysBusDevBase { + pub base: DeviceBase, /// System bus device type. pub dev_type: SysBusDevType, /// System resource. @@ -224,9 +226,20 @@ pub struct SysBusDevBase { pub interrupt_evt: Option>, } +impl Device for SysBusDevBase { + fn device_base(&self) -> &DeviceBase { + &self.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base + } +} + impl Default for SysBusDevBase { fn default() -> Self { SysBusDevBase { + base: DeviceBase::default(), dev_type: SysBusDevType::Others, res: SysRes::default(), interrupt_evt: None, @@ -237,6 +250,7 @@ impl Default for SysBusDevBase { impl SysBusDevBase { pub fn new(dev_type: SysBusDevType) -> SysBusDevBase { Self { + base: DeviceBase::default(), dev_type, res: SysRes::default(), interrupt_evt: None, @@ -267,7 +281,7 @@ impl SysBusDevBase { } /// Operations for sysbus devices. -pub trait SysBusDevOps: Send + AmlBuilder + AsAny { +pub trait SysBusDevOps: Device + Send + AmlBuilder + AsAny { fn sysbusdev_base(&self) -> &SysBusDevBase; fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase; diff --git a/util/src/device.rs b/util/src/device.rs new file mode 100644 index 000000000..3ac832ede --- /dev/null +++ b/util/src/device.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[derive(Clone, Default)] +pub struct DeviceBase { + /// Name of this device. + pub id: String, +} + +impl DeviceBase { + pub fn new(id: String) -> Self { + DeviceBase { id } + } +} + +pub trait Device { + fn device_base(&self) -> &DeviceBase; + + fn device_base_mut(&mut self) -> &mut DeviceBase; +} diff --git a/util/src/lib.rs b/util/src/lib.rs index 4d637a171..cc8f71fd2 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -17,6 +17,7 @@ pub mod byte_code; pub mod checksum; pub mod clock; pub mod daemonize; +pub mod device; #[cfg(target_arch = "aarch64")] pub mod device_tree; pub mod edid; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 01341ea3e..7cf65998f 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -39,6 +39,7 @@ use pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, pci_ext_cap_next, pci_ext_cap_ver, ranges_overlap, PciBus, PciDevBase, PciDevOps, }; +use util::device::{Device, DeviceBase}; use util::unix::host_page_size; use vfio_bindings::bindings::vfio; use vmm_sys_util::eventfd::EventFd; @@ -112,6 +113,7 @@ impl VfioPciDevice { Self { // Unknown PCI or PCIe type here, allocate enough space to match the two types. base: PciDevBase { + base: DeviceBase::new(name.clone()), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), devfn, name, @@ -812,6 +814,16 @@ impl VfioPciDevice { } } +impl Device for VfioPciDevice { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for VfioPciDevice { fn pci_base(&self) -> &PciDevBase { &self.base diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 7366eba80..ec2e86cf2 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -23,6 +23,7 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use vmm_sys_util::eventfd::EventFd; use crate::{ @@ -128,6 +129,7 @@ impl VirtioMmioDevice { VirtioMmioDevice { base: SysBusDevBase { + base: DeviceBase::default(), dev_type: SysBusDevType::VirtioMmio, res: SysRes::default(), interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), @@ -369,6 +371,16 @@ impl VirtioMmioDevice { } } +impl Device for VirtioMmioDevice { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl SysBusDevOps for VirtioMmioDevice { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index ba8536f54..79a13a390 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -33,6 +33,7 @@ use pci::{ ranges_overlap, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, }; use util::byte_code::ByteCode; +use util::device::{Device, DeviceBase}; use util::num_ops::{read_data_u32, write_data_u32}; use util::offset_of; use vmm_sys_util::eventfd::EventFd; @@ -291,6 +292,7 @@ impl VirtioPciDevice { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { base: PciDevBase { + base: DeviceBase::new(name.clone()), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), devfn, name, @@ -920,6 +922,16 @@ impl VirtioPciDevice { } } +impl Device for VirtioPciDevice { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + impl PciDevOps for VirtioPciDevice { fn pci_base(&self) -> &PciDevBase { &self.base -- Gitee From e8e546733083f8428ab9945d75352ddbd3aaec04 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 31 Jul 2023 17:51:11 +0800 Subject: [PATCH 1278/2187] Refactory: add name() function to get device name 1. Add name() function for device trait. 2. Delete useless name feild. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 5 ++-- devices/src/usb/mod.rs | 10 +++---- devices/src/usb/xhci/xhci_pci.rs | 7 +++-- machine/src/lib.rs | 4 +-- .../src/standard_vm/aarch64/pci_host_root.rs | 1 - machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 1 - machine/src/standard_vm/x86_64/mch.rs | 1 - pci/src/bus.rs | 23 +++++++--------- pci/src/demo_dev.rs | 4 +-- pci/src/host.rs | 1 - pci/src/lib.rs | 3 --- pci/src/root_port.rs | 24 +++++++---------- util/src/device.rs | 5 ++++ vfio/src/vfio_pci.rs | 5 ++-- virtio/src/transport/virtio_pci.rs | 27 +++++++++---------- 16 files changed, 52 insertions(+), 71 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 133909e51..dd24255b7 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -51,10 +51,9 @@ impl Ivshmem { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(name.clone()), + base: DeviceBase::new(name), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), devfn, - name, parent_bus, }, dev_id: Arc::new(AtomicU16::new(0)), @@ -142,7 +141,7 @@ impl PciDevOps for Ivshmem { Some(device) => bail!( "Devfn {:?} has been used by {:?}", &self.base.devfn, - device.lock().unwrap().pci_base().name + device.lock().unwrap().name() ), None => locked_pci_bus .devices diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f51ba5819..0fdfda004 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -114,7 +114,6 @@ impl UsbEndpoint { /// USB device common structure. pub struct UsbDevice { pub base: DeviceBase, - pub id: String, pub port: Option>>, pub speed: u32, pub addr: u8, @@ -144,8 +143,7 @@ impl Device for UsbDevice { impl UsbDevice { pub fn new(id: String, data_buf_len: usize) -> Self { let mut dev = UsbDevice { - base: DeviceBase::new(id.clone()), - id, + base: DeviceBase::new(id), port: None, speed: 0, addr: 0, @@ -204,7 +202,7 @@ impl UsbDevice { } pub fn generate_serial_number(&self, prefix: &str) -> String { - format!("{}-{}", prefix, self.id) + format!("{}-{}", prefix, self.base.id) } /// Handle USB control request which is for descriptor. @@ -339,7 +337,7 @@ impl UsbDevice { impl Drop for UsbDevice { fn drop(&mut self) { if self.unplugged { - send_device_deleted_msg(&self.id); + send_device_deleted_msg(&self.base.id); } } } @@ -404,7 +402,7 @@ pub trait UsbDeviceOps: Send + Sync { /// Unique device id. fn device_id(&self) -> &str { - &self.get_usb_device().id + &self.get_usb_device().base.id } /// Get the UsbDevice. diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 4060dec91..d0e832c97 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -81,7 +81,6 @@ impl XhciPciDevice { base: DeviceBase::new(config.id.clone().unwrap()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, - name: config.id.clone().unwrap(), parent_bus, }, xhci: XhciDevice::new(mem_space, config), @@ -254,13 +253,13 @@ impl PciDevOps for XhciPciDevice { intrs_num, &mut self.base.config, self.dev_id.clone(), - &self.base.name, + &self.base.base.id, Some(&self.mem_region), Some((XHCI_MSIX_TABLE_OFFSET, XHCI_MSIX_PBA_OFFSET)), )?; init_intx( - self.base.name.clone(), + self.name(), &mut self.base.config, self.base.parent_bus.clone(), self.base.devfn, @@ -308,7 +307,7 @@ impl PciDevOps for XhciPciDevice { bail!( "Devfn {:?} has been used by {:?}", &devfn, - pci_device.unwrap().lock().unwrap().pci_base().name + pci_device.unwrap().lock().unwrap().name() ); } Ok(()) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4d177c0ac..1df88903b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1082,12 +1082,12 @@ pub trait MachineOps { .with_context(|| format!("Parent bridge does not exist, dev id {}", dev_id))?; let dev = parent_bridge.upgrade().unwrap(); let locked_dev = dev.lock().unwrap(); - let name = locked_dev.pci_base().name.clone(); + let name = locked_dev.name(); drop(locked_dev); let mut devfn = None; let locked_bus = locked_pci_host.root_bus.lock().unwrap(); for (id, dev) in &locked_bus.devices { - if dev.lock().unwrap().pci_base().name == name { + if dev.lock().unwrap().name() == name { devfn = Some(*id); break; } diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index a44e7e96a..3d2b2f985 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -35,7 +35,6 @@ impl PciHostRoot { base: DeviceBase::new("PCI Host Root".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), parent_bus, - name: "PCI Host Root".to_string(), devfn: 0, }, } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 144f899b9..7dbc17d0b 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1355,7 +1355,7 @@ impl DeviceInterface for StdMachine { return match handle_unplug_pci_request(&bus, &dev) { Ok(()) => { let locked_dev = dev.lock().unwrap(); - let dev_id = &locked_dev.pci_base().name; + let dev_id = &locked_dev.name(); drop(locked_pci_host); self.del_bootindex_devices(dev_id); let vm_config = self.get_vm_config(); diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 29091761e..fc4fdb357 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -67,7 +67,6 @@ impl LPCBridge { base: DeviceBase::new("ICH9 LPC bridge".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0x1F << 3, - name: "ICH9 LPC bridge".to_string(), parent_bus, }, sys_io, diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 3cb232bb4..7c6f43a12 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -58,7 +58,6 @@ impl Mch { base: DeviceBase::new("Memory Controller Hub".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, - name: "Memory Controller Hub".to_string(), parent_bus, }, mmconfig_region: Some(mmconfig_region), diff --git a/pci/src/bus.rs b/pci/src/bus.rs index ed6f84c64..7a97c0fff 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -163,7 +163,7 @@ impl PciBus { // Device is attached in pci_bus. let locked_bus = pci_bus.lock().unwrap(); for dev in locked_bus.devices.values() { - if dev.lock().unwrap().pci_base().name == name { + if dev.lock().unwrap().name() == name { return Some((pci_bus.clone(), dev.clone())); } } @@ -184,18 +184,18 @@ impl PciBus { /// * `dev` - Device attached to the bus. pub fn detach_device(bus: &Arc>, dev: &Arc>) -> Result<()> { let mut dev_locked = dev.lock().unwrap(); - dev_locked.unrealize().with_context(|| { - format!("Failed to unrealize device {}", dev_locked.pci_base().name) - })?; + dev_locked + .unrealize() + .with_context(|| format!("Failed to unrealize device {}", dev_locked.name()))?; - let devfn = dev_locked.devfn().with_context(|| { - format!("Failed to get devfn: device {}", dev_locked.pci_base().name) - })?; + let devfn = dev_locked + .devfn() + .with_context(|| format!("Failed to get devfn: device {}", dev_locked.name()))?; let mut locked_bus = bus.lock().unwrap(); if locked_bus.devices.get(&devfn).is_some() { locked_bus.devices.remove(&devfn); } else { - bail!("Device {} not found in the bus", dev_locked.pci_base().name); + bail!("Device {} not found in the bus", dev_locked.name()); } Ok(()) @@ -364,7 +364,6 @@ mod tests { base: DeviceBase::new("test1".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 10, - name: String::from("test1"), parent_bus: root_bus.clone(), }, }; @@ -377,7 +376,6 @@ mod tests { base: DeviceBase::new("test2".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 12, - name: String::from("test2"), parent_bus: Arc::downgrade(&bus), }, }; @@ -390,13 +388,13 @@ mod tests { assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name, "pcie.0"); - assert_eq!(dev.lock().unwrap().pci_base().name, "test1"); + assert_eq!(dev.lock().unwrap().name(), "test1"); let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test2"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name, "pcie.1"); - assert_eq!(dev.lock().unwrap().pci_base().name, "test2"); + assert_eq!(dev.lock().unwrap().name(), "test2"); } #[test] @@ -414,7 +412,6 @@ mod tests { base: DeviceBase::new("test1".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, - name: String::from("test1"), parent_bus: Arc::downgrade(&bus), }, }; diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 8f8ca6617..87c45515b 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -86,7 +86,6 @@ impl DemoDev { base: DeviceBase::new(cfg.id.clone()), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), devfn, - name: cfg.id.clone(), parent_bus, }, cmd_cfg: cfg, @@ -192,14 +191,13 @@ impl PciDevOps for DemoDev { /// Realize PCI/PCIe device. fn realize(mut self) -> Result<()> { self.init_pci_config()?; - if self.cmd_cfg.bar_num > 0 { init_msix( 0, 1, &mut self.base.config, self.dev_id.clone(), - &self.base.name, + &self.base.base.id, None, None, )?; diff --git a/pci/src/host.rs b/pci/src/host.rs index e3a6db2f1..3260c5bf6 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -744,7 +744,6 @@ pub mod tests { base: DeviceBase::new("PCI device".to_string()), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 8, - name: "PCI device".to_string(), parent_bus: Arc::downgrade(&bus), }, }; diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 655e5b038..2da662ed4 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -137,8 +137,6 @@ pub struct PciDevBase { pub config: PciConfig, /// Devfn. pub devfn: u8, - /// Name of this device. - pub name: String, /// Primary Bus. pub parent_bus: Weak>, } @@ -478,7 +476,6 @@ mod tests { base: DeviceBase::new("PCI device".to_string()), config: PciConfig::new(1, 1), devfn: 0, - name: "PCI device".to_string(), parent_bus: Arc::downgrade(&parent_bus), }, }; diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 50c10f517..d1161e3b2 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -105,10 +105,9 @@ impl RootPort { Self { base: PciDevBase { - base: DeviceBase::new(name.clone()), + base: DeviceBase::new(name), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, - name, parent_bus, }, port_num, @@ -210,16 +209,12 @@ impl RootPort { let mut locked_dev = dev.lock().unwrap(); if let Err(e) = locked_dev.unrealize() { error!("{}", format!("{:?}", e)); - error!("Failed to unrealize device {}.", locked_dev.pci_base().name); + error!("Failed to unrealize device {}.", locked_dev.name()); } - info!( - "Device {} unplug from {}", - locked_dev.pci_base().name, - self.base.name - ); + info!("Device {} unplug from {}", locked_dev.name(), self.name()); // Send QMP event for successful hot unplugging. - send_device_deleted_msg(&locked_dev.pci_base().name); + send_device_deleted_msg(&locked_dev.name()); } self.sec_bus.lock().unwrap().devices.clear(); } @@ -383,13 +378,13 @@ impl PciDevOps for RootPort { 1, &mut self.base.config, self.dev_id.clone(), - &self.base.name, + &self.base.base.id, None, None, )?; init_intx( - self.base.name.clone(), + self.name(), &mut self.base.config, self.base.parent_bus.clone(), self.base.devfn, @@ -407,7 +402,7 @@ impl PciDevOps for RootPort { .add_subregion(self.sec_bus.lock().unwrap().mem_region.clone(), 0) .with_context(|| "Failed to register subregion in memory space.")?; - let name = self.base.name.clone(); + let name = self.name(); let root_port = Arc::new(Mutex::new(self)); #[allow(unused_mut)] let mut locked_root_port = root_port.lock().unwrap(); @@ -427,7 +422,7 @@ impl PciDevOps for RootPort { bail!( "Devfn {:?} has been used by {:?}", locked_root_port.base.devfn, - pci_device.unwrap().lock().unwrap().pci_base().name + pci_device.unwrap().lock().unwrap().name() ); } // Need to drop locked_root_port in order to register root_port instance. @@ -480,7 +475,8 @@ impl PciDevOps for RootPort { if let Err(e) = self.reset(true) { error!( "Failed to reset child devices under root port {}: {:?}", - self.base.name, e + self.name(), + e ) } } diff --git a/util/src/device.rs b/util/src/device.rs index 3ac832ede..d8e53b83f 100644 --- a/util/src/device.rs +++ b/util/src/device.rs @@ -26,4 +26,9 @@ pub trait Device { fn device_base(&self) -> &DeviceBase; fn device_base_mut(&mut self) -> &mut DeviceBase; + + /// Get device name. + fn name(&self) -> String { + self.device_base().id.clone() + } } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 7cf65998f..fb14ea6a2 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -113,10 +113,9 @@ impl VfioPciDevice { Self { // Unknown PCI or PCIe type here, allocate enough space to match the two types. base: PciDevBase { - base: DeviceBase::new(name.clone()), + base: DeviceBase::new(name), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), devfn, - name, parent_bus, }, config_size: 0, @@ -889,7 +888,7 @@ impl PciDevOps for VfioPciDevice { bail!( "Devfn {:?} has been used by {:?}", &devfn, - pci_device.unwrap().lock().unwrap().pci_base().name + pci_device.unwrap().lock().unwrap().name() ); } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 79a13a390..415285dbe 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -292,10 +292,9 @@ impl VirtioPciDevice { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { base: PciDevBase { - base: DeviceBase::new(name.clone()), + base: DeviceBase::new(name), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), devfn, - name, parent_bus, }, device, @@ -1040,19 +1039,18 @@ impl PciDevOps for VirtioPciDevice { )?; let nvectors = self.device.lock().unwrap().queue_num() + 1; - init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, nvectors as u32, &mut self.base.config, self.dev_id.clone(), - &self.base.name, + &self.base.base.id, None, None, )?; init_intx( - self.base.name.clone(), + self.name(), &mut self.base.config, self.base.parent_bus.clone(), self.base.devfn, @@ -1066,7 +1064,7 @@ impl PciDevOps for VirtioPciDevice { .realize() .with_context(|| "Failed to realize virtio device")?; - let name = self.base.name.clone(); + let name = self.name(); let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) @@ -1095,7 +1093,7 @@ impl PciDevOps for VirtioPciDevice { bail!( "Devfn {:?} has been used by {:?}", &devfn, - pci_device.unwrap().lock().unwrap().pci_base().name + pci_device.unwrap().lock().unwrap().name() ); } @@ -1114,11 +1112,8 @@ impl PciDevOps for VirtioPciDevice { let bus = self.base.parent_bus.upgrade().unwrap(); self.base.config.unregister_bars(&bus)?; - MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.base.name); - MigrationManager::unregister_transport_instance( - VirtioPciState::descriptor(), - &self.base.name, - ); + MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name()); + MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name()); Ok(()) } @@ -1506,12 +1501,13 @@ mod tests { false, ); + let id = virtio_pci.name(); assert!(init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, (virtio_dev.lock().unwrap().queue_num() + 1) as u32, &mut virtio_pci.base.config, virtio_pci.dev_id.clone(), - &virtio_pci.base.name, + &id, None, None, ) @@ -1659,19 +1655,20 @@ mod tests { #[cfg(target_arch = "aarch64")] virtio_pci.base.config.set_interrupt_pin(); + let id = virtio_pci.name(); init_msix( VIRTIO_PCI_MSIX_BAR_IDX as usize, virtio_pci.device.lock().unwrap().queue_num() as u32 + 1, &mut virtio_pci.base.config, virtio_pci.dev_id.clone(), - &virtio_pci.base.name, + &id, None, None, ) .unwrap(); init_intx( - virtio_pci.base.name.clone(), + id, &mut virtio_pci.base.config, virtio_pci.base.parent_bus.clone(), virtio_pci.base.devfn, -- Gitee From fb8516eb59e1a0d8bb1dccd61138ef836c8f3c1d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 7 Aug 2023 10:52:26 +0800 Subject: [PATCH 1279/2187] Refactory: set default read_config() function to read pci config space Set default read_config() function to read pci config space. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 4 ---- devices/src/usb/xhci/xhci_pci.rs | 4 ---- machine/src/standard_vm/aarch64/pci_host_root.rs | 4 ---- machine/src/standard_vm/x86_64/ich9_lpc.rs | 4 ---- machine/src/standard_vm/x86_64/mch.rs | 4 ---- pci/src/bus.rs | 4 ---- pci/src/demo_dev.rs | 5 ----- pci/src/host.rs | 4 ---- pci/src/lib.rs | 6 +++--- pci/src/root_port.rs | 4 ---- 10 files changed, 3 insertions(+), 40 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index dd24255b7..69295c7f5 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -150,10 +150,6 @@ impl PciDevOps for Ivshmem { Ok(()) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index d0e832c97..70d5c1c4b 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -321,10 +321,6 @@ impl PciDevOps for XhciPciDevice { Some(self.base.devfn) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { update_dev_id(&self.base.parent_bus, self.base.devfn, &self.dev_id); let parent_bus = self.base.parent_bus.upgrade().unwrap(); diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 3d2b2f985..564b6d48f 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -90,10 +90,6 @@ impl PciDevOps for PciHostRoot { Ok(()) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { self.base.config.write(offset, data, 0, None); } diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index fc4fdb357..9922fce0a 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -300,10 +300,6 @@ impl PciDevOps for LPCBridge { Ok(()) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { self.base.config.write(offset, data, 0, None, None); if ranges_overlap(offset, data.len(), PM_BASE_OFFSET as usize, 4) { diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 7c6f43a12..3a4b7f8c8 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -169,10 +169,6 @@ impl PciDevOps for Mch { Ok(()) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { let old_pciexbar: u64 = le_read_u64(&self.base.config.config, PCIEXBAR as usize).unwrap(); self.base.config.write(offset, data, 0, None, None); diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 7a97c0fff..9105b23b1 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -282,10 +282,6 @@ mod tests { &mut self.base } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { #[allow(unused_variables)] self.base.config.write( diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 87c45515b..194d683e2 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -215,11 +215,6 @@ impl PciDevOps for DemoDev { self.device.lock().unwrap().unrealize() } - /// read the pci configuration space - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.base.parent_bus.upgrade().unwrap(); diff --git a/pci/src/host.rs b/pci/src/host.rs index 3260c5bf6..ba6b8ed10 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -588,10 +588,6 @@ pub mod tests { Ok(()) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { #[allow(unused_variables)] self.base.config.write( diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 2da662ed4..66ab669ce 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -192,7 +192,9 @@ pub trait PciDevOps: Device + Send + AsAny { /// /// * `offset` - Offset in configuration space. /// * `data` - Data buffer for reading. - fn read_config(&mut self, offset: usize, data: &mut [u8]); + fn read_config(&mut self, offset: usize, data: &mut [u8]) { + self.pci_base_mut().config.read(offset, data); + } /// Configuration space write. /// @@ -450,8 +452,6 @@ mod tests { &mut self.base } - fn read_config(&mut self, _offset: usize, _data: &mut [u8]) {} - fn write_config(&mut self, _offset: usize, _data: &[u8]) {} fn realize(self) -> Result<()> { diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index d1161e3b2..1d5e75a3f 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -432,10 +432,6 @@ impl PciDevOps for RootPort { Ok(()) } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { - self.base.config.read(offset, data); - } - fn write_config(&mut self, offset: usize, data: &[u8]) { let size = data.len(); let end = offset + size; -- Gitee From 656bc67e4b85e0b9431d4df5d4e24b5adf4f63ff Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 7 Aug 2023 15:23:26 +0800 Subject: [PATCH 1280/2187] Refactory: add hotpluggable property in devicebase struct Pci device use devfn() and unrealize() function to determine if hot plugging/unplugging is possible. In fact, each device attached in pci bus should have one devfn. So, add new "hotpluggable" property to indicate hot plug/unplug capability, and then, we can remove devfn() function, and use pci_base().devfn to get it when we need. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 2 +- devices/src/scsi/disk.rs | 2 +- devices/src/usb/mod.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 6 +--- .../src/standard_vm/aarch64/pci_host_root.rs | 2 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 2 +- machine/src/standard_vm/x86_64/mch.rs | 2 +- pci/src/bus.rs | 14 +++----- pci/src/demo_dev.rs | 7 +--- pci/src/host.rs | 2 +- pci/src/intx.rs | 2 +- pci/src/lib.rs | 7 +--- pci/src/root_port.rs | 33 ++++++++----------- util/src/device.rs | 11 +++++-- vfio/src/vfio_pci.rs | 6 +--- virtio/src/transport/virtio_pci.rs | 6 +--- 16 files changed, 39 insertions(+), 67 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 69295c7f5..0b6975de9 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -51,7 +51,7 @@ impl Ivshmem { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(name), + base: DeviceBase::new(name, false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), devfn, parent_bus, diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 9736b56fe..7bae3660e 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -132,7 +132,7 @@ impl ScsiDevice { drive_files: Arc>>, ) -> ScsiDevice { ScsiDevice { - base: DeviceBase::new(config.id.clone()), + base: DeviceBase::new(config.id.clone(), false), config, state: ScsiDevState::new(), block_backend: None, diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 0fdfda004..ba44f018f 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -143,7 +143,7 @@ impl Device for UsbDevice { impl UsbDevice { pub fn new(id: String, data_buf_len: usize) -> Self { let mut dev = UsbDevice { - base: DeviceBase::new(id), + base: DeviceBase::new(id, false), port: None, speed: 0, addr: 0, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 70d5c1c4b..9e305032c 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -78,7 +78,7 @@ impl XhciPciDevice { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(config.id.clone().unwrap()), + base: DeviceBase::new(config.id.clone().unwrap(), true), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, parent_bus, @@ -317,10 +317,6 @@ impl PciDevOps for XhciPciDevice { Ok(()) } - fn devfn(&self) -> Option { - Some(self.base.devfn) - } - fn write_config(&mut self, offset: usize, data: &[u8]) { update_dev_id(&self.base.parent_bus, self.base.devfn, &self.dev_id); let parent_bus = self.base.parent_bus.upgrade().unwrap(); diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 564b6d48f..e7f0b6641 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -32,7 +32,7 @@ impl PciHostRoot { pub fn new(parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new("PCI Host Root".to_string()), + base: DeviceBase::new("PCI Host Root".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), parent_bus, devfn: 0, diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 9922fce0a..2151eea77 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -64,7 +64,7 @@ impl LPCBridge { ) -> Result { Ok(Self { base: PciDevBase { - base: DeviceBase::new("ICH9 LPC bridge".to_string()), + base: DeviceBase::new("ICH9 LPC bridge".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0x1F << 3, parent_bus, diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 3a4b7f8c8..faf112c95 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -55,7 +55,7 @@ impl Mch { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new("Memory Controller Hub".to_string()), + base: DeviceBase::new("Memory Controller Hub".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, parent_bus, diff --git a/pci/src/bus.rs b/pci/src/bus.rs index 9105b23b1..58380f171 100644 --- a/pci/src/bus.rs +++ b/pci/src/bus.rs @@ -188,9 +188,7 @@ impl PciBus { .unrealize() .with_context(|| format!("Failed to unrealize device {}", dev_locked.name()))?; - let devfn = dev_locked - .devfn() - .with_context(|| format!("Failed to get devfn: device {}", dev_locked.name()))?; + let devfn = dev_locked.pci_base().devfn; let mut locked_bus = bus.lock().unwrap(); if locked_bus.devices.get(&devfn).is_some() { locked_bus.devices.remove(&devfn); @@ -316,10 +314,6 @@ mod tests { fn unrealize(&mut self) -> Result<()> { Ok(()) } - - fn devfn(&self) -> Option { - Some(0) - } } pub fn create_pci_host() -> Arc> { @@ -357,7 +351,7 @@ mod tests { // Test device is attached to the root bus. let pci_dev = PciDevice { base: PciDevBase { - base: DeviceBase::new("test1".to_string()), + base: DeviceBase::new("test1".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 10, parent_bus: root_bus.clone(), @@ -369,7 +363,7 @@ mod tests { let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = PciDevice { base: PciDevBase { - base: DeviceBase::new("test2".to_string()), + base: DeviceBase::new("test2".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 12, parent_bus: Arc::downgrade(&bus), @@ -405,7 +399,7 @@ mod tests { let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = PciDevice { base: PciDevBase { - base: DeviceBase::new("test1".to_string()), + base: DeviceBase::new("test1".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, parent_bus: Arc::downgrade(&bus), diff --git a/pci/src/demo_dev.rs b/pci/src/demo_dev.rs index 194d683e2..66aaf27ea 100644 --- a/pci/src/demo_dev.rs +++ b/pci/src/demo_dev.rs @@ -83,7 +83,7 @@ impl DemoDev { }; DemoDev { base: PciDevBase { - base: DeviceBase::new(cfg.id.clone()), + base: DeviceBase::new(cfg.id.clone(), false), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), devfn, parent_bus, @@ -234,11 +234,6 @@ impl PciDevOps for DemoDev { fn reset(&mut self, _reset_child_device: bool) -> Result<()> { self.base.config.reset_common_regs() } - - /// Get device devfn - fn devfn(&self) -> Option { - Some(self.base.devfn) - } } pub trait DeviceTypeOperation: Send { diff --git a/pci/src/host.rs b/pci/src/host.rs index ba6b8ed10..8f2e647e6 100644 --- a/pci/src/host.rs +++ b/pci/src/host.rs @@ -737,7 +737,7 @@ pub mod tests { let bus = PciBus::find_bus_by_name(&pci_host.lock().unwrap().root_bus, "pcie.2").unwrap(); let pci_dev = PciDevice { base: PciDevBase { - base: DeviceBase::new("PCI device".to_string()), + base: DeviceBase::new("PCI device".to_string(), false), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 8, parent_bus: Arc::downgrade(&bus), diff --git a/pci/src/intx.rs b/pci/src/intx.rs index 50f727051..20da36d96 100644 --- a/pci/src/intx.rs +++ b/pci/src/intx.rs @@ -142,7 +142,7 @@ pub fn init_intx( let parent_bridge = parent_bridge.upgrade().unwrap(); let locked_parent_bridge = parent_bridge.lock().unwrap(); ( - swizzle_map_irq(locked_parent_bridge.devfn().unwrap(), pin), + swizzle_map_irq(locked_parent_bridge.pci_base().devfn, pin), locked_parent_bridge.get_intx_state(), ) } diff --git a/pci/src/lib.rs b/pci/src/lib.rs index 66ab669ce..53a4fac6d 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -224,11 +224,6 @@ pub trait PciDevOps: Device + Send + AsAny { Ok(()) } - /// Get device devfn - fn devfn(&self) -> Option { - None - } - /// Get the path of the PCI bus where the device resides. fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { let locked_parent_bus = parent_bus.lock().unwrap(); @@ -473,7 +468,7 @@ mod tests { let dev = PciDev { base: PciDevBase { - base: DeviceBase::new("PCI device".to_string()), + base: DeviceBase::new("PCI device".to_string(), false), config: PciConfig::new(1, 1), devfn: 0, parent_bus: Arc::downgrade(&parent_bus), diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs index 1d5e75a3f..4e8b3bde7 100644 --- a/pci/src/root_port.rs +++ b/pci/src/root_port.rs @@ -105,7 +105,7 @@ impl RootPort { Self { base: PciDevBase { - base: DeviceBase::new(name), + base: DeviceBase::new(name, true), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, parent_bus, @@ -507,10 +507,6 @@ impl PciDevOps for RootPort { self.do_unplug(offset, data, old_ctl, old_status); } - fn devfn(&self) -> Option { - Some(self.base.devfn) - } - /// Only set slot status to on, and no other device reset actions are implemented. fn reset(&mut self, reset_child_device: bool) -> Result<()> { if reset_child_device { @@ -562,11 +558,10 @@ impl PciDevOps for RootPort { impl HotplugOps for RootPort { fn plug(&mut self, dev: &Arc>) -> Result<()> { - let devfn = dev - .lock() - .unwrap() - .devfn() - .with_context(|| "Failed to get devfn")?; + if !dev.lock().unwrap().hotpluggable() { + bail!("Don't support hot-plug!"); + } + let devfn = dev.lock().unwrap().pci_base().devfn; // Only if devfn is equal to 0, hot plugging is supported. if devfn != 0 { return Err(anyhow!(PciError::HotplugUnsupported(devfn))); @@ -600,11 +595,10 @@ impl HotplugOps for RootPort { bail!("Guest is still on the fly of another (un)plugging"); } - let devfn = dev - .lock() - .unwrap() - .devfn() - .with_context(|| "Failed to get devfn")?; + if !dev.lock().unwrap().hotpluggable() { + bail!("Don't support hot-unplug request!"); + } + let devfn = dev.lock().unwrap().pci_base().devfn; if devfn != 0 { return self.unplug(dev); } @@ -643,11 +637,10 @@ impl HotplugOps for RootPort { } fn unplug(&mut self, dev: &Arc>) -> Result<()> { - let devfn = dev - .lock() - .unwrap() - .devfn() - .with_context(|| "Failed to get devfn")?; + if !dev.lock().unwrap().hotpluggable() { + bail!("Don't support hot-unplug!"); + } + let devfn = dev.lock().unwrap().pci_base().devfn; let mut locked_dev = dev.lock().unwrap(); locked_dev.unrealize()?; self.sec_bus.lock().unwrap().devices.remove(&devfn); diff --git a/util/src/device.rs b/util/src/device.rs index d8e53b83f..e85c14c0b 100644 --- a/util/src/device.rs +++ b/util/src/device.rs @@ -14,11 +14,13 @@ pub struct DeviceBase { /// Name of this device. pub id: String, + /// Whether it supports hot-plug/hot-unplug. + pub hotpluggable: bool, } impl DeviceBase { - pub fn new(id: String) -> Self { - DeviceBase { id } + pub fn new(id: String, hotpluggable: bool) -> Self { + DeviceBase { id, hotpluggable } } } @@ -31,4 +33,9 @@ pub trait Device { fn name(&self) -> String { self.device_base().id.clone() } + + /// Query whether it supports hot-plug/hot-unplug. + fn hotpluggable(&self) -> bool { + self.device_base().hotpluggable + } } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index fb14ea6a2..3f7fa4abb 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -113,7 +113,7 @@ impl VfioPciDevice { Self { // Unknown PCI or PCIe type here, allocate enough space to match the two types. base: PciDevBase { - base: DeviceBase::new(name), + base: DeviceBase::new(name, true), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), devfn, parent_bus, @@ -903,10 +903,6 @@ impl PciDevOps for VfioPciDevice { Ok(()) } - fn devfn(&self) -> Option { - Some(self.base.devfn) - } - /// Read pci data from pci config if it emulate, otherwise read from vfio device. fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 415285dbe..33697049b 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -292,7 +292,7 @@ impl VirtioPciDevice { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { base: PciDevBase { - base: DeviceBase::new(name), + base: DeviceBase::new(name, true), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), devfn, parent_bus, @@ -1147,10 +1147,6 @@ impl PciDevOps for VirtioPciDevice { self.do_cfg_access(offset, end, true); } - fn devfn(&self) -> Option { - Some(self.base.devfn) - } - fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { self.deactivate_device(); self.device -- Gitee From ea6c1f6d8c692a6855248e1c3c943b1668f1bdb0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 7 Aug 2023 19:22:45 +0800 Subject: [PATCH 1281/2187] Refactory: move pci/sysbus to devices lib Move pci/sysbus to devices lib. Signed-off-by: liuxiangdong --- Cargo.lock | 48 +-------------- devices/Cargo.toml | 3 - devices/src/acpi/ged.rs | 4 +- devices/src/acpi/power.rs | 4 +- devices/src/legacy/error.rs | 2 +- devices/src/legacy/fwcfg.rs | 14 ++--- devices/src/legacy/pflash.rs | 18 +++--- devices/src/legacy/pl011.rs | 4 +- devices/src/legacy/pl031.rs | 4 +- devices/src/legacy/ramfb.rs | 4 +- devices/src/legacy/rtc.rs | 6 +- devices/src/legacy/serial.rs | 6 +- devices/src/lib.rs | 32 ++++++++++ devices/src/misc/ivshmem.rs | 12 ++-- devices/src/misc/scream/mod.rs | 2 +- {pci/src => devices/src/pci}/bus.rs | 10 ++-- {pci/src => devices/src/pci}/config.rs | 8 +-- {pci/src => devices/src/pci}/demo_dev.rs | 14 ++--- .../src/pci}/demo_device/base_device.rs | 2 +- .../src/pci}/demo_device/dpy_device.rs | 2 +- .../src/pci}/demo_device/gpu_device.rs | 2 +- .../pci}/demo_device/kbd_pointer_device.rs | 2 +- .../src/pci}/demo_device/mod.rs | 0 {pci/src => devices/src/pci}/error.rs | 0 {pci/src => devices/src/pci}/host.rs | 32 +++++----- {pci/src => devices/src/pci}/hotplug.rs | 2 +- {pci/src => devices/src/pci}/intx.rs | 2 +- pci/src/lib.rs => devices/src/pci/mod.rs | 10 ++-- {pci/src => devices/src/pci}/msix.rs | 8 ++- {pci/src => devices/src/pci}/root_port.rs | 18 +++--- devices/src/scsi/disk.rs | 2 +- {sysbus/src => devices/src/sysbus}/error.rs | 0 .../src/lib.rs => devices/src/sysbus/mod.rs | 2 +- devices/src/usb/error.rs | 2 +- devices/src/usb/mod.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 60 ++++++++----------- machine/Cargo.toml | 2 - machine/src/lib.rs | 4 +- machine/src/micro_vm/mod.rs | 6 +- machine/src/standard_vm/aarch64/mod.rs | 4 +- .../src/standard_vm/aarch64/pci_host_root.rs | 4 +- machine/src/standard_vm/error.rs | 2 +- machine/src/standard_vm/mod.rs | 4 +- machine/src/standard_vm/x86_64/ich9_lpc.rs | 8 +-- machine/src/standard_vm/x86_64/mch.rs | 6 +- machine/src/standard_vm/x86_64/mod.rs | 4 +- pci/Cargo.toml | 29 --------- sysbus/Cargo.toml | 17 ------ util/src/device.rs | 41 ------------- util/src/lib.rs | 1 - vfio/Cargo.toml | 2 +- vfio/src/error.rs | 2 +- vfio/src/vfio_pci.rs | 41 ++++++------- vhost_user_fs/Cargo.toml | 2 - virtio/Cargo.toml | 2 - virtio/src/error.rs | 2 +- virtio/src/transport/virtio_mmio.rs | 4 +- virtio/src/transport/virtio_pci.rs | 16 ++--- 58 files changed, 216 insertions(+), 330 deletions(-) rename {pci/src => devices/src/pci}/bus.rs (98%) rename {pci/src => devices/src/pci}/config.rs (99%) rename {pci/src => devices/src/pci}/demo_dev.rs (96%) rename {pci/src => devices/src/pci}/demo_device/base_device.rs (97%) rename {pci/src => devices/src/pci}/demo_device/dpy_device.rs (99%) rename {pci/src => devices/src/pci}/demo_device/gpu_device.rs (99%) rename {pci/src => devices/src/pci}/demo_device/kbd_pointer_device.rs (99%) rename {pci/src => devices/src/pci}/demo_device/mod.rs (100%) rename {pci/src => devices/src/pci}/error.rs (100%) rename {pci/src => devices/src/pci}/host.rs (97%) rename {pci/src => devices/src/pci}/hotplug.rs (98%) rename {pci/src => devices/src/pci}/intx.rs (98%) rename pci/src/lib.rs => devices/src/pci/mod.rs (98%) rename {pci/src => devices/src/pci}/msix.rs (99%) rename {pci/src => devices/src/pci}/root_port.rs (98%) rename {sysbus/src => devices/src/sysbus}/error.rs (100%) rename sysbus/src/lib.rs => devices/src/sysbus/mod.rs (99%) delete mode 100644 pci/Cargo.toml delete mode 100644 sysbus/Cargo.toml delete mode 100644 util/src/device.rs diff --git a/Cargo.lock b/Cargo.lock index a47cf3d4c..67758cb64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,12 +341,10 @@ dependencies = [ "migration", "migration_derive", "once_cell", - "pci", "rand", "rusb", "serde", "serde_json", - "sysbus", "thiserror", "ui", "util", @@ -908,10 +906,8 @@ dependencies = [ "machine_manager", "migration", "migration_derive", - "pci", "serde_json", "smbios", - "sysbus", "thiserror", "ui", "util", @@ -1163,30 +1159,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "pci" -version = "2.2.0" -dependencies = [ - "acpi", - "address_space", - "anyhow", - "byteorder", - "hypervisor", - "kvm-bindings", - "kvm-ioctls", - "libc", - "log", - "machine_manager", - "migration", - "migration_derive", - "once_cell", - "sysbus", - "thiserror", - "ui", - "util", - "vmm-sys-util", -] - [[package]] name = "peeking_take_while" version = "0.1.2" @@ -1616,20 +1588,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sysbus" -version = "2.2.0" -dependencies = [ - "acpi", - "address_space", - "anyhow", - "hypervisor", - "kvm-ioctls", - "thiserror", - "util", - "vmm-sys-util", -] - [[package]] name = "system-deps" version = "6.1.0" @@ -1815,13 +1773,13 @@ dependencies = [ "address_space", "anyhow", "byteorder", + "devices", "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", "log", "once_cell", - "pci", "thiserror", "util", "vfio-bindings", @@ -1850,8 +1808,6 @@ dependencies = [ "migration", "migration_derive", "nix 0.26.2", - "pci", - "sysbus", "thiserror", "util", "virtio", @@ -1876,9 +1832,7 @@ dependencies = [ "migration", "migration_derive", "once_cell", - "pci", "serde_json", - "sysbus", "thiserror", "ui", "util", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 48c7f4e05..44c71f494 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -26,15 +26,12 @@ hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } -sysbus = { path = "../sysbus" } -pci = { path = "../pci" } util = { path = "../util" } acpi = { path = "../acpi" } block_backend = { path = "../block_backend"} [target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } -pci = { path = "../pci" } pulse = { version = "2.27", package = "libpulse-binding" } psimple = { version = "2.27", package = "libpulse-simple-binding" } rusb = "0.9" diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 0ca53aef7..73aae0aca 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; +use crate::{Device, DeviceBase}; use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; use address_space::GuestAddress; use anyhow::{Context, Result}; @@ -20,8 +22,6 @@ use machine_manager::qmp::QmpChannel; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; -use util::device::{Device, DeviceBase}; use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; use vmm_sys_util::epoll::EventSet; diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index a15abb015..152c35486 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -14,13 +14,14 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use std::time::Duration; +use crate::{Device, DeviceBase}; use anyhow::{Context, Result}; use log::info; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use util::num_ops::write_data_u32; use crate::acpi::ged::{AcpiEvent, Ged}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; use acpi::{ AmlAddressSpaceType, AmlBuilder, AmlDevice, AmlField, AmlFieldUnit, AmlIndex, AmlInteger, @@ -31,7 +32,6 @@ use address_space::GuestAddress; use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; const AML_ACAD_REG: &str = "ADPM"; const AML_ACAD_ONLINE: &str = "ADPO"; diff --git a/devices/src/legacy/error.rs b/devices/src/legacy/error.rs index 1a1d4b728..3495be54a 100644 --- a/devices/src/legacy/error.rs +++ b/devices/src/legacy/error.rs @@ -17,7 +17,7 @@ pub enum LegacyError { #[error("SysBus")] SysBus { #[from] - source: sysbus::error::SysBusError, + source: crate::sysbus::error::SysBusError, }, #[error("AddressSpace")] AddressSpace { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 8d007ff2a..0b546a96a 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -13,6 +13,8 @@ use std::sync::{Arc, Mutex}; use crate::legacy::error::LegacyError; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::{Device, DeviceBase}; use acpi::{ AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, AmlString, }; @@ -26,9 +28,7 @@ use anyhow::{anyhow, bail, Context, Result}; use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; use log::{error, warn}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use util::num_ops::extract_u64; use util::offset_of; #[cfg(target_arch = "x86_64")] @@ -990,14 +990,14 @@ impl SysBusDevOps for FwCfgMem { _sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> sysbus::Result<()> { + ) -> Result<()> { let res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) } - fn reset(&mut self) -> sysbus::Result<()> { + fn reset(&mut self) -> Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } @@ -1175,14 +1175,14 @@ impl SysBusDevOps for FwCfgIO { _sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> sysbus::Result<()> { + ) -> Result<()> { let mut res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) } - fn reset(&mut self) -> sysbus::Result<()> { + fn reset(&mut self) -> Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } @@ -1303,8 +1303,8 @@ impl AmlBuilder for FwCfgIO { #[cfg(test)] mod test { use super::*; + use crate::sysbus::{IRQ_BASE, IRQ_MAX}; use address_space::{AddressSpace, HostMemMapping, Region}; - use sysbus::{IRQ_BASE, IRQ_MAX}; fn sysbus_init() -> SysBus { let sys_mem = AddressSpace::new( diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index eb4dc81fa..80f4a1e98 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -15,12 +15,12 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use super::error::LegacyError; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use anyhow::{anyhow, bail, Context, Result}; use log::{debug, error, warn}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; -use util::device::{Device, DeviceBase}; use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; pub struct PFlash { @@ -850,7 +850,7 @@ impl SysBusDevOps for PFlash { _sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> sysbus::Result<()> { + ) -> Result<()> { let res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; @@ -858,10 +858,12 @@ impl SysBusDevOps for PFlash { Ok(()) } - fn reset(&mut self) -> sysbus::Result<()> { - sysbus::Result::with_context(self.rom.as_ref().unwrap().set_rom_device_romd(true), || { - "Fail to set PFlash rom region read only" - })?; + fn reset(&mut self) -> Result<()> { + self.rom + .as_ref() + .unwrap() + .set_rom_device_romd(true) + .with_context(|| "Fail to set PFlash rom region read only")?; self.cmd = 0x00; self.write_cycle = 0; self.status = 0x80; @@ -878,10 +880,10 @@ impl AmlBuilder for PFlash { #[cfg(test)] mod test { use super::*; + use crate::sysbus::{IRQ_BASE, IRQ_MAX}; use address_space::AddressSpace; use std::fs; pub use std::fs::File; - use sysbus::{IRQ_BASE, IRQ_MAX}; fn sysbus_init() -> SysBus { let sys_mem = AddressSpace::new( diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 35a48b077..eca9ef564 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -14,6 +14,8 @@ use std::sync::{Arc, Mutex}; use super::chardev::{Chardev, InputReceiver}; use super::error::LegacyError; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::{Device, DeviceBase}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlMemory32Fixed, AmlNameDecl, AmlReadAndWrite, AmlResTemplate, AmlResourceUsage, @@ -31,9 +33,7 @@ use migration::{ MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use util::loop_context::EventNotifierHelper; use util::num_ops::read_data_u32; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 5f09a2022..39ea28a01 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -14,6 +14,8 @@ use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; use super::error::LegacyError; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::GuestAddress; use anyhow::{Context, Result}; @@ -24,9 +26,7 @@ use migration::{ MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use util::num_ops::write_data_u32; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 776d76884..666c977fd 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -21,16 +21,16 @@ use log::error; use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; use crate::legacy::Result; +use crate::sysbus::{Result as SysBusResult, SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; +use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; -use sysbus::{Result as SysBusResult, SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use ui::console::{ console_init, display_graphic_update, display_replace_surface, set_run_stage, ConsoleType, DisplayConsole, DisplaySurface, HardWareOperations, VmRunningStage, }; use ui::input::{key_event, KEYCODE_RET}; -use util::device::{Device, DeviceBase}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; const BYTES_PER_PIXELS: u32 = 8; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index de0f4f0b2..ecd71517f 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -13,6 +13,8 @@ use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::{Device, DeviceBase}; use acpi::{ AmlBuilder, AmlDevice, AmlEisaId, AmlIoDecode, AmlIoResource, AmlIrqNoFlags, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, @@ -20,8 +22,6 @@ use acpi::{ use address_space::GuestAddress; use anyhow::Result; use log::{debug, error, warn}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; -use util::device::{Device, DeviceBase}; use vmm_sys_util::eventfd::EventFd; use util::time::{mktime64, NANOSECONDS_PER_SECOND}; @@ -402,7 +402,7 @@ impl SysBusDevOps for RTC { Some(&mut self.base.res) } - fn reset(&mut self) -> sysbus::Result<()> { + fn reset(&mut self) -> Result<()> { self.cmos_data.fill(0); self.init_rtc_reg(); self.set_memory(self.mem_size, self.gap_start); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 2070bafb4..2f990c9e1 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -13,6 +13,8 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::{Device, DeviceBase}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlIoDecode, AmlIoResource, AmlNameDecl, AmlResTemplate, @@ -29,9 +31,7 @@ use migration::{ MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use util::loop_context::EventNotifierHelper; use vmm_sys_util::eventfd::EventFd; @@ -391,7 +391,7 @@ impl SysBusDevOps for Serial { self.write_internal(offset, data[0]).is_ok() } - fn set_irq(&mut self, _sysbus: &mut SysBus) -> sysbus::Result { + fn set_irq(&mut self, _sysbus: &mut SysBus) -> Result { let mut irq: i32 = -1; if let Some(e) = self.interrupt_evt() { irq = UART_IRQ; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index eb7fbb7b9..526d6e55a 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -22,7 +22,9 @@ pub mod camera_backend; mod interrupt_controller; pub mod legacy; pub mod misc; +pub mod pci; pub mod scsi; +pub mod sysbus; pub mod usb; #[cfg(target_arch = "aarch64")] @@ -33,3 +35,33 @@ pub use interrupt_controller::{ pub use legacy::error::LegacyError as LegacyErrs; pub use scsi::bus as ScsiBus; pub use scsi::disk as ScsiDisk; + +#[derive(Clone, Default)] +pub struct DeviceBase { + /// Name of this device + pub id: String, + /// Whether it supports hot-plug/hot-unplug. + pub hotpluggable: bool, +} + +impl DeviceBase { + pub fn new(id: String, hotpluggable: bool) -> Self { + DeviceBase { id, hotpluggable } + } +} + +pub trait Device { + fn device_base(&self) -> &DeviceBase; + + fn device_base_mut(&mut self) -> &mut DeviceBase; + + /// Get device name. + fn name(&self) -> String { + self.device_base().id.clone() + } + + /// Query whether it supports hot-plug/hot-unplug. + fn hotpluggable(&self) -> bool { + self.device_base().hotpluggable + } +} diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 0b6975de9..e5c7cd74f 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -15,17 +15,17 @@ use std::sync::{ Arc, Mutex, Weak, }; -use anyhow::bail; +use anyhow::{bail, Result}; -use address_space::{GuestAddress, Region, RegionOps}; -use pci::{ +use crate::pci::{ config::{ PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }, le_write_u16, PciBus, PciDevBase, PciDevOps, }; -use util::device::{Device, DeviceBase}; +use crate::{Device, DeviceBase}; +use address_space::{GuestAddress, Region, RegionOps}; const PCI_VENDOR_ID_IVSHMEM: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; const PCI_DEVICE_ID_IVSHMEM: u16 = 0x1110; @@ -61,7 +61,7 @@ impl Ivshmem { } } - fn register_bars(&mut self) -> pci::Result<()> { + fn register_bars(&mut self) -> Result<()> { // Currently, ivshmem uses only the shared memory and does not use interrupt. // Therefore, bar0 read and write callback is not implemented. let reg_read = move |_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }; @@ -110,7 +110,7 @@ impl PciDevOps for Ivshmem { &mut self.base } - fn realize(mut self) -> pci::Result<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; le_write_u16( diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 99479a74c..c757bd320 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -30,8 +30,8 @@ use log::{error, warn}; use self::{alsa::AlsaStreamData, audio_demo::AudioDemo}; use super::ivshmem::Ivshmem; +use crate::pci::{PciBus, PciDevOps}; use machine_manager::config::scream::ScreamConfig; -use pci::{PciBus, PciDevOps}; use pulseaudio::{PulseStreamData, TARGET_LATENCY_MS}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; diff --git a/pci/src/bus.rs b/devices/src/pci/bus.rs similarity index 98% rename from pci/src/bus.rs rename to devices/src/pci/bus.rs index 58380f171..e41774457 100644 --- a/pci/src/bus.rs +++ b/devices/src/pci/bus.rs @@ -249,12 +249,12 @@ mod tests { use address_space::{AddressSpace, Region}; use super::*; - use crate::bus::PciBus; - use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; - use crate::root_port::RootPort; - use crate::{PciDevBase, PciHost}; + use crate::pci::bus::PciBus; + use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; + use crate::pci::root_port::RootPort; + use crate::pci::{PciDevBase, PciHost}; + use crate::{Device, DeviceBase}; use anyhow::Result; - use util::device::{Device, DeviceBase}; #[derive(Clone)] struct PciDevice { diff --git a/pci/src/config.rs b/devices/src/pci/config.rs similarity index 99% rename from pci/src/config.rs rename to devices/src/pci/config.rs index 276070ae0..6149c1805 100644 --- a/pci/src/config.rs +++ b/devices/src/pci/config.rs @@ -16,13 +16,13 @@ use std::sync::{Arc, Mutex}; use address_space::Region; use log::{error, warn}; -use crate::intx::Intx; -use crate::msix::{is_msix_enabled, Msix, MSIX_TABLE_ENTRY_SIZE}; -use crate::{ +use crate::pci::intx::Intx; +use crate::pci::msix::{is_msix_enabled, Msix, MSIX_TABLE_ENTRY_SIZE}; +use crate::pci::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, }; -use crate::{ranges_overlap, PciError}; +use crate::pci::{ranges_overlap, PciError}; use anyhow::{anyhow, Context, Result}; /// Size in bytes of the configuration space of legacy PCI device. diff --git a/pci/src/demo_dev.rs b/devices/src/pci/demo_dev.rs similarity index 96% rename from pci/src/demo_dev.rs rename to devices/src/pci/demo_dev.rs index 66aaf27ea..61c6ee9b9 100644 --- a/pci/src/demo_dev.rs +++ b/devices/src/pci/demo_dev.rs @@ -42,19 +42,19 @@ use log::error; use machine_manager::config::DemoDevConfig; #[cfg(not(target_env = "musl"))] -use crate::demo_device::{ +use crate::pci::demo_device::{ dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, }; -use crate::{ +use crate::pci::{ config::{ - PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, PCIE_CONFIG_SPACE_SIZE, - SUB_CLASS_CODE, VENDOR_ID, + PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, + PCIE_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }, init_msix, le_write_u16, PciBus, PciDevOps, }; -use crate::{demo_device::base_device::BaseDevice, PciDevBase}; +use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; +use crate::{Device, DeviceBase}; pub use anyhow::{bail, Result}; -use util::device::{Device, DeviceBase}; pub struct DemoDev { base: PciDevBase, @@ -153,7 +153,7 @@ impl DemoDev { self.base.config.register_bar( 0, self.mem_region.clone(), - crate::config::RegionType::Mem64Bit, + RegionType::Mem64Bit, false, (self.cmd_cfg.bar_size * self.cmd_cfg.bar_num as u64).next_power_of_two(), )?; diff --git a/pci/src/demo_device/base_device.rs b/devices/src/pci/demo_device/base_device.rs similarity index 97% rename from pci/src/demo_device/base_device.rs rename to devices/src/pci/demo_device/base_device.rs index 852a1ac1c..6dad14f35 100644 --- a/pci/src/demo_device/base_device.rs +++ b/devices/src/pci/demo_device/base_device.rs @@ -12,7 +12,7 @@ /// BaseDevice is a simplest demo-pci-device. Its function is to /// multiply data written by two and return it when reading. -use crate::demo_dev::DeviceTypeOperation; +use crate::pci::demo_dev::DeviceTypeOperation; use address_space::GuestAddress; pub use anyhow::{bail, Result}; use std::collections::HashMap; diff --git a/pci/src/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs similarity index 99% rename from pci/src/demo_device/dpy_device.rs rename to devices/src/pci/demo_device/dpy_device.rs index ada4952c7..c85a8556d 100644 --- a/pci/src/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -17,7 +17,7 @@ // Currently, the supported operations are: // Get surface size, Get cursor image size, Get Surface data, Get cursor image data. -use crate::demo_dev::DeviceTypeOperation; +use crate::pci::demo_dev::DeviceTypeOperation; use address_space::{AddressSpace, GuestAddress}; use anyhow::{bail, Ok, Result}; use byteorder::{ByteOrder, LittleEndian}; diff --git a/pci/src/demo_device/gpu_device.rs b/devices/src/pci/demo_device/gpu_device.rs similarity index 99% rename from pci/src/demo_device/gpu_device.rs rename to devices/src/pci/demo_device/gpu_device.rs index 6d52f9a22..08a320ae4 100644 --- a/pci/src/demo_device/gpu_device.rs +++ b/devices/src/pci/demo_device/gpu_device.rs @@ -39,7 +39,7 @@ use ui::{ }; use util::pixman::pixman_format_code_t; pub const UPDATE_FACTOR: [u8; 7] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40]; -use crate::demo_dev::DeviceTypeOperation; +use crate::pci::demo_dev::DeviceTypeOperation; #[derive(Debug)] pub enum GpuEvent { diff --git a/pci/src/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs similarity index 99% rename from pci/src/demo_device/kbd_pointer_device.rs rename to devices/src/pci/demo_device/kbd_pointer_device.rs index 991af7551..3d2b41013 100644 --- a/pci/src/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -23,7 +23,7 @@ use ui::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; use anyhow::{bail, Result}; -use crate::demo_dev::DeviceTypeOperation; +use crate::pci::demo_dev::DeviceTypeOperation; static MEM_ADDR: Lazy>> = Lazy::new(|| { Arc::new(Mutex::new(MemSpace { sys_mem: None, diff --git a/pci/src/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs similarity index 100% rename from pci/src/demo_device/mod.rs rename to devices/src/pci/demo_device/mod.rs diff --git a/pci/src/error.rs b/devices/src/pci/error.rs similarity index 100% rename from pci/src/error.rs rename to devices/src/pci/error.rs diff --git a/pci/src/host.rs b/devices/src/pci/host.rs similarity index 97% rename from pci/src/host.rs rename to devices/src/pci/host.rs index 8f2e647e6..b6164e88a 100644 --- a/pci/src/host.rs +++ b/devices/src/pci/host.rs @@ -12,6 +12,8 @@ use std::sync::{Arc, Mutex}; +use crate::sysbus::{SysBusDevBase, SysBusDevOps}; +use crate::{Device, DeviceBase}; use acpi::{ AmlActiveLevel, AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlElse, @@ -25,16 +27,14 @@ use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlOne, AmlQWordDesc}; use address_space::{AddressSpace, GuestAddress, RegionOps}; -use anyhow::Context; -use sysbus::{SysBusDevBase, SysBusDevOps}; -use util::device::{Device, DeviceBase}; +use anyhow::{Context, Result}; #[cfg(target_arch = "aarch64")] -use crate::PCI_INTR_BASE; -use crate::{bus::PciBus, PciDevOps, PCI_PIN_NUM, PCI_SLOT_MAX}; +use crate::pci::PCI_INTR_BASE; +use crate::pci::{bus::PciBus, PciDevOps, PCI_PIN_NUM, PCI_SLOT_MAX}; #[cfg(target_arch = "x86_64")] -use crate::{le_read_u32, le_write_u32}; +use crate::pci::{le_read_u32, le_write_u32}; #[cfg(target_arch = "x86_64")] const CONFIG_ADDRESS_ENABLE_MASK: u32 = 0x8000_0000; @@ -280,11 +280,13 @@ impl SysBusDevOps for PciHost { } } - fn reset(&mut self) -> sysbus::Result<()> { + fn reset(&mut self) -> Result<()> { for (_id, pci_dev) in self.root_bus.lock().unwrap().devices.iter_mut() { - sysbus::Result::with_context(pci_dev.lock().unwrap().reset(true), || { - "Fail to reset pci device under pci host" - })?; + pci_dev + .lock() + .unwrap() + .reset(true) + .with_context(|| "Fail to reset pci device under pci host")?; } Ok(()) @@ -543,11 +545,11 @@ pub mod tests { use byteorder::{ByteOrder, LittleEndian}; use super::*; - use crate::bus::PciBus; - use crate::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; - use crate::root_port::RootPort; - use crate::{PciDevBase, Result}; - use util::device::{Device, DeviceBase}; + use crate::pci::bus::PciBus; + use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; + use crate::pci::root_port::RootPort; + use crate::pci::{PciDevBase, Result}; + use crate::{Device, DeviceBase}; struct PciDevice { base: PciDevBase, diff --git a/pci/src/hotplug.rs b/devices/src/pci/hotplug.rs similarity index 98% rename from pci/src/hotplug.rs rename to devices/src/pci/hotplug.rs index 87658f6bd..b35d52f8d 100644 --- a/pci/src/hotplug.rs +++ b/devices/src/pci/hotplug.rs @@ -12,7 +12,7 @@ use std::sync::{Arc, Mutex}; -use crate::{PciBus, PciDevOps}; +use crate::pci::{PciBus, PciDevOps}; use anyhow::{bail, Context, Result}; pub trait HotplugOps: Send { diff --git a/pci/src/intx.rs b/devices/src/pci/intx.rs similarity index 98% rename from pci/src/intx.rs rename to devices/src/pci/intx.rs index 20da36d96..0af40f690 100644 --- a/pci/src/intx.rs +++ b/devices/src/pci/intx.rs @@ -16,7 +16,7 @@ use anyhow::Result; use log::error; use util::test_helper::{is_test_enabled, trigger_intx}; -use crate::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_INTR_BASE, PCI_PIN_NUM}; +use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_INTR_BASE, PCI_PIN_NUM}; pub type InterruptHandler = Box Result<()> + Send + Sync>; diff --git a/pci/src/lib.rs b/devices/src/pci/mod.rs similarity index 98% rename from pci/src/lib.rs rename to devices/src/pci/mod.rs index 53a4fac6d..f35beb093 100644 --- a/pci/src/lib.rs +++ b/devices/src/pci/mod.rs @@ -23,16 +23,14 @@ pub mod demo_device; mod host; mod root_port; +use crate::{Device, DeviceBase}; pub use bus::PciBus; pub use config::{PciConfig, INTERRUPT_PIN}; pub use host::PciHost; pub use intx::{init_intx, InterruptHandler, PciIntxState}; pub use msix::{init_msix, is_msix_enabled}; pub use root_port::RootPort; -use util::{ - device::{Device, DeviceBase}, - AsAny, -}; +use util::AsAny; use std::{ mem::size_of, @@ -42,7 +40,7 @@ use std::{ pub use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; -use crate::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; const BDF_FUNC_SHIFT: u8 = 3; pub const PCI_SLOT_MAX: u8 = 32; @@ -368,8 +366,8 @@ pub fn ranges_overlap(start: usize, size: usize, range_start: usize, range_size: #[cfg(test)] mod tests { + use crate::DeviceBase; use address_space::{AddressSpace, Region}; - use util::device::DeviceBase; use super::*; diff --git a/pci/src/msix.rs b/devices/src/pci/msix.rs similarity index 99% rename from pci/src/msix.rs rename to devices/src/pci/msix.rs index 2935e8ffb..fd9f36532 100644 --- a/pci/src/msix.rs +++ b/devices/src/pci/msix.rs @@ -31,8 +31,10 @@ use util::{ test_helper::{add_msix_msg, is_test_enabled}, }; -use crate::config::{CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM}; -use crate::{ +use crate::pci::config::{ + CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM, +}; +use crate::pci::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, ranges_overlap, PciBus, }; @@ -710,7 +712,7 @@ pub fn update_dev_id(parent_bus: &Weak>, devfn: u8, dev_id: &Arc pci::Result<()> { + fn mem_region_init(&mut self) -> Result<()> { let cap_region = Region::init_io_region( XHCI_PCI_CAP_LENGTH as u64, build_cap_ops(&self.xhci), "XhciPciCapRegion", ); - pci::Result::with_context( - self.mem_region - .add_subregion(cap_region, XHCI_PCI_CAP_OFFSET as u64), - || "Failed to register cap region.", - )?; + self.mem_region + .add_subregion(cap_region, XHCI_PCI_CAP_OFFSET as u64) + .with_context(|| "Failed to register cap region.")?; let mut oper_region = Region::init_io_region( XHCI_PCI_OPER_LENGTH as u64, @@ -110,11 +108,9 @@ impl XhciPciDevice { "XhciPciOperRegion", ); oper_region.set_access_size(4); - pci::Result::with_context( - self.mem_region - .add_subregion(oper_region, XHCI_PCI_OPER_OFFSET as u64), - || "Failed to register oper region.", - )?; + self.mem_region + .add_subregion(oper_region, XHCI_PCI_OPER_OFFSET as u64) + .with_context(|| "Failed to register oper region.")?; let port_num = self.xhci.lock().unwrap().usb_ports.len(); for i in 0..port_num { @@ -125,9 +121,9 @@ impl XhciPciDevice { "XhciPciPortRegion", ); let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i as u32) as u64; - pci::Result::with_context(self.mem_region.add_subregion(port_region, offset), || { - "Failed to register port region." - })?; + self.mem_region + .add_subregion(port_region, offset) + .with_context(|| "Failed to register port region.")?; } let mut runtime_region = Region::init_io_region( @@ -136,22 +132,18 @@ impl XhciPciDevice { "XhciPciRuntimeRegion", ); runtime_region.set_access_size(4); - pci::Result::with_context( - self.mem_region - .add_subregion(runtime_region, XHCI_PCI_RUNTIME_OFFSET as u64), - || "Failed to register runtime region.", - )?; + self.mem_region + .add_subregion(runtime_region, XHCI_PCI_RUNTIME_OFFSET as u64) + .with_context(|| "Failed to register runtime region.")?; let doorbell_region = Region::init_io_region( XHCI_PCI_DOORBELL_LENGTH as u64, build_doorbell_ops(&self.xhci), "XhciPciDoorbellRegion", ); - pci::Result::with_context( - self.mem_region - .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64), - || "Failed to register doorbell region.", - )?; + self.mem_region + .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64) + .with_context(|| "Failed to register doorbell region.")?; Ok(()) } @@ -215,7 +207,7 @@ impl PciDevOps for XhciPciDevice { &mut self.base } - fn realize(mut self) -> pci::Result<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; le_write_u16( @@ -313,7 +305,7 @@ impl PciDevOps for XhciPciDevice { Ok(()) } - fn unrealize(&mut self) -> pci::Result<()> { + fn unrealize(&mut self) -> Result<()> { Ok(()) } @@ -332,7 +324,7 @@ impl PciDevOps for XhciPciDevice { ); } - fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { self.xhci.lock().unwrap().reset(); self.base.config.reset()?; diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 4c87bf794..542996efc 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -24,8 +24,6 @@ hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } -pci = { path = "../pci" } -sysbus = { path = "../sysbus" } util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1df88903b..fc495021e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -55,6 +55,8 @@ use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; +use devices::pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; +use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(not(target_env = "musl"))] use devices::usb::{ camera::UsbCamera, keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, @@ -78,12 +80,10 @@ use machine_manager::config::{ }; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; -use pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use standard_vm::Result as StdResult; pub use standard_vm::StdMachine; -use sysbus::{SysBus, SysBusDevOps, SysBusDevType}; use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 0961f3be0..9ffa72317 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -60,6 +60,9 @@ use devices::legacy::PL031; #[cfg(target_arch = "x86_64")] use devices::legacy::SERIAL_ADDR; use devices::legacy::{FwCfgOps, Serial}; +use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; +#[cfg(target_arch = "aarch64")] +use devices::sysbus::{SysBusDevType, SysRes}; #[cfg(target_arch = "aarch64")] use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; #[cfg(target_arch = "x86_64")] @@ -81,9 +84,6 @@ use machine_manager::{ }; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use migration::{MigrationManager, MigrationStatus}; -use sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; -#[cfg(target_arch = "aarch64")] -use sysbus::{SysBusDevType, SysRes}; use syscall::syscall_whitelist; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, CompileFDT, FdtBuilder}; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 779f7aa01..dfdffe4eb 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -54,6 +54,8 @@ use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; +use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; +use devices::sysbus::{SysBus, SysBusDevType, SysRes}; use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; #[cfg(not(target_env = "musl"))] @@ -69,9 +71,7 @@ use machine_manager::machine::{ }; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; -use pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use pci_host_root::PciHostRoot; -use sysbus::{SysBus, SysBusDevType, SysRes}; use syscall::syscall_whitelist; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index e7f0b6641..9b0b3ca46 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -12,14 +12,14 @@ use std::sync::{Arc, Mutex, Weak}; -use pci::{ +use devices::pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }, le_write_u16, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; -use util::device::{Device, DeviceBase}; +use devices::{Device, DeviceBase}; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; diff --git a/machine/src/standard_vm/error.rs b/machine/src/standard_vm/error.rs index 539cd984a..f0384c50a 100644 --- a/machine/src/standard_vm/error.rs +++ b/machine/src/standard_vm/error.rs @@ -33,7 +33,7 @@ pub enum StandardVmError { #[error("")] PciErr { #[from] - source: pci::error::PciError, + source: devices::pci::error::PciError, }, #[error("")] Acpi { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 7dbc17d0b..9a4b4ef35 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -60,6 +60,8 @@ use anyhow::{bail, Context}; use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; +use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; +use devices::pci::PciBus; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, memory_unit_conversion, BlkDevConfig, ChardevType, ConfigCheck, DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, @@ -68,8 +70,6 @@ use machine_manager::config::{ use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::MigrationManager; -use pci::hotplug::{handle_plug, handle_unplug_pci_request}; -use pci::PciBus; use util::byte_code::ByteCode; use virtio::{ qmp_balloon, qmp_query_balloon, Block, BlockState, diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 2151eea77..0b38d04f5 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -20,16 +20,16 @@ use crate::standard_vm::Result; use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use anyhow::Context; -use log::error; -use pci::config::{ +use devices::pci::config::{ PciConfig, CLASS_CODE_ISA_BRIDGE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; -use pci::{ +use devices::pci::{ le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; +use devices::{Device, DeviceBase}; +use log::error; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use vmm_sys_util::eventfd::EventFd; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index faf112c95..731eb912c 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -14,15 +14,15 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{Region, RegionOps}; use anyhow::{bail, Result}; -use log::error; -use pci::{ +use devices::pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }, le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; -use util::device::{Device, DeviceBase}; +use devices::{Device, DeviceBase}; +use log::error; use super::VENDOR_ID_INTEL; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 9a45a3465..ec470b063 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -35,6 +35,8 @@ use devices::legacy::{ error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, }; +use devices::pci::{PciDevOps, PciHost}; +use devices::sysbus::SysBus; use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; #[cfg(not(target_env = "musl"))] @@ -52,8 +54,6 @@ use machine_manager::machine::{ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; -use pci::{PciDevOps, PciHost}; -use sysbus::SysBus; use syscall::syscall_whitelist; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, diff --git a/pci/Cargo.toml b/pci/Cargo.toml deleted file mode 100644 index 9fada9c4b..000000000 --- a/pci/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "pci" -version = "2.2.0" -authors = ["Huawei StratoVirt Team"] -edition = "2021" -license = "Mulan PSL v2" -description = "PCI" - -[dependencies] -byteorder = "1.4.3" -thiserror = "1.0" -anyhow = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" -libc = "0.2" -log = "0.4" -vmm-sys-util = "0.11.1" -once_cell = "1.18.0" -address_space = { path = "../address_space" } -hypervisor = { path = "../hypervisor" } -machine_manager = { path = "../machine_manager" } -migration = { path = "../migration" } -migration_derive = { path = "../migration/migration_derive" } -sysbus = { path = "../sysbus" } -util = { path = "../util" } -acpi = { path = "../acpi" } - -[target.'cfg(not(target_env = "musl"))'.dependencies] -ui = { path = "../ui" } diff --git a/sysbus/Cargo.toml b/sysbus/Cargo.toml deleted file mode 100644 index 30ec46911..000000000 --- a/sysbus/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "sysbus" -version = "2.2.0" -authors = ["Huawei StratoVirt Team"] -edition = "2021" -license = "Mulan PSL v2" -description = "Emulate system bus" - -[dependencies] -thiserror = "1.0" -anyhow = "1.0" -kvm-ioctls = "0.13.0" -vmm-sys-util = "0.11.1" -acpi = { path = "../acpi" } -address_space = { path = "../address_space" } -hypervisor = { path = "../hypervisor" } -util = {path = "../util"} diff --git a/util/src/device.rs b/util/src/device.rs deleted file mode 100644 index e85c14c0b..000000000 --- a/util/src/device.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -#[derive(Clone, Default)] -pub struct DeviceBase { - /// Name of this device. - pub id: String, - /// Whether it supports hot-plug/hot-unplug. - pub hotpluggable: bool, -} - -impl DeviceBase { - pub fn new(id: String, hotpluggable: bool) -> Self { - DeviceBase { id, hotpluggable } - } -} - -pub trait Device { - fn device_base(&self) -> &DeviceBase; - - fn device_base_mut(&mut self) -> &mut DeviceBase; - - /// Get device name. - fn name(&self) -> String { - self.device_base().id.clone() - } - - /// Query whether it supports hot-plug/hot-unplug. - fn hotpluggable(&self) -> bool { - self.device_base().hotpluggable - } -} diff --git a/util/src/lib.rs b/util/src/lib.rs index cc8f71fd2..4d637a171 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -17,7 +17,6 @@ pub mod byte_code; pub mod checksum; pub mod clock; pub mod daemonize; -pub mod device; #[cfg(target_arch = "aarch64")] pub mod device_tree; pub mod edid; diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index d4c8a42e1..f98706e3f 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -20,4 +20,4 @@ once_cell = "1.18.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } util = { path = "../util" } -pci = { path = "../pci" } +devices = { path = "../devices" } diff --git a/vfio/src/error.rs b/vfio/src/error.rs index 489218c32..4373ec717 100644 --- a/vfio/src/error.rs +++ b/vfio/src/error.rs @@ -17,7 +17,7 @@ pub enum VfioError { #[error("PciErr")] PciErr { #[from] - source: pci::error::PciError, + source: devices::pci::error::PciError, }, #[error("AddressSpace")] AddressSpace { diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 3f7fa4abb..a466fa46b 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -20,26 +20,26 @@ use crate::VfioError; use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; #[cfg(target_arch = "aarch64")] -use pci::config::SECONDARY_BUS_NUM; -use pci::config::{ +use devices::pci::config::SECONDARY_BUS_NUM; +use devices::pci::config::{ PciConfig, RegionType, BAR_0, BAR_5, BAR_IO_SPACE, BAR_MEM_64BIT, BAR_SPACE_UNMAPPED, COMMAND, COMMAND_BUS_MASTER, COMMAND_INTERRUPT_DISABLE, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, HEADER_TYPE, IO_BASE_ADDR_MASK, MEM_BASE_ADDR_MASK, PCIE_CONFIG_SPACE_SIZE, PCI_CONFIG_SPACE_SIZE, REG_SIZE, }; -use pci::msix::{ +use devices::pci::msix::{ is_msix_enabled, update_dev_id, Msix, MSIX_CAP_CONTROL, MSIX_CAP_ENABLE, MSIX_CAP_FUNC_MASK, MSIX_CAP_ID, MSIX_CAP_SIZE, MSIX_CAP_TABLE, MSIX_TABLE_BIR, MSIX_TABLE_ENTRY_SIZE, MSIX_TABLE_OFFSET, MSIX_TABLE_SIZE_MAX, }; -use pci::{ +use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, pci_ext_cap_next, pci_ext_cap_ver, ranges_overlap, PciBus, PciDevBase, PciDevOps, }; -use util::device::{Device, DeviceBase}; +use devices::{Device, DeviceBase}; +use hypervisor::kvm::{MsiVector, KVM_FDS}; +use log::error; use util::unix::host_page_size; use vfio_bindings::bindings::vfio; use vmm_sys_util::eventfd::EventFd; @@ -832,20 +832,20 @@ impl PciDevOps for VfioPciDevice { &mut self.base } - fn realize(mut self) -> pci::Result<()> { + fn realize(mut self) -> devices::pci::Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; - pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { + devices::pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Failed to reset vfio device" })?; - pci::Result::with_context(self.get_pci_config(), || { + devices::pci::Result::with_context(self.get_pci_config(), || { "Failed to get vfio device pci config space" })?; - pci::Result::with_context(self.pci_config_reset(), || { + devices::pci::Result::with_context(self.pci_config_reset(), || { "Failed to reset vfio device pci config space" })?; - pci::Result::with_context( + devices::pci::Result::with_context( init_multifunction( self.multi_func, &mut self.base.config.config, @@ -868,14 +868,15 @@ impl PciDevOps for VfioPciDevice { self.dev_id = Arc::new(AtomicU16::new(self.set_dev_id(bus_num, self.base.devfn))); } - self.msix_info = Some(pci::Result::with_context(self.get_msix_info(), || { - "Failed to get MSI-X info" - })?); - self.vfio_bars = Arc::new(Mutex::new(pci::Result::with_context( + self.msix_info = Some(devices::pci::Result::with_context( + self.get_msix_info(), + || "Failed to get MSI-X info", + )?); + self.vfio_bars = Arc::new(Mutex::new(devices::pci::Result::with_context( self.bar_region_info(), || "Failed to get bar region info", )?)); - pci::Result::with_context(self.register_bars(), || "Failed to register bars")?; + devices::pci::Result::with_context(self.register_bars(), || "Failed to register bars")?; let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); @@ -895,7 +896,7 @@ impl PciDevOps for VfioPciDevice { Ok(()) } - fn unrealize(&mut self) -> pci::Result<()> { + fn unrealize(&mut self) -> devices::pci::Result<()> { if let Err(e) = VfioPciDevice::unrealize(self) { error!("{:?}", e); bail!("Failed to unrealize vfio-pci."); @@ -1007,8 +1008,8 @@ impl PciDevOps for VfioPciDevice { } } - fn reset(&mut self, _reset_child_device: bool) -> pci::Result<()> { - pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { + fn reset(&mut self, _reset_child_device: bool) -> devices::pci::Result<()> { + devices::pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Fail to reset vfio dev" }) } diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 70a40a25b..0dd5b0d36 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -19,9 +19,7 @@ hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } -sysbus = { path = "../sysbus" } util = { path = "../util" } -pci = { path = "../pci" } acpi = { path = "../acpi" } devices = {path = "../devices"} virtio = {path = "../virtio"} diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 6f86b7f6e..79545cc43 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -21,9 +21,7 @@ hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } -sysbus = { path = "../sysbus" } util = { path = "../util" } -pci = { path = "../pci" } acpi = { path = "../acpi" } devices = {path = "../devices"} block_backend = {path = "../block_backend"} diff --git a/virtio/src/error.rs b/virtio/src/error.rs index 7535c938e..b5e69caa4 100644 --- a/virtio/src/error.rs +++ b/virtio/src/error.rs @@ -32,7 +32,7 @@ pub enum VirtioError { #[error("SysBus")] SysBus { #[from] - source: sysbus::error::SysBusError, + source: devices::sysbus::error::SysBusError, }, #[error("Failed to create eventfd.")] EventFdCreate, diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index ec2e86cf2..52d1ea292 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -16,14 +16,14 @@ use std::sync::{Arc, Mutex}; use crate::error::VirtioError; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use byteorder::{ByteOrder, LittleEndian}; +use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use devices::{Device, DeviceBase}; use log::{error, warn}; #[cfg(target_arch = "x86_64")] use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; -use sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use vmm_sys_util::eventfd::EventFd; use crate::{ diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 33697049b..130862c01 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -18,22 +18,22 @@ use std::sync::{Arc, Mutex, Weak}; use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use anyhow::{anyhow, bail, Context}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, warn}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; -use pci::config::{ +use devices::pci::config::{ RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; +use log::{debug, error, warn}; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; -use pci::msix::{update_dev_id, MsixState}; -use pci::{ +use devices::pci::msix::{update_dev_id, MsixState}; +use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, }; +use devices::{Device, DeviceBase}; use util::byte_code::ByteCode; -use util::device::{Device, DeviceBase}; use util::num_ops::{read_data_u32, write_data_u32}; use util::offset_of; use vmm_sys_util::eventfd::EventFd; @@ -1283,7 +1283,7 @@ mod tests { use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress, HostMemMapping}; - use pci::{ + use devices::pci::{ config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC}, le_read_u16, }; -- Gitee From 0dbbbf718e0fb32a9b55a453f70083ad285690e9 Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Tue, 8 Aug 2023 06:44:25 +0800 Subject: [PATCH 1282/2187] fix some ui problem maximize,minimize button disappear in some uos pc, bold font of headerbar title. Signed-off-by: wangmeiling --- ui/src/gtk/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index e857255fe..8d0ca431a 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -32,10 +32,10 @@ use gtk::{ glib::{self, Priority, SyncSender}, prelude::{ApplicationExt, ApplicationExtManual, Continue, NotebookExtManual}, traits::{ - CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, HeaderBarExt, MenuShellExt, + CheckMenuItemExt, GtkMenuItemExt, GtkWindowExt, HeaderBarExt, LabelExt, MenuShellExt, RadioMenuItemExt, WidgetExt, }, - Application, ApplicationWindow, DrawingArea, HeaderBar, RadioMenuItem, + Application, ApplicationWindow, DrawingArea, HeaderBar, Label, RadioMenuItem, }; use log::{debug, error}; @@ -553,7 +553,13 @@ fn set_program_attribute(gtk_cfg: &GtkConfig, window: &ApplicationWindow) -> Res // Set title bar. let header = HeaderBar::new(); header.set_show_close_button(true); - header.set_title(Some(>k_cfg.vm_name)); + header.set_decoration_layout(Some("menu:minimize,maximize,close")); + + let label: Label = Label::new(Some(>k_cfg.vm_name)); + label.set_markup( + &("".to_string() + >k_cfg.vm_name + ""), + ); + header.set_custom_title(Some(&label)); window.set_titlebar(Some(&header)); // Set default icon. -- Gitee From c74f5dece86fbdaabadbafcca432ba9329312812 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 8 Aug 2023 03:50:42 +0800 Subject: [PATCH 1283/2187] format: Modify package reference location Package reference location: extern crate thirdparty; pub mod ...; mod ...; pub use std::...; pub use thirdparty::...; pub use internal::...; use std::...; use thirdparty::...; use internal::...; Modify format only. No functional change. Signed-off-by: liuxiangdong --- acpi/src/acpi_device.rs | 2 +- acpi/src/acpi_table.rs | 3 +- acpi/src/lib.rs | 3 +- acpi/src/table_loader.rs | 4 +- address_space/src/address_space.rs | 12 ++-- address_space/src/host_mmap.rs | 8 ++- address_space/src/lib.rs | 6 +- address_space/src/listener.rs | 8 +-- address_space/src/region.rs | 6 +- address_space/src/state.rs | 3 +- block_backend/src/qcow2/cache.rs | 3 +- block_backend/src/qcow2/mod.rs | 3 +- block_backend/src/qcow2/refcount.rs | 2 +- boot_loader/src/aarch64/mod.rs | 5 +- boot_loader/src/x86_64/bootparam.rs | 9 ++- boot_loader/src/x86_64/direct_boot/gdt.rs | 9 ++- boot_loader/src/x86_64/direct_boot/mod.rs | 10 +-- boot_loader/src/x86_64/direct_boot/mptable.rs | 3 +- boot_loader/src/x86_64/standard_boot/mod.rs | 8 +-- cpu/src/aarch64/caps.rs | 2 +- cpu/src/aarch64/core_regs.rs | 3 +- cpu/src/aarch64/mod.rs | 6 +- cpu/src/lib.rs | 24 ++++--- cpu/src/x86_64/mod.rs | 7 ++- devices/src/acpi/ged.rs | 35 +++++------ devices/src/acpi/power.rs | 13 ++-- devices/src/camera_backend/demo.rs | 3 +- devices/src/camera_backend/mod.rs | 6 +- devices/src/camera_backend/v4l2.rs | 10 ++- .../src/interrupt_controller/aarch64/gicv2.rs | 12 ++-- .../src/interrupt_controller/aarch64/gicv3.rs | 13 ++-- .../src/interrupt_controller/aarch64/mod.rs | 6 +- .../src/interrupt_controller/aarch64/state.rs | 10 +-- devices/src/interrupt_controller/mod.rs | 3 +- devices/src/legacy/chardev.rs | 3 +- devices/src/legacy/fwcfg.rs | 12 ++-- devices/src/legacy/mod.rs | 7 ++- devices/src/legacy/pflash.rs | 10 +-- devices/src/legacy/pl011.rs | 7 ++- devices/src/legacy/pl031.rs | 9 +-- devices/src/legacy/rtc.rs | 11 ++-- devices/src/legacy/serial.rs | 11 ++-- devices/src/lib.rs | 3 +- devices/src/misc/mod.rs | 5 +- devices/src/misc/scream/mod.rs | 2 +- devices/src/pci/bus.rs | 9 ++- devices/src/pci/config.rs | 7 +-- devices/src/pci/demo_dev.rs | 7 +-- devices/src/pci/demo_device/base_device.rs | 10 +-- devices/src/pci/demo_device/dpy_device.rs | 14 +++-- devices/src/pci/demo_device/gpu_device.rs | 13 ++-- .../src/pci/demo_device/kbd_pointer_device.rs | 9 +-- devices/src/pci/host.rs | 17 +++-- devices/src/pci/hotplug.rs | 3 +- devices/src/pci/intx.rs | 2 +- devices/src/pci/mod.rs | 13 ++-- devices/src/pci/msix.rs | 15 +++-- devices/src/pci/root_port.rs | 16 ++--- devices/src/scsi/disk.rs | 2 +- devices/src/sysbus/mod.rs | 5 +- devices/src/usb/camera.rs | 18 +++--- devices/src/usb/camera_media_type_guid.rs | 3 +- devices/src/usb/descriptor.rs | 3 +- devices/src/usb/hid.rs | 3 +- devices/src/usb/mod.rs | 19 +++--- devices/src/usb/storage.rs | 3 +- devices/src/usb/usbhost/mod.rs | 4 +- devices/src/usb/xhci/mod.rs | 1 + devices/src/usb/xhci/xhci_controller.rs | 7 +-- devices/src/usb/xhci/xhci_pci.rs | 15 +++-- devices/src/usb/xhci/xhci_regs.rs | 8 +-- devices/src/usb/xhci/xhci_ring.rs | 3 +- hypervisor/src/kvm/mod.rs | 9 +-- hypervisor/src/lib.rs | 4 +- machine/src/lib.rs | 35 ++++++----- machine/src/micro_vm/mod.rs | 29 ++++----- machine/src/standard_vm/aarch64/mod.rs | 29 +++++---- machine/src/standard_vm/mod.rs | 46 +++++++------- machine/src/standard_vm/x86_64/ich9_lpc.rs | 7 ++- machine/src/standard_vm/x86_64/mch.rs | 8 +-- machine/src/standard_vm/x86_64/mod.rs | 23 +++---- machine_manager/src/cmdline.rs | 6 +- machine_manager/src/config/balloon.rs | 3 +- machine_manager/src/config/boot_source.rs | 8 ++- machine_manager/src/config/devices.rs | 3 +- machine_manager/src/config/drive.rs | 4 +- machine_manager/src/config/fs.rs | 3 +- machine_manager/src/config/gpu.rs | 5 +- machine_manager/src/config/iothread.rs | 2 +- machine_manager/src/config/mod.rs | 62 ++++++++++--------- machine_manager/src/config/network.rs | 3 +- machine_manager/src/config/ramfb.rs | 3 +- machine_manager/src/config/rng.rs | 3 +- machine_manager/src/config/sasl_auth.rs | 5 +- machine_manager/src/config/smbios.rs | 5 +- machine_manager/src/config/tls_creds.rs | 11 ++-- machine_manager/src/config/vfio.rs | 4 +- machine_manager/src/config/vnc.rs | 5 +- machine_manager/src/event_loop.rs | 6 +- machine_manager/src/lib.rs | 3 +- machine_manager/src/qmp/mod.rs | 17 ++--- machine_manager/src/qmp/qmp_schema.rs | 3 +- machine_manager/src/signal_handler.rs | 2 +- machine_manager/src/test_server.rs | 12 ++-- migration/migration_derive/src/attr_parser.rs | 3 +- migration/migration_derive/src/lib.rs | 8 +-- migration/src/error.rs | 3 +- migration/src/general.rs | 3 +- migration/src/lib.rs | 10 +-- migration/src/manager.rs | 2 +- migration/src/migration.rs | 2 +- migration/src/protocol.rs | 3 +- migration/src/snapshot.rs | 12 ++-- ozone/src/cgroup.rs | 3 +- ozone/src/handler.rs | 10 +-- ozone/src/main.rs | 12 ++-- ozone/src/namespace.rs | 3 +- smbios/src/smbios_table.rs | 3 +- src/main.rs | 4 +- tests/mod_test/src/libdriver/fwcfg.rs | 3 +- tests/mod_test/src/libdriver/machine.rs | 5 +- tests/mod_test/src/libdriver/pci.rs | 5 +- tests/mod_test/src/libdriver/pci_bus.rs | 5 +- tests/mod_test/src/libdriver/usb.rs | 3 +- tests/mod_test/src/libdriver/virtio.rs | 5 +- tests/mod_test/src/libdriver/virtio_block.rs | 4 +- tests/mod_test/src/libdriver/virtio_gpu.rs | 4 +- .../src/libdriver/virtio_pci_modern.rs | 7 ++- tests/mod_test/src/libdriver/virtio_rng.rs | 6 +- tests/mod_test/src/libdriver/vnc.rs | 13 ++-- tests/mod_test/src/utils.rs | 5 +- tests/mod_test/tests/acpi_test.rs | 9 +-- tests/mod_test/tests/balloon_test.rs | 14 +++-- tests/mod_test/tests/block_test.rs | 17 +++-- tests/mod_test/tests/fwcfg_test.rs | 11 ++-- tests/mod_test/tests/ged_test.rs | 2 +- tests/mod_test/tests/memory_test.rs | 6 +- tests/mod_test/tests/net_test.rs | 9 +-- tests/mod_test/tests/pci_test.rs | 11 ++-- tests/mod_test/tests/pl031_test.rs | 8 ++- tests/mod_test/tests/ramfb_test.rs | 12 ++-- tests/mod_test/tests/rng_test.rs | 14 ++--- tests/mod_test/tests/scsi_test.rs | 6 +- tests/mod_test/tests/usb_camera_test.rs | 2 +- tests/mod_test/tests/usb_storage_test.rs | 1 - tests/mod_test/tests/virtio_gpu_test.rs | 5 +- tests/mod_test/tests/virtio_test.rs | 7 ++- tests/mod_test/tests/virtiofs_test.rs | 6 +- tests/mod_test/tests/vnc_test.rs | 8 ++- ui/src/console.rs | 5 +- ui/src/gtk/menu.rs | 3 +- ui/src/gtk/mod.rs | 2 +- ui/src/input.rs | 9 +-- ui/src/lib.rs | 3 +- ui/src/vnc/auth_sasl.rs | 14 +++-- ui/src/vnc/auth_vencrypt.rs | 35 ++++++----- ui/src/vnc/client_io.rs | 30 ++++----- ui/src/vnc/encoding/enc_hextile.rs | 3 +- ui/src/vnc/encoding/mod.rs | 1 + ui/src/vnc/mod.rs | 24 +++---- ui/src/vnc/server_io.rs | 28 +++++---- util/src/aio/libaio.rs | 3 +- util/src/aio/mod.rs | 11 ++-- util/src/aio/raw.rs | 6 +- util/src/aio/uring.rs | 2 +- util/src/arg_parser.rs | 6 +- util/src/bitmap.rs | 3 +- util/src/daemonize.rs | 3 +- util/src/device_tree.rs | 3 +- util/src/leak_bucket.rs | 2 +- util/src/lib.rs | 6 +- util/src/loop_context.rs | 10 +-- util/src/seccomp.rs | 3 +- util/src/syscall.rs | 4 +- util/src/test_helper.rs | 3 +- util/src/trace.rs | 3 +- util/src/unix.rs | 3 +- vfio/src/lib.rs | 5 +- vfio/src/vfio_dev.rs | 6 +- vfio/src/vfio_pci.rs | 18 +++--- vhost_user_fs/src/vhost_user_fs.rs | 2 +- vhost_user_fs/src/virtio_fs.rs | 2 +- virtio/src/device/balloon.rs | 18 +++--- virtio/src/device/block.rs | 8 ++- virtio/src/device/gpu.rs | 29 +++++---- virtio/src/device/rng.rs | 31 +++++----- virtio/src/lib.rs | 35 ++++++----- virtio/src/queue/mod.rs | 8 ++- virtio/src/queue/split.rs | 4 +- virtio/src/transport/virtio_mmio.rs | 23 ++++--- virtio/src/transport/virtio_pci.rs | 44 +++++++------ virtio/src/vhost/kernel/mod.rs | 16 ++--- virtio/src/vhost/kernel/net.rs | 15 ++--- virtio/src/vhost/kernel/vsock.rs | 15 +++-- virtio/src/vhost/mod.rs | 2 +- virtio/src/vhost/user/block.rs | 8 +-- virtio/src/vhost/user/client.rs | 19 +++--- virtio/src/vhost/user/fs.rs | 9 ++- virtio/src/vhost/user/message.rs | 5 +- virtio/src/vhost/user/mod.rs | 10 +-- virtio/src/vhost/user/net.rs | 12 ++-- virtio/src/vhost/user/sock.rs | 5 +- 202 files changed, 950 insertions(+), 858 deletions(-) diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index 966e6472e..aff03b84d 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -12,9 +12,9 @@ use std::time::Instant; -use address_space::GuestAddress; use log::error; +use address_space::GuestAddress; use util::{ num_ops::{read_data_u16, write_data_u16}, time::NANOSECONDS_PER_SECOND, diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index d91ee3119..e8e22910b 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -10,9 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use util::byte_code::ByteCode; - use super::aml_compiler::AmlBuilder; +use util::byte_code::ByteCode; /// Offset of checksum field in ACPI table. pub const TABLE_CHECKSUM_OFFSET: u32 = 9; diff --git a/acpi/src/lib.rs b/acpi/src/lib.rs index 7894ac054..d5a81153d 100644 --- a/acpi/src/lib.rs +++ b/acpi/src/lib.rs @@ -10,10 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod acpi_device; pub mod acpi_table; pub(crate) mod aml_compiler; pub mod error; + +mod acpi_device; mod table_loader; pub use acpi_device::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index eb5f5e18e..d85eac0d4 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -12,11 +12,11 @@ use std::sync::{Arc, Mutex}; -use util::byte_code::ByteCode; +use anyhow::{anyhow, bail, Context, Result}; use crate::AcpiError; use crate::AmlBuilder; -use anyhow::{anyhow, bail, Context, Result}; +use util::byte_code::ByteCode; const TABLE_LOADER_FILE_NAME_SZ: usize = 56; const TABLE_LOADER_ENTRY_SZ: usize = 124; diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 49e8117a5..bece34c0f 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -10,22 +10,22 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, Context, Result}; -use arc_swap::ArcSwap; use std::fmt; use std::fmt::Debug; use std::io::Write; use std::sync::{Arc, Mutex}; -use migration::{migration::Migratable, MigrationManager}; -use util::aio::Iovec; -use util::byte_code::ByteCode; -use util::test_helper::is_test_enabled; +use anyhow::{anyhow, Context, Result}; +use arc_swap::ArcSwap; use crate::{ AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, RegionIoEventFd, RegionType, }; +use migration::{migration::Migratable, MigrationManager}; +use util::aio::Iovec; +use util::byte_code::ByteCode; +use util::test_helper::is_test_enabled; /// Contains an array of `FlatRange`. #[derive(Default, Clone, Debug)] diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 00fde9d59..e2e5e7156 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -18,14 +18,14 @@ use std::thread; use anyhow::{bail, Context, Result}; use log::{error, info}; + +use crate::{AddressRange, GuestAddress, Region}; use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; use util::{ syscall::mbind, unix::{do_mmap, host_page_size}, }; -use crate::{AddressRange, GuestAddress, Region}; - const MAX_PREALLOC_THREAD: u8 = 16; /// Verify existing pages in the mapping. const MPOL_MF_STRICT: u32 = 1; @@ -484,10 +484,12 @@ impl Drop for HostMemMapping { #[cfg(test)] mod test { - use super::*; use std::io::{Read, Seek, SeekFrom, Write}; + use vmm_sys_util::tempfile::TempFile; + use super::*; + fn identify(ram: HostMemMapping, st: u64, end: u64) { assert_eq!(ram.start_address(), GuestAddress(st)); assert_eq!(ram.size(), end - st); diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 233a037af..8a81202ca 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -77,17 +77,19 @@ //! } //! ``` +pub mod error; + mod address; mod address_space; -pub mod error; mod host_mmap; mod listener; mod region; mod state; +pub use anyhow::Result; + pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; -pub use anyhow::Result; pub use error::AddressSpaceError; pub use host_mmap::{create_backend_mem, create_default_mem, FileBackend, HostMemMapping}; #[cfg(target_arch = "x86_64")] diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index cc01a278e..01bb9390e 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -13,14 +13,14 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; -use hypervisor::kvm::KVM_FDS; +use anyhow::{anyhow, bail, Context, Result}; use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_READONLY}; use kvm_ioctls::{IoEventAddress, NoDatamatch}; use log::{debug, warn}; -use util::{num_ops::round_down, unix::host_page_size}; use crate::{AddressRange, AddressSpaceError, FlatRange, RegionIoEventFd, RegionType}; -use anyhow::{anyhow, bail, Context, Result}; +use hypervisor::kvm::KVM_FDS; +use util::{num_ops::round_down, unix::host_page_size}; /// Request type of listener. #[derive(Debug, Copy, Clone)] @@ -615,12 +615,12 @@ impl Listener for KvmIoListener { #[cfg(test)] mod test { - use hypervisor::kvm::KVMFds; use libc::EFD_NONBLOCK; use vmm_sys_util::eventfd::EventFd; use super::*; use crate::{GuestAddress, HostMemMapping, Region, RegionIoEventFd}; + use hypervisor::kvm::KVMFds; fn generate_region_ioeventfd>(addr: u64, datamatch: T) -> RegionIoEventFd { let data = datamatch.into(); diff --git a/address_space/src/region.rs b/address_space/src/region.rs index be32ede70..75058d932 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -10,21 +10,21 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Context, Result}; -use log::{debug, warn}; use std::fmt; use std::fmt::Debug; use std::os::unix::io::AsRawFd; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock, Weak}; -use migration::{migration::Migratable, MigrationManager}; +use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, warn}; use crate::address_space::FlatView; use crate::{ AddressRange, AddressSpace, AddressSpaceError, FileBackend, GuestAddress, HostMemMapping, RegionOps, }; +use migration::{migration::Migratable, MigrationManager}; /// Types of Region. #[allow(clippy::upper_case_acronyms)] diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 71099a83c..1073053c3 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -17,6 +17,7 @@ use std::sync::Arc; use anyhow::{Context, Result}; +use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; use migration::{ error::MigrationError, DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, }; @@ -24,8 +25,6 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::unix::host_page_size; -use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; - const MIGRATION_HEADER_LENGTH: usize = 4096; #[repr(C)] diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 097bb02a4..8cefe1ae5 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -224,9 +224,10 @@ impl Qcow2Cache { #[cfg(test)] mod test { - use super::{CacheTable, Qcow2Cache, ENTRY_SIZE_U64}; use std::{cell::RefCell, rc::Rc}; + use super::{CacheTable, Qcow2Cache, ENTRY_SIZE_U64}; + #[test] fn test_cache_entry() { let buf: Vec = vec![0x00, 0x01, 0x02, 0x03, 0x04]; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index bb7e50a30..ea0b2d0ac 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1564,14 +1564,13 @@ mod test { process::Command, }; + use super::*; use machine_manager::config::DiskFormat; use util::{ aio::{iov_to_buf_direct, Iovec, WriteZeroesState}, file::get_file_alignment, }; - use super::*; - const CLUSTER_SIZE: u64 = 64 * 1024; pub struct TestImage { diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 18e26feae..a34a580fe 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -14,7 +14,6 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::{bail, Context, Result}; use log::{error, info}; -use util::aio::OpCode; use crate::{ qcow2::{ @@ -25,6 +24,7 @@ use crate::{ }, BlockProperty, }; +use util::aio::OpCode; // The max refcount table size default is 4 clusters; const MAX_REFTABLE_NUM: u64 = 4; diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index 76010edde..20d564c7d 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -15,11 +15,12 @@ use std::io::Read; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, Context, Result}; +use log::info; + use crate::error::BootLoaderError; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, Context, Result}; use devices::legacy::{error::LegacyError as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; -use log::info; use util::byte_code::ByteCode; const AARCH64_KERNEL_OFFSET: u64 = 0x8_0000; diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs index 5d944b62d..fe7852adc 100644 --- a/boot_loader/src/x86_64/bootparam.rs +++ b/boot_loader/src/x86_64/bootparam.rs @@ -12,15 +12,15 @@ use std::sync::Arc; -use address_space::AddressSpace; -use util::byte_code::ByteCode; +use anyhow::{anyhow, Result}; use super::{ X86BootLoaderConfig, EBDA_START, MB_BIOS_BEGIN, REAL_MODE_IVT_BEGIN, VGA_RAM_BEGIN, VMLINUX_RAM_START, }; use crate::error::BootLoaderError; -use anyhow::{anyhow, Result}; +use address_space::AddressSpace; +use util::byte_code::ByteCode; pub const E820_RAM: u32 = 1; pub const E820_RESERVED: u32 = 2; @@ -226,10 +226,9 @@ mod test { use std::path::PathBuf; use std::sync::Arc; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use super::super::X86BootLoaderConfig; use super::*; + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; #[test] fn test_boot_param() { diff --git a/boot_loader/src/x86_64/direct_boot/gdt.rs b/boot_loader/src/x86_64/direct_boot/gdt.rs index c1ea257ed..62c70be68 100644 --- a/boot_loader/src/x86_64/direct_boot/gdt.rs +++ b/boot_loader/src/x86_64/direct_boot/gdt.rs @@ -12,13 +12,15 @@ use std::sync::Arc; +use anyhow::{Context, Result}; +use kvm_bindings::kvm_segment; + use super::super::BootGdtSegment; use super::super::{ BOOT_GDT_MAX, BOOT_GDT_OFFSET, BOOT_IDT_OFFSET, GDT_ENTRY_BOOT_CS, GDT_ENTRY_BOOT_DS, }; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{Context, Result}; -use kvm_bindings::kvm_segment; + // /* // * Constructor for a conventional segment GDT (or LDT) entry. // * This is a macro so it can be used in initializers. @@ -136,9 +138,10 @@ pub fn setup_gdt(guest_mem: &Arc) -> Result { #[cfg(test)] mod test { - use super::*; use kvm_bindings::kvm_segment; + use super::*; + #[test] fn test_gdt_entry() { assert_eq!(GdtEntry::new(0xa09b, 0x0, 0xfffff).0, 0xaf9b000000ffff); diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 3510fbbe7..e76058005 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -20,9 +20,6 @@ use std::sync::Arc; use anyhow::{Context, Result}; use log::info; -use address_space::{AddressSpace, GuestAddress}; -use util::byte_code::ByteCode; - use self::gdt::setup_gdt; use self::mptable::setup_isa_mptable; use super::bootparam::{BootParams, RealModeKernelHeader, UNDEFINED_ID}; @@ -32,6 +29,8 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::error::BootLoaderError; +use address_space::{AddressSpace, GuestAddress}; +use util::byte_code::ByteCode; /// Load bzImage linux kernel to Guest Memory. /// @@ -276,13 +275,14 @@ pub fn load_linux( #[cfg(test)] mod test { - use super::*; use std::path::PathBuf; use std::sync::Arc; + use kvm_bindings::kvm_segment; + use super::super::BOOT_GDT_MAX; + use super::*; use address_space::*; - use kvm_bindings::kvm_segment; #[test] fn test_x86_bootloader_and_kernel_cmdline() { diff --git a/boot_loader/src/x86_64/direct_boot/mptable.rs b/boot_loader/src/x86_64/direct_boot/mptable.rs index 6ec4d8764..8eee6d1c1 100644 --- a/boot_loader/src/x86_64/direct_boot/mptable.rs +++ b/boot_loader/src/x86_64/direct_boot/mptable.rs @@ -12,9 +12,10 @@ use std::sync::Arc; +use anyhow::{anyhow, Result}; + use crate::error::BootLoaderError; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, Result}; use util::byte_code::ByteCode; use util::checksum::obj_checksum; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 7944b9e12..0c17349ce 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -17,10 +17,8 @@ use std::fs::File; use std::io::{Read, Seek, SeekFrom}; use std::sync::Arc; -use address_space::AddressSpace; -use devices::legacy::{FwCfgEntryType, FwCfgOps}; +use anyhow::{bail, Context, Result}; use log::{error, info}; -use util::byte_code::ByteCode; use self::elf::load_elf_kernel; use super::bootparam::RealModeKernelHeader; @@ -29,7 +27,9 @@ use super::{BOOT_HDR_START, CMDLINE_START}; use crate::error::BootLoaderError; use crate::x86_64::bootparam::{E820Entry, E820_RAM, E820_RESERVED, UEFI_OVMF_ID}; use crate::x86_64::{INITRD_ADDR_MAX, SETUP_START}; -use anyhow::{bail, Context, Result}; +use address_space::AddressSpace; +use devices::legacy::{FwCfgEntryType, FwCfgOps}; +use util::byte_code::ByteCode; fn load_image( image: &mut File, diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index df46d07f0..14a92e811 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -15,9 +15,9 @@ use kvm_bindings::{ KVM_REG_SIZE_U64, }; use kvm_ioctls::{Cap, Kvm, VcpuFd}; -use machine_manager::config::{CpuConfig, PmuConfig}; use super::core_regs::Result; +use machine_manager::config::{CpuConfig, PmuConfig}; // Capabilities for ARM cpu. #[derive(Debug, Clone)] diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index eb53ddb4d..6deaf788a 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -17,9 +17,10 @@ use kvm_bindings::{ KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64, }; use kvm_ioctls::VcpuFd; -use util::offset_of; use vmm_sys_util::errno; +use util::offset_of; + pub type Result = std::result::Result; const KVM_NR_REGS: u64 = 31; diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index e7ab9618c..37dbc9565 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -13,6 +13,8 @@ pub mod caps; mod core_regs; +pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; + use std::{ mem::forget, os::unix::prelude::{AsRawFd, FromRawFd}, @@ -20,7 +22,6 @@ use std::{ }; use anyhow::{Context, Result}; -use hypervisor::kvm::KVM_FDS; use kvm_bindings::{ kvm_device_attr, kvm_mp_state, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, RegList, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, KVM_ARM_VCPU_PMU_V3_IRQ, @@ -29,10 +30,9 @@ use kvm_bindings::{ use kvm_ioctls::{DeviceFd, VcpuFd}; use self::caps::CpregListEntry; -pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; use self::core_regs::{get_core_regs, set_core_regs}; use crate::CPU; - +use hypervisor::kvm::KVM_FDS; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index c90d8c4e6..5b3abe211 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -27,6 +27,8 @@ //! - `x86_64` //! - `aarch64` +pub mod error; + #[cfg(target_arch = "aarch64")] #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] @@ -34,10 +36,6 @@ mod aarch64; #[cfg(target_arch = "x86_64")] mod x86_64; -pub mod error; -use anyhow::{anyhow, Context, Result}; -pub use error::CpuError; - #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUBootConfig as CPUBootConfig; #[cfg(target_arch = "aarch64")] @@ -52,9 +50,7 @@ pub use aarch64::ArmCPUTopology as CPUTopology; pub use aarch64::PMU_INTR; #[cfg(target_arch = "aarch64")] pub use aarch64::PPI_BASE; -use machine_manager::qmp::qmp_schema; -#[cfg(target_arch = "x86_64")] -use x86_64::caps::X86CPUCaps as CPUCaps; +pub use error::CpuError; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] @@ -68,17 +64,20 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; use std::time::Duration; +use anyhow::{anyhow, Context, Result}; use kvm_ioctls::{VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info, warn}; +use vmm_sys_util::signal::{register_signal_handler, Killable}; + use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; use machine_manager::machine::MachineInterface; -use machine_manager::{qmp::qmp_schema as schema, qmp::QmpChannel}; - +use machine_manager::{qmp::qmp_schema, qmp::QmpChannel}; #[cfg(not(test))] use util::test_helper::is_test_enabled; -use vmm_sys_util::signal::{register_signal_handler, Killable}; +#[cfg(target_arch = "x86_64")] +use x86_64::caps::X86CPUCaps as CPUCaps; // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal // number should be assigned to SIGRTMIN + n, (n = 0...30). @@ -458,7 +457,7 @@ impl CPUInterface for CPU { } if QmpChannel::is_connected() { - let shutdown_msg = schema::Shutdown { + let shutdown_msg = qmp_schema::Shutdown { guest: true, reason: "guest-shutdown".to_string(), }; @@ -862,13 +861,12 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::Duration; + use super::*; use hypervisor::kvm::{KVMFds, KVM_FDS}; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineInterface, MachineLifecycle, }; - use super::*; - struct TestVm { #[cfg(target_arch = "x86_64")] pio_in: Arc)>>>, diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 89b879f0a..12072a42a 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod caps; + mod cpuid; use std::sync::{Arc, Mutex}; @@ -23,15 +24,15 @@ use kvm_bindings::{ KVM_MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; + +use self::cpuid::host_cpuid; +use crate::CPU; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use self::cpuid::host_cpuid; -use crate::CPU; - const ECX_EPB_SHIFT: u32 = 3; const X86_FEATURE_HYPERVISOR: u32 = 31; const X86_FEATURE_TSC_DEADLINE_TIMER: u32 = 24; diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 73aae0aca..76527b844 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -10,32 +10,31 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::os::unix::prelude::AsRawFd; +use std::rc::Rc; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::{Arc, Mutex}; + +use anyhow::{Context, Result}; +use log::error; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; + use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; use crate::{Device, DeviceBase}; -use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; +use acpi::{ + AcpiError, AmlActiveLevel, AmlAddressSpaceType, AmlAnd, AmlBuilder, AmlDevice, AmlEdgeLevel, + AmlEqual, AmlExtendedInterrupt, AmlField, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUnit, + AmlFieldUpdateRule, AmlIf, AmlIntShare, AmlInteger, AmlLocal, AmlMethod, AmlName, AmlNameDecl, + AmlNotify, AmlOpRegion, AmlResTemplate, AmlResourceUsage, AmlScopeBuilder, AmlStore, AmlString, + INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, +}; use address_space::GuestAddress; -use anyhow::{Context, Result}; -use log::error; use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::QmpChannel; -use std::os::unix::prelude::AsRawFd; -use std::rc::Rc; -use std::sync::atomic::{AtomicU32, Ordering}; use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; -use vmm_sys_util::epoll::EventSet; - -use acpi::{ - AmlActiveLevel, AmlAddressSpaceType, AmlAnd, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlEqual, - AmlExtendedInterrupt, AmlField, AmlFieldUnit, AmlIf, AmlIntShare, AmlInteger, AmlLocal, - AmlMethod, AmlName, AmlNameDecl, AmlNotify, AmlOpRegion, AmlResTemplate, AmlResourceUsage, - AmlScopeBuilder, AmlStore, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, -}; - -use std::sync::{Arc, Mutex}; - -use vmm_sys_util::eventfd::EventFd; #[derive(Clone, Copy)] pub enum AcpiEvent { diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 152c35486..90bdc51fa 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -14,24 +14,23 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use std::time::Duration; -use crate::{Device, DeviceBase}; use anyhow::{Context, Result}; use log::info; -use util::byte_code::ByteCode; -use util::num_ops::write_data_u32; use crate::acpi::ged::{AcpiEvent, Ged}; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; -use acpi::{AcpiError, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUpdateRule}; +use crate::{Device, DeviceBase}; use acpi::{ - AmlAddressSpaceType, AmlBuilder, AmlDevice, AmlField, AmlFieldUnit, AmlIndex, AmlInteger, - AmlMethod, AmlName, AmlNameDecl, AmlOpRegion, AmlPackage, AmlReturn, AmlScopeBuilder, AmlStore, - AmlString, AmlZero, + AcpiError, AmlAddressSpaceType, AmlBuilder, AmlDevice, AmlField, AmlFieldAccessType, + AmlFieldLockRule, AmlFieldUnit, AmlFieldUpdateRule, AmlIndex, AmlInteger, AmlMethod, AmlName, + AmlNameDecl, AmlOpRegion, AmlPackage, AmlReturn, AmlScopeBuilder, AmlStore, AmlString, AmlZero, }; use address_space::GuestAddress; use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; +use util::num_ops::write_data_u32; const AML_ACAD_REG: &str = "ADPM"; const AML_ACAD_ONLINE: &str = "ADPO"; diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 073773c98..c1ff73c62 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -23,13 +23,12 @@ use log::{debug, error, info}; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; -use util::aio::{mem_from_buf, Iovec}; - use super::INTERVALS_PER_SEC; use crate::camera_backend::{ CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, }; +use util::aio::{mem_from_buf, Iovec}; #[derive(Debug)] enum RgbColor { diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index a65cf4f3d..ebdce1272 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -16,13 +16,13 @@ pub mod demo; pub mod v4l2; -use anyhow::{bail, Context, Result}; use std::sync::{Arc, Mutex}; -use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; -use util::aio::Iovec; +use anyhow::{bail, Context, Result}; use self::{demo::DemoCamera, v4l2::V4l2CameraBackend}; +use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; +use util::aio::Iovec; /// Frame interval in 100ns units. pub const INTERVALS_PER_SEC: u32 = 10_000_000; diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 1e9d41125..74d29fb7e 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -26,18 +26,16 @@ use v4l2_sys_mit::{ }; use vmm_sys_util::epoll::EventSet; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use util::aio::Iovec; -use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; - +use super::{PIXFMT_MJPG, PIXFMT_RGB565, PIXFMT_YUYV}; use crate::camera_backend::{ CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, INTERVALS_PER_SEC, }; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::aio::Iovec; +use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; use util::v4l2::{new_init, V4l2Backend}; -use super::{PIXFMT_MJPG, PIXFMT_RGB565, PIXFMT_YUYV}; - const BUFFER_CNT: usize = 4; #[derive(Default)] diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index 5f3b586ba..4727d97e5 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -13,15 +13,17 @@ use std::marker::{Send, Sync}; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, Context, Result}; +use kvm_ioctls::DeviceFd; +use log::error; + use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; use crate::interrupt_controller::InterruptError; use address_space::AddressSpace; -use anyhow::{anyhow, Context, Result}; use hypervisor::kvm::KVM_FDS; -use kvm_ioctls::DeviceFd; -use log::error; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use util::device_tree::{self, FdtBuilder}; + // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; @@ -295,13 +297,11 @@ impl GICDevice for GICv2 { #[cfg(test)] mod tests { - use hypervisor::kvm::KVMFds; - use super::super::GICVersion; use super::super::GICv2Config; use super::*; - use crate::GIC_IRQ_MAX; + use hypervisor::kvm::KVMFds; #[test] fn test_create_gicv2() { diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 2444e1658..2ebdf1e8f 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -12,17 +12,16 @@ use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, Context, Result}; +use kvm_ioctls::DeviceFd; +use log::{error, info}; + use super::{ state::{GICv3ItsState, GICv3State}, GICConfig, GICDevice, KvmDevice, UtilResult, }; use crate::interrupt_controller::error::InterruptError; -use anyhow::{anyhow, Context, Result}; - use hypervisor::kvm::KVM_FDS; -use kvm_ioctls::DeviceFd; -use log::error; -use log::info; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use migration::{ snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, @@ -577,13 +576,11 @@ impl GICv3Its { #[cfg(test)] mod tests { - use hypervisor::kvm::KVMFds; - use super::super::GICVersion; use super::super::GICv3Config; use super::*; - use crate::GIC_IRQ_MAX; + use hypervisor::kvm::KVMFds; #[test] fn test_create_gicv3() { diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 543e55bfb..bfad965df 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -15,15 +15,15 @@ mod gicv3; #[allow(dead_code)] mod state; -use kvm_ioctls::DeviceFd; - pub use gicv2::{GICv2, GICv2Config}; pub use gicv3::{GICv3, GICv3Config}; use std::sync::Arc; -use crate::interrupt_controller::error::InterruptError; use anyhow::{anyhow, Context, Result}; +use kvm_ioctls::DeviceFd; + +use crate::interrupt_controller::error::InterruptError; use machine_manager::machine::{KvmVmState, MachineLifecycle}; use util::{ device_tree::{self, FdtBuilder}, diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 63652fa62..e46334a39 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -10,17 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::Context; -use libc::c_uint; use std::mem::size_of; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; -use util::byte_code::ByteCode; +use anyhow::Context; +use libc::c_uint; use super::gicv3::{GICv3, GICv3Access, GICv3Its}; use super::GIC_IRQ_INTERNAL; use crate::interrupt_controller::Result; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; /// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. const REGISTER_SIZE: u64 = size_of::() as u64; diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 0dfaeded7..d036533e5 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -29,6 +29,8 @@ mod aarch64; mod error; +pub use anyhow::Result; + #[cfg(target_arch = "aarch64")] pub use aarch64::GICConfig as ICGICConfig; #[cfg(target_arch = "aarch64")] @@ -41,5 +43,4 @@ pub use aarch64::InterruptController; pub use aarch64::GIC_IRQ_INTERNAL; #[cfg(target_arch = "aarch64")] pub use aarch64::GIC_IRQ_MAX; -pub use anyhow::Result; pub use error::InterruptError; diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index a198113aa..ce4c1d1c6 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -21,6 +21,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; use log::{error, info}; +use vmm_sys_util::epoll::EventSet; + use machine_manager::machine::{PathInfo, PTY_PATH}; use machine_manager::{ config::{ChardevConfig, ChardevType}, @@ -32,7 +34,6 @@ use util::loop_context::{ }; use util::set_termi_raw_mode; use util::unix::limit_permission; -use vmm_sys_util::epoll::EventSet; /// Provide the trait that helps handle the input data. pub trait InputReceiver: Send { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 0b546a96a..fc6974142 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -12,6 +12,12 @@ use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, bail, Context, Result}; +#[cfg(target_arch = "x86_64")] +use byteorder::LittleEndian; +use byteorder::{BigEndian, ByteOrder}; +use log::{error, warn}; + use crate::legacy::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; @@ -23,14 +29,10 @@ use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlMemory32Fixed, AmlReadAndWrite}; use address_space::{AddressSpace, GuestAddress}; -use anyhow::{anyhow, bail, Context, Result}; -#[cfg(target_arch = "x86_64")] -use byteorder::LittleEndian; -use byteorder::{BigEndian, ByteOrder}; -use log::{error, warn}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; use util::offset_of; + #[cfg(target_arch = "x86_64")] const FW_CFG_IO_BASE: u64 = 0x510; // Size of ioports including control/data registers and DMA. diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 0b2e541cb..6fc9a29b9 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -25,8 +25,9 @@ //! - `x86_64` //! - `aarch64` -mod chardev; pub mod error; + +mod chardev; mod fwcfg; mod pflash; #[cfg(target_arch = "aarch64")] @@ -38,9 +39,11 @@ mod ramfb; #[cfg(target_arch = "x86_64")] mod rtc; mod serial; + +pub use anyhow::Result; + #[cfg(target_arch = "x86_64")] pub use self::rtc::{RTC, RTC_PORT_INDEX}; -pub use anyhow::Result; pub use chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; pub use error::LegacyError; #[cfg(target_arch = "x86_64")] diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 80f4a1e98..ebb9e7d58 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -14,13 +14,14 @@ use std::fs::File; use std::io::Write; use std::sync::{Arc, Mutex}; +use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, error, warn}; + use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; -use anyhow::{anyhow, bail, Context, Result}; -use log::{debug, error, warn}; use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; pub struct PFlash { @@ -879,11 +880,12 @@ impl AmlBuilder for PFlash { #[cfg(test)] mod test { + use std::fs; + use std::fs::File; + use super::*; use crate::sysbus::{IRQ_BASE, IRQ_MAX}; use address_space::AddressSpace; - use std::fs; - pub use std::fs::File; fn sysbus_init() -> SysBus { let sys_mem = AddressSpace::new( diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index eca9ef564..049025b95 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -12,6 +12,10 @@ use std::sync::{Arc, Mutex}; +use anyhow::{Context, Result}; +use log::{debug, error}; +use vmm_sys_util::eventfd::EventFd; + use super::chardev::{Chardev, InputReceiver}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; @@ -22,8 +26,6 @@ use acpi::{ AmlScopeBuilder, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::GuestAddress; -use anyhow::{Context, Result}; -use log::{debug, error}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, @@ -36,7 +38,6 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_data_u32; -use vmm_sys_util::eventfd::EventFd; const PL011_FLAG_TXFE: u8 = 0x80; const PL011_FLAG_RXFF: u8 = 0x40; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 39ea28a01..289d9f84b 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -13,14 +13,16 @@ use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use anyhow::{Context, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::error; +use vmm_sys_util::eventfd::EventFd; + use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::GuestAddress; -use anyhow::{Context, Result}; -use byteorder::{ByteOrder, LittleEndian}; -use log::error; use migration::{ snapshot::PL031_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, @@ -28,7 +30,6 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::num_ops::write_data_u32; -use vmm_sys_util::eventfd::EventFd; /// Registers for pl031 from ARM PrimeCell Real Time Clock Technical Reference Manual. /// Data Register. diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index ecd71517f..10f8b3d91 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -13,6 +13,10 @@ use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use anyhow::Result; +use log::{debug, error, warn}; +use vmm_sys_util::eventfd::EventFd; + use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::{ @@ -20,10 +24,6 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; -use anyhow::Result; -use log::{debug, error, warn}; -use vmm_sys_util::eventfd::EventFd; - use util::time::{mktime64, NANOSECONDS_PER_SECOND}; /// IO port of RTC device to select Register to read/write. @@ -432,9 +432,10 @@ impl AmlBuilder for RTC { #[cfg(test)] mod test { + use anyhow::Context; + use super::*; use address_space::GuestAddress; - use anyhow::Context; const WIGGLE: u8 = 2; diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 2f990c9e1..f2b1221ad 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -13,6 +13,12 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex}; +use anyhow::{bail, Context, Result}; +use log::error; +use vmm_sys_util::eventfd::EventFd; + +use super::chardev::{Chardev, InputReceiver}; +use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::{ @@ -22,7 +28,6 @@ use acpi::{ }; use address_space::GuestAddress; use hypervisor::kvm::KVM_FDS; -use log::error; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; @@ -33,11 +38,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use vmm_sys_util::eventfd::EventFd; -use super::chardev::{Chardev, InputReceiver}; -use super::error::LegacyError; -use anyhow::{bail, Context, Result}; pub const SERIAL_ADDR: u64 = 0x3f8; const UART_IER_RDI: u8 = 0x01; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 526d6e55a..232df248e 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -19,7 +19,6 @@ pub mod acpi; #[cfg(not(target_env = "musl"))] pub mod camera_backend; -mod interrupt_controller; pub mod legacy; pub mod misc; pub mod pci; @@ -27,6 +26,8 @@ pub mod scsi; pub mod sysbus; pub mod usb; +mod interrupt_controller; + #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, InterruptError as IntCtrlErrs, diff --git a/devices/src/misc/mod.rs b/devices/src/misc/mod.rs index a829356c1..9a22cecdd 100644 --- a/devices/src/misc/mod.rs +++ b/devices/src/misc/mod.rs @@ -10,7 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(not(target_env = "musl"))] -mod ivshmem; #[cfg(not(target_env = "musl"))] pub mod scream; + +#[cfg(not(target_env = "musl"))] +mod ivshmem; diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index c757bd320..f63358eb3 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -23,7 +23,6 @@ use std::{ thread, }; -use address_space::{GuestAddress, HostMemMapping, Region}; use anyhow::{bail, Context, Result}; use core::time; use log::{error, warn}; @@ -31,6 +30,7 @@ use log::{error, warn}; use self::{alsa::AlsaStreamData, audio_demo::AudioDemo}; use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; +use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::scream::ScreamConfig; use pulseaudio::{PulseStreamData, TARGET_LATENCY_MS}; diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index e41774457..954a4a1c6 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; -use address_space::Region; +use anyhow::{bail, Context, Result}; use log::debug; use super::{ @@ -21,8 +21,7 @@ use super::{ hotplug::HotplugOps, PciDevOps, PciIntxState, }; - -use anyhow::{bail, Context, Result}; +use address_space::Region; type DeviceBusInfo = (Arc>, Arc>); @@ -246,7 +245,7 @@ impl PciBus { #[cfg(test)] mod tests { - use address_space::{AddressSpace, Region}; + use anyhow::Result; use super::*; use crate::pci::bus::PciBus; @@ -254,7 +253,7 @@ mod tests { use crate::pci::root_port::RootPort; use crate::pci::{PciDevBase, PciHost}; use crate::{Device, DeviceBase}; - use anyhow::Result; + use address_space::{AddressSpace, Region}; #[derive(Clone)] struct PciDevice { diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 6149c1805..1afaa001b 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -13,7 +13,7 @@ use std::collections::HashSet; use std::sync::{Arc, Mutex}; -use address_space::Region; +use anyhow::{anyhow, Context, Result}; use log::{error, warn}; use crate::pci::intx::Intx; @@ -23,7 +23,7 @@ use crate::pci::{ pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, }; use crate::pci::{ranges_overlap, PciError}; -use anyhow::{anyhow, Context, Result}; +use address_space::Region; /// Size in bytes of the configuration space of legacy PCI device. pub const PCI_CONFIG_SPACE_SIZE: usize = 256; @@ -1186,9 +1186,8 @@ impl PciConfig { #[cfg(test)] mod tests { - use address_space::{AddressSpace, GuestAddress, RegionOps}; - use super::*; + use address_space::{AddressSpace, GuestAddress, RegionOps}; const MSI_CAP_ID: u8 = 0x05; const MSIX_CAP_ID: u8 = 0x11; diff --git a/devices/src/pci/demo_dev.rs b/devices/src/pci/demo_dev.rs index 61c6ee9b9..731fe95ee 100644 --- a/devices/src/pci/demo_dev.rs +++ b/devices/src/pci/demo_dev.rs @@ -28,7 +28,6 @@ /// /// The example cmdline for the device is: /// "-device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0,bar_num=3,bar_size=4096" -/// use std::{ sync::Mutex, sync::{ @@ -37,9 +36,8 @@ use std::{ }, }; -use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; +use anyhow::{bail, Result}; use log::error; -use machine_manager::config::DemoDevConfig; #[cfg(not(target_env = "musl"))] use crate::pci::demo_device::{ @@ -54,7 +52,8 @@ use crate::pci::{ }; use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; use crate::{Device, DeviceBase}; -pub use anyhow::{bail, Result}; +use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; +use machine_manager::config::DemoDevConfig; pub struct DemoDev { base: PciDevBase, diff --git a/devices/src/pci/demo_device/base_device.rs b/devices/src/pci/demo_device/base_device.rs index 6dad14f35..7c5b4e5cd 100644 --- a/devices/src/pci/demo_device/base_device.rs +++ b/devices/src/pci/demo_device/base_device.rs @@ -10,13 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -/// BaseDevice is a simplest demo-pci-device. Its function is to -/// multiply data written by two and return it when reading. +use std::collections::HashMap; + +use anyhow::Result; + use crate::pci::demo_dev::DeviceTypeOperation; use address_space::GuestAddress; -pub use anyhow::{bail, Result}; -use std::collections::HashMap; +/// BaseDevice is a simplest demo-pci-device. Its function is to +/// multiply data written by two and return it when reading. #[derive(Default)] pub struct BaseDevice { result: HashMap, diff --git a/devices/src/pci/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs index c85a8556d..678758134 100644 --- a/devices/src/pci/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -17,16 +17,18 @@ // Currently, the supported operations are: // Get surface size, Get cursor image size, Get Surface data, Get cursor image data. -use crate::pci::demo_dev::DeviceTypeOperation; -use address_space::{AddressSpace, GuestAddress}; -use anyhow::{bail, Ok, Result}; -use byteorder::{ByteOrder, LittleEndian}; -use log::error; -use once_cell::sync::Lazy; use std::{ ptr, sync::{Arc, Mutex}, }; + +use anyhow::{bail, Ok, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::error; +use once_cell::sync::Lazy; + +use crate::pci::demo_dev::DeviceTypeOperation; +use address_space::{AddressSpace, GuestAddress}; use ui::{ console::{ register_display, DisplayChangeListener, DisplayChangeListenerOperations, DisplayMouse, diff --git a/devices/src/pci/demo_device/gpu_device.rs b/devices/src/pci/demo_device/gpu_device.rs index 08a320ae4..3f03c56f6 100644 --- a/devices/src/pci/demo_device/gpu_device.rs +++ b/devices/src/pci/demo_device/gpu_device.rs @@ -19,14 +19,17 @@ // Set dirty for target area of the surface 、 // Update the cursor image. -use address_space::{AddressSpace, GuestAddress}; -use anyhow::{bail, Result}; -use byteorder::{ByteOrder, LittleEndian}; -use log::info; use std::{ ptr, sync::{Arc, Mutex, Weak}, }; + +use anyhow::{bail, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use log::info; + +use crate::pci::demo_dev::DeviceTypeOperation; +use address_space::{AddressSpace, GuestAddress}; use ui::{ console::{ console_close, console_init, display_cursor_define, display_graphic_update, @@ -38,8 +41,8 @@ use ui::{ }, }; use util::pixman::pixman_format_code_t; + pub const UPDATE_FACTOR: [u8; 7] = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40]; -use crate::pci::demo_dev::DeviceTypeOperation; #[derive(Debug)] pub enum GpuEvent { diff --git a/devices/src/pci/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs index 3d2b41013..e06fcba90 100644 --- a/devices/src/pci/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -15,15 +15,16 @@ // Users can write a rom address in the mmio configuration space of the device. // Then if an input event occurs, the event information will be recorded to the corresponding memory by this device. -use address_space::{AddressSpace, GuestAddress}; -use byteorder::{ByteOrder, LittleEndian}; -use once_cell::sync::Lazy; use std::sync::{Arc, Mutex}; -use ui::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; use anyhow::{bail, Result}; +use byteorder::{ByteOrder, LittleEndian}; +use once_cell::sync::Lazy; use crate::pci::demo_dev::DeviceTypeOperation; +use address_space::{AddressSpace, GuestAddress}; +use ui::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; + static MEM_ADDR: Lazy>> = Lazy::new(|| { Arc::new(Mutex::new(MemSpace { sys_mem: None, diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index b6164e88a..d7cef948b 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -12,6 +12,13 @@ use std::sync::{Arc, Mutex}; +use anyhow::{Context, Result}; + +#[cfg(target_arch = "aarch64")] +use crate::pci::PCI_INTR_BASE; +use crate::pci::{bus::PciBus, PciDevOps, PCI_PIN_NUM, PCI_SLOT_MAX}; +#[cfg(target_arch = "x86_64")] +use crate::pci::{le_read_u32, le_write_u32}; use crate::sysbus::{SysBusDevBase, SysBusDevOps}; use crate::{Device, DeviceBase}; use acpi::{ @@ -27,14 +34,6 @@ use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlOne, AmlQWordDesc}; use address_space::{AddressSpace, GuestAddress, RegionOps}; -use anyhow::{Context, Result}; - -#[cfg(target_arch = "aarch64")] -use crate::pci::PCI_INTR_BASE; -use crate::pci::{bus::PciBus, PciDevOps, PCI_PIN_NUM, PCI_SLOT_MAX}; - -#[cfg(target_arch = "x86_64")] -use crate::pci::{le_read_u32, le_write_u32}; #[cfg(target_arch = "x86_64")] const CONFIG_ADDRESS_ENABLE_MASK: u32 = 0x8000_0000; @@ -541,7 +540,6 @@ impl AmlBuilder for PciHost { #[cfg(test)] pub mod tests { - use address_space::Region; use byteorder::{ByteOrder, LittleEndian}; use super::*; @@ -550,6 +548,7 @@ pub mod tests { use crate::pci::root_port::RootPort; use crate::pci::{PciDevBase, Result}; use crate::{Device, DeviceBase}; + use address_space::Region; struct PciDevice { base: PciDevBase, diff --git a/devices/src/pci/hotplug.rs b/devices/src/pci/hotplug.rs index b35d52f8d..df5bfca39 100644 --- a/devices/src/pci/hotplug.rs +++ b/devices/src/pci/hotplug.rs @@ -12,9 +12,10 @@ use std::sync::{Arc, Mutex}; -use crate::pci::{PciBus, PciDevOps}; use anyhow::{bail, Context, Result}; +use crate::pci::{PciBus, PciDevOps}; + pub trait HotplugOps: Send { /// Plug device, usually called when hot plug device in device_add. fn plug(&mut self, dev: &Arc>) -> Result<()>; diff --git a/devices/src/pci/intx.rs b/devices/src/pci/intx.rs index 0af40f690..503f7c975 100644 --- a/devices/src/pci/intx.rs +++ b/devices/src/pci/intx.rs @@ -14,9 +14,9 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use log::error; -use util::test_helper::{is_test_enabled, trigger_intx}; use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_INTR_BASE, PCI_PIN_NUM}; +use util::test_helper::{is_test_enabled, trigger_intx}; pub type InterruptHandler = Box Result<()> + Send + Sync>; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index f35beb093..301955ecf 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -10,37 +10,38 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod error; -pub use error::PciError; pub mod config; pub mod demo_dev; +pub mod demo_device; +pub mod error; pub mod hotplug; pub mod intx; pub mod msix; mod bus; -pub mod demo_device; mod host; mod root_port; -use crate::{Device, DeviceBase}; +pub use anyhow::{bail, Result}; + pub use bus::PciBus; pub use config::{PciConfig, INTERRUPT_PIN}; +pub use error::PciError; pub use host::PciHost; pub use intx::{init_intx, InterruptHandler, PciIntxState}; pub use msix::{init_msix, is_msix_enabled}; pub use root_port::RootPort; -use util::AsAny; use std::{ mem::size_of, sync::{Arc, Mutex, Weak}, }; -pub use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use crate::{Device, DeviceBase}; +use util::AsAny; const BDF_FUNC_SHIFT: u8 = 3; pub const PCI_SLOT_MAX: u8 = 32; diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index fd9f36532..4fb060e60 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -19,6 +19,13 @@ use anyhow::{bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; +use crate::pci::config::{ + CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM, +}; +use crate::pci::{ + le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, + ranges_overlap, PciBus, +}; use address_space::{GuestAddress, Region, RegionOps}; use hypervisor::kvm::{MsiVector, KVM_FDS}; use migration::{ @@ -31,14 +38,6 @@ use util::{ test_helper::{add_msix_msg, is_test_enabled}, }; -use crate::pci::config::{ - CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM, -}; -use crate::pci::{ - le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, - ranges_overlap, PciBus, -}; - pub const MSIX_TABLE_ENTRY_SIZE: u16 = 16; pub const MSIX_TABLE_SIZE_MAX: u16 = 0x7ff; const MSIX_TABLE_VEC_CTL: u16 = 0x0c; diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index f91aa71b5..373c17576 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -13,17 +13,9 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use crate::{Device, DeviceBase}; -use address_space::Region; use anyhow::{anyhow, bail, Context, Result}; use log::{error, info}; -use machine_manager::qmp::send_device_deleted_msg; -use migration::{ - DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, -}; -use migration_derive::{ByteCode, Desc}; use once_cell::sync::OnceCell; -use util::byte_code::ByteCode; use super::config::{ PciConfig, PcieDevType, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE, @@ -46,6 +38,14 @@ use crate::pci::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, PciDevOps, }; +use crate::{Device, DeviceBase}; +use address_space::Region; +use machine_manager::qmp::send_device_deleted_msg; +use migration::{ + DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, +}; +use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; const DEVICE_ID_RP: u16 = 0x000c; diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index a3a3d2d1f..44e678de5 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -13,10 +13,10 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; -use crate::{Device, DeviceBase}; use anyhow::{bail, Result}; use crate::ScsiBus::{aio_complete_cb, ScsiBus, ScsiCompleteCb}; +use crate::{Device, DeviceBase}; use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; use util::aio::{Aio, WriteZeroesState}; diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 4f24a897a..a02c0697a 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -12,18 +12,19 @@ pub mod error; -use crate::{Device, DeviceBase}; pub use anyhow::{bail, Context, Result}; pub use error::SysBusError; use std::fmt; use std::sync::{Arc, Mutex}; +use vmm_sys_util::eventfd::EventFd; + +use crate::{Device, DeviceBase}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use hypervisor::kvm::KVM_FDS; use util::AsAny; -use vmm_sys_util::eventfd::EventFd; // Now that the serial device use a hardcoded IRQ number (4), and the starting // free IRQ number can be 5. diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 4402cc1c6..f21740f30 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -23,14 +23,6 @@ use log::{debug, error, info}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use machine_manager::config::UsbCameraConfig; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use util::aio::{iov_discard_front_direct, Iovec}; -use util::byte_code::ByteCode; -use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; - use super::camera_media_type_guid::MEDIA_TYPE_GUID_HASHMAP; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{ @@ -42,6 +34,13 @@ use crate::usb::descriptor::*; use crate::usb::{ UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; +use machine_manager::config::UsbCameraConfig; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::aio::{iov_discard_front_direct, Iovec}; +use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; // CRC16 of "STRATOVIRT" const UVC_VENDOR_ID: u16 = 0xB74C; @@ -1171,9 +1170,8 @@ fn gen_color_matching_desc() -> Result> { #[cfg(test)] mod test { - use crate::camera_backend::{CameraFormatList, CameraFrame, FmtType}; - use super::*; + use crate::camera_backend::{CameraFormatList, CameraFrame, FmtType}; fn test_interface_table_data_len(interface: Arc, size_offset: usize) { let descs = &interface.other_desc; diff --git a/devices/src/usb/camera_media_type_guid.rs b/devices/src/usb/camera_media_type_guid.rs index f58b0c96c..1b520eba1 100644 --- a/devices/src/usb/camera_media_type_guid.rs +++ b/devices/src/usb/camera_media_type_guid.rs @@ -12,9 +12,10 @@ //! Media Type GUID. referred with uvc.h in linux kernel. -use once_cell::sync::Lazy; use std::collections::HashMap; +use once_cell::sync::Lazy; + use crate::camera_backend::FmtType; pub const MEDIA_TYPE_GUID: [(FmtType, [u8; 16]); 2] = [ diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 95253fdc0..46c45484c 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -14,10 +14,9 @@ use std::sync::Arc; use anyhow::{bail, Context, Result}; -use util::byte_code::ByteCode; - use super::config::*; use super::UsbDevice; +use util::byte_code::ByteCode; pub const USB_MAX_INTERFACES: u32 = 16; const USB_DESCRIPTOR_TYPE_SHIFT: u32 = 8; diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 61e678868..3f05f8970 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -14,10 +14,9 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use log::{debug, error}; -use ui::input::set_kbd_led_state; - use super::config::*; use super::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; +use ui::input::set_kbd_led_state; /// HID keycode const HID_KEYBOARD_LEFT_CONTROL: u8 = 0xe0; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index dbefc378e..a0dad8996 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -10,21 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod error; -use crate::{Device, DeviceBase}; -pub use anyhow::Result; -pub use error::UsbError; -use util::byte_code::ByteCode; - #[cfg(not(target_env = "musl"))] pub mod camera; #[cfg(not(target_env = "musl"))] pub mod camera_media_type_guid; pub mod config; -mod descriptor; +pub mod error; #[cfg(not(target_env = "musl"))] pub mod hid; - #[cfg(not(target_env = "musl"))] pub mod keyboard; #[cfg(not(target_env = "musl"))] @@ -35,17 +28,25 @@ pub mod tablet; pub mod usbhost; pub mod xhci; +mod descriptor; + +pub use anyhow::Result; + +pub use error::UsbError; + use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context}; use log::{debug, error}; -use util::aio::{mem_from_buf, mem_to_buf, Iovec}; use self::descriptor::USB_MAX_INTERFACES; +use crate::{Device, DeviceBase}; use config::*; use descriptor::{UsbDescriptor, UsbDescriptorOps}; use machine_manager::qmp::send_device_deleted_msg; +use util::aio::{mem_from_buf, mem_to_buf, Iovec}; +use util::byte_code::ByteCode; use xhci::xhci_controller::{UsbPort, XhciDevice}; const USB_MAX_ENDPOINTS: u32 = 15; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 114f74a3d..fa9a4a631 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -20,8 +20,6 @@ use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, warn}; use once_cell::sync::Lazy; -use machine_manager::config::{DriveFile, UsbStorageConfig}; - use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, @@ -36,6 +34,7 @@ use crate::{ }, ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, }; +use machine_manager::config::{DriveFile, UsbStorageConfig}; // Storage device descriptor static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 7f216d694..c20ad89ad 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod host_usblib; + use std::{ collections::LinkedList, os::unix::io::RawFd, @@ -53,8 +55,6 @@ use util::{ loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}, }; -mod host_usblib; - const NON_ISO_PACKETS_NUMS: c_int = 0; const HANDLE_TIMEOUT_MS: u64 = 2; const USB_HOST_BUFFER_LEN: usize = 12 * 1024; diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index 20e205f8a..16b0a05f2 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -13,6 +13,7 @@ pub mod xhci_controller; pub mod xhci_pci; pub mod xhci_regs; + mod xhci_ring; /// Transfer Request Block diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index eaa5d0e21..a2d7b99b8 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -21,10 +21,6 @@ use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, warn}; -use address_space::{AddressSpace, GuestAddress}; -use machine_manager::config::XhciConfig; -use machine_manager::event_loop::EventLoop; - use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; use super::{ @@ -36,6 +32,9 @@ use crate::usb::{config::*, TransferOps}; use crate::usb::{ UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, }; +use address_space::{AddressSpace, GuestAddress}; +use machine_manager::config::XhciConfig; +use machine_manager::event_loop::EventLoop; const INVALID_SLOT_ID: u32 = 0; pub const MAX_INTRS: u32 = 1; diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 4bef38694..092770b4b 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -15,25 +15,24 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; +use log::debug; +use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; +use super::xhci_regs::{ + build_cap_ops, build_doorbell_ops, build_oper_ops, build_port_ops, build_runtime_ops, + XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME, +}; use crate::pci::config::{ PciConfig, RegionType, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use crate::pci::msix::update_dev_id; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; +use crate::usb::UsbDeviceOps; use crate::{Device, DeviceBase}; use address_space::{AddressSpace, Region}; -use log::debug; use machine_manager::config::XhciConfig; -use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; -use super::xhci_regs::{ - build_cap_ops, build_doorbell_ops, build_oper_ops, build_port_ops, build_runtime_ops, - XHCI_CAP_LENGTH, XHCI_OFF_DOORBELL, XHCI_OFF_RUNTIME, -}; -use crate::usb::UsbDeviceOps; - /// 5.2 PCI Configuration Registers(USB) const PCI_CLASS_PI: u16 = 0x09; const PCI_CACHE_LINE_SIZE: u16 = 0x0c; diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index b668ec944..269a90a22 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -14,18 +14,16 @@ use std::sync::atomic::{fence, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{bail, Result}; - -use address_space::{AddressSpace, GuestAddress, RegionOps}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error}; -use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; - -use super::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; use super::xhci_controller::dma_write_bytes; use super::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; use super::xhci_ring::XhciTRB; +use super::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; use crate::usb::{config::*, UsbError}; +use address_space::{AddressSpace, GuestAddress, RegionOps}; +use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; /// Capability offset or size. pub(crate) const XHCI_CAP_LENGTH: u32 = 0x40; diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index f39b72de1..396f0d97a 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -14,8 +14,6 @@ use std::sync::atomic::{fence, AtomicBool, AtomicU64, Ordering}; use std::sync::Arc; use anyhow::{bail, Context, Result}; - -use address_space::{AddressSpace, GuestAddress}; use byteorder::{ByteOrder, LittleEndian}; use log::debug; @@ -23,6 +21,7 @@ use super::super::UsbError; use super::xhci_controller::{dma_read_u32, dma_write_u32, DwordOrder, XhciEpCtx}; use super::{TRBType, TRB_C, TRB_LK_TC, TRB_SIZE, TRB_TR_CH, TRB_TYPE_MASK, TRB_TYPE_SHIFT}; use crate::usb::xhci::xhci_controller::dma_read_bytes; +use address_space::{AddressSpace, GuestAddress}; const TRB_LINK_LIMIT: u32 = 32; /// The max size of a ring segment in bytes is 64k. diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 8abd3ee2b..db27fa433 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -10,10 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod interrupt; + +pub use interrupt::MsiVector; + use std::collections::HashMap; use std::mem::{align_of, size_of}; use std::sync::{Arc, Mutex}; +use anyhow::{bail, Context, Result}; use arc_swap::ArcSwap; use kvm_bindings::kvm_userspace_memory_region as MemorySlot; use kvm_bindings::*; @@ -24,12 +29,8 @@ use vmm_sys_util::{ eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, }; -use anyhow::{bail, Context, Result}; -pub use interrupt::MsiVector; use interrupt::{IrqRoute, IrqRouteEntry, IrqRouteTable}; -mod interrupt; - // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; pub const KVM_SET_USER_MEMORY_REGION: u32 = 0x4020_ae46; diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 16edaa1ab..efe24a2d7 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -13,6 +13,6 @@ //! This crate offers interfaces for different kinds of hypervisors, such as KVM. pub mod error; -pub use error::HypervisorError; - pub mod kvm; + +pub use error::HypervisorError; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fc495021e..2b350971e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -11,12 +11,18 @@ // See the Mulan PSL v2 for more details. pub mod error; -mod micro_vm; pub mod standard_vm; + +mod micro_vm; #[cfg(target_arch = "x86_64")] mod vm_state; +pub use anyhow::Result; + pub use crate::error::MachineError; +pub use micro_vm::LightMachine; +pub use standard_vm::StdMachine; + use std::collections::{BTreeMap, HashMap}; use std::fs::{remove_file, File}; use std::net::TcpListener; @@ -27,34 +33,22 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; #[cfg(not(target_env = "musl"))] use std::time::Duration; -#[cfg(not(target_env = "musl"))] -use devices::misc::scream::Scream; +use anyhow::{anyhow, bail, Context}; use log::warn; #[cfg(not(target_env = "musl"))] -use machine_manager::config::scream::parse_scream; -use machine_manager::event_loop::EventLoop; -#[cfg(not(target_env = "musl"))] -use ui::console::{get_run_stage, VmRunningStage}; -use util::file::{clear_file, lock_file, unlock_file}; -#[cfg(not(target_env = "musl"))] use vmm_sys_util::eventfd::EventFd; -pub use micro_vm::LightMachine; - #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{ create_backend_mem, create_default_mem, AddressSpace, KvmMemoryListener, Region, }; -pub use anyhow::Result; -use anyhow::{anyhow, bail, Context}; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; -#[cfg(target_arch = "aarch64")] -use devices::InterruptController; - +#[cfg(not(target_env = "musl"))] +use devices::misc::scream::Scream; use devices::pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(not(target_env = "musl"))] @@ -62,8 +56,12 @@ use devices::usb::{ camera::UsbCamera, keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, usbhost::UsbHost, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, }; +#[cfg(target_arch = "aarch64")] +use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::scream::parse_scream; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, @@ -78,12 +76,15 @@ use machine_manager::config::{ parse_gpu, parse_usb_camera, parse_usb_host, parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, }; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; use smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use standard_vm::Result as StdResult; -pub use standard_vm::StdMachine; +#[cfg(not(target_env = "musl"))] +use ui::console::{get_run_stage, VmRunningStage}; +use util::file::{clear_file, lock_file, unlock_file}; use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 9ffa72317..7ec39c7e9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -29,17 +29,12 @@ //! - `aarch64` pub mod error; -pub use error::MicroVmError; -use machine_manager::config::DiskFormat; -use machine_manager::event_loop::EventLoop; -use machine_manager::qmp::qmp_schema::UpdateRegionArgument; -use util::aio::{AioEngine, WriteZeroesState}; mod mem_layout; mod syscall; -use super::Result as MachineResult; -use log::{error, info}; +pub use error::MicroVmError; + use std::collections::HashMap; use std::fmt; use std::fmt::Debug; @@ -48,6 +43,15 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Condvar, Mutex}; use std::vec::Vec; +use anyhow::{anyhow, bail, Context, Result}; +#[cfg(target_arch = "x86_64")] +use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; +use log::{error, info}; + +use super::Result as MachineResult; +use super::{error::MachineError, MachineOps}; +#[cfg(target_arch = "x86_64")] +use crate::vm_state; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; #[cfg(target_arch = "aarch64")] @@ -67,8 +71,9 @@ use devices::sysbus::{SysBusDevType, SysRes}; use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; #[cfg(target_arch = "x86_64")] use hypervisor::kvm::KVM_FDS; -#[cfg(target_arch = "x86_64")] -use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; +use machine_manager::config::DiskFormat; +use machine_manager::event_loop::EventLoop; +use machine_manager::qmp::qmp_schema::UpdateRegionArgument; use machine_manager::{ config::{ parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DriveFile, @@ -85,6 +90,7 @@ use machine_manager::{ use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use migration::{MigrationManager, MigrationStatus}; use syscall::syscall_whitelist; +use util::aio::{AioEngine, WriteZeroesState}; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::{ @@ -95,11 +101,6 @@ use virtio::{ VirtioMmioDevice, VirtioMmioState, VirtioNetState, }; -use super::{error::MachineError, MachineOps}; -#[cfg(target_arch = "x86_64")] -use crate::vm_state; -use anyhow::{anyhow, bail, Context, Result}; - // The replaceable block device maximum count. const MMIO_REPLACEABLE_BLK_NR: usize = 4; // The replaceable network device maximum count. diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index dfdffe4eb..cc22cfcee 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -14,23 +14,19 @@ mod pci_host_root; mod syscall; pub use crate::error::MachineError; -use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; -use devices::acpi::power::PowerDev; -use log::{error, info, warn}; -use machine_manager::config::ShutdownAction; -#[cfg(not(target_env = "musl"))] -use machine_manager::config::UiContext; -use machine_manager::event_loop::EventLoop; + use std::collections::HashMap; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; -#[cfg(not(target_env = "musl"))] -use ui::{gtk::gtk_display_init, vnc::vnc_init}; -use vmm_sys_util::eventfd::EventFd; +use anyhow::{bail, Context, Result}; use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; +use log::{error, info, warn}; +use vmm_sys_util::eventfd::EventFd; +use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; +use crate::MachineOps; use acpi::{ processor_append_priv_res, AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, @@ -48,23 +44,28 @@ use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{ CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuTopology, CPU, PMU_INTR, PPI_BASE, }; +use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; +use devices::acpi::power::PowerDev; #[cfg(not(target_env = "musl"))] use devices::legacy::Ramfb; use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; - use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use devices::sysbus::{SysBus, SysBusDevType, SysRes}; use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; #[cfg(not(target_env = "musl"))] use machine_manager::config::parse_ramfb; +use machine_manager::config::ShutdownAction; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::UiContext; use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, NumaNodes, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, @@ -73,16 +74,14 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci_host_root::PciHostRoot; use syscall::syscall_whitelist; +#[cfg(not(target_env = "musl"))] +use ui::{gtk::gtk_display_init, vnc::vnc_init}; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; use util::seccomp::BpfRule; use util::set_termi_canon_mode; -use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; -use crate::MachineOps; -use anyhow::{bail, Context, Result}; - /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { Flash = 0, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 9a4b4ef35..44a41f0a4 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -12,27 +12,16 @@ #[cfg(target_arch = "aarch64")] pub mod aarch64; +pub mod error; + #[cfg(target_arch = "x86_64")] mod x86_64; -pub mod error; -pub use error::StandardVmError; +pub use anyhow::Result; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; -use log::error; -use machine_manager::event_loop::EventLoop; -use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; -use machine_manager::{config::get_cameradev_config, machine::MachineLifecycle}; -#[cfg(not(target_env = "musl"))] -use ui::{ - input::{key_event, point_event}, - vnc::qmp_query_vnc, -}; -use util::aio::{AioEngine, WriteZeroesState}; -use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; -use vmm_sys_util::epoll::EventSet; -use vmm_sys_util::eventfd::EventFd; +pub use error::StandardVmError; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; @@ -44,8 +33,17 @@ use std::rc::Rc; use std::string::String; use std::sync::{Arc, Mutex}; +use anyhow::{bail, Context}; +use log::error; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; + +#[cfg(target_arch = "x86_64")] +use self::x86_64::ich9_lpc::{PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET}; use super::Result as MachineResult; use crate::MachineOps; +#[cfg(target_arch = "aarch64")] +use aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] use acpi::AcpiGenericAddress; use acpi::{ @@ -55,8 +53,6 @@ use acpi::{ use address_space::{ AddressRange, FileBackend, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, }; -pub use anyhow::Result; -use anyhow::{bail, Context}; use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; @@ -67,24 +63,28 @@ use machine_manager::config::{ ChardevType, ConfigCheck, DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{DeviceInterface, KvmVmState}; +use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; +use machine_manager::{config::get_cameradev_config, machine::MachineLifecycle}; use migration::MigrationManager; +#[cfg(not(target_env = "musl"))] +use ui::{ + input::{key_event, point_event}, + vnc::qmp_query_vnc, +}; +use util::aio::{AioEngine, WriteZeroesState}; use util::byte_code::ByteCode; +use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; use virtio::{ qmp_balloon, qmp_query_balloon, Block, BlockState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, VhostKern, VhostUser, VirtioDevice, VirtioNetState, VirtioPciDevice, }; - -#[cfg(target_arch = "aarch64")] -use aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] use x86_64::{LayoutEntryType, MEM_LAYOUT}; -#[cfg(target_arch = "x86_64")] -use self::x86_64::ich9_lpc::{PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET}; - trait StdMachineOps: AcpiBuilder { fn init_pci_host(&self) -> Result<()>; diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 0b38d04f5..4ccce0ca6 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -15,11 +15,14 @@ use std::sync::{ Arc, Mutex, Weak, }; +use anyhow::Context; +use log::error; +use vmm_sys_util::eventfd::EventFd; + use super::VENDOR_ID_INTEL; use crate::standard_vm::Result; use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; -use anyhow::Context; use devices::pci::config::{ PciConfig, CLASS_CODE_ISA_BRIDGE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, @@ -28,9 +31,7 @@ use devices::pci::{ le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; use devices::{Device, DeviceBase}; -use log::error; use util::byte_code::ByteCode; -use vmm_sys_util::eventfd::EventFd; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index 731eb912c..f679efe9d 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -12,8 +12,11 @@ use std::sync::{Arc, Mutex, Weak}; -use address_space::{Region, RegionOps}; use anyhow::{bail, Result}; +use log::error; + +use super::VENDOR_ID_INTEL; +use address_space::{Region, RegionOps}; use devices::pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, @@ -22,9 +25,6 @@ use devices::pci::{ le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; use devices::{Device, DeviceBase}; -use log::error; - -use super::VENDOR_ID_INTEL; const DEVICE_ID_INTEL_Q35_MCH: u16 = 0x29c0; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index ec470b063..b2e0fc38f 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -11,18 +11,26 @@ // See the Mulan PSL v2 for more details. pub(crate) mod ich9_lpc; + mod mch; mod syscall; -use crate::error::MachineError; -use log::{error, info}; use std::collections::HashMap; use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex}; + +use anyhow::{bail, Context, Result}; +use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; +use log::{error, info}; use vmm_sys_util::eventfd::EventFd; +use self::ich9_lpc::SLEEP_CTRL_OFFSET; +use super::error::StandardVmError; +use super::{AcpiBuilder, StdMachineOps}; +use crate::error::MachineError; +use crate::{vm_state, MachineOps}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, @@ -38,7 +46,6 @@ use devices::legacy::{ use devices::pci::{PciDevOps, PciHost}; use devices::sysbus::SysBus; use hypervisor::kvm::KVM_FDS; -use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; #[cfg(not(target_env = "musl"))] use machine_manager::config::UiContext; use machine_manager::config::{ @@ -55,18 +62,12 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use syscall::syscall_whitelist; +#[cfg(not(target_env = "musl"))] +use ui::{gtk::gtk_display_init, vnc::vnc_init}; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, }; -use self::ich9_lpc::SLEEP_CTRL_OFFSET; -use super::error::StandardVmError; -use super::{AcpiBuilder, StdMachineOps}; -use crate::{vm_state, MachineOps}; -use anyhow::{bail, Context, Result}; -#[cfg(not(target_env = "musl"))] -use ui::{gtk::gtk_display_init, vnc::vnc_init}; - const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; const HOLE_640K_END: u64 = 0x0010_0000; diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index b13b4fcc2..c7c41b3ab 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -13,14 +13,14 @@ use std::os::unix::net::UnixListener; use anyhow::{bail, Context, Result}; -use util::arg_parser::{Arg, ArgMatches, ArgParser}; -use util::file::clear_file; -use util::unix::{limit_permission, parse_unix_uri}; use crate::{ config::{add_trace_events, ChardevType, CmdParser, MachineType, VmConfig}, temp_cleaner::TempCleaner, }; +use util::arg_parser::{Arg, ArgMatches, ArgParser}; +use util::file::clear_file; +use util::unix::{limit_permission, parse_unix_uri}; /// This macro is to run struct $z 's function $s whose arg is $x 's inner member. /// There is a multi-macro-cast in cases of vec and bool. diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs index d2b931d7f..d385d49d2 100644 --- a/machine_manager/src/config/balloon.rs +++ b/machine_manager/src/config/balloon.rs @@ -116,9 +116,8 @@ pub fn parse_balloon(vm_config: &mut VmConfig, balloon_config: &str) -> Result Result<()> { let mut cmd_params = CmdParser::new("device"); diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index fc055ed5e..06bca1bac 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -26,6 +26,7 @@ use crate::config::{ }; use crate::qmp::qmp_schema; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; + const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; @@ -700,9 +701,8 @@ impl VmConfig { #[cfg(test)] mod tests { - use crate::config::get_pci_bdf; - use super::*; + use crate::config::get_pci_bdf; #[test] fn test_drive_config_cmdline_parser() { diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index 5b1d6f610..bce9e1b14 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -10,12 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{anyhow, bail, Context, Result}; + use super::error::ConfigError; use crate::config::{ pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_SOCK_PATH_LENGTH, MAX_STRING_LENGTH, MAX_TAG_LENGTH, }; -use anyhow::{anyhow, bail, Context, Result}; /// Config struct for `fs`. /// Contains fs device's attr. diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 186a1adbb..0668c6a6b 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::{error::ConfigError, M}; -use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; use anyhow::{anyhow, Result}; use log::warn; +use super::{error::ConfigError, M}; +use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; + /// The maximum number of outputs. pub const VIRTIO_GPU_MAX_OUTPUTS: usize = 16; diff --git a/machine_manager/src/config/iothread.rs b/machine_manager/src/config/iothread.rs index 69fa9a41b..ac3a0a9e5 100644 --- a/machine_manager/src/config/iothread.rs +++ b/machine_manager/src/config/iothread.rs @@ -10,11 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; use crate::config::{check_arg_too_long, CmdParser, ConfigCheck, VmConfig}; -use anyhow::{anyhow, Result}; const MAX_IOTHREAD_NUM: usize = 8; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 7b1267652..b6e9b0b62 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -10,6 +10,35 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod camera; +pub mod display; +pub mod error; +pub mod vnc; + +mod balloon; +mod boot_source; +mod chardev; +mod demo_dev; +mod devices; +mod drive; +mod fs; +mod gpu; +mod incoming; +mod iothread; +mod machine_config; +mod network; +mod numa; +mod pci; +mod ramfb; +mod rng; +mod sasl_auth; +pub mod scream; +mod scsi; +mod smbios; +mod tls_creds; +mod usb; +mod vfio; + pub use balloon::*; pub use boot_source::*; pub use camera::*; @@ -37,42 +66,14 @@ pub use usb::*; pub use vfio::*; pub use vnc::*; -mod balloon; -mod boot_source; -pub mod camera; -mod chardev; -mod demo_dev; -mod devices; -pub mod display; -mod drive; -pub mod error; -mod fs; -mod gpu; -mod incoming; -mod iothread; -mod machine_config; -mod network; -mod numa; -mod pci; -mod ramfb; -mod rng; -mod sasl_auth; -pub mod scream; -mod scsi; -mod smbios; -mod tls_creds; -mod usb; -mod vfio; -pub mod vnc; - use std::collections::HashMap; use std::fs::File; use std::str::FromStr; -use serde::{Deserialize, Serialize}; - use anyhow::{anyhow, bail, Context, Result}; use log::error; +use serde::{Deserialize, Serialize}; + #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::{ @@ -784,6 +785,7 @@ mod tests { fn test_add_trace_events_02() { use std::fs::File; use std::io::Write; + use util::trace::is_trace_event_enabled; let file = "/tmp/test_trace_events"; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 526945c4a..1c48af5a7 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -532,9 +532,8 @@ fn is_netdev_queues_valid(queues: u16) -> bool { #[cfg(test)] mod tests { - use crate::config::{get_pci_bdf, MAX_STRING_LENGTH}; - use super::*; + use crate::config::{get_pci_bdf, MAX_STRING_LENGTH}; #[test] fn test_network_config_cmdline_parser() { diff --git a/machine_manager/src/config/ramfb.rs b/machine_manager/src/config/ramfb.rs index 7eb5c5e19..8473c1df9 100644 --- a/machine_manager/src/config/ramfb.rs +++ b/machine_manager/src/config/ramfb.rs @@ -10,9 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::config::CmdParser; use anyhow::Result; +use crate::config::CmdParser; + pub fn parse_ramfb(cfg_args: &str) -> Result { let mut cmd_parser = CmdParser::new("ramfb"); cmd_parser.push("").push("install").push("id"); diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index 303b3f6f4..b153dcf2e 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -134,9 +134,8 @@ pub fn parse_rng_obj(object_args: &str) -> Result { #[cfg(test)] mod tests { - use crate::config::get_pci_bdf; - use super::*; + use crate::config::get_pci_bdf; #[test] fn test_rng_config_cmdline_parser_01() { diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs index c51a19ab1..506763adc 100644 --- a/machine_manager/src/config/sasl_auth.rs +++ b/machine_manager/src/config/sasl_auth.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{anyhow, Context, Result}; +use serde::{Deserialize, Serialize}; + use crate::config::{ ConfigError, {CmdParser, VmConfig}, }; -use anyhow::{anyhow, Context, Result}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct SaslAuthObjConfig { diff --git a/machine_manager/src/config/smbios.rs b/machine_manager/src/config/smbios.rs index 1d229891f..2c8f0d95d 100644 --- a/machine_manager/src/config/smbios.rs +++ b/machine_manager/src/config/smbios.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::str::FromStr; + use anyhow::{bail, Context, Result}; +use serde::{Deserialize, Serialize}; use crate::config::{CmdParser, VmConfig}; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; #[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct SmbiosType0Config { diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index b7ed7f2a4..7dd0eaade 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -10,12 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::path::Path; + +use anyhow::{anyhow, Context, Result}; +use serde::{Deserialize, Serialize}; + use crate::config::{ ConfigError, {CmdParser, VmConfig}, }; -use anyhow::{anyhow, Context, Result}; -use serde::{Deserialize, Serialize}; -use std::path::Path; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct TlsCredObjConfig { @@ -76,9 +78,10 @@ impl VmConfig { #[cfg(test)] mod tests { - use super::*; use std::{env, fs}; + use super::*; + #[test] fn test_add_tlscred() { let mut dir = env::current_dir().unwrap(); diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs index 2c6e545d6..dddebde74 100644 --- a/machine_manager/src/config/vfio.rs +++ b/machine_manager/src/config/vfio.rs @@ -10,9 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{anyhow, Result}; + use super::error::ConfigError; use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; -use anyhow::{anyhow, Result}; + #[derive(Default, Debug)] pub struct VfioConfig { pub sysfsdev: String, diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index 63c57964f..b243d9454 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::config::{CmdParser, ConfigError, VmConfig}; +use std::net::Ipv4Addr; use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; -use std::net::Ipv4Addr; + +use crate::config::{CmdParser, ConfigError, VmConfig}; /// Configuration of vnc. #[derive(Debug, Clone, Default, Serialize, Deserialize)] diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index bf7b27c58..f85982596 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -15,12 +15,12 @@ use std::os::unix::prelude::RawFd; use std::sync::{Arc, Mutex}; use std::{process, thread}; +use anyhow::bail; +use log::info; + use super::config::IothreadConfig; use crate::machine::IOTHREADS; use crate::qmp::qmp_schema::IothreadInfo; - -use anyhow::bail; -use log::info; use util::loop_context::{ gen_delete_notifiers, get_notifiers_fds, EventLoopContext, EventLoopManager, EventNotifier, }; diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index e873be623..78c6a77e9 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -30,5 +30,6 @@ pub mod qmp; pub mod signal_handler; pub mod socket; pub mod temp_cleaner; -pub use error::MachineManagerError; pub mod test_server; + +pub use error::MachineManagerError; diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index ff4e191f9..5608fd5d0 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -37,20 +37,20 @@ use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex, RwLock}; use std::time::{SystemTime, UNIX_EPOCH}; +use anyhow::{Context, Result}; use log::{error, info, warn}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::Value; -use util::leak_bucket::LeakBucket; -use util::set_termi_canon_mode; -use util::time::NANOSECONDS_PER_SECOND; use self::qmp_schema::{self as schema, QmpCommand}; use crate::event_loop::EventLoop; use crate::machine::MachineExternalInterface; use crate::socket::SocketRWHandler; use crate::temp_cleaner::TempCleaner; -use anyhow::{Context, Result}; +use util::leak_bucket::LeakBucket; +use util::set_termi_canon_mode; +use util::time::NANOSECONDS_PER_SECOND; static mut QMP_CHANNEL: Option> = None; @@ -602,9 +602,10 @@ pub fn send_device_deleted_msg(id: &str) { #[cfg(test)] mod tests { + use std::os::unix::net::{UnixListener, UnixStream}; + use super::*; use serde_json; - use std::os::unix::net::{UnixListener, UnixStream}; #[test] fn test_qmp_greeting_msg() { @@ -699,9 +700,10 @@ mod tests { #[test] fn test_qmp_event_macro() { - use crate::socket::{Socket, SocketRWHandler}; use std::io::Read; + use crate::socket::{Socket, SocketRWHandler}; + // Pre test. Environment preparation QmpChannel::object_init(); let mut buffer = [0u8; 200]; @@ -750,9 +752,10 @@ mod tests { #[test] fn test_qmp_send_response() { - use crate::socket::Socket; use std::io::Read; + use crate::socket::Socket; + // Pre test. Environment preparation let mut buffer = [0u8; 300]; let (listener, mut client, server) = prepare_unix_socket_environment("07"); diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 274303821..09e6f1169 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -10,8 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use serde::{Deserialize, Serialize}; pub use serde_json::Value as Any; + +use serde::{Deserialize, Serialize}; use strum_macros::{EnumIter, EnumString, EnumVariantNames}; use super::Version; diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index c2977e396..57e71b28b 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -13,7 +13,6 @@ use std::io::Write; use libc::{c_int, c_void, siginfo_t}; -use util::set_termi_canon_mode; use vmm_sys_util::signal::register_signal_handler; use crate::{ @@ -21,6 +20,7 @@ use crate::{ qmp::{qmp_schema, QmpChannel}, temp_cleaner::TempCleaner, }; +use util::set_termi_canon_mode; const VM_EXIT_SUCCESS: i32 = 0; pub const VM_EXIT_GENE_ERR: i32 = 1; diff --git a/machine_manager/src/test_server.rs b/machine_manager/src/test_server.rs index 4c83e6587..f126ede07 100644 --- a/machine_manager/src/test_server.rs +++ b/machine_manager/src/test_server.rs @@ -10,18 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::event_loop::EventLoop; -use crate::machine::{MachineTestInterface, IOTHREADS}; -use crate::socket::SocketHandler; -use hex::FromHexError; use std::os::unix::io::RawFd; use std::os::unix::net::UnixStream; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::sync::{Arc, Mutex}; + +use hex::FromHexError; +use vmm_sys_util::epoll::EventSet; + +use crate::event_loop::EventLoop; +use crate::machine::{MachineTestInterface, IOTHREADS}; +use crate::socket::SocketHandler; use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation}; use util::test_helper::{eoi_intx, get_test_clock, has_msix_msg, query_intx, set_test_clock}; -use vmm_sys_util::epoll::EventSet; pub struct TestSock { stream: UnixStream, diff --git a/migration/migration_derive/src/attr_parser.rs b/migration/migration_derive/src/attr_parser.rs index d86e4271c..ef1269f27 100644 --- a/migration/migration_derive/src/attr_parser.rs +++ b/migration/migration_derive/src/attr_parser.rs @@ -134,9 +134,10 @@ fn version_to_u32(version_str: &str) -> u32 { #[cfg(test)] mod test { - use super::*; use syn::{parse_quote, ItemStruct}; + use super::*; + #[test] fn test_version_to_u32() { let version_str_01 = "0.1.0"; diff --git a/migration/migration_derive/src/lib.rs b/migration/migration_derive/src/lib.rs index 1b39268a4..ad2946452 100644 --- a/migration/migration_derive/src/lib.rs +++ b/migration/migration_derive/src/lib.rs @@ -48,14 +48,14 @@ //! 2. The `ByteCode` derive to auto add `ByteCode` trait and its relying trait for //! struct, such as `Default`, `Sync`, `Send`. -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, DeriveInput}; - mod attr_parser; mod field_parser; mod struct_parser; +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + /// Define a macro derive `Desc`. #[proc_macro_derive(Desc, attributes(desc_version, alias))] pub fn derive_desc(input: TokenStream) -> TokenStream { diff --git a/migration/src/error.rs b/migration/src/error.rs index d7720cc7a..3c7014dc3 100644 --- a/migration/src/error.rs +++ b/migration/src/error.rs @@ -10,9 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::protocol::MigrationStatus; use thiserror::Error; +use crate::protocol::MigrationStatus; + #[derive(Error, Debug)] pub enum MigrationError { #[error("UtilError")] diff --git a/migration/src/general.rs b/migration/src/general.rs index 8cbc6d445..14077a1cc 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -15,12 +15,13 @@ use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::mem::size_of; +use anyhow::{anyhow, Context, Result}; + use crate::manager::{Instance, MIGRATION_MANAGER}; use crate::protocol::{ DeviceStateDesc, FileFormat, MigrationHeader, MigrationStatus, VersionCheck, HEADER_LENGTH, }; use crate::{MigrationError, MigrationManager}; -use anyhow::{anyhow, Context, Result}; use util::unix::host_page_size; impl MigrationManager { diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 82c346349..112fc6f57 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -21,16 +21,18 @@ pub mod migration; pub mod protocol; pub mod snapshot; +pub use anyhow::Result; + +pub use error::MigrationError; +pub use manager::{MigrationHook, MigrationManager}; +pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; + use std::time::Duration; use std::{net::TcpStream, os::unix::net::UnixStream, thread}; -pub use anyhow::Result; use log::error; -pub use error::MigrationError; use machine_manager::qmp::{qmp_schema, Response}; -pub use manager::{MigrationHook, MigrationManager}; -pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; /// Start to snapshot VM. /// diff --git a/migration/src/manager.rs b/migration/src/manager.rs index b10c7bc05..3a6e18745 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -17,13 +17,13 @@ use std::io::{Read, Write}; use std::sync::{Arc, Mutex, RwLock}; use std::time::Instant; +use anyhow::{Context, Result}; use log::info; use once_cell::sync::Lazy; use crate::general::translate_id; use crate::migration::DirtyBitmap; use crate::protocol::{DeviceStateDesc, MemBlock, MigrationStatus, StateTransfer}; -use anyhow::{Context, Result}; use machine_manager::config::VmConfig; use machine_manager::machine::MachineLifecycle; use util::byte_code::ByteCode; diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 5bd01e18b..854a6626e 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -16,6 +16,7 @@ use std::mem::size_of; use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; +use anyhow::{anyhow, bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as MemorySlot; use log::{info, warn}; @@ -23,7 +24,6 @@ use crate::general::Lifecycle; use crate::manager::MIGRATION_MANAGER; use crate::protocol::{MemBlock, MigrationStatus, Request, Response, TransStatus}; use crate::{MigrationError, MigrationManager}; -use anyhow::{anyhow, bail, Context, Result}; use hypervisor::kvm::KVM_FDS; use machine_manager::config::{get_pci_bdf, PciBdf, VmConfig}; use util::unix::host_page_size; diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 0fd1ec59a..aa02b0e7d 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -15,12 +15,13 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::slice::{from_raw_parts, from_raw_parts_mut}; +use anyhow::{anyhow, bail, Context, Result}; use kvm_ioctls::Kvm; use serde::{Deserialize, Serialize}; use crate::MigrationError; -use anyhow::{anyhow, bail, Context, Result}; use util::byte_code::ByteCode; + /// This status for migration in migration process. /// /// # Notes diff --git a/migration/src/snapshot.rs b/migration/src/snapshot.rs index 32b11e790..3a449bad8 100644 --- a/migration/src/snapshot.rs +++ b/migration/src/snapshot.rs @@ -10,15 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::general::{translate_id, Lifecycle}; -use crate::manager::{MigrationManager, MIGRATION_MANAGER}; -use crate::protocol::{DeviceStateDesc, FileFormat, MigrationStatus, HEADER_LENGTH}; -use crate::MigrationError; -use anyhow::{anyhow, bail, Context, Result}; use std::collections::HashMap; use std::fs::{create_dir, File}; use std::io::{Read, Write}; use std::path::PathBuf; + +use anyhow::{anyhow, bail, Context, Result}; + +use crate::general::{translate_id, Lifecycle}; +use crate::manager::{MigrationManager, MIGRATION_MANAGER}; +use crate::protocol::{DeviceStateDesc, FileFormat, MigrationStatus, HEADER_LENGTH}; +use crate::MigrationError; use util::unix::host_page_size; pub const SERIAL_SNAPSHOT_ID: &str = "serial"; diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index 94fe01a4b..5df3672cf 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -18,9 +18,10 @@ use std::{ process, }; -use crate::OzoneError; use anyhow::{bail, Context, Result}; +use crate::OzoneError; + const MOUNT_DIR: &str = "/proc/mounts"; const CGROUP_ALLOW_LIST: [&str; 2] = ["cpuset.cpus", "memory.limit_in_bytes"]; pub type CgroupCfg = HashMap>; diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index a2797b1fb..e7570c55d 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -10,11 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::cgroup::{self, init_cgroup, parse_cgroup, CgroupCfg}; -use crate::OzoneError; -use crate::{capability, namespace, syscall}; -use anyhow::{anyhow, bail, Context, Result}; - use std::process::Command; use std::{ fs::{canonicalize, read_dir}, @@ -23,6 +18,11 @@ use std::{ process::Stdio, }; +use anyhow::{anyhow, bail, Context, Result}; + +use crate::cgroup::{self, init_cgroup, parse_cgroup, CgroupCfg}; +use crate::OzoneError; +use crate::{capability, namespace, syscall}; use util::arg_parser::ArgMatches; const BASE_OZONE_PATH: &str = "/srv/ozone"; diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 3be2a5dbe..8c0abb18e 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -11,11 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod error; -use anyhow::{Context, Result}; -pub use error::OzoneError; - -use crate::args::create_args_parser; -use crate::handler::OzoneHandler; mod args; mod capability; @@ -24,6 +19,13 @@ mod handler; mod namespace; mod syscall; +pub use error::OzoneError; + +use anyhow::{Context, Result}; + +use crate::args::create_args_parser; +use crate::handler::OzoneHandler; + pub trait ExitCode { /// Returns the value to use as the exit status. fn code(self) -> i32; diff --git a/ozone/src/namespace.rs b/ozone/src/namespace.rs index b112dffdf..3e5757428 100644 --- a/ozone/src/namespace.rs +++ b/ozone/src/namespace.rs @@ -13,9 +13,10 @@ use std::fs::File; use std::os::unix::prelude::IntoRawFd; -use crate::syscall; use anyhow::{Context, Result}; +use crate::syscall; + const ROOT_DIR_NAME: &str = "/"; const OLD_ROOT_DIR_NAME: &str = "old_root"; const CURRENT_DIR_NAME: &str = "."; diff --git a/smbios/src/smbios_table.rs b/smbios/src/smbios_table.rs index 3857b46ac..9211bd881 100644 --- a/smbios/src/smbios_table.rs +++ b/smbios/src/smbios_table.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::mem::size_of; + use machine_manager::config::{ MachineConfig, SmbiosConfig, SmbiosType0Config, SmbiosType17Config, SmbiosType1Config, SmbiosType2Config, SmbiosType3Config, SmbiosType4Config, }; -use std::mem::size_of; use util::byte_code::ByteCode; const TYPE0_HANDLE: u16 = 0x0; diff --git a/src/main.rs b/src/main.rs index 73e4077b2..cc81afe6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use log::{error, info}; +use thiserror::Error; + use machine::{LightMachine, MachineOps, StdMachine}; use machine_manager::{ cmdline::{check_api_channel, create_args_parser, create_vmconfig}, @@ -31,8 +33,6 @@ use util::loop_context::EventNotifierHelper; use util::test_helper::{is_test_enabled, set_test_enabled}; use util::{arg_parser, daemonize::daemonize, logger, set_termi_canon_mode}; -use thiserror::Error; - #[derive(Error, Debug)] enum MainError { #[error("Manager")] diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs index 3bb71792b..84bffc14c 100644 --- a/tests/mod_test/src/libdriver/fwcfg.rs +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -12,11 +12,10 @@ use std::mem; -use devices::legacy::FwCfgEntryType; - use super::malloc::GuestAllocator; use crate::libtest::TestState; use crate::utils::{swap_u16, swap_u32, swap_u64}; +use devices::legacy::FwCfgEntryType; use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "aarch64")] diff --git a/tests/mod_test/src/libdriver/machine.rs b/tests/mod_test/src/libdriver/machine.rs index bada3c0ad..beba4786a 100644 --- a/tests/mod_test/src/libdriver/machine.rs +++ b/tests/mod_test/src/libdriver/machine.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::rc::Rc; + use super::malloc::GuestAllocator; use super::pci_bus::TestPciBus; use crate::libtest::TestState; -use std::cell::RefCell; -use std::rc::Rc; const ARM_VIRT_RAM_ADDR: u64 = 0x40000000; const ARM_VIRT_RAM_SIZE: u64 = 0x20000000; diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 5aad32b81..3e6cb540e 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::pci_bus::PciBusOps; -use super::pci_bus::TestPciBus; use std::cell::RefCell; use std::rc::Rc; +use super::pci_bus::PciBusOps; +use super::pci_bus::TestPciBus; + const BAR_MAP: [u8; 6] = [0x10, 0x14, 0x18, 0x1c, 0x20, 0x24]; const PCI_PIN_NUM: u8 = 4; pub const PCI_VENDOR_ID: u8 = 0x00; diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index fbff7b464..1cf5f0ad3 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -10,12 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::rc::Rc; + use crate::libdriver::pci::*; use crate::libtest::TestState; use crate::utils::{read_le_u16, read_le_u32, read_le_u64}; use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; -use std::cell::RefCell; -use std::rc::Rc; const PCIE_MMIO_BASE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; const PCIE_MMIO_SIZE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 91e870898..2e377f2e1 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -21,8 +21,6 @@ use std::{ use byteorder::{ByteOrder, LittleEndian}; use serde_json::Value; -use util::byte_code::ByteCode; - use super::{ machine::TestStdMachine, malloc::GuestAllocator, @@ -53,6 +51,7 @@ use devices::usb::{ }, UsbDeviceRequest, }; +use util::byte_code::ByteCode; pub const PCI_VENDOR_ID_REDHAT: u16 = 0x1b36; pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 4348fdf8c..b7db01a55 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -10,13 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::malloc::GuestAllocator; -use crate::libtest::TestState; use std::cell::RefCell; use std::collections::HashMap; use std::mem::size_of; use std::rc::Rc; use std::time; + +use super::malloc::GuestAllocator; +use crate::libtest::TestState; use util::byte_code::ByteCode; use util::num_ops::round_up; use util::offset_of; diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index ccf97add2..352dd2a0a 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -13,8 +13,6 @@ use std::cell::RefCell; use std::mem::size_of; use std::rc::Rc; -use util::byte_code::ByteCode; -use util::num_ops::round_up; use super::machine::TestStdMachine; use super::malloc::GuestAllocator; @@ -27,6 +25,8 @@ use crate::libdriver::virtio::{ use crate::libtest::{test_init, TestState}; use crate::utils::ImageType; use crate::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; +use util::byte_code::ByteCode; +use util::num_ops::round_up; pub const VIRTIO_BLK_F_BARRIER: u64 = 0; pub const VIRTIO_BLK_F_SIZE_MAX: u64 = 1; diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index 66a32757d..406f308d4 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{cell::RefCell, mem::size_of, rc::Rc, slice::from_raw_parts, vec}; + use super::{ machine::TestStdMachine, malloc::GuestAllocator, @@ -19,8 +21,6 @@ use super::{ use crate::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; use crate::libdriver::virtio_pci_modern::TestVirtioPciDev; use crate::libtest::{test_init, TestState}; - -use std::{cell::RefCell, mem::size_of, rc::Rc, slice::from_raw_parts, vec}; use util::byte_code::ByteCode; use virtio::{ VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index 2783d83e7..2054bcd1e 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -10,6 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::rc::Rc; +use std::time::{Duration, Instant}; + use super::malloc::GuestAllocator; use super::pci::{ PCIBarAddr, PciMsixOps, TestPciDev, PCI_CAP_ID_VNDR, PCI_DEVICE_ID, PCI_SUBSYSTEM_ID, @@ -21,9 +25,6 @@ use super::virtio::{ VIRTIO_CONFIG_S_FEATURES_OK, VIRTIO_F_VERSION_1, }; use crate::libtest::TestState; -use std::cell::RefCell; -use std::rc::Rc; -use std::time::{Duration, Instant}; use util::offset_of; const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1; diff --git a/tests/mod_test/src/libdriver/virtio_rng.rs b/tests/mod_test/src/libdriver/virtio_rng.rs index b14cb03a7..75d009820 100644 --- a/tests/mod_test/src/libdriver/virtio_rng.rs +++ b/tests/mod_test/src/libdriver/virtio_rng.rs @@ -10,14 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::rc::Rc; + use super::machine::TestStdMachine; use super::malloc::GuestAllocator; use super::virtio_pci_modern::TestVirtioPciDev; use crate::libtest::{test_init, TestState}; -use std::cell::RefCell; -use std::rc::Rc; - pub fn create_rng( random_file: String, max_bytes: u64, diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index f8f660959..1097046fb 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -10,12 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{ - libdriver::vnc::EncodingType::*, - libtest::{test_init, TestState}, -}; -use anyhow::{bail, Result}; -use core::time; use std::{ cell::RefCell, cmp, @@ -26,6 +20,9 @@ use std::{ thread::sleep, time::Duration, }; + +use anyhow::{bail, Result}; +use core::time; use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use super::{ @@ -34,6 +31,10 @@ use super::{ pci::{PCIBarAddr, TestPciDev, PCI_VENDOR_ID}, pci_bus::TestPciBus, }; +use crate::{ + libdriver::vnc::EncodingType::*, + libtest::{test_init, TestState}, +}; const EPOLL_DEFAULT_TIMEOUT: i32 = 1000; pub const MAX_RECVBUF_LEN: usize = 1024; diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index 7641e8f71..b84205d4b 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -10,12 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use rand::distributions::Alphanumeric; -use rand::{thread_rng, Rng}; use std::fs; use std::path::Path; use std::process::Command; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; + use crate::libdriver::qcow2::create_qcow2_img; pub fn get_rand_str(size: usize) -> String { diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index ee29acfb3..31da6432a 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -10,17 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{cell::RefCell, mem, rc::Rc}; + +use byteorder::{ByteOrder, LittleEndian}; + use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicRedistributor, AcpiRsdp, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTableHeader, CacheHierarchyNode, ProcessorHierarchyNode, }; -use byteorder::{ByteOrder, LittleEndian}; use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +use mod_test::libdriver::fwcfg::bios_args; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; -use std::{cell::RefCell, mem, rc::Rc}; - -use mod_test::libdriver::fwcfg::bios_args; use mod_test::libtest::{test_init, TestState}; // Now dsdt table data length is 3482. diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index b39f5058d..c61e40dd7 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -10,18 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use mod_test::libdriver::machine::TestStdMachine; -use mod_test::libdriver::malloc::GuestAllocator; -use mod_test::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; -use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; -use mod_test::libtest::{test_init, TestState}; -use serde_json::json; use std::cell::RefCell; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::process::Command; use std::rc::Rc; use std::{thread, time}; + +use serde_json::json; + +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; +use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; +use mod_test::libtest::{test_init, TestState}; use util::{byte_code::ByteCode, offset_of}; const BALLOON_F_DEFLATE_ON_OOM_TEST: u32 = 2; diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 333aff9d8..36542f3a5 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -10,10 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use mod_test::libdriver::qcow2::CLUSTER_SIZE; -use virtio::device::block::VirtioBlkConfig; +use std::cell::RefCell; +use std::mem::size_of; +use std::process::Command; +use std::rc::Rc; +use std::time::{Duration, Instant}; +use std::{thread, time}; use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libdriver::qcow2::CLUSTER_SIZE; use mod_test::libdriver::qcow2::{check_snapshot, create_snapshot, delete_snapshot}; use mod_test::libdriver::virtio::TestVringDescEntry; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps}; @@ -32,17 +37,11 @@ use mod_test::libdriver::virtio_block::{ use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::TestState; use mod_test::utils::{create_img, ImageType, TEST_IMAGE_SIZE}; - -use std::cell::RefCell; -use std::mem::size_of; -use std::process::Command; -use std::rc::Rc; -use std::time::{Duration, Instant}; -use std::{thread, time}; use util::aio::{aio_probe, AioEngine}; use util::byte_code::ByteCode; use util::num_ops::round_up; use util::offset_of; +use virtio::device::block::VirtioBlkConfig; const TEST_IMAGE_SIZE_1M: u64 = 1024 * 1024; diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index 46500bba1..9827d9dbe 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -10,7 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::{fs, mem}; + use byteorder::{ByteOrder, LittleEndian}; + use devices::legacy::FwCfgEntryType; use mod_test::libdriver::fwcfg::{bios_args, FW_CFG_BASE}; use mod_test::libdriver::machine::TestStdMachine; @@ -18,11 +24,6 @@ use mod_test::libtest::test_init; use mod_test::utils::{cleanup_img, create_img, ImageType, TEST_IMAGE_SIZE}; use mod_test::utils::{swap_u16, swap_u32}; -use std::cell::RefCell; -use std::process::Command; -use std::rc::Rc; -use std::{fs, mem}; - // FwCfg Signature const FW_CFG_DMA_SIGNATURE: u128 = 0x51454d5520434647; diff --git a/tests/mod_test/tests/ged_test.rs b/tests/mod_test/tests/ged_test.rs index c6a4a4449..c91b4a238 100644 --- a/tests/mod_test/tests/ged_test.rs +++ b/tests/mod_test/tests/ged_test.rs @@ -10,9 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; use mod_test::libtest::{test_init, TestState}; -use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; pub const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::Ged as usize].0; const ADD_ADDRESS: u64 = 1; diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index e26e00304..3ac0ad154 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -10,12 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{cell::RefCell, fs::File, process::Command, rc::Rc, string::String}; + +use serde_json::{json, Value::String as JsonString}; + use mod_test::{ libdriver::{machine::TestStdMachine, malloc::GuestAllocator}, libtest::{test_init, TestState}, }; -use serde_json::{json, Value::String as JsonString}; -use std::{cell::RefCell, fs::File, process::Command, rc::Rc, string::String}; pub struct MemoryTest { pub state: Rc>, diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 0b33faef3..33ec90477 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -10,16 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use rand::Rng; -use serde_json::json; use std::cell::RefCell; use std::mem::size_of; use std::process::Command; use std::rc::Rc; use std::thread::sleep; use std::time; -use util::byte_code::ByteCode; -use util::offset_of; + +use rand::Rng; +use serde_json::json; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; @@ -30,6 +29,8 @@ use mod_test::libdriver::virtio::{ }; use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; use mod_test::libtest::{test_init, TestState}; +use util::byte_code::ByteCode; +use util::offset_of; /// Device handles packets with partial checksum. const VIRTIO_NET_F_CSUM: u32 = 0; diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index e53bb8c8f..277f5a057 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -10,6 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::rc::Rc; +use std::{thread, time}; + +use serde_json::json; + use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::pci::*; @@ -23,11 +29,6 @@ use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::{test_init, TestState}; use mod_test::utils::{cleanup_img, create_img, read_le_u16, ImageType, TEST_IMAGE_SIZE}; -use serde_json::json; -use std::cell::RefCell; -use std::rc::Rc; -use std::{thread, time}; - const VIRTIO_PCI_VENDOR: u16 = 0x1af4; const BLK_DEVICE_ID: u16 = 0x1042; const MAX_DEVICE_NUM_IN_MULTIFUNC: u8 = 248; diff --git a/tests/mod_test/tests/pl031_test.rs b/tests/mod_test/tests/pl031_test.rs index d9ed2571d..2a9557914 100644 --- a/tests/mod_test/tests/pl031_test.rs +++ b/tests/mod_test/tests/pl031_test.rs @@ -10,12 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use devices::legacy::{RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; -use mod_test::libtest::{test_init, TestState}; -use rand::{thread_rng, Rng}; use std::thread::sleep; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use rand::{thread_rng, Rng}; + +use devices::legacy::{RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; +use mod_test::libtest::{test_init, TestState}; + const RTC_ADDR_BASE: u64 = 0x0901_0000; fn pl031_read_time(ts: &TestState) -> u32 { diff --git a/tests/mod_test/tests/ramfb_test.rs b/tests/mod_test/tests/ramfb_test.rs index 650044126..b147eb655 100644 --- a/tests/mod_test/tests/ramfb_test.rs +++ b/tests/mod_test/tests/ramfb_test.rs @@ -10,18 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use mod_test::libdriver::fwcfg::{bios_args, FwCfgDmaAccess}; -use mod_test::libdriver::machine::TestStdMachine; -use mod_test::libdriver::malloc::GuestAllocator; -use mod_test::libtest::{test_init, TestState}; -use mod_test::utils::{swap_u32, swap_u64}; - use std::cell::RefCell; use std::fs; use std::mem; use std::path::Path; use std::rc::Rc; +use mod_test::libdriver::fwcfg::{bios_args, FwCfgDmaAccess}; +use mod_test::libdriver::machine::TestStdMachine; +use mod_test::libdriver::malloc::GuestAllocator; +use mod_test::libtest::{test_init, TestState}; +use mod_test::utils::{swap_u32, swap_u64}; + const FRAMEBUFFER_SIZE: u64 = 3 * 1024 * 1024; const RAMFB_FORMAT: u32 = 0x34325258; const HORIZONTAL_RESOLUTION: u32 = 800; diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs index ee43001fe..c7a982212 100644 --- a/tests/mod_test/tests/rng_test.rs +++ b/tests/mod_test/tests/rng_test.rs @@ -10,6 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cell::RefCell; +use std::collections::HashSet; +use std::os::unix::fs::FileTypeExt; +use std::path::Path; +use std::rc::Rc; +use std::time::{Duration, Instant}; + use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::{ TestVirtQueue, TestVringDescEntry, VirtioDeviceOps, VIRTIO_F_VERSION_1, @@ -18,13 +25,6 @@ use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libdriver::virtio_rng::create_rng; use mod_test::libtest::TestState; -use std::cell::RefCell; -use std::collections::HashSet; -use std::os::unix::fs::FileTypeExt; -use std::path::Path; -use std::rc::Rc; -use std::time::{Duration, Instant}; - const TIMEOUT_US: u64 = 10 * 1000 * 1000; const RANDOM_FILE: &str = "/dev/random"; const RNG_DATA_BYTES: u64 = 64; diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 548a90642..356ef11df 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -17,9 +17,6 @@ use std::slice::from_raw_parts; use std::{thread, time}; use rand::Rng; -use util::aio::{aio_probe, AioEngine}; -use util::byte_code::ByteCode; -use util::offset_of; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; @@ -30,6 +27,9 @@ use mod_test::libdriver::virtio::{ use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::{test_init, TestState}; use mod_test::utils::{cleanup_img, create_img, ImageType, TEST_IMAGE_SIZE}; +use util::aio::{aio_probe, AioEngine}; +use util::byte_code::ByteCode; +use util::offset_of; const TEST_VIRTIO_SCSI_CDB_SIZE: usize = 32; const TEST_VIRTIO_SCSI_SENSE_SIZE: usize = 96; diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 321ac7b58..cbfeea320 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -14,7 +14,6 @@ use std::cell::{RefCell, RefMut}; use std::rc::Rc; use std::{fs::remove_file, fs::File, io::Write}; -use mod_test::libtest::TestState; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -23,6 +22,7 @@ use devices::usb::xhci::TRBCCode; use mod_test::libdriver::usb::{ TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, }; +use mod_test::libtest::TestState; const UVC_FID: u8 = 1; const UVC_HEADER_LEN: u8 = 2; diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs index 15c74da6f..a62c28d39 100644 --- a/tests/mod_test/tests/usb_storage_test.rs +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -23,7 +23,6 @@ use devices::usb::{ xhci::{TRBCCode, TRB_SIZE}, UsbDeviceRequest, }; - use mod_test::libdriver::usb::{ TestIovec, TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, STORAGE_DEVICE_IN_ENDPOINT_ID, STORAGE_DEVICE_OUT_ENDPOINT_ID, diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index 5915cc9c6..2bc1b6d21 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -10,6 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::time::{Duration, Instant}; +use std::vec; + use mod_test::libdriver::virtio::VirtioDeviceOps; use mod_test::libdriver::virtio_gpu::{ current_curosr_check, current_surface_check, get_display_info, get_edid, invalid_cmd_test, @@ -21,8 +24,6 @@ use mod_test::libdriver::virtio_gpu::{ VirtioGpuSetScanout, VirtioGpuTransferToHost2d, }; use mod_test::libdriver::virtio_gpu::{set_up, tear_down}; -use std::time::{Duration, Instant}; -use std::vec; use util::byte_code::ByteCode; use virtio::{ get_image_hostmem, get_pixman_format, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 0162b3b85..08ed05bb1 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -10,12 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use rand::Rng; -use serde_json::json; use std::cell::RefCell; use std::mem::size_of; use std::rc::Rc; -use util::offset_of; + +use rand::Rng; +use serde_json::json; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::{ @@ -32,6 +32,7 @@ use mod_test::libdriver::virtio_block::{ use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; use mod_test::libtest::TestState; use mod_test::utils::{ImageType, TEST_IMAGE_SIZE}; +use util::offset_of; fn add_request( test_state: Rc>, diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index baf7c94dd..0a668904e 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -9,13 +9,11 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + use std::{ cell::RefCell, env, mem::size_of, path::Path, process::Command, rc::Rc, slice::from_raw_parts, }; -use util::byte_code::ByteCode; -use util::offset_of; - use mod_test::libdriver::{ machine::TestStdMachine, malloc::GuestAllocator, @@ -28,6 +26,8 @@ use mod_test::libdriver::{ }; use mod_test::libtest::{test_init, TestState}; use mod_test::utils::get_rand_str; +use util::byte_code::ByteCode; +use util::offset_of; const DEFAULT_FS_DESC_ELEM: usize = 4; // 4 elems: inheader/inbody/outheader/outbody. const TIMEOUT_US: u64 = 10 * 1000 * 1000; // 10s timeout. diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index 6bb76709d..4381814f2 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -10,7 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{cell::RefCell, rc::Rc}; + use anyhow::Result; +use serde_json::Value; +use vmm_sys_util::epoll::EventSet; + use mod_test::{ libdriver::vnc::{ create_new_client, set_up, tear_down, DemoGpuConfig, EncodingType, InputConfig, @@ -20,9 +25,6 @@ use mod_test::{ }, libtest::TestState, }; -use serde_json::Value; -use std::{cell::RefCell, rc::Rc}; -use vmm_sys_util::epoll::EventSet; fn qmp_query_vnc(test_state: Rc>) -> Value { let str = "{\"execute\": \"query-vnc\"}".to_string(); diff --git a/ui/src/console.rs b/ui/src/console.rs index a976058b3..dfe5d89ea 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -22,14 +22,13 @@ use anyhow::Result; use log::error; use once_cell::sync::Lazy; -use machine_manager::event_loop::EventLoop; -use util::pixman::{pixman_format_code_t, pixman_image_t}; - use crate::pixman::{ create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, pixman_glyph_from_vgafont, pixman_glyph_render, unref_pixman_image, ColorNames, COLOR_TABLE_RGB, }; +use machine_manager::event_loop::EventLoop; +use util::pixman::{pixman_format_code_t, pixman_image_t}; static CONSOLES: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(ConsoleList::new()))); diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 3a4b987dc..d4e7db593 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -31,13 +31,12 @@ use gtk::{ }; use log::error; +use super::ScaleMode; use crate::{ console::{get_run_stage, VmRunningStage}, gtk::{renew_image, update_window_size, GtkDisplay, ZoomOperate, GTK_SCALE_MIN, GTK_ZOOM_STEP}, }; -use super::ScaleMode; - #[derive(Clone)] pub(crate) struct GtkMenu { pub(crate) window: ApplicationWindow, diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 8d0ca431a..fd5e99cf6 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -38,6 +38,7 @@ use gtk::{ Application, ApplicationWindow, DrawingArea, HeaderBar, Label, RadioMenuItem, }; use log::{debug, error}; +use vmm_sys_util::eventfd::EventFd; use crate::{ console::{ @@ -54,7 +55,6 @@ use crate::{ }; use machine_manager::config::{DisplayConfig, UiContext}; use util::pixman::{pixman_format_code_t, pixman_image_composite, pixman_op_t}; -use vmm_sys_util::eventfd::EventFd; const CHANNEL_BOUND: usize = 1024; /// Width of default window. diff --git a/ui/src/input.rs b/ui/src/input.rs index 1be74dd1c..8ddec12f2 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -10,16 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::Result; -use log::debug; -use once_cell::sync::Lazy; use std::{ collections::HashMap, sync::{Arc, Mutex}, }; -use util::bitmap::Bitmap; + +use anyhow::Result; +use log::debug; +use once_cell::sync::Lazy; use crate::data::keycode::KEYSYM2KEYCODE; +use util::bitmap::Bitmap; // Logical window size for mouse. pub const ABS_MAX: u64 = 0x7fff; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 61c8be58f..ecd7cc4b1 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -11,10 +11,11 @@ // See the Mulan PSL v2 for more details. pub mod console; -mod data; pub mod error; pub mod gtk; pub mod input; pub mod pixman; pub mod utils; pub mod vnc; + +mod data; diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 1de8a7759..ef5f7f5ca 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -10,10 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{ - error::VncError, - vnc::client_io::{vnc_flush, vnc_write, ClientIoHandler, APP_NAME}, -}; +use std::ffi::{CStr, CString}; +use std::ptr; + use anyhow::{anyhow, Result}; use libc::{c_char, c_int, c_uint, c_void}; use log::info; @@ -24,8 +23,11 @@ use sasl2_sys::prelude::{ SASL_SUCCESS_DATA, }; use sasl2_sys::sasl::SASL_USERNAME; -use std::ffi::{CStr, CString}; -use std::ptr; + +use crate::{ + error::VncError, + vnc::client_io::{vnc_flush, vnc_write, ClientIoHandler, APP_NAME}, +}; use util::byte_code::ByteCode; /// Vnc Service. diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index ec274d4fb..902ee14ad 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -10,16 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use crate::{ - error::VncError, - vnc::{ - auth_sasl::SubAuthState, - client_io::{vnc_flush, vnc_write, ClientIoHandler, IoOperations}, - }, +use std::{ + cell::RefCell, + fs::File, + io::{BufReader, ErrorKind, Read, Write}, + net::TcpStream, + os::unix::prelude::{AsRawFd, RawFd}, + rc::Rc, + sync::Arc, }; + use anyhow::{anyhow, bail, Result}; use log::error; -use machine_manager::event_loop::EventLoop; use rustls::{ self, cipher_suite::{ @@ -36,19 +38,18 @@ use rustls::{ Certificate, KeyLogFile, PrivateKey, RootCertStore, ServerConfig, ServerConnection, SupportedCipherSuite, SupportedKxGroup, SupportedProtocolVersion, Ticketer, }; -use std::{ - cell::RefCell, - fs::File, - io::{BufReader, ErrorKind, Read, Write}, - net::TcpStream, - os::unix::prelude::{AsRawFd, RawFd}, - rc::Rc, - sync::Arc, -}; -use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use vmm_sys_util::epoll::EventSet; use super::client_io::vnc_disconnect_start; +use crate::{ + error::VncError, + vnc::{ + auth_sasl::SubAuthState, + client_io::{vnc_flush, vnc_write, ClientIoHandler, IoOperations}, + }, +}; +use machine_manager::event_loop::EventLoop; +use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; const TLS_CREDS_SERVER_CACERT: &str = "cacert.pem"; const TLS_CREDS_SERVERCERT: &str = "servercert.pem"; diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 4c3124607..a418b38e4 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -10,6 +10,22 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{ + cell::RefCell, + cmp, + collections::HashMap, + io::{Read, Write}, + net::{Shutdown, TcpStream}, + os::unix::prelude::{AsRawFd, RawFd}, + rc::Rc, + sync::{Arc, Mutex, Weak}, +}; + +use anyhow::{anyhow, bail, Result}; +use log::error; +use sscanf::scanf; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + use crate::{ console::console_select, error::VncError, @@ -26,19 +42,6 @@ use crate::{ MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, }, }; -use anyhow::{anyhow, bail, Result}; -use log::error; -use sscanf::scanf; -use std::{ - cell::RefCell, - cmp, - collections::HashMap, - io::{Read, Write}, - net::{Shutdown, TcpStream}, - os::unix::prelude::{AsRawFd, RawFd}, - rc::Rc, - sync::{Arc, Mutex, Weak}, -}; use util::{ bitmap::Bitmap, loop_context::{ @@ -46,7 +49,6 @@ use util::{ NotifierOperation, }, }; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; pub const APP_NAME: &str = "stratovirt"; const MAX_RECVBUF_LEN: usize = 1024; diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index 7281f1abf..6f4c5699b 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{cmp, mem}; + use crate::{ pixman::{bytes_per_pixel, get_image_data, get_image_stride}, vnc::{ @@ -17,7 +19,6 @@ use crate::{ write_pixel, }, }; -use std::{cmp, mem}; use util::pixman::pixman_image_t; /// Size of subrectangle. diff --git a/ui/src/vnc/encoding/mod.rs b/ui/src/vnc/encoding/mod.rs index 6cabbc97c..1cb6fe98d 100644 --- a/ui/src/vnc/encoding/mod.rs +++ b/ui/src/vnc/encoding/mod.rs @@ -11,5 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod enc_hextile; + #[cfg(test)] mod test_hextile_image_data; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index a0cf1670e..9b03c7c8b 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -16,6 +16,19 @@ pub mod client_io; pub mod encoding; pub mod server_io; +use std::{ + cmp, + collections::HashMap, + net::TcpListener, + ptr, + sync::{Arc, Mutex}, + thread, +}; + +use anyhow::{anyhow, Result}; +use core::time; +use once_cell::sync::Lazy; + use crate::{ console::{ graphic_hardware_update, register_display, DisplayChangeListener, @@ -38,22 +51,11 @@ use crate::{ server_io::{make_server_config, VncConnHandler, VncServer, VncSurface}, }, }; -use anyhow::{anyhow, Result}; -use core::time; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, qmp::qmp_schema::{VncClientInfo, VncInfo}, }; -use once_cell::sync::Lazy; -use std::{ - cmp, - collections::HashMap, - net::TcpListener, - ptr, - sync::{Arc, Mutex}, - thread, -}; use util::{ bitmap::Bitmap, loop_context::EventNotifierHelper, diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index e6c0b7d3a..7c0797e6b 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -10,6 +10,21 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{ + cell::RefCell, + cmp, + collections::HashMap, + net::{SocketAddr, TcpListener, TcpStream}, + os::unix::prelude::{AsRawFd, RawFd}, + ptr, + rc::Rc, + sync::{Arc, Mutex, Weak}, +}; + +use anyhow::{anyhow, Result}; +use log::{error, info}; +use vmm_sys_util::epoll::EventSet; + use crate::{ console::{DisplayChangeListener, DisplayMouse}, error::VncError, @@ -26,22 +41,10 @@ use crate::{ VNC_BITMAP_WIDTH, VNC_SERVERS, }, }; -use anyhow::{anyhow, Result}; -use log::{error, info}; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, }; -use std::{ - cell::RefCell, - cmp, - collections::HashMap, - net::{SocketAddr, TcpListener, TcpStream}, - os::unix::prelude::{AsRawFd, RawFd}, - ptr, - rc::Rc, - sync::{Arc, Mutex, Weak}, -}; use util::{ bitmap::Bitmap, loop_context::{ @@ -49,7 +52,6 @@ use util::{ }, pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_t}, }; -use vmm_sys_util::epoll::EventSet; const CONNECTION_LIMIT: usize = 1; diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 2407fcd3a..915cdf3e1 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -13,11 +13,12 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{fence, Ordering}; -use super::{AioCb, AioContext, AioEvent, OpCode, Result}; use anyhow::bail; use kvm_bindings::__IncompleteArrayField; use vmm_sys_util::eventfd::EventFd; +use super::{AioCb, AioContext, AioEvent, OpCode, Result}; + const IOCB_FLAG_RESFD: u32 = 1; #[allow(dead_code)] const IOCB_FLAG_IOPRIO: u32 = 1 << 1; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index b88cc1c9d..020f815b0 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -14,6 +14,8 @@ mod libaio; mod raw; mod uring; +pub use raw::*; + use std::clone::Clone; use std::io::Write; use std::os::unix::io::RawFd; @@ -21,18 +23,17 @@ use std::sync::atomic::{AtomicI64, AtomicU32, AtomicU64, Ordering}; use std::sync::Arc; use std::{cmp, str::FromStr}; +use anyhow::{anyhow, bail, Context, Result}; use libc::c_void; use log::{error, warn}; use serde::{Deserialize, Serialize}; +use uring::IoUringContext; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; use crate::num_ops::{round_down, round_up}; use crate::unix::host_page_size; -use anyhow::{anyhow, bail, Context, Result}; use libaio::LibaioContext; -pub use raw::*; -use uring::IoUringContext; type CbList = List>; type CbNode = Node>; @@ -758,10 +759,12 @@ pub fn iovec_write_zero(iovec: &[Iovec]) { #[cfg(test)] mod tests { - use super::*; use std::os::unix::prelude::AsRawFd; + use vmm_sys_util::tempfile::TempFile; + use super::*; + fn perform_sync_rw( fsize: usize, offset: usize, diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index fa2f0185a..8db7e7788 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -10,13 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use super::Iovec; +use std::os::unix::io::RawFd; + use libc::{ c_int, c_void, fallocate, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE, FALLOC_FL_ZERO_RANGE, }; use log::error; -use std::os::unix::io::RawFd; + +use super::Iovec; pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { let mut ret; diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index b413641a4..4ae0dc442 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -10,11 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use libc; use std::os::unix::io::AsRawFd; use anyhow::{bail, Context}; use io_uring::{opcode, squeue, types, IoUring}; +use libc; use vmm_sys_util::eventfd::EventFd; use super::{AioCb, AioContext, AioEvent, OpCode, Result}; diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index d0befdb2d..8b9c5c3bc 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -17,9 +17,10 @@ use std::env; use std::io::Write; use std::process; -use crate::UtilError; use anyhow::{anyhow, bail, Result}; +use crate::UtilError; + const PREFIX_CHARS_SHORT: &str = "-"; const PREFIX_CHARS_LONG: &str = "-"; const PREFIX_OPT_LONG: &str = "--"; @@ -705,9 +706,10 @@ fn split_arg(arg: &str, prefix_chars: &str) -> String { #[cfg(test)] mod tests { - use super::*; use std::io::{Cursor, Read, Seek, SeekFrom}; + use super::*; + #[derive(Default)] struct TestBuffer { inner: Cursor>, diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 5f4c60e88..fed47e0fb 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -13,9 +13,10 @@ use std::cmp::Ord; use std::mem::size_of; -use crate::UtilError; use anyhow::{anyhow, Context, Result}; +use crate::UtilError; + /// This struct is used to offer bitmap. pub struct Bitmap { /// The data to restore bit information. diff --git a/util/src/daemonize.rs b/util/src/daemonize.rs index 3453913eb..a9fe6af46 100644 --- a/util/src/daemonize.rs +++ b/util/src/daemonize.rs @@ -39,9 +39,10 @@ use std::os::unix::io::RawFd; use std::path::Path; use std::process::exit; -use crate::UtilError; use anyhow::{anyhow, Result}; +use crate::UtilError; + /// Write process id to pid file. fn create_pid_file(path: &str) -> Result<()> { let pid: u32 = std::process::id(); diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 3895dcc43..12bc60182 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -12,10 +12,11 @@ use std::mem::size_of; -use crate::UtilError; use anyhow::{anyhow, Context, Result}; use byteorder::{BigEndian, ByteOrder}; +use crate::UtilError; + pub const CLK_PHANDLE: u32 = 1; pub const GIC_PHANDLE: u32 = 2; pub const GIC_ITS_PHANDLE: u32 = 3; diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index c2196c357..1d39fb14f 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -15,13 +15,13 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::Arc; use std::time::{Duration, Instant}; +use anyhow::Result; use log::error; use vmm_sys_util::eventfd::EventFd; use crate::clock::get_current_time; use crate::loop_context::EventLoopContext; use crate::time::NANOSECONDS_PER_SECOND; -use anyhow::Result; /// Used to improve the accuracy of bucket level. const ACCURACY_SCALE: u64 = 1000; diff --git a/util/src/lib.rs b/util/src/lib.rs index 4d637a171..6ef4d478c 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -39,12 +39,16 @@ pub mod time; pub mod trace; pub mod unix; pub mod v4l2; + pub use anyhow::Result; + pub use error::UtilError; + +use std::{any::Any, sync::Mutex}; + use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; use log::debug; use once_cell::sync::Lazy; -use std::{any::Any, sync::Mutex}; use vmm_sys_util::terminal::Terminal; /// Read the program version in `Cargo.toml` and concat with git commit id. diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index fa5547f91..5fe1263b4 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -11,12 +11,15 @@ // See the Mulan PSL v2 for more details. use std::collections::BTreeMap; +use std::fmt; +use std::fmt::Debug; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; +use anyhow::{anyhow, Context, Result}; use libc::{c_void, read, EFD_NONBLOCK}; use log::{error, warn}; use nix::errno::Errno; @@ -29,9 +32,6 @@ use vmm_sys_util::eventfd::EventFd; use crate::clock::{get_current_time, ClockState}; use crate::UtilError; -use anyhow::{anyhow, Context, Result}; -use std::fmt; -use std::fmt::Debug; const READY_EVENT_MAX: usize = 256; const AIO_PRFETCH_CYCLE_TIME: usize = 100; @@ -669,10 +669,12 @@ pub fn read_fd(fd: RawFd) -> u64 { #[cfg(test)] mod test { - use super::*; use std::os::unix::io::{AsRawFd, RawFd}; + use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + use super::*; + impl EventLoopContext { fn check_existence(&self, fd: RawFd) -> Option { let events_map = self.events.read().unwrap(); diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 7a0992df9..5224aabc1 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -77,10 +77,9 @@ //! ``` //! This programe will be trapped. -use anyhow::bail; +use anyhow::{bail, Result}; use crate::offset_of; -use anyhow::Result; // BPF Instruction classes /// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/bpf_common.h#L7 diff --git a/util/src/syscall.rs b/util/src/syscall.rs index c2d7b16c1..5a6f0ea57 100644 --- a/util/src/syscall.rs +++ b/util/src/syscall.rs @@ -10,11 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::bail; +use anyhow::{bail, Result}; use libc::{c_void, syscall, SYS_mbind}; -use anyhow::Result; - /// This function set memory policy for host NUMA node memory range. /// /// * Arguments diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs index 64ac2bba1..e55669d7c 100644 --- a/util/src/test_helper.rs +++ b/util/src/test_helper.rs @@ -10,10 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use once_cell::sync::{Lazy, OnceCell}; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; +use once_cell::sync::{Lazy, OnceCell}; + #[derive(Default, Clone, Copy)] struct MsixMsg { addr: u64, diff --git a/util/src/trace.rs b/util/src/trace.rs index 36b5e3ca2..dee3acb01 100644 --- a/util/src/trace.rs +++ b/util/src/trace.rs @@ -16,12 +16,11 @@ use std::io::{prelude::Write, BufRead, BufReader}; use std::ops::Deref; use std::sync::Arc; +use anyhow::{Context, Result}; use arc_swap::ArcSwap; use log::error; use once_cell::sync::Lazy; -use anyhow::{Context, Result}; - static TRACE_MARKER_FD: Lazy> = Lazy::new(open_trace_marker); static TRACE_EVENTS: Lazy>> = Lazy::new(|| ArcSwap::new(Arc::new(HashSet::new()))); diff --git a/util/src/unix.rs b/util/src/unix.rs index 938bf8de8..a5f406f7e 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::anyhow; use std::fs::File; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; @@ -18,6 +17,7 @@ use std::os::unix::net::{UnixListener, UnixStream}; use std::path::Path; use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; +use anyhow::{anyhow, bail, Context, Result}; use libc::{ c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, CMSG_LEN, CMSG_SPACE, MSG_NOSIGNAL, MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, @@ -25,7 +25,6 @@ use libc::{ use log::error; use crate::UtilError; -use anyhow::{bail, Context, Result}; /// This function returns the caller's thread ID(TID). pub fn gettid() -> u64 { diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 05d9a7ab8..596a22945 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -11,11 +11,11 @@ // See the Mulan PSL v2 for more details. pub mod error; -pub use error::VfioError; mod vfio_dev; mod vfio_pci; +pub use error::VfioError; pub use vfio_dev::{ VfioContainer, VfioDevice, VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, @@ -28,11 +28,12 @@ use std::collections::HashMap; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; -use hypervisor::kvm::KVM_FDS; use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO}; use kvm_ioctls::DeviceFd; use log::error; use once_cell::sync::Lazy; + +use hypervisor::kvm::KVM_FDS; use vfio_dev::VfioGroup; pub static KVM_DEVICE_FD: Lazy> = Lazy::new(create_kvm_vfio_device); diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 5bac11d56..0e463104b 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use log::warn; use std::collections::HashMap; use std::ffi::CString; use std::fs::{File, OpenOptions}; @@ -20,11 +19,12 @@ use std::os::unix::prelude::FileExt; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, Weak}; -use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use kvm_bindings::{ kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, }; +use log::warn; use vfio_bindings::bindings::vfio; use vmm_sys_util::ioctl::{ ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, @@ -33,7 +33,7 @@ use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; use crate::VfioError; -use anyhow::{anyhow, bail, Context, Result}; +use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; /// Refer to VFIO in https://github.com/torvalds/linux/blob/master/include/uapi/linux/vfio.h const IOMMU_GROUP: &str = "iommu_group"; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index a466fa46b..be7ab0cc6 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -16,10 +16,17 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use crate::VfioError; -use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; +use log::error; +use vfio_bindings::bindings::vfio; +use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::ioctl::ioctl_with_mut_ref; + +use crate::vfio_dev::*; +use crate::VfioError; +use crate::{CONTAINERS, GROUPS}; +use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps}; #[cfg(target_arch = "aarch64")] use devices::pci::config::SECONDARY_BUS_NUM; use devices::pci::config::{ @@ -39,14 +46,7 @@ use devices::pci::{ }; use devices::{Device, DeviceBase}; use hypervisor::kvm::{MsiVector, KVM_FDS}; -use log::error; use util::unix::host_page_size; -use vfio_bindings::bindings::vfio; -use vmm_sys_util::eventfd::EventFd; -use vmm_sys_util::ioctl::ioctl_with_mut_ref; - -use crate::vfio_dev::*; -use crate::{CONTAINERS, GROUPS}; const PCI_NUM_BARS: u8 = 6; const PCI_ROM_SLOT: u8 = 6; diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index 044021db0..d80d02e87 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -20,6 +20,7 @@ use std::{ use anyhow::{Context, Result}; use log::error; +use vmm_sys_util::epoll::EventSet; use super::cmdline::FsConfig; use super::fs::set_rlimit_nofile; @@ -29,7 +30,6 @@ use machine_manager::{event_loop::EventLoop, temp_cleaner::TempCleaner}; use util::loop_context::{ EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use vmm_sys_util::epoll::EventSet; /// The vhost-user filesystem device contains virtio fs device and the vhost-user /// server which can be connected with the vhost-user client in StratoVirt. diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 3fbc2d1c0..10e164fed 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -28,6 +28,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use log::error; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::fs::FileSystem; use super::fuse_req::FuseReq; @@ -44,7 +45,6 @@ use virtio::{ Queue, QueueConfig, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, }; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; const VIRTIO_FS_VRING_NO_FD_MASK: usize = 0x1 << 8; diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index dbb581602..31df8fd81 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -20,11 +20,18 @@ use std::{ time::Duration, }; +use anyhow::{anyhow, Context, Result}; +use log::{error, warn}; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; + +use crate::{ + error::*, read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, + VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, +}; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; -use anyhow::{anyhow, Context, Result}; -use log::{error, warn}; use machine_manager::{ config::{BalloonConfig, DEFAULT_VIRTQUEUE_SIZE}, event, @@ -43,13 +50,6 @@ use util::{ seccomp::BpfRule, unix::host_page_size, }; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; - -use crate::{ - error::*, read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, - VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, -}; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; const VIRTIO_BALLOON_F_REPORTING: u32 = 5; diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 92941d6fb..d4130a12e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1330,13 +1330,15 @@ impl VirtioTrace for AioCompleteCb {} #[cfg(test)] mod tests { + use std::sync::atomic::{AtomicU32, Ordering}; + use std::{thread, time::Duration}; + + use vmm_sys_util::tempfile::TempFile; + use super::*; use crate::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use machine_manager::config::{IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; - use std::sync::atomic::{AtomicU32, Ordering}; - use std::{thread, time::Duration}; - use vmm_sys_util::tempfile::TempFile; const QUEUE_NUM_BLK: usize = 1; const CONFIG_SPACE_SIZE: usize = 60; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 4c2462727..47f5aa040 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -21,6 +21,20 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use crate::{ + check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, + ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, + VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_F_EDID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, + VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, +}; use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -46,21 +60,6 @@ use util::pixman::{ virtio_gpu_unref_resource_callback, }; -use crate::{ - check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, - ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, - VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, - VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_F_EDID, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, - VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, -}; - /// Number of virtqueues const QUEUE_NUM_GPU: usize = 2; /// Display changed event diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 3cb41c18c..86e4fbbb6 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -18,6 +18,16 @@ use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use anyhow::{bail, Context, Result}; +use log::error; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; + +use crate::error::VirtioError; +use crate::{ + ElemIovec, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, +}; use address_space::AddressSpace; use machine_manager::{ config::{RngConfig, DEFAULT_VIRTQUEUE_SIZE}, @@ -25,6 +35,7 @@ use machine_manager::{ event_loop::{register_event_helper, unregister_event_helper}, }; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; use util::aio::raw_read; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; @@ -32,18 +43,6 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use log::error; -use migration_derive::{ByteCode, Desc}; -use vmm_sys_util::epoll::EventSet; -use vmm_sys_util::eventfd::EventFd; - -use crate::error::VirtioError; -use crate::{ - ElemIovec, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, -}; -use anyhow::{bail, Context, Result}; - const QUEUE_NUM_RNG: usize = 1; const RNG_SIZE_MAX: u32 = 1 << 20; @@ -353,17 +352,17 @@ impl VirtioTrace for RngHandler {} #[cfg(test)] mod tests { - use super::*; - use crate::*; - use std::io::Write; use std::mem::size_of; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; + use vmm_sys_util::tempfile::TempFile; + + use super::*; + use crate::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use machine_manager::config::{RngConfig, DEFAULT_VIRTQUEUE_SIZE}; - use vmm_sys_util::tempfile::TempFile; const VIRTQ_DESC_F_NEXT: u16 = 0x01; const VIRTQ_DESC_F_WRITE: u16 = 0x02; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 50fa0165a..e5c25d93d 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -27,9 +27,26 @@ pub mod device; pub mod error; +pub mod vhost; + mod queue; mod transport; -pub mod vhost; + +pub use device::balloon::*; +pub use device::block::{Block, BlockState, VirtioBlkConfig}; +#[cfg(not(target_env = "musl"))] +pub use device::gpu::*; +pub use device::net::*; +pub use device::rng::{Rng, RngState}; +pub use device::scsi_cntlr as ScsiCntlr; +pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; +pub use error::VirtioError; +pub use error::*; +pub use queue::*; +pub use transport::virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; +pub use transport::virtio_pci::VirtioPciDevice; +pub use vhost::kernel as VhostKern; +pub use vhost::user as VhostUser; use std::cmp; use std::io::Write; @@ -48,22 +65,6 @@ use util::aio::{mem_to_buf, Iovec}; use util::num_ops::{read_u32, write_u32}; use util::AsAny; -pub use device::balloon::*; -pub use device::block::{Block, BlockState, VirtioBlkConfig}; -#[cfg(not(target_env = "musl"))] -pub use device::gpu::*; -pub use device::net::*; -pub use device::rng::{Rng, RngState}; -pub use device::scsi_cntlr as ScsiCntlr; -pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; -pub use error::VirtioError; -pub use error::*; -pub use queue::*; -pub use transport::virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; -pub use transport::virtio_pci::VirtioPciDevice; -pub use vhost::kernel as VhostKern; -pub use vhost::user as VhostUser; - /// Check if the bit of features is configured. pub fn virtio_has_feature(feature: u64, fbit: u32) -> bool { feature & (1 << fbit) != 0 diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index 5f58f7a39..b49280730 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -12,12 +12,14 @@ mod split; -use address_space::{AddressSpace, GuestAddress, RegionCache}; -use anyhow::{bail, Result}; +pub use split::*; + use std::sync::Arc; + +use anyhow::{bail, Result}; use vmm_sys_util::eventfd::EventFd; -pub use split::*; +use address_space::{AddressSpace, GuestAddress, RegionCache}; /// Split Virtqueue. pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 88b68e072..f166c84c8 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -17,10 +17,8 @@ use std::ops::{Deref, DerefMut}; use std::sync::atomic::{fence, AtomicBool, Ordering}; use std::sync::Arc; -use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; -use util::byte_code::ByteCode; use super::{ checked_offset_mem, ElemIovec, Element, VringOps, INVALID_VECTOR_NUM, VIRTQ_DESC_F_INDIRECT, @@ -29,6 +27,8 @@ use super::{ use crate::{ report_virtio_error, virtio_has_feature, VirtioError, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, }; +use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; +use util::byte_code::ByteCode; /// When host consumes a buffer, don't interrupt the guest. const VRING_AVAIL_F_NO_INTERRUPT: u16 = 1; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 52d1ea292..997ed0aa8 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -13,26 +13,26 @@ use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; -use crate::error::VirtioError; -use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; -use devices::{Device, DeviceBase}; use log::{error, warn}; -#[cfg(target_arch = "x86_64")] -use machine_manager::config::{BootSource, Param}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; -use util::byte_code::ByteCode; use vmm_sys_util::eventfd::EventFd; +use crate::error::VirtioError; use crate::{ virtio_has_feature, Queue, VirtioBaseState, VirtioDevice, VirtioInterrupt, VirtioInterruptType, CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, NOTIFY_REG_OFFSET, QUEUE_TYPE_PACKED_VRING, VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, }; -use anyhow::{anyhow, bail, Context, Result}; +use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; +use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use devices::{Device, DeviceBase}; +#[cfg(target_arch = "x86_64")] +use machine_manager::config::{BootSource, Param}; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; /// Registers of virtio-mmio device refer to Virtio Spec. /// Magic value - Read Only. @@ -585,13 +585,12 @@ impl MigrationHook for VirtioMmioDevice { #[cfg(test)] mod tests { - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use super::*; use crate::{ check_config_space_rw, read_config_default, VirtioBase, QUEUE_TYPE_SPLIT_VRING, VIRTIO_TYPE_BLOCK, }; + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 130862c01..181a668fb 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -15,34 +15,15 @@ use std::mem::size_of; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use anyhow::{anyhow, bail, Context}; use byteorder::{ByteOrder, LittleEndian}; -use devices::pci::config::{ - RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, - PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, - STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, -}; use log::{debug, error, warn}; -use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; -use migration_derive::{ByteCode, Desc}; - -use devices::pci::msix::{update_dev_id, MsixState}; -use devices::pci::{ - config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, - ranges_overlap, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, -}; -use devices::{Device, DeviceBase}; -use util::byte_code::ByteCode; -use util::num_ops::{read_data_u32, write_data_u32}; -use util::offset_of; use vmm_sys_util::eventfd::EventFd; use crate::{ virtio_has_feature, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, VirtioInterrupt, VirtioInterruptType, }; - use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, @@ -50,6 +31,23 @@ use crate::{ VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_CONSOLE, VIRTIO_TYPE_FS, VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; +use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use devices::pci::config::{ + RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, + PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, + STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, +}; +use devices::pci::msix::{update_dev_id, MsixState}; +use devices::pci::{ + config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, + ranges_overlap, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, +}; +use devices::{Device, DeviceBase}; +use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; +use migration_derive::{ByteCode, Desc}; +use util::byte_code::ByteCode; +use util::num_ops::{read_data_u32, write_data_u32}; +use util::offset_of; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -1282,15 +1280,15 @@ impl MigrationHook for VirtioPciDevice { mod tests { use std::sync::{Arc, Mutex}; + use vmm_sys_util::eventfd::EventFd; + + use super::*; + use crate::{Result as VirtioResult, VirtioBase}; use address_space::{AddressSpace, GuestAddress, HostMemMapping}; use devices::pci::{ config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC}, le_read_u16, }; - use vmm_sys_util::eventfd::EventFd; - - use super::*; - use crate::{Result as VirtioResult, VirtioBase}; const VIRTIO_DEVICE_TEST_TYPE: u32 = 1; const VIRTIO_DEVICE_QUEUE_NUM: usize = 2; diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 19638e1a8..0058c2ca4 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -23,14 +23,8 @@ use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use address_space::{ - AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, -}; +use anyhow::{anyhow, Context, Result}; use log::{debug, error}; -use util::byte_code::ByteCode; -use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; @@ -39,7 +33,13 @@ use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_ use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; use super::{VhostNotify, VhostOps}; use crate::VirtioError; -use anyhow::{anyhow, Context, Result}; +use address_space::{ + AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, +}; +use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; /// Refer to VHOST_VIRTIO in /// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 98b56646f..4f39a9627 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -14,13 +14,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; -use address_space::AddressSpace; use anyhow::{anyhow, bail, Context, Result}; -use machine_manager::config::NetworkInterfaceConfig; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; -use util::tap::Tap; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; @@ -36,6 +30,12 @@ use crate::{ VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; +use address_space::AddressSpace; +use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::byte_code::ByteCode; +use util::loop_context::EventNotifierHelper; +use util::tap::Tap; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; @@ -414,10 +414,11 @@ impl VirtioDevice for Net { #[cfg(test)] mod tests { + use std::fs::File; + use super::*; use address_space::*; use machine_manager::config::DEFAULT_VIRTQUEUE_SIZE; - use std::fs::File; const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index fb0e21c4c..9bde8ccd5 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -15,11 +15,17 @@ use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; +use byteorder::{ByteOrder, LittleEndian}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; +use super::super::{VhostNotify, VhostOps}; +use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; +use crate::{ + check_config_space_rw, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, + VirtioInterruptType, VIRTIO_TYPE_VSOCK, +}; use address_space::AddressSpace; -use byteorder::{ByteOrder, LittleEndian}; use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -27,13 +33,6 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; -use crate::{ - check_config_space_rw, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_TYPE_VSOCK, -}; - /// Number of virtqueues. const QUEUE_NUM_VSOCK: usize = 3; /// Backend vhost-vsock device path. diff --git a/virtio/src/vhost/mod.rs b/virtio/src/vhost/mod.rs index 03d65f617..fdd8d7f96 100644 --- a/virtio/src/vhost/mod.rs +++ b/virtio/src/vhost/mod.rs @@ -15,10 +15,10 @@ pub mod user; use std::sync::{Arc, Mutex}; +use anyhow::Result; use vmm_sys_util::eventfd::EventFd; use super::{Queue, QueueConfig}; -use anyhow::Result; /// Vhost vring call notify structure. pub struct VhostNotify { diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 95e427298..bfbc6fb9e 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -10,12 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Context, Result}; use std::sync::{Arc, Mutex}; -use address_space::AddressSpace; -use machine_manager::config::BlkDevConfig; -use util::byte_code::ByteCode; +use anyhow::{anyhow, bail, Context, Result}; use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; @@ -30,6 +27,9 @@ use crate::{ VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; +use address_space::AddressSpace; +use machine_manager::config::BlkDevConfig; +use util::byte_code::ByteCode; pub struct Block { /// Virtio device base property. diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 48e87350f..4253c9cb0 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -19,17 +19,8 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use anyhow::{bail, Context, Result}; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; - -use address_space::{ - AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, -}; use log::{error, info, warn}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; -use util::loop_context::{ - gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; -use util::unix::do_mmap; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::super::VhostOps; use super::message::{ @@ -40,6 +31,14 @@ use super::sock::VhostUserSock; use crate::device::block::VirtioBlkConfig; use crate::VhostUser::message::VhostUserConfig; use crate::{virtio_has_feature, Queue, QueueConfig}; +use address_space::{ + AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, +}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; +use util::loop_context::{ + gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; +use util::unix::do_mmap; /// Vhost supports multiple queue pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index d11bc2777..6c7fcdaee 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -25,6 +25,10 @@ use anyhow::{anyhow, Context, Result}; use log::error; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use super::super::super::{VirtioDevice, VIRTIO_TYPE_FS}; +use super::super::{VhostNotify, VhostOps}; +use super::{VhostBackendType, VhostUserClient}; +use crate::{read_config_default, VirtioBase, VirtioInterrupt, VirtioInterruptType}; use address_space::AddressSpace; use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -33,11 +37,6 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use super::super::super::{VirtioDevice, VIRTIO_TYPE_FS}; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackendType, VhostUserClient}; -use crate::{read_config_default, VirtioBase, VirtioInterrupt, VirtioInterruptType}; - #[derive(Copy, Clone)] #[repr(C, packed)] struct VirtioFsConfig { diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 31699f7c3..85d79c134 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -10,15 +10,16 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::bail; use std::mem::size_of; + +use anyhow::{bail, Result}; + /// The version of the protocol StratoVirt support. pub const VHOST_USER_VERSION: u32 = 0x1; pub const VHOST_USER_MSG_MAX_SIZE: usize = 0x1000; pub const MAX_ATTACHED_FD_ENTRIES: usize = 32; pub const VHOST_USER_F_PROTOCOL_FEATURES: u32 = 30; pub const VHOST_USER_MAX_CONFIG_SIZE: u32 = 256; -use anyhow::Result; /// Type of requests sending from vhost user device to the userspace process. #[repr(u32)] diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 02f4c8af1..46096dcf2 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -10,17 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod fs; + mod block; mod client; mod message; mod net; mod sock; -pub use block::Block; -use client::VhostUserClient; -pub use net::Net; -pub mod fs; pub use self::client::*; pub use self::fs::*; pub use self::message::*; pub use self::sock::*; +pub use block::Block; +pub use net::Net; + +use client::VhostUserClient; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 4bf04b07c..74b5698b5 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -13,11 +13,7 @@ use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; -use address_space::AddressSpace; -use machine_manager::config::NetworkInterfaceConfig; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; +use anyhow::{anyhow, Context, Result}; use vmm_sys_util::eventfd::EventFd; use super::super::VhostOps; @@ -31,7 +27,11 @@ use crate::{ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; -use anyhow::{anyhow, Context, Result}; +use address_space::AddressSpace; +use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use util::byte_code::ByteCode; +use util::loop_context::EventNotifierHelper; /// Number of virtqueues. const QUEUE_NUM_NET: usize = 2; diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index 5e1d25442..f4bcd27b2 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -13,12 +13,11 @@ use std::mem::size_of; use std::os::unix::io::RawFd; +use anyhow::{bail, Result}; use libc::{c_void, iovec}; -use util::unix::UnixSock; use super::message::{MAX_ATTACHED_FD_ENTRIES, VHOST_USER_MSG_MAX_SIZE}; -use anyhow::bail; -use anyhow::Result; +use util::unix::UnixSock; #[derive(Clone)] pub struct VhostUserSock { -- Gitee From 4a99fb6dd9e6020a541415f42f2235f96dccf1cb Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 10:34:52 +0800 Subject: [PATCH 1284/2187] add basic rustfmt config Enable two optional features: - Use field initialize shorthand if possible. - Use 2021 edition Signed-off-by: yezengruan --- .rustfmt.toml | 2 ++ block_backend/src/qcow2/refcount.rs | 2 +- tests/mod_test/src/libdriver/usb.rs | 4 ++-- tests/mod_test/src/libdriver/virtio.rs | 2 +- tests/mod_test/src/libdriver/virtio_gpu.rs | 2 +- tests/mod_test/tests/memory_test.rs | 2 +- tests/mod_test/tests/scsi_test.rs | 20 ++++++++++---------- tests/mod_test/tests/virtiofs_test.rs | 2 +- tests/mod_test/tests/vnc_test.rs | 2 +- 9 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 000000000..466b81b3f --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,2 @@ +use_field_init_shorthand = true +edition = "2021" diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 4d414905f..c71e56aaa 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -624,7 +624,7 @@ mod test { version: 3, backing_file_offset: 0, backing_file_size: 0, - cluster_bits: cluster_bits, + cluster_bits, size: 1 << img_bits, crypt_method: 0, l1_size: 1 << (img_bits - (cluster_bits * 2 - 3)), diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 28a6833c4..91e870898 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -1982,7 +1982,7 @@ impl TestXhciPciDevice { request_type: USB_INTERFACE_IN_REQUEST, request: USB_REQUEST_GET_INTERFACE, value: 0, - index: index, + index, length: buf_len, }; self.queue_device_request(slot_id, &device_req); @@ -1994,7 +1994,7 @@ impl TestXhciPciDevice { request_type: USB_INTERFACE_OUT_REQUEST, request: USB_REQUEST_SET_INTERFACE, value: v, - index: index, + index, length: buf_len, }; self.queue_device_request(slot_id, &device_req); diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 9315f3cfe..4348fdf8c 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -499,7 +499,7 @@ impl TestVirtQueue { let desc_elem = VringDesc { addr: data, - len: len, + len, flags, next: 0, }; diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index 3bf004514..66a32757d 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -399,7 +399,7 @@ impl TestVirtioGpu { Self { device: Rc::new(RefCell::new(TestVirtioPciDev::new(pci_bus))), allocator, - state: state, + state, ctrl_q: Rc::new(RefCell::new(TestVirtQueue::new())), cursor_q: Rc::new(RefCell::new(TestVirtQueue::new())), } diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index c9b553986..e26e00304 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -78,7 +78,7 @@ impl MemoryTest { MemoryTest { state: test_state, - allocator: allocator, + allocator, } } } diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 743d33ddc..548a90642 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -338,9 +338,9 @@ impl VirtioScsiTest { data_out: Some(write_data.clone()), data_in_length: 0, expect_response: VIRTIO_SCSI_S_OK, - expect_status: expect_status, + expect_status, expect_result_data: None, - expect_sense: expect_sense, + expect_sense, }; self.scsi_cdb_test(cdb_test_args); @@ -1869,8 +1869,8 @@ fn aio_model_test() { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, image_path: image_path.clone(), - target: target, - lun: lun, + target, + lun, read_only: false, direct: false, aio: TestAioType::AioIOUring, @@ -1884,8 +1884,8 @@ fn aio_model_test() { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, image_path: image_path.clone(), - target: target, - lun: lun, + target, + lun, read_only: false, direct: true, aio: TestAioType::AioIOUring, @@ -1903,8 +1903,8 @@ fn aio_model_test() { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, image_path: image_path.clone(), - target: target, - lun: lun, + target, + lun, read_only: false, direct: false, aio: TestAioType::AioOff, @@ -1921,8 +1921,8 @@ fn aio_model_test() { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, image_path: image_path.clone(), - target: target, - lun: lun, + target, + lun, read_only: false, direct: true, aio: TestAioType::AioNative, diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 654a5021e..baf7c94dd 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -141,7 +141,7 @@ impl VirtioFsTest { VirtioFsTest { device: dev, state: test_state, - allocator: allocator, + allocator, queues, } } diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index a8e6d6cfa..6bb76709d 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -799,7 +799,7 @@ fn test_client_cut_event(test_state: Rc>, port: u16) -> Resul pad0: 0, pad1: 0, length: text.len() as u32, - text: text, + text, }; assert!(vnc_client.test_send_client_cut(client_cut).is_ok()); // Send a qmp to query vnc client state. -- Gitee From add787984be5acb0d1cf3a1173f7a94d77c5a9e1 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 8 Aug 2023 14:54:34 +0800 Subject: [PATCH 1285/2187] fix some clippy warnings of newer rust version Signed-off-by: yezengruan --- block_backend/src/qcow2/mod.rs | 10 +++---- block_backend/src/qcow2/refcount.rs | 8 +++--- block_backend/src/qcow2/table.rs | 4 +-- boot_loader/src/x86_64/direct_boot/mod.rs | 2 +- boot_loader/src/x86_64/standard_boot/elf.rs | 2 +- boot_loader/src/x86_64/standard_boot/mod.rs | 2 +- .../src/interrupt_controller/aarch64/gicv2.rs | 2 +- devices/src/legacy/fwcfg.rs | 4 +-- devices/src/legacy/pl011.rs | 6 ++--- machine/src/micro_vm/mod.rs | 4 +-- machine/src/standard_vm/aarch64/mod.rs | 26 +++++++++---------- .../src/standard_vm/aarch64/pci_host_root.rs | 2 +- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 2 +- util/src/aio/mod.rs | 4 +-- 15 files changed, 38 insertions(+), 42 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index c4c915b7a..bb7e50a30 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -260,8 +260,8 @@ impl Qcow2Driver { } fn load_refcount_table(&mut self) -> Result<()> { - let sz = self.header.refcount_table_clusters as u64 - * (self.header.cluster_size() / ENTRY_SIZE as u64); + let sz = + self.header.refcount_table_clusters as u64 * (self.header.cluster_size() / ENTRY_SIZE); self.refcount.refcount_table = self .sync_aio .borrow_mut() @@ -1012,11 +1012,7 @@ impl Qcow2Driver { ); } - if !self - .table - .l2_table_cache - .contains_keys(l2_table_offset as u64) - { + if !self.table.l2_table_cache.contains_keys(l2_table_offset) { let l2_cluster = self.load_cluster(l2_table_offset)?; let l2_table_entry = Rc::new(RefCell::new(CacheTable::new( l2_table_offset, diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index c71e56aaa..18e26feae 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -222,7 +222,7 @@ impl RefCount { let mut i = 0; while i < clusters { let rt_idx = (first_cluster + i) >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size as u64 { + if rt_idx >= self.refcount_table_size { bail!("Invalid refcount table index {}", rt_idx); } let rb_addr = self.refcount_table[rt_idx as usize]; @@ -368,7 +368,7 @@ impl RefCount { pub fn get_refcount(&mut self, offset: u64) -> Result { let cluster = offset >> self.cluster_bits; let rt_idx = cluster >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size as u64 { + if rt_idx >= self.refcount_table_size { bail!( "Invalid refcount table index {}, refcount table size {}", rt_idx, @@ -482,7 +482,7 @@ impl RefCount { while i < clusters { // Check if it needs to extend refcount table. let rt_idx = current_index >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size as u64 { + if rt_idx >= self.refcount_table_size { self.extend_refcount_table(header, current_index)?; if current_index > self.free_cluster_index { current_index = self.free_cluster_index; @@ -497,7 +497,7 @@ impl RefCount { let rb_addr = self.refcount_table[rt_idx as usize]; if rb_addr == 0 { // Need to alloc refcount block. - self.alloc_refcount_block(rt_idx as u64, current_index)?; + self.alloc_refcount_block(rt_idx, current_index)?; current_index += 1; i = 0; continue; diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 90357cd75..44a6dff18 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -188,12 +188,12 @@ impl Qcow2Table { pub fn update_l2_table(&mut self, l2_table_entry: Rc>) -> Result<()> { let l2_entry_addr = l2_table_entry.borrow().addr; - if self.l2_table_cache.contains_keys(l2_entry_addr as u64) { + if self.l2_table_cache.contains_keys(l2_entry_addr) { self.l2_table_cache.cache_map.remove(&l2_entry_addr); } if let Some(replaced_entry) = self .l2_table_cache - .lru_replace(l2_entry_addr as u64, l2_table_entry) + .lru_replace(l2_entry_addr, l2_table_entry) { let borrowed_entry = replaced_entry.borrow(); // Flush the dirty entry. diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index f16afe4f2..3510fbbe7 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -63,7 +63,7 @@ fn load_bzimage(kernel_image: &mut File) -> Result { boot_hdr.type_of_loader = UNDEFINED_ID; if let Err(e) = boot_hdr.check_valid_kernel() { - kernel_image.seek(SeekFrom::Start(0))?; + kernel_image.rewind()?; return Err(e); } diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 46f8bc5ca..2817010ae 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -133,7 +133,7 @@ pub fn load_elf_kernel( sys_mem: &Arc, fwcfg: &mut dyn FwCfgOps, ) -> Result<()> { - kernel_image.seek(SeekFrom::Start(0))?; + kernel_image.rewind()?; let kernel_length = kernel_image.metadata().map(|m| m.len())?; let mut elf_header = Elf64Header::default(); diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 659362ad9..7944b9e12 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -66,7 +66,7 @@ fn load_kernel_image( setup_size = (setup_size + 1) << 9; let mut setup_data = vec![0_u8; setup_size as usize]; - kernel_image.seek(SeekFrom::Start(0))?; + kernel_image.rewind()?; kernel_image.read_exact(setup_data.as_mut_slice())?; let kernel_size = kernel_image.metadata().unwrap().len() - setup_size; diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index def1df699..5f3b586ba 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -215,7 +215,7 @@ impl MachineLifecycle for GICv2 { impl GICv2Access for GICv2 { fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) - & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK) | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) } diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index a323ef70c..6f3d1fe9e 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -908,7 +908,7 @@ fn read_bytes( 1 => data[0] = value as u8, 2 => BigEndian::write_u16(data, value as u16), 4 => BigEndian::write_u32(data, value as u32), - 8 => BigEndian::write_u64(data, value as u64), + 8 => BigEndian::write_u64(data, value), _ => {} } true @@ -933,7 +933,7 @@ impl SysBusDevOps for FwCfgMem { 1 => data[0] as u64, 2 => BigEndian::read_u16(data) as u64, 4 => BigEndian::read_u32(data) as u64, - 8 => BigEndian::read_u64(data) as u64, + 8 => BigEndian::read_u64(data), _ => 0, }; match offset { diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 6f4e964b0..2c15bc815 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -210,7 +210,7 @@ impl InputReceiver for PL011 { self.state.flags |= PL011_FLAG_RXFF as u32; } if self.state.read_count >= self.state.read_trigger { - self.state.int_level |= INT_RX as u32; + self.state.int_level |= INT_RX; self.interrupt(); } } @@ -245,7 +245,7 @@ impl SysBusDevOps for PL011 { self.state.flags |= PL011_FLAG_RXFE as u32; } if self.state.read_count == self.state.read_trigger - 1 { - self.state.int_level &= !(INT_RX as u32); + self.state.int_level &= !INT_RX; } self.state.rsr = c >> 8; self.interrupt(); @@ -328,7 +328,7 @@ impl SysBusDevOps for PL011 { return false; } - self.state.int_level |= INT_TX as u32; + self.state.int_level |= INT_TX; self.interrupt(); } 1 => { diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index a4685cb7a..b260732cd 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -895,7 +895,7 @@ impl MachineOps for LightMachine { .sys_mem .write( &mut fdt_vec.as_slice(), - GuestAddress(boot_cfg.fdt_addr as u64), + GuestAddress(boot_cfg.fdt_addr), fdt_vec.len() as u64, ) .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; @@ -1634,7 +1634,7 @@ impl CompileFDTHelper for LightMachine { let node = "memory"; let memory_node_dep = fdt.begin_node(node)?; fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; fdt.end_node(memory_node_dep)?; Ok(()) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index a4d6587e6..b4c7d35c1 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -279,7 +279,7 @@ impl StdMachine { .sys_mem .write( &mut locked_vm.dtb_vec.as_slice(), - GuestAddress(fdt_addr as u64), + GuestAddress(fdt_addr), locked_vm.dtb_vec.len() as u64, ) .with_context(|| "Fail to write dtb into sysmem")?; @@ -682,7 +682,7 @@ impl MachineOps for StdMachine { .sys_mem .write( &mut fdt_vec.as_slice(), - GuestAddress(boot_cfg.fdt_addr as u64), + GuestAddress(boot_cfg.fdt_addr), fdt_vec.len() as u64, ) .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; @@ -873,7 +873,7 @@ impl AcpiBuilder for StdMachine { let gtdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, >dt) .with_context(|| "Fail to add GTDT table to loader")?; - Ok(gtdt_begin as u64) + Ok(gtdt_begin) } fn build_iort_table( @@ -916,7 +916,7 @@ impl AcpiBuilder for StdMachine { let iort_begin = StdMachine::add_table_to_loader(acpi_data, loader, &iort) .with_context(|| "Fail to add IORT table to loader")?; - Ok(iort_begin as u64) + Ok(iort_begin) } fn build_spcr_table( @@ -960,7 +960,7 @@ impl AcpiBuilder for StdMachine { let spcr_begin = StdMachine::add_table_to_loader(acpi_data, loader, &spcr) .with_context(|| "Fail to add SPCR table to loader")?; - Ok(spcr_begin as u64) + Ok(spcr_begin) } fn build_dsdt_table( @@ -992,7 +992,7 @@ impl AcpiBuilder for StdMachine { let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) .with_context(|| "Fail to add DSDT table to loader")?; - Ok(dsdt_begin as u64) + Ok(dsdt_begin) } fn build_madt_table( @@ -1051,7 +1051,7 @@ impl AcpiBuilder for StdMachine { let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) .with_context(|| "Fail to add MADT table to loader")?; - Ok(madt_begin as u64) + Ok(madt_begin) } fn build_srat_cpu(&self, proximity_domain: u32, node: &NumaNode, srat: &mut AcpiTable) { @@ -1112,7 +1112,7 @@ impl AcpiBuilder for StdMachine { let srat_begin = StdMachine::add_table_to_loader(acpi_data, loader, &srat) .with_context(|| "Fail to add SRAT table to loader")?; - Ok(srat_begin as u64) + Ok(srat_begin) } fn build_pptt_table( @@ -1125,7 +1125,7 @@ impl AcpiBuilder for StdMachine { self.build_pptt_sockets(&mut pptt, &mut uid); let pptt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &pptt) .with_context(|| "Fail to add PPTT table to loader")?; - Ok(pptt_begin as u64) + Ok(pptt_begin) } } @@ -1545,7 +1545,7 @@ impl CompileFDTHelper for StdMachine { if let Some(numa_nodes) = &self.numa_nodes { for numa_index in 0..numa_nodes.len() { let numa_node = numa_nodes.get(&(numa_index as u32)); - if numa_node.unwrap().cpus.contains(&(cpu_index as u8)) { + if numa_node.unwrap().cpus.contains(&(cpu_index)) { fdt.set_property_u32("numa-node-id", numa_index as u32)?; } } @@ -1571,7 +1571,7 @@ impl CompileFDTHelper for StdMachine { let node = "memory"; let memory_node_dep = fdt.begin_node(node)?; fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; fdt.end_node(memory_node_dep)?; return Ok(()); @@ -1584,7 +1584,7 @@ impl CompileFDTHelper for StdMachine { let node = format!("memory@{:x}", mem_base); let memory_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size as u64])?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; fdt.set_property_u32("numa-node-id", id as u32)?; fdt.end_node(memory_node_dep)?; mem_base += mem_size; @@ -1694,7 +1694,7 @@ impl CompileFDTHelper for StdMachine { let distances = &node.1.distances; for i in existing_nodes.iter() { matrix.push(id as u32); - matrix.push(*i as u32); + matrix.push(*i); let dist: u32 = if id as u32 == *i { 10 } else if let Some(distance) = distances.get(i) { diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/standard_vm/aarch64/pci_host_root.rs index 5be4da5ed..cffcf4a98 100644 --- a/machine/src/standard_vm/aarch64/pci_host_root.rs +++ b/machine/src/standard_vm/aarch64/pci_host_root.rs @@ -67,7 +67,7 @@ impl PciDevOps for PciHostRoot { SUB_CLASS_CODE as usize, CLASS_CODE_HOST_BRIDGE, )?; - le_write_u16(&mut self.config.config, REVISION_ID as usize, 0)?; + le_write_u16(&mut self.config.config, REVISION_ID, 0)?; let parent_bus = self.parent_bus.upgrade().unwrap(); parent_bus diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index f4311fd37..ad583dae3 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1939,7 +1939,7 @@ impl DeviceInterface for StdMachine { .delete_snapshot(args.name.clone()); match result { Ok(snap_info) => { - Response::create_response(serde_json::to_value(&snap_info).unwrap(), None) + Response::create_response(serde_json::to_value(snap_info).unwrap(), None) } Err(e) => Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(format!( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 17f0e3af9..9a45a3465 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -601,7 +601,7 @@ impl MachineOps for StdMachine { rom_region.set_priority(10); self.sys_mem.root().add_subregion(rom_region, rom_base)?; - fd.seek(SeekFrom::Start(0))?; + fd.rewind()? } let sector_len: u32 = 1024 * 4; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 3b1eb3837..b88cc1c9d 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -735,12 +735,12 @@ pub fn iovecs_split(iovecs: Vec, mut size: u64) -> (Vec, Vec size { + if iov.iov_len > size { begin.push(Iovec::new(iov.iov_base, size)); end.push(Iovec::new(iov.iov_base + size, iov.iov_len - size)); size = 0; } else { - size -= iov.iov_len as u64; + size -= iov.iov_len; begin.push(iov); } } -- Gitee From 2b3822668810e8ddcda040423553b548d0d3904d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 8 Aug 2023 19:37:55 +0800 Subject: [PATCH 1286/2187] fix mod test unresolved import error mod test error: unresolved import `virtio::VIRTIO_GPU_FORMAT_INVALID_UNORM` Fixes: 721a74a3d68d ("vitio: Remove redundant pub declarations") Signed-off-by: yezengruan --- virtio/src/device/gpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index f11da1a9b..4c2462727 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -496,7 +496,7 @@ const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; -const _VIRTIO_GPU_FORMAT_INVALID_UNORM: u32 = 135; +pub const VIRTIO_GPU_FORMAT_INVALID_UNORM: u32 = 135; pub fn get_pixman_format(format: u32) -> Result { match format { -- Gitee From 46de53244a1723dda0ffb39aa21e80bae2b3ba41 Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Tue, 8 Aug 2023 21:53:31 +0800 Subject: [PATCH 1287/2187] Virtio: provide a new approach to get device's customized modification Add device_quirk function in virtio_device trait which can pass device-specific customized modification to the virtio_device layer. Signed-off-by: wubinfeng --- virtio/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index e5c25d93d..5c1503000 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -320,6 +320,11 @@ pub enum VirtioInterruptType { pub type VirtioInterrupt = Box, bool) -> Result<()> + Send + Sync>; +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum VirtioDeviceQuirk { + VirtioDeviceQuirkMax, +} + #[derive(Default)] pub struct VirtioBase { /// Device type @@ -499,6 +504,11 @@ pub trait VirtioDevice: Send + AsAny { self.virtio_base().device_type } + /// Get the virtio device customized modification. + fn device_quirk(&self) -> Option { + None + } + /// Get the count of virtio device queues. fn queue_num(&self) -> usize { self.virtio_base().queue_num -- Gitee From 09ea78c5981a938b9af218f239bdea3fa78c8c5e Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Tue, 8 Aug 2023 22:23:23 +0800 Subject: [PATCH 1288/2187] virtio-gpu: add new feature enable_bar0 Add new feature enable_bar0, which will add a 64*M bar0 in virtio-gpu. As of now, this bar is not useful. Signed-off-by: wubinfeng --- machine_manager/src/config/gpu.rs | 8 +++++- virtio/src/device/gpu.rs | 29 ++++++++++++++-------- virtio/src/lib.rs | 1 + virtio/src/transport/virtio_pci.rs | 40 +++++++++++++++++++++++++++--- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 0668c6a6b..196bd0ebb 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -29,6 +29,7 @@ pub struct GpuDevConfig { pub xres: u32, pub yres: u32, pub max_hostmem: u64, + pub enable_bar0: bool, } impl Default for GpuDevConfig { @@ -40,6 +41,7 @@ impl Default for GpuDevConfig { xres: 1024, yres: 768, max_hostmem: VIRTIO_GPU_MAX_HOSTMEM, + enable_bar0: false, } } } @@ -89,7 +91,8 @@ pub fn parse_gpu(gpu_config: &str) -> Result { .push("yres") .push("max_hostmem") .push("bus") - .push("addr"); + .push("addr") + .push("enable_bar0"); cmd_parser.parse(gpu_config)?; let mut gpu_cfg: GpuDevConfig = GpuDevConfig::default(); @@ -111,6 +114,9 @@ pub fn parse_gpu(gpu_config: &str) -> Result { if let Some(max_hostmem) = cmd_parser.get_value::("max_hostmem")? { gpu_cfg.max_hostmem = max_hostmem; } + if let Some(enable_bar0) = cmd_parser.get_value::("enable_bar0")? { + gpu_cfg.enable_bar0 = enable_bar0; + } gpu_cfg.check()?; Ok(gpu_cfg) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 47f5aa040..3301bb920 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -23,17 +23,17 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, - ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, - VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, - VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, - VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, - VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_F_EDID, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, - VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, + ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioDeviceQuirk, VirtioError, + VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, + VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, + VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, + VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; @@ -1465,6 +1465,13 @@ impl VirtioDevice for Gpu { &mut self.base } + fn device_quirk(&self) -> Option { + if self.cfg.enable_bar0 { + return Some(VirtioDeviceQuirk::VirtioGpuEnableBar0); + } + None + } + fn realize(&mut self) -> Result<()> { if self.cfg.max_outputs > VIRTIO_GPU_MAX_OUTPUTS as u32 { bail!( diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 5c1503000..70e47b95c 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -322,6 +322,7 @@ pub type VirtioInterrupt = #[derive(Copy, Clone, PartialEq, Eq)] pub enum VirtioDeviceQuirk { + VirtioGpuEnableBar0, VirtioDeviceQuirkMax, } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 181a668fb..fe654b939 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -18,11 +18,12 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, warn}; +use machine_manager::config::M; use vmm_sys_util::eventfd::EventFd; use crate::{ - virtio_has_feature, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, VirtioInterrupt, - VirtioInterruptType, + virtio_has_feature, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, VirtioDeviceQuirk, + VirtioInterrupt, VirtioInterruptType, }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, @@ -31,7 +32,9 @@ use crate::{ VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_CONSOLE, VIRTIO_TYPE_FS, VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; -use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use address_space::{ + AddressRange, AddressSpace, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, +}; use devices::pci::config::{ RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, @@ -125,6 +128,32 @@ const COMMON_Q_USEDHI_REG: u64 = 0x34; /// 1: select feature bits 32 to 63. const MAX_FEATURES_SELECT_NUM: u32 = 2; +/// The bar0 size of enable_bar0 features +const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; + +fn init_gpu_bar0(config: &mut PciConfig) -> PciResult<()> { + let host_mmap = Arc::new(HostMemMapping::new( + GuestAddress(0), + None, + VIRTIO_GPU_ENABLE_BAR0_SIZE, + None, + false, + false, + false, + )?); + + let region = Region::init_ram_region(host_mmap, "vgpu.vram"); + config.register_bar( + 0, + region, + RegionType::Mem32Bit, + true, + VIRTIO_GPU_ENABLE_BAR0_SIZE, + )?; + + Ok(()) +} + /// Get class id according to device type. /// /// # Arguments @@ -942,6 +971,7 @@ impl PciDevOps for VirtioPciDevice { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; + let device_quirk = self.device.lock().unwrap().device_quirk(); let device_type = self.device.lock().unwrap().device_type(); le_write_u16( &mut self.base.config.config, @@ -1056,6 +1086,10 @@ impl PciDevOps for VirtioPciDevice { self.assign_interrupt_cb(); + if device_quirk == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0) { + init_gpu_bar0(&mut self.base.config)?; + } + self.device .lock() .unwrap() -- Gitee From e9fe74805e395b24bf0682c07165594cd52125f0 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 9 Aug 2023 09:38:46 +0800 Subject: [PATCH 1289/2187] link_list: bugfix for link_list when use pop_head The following steps trigger bugs: 1. add_tail(node0) => head = node0, tail = node0; 2. pop_head => head = None, tail = node; 3. add_tail(node1) => head = None, tail = node1; The value of pop_head after step 3 is None. The caller triggers an unwrap error. Signed-off-by: Mingwang Li --- util/src/link_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/link_list.rs b/util/src/link_list.rs index 1565985e5..55f91a564 100644 --- a/util/src/link_list.rs +++ b/util/src/link_list.rs @@ -128,7 +128,7 @@ impl List { self.head = node.next; match self.head { - None => self.head = None, + None => self.tail = None, Some(mut h) => h.as_mut().prev = None, } -- Gitee From 30b28fb15fdabc9064af096d2cefc7d049056c7d Mon Sep 17 00:00:00 2001 From: wubinfeng Date: Tue, 8 Aug 2023 23:01:00 +0800 Subject: [PATCH 1290/2187] virtio-gpu: support use it in bios phase Edk2 use bar0 like vram in bios phase, only work with special edk2 and which will be used with windows guest. Signed-off-by: wubinfeng --- devices/src/legacy/ramfb.rs | 5 ++--- ui/src/console.rs | 1 + ui/src/gtk/mod.rs | 16 +++++++++++++--- virtio/src/device/gpu.rs | 30 ++++++++++++++++++++++++++++-- virtio/src/transport/virtio_pci.rs | 15 ++++++++++----- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 666c977fd..2d0f3978e 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -27,8 +27,8 @@ use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; use ui::console::{ - console_init, display_graphic_update, display_replace_surface, set_run_stage, ConsoleType, - DisplayConsole, DisplaySurface, HardWareOperations, VmRunningStage, + console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, + DisplaySurface, HardWareOperations, }; use ui::input::{key_event, KEYCODE_RET}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; @@ -70,7 +70,6 @@ impl RamfbState { pub fn new(sys_mem: Arc, install: bool) -> Self { let ramfb_opts = Arc::new(RamfbInterface {}); let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); - set_run_stage(VmRunningStage::Bios); Self { surface: None, con, diff --git a/ui/src/console.rs b/ui/src/console.rs index dfe5d89ea..890b19004 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -671,6 +671,7 @@ pub fn console_init( ); display_replace_surface(&Some(con.clone()), surface) .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); + set_run_stage(VmRunningStage::Bios); Some(con) } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index fd5e99cf6..00e11a7ec 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -42,9 +42,10 @@ use vmm_sys_util::eventfd::EventFd; use crate::{ console::{ - create_msg_surface, get_active_console, graphic_hardware_update, register_display, - DisplayChangeListener, DisplayChangeListenerOperations, DisplayConsole, DisplayMouse, - DisplaySurface, DEFAULT_SURFACE_HEIGHT, DEFAULT_SURFACE_WIDTH, + create_msg_surface, get_active_console, get_run_stage, graphic_hardware_update, + register_display, DisplayChangeListener, DisplayChangeListenerOperations, DisplayConsole, + DisplayMouse, DisplaySurface, VmRunningStage, DEFAULT_SURFACE_HEIGHT, + DEFAULT_SURFACE_WIDTH, DISPLAY_UPDATE_INTERVAL_DEFAULT, }, data::keycode::KEYSYM2KEYCODE, gtk::{draw::set_callback_for_draw_area, menu::GtkMenu}, @@ -166,6 +167,15 @@ impl DisplayChangeListenerOperations for GtkInterface { &self, dcl: &std::sync::Arc>, ) -> Result<()> { + // The way virtio-gpu devices are used in phase OS and others is different. + if self.dev_name.starts_with("virtio-gpu") { + if get_run_stage() == VmRunningStage::Os { + dcl.lock().unwrap().update_interval = 0; + } else { + dcl.lock().unwrap().update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT; + } + } + let event = DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplayRefresh); let con_id = dcl.lock().unwrap().con_id; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 3301bb920..b221ad417 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -67,6 +67,9 @@ const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0; /// The flag indicates that the frame buffer only used in windows. const VIRTIO_GPU_RES_WIN_FRAMEBUF: u32 = 0x80000000; +/// The flag indicates that the frame buffer only used in special bios phase for windows. +const VIRTIO_GPU_RES_EFI_FRAMEBUF: u32 = 0x40000000; +const VIRTIO_GPU_RES_FRAMEBUF: u32 = VIRTIO_GPU_RES_WIN_FRAMEBUF | VIRTIO_GPU_RES_EFI_FRAMEBUF; #[derive(Debug)] struct GpuResource { @@ -286,9 +289,29 @@ struct GpuOpts { config_space: Arc>, /// Callback to trigger interrupt. interrupt_cb: Option>, + /// Whether to use it in the bios phase. + enable_bar0: bool, } impl HardWareOperations for GpuOpts { + fn hw_update(&self, con: Arc>) { + // Only in the Bios phase and configured with enable_bar0 feature and need to + // use special modifications with edk2. + if !self.enable_bar0 || get_run_stage() != VmRunningStage::Bios { + return; + } + + let locked_con = con.lock().unwrap(); + if locked_con.surface.is_none() { + return; + } + let width = locked_con.width; + let height = locked_con.height; + drop(locked_con); + display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, width, height) + .unwrap_or_else(|e| error!("Error occurs during graphic updating: {:?}", e)); + } + fn hw_ui_info(&self, con: Arc>, width: u32, height: u32) { let con_id = con.lock().unwrap().con_id; @@ -955,7 +978,7 @@ impl GpuIoHandler { let pixman_stride = unsafe { pixman_image_get_stride(res.pixman_image) }; let offset = info_set_scanout.rect.x_coord * bpp + info_set_scanout.rect.y_coord * pixman_stride as u32; - let res_data = if info_set_scanout.resource_id & VIRTIO_GPU_RES_WIN_FRAMEBUF != 0 { + let res_data = if info_set_scanout.resource_id & VIRTIO_GPU_RES_FRAMEBUF != 0 { res.iov[0].iov_base as *mut u32 } else { unsafe { pixman_image_get_data(res.pixman_image) } @@ -1085,7 +1108,7 @@ impl GpuIoHandler { } let res = &self.resources_list[res_idx.unwrap()]; - if res.resource_id & VIRTIO_GPU_RES_WIN_FRAMEBUF != 0 { + if res.resource_id & VIRTIO_GPU_RES_FRAMEBUF != 0 { return (None, VIRTIO_GPU_RESP_OK_NODATA); } if !is_rect_in_resource(&info_transfer.rect, res) { @@ -1489,6 +1512,7 @@ impl VirtioDevice for Gpu { output_states: self.output_states.clone(), config_space: self.config_space.clone(), interrupt_cb: None, + enable_bar0: self.cfg.enable_bar0, }); for i in 0..self.cfg.max_outputs { let dev_name = format!("virtio-gpu{}", i); @@ -1497,6 +1521,7 @@ impl VirtioDevice for Gpu { output_states[i as usize].con_id = con_ref.lock().unwrap().con_id; self.consoles.push(con); } + drop(output_states); self.init_config_features()?; @@ -1563,6 +1588,7 @@ impl VirtioDevice for Gpu { output_states: self.output_states.clone(), config_space: self.config_space.clone(), interrupt_cb: Some(interrupt_cb.clone()), + enable_bar0: self.cfg.enable_bar0, }); for con in &self.consoles { let con_ref = con.as_ref().unwrap().upgrade().unwrap(); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index fe654b939..c39cf0bd4 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -63,7 +63,6 @@ const VIRTIO_PCI_CLASS_ID_STORAGE_OTHER: u16 = 0x0180; const VIRTIO_PCI_CLASS_ID_COMMUNICATION_OTHER: u16 = 0x0780; #[cfg(target_arch = "aarch64")] const VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER: u16 = 0x0380; -#[cfg(target_arch = "x86_64")] const VIRTIO_PCI_CLASS_ID_DISPLAY_VGA: u16 = 0x0300; const VIRTIO_PCI_CLASS_ID_OTHERS: u16 = 0x00ff; @@ -158,8 +157,9 @@ fn init_gpu_bar0(config: &mut PciConfig) -> PciResult<()> { /// /// # Arguments /// -/// * `device_type` - Device type set by the host. -fn get_virtio_class_id(device_type: u32) -> u16 { +/// * `device_type` - Device type set by the host. +/// * `device_quirk` - Device quirk set by the host. +fn get_virtio_class_id(device_type: u32, _device_quirk: Option) -> u16 { match device_type { VIRTIO_TYPE_BLOCK => VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_SCSI => VIRTIO_PCI_CLASS_ID_BLOCK, @@ -169,7 +169,12 @@ fn get_virtio_class_id(device_type: u32) -> u16 { #[cfg(target_arch = "x86_64")] VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_VGA, #[cfg(target_arch = "aarch64")] - VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER, + VIRTIO_TYPE_GPU => { + if _device_quirk == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0) { + return VIRTIO_PCI_CLASS_ID_DISPLAY_VGA; + } + VIRTIO_PCI_CLASS_ID_DISPLAY_OTHER + } _ => { warn!("Unknown device type, please make sure it is supported."); VIRTIO_PCI_CLASS_ID_OTHERS @@ -984,7 +989,7 @@ impl PciDevOps for VirtioPciDevice { VIRTIO_PCI_DEVICE_ID_BASE + device_type as u16, )?; self.base.config.config[REVISION_ID] = VIRTIO_PCI_ABI_VERSION; - let class_id = get_virtio_class_id(device_type); + let class_id = get_virtio_class_id(device_type, device_quirk); le_write_u16( &mut self.base.config.config, SUB_CLASS_CODE as usize, -- Gitee From 9e3edc6905a182e61ca580c5a014da174c0046fb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 9 Aug 2023 09:56:10 +0800 Subject: [PATCH 1291/2187] qcow2: optimize create and delete snapshot operation Delete the complicated code for error recovering. Do not write changed metadata(refcount block and l1/l2 table) to disk immediately, save it until the create and delete snapshot process succeed. Otherwise, drop all the dirty cache. Signed-off-by: Yan Wang --- block_backend/src/qcow2/cache.rs | 12 + block_backend/src/qcow2/mod.rs | 475 +++++++++++----------------- block_backend/src/qcow2/refcount.rs | 8 +- block_backend/src/qcow2/snapshot.rs | 20 +- block_backend/src/qcow2/table.rs | 23 +- 5 files changed, 223 insertions(+), 315 deletions(-) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 8cefe1ae5..c5758bed8 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -220,6 +220,18 @@ impl Qcow2Cache { self.cache_map.insert(key, entry); replaced_entry } + + pub fn clean_up_dirty_cache(&mut self) { + let mut dirty_keys = Vec::with_capacity(self.cache_map.len()); + for (key, entry) in self.cache_map.iter() { + if entry.borrow().dirty_info.is_dirty { + dirty_keys.push(*key); + } + } + for key in dirty_keys { + self.cache_map.remove(&key); + } + } } #[cfg(test)] diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 972150a43..722d1daca 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -249,7 +249,19 @@ impl Qcow2Driver { } pub fn flush(&mut self) -> Result<()> { - self.table.flush() + self.table.flush()?; + self.refcount.flush() + } + + pub fn drop_dirty_caches(&mut self) { + self.table.drop_dirty_caches(); + self.refcount.drop_dirty_caches(); + self.table.load_l1_table().unwrap_or_else(|e| { + error!( + "Failed to reload l1 table for dropping unused changes, {:?}", + e + ) + }); } fn load_header(&mut self) -> Result<()> { @@ -425,7 +437,7 @@ impl Qcow2Driver { // Update l1_table. self.table .update_l1_table(l1_index as usize, new_l2_offset | QCOW2_OFFSET_COPIED); - self.table.save_l1_table(&self.header)?; + self.table.save_l1_table()?; // Decrease the refcount of the old table. if old_l2_offset != 0 { @@ -658,153 +670,72 @@ impl Qcow2Driver { bail!("Snapshot with name {} does not exist", name); } - // Alloc new snapshot table. Delete the new snapshot from the snapshot table - // and write new snapshot table to file. - let cluster_size = self.header.cluster_size(); + // Delete snapshot information in memory. let snap = self.snapshot.del_snapshot(snapshot_idx as usize); - let new_snapshots_table_clusters = - bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); - let l1_table_clusters = - bytes_to_clusters(snap.l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); - let mut new_snapshot_table_offset = 0_u64; - let mut err_msg: String = "".to_string(); - let mut error_stage = 0; - - // Using loop for error handling. - #[allow(clippy::never_loop)] - loop { - if new_snapshots_table_clusters != 0 { - new_snapshot_table_offset = self - .alloc_cluster(new_snapshots_table_clusters, true) - .unwrap_or_else(|e| { - err_msg = format!("{:?}", e); - error_stage = 1; - 0 - }); - - if new_snapshot_table_offset == 0 { - break; - } - - if let Err(e) = self.snapshot.save_snapshot_table(new_snapshot_table_offset) { - err_msg = format!("{:?}", e); - error_stage = 2; - break; - } - } - - // Decrease the refcounts of clusters referenced by the snapshot. - if let Err(e) = self.qcow2_update_snapshot_refcount(snap.l1_table_offset, -1) { - err_msg = format!("{:?}", e); - error_stage = 2; - break; - } - // Free the snaphshot L1 table. - if let Err(e) = self.refcount.update_refcount( - snap.l1_table_offset, - l1_table_clusters, - -1, - false, - &Qcow2DiscardType::Snapshot, - ) { - err_msg = format!("{:?}", e); - error_stage = 3; - break; - } - - // Update the copied flag on the current cluster offsets. - if let Err(e) = self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0) { - err_msg = format!("{:?}", e); - error_stage = 4; - break; - } + // Alloc new cluster to save snapshots(except the deleted one) to disk. + let cluster_size = self.header.cluster_size(); + let mut new_snapshots_offset = 0_u64; + let snapshot_table_clusters = + bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); + if self.snapshot.snapshots_number() > 0 { + new_snapshots_offset = self.alloc_cluster(snapshot_table_clusters, true)?; + self.snapshot + .save_snapshot_table(new_snapshots_offset, &snap, false)?; + } + self.snapshot.snapshot_table_offset = new_snapshots_offset; - if let Err(e) = self.refcount.update_refcount( - self.header.snapshots_offset, - 1, - -1, - true, - &Qcow2DiscardType::Snapshot, - ) { - err_msg = format!("{:?}", e); - error_stage = 5; - break; - } + // Decrease the refcounts of clusters referenced by the snapshot. + self.qcow2_update_snapshot_refcount(snap.l1_table_offset, -1)?; - let mut new_header = self.header.clone(); - new_header.snapshots_offset = new_snapshot_table_offset; - new_header.nb_snapshots -= 1; - if let Err(e) = self - .sync_aio - .borrow_mut() - .write_buffer(0, &new_header.to_vec()) - { - err_msg = format!("{:?}", e); - error_stage = 6; - break; - } - self.header.snapshots_offset = new_header.snapshots_offset; - self.header.nb_snapshots = new_header.nb_snapshots; - self.refcount.sync_process_discards(OpCode::Discard); + // Free the snaphshot L1 table. + let l1_table_clusters = + bytes_to_clusters(snap.l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); + self.refcount.update_refcount( + snap.l1_table_offset, + l1_table_clusters, + -1, + false, + &Qcow2DiscardType::Snapshot, + )?; + + // Update the flag of the L1/L2 table entries. + self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; + + // Free the cluster of the old snapshot table. + self.refcount.update_refcount( + self.header.snapshots_offset, + snapshot_table_clusters, + -1, + false, + &Qcow2DiscardType::Snapshot, + )?; + + // Flush the cache of the refcount block and l2 table. + self.flush()?; - return Ok(SnapshotInfo { - id: snap.id.to_string(), - name: snap.name.clone(), - vm_state_size: snap.vm_state_size as u64, - date_sec: snap.date_sec, - date_nsec: snap.date_nsec, - vm_clock_nsec: snap.vm_clock_nsec, - icount: snap.icount, - }); - } + // Update the snapshot information in qcow2 header. + self.update_snapshot_info_in_header(new_snapshots_offset, false)?; - // Error handling, to revert some operation. - self.refcount.discard_list.clear(); - if error_stage >= 6 { - self.refcount.update_refcount( - self.header.snapshots_offset, - 1, - 1, - true, - &Qcow2DiscardType::Never, - )?; - } - if error_stage >= 4 { - self.refcount.update_refcount( - snap.l1_table_offset, - l1_table_clusters, - 1, - true, - &Qcow2DiscardType::Never, - )?; - } - if error_stage >= 3 { - self.qcow2_update_snapshot_refcount(snap.l1_table_offset, 1)?; - if error_stage >= 5 { - self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; - } - } - if error_stage >= 2 && new_snapshots_table_clusters != 0 { - self.free_cluster( - new_snapshot_table_offset, - new_snapshots_table_clusters, - true, - &Qcow2DiscardType::Never, - )?; - } - if error_stage >= 1 { - self.snapshot.insert_snapshot(snap, snapshot_idx as usize) - } + // Discard unused clusters. + self.refcount.sync_process_discards(OpCode::Discard); - bail!("{}", err_msg); + Ok(SnapshotInfo { + id: snap.id.to_string(), + name: snap.name.clone(), + vm_state_size: snap.vm_state_size as u64, + date_sec: snap.date_sec, + date_nsec: snap.date_nsec, + vm_clock_nsec: snap.vm_clock_nsec, + icount: snap.icount, + }) } fn qcow2_create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()> { if self.get_snapshot_by_name(&name) >= 0 { bail!("Snapshot {} exists!", name); } - if self.snapshot.snapshots_number() > QCOW2_MAX_SNAPSHOTS { + if self.snapshot.snapshots_number() >= QCOW2_MAX_SNAPSHOTS { bail!( "The number of snapshots exceed the maximum limit {}", QCOW2_MAX_SNAPSHOTS @@ -812,173 +743,102 @@ impl Qcow2Driver { } // Alloc cluster and copy L1 table for snapshot. - let l1_table_len = self.header.l1_size as u64 * ENTRY_SIZE; let cluster_size = self.header.cluster_size(); + let l1_table_len = self.header.l1_size as u64 * ENTRY_SIZE; let l1_table_clusters = bytes_to_clusters(l1_table_len, cluster_size).unwrap(); let new_l1_table_offset = self.alloc_cluster(l1_table_clusters, true)?; - - let old_snapshots_table_len = self.snapshot.snapshot_size; - let old_snapshots_table_clusters = - bytes_to_clusters(old_snapshots_table_len, cluster_size).unwrap(); - let mut err_msg: String = "".to_string(); - let mut new_snapshot_table_offset = 0_u64; - let mut error_stage = 0; - - // Using loop for error handling. - #[allow(clippy::never_loop)] - loop { - // Check if l1 table offset is overlap. - if self.qcow2_pre_write_overlap_check(0, new_l1_table_offset, l1_table_len) != 0 { - err_msg = format!( - "Allocated snapshot L1 table addr {:x} is illegal!", - new_l1_table_offset - ); - error_stage = 1; - break; - } - - // Write L1 table to file. - if let Err(e) = self - .sync_aio - .borrow_mut() - .write_ctrl_cluster(new_l1_table_offset, &self.table.l1_table) - { - error_stage = 1; - err_msg = format!("{:?}", e); - break; - } - - // Increase the refcounts of all clusters searched by L1 table. - if let Err(e) = self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 1) { - error_stage = 1; - err_msg = format!("{:?}", e); - break; - } - - // Alloc new snapshot table. - let (date_sec, date_nsec) = gettime(); - let snap = QcowSnapshot { - l1_table_offset: new_l1_table_offset, - l1_size: self.header.l1_size, - id: self.snapshot.find_new_snapshot_id(), - name, - disk_size: self.virtual_disk_size(), - vm_state_size: 0, - date_sec, - date_nsec, - vm_clock_nsec, - icount: u64::MAX, - extra_data_size: size_of::() as u32, - }; - let new_snapshots_table_clusters = - bytes_to_clusters(old_snapshots_table_len + snap.get_size(), cluster_size).unwrap(); - new_snapshot_table_offset = self - .alloc_cluster(new_snapshots_table_clusters, true) - .unwrap_or_else(|e| { - error_stage = 2; - err_msg = format!("{:?}", e); - 0 - }); - if new_snapshot_table_offset == 0 { - break; - } - info!( - "Snapshot table offset: old(0x{:x}) -> new(0x{:x})", - self.header.snapshots_offset, new_snapshot_table_offset, + let ret = self.check_overlap(0, new_l1_table_offset, l1_table_len); + if ret != 0 { + bail!( + "Allocated snapshot L1 table addr {:x} is illegal, ret is {}!", + new_l1_table_offset, + ret ); - - // Append the new snapshot to the snapshot table and write new snapshot table to file. - self.snapshot.add_snapshot(snap); - if let Err(e) = self.snapshot.save_snapshot_table(new_snapshot_table_offset) { - error_stage = 3; - err_msg = format!("{:?}", e); - break; - } - self.snapshot.snapshot_table_offset = new_snapshot_table_offset; - - // Free the old snapshot table cluster if snapshot exists. - if self.header.snapshots_offset != 0 { - if let Err(e) = self.refcount.update_refcount( - self.header.snapshots_offset, - old_snapshots_table_clusters, - -1, - true, - &Qcow2DiscardType::Snapshot, - ) { - error_stage = 4; - err_msg = format!("{:?}", e); - break; - } - } - - // Update snapshot offset and num in qcow2 header. - let mut new_header = self.header.clone(); - new_header.snapshots_offset = new_snapshot_table_offset; - new_header.nb_snapshots += 1; - if let Err(e) = self - .sync_aio - .borrow_mut() - .write_buffer(0, &new_header.to_vec()) - { - error_stage = 5; - err_msg = format!("{:?}", e); - break; - } - self.header.snapshots_offset = new_header.snapshots_offset; - self.header.nb_snapshots = new_header.nb_snapshots; - self.refcount.sync_process_discards(OpCode::Discard); - - return Ok(()); } + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(new_l1_table_offset, &self.table.l1_table)?; + + // Increase the refcount of all clusters searched by L1 table. + self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 1)?; + + // Alloc new snapshot table. + let (date_sec, date_nsec) = gettime(); + let snap = QcowSnapshot { + l1_table_offset: new_l1_table_offset, + l1_size: self.header.l1_size, + id: self.snapshot.find_new_snapshot_id(), + name, + disk_size: self.virtual_disk_size(), + vm_state_size: 0, + date_sec, + date_nsec, + vm_clock_nsec, + icount: u64::MAX, + extra_data_size: size_of::() as u32, + }; + let old_snapshot_table_len = self.snapshot.snapshot_size; + let snapshot_table_clusters = + bytes_to_clusters(old_snapshot_table_len + snap.get_size(), cluster_size).unwrap(); + let new_snapshots_offset = self.alloc_cluster(snapshot_table_clusters, true)?; + info!( + "Snapshot table offset: old(0x{:x}) -> new(0x{:x})", + self.header.snapshots_offset, new_snapshots_offset, + ); - // Error handling, to revert some operation. - self.refcount.discard_list.clear(); - if error_stage >= 5 && self.header.snapshots_offset != 0 { + // Append the new snapshot to the snapshot table and write new snapshot table to file. + self.snapshot + .save_snapshot_table(new_snapshots_offset, &snap, true)?; + + // Free the old snapshot table cluster if snapshot exists. + if self.header.snapshots_offset != 0 { + let clusters = bytes_to_clusters(old_snapshot_table_len, cluster_size).unwrap(); self.refcount.update_refcount( self.header.snapshots_offset, - old_snapshots_table_clusters, - 1, - true, - &Qcow2DiscardType::Never, - )?; - } - - if error_stage >= 4 { - self.snapshot.snapshot_table_offset = self.header.snapshots_offset; - } - if error_stage >= 3 { - // Delete new added snapshot. - let num_clusters = - bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); - self.snapshot - .del_snapshot(self.snapshot.snapshots_number() - 1); - self.free_cluster( - new_snapshot_table_offset, - num_clusters, - true, + clusters, + -1, + false, &Qcow2DiscardType::Snapshot, )?; } - if error_stage >= 2 { - self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, -1)?; - } - if error_stage >= 1 { - self.free_cluster( - new_l1_table_offset, - l1_table_clusters, - true, - &Qcow2DiscardType::Snapshot, - )?; + + // Flush the cache of the refcount block and l1/l2 table. + self.flush()?; + + // Update snapshot offset and num in qcow2 header. + self.update_snapshot_info_in_header(new_snapshots_offset, true)?; + + // Add and update snapshot information in memory. + self.snapshot.add_snapshot(snap); + self.snapshot.snapshot_table_offset = new_snapshots_offset; + + // Discard unused clusters. + self.refcount.sync_process_discards(OpCode::Discard); + + Ok(()) + } + + fn update_snapshot_info_in_header(&mut self, snapshot_offset: u64, add: bool) -> Result<()> { + let mut new_header = self.header.clone(); + new_header.snapshots_offset = snapshot_offset; + if add { + new_header.nb_snapshots += 1; + } else { + new_header.nb_snapshots -= 1; } + self.sync_aio + .borrow_mut() + .write_buffer(0, &new_header.to_vec())?; + self.header.snapshots_offset = new_header.snapshots_offset; + self.header.nb_snapshots = new_header.nb_snapshots; - bail!("{}", err_msg); + Ok(()) } /// Update the refcounts of all clusters searched by l1_table_offset. fn qcow2_update_snapshot_refcount(&mut self, l1_table_offset: u64, added: i32) -> Result<()> { let l1_table_size = self.header.l1_size as usize; let mut l1_table = self.table.l1_table.clone(); - let mut l1_changed = false; debug!( "Update snapshot refcount: l1 table offset {:x}, active header l1 table addr {:x}, add {}", l1_table_offset, @@ -1061,9 +921,6 @@ impl Qcow2Driver { borrowed_table.set_entry_map(idx, new_l2_entry)?; } } - self.sync_aio - .borrow_mut() - .write_buffer(l2_table_offset, borrowed_table.get_value())?; drop(borrowed_table); if added != 0 { @@ -1083,18 +940,11 @@ impl Qcow2Driver { } if l2_table_offset != old_l2_table_offset { *l1_entry = l2_table_offset; - l1_changed = true; if l1_table_offset == self.header.l1_table_offset { self.table.update_l1_table(i, l2_table_offset); } } } - self.refcount.flush_refcount_block_cache()?; - if l1_changed { - self.sync_aio - .borrow_mut() - .write_ctrl_cluster(l1_table_offset, &l1_table)?; - } Ok(()) } @@ -1139,7 +989,7 @@ impl Qcow2Driver { } // Check if there exist intersection between given address range and qcow2 mede data. - fn qcow2_pre_write_overlap_check(&self, ignore: u64, offset: u64, size: u64) -> i64 { + fn check_overlap(&self, ignore: u64, offset: u64, size: u64) -> i64 { let check = DEFAULT_QCOW2_METADATA_OVERLAP_CHECK | !ignore; if check == 0 { return 0; @@ -1264,11 +1114,24 @@ pub trait InternalSnapshotOps: Send + Sync { impl InternalSnapshotOps for Qcow2Driver { fn create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()> { + // Flush the dirty metadata first, so it can drop dirty caches for reverting + // when creating snapshot failed. + self.flush()?; self.qcow2_create_snapshot(name, vm_clock_nsec) + .map_err(|e| { + self.drop_dirty_caches(); + e + }) } fn delete_snapshot(&mut self, name: String) -> Result { - self.qcow2_delete_snapshot(name) + // Flush the dirty metadata first, so it can drop dirty caches for reverting + // when deleting snapshot failed. + self.flush()?; + self.qcow2_delete_snapshot(name).map_err(|e| { + self.drop_dirty_caches(); + e + }) } fn list_snapshots(&self) -> String { @@ -1306,7 +1169,7 @@ impl Qcow2Driver { } self.refcount - .flush_refcount_block_cache() + .flush() .unwrap_or_else(|e| error!("Flush refcount block failed: {:?}", e)); self.table .flush_l2_table_cache() @@ -2361,6 +2224,20 @@ mod test { fn get_host_offset(qcow2_driver: &mut Qcow2Driver<()>, guest_offset: u64) -> u64 { let l2_index = qcow2_driver.table.get_l2_table_index(guest_offset); + if qcow2_driver + .table + .get_l2_table_cache_entry(guest_offset) + .is_none() + { + let l2_address = + qcow2_driver.table.get_l1_table_entry(guest_offset) & L1_TABLE_OFFSET_MASK; + let l2_cluster = qcow2_driver.load_cluster(l2_address).unwrap(); + let l2_table = Rc::new(RefCell::new( + CacheTable::new(l2_address, l2_cluster, ENTRY_SIZE_U64).unwrap(), + )); + qcow2_driver.table.update_l2_table(l2_table).unwrap(); + } + // All used l2 table will be cached for it's little data size in these tests. let l2_table = qcow2_driver .table @@ -2401,7 +2278,7 @@ mod test { // 2) Refcount is right. for case in &case_list { let host_offset = get_host_offset(&mut qcow2_driver, case.offset as u64); - assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 1); + assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 2); } // 3) L1 table refcount is right. assert_eq!( @@ -2423,7 +2300,7 @@ mod test { .refcount .get_refcount(*l1_entry & L1_TABLE_OFFSET_MASK) .unwrap(), - 1 + 2 ); } } diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index a34a580fe..4ac62dee3 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -250,7 +250,7 @@ impl RefCount { bail!("Failed to set refcounts, recover {}", status); } if flush { - self.flush_refcount_block_cache()?; + self.flush()?; } Ok(()) } @@ -279,7 +279,7 @@ impl RefCount { rc_vec.len() } - pub fn flush_refcount_block_cache(&self) -> Result<()> { + pub fn flush(&self) -> Result<()> { for (_, entry) in self.refcount_blk_cache.iter() { let mut borrowed_entry = entry.borrow_mut(); if !borrowed_entry.dirty_info.is_dirty { @@ -594,6 +594,10 @@ impl RefCount { borrowed_entry.dirty_info.end, ) } + + pub fn drop_dirty_caches(&mut self) { + self.refcount_blk_cache.clean_up_dirty_cache(); + } } #[cfg(test)] diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index abcc98a29..e2ebbe8d8 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -78,13 +78,6 @@ impl InternalSnapshot { snap } - pub fn insert_snapshot(&mut self, snap: QcowSnapshot, index: usize) { - let size = snap.get_size(); - self.snapshots.insert(index, snap); - self.snapshot_size += size; - self.nb_snapshots += 1; - } - pub fn find_new_snapshot_id(&self) -> u64 { let mut id_max = 0; for snap in &self.snapshots { @@ -96,11 +89,22 @@ impl InternalSnapshot { id_max + 1 } - pub fn save_snapshot_table(&self, addr: u64) -> Result<()> { + pub fn save_snapshot_table( + &self, + addr: u64, + extra_snap: &QcowSnapshot, + attach: bool, + ) -> Result<()> { let mut buf = Vec::new(); for snap in &self.snapshots { + if !attach && snap.id == extra_snap.id { + continue; + } buf.append(&mut snap.gen_snapshot_table_entry()); } + if attach { + buf.append(&mut extra_snap.gen_snapshot_table_entry()); + } self.sync_aio.borrow_mut().write_buffer(addr, &buf) } diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 44a6dff18..571dd200a 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -83,6 +83,8 @@ pub struct Qcow2Table { cluster_bits: u64, cluster_size: u64, pub l1_table: Vec, + l1_table_offset: u64, + l1_size: u32, pub l2_table_cache: Qcow2Cache, sync_aio: Rc>, l2_bits: u64, @@ -96,6 +98,8 @@ impl Qcow2Table { cluster_bits: 0, cluster_size: 0, l1_table: Vec::new(), + l1_table_offset: 0, + l1_size: 0, l2_table_cache: Qcow2Cache::default(), l2_bits: 0, l2_size: 0, @@ -131,23 +135,25 @@ impl Qcow2Table { self.l2_bits = header.cluster_bits as u64 - ENTRY_BITS; self.l2_size = header.cluster_size() / ENTRY_SIZE; self.l2_table_cache = l2_table_cache; - self.load_l1_table(header) + self.l1_table_offset = header.l1_table_offset; + self.l1_size = header.l1_size; + self.load_l1_table() .with_context(|| "Failed to load l1 table")?; Ok(()) } - fn load_l1_table(&mut self, header: &QcowHeader) -> Result<()> { + pub fn load_l1_table(&mut self) -> Result<()> { self.l1_table = self .sync_aio .borrow_mut() - .read_ctrl_cluster(header.l1_table_offset, header.l1_size as u64)?; + .read_ctrl_cluster(self.l1_table_offset, self.l1_size as u64)?; Ok(()) } - pub fn save_l1_table(&mut self, header: &QcowHeader) -> Result<()> { + pub fn save_l1_table(&mut self) -> Result<()> { self.sync_aio .borrow_mut() - .write_ctrl_cluster(header.l1_table_offset, &self.l1_table) + .write_ctrl_cluster(self.l1_table_offset, &self.l1_table) } pub fn get_l1_table_index(&self, guest_offset: u64) -> u64 { @@ -227,7 +233,12 @@ impl Qcow2Table { } pub fn flush(&mut self) -> Result<()> { - self.flush_l2_table_cache() + self.flush_l2_table_cache()?; + self.save_l1_table() + } + + pub fn drop_dirty_caches(&mut self) { + self.l2_table_cache.clean_up_dirty_cache(); } } -- Gitee From 6bff66a612ede9614b2b3200aae35e665546fc06 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 9 Aug 2023 09:57:54 +0800 Subject: [PATCH 1292/2187] qcow2: fix an error when checking metadata overlap Using '&' instead of '|' for ignored metadata check. Signed-off-by: Yan Wang --- block_backend/src/qcow2/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 722d1daca..48d828834 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -990,7 +990,7 @@ impl Qcow2Driver { // Check if there exist intersection between given address range and qcow2 mede data. fn check_overlap(&self, ignore: u64, offset: u64, size: u64) -> i64 { - let check = DEFAULT_QCOW2_METADATA_OVERLAP_CHECK | !ignore; + let check = DEFAULT_QCOW2_METADATA_OVERLAP_CHECK & !ignore; if check == 0 { return 0; } -- Gitee From 21cdb8f6f6909b294ebaf4959e76092c2f2493bc Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 9 Aug 2023 10:10:21 +0800 Subject: [PATCH 1293/2187] qcow2: optimize the qcow2 performance Do not write metadata directly after modified. Use timer to minus the sync write for metadata. Signed-off-by: Yan Wang --- block_backend/src/lib.rs | 6 +- block_backend/src/qcow2/cache.rs | 49 +++++++-- block_backend/src/qcow2/mod.rs | 163 +++++++++++++++++++--------- block_backend/src/qcow2/refcount.rs | 38 +++---- block_backend/src/qcow2/table.rs | 52 ++++----- 5 files changed, 197 insertions(+), 111 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index d3f078fb6..7de6a08cf 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -27,7 +27,7 @@ use machine_manager::{ config::DiskFormat, temp_cleaner::{ExitNotifier, TempCleaner}, }; -use qcow2::{Qcow2Driver, QCOW2_LIST}; +use qcow2::{qcow2_flush_metadata, Qcow2Driver, QCOW2_LIST}; use raw::RawDriver; use util::aio::{Aio, Iovec, WriteZeroesState}; @@ -134,6 +134,10 @@ pub fn create_block_backend( } }) as Arc; TempCleaner::add_exit_notifier(prop.id, exit_notifier); + + // Add timer for flushing qcow2 metadata. + qcow2_flush_metadata(Arc::downgrade(&new_qcow2)); + Ok(new_qcow2) } } diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index c5758bed8..ed8d6f1f2 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -10,15 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{ - cell::RefCell, - collections::{hash_map::Iter, HashMap}, - rc::Rc, -}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use anyhow::{bail, Result}; use byteorder::{BigEndian, ByteOrder}; -use log::warn; +use log::{error, warn}; + +use crate::qcow2::SyncAioInfo; const CACHE_DEFAULT_SIZE: usize = 1; pub const ENTRY_SIZE_U16: usize = 2; @@ -99,7 +97,7 @@ impl CacheTable { } #[inline(always)] - pub fn get_entry_map(&mut self, idx: usize) -> Result { + pub fn get_entry_map(&self, idx: usize) -> Result { self.be_read(idx) } @@ -145,6 +143,8 @@ pub struct Qcow2Cache { /// LRU count which record the latest count and increased when cache is accessed. pub lru_count: u64, pub cache_map: HashMap>>, + /// Used to store the modified CacheTable. + dirty_tables: Vec>>, } impl Qcow2Cache { @@ -160,6 +160,7 @@ impl Qcow2Cache { max_size, lru_count: 0, cache_map: HashMap::with_capacity(max_size), + dirty_tables: Vec::with_capacity(max_size), } } @@ -187,10 +188,6 @@ impl Qcow2Cache { Some(entry) } - pub fn iter(&self) -> Iter>> { - self.cache_map.iter() - } - pub fn lru_replace( &mut self, key: u64, @@ -232,6 +229,36 @@ impl Qcow2Cache { self.cache_map.remove(&key); } } + + pub fn flush(&mut self, sync_aio: Rc>) -> Result<()> { + let mut ret = Ok(()); + for entry in self.dirty_tables.iter() { + let mut borrowed_entry = entry.borrow_mut(); + if !borrowed_entry.dirty_info.is_dirty { + continue; + } + sync_aio + .borrow_mut() + .write_dirty_info( + borrowed_entry.addr, + borrowed_entry.get_value(), + borrowed_entry.dirty_info.start, + borrowed_entry.dirty_info.end, + ) + .unwrap_or_else(|e| { + error!("Failed to flush cache, {:?}", e.to_string()); + ret = Err(e); + }); + borrowed_entry.dirty_info.clear(); + } + self.dirty_tables.clear(); + + ret + } + + pub fn add_dirty_table(&mut self, table: Rc>) { + self.dirty_tables.push(table); + } } #[cfg(test)] diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 48d828834..3644e3889 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -23,7 +23,8 @@ use std::{ mem::size_of, os::unix::io::{AsRawFd, RawFd}, rc::Rc, - sync::{atomic::AtomicBool, Arc, Mutex}, + sync::{atomic::AtomicBool, Arc, Mutex, Weak}, + time::Duration, }; use anyhow::{bail, Context, Result}; @@ -43,6 +44,7 @@ use crate::{ }, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, }; +use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::SnapshotInfo; use util::{ aio::{ @@ -64,6 +66,9 @@ const QCOW2_OFFSET_COMPRESSED: u64 = 1 << 62; const QCOW2_OFFSET_COPIED: u64 = 1 << 63; const DEFAULT_SECTOR_SIZE: u64 = 512; +// The default flush interval is 30s. +const DEFAULT_METADATA_FLUSH_INTERVAL: u64 = 30; + const METADATA_OVERLAP_CHECK_MAINHEADER: u64 = 1 << 0; const METADATA_OVERLAP_CHECK_ACTIVEL1: u64 = 1 << 1; const METADATA_OVERLAP_CHECK_ACTIVEL2: u64 = 1 << 2; @@ -100,7 +105,7 @@ pub struct SyncAioInfo { /// Aio for sync read/write metadata. aio: Aio<()>, fd: RawFd, - prop: BlockProperty, + pub prop: BlockProperty, } impl SyncAioInfo { @@ -205,6 +210,29 @@ impl Drop for Qcow2Driver { } } +/// Add timer for flushing qcow2 metadata. +pub fn qcow2_flush_metadata(qcow2_driver: Weak>>) { + if qcow2_driver.upgrade().is_none() { + info!("Qcow2 flush metadata timer exit"); + return; + } + + let driver = qcow2_driver.upgrade().unwrap(); + let mut locked_driver = driver.lock().unwrap(); + locked_driver + .flush() + .unwrap_or_else(|e| error!("Flush qcow2 metadata failed, {:?}", e)); + + let flush_func = Box::new(move || { + qcow2_flush_metadata(qcow2_driver.clone()); + }); + let iothread = locked_driver.sync_aio.borrow().prop.iothread.clone(); + EventLoop::get_ctx(iothread.as_ref()).unwrap().timer_add( + flush_func, + Duration::from_secs(DEFAULT_METADATA_FLUSH_INTERVAL), + ); +} + impl Qcow2Driver { pub fn new(file: File, aio: Aio, conf: BlockProperty) -> Result { let fd = file.as_raw_fd(); @@ -298,7 +326,7 @@ impl Qcow2Driver { ENTRY_SIZE_U64, )?)); let res = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; - self.table.update_l2_table(l2_table)?; + self.table.cache_l2_table(l2_table)?; Ok(res) } } @@ -363,10 +391,12 @@ impl Qcow2Driver { } } - fn host_offset_for_write(&mut self, guest_offset: u64) -> Result { + fn host_offset_for_write(&mut self, guest_offset: u64, nbytes: u64) -> Result { + let mut need_check = false; let l2_index = self.table.get_l2_table_index(guest_offset); let l2_table = self.get_table_cluster(guest_offset)?; let mut l2_entry = l2_table.borrow_mut().get_entry_map(l2_index as usize)?; + let old_l2_entry = l2_entry; l2_entry &= !QCOW2_OFLAG_ZERO; let mut cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; if cluster_addr == 0 { @@ -375,19 +405,33 @@ impl Qcow2Driver { cluster_addr = new_addr & L2_TABLE_OFFSET_MASK; } else if l2_entry & QCOW2_OFFSET_COPIED == 0 { // Copy on write for data cluster. - let new_data_addr = self.alloc_cluster(1, false)?; - let data = self.load_cluster(cluster_addr)?; - self.sync_aio - .borrow_mut() - .write_buffer(new_data_addr, &data)?; + let new_data_addr = self.alloc_cluster(1, true)?; + if nbytes < self.header.cluster_size() { + let data = self.load_cluster(cluster_addr)?; + self.sync_aio + .borrow_mut() + .write_buffer(new_data_addr, &data)?; + } self.refcount - .update_refcount(cluster_addr, 1, -1, true, &Qcow2DiscardType::Other)?; + .update_refcount(cluster_addr, 1, -1, false, &Qcow2DiscardType::Other)?; l2_entry = new_data_addr | QCOW2_OFFSET_COPIED; cluster_addr = new_data_addr & L2_TABLE_OFFSET_MASK; + } else { + need_check = true; } - l2_table - .borrow_mut() - .set_entry_map(l2_index as usize, l2_entry)?; + + if need_check && self.check_overlap(0, cluster_addr, nbytes) != 0 { + bail!( + "Failed to check overlap when getting host offset, addr: 0x{:x}, size: {}", + cluster_addr, + nbytes + ); + } + if l2_entry != old_l2_entry { + self.table + .update_l2_table(l2_table, l2_index as usize, l2_entry)?; + } + Ok(cluster_addr + self.offset_into_cluster(guest_offset)) } @@ -415,7 +459,7 @@ impl Qcow2Driver { if l1_entry & QCOW2_OFFSET_COPIED == 0 { // Alloc a new l2_table. let old_l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; - let new_l2_offset = self.alloc_cluster(1, true)?; + let new_l2_offset = self.alloc_cluster(1, false)?; let l2_cluster: Vec = if let Some(entry) = self.table.get_l2_table_cache_entry(guest_offset) { entry.borrow().get_value().to_vec() @@ -432,7 +476,7 @@ impl Qcow2Driver { l2_cluster, ENTRY_SIZE_U64, )?)); - self.table.update_l2_table(l2_cache_entry)?; + self.table.cache_l2_table(l2_cache_entry)?; // Update l1_table. self.table @@ -464,7 +508,7 @@ impl Qcow2Driver { l2_cluster, ENTRY_SIZE_U64, )?)); - self.table.update_l2_table(l2_table_entry.clone())?; + self.table.cache_l2_table(l2_table_entry.clone())?; Ok(l2_table_entry) } @@ -476,7 +520,7 @@ impl Qcow2Driver { bail!("Buffer size: is out of range",); } // Return if the address is not allocated. - let host_offset = self.host_offset_for_write(guest_offset)?; + let host_offset = self.host_offset_for_write(guest_offset, buf.len() as u64)?; self.sync_aio.borrow_mut().write_buffer(host_offset, buf)?; Ok(()) } @@ -512,9 +556,8 @@ impl Qcow2Driver { continue; } - table_entry - .borrow_mut() - .set_entry_map(new_l2_index as usize, new_l2_entry)?; + self.table + .update_l2_table(table_entry.clone(), new_l2_index as usize, new_l2_entry)?; if unmap { self.qcow2_free_cluster(old_l2_entry, &Qcow2DiscardType::Request)?; } @@ -554,9 +597,9 @@ impl Qcow2Driver { } // Update l2 entry. - table_entry - .borrow_mut() - .set_entry_map(new_l2_index as usize, new_l2_entry)?; + self.table + .update_l2_table(table_entry.clone(), new_l2_index as usize, new_l2_entry)?; + // Decrease the refcount. self.qcow2_free_cluster(old_l2_entry, discard_type)?; } @@ -622,6 +665,15 @@ impl Qcow2Driver { let size = clusters * self.header.cluster_size(); let addr = self.refcount.alloc_cluster(&mut self.header, size)?; + let ret = self.check_overlap(0, addr, size); + if ret != 0 { + bail!( + "Failed to check overlap when allocing clusterk, ret is {}, addr: 0x{:x}, size: {}", + ret, + addr, + size + ); + } if write_zero && addr < self.driver.disk_size()? { let ret = raw_write_zeroes(self.sync_aio.borrow_mut().fd, addr as usize, size); if ret < 0 { @@ -714,6 +766,8 @@ impl Qcow2Driver { // Flush the cache of the refcount block and l2 table. self.flush()?; + self.table.save_l1_table()?; + // Update the snapshot information in qcow2 header. self.update_snapshot_info_in_header(new_snapshots_offset, false)?; @@ -747,14 +801,6 @@ impl Qcow2Driver { let l1_table_len = self.header.l1_size as u64 * ENTRY_SIZE; let l1_table_clusters = bytes_to_clusters(l1_table_len, cluster_size).unwrap(); let new_l1_table_offset = self.alloc_cluster(l1_table_clusters, true)?; - let ret = self.check_overlap(0, new_l1_table_offset, l1_table_len); - if ret != 0 { - bail!( - "Allocated snapshot L1 table addr {:x} is illegal, ret is {}!", - new_l1_table_offset, - ret - ); - } self.sync_aio .borrow_mut() .write_ctrl_cluster(new_l1_table_offset, &self.table.l1_table)?; @@ -805,6 +851,8 @@ impl Qcow2Driver { // Flush the cache of the refcount block and l1/l2 table. self.flush()?; + self.table.save_l1_table()?; + // Update snapshot offset and num in qcow2 header. self.update_snapshot_info_in_header(new_snapshots_offset, true)?; @@ -879,14 +927,14 @@ impl Qcow2Driver { l2_cluster, ENTRY_SIZE_U64, )?)); - self.table.update_l2_table(l2_table_entry)?; + self.table.cache_l2_table(l2_table_entry)?; } let cached_l2_table = self.table.l2_table_cache.get(l2_table_offset).unwrap(); - let mut borrowed_table = cached_l2_table.borrow_mut(); - - for idx in 0..borrowed_table.get_entry_num() { - let l2_entry = borrowed_table.get_entry_map(idx)?; + let entry_num = cached_l2_table.borrow().get_entry_num(); + let cloned_table = cached_l2_table.clone(); + for idx in 0..entry_num { + let l2_entry = cloned_table.borrow().get_entry_map(idx)?; let mut new_l2_entry = l2_entry & !QCOW2_OFFSET_COPIED; let data_cluster_offset = new_l2_entry & L2_TABLE_OFFSET_MASK; if data_cluster_offset == 0 { @@ -918,10 +966,10 @@ impl Qcow2Driver { new_l2_entry |= QCOW2_OFFSET_COPIED; } if l2_entry != new_l2_entry { - borrowed_table.set_entry_map(idx, new_l2_entry)?; + self.table + .update_l2_table(cloned_table.clone(), idx, new_l2_entry)?; } } - drop(borrowed_table); if added != 0 { // Update L2 table cluster refcount. @@ -988,7 +1036,7 @@ impl Qcow2Driver { snap_strs } - // Check if there exist intersection between given address range and qcow2 mede data. + // Check if there exist intersection between given address range and qcow2 metadata. fn check_overlap(&self, ignore: u64, offset: u64, size: u64) -> i64 { let check = DEFAULT_QCOW2_METADATA_OVERLAP_CHECK & !ignore; if check == 0 { @@ -1168,12 +1216,19 @@ impl Qcow2Driver { } } - self.refcount - .flush() - .unwrap_or_else(|e| error!("Flush refcount block failed: {:?}", e)); - self.table - .flush_l2_table_cache() - .unwrap_or_else(|e| error!("Flush l2 table cache failed: {:?}", e)); + self.table.flush().unwrap_or_else(|e| { + error!( + "Flush l2 table cache failed while discarding clusters, {:?}", + e + ) + }); + self.refcount.flush().unwrap_or_else(|e| { + error!( + "Flush refcount block failed when discarding clusters, {:?}", + e + ) + }); + self.process_discards(args, OpCode::Discard, false) } @@ -1200,8 +1255,11 @@ impl Qcow2Driver { } self.table - .flush_l2_table_cache() - .unwrap_or_else(|e| error!("Flush l2 table cache failed: {:?}", e)); + .flush() + .unwrap_or_else(|e| error!("Flush l2 table cache failed when writing zeroes, {:?}", e)); + self.refcount + .flush() + .unwrap_or_else(|e| error!("Flush refcount block failed when writing zeroes, {:?}", e)); Ok(()) } @@ -1279,7 +1337,7 @@ impl BlockDriverOps for Qcow2Driver { while copied < nbytes { let pos = offset as u64 + copied; let count = self.cluster_aligned_bytes(pos, nbytes - copied); - let host_offset = self.host_offset_for_write(pos)?; + let host_offset = self.host_offset_for_write(pos, count)?; if let Some(end) = req_list.last_mut() { if end.offset + end.nbytes == host_offset { end.nbytes += count; @@ -1310,6 +1368,8 @@ impl BlockDriverOps for Qcow2Driver { } fn datasync(&mut self, completecb: T) -> Result<()> { + self.flush() + .unwrap_or_else(|e| error!("Flush failed when syncing data, {:?}", e)); self.driver.datasync(completecb) } @@ -1389,7 +1449,6 @@ impl BlockDriverOps for Qcow2Driver { } fn flush_request(&mut self) -> Result<()> { - self.flush()?; self.driver.flush_request() } @@ -2235,7 +2294,7 @@ mod test { let l2_table = Rc::new(RefCell::new( CacheTable::new(l2_address, l2_cluster, ENTRY_SIZE_U64).unwrap(), )); - qcow2_driver.table.update_l2_table(l2_table).unwrap(); + qcow2_driver.table.cache_l2_table(l2_table).unwrap(); } // All used l2 table will be cached for it's little data size in these tests. @@ -2278,7 +2337,7 @@ mod test { // 2) Refcount is right. for case in &case_list { let host_offset = get_host_offset(&mut qcow2_driver, case.offset as u64); - assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 2); + assert_eq!(qcow2_driver.refcount.get_refcount(host_offset).unwrap(), 1); } // 3) L1 table refcount is right. assert_eq!( @@ -2300,7 +2359,7 @@ mod test { .refcount .get_refcount(*l1_entry & L1_TABLE_OFFSET_MASK) .unwrap(), - 2 + 1 ); } } diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 4ac62dee3..5e44a2998 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -157,6 +157,7 @@ impl RefCount { 0, self.cluster_size, )?; + borrow_entry.dirty_info.clear(); // Write new extended refcount table to disk. let size = self.refcount_table.len() + (self.cluster_size / ENTRY_SIZE) as usize; @@ -279,24 +280,8 @@ impl RefCount { rc_vec.len() } - pub fn flush(&self) -> Result<()> { - for (_, entry) in self.refcount_blk_cache.iter() { - let mut borrowed_entry = entry.borrow_mut(); - if !borrowed_entry.dirty_info.is_dirty { - continue; - } - let ret = self.sync_aio.borrow_mut().write_dirty_info( - borrowed_entry.addr, - borrowed_entry.get_value(), - borrowed_entry.dirty_info.start, - borrowed_entry.dirty_info.end, - ); - if let Err(err) = ret { - error!("Flush refcount table cache failed, {}", err.to_string()); - } - borrowed_entry.dirty_info.clear(); - } - Ok(()) + pub fn flush(&mut self) -> Result<()> { + self.refcount_blk_cache.flush(self.sync_aio.clone()) } fn set_refcount( @@ -322,6 +307,7 @@ impl RefCount { let mut rb_vec = Vec::new(); let mut borrowed_entry = cache_entry.borrow_mut(); + let is_dirty = borrowed_entry.dirty_info.is_dirty; for i in 0..clusters { let mut rc_value = borrowed_entry.get_entry_map(rb_idx as usize + i)? as u16; rc_value = if is_add { @@ -361,6 +347,9 @@ impl RefCount { for (idx, rc_value) in rb_vec.iter().enumerate() { borrowed_entry.set_entry_map(rb_idx as usize + idx, *rc_value as u64)?; } + if !is_dirty { + self.refcount_blk_cache.add_dirty_table(cache_entry.clone()); + } Ok(()) } @@ -467,6 +456,7 @@ impl RefCount { 0, self.cluster_size, )?; + borrow_entry.dirty_info.clear(); drop(borrow_entry); if let Some(replaced_entry) = self.refcount_blk_cache.lru_replace(rt_idx, cache_entry) { self.save_refcount_block(&replaced_entry)?; @@ -541,7 +531,8 @@ impl RefCount { } let addr = self.find_free_cluster(header, size)?; let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); - self.update_refcount(addr, clusters, 1, true, &Qcow2DiscardType::Other)?; + self.update_refcount(addr, clusters, 1, false, &Qcow2DiscardType::Other)?; + Ok(addr) } @@ -577,7 +568,7 @@ impl RefCount { } fn save_refcount_block(&mut self, entry: &Rc>) -> Result<()> { - let borrowed_entry = entry.borrow(); + let mut borrowed_entry = entry.borrow_mut(); if !borrowed_entry.dirty_info.is_dirty { return Ok(()); } @@ -592,7 +583,10 @@ impl RefCount { borrowed_entry.get_value(), borrowed_entry.dirty_info.start, borrowed_entry.dirty_info.end, - ) + )?; + borrowed_entry.dirty_info.clear(); + + Ok(()) } pub fn drop_dirty_caches(&mut self) { @@ -712,6 +706,7 @@ mod test { 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); let addr = qcow2.alloc_cluster(1, true).unwrap(); assert_eq!(addr, cluster_sz * free_cluster_index as u64); + qcow2.flush().unwrap(); // Check if the refcount of the cluster is updated to the disk. let mut rc_value = [0_u8; 2]; cloned_file @@ -746,6 +741,7 @@ mod test { for _ in 0..clusters + 1 { qcow2.alloc_cluster(2, true).unwrap(); } + qcow2.flush().unwrap(); let new_rct_offset = qcow2.header.refcount_table_offset; let new_rct_clusters = qcow2.header.refcount_table_clusters; assert_ne!(new_rct_offset, rct_offset); diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 571dd200a..ef787db8f 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -192,7 +192,22 @@ impl Qcow2Table { self.l1_table[l1_index] = l2_address; } - pub fn update_l2_table(&mut self, l2_table_entry: Rc>) -> Result<()> { + pub fn update_l2_table( + &mut self, + table: Rc>, + index: usize, + entry: u64, + ) -> Result<()> { + let is_dirty = table.borrow().dirty_info.is_dirty; + table.borrow_mut().set_entry_map(index, entry)?; + if !is_dirty { + self.l2_table_cache.add_dirty_table(table); + } + + Ok(()) + } + + pub fn cache_l2_table(&mut self, l2_table_entry: Rc>) -> Result<()> { let l2_entry_addr = l2_table_entry.borrow().addr; if self.l2_table_cache.contains_keys(l2_entry_addr) { self.l2_table_cache.cache_map.remove(&l2_entry_addr); @@ -215,26 +230,8 @@ impl Qcow2Table { Ok(()) } - pub fn flush_l2_table_cache(&self) -> Result<()> { - for (_idx, entry) in self.l2_table_cache.iter() { - let mut borrowed_entry = entry.borrow_mut(); - if !borrowed_entry.dirty_info.is_dirty { - continue; - } - self.sync_aio.borrow_mut().write_dirty_info( - borrowed_entry.addr, - borrowed_entry.get_value(), - borrowed_entry.dirty_info.start, - borrowed_entry.dirty_info.end, - )?; - borrowed_entry.dirty_info.clear(); - } - Ok(()) - } - pub fn flush(&mut self) -> Result<()> { - self.flush_l2_table_cache()?; - self.save_l1_table() + self.l2_table_cache.flush(self.sync_aio.clone()) } pub fn drop_dirty_caches(&mut self) { @@ -265,10 +262,13 @@ mod test { let l2_table = Rc::new(RefCell::new( CacheTable::new(addr, l2_cluster.clone(), ENTRY_SIZE_U64).unwrap(), )); - qcow2.table.update_l2_table(l2_table.clone()).unwrap(); + qcow2.table.cache_l2_table(l2_table.clone()).unwrap(); let test_val1 = 0xff00ff00_u64; - l2_table.borrow_mut().set_entry_map(0, test_val1).unwrap(); + qcow2 + .table + .update_l2_table(l2_table.clone(), 0, test_val1) + .unwrap(); let res = l2_table.borrow_mut().get_entry_map(0).unwrap(); assert_eq!(res, test_val1); @@ -278,14 +278,14 @@ mod test { assert_eq!(buf, [0_u8; ENTRY_SIZE_U64]); let test_val2 = 0x00ff00ff_u64; - l2_table - .borrow_mut() - .set_entry_map(8191, test_val2) + qcow2 + .table + .update_l2_table(l2_table.clone(), 8191, test_val2) .unwrap(); let res = l2_table.borrow_mut().get_entry_map(8191).unwrap(); assert_eq!(res, test_val2); - qcow2.table.flush_l2_table_cache().unwrap(); + qcow2.table.flush().unwrap(); image.file.seek(SeekFrom::Start(addr)).unwrap(); let mut buf = vec![0_u8; ENTRY_SIZE_U64]; image.file.read_exact(&mut buf).unwrap(); -- Gitee From ad2eb51a38195dd6aeb493e72ea14dea17179331 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jul 2023 04:53:21 +0800 Subject: [PATCH 1294/2187] util: extract a common function to check if two regions intersect Extract a common function ranges_overlap to check if two regions intersect. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 73 +++++++++++----------- devices/src/pci/config.rs | 14 +++-- devices/src/pci/mod.rs | 24 ------- devices/src/pci/msix.rs | 13 ++-- devices/src/pci/root_port.rs | 20 +++--- machine/src/standard_vm/x86_64/ich9_lpc.rs | 6 +- machine/src/standard_vm/x86_64/mch.rs | 7 ++- util/src/num_ops.rs | 40 ++++++++++++ vfio/src/vfio_pci.rs | 15 +++-- virtio/src/transport/virtio_pci.rs | 7 ++- 10 files changed, 124 insertions(+), 95 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 3644e3889..06dc28a7c 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -51,7 +51,7 @@ use util::{ get_iov_size, iovec_write_zero, iovecs_split, raw_write_zeroes, Aio, AioCb, AioEngine, Iovec, OpCode, }, - num_ops::{div_round_up, round_down, round_up}, + num_ops::{div_round_up, ranges_overlap, round_down, round_up}, time::{get_format_time, gettime}, }; @@ -1051,81 +1051,91 @@ impl Qcow2Driver { self.refcount.offset_into_cluster(offset) + size, self.header.cluster_size(), ) - .unwrap(); - let offset = self.refcount.start_of_cluster(offset); - if u64::MAX - offset < size { + .unwrap() as usize; + let offset = self.refcount.start_of_cluster(offset) as usize; + if usize::MAX - offset < size { // Ensure there exist no overflow. return -1; } + // SAFETY: all tables have been assigned, indicating that their addresses are reasonable. if check & METADATA_OVERLAP_CHECK_ACTIVEL1 != 0 && self.header.l1_size != 0 - && check_overlaps( + && ranges_overlap( offset, size, - self.header.l1_table_offset, - self.header.l1_size as u64 * ENTRY_SIZE, + self.header.l1_table_offset as usize, + self.header.l1_size as usize * ENTRY_SIZE as usize, ) + .unwrap() { return METADATA_OVERLAP_CHECK_ACTIVEL1 as i64; } if check & METADATA_OVERLAP_CHECK_ACTIVEL2 != 0 { for l1_entry in &self.table.l1_table { - if check_overlaps( + if ranges_overlap( offset, size, - l1_entry & L1_TABLE_OFFSET_MASK, - self.header.cluster_size(), - ) { + (l1_entry & L1_TABLE_OFFSET_MASK) as usize, + self.header.cluster_size() as usize, + ) + .unwrap() + { return METADATA_OVERLAP_CHECK_ACTIVEL2 as i64; } } } if check & METADATA_OVERLAP_CHECK_REFCOUNTTABLE != 0 - && check_overlaps( + && ranges_overlap( offset, size, - self.header.refcount_table_offset, - self.header.refcount_table_clusters as u64 * self.header.cluster_size(), + self.header.refcount_table_offset as usize, + self.header.refcount_table_clusters as usize * self.header.cluster_size() as usize, ) + .unwrap() { return METADATA_OVERLAP_CHECK_REFCOUNTTABLE as i64; } if check & METADATA_OVERLAP_CHECK_REFCOUNTBLOCK != 0 { for block_offset in &self.refcount.refcount_table { - if check_overlaps( + if ranges_overlap( offset, size, - block_offset & REFCOUNT_TABLE_OFFSET_MASK, - self.header.cluster_size(), - ) { + (block_offset & REFCOUNT_TABLE_OFFSET_MASK) as usize, + self.header.cluster_size() as usize, + ) + .unwrap() + { return METADATA_OVERLAP_CHECK_REFCOUNTBLOCK as i64; } } } if check & METADATA_OVERLAP_CHECK_SNAPSHOTTABLE != 0 - && check_overlaps( + && ranges_overlap( offset, size, - self.snapshot.snapshot_table_offset, - self.snapshot.snapshot_size, + self.snapshot.snapshot_table_offset as usize, + self.snapshot.snapshot_size as usize, ) + .unwrap() { return METADATA_OVERLAP_CHECK_SNAPSHOTTABLE as i64; } if check & METADATA_OVERLAP_CHECK_INACTIVEL1 != 0 { for snap in &self.snapshot.snapshots { - if check_overlaps( + if ranges_overlap( offset, size, - snap.l1_table_offset, - snap.l1_size as u64 * ENTRY_SIZE, - ) { + snap.l1_table_offset as usize, + snap.l1_size as usize * ENTRY_SIZE as usize, + ) + .unwrap() + { return METADATA_OVERLAP_CHECK_INACTIVEL1 as i64; } } @@ -1140,19 +1150,6 @@ pub fn bytes_to_clusters(size: u64, cluster_sz: u64) -> Result { .with_context(|| format!("Failed to div round up, size is {}", size)) } -// Check if there is an intersection between two addresses. Return true if there is a crossover. -fn check_overlaps(addr1: u64, size1: u64, addr2: u64, size2: u64) -> bool { - // Note: ensure that there is no overflow at the calling function. - if addr1 + size1 == 0 || addr2 + size2 == 0 { - return false; - } - - let last1 = addr1 + size1 - 1; - let last2 = addr2 + size2 - 1; - - !(last1 < addr2 || last2 < addr1) -} - pub trait InternalSnapshotOps: Send + Sync { fn create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()>; fn delete_snapshot(&mut self, name: String) -> Result; diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 1afaa001b..05a952802 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -20,10 +20,10 @@ use crate::pci::intx::Intx; use crate::pci::msix::{is_msix_enabled, Msix, MSIX_TABLE_ENTRY_SIZE}; use crate::pci::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, - pci_ext_cap_next, PciBus, BDF_FUNC_SHIFT, + pci_ext_cap_next, PciBus, PciError, BDF_FUNC_SHIFT, }; -use crate::pci::{ranges_overlap, PciError}; use address_space::Region; +use util::num_ops::ranges_overlap; /// Size in bytes of the configuration space of legacy PCI device. pub const PCI_CONFIG_SPACE_SIZE: usize = 256; @@ -521,7 +521,8 @@ impl PciConfig { } let size = buf.len(); - if ranges_overlap(offset, size, STATUS as usize, 1) { + // SAFETY: checked in "validate_config_boundary". + if ranges_overlap(offset, size, STATUS as usize, 1).unwrap() { if let Some(intx) = &self.intx { if intx.lock().unwrap().level == 1 { self.config[STATUS as usize] |= STATUS_INTERRUPT; @@ -588,10 +589,11 @@ impl PciConfig { }; let size = data.len(); - let cmd_overlap = ranges_overlap(old_offset, size, COMMAND as usize, 1); + // SAFETY: checked in "validate_config_boundary". + let cmd_overlap = ranges_overlap(old_offset, size, COMMAND as usize, 1).unwrap(); if cmd_overlap - || ranges_overlap(old_offset, size, BAR_0 as usize, REG_SIZE * bar_num) - || ranges_overlap(old_offset, size, rom_addr, 4) + || ranges_overlap(old_offset, size, BAR_0 as usize, REG_SIZE * bar_num).unwrap() + || ranges_overlap(old_offset, size, rom_addr, 4).unwrap() { if let Err(e) = self.update_bar_mapping( #[cfg(target_arch = "x86_64")] diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 301955ecf..b19abe01c 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -351,20 +351,6 @@ pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { ((pci_slot + pin) % PCI_PIN_NUM) as u32 } -/// Check whether two regions overlap with each other. -/// -/// # Arguments -/// -/// * `start` - Start address of the first region. -/// * `end` - End address of the first region. -/// * `region_start` - Start address of the second region. -/// * `region_end` - End address of the second region. -pub fn ranges_overlap(start: usize, size: usize, range_start: usize, range_size: usize) -> bool { - let end = start + size; - let range_end = range_start + range_size; - !(start >= range_end || range_start >= end) -} - #[cfg(test)] mod tests { use crate::DeviceBase; @@ -411,16 +397,6 @@ mod tests { assert!(le_write_u64(&mut buf, 1, 0x1234_5678_9abc_deff).is_err()); } - #[test] - fn test_ranges_overlap() { - assert!(ranges_overlap(100, 100, 150, 100)); - assert!(ranges_overlap(100, 100, 150, 50)); - assert!(!ranges_overlap(100, 100, 200, 50)); - assert!(ranges_overlap(100, 100, 100, 50)); - assert!(!ranges_overlap(100, 100, 50, 50)); - assert!(ranges_overlap(100, 100, 50, 100)); - } - #[test] fn set_dev_id() { struct PciDev { diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 4fb060e60..761ea9ffe 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -23,8 +23,7 @@ use crate::pci::config::{ CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM, }; use crate::pci::{ - le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, - ranges_overlap, PciBus, + le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, PciBus, }; use address_space::{GuestAddress, Region, RegionOps}; use hypervisor::kvm::{MsiVector, KVM_FDS}; @@ -34,7 +33,7 @@ use migration::{ use migration_derive::{ByteCode, Desc}; use util::{ byte_code::ByteCode, - num_ops::round_up, + num_ops::{ranges_overlap, round_up}, test_helper::{add_msix_msg, is_test_enabled}, }; @@ -457,7 +456,9 @@ impl Msix { let len = data.len(); let msix_cap_control_off: usize = self.msix_cap_offset as usize + MSIX_CAP_CONTROL as usize; // Only care about the bits Masked(14) & Enabled(15) in msix control register. - if !ranges_overlap(offset, len, msix_cap_control_off + 1, 1) { + // SAFETY: msix_cap_control_off is less than u16::MAX. + // Offset and len have been checked in call function PciConfig::write. + if !ranges_overlap(offset, len, msix_cap_control_off + 1, 1).unwrap() { return; } @@ -654,7 +655,9 @@ pub fn init_msix( table_size as usize, pba_offset as usize, pba_size as usize, - ) { + ) + .unwrap() + { bail!("msix table and pba table overlapped."); } le_write_u32(&mut config.config, offset, table_offset | bar_id as u32)?; diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 373c17576..71be40fd8 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -35,8 +35,7 @@ use crate::pci::intx::init_intx; use crate::pci::msix::init_msix; use crate::pci::{init_multifunction, PciDevBase, PciError, PciIntxState, INTERRUPT_PIN}; use crate::pci::{ - le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, ranges_overlap, - PciDevOps, + le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, PciDevOps, }; use crate::{Device, DeviceBase}; use address_space::Region; @@ -45,7 +44,7 @@ use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::byte_code::ByteCode; +use util::{byte_code::ByteCode, num_ops::ranges_overlap}; const DEVICE_ID_RP: u16 = 0x000c; @@ -257,7 +256,8 @@ impl RootPort { fn correct_race_unplug(&mut self, offset: usize, data: &[u8], old_status: u16) { let size = data.len(); let cap_offset = self.base.config.pci_express_cap_offset; - if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTSTA) as usize, 2) { + // SAFETY: Checked in write_config. + if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTSTA) as usize, 2).unwrap() { return; } @@ -285,7 +285,8 @@ impl RootPort { let size = data.len(); let cap_offset = self.base.config.pci_express_cap_offset; // Only care the write config about slot control - if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTCTL) as usize, 2) { + // SAFETY: Checked in write_config. + if !ranges_overlap(offset, size, (cap_offset + PCI_EXP_SLTCTL) as usize, 2).unwrap() { return; } @@ -434,6 +435,7 @@ impl PciDevOps for RootPort { fn write_config(&mut self, offset: usize, data: &[u8]) { let size = data.len(); + // SAFETY: offset is no more than 0xfff. let end = offset + size; if end > PCIE_CONFIG_SPACE_SIZE || size > 4 { error!( @@ -477,9 +479,9 @@ impl PciDevOps for RootPort { } } - if ranges_overlap(offset, size, COMMAND as usize, 1) - || ranges_overlap(offset, size, IO_BASE as usize, 2) - || ranges_overlap(offset, size, MEMORY_BASE as usize, 20) + if ranges_overlap(offset, size, COMMAND as usize, 1).unwrap() + || ranges_overlap(offset, size, IO_BASE as usize, 2).unwrap() + || ranges_overlap(offset, size, MEMORY_BASE as usize, 20).unwrap() { self.register_region(); } @@ -490,7 +492,7 @@ impl PciDevOps for RootPort { ) .unwrap(); let exp_slot_status = (cap_offset + PCI_EXP_SLTSTA) as usize; - if ranges_overlap(offset, size, exp_slot_status, 2) { + if ranges_overlap(offset, size, exp_slot_status, 2).unwrap() { let new_status = le_read_u16(data, 0).unwrap(); if new_status & !old_status & PCI_EXP_SLOTSTA_EVENTS != 0 { status = (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs index 4ccce0ca6..3022839c6 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs @@ -28,10 +28,11 @@ use devices::pci::config::{ HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use devices::pci::{ - le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, + le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; use devices::{Device, DeviceBase}; use util::byte_code::ByteCode; +use util::num_ops::ranges_overlap; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; @@ -303,7 +304,8 @@ impl PciDevOps for LPCBridge { fn write_config(&mut self, offset: usize, data: &[u8]) { self.base.config.write(offset, data, 0, None, None); - if ranges_overlap(offset, data.len(), PM_BASE_OFFSET as usize, 4) { + // SAFETY: offset is no more than 0xfff. + if ranges_overlap(offset, data.len(), PM_BASE_OFFSET as usize, 4).unwrap() { if let Err(e) = self.update_pm_base() { error!("Failed to update PM base addr: {:?}", e); } diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/standard_vm/x86_64/mch.rs index f679efe9d..104eeacb0 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/standard_vm/x86_64/mch.rs @@ -22,9 +22,10 @@ use devices::pci::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }, - le_read_u64, le_write_u16, ranges_overlap, PciBus, PciDevBase, PciDevOps, Result as PciResult, + le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, Result as PciResult, }; use devices::{Device, DeviceBase}; +use util::num_ops::ranges_overlap; const DEVICE_ID_INTEL_Q35_MCH: u16 = 0x29c0; @@ -172,8 +173,8 @@ impl PciDevOps for Mch { fn write_config(&mut self, offset: usize, data: &[u8]) { let old_pciexbar: u64 = le_read_u64(&self.base.config.config, PCIEXBAR as usize).unwrap(); self.base.config.write(offset, data, 0, None, None); - - if ranges_overlap(offset, data.len(), PCIEXBAR as usize, 8) + // SAFETY: offset is no more than 0xfff. + if ranges_overlap(offset, data.len(), PCIEXBAR as usize, 8).unwrap() && self.check_pciexbar_update(old_pciexbar) { if let Err(e) = self.update_pciexbar_mapping() { diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 3a42533cc..9db94e5d0 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -442,6 +442,35 @@ pub fn str_to_usize(string_in: String) -> Result { Ok(num) } +/// Check whether two regions overlap with each other. +/// +/// # Arguments +/// +/// * `start1` - Start address of the first region. +/// * `size1` - Size of the first region. +/// * `start2` - Start address of the second region. +/// * `size2` - Size of the second region. +/// +/// # Examples +/// +/// ```rust +/// extern crate util; +/// use util::num_ops::ranges_overlap; +/// +/// let value = ranges_overlap(100, 100, 150, 100).unwrap(); +/// assert!(value == true); +/// ``` +pub fn ranges_overlap(start1: usize, size1: usize, start2: usize, size2: usize) -> Result { + let end1 = start1 + .checked_add(size1) + .with_context(|| format!("range 1 overflows: start {}, size {}", start1, size1))?; + let end2 = start2 + .checked_add(size2) + .with_context(|| format!("range 2 overflows: start {}, size {}", start1, size1))?; + + Ok(!(start1 >= end2 || start2 >= end1)) +} + #[cfg(test)] mod test { use super::*; @@ -618,4 +647,15 @@ mod test { let value = str_to_usize("17".to_string()).unwrap(); assert!(value == 17); } + + #[test] + fn test_ranges_overlap() { + assert!(ranges_overlap(100, 100, 150, 100).unwrap()); + assert!(ranges_overlap(100, 100, 150, 50).unwrap()); + assert!(!ranges_overlap(100, 100, 200, 50).unwrap()); + assert!(ranges_overlap(100, 100, 100, 50).unwrap()); + assert!(!ranges_overlap(100, 100, 50, 50).unwrap()); + assert!(ranges_overlap(100, 100, 50, 100).unwrap()); + assert!(ranges_overlap(usize::MAX, 1, 100, 50).is_err()) + } } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index be7ab0cc6..903281b73 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -42,10 +42,11 @@ use devices::pci::msix::{ }; use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, - pci_ext_cap_next, pci_ext_cap_ver, ranges_overlap, PciBus, PciDevBase, PciDevOps, + pci_ext_cap_next, pci_ext_cap_ver, PciBus, PciDevBase, PciDevOps, }; use devices::{Device, DeviceBase}; use hypervisor::kvm::{MsiVector, KVM_FDS}; +use util::num_ops::ranges_overlap; use util::unix::host_page_size; const PCI_NUM_BARS: u8 = 6; @@ -907,6 +908,7 @@ impl PciDevOps for VfioPciDevice { /// Read pci data from pci config if it emulate, otherwise read from vfio device. fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); + // SAFETY: offset is no more than 0xfff. let end = offset + size; if end > (self.config_size as usize) || size > 4 { error!( @@ -919,9 +921,9 @@ impl PciDevOps for VfioPciDevice { // BAR, header_type and extended caps are always controlled by StratoVirt. let bars_size = (BAR_5 - BAR_0) as usize + REG_SIZE; let ext_cfg_size = PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE; - if ranges_overlap(offset, size, BAR_0 as usize, bars_size) - || ranges_overlap(offset, size, HEADER_TYPE as usize, 2) - || ranges_overlap(offset, size, PCI_CONFIG_SPACE_SIZE, ext_cfg_size) + if ranges_overlap(offset, size, BAR_0 as usize, bars_size).unwrap() + || ranges_overlap(offset, size, HEADER_TYPE as usize, 2).unwrap() + || ranges_overlap(offset, size, PCI_CONFIG_SPACE_SIZE, ext_cfg_size).unwrap() { self.base.config.read(offset, data); return; @@ -947,6 +949,7 @@ impl PciDevOps for VfioPciDevice { /// Write data to pci config and vfio device at the same time fn write_config(&mut self, offset: usize, data: &[u8]) { let size = data.len(); + // SAFETY: offset is no more than 0xfff. let end = offset + size; if end > (self.config_size as usize) || size > 4 { error!( @@ -985,7 +988,7 @@ impl PciDevOps for VfioPciDevice { Some(&locked_parent_bus.mem_region), ); - if ranges_overlap(offset, size, COMMAND as usize, REG_SIZE) { + if ranges_overlap(offset, size, COMMAND as usize, REG_SIZE).unwrap() { if le_read_u32(&self.base.config.config, offset).unwrap() & COMMAND_MEMORY_SPACE as u32 != 0 { @@ -993,7 +996,7 @@ impl PciDevOps for VfioPciDevice { error!("Failed to map bar regions, error is {:?}", e); } } - } else if ranges_overlap(offset, size, cap_offset, MSIX_CAP_SIZE as usize) { + } else if ranges_overlap(offset, size, cap_offset, MSIX_CAP_SIZE as usize).unwrap() { let is_enable = is_msix_enabled(cap_offset, &self.base.config.config); if !was_enable && is_enable { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index c39cf0bd4..8507e5f7c 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -43,12 +43,13 @@ use devices::pci::config::{ use devices::pci::msix::{update_dev_id, MsixState}; use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, - ranges_overlap, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, + PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, }; use devices::{Device, DeviceBase}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::num_ops::ranges_overlap; use util::num_ops::{read_data_u32, write_data_u32}; use util::offset_of; @@ -855,7 +856,9 @@ impl VirtioPciDevice { let pci_cfg_data_offset = self.cfg_cap_offset + offset_of!(VirtioPciCfgAccessCap, pci_cfg_data); let cap_size = size_of::(); - if !ranges_overlap(start, end - start, pci_cfg_data_offset, cap_size) { + // SAFETY: pci_cfg_data_offset is the offset of VirtioPciCfgAccessCap in Pci config space + // which is much less than u16::MAX. + if !ranges_overlap(start, end - start, pci_cfg_data_offset, cap_size).unwrap() { return; } -- Gitee From 95804c516978e17e895ab696fcc0b44b69cd16c8 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Wed, 9 Aug 2023 22:56:04 +0800 Subject: [PATCH 1295/2187] file: change lock to fcntl() use fcntl() to lock file instead of flock(). Signed-off-by: mayunlong --- util/src/file.rs | 80 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/util/src/file.rs b/util/src/file.rs index 942edb2e9..f0ddd8c59 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -19,6 +19,16 @@ use anyhow::{bail, Context, Ok, Result}; const MIN_FILE_ALIGN: u32 = 512; const MAX_FILE_ALIGN: u32 = 4096; +/// Permission to read +const FILE_LOCK_READ: u64 = 0x01; +/// Permission to write +const FILE_LOCK_WRITE: u64 = 0x02; +/// All permissions +const FILE_LOCK_ALL:[u64; 2] = [FILE_LOCK_READ, FILE_LOCK_WRITE]; +/// Permission lock base address, consistent with qemu +const LOCK_PERM_BASE: u64 = 100; +/// Shared lock base address, consistent with qemu +const LOCK_SHARED_BASE: u64 = 200; pub fn open_file(path: &str, read_only: bool, direct: bool) -> Result { let mut options = OpenOptions::new(); @@ -94,41 +104,75 @@ pub fn get_file_alignment(file: &File, direct: bool) -> (u32, u32) { (req_align, buf_align) } -pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { - let (lockop, lockname) = if read_only { - (libc::LOCK_SH | libc::LOCK_NB, "read lock") - } else { - (libc::LOCK_EX | libc::LOCK_NB, "write lock") - }; +fn do_fcntl_lock( + file: &File, + path: &str, + lockname: &str, + flock: libc::flock, + is_lock: bool, +) -> Result<()> { // SAFETY: the file has a valid raw fd. - let ret = unsafe { libc::flock(file.as_raw_fd(), lockop) }; - if ret < 0 { + let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLK, &flock) }; + if ret >= 0 { + return Ok(()); + } + + if is_lock { bail!( "Failed to get {} on file: {}. Is it used more than once or \ - another process using the same file?. Error: {}", + another process using the same file? Error: {}", lockname, path, std::io::Error::last_os_error(), ); - } - - Ok(()) -} - -pub fn unlock_file(file: &File, path: &str) -> Result<()> { - // SAFETY: the file has a valid raw fd. - let ret = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_UN) }; - if ret < 0 { + } else { bail!( "Failed to release lock on file: {}. Error: {}", path, std::io::Error::last_os_error(), ); } +} + +fn lock_or_unlock_file( + file: &File, + path: &str, + lock_op: i16, + lock_name: &str, + is_lock: bool, +) -> Result<()> { + let pid = unsafe { libc::getpid() }; + let mut flock = libc::flock { + l_whence: libc::SEEK_SET as i16, + l_len: 1, + l_pid: pid, + l_type: lock_op, + l_start: 0, + }; + + for lock in FILE_LOCK_ALL { + flock.l_start = (LOCK_PERM_BASE + lock) as i64; + do_fcntl_lock(file, path, lock_name, flock, is_lock)?; + } + flock.l_start = (LOCK_SHARED_BASE + FILE_LOCK_WRITE) as i64; + do_fcntl_lock(file, path, lock_name, flock, is_lock)?; Ok(()) } +pub fn lock_file(file: &File, path: &str, read_only: bool) -> Result<()> { + let (lock_op, lock_name) = if read_only { + (libc::F_RDLCK, "read lock") + } else { + (libc::F_WRLCK, "write lock") + }; + lock_or_unlock_file(file, path, lock_op as i16, lock_name, true) +} + +pub fn unlock_file(file: &File, path: &str) -> Result<()> { + lock_or_unlock_file(file, path, libc::F_UNLCK as i16, "", false) +} + pub fn clear_file(path: String) -> Result<()> { if Path::new(&path).exists() { remove_file(&path) -- Gitee From 6f3a20a18cfc66fa78138f9c587035aa070ca2f7 Mon Sep 17 00:00:00 2001 From: mayunlong Date: Thu, 10 Aug 2023 21:03:42 +0800 Subject: [PATCH 1296/2187] file: fix format errors A space is missing in the file file.rs, which causes a format error. Fix it. Signed-off-by: mayunlong --- util/src/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/file.rs b/util/src/file.rs index f0ddd8c59..836dba131 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -24,7 +24,7 @@ const FILE_LOCK_READ: u64 = 0x01; /// Permission to write const FILE_LOCK_WRITE: u64 = 0x02; /// All permissions -const FILE_LOCK_ALL:[u64; 2] = [FILE_LOCK_READ, FILE_LOCK_WRITE]; +const FILE_LOCK_ALL: [u64; 2] = [FILE_LOCK_READ, FILE_LOCK_WRITE]; /// Permission lock base address, consistent with qemu const LOCK_PERM_BASE: u64 = 100; /// Shared lock base address, consistent with qemu -- Gitee From 69a5bb8210fab7d06e8c391f88426986cf2cb069 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Wed, 9 Aug 2023 14:49:44 +0800 Subject: [PATCH 1297/2187] usb: compile some devices on musl Compile usb keyboard, tablet and xhci on musl. Signed-off-by: zhouli57 --- devices/Cargo.toml | 2 +- devices/src/usb/mod.rs | 4 ---- machine/Cargo.toml | 2 -- machine/src/lib.rs | 20 +++++--------------- machine/src/standard_vm/mod.rs | 25 ++++++++----------------- ui/Cargo.toml | 10 ++++++---- ui/src/lib.rs | 4 ++++ 7 files changed, 24 insertions(+), 43 deletions(-) diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 44c71f494..03c72cb07 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -29,9 +29,9 @@ migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } acpi = { path = "../acpi" } block_backend = { path = "../block_backend"} +ui = { path = "../ui" } [target.'cfg(not(target_env = "musl"))'.dependencies] -ui = { path = "../ui" } pulse = { version = "2.27", package = "libpulse-binding" } psimple = { version = "2.27", package = "libpulse-simple-binding" } rusb = "0.9" diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index a0dad8996..5eb011c3b 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -16,13 +16,9 @@ pub mod camera; pub mod camera_media_type_guid; pub mod config; pub mod error; -#[cfg(not(target_env = "musl"))] pub mod hid; -#[cfg(not(target_env = "musl"))] pub mod keyboard; -#[cfg(not(target_env = "musl"))] pub mod storage; -#[cfg(not(target_env = "musl"))] pub mod tablet; #[cfg(not(target_env = "musl"))] pub mod usbhost; diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 542996efc..01a06be5f 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -28,8 +28,6 @@ util = { path = "../util" } virtio = { path = "../virtio" } vfio = { path = "../vfio" } block_backend = { path = "../block_backend" } - -[target.'cfg(not(target_env = "musl"))'.dependencies] ui = { path = "../ui" } [features] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 2b350971e..7b23073e4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -52,9 +52,10 @@ use devices::misc::scream::Scream; use devices::pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(not(target_env = "musl"))] +use devices::usb::{camera::UsbCamera, usbhost::UsbHost}; use devices::usb::{ - camera::UsbCamera, keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, - usbhost::UsbHost, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, + keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, + UsbDeviceOps, }; #[cfg(target_arch = "aarch64")] use devices::InterruptController; @@ -72,9 +73,9 @@ use machine_manager::config::{ MAX_VIRTIO_QUEUE, }; #[cfg(not(target_env = "musl"))] +use machine_manager::config::{parse_gpu, parse_usb_camera, parse_usb_host}; use machine_manager::config::{ - parse_gpu, parse_usb_camera, parse_usb_host, parse_usb_keyboard, parse_usb_storage, - parse_usb_tablet, parse_xhci, + parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; @@ -643,7 +644,6 @@ pub trait MachineOps { Ok(()) } - #[cfg(not(target_env = "musl"))] fn check_id_existed_in_xhci(&mut self, id: &str) -> Result { let vm_config = self.get_vm_config(); let locked_vmconfig = vm_config.lock().unwrap(); @@ -1198,7 +1198,6 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - XHCI Configuration. - #[cfg(not(target_env = "musl"))] fn add_usb_xhci(&mut self, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let device_cfg = parse_xhci(cfg_args)?; @@ -1290,7 +1289,6 @@ pub trait MachineOps { /// /// * `vm_config` - VM configuration. /// * `usb_dev` - Usb device. - #[cfg(not(target_env = "musl"))] fn attach_usb_to_xhci_controller( &mut self, vm_config: &mut VmConfig, @@ -1315,7 +1313,6 @@ pub trait MachineOps { /// /// * `vm_config` - VM configuration. /// * `id` - id of the usb device. - #[cfg(not(target_env = "musl"))] fn detach_usb_from_xhci_controller( &mut self, vm_config: &mut VmConfig, @@ -1339,7 +1336,6 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - Keyboard Configuration. - #[cfg(not(target_env = "musl"))] fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_keyboard(cfg_args)?; // SAFETY: id is already checked not none in parse_usb_keyboard(). @@ -1356,7 +1352,6 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - Tablet Configuration. - #[cfg(not(target_env = "musl"))] fn add_usb_tablet(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_tablet(cfg_args)?; // SAFETY: id is already checked not none in parse_usb_tablet(). @@ -1391,7 +1386,6 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - USB Storage Configuration. - #[cfg(not(target_env = "musl"))] fn add_usb_storage(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_storage(vm_config, cfg_args)?; let storage = UsbStorage::new(device_cfg, self.get_drive_files()); @@ -1508,15 +1502,12 @@ pub trait MachineOps { "vhost-user-fs-pci" | "vhost-user-fs-device" => { self.add_virtio_fs(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } - #[cfg(not(target_env = "musl"))] "usb-kbd" => { self.add_usb_keyboard(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] "usb-tablet" => { self.add_usb_tablet(vm_config, cfg_args)?; } @@ -1524,7 +1515,6 @@ pub trait MachineOps { "usb-camera" => { self.add_usb_camera(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] "usb-storage" => { self.add_usb_storage(vm_config, cfg_args)?; } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 44a41f0a4..d65f409dd 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -69,11 +69,9 @@ use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use machine_manager::{config::get_cameradev_config, machine::MachineLifecycle}; use migration::MigrationManager; +use ui::input::{key_event, point_event}; #[cfg(not(target_env = "musl"))] -use ui::{ - input::{key_event, point_event}, - vnc::qmp_query_vnc, -}; +use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; @@ -1011,12 +1009,14 @@ impl StdMachine { Ok(()) } - #[cfg(not(target_env = "musl"))] fn plug_usb_device(&mut self, args: &qmp_schema::DeviceAddArgument) -> Result<()> { let driver = args.driver.as_str(); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); + #[cfg(not(target_env = "musl"))] let mut cfg_args = format!("id={}", args.id); + #[cfg(target_env = "musl")] + let cfg_args = format!("id={}", args.id); match driver { "usb-kbd" => { self.add_usb_keyboard(&mut locked_vmconfig, &cfg_args)?; @@ -1024,6 +1024,7 @@ impl StdMachine { "usb-tablet" => { self.add_usb_tablet(&mut locked_vmconfig, &cfg_args)?; } + #[cfg(not(target_env = "musl"))] "usb-camera" => { if let Some(cameradev) = &args.cameradev { cfg_args = format!("{},cameradev={}", cfg_args, cameradev); @@ -1033,6 +1034,7 @@ impl StdMachine { } self.add_usb_camera(&mut locked_vmconfig, &cfg_args)?; } + #[cfg(not(target_env = "musl"))] "usb-host" => { let default_value = "0".to_string(); let hostbus = args.hostbus.as_ref().unwrap_or(&default_value); @@ -1064,7 +1066,6 @@ impl StdMachine { Ok(()) } - #[cfg(not(target_env = "musl"))] fn handle_unplug_usb_request(&mut self, id: String) -> Result<()> { let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); @@ -1292,7 +1293,6 @@ impl DeviceInterface for StdMachine { ); } } - #[cfg(not(target_env = "musl"))] "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" => { if let Err(e) = self.plug_usb_device(args.as_ref()) { error!("{:?}", e); @@ -1373,19 +1373,12 @@ impl DeviceInterface for StdMachine { drop(locked_pci_host); // The device is not a pci device, assume it is a usb device. - #[cfg(not(target_env = "musl"))] - return match self.handle_unplug_usb_request(device_id) { + match self.handle_unplug_usb_request(device_id) { Ok(()) => Response::create_empty_response(), Err(e) => Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, ), - }; - - #[cfg(target_env = "musl")] - { - let err_str = format!("Failed to remove device: id {} not found", &device_id); - Response::create_error_response(qmp_schema::QmpErrorClass::GenericError(err_str), None) } } @@ -1744,7 +1737,6 @@ impl DeviceInterface for StdMachine { Response::create_empty_response() } - #[cfg(not(target_env = "musl"))] fn input_event(&self, key: String, value: String) -> Response { match send_input_event(key, value) { Ok(()) => Response::create_empty_response(), @@ -2013,7 +2005,6 @@ fn parse_blockdev(args: &BlockDevAddArgument) -> Result { Ok(config) } -#[cfg(not(target_env = "musl"))] fn send_input_event(key: String, value: String) -> Result<()> { match key.as_str() { "keyboard" => { diff --git a/ui/Cargo.toml b/ui/Cargo.toml index a9676619a..423c2de92 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -15,11 +15,13 @@ serde_json = "1.0" vmm-sys-util = "0.11.1" once_cell = "1.18.0" sscanf = "0.4.1" +bitintr = "0.3.0" +machine_manager = { path = "../machine_manager" } +util = { path = "../util" } + +[target.'cfg(not(target_env = "musl"))'.dependencies] rustls = "0.21.1" rustls-pemfile = "1.0.2" sasl2-sys = "0.1.20" -bitintr = "0.3.0" gtk = "0.17.1" -gettext-rs = { version = "0.7.0", features = ["gettext-system"]} -machine_manager = { path = "../machine_manager" } -util = { path = "../util" } +gettext-rs = { version = "0.7.0", features = ["gettext-system"] } diff --git a/ui/src/lib.rs b/ui/src/lib.rs index ecd7cc4b1..eab7f7939 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -10,12 +10,16 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(not(target_env = "musl"))] pub mod console; pub mod error; +#[cfg(not(target_env = "musl"))] pub mod gtk; pub mod input; +#[cfg(not(target_env = "musl"))] pub mod pixman; pub mod utils; +#[cfg(not(target_env = "musl"))] pub mod vnc; mod data; -- Gitee From bad3c867d0c6af96a4b1c5a1413479a21e110f1c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 11 Aug 2023 15:07:08 +0800 Subject: [PATCH 1298/2187] cmsg: remove unnecessary target_env of musl Signed-off-by: yezengruan --- machine_manager/src/socket.rs | 5 +---- util/src/unix.rs | 30 +++++++----------------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 90b4d1a44..f79044a65 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -366,11 +366,8 @@ impl SocketRWHandler { && scm.cmsg_len as u64 >= min_cmsg_len { let fds = unsafe { - #[cfg(not(target_env = "musl"))] - let fd_num = (scm.cmsg_len - CMSG_LEN(0) as usize) / size_of::(); - #[cfg(target_env = "musl")] let fd_num = - (scm.cmsg_len as usize - CMSG_LEN(0) as usize) / size_of::(); + (scm.cmsg_len as u64 - CMSG_LEN(0) as u64) as usize / size_of::(); std::slice::from_raw_parts(CMSG_DATA(scm) as *const RawFd, fd_num) }; self.scm_fd.append(&mut fds.to_vec()); diff --git a/util/src/unix.rs b/util/src/unix.rs index a5f406f7e..e0e3aaf50 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -272,27 +272,20 @@ impl UnixSock { let cmsg_capacity = unsafe { CMSG_SPACE((size_of::() * out_fds.len()) as u32) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; - #[cfg(target_env = "musl")] - let iovecs_len = iovecs_len as i32; - #[cfg(not(target_env = "musl"))] - let cmsg_len = cmsg_len as usize; - #[cfg(not(target_env = "musl"))] - let cmsg_capacity = cmsg_capacity as usize; - // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. let mut msg: msghdr = unsafe { std::mem::zeroed() }; msg.msg_name = null_mut(); msg.msg_namelen = 0; msg.msg_iov = iovecs.as_mut_ptr(); - msg.msg_iovlen = iovecs_len; + msg.msg_iovlen = iovecs_len as _; msg.msg_control = null_mut(); msg.msg_controllen = 0; msg.msg_flags = 0; if !out_fds.is_empty() { let cmsg = cmsghdr { - cmsg_len, + cmsg_len: cmsg_len as _, #[cfg(target_env = "musl")] __pad1: 0, cmsg_level: SOL_SOCKET, @@ -309,7 +302,7 @@ impl UnixSock { } msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void; - msg.msg_controllen = cmsg_capacity; + msg.msg_controllen = cmsg_capacity as _; } // Safe as msg parameters are valid. @@ -350,25 +343,20 @@ impl UnixSock { let cmsg_capacity = unsafe { CMSG_SPACE((size_of::() * in_fds.len()) as u32) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; - #[cfg(target_env = "musl")] - let iovecs_len = iovecs_len as i32; - #[cfg(not(target_env = "musl"))] - let cmsg_capacity = cmsg_capacity as usize; - // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. let mut msg: msghdr = unsafe { std::mem::zeroed() }; msg.msg_name = null_mut(); msg.msg_namelen = 0; msg.msg_iov = iovecs.as_mut_ptr(); - msg.msg_iovlen = iovecs_len; + msg.msg_iovlen = iovecs_len as _; msg.msg_control = null_mut(); msg.msg_controllen = 0; msg.msg_flags = 0; if !in_fds.is_empty() { msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void; - msg.msg_controllen = cmsg_capacity; + msg.msg_controllen = cmsg_capacity as _; } // Safe as msg parameters are valid. @@ -406,12 +394,8 @@ impl UnixSock { let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() }; if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS { - #[cfg(not(target_env = "musl"))] - let fd_count = - (cmsg.cmsg_len - unsafe { CMSG_LEN(0) } as usize) / size_of::(); - #[cfg(target_env = "musl")] - let fd_count = - (cmsg.cmsg_len as usize - unsafe { CMSG_LEN(0) } as usize) / size_of::(); + let fd_count = (cmsg.cmsg_len as u64 - unsafe { CMSG_LEN(0) } as u64) as usize + / size_of::(); unsafe { copy_nonoverlapping( self.cmsg_data(cmsg_ptr), -- Gitee From cadb8473057cdc61738f1f21b6cd3cb18e1cc88c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 13 Aug 2023 10:12:55 +0800 Subject: [PATCH 1299/2187] usb-host: some minor optimizations 1. There is no need to set the log level, as libUSB logs are output standard interfaces and not saved. 2. When completed asynchronously, a timeout of 0 is sufficient. 3. When resetting the device, there is no need to cancel the request. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index c20ad89ad..0fb174f9e 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -365,7 +365,7 @@ unsafe impl Send for UsbHost {} impl UsbHost { pub fn new(config: UsbHostConfig) -> Result { let mut context = Context::new()?; - context.set_log_level(rusb::LogLevel::Warning); + context.set_log_level(rusb::LogLevel::None); let iso_urb_frames = config.iso_urb_frames; let iso_urb_count = config.iso_urb_count; let id = config.id.clone().unwrap(); @@ -893,7 +893,7 @@ impl EventNotifierHelper for UsbHost { let mut notifiers = Vec::new(); let poll = get_libusb_pollfds(usbhost); - let timeout = Some(Duration::from_micros(500)); + let timeout = Some(Duration::new(0, 0)); let handler: Rc = Rc::new(move |_, _fd: RawFd| { cloned_usbhost .lock() @@ -940,8 +940,6 @@ impl UsbDeviceOps for UsbHost { if self.handle.is_none() { return; } - self.abort_host_transfers() - .unwrap_or_else(|e| error!("Failed to abort all libusb transfers: {:?}", e)); self.clear_iso_queues(); } -- Gitee From dbfd8eb1a7a4381cff2ff81a73b439764b166393 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Aug 2023 14:38:02 +0800 Subject: [PATCH 1300/2187] pci: use checked_add to prevent overflow Use checked_add to prevent overflow in validate_config_boundary. Signed-off-by: liuxiangdong --- devices/src/pci/config.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 05a952802..ed13caa79 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -536,13 +536,6 @@ impl PciConfig { } fn validate_config_boundary(&self, offset: usize, data: &[u8]) -> Result<()> { - if offset + data.len() > self.config.len() { - return Err(anyhow!(PciError::InvalidConf( - "config size".to_string(), - format!("offset {} with len {}", offset, data.len()) - ))); - } - // According to pcie specification 7.2.2.2 PCI Express Device Requirements: if data.len() > 4 { return Err(anyhow!(PciError::InvalidConf( @@ -551,6 +544,16 @@ impl PciConfig { ))); } + offset + .checked_add(data.len()) + .filter(|&end| end <= self.config.len()) + .with_context(|| { + PciError::InvalidConf( + "config size".to_string(), + format!("offset {} with len {}", offset, data.len()), + ) + })?; + Ok(()) } -- Gitee From 3a7b4d58327dba154acaf6c283fe81669a6fdf02 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Aug 2023 16:38:59 +0800 Subject: [PATCH 1301/2187] snapshot: fix snapshot extra data size error Fix snapshot extra data size error when parse qcow2 which doesn't have icount field in snapshot extra data. Old version(<=5.0) qemu creates internal snapshot by using extra data format: QCowSnapshotExtraData { uint64_t vm_state_size_large; uint64_t disk_size; } new version(>=6.0) qemu creates internal snapshot by using extra data format: QCowSnapshotExtraData { uint64_t vm_state_size_large; uint64_t disk_size; uint64 icount; } Stratovirt cannot parse these old qcow2 version file rightly now. Fix it. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/snapshot.rs | 40 +++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index e2ebbe8d8..224575569 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -22,6 +22,12 @@ use util::num_ops::round_up; /// Maximum number of snapshots. pub const QCOW2_MAX_SNAPSHOTS: usize = 65536; +// Length of Qcow2 internal snapshot which doesn't have icount in extra data. +// Qcow2 snapshots created by qemu-kvm/qemu-img(version <= 5.0) may have this format. +const SNAPSHOT_EXTRA_DATA_LEN_16: usize = 16; +// Length of Qcow2 internal snapshot which has icount in extra data. +const SNAPSHOT_EXTRA_DATA_LEN_24: usize = 24; + #[derive(Clone)] pub struct InternalSnapshot { pub snapshots: Vec, @@ -126,7 +132,10 @@ impl InternalSnapshot { let header = QcowSnapshotHeader::from_vec(&header_buf)?; pos += header_size; - let extra_size = size_of::(); + let extra_size = header.extra_date_size as usize; + if ![SNAPSHOT_EXTRA_DATA_LEN_16, SNAPSHOT_EXTRA_DATA_LEN_24].contains(&extra_size) { + bail!("Invalid extra data size {}", extra_size); + } let mut extra_buf = vec![0_u8; extra_size]; self.sync_aio .borrow_mut() @@ -214,7 +223,7 @@ impl QcowSnapshot { fn gen_snapshot_table_entry(&self) -> Vec { let id_str = self.id.to_string(); - let entry_size = size_of::() + size_of::(); + let entry_size = size_of::() + self.extra_data_size as usize; let mut buf = vec![0_u8; entry_size]; // Snapshot Header. @@ -226,14 +235,16 @@ impl QcowSnapshot { BigEndian::write_u32(&mut buf[20..24], self.date_nsec); BigEndian::write_u64(&mut buf[24..32], self.vm_clock_nsec); BigEndian::write_u32(&mut buf[32..36], self.vm_state_size); - BigEndian::write_u32(&mut buf[36..40], size_of::() as u32); + BigEndian::write_u32(&mut buf[36..40], self.extra_data_size); // Snapshot Extra data. // vm_state_size_large is used for vm snapshot. // It's equal to vm_state_size which is also 0 in disk snapshot. BigEndian::write_u64(&mut buf[40..48], self.vm_state_size as u64); BigEndian::write_u64(&mut buf[48..56], self.disk_size); - BigEndian::write_u64(&mut buf[56..64], self.icount); + if self.extra_data_size == SNAPSHOT_EXTRA_DATA_LEN_24 as u32 { + BigEndian::write_u64(&mut buf[56..64], self.icount); + } // Snapshot ID. let mut id_vec = id_str.as_bytes().to_vec(); @@ -292,15 +303,22 @@ pub struct QcowSnapshotExtraData { impl QcowSnapshotExtraData { fn from_vec(buf: &[u8]) -> Result { - let extra_len = size_of::(); - if buf.len() != extra_len { - bail!("Only support snapshot extra data length {}", extra_len); - } + let has_icount = match buf.len() { + SNAPSHOT_EXTRA_DATA_LEN_24 => true, + SNAPSHOT_EXTRA_DATA_LEN_16 => false, + _ => bail!("Invalid snapshot extra data length {}.", buf.len()), + }; - Ok(QcowSnapshotExtraData { + let mut extra = QcowSnapshotExtraData { _vm_state_size_large: BigEndian::read_u64(&buf[0..8]), disk_size: BigEndian::read_u64(&buf[8..16]), - icount: BigEndian::read_u64(&buf[16..24]), - }) + icount: u64::MAX, + }; + + if has_icount { + extra.icount = BigEndian::read_u64(&buf[16..24]); + } + + Ok(extra) } } -- Gitee From ff8b9aae642cce1cdc940e9eeb760bae053f7fb4 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 11 Aug 2023 19:27:18 +0800 Subject: [PATCH 1302/2187] code comment format standardization Use the following rustfmt configurations to standardize the comment format: comment_width = 100 wrap_comments = true normalize_comments = true format_code_in_doc_comments = true These are unstable configurations of rustfmt, which can only be checked with `cargo +nightly fmt --all`, currently will not be integrated into CI. Signed-off-by: yezengruan --- .rustfmt.toml | 8 +++ acpi/src/aml_compiler.rs | 4 +- acpi/src/table_loader.rs | 12 ++-- address_space/src/lib.rs | 26 +++---- address_space/src/listener.rs | 13 ++-- address_space/src/region.rs | 21 +++--- block_backend/src/qcow2/mod.rs | 6 +- block_backend/src/qcow2/snapshot.rs | 4 +- boot_loader/src/lib.rs | 18 +++-- boot_loader/src/x86_64/direct_boot/mod.rs | 4 +- cpu/src/aarch64/core_regs.rs | 3 +- cpu/src/x86_64/mod.rs | 2 +- devices/src/acpi/power.rs | 5 +- devices/src/camera_backend/mod.rs | 3 +- devices/src/camera_backend/v4l2.rs | 3 +- .../src/interrupt_controller/aarch64/state.rs | 6 +- devices/src/legacy/fwcfg.rs | 3 - devices/src/legacy/pl031.rs | 10 +-- devices/src/legacy/rtc.rs | 4 +- devices/src/misc/scream/pulseaudio.rs | 16 ++--- devices/src/pci/config.rs | 5 +- devices/src/pci/demo_dev.rs | 4 +- .../src/pci/demo_device/kbd_pointer_device.rs | 8 +-- devices/src/pci/host.rs | 16 ++--- devices/src/pci/msix.rs | 3 +- devices/src/scsi/bus.rs | 68 +++++++++++------- devices/src/usb/camera.rs | 5 +- devices/src/usb/storage.rs | 12 ++-- devices/src/usb/usbhost/mod.rs | 5 +- devices/src/usb/xhci/xhci_controller.rs | 9 ++- machine/src/lib.rs | 3 +- machine/src/micro_vm/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 6 +- machine_manager/src/config/machine_config.rs | 4 +- machine_manager/src/config/scsi.rs | 2 +- machine_manager/src/qmp/mod.rs | 6 +- machine_manager/src/socket.rs | 12 ++-- migration/migration_derive/src/lib.rs | 6 +- migration/src/manager.rs | 4 +- migration/src/protocol.rs | 53 +++++--------- ozone/src/cgroup.rs | 19 ++--- tests/mod_test/src/libdriver/qcow2.rs | 3 +- tests/mod_test/src/libdriver/usb.rs | 4 +- tests/mod_test/src/libdriver/vnc.rs | 3 +- tests/mod_test/tests/acpi_test.rs | 11 ++- tests/mod_test/tests/ged_test.rs | 11 ++- tests/mod_test/tests/memory_test.rs | 16 +++-- tests/mod_test/tests/net_test.rs | 3 +- tests/mod_test/tests/pci_test.rs | 15 ++-- tests/mod_test/tests/scream_test.rs | 6 +- tests/mod_test/tests/scsi_test.rs | 72 ++++++++++--------- tests/mod_test/tests/serial_test.rs | 33 ++++++--- tests/mod_test/tests/usb_test.rs | 2 +- tests/mod_test/tests/virtio_test.rs | 71 +++++++++--------- tests/mod_test/tests/virtiofs_test.rs | 2 +- tests/mod_test/tests/vnc_test.rs | 12 ++-- ui/src/console.rs | 10 +-- ui/src/pixman.rs | 32 ++++----- ui/src/vnc/auth_sasl.rs | 11 +-- ui/src/vnc/encoding/enc_hextile.rs | 2 +- ui/src/vnc/server_io.rs | 6 +- util/src/arg_parser.rs | 4 +- util/src/leak_bucket.rs | 4 +- util/src/offsetof.rs | 9 ++- util/src/seccomp.rs | 19 ++--- vfio/src/vfio_pci.rs | 5 +- vhost_user_fs/src/fs_ops.rs | 47 ++++++------ vhost_user_fs/src/fuse_msg.rs | 16 ++--- vhost_user_fs/src/fuse_proc.rs | 2 +- vhost_user_fs/src/sandbox.rs | 8 ++- virtio/src/device/balloon.rs | 21 +++--- virtio/src/device/block.rs | 3 +- virtio/src/device/scsi_cntlr.rs | 21 +++--- virtio/src/device/serial.rs | 10 +-- virtio/src/lib.rs | 7 +- virtio/src/queue/split.rs | 38 +++++----- virtio/src/transport/virtio_mmio.rs | 3 +- virtio/src/vhost/user/message.rs | 4 +- 78 files changed, 524 insertions(+), 445 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index 466b81b3f..5d983aa2d 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,2 +1,10 @@ use_field_init_shorthand = true edition = "2021" + +# These are unstable configurations of rustfmt, which can only be checked with +# `cargo +nightly fmt --all`, currently will not be integrated into CI. + +#wrap_comments = true +#comment_width = 100 +#normalize_comments = true +#format_code_in_doc_comments = true diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index b5e49c100..760c6f820 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -1139,8 +1139,8 @@ impl AmlBuilder for AmlMutex { pub struct AmlAcquire { /// The mutex object is converted to byte stream. mutex: Vec, - /// If the mutex is owned by others, current thread suspends and waits for `timeout` **milliseconds** - /// `timeout` being set as 0xFFFF indicates that there is no timeout and + /// If the mutex is owned by others, current thread suspends and waits for `timeout` + /// **milliseconds**, `timeout` being set as 0xFFFF indicates that there is no timeout and /// the acquire mutex operation will keeping waiting. time_out: u16, } diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index d85eac0d4..257552418 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -78,10 +78,10 @@ impl ByteCode for EntryContent {} /// Stores the command and corresponding content of entry. /// - For `AllocateEntry`, Guest will alloc guest memory resource. -/// - For `AddPointerEntry`, Guest will update pointer at specified offset of dst file -/// by adding base address of source file. -/// - For `AddPointerEntry`, Guest will calculate u8-type checksum of a range in file -/// and store it at specified offset of the same file. +/// - For `AddPointerEntry`, Guest will update pointer at specified offset of dst file by adding +/// base address of source file. +/// - For `AddPointerEntry`, Guest will calculate u8-type checksum of a range in file and store it +/// at specified offset of the same file. #[derive(Copy, Clone, Default)] struct TableLoaderEntry { /// The Type of command. @@ -221,8 +221,8 @@ impl TableLoader { /// * `file` - File name. /// * `file_blob` - File blob data. /// * `align` - Required alignment of this blob. - /// * `is_fseg` - Represents range where Guest will allocate for this entry. - /// If true, Guest will allocate in FSEG zone. + /// * `is_fseg` - Represents range where Guest will allocate for this entry. If true, Guest will + /// allocate in FSEG zone. pub fn add_alloc_entry( &mut self, file: &str, diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 8a81202ca..fc63713c7 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -18,7 +18,9 @@ //! ```rust //! use std::sync::{Arc, Mutex}; //! extern crate address_space; -//! use address_space::{AddressSpace, Region, GuestAddress, HostMemMapping, RegionOps, FileBackend}; +//! use address_space::{ +//! AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps, +//! }; //! //! struct DummyDevice; //! impl DummyDevice { @@ -34,18 +36,16 @@ //! //! fn main() { //! // 1. create address_space -//! let space = AddressSpace::new(Region::init_container_region(u64::max_value(), "space"), "space").unwrap(); +//! let space = AddressSpace::new( +//! Region::init_container_region(u64::max_value(), "space"), +//! "space", +//! ) +//! .unwrap(); //! //! // 2. create an Ram-type Region, and set it's priority -//! let mem_mapping = Arc::new(HostMemMapping::new( -//! GuestAddress(0), -//! None, -//! 0x1000, -//! None, -//! false, -//! false, -//! false, -//! ).unwrap()); +//! let mem_mapping = Arc::new( +//! HostMemMapping::new(GuestAddress(0), None, 0x1000, None, false, false, false).unwrap(), +//! ); //! let ram_region = Region::init_ram_region(mem_mapping.clone(), "ram"); //! ram_region.set_priority(10); //! @@ -69,7 +69,9 @@ //! let io_region = Region::init_io_region(0x1000, dev_ops, "io_region"); //! //! // 4. add sub_region to address_space's root region -//! space.root().add_subregion(ram_region, mem_mapping.start_address().raw_value()); +//! space +//! .root() +//! .add_subregion(ram_region, mem_mapping.start_address().raw_value()); //! space.root().add_subregion(io_region, 0x2000); //! //! // 5. access address_space diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 01bb9390e..044b11663 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -735,25 +735,26 @@ mod test { assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) .is_ok()); - // The evtfd already added, adding again should make an error + // The evtfd already added, adding again should make an error. assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) .is_err()); assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) .is_ok()); - // The evtfd already deleted, deleting again should cause an error + // The evtfd already deleted, deleting again should cause an error. assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) .is_err()); - // Register an ioeventfd with data-match + // Register an ioeventfd with data-match. let evtfd = generate_region_ioeventfd(64, 4_u64); assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) .is_ok()); - // Register an ioeventfd which has same address with previously registered ones will cause an error + // Register an ioeventfd which has same address with previously registered ones will cause + // an error. let same_addred_evtfd = generate_region_ioeventfd(64, 4_u64); assert!(kml .handle_request( @@ -836,14 +837,14 @@ mod test { assert!(iol .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) .is_ok()); - // evtfd already added, adding again should make an error + // evtfd already added, adding again should make an error. assert!(iol .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) .is_err()); assert!(iol .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) .is_ok()); - // evtfd already deleted, deleting again should make an error + // evtfd already deleted, deleting again should make an error. assert!(iol .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) .is_err()); diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 75058d932..0eb7fdb0b 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -57,7 +57,8 @@ pub struct Region { size: Arc, /// Offset in parent Container-type region. It won't be changed once initialized. offset: Arc>, - /// If not Ram, RomDevice, RamDevice Region type, `mem_mapping` is None. It won't be changed once initialized. + /// If not Ram, RomDevice, RamDevice Region type, `mem_mapping` is None. It won't be changed + /// once initialized. mem_mapping: Option>, /// `ops` provides read/write function. ops: Option, @@ -67,7 +68,8 @@ pub struct Region { space: Arc>>, /// Sub-regions array, keep sorted. subregions: Arc>>, - /// This field is useful for RomDevice-type Region. If true, in read-only mode, otherwise in IO mode. + /// This field is useful for RomDevice-type Region. If true, in read-only mode, otherwise in IO + /// mode. rom_dev_romd: Arc, /// Max access size supported by the device. max_access_size: Option, @@ -193,7 +195,8 @@ struct MultiOpsArgs { /// Read/Write for multi times. macro_rules! rw_multi_ops { ( $ops: ident, $slice: expr, $args: ident ) => { - // The data size is larger than the max access size, we split to read/write for multiple times. + // The data size is larger than the max access size, we split to read/write for multiple + // times. let base = $args.base; let offset = $args.offset; let cnt = $args.count; @@ -215,11 +218,7 @@ macro_rules! rw_multi_ops { } // Unaligned memory access. if cnt % access_size > 0 - && !$ops( - &mut $slice[pos as usize..cnt as usize], - base, - offset + pos, - ) + && !$ops(&mut $slice[pos as usize..cnt as usize], base, offset + pos) { return Err(anyhow!(AddressSpaceError::IoAccess( base.raw_value(), @@ -456,8 +455,8 @@ impl Region { Ok(()) } - /// Get read-only mode of RomDevice-type region. Return true if in read-only mode, otherwise return false. - /// Return None if it is not a RomDevice-type region. + /// Get read-only mode of RomDevice-type region. Return true if in read-only mode, otherwise + /// return false. Return None if it is not a RomDevice-type region. pub fn get_rom_device_romd(&self) -> Option { if self.region_type != RegionType::RomDevice { None @@ -1139,7 +1138,7 @@ mod test { ram_region.set_offset(GuestAddress(0x11u64)); assert_eq!(ram_region.offset(), GuestAddress(0x11u64)); - //test read/write + // test read/write assert!(ram_region .write(&mut data.as_ref(), GuestAddress(0), 0, count) .is_ok()); diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 06dc28a7c..5ae402652 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -2308,7 +2308,8 @@ mod test { host_offset } - // Change snapshot table offset to unaligned address which will lead to error in refcount update process. + // Change snapshot table offset to unaligned address which will lead to error in refcount update + // process. #[test] fn simulate_revert_snapshot_creation() { let path = "/tmp/revert_create.qcow2"; @@ -2361,7 +2362,8 @@ mod test { } } - // Change snapshot table offset to unaligned address which will lead to error in refcount update process. + // Change snapshot table offset to unaligned address which will lead to error in refcount update + // process. #[test] fn simulate_revert_snapshot_deletion() { let path = "/tmp/revert_delete.qcow2"; diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index e2ebbe8d8..4a1a5bc12 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -196,8 +196,8 @@ pub struct QcowSnapshot { pub date_sec: u32, pub date_nsec: u32, pub vm_clock_nsec: u64, - // Icount value which corresponds to the record/replay instruction count when the snapshots was token. - // Sed to -1 which means icount was disabled. + // Icount value which corresponds to the record/replay instruction count when the snapshots was + // token. Sed to -1 which means icount was disabled. pub icount: u64, pub extra_data_size: u32, } diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index c2513ab13..589f835c8 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -36,11 +36,15 @@ //! # extern crate boot_loader; //! //! use address_space::{AddressSpace, Region}; -//! use boot_loader::{BootLoaderConfig, load_linux}; +//! use boot_loader::{load_linux, BootLoaderConfig}; //! -//! #[cfg(target_arch="x86_64")] +//! #[cfg(target_arch = "x86_64")] //! fn main() { -//! let guest_mem = AddressSpace::new(Region::init_container_region(std::u64::MAX, "guest_mem"), "guest_mem").unwrap(); +//! let guest_mem = AddressSpace::new( +//! Region::init_container_region(std::u64::MAX, "guest_mem"), +//! "guest_mem", +//! ) +//! .unwrap(); //! let kernel_file = std::path::PathBuf::from("/path/to/my/kernel"); //! let bootloader_config = BootLoaderConfig { //! kernel: Some(kernel_file), @@ -58,9 +62,13 @@ //! // Now PE linux kernel and kernel cmdline are loaded to guest memory... //! } //! -//! #[cfg(target_arch="aarch64")] +//! #[cfg(target_arch = "aarch64")] //! fn main() { -//! let guest_mem = AddressSpace::new(Region::init_container_region(u64::MAX, "guest_mem"), "guest_mem").unwrap(); +//! let guest_mem = AddressSpace::new( +//! Region::init_container_region(u64::MAX, "guest_mem"), +//! "guest_mem", +//! ) +//! .unwrap(); //! let kernel_file = std::path::PathBuf::from("/path/to/my/kernel"); //! let bootloader_config = BootLoaderConfig { //! kernel: Some(kernel_file), diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index e76058005..534fb4bb3 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -337,7 +337,7 @@ mod test { let mut boot_hdr = RealModeKernelHeader::new(); assert!(setup_boot_params(&config, &space, &boot_hdr).is_ok()); - //test setup_gdt function + // test setup_gdt function let c_seg = kvm_segment { base: 0, limit: 1048575, @@ -386,7 +386,7 @@ mod test { assert_eq!(arr[2], 0xaf9b000000ffff); assert_eq!(arr[3], 0xcf93000000ffff); - //test setup_kernel_cmdline function + // test setup_kernel_cmdline function let cmd_len: u64 = config.kernel_cmdline.len() as u64; let mut read_buffer: [u8; 30] = [0; 30]; assert!(setup_kernel_cmdline(&config, &space, &mut boot_hdr).is_ok()); diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index 6deaf788a..ee8a8ba29 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -113,7 +113,8 @@ impl From for u64 { // #define KVM_REG_SIZE_U128 0x0040000000000000ULL // #define KVM_REG_ARM_CORE 0x00100000ULL - // The id of the register is encoded as specified for `KVM_GET_ONE_REG` in the kernel documentation. + // The id of the register is encoded as specified for `KVM_GET_ONE_REG` in the kernel + // documentation. // reg_id = KVM_REG_ARM64 | KVM_REG_SIZE_* | KVM_REG_ARM_CORE | reg_offset_index // reg_offset_index = reg_offset / sizeof(u32) // KVM_REG_SIZE_* => KVM_REG_SIZE_U32/KVM_REG_SIZE_U64/KVM_REG_SIZE_U128 diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 12072a42a..17faff2a2 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -675,7 +675,7 @@ mod test { vm_fd.create_irq_chip().unwrap(); let vcpu = Arc::new(vm_fd.create_vcpu(0).unwrap()); let mut x86_cpu = X86CPUState::new(0, 1); - //test `set_boot_config` function + // test `set_boot_config` function assert!(x86_cpu.set_boot_config(&vcpu, &cpu_config).is_ok()); // test setup special registers diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 90bdc51fa..87c8bc70e 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -112,9 +112,8 @@ impl PowerDev { sprop ) })?; - /* All the values except "online" property is multiplicated by 1000 - * Only "online" property starts with 'o' character - */ + // All the values except "online" property is multiplicated by 1000. + // Only "online" property starts with 'o' character. pdev_props[i] = if sysfs_props[i].starts_with('o') { prop.unsigned_abs() as u32 } else { diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index ebdce1272..1dcf05b47 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -11,7 +11,8 @@ // See the Mulan PSL v2 for more details. //! The abstract layer that connects different frontend & backend camera devices. -//! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait CameraHostdevOps. +//! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait +//! CameraHostdevOps. pub mod demo; pub mod v4l2; diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 74d29fb7e..656c51cea 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -534,7 +534,8 @@ impl EventNotifierHelper for V4l2IoHandler { NotifierOperation::AddShared, v4l2_handler.lock().unwrap().backend.as_raw_fd(), None, - EventSet::IN | EventSet::EDGE_TRIGGERED | EventSet::HANG_UP, // For unexpected device removal. + // For unexpected device removal. + EventSet::IN | EventSet::EDGE_TRIGGERED | EventSet::HANG_UP, vec![handler], )] } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index e46334a39..dff4adb60 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -402,7 +402,8 @@ impl GICv3 { self.access_gic_cpu(ICC_BPR1_EL1, gic_cpu.vcpu, &mut gic_cpu.icc_bpr1_el1, false)?; // ICC_CTLR_EL1.PRIbits is [10:8] in ICC_CTLR_EL1 - // PRIBits indicate the number of priority bits implemented, independently for each target PE. + // PRIBits indicate the number of priority bits implemented, independently for each target + // PE. let icc_ctlr_el1_pri = ((gic_cpu.icc_ctlr_el1 & ICC_CTLR_EL1_PRIBITS_MASK) >> ICC_CTLR_EL1_PRIBITS_SHIFT) + 1; // Save APnR registers based on ICC_CTLR_EL1.PRIBITS @@ -486,7 +487,8 @@ impl GICv3 { self.access_gic_cpu(ICC_BPR1_EL1, gic_cpu.vcpu, &mut gic_cpu.icc_bpr1_el1, true)?; // ICC_CTLR_EL1.PRIbits is [10:8] in ICC_CTLR_EL1 - // PRIBits indicate the number of priority bits implemented, independently for each target PE. + // PRIBits indicate the number of priority bits implemented, independently for each target + // PE. let icc_ctlr_el1_pri = ((gic_cpu.icc_ctlr_el1 & ICC_CTLR_EL1_PRIBITS_MASK) >> ICC_CTLR_EL1_PRIBITS_SHIFT) + 1; // Restore APnR registers based on ICC_CTLR_EL1.PRIBITS diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index fc6974142..2f097bd20 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -664,7 +664,6 @@ impl FwCfgCommon { /// # Errors /// /// Return Error if fail to add the file entry. - /// fn dma_mem_write(&mut self, addr: u64, value: u64, size: u32) -> Result<()> { if size == 4 { if addr == 0 { @@ -696,7 +695,6 @@ impl FwCfgCommon { /// # Return /// /// Return the value of the register - /// fn dma_mem_read(&self, addr: u64, size: u32) -> Result { extract_u64( FW_CFG_DMA_SIGNATURE as u64, @@ -716,7 +714,6 @@ impl FwCfgCommon { /// # Return /// /// Return the value of the register - /// fn read_data_reg(&mut self, _addr: u64, mut size: u32) -> Result { if size == 0 || size > std::mem::size_of::() as u32 { bail!( diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 289d9f84b..de5351f69 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -180,11 +180,11 @@ impl SysBusDevOps for PL031 { match offset { RTC_MR => { - // TODO: The MR register is used for implementing the RTC alarm. A RTC alarm is a feature - // that can be used to allow a computer to 'wake up' after shut down to execute tasks - // every day or on a certain day. It can sometimes be found in the 'Power Management' - // section of motherboard's BIOS setup. This RTC alarm function is not implemented yet, - // here is a reminder just in case. + // TODO: The MR register is used for implementing the RTC alarm. A RTC alarm is a + // feature that can be used to allow a computer to 'wake up' after shut down to + // execute tasks every day or on a certain day. It can sometimes be found in the + // 'Power Management' section of motherboard's BIOS setup. This RTC alarm function + // is not implemented yet, here is a reminder just in case. self.state.mr = value; } RTC_LR => { diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 10f8b3d91..10df08446 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -154,8 +154,8 @@ impl RTC { /// # Arguments /// /// * `mem_size` - Guest memory size. - /// * `gap_start` - The start address of gap on x86_64 platform. - /// This value can be found in memory layout. + /// * `gap_start` - The start address of gap on x86_64 platform. This value can be found in + /// memory layout. pub fn set_memory(&mut self, mem_size: u64, gap_start: u64) { self.mem_size = mem_size; self.gap_start = gap_start; diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 5aa88fbec..b0f99fbdf 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -130,13 +130,12 @@ impl PulseStreamData { self.channel_map.init(); self.channel_map.set_len(format.channels); let map: &mut [Position] = self.channel_map.get_mut(); - /* In Windows, the channel mask shows as following figure. - * 31 11 10 9 8 7 6 5 4 3 2 1 0 - * | | | SR | SL | BC | FRC| FLC| BR | BL | LFE| FC | FR | FL | - * - * Each bit in the channel mask represents a particular speaker position. - * Now, it map a windows SPEAKER_* position to a PA_CHANNEL_POSITION_*. - */ + // In Windows, the channel mask shows as following figure. + // 31 11 10 9 8 7 6 5 4 3 2 1 0 + // | | | SR | SL | BC | FRC| FLC| BR | BL | LFE| FC | FR | FL | + // + // Each bit in the channel mask represents a particular speaker position. + // Now, it map a windows SPEAKER_* position to a PA_CHANNEL_POSITION_*. let mut key: i32 = -1; for (i, item) in map.iter_mut().enumerate().take(format.channels as usize) { for j in (key + 1)..32 { @@ -204,7 +203,8 @@ impl PulseStreamData { } if self.ss.rate > 0 { - // Sample spec has changed, so the playback buffer size for the requested latency must be recalculated as well. + // Sample spec has changed, so the playback buffer size for the requested latency must + // be recalculated as well. self.buffer_attr.tlength = self.ss .usec_to_bytes(MicroSeconds(self.latency as u64 * 1000)) as u32; diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 05a952802..25ed4cebd 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -300,7 +300,7 @@ pub const PCI_EXP_HP_EV_CCI: u16 = PCI_EXP_SLTCTL_CCIE; // XHCI device id pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; -/* Device classes and subclasses */ +// Device classes and subclasses pub const PCI_CLASS_MEMORY_RAM: u16 = 0x0500; pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; @@ -655,7 +655,8 @@ impl PciConfig { le_write_u16(&mut self.config, offset, old_command & !writable_command) } - /// Reset bits that's writable in the common configuration fields for both type0 and type1 devices. + /// Reset bits that's writable in the common configuration fields for both type0 and type1 + /// devices. pub fn reset_common_regs(&mut self) -> Result<()> { self.reset_single_writable_reg(COMMAND as usize)?; self.reset_single_writable_reg(STATUS as usize)?; diff --git a/devices/src/pci/demo_dev.rs b/devices/src/pci/demo_dev.rs index 731fe95ee..7602479eb 100644 --- a/devices/src/pci/demo_dev.rs +++ b/devices/src/pci/demo_dev.rs @@ -15,8 +15,8 @@ /// It can have 0-6 bars, if set, msix always lives in bar 0, data handling in bar 1. /// 1. its functionality is to read and write data for the guest, meanwhile, do a little /// mathmetic logic(multiply data[0] with 2) with the write op. -/// 2. After r/w, it sends back a msix interrupt to the guest, which means that it has -/// also msix capability. We assume msix bar is in bar 0. +/// 2. After r/w, it sends back a msix interrupt to the guest, which means that it has also +/// msix capability. We assume msix bar is in bar 0. /// 3. Finally, it supports hotplug/hotunplug. /// As that it has device memory, it means it has a bar space, we assume the /// bar size is 4KB in bar 1. diff --git a/devices/src/pci/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs index e06fcba90..54f9c8459 100644 --- a/devices/src/pci/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -// Demo keyboard-pointer device is a simple pci device. It can be used to test whether -// VNC can correctly receive the input from the client and transmit it to the keyboard and pointer device -// Users can write a rom address in the mmio configuration space of the device. -// Then if an input event occurs, the event information will be recorded to the corresponding memory by this device. +// Demo keyboard-pointer device is a simple pci device. It can be used to test whether VNC can +// correctly receive the input from the client and transmit it to the keyboard and pointer device. +// Users can write a rom address in the mmio configuration space of the device. Then if an input +// event occurs, the event information will be recorded to the corresponding memory by this device. use std::sync::{Arc, Mutex}; diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index d7cef948b..d46a26194 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -304,10 +304,8 @@ fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { if_obj_0.append_child(AmlCreateDWordField::new(AmlArg(3), AmlInteger(8), "CDW3")); let cdw3 = AmlName("CDW3".to_string()); if_obj_0.append_child(AmlStore::new(cdw3.clone(), AmlLocal(0))); - /* - * Hotplug: We now support PCIe native hotplug(bit 0) with PCI Express Capability Structure(bit 4) - * other bits: bit1: SHPC; bit2: PME; bit3: AER; - */ + // Hotplug: We now support PCIe native hotplug(bit 0) with PCI Express Capability + // Structure(bit 4), other bits: bit1: SHPC; bit2: PME; bit3: AER. if_obj_0.append_child(AmlAnd::new(AmlLocal(0), AmlInteger(0x11), AmlLocal(0))); let mut if_obj_1 = AmlIf::new(AmlLNot::new(AmlEqual::new(AmlArg(1), AmlInteger(1)))); let cdw1 = AmlName("CDW1".to_string()); @@ -348,10 +346,8 @@ fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { AmlName("CDW3".to_string()), AmlName("CTRL".to_string()), )); - /* - * Hotplug: We now support PCIe native hotplug(bit 0) with PCI Express Capability Structure(bit 4) - * other bits: bit1: SHPC; bit2: PME; bit3: AER; - */ + // Hotplug: We now support PCIe native hotplug(bit 0) with PCI Express Capability + // Structure(bit4), other bits: bit1: SHPC; bit2: PME; bit3: AER. if_obj_0.append_child(AmlStore::new( AmlAnd::new(AmlName("CTRL".to_string()), AmlInteger(0x11), AmlLocal(0)), AmlName("CTRL".to_string()), @@ -377,8 +373,8 @@ fn build_osc_for_aml(pci_host_bridge: &mut AmlDevice) { AmlName("CTRL".to_string()), AmlName("CDW3".to_string()), )); - // For pci host, kernel will use _OSC return value to determine - // whether native_pcie_hotplug is enabled or not. + // For pci host, kernel will use _OSC return value to determine whether + // native_pcie_hotplug is enabled or not. if_obj_0.append_child(AmlReturn::with_value(AmlArg(3))); method.append_child(if_obj_0); let mut else_obj_0 = AmlElse::new(); diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 761ea9ffe..f5e84cf41 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -621,7 +621,8 @@ fn send_msix(msg: Message, dev_id: u16) { /// * `dev_id` - Dev id. /// * `_id` - MSI-X id used in MigrationManager. /// * `parent_region` - Parent region which the MSI-X region registered. If none, registered in BAR. -/// * `offset_opt` - Offset of table(table_offset) and Offset of pba(pba_offset). Set the table_offset and pba_offset together. +/// * `offset_opt` - Offset of table(table_offset) and Offset of pba(pba_offset). Set the +/// table_offset and pba_offset together. pub fn init_msix( bar_id: usize, vector_nr: u32, diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 19d6542d1..83cd0edd1 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -272,9 +272,11 @@ const SCSI_TARGET_INQUIRY_LEN: u32 = 36; /// | Peripheral Qualifier | Peripheral Device Type | /// Unknown or no device type. const TYPE_UNKNOWN: u8 = 0x1f; -/// A peripheral device having the specified peripheral device type is not connected to this logical unit. +/// A peripheral device having the specified peripheral device type is not connected to this logical +/// unit. const TYPE_INACTIVE: u8 = 0x20; -/// Scsi target device is not capable of supporting a peripheral device connected to this logical unit. +/// Scsi target device is not capable of supporting a peripheral device connected to this logical +/// unit. const TYPE_NO_LUN: u8 = 0x7f; /// Notification Classes for GET EVENT STATUS NOTIFICATION. @@ -314,14 +316,15 @@ pub const GESN_EC_NEWMEDIA: u8 = 2; /// The media has been removed from the specified slot, and the Drive is unable to access the media /// without user intervention. This applies to media changers only. pub const GESN_EC_MEDIAREMOVAL: u8 = 3; -/// The user has requested that the media in the specified slot be loaded. This applies to media changers only. +/// The user has requested that the media in the specified slot be loaded. This applies to media +/// changers only. pub const GESN_EC_MEDIACHANGED: u8 = 4; -/// A DVD+RW background format has completed. Since DVD+RW Drives are capable of generationg multiple -/// media events concurrently, such Drives shall be capable of queuing media events. +/// A DVD+RW background format has completed. Since DVD+RW Drives are capable of generationg +/// multiple media events concurrently, such Drives shall be capable of queuing media events. pub const GESN_EC_BGFORMATCOMPLETED: u8 = 5; -/// A DVD+RW background format has been automatically restarted by the Drive. Since DVD+RW Drives are -/// capable of generationg multiple media events concurrently, such Drives shall be capable of queuing -/// media event. +/// A DVD+RW background format has been automatically restarted by the Drive. Since DVD+RW Drives +/// are capable of generationg multiple media events concurrently, such Drives shall be capable of +/// queuing media event. pub const GESN_EC_BGFORMATRESTARTED: u8 = 6; /// Some generally useful CD-ROM information. From @@ -484,7 +487,8 @@ pub trait ScsiRequestOps: Send + Sync + AsAny { pub struct ScsiRequest { pub cmd: ScsiCommand, - // Requested lun id for scsi request. It may be not equal to scsi device's lun id when it's a scsi target request. + // Requested lun id for scsi request. It may be not equal to scsi device's lun id when it's a + // scsi target request. pub req_lun: u16, pub opstype: u32, // For datain and dataout. Can be empty when it's a ScsiXferMode::ScsiXferNone request. @@ -1002,7 +1006,8 @@ fn scsi_command_emulate_vpd_page( } } - // It's OK for just using outbuf byte 3, because all page_code's buflen in stratovirt is less than 255 now. + // It's OK for just using outbuf byte 3, because all page_code's buflen in stratovirt is less + // than 255 now. outbuf[3] = buflen as u8 - 4; Ok(outbuf) } @@ -1141,7 +1146,8 @@ fn scsi_command_emulate_read_capacity_10( nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; nb_sectors -= 1; - // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical block). + // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical + // block). // Bytes[4-7]: Logical Block Length In Bytes. BigEndian::write_u32(&mut outbuf[0..4], nb_sectors); BigEndian::write_u32(&mut outbuf[4..8], block_size); @@ -1309,26 +1315,30 @@ fn scsi_command_emulate_mode_sense_page( // Byte[buflen + 3]: Bits[6-7]: Reserved | DVD-RAW WRITE | DVD-R WRITE | // Reserved | Test Write | CD-R/RW Write | CD-R Write. // Byte[buflen + 4]: BUF | Multi Session(1) | Mode 2 Form 2(1) | Mode 2 Form 1(1) | - // Digital Port 2(1) | Digital Port 1(1) | Composite(1) | Audio Play(1). + // Digital Port 2(1) | Digital Port 1(1) | Composite(1) | + // Audio Play(1). // Byte[buflen + 5]: Read Bar Code(1) | UPC(1) | ISRC(1) | C2 Pointers supported(1) | // R-W Deinterleaved & corrected(1) | R-W supported(1) | // CD-DA Stream is Accurate(1) | CD-DA Cmds supported(1). - // Byte[buflen + 6]: Bits[5-7]: Loading Mechanism Type(1) | Reserved | Eject(1) | Prevent Jumper(1) | - // Lock State | Lock(1). + // Byte[buflen + 6]: Bits[5-7]: Loading Mechanism Type(1) | Reserved | Eject(1) | + // Prevent Jumper(1) | Lock State | Lock(1). // Byte[buflen + 7]: Bits[6-7]: Reserved | R-W in Lead-in | Side Change Capable | SSS | - // Changer Supports Disc Present | Separate Channel Mute | Separate volume levels + // Changer Supports Disc Present | Separate Channel Mute | + // Separate volume levels. // Bytes[buflen + 8 - buflen + 9]: Obsolete. // Bytes[buflen + 10 - buflen + 11]: Number of Volume Levels Supported. // Bytes[buflen + 12 - buflen + 13]: Buffer Size Supported. // Bytes[buflen + 14 - buflen + 15]: Obsolete. // Byte[buflen + 16]: Reserved. - // Byte[buflen + 17]: Bits[6-7]: Reserved | Bits[4-5]: Length | LSBF | RCK | BCKF | Reserved. + // Byte[buflen + 17]: Bits[6-7]: Reserved | Bits[4-5]: Length | LSBF | RCK | BCKF | + // Reserved. // Bytes[buflen + 18 - buflen + 21]: Obsolete. // Bytes[buflen + 22 - buflen + 23]: Copy Management Revision Supported. // Bytes[buflen + 24 - buflen + 26]: Reserved. // Byte[buflen + 27]: Bits[2-7]: Reserved. Bits[0-1]: Rotation Control Selected. // Bytes[buflen + 28 - buflen + 29]: Current Write Speed Selected. - // Bytes[buflen + 31]: Number of Logical Unit Write Speed Performance Descriptor Tables(n). + // Bytes[buflen + 31]: Number of Logical Unit Write Speed Performance Descriptor + // Tables(n). outbuf.resize(buflen + 32, 0); outbuf[buflen] = page; outbuf[buflen + 1] = 28; @@ -1423,7 +1433,8 @@ fn scsi_command_emulate_service_action_in_16( drop(dev_lock); - // Byte[0-7]: Returned Logical BLock Address(the logical block address of the last logical block). + // Byte[0-7]: Returned Logical BLock Address(the logical block address of the last logical + // block). // Byte[8-11]: Logical Block Length in Bytes. BigEndian::write_u64(&mut outbuf[0..8], nb_sectors); BigEndian::write_u32(&mut outbuf[8..12], block_size); @@ -1460,12 +1471,14 @@ fn scsi_command_emulate_read_disc_information( // Outbuf: // Bytes[0-1]: Disc Information Length(32). - // Byte2: Disc Information Data Type(000b) | Erasable(0) | State of last Session(01b) | Disc Status(11b). + // Byte2: Disc Information Data Type(000b) | Erasable(0) | State of last Session(01b) | + // Disc Status(11b). // Byte3: Number of First Track on Disc. // Byte4: Number of Sessions. // Byte5: First Track Number in Last Session(Least Significant Byte). // Byte6: Last Track Number in Last Session(Last Significant Byte). - // Byte7: DID_V | DBC_V | URU:Unrestricted Use Disc(1) | DAC_V | Reserved | Legacy | BG Format Status. + // Byte7: DID_V | DBC_V | URU:Unrestricted Use Disc(1) | DAC_V | Reserved | Legacy | + // BG Format Status. // Byte8: Disc Type(00h: CD-DA or CD-ROM Disc). // Byte9: Number of sessions(Most Significant Byte). // Byte10: First Trace Number in Last Session(Most Significant Byte). @@ -1584,7 +1597,8 @@ fn scsi_command_emulate_get_configuration( // Bytes[8-n]: Feature Descriptor(s): // Bytes[20-31]: Feature 1: Core Feature: // Bytes[20-21]: Feature Code(0001h). - // Byte[22]: Bits[6-7]: Reserved. Bits[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + // Byte[22]: Bits[6-7]: Reserved. Bits[2-5]: Version(0010b). Bit 1: Persistent(1). + // Bit 0: Current(1). // Byte[23]: Additional Length(8). // Bytes[24-27]: Physical Interface Standard. (Scsi Family: 00000001h) // Byte[28]: Bits[2-7]: Reserved. Bit 1: INQ2. Bit 0: DBE(1). @@ -1598,10 +1612,11 @@ fn scsi_command_emulate_get_configuration( // Bytes[8-n]: Feature Descriptor(s): // Bytes[32-40]: Feature 2: Removable media feature: // Bytes[32-33]: Feature Code(0003h). - // Byte[34]: Bits[6-7]: Reserved. Bit[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + // Byte[34]: Bits[6-7]: Reserved. Bit[2-5]: Version(0010b). Bit 1: Persistent(1). + // Bit 0: Current(1). // Byte[35]: Additional Length(4). - // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). Bit 2: Pvnt Jmpr. - // Bit 1: DBML. Bit 0: Lock(1). + // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). + // Bit 2: Pvnt Jmpr. Bit 1: DBML. Bit 0: Lock(1). // Byte[37-39]: Reserved. BigEndian::write_u16(&mut outbuf[32..34], GC_FC_REMOVABLE_MEDIUM); outbuf[34] = 0x0b; @@ -1651,8 +1666,9 @@ fn scsi_command_emulate_get_event_status_notification( // Byte6: Start Slot. // Byte7: End Slot. - // Do not support hot-plug/hot-unplug scsi cd which will be present all the time once vm starts. - // To do: this outbuf event code and media status should be changed after allowing hot-plug. + // Do not support hot-plug/hot-unplug scsi cd which will be present all the time once vm + // starts. To do: this outbuf event code and media status should be changed after + // allowing hot-plug. outbuf[4] = GESN_EC_NOCHG; outbuf[5] = 1 << GESN_MS_MEDIA_PRESENT_BIT; } else { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index f21740f30..1bcf3e556 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -1235,8 +1235,9 @@ mod test { #[test] fn test_interfaces_table_data_len() { - // VC and VS's header difference, their wTotalSize field's offset are the bit 5 and 4 respectively in their data[0] vector. - // the rest data follow the same principle that the 1st element is the very data vector's length. + // VC and VS's header difference, their wTotalSize field's offset are the bit 5 and 4 + // respectively in their data[0] vector. The rest data follow the same principle that the + // 1st element is the very data vector's length. test_interface_table_data_len(gen_desc_interface_camera_vc().unwrap(), 5); test_interface_table_data_len(gen_desc_interface_camera_vs(list_format()).unwrap(), 4); } diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index fa9a4a631..652463c50 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -232,10 +232,11 @@ pub struct UsbStorage { /// Scsi bus attached to this usb-storage device. scsi_bus: Arc>, /// Effective scsi backend. - // Note: scsi device should attach to scsi bus. Logically, scsi device should not be placed in UsbStorage. - // But scsi device is needed in processing scsi request. Because the three (usb-storage/scsi bus/scsi device) - // correspond one-to-one, add scsi device member here for the execution efficiency (No need to find a unique - // device from the hash table of the unique bus). + // Note: scsi device should attach to scsi bus. Logically, scsi device should not be placed in + // UsbStorage. But scsi device is needed in processing scsi request. Because the three + // (usb-storage/scsi bus/scsi device) correspond one-to-one, add scsi device member here + // for the execution efficiency (No need to find a unique device from the hash table of the + // unique bus). scsi_dev: Arc>, } @@ -518,7 +519,8 @@ impl UsbDeviceOps for UsbStorage { self.usb_device .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; - // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not supported. + // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not + // supported. let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); locked_scsi_dev.realize(None)?; drop(locked_scsi_dev); diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index c20ad89ad..d5fff7461 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -844,8 +844,9 @@ impl UsbHost { Err(e) => { locked_iso_queue.unused.push_back(iso_transfer.unwrap()); if e == Error::NoDevice || e == Error::Io { - // When the USB device reports the preceding error, XHCI notifies the guest of the - // error through packet status. The guest initiallizes the device again. + // When the USB device reports the preceding error, XHCI notifies the guest + // of the error through packet status. The guest initiallizes the device + // again. packet.lock().unwrap().status = UsbPacketStatus::Stall; }; break; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a2d7b99b8..21ff229d0 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -243,7 +243,8 @@ impl XhciTransfer { } fn report_transfer_error(&mut self) -> Result<()> { - // An error occurs in the transfer. The transfer is set to the completed and will not be retried. + // An error occurs in the transfer. The transfer is set to the completed and will not be + // retried. self.complete = true; let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::TrbError); evt.slot_id = self.slotid as u8; @@ -1382,7 +1383,8 @@ impl XhciDevice { let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, - // It is safe to use plus here becuase we previously verify the address on the outer layer. + // It is safe to use plus here becuase we previously verify the address on the outer + // layer. GuestAddress(input_ctx + EP_INPUT_CTX_OFFSET + entry_offset), ep_ctx.as_mut_dwords(), )?; @@ -1397,7 +1399,8 @@ impl XhciDevice { ep_ctx.ep_info |= EP_RUNNING; dma_write_u32( &self.mem_space, - // It is safe to use plus here becuase we previously verify the address on the outer layer. + // It is safe to use plus here becuase we previously verify the address on the outer + // layer. GuestAddress(output_ctx + EP_CTX_OFFSET + entry_offset), ep_ctx.as_dwords(), )?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7b23073e4..00877932a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -663,7 +663,8 @@ pub trait MachineOps { fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { - // Because device_del needs an id when removing a device, it's necessary to ensure that the id is unique. + // Because device_del needs an id when removing a device, it's necessary to ensure that + // the id is unique. if name.is_empty() { bail!("Device id is empty"); } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 7ec39c7e9..8098b21b9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -807,7 +807,7 @@ impl MachineOps for LightMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { let mut locked_vm = vm.lock().unwrap(); - //trace for lightmachine + // trace for lightmachine trace_sysbus(&locked_vm.sysbus); trace_vm_state(&locked_vm.vm_state); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d65f409dd..9c1573824 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1231,7 +1231,8 @@ impl DeviceInterface for StdMachine { ); } - // Use args.bus.clone() and args.addr.clone() because args borrowed in the following process. + // Use args.bus.clone() and args.addr.clone() because args borrowed in the following + // process. let pci_bdf = match get_device_bdf(args.bus.clone(), args.addr.clone()) { Ok(bdf) => bdf, Err(e) => { @@ -1832,7 +1833,8 @@ impl DeviceInterface for StdMachine { } let mut info_str = "List of snapshots present on all disks:\r\n".to_string(); - // Note: VM state is "None" in disk snapshots. It's used for vm snapshots which we don't support. + // Note: VM state is "None" in disk snapshots. It's used for vm snapshots which we + // don't support. let vmstate_str = "None\r\n".to_string(); info_str += &vmstate_str; diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 1b2f41746..8a3818843 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -400,7 +400,7 @@ impl VmConfig { cmd_parser.push(""); cmd_parser.push("pmu"); cmd_parser.parse(features)?; - //Check PMU when actually enabling PMU. + // Check PMU when actually enabling PMU. if let Some(k) = cmd_parser.get_value::("pmu")? { self.machine_config.cpu_config.pmu = match k.as_ref() { "on" => PmuConfig::On, @@ -1106,7 +1106,7 @@ mod tests { #[cfg(target_arch = "aarch64")] #[test] fn test_cpu_features() { - //Test PMU flags + // Test PMU flags let mut vm_config = VmConfig::default(); vm_config.add_cpu_feature("host").unwrap(); assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index bb2defb98..b73833bcd 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -53,7 +53,7 @@ impl Default for ScsiCntlrConfig { ScsiCntlrConfig { id: "".to_string(), iothread: None, - //At least 1 cmd queue. + // At least 1 cmd queue. queues: 1, boot_prefix: None, queue_size: DEFAULT_VIRTQUEUE_SIZE, diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index 5608fd5d0..b6414ff73 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -229,8 +229,7 @@ impl Response { /// # Arguments /// /// * `v` - The `Value` of qmp `return` field. - /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s - /// `id`. + /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s `id`. pub fn create_response(v: Value, id: Option) -> Self { Response { return_: Some(v), @@ -252,8 +251,7 @@ impl Response { /// # Arguments /// /// * `err_class` - The `QmpErrorClass` of qmp `error` field. - /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s - /// `id`. + /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s `id`. pub fn create_error_response(err_class: schema::QmpErrorClass, id: Option) -> Self { Response { return_: None, diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index f79044a65..dee35681c 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -46,9 +46,9 @@ pub(crate) const LEAK_BUCKET_LIMIT: u64 = 100; /// # Example /// /// ```no_run -/// use std::os::unix::net::{UnixListener, UnixStream}; -/// use std::os::unix::io::AsRawFd; /// use std::io::prelude::*; +/// use std::os::unix::io::AsRawFd; +/// use std::os::unix::net::{UnixListener, UnixStream}; /// /// use machine_manager::socket::Socket; /// @@ -290,9 +290,9 @@ impl AsRawFd for SocketStream { /// # Examples /// /// ```no_run -/// use std::os::unix::net::UnixStream; -/// use std::os::unix::io::AsRawFd; /// use std::io::prelude::*; +/// use std::os::unix::io::AsRawFd; +/// use std::os::unix::net::UnixStream; /// /// use machine_manager::socket::SocketRWHandler; /// @@ -514,9 +514,9 @@ impl Write for SocketRWHandler { /// # Examples /// /// ```no_run -/// use std::os::unix::net::UnixStream; -/// use std::os::unix::io::AsRawFd; /// use std::io::prelude::*; +/// use std::os::unix::io::AsRawFd; +/// use std::os::unix::net::UnixStream; /// /// use machine_manager::socket::SocketHandler; /// diff --git a/migration/migration_derive/src/lib.rs b/migration/migration_derive/src/lib.rs index ad2946452..11736fc1a 100644 --- a/migration/migration_derive/src/lib.rs +++ b/migration/migration_derive/src/lib.rs @@ -40,9 +40,11 @@ //! } //! //! fn main() { -//! println!("Description of DeviceState is {:?}", DeviceState::descriptor()); +//! println!( +//! "Description of DeviceState is {:?}", +//! DeviceState::descriptor() +//! ); //! } -//! //! ``` //! //! 2. The `ByteCode` derive to auto add `ByteCode` trait and its relying trait for diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 3a6e18745..0f0155018 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -50,8 +50,8 @@ pub trait MigrationHook: StateTransfer { /// /// # Arguments /// - /// * `id` - This unique id to represent a single device. It can be treated - /// as `object_id` in `InstanceId`. + /// * `id` - This unique id to represent a single device. It can be treated as `object_id` in + /// `InstanceId`. /// * `fd` - The `Write` trait object to save device data. fn save_device(&self, id: u64, fd: &mut dyn Write) -> Result<()> { let state_data = self diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index aa02b0e7d..fd791d6ad 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -885,13 +885,10 @@ pub mod tests { #[test] fn test_desc_basic_padding() { - /* - * This test makes two version of a device. - * Those devices's difference is appending a new field `mcr` in - * device state. - * Add_padding can solve this change in descriptor of device state. - * Test can verify this function works. - */ + // This test makes two version of a device. Those devices's difference is appending a new + // field `mcr` in device state. + // Add_padding can solve this change in descriptor of device state. + // Test can verify this function works. let mut device_v1 = DeviceV1 { state: DeviceV1State::default(), @@ -932,13 +929,10 @@ pub mod tests { #[test] fn test_desc_data_type_padding() { - /* - * This test makes two version of a device. - * Those devices's difference is appending all fields data value changed from - * u8 to u64. - * Add_padding can solve this change in descriptor of device state. - * Test can verify this function works. - */ + // This test makes two version of a device. Those devices's difference is appending all + // fields data value changed from u8 to u64. + // Add_padding can solve this change in descriptor of device state. + // Test can verify this function works. let mut device_v2 = DeviceV2 { state: DeviceV2State::default(), }; @@ -978,13 +972,10 @@ pub mod tests { #[test] fn test_desc_field_name_padding() { - /* - * This test makes two version of a device. - * Those devices's difference is appending all fields name changed from - * u8 to u64. - * Add_padding can solve this change in descriptor of device state. - * Test can verify this function works. - */ + // This test makes two version of a device. Those devices's difference is appending all + // fields name changed from u8 to u64. + // Add_padding can solve this change in descriptor of device state. + // Test can verify this function works. let mut device_v3 = DeviceV3 { state: DeviceV3State::default(), }; @@ -1024,13 +1015,10 @@ pub mod tests { #[test] fn test_desc_field_delete_padding() { - /* - * This test makes two version of a device. - * Those devices's difference is appending all fields name changed from - * u8 to u64. - * Add_padding can solve this change in descriptor of device state. - * Test can verify this function works. - */ + // This test makes two version of a device. Those devices's difference is appending all + // fields name changed from u8 to u64. + // Add_padding can solve this change in descriptor of device state. + // Test can verify this function works. let mut device_v4 = DeviceV4 { state: DeviceV4State::default(), }; @@ -1067,12 +1055,9 @@ pub mod tests { #[test] fn test_desc_jump_version_padding() { - /* - * This test makes two version of a device. - * Those devices jump from v2 to v5 once. - * Add_padding can solve this change in descriptor of device state. - * Test can verify this function works. - */ + // This test makes two version of a device. Those devices jump from v2 to v5 once. + // Add_padding can solve this change in descriptor of device state. + // Test can verify this function works. let mut device_v2 = DeviceV2 { state: DeviceV2State::default(), }; diff --git a/ozone/src/cgroup.rs b/ozone/src/cgroup.rs index 5df3672cf..4bb79e6de 100644 --- a/ozone/src/cgroup.rs +++ b/ozone/src/cgroup.rs @@ -171,15 +171,16 @@ fn read_file_value(path: PathBuf) -> Result { } // Reason for inherit configuration: -// Ozone creates a new hierarchy: /sys/fs/cgroup/// in cgroup. As the value in -// current hierarchy should be a sub-aggregate of its parent hierarchy, in other words: value in "..// -// ///file" should be a sub-aggregate of that in "..///file". However, When -// creating the hierarchy "..///" values in "..///file" always -// be empty, which means that the attempts to set values in "..////file" will fail. -// In order to address this problem, Ozone inherit configuration from "..//file" to ""../ -// //file". -// IF many Ozones are launched with the same "exec_file", the first launched one will inherit configuration, other ones -// will not do that. +// Ozone creates a new hierarchy: /sys/fs/cgroup/// in cgroup. As the +// value in current hierarchy should be a sub-aggregate of its parent hierarchy, in other words: +// value in "..// ///file" should be a sub-aggregate of that in +// "..///file". However, When creating the hierarchy +// "..///" values in "..///file" always +// be empty, which means that the attempts to set values in +// "..////file" will fail. In order to address this problem, Ozone +// inherit configuration from "..//file" to ""../ //file". +// If many Ozones are launched with the same "exec_file", the first launched one will inherit +// configuration, other ones will not do that. fn inherit_config(path: &Path, file: &str) -> Result<()> { let upper_file = path.with_file_name(file); let value = read_file_value(upper_file.clone())?; diff --git a/tests/mod_test/src/libdriver/qcow2.rs b/tests/mod_test/src/libdriver/qcow2.rs index 6511a381d..ca07a6093 100644 --- a/tests/mod_test/src/libdriver/qcow2.rs +++ b/tests/mod_test/src/libdriver/qcow2.rs @@ -267,7 +267,8 @@ pub fn create_qcow2_img(image_path: String, image_size: u64) { /// By default, the data occupied by the l2 table and refcount table should not exceed one cluster. /// If the defined disk is too large, it may result in incorrect data format for. /// For example. -/// If you defined cluster size = 1 << 16, the max disk size cannout exceed the 1 << (16 * 2 - 3) = 512M. +/// If you defined cluster size = 1 << 16, the max disk size cannout exceed the +/// 1 << (16 * 2 - 3) = 512M. fn write_full_disk(image_path: String) { let mut qcow2 = Qcow2Driver::new(image_path); let cluster_bits = qcow2.header.cluster_bits; diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 2e377f2e1..e5cf7533a 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -1614,7 +1614,7 @@ impl TestXhciPciDevice { // subclass assert_eq!(buf[5], SC_VIDEO_INTERFACE_COLLECTION); - //2. VC interface + // 2. VC interface *offset += 8; let buf = self.get_transfer_data_indirect_with_offset( addr, @@ -1637,7 +1637,7 @@ impl TestXhciPciDevice { *offset += 0xd; let _buf = self.get_transfer_data_indirect_with_offset(addr, remained as usize, *offset); - //3. VS interface + // 3. VS interface *offset += remained as u64; let buf = self.get_transfer_data_indirect_with_offset( addr, diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index 1097046fb..368f9a8b8 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -804,7 +804,8 @@ impl VncClient { match sec_type { TestAuthType::VncAuthNone => { - // Step 3. Handle_auth: Authstate::No, Server accept auth and client send share mode. + // Step 3. Handle_auth: Authstate::No, Server accept auth and client send share + // mode. self.read_msg(&mut buf, 4)?; if buf[..4].to_vec() != [0_u8; 4].to_vec() { bail!("Reject by vnc server"); diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index 31da6432a..2c5b59dd0 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -157,7 +157,9 @@ fn check_iort(data: &[u8]) { assert_eq!(LittleEndian::read_u32(&data[88..]), 1); // Cache of coherent device assert_eq!(data[95], 3); // Memory flags of coherent device assert_eq!(LittleEndian::read_u32(&data[112..]), 0xffff); // Identity RID mapping - assert_eq!(LittleEndian::read_u32(&data[120..]), 48); // Without SMMU, id mapping is the first node in ITS group node + + // Without SMMU, id mapping is the first node in ITS group node + assert_eq!(LittleEndian::read_u32(&data[120..]), 48); } fn check_spcr(data: &[u8]) { @@ -176,8 +178,11 @@ fn check_spcr(data: &[u8]) { assert_eq!(data[58], 3); // Set baud rate: 3 = 9600 assert_eq!(data[60], 1); // Stop bit assert_eq!(data[61], 2); // Hardware flow control - assert_eq!(LittleEndian::read_u16(&data[64..]), 0xffff); // PCI Device ID: it is not a PCI device - assert_eq!(LittleEndian::read_u16(&data[66..]), 0xffff); // PCI Vendor ID: it is not a PCI device + + // PCI Device ID: it is not a PCI device + assert_eq!(LittleEndian::read_u16(&data[64..]), 0xffff); + // PCI Vendor ID: it is not a PCI device + assert_eq!(LittleEndian::read_u16(&data[66..]), 0xffff); } fn check_mcfg(data: &[u8]) { diff --git a/tests/mod_test/tests/ged_test.rs b/tests/mod_test/tests/ged_test.rs index c91b4a238..40d850952 100644 --- a/tests/mod_test/tests/ged_test.rs +++ b/tests/mod_test/tests/ged_test.rs @@ -48,11 +48,10 @@ fn set_up() -> TestState { /// Test the read and write functions of a ged device. /// /// Steps -/// 1. Send qmp command "system_powerdown" +/// 1. Send qmp command "system_powerdown". /// 2. Read ged event. /// 3. Read abnormal address, except 0. -/// 4. Write event and read, excepy 0 because -/// ged can't write. +/// 4. Write event and read, excepy 0 because ged can't write. #[test] #[cfg(target_arch = "aarch64")] fn test_shutdown() { @@ -76,11 +75,9 @@ fn test_shutdown() { /// Verify that the restart function is normal. /// /// Steps -/// 1. Send qmp command "system_powerdown" and -/// "system_reset" to achieve "reboot" +/// 1. Send qmp command "system_powerdown" and "system_reset" to achieve "reboot". /// 2. Read ged event. -/// 3. Send qmp command "query-status" to get -/// the status of vm, except "running". +/// 3. Send qmp command "query-status" to get the status of vm, except "running". #[test] #[cfg(target_arch = "aarch64")] fn test_reboot() { diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 3ac0ad154..7398a71cc 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -259,14 +259,16 @@ fn region_update_exception() { /// 1/2/6/7: Success. /// 4/5: Failed. /// 3: Got [0x02u8; 8] from the device. The read and write behavior is the same as io region. -/// 8: Got [0x00u8; 8] fro the device. The write operation does nothing, and read the original data. +/// 8: Got [0x00u8; 8] fro the device. The write operation does nothing, and read the original +/// data. #[test] fn rom_device_region_readwrite() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); let addr = 0x100_0000_0000; // 1TB - // Add a dummy rom device by qmp. The function of the device is to multiply the written value by 2 - // through the write interface and save it, and read the saved value through the read interface. + // Add a dummy rom device by qmp. The function of the device is to multiply the written value by + // 2 through the write interface and save it, and read the saved value through the read + // interface. let file = File::create(&ROM_DEV_PATH).unwrap(); file.set_len(PAGE_SIZE).unwrap(); let qmp_str = format!( @@ -599,8 +601,8 @@ fn ram_readwrite_exception() { /// Ram read and write Test. /// TestStep: /// 1. Start device. -/// 2. Write some data("test memory read write") to the address. -/// And the read/write will across numa. +/// 2. Write some data("test memory read write") to the address. And the read/write will across +/// numa. /// 3. Read data from the address and check it. /// 4. Destroy device. /// Expect: @@ -655,8 +657,8 @@ fn ram_readwrite_numa() { /// Ram read and write Test. /// TestStep: /// 1. Start device. -/// 2. Write some data("test memory read write") to the address. -/// And the read/write will across numa. +/// 2. Write some data("test memory read write") to the address. And the read/write will across +/// numa. /// 3. Read data from the address and check it. /// 4. Destroy device. /// Expect: diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 33ec90477..e61b6281a 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -979,8 +979,7 @@ fn write_net_config_check(net: Rc>, offset: u64, value /// Write value to virtio net configure, and check the write result. /// TestStep: /// 1. Init device. -/// 2. Write value to virtio net configure which can not be changed -/// except mac in some conditions. +/// 2. Write value to virtio net configure which can not be changed except mac in some conditions. /// 3. Destroy device. /// Expect: /// 1/2/3: success. diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 277f5a057..c6a0f2ed3 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -44,7 +44,6 @@ struct DemoDev { } fn fmt_demo_deves(cfg: DemoDev, num: u8) -> String { - // let mut dev_str = format!("-device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.{}", cfg.bus_num); let mut dev_str: String = String::new(); for i in 1..num + 1 { @@ -1122,7 +1121,8 @@ fn test_pci_type0_config() { 0xFFFF, ); - // verify that the lower three bits of the command register of type0 device is readable and writable. + // verify that the lower three bits of the command register of type0 device is readable and + // writable. validate_config_perm_2byte(blk.borrow().pci_dev.clone(), PCI_COMMAND, 0x4, 0x4, 0x7); // verify that the interrupt status of the status register of type0 device is read-only. @@ -2276,7 +2276,8 @@ fn test_pci_hotunplug_006() { tear_down(None, test_state, alloc, None, Some(image_paths)); } -/// Guest sets PIC/PCC twice during hotunplug, the device ignores the 2nd write to speed up hotunplug. +/// Guest sets PIC/PCC twice during hotunplug, the device ignores the 2nd write to speed up +/// hotunplug. #[test] fn test_pci_hotunplug_007() { let blk_nums = 1; @@ -2911,7 +2912,7 @@ fn test_pci_root_port_exp_cap() { fn test_pci_combine_000() { let cfg = DemoDev { bar_num: 3, - bar_size: 0x100_0000, //16MB + bar_size: 0x100_0000, // 16MB bus_num: 0, dev_num: 5, }; @@ -2938,7 +2939,7 @@ fn test_pci_combine_000() { fn test_pci_combine_001() { let cfg = DemoDev { bar_num: 3, - bar_size: 0x100_0000, //16MB + bar_size: 0x100_0000, // 16MB bus_num: 0, dev_num: 5, }; @@ -3035,7 +3036,7 @@ fn test_pci_combine_002() { fn test_pci_combine_003() { let mut cfg = DemoDev { bar_num: 3, - bar_size: 0x100_0000, //16MB + bar_size: 0x100_0000, // 16MB bus_num: 0, dev_num: 5, }; @@ -3045,7 +3046,7 @@ fn test_pci_combine_003() { // the mmio space is 78MB, bar1 got over bounded assert!(bar_addr != INVALID_BAR_ADDR); - cfg.bar_size = 0x1000_0000; //2GB + cfg.bar_size = 0x1000_0000; // 2GB let (pci_dev, _) = init_demo_dev(cfg, 1); let bar_addr = pci_dev.borrow().io_map(0); diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index 4d06f6b6e..3f969d3b9 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -120,7 +120,8 @@ fn stream_header_init(ivshmem: &mut TestIvshmemDev, base: u64, offset: u64) { // set channel_map ivshmem.writel(fmt_base + offset_of!(ShmemStreamFmt, channel_map) as u64, 3); - // Setting is_started, it must be set at the end. Otherwise, the fmt data may not be updated in time. + // Setting is_started, it must be set at the end. Otherwise, the fmt data may not be updated in + // time. ivshmem.writel(base + offset_of!(ShmemStreamHeader, is_started) as u64, 1); } @@ -253,7 +254,8 @@ fn scream_playback_basic_test() { thread::sleep(time::Duration::from_millis(1000)); - // When four consecutive frames of data are written, only the last two frames of data can be read. + // When four consecutive frames of data are written, only the last two frames of data can be + // read. for i in 0..AUDIO_CHUNK_SIZE { ivshmem .borrow_mut() diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 356ef11df..7ca9757ed 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -315,8 +315,8 @@ impl VirtioScsiTest { fn scsi_try_io(&mut self, target: u8, lun: u16, scsi_type: ScsiDeviceType) { // Test: scsi command: WRITE_10. // Write to LBA(logical block address) 0, transfer length 1 sector. - // Test Result: Check if scsi command WRITE_10 was handled successfully for scsi harddisk and - // was failure for scsi CD-ROM. + // Test Result: Check if scsi command WRITE_10 was handled successfully for scsi harddisk + // and was failure for scsi CD-ROM. let mut write_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; write_cdb[0] = WRITE_10; write_cdb[8] = 0x1; // 1 logical sector. CD: 2048 Bytes. HD: 512 Bytes. @@ -346,8 +346,8 @@ impl VirtioScsiTest { // Test: scsi command: READ_10. // Read from LBA(logical block address) 0, transfer length 1. - // Test Result: Check if scsi command READ_10 was handled successfully. And check the read data is - // the right data which was sent in WRITE_10 test for scsi harddisk. + // Test Result: Check if scsi command READ_10 was handled successfully. And check the read + // data is the right data which was sent in WRITE_10 test for scsi harddisk. let mut read_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; read_cdb[0] = READ_10; read_cdb[8] = 0x1; // 1 sector. @@ -635,8 +635,8 @@ fn scsi_test_init( /// Virtio Scsi hard disk basic function test. target 31, lun 7. /// TestStep: /// 0. Init process. -/// 1. Traverse all possible targets from 0 to VIRTIO_SCSI_MAX_TARGET(255). -/// (using scsi command INQUIRY) (lun is always 0 in this traverse process). +/// 1. Traverse all possible targets from 0 to VIRTIO_SCSI_MAX_TARGET(255). (using scsi command +/// INQUIRY) (lun is always 0 in this traverse process). /// 2. Get all luns info in target 31.(using scsi command REPORT_LUNS) /// 3. Check if scsi device is OK.(using scsi command TEST_UNIT_READY) /// 4. Get the capacity of the disk.(using scsi command READ_CAPACITY_10) @@ -666,7 +666,8 @@ fn scsi_hd_basic_test() { inquiry_cdb[0] = INQUIRY; inquiry_cdb[4] = INQUIRY_DATA_LEN; for i in 0..32 { - // Test 1 Result: Only response 0 for target == 31. Otherwise response VIRTIO_SCSI_S_BAD_TARGET. + // Test 1 Result: Only response 0 for target == 31. Otherwise response + // VIRTIO_SCSI_S_BAD_TARGET. let expect_result = if i == target as u16 { VIRTIO_SCSI_S_OK } else { @@ -750,7 +751,8 @@ fn scsi_hd_basic_test() { }; let data_in = vst.scsi_cdb_test(cdb_test_args); - // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical block). + // Bytes[0-3]: Returned Logical Block Address(the logical block address of the last logical + // block). // Bytes[4-7]: Logical Block Length In Bytes. // Total size = (last logical block address + 1) * block length. assert_eq!( @@ -1092,14 +1094,14 @@ fn scsi_cd_basic_test() { // Byte[8]: BUF/Multi Session(1)/Mode 2 Form 2(1)/Mode 2 Form 1(1)/Digital Port 2(1)/ // Digital Port 1(1)/Composite(1)/Audio Play(1). expect_result_vec[8] = 0x7f; - // Byte[9]: Read Bar Code(1)/UPC(1)/ISRC(1)/C2 Pointers supported(1)/R-W Deinterleaved & corrected(1)/ - // R-W supported(1)/CD-DA Stream is Accurate(1)/CD-DA Cmds supported(1). + // Byte[9]: Read Bar Code(1)/UPC(1)/ISRC(1)/C2 Pointers supported(1)/R-W Deinterleaved & + // corrected(1)/R-W supported(1)/CD-DA Stream is Accurate(1)/CD-DA Cmds supported(1). expect_result_vec[9] = 0xff; // Byte[10]: Bits[5-7]: Loading Mechanism Type(1)/Reserved/Eject(1)/Prevent Jumper(1)/ // Lock State/Lock(1). expect_result_vec[10] = 0x2d; - // Byte[11]: Bits[6-7]: Reserved/R-W in Lead-in/Side Change Capable/SSS/Changer Supports Disc Present/ - // Separate Channel Mute/Separate volume levels + // Byte[11]: Bits[6-7]: Reserved/R-W in Lead-in/Side Change Capable/SSS/Changer Supports Disc + // Present/Separate Channel Mute/Separate volume levels // Bytes[12-13]: Obsolete. // Bytes[14-15]: Number of Volume Levels Supported. expect_result_vec[15] = 0x2; @@ -1141,8 +1143,8 @@ fn scsi_cd_basic_test() { // Test 3: scsi command: READ_TOC. // Test 3.1: // Byte1 bit1: MSF = 0. Byte2 bits[0-3]: Format = 0; - // Test 3.1 Result: Check if scsi command READ_TOC was handled successfully. And check the read data - // is the same with the expect result. + // Test 3.1 Result: Check if scsi command READ_TOC was handled successfully. And check the read + // data is the same with the expect result. let mut read_toc_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; read_toc_cdb[0] = READ_TOC; read_toc_cdb[8] = READ_TOC_DATA_LEN; @@ -1185,8 +1187,8 @@ fn scsi_cd_basic_test() { // Byte1 bit1: MSF = 1. // Byte2 bits[0-3]: Format = 0; (Format(Select specific returned data format)(CD: 0,1,2)). // Byte6: Track/Session Number. - // Test 3.2 Result: Check if scsi command READ_TOC was handled successfully. And check the read data - // is the same with the expect result. + // Test 3.2 Result: Check if scsi command READ_TOC was handled successfully. And check the read + // data is the same with the expect result. let mut read_toc_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; read_toc_cdb[0] = READ_TOC; read_toc_cdb[1] = 2; @@ -1218,8 +1220,8 @@ fn scsi_cd_basic_test() { // Byte1 bit1: MSF = 0. // Byte2 bits[0-3]: Format = 1; (Format(Select specific returned data format)(CD: 0,1,2)). // Byte6: Track/Session Number. - // Test 3.3 Result: Check if scsi command READ_TOC was handled successfully. And check the read data - // is the same with the expect result. + // Test 3.3 Result: Check if scsi command READ_TOC was handled successfully. And check the read + // data is the same with the expect result. let mut read_toc_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; read_toc_cdb[0] = READ_TOC; read_toc_cdb[2] = 1; @@ -1239,19 +1241,21 @@ fn scsi_cd_basic_test() { vst.scsi_cdb_test(cdb_test_args); // Test 4: scsi command: READ_DISC_INFORMATION. - // Test 4 Result: Check if scsi command READ_DISC_INFORMATION was handled successfully. And check the read data - // is the same with the expect result. + // Test 4 Result: Check if scsi command READ_DISC_INFORMATION was handled successfully. And + // check the read data is the same with the expect result. let mut read_disc_information_cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE] = [0; TEST_VIRTIO_SCSI_CDB_SIZE]; read_disc_information_cdb[0] = READ_DISC_INFORMATION; read_disc_information_cdb[8] = READ_DISC_INFORMATION_DATA_LEN; // Bytes[0-1]: Disc Information Length(32). - // Byte2: Disc Information Data Type(000b) | Erasable(0) | State of last Session(01b) | Disc Status(11b). + // Byte2: Disc Information Data Type(000b) | Erasable(0) | State of last Session(01b) | + // Disc Status(11b). // Byte3: Number of First Track on Disc. // Byte4: Number of Sessions. // Byte5: First Track Number in Last Session(Least Significant Byte). // Byte6: Last Track Number in Last Session(Last Significant Byte). - // Byte7: DID_V | DBC_V | URU:Unrestricted Use Disc(1) | DAC_V | Reserved | Legacy | BG Format Status. + // Byte7: DID_V | DBC_V | URU:Unrestricted Use Disc(1) | DAC_V | Reserved | Legacy | + // BG Format Status. // Byte8: Disc Type(00h: CD-DA or CD-ROM Disc). // Byte9: Number of sessions(Most Significant Byte). // Byte10: First Trace Number in Last Session(Most Significant Byte). @@ -1279,8 +1283,8 @@ fn scsi_cd_basic_test() { // Test 5: scsi command: GET_CONFIGURATION. // The size of test img is TEST_IMAGE_SIZE(64M), so it is a CD-ROM. - // Test 5 Result: Check if scsi command GET_CONFIGURATION was handled successfully. And check the read data - // is the same with the expect result. + // Test 5 Result: Check if scsi command GET_CONFIGURATION was handled successfully. And check + // the read data is the same with the expect result. let mut get_configuration_cdb = [0_u8; TEST_VIRTIO_SCSI_CDB_SIZE]; get_configuration_cdb[0] = GET_CONFIGURATION; get_configuration_cdb[8] = GET_CONFIGURATION_DATA_LEN; @@ -1311,7 +1315,8 @@ fn scsi_cd_basic_test() { // Bytes[20-31]: Feature 1: Core Feature: // Bytes[20-21]: Feature Code(0001h). expect_result_vec[21] = 0x1; - // Byte[22]: Bits[6-7]: Reserved. Bits[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + // Byte[22]: Bits[6-7]: Reserved. Bits[2-5]: Version(0010b). Bit 1: Persistent(1). + // Bit 0: Current(1). expect_result_vec[22] = 0xb; // Byte[23]: Additional Length(8). expect_result_vec[23] = 8; @@ -1323,12 +1328,13 @@ fn scsi_cd_basic_test() { // Bytes[32-40]: Feature 2: Removable media feature: // Bytes[32-33]: Feature Code(0003h). expect_result_vec[33] = 3; - // Byte[34]: Bits[6-7]: Reserved. Bit[2-5]: Version(0010b). Bit 1: Persistent(1). Bit 0: Current(1). + // Byte[34]: Bits[6-7]: Reserved. Bit[2-5]: Version(0010b). Bit 1: Persistent(1). + // Bit 0: Current(1). expect_result_vec[34] = 0xb; // Byte[35]: Additional Length(4). expect_result_vec[35] = 4; - // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). Bit 2: Pvnt Jmpr. - // Bit 1: DBML. Bit 0: Lock(1). + // Byte[36]: Bits[5-7]: Loading Mechanism Type(001b). Bit4: Load(1). Bit 3: Eject(1). + // Bit 2: Pvnt Jmpr. Bit 1: DBML. Bit 0: Lock(1). expect_result_vec[36] = 0x39; // Byte[37-39]: Reserved. let cdb_test_args = CdbTest { @@ -1345,8 +1351,8 @@ fn scsi_cd_basic_test() { vst.scsi_cdb_test(cdb_test_args); // Test 6: scsi command: GET_EVENT_STATUS_NOTIFICATION. - // Test 6 Result: Check if scsi command GET_EVENT_STATUS_NOTIFICATION was handled successfully. And check the read data - // is the same with the expect result. + // Test 6 Result: Check if scsi command GET_EVENT_STATUS_NOTIFICATION was handled successfully. + // And check the read data is the same with the expect result. let mut get_event_status_notification_cdb: [u8; TEST_VIRTIO_SCSI_CDB_SIZE] = [0; TEST_VIRTIO_SCSI_CDB_SIZE]; get_event_status_notification_cdb[0] = GET_EVENT_STATUS_NOTIFICATION; @@ -1647,8 +1653,8 @@ struct VirtioScsiConfig { /// can be set from guest. /// TestStep: /// 1. Init process. -/// 2. For every parameter in VirtioScsiConfig, do check just like: -/// Read default value -> Set other value -> Read value again -> Check if value was set successfully. +/// 2. For every parameter in VirtioScsiConfig, do check just like: Read default value -> Set +/// other value -> Read value again -> Check if value was set successfully. /// 3. Destroy device. /// Note: /// 1. sense size and cdb size can not be changed in stratovirt now. So, they are 0 now. @@ -1896,7 +1902,7 @@ fn aio_model_test() { // Scsi Disk 3. AIO OFF. Direct true. This is not allowed. // Stratovirt will report "low performance expect when use sync io with direct on" - //Scsi Disk 4. AIO OFF. Direct false. + // Scsi Disk 4. AIO OFF. Direct false. lun += 1; let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw)); device_vec.push(ScsiDeviceConfig { diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index beea92a67..47cfdc4bd 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -162,7 +162,8 @@ impl SerialTest { // Fill a batch of buffers elements in queues[$queue_id]. fn fill_buffer_in_vq(&mut self, queue_id: usize) -> (Vec, Vec) { - // Note: limited by the MST framework, we only allocate 32 * 1K sized buffers. It's enough for test. + // Note: limited by the MST framework, we only allocate 32 * 1K sized buffers. It's enough + // for test. let mut buf_addrs = Vec::with_capacity(32); let mut free_heads = Vec::with_capacity(32); for _ in 0..32 { @@ -664,11 +665,13 @@ fn virtconsole_pty_err_out_control_msg() { st.serial_init(); - // Error out control msg which has invalid event. Just discard this invalid msg. Nothing happened. + // Error out control msg which has invalid event. Just discard this invalid msg. Nothing + // happened. let invalid_event_msg = VirtioConsoleControl::new(nr as u32, VIRTIO_CONSOLE_PORT_NAME, 1); st.out_control_event(invalid_event_msg); - // Error out control msg which has non-existed port id. Just discard this invalid msg. Nothing happened. + // Error out control msg which has non-existed port id. Just discard this invalid msg. Nothing + // happened. let invalid_event_msg = VirtioConsoleControl::new((nr + 5) as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); st.out_control_event(invalid_event_msg); @@ -689,8 +692,10 @@ fn virtconsole_pty_err_out_control_msg() { /// Virtio serial pci device invalid input control message buffer test. /// TestStep: /// 1. Init virtio serial device(1 virtconsole, pty backend chardev). -/// 2. Don't provide buffer in input_control_queue. Send a message which should response in input_control_queue. -/// 3. Provide 1 byte buffer in input_control_queue. Send a message which should response in input_control_queue. +/// 2. Don't provide buffer in input_control_queue. Send a message which should response in +/// input_control_queue. +/// 3. Provide 1 byte buffer in input_control_queue. Send a message which should response in +/// input_control_queue. /// 4. Destroy device. /// Expect: /// 1/4: success. @@ -710,7 +715,8 @@ fn virtconsole_pty_invalid_in_control_buffer() { // Init virtqueues. st.virtqueue_setup(DEFAULT_SERIAL_VIRTQUEUES); - // No buffer in input_control_queue. Will discard all requests sent by input_control_queue. Nothing else happened. + // No buffer in input_control_queue. Will discard all requests sent by input_control_queue. + // Nothing else happened. let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 1); st.out_control_event(ready_msg); @@ -721,7 +727,8 @@ fn virtconsole_pty_invalid_in_control_buffer() { size_of::() as u64, ); - // Error control msg: Guest is not ready. It will do nothing. Buffer in input_control_queue will not be used. + // Error control msg: Guest is not ready. It will do nothing. Buffer in input_control_queue will + // not be used. let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_DEVICE_READY, 0); st.out_control_event(ready_msg); @@ -732,11 +739,13 @@ fn virtconsole_pty_invalid_in_control_buffer() { // Give only 1 byte for input control message which will result virtio error. st.virtqueue_add_element(IN_CONTROL_QUEUE_ID, None, 1); - // Error control msg: Port is not ready. It will do nothing. Buffer in input_control_queue will not be used. + // Error control msg: Port is not ready. It will do nothing. Buffer in input_control_queue will + // not be used. let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_READY, 0); st.out_control_event(ready_msg); - // Console is default host connected. Should response VIRTIO_CONSOLE_CONSOLE_PORT msg. 1 byte Buffer will be used. + // Console is default host connected. Should response VIRTIO_CONSOLE_CONSOLE_PORT msg. 1 byte + // Buffer will be used. let ready_msg = VirtioConsoleControl::new(0, VIRTIO_CONSOLE_PORT_READY, 1); st.out_control_event(ready_msg); @@ -781,11 +790,13 @@ fn virtserialport_socket_not_connect() { st.serial_init(); - // Requests will be discarded when host (port 1, output queue id: 5) is not connected. Nothing happened. + // Requests will be discarded when host (port 1, output queue id: 5) is not connected. Nothing + // happened. let test_data = String::from("Test\n"); st.virtqueue_add_element(5, Some(test_data.as_bytes()), test_data.len() as u64); - // Requests will be discarded when it is sent in virtqueue which has no port(port 2, output queue id: 7). Nothing happened. + // Requests will be discarded when it is sent in virtqueue which has no port(port 2, output + // queue id: 7). Nothing happened. let test_data = String::from("Test\n"); st.virtqueue_add_element(7, Some(test_data.as_bytes()), test_data.len() as u64); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 98742e184..cb1f33cf6 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -1917,7 +1917,7 @@ fn test_xhci_keyboard_device_init_device_request_repeat() { xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - //get protocol + // get protocol xhci.get_protocol(slot_id); xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 08ed05bb1..0e7cb2374 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -230,8 +230,7 @@ fn do_event_idx_with_flag(flag: u16) { assert_eq!(status, VIRTIO_BLK_S_OK); // DEFAULT_IO_REQS write requests: - // Write "TEST" to sector 0 to DEFAULT_IO_REQS. - //let mut req_addr = 0_u64; + // Write "TEST" to sector 0 to DEFAULT_IO_REQS. for i in 1..DEFAULT_IO_REQS { (_, req_addr) = add_request( test_state.clone(), @@ -692,8 +691,8 @@ fn virtio_feature_indirect_and_event_idx() { /// TestStep: /// 1. Init device. /// 1) set device status: special status and random status. -/// 2) ACKNOWLEDGE -> DRIVER -> DRIVER -> negotiate_features -> FEATURES_OK -/// -> setup_virtqueue -> DRIVER_OK. +/// 2) ACKNOWLEDGE -> DRIVER -> DRIVER -> negotiate_features -> FEATURES_OK -> setup_virtqueue +/// -> DRIVER_OK. /// 2. Do the I/O request. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. @@ -1090,13 +1089,13 @@ fn virtio_init_device_abnormal_vring_info() { /// Init device out of order test 1. /// TestStep: /// 1. Abnormal init device. -/// 1.1->1.3->1.2->1.4->1.5->1.6->1.7->1.8 -/// 1.1->1.2->1.4->1.3->1.5->1.6->1.7->1.8 -/// 1.1->1.2->1.3->1.5->1.4->1.6->1.7->1.8 -/// 1.1->1.2->1.3->1.4->1.6->1.5->1.7->1.8 -/// 1.1->1.2->1.3->1.4->1.7->1.6->1.5->1.8 -/// 2. Normal init device. -/// 3. Write and read. +/// 1.1->1.3->1.2->1.4->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.4->1.3->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.5->1.4->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.6->1.5->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.7->1.6->1.5->1.8 +/// 2. Normal init device. +/// 3. Write and read. /// 4. Destroy device. /// Expect: /// 1/2: success or failed, stratovirt process status is normal. @@ -1149,13 +1148,13 @@ fn virtio_init_device_out_of_order_1() { /// Init device out of order test 2. /// TestStep: /// 1. Abnormal init device. -/// 1.1->1.2->1.3->1.4->1.8->1.6->1.7->1.5 -/// 1.1->1.3->1.4->1.5->1.6->1.7->1.8 -/// 1.1->1.2->1.4->1.5->1.6->1.7->1.8 -/// 1.1->1.2->1.3->1.4->1.6->1.7->1.8 -/// 1.1->1.2->1.3->1.4->1.5->1.6->1.8 -/// 2. Normal init device. -/// 3. Write and read. +/// 1.1->1.2->1.3->1.4->1.8->1.6->1.7->1.5 +/// 1.1->1.3->1.4->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.4->1.5->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.6->1.7->1.8 +/// 1.1->1.2->1.3->1.4->1.5->1.6->1.8 +/// 2. Normal init device. +/// 3. Write and read. /// 4. Destroy device. /// Expect: /// 1/2: success or failed, stratovirt process status is normal. @@ -1208,13 +1207,13 @@ fn virtio_init_device_out_of_order_2() { /// Init device out of order test 3. /// TestStep: /// 1. Abnormal init device. -/// 1.1->1.2->1.3->1.4->1.5->1.6->1.7 -/// 1.1->1.2->1.3->1.4->1.9 -/// 1.1->1.2->1.3->1.5->1.8 -/// 1.1->1.2->1.3->1.4->1.9(FAILED)->normal init process -/// 1.1->1.2->1.3->1.4->1.9(FAILED)->1.2->1.3->1.4->1.5->1.6->1.7->1.8 -/// 2. Normal init device. -/// 3. Write and read. +/// 1.1->1.2->1.3->1.4->1.5->1.6->1.7 +/// 1.1->1.2->1.3->1.4->1.9 +/// 1.1->1.2->1.3->1.5->1.8 +/// 1.1->1.2->1.3->1.4->1.9(FAILED)->normal init process +/// 1.1->1.2->1.3->1.4->1.9(FAILED)->1.2->1.3->1.4->1.5->1.6->1.7->1.8 +/// 2. Normal init device. +/// 3. Write and read. /// 4. Destroy device. /// Expect: /// 1/2: success or failed, stratovirt process status is normal. @@ -1404,8 +1403,8 @@ fn virtio_io_abnormal_desc_addr() { /// 2) 0x5000 with 1 request 3 desc elems; /// 3) u32::MAX with 1 request 3 desc elems; /// 4) u32::MAX with 2 request to test overflow; -/// 5) total length of all desc is bigger than (1 << 32): -/// ((1 << 32) / 64) with indirect request which has 65 desc elems; +/// 5) total length of all desc is bigger than (1 << 32): ((1 << 32) / 64) with indirect request +/// which has 65 desc elems; /// 6) test the invalid length of the indirect desc. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. @@ -1572,8 +1571,8 @@ fn virtio_io_abnormal_desc_flags_1() { /// Setting abnormal desc flag in IO request, testcase 2. /// TestStep: /// 1. Init device, negotiate INDIRECT_DESC feature. -/// 2. Do the I/O request with abnormal desc[i]->flags: -/// add VRING_DESC_F_INDIRECT to flags in indirect desc table. +/// 2. Do the I/O request with abnormal desc[i]->flags: add VRING_DESC_F_INDIRECT to flags in +/// indirect desc table. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. /// Expect: @@ -1639,9 +1638,9 @@ fn virtio_io_abnormal_desc_flags_2() { /// Setting abnormal desc flag in IO request, testcase 3. /// TestStep: /// 1. Init device, negotiate INDIRECT_DESC feature. -/// 2. Do the I/O request with abnormal desc[i]->flags: -/// add VRING_DESC_F_INDIRECT | VRING_DESC_F_WRITE to flags in indirect desc table, -/// and the device will ignore the VRING_DESC_F_WRITE flag. +/// 2. Do the I/O request with abnormal desc[i]->flags: add VRING_DESC_F_INDIRECT | +/// VRING_DESC_F_WRITE to flags in indirect desc table, and the device will ignore the +/// VRING_DESC_F_WRITE flag. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. /// Expect: @@ -1670,7 +1669,7 @@ fn virtio_io_abnormal_desc_flags_3() { let free_head = vqs[0] .borrow_mut() .add(test_state.clone(), req_addr, 8, false); - //vqs[0].borrow().set_desc_flag(free_head, VRING_DESC_F_NEXT); + let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() @@ -1790,8 +1789,7 @@ fn virtio_io_abnormal_desc_next() { /// Setting desc elems in abnormal place in IO request. /// TestStep: /// 1. Init device. -/// 2. Do the I/O request with writable desc elem before -/// readable desc elem. +/// 2. Do the I/O request with writable desc elem before readable desc elem. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. /// Expect: @@ -1848,8 +1846,7 @@ fn virtio_io_abnormal_desc_elem_place() { /// Setting (queue_size + 1) indirect desc elems in IO request. /// TestStep: /// 1. Init device with INDIRECT feature. -/// 2. Do the I/O request with (queue_size + 1) desc elems in -/// indirect desc table. +/// 2. Do the I/O request with (queue_size + 1) desc elems in indirect desc table. /// 3. Send qmp to StratoVirt. /// 4. Destroy device. /// Expect: diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 0a668904e..2a8d521ac 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -33,7 +33,7 @@ const DEFAULT_FS_DESC_ELEM: usize = 4; // 4 elems: inheader/inbody/outheader/out const TIMEOUT_US: u64 = 10 * 1000 * 1000; // 10s timeout. const PARENT_NODEID: u64 = 1; // parent dir nodeid. const TEST_MEM_SIZE: u64 = 1024; // 1G mem size. -const TEST_PAGE_SIZE: u64 = 4096; //4k page size. +const TEST_PAGE_SIZE: u64 = 4096; // 4k page size. const TEST_FILE_NAME: &str = "testfile"; const TEST_CHARDEV_NAME: &str = "testchar"; const DEFAULT_READ_SIZE: usize = 1024; // 1024 Bytes. diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index 4381814f2..e4f4fdf16 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -226,7 +226,8 @@ fn test_set_multiple_area_dirty() { /// 9. Demo GPU update the abnormal cursor image of VNC server -> expect 2. /// ExpectOutput: /// 1. The client receives the cursor image, and the format meets expect. -/// 2. The state of VNC client and server are normal, and the next normal connection will not be effect. +/// 2. The state of VNC client and server are normal, and the next normal connection will not be +/// effect. #[test] fn test_send_cursor_image() { let port: u16 = 1; @@ -529,8 +530,10 @@ fn test_set_pixel_format() { /// 2. VNC client send key event -> expect 1. /// 3. VNC client send pointer event -> expect 2. /// ExpectOutput: -/// 1. VNC server received the keyboard event, the observed key value in demo keyboard device meets the expectation. -/// 2. VNC server received the pointer event, the observed coordinate in demo pointer device has been changed. +/// 1. VNC server received the keyboard event, the observed key value in demo keyboard device meets +/// the expectation. +/// 2. VNC server received the pointer event, the observed coordinate in demo pointer device has +/// been changed. #[test] fn test_vnc_kbd_mouse() { let port: u16 = 4; @@ -593,7 +596,8 @@ fn test_vnc_kbd_mouse() { /// 2. VNC client setting feature of EncodingDesktopresize. /// 3. VNC client send the key event of Ctl+Alt+Num -> expect 1. /// ExpectOutput: -/// 1. The activate display device is be changed, and the VNC client receive the message of desktopresize. +/// 1. The activate display device is be changed, and the VNC client receive the message of +/// desktopresize. #[test] fn test_switch_display_device() { let port: u16 = 5; diff --git a/ui/src/console.rs b/ui/src/console.rs index 890b19004..eca469003 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -240,8 +240,8 @@ struct DisplayState { refresh_num: i32, } -// SAFETY: The Arc in rust doesn't impl Send, it will be delivered only once during initialization process, -// and only be saved in the single thread. So implement Send is safe. +// SAFETY: The Arc in rust doesn't impl Send, it will be delivered only once during +// initialization process, and only be saved in the single thread. So implement Send is safe. unsafe impl Send for DisplayState {} impl DisplayState { @@ -283,8 +283,10 @@ pub struct ConsoleList { } // SAFETY: -// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image by this pointer. -// 2. The Arc in rust doesn't impl Send, it will be delivered only once during initialization process, +// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image +// by this pointer. +// 2. The Arc in rust doesn't impl Send, it will be delivered only once during +// initialization process, // and only be saved in the single thread. // So implement Send is safe. unsafe impl Send for ConsoleList {} diff --git a/ui/src/pixman.rs b/ui/src/pixman.rs index e191509b1..087abfe94 100644 --- a/ui/src/pixman.rs +++ b/ui/src/pixman.rs @@ -254,49 +254,49 @@ pub const COLOR_TABLE_RGB: [[pixman_color_t; 8]; 2] = [ green: 0x00 << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* black */ + }, // black pixman_color_t { red: 0x00 << 8, green: 0x00 << 8, blue: 0xaa << 8, alpha: 0xffff, - }, /* blue */ + }, // blue pixman_color_t { red: 0x00 << 8, green: 0xaa << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* green */ + }, // green pixman_color_t { red: 0x00 << 8, green: 0xaa << 8, blue: 0xaa << 8, alpha: 0xffff, - }, /* cyan */ + }, // cyan pixman_color_t { red: 0xaa << 8, green: 0x00 << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* red */ + }, // red pixman_color_t { red: 0xaa << 8, green: 0x00 << 8, blue: 0xaa << 8, alpha: 0xffff, - }, /* magenta */ + }, // magenta pixman_color_t { red: 0xaa << 8, green: 0xaa << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* yellow */ + }, // yellow pixman_color_t { red: 0xaa << 8, green: 0xaa << 8, blue: 0xaa << 8, alpha: 0xffff, - }, /* white */ + }, // white ], [ pixman_color_t { @@ -304,49 +304,49 @@ pub const COLOR_TABLE_RGB: [[pixman_color_t; 8]; 2] = [ green: 0x00 << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* black */ + }, // black pixman_color_t { red: 0x00 << 8, green: 0x00 << 8, blue: 0xff << 8, alpha: 0xffff, - }, /* blue */ + }, // blue pixman_color_t { red: 0x00 << 8, green: 0xff << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* green */ + }, // green pixman_color_t { red: 0x00 << 8, green: 0xff << 8, blue: 0xff << 8, alpha: 0xffff, - }, /* cyan */ + }, // cyan pixman_color_t { red: 0xff << 8, green: 0x00 << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* red */ + }, // red pixman_color_t { red: 0xff << 8, green: 0x00 << 8, blue: 0xff << 8, alpha: 0xffff, - }, /* magenta */ + }, // magenta pixman_color_t { red: 0xff << 8, green: 0xff << 8, blue: 0x00 << 8, alpha: 0xffff, - }, /* yellow */ + }, // yellow pixman_color_t { red: 0xff << 8, green: 0xff << 8, blue: 0xff << 8, alpha: 0xffff, - }, /* white */ + }, // white ], ]; diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index ef5f7f5ca..0207ee284 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -210,8 +210,9 @@ impl ClientIoHandler { // Start authentication. let err: c_int = match security.saslconfig.sasl_stage { - // SAFETY: sasl_server_start() and sasl_server_step() is C function. All parameters passed of the - // function have been checked. Memory will be allocated for the incoming pointer inside the function. + // SAFETY: sasl_server_start() and sasl_server_step() is C function. All parameters + // passed of the function have been checked. Memory will be allocated for the incoming + // pointer inside the function. SaslStage::SaslServerStart => unsafe { sasl_server_start( security.saslconfig.sasl_conn, @@ -311,9 +312,9 @@ impl ClientIoHandler { info!("local_addr: {} remote_addr: {}", local_addr, remote_addr); let local_addr = CString::new(local_addr)?; let remote_addr = CString::new(remote_addr)?; - // SAFETY: sasl_server_init() and sasl_server_new() is C function. All parameters passed of the - // function have been checked. Memory will be allocated for the incoming pointer inside the function. - // Sasl server init. + // SAFETY: sasl_server_init() and sasl_server_new() is C function. All parameters passed of + // the function have been checked. Memory will be allocated for the incoming pointer inside + // the function. Sasl server init. unsafe { err = sasl_server_init(ptr::null_mut(), appname.as_ptr()); } diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index 6f4c5699b..0bada3d58 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -126,7 +126,7 @@ fn compress_each_tile<'a>( client_dpm, &mut tmp_buf, ); - //If the length becomes longer after compression, give up compression. + // If the length becomes longer after compression, give up compression. if tmp_buf.len() > (sub_rect.h * sub_rect.w * client_dpm.pf.pixel_bytes as i32) as usize { flag = RAW; diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 7c0797e6b..a438a5e34 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -78,8 +78,10 @@ pub struct VncServer { } // SAFETY: -// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image by this pointer. -// 2. It can be sure that Rc> and Rc> are used only in single thread. +// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image +// by this pointer. +// 2. It can be sure that Rc> and Rc> are used only in +// single thread. // So implement Send and Sync is safe. unsafe impl Send for VncServer {} unsafe impl Sync for VncServer {} diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index 8b9c5c3bc..ad7d1fcb1 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -59,9 +59,7 @@ pub enum HelpType { /// .author("example") /// .version("0.0.1") /// .about("Description for application") -/// .arg( -/// Arg::with_name("arg_name") -/// ) +/// .arg(Arg::with_name("arg_name")) /// .get_matches(); /// ``` #[derive(Clone, Debug, Default)] diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 1d39fb14f..5dd65f2f0 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -36,8 +36,8 @@ pub struct LeakBucket { prev_time: Instant, /// Indicate whether the timer started. timer_started: bool, - /// When bucket is ready for allowing more IO operation, the internal callback will write this FD. - /// This FD should be listened by IO thread. + /// When bucket is ready for allowing more IO operation, the internal callback will write this + /// FD. This FD should be listened by IO thread. timer_wakeup: Arc, } diff --git a/util/src/offsetof.rs b/util/src/offsetof.rs index a6b55d8c0..da6f4c4d8 100644 --- a/util/src/offsetof.rs +++ b/util/src/offsetof.rs @@ -16,13 +16,12 @@ macro_rules! __offset_of { ($type_name:ty, $field:ident) => {{ let tmp = core::mem::MaybeUninit::<$type_name>::uninit(); let outer = tmp.as_ptr(); - // Safe because the pointer is valid and aligned, just not initialised; `addr_of` ensures that - // we don't actually read from `outer` (which would be UB) nor create an intermediate reference. + // Safe because the pointer is valid and aligned, just not initialised; `addr_of` ensures + // that we don't actually read from `outer` (which would be UB) nor create an + // intermediate reference. let inner = unsafe { core::ptr::addr_of!((*outer).$field) } as *const u8; // Safe because the two pointers are within the same allocation block. - unsafe { - inner.offset_from(outer as *const u8) as usize - } + unsafe { inner.offset_from(outer as *const u8) as usize } }}; } diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 5224aabc1..c5ea7e34d 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -50,19 +50,20 @@ //! let mut seccomp_filter = SyscallFilter::new(SeccompOpt::Trap); //! //! let nr_open = { -//! #[cfg(target_arch="x86_64")] +//! #[cfg(target_arch = "x86_64")] //! let nr = libc::SYS_open; -//! #[cfg(target_arch="aarch64")] +//! #[cfg(target_arch = "aarch64")] //! let nr = libc::SYS_openat; //! nr //! }; //! //! seccomp_filter.push(&mut BpfRule::new(nr_open)); //! seccomp_filter.push(&mut BpfRule::new(libc::SYS_fcntl)); -//! seccomp_filter.push( -//! &mut BpfRule::new(libc::SYS_read) -//! .add_constraint(SeccompCmpOpt::Ne, 2, 1024) -//! ); +//! seccomp_filter.push(&mut BpfRule::new(libc::SYS_read).add_constraint( +//! SeccompCmpOpt::Ne, +//! 2, +//! 1024, +//! )); //! seccomp_filter.push(&mut BpfRule::new(libc::SYS_write)); //! seccomp_filter.push(&mut BpfRule::new(libc::SYS_close)); //! seccomp_filter.push(&mut BpfRule::new(libc::SYS_sigaltstack)); @@ -318,8 +319,8 @@ impl BpfRule { /// # Arguments /// * `cmp` - Compare operator for given args_value and the raw args_value. /// * `args_idx` - The index number of system call's arguments. - /// * `args_value` - The value of args_num you want to limit. This value - /// used with `cmp` together. + /// * `args_value` - The value of args_num you want to limit. This value used with `cmp` + /// together. pub fn add_constraint(mut self, cmp: SeccompCmpOpt, args_idx: u32, args_value: u32) -> BpfRule { if self.inner_rules.is_empty() { self.tail_rule = bpf_stmt(BPF_LD + BPF_W + BPF_ABS, SeccompData::nr()); @@ -432,7 +433,7 @@ impl SyscallFilter { /// this structure dropped or not. You can only use this function once in /// a thread. Otherwise you will get an error. pub fn realize(mut self) -> Result<()> { - //Add opt as a bpf_filter to sock_filters + // Add opt as a bpf_filter to sock_filters. self.sock_filters.append(&mut handle_process(self.opt)); let sock_bpf_vec = self.sock_filters; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 903281b73..f723d414b 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -165,8 +165,9 @@ impl VfioPciDevice { return Ok(()); } - // Cache the pci config space to avoid overwriting the original config space. Because we will - // parse the chain of extended caps in cache config and insert them into original config space. + // Cache the pci config space to avoid overwriting the original config space. Because we + // will parse the chain of extended caps in cache config and insert them into original + // config space. let mut config = PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS); config.config = config_data; let mut next = PCI_CONFIG_SPACE_SIZE; diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index a9e265291..57c06d74e 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -28,7 +28,8 @@ pub type DirentPtr = *mut libc::dirent; /// /// # Arguments /// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the file. +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `flags` - The flags used to get the information of the file. pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { @@ -64,7 +65,8 @@ pub fn open(name: CString, flags: i32) -> (Option, i32) { /// /// # Arguments /// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the file. +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode used to open a file. pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option, i32) { @@ -100,7 +102,7 @@ pub fn fchmod(file: &File, mode: u32) -> i32 { /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode indicates the permissions of the file will be set. pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { @@ -117,7 +119,7 @@ pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `uid` - The user id will be set. /// * `gid` - The group id will be set. @@ -175,12 +177,13 @@ pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) - FUSE_OK } -/// Update the timestamps with nanosecond precision by path name that is relative to the starting directory. +/// Update the timestamps with nanosecond precision by path name that is relative to the starting +/// directory. /// /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `a_sec` - The second of last access time. /// * `a_nsec` - The nanosecond of last access time. @@ -219,7 +222,7 @@ pub fn utimensat( /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { let mut buf = vec![0; MAX_PATH_LEN + 1]; @@ -248,7 +251,7 @@ pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `link_name` - The link name is new path name for the target path name. pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { @@ -319,7 +322,7 @@ pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode indicates both the file mode to use and the type of node to be created. /// * `rdev` - The rdev indicates the major and minor numbers of the special file. @@ -340,7 +343,7 @@ pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `mode` - The mode indicates the permissions of the new directory. pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { @@ -357,7 +360,7 @@ pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { /// # Arguments /// /// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. +/// file. /// * `name` - The name indicates the file path is relative to the starting directory. /// * `flags` - The flags indicates the operation of deleting a name. pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { @@ -376,11 +379,11 @@ pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { /// /// # Arguments /// -/// * `olddir` - The directory file handler saves the file descriptor of starting directory to look up for the -/// old file. +/// * `olddir` - The directory file handler saves the file descriptor of starting directory to look +/// up for the old file. /// * `name` - The name indicates the file path is relative to the starting of old directory. -/// * `newdir` - The directory file handler saves the file descriptor of starting directory to look up for the -/// new file. +/// * `newdir` - The directory file handler saves the file descriptor of starting directory to look +/// up for the new file. /// * `newname` - The name indicates the file path is relative to the starting of new directory. pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> i32 { nix::errno::Errno::clear(); @@ -401,15 +404,16 @@ pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> FUSE_OK } -/// Change a name for file in host filesystem by path name that is relative to the starting directory. +/// Change a name for file in host filesystem by path name that is relative to the starting +/// directory. /// /// # Arguments /// -/// * `old_file` - The file handler saves the file descriptor of starting old directory to look up for the -/// file. +/// * `old_file` - The file handler saves the file descriptor of starting old directory to look up +/// for the file. /// * `old_name` - The name indicates the file path is relative to the starting of old directory. -/// * `new_file` - The file handler saves the file descriptor of starting new directory to look up for the -/// file. +/// * `new_file` - The file handler saves the file descriptor of starting new directory to look up +/// for the file. /// * `new_name` - The name indicates the file path is relative to the starting of new directory. /// * `flags` - The flags indicates the operation of change a name. pub fn linkat( @@ -459,8 +463,7 @@ pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { /// # Arguments /// /// * `file` - The file handler saves the open file descriptor. -/// * `datasync` - The datasync indicates whether to use the fdatasync -/// or fsync interface. +/// * `datasync` - The datasync indicates whether to use the fdatasync or fsync interface. pub fn fsync(file: &File, datasync: bool) -> i32 { nix::errno::Errno::clear(); diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 12fc122d9..02c8b8357 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -378,7 +378,8 @@ impl FuseBuffer { /// * `fd` - The file descriptor in host. /// * `offset` - The offset which needs to be read and written in host file. /// * `size` - The size which needs to be read and written in host file. - /// * `is_read` - If it is true, writing the data which is read from host file to the fuse buffers. + /// * `is_read` - If it is true, writing the data which is read from host file to the fuse + /// buffers. /// If it is false, writing the data which is read from the fuse buffers to host file. pub fn access_file( &mut self, @@ -494,12 +495,11 @@ impl<'a> FuseIovec<'a> { /// * `writer` - The writable fuse buffers. /// * `sys_mem` - Address space mapped with StratoVirt. /// * `in_header` - The in_header reading from the read-only fuse buffers. -/// * `err` - The error number for processing the fuse message. If it is ok, set -/// error number to 0. If it is false, set error number from linux. -/// * `body_opt` - The body for replying the fuse message needs to be written -/// to fuse buffers. -/// * `body_len` - The length of body for replying the fuse message. if the body -/// is none, set the length to 0. +/// * `err` - The error number for processing the fuse message. If it is ok, set error number to 0. +/// If it is false, set error number from linux. +/// * `body_opt` - The body for replying the fuse message needs to be written to fuse buffers. +/// * `body_len` - The length of body for replying the fuse message. if the body is none, set the +/// length to 0. pub fn reply_fuse_msg( writer: &mut FuseBuffer, sys_mem: &Arc, @@ -525,7 +525,7 @@ pub fn reply_fuse_msg( written_len = 0_u32; }; - //write the body of fuse message in address space + // write the body of fuse message in address space. if let Some(body) = body_opt { for fuse_iov in body.iter() { if let Err(e) = writer.write_slice(sys_mem, fuse_iov.body, fuse_iov.len) { diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index a389129b7..802fc133a 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -1160,7 +1160,7 @@ pub fn do_fuse_init( max_background: 0, congestion_threshold: 0, max_write: MAX_WRITE_SIZE, - /* Granularity of c/m/atime in ns (cannot be worse than a second), default 1 */ + // Granularity of c/m/atime in ns (cannot be worse than a second), default 1. time_gran: 1, max_pages: ((MAX_WRITE_SIZE + pagesize - 1) / pagesize) as u16, map_alignment: 0, diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs index 3de2d6125..115057658 100644 --- a/vhost_user_fs/src/sandbox.rs +++ b/vhost_user_fs/src/sandbox.rs @@ -62,7 +62,8 @@ impl Sandbox { } /// In "namespace" sandbox mode. - /// The program switches into a new file system namespace and invokes pivot_root(2) to make the shared directory tree its root. + /// The program switches into a new file system namespace and invokes pivot_root(2) to make the + /// shared directory tree its root. pub fn enable_namespace(&mut self) -> Result<()> { let mut flags = libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::CLONE_NEWNET; let euid = unsafe { libc::geteuid() }; @@ -335,8 +336,9 @@ pub fn drop_groups() -> Result<()> { if group_num == -1 { bail!("getgroups fail"); } else if group_num > 0 { - // Sets the supplementary group IDs for the calling process. Appropriate privileges are required. - // A process can drop all of its supplementary groups with the call:setgroups(0, NULL). + // Sets the supplementary group IDs for the calling process. Appropriate privileges are + // required. A process can drop all of its supplementary groups with the + // call:setgroups(0, NULL). if unsafe { libc::setgroups(0, std::ptr::null()) } == -1 { bail!("setgroups fail"); } diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 31df8fd81..7e5e0ae63 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -298,9 +298,9 @@ impl Request { let host_page_size = host_page_size(); let mut advice = 0; // If host_page_size equals BALLOON_PAGE_SIZE and have the same share properties, - // we can directly call the madvise function without any problem. And if the advice is MADV_WILLNEED, - // we just hint the whole host page it lives on, since we can't do anything - // smaller. + // we can directly call the madvise function without any problem. And if the advice is + // MADV_WILLNEED, we just hint the whole host page it lives on, since we can't do + // anything smaller. if host_page_size == BALLOON_PAGE_SIZE { while let Some((hva, share)) = hvaset.pop() { if last_addr == 0 { @@ -604,7 +604,8 @@ impl BalloonIoHandler { /// /// * `req_type` - Type of request. /// - /// if `req_type` is `BALLOON_INFLATE_EVENT`, then inflate the balloon, otherwise, deflate the balloon. + /// if `req_type` is `BALLOON_INFLATE_EVENT`, then inflate the balloon, otherwise, deflate the + /// balloon. fn process_balloon_queue(&mut self, req_type: bool) -> Result<()> { let queue = if req_type { self.trace_request("Balloon".to_string(), "to inflate".to_string()); @@ -922,8 +923,8 @@ impl Balloon { /// Init balloon object for global use. pub fn object_init(dev: Arc>) { - // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other words, - // this function will not be called simultaneously. + // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other + // words, this function will not be called simultaneously. unsafe { if BALLOON_DEV.is_none() { BALLOON_DEV = Some(dev) @@ -1139,8 +1140,8 @@ impl VirtioDevice for Balloon { } pub fn qmp_balloon(target: u64) -> bool { - // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other words, - // this function will not be called simultaneously. + // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other + // words, this function will not be called simultaneously. if let Some(dev) = unsafe { &BALLOON_DEV } { match dev.lock().unwrap().set_guest_memory_size(target) { Ok(()) => { @@ -1157,8 +1158,8 @@ pub fn qmp_balloon(target: u64) -> bool { } pub fn qmp_query_balloon() -> Option { - // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other words, - // this function will not be called simultaneously. + // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other + // words, this function will not be called simultaneously. if let Some(dev) = unsafe { &BALLOON_DEV } { let unlocked_dev = dev.lock().unwrap(); return Some(unlocked_dev.get_guest_memory_size()); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index d4130a12e..428215da1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1233,7 +1233,8 @@ impl VirtioDevice for Block { } if !is_plug { - // If it is an unplug operation, the block backend is set to none. Unregister aio before it. + // If it is an unplug operation, the block backend is set to none. Unregister aio before + // it. if let Some(block_backend) = self.block_backend.as_ref() { block_backend.lock().unwrap().unregister_io_event()?; } else { diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 486a7f57f..dcc0c61fa 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -78,13 +78,14 @@ pub const VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: u32 = 7; /// Command-specific response values. /// The request was completed and the status byte if filled with a SCSI status code. const VIRTIO_SCSI_S_OK: u8 = 0; -/// If the content of the CDB(such as the allocation length, parameter length or transfer size) requires -/// more data than is available in the datain and dataout buffers. +/// If the content of the CDB(such as the allocation length, parameter length or transfer size) +/// requires more data than is available in the datain and dataout buffers. const VIRTIO_SCSI_S_OVERRUN: u8 = 1; /// The request was never processed because the target indicated by lun does not exist. const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; -/// Other host or driver error. In particular, if neither dataout nor datain is empty, and the VIRTIO_SCSI_F_INOUT -/// feature has not been negotiated, the request will be immediately returned with a response equal to VIRTIO_SCSI_S_FAILURE. +/// Other host or driver error. In particular, if neither dataout nor datain is empty, and the +/// VIRTIO_SCSI_F_INOUT feature has not been negotiated, the request will be immediately returned +/// with a response equal to VIRTIO_SCSI_S_FAILURE. const VIRTIO_SCSI_S_FAILURE: u8 = 9; #[repr(C, packed)] @@ -532,9 +533,9 @@ impl VirtioScsiReq .with_context(|| "Failed to write the scsi response")?; let mut queue_lock = self.queue.lock().unwrap(); - // Note: U(response) is the header part of in_iov and self.data_len is the rest part of the in_iov or - // the out_iov. in_iov and out_iov total len is no more than DESC_CHAIN_MAX_TOTAL_LEN(1 << 32). So, - // it will not overflow here. + // Note: U(response) is the header part of in_iov and self.data_len is the rest part of the + // in_iov or the out_iov. in_iov and out_iov total len is no more than + // DESC_CHAIN_MAX_TOTAL_LEN(1 << 32). So, it will not overflow here. queue_lock .vring .add_used( @@ -626,7 +627,8 @@ impl ScsiCtrlQueueHandler { )?; info!("incomplete tmf req, subtype {}!", tmf.req.subtype); // Scsi Task Management Function is not supported. - // So, do nothing when stratovirt receives TMF request except responding guest scsi drivers. + // So, do nothing when stratovirt receives TMF request except responding guest + // scsi drivers. tmf.resp.response = VIRTIO_SCSI_S_OK; tmf.complete()?; } @@ -887,7 +889,8 @@ impl ScsiCmdQueueHandler { Box::new(qrequest.clone()), ); if scsi_req.is_err() { - // Wrong scsi cdb. Response CHECK_CONDITION / SCSI_SENSE_INVALID_OPCODE to guest scsi drivers. + // Wrong scsi cdb. Response CHECK_CONDITION / SCSI_SENSE_INVALID_OPCODE to guest scsi + // drivers. qrequest.resp.set_scsi_sense(SCSI_SENSE_INVALID_OPCODE); qrequest.resp.status = CHECK_CONDITION; qrequest.complete()?; diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 37f4a4245..28c6ed6d8 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -352,7 +352,8 @@ pub struct SerialPort { impl SerialPort { pub fn new(port_cfg: VirtioSerialPort) -> Self { - // Console is default host connected. And pty chardev has opened by default in realize() function. + // Console is default host connected. And pty chardev has opened by default in realize() + // function. let host_connected = port_cfg.is_console || port_cfg.chardev.backend == ChardevType::Pty; SerialPort { @@ -443,8 +444,8 @@ impl SerialPortHandler { } debug!("elem desc_unm: {}", elem.desc_num); - // Discard requests when there is no port using this queue or this port's socket is not connected. - // Popping elements without processing means discarding the request. + // Discard requests when there is no port using this queue or this port's socket is not + // connected. Popping elements without processing means discarding the request. if self.port.is_some() && self.port.as_ref().unwrap().lock().unwrap().host_connected { let mut iovec = elem.out_iovec; let mut iovec_size = Element::iovec_size(&iovec); @@ -497,7 +498,8 @@ impl SerialPortHandler { let mut locked_output = output.lock().unwrap(); // To do: // If the buffer is not fully written to chardev, the incomplete part will be discarded. - // This may occur when chardev is abnormal. Consider optimizing this logic in the future. + // This may occur when chardev is abnormal. Consider optimizing this logic in the + // future. if let Err(e) = locked_output.write_all(&buffer[..write_len]) { error!("Failed to write msg to chardev: {:?}", e); } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 70e47b95c..2d0db560c 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -215,13 +215,14 @@ pub const VIRTIO_BLK_F_MQ: u32 = 12; /// A single request can include both device-readable and device-writable data buffers. pub const VIRTIO_SCSI_F_INOUT: u32 = 0; -/// The host SHOULD enable reporting of hot-plug and hot-unplug events for LUNs and targets on the SCSI bus. -/// The guest SHOULD handle hot-plug and hot-unplug events. +/// The host SHOULD enable reporting of hot-plug and hot-unplug events for LUNs and targets on the +/// SCSI bus. The guest SHOULD handle hot-plug and hot-unplug events. pub const VIRTIO_SCSI_F_HOTPLUG: u32 = 1; /// The host will report changes to LUN parameters via a VIRTIO_SCSI_T_PARAM_CHANGE event. /// The guest SHOULD handle them. pub const VIRTIO_SCSI_F_CHANGE: u32 = 2; -/// The extended fields for T10 protection information (DIF/DIX) are included in the SCSI request header. +/// The extended fields for T10 protection information (DIF/DIX) are included in the SCSI request +/// header. pub const VIRTIO_SCSI_F_T10_PI: u32 = 3; /// The IO type of virtio block, refer to Virtio Spec. diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index f166c84c8..100c4d4de 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -97,7 +97,6 @@ impl QueueConfig { /// # Arguments /// /// * `max_size` - The maximum size of the virtqueue. - /// pub fn new(max_size: u16) -> Self { let addr_cache = VirtioAddrCache::default(); QueueConfig { @@ -1232,7 +1231,8 @@ mod tests { let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), true); - // it is invalid when the address of descriptor table is overlapped to the address of avail ring + // it is invalid when the address of descriptor table is overlapped to the address of avail + // ring. queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN - 1); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); @@ -1592,8 +1592,8 @@ mod tests { assert!(false); } - // error comes when the length of indirect descriptor is more than the length of descriptor chain - // set the information of index 0 for descriptor + // error comes when the length of indirect descriptor is more than the length of descriptor + // chain set the information of index 0 for descriptor. vring .set_desc( &sys_space, @@ -1953,30 +1953,34 @@ mod tests { assert!(vring.set_avail_ring_flags(&sys_space, 0).is_ok()); assert_eq!(vring.should_notify(&sys_space, features), true); - // it's false when the feature of event idx is closed and the feature of no interrupt for the avail ring is open + // it's false when the feature of event idx is closed and the feature of no interrupt for + // the avail ring is open let features = 0 as u64; assert!(vring .set_avail_ring_flags(&sys_space, VRING_AVAIL_F_NO_INTERRUPT) .is_ok()); assert_eq!(vring.should_notify(&sys_space, features), false); - // it's true when the feature of event idx is open and (new - event_idx - Wrapping(1) < new -old) + // it's true when the feature of event idx is open and + // (new - event_idx - Wrapping(1) < new -old) let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; - vring.last_signal_used = Wrapping(5); //old - assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); //new - assert!(vring.set_used_event_idx(&sys_space, 6).is_ok()); //event_idx + vring.last_signal_used = Wrapping(5); // old + assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new + assert!(vring.set_used_event_idx(&sys_space, 6).is_ok()); // event_idx assert_eq!(vring.should_notify(&sys_space, features), true); - // it's false when the feature of event idx is open and (new - event_idx - Wrapping(1) > new -old) - vring.last_signal_used = Wrapping(5); //old - assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); //new - assert!(vring.set_used_event_idx(&sys_space, 1).is_ok()); //event_idx + // it's false when the feature of event idx is open and + // (new - event_idx - Wrapping(1) > new - old) + vring.last_signal_used = Wrapping(5); // old + assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new + assert!(vring.set_used_event_idx(&sys_space, 1).is_ok()); // event_idx assert_eq!(vring.should_notify(&sys_space, features), false); - // it's false when the feature of event idx is open and (new - event_idx - Wrapping(1) = new -old) - vring.last_signal_used = Wrapping(5); //old - assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); //new - assert!(vring.set_used_event_idx(&sys_space, 4).is_ok()); //event_idx + // it's false when the feature of event idx is open and + // (new - event_idx - Wrapping(1) = new -old) + vring.last_signal_used = Wrapping(5); // old + assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new + assert!(vring.set_used_event_idx(&sys_space, 4).is_ok()); // event_idx assert_eq!(vring.should_notify(&sys_space, features), false); } } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 997ed0aa8..e0a6fe0a3 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -923,7 +923,8 @@ mod tests { assert_eq!(virtio_device.lock().unwrap().hfeatures_sel(), 2); // write the device features - // false when the device status is CONFIG_STATUS_FEATURES_OK or CONFIG_STATUS_FAILED isn't CONFIG_STATUS_DRIVER + // false when the device status is CONFIG_STATUS_FEATURES_OK or CONFIG_STATUS_FAILED isn't + // CONFIG_STATUS_DRIVER virtio_device .lock() .unwrap() diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 85d79c134..29803bf76 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -116,7 +116,7 @@ pub enum VhostUserHdrFlag { ReservedBits = !0xf, } -///the struct for the header of vhost user message. +/// the struct for the header of vhost user message. #[repr(C)] pub struct VhostUserMsgHdr { /// The request id for vhost-user message @@ -269,7 +269,7 @@ impl VhostUserVringState { } } -///The configuration for the address of virtual ring. +/// The configuration for the address of virtual ring. #[repr(C)] pub struct VhostUserVringAddr { /// Index for virtual ring. -- Gitee From ada0c2c90195febfa0fd99883423a24a1bba18b3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 14 Aug 2023 18:28:45 +0800 Subject: [PATCH 1303/2187] vhost_user_fs: enable vhost_user_fs in microvm According to virtio 1.2 spec: Virtio-mmio device add five registers for shared memory: SHMSel(0x0ac): Shared memory id. SHMLenLow(0x0b0) SHMLenHigh(0x0b4): Shared memory region 64 bit long length. SHMBaseLow(0x0b8) SHMBaseHigh(0x0bc): Shared memory region 64 bit long physical address. Guest kernel will check shared memory region in virtiofs module. As SHMSel is not supported now, kernel will check if region length is -1. If that's the case, the shared memory region does not exist and guest kernel will not proceed further. So, return u32::MAX when guest reads SHMLenLow and SHMLenHigh register. Fix: 4f86d81675e82293bae964f96479924ff68e049c(virtiofs: complete main.rs) Signed-off-by: liuxiangdong --- virtio/src/transport/virtio_mmio.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index e0a6fe0a3..167c04136 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -77,6 +77,17 @@ const QUEUE_AVAIL_HIGH_REG: u64 = 0x94; const QUEUE_USED_LOW_REG: u64 = 0xa0; /// The high 32bit of queue's Used Ring address. const QUEUE_USED_HIGH_REG: u64 = 0xa4; +/// Shared memory region id. +#[allow(unused)] +const SHM_SEL: u64 = 0xac; +/// Shared memory region 64 bit long length. 64 bits in two halves. +const SHM_LEN_LOW: u64 = 0xb0; +const SHM_LEN_HIGH: u64 = 0xb4; +/// Shared memory region 64 bit long physical address. 64 bits in two halves. +#[allow(unused)] +const SHM_BASE_LOW: u64 = 0xb8; +#[allow(unused)] +const SHM_BASE_HIGH: u64 = 0xbc; /// Configuration atomicity value. const CONFIG_GENERATION_REG: u64 = 0xfc; @@ -290,6 +301,9 @@ impl VirtioMmioDevice { INTERRUPT_STATUS_REG => locked_device.interrupt_status(), STATUS_REG => locked_device.device_status(), CONFIG_GENERATION_REG => locked_device.config_generation() as u32, + // SHM_SEL is unimplemented. According to the Virtio v1.2 spec: Reading from a non-existent + // region(i.e. where the ID written to SHMSel is unused) results in a length of -1. + SHM_LEN_LOW | SHM_LEN_HIGH => u32::MAX, _ => { return Err(anyhow!(VirtioError::MmioRegErr(offset))); } -- Gitee From 0072ddefc2fa7e787a0b907ace2f65e1e2230249 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 15 Aug 2023 14:12:30 +0800 Subject: [PATCH 1304/2187] usb: remove target macros in check_device_id_existed Fix 69a5bb82. Signed-off-by: zhouli57 --- machine/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7b23073e4..982918eb8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -670,7 +670,6 @@ pub trait MachineOps { if PciBus::find_attached_bus(&pci_host.lock().unwrap().root_bus, name).is_some() { bail!("Device id {} existed", name); } - #[cfg(not(target_env = "musl"))] if self.check_id_existed_in_xhci(name).unwrap_or_default() { bail!("Device id {} existed in xhci", name); } -- Gitee From 06ec9b404229dfd1f0a488b3f821e063cb5c1a0f Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Wed, 16 Aug 2023 10:23:55 +0800 Subject: [PATCH 1305/2187] virtiogpu: Fix cursor disappear When click the shared desktop button of DingTalk app, this action trggers the drawing of transparent mouse.In order to prevent the icon from returning to its normal state, an update is forcibly executed Signed-off-by:wangmeiling --- virtio/src/device/gpu.rs | 42 +++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index b221ad417..ffad02f6a 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -725,6 +725,29 @@ impl GpuIoHandler { scanout.cursor_visible = true; } + fn update_cursor(&mut self, info_cursor: &VirtioGpuUpdateCursor, hdr_type: u32) -> Result<()> { + let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; + match &mut scanout.mouse { + None => { + let mouse = DisplayMouse::new(64, 64, info_cursor.hot_x, info_cursor.hot_y); + scanout.mouse = Some(mouse); + } + Some(mouse) => { + if hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { + mouse.hot_x = info_cursor.hot_x; + mouse.hot_y = info_cursor.hot_y; + } + } + } + + if info_cursor.resource_id > 0 { + self.update_cursor_image(info_cursor); + } + let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; + display_cursor_define(&scanout.con, scanout.mouse.as_ref().unwrap())?; + Ok(()) + } + fn cmd_update_cursor(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_cursor = VirtioGpuUpdateCursor::default(); self.get_request(req, &mut info_cursor)?; @@ -758,24 +781,11 @@ impl GpuIoHandler { } display_cursor_define(&scanout.con, scanout.mouse.as_ref().unwrap())?; scanout.cursor_visible = false; + } else if info_cursor.resource_id > 0 && !scanout.cursor_visible { + self.update_cursor(&info_cursor, VIRTIO_GPU_CMD_MOVE_CURSOR)?; } } else if req.header.hdr_type == VIRTIO_GPU_CMD_UPDATE_CURSOR { - match &mut scanout.mouse { - None => { - let mouse = DisplayMouse::new(64, 64, info_cursor.hot_x, info_cursor.hot_y); - scanout.mouse = Some(mouse); - } - Some(mouse) => { - mouse.hot_x = info_cursor.hot_x; - mouse.hot_y = info_cursor.hot_y; - } - } - - if info_cursor.resource_id > 0 { - self.update_cursor_image(&info_cursor); - } - let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; - display_cursor_define(&scanout.con, scanout.mouse.as_ref().unwrap())?; + self.update_cursor(&info_cursor, VIRTIO_GPU_CMD_UPDATE_CURSOR)?; } else { bail!("Wrong header type for cursor queue"); } -- Gitee From f99fbaba1c071400b019bc7dabb97b7dd2930474 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 17 Aug 2023 20:37:14 +0800 Subject: [PATCH 1306/2187] chardev: Set pty master to non-blocking mode We should not block vcpu when serial device writes buffer to pty. Signed-off-by: Keqian Zhu --- devices/src/legacy/chardev.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/devices/src/legacy/chardev.rs b/devices/src/legacy/chardev.rs index ce4c1d1c6..b7a728078 100644 --- a/devices/src/legacy/chardev.rs +++ b/devices/src/legacy/chardev.rs @@ -215,6 +215,16 @@ fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { std::io::Error::last_os_error() ); } + + // SAFETY: master is got from openpty. + let ret = unsafe { libc::fcntl(master, libc::F_SETFL, libc::O_NONBLOCK) }; + if ret < 0 { + bail!( + "Failed to set pty master to nonblocking mode, error is {}", + std::io::Error::last_os_error() + ); + } + Ok((master, path)) } -- Gitee From 5d829c955d37c179e398f3b4c70663f87e357db5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 18 Aug 2023 17:52:56 +0800 Subject: [PATCH 1307/2187] serial: Properly handle write error of serial 1. print error log for serial. 2. change log to debug for pl011. Signed-off-by: Keqian Zhu --- devices/src/legacy/pl011.rs | 4 ++-- devices/src/legacy/serial.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 049025b95..16b853e8f 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -339,10 +339,10 @@ impl SysBusDevOps for PL011 { if let Some(output) = &mut self.chardev.lock().unwrap().output { let mut locked_output = output.lock().unwrap(); if let Err(e) = locked_output.write_all(&[ch]) { - error!("Failed to write to pl011 output fd, error is {:?}", e); + debug!("Failed to write to pl011 output fd, error is {:?}", e); } if let Err(e) = locked_output.flush() { - error!("Failed to flush pl011, error is {:?}", e); + debug!("Failed to flush pl011, error is {:?}", e); } } else { debug!("Failed to get output fd"); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index f2b1221ad..82c1369f7 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -14,7 +14,7 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use log::error; +use log::{debug, error}; use vmm_sys_util::eventfd::EventFd; use super::chardev::{Chardev, InputReceiver}; @@ -389,7 +389,12 @@ impl SysBusDevOps for Serial { } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { - self.write_internal(offset, data[0]).is_ok() + if let Err(e) = self.write_internal(offset, data[0]) { + debug!("Failed to write serial device {}: {:?}", self.name(), e); + false + } else { + true + } } fn set_irq(&mut self, _sysbus: &mut SysBus) -> Result { -- Gitee From 4c668f69a99014aaa234b4b6ea4a8fd9487ab10f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 18 Aug 2023 17:18:26 +0800 Subject: [PATCH 1308/2187] seccomp: Fix configuration of SYS_fcntl Add SETLK of SYS_fcntl for all types of machine. Remove dumplicate configuration of SYS_Fcntl. Signed-off-by: Keqian Zhu --- machine/src/micro_vm/syscall.rs | 13 ++++--------- machine/src/standard_vm/aarch64/syscall.rs | 19 ++++++------------- machine/src/standard_vm/x86_64/syscall.rs | 19 ++++++------------- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index b5d732b7a..c37d3f4ea 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -30,12 +30,6 @@ const FUTEX_CMP_REQUEUE_PRIVATE: u32 = FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG; const FUTEX_WAKE_OP_PRIVATE: u32 = FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG; const FUTEX_WAIT_BITSET_PRIVATE: u32 = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG; -/// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/fcntl.h -const F_GETFD: u32 = 1; -const F_SETFD: u32 = 2; -const F_LINUX_SPECIFIC_BASE: u32 = 1024; -const F_DUPFD_CLOEXEC: u32 = F_LINUX_SPECIFIC_BASE + 6; - // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/ioctls.h const TCGETS: u32 = 0x5401; const TCSETS: u32 = 0x5402; @@ -82,9 +76,10 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_io_setup), BpfRule::new(libc::SYS_brk), BpfRule::new(libc::SYS_fcntl) - .add_constraint(SeccompCmpOpt::Eq, 1, F_DUPFD_CLOEXEC) - .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFD) - .add_constraint(SeccompCmpOpt::Eq, 1, F_GETFD), + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_DUPFD_CLOEXEC as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFD as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFD as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETLK as u32), BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), #[cfg(target_arch = "x86_64")] diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 842413bcb..6cdb20dfb 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -41,13 +41,6 @@ const FUTEX_CMP_REQUEUE_PRIVATE: u32 = FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG; const FUTEX_WAKE_OP_PRIVATE: u32 = FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG; const FUTEX_WAIT_BITSET_PRIVATE: u32 = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG; -/// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/fcntl.h -const F_GETFD: u32 = 1; -const F_SETFD: u32 = 2; -const F_SETFL: u32 = 4; -const F_LINUX_SPECIFIC_BASE: u32 = 1024; -const F_DUPFD_CLOEXEC: u32 = F_LINUX_SPECIFIC_BASE + 6; - // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/ioctls.h const TCGETS: u32 = 0x5401; const TCSETS: u32 = 0x5402; @@ -94,10 +87,12 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_io_setup), BpfRule::new(libc::SYS_brk), BpfRule::new(libc::SYS_fcntl) - .add_constraint(SeccompCmpOpt::Eq, 1, F_DUPFD_CLOEXEC) - .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFD) - .add_constraint(SeccompCmpOpt::Eq, 1, F_GETFD) - .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFL), + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_DUPFD_CLOEXEC as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFD as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFD as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETLK as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFL as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFL as u32), BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), BpfRule::new(libc::SYS_openat), @@ -182,8 +177,6 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_pipe2), #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_fcntl), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_memfd_create), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_ftruncate), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 488298aa2..256033486 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -41,13 +41,6 @@ const FUTEX_CMP_REQUEUE_PRIVATE: u32 = FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG; const FUTEX_WAKE_OP_PRIVATE: u32 = FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG; const FUTEX_WAIT_BITSET_PRIVATE: u32 = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG; -/// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/fcntl.h -const F_GETFD: u32 = 1; -const F_SETFD: u32 = 2; -const F_SETFL: u32 = 4; -const F_LINUX_SPECIFIC_BASE: u32 = 1024; -const F_DUPFD_CLOEXEC: u32 = F_LINUX_SPECIFIC_BASE + 6; - // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/ioctls.h const TCGETS: u32 = 0x5401; const TCSETS: u32 = 0x5402; @@ -96,10 +89,12 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_io_setup), BpfRule::new(libc::SYS_brk), BpfRule::new(libc::SYS_fcntl) - .add_constraint(SeccompCmpOpt::Eq, 1, F_DUPFD_CLOEXEC) - .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFD) - .add_constraint(SeccompCmpOpt::Eq, 1, F_GETFD) - .add_constraint(SeccompCmpOpt::Eq, 1, F_SETFL), + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_DUPFD_CLOEXEC as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFD as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFD as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETLK as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFL as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFL as u32), BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), BpfRule::new(libc::SYS_open), @@ -190,8 +185,6 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_pipe2), #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_fcntl), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_memfd_create), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_ftruncate), -- Gitee From 8928f5a64663ad2b3deecfdebaa8074babf08fbf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 17 Aug 2023 20:41:17 +0800 Subject: [PATCH 1309/2187] code_style: Fix clippy warning Signed-off-by: Keqian Zhu --- devices/src/legacy/fwcfg.rs | 2 +- devices/src/usb/xhci/xhci_controller.rs | 4 ++-- machine/src/micro_vm/mod.rs | 4 ++-- machine/src/standard_vm/mod.rs | 4 ++-- machine_manager/src/config/tls_creds.rs | 6 +----- util/src/device_tree.rs | 6 ++---- util/src/unix.rs | 6 +++--- virtio/src/vhost/user/sock.rs | 6 +++--- 8 files changed, 16 insertions(+), 22 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 2f097bd20..47cda8315 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1175,7 +1175,7 @@ impl SysBusDevOps for FwCfgIO { region_base: u64, region_size: u64, ) -> Result<()> { - let mut res = self.get_sys_resource().unwrap(); + let res = self.get_sys_resource().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 21ff229d0..d9719e820 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2135,7 +2135,7 @@ pub fn dma_read_u32( addr: GuestAddress, buf: &mut [u32], ) -> Result<()> { - let vec_len = size_of::() * buf.len(); + let vec_len = std::mem::size_of_val(buf); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); dma_read_bytes(addr_space, addr, tmp)?; @@ -2150,7 +2150,7 @@ pub fn dma_write_u32( addr: GuestAddress, buf: &[u32], ) -> Result<()> { - let vec_len = size_of::() * buf.len(); + let vec_len = std::mem::size_of_val(buf); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); for i in 0..buf.len() { diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 8098b21b9..191a47bf4 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -1038,7 +1038,7 @@ impl DeviceInterface for LightMachine { _ => Default::default(), }; - Response::create_response(serde_json::to_value(&qmp_state).unwrap(), None) + Response::create_response(serde_json::to_value(qmp_state).unwrap(), None) } fn query_cpus(&self) -> Response { @@ -1126,7 +1126,7 @@ impl DeviceInterface for LightMachine { fn query_balloon(&self) -> Response { if let Some(actual) = qmp_query_balloon() { let ret = qmp_schema::BalloonInfo { actual }; - return Response::create_response(serde_json::to_value(&ret).unwrap(), None); + return Response::create_response(serde_json::to_value(ret).unwrap(), None); } Response::create_error_response( qmp_schema::QmpErrorClass::DeviceNotActive( diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 9c1573824..9076da21f 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1134,7 +1134,7 @@ impl DeviceInterface for StdMachine { _ => Default::default(), }; - Response::create_response(serde_json::to_value(&qmp_state).unwrap(), None) + Response::create_response(serde_json::to_value(qmp_state).unwrap(), None) } fn query_cpus(&self) -> Response { @@ -1195,7 +1195,7 @@ impl DeviceInterface for StdMachine { fn query_balloon(&self) -> Response { if let Some(actual) = qmp_query_balloon() { let ret = qmp_schema::BalloonInfo { actual }; - return Response::create_response(serde_json::to_value(&ret).unwrap(), None); + return Response::create_response(serde_json::to_value(ret).unwrap(), None); } Response::create_error_response( qmp_schema::QmpErrorClass::DeviceNotActive( diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index 7dd0eaade..8803ea428 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -57,11 +57,7 @@ impl VmConfig { tlscred.endpoint = Some(endpoint); } if let Some(verifypeer) = cmd_parser.get_value::("verify-peer")? { - if verifypeer == *"true" { - tlscred.verifypeer = true; - } else { - tlscred.verifypeer = false; - } + tlscred.verifypeer = verifypeer == *"true"; } tlscred.cred_type = "x509".to_string(); diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index 12bc60182..ebb50d0e2 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::mem::size_of; - use anyhow::{anyhow, Context, Result}; use byteorder::{BigEndian, ByteOrder}; @@ -240,7 +238,7 @@ impl FdtBuilder { } pub fn set_property_array_u32(&mut self, prop: &str, array: &[u32]) -> Result<()> { - let mut prop_array = Vec::with_capacity(array.len() * size_of::()); + let mut prop_array = Vec::with_capacity(std::mem::size_of_val(array)); for element in array { prop_array.extend_from_slice(&element.to_be_bytes()[..]); } @@ -249,7 +247,7 @@ impl FdtBuilder { } pub fn set_property_array_u64(&mut self, prop: &str, array: &[u64]) -> Result<()> { - let mut prop_array = Vec::with_capacity(array.len() * size_of::()); + let mut prop_array = Vec::with_capacity(std::mem::size_of_val(array)); for element in array { prop_array.extend_from_slice(&element.to_be_bytes()[..]); } diff --git a/util/src/unix.rs b/util/src/unix.rs index e0e3aaf50..49bf9a71c 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -267,9 +267,9 @@ impl UnixSock { // SAFETY: we checked the iovecs lens before. let iovecs_len = iovecs.len(); // SATETY: we checked the out_fds lens before. - let cmsg_len = unsafe { CMSG_LEN((size_of::() * out_fds.len()) as u32) }; + let cmsg_len = unsafe { CMSG_LEN((std::mem::size_of_val(out_fds)) as u32) }; // SAFETY: we checked the out_fds lens before. - let cmsg_capacity = unsafe { CMSG_SPACE((size_of::() * out_fds.len()) as u32) }; + let cmsg_capacity = unsafe { CMSG_SPACE((std::mem::size_of_val(out_fds)) as u32) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be @@ -340,7 +340,7 @@ impl UnixSock { // SAFETY: we check the iovecs lens before. let iovecs_len = iovecs.len(); // SAFETY: we check the in_fds lens before. - let cmsg_capacity = unsafe { CMSG_SPACE((size_of::() * in_fds.len()) as u32) }; + let cmsg_capacity = unsafe { CMSG_SPACE((std::mem::size_of_val(in_fds)) as u32) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be diff --git a/virtio/src/vhost/user/sock.rs b/virtio/src/vhost/user/sock.rs index f4bcd27b2..a944ebdc2 100644 --- a/virtio/src/vhost/user/sock.rs +++ b/virtio/src/vhost/user/sock.rs @@ -75,9 +75,9 @@ impl VhostUserSock { if let Some(payload) = payload_opt { iovs.push(iovec { iov_base: payload.as_ptr() as *const u8 as *mut c_void, - iov_len: payload.len() * size_of::

(), + iov_len: std::mem::size_of_val(payload), }); - total_len += payload.len() * size_of::

(); + total_len += std::mem::size_of_val(payload); } if (total_len - size_of::()) > VHOST_USER_MSG_MAX_SIZE { @@ -140,7 +140,7 @@ impl VhostUserSock { if let Some(payload) = payload_opt { iovs.push(iovec { iov_base: payload.as_ptr() as *const u8 as *mut c_void, - iov_len: payload.len() * size_of::

(), + iov_len: std::mem::size_of_val(payload), }); } -- Gitee From 191214a8a44e9a41d876608625161a7dae38b4bc Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 19:04:44 +0800 Subject: [PATCH 1310/2187] build: Add feature optional scream Add new "scream" build feature which enables scream by `--features scream_[interface]`. - scream_alsa: enable virtual sound card with `ALSA` interface - scream_pulseaudio: enable virtual sound card with `PulseAudio` interface Signed-off-by: yezengruan --- Cargo.toml | 2 ++ devices/Cargo.toml | 9 ++++-- devices/src/misc/mod.rs | 4 +-- devices/src/misc/scream/alsa.rs | 4 +-- devices/src/misc/scream/mod.rs | 35 ++++++++++++----------- devices/src/misc/scream/pulseaudio.rs | 3 +- docs/build_guide.ch.md | 13 +++++++++ docs/build_guide.md | 13 +++++++++ docs/config_guidebook.md | 5 ++-- machine/Cargo.toml | 3 ++ machine/src/lib.rs | 8 +++--- machine_manager/Cargo.toml | 4 ++- machine_manager/src/config/mod.rs | 1 + machine_manager/src/config/scream.rs | 41 ++++++++++++++++++++++----- tests/mod_test/Cargo.toml | 2 +- 15 files changed, 107 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bdb211dff..c52ce7ce0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ members = [ [features] default = [] boot_time = ["machine/boot_time"] +scream_alsa = ["machine/scream_alsa"] +scream_pulseaudio = ["machine/scream_pulseaudio"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 03c72cb07..b9f880a2d 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -30,14 +30,17 @@ util = { path = "../util" } acpi = { path = "../acpi" } block_backend = { path = "../block_backend"} ui = { path = "../ui" } +pulse = { version = "2.27", package = "libpulse-binding", optional = true } +psimple = { version = "2.27", package = "libpulse-simple-binding", optional = true } +alsa = { version = "0.7.0", optional = true } [target.'cfg(not(target_env = "musl"))'.dependencies] -pulse = { version = "2.27", package = "libpulse-binding" } -psimple = { version = "2.27", package = "libpulse-simple-binding" } rusb = "0.9" libusb1-sys = "0.6.4" cairo-rs = "0.17.10" -alsa = "0.7.0" [features] default = [] +scream = ["machine_manager/scream"] +scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] +scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] diff --git a/devices/src/misc/mod.rs b/devices/src/misc/mod.rs index 9a22cecdd..79e459724 100644 --- a/devices/src/misc/mod.rs +++ b/devices/src/misc/mod.rs @@ -10,8 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "scream")] pub mod scream; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "scream")] mod ivshmem; diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index 251878142..74c4a9add 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -24,8 +24,8 @@ use anyhow::Result; use log::{debug, error, warn}; use super::{ - pulseaudio::TARGET_LATENCY_MS, AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, - AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, + AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, AUDIO_SAMPLE_RATE_44KHZ, + AUDIO_SAMPLE_RATE_48KHZ, TARGET_LATENCY_MS, WINDOWS_SAMPLE_BASE_RATE, }; const MAX_CHANNELS: u8 = 8; diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index f63358eb3..4fc0a5258 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -10,8 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "scream_alsa")] mod alsa; mod audio_demo; +#[cfg(feature = "scream_pulseaudio")] mod pulseaudio; use std::{ @@ -27,18 +29,23 @@ use anyhow::{bail, Context, Result}; use core::time; use log::{error, warn}; -use self::{alsa::AlsaStreamData, audio_demo::AudioDemo}; +#[cfg(feature = "scream_alsa")] +use self::alsa::AlsaStreamData; +use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; -use machine_manager::config::scream::ScreamConfig; -use pulseaudio::{PulseStreamData, TARGET_LATENCY_MS}; +use machine_manager::config::scream::{ScreamConfig, ScreamInterface}; +#[cfg(feature = "scream_pulseaudio")] +use pulseaudio::PulseStreamData; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; pub const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; +pub const TARGET_LATENCY_MS: u32 = 50; + // A frame of back-end audio data is 50ms, and the next frame of audio data needs // to be trained in polling within 50ms. Theoretically, the shorter the polling time, // the better. However, if the value is too small, the overhead is high. So take a @@ -280,7 +287,7 @@ impl StreamData { pub struct Scream { hva: u64, size: u64, - interface: String, + interface: ScreamInterface, playback: String, record: String, } @@ -290,28 +297,24 @@ impl Scream { Self { hva: 0, size, - interface: dev_cfg.interface.clone(), + interface: dev_cfg.interface, playback: dev_cfg.playback.clone(), record: dev_cfg.record.clone(), } } + #[allow(unused_variables)] fn interface_init(&self, name: &str, dir: ScreamDirection) -> Arc> { - match self.interface.as_str() { - "ALSA" => Arc::new(Mutex::new(AlsaStreamData::init(name, dir))), - "PulseAudio" => Arc::new(Mutex::new(PulseStreamData::init(name, dir))), - "Demo" => Arc::new(Mutex::new(AudioDemo::init( + match self.interface { + #[cfg(feature = "scream_alsa")] + ScreamInterface::Alsa => Arc::new(Mutex::new(AlsaStreamData::init(name, dir))), + #[cfg(feature = "scream_pulseaudio")] + ScreamInterface::PulseAudio => Arc::new(Mutex::new(PulseStreamData::init(name, dir))), + ScreamInterface::Demo => Arc::new(Mutex::new(AudioDemo::init( dir, self.playback.clone(), self.record.clone(), ))), - _ => { - error!( - "Unsupported audio interface {}, falling back to ALSA", - self.interface - ); - Arc::new(Mutex::new(AlsaStreamData::init(name, dir))) - } } } diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 5aa88fbec..e35f6104f 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -25,9 +25,8 @@ use pulse::{ use super::{ AudioInterface, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, }; -use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData}; +use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData, TARGET_LATENCY_MS}; -pub const TARGET_LATENCY_MS: u32 = 50; const MAX_LATENCY_MS: u32 = 100; const STREAM_NAME: &str = "Audio"; diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index ac9d4668c..d43ecd3ca 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -44,3 +44,16 @@ $ cargo build --release --target ${arch}-unknown-linux-musl ``` 现在你可找到StratoVirt静态链接二进制的路径在 `target/${arch}-unknown-linux-musl/release/stratovirt`. + +## 4. 特性编译选项 + +对于不同的场景,StratoVirt提供基于cargo `feature`的特性条件编译选项。 + +可选特性清单如下: + +- scream_alsa:使能虚拟声卡,使用`ALSA`后端 +- scream_pulseaudio:使能虚拟声卡,使用`PulseAudio`后端 + +```shell +$ cargo build --release --features "scream_alsa" +``` diff --git a/docs/build_guide.md b/docs/build_guide.md index 3d7320e43..f7a96e4f0 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -45,3 +45,16 @@ $ cargo build --release --target ${arch}-unknown-linux-musl ``` Now you can find StratoVirt static binary file in `target/${arch}-unknown-linux-musl/release/stratovirt`. + +## 4. Build with features + +For different scenarios, StratoVirt provides feature conditional compilation options based on the cargo `feature`. + +List of optional features: + +- scream_alsa: enable virtual sound card with `ALSA` interface +- scream_pulseaudio: enable virtual sound card with `PulseAudio` interface + +```shell +$ cargo build --release --features "scream_alsa" +``` diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 7efd39381..690a4585f 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1065,7 +1065,6 @@ Nine properties are supported for ivshmem-scream device. * id: unique device id. * memdev: configuration of the back-end memory device used by the ivshmem. * interface: configuring audio playback and recording interfaces, currently can be set to `ALSA`, `PulseAudio` or `Demo`. -`ALSA` is used by default. * playback: Path for storing audio. When interface is set to Demo, playback is mandatory. * record: Path for obtaining audio. When interface is set to Demo, record is mandatory. * bus: bus number of the device. @@ -1076,10 +1075,12 @@ Nine properties are supported for ivshmem-scream device. Sample Configuration: ```shell --device ivshmem-scream,id=,memdev=[,interface=][,playback=][,record=],bus=pcie.0,addr=0x2.0x0 +-device ivshmem-scream,id=,memdev=,interface=[,playback=][,record=],bus=pcie.0,addr=0x2.0x0 -object memory-backend-ram,id=,share=on,size=2M ``` +Please see the [4. Build with features](docs/build_guide.md) if you want to enable scream. + ### 2.20 ramfb Ramfb is a simple display device. It is used in the Windows system on aarch64. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 01a06be5f..cfbbe78e9 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -34,3 +34,6 @@ ui = { path = "../ui" } default = ["qmp"] qmp = [] boot_time = ["cpu/boot_time"] +scream = ["devices/scream", "machine_manager/scream"] +scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] +scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7b23073e4..0125babc8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -47,7 +47,7 @@ use address_space::{ use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "scream")] use devices::misc::scream::Scream; use devices::pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; @@ -61,7 +61,7 @@ use devices::usb::{ use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "scream")] use machine_manager::config::scream::parse_scream; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, @@ -1216,7 +1216,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - scream configuration. - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "scream")] fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; @@ -1533,7 +1533,7 @@ pub trait MachineOps { "pcie-demo-dev" => { self.add_demo_dev(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "scream")] "ivshmem-scream" => { self.add_ivshmem_scream(vm_config, cfg_args)?; } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index de62997d1..fa4aeff39 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -24,4 +24,6 @@ util = { path = "../util" } [features] default = [] - +scream = [] +scream_alsa = ["scream"] +scream_pulseaudio = ["scream"] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index b6e9b0b62..8747f6054 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -32,6 +32,7 @@ mod pci; mod ramfb; mod rng; mod sasl_auth; +#[cfg(feature = "scream")] pub mod scream; mod scsi; mod smbios; diff --git a/machine_manager/src/config/scream.rs b/machine_manager/src/config/scream.rs index ea715c11b..d47059859 100644 --- a/machine_manager/src/config/scream.rs +++ b/machine_manager/src/config/scream.rs @@ -10,13 +10,40 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{Context, Result}; +use std::str::FromStr; + +use anyhow::{anyhow, Context, Result}; +use serde::{Deserialize, Serialize}; use super::{pci_args_check, CmdParser}; +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ScreamInterface { + #[cfg(feature = "scream_alsa")] + Alsa, + #[cfg(feature = "scream_pulseaudio")] + PulseAudio, + Demo, +} + +impl FromStr for ScreamInterface { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + #[cfg(feature = "scream_alsa")] + "ALSA" => Ok(ScreamInterface::Alsa), + #[cfg(feature = "scream_pulseaudio")] + "PulseAudio" => Ok(ScreamInterface::PulseAudio), + "Demo" => Ok(ScreamInterface::Demo), + _ => Err(anyhow!("Unknown scream interface")), + } + } +} + pub struct ScreamConfig { pub memdev: String, - pub interface: String, + pub interface: ScreamInterface, pub playback: String, pub record: String, } @@ -25,7 +52,7 @@ impl ScreamConfig { pub fn new() -> Self { Self { memdev: "".to_string(), - interface: "ALSA".to_string(), + interface: ScreamInterface::Demo, playback: "".to_string(), record: "".to_string(), } @@ -59,11 +86,11 @@ pub fn parse_scream(cfg_args: &str) -> Result { .get_value::("memdev")? .with_context(|| "No memdev configured for scream device")?; - if let Some(interface) = cmd_parser.get_value::("interface")? { - dev_cfg.interface = interface; - } + dev_cfg.interface = cmd_parser + .get_value::("interface")? + .with_context(|| "No interface configured for scream device")?; - if dev_cfg.interface.eq(&"Demo".to_string()) { + if dev_cfg.interface == ScreamInterface::Demo { dev_cfg.playback = cmd_parser .get_value::("playback")? .with_context(|| "No playback configured for interface")?; diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 2df3f4bd4..7ac63f3cb 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -14,7 +14,7 @@ serde_json = "1.0" libc = "0.2" byteorder = "1.4.3" serde = { version = "1.0", features = ["derive"] } -devices = { path = "../../devices" } +devices = { path = "../../devices", features = ["scream"]} util = { path = "../../util" } acpi = { path = "../../acpi" } smbios = { path = "../../smbios" } -- Gitee From ef096a060a02184ea3d8bd373485ceba0942d875 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 17 Aug 2023 16:53:17 +0800 Subject: [PATCH 1311/2187] mod-test: add missing target_env of aarch64 Although the x86 mod test can't run yeti, let's fix it first. Signed-off-by: yezengruan --- machine/src/standard_vm/mod.rs | 2 +- tests/mod_test/src/libdriver/fwcfg.rs | 1 + tests/mod_test/src/libdriver/pci_bus.rs | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 9076da21f..9740feff5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -15,7 +15,7 @@ pub mod aarch64; pub mod error; #[cfg(target_arch = "x86_64")] -mod x86_64; +pub mod x86_64; pub use anyhow::Result; diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs index 84bffc14c..c26cd4d6c 100644 --- a/tests/mod_test/src/libdriver/fwcfg.rs +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -16,6 +16,7 @@ use super::malloc::GuestAllocator; use crate::libtest::TestState; use crate::utils::{swap_u16, swap_u32, swap_u64}; use devices::legacy::FwCfgEntryType; +#[cfg(target_arch = "aarch64")] use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "aarch64")] diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 1cf5f0ad3..74773d50d 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -16,11 +16,17 @@ use std::rc::Rc; use crate::libdriver::pci::*; use crate::libtest::TestState; use crate::utils::{read_le_u16, read_le_u32, read_le_u64}; +#[cfg(target_arch = "aarch64")] use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +#[cfg(target_arch = "x86_64")] +use machine::standard_vm::x86_64::{LayoutEntryType, MEM_LAYOUT}; const PCIE_MMIO_BASE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; const PCIE_MMIO_SIZE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; +#[cfg(target_arch = "aarch64")] const PCIE_ECAM_BASE: u64 = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; +#[cfg(target_arch = "x86_64")] +const PCIE_ECAM_BASE: u64 = MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].0; pub trait PciBusOps { fn memread(&self, addr: u32, len: usize) -> Vec; -- Gitee From 2489d498169834cc5e48c967f546123d5881ec2b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 19 Aug 2023 16:51:57 +0800 Subject: [PATCH 1312/2187] feature: remove unused feature qmp in Cargo.toml Signed-off-by: yezengruan --- machine/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/machine/Cargo.toml b/machine/Cargo.toml index cfbbe78e9..9018506ff 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -31,8 +31,7 @@ block_backend = { path = "../block_backend" } ui = { path = "../ui" } [features] -default = ["qmp"] -qmp = [] +default = [] boot_time = ["cpu/boot_time"] scream = ["devices/scream", "machine_manager/scream"] scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] -- Gitee From bb6f6814aca3f23788cd89d86ea2e88b3afa754a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 17 Aug 2023 16:34:21 +0800 Subject: [PATCH 1313/2187] build: add feature demo_device for demo devices The demo devices are only used for mod test, add feature demo_device to it. We can enable this feature by: cargo build --features "demo_device" Signed-off-by: yezengruan --- Cargo.toml | 1 + devices/Cargo.toml | 1 + devices/src/pci/demo_dev.rs | 4 ---- devices/src/pci/demo_device/mod.rs | 3 --- devices/src/pci/mod.rs | 2 ++ machine/Cargo.toml | 1 + machine/src/lib.rs | 21 +++++++++++++-------- machine_manager/Cargo.toml | 1 + machine_manager/src/config/mod.rs | 2 ++ 9 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c52ce7ce0..d97b01c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ default = [] boot_time = ["machine/boot_time"] scream_alsa = ["machine/scream_alsa"] scream_pulseaudio = ["machine/scream_pulseaudio"] +demo_device = ["machine/demo_device"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index b9f880a2d..8130acd1a 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -44,3 +44,4 @@ default = [] scream = ["machine_manager/scream"] scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] +demo_device = ["machine_manager/demo_device"] diff --git a/devices/src/pci/demo_dev.rs b/devices/src/pci/demo_dev.rs index 7602479eb..f90877001 100644 --- a/devices/src/pci/demo_dev.rs +++ b/devices/src/pci/demo_dev.rs @@ -39,7 +39,6 @@ use std::{ use anyhow::{bail, Result}; use log::error; -#[cfg(not(target_env = "musl"))] use crate::pci::demo_device::{ dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, }; @@ -72,11 +71,8 @@ impl DemoDev { ) -> Self { // You can choose different device function based on the parameter of device_type. let device: Arc> = match cfg.device_type.as_str() { - #[cfg(not(target_env = "musl"))] "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem, cfg.id.clone()))), - #[cfg(not(target_env = "musl"))] "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), - #[cfg(not(target_env = "musl"))] "demo-display" => Arc::new(Mutex::new(DemoDisplay::new(_sys_mem))), _ => Arc::new(Mutex::new(BaseDevice::new())), }; diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 10a3c8273..839ee8869 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -11,9 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod base_device; -#[cfg(not(target_env = "musl"))] pub mod dpy_device; -#[cfg(not(target_env = "musl"))] pub mod gpu_device; -#[cfg(not(target_env = "musl"))] pub mod kbd_pointer_device; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index b19abe01c..7ee6bf117 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -11,7 +11,9 @@ // See the Mulan PSL v2 for more details. pub mod config; +#[cfg(feature = "demo_device")] pub mod demo_dev; +#[cfg(feature = "demo_device")] pub mod demo_device; pub mod error; pub mod hotplug; diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 9018506ff..c3890595e 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -36,3 +36,4 @@ boot_time = ["cpu/boot_time"] scream = ["devices/scream", "machine_manager/scream"] scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] +demo_device = ["devices/demo_device", "machine_manager/demo_device"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index de74eccce..e6898f197 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -49,7 +49,9 @@ use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(feature = "scream")] use devices::misc::scream::Scream; -use devices::pci::{demo_dev::DemoDev, PciBus, PciDevOps, PciHost, RootPort}; +#[cfg(feature = "demo_device")] +use devices::pci::demo_dev::DemoDev; +use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(not(target_env = "musl"))] use devices::usb::{camera::UsbCamera, usbhost::UsbHost}; @@ -61,16 +63,17 @@ use devices::usb::{ use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; +#[cfg(feature = "demo_device")] +use machine_manager::config::parse_demo_dev; #[cfg(feature = "scream")] use machine_manager::config::scream::parse_scream; use machine_manager::config::{ - complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_demo_dev, - parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, - parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, - parse_vhost_user_blk_pci, parse_virtio_serial, parse_virtserialport, parse_vsock, - BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, - NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, - MAX_VIRTIO_QUEUE, + complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, + parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, + parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk_pci, + parse_virtio_serial, parse_virtserialport, parse_vsock, BootIndexInfo, DriveFile, Incoming, + MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, + PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; #[cfg(not(target_env = "musl"))] use machine_manager::config::{parse_gpu, parse_usb_camera, parse_usb_host}; @@ -1530,6 +1533,7 @@ pub trait MachineOps { "ramfb" => { self.add_ramfb(cfg_args)?; } + #[cfg(feature = "demo_device")] "pcie-demo-dev" => { self.add_demo_dev(vm_config, cfg_args)?; } @@ -1558,6 +1562,7 @@ pub trait MachineOps { bail!("Display is not supported."); } + #[cfg(feature = "demo_device")] fn add_demo_dev(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index fa4aeff39..5585bb931 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -27,3 +27,4 @@ default = [] scream = [] scream_alsa = ["scream"] scream_pulseaudio = ["scream"] +demo_device = [] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 8747f6054..cc8f5b7f5 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -18,6 +18,7 @@ pub mod vnc; mod balloon; mod boot_source; mod chardev; +#[cfg(feature = "demo_device")] mod demo_dev; mod devices; mod drive; @@ -44,6 +45,7 @@ pub use balloon::*; pub use boot_source::*; pub use camera::*; pub use chardev::*; +#[cfg(feature = "demo_device")] pub use demo_dev::*; pub use devices::*; pub use display::*; -- Gitee From 109fddb314287ca87d2b13234f4df4fbdb82f1ad Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 19:59:08 +0800 Subject: [PATCH 1314/2187] build: Add feature optional usb_host Add new "usb_host" build feature which enables usb-host by `--features usb_host`. Signed-off-by: yezengruan --- Cargo.toml | 1 + devices/Cargo.toml | 5 +++-- devices/src/usb/mod.rs | 2 +- docs/build_guide.ch.md | 1 + docs/build_guide.md | 1 + docs/config_guidebook.md | 2 ++ machine/Cargo.toml | 1 + machine/src/lib.rs | 12 ++++++++---- machine/src/standard_vm/mod.rs | 3 ++- machine_manager/Cargo.toml | 1 + machine_manager/src/config/usb.rs | 9 ++++++++- 11 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d97b01c75..9360fc4ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ boot_time = ["machine/boot_time"] scream_alsa = ["machine/scream_alsa"] scream_pulseaudio = ["machine/scream_pulseaudio"] demo_device = ["machine/demo_device"] +usb_host = ["machine/usb_host"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 8130acd1a..0984afe8f 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -33,10 +33,10 @@ ui = { path = "../ui" } pulse = { version = "2.27", package = "libpulse-binding", optional = true } psimple = { version = "2.27", package = "libpulse-simple-binding", optional = true } alsa = { version = "0.7.0", optional = true } +rusb = { version = "0.9", optional = true } +libusb1-sys = { version = "0.6.4", optional = true } [target.'cfg(not(target_env = "musl"))'.dependencies] -rusb = "0.9" -libusb1-sys = "0.6.4" cairo-rs = "0.17.10" [features] @@ -45,3 +45,4 @@ scream = ["machine_manager/scream"] scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] demo_device = ["machine_manager/demo_device"] +usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 5eb011c3b..59bf2da2a 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -20,7 +20,7 @@ pub mod hid; pub mod keyboard; pub mod storage; pub mod tablet; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "usb_host")] pub mod usbhost; pub mod xhci; diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index d43ecd3ca..bc78dad71 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -53,6 +53,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - scream_alsa:使能虚拟声卡,使用`ALSA`后端 - scream_pulseaudio:使能虚拟声卡,使用`PulseAudio`后端 +- usb_host:使能USB Host设备 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index f7a96e4f0..7dc4a558c 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -54,6 +54,7 @@ List of optional features: - scream_alsa: enable virtual sound card with `ALSA` interface - scream_pulseaudio: enable virtual sound card with `PulseAudio` interface +- usb_host: enable USB Host device ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 690a4585f..d5b4436d8 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -898,6 +898,8 @@ Note: 1. The combination of vendor and product ID takes precedence over the combination of bus number and physical port number. 2. The combination of bus and physical port takes precedence over the combination of bus number and addr number. +Please see the [4. Build with features](docs/build_guide.md) if you want to enable usb-host. + ### 2.14 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index c3890595e..a1a470ac7 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -37,3 +37,4 @@ scream = ["devices/scream", "machine_manager/scream"] scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] demo_device = ["devices/demo_device", "machine_manager/demo_device"] +usb_host = ["devices/usb_host", "machine_manager/usb_host"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e6898f197..061ab5304 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -54,7 +54,9 @@ use devices::pci::demo_dev::DemoDev; use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(not(target_env = "musl"))] -use devices::usb::{camera::UsbCamera, usbhost::UsbHost}; +use devices::usb::camera::UsbCamera; +#[cfg(feature = "usb_host")] +use devices::usb::usbhost::UsbHost; use devices::usb::{ keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, UsbDeviceOps, @@ -65,6 +67,8 @@ use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; +#[cfg(feature = "usb_host")] +use machine_manager::config::parse_usb_host; #[cfg(feature = "scream")] use machine_manager::config::scream::parse_scream; use machine_manager::config::{ @@ -76,7 +80,7 @@ use machine_manager::config::{ PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; #[cfg(not(target_env = "musl"))] -use machine_manager::config::{parse_gpu, parse_usb_camera, parse_usb_host}; +use machine_manager::config::{parse_gpu, parse_usb_camera}; use machine_manager::config::{ parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, }; @@ -1406,7 +1410,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - USB Host Configuration. - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "usb_host")] fn add_usb_host(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_host(cfg_args)?; let usbhost = UsbHost::new(device_cfg)?; @@ -1521,7 +1525,7 @@ pub trait MachineOps { "usb-storage" => { self.add_usb_storage(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "usb_host")] "usb-host" => { self.add_usb_host(vm_config, cfg_args)?; } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 9740feff5..d3d7f344d 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1034,8 +1034,9 @@ impl StdMachine { } self.add_usb_camera(&mut locked_vmconfig, &cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "usb_host")] "usb-host" => { + let mut cfg_args = format!("id={}", args.id); let default_value = "0".to_string(); let hostbus = args.hostbus.as_ref().unwrap_or(&default_value); let hostaddr = args.hostaddr.as_ref().unwrap_or(&default_value); diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 5585bb931..05c617895 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -28,3 +28,4 @@ scream = [] scream_alsa = ["scream"] scream_pulseaudio = ["scream"] demo_device = [] +usb_host = [] diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index a1bb53dd7..3c842f880 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -12,13 +12,16 @@ use anyhow::{anyhow, bail, Context, Result}; -use super::{error::ConfigError, get_cameradev_by_id, UnsignedInteger}; +#[cfg(feature = "usb_host")] +use super::UnsignedInteger; +use super::{error::ConfigError, get_cameradev_by_id}; use crate::config::{ check_arg_nonexist, check_arg_too_long, CamBackendType, CameraDevConfig, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, }; use util::aio::AioEngine; +#[cfg(feature = "usb_host")] const USBHOST_ADDR_MAX: u8 = 127; /// XHCI controller configuration. @@ -299,6 +302,7 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result } #[derive(Clone, Debug, Default)] +#[cfg(feature = "usb_host")] pub struct UsbHostConfig { /// USB Host device id. pub id: Option, @@ -316,6 +320,7 @@ pub struct UsbHostConfig { pub iso_urb_count: u32, } +#[cfg(feature = "usb_host")] impl UsbHostConfig { fn check_range(&self) -> Result<()> { if self.hostaddr > USBHOST_ADDR_MAX { @@ -325,6 +330,7 @@ impl UsbHostConfig { } } +#[cfg(feature = "usb_host")] impl ConfigCheck for UsbHostConfig { fn check(&self) -> Result<()> { check_id(self.id.clone(), "usb-host")?; @@ -332,6 +338,7 @@ impl ConfigCheck for UsbHostConfig { } } +#[cfg(feature = "usb_host")] pub fn parse_usb_host(cfg_args: &str) -> Result { let mut cmd_parser = CmdParser::new("usb-host"); cmd_parser -- Gitee From a91e060aadd43a9815dabacd7a6b0390067379ca Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 15 Aug 2023 11:11:22 +0800 Subject: [PATCH 1315/2187] build: Add feature optional usb_camera Add new "usb_camera" build feature which enables usb-camera by `--features usb_camera`. Signed-off-by: yezengruan --- Cargo.toml | 1 + devices/Cargo.toml | 7 +++--- devices/src/lib.rs | 2 +- devices/src/usb/mod.rs | 4 ++-- docs/build_guide.ch.md | 1 + docs/build_guide.md | 1 + docs/config_guidebook.md | 2 ++ machine/Cargo.toml | 1 + machine/src/lib.rs | 12 ++++++----- machine/src/standard_vm/aarch64/syscall.rs | 12 ++++++++--- machine/src/standard_vm/mod.rs | 12 ++++++----- machine/src/standard_vm/x86_64/syscall.rs | 12 ++++++++--- machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 25 +++++++++++++--------- machine_manager/src/config/mod.rs | 3 +++ machine_manager/src/config/usb.rs | 14 +++++++++--- machine_manager/src/machine.rs | 14 ++++++++++-- tests/mod_test/Cargo.toml | 4 ++++ tests/mod_test/src/libdriver/usb.rs | 12 ++++++++--- util/Cargo.toml | 6 +++++- util/src/lib.rs | 1 + 21 files changed, 105 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9360fc4ec..a622d6405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ scream_alsa = ["machine/scream_alsa"] scream_pulseaudio = ["machine/scream_pulseaudio"] demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] +usb_camera = ["machine/usb_camera"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 0984afe8f..3242bdba4 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -17,7 +17,7 @@ vmm-sys-util = "0.11.1" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" once_cell = "1.18.0" -v4l2-sys-mit = "0.3.0" +v4l2-sys-mit = { version = "0.3.0", optional = true } serde_json = "1.0" rand = "0.8.5" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } @@ -35,9 +35,7 @@ psimple = { version = "2.27", package = "libpulse-simple-binding", optional = tr alsa = { version = "0.7.0", optional = true } rusb = { version = "0.9", optional = true } libusb1-sys = { version = "0.6.4", optional = true } - -[target.'cfg(not(target_env = "musl"))'.dependencies] -cairo-rs = "0.17.10" +cairo-rs = { version = "0.17.10", optional = true } [features] default = [] @@ -46,3 +44,4 @@ scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] demo_device = ["machine_manager/demo_device"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] +usb_camera = ["dep:v4l2-sys-mit", "dep:cairo-rs", "machine_manager/usb_camera", "util/usb_camera"] diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 232df248e..077c9ebea 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -17,7 +17,7 @@ //! - legacy devices, such as serial devices pub mod acpi; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "usb_camera")] pub mod camera_backend; pub mod legacy; pub mod misc; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 59bf2da2a..c9039ba71 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -10,9 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "usb_camera")] pub mod camera; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "usb_camera")] pub mod camera_media_type_guid; pub mod config; pub mod error; diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index bc78dad71..3af2949f9 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -54,6 +54,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - scream_alsa:使能虚拟声卡,使用`ALSA`后端 - scream_pulseaudio:使能虚拟声卡,使用`PulseAudio`后端 - usb_host:使能USB Host设备 +- usb_camera:使能USB摄像头 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index 7dc4a558c..c09960699 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -55,6 +55,7 @@ List of optional features: - scream_alsa: enable virtual sound card with `ALSA` interface - scream_pulseaudio: enable virtual sound card with `PulseAudio` interface - usb_host: enable USB Host device +- usb_camera: enable USB camera ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index d5b4436d8..6a647c25e 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -843,6 +843,8 @@ Video Camera Device that based on USB video class protocol. It should be attache Note: Only one camera can be configured. +Please see the [4. Build with features](docs/build_guide.md) if you want to enable usb-camera. + #### 2.13.5 USB Storage USB storage device that base on classic bulk-only transport protocol. It should be attached to USB controller. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index a1a470ac7..1f469466f 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -38,3 +38,4 @@ scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] +usb_camera = ["devices/usb_camera", "machine_manager/usb_camera", "util/usb_camera"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 061ab5304..09f59f8de 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -53,7 +53,7 @@ use devices::misc::scream::Scream; use devices::pci::demo_dev::DemoDev; use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "usb_camera")] use devices::usb::camera::UsbCamera; #[cfg(feature = "usb_host")] use devices::usb::usbhost::UsbHost; @@ -67,6 +67,10 @@ use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; +#[cfg(not(target_env = "musl"))] +use machine_manager::config::parse_gpu; +#[cfg(feature = "usb_camera")] +use machine_manager::config::parse_usb_camera; #[cfg(feature = "usb_host")] use machine_manager::config::parse_usb_host; #[cfg(feature = "scream")] @@ -79,8 +83,6 @@ use machine_manager::config::{ MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; -#[cfg(not(target_env = "musl"))] -use machine_manager::config::{parse_gpu, parse_usb_camera}; use machine_manager::config::{ parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, }; @@ -1377,7 +1379,7 @@ pub trait MachineOps { /// # Arguments /// /// * `cfg_args` - Camera Configuration. - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "usb_camera")] fn add_usb_camera(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_usb_camera(vm_config, cfg_args)?; let camera = UsbCamera::new(device_cfg)?; @@ -1518,7 +1520,7 @@ pub trait MachineOps { "usb-tablet" => { self.add_usb_tablet(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "usb_camera")] "usb-camera" => { self.add_usb_camera(vm_config, cfg_args)?; } diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 6cdb20dfb..645a8e92b 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -13,6 +13,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; +#[cfg(feature = "usb_camera")] use util::v4l2::{ VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, @@ -210,7 +211,7 @@ pub fn syscall_whitelist() -> Vec { /// Create a syscall bpf rule for syscall `ioctl`. fn ioctl_allow_list() -> BpfRule { - BpfRule::new(libc::SYS_ioctl) + let bpf_rule = BpfRule::new(libc::SYS_ioctl) .add_constraint(SeccompCmpOpt::Eq, 1, TCGETS) .add_constraint(SeccompCmpOpt::Eq, 1, TCSETS) .add_constraint(SeccompCmpOpt::Eq, 1, TIOCGWINSZ) @@ -267,7 +268,10 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32); + + #[cfg(feature = "usb_camera")] + let bpf_rule = bpf_rule .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_G_FMT() as u32) @@ -280,7 +284,9 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMOFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_PARM() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMESIZES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32); + + bpf_rule } fn madvise_rule() -> BpfRule { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d3d7f344d..f4ec3a547 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -58,16 +58,18 @@ use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; use devices::pci::PciBus; +#[cfg(feature = "usb_camera")] +use machine_manager::config::get_cameradev_config; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, memory_unit_conversion, BlkDevConfig, ChardevType, ConfigCheck, DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; +use machine_manager::machine::MachineLifecycle; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; -use machine_manager::{config::get_cameradev_config, machine::MachineLifecycle}; use migration::MigrationManager; use ui::input::{key_event, point_event}; #[cfg(not(target_env = "musl"))] @@ -1013,9 +1015,6 @@ impl StdMachine { let driver = args.driver.as_str(); let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); - #[cfg(not(target_env = "musl"))] - let mut cfg_args = format!("id={}", args.id); - #[cfg(target_env = "musl")] let cfg_args = format!("id={}", args.id); match driver { "usb-kbd" => { @@ -1024,8 +1023,9 @@ impl StdMachine { "usb-tablet" => { self.add_usb_tablet(&mut locked_vmconfig, &cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "usb_camera")] "usb-camera" => { + let mut cfg_args = format!("id={}", args.id); if let Some(cameradev) = &args.cameradev { cfg_args = format!("{},cameradev={}", cfg_args, cameradev); } @@ -1524,6 +1524,7 @@ impl DeviceInterface for StdMachine { } } + #[cfg(feature = "usb_camera")] fn cameradev_add(&mut self, args: qmp_schema::CameraDevAddArgument) -> Response { let config = match get_cameradev_config(args) { Ok(conf) => conf, @@ -1549,6 +1550,7 @@ impl DeviceInterface for StdMachine { } } + #[cfg(feature = "usb_camera")] fn cameradev_del(&mut self, id: String) -> Response { match self .get_vm_config() diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 256033486..41e36a3ac 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -13,6 +13,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; +#[cfg(feature = "usb_camera")] use util::v4l2::{ VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, @@ -212,7 +213,7 @@ pub fn syscall_whitelist() -> Vec { /// Create a syscall bpf rule for syscall `ioctl`. fn ioctl_allow_list() -> BpfRule { - BpfRule::new(libc::SYS_ioctl) + let bpf_rule = BpfRule::new(libc::SYS_ioctl) .add_constraint(SeccompCmpOpt::Eq, 1, TCGETS) .add_constraint(SeccompCmpOpt::Eq, 1, TCSETS) .add_constraint(SeccompCmpOpt::Eq, 1, TIOCGWINSZ) @@ -285,7 +286,10 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32); + + #[cfg(feature = "usb_camera")] + let bpf_rule = bpf_rule .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_G_FMT() as u32) @@ -298,7 +302,9 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMOFF() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_PARM() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMESIZES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32); + + bpf_rule } fn madvise_rule() -> BpfRule { diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 05c617895..17509d85d 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -29,3 +29,4 @@ scream_alsa = ["scream"] scream_pulseaudio = ["scream"] demo_device = [] usb_host = [] +usb_camera = [] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index c7c41b3ab..e152b7de1 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -70,7 +70,7 @@ macro_rules! add_args_to_config_multi { /// This function is to define all commandline arguments. pub fn create_args_parser<'a>() -> ArgParser<'a> { - ArgParser::new("StratoVirt") + let parser = ArgParser::new("StratoVirt") .version(util::VERSION) .author("The StratoVirt Project Developers") .about("A light kvm-based hypervisor.") @@ -155,14 +155,6 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { \n\t\tset numa distance: -numa dist,src=<0>,dst=<1>,val=<20> ") .takes_values(true), ) - .arg( - Arg::with_name("cameradev") - .multiple(true) - .long("cameradev") - .value_name("") - .help("set cameradev: -cameradev v4l2,id=,path=") - .takes_values(true), - ) .arg( Arg::with_name("kernel") .long("kernel") @@ -489,7 +481,19 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { \n\t\tadd type4 table: -smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,max-speed=%d][,current-speed=%d]; \ \n\t\tadd type17 table: -smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str][,asset=str][,part=str][,speed=%d]") .takes_values(true), - ) + ); + + #[cfg(feature = "usb_camera")] + let parser = parser.arg( + Arg::with_name("cameradev") + .multiple(true) + .long("cameradev") + .value_name("") + .help("set cameradev: -cameradev v4l2,id=,path=") + .takes_values(true), + ); + + parser } /// Create `VmConfig` from `ArgMatches`'s arg. @@ -556,6 +560,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config_multi!((args.values_of("device")), vm_cfg, add_device); add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); + #[cfg(feature = "usb_camera")] add_args_to_config_multi!((args.values_of("cameradev")), vm_cfg, add_camera_backend); add_args_to_config_multi!((args.values_of("smbios")), vm_cfg, add_smbios); diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index cc8f5b7f5..5a5692b9d 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "usb_camera")] pub mod camera; pub mod display; pub mod error; @@ -43,6 +44,7 @@ mod vfio; pub use balloon::*; pub use boot_source::*; +#[cfg(feature = "usb_camera")] pub use camera::*; pub use chardev::*; #[cfg(feature = "demo_device")] @@ -129,6 +131,7 @@ pub struct VmConfig { pub incoming: Option, pub vnc: Option, pub display: Option, + #[cfg(feature = "usb_camera")] pub camera_backend: HashMap, pub windows_emu_pid: Option, pub smbios: SmbiosConfig, diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 3c842f880..04863c698 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -12,13 +12,16 @@ use anyhow::{anyhow, bail, Context, Result}; +use super::error::ConfigError; +#[cfg(feature = "usb_camera")] +use super::get_cameradev_by_id; #[cfg(feature = "usb_host")] use super::UnsignedInteger; -use super::{error::ConfigError, get_cameradev_by_id}; use crate::config::{ - check_arg_nonexist, check_arg_too_long, CamBackendType, CameraDevConfig, CmdParser, - ConfigCheck, ScsiDevConfig, VmConfig, + check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, }; +#[cfg(feature = "usb_camera")] +use crate::config::{CamBackendType, CameraDevConfig}; use util::aio::AioEngine; #[cfg(feature = "usb_host")] @@ -154,6 +157,7 @@ pub fn parse_usb_tablet(conf: &str) -> Result { Ok(dev) } +#[cfg(feature = "usb_camera")] pub fn parse_usb_camera(vm_config: &mut VmConfig, conf: &str) -> Result { let mut cmd_parser = CmdParser::new("usb-camera"); cmd_parser @@ -191,6 +195,7 @@ pub fn check_id(id: Option, device: &str) -> Result<()> { Ok(()) } +#[cfg(feature = "usb_camera")] #[derive(Clone, Debug)] pub struct UsbCameraConfig { pub id: Option, @@ -200,6 +205,7 @@ pub struct UsbCameraConfig { pub drive: CameraDevConfig, } +#[cfg(feature = "usb_camera")] impl UsbCameraConfig { pub fn new() -> Self { UsbCameraConfig { @@ -212,12 +218,14 @@ impl UsbCameraConfig { } } +#[cfg(feature = "usb_camera")] impl Default for UsbCameraConfig { fn default() -> Self { Self::new() } } +#[cfg(feature = "usb_camera")] impl ConfigCheck for UsbCameraConfig { fn check(&self) -> Result<()> { check_id(self.id.clone(), "usb-camera")?; diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 2541e0f8b..0244f3609 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -175,10 +175,20 @@ pub trait DeviceInterface { fn chardev_remove(&mut self, _id: String) -> Response; /// Creates a new camera device. - fn cameradev_add(&mut self, args: CameraDevAddArgument) -> Response; + fn cameradev_add(&mut self, _args: CameraDevAddArgument) -> Response { + Response::create_response( + serde_json::to_value("cameradev_add not supported for VM".to_string()).unwrap(), + None, + ) + } /// Delete a camera device. - fn cameradev_del(&mut self, id: String) -> Response; + fn cameradev_del(&mut self, _id: String) -> Response { + Response::create_response( + serde_json::to_value("cameradev_del not supported for VM".to_string()).unwrap(), + None, + ) + } /// Receive a file descriptor via SCM rights and assign it a name. fn getfd(&self, fd_name: String, if_fd: Option) -> Response; diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 7ac63f3cb..d0a59ff0a 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -20,3 +20,7 @@ acpi = { path = "../../acpi" } smbios = { path = "../../smbios" } machine = { path = "../../machine" } virtio = { path = "../../virtio"} + +[features] +default = [] +usb_camera = ["devices/usb_camera"] diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index e5cf7533a..3ef88694a 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -29,10 +29,11 @@ use super::{ }; use crate::libdriver::pci::{PciMsixOps, PCI_DEVICE_ID}; use crate::libtest::{test_init, TestState}; +#[cfg(feature = "usb_camera")] +use devices::usb::camera::{ + INTERFACE_ID_CONTROL, SC_VIDEOCONTROL, SC_VIDEOSTREAMING, SC_VIDEO_INTERFACE_COLLECTION, +}; use devices::usb::{ - camera::{ - INTERFACE_ID_CONTROL, SC_VIDEOCONTROL, SC_VIDEOSTREAMING, SC_VIDEO_INTERFACE_COLLECTION, - }, config::*, hid::{ HID_GET_IDLE, HID_GET_PROTOCOL, HID_GET_REPORT, HID_SET_IDLE, HID_SET_PROTOCOL, @@ -1612,6 +1613,7 @@ impl TestXhciPciDevice { // class assert_eq!(buf[4], USB_CLASS_VIDEO); // subclass + #[cfg(feature = "usb_camera")] assert_eq!(buf[5], SC_VIDEO_INTERFACE_COLLECTION); // 2. VC interface @@ -1622,9 +1624,12 @@ impl TestXhciPciDevice { *offset, ); + #[cfg(feature = "usb_camera")] assert_eq!(buf[1], USB_DT_INTERFACE); + #[cfg(feature = "usb_camera")] assert_eq!(buf[2], INTERFACE_ID_CONTROL); assert_eq!(buf[5], USB_CLASS_VIDEO); + #[cfg(feature = "usb_camera")] assert_eq!(buf[6], SC_VIDEOCONTROL); // get total vc length from its header descriptor @@ -1647,6 +1652,7 @@ impl TestXhciPciDevice { assert_eq!(buf[1], USB_DT_INTERFACE); assert_eq!(buf[5], USB_CLASS_VIDEO); + #[cfg(feature = "usb_camera")] assert_eq!(buf[6], SC_VIDEOSTREAMING); // get total vs length from its header descriptor diff --git a/util/Cargo.toml b/util/Cargo.toml index 10c1d70c7..83a49a34c 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -20,5 +20,9 @@ byteorder = "1.4.3" once_cell = "1.18.0" io-uring = "0.6.0" serde = { version = "1.0", features = ["derive"] } -v4l2-sys-mit = "0.3.0" +v4l2-sys-mit = { version = "0.3.0", optional = true } nix = "0.26.2" + +[features] +default = [] +usb_camera = ["dep:v4l2-sys-mit"] diff --git a/util/src/lib.rs b/util/src/lib.rs index 6ef4d478c..1d8ba7f05 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -38,6 +38,7 @@ pub mod test_helper; pub mod time; pub mod trace; pub mod unix; +#[cfg(feature = "usb_camera")] pub mod v4l2; pub use anyhow::Result; -- Gitee From da06eeab59462c0b9d9bc999b2e05dd8636e4fd4 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 22 Aug 2023 11:04:00 +0800 Subject: [PATCH 1316/2187] mem: Fixed the issue where files were deleted by mistake Memory backend files created do not need to be deleted. Signed-off-by: jiewangqun --- address_space/src/host_mmap.rs | 49 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index e2e5e7156..e94d4aeea 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -43,6 +43,22 @@ pub struct FileBackend { pub page_size: u64, } +fn file_unlink(file_path: &str) { + let fs_cstr = std::ffi::CString::new(file_path).unwrap().into_raw(); + + if unsafe { libc::unlink(fs_cstr) } != 0 { + error!( + "Failed to unlink file \"{}\", error: {:?}", + file_path, + std::io::Error::last_os_error() + ); + } + // SAFETY: fs_cstr obtained by calling CString::into_raw. + unsafe { + drop(std::ffi::CString::from_raw(fs_cstr)); + } +} + impl FileBackend { /// Construct a new FileBackend with an opened file. /// @@ -73,6 +89,7 @@ impl FileBackend { /// * fail to set file length. pub fn new_mem(file_path: &str, file_len: u64) -> Result { let path = std::path::Path::new(&file_path); + let mut need_unlink = false; let file = if path.is_dir() { // The last six characters of template file must be "XXXXXX" for `mkstemp` // function to create unique temporary file. @@ -103,32 +120,14 @@ impl FileBackend { } unsafe { File::from_raw_fd(raw_fd) } } else { - let not_existed = !path.exists(); + need_unlink = !path.exists(); // Open the file, if not exist, create it. - let file_ret = std::fs::OpenOptions::new() + std::fs::OpenOptions::new() .read(true) .write(true) .create(true) .open(path) - .with_context(|| format!("Failed to open file: {}", file_path))?; - - if not_existed { - let fs_cstr = std::ffi::CString::new(file_path).unwrap().into_raw(); - - if unsafe { libc::unlink(fs_cstr) } != 0 { - error!( - "Failed to unlink file \"{}\", error: {:?}", - file_path, - std::io::Error::last_os_error() - ); - } - // SAFETY: fs_cstr obtained by calling CString::into_raw. - unsafe { - drop(std::ffi::CString::from_raw(fs_cstr)); - } - } - - file_ret + .with_context(|| format!("Failed to open file: {}", file_path))? }; // Safe because struct `statfs` only contains plain-data-type field, @@ -142,8 +141,12 @@ impl FileBackend { let old_file_len = file.metadata().unwrap().len(); if old_file_len == 0 { - file.set_len(file_len) - .with_context(|| format!("Failed to set length of file: {}", file_path))?; + if file.set_len(file_len).is_err() { + if need_unlink { + file_unlink(file_path); + } + bail!("Failed to set length of file: {}", file_path); + } } else if old_file_len < file_len { bail!( "Backing file {} does not has sufficient resource for allocating RAM (size is 0x{:X})", -- Gitee From ff34e51692512f107432b8e6dd752d2ab3654740 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 22 Aug 2023 20:55:03 +0800 Subject: [PATCH 1317/2187] build: Add feature optional usb_camera_v4l2 Add new "usb_camera_v4l2" build feature which enables usb-camera by `--features usb_camera_v4l2` with `v4l2` backend. Signed-off-by: yezengruan --- Cargo.toml | 2 +- devices/Cargo.toml | 3 ++- devices/src/camera_backend/mod.rs | 6 +++++- docs/build_guide.ch.md | 2 +- docs/build_guide.md | 2 +- machine/Cargo.toml | 3 ++- machine/src/standard_vm/aarch64/syscall.rs | 4 ++-- machine/src/standard_vm/x86_64/syscall.rs | 4 ++-- machine_manager/Cargo.toml | 1 + machine_manager/src/config/camera.rs | 4 +++- util/Cargo.toml | 2 +- util/src/lib.rs | 2 +- 12 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a622d6405..08c2fc237 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ scream_alsa = ["machine/scream_alsa"] scream_pulseaudio = ["machine/scream_pulseaudio"] demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] -usb_camera = ["machine/usb_camera"] +usb_camera_v4l2 = ["machine/usb_camera_v4l2"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 3242bdba4..581696f51 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -44,4 +44,5 @@ scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] demo_device = ["machine_manager/demo_device"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] -usb_camera = ["dep:v4l2-sys-mit", "dep:cairo-rs", "machine_manager/usb_camera", "util/usb_camera"] +usb_camera = ["dep:cairo-rs", "machine_manager/usb_camera"] +usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 1dcf05b47..c32820ad9 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -15,13 +15,16 @@ //! CameraHostdevOps. pub mod demo; +#[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use self::{demo::DemoCamera, v4l2::V4l2CameraBackend}; +use self::demo::DemoCamera; +#[cfg(feature = "usb_camera_v4l2")] +use self::v4l2::V4l2CameraBackend; use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; use util::aio::Iovec; @@ -142,6 +145,7 @@ pub trait CameraHostdevOps: Send + Sync { pub fn camera_ops(config: UsbCameraConfig) -> Result>> { let cam: Arc> = match config.backend { + #[cfg(feature = "usb_camera_v4l2")] CamBackendType::V4l2 => Arc::new(Mutex::new(V4l2CameraBackend::new( config.drive.id.clone().unwrap(), config.drive.path.clone().with_context(|| { diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index 3af2949f9..b008d6805 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -54,7 +54,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - scream_alsa:使能虚拟声卡,使用`ALSA`后端 - scream_pulseaudio:使能虚拟声卡,使用`PulseAudio`后端 - usb_host:使能USB Host设备 -- usb_camera:使能USB摄像头 +- usb_camera_v4l2:使能USB摄像头,使用`v4l2`后端 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index c09960699..ccf1089d5 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -55,7 +55,7 @@ List of optional features: - scream_alsa: enable virtual sound card with `ALSA` interface - scream_pulseaudio: enable virtual sound card with `PulseAudio` interface - usb_host: enable USB Host device -- usb_camera: enable USB camera +- usb_camera_v4l2: enable USB camera with `v4l2` backend ```shell $ cargo build --release --features "scream_alsa" diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 1f469466f..b605eabaa 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -38,4 +38,5 @@ scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] -usb_camera = ["devices/usb_camera", "machine_manager/usb_camera", "util/usb_camera"] +usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] +usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 645a8e92b..5cb15e6c8 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -13,7 +13,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; -#[cfg(feature = "usb_camera")] +#[cfg(feature = "usb_camera_v4l2")] use util::v4l2::{ VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, @@ -270,7 +270,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32); - #[cfg(feature = "usb_camera")] + #[cfg(feature = "usb_camera_v4l2")] let bpf_rule = bpf_rule .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 41e36a3ac..065d6f472 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -13,7 +13,7 @@ use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; -#[cfg(feature = "usb_camera")] +#[cfg(feature = "usb_camera_v4l2")] use util::v4l2::{ VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, @@ -288,7 +288,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32); - #[cfg(feature = "usb_camera")] + #[cfg(feature = "usb_camera_v4l2")] let bpf_rule = bpf_rule .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 17509d85d..3a9b0a4da 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -30,3 +30,4 @@ scream_pulseaudio = ["scream"] demo_device = [] usb_host = [] usb_camera = [] +usb_camera_v4l2 = ["usb_camera"] diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index ee17aaf5f..17c9da6c1 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -31,6 +31,7 @@ pub struct CameraDevConfig { #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum CamBackendType { + #[cfg(feature = "usb_camera_v4l2")] V4l2, Demo, } @@ -40,6 +41,7 @@ impl FromStr for CamBackendType { fn from_str(s: &str) -> std::result::Result { match s { + #[cfg(feature = "usb_camera_v4l2")] "v4l2" => Ok(CamBackendType::V4l2), "demo" => Ok(CamBackendType::Demo), _ => Err(anyhow!("Unknown camera backend type")), @@ -52,7 +54,7 @@ impl CameraDevConfig { CameraDevConfig { id: None, path: None, - backend: CamBackendType::V4l2, + backend: CamBackendType::Demo, } } } diff --git a/util/Cargo.toml b/util/Cargo.toml index 83a49a34c..52a84828c 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -25,4 +25,4 @@ nix = "0.26.2" [features] default = [] -usb_camera = ["dep:v4l2-sys-mit"] +usb_camera_v4l2 = ["dep:v4l2-sys-mit"] diff --git a/util/src/lib.rs b/util/src/lib.rs index 1d8ba7f05..6f49ca9c0 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -38,7 +38,7 @@ pub mod test_helper; pub mod time; pub mod trace; pub mod unix; -#[cfg(feature = "usb_camera")] +#[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; pub use anyhow::Result; -- Gitee From aeb41028120ee10037b625de23fca01c4472e5f2 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Mon, 21 Aug 2023 23:27:05 +0800 Subject: [PATCH 1318/2187] blockdev_add: add `aio` sub field to qmp command `blockdev_add`'s field `file`. This field is used to control the aio feature for block backend in block device hotplug. Signed-off-by: Liu Wenyuan --- machine/src/micro_vm/mod.rs | 9 ++------- machine/src/standard_vm/mod.rs | 3 +-- machine_manager/src/qmp/qmp_schema.rs | 3 +++ util/src/aio/mod.rs | 6 +++++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 191a47bf4..d6f9df0a3 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -90,7 +90,7 @@ use machine_manager::{ use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use migration::{MigrationManager, MigrationStatus}; use syscall::syscall_whitelist; -use util::aio::{AioEngine, WriteZeroesState}; +use util::aio::WriteZeroesState; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::{ @@ -1223,12 +1223,7 @@ impl DeviceInterface for LightMachine { boot_index: None, chardev: None, socket_path: None, - // TODO Add aio option by qmp, now we set it based on "direct". - aio: if direct { - AioEngine::Native - } else { - AioEngine::Off - }, + aio: args.file.aio, queue_size: DEFAULT_VIRTQUEUE_SIZE, discard: false, write_zeroes: WriteZeroesState::Off, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 9076da21f..dceb5c361 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1953,8 +1953,7 @@ fn parse_blockdev(args: &BlockDevAddArgument) -> Result { read_only: args.read_only.unwrap_or(false), direct: true, iops: args.iops, - // TODO Add aio option by qmp, now we set it based on "direct". - aio: AioEngine::Native, + aio: args.file.aio, media: "disk".to_string(), discard: false, write_zeroes: WriteZeroesState::Off, diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 09e6f1169..401c3341a 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -17,6 +17,7 @@ use strum_macros::{EnumIter, EnumString, EnumVariantNames}; use super::Version; use crate::qmp::{Command, Empty, TimeStamp}; +use util::aio::AioEngine; /// A error enum for qmp #[allow(clippy::upper_case_acronyms)] @@ -717,6 +718,8 @@ impl Command for update_region { pub struct FileOptions { pub driver: String, pub filename: String, + #[serde(default)] + pub aio: AioEngine, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 020f815b0..77d2e1458 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -47,10 +47,14 @@ const AIO_IOURING: &str = "io_uring"; /// Max bytes of bounce buffer for IO. const MAX_LEN_BOUNCE_BUFF: u64 = 1 << 20; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] +#[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] pub enum AioEngine { + #[serde(alias = "off")] + #[default] Off = 0, + #[serde(alias = "native")] Native = 1, + #[serde(alias = "iouring")] IoUring = 2, } -- Gitee From 3ca0ced9ff8aaf3a1901632385b725cf67747d1c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 23 Aug 2023 09:52:58 +0800 Subject: [PATCH 1319/2187] remove redundant target_arch of aarch64 Signed-off-by: yezengruan --- cpu/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 5b3abe211..fa34a7828 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -29,7 +29,6 @@ pub mod error; -#[cfg(target_arch = "aarch64")] #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] mod aarch64; -- Gitee From eedc9835871345919ea601fb4c3b4a795bdea8dc Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 23 Aug 2023 09:48:25 +0800 Subject: [PATCH 1320/2187] test: remove temp files after util socket testing Files left after cargo test, let's remove them: - util/test_socket1.sock - util/test_socket2.sock Signed-off-by: yezengruan --- util/src/unix.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/util/src/unix.rs b/util/src/unix.rs index 49bf9a71c..f0ba2348b 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -414,6 +414,7 @@ impl UnixSock { #[cfg(test)] mod tests { + use std::fs; use std::path::Path; use std::time::Duration; @@ -457,6 +458,10 @@ mod tests { assert!(listener.accept().is_ok()); assert_eq!(listener.is_accepted(), true); + + if sock_path.exists() { + fs::remove_file("./test_socket1.sock").unwrap(); + } } #[test] @@ -491,5 +496,9 @@ mod tests { let (data_size, fd_size) = stream.recv_msg(&mut recv, &mut in_fd).unwrap(); assert_eq!(data_size, buff.len()); assert_eq!(fd_size, in_fd.len()); + + if sock_path.exists() { + fs::remove_file("./test_socket2.sock").unwrap(); + } } } -- Gitee From 2ed4ef29071aab7f0b58c60470bed0433af1fe07 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Thu, 24 Aug 2023 09:32:02 +0800 Subject: [PATCH 1321/2187] memory: fix LLT test Delete residual files. Signed-off-by: jiewangqun --- address_space/src/host_mmap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index e94d4aeea..d22b45e82 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -563,6 +563,7 @@ mod test { f_back.as_ref().unwrap().file.metadata().unwrap().len(), 100u64 ); + std::fs::remove_file(file_path).unwrap(); } #[test] @@ -612,5 +613,6 @@ mod test { ) .unwrap(); mem_prealloc(host_addr, 0x10_0000, 2); + std::fs::remove_file(file_path).unwrap(); } } -- Gitee From 09ba4593b7f26d44070fafcd1b658ff537f24f81 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Thu, 24 Aug 2023 10:24:34 +0800 Subject: [PATCH 1322/2187] blockdev_add: fix the pci_test_case to adapt the `aio` field add `aio` field in the `build_hotplug_blk_cmd`. Signed-off-by: Liu Wenyuan --- tests/mod_test/tests/pci_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index c6a0f2ed3..28d3bc6fb 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -248,8 +248,8 @@ fn build_hotplug_blk_cmd( let add_blk_command = format!( "{{\"execute\": \"blockdev-add\", \ \"arguments\": {{\"node-name\": \"drive-{}\", \"file\": {{\"driver\": \ - \"file\", \"filename\": \"{}\"}}, \"cache\": {{\"direct\": true}}, \ - \"read-only\": false}}}}", + \"file\", \"filename\": \"{}\"}, \"aio\": \"native\"}, \ + \"cache\": {{\"direct\": true}}, \"read-only\": false}}}}", hotplug_blk_id, hotplug_image_path ); -- Gitee From 5373ae18a37fe503bd23d5ab6d56a1fd5ddb1236 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Thu, 24 Aug 2023 16:51:47 +0800 Subject: [PATCH 1323/2187] Params: fix a problem when parsing a config with multiple '=' Signed-off-by: Liu Wenyuan --- machine_manager/src/config/boot_source.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_manager/src/config/boot_source.rs b/machine_manager/src/config/boot_source.rs index c7d1a1b74..3cd9c3692 100644 --- a/machine_manager/src/config/boot_source.rs +++ b/machine_manager/src/config/boot_source.rs @@ -187,7 +187,7 @@ impl Param { /// /// * `item` - The `str` transformed to `Param`. fn from_str(item: &str) -> Self { - let split = item.split('='); + let split = item.splitn(2, '='); let vec = split.collect::>(); if vec.len() == 1 { Param { -- Gitee From f1942387613a5afa57993420bb5e1731ef967ed0 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 2 Jul 2023 18:49:30 +0800 Subject: [PATCH 1324/2187] build: Add feature optional gtk Add new "gtk" build feature which enables gtk by `--features gtk`. Signed-off-by: Yan Wen Signed-off-by: yezengruan --- Cargo.toml | 1 + docs/build_guide.ch.md | 1 + docs/build_guide.md | 1 + machine/Cargo.toml | 1 + machine/src/standard_vm/aarch64/mod.rs | 7 +++++-- machine/src/standard_vm/x86_64/mod.rs | 7 +++++-- machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 19 +++++++++++-------- machine_manager/src/config/mod.rs | 3 +++ ui/Cargo.toml | 7 +++++-- ui/src/lib.rs | 2 +- 11 files changed, 35 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08c2fc237..cb44dd5a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ scream_pulseaudio = ["machine/scream_pulseaudio"] demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] usb_camera_v4l2 = ["machine/usb_camera_v4l2"] +gtk = ["machine/gtk"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index b008d6805..f4c28810f 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -55,6 +55,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - scream_pulseaudio:使能虚拟声卡,使用`PulseAudio`后端 - usb_host:使能USB Host设备 - usb_camera_v4l2:使能USB摄像头,使用`v4l2`后端 +- gtk:使能GTK显示 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index ccf1089d5..a613e0b3e 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -56,6 +56,7 @@ List of optional features: - scream_pulseaudio: enable virtual sound card with `PulseAudio` interface - usb_host: enable USB Host device - usb_camera_v4l2: enable USB camera with `v4l2` backend +- gtk: enable GTK display ```shell $ cargo build --release --features "scream_alsa" diff --git a/machine/Cargo.toml b/machine/Cargo.toml index b605eabaa..4de689cca 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -40,3 +40,4 @@ demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] +gtk = ["ui/gtk", "machine_manager/gtk"] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index cc22cfcee..acae079a0 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -58,7 +58,7 @@ use hypervisor::kvm::KVM_FDS; #[cfg(not(target_env = "musl"))] use machine_manager::config::parse_ramfb; use machine_manager::config::ShutdownAction; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, @@ -74,8 +74,10 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::{MigrationManager, MigrationStatus}; use pci_host_root::PciHostRoot; use syscall::syscall_whitelist; +#[cfg(feature = "gtk")] +use ui::gtk::gtk_display_init; #[cfg(not(target_env = "musl"))] -use ui::{gtk::gtk_display_init, vnc::vnc_init}; +use ui::vnc::vnc_init; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; @@ -754,6 +756,7 @@ impl MachineOps for StdMachine { #[cfg(not(target_env = "musl"))] fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { // GTK display init. + #[cfg(feature = "gtk")] match vm_config.display { Some(ref ds_cfg) if ds_cfg.gtk => { let ui_context = UiContext { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index b2e0fc38f..3067b3bb2 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -46,7 +46,7 @@ use devices::legacy::{ use devices::pci::{PciDevOps, PciHost}; use devices::sysbus::SysBus; use hypervisor::kvm::KVM_FDS; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, @@ -62,8 +62,10 @@ use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use syscall::syscall_whitelist; +#[cfg(feature = "gtk")] +use ui::gtk::gtk_display_init; #[cfg(not(target_env = "musl"))] -use ui::{gtk::gtk_display_init, vnc::vnc_init}; +use ui::vnc::vnc_init; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, }; @@ -634,6 +636,7 @@ impl MachineOps for StdMachine { #[cfg(not(target_env = "musl"))] fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { // GTK display init. + #[cfg(feature = "gtk")] match vm_config.display { Some(ref ds_cfg) if ds_cfg.gtk => { let ui_context = UiContext { diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 3a9b0a4da..d82ceeeca 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -31,3 +31,4 @@ demo_device = [] usb_host = [] usb_camera = [] usb_camera_v4l2 = ["usb_camera"] +gtk = [] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index e152b7de1..0fdbfa054 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -453,14 +453,6 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("specify the ip and port for vnc") .takes_value(true), ) - .arg( - Arg::with_name("display") - .multiple(false) - .long("display") - .value_name("gtk") - .help("set display for virtual machine: currently only supports gtk") - .takes_value(true), - ) .arg( Arg::with_name("windows_emu_pid") .multiple(false) @@ -493,6 +485,16 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true), ); + #[cfg(feature = "gtk")] + let parser = parser.arg( + Arg::with_name("display") + .multiple(false) + .long("display") + .value_name("gtk") + .help("set display for virtual machine: currently only supports gtk") + .takes_value(true), + ); + parser } @@ -528,6 +530,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); + #[cfg(feature = "gtk")] add_args_to_config!((args.value_of("display")), vm_cfg, add_display); add_args_to_config!( (args.value_of("windows_emu_pid")), diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 5a5692b9d..52734d012 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -12,6 +12,7 @@ #[cfg(feature = "usb_camera")] pub mod camera; +#[cfg(feature = "gtk")] pub mod display; pub mod error; pub mod vnc; @@ -50,6 +51,7 @@ pub use chardev::*; #[cfg(feature = "demo_device")] pub use demo_dev::*; pub use devices::*; +#[cfg(feature = "gtk")] pub use display::*; pub use drive::*; pub use error::ConfigError; @@ -130,6 +132,7 @@ pub struct VmConfig { pub numa_nodes: Vec<(String, String)>, pub incoming: Option, pub vnc: Option, + #[cfg(feature = "gtk")] pub display: Option, #[cfg(feature = "usb_camera")] pub camera_backend: HashMap, diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 423c2de92..55a2220d5 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -16,6 +16,8 @@ vmm-sys-util = "0.11.1" once_cell = "1.18.0" sscanf = "0.4.1" bitintr = "0.3.0" +gtk = { version = "0.17.1", optional = true } +gettext-rs = { version = "0.7.0", features = ["gettext-system"], optional = true } machine_manager = { path = "../machine_manager" } util = { path = "../util" } @@ -23,5 +25,6 @@ util = { path = "../util" } rustls = "0.21.1" rustls-pemfile = "1.0.2" sasl2-sys = "0.1.20" -gtk = "0.17.1" -gettext-rs = { version = "0.7.0", features = ["gettext-system"] } + +[features] +gtk = ["dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] diff --git a/ui/src/lib.rs b/ui/src/lib.rs index eab7f7939..c85f94724 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -13,7 +13,7 @@ #[cfg(not(target_env = "musl"))] pub mod console; pub mod error; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "gtk")] pub mod gtk; pub mod input; #[cfg(not(target_env = "musl"))] -- Gitee From 4fdcce7d655b25e37d45325e2a21f0b2c0f44d9c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 7 Aug 2023 16:07:09 +0800 Subject: [PATCH 1325/2187] build: Add feature optional vnc Add new "vnc" build feature which enables vnc by `--features vnc`. Signed-off-by: yezengruan --- Cargo.toml | 1 + docs/build_guide.ch.md | 1 + docs/build_guide.md | 1 + docs/config_guidebook.md | 2 ++ machine/Cargo.toml | 1 + machine/src/standard_vm/aarch64/mod.rs | 6 +++--- machine/src/standard_vm/mod.rs | 4 ++-- machine/src/standard_vm/x86_64/mod.rs | 6 +++--- machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 19 +++++++++++-------- machine_manager/src/config/mod.rs | 3 +++ ui/Cargo.toml | 9 ++++----- ui/src/lib.rs | 2 +- 13 files changed, 34 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb44dd5a7..2c2db9da1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] usb_camera_v4l2 = ["machine/usb_camera_v4l2"] gtk = ["machine/gtk"] +vnc = ["machine/vnc"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index f4c28810f..8e6f3affb 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -56,6 +56,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - usb_host:使能USB Host设备 - usb_camera_v4l2:使能USB摄像头,使用`v4l2`后端 - gtk:使能GTK显示 +- vnc:使能VNC显示 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index a613e0b3e..d76dbe306 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -57,6 +57,7 @@ List of optional features: - usb_host: enable USB Host device - usb_camera_v4l2: enable USB camera with `v4l2` backend - gtk: enable GTK display +- vnc: enable VNC display ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 6a647c25e..2ce69fb01 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -989,6 +989,8 @@ Sample Configuration: Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. +Please see the [4. Build with features](docs/build_guide.md) if you want to enable VNC. + ### 2.17 Virtio-fs Virtio-fs is a shared file system that lets virtual machines access a directory tree on the host. Unlike existing approaches, it is designed to offer local file system semantics and performance. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 4de689cca..bfbe8fb00 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -41,3 +41,4 @@ usb_host = ["devices/usb_host", "machine_manager/usb_host"] usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] gtk = ["ui/gtk", "machine_manager/gtk"] +vnc = ["ui/vnc", "machine_manager/vnc"] diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index acae079a0..903337321 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -76,7 +76,7 @@ use pci_host_root::PciHostRoot; use syscall::syscall_whitelist; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "vnc")] use ui::vnc::vnc_init; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; @@ -705,7 +705,6 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; - #[cfg(not(target_env = "musl"))] locked_vm .display_init(vm_config) .with_context(|| "Fail to init display")?; @@ -753,7 +752,7 @@ impl MachineOps for StdMachine { } /// Create display. - #[cfg(not(target_env = "musl"))] + #[allow(unused_variables)] fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { // GTK display init. #[cfg(feature = "gtk")] @@ -773,6 +772,7 @@ impl MachineOps for StdMachine { }; // VNC display init. + #[cfg(feature = "vnc")] vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; Ok(()) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 94afd6904..d98ad7012 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -72,7 +72,7 @@ use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; use migration::MigrationManager; use ui::input::{key_event, point_event}; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "vnc")] use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; use util::byte_code::ByteCode; @@ -1212,7 +1212,7 @@ impl DeviceInterface for StdMachine { } fn query_vnc(&self) -> Response { - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "vnc")] if let Some(vnc_info) = qmp_query_vnc() { return Response::create_response(serde_json::to_value(vnc_info).unwrap(), None); } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 3067b3bb2..43ed33bf8 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -64,7 +64,7 @@ use migration::{MigrationManager, MigrationStatus}; use syscall::syscall_whitelist; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "vnc")] use ui::vnc::vnc_init; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, @@ -547,7 +547,6 @@ impl MachineOps for StdMachine { .reset_fwcfg_boot_order() .with_context(|| "Fail to update boot order imformation to FwCfg device")?; - #[cfg(not(target_env = "musl"))] locked_vm .display_init(vm_config) .with_context(|| "Fail to init display")?; @@ -633,7 +632,7 @@ impl MachineOps for StdMachine { } /// Create display. - #[cfg(not(target_env = "musl"))] + #[allow(unused_variables)] fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { // GTK display init. #[cfg(feature = "gtk")] @@ -653,6 +652,7 @@ impl MachineOps for StdMachine { }; // VNC display init. + #[cfg(feature = "vnc")] vnc_init(&vm_config.vnc, &vm_config.object) .with_context(|| "Failed to init VNC server!")?; Ok(()) diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index d82ceeeca..dc3f7ce61 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -32,3 +32,4 @@ usb_host = [] usb_camera = [] usb_camera_v4l2 = ["usb_camera"] gtk = [] +vnc = [] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 0fdbfa054..524d882ea 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -445,14 +445,6 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true) .required(false), ) - .arg( - Arg::with_name("vnc") - .multiple(false) - .long("vnc") - .value_name("ip:port") - .help("specify the ip and port for vnc") - .takes_value(true), - ) .arg( Arg::with_name("windows_emu_pid") .multiple(false) @@ -495,6 +487,16 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_value(true), ); + #[cfg(feature = "vnc")] + let parser = parser.arg( + Arg::with_name("vnc") + .multiple(false) + .long("vnc") + .value_name("ip:port") + .help("specify the ip and port for vnc") + .takes_value(true), + ); + parser } @@ -529,6 +531,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("initrd-file")), vm_cfg, add_initrd); add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); + #[cfg(feature = "vnc")] add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); #[cfg(feature = "gtk")] add_args_to_config!((args.value_of("display")), vm_cfg, add_display); diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 52734d012..86046dae4 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -15,6 +15,7 @@ pub mod camera; #[cfg(feature = "gtk")] pub mod display; pub mod error; +#[cfg(feature = "vnc")] pub mod vnc; mod balloon; @@ -71,6 +72,7 @@ pub use smbios::*; pub use tls_creds::*; pub use usb::*; pub use vfio::*; +#[cfg(feature = "vnc")] pub use vnc::*; use std::collections::HashMap; @@ -131,6 +133,7 @@ pub struct VmConfig { pub global_config: HashMap, pub numa_nodes: Vec<(String, String)>, pub incoming: Option, + #[cfg(feature = "vnc")] pub vnc: Option, #[cfg(feature = "gtk")] pub display: Option, diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 55a2220d5..7fad0ba37 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -18,13 +18,12 @@ sscanf = "0.4.1" bitintr = "0.3.0" gtk = { version = "0.17.1", optional = true } gettext-rs = { version = "0.7.0", features = ["gettext-system"], optional = true } +rustls = { version = "0.21.1", optional = true } +rustls-pemfile = { version = "1.0.2", optional = true } +sasl2-sys = { version = "0.1.20", optional = true } machine_manager = { path = "../machine_manager" } util = { path = "../util" } -[target.'cfg(not(target_env = "musl"))'.dependencies] -rustls = "0.21.1" -rustls-pemfile = "1.0.2" -sasl2-sys = "0.1.20" - [features] gtk = ["dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] +vnc = ["dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] diff --git a/ui/src/lib.rs b/ui/src/lib.rs index c85f94724..7cf409277 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -19,7 +19,7 @@ pub mod input; #[cfg(not(target_env = "musl"))] pub mod pixman; pub mod utils; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "vnc")] pub mod vnc; mod data; -- Gitee From 7aa579536b1158d62279b685a708106e453c1b2b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 16 Aug 2023 19:31:20 +0800 Subject: [PATCH 1326/2187] build: Add feature optional ramfb Add new "ramfb" build feature which enables ramfb by `--features ramfb`. Signed-off-by: yezengruan --- Cargo.toml | 1 + devices/Cargo.toml | 1 + devices/src/legacy/mod.rs | 5 ++--- devices/src/sysbus/mod.rs | 1 + docs/build_guide.ch.md | 1 + docs/build_guide.md | 1 + docs/config_guidebook.md | 2 ++ machine/Cargo.toml | 1 + machine/src/lib.rs | 2 +- machine/src/standard_vm/aarch64/mod.rs | 6 +++--- machine_manager/Cargo.toml | 1 + machine_manager/src/config/mod.rs | 2 ++ 12 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c2db9da1..fd119113f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ usb_host = ["machine/usb_host"] usb_camera_v4l2 = ["machine/usb_camera_v4l2"] gtk = ["machine/gtk"] vnc = ["machine/vnc"] +ramfb = ["machine/ramfb"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 581696f51..6bbae3a19 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -46,3 +46,4 @@ demo_device = ["machine_manager/demo_device"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] usb_camera = ["dep:cairo-rs", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] +ramfb = [] diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 6fc9a29b9..cee969f52 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -34,7 +34,7 @@ mod pflash; mod pl011; #[cfg(target_arch = "aarch64")] mod pl031; -#[cfg(all(not(target_env = "musl"), target_arch = "aarch64"))] +#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] mod ramfb; #[cfg(target_arch = "x86_64")] mod rtc; @@ -56,7 +56,6 @@ pub use pflash::PFlash; pub use pl011::PL011; #[cfg(target_arch = "aarch64")] pub use pl031::{PL031, RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; -#[cfg(target_arch = "aarch64")] -#[cfg(not(target_env = "musl"))] +#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] pub use ramfb::Ramfb; pub use serial::{Serial, SERIAL_ADDR}; diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index a02c0697a..73670dba6 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -212,6 +212,7 @@ pub enum SysBusDevType { PL011, FwCfg, Flash, + #[cfg(all(feature = "ramfb", target_arch = "aarch64"))] Ramfb, Others, } diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index 8e6f3affb..d0005dba1 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -57,6 +57,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - usb_camera_v4l2:使能USB摄像头,使用`v4l2`后端 - gtk:使能GTK显示 - vnc:使能VNC显示 +- ramfb:使能ramfb显示设备 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index d76dbe306..47d80d485 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -58,6 +58,7 @@ List of optional features: - usb_camera_v4l2: enable USB camera with `v4l2` backend - gtk: enable GTK display - vnc: enable VNC display +- ramfb: enable ramfb display device ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2ce69fb01..1c8c466dc 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1101,6 +1101,8 @@ Sample Configuration: Note: Only supported on aarch64. +Please see the [4. Build with features](docs/build_guide.md) if you want to enable ramfb. + ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index bfbe8fb00..40429ecde 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -42,3 +42,4 @@ usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] gtk = ["ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] +ramfb = ["devices/ramfb", "machine_manager/ramfb"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 09f59f8de..74ca66329 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1535,7 +1535,7 @@ pub trait MachineOps { "virtio-gpu-pci" => { self.add_virtio_pci_gpu(cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "ramfb")] "ramfb" => { self.add_ramfb(cfg_args)?; } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 903337321..efa963f1d 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -46,7 +46,7 @@ use cpu::{ }; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; use devices::acpi::power::PowerDev; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "ramfb")] use devices::legacy::Ramfb; use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, @@ -55,7 +55,7 @@ use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use devices::sysbus::{SysBus, SysBusDevType, SysRes}; use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "ramfb")] use machine_manager::config::parse_ramfb; use machine_manager::config::ShutdownAction; #[cfg(feature = "gtk")] @@ -778,7 +778,7 @@ impl MachineOps for StdMachine { Ok(()) } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "ramfb")] fn add_ramfb(&mut self, cfg_args: &str) -> Result<()> { let install = parse_ramfb(cfg_args)?; let fwcfg_dev = self diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index dc3f7ce61..aa4c7f7d6 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -33,3 +33,4 @@ usb_camera = [] usb_camera_v4l2 = ["usb_camera"] gtk = [] vnc = [] +ramfb = [] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 86046dae4..d45d25e1b 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -33,6 +33,7 @@ mod machine_config; mod network; mod numa; mod pci; +#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] mod ramfb; mod rng; mod sasl_auth; @@ -64,6 +65,7 @@ pub use machine_config::*; pub use network::*; pub use numa::*; pub use pci::*; +#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] pub use ramfb::*; pub use rng::*; pub use sasl_auth::*; -- Gitee From 94a60311fe25fdc92d00e2b019f84be9f9cef112 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 17 Aug 2023 15:38:56 +0800 Subject: [PATCH 1327/2187] build: Add feature optional virtio_gpu Add new "virtio_gpu" build feature which enables virtio-gpu by `--features virtio_gpu`. Signed-off-by: yezengruan --- Cargo.toml | 1 + docs/build_guide.ch.md | 1 + docs/build_guide.md | 1 + docs/config_guidebook.md | 2 ++ machine/Cargo.toml | 1 + machine/src/lib.rs | 8 ++++---- machine_manager/Cargo.toml | 1 + machine_manager/src/config/mod.rs | 2 ++ tests/mod_test/Cargo.toml | 2 +- virtio/Cargo.toml | 6 ++++-- virtio/src/device/mod.rs | 2 +- virtio/src/lib.rs | 2 +- 12 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd119113f..690c430c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ usb_camera_v4l2 = ["machine/usb_camera_v4l2"] gtk = ["machine/gtk"] vnc = ["machine/vnc"] ramfb = ["machine/ramfb"] +virtio_gpu = ["machine/virtio_gpu"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index d0005dba1..5f97384f6 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -58,6 +58,7 @@ $ cargo build --release --target ${arch}-unknown-linux-musl - gtk:使能GTK显示 - vnc:使能VNC显示 - ramfb:使能ramfb显示设备 +- virtio_gpu:使能virtio-gpu虚拟显卡 ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/build_guide.md b/docs/build_guide.md index 47d80d485..3ee1120ca 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -59,6 +59,7 @@ List of optional features: - gtk: enable GTK display - vnc: enable VNC display - ramfb: enable ramfb display device +- virtio_gpu: enable virtio-gpu virtualized graphics card ```shell $ cargo build --release --features "scream_alsa" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 1c8c466dc..50f8ddcb7 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1063,6 +1063,8 @@ Note: 1. Only virtio-gpu 2D supported. 2. Live migration is not supported. +Please see the [4. Build with features](docs/build_guide.md) if you want to enable virtio-gpu. + ### 2.19 ivshmem-scream ivshmem-scream is a virtual sound card that relies on Intel-VM shared memory to transmit audio data. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 40429ecde..be27c74d6 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -43,3 +43,4 @@ usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb gtk = ["ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] +virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 74ca66329..3936cc1c9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -67,7 +67,7 @@ use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "virtio_gpu")] use machine_manager::config::parse_gpu; #[cfg(feature = "usb_camera")] use machine_manager::config::parse_usb_camera; @@ -100,7 +100,7 @@ use util::{ seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; use vfio::{VfioDevice, VfioPciDevice}; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "virtio_gpu")] use virtio::Gpu; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, Block, BlockState, Rng, @@ -1002,7 +1002,7 @@ pub trait MachineOps { Ok(()) } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "virtio_gpu")] fn add_virtio_pci_gpu(&mut self, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -1531,7 +1531,7 @@ pub trait MachineOps { "usb-host" => { self.add_usb_host(vm_config, cfg_args)?; } - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "virtio_gpu")] "virtio-gpu-pci" => { self.add_virtio_pci_gpu(cfg_args)?; } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index aa4c7f7d6..c62b9631a 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -34,3 +34,4 @@ usb_camera_v4l2 = ["usb_camera"] gtk = [] vnc = [] ramfb = [] +virtio_gpu = [] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index d45d25e1b..3fa5891d2 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -26,6 +26,7 @@ mod demo_dev; mod devices; mod drive; mod fs; +#[cfg(feature = "virtio_gpu")] mod gpu; mod incoming; mod iothread; @@ -58,6 +59,7 @@ pub use display::*; pub use drive::*; pub use error::ConfigError; pub use fs::*; +#[cfg(feature = "virtio_gpu")] pub use gpu::*; pub use incoming::*; pub use iothread::*; diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index d0a59ff0a..f89a53b88 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -19,7 +19,7 @@ util = { path = "../../util" } acpi = { path = "../../acpi" } smbios = { path = "../../smbios" } machine = { path = "../../machine" } -virtio = { path = "../../virtio"} +virtio = { path = "../../virtio", features = ["virtio_gpu"] } [features] default = [] diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 79545cc43..75f6fe56f 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -25,6 +25,8 @@ util = { path = "../util" } acpi = { path = "../acpi" } devices = {path = "../devices"} block_backend = {path = "../block_backend"} +ui = { path = "../ui", optional = true } -[target.'cfg(not(target_env = "musl"))'.dependencies] -ui = { path = "../ui" } +[features] +default = [] +virtio_gpu = ["ui", "machine_manager/virtio_gpu"] diff --git a/virtio/src/device/mod.rs b/virtio/src/device/mod.rs index 26eee300a..11ea8ed93 100644 --- a/virtio/src/device/mod.rs +++ b/virtio/src/device/mod.rs @@ -12,7 +12,7 @@ pub mod balloon; pub mod block; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "virtio_gpu")] pub mod gpu; pub mod net; pub mod rng; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 2d0db560c..ce387e8b3 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -34,7 +34,7 @@ mod transport; pub use device::balloon::*; pub use device::block::{Block, BlockState, VirtioBlkConfig}; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "virtio_gpu")] pub use device::gpu::*; pub use device::net::*; pub use device::rng::{Rng, RngState}; -- Gitee From ca88858524fecabf31f68e4fbbbd8a077100686c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 18 Aug 2023 20:33:38 +0800 Subject: [PATCH 1328/2187] build: Add internally feature optional pixman Some features depend on pixman, we should add rustflags `-C link-arg=-lpixman-1` when compiling. Signed-off-by: yezengruan --- build.rs | 23 +++++++++++++++++++++++ devices/Cargo.toml | 4 ++-- ui/Cargo.toml | 6 ++++-- ui/src/lib.rs | 4 ++-- util/Cargo.toml | 1 + util/src/lib.rs | 2 +- virtio/Cargo.toml | 4 ++-- 7 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..7dd9fd12a --- /dev/null +++ b/build.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +fn main() { + if cfg!(any( + feature = "demo_device", + feature = "gtk", + feature = "ramfb", + feature = "virtio_gpu", + feature = "vnc", + )) { + println!("cargo:rustc-link-arg=-lpixman-1"); + } +} diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 6bbae3a19..b543373f6 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -42,8 +42,8 @@ default = [] scream = ["machine_manager/scream"] scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] -demo_device = ["machine_manager/demo_device"] +demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] usb_camera = ["dep:cairo-rs", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] -ramfb = [] +ramfb = ["ui/console", "util/pixman"] diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 7fad0ba37..409278d62 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -25,5 +25,7 @@ machine_manager = { path = "../machine_manager" } util = { path = "../util" } [features] -gtk = ["dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] -vnc = ["dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] +pixman = ["util/pixman"] +console = ["pixman"] +gtk = ["console", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] +vnc = ["console", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 7cf409277..843f6e43e 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -10,13 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "console")] pub mod console; pub mod error; #[cfg(feature = "gtk")] pub mod gtk; pub mod input; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "pixman")] pub mod pixman; pub mod utils; #[cfg(feature = "vnc")] diff --git a/util/Cargo.toml b/util/Cargo.toml index 52a84828c..a0a5b977f 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -26,3 +26,4 @@ nix = "0.26.2" [features] default = [] usb_camera_v4l2 = ["dep:v4l2-sys-mit"] +pixman = [] diff --git a/util/src/lib.rs b/util/src/lib.rs index 6f49ca9c0..3d618d583 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -28,7 +28,7 @@ pub mod logger; pub mod loop_context; pub mod num_ops; pub mod offsetof; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "pixman")] pub mod pixman; pub mod reader; pub mod seccomp; diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 75f6fe56f..8ca391d78 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -25,8 +25,8 @@ util = { path = "../util" } acpi = { path = "../acpi" } devices = {path = "../devices"} block_backend = {path = "../block_backend"} -ui = { path = "../ui", optional = true } +ui = { path = "../ui", features = ["console"], optional = true } [features] default = [] -virtio_gpu = ["ui", "machine_manager/virtio_gpu"] +virtio_gpu = ["ui", "machine_manager/virtio_gpu", "util/pixman"] -- Gitee From 4b93edd0c4fa2b4e5c893a861aa214daaae2d0c3 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 25 Aug 2023 10:28:21 +0800 Subject: [PATCH 1329/2187] build: Add internally feature optional windows_emu_pid Add new feature "windows_emu_pid", currently only gtk will use it. Signed-off-by: yezengruan --- machine/Cargo.toml | 3 ++- machine/src/lib.rs | 8 ++++---- machine/src/standard_vm/aarch64/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 2 +- machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 19 +++++++++++-------- machine_manager/src/config/mod.rs | 2 ++ 8 files changed, 23 insertions(+), 16 deletions(-) diff --git a/machine/Cargo.toml b/machine/Cargo.toml index be27c74d6..f73fea7fb 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -40,7 +40,8 @@ demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] -gtk = ["ui/gtk", "machine_manager/gtk"] +windows_emu_pid = ["ui/console", "machine_manager/windows_emu_pid"] +gtk = ["windows_emu_pid", "ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3936cc1c9..f2f872b6b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -30,12 +30,12 @@ use std::ops::Deref; use std::os::unix::net::UnixListener; use std::path::Path; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "windows_emu_pid")] use std::time::Duration; use anyhow::{anyhow, bail, Context}; use log::warn; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "windows_emu_pid")] use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] @@ -92,7 +92,7 @@ use migration::MigrationManager; use smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use standard_vm::Result as StdResult; -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; use util::file::{clear_file, lock_file, unlock_file}; use util::{ @@ -1938,7 +1938,7 @@ fn coverage_allow_list(syscall_allow_list: &mut Vec) { ]) } -#[cfg(not(target_env = "musl"))] +#[cfg(feature = "windows_emu_pid")] fn check_windows_emu_pid( pid_path: String, powerdown_req: Arc, diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index efa963f1d..54786d101 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -709,7 +709,7 @@ impl MachineOps for StdMachine { .display_init(vm_config) .with_context(|| "Fail to init display")?; - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "windows_emu_pid")] locked_vm.watch_windows_emu_pid( vm_config, locked_vm.power_button.clone(), diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d98ad7012..2ceafd631 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1097,7 +1097,7 @@ impl StdMachine { } /// When windows emu exits, stratovirt should exits too. - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "windows_emu_pid")] fn watch_windows_emu_pid( &self, vm_config: &VmConfig, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 43ed33bf8..25cb57395 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -551,7 +551,7 @@ impl MachineOps for StdMachine { .display_init(vm_config) .with_context(|| "Fail to init display")?; - #[cfg(not(target_env = "musl"))] + #[cfg(feature = "windows_emu_pid")] locked_vm.watch_windows_emu_pid( vm_config, locked_vm.shutdown_req.clone(), diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index c62b9631a..4750ad6e8 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -31,6 +31,7 @@ demo_device = [] usb_host = [] usb_camera = [] usb_camera_v4l2 = ["usb_camera"] +windows_emu_pid = [] gtk = [] vnc = [] ramfb = [] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 524d882ea..f3596fd23 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -445,14 +445,6 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true) .required(false), ) - .arg( - Arg::with_name("windows_emu_pid") - .multiple(false) - .long("windows_emu_pid") - .value_name("pid") - .help("watch on the external windows emu pid") - .takes_value(true), - ) .arg( Arg::with_name("smbios") .multiple(true) @@ -497,6 +489,16 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_value(true), ); + #[cfg(feature = "windows_emu_pid")] + let parser = parser.arg( + Arg::with_name("windows_emu_pid") + .multiple(false) + .long("windows_emu_pid") + .value_name("pid") + .help("watch on the external windows emu pid") + .takes_value(true), + ); + parser } @@ -535,6 +537,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); #[cfg(feature = "gtk")] add_args_to_config!((args.value_of("display")), vm_cfg, add_display); + #[cfg(feature = "windows_emu_pid")] add_args_to_config!( (args.value_of("windows_emu_pid")), vm_cfg, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 3fa5891d2..2303e47af 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -143,6 +143,7 @@ pub struct VmConfig { pub display: Option, #[cfg(feature = "usb_camera")] pub camera_backend: HashMap, + #[cfg(feature = "windows_emu_pid")] pub windows_emu_pid: Option, pub smbios: SmbiosConfig, } @@ -276,6 +277,7 @@ impl VmConfig { /// # Arguments /// /// * `windows_emu_pid` - The args of windows_emu_pid. + #[cfg(feature = "windows_emu_pid")] pub fn add_windows_emu_pid(&mut self, windows_emu_pid: &str) -> Result<()> { if windows_emu_pid.is_empty() { bail!("The arg of windows_emu_pid is empty!"); -- Gitee From 78674773c5f15264a81efd0d3e43254207902e87 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Fri, 25 Aug 2023 13:34:47 +0800 Subject: [PATCH 1330/2187] docs: update the `aio` description in qmp.md update qmp.md for the `aio` sub field in `blockdev_add`'s `file` field. Signed-off-by: Liu Wenyuan --- docs/qmp.md | 3 ++- tests/mod_test/tests/pci_test.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index fbaaf7353..d27cf044a 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -65,6 +65,7 @@ Add a block backend. * `cache` : if use direct io. * `read-only` : if readonly. * `driver` : the block image format. Possible values are `raw` or `qcow2`. If not set, default is `raw`. +* `aio` : the aio type of block device. #### Notes @@ -80,7 +81,7 @@ Add a block backend. #### Example ```json -<- {"execute": "blockdev-add", "arguments": {"node-name": "drive-0", "file": {"driver": "file", "filename": "/path/to/block"}, "cache": {"direct": true}, "read-only": false}} +<- {"execute": "blockdev-add", "arguments": {"node-name": "drive-0", "file": {"driver": "file", "filename": "/path/to/block", "aio": native}, "cache": {"direct": true}, "read-only": false}} -> {"return": {}} ``` diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 28d3bc6fb..5cd2454bc 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -248,7 +248,7 @@ fn build_hotplug_blk_cmd( let add_blk_command = format!( "{{\"execute\": \"blockdev-add\", \ \"arguments\": {{\"node-name\": \"drive-{}\", \"file\": {{\"driver\": \ - \"file\", \"filename\": \"{}\"}, \"aio\": \"native\"}, \ + \"file\", \"filename\": \"{}\", \"aio\": \"native\"}}, \ \"cache\": {{\"direct\": true}}, \"read-only\": false}}}}", hotplug_blk_id, hotplug_image_path ); -- Gitee From cc517a036849b97dcb1b8a81f463e544fceaf50c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 28 Aug 2023 15:32:48 +0800 Subject: [PATCH 1331/2187] virtio-pci: Drop pending intx and msix when device deactivate If we return back to guest with device deactivated and with pending interrupt, guest may hangup. Signed-off-by: Keqian Zhu --- devices/src/pci/msix.rs | 7 +++++++ virtio/src/transport/virtio_pci.rs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index f5e84cf41..44cc02441 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -180,6 +180,13 @@ impl Msix { le_write_u64(&mut self.pba, offset, old_val & pending_bit).unwrap(); } + pub fn clear_pending_vectors(&mut self) { + let max_vector_nr = self.table.len() as u16 / MSIX_TABLE_ENTRY_SIZE; + for v in 0..max_vector_nr { + self.clear_pending_vector(v); + } + } + fn update_irq_routing(&mut self, vector: u16, is_masked: bool) -> Result<()> { let entry = self.get_message(vector); let route = if let Some(route) = self.gsi_msi_routes.get_mut(&vector) { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 8507e5f7c..97864abff 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -510,6 +510,14 @@ impl VirtioPciDevice { } locked_dev.virtio_base_mut().reset(); } + + if let Some(intx) = &self.base.config.intx { + intx.lock().unwrap().reset(); + } + if let Some(msix) = &self.base.config.msix { + msix.lock().unwrap().clear_pending_vectors(); + } + true } -- Gitee From d87ef31f540cc2fd9a479da8e6f1982869dc07a4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 27 Jul 2023 19:02:05 +0800 Subject: [PATCH 1332/2187] GTK: Recalculate the value of scale Recalculate the value of scale after switching resolution. Signed-off-by: Xiao Ye --- ui/src/gtk/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 00e11a7ec..a2178d4ad 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -837,6 +837,7 @@ fn do_update_event(gs: &Rc>, event: DisplayChangeEvent /// Switch display image. fn do_switch_event(gs: &Rc>) -> Result<()> { let mut borrowed_gs = gs.borrow_mut(); + let scale_mode = borrowed_gs.scale_mode.clone(); let active_con = borrowed_gs.con.upgrade(); let con = match active_con { Some(con) => con, @@ -938,6 +939,12 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { image: transfer_image, }); }; + + let (window_width, window_height) = borrowed_gs.get_window_size()?; + if scale_mode.borrow().is_full_screen() || scale_mode.borrow().is_free_scale() { + borrowed_gs.scale_x = window_width / surface_width as f64; + borrowed_gs.scale_y = window_height / surface_height as f64; + } drop(borrowed_gs); if need_resize { -- Gitee From d54bcea93a3cbe3adf8258fe6ca7faf64a23aa0f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 30 Aug 2023 14:36:54 +0800 Subject: [PATCH 1333/2187] virtio-gpu: Fix write_config Should use config_cpy to get events_clear, otherwise the events_read is not clear forever. Signed-off-by: Keqian Zhu --- virtio/src/device/gpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index ffad02f6a..2db11aac5 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1572,7 +1572,7 @@ impl VirtioDevice for Gpu { let config_cpy_slice = config_cpy.as_mut_bytes(); config_cpy_slice[(offset as usize)..(offset as usize + data.len())].copy_from_slice(data); - if config_space.events_clear != 0 { + if config_cpy.events_clear != 0 { config_space.events_read &= !config_cpy.events_clear; } -- Gitee From 1e9b3e1c1ce7eea8662b5870d62ed94c872ddc0a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 31 Aug 2023 17:33:38 +0800 Subject: [PATCH 1334/2187] test: add mod.rs for mod test cases Add mod.rs to control different architecture test cases. Signed-off-by: yezengruan --- tests/mod_test/tests/acpi_test.rs | 2 -- tests/mod_test/tests/ged_test.rs | 2 -- tests/mod_test/tests/mod.rs | 38 ++++++++++++++++++++++++++++++ tests/mod_test/tests/pl031_test.rs | 5 ---- 4 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 tests/mod_test/tests/mod.rs diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index 2c5b59dd0..226deb406 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -416,7 +416,6 @@ fn check_madt_of_two_gicr( ); } -#[cfg(target_arch = "aarch64")] #[test] fn test_acpi_virt() { let mut args = Vec::new(); @@ -467,7 +466,6 @@ fn test_acpi_virt() { test_state.borrow_mut().stop(); } -#[cfg(target_arch = "aarch64")] #[test] fn test_acpi_two_gicr() { let mut args = Vec::new(); diff --git a/tests/mod_test/tests/ged_test.rs b/tests/mod_test/tests/ged_test.rs index 40d850952..fa7b8c13b 100644 --- a/tests/mod_test/tests/ged_test.rs +++ b/tests/mod_test/tests/ged_test.rs @@ -53,7 +53,6 @@ fn set_up() -> TestState { /// 3. Read abnormal address, except 0. /// 4. Write event and read, excepy 0 because ged can't write. #[test] -#[cfg(target_arch = "aarch64")] fn test_shutdown() { let mut ts = set_up(); @@ -79,7 +78,6 @@ fn test_shutdown() { /// 2. Read ged event. /// 3. Send qmp command "query-status" to get the status of vm, except "running". #[test] -#[cfg(target_arch = "aarch64")] fn test_reboot() { let mut ts = set_up(); diff --git a/tests/mod_test/tests/mod.rs b/tests/mod_test/tests/mod.rs new file mode 100644 index 000000000..9ab16c31b --- /dev/null +++ b/tests/mod_test/tests/mod.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(target_arch = "aarch64")] +mod acpi_test; +mod balloon_test; +mod block_test; +mod fwcfg_test; +#[cfg(target_arch = "aarch64")] +mod ged_test; +mod memory_test; +mod net_test; +mod pci_test; +#[cfg(target_arch = "aarch64")] +mod pl031_test; +#[cfg(target_arch = "aarch64")] +mod ramfb_test; +mod rng_test; +mod scream_test; +mod scsi_test; +mod serial_test; +#[cfg(feature = "usb_camera")] +mod usb_camera_test; +mod usb_storage_test; +mod usb_test; +mod virtio_gpu_test; +mod virtio_test; +mod virtiofs_test; +mod vnc_test; diff --git a/tests/mod_test/tests/pl031_test.rs b/tests/mod_test/tests/pl031_test.rs index 2a9557914..06807b1b5 100644 --- a/tests/mod_test/tests/pl031_test.rs +++ b/tests/mod_test/tests/pl031_test.rs @@ -49,7 +49,6 @@ fn set_up() -> TestState { } #[test] -#[cfg(target_arch = "aarch64")] fn check_time() { let mut ts = set_up(); @@ -71,7 +70,6 @@ fn check_time() { } #[test] -#[cfg(target_arch = "aarch64")] fn set_time() { let mut ts = set_up(); let time1 = pl031_read_time(&ts); @@ -89,7 +87,6 @@ fn set_time() { } #[test] -#[cfg(target_arch = "aarch64")] fn rtc_enable() { let mut ts = set_up(); @@ -98,7 +95,6 @@ fn rtc_enable() { } #[test] -#[cfg(target_arch = "aarch64")] fn set_mask() { let mut ts = set_up(); @@ -109,7 +105,6 @@ fn set_mask() { } #[test] -#[cfg(target_arch = "aarch64")] fn reg_fuzz() { let mut ts = set_up(); let mut rng = thread_rng(); -- Gitee From a2bfa93f0f0bd063b5bebd64ea7695cbe6e98a77 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 31 Aug 2023 18:38:04 +0800 Subject: [PATCH 1335/2187] test: remove unused constants Signed-off-by: yezengruan --- tests/mod_test/tests/net_test.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index e61b6281a..8aa088912 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -97,12 +97,6 @@ pub const VIRTIO_NET_CTRL_VLAN_DEL: u8 = 1; pub const VIRTIO_NET_CTRL_MQ: u8 = 4; /// Driver configure the command before enabling virtqueue. pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: u16 = 0; -/// The minimum pairs of multiple queue. -pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN: u16 = 1; -/// The maximum pairs of multiple queue. -pub const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX: u16 = 0x8000; -/// Support more than one virtqueue. -pub const VIRTIO_BLK_F_MQ: u32 = 12; const QUEUE_SIZE_NET: u16 = 256; -- Gitee From 92b2c10bdb6403297ae0775373cfdef5a2da4ac1 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 31 Aug 2023 21:28:12 +0800 Subject: [PATCH 1336/2187] test: decouple the mod test and device code of usb camera Signed-off-by: yezengruan --- devices/src/usb/camera.rs | 10 +++++----- tests/mod_test/Cargo.toml | 4 ---- tests/mod_test/src/libdriver/usb.rs | 15 ++++++--------- tests/mod_test/tests/mod.rs | 1 - tests/mod_test/tests/usb_camera_test.rs | 9 ++++++++- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 1bcf3e556..12dce9df4 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -47,8 +47,8 @@ const UVC_VENDOR_ID: u16 = 0xB74C; // The first 4 chars of "VIDEO", 5 substitutes V. const UVC_PRODUCT_ID: u16 = 0x51DE; -pub const INTERFACE_ID_CONTROL: u8 = 0; -pub const INTERFACE_ID_STREAMING: u8 = 1; +const INTERFACE_ID_CONTROL: u8 = 0; +const INTERFACE_ID_STREAMING: u8 = 1; const TERMINAL_ID_INPUT_TERMINAL: u8 = 1; const TERMINAL_ID_OUTPUT_TERMINAL: u8 = 2; @@ -58,9 +58,9 @@ const VS_INTERFACE_NUM: u8 = 1; // According to UVC specification 1.5 // A.2. Video Interface Subclass Codes -pub const SC_VIDEOCONTROL: u8 = 0x01; -pub const SC_VIDEOSTREAMING: u8 = 0x02; -pub const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; +const SC_VIDEOCONTROL: u8 = 0x01; +const SC_VIDEOSTREAMING: u8 = 0x02; +const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; // A.3. Video Interface Protocol Codes const PC_PROTOCOL_UNDEFINED: u8 = 0x0; // A.4. Video Class-Specific Descriptor Types diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index f89a53b88..e87b29f2a 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -20,7 +20,3 @@ acpi = { path = "../../acpi" } smbios = { path = "../../smbios" } machine = { path = "../../machine" } virtio = { path = "../../virtio", features = ["virtio_gpu"] } - -[features] -default = [] -usb_camera = ["devices/usb_camera"] diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 3ef88694a..160fc94a5 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -29,10 +29,6 @@ use super::{ }; use crate::libdriver::pci::{PciMsixOps, PCI_DEVICE_ID}; use crate::libtest::{test_init, TestState}; -#[cfg(feature = "usb_camera")] -use devices::usb::camera::{ - INTERFACE_ID_CONTROL, SC_VIDEOCONTROL, SC_VIDEOSTREAMING, SC_VIDEO_INTERFACE_COLLECTION, -}; use devices::usb::{ config::*, hid::{ @@ -139,6 +135,12 @@ pub const STORAGE_DEVICE_OUT_ENDPOINT_ID: u32 = 4; pub const PRIMARY_INTERRUPTER_ID: usize = 0; pub const XHCI_PCI_SLOT_NUM: u8 = 0x5; pub const XHCI_PCI_FUN_NUM: u8 = 0; +const INTERFACE_ID_CONTROL: u8 = 0; +// According to UVC specification 1.5 +// A.2. Video Interface Subclass Codes +const SC_VIDEOCONTROL: u8 = 0x01; +const SC_VIDEOSTREAMING: u8 = 0x02; +const SC_VIDEO_INTERFACE_COLLECTION: u8 = 0x03; #[derive(Eq, PartialEq)] enum UsbDeviceType { @@ -1613,7 +1615,6 @@ impl TestXhciPciDevice { // class assert_eq!(buf[4], USB_CLASS_VIDEO); // subclass - #[cfg(feature = "usb_camera")] assert_eq!(buf[5], SC_VIDEO_INTERFACE_COLLECTION); // 2. VC interface @@ -1624,12 +1625,9 @@ impl TestXhciPciDevice { *offset, ); - #[cfg(feature = "usb_camera")] assert_eq!(buf[1], USB_DT_INTERFACE); - #[cfg(feature = "usb_camera")] assert_eq!(buf[2], INTERFACE_ID_CONTROL); assert_eq!(buf[5], USB_CLASS_VIDEO); - #[cfg(feature = "usb_camera")] assert_eq!(buf[6], SC_VIDEOCONTROL); // get total vc length from its header descriptor @@ -1652,7 +1650,6 @@ impl TestXhciPciDevice { assert_eq!(buf[1], USB_DT_INTERFACE); assert_eq!(buf[5], USB_CLASS_VIDEO); - #[cfg(feature = "usb_camera")] assert_eq!(buf[6], SC_VIDEOSTREAMING); // get total vs length from its header descriptor diff --git a/tests/mod_test/tests/mod.rs b/tests/mod_test/tests/mod.rs index 9ab16c31b..313e82c98 100644 --- a/tests/mod_test/tests/mod.rs +++ b/tests/mod_test/tests/mod.rs @@ -28,7 +28,6 @@ mod rng_test; mod scream_test; mod scsi_test; mod serial_test; -#[cfg(feature = "usb_camera")] mod usb_camera_test; mod usb_storage_test; mod usb_test; diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index cbfeea320..756d48d48 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -17,7 +17,6 @@ use std::{fs::remove_file, fs::File, io::Write}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use devices::camera_backend::FmtType; use devices::usb::xhci::TRBCCode; use mod_test::libdriver::usb::{ TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, @@ -28,6 +27,14 @@ const UVC_FID: u8 = 1; const UVC_HEADER_LEN: u8 = 2; const VS_ENDPOINT_ID: u32 = 3; +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Default)] +enum FmtType { + #[default] + Yuy2 = 0, + Rgb565, + Mjpg, +} + #[derive(Debug, Clone, Serialize, Deserialize)] struct DeviceConfig { check_interval: u64, -- Gitee From 488ea7338e86c8bf5874a358aa6f5693eaf21bb1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 2 Sep 2023 00:11:52 +0800 Subject: [PATCH 1337/2187] chardev: Factor out chardev into chardev_backend Signed-off-by: Keqian Zhu --- Cargo.lock | 14 ++++++++++++++ chardev_backend/Cargo.toml | 14 ++++++++++++++ .../src/legacy => chardev_backend/src}/chardev.rs | 0 chardev_backend/src/lib.rs | 13 +++++++++++++ devices/Cargo.toml | 1 + devices/src/legacy/mod.rs | 2 -- devices/src/legacy/pl011.rs | 2 +- devices/src/legacy/serial.rs | 2 +- virtio/Cargo.toml | 1 + virtio/src/device/serial.rs | 2 +- 10 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 chardev_backend/Cargo.toml rename {devices/src/legacy => chardev_backend/src}/chardev.rs (100%) create mode 100644 chardev_backend/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 67758cb64..d19147975 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -259,6 +259,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chardev_backend" +version = "2.2.0" +dependencies = [ + "anyhow", + "libc", + "log", + "machine_manager", + "util", + "vmm-sys-util", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -328,6 +340,7 @@ dependencies = [ "block_backend", "byteorder", "cairo-rs", + "chardev_backend", "drm-fourcc", "hypervisor", "kvm-bindings", @@ -1823,6 +1836,7 @@ dependencies = [ "anyhow", "block_backend", "byteorder", + "chardev_backend", "devices", "hypervisor", "kvm-ioctls", diff --git a/chardev_backend/Cargo.toml b/chardev_backend/Cargo.toml new file mode 100644 index 000000000..02433d30b --- /dev/null +++ b/chardev_backend/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "chardev_backend" +version = "2.2.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" + +[dependencies] +vmm-sys-util = "0.11.0" +anyhow = "1.0" +log = "0.4" +libc = "0.2" +machine_manager = { path = "../machine_manager" } +util = { path = "../util" } diff --git a/devices/src/legacy/chardev.rs b/chardev_backend/src/chardev.rs similarity index 100% rename from devices/src/legacy/chardev.rs rename to chardev_backend/src/chardev.rs diff --git a/chardev_backend/src/lib.rs b/chardev_backend/src/lib.rs new file mode 100644 index 000000000..961218000 --- /dev/null +++ b/chardev_backend/src/lib.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod chardev; diff --git a/devices/Cargo.toml b/devices/Cargo.toml index b543373f6..347e9f6f1 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -29,6 +29,7 @@ migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } acpi = { path = "../acpi" } block_backend = { path = "../block_backend"} +chardev_backend = { path = "../chardev_backend" } ui = { path = "../ui" } pulse = { version = "2.27", package = "libpulse-binding", optional = true } psimple = { version = "2.27", package = "libpulse-simple-binding", optional = true } diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index cee969f52..b6aa7da41 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -27,7 +27,6 @@ pub mod error; -mod chardev; mod fwcfg; mod pflash; #[cfg(target_arch = "aarch64")] @@ -44,7 +43,6 @@ pub use anyhow::Result; #[cfg(target_arch = "x86_64")] pub use self::rtc::{RTC, RTC_PORT_INDEX}; -pub use chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; pub use error::LegacyError; #[cfg(target_arch = "x86_64")] pub use fwcfg::FwCfgIO; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 16b853e8f..fd1d73234 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -16,7 +16,6 @@ use anyhow::{Context, Result}; use log::{debug, error}; use vmm_sys_util::eventfd::EventFd; -use super::chardev::{Chardev, InputReceiver}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; @@ -26,6 +25,7 @@ use acpi::{ AmlScopeBuilder, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::GuestAddress; +use chardev_backend::chardev::{Chardev, InputReceiver}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 82c1369f7..cffcb5ad8 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -17,7 +17,6 @@ use anyhow::{bail, Context, Result}; use log::{debug, error}; use vmm_sys_util::eventfd::EventFd; -use super::chardev::{Chardev, InputReceiver}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; @@ -27,6 +26,7 @@ use acpi::{ AmlResourceUsage, AmlScopeBuilder, }; use address_space::GuestAddress; +use chardev_backend::chardev::{Chardev, InputReceiver}; use hypervisor::kvm::KVM_FDS; #[cfg(target_arch = "aarch64")] use machine_manager::config::{BootSource, Param}; diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 8ca391d78..5b77892fc 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -25,6 +25,7 @@ util = { path = "../util" } acpi = { path = "../acpi" } devices = {path = "../devices"} block_backend = {path = "../block_backend"} +chardev_backend = {path = "../chardev_backend" } ui = { path = "../ui", features = ["console"], optional = true } [features] diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 28c6ed6d8..890d082b0 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -30,7 +30,7 @@ use crate::{ VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; -use devices::legacy::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; +use chardev_backend::chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; use machine_manager::{ config::{ChardevType, VirtioSerialInfo, VirtioSerialPort, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, -- Gitee From c4f348af91e2f6ffb7b0643bd5334e5ad4f212b6 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 2 Sep 2023 13:47:56 +0800 Subject: [PATCH 1338/2187] chardev: Some minor refactory Use "receiver" abstraction in chardev. Signed-off-by: Keqian Zhu --- chardev_backend/src/chardev.rs | 97 ++++++++++++++-------------------- devices/src/legacy/pl011.rs | 8 +-- devices/src/legacy/serial.rs | 8 +-- virtio/src/device/serial.rs | 6 +-- 4 files changed, 50 insertions(+), 69 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index b7a728078..1101234ed 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -37,9 +37,10 @@ use util::unix::limit_permission; /// Provide the trait that helps handle the input data. pub trait InputReceiver: Send { - fn input_handle(&mut self, buffer: &[u8]); - - fn get_remain_space_size(&mut self) -> usize; + /// Handle the input data and trigger interrupt if necessary. + fn receive(&mut self, buffer: &[u8]); + /// Return the remain space size of receiver buffer. + fn remain_size(&mut self) -> usize; } /// Provide the trait that notifies device the socket is opened or closed. @@ -52,8 +53,6 @@ pub enum ChardevStatus { Open, } -type ReceFn = Option>; - /// Character device structure. pub struct Chardev { /// Id of chardev. @@ -70,10 +69,8 @@ pub struct Chardev { stream_fd: Option, /// Device is deactivated or not. pub deactivated: bool, - /// Handle the input data and trigger interrupt if necessary. - receive: ReceFn, - /// Return the remain space size of receiver buffer. - get_remain_space_size: Option usize + Send + Sync>>, + /// Input receiver. + receiver: Option>>, /// Used to notify device the socket is opened or closed. dev: Option>>, } @@ -88,8 +85,7 @@ impl Chardev { output: None, stream_fd: None, deactivated: false, - receive: None, - get_remain_space_size: None, + receiver: None, dev: None, } } @@ -153,15 +149,8 @@ impl Chardev { Ok(()) } - pub fn set_input_callback(&mut self, dev: &Arc>) { - let cloned_dev = dev.clone(); - self.receive = Some(Arc::new(move |data: &[u8]| { - cloned_dev.lock().unwrap().input_handle(data) - })); - let cloned_dev = dev.clone(); - self.get_remain_space_size = Some(Arc::new(move || { - cloned_dev.lock().unwrap().get_remain_space_size() - })); + pub fn set_receiver(&mut self, dev: &Arc>) { + self.receiver = Some(dev.clone()); } pub fn set_device(&mut self, dev: Arc>) { @@ -235,37 +224,30 @@ fn get_notifier_handler( match backend { ChardevType::Stdio | ChardevType::Pty => Rc::new(move |_, _| { let locked_chardev = chardev.lock().unwrap(); - let get_remain_space_size = locked_chardev - .get_remain_space_size - .as_ref() - .unwrap() - .clone(); - drop(locked_chardev); - let buff_size = get_remain_space_size(); - let locked_chardev = chardev.lock().unwrap(); - if locked_chardev.deactivated { + if locked_chardev.receiver.is_none() || locked_chardev.deactivated { + error!("Failed to get chardev receiver"); return None; } - let mut buffer = vec![0_u8; buff_size]; - let input_h = locked_chardev.input.clone(); - let receive = locked_chardev.receive.clone(); + if locked_chardev.input.is_none() { + error!("Failed to get chardev input fd"); + return None; + } + let receiver = locked_chardev.receiver.clone().unwrap(); + let input = locked_chardev.input.clone().unwrap(); drop(locked_chardev); - if let Some(input) = input_h { - if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { - receive.as_ref().unwrap()(&mut buffer[..index]); - } else { - error!("Failed to read input data"); - } + + let mut locked_receiver = receiver.lock().unwrap(); + let buff_size = locked_receiver.remain_size(); + let mut buffer = vec![0_u8; buff_size]; + if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { + locked_receiver.receive(&buffer[..index]); } else { - error!("Failed to get chardev input fd"); + error!("Failed to read input data"); } None }), ChardevType::Socket { .. } => Rc::new(move |_, _| { let mut locked_chardev = chardev.lock().unwrap(); - if locked_chardev.deactivated { - return None; - } let (stream, _) = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); let stream_fd = stream.as_raw_fd(); @@ -282,26 +264,25 @@ fn get_notifier_handler( let inner_handler: Rc = Rc::new(move |event, _| { let mut locked_chardev = cloned_chardev.lock().unwrap(); if event == EventSet::IN { - let get_remain_space_size = locked_chardev - .get_remain_space_size - .as_ref() - .unwrap() - .clone(); - drop(locked_chardev); - let buff_size = get_remain_space_size(); - let locked_chardev = cloned_chardev.lock().unwrap(); - if locked_chardev.deactivated { + if locked_chardev.receiver.is_none() || locked_chardev.deactivated { + error!("Failed to get chardev receiver"); return None; } + if locked_chardev.input.is_none() { + error!("Failed to get chardev input fd"); + return None; + } + let receiver = locked_chardev.receiver.clone().unwrap(); + let input = locked_chardev.input.clone().unwrap(); + drop(locked_chardev); + + let mut locked_receiver = receiver.lock().unwrap(); + let buff_size = locked_receiver.remain_size(); let mut buffer = vec![0_u8; buff_size]; - if let Some(input) = locked_chardev.input.clone() { - if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { - locked_chardev.receive.as_ref().unwrap()(&mut buffer[..index]); - } else { - error!("Failed to read input data"); - } + if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { + locked_receiver.receive(&buffer[..index]); } else { - error!("Failed to get chardev input fd"); + error!("Failed to read input data"); } None } else if event & EventSet::HANG_UP == EventSet::HANG_UP { diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index fd1d73234..d7d529f38 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -186,7 +186,7 @@ impl PL011 { PL011_SNAPSHOT_ID, ); let locked_dev = dev.lock().unwrap(); - locked_dev.chardev.lock().unwrap().set_input_callback(&dev); + locked_dev.chardev.lock().unwrap().set_receiver(&dev); EventLoop::update_event( EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, @@ -197,7 +197,7 @@ impl PL011 { } impl InputReceiver for PL011 { - fn input_handle(&mut self, data: &[u8]) { + fn receive(&mut self, data: &[u8]) { self.state.flags &= !PL011_FLAG_RXFE as u32; for val in data { let mut slot = (self.state.read_pos + self.state.read_count) as usize; @@ -218,7 +218,7 @@ impl InputReceiver for PL011 { } } - fn get_remain_space_size(&mut self) -> usize { + fn remain_size(&mut self) -> usize { PL011_FIFO_SIZE - self.state.read_count as usize } } @@ -492,7 +492,7 @@ mod test { assert_eq!(pl011_dev.state.int_enabled, 0); let data = vec![0x12, 0x34, 0x56, 0x78, 0x90]; - pl011_dev.input_handle(&data); + pl011_dev.receive(&data); assert_eq!(pl011_dev.state.read_count, data.len() as u32); for i in 0..data.len() { assert_eq!(pl011_dev.state.rfifo[i], data[i] as u32); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index cffcb5ad8..a5d27ce69 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -163,7 +163,7 @@ impl Serial { value: format!("uart,mmio,0x{:08x}", region_base), }); let locked_dev = dev.lock().unwrap(); - locked_dev.chardev.lock().unwrap().set_input_callback(&dev); + locked_dev.chardev.lock().unwrap().set_receiver(&dev); EventLoop::update_event( EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, @@ -342,7 +342,7 @@ impl Serial { } impl InputReceiver for Serial { - fn input_handle(&mut self, data: &[u8]) { + fn receive(&mut self, data: &[u8]) { if self.state.mcr & UART_MCR_LOOP == 0 { let len = self.rbr.len(); if len >= RECEIVER_BUFF_SIZE { @@ -359,7 +359,7 @@ impl InputReceiver for Serial { } } - fn get_remain_space_size(&mut self) -> usize { + fn remain_size(&mut self) -> usize { RECEIVER_BUFF_SIZE } } @@ -496,7 +496,7 @@ mod test { // test receive method let data = [0x01, 0x02]; - usart.input_handle(&data); + usart.receive(&data); assert_eq!(usart.rbr.is_empty(), false); assert_eq!(usart.rbr.len(), 2); assert_eq!(usart.rbr.front(), Some(&0x01)); diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 890d082b0..888d48650 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -382,7 +382,7 @@ impl SerialPort { } fn activate(&mut self, handler: &Arc>) { - self.chardev.lock().unwrap().set_input_callback(handler); + self.chardev.lock().unwrap().set_receiver(handler); self.chardev.lock().unwrap().deactivated = false; } @@ -611,7 +611,7 @@ impl EventNotifierHelper for SerialPortHandler { } impl InputReceiver for SerialPortHandler { - fn input_handle(&mut self, buffer: &[u8]) { + fn receive(&mut self, buffer: &[u8]) { self.input_handle_internal(buffer).unwrap_or_else(|e| { error!("Port handle input error: {:?}", e); report_virtio_error( @@ -622,7 +622,7 @@ impl InputReceiver for SerialPortHandler { }); } - fn get_remain_space_size(&mut self) -> usize { + fn remain_size(&mut self) -> usize { BUF_SIZE } } -- Gitee From 8a43c96b37d17b5a360d0b6f9bb30a7a8f8b1391 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 2 Sep 2023 15:01:28 +0800 Subject: [PATCH 1339/2187] virtio-serial: Fix handling device deactivation Put deactivation flag in chardev is unsafe. As we drop chardev lock before handling IO, the deactivation status may has changed before handling IO. Signed-off-by: Keqian Zhu --- chardev_backend/src/chardev.rs | 13 ++++++++----- virtio/src/device/serial.rs | 15 +++++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 1101234ed..0bfdaf89f 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -67,8 +67,6 @@ pub struct Chardev { pub output: Option>>, /// Fd of socket stream. stream_fd: Option, - /// Device is deactivated or not. - pub deactivated: bool, /// Input receiver. receiver: Option>>, /// Used to notify device the socket is opened or closed. @@ -84,7 +82,6 @@ impl Chardev { input: None, output: None, stream_fd: None, - deactivated: false, receiver: None, dev: None, } @@ -224,7 +221,7 @@ fn get_notifier_handler( match backend { ChardevType::Stdio | ChardevType::Pty => Rc::new(move |_, _| { let locked_chardev = chardev.lock().unwrap(); - if locked_chardev.receiver.is_none() || locked_chardev.deactivated { + if locked_chardev.receiver.is_none() { error!("Failed to get chardev receiver"); return None; } @@ -238,6 +235,9 @@ fn get_notifier_handler( let mut locked_receiver = receiver.lock().unwrap(); let buff_size = locked_receiver.remain_size(); + if buff_size == 0 { + return None; + } let mut buffer = vec![0_u8; buff_size]; if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { locked_receiver.receive(&buffer[..index]); @@ -264,7 +264,7 @@ fn get_notifier_handler( let inner_handler: Rc = Rc::new(move |event, _| { let mut locked_chardev = cloned_chardev.lock().unwrap(); if event == EventSet::IN { - if locked_chardev.receiver.is_none() || locked_chardev.deactivated { + if locked_chardev.receiver.is_none() { error!("Failed to get chardev receiver"); return None; } @@ -278,6 +278,9 @@ fn get_notifier_handler( let mut locked_receiver = receiver.lock().unwrap(); let buff_size = locked_receiver.remain_size(); + if buff_size == 0 { + return None; + } let mut buffer = vec![0_u8; buff_size]; if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { locked_receiver.receive(&buffer[..index]); diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 888d48650..83bfe9ac0 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -342,6 +342,8 @@ pub struct SerialPort { nr: u32, /// Whether the port is a console port. pub is_console: bool, + /// Whether the guest activate the serial device. + device_activated: bool, /// Whether the guest open the serial port. guest_connected: bool, /// Whether the host open the serial socket. @@ -361,6 +363,7 @@ impl SerialPort { chardev: Arc::new(Mutex::new(Chardev::new(port_cfg.chardev))), nr: port_cfg.nr, is_console: port_cfg.is_console, + device_activated: false, guest_connected: false, host_connected, ctrl_handler: None, @@ -373,7 +376,6 @@ impl SerialPort { .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; - self.chardev.lock().unwrap().deactivated = true; EventLoop::update_event( EventNotifierHelper::internal_notifiers(self.chardev.clone()), None, @@ -383,11 +385,11 @@ impl SerialPort { fn activate(&mut self, handler: &Arc>) { self.chardev.lock().unwrap().set_receiver(handler); - self.chardev.lock().unwrap().deactivated = false; + self.device_activated = true; } fn deactivate(&mut self) { - self.chardev.lock().unwrap().deactivated = true; + self.device_activated = false; self.guest_connected = false; } } @@ -623,7 +625,12 @@ impl InputReceiver for SerialPortHandler { } fn remain_size(&mut self) -> usize { - BUF_SIZE + if let Some(port) = &self.port { + if port.lock().unwrap().device_activated { + return BUF_SIZE; + } + } + 0 } } -- Gitee From 8805f27be5780f37a6359f4d1994ae230d18cd0d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 01:34:56 +0800 Subject: [PATCH 1340/2187] refactory/qmp: Split qmp into multi mod Split qmp into channel, response, schema and scoket. Signed-off-by: Keqian Zhu --- cpu/src/lib.rs | 2 +- devices/src/acpi/ged.rs | 2 +- devices/src/pci/root_port.rs | 2 +- devices/src/usb/mod.rs | 2 +- machine/src/micro_vm/mod.rs | 26 +- machine/src/standard_vm/aarch64/mod.rs | 2 +- machine/src/standard_vm/mod.rs | 2 +- machine/src/standard_vm/x86_64/mod.rs | 2 +- machine_manager/src/config/network.rs | 2 +- machine_manager/src/machine.rs | 2 +- machine_manager/src/qmp/mod.rs | 795 +----------------------- machine_manager/src/qmp/qmp_channel.rs | 193 ++++++ machine_manager/src/qmp/qmp_response.rs | 280 +++++++++ machine_manager/src/qmp/qmp_schema.rs | 27 +- machine_manager/src/qmp/qmp_socket.rs | 637 +++++++++++++++++++ machine_manager/src/signal_handler.rs | 2 +- machine_manager/src/socket.rs | 291 +-------- migration/src/lib.rs | 2 +- src/main.rs | 4 +- virtio/src/device/balloon.rs | 2 +- 20 files changed, 1166 insertions(+), 1111 deletions(-) create mode 100644 machine_manager/src/qmp/qmp_channel.rs create mode 100644 machine_manager/src/qmp/qmp_response.rs create mode 100644 machine_manager/src/qmp/qmp_socket.rs diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index fa34a7828..ff6834f3c 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -72,7 +72,7 @@ use vmm_sys_util::signal::{register_signal_handler, Killable}; use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; use machine_manager::machine::MachineInterface; -use machine_manager::{qmp::qmp_schema, qmp::QmpChannel}; +use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; #[cfg(not(test))] use util::test_helper::is_test_enabled; #[cfg(target_arch = "x86_64")] diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 76527b844..d22701cdd 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -32,7 +32,7 @@ use acpi::{ use address_space::GuestAddress; use machine_manager::event; use machine_manager::event_loop::EventLoop; -use machine_manager::qmp::QmpChannel; +use machine_manager::qmp::qmp_channel::QmpChannel; use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 71be40fd8..055ca1537 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -39,7 +39,7 @@ use crate::pci::{ }; use crate::{Device, DeviceBase}; use address_space::Region; -use machine_manager::qmp::send_device_deleted_msg; +use machine_manager::qmp::qmp_channel::send_device_deleted_msg; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index c9039ba71..195c03e83 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -40,7 +40,7 @@ use self::descriptor::USB_MAX_INTERFACES; use crate::{Device, DeviceBase}; use config::*; use descriptor::{UsbDescriptor, UsbDescriptorOps}; -use machine_manager::qmp::send_device_deleted_msg; +use machine_manager::qmp::qmp_channel::send_device_deleted_msg; use util::aio::{mem_from_buf, mem_to_buf, Iovec}; use util::byte_code::ByteCode; use xhci::xhci_controller::{UsbPort, XhciDevice}; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index d6f9df0a3..1107f6bb4 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -71,21 +71,19 @@ use devices::sysbus::{SysBusDevType, SysRes}; use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; #[cfg(target_arch = "x86_64")] use hypervisor::kvm::KVM_FDS; -use machine_manager::config::DiskFormat; +use machine_manager::config::{ + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DiskFormat, + DriveFile, Incoming, MigrateMode, NetworkInterfaceConfig, NumaNodes, SerialConfig, VmConfig, + DEFAULT_VIRTQUEUE_SIZE, +}; +use machine_manager::event; use machine_manager::event_loop::EventLoop; -use machine_manager::qmp::qmp_schema::UpdateRegionArgument; -use machine_manager::{ - config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DriveFile, - Incoming, MigrateMode, NetworkInterfaceConfig, NumaNodes, SerialConfig, VmConfig, - DEFAULT_VIRTQUEUE_SIZE, - }, - event, - machine::{ - DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, - MachineInterface, MachineLifecycle, MigrateInterface, - }, - qmp::{qmp_schema, QmpChannel, Response}, +use machine_manager::machine::{ + DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, + MachineInterface, MachineLifecycle, MigrateInterface, +}; +use machine_manager::qmp::{ + qmp_channel::QmpChannel, qmp_response::Response, qmp_schema, qmp_schema::UpdateRegionArgument, }; use mem_layout::{LayoutEntryType, MEM_LAYOUT}; use migration::{MigrationManager, MigrationStatus}; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 54786d101..e67e88723 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -70,7 +70,7 @@ use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, }; -use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; +use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; use pci_host_root::PciHostRoot; use syscall::syscall_whitelist; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 2ceafd631..006c10f7d 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -69,7 +69,7 @@ use machine_manager::event_loop::EventLoop; use machine_manager::machine::MachineLifecycle; use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; -use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; +use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::MigrationManager; use ui::input::{key_event, point_event}; #[cfg(feature = "vnc")] diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 25cb57395..5c9cbce3d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -58,7 +58,7 @@ use machine_manager::machine::{ KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, }; -use machine_manager::qmp::{qmp_schema, QmpChannel, Response}; +use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use mch::Mch; use migration::{MigrationManager, MigrationStatus}; use syscall::syscall_whitelist; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 1c48af5a7..eba1e2c4f 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -21,7 +21,7 @@ use crate::config::{ check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_VIRTIO_QUEUE, }; -use crate::qmp::{qmp_schema, QmpChannel}; +use crate::qmp::{qmp_channel::QmpChannel, qmp_schema}; const MAC_ADDRESS_LENGTH: usize = 17; diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 0244f3609..a6ab65055 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -17,6 +17,7 @@ use once_cell::sync::Lazy; use strum::VariantNames; use crate::config::ShutdownAction; +use crate::qmp::qmp_response::{Response, Version}; use crate::qmp::qmp_schema::{ BlockDevAddArgument, BlockdevSnapshotInternalArgument, CameraDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, CmdParameter, DeviceAddArgument, DeviceProps, @@ -24,7 +25,6 @@ use crate::qmp::qmp_schema::{ MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, Target, TypeLists, UpdateRegionArgument, }; -use crate::qmp::{Response, Version}; #[derive(Clone)] pub struct PathInfo { diff --git a/machine_manager/src/qmp/mod.rs b/machine_manager/src/qmp/mod.rs index b6414ff73..53f87e252 100644 --- a/machine_manager/src/qmp/mod.rs +++ b/machine_manager/src/qmp/mod.rs @@ -26,799 +26,10 @@ //! `qmp-schema.json`. It's can be compatible by Qemu's zoology. Those //! transformed structures can be found in `machine_manager/src/qmp/qmp_schema.rs` +pub mod qmp_channel; +pub mod qmp_response; #[allow(non_upper_case_globals)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] pub mod qmp_schema; - -use std::collections::BTreeMap; -use std::io::Write; -use std::os::unix::io::RawFd; -use std::sync::{Arc, Mutex, RwLock}; -use std::time::{SystemTime, UNIX_EPOCH}; - -use anyhow::{Context, Result}; -use log::{error, info, warn}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -use self::qmp_schema::{self as schema, QmpCommand}; -use crate::event_loop::EventLoop; -use crate::machine::MachineExternalInterface; -use crate::socket::SocketRWHandler; -use crate::temp_cleaner::TempCleaner; -use util::leak_bucket::LeakBucket; -use util::set_termi_canon_mode; -use util::time::NANOSECONDS_PER_SECOND; - -static mut QMP_CHANNEL: Option> = None; - -/// Macro `event!`: send event to qmp-client. -/// -/// # Arguments -/// -/// * `$x` - event type -/// * `$y` - event context -/// -/// # Example -/// -/// ```text -/// #[macro_use] -/// use machine_manager::qmp::*; -/// -/// event!(Shutdown; shutdown_msg); -/// event!(Stop); -/// event!(Resume); -/// ``` -#[macro_export] -macro_rules! event { - ( $x:tt ) => {{ - QmpChannel::send_event(&$crate::qmp::qmp_schema::QmpEvent::$x { - data: Default::default(), - timestamp: $crate::qmp::create_timestamp(), - }); - }}; - ( $x:tt;$y:expr ) => {{ - QmpChannel::send_event(&$crate::qmp::qmp_schema::QmpEvent::$x { - data: $y, - timestamp: $crate::qmp::create_timestamp(), - }); - }}; -} - -/// Macro `create_command_matches!`: Generate a match statement for qmp_command -/// , which is combined with its handle func. -/// -/// # Arguments -/// -/// `cmd_type_1` - The qmp command with no arguments. -/// `cmd_type_2` - The qmp command with arguments. -macro_rules! create_command_matches { - ( $command:expr; $executor:expr; $ret:expr; - $(($cmd_type_1:tt, $func_1:tt)),*; - $(($cmd_type_2:tt, $func_2:tt, $($arg:tt),*)),*; - $(($cmd_type_3:tt, $func_3:tt)),* - ) => { - match $command { - $( - $crate::qmp::qmp_schema::QmpCommand::$cmd_type_1{ id, ..} => { - qmp_command_match!($func_1, $executor, $ret); - id - }, - )* - $( - $crate::qmp::qmp_schema::QmpCommand::$cmd_type_2{ arguments, id } => { - qmp_command_match!($func_2, $executor, arguments, $ret, $($arg),*); - id - }, - )* - $( - $crate::qmp::qmp_schema::QmpCommand::$cmd_type_3{ arguments, id } => { - qmp_command_match_with_argument!($func_3, $executor, arguments, $ret); - id - }, - )* - _ => None, - } - }; -} - -/// Macro: to execute handle func with every arguments. -macro_rules! qmp_command_match { - ( $func:tt, $executor:expr, $ret:expr ) => { - $ret = $executor.$func().into(); - }; - ( $func:tt, $executor:expr, $cmd:expr, $ret:expr, $($arg:tt),* ) => { - $ret = $executor.$func( - $($cmd.$arg),* - ).into(); - }; -} - -/// Macro: to execute handle func with all arguments. -macro_rules! qmp_command_match_with_argument { - ( $func:tt, $executor:expr, $cmd:expr, $ret:expr ) => { - $ret = $executor.$func($cmd).into(); - }; -} - -/// Qmp greeting message. -/// -/// # Notes -/// -/// It contains the version of VM or fake Qemu version to adapt others. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -pub struct QmpGreeting { - #[serde(rename = "QMP")] - qmp: Greeting, -} - -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -struct Greeting { - version: Version, - capabilities: Vec, -} - -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -pub struct Version { - #[serde(rename = "qemu")] - application: VersionNumber, - package: String, -} - -impl Version { - pub fn new(micro: u8, minor: u8, major: u8) -> Self { - let version_number = VersionNumber { - micro, - minor, - major, - }; - Version { - application: version_number, - package: "StratoVirt-".to_string() + env!("CARGO_PKG_VERSION"), - } - } -} - -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -struct VersionNumber { - micro: u8, - minor: u8, - major: u8, -} - -impl QmpGreeting { - /// Create qmp greeting message. - /// - /// # Arguments - /// - /// * `micro` - Micro version number. - /// * `minor` - Minor version number. - /// * `major` - Major version number. - pub fn create_greeting(micro: u8, minor: u8, major: u8) -> Self { - let version = Version::new(micro, minor, major); - let cap: Vec = Default::default(); - let greeting = Greeting { - version, - capabilities: cap, - }; - QmpGreeting { qmp: greeting } - } -} - -/// Qmp response to client -/// -/// # Notes -/// -/// It contains two kind response: `BadResponse` and `GoodResponse`. This two -/// kind response are fit by executing qmp command by success and failure. -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct Response { - #[serde(rename = "return", default, skip_serializing_if = "Option::is_none")] - return_: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - error: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, -} - -impl Response { - /// Create qmp response with inner `Value` and `id`. - /// - /// # Arguments - /// - /// * `v` - The `Value` of qmp `return` field. - /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s `id`. - pub fn create_response(v: Value, id: Option) -> Self { - Response { - return_: Some(v), - error: None, - id, - } - } - - /// Create a empty qmp response, `return` field will be empty. - pub fn create_empty_response() -> Self { - Response { - return_: Some(serde_json::to_value(Empty {}).unwrap()), - error: None, - id: None, - } - } - - /// Create a error qmo response with `err_class` and `id`. - /// # Arguments - /// - /// * `err_class` - The `QmpErrorClass` of qmp `error` field. - /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s `id`. - pub fn create_error_response(err_class: schema::QmpErrorClass, id: Option) -> Self { - Response { - return_: None, - error: Some(ErrorMessage::new(&err_class)), - id, - } - } - - fn change_id(&mut self, id: Option) { - self.id = id; - } -} - -impl From for Response { - fn from(value: bool) -> Self { - if value { - Response::create_empty_response() - } else { - Response::create_error_response( - schema::QmpErrorClass::GenericError(String::new()), - None, - ) - } - } -} - -/// `ErrorMessage` for Qmp Response. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct ErrorMessage { - #[serde(rename = "class")] - errorkind: String, - desc: String, -} - -impl ErrorMessage { - fn new(e: &schema::QmpErrorClass) -> Self { - let content = e.to_content(); - let serde_str = serde_json::to_string(&e).unwrap(); - let serde_vec: Vec<&str> = serde_str.split(':').collect(); - let class_name = serde_vec[0]; - let len: usize = class_name.len(); - ErrorMessage { - errorkind: class_name[2..len - 1].to_string(), - desc: content, - } - } -} - -/// Empty message for QMP. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct Empty {} - -/// Command trait for Deserialize and find back Response. -pub trait Command: Serialize { - type Res: DeserializeOwned; - fn back(self) -> Self::Res; -} - -/// `TimeStamp` structure for `QmpEvent`. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct TimeStamp { - seconds: u64, - microseconds: u64, -} - -/// Constructs a `TimeStamp` struct. -pub fn create_timestamp() -> TimeStamp { - let start = SystemTime::now(); - let since_the_epoch = start - .duration_since(UNIX_EPOCH) - .expect("Time went backwards"); - let seconds = u128::from(since_the_epoch.as_secs()); - let microseconds = - (since_the_epoch.as_nanos() - seconds * (NANOSECONDS_PER_SECOND as u128)) / (1_000_u128); - TimeStamp { - seconds: seconds as u64, - microseconds: microseconds as u64, - } -} - -/// Accept qmp command, analyze and exec it. -/// -/// # Arguments -/// -/// * `stream_fd` - The input stream file description. -/// * `controller` - The controller which execute actual qmp command. -/// * `leak_bucket` - The LeakBucket flow controller for qmp command. -/// -/// # Errors -/// -/// This function will fail when json parser failed or socket file description broke. -pub fn handle_qmp( - stream_fd: RawFd, - controller: &Arc>, - leak_bucket: &mut LeakBucket, -) -> Result<()> { - let mut qmp_service = crate::socket::SocketHandler::new(stream_fd); - - // If flow over `LEAK_BUCKET_LIMIT` per seconds, discard the request and return - // a `OperationThrottled` error. - if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), 1_u64) { - qmp_service.discard()?; - let err_resp = schema::QmpErrorClass::OperationThrottled(crate::socket::LEAK_BUCKET_LIMIT); - qmp_service - .send_str(&serde_json::to_string(&Response::create_error_response( - err_resp, None, - ))?) - .with_context(|| "Failed to send message to qmp client.")?; - return Ok(()); - } - - match qmp_service.decode_line() { - (Ok(None), _) => Ok(()), - (Ok(buffer), if_fd) => { - info!("QMP: <-- {:?}", buffer); - let qmp_command: schema::QmpCommand = buffer.unwrap(); - let (return_msg, shutdown_flag) = qmp_command_exec(qmp_command, controller, if_fd); - info!("QMP: --> {:?}", return_msg); - qmp_service.send_str(&return_msg)?; - - // handle shutdown command - if shutdown_flag { - let shutdown_msg = schema::Shutdown { - guest: false, - reason: "host-qmp-quit".to_string(), - }; - event!(Shutdown; shutdown_msg); - TempCleaner::clean(); - set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); - - std::process::exit(0); - } - - Ok(()) - } - (Err(e), _) => { - let err_resp = schema::QmpErrorClass::GenericError(format!("{}", &e)); - warn!("Qmp json parser made an error: {:?}", e); - qmp_service.send_str(&serde_json::to_string(&Response::create_error_response( - err_resp, None, - ))?)?; - Ok(()) - } - } -} - -/// Create a match , where `qmp_command` and its arguments matching by handle -/// function, and exec this qmp command. -fn qmp_command_exec( - qmp_command: QmpCommand, - controller: &Arc>, - if_fd: Option, -) -> (String, bool) { - let mut qmp_response = Response::create_empty_response(); - let mut shutdown_flag = false; - - // Use macro create match to cover most Qmp command - let mut id = create_command_matches!( - qmp_command.clone(); controller.lock().unwrap(); qmp_response; - (stop, pause), - (cont, resume), - (system_powerdown, powerdown), - (system_reset, reset), - (query_status, query_status), - (query_version, query_version), - (query_commands, query_commands), - (query_target, query_target), - (query_kvm, query_kvm), - (query_events, query_events), - (query_machines, query_machines), - (query_tpm_models, query_tpm_models), - (query_tpm_types, query_tpm_types), - (query_command_line_options, query_command_line_options), - (query_migrate_capabilities, query_migrate_capabilities), - (query_qmp_schema, query_qmp_schema), - (query_sev_capabilities, query_sev_capabilities), - (query_chardev, query_chardev), - (qom_list, qom_list), - (qom_get, qom_get), - (query_block, query_block), - (query_named_block_nodes, query_named_block_nodes), - (query_blockstats, query_blockstats), - (query_block_jobs, query_block_jobs), - (query_gic_capabilities, query_gic_capabilities), - (query_iothreads, query_iothreads), - (query_migrate, query_migrate), - (cancel_migrate, cancel_migrate), - (query_cpus, query_cpus), - (query_balloon, query_balloon), - (query_mem, query_mem), - (query_vnc, query_vnc), - (list_type, list_type), - (query_hotpluggable_cpus, query_hotpluggable_cpus); - (input_event, input_event, key, value), - (device_list_properties, device_list_properties, typename), - (device_del, device_del, id), - (blockdev_del, blockdev_del, node_name), - (netdev_del, netdev_del, id), - (chardev_remove, chardev_remove, id), - (cameradev_del, cameradev_del,id), - (balloon, balloon, value), - (migrate, migrate, uri); - (device_add, device_add), - (blockdev_add, blockdev_add), - (netdev_add, netdev_add), - (chardev_add, chardev_add), - (cameradev_add, cameradev_add), - (update_region, update_region), - (human_monitor_command, human_monitor_command), - (blockdev_snapshot_internal_sync, blockdev_snapshot_internal_sync), - (blockdev_snapshot_delete_internal_sync, blockdev_snapshot_delete_internal_sync) - ); - - // Handle the Qmp command which macro can't cover - if id.is_none() { - id = match qmp_command { - QmpCommand::quit { id, .. } => { - controller.lock().unwrap().destroy(); - shutdown_flag = true; - id - } - QmpCommand::getfd { arguments, id } => { - qmp_response = controller.lock().unwrap().getfd(arguments.fd_name, if_fd); - id - } - _ => None, - } - } - - // Change response id with input qmp message - qmp_response.change_id(id); - (serde_json::to_string(&qmp_response).unwrap(), shutdown_flag) -} - -/// The struct `QmpChannel` is the only struct can handle Global variable -/// `QMP_CHANNEL`. -/// It is used to send event to qmp client and restore some file descriptor -/// which was sended by client. -pub struct QmpChannel { - /// The `writer` to send `QmpEvent`. - event_writer: RwLock>, - /// Restore file descriptor received from client. - fds: Arc>>, -} - -impl QmpChannel { - /// Constructs a `QmpChannel` in global `QMP_CHANNEL`. - pub fn object_init() { - unsafe { - if QMP_CHANNEL.is_none() { - QMP_CHANNEL = Some(Arc::new(QmpChannel { - event_writer: RwLock::new(None), - fds: Arc::new(RwLock::new(BTreeMap::new())), - })); - } - } - } - - /// Bind a `SocketRWHandler` to `QMP_CHANNEL`. - /// - /// # Arguments - /// - /// * `writer` - The `SocketRWHandler` used to communicate with client. - pub fn bind_writer(writer: SocketRWHandler) { - *Self::inner().event_writer.write().unwrap() = Some(writer); - } - - /// Unbind `SocketRWHandler` from `QMP_CHANNEL`. - pub fn unbind() { - *Self::inner().event_writer.write().unwrap() = None; - } - - /// Check whether a `SocketRWHandler` bind with `QMP_CHANNEL` or not. - pub fn is_connected() -> bool { - Self::inner().event_writer.read().unwrap().is_some() - } - - /// Restore extern file descriptor in `QMP_CHANNEL`. - /// - /// # Arguments - /// - /// * `name` - Name of file descriptor. - /// * `fd` - File descriptor sent by client. - pub fn set_fd(name: String, fd: RawFd) { - Self::inner().fds.write().unwrap().insert(name, fd); - } - - /// Get extern file descriptor restored in `QMP_CHANNEL`. - /// - /// # Arguments - /// - /// * `name` - Name of file descriptor. - pub fn get_fd(name: &str) -> Option { - Self::inner().fds.read().unwrap().get(name).copied() - } - - /// Send a `QmpEvent` to client. - /// - /// # Arguments - /// - /// * `event` - The `QmpEvent` sent to client. - #[allow(clippy::unused_io_amount)] - pub fn send_event(event: &schema::QmpEvent) { - if Self::is_connected() { - let mut event_str = serde_json::to_string(&event).unwrap(); - let mut writer_unlocked = Self::inner().event_writer.write().unwrap(); - let writer = writer_unlocked.as_mut().unwrap(); - - if let Err(e) = writer.flush() { - error!("flush err, {:?}", e); - return; - } - event_str.push_str("\r\n"); - if let Err(e) = writer.write(event_str.as_bytes()) { - error!("write err, {:?}", e); - return; - } - info!("EVENT: --> {:?}", event); - } - } - - fn inner() -> &'static std::sync::Arc { - unsafe { - match &QMP_CHANNEL { - Some(channel) => channel, - None => { - panic!("Qmp channel not initialized"); - } - } - } - } -} - -/// Send device deleted message to qmp client. -pub fn send_device_deleted_msg(id: &str) { - if QmpChannel::is_connected() { - let deleted_event = schema::DeviceDeleted { - device: Some(id.to_string()), - path: format!("/machine/peripheral/{}", id), - }; - event!(DeviceDeleted; deleted_event); - } else { - warn!("Qmp channel is not connected while sending device deleted message"); - } -} - -#[cfg(test)] -mod tests { - use std::os::unix::net::{UnixListener, UnixStream}; - - use super::*; - use serde_json; - - #[test] - fn test_qmp_greeting_msg() { - let greeting_msg = QmpGreeting::create_greeting(1, 0, 5); - - let json_msg = r#" - { - "QMP":{ - "version":{ - "qemu":{ - "micro": 1, - "minor": 0, - "major": 5 - }, - "package": "StratoVirt-2.2.0" - }, - "capabilities": [] - } - } - "#; - let greeting_from_json: QmpGreeting = serde_json::from_str(json_msg).unwrap(); - - assert_eq!(greeting_from_json, greeting_msg); - } - - #[test] - fn test_qmp_resp() { - // 1.Empty response and ID change; - let mut resp = Response::create_empty_response(); - resp.change_id(Some("0".to_string())); - - let json_msg = r#"{"return":{},"id":"0"}"#; - assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); - - resp.change_id(Some("1".to_string())); - let json_msg = r#"{"return":{},"id":"1"}"#; - assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); - - // 2.Normal response - let resp_value = schema::StatusInfo { - singlestep: false, - running: true, - status: schema::RunState::running, - }; - let resp = Response::create_response(serde_json::to_value(&resp_value).unwrap(), None); - - let json_msg = r#"{"return":{"running":true,"singlestep":false,"status":"running"}}"#; - assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); - - // 3.Error response - let qmp_err = - schema::QmpErrorClass::GenericError("Invalid Qmp command arguments!".to_string()); - let resp = Response::create_error_response(qmp_err, None); - - let json_msg = - r#"{"error":{"class":"GenericError","desc":"Invalid Qmp command arguments!"}}"#; - assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); - } - - #[test] - fn test_qmp_event_msg() { - let event_json = - r#"{"event":"STOP","data":{},"timestamp":{"seconds":1575531524,"microseconds":91519}}"#; - let qmp_event: schema::QmpEvent = serde_json::from_str(&event_json).unwrap(); - match qmp_event { - schema::QmpEvent::Stop { - data: _, - timestamp: _, - } => { - assert!(true); - } - _ => assert!(false), - } - } - - // Environment Preparation for UnixSocket - fn prepare_unix_socket_environment(socket_id: &str) -> (UnixListener, UnixStream, UnixStream) { - let socket_name: String = format!("test_{}.sock", socket_id); - let _ = std::fs::remove_file(&socket_name); - - let listener = UnixListener::bind(&socket_name).unwrap(); - let client = UnixStream::connect(&socket_name).unwrap(); - let (server, _) = listener.accept().unwrap(); - (listener, client, server) - } - - // Environment Recovery for UnixSocket - fn recover_unix_socket_environment(socket_id: &str) { - let socket_name: String = format!("test_{}.sock", socket_id); - std::fs::remove_file(&socket_name).unwrap(); - } - - #[test] - fn test_qmp_event_macro() { - use std::io::Read; - - use crate::socket::{Socket, SocketRWHandler}; - - // Pre test. Environment preparation - QmpChannel::object_init(); - let mut buffer = [0u8; 200]; - let (listener, mut client, server) = prepare_unix_socket_environment("06"); - - // Use event! macro to send event msg to client - let socket = Socket::from_unix_listener(listener, None); - socket.bind_unix_stream(server); - QmpChannel::bind_writer(SocketRWHandler::new(socket.get_stream_fd())); - - // 1.send no-content event - event!(Stop); - let length = client.read(&mut buffer).unwrap(); - let qmp_event: schema::QmpEvent = - serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); - match qmp_event { - schema::QmpEvent::Stop { - data: _, - timestamp: _, - } => { - assert!(true); - } - _ => assert!(false), - } - - // 2.send with-content event - let shutdown_event = schema::Shutdown { - guest: true, - reason: "guest-shutdown".to_string(), - }; - event!(Shutdown; shutdown_event); - let length = client.read(&mut buffer).unwrap(); - let qmp_event: schema::QmpEvent = - serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); - match qmp_event { - schema::QmpEvent::Shutdown { data, timestamp: _ } => { - assert_eq!(data.guest, true); - assert_eq!(data.reason, "guest-shutdown".to_string()); - } - _ => assert!(false), - } - - // After test. Environment Recover - recover_unix_socket_environment("06"); - } - - #[test] - fn test_qmp_send_response() { - use std::io::Read; - - use crate::socket::Socket; - - // Pre test. Environment preparation - let mut buffer = [0u8; 300]; - let (listener, mut client, server) = prepare_unix_socket_environment("07"); - - // Use event! macro to send event msg to client - let socket = Socket::from_unix_listener(listener, None); - socket.bind_unix_stream(server); - - // 1.send greeting response - let res = socket.send_response(true); - let length = client.read(&mut buffer).unwrap(); - let qmp_response: QmpGreeting = - serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); - let qmp_greeting = QmpGreeting::create_greeting(1, 0, 5); - assert_eq!(qmp_greeting, qmp_response); - assert_eq!(res.is_err(), false); - - // 2.send empty response - let res = socket.send_response(false); - let length = client.read(&mut buffer).unwrap(); - let qmp_response: Response = - serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); - let qmp_empty_response = Response::create_empty_response(); - assert_eq!(qmp_empty_response, qmp_response); - assert_eq!(res.is_err(), false); - - // After test. Environment Recover - recover_unix_socket_environment("07"); - drop(socket); - } - - #[test] - fn test_create_error_response() { - let strange_msg = "!?/.,、。’】= -~1!@#¥%……&*()——+".to_string(); - - let err_cls = schema::QmpErrorClass::GenericError(strange_msg.clone()); - let msg = ErrorMessage::new(&err_cls); - assert_eq!(msg.desc, strange_msg); - assert_eq!(msg.errorkind, "GenericError".to_string()); - let qmp_err = schema::QmpErrorClass::GenericError(strange_msg.clone()); - let resp = Response::create_error_response(qmp_err, None); - assert_eq!(resp.error, Some(msg)); - - let err_cls = schema::QmpErrorClass::CommandNotFound(strange_msg.clone()); - let msg = ErrorMessage::new(&err_cls); - assert_eq!(msg.desc, strange_msg); - assert_eq!(msg.errorkind, "CommandNotFound".to_string()); - let qmp_err = schema::QmpErrorClass::CommandNotFound(strange_msg.clone()); - let resp = Response::create_error_response(qmp_err, None); - assert_eq!(resp.error, Some(msg)); - - let err_cls = schema::QmpErrorClass::DeviceNotFound(strange_msg.clone()); - let msg = ErrorMessage::new(&err_cls); - assert_eq!(msg.desc, strange_msg); - assert_eq!(msg.errorkind, "DeviceNotFound".to_string()); - let qmp_err = schema::QmpErrorClass::DeviceNotFound(strange_msg.clone()); - let resp = Response::create_error_response(qmp_err, None); - assert_eq!(resp.error, Some(msg)); - - let err_cls = schema::QmpErrorClass::KVMMissingCap(strange_msg.clone()); - let msg = ErrorMessage::new(&err_cls); - assert_eq!(msg.desc, strange_msg); - assert_eq!(msg.errorkind, "KVMMissingCap".to_string()); - let qmp_err = schema::QmpErrorClass::KVMMissingCap(strange_msg.clone()); - let resp = Response::create_error_response(qmp_err, None); - assert_eq!(resp.error, Some(msg)); - } -} +pub mod qmp_socket; diff --git a/machine_manager/src/qmp/qmp_channel.rs b/machine_manager/src/qmp/qmp_channel.rs new file mode 100644 index 000000000..42ee1afca --- /dev/null +++ b/machine_manager/src/qmp/qmp_channel.rs @@ -0,0 +1,193 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::BTreeMap; +use std::io::Write; +use std::os::unix::io::RawFd; +use std::sync::{Arc, RwLock}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use log::{error, info, warn}; +use serde::{Deserialize, Serialize}; + +use super::qmp_schema::{self as schema}; +use crate::socket::SocketRWHandler; +use util::time::NANOSECONDS_PER_SECOND; + +static mut QMP_CHANNEL: Option> = None; + +/// Macro `event!`: send event to qmp-client. +/// +/// # Arguments +/// +/// * `$x` - event type +/// * `$y` - event context +/// +/// # Example +/// +/// ```text +/// #[macro_use] +/// use machine_manager::qmp::*; +/// +/// event!(Shutdown; shutdown_msg); +/// event!(Stop); +/// event!(Resume); +/// ``` +#[macro_export] +macro_rules! event { + ( $x:tt ) => {{ + QmpChannel::send_event(&$crate::qmp::qmp_schema::QmpEvent::$x { + data: Default::default(), + timestamp: $crate::qmp::qmp_channel::create_timestamp(), + }); + }}; + ( $x:tt;$y:expr ) => {{ + QmpChannel::send_event(&$crate::qmp::qmp_schema::QmpEvent::$x { + data: $y, + timestamp: $crate::qmp::qmp_channel::create_timestamp(), + }); + }}; +} + +/// `TimeStamp` structure for `QmpEvent`. +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct TimeStamp { + seconds: u64, + microseconds: u64, +} + +/// Constructs a `TimeStamp` struct. +pub fn create_timestamp() -> TimeStamp { + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + let seconds = u128::from(since_the_epoch.as_secs()); + let microseconds = + (since_the_epoch.as_nanos() - seconds * (NANOSECONDS_PER_SECOND as u128)) / (1_000_u128); + TimeStamp { + seconds: seconds as u64, + microseconds: microseconds as u64, + } +} + +/// The struct `QmpChannel` is the only struct can handle Global variable +/// `QMP_CHANNEL`. +/// It is used to send event to qmp client and restore some file descriptor +/// which was sended by client. +pub struct QmpChannel { + /// The `writer` to send `QmpEvent`. + event_writer: RwLock>, + /// Restore file descriptor received from client. + fds: Arc>>, +} + +impl QmpChannel { + /// Constructs a `QmpChannel` in global `QMP_CHANNEL`. + pub fn object_init() { + unsafe { + if QMP_CHANNEL.is_none() { + QMP_CHANNEL = Some(Arc::new(QmpChannel { + event_writer: RwLock::new(None), + fds: Arc::new(RwLock::new(BTreeMap::new())), + })); + } + } + } + + /// Bind a `SocketRWHandler` to `QMP_CHANNEL`. + /// + /// # Arguments + /// + /// * `writer` - The `SocketRWHandler` used to communicate with client. + pub(crate) fn bind_writer(writer: SocketRWHandler) { + *Self::inner().event_writer.write().unwrap() = Some(writer); + } + + /// Unbind `SocketRWHandler` from `QMP_CHANNEL`. + pub(crate) fn unbind() { + *Self::inner().event_writer.write().unwrap() = None; + } + + /// Check whether a `SocketRWHandler` bind with `QMP_CHANNEL` or not. + pub fn is_connected() -> bool { + Self::inner().event_writer.read().unwrap().is_some() + } + + /// Restore extern file descriptor in `QMP_CHANNEL`. + /// + /// # Arguments + /// + /// * `name` - Name of file descriptor. + /// * `fd` - File descriptor sent by client. + pub fn set_fd(name: String, fd: RawFd) { + Self::inner().fds.write().unwrap().insert(name, fd); + } + + /// Get extern file descriptor restored in `QMP_CHANNEL`. + /// + /// # Arguments + /// + /// * `name` - Name of file descriptor. + pub fn get_fd(name: &str) -> Option { + Self::inner().fds.read().unwrap().get(name).copied() + } + + /// Send a `QmpEvent` to client. + /// + /// # Arguments + /// + /// * `event` - The `QmpEvent` sent to client. + #[allow(clippy::unused_io_amount)] + pub fn send_event(event: &schema::QmpEvent) { + if Self::is_connected() { + let mut event_str = serde_json::to_string(&event).unwrap(); + let mut writer_unlocked = Self::inner().event_writer.write().unwrap(); + let writer = writer_unlocked.as_mut().unwrap(); + + if let Err(e) = writer.flush() { + error!("flush err, {:?}", e); + return; + } + event_str.push_str("\r\n"); + if let Err(e) = writer.write(event_str.as_bytes()) { + error!("write err, {:?}", e); + return; + } + info!("EVENT: --> {:?}", event); + } + } + + fn inner() -> &'static std::sync::Arc { + unsafe { + match &QMP_CHANNEL { + Some(channel) => channel, + None => { + panic!("Qmp channel not initialized"); + } + } + } + } +} + +/// Send device deleted message to qmp client. +pub fn send_device_deleted_msg(id: &str) { + if QmpChannel::is_connected() { + let deleted_event = schema::DeviceDeleted { + device: Some(id.to_string()), + path: format!("/machine/peripheral/{}", id), + }; + event!(DeviceDeleted; deleted_event); + } else { + warn!("Qmp channel is not connected while sending device deleted message"); + } +} diff --git a/machine_manager/src/qmp/qmp_response.rs b/machine_manager/src/qmp/qmp_response.rs new file mode 100644 index 000000000..ea93c92e0 --- /dev/null +++ b/machine_manager/src/qmp/qmp_response.rs @@ -0,0 +1,280 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use super::qmp_schema::{self as schema}; + +/// Qmp greeting message. +/// +/// # Notes +/// +/// It contains the version of VM or fake Qemu version to adapt others. +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +pub(crate) struct QmpGreeting { + #[serde(rename = "QMP")] + qmp: Greeting, +} + +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +struct Greeting { + version: Version, + capabilities: Vec, +} + +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct Version { + #[serde(rename = "qemu")] + application: VersionNumber, + package: String, +} + +impl Version { + pub fn new(micro: u8, minor: u8, major: u8) -> Self { + let version_number = VersionNumber { + micro, + minor, + major, + }; + Version { + application: version_number, + package: "StratoVirt-".to_string() + env!("CARGO_PKG_VERSION"), + } + } +} + +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +struct VersionNumber { + micro: u8, + minor: u8, + major: u8, +} + +impl QmpGreeting { + /// Create qmp greeting message. + /// + /// # Arguments + /// + /// * `micro` - Micro version number. + /// * `minor` - Minor version number. + /// * `major` - Major version number. + pub(crate) fn create_greeting(micro: u8, minor: u8, major: u8) -> Self { + let version = Version::new(micro, minor, major); + let cap: Vec = Default::default(); + let greeting = Greeting { + version, + capabilities: cap, + }; + QmpGreeting { qmp: greeting } + } +} + +/// `ErrorMessage` for Qmp Response. +#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] +struct ErrorMessage { + #[serde(rename = "class")] + errorkind: String, + desc: String, +} + +impl ErrorMessage { + fn new(e: &schema::QmpErrorClass) -> Self { + let content = e.to_content(); + let serde_str = serde_json::to_string(&e).unwrap(); + let serde_vec: Vec<&str> = serde_str.split(':').collect(); + let class_name = serde_vec[0]; + let len: usize = class_name.len(); + ErrorMessage { + errorkind: class_name[2..len - 1].to_string(), + desc: content, + } + } +} + +/// Empty message for QMP. +#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub(crate) struct Empty {} + +/// Qmp response to client +/// +/// # Notes +/// +/// It contains two kind response: `BadResponse` and `GoodResponse`. This two +/// kind response are fit by executing qmp command by success and failure. +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct Response { + #[serde(rename = "return", default, skip_serializing_if = "Option::is_none")] + return_: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + error: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, +} + +impl Response { + /// Create qmp response with inner `Value` and `id`. + /// + /// # Arguments + /// + /// * `v` - The `Value` of qmp `return` field. + /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s `id`. + pub fn create_response(v: Value, id: Option) -> Self { + Response { + return_: Some(v), + error: None, + id, + } + } + + /// Create a empty qmp response, `return` field will be empty. + pub fn create_empty_response() -> Self { + Response { + return_: Some(serde_json::to_value(Empty {}).unwrap()), + error: None, + id: None, + } + } + + /// Create a error qmo response with `err_class` and `id`. + /// # Arguments + /// + /// * `err_class` - The `QmpErrorClass` of qmp `error` field. + /// * `id` - The `id` for qmp `Response`, it must be equal to `Request`'s `id`. + pub fn create_error_response(err_class: schema::QmpErrorClass, id: Option) -> Self { + Response { + return_: None, + error: Some(ErrorMessage::new(&err_class)), + id, + } + } + + pub(crate) fn change_id(&mut self, id: Option) { + self.id = id; + } +} + +impl From for Response { + fn from(value: bool) -> Self { + if value { + Response::create_empty_response() + } else { + Response::create_error_response( + schema::QmpErrorClass::GenericError(String::new()), + None, + ) + } + } +} + +#[cfg(test)] +mod tests { + use serde_json; + + use super::*; + use crate::qmp::qmp_schema; + + #[test] + fn test_qmp_greeting_msg() { + let greeting_msg = QmpGreeting::create_greeting(1, 0, 5); + + let json_msg = r#" + { + "QMP":{ + "version":{ + "qemu":{ + "micro": 1, + "minor": 0, + "major": 5 + }, + "package": "StratoVirt-2.2.0" + }, + "capabilities": [] + } + } + "#; + let greeting_from_json: QmpGreeting = serde_json::from_str(json_msg).unwrap(); + + assert_eq!(greeting_from_json, greeting_msg); + } + + #[test] + fn test_qmp_resp() { + // 1.Empty response and ID change; + let mut resp = Response::create_empty_response(); + resp.change_id(Some("0".to_string())); + + let json_msg = r#"{"return":{},"id":"0"}"#; + assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); + + resp.change_id(Some("1".to_string())); + let json_msg = r#"{"return":{},"id":"1"}"#; + assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); + + // 2.Normal response + let resp_value = qmp_schema::StatusInfo { + singlestep: false, + running: true, + status: qmp_schema::RunState::running, + }; + let resp = Response::create_response(serde_json::to_value(&resp_value).unwrap(), None); + + let json_msg = r#"{"return":{"running":true,"singlestep":false,"status":"running"}}"#; + assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); + + // 3.Error response + let qmp_err = + qmp_schema::QmpErrorClass::GenericError("Invalid Qmp command arguments!".to_string()); + let resp = Response::create_error_response(qmp_err, None); + + let json_msg = + r#"{"error":{"class":"GenericError","desc":"Invalid Qmp command arguments!"}}"#; + assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); + } + + #[test] + fn test_create_error_response() { + let strange_msg = "!?/.,、。’】= -~1!@#¥%……&*()——+".to_string(); + + let err_cls = qmp_schema::QmpErrorClass::GenericError(strange_msg.clone()); + let msg = ErrorMessage::new(&err_cls); + assert_eq!(msg.desc, strange_msg); + assert_eq!(msg.errorkind, "GenericError".to_string()); + let qmp_err = qmp_schema::QmpErrorClass::GenericError(strange_msg.clone()); + let resp = Response::create_error_response(qmp_err, None); + assert_eq!(resp.error, Some(msg)); + + let err_cls = qmp_schema::QmpErrorClass::CommandNotFound(strange_msg.clone()); + let msg = ErrorMessage::new(&err_cls); + assert_eq!(msg.desc, strange_msg); + assert_eq!(msg.errorkind, "CommandNotFound".to_string()); + let qmp_err = qmp_schema::QmpErrorClass::CommandNotFound(strange_msg.clone()); + let resp = Response::create_error_response(qmp_err, None); + assert_eq!(resp.error, Some(msg)); + + let err_cls = qmp_schema::QmpErrorClass::DeviceNotFound(strange_msg.clone()); + let msg = ErrorMessage::new(&err_cls); + assert_eq!(msg.desc, strange_msg); + assert_eq!(msg.errorkind, "DeviceNotFound".to_string()); + let qmp_err = qmp_schema::QmpErrorClass::DeviceNotFound(strange_msg.clone()); + let resp = Response::create_error_response(qmp_err, None); + assert_eq!(resp.error, Some(msg)); + + let err_cls = qmp_schema::QmpErrorClass::KVMMissingCap(strange_msg.clone()); + let msg = ErrorMessage::new(&err_cls); + assert_eq!(msg.desc, strange_msg); + assert_eq!(msg.errorkind, "KVMMissingCap".to_string()); + let qmp_err = qmp_schema::QmpErrorClass::KVMMissingCap(strange_msg.clone()); + let resp = Response::create_error_response(qmp_err, None); + assert_eq!(resp.error, Some(msg)); + } +} diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 401c3341a..8874b6b83 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -12,11 +12,12 @@ pub use serde_json::Value as Any; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use strum_macros::{EnumIter, EnumString, EnumVariantNames}; -use super::Version; -use crate::qmp::{Command, Empty, TimeStamp}; +use super::qmp_channel::TimeStamp; +use super::qmp_response::{Empty, Version}; use util::aio::AioEngine; /// A error enum for qmp @@ -438,6 +439,12 @@ pub enum QmpCommand { }, } +/// Command trait for Deserialize and find back Response. +trait Command: Serialize { + type Res: DeserializeOwned; + fn back(self) -> Self::Res; +} + /// qmp_capabilities /// /// Enable QMP capabilities. @@ -2380,6 +2387,22 @@ impl Command for query_mem { mod tests { use super::*; + #[test] + fn test_qmp_event_msg() { + let event_json = + r#"{"event":"STOP","data":{},"timestamp":{"seconds":1575531524,"microseconds":91519}}"#; + let qmp_event: QmpEvent = serde_json::from_str(&event_json).unwrap(); + match qmp_event { + QmpEvent::Stop { + data: _, + timestamp: _, + } => { + assert!(true); + } + _ => assert!(false), + } + } + #[test] fn test_qmp_unexpected_arguments() { // qmp: quit. diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs new file mode 100644 index 000000000..bd140bf32 --- /dev/null +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -0,0 +1,637 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::net::{UnixListener, UnixStream}; +use std::rc::Rc; +use std::sync::{Arc, Mutex, RwLock}; + +use anyhow::{Context, Result}; +use log::{error, info, warn}; +use vmm_sys_util::epoll::EventSet; + +use super::qmp_schema; +use super::qmp_schema::QmpCommand; +use super::{qmp_channel::QmpChannel, qmp_response::QmpGreeting, qmp_response::Response}; +use crate::event; +use crate::event_loop::EventLoop; +use crate::machine::MachineExternalInterface; +use crate::socket::SocketHandler; +use crate::socket::SocketRWHandler; +use crate::temp_cleaner::TempCleaner; +use util::leak_bucket::LeakBucket; +use util::loop_context::{ + gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, +}; +use util::set_termi_canon_mode; + +const LEAK_BUCKET_LIMIT: u64 = 100; + +/// Type for api socket. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum SocketType { + Unix = 1, +} + +/// Wrapper over UnixSteam. +#[derive(Debug)] +struct SocketStream(UnixStream); + +impl SocketStream { + fn from_unix_stream(stream: UnixStream) -> Self { + SocketStream(stream) + } +} + +impl AsRawFd for SocketStream { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +/// The wrapper over Unix socket and socket handler. +/// +/// # Example +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::os::unix::io::AsRawFd; +/// use std::os::unix::net::{UnixListener, UnixStream}; +/// +/// use machine_manager::qmp::qmp_socket::Socket; +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/my/socket")?; +/// let socket = Socket::from_unix_listener(listener, None); +/// assert!(!socket.is_connected()); +/// +/// let client_stream = UnixStream::connect("/path/to/my/socket")?; +/// let server_stream = socket.accept_unix_stream(); +/// socket.bind_unix_stream(server_stream); +/// assert!(socket.is_connected()); +/// Ok(()) +/// } +/// ``` +pub struct Socket { + /// Type for Socket + sock_type: SocketType, + /// Socket listener tuple + listener: UnixListener, + /// Socket stream with RwLock + stream: RwLock>, + /// Perform socket command + performer: Option>>, +} + +impl Socket { + /// Allocates a new `Socket` with `UnixListener`. + /// + /// # Arguments + /// + /// * `listener` - The `UnixListener` bind to `Socket`. + /// * `performer` - The `VM` to perform socket command. + pub fn from_unix_listener( + listener: UnixListener, + performer: Option>>, + ) -> Self { + Socket { + sock_type: SocketType::Unix, + listener, + stream: RwLock::new(None), + performer, + } + } + + /// Get listener's fd from `Socket`. + fn get_listener_fd(&self) -> RawFd { + self.listener.as_raw_fd() + } + + /// Accept stream and bind to Socket. + fn accept(&self) { + match self.sock_type { + SocketType::Unix => { + let stream = self.accept_unix_stream(); + self.bind_unix_stream(stream); + } + } + } + + /// Accept a new incoming connection unix stream from unix listener. + pub fn accept_unix_stream(&self) -> UnixStream { + let (stream, _) = self.listener.accept().unwrap(); + stream + } + + /// Get socket type from `Socket`. + #[allow(unused)] + fn get_socket_type(&self) -> SocketType { + self.sock_type + } + + /// Bind `Socket` with a `UnixStream`. + /// + /// # Arguments + /// + /// * `unix_stream` - The `UnixStream` bind to `Socket`. + pub fn bind_unix_stream(&self, unix_stream: UnixStream) { + let stream = SocketStream::from_unix_stream(unix_stream); + *self.stream.write().unwrap() = Some(stream); + } + + /// Unbind stream from `Socket`, reset the state. + #[allow(unused)] + fn drop_stream(&self) { + *self.stream.write().unwrap() = None; + } + + /// Confirm whether socket stream bind to `Socket` or not. + pub fn is_connected(&self) -> bool { + self.stream.read().unwrap().is_some() + } + + /// Get socket fd from `Socket`, it a private function. + fn get_stream_fd(&self) -> RawFd { + if self.is_connected() { + self.stream.read().unwrap().as_ref().unwrap().as_raw_fd() + } else { + panic!("Failed to get socket fd!"); + } + } + + /// Get a `SocketHandler` from `Socket`. + fn get_socket_handler(&self) -> SocketHandler { + SocketHandler::new(self.get_stream_fd()) + } + + /// In qmp feature, send empty or greeting response to client. + /// + /// # Arguments + /// + /// * `is_greeting` - Whether sending greeting response or not. + fn send_response(&self, is_greeting: bool) -> std::io::Result<()> { + if self.is_connected() { + let mut handler = self.get_socket_handler(); + let resp = if is_greeting { + serde_json::to_string(&QmpGreeting::create_greeting(1, 0, 5)).unwrap() + } else { + serde_json::to_string(&Response::create_empty_response()).unwrap() + }; + handler.send_str(&resp)?; + info!("QMP: --> {:?}", resp); + } + Ok(()) + } + + /// Create socket's accepted stream to `event_notifier`. + fn create_event_notifier(&mut self, shared_socket: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let leak_bucket = LeakBucket::new(LEAK_BUCKET_LIMIT); + if let Err(e) = leak_bucket { + error!("Failed to create leak bucket, {:?}", e); + return notifiers; + } + let leak_bucket = Arc::new(Mutex::new(leak_bucket.unwrap())); + let shared_leak_bucket = leak_bucket.clone(); + let leak_bucket_fd = leak_bucket.lock().unwrap().as_raw_fd(); + + self.accept(); + QmpChannel::bind_writer(SocketRWHandler::new(self.get_stream_fd())); + if let Err(e) = self.send_response(true) { + error!("{:?}", e); + QmpChannel::unbind(); + return notifiers; + } + let handler: Rc = Rc::new(move |event, _| { + if event == EventSet::IN { + let socket_mutexed = shared_socket.lock().unwrap(); + let stream_fd = socket_mutexed.get_stream_fd(); + + let performer = &socket_mutexed.performer.as_ref().unwrap(); + if let Err(e) = handle_qmp( + stream_fd, + performer, + &mut shared_leak_bucket.lock().unwrap(), + ) { + error!("{:?}", e); + } + } + if event & EventSet::HANG_UP == EventSet::HANG_UP { + let socket_mutexed = shared_socket.lock().unwrap(); + let stream_fd = socket_mutexed.get_stream_fd(); + + QmpChannel::unbind(); + Some(gen_delete_notifiers(&[stream_fd, leak_bucket_fd])) + } else { + None + } + }); + let qmp_notifier = EventNotifier::new( + NotifierOperation::AddShared, + self.get_stream_fd(), + Some(self.get_listener_fd()), + EventSet::IN | EventSet::HANG_UP, + vec![handler], + ); + notifiers.push(qmp_notifier); + + let leak_bucket_notifier = EventNotifier::new( + NotifierOperation::AddShared, + leak_bucket_fd, + None, + EventSet::IN, + vec![Rc::new(move |_, fd| { + read_fd(fd); + leak_bucket.lock().unwrap().clear_timer(); + None + })], + ); + notifiers.push(leak_bucket_notifier); + + notifiers + } +} + +impl EventNotifierHelper for Socket { + fn internal_notifiers(shared_socket: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let socket = shared_socket.clone(); + let handler: Rc = + Rc::new(move |_, _| Some(socket.lock().unwrap().create_event_notifier(socket.clone()))); + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + shared_socket.lock().unwrap().get_listener_fd(), + None, + EventSet::IN, + vec![handler], + ); + notifiers.push(notifier); + + notifiers + } +} + +/// Macro: to execute handle func with every arguments. +macro_rules! qmp_command_match { + ( $func:tt, $executor:expr, $ret:expr ) => { + $ret = $executor.$func().into(); + }; + ( $func:tt, $executor:expr, $cmd:expr, $ret:expr, $($arg:tt),* ) => { + $ret = $executor.$func( + $($cmd.$arg),* + ).into(); + }; +} + +/// Macro: to execute handle func with all arguments. +macro_rules! qmp_command_match_with_argument { + ( $func:tt, $executor:expr, $cmd:expr, $ret:expr ) => { + $ret = $executor.$func($cmd).into(); + }; +} + +/// Macro `create_command_matches!`: Generate a match statement for qmp_command +/// , which is combined with its handle func. +/// +/// # Arguments +/// +/// `cmd_type_1` - The qmp command with no arguments. +/// `cmd_type_2` - The qmp command with arguments. +macro_rules! create_command_matches { + ( $command:expr; $executor:expr; $ret:expr; + $(($cmd_type_1:tt, $func_1:tt)),*; + $(($cmd_type_2:tt, $func_2:tt, $($arg:tt),*)),*; + $(($cmd_type_3:tt, $func_3:tt)),* + ) => { + match $command { + $( + $crate::qmp::qmp_schema::QmpCommand::$cmd_type_1{ id, ..} => { + qmp_command_match!($func_1, $executor, $ret); + id + }, + )* + $( + $crate::qmp::qmp_schema::QmpCommand::$cmd_type_2{ arguments, id } => { + qmp_command_match!($func_2, $executor, arguments, $ret, $($arg),*); + id + }, + )* + $( + $crate::qmp::qmp_schema::QmpCommand::$cmd_type_3{ arguments, id } => { + qmp_command_match_with_argument!($func_3, $executor, arguments, $ret); + id + }, + )* + _ => None, + } + }; +} + +/// Accept qmp command, analyze and exec it. +/// +/// # Arguments +/// +/// * `stream_fd` - The input stream file description. +/// * `controller` - The controller which execute actual qmp command. +/// * `leak_bucket` - The LeakBucket flow controller for qmp command. +/// +/// # Errors +/// +/// This function will fail when json parser failed or socket file description broke. +fn handle_qmp( + stream_fd: RawFd, + controller: &Arc>, + leak_bucket: &mut LeakBucket, +) -> Result<()> { + let mut qmp_service = crate::socket::SocketHandler::new(stream_fd); + + // If flow over `LEAK_BUCKET_LIMIT` per seconds, discard the request and return + // a `OperationThrottled` error. + if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), 1_u64) { + qmp_service.discard()?; + let err_resp = qmp_schema::QmpErrorClass::OperationThrottled(LEAK_BUCKET_LIMIT); + qmp_service + .send_str(&serde_json::to_string(&Response::create_error_response( + err_resp, None, + ))?) + .with_context(|| "Failed to send message to qmp client.")?; + return Ok(()); + } + + match qmp_service.decode_line() { + (Ok(None), _) => Ok(()), + (Ok(buffer), if_fd) => { + info!("QMP: <-- {:?}", buffer); + let qmp_command: QmpCommand = buffer.unwrap(); + let (return_msg, shutdown_flag) = qmp_command_exec(qmp_command, controller, if_fd); + info!("QMP: --> {:?}", return_msg); + qmp_service.send_str(&return_msg)?; + + // handle shutdown command + if shutdown_flag { + let shutdown_msg = qmp_schema::Shutdown { + guest: false, + reason: "host-qmp-quit".to_string(), + }; + event!(Shutdown; shutdown_msg); + TempCleaner::clean(); + set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); + + std::process::exit(0); + } + + Ok(()) + } + (Err(e), _) => { + let err_resp = qmp_schema::QmpErrorClass::GenericError(format!("{}", &e)); + warn!("Qmp json parser made an error: {:?}", e); + qmp_service.send_str(&serde_json::to_string(&Response::create_error_response( + err_resp, None, + ))?)?; + Ok(()) + } + } +} + +/// Create a match , where `qmp_command` and its arguments matching by handle +/// function, and exec this qmp command. +fn qmp_command_exec( + qmp_command: QmpCommand, + controller: &Arc>, + if_fd: Option, +) -> (String, bool) { + let mut qmp_response = Response::create_empty_response(); + let mut shutdown_flag = false; + + // Use macro create match to cover most Qmp command + let mut id = create_command_matches!( + qmp_command.clone(); controller.lock().unwrap(); qmp_response; + (stop, pause), + (cont, resume), + (system_powerdown, powerdown), + (system_reset, reset), + (query_status, query_status), + (query_version, query_version), + (query_commands, query_commands), + (query_target, query_target), + (query_kvm, query_kvm), + (query_events, query_events), + (query_machines, query_machines), + (query_tpm_models, query_tpm_models), + (query_tpm_types, query_tpm_types), + (query_command_line_options, query_command_line_options), + (query_migrate_capabilities, query_migrate_capabilities), + (query_qmp_schema, query_qmp_schema), + (query_sev_capabilities, query_sev_capabilities), + (query_chardev, query_chardev), + (qom_list, qom_list), + (qom_get, qom_get), + (query_block, query_block), + (query_named_block_nodes, query_named_block_nodes), + (query_blockstats, query_blockstats), + (query_block_jobs, query_block_jobs), + (query_gic_capabilities, query_gic_capabilities), + (query_iothreads, query_iothreads), + (query_migrate, query_migrate), + (cancel_migrate, cancel_migrate), + (query_cpus, query_cpus), + (query_balloon, query_balloon), + (query_mem, query_mem), + (query_vnc, query_vnc), + (list_type, list_type), + (query_hotpluggable_cpus, query_hotpluggable_cpus); + (input_event, input_event, key, value), + (device_list_properties, device_list_properties, typename), + (device_del, device_del, id), + (blockdev_del, blockdev_del, node_name), + (netdev_del, netdev_del, id), + (chardev_remove, chardev_remove, id), + (cameradev_del, cameradev_del,id), + (balloon, balloon, value), + (migrate, migrate, uri); + (device_add, device_add), + (blockdev_add, blockdev_add), + (netdev_add, netdev_add), + (chardev_add, chardev_add), + (cameradev_add, cameradev_add), + (update_region, update_region), + (human_monitor_command, human_monitor_command), + (blockdev_snapshot_internal_sync, blockdev_snapshot_internal_sync), + (blockdev_snapshot_delete_internal_sync, blockdev_snapshot_delete_internal_sync) + ); + + // Handle the Qmp command which macro can't cover + if id.is_none() { + id = match qmp_command { + QmpCommand::quit { id, .. } => { + controller.lock().unwrap().destroy(); + shutdown_flag = true; + id + } + QmpCommand::getfd { arguments, id } => { + qmp_response = controller.lock().unwrap().getfd(arguments.fd_name, if_fd); + id + } + _ => None, + } + } + + // Change response id with input qmp message + qmp_response.change_id(id); + (serde_json::to_string(&qmp_response).unwrap(), shutdown_flag) +} + +#[cfg(test)] +mod tests { + use std::os::unix::net::{UnixListener, UnixStream}; + use std::time::Duration; + + use super::*; + use serde_json; + + // Environment Preparation for UnixSocket + fn prepare_unix_socket_environment(socket_id: &str) -> (UnixListener, UnixStream, UnixStream) { + let socket_name: String = format!("test_{}.sock", socket_id); + let _ = std::fs::remove_file(&socket_name); + + let listener = UnixListener::bind(&socket_name).unwrap(); + std::thread::sleep(Duration::from_millis(100)); + let client = UnixStream::connect(&socket_name).unwrap(); + let (server, _) = listener.accept().unwrap(); + (listener, client, server) + } + + // Environment Recovery for UnixSocket + fn recover_unix_socket_environment(socket_id: &str) { + let socket_name: String = format!("test_{}.sock", socket_id); + std::fs::remove_file(&socket_name).unwrap(); + } + + #[test] + fn test_socket_lifecycle() { + // Pre test. Environment Preparation + let (listener, _, server) = prepare_unix_socket_environment("04"); + let socket = Socket::from_unix_listener(listener, None); + + // life cycle test + // 1.Unconnected + assert_eq!(socket.is_connected(), false); + + // 2.Connected + socket.bind_unix_stream(server); + assert_eq!(socket.is_connected(), true); + assert_eq!(socket.get_socket_type(), SocketType::Unix); + + // 3.Unbind SocketStream, reset state + socket.drop_stream(); + assert_eq!(socket.is_connected(), false); + + // 4.Accept and reconnect a new UnixStream + let _new_client = UnixStream::connect("test_04.sock"); + let new_server = socket.accept_unix_stream(); + socket.bind_unix_stream(new_server); + assert_eq!(socket.is_connected(), true); + + // After test. Environment Recover + recover_unix_socket_environment("04"); + } + + #[test] + fn test_qmp_event_macro() { + use std::io::Read; + + use crate::socket::SocketRWHandler; + + // Pre test. Environment preparation + QmpChannel::object_init(); + let mut buffer = [0u8; 200]; + let (listener, mut client, server) = prepare_unix_socket_environment("06"); + + // Use event! macro to send event msg to client + let socket = Socket::from_unix_listener(listener, None); + socket.bind_unix_stream(server); + QmpChannel::bind_writer(SocketRWHandler::new(socket.get_stream_fd())); + + // 1.send no-content event + event!(Stop); + let length = client.read(&mut buffer).unwrap(); + let qmp_event: qmp_schema::QmpEvent = + serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); + match qmp_event { + qmp_schema::QmpEvent::Stop { + data: _, + timestamp: _, + } => { + assert!(true); + } + _ => assert!(false), + } + + // 2.send with-content event + let shutdown_event = qmp_schema::Shutdown { + guest: true, + reason: "guest-shutdown".to_string(), + }; + event!(Shutdown; shutdown_event); + let length = client.read(&mut buffer).unwrap(); + let qmp_event: qmp_schema::QmpEvent = + serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); + match qmp_event { + qmp_schema::QmpEvent::Shutdown { data, timestamp: _ } => { + assert_eq!(data.guest, true); + assert_eq!(data.reason, "guest-shutdown".to_string()); + } + _ => assert!(false), + } + + // After test. Environment Recover + recover_unix_socket_environment("06"); + } + + #[test] + fn test_qmp_send_response() { + use std::io::Read; + + // Pre test. Environment preparation + let mut buffer = [0u8; 300]; + let (listener, mut client, server) = prepare_unix_socket_environment("07"); + + // Use event! macro to send event msg to client + let socket = Socket::from_unix_listener(listener, None); + socket.bind_unix_stream(server); + + // 1.send greeting response + let res = socket.send_response(true); + let length = client.read(&mut buffer).unwrap(); + let qmp_response: QmpGreeting = + serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); + let qmp_greeting = QmpGreeting::create_greeting(1, 0, 5); + assert_eq!(qmp_greeting, qmp_response); + assert_eq!(res.is_err(), false); + + // 2.send empty response + let res = socket.send_response(false); + let length = client.read(&mut buffer).unwrap(); + let qmp_response: Response = + serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); + let qmp_empty_response = Response::create_empty_response(); + assert_eq!(qmp_empty_response, qmp_response); + assert_eq!(res.is_err(), false); + + // After test. Environment Recover + recover_unix_socket_environment("07"); + drop(socket); + } +} diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 57e71b28b..5ab376acb 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -17,7 +17,7 @@ use vmm_sys_util::signal::register_signal_handler; use crate::{ event, - qmp::{qmp_schema, QmpChannel}, + qmp::{qmp_channel::QmpChannel, qmp_schema}, temp_cleaner::TempCleaner, }; use util::set_termi_canon_mode; diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index dee35681c..494e4569f 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -12,278 +12,20 @@ use std::io::{Error, ErrorKind, Read, Write}; use std::mem::size_of; -use std::os::unix::io::{AsRawFd, RawFd}; -use std::os::unix::net::{UnixListener, UnixStream}; -use std::rc::Rc; -use std::sync::{Arc, Mutex, RwLock}; +use std::os::unix::io::RawFd; use anyhow::{bail, Result}; use libc::{ c_void, iovec, msghdr, recvmsg, sendmsg, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, MSG_DONTWAIT, MSG_NOSIGNAL, SCM_RIGHTS, SOL_SOCKET, }; -use log::{error, info}; use serde::Deserialize; -use vmm_sys_util::epoll::EventSet; - -use crate::machine::MachineExternalInterface; -use crate::qmp::{QmpChannel, QmpGreeting, Response}; -use util::leak_bucket::LeakBucket; -use util::loop_context::{ - gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, - NotifierOperation, -}; const MAX_SOCKET_MSG_LENGTH: usize = 8192; /// The max buffer length received by recvmsg. const MAX_RECV_BUF_LEN: usize = 4096; /// The max buffer length used by recvmsg for file descriptors. const MAX_RECV_FDS_LEN: usize = MAX_RECV_BUF_LEN; -pub(crate) const LEAK_BUCKET_LIMIT: u64 = 100; - -/// The wrapper over Unix socket and socket handler. -/// -/// # Example -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::os::unix::io::AsRawFd; -/// use std::os::unix::net::{UnixListener, UnixStream}; -/// -/// use machine_manager::socket::Socket; -/// -/// fn main() -> std::io::Result<()> { -/// let listener = UnixListener::bind("/path/to/my/socket")?; -/// let socket = Socket::from_unix_listener(listener, None); -/// assert!(!socket.is_connected()); -/// -/// let client_stream = UnixStream::connect("/path/to/my/socket")?; -/// let server_stream = socket.accept_unix_stream(); -/// socket.bind_unix_stream(server_stream); -/// assert!(socket.is_connected()); -/// Ok(()) -/// } -/// ``` -pub struct Socket { - /// Type for Socket - sock_type: SocketType, - /// Socket listener tuple - listener: UnixListener, - /// Socket stream with RwLock - stream: RwLock>, - /// Perform socket command - performer: Option>>, -} - -impl Socket { - /// Allocates a new `Socket` with `UnixListener`. - /// - /// # Arguments - /// - /// * `listener` - The `UnixListener` bind to `Socket`. - /// * `performer` - The `VM` to perform socket command. - pub fn from_unix_listener( - listener: UnixListener, - performer: Option>>, - ) -> Self { - Socket { - sock_type: SocketType::Unix, - listener, - stream: RwLock::new(None), - performer, - } - } - - /// Get listener's fd from `Socket`. - pub fn get_listener_fd(&self) -> RawFd { - self.listener.as_raw_fd() - } - - /// Accept stream and bind to Socket. - pub fn accept(&self) { - match self.sock_type { - SocketType::Unix => { - let stream = self.accept_unix_stream(); - self.bind_unix_stream(stream); - } - } - } - - /// Accept a new incoming connection unix stream from unix listener. - pub fn accept_unix_stream(&self) -> UnixStream { - let (stream, _) = self.listener.accept().unwrap(); - stream - } - - /// Get socket type from `Socket`. - pub fn get_socket_type(&self) -> SocketType { - self.sock_type - } - - /// Bind `Socket` with a `UnixStream`. - /// - /// # Arguments - /// - /// * `unix_stream` - The `UnixStream` bind to `Socket`. - pub fn bind_unix_stream(&self, unix_stream: UnixStream) { - let stream = SocketStream::from_unix_stream(unix_stream); - *self.stream.write().unwrap() = Some(stream); - } - - /// Unbind stream from `Socket`, reset the state. - pub fn drop_stream(&self) { - *self.stream.write().unwrap() = None; - } - - /// Confirm whether socket stream bind to `Socket` or not. - pub fn is_connected(&self) -> bool { - self.stream.read().unwrap().is_some() - } - - /// Get socket fd from `Socket`, it a private function. - pub fn get_stream_fd(&self) -> RawFd { - if self.is_connected() { - self.stream.read().unwrap().as_ref().unwrap().as_raw_fd() - } else { - panic!("Failed to get socket fd!"); - } - } - - /// Get a `SocketHandler` from `Socket`. - pub fn get_socket_handler(&self) -> SocketHandler { - SocketHandler::new(self.get_stream_fd()) - } - - /// In qmp feature, send empty or greeting response to client. - /// - /// # Arguments - /// - /// * `is_greeting` - Whether sending greeting response or not. - pub fn send_response(&self, is_greeting: bool) -> std::io::Result<()> { - if self.is_connected() { - let mut handler = self.get_socket_handler(); - let resp = if is_greeting { - serde_json::to_string(&QmpGreeting::create_greeting(1, 0, 5)).unwrap() - } else { - serde_json::to_string(&Response::create_empty_response()).unwrap() - }; - handler.send_str(&resp)?; - info!("QMP: --> {:?}", resp); - } - Ok(()) - } - - /// Create socket's accepted stream to `event_notifier`. - fn create_event_notifier(&mut self, shared_socket: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let leak_bucket = LeakBucket::new(LEAK_BUCKET_LIMIT); - if let Err(e) = leak_bucket { - error!("Failed to create leak bucket, {:?}", e); - return notifiers; - } - let leak_bucket = Arc::new(Mutex::new(leak_bucket.unwrap())); - let shared_leak_bucket = leak_bucket.clone(); - let leak_bucket_fd = leak_bucket.lock().unwrap().as_raw_fd(); - - self.accept(); - QmpChannel::bind_writer(SocketRWHandler::new(self.get_stream_fd())); - if let Err(e) = self.send_response(true) { - error!("{:?}", e); - QmpChannel::unbind(); - return notifiers; - } - let handler: Rc = Rc::new(move |event, _| { - if event == EventSet::IN { - let socket_mutexed = shared_socket.lock().unwrap(); - let stream_fd = socket_mutexed.get_stream_fd(); - - let performer = &socket_mutexed.performer.as_ref().unwrap(); - if let Err(e) = crate::qmp::handle_qmp( - stream_fd, - performer, - &mut shared_leak_bucket.lock().unwrap(), - ) { - error!("{:?}", e); - } - } - if event & EventSet::HANG_UP == EventSet::HANG_UP { - let socket_mutexed = shared_socket.lock().unwrap(); - let stream_fd = socket_mutexed.get_stream_fd(); - - QmpChannel::unbind(); - Some(gen_delete_notifiers(&[stream_fd, leak_bucket_fd])) - } else { - None - } - }); - let qmp_notifier = EventNotifier::new( - NotifierOperation::AddShared, - self.get_stream_fd(), - Some(self.get_listener_fd()), - EventSet::IN | EventSet::HANG_UP, - vec![handler], - ); - notifiers.push(qmp_notifier); - - let leak_bucket_notifier = EventNotifier::new( - NotifierOperation::AddShared, - leak_bucket_fd, - None, - EventSet::IN, - vec![Rc::new(move |_, fd| { - read_fd(fd); - leak_bucket.lock().unwrap().clear_timer(); - None - })], - ); - notifiers.push(leak_bucket_notifier); - - notifiers - } -} - -impl EventNotifierHelper for Socket { - fn internal_notifiers(shared_socket: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let socket = shared_socket.clone(); - let handler: Rc = - Rc::new(move |_, _| Some(socket.lock().unwrap().create_event_notifier(socket.clone()))); - let notifier = EventNotifier::new( - NotifierOperation::AddShared, - shared_socket.lock().unwrap().get_listener_fd(), - None, - EventSet::IN, - vec![handler], - ); - notifiers.push(notifier); - - notifiers - } -} - -/// Type for api socket. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SocketType { - Unix = 1, -} - -/// Wrapper over UnixSteam. -#[derive(Debug)] -struct SocketStream(UnixStream); - -impl SocketStream { - fn from_unix_stream(stream: UnixStream) -> Self { - SocketStream(stream) - } -} - -impl AsRawFd for SocketStream { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} /// Wrapper over socket file description read and write message. /// @@ -634,7 +376,7 @@ mod tests { use serde::{Deserialize, Serialize}; - use super::{Socket, SocketHandler, SocketRWHandler, SocketType}; + use crate::socket::{SocketHandler, SocketRWHandler}; // Environment Preparation for UnixSocket fn prepare_unix_socket_environment(socket_id: &str) -> (UnixListener, UnixStream, UnixStream) { @@ -806,33 +548,4 @@ mod tests { // After test. Environment Recover recover_unix_socket_environment("03"); } - - #[test] - fn test_socket_lifecycle() { - // Pre test. Environment Preparation - let (listener, _, server) = prepare_unix_socket_environment("04"); - let socket = Socket::from_unix_listener(listener, None); - - // life cycle test - // 1.Unconnected - assert_eq!(socket.is_connected(), false); - - // 2.Connected - socket.bind_unix_stream(server); - assert_eq!(socket.is_connected(), true); - assert_eq!(socket.get_socket_type(), SocketType::Unix); - - // 3.Unbind SocketStream, reset state - socket.drop_stream(); - assert_eq!(socket.is_connected(), false); - - // 4.Accept and reconnect a new UnixStream - let _new_client = UnixStream::connect("test_04.sock"); - let new_server = socket.accept_unix_stream(); - socket.bind_unix_stream(new_server); - assert_eq!(socket.is_connected(), true); - - // After test. Environment Recover - recover_unix_socket_environment("04"); - } } diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 112fc6f57..c7243aea1 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -32,7 +32,7 @@ use std::{net::TcpStream, os::unix::net::UnixStream, thread}; use log::error; -use machine_manager::qmp::{qmp_schema, Response}; +use machine_manager::qmp::{qmp_response::Response, qmp_schema}; /// Start to snapshot VM. /// diff --git a/src/main.rs b/src/main.rs index cc81afe6f..4afc90e9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,9 +23,9 @@ use machine_manager::{ config::MachineType, config::VmConfig, event_loop::EventLoop, - qmp::QmpChannel, + qmp::qmp_channel::QmpChannel, + qmp::qmp_socket::Socket, signal_handler::{exit_with_code, register_kill_signal, VM_EXIT_GENE_ERR}, - socket::Socket, temp_cleaner::TempCleaner, test_server::TestSock, }; diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 7e5e0ae63..c05c599b7 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -36,8 +36,8 @@ use machine_manager::{ config::{BalloonConfig, DEFAULT_VIRTQUEUE_SIZE}, event, event_loop::{register_event_helper, unregister_event_helper}, + qmp::qmp_channel::QmpChannel, qmp::qmp_schema::BalloonInfo, - qmp::QmpChannel, }; use util::{ bitmap::Bitmap, -- Gitee From 10bb7045955af203a3cdb0bbbdcc0b957b97e2b9 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:01:52 +0800 Subject: [PATCH 1341/2187] devices: Delete useless trait implementation The trait is useful only for last level device. Signed-off-by: Keqian Zhu --- devices/src/pci/mod.rs | 10 ---------- devices/src/sysbus/mod.rs | 10 ---------- devices/src/usb/mod.rs | 12 +----------- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 7ee6bf117..4899fb9f9 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -142,16 +142,6 @@ pub struct PciDevBase { pub parent_bus: Weak>, } -impl Device for PciDevBase { - fn device_base(&self) -> &DeviceBase { - &self.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base - } -} - pub trait PciDevOps: Device + Send + AsAny { /// Get base property of pci device. fn pci_base(&self) -> &PciDevBase; diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 73670dba6..a32792612 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -228,16 +228,6 @@ pub struct SysBusDevBase { pub interrupt_evt: Option>, } -impl Device for SysBusDevBase { - fn device_base(&self) -> &DeviceBase { - &self.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base - } -} - impl Default for SysBusDevBase { fn default() -> Self { SysBusDevBase { diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 195c03e83..703747fa1 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -37,7 +37,7 @@ use anyhow::{bail, Context}; use log::{debug, error}; use self::descriptor::USB_MAX_INTERFACES; -use crate::{Device, DeviceBase}; +use crate::DeviceBase; use config::*; use descriptor::{UsbDescriptor, UsbDescriptorOps}; use machine_manager::qmp::qmp_channel::send_device_deleted_msg; @@ -127,16 +127,6 @@ pub struct UsbDevice { pub altsetting: [u32; USB_MAX_INTERFACES as usize], } -impl Device for UsbDevice { - fn device_base(&self) -> &DeviceBase { - &self.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base - } -} - impl UsbDevice { pub fn new(id: String, data_buf_len: usize) -> Self { let mut dev = UsbDevice { -- Gitee From 16270a3ca1bf087a35748fc6d5765a2feabe1461 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:29:41 +0800 Subject: [PATCH 1342/2187] usb_device: Unify the naming of device trait Signed-off-by: Keqian Zhu --- devices/src/usb/camera.rs | 51 ++++++++++---------- devices/src/usb/descriptor.rs | 4 +- devices/src/usb/keyboard.rs | 52 ++++++++++----------- devices/src/usb/mod.rs | 42 ++++++++--------- devices/src/usb/storage.rs | 45 +++++++++--------- devices/src/usb/tablet.rs | 55 ++++++++++------------ devices/src/usb/usbhost/mod.rs | 62 +++++++++++-------------- devices/src/usb/xhci/xhci_controller.rs | 12 ++--- devices/src/usb/xhci/xhci_pci.rs | 6 +-- machine/src/lib.rs | 4 +- 10 files changed, 157 insertions(+), 176 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 12dce9df4..e25a1dbe4 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -32,7 +32,7 @@ use crate::camera_backend::{ use crate::usb::config::*; use crate::usb::descriptor::*; use crate::usb::{ - UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, + UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; use machine_manager::config::UsbCameraConfig; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -93,7 +93,7 @@ const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; const USB_CAMERA_BUFFER_LEN: usize = 12 * 1024; pub struct UsbCamera { - usb_device: UsbDevice, // general usb device object + base: UsbDeviceBase, // general usb device object vs_control: VideoStreamingControl, // video stream control info camera_fd: Arc, // camera io fd camera_dev: Arc>, // backend device @@ -493,7 +493,7 @@ impl UsbCamera { pub fn new(config: UsbCameraConfig) -> Result { let camera = camera_ops(config.clone())?; Ok(Self { - usb_device: UsbDevice::new(config.id.unwrap(), USB_CAMERA_BUFFER_LEN), + base: UsbDeviceBase::new(config.id.unwrap(), USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), camera_dev: camera, @@ -600,8 +600,8 @@ impl UsbCamera { match device_req.request_type { USB_INTERFACE_IN_REQUEST => { if device_req.request == USB_REQUEST_GET_STATUS { - self.usb_device.data_buf[0] = 0; - self.usb_device.data_buf[1] = 0; + self.base.data_buf[0] = 0; + self.base.data_buf[1] = 0; packet.actual_length = 2; return Ok(()); } @@ -640,7 +640,7 @@ impl UsbCamera { ) -> Result<()> { match device_req.request { GET_INFO => { - self.usb_device.data_buf[0] = 1 | 2; + self.base.data_buf[0] = 1 | 2; packet.actual_length = 1; } GET_CUR | GET_MIN | GET_MAX | GET_DEF => { @@ -666,7 +666,7 @@ impl UsbCamera { bail!("Invalid VS Control Selector {}", cs); } let len = self.vs_control.as_bytes().len(); - self.usb_device.data_buf[0..len].copy_from_slice(self.vs_control.as_bytes()); + self.base.data_buf[0..len].copy_from_slice(self.vs_control.as_bytes()); packet.actual_length = len as u32; Ok(()) } @@ -676,7 +676,7 @@ impl UsbCamera { let len = vs_control.as_mut_bytes().len(); vs_control .as_mut_bytes() - .copy_from_slice(&self.usb_device.data_buf[0..len]); + .copy_from_slice(&self.base.data_buf[0..len]); let cs = (device_req.value >> 8) as u8; debug!("VideoStreamingControl {} {:?}", cs, vs_control); match device_req.request { @@ -737,19 +737,26 @@ impl UsbCamera { } } -impl UsbDeviceOps for UsbCamera { - fn realize(mut self) -> Result>> { +impl UsbDevice for UsbCamera { + fn usb_device_base(&self) -> &UsbDeviceBase { + &self.base + } + + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { + &mut self.base + } + + fn realize(mut self) -> Result>> { let fmt_list = self.camera_dev.lock().unwrap().list_format()?; - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_SUPER; + self.base.reset_usb_endpoint(); + self.base.speed = USB_SPEED_SUPER; let mut s: Vec = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); let prefix = &s[UsbCameraStringIDs::SerialNumber as usize]; - s[UsbCameraStringIDs::SerialNumber as usize] = - self.usb_device.generate_serial_number(prefix); + s[UsbCameraStringIDs::SerialNumber as usize] = self.base.generate_serial_number(prefix); let iad = &s[UsbCameraStringIDs::Iad as usize]; s[UsbCameraStringIDs::Iad as usize] = self.generate_iad(iad); let device_desc = gen_desc_device_camera(fmt_list)?; - self.usb_device.init_descriptor(device_desc, s)?; + self.base.init_descriptor(device_desc, s)?; self.register_cb(); let camera = Arc::new(Mutex::new(self)); @@ -764,7 +771,7 @@ impl UsbDeviceOps for UsbCamera { fn reset(&mut self) { info!("Camera {} device reset", self.device_id()); - self.usb_device.addr = 0; + self.base.addr = 0; if let Err(e) = self.unregister_camera_fd() { error!("Failed to unregister fd when reset {:?}", e); } @@ -778,7 +785,7 @@ impl UsbDeviceOps for UsbCamera { fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { let mut locked_packet = packet.lock().unwrap(); match self - .usb_device + .base .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { @@ -832,15 +839,7 @@ impl UsbDeviceOps for UsbCamera { } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.usb_device.get_endpoint(true, 1) - } - - fn get_usb_device(&self) -> &UsbDevice { - &self.usb_device - } - - fn get_mut_usb_device(&mut self) -> &mut UsbDevice { - &mut self.usb_device + self.base.get_endpoint(true, 1) } } diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 46c45484c..8005f1102 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -15,7 +15,7 @@ use std::sync::Arc; use anyhow::{bail, Context, Result}; use super::config::*; -use super::UsbDevice; +use super::UsbDeviceBase; use util::byte_code::ByteCode; pub const USB_MAX_INTERFACES: u32 = 16; @@ -473,7 +473,7 @@ pub trait UsbDescriptorOps { fn init_descriptor(&mut self, desc: Arc, str: Vec) -> Result<()>; } -impl UsbDescriptorOps for UsbDevice { +impl UsbDescriptorOps for UsbDeviceBase { fn get_descriptor(&self, value: u32) -> Result> { let desc_type = value >> USB_DESCRIPTOR_TYPE_SHIFT; let index = value & USB_DESCRIPTOR_INDEX_MASK; diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 37cefbb0c..d2367d663 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -24,7 +24,7 @@ use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; use super::xhci::xhci_controller::XhciDevice; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{ - notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, + notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; use ui::input::{register_keyboard, unregister_keyboard, KeyboardOpts}; @@ -120,7 +120,7 @@ const DESC_STRINGS: [&str; 5] = [ /// USB keyboard device. pub struct UsbKeyboard { - usb_device: UsbDevice, + base: UsbDeviceBase, hid: Hid, /// USB controller used to notify controller to transfer data. cntlr: Option>>, @@ -157,29 +157,36 @@ impl KeyboardOpts for UsbKeyboardAdapter { } drop(locked_kbd); let clone_kbd = self.usb_kbd.clone(); - notify_controller(&(clone_kbd as Arc>)) + notify_controller(&(clone_kbd as Arc>)) } } impl UsbKeyboard { pub fn new(id: String) -> Self { Self { - usb_device: UsbDevice::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), + base: UsbDeviceBase::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Keyboard), cntlr: None, } } } -impl UsbDeviceOps for UsbKeyboard { - fn realize(mut self) -> Result>> { - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_FULL; +impl UsbDevice for UsbKeyboard { + fn usb_device_base(&self) -> &UsbDeviceBase { + &self.base + } + + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { + &mut self.base + } + + fn realize(mut self) -> Result>> { + self.base.reset_usb_endpoint(); + self.base.speed = USB_SPEED_FULL; let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); let prefix = &s[STR_SERIAL_KEYBOARD_INDEX as usize]; - s[STR_SERIAL_KEYBOARD_INDEX as usize] = self.usb_device.generate_serial_number(prefix); - self.usb_device - .init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; + s[STR_SERIAL_KEYBOARD_INDEX as usize] = self.base.generate_serial_number(prefix); + self.base.init_descriptor(DESC_DEVICE_KEYBOARD.clone(), s)?; let id = self.device_id().to_string(); let kbd = Arc::new(Mutex::new(self)); let kbd_adapter = Arc::new(Mutex::new(UsbKeyboardAdapter { @@ -197,8 +204,8 @@ impl UsbDeviceOps for UsbKeyboard { fn reset(&mut self) { info!("Keyboard device reset"); - self.usb_device.remote_wakeup = 0; - self.usb_device.addr = 0; + self.base.remote_wakeup = 0; + self.base.addr = 0; self.hid.reset(); } @@ -206,7 +213,7 @@ impl UsbDeviceOps for UsbKeyboard { debug!("handle_control request {:?}", device_req); let mut locked_packet = packet.lock().unwrap(); match self - .usb_device + .base .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { @@ -221,11 +228,8 @@ impl UsbDeviceOps for UsbKeyboard { return; } } - self.hid.handle_control_packet( - &mut locked_packet, - device_req, - &mut self.usb_device.data_buf, - ); + self.hid + .handle_control_packet(&mut locked_packet, device_req, &mut self.base.data_buf); } fn handle_data(&mut self, p: &Arc>) { @@ -233,14 +237,6 @@ impl UsbDeviceOps for UsbKeyboard { self.hid.handle_data_packet(&mut locked_p); } - fn get_usb_device(&self) -> &UsbDevice { - &self.usb_device - } - - fn get_mut_usb_device(&mut self) -> &mut UsbDevice { - &mut self.usb_device - } - fn set_controller(&mut self, cntlr: Weak>) { self.cntlr = Some(cntlr); } @@ -250,6 +246,6 @@ impl UsbDeviceOps for UsbKeyboard { } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.usb_device.get_endpoint(true, 1) + self.base.get_endpoint(true, 1) } } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 703747fa1..b7ea219ef 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -109,7 +109,7 @@ impl UsbEndpoint { } /// USB device common structure. -pub struct UsbDevice { +pub struct UsbDeviceBase { pub base: DeviceBase, pub port: Option>>, pub speed: u32, @@ -127,9 +127,9 @@ pub struct UsbDevice { pub altsetting: [u32; USB_MAX_INTERFACES as usize], } -impl UsbDevice { +impl UsbDeviceBase { pub fn new(id: String, data_buf_len: usize) -> Self { - let mut dev = UsbDevice { + let mut dev = UsbDeviceBase { base: DeviceBase::new(id, false), port: None, speed: 0, @@ -321,7 +321,7 @@ impl UsbDevice { } } -impl Drop for UsbDevice { +impl Drop for UsbDeviceBase { fn drop(&mut self) { if self.unplugged { send_device_deleted_msg(&self.base.id); @@ -329,11 +329,17 @@ impl Drop for UsbDevice { } } -/// UsbDeviceOps is the interface for USB device. +/// UsbDevice is the interface for USB device. /// Include device handle attach/detach and the transfer between controller and device. -pub trait UsbDeviceOps: Send + Sync { +pub trait UsbDevice: Send + Sync { + /// Get the UsbDeviceBase. + fn usb_device_base(&self) -> &UsbDeviceBase; + + /// Get the mut UsbDeviceBase. + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase; + /// Realize the USB device. - fn realize(self) -> Result>>; + fn realize(self) -> Result>>; /// Unrealize the USB device. fn unrealize(&mut self) -> Result<()> { @@ -341,7 +347,7 @@ pub trait UsbDeviceOps: Send + Sync { } /// Handle the attach ops when attach device to controller. fn handle_attach(&mut self) -> Result<()> { - let usb_dev = self.get_mut_usb_device(); + let usb_dev = self.usb_device_base_mut(); usb_dev.set_config_descriptor(0)?; Ok(()) } @@ -361,7 +367,7 @@ pub trait UsbDeviceOps: Send + Sync { /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { - let usb_dev = self.get_mut_usb_device(); + let usb_dev = self.usb_device_base_mut(); usb_dev.port = port; } @@ -389,23 +395,17 @@ pub trait UsbDeviceOps: Send + Sync { /// Unique device id. fn device_id(&self) -> &str { - &self.get_usb_device().base.id + &self.usb_device_base().base.id } - /// Get the UsbDevice. - fn get_usb_device(&self) -> &UsbDevice; - - /// Get the mut UsbDevice. - fn get_mut_usb_device(&mut self) -> &mut UsbDevice; - /// Get the device speed. fn speed(&self) -> u32 { - let usb_dev = self.get_usb_device(); + let usb_dev = self.usb_device_base(); usb_dev.speed } fn do_parameter(&mut self, packet: &Arc>) -> Result<()> { - let usb_dev = self.get_mut_usb_device(); + let usb_dev = self.usb_device_base_mut(); let mut locked_p = packet.lock().unwrap(); let device_req = UsbDeviceRequest { request_type: locked_p.parameter as u8, @@ -424,7 +424,7 @@ pub trait UsbDeviceOps: Send + Sync { drop(locked_p); self.handle_control(packet, &device_req); let mut locked_p = packet.lock().unwrap(); - let usb_dev = self.get_mut_usb_device(); + let usb_dev = self.usb_device_base_mut(); if locked_p.is_async { return Ok(()); } @@ -441,14 +441,14 @@ pub trait UsbDeviceOps: Send + Sync { } /// Notify controller to process data request. -pub fn notify_controller(dev: &Arc>) -> Result<()> { +pub fn notify_controller(dev: &Arc>) -> Result<()> { let locked_dev = dev.lock().unwrap(); let xhci = if let Some(cntlr) = &locked_dev.get_controller() { cntlr.upgrade().unwrap() } else { bail!("USB controller not found"); }; - let usb_dev = locked_dev.get_usb_device(); + let usb_dev = locked_dev.usb_device_base(); let usb_port = if let Some(port) = &usb_dev.port { port.upgrade().unwrap() } else { diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 652463c50..be1473ca2 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -26,7 +26,7 @@ use super::descriptor::{ }; use super::xhci::xhci_controller::XhciDevice; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; -use super::{UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; +use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; use crate::{ ScsiBus::{ ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD, @@ -223,7 +223,7 @@ impl UsbStorageState { /// USB storage device. pub struct UsbStorage { - usb_device: UsbDevice, + base: UsbDeviceBase, state: UsbStorageState, /// USB controller used to notify controller to transfer data. cntlr: Option>>, @@ -314,7 +314,7 @@ impl UsbStorage { }; Self { - usb_device: UsbDevice::new(config.id.clone().unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN), + base: UsbDeviceBase::new(config.id.clone().unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN), state: UsbStorageState::new(), cntlr: None, config: config.clone(), @@ -344,7 +344,7 @@ impl UsbStorage { if device_req.request == GET_MAX_LUN { // TODO: Now only supports 1 LUN. let maxlun = USB_STORAGE_SCSI_LUN_ID; - self.usb_device.data_buf[0] = maxlun; + self.base.data_buf[0] = maxlun; packet.actual_length = 1; return; } @@ -509,15 +509,22 @@ impl UsbStorage { } } -impl UsbDeviceOps for UsbStorage { - fn realize(mut self) -> Result>> { - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_HIGH; +impl UsbDevice for UsbStorage { + fn usb_device_base(&self) -> &UsbDeviceBase { + &self.base + } + + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { + &mut self.base + } + + fn realize(mut self) -> Result>> { + self.base.reset_usb_endpoint(); + self.base.speed = USB_SPEED_HIGH; let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); let prefix = &s[STR_SERIAL_STORAGE_INDEX as usize]; - s[STR_SERIAL_STORAGE_INDEX as usize] = self.usb_device.generate_serial_number(prefix); - self.usb_device - .init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; + s[STR_SERIAL_STORAGE_INDEX as usize] = self.base.generate_serial_number(prefix); + self.base.init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. @@ -536,8 +543,8 @@ impl UsbDeviceOps for UsbStorage { fn reset(&mut self) { info!("Storage device reset"); - self.usb_device.remote_wakeup = 0; - self.usb_device.addr = 0; + self.base.remote_wakeup = 0; + self.base.addr = 0; self.state = UsbStorageState::new(); } @@ -545,7 +552,7 @@ impl UsbDeviceOps for UsbStorage { debug!("Storage device handle_control request {:?}, ", device_req); let mut locked_packet = packet.lock().unwrap(); match self - .usb_device + .base .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { @@ -585,14 +592,6 @@ impl UsbDeviceOps for UsbStorage { } } - fn get_usb_device(&self) -> &UsbDevice { - &self.usb_device - } - - fn get_mut_usb_device(&mut self) -> &mut UsbDevice { - &mut self.usb_device - } - fn set_controller(&mut self, cntlr: Weak>) { self.cntlr = Some(cntlr); } @@ -602,6 +601,6 @@ impl UsbDeviceOps for UsbStorage { } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.usb_device.get_endpoint(true, 1) + self.base.get_endpoint(true, 1) } } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 2650a0ed9..d5c2fbae7 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -23,10 +23,9 @@ use super::descriptor::{ }; use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; use super::xhci::xhci_controller::XhciDevice; -use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{ - notify_controller, UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, - UsbPacketStatus, + config::*, notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, + UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; use ui::input::{ register_pointer, unregister_pointer, PointerOpts, INPUT_BUTTON_WHEEL_DOWN, @@ -113,7 +112,7 @@ const STR_SERIAL_TABLET_INDEX: u8 = 4; const DESC_STRINGS: [&str; 5] = ["", "StratoVirt", "StratoVirt USB Tablet", "HID Tablet", "2"]; /// USB tablet device. pub struct UsbTablet { - usb_device: UsbDevice, + base: UsbDeviceBase, hid: Hid, /// USB controller used to notify controller to transfer data. cntlr: Option>>, @@ -122,7 +121,7 @@ pub struct UsbTablet { impl UsbTablet { pub fn new(id: String) -> Self { Self { - usb_device: UsbDevice::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), + base: UsbDeviceBase::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Tablet), cntlr: None, } @@ -163,19 +162,26 @@ impl PointerOpts for UsbTabletAdapter { locked_tablet.hid.num += 1; drop(locked_tablet); let clone_tablet = self.tablet.clone(); - notify_controller(&(clone_tablet as Arc>)) + notify_controller(&(clone_tablet as Arc>)) } } -impl UsbDeviceOps for UsbTablet { - fn realize(mut self) -> Result>> { - self.usb_device.reset_usb_endpoint(); - self.usb_device.speed = USB_SPEED_FULL; +impl UsbDevice for UsbTablet { + fn usb_device_base(&self) -> &UsbDeviceBase { + &self.base + } + + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { + &mut self.base + } + + fn realize(mut self) -> Result>> { + self.base.reset_usb_endpoint(); + self.base.speed = USB_SPEED_FULL; let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); let prefix = &s[STR_SERIAL_TABLET_INDEX as usize]; - s[STR_SERIAL_TABLET_INDEX as usize] = self.usb_device.generate_serial_number(prefix); - self.usb_device - .init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; + s[STR_SERIAL_TABLET_INDEX as usize] = self.base.generate_serial_number(prefix); + self.base.init_descriptor(DESC_DEVICE_TABLET.clone(), s)?; let id = self.device_id().to_string(); let tablet = Arc::new(Mutex::new(self)); let tablet_adapter = Arc::new(Mutex::new(UsbTabletAdapter { @@ -192,8 +198,8 @@ impl UsbDeviceOps for UsbTablet { fn reset(&mut self) { info!("Tablet device reset"); - self.usb_device.remote_wakeup = 0; - self.usb_device.addr = 0; + self.base.remote_wakeup = 0; + self.base.addr = 0; self.hid.reset(); } @@ -201,7 +207,7 @@ impl UsbDeviceOps for UsbTablet { debug!("handle_control request {:?}", device_req); let mut locked_packet = packet.lock().unwrap(); match self - .usb_device + .base .handle_control_for_descriptor(&mut locked_packet, device_req) { Ok(handled) => { @@ -216,11 +222,8 @@ impl UsbDeviceOps for UsbTablet { return; } } - self.hid.handle_control_packet( - &mut locked_packet, - device_req, - &mut self.usb_device.data_buf, - ); + self.hid + .handle_control_packet(&mut locked_packet, device_req, &mut self.base.data_buf); } fn handle_data(&mut self, p: &Arc>) { @@ -228,14 +231,6 @@ impl UsbDeviceOps for UsbTablet { self.hid.handle_data_packet(&mut locked_p); } - fn get_usb_device(&self) -> &UsbDevice { - &self.usb_device - } - - fn get_mut_usb_device(&mut self) -> &mut UsbDevice { - &mut self.usb_device - } - fn set_controller(&mut self, cntlr: Weak>) { self.cntlr = Some(cntlr); } @@ -245,6 +240,6 @@ impl UsbDeviceOps for UsbTablet { } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.usb_device.get_endpoint(true, 1) + self.base.get_endpoint(true, 1) } } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index dd3e4270b..879273e17 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -41,7 +41,7 @@ use crate::usb::{ }, descriptor::USB_MAX_INTERFACES, xhci::xhci_controller::XhciDevice, - UsbDevice, UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, + UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; use host_usblib::*; use machine_manager::{ @@ -332,6 +332,7 @@ impl IsoQueue { /// Abstract object of the host USB device. pub struct UsbHost { + base: UsbDeviceBase, config: UsbHostConfig, /// Libusb context. context: Context, @@ -346,7 +347,6 @@ pub struct UsbHost { /// Configuration interface number. ifs_num: u8, ifs: [InterfaceStatus; USB_MAX_INTERFACES as usize], - usb_device: UsbDevice, /// Callback for release dev to Host after the vm exited. exit: Option>, /// All pending asynchronous usb request. @@ -378,7 +378,7 @@ impl UsbHost { libevt: Vec::new(), ifs_num: 0, ifs: [InterfaceStatus::default(); USB_MAX_INTERFACES as usize], - usb_device: UsbDevice::new(id, USB_HOST_BUFFER_LEN), + base: UsbDeviceBase::new(id, USB_HOST_BUFFER_LEN), exit: None, requests: Arc::new(Mutex::new(List::new())), iso_queues: Arc::new(Mutex::new(LinkedList::new())), @@ -517,7 +517,7 @@ impl UsbHost { } fn ep_update(&mut self) { - self.usb_device.reset_usb_endpoint(); + self.base.reset_usb_endpoint(); let conf = match self.libdev.as_ref().unwrap().active_config_descriptor() { Ok(conf) => conf, Err(_) => return, @@ -531,8 +531,7 @@ impl UsbHost { if intf_desc.is_none() { continue; } - let alt = - self.usb_device.altsetting[intf_desc.as_ref().unwrap().interface_number() as usize]; + let alt = self.base.altsetting[intf_desc.as_ref().unwrap().interface_number() as usize]; if alt != 0 { if alt >= intf.descriptors().count() as u32 { error!( @@ -558,12 +557,11 @@ impl UsbHost { return; } let in_direction = pid == USB_TOKEN_IN; - if self.usb_device.get_endpoint(in_direction, ep_num).ep_type - != USB_ENDPOINT_ATTR_INVALID + if self.base.get_endpoint(in_direction, ep_num).ep_type != USB_ENDPOINT_ATTR_INVALID { error!("duplicate endpoint address") } - let usb_ep = self.usb_device.get_mut_endpoint(in_direction, ep_num); + let usb_ep = self.base.get_mut_endpoint(in_direction, ep_num); usb_ep.set_max_packet_size(ep.max_packet_size()); usb_ep.ep_type = ep_type; usb_ep.ifnum = i as u8; @@ -581,7 +579,7 @@ impl UsbHost { self.ep_update(); - self.usb_device.speed = self.libdev.as_ref().unwrap().speed() as u32 - 1; + self.base.speed = self.libdev.as_ref().unwrap().speed() as u32 - 1; Ok(()) } @@ -613,7 +611,7 @@ impl UsbHost { } fn claim_interfaces(&mut self) -> UsbPacketStatus { - self.usb_device.altsetting = [0; USB_MAX_INTERFACES as usize]; + self.base.altsetting = [0; USB_MAX_INTERFACES as usize]; if self.detach_kernel().is_err() { return UsbPacketStatus::Stall; } @@ -687,7 +685,7 @@ impl UsbHost { .set_alternate_setting(iface as u8, alt as u8) { Ok(_) => { - self.usb_device.altsetting[iface as usize] = alt as u32; + self.base.altsetting[iface as usize] = alt as u32; self.ep_update(); } Err(e) => { @@ -704,7 +702,7 @@ impl UsbHost { if self.handle.as_mut().unwrap().clear_halt(index).is_err() { warn!("Failed to clear halt"); } - self.usb_device + self.base .get_mut_endpoint(pid == USB_TOKEN_IN, index & 0x0f) .halted = false; } @@ -780,7 +778,7 @@ impl UsbHost { let iso_queue = Arc::new(Mutex::new(IsoQueue::new(locked_packet.ep_number))); let cloned_iso_queue = iso_queue.clone(); let ep = self - .usb_device + .base .get_endpoint(in_direction, locked_packet.ep_number); let id = self.device_id().to_string(); match iso_queue.lock().unwrap().realize( @@ -805,7 +803,7 @@ impl UsbHost { let in_direction = locked_packet.pid == USB_TOKEN_IN as u32; let ep = self - .usb_device + .base .get_endpoint(in_direction, locked_packet.ep_number); drop(locked_packet); @@ -911,8 +909,16 @@ impl EventNotifierHelper for UsbHost { } } -impl UsbDeviceOps for UsbHost { - fn realize(mut self) -> Result>> { +impl UsbDevice for UsbHost { + fn usb_device_base(&self) -> &UsbDeviceBase { + &self.base + } + + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { + &mut self.base + } + + fn realize(mut self) -> Result>> { self.libdev = self.find_libdev(); if self.libdev.is_none() { bail!("Invalid USB host config: {:?}", self.config); @@ -958,7 +964,7 @@ impl UsbDeviceOps for UsbHost { } fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.usb_device.get_endpoint(true, 1) + self.base.get_endpoint(true, 1) } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { @@ -970,7 +976,7 @@ impl UsbDeviceOps for UsbHost { match device_req.request_type { USB_DEVICE_OUT_REQUEST => { if device_req.request == USB_REQUEST_SET_ADDRESS { - self.usb_device.addr = device_req.value as u8; + self.base.addr = device_req.value as u8; return; } else if device_req.request == USB_REQUEST_SET_CONFIGURATION { self.set_config(device_req.value as u8, &mut locked_packet); @@ -1001,7 +1007,7 @@ impl UsbDeviceOps for UsbHost { true, ))); node.value.setup_ctrl_buffer( - &self.usb_device.data_buf[..device_req.length as usize], + &self.base.data_buf[..device_req.length as usize], device_req, ); @@ -1028,7 +1034,7 @@ impl UsbDeviceOps for UsbHost { } let in_direction = locked_packet.pid as u8 == USB_TOKEN_IN; if self - .usb_device + .base .get_endpoint(in_direction, locked_packet.ep_number) .halted { @@ -1040,11 +1046,7 @@ impl UsbDeviceOps for UsbHost { let mut ep_number = packet.lock().unwrap().ep_number; let host_transfer: *mut libusb_transfer; - match self - .usb_device - .get_endpoint(in_direction, ep_number) - .ep_type - { + match self.base.get_endpoint(in_direction, ep_number).ep_type { USB_ENDPOINT_ATTR_BULK => { host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); let mut node = Box::new(Node::new(UsbHostRequest::new( @@ -1104,14 +1106,6 @@ impl UsbDeviceOps for UsbHost { }; self.submit_host_transfer(host_transfer, packet); } - - fn get_usb_device(&self) -> &UsbDevice { - &self.usb_device - } - - fn get_mut_usb_device(&mut self) -> &mut UsbDevice { - &mut self.usb_device - } } fn check_device_valid(device: &Device) -> bool { diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 4a1edd656..857fd6855 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -29,9 +29,7 @@ use super::{ TRB_TYPE_SHIFT, }; use crate::usb::{config::*, TransferOps}; -use crate::usb::{ - UsbDeviceOps, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus, -}; +use crate::usb::{UsbDevice, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus}; use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::XhciConfig; use machine_manager::event_loop::EventLoop; @@ -461,7 +459,7 @@ pub struct UsbPort { /// Port ID pub port_id: u8, pub speed_mask: u32, - pub dev: Option>>, + pub dev: Option>>, pub used: bool, pub slot_id: u32, } @@ -1163,7 +1161,7 @@ impl XhciDevice { } /// Send SET_ADDRESS request to usb device. - fn set_device_address(&mut self, dev: &Arc>, addr: u32) { + fn set_device_address(&mut self, dev: &Arc>, addr: u32) { let mut locked_dev = dev.lock().unwrap(); let device_req = UsbDeviceRequest { request_type: USB_DEVICE_OUT_REQUEST, @@ -1918,7 +1916,7 @@ impl XhciDevice { Ok(Arc::new(Mutex::new(packet))) } - fn get_usb_dev(&self, slotid: u32, epid: u32) -> Result>> { + fn get_usb_dev(&self, slotid: u32, epid: u32) -> Result>> { let port = self.slots[(slotid - 1) as usize] .usb_port .as_ref() @@ -2043,7 +2041,7 @@ impl XhciDevice { /// Assign USB port and attach the device. pub fn assign_usb_port( &mut self, - dev: &Arc>, + dev: &Arc>, ) -> Option>> { let speed = dev.lock().unwrap().speed(); for port in &self.usb_ports { diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 9462aa0d2..9b9564882 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -33,7 +33,7 @@ use crate::pci::config::{ }; use crate::pci::msix::update_dev_id; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; -use crate::usb::UsbDeviceOps; +use crate::usb::UsbDevice; use crate::{Device, DeviceBase}; use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::XhciConfig; @@ -172,7 +172,7 @@ impl XhciPciDevice { }] } - pub fn attach_device(&self, dev: &Arc>) -> Result<()> { + pub fn attach_device(&self, dev: &Arc>) -> Result<()> { let mut locked_xhci = self.xhci.lock().unwrap(); let usb_port = locked_xhci .assign_usb_port(dev) @@ -204,7 +204,7 @@ impl XhciPciDevice { let mut locked_port = usb_port.lock().unwrap(); let dev = locked_port.dev.as_ref().unwrap(); let mut locked_dev = dev.lock().unwrap(); - locked_dev.get_mut_usb_device().unplugged = true; + locked_dev.usb_device_base_mut().unplugged = true; locked_dev.unrealize()?; drop(locked_dev); locked_xhci.discharge_usb_port(&mut locked_port); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f2f872b6b..2fe7bb89d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -59,7 +59,7 @@ use devices::usb::camera::UsbCamera; use devices::usb::usbhost::UsbHost; use devices::usb::{ keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, - UsbDeviceOps, + UsbDevice, }; #[cfg(target_arch = "aarch64")] use devices::InterruptController; @@ -1301,7 +1301,7 @@ pub trait MachineOps { fn attach_usb_to_xhci_controller( &mut self, vm_config: &mut VmConfig, - usb_dev: Arc>, + usb_dev: Arc>, ) -> Result<()> { let parent_dev = self .get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci") -- Gitee From bee409dea325f4181618c32c84341fb19de0cedc Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 4 Sep 2023 16:26:22 +0800 Subject: [PATCH 1343/2187] usb-host: add reset device function Some USB devices must call reset when hot-plug, otherwise Guest may not recognize them. Signed-off-by: Mingwang Li --- devices/src/usb/usbhost/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 209d06223..dd3e4270b 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -943,6 +943,12 @@ impl UsbDeviceOps for UsbHost { } self.clear_iso_queues(); + + self.handle + .as_mut() + .unwrap() + .reset() + .unwrap_or_else(|e| error!("Failed to reset the usb host device {:?}", e)); } fn set_controller(&mut self, _cntlr: std::sync::Weak>) {} -- Gitee From 5eee0e45b78fbdcac2f18307afc6633632c11413 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 5 Sep 2023 19:51:00 +0800 Subject: [PATCH 1344/2187] mem: Fix the logical order of backend memory configuration files and memfd If both the large page memory and shared properties are configured, the large page properties do not take effect. Signed-off-by: jiewangqun --- address_space/src/host_mmap.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index d22b45e82..917c398dd 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -244,7 +244,12 @@ fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Result { let mut f_back: Option = None; - if mem_config.mem_share { + if let Some(path) = &mem_config.mem_path { + f_back = Some( + FileBackend::new_mem(path, mem_config.mem_size) + .with_context(|| "Failed to create file that backs memory")?, + ); + } else if mem_config.mem_share { let anon_mem_name = String::from("stratovirt_anon_mem"); let anon_fd = @@ -263,11 +268,6 @@ pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Resu offset: 0, page_size: host_page_size(), }); - } else if let Some(path) = &mem_config.mem_path { - f_back = Some( - FileBackend::new_mem(path, mem_config.mem_size) - .with_context(|| "Failed to create file that backs memory")?, - ); } let block = Arc::new(HostMemMapping::new( GuestAddress(0), -- Gitee From ef3acae7d33eac8ae97fbcd55cb769004c0f0029 Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Thu, 24 Aug 2023 15:50:48 +0800 Subject: [PATCH 1345/2187] usb: add ioeventfd for doorbell 0 Add ioeventfd for doorbell 0 to process control commands. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_pci.rs | 81 ++++++++++++++++++++++++++++++- devices/src/usb/xhci/xhci_regs.rs | 12 +---- docs/config_guidebook.md | 3 +- machine_manager/src/config/usb.rs | 19 ++++---- 4 files changed, 93 insertions(+), 22 deletions(-) diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 092770b4b..9462aa0d2 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -11,11 +11,16 @@ // See the Mulan PSL v2 for more details. use std::cmp::max; +use std::os::unix::io::AsRawFd; +use std::os::unix::prelude::RawFd; +use std::rc::Rc; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; -use log::debug; +use log::{debug, error}; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; use super::xhci_controller::{XhciDevice, MAX_INTRS, MAX_SLOTS}; use super::xhci_regs::{ @@ -30,8 +35,12 @@ use crate::pci::msix::update_dev_id; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; use crate::usb::UsbDeviceOps; use crate::{Device, DeviceBase}; -use address_space::{AddressSpace, Region}; +use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::XhciConfig; +use machine_manager::event_loop::register_event_helper; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; /// 5.2 PCI Configuration Registers(USB) const PCI_CLASS_PI: u16 = 0x09; @@ -66,6 +75,9 @@ pub struct XhciPciDevice { pub xhci: Arc>, dev_id: Arc, mem_region: Region, + doorbell_fd: Arc, + delete_evts: Vec, + iothread: Option, } impl XhciPciDevice { @@ -88,6 +100,9 @@ impl XhciPciDevice { XHCI_PCI_CONFIG_LENGTH as u64, "XhciPciContainer", ), + doorbell_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + delete_evts: Vec::new(), + iothread: config.iothread.clone(), } } @@ -140,12 +155,23 @@ impl XhciPciDevice { build_doorbell_ops(&self.xhci), "XhciPciDoorbellRegion", ); + doorbell_region.set_ioeventfds(&self.ioeventfds()); + self.mem_region .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64) .with_context(|| "Failed to register doorbell region.")?; Ok(()) } + fn ioeventfds(&self) -> Vec { + vec![RegionIoEventFd { + fd: self.doorbell_fd.clone(), + addr_range: AddressRange::from((0, 4u64)), + data_match: false, + data: 0, + }] + } + pub fn attach_device(&self, dev: &Arc>) -> Result<()> { let mut locked_xhci = self.xhci.lock().unwrap(); let usb_port = locked_xhci @@ -197,6 +223,46 @@ impl Device for XhciPciDevice { } } +struct DoorbellHandler { + xhci: Arc>, + fd: Arc, +} + +impl DoorbellHandler { + fn new(xhci: Arc>, fd: Arc) -> Self { + DoorbellHandler { xhci, fd } + } +} + +impl EventNotifierHelper for DoorbellHandler { + fn internal_notifiers(io_handler: Arc>) -> Vec { + let cloned_io_handler = io_handler.clone(); + let handler: Rc = Rc::new(move |_event, fd: RawFd| { + read_fd(fd); + let locked_handler = cloned_io_handler.lock().unwrap(); + let mut locked_xhci = locked_handler.xhci.lock().unwrap(); + + if !locked_xhci.running() { + error!("Failed to write doorbell, XHCI is not running"); + return None; + } + if let Err(e) = locked_xhci.handle_command() { + error!("Failed to handle command: {:?}", e); + locked_xhci.host_controller_error(); + } + + None + }); + vec![EventNotifier::new( + NotifierOperation::AddShared, + io_handler.lock().unwrap().fd.as_raw_fd(), + None, + EventSet::IN, + vec![handler], + )] + } +} + impl PciDevOps for XhciPciDevice { fn pci_base(&self) -> &PciDevBase { &self.base @@ -238,6 +304,17 @@ impl PciDevOps for XhciPciDevice { self.dev_id.store(self.base.devfn as u16, Ordering::SeqCst); self.mem_region_init()?; + let handler = Arc::new(Mutex::new(DoorbellHandler::new( + self.xhci.clone(), + self.doorbell_fd.clone(), + ))); + + register_event_helper( + EventNotifierHelper::internal_notifiers(handler), + self.iothread.as_ref(), + &mut self.delete_evts, + )?; + let intrs_num = self.xhci.lock().unwrap().intrs.len() as u32; init_msix( 0_usize, diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 269a90a22..f4c30b098 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -700,16 +700,8 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let mut xhci = xhci.lock().unwrap(); let slot_id = (offset >> 2) as u32; if slot_id == 0 { - if value == 0 { - if let Err(e) = xhci.handle_command() { - error!("Failed to handle command: {:?}", e); - xhci.host_controller_error(); - return false; - } - } else { - error!("Invalid doorbell write: value {:x}", value); - return false; - } + error!("Invalid slot id 0 !"); + return false; } else { let ep_id = value & DB_TARGET_MASK; if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 50f8ddcb7..bbc6ecbea 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -794,9 +794,10 @@ Three properties can be set for USB controller. * id: unique device id. * bus: bus number of the device. * addr: including slot number and function number. +* iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) ```shell --device nec-usb-xhci,id=,bus=,addr=<0xa> +-device nec-usb-xhci,id=,bus=,addr=<0xa>[,iothread=] ``` Note: Only one USB controller can be configured, USB controller can only support USB keyboard and USB tablet. diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 04863c698..4142f8fae 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -35,6 +35,7 @@ pub struct XhciConfig { pub p2: Option, // number of usb3.0 ports pub p3: Option, + pub iothread: Option, } impl XhciConfig { @@ -43,6 +44,7 @@ impl XhciConfig { id: None, p2: None, p3: None, + iothread: None, } } @@ -72,6 +74,9 @@ impl XhciConfig { impl ConfigCheck for XhciConfig { fn check(&self) -> Result<()> { check_id(self.id.clone(), "xhci controller")?; + if let Some(iothread) = self.iothread.as_ref() { + check_arg_too_long(iothread, "iothread name")?; + } self.check_ports() } } @@ -84,18 +89,14 @@ pub fn parse_xhci(conf: &str) -> Result { .push("bus") .push("addr") .push("p2") - .push("p3"); + .push("p3") + .push("iothread"); cmd_parser.parse(conf)?; let mut dev = XhciConfig::new(); dev.id = cmd_parser.get_value::("id")?; - - if let Some(p2) = cmd_parser.get_value::("p2")? { - dev.p2 = Some(p2); - } - - if let Some(p3) = cmd_parser.get_value::("p3")? { - dev.p3 = Some(p3); - } + dev.p2 = cmd_parser.get_value::("p2")?; + dev.p3 = cmd_parser.get_value::("p3")?; + dev.iothread = cmd_parser.get_value::("iothread")?; dev.check()?; Ok(dev) -- Gitee From d855ea937dbdb752518c197020455d96f0a15d7c Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 5 Sep 2023 21:29:47 +0800 Subject: [PATCH 1346/2187] xhci: reduce useless logs Remove the log when ui wakeup the endpoint which is not enabled. Signed-off-by: zhouli57 --- devices/src/usb/xhci/xhci_controller.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index d9719e820..4a1edd656 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2009,11 +2009,14 @@ impl XhciDevice { /// Used for device to wakeup endpoint pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &UsbEndpoint) -> Result<()> { - if slot_id == INVALID_SLOT_ID { - debug!("Invalid slot id, maybe device not activated."); + let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); + if let Err(e) = self.get_endpoint_ctx(slot_id, ep_id as u32) { + debug!( + "Invalid slot id or ep id, maybe device not activated, {:?}", + e + ); return Ok(()); } - let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); self.kick_endpoint(slot_id, ep_id as u32)?; Ok(()) } -- Gitee From 19756ef4a56339e5a79e561776bddf552aa7a76d Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 6 Sep 2023 15:31:31 +0800 Subject: [PATCH 1347/2187] vsock: diable VIRTIO_F_ACCESS_PLATFORM feature Currently, we don't support VIRTIO_F_ACCESS_PLATFORM feature. So, disable it. Signed-off-by: Yan Wang --- virtio/src/vhost/kernel/vsock.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 9bde8ccd5..800ea1ec3 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -23,7 +23,7 @@ use super::super::{VhostNotify, VhostOps}; use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ check_config_space_rw, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_TYPE_VSOCK, + VirtioInterruptType, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_TYPE_VSOCK, }; use address_space::AddressSpace; use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; @@ -194,9 +194,10 @@ impl VirtioDevice for Vsock { fn init_config_features(&mut self) -> Result<()> { let backend = self.backend.as_ref().unwrap(); - self.base.device_features = backend + let features = backend .get_features() .with_context(|| "Failed to get features for vsock")?; + self.base.device_features = features & !(1_u64 << VIRTIO_F_ACCESS_PLATFORM); Ok(()) } -- Gitee From 7fc40127e15cf7111fba7dc00e87c8a9d5d12707 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Mon, 4 Sep 2023 20:50:08 +0800 Subject: [PATCH 1348/2187] build: Allow stratovirt to be built in an openeuler container Add a `tools` directory at root, and initialize it with a stratovirt docker-build script and Dockerfile, which allows users to build stratovirt in isolated environment. It also can be used for automated build. Signed-off-by: Liu Wenyuan --- docs/build_guide.ch.md | 28 +++++++++++ docs/build_guide.md | 28 +++++++++++ tools/build_stratovirt_static/Dockerfile | 23 +++++++++ .../build-stratovirt-from-docker.sh | 48 +++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 tools/build_stratovirt_static/Dockerfile create mode 100755 tools/build_stratovirt_static/build-stratovirt-from-docker.sh diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index 5f97384f6..bdb248108 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -63,3 +63,31 @@ $ cargo build --release --target ${arch}-unknown-linux-musl ```shell $ cargo build --release --features "scream_alsa" ``` + +# 通过容器构建StratoVirt静态链接二进制 + +## 1. 检查docker环境 + +为了通过容器构建StratoVirt,需保证已经安装了docker软件。可通过下面的命令检查: + +```shell +$ docker -v +Docker version 18.09.0 +``` + +如果你想部署docker环境,下面的链接可以帮助你: + + + +## 2. 使用tools下提供的构建工具 + +运行tools/build_stratovirt_static下的脚本,自动拉起docker容器构建静态链接的StratoVirt。 + +```shell +$ cd tools/build_stratovirt_static +# 自定义一个镜像名称,构建StratoVirt静态链接二进制 +$ sh build_stratovirt_from_docker.sh custom_image_name +``` + +构建完成后,可找到StratoVirt构建静态链接二进制的路径在 `target/${arch}-unknown-linux-musl/release/stratovirt`. + diff --git a/docs/build_guide.md b/docs/build_guide.md index 3ee1120ca..816cfd21d 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -64,3 +64,31 @@ List of optional features: ```shell $ cargo build --release --features "scream_alsa" ``` + +# Build static StratoVirt in containers + +## 1. Check docker environment + +In order to build StratoVirt in containers, ensure that the docker software is installed. This can be checked with the following command: + +```shell +$ docker -v +Docker version 18.09.0 +``` + +If you want to deploy a docker environment, the following link can help you: + + + +## 2. Run the build script + +Run the script under tools/build_stratovirt_static directory to automatically run a docker container to build a statically linked StratoVirt. + +```shell +$ cd tools/build_stratovirt_static +# Build StratoVirt with your custom_image_name +$ sh build_stratovirt_from_docker.sh custom_image_name +``` + +After the build is complete, you can find the statically linked binary StratoVirt in the path: `target/${arch}-unknown-linux-musl/release/stratovirt`. + diff --git a/tools/build_stratovirt_static/Dockerfile b/tools/build_stratovirt_static/Dockerfile new file mode 100644 index 000000000..dcc66aae6 --- /dev/null +++ b/tools/build_stratovirt_static/Dockerfile @@ -0,0 +1,23 @@ +# Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +# +# StratoVirt is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan +# PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +from openeuler/openeuler:22.03-lts-sp2 + +ARG ARCH + +RUN yum update -y && \ + yum upgrade -y && \ + yum install -y cargo musl-gcc cyrus-sasl-devel && \ + yum install -y libcap-devel libcap-ng-devel libseccomp-devel && \ + if [ "${ARCH}" == aarch64 ]; then yum install -y dtc-devel; fi && \ + yum clean all + diff --git a/tools/build_stratovirt_static/build-stratovirt-from-docker.sh b/tools/build_stratovirt_static/build-stratovirt-from-docker.sh new file mode 100755 index 000000000..a1708e7f9 --- /dev/null +++ b/tools/build_stratovirt_static/build-stratovirt-from-docker.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +# +# StratoVirt is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan +# PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +set -o errexit +set -o nounset +set -o pipefail + +build_stratovirt() { + sudo "${container_engine}" run \ + --rm -i \ + --env ARCH="${ARCH}" \ + -v "${repo_root_dir}:/root/stratovirt" \ + "${container_image}" \ + bash -c "cd /root/stratovirt && ${CARGO} build --workspace --bin stratovirt --release --target=${RUST_TARGET}" +} + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root_dir="$(cd ${script_dir}/../.. && pwd)" + +ARCH=${ARCH:-$(uname -m)} +CARGO="/usr/bin/env CARGO_HOME=.cargo RUSTC_BOOTSTRAP=1 /usr/bin/cargo" +container_engine="${container_engine:-docker}" +container_image="${container_image:-$1}" + +if [ "${ARCH}" == "x86_64" ]; then RUST_TARGET="x86_64-unknown-linux-musl"; fi +if [ "${ARCH}" == "aarch64" ]; then RUST_TARGET="aarch64-unknown-linux-musl"; fi + +echo "Building StratoVirt with ${RUST_TARGET}" + +sudo "${container_engine}" build \ + --build-arg ARCH="${ARCH}" \ + "${repo_root_dir}" \ + -f "${script_dir}/Dockerfile" \ + -t "${container_image}" && \ + +build_stratovirt + -- Gitee From a7293ec435739605c4fff8c72bff70310b5ad9f4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 7 Sep 2023 21:11:05 +0800 Subject: [PATCH 1349/2187] vhost-user-fs: fix the process of negotiation If VHOST_USER_F_PROTOCOL_FEATURES has been negotiated, it should call set_vring_enable to enable vring. Otherwise, the ring is enabled by default. Currently, only vhost-user-blk device support negotiation of VHOST_USER_F_PROTOCOL_FEATURES. So, fix the negotiation process of vhost-user-fs. Signed-off-by: Yan Wang --- vhost_user_fs/src/virtio_fs.rs | 92 ++++++++++++++++++++------------- virtio/src/vhost/user/client.rs | 23 +++++---- 2 files changed, 69 insertions(+), 46 deletions(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 10e164fed..d46b213df 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -40,9 +40,9 @@ use util::loop_context::{ gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use virtio::vhost::user::RegionMemInfo; use virtio::{ - Queue, QueueConfig, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, + vhost::user::RegionMemInfo, virtio_has_feature, Queue, QueueConfig, + VhostUser::VHOST_USER_F_PROTOCOL_FEATURES, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, }; @@ -246,6 +246,51 @@ impl VirtioFs { bail!("Failed to find the guest address for addr: 0x{:X}", addr); } + + fn register_fs_handler(&mut self, queue_index: usize) -> Result<()> { + // Before setting up new notifiers, we should remove old ones. + self.unregister_fs_handler(queue_index)?; + + let driver_features = self.config.driver_features; + let mut queue_info = self.config.get_mut_queue_config(queue_index)?; + if queue_info.kick_evt.is_none() || queue_info.call_evt.is_none() { + bail!( + "The event for kicking {} or calling {} is none", + queue_info.kick_evt.is_none(), + queue_info.call_evt.is_none(), + ); + } + queue_info.config.ready = true; + + let fs_handler = Arc::new(Mutex::new( + FsIoHandler::new( + queue_info.config, + queue_info.kick_evt.as_ref().unwrap().clone(), + queue_info.call_evt.as_ref().unwrap().clone(), + &self.sys_mem, + driver_features, + self.fs.clone(), + ) + .with_context(|| "Failed to create fs handler")?, + )); + + self.fs_handlers[queue_index] = Some(fs_handler.clone()); + EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) + .with_context(|| "Failed to update event for queue status which is ready")?; + + Ok(()) + } + + fn unregister_fs_handler(&mut self, queue_index: usize) -> Result<()> { + if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { + EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) + .with_context(|| "Failed to update event for queue status which is not ready")?; + }; + let mut queue_info = self.config.get_mut_queue_config(queue_index)?; + queue_info.config.ready = false; + + Ok(()) + } } impl VhostUserReqHandler for VirtioFs { @@ -398,6 +443,11 @@ impl VhostUserReqHandler for VirtioFs { queue_info.call_evt = Some(Arc::new(call_evt)); }) .with_context(|| format!("Failed to set vring call, index: {}", index))?; + + if !virtio_has_feature(self.config.driver_features, VHOST_USER_F_PROTOCOL_FEATURES) { + self.register_fs_handler(queue_index)?; + } + Ok(()) } @@ -417,43 +467,11 @@ impl VhostUserReqHandler for VirtioFs { } fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()> { - let driver_features = self.config.driver_features; - - let mut queue_info = self.config.get_mut_queue_config(queue_index)?; - queue_info.config.ready = status == 1; - - // Before setting up new notifiers, we should remove old ones. - if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { - EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) - .with_context(|| "Failed to update event for queue status which is not ready")?; - }; - if status == 1 { - if queue_info.kick_evt.is_none() || queue_info.call_evt.is_none() { - bail!( - "The event for kicking {} or calling {} is none", - queue_info.kick_evt.is_none(), - queue_info.call_evt.is_none(), - ); - } - - let fs_handler = Arc::new(Mutex::new( - FsIoHandler::new( - queue_info.config, - queue_info.kick_evt.as_ref().unwrap().clone(), - queue_info.call_evt.as_ref().unwrap().clone(), - &self.sys_mem, - driver_features, - self.fs.clone(), - ) - .with_context(|| "Failed to create fs handler")?, - )); - - self.fs_handlers[queue_index] = Some(fs_handler.clone()); - EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) - .with_context(|| "Failed to update event for queue status which is ready")?; + self.register_fs_handler(queue_index)?; + } else { + self.unregister_fs_handler(queue_index)?; } - Ok(()) } } diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 4253c9cb0..24cd9fd83 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -534,15 +534,20 @@ impl VhostUserClient { })?; } - for (queue_index, queue) in self.queues.iter().enumerate() { - let enabled = queue.lock().unwrap().is_enabled(); - self.set_vring_enable(queue_index, enabled) - .with_context(|| { - format!( - "Failed to set vring enable for vhost-user, index: {}", - queue_index, - ) - })?; + if self.backend_type == VhostBackendType::TypeBlock { + // If VHOST_USER_F_PROTOCOL_FEATURES has been negotiated, it should call + // set_vring_enable to enable vring. Otherwise, the ring is enabled by default. + // Currently, only vhost-user-blk device support negotiate VHOST_USER_F_PROTOCOL_FEATURES. + for (queue_index, queue) in self.queues.iter().enumerate() { + let enabled = queue.lock().unwrap().is_enabled(); + self.set_vring_enable(queue_index, enabled) + .with_context(|| { + format!( + "Failed to set vring enable for vhost-user, index: {}", + queue_index, + ) + })?; + } } Ok(()) -- Gitee From 3b31d74fbd22f95a5034f55040bfb260b4dd8618 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 7 Sep 2023 21:34:43 +0800 Subject: [PATCH 1350/2187] vhost-user: fix vring base caculating When the queue is not enabled, it should not call get_avail_idx which will cause an error. So, default to 0 in this situation. Signed-off-by: Yan Wang --- virtio/src/vhost/user/client.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 24cd9fd83..328deaffe 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -510,7 +510,11 @@ impl VhostUserClient { queue_index, ) })?; - let last_avail_idx = queue.vring.get_avail_idx(&self.mem_space)?; + let last_avail_idx = if queue.vring.is_enabled() { + queue.vring.get_avail_idx(&self.mem_space)? + } else { + 0 + }; self.set_vring_base(queue_index, last_avail_idx) .with_context(|| { format!( -- Gitee From dff929785c405a3ff76611e442a75fd40ec37e17 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Fri, 8 Sep 2023 16:58:48 +0800 Subject: [PATCH 1351/2187] Fix unresolved imports error Signed-off-by: Gan Qixin --- devices/src/usb/xhci/mod.rs | 2 +- tests/mod_test/src/libdriver/usb.rs | 2 +- tests/mod_test/tests/usb_camera_test.rs | 2 +- tests/mod_test/tests/usb_storage_test.rs | 2 +- tests/mod_test/tests/usb_test.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index 2c01d4263..63e0dcf00 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -13,6 +13,6 @@ pub mod xhci_controller; pub mod xhci_pci; pub mod xhci_regs; +pub mod xhci_trb; mod xhci_ring; -mod xhci_trb; diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 160fc94a5..8a18351c9 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -44,7 +44,7 @@ use devices::usb::{ XHCI_INTR_REG_IMAN, XHCI_INTR_REG_IMOD, XHCI_INTR_REG_SIZE, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_PAGESIZE, XHCI_OPER_REG_USBCMD, XHCI_OPER_REG_USBSTS, }, - TRBCCode, TRBType, TRB_SIZE, + xhci_trb::{TRBCCode, TRBType, TRB_SIZE}, }, UsbDeviceRequest, }; diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 756d48d48..66d75983d 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -17,7 +17,7 @@ use std::{fs::remove_file, fs::File, io::Write}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use devices::usb::xhci::TRBCCode; +use devices::usb::xhci::xhci_trb::TRBCCode; use mod_test::libdriver::usb::{ TestUsbBuilder, TestXhciPciDevice, CONTROL_ENDPOINT_ID, PRIMARY_INTERRUPTER_ID, }; diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs index a62c28d39..472d91ef2 100644 --- a/tests/mod_test/tests/usb_storage_test.rs +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -20,7 +20,7 @@ use devices::usb::{ UsbMsdCswStatus, CBW_FLAG_IN, CBW_FLAG_OUT, CBW_SIGNATURE, CBW_SIZE, CSW_SIGNATURE, CSW_SIZE, GET_MAX_LUN, MASS_STORAGE_RESET, }, - xhci::{TRBCCode, TRB_SIZE}, + xhci::xhci_trb::{TRBCCode, TRB_SIZE}, UsbDeviceRequest, }; use mod_test::libdriver::usb::{ diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index cb1f33cf6..aa4c20fd0 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -19,7 +19,7 @@ use devices::usb::xhci::xhci_regs::{ XHCI_INTR_REG_ERSTBA_LO, XHCI_INTR_REG_ERSTSZ, XHCI_INTR_REG_IMAN, XHCI_OPER_NE_MASK, XHCI_OPER_REG_CONFIG, XHCI_OPER_REG_DNCTRL, XHCI_OPER_REG_USBCMD, XHCI_OPER_REG_USBSTS, }; -use devices::usb::xhci::{TRBCCode, TRBType, TRB_SIZE}; +use devices::usb::xhci::xhci_trb::{TRBCCode, TRBType, TRB_SIZE}; use devices::usb::UsbDeviceRequest; use mod_test::libdriver::pci::{PCI_DEVICE_ID, PCI_VENDOR_ID}; use mod_test::libdriver::usb::{ -- Gitee From 11fde300b8a0c1ca76722a95bf90f519e0758a29 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 11 Sep 2023 11:47:10 +0800 Subject: [PATCH 1352/2187] StratoVirt: update the version to v2.3.0 Signed-off-by:Fei Xu --- Cargo.lock | 38 ++++++++++++------------- Cargo.toml | 2 +- acpi/Cargo.toml | 2 +- address_space/Cargo.toml | 2 +- block_backend/Cargo.toml | 2 +- boot_loader/Cargo.toml | 2 +- chardev_backend/Cargo.toml | 2 +- cpu/Cargo.toml | 2 +- devices/Cargo.toml | 2 +- hypervisor/Cargo.toml | 2 +- machine/Cargo.toml | 2 +- machine_manager/Cargo.toml | 2 +- machine_manager/src/qmp/qmp_response.rs | 2 +- migration/Cargo.toml | 2 +- ozone/Cargo.toml | 2 +- smbios/Cargo.toml | 2 +- ui/Cargo.toml | 2 +- util/Cargo.toml | 2 +- vfio/Cargo.toml | 2 +- vhost_user_fs/Cargo.toml | 2 +- virtio/Cargo.toml | 2 +- 21 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d19147975..411c60535 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acpi" -version = "2.2.0" +version = "2.3.0" dependencies = [ "address_space", "anyhow", @@ -16,7 +16,7 @@ dependencies = [ [[package]] name = "address_space" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "arc-swap", @@ -155,7 +155,7 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block_backend" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "byteorder", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "boot_loader" -version = "2.2.0" +version = "2.3.0" dependencies = [ "address_space", "anyhow", @@ -261,7 +261,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chardev_backend" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "libc", @@ -313,7 +313,7 @@ dependencies = [ [[package]] name = "cpu" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "hypervisor", @@ -331,7 +331,7 @@ dependencies = [ [[package]] name = "devices" -version = "2.2.0" +version = "2.3.0" dependencies = [ "acpi", "address_space", @@ -726,7 +726,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hypervisor" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "arc-swap", @@ -903,7 +903,7 @@ checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "machine" -version = "2.2.0" +version = "2.3.0" dependencies = [ "acpi", "address_space", @@ -931,7 +931,7 @@ dependencies = [ [[package]] name = "machine_manager" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "hex", @@ -983,7 +983,7 @@ dependencies = [ [[package]] name = "migration" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "hypervisor", @@ -1138,7 +1138,7 @@ dependencies = [ [[package]] name = "ozone" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "libc", @@ -1495,7 +1495,7 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smbios" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "byteorder", @@ -1544,7 +1544,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratovirt" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "log", @@ -1682,7 +1682,7 @@ dependencies = [ [[package]] name = "ui" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "bitintr", @@ -1734,7 +1734,7 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "util" -version = "2.2.0" +version = "2.3.0" dependencies = [ "anyhow", "arc-swap", @@ -1781,7 +1781,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vfio" -version = "2.2.0" +version = "2.3.0" dependencies = [ "address_space", "anyhow", @@ -1807,7 +1807,7 @@ checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce" [[package]] name = "vhost_user_fs" -version = "2.2.0" +version = "2.3.0" dependencies = [ "acpi", "address_space", @@ -1829,7 +1829,7 @@ dependencies = [ [[package]] name = "virtio" -version = "2.2.0" +version = "2.3.0" dependencies = [ "acpi", "address_space", diff --git a/Cargo.toml b/Cargo.toml index 690c430c9..747a2a1b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stratovirt" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" description = "a lightweight hypervisor with low memory overhead and fast booting speed" diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml index bbe9e5ace..b8d52d1d5 100644 --- a/acpi/Cargo.toml +++ b/acpi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "acpi" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 82e4f730b..190415714 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "address_space" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index f997c6b8d..ef4e9509e 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "block_backend" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index 2d14d2b08..f73eef25b 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "boot_loader" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/chardev_backend/Cargo.toml b/chardev_backend/Cargo.toml index 02433d30b..935988619 100644 --- a/chardev_backend/Cargo.toml +++ b/chardev_backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chardev_backend" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 88dfe2175..03d901f49 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cpu" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 347e9f6f1..72918c0ac 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "devices" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 587d36945..abf694fb5 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hypervisor" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/machine/Cargo.toml b/machine/Cargo.toml index f73fea7fb..cc7346b3e 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "machine" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 4750ad6e8..c63ef5448 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "machine_manager" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/machine_manager/src/qmp/qmp_response.rs b/machine_manager/src/qmp/qmp_response.rs index ea93c92e0..831be4f3b 100644 --- a/machine_manager/src/qmp/qmp_response.rs +++ b/machine_manager/src/qmp/qmp_response.rs @@ -196,7 +196,7 @@ mod tests { "minor": 0, "major": 5 }, - "package": "StratoVirt-2.2.0" + "package": "StratoVirt-2.3.0" }, "capabilities": [] } diff --git a/migration/Cargo.toml b/migration/Cargo.toml index feec2bab7..4ee5b5ba7 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "migration" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index 7d1280457..4311a5206 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ozone" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" description = "Provides protection for stratovirt" diff --git a/smbios/Cargo.toml b/smbios/Cargo.toml index 62d539053..c9bf0e728 100644 --- a/smbios/Cargo.toml +++ b/smbios/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smbios" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 409278d62..3344a75f3 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ui" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/util/Cargo.toml b/util/Cargo.toml index a0a5b977f..35c50ab55 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "util" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index f98706e3f..5f7611f5c 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vfio" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 0dd5b0d36..dfb88130c 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vhost_user_fs" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 5b77892fc..30a8febc8 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "virtio" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" -- Gitee From 72d0dd3f0e2d67830defa4c9e8d5339c49cbf312 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 11 Sep 2023 17:14:00 +0800 Subject: [PATCH 1353/2187] StratoVirt: update the version(v2.3.0) which is not completely modified Signed-off-by:Fei Xu --- Cargo.lock | 4 ++-- machine_manager/src/qmp/qmp_schema.rs | 2 +- migration/migration_derive/Cargo.toml | 2 +- tests/mod_test/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 411c60535..001bd95ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1001,7 +1001,7 @@ dependencies = [ [[package]] name = "migration_derive" -version = "2.2.0" +version = "2.3.0" dependencies = [ "migration", "proc-macro2", @@ -1018,7 +1018,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mod_test" -version = "2.2.0" +version = "2.3.0" dependencies = [ "acpi", "anyhow", diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 8874b6b83..9f7fd60dd 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1657,7 +1657,7 @@ impl Command for balloon { /// /// ```text /// -> { "execute": "query-version" } -/// <- {"return":{"version":{"qemu":{"minor":1,"micro":0,"major":5},"package":"StratoVirt-2.2.0"},"capabilities":[]}} +/// <- {"return":{"version":{"qemu":{"minor":1,"micro":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_version {} diff --git a/migration/migration_derive/Cargo.toml b/migration/migration_derive/Cargo.toml index 418b10ecd..d0e71a38c 100644 --- a/migration/migration_derive/Cargo.toml +++ b/migration/migration_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "migration_derive" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index e87b29f2a..b6847e73d 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_test" -version = "2.2.0" +version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" -- Gitee From 8affb37e35ab582919ab4bf31337581ba82d269d Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 25 Jul 2023 08:32:59 +0800 Subject: [PATCH 1354/2187] Qcow2: fix function of alloc cluster 1. Fix function of alloc cluster. The old alloc function has a maximum limit, if the number of clusters exceed, the function will loop dead (maximun = cluster_size * cluster_size / refcount bytes). 2. Add an unit test of test_alloc_cluster_with_refcount_check. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/cache.rs | 10 - block_backend/src/qcow2/mod.rs | 4 +- block_backend/src/qcow2/refcount.rs | 532 +++++++++++++++++----------- 3 files changed, 335 insertions(+), 211 deletions(-) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index ed8d6f1f2..4a17ff102 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -121,16 +121,6 @@ impl CacheTable { Ok(()) } - pub fn find_empty_entry(&self, start: usize) -> Result { - let len = self.table_data.len() / self.entry_size; - for i in start..len { - if self.be_read(i)? == 0 { - return Ok(i); - } - } - Ok(len) - } - pub fn get_value(&self) -> &[u8] { &self.table_data } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 5065a5a52..8aa12857d 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -664,7 +664,9 @@ impl Qcow2Driver { } let size = clusters * self.header.cluster_size(); - let addr = self.refcount.alloc_cluster(&mut self.header, size)?; + let addr = self + .refcount + .alloc_clusters_with_ref(&mut self.header, size)?; let ret = self.check_overlap(0, addr, size); if ret != 0 { bail!( diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index b40f0008e..a319e0101 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -20,11 +20,14 @@ use crate::{ bytes_to_clusters, cache::{CacheTable, Qcow2Cache, ENTRY_SIZE_U16}, header::QcowHeader, - is_aligned, SyncAioInfo, ENTRY_SIZE, + is_aligned, SyncAioInfo, ENTRY_SIZE, REFCOUNT_TABLE_OFFSET_MASK, }, BlockProperty, }; -use util::aio::OpCode; +use util::{ + aio::OpCode, + num_ops::{div_round_up, round_up}, +}; // The max refcount table size default is 4 clusters; const MAX_REFTABLE_NUM: u64 = 4; @@ -136,43 +139,101 @@ impl RefCount { cluster_index & (self.refcount_blk_size - 1) as u64 } - fn extend_refcount_table(&mut self, header: &mut QcowHeader, cluster_index: u64) -> Result<()> { + /// Allocate a continuous space that is not referenced by existing refcount table + fn alloc_clusters_with_noref(&mut self, size: u64) -> Result { + if !self.discard_list.is_empty() { + self.sync_process_discards(OpCode::Discard); + } + + let nb_clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); + let mut free_clusters = 0; + while free_clusters < nb_clusters { + let offset = self.free_cluster_index << self.cluster_bits; + self.free_cluster_index += 1; + if self.get_refcount(offset)? != 0 { + free_clusters = 0; + } else { + free_clusters += 1; + } + } + + let cluster_index = self.free_cluster_index - nb_clusters; + Ok(cluster_index << self.cluster_bits) + } + + /// Allocate a contiguous space that already has a reference count in the refcount table + pub fn alloc_clusters_with_ref(&mut self, header: &mut QcowHeader, size: u64) -> Result { + if size == 0 { + bail!("Don't allow to alloc cluster size of 0!"); + } + let offset = self.alloc_clusters_with_noref(size)?; + let offset_end = round_up(offset + size, self.cluster_size).unwrap(); + let rt_end = offset_end >> (self.cluster_bits + self.refcount_blk_bits); + let nb_clusters = bytes_to_clusters(offset_end - offset, self.cluster_size)?; + + if rt_end >= self.refcount_table_size { + let clusters = self.free_cluster_index; + let start_idx = self.free_cluster_index; + let (table, blocks) = refcount_metadata_size( + clusters, + self.cluster_size, + header.refcount_order as u64, + true, + )?; + self.extend_refcount_table(header, start_idx, table, blocks)?; + } + self.update_alloc_refcount(offset, nb_clusters, 1, false, &Qcow2DiscardType::Never)?; + Ok(offset) + } + + /// Extend refcount table. + /// + /// # Arguments + /// + /// * `header` - header message of this qower driver. + /// * `start_idx` - alloc space for the new refcount table starting from the start index. + /// * `new_table_clusters` - number of clusters for new refcount table. + /// * `new_block_clusters` - number of clusters for refcount block, the size of refcount blocks + /// should be guaranteed to record all newly added clusters. + fn extend_refcount_table( + &mut self, + header: &mut QcowHeader, + start_idx: u64, + new_table_clusters: u64, + new_block_clusters: u64, + ) -> Result<()> { info!("Qcow2 needs to extend the refcount table"); // Alloc space for new refcount table. - let mut offset = cluster_index << self.cluster_bits; - let rcb_offset = self.cluster_in_rc_block(cluster_index); - let rc_block = vec![0_u8; self.cluster_size as usize]; - let cache_entry = Rc::new(RefCell::new(CacheTable::new( - offset, - rc_block, - ENTRY_SIZE_U16, - )?)); - let mut borrow_entry = cache_entry.borrow_mut(); - for i in rcb_offset..rcb_offset + self.refcount_table_clusters as u64 + 2 { - borrow_entry.set_entry_map(i as usize, 1)?; + let new_table_size = new_table_clusters * (self.cluster_size / ENTRY_SIZE); + if new_block_clusters > new_table_size { + bail!( + "Refcount block clusters {:?} exceed table size: {:?}", + new_block_clusters, + new_table_size + ); } - self.sync_aio.borrow_mut().write_dirty_info( - borrow_entry.addr, - borrow_entry.get_value(), - 0, - self.cluster_size, - )?; - borrow_entry.dirty_info.clear(); + let mut new_table = self.refcount_table.clone(); + new_table.resize(new_table_size as usize, 0); + let start_offset = start_idx * self.cluster_size; + let mut table_offset = start_offset; + for i in 0..new_block_clusters { + if new_table[i as usize] == 0 { + new_table[i as usize] = table_offset; + table_offset += self.cluster_size; + } + } + let end_offset = table_offset + new_table_clusters * self.cluster_size; + let metadata_clusters = div_round_up(end_offset - start_offset, self.cluster_size).unwrap(); // Write new extended refcount table to disk. - let size = self.refcount_table.len() + (self.cluster_size / ENTRY_SIZE) as usize; - let mut new_rc_table = self.refcount_table.clone(); - new_rc_table.resize(size, 0); - new_rc_table[(cluster_index >> self.refcount_blk_bits) as usize] = offset; - offset += self.cluster_size; self.sync_aio .borrow_mut() - .write_ctrl_cluster(offset, &new_rc_table)?; + .write_ctrl_cluster(table_offset, &new_table)?; // Update and save qcow2 header to disk. let mut new_header = header.clone(); - new_header.refcount_table_offset = offset; - new_header.refcount_table_clusters += 1; + new_header.refcount_table_offset = table_offset; + new_header.refcount_table_clusters = new_table_clusters as u32; self.sync_aio .borrow_mut() .write_buffer(0, &new_header.to_vec())?; @@ -182,31 +243,66 @@ impl RefCount { header.refcount_table_clusters = new_header.refcount_table_clusters; // Update refcount information. - let old_rct_offset = self.refcount_table_offset; - let old_rct_size = self.refcount_table_size; - self.refcount_table = new_rc_table; + let old_table_offset = self.refcount_table_offset; + let old_table_clusters = self.refcount_table_clusters; + self.refcount_table = new_table; self.refcount_table_offset = header.refcount_table_offset; self.refcount_table_clusters = header.refcount_table_clusters; - self.refcount_table_size = - (self.refcount_table_clusters << self.cluster_bits) as u64 / ENTRY_SIZE; + self.refcount_table_size = new_table_size; + self.free_cluster_index = end_offset / self.cluster_size; + + // Update refcount for metadata. + self.update_refcount( + start_offset, + metadata_clusters, + 1, + true, + &Qcow2DiscardType::Never, + )?; // Free the old cluster of refcount table. - let clusters = bytes_to_clusters(old_rct_size, self.cluster_size).unwrap(); self.update_refcount( - old_rct_offset, - clusters, + old_table_offset, + old_table_clusters as u64, -1, true, - &Qcow2DiscardType::Snapshot, + &Qcow2DiscardType::Other, )?; info!( "Qcow2 extends refcount table success, offset 0x{:x} -> 0x{:x}", - old_rct_offset, self.refcount_table_offset + old_table_offset, self.refcount_table_offset ); Ok(()) } + fn update_alloc_refcount( + &mut self, + offset: u64, + clusters: u64, + added: i32, + flush: bool, + discard_type: &Qcow2DiscardType, + ) -> Result<()> { + let start_clusters = div_round_up(offset, self.cluster_size).unwrap(); + for i in start_clusters..start_clusters + clusters { + let rt_idx = i >> self.refcount_blk_bits; + if rt_idx >= self.refcount_table_size as u64 { + bail!("Invalid refcount table index {}", rt_idx); + } + + let rb_addr = self.refcount_table[rt_idx as usize]; + if rb_addr == 0 { + self.alloc_refcount_block(rt_idx).map_err(|e| { + self.refcount_table[rt_idx as usize] = 0; + e + })?; + } + } + + self.update_refcount(offset, clusters, added, flush, discard_type) + } + pub fn update_refcount( &mut self, offset: u64, @@ -357,16 +453,16 @@ impl RefCount { pub fn get_refcount(&mut self, offset: u64) -> Result { let cluster = offset >> self.cluster_bits; let rt_idx = cluster >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size { - bail!( - "Invalid refcount table index {}, refcount table size {}", - rt_idx, - self.refcount_table_size - ); + if rt_idx >= self.refcount_table_size as u64 { + return Ok(0); } - let rb_addr = self.refcount_table[rt_idx as usize]; - if rb_addr == 0 || self.offset_into_cluster(rb_addr) != 0 { + let rb_addr = self.refcount_table[rt_idx as usize] & REFCOUNT_TABLE_OFFSET_MASK; + if rb_addr == 0 { + return Ok(0); + } + + if self.offset_into_cluster(rb_addr) != 0 { bail!( "Invalid refcount block address 0x{:x}, index is {}", rb_addr, @@ -428,112 +524,59 @@ impl RefCount { offset & (self.cluster_size - 1) } - fn alloc_refcount_block(&mut self, rt_idx: u64, free_idx: u64) -> Result<()> { - let rb_addr = free_idx << self.cluster_bits; - let rc_block = vec![0_u8; self.cluster_size as usize]; + /// Alloc a new cluster for refcount block. If this new allocated cluster exceed + /// the refcount table, then return fail. + fn alloc_refcount_block(&mut self, rt_idx: u64) -> Result<()> { + if rt_idx >= self.refcount_table_size { + bail!("The size of refcount table is not enough"); + } - // Update refcount table. - self.refcount_table[rt_idx as usize] = rb_addr; - let start = rt_idx * ENTRY_SIZE; - let ret = self.save_refcount_table(start, start + ENTRY_SIZE); - if ret.is_err() { - self.refcount_table[rt_idx as usize] = 0; - ret?; + let alloc_offset = self.alloc_clusters_with_noref(self.cluster_size)?; + let alloc_cluster_idx = alloc_offset >> self.cluster_bits; + let alloc_rt_idx = alloc_cluster_idx >> self.refcount_blk_bits; + // Avoid to resize the refcount table. + if alloc_rt_idx >= self.refcount_table_size { + bail!("The size of refcount table is not enough"); } - // Create recount block cache. + // Update refcount table. + self.refcount_table[rt_idx as usize] = alloc_offset; + let rc_block = vec![0_u8; self.cluster_size as usize]; let cache_entry = Rc::new(RefCell::new(CacheTable::new( - rb_addr, + alloc_offset, rc_block, ENTRY_SIZE_U16, )?)); - // Update and save refcount block. - let mut borrow_entry = cache_entry.borrow_mut(); - borrow_entry.set_entry_map(self.cluster_in_rc_block(free_idx) as usize, 1)?; - self.sync_aio.borrow_mut().write_dirty_info( - borrow_entry.addr, - borrow_entry.get_value(), - 0, - self.cluster_size, - )?; - borrow_entry.dirty_info.clear(); - drop(borrow_entry); - if let Some(replaced_entry) = self.refcount_blk_cache.lru_replace(rt_idx, cache_entry) { + if let Some(replaced_entry) = self + .refcount_blk_cache + .lru_replace(rt_idx, cache_entry.clone()) + { self.save_refcount_block(&replaced_entry)?; } - Ok(()) - } - - fn find_free_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { - let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); - let mut current_index = self.free_cluster_index; - let mut i = 0; - while i < clusters { - // Check if it needs to extend refcount table. - let rt_idx = current_index >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size { - self.extend_refcount_table(header, current_index)?; - if current_index > self.free_cluster_index { - current_index = self.free_cluster_index; - } else { - current_index += 1; - } - i = 0; - continue; - } - - // Check if it needs to alloc refcount block. - let rb_addr = self.refcount_table[rt_idx as usize]; - if rb_addr == 0 { - // Need to alloc refcount block. - self.alloc_refcount_block(rt_idx, current_index)?; - current_index += 1; - i = 0; - continue; - } else if self.offset_into_cluster(rb_addr) != 0 { - bail!( - "Invalid refcount block address 0x{:x}, index is {}", - rb_addr, - rt_idx - ); - } - - // Load refcount block from disk which is not in cache. - if !self.refcount_blk_cache.contains_keys(rt_idx) { - self.load_refcount_block(rt_idx).with_context(|| { - format!("Failed to get refcount block cache, index is {}", rt_idx) - })?; - } - - // Check if the cluster of current_index is free. - let idx = self.cluster_in_rc_block(current_index) as usize; - let cache_entry = self.refcount_blk_cache.get(rt_idx).unwrap(); - let borrowed_entry = cache_entry.borrow(); - - let find_idx = borrowed_entry.find_empty_entry(idx)?; - if find_idx != idx { - current_index += (find_idx - idx) as u64; - i = 0; - continue; - } - - i += 1; - current_index += 1; + let mut borrowed_entry = cache_entry.borrow_mut(); + if alloc_rt_idx == rt_idx { + // Update and save refcount block. + let alloc_rcb_idx = self.cluster_in_rc_block(alloc_cluster_idx); + borrowed_entry.set_entry_map(self.cluster_in_rc_block(alloc_rcb_idx) as usize, 1)?; + borrowed_entry.dirty_info.clear(); } - self.free_cluster_index = current_index; - Ok((current_index - clusters) << self.cluster_bits) - } - pub fn alloc_cluster(&mut self, header: &mut QcowHeader, size: u64) -> Result { - if size == 0 { - bail!("Don't allow to alloc 0 size cluster!"); + // Sync to disk. + self.sync_aio.borrow_mut().write_dirty_info( + borrowed_entry.addr, + borrowed_entry.get_value(), + 0, + self.cluster_size, + )?; + drop(borrowed_entry); + + if alloc_rt_idx != rt_idx { + self.update_alloc_refcount(alloc_offset, 1, 1, true, &Qcow2DiscardType::Never)?; } - let addr = self.find_free_cluster(header, size)?; - let clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); - self.update_refcount(addr, clusters, 1, false, &Qcow2DiscardType::Other)?; - Ok(addr) + let start = rt_idx * ENTRY_SIZE; + self.save_refcount_table(start, start + ENTRY_SIZE) } fn load_refcount_block(&mut self, rt_idx: u64) -> Result<()> { @@ -594,6 +637,52 @@ impl RefCount { } } +/// Recalculate the metadata size of refcount to expand the Refcount table, +/// so that it can refcount enough clusters. +/// +/// # Arguments +/// +/// * `nb_clusters` - number of clusters to refcount. +/// * `cluster_size` - size of cluster in bytes. +/// * `refcount_order` - refcount bits power of 2 exponent. +/// * `is_reserve` - if this parameter set true, the refcount table size +/// will have 50% more entries than necessary, which can avoid growing again soon. +/// Returns: (clusters of new refcount table, clusters of refcount block) +pub fn refcount_metadata_size( + nb_clusters: u64, + cluster_size: u64, + refcount_order: u64, + mut is_reserve: bool, +) -> Result<(u64, u64)> { + let reftable_entries = cluster_size / ENTRY_SIZE; + let refblock_entries = cluster_size * 8 / (1 << refcount_order); + let mut table = 0; + let mut blocks = 0; + let mut clusters = nb_clusters; + let mut last_clusters; + let mut total_clusters = 0; + + loop { + last_clusters = total_clusters; + blocks = div_round_up(clusters + table + blocks, refblock_entries).unwrap(); + table = div_round_up(blocks, reftable_entries).unwrap(); + total_clusters = table + blocks + clusters; + + if total_clusters == last_clusters { + if is_reserve { + clusters += div_round_up(table, 2).unwrap(); + total_clusters = 0; + is_reserve = false; + } else { + break; + } + } + } + + table = total_clusters - nb_clusters - blocks; + Ok((table, blocks)) +} + #[cfg(test)] mod test { use std::{ @@ -606,12 +695,40 @@ mod test { use byteorder::{BigEndian, ByteOrder}; use crate::qcow2::*; - use crate::qcow2::{header::*, refcount::Qcow2DiscardType}; + use crate::qcow2::{ + header::*, + refcount::{refcount_metadata_size, Qcow2DiscardType}, + }; use machine_manager::config::DiskFormat; use util::aio::{Aio, WriteZeroesState}; fn image_create(path: &str, img_bits: u32, cluster_bits: u32) -> File { let cluster_sz = 1 << cluster_bits; + let l1_size: u64 = 1 << (img_bits - (cluster_bits * 2 - ENTRY_SIZE as u32)); + let entry_per_l1_cluster: u64 = 1 << (cluster_bits - ENTRY_BITS as u32); + let l1_clusters = div_round_up(l1_size, entry_per_l1_cluster).unwrap(); + // Header + l1 table clusters + let nb_clusters = 1 + l1_clusters; + let (rct, rcb) = refcount_metadata_size(nb_clusters, cluster_sz, 4, false).unwrap(); + let mut rc_table: Vec = Vec::new(); + let mut rc_block: Vec = Vec::new(); + let l1_table = vec![0_u8; (l1_clusters * cluster_sz) as usize]; + let rct_offset = cluster_sz; + let rcb_offset = (1 + rct) * cluster_sz; + let l1_table_offset = (1 + rct + rcb) * cluster_sz; + let total_clusters = nb_clusters + rct + rcb; + let rc_per_block = cluster_sz / 2; + let rct_size = div_round_up(total_clusters, rc_per_block).unwrap(); + for i in 0..rct_size { + let addr = rcb_offset + i * cluster_sz; + rc_table.append(&mut addr.to_be_bytes().to_vec()); + } + for _i in 0..total_clusters { + rc_block.push(0x00); + rc_block.push(0x01); + } + rc_table.resize((rct * cluster_sz) as usize, 0); + rc_block.resize((rcb * cluster_sz) as usize, 0); let header = QcowHeader { magic: QCOW_MAGIC, version: 3, @@ -620,10 +737,10 @@ mod test { cluster_bits, size: 1 << img_bits, crypt_method: 0, - l1_size: 1 << (img_bits - (cluster_bits * 2 - 3)), - l1_table_offset: 3 * cluster_sz, - refcount_table_offset: cluster_sz, - refcount_table_clusters: 1, + l1_size: l1_size as u32, + l1_table_offset, + refcount_table_offset: rct_offset, + refcount_table_clusters: rct as u32, nb_snapshots: 0, snapshots_offset: 0, incompatible_features: 0, @@ -638,28 +755,20 @@ mod test { .custom_flags(libc::O_CREAT | libc::O_TRUNC) .open(path) .unwrap(); - file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) - .unwrap(); + file.set_len(total_clusters * cluster_sz).unwrap(); file.write_all(&header.to_vec()).unwrap(); - // Cluster 1 is the refcount table. - assert_eq!(header.refcount_table_offset, cluster_sz * 1); - let mut refcount_table = [0_u8; ENTRY_SIZE as usize]; - BigEndian::write_u64(&mut refcount_table, cluster_sz * 2); - file.seek(SeekFrom::Start(cluster_sz * 1)).unwrap(); - file.write_all(&refcount_table).unwrap(); + // Refcount table. + file.seek(SeekFrom::Start(rct_offset)).unwrap(); + file.write_all(&rc_table).unwrap(); - // Clusters which has been allocated. - assert_eq!(header.refcount_order, 4); - let clusters = - 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); - let mut refcount_block = Vec::new(); - for _ in 0..clusters { - refcount_block.push(0x00); - refcount_block.push(0x01); - } - file.seek(SeekFrom::Start(cluster_sz * 2)).unwrap(); - file.write_all(&refcount_block).unwrap(); + // Recount block. + file.seek(SeekFrom::Start(rcb_offset)).unwrap(); + file.write_all(&rc_block).unwrap(); + + // L1 table. + file.seek(SeekFrom::Start(l1_table_offset)).unwrap(); + file.write_all(&l1_table).unwrap(); file } @@ -718,6 +827,58 @@ mod test { remove_file(path).unwrap(); } + /// Test function of allocating cluster. + /// TestStep: + /// 1. Init qcow2 file driver. + /// 2. Call alloc function. + /// Expect: + /// 1. The refcount on the disk has been updated correctly. + /// 2. All allocated spaces will not overlap. + #[test] + fn test_alloc_cluster_with_refcount_check() { + let path = "/tmp/test_alloc_cluster_with_refcount_check.qcow2"; + let image_bits = 30; + let cluster_bits = 9; + let (mut qcow2, cloned_file) = create_qcow2_driver(path, image_bits, cluster_bits); + + let test_data = vec![1024, 152, 2048, 1, 20000, 65536, 512, 768, 7111, 2000000]; + let cluster_size: u64 = 1 << cluster_bits; + let mut res_data: Vec<(u64, u64)> = vec![]; + // Call function of alloc_cluster. + for clusters in test_data { + let addr = qcow2.alloc_cluster(clusters, true).unwrap(); + res_data.push((addr, clusters * cluster_size)); + } + // The refcount of all cluster update to disk. + let image_size: u64 = 1 << image_bits; + let table_offset = qcow2.header.refcount_table_offset; + let block_size: u64 = 1 << qcow2.refcount.refcount_blk_bits; + let table_size = div_round_up(image_size, block_size * cluster_size).unwrap(); + let mut refcount_table = vec![0_u8; table_size as usize * ENTRY_SIZE as usize]; + assert!(cloned_file + .read_at(&mut refcount_table, table_offset) + .is_ok()); + for i in 0..table_size { + let start_idx = i as usize * 8; + let addr = BigEndian::read_u64(&refcount_table[start_idx..start_idx + 8]); + assert_ne!(addr, 0); + } + + // All allocated cluster should not overlap with each other. + let len = res_data.len(); + for i in 0..len { + let addr1 = res_data[i].0 as usize; + let size1 = res_data[i].1 as usize; + for j in (i + 1)..len { + let addr2 = res_data[j].0 as usize; + let size2 = res_data[j].1 as usize; + assert_eq!(ranges_overlap(addr1, size1, addr2, size2).unwrap(), false); + } + } + + remove_file(path).unwrap(); + } + #[test] fn test_extend_refcount_table() { let path = "/tmp/refcount_case2.qcow2"; @@ -735,23 +896,26 @@ mod test { // 3 bit means refcount table entry size(8 Byte) // 1 bit means refcount block entry size(2 Byte). let mut clusters = 1 << (cluster_bits - 3 + cluster_bits - 1); - clusters /= 2; + clusters /= 64; // Alloc 2 cluster once for all clusters which will cause extending refcount table. for _ in 0..clusters + 1 { - qcow2.alloc_cluster(2, true).unwrap(); + qcow2.alloc_cluster(128, true).unwrap(); } qcow2.flush().unwrap(); let new_rct_offset = qcow2.header.refcount_table_offset; let new_rct_clusters = qcow2.header.refcount_table_clusters; assert_ne!(new_rct_offset, rct_offset); - assert_eq!(qcow2.header.refcount_table_clusters, rct_clusters + 1); + assert!(qcow2.header.refcount_table_clusters > rct_clusters); // Check if the new refcount table contains the old refcount table. let old_rct_size = cluster_sz as usize * rct_clusters as usize; + let new_rct_size = cluster_sz as usize * new_rct_clusters as usize; let mut old_rc_table = vec![0_u8; old_rct_size]; cloned_file.read_at(&mut old_rc_table, rct_offset).unwrap(); - let mut new_rc_table = vec![0_u8; old_rct_size]; - cloned_file.read_at(&mut new_rc_table, rct_offset).unwrap(); + let mut new_rc_table = vec![0_u8; new_rct_size]; + cloned_file + .read_at(&mut new_rc_table, new_rct_offset as u64) + .unwrap(); for i in 0..old_rct_size { assert_eq!(old_rc_table[i], new_rc_table[i]); } @@ -847,36 +1011,4 @@ mod test { remove_file(path).unwrap(); } - - #[test] - fn test_find_free_cluster() { - let path = "/tmp/refcount_case5.qcow2"; - let image_bits = 30; - let cluster_bits = 16; - let cluster_sz = 1 << 16; - let (qcow2, _) = create_qcow2_driver(path, image_bits, cluster_bits); - let mut refcount = qcow2.refcount.clone(); - let mut header = qcow2.header.clone(); - - // Test find 1 free cluster. - let ret = refcount.find_free_cluster(&mut header, cluster_sz); - assert!(ret.is_ok()); - - // Test find 10 continuous free cluster. - let ret = refcount.find_free_cluster(&mut header, 10 * cluster_sz); - assert!(ret.is_ok()); - - // Test invalid refcount table entry. - refcount.refcount_table[0] |= 0x1; - let ret = refcount.find_free_cluster(&mut header, 1 << cluster_bits); - if let Err(err) = ret { - let err_msg = format!("Invalid refcount block address 0x20001, index is 0"); - assert_eq!(err.to_string(), err_msg); - } else { - assert!(false); - } - refcount.refcount_table[0] &= !0x1; - - remove_file(path).unwrap(); - } } -- Gitee From 6b1ccd8f205a497ab7719164222325e937508786 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 20:05:55 +0800 Subject: [PATCH 1355/2187] Stratovirt-img: add check in the function of load_header External snapshots are not supported noe. If the header has related tags, it will return failture. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 8aa12857d..f84d72277 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -296,6 +296,9 @@ impl Qcow2Driver { let mut buf = vec![0; QcowHeader::len()]; self.sync_aio.borrow_mut().read_buffer(0, &mut buf)?; self.header = QcowHeader::from_vec(&buf)?; + if self.header.backing_file_size != 0 { + bail!("Backing file is not supported now"); + } Ok(()) } -- Gitee From c98f4c2b080ab9b81bcfc129353f25bd9e08404c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 14:56:24 +0800 Subject: [PATCH 1356/2187] Stratovirt-img: add stratovirt-img Add binary named stratovirt-img, which can be used for offline operations on disks Signed-off-by: Xiao Ye --- Cargo.lock | 12 ++++ Cargo.toml | 1 + block_backend/src/file.rs | 2 +- block_backend/src/lib.rs | 99 ++++++++++++++++++++++++++++- block_backend/src/qcow2/cache.rs | 5 ++ block_backend/src/qcow2/mod.rs | 99 ++++++++++++++++++----------- block_backend/src/qcow2/snapshot.rs | 4 +- block_backend/src/qcow2/table.rs | 4 +- block_backend/src/raw.rs | 12 +++- image/Cargo.toml | 15 +++++ image/src/img.rs | 69 ++++++++++++++++++++ image/src/main.rs | 70 ++++++++++++++++++++ machine_manager/src/config/drive.rs | 11 +++- 13 files changed, 353 insertions(+), 50 deletions(-) create mode 100644 image/Cargo.toml create mode 100644 image/src/img.rs create mode 100644 image/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 001bd95ca..ec6c5cea8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1554,6 +1554,18 @@ dependencies = [ "util", ] +[[package]] +name = "stratovirt-img" +version = "2.3.0" +dependencies = [ + "anyhow", + "block_backend", + "libc", + "log", + "machine_manager", + "util", +] + [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 747a2a1b7..930744436 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ util = { path = "util" } members = [ "vhost_user_fs", "ozone", + "image", "tests/mod_test", ] diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 32180cb29..41ee8ab0f 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -52,7 +52,7 @@ impl CombineRequest { } pub struct FileDriver { - file: File, + pub file: File, aio: Rc>>, incomplete: Arc, delete_evts: Vec, diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 7de6a08cf..3603ad272 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -10,10 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod file; pub mod qcow2; - -mod file; -mod raw; +pub mod raw; use std::{ fs::File, @@ -34,6 +33,79 @@ use util::aio::{Aio, Iovec, WriteZeroesState}; /// Callback function which is called when aio handle failed. pub type BlockIoErrorCallback = Arc; +pub const SECTOR_BITS: u64 = 9; +pub const SECTOR_SIZE: u64 = 1 << SECTOR_BITS; +pub const CLUSTER_SIZE_MIN: u64 = 1 << 9; +pub const CLUSTER_SIZE_MAX: u64 = 1 << 21; +pub const NO_FIX: u64 = 0; +pub const FIX_LEAKS: u64 = 1; +pub const FIX_ERRORS: u64 = 2; + +const DEFAULT_QCOW2_VERSION: u32 = 3; +const DEFAULT_CLUSTER_BITS: u64 = 16; +const DEFAULT_CLUSTER_SIZE: u64 = 1 << DEFAULT_CLUSTER_BITS; +const DEFAULT_REFCOUNT_BITS: u64 = 16; +const MAX_REFCOUNT_BITS: u64 = 64; + +#[macro_export] +macro_rules! output_msg { + ($lvl:expr, $($arg:tt)*) => { + if !$lvl { + println!($($arg)*) + } + } +} + +pub struct RawCreateOptions { + pub path: String, + pub img_size: u64, +} + +pub struct Qcow2CreateOptions { + pub path: String, + pub img_size: u64, + pub version: u32, + pub cluster_size: u64, + pub refcount_bits: u64, +} + +#[derive(Default)] +pub struct CreateOptions { + pub path: String, + pub img_size: u64, + pub cluster_size: Option, + pub refcount_bits: Option, + pub conf: BlockProperty, +} + +#[derive(Default, Clone, Copy)] +pub struct DiskFragments { + pub allocated_clusters: u64, + pub total_clusters: u64, + pub fragments: u64, + pub compressed_clusters: u64, +} + +#[derive(Default, Clone, Copy)] +pub struct CheckResult { + /// Number of leaked clusters. + pub leaks: i32, + /// Number of leaked clusters that have been fixed. + pub leaks_fixed: i32, + /// Number of corruptions clusters. + pub corruptions: i32, + /// Number of corruptions clusters that have been fixed. + pub corruptions_fixed: i32, + /// File length of virtual disk. + pub image_end_offset: u64, + /// Whether the refcount block table needs to be rebuilt. + pub need_rebuild: bool, + /// Total number of errors during the check. + pub err_num: u64, + /// Statistics information for clusters of virtual disk. + pub disk_frag: DiskFragments, +} + pub enum BlockStatus { Init, NormalIO, @@ -54,7 +126,28 @@ pub struct BlockProperty { pub refcount_cache_size: Option, } +impl Default for BlockProperty { + fn default() -> Self { + Self { + id: "".to_string(), + format: DiskFormat::Raw, + iothread: None, + direct: false, + req_align: 1_u32, + buf_align: 1_u32, + discard: false, + write_zeroes: WriteZeroesState::Off, + l2_cache_size: None, + refcount_cache_size: None, + } + } +} + pub trait BlockDriverOps: Send { + fn create_image(&mut self, options: &CreateOptions) -> Result; + + fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()>; + fn disk_size(&mut self) -> Result; fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()>; diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 4a17ff102..36dea3656 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -178,6 +178,11 @@ impl Qcow2Cache { Some(entry) } + pub fn clear_cache(&mut self) { + self.cache_map.clear(); + self.dirty_tables.clear(); + } + pub fn lru_replace( &mut self, key: u64, diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index f84d72277..de7976aa0 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -10,16 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod cache; -mod header; -mod refcount; -mod snapshot; -mod table; +pub mod cache; +pub mod header; +pub mod refcount; +pub mod snapshot; +pub mod table; use std::{ cell::RefCell, collections::HashMap, fs::File, + io::{Seek, SeekFrom, Write}, mem::size_of, os::unix::io::{AsRawFd, RawFd}, rc::Rc, @@ -32,7 +33,7 @@ use byteorder::{BigEndian, ByteOrder}; use log::{debug, error, info}; use once_cell::sync::Lazy; -use self::{cache::ENTRY_SIZE_U64, refcount::Qcow2DiscardType}; +use self::{cache::ENTRY_SIZE_U64, header::QCOW_MAGIC, refcount::Qcow2DiscardType}; use crate::{ file::{CombineRequest, FileDriver}, qcow2::{ @@ -42,7 +43,7 @@ use crate::{ snapshot::{InternalSnapshot, QcowSnapshot, QcowSnapshotExtraData, QCOW2_MAX_SNAPSHOTS}, table::{Qcow2ClusterType, Qcow2Table}, }, - BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, + BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, CheckResult, CreateOptions, }; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::SnapshotInfo; @@ -56,28 +57,32 @@ use util::{ }; // The L1/L2/Refcount table entry size. -const ENTRY_SIZE: u64 = 1 << ENTRY_BITS; -const ENTRY_BITS: u64 = 3; -const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; -const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; -const REFCOUNT_TABLE_OFFSET_MASK: u64 = 0xffff_ffff_ffff_fe00; -const QCOW2_OFLAG_ZERO: u64 = 1 << 0; +pub const ENTRY_SIZE: u64 = 1 << ENTRY_BITS; +pub const ENTRY_BITS: u64 = 3; +pub const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; +pub const L1_RESERVED_MASK: u64 = 0x7f00_0000_0000_01ff; +pub const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00; +pub const L2_STD_RESERVED_MASK: u64 = 0x3f00_0000_0000_01fe; +pub const REFCOUNT_TABLE_OFFSET_MASK: u64 = 0xffff_ffff_ffff_fe00; +pub const REFCOUNT_TABLE_RESERVED_MASK: u64 = 0x0000_0000_0000_01ff; +pub const QCOW2_OFLAG_ZERO: u64 = 1 << 0; const QCOW2_OFFSET_COMPRESSED: u64 = 1 << 62; -const QCOW2_OFFSET_COPIED: u64 = 1 << 63; +pub const QCOW2_OFFSET_COPIED: u64 = 1 << 63; +const MAX_L1_SIZE: u64 = 32 * (1 << 20); const DEFAULT_SECTOR_SIZE: u64 = 512; +pub(crate) const QCOW2_MAX_L1_SIZE: u64 = 1 << 25; // The default flush interval is 30s. const DEFAULT_METADATA_FLUSH_INTERVAL: u64 = 30; const METADATA_OVERLAP_CHECK_MAINHEADER: u64 = 1 << 0; const METADATA_OVERLAP_CHECK_ACTIVEL1: u64 = 1 << 1; -const METADATA_OVERLAP_CHECK_ACTIVEL2: u64 = 1 << 2; +pub(crate) const METADATA_OVERLAP_CHECK_ACTIVEL2: u64 = 1 << 2; const METADATA_OVERLAP_CHECK_REFCOUNTTABLE: u64 = 1 << 3; const METADATA_OVERLAP_CHECK_REFCOUNTBLOCK: u64 = 1 << 4; const METADATA_OVERLAP_CHECK_SNAPSHOTTABLE: u64 = 1 << 5; const METADATA_OVERLAP_CHECK_INACTIVEL1: u64 = 1 << 6; -#[allow(unused)] -const METADATA_OVERLAP_CHECK_INACTIVEL2: u64 = 1 << 7; +pub(crate) const METADATA_OVERLAP_CHECK_INACTIVEL2: u64 = 1 << 7; #[allow(unused)] const METADATA_OVERLAP_CHECK_BITMAPDIRECTORY: u64 = 1 << 8; @@ -104,7 +109,7 @@ pub enum HostRange { pub struct SyncAioInfo { /// Aio for sync read/write metadata. aio: Aio<()>, - fd: RawFd, + pub(crate) fd: RawFd, pub prop: BlockProperty, } @@ -121,7 +126,7 @@ impl SyncAioInfo { Ok(()) } - fn new(fd: RawFd, prop: BlockProperty) -> Result { + pub fn new(fd: RawFd, prop: BlockProperty) -> Result { Ok(Self { aio: Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off)?, fd, @@ -165,7 +170,7 @@ impl SyncAioInfo { self.aio.submit_request(aiocb) } - fn write_buffer(&mut self, offset: u64, buf: &[u8]) -> Result<()> { + pub(crate) fn write_buffer(&mut self, offset: u64, buf: &[u8]) -> Result<()> { let ptr = buf.as_ptr() as u64; let cnt = buf.len() as u64; let aiocb = self.package_sync_aiocb( @@ -177,12 +182,12 @@ impl SyncAioInfo { self.aio.submit_request(aiocb) } - fn write_ctrl_cluster(&mut self, addr: u64, buf: &[u64]) -> Result<()> { + pub(crate) fn write_ctrl_cluster(&mut self, addr: u64, buf: &[u64]) -> Result<()> { let output: Vec = buf.iter().flat_map(|val| val.to_be_bytes()).collect(); self.write_buffer(addr, &output) } - fn read_ctrl_cluster(&mut self, addr: u64, sz: u64) -> Result> { + pub(crate) fn read_ctrl_cluster(&mut self, addr: u64, sz: u64) -> Result> { let mut buf = vec![0; sz as usize]; let vec_len = size_of::() * sz as usize; let mut vec = vec![0_u8; vec_len]; @@ -193,7 +198,13 @@ impl SyncAioInfo { Ok(buf) } - fn write_dirty_info(&mut self, addr: u64, buf: &[u8], start: u64, end: u64) -> Result<()> { + pub(crate) fn write_dirty_info( + &mut self, + addr: u64, + buf: &[u8], + start: u64, + end: u64, + ) -> Result<()> { let start = round_down(start, DEFAULT_SECTOR_SIZE) .with_context(|| format!("Round down failed, value is {}", start))?; let end = round_up(end, DEFAULT_SECTOR_SIZE) @@ -203,13 +214,13 @@ impl SyncAioInfo { } pub struct Qcow2Driver { - driver: FileDriver, - sync_aio: Rc>, - header: QcowHeader, - table: Qcow2Table, - refcount: RefCount, - snapshot: InternalSnapshot, - status: Arc>, + pub driver: FileDriver, + pub sync_aio: Rc>, + pub header: QcowHeader, + pub table: Qcow2Table, + pub refcount: RefCount, + pub snapshot: InternalSnapshot, + pub status: Arc>, } impl Drop for Qcow2Driver { @@ -261,8 +272,12 @@ impl Qcow2Driver { qcow2.header.check().with_context(|| "Invalid header")?; qcow2 .table - .init_table(&qcow2.header, &conf) + .init_table_info(&qcow2.header, &conf) .with_context(|| "Failed to create qcow2 table")?; + qcow2 + .table + .load_l1_table() + .with_context(|| "Failed to load l1 table")?; qcow2.refcount.init_refcount_info(&qcow2.header, &conf); qcow2 .load_refcount_table() @@ -292,7 +307,7 @@ impl Qcow2Driver { }); } - fn load_header(&mut self) -> Result<()> { + pub fn load_header(&mut self) -> Result<()> { let mut buf = vec![0; QcowHeader::len()]; self.sync_aio.borrow_mut().read_buffer(0, &mut buf)?; self.header = QcowHeader::from_vec(&buf)?; @@ -302,7 +317,7 @@ impl Qcow2Driver { Ok(()) } - fn load_refcount_table(&mut self) -> Result<()> { + pub fn load_refcount_table(&mut self) -> Result<()> { let sz = self.header.refcount_table_clusters as u64 * (self.header.cluster_size() / ENTRY_SIZE); self.refcount.refcount_table = self @@ -376,7 +391,7 @@ impl Qcow2Driver { )) } - fn host_offset_for_read(&mut self, guest_offset: u64, req_len: u64) -> Result { + pub fn host_offset_for_read(&mut self, guest_offset: u64, req_len: u64) -> Result { // Request not support cross l2 table. let l2_max_len = self .table @@ -442,7 +457,7 @@ impl Qcow2Driver { /// If the corresponding entry didn't cache, it will be read from the disk synchronously. /// Input: guest offset. /// Output: target entry. - fn get_table_cluster(&mut self, guest_offset: u64) -> Result>> { + pub fn get_table_cluster(&mut self, guest_offset: u64) -> Result>> { let l1_index = self.table.get_l1_table_index(guest_offset); if l1_index >= self.header.l1_size as u64 { bail!("Need to grow l1 table size."); @@ -643,7 +658,7 @@ impl Qcow2Driver { guest_offset & (self.header.cluster_size() - 1) } - fn load_cluster(&mut self, addr: u64) -> Result> { + pub(crate) fn load_cluster(&mut self, addr: u64) -> Result> { if !is_aligned(self.header.cluster_size(), addr) { bail!("Cluster address not aligned {}", addr); } @@ -661,7 +676,7 @@ impl Qcow2Driver { std::cmp::min(cnt, self.header.cluster_size() - offset) } - fn alloc_cluster(&mut self, clusters: u64, write_zero: bool) -> Result { + pub fn alloc_cluster(&mut self, clusters: u64, write_zero: bool) -> Result { if !self.refcount.discard_list.is_empty() { self.refcount.sync_process_discards(OpCode::Discard); } @@ -1042,7 +1057,7 @@ impl Qcow2Driver { } // Check if there exist intersection between given address range and qcow2 metadata. - fn check_overlap(&self, ignore: u64, offset: u64, size: u64) -> i64 { + pub(crate) fn check_overlap(&self, ignore: u64, offset: u64, size: u64) -> i64 { let check = DEFAULT_QCOW2_METADATA_OVERLAP_CHECK & !ignore; if check == 0 { return 0; @@ -1296,6 +1311,14 @@ impl Qcow2Driver { } impl BlockDriverOps for Qcow2Driver { + fn create_image(&mut self, options: &CreateOptions) -> Result { + todo!() + } + + fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()> { + todo!() + } + fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { let nbytes = get_iov_size(&iovec); self.check_request(offset, nbytes) diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index 86fa34717..76bf9a804 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -37,7 +37,7 @@ pub struct InternalSnapshot { pub snapshot_size: u64, pub snapshot_table_offset: u64, // Number of snapshot table entry. - nb_snapshots: u32, + pub(crate) nb_snapshots: u32, } impl InternalSnapshot { @@ -221,7 +221,7 @@ impl QcowSnapshot { round_up(tmp_size as u64, 8).unwrap() } - fn gen_snapshot_table_entry(&self) -> Vec { + pub(crate) fn gen_snapshot_table_entry(&self) -> Vec { let id_str = self.id.to_string(); let entry_size = size_of::() + self.extra_data_size as usize; let mut buf = vec![0_u8; entry_size]; diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index ef787db8f..bf39fce0b 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -106,7 +106,7 @@ impl Qcow2Table { } } - pub fn init_table(&mut self, header: &QcowHeader, conf: &BlockProperty) -> Result<()> { + pub fn init_table_info(&mut self, header: &QcowHeader, conf: &BlockProperty) -> Result<()> { let max_l2_entries = div_round_up(header.size, header.cluster_size()).with_context(|| { format!( @@ -137,8 +137,6 @@ impl Qcow2Table { self.l2_table_cache = l2_table_cache; self.l1_table_offset = header.l1_table_offset; self.l1_size = header.l1_size; - self.load_l1_table() - .with_context(|| "Failed to load l1 table")?; Ok(()) } diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index cd351027b..069330983 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -15,11 +15,11 @@ use std::{ sync::{atomic::AtomicBool, Arc, Mutex}, }; -use anyhow::Result; +use anyhow::{bail, Result}; use crate::{ file::{CombineRequest, FileDriver}, - BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, + BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, CheckResult, CreateOptions, }; use util::aio::{get_iov_size, Aio, Iovec}; @@ -43,6 +43,14 @@ impl RawDriver { } impl BlockDriverOps for RawDriver { + fn create_image(&mut self, options: &CreateOptions) -> Result { + todo!() + } + + fn check_image(&mut self, _res: &mut CheckResult, _quite: bool, _fix: u64) -> Result<()> { + bail!("This image format does not support checks"); + } + fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { let nbytes = get_iov_size(&iovec); self.driver.read_vectored( diff --git a/image/Cargo.toml b/image/Cargo.toml new file mode 100644 index 000000000..d0ecafb9d --- /dev/null +++ b/image/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "stratovirt-img" +version = "2.3.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "Binary tools for offline disk operations" + +[dependencies] +anyhow = "1.0" +log = "0.4" +libc = "0.2" +util = { path = "../util" } +machine_manager = { path = "../machine_manager" } +block_backend = { path = "../block_backend"} diff --git a/image/src/img.rs b/image/src/img.rs new file mode 100644 index 000000000..9d6d314b3 --- /dev/null +++ b/image/src/img.rs @@ -0,0 +1,69 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::File, + os::unix::prelude::{FileExt, OpenOptionsExt}, + str::FromStr, + sync::Arc, +}; + +use anyhow::{bail, Context, Result}; + +pub(crate) fn image_create(_args: Vec) -> Result<()> { + todo!() +} + +pub(crate) fn image_check(_args: Vec) -> Result<()> { + todo!() +} + +pub(crate) fn image_snapshot(_args: Vec) -> Result<()> { + todo!() +} + +pub fn print_help() { + print!( + r#"Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +Usage: stratovirt-img [standard options] command [command options] +Stratovirt disk image utility + +'-h', '--help' display this help and exit +'-v', '--version' output version information and exit + +Command syntax: +create [-f fmt] [-o options] filename [size] +check [-r [leaks | all]] [-no_print_error] [-f fmt] filename +snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename + +Command parameters: +'filename' is a disk image filename +'fmt' is the disk image format +'size' is the disk image size in bytes +'options' is a comma separated list of format specific options in a +name=value format. + +Parameters to check subcommand: + '-no_print_error' don't print error detail. + '-r' tries to repair any inconsistencies that are found during the check. + '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all + kinds of errors. + +Parameters to snapshot subcommand: +'snapshot' is the name of the snapshot to create, apply or delete + '-a' applies a snapshot (revert disk to saved state) + '-c' creates a snapshot + '-d' deletes a snapshot + '-l' lists all snapshots in the given image +"#, + ); +} diff --git a/image/src/main.rs b/image/src/main.rs new file mode 100644 index 000000000..2db4a7dee --- /dev/null +++ b/image/src/main.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod img; + +use std::env; + +use crate::img::{image_check, image_create, image_snapshot, print_help}; + +const BINARY_NAME: &str = "stratovirt-img"; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 2 { + println!( + "{0}: Not enough arguments\n\ + Try '{0} --help' for more information", + BINARY_NAME + ); + return; + } + + let opt = args[1].clone(); + + match opt.as_str() { + "create" => { + if let Err(e) = image_create(args[2..].to_vec()) { + println!("{}: {:?}", BINARY_NAME, e); + } + } + "check" => { + if let Err(e) = image_check(args[2..].to_vec()) { + println!("{}: {:?}", BINARY_NAME, e); + } + } + "snapshot" => { + if let Err(e) = image_snapshot(args[2..].to_vec()) { + println!("{}: {:?}", BINARY_NAME, e); + } + } + "-v" | "--version" => { + println!( + "{} version {}\ + Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved.", + BINARY_NAME, + util::VERSION, + ) + } + "-h" | "--help" => { + print_help(); + } + _ => { + println!( + "{}: Command not found: {}\n\ + Try 'stratovirt-img --help' for more information.", + BINARY_NAME, + opt.as_str() + ); + } + } +} diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index e70e8404d..f0509c2a3 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -19,7 +19,7 @@ use anyhow::{anyhow, bail, Context, Result}; use log::error; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, pci_args_check}; +use super::{error::ConfigError, pci_args_check, M}; use crate::config::{ check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, @@ -129,6 +129,15 @@ impl FromStr for DiskFormat { } } +impl ToString for DiskFormat { + fn to_string(&self) -> String { + match *self { + DiskFormat::Raw => "raw".to_string(), + DiskFormat::Qcow2 => "qcow2".to_string(), + } + } +} + /// Config struct for `drive`. /// Contains block device's attr. #[derive(Debug, Clone, Serialize, Deserialize)] -- Gitee From f8b57adc3a326dcc0a21da75e14348f8e9a01d3e Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 19:08:21 +0800 Subject: [PATCH 1357/2187] Stratovirt-img: split the function of new for qcow2 driver. Split the function of load metadata from the qcow2 driver create operation. Signed-off-by: Xiao Ye --- block_backend/src/lib.rs | 4 +++ block_backend/src/qcow2/mod.rs | 45 +++++++++++++++-------------- block_backend/src/qcow2/refcount.rs | 4 ++- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 3603ad272..d7cac777e 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -198,6 +198,10 @@ pub fn create_block_backend( DiskFormat::Qcow2 => { let mut qcow2 = Qcow2Driver::new(file, aio, prop.clone()) .with_context(|| "Failed to create qcow2 driver")?; + qcow2 + .load_metadata(prop.clone()) + .with_context(|| "Failed to load metadata")?; + let file_size = qcow2.disk_size()?; if file_size & (prop.req_align as u64 - 1) != 0 { bail!( diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index de7976aa0..89d162f69 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -257,38 +257,35 @@ impl Qcow2Driver { pub fn new(file: File, aio: Aio, conf: BlockProperty) -> Result { let fd = file.as_raw_fd(); let sync_aio = Rc::new(RefCell::new(SyncAioInfo::new(fd, conf.clone())?)); - let mut qcow2 = Self { - driver: FileDriver::new(file, aio, conf.clone()), + Ok(Self { + driver: FileDriver::new(file, aio, conf), sync_aio: sync_aio.clone(), header: QcowHeader::default(), table: Qcow2Table::new(sync_aio.clone()), refcount: RefCount::new(sync_aio.clone()), snapshot: InternalSnapshot::new(sync_aio), status: Arc::new(Mutex::new(BlockStatus::Init)), - }; - qcow2 - .load_header() + }) + } + + pub fn load_metadata(&mut self, conf: BlockProperty) -> Result<()> { + self.load_header() .with_context(|| "Failed to load header")?; - qcow2.header.check().with_context(|| "Invalid header")?; - qcow2 - .table - .init_table_info(&qcow2.header, &conf) + self.header.check().with_context(|| "Invalid header")?; + self.table + .init_table_info(&self.header, &conf) .with_context(|| "Failed to create qcow2 table")?; - qcow2 - .table + self.table .load_l1_table() .with_context(|| "Failed to load l1 table")?; - qcow2.refcount.init_refcount_info(&qcow2.header, &conf); - qcow2 - .load_refcount_table() + self.refcount.init_refcount_info(&self.header, &conf); + self.load_refcount_table() .with_context(|| "Failed to load refcount table")?; - qcow2.snapshot.set_cluster_size(qcow2.header.cluster_size()); - qcow2 - .snapshot - .load_snapshot_table(qcow2.header.snapshots_offset, qcow2.header.nb_snapshots) + self.snapshot.set_cluster_size(self.header.cluster_size()); + self.snapshot + .load_snapshot_table(self.header.snapshots_offset, self.header.nb_snapshots) .with_context(|| "Failed to load snapshot table")?; - - Ok(qcow2) + Ok(()) } pub fn flush(&mut self) -> Result<()> { @@ -1608,7 +1605,9 @@ mod test { util::aio::AioEngine::Off, ) .unwrap(); - Qcow2Driver::new(file, aio, conf).unwrap() + let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()).unwrap(); + qcow2_driver.load_metadata(conf).unwrap(); + qcow2_driver } /// Write full the disk with value disorderly. @@ -1694,7 +1693,9 @@ mod test { refcount_cache_size: None, }; image.file = file.try_clone().unwrap(); - (image, Qcow2Driver::new(file, aio, conf).unwrap()) + let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()).unwrap(); + qcow2_driver.load_metadata(conf).unwrap(); + (image, qcow2_driver) } fn qcow2_read(qcow2: &mut Qcow2Driver<()>, buf: &mut [u8], offset: usize) -> Result<()> { diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index a319e0101..a8a0ac97e 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -797,7 +797,9 @@ mod test { refcount_cache_size: None, }; let cloned_file = file.try_clone().unwrap(); - (Qcow2Driver::new(file, aio, conf).unwrap(), cloned_file) + let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()).unwrap(); + qcow2_driver.load_metadata(conf).unwrap(); + (qcow2_driver, cloned_file) } #[test] -- Gitee From 6996079cf5bab45e15c4afd1d8413d6865cd19c3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 14:59:14 +0800 Subject: [PATCH 1358/2187] Stratovirt-img: modify function of memory_unit_conversion Add param for function of memory_unit_conversion to represent the default unit. Signed-off-by: Xiao Ye --- machine/src/standard_vm/mod.rs | 6 +- machine_manager/src/config/drive.rs | 4 +- machine_manager/src/config/machine_config.rs | 60 ++++++++++++-------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 006c10f7d..8a1557c03 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -63,7 +63,7 @@ use machine_manager::config::get_cameradev_config; use machine_manager::config::{ get_chardev_config, get_netdev_config, get_pci_df, memory_unit_conversion, BlkDevConfig, ChardevType, ConfigCheck, DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, - NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, + NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, M, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::MachineLifecycle; @@ -1995,12 +1995,12 @@ fn parse_blockdev(args: &BlockDevAddArgument) -> Result { config.format = format.as_str().parse::()?; } if let Some(l2_cache) = args.l2_cache_size.as_ref() { - let sz = memory_unit_conversion(l2_cache) + let sz = memory_unit_conversion(l2_cache, M) .with_context(|| format!("Invalid l2 cache size: {}", l2_cache))?; config.l2_cache_size = Some(sz); } if let Some(rc_cache) = args.refcount_cache_size.as_ref() { - let sz = memory_unit_conversion(rc_cache) + let sz = memory_unit_conversion(rc_cache, M) .with_context(|| format!("Invalid refcount cache size: {}", rc_cache))?; config.refcount_cache_size = Some(sz); } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index f0509c2a3..29f7d96f2 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -357,12 +357,12 @@ fn parse_drive(cmd_parser: CmdParser) -> Result { .unwrap_or(WriteZeroesState::Off); if let Some(l2_cache) = cmd_parser.get_value::("l2-cache-size")? { - let sz = memory_unit_conversion(&l2_cache) + let sz = memory_unit_conversion(&l2_cache, M) .with_context(|| format!("Invalid l2 cache size: {}", l2_cache))?; drive.l2_cache_size = Some(sz); } if let Some(rc_cache) = cmd_parser.get_value::("refcount-cache-size")? { - let sz = memory_unit_conversion(&rc_cache) + let sz = memory_unit_conversion(&rc_cache, M) .with_context(|| format!("Invalid refcount cache size: {}", rc_cache))?; drive.refcount_cache_size = Some(sz); } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 8a3818843..8195b965a 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -33,6 +33,7 @@ const MAX_NR_CPUS: u64 = 254; const MIN_NR_CPUS: u64 = 1; const MAX_MEMSIZE: u64 = 549_755_813_888; const MIN_MEMSIZE: u64 = 134_217_728; +pub const K: u64 = 1024; pub const M: u64 = 1024 * 1024; pub const G: u64 = 1024 * 1024 * 1024; @@ -284,9 +285,9 @@ impl VmConfig { cmd_parser.parse(mem_config)?; let mem = if let Some(mem_size) = cmd_parser.get_value::("")? { - memory_unit_conversion(&mem_size)? + memory_unit_conversion(&mem_size, M)? } else if let Some(mem_size) = cmd_parser.get_value::("size")? { - memory_unit_conversion(&mem_size)? + memory_unit_conversion(&mem_size, M)? } else { return Err(anyhow!(ConfigError::FieldIsMissing( "size".to_string(), @@ -454,7 +455,7 @@ impl VmConfig { fn get_mem_zone_size(&self, cmd_parser: &CmdParser) -> Result { if let Some(mem) = cmd_parser.get_value::("size")? { - let size = memory_unit_conversion(&mem)?; + let size = memory_unit_conversion(&mem, M)?; Ok(size) } else { Err(anyhow!(ConfigError::FieldIsMissing( @@ -656,8 +657,21 @@ fn adjust_topology( /// # Arguments /// /// * `origin_value` - The origin memory value from user. -pub fn memory_unit_conversion(origin_value: &str) -> Result { - if (origin_value.ends_with('M') | origin_value.ends_with('m')) +pub fn memory_unit_conversion(origin_value: &str, default_unit: u64) -> Result { + if (origin_value.ends_with('K') | origin_value.ends_with('k')) + && (origin_value.contains('K') ^ origin_value.contains('k')) + { + let value = origin_value.replacen('K', "", 1); + let value = value.replacen('k', "", 1); + get_inner( + value + .parse::() + .with_context(|| { + ConfigError::ConvertValueFailed(origin_value.to_string(), String::from("u64")) + })? + .checked_mul(K), + ) + } else if (origin_value.ends_with('M') | origin_value.ends_with('m')) && (origin_value.contains('M') ^ origin_value.contains('m')) { let value = origin_value.replacen('M', "", 1); @@ -688,7 +702,7 @@ pub fn memory_unit_conversion(origin_value: &str) -> Result { ConfigError::ConvertValueFailed(origin_value.to_string(), String::from("u64")) })?; - let memory_size = size.checked_mul(M); + let memory_size = size.checked_mul(default_unit); get_inner(memory_size) } @@ -745,82 +759,82 @@ mod tests { #[test] fn test_memory_unit_conversion() { let test_string = "6G"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_ok()); let ret = ret.unwrap(); assert_eq!(ret, 6 * 1024 * 1024 * 1024); let test_string = "6g"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_ok()); let ret = ret.unwrap(); assert_eq!(ret, 6 * 1024 * 1024 * 1024); let test_string = "6M"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_ok()); let ret = ret.unwrap(); assert_eq!(ret, 6 * 1024 * 1024); let test_string = "6m"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_ok()); let ret = ret.unwrap(); assert_eq!(ret, 6 * 1024 * 1024); // default unit is MiB let test_string = "6"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_ok()); let ret = ret.unwrap(); assert_eq!(ret, 6 * 1024 * 1024); let test_string = "G6"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "G6G"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "6Gg"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "6gG"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "g6G"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "G6g"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "M6"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "M6M"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "6Mm"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "6mM"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "m6M"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); let test_string = "M6m"; - let ret = memory_unit_conversion(test_string); + let ret = memory_unit_conversion(test_string, M); assert!(ret.is_err()); } -- Gitee From 37794c1b38119efad9973aeacb1a5ff4abd0f341 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 04:39:43 +0800 Subject: [PATCH 1359/2187] Stratovirt-img: add arg_parse The struct of arg_parse can provide parameter parse for executable binaries. Signed-off-by: Xiao Ye --- image/src/cmdline.rs | 188 +++++++++++++++++++++++++++++++++++++++++++ image/src/main.rs | 1 + 2 files changed, 189 insertions(+) create mode 100644 image/src/cmdline.rs diff --git a/image/src/cmdline.rs b/image/src/cmdline.rs new file mode 100644 index 000000000..a2f6079bf --- /dev/null +++ b/image/src/cmdline.rs @@ -0,0 +1,188 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; + +use anyhow::{bail, Result}; + +enum ArgsType { + Flag, + Opt, + OptMulti, +} + +struct Arg { + args_type: ArgsType, + value: Option, + values: Vec, + // Whether this parameter was configured. + presented: bool, +} + +impl Arg { + fn new(args_type: ArgsType) -> Self { + Self { + args_type, + value: None, + values: vec![], + presented: false, + } + } +} + +pub struct ArgsParse { + args: HashMap, + pub free: Vec, +} + +impl ArgsParse { + pub fn create(opt_flag: Vec<&str>, opt_short: Vec<&str>, opt_multi: Vec<&str>) -> Self { + let mut args: HashMap = HashMap::new(); + for arg_name in opt_flag { + args.insert(arg_name.to_string(), Arg::new(ArgsType::Flag)); + } + + for arg_name in opt_short { + args.insert(arg_name.to_string(), Arg::new(ArgsType::Opt)); + } + + for arg_name in opt_multi { + args.insert(arg_name.to_string(), Arg::new(ArgsType::OptMulti)); + } + + Self { + args, + free: Vec::new(), + } + } + + pub fn parse(&mut self, args: Vec) -> Result<()> { + let len = args.len(); + let mut pre_opt = (0, "".to_string()); + + for idx in 0..len { + let str = args[idx as usize].clone(); + if str.starts_with("-") && str.len() > 1 { + if pre_opt.1.len() != 0 { + bail!("missing argument for option '{}'", pre_opt.1); + } + + let name = if str.starts_with("--") && str.len() > 2 { + (&str[2..]).to_string() + } else if str.starts_with("-") && str.len() > 1 { + (&str[1..]).to_string() + } else { + bail!("unrecognized option '{}'", str); + }; + + if let Some(args) = self.args.get_mut(&name) { + match args.args_type { + ArgsType::Flag => { + args.presented = true; + } + _ => { + pre_opt = (idx, name); + } + }; + } else { + bail!("unrecognized option '{}'", name); + } + + continue; + } + + if pre_opt.0 + 1 == idx && pre_opt.1.len() != 0 { + let name = pre_opt.1.to_string(); + let value = str.to_string(); + if let Some(arg) = self.args.get_mut(&name) { + match arg.args_type { + ArgsType::Opt => { + arg.presented = true; + arg.value = Some(value); + } + ArgsType::OptMulti => { + arg.presented = true; + arg.values.push(value); + } + _ => bail!("unrecognized option '{}'", name), + } + } + pre_opt = (0, "".to_string()); + } else if pre_opt.1.len() == 0 { + self.free.push(str.to_string()); + } else { + bail!("unrecognized option '{}'", pre_opt.1); + } + } + + if pre_opt.0 == 0 && pre_opt.1.len() != 0 { + bail!("unrecognized option '{}'", pre_opt.1); + } + + Ok(()) + } + + pub fn opt_present(&mut self, name: &str) -> bool { + if let Some(arg) = self.args.get(name) { + return arg.presented; + } + false + } + + pub fn opt_str(&mut self, name: &str) -> Option { + if let Some(arg) = self.args.get(name) { + return arg.value.clone(); + } + None + } + + pub fn opt_strs(&mut self, name: &str) -> Vec { + let mut values: Vec = vec![]; + if let Some(arg) = self.args.get(name) { + values = arg.values.clone(); + } + values + } +} + +#[cfg(test)] +mod test { + use super::ArgsParse; + + #[test] + fn test_arg_parse() { + let mut arg_parser = ArgsParse::create(vec!["q", "h", "help"], vec!["f"], vec!["o"]); + let cmd_line = "-f qcow2 -q -h --help -o cluster_size=512 -o refcount_bits=16 img_path +1G"; + let cmd_args: Vec = cmd_line + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + + let ret = arg_parser.parse(cmd_args); + println!("{:?}", ret); + assert!(ret.is_ok()); + + assert_eq!(arg_parser.opt_present("f"), true); + assert_eq!(arg_parser.opt_present("q"), true); + assert_eq!(arg_parser.opt_present("h"), true); + assert_eq!(arg_parser.opt_present("help"), true); + + let values = arg_parser.opt_strs("o"); + assert!(values.contains(&"cluster_size=512".to_string())); + assert!(values.contains(&"refcount_bits=16".to_string())); + + let free = arg_parser.free.clone(); + assert_eq!(free[0], "img_path".to_string()); + assert_eq!(free[1], "+1G".to_string()); + } +} diff --git a/image/src/main.rs b/image/src/main.rs index 2db4a7dee..0211c9ccf 100644 --- a/image/src/main.rs +++ b/image/src/main.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod cmdline; mod img; use std::env; -- Gitee From d5015fdffcc118aafb17c291e3f3570ecdd81c38 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:11:27 +0800 Subject: [PATCH 1360/2187] Stratovirt-img: add operation of image create add operation of image create Signed-off-by: Xiao Ye --- block_backend/src/lib.rs | 69 ++++++++++++++++ block_backend/src/qcow2/mod.rs | 143 ++++++++++++++++++++++++++++++++- block_backend/src/raw.rs | 5 +- image/src/img.rs | 90 ++++++++++++++++++++- 4 files changed, 303 insertions(+), 4 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index d7cac777e..ccdeb902c 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -78,6 +78,75 @@ pub struct CreateOptions { pub conf: BlockProperty, } +impl CreateOptions { + fn check(&self) -> Result<()> { + if self.img_size == 0 || self.img_size % SECTOR_SIZE != 0 { + bail!( + "Image size {} is invalid, it can't be zero and it must be multiple of {}", + self.img_size, + SECTOR_SIZE + ); + } + + Ok(()) + } + + pub(crate) fn raw(&self) -> Result { + self.check()?; + if self.cluster_size.is_some() { + bail!("Format raw does not support parameter 'cluster_size'"); + } + + if self.refcount_bits.is_some() { + bail!("Format raw does not support parameter 'refcount_bits'"); + } + + let options_raw = RawCreateOptions { + path: self.path.clone(), + img_size: self.img_size, + }; + Ok(options_raw) + } + + pub(crate) fn qcow2(&self) -> Result { + self.check()?; + let mut cluster_size = DEFAULT_CLUSTER_SIZE; + if let Some(size) = self.cluster_size { + if !size.is_power_of_two() || !(CLUSTER_SIZE_MIN..=CLUSTER_SIZE_MAX).contains(&size) { + bail!( + "Cluster size is {}, it should be power of 2 and within the range of [{}:{}]", + size, + CLUSTER_SIZE_MIN, + CLUSTER_SIZE_MAX, + ); + } + cluster_size = size; + } + + let mut refcount_bits = DEFAULT_REFCOUNT_BITS; + if let Some(rc_bits) = self.refcount_bits { + if rc_bits > MAX_REFCOUNT_BITS || !rc_bits.is_power_of_two() { + bail!( + "Refcount bis {} is invalid, it should be power of 2 and not exceed {} bits", + rc_bits, + MAX_REFCOUNT_BITS + ); + } + refcount_bits = rc_bits; + } + + let options_qcow2 = Qcow2CreateOptions { + path: self.path.clone(), + img_size: self.img_size, + version: DEFAULT_QCOW2_VERSION, + cluster_size, + refcount_bits, + }; + + Ok(options_qcow2) + } +} + #[derive(Default, Clone, Copy)] pub struct DiskFragments { pub allocated_clusters: u64, diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 89d162f69..717b8d54e 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -450,6 +450,59 @@ impl Qcow2Driver { Ok(cluster_addr + self.offset_into_cluster(guest_offset)) } + /// Extend the l1 table. + pub fn grow_l1_table(&mut self, new_l1_size: u64) -> Result<()> { + let old_l1_size = self.header.l1_size as u64; + if new_l1_size <= old_l1_size { + return Ok(()); + } + if new_l1_size > MAX_L1_SIZE / ENTRY_SIZE { + bail!("L1 size {} is too large", new_l1_size); + } + + info!( + "Resize the l1 table size from {} to {}", + old_l1_size, new_l1_size + ); + + // Copy data from old l1 table. + let cluster_size = self.header.cluster_size(); + let old_l1_table_size = old_l1_size * ENTRY_SIZE; + let old_l1_table_clusters = div_round_up(old_l1_table_size, cluster_size).unwrap(); + let old_l1_table_offset = self.header.l1_table_offset; + let new_l1_table_size = new_l1_size * ENTRY_SIZE; + let new_l1_table_clusters = div_round_up(new_l1_table_size, cluster_size).unwrap(); + let new_l1_table_offset = self.alloc_cluster(new_l1_table_clusters, true)?; + let mut new_l1_table = vec![0_u64; new_l1_size as usize]; + new_l1_table[..(old_l1_size as usize)] + .copy_from_slice(&self.table.l1_table[..(old_l1_size as usize)]); + + let ret = self.check_overlap(0, new_l1_table_offset, new_l1_table_size); + if ret != 0 { + bail!("Write on metadata(overlap with {:?})", ret); + } + self.refcount.flush()?; + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(new_l1_table_offset, &new_l1_table)?; + + // Flush new data to disk. + let mut new_header = self.header.clone(); + new_header.l1_size = new_l1_size as u32; + new_header.l1_table_offset = new_l1_table_offset; + self.sync_aio + .borrow_mut() + .write_buffer(0, &new_header.to_vec())?; + self.header = new_header; + self.table.l1_table = new_l1_table; + self.free_cluster( + old_l1_table_offset, + old_l1_table_clusters, + true, + &Qcow2DiscardType::Other, + ) + } + /// Obtaining the target entry for guest offset. /// If the corresponding entry didn't cache, it will be read from the disk synchronously. /// Input: guest offset. @@ -1309,7 +1362,95 @@ impl Qcow2Driver { impl BlockDriverOps for Qcow2Driver { fn create_image(&mut self, options: &CreateOptions) -> Result { - todo!() + let qcow2_options = options.qcow2()?; + let cluster_size = qcow2_options.cluster_size; + let refcount_bytes = qcow2_options.refcount_bits / 8; + + let rc_table_offset: u64 = cluster_size; + let rc_block_offset: u64 = cluster_size * 2; + let mut rc_table: Vec = Vec::new(); + let mut rc_block: Vec = Vec::new(); + let zero_buf: Vec = vec![0_u8; cluster_size as usize]; + rc_table.append(&mut rc_block_offset.to_be_bytes().to_vec()); + + // Init image with 3 clusters(Header + refcount table + refcount block) + let count = match refcount_bytes { + 1 => 1_u8.to_be_bytes().to_vec(), + 2 => 1_u16.to_be_bytes().to_vec(), + 4 => 1_u32.to_be_bytes().to_vec(), + 8 => 1_u64.to_be_bytes().to_vec(), + _ => { + bail!("Refcount bytes {:?} is invalid", refcount_bytes); + } + }; + for _ in 0..3 { + rc_block.append(&mut count.clone()); + } + + let header = QcowHeader { + magic: QCOW_MAGIC, + version: qcow2_options.version, + backing_file_offset: 0, + backing_file_size: 0, + cluster_bits: qcow2_options.cluster_size.trailing_zeros(), + size: qcow2_options.img_size, + crypt_method: 0, + l1_size: 0, + l1_table_offset: 0, + refcount_table_offset: rc_table_offset, + refcount_table_clusters: 1, + nb_snapshots: 0, + snapshots_offset: 0, + incompatible_features: 0, + compatible_features: 0, + autoclear_features: 0, + refcount_order: qcow2_options.refcount_bits.trailing_zeros(), + header_length: std::mem::size_of::() as u32, + }; + + let conf = options.conf.clone(); + self.table + .init_table_info(&header, &conf) + .with_context(|| "Failed to create qcow2 table")?; + self.refcount.init_refcount_info(&header, &conf); + self.snapshot.set_cluster_size(header.cluster_size()); + self.header = header; + + // Header. + self.driver.file.set_len(3 * cluster_size)?; + + // Write zero. + for i in 0..3 { + let offset = i * cluster_size; + self.driver.file.seek(SeekFrom::Start(offset))?; + self.driver.file.write_all(&zero_buf.to_vec())? + } + self.driver.file.seek(SeekFrom::Start(0))?; + self.driver.file.write_all(&self.header.to_vec())?; + + // Refcount table. + self.driver.file.seek(SeekFrom::Start(cluster_size))?; + self.driver.file.write_all(&rc_table)?; + + // Refcount block table. + self.driver.file.seek(SeekFrom::Start(cluster_size * 2))?; + self.driver.file.write_all(&rc_block)?; + + // Create qcow2 driver. + self.load_refcount_table()?; + + let l1_entry_size = cluster_size * (cluster_size / ENTRY_SIZE); + let l1_size = div_round_up(qcow2_options.img_size, l1_entry_size).unwrap(); + self.grow_l1_table(l1_size)?; + self.flush()?; + + let image_info = format!( + "fmt=qcow2 cluster_size={} extended_l2=off compression_type=zlib size={} lazy_refcounts=off refcount_bits={}", + qcow2_options.cluster_size, + qcow2_options.img_size, + qcow2_options.refcount_bits + ); + Ok(image_info) } fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()> { diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 069330983..aece80414 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -44,7 +44,10 @@ impl RawDriver { impl BlockDriverOps for RawDriver { fn create_image(&mut self, options: &CreateOptions) -> Result { - todo!() + let raw_options = options.raw()?; + self.driver.file.set_len(raw_options.img_size)?; + let image_info = format!("fmt=raw size={}", raw_options.img_size); + Ok(image_info) } fn check_image(&mut self, _res: &mut CheckResult, _quite: bool, _fix: u64) -> Result<()> { diff --git a/image/src/img.rs b/image/src/img.rs index 9d6d314b3..1740b10f3 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -19,8 +19,94 @@ use std::{ use anyhow::{bail, Context, Result}; -pub(crate) fn image_create(_args: Vec) -> Result<()> { - todo!() +use crate::cmdline::ArgsParse; +use block_backend::{ + qcow2::{header::QcowHeader, InternalSnapshotOps, Qcow2Driver, SyncAioInfo}, + raw::RawDriver, + BlockDriverOps, BlockProperty, CheckResult, CreateOptions, FIX_ERRORS, FIX_LEAKS, NO_FIX, + SECTOR_SIZE, +}; +use machine_manager::config::{memory_unit_conversion, DiskFormat}; +use util::{ + aio::{Aio, AioEngine}, + file::{lock_file, open_file, unlock_file}, +}; + +pub(crate) fn image_create(args: Vec) -> Result<()> { + let mut create_options = CreateOptions::default(); + let mut arg_parser = ArgsParse::create(vec!["h", "help"], vec!["f"], vec!["o"]); + arg_parser.parse(args.clone())?; + + if arg_parser.opt_present("h") || arg_parser.opt_present("help") { + print_help(); + return Ok(()); + } + + let mut disk_fmt = DiskFormat::Raw; + if let Some(fmt) = arg_parser.opt_str("f") { + disk_fmt = DiskFormat::from_str(&fmt)?; + }; + + let extra_options = arg_parser.opt_strs("o"); + for option in extra_options { + if option.starts_with("cluster_size=") { + let vec: Vec = option.split('=').map(|str| str.to_string()).collect(); + if vec.len() == 2 && vec[0] == *"cluster_size" { + let str = vec[1].clone(); + create_options.cluster_size = Some(memory_unit_conversion(&str, 1)?); + continue; + } + } + if option.starts_with("refcount_bits=") { + let vec: Vec = option.split('=').map(|str| str.to_string()).collect(); + if vec.len() == 2 && vec[0] == *"refcount_bits" { + let value = vec[1].clone(); + create_options.refcount_bits = Some(value.parse::()?); + continue; + } + } + + bail!("Invalid parameter '{}'", option); + } + + let len = arg_parser.free.len(); + match len { + 0 => bail!("Image creation requires path and size parameters"), + 1 => bail!("Image creation requires size parameters"), + 2 => { + create_options.path = arg_parser.free[0].clone(); + let img_size_str = arg_parser.free[1].clone(); + create_options.img_size = memory_unit_conversion(&img_size_str, 1)?; + } + _ => { + let param = arg_parser.free[2].clone(); + bail!("Unexpected argument: {}", param); + } + } + + let path = create_options.path.clone(); + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_CREAT | libc::O_TRUNC) + .open(path.clone())?; + + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off)?; + let image_info = match disk_fmt { + DiskFormat::Raw => { + create_options.conf.format = DiskFormat::Raw; + let mut raw_driver = RawDriver::new(file, aio, create_options.conf.clone()); + raw_driver.create_image(&create_options)? + } + DiskFormat::Qcow2 => { + create_options.conf.format = DiskFormat::Qcow2; + let mut qcow2_driver = Qcow2Driver::new(file, aio, create_options.conf.clone())?; + qcow2_driver.create_image(&create_options)? + } + }; + println!("Stratovirt-img: {}", image_info); + + Ok(()) } pub(crate) fn image_check(_args: Vec) -> Result<()> { -- Gitee From 4eb6797d7263e03d699d328d95f274bfeb10d4a5 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:15:13 +0800 Subject: [PATCH 1361/2187] Stratovirt-img: parse arguments for img check 1. Parse arguments for img check. 2. Add some data structure for img check. Signed-off-by: Xiao Ye --- block_backend/src/lib.rs | 72 +++++++++ block_backend/src/qcow2/check.rs | 228 ++++++++++++++++++++++++++++ block_backend/src/qcow2/mod.rs | 1 + block_backend/src/qcow2/refcount.rs | 12 +- image/src/img.rs | 169 ++++++++++++++++++++- 5 files changed, 474 insertions(+), 8 deletions(-) create mode 100644 block_backend/src/qcow2/check.rs diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index ccdeb902c..8faaa2fb8 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -175,6 +175,78 @@ pub struct CheckResult { pub disk_frag: DiskFragments, } +impl CheckResult { + pub fn merge_result(&mut self, res: &CheckResult) { + self.corruptions += res.corruptions; + self.leaks += res.leaks; + self.err_num += res.err_num; + self.corruptions_fixed += res.corruptions_fixed; + self.leaks_fixed += res.leaks_fixed; + self.image_end_offset = res.image_end_offset; + self.disk_frag = res.disk_frag; + } + + pub fn collect_check_message(&self) -> String { + let mut message = String::from(""); + if self.leaks_fixed != 0 || self.corruptions_fixed != 0 { + message.push_str(&format!( + "The following inconsistencies were found and repaired:\n\n\ + \t{} leaked clusters\n\ + \t{} corruptions\n\n\ + Double checking the fixed image now..\n", + self.leaks_fixed, self.corruptions_fixed + )); + } + + if self.corruptions == 0 && self.leaks == 0 && self.err_num == 0 { + message.push_str("No errors were found on the image.\n"); + } else { + if self.corruptions != 0 { + message.push_str(&format!( + "{} errors were found on the image.\n\ + Data may be corrupted, or further writes to the image may corrupt it.\n", + self.corruptions + )); + } + + if self.leaks != 0 { + message.push_str(&format!( + "{} leaked clusters were found on the image.\n\ + This means waste of disk space, but no harm to data.\n", + self.leaks + )); + } + + if self.err_num != 0 { + message.push_str(&format!( + "{} internal errors have occurred during the check.\n", + self.err_num + )); + } + } + + if self.disk_frag.total_clusters != 0 && self.disk_frag.allocated_clusters != 0 { + message.push_str(&format!( + "{}/{} = {:.2}% allocated, {:.2}% fragmented, {:.2}% compressed clusters\n", + self.disk_frag.allocated_clusters, + self.disk_frag.total_clusters, + self.disk_frag.allocated_clusters as f64 / self.disk_frag.total_clusters as f64 + * 100.0, + self.disk_frag.fragments as f64 / self.disk_frag.allocated_clusters as f64 * 100.0, + self.disk_frag.compressed_clusters as f64 + / self.disk_frag.allocated_clusters as f64 + * 100.0 + )); + } + + if self.image_end_offset != 0 { + message.push_str(&format!("Image end offset: {}\n", self.image_end_offset)); + } + + message + } +} + pub enum BlockStatus { Init, NormalIO, diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs new file mode 100644 index 000000000..f9fc8f143 --- /dev/null +++ b/block_backend/src/qcow2/check.rs @@ -0,0 +1,228 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{cell::RefCell, cmp::Ordering, mem::size_of, rc::Rc}; + +use anyhow::{bail, Context, Result}; +use byteorder::{BigEndian, ByteOrder}; + +use crate::qcow2::{ + bytes_to_clusters, + cache::{CacheTable, ENTRY_SIZE_U16, ENTRY_SIZE_U64}, + header::QcowHeader, + is_aligned, + refcount::Qcow2DiscardType, + snapshot::{QcowSnapshotHeader, QCOW2_MAX_SNAPSHOTS}, + table::Qcow2ClusterType, + Qcow2Driver, SyncAioInfo, ENTRY_BITS, ENTRY_SIZE, L1_RESERVED_MASK, L1_TABLE_OFFSET_MASK, + L2_STD_RESERVED_MASK, L2_TABLE_OFFSET_MASK, METADATA_OVERLAP_CHECK_ACTIVEL2, + METADATA_OVERLAP_CHECK_INACTIVEL2, QCOW2_MAX_L1_SIZE, QCOW2_OFFSET_COPIED, QCOW2_OFLAG_ZERO, + REFCOUNT_TABLE_OFFSET_MASK, REFCOUNT_TABLE_RESERVED_MASK, +}; +use crate::{output_msg, CheckResult, FIX_ERRORS, FIX_LEAKS}; +use util::{ + aio::{raw_write_zeroes, OpCode}, + byte_code::ByteCode, + num_ops::div_round_up, + offset_of, +}; + +pub struct Qcow2Check { + pub res: CheckResult, + pub refblock: RefcountBlock, + pub fix: u64, + pub quite: bool, +} + +impl Qcow2Check { + pub fn new(fix: u64, quite: bool, entry_bytes: usize, table_size: usize) -> Self { + Self { + res: CheckResult::default(), + refblock: RefcountBlock::new(entry_bytes, table_size), + fix, + quite, + } + } +} + +pub struct RefcountBlock { + data: Vec, + table_size: usize, + entry_bytes: usize, + max_refcount: u64, + nb_clusters: u64, +} + +impl RefcountBlock { + fn new(entry_bytes: usize, table_size: usize) -> Self { + Self { + data: vec![0_u8; table_size * entry_bytes], + table_size, + entry_bytes, + max_refcount: ((1 << (entry_bytes * 8)) - 1) as u64, + nb_clusters: table_size as u64, + } + } + + fn reset(&mut self) { + self.data.fill(0); + } + + fn extend_table(&mut self, new_size: usize) { + if new_size <= self.table_size { + return; + }; + let new_table_bytes = new_size * self.entry_bytes; + self.data.resize(new_table_bytes, 0); + self.table_size = new_size; + self.nb_clusters = new_size as u64; + } + + fn get_data(&mut self, start: usize, size: usize) -> Vec { + let mut start_bytes = start * self.entry_bytes; + let mut end_bytes = (start + size) * self.entry_bytes; + start_bytes = std::cmp::min(start_bytes, self.data.len()); + end_bytes = std::cmp::min(end_bytes, self.data.len()); + self.data[start_bytes..end_bytes].to_vec() + } + + #[inline(always)] + fn get_refcount(&mut self, idx: usize) -> Result { + if idx >= self.table_size { + return Ok(0); + } + + let start_bytes = idx * self.entry_bytes; + let end_bytes = start_bytes + self.entry_bytes; + let value = match self.entry_bytes { + ENTRY_SIZE_U16 => BigEndian::read_u16(&self.data[start_bytes..end_bytes]) as u64, + ENTRY_SIZE_U64 => BigEndian::read_u64(&self.data[start_bytes..end_bytes]), + _ => bail!("Entry size is unsupported"), + }; + Ok(value) + } + + #[inline(always)] + fn set_refcount(&mut self, idx: usize, value: u64) -> Result<()> { + if idx >= self.table_size { + bail!("Idx {:?} exceed table size {}", idx, self.table_size) + } + + let start_bytes = idx * self.entry_bytes; + let end_bytes = start_bytes + self.entry_bytes; + match self.entry_bytes { + ENTRY_SIZE_U16 => { + BigEndian::write_u16(&mut self.data[start_bytes..end_bytes], value as u16) + } + ENTRY_SIZE_U64 => BigEndian::write_u64(&mut self.data[start_bytes..end_bytes], value), + _ => bail!("Entry size is unsupported"), + } + Ok(()) + } + + /// Alloc blocks based on reference recorded in the refcount block, and the reference + /// of these clusters should be updated later by calling set_refcount function. + /// + /// # Arguments + /// + /// * `total_counts` - Total number of consecutive clusters that need to be allocated. + /// * `cluster_bits` - Bits of cluster. + /// * `first_free_cluster` - Alloc consecutive free data from first_free_cluster. + /// * `sync_aio` - The newly allocated data block needs to reset to 0 on disk. + fn alloc_clusters( + &mut self, + total_counts: u64, + cluster_bits: u64, + first_free_cluster: &mut usize, + sync_aio: Rc>, + ) -> Result { + let cluster_size = 1 << cluster_bits; + let mut first_update: bool = true; + let mut cluster_idx = *first_free_cluster; + let mut continue_clusters: usize = 0; + while continue_clusters < total_counts as usize { + if self.get_refcount(cluster_idx as usize)? == 0 { + continue_clusters += 1; + if first_update { + *first_free_cluster = cluster_idx; + first_update = false; + } + } else { + continue_clusters = 0; + } + + cluster_idx += 1; + } + + if cluster_idx > self.table_size { + self.extend_table(cluster_idx); + } + + let start_idx = cluster_idx - total_counts as usize; + let zero_buf = vec![0_u8; cluster_size]; + for i in 0..total_counts { + let cluster_offset = (start_idx as u64 + i) << cluster_bits; + self.set_refcount(start_idx + i as usize, 1)?; + // Write zero to disk + let ret = raw_write_zeroes( + sync_aio.borrow_mut().fd, + cluster_offset as usize, + cluster_size as u64, + ); + if ret < 0 { + sync_aio + .borrow_mut() + .write_buffer(cluster_offset, &zero_buf)?; + } + } + + Ok((start_idx as u64) << cluster_bits) + } +} + +#[cfg(test)] +mod test { + use super::RefcountBlock; + + #[test] + fn test_refcount_block_basic() { + let mut refblock = RefcountBlock::new(2, 10); + assert!(refblock.set_refcount(10, 1).is_err()); + assert_eq!(refblock.max_refcount, 65535); + assert_eq!(refblock.table_size, 10); + assert!(refblock.set_refcount(0, 1).is_ok()); + assert!(refblock.set_refcount(1, 7).is_ok()); + assert!(refblock.set_refcount(9, 9).is_ok()); + + // Get inner dat + let mut vec_1 = (1 as u16).to_be_bytes().to_vec(); + let mut vec_2 = (7 as u16).to_be_bytes().to_vec(); + vec_1.append(&mut vec_2); + let buf = refblock.get_data(0, 2); + assert_eq!(buf, vec_1); + + // Get refcount + let count = refblock.get_refcount(0).unwrap(); + assert_eq!(count, 1); + let count = refblock.get_refcount(9).unwrap(); + assert_eq!(count, 9); + + refblock.extend_table(10); + refblock.extend_table(11); + let count = refblock.get_refcount(10).unwrap(); + assert_eq!(count, 0); + + refblock.reset(); + let count = refblock.get_refcount(9).unwrap(); + assert_eq!(count, 0); + } +} diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 717b8d54e..6ef7cf3e6 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod cache; +pub mod check; pub mod header; pub mod refcount; pub mod snapshot; diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index a8a0ac97e..6c0406b7a 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -65,18 +65,18 @@ impl DiscardTask { pub struct RefCount { pub refcount_table: Vec, sync_aio: Rc>, - refcount_blk_cache: Qcow2Cache, + pub(crate) refcount_blk_cache: Qcow2Cache, pub discard_list: Vec, /// Pass the discard operation if refcount of cluster decrease to 0. pub discard_passthrough: Vec, free_cluster_index: u64, - refcount_table_offset: u64, - refcount_table_clusters: u32, + pub(crate) refcount_table_offset: u64, + pub(crate) refcount_table_clusters: u32, /// Number of refcount table entries. - refcount_table_size: u64, - refcount_blk_bits: u32, + pub(crate) refcount_table_size: u64, + pub(crate) refcount_blk_bits: u32, /// Number of refcount block entries. - refcount_blk_size: u32, + pub(crate) refcount_blk_size: u32, refcount_max: u64, /// Cluster size in bytes. cluster_size: u64, diff --git a/image/src/img.rs b/image/src/img.rs index 1740b10f3..f4cb718d8 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -32,6 +32,79 @@ use util::{ file::{lock_file, open_file, unlock_file}, }; +pub struct ImageFile { + file: File, + path: String, +} + +impl ImageFile { + fn create(path: &str, read_only: bool) -> Result { + let file = open_file(path, read_only, false)?; + + // Add write lock for image file. + lock_file(&file, path, read_only).with_context(|| { + format!( + "Could not open '{0}': Failed to get \"write\" lock\n\ + Is another process using the image {0}", + path + ) + })?; + + Ok(Self { + file, + path: path.to_string(), + }) + } + + /// If the image format is not specified by user, active detection is required + /// For qcow2: will check its version in header. + /// If the image does not belong to any supported format, it defaults to raw. + fn detect_img_format(&self) -> Result { + let mut buf = vec![0_u8; SECTOR_SIZE as usize]; + self.file.read_at(&mut buf, 0)?; + + let mut disk_format = DiskFormat::Raw; + if let Ok(header) = QcowHeader::from_vec(&buf) { + if header.version == 3 { + disk_format = DiskFormat::Qcow2; + } + } + + Ok(disk_format) + } + + fn check_img_format( + &self, + input_fmt: Option, + detect_fmt: DiskFormat, + ) -> Result { + let real_fmt = match input_fmt { + Some(fmt) if fmt == DiskFormat::Raw => DiskFormat::Raw, + Some(fmt) => { + if fmt != detect_fmt { + bail!( + "Could not open '{}': Image is not in {} fmt", + self.path, + fmt.to_string() + ); + } + fmt + } + _ => detect_fmt, + }; + + Ok(real_fmt) + } +} + +impl Drop for ImageFile { + fn drop(&mut self) { + if let Err(e) = unlock_file(&self.file, &self.path) { + println!("{:?}", e); + } + } +} + pub(crate) fn image_create(args: Vec) -> Result<()> { let mut create_options = CreateOptions::default(); let mut arg_parser = ArgsParse::create(vec!["h", "help"], vec!["f"], vec!["o"]); @@ -109,14 +182,106 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { Ok(()) } -pub(crate) fn image_check(_args: Vec) -> Result<()> { - todo!() +pub(crate) fn image_check(args: Vec) -> Result<()> { + let mut arg_parser = + ArgsParse::create(vec!["no_print_error", "h", "help"], vec!["f", "r"], vec![]); + arg_parser.parse(args)?; + + if arg_parser.opt_present("h") || arg_parser.opt_present("help") { + print_help(); + return Ok(()); + } + + let mut quite = false; + let mut disk_fmt: Option = None; + let mut fix = NO_FIX; + + if arg_parser.opt_present("no_print_error") { + quite = true; + } + if let Some(fmt) = arg_parser.opt_str("f") { + disk_fmt = Some(DiskFormat::from_str(&fmt)?); + } + + if let Some(kind) = arg_parser.opt_str("r") { + if kind == "leaks".to_string() { + fix |= FIX_LEAKS; + } else if kind == "all".to_string() { + fix |= FIX_LEAKS; + fix |= FIX_ERRORS; + } else { + bail!( + "Unknown option value for -r {:?}(expects 'leaks' or 'all')", + kind + ); + } + } + + // Parse image path. + let len = arg_parser.free.len(); + let path = match len { + 0 => bail!("Image check requires path"), + 1 => arg_parser.free[0].clone(), + _ => { + let param = arg_parser.free[1].clone(); + bail!("Unexpected argument: {}", param); + } + }; + + let read_only = fix == NO_FIX; + let image_file = ImageFile::create(&path, read_only)?; + let detect_fmt = image_file.detect_img_format()?; + let real_fmt = image_file.check_img_format(disk_fmt, detect_fmt)?; + + let mut check_res = CheckResult::default(); + let file = image_file.file.try_clone()?; + match real_fmt { + DiskFormat::Raw => { + bail!("stratovirt-img: This image format does not support checks"); + } + DiskFormat::Qcow2 => { + let mut conf = BlockProperty::default(); + conf.format = DiskFormat::Qcow2; + let mut qcow2_driver = create_qcow2_driver_for_check(file, conf)?; + let ret = qcow2_driver.check_image(&mut check_res, quite, fix); + let check_message = check_res.collect_check_message(); + print!("{}", check_message); + ret + } + } } pub(crate) fn image_snapshot(_args: Vec) -> Result<()> { todo!() } +pub(crate) fn create_qcow2_driver_for_check( + file: File, + conf: BlockProperty, +) -> Result> { + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); + let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()) + .with_context(|| "Failed to create qcow2 driver")?; + + qcow2_driver + .load_header() + .with_context(|| "Failed to load header")?; + qcow2_driver + .table + .init_table_info(&qcow2_driver.header, &conf) + .with_context(|| "Failed to create qcow2 table")?; + qcow2_driver + .refcount + .init_refcount_info(&qcow2_driver.header, &conf); + qcow2_driver + .load_refcount_table() + .with_context(|| "Failed to load refcount table")?; + qcow2_driver + .snapshot + .set_cluster_size(qcow2_driver.header.cluster_size()); + Ok(qcow2_driver) +} + pub fn print_help() { print!( r#"Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -- Gitee From f10a7ba5e6fc208ca829315e6874440860a5880a Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:25:43 +0800 Subject: [PATCH 1362/2187] Stratovirt-img: check the refcount of cluster Add checking of cluster refcount during image check. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/check.rs | 832 +++++++++++++++++++++++++++++++ block_backend/src/qcow2/mod.rs | 23 +- 2 files changed, 853 insertions(+), 2 deletions(-) diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index f9fc8f143..6394ad235 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -189,6 +189,838 @@ impl RefcountBlock { } } +impl Qcow2Driver { + pub(crate) fn check_read_snapshot_table( + &mut self, + res: &mut CheckResult, + quite: bool, + fix: u64, + ) -> Result<()> { + todo!() + } + + pub fn check_fix_snapshot_table( + &mut self, + res: &mut CheckResult, + quite: bool, + fix: u64, + ) -> Result<()> { + todo!() + } + + /// Rebuild a new refcount table according to metadata, including active l1 table, active l2 table, + /// snapshot table, refcount table and refcount block. + pub(crate) fn check_refcounts(&mut self, check: &mut Qcow2Check) -> Result<()> { + let cluster_bits = self.header.cluster_bits as u64; + let cluster_size = 1 << cluster_bits; + let virtual_size = self.header.size; + check.res.disk_frag.total_clusters = div_round_up(virtual_size, cluster_size).unwrap(); + + let file_len = self.driver.disk_size()?; + let nb_clusters = div_round_up(file_len, cluster_size).unwrap(); + if nb_clusters > i32::MAX as u64 { + check.res.err_num += 1; + bail!("Number of clusters exceed {:?}", i32::MAX); + } + + // Rebuild refcount block data in memory + self.calculate_refcount(check)?; + + // Compare the refcount block in Memory with real refcount block. + let pre_compare_res = check.res; + self.compare_refcounts(check)?; + + if check.res.need_rebuild && check.fix & FIX_ERRORS != 0 { + let old_res = check.res; + let mut fresh_leak = 0; + + output_msg!(check.quite, "Rebuilding refcount structure"); + self.rebuild_refcount_structure(check)?; + + check.res.corruptions = 0; + check.res.leaks = 0; + check.res.need_rebuild = false; + check.refblock.reset(); + + // This operation will leaks the old refcount table, so fix it. + self.calculate_refcount(check)?; + if check.fix & FIX_LEAKS != 0 { + let saved_res = check.res; + check.res = CheckResult::default(); + self.compare_refcounts(check)?; + if check.res.need_rebuild { + output_msg!( + check.quite, + "ERROR rebuilt refcount structure is still broken" + ); + } + fresh_leak = check.res.leaks; + check.res = saved_res; + } + + if check.res.corruptions < old_res.corruptions { + check.res.corruptions_fixed += old_res.corruptions - check.res.corruptions; + } + if check.res.leaks < old_res.leaks { + check.res.leaks_fixed += old_res.leaks - check.res.leaks; + } + + check.res.leaks += fresh_leak; + } else if check.fix != 0 { + if check.res.need_rebuild { + bail!("ERROR need to rebuild refcount structures"); + } + + if check.res.leaks | check.res.corruptions != 0 { + check.res = pre_compare_res; + check.fix = 0; + self.compare_refcounts(check)?; + } + } + + // Check OFLAG_COPIED in l1 table and l2 table. + self.check_oflag_copied(check) + } + + /// Calculate the reference of all cluster data according to l1 and l2 table. + fn calculate_refcount(&mut self, check: &mut Qcow2Check) -> Result<()> { + let file_len = self.driver.disk_size()?; + + // Increase the refcount of qcow2 header. + self.increase_refcounts( + 0, + self.header.cluster_size(), + file_len, + self.header.cluster_bits as u64, + check, + )?; + + // Increase the refcount of active l1 table. + let active_l1_offset = self.header.l1_table_offset; + let active_l1_size = self.header.l1_size; + self.check_refcounts_l1(active_l1_offset, active_l1_size as u64, true, check)?; + + // Increase the refcount of snapshot table. + for idx in 0..self.header.nb_snapshots { + let snap = self.snapshot.snapshots[idx as usize].clone(); + let snap_l1_offset = snap.l1_table_offset; + let snap_l1_size = snap.l1_size; + if !is_aligned(self.header.cluster_size(), snap_l1_offset) { + output_msg!( + check.quite, + "ERROR snapshot {:?}({:?}) l1_offset={:#X} L1 table is not cluster aligned; snapshot table entry corrupted", + snap.id, snap.name, snap_l1_offset + ); + check.res.corruptions += 1; + continue; + } + + if snap_l1_size as u64 > QCOW2_MAX_L1_SIZE / ENTRY_SIZE { + output_msg!( + check.quite, + "ERROR snapshot {:?}({:?}) l1_size={:?} l1 table is too large; snapshot table entry courropted", + snap.id, snap.name, snap_l1_size + ); + check.res.corruptions += 1; + continue; + } + + self.check_refcounts_l1(snap_l1_offset, snap_l1_size as u64, false, check)?; + } + + let snap_table_offset = self.header.snapshots_offset; + let snap_table_size = self.snapshot.snapshot_size; + if snap_table_offset != 0 && snap_table_size != 0 { + self.increase_refcounts( + snap_table_offset, + snap_table_size, + file_len, + self.header.cluster_bits as u64, + check, + )?; + } + + let reftable_offset = self.header.refcount_table_offset; + let reftable_bytes = + self.header.refcount_table_clusters as u64 * self.header.cluster_size(); + self.increase_refcounts( + reftable_offset, + reftable_bytes, + file_len, + self.header.cluster_bits as u64, + check, + )?; + + self.check_refcount_block(check) + } + + /// Traverse all l1 tables and data blocks indexed by l1 table and calculate the real + /// reference of these clusters, and performs some checks as well. + fn check_refcounts_l1( + &mut self, + l1_offset: u64, + l1_size: u64, + is_active: bool, + check: &mut Qcow2Check, + ) -> Result<()> { + if l1_offset == 0 || l1_size == 0 { + return Ok(()); + } + + let l1_size_bytes = l1_size * ENTRY_SIZE; + let file_len = self.driver.disk_size()?; + // Increase the refcount of cluster which l1 table is located. + self.increase_refcounts( + l1_offset, + l1_size_bytes, + file_len, + self.header.cluster_bits as u64, + check, + )?; + let l1_table = self + .sync_aio + .borrow_mut() + .read_ctrl_cluster(l1_offset, l1_size as u64)?; + + // Entry in l1 table + for idx in 0..l1_size { + let l1_entry = match l1_table.get(idx as usize) { + Some(v) => v, + None => continue, + }; + + if l1_entry == &0 { + continue; + } + + // The error in reserved field of l1 entry not need to be fixed, as it not effect the basic functions + if l1_entry & L1_RESERVED_MASK != 0 { + output_msg!( + check.quite, + "ERROR found L1 entry with reserved bits set {:#X}", + l1_entry + ); + check.res.corruptions += 1; + } + + let l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; + self.increase_refcounts( + l2_offset, + self.header.cluster_size(), + file_len, + self.header.cluster_bits as u64, + check, + )?; + + if !is_aligned(self.header.cluster_size(), l2_offset) { + output_msg!( + check.quite, + "ERROR l2_offset={:#X}: Table is not \n\ + cluster aligned; l1 entry corrupted", + l2_offset + ); + check.res.corruptions += 1; + } + + self.check_refcount_l2(l2_offset, is_active, file_len, check)?; + } + + // The l2 entry on disk may be modified. + if check.fix & FIX_ERRORS != 0 { + self.table.l2_table_cache.clear_cache(); + } + + Ok(()) + } + + fn check_refcount_l2( + &mut self, + l2_offset: u64, + is_active: bool, + file_len: u64, + check: &mut Qcow2Check, + ) -> Result<()> { + let cluster_bits = self.header.cluster_bits as u64; + let cluster_size = 1 << cluster_bits; + let l2_size = cluster_size >> ENTRY_BITS; + + // Read l2 table from disk + let mut l2_table: Vec; + match self + .sync_aio + .borrow_mut() + .read_ctrl_cluster(l2_offset, l2_size) + { + Ok(buf) => l2_table = buf, + Err(e) => { + check.res.err_num += 1; + bail!("ERROR: I/O error in reading l2 table {}", e); + } + }; + + let err_flag = if check.fix & FIX_ERRORS != 0 { + "Repairing".to_string() + } else { + "ERROR".to_string() + }; + let mut next_continue_offset: u64 = 0; + for l2_idx in 0..l2_size { + let l2_entry = match l2_table.get(l2_idx as usize) { + Some(value) => *value, + None => continue, + }; + let cluster_type = Qcow2ClusterType::get_cluster_type(l2_entry); + // The error in reserved field of l2 entry not need to be fixed, as it not effect the basic functions + if cluster_type != Qcow2ClusterType::Compressed && l2_entry & L2_STD_RESERVED_MASK != 0 + { + output_msg!( + check.quite, + "ERROR found l2 entry with reserved bits set: {:#X}", + l2_entry + ); + check.res.corruptions += 1; + } + + match cluster_type { + Qcow2ClusterType::Compressed => { + output_msg!(check.quite, "Compressed is not supported"); + } + Qcow2ClusterType::ZeroPlain | Qcow2ClusterType::Unallocated => continue, + Qcow2ClusterType::ZeroAlloc | Qcow2ClusterType::Normal => { + let cluster_offset = l2_entry & L1_TABLE_OFFSET_MASK; + + if !is_aligned(cluster_size, cluster_offset) { + check.res.corruptions += 1; + let contains_data = l2_entry & QCOW2_OFLAG_ZERO != 0; + if !contains_data { + output_msg!( + check.quite, + "{} offset={:#X}: Preallocated cluster is not properly aligned; L2 entry corrupted.", + err_flag, cluster_offset + ); + if check.fix & FIX_ERRORS != 0 { + self.repair_l2_entry( + &mut l2_table, + l2_offset, + l2_idx, + is_active, + check, + )?; + } + continue; + } else { + output_msg!( + check.quite, + "ERROR offset={:#X}: Data cluster is not properly aligned; L2 entry corrupted.", + cluster_offset + ); + } + } + + // Disk Fragmentation + check.res.disk_frag.allocated_clusters += 1; + if next_continue_offset != 0 && next_continue_offset != cluster_offset { + check.res.disk_frag.fragments += 1; + } + next_continue_offset = cluster_offset + cluster_size; + + // Mark cluster in refcount table. + self.increase_refcounts( + cluster_offset, + cluster_size, + file_len, + cluster_bits, + check, + )?; + } + } + } + Ok(()) + } + + /// Fix l2 entry with oflag zero. + fn repair_l2_entry( + &mut self, + l2_table: &mut [u64], + l2_offset: u64, + l2_idx: u64, + is_active: bool, + check: &mut Qcow2Check, + ) -> Result<()> { + let ignore = if is_active { + METADATA_OVERLAP_CHECK_ACTIVEL2 + } else { + METADATA_OVERLAP_CHECK_INACTIVEL2 + }; + let l2e_offset = l2_offset + l2_idx * ENTRY_SIZE; + l2_table[l2_idx as usize] = QCOW2_OFLAG_ZERO; + + let ret = self.check_overlap(ignore, l2e_offset, ENTRY_SIZE); + if ret != 0 { + bail!("ERROR: Overlap check failed"); + } + + // Write sync to disk + let buf = QCOW2_OFLAG_ZERO.to_be_bytes().to_vec(); + if let Err(e) = self.sync_aio.borrow_mut().write_buffer(l2e_offset, &buf) { + bail!("ERROR: Failed to overwrite L2 table entry: {:?}", e); + }; + + check.res.corruptions -= 1; + check.res.corruptions_fixed += 1; + Ok(()) + } + + fn check_refcount_block(&mut self, check: &mut Qcow2Check) -> Result<()> { + let cluster_bits = self.header.cluster_bits as u64; + let cluster_size = 1 << cluster_bits; + let file_len = self.driver.disk_size()?; + let nb_clusters = bytes_to_clusters(file_len, cluster_size)?; + let err_flag = if check.fix & FIX_ERRORS != 0 { + "Repairing".to_string() + } else { + "ERROR".to_string() + }; + + let reftable_size = self.refcount.refcount_table.len(); + let reftable = self.refcount.refcount_table.clone(); + for idx in 0..reftable_size { + let reftable_entry = reftable[idx as usize]; + let refblock_offset = reftable_entry & REFCOUNT_TABLE_OFFSET_MASK; + let cluster_idx = refblock_offset >> cluster_bits; + if reftable_entry & REFCOUNT_TABLE_RESERVED_MASK != 0 { + output_msg!( + check.quite, + "ERROR refcount table entry {:?} has reserved bits set", + idx + ); + check.res.corruptions += 1; + check.res.need_rebuild = true; + continue; + } + + if !is_aligned(cluster_size, refblock_offset) { + output_msg!( + check.quite, + "ERROR refcount block {:?} is not cluster aligned; refcount table entry corrupted", + idx + ); + check.res.corruptions += 1; + check.res.need_rebuild = true; + continue; + } + + if cluster_idx >= nb_clusters { + output_msg!( + check.quite, + "{} refcount block {} is outside image", + err_flag, + cluster_idx + ); + check.res.corruptions += 1; + + if check.fix & FIX_ERRORS != 0 { + // Need to try resize the image size. + check.res.need_rebuild = true; + } + continue; + } + + if refblock_offset != 0 { + self.increase_refcounts( + refblock_offset, + cluster_size, + file_len, + cluster_bits, + check, + )?; + let rc_value = check.refblock.get_refcount(cluster_idx as usize)?; + // The refcount for data clusters of refcount block must be 1 + if rc_value != 1 { + output_msg!( + check.quite, + "ERROR refcount block {:?}, refcount={:?}", + idx, + rc_value + ); + check.res.need_rebuild = true; + } + } + } + + Ok(()) + } + + /// Compare the real references of clusters with the references recorded on the disk, + /// and choose whether to repair it on the disk. + pub(crate) fn compare_refcounts(&mut self, check: &mut Qcow2Check) -> Result<()> { + self.table.load_l1_table()?; + self.table.l2_table_cache.clear_cache(); + self.load_refcount_table()?; + self.refcount.refcount_blk_cache.clear_cache(); + + let mut rc_value_1: u16; + let mut rc_value_2: u16; + let cluster_bits = self.header.cluster_bits; + let file_len = self.driver.disk_size()?; + let nb_clusters = div_round_up(file_len, self.header.cluster_size()).unwrap(); + for cluster_idx in 0..nb_clusters { + match self.refcount.get_refcount(cluster_idx << cluster_bits) { + Err(e) => { + output_msg!(check.quite, "Cant't get refcount for cluster {:?}", e); + check.res.err_num += 1; + continue; + } + Ok(value) => rc_value_1 = value, + }; + rc_value_2 = check.refblock.get_refcount(cluster_idx as usize)? as u16; + + if rc_value_1 != 0 || rc_value_2 != 0 { + check.res.image_end_offset = (cluster_idx + 1) << cluster_bits; + } + + if rc_value_1 != rc_value_2 { + let mut need_fixed: bool = false; + if rc_value_1 == 0 { + // The refcount block may not have assigned cluster, + // so need to rebuild refcount structure. + check.res.need_rebuild = true; + } else if (rc_value_1 > rc_value_2 && check.fix & FIX_LEAKS != 0) + || (rc_value_1 < rc_value_2 && check.fix & FIX_ERRORS != 0) + { + need_fixed = true; + } + let err_flag = if need_fixed { + "Repairing" + } else if rc_value_1 > rc_value_2 { + "Leaked" + } else { + "ERROR" + }; + output_msg!( + check.quite, + "{} cluster {:?} refcount={:?} reference={:?}", + err_flag, + cluster_idx, + rc_value_1, + rc_value_2 + ); + + if need_fixed { + let added = rc_value_2 as i32 - rc_value_1 as i32; + let cluster_offset = cluster_idx << cluster_bits; + self.refcount.update_refcount( + cluster_offset, + 1, + added, + false, + &Qcow2DiscardType::Always, + )?; + if added < 0 { + check.res.leaks_fixed += 1; + } else { + check.res.corruptions_fixed += 1; + } + continue; + } + + match rc_value_1.cmp(&rc_value_2) { + Ordering::Less => check.res.corruptions += 1, + Ordering::Greater => check.res.leaks += 1, + Ordering::Equal => {} + }; + } + } + + if !self.refcount.discard_list.is_empty() { + self.refcount.sync_process_discards(OpCode::Discard); + } + self.refcount.flush()?; + + Ok(()) + } + + // For the entry in active table, the reference equals to 1 means don't need to copy on write, + // So the oflag of copied must set to nonzero. + fn check_oflag_copied(&mut self, check: &mut Qcow2Check) -> Result<()> { + let l1_size = self.table.l1_table.len(); + let l2_size = div_round_up(self.header.cluster_size(), ENTRY_SIZE).unwrap(); + let mut l1_dirty = false; + let mut l1_corruptions: i32 = 0; + let mut l1_corruptions_fixed: i32 = 0; + + let repair = if check.fix & FIX_ERRORS != 0 { + true + } else if check.fix & FIX_LEAKS != 0 { + check.res.err_num == 0 && check.res.corruptions == 0 && check.res.leaks == 0 + } else { + false + }; + + for l1_idx in 0..l1_size { + let l1_entry = self.table.l1_table[l1_idx as usize]; + let l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; + if l2_offset == 0 { + continue; + } + let rc_value = self.refcount.get_refcount(l2_offset)?; + if (rc_value == 1) ^ (l1_entry & QCOW2_OFFSET_COPIED != 0) { + l1_corruptions += 1; + output_msg!( + check.quite, + "{} OFLAG_COPIED L2 cluster: l1_index={} l1_entry={:#X} refcount={}", + if repair { "Repairing" } else { "ERROR" }, + l1_idx, + l1_entry, + rc_value + ); + if repair { + let new_l1_entry = if rc_value == 1 { + l1_entry | QCOW2_OFFSET_COPIED + } else { + l1_entry & !QCOW2_OFFSET_COPIED + }; + self.table.l1_table[l1_idx as usize] = new_l1_entry; + l1_dirty = true; + l1_corruptions -= 1; + l1_corruptions_fixed += 1; + } + } + + let mut num_repaired = 0; + let l2_buf = self.load_cluster(l2_offset)?; + let l2_table = Rc::new(RefCell::new(CacheTable::new( + l2_offset, + l2_buf, + ENTRY_SIZE_U64, + )?)); + + for l2_idx in 0..l2_size { + let cluster_entry = l2_table.borrow_mut().get_entry_map(l2_idx as usize)?; + let cluster_offset = cluster_entry & L2_TABLE_OFFSET_MASK; + let cluster_type = Qcow2ClusterType::get_cluster_type(cluster_entry); + + if cluster_type == Qcow2ClusterType::Normal + || cluster_type == Qcow2ClusterType::ZeroAlloc + { + let rc_value = match self.refcount.get_refcount(cluster_offset) { + Ok(value) => value, + Err(_) => continue, + }; + + if (rc_value == 1) ^ (cluster_entry & QCOW2_OFFSET_COPIED != 0) { + check.res.corruptions += 1; + output_msg!( + check.quite, + "{} OFLAG_COPIED data cluster: l2_entry={:#X} refcount={:?}", + if repair { "Repairing" } else { "ERROR" }, + cluster_entry, + rc_value + ); + if repair { + let new_cluster_entry = if rc_value == 1 { + cluster_entry | QCOW2_OFFSET_COPIED + } else { + cluster_entry & !QCOW2_OFFSET_COPIED + }; + num_repaired += 1; + l2_table + .borrow_mut() + .set_entry_map(l2_idx as usize, new_cluster_entry)?; + } + } + } + } + + let mut borrowed_l2 = l2_table.borrow_mut(); + if num_repaired != 0 && borrowed_l2.dirty_info.is_dirty { + if self.check_overlap( + METADATA_OVERLAP_CHECK_ACTIVEL2, + l2_offset, + self.header.cluster_size(), + ) != 0 + { + bail!("ERROR: Could not write L2 table; metadata overlap check failed"); + } + + self.sync_aio.borrow_mut().write_dirty_info( + borrowed_l2.addr, + borrowed_l2.get_value(), + borrowed_l2.dirty_info.start, + borrowed_l2.dirty_info.end, + )?; + borrowed_l2.dirty_info.clear(); + } + drop(borrowed_l2); + check.res.corruptions -= num_repaired; + check.res.corruptions_fixed += num_repaired; + } + + if l1_dirty && repair { + if let Err(e) = self.table.save_l1_table() { + check.res.err_num += 1; + return Err(e); + } + } + check.res.corruptions += l1_corruptions; + check.res.corruptions_fixed += l1_corruptions_fixed; + + // The entry on disk may not be consistent with the cache. + self.table.l2_table_cache.clear_cache(); + Ok(()) + } + + /// The error in refcount block can't be fixed, so has to rebuild the structure of refcount table + fn rebuild_refcount_structure(&mut self, check: &mut Qcow2Check) -> Result<()> { + let mut cluster_idx: u64 = 0; + let mut first_free_cluster: u64 = 0; + let mut reftable_offset: u64 = 0; + let mut new_reftable: Vec = Vec::new(); + let mut reftable_clusters: u64 = 0; + let cluster_bits = self.header.cluster_bits as u64; + let refblock_bits: u64 = (cluster_bits + 3 - self.header.refcount_order as u64) as u64; + let refblock_size: u64 = 1 << refblock_bits; + + // self.refblock.nb_clusters means the maximum number of clusters that can be represented by + // the refcount table in memory. During this loop, this value may increase as the Refcount table expands. + // This operation will leaks old refcount table and old refcount block table, and it will be fixed later. + while cluster_idx < check.refblock.nb_clusters { + if check.refblock.get_refcount(cluster_idx as usize)? == 0 { + cluster_idx += 1; + continue; + } + let refblock_idx = (cluster_idx >> refblock_bits) as usize; + let refblock_start = (refblock_idx << refblock_bits) as u64; + // Refblock data with index smaller than refblock_start has been written to disk. + first_free_cluster = std::cmp::max(refblock_start, first_free_cluster); + + // Alloc a new cluster from first_free_cluster. + let refblock_offset = check.refblock.alloc_clusters( + 1, + cluster_bits, + &mut (first_free_cluster as usize), + self.sync_aio.clone(), + )?; + + // Extend the refcount table + if new_reftable.len() <= refblock_idx as usize { + new_reftable.resize(refblock_idx + 1, 0); + // Need to reallocate clusters for new refcount table. + reftable_offset = 0; + } + new_reftable[refblock_idx] = refblock_offset; + + // Alloc clusters for new refcount table. + if refblock_idx + 1 >= (check.refblock.nb_clusters >> refblock_bits) as usize + && reftable_offset == 0 + { + let reftable_size = new_reftable.len() as u64; + reftable_clusters = + bytes_to_clusters(reftable_size * ENTRY_SIZE, self.header.cluster_size())?; + reftable_offset = check.refblock.alloc_clusters( + reftable_clusters, + cluster_bits as u64, + &mut (first_free_cluster as usize), + self.sync_aio.clone(), + )?; + } + + // New allocated refblock offset is overlap with other matedata. + if self.check_overlap(0, refblock_offset, self.header.cluster_size()) != 0 { + bail!("ERROR writing refblock"); + } + + // Refcount block data written back to disk + let start = refblock_idx * refblock_size as usize; + let size = refblock_size; + let refblock_buf = check.refblock.get_data(start, size as usize); + self.sync_aio + .borrow_mut() + .write_buffer(refblock_offset, &refblock_buf)?; + + // All data of this refcount block has been written to disk, so go to the next refcount block. + cluster_idx = refblock_start + refblock_size; + } + + if reftable_offset == 0 { + bail!("ERROR allocating reftable"); + } + + // Write new refcount table to disk + let reftable_size = new_reftable.len(); + if self.check_overlap(0, reftable_offset, reftable_size as u64 * ENTRY_SIZE) != 0 { + bail!("ERROR writing reftable"); + } + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(reftable_offset, &new_reftable)?; + + // Update header message to disk + // Inclust reftable offset and reftable cluster + let mut new_header = self.header.clone(); + new_header.refcount_table_offset = reftable_offset; + new_header.refcount_table_clusters = reftable_clusters as u32; + let header_buf = new_header.to_vec(); + self.sync_aio.borrow_mut().write_buffer(0, &header_buf)?; + self.header.refcount_table_offset = new_header.refcount_table_offset; + self.header.refcount_table_clusters = new_header.refcount_table_clusters; + + // Update the info of refcount table + self.refcount.refcount_table_offset = new_header.refcount_table_offset; + self.refcount.refcount_table_clusters = new_header.refcount_table_clusters; + self.refcount.refcount_table_size = new_reftable.len() as u64; + self.refcount.refcount_table = new_reftable; + self.refcount.refcount_blk_cache.clear_cache(); + + Ok(()) + } + + /// Increase the refcounts for a range of clusters. + fn increase_refcounts( + &mut self, + offset: u64, + size: u64, + file_len: u64, + cluster_bits: u64, + check: &mut Qcow2Check, + ) -> Result<()> { + if size == 0 { + return Ok(()); + } + + let cluster_size = 1 << cluster_bits; + if offset + size > file_len && offset + size - file_len >= cluster_size { + check.res.corruptions += 1; + bail!( + "ERROR: counting reference for region exceeding the end of the file by one cluster or more: offset {:#X} size {:#X}", + offset, size + ); + } + + let mut offset_beg = offset & !(cluster_size - 1); + let offset_end = (offset + size - 1) & !(cluster_size - 1); + while offset_beg <= offset_end { + let cluster_idx = offset_beg >> cluster_bits; + let rc_value = check.refblock.get_refcount(cluster_idx as usize)?; + if rc_value == check.refblock.max_refcount { + output_msg!( + check.quite, + "ERROR: overflow cluster offset={:#X}", + offset_beg + ); + check.res.corruptions += 1; + offset_beg += cluster_size; + continue; + } + check + .refblock + .set_refcount(cluster_idx as usize, rc_value + 1)?; + offset_beg += cluster_size; + } + Ok(()) + } +} + #[cfg(test)] mod test { use super::RefcountBlock; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 6ef7cf3e6..9f18ce867 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -34,7 +34,9 @@ use byteorder::{BigEndian, ByteOrder}; use log::{debug, error, info}; use once_cell::sync::Lazy; -use self::{cache::ENTRY_SIZE_U64, header::QCOW_MAGIC, refcount::Qcow2DiscardType}; +use self::{ + cache::ENTRY_SIZE_U64, check::Qcow2Check, header::QCOW_MAGIC, refcount::Qcow2DiscardType, +}; use crate::{ file::{CombineRequest, FileDriver}, qcow2::{ @@ -1455,7 +1457,24 @@ impl BlockDriverOps for Qcow2Driver { } fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()> { - todo!() + let cluster_size = self.header.cluster_size(); + let refcount_order = self.header.refcount_order; + let entry_bytes = ((1 << refcount_order) / 8) as usize; + let file_len = self.driver.disk_size().unwrap(); + let nb_clusters = div_round_up(file_len, cluster_size).unwrap(); + let mut qcow2_check = Qcow2Check::new(fix, quite, entry_bytes, nb_clusters as usize); + + self.check_read_snapshot_table(res, quite, fix)?; + + let mut ret = self.check_refcounts(&mut qcow2_check); + if ret.is_err() { + res.merge_result(&qcow2_check.res); + return ret; + } + + ret = self.check_fix_snapshot_table(res, quite, fix); + res.merge_result(&qcow2_check.res); + ret } fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { -- Gitee From b898f3f4696f7a83c98165cf141354ddbfe140e0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:42:23 +0800 Subject: [PATCH 1363/2187] Stratovirt-img: check read snapshot Checking read snapshot and fix the error during the image check. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/check.rs | 92 ++++++++++++++++++++++++++++- block_backend/src/qcow2/mod.rs | 63 +++++++++++++++++++- block_backend/src/qcow2/snapshot.rs | 57 +++++++++++++++--- 3 files changed, 201 insertions(+), 11 deletions(-) diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index 6394ad235..2cc6cf9be 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -190,13 +190,91 @@ impl RefcountBlock { } impl Qcow2Driver { + /// Read the snapshot table from the disk and verify it. pub(crate) fn check_read_snapshot_table( &mut self, res: &mut CheckResult, quite: bool, fix: u64, ) -> Result<()> { - todo!() + let mut extra_data_dropped: i32 = 0; + let mut nb_clusters_reduced: i32 = 0; + let mut nb_snapshots = self.header.nb_snapshots; + + // Validate the number of snapshots. + if nb_snapshots as usize > QCOW2_MAX_SNAPSHOTS { + if fix & FIX_ERRORS == 0 { + res.err_num += 1; + bail!("You can force-remove all {} overhanging snapshots with \"stratovirt-img check -r all\"",nb_snapshots as usize - QCOW2_MAX_SNAPSHOTS); + } + + output_msg!( + quite, + "Discarding {} overhanging snapshots", + nb_snapshots as usize - QCOW2_MAX_SNAPSHOTS + ); + nb_clusters_reduced += (nb_snapshots as usize - QCOW2_MAX_SNAPSHOTS) as i32; + nb_snapshots = QCOW2_MAX_SNAPSHOTS as u32; + } + + let snapshot_table_length = size_of::() as u64; + let snapshot_table_offset = self.header.snapshots_offset; + // Validate snapshot table. + if (u64::MAX - nb_snapshots as u64 * snapshot_table_length) < snapshot_table_offset + || !is_aligned(self.header.cluster_size(), snapshot_table_offset) + { + res.err_num += 1; + self.header.snapshots_offset = 0; + self.header.nb_snapshots = 0; + bail!("Snapshot table can't exceeds the limit and it's offset must be aligned to cluster size {}", self.header.cluster_size()); + } + + match self.snapshot.load_snapshot_table( + snapshot_table_offset, + nb_snapshots, + fix & FIX_ERRORS != 0, + ) { + Ok((cluster_reduced, data_dropped)) => { + nb_clusters_reduced += cluster_reduced; + extra_data_dropped += data_dropped; + } + Err(e) => { + res.err_num += 1; + self.snapshot.snapshot_table_offset = 0; + self.snapshot.nb_snapshots = 0; + bail!("ERROR failed to read the snapshot table: {}", e); + } + } + res.corruptions += nb_clusters_reduced + extra_data_dropped; + + // Update snapshot in header + // This operations will leaks clusters(extra clusters of snapshot table will be dropped), which will + // be fixed in function of check_refcounts later. + if nb_clusters_reduced > 0 { + let new_nb_snapshots = self.snapshot.nb_snapshots; + let buf = new_nb_snapshots.as_bytes().to_vec(); + let offset = offset_of!(QcowHeader, nb_snapshots); + if let Err(e) = self + .sync_aio + .borrow_mut() + .write_buffer(offset as u64, &buf) + .with_context(|| { + "Failed to update the snapshot count in the image header".to_string() + }) + { + res.err_num += 1; + bail!( + "Failed to update the snapshot count in the image header: {}", + e + ); + } + + self.header.nb_snapshots = new_nb_snapshots; + res.corruptions_fixed += nb_clusters_reduced; + res.corruptions -= nb_clusters_reduced; + } + + Ok(()) } pub fn check_fix_snapshot_table( @@ -205,7 +283,17 @@ impl Qcow2Driver { quite: bool, fix: u64, ) -> Result<()> { - todo!() + if res.corruptions != 0 && fix & FIX_ERRORS != 0 { + if let Err(e) = self.write_snapshots_to_disk() { + res.err_num += 1; + output_msg!(quite, "ERROR failed to update snapshot table {:?}", e); + } + + res.corruptions_fixed += res.corruptions; + res.corruptions = 0; + } + + Ok(()) } /// Rebuild a new refcount table according to metadata, including active l1 table, active l2 table, diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 9f18ce867..e1236b37e 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -286,7 +286,11 @@ impl Qcow2Driver { .with_context(|| "Failed to load refcount table")?; self.snapshot.set_cluster_size(self.header.cluster_size()); self.snapshot - .load_snapshot_table(self.header.snapshots_offset, self.header.nb_snapshots) + .load_snapshot_table( + self.header.snapshots_offset, + self.header.nb_snapshots, + false, + ) .with_context(|| "Failed to load snapshot table")?; Ok(()) } @@ -956,6 +960,63 @@ impl Qcow2Driver { Ok(()) } + /// Write the modified snapshots back to disk + /// 1. Alloc a new space for new snapshot table. + /// 2. Write the snapshots to the new space. + /// 3. Free the old snapshot table. + /// 4. Modify qcow2 header. + pub(crate) fn write_snapshots_to_disk(&mut self) -> Result<()> { + let cluster_size = self.header.cluster_size(); + let snapshot_size = self.snapshot.snapshot_size; + let snapshot_clusters = bytes_to_clusters(snapshot_size, cluster_size).unwrap(); + let new_nb_snapshots = self.snapshot.snapshots.len() as u32; + let mut new_snapshot_offset = 0_u64; + if snapshot_clusters != 0 { + new_snapshot_offset = self.alloc_cluster(snapshot_clusters, true)?; + info!( + "Snapshot table offset: old(0x{:x}) -> new(0x{:x})", + self.header.snapshots_offset, new_snapshot_offset, + ); + + // Write new snapshots to disk. + let mut snap_buf = Vec::new(); + for snap in &self.snapshot.snapshots { + snap_buf.append(&mut snap.gen_snapshot_table_entry()); + } + self.sync_aio + .borrow_mut() + .write_buffer(new_snapshot_offset, &snap_buf)?; + } + + let old_snapshot_offset = self.header.snapshots_offset; + // Free the old snapshot table cluster if snapshot exists. + if self.header.snapshots_offset != 0 { + self.refcount.update_refcount( + old_snapshot_offset, + snapshot_clusters, + -1, + false, + &Qcow2DiscardType::Snapshot, + )?; + } + + // Flush the cache of the refcount block and l1/l2 table. + self.flush()?; + + // Update snapshot offset and num in qcow2 header. + let mut new_header = self.header.clone(); + new_header.snapshots_offset = new_snapshot_offset; + new_header.nb_snapshots = new_nb_snapshots; + self.sync_aio + .borrow_mut() + .write_buffer(0, &new_header.to_vec())?; + + self.snapshot.snapshot_table_offset = new_snapshot_offset; + self.header.snapshots_offset = new_header.snapshots_offset; + self.header.nb_snapshots = new_header.nb_snapshots; + Ok(()) + } + /// Update the refcounts of all clusters searched by l1_table_offset. fn qcow2_update_snapshot_refcount(&mut self, l1_table_offset: u64, added: i32) -> Result<()> { let l1_table_size = self.header.l1_size as usize; diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index 76bf9a804..b9a93e81f 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -114,12 +114,29 @@ impl InternalSnapshot { self.sync_aio.borrow_mut().write_buffer(addr, &buf) } - pub fn load_snapshot_table(&mut self, addr: u64, nb_snapshots: u32) -> Result<()> { - if !is_aligned(self.cluster_size, addr) { - bail!("snapshot table address not aligned {}", addr); + pub(crate) fn load_snapshot_table( + &mut self, + addr: u64, + nb_snapshots: u32, + repair: bool, + ) -> Result<(i32, i32)> { + let mut extra_data_dropped: i32 = 0; + let mut clusters_reduced: i32 = 0; + + if nb_snapshots == 0 { + self.nb_snapshots = 0; + self.snapshots.clear(); + return Ok((clusters_reduced, extra_data_dropped)); + } + + if addr == 0 || !is_aligned(self.cluster_size, addr) { + bail!( + "The offset of snapshot table {} can't be 0 and mut aligned to cluster size", + addr + ); } - for _ in 0..nb_snapshots { + for i in 0..nb_snapshots { let offset = addr + self.snapshot_size; let mut pos = 0; @@ -134,7 +151,15 @@ impl InternalSnapshot { let extra_size = header.extra_date_size as usize; if ![SNAPSHOT_EXTRA_DATA_LEN_16, SNAPSHOT_EXTRA_DATA_LEN_24].contains(&extra_size) { - bail!("Invalid extra data size {}", extra_size); + if !repair { + bail!("Too much extra metadata in snapshot table entry {}", i); + } + let err_msg = format!( + "Discarding too much extra metadata in snapshot table entry {}, {} > {}", + i, extra_size, SNAPSHOT_EXTRA_DATA_LEN_24 + ); + println!("{:?}", err_msg); + extra_data_dropped += 1; } let mut extra_buf = vec![0_u8; extra_size]; self.sync_aio @@ -159,7 +184,7 @@ impl InternalSnapshot { .with_context(|| { format!("read snapshot ID error(addr {:x}).", offset + pos as u64) })?; - let id = from_utf8(&id_buf).unwrap().parse::().unwrap(); + let id = from_utf8(&id_buf)?.parse::()?; pos += header.id_str_size as usize; let mut name_buf = vec![0_u8; header.name_size as usize]; @@ -169,7 +194,7 @@ impl InternalSnapshot { .with_context(|| { format!("read snapshot name error(addr {:x}).", offset + pos as u64) })?; - let name = from_utf8(&name_buf).unwrap(); + let name = from_utf8(&name_buf)?; let snap = QcowSnapshot { l1_table_offset: header.l1_table_offset, @@ -186,9 +211,25 @@ impl InternalSnapshot { }; self.add_snapshot(snap); + if self.snapshot_size > QCOW2_MAX_SNAPSHOTS as u64 * 1024 + || offset - addr > i32::MAX as u64 + { + if !repair { + bail!("Snapshot table is too big"); + } + let err_msg = format!( + "Discarding {} overhanging snapshots(snapshot) table is too big", + nb_snapshots - i + ); + println!("{:?}", err_msg); + clusters_reduced += (nb_snapshots - i) as i32; + self.del_snapshot(i as usize); + self.nb_snapshots = i; + break; + } } - Ok(()) + Ok((clusters_reduced, extra_data_dropped)) } } -- Gitee From 16211df9b47b53724be32e37a3c063ff9ef5e74c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:49:42 +0800 Subject: [PATCH 1364/2187] Stratovirt-img: add function of apply image add function of apply image Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 71 +++++++++++++++++++++++ image/src/img.rs | 103 ++++++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 2 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index e1236b37e..bb8eb4fa9 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -793,6 +793,68 @@ impl Qcow2Driver { self.snapshot.find_snapshot(name) } + fn qcow2_apply_snapshot(&mut self, name: String) -> Result<()> { + // Get target snapshot by name. + let snap_id = self.get_snapshot_by_name(&name); + if snap_id < 0 { + bail!("Failed to load snapshots {}", name); + } + let snap = self.snapshot.snapshots[snap_id as usize].clone(); + + // Validate snapshot table + if snap.l1_size as u64 > MAX_L1_SIZE / ENTRY_SIZE { + bail!("Snapshot L1 table too large"); + } + + if i64::MAX as u64 - snap.l1_size as u64 * ENTRY_SIZE < snap.l1_table_offset + || !is_aligned(self.header.cluster_size(), snap.l1_table_offset) + { + bail!("Snapshot L1 table offset invalid"); + } + + if snap.disk_size != self.virtual_disk_size() { + bail!("The virtual disk size has been changed"); + } + + // Apply the l1 table of snapshot to active l1 table. + let mut snap_l1_table = self + .sync_aio + .borrow_mut() + .read_ctrl_cluster(snap.l1_table_offset, snap.l1_size as u64)?; + snap_l1_table.resize(self.header.l1_size as usize, 0); + + // Increase the refcount of all clusters searched by L1 table. + self.qcow2_update_snapshot_refcount(snap.l1_table_offset, 1)?; + + let active_l1_bytes = self.header.l1_size as u64 * ENTRY_SIZE; + if self.check_overlap( + METADATA_OVERLAP_CHECK_ACTIVEL1, + self.header.l1_table_offset, + active_l1_bytes, + ) != 0 + { + bail!("check overlap failed"); + } + + // Synchronous write back to disk. + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(self.header.l1_table_offset, &snap_l1_table)?; + + self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, -1)?; + + // Update flag of QCOW2_OFFSET_COPIED in current active l1 table, as it has been changed. + self.table.l1_table = snap_l1_table; + self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; + + self.table.save_l1_table()?; + + // Discard unused clusters. + self.refcount.sync_process_discards(OpCode::Discard); + + Ok(()) + } + fn qcow2_delete_snapshot(&mut self, name: String) -> Result { let snapshot_idx = self.get_snapshot_by_name(&name); if snapshot_idx < 0 { @@ -1287,6 +1349,7 @@ pub fn bytes_to_clusters(size: u64, cluster_sz: u64) -> Result { pub trait InternalSnapshotOps: Send + Sync { fn create_snapshot(&mut self, name: String, vm_clock_nsec: u64) -> Result<()>; fn delete_snapshot(&mut self, name: String) -> Result; + fn apply_snapshot(&mut self, name: String) -> Result<()>; fn list_snapshots(&self) -> String; fn get_status(&self) -> Arc>; } @@ -1313,6 +1376,14 @@ impl InternalSnapshotOps for Qcow2Driver { }) } + fn apply_snapshot(&mut self, name: String) -> Result<()> { + self.flush()?; + self.qcow2_apply_snapshot(name).map_err(|e| { + self.drop_dirty_caches(); + e + }) + } + fn list_snapshots(&self) -> String { self.qcow2_list_snapshots() } diff --git a/image/src/img.rs b/image/src/img.rs index f4cb718d8..055a499c0 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -32,6 +32,13 @@ use util::{ file::{lock_file, open_file, unlock_file}, }; +enum SnapshotOperation { + Create, + Delete, + Apply, + List, +} + pub struct ImageFile { file: File, path: String, @@ -251,8 +258,100 @@ pub(crate) fn image_check(args: Vec) -> Result<()> { } } -pub(crate) fn image_snapshot(_args: Vec) -> Result<()> { - todo!() +pub(crate) fn image_snapshot(args: Vec) -> Result<()> { + let mut arg_parser = + ArgsParse::create(vec!["l", "h", "help"], vec!["f", "c", "d", "a"], vec![]); + arg_parser.parse(args)?; + + if arg_parser.opt_present("h") || arg_parser.opt_present("help") { + print_help(); + return Ok(()); + } + + let mut snapshot_name: String = String::from(""); + let mut snapshot_operation: Option = None; + let mut disk_fmt: Option = None; + let err_msg = "Cannot mix '-l', '-a', '-c', '-d'".to_string(); + + if let Some(fmt) = arg_parser.opt_str("f") { + disk_fmt = Some(DiskFormat::from_str(&fmt)?); + } + + if arg_parser.opt_present("l") { + snapshot_operation = Some(SnapshotOperation::List); + } + + if let Some(name) = arg_parser.opt_str("c") { + if snapshot_operation.is_some() { + bail!("{}", err_msg); + } + snapshot_operation = Some(SnapshotOperation::Create); + snapshot_name = name; + } + + if let Some(name) = arg_parser.opt_str("d") { + if snapshot_operation.is_some() { + bail!("{}", err_msg); + } + snapshot_operation = Some(SnapshotOperation::Delete); + snapshot_name = name; + } + + if let Some(name) = arg_parser.opt_str("a") { + if snapshot_operation.is_some() { + bail!("{}", err_msg); + } + snapshot_operation = Some(SnapshotOperation::Apply); + snapshot_name = name; + } + + // Parse image path. + let len = arg_parser.free.len(); + let path = match len { + 0 => bail!("Image snapshot requires path"), + 1 => arg_parser.free[0].clone(), + _ => { + let param = arg_parser.free[1].clone(); + bail!("Unexpected argument: {}", param); + } + }; + + // Detect the image fmt. + let image_file = ImageFile::create(&path, false)?; + let detect_fmt = image_file.detect_img_format()?; + let real_fmt = image_file.check_img_format(disk_fmt, detect_fmt)?; + if real_fmt != DiskFormat::Qcow2 { + bail!( + "Could not create snapshot '{}'(Operation not supported)", + snapshot_name + ); + } + + // Create qcow2 driver. + let mut qcow2_conf = BlockProperty::default(); + qcow2_conf.format = DiskFormat::Qcow2; + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); + let mut qcow2_driver = Qcow2Driver::new(image_file.file.try_clone()?, aio, qcow2_conf.clone())?; + qcow2_driver.load_metadata(qcow2_conf)?; + + match snapshot_operation { + Some(SnapshotOperation::Create) => { + qcow2_driver.create_snapshot(snapshot_name, 0)?; + } + Some(SnapshotOperation::List) => { + let info = qcow2_driver.list_snapshots(); + println!("{}", info); + } + Some(SnapshotOperation::Delete) => { + qcow2_driver.delete_snapshot(snapshot_name)?; + } + Some(SnapshotOperation::Apply) => { + qcow2_driver.apply_snapshot(snapshot_name)?; + } + None => return Ok(()), + }; + + Ok(()) } pub(crate) fn create_qcow2_driver_for_check( -- Gitee From 625e3aba9dace2843c4d28a48749db4985df466a Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 17 Sep 2023 05:53:30 +0800 Subject: [PATCH 1365/2187] Stratovirt-img: add test Add test case for stratovirt-img Signed-off-by: Xiao Ye --- image/src/img.rs | 1036 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1036 insertions(+) diff --git a/image/src/img.rs b/image/src/img.rs index 055a499c0..82b428391 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -417,3 +417,1039 @@ Parameters to snapshot subcommand: "#, ); } + +#[cfg(test)] +mod test { + use std::{ + fs::remove_file, + io::{Seek, SeekFrom}, + }; + + use super::*; + use block_backend::qcow2::{ + refcount::Qcow2DiscardType, HostRange, ENTRY_SIZE, L2_TABLE_OFFSET_MASK, + QCOW2_OFFSET_COPIED, + }; + use util::aio::Iovec; + + const M: u64 = 1024 * 1024; + const G: u64 = 1024 * 1024 * 1024; + + pub struct TestQcow2Image { + pub header: QcowHeader, + pub cluster_bits: u64, + pub path: String, + pub file: File, + } + + impl TestQcow2Image { + pub fn create(cluster_bits: u64, refcount_bits: u64, path: &str, img_size: &str) -> Self { + let cluster_size = 1 << cluster_bits; + // Create image. + let create_str = format!( + "-f qcow2 -o cluster_size={} -o refcount_bits={} {} {}", + cluster_size, refcount_bits, path, img_size, + ); + let create_args: Vec = create_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + assert!(image_create(create_args).is_ok()); + + // Read header. + let file = open_file(path, false, false).unwrap(); + let mut buf = vec![0; QcowHeader::len()]; + assert!(file.read_at(&mut buf, 0).is_ok()); + let header = QcowHeader::from_vec(&buf).unwrap(); + assert_eq!(header.cluster_bits as u64, cluster_bits); + + Self { + header, + cluster_bits, + path: path.to_string(), + file, + } + } + + fn create_driver(&self) -> Qcow2Driver<()> { + let mut conf = BlockProperty::default(); + conf.format = DiskFormat::Qcow2; + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); + let mut qcow2_driver = + Qcow2Driver::new(self.file.try_clone().unwrap(), aio, conf.clone()).unwrap(); + qcow2_driver.load_metadata(conf).unwrap(); + qcow2_driver + } + + fn create_driver_for_check(&self) -> Qcow2Driver<()> { + let file = self.file.try_clone().unwrap(); + let mut conf = BlockProperty::default(); + conf.format = DiskFormat::Qcow2; + let qcow2_driver = create_qcow2_driver_for_check(file, conf).unwrap(); + qcow2_driver + } + + fn read_data(&self, guest_offset: u64, buf: &Vec) -> Result<()> { + let mut qocw2_driver = self.create_driver(); + qocw2_driver.read_vectored( + vec![Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + guest_offset as usize, + (), + ) + } + + fn write_data(&self, guest_offset: u64, buf: &Vec) -> Result<()> { + let mut qocw2_driver = self.create_driver(); + qocw2_driver.write_vectored( + vec![Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + guest_offset as usize, + (), + ) + } + + fn check_image(&self, quite: bool, fix: u64) -> bool { + let mut res = CheckResult::default(); + let mut qcow2_driver = self.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + + res.err_num == 0 + && res.leaks == 0 + && res.leaks_fixed == 0 + && res.corruptions == 0 + && res.corruptions_fixed == 0 + } + + fn file_len(&mut self) -> u64 { + let file_len = self.file.seek(SeekFrom::End(0)).unwrap(); + file_len + } + + fn clear_reftable(&mut self) { + self.header.refcount_table_clusters = 0; + self.header.refcount_table_offset = 0; + + let mut buf = self.header.to_vec(); + assert!(self.file.write_at(&mut buf, 0).is_ok()); + } + } + + impl Drop for TestQcow2Image { + fn drop(&mut self) { + assert!(remove_file(self.path.clone()).is_ok()); + } + } + + /// Test the function of creating image. + /// TestStep: + /// 1. Create image with different args. + /// Expect: + /// 1. If the format of args is invalid, creation failed. + #[test] + fn test_args_parse_of_imgae_create() { + let path = "/tmp/test_args_parse_of_imgae_create.qcow2"; + let test_case = vec![ + ( + "-f qcow2 -o cluster_size=65536 -o refcount_bits=16 img_path +1G", + true, + ), + ( + "-f qcow2 -o cluster_size=65536 refcount_bits=16 img_path +1G", + false, + ), + ("-h", true), + ("-f raw img_path +1G", true), + ("-f raw img_path", false), + ("-f raw -o refcount_bits=16 img_path +1G", false), + ("-f raw -o cluster_size=65536 img_path +1G", false), + ("-f invalid_fmt img_path", false), + ("img_path +1G", true), + ("img_path 1G", true), + ("-f qcow2 -o cluster_size=256 img_path +1G", false), + ("-f qcow2 img_path +1G", true), + ("-f qcow2 img_path +0G", false), + ("-f qcow2 -b backing_file img_path +1G", false), + ("-f qcow2 img_path", false), + ("-f qcow2 +1G", false), + ("-f qcow2 img_path +1G extra_params", false), + ("-f qcow2 -o cluster_size=65536 img_path +1G", true), + ("-f qcow2 -o refcount_bits=16 img_path +1G", true), + ("-f qcow2 -o refcount_bits=128 img_path +1G", false), + ("-f qcow2 -o refcount_bits=63 img_path +1G", false), + ("-f qcow2 -o cluster_size img_path +1G", false), + ("-f qcow2 -o cluster_size=65536 img_path", false), + ("-f qcow2 -o invalid_param img_path", false), + ("-f qcow2 -f raw img_path +1G", true), + ]; + + for case in test_case { + let create_str = case.0.replace("img_path", path); + println!("Create options: {}", create_str); + let create_args: Vec = create_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + + if case.1 { + assert!(image_create(create_args).is_ok()); + } else { + assert!(image_create(create_args).is_err()); + } + } + + assert!(remove_file(path).is_ok()); + } + + /// Test the function of creating image. + /// TestStep: + /// 1. Create image with different cluster bits, image size and refcount bits. + /// Expect: + /// 1. The header of new image meets expectations. + /// 2. No errors were found during the image check. + #[test] + fn test_create_qcow2_img() { + let path = "/tmp/test_create_qcow2_img.qcow2"; + // (cluster bits, image size in str, image size in number) + let test_case = [ + (9, "+1G", G), + (9, "+128G", 128 * G), + (10, "+20M", 20 * M), + (16, "+50M", 50 * M), + (16, "1024M", G), + (16, "+128G", 128 * G), + ]; + // Only refcount bit=16 is supported currently. + let refcount_bits = 16; + + for case in test_case { + let cluster_bits = case.0; + let cluster_size = 1 << cluster_bits; + let image_size = case.2; + let mut test_image = TestQcow2Image::create(cluster_bits, refcount_bits, path, case.1); + + // Check header. + let file_len = test_image.file_len(); + let l1_size = test_image.header.l1_size; + let reftable_clusters = test_image.header.refcount_table_clusters; + let reftable_size = reftable_clusters as u64 * cluster_size / ENTRY_SIZE; + let refblock_size = cluster_size / (refcount_bits / 8); + + assert_ne!(l1_size, 0); + assert_ne!(reftable_clusters, 0); + assert!(l1_size as u64 * cluster_size * cluster_size / ENTRY_SIZE >= image_size); + assert!(reftable_size * refblock_size * cluster_size >= file_len); + assert_eq!(test_image.header.cluster_bits as u64, cluster_bits); + assert_eq!(test_image.header.size, image_size); + + // Check refcount. + assert_eq!(test_image.check_image(false, 0), true); + } + } + + /// Test the function of detect image format. + /// TestStep: + /// 1. Create image with different disk format. + /// 2. Detect the format of disk. + /// 3. Apply image check, and specify the format of raw. + /// Expect: + /// 1. The detected disk format is correct. + /// 2. Image check returned error, as raw format is not supported to check. + #[test] + fn test_detect_image_format() { + let path = "/tmp/test_detect_image_format.qcow2"; + let test_case = [ + ("-f raw path +1G", DiskFormat::Raw), + ("-f qcow2 path +1G", DiskFormat::Qcow2), + ]; + let check_str = format!("-f raw {}", path); + let check_args: Vec = check_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + + for case in test_case { + let create_str = case.0.replace("path", path); + println!("stratovirt-img {}", create_str); + let create_args: Vec = create_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + assert!(image_create(create_args).is_ok()); + + let image_file = ImageFile::create(path, false).unwrap(); + assert_eq!(image_file.detect_img_format().unwrap(), case.1); + + assert!(image_check(check_args.clone()).is_err()); + } + + assert!(remove_file(path).is_ok()); + } + + /// Test the function of check image. + /// TestStep: + /// 1. Check image with different args. + /// Expect: + /// 1. If the args is invalid, check operation failed. + #[test] + fn test_args_parse_of_image_check() { + let path = "/tmp/test_args_parse_of_image_check"; + let create_str = "-f disk_fmt img_path +1G".replace("img_path", path); + let test_case = [ + ("qcow2", "-f qcow2 img_path", true), + ("qcow2", "-f qcow2", false), + ("qcow2", "-r leaks -f qcow2 img_path", true), + ("qcow2", "-r all -f qcow2 img_path", true), + ("qcow2", "-r invalid_param -f qcow2 img_path", false), + ("qcow2", "-r -f qcow2 img_path", false), + ("qcow2", "-f raw img_path", false), + ("qcow2", "img_path", true), + ("qcow2", "-f qcow2 img_path extra_params", false), + ("qcow2", "-f raw -f qcow2 img_path", true), + ("qcow2", "-f qcow2 -f raw img_path", false), + ("raw", "-f qcow2 img_path", false), + ]; + + for case in test_case { + let create_string = create_str.replace("disk_fmt", case.0); + let create_args: Vec = create_string + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + println!("Create args: {}", create_string); + assert!(image_create(create_args.clone()).is_ok()); + + let check_str = case.1.replace("img_path", path); + let check_args: Vec = check_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + println!("Check args: {}", check_str); + + if case.2 { + assert!(image_check(check_args).is_ok()); + } else { + assert!(image_check(check_args).is_err()); + } + + assert!(remove_file(path).is_ok()); + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create image with different image size. + /// 2. Alloc a a new cluster, and create a snapshot, so the real refcount of this cluster is 2. + /// 3. Decrease the refcount of this cluster in refcount block + /// Expect: + /// 1. The corruptions cluster can be found and fixed during image check. + #[test] + fn test_check_refcount_corruptions() { + let path = "/tmp/test_check_refcount_corruptions.qcow2"; + // (cluster bits, image size in str, image size in number) + let test_case = [ + (16, "+50M", 50 * M), + (16, "1024M", G), + (16, "+128G", 128 * G), + ]; + // Only refcount bit=16 is supported currently. + let refcount_bits = 16; + + for case in test_case { + let cluster_bits = case.0; + let cluster_size = 1 << cluster_bits; + let test_image = TestQcow2Image::create(cluster_bits, refcount_bits, path, case.1); + let mut qcow2_driver = test_image.create_driver(); + + // 1. Alloc a cluster. + // 2. Create a snapshot on the image, so the refcount of this cluster is 2. + // 3. Decrease the refcount of this cluster. + let buf = vec![1_u8; cluster_size as usize]; + assert!(qcow2_driver + .write_vectored( + vec![Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + 0, + (), + ) + .is_ok()); + // Get hostoffset of 0 + let mut offset = 0; + match qcow2_driver.host_offset_for_read(0, cluster_size).unwrap() { + HostRange::DataNotInit(_) => assert!(false), + HostRange::DataAddress(addr, bytes) => { + assert!(bytes >= cluster_size); + offset = addr; + } + }; + assert_ne!(offset, 0); + assert!(qcow2_driver + .create_snapshot("test_refcount".to_string(), 0) + .is_ok()); + qcow2_driver + .refcount + .update_refcount(offset, 1, -1, true, &Qcow2DiscardType::Never) + .unwrap(); + drop(qcow2_driver); + + // Check refcount. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver + .check_image(&mut res, false, FIX_ERRORS) + .is_ok()); + assert_eq!(res.corruptions_fixed, 1); + assert_eq!(res.corruptions, 0); + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create image with different image size. + /// 2. Alloc a a new cluster, the real reference of this cluster is 1. + /// 3. Update the reference of this cluster to 10. + /// Expect: + /// 1. The leaks cluster can be found and fixed by image check. + #[test] + fn test_check_refcount_leaks() { + let path = "/tmp/test_check_refcount_leaks.qcow2"; + // (cluster bits, image size in str, image size in number, number clusters) + let test_case = [ + (16, "+50M", 50 * M, 1), + (16, "1024M", G, 1), + (16, "1024M", G, 11), + (16, "+128G", 128 * G, 1), + (16, "+128G", 128 * G, 37), + ]; + // Only refcount bit=16 is supported currently. + let refcount_bits = 16; + + for case in test_case { + let cluster_bits = case.0; + let test_image = TestQcow2Image::create(cluster_bits, refcount_bits, path, case.1); + let mut qcow2_driver = test_image.create_driver(); + + // Alloc cluster, and update the refcount to 10. + let nb_clusters = case.3 as u64; + let offset = qcow2_driver.alloc_cluster(nb_clusters, true).unwrap(); + qcow2_driver + .refcount + .update_refcount(offset, nb_clusters, 9, true, &Qcow2DiscardType::Never) + .unwrap(); + drop(qcow2_driver); + + // Check refcount. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, false, FIX_LEAKS).is_ok()); + assert_eq!(res.leaks, 0); + assert_eq!(res.leaks_fixed, nb_clusters as i32); + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create a new image. + /// 2. Alloc a new cluster, the real reference of this cluster is 1, + /// the l1 entry of this cluster identification with oflag of copied. + /// 3. Clean the oflag of copied in l1 entry of this cluster, and apply image check. + /// Expect: + /// 1. The wrong of oflag in l1 entry can be found and fixed. + #[test] + fn test_check_remove_oflag_copied_in_l1_entry() { + let path = "/tmp/test_check_remove_oflag_copied_in_l1_entry.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + let image_size = 1 << 30; + let image_size_str = "+1G"; + + let test_case = vec![ + // (fix, guest_offset, corruptions, corruptions_fixed) + (0, 0, 1, 0), + (FIX_LEAKS, 0, 0, 1), + (FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, cluster_size * 10, 0, 1), + (FIX_LEAKS, cluster_size * 10, 0, 1), + (FIX_ERRORS, cluster_size * 10, 0, 1), + ( + FIX_LEAKS | FIX_ERRORS, + cluster_size + cluster_size / 2, + 0, + 1, + ), + (FIX_LEAKS, image_size - cluster_size, 0, 1), + (FIX_LEAKS | FIX_ERRORS, image_size - cluster_size, 0, 1), + ]; + + // Test different guest offset + for case in test_case { + let quite = false; + let fix = case.0; + let guest_offset = case.1; + let expect_corruptions = case.2; + let expect_corruptions_fixed = case.3; + let test_image = + TestQcow2Image::create(cluster_bits, refcount_bits, path, image_size_str); + // Write data to guest offset. + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(guest_offset, &buf).is_ok()); + + // Modify the oflag of l1 entry of cluster 0. + let mut qcow2_driver = test_image.create_driver(); + let l1_idx = qcow2_driver.table.get_l1_table_index(guest_offset); + qcow2_driver.table.l1_table[l1_idx as usize] &= !QCOW2_OFFSET_COPIED; + assert!(qcow2_driver.table.save_l1_table().is_ok()); + drop(qcow2_driver); + + // Check and fix error copied flag in l1 table. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + assert_eq!(res.corruptions, expect_corruptions); + assert_eq!(res.corruptions_fixed, expect_corruptions_fixed); + let message = res.collect_check_message(); + println!("{}", message); + drop(qcow2_driver); + + // The oflag error in l1 table has been fixed. + if expect_corruptions_fixed != 0 { + let qcow2_driver = test_image.create_driver(); + assert_ne!( + qcow2_driver.table.l1_table[l1_idx as usize] & QCOW2_OFFSET_COPIED, + 0 + ); + drop(qcow2_driver); + } + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create a new image. + /// 2. Alloc a a new cluster, the real reference of this cluster is 1, + /// the l2 entry of this cluster identification with oflag of copied. + /// 3. Clean the oflag of copied in l2 entry of this cluster, and apply image check. + /// Expect: + /// 2. The wrong of oflag in l2 entry can be found and fixed. + #[test] + fn test_check_remove_oflag_copied_in_l2_entry() { + let path = "/tmp/test_check_remove_oflag_copied_in_l2_entry.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + let image_size = 1 << 30; + let image_size_str = "+1G"; + + let test_case = vec![ + // (fix, guest_offset, corruptions, corruptions_fixed) + (0, 0, 1, 0), + (FIX_LEAKS, 0, 0, 1), + (FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, cluster_size * 10, 0, 1), + (FIX_LEAKS, cluster_size * 10, 0, 1), + (FIX_ERRORS, cluster_size * 10, 0, 1), + ( + FIX_LEAKS | FIX_ERRORS, + cluster_size + cluster_size / 2, + 0, + 1, + ), + (FIX_LEAKS, image_size - cluster_size, 0, 1), + (FIX_LEAKS | FIX_ERRORS, image_size - cluster_size, 0, 1), + ]; + + // Test different guest offset + for case in test_case { + let quite = false; + let fix = case.0; + let guest_offset = case.1; + let expect_corruptions = case.2; + let expect_corruptions_fixed = case.3; + let test_image = + TestQcow2Image::create(cluster_bits, refcount_bits, path, image_size_str); + // Write data to guest offset. + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(guest_offset, &buf).is_ok()); + + // Modify the oflag of l2 entry. + let mut qcow2_driver = test_image.create_driver(); + let l2_idx = qcow2_driver.table.get_l2_table_index(guest_offset) as usize; + let cache_entry = qcow2_driver.get_table_cluster(guest_offset).unwrap(); + let mut l2_entry = cache_entry.borrow_mut().get_entry_map(l2_idx).unwrap(); + l2_entry = l2_entry & !QCOW2_OFFSET_COPIED; + assert!(cache_entry + .borrow_mut() + .set_entry_map(l2_idx, l2_entry) + .is_ok()); + qcow2_driver + .table + .l2_table_cache + .add_dirty_table(cache_entry); + drop(qcow2_driver); + + // Check and fix copied flag in l2 table. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + assert_eq!(res.corruptions, expect_corruptions); + assert_eq!(res.corruptions_fixed, expect_corruptions_fixed); + let message = res.collect_check_message(); + println!("{}", message); + drop(qcow2_driver); + + // The oflag error in l2 table has been fixed. + if expect_corruptions_fixed != 0 { + let mut qcow2_driver = test_image.create_driver(); + let cache_entry = qcow2_driver.get_table_cluster(guest_offset).unwrap(); + let l2_entry = cache_entry.borrow_mut().get_entry_map(l2_idx).unwrap(); + assert_ne!(l2_entry & QCOW2_OFFSET_COPIED, 0); + drop(qcow2_driver); + } + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create a new image. + /// 2. Alloc a a new cluster, and create a new snapshot. So the oflag of copied in l1 entry will be cleared, + /// which means writing to this cluster will result in copy on write. + /// 3. Set the oflag of copied in l1 entry of this cluster, and apply image check. + /// Expect: + /// 1. The wrong of oflag in l1 entry can be found and fixed. + #[test] + fn test_check_add_oflag_copied_in_l1_entry() { + let path = "/tmp/test_check_add_oflag_copied_in_l1_entry.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + let image_size = 1 << 30; + let image_size_str = "+1G"; + let test_case = vec![ + // (fix, guest_offset, corruptions, corruptions_fixed) + (0, 0, 1, 0), + (FIX_LEAKS, 0, 0, 1), + (FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, cluster_size * 10, 0, 1), + (FIX_LEAKS, cluster_size * 10, 0, 1), + (FIX_ERRORS, cluster_size * 10, 0, 1), + ( + FIX_LEAKS | FIX_ERRORS, + cluster_size + cluster_size / 2, + 0, + 1, + ), + (FIX_LEAKS, image_size - cluster_size, 0, 1), + (FIX_LEAKS | FIX_ERRORS, image_size - cluster_size, 0, 1), + ]; + + // Test different guest offset + for case in test_case { + let quite = false; + let fix = case.0; + let guest_offset = case.1; + let expect_corruptions = case.2; + let expect_corruptions_fixed = case.3; + + // Write data to guest offset. + let test_image = + TestQcow2Image::create(cluster_bits, refcount_bits, path, image_size_str); + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(guest_offset, &buf).is_ok()); + + // Create a new snapshot, ensure that the refcount of data cluster is 2. + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot0".to_string(), + path.to_string() + ]) + .is_ok()); + + // Add the oflag copied for l1 entry. + let mut qcow2_driver = test_image.create_driver(); + let l1_idx = qcow2_driver.table.get_l1_table_index(guest_offset); + qcow2_driver.table.l1_table[l1_idx as usize] |= QCOW2_OFFSET_COPIED; + assert!(qcow2_driver.table.save_l1_table().is_ok()); + drop(qcow2_driver); + + // Check and fix error copied flag in l1 table. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + assert_eq!(res.corruptions, expect_corruptions); + assert_eq!(res.corruptions_fixed, expect_corruptions_fixed); + let message = res.collect_check_message(); + println!("{}", message); + drop(qcow2_driver); + + // The oflag error in l1 table has been fixed. + if expect_corruptions_fixed != 0 { + let qcow2_driver = test_image.create_driver(); + assert_eq!( + qcow2_driver.table.l1_table[l1_idx as usize] & QCOW2_OFFSET_COPIED, + 0 + ); + drop(qcow2_driver); + } + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create a new image. + /// 2. Alloc a a new cluster, and create a new snapshot. So the oflag of l2 entry will be cleared, + /// which means writing to this cluster will result in copy on write. + /// 3. Set the oflag of copied in l2 entry of this cluster, and apply image check. + /// Expect: + /// 2. The wrong of oflag in l2 entry can be found and fixed. + #[test] + fn test_check_add_oflag_copied_in_l2_entry() { + let path = "/tmp/test_check_add_oflag_copied_in_l2_entry.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + let image_size = 1 << 30; + let image_size_str = "+1G"; + let test_case = vec![ + // (fix, guest_offset, corruptions, corruptions_fixed) + (0, 0, 1, 0), + (FIX_LEAKS, 0, 0, 1), + (FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, 0, 0, 1), + (FIX_LEAKS | FIX_ERRORS, cluster_size * 10, 0, 1), + (FIX_LEAKS, cluster_size * 10, 0, 1), + (FIX_ERRORS, cluster_size * 10, 0, 1), + ( + FIX_LEAKS | FIX_ERRORS, + cluster_size + cluster_size / 2, + 0, + 1, + ), + (FIX_LEAKS, image_size - cluster_size, 0, 1), + (FIX_LEAKS | FIX_ERRORS, image_size - cluster_size, 0, 1), + ]; + + // Test different guest offset + for case in test_case { + let fix = case.0; + let quite = false; + let guest_offset = case.1; + let expect_corruptions = case.2; + let expect_corruptions_fixed = case.3; + + // Write data to guest offset. + let test_image = + TestQcow2Image::create(cluster_bits, refcount_bits, path, image_size_str); + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(guest_offset, &buf).is_ok()); + + // Create a new snapshot, ensure that the refcount of data cluster is 2. + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot0".to_string(), + path.to_string() + ]) + .is_ok()); + + // Add the oflag copide for l2 entry. + let mut qcow2_driver = test_image.create_driver(); + let l2_idx = qcow2_driver.table.get_l2_table_index(guest_offset) as usize; + let cache_entry = qcow2_driver.get_table_cluster(guest_offset).unwrap(); + let mut l2_entry = cache_entry.borrow_mut().get_entry_map(l2_idx).unwrap(); + l2_entry |= QCOW2_OFFSET_COPIED; + assert!(cache_entry + .borrow_mut() + .set_entry_map(l2_idx, l2_entry) + .is_ok()); + qcow2_driver + .table + .l2_table_cache + .add_dirty_table(cache_entry); + drop(qcow2_driver); + + // Check and fix error copied flag in l1 table. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + assert_eq!(res.corruptions, expect_corruptions); + assert_eq!(res.corruptions_fixed, expect_corruptions_fixed); + let message = res.collect_check_message(); + println!("{}", message); + drop(qcow2_driver); + + // The oflag error in l2 table has been fixed. + if expect_corruptions_fixed != 0 { + let mut qcow2_driver = test_image.create_driver(); + let cache_entry = qcow2_driver.get_table_cluster(guest_offset).unwrap(); + let l2_entry = cache_entry.borrow_mut().get_entry_map(l2_idx).unwrap(); + assert_eq!(l2_entry & QCOW2_OFFSET_COPIED, 0); + drop(qcow2_driver); + } + } + } + + /// Test the function of image check. + /// + /// TestStep: + /// 1. Create a new image with different cluster bits and image size. + /// 2. Set the refcount table offset and refcount table clusters to 0 in header, + /// so it is unable to find data of refcount table by header. + /// 3. Apply image check. + /// Expect: + /// 1. The refcount table and refcount block of this image can be rebuild. + #[test] + fn test_rebuild_refcount() { + let path = "/tmp/test_rebuild_refcount.qcow2"; + // (cluster bits, image size in str, image size in number) + let test_case = [ + (9, "+1G", G), + (10, "+20M", 20 * M), + (16, "+50M", 50 * M), + (16, "1024M", G), + (16, "+128G", 128 * G), + ]; + // Only refcount bit=16 is supported currently. + let refcount_bits = 16; + + for case in test_case { + let cluster_bits = case.0; + let mut test_image = TestQcow2Image::create(cluster_bits, refcount_bits, path, case.1); + test_image.clear_reftable(); + + // Try to rebuild refcount table. + let fix = FIX_ERRORS | FIX_LEAKS; + let quite = false; + + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + assert_eq!(res.corruptions, 0); + assert!(res.corruptions_fixed != 0); + drop(qcow2_driver); + } + } + + /// Test the function of image check. + /// 1. If the l2 offset is not align to cluster size, it will be set to zero during checking. + /// 2. The value of reserved area of l2 entry is expected to 0(Seen L2_STD_RESERVED_MASK). If not , + /// and error message will be recorded, but the repair action will not be tooken, as this error has no other impact. + /// + /// TestStep: + /// 1. Create a new image. + /// 2. Alloc a new cluster, change the offset in l2 entry of this cluster. + /// 3. Apply image check. + /// Expect: + /// 1. The refcount table and refcount block of this image can be rebuild. + #[test] + fn test_check_fix_l2_entry() { + let path = "/tmp/test_check_fix_l2_entry.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + let image_size_str = "+1G"; + let fix = FIX_LEAKS | FIX_ERRORS; + let quite = false; + + let guest_offset = 0; + let test_case: Vec<(u64, bool)> = vec![ + (1 << 1, false), + (1 << 8, false), + (1 << 9, true), + (1 << (cluster_bits - 1), true), + (1 << 57, false), + ]; + + for case in test_case { + // Write data to guest offset. + let offset = case.0; + let need_fixed = case.1; + println!("offset: {}, need_fixed: {}", offset, need_fixed); + let test_image = + TestQcow2Image::create(cluster_bits, refcount_bits, path, image_size_str); + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(guest_offset, &buf).is_ok()); + + // Modify the l2 offset, make it not align to cluster size. + let mut qcow2_driver = test_image.create_driver(); + let l2_idx = qcow2_driver.table.get_l2_table_index(guest_offset) as usize; + let cache_entry = qcow2_driver.get_table_cluster(guest_offset).unwrap(); + let mut l2_entry = cache_entry.borrow_mut().get_entry_map(l2_idx).unwrap(); + let l2_offset = l2_entry & L2_TABLE_OFFSET_MASK; + let l2_entry_flag = l2_entry & !L2_TABLE_OFFSET_MASK; + l2_entry = (l2_offset + offset) | l2_entry_flag; + assert!(cache_entry + .borrow_mut() + .set_entry_map(l2_idx, l2_entry) + .is_ok()); + qcow2_driver + .table + .l2_table_cache + .add_dirty_table(cache_entry); + drop(qcow2_driver); + + // Check and fix error copied flag in l1 table. + let mut res = CheckResult::default(); + let mut qcow2_driver = test_image.create_driver_for_check(); + assert!(qcow2_driver.check_image(&mut res, quite, fix).is_ok()); + if need_fixed { + assert_eq!(res.corruptions, 0); + assert_eq!(res.corruptions_fixed, 1); + } else { + assert_eq!(res.corruptions, 1); + assert_eq!(res.corruptions_fixed, 0); + } + drop(qcow2_driver); + } + } + + /// Test the function of snapshot operation. + /// TestStep: + /// 1. Operate snapshot with different args. + /// Expect: + /// 1. If the args is invalid, operation failed. + #[test] + fn test_args_parse_of_image_snapshot() { + let path = "/tmp/test_args_parse_of_image_snapshot"; + let create_str = "-f disk_fmt img_path +1G".replace("img_path", path); + let test_case = [ + ("qcow2", "-c snapshot0 img_path", true), + ("qcow2", "-f qcow2 -l img_path", true), + ("qcow2", "-d snapshot0 img_path", false), + ("qcow2", "-a snapshot0 img_path", false), + ("qcow2", "-c snapshot0 -l img_path", false), + ("raw", "-f qcow2 -l img_path", false), + ("raw", "-l img_path", false), + ]; + + for case in test_case { + let create_string = create_str.replace("disk_fmt", case.0); + let create_args: Vec = create_string + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + println!("Create args: {}", create_string); + assert!(image_create(create_args).is_ok()); + + let snapshot_str = case.1.replace("img_path", path); + let snapshot_args: Vec = snapshot_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + let ret = image_snapshot(snapshot_args); + if case.2 { + assert!(ret.is_ok()); + } else { + assert!(ret.is_err()); + } + + assert!(remove_file(path).is_ok()); + } + } + + /// Test the function of apply snapshot. + /// + /// TestStep: + /// 1. Create a new image. alloc a new cluster and write 1. + /// 2. Create snapshot named snapshot0, write 2 to the cluster. + /// 3. Create snapshot named snapshot1, write 3 to the cluster. + /// 4. Apply snapshot named snapshot0, and read the data by qcow2 driver. + /// Expect: + /// 1. No errors were found during the image check. + /// 2. The data read after snapshot apply is 2. + #[test] + fn test_check_snapshot_apply_basic() { + let path = "/tmp/test_check_snapshot_apply_basic.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + let test_image = TestQcow2Image::create(cluster_bits, refcount_bits, path, "+1G"); + let quite = false; + let fix = FIX_ERRORS | FIX_LEAKS; + + assert_eq!(test_image.check_image(quite, fix), true); + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + + // Create a snapshot named test_snapshot0 + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot0".to_string(), + path.to_string() + ]) + .is_ok()); + + assert_eq!(test_image.check_image(quite, fix), true); + let buf = vec![2_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + + // Create as snapshot named test_snapshot1. + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot1".to_string(), + path.to_string() + ]) + .is_ok()); + + assert_eq!(test_image.check_image(quite, fix), true); + let buf = vec![3_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + + // Apply snapshot named test_snapshot0. + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot0".to_string(), + path.to_string() + ]) + .is_ok()); + + assert_eq!(test_image.check_image(quite, fix), true); + let buf = vec![0_u8; cluster_size as usize]; + assert!(test_image.read_data(0, &buf).is_ok()); + for elem in buf { + assert_eq!(elem, 1); + } + let buf = vec![4_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + + // Apply snapshot named test_snapshot1 + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot1".to_string(), + path.to_string() + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let buf = vec![0_u8; cluster_size as usize]; + assert!(test_image.read_data(0, &buf).is_ok()); + for elem in buf { + assert_eq!(elem, 2); + } + } +} -- Gitee From 521bc8d2523fb587a45c3c79db0151e305fd1c9a Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 18 Sep 2023 15:27:27 +0800 Subject: [PATCH 1366/2187] docs: add docs for gtk Signed-off-by: Xiao Ye --- docs/config_guidebook.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index d1aa6912d..44ae369ce 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -946,7 +946,30 @@ It determines the order of bootable devices which firmware will use for booting -drive file=path_on_host,id=drive-scsi0-0-0-0[,readonly=true,aio=native,direct=true] -device scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0[,serial=123456,bootindex=1] ``` -### 2.16 VNC +### 2.16 Display + +Multiple display methods are supported by stratovirt, including `GTK` and `VNC`, which allows users to interact with virtual machine. + +#### 2.16.1 GTK + +Graphical interface drawn by gtk toolkits. Visit [GTK](https://www.gtk.org) for more details. + +Two properties can be set for GTK. + +* appname: string of the program name, which will be drawn on the titlebar of the window. +* full-screen: if configured on, the initial window will cover the entire screen. + +Sample Configuration: + +```shell +-display gtk[,appname=,full-screen={on|off}] +``` + +Note: It should be ensured that gtk toolkits have been installed before using gtk. + +Please see the [4. Build with features](docs/build_guide.md) if you want to enable GTK. + +#### 2.16.2 VNC VNC can provide the users with way to login virtual machines remotely. In order to use VNC, the ip and port value must be configured. The IP address can be set to a specified value or `0.0.0.0`, which means that all IP addresses on the host network card are monitored -- Gitee From 9ea80786da03985767c1cb09d2b0bb8ed166380e Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 18 Sep 2023 15:38:08 +0800 Subject: [PATCH 1367/2187] docs: add docs for stratovirt-img Signed-off-by: Xiao Ye --- docs/stratovirt-img.md | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/stratovirt-img.md diff --git a/docs/stratovirt-img.md b/docs/stratovirt-img.md new file mode 100644 index 000000000..8aeca46b8 --- /dev/null +++ b/docs/stratovirt-img.md @@ -0,0 +1,81 @@ +# stratovirt-img + +stratovirt-img is an offline tool for virtual disks. + +Usage: + +```shell +stratovirt-img command [command options] +``` + +Command parameters: + +- img_path: the path for image. +- fmt: disk format. +- img_size: size for image, the unit can be K, M, G or none for bytes. +- options is a comma separated list of format specific options in a name=value format. + +Following commands are supported now: + +## Create + +Create virtual disk with different format. +Command syntax: + +```shell +create [-f fmt] [-o options] img_path img_size +``` + +Sample Configuration: + +```shell +stratovirt-img create -f raw img_path img_size +stratovirt-img create -f qcow2 -o cluster-size=65536 img_path img_size +``` + +Note: 1. The cluster size can be only be set for `qcow2` or default to 65536. 2. Disk format is default to raw. + +## Check + +Check if there are some mistakes on the image and choose to fix. +Command syntax: + +```shell +check [-r {leaks|all}] [-no_print_error] [-f fmt] img_path +``` + +- -r: `leaks` means only the leaked cluster will be fixed, `all` means all repairable mistake will be fixed. +- -no_print_error: do not print detailed error messages. + +Sample Configuration: + +```shell +stratovirt-img check img_path +``` + +Note: The command of check is not supported by raw format. + +## Snapshot + +Operating internal snapshot for disk, it is only supported by qcow2. +Command syntax: + +```shell +snapshot [-l | -a snapshot_name | -c snapshot_name | -d snapshot_name] img_path +``` + +- -a snapshot_name: applies a snapshot (revert disk to saved state). +- -c snapshot_name: creates a snapshot. +- -d snapshot_name: deletes a snapshot. +- -l: lists all snapshots in the given image. + +Sample Configuration: + +```shell +stratovirt-img snapshot -c snapshot_name img_path +stratovirt-img snapshot -a snapshot_name img_path +stratovirt-img snapshot -d snapshot_name img_path +stratovirt-img snapshot -l img_path +``` + +Note: The internal snapshot is not supported by raw. -- Gitee From acd6658029c7de63b66afc10aabe57e5f84976c8 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 01:15:52 +0800 Subject: [PATCH 1368/2187] GTK: fix invalid parameter of full-screen Because is to set full screen by calling full_screen_callback, it is necessary to set the full_screen parameter in scale_mode to false. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 4 ++-- ui/src/gtk/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index d4e7db593..f841b9511 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -246,8 +246,8 @@ impl GtkMenu { } /// Show window. - pub(crate) fn show_window(&self, scale_mode: Rc>) { - if scale_mode.borrow().full_screen { + pub(crate) fn show_window(&self, scale_mode: Rc>, full_screen: bool) { + if full_screen { self.full_screen_item.activate(); } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index a2178d4ad..c961df567 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -226,7 +226,7 @@ impl GtkDisplay { fn create(gtk_menu: GtkMenu, gtk_cfg: &GtkConfig) -> Self { // Window scale mode. let scale_mode = Rc::new(RefCell::new(ScaleMode { - full_screen: gtk_cfg.full_screen, + full_screen: false, free_scale: true, })); // Mapping ASCII to keycode. @@ -556,7 +556,7 @@ fn build_ui(app: &Application, gtk_cfg: &GtkConfig) { .with_context(|| "Gtk display init failed!") .unwrap(); - gtk_menu.show_window(scale_mode); + gtk_menu.show_window(scale_mode, gtk_cfg.full_screen); } fn set_program_attribute(gtk_cfg: &GtkConfig, window: &ApplicationWindow) -> Result<()> { -- Gitee From 5325cf8c3d9a1b54820ba377db52d16e7399e5b7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 00:27:57 +0800 Subject: [PATCH 1369/2187] GTK: Fix error log of "No active display" in normal process If gtk widget is not visible, it will faile while calling the function of gtk_notebook_get_current_page. So it is need to adjuste the order of some functions and placed them after the function of gtk_widget_show_all. Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 3 ++- ui/src/gtk/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index f841b9511..0c451b191 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -247,6 +247,8 @@ impl GtkMenu { /// Show window. pub(crate) fn show_window(&self, scale_mode: Rc>, full_screen: bool) { + self.window.show_all(); + if full_screen { self.full_screen_item.activate(); } @@ -255,7 +257,6 @@ impl GtkMenu { self.zoom_fit.activate(); } - self.window.show_all(); self.menu_bar.hide(); } } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index c961df567..1ce77c232 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -308,7 +308,7 @@ impl GtkDisplay { let first_radio = &self.gtk_menu.radio_group[0]; gs_show_menu.join_group(Some(first_radio)); } else { - gs_show_menu.activate(); + note_book.set_current_page(Some(page_num)); } self.gtk_menu.radio_group.push(gs_show_menu.clone()); -- Gitee From a6ae1b39ce31f7ecb00ce764c0466a39643565ec Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 03:17:05 +0800 Subject: [PATCH 1370/2187] GTK: fix error log of "window size can not be zero" in normal process If widget of note_book contains two draws_area, and hide one. The hidden one will fail whil calling the function of gtk_widget_get_window. It is no need to print an error in this case. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 7 ++++++- ui/src/gtk/mod.rs | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 3aa47153f..86c0866ea 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -320,7 +320,12 @@ fn da_draw_callback(gs: &Rc>, cr: &cairo::Context) -> if surface_width.le(&0.0) || surface_height.le(&0.0) { return Ok(()); } - let (window_width, window_height) = borrowed_gs.get_window_size()?; + + let (window_width, window_height); + match borrowed_gs.get_window_size() { + Some((w, h)) => (window_width, window_height) = (w, h), + None => return Ok(()), + }; if scale_mode.borrow().is_full_screen() || scale_mode.borrow().is_free_scale() { borrowed_gs.scale_x = window_width / surface_width; diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 1ce77c232..74e1a466f 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -424,17 +424,17 @@ impl GtkDisplayScreen { } } - fn get_window_size(&self) -> Result<(f64, f64)> { - let (mut w_width, mut w_height) = (0.0, 0.0); + fn get_window_size(&self) -> Option<(f64, f64)> { if let Some(win) = self.draw_area.window() { - w_width = win.width() as f64; - w_height = win.height() as f64; + let w_width = win.width() as f64; + let w_height = win.height() as f64; + + if w_width.ne(&0.0) && w_height.ne(&0.0) { + return Some((w_width, w_height)); + } }; - if w_width.eq(&0.0) || w_height.eq(&0.0) { - bail!("The window size can not be zero!"); - } - Ok((w_width, w_height)) + None } /// Convert coordinates of the window to relative coordinates of the image. @@ -453,7 +453,10 @@ impl GtkDisplayScreen { (surface_height as f64) * self.scale_y, ); - let (window_width, window_height) = self.get_window_size()?; + let (mut window_width, mut window_height) = (0.0, 0.0); + if let Some((w, h)) = self.get_window_size() { + (window_width, window_height) = (w, h); + }; let scale_factor = match self.draw_area.window() { Some(window) => window.scale_factor() as f64, None => bail!("No display window."), @@ -813,7 +816,11 @@ fn do_update_event(gs: &Rc>, event: DisplayChangeEvent let scale_width = (surface_width as f64) * borrowed_gs.scale_x; let scale_height = (surface_height as f64) * borrowed_gs.scale_y; - let (window_width, window_height) = borrowed_gs.get_window_size()?; + let (window_width, window_height); + match borrowed_gs.get_window_size() { + Some((w, h)) => (window_width, window_height) = (w, h), + None => return Ok(()), + }; let mut mx: f64 = 0.0; let mut my: f64 = 0.0; @@ -940,7 +947,11 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { }); }; - let (window_width, window_height) = borrowed_gs.get_window_size()?; + let (window_width, window_height); + match borrowed_gs.get_window_size() { + Some((w, h)) => (window_width, window_height) = (w, h), + None => return Ok(()), + }; if scale_mode.borrow().is_full_screen() || scale_mode.borrow().is_free_scale() { borrowed_gs.scale_x = window_width / surface_width as f64; borrowed_gs.scale_y = window_height / surface_height as f64; @@ -1013,7 +1024,12 @@ pub(crate) fn update_window_size(gs: &Rc>) -> Result<( /// Ask the gtk display to update the display. pub(crate) fn renew_image(gs: &Rc>) -> Result<()> { let borrowed_gs = gs.borrow(); - let (width, height) = borrowed_gs.get_window_size()?; + let (width, height); + match borrowed_gs.get_window_size() { + Some((w, h)) => (width, height) = (w, h), + None => return Ok(()), + }; + borrowed_gs .draw_area .queue_draw_area(0, 0, width as i32, height as i32); -- Gitee From f9322cd9832d2c5da1f098f8e20157b278d55e23 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:41:57 +0800 Subject: [PATCH 1371/2187] usb/descriptor: Remove useless descriptor definition Signed-off-by: Keqian Zhu --- devices/src/usb/descriptor.rs | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 8005f1102..bdb30f771 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -115,36 +115,6 @@ pub struct UsbEndpointDescriptor { impl ByteCode for UsbEndpointDescriptor {} -/// USB qualifier descriptor for transfer -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct UsbQualifierDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub bcdUSB: u16, - pub bDeviceClass: u8, - pub bDeviceSubClass: u8, - pub bDeviceProtocol: u8, - pub bMaxPacketSize0: u8, - pub bNumConfigurations: u8, - pub bRESERVED: u8, -} - -impl ByteCode for UsbQualifierDescriptor {} - -/// USB string descriptor for transfer -#[allow(non_snake_case)] -#[repr(C, packed)] -#[derive(Copy, Clone, Debug, Default)] -struct UsbStringDescriptor { - pub bLength: u8, - pub bDescriptorType: u8, - pub wData: [u16; 1], -} - -impl ByteCode for UsbStringDescriptor {} - /// USB binary device object store descriptor for transfer. #[allow(non_snake_case)] #[repr(C, packed)] -- Gitee From 9b3830ccfca144b70533b5e378b4b1770699292d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:51:16 +0800 Subject: [PATCH 1372/2187] usb/xhci: Move TRB related definition to separate mod Signed-off-by: Keqian Zhu --- devices/src/usb/xhci/mod.rs | 150 +--------------------- devices/src/usb/xhci/xhci_controller.rs | 2 +- devices/src/usb/xhci/xhci_regs.rs | 2 +- devices/src/usb/xhci/xhci_ring.rs | 4 +- devices/src/usb/xhci/xhci_trb.rs | 160 ++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 152 deletions(-) create mode 100644 devices/src/usb/xhci/xhci_trb.rs diff --git a/devices/src/usb/xhci/mod.rs b/devices/src/usb/xhci/mod.rs index 16b0a05f2..2c01d4263 100644 --- a/devices/src/usb/xhci/mod.rs +++ b/devices/src/usb/xhci/mod.rs @@ -15,152 +15,4 @@ pub mod xhci_pci; pub mod xhci_regs; mod xhci_ring; - -/// Transfer Request Block -pub const TRB_SIZE: u32 = 16; -pub const TRB_TYPE_SHIFT: u32 = 10; -pub const TRB_TYPE_MASK: u32 = 0x3f; -/// Cycle bit -pub const TRB_C: u32 = 1; -/// Event Data -pub const TRB_EV_ED: u32 = 1 << 2; -/// Toggle Cycle -pub const TRB_LK_TC: u32 = 1 << 1; -/// Interrupt-on Short Packet -pub const TRB_TR_ISP: u32 = 1 << 2; -/// Chain bit -pub const TRB_TR_CH: u32 = 1 << 4; -/// Interrupt On Completion -pub const TRB_TR_IOC: u32 = 1 << 5; -/// Immediate Data. -pub const TRB_TR_IDT: u32 = 1 << 6; -/// Direction of the data transfer. -pub const TRB_TR_DIR: u32 = 1 << 16; -/// Frame ID shift. -pub const TRB_TR_FRAMEID_SHIFT: u32 = 20; -/// Frame ID mask. -pub const TRB_TR_FRAMEID_MASK: u32 = 0x7ff; -/// Start Isoch ASAP. -pub const TRB_TR_SIA: u32 = 1 << 31; -/// TRB Transfer Length Mask -pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; -/// Setup Stage TRB Length always 8 -pub const SETUP_TRB_TR_LEN: u32 = 8; - -/// TRB Type Definitions. See the spec 6.4.6 TRB types. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TRBType { - TrbReserved = 0, - TrNormal, - TrSetup, - TrData, - TrStatus, - TrIsoch, - TrLink, - TrEvdata, - TrNoop, - CrEnableSlot, - CrDisableSlot, - CrAddressDevice, - CrConfigureEndpoint, - CrEvaluateContext, - CrResetEndpoint, - CrStopEndpoint, - CrSetTrDequeue, - CrResetDevice, - CrForceEvent, - CrNegotiateBw, - CrSetLatencyTolerance, - CrGetPortBandwidth, - CrForceHeader, - CrNoop, - ErTransfer = 32, - ErCommandComplete, - ErPortStatusChange, - ErBandwidthRequest, - ErDoorbell, - ErHostController, - ErDeviceNotification, - ErMfindexWrap, - Unknown, -} - -impl From for TRBType { - fn from(t: u32) -> TRBType { - match t { - 0 => TRBType::TrbReserved, - 1 => TRBType::TrNormal, - 2 => TRBType::TrSetup, - 3 => TRBType::TrData, - 4 => TRBType::TrStatus, - 5 => TRBType::TrIsoch, - 6 => TRBType::TrLink, - 7 => TRBType::TrEvdata, - 8 => TRBType::TrNoop, - 9 => TRBType::CrEnableSlot, - 10 => TRBType::CrDisableSlot, - 11 => TRBType::CrAddressDevice, - 12 => TRBType::CrConfigureEndpoint, - 13 => TRBType::CrEvaluateContext, - 14 => TRBType::CrResetEndpoint, - 15 => TRBType::CrStopEndpoint, - 16 => TRBType::CrSetTrDequeue, - 17 => TRBType::CrResetDevice, - 18 => TRBType::CrForceEvent, - 19 => TRBType::CrNegotiateBw, - 20 => TRBType::CrSetLatencyTolerance, - 21 => TRBType::CrGetPortBandwidth, - 22 => TRBType::CrForceHeader, - 23 => TRBType::CrNoop, - 32 => TRBType::ErTransfer, - 33 => TRBType::ErCommandComplete, - 34 => TRBType::ErPortStatusChange, - 35 => TRBType::ErBandwidthRequest, - 36 => TRBType::ErDoorbell, - 37 => TRBType::ErHostController, - 38 => TRBType::ErDeviceNotification, - 39 => TRBType::ErMfindexWrap, - _ => TRBType::Unknown, - } - } -} - -/// TRB Completion Code. See the spec 6.4.5 TRB Completion Codes. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TRBCCode { - Invalid = 0, - Success, - DataBufferError, - BabbleDetected, - UsbTransactionError, - TrbError, - StallError, - ResourceError, - BandwidthError, - NoSlotsError, - InvalidStreamTypeError, - SlotNotEnabledError, - EpNotEnabledError, - ShortPacket, - RingUnderrun, - RingOverrun, - VfErFull, - ParameterError, - BandwidthOverrun, - ContextStateError, - NoPingResponseError, - EventRingFullError, - IncompatibleDeviceError, - MissedServiceError, - CommandRingStopped, - CommandAborted, - Stopped, - StoppedLengthInvalid, - MaxExitLatencyTooLargeError = 29, - IsochBufferOverrun = 31, - EventLostError, - UndefinedError, - InvalidStreamIdError, - SecondaryBandwidthError, - SplitTransactionError, -} +mod xhci_trb; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 857fd6855..a706fb619 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -23,7 +23,7 @@ use log::{debug, error, info, warn}; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; -use super::{ +use super::xhci_trb::{ TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_TR_DIR, TRB_TR_FRAMEID_MASK, TRB_TR_FRAMEID_SHIFT, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TR_SIA, TRB_TYPE_SHIFT, diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index f4c30b098..c244dddc7 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -20,7 +20,7 @@ use log::{debug, error}; use super::xhci_controller::dma_write_bytes; use super::xhci_controller::{UsbPort, XhciDevice, XhciEvent}; use super::xhci_ring::XhciTRB; -use super::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; +use super::xhci_trb::{TRBCCode, TRBType, TRB_C, TRB_SIZE}; use crate::usb::{config::*, UsbError}; use address_space::{AddressSpace, GuestAddress, RegionOps}; use util::num_ops::{read_data_u32, read_u32, write_data_u32, write_u64_high, write_u64_low}; diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 396f0d97a..6b8ed77d0 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -19,7 +19,9 @@ use log::debug; use super::super::UsbError; use super::xhci_controller::{dma_read_u32, dma_write_u32, DwordOrder, XhciEpCtx}; -use super::{TRBType, TRB_C, TRB_LK_TC, TRB_SIZE, TRB_TR_CH, TRB_TYPE_MASK, TRB_TYPE_SHIFT}; +use super::xhci_trb::{ + TRBType, TRB_C, TRB_LK_TC, TRB_SIZE, TRB_TR_CH, TRB_TYPE_MASK, TRB_TYPE_SHIFT, +}; use crate::usb::xhci::xhci_controller::dma_read_bytes; use address_space::{AddressSpace, GuestAddress}; diff --git a/devices/src/usb/xhci/xhci_trb.rs b/devices/src/usb/xhci/xhci_trb.rs new file mode 100644 index 000000000..f77336e91 --- /dev/null +++ b/devices/src/usb/xhci/xhci_trb.rs @@ -0,0 +1,160 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// Transfer Request Block +pub const TRB_SIZE: u32 = 16; +pub const TRB_TYPE_SHIFT: u32 = 10; +pub const TRB_TYPE_MASK: u32 = 0x3f; +/// Cycle bit +pub const TRB_C: u32 = 1; +/// Event Data +pub const TRB_EV_ED: u32 = 1 << 2; +/// Toggle Cycle +pub const TRB_LK_TC: u32 = 1 << 1; +/// Interrupt-on Short Packet +pub const TRB_TR_ISP: u32 = 1 << 2; +/// Chain bit +pub const TRB_TR_CH: u32 = 1 << 4; +/// Interrupt On Completion +pub const TRB_TR_IOC: u32 = 1 << 5; +/// Immediate Data. +pub const TRB_TR_IDT: u32 = 1 << 6; +/// Direction of the data transfer. +pub const TRB_TR_DIR: u32 = 1 << 16; +/// Frame ID shift. +pub const TRB_TR_FRAMEID_SHIFT: u32 = 20; +/// Frame ID mask. +pub const TRB_TR_FRAMEID_MASK: u32 = 0x7ff; +/// Start Isoch ASAP. +pub const TRB_TR_SIA: u32 = 1 << 31; +/// TRB Transfer Length Mask +pub const TRB_TR_LEN_MASK: u32 = 0x1ffff; +/// Setup Stage TRB Length always 8 +pub const SETUP_TRB_TR_LEN: u32 = 8; + +/// TRB Type Definitions. See the spec 6.4.6 TRB types. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TRBType { + TrbReserved = 0, + TrNormal, + TrSetup, + TrData, + TrStatus, + TrIsoch, + TrLink, + TrEvdata, + TrNoop, + CrEnableSlot, + CrDisableSlot, + CrAddressDevice, + CrConfigureEndpoint, + CrEvaluateContext, + CrResetEndpoint, + CrStopEndpoint, + CrSetTrDequeue, + CrResetDevice, + CrForceEvent, + CrNegotiateBw, + CrSetLatencyTolerance, + CrGetPortBandwidth, + CrForceHeader, + CrNoop, + ErTransfer = 32, + ErCommandComplete, + ErPortStatusChange, + ErBandwidthRequest, + ErDoorbell, + ErHostController, + ErDeviceNotification, + ErMfindexWrap, + Unknown, +} + +impl From for TRBType { + fn from(t: u32) -> TRBType { + match t { + 0 => TRBType::TrbReserved, + 1 => TRBType::TrNormal, + 2 => TRBType::TrSetup, + 3 => TRBType::TrData, + 4 => TRBType::TrStatus, + 5 => TRBType::TrIsoch, + 6 => TRBType::TrLink, + 7 => TRBType::TrEvdata, + 8 => TRBType::TrNoop, + 9 => TRBType::CrEnableSlot, + 10 => TRBType::CrDisableSlot, + 11 => TRBType::CrAddressDevice, + 12 => TRBType::CrConfigureEndpoint, + 13 => TRBType::CrEvaluateContext, + 14 => TRBType::CrResetEndpoint, + 15 => TRBType::CrStopEndpoint, + 16 => TRBType::CrSetTrDequeue, + 17 => TRBType::CrResetDevice, + 18 => TRBType::CrForceEvent, + 19 => TRBType::CrNegotiateBw, + 20 => TRBType::CrSetLatencyTolerance, + 21 => TRBType::CrGetPortBandwidth, + 22 => TRBType::CrForceHeader, + 23 => TRBType::CrNoop, + 32 => TRBType::ErTransfer, + 33 => TRBType::ErCommandComplete, + 34 => TRBType::ErPortStatusChange, + 35 => TRBType::ErBandwidthRequest, + 36 => TRBType::ErDoorbell, + 37 => TRBType::ErHostController, + 38 => TRBType::ErDeviceNotification, + 39 => TRBType::ErMfindexWrap, + _ => TRBType::Unknown, + } + } +} + +/// TRB Completion Code. See the spec 6.4.5 TRB Completion Codes. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TRBCCode { + Invalid = 0, + Success, + DataBufferError, + BabbleDetected, + UsbTransactionError, + TrbError, + StallError, + ResourceError, + BandwidthError, + NoSlotsError, + InvalidStreamTypeError, + SlotNotEnabledError, + EpNotEnabledError, + ShortPacket, + RingUnderrun, + RingOverrun, + VfErFull, + ParameterError, + BandwidthOverrun, + ContextStateError, + NoPingResponseError, + EventRingFullError, + IncompatibleDeviceError, + MissedServiceError, + CommandRingStopped, + CommandAborted, + Stopped, + StoppedLengthInvalid, + MaxExitLatencyTooLargeError = 29, + IsochBufferOverrun = 31, + EventLostError, + UndefinedError, + InvalidStreamIdError, + SecondaryBandwidthError, + SplitTransactionError, +} -- Gitee From 708c7fac2393fd1d0c1e7c680d5a9a435798ea1b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 07:55:13 +0800 Subject: [PATCH 1373/2187] xhci_pci: Refactor code order Put impl of Device and PciDev together. Signed-off-by: Keqian Zhu --- devices/src/usb/xhci/xhci_pci.rs | 80 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 9b9564882..84d84e0da 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -223,46 +223,6 @@ impl Device for XhciPciDevice { } } -struct DoorbellHandler { - xhci: Arc>, - fd: Arc, -} - -impl DoorbellHandler { - fn new(xhci: Arc>, fd: Arc) -> Self { - DoorbellHandler { xhci, fd } - } -} - -impl EventNotifierHelper for DoorbellHandler { - fn internal_notifiers(io_handler: Arc>) -> Vec { - let cloned_io_handler = io_handler.clone(); - let handler: Rc = Rc::new(move |_event, fd: RawFd| { - read_fd(fd); - let locked_handler = cloned_io_handler.lock().unwrap(); - let mut locked_xhci = locked_handler.xhci.lock().unwrap(); - - if !locked_xhci.running() { - error!("Failed to write doorbell, XHCI is not running"); - return None; - } - if let Err(e) = locked_xhci.handle_command() { - error!("Failed to handle command: {:?}", e); - locked_xhci.host_controller_error(); - } - - None - }); - vec![EventNotifier::new( - NotifierOperation::AddShared, - io_handler.lock().unwrap().fd.as_raw_fd(), - None, - EventSet::IN, - vec![handler], - )] - } -} - impl PciDevOps for XhciPciDevice { fn pci_base(&self) -> &PciDevBase { &self.base @@ -408,3 +368,43 @@ impl PciDevOps for XhciPciDevice { Ok(()) } } + +struct DoorbellHandler { + xhci: Arc>, + fd: Arc, +} + +impl DoorbellHandler { + fn new(xhci: Arc>, fd: Arc) -> Self { + DoorbellHandler { xhci, fd } + } +} + +impl EventNotifierHelper for DoorbellHandler { + fn internal_notifiers(io_handler: Arc>) -> Vec { + let cloned_io_handler = io_handler.clone(); + let handler: Rc = Rc::new(move |_event, fd: RawFd| { + read_fd(fd); + let locked_handler = cloned_io_handler.lock().unwrap(); + let mut locked_xhci = locked_handler.xhci.lock().unwrap(); + + if !locked_xhci.running() { + error!("Failed to write doorbell, XHCI is not running"); + return None; + } + if let Err(e) = locked_xhci.handle_command() { + error!("Failed to handle command: {:?}", e); + locked_xhci.host_controller_error(); + } + + None + }); + vec![EventNotifier::new( + NotifierOperation::AddShared, + io_handler.lock().unwrap().fd.as_raw_fd(), + None, + EventSet::IN, + vec![handler], + )] + } +} -- Gitee From aab74c0d5948bcb5e5738d0aa08e5f32b440c46a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 08:20:00 +0800 Subject: [PATCH 1374/2187] pci/demo_dev: Move demo_dev into demo_device Signed-off-by: Keqian Zhu --- devices/src/pci/demo_dev.rs | 239 ------------------ devices/src/pci/demo_device/base_device.rs | 2 +- devices/src/pci/demo_device/dpy_device.rs | 2 +- devices/src/pci/demo_device/gpu_device.rs | 2 +- .../src/pci/demo_device/kbd_pointer_device.rs | 2 +- devices/src/pci/demo_device/mod.rs | 228 +++++++++++++++++ devices/src/pci/mod.rs | 2 - machine/src/lib.rs | 2 +- 8 files changed, 233 insertions(+), 246 deletions(-) delete mode 100644 devices/src/pci/demo_dev.rs diff --git a/devices/src/pci/demo_dev.rs b/devices/src/pci/demo_dev.rs deleted file mode 100644 index f90877001..000000000 --- a/devices/src/pci/demo_dev.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -/// DemoDev is a demo PCIe device, that can have device properties configurable, eg. -/// bar num, max msix vector num, etc. -/// It can have 0-6 bars, if set, msix always lives in bar 0, data handling in bar 1. -/// 1. its functionality is to read and write data for the guest, meanwhile, do a little -/// mathmetic logic(multiply data[0] with 2) with the write op. -/// 2. After r/w, it sends back a msix interrupt to the guest, which means that it has also -/// msix capability. We assume msix bar is in bar 0. -/// 3. Finally, it supports hotplug/hotunplug. -/// As that it has device memory, it means it has a bar space, we assume the -/// bar size is 4KB in bar 1. -/// As that it has device memory, it means it has a bar space other than the msix one.( -/// therotically they can share the same bar as well). -/// -/// Note: developers can also add yourself mmio r/w ops for this device by changing the -/// callback fn write_data_internal_func(), using trait to expand this function is recommended. -/// -/// The example cmdline for the device is: -/// "-device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0,bar_num=3,bar_size=4096" -use std::{ - sync::Mutex, - sync::{ - atomic::{AtomicU16, Ordering}, - Arc, Weak, - }, -}; - -use anyhow::{bail, Result}; -use log::error; - -use crate::pci::demo_device::{ - dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, -}; -use crate::pci::{ - config::{ - PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, - PCIE_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, - }, - init_msix, le_write_u16, PciBus, PciDevOps, -}; -use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; -use crate::{Device, DeviceBase}; -use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; -use machine_manager::config::DemoDevConfig; - -pub struct DemoDev { - base: PciDevBase, - cmd_cfg: DemoDevConfig, - mem_region: Region, - dev_id: Arc, - device: Arc>, -} - -impl DemoDev { - pub fn new( - cfg: DemoDevConfig, - devfn: u8, - _sys_mem: Arc, - parent_bus: Weak>, - ) -> Self { - // You can choose different device function based on the parameter of device_type. - let device: Arc> = match cfg.device_type.as_str() { - "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem, cfg.id.clone()))), - "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), - "demo-display" => Arc::new(Mutex::new(DemoDisplay::new(_sys_mem))), - _ => Arc::new(Mutex::new(BaseDevice::new())), - }; - DemoDev { - base: PciDevBase { - base: DeviceBase::new(cfg.id.clone(), false), - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), - devfn, - parent_bus, - }, - cmd_cfg: cfg, - mem_region: Region::init_container_region(u32::MAX as u64, "DemoDev"), - dev_id: Arc::new(AtomicU16::new(0)), - device, - } - } - - fn init_pci_config(&mut self) -> Result<()> { - self.init_write_mask(false)?; - self.init_write_clear_mask(false)?; - - let config = &mut self.base.config.config; - le_write_u16(config, DEVICE_ID as usize, DEVICE_ID_DEMO)?; - le_write_u16(config, VENDOR_ID as usize, VENDOR_ID_DEMO)?; - le_write_u16(config, SUB_CLASS_CODE as usize, CLASS_CODE_DEMO)?; - config[HEADER_TYPE as usize] = HEADER_TYPE_ENDPOINT; - - Ok(()) - } - - fn attach_to_parent_bus(self) -> Result<()> { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let mut locked_parent_bus = parent_bus.lock().unwrap(); - if locked_parent_bus.devices.get(&self.base.devfn).is_some() { - bail!("device already existed"); - } - let devfn = self.base.devfn; - let demo_pci_dev = Arc::new(Mutex::new(self)); - locked_parent_bus.devices.insert(devfn, demo_pci_dev); - - Ok(()) - } - - fn register_data_handling_bar(&mut self) -> Result<()> { - let device = self.device.clone(); - let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { - device - .lock() - .unwrap() - .write(data, addr, offset) - .unwrap_or_else(|e| error!("Some error occur in writing: {:?}", e)); - true - }; - - let device = self.device.clone(); - let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { - device - .lock() - .unwrap() - .read(data, addr, offset) - .unwrap_or_else(|e| error!("Some error occur in reading: {:?}", e)); - true - }; - - let region_ops = RegionOps { - read: Arc::new(read_ops), - write: Arc::new(write_ops), - }; - - let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops, "DemoRegion"); - - self.mem_region.add_subregion(region, 0)?; - self.base.config.register_bar( - 0, - self.mem_region.clone(), - RegionType::Mem64Bit, - false, - (self.cmd_cfg.bar_size * self.cmd_cfg.bar_num as u64).next_power_of_two(), - )?; - - Ok(()) - } -} - -// reference to https://pci-ids.ucw.cz/read/PC?restrict=1 -// "DEAD BEEF" seems will not be used for a long time. -const VENDOR_ID_DEMO: u16 = 0xDEAD; -const DEVICE_ID_DEMO: u16 = 0xBEEF; -// reference to https://pci-ids.ucw.cz/read/PD/ -const CLASS_CODE_DEMO: u16 = 0xEE; - -impl Device for DemoDev { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } -} - -impl PciDevOps for DemoDev { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } - - /// Realize PCI/PCIe device. - fn realize(mut self) -> Result<()> { - self.init_pci_config()?; - if self.cmd_cfg.bar_num > 0 { - init_msix( - 0, - 1, - &mut self.base.config, - self.dev_id.clone(), - &self.base.base.id, - None, - None, - )?; - } - - self.register_data_handling_bar()?; - self.device.lock().unwrap().realize()?; - - self.attach_to_parent_bus()?; - Ok(()) - } - - /// Unrealize PCI/PCIe device. - fn unrealize(&mut self) -> Result<()> { - self.device.lock().unwrap().unrealize() - } - - /// write the pci configuration space - fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let parent_bus_locked = parent_bus.lock().unwrap(); - - self.base.config.write( - offset, - data, - self.dev_id.load(Ordering::Acquire), - #[cfg(target_arch = "x86_64")] - None, - Some(&parent_bus_locked.mem_region), - ); - } - - /// Reset device - fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - self.base.config.reset_common_regs() - } -} - -pub trait DeviceTypeOperation: Send { - fn read(&mut self, data: &mut [u8], addr: GuestAddress, offset: u64) -> Result<()>; - fn write(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; - fn realize(&mut self) -> Result<()>; - fn unrealize(&mut self) -> Result<()>; -} diff --git a/devices/src/pci/demo_device/base_device.rs b/devices/src/pci/demo_device/base_device.rs index 7c5b4e5cd..ba675514f 100644 --- a/devices/src/pci/demo_device/base_device.rs +++ b/devices/src/pci/demo_device/base_device.rs @@ -14,7 +14,7 @@ use std::collections::HashMap; use anyhow::Result; -use crate::pci::demo_dev::DeviceTypeOperation; +use super::DeviceTypeOperation; use address_space::GuestAddress; /// BaseDevice is a simplest demo-pci-device. Its function is to diff --git a/devices/src/pci/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs index 678758134..b46a61a6d 100644 --- a/devices/src/pci/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -27,7 +27,7 @@ use byteorder::{ByteOrder, LittleEndian}; use log::error; use once_cell::sync::Lazy; -use crate::pci::demo_dev::DeviceTypeOperation; +use super::DeviceTypeOperation; use address_space::{AddressSpace, GuestAddress}; use ui::{ console::{ diff --git a/devices/src/pci/demo_device/gpu_device.rs b/devices/src/pci/demo_device/gpu_device.rs index 3f03c56f6..9168e0582 100644 --- a/devices/src/pci/demo_device/gpu_device.rs +++ b/devices/src/pci/demo_device/gpu_device.rs @@ -28,7 +28,7 @@ use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::info; -use crate::pci::demo_dev::DeviceTypeOperation; +use super::DeviceTypeOperation; use address_space::{AddressSpace, GuestAddress}; use ui::{ console::{ diff --git a/devices/src/pci/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs index 54f9c8459..3836be672 100644 --- a/devices/src/pci/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -21,7 +21,7 @@ use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; use once_cell::sync::Lazy; -use crate::pci::demo_dev::DeviceTypeOperation; +use super::DeviceTypeOperation; use address_space::{AddressSpace, GuestAddress}; use ui::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 839ee8869..a94eea6f9 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -10,7 +10,235 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +/// DemoDev is a demo PCIe device, that can have device properties configurable, eg. +/// bar num, max msix vector num, etc. +/// It can have 0-6 bars, if set, msix always lives in bar 0, data handling in bar 1. +/// 1. its functionality is to read and write data for the guest, meanwhile, do a little +/// mathmetic logic(multiply data[0] with 2) with the write op. +/// 2. After r/w, it sends back a msix interrupt to the guest, which means that it has also +/// msix capability. We assume msix bar is in bar 0. +/// 3. Finally, it supports hotplug/hotunplug. +/// As that it has device memory, it means it has a bar space, we assume the +/// bar size is 4KB in bar 1. +/// As that it has device memory, it means it has a bar space other than the msix one.( +/// therotically they can share the same bar as well). +/// +/// Note: developers can also add yourself mmio r/w ops for this device by changing the +/// callback fn write_data_internal_func(), using trait to expand this function is recommended. +/// +/// The example cmdline for the device is: +/// "-device pcie-demo-dev,addr=0x5,bus=pcie.0,id=demo0,bar_num=3,bar_size=4096" pub mod base_device; pub mod dpy_device; pub mod gpu_device; pub mod kbd_pointer_device; + +use std::{ + sync::Mutex, + sync::{ + atomic::{AtomicU16, Ordering}, + Arc, Weak, + }, +}; + +use anyhow::{bail, Result}; +use log::error; + +use crate::pci::demo_device::{ + dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, +}; +use crate::pci::{ + config::{ + PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, + PCIE_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, + }, + init_msix, le_write_u16, PciBus, PciDevOps, +}; +use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; +use crate::{Device, DeviceBase}; +use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; +use machine_manager::config::DemoDevConfig; + +pub struct DemoDev { + base: PciDevBase, + cmd_cfg: DemoDevConfig, + mem_region: Region, + dev_id: Arc, + device: Arc>, +} + +impl DemoDev { + pub fn new( + cfg: DemoDevConfig, + devfn: u8, + _sys_mem: Arc, + parent_bus: Weak>, + ) -> Self { + // You can choose different device function based on the parameter of device_type. + let device: Arc> = match cfg.device_type.as_str() { + "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem, cfg.id.clone()))), + "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), + "demo-display" => Arc::new(Mutex::new(DemoDisplay::new(_sys_mem))), + _ => Arc::new(Mutex::new(BaseDevice::new())), + }; + DemoDev { + base: PciDevBase { + base: DeviceBase::new(cfg.id.clone(), false), + config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), + devfn, + parent_bus, + }, + cmd_cfg: cfg, + mem_region: Region::init_container_region(u32::MAX as u64, "DemoDev"), + dev_id: Arc::new(AtomicU16::new(0)), + device, + } + } + + fn init_pci_config(&mut self) -> Result<()> { + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; + + let config = &mut self.base.config.config; + le_write_u16(config, DEVICE_ID as usize, DEVICE_ID_DEMO)?; + le_write_u16(config, VENDOR_ID as usize, VENDOR_ID_DEMO)?; + le_write_u16(config, SUB_CLASS_CODE as usize, CLASS_CODE_DEMO)?; + config[HEADER_TYPE as usize] = HEADER_TYPE_ENDPOINT; + + Ok(()) + } + + fn attach_to_parent_bus(self) -> Result<()> { + let parent_bus = self.base.parent_bus.upgrade().unwrap(); + let mut locked_parent_bus = parent_bus.lock().unwrap(); + if locked_parent_bus.devices.get(&self.base.devfn).is_some() { + bail!("device already existed"); + } + let devfn = self.base.devfn; + let demo_pci_dev = Arc::new(Mutex::new(self)); + locked_parent_bus.devices.insert(devfn, demo_pci_dev); + + Ok(()) + } + + fn register_data_handling_bar(&mut self) -> Result<()> { + let device = self.device.clone(); + let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { + device + .lock() + .unwrap() + .write(data, addr, offset) + .unwrap_or_else(|e| error!("Some error occur in writing: {:?}", e)); + true + }; + + let device = self.device.clone(); + let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { + device + .lock() + .unwrap() + .read(data, addr, offset) + .unwrap_or_else(|e| error!("Some error occur in reading: {:?}", e)); + true + }; + + let region_ops = RegionOps { + read: Arc::new(read_ops), + write: Arc::new(write_ops), + }; + + let region = Region::init_io_region(self.cmd_cfg.bar_size, region_ops, "DemoRegion"); + + self.mem_region.add_subregion(region, 0)?; + self.base.config.register_bar( + 0, + self.mem_region.clone(), + RegionType::Mem64Bit, + false, + (self.cmd_cfg.bar_size * self.cmd_cfg.bar_num as u64).next_power_of_two(), + )?; + + Ok(()) + } +} + +// reference to https://pci-ids.ucw.cz/read/PC?restrict=1 +// "DEAD BEEF" seems will not be used for a long time. +const VENDOR_ID_DEMO: u16 = 0xDEAD; +const DEVICE_ID_DEMO: u16 = 0xBEEF; +// reference to https://pci-ids.ucw.cz/read/PD/ +const CLASS_CODE_DEMO: u16 = 0xEE; + +impl Device for DemoDev { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + +impl PciDevOps for DemoDev { + fn pci_base(&self) -> &PciDevBase { + &self.base + } + + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base + } + + /// Realize PCI/PCIe device. + fn realize(mut self) -> Result<()> { + self.init_pci_config()?; + if self.cmd_cfg.bar_num > 0 { + init_msix( + 0, + 1, + &mut self.base.config, + self.dev_id.clone(), + &self.base.base.id, + None, + None, + )?; + } + + self.register_data_handling_bar()?; + self.device.lock().unwrap().realize()?; + + self.attach_to_parent_bus()?; + Ok(()) + } + + /// Unrealize PCI/PCIe device. + fn unrealize(&mut self) -> Result<()> { + self.device.lock().unwrap().unrealize() + } + + /// write the pci configuration space + fn write_config(&mut self, offset: usize, data: &[u8]) { + let parent_bus = self.base.parent_bus.upgrade().unwrap(); + let parent_bus_locked = parent_bus.lock().unwrap(); + + self.base.config.write( + offset, + data, + self.dev_id.load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + None, + Some(&parent_bus_locked.mem_region), + ); + } + + /// Reset device + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.base.config.reset_common_regs() + } +} + +pub trait DeviceTypeOperation: Send { + fn read(&mut self, data: &mut [u8], addr: GuestAddress, offset: u64) -> Result<()>; + fn write(&mut self, data: &[u8], addr: GuestAddress, offset: u64) -> Result<()>; + fn realize(&mut self) -> Result<()>; + fn unrealize(&mut self) -> Result<()>; +} diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 4899fb9f9..71e0bc00e 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -12,8 +12,6 @@ pub mod config; #[cfg(feature = "demo_device")] -pub mod demo_dev; -#[cfg(feature = "demo_device")] pub mod demo_device; pub mod error; pub mod hotplug; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 2fe7bb89d..424618a6a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -50,7 +50,7 @@ use devices::legacy::FwCfgOps; #[cfg(feature = "scream")] use devices::misc::scream::Scream; #[cfg(feature = "demo_device")] -use devices::pci::demo_dev::DemoDev; +use devices::pci::demo_device::DemoDev; use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] -- Gitee From f2e3b39d0e81ad247cc443d7d0f65233f6453fd4 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 08:33:56 +0800 Subject: [PATCH 1375/2187] camera_backend: Refactor for better naming The backend suffix is more proper. Signed-off-by: Keqian Zhu --- devices/src/camera_backend/demo.rs | 10 +++--- devices/src/camera_backend/mod.rs | 12 +++---- devices/src/camera_backend/v4l2.rs | 4 +-- devices/src/usb/camera.rs | 50 +++++++++++++++--------------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index c1ff73c62..5ac70cc99 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize}; use super::INTERVALS_PER_SEC; use crate::camera_backend::{ - CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, + CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, }; use util::aio::{mem_from_buf, Iovec}; @@ -119,7 +119,7 @@ impl Default for DeviceConfig { } /// Demo camera backend used for test. -pub struct DemoCamera { +pub struct DemoCameraBackend { id: String, /// Device config path. config_path: String, @@ -137,9 +137,9 @@ pub struct DemoCamera { state: Arc>, } -impl DemoCamera { +impl DemoCameraBackend { pub fn new(id: String, config_path: String) -> Result { - Ok(DemoCamera { + Ok(DemoCameraBackend { id, config_path, frame_image: Arc::new(Mutex::new(FrameImage::default())), @@ -374,7 +374,7 @@ fn build_rgb565_list() -> CameraFormatList { } } -impl CameraHostdevOps for DemoCamera { +impl CameraBackend for DemoCameraBackend { fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { *self.cur_format.lock().unwrap() = *cam_fmt; info!("Demo camera backend set format {:?}", cam_fmt); diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index c32820ad9..ae49eac31 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -12,7 +12,7 @@ //! The abstract layer that connects different frontend & backend camera devices. //! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait -//! CameraHostdevOps. +//! CameraBackend. pub mod demo; #[cfg(feature = "usb_camera_v4l2")] @@ -22,7 +22,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use self::demo::DemoCamera; +use self::demo::DemoCameraBackend; #[cfg(feature = "usb_camera_v4l2")] use self::v4l2::V4l2CameraBackend; use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; @@ -105,7 +105,7 @@ pub type CameraNotifyCallback = Arc; /// Callback function which is called when backend is broken. pub type CameraBrokenCallback = Arc; -pub trait CameraHostdevOps: Send + Sync { +pub trait CameraBackend: Send + Sync { /// Set a specific format. fn set_fmt(&mut self, fmt: &CamBasicFmt) -> Result<()>; @@ -143,8 +143,8 @@ pub trait CameraHostdevOps: Send + Sync { fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } -pub fn camera_ops(config: UsbCameraConfig) -> Result>> { - let cam: Arc> = match config.backend { +pub fn create_cam_backend(config: UsbCameraConfig) -> Result>> { + let cam: Arc> = match config.backend { #[cfg(feature = "usb_camera_v4l2")] CamBackendType::V4l2 => Arc::new(Mutex::new(V4l2CameraBackend::new( config.drive.id.clone().unwrap(), @@ -153,7 +153,7 @@ pub fn camera_ops(config: UsbCameraConfig) -> Result Arc::new(Mutex::new(DemoCamera::new( + CamBackendType::Demo => Arc::new(Mutex::new(DemoCameraBackend::new( config.id.clone().unwrap(), config.path.with_context(|| { ConfigError::FieldIsMissing("path".to_string(), "Demo".to_string()) diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 656c51cea..9b7041076 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -28,7 +28,7 @@ use vmm_sys_util::epoll::EventSet; use super::{PIXFMT_MJPG, PIXFMT_RGB565, PIXFMT_YUYV}; use crate::camera_backend::{ - CamBasicFmt, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraHostdevOps, + CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, INTERVALS_PER_SEC, }; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -230,7 +230,7 @@ impl V4l2CameraBackend { } } -impl CameraHostdevOps for V4l2CameraBackend { +impl CameraBackend for V4l2CameraBackend { fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { info!("Camera {} set format {:?}", self.id, cam_fmt); if self.listening { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index e25a1dbe4..0a857af91 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -26,8 +26,8 @@ use vmm_sys_util::eventfd::EventFd; use super::camera_media_type_guid::MEDIA_TYPE_GUID_HASHMAP; use super::xhci::xhci_controller::XhciDevice; use crate::camera_backend::{ - camera_ops, get_bit_rate, get_video_frame_size, CamBasicFmt, CameraBrokenCallback, - CameraFormatList, CameraFrame, CameraHostdevOps, CameraNotifyCallback, FmtType, + create_cam_backend, get_bit_rate, get_video_frame_size, CamBasicFmt, CameraBackend, + CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, }; use crate::usb::config::*; use crate::usb::descriptor::*; @@ -93,14 +93,14 @@ const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; const USB_CAMERA_BUFFER_LEN: usize = 12 * 1024; pub struct UsbCamera { - base: UsbDeviceBase, // general usb device object - vs_control: VideoStreamingControl, // video stream control info - camera_fd: Arc, // camera io fd - camera_dev: Arc>, // backend device + base: UsbDeviceBase, // general usb device object + vs_control: VideoStreamingControl, // video stream control info + camera_fd: Arc, // camera io fd + camera_backend: Arc>, // backend device packet_list: Arc>>>>, // packet to be processed - payload: Arc>, // uvc payload - listening: bool, // if the camera is listening or not - broken: Arc, // if the device broken or not + payload: Arc>, // uvc payload + listening: bool, // if the camera is listening or not + broken: Arc, // if the device broken or not iothread: Option, delete_evts: Vec, } @@ -491,12 +491,12 @@ impl VideoStreamingControl { impl UsbCamera { pub fn new(config: UsbCameraConfig) -> Result { - let camera = camera_ops(config.clone())?; + let camera = create_cam_backend(config.clone())?; Ok(Self { base: UsbDeviceBase::new(config.id.unwrap(), USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), - camera_dev: camera, + camera_backend: camera, packet_list: Arc::new(Mutex::new(LinkedList::new())), payload: Arc::new(Mutex::new(UvcPayload::new())), listening: false, @@ -528,16 +528,16 @@ impl UsbCamera { error!("Failed to notify camera fd {:?}", e); } }); - let mut locked_camera = self.camera_dev.lock().unwrap(); + let mut locked_camera = self.camera_backend.lock().unwrap(); locked_camera.register_notify_cb(notify_cb); locked_camera.register_broken_cb(broken_cb); } fn activate(&mut self, fmt: &CamBasicFmt) -> Result<()> { info!("USB Camera {} activate", self.device_id()); - self.camera_dev.lock().unwrap().reset(); + self.camera_backend.lock().unwrap().reset(); self.payload.lock().unwrap().reset(); - let mut locked_camera = self.camera_dev.lock().unwrap(); + let mut locked_camera = self.camera_backend.lock().unwrap(); locked_camera.set_fmt(fmt)?; locked_camera.video_stream_on()?; drop(locked_camera); @@ -552,7 +552,7 @@ impl UsbCamera { let cam_handler = Arc::new(Mutex::new(CameraIoHandler::new( &self.camera_fd, &self.packet_list, - &self.camera_dev, + &self.camera_backend, &self.payload, &self.broken, ))); @@ -572,10 +572,10 @@ impl UsbCamera { "USB Camera {} broken when deactivate, reset it.", self.device_id() ); - self.camera_dev.lock().unwrap().reset(); + self.camera_backend.lock().unwrap().reset(); self.broken.store(false, Ordering::SeqCst); } else { - self.camera_dev.lock().unwrap().video_stream_off()?; + self.camera_backend.lock().unwrap().video_stream_off()?; } self.unregister_camera_fd()?; self.packet_list.lock().unwrap().clear(); @@ -683,7 +683,7 @@ impl UsbCamera { SET_CUR => match cs { VS_PROBE_CONTROL => { let fmt = self - .camera_dev + .camera_backend .lock() .unwrap() .get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; @@ -691,7 +691,7 @@ impl UsbCamera { } VS_COMMIT_CONTROL => { let fmt = self - .camera_dev + .camera_backend .lock() .unwrap() .get_format_by_index(vs_control.bFormatIndex, vs_control.bFrameIndex)?; @@ -724,7 +724,7 @@ impl UsbCamera { fn reset_vs_control(&mut self) { let default_fmt = self - .camera_dev + .camera_backend .lock() .unwrap() .get_format_by_index(1, 1) @@ -747,7 +747,7 @@ impl UsbDevice for UsbCamera { } fn realize(mut self) -> Result>> { - let fmt_list = self.camera_dev.lock().unwrap().list_format()?; + let fmt_list = self.camera_backend.lock().unwrap().list_format()?; self.base.reset_usb_endpoint(); self.base.speed = USB_SPEED_SUPER; let mut s: Vec = UVC_CAMERA_STRINGS.iter().map(|&s| s.to_string()).collect(); @@ -765,7 +765,7 @@ impl UsbDevice for UsbCamera { fn unrealize(&mut self) -> Result<()> { info!("Camera {} unrealize", self.device_id()); - self.camera_dev.lock().unwrap().reset(); + self.camera_backend.lock().unwrap().reset(); Ok(()) } @@ -777,7 +777,7 @@ impl UsbDevice for UsbCamera { } self.reset_vs_control(); self.payload.lock().unwrap().reset(); - self.camera_dev.lock().unwrap().reset(); + self.camera_backend.lock().unwrap().reset(); self.packet_list.lock().unwrap().clear(); self.broken.store(false, Ordering::SeqCst); } @@ -910,7 +910,7 @@ impl UvcPayload { /// Camere handler for copying frame data to usb packet. struct CameraIoHandler { - camera: Arc>, + camera: Arc>, fd: Arc, packet_list: Arc>>>>, payload: Arc>, @@ -921,7 +921,7 @@ impl CameraIoHandler { fn new( fd: &Arc, list: &Arc>>>>, - camera: &Arc>, + camera: &Arc>, payload: &Arc>, broken: &Arc, ) -> Self { -- Gitee From 6ed52739e9026065a1a5accce762290e53d80555 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 3 Sep 2023 08:41:44 +0800 Subject: [PATCH 1376/2187] interrupt_controller: Control the compile in one place Signed-off-by: Keqian Zhu --- devices/src/interrupt_controller/error.rs | 2 -- devices/src/interrupt_controller/mod.rs | 7 ------- devices/src/lib.rs | 1 + 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/devices/src/interrupt_controller/error.rs b/devices/src/interrupt_controller/error.rs index cb2a0bbfa..b008d64bd 100644 --- a/devices/src/interrupt_controller/error.rs +++ b/devices/src/interrupt_controller/error.rs @@ -14,10 +14,8 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum InterruptError { - #[cfg(target_arch = "aarch64")] #[error("Invalid GIC config: {0}")] InvalidConfig(String), - #[cfg(target_arch = "aarch64")] #[error("Failed to create KVM device: {0:#?}.")] CreateKvmDevice(kvm_ioctls::Error), } diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index d036533e5..7738ae6af 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -25,22 +25,15 @@ //! - `aarch64` #[allow(clippy::upper_case_acronyms)] -#[cfg(target_arch = "aarch64")] mod aarch64; mod error; pub use anyhow::Result; -#[cfg(target_arch = "aarch64")] pub use aarch64::GICConfig as ICGICConfig; -#[cfg(target_arch = "aarch64")] pub use aarch64::GICv2Config as ICGICv2Config; -#[cfg(target_arch = "aarch64")] pub use aarch64::GICv3Config as ICGICv3Config; -#[cfg(target_arch = "aarch64")] pub use aarch64::InterruptController; -#[cfg(target_arch = "aarch64")] pub use aarch64::GIC_IRQ_INTERNAL; -#[cfg(target_arch = "aarch64")] pub use aarch64::GIC_IRQ_MAX; pub use error::InterruptError; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 077c9ebea..c7ea949e8 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -26,6 +26,7 @@ pub mod scsi; pub mod sysbus; pub mod usb; +#[cfg(target_arch = "aarch64")] mod interrupt_controller; #[cfg(target_arch = "aarch64")] -- Gitee From a415d1d326679860640abb8c591dcfd75995d0bc Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 12 Sep 2023 20:21:16 +0800 Subject: [PATCH 1377/2187] qcow2: remove warn log Usually, the size occupied by the newly created qcow2 image is inconsistent with the set size. To reduce the log, remove the warn log with different size in header and file size. Signed-off-by: zhouli57 --- block_backend/src/file.rs | 4 ---- block_backend/src/qcow2/header.rs | 11 ++--------- block_backend/src/qcow2/mod.rs | 11 +---------- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 451e18114..32180cb29 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -209,10 +209,6 @@ impl FileDriver { } Ok(()) } - - pub fn meta_len(&self) -> Result { - Ok(self.file.metadata()?.len()) - } } struct FileIoHandler { diff --git a/block_backend/src/qcow2/header.rs b/block_backend/src/qcow2/header.rs index 11a7c467d..d2b9f8f78 100644 --- a/block_backend/src/qcow2/header.rs +++ b/block_backend/src/qcow2/header.rs @@ -12,7 +12,6 @@ use anyhow::{bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; -use log::warn; use super::ENTRY_SIZE; use util::num_ops::div_round_up; @@ -135,7 +134,7 @@ impl QcowHeader { 0x1 << self.cluster_bits } - pub fn check(&self, file_sz: u64) -> Result<()> { + pub fn check(&self) -> Result<()> { if !(MIN_CLUSTER_BIT..=MAX_CLUSTER_BIT).contains(&self.cluster_bits) { bail!("Invalid cluster bits {}", self.cluster_bits); } @@ -160,12 +159,6 @@ impl QcowHeader { self.refcount_order ); } - if self.size > file_sz { - warn!( - "Header size {} is large than the file size {}", - self.size, file_sz - ); - } self.check_refcount_table()?; self.check_l1_table()?; Ok(()) @@ -423,7 +416,7 @@ mod test { for (buf, err) in list { match QcowHeader::from_vec(&buf) { Ok(header) => { - let e = header.check(0).err().unwrap(); + let e = header.check().err().unwrap(); assert!(e.to_string().contains(&err)); } Err(e) => { diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index bc2534558..6110bdc0e 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -249,7 +249,7 @@ impl Qcow2Driver { qcow2 .load_header() .with_context(|| "Failed to load header")?; - qcow2.check().with_context(|| "Invalid header")?; + qcow2.header.check().with_context(|| "Invalid header")?; qcow2 .table .init_table(&qcow2.header, &conf) @@ -267,15 +267,6 @@ impl Qcow2Driver { Ok(qcow2) } - fn check(&self) -> Result<()> { - let file_sz = self - .driver - .meta_len() - .with_context(|| "Failed to get metadata len")?; - self.header.check(file_sz)?; - Ok(()) - } - pub fn flush(&mut self) -> Result<()> { self.table.flush()?; self.refcount.flush() -- Gitee From 4c95bcc3621a5127e4416b947491a24eedfc20db Mon Sep 17 00:00:00 2001 From: zhouli57 Date: Tue, 12 Sep 2023 20:31:47 +0800 Subject: [PATCH 1378/2187] qcow2: return error in sync aio complete callback If writing qcow2 metadata fails, we should return an error so that the block device can sence that IO error occurred. Signed-off-by: zhouli57 --- block_backend/src/qcow2/mod.rs | 35 +++++++++++++++++++---------- block_backend/src/qcow2/refcount.rs | 13 +++++------ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 6110bdc0e..5065a5a52 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -109,12 +109,21 @@ pub struct SyncAioInfo { } impl SyncAioInfo { - fn new(fd: RawFd, prop: BlockProperty) -> Result { - fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { - Ok(()) + pub fn complete_func(aio: &AioCb<()>, ret: i64) -> Result<()> { + if ret < 0 { + bail!( + "Failed to complete {:?} offset {} nbytes {}", + aio.opcode, + aio.offset, + aio.nbytes + ); } + Ok(()) + } + + fn new(fd: RawFd, prop: BlockProperty) -> Result { Ok(Self { - aio: Aio::new(Arc::new(stub_func), AioEngine::Off)?, + aio: Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off)?, fd, prop, }) @@ -1561,15 +1570,16 @@ mod test { } fn create_qcow2_driver(&self, conf: BlockProperty) -> Qcow2Driver<()> { - fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { - Ok(()) - } let file = std::fs::OpenOptions::new() .read(true) .write(true) .open(&self.path) .unwrap(); - let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); + let aio = Aio::new( + Arc::new(SyncAioInfo::complete_func), + util::aio::AioEngine::Off, + ) + .unwrap(); Qcow2Driver::new(file, aio, conf).unwrap() } @@ -1632,15 +1642,16 @@ mod test { pub fn create_qcow2(path: &str) -> (TestImage, Qcow2Driver<()>) { let mut image = TestImage::new(path, 30, 16); - fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { - Ok(()) - } let file = std::fs::OpenOptions::new() .read(true) .write(true) .open(path) .unwrap(); - let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); + let aio = Aio::new( + Arc::new(SyncAioInfo::complete_func), + util::aio::AioEngine::Off, + ) + .unwrap(); let (req_align, buf_align) = get_file_alignment(&image.file, true); let conf = BlockProperty { id: path.to_string(), diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 5e44a2998..b40f0008e 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -603,17 +603,12 @@ mod test { sync::Arc, }; - use anyhow::Result; use byteorder::{BigEndian, ByteOrder}; use crate::qcow2::*; use crate::qcow2::{header::*, refcount::Qcow2DiscardType}; use machine_manager::config::DiskFormat; - use util::aio::{Aio, AioCb, WriteZeroesState}; - - fn stub_func(_: &AioCb<()>, _: i64) -> Result<()> { - Ok(()) - } + use util::aio::{Aio, WriteZeroesState}; fn image_create(path: &str, img_bits: u32, cluster_bits: u32) -> File { let cluster_sz = 1 << cluster_bits; @@ -675,7 +670,11 @@ mod test { cluster_bits: u32, ) -> (Qcow2Driver<()>, File) { let file = image_create(path, img_bits, cluster_bits); - let aio = Aio::new(Arc::new(stub_func), util::aio::AioEngine::Off).unwrap(); + let aio = Aio::new( + Arc::new(SyncAioInfo::complete_func), + util::aio::AioEngine::Off, + ) + .unwrap(); let conf = BlockProperty { id: path.to_string(), format: DiskFormat::Qcow2, -- Gitee From badc351d38c1e6c2c2a567506849e26a7dcf8eb9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 18 Oct 2023 15:23:05 +0800 Subject: [PATCH 1379/2187] Input: fix the problem of hard keyboard encoding Generate the keysym to qkeycode mapping table by keysym code table and qkeycode code table. Signed-off-by: Xiao Ye --- ui/src/data/keycode.rs | 188 -------------- ui/src/data/mod.rs | 13 - ui/src/gtk/mod.rs | 9 +- ui/src/input.rs | 16 +- ui/src/keycode.rs | 556 +++++++++++++++++++++++++++++++++++++++++ ui/src/lib.rs | 3 +- ui/src/vnc/mod.rs | 8 +- 7 files changed, 570 insertions(+), 223 deletions(-) delete mode 100644 ui/src/data/keycode.rs delete mode 100644 ui/src/data/mod.rs create mode 100644 ui/src/keycode.rs diff --git a/ui/src/data/keycode.rs b/ui/src/data/keycode.rs deleted file mode 100644 index 38778cff7..000000000 --- a/ui/src/data/keycode.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub const KEYSYM2KEYCODE: [(u16, u16); 173] = [ - // (Keysym , Keycode) - (0x0020, 0x0039), - (0x0021, 0x0102), - (0x0022, 0x0128), - (0x0023, 0x0104), - (0x0024, 0x0105), - (0x0025, 0x0106), - (0x0026, 0x0108), - (0x0027, 0x0028), - (0x0028, 0x010A), - (0x0029, 0x010B), - (0x002A, 0x0109), - (0x002B, 0x010D), - (0x002C, 0x0033), - (0x002D, 0x000C), - (0x002E, 0x0034), - (0x002F, 0x0035), - (0x0030, 0x000B), - (0x0031, 0x0002), - (0x0032, 0x0003), - (0x0033, 0x0004), - (0x0034, 0x0005), - (0x0035, 0x0006), - (0x0036, 0x0007), - (0x0037, 0x0008), - (0x0038, 0x0009), - (0x0039, 0x000A), - (0x003A, 0x0127), - (0x003B, 0x0027), - (0x003C, 0x0133), - (0x003D, 0x000D), - (0x003E, 0x0134), - (0x003F, 0x0135), - (0x0040, 0x0103), - (0x0041, 0x011E), - (0x0042, 0x0130), - (0x0043, 0x012E), - (0x0044, 0x0120), - (0x0045, 0x0112), - (0x0046, 0x0121), - (0x0047, 0x0122), - (0x0048, 0x0123), - (0x0049, 0x0117), - (0x004A, 0x0124), - (0x004B, 0x0125), - (0x004C, 0x0126), - (0x004D, 0x0132), - (0x004E, 0x0131), - (0x004F, 0x0118), - (0x0050, 0x0119), - (0x0051, 0x0110), - (0x0052, 0x0113), - (0x0053, 0x011F), - (0x0054, 0x0114), - (0x0055, 0x0116), - (0x0056, 0x012F), - (0x0057, 0x0111), - (0x0058, 0x012D), - (0x0059, 0x0115), - (0x005A, 0x012C), - (0x005B, 0x001A), - (0x005C, 0x002B), - (0x005D, 0x001B), - (0x005E, 0x0107), - (0x005F, 0x010C), - (0x0060, 0x0029), - (0x0061, 0x001E), - (0x0062, 0x0030), - (0x0063, 0x002E), - (0x0064, 0x0020), - (0x0065, 0x0012), - (0x0066, 0x0021), - (0x0067, 0x0022), - (0x0068, 0x0023), - (0x0069, 0x0017), - (0x006A, 0x0024), - (0x006B, 0x0025), - (0x006C, 0x0026), - (0x006D, 0x0032), - (0x006E, 0x0031), - (0x006F, 0x0018), - (0x0070, 0x0019), - (0x0071, 0x0010), - (0x0072, 0x0013), - (0x0073, 0x001F), - (0x0074, 0x0014), - (0x0075, 0x0016), - (0x0076, 0x002F), - (0x0077, 0x0011), - (0x0078, 0x002D), - (0x0079, 0x0015), - (0x007A, 0x002C), - (0x007B, 0x011A), - (0x007C, 0x012B), - (0x007D, 0x011B), - (0x007E, 0x0129), - (0x00A6, 0x0956), - (0xFE03, 0x00B8), - (0xFF08, 0x000E), - (0xFF09, 0x000F), - (0xFF0D, 0x001C), - (0xFF13, 0x00C6), - (0xFF14, 0x0046), - (0xFF15, 0x0054), - (0xFF1B, 0x0001), - (0xFF22, 0x007B), - (0xFF23, 0x0079), - (0xFF50, 0x00C7), - (0xFF51, 0x00CB), - (0xFF52, 0x00C8), - (0xFF53, 0x00CD), - (0xFF54, 0x00D0), - (0xFF55, 0x00C9), - (0xFF56, 0x00D1), - (0xFF57, 0x00CF), - (0xFF61, 0x0054), - (0xFF62, 0x0054), - (0xFF63, 0x00D2), - (0xFF67, 0x00DD), - (0xFF7E, 0x00B8), - (0xFF7F, 0x0045), - (0xFF8D, 0x009C), - (0xFF95, 0x0047), - (0xFF96, 0x004B), - (0xFF97, 0x0048), - (0xFF98, 0x004D), - (0xFF99, 0x0050), - (0xFF9A, 0x0049), - (0xFF9B, 0x0051), - (0xFF9C, 0x004F), - (0xFF9D, 0x004C), - (0xFF9E, 0x0052), - (0xFF9F, 0x0053), - (0xFFAA, 0x0037), - (0xFFAB, 0x004E), - (0xFFAC, 0x0053), - (0xFFAD, 0x004A), - (0xFFAE, 0x0053), - (0xFFAF, 0x00B5), - (0xFFB0, 0x0052), - (0xFFB1, 0x004F), - (0xFFB2, 0x0050), - (0xFFB3, 0x0051), - (0xFFB4, 0x004B), - (0xFFB5, 0x004C), - (0xFFB6, 0x004D), - (0xFFB7, 0x0047), - (0xFFB8, 0x0048), - (0xFFB9, 0x0049), - (0xFFBD, 0x0059), - (0xFFBE, 0x003B), - (0xFFBF, 0x003C), - (0xFFC0, 0x003D), - (0xFFC1, 0x003E), - (0xFFC2, 0x003F), - (0xFFC3, 0x0040), - (0xFFC4, 0x0041), - (0xFFC5, 0x0042), - (0xFFC6, 0x0043), - (0xFFC7, 0x0044), - (0xFFC8, 0x0057), - (0xFFC9, 0x0058), - (0xFFE1, 0x002A), - (0xFFE2, 0x0036), - (0xFFE3, 0x001D), - (0xFFE4, 0x009D), - (0xFFE5, 0x003A), - (0xFFE7, 0x0138), - (0xFFE8, 0x01B8), - (0xFFE9, 0x0038), - (0xFFEA, 0x00B8), - (0xFFEB, 0x00DB), - (0xFFEC, 0x00DC), - (0xFFFF, 0x00D3), -]; diff --git a/ui/src/data/mod.rs b/ui/src/data/mod.rs deleted file mode 100644 index efc157923..000000000 --- a/ui/src/data/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod keycode; diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 74e1a466f..8d1ce7382 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -47,8 +47,8 @@ use crate::{ DisplayMouse, DisplaySurface, VmRunningStage, DEFAULT_SURFACE_HEIGHT, DEFAULT_SURFACE_WIDTH, DISPLAY_UPDATE_INTERVAL_DEFAULT, }, - data::keycode::KEYSYM2KEYCODE, gtk::{draw::set_callback_for_draw_area, menu::GtkMenu}, + keycode::KeyCode, pixman::{ create_pixman_image, get_image_data, get_image_height, get_image_width, ref_pixman_image, unref_pixman_image, @@ -230,12 +230,7 @@ impl GtkDisplay { free_scale: true, })); // Mapping ASCII to keycode. - let keysym2keycode: Rc>> = Rc::new(RefCell::new(HashMap::new())); - let mut borrow_keysym2keycode = keysym2keycode.borrow_mut(); - for &(k, v) in KEYSYM2KEYCODE.iter() { - borrow_keysym2keycode.insert(k, v); - } - drop(borrow_keysym2keycode); + let keysym2keycode = Rc::new(RefCell::new(KeyCode::keysym_to_qkeycode())); Self { gtk_menu, scale_mode, diff --git a/ui/src/input.rs b/ui/src/input.rs index 8ddec12f2..534d94d9d 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -19,7 +19,7 @@ use anyhow::Result; use log::debug; use once_cell::sync::Lazy; -use crate::data::keycode::KEYSYM2KEYCODE; +use crate::keycode::KeyCode; use util::bitmap::Bitmap; // Logical window size for mouse. @@ -94,8 +94,9 @@ pub struct KeyBoardState { impl Default for KeyBoardState { fn default() -> Self { let mut max_keycode: u16 = 0; - for &(_, v) in KEYSYM2KEYCODE.iter() { - max_keycode = std::cmp::max(max_keycode, v); + let keysym2keycode: HashMap = KeyCode::keysym_to_qkeycode(); + for (_, v) in keysym2keycode.iter() { + max_keycode = std::cmp::max(max_keycode, *v); } KeyBoardState::new(max_keycode as usize) } @@ -363,17 +364,18 @@ pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { /// Release all pressed key. pub fn release_all_key() -> Result<()> { let mut locked_input = INPUTS.lock().unwrap(); - for &(_, keycode) in KEYSYM2KEYCODE.iter() { + let keysym2keycode = KeyCode::keysym_to_qkeycode(); + for (_, keycode) in keysym2keycode.iter() { if locked_input .keyboard_state .keystate - .contain(keycode as usize)? + .contain(*keycode as usize)? { locked_input .keyboard_state - .keyboard_state_update(keycode, false)?; + .keyboard_state_update(*keycode, false)?; if let Some(k) = locked_input.get_active_kbd().as_ref() { - k.lock().unwrap().do_key_event(keycode, false)?; + k.lock().unwrap().do_key_event(*keycode, false)?; } } } diff --git a/ui/src/keycode.rs b/ui/src/keycode.rs new file mode 100644 index 000000000..507f5a1d8 --- /dev/null +++ b/ui/src/keycode.rs @@ -0,0 +1,556 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; + +#[derive(Clone, Copy, Debug)] +pub enum KeyCode { + Escape, + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + Key0, + Minus, + Equal, + BackSpace, + Tab, + Keyq, + Keyw, + Keye, + Keyr, + Keyt, + Keyy, + Keyu, + Keyi, + Keyo, + Keyp, + BracketLeft, + BracketRight, + Return, + ControlL, + Keya, + Keys, + Keyd, + Keyf, + Keyg, + Keyh, + Keyj, + Keyk, + Keyl, + Semicolon, + Apostrophe, + Grave, + ShiftL, + BackSlash, + Keyz, + Keyx, + Keyc, + Keyv, + Keyb, + Keyn, + Keym, + Comma, + Period, + Slash, + ShiftR, + KPMultiply, + AltL, + Space, + CapsLock, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + NumLock, + ScrollLock, + KPHome, + KP7, + KPUp, + KP8, + KPPrior, + KP9, + KPSubtract, + KPLeft, + KP4, + KPBegin, + KP5, + KPRight, + KP6, + KPAdd, + KPEnd, + KP1, + KPDown, + KP2, + KPNext, + KP3, + KPInsert, + KP0, + KPDelete, + KPSeparator, + KPDecimal, + Execute, + Print, + SysReq, + F11, + F12, + KPEqual, + HenkanMode, + Muhenkan, + KPEnter, + ControlR, + KPDivide, + AltR, + ISOLevel3Shift, + ModeSwitch, + Pause, + Home, + Up, + Prior, + Left, + Right, + End, + Down, + Next, + Insert, + Delete, + SuperL, + SuperR, + Menu, + Exclam, + At, + Numbersign, + Dollar, + Percent, + Asciicircum, + Ampersand, + Asterisk, + Parenleft, + Parenright, + Underscore, + Plus, + KeyQ, + KeyW, + KeyE, + KeyR, + KeyT, + KeyY, + KeyU, + KeyI, + KeyO, + KeyP, + Braceleft, + Braceright, + KeyA, + KeyS, + KeyD, + KeyF, + KeyG, + KeyH, + KeyJ, + KeyK, + KeyL, + Colon, + Quotedbl, + Asciitilde, + Bar, + KeyZ, + KeyX, + KeyC, + KeyV, + KeyB, + KeyN, + KeyM, + Less, + Greater, + Question, + MetaL, + MetaR, + Brokenbar, +} + +impl KeyCode { + fn to_key_num(self) -> u16 { + match self { + KeyCode::Escape => 0x0001, + KeyCode::Key1 => 0x0002, + KeyCode::Key2 => 0x0003, + KeyCode::Key3 => 0x0004, + KeyCode::Key4 => 0x0005, + KeyCode::Key5 => 0x0006, + KeyCode::Key6 => 0x0007, + KeyCode::Key7 => 0x0008, + KeyCode::Key8 => 0x0009, + KeyCode::Key9 => 0x000A, + KeyCode::Key0 => 0x000B, + KeyCode::Minus => 0x000C, + KeyCode::Equal => 0x000D, + KeyCode::BackSpace => 0x000E, + KeyCode::Tab => 0x000F, + KeyCode::Keyq => 0x0010, + KeyCode::Keyw => 0x0011, + KeyCode::Keye => 0x0012, + KeyCode::Keyr => 0x0013, + KeyCode::Keyt => 0x0014, + KeyCode::Keyy => 0x0015, + KeyCode::Keyu => 0x0016, + KeyCode::Keyi => 0x0017, + KeyCode::Keyo => 0x0018, + KeyCode::Keyp => 0x0019, + KeyCode::BracketLeft => 0x001A, + KeyCode::BracketRight => 0x001B, + KeyCode::Return => 0x001C, + KeyCode::ControlL => 0x001D, + KeyCode::Keya => 0x001E, + KeyCode::Keys => 0x001F, + KeyCode::Keyd => 0x0020, + KeyCode::Keyf => 0x0021, + KeyCode::Keyg => 0x0022, + KeyCode::Keyh => 0x0023, + KeyCode::Keyj => 0x0024, + KeyCode::Keyk => 0x0025, + KeyCode::Keyl => 0x0026, + KeyCode::Semicolon => 0x0027, + KeyCode::Apostrophe => 0x0028, + KeyCode::Grave => 0x0029, + KeyCode::ShiftL => 0x002A, + KeyCode::BackSlash => 0x002B, + KeyCode::Keyz => 0x002C, + KeyCode::Keyx => 0x002D, + KeyCode::Keyc => 0x002E, + KeyCode::Keyv => 0x002F, + KeyCode::Keyb => 0x0030, + KeyCode::Keyn => 0x0031, + KeyCode::Keym => 0x0032, + KeyCode::Comma => 0x0033, + KeyCode::Period => 0x0034, + KeyCode::Slash => 0x0035, + KeyCode::ShiftR => 0x0036, + KeyCode::KPMultiply => 0x0037, + KeyCode::AltL => 0x0038, + KeyCode::Space => 0x0039, + KeyCode::CapsLock => 0x003A, + KeyCode::F1 => 0x003B, + KeyCode::F2 => 0x003C, + KeyCode::F3 => 0x003D, + KeyCode::F4 => 0x003E, + KeyCode::F5 => 0x003F, + KeyCode::F6 => 0x0040, + KeyCode::F7 => 0x0041, + KeyCode::F8 => 0x0042, + KeyCode::F9 => 0x0043, + KeyCode::F10 => 0x0044, + KeyCode::NumLock => 0x0045, + KeyCode::ScrollLock => 0x0046, + KeyCode::KPHome => 0x0047, + KeyCode::KPUp => 0x0048, + KeyCode::KPPrior => 0x0049, + KeyCode::KPSubtract => 0x004A, + KeyCode::KPLeft => 0x004B, + KeyCode::KPBegin => 0x004C, + KeyCode::KPRight => 0x004D, + KeyCode::KPAdd => 0x004E, + KeyCode::KPEnd => 0x004F, + KeyCode::KPDown => 0x0050, + KeyCode::KPNext => 0x0051, + KeyCode::KPInsert => 0x0052, + KeyCode::KPDelete => 0x0053, + KeyCode::KPSeparator => 0x0053, + KeyCode::KP1 => 0x004F, // Numlock KPEnd + KeyCode::KP2 => 0x0050, // Numlock KPDown + KeyCode::KP3 => 0x0051, // Numlock KPNext + KeyCode::KP4 => 0x004B, // Numlock KPLeft + KeyCode::KP5 => 0x004C, // Numlock KPBegin + KeyCode::KP6 => 0x004D, // Numlock KPRight + KeyCode::KP7 => 0x0047, // Numlock KPHome + KeyCode::KP8 => 0x0048, // Numlock KPUp + KeyCode::KP9 => 0x0049, // Numlock KPPrior + KeyCode::KP0 => 0x0052, // Numlock KPInsert + KeyCode::KPDecimal => 0x0053, // Numlock KPDelete + KeyCode::Execute => 0x0054, + KeyCode::Print => 0x0054, + KeyCode::SysReq => 0x0054, + KeyCode::F11 => 0x0057, + KeyCode::F12 => 0x0058, + KeyCode::KPEqual => 0x0059, + KeyCode::HenkanMode => 0x0079, + KeyCode::Muhenkan => 0x007B, + KeyCode::KPEnter => 0x009C, + KeyCode::ControlR => 0x009D, + KeyCode::KPDivide => 0x00B5, + KeyCode::AltR => 0x00B8, + KeyCode::ISOLevel3Shift => 0x00B8, + KeyCode::ModeSwitch => 0x00B8, + KeyCode::Pause => 0x00C6, + KeyCode::Home => 0x00C7, + KeyCode::Up => 0x00C8, + KeyCode::Prior => 0x00C9, + KeyCode::Left => 0x00CB, + KeyCode::Right => 0x00CD, + KeyCode::End => 0x00CF, + KeyCode::Down => 0x00D0, + KeyCode::Next => 0x00D1, + KeyCode::Insert => 0x00D2, + KeyCode::Delete => 0x00D3, + KeyCode::SuperL => 0x00DB, + KeyCode::SuperR => 0x00DC, + KeyCode::Menu => 0x00DD, + KeyCode::Exclam => 0x0102, // Shift 1 + KeyCode::At => 0x0103, // Shift 2 + KeyCode::Numbersign => 0x0104, // Shift 3 + KeyCode::Dollar => 0x0105, // Shift 4 + KeyCode::Percent => 0x0106, // Shift 5 + KeyCode::Asciicircum => 0x0107, // Shift 6 + KeyCode::Ampersand => 0x0108, // Shift 7 + KeyCode::Asterisk => 0x0109, // Shift 8 + KeyCode::Parenleft => 0x010A, // Shift 9 + KeyCode::Parenright => 0x010B, // Shift 0 + KeyCode::Underscore => 0x010C, // Shift Minus + KeyCode::Plus => 0x010D, // Shift Equal + KeyCode::KeyQ => 0x0110, // Shift q + KeyCode::KeyW => 0x0111, // Shift w + KeyCode::KeyE => 0x0112, // Shift e + KeyCode::KeyR => 0x0113, // Shift r + KeyCode::KeyT => 0x0114, // Shift t + KeyCode::KeyY => 0x0115, // Shift y + KeyCode::KeyU => 0x0116, // Shift u + KeyCode::KeyI => 0x0117, // Shift i + KeyCode::KeyO => 0x0118, // Shift o + KeyCode::KeyP => 0x0119, // Shift p + KeyCode::Braceleft => 0x011A, // Shift Bracketleft + KeyCode::Braceright => 0x011B, // Shift Bracketright + KeyCode::KeyA => 0x011E, // Shift a + KeyCode::KeyS => 0x011F, // Shift s + KeyCode::KeyD => 0x0120, // Shift d + KeyCode::KeyF => 0x0121, // Shift f + KeyCode::KeyG => 0x0122, // Shift g + KeyCode::KeyH => 0x0123, // Shift h + KeyCode::KeyJ => 0x0124, // Shift j + KeyCode::KeyK => 0x0125, // Shift k + KeyCode::KeyL => 0x0126, // Shift l + KeyCode::Colon => 0x0127, // Shift Semicolon + KeyCode::Quotedbl => 0x0128, // Shift Apostrophe + KeyCode::Asciitilde => 0x0129, // Shift Grave + KeyCode::Bar => 0x012B, // Shift Backslash + KeyCode::KeyZ => 0x012C, // Shift z + KeyCode::KeyX => 0x012D, // Shift x + KeyCode::KeyC => 0x012E, // Shift c + KeyCode::KeyV => 0x012F, // Shift v + KeyCode::KeyB => 0x0130, // Shift b + KeyCode::KeyN => 0x0131, // Shift n + KeyCode::KeyM => 0x0132, // Shift m + KeyCode::Less => 0x0133, // Shift Comma + KeyCode::Greater => 0x0134, // Shift Period + KeyCode::Question => 0x0135, // Shift Slash + KeyCode::MetaL => 0x0138, // Shift AltL + KeyCode::MetaR => 0x01B8, // Shift AltR + KeyCode::Brokenbar => 0x0956, // Shift Altgr Less + } + } + + pub fn keysym_to_qkeycode() -> HashMap { + let mut keysym2qkeycode: HashMap = HashMap::new(); + // Mapping ASCII to keycode. + for &(keycode, keysym) in KEY_CODE_ASCLL.iter() { + let qkeycode = keycode.to_key_num(); + keysym2qkeycode.insert(keysym, qkeycode); + } + keysym2qkeycode + } +} + +const KEY_CODE_ASCLL: [(KeyCode, u16); 173] = [ + (KeyCode::Space, 0x0020), + (KeyCode::Exclam, 0x0021), + (KeyCode::Quotedbl, 0x0022), + (KeyCode::Numbersign, 0x0023), + (KeyCode::Dollar, 0x0024), + (KeyCode::Percent, 0x0025), + (KeyCode::Ampersand, 0x0026), + (KeyCode::Apostrophe, 0x0027), + (KeyCode::Parenleft, 0x0028), + (KeyCode::Parenright, 0x0029), + (KeyCode::Asterisk, 0x002A), + (KeyCode::Plus, 0x002B), + (KeyCode::Comma, 0x002C), + (KeyCode::Minus, 0x002D), + (KeyCode::Period, 0x002E), + (KeyCode::Slash, 0x002F), + (KeyCode::Key0, 0x0030), + (KeyCode::Key1, 0x0031), + (KeyCode::Key2, 0x0032), + (KeyCode::Key3, 0x0033), + (KeyCode::Key4, 0x0034), + (KeyCode::Key5, 0x0035), + (KeyCode::Key6, 0x0036), + (KeyCode::Key7, 0x0037), + (KeyCode::Key8, 0x0038), + (KeyCode::Key9, 0x0039), + (KeyCode::Colon, 0x003A), + (KeyCode::Semicolon, 0x003B), + (KeyCode::Less, 0x003C), + (KeyCode::Equal, 0x003D), + (KeyCode::Greater, 0x003E), + (KeyCode::Question, 0x003F), + (KeyCode::At, 0x0040), + (KeyCode::KeyA, 0x0041), + (KeyCode::KeyB, 0x0042), + (KeyCode::KeyC, 0x0043), + (KeyCode::KeyD, 0x0044), + (KeyCode::KeyE, 0x0045), + (KeyCode::KeyF, 0x0046), + (KeyCode::KeyG, 0x0047), + (KeyCode::KeyH, 0x0048), + (KeyCode::KeyI, 0x0049), + (KeyCode::KeyJ, 0x004A), + (KeyCode::KeyK, 0x004B), + (KeyCode::KeyL, 0x004C), + (KeyCode::KeyM, 0x004D), + (KeyCode::KeyN, 0x004E), + (KeyCode::KeyO, 0x004F), + (KeyCode::KeyP, 0x0050), + (KeyCode::KeyQ, 0x0051), + (KeyCode::KeyR, 0x0052), + (KeyCode::KeyS, 0x0053), + (KeyCode::KeyT, 0x0054), + (KeyCode::KeyU, 0x0055), + (KeyCode::KeyV, 0x0056), + (KeyCode::KeyW, 0x0057), + (KeyCode::KeyX, 0x0058), + (KeyCode::KeyY, 0x0059), + (KeyCode::KeyZ, 0x005A), + (KeyCode::BracketLeft, 0x005B), + (KeyCode::BackSlash, 0x005C), + (KeyCode::BracketRight, 0x005D), + (KeyCode::Asciicircum, 0x005E), + (KeyCode::Underscore, 0x005F), + (KeyCode::Grave, 0x0060), + (KeyCode::Keya, 0x0061), + (KeyCode::Keyb, 0x0062), + (KeyCode::Keyc, 0x0063), + (KeyCode::Keyd, 0x0064), + (KeyCode::Keye, 0x0065), + (KeyCode::Keyf, 0x0066), + (KeyCode::Keyg, 0x0067), + (KeyCode::Keyh, 0x0068), + (KeyCode::Keyi, 0x0069), + (KeyCode::Keyj, 0x006A), + (KeyCode::Keyk, 0x006B), + (KeyCode::Keyl, 0x006C), + (KeyCode::Keym, 0x006D), + (KeyCode::Keyn, 0x006E), + (KeyCode::Keyo, 0x006F), + (KeyCode::Keyp, 0x0070), + (KeyCode::Keyq, 0x0071), + (KeyCode::Keyr, 0x0072), + (KeyCode::Keys, 0x0073), + (KeyCode::Keyt, 0x0074), + (KeyCode::Keyu, 0x0075), + (KeyCode::Keyv, 0x0076), + (KeyCode::Keyw, 0x0077), + (KeyCode::Keyx, 0x0078), + (KeyCode::Keyy, 0x0079), + (KeyCode::Keyz, 0x007A), + (KeyCode::Braceleft, 0x007B), + (KeyCode::Bar, 0x007C), + (KeyCode::Braceright, 0x007D), + (KeyCode::Asciitilde, 0x007E), + (KeyCode::Brokenbar, 0x00A6), + (KeyCode::ISOLevel3Shift, 0xFE03), + (KeyCode::BackSpace, 0xFF08), + (KeyCode::Tab, 0xFF09), + (KeyCode::Return, 0xFF0D), + (KeyCode::Pause, 0xFF13), + (KeyCode::ScrollLock, 0xFF14), + (KeyCode::SysReq, 0xFF15), + (KeyCode::Escape, 0xFF1B), + (KeyCode::Muhenkan, 0xFF22), + (KeyCode::HenkanMode, 0xFF23), + (KeyCode::Home, 0xFF50), + (KeyCode::Left, 0xFF51), + (KeyCode::Up, 0xFF52), + (KeyCode::Right, 0xFF53), + (KeyCode::Down, 0xFF54), + (KeyCode::Prior, 0xFF55), + (KeyCode::Next, 0xFF56), + (KeyCode::End, 0xFF57), + (KeyCode::Print, 0xFF61), + (KeyCode::Execute, 0xFF62), + (KeyCode::Insert, 0xFF63), + (KeyCode::Menu, 0xFF67), + (KeyCode::ModeSwitch, 0xFF7E), + (KeyCode::NumLock, 0xFF7F), + (KeyCode::KPEnter, 0xFF8D), + (KeyCode::KPHome, 0xFF95), + (KeyCode::KPLeft, 0xFF96), + (KeyCode::KPUp, 0xFF97), + (KeyCode::KPRight, 0xFF98), + (KeyCode::KPDown, 0xFF99), + (KeyCode::KPPrior, 0xFF9A), + (KeyCode::KPNext, 0xFF9B), + (KeyCode::KPEnd, 0xFF9C), + (KeyCode::KPBegin, 0xFF9D), + (KeyCode::KPInsert, 0xFF9E), + (KeyCode::KPDelete, 0xFF9F), + (KeyCode::KPMultiply, 0xFFAA), + (KeyCode::KPAdd, 0xFFAB), + (KeyCode::KPSeparator, 0xFFAC), + (KeyCode::KPSubtract, 0xFFAD), + (KeyCode::KPDecimal, 0xFFAE), + (KeyCode::KPDivide, 0xFFAF), + (KeyCode::KP0, 0xFFB0), + (KeyCode::KP1, 0xFFB1), + (KeyCode::KP2, 0xFFB2), + (KeyCode::KP3, 0xFFB3), + (KeyCode::KP4, 0xFFB4), + (KeyCode::KP5, 0xFFB5), + (KeyCode::KP6, 0xFFB6), + (KeyCode::KP7, 0xFFB7), + (KeyCode::KP8, 0xFFB8), + (KeyCode::KP9, 0xFFB9), + (KeyCode::KPEqual, 0xFFBD), + (KeyCode::F1, 0xFFBE), + (KeyCode::F2, 0xFFBF), + (KeyCode::F3, 0xFFC0), + (KeyCode::F4, 0xFFC1), + (KeyCode::F5, 0xFFC2), + (KeyCode::F6, 0xFFC3), + (KeyCode::F7, 0xFFC4), + (KeyCode::F8, 0xFFC5), + (KeyCode::F9, 0xFFC6), + (KeyCode::F10, 0xFFC7), + (KeyCode::F11, 0xFFC8), + (KeyCode::F12, 0xFFC9), + (KeyCode::ShiftL, 0xFFE1), + (KeyCode::ShiftR, 0xFFE2), + (KeyCode::ControlL, 0xFFE3), + (KeyCode::ControlR, 0xFFE4), + (KeyCode::CapsLock, 0xFFE5), + (KeyCode::MetaL, 0xFFE7), + (KeyCode::MetaR, 0xFFE8), + (KeyCode::AltL, 0xFFE9), + (KeyCode::AltR, 0xFFEA), + (KeyCode::SuperL, 0xFFEB), + (KeyCode::SuperR, 0xFFEC), + (KeyCode::Delete, 0xFFFF), +]; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 843f6e43e..0ae9f7d03 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -16,10 +16,9 @@ pub mod error; #[cfg(feature = "gtk")] pub mod gtk; pub mod input; +mod keycode; #[cfg(feature = "pixman")] pub mod pixman; pub mod utils; #[cfg(feature = "vnc")] pub mod vnc; - -mod data; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 9b03c7c8b..fb0f56fb4 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -18,7 +18,6 @@ pub mod server_io; use std::{ cmp, - collections::HashMap, net::TcpListener, ptr, sync::{Arc, Mutex}, @@ -35,8 +34,8 @@ use crate::{ DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, }, - data::keycode::KEYSYM2KEYCODE, error::VncError, + keycode::KeyCode, pixman::{ bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, ref_pixman_image, unref_pixman_image, @@ -280,11 +279,8 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { .set_nonblocking(true) .expect("Set noblocking for vnc socket failed"); - let mut keysym2keycode: HashMap = HashMap::new(); // Mapping ASCII to keycode. - for &(k, v) in KEYSYM2KEYCODE.iter() { - keysym2keycode.insert(k, v); - } + let keysym2keycode = KeyCode::keysym_to_qkeycode(); let vnc_opts = Arc::new(VncInterface::default()); let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, vnc_opts))); -- Gitee From 61f038a73e9becf7f1499de4550b5717148a6d33 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 19 Sep 2023 22:09:38 +0800 Subject: [PATCH 1380/2187] Input: Using Hashset instead of Bitmap to record keyboard status Due to the sparsity of keycode encoding, It is more memory efficient to record keycode by hashset instead of bitmap. Signed-off-by: Xiao Ye --- ui/src/input.rs | 156 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 41 deletions(-) diff --git a/ui/src/input.rs b/ui/src/input.rs index 534d94d9d..23f430b37 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, sync::{Arc, Mutex}, }; @@ -19,7 +19,6 @@ use anyhow::Result; use log::debug; use once_cell::sync::Lazy; -use crate::keycode::KeyCode; use util::bitmap::Bitmap; // Logical window size for mouse. @@ -86,32 +85,23 @@ pub enum KeyboardModifier { /// and some status information. pub struct KeyBoardState { /// Keyboard state. - pub keystate: Bitmap, + pub keystate: HashSet, /// Key Modifier states. pub keymods: Bitmap, } impl Default for KeyBoardState { fn default() -> Self { - let mut max_keycode: u16 = 0; - let keysym2keycode: HashMap = KeyCode::keysym_to_qkeycode(); - for (_, v) in keysym2keycode.iter() { - max_keycode = std::cmp::max(max_keycode, *v); - } - KeyBoardState::new(max_keycode as usize) - } -} - -impl KeyBoardState { - pub fn new(key_num: usize) -> Self { Self { - keystate: Bitmap::new(key_num / (BIT_PER_BYTE as usize) + 1), + keystate: HashSet::new(), keymods: Bitmap::new( KeyboardModifier::KeyModMax as usize / (BIT_PER_BYTE as usize) + 1, ), } } +} +impl KeyBoardState { /// Get the corresponding keyboard modifier. fn keyboard_modifier_get(&self, key_mod: KeyboardModifier) -> bool { match self.keymods.contain(key_mod as usize) { @@ -128,15 +118,15 @@ impl KeyBoardState { /// Record the press and up state in the keyboard. fn keyboard_state_update(&mut self, keycode: u16, down: bool) -> Result<()> { // Key is not pressed and the incoming key action is up. - if !down && !self.keystate.contain(keycode as usize)? { + if !down && !self.keystate.contains(&keycode) { return Ok(()); } // Update Keyboard key modifier state. if down { - self.keystate.set(keycode as usize)?; + self.keystate.insert(keycode); } else { - self.keystate.clear(keycode as usize)?; + self.keystate.remove(&keycode); } // Update Keyboard modifier state. @@ -196,10 +186,7 @@ impl KeyBoardState { keycode_2: u16, mod_state: KeyboardModifier, ) -> Result<()> { - let mut res = self.keystate.contain(keycode_1 as usize)?; - res |= self.keystate.contain(keycode_2 as usize)?; - - if res { + if self.keystate.contains(&keycode_1) | self.keystate.contains(&keycode_2) { self.keymods.set(mod_state as usize)?; } else { self.keymods.clear(mod_state as usize)?; @@ -364,19 +351,16 @@ pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { /// Release all pressed key. pub fn release_all_key() -> Result<()> { let mut locked_input = INPUTS.lock().unwrap(); - let keysym2keycode = KeyCode::keysym_to_qkeycode(); - for (_, keycode) in keysym2keycode.iter() { - if locked_input + let mut keycode_lists: Vec = Vec::new(); + for keycode in locked_input.keyboard_state.keystate.iter() { + keycode_lists.push(*keycode); + } + for keycode in keycode_lists.iter() { + locked_input .keyboard_state - .keystate - .contain(*keycode as usize)? - { - locked_input - .keyboard_state - .keyboard_state_update(*keycode, false)?; - if let Some(k) = locked_input.get_active_kbd().as_ref() { - k.lock().unwrap().do_key_event(*keycode, false)?; - } + .keyboard_state_update(*keycode, false)?; + if let Some(k) = locked_input.get_active_kbd().as_ref() { + k.lock().unwrap().do_key_event(*keycode, false)?; } } Ok(()) @@ -419,7 +403,50 @@ pub trait PointerOpts: Send { #[cfg(test)] mod tests { + use crate::keycode::KeyCode; + static TEST_INPUT: Lazy>> = + Lazy::new(|| Arc::new(Mutex::new(TestInput::default()))); + use super::*; + + pub struct TestInput { + kbd: Arc>, + tablet: Arc>, + } + + impl Default for TestInput { + fn default() -> Self { + Self { + kbd: Arc::new(Mutex::new(TestKbd { + keycode: 0, + down: false, + })), + tablet: Arc::new(Mutex::new(TestTablet { + button: 0, + x: 0, + y: 0, + })), + } + } + } + + impl TestInput { + fn register_input(&self) { + register_keyboard("TestKeyboard", self.kbd.clone()); + register_pointer("TestPointer", self.tablet.clone()); + } + + fn unregister_input(&self) { + unregister_keyboard("TestKeyboard"); + unregister_pointer("TestPointer"); + self.kbd.lock().unwrap().keycode = 0; + self.kbd.lock().unwrap().down = false; + self.tablet.lock().unwrap().x = 0; + self.tablet.lock().unwrap().y = 0; + self.tablet.lock().unwrap().button = 0; + } + } + #[derive(Default)] pub struct TestKbd { keycode: u16, @@ -451,18 +478,16 @@ mod tests { #[test] fn test_input_basic() { - // Test keyboard event. - let test_kdb = Arc::new(Mutex::new(TestKbd { - keycode: 0, - down: false, - })); - register_keyboard("TestKeyboard", test_kdb.clone()); + let test_input = TEST_INPUT.lock().unwrap(); + test_input.register_input(); + let test_kdb = test_input.kbd.clone(); + let test_mouse = test_input.tablet.clone(); + assert!(key_event(12, true).is_ok()); assert_eq!(test_kdb.lock().unwrap().keycode, 12); assert_eq!(test_kdb.lock().unwrap().down, true); // Test point event. - let test_mouse = Arc::new(Mutex::new(TestTablet::default())); assert_eq!(test_mouse.lock().unwrap().button, 0); assert_eq!(test_mouse.lock().unwrap().x, 0); assert_eq!(test_mouse.lock().unwrap().y, 0); @@ -471,5 +496,54 @@ mod tests { assert_eq!(test_mouse.lock().unwrap().button, 1); assert_eq!(test_mouse.lock().unwrap().x, 54); assert_eq!(test_mouse.lock().unwrap().y, 12); + + test_input.unregister_input(); + } + + #[test] + fn test_release_all_key() { + fn do_key_event(press: bool, keysym: i32, keycode: u16) -> Result<()> { + update_key_state(press, keysym, keycode)?; + key_event(keycode, press) + } + + // Test keyboard event. + let test_input = TEST_INPUT.lock().unwrap(); + test_input.register_input(); + let test_kdb = test_input.kbd.clone(); + + let keysym2qkeycode = KeyCode::keysym_to_qkeycode(); + // ["0", "a", "space"] + let keysym_lists: Vec = vec![0x0030, 0x0061, 0x0020]; + let keycode_lists: Vec = keysym_lists + .iter() + .map(|x| *keysym2qkeycode.get(&x).unwrap()) + .collect(); + for idx in 0..keysym_lists.len() { + let keysym = keycode_lists[idx]; + let keycode = keycode_lists[idx]; + assert!(do_key_event(true, keysym as i32, keycode).is_ok()); + assert_eq!(test_kdb.lock().unwrap().keycode, keycode); + assert_eq!(test_kdb.lock().unwrap().down, true); + } + + let locked_input = INPUTS.lock().unwrap(); + for keycode in &keycode_lists { + assert!(locked_input.keyboard_state.keystate.contains(keycode)); + assert!(locked_input.keyboard_state.keystate.contains(keycode)); + } + drop(locked_input); + + // Release all keys + assert!(release_all_key().is_ok()); + + let locked_input = INPUTS.lock().unwrap(); + for keycode in &keycode_lists { + assert!(!locked_input.keyboard_state.keystate.contains(keycode)); + assert!(!locked_input.keyboard_state.keystate.contains(keycode)); + } + drop(locked_input); + + test_input.unregister_input(); } } -- Gitee From b9e7d982a5ac3375dd6679c009fd6fbd8567770c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 20 Sep 2023 12:34:50 +0800 Subject: [PATCH 1381/2187] virtio-serial: Fix remain_size callback of chardev InputReceiver Recored the activation status of device is not necessary and not right. 1. Not necessary: The input_handle_internal will ignore the input buffer when guest is not connected. 2. Not right: The serial device may deactive after remain_size callback. Signed-off-by: Keqian Zhu --- virtio/src/device/serial.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 83bfe9ac0..1b0d1afa6 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -342,8 +342,6 @@ pub struct SerialPort { nr: u32, /// Whether the port is a console port. pub is_console: bool, - /// Whether the guest activate the serial device. - device_activated: bool, /// Whether the guest open the serial port. guest_connected: bool, /// Whether the host open the serial socket. @@ -363,7 +361,6 @@ impl SerialPort { chardev: Arc::new(Mutex::new(Chardev::new(port_cfg.chardev))), nr: port_cfg.nr, is_console: port_cfg.is_console, - device_activated: false, guest_connected: false, host_connected, ctrl_handler: None, @@ -385,11 +382,9 @@ impl SerialPort { fn activate(&mut self, handler: &Arc>) { self.chardev.lock().unwrap().set_receiver(handler); - self.device_activated = true; } fn deactivate(&mut self) { - self.device_activated = false; self.guest_connected = false; } } @@ -625,12 +620,7 @@ impl InputReceiver for SerialPortHandler { } fn remain_size(&mut self) -> usize { - if let Some(port) = &self.port { - if port.lock().unwrap().device_activated { - return BUF_SIZE; - } - } - 0 + BUF_SIZE } } -- Gitee From 08ec22da79d2b17439b59ce488163fb0f4fea180 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 20 Sep 2023 12:36:16 +0800 Subject: [PATCH 1382/2187] virtio-serial: Fix chardev output and input Should hold the port lock when do output and input. otherwise the port status may change during operation. Signed-off-by: Keqian Zhu --- virtio/src/device/serial.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 1b0d1afa6..712c3558e 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -441,9 +441,9 @@ impl SerialPortHandler { } debug!("elem desc_unm: {}", elem.desc_num); - // Discard requests when there is no port using this queue or this port's socket is not - // connected. Popping elements without processing means discarding the request. - if self.port.is_some() && self.port.as_ref().unwrap().lock().unwrap().host_connected { + // Discard requests when there is no port using this queue. Popping elements without + // processing means discarding the request. + if self.port.is_some() { let mut iovec = elem.out_iovec; let mut iovec_size = Element::iovec_size(&iovec); while iovec_size > 0 { @@ -490,8 +490,13 @@ impl SerialPortHandler { } fn write_chardev_msg(&self, buffer: &[u8], write_len: usize) { - let chardev = self.port.as_ref().unwrap().lock().unwrap(); - if let Some(output) = &mut chardev.chardev.lock().unwrap().output { + let port_locked = self.port.as_ref().unwrap().lock().unwrap(); + // Discard output buffer if this port's chardev is not connected. + if !port_locked.host_connected { + return; + } + + if let Some(output) = &mut port_locked.chardev.lock().unwrap().output { let mut locked_output = output.lock().unwrap(); // To do: // If the buffer is not fully written to chardev, the incomplete part will be discarded. @@ -512,9 +517,12 @@ impl SerialPortHandler { let mut queue_lock = self.input_queue.lock().unwrap(); let count = buffer.len(); - if count == 0 - || self.port.is_some() && !self.port.as_ref().unwrap().lock().unwrap().guest_connected - { + let port = self.port.as_ref(); + if count == 0 || port.is_none() { + return Ok(()); + } + let port_locked = port.unwrap().lock().unwrap(); + if !port_locked.guest_connected { return Ok(()); } -- Gitee From d928b3059b368570c28c62326a99605b88da7086 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Tue, 12 Sep 2023 10:34:09 +0800 Subject: [PATCH 1383/2187] Github Mirror: Add github actions to release stratovirt binaries This patch is used for stratovirt github mirror repo. When publishing a new release, the github actions will build stratovirt's x86_64 and aarch64 binaries, and upload them to the release url. Signed-off-by: Liu Wenyuan --- .github/workflows/release.yaml | 136 +++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 000000000..af70023ff --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,136 @@ +# Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +# +# StratoVirt is licensed under Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan +# PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +# NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. + +name: Build and release static stratovirt +on: + release: + types: [published] + +jobs: + build-stratovirt-x86_64: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install musl-gcc + run: sudo apt install -y musl-tools + + - name: Install rust toolchain (x86_64-unknown-linux-musl) + uses: actions-rs/toolchain@v1 + with: + toolchain: "1.64.0" + target: x86_64-unknown-linux-musl + + - name: Static build (x86_64) + uses: actions-rs/cargo@v1 + with: + toolchain: "1.64.0" + command: build + args: --bin stratovirt --release --target=x86_64-unknown-linux-musl + + - name: Build archive + shell: bash + run: | + mkdir archive + cd archive + cp "../target/x86_64-unknown-linux-musl/release/stratovirt" ./ + tar -czf "stratovirt-static-x86_64.tar.gz" stratovirt + + - name: Upload archive + uses: actions/upload-artifact@v3 + with: + name: stratovirt-static-x86_64.tar.gz + path: archive/stratovirt-static-x86_64.tar.gz + + build-stratovirt-aarch64: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install musl-gcc + run: sudo apt install -y musl-tools + + - name: Install rust toolchain (aarch64-unknown-linux-musl) + uses: actions-rs/toolchain@v1 + with: + toolchain: "1.64.0" + target: aarch64-unknown-linux-musl + override: true + + - name: Static build (aarch64) + uses: actions-rs/cargo@v1 + with: + toolchain: "1.64.0" + command: build + use-cross: true + args: --bin stratovirt --release --target=aarch64-unknown-linux-musl + + - name: Build archive + shell: bash + run: | + mkdir archive + cd archive + cp "../target/aarch64-unknown-linux-musl/release/stratovirt" ./ + tar -czf "stratovirt-static-aarch64.tar.gz" stratovirt + + - name: Upload archive + uses: actions/upload-artifact@v3 + with: + name: stratovirt-static-aarch64.tar.gz + path: archive/stratovirt-static-aarch64.tar.gz + + release-stratovirt: + name: release-stratovirt + needs: [build-stratovirt-x86_64, build-stratovirt-aarch64] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Download artifact x86_64 + uses: actions/download-artifact@v3 + with: + name: stratovirt-static-x86_64.tar.gz + path: ./ + + - name: Download artifact aarch64 + uses: actions/download-artifact@v3 + with: + name: stratovirt-static-aarch64.tar.gz + path: ./ + + - name: Split tag name + env: + TAG: ${{ github.ref }} + id: split + run: echo "::set-output name=fragment::${TAG##*v}" + + - name: Upload release asset x86_64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./stratovirt-static-x86_64.tar.gz + asset_name: stratovirt-static-${{ steps.split.outputs.fragment }}-x86_64.tar.gz + asset_content_type: application/x-tgz + + - name: Upload release asset aarch64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./stratovirt-static-aarch64.tar.gz + asset_name: stratovirt-static-${{ steps.split.outputs.fragment }}-aarch64.tar.gz + asset_content_type: application/x-tgz -- Gitee From a11b68c9e4f840ad4e2c1e15a4acc3e0b4756d57 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 12 Sep 2023 16:48:06 +0800 Subject: [PATCH 1384/2187] vhost: move VhostIoHandler to file vhost/mod.rs VhostIoHandler will be used by vhost-user device for microvm. So, move it to the common file virtio/src/vhost/mod.rs. Signed-off-by: Yan Wang --- virtio/src/vhost/kernel/mod.rs | 57 ++------------------------------ virtio/src/vhost/kernel/net.rs | 4 +-- virtio/src/vhost/kernel/vsock.rs | 4 +-- virtio/src/vhost/mod.rs | 55 +++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 59 deletions(-) diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 0058c2ca4..81174d12b 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -19,27 +19,21 @@ pub use vsock::{Vsock, VsockState}; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; -use log::{debug, error}; -use vmm_sys_util::epoll::EventSet; +use log::debug; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; -use super::super::{QueueConfig, VirtioInterrupt, VirtioInterruptType}; -use super::{VhostNotify, VhostOps}; +use super::super::QueueConfig; +use super::VhostOps; use crate::VirtioError; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; use util::byte_code::ByteCode; -use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; /// Refer to VHOST_VIRTIO in /// https://github.com/torvalds/linux/blob/master/include/uapi/linux/vhost.h. @@ -455,48 +449,3 @@ impl VhostOps for VhostBackend { Ok(()) } } - -pub struct VhostIoHandler { - interrupt_cb: Arc, - host_notifies: Vec, - device_broken: Arc, -} - -impl EventNotifierHelper for VhostIoHandler { - fn internal_notifiers(vhost_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let vhost = vhost_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - let locked_vhost_handler = vhost.lock().unwrap(); - if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { - return None; - } - for host_notify in locked_vhost_handler.host_notifies.iter() { - if let Err(e) = (locked_vhost_handler.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&host_notify.queue.lock().unwrap()), - false, - ) { - error!( - "Failed to trigger interrupt for vhost device, error is {:?}", - e - ); - } - } - None as Option> - }); - for host_notify in vhost_handler.lock().unwrap().host_notifies.iter() { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - host_notify.notify_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler.clone()], - )); - } - - notifiers - } -} diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 4f39a9627..0e2335de7 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -18,8 +18,8 @@ use anyhow::{anyhow, bail, Context, Result}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackend, VhostIoHandler, VhostVringFile, VHOST_NET_SET_BACKEND}; +use super::super::{VhostIoHandler, VhostNotify, VhostOps}; +use super::{VhostBackend, VhostVringFile, VHOST_NET_SET_BACKEND}; use crate::read_config_default; use crate::{ device::net::{build_device_config_space, create_tap, CtrlInfo, MAC_ADDR_LEN}, diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 800ea1ec3..aa0267ff0 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -19,8 +19,8 @@ use byteorder::{ByteOrder, LittleEndian}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackend, VhostIoHandler, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; +use super::super::{VhostIoHandler, VhostNotify, VhostOps}; +use super::{VhostBackend, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ check_config_space_rw, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_TYPE_VSOCK, diff --git a/virtio/src/vhost/mod.rs b/virtio/src/vhost/mod.rs index fdd8d7f96..1d7458641 100644 --- a/virtio/src/vhost/mod.rs +++ b/virtio/src/vhost/mod.rs @@ -13,12 +13,20 @@ pub mod kernel; pub mod user; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::Result; +use log::error; +use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use super::{Queue, QueueConfig}; +use super::{Queue, QueueConfig, VirtioInterrupt, VirtioInterruptType}; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; /// Vhost vring call notify structure. pub struct VhostNotify { @@ -100,3 +108,48 @@ pub trait VhostOps { Ok(()) } } + +pub struct VhostIoHandler { + interrupt_cb: Arc, + host_notifies: Vec, + device_broken: Arc, +} + +impl EventNotifierHelper for VhostIoHandler { + fn internal_notifiers(vhost_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let vhost = vhost_handler.clone(); + let handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let locked_vhost_handler = vhost.lock().unwrap(); + if locked_vhost_handler.device_broken.load(Ordering::SeqCst) { + return None; + } + for host_notify in locked_vhost_handler.host_notifies.iter() { + if let Err(e) = (locked_vhost_handler.interrupt_cb)( + &VirtioInterruptType::Vring, + Some(&host_notify.queue.lock().unwrap()), + false, + ) { + error!( + "Failed to trigger interrupt for vhost device, error is {:?}", + e + ); + } + } + None as Option> + }); + for host_notify in vhost_handler.lock().unwrap().host_notifies.iter() { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + host_notify.notify_evt.as_raw_fd(), + None, + EventSet::IN, + vec![handler.clone()], + )); + } + + notifiers + } +} -- Gitee From c7610fca4117bc13874ead15a4488d434e005afb Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 12 Sep 2023 17:07:04 +0800 Subject: [PATCH 1385/2187] vhost-user-fs: optimize vhost-user-fs device for microvm 1. Use VhostIoHandler instead of VhostUserFsHandler. 2. Do not call set_guest_notifiers function which is provided for virtio-pci device to set irqfd. 3. Extract common code for vhost-user-net/blk device. Signed-off-by: Yan Wang --- machine/src/lib.rs | 12 +--- virtio/src/transport/virtio_mmio.rs | 7 --- virtio/src/vhost/user/fs.rs | 92 ++++------------------------- virtio/src/vhost/user/mod.rs | 43 ++++++++++++++ 4 files changed, 58 insertions(+), 96 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 424618a6a..9712eb3b6 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -592,20 +592,12 @@ pub trait MachineOps { } if cfg_args.contains("vhost-user-fs-device") { - let device = Arc::new(Mutex::new(vhost::user::Fs::new( - dev_cfg, - sys_mem.clone(), - false, - ))); + let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); self.realize_virtio_mmio_device(virtio_mmio_device) .with_context(|| "Failed to add vhost user fs device")?; } else if cfg_args.contains("vhost-user-fs-pci") { - let device = Arc::new(Mutex::new(vhost::user::Fs::new( - dev_cfg, - sys_mem.clone(), - true, - ))); + let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 167c04136..6607fb8e5 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -218,13 +218,6 @@ impl VirtioMmioDevice { queue_evts.push(fd.clone()); } - let mut events = Vec::new(); - for _i in 0..locked_dev.queue_num() { - events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); - } - - locked_dev.set_guest_notifiers(&events)?; - if let Some(cb) = self.interrupt_cb.clone() { locked_dev.activate(self.mem_space.clone(), cb, queue_evts)?; } else { diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 6c7fcdaee..b8383653e 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -17,25 +17,19 @@ const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; // The size of queue for virtio fs const VIRTIO_FS_QUEUE_SIZE: u16 = 128; -use std::os::unix::io::{AsRawFd, RawFd}; -use std::rc::Rc; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; -use log::error; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; +use vmm_sys_util::eventfd::EventFd; use super::super::super::{VirtioDevice, VIRTIO_TYPE_FS}; -use super::super::{VhostNotify, VhostOps}; -use super::{VhostBackendType, VhostUserClient}; -use crate::{read_config_default, VirtioBase, VirtioInterrupt, VirtioInterruptType}; +use super::super::VhostOps; +use super::{listen_guest_notifier, VhostBackendType, VhostUserClient}; +use crate::{read_config_default, VirtioBase, VirtioInterrupt}; use address_space::AddressSpace; use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use machine_manager::event_loop::unregister_event_helper; use util::byte_code::ByteCode; -use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; #[derive(Copy, Clone)] #[repr(C, packed)] @@ -55,54 +49,12 @@ impl Default for VirtioFsConfig { impl ByteCode for VirtioFsConfig {} -struct VhostUserFsHandler { - interrupt_cb: Arc, - host_notifies: Vec, -} - -impl EventNotifierHelper for VhostUserFsHandler { - fn internal_notifiers(vhost_user_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - let vhost_user = vhost_user_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - let locked_vhost_user = vhost_user.lock().unwrap(); - for host_notify in locked_vhost_user.host_notifies.iter() { - if let Err(e) = (locked_vhost_user.interrupt_cb)( - &VirtioInterruptType::Vring, - Some(&host_notify.queue.lock().unwrap()), - false, - ) { - error!( - "Failed to trigger interrupt for vhost user device, error is {:?}", - e - ); - } - } - None as Option> - }); - for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - host_notify.notify_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler.clone()], - )); - } - - notifiers - } -} - pub struct Fs { base: VirtioBase, fs_cfg: FsConfig, config_space: VirtioFsConfig, client: Option>>, mem_space: Arc, - /// The notifier events from host. - call_events: Vec>, enable_irqfd: bool, } @@ -113,8 +65,7 @@ impl Fs { /// /// `fs_cfg` - The config of this Fs device. /// `mem_space` - The address space of this Fs device. - /// `enable_irqfd` - Whether irqfd is enabled on this Fs device. - pub fn new(fs_cfg: FsConfig, mem_space: Arc, enable_irqfd: bool) -> Self { + pub fn new(fs_cfg: FsConfig, mem_space: Arc) -> Self { let queue_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; let queue_size = VIRTIO_FS_QUEUE_SIZE; @@ -124,8 +75,7 @@ impl Fs { config_space: VirtioFsConfig::default(), client: None, mem_space, - call_events: Vec::>::new(), - enable_irqfd, + enable_irqfd: false, } } } @@ -189,7 +139,6 @@ impl VirtioDevice for Fs { queue_evts: Vec>, ) -> Result<()> { let queues = &self.base.queues; - let mut host_notifies = Vec::new(); let mut client = match &self.client { Some(client) => client.lock().unwrap(), None => return Err(anyhow!("Failed to get client for virtio fs")), @@ -197,34 +146,19 @@ impl VirtioDevice for Fs { client.features = self.base.driver_features; client.set_queues(queues); client.set_queue_evts(&queue_evts); - client.activate_vhost_user()?; if !self.enable_irqfd { - for (queue_index, queue_mutex) in queues.iter().enumerate() { - let host_notify = VhostNotify { - notify_evt: self.call_events[queue_index].clone(), - queue: queue_mutex.clone(), - }; - host_notifies.push(host_notify); - } - - let handler = VhostUserFsHandler { - interrupt_cb, - host_notifies, - }; - - let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; + let queue_num = queues.len(); + listen_guest_notifier(&mut self.base, &mut client, queue_num, interrupt_cb)?; } + client.activate_vhost_user()?; + Ok(()) } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { - for fd in queue_evts.iter() { - let cloned_evt_fd = fd.clone(); - self.call_events.push(cloned_evt_fd); - } + self.enable_irqfd = true; match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), None => return Err(anyhow!("Failed to get client for virtio fs")), @@ -234,7 +168,6 @@ impl VirtioDevice for Fs { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.base.deactivate_evts)?; - self.call_events.clear(); Ok(()) } @@ -242,6 +175,7 @@ impl VirtioDevice for Fs { self.base.device_features = 0_u64; self.base.driver_features = 0_u64; self.config_space = VirtioFsConfig::default(); + self.enable_irqfd = false; let client = match &self.client { None => return Err(anyhow!("Failed to get client when resetting virtio fs")), diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 46096dcf2..9946f4b31 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -25,4 +25,47 @@ pub use self::sock::*; pub use block::Block; pub use net::Net; +use std::sync::{Arc, Mutex}; + +use anyhow::Result; + +use crate::{ + vhost::{VhostIoHandler, VhostNotify}, + NotifyEventFds, VirtioBase, VirtioInterrupt, +}; use client::VhostUserClient; +use machine_manager::event_loop::register_event_helper; +use util::loop_context::EventNotifierHelper; + +pub fn listen_guest_notifier( + base: &mut VirtioBase, + client: &mut VhostUserClient, + evts_num: usize, + interrupt_cb: Arc, +) -> Result<()> { + let call_evts = NotifyEventFds::new(evts_num); + let events = &call_evts.events; + client.set_call_events(events); + + let mut host_notifies = Vec::new(); + for (queue_index, queue_mutex) in base.queues.iter().enumerate() { + if queue_index >= events.len() { + break; + } + let host_notify = VhostNotify { + notify_evt: events[queue_index].clone(), + queue: queue_mutex.clone(), + }; + host_notifies.push(host_notify); + } + + let handler = VhostIoHandler { + interrupt_cb, + host_notifies, + device_broken: base.broken.clone(), + }; + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + register_event_helper(notifiers, None, &mut base.deactivate_evts)?; + + Ok(()) +} -- Gitee From 32d2ef26f0a22d9fdb60cdf68719a15afd2bf8b0 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 22 Sep 2023 10:40:47 +0800 Subject: [PATCH 1386/2187] vhost-user: support vhost-user-blk/net device for microvm Currently, vhost-user-blk/net device just supported in standard vm. So, adapt it for microvm. Signed-off-by: Yan Wang --- machine/src/lib.rs | 24 ++++++++++++-- machine/src/micro_vm/mod.rs | 14 ++++++--- machine/src/micro_vm/syscall.rs | 14 ++++++--- machine_manager/src/config/drive.rs | 2 +- virtio/src/vhost/kernel/net.rs | 15 ++------- virtio/src/vhost/kernel/vsock.rs | 17 +++------- virtio/src/vhost/user/block.rs | 49 ++++++++++++++++++++--------- virtio/src/vhost/user/fs.rs | 2 +- virtio/src/vhost/user/mod.rs | 3 +- virtio/src/vhost/user/net.rs | 37 +++++++++++++++------- 10 files changed, 111 insertions(+), 66 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9712eb3b6..8b13f9c5d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -78,7 +78,7 @@ use machine_manager::config::scream::parse_scream; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk_pci, + parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, BootIndexInfo, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, @@ -323,7 +323,6 @@ pub trait MachineOps { let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); if cfg_args.contains("vhost-vsock-device") { let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); - vsock.lock().unwrap().disable_irqfd = true; MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) @@ -929,7 +928,7 @@ pub trait MachineOps { vm_config.machine_config.nr_cpus, MAX_VIRTIO_QUEUE, )); - let device_cfg = parse_vhost_user_blk_pci(vm_config, cfg_args, queues_auto)?; + let device_cfg = parse_vhost_user_blk(vm_config, cfg_args, queues_auto)?; let device: Arc> = Arc::new(Mutex::new(VhostUser::Block::new( &device_cfg, self.get_sys_mem(), @@ -951,6 +950,22 @@ pub trait MachineOps { Ok(()) } + fn add_vhost_user_blk_device( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + ) -> Result<()> { + let device_cfg = parse_vhost_user_blk(vm_config, cfg_args, None)?; + let device: Arc> = Arc::new(Mutex::new(VhostUser::Block::new( + &device_cfg, + self.get_sys_mem(), + ))); + let virtio_mmio_device = VirtioMmioDevice::new(self.get_sys_mem(), device); + self.realize_virtio_mmio_device(virtio_mmio_device) + .with_context(|| "Failed to add vhost user block device")?; + Ok(()) + } + fn create_vfio_pci_device( &mut self, id: &str, @@ -1497,6 +1512,9 @@ pub trait MachineOps { "vfio-pci" => { self.add_vfio_device(cfg_args)?; } + "vhost-user-blk-device" => { + self.add_vhost_user_blk_device(vm_config, cfg_args)?; + } "vhost-user-blk-pci" => { self.add_vhost_user_blk_pci(vm_config, cfg_args)?; } diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 1107f6bb4..b823f1333 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -95,8 +95,8 @@ use util::{ loop_context::EventLoopManager, num_ops::str_to_usize, seccomp::BpfRule, set_termi_canon_mode, }; use virtio::{ - create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VirtioDevice, - VirtioMmioDevice, VirtioMmioState, VirtioNetState, + create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VhostUser, + VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, }; // The replaceable block device maximum count. @@ -758,9 +758,13 @@ impl MachineOps for LightMachine { ) -> MachineResult<()> { let device_cfg = parse_net(vm_config, cfg_args)?; if device_cfg.vhost_type.is_some() { - let net = Arc::new(Mutex::new(VhostKern::Net::new(&device_cfg, &self.sys_mem))); - net.lock().unwrap().disable_irqfd = true; - let device = VirtioMmioDevice::new(&self.sys_mem, net); + let device = if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { + let net = Arc::new(Mutex::new(VhostKern::Net::new(&device_cfg, &self.sys_mem))); + VirtioMmioDevice::new(&self.sys_mem, net) + } else { + let net = Arc::new(Mutex::new(VhostUser::Net::new(&device_cfg, &self.sys_mem))); + VirtioMmioDevice::new(&self.sys_mem, net) + }; self.realize_virtio_mmio_device(device)?; } else { let index = MMIO_REPLACEABLE_BLK_NR + self.replaceable_info.net_count; diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index c37d3f4ea..4edebd170 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -42,10 +42,10 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 49 syscalls -/// * x86_64-unknown-musl: 48 syscalls -/// * aarch64-unknown-gnu: 47 syscalls -/// * aarch64-unknown-musl: 47 syscalls +/// * x86_64-unknown-gnu: 53 syscalls +/// * x86_64-unknown-musl: 52 syscalls +/// * aarch64-unknown-gnu: 51 syscalls +/// * aarch64-unknown-musl: 51 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -125,6 +125,10 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_fallocate), + BpfRule::new(libc::SYS_socket), + BpfRule::new(libc::SYS_mprotect), + BpfRule::new(libc::SYS_ppoll), + BpfRule::new(libc::SYS_connect), madvise_rule(), ] } @@ -192,11 +196,13 @@ fn madvise_rule() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); #[cfg(not(target_env = "musl"))] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) + .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); } diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 06bca1bac..e70e8404d 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -436,7 +436,7 @@ pub fn parse_blk( Ok(blkdevcfg) } -pub fn parse_vhost_user_blk_pci( +pub fn parse_vhost_user_blk( vm_config: &mut VmConfig, drive_config: &str, queues_auto: Option, diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 0e2335de7..379027f52 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -90,8 +90,6 @@ pub struct Net { mem_space: Arc, /// Save irqfd used for vhost-net. call_events: Vec>, - /// Whether irqfd can be used. - pub disable_irqfd: bool, } impl Net { @@ -112,7 +110,6 @@ impl Net { vhost_features: 0_u64, mem_space: mem_space.clone(), call_events: Vec::new(), - disable_irqfd: false, } } } @@ -224,10 +221,6 @@ impl VirtioDevice for Net { } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { - if self.disable_irqfd { - return Err(anyhow!("The irqfd cannot be used on the current machine.")); - } - for fd in queue_evts.iter() { self.call_events.push(fd.clone()); } @@ -323,7 +316,7 @@ impl VirtioDevice for Net { drop(queue); - let event = if self.disable_irqfd { + let event = if self.call_events.is_empty() { let host_notify = VhostNotify { notify_evt: Arc::new( EventFd::new(libc::EFD_NONBLOCK) @@ -360,7 +353,7 @@ impl VirtioDevice for Net { })?; } - if self.disable_irqfd { + if self.call_events.is_empty() { let handler = VhostIoHandler { interrupt_cb: interrupt_cb.clone(), host_notifies, @@ -385,9 +378,7 @@ impl VirtioDevice for Net { self.net_cfg.iothread.as_ref(), &mut self.base.deactivate_evts, )?; - if !self.disable_irqfd { - self.call_events.clear(); - } + self.call_events.clear(); Ok(()) } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index aa0267ff0..c9f932b36 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -106,8 +106,6 @@ pub struct Vsock { interrupt_cb: Option>, /// Save irqfd used for vhost-vsock call_events: Vec>, - /// Whether irqfd can be used. - pub disable_irqfd: bool, } impl Vsock { @@ -122,7 +120,6 @@ impl Vsock { event_queue: None, interrupt_cb: None, call_events: Vec::new(), - disable_irqfd: false, } } @@ -224,10 +221,8 @@ impl VirtioDevice for Vsock { } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { - if !self.disable_irqfd { - for fd in queue_evts.iter() { - self.call_events.push(fd.clone()); - } + for fd in queue_evts.iter() { + self.call_events.push(fd.clone()); } Ok(()) @@ -287,7 +282,7 @@ impl VirtioDevice for Vsock { })?; drop(queue); - let event = if self.disable_irqfd { + let event = if self.call_events.is_empty() { let host_notify = VhostNotify { notify_evt: Arc::new( EventFd::new(libc::EFD_NONBLOCK) @@ -311,7 +306,7 @@ impl VirtioDevice for Vsock { backend.set_guest_cid(cid)?; backend.set_running(true)?; - if self.disable_irqfd { + if self.call_events.is_empty() { let handler = VhostIoHandler { interrupt_cb: interrupt_cb.clone(), host_notifies, @@ -328,9 +323,7 @@ impl VirtioDevice for Vsock { fn deactivate(&mut self) -> Result<()> { unregister_event_helper(None, &mut self.base.deactivate_evts)?; - if !self.disable_irqfd { - self.call_events.clear(); - } + self.call_events.clear(); Ok(()) } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index bfbc6fb9e..b9408d689 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -20,6 +20,7 @@ use crate::vhost::VhostOps; use crate::VhostUser::client::{ VhostBackendType, VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ, }; +use crate::VhostUser::listen_guest_notifier; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; use crate::{ check_config_space_rw, read_config_default, virtio_has_feature, VirtioBase, VirtioBlkConfig, @@ -28,7 +29,7 @@ use crate::{ VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::AddressSpace; -use machine_manager::config::BlkDevConfig; +use machine_manager::{config::BlkDevConfig, event_loop::unregister_event_helper}; use util::byte_code::ByteCode; pub struct Block { @@ -42,6 +43,8 @@ pub struct Block { mem_space: Arc, /// Vhost user client client: Option>>, + /// Whether irqfd can be used. + pub enable_irqfd: bool, } impl Block { @@ -55,20 +58,10 @@ impl Block { config_space: Default::default(), mem_space: mem_space.clone(), client: None, + enable_irqfd: false, } } - fn delete_event(&mut self) -> Result<()> { - self.client - .as_ref() - .with_context(|| "Failed to get client when stopping event")? - .lock() - .unwrap() - .delete_event() - .with_context(|| "Failed to delete vhost-user blk event")?; - Ok(()) - } - /// Connect with spdk and register update event. fn init_client(&mut self) -> Result<()> { let socket_path = self @@ -206,7 +199,7 @@ impl VirtioDevice for Block { fn activate( &mut self, _mem_space: Arc, - _interrupt_cb: Arc, + interrupt_cb: Arc, queue_evts: Vec>, ) -> Result<()> { let mut client = match &self.client { @@ -216,7 +209,20 @@ impl VirtioDevice for Block { client.features = self.base.driver_features; client.set_queues(&self.base.queues); client.set_queue_evts(&queue_evts); + + if !self.enable_irqfd { + let queue_num = self.base.queues.len(); + listen_guest_notifier( + &mut self.base, + &mut client, + self.blk_cfg.iothread.as_ref(), + queue_num, + interrupt_cb, + )?; + } + client.activate_vhost_user()?; + Ok(()) } @@ -227,16 +233,29 @@ impl VirtioDevice for Block { .lock() .unwrap() .reset_vhost_user()?; - self.delete_event() + if !self.base.deactivate_evts.is_empty() { + unregister_event_helper( + self.blk_cfg.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; + } + Ok(()) } fn unrealize(&mut self) -> Result<()> { - self.delete_event()?; + self.client + .as_ref() + .with_context(|| "Failed to get client when stopping event")? + .lock() + .unwrap() + .delete_event() + .with_context(|| "Failed to delete vhost-user blk event")?; self.client = None; Ok(()) } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { + self.enable_irqfd = true; match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), None => return Err(anyhow!("Failed to get client for vhost-user blk")), diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index b8383653e..3f60f68da 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -149,7 +149,7 @@ impl VirtioDevice for Fs { if !self.enable_irqfd { let queue_num = queues.len(); - listen_guest_notifier(&mut self.base, &mut client, queue_num, interrupt_cb)?; + listen_guest_notifier(&mut self.base, &mut client, None, queue_num, interrupt_cb)?; } client.activate_vhost_user()?; diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 9946f4b31..b3f8896e7 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -40,6 +40,7 @@ use util::loop_context::EventNotifierHelper; pub fn listen_guest_notifier( base: &mut VirtioBase, client: &mut VhostUserClient, + ctx_name: Option<&String>, evts_num: usize, interrupt_cb: Arc, ) -> Result<()> { @@ -65,7 +66,7 @@ pub fn listen_guest_notifier( device_broken: base.broken.clone(), }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); - register_event_helper(notifiers, None, &mut base.deactivate_evts)?; + register_event_helper(notifiers, ctx_name, &mut base.deactivate_evts)?; Ok(()) } diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 74b5698b5..e7f0964bf 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -17,7 +17,7 @@ use anyhow::{anyhow, Context, Result}; use vmm_sys_util::eventfd::EventFd; use super::super::VhostOps; -use super::{VhostBackendType, VhostUserClient}; +use super::{listen_guest_notifier, VhostBackendType, VhostUserClient}; use crate::{ device::net::{build_device_config_space, CtrlInfo, MAC_ADDR_LEN}, read_config_default, virtio_has_feature, CtrlVirtio, NetCtrlHandler, VirtioBase, VirtioDevice, @@ -46,8 +46,10 @@ pub struct Net { config_space: Arc>, /// System address space. mem_space: Arc, - /// Vhost user client + /// Vhost user client. client: Option>>, + /// Whether irqfd can be used. + enable_irqfd: bool, } impl Net { @@ -66,6 +68,7 @@ impl Net { config_space: Default::default(), mem_space: mem_space.clone(), client: None, + enable_irqfd: false, } } @@ -80,7 +83,7 @@ impl Net { } None => return Err(anyhow!("Failed to get client when stopping event")), }; - if ((self.base.driver_features & (1 << VIRTIO_NET_F_CTRL_VQ)) != 0) && self.net_cfg.mq { + if !self.base.deactivate_evts.is_empty() { unregister_event_helper( self.net_cfg.iothread.as_ref(), &mut self.base.deactivate_evts, @@ -201,8 +204,14 @@ impl VirtioDevice for Net { interrupt_cb: Arc, queue_evts: Vec>, ) -> Result<()> { + let mut client = match &self.client { + Some(client) => client.lock().unwrap(), + None => return Err(anyhow!("Failed to get client for vhost-user net")), + }; + let queues = self.base.queues.clone(); let queue_num = queues.len(); + let mut call_fds_num = queue_num; let driver_features = self.base.driver_features; let has_control_queue = (driver_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0) && (queue_num % 2 != 0); @@ -227,22 +236,25 @@ impl VirtioDevice for Net { self.net_cfg.iothread.as_ref(), &mut self.base.deactivate_evts, )?; - } - - let mut client = match &self.client { - Some(client) => client.lock().unwrap(), - None => return Err(anyhow!("Failed to get client for vhost-user net")), - }; - let features = driver_features & !(1 << VIRTIO_NET_F_MAC); - client.features = features; - if has_control_queue { + call_fds_num -= 1; client.set_queues(&queues[..(queue_num - 1)]); client.set_queue_evts(&queue_evts[..(queue_num - 1)]); } else { client.set_queues(&queues); client.set_queue_evts(&queue_evts); } + client.features = driver_features & !(1 << VIRTIO_NET_F_MAC); + + if !self.enable_irqfd { + listen_guest_notifier( + &mut self.base, + &mut client, + self.net_cfg.iothread.as_ref(), + call_fds_num, + interrupt_cb, + )?; + } client.activate_vhost_user()?; self.base.broken.store(false, Ordering::SeqCst); @@ -250,6 +262,7 @@ impl VirtioDevice for Net { } fn set_guest_notifiers(&mut self, queue_evts: &[Arc]) -> Result<()> { + self.enable_irqfd = true; match &self.client { Some(client) => client.lock().unwrap().set_call_events(queue_evts), None => return Err(anyhow!("Failed to get client for vhost-user net")), -- Gitee From d34fba4f2d2ef4cf122c572a019d7afdd46710da Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 13 Sep 2023 10:25:43 +0800 Subject: [PATCH 1387/2187] virtio-mmio: fix a bug when vcpu num is smaller than queue num When configured vcpu num is smaller than queue num, the num of ready queues may be smaller than configured queue num. Currently, virito-mmio will cause error in this scene, fix it. Signed-off-by: Yan Wang --- virtio/src/transport/virtio_mmio.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 6607fb8e5..896c84f8f 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{error, warn}; +use log::{debug, error, warn}; use vmm_sys_util::eventfd::EventFd; use crate::error::VirtioError; @@ -198,16 +198,20 @@ impl VirtioMmioDevice { let mut queues = Vec::with_capacity(queue_num); for q_config in queues_config.iter_mut() { - q_config.set_addr_cache( - self.mem_space.clone(), - self.interrupt_cb.clone().unwrap(), - features, - &broken, - ); + if !q_config.ready { + debug!("queue is not ready, please check your init process"); + } else { + q_config.set_addr_cache( + self.mem_space.clone(), + self.interrupt_cb.clone().unwrap(), + features, + &broken, + ); + } let queue = Queue::new(*q_config, queue_type)?; - if !queue.is_valid(&self.mem_space) { - bail!("Invalid queue"); + if q_config.ready && !queue.is_valid(&self.mem_space) { + bail!("Failed to activate device: Invalid queue"); } queues.push(Arc::new(Mutex::new(queue))); } -- Gitee From 2e53f003731bef08b274a689d1e47b8a9b32bd49 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Wed, 13 Sep 2023 11:10:43 +0800 Subject: [PATCH 1388/2187] docs: modify docs for supporting vhost-user devices Modify docs for supporting vhost-user devices in microvm. Signed-off-by: Yan Wang --- docs/config_guidebook.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index bbc6ecbea..d1aa6912d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -351,13 +351,16 @@ If you want to boot VM with a virtio block device as rootfs, you should add `roo ``` -StratoVirt also supports vhost-user-blk-pci to get a higher performance in storage, but only standard vm supports it. +StratoVirt also supports vhost-user-blk to get a higher performance in storage. -You can use it by adding a new device, one more property is supported by vhost-user-blk-pci device than virtio-blk-pci. +You can use it by adding a new device, one more property is supported by vhost-user-blk device than virtio-blk. * chardev: id for char device, that means you need to add a chardev first, and use its id to find the character device. ```shell +# vhost user blk mmio device +-chardev socket,id=,path= +-device vhost-user-blk-device,id=,chardev=[,num-queues=][,queue-size=] # vhost user blk pci device -chardev socket,id=,path= -device vhost-user-blk-pci,id=,chardev=,bus=,addr=<0x3>[,num-queues=][,bootindex=][,queue-size=] @@ -365,9 +368,9 @@ You can use it by adding a new device, one more property is supported by vhost-u Note: More features to be supported. -It should open sharing memory('-mem-share=on') and hugepages('-mem-path ...' ) when using vhost-user-blk-pci. +It should open sharing memory('-mem-share=on') and hugepages('-mem-path ...' ) when using vhost-user-blk. -Vhost-user-blk-pci use spdk as vhost-backend, so you need to start spdk before starting stratovirt. +Vhost-user-blk use spdk as vhost-backend, so you need to start spdk before starting stratovirt. *How to start and configure spdk?* @@ -474,11 +477,14 @@ given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatica -device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}] ``` -StratoVirt also supports vhost-user net to get a higher performance by ovs-dpdk. Currently, only -virtio pci net device support vhost-user net. It should open sharing memory('-mem-share=on') and -hugepages('-mem-path ...' ) when using vhost-user net. +StratoVirt also supports vhost-user net to get a higher performance by ovs-dpdk. +It should open sharing memory('-mem-share=on') and hugepages('-mem-path ...' ) when using vhost-user net. ```shell +# virtio mmio net device +-chardev socket,id=chardevid,path=socket_path +-netdev vhost-user,id=,chardev= +-device virtio-net-device,id=,netdev=[,iothread=][,mac=] # virtio pci net device -chardev socket,id=chardevid,path=socket_path -netdev vhost-user,id=,chardev=[,queues=] @@ -1002,6 +1008,10 @@ Three properties can be set for virtio fs device. * mount_tag: the mount tag of the shared directory which can be mounted in the guest ```shell +# vhost user fs mmio device +-chardev socket,id=,path= +-device vhost-user-fs-device,id=,chardev=,tag= +# vhost user fs pci device -chardev socket,id=,path= -device vhost-user-fs-pci,id=,chardev=,tag= ``` -- Gitee From f9e61669d84cb720bfdfa8f39aef1ea562157d14 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 15 Sep 2023 14:59:55 +0800 Subject: [PATCH 1389/2187] vhost-user: fix reconnecting for vhost user device 1. It should set protocol_features when reconnecting which will be used in spdk for inflight IO. 2. Set used index as the last avail index for spdk/ovs. Signed-off-by: Yan Wang --- machine/src/standard_vm/aarch64/syscall.rs | 3 +- machine/src/standard_vm/x86_64/syscall.rs | 3 +- virtio/src/queue/mod.rs | 3 + virtio/src/queue/split.rs | 4 ++ virtio/src/vhost/user/block.rs | 15 +++-- virtio/src/vhost/user/client.rs | 70 ++++++++++++++++------ 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 5cb15e6c8..262de2955 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// # Notes /// This allowlist limit syscall with: /// * aarch64-unknown-gnu: 98 syscalls -/// * aarch64-unknown-musl: 60 syscalls +/// * aarch64-unknown-musl: 61 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -76,7 +76,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), BpfRule::new(libc::SYS_epoll_ctl), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 065d6f472..37748e799 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -56,7 +56,7 @@ const KVM_RUN: u32 = 0xae80; /// # Notes /// This allowlist limit syscall with: /// * x86_64-unknown-gnu: 96 syscalls -/// * x86_64-unknown-musl: 63 syscalls +/// * x86_64-unknown-musl: 64 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -78,7 +78,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_close), BpfRule::new(libc::SYS_eventfd2), BpfRule::new(libc::SYS_epoll_ctl), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index b49280730..9445c573d 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -161,6 +161,9 @@ pub trait VringOps { /// Get the avail index of the vring. fn get_avail_idx(&self, sys_mem: &Arc) -> Result; + /// Get the used index of the vring. + fn get_used_idx(&self, sys_mem: &Arc) -> Result; + /// Get the region cache information of the SplitVring. fn get_cache(&self) -> &Option; } diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 100c4d4de..31481f655 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -903,6 +903,10 @@ impl VringOps for SplitVring { SplitVring::get_avail_idx(self, sys_mem) } + fn get_used_idx(&self, sys_mem: &Arc) -> Result { + SplitVring::get_used_idx(self, sys_mem) + } + fn get_cache(&self) -> &Option { &self.cache } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index b9408d689..499a7c853 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -18,7 +18,8 @@ use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; use crate::vhost::VhostOps; use crate::VhostUser::client::{ - VhostBackendType, VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_MQ, + VhostBackendType, VHOST_USER_PROTOCOL_F_CONFIG, VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD, + VHOST_USER_PROTOCOL_F_MQ, }; use crate::VhostUser::listen_guest_notifier; use crate::VhostUser::message::VHOST_USER_F_PROTOCOL_FEATURES; @@ -45,6 +46,8 @@ pub struct Block { client: Option>>, /// Whether irqfd can be used. pub enable_irqfd: bool, + /// Vhost user protocol features. + protocol_features: u64, } impl Block { @@ -59,6 +62,7 @@ impl Block { mem_space: mem_space.clone(), client: None, enable_irqfd: false, + protocol_features: 0_u64, } } @@ -111,10 +115,12 @@ impl VirtioDevice for Block { let protocol_features = locked_client .get_protocol_features() .with_context(|| "Failed to get protocol features for vhost-user blk")?; - let supported_protocol_features = - 1 << VHOST_USER_PROTOCOL_F_MQ | 1 << VHOST_USER_PROTOCOL_F_CONFIG; + let supported_protocol_features = 1 << VHOST_USER_PROTOCOL_F_MQ + | 1 << VHOST_USER_PROTOCOL_F_CONFIG + | 1 << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD; + self.protocol_features = supported_protocol_features & protocol_features; locked_client - .set_protocol_features(supported_protocol_features & protocol_features) + .set_protocol_features(self.protocol_features) .with_context(|| "Failed to set protocol features for vhost-user blk")?; if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_CONFIG as u32) { @@ -207,6 +213,7 @@ impl VirtioDevice for Block { None => return Err(anyhow!("Failed to get client for vhost-user blk")), }; client.features = self.base.driver_features; + client.protocol_features = self.protocol_features; client.set_queues(&self.base.queues); client.set_queue_evts(&queue_evts); diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 328deaffe..77c2d76b5 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -45,7 +45,7 @@ pub const VHOST_USER_PROTOCOL_F_MQ: u8 = 0; /// Vhost supports `VHOST_USER_SET_CONFIG` and `VHOST_USER_GET_CONFIG` msg. pub const VHOST_USER_PROTOCOL_F_CONFIG: u8 = 9; /// Vhost supports `VHOST_USER_SET_INFLIGHT_FD` and `VHOST_USER_GET_INFLIGHT_FD` msg. -const VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD: u8 = 12; +pub const VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD: u8 = 12; struct ClientInternal { // Used to send requests to the vhost user backend in userspace. @@ -100,7 +100,8 @@ fn vhost_user_reconnect(client: &Arc>) { vhost_user_reconnect(&cloned_client); }); - info!("Try to reconnect vhost-user net."); + let dev_type = client.lock().unwrap().backend_type.to_string(); + info!("Try to reconnect vhost-user {}.", dev_type); if let Err(_e) = client .lock() .unwrap() @@ -121,9 +122,22 @@ fn vhost_user_reconnect(client: &Arc>) { client.lock().unwrap().reconnecting = false; if let Err(e) = VhostUserClient::add_event(client) { error!("Failed to update event for client sock, {:?}", e); + return; + } + + let mut locked_client = client.lock().unwrap(); + let protocol_features = locked_client.protocol_features; + if protocol_features != 0 { + if let Err(e) = locked_client.set_protocol_features(protocol_features) { + error!( + "Failed to set protocol features for vhost-user {}, {:?}", + dev_type, e + ); + return; + } } - if let Err(e) = client.lock().unwrap().activate_vhost_user() { + if let Err(e) = locked_client.activate_vhost_user() { error!("Failed to reactivate vhost-user net, {:?}", e); } else { info!("Reconnecting vhost-user net succeed."); @@ -350,6 +364,16 @@ pub enum VhostBackendType { TypeFs, } +impl ToString for VhostBackendType { + fn to_string(&self) -> String { + match self { + VhostBackendType::TypeNet => String::from("net"), + VhostBackendType::TypeBlock => String::from("block"), + VhostBackendType::TypeFs => String::from("fs"), + } + } +} + /// Struct for communication with the vhost user backend in userspace pub struct VhostUserClient { client: Arc>, @@ -363,6 +387,7 @@ pub struct VhostUserClient { reconnecting: bool, inflight: Option, backend_type: VhostBackendType, + pub protocol_features: u64, } impl VhostUserClient { @@ -398,6 +423,7 @@ impl VhostUserClient { reconnecting: false, inflight: None, backend_type, + protocol_features: 0_u64, }) } @@ -501,8 +527,12 @@ impl VhostUserClient { for (queue_index, queue_mutex) in self.queues.iter().enumerate() { let queue = queue_mutex.lock().unwrap(); - let queue_config = queue.vring.get_queue_config(); + if !queue.vring.is_enabled() { + warn!("Queue {} is not enabled, skip it", queue_index); + continue; + } + let queue_config = queue.vring.get_queue_config(); self.set_vring_addr(&queue_config, queue_index, 0) .with_context(|| { format!( @@ -510,11 +540,9 @@ impl VhostUserClient { queue_index, ) })?; - let last_avail_idx = if queue.vring.is_enabled() { - queue.vring.get_avail_idx(&self.mem_space)? - } else { - 0 - }; + // When spdk/ovs has been killed, stratovirt can not get the last avail + // index in spdk/ovs, it can only use used index as last avail index. + let last_avail_idx = queue.vring.get_used_idx(&self.mem_space)?; self.set_vring_base(queue_index, last_avail_idx) .with_context(|| { format!( @@ -542,15 +570,16 @@ impl VhostUserClient { // If VHOST_USER_F_PROTOCOL_FEATURES has been negotiated, it should call // set_vring_enable to enable vring. Otherwise, the ring is enabled by default. // Currently, only vhost-user-blk device support negotiate VHOST_USER_F_PROTOCOL_FEATURES. - for (queue_index, queue) in self.queues.iter().enumerate() { - let enabled = queue.lock().unwrap().is_enabled(); - self.set_vring_enable(queue_index, enabled) - .with_context(|| { - format!( - "Failed to set vring enable for vhost-user, index: {}", - queue_index, - ) - })?; + for (queue_index, queue_mutex) in self.queues.iter().enumerate() { + if !queue_mutex.lock().unwrap().is_enabled() { + continue; + } + self.set_vring_enable(queue_index, true).with_context(|| { + format!( + "Failed to set vring enable for vhost-user, index: {}", + queue_index, + ) + })?; } } @@ -558,7 +587,10 @@ impl VhostUserClient { } pub fn reset_vhost_user(&mut self) -> Result<()> { - for (queue_index, _) in self.queues.iter().enumerate() { + for (queue_index, queue_mutex) in self.queues.iter().enumerate() { + if !queue_mutex.lock().unwrap().vring.is_enabled() { + continue; + } self.set_vring_enable(queue_index, false) .with_context(|| format!("Failed to set vring disable, index: {}", queue_index))?; self.get_vring_base(queue_index) -- Gitee From 216d83d9d68258fc751881be80d128e104ea6457 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Fri, 22 Sep 2023 11:30:47 +0800 Subject: [PATCH 1390/2187] vhost: delete invalid interrupt notify Just send the required interrupt to kvm, delete the invalid interrupt notify. Signed-off-by: Yan Wang --- virtio/src/vhost/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtio/src/vhost/mod.rs b/virtio/src/vhost/mod.rs index 1d7458641..8f78ef666 100644 --- a/virtio/src/vhost/mod.rs +++ b/virtio/src/vhost/mod.rs @@ -127,6 +127,9 @@ impl EventNotifierHelper for VhostIoHandler { return None; } for host_notify in locked_vhost_handler.host_notifies.iter() { + if host_notify.notify_evt.as_raw_fd() != fd { + continue; + } if let Err(e) = (locked_vhost_handler.interrupt_cb)( &VirtioInterruptType::Vring, Some(&host_notify.queue.lock().unwrap()), -- Gitee From 7eab4dc3c96a71020d045bf591dfa0a788ed7637 Mon Sep 17 00:00:00 2001 From: prostous Date: Thu, 28 Sep 2023 13:48:29 +0300 Subject: [PATCH 1391/2187] machine: acpi dbg2 table added for aarch64 Created and registered DBG2 table in ACPI for aarch64 standard vm to make available debugging Windows OS with WinDbg utility. Signed-off-by: prostous --- machine/src/standard_vm/aarch64/mod.rs | 83 ++++++++++++++++++++++++++ machine/src/standard_vm/mod.rs | 23 +++++++ 2 files changed, 106 insertions(+) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index e67e88723..307fbdd04 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -878,6 +878,89 @@ impl AcpiBuilder for StdMachine { Ok(gtdt_begin) } + fn build_dbg2_table( + &self, + acpi_data: &Arc>>, + loader: &mut TableLoader, + ) -> super::Result { + // Table format described at: + // https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/acpi-debug-port-table + + let dev_name = "COM0"; + let dev_name_length = dev_name.len() + 1; + + let dbg2_table_size = 82 // Fixed size part of table + + dev_name_length; + + let dbg2_info_size = 22 // BaseAddressRegister offset + + 12 // BaseAddressRegister + + 4 // AddressSize + + dev_name_length; + + let mut dbg2 = AcpiTable::new(*b"DBG2", 0, *b"STRATO", *b"VIRTDBG2", 1); + dbg2.set_table_len(dbg2_table_size); + + // Table 1. Debug Port Table 2 format + // OffsetDbgDeviceInfo + dbg2.set_field(36, 44_u32); + // NumberDbgDeviceInfo + dbg2.set_field(40, 1_u32); + + // Table 2. Debug Device Information structure format + let offset = 44; + // Revision + dbg2.set_field(offset, 0_u8); + // Length + dbg2.set_field(offset + 1, dbg2_info_size as u16); + // NumberofGenericAddressRegisters + dbg2.set_field(offset + 3, 1_u8); + // NamespaceStringLength + dbg2.set_field(offset + 4, dev_name_length as u16); + // NamespaceStringOffset + dbg2.set_field(offset + 6, 38_u16); + // OemDataLength + dbg2.set_field(offset + 8, 0_u16); + // OemDataOffset + dbg2.set_field(offset + 10, 0_u16); + // Port Type: 0x8000 is serial + dbg2.set_field(offset + 12, 0x8000_u16); + // Port Subtype: 0x3 is ARM PL011 UART + dbg2.set_field(offset + 14, 0x3_u16); + + // BaseAddressRegisterOffset + dbg2.set_field(offset + 18, 22_u16); + // AddressSizeOffset + dbg2.set_field(offset + 20, 34_u16); + + let uart_memory_address = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; + let uart_memory_size = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; + + // BaseAddressRegister: aml address space + dbg2.set_field(offset + 22, 0_u8); + // BaseAddressRegister: bit width + dbg2.set_field(offset + 23, 8_u8); + // BaseAddressRegister: bit offset + dbg2.set_field(offset + 24, 0_u8); + // BaseAddressRegister: access width + dbg2.set_field(offset + 25, 1_u8); + // BaseAddressRegister: address + dbg2.set_field(offset + 26, uart_memory_address as u64); + // AddressSize + dbg2.set_field(offset + 34, uart_memory_size as u32); + + // NamespaceString + let mut offset = offset + 38; + for ch in dev_name.chars() { + dbg2.set_field(offset, ch as u8); + offset += 1; + } + dbg2.set_field(offset, 0_u8); + + let dbg2_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dbg2) + .with_context(|| "Fail to add DBG2 table to loader")?; + Ok(dbg2_begin) + } + fn build_iort_table( &self, acpi_data: &Arc>>, diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 006c10f7d..ce845043e 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -129,6 +129,11 @@ trait StdMachineOps: AcpiBuilder { .with_context(|| "Failed to build ACPI GTDT table")?; xsdt_entries.push(gtdt_addr); + let dbg2_addr = self + .build_dbg2_table(&acpi_tables, &mut loader) + .with_context(|| "Failed to build ACPI DBG2 table")?; + xsdt_entries.push(dbg2_addr); + let iort_addr = self .build_iort_table(&acpi_tables, &mut loader) .with_context(|| "Failed to build ACPI IORT table")?; @@ -386,6 +391,24 @@ trait AcpiBuilder { Ok(0) } + /// Build ACPI DBG2 table, returns the offset of ACPI DBG2 table in `acpi_data`. + /// + /// # Arguments + /// + /// `acpi_data` - Bytes streams that ACPI tables converts to. + /// `loader` - ACPI table loader. + #[cfg(target_arch = "aarch64")] + fn build_dbg2_table( + &self, + _acpi_data: &Arc>>, + _loader: &mut TableLoader, + ) -> Result + where + Self: Sized, + { + bail!("Not implemented"); + } + /// Build ACPI IORT table, returns the offset of ACPI IORT table in `acpi_data`. /// /// # Arguments -- Gitee From b4c004efcee1335620a9584bb364943c065cde04 Mon Sep 17 00:00:00 2001 From: zhangdianndian0127 <1635468471@qq.com> Date: Sun, 8 Oct 2023 02:15:16 +0800 Subject: [PATCH 1392/2187] Fix some spelling error Signed-off-by: zhangdianndian0127 <1635468471@qq.com> --- address_space/src/region.rs | 2 +- devices/src/usb/xhci/xhci_controller.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 0eb7fdb0b..6651ca83d 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1184,7 +1184,7 @@ mod test { assert_eq!(&slice, &mut res_slice); // write the file content to 0~24 (24 not included) - // then ckeck the ram's content + // then check the ram's content file_read.seek(SeekFrom::Start(0)).unwrap(); assert!(ram_region.write(&mut file_read, rgn_start, 0, 24).is_ok()); ram_region diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a706fb619..a49813d99 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1087,7 +1087,7 @@ impl XhciDevice { dma_read_u32( &self.mem_space, GuestAddress( - // It is safe to plus here becuase we previously verify the address. + // It is safe to plus here because we previously verify the address. ictx + SLOT_INPUT_CTX_OFFSET, ), slot_ctx.as_mut_dwords(), @@ -1298,7 +1298,7 @@ impl XhciDevice { dma_read_u32( &self.mem_space, GuestAddress( - // It is safe to plus here becuase we previously verify the address. + // It is safe to plus here because we previously verify the address. ictx + SLOT_INPUT_CTX_OFFSET, ), islot_ctx.as_mut_dwords(), @@ -1319,7 +1319,7 @@ impl XhciDevice { dma_read_u32( &self.mem_space, GuestAddress( - // It is safe to use plus here becuase we previously verify the address. + // It is safe to use plus here because we previously verify the address. ictx + EP_INPUT_CTX_OFFSET, ), iep_ctx.as_mut_dwords(), @@ -1328,7 +1328,7 @@ impl XhciDevice { dma_read_u32( &self.mem_space, GuestAddress( - // It is safe to use plus here becuase we previously verify the address. + // It is safe to use plus here because we previously verify the address. octx + EP_CTX_OFFSET, ), ep_ctx.as_mut_dwords(), @@ -1336,7 +1336,7 @@ impl XhciDevice { ep_ctx.set_max_packet_size(iep_ctx.get_max_packet_size()); dma_write_u32( &self.mem_space, - // It is safe to use plus here becuase we previously verify the address. + // It is safe to use plus here because we previously verify the address. GuestAddress(octx + EP_CTX_OFFSET), ep_ctx.as_dwords(), )?; @@ -1381,7 +1381,7 @@ impl XhciDevice { let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, - // It is safe to use plus here becuase we previously verify the address on the outer + // It is safe to use plus here because we previously verify the address on the outer // layer. GuestAddress(input_ctx + EP_INPUT_CTX_OFFSET + entry_offset), ep_ctx.as_mut_dwords(), @@ -1390,14 +1390,14 @@ impl XhciDevice { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; epctx.epid = ep_id; epctx.enabled = true; - // It is safe to use plus here becuase we previously verify the address on the outer layer. + // It is safe to use plus here because we previously verify the address on the outer layer. epctx.init_ctx(output_ctx + EP_CTX_OFFSET + entry_offset, &ep_ctx); epctx.set_ep_state(EP_RUNNING); ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= EP_RUNNING; dma_write_u32( &self.mem_space, - // It is safe to use plus here becuase we previously verify the address on the outer + // It is safe to use plus here because we previously verify the address on the outer // layer. GuestAddress(output_ctx + EP_CTX_OFFSET + entry_offset), ep_ctx.as_dwords(), -- Gitee From 7856598c5000defac87ebe2a1f76db38d514d20f Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 8 Oct 2023 15:31:27 +0800 Subject: [PATCH 1393/2187] mst: add test for dbg2 dbg2 has been added to the arm acpi table, thereforce the acpi_test case needs to be adapted. Signed-off-by: Mingwang Li --- tests/mod_test/tests/acpi_test.rs | 54 +++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/acpi_test.rs index 226deb406..174d82128 100644 --- a/tests/mod_test/tests/acpi_test.rs +++ b/tests/mod_test/tests/acpi_test.rs @@ -32,16 +32,32 @@ const FADT_TABLE_DATA_LENGTH: u32 = 276; const MADT_TABLE_DATA_LENGTH: u32 = 744; // Now gtdt table data length is 96. const GTDT_TABLE_DATA_LENGTH: u32 = 96; +// Now dbg2 table data length is 87. +const DBG2_TABLE_DATA_LENGTH: u32 = 87; // Now iort table data length is 128. const IORT_TABLE_DATA_LENGTH: u32 = 128; // Now spcr table data length is 80. const SPCR_TABLE_DATA_LENGTH: u32 = 80; // Now mcfg table data length is 60. const MCFG_TABLE_DATA_LENGTH: u32 = 60; -// Now acpi tables data length is 5974(cpu number is 8). -const ACPI_TABLES_DATA_LENGTH_8: usize = 5974; -// Now acpi tables data length is 40415(cpu number is 200). -const ACPI_TABLES_DATA_LENGTH_200: usize = 40415; +// Now acpi tables data length is 6069(cpu number is 8). +const ACPI_TABLES_DATA_LENGTH_8: usize = 6069; +// Now acpi tables data length is 40510(cpu number is 200). +const ACPI_TABLES_DATA_LENGTH_200: usize = 40510; + +enum TABLE { + Dsdt, + Fadt, + Madt, + Gtdt, + Dbg2, + Iort, + Spcr, + Mcfg, + Srat, + Slit, + Pptt, +} fn test_rsdp(test_state: &TestState, alloc: &mut GuestAllocator) -> u64 { let file_name = "etc/acpi/rsdp"; @@ -140,6 +156,11 @@ fn check_gtdt(data: &[u8]) { assert_eq!(LittleEndian::read_u32(&data[76..]), 0); // Non secure EL2 flags } +fn check_dbg2(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "DBG2"); + assert_eq!(LittleEndian::read_u32(&data[4..]), DBG2_TABLE_DATA_LENGTH); // Check length +} + fn check_iort(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "IORT"); assert_eq!(LittleEndian::read_u32(&data[4..]), IORT_TABLE_DATA_LENGTH); // Check length @@ -326,52 +347,57 @@ fn test_tables(test_state: &TestState, alloc: &mut GuestAllocator, xsdt_addr: us let entry_addr = xsdt_addr + mem::size_of::() - 8; // Check DSDT - let mut offset = entry_addr; + let mut offset = entry_addr + TABLE::Dsdt as usize * 8; let dsdt_addr = LittleEndian::read_u64(&read_data[offset..]); check_dsdt(&read_data[(dsdt_addr as usize)..]); // Check FADT - offset = entry_addr + 1 * 8; + offset = entry_addr + TABLE::Fadt as usize * 8; let fadt_addr = LittleEndian::read_u64(&read_data[offset..]); check_fadt(&read_data[(fadt_addr as usize)..]); // Check MADT - offset = entry_addr + 2 * 8; + offset = entry_addr + TABLE::Madt as usize * 8; let madt_addr = LittleEndian::read_u64(&read_data[offset..]); check_madt(&read_data[(madt_addr as usize)..], cpu); // Check GTDT - offset = entry_addr + 3 * 8; + offset = entry_addr + TABLE::Gtdt as usize * 8; let gtdt_addr = LittleEndian::read_u64(&read_data[offset..]); check_gtdt(&read_data[(gtdt_addr as usize)..]); + // Check DBG2 + offset = entry_addr + TABLE::Dbg2 as usize * 8; + let gtdt_addr = LittleEndian::read_u64(&read_data[offset..]); + check_dbg2(&read_data[(gtdt_addr as usize)..]); + // Check IORT - offset = entry_addr + 4 * 8; + offset = entry_addr + TABLE::Iort as usize * 8; let iort_addr = LittleEndian::read_u64(&read_data[offset..]); check_iort(&read_data[(iort_addr as usize)..]); // Check SPCR - offset = entry_addr + 5 * 8; + offset = entry_addr + TABLE::Spcr as usize * 8; let spcr_addr = LittleEndian::read_u64(&read_data[offset..]); check_spcr(&read_data[(spcr_addr as usize)..]); // Check MCFG - offset = entry_addr + 6 * 8; + offset = entry_addr + TABLE::Mcfg as usize * 8; let mcfg_addr = LittleEndian::read_u64(&read_data[offset..]); check_mcfg(&read_data[(mcfg_addr as usize)..]); // Check SRAT - offset = entry_addr + 7 * 8; + offset = entry_addr + TABLE::Srat as usize * 8; let srat_addr = LittleEndian::read_u64(&read_data[offset..]); check_srat(&read_data[(srat_addr as usize)..]); // Check SLIT - offset = entry_addr + 8 * 8; + offset = entry_addr + TABLE::Slit as usize * 8; let slit_addr = LittleEndian::read_u64(&read_data[offset..]); check_slit(&read_data[(slit_addr as usize)..]); // Check PPTT - offset = entry_addr + 9 * 8; + offset = entry_addr + TABLE::Pptt as usize * 8; let pptt_addr = LittleEndian::read_u64(&read_data[offset..]); check_pptt(&read_data[(pptt_addr as usize)..]); } -- Gitee From b115bab8d50fce627a30523e04c9d3422647e981 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 8 Oct 2023 09:40:58 +0800 Subject: [PATCH 1394/2187] qmp: fix docs and logs typos Refer to the qmp document to modify the arrow direction of client and server information display: -> data issued by the Client <- Server data response https://www.qemu.org/docs/master/interop/qmp-spec.html Signed-off-by: yezengruan --- docs/migration.md | 18 ++--- docs/qmp.md | 96 +++++++++++++-------------- docs/vfio.md | 10 +-- machine_manager/src/qmp/qmp_socket.rs | 6 +- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/docs/migration.md b/docs/migration.md index f47d8d13a..68e8fa7b7 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -54,9 +54,9 @@ Note: Start to send migration for the source VM: ```shell $ ncat -U path/to/socket1 --> {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -<- {"execute":"migrate", "arguments":{"uri":"tcp:192.168.0.1:4446"}} --> {"return":{}} +<- {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +-> {"execute":"migrate", "arguments":{"uri":"tcp:192.168.0.1:4446"}} +<- {"return":{}} ``` Note: @@ -71,9 +71,9 @@ migrated to the destination VM. If you want to cancel the live migration, executing the following command: ```shell $ ncat -U path/to/socket1 --> {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -<- {"execute":"migrate_cancel"} --> {"return":{}} +<- {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +-> {"execute":"migrate_cancel"} +<- {"return":{}} ``` ## Query migration state @@ -81,9 +81,9 @@ $ ncat -U path/to/socket1 Use QMP command `query-migrate` to check migration state: ```shell $ ncat -U path/to/socket --> {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -<- {"execute":"query-migrate"} --> {"return":{"status":"completed"}} +<- {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +-> {"execute":"query-migrate"} +<- {"return":{"status":"completed"}} ``` Now there are 5 states during migration: diff --git a/docs/qmp.md b/docs/qmp.md index d27cf044a..254247938 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -81,8 +81,8 @@ Add a block backend. #### Example ```json -<- {"execute": "blockdev-add", "arguments": {"node-name": "drive-0", "file": {"driver": "file", "filename": "/path/to/block", "aio": native}, "cache": {"direct": true}, "read-only": false}} --> {"return": {}} +-> {"execute": "blockdev-add", "arguments": {"node-name": "drive-0", "file": {"driver": "file", "filename": "/path/to/block", "aio": native}, "cache": {"direct": true}, "read-only": false}} +<- {"return": {}} ``` ### blockdev-del @@ -96,8 +96,8 @@ Remove a block backend. #### Example ```json -<- {"execute": "blockdev-del", "arguments": {"node-name": "drive-0"}} --> {"return": {}} +-> {"execute": "blockdev-del", "arguments": {"node-name": "drive-0"}} +<- {"return": {}} ``` ## Net device backend management @@ -131,8 +131,8 @@ Add a network backend. #### Example ```json -<- {"execute":"netdev_add", "arguments":{"id":"net-0", "ifname":"tap0"}} --> {"return": {}} +-> {"execute":"netdev_add", "arguments":{"id":"net-0", "ifname":"tap0"}} +<- {"return": {}} ``` ### netdev_del @@ -146,8 +146,8 @@ Remove a network backend. #### Example ```json -<- {"execute": "netdev_del", "arguments": {"id": "net-0"}} --> {"return": {}} +-> {"execute": "netdev_del", "arguments": {"id": "net-0"}} +<- {"return": {}} ``` ## Camera device backend management @@ -169,8 +169,8 @@ Add a camera backend. #### Example ```json -<- {"execute":"cameradev_add", "arguments":{"id":"cam-0", "driver": "v4l2", "path":"/dev/video0"}} --> {"return": {}} +-> {"execute":"cameradev_add", "arguments":{"id":"cam-0", "driver": "v4l2", "path":"/dev/video0"}} +<- {"return": {}} ``` ### cameradev_del @@ -188,8 +188,8 @@ Remove a camera backend. #### Example ```json -<- {"execute": "cameradev_del", "arguments": {"id": "cam-0"}} --> {"return": {}} +-> {"execute": "cameradev_del", "arguments": {"id": "cam-0"}} +<- {"return": {}} ``` ## Character device backend management @@ -214,8 +214,8 @@ Add a character device backend. #### Example ```json -<- {"execute":"chardev-add", "arguments": {"id": "chardev_id", "backend": {"type": "socket", "data": {"addr": {"type": "unix", "data": {"path": "/path/to/socket"}}, "server": false}}}} --> {"return": {}} +-> {"execute":"chardev-add", "arguments": {"id": "chardev_id", "backend": {"type": "socket", "data": {"addr": {"type": "unix", "data": {"path": "/path/to/socket"}}, "server": false}}}} +<- {"return": {}} ``` ### chardev-remove @@ -229,8 +229,8 @@ Remove a character device backend. #### Example ```json -<- {"execute": "chardev-remove", "arguments": {"id": "chardev_id"}} --> {"return": {}} +-> {"execute": "chardev-remove", "arguments": {"id": "chardev_id"}} +<- {"return": {}} ``` ## Hot plug management @@ -266,8 +266,8 @@ Add a device. #### Example ```json -<- {"execute":"device_add", "arguments":{"id":"net-0", "driver":"virtio-net-mmio", "addr":"0x0"}} --> {"return": {}} +-> {"execute":"device_add", "arguments":{"id":"net-0", "driver":"virtio-net-mmio", "addr":"0x0"}} +<- {"return": {}} ``` ### device_del @@ -285,9 +285,9 @@ Remove a device from a guest. #### Example ```json -<- {"execute": "device_del", "arguments": {"id": "net-0"}} --> {"event":"DEVICE_DELETED","data":{"device":"net-0","path":"net-0"},"timestamp":{"seconds":1614310541,"microseconds":554250}} --> {"return": {}} +-> {"execute": "device_del", "arguments": {"id": "net-0"}} +<- {"event":"DEVICE_DELETED","data":{"device":"net-0","path":"net-0"},"timestamp":{"seconds":1614310541,"microseconds":554250}} +<- {"return": {}} ``` ## Lifecycle Management @@ -302,9 +302,9 @@ Stop all guest VCPUs execution. #### Example ```json -<- {"execute":"stop"} --> {"event":"STOP","data":{},"timestamp":{"seconds":1583908726,"microseconds":162739}} --> {"return":{}} +-> {"execute":"stop"} +<- {"event":"STOP","data":{},"timestamp":{"seconds":1583908726,"microseconds":162739}} +<- {"return":{}} ``` ### cont @@ -314,9 +314,9 @@ Resume all guest VCPUs execution. #### Example ```json -<- {"execute":"cont"} --> {"event":"RESUME","data":{},"timestamp":{"seconds":1583908853,"microseconds":411394}} --> {"return":{}} +-> {"execute":"cont"} +<- {"event":"RESUME","data":{},"timestamp":{"seconds":1583908853,"microseconds":411394}} +<- {"return":{}} ``` ### system_reset @@ -326,9 +326,9 @@ Reset all guest VCPUs execution. #### Example ```json -<- {"execute":"system_reset"} --> {"return":{}} --> {"event":"RESET","data":{"guest":true},"timestamp":{"seconds":1677381086,"microseconds":432033}} +-> {"execute":"system_reset"} +<- {"return":{}} +<- {"event":"RESET","data":{"guest":true},"timestamp":{"seconds":1677381086,"microseconds":432033}} ``` ### system_powerdown @@ -338,9 +338,9 @@ Requests that a guest perform a powerdown operation. ### Example ```json -<- {"execute":"system_powerdown"} --> {"return":{}} --> {"event":"POWERDOWN","data":{},"timestamp":{"seconds":1677850193,"microseconds":617907}} +-> {"execute":"system_powerdown"} +<- {"return":{}} +<- {"event":"POWERDOWN","data":{},"timestamp":{"seconds":1677850193,"microseconds":617907}} ``` ### quit @@ -350,9 +350,9 @@ This command will cause StratoVirt process to exit gracefully. #### Example ```json -<- {"execute":"quit"} --> {"return":{}} --> {"event":"SHUTDOWN","data":{"guest":false,"reason":"host-qmp-quit"},"timestamp":{"ds":1590563776,"microseconds":519808}} +-> {"execute":"quit"} +<- {"return":{}} +<- {"event":"SHUTDOWN","data":{"guest":false,"reason":"host-qmp-quit"},"timestamp":{"ds":1590563776,"microseconds":519808}} ``` ### query-status @@ -362,8 +362,8 @@ Query the running status of all VCPUs. #### Example ```json -<- { "execute": "query-status" } --> { "return": { "running": true,"singlestep": false,"status": "running" } } +-> { "execute": "query-status" } +<- { "return": { "running": true,"singlestep": false,"status": "running" } } ``` ### getfd @@ -373,8 +373,8 @@ Receive a file descriptor via SCM rights and assign it a name. #### Example ```json -<- { "execute": "getfd", "arguments": { "fdname": "fd1" } } --> { "return": {} } +-> { "execute": "getfd", "arguments": { "fdname": "fd1" } } +<- { "return": {} } ``` ## balloon @@ -392,8 +392,8 @@ Set target memory size of guest. #### Example ```json -<- { "execute": "balloon", "arguments": { "value": 2147483648 } } --> {"return":{}} +-> { "execute": "balloon", "arguments": { "value": 2147483648 } } +<- {"return":{}} ``` ### query-balloon @@ -403,8 +403,8 @@ Get memory size of guest. #### Example ```json -<- { "execute": "query-balloon" } --> {"return":{"actual":2147483648}} +-> { "execute": "query-balloon" } +<- {"return":{"actual":2147483648}} ``` ## Migration @@ -420,8 +420,8 @@ Take a snapshot of the VM into the specified directory. #### Example ```json -<- {"execute":"migrate", "arguments":{"uri":"file:path/to/template"}} --> {"return":{}} +-> {"execute":"migrate", "arguments":{"uri":"file:path/to/template"}} +<- {"return":{}} ``` ### query-migrate @@ -441,8 +441,8 @@ Now there are 5 states during snapshot: #### Example ```json -<- {"execute":"query-migrate"} --> {"return":{"status":"completed"}} +-> {"execute":"query-migrate"} +<- {"return":{"status":"completed"}} ``` ## Event Notification diff --git a/docs/vfio.md b/docs/vfio.md index 227ca7159..0316e6ab7 100644 --- a/docs/vfio.md +++ b/docs/vfio.md @@ -60,14 +60,14 @@ Refer to qmp.md for specific command line parameters. hot plug VFIO device: ```json -<- {"execute":"device_add", "arguments":{"id":"vfio-0", "driver":"vfio-pci", "bus": "pcie.1", "addr":"0x0", "host": "0000:1a:00.3"}} --> {"return": {}} +-> {"execute":"device_add", "arguments":{"id":"vfio-0", "driver":"vfio-pci", "bus": "pcie.1", "addr":"0x0", "host": "0000:1a:00.3"}} +<- {"return": {}} ``` hot unplug VFIO device: ```json -<- {"execute": "device_del", "arguments": {"id": "vfio-0"}} --> {"event":"DEVICE_DELETED","data":{"device":"vfio-0","path":"vfio-0"},"timestamp":{"seconds":1614310541,"microseconds":554250}} --> {"return": {}} +-> {"execute": "device_del", "arguments": {"id": "vfio-0"}} +<- {"event":"DEVICE_DELETED","data":{"device":"vfio-0","path":"vfio-0"},"timestamp":{"seconds":1614310541,"microseconds":554250}} +<- {"return": {}} ``` ## Unbind VFIO device diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index bd140bf32..1b2215e8b 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -188,7 +188,7 @@ impl Socket { serde_json::to_string(&Response::create_empty_response()).unwrap() }; handler.send_str(&resp)?; - info!("QMP: --> {:?}", resp); + info!("QMP: <-- {:?}", resp); } Ok(()) } @@ -373,10 +373,10 @@ fn handle_qmp( match qmp_service.decode_line() { (Ok(None), _) => Ok(()), (Ok(buffer), if_fd) => { - info!("QMP: <-- {:?}", buffer); + info!("QMP: --> {:?}", buffer); let qmp_command: QmpCommand = buffer.unwrap(); let (return_msg, shutdown_flag) = qmp_command_exec(qmp_command, controller, if_fd); - info!("QMP: --> {:?}", return_msg); + info!("QMP: <-- {:?}", return_msg); qmp_service.send_str(&return_msg)?; // handle shutdown command -- Gitee From e912b735df6b3bb978a8e588b52cf81ada5c709b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 8 Oct 2023 09:55:21 +0800 Subject: [PATCH 1395/2187] docs: update build guide Signed-off-by: yezengruan --- docs/build_guide.ch.md | 8 ++++---- docs/build_guide.md | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index bdb248108..f022c364e 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -4,11 +4,11 @@ ## 1. 检查Rust构建环境 为了构建StratoVirt,需保证已经安装了Rust语言环境和Cargo软件。 -rustc的推荐版本为1.51.0及其之后的版本, 否则编译可能失败。 +rustc的推荐版本为1.64.0及其之后的版本, 否则编译可能失败。 ```shell $ rustc --version -rustc 1.51.0 +rustc 1.64.0 ``` 如果你想部署rust环境,下面的链接可以帮助你: @@ -25,7 +25,7 @@ $ arch=`uname -m` $ rustup target add ${arch}-unknown-linux-gnu # 构建StratoVirt -$ cargo build --release --target ${arch}-unknown-linux-gnu +$ cargo build --workspace --bins --release --target ${arch}-unknown-linux-gnu ``` 现在你可找到StratoVirt二进制的路径在 `target/${arch}-unknown-linux-gnu/release/stratovirt`. @@ -40,7 +40,7 @@ $ arch=`uname -m` $ rustup target add ${arch}-unknown-linux-musl # 构建StratoVirt -$ cargo build --release --target ${arch}-unknown-linux-musl +$ cargo build --workspace --bins --release --target ${arch}-unknown-linux-musl ``` 现在你可找到StratoVirt静态链接二进制的路径在 `target/${arch}-unknown-linux-musl/release/stratovirt`. diff --git a/docs/build_guide.md b/docs/build_guide.md index 816cfd21d..9932ef6ef 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -4,11 +4,11 @@ ## 1. Check Rust environment To build StratoVirt, make sure that Rust language environment and Cargo have already been installed. -The recommended version of rustc is 1.51.0 or later, otherwise compilation may be failed. +The recommended version of rustc is 1.64.0 or later, otherwise compilation may be failed. ```shell $ rustc --version -rustc 1.51.0 +rustc 1.64.0 ``` If you want to deploy rust environment, the following link will help you: @@ -25,7 +25,7 @@ $ arch=`uname -m` $ rustup target add ${arch}-unknown-linux-gnu # Build StratoVirt -$ cargo build --release --target ${arch}-unknown-linux-gnu +$ cargo build --workspace --bins --release --target ${arch}-unknown-linux-gnu ``` Now you can find StratoVirt binary file in `target/${arch}-unknown-linux-gnu/release/stratovirt`. @@ -41,7 +41,7 @@ $ arch=`uname -m` $ rustup target add ${arch}-unknown-linux-musl # Build StratoVirt -$ cargo build --release --target ${arch}-unknown-linux-musl +$ cargo build --workspace --bins --release --target ${arch}-unknown-linux-musl ``` Now you can find StratoVirt static binary file in `target/${arch}-unknown-linux-musl/release/stratovirt`. @@ -62,7 +62,7 @@ List of optional features: - virtio_gpu: enable virtio-gpu virtualized graphics card ```shell -$ cargo build --release --features "scream_alsa" +$ cargo build --workspace --bins --release --features "scream_alsa" ``` # Build static StratoVirt in containers -- Gitee From 124481cdcae7f63dd10d9c6e73fb1c6e955ba154 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Oct 2023 16:57:47 +0800 Subject: [PATCH 1396/2187] fix some clippy warnings of newer rust version Signed-off-by: yezengruan --- block_backend/src/qcow2/check.rs | 18 ++++++++---------- block_backend/src/qcow2/mod.rs | 2 +- block_backend/src/qcow2/refcount.rs | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index 2cc6cf9be..160e73403 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -150,7 +150,7 @@ impl RefcountBlock { let mut cluster_idx = *first_free_cluster; let mut continue_clusters: usize = 0; while continue_clusters < total_counts as usize { - if self.get_refcount(cluster_idx as usize)? == 0 { + if self.get_refcount(cluster_idx)? == 0 { continue_clusters += 1; if first_update { *first_free_cluster = cluster_idx; @@ -468,7 +468,7 @@ impl Qcow2Driver { let l1_table = self .sync_aio .borrow_mut() - .read_ctrl_cluster(l1_offset, l1_size as u64)?; + .read_ctrl_cluster(l1_offset, l1_size)?; // Entry in l1 table for idx in 0..l1_size { @@ -670,10 +670,8 @@ impl Qcow2Driver { "ERROR".to_string() }; - let reftable_size = self.refcount.refcount_table.len(); let reftable = self.refcount.refcount_table.clone(); - for idx in 0..reftable_size { - let reftable_entry = reftable[idx as usize]; + for (idx, reftable_entry) in reftable.iter().enumerate() { let refblock_offset = reftable_entry & REFCOUNT_TABLE_OFFSET_MASK; let cluster_idx = refblock_offset >> cluster_bits; if reftable_entry & REFCOUNT_TABLE_RESERVED_MASK != 0 { @@ -846,7 +844,7 @@ impl Qcow2Driver { }; for l1_idx in 0..l1_size { - let l1_entry = self.table.l1_table[l1_idx as usize]; + let l1_entry = self.table.l1_table[l1_idx]; let l2_offset = l1_entry & L1_TABLE_OFFSET_MASK; if l2_offset == 0 { continue; @@ -868,7 +866,7 @@ impl Qcow2Driver { } else { l1_entry & !QCOW2_OFFSET_COPIED }; - self.table.l1_table[l1_idx as usize] = new_l1_entry; + self.table.l1_table[l1_idx] = new_l1_entry; l1_dirty = true; l1_corruptions -= 1; l1_corruptions_fixed += 1; @@ -966,7 +964,7 @@ impl Qcow2Driver { let mut new_reftable: Vec = Vec::new(); let mut reftable_clusters: u64 = 0; let cluster_bits = self.header.cluster_bits as u64; - let refblock_bits: u64 = (cluster_bits + 3 - self.header.refcount_order as u64) as u64; + let refblock_bits: u64 = cluster_bits + 3 - self.header.refcount_order as u64; let refblock_size: u64 = 1 << refblock_bits; // self.refblock.nb_clusters means the maximum number of clusters that can be represented by @@ -991,7 +989,7 @@ impl Qcow2Driver { )?; // Extend the refcount table - if new_reftable.len() <= refblock_idx as usize { + if new_reftable.len() <= refblock_idx { new_reftable.resize(refblock_idx + 1, 0); // Need to reallocate clusters for new refcount table. reftable_offset = 0; @@ -1007,7 +1005,7 @@ impl Qcow2Driver { bytes_to_clusters(reftable_size * ENTRY_SIZE, self.header.cluster_size())?; reftable_offset = check.refblock.alloc_clusters( reftable_clusters, - cluster_bits as u64, + cluster_bits, &mut (first_free_cluster as usize), self.sync_aio.clone(), )?; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index bb8eb4fa9..16623852b 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1560,7 +1560,7 @@ impl BlockDriverOps for Qcow2Driver { self.driver.file.seek(SeekFrom::Start(offset))?; self.driver.file.write_all(&zero_buf.to_vec())? } - self.driver.file.seek(SeekFrom::Start(0))?; + self.driver.file.rewind()?; self.driver.file.write_all(&self.header.to_vec())?; // Refcount table. diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 6c0406b7a..9eeb686e7 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -287,7 +287,7 @@ impl RefCount { let start_clusters = div_round_up(offset, self.cluster_size).unwrap(); for i in start_clusters..start_clusters + clusters { let rt_idx = i >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size as u64 { + if rt_idx >= self.refcount_table_size { bail!("Invalid refcount table index {}", rt_idx); } @@ -453,7 +453,7 @@ impl RefCount { pub fn get_refcount(&mut self, offset: u64) -> Result { let cluster = offset >> self.cluster_bits; let rt_idx = cluster >> self.refcount_blk_bits; - if rt_idx >= self.refcount_table_size as u64 { + if rt_idx >= self.refcount_table_size { return Ok(0); } -- Gitee From e50d473af26cd2ee4870fcaf90f99b150a3e0341 Mon Sep 17 00:00:00 2001 From: ZhiGang Date: Thu, 12 Oct 2023 08:52:52 +0000 Subject: [PATCH 1397/2187] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E4=BA=86re?= =?UTF-8?q?adme=E4=B8=AD=E5=AF=B9=E6=89=A9=E5=B1=95=E6=80=A7=E7=9A=84?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: ZhiGang --- README.ch.md | 5 +++-- README.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.ch.md b/README.ch.md index 1837a946e..5ffdd03d5 100644 --- a/README.ch.md +++ b/README.ch.md @@ -1,7 +1,8 @@ # StratoVirt: -StratoVirt是计算产业中面向云数据中心的企业级虚拟化平台,实现了一套架构统一支持虚拟机、容器、Serverless三种场景。StratoVirt在轻量低噪、软硬协同、Rust语言级安全等方面具备关键技术竞争优势。 +StratoVirt是计算产业中面向云数据中心的企业级虚拟化平台,实现了一套架构统一支持虚拟机、容器、Serverless三种场景。 +StratoVirt在轻量低噪、软硬协同、Rust语言级安全等方面具备关键技术竞争优势。 -StratoVirt预留了接口和设计来支持更多特性,未来甚至向标准虚拟化演进。 +StratoVirt预留了接口和设计来支持更多特性,同时支持标准虚拟化和轻量级虚拟化,也预留了对新型异构设备扩展支持能力。 ## 如何开始 diff --git a/README.md b/README.md index 1ab8e86a9..ab26df53f 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ three scenarios: virtual machines, containers, and serverless computing. StratoVirt has competitive advantages in light weight and low noise, software and hardware coordination, and Rust language-level security. -StratoVirt reserves interface and design for importing more features, even -evaluates to standard virtualization. +StratoVirt reserves interfaces and design to support more features, now can support +standard and lightweight virtualization together, +as well as the ability to extend support for new heterogeneous devices. ## How to start -- Gitee From 2b9b30b4081494badfb8ac248842c589225dff8a Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:29:25 +0300 Subject: [PATCH 1398/2187] machine_manager: tcp-socket argument in cmdline Command line parser adjusted to support a new type of backend (tcp-socket) for chardev device. Signed-off-by: prostous --- chardev_backend/src/chardev.rs | 16 +- machine/src/standard_vm/mod.rs | 6 +- machine_manager/src/cmdline.rs | 22 +- machine_manager/src/config/chardev.rs | 307 +++++++++++++++----------- machine_manager/src/config/fs.rs | 4 +- 5 files changed, 216 insertions(+), 139 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 0bfdaf89f..cd38efbc1 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -108,7 +108,7 @@ impl Chardev { self.input = Some(master_arc.clone()); self.output = Some(master_arc); } - ChardevType::Socket { + ChardevType::UnixSocket { path, server, nowait, @@ -132,6 +132,9 @@ impl Chardev { ) })?; } + ChardevType::TcpSocket { .. } => { + error!("tcp-socket backend not implemented") + } ChardevType::File(path) => { let file = Arc::new(Mutex::new( OpenOptions::new() @@ -246,7 +249,7 @@ fn get_notifier_handler( } None }), - ChardevType::Socket { .. } => Rc::new(move |_, _| { + ChardevType::UnixSocket { .. } => Rc::new(move |_, _| { let mut locked_chardev = chardev.lock().unwrap(); let (stream, _) = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); @@ -309,6 +312,10 @@ fn get_notifier_handler( vec![inner_handler], )]) }), + ChardevType::TcpSocket { .. } => Rc::new(move |_, _| { + error!("tcp-socket not implemented"); + None + }), ChardevType::File(_) => Rc::new(move |_, _| None), } } @@ -330,7 +337,7 @@ impl EventNotifierHelper for Chardev { )); } } - ChardevType::Socket { .. } => { + ChardevType::UnixSocket { .. } => { if chardev.lock().unwrap().stream_fd.is_some() { notifiers.push(EventNotifier::new( NotifierOperation::Resume, @@ -349,6 +356,9 @@ impl EventNotifierHelper for Chardev { )); } } + ChardevType::TcpSocket { .. } => { + error!("tcp-socket not implemented"); + } ChardevType::File(_) => (), } notifiers diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 7a4ac9ca0..1b6610d88 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -955,7 +955,7 @@ impl StdMachine { .with_context(|| format!("Chardev: {:?} not found for character device", &chardev))?; let socket_path = match &char_dev.backend { - ChardevType::Socket { + ChardevType::UnixSocket { path, server, nowait, @@ -963,13 +963,13 @@ impl StdMachine { if *server || *nowait { bail!( "Argument \'server\' or \'nowait\' is not needed for chardev \'{}\'", - path + &chardev ); } Some(path.clone()) } _ => { - bail!("Chardev {:?} backend should be socket type.", &chardev); + bail!("Chardev {:?} backend should be unix-socket type.", &chardev); } }; diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index f3596fd23..df414e18f 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -215,8 +215,12 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("chardev") .multiple(true) .long("chardev") - .value_name("socket,id=,path=") - .help("set char device virtio console for vm") + .value_name("") + .help("\n\t\tadd standard i/o device: -chardev stdio,id=; \ + \n\t\tadd pseudo-terminal: -chardev pty,id=; \ + \n\t\tadd file: -chardev file,id=,path=; \ + \n\t\tadd unix-socket: -chardev socket,id=,path=[,server][,nowait]; \ + \n\t\tadd tcp-socket: -chardev socket,id=,port=[,host=host][,server][,nowait];") .takes_values(true), ) .arg( @@ -253,8 +257,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("serial") .long("serial") - .value_name("backend[,path=,server,nowait] or chardev:") - .help("add serial and set chardev for it") + .value_name("") + .help("\n\t\tuse chardev device: -serial chardev:; \ + \n\t\tuse standard i/o device: -serial stdio; \ + \n\t\tuse pseudo-terminal: -serial pty; \ + \n\t\tuse file: -serial file,path=; \ + \n\t\tuse unix-socket: -serial socket,path=[,server][,nowait]; \ + \n\t\tuse tcp-socket: -serial socket,port=[,host=][,server][,nowait]; \ + ") .takes_value(true), ) .arg( @@ -634,7 +644,7 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< } if let Some(cfg) = vm_config.chardev.remove(&chardev) { - if let ChardevType::Socket { + if let ChardevType::UnixSocket { path, server, nowait, @@ -648,7 +658,7 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< } sock_paths.push(path); } else { - bail!("Only socket-type of chardev can be used for monitor"); + bail!("Only chardev of unix-socket type can be used for monitor"); } } else { bail!("No chardev found: {}", &chardev); diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 5bdd70d2d..401c99aed 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -10,6 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::net::IpAddr; +use std::str::FromStr; + use anyhow::{anyhow, bail, Context, Result}; use log::error; use serde::{Deserialize, Serialize}; @@ -31,11 +34,17 @@ const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31; pub enum ChardevType { Stdio, Pty, - Socket { + UnixSocket { path: String, server: bool, nowait: bool, }, + TcpSocket { + host: String, + port: u16, + server: bool, + nowait: bool, + }, File(String), } @@ -64,130 +73,185 @@ pub struct ChardevConfig { impl ConfigCheck for ChardevConfig { fn check(&self) -> Result<()> { check_arg_too_long(&self.id, "chardev id")?; - - let len = match &self.backend { - ChardevType::Socket { path, .. } => path.len(), - ChardevType::File(path) => path.len(), - _ => 0, - }; - if len > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "socket path".to_string(), - MAX_PATH_LENGTH - ))); - } - - Ok(()) - } -} - -fn check_chardev_args(cmd_parser: CmdParser) -> Result<()> { - if let Some(chardev_type) = cmd_parser.get_value::("")? { - let chardev_str = chardev_type.as_str(); - let server = cmd_parser.get_value::("server")?; - let nowait = cmd_parser.get_value::("nowait")?; - match chardev_str { - "stdio" | "pty" | "file" => { - if server.is_some() { - bail!( - "Chardev of {}-type does not support \'server\' argument", - chardev_str - ); - } - if nowait.is_some() { - bail!( - "Chardev of {}-type does not support \'nowait\' argument", - chardev_str - ); + match &self.backend { + ChardevType::UnixSocket { path, .. } => { + if path.len() > MAX_PATH_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "unix-socket path".to_string(), + MAX_PATH_LENGTH + ))); } + Ok(()) } - "socket" => { - if let Some(server) = server { - if server.ne("") { - bail!("No parameter needed for server"); - } + ChardevType::TcpSocket { host, port, .. } => { + if *port == 0u16 { + return Err(anyhow!(ConfigError::InvalidParam( + "port".to_string(), + "tcp-socket".to_string() + ))); + } + let ip_address = IpAddr::from_str(host); + if ip_address.is_err() { + return Err(anyhow!(ConfigError::InvalidParam( + "host".to_string(), + "tcp-socket".to_string() + ))); } - if let Some(nowait) = nowait { - if nowait.ne("") { - bail!("No parameter needed for nowait"); - } + Ok(()) + } + ChardevType::File(path) => { + if path.len() > MAX_PATH_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "file path".to_string(), + MAX_PATH_LENGTH + ))); } + Ok(()) } - _ => (), + _ => Ok(()), + } + } +} + +fn check_chardev_fields( + dev_type: &str, + cmd_parser: &CmdParser, + supported_fields: &[&str], +) -> Result<()> { + for (field, value) in &cmd_parser.params { + let supported_field = supported_fields.contains(&field.as_str()); + if !supported_field && value.is_some() { + bail!( + "Chardev of type {} does not support \'{}\' argument", + dev_type, + field + ); } } Ok(()) } -pub fn parse_chardev(cmd_parser: CmdParser) -> Result { - let chardev_id = cmd_parser - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "chardev".to_string()))?; - let backend = cmd_parser.get_value::("")?; - let path = cmd_parser.get_value::("path")?; - let server = if let Some(server) = cmd_parser.get_value::("server")? { +fn parse_stdio_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { + let supported_fields = ["", "id"]; + check_chardev_fields("stdio", &cmd_parser, &supported_fields)?; + Ok(ChardevConfig { + id: chardev_id, + backend: ChardevType::Stdio, + }) +} + +fn parse_pty_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { + let supported_fields = ["", "id"]; + check_chardev_fields("pty", &cmd_parser, &supported_fields)?; + Ok(ChardevConfig { + id: chardev_id, + backend: ChardevType::Pty, + }) +} + +fn parse_file_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { + let supported_fields = ["", "id", "path"]; + check_chardev_fields("file", &cmd_parser, &supported_fields)?; + + let path = cmd_parser + .get_value::("path")? + .with_context(|| ConfigError::FieldIsMissing("path".to_string(), "chardev".to_string()))?; + + let canonical_path = std::fs::canonicalize(path)?; + let file_path = String::from(canonical_path.to_str().unwrap()); + Ok(ChardevConfig { + id: chardev_id, + backend: ChardevType::File(file_path), + }) +} + +fn parse_socket_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { + let mut server_enabled = false; + let server = cmd_parser.get_value::("server")?; + if let Some(server) = server { if server.ne("") { bail!("No parameter needed for server"); } - true - } else { - false - }; - let nowait = if let Some(nowait) = cmd_parser.get_value::("nowait")? { + server_enabled = true; + } + + let mut nowait_enabled = false; + let nowait = cmd_parser.get_value::("nowait")?; + if let Some(nowait) = nowait { if nowait.ne("") { bail!("No parameter needed for nowait"); } - true - } else { - false - }; - check_chardev_args(cmd_parser)?; - let chardev_type = if let Some(backend) = backend { - match backend.as_str() { - "stdio" => ChardevType::Stdio, - "pty" => ChardevType::Pty, - "socket" => { - if let Some(path) = path { - ChardevType::Socket { - path, - server, - nowait, - } - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "path".to_string(), - "socket-type chardev".to_string() - ))); - } - } - "file" => { - if let Some(path) = path { - ChardevType::File(path) - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "path".to_string(), - "file-type chardev".to_string() - ))); - } - } - _ => { - return Err(anyhow!(ConfigError::InvalidParam( - backend, - "chardev".to_string() - ))) - } - } - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "backend".to_string(), - "chardev".to_string() - ))); - }; + nowait_enabled = true; + } - Ok(ChardevConfig { - id: chardev_id, - backend: chardev_type, - }) + let path = cmd_parser.get_value::("path")?; + if let Some(path) = path { + let supported_fields = ["", "id", "path", "server", "nowait"]; + check_chardev_fields("unix-socket", &cmd_parser, &supported_fields)?; + + let default_value = path.clone(); + let socket_path = std::fs::canonicalize(path).map_or(default_value, |canonical_path| { + String::from(canonical_path.to_str().unwrap()) + }); + return Ok(ChardevConfig { + id: chardev_id, + backend: ChardevType::UnixSocket { + path: socket_path, + server: server_enabled, + nowait: nowait_enabled, + }, + }); + } + + let port = cmd_parser.get_value::("port")?; + if let Some(port) = port { + let supported_fields = ["", "id", "host", "port", "server", "nowait"]; + check_chardev_fields("tcp-socket", &cmd_parser, &supported_fields)?; + + let host = cmd_parser.get_value::("host")?; + return Ok(ChardevConfig { + id: chardev_id, + backend: ChardevType::TcpSocket { + host: host.unwrap_or_else(|| String::from("0.0.0.0")), + port, + server: server_enabled, + nowait: nowait_enabled, + }, + }); + } + + Err(anyhow!(ConfigError::InvalidParam( + "backend".to_string(), + "chardev".to_string() + ))) +} + +pub fn parse_chardev(chardev_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("chardev"); + for field in ["", "id", "path", "host", "port", "server", "nowait"] { + cmd_parser.push(field); + } + + cmd_parser.parse(chardev_config)?; + + let chardev_id = cmd_parser + .get_value::("id")? + .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "chardev".to_string()))?; + + let backend = cmd_parser + .get_value::("")? + .with_context(|| ConfigError::InvalidParam("backend".to_string(), "chardev".to_string()))?; + + match backend.as_str() { + "stdio" => parse_stdio_chardev(chardev_id, cmd_parser), + "pty" => parse_pty_chardev(chardev_id, cmd_parser), + "file" => parse_file_chardev(chardev_id, cmd_parser), + "socket" => parse_socket_chardev(chardev_id, cmd_parser), + _ => Err(anyhow!(ConfigError::InvalidParam( + backend, + "chardev".to_string() + ))), + } } /// Get chardev config from qmp arguments. @@ -224,7 +288,7 @@ pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result Result Result { if let Some(char_dev) = vm_config.chardev.remove(chardev) { match char_dev.backend.clone() { - ChardevType::Socket { + ChardevType::UnixSocket { path, server, nowait, @@ -255,7 +319,10 @@ pub fn get_chardev_socket_path(chardev: &str, vm_config: &mut VmConfig) -> Resul Ok(path) } _ => { - bail!("Chardev {:?} backend should be socket type.", &char_dev.id); + bail!( + "Chardev {:?} backend should be unix-socket type.", + &char_dev.id + ); } } } else { @@ -302,17 +369,7 @@ pub fn parse_virtserialport( impl VmConfig { /// Add chardev config to `VmConfig`. pub fn add_chardev(&mut self, chardev_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("chardev"); - cmd_parser - .push("") - .push("id") - .push("path") - .push("server") - .push("nowait"); - - cmd_parser.parse(chardev_config)?; - - let chardev = parse_chardev(cmd_parser)?; + let chardev = parse_chardev(chardev_config)?; chardev.check()?; let chardev_id = chardev.id.clone(); if self.chardev.get(&chardev_id).is_none() { @@ -545,7 +602,7 @@ mod tests { assert_eq!(console_cfg.id, "console1"); assert_eq!( console_cfg.chardev.backend, - ChardevType::Socket { + ChardevType::UnixSocket { path: "/path/to/socket".to_string(), server: true, nowait: true, @@ -603,7 +660,7 @@ mod tests { assert_eq!(bdf.addr, (1, 2)); assert_eq!( console_cfg.chardev.backend, - ChardevType::Socket { + ChardevType::UnixSocket { path: "/path/to/socket".to_string(), server: true, nowait: true, @@ -651,7 +708,7 @@ mod tests { if let Some(char_dev) = vm_config.chardev.remove("test_id") { assert_eq!( char_dev.backend, - ChardevType::Socket { + ChardevType::UnixSocket { path: "/path/to/socket".to_string(), server: false, nowait: false, diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index bce9e1b14..e1a16ab35 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -93,11 +93,11 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { if let Some(name) = cmd_parser.get_value::("chardev")? { if let Some(char_dev) = vm_config.chardev.remove(&name) { match &char_dev.backend { - ChardevType::Socket { path, .. } => { + ChardevType::UnixSocket { path, .. } => { fs_cfg.sock = path.clone(); } _ => { - bail!("Chardev {:?} backend should be socket type.", &name); + bail!("Chardev {:?} backend should be unix-socket type.", &name); } } } else { -- Gitee From f0c43d2b77c6fd2008b38a6dd5244701392169fe Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 24 Oct 2023 17:44:48 +0800 Subject: [PATCH 1399/2187] scsi: change the SCSI instruction log level to debug Guests can cause backend logs to explode by issuing error instructions without stopping. Change the scsi instruction log level to debug. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 83cd0edd1..ee78f472a 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -17,7 +17,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; -use log::{debug, error, info}; +use log::{debug, info}; use crate::ScsiDisk::{ ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, @@ -686,11 +686,11 @@ impl ScsiRequest { } Err(ref e) => { if not_supported_flag { - debug!("emulation scsi command {:#x} is no supported", self.cmd.op); + debug!("emulation scsi command {:#x} is not supported", self.cmd.op); status = CHECK_CONDITION; sense = Some(SCSI_SENSE_INVALID_OPCODE); } else { - error!( + debug!( "Error in processing scsi command {:#x}, err is {:?}", self.cmd.op, e ); -- Gitee From 5fd56f1bfaf8cc68b59bf391070cccaab8934904 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 25 Oct 2023 03:41:03 +0800 Subject: [PATCH 1400/2187] usb: change the USB control request log level to warn Guest can maliciously inject incorrect requests to cause the backend to print logs crazily. Due to this being a serious error, we must keep this log for the purpose of locating the issue. Change the log level to warn, and since logs can be sharded and dumped, the risk of backend log explosion is mitigated by other components. Signed-off-by: liuxiangdong --- devices/src/usb/camera.rs | 4 ++-- devices/src/usb/keyboard.rs | 4 ++-- devices/src/usb/storage.rs | 2 +- devices/src/usb/tablet.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 0a857af91..3577fa724 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -19,7 +19,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -795,7 +795,7 @@ impl UsbDevice for UsbCamera { } } Err(e) => { - error!("Camera descriptor error {:?}", e); + warn!("Camera descriptor error {:?}", e); locked_packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index d2367d663..f46c8a4b2 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; -use log::{debug, error, info}; +use log::{debug, info, warn}; use once_cell::sync::Lazy; use super::descriptor::{ @@ -223,7 +223,7 @@ impl UsbDevice for UsbKeyboard { } } Err(e) => { - error!("Keyboard descriptor error {:?}", e); + warn!("Keyboard descriptor error {:?}", e); locked_packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index be1473ca2..856664acc 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -563,7 +563,7 @@ impl UsbDevice for UsbStorage { self.handle_control_packet(&mut locked_packet, device_req) } Err(e) => { - error!("Storage descriptor error {:?}", e); + warn!("Storage descriptor error {:?}", e); locked_packet.status = UsbPacketStatus::Stall; } } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index d5c2fbae7..13d040646 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -14,7 +14,7 @@ use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; -use log::{debug, error, info}; +use log::{debug, info, warn}; use once_cell::sync::Lazy; use super::descriptor::{ @@ -217,7 +217,7 @@ impl UsbDevice for UsbTablet { } } Err(e) => { - error!("Tablet descriptor error {:?}", e); + warn!("Tablet descriptor error {:?}", e); locked_packet.status = UsbPacketStatus::Stall; return; } -- Gitee From 7c149c112956ee23e7b798c0dc92757d960c02d2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 25 Oct 2023 04:55:53 +0800 Subject: [PATCH 1401/2187] virtio-serial: check control handler when chardev notifies device Ctrl_handler may be useless when chardev need to notify device. Check before using it. Signed-off-by: liuxiangdong --- virtio/src/device/serial.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 712c3558e..2a0229eaf 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -884,22 +884,31 @@ impl EventNotifierHelper for SerialControlHandler { impl ChardevNotifyDevice for SerialPort { fn chardev_notify(&mut self, status: ChardevStatus) { + if self.ctrl_handler.is_none() { + warn!("No control handler for port {}.", self.nr); + return; + } + match (&status, self.host_connected) { (ChardevStatus::Close, _) => self.host_connected = false, (ChardevStatus::Open, false) => self.host_connected = true, (ChardevStatus::Open, true) => return, } - if let Some(handler) = &self.ctrl_handler { - let handler = handler.upgrade().unwrap(); - handler.lock().unwrap().send_control_event( - self.nr, - VIRTIO_CONSOLE_PORT_OPEN, - status as u16, - ); - } else { - error!("Control handler for port {} is None", self.nr); + let handler = self.ctrl_handler.as_ref().unwrap().upgrade(); + if handler.is_none() { + warn!("Control handler for port {} is invalid", self.nr); + return; } + + // Note: when virtio serial devices are deactivated, all handlers will be unregistered. + // For this action is in the same thread with `chardev_notify`, these two operations will + // not be executed concurrently. So, `handler` must be effective here. + handler.unwrap().lock().unwrap().send_control_event( + self.nr, + VIRTIO_CONSOLE_PORT_OPEN, + status as u16, + ); } } -- Gitee From 519a7430d1226ea2322b4976bc1ed0542b64bb2e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 25 Oct 2023 14:51:35 +0800 Subject: [PATCH 1402/2187] chardev: drop chardev lock before notifying device The lock holding time should be as short as possible. Drop chardev lock before notifying device. Signed-off-by: liuxiangdong --- chardev_backend/src/chardev.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 4c30d9196..1cfa52f04 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -323,15 +323,17 @@ fn get_socket_notifier(chardev: Arc>) -> Option { "Chardev \'{}\' event, connection opened: {}", &locked_chardev.id, connection_info ); - let stream_fd = stream.as_raw_fd(); let stream_arc = Arc::new(Mutex::new(stream)); + let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); + let notify_dev = locked_chardev.dev.clone(); locked_chardev.stream_fd = Some(stream_fd); locked_chardev.input = Some(stream_arc.clone()); locked_chardev.output = Some(stream_arc.clone()); + drop(locked_chardev); - if let Some(dev) = &locked_chardev.dev { + if let Some(dev) = notify_dev { dev.lock().unwrap().chardev_notify(ChardevStatus::Open); } @@ -372,19 +374,19 @@ fn get_socket_notifier(chardev: Arc>) -> Option { None } else if event & EventSet::HANG_UP == EventSet::HANG_UP { let mut locked_chardev = cloned_chardev.lock().unwrap(); - - if let Some(dev) = &locked_chardev.dev { - dev.lock().unwrap().chardev_notify(ChardevStatus::Close); - } - + let notify_dev = locked_chardev.dev.clone(); locked_chardev.input = None; locked_chardev.output = None; locked_chardev.stream_fd = None; - info!( "Chardev \'{}\' event, connection closed: {}", &locked_chardev.id, connection_info ); + drop(locked_chardev); + + if let Some(dev) = notify_dev { + dev.lock().unwrap().chardev_notify(ChardevStatus::Close); + } // Note: we use stream_arc variable here because we want to capture it and prolongate // its lifetime with this notifier callback lifetime. It allows us to ensure @@ -396,7 +398,6 @@ fn get_socket_notifier(chardev: Arc>) -> Option { } }); - let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); Some(vec![EventNotifier::new( NotifierOperation::AddShared, stream_fd, -- Gitee From 1251931ebe9cfc540fb377cf1452b137f892ea1d Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:48:09 +0300 Subject: [PATCH 1403/2187] machine_manager: additional unit-tests Implemented additional unit-tests to cover new command line arguments of socket chardev. Signed-off-by: prostous --- machine_manager/src/config/chardev.rs | 162 ++++++++++++++++++-------- 1 file changed, 116 insertions(+), 46 deletions(-) diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 401c99aed..5bcdaf874 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -157,8 +157,11 @@ fn parse_file_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result("path")? .with_context(|| ConfigError::FieldIsMissing("path".to_string(), "chardev".to_string()))?; - let canonical_path = std::fs::canonicalize(path)?; - let file_path = String::from(canonical_path.to_str().unwrap()); + let default_value = path.clone(); + let file_path = std::fs::canonicalize(path).map_or(default_value, |canonical_path| { + String::from(canonical_path.to_str().unwrap()) + }); + Ok(ChardevConfig { id: chardev_id, backend: ChardevType::File(file_path), @@ -193,6 +196,7 @@ fn parse_socket_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result Date: Tue, 3 Oct 2023 15:01:46 +0300 Subject: [PATCH 1404/2187] util: generic socket implementation Implemented socket listener and socket stream to incapsulate two backends: - unix domain socket - tcp socket Signed-off-by: prostous --- util/src/lib.rs | 1 + util/src/socket.rs | 164 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 util/src/socket.rs diff --git a/util/src/lib.rs b/util/src/lib.rs index 3d618d583..abe1a436b 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -32,6 +32,7 @@ pub mod offsetof; pub mod pixman; pub mod reader; pub mod seccomp; +pub mod socket; pub mod syscall; pub mod tap; pub mod test_helper; diff --git a/util/src/socket.rs b/util/src/socket.rs new file mode 100644 index 000000000..3b1290bbe --- /dev/null +++ b/util/src/socket.rs @@ -0,0 +1,164 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::io::{IoSlice, IoSliceMut, Result as IoResult}; +use std::net::{TcpListener, TcpStream}; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::net::{UnixListener, UnixStream}; + +use anyhow::Result; + +/// Provide socket abstraction for UnixStream and TcpStream. +#[derive(Debug)] +pub enum SocketStream { + Tcp { + link_description: String, + stream: TcpStream, + }, + Unix { + link_description: String, + stream: UnixStream, + }, +} + +impl SocketStream { + pub fn link_description(&self) -> String { + match self { + SocketStream::Tcp { + link_description, .. + } => link_description.clone(), + SocketStream::Unix { + link_description, .. + } => link_description.clone(), + } + } +} + +impl AsRawFd for SocketStream { + fn as_raw_fd(&self) -> RawFd { + match self { + SocketStream::Tcp { stream, .. } => stream.as_raw_fd(), + SocketStream::Unix { stream, .. } => stream.as_raw_fd(), + } + } +} + +impl std::io::Read for SocketStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match self { + SocketStream::Tcp { stream, .. } => stream.read(buf), + SocketStream::Unix { stream, .. } => stream.read(buf), + } + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> IoResult { + match self { + SocketStream::Tcp { stream, .. } => stream.read_vectored(bufs), + SocketStream::Unix { stream, .. } => stream.read_vectored(bufs), + } + } +} + +impl std::io::Write for SocketStream { + fn write(&mut self, buf: &[u8]) -> IoResult { + match self { + SocketStream::Tcp { stream, .. } => stream.write(buf), + SocketStream::Unix { stream, .. } => stream.write(buf), + } + } + + fn write_vectored(&mut self, bufs: &[IoSlice]) -> IoResult { + match self { + SocketStream::Tcp { stream, .. } => stream.write_vectored(bufs), + SocketStream::Unix { stream, .. } => stream.write_vectored(bufs), + } + } + + fn flush(&mut self) -> IoResult<()> { + match self { + SocketStream::Tcp { stream, .. } => stream.flush(), + SocketStream::Unix { stream, .. } => stream.flush(), + } + } +} + +/// Provide listener abstraction for UnixListener and TcpListener. +#[derive(Debug)] +pub enum SocketListener { + Tcp { + address: String, + listener: TcpListener, + }, + Unix { + address: String, + listener: UnixListener, + }, +} + +impl SocketListener { + pub fn bind_by_tcp(host: &str, port: u16) -> Result { + let address = format!("{}:{}", &host, &port); + let listener = TcpListener::bind(&address)?; + listener.set_nonblocking(true)?; + Ok(SocketListener::Tcp { address, listener }) + } + + pub fn bind_by_uds(path: &str) -> Result { + let listener = UnixListener::bind(path)?; + listener.set_nonblocking(true)?; + Ok(SocketListener::Unix { + address: String::from(path), + listener, + }) + } + + pub fn address(&self) -> String { + match self { + SocketListener::Tcp { address, .. } => address.clone(), + SocketListener::Unix { address, .. } => address.clone(), + } + } + + pub fn accept(&self) -> Result { + match self { + SocketListener::Tcp { listener, address } => { + let (stream, sock_addr) = listener.accept()?; + let peer_address = sock_addr.to_string(); + let link_description = format!( + "{{ protocol: tcp, address: {}, peer: {} }}", + address, peer_address + ); + Ok(SocketStream::Tcp { + link_description, + stream, + }) + } + SocketListener::Unix { listener, address } => { + let (stream, _) = listener.accept()?; + let link_description = format!("{{ protocol: unix, address: {} }}", address); + Ok(SocketStream::Unix { + link_description, + stream, + }) + } + } + } +} + +impl AsRawFd for SocketListener { + fn as_raw_fd(&self) -> RawFd { + match self { + SocketListener::Tcp { listener, .. } => listener.as_raw_fd(), + SocketListener::Unix { listener, .. } => listener.as_raw_fd(), + } + } +} -- Gitee From b2249c4fdf898ba5f7427f94dc392caae4e1b825 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:46:33 +0300 Subject: [PATCH 1405/2187] chardev: use generic socket listener In chardev backend unix listener and stream replaced by generic version which implements both unix domain socket and tcp socket. As a result, chardev starts working with both types of socket. Signed-off-by: prostous --- chardev_backend/src/chardev.rs | 63 +++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index cd38efbc1..b3f09045f 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -13,7 +13,6 @@ use std::fs::{read_link, File, OpenOptions}; use std::io::{Stdin, Stdout}; use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -33,6 +32,7 @@ use util::loop_context::{ gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::set_termi_raw_mode; +use util::socket::{SocketListener, SocketStream}; use util::unix::limit_permission; /// Provide the trait that helps handle the input data. @@ -59,8 +59,8 @@ pub struct Chardev { id: String, /// Type of backend device. backend: ChardevType, - /// UnixListener for socket-type chardev. - listener: Option, + /// Socket listener for chardev of socket type. + listener: Option, /// Chardev input. input: Option>>, /// Chardev output. @@ -116,24 +116,48 @@ impl Chardev { if !*server || !*nowait { bail!( "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", - path + &self.id ); } + clear_file(path.clone())?; - let sock = UnixListener::bind(path.clone()) - .with_context(|| format!("Failed to bind socket for chardev, path:{}", path))?; - self.listener = Some(sock); + let listener = SocketListener::bind_by_uds(path).with_context(|| { + format!( + "Failed to bind socket for chardev \'{}\', path: {}", + &self.id, path + ) + })?; + self.listener = Some(listener); + // add file to temporary pool, so it could be cleaned when vm exit. TempCleaner::add_path(path.clone()); limit_permission(path).with_context(|| { format!( - "Failed to change file permission for chardev, path:{}", - path + "Failed to change file permission for chardev \'{}\', path: {}", + &self.id, path ) })?; } - ChardevType::TcpSocket { .. } => { - error!("tcp-socket backend not implemented") + ChardevType::TcpSocket { + host, + port, + server, + nowait, + } => { + if !*server || !*nowait { + bail!( + "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", + &self.id + ); + } + + let listener = SocketListener::bind_by_tcp(host, *port).with_context(|| { + format!( + "Failed to bind socket for chardev \'{}\', address: {}:{}", + &self.id, host, port + ) + })?; + self.listener = Some(listener); } ChardevType::File(path) => { let file = Arc::new(Mutex::new( @@ -249,9 +273,9 @@ fn get_notifier_handler( } None }), - ChardevType::UnixSocket { .. } => Rc::new(move |_, _| { + ChardevType::UnixSocket { .. } | ChardevType::TcpSocket { .. } => Rc::new(move |_, _| { let mut locked_chardev = chardev.lock().unwrap(); - let (stream, _) = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); + let stream = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); let stream_fd = stream.as_raw_fd(); locked_chardev.stream_fd = Some(stream_fd); @@ -312,10 +336,6 @@ fn get_notifier_handler( vec![inner_handler], )]) }), - ChardevType::TcpSocket { .. } => Rc::new(move |_, _| { - error!("tcp-socket not implemented"); - None - }), ChardevType::File(_) => Rc::new(move |_, _| None), } } @@ -337,7 +357,7 @@ impl EventNotifierHelper for Chardev { )); } } - ChardevType::UnixSocket { .. } => { + ChardevType::UnixSocket { .. } | ChardevType::TcpSocket { .. } => { if chardev.lock().unwrap().stream_fd.is_some() { notifiers.push(EventNotifier::new( NotifierOperation::Resume, @@ -356,9 +376,6 @@ impl EventNotifierHelper for Chardev { )); } } - ChardevType::TcpSocket { .. } => { - error!("tcp-socket not implemented"); - } ChardevType::File(_) => (), } notifiers @@ -381,10 +398,10 @@ pub trait CommunicatInInterface: std::marker::Send + std::os::unix::io::AsRawFd /// Provide backend trait object processing the output from the guest. pub trait CommunicatOutInterface: std::io::Write + std::marker::Send {} -impl CommunicatInInterface for UnixStream {} +impl CommunicatInInterface for SocketStream {} impl CommunicatInInterface for File {} impl CommunicatInInterface for Stdin {} -impl CommunicatOutInterface for UnixStream {} +impl CommunicatOutInterface for SocketStream {} impl CommunicatOutInterface for File {} impl CommunicatOutInterface for Stdout {} -- Gitee From d2ecb6e6e9cc612048fec143f8b729d7b2367c0c Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 20:50:40 +0300 Subject: [PATCH 1406/2187] machine: whitelisted syscall 'sendto' Syscall 'sendto' whitelisted to allow tcp socket work properly without restrictions. Also, fixed wrong information about number of whitelisted syscalls for all vm types. Signed-off-by: prostous Signed-off-by: Andrey Kazmin --- machine/src/micro_vm/syscall.rs | 9 +++++---- machine/src/standard_vm/aarch64/syscall.rs | 6 +++--- machine/src/standard_vm/x86_64/syscall.rs | 6 +++--- vhost_user_fs/src/securecomputing.rs | 1 + 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index 4edebd170..a7b4b09ed 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -42,10 +42,10 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 53 syscalls -/// * x86_64-unknown-musl: 52 syscalls -/// * aarch64-unknown-gnu: 51 syscalls -/// * aarch64-unknown-musl: 51 syscalls +/// * x86_64-unknown-gnu: 59 syscalls +/// * x86_64-unknown-musl: 58 syscalls +/// * aarch64-unknown-gnu: 57 syscalls +/// * aarch64-unknown-musl: 57 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -71,6 +71,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + BpfRule::new(libc::SYS_sendto), BpfRule::new(libc::SYS_recvfrom), BpfRule::new(libc::SYS_mremap), BpfRule::new(libc::SYS_io_setup), diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_vm/aarch64/syscall.rs index 262de2955..7a2997a0a 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_vm/aarch64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 98 syscalls -/// * aarch64-unknown-musl: 61 syscalls +/// * aarch64-unknown-gnu: 100 syscalls +/// * aarch64-unknown-musl: 64 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -80,6 +80,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + BpfRule::new(libc::SYS_sendto), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sendmmsg), BpfRule::new(libc::SYS_recvfrom), @@ -132,7 +133,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_getcwd), BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_prctl), - BpfRule::new(libc::SYS_sendto), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockname), #[cfg(target_env = "gnu")] diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs index 37748e799..83e1c8d48 100644 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ b/machine/src/standard_vm/x86_64/syscall.rs @@ -55,8 +55,8 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 96 syscalls -/// * x86_64-unknown-musl: 64 syscalls +/// * x86_64-unknown-gnu: 98 syscalls +/// * x86_64-unknown-musl: 67 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { vec![ @@ -82,6 +82,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_fdatasync), BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), + BpfRule::new(libc::SYS_sendto), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sendmmsg), BpfRule::new(libc::SYS_recvfrom), @@ -141,7 +142,6 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_clone3), BpfRule::new(libc::SYS_prctl), - BpfRule::new(libc::SYS_sendto), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockname), #[cfg(target_env = "gnu")] diff --git a/vhost_user_fs/src/securecomputing.rs b/vhost_user_fs/src/securecomputing.rs index 42380df52..f6178b80d 100644 --- a/vhost_user_fs/src/securecomputing.rs +++ b/vhost_user_fs/src/securecomputing.rs @@ -98,6 +98,7 @@ fn syscall_whitelist() -> Vec { v.push(libc::SYS_rt_sigreturn); v.push(libc::SYS_sched_getaffinity); v.push(libc::SYS_sendmsg); + v.push(libc::SYS_sendto); v.push(libc::SYS_setresgid); v.push(libc::SYS_setresuid); v.push(libc::SYS_set_robust_list); -- Gitee From 416420ec6532fb6b6f05a48f48f82a67fd9e2436 Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 27 Sep 2023 21:55:48 +0300 Subject: [PATCH 1407/2187] docs: chardev's tcp socket usage described Adjusted documentation about serial and chardev devices to show support of tcp socket backend. Signed-off-by: prostous --- docs/config_guidebook.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 44ae369ce..de36681d4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -657,6 +657,7 @@ Or you can simply use `-serial dev` to bind serial with character device. -serial stdio -serial pty -serial socket,path=,server,nowait +-serial socket,port=[,host=],server,nowait -serial file,path= ``` @@ -773,19 +774,32 @@ See [VFIO](./vfio.md) for more details. ### 2.12 Chardev The type of chardev backend could be: stdio, pty, socket and file(output only). -Five properties can be set for chardev. +One property can be set for chardev of stdio or pty type. +* id: unique chardev-id. + +Four properties can be set for chardev of unix-socket type. +* id: unique chardev-id. +* path: path to the unix-socket file on the host. +* server: run as a server. +* nowait: do not wait for connection. + +Five properties can be set for chardev of tcp-socket type. +* id: unique chardev-id. +* host: host for binding on (in case of server mode) or connecting to (in case of non-server mode). Default value for binding is '0.0.0.0'. +* port: port for binding on (in case of server mode) or connecting to (in case of non-server mode). +* server: run as a server. +* nowait: do not wait for connection. +Two properties can be set for chardev of file type. * id: unique chardev-id. -* backend: the type of redirect method. -* path: the path of backend in the host. This argument is only required for socket-type chardev and file-type chardev. -* server: run as a server. This argument is only required for socket-type chardev. -* nowait: do not wait for connection. This argument is only required for socket-type chardev. +* path: path to the input data file on the host. ```shell # redirect methods -chardev stdio,id= -chardev pty,id= -chardev socket,id=,path=[,server,nowait] +-chardev socket,id=,port=[,host=][,server,nowait] -chardev file,id=,path= ``` -- Gitee From d2ed6ba6ec5a025efd7b81ec548248923df49f5f Mon Sep 17 00:00:00 2001 From: prostous Date: Thu, 28 Sep 2023 17:11:30 +0300 Subject: [PATCH 1408/2187] chardev_backend: removed obsolete code There was a crutch for virtio serial port in chardev implementation but it is already obsolete, so it was removed. Signed-off-by: prostous --- chardev_backend/src/chardev.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index b3f09045f..971c91d96 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -358,15 +358,7 @@ impl EventNotifierHelper for Chardev { } } ChardevType::UnixSocket { .. } | ChardevType::TcpSocket { .. } => { - if chardev.lock().unwrap().stream_fd.is_some() { - notifiers.push(EventNotifier::new( - NotifierOperation::Resume, - chardev.lock().unwrap().stream_fd.unwrap(), - None, - EventSet::IN | EventSet::HANG_UP, - Vec::new(), - )); - } else if let Some(listener) = chardev.lock().unwrap().listener.as_ref() { + if let Some(listener) = chardev.lock().unwrap().listener.as_ref() { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, listener.as_raw_fd(), -- Gitee From cebf73a1eb8c3227c1431c8392acdfb03b3b67a9 Mon Sep 17 00:00:00 2001 From: prostous Date: Thu, 28 Sep 2023 20:21:22 +0300 Subject: [PATCH 1409/2187] chardev_backend: minor refactoring Some refactoring in chardev implementation, logic fully saved: - splitted event notifications function into multiple methods - added some additional log messages Signed-off-by: prostous --- chardev_backend/src/chardev.rs | 302 ++++++++++++++++++++------------- 1 file changed, 180 insertions(+), 122 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 971c91d96..4c30d9196 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -241,136 +241,194 @@ fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { Ok((master, path)) } -fn get_notifier_handler( - chardev: Arc>, - backend: ChardevType, -) -> Rc { - match backend { - ChardevType::Stdio | ChardevType::Pty => Rc::new(move |_, _| { - let locked_chardev = chardev.lock().unwrap(); - if locked_chardev.receiver.is_none() { - error!("Failed to get chardev receiver"); - return None; - } - if locked_chardev.input.is_none() { - error!("Failed to get chardev input fd"); - return None; - } - let receiver = locked_chardev.receiver.clone().unwrap(); - let input = locked_chardev.input.clone().unwrap(); - drop(locked_chardev); - - let mut locked_receiver = receiver.lock().unwrap(); - let buff_size = locked_receiver.remain_size(); - if buff_size == 0 { - return None; - } - let mut buffer = vec![0_u8; buff_size]; - if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { - locked_receiver.receive(&buffer[..index]); - } else { - error!("Failed to read input data"); - } - None - }), - ChardevType::UnixSocket { .. } | ChardevType::TcpSocket { .. } => Rc::new(move |_, _| { - let mut locked_chardev = chardev.lock().unwrap(); - let stream = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); - let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); - let stream_fd = stream.as_raw_fd(); - locked_chardev.stream_fd = Some(stream_fd); - let stream_arc = Arc::new(Mutex::new(stream)); - locked_chardev.input = Some(stream_arc.clone()); - locked_chardev.output = Some(stream_arc); - - if let Some(dev) = &locked_chardev.dev { - dev.lock().unwrap().chardev_notify(ChardevStatus::Open); - } +// Notification handling in case of stdio or pty usage. +fn get_terminal_notifier(chardev: Arc>) -> Option { + let locked_chardev = chardev.lock().unwrap(); + let input = locked_chardev.input.clone(); + if input.is_none() { + // Method `realize` expected to be called before we get here because to build event + // notifier we need already valid file descriptors here. + error!( + "Failed to initialize input events for chardev \'{}\', chardev not initialized", + &locked_chardev.id + ); + return None; + } - let cloned_chardev = chardev.clone(); - let inner_handler: Rc = Rc::new(move |event, _| { - let mut locked_chardev = cloned_chardev.lock().unwrap(); - if event == EventSet::IN { - if locked_chardev.receiver.is_none() { - error!("Failed to get chardev receiver"); - return None; - } - if locked_chardev.input.is_none() { - error!("Failed to get chardev input fd"); - return None; - } - let receiver = locked_chardev.receiver.clone().unwrap(); - let input = locked_chardev.input.clone().unwrap(); - drop(locked_chardev); - - let mut locked_receiver = receiver.lock().unwrap(); - let buff_size = locked_receiver.remain_size(); - if buff_size == 0 { - return None; - } - let mut buffer = vec![0_u8; buff_size]; - if let Ok(index) = input.lock().unwrap().chr_read_raw(&mut buffer) { - locked_receiver.receive(&buffer[..index]); - } else { - error!("Failed to read input data"); - } - None - } else if event & EventSet::HANG_UP == EventSet::HANG_UP { - // Always allow disconnect even if has deactivated. - if let Some(dev) = &locked_chardev.dev { - dev.lock().unwrap().chardev_notify(ChardevStatus::Close); - } - locked_chardev.input = None; - locked_chardev.output = None; - locked_chardev.stream_fd = None; - Some(gen_delete_notifiers(&[stream_fd])) + let cloned_chardev = chardev.clone(); + let event_handler: Rc = Rc::new(move |_, _| { + let locked_chardev = cloned_chardev.lock().unwrap(); + if locked_chardev.receiver.is_none() { + error!( + "Failed to read input data from chardev \'{}\', receiver is none", + &locked_chardev.id + ); + return None; + } + let receiver = locked_chardev.receiver.clone().unwrap(); + let input = locked_chardev.input.clone().unwrap(); + drop(locked_chardev); + + let mut locked_receiver = receiver.lock().unwrap(); + let buff_size = locked_receiver.remain_size(); + if buff_size == 0 { + return None; + } + + let mut buffer = vec![0_u8; buff_size]; + if let Ok(bytes_count) = input.lock().unwrap().chr_read_raw(&mut buffer) { + locked_receiver.receive(&buffer[..bytes_count]); + } else { + let os_error = std::io::Error::last_os_error(); + let locked_chardev = cloned_chardev.lock().unwrap(); + error!( + "Failed to read input data from chardev \'{}\', {}", + &locked_chardev.id, &os_error + ); + } + None + }); + + let input_fd = input.unwrap().lock().unwrap().as_raw_fd(); + Some(EventNotifier::new( + NotifierOperation::AddShared, + input_fd, + None, + EventSet::IN, + vec![event_handler], + )) +} + +// Notification handling in case of listening (server) socket. +fn get_socket_notifier(chardev: Arc>) -> Option { + let locked_chardev = chardev.lock().unwrap(); + let listener = &locked_chardev.listener; + if listener.is_none() { + // Method `realize` expected to be called before we get here because to build event + // notifier we need already valid file descriptors here. + error!( + "Failed to setup io-event notifications for chardev \'{}\', device not initialized", + &locked_chardev.id + ); + return None; + } + + let cloned_chardev = chardev.clone(); + let event_handler: Rc = Rc::new(move |_, _| { + let mut locked_chardev = cloned_chardev.lock().unwrap(); + + let stream = locked_chardev.listener.as_ref().unwrap().accept().unwrap(); + let connection_info = stream.link_description(); + info!( + "Chardev \'{}\' event, connection opened: {}", + &locked_chardev.id, connection_info + ); + + let stream_fd = stream.as_raw_fd(); + let stream_arc = Arc::new(Mutex::new(stream)); + + locked_chardev.stream_fd = Some(stream_fd); + locked_chardev.input = Some(stream_arc.clone()); + locked_chardev.output = Some(stream_arc.clone()); + + if let Some(dev) = &locked_chardev.dev { + dev.lock().unwrap().chardev_notify(ChardevStatus::Open); + } + + let cloned_chardev = cloned_chardev.clone(); + let inner_handler: Rc = Rc::new(move |event, _| { + if event == EventSet::IN { + let locked_chardev = cloned_chardev.lock().unwrap(); + if locked_chardev.receiver.is_none() { + error!( + "Failed to read input data from chardev \'{}\', receiver is none", + &locked_chardev.id + ); + return None; + } + let receiver = locked_chardev.receiver.clone().unwrap(); + let input = locked_chardev.input.clone().unwrap(); + drop(locked_chardev); + + let mut locked_receiver = receiver.lock().unwrap(); + let buff_size = locked_receiver.remain_size(); + if buff_size == 0 { + return None; + } + + let mut buffer = vec![0_u8; buff_size]; + if let Ok(bytes_count) = input.lock().unwrap().chr_read_raw(&mut buffer) { + locked_receiver.receive(&buffer[..bytes_count]); } else { - None + let os_error = std::io::Error::last_os_error(); + if os_error.kind() != std::io::ErrorKind::WouldBlock { + let locked_chardev = cloned_chardev.lock().unwrap(); + error!( + "Failed to read input data from chardev \'{}\', {}", + &locked_chardev.id, &os_error + ); + } } - }); - Some(vec![EventNotifier::new( - NotifierOperation::AddShared, - stream_fd, - Some(listener_fd), - EventSet::IN | EventSet::HANG_UP, - vec![inner_handler], - )]) - }), - ChardevType::File(_) => Rc::new(move |_, _| None), - } + None + } else if event & EventSet::HANG_UP == EventSet::HANG_UP { + let mut locked_chardev = cloned_chardev.lock().unwrap(); + + if let Some(dev) = &locked_chardev.dev { + dev.lock().unwrap().chardev_notify(ChardevStatus::Close); + } + + locked_chardev.input = None; + locked_chardev.output = None; + locked_chardev.stream_fd = None; + + info!( + "Chardev \'{}\' event, connection closed: {}", + &locked_chardev.id, connection_info + ); + + // Note: we use stream_arc variable here because we want to capture it and prolongate + // its lifetime with this notifier callback lifetime. It allows us to ensure + // that socket fd be valid until we unregister it from epoll_fd subscription. + let stream_fd = stream_arc.lock().unwrap().as_raw_fd(); + Some(gen_delete_notifiers(&[stream_fd])) + } else { + None + } + }); + + let listener_fd = locked_chardev.listener.as_ref().unwrap().as_raw_fd(); + Some(vec![EventNotifier::new( + NotifierOperation::AddShared, + stream_fd, + Some(listener_fd), + EventSet::IN | EventSet::HANG_UP, + vec![inner_handler], + )]) + }); + + let listener_fd = listener.as_ref().unwrap().as_raw_fd(); + Some(EventNotifier::new( + NotifierOperation::AddShared, + listener_fd, + None, + EventSet::IN, + vec![event_handler], + )) } impl EventNotifierHelper for Chardev { fn internal_notifiers(chardev: Arc>) -> Vec { - let mut notifiers = Vec::new(); - let backend = chardev.lock().unwrap().backend.clone(); - let cloned_chardev = chardev.clone(); - match backend { - ChardevType::Stdio | ChardevType::Pty => { - if let Some(input) = chardev.lock().unwrap().input.clone() { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - input.lock().unwrap().as_raw_fd(), - None, - EventSet::IN, - vec![get_notifier_handler(cloned_chardev, backend)], - )); - } - } - ChardevType::UnixSocket { .. } | ChardevType::TcpSocket { .. } => { - if let Some(listener) = chardev.lock().unwrap().listener.as_ref() { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - listener.as_raw_fd(), - None, - EventSet::IN, - vec![get_notifier_handler(cloned_chardev, backend)], - )); - } + let notifier = { + let backend = chardev.lock().unwrap().backend.clone(); + match backend { + ChardevType::Stdio => get_terminal_notifier(chardev), + ChardevType::Pty => get_terminal_notifier(chardev), + ChardevType::UnixSocket { .. } => get_socket_notifier(chardev), + ChardevType::TcpSocket { .. } => get_socket_notifier(chardev), + ChardevType::File(_) => None, } - ChardevType::File(_) => (), - } - notifiers + }; + notifier.map_or(Vec::new(), |value| vec![value]) } } -- Gitee From d261ddfd8d6bf6aa16eaf09002b0fd114c853f50 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 26 Oct 2023 19:55:41 +0800 Subject: [PATCH 1410/2187] qcow2: don't attempt to acquire locks during the exit phase If the process receives a kill signal to exit, the executed process will not be terminated, which means that the acquired lock will not be released. So don't try to obtain locks during the abnormal exit process, as this behavior may lead to deadlocks. Signed-off-by: liuxiangdong --- block_backend/src/lib.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 8faaa2fb8..2d538222d 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -359,16 +359,29 @@ pub fn create_block_backend( // NOTE: we can drain request when request in io thread. let drain = prop.iothread.is_some(); let cloned_drive_id = prop.id.clone(); + + // `qcow2` address has changed after Arc::new(), so we should use the new one. + let exit_qcow2 = new_qcow2.lock().unwrap(); + let exit_qcow2_ptr = &(*exit_qcow2) as *const Qcow2Driver as u64; + drop(exit_qcow2); let exit_notifier = Arc::new(move || { - if let Some(qcow2) = cloned_qcow2.upgrade() { - let mut locked_qcow2 = qcow2.lock().unwrap(); - info!("clean up qcow2 {:?} resources.", cloned_drive_id); - if let Err(e) = locked_qcow2.flush() { - error!("Failed to flush qcow2 {:?}", e); - } - if drain { - locked_qcow2.drain_request(); - } + // Note: Increase the reference count of `qcow2`` to avoid abnormal kill during the + // hot-unplug process, where a reference count of 0 leads to null pointer access. + let qcow2_h = cloned_qcow2.upgrade(); + if qcow2_h.is_none() { + return; + } + + // SAFETY: This callback is called only when the vm exits abnormally. + let qcow2 = unsafe { + &mut std::slice::from_raw_parts_mut(exit_qcow2_ptr as *mut Qcow2Driver, 1)[0] + }; + info!("clean up qcow2 {:?} resources.", cloned_drive_id); + if let Err(e) = qcow2.flush() { + error!("Failed to flush qcow2 {:?}", e); + } + if drain { + qcow2.drain_request(); } }) as Arc; TempCleaner::add_exit_notifier(prop.id, exit_notifier); @@ -383,4 +396,5 @@ pub fn create_block_backend( pub fn remove_block_backend(id: &str) { QCOW2_LIST.lock().unwrap().remove(id); + TempCleaner::remove_exit_notifier(id); } -- Gitee From e85c706eafad56fb49d7bae1694f476ca5385640 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 27 Oct 2023 11:27:42 +0800 Subject: [PATCH 1411/2187] fix: migration: Fix possible OOM bug The len is controlled by external user. Signed-off-by: Keqian Zhu --- migration/src/migration.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 854a6626e..98e24ca17 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -190,6 +190,11 @@ impl MigrationManager { where T: Write + Read, { + // Sanity check for len to avoid OOM. Given 1MB is enough. + if len > (1 << 20) { + bail!("Source vm_config size is too large"); + } + let mut data: Vec = Vec::new(); data.resize_with(len as usize, Default::default); fd.read_exact(&mut data)?; @@ -319,6 +324,11 @@ impl MigrationManager { where T: Write + Read, { + // Sanity check for len to avoid OOM. Given 1MB is enough. + if len > (1 << 20) { + bail!("Source MemBlock config size is too large"); + } + let mut blocks = Vec::::new(); blocks.resize_with(len as usize / (size_of::()), Default::default); fd.read_exact(unsafe { -- Gitee From 16ebc0c0c9ae855e551d86153aa034f76bfd65f0 Mon Sep 17 00:00:00 2001 From: Astro Date: Thu, 19 Oct 2023 09:28:55 +0800 Subject: [PATCH 1412/2187] micro_vm: allow SYS_clock_gettime To make stratovirt package in nixos/nixpkgs run with microvm.nix, let's allow SYS_clock_gettime in the seccomp policy. https://github.com/openeuler-mirror/stratovirt/pull/1 https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/virtualization/stratovirt Signed-off-by: Astro Signed-off-by: yezengruan --- machine/src/micro_vm/syscall.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_vm/syscall.rs index a7b4b09ed..f3bf3553b 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_vm/syscall.rs @@ -42,9 +42,9 @@ const KVM_RUN: u32 = 0xae80; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 59 syscalls +/// * x86_64-unknown-gnu: 60 syscalls /// * x86_64-unknown-musl: 58 syscalls -/// * aarch64-unknown-gnu: 57 syscalls +/// * aarch64-unknown-gnu: 58 syscalls /// * aarch64-unknown-musl: 57 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { @@ -130,6 +130,8 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_mprotect), BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_connect), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_clock_gettime), madvise_rule(), ] } -- Gitee From 9eca16adf7f70b279b4176aaad3865b429725c98 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Fri, 27 Oct 2023 23:06:11 +0800 Subject: [PATCH 1413/2187] fix cargo build warn info in new rustc version: warning: private item shadows public glob re-export Signed-off-by: Yan Wen --- virtio/src/vhost/user/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index b3f8896e7..2f0dc96f0 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -18,12 +18,12 @@ mod message; mod net; mod sock; +pub use self::block::Block; pub use self::client::*; pub use self::fs::*; pub use self::message::*; +pub use self::net::Net; pub use self::sock::*; -pub use block::Block; -pub use net::Net; use std::sync::{Arc, Mutex}; @@ -33,7 +33,7 @@ use crate::{ vhost::{VhostIoHandler, VhostNotify}, NotifyEventFds, VirtioBase, VirtioInterrupt, }; -use client::VhostUserClient; + use machine_manager::event_loop::register_event_helper; use util::loop_context::EventNotifierHelper; -- Gitee From 79e1034f2e6ff38989a6f1d312225a8dfd11b4fe Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Fri, 27 Oct 2023 17:53:14 +0300 Subject: [PATCH 1414/2187] Allow efi-bios size be less then memory-region we check that if pflash-param is read-only. And if so, we allow size smaller then specified memory region size. We fail if file-size is zero or if size is greater then specified region size. This allows to boot using EFI-Bios image with size less then 64M without padding it with zeroes Signed-off-by: Dmitry Skorodumov --- devices/src/legacy/pflash.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index ebb9e7d58..e123fee80 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -93,15 +93,12 @@ impl PFlash { } if let Some(fd) = backend.as_ref() { let len = fd.metadata().unwrap().len(); - if len < size { + if len > size || len == 0 || (!read_only && len != size) { bail!( - "Mmap requires 0x{:X} bytes, given file provides 0x{:X} bytes", - size, - len + "Invalid flash file: Region size 0x{size:X}, file size 0x{len:X}; read_only {read_only}" ); } } - let num_devices: u32 = if device_width == 0 { 1 } else { -- Gitee From ca2bcf12e73329f474a5d78badf44ad066b1d2da Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 6 Sep 2023 15:28:32 +0800 Subject: [PATCH 1415/2187] debug/qmp: support query vcpu register It is only supported by the aarch64 architecture. We will query the value of the vcpu register through the KVM_GET_ONE_REG ioctl. It should be noted that the VM will pause during the query and then resume. Signed-off-by: yezengruan --- docs/qmp.md | 23 ++++++++++++++++++++ machine/src/standard_vm/aarch64/mod.rs | 24 ++++++++++++++++++++- machine/src/standard_vm/mod.rs | 27 ++++++++++++++++++++++++ machine/src/standard_vm/x86_64/mod.rs | 4 ++++ machine_manager/src/machine.rs | 11 ++++++++-- machine_manager/src/qmp/qmp_schema.rs | 29 ++++++++++++++++++++++++++ machine_manager/src/qmp/qmp_socket.rs | 3 ++- 7 files changed, 117 insertions(+), 4 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index 254247938..7a604250c 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -445,6 +445,29 @@ Now there are 5 states during snapshot: <- {"return":{"status":"completed"}} ``` +## Debug + +### query-vcpu-reg + +Query vcpu register value. + +#### Arguments + +* `addr` : the register address. +* `vcpu` : vcpu id. + +#### Notes + +- The VM will pause during the query and then resume. +- Only aarch64 is supported now. + +#### Example + +```json +-> {"execute": "query-vcpu-reg", "arguments": {"addr": "603000000013df1a", "vcpu": 0}} +<- {"return": "348531C5"} +``` + ## Event Notification When some events happen, connected client will receive QMP events. diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 307fbdd04..8f20cb6bf 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -42,7 +42,8 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{ - CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuTopology, CPU, PMU_INTR, PPI_BASE, + CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuLifecycleState, CpuTopology, CPU, + PMU_INTR, PPI_BASE, }; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; use devices::acpi::power::PowerDev; @@ -382,6 +383,27 @@ impl StdMachine { let machine_ram = self.get_vm_ram(); machine_ram.mtree(0_u32); } + + pub fn get_vcpu_reg_val(&self, addr: u64, vcpu_index: usize) -> Option { + if let Some(vcpu) = self.get_cpus().get(vcpu_index) { + let (cpu_state, _) = vcpu.state(); + let cpu_state = *cpu_state.lock().unwrap(); + if cpu_state != CpuLifecycleState::Paused { + self.pause(); + } + + let value = match vcpu.fd().get_one_reg(addr) { + Ok(value) => Some(value), + _ => None, + }; + + if cpu_state != CpuLifecycleState::Paused { + self.resume(); + } + return value; + } + None + } } impl StdMachineOps for StdMachine { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 1b6610d88..e2fcef6af 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1970,6 +1970,33 @@ impl DeviceInterface for StdMachine { ), } } + + fn query_vcpu_reg(&self, args: qmp_schema::QueryVcpuRegArgument) -> Response { + let vcpu = args.vcpu; + let addr = match u64::from_str_radix(args.addr.as_str(), 16) { + Ok(addr) => addr, + _ => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "address not in hexadecimal".to_string(), + ), + None, + ); + } + }; + + if let Some(val) = self.get_vcpu_reg_val(addr, vcpu) { + return Response::create_response( + serde_json::to_value(format!("{:X}", val)).unwrap(), + None, + ); + } + + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError("not supported or arguments error".to_string()), + None, + ) + } } fn parse_blockdev(args: &BlockDevAddArgument) -> Result { diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 5c9cbce3d..1f8698c34 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -298,6 +298,10 @@ impl StdMachine { let machine_ram = self.get_vm_ram(); machine_ram.mtree(0_u32); } + + pub fn get_vcpu_reg_val(&self, _addr: u64, _vcpu: usize) -> Option { + None + } } impl StdMachineOps for StdMachine { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index a6ab65055..3b7efa6bb 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -22,8 +22,8 @@ use crate::qmp::qmp_schema::{ BlockDevAddArgument, BlockdevSnapshotInternalArgument, CameraDevAddArgument, CharDevAddArgument, ChardevInfo, Cmd, CmdLine, CmdParameter, DeviceAddArgument, DeviceProps, Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, MachineInfo, - MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, Target, - TypeLists, UpdateRegionArgument, + MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, + QueryVcpuRegArgument, Target, TypeLists, UpdateRegionArgument, }; #[derive(Clone)] @@ -483,6 +483,13 @@ pub trait DeviceInterface { ) -> Response { Response::create_empty_response() } + + fn query_vcpu_reg(&self, _args: QueryVcpuRegArgument) -> Response { + Response::create_error_response( + QmpErrorClass::GenericError("query_vcpu_reg is not supported yet".to_string()), + None, + ) + } } /// Migrate external api diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 9f7fd60dd..c138f4f8a 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -437,6 +437,12 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "query-vcpu-reg")] + query_vcpu_reg { + arguments: query_vcpu_reg, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, } /// Command trait for Deserialize and find back Response. @@ -2383,6 +2389,29 @@ impl Command for query_mem { } } +/// query-vcpu-reg +/// +/// # Arguments +/// +/// * `addr` - the register addr will be query. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "query-vcpu-reg", +/// "arguments": { "addr": "603000000013df1a", "vcpu": 0 } } +/// <- { "return": "348531C5" } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct query_vcpu_reg { + #[serde(rename = "addr")] + pub addr: String, + #[serde(rename = "vcpu")] + pub vcpu: usize, +} +pub type QueryVcpuRegArgument = query_vcpu_reg; + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 1b2215e8b..86c59aa9b 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -469,7 +469,8 @@ fn qmp_command_exec( (update_region, update_region), (human_monitor_command, human_monitor_command), (blockdev_snapshot_internal_sync, blockdev_snapshot_internal_sync), - (blockdev_snapshot_delete_internal_sync, blockdev_snapshot_delete_internal_sync) + (blockdev_snapshot_delete_internal_sync, blockdev_snapshot_delete_internal_sync), + (query_vcpu_reg, query_vcpu_reg) ); // Handle the Qmp command which macro can't cover -- Gitee From 6d9b72139e9314cc702d0c06e58ed2af6e2a4d0a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 6 Sep 2023 10:06:32 +0800 Subject: [PATCH 1416/2187] debug/qmp: support query VM memory gpa In some VM abnormal scenarios, we hope to obtain the instructions executed by VM, let's support gpa query through qmp. Signed-off-by: yezengruan --- docs/qmp.md | 15 +++++++++++++++ machine/src/standard_vm/mod.rs | 26 ++++++++++++++++++++++++++ machine_manager/src/machine.rs | 9 ++++++++- machine_manager/src/qmp/qmp_schema.rs | 26 ++++++++++++++++++++++++++ machine_manager/src/qmp/qmp_socket.rs | 3 ++- 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index 7a604250c..197282eae 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -468,6 +468,21 @@ Query vcpu register value. <- {"return": "348531C5"} ``` +### query-mem-gpa + +Query the value of the guest physical address. + +#### Arguments + +* `gpa` : the guest physical address. + +#### Example + +```json +-> {"execute": "query-mem-gpa", "arguments": {"gpa": "13c4d1d00" }} +<- {"return": "B9000001"} +``` + ## Event Notification When some events happen, connected client will receive QMP events. diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index e2fcef6af..51e8ac4d0 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1997,6 +1997,32 @@ impl DeviceInterface for StdMachine { None, ) } + + fn query_mem_gpa(&self, args: qmp_schema::QueryMemGpaArgument) -> Response { + let gpa = match u64::from_str_radix(args.gpa.as_str(), 16) { + Ok(gpa) => gpa, + _ => { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "address not in hexadecimal".to_string(), + ), + None, + ); + } + }; + + match self.sys_mem.read_object::(GuestAddress(gpa)) { + Ok(val) => { + Response::create_response(serde_json::to_value(format!("{:X}", val)).unwrap(), None) + } + _ => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "this gpa does not support query".to_string(), + ), + None, + ), + } + } } fn parse_blockdev(args: &BlockDevAddArgument) -> Result { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 3b7efa6bb..12e5052eb 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -23,7 +23,7 @@ use crate::qmp::qmp_schema::{ CharDevAddArgument, ChardevInfo, Cmd, CmdLine, CmdParameter, DeviceAddArgument, DeviceProps, Events, GicCap, HumanMonitorCmdArgument, IothreadInfo, KvmInfo, MachineInfo, MigrateCapabilities, NetDevAddArgument, PropList, QmpCommand, QmpErrorClass, QmpEvent, - QueryVcpuRegArgument, Target, TypeLists, UpdateRegionArgument, + QueryMemGpaArgument, QueryVcpuRegArgument, Target, TypeLists, UpdateRegionArgument, }; #[derive(Clone)] @@ -490,6 +490,13 @@ pub trait DeviceInterface { None, ) } + + fn query_mem_gpa(&self, _args: QueryMemGpaArgument) -> Response { + Response::create_error_response( + QmpErrorClass::GenericError("query_mem_gpa is not supported yet".to_string()), + None, + ) + } } /// Migrate external api diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index c138f4f8a..7c6c39efc 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -191,6 +191,13 @@ pub enum QmpCommand { #[serde(default, skip_serializing_if = "Option::is_none")] id: Option, }, + #[serde(rename = "query-mem-gpa")] + query_mem_gpa { + #[serde(default)] + arguments: query_mem_gpa, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, #[serde(rename = "query-balloon")] query_balloon { #[serde(default)] @@ -2412,6 +2419,25 @@ pub struct query_vcpu_reg { } pub type QueryVcpuRegArgument = query_vcpu_reg; +/// query-mem-gpa +/// +/// This command +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "query-mem-gpa", "arguments": { "gpa": "13c4d1d00" } } +/// <- { "return": "B9000001" } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct query_mem_gpa { + #[serde(rename = "gpa")] + pub gpa: String, +} + +pub type QueryMemGpaArgument = query_mem_gpa; + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 86c59aa9b..08dac9660 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -470,7 +470,8 @@ fn qmp_command_exec( (human_monitor_command, human_monitor_command), (blockdev_snapshot_internal_sync, blockdev_snapshot_internal_sync), (blockdev_snapshot_delete_internal_sync, blockdev_snapshot_delete_internal_sync), - (query_vcpu_reg, query_vcpu_reg) + (query_vcpu_reg, query_vcpu_reg), + (query_mem_gpa, query_mem_gpa) ); // Handle the Qmp command which macro can't cover -- Gitee From c20a27262f26cdd3e5c1a7fe5fa24ba8688f77a9 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 10 Oct 2023 10:04:36 +0800 Subject: [PATCH 1417/2187] machine: create MachineBase structure LightMachine has the same members as StdMachine, which are extrated into MachineBase. Signed-off-by: Mingwang Li --- machine/src/lib.rs | 97 ++++++++- machine/src/micro_vm/mod.rs | 268 +++++++++++-------------- machine/src/standard_vm/aarch64/mod.rs | 259 +++++++++++------------- machine/src/standard_vm/x86_64/mod.rs | 191 +++++++----------- 4 files changed, 407 insertions(+), 408 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8b13f9c5d..381964819 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -45,7 +45,7 @@ use address_space::{ }; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; -use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CPU}; +use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(feature = "scream")] use devices::misc::scream::Scream; @@ -79,9 +79,9 @@ use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, - parse_virtio_serial, parse_virtserialport, parse_vsock, BootIndexInfo, DriveFile, Incoming, - MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, - PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + parse_virtio_serial, parse_virtserialport, parse_vsock, BootIndexInfo, BootSource, DriveFile, + Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, + PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::config::{ parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, @@ -110,6 +110,95 @@ use virtio::{ VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; +/// Machine structure include base members. +struct MachineBase { + /// `vCPU` topology, support sockets, cores, threads. + cpu_topo: CpuTopology, + /// `vCPU` devices. + cpus: Vec>, + /// `vCPU` family and feature configuration. Only supports aarch64 currently. + #[cfg(target_arch = "aarch64")] + cpu_features: CPUFeatures, + /// Interrupt controller device. + #[cfg(target_arch = "aarch64")] + irq_chip: Option>, + /// Memory address space. + sys_mem: Arc, + // IO address space. + #[cfg(target_arch = "x86_64")] + sys_io: Arc, + /// System bus. + sysbus: SysBus, + /// VM running state. + vm_state: Arc<(Mutex, Condvar)>, + /// Vm boot_source config. + boot_source: Arc>, + /// All configuration information of virtual machine. + vm_config: Arc>, + /// List of guest NUMA nodes information. + numa_nodes: Option, + /// Drive backend files. + drive_files: Arc>>, + /// machine all backend memory region tree + machine_ram: Arc, +} + +impl MachineBase { + pub fn new( + vm_config: &VmConfig, + free_irqs: (i32, i32), + mmio_region: (u64, u64), + ) -> Result { + let cpu_topo = CpuTopology::new( + vm_config.machine_config.nr_cpus, + vm_config.machine_config.nr_sockets, + vm_config.machine_config.nr_dies, + vm_config.machine_config.nr_clusters, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_threads, + vm_config.machine_config.max_cpus, + ); + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "SysMem"), + "sys_mem", + ) + .with_context(|| MachineError::CrtIoSpaceErr)?; + + #[cfg(target_arch = "x86_64")] + let sys_io = AddressSpace::new(Region::init_container_region(1 << 16, "SysIo"), "SysIo") + .with_context(|| MachineError::CrtIoSpaceErr)?; + let sysbus = SysBus::new( + #[cfg(target_arch = "x86_64")] + &sys_io, + &sys_mem, + free_irqs, + mmio_region, + ); + + Ok(MachineBase { + cpu_topo, + cpus: Vec::new(), + #[cfg(target_arch = "aarch64")] + cpu_features: (&vm_config.machine_config.cpu_config).into(), + #[cfg(target_arch = "aarch64")] + irq_chip: None, + sys_mem, + #[cfg(target_arch = "x86_64")] + sys_io, + sysbus, + vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), + boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), + vm_config: Arc::new(Mutex::new(vm_config.clone())), + numa_nodes: None, + drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), + machine_ram: Arc::new(Region::init_container_region( + u64::max_value(), + "MachineRam", + )), + }) + } +} + pub trait MachineOps { fn build_smbios( &self, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index b823f1333..539442ce8 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -52,13 +52,14 @@ use super::Result as MachineResult; use super::{error::MachineError, MachineOps}; #[cfg(target_arch = "x86_64")] use crate::vm_state; +use crate::MachineBase; use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; #[cfg(target_arch = "aarch64")] use cpu::PMU_INTR; -use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState}; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; #[cfg(target_arch = "x86_64")] @@ -72,8 +73,8 @@ use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GI #[cfg(target_arch = "x86_64")] use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, BootSource, ConfigCheck, DiskFormat, - DriveFile, Incoming, MigrateMode, NetworkInterfaceConfig, NumaNodes, SerialConfig, VmConfig, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, ConfigCheck, DiskFormat, DriveFile, + Incoming, MigrateMode, NetworkInterfaceConfig, NumaNodes, SerialConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }; use machine_manager::event; @@ -159,37 +160,10 @@ impl MmioReplaceableInfo { /// A wrapper around creating and using a kvm-based micro VM. pub struct LightMachine { - // `vCPU` topology, support sockets, cores, threads. - cpu_topo: CpuTopology, - // `vCPU` family and feature configuration. Only supports aarch64 currently. - #[cfg(target_arch = "aarch64")] - cpu_feature: CPUFeatures, - // `vCPU` devices. - cpus: Vec>, - // Interrupt controller device. - #[cfg(target_arch = "aarch64")] - irq_chip: Option>, - // Memory address space. - sys_mem: Arc, - // IO address space. - #[cfg(target_arch = "x86_64")] - sys_io: Arc, - // System bus. - sysbus: SysBus, + // Machine base members. + base: MachineBase, // All replaceable device information. replaceable_info: MmioReplaceableInfo, - // VM running state. - vm_state: Arc<(Mutex, Condvar)>, - // Vm boot_source config. - boot_source: Arc>, - // All configuration information of virtual machine. - vm_config: Arc>, - // List of guest NUMA nodes information. - numa_nodes: Option, - // Drive backend files. - drive_files: Arc>>, - // All backend memory region tree. - machine_ram: Arc, } impl LightMachine { @@ -199,56 +173,16 @@ impl LightMachine { /// /// * `vm_config` - Represents the configuration for VM. pub fn new(vm_config: &VmConfig) -> MachineResult { - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "SysMem"), - "sys_mem", - ) - .with_context(|| MachineError::CrtMemSpaceErr)?; - #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16, "SysIo"), "SysIo") - .with_context(|| MachineError::CrtIoSpaceErr)?; let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, ); - let sysbus = SysBus::new( - #[cfg(target_arch = "x86_64")] - &sys_io, - &sys_mem, - free_irqs, - mmio_region, - ); - - // Machine state init - let vm_state = Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())); + let base = MachineBase::new(vm_config, free_irqs, mmio_region)?; Ok(LightMachine { - cpu_topo: CpuTopology::new( - vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_sockets, - vm_config.machine_config.nr_dies, - vm_config.machine_config.nr_clusters, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_threads, - vm_config.machine_config.max_cpus, - ), - cpus: Vec::new(), - #[cfg(target_arch = "aarch64")] - cpu_feature: (&vm_config.machine_config.cpu_config).into(), - #[cfg(target_arch = "aarch64")] - irq_chip: None, - sys_mem, - #[cfg(target_arch = "x86_64")] - sys_io, - sysbus, + base, replaceable_info: MmioReplaceableInfo::new(), - boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), - vm_state, - vm_config: Arc::new(Mutex::new(vm_config.clone())), - numa_nodes: None, - drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), - machine_ram: Arc::new(Region::init_container_region(u64::max_value(), "pc.ram")), }) } @@ -272,9 +206,9 @@ impl LightMachine { } pub fn mem_show(&self) { - self.sys_mem.memspace_show(); + self.base.sys_mem.memspace_show(); #[cfg(target_arch = "x86_64")] - self.sys_io.memspace_show(); + self.base.sys_io.memspace_show(); let machine_ram = self.get_vm_ram(); machine_ram.mtree(0_u32); @@ -287,7 +221,7 @@ impl LightMachine { BlkDevConfig::default(), self.get_drive_files(), ))); - let virtio_mmio = VirtioMmioDevice::new(&self.sys_mem, block.clone()); + let virtio_mmio = VirtioMmioDevice::new(&self.base.sys_mem, block.clone()); rpl_devs.push(virtio_mmio); MigrationManager::register_device_instance( @@ -298,7 +232,7 @@ impl LightMachine { } for id in 0..MMIO_REPLACEABLE_NET_NR { let net = Arc::new(Mutex::new(Net::new(NetworkInterfaceConfig::default()))); - let virtio_mmio = VirtioMmioDevice::new(&self.sys_mem, net.clone()); + let virtio_mmio = VirtioMmioDevice::new(&self.base.sys_mem, net.clone()); rpl_devs.push(virtio_mmio); MigrationManager::register_device_instance( @@ -308,7 +242,7 @@ impl LightMachine { ); } - let mut region_base = self.sysbus.min_free_base; + let mut region_base = self.base.sysbus.min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; for (id, dev) in rpl_devs.into_iter().enumerate() { self.replaceable_info @@ -325,18 +259,18 @@ impl LightMachine { VirtioMmioState::descriptor(), VirtioMmioDevice::realize( dev, - &mut self.sysbus, + &mut self.base.sysbus, region_base, MEM_LAYOUT[LayoutEntryType::Mmio as usize].1, #[cfg(target_arch = "x86_64")] - &self.boot_source, + &self.base.boot_source, ) .with_context(|| MicroVmError::RlzVirtioMmioErr)?, &id.to_string(), ); region_base += region_size; } - self.sysbus.min_free_base = region_base; + self.base.sysbus.min_free_base = region_base; Ok(()) } @@ -490,7 +424,7 @@ impl LightMachine { fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { let features = vcpu_cfg.unwrap_or_default(); if features.pmu { - for cpu in self.cpus.iter() { + for cpu in self.base.cpus.iter() { cpu.init_pmu()?; } } @@ -583,8 +517,8 @@ impl MachineOps for LightMachine { v2: Some(v2), }; let irq_chip = InterruptController::new(&intc_conf)?; - self.irq_chip = Some(Arc::new(irq_chip)); - self.irq_chip.as_ref().unwrap().realize()?; + self.base.irq_chip = Some(Arc::new(irq_chip)); + self.base.irq_chip.as_ref().unwrap().realize()?; Ok(()) } @@ -593,7 +527,7 @@ impl MachineOps for LightMachine { &self, fwcfg: Option<&Arc>>, ) -> MachineResult { - let boot_source = self.boot_source.lock().unwrap(); + let boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 @@ -603,14 +537,14 @@ impl MachineOps for LightMachine { kernel: boot_source.kernel_file.clone(), initrd, kernel_cmdline: boot_source.kernel_cmdline.to_string(), - cpu_count: self.cpu_topo.nrcpus, + cpu_count: self.base.cpu_topo.nrcpus, gap_range: (gap_start, gap_end - gap_start), ioapic_addr: MEM_LAYOUT[LayoutEntryType::IoApic as usize].0 as u32, lapic_addr: MEM_LAYOUT[LayoutEntryType::LocalApic as usize].0 as u32, ident_tss_range: None, prot64_mode: true, }; - let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) + let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) .with_context(|| MachineError::LoadKernErr)?; Ok(CPUBootConfig { @@ -634,7 +568,7 @@ impl MachineOps for LightMachine { &self, fwcfg: Option<&Arc>>, ) -> MachineResult { - let mut boot_source = self.boot_source.lock().unwrap(); + let mut boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); let bootloader_config = BootLoaderConfig { @@ -642,7 +576,7 @@ impl MachineOps for LightMachine { initrd, mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, }; - let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) + let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) .with_context(|| MachineError::LoadKernErr)?; if let Some(rd) = &mut boot_source.initrd { rd.initrd_addr = layout.initrd_start; @@ -659,31 +593,31 @@ impl MachineOps for LightMachine { &mut self, dev: VirtioMmioDevice, ) -> MachineResult>> { - let region_base = self.sysbus.min_free_base; + let region_base = self.base.sysbus.min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; let realized_virtio_mmio_device = VirtioMmioDevice::realize( dev, - &mut self.sysbus, + &mut self.base.sysbus, region_base, region_size, #[cfg(target_arch = "x86_64")] - &self.boot_source, + &self.base.boot_source, ) .with_context(|| MicroVmError::RlzVirtioMmioErr)?; - self.sysbus.min_free_base += region_size; + self.base.sysbus.min_free_base += region_size; Ok(realized_virtio_mmio_device) } fn get_sys_mem(&mut self) -> &Arc { - &self.sys_mem + &self.base.sys_mem } fn get_vm_config(&self) -> Arc> { - self.vm_config.clone() + self.base.vm_config.clone() } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.vm_state + &self.base.vm_state } fn get_migrate_info(&self) -> Incoming { @@ -695,22 +629,22 @@ impl MachineOps for LightMachine { } fn get_sys_bus(&mut self) -> &SysBus { - &self.sysbus + &self.base.sysbus } fn get_vm_ram(&self) -> &Arc { - &self.machine_ram + &self.base.machine_ram } fn get_numa_nodes(&self) -> &Option { - &self.numa_nodes + &self.base.numa_nodes } #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { PL031::realize( PL031::default(), - &mut self.sysbus, + &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, ) @@ -741,11 +675,11 @@ impl MachineOps for LightMachine { let serial = Serial::new(config.clone()); serial .realize( - &mut self.sysbus, + &mut self.base.sysbus, region_base, region_size, #[cfg(target_arch = "aarch64")] - &self.boot_source, + &self.base.boot_source, ) .with_context(|| "Failed to realize serial device.")?; Ok(()) @@ -759,11 +693,17 @@ impl MachineOps for LightMachine { let device_cfg = parse_net(vm_config, cfg_args)?; if device_cfg.vhost_type.is_some() { let device = if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { - let net = Arc::new(Mutex::new(VhostKern::Net::new(&device_cfg, &self.sys_mem))); - VirtioMmioDevice::new(&self.sys_mem, net) + let net = Arc::new(Mutex::new(VhostKern::Net::new( + &device_cfg, + &self.base.sys_mem, + ))); + VirtioMmioDevice::new(&self.base.sys_mem, net) } else { - let net = Arc::new(Mutex::new(VhostUser::Net::new(&device_cfg, &self.sys_mem))); - VirtioMmioDevice::new(&self.sys_mem, net) + let net = Arc::new(Mutex::new(VhostUser::Net::new( + &device_cfg, + &self.base.sys_mem, + ))); + VirtioMmioDevice::new(&self.base.sys_mem, net) }; self.realize_virtio_mmio_device(device)?; } else { @@ -803,15 +743,15 @@ impl MachineOps for LightMachine { } fn get_drive_files(&self) -> Arc>> { - self.drive_files.clone() + self.base.drive_files.clone() } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { let mut locked_vm = vm.lock().unwrap(); // trace for lightmachine - trace_sysbus(&locked_vm.sysbus); - trace_vm_state(&locked_vm.vm_state); + trace_sysbus(&locked_vm.base.sysbus); + trace_vm_state(&locked_vm.base.vm_state); let topology = CPUTopology::new().set_topology(( vm_config.machine_config.nr_threads, @@ -819,12 +759,12 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_dies, )); trace_cpu_topo(&topology); - locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, #[cfg(target_arch = "x86_64")] - &locked_vm.sys_io, - &locked_vm.sys_mem, + &locked_vm.base.sys_io, + &locked_vm.base.sys_mem, vm_config.machine_config.nr_cpus, )?; @@ -849,7 +789,7 @@ impl MachineOps for LightMachine { }; // vCPUs init - locked_vm.cpus.extend(::init_vcpu( + locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, &topology, @@ -869,7 +809,7 @@ impl MachineOps for LightMachine { }; // vCPUs init,and apply CPU features (for aarch64) - locked_vm.cpus.extend(::init_vcpu( + locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, &topology, @@ -895,6 +835,7 @@ impl MachineOps for LightMachine { .with_context(|| MachineError::GenFdtErr)?; let fdt_vec = fdt_helper.finish()?; locked_vm + .base .sys_mem .write( &mut fdt_vec.as_slice(), @@ -919,7 +860,11 @@ impl MachineOps for LightMachine { } fn run(&self, paused: bool) -> MachineResult<()> { - self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) + self.vm_start( + paused, + &self.base.cpus, + &mut self.base.vm_state.0.lock().unwrap(), + ) } } @@ -944,7 +889,7 @@ impl MachineLifecycle for LightMachine { fn destroy(&self) -> bool { let vmstate = { - let state = self.vm_state.deref().0.lock().unwrap(); + let state = self.base.vm_state.deref().0.lock().unwrap(); *state }; @@ -960,7 +905,7 @@ impl MachineLifecycle for LightMachine { fn reset(&mut self) -> bool { // For micro vm, the reboot command is equivalent to the shutdown command. - for cpu in self.cpus.iter() { + for cpu in self.base.cpus.iter() { let (cpu_state, _) = cpu.state(); *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; } @@ -970,10 +915,10 @@ impl MachineLifecycle for LightMachine { fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { if let Err(e) = self.vm_state_transfer( - &self.cpus, + &self.base.cpus, #[cfg(target_arch = "aarch64")] - &self.irq_chip, - &mut self.vm_state.0.lock().unwrap(), + &self.base.irq_chip, + &mut self.base.vm_state.0.lock().unwrap(), old, new, ) { @@ -995,7 +940,8 @@ impl MachineAddressInterface for LightMachine { return true; } let length = data.len() as u64; - self.sys_io + self.base + .sys_io .read(&mut data, GuestAddress(addr), length) .is_ok() } @@ -1003,21 +949,24 @@ impl MachineAddressInterface for LightMachine { #[cfg(target_arch = "x86_64")] fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { let count = data.len() as u64; - self.sys_io + self.base + .sys_io .write(&mut data, GuestAddress(addr), count) .is_ok() } fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { let length = data.len() as u64; - self.sys_mem + self.base + .sys_mem .read(&mut data, GuestAddress(addr), length) .is_ok() } fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { let count = data.len() as u64; - self.sys_mem + self.base + .sys_mem .write(&mut data, GuestAddress(addr), count) .is_ok() } @@ -1045,10 +994,13 @@ impl DeviceInterface for LightMachine { fn query_cpus(&self) -> Response { let mut cpu_vec: Vec = Vec::new(); - for cpu_index in 0..self.cpu_topo.max_cpus { - if self.cpu_topo.get_mask(cpu_index as usize) == 1 { - let thread_id = self.cpus[cpu_index as usize].tid(); - let cpu_instance = self.cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); + for cpu_index in 0..self.base.cpu_topo.max_cpus { + if self.base.cpu_topo.get_mask(cpu_index as usize) == 1 { + let thread_id = self.base.cpus[cpu_index as usize].tid(); + let cpu_instance = self + .base + .cpu_topo + .get_topo_instance_for_qmp(cpu_index as usize); let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") @@ -1087,9 +1039,12 @@ impl DeviceInterface for LightMachine { #[cfg(target_arch = "aarch64")] let cpu_type = String::from("host-aarch64-cpu"); - for cpu_index in 0..self.cpu_topo.max_cpus { - if self.cpu_topo.get_mask(cpu_index as usize) == 0 { - let cpu_instance = self.cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); + for cpu_index in 0..self.base.cpu_topo.max_cpus { + if self.base.cpu_topo.get_mask(cpu_index as usize) == 0 { + let cpu_instance = self + .base + .cpu_topo + .get_topo_instance_for_qmp(cpu_index as usize); let hotpluggable_cpu = qmp_schema::HotpluggableCPU { type_: cpu_type.clone(), vcpus_count: 1, @@ -1098,7 +1053,10 @@ impl DeviceInterface for LightMachine { }; hotplug_vec.push(serde_json::to_value(hotpluggable_cpu).unwrap()); } else { - let cpu_instance = self.cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); + let cpu_instance = self + .base + .cpu_topo + .get_topo_instance_for_qmp(cpu_index as usize); let hotpluggable_cpu = qmp_schema::HotpluggableCPU { type_: cpu_type.clone(), vcpus_count: 1, @@ -1429,7 +1387,7 @@ impl MachineExternalInterface for LightMachine {} impl EventLoopManager for LightMachine { fn loop_should_exit(&self) -> bool { - let vmstate = self.vm_state.deref().0.lock().unwrap(); + let vmstate = self.base.vm_state.deref().0.lock().unwrap(); *vmstate == KvmVmState::Shutdown } @@ -1562,26 +1520,26 @@ impl CompileFDTHelper for LightMachine { // Generate CPU topology let cpu_map_node_dep = fdt.begin_node("cpu-map")?; - for socket in 0..self.cpu_topo.sockets { + for socket in 0..self.base.cpu_topo.sockets { let sock_name = format!("cluster{}", socket); let sock_node_dep = fdt.begin_node(&sock_name)?; - for cluster in 0..self.cpu_topo.clusters { + for cluster in 0..self.base.cpu_topo.clusters { let clster = format!("cluster{}", cluster); let cluster_node_dep = fdt.begin_node(&clster)?; - for core in 0..self.cpu_topo.cores { + for core in 0..self.base.cpu_topo.cores { let core_name = format!("core{}", core); let core_node_dep = fdt.begin_node(&core_name)?; - for thread in 0..self.cpu_topo.threads { + for thread in 0..self.base.cpu_topo.threads { let thread_name = format!("thread{}", thread); let thread_node_dep = fdt.begin_node(&thread_name)?; - let vcpuid = self.cpu_topo.threads - * self.cpu_topo.cores - * self.cpu_topo.clusters + let vcpuid = self.base.cpu_topo.threads + * self.base.cpu_topo.cores + * self.base.cpu_topo.clusters * socket - + self.cpu_topo.threads * self.cpu_topo.cores * cluster - + self.cpu_topo.threads * core + + self.base.cpu_topo.threads * self.base.cpu_topo.cores * cluster + + self.base.cpu_topo.threads * core + thread; fdt.set_property_u32( "cpu", @@ -1597,8 +1555,12 @@ impl CompileFDTHelper for LightMachine { } fdt.end_node(cpu_map_node_dep)?; - for cpu_index in 0..self.cpu_topo.nrcpus { - let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); + for cpu_index in 0..self.base.cpu_topo.nrcpus { + let mpidr = self.base.cpus[cpu_index as usize] + .arch() + .lock() + .unwrap() + .mpidr(); let node = format!("cpu@{:x}", mpidr); let mpidr_node_dep = fdt.begin_node(&node)?; @@ -1608,7 +1570,7 @@ impl CompileFDTHelper for LightMachine { )?; fdt.set_property_string("device_type", "cpu")?; fdt.set_property_string("compatible", "arm,arm-v8")?; - if self.cpu_topo.max_cpus > 1 { + if self.base.cpu_topo.max_cpus > 1 { fdt.set_property_string("enable-method", "psci")?; } fdt.set_property_u64("reg", mpidr & 0x007F_FFFF)?; @@ -1618,7 +1580,7 @@ impl CompileFDTHelper for LightMachine { fdt.end_node(cpus_node_dep)?; // CPU Features : PMU - if self.cpu_feature.pmu { + if self.base.cpu_features.pmu { generate_pmu_node(fdt)?; } @@ -1627,7 +1589,7 @@ impl CompileFDTHelper for LightMachine { fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.sys_mem.memory_end_address().raw_value() + let mem_size = self.base.sys_mem.memory_end_address().raw_value() - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let node = "memory"; let memory_node_dep = fdt.begin_node(node)?; @@ -1670,7 +1632,7 @@ impl CompileFDTHelper for LightMachine { fdt.set_property_string("method", "hvc")?; fdt.end_node(psci_node_dep)?; - for dev in self.sysbus.devices.iter() { + for dev in self.base.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); let dev_type = locked_dev.sysbusdev_base().dev_type; let sys_res = locked_dev.sysbusdev_base().res; @@ -1687,7 +1649,7 @@ impl CompileFDTHelper for LightMachine { fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "chosen"; - let boot_source = self.boot_source.lock().unwrap(); + let boot_source = self.base.boot_source.lock().unwrap(); let chosen_node_dep = fdt.begin_node(node)?; let cmdline = &boot_source.kernel_cmdline.to_string(); @@ -1721,7 +1683,11 @@ impl device_tree::CompileFDT for LightMachine { self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; // SAFETY: ARM architecture must have interrupt controllers in user mode. - self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; + self.base + .irq_chip + .as_ref() + .unwrap() + .generate_fdt_node(fdt)?; fdt.end_node(node_dep)?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 8f20cb6bf..294402d4a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -26,7 +26,7 @@ use log::{error, info, warn}; use vmm_sys_util::eventfd::EventFd; use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; -use crate::MachineOps; +use crate::{MachineBase, MachineOps}; use acpi::{ processor_append_priv_res, AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, @@ -62,8 +62,8 @@ use machine_manager::config::ShutdownAction; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, - NumaNodes, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, DriveFile, Incoming, MigrateMode, NumaNode, NumaNodes, + PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -139,27 +139,12 @@ const IRQ_MAP: &[(i32, i32)] = &[ /// Standard machine structure. pub struct StdMachine { - /// `vCPU` topology, support sockets, cores, threads. - cpu_topo: CpuTopology, - /// `vCPU` devices. - cpus: Vec>, - cpu_features: CPUFeatures, - // Interrupt controller device. - irq_chip: Option>, - /// Memory address space. - pub sys_mem: Arc, - /// System bus. - sysbus: SysBus, + /// Machine base members. + base: MachineBase, /// PCI/PCIe host bridge. pci_host: Arc>, - /// VM running state. - vm_state: Arc<(Mutex, Condvar)>, - /// Vm boot_source config. - boot_source: Arc>, /// VM power button, handle VM `Shutdown` event. pub power_button: Arc, - /// All configuration information of virtual machine. - vm_config: Arc>, /// Shutdown request, handle VM `shutdown` event. shutdown_req: Arc, /// Reset request, handle VM `Reset` event. @@ -170,53 +155,27 @@ pub struct StdMachine { resume_req: Arc, /// Device Tree Blob. dtb_vec: Vec, - /// List of guest NUMA nodes information. - numa_nodes: Option, /// List contains the boot order of boot devices. boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, - /// Drive backend files. - drive_files: Arc>>, - /// machine all backend memory region tree - machine_ram: Arc, } impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { - let cpu_topo = CpuTopology::new( - vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_sockets, - vm_config.machine_config.nr_dies, - vm_config.machine_config.nr_clusters, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_threads, - vm_config.machine_config.max_cpus, + let free_irqs = ( + IRQ_MAP[IrqEntryType::Sysbus as usize].0, + IRQ_MAP[IrqEntryType::Sysbus as usize].1, ); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "SysMem"), - "sys_mem", - ) - .with_context(|| MachineError::CrtIoSpaceErr)?; - let sysbus = SysBus::new( - &sys_mem, - ( - IRQ_MAP[IrqEntryType::Sysbus as usize].0, - IRQ_MAP[IrqEntryType::Sysbus as usize].1, - ), - ( - MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, - MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, - ), + let mmio_region: (u64, u64) = ( + MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, + MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, ); + let base = MachineBase::new(vm_config, free_irqs, mmio_region)?; + let sys_mem = base.sys_mem.clone(); Ok(StdMachine { - cpu_topo, - cpus: Vec::new(), - cpu_features: (&vm_config.machine_config.cpu_config).into(), - irq_chip: None, - sys_mem: sys_mem.clone(), - sysbus, + base, pci_host: Arc::new(Mutex::new(PciHost::new( &sys_mem, MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize], @@ -225,13 +184,10 @@ impl StdMachine { MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize], IRQ_MAP[IrqEntryType::Pcie as usize].0, ))), - boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), - vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), power_button: Arc::new( EventFd::new(libc::EFD_NONBLOCK) .with_context(|| MachineError::InitEventFdErr("power_button".to_string()))?, ), - vm_config: Arc::new(Mutex::new(vm_config.clone())), shutdown_req: Arc::new( EventFd::new(libc::EFD_NONBLOCK) .with_context(|| MachineError::InitEventFdErr("shutdown_req".to_string()))?, @@ -249,14 +205,8 @@ impl StdMachine { .with_context(|| MachineError::InitEventFdErr("resume_req".to_string()))?, ), dtb_vec: Vec::new(), - numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, - drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), - machine_ram: Arc::new(Region::init_container_region( - u64::max_value(), - "MachineRam", - )), }) } @@ -264,7 +214,7 @@ impl StdMachine { let mut locked_vm = vm.lock().unwrap(); let mut fdt_addr: u64 = 0; - for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { + for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { cpu.pause() .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; @@ -278,6 +228,7 @@ impl StdMachine { } locked_vm + .base .sys_mem .write( &mut locked_vm.dtb_vec.as_slice(), @@ -298,9 +249,9 @@ impl StdMachine { event!(Reset; reset_msg); } - locked_vm.irq_chip.as_ref().unwrap().reset()?; + locked_vm.base.irq_chip.as_ref().unwrap().reset()?; - for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { + for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { cpu.resume() .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; } @@ -309,7 +260,7 @@ impl StdMachine { } fn build_pptt_cores(&self, pptt: &mut AcpiTable, cluster_offset: u32, uid: &mut u32) { - for core in 0..self.cpu_topo.cores { + for core in 0..self.base.cpu_topo.cores { let mut priv_resources = vec![0; 3]; priv_resources[0] = pptt.table_len() as u32; let mut cache_hierarchy_node = CacheHierarchyNode::new(0, CacheType::L2); @@ -321,13 +272,13 @@ impl StdMachine { cache_hierarchy_node = CacheHierarchyNode::new(priv_resources[0], CacheType::L1I); pptt.append_child(&cache_hierarchy_node.aml_bytes()); - if self.cpu_topo.threads > 1 { + if self.base.cpu_topo.threads > 1 { let core_offset = pptt.table_len(); let core_hierarchy_node = ProcessorHierarchyNode::new(0x0, cluster_offset, core as u32, 3); pptt.append_child(&core_hierarchy_node.aml_bytes()); processor_append_priv_res(pptt, priv_resources); - for _thread in 0..self.cpu_topo.threads { + for _thread in 0..self.base.cpu_topo.threads { let thread_hierarchy_node = ProcessorHierarchyNode::new(0xE, core_offset as u32, *uid, 0); pptt.append_child(&thread_hierarchy_node.aml_bytes()); @@ -343,7 +294,7 @@ impl StdMachine { } fn build_pptt_clusters(&self, pptt: &mut AcpiTable, socket_offset: u32, uid: &mut u32) { - for cluster in 0..self.cpu_topo.clusters { + for cluster in 0..self.base.cpu_topo.clusters { let cluster_offset = pptt.table_len(); let cluster_hierarchy_node = ProcessorHierarchyNode::new(0x0, socket_offset, cluster as u32, 0); @@ -353,7 +304,7 @@ impl StdMachine { } fn build_pptt_sockets(&self, pptt: &mut AcpiTable, uid: &mut u32) { - for socket in 0..self.cpu_topo.sockets { + for socket in 0..self.base.cpu_topo.sockets { let priv_resources = vec![pptt.table_len() as u32]; let cache_hierarchy_node = CacheHierarchyNode::new(0, CacheType::L3); pptt.append_child(&cache_hierarchy_node.aml_bytes()); @@ -371,7 +322,7 @@ impl StdMachine { fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { let features = vcpu_cfg.unwrap_or_default(); if features.pmu { - for cpu in self.cpus.iter() { + for cpu in self.base.cpus.iter() { cpu.init_pmu()?; } } @@ -379,7 +330,7 @@ impl StdMachine { } pub fn mem_show(&self) { - self.sys_mem.memspace_show(); + self.base.sys_mem.memspace_show(); let machine_ram = self.get_vm_ram(); machine_ram.mtree(0_u32); } @@ -415,7 +366,8 @@ impl StdMachineOps for StdMachine { mmconfig_region_ops, "PcieEcamIo", ); - self.sys_mem + self.base + .sys_mem .root() .add_subregion( mmconfig_region, @@ -432,16 +384,22 @@ impl StdMachineOps for StdMachine { } fn add_fwcfg_device(&mut self, nr_cpus: u8) -> StdResult>>> { - if self.vm_config.lock().unwrap().pflashs.is_none() { + if self.base.vm_config.lock().unwrap().pflashs.is_none() { return Ok(None); } - let mut fwcfg = FwCfgMem::new(self.sys_mem.clone()); + let mut fwcfg = FwCfgMem::new(self.base.sys_mem.clone()); fwcfg .add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec()) .with_context(|| DevErrorKind::AddEntryErr("NbCpus".to_string()))?; - let cmdline = self.boot_source.lock().unwrap().kernel_cmdline.to_string(); + let cmdline = self + .base + .boot_source + .lock() + .unwrap() + .kernel_cmdline + .to_string(); fwcfg .add_data_entry( FwCfgEntryType::CmdlineSize, @@ -464,7 +422,7 @@ impl StdMachineOps for StdMachine { let fwcfg_dev = FwCfgMem::realize( fwcfg, - &mut self.sysbus, + &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0, MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, ) @@ -475,15 +433,15 @@ impl StdMachineOps for StdMachine { } fn get_cpu_topo(&self) -> &CpuTopology { - &self.cpu_topo + &self.base.cpu_topo } fn get_cpus(&self) -> &Vec> { - &self.cpus + &self.base.cpus } fn get_guest_numa(&self) -> &Option { - &self.numa_nodes + &self.base.numa_nodes } } @@ -522,8 +480,8 @@ impl MachineOps for StdMachine { v3: Some(v3), }; let irq_chip = InterruptController::new(&intc_conf)?; - self.irq_chip = Some(Arc::new(irq_chip)); - self.irq_chip.as_ref().unwrap().realize()?; + self.base.irq_chip = Some(Arc::new(irq_chip)); + self.base.irq_chip.as_ref().unwrap().realize()?; KVM_FDS .load() .irq_route_table @@ -553,7 +511,7 @@ impl MachineOps for StdMachine { } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { - let mut boot_source = self.boot_source.lock().unwrap(); + let mut boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); let bootloader_config = BootLoaderConfig { @@ -561,7 +519,7 @@ impl MachineOps for StdMachine { initrd, mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, }; - let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) + let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) .with_context(|| MachineError::LoadKernErr)?; if let Some(rd) = &mut boot_source.initrd { rd.initrd_addr = layout.initrd_start; @@ -578,7 +536,7 @@ impl MachineOps for StdMachine { let rtc = PL031::default(); PL031::realize( rtc, - &mut self.sysbus, + &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, ) @@ -587,11 +545,11 @@ impl MachineOps for StdMachine { } fn add_ged_device(&mut self) -> Result<()> { - let battery_present = self.vm_config.lock().unwrap().machine_config.battery; + let battery_present = self.base.vm_config.lock().unwrap().machine_config.battery; let ged = Ged::default(); let ged_dev = ged .realize( - &mut self.sysbus, + &mut self.base.sysbus, self.power_button.clone(), battery_present, MEM_LAYOUT[LayoutEntryType::Ged as usize].0, @@ -601,7 +559,7 @@ impl MachineOps for StdMachine { if battery_present { let pdev = PowerDev::new(ged_dev); pdev.realize( - &mut self.sysbus, + &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].0, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].1, ) @@ -617,10 +575,10 @@ impl MachineOps for StdMachine { let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; pl011 .realize( - &mut self.sysbus, + &mut self.base.sysbus, region_base, region_size, - &self.boot_source, + &self.base.boot_source, ) .with_context(|| "Failed to realize PL011")?; Ok(()) @@ -631,7 +589,7 @@ impl MachineOps for StdMachine { } fn get_drive_files(&self) -> Arc>> { - self.drive_files.clone() + self.base.drive_files.clone() } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { @@ -653,10 +611,10 @@ impl MachineOps for StdMachine { .register_resume_event(locked_vm.resume_req.clone(), vm.clone()) .with_context(|| "Fail to register resume event")?; - locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, - &locked_vm.sys_mem, + &locked_vm.base.sys_mem, nr_cpus, )?; @@ -677,7 +635,7 @@ impl MachineOps for StdMachine { None }; - locked_vm.cpus.extend(::init_vcpu( + locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), nr_cpus, &CPUTopology::new(), @@ -702,6 +660,7 @@ impl MachineOps for StdMachine { let fdt_vec = fdt_helper.finish()?; locked_vm.dtb_vec = fdt_vec.clone(); locked_vm + .base .sys_mem .write( &mut fdt_vec.as_slice(), @@ -765,7 +724,7 @@ impl MachineOps for StdMachine { let pflash = PFlash::new(flash_size, &fd, sector_len, 4, 2, read_only) .with_context(|| StdErrorKind::InitPflashErr)?; - PFlash::realize(pflash, &mut self.sysbus, flash_base, flash_size, fd) + PFlash::realize(pflash, &mut self.base.sysbus, flash_base, flash_size, fd) .with_context(|| StdErrorKind::RlzPflashErr)?; flash_base += flash_size; } @@ -810,24 +769,28 @@ impl MachineOps for StdMachine { let mut ramfb = Ramfb::new(sys_mem.clone(), install); ramfb.ramfb_state.setup(&fwcfg_dev)?; - ramfb.realize(&mut self.sysbus)?; + ramfb.realize(&mut self.base.sysbus)?; Ok(()) } fn run(&self, paused: bool) -> Result<()> { - self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) + self.vm_start( + paused, + &self.base.cpus, + &mut self.base.vm_state.0.lock().unwrap(), + ) } fn get_sys_mem(&mut self) -> &Arc { - &self.sys_mem + &self.base.sys_mem } fn get_vm_config(&self) -> Arc> { - self.vm_config.clone() + self.base.vm_config.clone() } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.vm_state + &self.base.vm_state } fn get_migrate_info(&self) -> Incoming { @@ -843,15 +806,15 @@ impl MachineOps for StdMachine { } fn get_sys_bus(&mut self) -> &SysBus { - &self.sysbus + &self.base.sysbus } fn get_vm_ram(&self) -> &Arc { - &self.machine_ram + &self.base.machine_ram } fn get_numa_nodes(&self) -> &Option { - &self.numa_nodes + &self.base.numa_nodes } fn get_fwcfg_dev(&mut self) -> Option>> { @@ -1046,7 +1009,7 @@ impl AcpiBuilder for StdMachine { spcr.set_field(52, 1_u8 << 3); // Irq number used by the UART let mut uart_irq: u32 = 0; - for dev in self.sysbus.devices.iter() { + for dev in self.base.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); if locked_dev.sysbusdev_base().dev_type == SysBusDevType::PL011 { uart_irq = locked_dev.sysbusdev_base().res.irq as u32; @@ -1078,7 +1041,7 @@ impl AcpiBuilder for StdMachine { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. - let cpus_count = self.cpus.len() as u64; + let cpus_count = self.base.cpus.len() as u64; let mut sb_scope = AmlScope::new("\\_SB"); for cpu_id in 0..cpus_count { let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); @@ -1095,7 +1058,7 @@ impl AcpiBuilder for StdMachine { dsdt.append_child(sb_scope.aml_bytes().as_slice()); // 3. Info of devices attached to system bus. - dsdt.append_child(self.sysbus.aml_bytes().as_slice()); + dsdt.append_child(self.base.sysbus.aml_bytes().as_slice()); let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) .with_context(|| "Fail to add DSDT table to loader")?; @@ -1119,9 +1082,13 @@ impl AcpiBuilder for StdMachine { madt.append_child(&gic_dist.aml_bytes()); // 2. GIC CPU. - let cpus_count = self.cpus.len() as u64; + let cpus_count = self.base.cpus.len() as u64; for cpu_index in 0..cpus_count { - let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); + let mpidr = self.base.cpus[cpu_index as usize] + .arch() + .lock() + .unwrap() + .mpidr(); let mpidr_mask: u64 = 0x007f_ffff; let mut gic_cpu = AcpiGicCpu::default(); gic_cpu.type_id = ACPI_MADT_GENERIC_CPU_INTERFACE; @@ -1143,7 +1110,7 @@ impl AcpiBuilder for StdMachine { gic_redist.length = 16; madt.append_child(&gic_redist.aml_bytes()); // SAFETY: ARM architecture must have interrupt controllers in user mode. - if self.irq_chip.as_ref().unwrap().get_redist_count() > 1 { + if self.base.irq_chip.as_ref().unwrap().get_redist_count() > 1 { gic_redist.range_length = MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].1 as u32; gic_redist.base_addr = MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].0; madt.append_child(&gic_redist.aml_bytes()); @@ -1212,7 +1179,7 @@ impl AcpiBuilder for StdMachine { let mut next_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; // SAFETY: the SRAT table is created only when numa node configured. - for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { + for (id, node) in self.base.numa_nodes.as_ref().unwrap().iter() { self.build_srat_cpu(*id, node, &mut srat); next_base = self.build_srat_mem(next_base, *id, node, &mut srat); } @@ -1256,7 +1223,7 @@ impl MachineLifecycle for StdMachine { fn destroy(&self) -> bool { let vmstate = { - let state = self.vm_state.deref().0.lock().unwrap(); + let state = self.base.vm_state.deref().0.lock().unwrap(); *state }; @@ -1283,7 +1250,8 @@ impl MachineLifecycle for StdMachine { } fn get_shutdown_action(&self) -> ShutdownAction { - self.vm_config + self.base + .vm_config .lock() .unwrap() .machine_config @@ -1300,9 +1268,9 @@ impl MachineLifecycle for StdMachine { fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { if let Err(e) = self.vm_state_transfer( - &self.cpus, - &self.irq_chip, - &mut self.vm_state.0.lock().unwrap(), + &self.base.cpus, + &self.base.irq_chip, + &mut self.base.vm_state.0.lock().unwrap(), old, new, ) { @@ -1316,14 +1284,16 @@ impl MachineLifecycle for StdMachine { impl MachineAddressInterface for StdMachine { fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { let length = data.len() as u64; - self.sys_mem + self.base + .sys_mem .read(&mut data, GuestAddress(addr), length) .is_ok() } fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { let count = data.len() as u64; - self.sys_mem + self.base + .sys_mem .write(&mut data, GuestAddress(addr), count) .is_ok() } @@ -1357,7 +1327,7 @@ impl MachineTestInterface for StdMachine {} impl EventLoopManager for StdMachine { fn loop_should_exit(&self) -> bool { - let vmstate = self.vm_state.deref().0.lock().unwrap(); + let vmstate = self.base.vm_state.deref().0.lock().unwrap(); *vmstate == KvmVmState::Shutdown } @@ -1601,23 +1571,24 @@ impl CompileFDTHelper for StdMachine { // Generate CPU topology let cpu_map_node_dep = fdt.begin_node("cpu-map")?; - for socket in 0..self.cpu_topo.sockets { + for socket in 0..self.base.cpu_topo.sockets { let sock_name = format!("cluster{}", socket); let sock_node_dep = fdt.begin_node(&sock_name)?; - for cluster in 0..self.cpu_topo.clusters { + for cluster in 0..self.base.cpu_topo.clusters { let clster = format!("cluster{}", cluster); let cluster_node_dep = fdt.begin_node(&clster)?; - for core in 0..self.cpu_topo.cores { + for core in 0..self.base.cpu_topo.cores { let core_name = format!("core{}", core); let core_node_dep = fdt.begin_node(&core_name)?; - for thread in 0..self.cpu_topo.threads { + for thread in 0..self.base.cpu_topo.threads { let thread_name = format!("thread{}", thread); let thread_node_dep = fdt.begin_node(&thread_name)?; - let vcpuid = self.cpu_topo.threads * self.cpu_topo.cores * cluster - + self.cpu_topo.threads * core - + thread; + let vcpuid = + self.base.cpu_topo.threads * self.base.cpu_topo.cores * cluster + + self.base.cpu_topo.threads * core + + thread; fdt.set_property_u32( "cpu", u32::from(vcpuid) + device_tree::CPU_PHANDLE_START, @@ -1632,8 +1603,12 @@ impl CompileFDTHelper for StdMachine { } fdt.end_node(cpu_map_node_dep)?; - for cpu_index in 0..self.cpu_topo.nrcpus { - let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); + for cpu_index in 0..self.base.cpu_topo.nrcpus { + let mpidr = self.base.cpus[cpu_index as usize] + .arch() + .lock() + .unwrap() + .mpidr(); let node = format!("cpu@{:x}", mpidr); let mpidr_node_dep = fdt.begin_node(&node)?; @@ -1643,13 +1618,13 @@ impl CompileFDTHelper for StdMachine { )?; fdt.set_property_string("device_type", "cpu")?; fdt.set_property_string("compatible", "arm,arm-v8")?; - if self.cpu_topo.max_cpus > 1 { + if self.base.cpu_topo.max_cpus > 1 { fdt.set_property_string("enable-method", "psci")?; } fdt.set_property_u64("reg", mpidr & 0x007F_FFFF)?; fdt.set_property_u32("phandle", device_tree::FIRST_VCPU_PHANDLE)?; - if let Some(numa_nodes) = &self.numa_nodes { + if let Some(numa_nodes) = &self.base.numa_nodes { for numa_index in 0..numa_nodes.len() { let numa_node = numa_nodes.get(&(numa_index as u32)); if numa_node.unwrap().cpus.contains(&(cpu_index)) { @@ -1663,7 +1638,7 @@ impl CompileFDTHelper for StdMachine { fdt.end_node(cpus_node_dep)?; - if self.cpu_features.pmu { + if self.base.cpu_features.pmu { generate_pmu_node(fdt)?; } @@ -1671,9 +1646,9 @@ impl CompileFDTHelper for StdMachine { } fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - if self.numa_nodes.is_none() { + if self.base.numa_nodes.is_none() { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.sys_mem.memory_end_address().raw_value() + let mem_size = self.base.sys_mem.memory_end_address().raw_value() - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let node = "memory"; let memory_node_dep = fdt.begin_node(node)?; @@ -1686,7 +1661,7 @@ impl CompileFDTHelper for StdMachine { // Set NUMA node information. let mut mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - for (id, node) in self.numa_nodes.as_ref().unwrap().iter().enumerate() { + for (id, node) in self.base.numa_nodes.as_ref().unwrap().iter().enumerate() { let mem_size = node.1.size; let node = format!("memory@{:x}", mem_base); let memory_node_dep = fdt.begin_node(&node)?; @@ -1732,7 +1707,7 @@ impl CompileFDTHelper for StdMachine { fdt.set_property_string("method", "hvc")?; fdt.end_node(psci_node_dep)?; - for dev in self.sysbus.devices.iter() { + for dev in self.base.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); match locked_dev.sysbusdev_base().dev_type { SysBusDevType::PL011 => { @@ -1764,7 +1739,7 @@ impl CompileFDTHelper for StdMachine { fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "chosen"; - let boot_source = self.boot_source.lock().unwrap(); + let boot_source = self.base.boot_source.lock().unwrap(); let chosen_node_dep = fdt.begin_node(node)?; let cmdline = &boot_source.kernel_cmdline.to_string(); @@ -1787,7 +1762,7 @@ impl CompileFDTHelper for StdMachine { } fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - if self.numa_nodes.is_none() { + if self.base.numa_nodes.is_none() { return Ok(()); } @@ -1795,7 +1770,7 @@ impl CompileFDTHelper for StdMachine { fdt.set_property_string("compatible", "numa-distance-map-v1")?; let mut matrix = Vec::new(); - let numa_nodes = self.numa_nodes.as_ref().unwrap(); + let numa_nodes = self.base.numa_nodes.as_ref().unwrap(); let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); for (id, node) in numa_nodes.iter().enumerate() { let distances = &node.1.distances; @@ -1833,7 +1808,11 @@ impl device_tree::CompileFDT for StdMachine { self.generate_memory_node(fdt)?; self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; - self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; + self.base + .irq_chip + .as_ref() + .unwrap() + .generate_fdt_node(fdt)?; self.generate_distance_node(fdt)?; fdt.end_node(node_dep)?; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 1f8698c34..d6c3df78d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -30,7 +30,7 @@ use self::ich9_lpc::SLEEP_CTRL_OFFSET; use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; use crate::error::MachineError; -use crate::{vm_state, MachineOps}; +use crate::{vm_state, MachineBase, MachineOps}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, @@ -49,8 +49,8 @@ use hypervisor::kvm::KVM_FDS; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, BootSource, DriveFile, Incoming, MigrateMode, NumaNode, - NumaNodes, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, DriveFile, Incoming, MigrateMode, NumaNode, NumaNodes, + PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -116,79 +116,36 @@ const IRQ_MAP: &[(i32, i32)] = &[ /// Standard machine structure. pub struct StdMachine { - /// `vCPU` topology, support sockets, cores, threads. - cpu_topo: CpuTopology, - /// `vCPU` devices. - cpus: Vec>, - /// IO address space. - sys_io: Arc, - /// Memory address space. - pub sys_mem: Arc, - /// System bus. - sysbus: SysBus, + // Machine base members. + base: MachineBase, /// PCI/PCIe host bridge. pci_host: Arc>, - /// VM running state. - vm_state: Arc<(Mutex, Condvar)>, - /// Vm boot_source config. - boot_source: Arc>, /// Reset request, handle VM `Reset` event. reset_req: Arc, /// Shutdown_req, handle VM 'ShutDown' event. shutdown_req: Arc, - /// All configuration information of virtual machine. - vm_config: Arc>, - /// List of guest NUMA nodes information. - numa_nodes: Option, /// List contains the boot order of boot devices. boot_order_list: Arc>>, /// FwCfg device. fwcfg_dev: Option>>, - /// Drive backend files. - drive_files: Arc>>, - /// All backend memory region tree - machine_ram: Arc, } impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { - let cpu_topo = CpuTopology::new( - vm_config.machine_config.nr_cpus, - vm_config.machine_config.nr_sockets, - vm_config.machine_config.nr_dies, - vm_config.machine_config.nr_clusters, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_threads, - vm_config.machine_config.max_cpus, + let free_irqs = ( + IRQ_MAP[IrqEntryType::Sysbus as usize].0, + IRQ_MAP[IrqEntryType::Sysbus as usize].1, ); - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16, "SysIo"), "SysIo") - .with_context(|| MachineError::CrtIoSpaceErr)?; - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "SysMem"), - "SysMem", - ) - .with_context(|| MachineError::CrtMemSpaceErr)?; - let sysbus = SysBus::new( - &sys_io, - &sys_mem, - ( - IRQ_MAP[IrqEntryType::Sysbus as usize].0, - IRQ_MAP[IrqEntryType::Sysbus as usize].1, - ), - ( - MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, - MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, - ), + let mmio_region = ( + MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, + MEM_LAYOUT[LayoutEntryType::Mmio as usize + 1].0, ); - // Machine state init - let vm_state = Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())); + let base = MachineBase::new(vm_config, free_irqs, mmio_region)?; + let sys_mem = base.sys_mem.clone(); + let sys_io = base.sys_io.clone(); Ok(StdMachine { - cpu_topo, - cpus: Vec::new(), - sys_io: sys_io.clone(), - sys_mem: sys_mem.clone(), - sysbus, + base, pci_host: Arc::new(Mutex::new(PciHost::new( &sys_io, &sys_mem, @@ -196,8 +153,6 @@ impl StdMachine { MEM_LAYOUT[LayoutEntryType::PcieMmio as usize], IRQ_MAP[IrqEntryType::Pcie as usize].0, ))), - boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), - vm_state, reset_req: Arc::new( EventFd::new(libc::EFD_NONBLOCK) .with_context(|| MachineError::InitEventFdErr("reset request".to_string()))?, @@ -207,22 +162,15 @@ impl StdMachine { MachineError::InitEventFdErr("shutdown request".to_string()) })?, ), - vm_config: Arc::new(Mutex::new(vm_config.clone())), - numa_nodes: None, boot_order_list: Arc::new(Mutex::new(Vec::new())), fwcfg_dev: None, - drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), - machine_ram: Arc::new(Region::init_container_region( - u64::max_value(), - "MachineRam", - )), }) } pub fn handle_reset_request(vm: &Arc>) -> Result<()> { let mut locked_vm = vm.lock().unwrap(); - for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { + for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { cpu.pause() .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; @@ -241,7 +189,7 @@ impl StdMachine { event!(Reset; reset_msg); } - for (cpu_index, cpu) in locked_vm.cpus.iter().enumerate() { + for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { cpu.reset() .with_context(|| format!("Failed to reset vcpu{}", cpu_index))?; cpu.resume() @@ -280,7 +228,7 @@ impl StdMachine { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let ich = ich9_lpc::LPCBridge::new( root_bus, - self.sys_io.clone(), + self.base.sys_io.clone(), self.reset_req.clone(), self.shutdown_req.clone(), )?; @@ -293,8 +241,8 @@ impl StdMachine { } pub fn mem_show(&self) { - self.sys_mem.memspace_show(); - self.sys_io.memspace_show(); + self.base.sys_mem.memspace_show(); + self.base.sys_io.memspace_show(); let machine_ram = self.get_vm_ram(); machine_ram.mtree(0_u32); } @@ -313,7 +261,8 @@ impl StdMachineOps for StdMachine { mmconfig_region_ops.clone(), "PcieEcamSpace", ); - self.sys_mem + self.base + .sys_mem .root() .add_subregion( mmconfig_region.clone(), @@ -323,13 +272,15 @@ impl StdMachineOps for StdMachine { let pio_addr_ops = PciHost::build_pio_addr_ops(self.pci_host.clone()); let pio_addr_region = Region::init_io_region(4, pio_addr_ops, "PioAddr"); - self.sys_io + self.base + .sys_io .root() .add_subregion(pio_addr_region, 0xcf8) .with_context(|| "Failed to register CONFIG_ADDR port in I/O space.")?; let pio_data_ops = PciHost::build_pio_data_ops(self.pci_host.clone()); let pio_data_region = Region::init_io_region(4, pio_data_ops, "PioData"); - self.sys_io + self.base + .sys_io .root() .add_subregion(pio_data_region, 0xcfc) .with_context(|| "Failed to register CONFIG_DATA port in I/O space.")?; @@ -340,7 +291,7 @@ impl StdMachineOps for StdMachine { } fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::Result>>> { - let mut fwcfg = FwCfgIO::new(self.sys_mem.clone()); + let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone()); fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; @@ -350,7 +301,7 @@ impl StdMachineOps for StdMachine { .add_file_entry("bootorder", boot_order) .with_context(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; - let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.sysbus) + let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.base.sysbus) .with_context(|| "Failed to realize fwcfg device")?; self.fwcfg_dev = Some(fwcfg_dev.clone()); @@ -358,15 +309,15 @@ impl StdMachineOps for StdMachine { } fn get_cpu_topo(&self) -> &CpuTopology { - &self.cpu_topo + &self.base.cpu_topo } fn get_cpus(&self) -> &Vec> { - &self.cpus + &self.base.cpus } fn get_guest_numa(&self) -> &Option { - &self.numa_nodes + &self.base.numa_nodes } } @@ -418,7 +369,7 @@ impl MachineOps for StdMachine { } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { - let boot_source = self.boot_source.lock().unwrap(); + let boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 @@ -428,14 +379,14 @@ impl MachineOps for StdMachine { kernel: boot_source.kernel_file.clone(), initrd, kernel_cmdline: boot_source.kernel_cmdline.to_string(), - cpu_count: self.cpu_topo.nrcpus, + cpu_count: self.base.cpu_topo.nrcpus, gap_range: (gap_start, gap_end - gap_start), ioapic_addr: MEM_LAYOUT[LayoutEntryType::IoApic as usize].0 as u32, lapic_addr: MEM_LAYOUT[LayoutEntryType::LocalApic as usize].0 as u32, ident_tss_range: Some(MEM_LAYOUT[LayoutEntryType::IdentTss as usize]), prot64_mode: false, }; - let layout = load_linux(&bootloader_config, &self.sys_mem, fwcfg) + let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) .with_context(|| MachineError::LoadKernErr)?; Ok(CPUBootConfig { @@ -454,7 +405,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); - RTC::realize(rtc, &mut self.sysbus).with_context(|| "Failed to realize RTC device")?; + RTC::realize(rtc, &mut self.base.sysbus).with_context(|| "Failed to realize RTC device")?; Ok(()) } @@ -464,7 +415,7 @@ impl MachineOps for StdMachine { let region_size: u64 = 8; let serial = Serial::new(config.clone()); serial - .realize(&mut self.sysbus, region_base, region_size) + .realize(&mut self.base.sysbus, region_base, region_size) .with_context(|| "Failed to realize serial device.")?; Ok(()) } @@ -474,7 +425,7 @@ impl MachineOps for StdMachine { } fn get_drive_files(&self) -> Arc>> { - self.drive_files.clone() + self.base.drive_files.clone() } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { @@ -482,11 +433,11 @@ impl MachineOps for StdMachine { let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; - locked_vm.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, - &locked_vm.sys_io, - &locked_vm.sys_mem, + &locked_vm.base.sys_io, + &locked_vm.base.sys_mem, nr_cpus, )?; @@ -514,7 +465,7 @@ impl MachineOps for StdMachine { vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, )); - locked_vm.cpus.extend(::init_vcpu( + locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), nr_cpus, &topology, @@ -605,7 +556,10 @@ impl MachineOps for StdMachine { let rom_region = Region::init_ram_region(ram1, "PflashRam"); rom_region.write(&mut fd, GuestAddress(rom_base), 0, rom_size)?; rom_region.set_priority(10); - self.sys_mem.root().add_subregion(rom_region, rom_base)?; + self.base + .sys_mem + .root() + .add_subregion(rom_region, rom_base)?; fd.rewind()? } @@ -623,7 +577,7 @@ impl MachineOps for StdMachine { .with_context(|| StandardVmError::InitPflashErr)?; PFlash::realize( pflash, - &mut self.sysbus, + &mut self.base.sysbus, flash_end - pfl_size, pfl_size, backend, @@ -663,19 +617,23 @@ impl MachineOps for StdMachine { } fn run(&self, paused: bool) -> Result<()> { - self.vm_start(paused, &self.cpus, &mut self.vm_state.0.lock().unwrap()) + self.vm_start( + paused, + &self.base.cpus, + &mut self.base.vm_state.0.lock().unwrap(), + ) } fn get_sys_mem(&mut self) -> &Arc { - &self.sys_mem + &self.base.sys_mem } fn get_vm_config(&self) -> Arc> { - self.vm_config.clone() + self.base.vm_config.clone() } fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.vm_state + &self.base.vm_state } fn get_migrate_info(&self) -> Incoming { @@ -691,15 +649,15 @@ impl MachineOps for StdMachine { } fn get_sys_bus(&mut self) -> &SysBus { - &self.sysbus + &self.base.sysbus } fn get_vm_ram(&self) -> &Arc { - &self.machine_ram + &self.base.machine_ram } fn get_numa_nodes(&self) -> &Option { - &self.numa_nodes + &self.base.numa_nodes } fn get_fwcfg_dev(&mut self) -> Option>> { @@ -723,7 +681,7 @@ impl AcpiBuilder for StdMachine { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. - let cpus_count = self.cpus.len() as u64; + let cpus_count = self.base.cpus.len() as u64; let mut sb_scope = AmlScope::new("\\_SB"); for cpu_id in 0..cpus_count { let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); @@ -738,7 +696,7 @@ impl AcpiBuilder for StdMachine { dsdt.append_child(sb_scope.aml_bytes().as_slice()); // 3. Info of devices attached to system bus. - dsdt.append_child(self.sysbus.aml_bytes().as_slice()); + dsdt.append_child(self.base.sysbus.aml_bytes().as_slice()); // 4. Add _S5 sleep state. let mut package = AmlPackage::new(4); @@ -774,7 +732,7 @@ impl AcpiBuilder for StdMachine { }; madt.append_child(ioapic.aml_bytes().as_ref()); - self.cpus.iter().for_each(|cpu| { + self.base.cpus.iter().for_each(|cpu| { let lapic = AcpiLocalApic { type_id: 0, length: size_of::() as u8, @@ -895,7 +853,7 @@ impl AcpiBuilder for StdMachine { srat.append_child(&[0_u8; 8_usize]); let mut next_base = 0_u64; - for (id, node) in self.numa_nodes.as_ref().unwrap().iter() { + for (id, node) in self.base.numa_nodes.as_ref().unwrap().iter() { self.build_srat_cpu(*id, node, &mut srat); next_base = self.build_srat_mem(next_base, *id, node, &mut srat); } @@ -926,7 +884,7 @@ impl MachineLifecycle for StdMachine { fn destroy(&self) -> bool { let vmstate = { - let state = self.vm_state.deref().0.lock().unwrap(); + let state = self.base.vm_state.deref().0.lock().unwrap(); *state }; @@ -949,9 +907,12 @@ impl MachineLifecycle for StdMachine { } fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { - if let Err(e) = - self.vm_state_transfer(&self.cpus, &mut self.vm_state.0.lock().unwrap(), old, new) - { + if let Err(e) = self.vm_state_transfer( + &self.base.cpus, + &mut self.base.vm_state.0.lock().unwrap(), + old, + new, + ) { error!("VM state transfer failed: {:?}", e); return false; } @@ -977,7 +938,8 @@ impl MachineAddressInterface for StdMachine { } let length = data.len() as u64; - self.sys_io + self.base + .sys_io .read(&mut data, GuestAddress(addr), length) .is_ok() } @@ -985,25 +947,28 @@ impl MachineAddressInterface for StdMachine { fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { let count = data.len() as u64; if addr == SLEEP_CTRL_OFFSET as u64 { - if let Err(e) = self.cpus[0].pause() { + if let Err(e) = self.base.cpus[0].pause() { error!("Fail to pause bsp, {:?}", e); } } - self.sys_io + self.base + .sys_io .write(&mut data, GuestAddress(addr), count) .is_ok() } fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { let length = data.len() as u64; - self.sys_mem + self.base + .sys_mem .read(&mut data, GuestAddress(addr), length) .is_ok() } fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { let count = data.len() as u64; - self.sys_mem + self.base + .sys_mem .write(&mut data, GuestAddress(addr), count) .is_ok() } @@ -1037,7 +1002,7 @@ impl MachineTestInterface for StdMachine {} impl EventLoopManager for StdMachine { fn loop_should_exit(&self) -> bool { - let vmstate = self.vm_state.deref().0.lock().unwrap(); + let vmstate = self.base.vm_state.deref().0.lock().unwrap(); *vmstate == KvmVmState::Shutdown } -- Gitee From 94f87075cc465eb54aec23b5af599f2e2fdf408e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 10 Oct 2023 15:46:05 +0800 Subject: [PATCH 1418/2187] machine: move some trait implemented in StdMachine These functions arm implemented in the same way. Thereforce, they are implemented in the trait. Signed-off-by: Mingwang Li --- machine/src/standard_vm/aarch64/mod.rs | 19 +++++-------------- machine/src/standard_vm/mod.rs | 22 +++++++++++++++++----- machine/src/standard_vm/x86_64/mod.rs | 18 +++++------------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 294402d4a..27a7cedd4 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -42,8 +42,7 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{ - CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuLifecycleState, CpuTopology, CPU, - PMU_INTR, PPI_BASE, + CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE, }; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; use devices::acpi::power::PowerDev; @@ -358,6 +357,10 @@ impl StdMachine { } impl StdMachineOps for StdMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + fn init_pci_host(&self) -> StdResult<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); @@ -431,18 +434,6 @@ impl StdMachineOps for StdMachine { Ok(Some(fwcfg_dev)) } - - fn get_cpu_topo(&self) -> &CpuTopology { - &self.base.cpu_topo - } - - fn get_cpus(&self) -> &Vec> { - &self.base.cpus - } - - fn get_guest_numa(&self) -> &Option { - &self.base.numa_nodes - } } impl MachineOps for StdMachine { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 51e8ac4d0..d754ffcd7 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -41,7 +41,7 @@ use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] use self::x86_64::ich9_lpc::{PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET}; use super::Result as MachineResult; -use crate::MachineOps; +use crate::{MachineBase, MachineOps}; #[cfg(target_arch = "aarch64")] use aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] @@ -86,6 +86,8 @@ use virtio::{ use x86_64::{LayoutEntryType, MEM_LAYOUT}; trait StdMachineOps: AcpiBuilder { + fn machine_base(&self) -> &MachineBase; + fn init_pci_host(&self) -> Result<()>; /// Build all ACPI tables and RSDP, and add them to FwCfg as file entries. @@ -192,11 +194,17 @@ trait StdMachineOps: AcpiBuilder { bail!("Not implemented"); } - fn get_cpu_topo(&self) -> &CpuTopology; + fn get_cpu_topo(&self) -> &CpuTopology { + &self.machine_base().cpu_topo + } - fn get_cpus(&self) -> &Vec>; + fn get_cpus(&self) -> &Vec> { + &self.machine_base().cpus + } - fn get_guest_numa(&self) -> &Option; + fn get_guest_numa(&self) -> &Option { + &self.machine_base().numa_nodes + } /// Register event notifier for reset of standard machine. /// @@ -2011,7 +2019,11 @@ impl DeviceInterface for StdMachine { } }; - match self.sys_mem.read_object::(GuestAddress(gpa)) { + match self + .machine_base() + .sys_mem + .read_object::(GuestAddress(gpa)) + { Ok(val) => { Response::create_response(serde_json::to_value(format!("{:X}", val)).unwrap(), None) } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index d6c3df78d..b13a5cffc 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -38,7 +38,7 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; +use cpu::{CPUBootConfig, CPUInterface, CPUTopology}; use devices::legacy::{ error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, @@ -253,6 +253,10 @@ impl StdMachine { } impl StdMachineOps for StdMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + fn init_pci_host(&self) -> Result<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); @@ -307,18 +311,6 @@ impl StdMachineOps for StdMachine { Ok(Some(fwcfg_dev)) } - - fn get_cpu_topo(&self) -> &CpuTopology { - &self.base.cpu_topo - } - - fn get_cpus(&self) -> &Vec> { - &self.base.cpus - } - - fn get_guest_numa(&self) -> &Option { - &self.base.numa_nodes - } } impl MachineOps for StdMachine { -- Gitee From 3db2a8b29b3f48a4e45275c4ebdf150be6002ebf Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 10 Oct 2023 18:38:36 +0800 Subject: [PATCH 1419/2187] machine: move some function to MachineOps The functions shared by LightMachine and StdMachine are implemented in MachineOps trait. Signed-off-by: Mingwang Li --- devices/src/legacy/fwcfg.rs | 2 +- machine/src/lib.rs | 139 ++++++++++++++++++++-- machine/src/micro_vm/mem_layout.rs | 2 + machine/src/micro_vm/mod.rs | 155 ++++--------------------- machine/src/standard_vm/aarch64/mod.rs | 131 +++------------------ machine/src/standard_vm/mod.rs | 21 +--- machine/src/standard_vm/x86_64/mod.rs | 100 ++-------------- 7 files changed, 185 insertions(+), 365 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 47cda8315..4bbd46485 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1187,7 +1187,7 @@ impl SysBusDevOps for FwCfgIO { } } -pub trait FwCfgOps { +pub trait FwCfgOps: Send + Sync { fn fw_cfg_common(&mut self) -> &mut FwCfgCommon; /// Add an entry to FwCfg device, with Vector content. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 381964819..92d77f87c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -111,7 +111,7 @@ use virtio::{ }; /// Machine structure include base members. -struct MachineBase { +pub struct MachineBase { /// `vCPU` topology, support sockets, cores, threads. cpu_topo: CpuTopology, /// `vCPU` devices. @@ -139,6 +139,8 @@ struct MachineBase { numa_nodes: Option, /// Drive backend files. drive_files: Arc>>, + /// FwCfg device. + fwcfg_dev: Option>>, /// machine all backend memory region tree machine_ram: Arc, } @@ -191,6 +193,7 @@ impl MachineBase { vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), + fwcfg_dev: None, machine_ram: Arc::new(Region::init_container_region( u64::max_value(), "MachineRam", @@ -200,6 +203,8 @@ impl MachineBase { } pub trait MachineOps { + fn machine_base(&self) -> &MachineBase; + fn build_smbios( &self, fw_cfg: &Arc>, @@ -227,8 +232,38 @@ pub trait MachineOps { Ok(()) } + #[cfg(target_arch = "x86_64")] fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result; + #[cfg(target_arch = "aarch64")] + fn load_boot_source( + &self, + fwcfg: Option<&Arc>>, + mem_start: u64, + ) -> Result { + use boot_loader::{load_linux, BootLoaderConfig}; + + let mut boot_source = self.machine_base().boot_source.lock().unwrap(); + let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); + + let bootloader_config = BootLoaderConfig { + kernel: boot_source.kernel_file.clone(), + initrd, + mem_start, + }; + let layout = load_linux(&bootloader_config, &self.machine_base().sys_mem, fwcfg) + .with_context(|| MachineError::LoadKernErr)?; + if let Some(rd) = &mut boot_source.initrd { + rd.initrd_addr = layout.initrd_start; + rd.initrd_size = layout.initrd_size; + } + + Ok(CPUBootConfig { + fdt_addr: layout.dtb_start, + boot_pc: layout.boot_pc, + }) + } + #[cfg(target_arch = "aarch64")] fn load_cpu_features(&self, vmcfg: &VmConfig) -> Result { Ok((&vmcfg.machine_config.cpu_config).into()) @@ -307,6 +342,13 @@ pub trait MachineOps { Ok(()) } + fn mem_show(&self) { + self.machine_base().sys_mem.memspace_show(); + #[cfg(target_arch = "x86_64")] + self.machine_base().sys_io.memspace_show(); + self.get_vm_ram().mtree(0_u32); + } + /// Init vcpu register with boot message. /// /// # Arguments @@ -370,6 +412,47 @@ pub trait MachineOps { Ok(cpus) } + #[cfg(target_arch = "x86_64")] + fn arch_init(&self, identity_addr: u64) -> Result<()> { + use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; + + let kvm_fds = KVM_FDS.load(); + let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); + + vm_fd + .set_identity_map_address(identity_addr) + .with_context(|| MachineError::SetIdentityMapAddr)?; + + // Page table takes 1 page, TSS takes the following 3 pages. + vm_fd + .set_tss_address((identity_addr + 0x1000) as usize) + .with_context(|| MachineError::SetTssErr)?; + + let pit_config = kvm_pit_config { + flags: KVM_PIT_SPEAKER_DUMMY, + pad: Default::default(), + }; + vm_fd + .create_pit2(pit_config) + .with_context(|| MachineError::CrtPitErr) + } + + /// Must be called after the CPUs have been realized and GIC has been created. + /// + /// # Arguments + /// + /// * `CPUFeatures` - The features of vcpu. + #[cfg(target_arch = "aarch64")] + fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { + let features = vcpu_cfg.unwrap_or_default(); + if features.pmu { + for cpu in self.machine_base().cpus.iter() { + cpu.init_pmu()?; + } + } + Ok(()) + } + /// Add interrupt controller. /// /// # Arguments @@ -451,19 +534,43 @@ pub trait MachineOps { bail!("Virtio mmio devices not supported"); } - fn get_sys_mem(&mut self) -> &Arc; + fn get_cpu_topo(&self) -> &CpuTopology { + &self.machine_base().cpu_topo + } - fn get_vm_config(&self) -> Arc>; + fn get_cpus(&self) -> &Vec> { + &self.machine_base().cpus + } - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)>; + fn get_sys_mem(&mut self) -> &Arc { + &self.machine_base().sys_mem + } - fn get_vm_ram(&self) -> &Arc; + fn get_vm_config(&self) -> Arc> { + self.machine_base().vm_config.clone() + } - fn get_numa_nodes(&self) -> &Option; + fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { + &self.machine_base().vm_state + } + + fn get_vm_ram(&self) -> &Arc { + &self.machine_base().machine_ram + } + + fn get_numa_nodes(&self) -> &Option { + &self.machine_base().numa_nodes + } /// Get migration mode and path from VM config. There are four modes in total: /// Tcp, Unix, File and Unknown. - fn get_migrate_info(&self) -> Incoming; + fn get_migrate_info(&self) -> Incoming { + if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { + return (*mode, path.to_string()); + } + + (MigrateMode::Unknown, String::new()) + } /// Add net device. /// @@ -703,10 +810,12 @@ pub trait MachineOps { Ok(()) } - fn get_sys_bus(&mut self) -> &SysBus; + fn get_sys_bus(&mut self) -> &SysBus { + &self.machine_base().sysbus + } fn get_fwcfg_dev(&mut self) -> Option>> { - None + self.machine_base().fwcfg_dev.clone() } fn get_boot_order_list(&self) -> Option>>> { @@ -1708,7 +1817,9 @@ pub trait MachineOps { } /// Get the drive backend files. - fn get_drive_files(&self) -> Arc>>; + fn get_drive_files(&self) -> Arc>> { + self.machine_base().drive_files.clone() + } /// Fetch a cloned file from drive backend files. fn fetch_drive_file(&self, path: &str) -> Result { @@ -1788,7 +1899,13 @@ pub trait MachineOps { /// # Arguments /// /// * `paused` - Flag for `paused` when `LightMachine` starts to run. - fn run(&self, paused: bool) -> Result<()>; + fn run(&self, paused: bool) -> Result<()> { + self.vm_start( + paused, + &self.machine_base().cpus, + &mut self.machine_base().vm_state.0.lock().unwrap(), + ) + } /// Start machine as `Running` or `Paused` state. /// diff --git a/machine/src/micro_vm/mem_layout.rs b/machine/src/micro_vm/mem_layout.rs index b17310fc7..3f26013e7 100644 --- a/machine/src/micro_vm/mem_layout.rs +++ b/machine/src/micro_vm/mem_layout.rs @@ -47,6 +47,7 @@ pub enum LayoutEntryType { Mmio, IoApic, LocalApic, + IdentTss, MemAbove4g, } @@ -57,5 +58,6 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0xF010_0000, 0x200), // Mmio (0xFEC0_0000, 0x10_0000), // IoApic (0xFEE0_0000, 0x10_0000), // LocalApic + (0xFEF0_C000, 0x4000), // Identity map address and TSS (0x1_0000_0000, 0x80_0000_0000), // MemAbove4g ]; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 539442ce8..5dbe15e64 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -35,7 +35,6 @@ mod syscall; pub use error::MicroVmError; -use std::collections::HashMap; use std::fmt; use std::fmt::Debug; use std::ops::Deref; @@ -44,8 +43,6 @@ use std::sync::{Arc, Condvar, Mutex}; use std::vec::Vec; use anyhow::{anyhow, bail, Context, Result}; -#[cfg(target_arch = "x86_64")] -use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use log::{error, info}; use super::Result as MachineResult; @@ -54,17 +51,18 @@ use super::{error::MachineError, MachineOps}; use crate::vm_state; use crate::MachineBase; use address_space::{AddressSpace, GuestAddress, Region}; -use boot_loader::{load_linux, BootLoaderConfig}; -#[cfg(target_arch = "aarch64")] -use cpu::CPUFeatures; +#[cfg(target_arch = "x86_64")] +use cpu::CPUBootConfig; #[cfg(target_arch = "aarch64")] use cpu::PMU_INTR; -use cpu::{CPUBootConfig, CPUTopology, CpuLifecycleState}; +use cpu::{CPUTopology, CpuLifecycleState}; +#[cfg(target_arch = "x86_64")] +use devices::legacy::FwCfgOps; +use devices::legacy::Serial; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; #[cfg(target_arch = "x86_64")] use devices::legacy::SERIAL_ADDR; -use devices::legacy::{FwCfgOps, Serial}; use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; #[cfg(target_arch = "aarch64")] use devices::sysbus::{SysBusDevType, SysRes}; @@ -73,9 +71,8 @@ use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GI #[cfg(target_arch = "x86_64")] use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, ConfigCheck, DiskFormat, DriveFile, - Incoming, MigrateMode, NetworkInterfaceConfig, NumaNodes, SerialConfig, VmConfig, - DEFAULT_VIRTQUEUE_SIZE, + parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, ConfigCheck, DiskFormat, MigrateMode, + NetworkInterfaceConfig, SerialConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -186,34 +183,6 @@ impl LightMachine { }) } - #[cfg(target_arch = "x86_64")] - fn arch_init() -> MachineResult<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - vm_fd - .set_tss_address(0xfffb_d000_usize) - .with_context(|| MachineError::SetTssErr)?; - - let pit_config = kvm_pit_config { - flags: KVM_PIT_SPEAKER_DUMMY, - pad: Default::default(), - }; - vm_fd - .create_pit2(pit_config) - .with_context(|| MachineError::CrtPitErr)?; - - Ok(()) - } - - pub fn mem_show(&self) { - self.base.sys_mem.memspace_show(); - #[cfg(target_arch = "x86_64")] - self.base.sys_io.memspace_show(); - - let machine_ram = self.get_vm_ram(); - machine_ram.mtree(0_u32); - } - fn create_replaceable_devices(&mut self) -> Result<()> { let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { @@ -418,21 +387,13 @@ impl LightMachine { } Ok(id.to_string()) } - - /// Must be called after the CPUs have been realized and GIC has been created. - #[cfg(target_arch = "aarch64")] - fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { - let features = vcpu_cfg.unwrap_or_default(); - if features.pmu { - for cpu in self.base.cpus.iter() { - cpu.init_pmu()?; - } - } - Ok(()) - } } impl MachineOps for LightMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); @@ -527,6 +488,8 @@ impl MachineOps for LightMachine { &self, fwcfg: Option<&Arc>>, ) -> MachineResult { + use boot_loader::{load_linux, BootLoaderConfig}; + let boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); @@ -563,32 +526,6 @@ impl MachineOps for LightMachine { }) } - #[cfg(target_arch = "aarch64")] - fn load_boot_source( - &self, - fwcfg: Option<&Arc>>, - ) -> MachineResult { - let mut boot_source = self.base.boot_source.lock().unwrap(); - let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); - - let bootloader_config = BootLoaderConfig { - kernel: boot_source.kernel_file.clone(), - initrd, - mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, - }; - let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) - .with_context(|| MachineError::LoadKernErr)?; - if let Some(rd) = &mut boot_source.initrd { - rd.initrd_addr = layout.initrd_start; - rd.initrd_size = layout.initrd_size; - } - - Ok(CPUBootConfig { - fdt_addr: layout.dtb_start, - boot_pc: layout.boot_pc, - }) - } - fn realize_virtio_mmio_device( &mut self, dev: VirtioMmioDevice, @@ -608,38 +545,6 @@ impl MachineOps for LightMachine { Ok(realized_virtio_mmio_device) } - fn get_sys_mem(&mut self) -> &Arc { - &self.base.sys_mem - } - - fn get_vm_config(&self) -> Arc> { - self.base.vm_config.clone() - } - - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.base.vm_state - } - - fn get_migrate_info(&self) -> Incoming { - if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { - return (*mode, path.to_string()); - } - - (MigrateMode::Unknown, String::new()) - } - - fn get_sys_bus(&mut self) -> &SysBus { - &self.base.sysbus - } - - fn get_vm_ram(&self) -> &Arc { - &self.base.machine_ram - } - - fn get_numa_nodes(&self) -> &Option { - &self.base.numa_nodes - } - #[cfg(target_arch = "aarch64")] fn add_rtc_device(&mut self) -> MachineResult<()> { PL031::realize( @@ -742,10 +647,6 @@ impl MachineOps for LightMachine { syscall_whitelist() } - fn get_drive_files(&self) -> Arc>> { - self.base.drive_files.clone() - } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { let mut locked_vm = vm.lock().unwrap(); @@ -773,7 +674,7 @@ impl MachineOps for LightMachine { #[cfg(target_arch = "x86_64")] { locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; - LightMachine::arch_init()?; + locked_vm.arch_init(MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0)?; // Add mmio devices locked_vm @@ -801,7 +702,10 @@ impl MachineOps for LightMachine { { let (boot_config, cpu_config) = if migrate_info.0 == MigrateMode::Unknown { ( - Some(locked_vm.load_boot_source(None)?), + Some( + locked_vm + .load_boot_source(None, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?, + ), Some(locked_vm.load_cpu_features(vm_config)?), ) } else { @@ -858,14 +762,6 @@ impl MachineOps for LightMachine { Ok(()) } - - fn run(&self, paused: bool) -> MachineResult<()> { - self.vm_start( - paused, - &self.base.cpus, - &mut self.base.vm_state.0.lock().unwrap(), - ) - } } impl MachineLifecycle for LightMachine { @@ -994,13 +890,12 @@ impl DeviceInterface for LightMachine { fn query_cpus(&self) -> Response { let mut cpu_vec: Vec = Vec::new(); - for cpu_index in 0..self.base.cpu_topo.max_cpus { - if self.base.cpu_topo.get_mask(cpu_index as usize) == 1 { - let thread_id = self.base.cpus[cpu_index as usize].tid(); - let cpu_instance = self - .base - .cpu_topo - .get_topo_instance_for_qmp(cpu_index as usize); + let cpu_topo = self.get_cpu_topo(); + let cpus = self.get_cpus(); + for cpu_index in 0..cpu_topo.max_cpus { + if cpu_topo.get_mask(cpu_index as usize) == 1 { + let thread_id = cpus[cpu_index as usize].tid(); + let cpu_instance = cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 27a7cedd4..0288e53e4 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -15,10 +15,9 @@ mod syscall; pub use crate::error::MachineError; -use std::collections::HashMap; use std::mem::size_of; use std::ops::Deref; -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; @@ -40,10 +39,8 @@ use acpi::{ ROOT_COMPLEX_ENTRY_SIZE, }; use address_space::{AddressSpace, GuestAddress, Region}; -use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{ - CPUBootConfig, CPUFeatures, CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE, -}; +use cpu::{CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE}; + use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; use devices::acpi::power::PowerDev; #[cfg(feature = "ramfb")] @@ -52,7 +49,7 @@ use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; -use devices::sysbus::{SysBus, SysBusDevType, SysRes}; +use devices::sysbus::{SysBusDevType, SysRes}; use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; #[cfg(feature = "ramfb")] @@ -61,8 +58,7 @@ use machine_manager::config::ShutdownAction; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, DriveFile, Incoming, MigrateMode, NumaNode, NumaNodes, - PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, MigrateMode, NumaNode, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -156,8 +152,6 @@ pub struct StdMachine { dtb_vec: Vec, /// List contains the boot order of boot devices. boot_order_list: Arc>>, - /// FwCfg device. - fwcfg_dev: Option>>, } impl StdMachine { @@ -205,7 +199,6 @@ impl StdMachine { ), dtb_vec: Vec::new(), boot_order_list: Arc::new(Mutex::new(Vec::new())), - fwcfg_dev: None, }) } @@ -317,23 +310,6 @@ impl StdMachine { } } - /// Must be called after the CPUs have been realized and GIC has been created. - fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { - let features = vcpu_cfg.unwrap_or_default(); - if features.pmu { - for cpu in self.base.cpus.iter() { - cpu.init_pmu()?; - } - } - Ok(()) - } - - pub fn mem_show(&self) { - self.base.sys_mem.memspace_show(); - let machine_ram = self.get_vm_ram(); - machine_ram.mtree(0_u32); - } - pub fn get_vcpu_reg_val(&self, addr: u64, vcpu_index: usize) -> Option { if let Some(vcpu) = self.get_cpus().get(vcpu_index) { let (cpu_state, _) = vcpu.state(); @@ -357,10 +333,6 @@ impl StdMachine { } impl StdMachineOps for StdMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - fn init_pci_host(&self) -> StdResult<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); @@ -430,13 +402,17 @@ impl StdMachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, ) .with_context(|| "Failed to realize fwcfg device")?; - self.fwcfg_dev = Some(fwcfg_dev.clone()); + self.base.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(Some(fwcfg_dev)) } } impl MachineOps for StdMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); @@ -501,28 +477,6 @@ impl MachineOps for StdMachine { Ok(()) } - fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { - let mut boot_source = self.base.boot_source.lock().unwrap(); - let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); - - let bootloader_config = BootLoaderConfig { - kernel: boot_source.kernel_file.clone(), - initrd, - mem_start: MEM_LAYOUT[LayoutEntryType::Mem as usize].0, - }; - let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) - .with_context(|| MachineError::LoadKernErr)?; - if let Some(rd) = &mut boot_source.initrd { - rd.initrd_addr = layout.initrd_start; - rd.initrd_size = layout.initrd_size; - } - - Ok(CPUBootConfig { - fdt_addr: layout.dtb_start, - boot_pc: layout.boot_pc, - }) - } - fn add_rtc_device(&mut self) -> Result<()> { let rtc = PL031::default(); PL031::realize( @@ -579,10 +533,6 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn get_drive_files(&self) -> Arc>> { - self.base.drive_files.clone() - } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { use super::error::StandardVmError as StdErrorKind; @@ -615,11 +565,15 @@ impl MachineOps for StdMachine { let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; let migrate = locked_vm.get_migrate_info(); - let boot_config = if migrate.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(fwcfg.as_ref())?) - } else { - None - }; + let boot_config = + if migrate.0 == MigrateMode::Unknown { + Some(locked_vm.load_boot_source( + fwcfg.as_ref(), + MEM_LAYOUT[LayoutEntryType::Mem as usize].0, + )?) + } else { + None + }; let cpu_config = if migrate.0 == MigrateMode::Unknown { Some(locked_vm.load_cpu_features(vm_config)?) } else { @@ -764,57 +718,10 @@ impl MachineOps for StdMachine { Ok(()) } - fn run(&self, paused: bool) -> Result<()> { - self.vm_start( - paused, - &self.base.cpus, - &mut self.base.vm_state.0.lock().unwrap(), - ) - } - - fn get_sys_mem(&mut self) -> &Arc { - &self.base.sys_mem - } - - fn get_vm_config(&self) -> Arc> { - self.base.vm_config.clone() - } - - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.base.vm_state - } - - fn get_migrate_info(&self) -> Incoming { - if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { - return (*mode, path.to_string()); - } - - (MigrateMode::Unknown, String::new()) - } - fn get_pci_host(&mut self) -> StdResult<&Arc>> { Ok(&self.pci_host) } - fn get_sys_bus(&mut self) -> &SysBus { - &self.base.sysbus - } - - fn get_vm_ram(&self) -> &Arc { - &self.base.machine_ram - } - - fn get_numa_nodes(&self) -> &Option { - &self.base.numa_nodes - } - - fn get_fwcfg_dev(&mut self) -> Option>> { - if let Some(fwcfg_dev) = &self.fwcfg_dev { - return Some(fwcfg_dev.clone()); - } - None - } - fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d754ffcd7..5fe697190 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -41,7 +41,7 @@ use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] use self::x86_64::ich9_lpc::{PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET}; use super::Result as MachineResult; -use crate::{MachineBase, MachineOps}; +use crate::MachineOps; #[cfg(target_arch = "aarch64")] use aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] @@ -54,7 +54,6 @@ use address_space::{ AddressRange, FileBackend, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, }; use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; -use cpu::{CpuTopology, CPU}; use devices::legacy::FwCfgOps; use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; use devices::pci::PciBus; @@ -85,9 +84,7 @@ use virtio::{ #[cfg(target_arch = "x86_64")] use x86_64::{LayoutEntryType, MEM_LAYOUT}; -trait StdMachineOps: AcpiBuilder { - fn machine_base(&self) -> &MachineBase; - +trait StdMachineOps: AcpiBuilder + MachineOps { fn init_pci_host(&self) -> Result<()>; /// Build all ACPI tables and RSDP, and add them to FwCfg as file entries. @@ -151,7 +148,7 @@ trait StdMachineOps: AcpiBuilder { .with_context(|| "Failed to build ACPI MCFG table")?; xsdt_entries.push(mcfg_addr); - if let Some(numa_nodes) = self.get_guest_numa() { + if let Some(numa_nodes) = self.get_numa_nodes() { let srat_addr = self .build_srat_table(&acpi_tables, &mut loader) .with_context(|| "Failed to build ACPI SRAT table")?; @@ -194,18 +191,6 @@ trait StdMachineOps: AcpiBuilder { bail!("Not implemented"); } - fn get_cpu_topo(&self) -> &CpuTopology { - &self.machine_base().cpu_topo - } - - fn get_cpus(&self) -> &Vec> { - &self.machine_base().cpus - } - - fn get_guest_numa(&self) -> &Option { - &self.machine_base().numa_nodes - } - /// Register event notifier for reset of standard machine. /// /// # Arguments diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index b13a5cffc..7fbf73dd7 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -15,14 +15,12 @@ pub(crate) mod ich9_lpc; mod mch; mod syscall; -use std::collections::HashMap; use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; use log::{error, info}; use vmm_sys_util::eventfd::EventFd; @@ -44,13 +42,11 @@ use devices::legacy::{ SERIAL_ADDR, }; use devices::pci::{PciDevOps, PciHost}; -use devices::sysbus::SysBus; use hypervisor::kvm::KVM_FDS; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, DriveFile, Incoming, MigrateMode, NumaNode, NumaNodes, - PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, MigrateMode, NumaNode, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -199,30 +195,6 @@ impl StdMachine { Ok(()) } - fn arch_init() -> Result<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let identity_addr: u64 = MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0; - - vm_fd - .set_identity_map_address(identity_addr) - .with_context(|| MachineError::SetIdentityMapAddr)?; - - // Page table takes 1 page, TSS takes the following 3 pages. - vm_fd - .set_tss_address((identity_addr + 0x1000) as usize) - .with_context(|| MachineError::SetTssErr)?; - - let pit_config = kvm_pit_config { - flags: KVM_PIT_SPEAKER_DUMMY, - pad: Default::default(), - }; - vm_fd - .create_pit2(pit_config) - .with_context(|| MachineError::CrtPitErr)?; - Ok(()) - } - fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); @@ -240,23 +212,12 @@ impl StdMachine { Ok(()) } - pub fn mem_show(&self) { - self.base.sys_mem.memspace_show(); - self.base.sys_io.memspace_show(); - let machine_ram = self.get_vm_ram(); - machine_ram.mtree(0_u32); - } - pub fn get_vcpu_reg_val(&self, _addr: u64, _vcpu: usize) -> Option { None } } impl StdMachineOps for StdMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - fn init_pci_host(&self) -> Result<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); @@ -314,6 +275,10 @@ impl StdMachineOps for StdMachine { } impl MachineOps for StdMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let ram = self.get_vm_ram(); let below4g_size = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; @@ -416,10 +381,6 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn get_drive_files(&self) -> Arc>> { - self.base.drive_files.clone() - } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let clone_vm = vm.clone(); @@ -434,7 +395,7 @@ impl MachineOps for StdMachine { )?; locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; - StdMachine::arch_init()?; + locked_vm.arch_init(MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0)?; locked_vm .init_pci_host() @@ -608,57 +569,10 @@ impl MachineOps for StdMachine { Ok(()) } - fn run(&self, paused: bool) -> Result<()> { - self.vm_start( - paused, - &self.base.cpus, - &mut self.base.vm_state.0.lock().unwrap(), - ) - } - - fn get_sys_mem(&mut self) -> &Arc { - &self.base.sys_mem - } - - fn get_vm_config(&self) -> Arc> { - self.base.vm_config.clone() - } - - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { - &self.base.vm_state - } - - fn get_migrate_info(&self) -> Incoming { - if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() { - return (*mode, path.to_string()); - } - - (MigrateMode::Unknown, String::new()) - } - fn get_pci_host(&mut self) -> Result<&Arc>> { Ok(&self.pci_host) } - fn get_sys_bus(&mut self) -> &SysBus { - &self.base.sysbus - } - - fn get_vm_ram(&self) -> &Arc { - &self.base.machine_ram - } - - fn get_numa_nodes(&self) -> &Option { - &self.base.numa_nodes - } - - fn get_fwcfg_dev(&mut self) -> Option>> { - if let Some(fwcfg_dev) = &self.fwcfg_dev { - return Some(fwcfg_dev.clone()); - } - None - } - fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } -- Gitee From 99b5bcdee297da3cc526d3884caddcf3331c0e69 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 29 Oct 2023 19:03:28 +0800 Subject: [PATCH 1420/2187] machine: move MachineAddressInterface trait implement Signed-off-by: Mingwang Li --- machine/src/lib.rs | 52 ++++++++++++++++++++++++- machine/src/micro_vm/mod.rs | 31 +++------------ machine/src/standard_vm/aarch64/mod.rs | 16 ++------ machine/src/standard_vm/x86_64/mod.rs | 53 ++++---------------------- 4 files changed, 69 insertions(+), 83 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 92d77f87c..5c0b4281e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -41,7 +41,7 @@ use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{ - create_backend_mem, create_default_mem, AddressSpace, KvmMemoryListener, Region, + create_backend_mem, create_default_mem, AddressSpace, GuestAddress, KvmMemoryListener, Region, }; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; @@ -200,6 +200,56 @@ impl MachineBase { )), }) } + + #[cfg(target_arch = "x86_64")] + fn pio_in(&self, addr: u64, mut data: &mut [u8]) -> bool { + // The function pit_calibrate_tsc() in kernel gets stuck if data read from + // io-port 0x61 is not 0x20. + // This problem only happens before Linux version 4.18 (fixed by 368a540e0) + if addr == 0x61 { + data[0] = 0x20; + return true; + } + if addr == 0x64 { + // UEFI will read PS2 Keyboard's Status register 0x64 to detect if + // this device is present. + data[0] = 0xFF; + } + + let length = data.len() as u64; + self.sys_io + .read(&mut data, GuestAddress(addr), length) + .is_ok() + } + + #[cfg(target_arch = "x86_64")] + fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { + use standard_vm::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET; + + let count = data.len() as u64; + if addr == SLEEP_CTRL_OFFSET as u64 { + if let Err(e) = self.cpus[0].pause() { + log::error!("Fail to pause bsp, {:?}", e); + } + } + self.sys_io + .write(&mut data, GuestAddress(addr), count) + .is_ok() + } + + fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { + let length = data.len() as u64; + self.sys_mem + .read(&mut data, GuestAddress(addr), length) + .is_ok() + } + + fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { + let count = data.len() as u64; + self.sys_mem + .write(&mut data, GuestAddress(addr), count) + .is_ok() + } } pub trait MachineOps { diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 5dbe15e64..6974a608d 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -827,19 +827,8 @@ impl MachineLifecycle for LightMachine { impl MachineAddressInterface for LightMachine { #[cfg(target_arch = "x86_64")] - fn pio_in(&self, addr: u64, mut data: &mut [u8]) -> bool { - // The function pit_calibrate_tsc() in kernel gets stuck if data read from - // io-port 0x61 is not 0x20. - // This problem only happens before Linux version 4.18 (fixed by 368a540e0) - if addr == 0x61 { - data[0] = 0x20; - return true; - } - let length = data.len() as u64; - self.base - .sys_io - .read(&mut data, GuestAddress(addr), length) - .is_ok() + fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().pio_in(addr, data) } #[cfg(target_arch = "x86_64")] @@ -851,20 +840,12 @@ impl MachineAddressInterface for LightMachine { .is_ok() } - fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { - let length = data.len() as u64; - self.base - .sys_mem - .read(&mut data, GuestAddress(addr), length) - .is_ok() + fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().mmio_read(addr, data) } - fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { - let count = data.len() as u64; - self.base - .sys_mem - .write(&mut data, GuestAddress(addr), count) - .is_ok() + fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { + self.machine_base().mmio_write(addr, data) } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 0288e53e4..b2449a613 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -1180,20 +1180,12 @@ impl MachineLifecycle for StdMachine { } impl MachineAddressInterface for StdMachine { - fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { - let length = data.len() as u64; - self.base - .sys_mem - .read(&mut data, GuestAddress(addr), length) - .is_ok() + fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().mmio_read(addr, data) } - fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { - let count = data.len() as u64; - self.base - .sys_mem - .write(&mut data, GuestAddress(addr), count) - .is_ok() + fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { + self.machine_base().mmio_write(addr, data) } } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 7fbf73dd7..51bcf3377 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -24,7 +24,6 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use vmm_sys_util::eventfd::EventFd; -use self::ich9_lpc::SLEEP_CTRL_OFFSET; use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; use crate::error::MachineError; @@ -827,56 +826,20 @@ impl MachineLifecycle for StdMachine { } impl MachineAddressInterface for StdMachine { - fn pio_in(&self, addr: u64, mut data: &mut [u8]) -> bool { - if (0x60..=0x64).contains(&addr) { - // The function pit_calibrate_tsc() in kernel gets stuck if data read from - // io-port 0x61 is not 0x20. - // This problem only happens before Linux version 4.18 (fixed by 368a540e0) - if addr == 0x61 { - data[0] = 0x20; - return true; - } - if addr == 0x64 { - // UEFI will read PS2 Keyboard's Status register 0x64 to detect if - // this device is present. - data[0] = 0xFF; - } - } - - let length = data.len() as u64; - self.base - .sys_io - .read(&mut data, GuestAddress(addr), length) - .is_ok() + fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().pio_in(addr, data) } - fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { - let count = data.len() as u64; - if addr == SLEEP_CTRL_OFFSET as u64 { - if let Err(e) = self.base.cpus[0].pause() { - error!("Fail to pause bsp, {:?}", e); - } - } - self.base - .sys_io - .write(&mut data, GuestAddress(addr), count) - .is_ok() + fn pio_out(&self, addr: u64, data: &[u8]) -> bool { + self.machine_base().pio_out(addr, data) } - fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { - let length = data.len() as u64; - self.base - .sys_mem - .read(&mut data, GuestAddress(addr), length) - .is_ok() + fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().mmio_read(addr, data) } - fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { - let count = data.len() as u64; - self.base - .sys_mem - .write(&mut data, GuestAddress(addr), count) - .is_ok() + fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { + self.machine_base().mmio_write(addr, data) } } -- Gitee From fd52d04e54a67c947b3a22c00a71282f21900998 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 29 Oct 2023 19:35:34 +0800 Subject: [PATCH 1421/2187] serial: aarch64 micro machine serial use pl011 Signed-off-by: Mingwang Li --- docs/boot.ch.md | 29 +++++++------ docs/boot.md | 30 +++++++------ machine/src/lib.rs | 2 + machine/src/micro_vm/mod.rs | 60 +++++++++++++++++--------- machine/src/standard_vm/aarch64/mod.rs | 4 ++ machine/src/standard_vm/x86_64/mod.rs | 4 ++ 6 files changed, 83 insertions(+), 46 deletions(-) diff --git a/docs/boot.ch.md b/docs/boot.ch.md index 5b95bb099..09d459b60 100644 --- a/docs/boot.ch.md +++ b/docs/boot.ch.md @@ -2,6 +2,21 @@ StratoVirt提供了轻量虚拟机和标准虚拟机两种机型。两种机型的启动过程如下。 +## 前置参数设置 + +```shell +arch=`uname -m` +if [ ${arch} = "x86_64" ]; then + con=ttyS0 + machine="q35" +elif [ ${arch} = "aarch64" ]; then + con=ttyAMA0 + machine="virt" +else + echo "${arch} architecture not supported." + exit 1 +fi + ## 轻量虚拟机启动过程 ### 1. 构建内核镜像 @@ -54,7 +69,7 @@ Rootfs镜像是一种文件系统镜像。在StratoVirt启动时可以挂载带 -kernel /path/to/kernel \ -smp 1 \ -m 1024m \ - -append "console=ttyS0 pci=off reboot=k quiet panic=1 root=/dev/vda" \ + -append "console=${con} pci=off reboot=k quiet panic=1 root=/dev/vda" \ -drive file=/path/to/rootfs,id=rootfs,readonly=off,direct=off \ -device virtio-blk-device,drive=rootfs,id=rootfs \ -qmp unix:/path/to/socket,server,nowait \ @@ -228,18 +243,6 @@ $ qemu-img convert -f qcow2 -O raw openEuler-21.03-x86_64.qcow2 openEuler-21.03- 首先给出 kernel + rootfs 的启动命令,具体如下: ```shell -arch=`uname -m` -if [ ${arch} = "x86_64" ]; then - con=ttyS0 - machine="q35" -elif [ ${arch} = "aarch64" ]; then - con=ttyAMA0 - machine="virt" -else - echo "${arch} architecture not supported." - exit 1 -fi - /usr/bin/stratovirt \ -machine ${machine} \ -kernel /path/to/kernel \ diff --git a/docs/boot.md b/docs/boot.md index 4cec5dc6a..72fe1de06 100644 --- a/docs/boot.md +++ b/docs/boot.md @@ -3,6 +3,22 @@ StratoVirt provides two kinds of machine, which are microvm and standard VM. The boot process of these two machines are as follows. +## pre-parameter setting + +```shell +arch=`uname -m` +if [ ${arch} = "x86_64" ]; then + con=ttyS0 + machine="q35" +elif [ ${arch} = "aarch64" ]; then + con=ttyAMA0 + machine="virt" +else + echo "${arch} architecture not supported." + exit 1 +fi +``` + ## microvm boot process ### 1. Build kernel @@ -56,7 +72,7 @@ be mounted at boot time in StratoVirt. You can check [Appendix](#2Appendix). -kernel /path/to/kernel \ -smp 1 \ -m 1024m \ - -append "console=ttyS0 pci=off reboot=k quiet panic=1 root=/dev/vda" \ + -append "console=${con} pci=off reboot=k quiet panic=1 root=/dev/vda" \ -drive file=/path/to/rootfs,id=rootfs,readonly=off,direct=off \ -device virtio-blk-device,drive=rootfs,id=rootfs \ -qmp unix:/path/to/socket,server,nowait \ @@ -232,18 +248,6 @@ be omitted whose unit is 1. But code storage file with unit 0 is necessary. Run the following commands to boot with the kernel and rootfs: ```shell -arch=`uname -m` -if [ ${arch} = "x86_64" ]; then - con=ttyS0 - machine="q35" -elif [ ${arch} = "aarch64" ]; then - con=ttyAMA0 - machine="virt" -else - echo "${arch} architecture not supported." - exit 1 -fi - /usr/bin/stratovirt \ -machine ${machine} \ -kernel /path/to/kernel \ diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5c0b4281e..6a1a137c4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -255,6 +255,8 @@ impl MachineBase { pub trait MachineOps { fn machine_base(&self) -> &MachineBase; + fn machine_base_mut(&mut self) -> &mut MachineBase; + fn build_smbios( &self, fw_cfg: &Arc>, diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 6974a608d..cdadbde42 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -58,11 +58,8 @@ use cpu::PMU_INTR; use cpu::{CPUTopology, CpuLifecycleState}; #[cfg(target_arch = "x86_64")] use devices::legacy::FwCfgOps; -use devices::legacy::Serial; #[cfg(target_arch = "aarch64")] use devices::legacy::PL031; -#[cfg(target_arch = "x86_64")] -use devices::legacy::SERIAL_ADDR; use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; #[cfg(target_arch = "aarch64")] use devices::sysbus::{SysBusDevType, SysRes}; @@ -394,6 +391,10 @@ impl MachineOps for LightMachine { &self.base } + fn machine_base_mut(&mut self) -> &mut MachineBase { + &mut self.base + } + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); @@ -567,27 +568,39 @@ impl MachineOps for LightMachine { Ok(()) } - fn add_serial_device(&mut self, config: &SerialConfig) -> MachineResult<()> { - #[cfg(target_arch = "x86_64")] - let region_base: u64 = SERIAL_ADDR; - #[cfg(target_arch = "aarch64")] + #[cfg(target_arch = "aarch64")] + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { + use devices::legacy::PL011; + let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; - #[cfg(target_arch = "x86_64")] - let region_size: u64 = 8; - #[cfg(target_arch = "aarch64")] let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; + let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; + let base = self.machine_base_mut(); + pl011 + .realize( + &mut base.sysbus, + region_base, + region_size, + &base.boot_source, + ) + .with_context(|| "Failed to realize PL011") + } + + #[cfg(target_arch = "x86_64")] + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { + use devices::legacy::{Serial, SERIAL_ADDR}; + + let region_base: u64 = SERIAL_ADDR; + let region_size: u64 = 8; let serial = Serial::new(config.clone()); serial .realize( - &mut self.base.sysbus, + &mut self.machine_base_mut().sysbus, region_base, region_size, - #[cfg(target_arch = "aarch64")] - &self.base.boot_source, ) - .with_context(|| "Failed to realize serial device.")?; - Ok(()) + .with_context(|| "Failed to realize serial device.") } fn add_virtio_mmio_net( @@ -1281,11 +1294,14 @@ impl EventLoopManager for LightMachine { // * `fdt` - Flatted device-tree blob where serial node will be filled into. #[cfg(target_arch = "aarch64")] fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("uart@{:x}", res.region_base); + let node = format!("pl011@{:x}", res.region_base); let serial_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "ns16550a")?; - fdt.set_property_string("clock-names", "apb_pclk")?; - fdt.set_property_u32("clocks", device_tree::CLK_PHANDLE)?; + fdt.set_property_string("compatible", "arm,pl011\0arm,primecell")?; + fdt.set_property_string("clock-names", "uartclk\0apb_pclk")?; + fdt.set_property_array_u32( + "clocks", + &[device_tree::CLK_PHANDLE, device_tree::CLK_PHANDLE], + )?; fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; fdt.set_property_array_u32( "interrupts", @@ -1513,7 +1529,7 @@ impl CompileFDTHelper for LightMachine { let dev_type = locked_dev.sysbusdev_base().dev_type; let sys_res = locked_dev.sysbusdev_base().res; match dev_type { - SysBusDevType::Serial => generate_serial_device_node(fdt, &sys_res)?, + SysBusDevType::PL011 => generate_serial_device_node(fdt, &sys_res)?, SysBusDevType::Rtc => generate_rtc_device_node(fdt, &sys_res)?, SysBusDevType::VirtioMmio => generate_virtio_devices_node(fdt, &sys_res)?, _ => (), @@ -1531,6 +1547,10 @@ impl CompileFDTHelper for LightMachine { let cmdline = &boot_source.kernel_cmdline.to_string(); fdt.set_property_string("bootargs", cmdline.as_str())?; + let pl011_property_string = + format!("/pl011@{:x}", MEM_LAYOUT[LayoutEntryType::Uart as usize].0); + fdt.set_property_string("stdout-path", &pl011_property_string)?; + match &boot_source.initrd { Some(initrd) => { fdt.set_property_u64("linux,initrd-start", initrd.initrd_addr)?; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index b2449a613..75e3cdf5c 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -413,6 +413,10 @@ impl MachineOps for StdMachine { &self.base } + fn machine_base_mut(&mut self) -> &mut MachineBase { + &mut self.base + } + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 51bcf3377..b9b68f075 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -278,6 +278,10 @@ impl MachineOps for StdMachine { &self.base } + fn machine_base_mut(&mut self) -> &mut MachineBase { + &mut self.base + } + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let ram = self.get_vm_ram(); let below4g_size = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; -- Gitee From bdb8b4b4e08a0ce491fe486347cd2b695bd916d4 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 29 Oct 2023 21:03:29 +0800 Subject: [PATCH 1422/2187] fdt: fdt implements normalization Signed-off-by: Mingwang Li --- machine/src/fdt.rs | 343 ++++++++++++++++++ machine/src/lib.rs | 2 + machine/src/micro_vm/mod.rs | 260 +------------- machine/src/standard_vm/aarch64/mod.rs | 480 ++++--------------------- 4 files changed, 422 insertions(+), 663 deletions(-) create mode 100644 machine/src/fdt.rs diff --git a/machine/src/fdt.rs b/machine/src/fdt.rs new file mode 100644 index 000000000..88f4d0eb5 --- /dev/null +++ b/machine/src/fdt.rs @@ -0,0 +1,343 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::MachineBase; +use cpu::PMU_INTR; +use devices::sysbus::{SysBusDevType, SysRes}; +use util::device_tree::{self, FdtBuilder}; + +/// Function that helps to generate arm pmu in device-tree. +/// +/// # Arguments +/// +/// * `fdt` - Flatted device-tree blob where node will be filled into. +fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { + let node = "pmu"; + let pmu_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("compatible", "arm,armv8-pmuv3")?; + fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; + fdt.set_property_array_u32( + "interrupts", + &[ + device_tree::GIC_FDT_IRQ_TYPE_PPI, + PMU_INTR, + device_tree::IRQ_TYPE_LEVEL_HIGH, + ], + )?; + fdt.end_node(pmu_node_dep) +} + +/// Function that helps to generate serial node in device-tree. +/// +/// # Arguments +/// +/// * `dev_info` - Device resource info of serial device. +/// * `fdt` - Flatted device-tree blob where serial node will be filled into. +fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { + let node = format!("pl011@{:x}", res.region_base); + let serial_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("compatible", "arm,pl011\0arm,primecell")?; + fdt.set_property_string("clock-names", "uartclk\0apb_pclk")?; + fdt.set_property_array_u32( + "clocks", + &[device_tree::CLK_PHANDLE, device_tree::CLK_PHANDLE], + )?; + fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; + fdt.set_property_array_u32( + "interrupts", + &[ + device_tree::GIC_FDT_IRQ_TYPE_SPI, + res.irq as u32, + device_tree::IRQ_TYPE_EDGE_RISING, + ], + )?; + fdt.end_node(serial_node_dep) +} + +/// Function that helps to generate RTC node in device-tree. +/// +/// # Arguments +/// +/// * `dev_info` - Device resource info of RTC device. +/// * `fdt` - Flatted device-tree blob where RTC node will be filled into. +fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { + let node = format!("pl031@{:x}", res.region_base); + let rtc_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("compatible", "arm,pl031\0arm,primecell\0")?; + fdt.set_property_string("clock-names", "apb_pclk")?; + fdt.set_property_u32("clocks", device_tree::CLK_PHANDLE)?; + fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; + fdt.set_property_array_u32( + "interrupts", + &[ + device_tree::GIC_FDT_IRQ_TYPE_SPI, + res.irq as u32, + device_tree::IRQ_TYPE_LEVEL_HIGH, + ], + )?; + fdt.end_node(rtc_node_dep) +} + +/// Function that helps to generate Virtio-Mmio device's node in device-tree. +/// +/// # Arguments +/// +/// * `dev_info` - Device resource info of Virtio-Mmio device. +/// * `fdt` - Flatted device-tree blob where node will be filled into. +fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { + let node = format!("virtio_mmio@{:x}", res.region_base); + let virtio_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("compatible", "virtio,mmio")?; + fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; + fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; + fdt.set_property_array_u32( + "interrupts", + &[ + device_tree::GIC_FDT_IRQ_TYPE_SPI, + res.irq as u32, + device_tree::IRQ_TYPE_EDGE_RISING, + ], + )?; + fdt.end_node(virtio_node_dep) +} + +/// Function that helps to generate fw-cfg node in device-tree. +/// +/// # Arguments +/// +/// * `dev_info` - Device resource info of fw-cfg device. +/// * `fdt` - Flatted device-tree blob where fw-cfg node will be filled into. +fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { + let node = format!("fw-cfg@{:x}", res.region_base); + let fwcfg_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("compatible", "qemu,fw-cfg-mmio")?; + fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; + fdt.end_node(fwcfg_node_dep) +} + +/// Function that helps to generate flash node in device-tree. +/// +/// # Arguments +/// +/// * `dev_info` - Device resource info of fw-cfg device. +/// * `flash` - Flatted device-tree blob where fw-cfg node will be filled into. +fn generate_flash_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { + let flash_base = res.region_base; + let flash_size = res.region_size; + let node = format!("flash@{:x}", flash_base); + let flash_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("compatible", "cfi-flash")?; + fdt.set_property_array_u64( + "reg", + &[flash_base, flash_size, flash_base + flash_size, flash_size], + )?; + fdt.set_property_u32("bank-width", 4)?; + fdt.end_node(flash_node_dep) +} + +/// Trait that helps to generate all nodes in device-tree. +#[allow(clippy::upper_case_acronyms)] +trait CompileFDTHelper { + /// Function that helps to generate cpu nodes. + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + /// Function that helps to generate Virtio-mmio devices' nodes. + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + /// Function that helps to generate numa node distances. + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; +} + +impl CompileFDTHelper for MachineBase { + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + let node = "cpus"; + + let cpus_node_dep = fdt.begin_node(node)?; + fdt.set_property_u32("#address-cells", 0x02)?; + fdt.set_property_u32("#size-cells", 0x0)?; + + // Generate CPU topology + let cpu_map_node_dep = fdt.begin_node("cpu-map")?; + for socket in 0..self.cpu_topo.sockets { + let sock_name = format!("cluster{}", socket); + let sock_node_dep = fdt.begin_node(&sock_name)?; + for cluster in 0..self.cpu_topo.clusters { + let clster = format!("cluster{}", cluster); + let cluster_node_dep = fdt.begin_node(&clster)?; + + for core in 0..self.cpu_topo.cores { + let core_name = format!("core{}", core); + let core_node_dep = fdt.begin_node(&core_name)?; + + for thread in 0..self.cpu_topo.threads { + let thread_name = format!("thread{}", thread); + let thread_node_dep = fdt.begin_node(&thread_name)?; + let vcpuid = self.cpu_topo.threads * self.cpu_topo.cores * cluster + + self.cpu_topo.threads * core + + thread; + fdt.set_property_u32( + "cpu", + u32::from(vcpuid) + device_tree::CPU_PHANDLE_START, + )?; + fdt.end_node(thread_node_dep)?; + } + fdt.end_node(core_node_dep)?; + } + fdt.end_node(cluster_node_dep)?; + } + fdt.end_node(sock_node_dep)?; + } + fdt.end_node(cpu_map_node_dep)?; + + for cpu_index in 0..self.cpu_topo.nrcpus { + let mpidr = self.cpus[cpu_index as usize].arch().lock().unwrap().mpidr(); + + let node = format!("cpu@{:x}", mpidr); + let mpidr_node_dep = fdt.begin_node(&node)?; + fdt.set_property_u32( + "phandle", + u32::from(cpu_index) + device_tree::CPU_PHANDLE_START, + )?; + fdt.set_property_string("device_type", "cpu")?; + fdt.set_property_string("compatible", "arm,arm-v8")?; + if self.cpu_topo.max_cpus > 1 { + fdt.set_property_string("enable-method", "psci")?; + } + fdt.set_property_u64("reg", mpidr & 0x007F_FFFF)?; + fdt.set_property_u32("phandle", device_tree::FIRST_VCPU_PHANDLE)?; + + if let Some(numa_nodes) = &self.numa_nodes { + for numa_index in 0..numa_nodes.len() { + let numa_node = numa_nodes.get(&(numa_index as u32)); + if numa_node.unwrap().cpus.contains(&(cpu_index)) { + fdt.set_property_u32("numa-node-id", numa_index as u32)?; + } + } + } + + fdt.end_node(mpidr_node_dep)?; + } + + fdt.end_node(cpus_node_dep)?; + + if self.cpu_features.pmu { + generate_pmu_node(fdt)?; + } + + Ok(()) + } + + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + // timer + let mut cells: Vec = Vec::new(); + for &irq in [13, 14, 11, 10].iter() { + cells.push(device_tree::GIC_FDT_IRQ_TYPE_PPI); + cells.push(irq); + cells.push(device_tree::IRQ_TYPE_LEVEL_HIGH); + } + let node = "timer"; + let timer_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("compatible", "arm,armv8-timer")?; + fdt.set_property("always-on", &Vec::new())?; + fdt.set_property_array_u32("interrupts", &cells)?; + fdt.end_node(timer_node_dep)?; + + // clock + let node = "apb-pclk"; + let clock_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("compatible", "fixed-clock")?; + fdt.set_property_string("clock-output-names", "clk24mhz")?; + fdt.set_property_u32("#clock-cells", 0x0)?; + fdt.set_property_u32("clock-frequency", 24_000_000)?; + fdt.set_property_u32("phandle", device_tree::CLK_PHANDLE)?; + fdt.end_node(clock_node_dep)?; + + // psci + let node = "psci"; + let psci_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("compatible", "arm,psci-0.2")?; + fdt.set_property_string("method", "hvc")?; + fdt.end_node(psci_node_dep)?; + + for dev in self.sysbus.devices.iter() { + let locked_dev = dev.lock().unwrap(); + match locked_dev.sysbusdev_base().dev_type { + SysBusDevType::PL011 => { + // SAFETY: Legacy devices guarantee is not empty. + generate_serial_device_node(fdt, &locked_dev.sysbusdev_base().res)? + } + SysBusDevType::Rtc => { + // SAFETY: Legacy devices guarantee is not empty. + generate_rtc_device_node(fdt, &locked_dev.sysbusdev_base().res)? + } + SysBusDevType::VirtioMmio => { + // SAFETY: Legacy devices guarantee is not empty. + generate_virtio_devices_node(fdt, &locked_dev.sysbusdev_base().res)? + } + SysBusDevType::FwCfg => { + // SAFETY: Legacy devices guarantee is not empty. + generate_fwcfg_device_node(fdt, &locked_dev.sysbusdev_base().res)?; + } + SysBusDevType::Flash => { + // SAFETY: Legacy devices guarantee is not empty. + generate_flash_device_node(fdt, &locked_dev.sysbusdev_base().res)?; + } + _ => (), + } + } + + Ok(()) + } + + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + if self.numa_nodes.is_none() { + return Ok(()); + } + + let distance_node_dep = fdt.begin_node("distance-map")?; + fdt.set_property_string("compatible", "numa-distance-map-v1")?; + + let mut matrix = Vec::new(); + let numa_nodes = self.numa_nodes.as_ref().unwrap(); + let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); + for (id, node) in numa_nodes.iter().enumerate() { + let distances = &node.1.distances; + for i in existing_nodes.iter() { + matrix.push(id as u32); + matrix.push(*i); + let dist: u32 = if id as u32 == *i { + 10 + } else if let Some(distance) = distances.get(i) { + *distance as u32 + } else { + 20 + }; + matrix.push(dist); + } + } + + fdt.set_property_array_u32("distance-matrix", matrix.as_ref())?; + fdt.end_node(distance_node_dep) + } +} + +impl device_tree::CompileFDT for MachineBase { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fdt.set_property_string("compatible", "linux,dummy-virt")?; + fdt.set_property_u32("#address-cells", 0x2)?; + fdt.set_property_u32("#size-cells", 0x2)?; + fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; + + self.generate_cpu_nodes(fdt)?; + self.generate_devices_node(fdt)?; + self.irq_chip.as_ref().unwrap().generate_fdt_node(fdt)?; + self.generate_distance_node(fdt) + } +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6a1a137c4..ce2fe5ac0 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -13,6 +13,8 @@ pub mod error; pub mod standard_vm; +#[cfg(target_arch = "aarch64")] +mod fdt; mod micro_vm; #[cfg(target_arch = "x86_64")] mod vm_state; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index cdadbde42..c105a644e 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -53,8 +53,6 @@ use crate::MachineBase; use address_space::{AddressSpace, GuestAddress, Region}; #[cfg(target_arch = "x86_64")] use cpu::CPUBootConfig; -#[cfg(target_arch = "aarch64")] -use cpu::PMU_INTR; use cpu::{CPUTopology, CpuLifecycleState}; #[cfg(target_arch = "x86_64")] use devices::legacy::FwCfgOps; @@ -62,8 +60,6 @@ use devices::legacy::FwCfgOps; use devices::legacy::PL031; use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; #[cfg(target_arch = "aarch64")] -use devices::sysbus::{SysBusDevType, SysRes}; -#[cfg(target_arch = "aarch64")] use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; #[cfg(target_arch = "x86_64")] use hypervisor::kvm::KVM_FDS; @@ -1286,199 +1282,18 @@ impl EventLoopManager for LightMachine { } } -// Function that helps to generate serial node in device-tree. -// -// # Arguments -// -// * `dev_info` - Device resource info of serial device. -// * `fdt` - Flatted device-tree blob where serial node will be filled into. -#[cfg(target_arch = "aarch64")] -fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("pl011@{:x}", res.region_base); - let serial_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "arm,pl011\0arm,primecell")?; - fdt.set_property_string("clock-names", "uartclk\0apb_pclk")?; - fdt.set_property_array_u32( - "clocks", - &[device_tree::CLK_PHANDLE, device_tree::CLK_PHANDLE], - )?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_SPI, - res.irq as u32, - device_tree::IRQ_TYPE_EDGE_RISING, - ], - )?; - fdt.end_node(serial_node_dep)?; - - Ok(()) -} - -// Function that helps to generate RTC node in device-tree. -// -// # Arguments -// -// * `dev_info` - Device resource info of RTC device. -// * `fdt` - Flatted device-tree blob where RTC node will be filled into. -#[cfg(target_arch = "aarch64")] -fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("pl031@{:x}", res.region_base); - let rtc_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "arm,pl031\0arm,primecell\0")?; - fdt.set_property_string("clock-names", "apb_pclk")?; - fdt.set_property_u32("clocks", device_tree::CLK_PHANDLE)?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_SPI, - res.irq as u32, - device_tree::IRQ_TYPE_LEVEL_HIGH, - ], - )?; - fdt.end_node(rtc_node_dep)?; - - Ok(()) -} - -// Function that helps to generate Virtio-Mmio device's node in device-tree. -// -// # Arguments -// -// * `dev_info` - Device resource info of Virtio-Mmio device. -// * `fdt` - Flatted device-tree blob where node will be filled into. -#[cfg(target_arch = "aarch64")] -fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("virtio_mmio@{:x}", res.region_base); - let virtio_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "virtio,mmio")?; - fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_SPI, - res.irq as u32, - device_tree::IRQ_TYPE_EDGE_RISING, - ], - )?; - fdt.end_node(virtio_node_dep)?; - Ok(()) -} - -#[cfg(target_arch = "aarch64")] -fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { - let node = "pmu"; - let pmu_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "arm,armv8-pmuv3")?; - fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_PPI, - PMU_INTR, - device_tree::IRQ_TYPE_LEVEL_HIGH, - ], - )?; - - fdt.end_node(pmu_node_dep)?; - Ok(()) -} - /// Trait that helps to generate all nodes in device-tree. #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "aarch64")] trait CompileFDTHelper { - /// Function that helps to generate cpu nodes. - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate memory nodes. fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; - /// Function that helps to generate Virtio-mmio devices' nodes. - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate the chosen node. fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; } #[cfg(target_arch = "aarch64")] impl CompileFDTHelper for LightMachine { - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - let node = "cpus"; - - let cpus_node_dep = fdt.begin_node(node)?; - fdt.set_property_u32("#address-cells", 0x02)?; - fdt.set_property_u32("#size-cells", 0x0)?; - - // Generate CPU topology - let cpu_map_node_dep = fdt.begin_node("cpu-map")?; - for socket in 0..self.base.cpu_topo.sockets { - let sock_name = format!("cluster{}", socket); - let sock_node_dep = fdt.begin_node(&sock_name)?; - for cluster in 0..self.base.cpu_topo.clusters { - let clster = format!("cluster{}", cluster); - let cluster_node_dep = fdt.begin_node(&clster)?; - - for core in 0..self.base.cpu_topo.cores { - let core_name = format!("core{}", core); - let core_node_dep = fdt.begin_node(&core_name)?; - - for thread in 0..self.base.cpu_topo.threads { - let thread_name = format!("thread{}", thread); - let thread_node_dep = fdt.begin_node(&thread_name)?; - let vcpuid = self.base.cpu_topo.threads - * self.base.cpu_topo.cores - * self.base.cpu_topo.clusters - * socket - + self.base.cpu_topo.threads * self.base.cpu_topo.cores * cluster - + self.base.cpu_topo.threads * core - + thread; - fdt.set_property_u32( - "cpu", - u32::from(vcpuid) + device_tree::CPU_PHANDLE_START, - )?; - fdt.end_node(thread_node_dep)?; - } - fdt.end_node(core_node_dep)?; - } - fdt.end_node(cluster_node_dep)?; - } - fdt.end_node(sock_node_dep)?; - } - fdt.end_node(cpu_map_node_dep)?; - - for cpu_index in 0..self.base.cpu_topo.nrcpus { - let mpidr = self.base.cpus[cpu_index as usize] - .arch() - .lock() - .unwrap() - .mpidr(); - - let node = format!("cpu@{:x}", mpidr); - let mpidr_node_dep = fdt.begin_node(&node)?; - fdt.set_property_u32( - "phandle", - u32::from(cpu_index) + device_tree::CPU_PHANDLE_START, - )?; - fdt.set_property_string("device_type", "cpu")?; - fdt.set_property_string("compatible", "arm,arm-v8")?; - if self.base.cpu_topo.max_cpus > 1 { - fdt.set_property_string("enable-method", "psci")?; - } - fdt.set_property_u64("reg", mpidr & 0x007F_FFFF)?; - fdt.end_node(mpidr_node_dep)?; - } - - fdt.end_node(cpus_node_dep)?; - - // CPU Features : PMU - if self.base.cpu_features.pmu { - generate_pmu_node(fdt)?; - } - - Ok(()) - } - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let mem_size = self.base.sys_mem.memory_end_address().raw_value() @@ -1487,56 +1302,7 @@ impl CompileFDTHelper for LightMachine { let memory_node_dep = fdt.begin_node(node)?; fdt.set_property_string("device_type", "memory")?; fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; - fdt.end_node(memory_node_dep)?; - - Ok(()) - } - - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - // timer - let mut cells: Vec = Vec::new(); - for &irq in [13, 14, 11, 10].iter() { - cells.push(device_tree::GIC_FDT_IRQ_TYPE_PPI); - cells.push(irq); - cells.push(device_tree::IRQ_TYPE_LEVEL_HIGH); - } - let node = "timer"; - let timer_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "arm,armv8-timer")?; - fdt.set_property("always-on", &Vec::new())?; - fdt.set_property_array_u32("interrupts", &cells)?; - fdt.end_node(timer_node_dep)?; - - // clock - let node = "apb-pclk"; - let clock_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "fixed-clock")?; - fdt.set_property_string("clock-output-names", "clk24mhz")?; - fdt.set_property_u32("#clock-cells", 0x0)?; - fdt.set_property_u32("clock-frequency", 24_000_000)?; - fdt.set_property_u32("phandle", device_tree::CLK_PHANDLE)?; - fdt.end_node(clock_node_dep)?; - - // psci - let node = "psci"; - let psci_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "arm,psci-0.2")?; - fdt.set_property_string("method", "hvc")?; - fdt.end_node(psci_node_dep)?; - - for dev in self.base.sysbus.devices.iter() { - let locked_dev = dev.lock().unwrap(); - let dev_type = locked_dev.sysbusdev_base().dev_type; - let sys_res = locked_dev.sysbusdev_base().res; - match dev_type { - SysBusDevType::PL011 => generate_serial_device_node(fdt, &sys_res)?, - SysBusDevType::Rtc => generate_rtc_device_node(fdt, &sys_res)?, - SysBusDevType::VirtioMmio => generate_virtio_devices_node(fdt, &sys_res)?, - _ => (), - } - } - - Ok(()) + fdt.end_node(memory_node_dep) } fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { @@ -1558,9 +1324,7 @@ impl CompileFDTHelper for LightMachine { } None => {} } - fdt.end_node(chosen_node_dep)?; - - Ok(()) + fdt.end_node(chosen_node_dep) } } @@ -1568,26 +1332,10 @@ impl CompileFDTHelper for LightMachine { impl device_tree::CompileFDT for LightMachine { fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node_dep = fdt.begin_node("")?; - - fdt.set_property_string("compatible", "linux,dummy-virt")?; - fdt.set_property_u32("#address-cells", 0x2)?; - fdt.set_property_u32("#size-cells", 0x2)?; - fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; - - self.generate_cpu_nodes(fdt)?; + self.base.generate_fdt_node(fdt)?; self.generate_memory_node(fdt)?; - self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; - // SAFETY: ARM architecture must have interrupt controllers in user mode. - self.base - .irq_chip - .as_ref() - .unwrap() - .generate_fdt_node(fdt)?; - - fdt.end_node(node_dep)?; - - Ok(()) + fdt.end_node(node_dep) } } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 75e3cdf5c..42587f429 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -49,7 +49,7 @@ use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; -use devices::sysbus::{SysBusDevType, SysRes}; +use devices::sysbus::SysBusDevType; use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::KVM_FDS; #[cfg(feature = "ramfb")] @@ -1231,312 +1231,88 @@ impl EventLoopManager for StdMachine { } } -// Function that helps to generate pci node in device-tree. -// -// # Arguments -// -// * `fdt` - Flatted device-tree blob where node will be filled into. -fn generate_pci_host_node(fdt: &mut FdtBuilder) -> util::Result<()> { - let pcie_ecam_base = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; - let pcie_ecam_size = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1; - let pcie_buses_num = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1 >> 20; - let node = format!("pcie@{:x}", pcie_ecam_base); - let pci_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "pci-host-ecam-generic")?; - fdt.set_property_string("device_type", "pci")?; - fdt.set_property_array_u64("reg", &[pcie_ecam_base, pcie_ecam_size])?; - fdt.set_property_array_u32("bus-range", &[0, (pcie_buses_num - 1) as u32])?; - fdt.set_property_u32("linux,pci-domain", 0)?; - fdt.set_property_u32("#address-cells", 3)?; - fdt.set_property_u32("#size-cells", 2)?; - - let high_pcie_mmio_base = MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize].0; - let high_pcie_mmio_size = MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize].1; - let fdt_pci_mmio_type_64bit: u32 = device_tree::FDT_PCI_RANGE_MMIO_64BIT; - let high_mmio_base_hi: u32 = (high_pcie_mmio_base >> 32) as u32; - let high_mmio_base_lo: u32 = (high_pcie_mmio_base & 0xffff_ffff) as u32; - let high_mmio_size_hi: u32 = (high_pcie_mmio_size >> 32) as u32; - let high_mmio_size_lo: u32 = (high_pcie_mmio_size & 0xffff_ffff) as u32; - - let pcie_mmio_base = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; - let pcie_mmio_size = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; - let fdt_pci_mmio_type: u32 = device_tree::FDT_PCI_RANGE_MMIO; - let mmio_base_hi: u32 = (pcie_mmio_base >> 32) as u32; - let mmio_base_lo: u32 = (pcie_mmio_base & 0xffff_ffff) as u32; - let mmio_size_hi: u32 = (pcie_mmio_size >> 32) as u32; - let mmio_size_lo: u32 = (pcie_mmio_size & 0xffff_ffff) as u32; - - let pcie_pio_base = MEM_LAYOUT[LayoutEntryType::PciePio as usize].0; - let pcie_pio_size = MEM_LAYOUT[LayoutEntryType::PciePio as usize].1; - let fdt_pci_pio_type: u32 = device_tree::FDT_PCI_RANGE_IOPORT; - let pio_base_hi: u32 = (pcie_pio_base >> 32) as u32; - let pio_base_lo: u32 = (pcie_pio_base & 0xffff_ffff) as u32; - let pio_size_hi: u32 = (pcie_pio_size >> 32) as u32; - let pio_size_lo: u32 = (pcie_pio_size & 0xffff_ffff) as u32; - - fdt.set_property_array_u32( - "ranges", - &[ - fdt_pci_pio_type, - 0, - 0, - pio_base_hi, - pio_base_lo, - pio_size_hi, - pio_size_lo, - fdt_pci_mmio_type, - mmio_base_hi, - mmio_base_lo, - mmio_base_hi, - mmio_base_lo, - mmio_size_hi, - mmio_size_lo, - fdt_pci_mmio_type_64bit, - high_mmio_base_hi, - high_mmio_base_lo, - high_mmio_base_hi, - high_mmio_base_lo, - high_mmio_size_hi, - high_mmio_size_lo, - ], - )?; - - fdt.set_property_u32("msi-parent", device_tree::GIC_ITS_PHANDLE)?; - fdt.end_node(pci_node_dep)?; - Ok(()) -} - -// Function that helps to generate Virtio-Mmio device's node in device-tree. -// -// # Arguments -// -// * `dev_info` - Device resource info of Virtio-Mmio device. -// * `fdt` - Flatted device-tree blob where node will be filled into. -fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("virtio_mmio@{:x}", res.region_base); - let virtio_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "virtio,mmio")?; - fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_SPI, - res.irq as u32, - device_tree::IRQ_TYPE_EDGE_RISING, - ], - )?; - fdt.end_node(virtio_node_dep)?; - Ok(()) -} - /// Function that helps to generate flash node in device-tree. /// -/// # Arguments -/// -/// * `dev_info` - Device resource info of fw-cfg device. -/// * `flash` - Flatted device-tree blob where fw-cfg node will be filled into. -fn generate_flash_device_node(fdt: &mut FdtBuilder) -> util::Result<()> { - let flash_base = MEM_LAYOUT[LayoutEntryType::Flash as usize].0; - let flash_size = MEM_LAYOUT[LayoutEntryType::Flash as usize].1 / 2; - let node = format!("flash@{:x}", flash_base); - let flash_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "cfi-flash")?; - fdt.set_property_array_u64( - "reg", - &[flash_base, flash_size, flash_base + flash_size, flash_size], - )?; - fdt.set_property_u32("bank-width", 4)?; - fdt.end_node(flash_node_dep)?; - Ok(()) -} - -/// Function that helps to generate fw-cfg node in device-tree. -/// -/// # Arguments -/// -/// * `dev_info` - Device resource info of fw-cfg device. -/// * `fdt` - Flatted device-tree blob where fw-cfg node will be filled into. -fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("fw-cfg@{:x}", res.region_base); - let fwcfg_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "qemu,fw-cfg-mmio")?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.end_node(fwcfg_node_dep)?; - - Ok(()) -} - -// Function that helps to generate serial node in device-tree. -// -// # Arguments -// -// * `dev_info` - Device resource info of serial device. -// * `fdt` - Flatted device-tree blob where serial node will be filled into. -fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("pl011@{:x}", res.region_base); - let serial_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "arm,pl011\0arm,primecell")?; - fdt.set_property_string("clock-names", "uartclk\0apb_pclk")?; - fdt.set_property_array_u32( - "clocks", - &[device_tree::CLK_PHANDLE, device_tree::CLK_PHANDLE], - )?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_SPI, - res.irq as u32, - device_tree::IRQ_TYPE_EDGE_RISING, - ], - )?; - fdt.end_node(serial_node_dep)?; - - Ok(()) -} - -// Function that helps to generate RTC node in device-tree. -// -// # Arguments -// -// * `dev_info` - Device resource info of RTC device. -// * `fdt` - Flatted device-tree blob where RTC node will be filled into. -fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { - let node = format!("pl031@{:x}", res.region_base); - let rtc_node_dep = fdt.begin_node(&node)?; - fdt.set_property_string("compatible", "arm,pl031\0arm,primecell\0")?; - fdt.set_property_string("clock-names", "apb_pclk")?; - fdt.set_property_u32("clocks", device_tree::CLK_PHANDLE)?; - fdt.set_property_array_u64("reg", &[res.region_base, res.region_size])?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_SPI, - res.irq as u32, - device_tree::IRQ_TYPE_LEVEL_HIGH, - ], - )?; - fdt.end_node(rtc_node_dep)?; - - Ok(()) -} - -fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { - let node = "pmu"; - let pmu_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "arm,armv8-pmuv3")?; - fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; - fdt.set_property_array_u32( - "interrupts", - &[ - device_tree::GIC_FDT_IRQ_TYPE_PPI, - PMU_INTR, - device_tree::IRQ_TYPE_LEVEL_HIGH, - ], - )?; - fdt.end_node(pmu_node_dep)?; - - Ok(()) -} /// Trait that helps to generate all nodes in device-tree. #[allow(clippy::upper_case_acronyms)] trait CompileFDTHelper { - /// Function that helps to generate cpu nodes. - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate memory nodes. fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; - /// Function that helps to generate Virtio-mmio devices' nodes. - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + /// Function that helps to generate pci node in device-tree. + fn generate_pci_host_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; /// Function that helps to generate the chosen node. fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; - /// Function that helps to generate numa node distances. - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; } impl CompileFDTHelper for StdMachine { - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - let node = "cpus"; - - let cpus_node_dep = fdt.begin_node(node)?; - fdt.set_property_u32("#address-cells", 0x02)?; - fdt.set_property_u32("#size-cells", 0x0)?; - - // Generate CPU topology - let cpu_map_node_dep = fdt.begin_node("cpu-map")?; - for socket in 0..self.base.cpu_topo.sockets { - let sock_name = format!("cluster{}", socket); - let sock_node_dep = fdt.begin_node(&sock_name)?; - for cluster in 0..self.base.cpu_topo.clusters { - let clster = format!("cluster{}", cluster); - let cluster_node_dep = fdt.begin_node(&clster)?; - - for core in 0..self.base.cpu_topo.cores { - let core_name = format!("core{}", core); - let core_node_dep = fdt.begin_node(&core_name)?; - - for thread in 0..self.base.cpu_topo.threads { - let thread_name = format!("thread{}", thread); - let thread_node_dep = fdt.begin_node(&thread_name)?; - let vcpuid = - self.base.cpu_topo.threads * self.base.cpu_topo.cores * cluster - + self.base.cpu_topo.threads * core - + thread; - fdt.set_property_u32( - "cpu", - u32::from(vcpuid) + device_tree::CPU_PHANDLE_START, - )?; - fdt.end_node(thread_node_dep)?; - } - fdt.end_node(core_node_dep)?; - } - fdt.end_node(cluster_node_dep)?; - } - fdt.end_node(sock_node_dep)?; - } - fdt.end_node(cpu_map_node_dep)?; - - for cpu_index in 0..self.base.cpu_topo.nrcpus { - let mpidr = self.base.cpus[cpu_index as usize] - .arch() - .lock() - .unwrap() - .mpidr(); - - let node = format!("cpu@{:x}", mpidr); - let mpidr_node_dep = fdt.begin_node(&node)?; - fdt.set_property_u32( - "phandle", - u32::from(cpu_index) + device_tree::CPU_PHANDLE_START, - )?; - fdt.set_property_string("device_type", "cpu")?; - fdt.set_property_string("compatible", "arm,arm-v8")?; - if self.base.cpu_topo.max_cpus > 1 { - fdt.set_property_string("enable-method", "psci")?; - } - fdt.set_property_u64("reg", mpidr & 0x007F_FFFF)?; - fdt.set_property_u32("phandle", device_tree::FIRST_VCPU_PHANDLE)?; - - if let Some(numa_nodes) = &self.base.numa_nodes { - for numa_index in 0..numa_nodes.len() { - let numa_node = numa_nodes.get(&(numa_index as u32)); - if numa_node.unwrap().cpus.contains(&(cpu_index)) { - fdt.set_property_u32("numa-node-id", numa_index as u32)?; - } - } - } - - fdt.end_node(mpidr_node_dep)?; - } - - fdt.end_node(cpus_node_dep)?; - - if self.base.cpu_features.pmu { - generate_pmu_node(fdt)?; - } + fn generate_pci_host_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + let pcie_ecam_base = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; + let pcie_ecam_size = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1; + let pcie_buses_num = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1 >> 20; + let node = format!("pcie@{:x}", pcie_ecam_base); + let pci_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("compatible", "pci-host-ecam-generic")?; + fdt.set_property_string("device_type", "pci")?; + fdt.set_property_array_u64("reg", &[pcie_ecam_base, pcie_ecam_size])?; + fdt.set_property_array_u32("bus-range", &[0, (pcie_buses_num - 1) as u32])?; + fdt.set_property_u32("linux,pci-domain", 0)?; + fdt.set_property_u32("#address-cells", 3)?; + fdt.set_property_u32("#size-cells", 2)?; + + let high_pcie_mmio_base = MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize].0; + let high_pcie_mmio_size = MEM_LAYOUT[LayoutEntryType::HighPcieMmio as usize].1; + let fdt_pci_mmio_type_64bit: u32 = device_tree::FDT_PCI_RANGE_MMIO_64BIT; + let high_mmio_base_hi: u32 = (high_pcie_mmio_base >> 32) as u32; + let high_mmio_base_lo: u32 = (high_pcie_mmio_base & 0xffff_ffff) as u32; + let high_mmio_size_hi: u32 = (high_pcie_mmio_size >> 32) as u32; + let high_mmio_size_lo: u32 = (high_pcie_mmio_size & 0xffff_ffff) as u32; + + let pcie_mmio_base = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; + let pcie_mmio_size = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; + let fdt_pci_mmio_type: u32 = device_tree::FDT_PCI_RANGE_MMIO; + let mmio_base_hi: u32 = (pcie_mmio_base >> 32) as u32; + let mmio_base_lo: u32 = (pcie_mmio_base & 0xffff_ffff) as u32; + let mmio_size_hi: u32 = (pcie_mmio_size >> 32) as u32; + let mmio_size_lo: u32 = (pcie_mmio_size & 0xffff_ffff) as u32; + + let pcie_pio_base = MEM_LAYOUT[LayoutEntryType::PciePio as usize].0; + let pcie_pio_size = MEM_LAYOUT[LayoutEntryType::PciePio as usize].1; + let fdt_pci_pio_type: u32 = device_tree::FDT_PCI_RANGE_IOPORT; + let pio_base_hi: u32 = (pcie_pio_base >> 32) as u32; + let pio_base_lo: u32 = (pcie_pio_base & 0xffff_ffff) as u32; + let pio_size_hi: u32 = (pcie_pio_size >> 32) as u32; + let pio_size_lo: u32 = (pcie_pio_size & 0xffff_ffff) as u32; + + fdt.set_property_array_u32( + "ranges", + &[ + fdt_pci_pio_type, + 0, + 0, + pio_base_hi, + pio_base_lo, + pio_size_hi, + pio_size_lo, + fdt_pci_mmio_type, + mmio_base_hi, + mmio_base_lo, + mmio_base_hi, + mmio_base_lo, + mmio_size_hi, + mmio_size_lo, + fdt_pci_mmio_type_64bit, + high_mmio_base_hi, + high_mmio_base_lo, + high_mmio_base_hi, + high_mmio_base_lo, + high_mmio_size_hi, + high_mmio_size_lo, + ], + )?; - Ok(()) + fdt.set_property_u32("msi-parent", device_tree::GIC_ITS_PHANDLE)?; + fdt.end_node(pci_node_dep) } fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { @@ -1569,67 +1345,6 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - // timer - let mut cells: Vec = Vec::new(); - for &irq in [13, 14, 11, 10].iter() { - cells.push(device_tree::GIC_FDT_IRQ_TYPE_PPI); - cells.push(irq); - cells.push(device_tree::IRQ_TYPE_LEVEL_HIGH); - } - let node = "timer"; - let timer_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "arm,armv8-timer")?; - fdt.set_property("always-on", &Vec::new())?; - fdt.set_property_array_u32("interrupts", &cells)?; - fdt.end_node(timer_node_dep)?; - - // clock - let node = "apb-pclk"; - let clock_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "fixed-clock")?; - fdt.set_property_string("clock-output-names", "clk24mhz")?; - fdt.set_property_u32("#clock-cells", 0x0)?; - fdt.set_property_u32("clock-frequency", 24_000_000)?; - fdt.set_property_u32("phandle", device_tree::CLK_PHANDLE)?; - fdt.end_node(clock_node_dep)?; - - // psci - let node = "psci"; - let psci_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("compatible", "arm,psci-0.2")?; - fdt.set_property_string("method", "hvc")?; - fdt.end_node(psci_node_dep)?; - - for dev in self.base.sysbus.devices.iter() { - let locked_dev = dev.lock().unwrap(); - match locked_dev.sysbusdev_base().dev_type { - SysBusDevType::PL011 => { - // SAFETY: Legacy devices guarantee is not empty. - generate_serial_device_node(fdt, &locked_dev.sysbusdev_base().res)? - } - SysBusDevType::Rtc => { - // SAFETY: Legacy devices guarantee is not empty. - generate_rtc_device_node(fdt, &locked_dev.sysbusdev_base().res)? - } - SysBusDevType::VirtioMmio => { - // SAFETY: Legacy devices guarantee is not empty. - generate_virtio_devices_node(fdt, &locked_dev.sysbusdev_base().res)? - } - SysBusDevType::FwCfg => { - // SAFETY: Legacy devices guarantee is not empty. - generate_fwcfg_device_node(fdt, &locked_dev.sysbusdev_base().res)?; - } - _ => (), - } - } - generate_flash_device_node(fdt)?; - - generate_pci_host_node(fdt)?; - - Ok(()) - } - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node = "chosen"; @@ -1650,66 +1365,17 @@ impl CompileFDTHelper for StdMachine { } None => {} } - fdt.end_node(chosen_node_dep)?; - - Ok(()) - } - - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - if self.base.numa_nodes.is_none() { - return Ok(()); - } - - let distance_node_dep = fdt.begin_node("distance-map")?; - fdt.set_property_string("compatible", "numa-distance-map-v1")?; - - let mut matrix = Vec::new(); - let numa_nodes = self.base.numa_nodes.as_ref().unwrap(); - let existing_nodes: Vec = numa_nodes.keys().cloned().collect(); - for (id, node) in numa_nodes.iter().enumerate() { - let distances = &node.1.distances; - for i in existing_nodes.iter() { - matrix.push(id as u32); - matrix.push(*i); - let dist: u32 = if id as u32 == *i { - 10 - } else if let Some(distance) = distances.get(i) { - *distance as u32 - } else { - 20 - }; - matrix.push(dist); - } - } - - fdt.set_property_array_u32("distance-matrix", matrix.as_ref())?; - fdt.end_node(distance_node_dep)?; - - Ok(()) + fdt.end_node(chosen_node_dep) } } impl device_tree::CompileFDT for StdMachine { fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { let node_dep = fdt.begin_node("")?; - - fdt.set_property_string("compatible", "linux,dummy-virt")?; - fdt.set_property_u32("#address-cells", 0x2)?; - fdt.set_property_u32("#size-cells", 0x2)?; - fdt.set_property_u32("interrupt-parent", device_tree::GIC_PHANDLE)?; - - self.generate_cpu_nodes(fdt)?; + self.base.generate_fdt_node(fdt)?; self.generate_memory_node(fdt)?; - self.generate_devices_node(fdt)?; self.generate_chosen_node(fdt)?; - self.base - .irq_chip - .as_ref() - .unwrap() - .generate_fdt_node(fdt)?; - self.generate_distance_node(fdt)?; - fdt.end_node(node_dep)?; - - Ok(()) + self.generate_pci_host_node(fdt)?; + fdt.end_node(node_dep) } } -- Gitee From f20d894c19efa81afbfb08cdba64cdb79ce77b40 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Tue, 31 Oct 2023 10:25:37 +0800 Subject: [PATCH 1423/2187] smbios: Directory reconfiguration The SMBIOS module is moved to the device directory Signed-off-by: jiewangqun --- Cargo.lock | 12 ------------ devices/src/lib.rs | 1 + smbios/src/lib.rs => devices/src/smbios/mod.rs | 0 {smbios/src => devices/src/smbios}/smbios_table.rs | 0 machine/Cargo.toml | 1 - machine/src/lib.rs | 4 ++-- smbios/Cargo.toml | 14 -------------- tests/mod_test/Cargo.toml | 1 - 8 files changed, 3 insertions(+), 30 deletions(-) rename smbios/src/lib.rs => devices/src/smbios/mod.rs (100%) rename {smbios/src => devices/src/smbios}/smbios_table.rs (100%) delete mode 100644 smbios/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index ec6c5cea8..d2164deee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,7 +920,6 @@ dependencies = [ "migration", "migration_derive", "serde_json", - "smbios", "thiserror", "ui", "util", @@ -1030,7 +1029,6 @@ dependencies = [ "rand", "serde", "serde_json", - "smbios", "util", "virtio", "vmm-sys-util", @@ -1493,16 +1491,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" -[[package]] -name = "smbios" -version = "2.3.0" -dependencies = [ - "anyhow", - "byteorder", - "machine_manager", - "util", -] - [[package]] name = "spin" version = "0.5.2" diff --git a/devices/src/lib.rs b/devices/src/lib.rs index c7ea949e8..0e5ec2091 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -23,6 +23,7 @@ pub mod legacy; pub mod misc; pub mod pci; pub mod scsi; +pub mod smbios; pub mod sysbus; pub mod usb; diff --git a/smbios/src/lib.rs b/devices/src/smbios/mod.rs similarity index 100% rename from smbios/src/lib.rs rename to devices/src/smbios/mod.rs diff --git a/smbios/src/smbios_table.rs b/devices/src/smbios/smbios_table.rs similarity index 100% rename from smbios/src/smbios_table.rs rename to devices/src/smbios/smbios_table.rs diff --git a/machine/Cargo.toml b/machine/Cargo.toml index cc7346b3e..c1770f727 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -15,7 +15,6 @@ vmm-sys-util = "0.11.1" thiserror = "1.0" anyhow = "1.0" acpi = { path = "../acpi" } -smbios = { path = "../smbios" } address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } cpu = { path = "../cpu" } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8b13f9c5d..3d9ec164c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -52,6 +52,8 @@ use devices::misc::scream::Scream; #[cfg(feature = "demo_device")] use devices::pci::demo_device::DemoDev; use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; +use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; +use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::UsbCamera; @@ -89,8 +91,6 @@ use machine_manager::config::{ use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; -use smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; -use smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use standard_vm::Result as StdResult; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; diff --git a/smbios/Cargo.toml b/smbios/Cargo.toml deleted file mode 100644 index c9bf0e728..000000000 --- a/smbios/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "smbios" -version = "2.3.0" -authors = ["Huawei StratoVirt Team"] -edition = "2021" -license = "Mulan PSL v2" -description = "smbios module" - -[dependencies] -byteorder = "1.4.3" -anyhow = "1.0" - -util = {path = "../util"} -machine_manager = { path = "../machine_manager" } diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index b6847e73d..406988b6f 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -17,6 +17,5 @@ serde = { version = "1.0", features = ["derive"] } devices = { path = "../../devices", features = ["scream"]} util = { path = "../../util" } acpi = { path = "../../acpi" } -smbios = { path = "../../smbios" } machine = { path = "../../machine" } virtio = { path = "../../virtio", features = ["virtio_gpu"] } -- Gitee From 342ded81498a88c8466115746a01e30681757fee Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 31 Oct 2023 20:55:27 +0800 Subject: [PATCH 1424/2187] fix clippy warning for dbg2 Signed-off-by: yezengruan --- machine/src/standard_vm/aarch64/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 42587f429..078aedecc 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -831,7 +831,7 @@ impl AcpiBuilder for StdMachine { // BaseAddressRegister: access width dbg2.set_field(offset + 25, 1_u8); // BaseAddressRegister: address - dbg2.set_field(offset + 26, uart_memory_address as u64); + dbg2.set_field(offset + 26, uart_memory_address); // AddressSize dbg2.set_field(offset + 34, uart_memory_size as u32); -- Gitee From a8daccf26460920e1fff0be1e3efe41bd6cae169 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 1 Nov 2023 08:57:44 +0800 Subject: [PATCH 1425/2187] add feature keycode Fix warning of keycode when compiling without features gtk or vnc: warning: multiple variants are never constructed Signed-off-by: yezengruan --- ui/Cargo.toml | 5 +++-- ui/src/input.rs | 2 ++ ui/src/lib.rs | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 3344a75f3..0a76ec2d3 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -25,7 +25,8 @@ machine_manager = { path = "../machine_manager" } util = { path = "../util" } [features] +keycode = [] pixman = ["util/pixman"] console = ["pixman"] -gtk = ["console", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] -vnc = ["console", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] +gtk = ["console", "keycode", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] +vnc = ["console", "keycode", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] diff --git a/ui/src/input.rs b/ui/src/input.rs index 23f430b37..b72cb7642 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -403,6 +403,7 @@ pub trait PointerOpts: Send { #[cfg(test)] mod tests { + #[cfg(feature = "keycode")] use crate::keycode::KeyCode; static TEST_INPUT: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(TestInput::default()))); @@ -500,6 +501,7 @@ mod tests { test_input.unregister_input(); } + #[cfg(feature = "keycode")] #[test] fn test_release_all_key() { fn do_key_event(press: bool, keysym: i32, keycode: u16) -> Result<()> { diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 0ae9f7d03..a81b84e2c 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -16,6 +16,7 @@ pub mod error; #[cfg(feature = "gtk")] pub mod gtk; pub mod input; +#[cfg(feature = "keycode")] mod keycode; #[cfg(feature = "pixman")] pub mod pixman; -- Gitee From 1296e607568651168e30ce49d8758670e495548a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 4 Nov 2023 17:24:18 +0800 Subject: [PATCH 1426/2187] virtio/serial: Fix host_connected is not changed when ctrl_handler is none The host_connected should not affect by ctrl_handler, fix that. Fixes: 7c149c112 virtio-serial: check control handler when chardev notifies device Signed-off-by: Keqian Zhu --- virtio/src/device/serial.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 2a0229eaf..5cce07dbc 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -884,17 +884,17 @@ impl EventNotifierHelper for SerialControlHandler { impl ChardevNotifyDevice for SerialPort { fn chardev_notify(&mut self, status: ChardevStatus) { - if self.ctrl_handler.is_none() { - warn!("No control handler for port {}.", self.nr); - return; - } - match (&status, self.host_connected) { (ChardevStatus::Close, _) => self.host_connected = false, (ChardevStatus::Open, false) => self.host_connected = true, (ChardevStatus::Open, true) => return, } + if self.ctrl_handler.is_none() { + warn!("No control handler for port {}.", self.nr); + return; + } + let handler = self.ctrl_handler.as_ref().unwrap().upgrade(); if handler.is_none() { warn!("Control handler for port {} is invalid", self.nr); -- Gitee From 12f3b2414caa5fe58a47e677f1fe96a322ddbd6e Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 5 Nov 2023 22:50:09 +0800 Subject: [PATCH 1427/2187] Markdown syntax corrections in config_guidebook.md Signed-off-by: Yan Wen --- docs/config_guidebook.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index de36681d4..70d355e5d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -107,7 +107,7 @@ The path has to be absolute path. -mem-path ``` -### 1.4.1 hugepages +#### 1.4.1 hugepages Memory backend file can be used to let guest use hugetlbfs on host. It supports 2M or 1G hugepages memory. The following steps show how to use hugepages: @@ -425,14 +425,19 @@ Virtio-net is a virtual Ethernet card in VM. It can enable the network capabilit Six properties are supported for netdev. * tap/vhost-user: the type of net device. NB: currently only tap and vhost-user is supported. + * id: unique netdev id. + * ifname: name of tap device in host. + * fd: the file descriptor of opened tap device. + * fds: file descriptors of opened tap device. + * queues: the optional queues attribute controls the number of queues to be used for either multiple queue virtio-net or - vhost-net device. The max queues number supported is no more than 16. -NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, -the tap device would be created according to `ifname`. + vhost-net device. The max queues number supported is no more than 16. + +NB: to configure a tap device, use either `fd` or `ifname`, if both of them are given, the tap device would be created according to `ifname`. Eight properties are supported for virtio-net-device or virtio-net-pci. * id: unique net device id. -- Gitee From f6371ec5d215ac94ea44daf2ccbd1808fb4dcb45 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 9 Nov 2023 09:13:01 +0800 Subject: [PATCH 1428/2187] acpi: Reduce unsafe code Reduce unsafe code and add SAFETY comments for unsafe code. Signed-off-by: Keqian Zhu --- acpi/src/table_loader.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 257552418..0a58c6de6 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -381,20 +381,16 @@ mod test { .is_ok()); let file_bytes = file_name.as_bytes(); + // SATETY: The "alloc" field of union consists of u8 members, so the access is safe. + let alloc = unsafe { &table_loader.cmds.get(0).unwrap().entry.alloc }; assert_eq!( - unsafe { - table_loader.cmds.get(0).unwrap().entry.alloc.file[0..file_bytes.len()].to_vec() - }, + alloc.file[0..file_bytes.len()].to_vec(), file_bytes.to_vec() ); - assert_eq!( - unsafe { table_loader.cmds.get(0).unwrap().entry.alloc.align }, - 4_u32 - ); - assert_eq!( - unsafe { table_loader.cmds.get(0).unwrap().entry.alloc.zone }, - 0x1 - ); + // Copy to local var to avoid unaligned_references error. + let align = alloc.align; + assert_eq!(align, 4_u32); + assert_eq!(alloc.zone, 0x1); assert!(table_loader .add_alloc_entry("etc/table-loader", file_blob, 4_u32, false) -- Gitee From 14f19021524712e524d3922d3dc5dc17d1e07184 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 9 Nov 2023 09:17:56 +0800 Subject: [PATCH 1429/2187] address_space: Reduce some unsafe code Signed-off-by: Keqian Zhu --- Cargo.lock | 1 + address_space/Cargo.toml | 1 + address_space/src/host_mmap.rs | 51 +++++++++++++++++----------------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2164deee..f020351dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,7 @@ dependencies = [ "machine_manager", "migration", "migration_derive", + "nix 0.26.2", "thiserror", "util", "vmm-sys-util", diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 190415714..83e964ff5 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -13,6 +13,7 @@ kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } kvm-ioctls = "0.13.0" vmm-sys-util = "0.11.1" arc-swap = "1.6.0" +nix = "0.26.2" thiserror = "1.0" anyhow = "1.0" hypervisor = { path = "../hypervisor" } diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 917c398dd..e7dfafaa9 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -11,13 +11,16 @@ // See the Mulan PSL v2 for more details. use std::cmp::min; -use std::fs::File; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::fs::{remove_file, File}; +use std::os::unix::io::{FromRawFd, RawFd}; use std::sync::Arc; use std::thread; use anyhow::{bail, Context, Result}; use log::{error, info}; +use nix::sys::statfs::fstatfs; +use nix::unistd::sysconf; +use nix::unistd::SysconfVar; use crate::{AddressRange, GuestAddress, Region}; use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; @@ -44,18 +47,8 @@ pub struct FileBackend { } fn file_unlink(file_path: &str) { - let fs_cstr = std::ffi::CString::new(file_path).unwrap().into_raw(); - - if unsafe { libc::unlink(fs_cstr) } != 0 { - error!( - "Failed to unlink file \"{}\", error: {:?}", - file_path, - std::io::Error::last_os_error() - ); - } - // SAFETY: fs_cstr obtained by calling CString::into_raw. - unsafe { - drop(std::ffi::CString::from_raw(fs_cstr)); + if let Err(e) = remove_file(file_path) { + error!("Failed to unlink file \"{}\", error: {:?}", file_path, e); } } @@ -130,13 +123,10 @@ impl FileBackend { .with_context(|| format!("Failed to open file: {}", file_path))? }; - // Safe because struct `statfs` only contains plain-data-type field, - // and set to all-zero will not cause any undefined behavior. - let mut fstat: libc::statfs = unsafe { std::mem::zeroed() }; - unsafe { libc::fstatfs(file.as_raw_fd(), &mut fstat) }; + let fstat = fstatfs(&file).with_context(|| "Failed to fstatfs file")?; info!( "Using memory backing file, the page size is {}", - fstat.f_bsize + fstat.optimal_transfer_size() ); let old_file_len = file.metadata().unwrap().len(); @@ -158,7 +148,7 @@ impl FileBackend { Ok(FileBackend { file: Arc::new(file), offset: 0_u64, - page_size: fstat.f_bsize as u64, + page_size: fstat.optimal_transfer_size() as u64, }) } } @@ -169,12 +159,23 @@ impl FileBackend { /// /// * `nr_vcpus` - Number of vcpus. fn max_nr_threads(nr_vcpus: u8) -> u8 { - let nr_host_cpu = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) }; - if nr_host_cpu > 0 { - return min(min(nr_host_cpu as u8, MAX_PREALLOC_THREAD), nr_vcpus); - } + let conf = sysconf(SysconfVar::_NPROCESSORS_ONLN); + // If fails to call `sysconf` function, just use a single thread to touch pages. - 1 + if conf.is_err() || conf.unwrap().is_none() { + log::warn!("Failed to get sysconf of _NPROCESSORS_ONLN"); + return 1; + } + let nr_host_cpu = conf.unwrap().unwrap(); + if nr_host_cpu <= 0 { + log::warn!( + "The sysconf of _NPROCESSORS_ONLN: {} is ignored", + nr_host_cpu + ); + return 1; + } + + min(min(nr_host_cpu as u8, MAX_PREALLOC_THREAD), nr_vcpus) } /// Touch pages to pre-alloc memory for VM. -- Gitee From d7e406ca9c0a7fe0c91d046afc35e4ba108bed06 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 8 Nov 2023 15:04:30 +0800 Subject: [PATCH 1430/2187] vhost-user: fixup the crash issue while using multiple shared memory subregions There might be two crash case: 1. while delete a subregion without file backend 2. multiple subregions are unordered This patch changes add and delete handlers: 1. insert the new region in ordering 2. ignore the subregion without file backend Signed-off-by: Zhao Yi Min --- virtio/src/vhost/user/client.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 77c2d76b5..521cca78b 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::cmp::Ordering; use std::fs::File; use std::mem::size_of; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -249,7 +250,23 @@ impl VhostUserMemInfo { mmap_offset: file_back.offset + fr.offset_in_region, }; let region_info = RegionInfo { region, file_back }; - self.regions.lock().unwrap().push(region_info); + let mut locked_regions = self.regions.lock().unwrap(); + match locked_regions.binary_search_by(|r| { + if (r.region.guest_phys_addr + r.region.memory_size - 1) < guest_phys_addr { + Ordering::Less + } else if r.region.guest_phys_addr > (guest_phys_addr + memory_size - 1) { + Ordering::Greater + } else { + Ordering::Equal + } + }) { + Ok(p) => bail!( + "New region {:?} is overlapped with region {:?}", + region_info.region, + locked_regions[p].region + ), + Err(p) => locked_regions.insert(p, region_info), + } Ok(()) } @@ -259,10 +276,13 @@ impl VhostUserMemInfo { return Ok(()); } - let file_back = fr - .owner - .get_file_backend() - .with_context(|| "Failed to get file backend")?; + let file_back = match fr.owner.get_file_backend() { + None => { + info!("fr {:?} backend is not file, ignored", fr); + return Ok(()); + } + Some(fb) => fb, + }; let mut mem_regions = self.regions.lock().unwrap(); let host_address = match fr.owner.get_host_address() { Some(addr) => addr, -- Gitee From 94145c200cc047251c0e6402b1ac1cb2e7e13ba5 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 9 Nov 2023 17:30:49 +0800 Subject: [PATCH 1431/2187] Scream: check whether the recording delay exceeds 500ms During the host headset switchover, io.read is blocked for a long time. As a result, the VM recording delay exceeds 1s. Thereforce, check whether the delay exceeds 500ms. If the delay exceeds 500ms, start recording again. Signed-off-by: Mingwang Li --- devices/src/misc/scream/alsa.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index 74c4a9add..3ce2e0f78 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -251,6 +251,18 @@ impl AudioInterface for AlsaStreamData { } Ok(n) => { frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); + + // During the host headset switchover, io.read is blocked for a long time. + // As a result, the VM recording delay exceeds 1s. Thereforce, check whether + // the delay exceeds 500ms. If the delay exceeds 500ms, start recording again. + let delay = self.pcm.as_ref().unwrap().delay().unwrap_or_else(|e| { + warn!("Scream alsa can't get frames delay: {e:?}"); + 0 + }); + if delay > self.rate as i64 >> 1 { + warn!("Scream alsa read audio blocked too long, delay {delay} frames, init again!"); + self.init = false; + } } } } -- Gitee From e4bd1f0096da0a89404493e7a752c35eb5b8cc23 Mon Sep 17 00:00:00 2001 From: jiewang-group Date: Wed, 8 Nov 2023 09:41:41 +0800 Subject: [PATCH 1432/2187] memory: The migration function of memory snapshots is fixed Fixed saving and restoring snapshots after reconfiguring memory ... Signed-off-by: jiewangqun --- address_space/src/address_space.rs | 30 ++++-- address_space/src/lib.rs | 1 + address_space/src/state.rs | 111 ++++++++++++++-------- boot_loader/src/lib.rs | 2 + boot_loader/src/x86_64/bootparam.rs | 2 +- boot_loader/src/x86_64/direct_boot/mod.rs | 2 +- devices/src/legacy/fwcfg.rs | 11 ++- devices/src/legacy/pflash.rs | 9 +- devices/src/pci/bus.rs | 9 +- devices/src/pci/config.rs | 9 +- devices/src/pci/host.rs | 9 +- devices/src/pci/mod.rs | 1 + machine/src/lib.rs | 18 ++-- vhost_user_fs/src/virtio_fs.rs | 1 + virtio/src/device/balloon.rs | 2 +- virtio/src/device/block.rs | 2 +- virtio/src/device/rng.rs | 2 +- virtio/src/queue/split.rs | 2 +- virtio/src/transport/virtio_mmio.rs | 2 +- virtio/src/transport/virtio_pci.rs | 7 ++ virtio/src/vhost/kernel/net.rs | 2 +- virtio/src/vhost/kernel/vsock.rs | 2 +- 22 files changed, 162 insertions(+), 74 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index bece34c0f..e6963e930 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -132,6 +132,8 @@ pub struct AddressSpace { listeners: Arc>>, /// The current layout of ioeventfds, which is compared with new ones in topology-update stage. ioeventfds: Arc>>, + /// The backend memory region tree, used for migrate. + machine_ram: Option>, } impl fmt::Debug for AddressSpace { @@ -151,13 +153,18 @@ impl AddressSpace { /// /// * `root` - Root region of address space. /// * `name` - the name of AddressSpace. - pub fn new(root: Region, name: &str) -> Result> { + pub fn new( + root: Region, + name: &str, + machine_ram: Option>, + ) -> Result> { let space = Arc::new(AddressSpace { name: String::from(name), root: root.clone(), flat_view: Arc::new(ArcSwap::new(Arc::new(FlatView::default()))), listeners: Arc::new(Mutex::new(Vec::new())), ioeventfds: Arc::new(Mutex::new(Vec::new())), + machine_ram, }); root.set_belonged_address_space(&space); @@ -170,6 +177,13 @@ impl AddressSpace { Ok(space) } + pub fn get_machine_ram(&self) -> Option<&Arc> { + if let Some(region) = &self.machine_ram { + return Some(region); + } + None + } + /// Get the reference of root region of AddressSpace. pub fn root(&self) -> &Region { &self.root @@ -875,7 +889,7 @@ mod test { } let root = Region::init_container_region(8000, "root"); - let space = AddressSpace::new(root, "space").unwrap(); + let space = AddressSpace::new(root, "space", None).unwrap(); let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener3 = Arc::new(Mutex::new(ListenerPrior3::default())); @@ -926,7 +940,7 @@ mod test { } let root = Region::init_container_region(8000, "root"); - let space = AddressSpace::new(root, "space").unwrap(); + let space = AddressSpace::new(root, "space", None).unwrap(); let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); space.register_listener(listener1.clone()).unwrap(); @@ -942,7 +956,7 @@ mod test { #[test] fn test_update_topology() { let root = Region::init_container_region(8000, "root"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let listener = Arc::new(Mutex::new(TestListener::default())); space.register_listener(listener.clone()).unwrap(); @@ -1040,7 +1054,7 @@ mod test { // the flat_view is as follows, // [BBBBBBBBBBBBB][CCCCC] let root = Region::init_container_region(8000, "region"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let listener = Arc::new(Mutex::new(TestListener::default())); space.register_listener(listener.clone()).unwrap(); @@ -1102,7 +1116,7 @@ mod test { // the flat_view is as follows, // [CCCCCC] let root = Region::init_container_region(8000, "root"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let listener = Arc::new(Mutex::new(TestListener::default())); space.register_listener(listener.clone()).unwrap(); @@ -1124,7 +1138,7 @@ mod test { #[test] fn test_get_ram_info() { let root = Region::init_container_region(8000, "root"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let default_ops = RegionOps { read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }), write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }), @@ -1197,7 +1211,7 @@ mod test { #[test] fn test_write_and_read_object() { let root = Region::init_container_region(8000, "root"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let ram1 = Arc::new( HostMemMapping::new(GuestAddress(0), None, 1000, None, false, false, false).unwrap(), ); diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index fc63713c7..3507e769d 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -39,6 +39,7 @@ //! let space = AddressSpace::new( //! Region::init_container_region(u64::max_value(), "space"), //! "space", +//! None, //! ) //! .unwrap(); //! diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 1073053c3..6a6d81ff7 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -15,7 +15,7 @@ use std::io::{Read, Write}; use std::mem::size_of; use std::sync::Arc; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; use migration::{ @@ -31,13 +31,15 @@ const MIGRATION_HEADER_LENGTH: usize = 4096; #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] pub struct AddressSpaceState { + nr_alias_region: u64, + ram_alias_state: [RamRegionState; 16], nr_ram_region: u64, ram_region_state: [RamRegionState; 16], } #[derive(Copy, Clone, ByteCode)] pub struct RamRegionState { - base_address: u64, + alias_offset: u64, size: u64, // The offset of this memory region in file backend file. offset: u64, @@ -59,14 +61,28 @@ impl StateTransfer for AddressSpace { let mut state = AddressSpaceState::default(); let mut offset = memory_offset() as u64; + if self.get_machine_ram().is_none() { + bail!("This address space does not support migration."); + } + + let machine_ram = self.get_machine_ram().unwrap(); + for region in machine_ram.subregions().iter() { + state.ram_alias_state[state.nr_alias_region as usize] = RamRegionState { + alias_offset: 0, + size: region.size(), + offset, + }; + offset += region.size(); + state.nr_alias_region += 1; + } + for region in self.root().subregions().iter() { - if let Some(start_addr) = region.start_addr() { + if region.alias_name().is_some() { state.ram_region_state[state.nr_ram_region as usize] = RamRegionState { - base_address: start_addr.0, + alias_offset: region.alias_offset(), size: region.size(), - offset, + offset: region.offset().0, }; - offset += region.size(); state.nr_ram_region += 1; } } @@ -86,15 +102,15 @@ impl MigrationHook for AddressSpace { let padding_buffer = [0].repeat(memory_offset() - MIGRATION_HEADER_LENGTH - size_of::()); fd.write_all(&padding_buffer)?; - - for region in self.root().subregions().iter() { - if let Some(base_addr) = region.start_addr() { - region - .read(fd, base_addr, 0, region.size()) - .map_err(|e| MigrationError::SaveVmMemoryErr(e.to_string()))?; + if let Some(machine_ram) = self.get_machine_ram() { + for region in machine_ram.subregions().iter() { + if let Some(base_addr) = region.start_addr() { + region + .read(fd, base_addr, 0, region.size()) + .map_err(|e| MigrationError::SaveVmMemoryErr(e.to_string()))?; + } } } - Ok(()) } @@ -103,34 +119,49 @@ impl MigrationHook for AddressSpace { AddressSpaceState::from_bytes(&state[0..size_of::()]) .with_context(|| MigrationError::FromBytesError("MEMORY"))?; let memfile_arc = Arc::new(memory.unwrap().try_clone().unwrap()); - - for ram_state in address_space_state.ram_region_state - [0..address_space_state.nr_ram_region as usize] - .iter() - { - let file_backend = FileBackend { - file: memfile_arc.clone(), - offset: ram_state.offset, - page_size: host_page_size(), - }; - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(ram_state.base_address), - None, + if let Some(machine_ram) = self.get_machine_ram() { + let mut offset = 0_u64; + for ram_state in address_space_state.ram_alias_state + [0..address_space_state.nr_alias_region as usize] + .iter() + { + let file_backend = FileBackend { + file: memfile_arc.clone(), + offset: ram_state.offset, + page_size: host_page_size(), + }; + let host_mmap = Arc::new( + HostMemMapping::new( + GuestAddress(0), + None, + ram_state.size, + Some(file_backend), + false, + false, + false, + ) + .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?, + ); + machine_ram + .add_subregion_not_update( + Region::init_ram_region(host_mmap.clone(), "HostMem"), + offset, + ) + .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?; + offset += ram_state.size; + } + for ram_state in address_space_state.ram_region_state + [0..address_space_state.nr_ram_region as usize] + .iter() + { + let ram = Region::init_alias_region( + machine_ram.clone(), + ram_state.alias_offset, ram_state.size, - Some(file_backend), - false, - false, - false, - ) - .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?, - ); - self.root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "HostMem"), - host_mmap.start_address().raw_value(), - ) - .map_err(|e| MigrationError::RestoreVmMemoryErr(e.to_string()))?; + "ram", + ); + self.root().add_subregion(ram, ram_state.offset)?; + } } Ok(()) diff --git a/boot_loader/src/lib.rs b/boot_loader/src/lib.rs index 589f835c8..46955e83f 100644 --- a/boot_loader/src/lib.rs +++ b/boot_loader/src/lib.rs @@ -43,6 +43,7 @@ //! let guest_mem = AddressSpace::new( //! Region::init_container_region(std::u64::MAX, "guest_mem"), //! "guest_mem", +//! None, //! ) //! .unwrap(); //! let kernel_file = std::path::PathBuf::from("/path/to/my/kernel"); @@ -67,6 +68,7 @@ //! let guest_mem = AddressSpace::new( //! Region::init_container_region(u64::MAX, "guest_mem"), //! "guest_mem", +//! None, //! ) //! .unwrap(); //! let kernel_file = std::path::PathBuf::from("/path/to/my/kernel"); diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs index fe7852adc..7efb5ab57 100644 --- a/boot_loader/src/x86_64/bootparam.rs +++ b/boot_loader/src/x86_64/bootparam.rs @@ -233,7 +233,7 @@ mod test { #[test] fn test_boot_param() { let root = Region::init_container_region(0x2000_0000, "root"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let ram1 = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 534fb4bb3..ddeede393 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -287,7 +287,7 @@ mod test { #[test] fn test_x86_bootloader_and_kernel_cmdline() { let root = Region::init_container_region(0x2000_0000, "root"); - let space = AddressSpace::new(root.clone(), "space").unwrap(); + let space = AddressSpace::new(root.clone(), "space", None).unwrap(); let ram1 = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 4bbd46485..8c476b376 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1309,11 +1309,16 @@ mod test { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sys_mem"), "sys_mem", + None, ) .unwrap(); #[cfg(target_arch = "x86_64")] - let sys_io = - AddressSpace::new(Region::init_container_region(1 << 16, "sys_io"), "sys_io").unwrap(); + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sys_io"), + "sys_io", + None, + ) + .unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( @@ -1327,7 +1332,7 @@ mod test { fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "root"); - let sys_space = AddressSpace::new(root, "sys_space").unwrap(); + let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index e123fee80..d3d51099b 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -888,11 +888,16 @@ mod test { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sys_mem"), "sys_mem", + None, ) .unwrap(); #[cfg(target_arch = "x86_64")] - let sys_io = - AddressSpace::new(Region::init_container_region(1 << 16, "sys_io"), "sys_io").unwrap(); + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sys_io"), + "sys_io", + None, + ) + .unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 954a4a1c6..0fa061395 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -317,11 +317,16 @@ mod tests { pub fn create_pci_host() -> Arc> { #[cfg(target_arch = "x86_64")] - let sys_io = - AddressSpace::new(Region::init_container_region(1 << 16, "sysio"), "sysio").unwrap(); + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sysio"), + "sysio", + None, + ) + .unwrap(); let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); Arc::new(Mutex::new(PciHost::new( diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 81467ffc6..7af249346 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -1342,11 +1342,16 @@ mod tests { .unwrap(); #[cfg(target_arch = "x86_64")] - let sys_io = - AddressSpace::new(Region::init_container_region(1 << 16, "sysio"), "sysio").unwrap(); + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sysio"), + "sysio", + None, + ) + .unwrap(); let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); assert_eq!(pci_config.bars[1].address, BAR_SPACE_UNMAPPED); diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index d46a26194..1ec5dd6fe 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -619,11 +619,16 @@ pub mod tests { pub fn create_pci_host() -> Arc> { #[cfg(target_arch = "x86_64")] - let sys_io = - AddressSpace::new(Region::init_container_region(1 << 16, "sysio"), "sysio").unwrap(); + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sysio"), + "sysio", + None, + ) + .unwrap(); let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); Arc::new(Mutex::new(PciHost::new( diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 71e0bc00e..1bb09976f 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -422,6 +422,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus: Arc> = Arc::new(Mutex::new(PciBus::new( diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9dc9e713c..a5ad44d39 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -162,15 +162,24 @@ impl MachineBase { vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ); + let machine_ram = Arc::new(Region::init_container_region( + u64::max_value(), + "MachineRam", + )); let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "SysMem"), "sys_mem", + Some(machine_ram.clone()), ) .with_context(|| MachineError::CrtIoSpaceErr)?; #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new(Region::init_container_region(1 << 16, "SysIo"), "SysIo") - .with_context(|| MachineError::CrtIoSpaceErr)?; + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "SysIo"), + "SysIo", + None, + ) + .with_context(|| MachineError::CrtIoSpaceErr)?; let sysbus = SysBus::new( #[cfg(target_arch = "x86_64")] &sys_io, @@ -196,10 +205,7 @@ impl MachineBase { numa_nodes: None, drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), fwcfg_dev: None, - machine_ram: Arc::new(Region::init_container_region( - u64::max_value(), - "MachineRam", - )), + machine_ram, }) } diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index d46b213df..47943e90b 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -216,6 +216,7 @@ impl VirtioFs { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "VirtioFsMem"), "VirtioFsMem", + None, ) .with_context(|| "Failed to create address space")?; diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index c05c599b7..210c470bf 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1190,7 +1190,7 @@ mod tests { fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "space"); - let sys_space = AddressSpace::new(root, "space").unwrap(); + let sys_space = AddressSpace::new(root, "space", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 428215da1..8c5226d6e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1350,7 +1350,7 @@ mod tests { // build dummy address space of vm fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem").unwrap(); + let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 86e4fbbb6..06313cef2 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -371,7 +371,7 @@ mod tests { // build dummy address space of vm fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem").unwrap(); + let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 31481f655..c4f6efa00 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -920,7 +920,7 @@ mod tests { fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem").unwrap(); + let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 896c84f8f..26e8f0845 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -605,7 +605,7 @@ mod tests { fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem").unwrap(); + let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 97864abff..97051ed6c 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1422,6 +1422,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( @@ -1478,6 +1479,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( @@ -1527,6 +1529,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( @@ -1603,6 +1606,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( @@ -1642,6 +1646,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( @@ -1666,6 +1671,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let mem_size: u64 = 1024 * 1024; @@ -1772,6 +1778,7 @@ mod tests { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", + None, ) .unwrap(); let parent_bus = Arc::new(Mutex::new(PciBus::new( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 379027f52..6a11ae902 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -415,7 +415,7 @@ mod tests { fn vhost_address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem").unwrap(); + let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index c9f932b36..02cffe007 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -394,7 +394,7 @@ mod tests { fn vsock_address_space_init() -> Arc { let root = Region::init_container_region(u64::max_value(), "sysmem"); - let sys_mem = AddressSpace::new(root, "sysmem").unwrap(); + let sys_mem = AddressSpace::new(root, "sysmem", None).unwrap(); sys_mem } -- Gitee From a35422cba46f8009ef685a8b33c371d47873b0c4 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sat, 11 Nov 2023 11:22:35 +0800 Subject: [PATCH 1433/2187] Code cleanup: remove unused file reader.rs Signed-off-by: Yan Wen --- util/src/lib.rs | 1 - util/src/reader.rs | 171 --------------------------------------------- 2 files changed, 172 deletions(-) delete mode 100644 util/src/reader.rs diff --git a/util/src/lib.rs b/util/src/lib.rs index abe1a436b..ecf8ded33 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -30,7 +30,6 @@ pub mod num_ops; pub mod offsetof; #[cfg(feature = "pixman")] pub mod pixman; -pub mod reader; pub mod seccomp; pub mod socket; pub mod syscall; diff --git a/util/src/reader.rs b/util/src/reader.rs deleted file mode 100644 index d0b0f9be5..000000000 --- a/util/src/reader.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::cmp::Ordering; -use std::io::{Read, Result}; - -const DEFAULT_BUFFER_SIZE: usize = 8192; - -pub struct BufferReader { - reader: R, - buffer: Vec, - pos: usize, - end: usize, -} - -impl BufferReader { - /// Create a new buffer_reader instance from `Read`. - pub fn new(reader: R) -> Self { - let mut buffer = Vec::::new(); - buffer.resize(DEFAULT_BUFFER_SIZE, 0); - - BufferReader { - reader, - buffer, - pos: 0, - end: 0, - } - } - - pub fn with_capacity(reader: R, capacity: usize) -> Self { - let mut buffer = Vec::::new(); - buffer.resize(capacity, 0_u8); - - BufferReader { - reader, - buffer, - pos: 0, - end: 0, - } - } - - /// Returns the number of bytes the internal buffer can hold at once. - pub fn capacity(&self) -> usize { - self.buffer.len() - } - - /// Read data from `reader` to fill `buffer`, update `pos` and `end`. - pub fn read_buffer(&mut self) -> Result<()> { - let length = self.reader.read(&mut self.buffer)?; - self.pos = 0; - self.end = length; - - Ok(()) - } - - /// Read assigned length bytes from `file` to come out with `Vec`. - /// - /// # Arguments - /// - /// * `bytes_num` - length wanted to read from `file`. - pub fn read_vectored(&mut self, bytes_num: usize) -> Option> { - let mut slice_vec = Vec::::new(); - let mut read_len = bytes_num; - - // Judge the file is read over or not. - while self.end != 0 { - match read_len.cmp(&(self.end - self.pos)) { - Ordering::Greater => { - slice_vec.extend(&self.buffer[self.pos..self.end]); - read_len -= self.end - self.pos; - self.read_buffer().unwrap(); - } - Ordering::Equal => { - slice_vec.extend(&self.buffer[self.pos..self.end]); - self.read_buffer().unwrap(); - break; - } - Ordering::Less => { - slice_vec.extend(&self.buffer[self.pos..self.pos + read_len]); - self.pos += read_len; - break; - } - } - } - - if slice_vec.len() == bytes_num { - Some(slice_vec) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use std::fs::File; - use std::io::{Result, Write}; - - use super::BufferReader; - - fn mk_tempfile() -> Result<()> { - let module_dir = option_env!("CARGO_MANIFEST_DIR").unwrap(); - let tempfile_path = module_dir.to_string() + "/tempfile"; - let mut file = File::create(tempfile_path)?; - write_test_data(&mut file) - } - - fn open_tempfile() -> Result { - let module_dir = option_env!("CARGO_MANIFEST_DIR").unwrap(); - let tempfile_path = module_dir.to_string() + "/tempfile"; - File::open(tempfile_path) - } - - fn del_tempfile() -> Result<()> { - let module_dir = option_env!("CARGO_MANIFEST_DIR").unwrap(); - let tempfile_path = module_dir.to_string() + "/tempfile"; - std::fs::remove_file(tempfile_path) - } - - fn write_test_data(file: &mut File) -> Result<()> { - let mut test_buffer_vec_01 = [0_u8; 16384]; - for i in 0..16384 { - test_buffer_vec_01[i] = (i % 256) as u8; - } - file.write(&mut test_buffer_vec_01)?; - - let mut test_buffer_vec_02 = [8_u8; 16384]; - file.write(&mut test_buffer_vec_02)?; - - Ok(()) - } - - #[test] - fn test_buffer_reader() -> Result<()> { - assert!(mk_tempfile().is_ok()); - - let file = open_tempfile()?; - let mut buffer_reader = BufferReader::new(&file); - - assert_eq!(buffer_reader.capacity(), 8_192); - assert!(buffer_reader.read_buffer().is_ok()); - assert_eq!(buffer_reader.read_vectored(4), Some(vec![0, 1, 2, 3])); - assert_eq!( - buffer_reader.read_vectored(8), - Some(vec![4, 5, 6, 7, 8, 9, 10, 11]) - ); - assert_eq!( - buffer_reader.read_vectored(243), - Some((12..255).into_iter().collect::>()) - ); - assert!(buffer_reader.read_vectored(16125).is_some()); - assert_eq!( - buffer_reader.read_vectored(8), - Some(vec![252, 253, 254, 255, 8, 8, 8, 8]) - ); - assert!(buffer_reader.read_vectored(16380).is_some()); - assert!(buffer_reader.read_vectored(1).is_none()); - - assert!(del_tempfile().is_ok()); - Ok(()) - } -} -- Gitee From 41bb474e66a9ef9ff3384f3363a276ba6ccf6324 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 11 Nov 2023 11:14:55 +0800 Subject: [PATCH 1434/2187] block_backend: Fix potential OOM Constrait the max value of parameter. Signed-off-by: Keqian Zhu --- block_backend/src/qcow2/table.rs | 4 +--- machine_manager/src/config/drive.rs | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index bf39fce0b..4f8c93a7a 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -25,11 +25,9 @@ use crate::{ }, BlockProperty, }; +use machine_manager::config::MAX_L2_CACHE_SIZE; use util::num_ops::div_round_up; -// L2 Cache max size is 32M. -const MAX_L2_CACHE_SIZE: u64 = 32 * (1 << 20); - #[derive(PartialEq, Eq, Debug)] pub enum Qcow2ClusterType { /// Cluster is unallocated. diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 29f7d96f2..a042280d8 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -36,6 +36,11 @@ const MIN_QUEUE_SIZE_BLK: u16 = 2; // Max size of each virtqueue for virtio-blk. const MAX_QUEUE_SIZE_BLK: u16 = 1024; +// L2 Cache max size is 32M. +pub const MAX_L2_CACHE_SIZE: u64 = 32 * (1 << 20); +// Refcount table cache max size is 32M. +const MAX_REFTABLE_CACHE_SIZE: u64 = 32 * (1 << 20); + /// Represent a single drive backend file. pub struct DriveFile { /// Drive id. @@ -226,7 +231,7 @@ impl ConfigCheck for DriveConfig { MAX_PATH_LENGTH, ))); } - if self.iops.is_some() && self.iops.unwrap() > MAX_IOPS { + if self.iops > Some(MAX_IOPS) { return Err(anyhow!(ConfigError::IllegalValue( "iops of block device".to_string(), 0, @@ -257,6 +262,25 @@ impl ConfigCheck for DriveConfig { ))); } + if self.l2_cache_size > Some(MAX_L2_CACHE_SIZE) { + return Err(anyhow!(ConfigError::IllegalValue( + "l2-cache-size".to_string(), + 0, + true, + MAX_L2_CACHE_SIZE, + true + ))); + } + if self.refcount_cache_size > Some(MAX_REFTABLE_CACHE_SIZE) { + return Err(anyhow!(ConfigError::IllegalValue( + "refcount-cache-size".to_string(), + 0, + true, + MAX_REFTABLE_CACHE_SIZE, + true + ))); + } + Ok(()) } } -- Gitee From 83471745e7a0a5ff54c66af7fb4a71f669e52a64 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 11 Nov 2023 11:26:15 +0800 Subject: [PATCH 1435/2187] block_backend: Simply hashmap element dropping Use hashmap retain method instead. Signed-off-by: Keqian Zhu --- block_backend/src/qcow2/cache.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 36dea3656..445ddc31e 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -214,15 +214,8 @@ impl Qcow2Cache { } pub fn clean_up_dirty_cache(&mut self) { - let mut dirty_keys = Vec::with_capacity(self.cache_map.len()); - for (key, entry) in self.cache_map.iter() { - if entry.borrow().dirty_info.is_dirty { - dirty_keys.push(*key); - } - } - for key in dirty_keys { - self.cache_map.remove(&key); - } + self.cache_map + .retain(|_, value| !value.borrow().dirty_info.is_dirty); } pub fn flush(&mut self, sync_aio: Rc>) -> Result<()> { -- Gitee From 6d09a5e690ee195103ade6252623439d6c01e02d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 11 Nov 2023 11:58:36 +0800 Subject: [PATCH 1436/2187] machine_manager/config: Refactor boot_source config length is unnecessary. Signed-off-by: Keqian Zhu --- machine_manager/src/config/boot_source.rs | 32 +++++++---------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/machine_manager/src/config/boot_source.rs b/machine_manager/src/config/boot_source.rs index 3cd9c3692..6f7c227a4 100644 --- a/machine_manager/src/config/boot_source.rs +++ b/machine_manager/src/config/boot_source.rs @@ -96,16 +96,14 @@ impl ConfigCheck for InitrdConfig { } /// Struct `KernelParams` used to parse kernel cmdline to config. -/// Contains a `Vec` and its `len()`. #[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct KernelParams { pub params: Vec, - pub length: usize, } impl ConfigCheck for KernelParams { fn check(&self) -> Result<()> { - for param in self.params.clone() { + for param in self.params.iter() { check_arg_too_long(¶m.value, "kernel params")?; } @@ -118,37 +116,27 @@ impl KernelParams { fn from_str(kernel_cmdline: String) -> Self { let split = kernel_cmdline.split(' '); let vec = split.collect::>(); - let mut params: Vec = Vec::new(); - let mut length: usize = 0; + let mut params: Vec = Vec::with_capacity(vec.len()); for item in vec { params.push(Param::from_str(item)); - length += 1; } - KernelParams { params, length } + KernelParams { params } } /// Push new `Param` to `KernelParams`. pub fn push(&mut self, item: Param) { self.params.push(item); - self.length = self - .length - .checked_add(1) - .unwrap_or_else(|| panic!("Kernel params length is too long: {}", self.length)); } /// Move all the `Param` into `KernelParams`. pub fn append(&mut self, items: &mut Vec) { - self.length = self - .length - .checked_add(items.len()) - .unwrap_or_else(|| panic!("Kernel params length is too long: {}", self.length)); self.params.append(items); } /// Check `KernelParam` whether contains `item` or not. pub fn contains(&self, item: &str) -> bool { - for i in 0..self.length { - if self.params[i].param_type == item { + for param in self.params.iter() { + if param.param_type == item { return true; } } @@ -158,9 +146,9 @@ impl KernelParams { impl fmt::Display for KernelParams { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut vec: Vec = Vec::with_capacity(self.length); - for i in 0..self.length { - vec.push(self.params[i].to_string()); + let mut vec: Vec = Vec::with_capacity(self.params.len()); + for param in self.params.iter() { + vec.push(param.to_string()); } write!(f, "{}", vec.join(" ")) } @@ -247,10 +235,10 @@ mod tests { let test_kernel = "reboot=k panic=1 pci=off nomodules 8250.nr_uarts=0"; let mut test_kernel_param = KernelParams::from_str(test_kernel.to_string()); - assert_eq!(test_kernel_param.length, 5); + assert_eq!(test_kernel_param.params.len(), 5); test_kernel_param.push(Param::from_str("maxcpus=8")); - assert_eq!(test_kernel_param.length, 6); + assert_eq!(test_kernel_param.params.len(), 6); assert_eq!(test_kernel_param.contains("maxcpus"), true); assert_eq!(test_kernel_param.contains("cpus"), false); assert_eq!( -- Gitee From 3ec821ef77df0f00c9ea824b72e411b60ad75d71 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 12 Nov 2023 01:57:25 +0800 Subject: [PATCH 1437/2187] safety: Fix possible OOM problem related to resize() Signed-off-by: Keqian Zhu --- address_space/src/host_mmap.rs | 1 + block_backend/src/qcow2/check.rs | 2 ++ block_backend/src/qcow2/mod.rs | 1 + block_backend/src/qcow2/refcount.rs | 2 ++ block_backend/src/qcow2/snapshot.rs | 1 + devices/src/usb/camera.rs | 1 + migration/src/general.rs | 9 ++++++++- migration/src/protocol.rs | 7 +++++++ ui/src/vnc/auth_vencrypt.rs | 1 + virtio/src/device/gpu.rs | 1 + 10 files changed, 25 insertions(+), 1 deletion(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index e7dfafaa9..02da2f6b1 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -355,6 +355,7 @@ fn set_host_memory_policy(mem_mappings: &Arc, zone: &MemZoneConf let mut max_node = nodes[nodes.len() - 1] as usize; let mut nmask: Vec = Vec::new(); + // Upper limit of max_node is MAX_NODES. nmask.resize(max_node / 64 + 1, 0); for node in nodes.iter() { nmask[(*node / 64) as usize] |= 1_u64 << (*node % 64); diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index 160e73403..2f6bb9f70 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -82,6 +82,7 @@ impl RefcountBlock { return; }; let new_table_bytes = new_size * self.entry_bytes; + // SAFETY: Upper limit of new_table_bytes is decided by disk file size. self.data.resize(new_table_bytes, 0); self.table_size = new_size; self.nb_clusters = new_size as u64; @@ -990,6 +991,7 @@ impl Qcow2Driver { // Extend the refcount table if new_reftable.len() <= refblock_idx { + // SAFETY: Upper limit of refblock_idx is decided by disk file size. new_reftable.resize(refblock_idx + 1, 0); // Need to reallocate clusters for new refcount table. reftable_offset = 0; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 16623852b..c32e06c1d 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -821,6 +821,7 @@ impl Qcow2Driver { .sync_aio .borrow_mut() .read_ctrl_cluster(snap.l1_table_offset, snap.l1_size as u64)?; + // SAFETY: Upper limit of l1_size is decided by disk virtual size. snap_l1_table.resize(self.header.l1_size as usize, 0); // Increase the refcount of all clusters searched by L1 table. diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 9eeb686e7..41da3cb06 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -213,6 +213,7 @@ impl RefCount { ); } let mut new_table = self.refcount_table.clone(); + // SAFETY: Upper limit of new_table_size is disk file size. new_table.resize(new_table_size as usize, 0); let start_offset = start_idx * self.cluster_size; let mut table_offset = start_offset; @@ -727,6 +728,7 @@ mod test { rc_block.push(0x00); rc_block.push(0x01); } + // SAFETY: Upper limit of following value is decided by disk file size. rc_table.resize((rct * cluster_sz) as usize, 0); rc_block.resize((rcb * cluster_sz) as usize, 0); let header = QcowHeader { diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index b9a93e81f..78e7841a0 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -298,6 +298,7 @@ impl QcowSnapshot { // 8 bytes Alignment. let tmp_size = buf.len(); let size = round_up(tmp_size as u64, 8).unwrap(); + // SAFETY: The size is a round up of old size. buf.resize(size as usize, 0); buf diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 3577fa724..057ab4a94 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -1064,6 +1064,7 @@ fn gen_fmt_desc(fmt_list: Vec) -> Result + body.clone().iter().fold(0, |len, x| len + x.data.len()) as u16; let mut vec = header_struct.as_bytes().to_vec(); + // SAFETY: bLength is 0xb + fmt_list.len(). vec.resize(header_struct.bLength as usize, 0); buf.push(Arc::new(UsbDescOther { data: vec })); buf.append(&mut body); diff --git a/migration/src/general.rs b/migration/src/general.rs index 14077a1cc..950e613d9 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -15,7 +15,7 @@ use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::mem::size_of; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use crate::manager::{Instance, MIGRATION_MANAGER}; use crate::protocol::{ @@ -81,6 +81,7 @@ impl MigrationManager { // 2. read header according to its length let mut header_bytes = Vec::new(); + // SAFETY: upper limit of header_len is HEADER_LENGTH - 8. header_bytes.resize(header_len as usize, 0); fd.read_exact(&mut header_bytes)?; @@ -110,6 +111,7 @@ impl MigrationManager { pub fn save_desc_db(fd: &mut dyn Write) -> Result<()> { let length = Self::desc_db_len()?; let mut buffer = Vec::new(); + // SAFETY: desc db length is under control. buffer.resize(length, 0); let mut start = 0; @@ -132,6 +134,7 @@ impl MigrationManager { desc_length: usize, ) -> Result> { let mut desc_buffer = Vec::new(); + // SAFETY: desc_length has been checked in check_header(). desc_buffer.resize(desc_length, 0); fd.read_exact(&mut desc_buffer)?; let mut snapshot_desc_db = HashMap::::new(); @@ -142,6 +145,9 @@ impl MigrationManager { Ok(desc) => desc, Err(_) => break, }; + if device_desc.size > (1 << 20) { + bail!("The size field of DeviceStateDesc is too large"); + } snapshot_desc_db.insert(device_desc.alias, device_desc); } @@ -176,6 +182,7 @@ impl MigrationManager { .with_context(|| "Failed to get snap_desc name")?; let mut state_data = Vec::new(); + // SAFETY: size has been checked in restore_desc_db(). state_data.resize(snap_desc.size as usize, 0); fd.read_exact(&mut state_data)?; diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index fd791d6ad..fbaaa8371 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -446,6 +446,12 @@ impl MigrationHeader { ))); } + if self.desc_len > (1 << 20) { + return Err(anyhow!(MigrationError::HeaderItemNotFit( + "Desc length".to_string() + ))); + } + Ok(()) } } @@ -563,6 +569,7 @@ impl DeviceStateDesc { pub fn add_padding(&self, desc: &DeviceStateDesc, current_slice: &mut Vec) -> Result<()> { let tmp_slice = current_slice.clone(); current_slice.clear(); + // SAFETY: size has been checked in restore_desc_db(). current_slice.resize(self.size as usize, 0); for field in self.clone().fields { if desc.contains(&field.alias) { diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index 902ee14ad..c5f3a845b 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -420,6 +420,7 @@ impl IoOperations for TlsIoChannel { let io_state = self.tls_conn.process_new_packets()?; if io_state.plaintext_bytes_to_read() > 0 { len = io_state.plaintext_bytes_to_read(); + // FIXME: Split len to avoid possible OOM. buf.resize(len, 0u8); self.tls_conn.reader().read_exact(buf)?; } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 2db11aac5..ca34c7abb 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1250,6 +1250,7 @@ impl GpuIoHandler { // Start reading and parsing. let mut ents = Vec::::new(); + // SAFETY: Upper limit of ents is 16384. ents.resize(entries as usize, VirtioGpuMemEntry::default()); let ents_buf = unsafe { from_raw_parts_mut(ents.as_mut_ptr() as *mut u8, ents_size as usize) }; -- Gitee From 1985bf85ba979424722176d69c14aa80f1edd360 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 12 Nov 2023 04:23:11 +0800 Subject: [PATCH 1438/2187] msix: Fix reset of pba and msix table The old code actually has no effect on table and pba. Signed-off-by: Keqian Zhu --- devices/src/pci/msix.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 44cc02441..12606614f 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -129,8 +129,8 @@ impl Msix { } pub fn reset(&mut self) { - self.table.resize_with(self.table.len(), || 0); - self.pba.resize_with(self.pba.len(), || 0); + self.table.fill(0); + self.pba.fill(0); self.func_masked = true; self.enabled = true; self.mask_all_vectors(); -- Gitee From 792245feac768db9593473bd3377d9607923518d Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Wed, 8 Nov 2023 16:39:45 +0800 Subject: [PATCH 1439/2187] qmp: Add tcp socket for qmp can start tcp socket by qmp, parameters such as: -qmp tcp:127.0.0.1:4444,server,nowait or: -chardev socket,host=127.0.0.1,port=4444,id=chardev_id,server,nowait \ -mon chardev=chardev_id,id=monitor_id,mode=control Signed-off-by: Huxiaohang --- docs/qmp.md | 20 +++- machine_manager/src/cmdline.rs | 83 ++++++++++----- machine_manager/src/qmp/qmp_socket.rs | 146 ++++++++++++++------------ machine_manager/src/socket.rs | 8 +- src/main.rs | 6 +- 5 files changed, 165 insertions(+), 98 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index 197282eae..da806102e 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -9,12 +9,18 @@ StratoVirt controls VM's lifecycle and external api interface with [QMP](https:/ When running StratoVirt, you must create QMP in cmdline arguments as a management interface. -StratoVirt supports UnixSocket-type QMP, you can set it by: +StratoVirt supports UnixSocket-type QMP and TcpSocket-type QMP, you can set it by: ```shell # cmdline +# Start with UnixSocket -qmp unix:/path/to/api/socket,server,nowait ``` +```shell +# cmdline +# Start with TcpSocket +-qmp tcp:ip:port,server,nowait +``` Where, the information about 'server' and 'nowait' can be found in [section 2.12 Chardev](#212-chardev) On top of that, monitor can be used to create QMP connection as well. @@ -29,9 +35,16 @@ Three properties can be set for monitor. ```shell # cmdline +# Start with UnixSocket -chardev socket,path=/path/to/monitor/sock,id=chardev_id,server,nowait -mon chardev=chardev_id,id=monitor_id,mode=control ``` +```shell +# cmdline +# Start with TcpSocket +-chardev socket,host=ip,port=port,id=chardev_id,server,nowait +-mon chardev=chardev_id,id=monitor_id,mode=control +``` ## QMP Connection @@ -43,7 +56,10 @@ Several steps to connect QMP are showed as following: # Start with UnixSocket $ ncat -U /path/to/api/socket ``` - +```shell +# Start with TcpSocket +$ ncat ip port +``` Once connection is built, you will receive a `greeting` message from StratoVirt. ```json diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index df414e18f..fdff9f035 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -10,17 +10,19 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::unix::net::UnixListener; - use anyhow::{bail, Context, Result}; use crate::{ config::{add_trace_events, ChardevType, CmdParser, MachineType, VmConfig}, + qmp::qmp_socket::QmpSocketPath, temp_cleaner::TempCleaner, }; -use util::arg_parser::{Arg, ArgMatches, ArgParser}; use util::file::clear_file; -use util::unix::{limit_permission, parse_unix_uri}; +use util::unix::limit_permission; +use util::{ + arg_parser::{Arg, ArgMatches, ArgParser}, + socket::SocketListener, +}; /// This macro is to run struct $z 's function $s whose arg is $x 's inner member. /// There is a multi-macro-cast in cases of vec and bool. @@ -180,8 +182,9 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("qmp") .long("qmp") - .value_name("unix:") - .help("set QMP's unix socket path") + .value_name("") + .help("\n\t\tset unix socket path: unix:,server,nowait; \ + \n\t\tset tcp socket path: tcp:ip:port,server,nowait") .takes_value(true) ) .arg( @@ -605,7 +608,10 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { /// # Errors /// /// The value of `qmp` is illegel. -pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result> { +pub fn check_api_channel( + args: &ArgMatches, + vm_config: &mut VmConfig, +) -> Result> { let mut sock_paths = Vec::new(); if let Some(qmp_config) = args.value_of("qmp") { let mut cmd_parser = CmdParser::new("qmp"); @@ -613,9 +619,9 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< cmd_parser.parse(&qmp_config)?; if let Some(uri) = cmd_parser.get_value::("")? { - let api_path = - parse_unix_uri(&uri).with_context(|| "Failed to parse qmp socket path")?; - sock_paths.push(api_path); + let sock_path = + QmpSocketPath::new(uri).with_context(|| "Failed to parse qmp socket path")?; + sock_paths.push(sock_path); } else { bail!("No uri found for qmp"); } @@ -656,7 +662,23 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< path ); } - sock_paths.push(path); + sock_paths.push( + QmpSocketPath::new(path).with_context(|| "Failed to parse qmp socket path")?, + ); + } else if let ChardevType::TcpSocket { + host, + port, + server, + nowait, + } = cfg.backend + { + if !server || !nowait { + bail!( + "Argument \'server\' and \'nowait\' are both required for chardev \'{}:{}\'", + host, port + ); + } + sock_paths.push(QmpSocketPath::Tcp { host, port }); } else { bail!("Only chardev of unix-socket type can be used for monitor"); } @@ -669,23 +691,34 @@ pub fn check_api_channel(args: &ArgMatches, vm_config: &mut VmConfig) -> Result< bail!("Please use \'-qmp\' or \'-mon\' to give a qmp path for Unix socket"); } let mut listeners = Vec::new(); - for path in sock_paths { - listeners.push( - bind_socket(path.clone()) - .with_context(|| format!("Failed to bind socket for path: {:?}", &path))?, - ) + for sock_path in sock_paths { + listeners.push(bind_socket(&sock_path).with_context(|| { + format!( + "Failed to bind socket for path: {:?}", + sock_path.to_string() + ) + })?) } Ok(listeners) } -fn bind_socket(path: String) -> Result { - clear_file(path.clone())?; - let listener = UnixListener::bind(&path) - .with_context(|| format!("Failed to bind socket file {}", &path))?; - // Add file to temporary pool, so it could be cleaned when vm exits. - TempCleaner::add_path(path.clone()); - limit_permission(&path) - .with_context(|| format!("Failed to limit permission for socket file {}", &path))?; - Ok(listener) +fn bind_socket(path: &QmpSocketPath) -> Result { + match path { + QmpSocketPath::Tcp { host, port } => { + let listener = SocketListener::bind_by_tcp(host, *port) + .with_context(|| format!("Failed to bind tcp socket {}:{}", &host, &port))?; + Ok(listener) + } + QmpSocketPath::Unix { path } => { + clear_file(path.clone())?; + let listener = SocketListener::bind_by_uds(path) + .with_context(|| format!("Failed to bind socket file {}", &path))?; + // Add file to temporary pool, so it could be cleaned when vm exits. + TempCleaner::add_path(path.clone()); + limit_permission(path) + .with_context(|| format!("Failed to limit permission for socket file {}", &path))?; + Ok(listener) + } + } } diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 08dac9660..9dd2beb9c 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -10,12 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::net::IpAddr; use std::os::unix::io::{AsRawFd, RawFd}; -use std::os::unix::net::{UnixListener, UnixStream}; use std::rc::Rc; +use std::str::FromStr; use std::sync::{Arc, Mutex, RwLock}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use log::{error, info, warn}; use vmm_sys_util::epoll::EventSet; @@ -34,59 +35,68 @@ use util::loop_context::{ NotifierOperation, }; use util::set_termi_canon_mode; +use util::socket::{SocketListener, SocketStream}; +use util::unix::parse_unix_uri; const LEAK_BUCKET_LIMIT: u64 = 100; -/// Type for api socket. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum SocketType { - Unix = 1, +pub enum QmpSocketPath { + Unix { path: String }, + Tcp { host: String, port: u16 }, } -/// Wrapper over UnixSteam. -#[derive(Debug)] -struct SocketStream(UnixStream); - -impl SocketStream { - fn from_unix_stream(stream: UnixStream) -> Self { - SocketStream(stream) +impl QmpSocketPath { + pub fn new(path: String) -> Result { + if path.starts_with('u') { + Ok(QmpSocketPath::Unix { + path: parse_unix_uri(&path)?, + }) + } else if path.starts_with('t') { + let (host, port) = parse_tcp_uri(&path)?; + Ok(QmpSocketPath::Tcp { host, port }) + } else { + bail!("invalid socket type: {}", path) + } } } -impl AsRawFd for SocketStream { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() +impl ToString for QmpSocketPath { + fn to_string(&self) -> String { + match self { + QmpSocketPath::Tcp { host, port } => { + format!("{}:{}", &host, &port) + } + QmpSocketPath::Unix { path } => path.clone(), + } } } -/// The wrapper over Unix socket and socket handler. +/// The wrapper over socket and socket handler. /// /// # Example /// /// ```no_run /// use std::io::prelude::*; /// use std::os::unix::io::AsRawFd; -/// use std::os::unix::net::{UnixListener, UnixStream}; +/// use std::os::unix::net::UnixStream; /// /// use machine_manager::qmp::qmp_socket::Socket; +/// use util::socket::SocketListener; /// /// fn main() -> std::io::Result<()> { -/// let listener = UnixListener::bind("/path/to/my/socket")?; -/// let socket = Socket::from_unix_listener(listener, None); +/// let listener = SocketListener::bind_by_uds("/path/to/my/socket").unwrap(); +/// let socket = Socket::from_listener(listener, None); /// assert!(!socket.is_connected()); /// /// let client_stream = UnixStream::connect("/path/to/my/socket")?; -/// let server_stream = socket.accept_unix_stream(); -/// socket.bind_unix_stream(server_stream); +/// socket.accept(); /// assert!(socket.is_connected()); /// Ok(()) /// } /// ``` pub struct Socket { - /// Type for Socket - sock_type: SocketType, /// Socket listener tuple - listener: UnixListener, + listener: SocketListener, /// Socket stream with RwLock stream: RwLock>, /// Perform socket command @@ -94,18 +104,17 @@ pub struct Socket { } impl Socket { - /// Allocates a new `Socket` with `UnixListener`. + /// Allocates a new `Socket` with `SocketListener`. /// /// # Arguments /// - /// * `listener` - The `UnixListener` bind to `Socket`. + /// * `listener` - The `SocketListener` bind to `Socket`. /// * `performer` - The `VM` to perform socket command. - pub fn from_unix_listener( - listener: UnixListener, + pub fn from_listener( + listener: SocketListener, performer: Option>>, ) -> Self { Socket { - sock_type: SocketType::Unix, listener, stream: RwLock::new(None), performer, @@ -118,34 +127,16 @@ impl Socket { } /// Accept stream and bind to Socket. - fn accept(&self) { - match self.sock_type { - SocketType::Unix => { - let stream = self.accept_unix_stream(); - self.bind_unix_stream(stream); - } - } - } - - /// Accept a new incoming connection unix stream from unix listener. - pub fn accept_unix_stream(&self) -> UnixStream { - let (stream, _) = self.listener.accept().unwrap(); - stream - } - - /// Get socket type from `Socket`. - #[allow(unused)] - fn get_socket_type(&self) -> SocketType { - self.sock_type + pub fn accept(&self) { + self.bind_stream(self.listener.accept().unwrap()); } - /// Bind `Socket` with a `UnixStream`. + /// Bind `Socket` with a `SocketStream`. /// /// # Arguments /// - /// * `unix_stream` - The `UnixStream` bind to `Socket`. - pub fn bind_unix_stream(&self, unix_stream: UnixStream) { - let stream = SocketStream::from_unix_stream(unix_stream); + /// * `sock_stream` - The `SocketStream` bind to `Socket`. + pub fn bind_stream(&self, stream: SocketStream) { *self.stream.write().unwrap() = Some(stream); } @@ -339,6 +330,28 @@ macro_rules! create_command_matches { }; } +/// Parse tcp uri to ip:port. +/// +/// # Notions +/// +/// tcp uri is the string as `tcp:ip:port`. +fn parse_tcp_uri(uri: &str) -> Result<(String, u16)> { + let parse_vec: Vec<&str> = uri.splitn(3, ':').collect(); + if parse_vec.len() == 3 && parse_vec[0] == "tcp" { + if let Ok(host) = IpAddr::from_str(parse_vec[1]) { + if let Ok(port) = parse_vec[2].parse::() { + Ok((host.to_string(), port)) + } else { + bail!("Invalid port used by tcp socket: {}", parse_vec[2]) + } + } else { + bail!("Invalid host used by tcp socket: {}", parse_vec[1]) + } + } else { + bail!("Invalid tcp uri: {}", uri) + } +} + /// Accept qmp command, analyze and exec it. /// /// # Arguments @@ -497,21 +510,24 @@ fn qmp_command_exec( #[cfg(test)] mod tests { - use std::os::unix::net::{UnixListener, UnixStream}; + use std::os::unix::net::UnixStream; use std::time::Duration; use super::*; use serde_json; // Environment Preparation for UnixSocket - fn prepare_unix_socket_environment(socket_id: &str) -> (UnixListener, UnixStream, UnixStream) { + fn prepare_unix_socket_environment( + socket_id: &str, + ) -> (SocketListener, UnixStream, SocketStream) { let socket_name: String = format!("test_{}.sock", socket_id); let _ = std::fs::remove_file(&socket_name); - let listener = UnixListener::bind(&socket_name).unwrap(); + let listener = SocketListener::bind_by_uds(&socket_name).unwrap(); + std::thread::sleep(Duration::from_millis(100)); let client = UnixStream::connect(&socket_name).unwrap(); - let (server, _) = listener.accept().unwrap(); + let server = listener.accept().unwrap(); (listener, client, server) } @@ -525,16 +541,15 @@ mod tests { fn test_socket_lifecycle() { // Pre test. Environment Preparation let (listener, _, server) = prepare_unix_socket_environment("04"); - let socket = Socket::from_unix_listener(listener, None); + let socket = Socket::from_listener(listener, None); // life cycle test // 1.Unconnected assert_eq!(socket.is_connected(), false); // 2.Connected - socket.bind_unix_stream(server); + socket.bind_stream(server); assert_eq!(socket.is_connected(), true); - assert_eq!(socket.get_socket_type(), SocketType::Unix); // 3.Unbind SocketStream, reset state socket.drop_stream(); @@ -542,8 +557,7 @@ mod tests { // 4.Accept and reconnect a new UnixStream let _new_client = UnixStream::connect("test_04.sock"); - let new_server = socket.accept_unix_stream(); - socket.bind_unix_stream(new_server); + socket.accept(); assert_eq!(socket.is_connected(), true); // After test. Environment Recover @@ -562,8 +576,8 @@ mod tests { let (listener, mut client, server) = prepare_unix_socket_environment("06"); // Use event! macro to send event msg to client - let socket = Socket::from_unix_listener(listener, None); - socket.bind_unix_stream(server); + let socket = Socket::from_listener(listener, None); + socket.bind_stream(server); QmpChannel::bind_writer(SocketRWHandler::new(socket.get_stream_fd())); // 1.send no-content event @@ -611,8 +625,8 @@ mod tests { let (listener, mut client, server) = prepare_unix_socket_environment("07"); // Use event! macro to send event msg to client - let socket = Socket::from_unix_listener(listener, None); - socket.bind_unix_stream(server); + let socket = Socket::from_listener(listener, None); + socket.bind_stream(server); // 1.send greeting response let res = socket.send_response(true); diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 494e4569f..33535c3b9 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -151,9 +151,13 @@ impl SocketRWHandler { // MSG_DONTWAIT: Enables nonblocking operation, if the operation would block the call // fails with the error EAGAIN or EWOULDBLOCK. When this error occurs, break loop let ret = unsafe { recvmsg(self.socket_fd, &mut mhdr, MSG_DONTWAIT) }; - if ret == -1 { + // when use tcpsocket client and exit with ctrl+c, ret value will return 0 and get + // error WouldBlock or BrokenPipe, so we should handle this 0 to break this loop. + if ret == -1 || ret == 0 { let sock_err = Error::last_os_error(); - if sock_err.kind() == ErrorKind::WouldBlock { + if sock_err.kind() == ErrorKind::WouldBlock + || sock_err.kind() == ErrorKind::BrokenPipe + { break; } else { return Err(sock_err); diff --git a/src/main.rs b/src/main.rs index 4afc90e9b..0e25d354d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -173,7 +173,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res EventLoop::set_manager(vm.clone(), None); for listener in listeners { - sockets.push(Socket::from_unix_listener(listener, Some(vm.clone()))); + sockets.push(Socket::from_listener(listener, Some(vm.clone()))); } vm } @@ -198,7 +198,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } for listener in listeners { - sockets.push(Socket::from_unix_listener(listener, Some(vm.clone()))); + sockets.push(Socket::from_listener(listener, Some(vm.clone()))); } vm } @@ -212,7 +212,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res EventLoop::set_manager(vm.clone(), None); for listener in listeners { - sockets.push(Socket::from_unix_listener(listener, Some(vm.clone()))); + sockets.push(Socket::from_listener(listener, Some(vm.clone()))); } vm } -- Gitee From 7cce09a335c4e604597105a774031c4f39b9ef31 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 8 Nov 2023 17:26:26 +0800 Subject: [PATCH 1440/2187] serial: fixup the logic calculating the offset of buffer Current logic calculating has potential risk that reread the buffer from 0 offset if loop more than 1 time. So this patch fixes the issue and optimize the calculation logic. Signed-off-by: Zhao Yi Min --- virtio/src/device/serial.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 5cce07dbc..b47ed3288 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -516,9 +516,9 @@ impl SerialPortHandler { fn input_handle_internal(&mut self, buffer: &[u8]) -> Result<()> { let mut queue_lock = self.input_queue.lock().unwrap(); - let count = buffer.len(); + let mut left = buffer.len(); let port = self.port.as_ref(); - if count == 0 || port.is_none() { + if left == 0 || port.is_none() { return Ok(()); } let port_locked = port.unwrap().lock().unwrap(); @@ -526,6 +526,7 @@ impl SerialPortHandler { return Ok(()); } + let mut written_count = 0_usize; loop { let elem = queue_lock .vring @@ -534,11 +535,11 @@ impl SerialPortHandler { break; } - let mut written_count = 0_usize; + let mut once_count = 0_usize; for elem_iov in elem.in_iovec.iter() { - let allow_write_count = cmp::min(written_count + elem_iov.len as usize, count); - let mut source_slice = &buffer[written_count..allow_write_count]; - let len = source_slice.len(); + let len = cmp::min(elem_iov.len as usize, left); + let write_end = written_count + len; + let mut source_slice = &buffer[written_count..write_end]; self.mem_space .write(&mut source_slice, elem_iov.addr, len as u64) @@ -549,19 +550,21 @@ impl SerialPortHandler { ) })?; - written_count = allow_write_count; - if written_count >= count { + written_count = write_end; + once_count += len; + left -= len; + if left == 0 { break; } } queue_lock .vring - .add_used(&self.mem_space, elem.index, written_count as u32) + .add_used(&self.mem_space, elem.index, once_count as u32) .with_context(|| { format!( "Failed to add used ring for virtio serial port input: index {} len {}", - elem.index, written_count + elem.index, once_count ) })?; @@ -578,7 +581,7 @@ impl SerialPortHandler { })?; } - if written_count >= count { + if left == 0 { break; } } -- Gitee From 9a9772e6d5ae9900b495e6bfdee87aee1188a643 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 14 Nov 2023 10:17:07 +0800 Subject: [PATCH 1441/2187] virtio: split-queue: introduce get_desc_info() This patch introduces get_desc_info() for later use. Signed-off-by: Zhao Yi Min --- virtio/src/queue/split.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 31481f655..e608c790e 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -733,14 +733,14 @@ impl SplitVring { } } - fn get_vring_element( + fn get_desc_info( &mut self, sys_mem: &Arc, + next_avail: Wrapping, features: u64, - elem: &mut Element, - ) -> Result<()> { - let index_offset = VRING_FLAGS_AND_IDX_LEN - + AVAILELEM_LEN * u64::from(self.next_avail.0 % self.actual_size()); + ) -> Result { + let index_offset = + VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(next_avail.0 % self.actual_size()); // The GPA of avail_ring_host with avail table length has been checked in // is_invalid_memory which must not be overflowed. let desc_index_addr = self.addr_cache.avail_ring_host + index_offset; @@ -760,16 +760,26 @@ impl SplitVring { // Suppress queue notification related to current processing desc chain. if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { - self.set_avail_event(sys_mem, (self.next_avail + Wrapping(1)).0) + self.set_avail_event(sys_mem, (next_avail + Wrapping(1)).0) .with_context(|| "Failed to set avail event for popping avail ring")?; } - let desc_info = DescInfo { + Ok(DescInfo { table_host: self.addr_cache.desc_table_host, size: self.actual_size(), index: desc_index, desc, - }; + }) + } + + fn get_vring_element( + &mut self, + sys_mem: &Arc, + features: u64, + elem: &mut Element, + ) -> Result<()> { + let desc_info = self.get_desc_info(sys_mem, self.next_avail, features)?; + SplitVringDesc::get_element(sys_mem, &desc_info, &mut self.cache, elem).with_context( || { format!( -- Gitee From a562ba34e31e0f4d279cce40977561971cc9d26d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 2 Nov 2023 10:36:50 +0800 Subject: [PATCH 1442/2187] mod_test: move the aarch64 testcases to a separate directory Signed-off-by: yezengruan --- tests/mod_test/tests/{ => aarch64}/acpi_test.rs | 0 tests/mod_test/tests/{ => aarch64}/ged_test.rs | 0 tests/mod_test/tests/aarch64/mod.rs | 16 ++++++++++++++++ tests/mod_test/tests/{ => aarch64}/pl031_test.rs | 0 tests/mod_test/tests/{ => aarch64}/ramfb_test.rs | 0 tests/mod_test/tests/mod.rs | 8 +------- 6 files changed, 17 insertions(+), 7 deletions(-) rename tests/mod_test/tests/{ => aarch64}/acpi_test.rs (100%) rename tests/mod_test/tests/{ => aarch64}/ged_test.rs (100%) create mode 100644 tests/mod_test/tests/aarch64/mod.rs rename tests/mod_test/tests/{ => aarch64}/pl031_test.rs (100%) rename tests/mod_test/tests/{ => aarch64}/ramfb_test.rs (100%) diff --git a/tests/mod_test/tests/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs similarity index 100% rename from tests/mod_test/tests/acpi_test.rs rename to tests/mod_test/tests/aarch64/acpi_test.rs diff --git a/tests/mod_test/tests/ged_test.rs b/tests/mod_test/tests/aarch64/ged_test.rs similarity index 100% rename from tests/mod_test/tests/ged_test.rs rename to tests/mod_test/tests/aarch64/ged_test.rs diff --git a/tests/mod_test/tests/aarch64/mod.rs b/tests/mod_test/tests/aarch64/mod.rs new file mode 100644 index 000000000..846da10cc --- /dev/null +++ b/tests/mod_test/tests/aarch64/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod acpi_test; +mod ged_test; +mod pl031_test; +mod ramfb_test; diff --git a/tests/mod_test/tests/pl031_test.rs b/tests/mod_test/tests/aarch64/pl031_test.rs similarity index 100% rename from tests/mod_test/tests/pl031_test.rs rename to tests/mod_test/tests/aarch64/pl031_test.rs diff --git a/tests/mod_test/tests/ramfb_test.rs b/tests/mod_test/tests/aarch64/ramfb_test.rs similarity index 100% rename from tests/mod_test/tests/ramfb_test.rs rename to tests/mod_test/tests/aarch64/ramfb_test.rs diff --git a/tests/mod_test/tests/mod.rs b/tests/mod_test/tests/mod.rs index 313e82c98..826c4d47a 100644 --- a/tests/mod_test/tests/mod.rs +++ b/tests/mod_test/tests/mod.rs @@ -11,19 +11,13 @@ // See the Mulan PSL v2 for more details. #[cfg(target_arch = "aarch64")] -mod acpi_test; +mod aarch64; mod balloon_test; mod block_test; mod fwcfg_test; -#[cfg(target_arch = "aarch64")] -mod ged_test; mod memory_test; mod net_test; mod pci_test; -#[cfg(target_arch = "aarch64")] -mod pl031_test; -#[cfg(target_arch = "aarch64")] -mod ramfb_test; mod rng_test; mod scream_test; mod scsi_test; -- Gitee From 20d1bc3b315c4cb2d87f5d350a08896ba72b3c32 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 14 Nov 2023 10:19:39 +0800 Subject: [PATCH 1443/2187] virtio: introduce get_avail_bytes() for VringOps trait This patch introduces get_avail_bytes() for VringOps trait. Later virtio-serial will use it. Signed-off-by: Zhao Yi Min --- virtio/src/queue/mod.rs | 8 ++++++++ virtio/src/queue/split.rs | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index 9445c573d..5636c7d79 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -166,6 +166,14 @@ pub trait VringOps { /// Get the region cache information of the SplitVring. fn get_cache(&self) -> &Option; + + /// Get the available bytes of the vring to read from or write to the guest + fn get_avail_bytes( + &mut self, + sys_mem: &Arc, + max_size: usize, + is_in: bool, + ) -> Result; } /// Virtio queue. diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index e608c790e..187bb30e1 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -920,6 +920,48 @@ impl VringOps for SplitVring { fn get_cache(&self) -> &Option { &self.cache } + + fn get_avail_bytes( + &mut self, + sys_mem: &Arc, + max_size: usize, + is_in: bool, + ) -> Result { + if !self.is_enabled() { + return Ok(0); + } + fence(Ordering::Acquire); + + let mut avail_bytes = 0_usize; + let mut avail_idx = self.next_avail; + let end_idx = self.get_avail_idx(sys_mem).map(Wrapping)?; + while (end_idx - avail_idx).0 > 0 { + let desc_info = self.get_desc_info(sys_mem, avail_idx, 0)?; + + let mut elem = Element::new(0); + SplitVringDesc::get_element(sys_mem, &desc_info, &mut self.cache, &mut elem).with_context( + || { + format!( + "Failed to get element from descriptor chain {}, table addr: 0x{:X}, size: {}", + desc_info.index, desc_info.table_host, desc_info.size, + ) + }, + )?; + + for e in match is_in { + true => elem.in_iovec, + false => elem.out_iovec, + } { + avail_bytes += e.len as usize; + } + + if avail_bytes >= max_size { + return Ok(max_size); + } + avail_idx += Wrapping(1); + } + Ok(avail_bytes) + } } #[cfg(test)] -- Gitee From 39fcb6a4a1b3772a76a4d37ec82f691d61778957 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 16 Nov 2023 16:39:46 +0800 Subject: [PATCH 1444/2187] virtio-serial: return the available bytes of vring The func remain_size() always returns 4096 buffer size. This can trigger that the received data might be dropped. So let's return the available bytes in vring. Signed-off-by: Zhao Yi Min --- virtio/src/device/serial.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index b47ed3288..52130258e 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -513,6 +513,31 @@ impl SerialPortHandler { }; } + fn get_input_avail_bytes(&mut self, max_size: usize) -> usize { + let port = self.port.as_ref(); + if port.is_none() || !port.unwrap().lock().unwrap().guest_connected { + warn!("virtio-serial port is none or disconnected"); + return 0; + } + + if self.device_broken.load(Ordering::SeqCst) { + warn!("virtio-serial device is broken"); + return 0; + } + + let mut locked_queue = self.input_queue.lock().unwrap(); + match locked_queue + .vring + .get_avail_bytes(&self.mem_space, max_size, true) + { + Ok(n) => n, + Err(_) => { + warn!("error occurred while getting available bytes of vring"); + 0 + } + } + } + fn input_handle_internal(&mut self, buffer: &[u8]) -> Result<()> { let mut queue_lock = self.input_queue.lock().unwrap(); @@ -631,7 +656,7 @@ impl InputReceiver for SerialPortHandler { } fn remain_size(&mut self) -> usize { - BUF_SIZE + self.get_input_avail_bytes(BUF_SIZE) } } -- Gitee From 850525196c1217971df445e2532fa110a08a558f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 Nov 2023 15:08:36 +0800 Subject: [PATCH 1445/2187] virtconsole: Allow virtconsole to use reserved 0 without configured nr If virtconsole doesn't configure nr, set the first one in command line with nr=0; Signed-off-by: Yan Wen Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- machine/src/lib.rs | 6 ++++-- machine_manager/src/config/chardev.rs | 10 +++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 70d355e5d..2f42c7953 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -565,7 +565,7 @@ redirection will be required. See [section 2.12 Chardev](#212-chardev) for detai Three properties can be set for virtconsole(console port) and virtserialport(generic port). * id: unique device-id. * chardev: char device of this console/generic port. -* nr: unique port number for this port. (optional) If set, all virtserialports and virtconsoles should set. nr = 0 is only allowed for virtconsole. +* nr: unique port number for this port. (optional) If set, all virtserialports and virtconsoles should set. nr = 0 is only allowed for virtconsole. The default nr for generic port starts from 1 and starts from 0 for console port. If not set, nr = 0 will be assigned to the first console port in the command line. And nr = 0 will be reserved if there is no console port in the command line. For virtio-serial-pci, Four more properties are required. * bus: bus number of virtio console. diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a5ad44d39..e866ed58b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -767,9 +767,11 @@ pub trait MachineOps { let mut virtio_dev_h = virtio_dev.lock().unwrap(); let serial = virtio_dev_h.as_any_mut().downcast_mut::().unwrap(); - // Note: port 0 is reserved for a virtconsole. "nr=0" should be specified to configure. + let free_port0 = find_port_by_nr(&serial.ports, 0).is_none(); + // Note: port 0 is reserved for a virtconsole. let free_nr = get_max_nr(&serial.ports) + 1; - let serialport_cfg = parse_virtserialport(vm_config, cfg_args, is_console, free_nr)?; + let serialport_cfg = + parse_virtserialport(vm_config, cfg_args, is_console, free_nr, free_port0)?; if serialport_cfg.nr >= serial.max_nr_ports { bail!( "virtio serial port nr {} should be less than virtio serial's max_nr_ports {}", diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 5bcdaf874..943de721b 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -339,6 +339,7 @@ pub fn parse_virtserialport( config_args: &str, is_console: bool, free_nr: u32, + free_port0: bool, ) -> Result { let mut cmd_parser = CmdParser::new("virtserialport"); cmd_parser.push("").push("id").push("chardev").push("nr"); @@ -352,7 +353,11 @@ pub fn parse_virtserialport( let id = cmd_parser.get_value::("id")?.with_context(|| { ConfigError::FieldIsMissing("id".to_string(), "virtserialport".to_string()) })?; - let nr = cmd_parser.get_value::("nr")?.unwrap_or(free_nr); + + let nr = cmd_parser + .get_value::("nr")? + .unwrap_or(if is_console && free_port0 { 0 } else { free_nr }); + if nr == 0 && !is_console { bail!("Port number 0 on virtio-serial devices reserved for virtconsole device."); } @@ -598,6 +603,7 @@ mod tests { "virtconsole,chardev=test_console,id=console1,nr=1", true, 0, + true, ); assert!(virt_console.is_ok()); @@ -624,6 +630,7 @@ mod tests { "virtconsole,chardev=test_console1,id=console1,nr=1", true, 0, + true, ); // test_console1 does not exist. assert!(virt_console.is_err()); @@ -663,6 +670,7 @@ mod tests { "virtconsole,chardev=test_console,id=console1,nr=1", true, 0, + true, ); assert!(virt_console.is_ok()); let console_cfg = virt_console.unwrap(); -- Gitee From 0c45a3f9933e5d21582d8c1e2f10ea27de08e0d8 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 2 Nov 2023 20:16:54 +0800 Subject: [PATCH 1446/2187] mod_test: define MACHINE_TYPE_ARG for different architectures Simple code refactoring to support mod test on x86 in the future. Signed-off-by: yezengruan --- tests/mod_test/src/libdriver/fwcfg.rs | 4 ++-- tests/mod_test/src/libdriver/usb.rs | 4 ++-- tests/mod_test/src/libdriver/virtio_block.rs | 4 ++-- tests/mod_test/src/libdriver/virtio_gpu.rs | 5 ++--- tests/mod_test/src/libdriver/virtio_rng.rs | 4 ++-- tests/mod_test/src/libdriver/vnc.rs | 5 ++--- tests/mod_test/src/libtest.rs | 4 ++++ tests/mod_test/tests/aarch64/ged_test.rs | 4 ++-- tests/mod_test/tests/aarch64/pl031_test.rs | 4 ++-- tests/mod_test/tests/balloon_test.rs | 15 +++++++-------- tests/mod_test/tests/memory_test.rs | 6 +++--- tests/mod_test/tests/net_test.rs | 4 ++-- tests/mod_test/tests/pci_test.rs | 9 ++++++--- tests/mod_test/tests/scream_test.rs | 4 ++-- tests/mod_test/tests/scsi_test.rs | 4 ++-- tests/mod_test/tests/serial_test.rs | 4 ++-- tests/mod_test/tests/virtiofs_test.rs | 5 +++-- 17 files changed, 47 insertions(+), 42 deletions(-) diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs index c26cd4d6c..7774dbded 100644 --- a/tests/mod_test/src/libdriver/fwcfg.rs +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -13,7 +13,7 @@ use std::mem; use super::malloc::GuestAllocator; -use crate::libtest::TestState; +use crate::libtest::{TestState, MACHINE_TYPE_ARG}; use crate::utils::{swap_u16, swap_u32, swap_u64}; use devices::legacy::FwCfgEntryType; #[cfg(target_arch = "aarch64")] @@ -34,7 +34,7 @@ pub struct FwCfgDmaAccess { } pub fn bios_args(base_args: &mut Vec<&str>) { - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); base_args.append(&mut args); args = "-drive file=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true" .split(' ') diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 8a18351c9..a115ee285 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -28,7 +28,7 @@ use super::{ pci_bus::TestPciBus, }; use crate::libdriver::pci::{PciMsixOps, PCI_DEVICE_ID}; -use crate::libtest::{test_init, TestState}; +use crate::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use devices::usb::{ config::*, hid::{ @@ -2321,7 +2321,7 @@ pub struct TestUsbBuilder { impl TestUsbBuilder { pub fn new() -> Self { let mut args = Vec::new(); - let machine: Vec<&str> = "-machine virt".split(' ').collect(); + let machine: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); let mut arg = machine.into_iter().map(|s| s.to_string()).collect(); args.append(&mut arg); diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index 352dd2a0a..2896a5396 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -22,7 +22,7 @@ use super::virtio_pci_modern::TestVirtioPciDev; use crate::libdriver::virtio::{ TestVringDescEntry, VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, }; -use crate::libtest::{test_init, TestState}; +use crate::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use crate::utils::ImageType; use crate::utils::{cleanup_img, create_img, TEST_IMAGE_SIZE}; use util::byte_code::ByteCode; @@ -129,7 +129,7 @@ pub fn create_blk( &ImageType::Qcow2 => "qcow2", }; - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); let blk_pci_args = format!( diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index 406f308d4..b8e941bf7 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -20,7 +20,7 @@ use super::{ }; use crate::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; use crate::libdriver::virtio_pci_modern::TestVirtioPciDev; -use crate::libtest::{test_init, TestState}; +use crate::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use util::byte_code::ByteCode; use virtio::{ VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, @@ -610,8 +610,7 @@ pub fn set_up( let mut args: Vec = Vec::new(); // vm args - let vm_args = String::from("-machine virt"); - let vm_args: Vec<&str> = vm_args[..].split(' ').collect(); + let vm_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); args.append(&mut vm_args); // log args diff --git a/tests/mod_test/src/libdriver/virtio_rng.rs b/tests/mod_test/src/libdriver/virtio_rng.rs index 75d009820..9b7520cda 100644 --- a/tests/mod_test/src/libdriver/virtio_rng.rs +++ b/tests/mod_test/src/libdriver/virtio_rng.rs @@ -16,7 +16,7 @@ use std::rc::Rc; use super::machine::TestStdMachine; use super::malloc::GuestAllocator; use super::virtio_pci_modern::TestVirtioPciDev; -use crate::libtest::{test_init, TestState}; +use crate::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; pub fn create_rng( random_file: String, @@ -31,7 +31,7 @@ pub fn create_rng( let pci_fn: u8 = 0x0; let mut extra_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); let rng_pci_args = format!( diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index 368f9a8b8..3158c5ee7 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -33,7 +33,7 @@ use super::{ }; use crate::{ libdriver::vnc::EncodingType::*, - libtest::{test_init, TestState}, + libtest::{test_init, TestState, MACHINE_TYPE_ARG}, }; const EPOLL_DEFAULT_TIMEOUT: i32 = 1000; @@ -1324,8 +1324,7 @@ pub fn set_up( ) { let mut args: Vec = Vec::new(); // vm args. - let vm_args = String::from("-machine virt"); - let vm_args: Vec<&str> = vm_args[..].split(' ').collect(); + let vm_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); let mut vm_args = vm_args.into_iter().map(|s| s.to_string()).collect(); args.append(&mut vm_args); // Log. diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 151b2aa9d..9b1f6f685 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -27,6 +27,10 @@ use serde_json::Value; use crate::utils::get_tmp_dir; const MAX_SOCKET_MSG_LENGTH: usize = 8192; +#[cfg(target_arch = "x86_64")] +pub const MACHINE_TYPE_ARG: &str = "-machine q35"; +#[cfg(target_arch = "aarch64")] +pub const MACHINE_TYPE_ARG: &str = "-machine virt"; pub struct StreamHandler { stream: UnixStream, diff --git a/tests/mod_test/tests/aarch64/ged_test.rs b/tests/mod_test/tests/aarch64/ged_test.rs index fa7b8c13b..9de204bf0 100644 --- a/tests/mod_test/tests/aarch64/ged_test.rs +++ b/tests/mod_test/tests/aarch64/ged_test.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; pub const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::Ged as usize].0; const ADD_ADDRESS: u64 = 1; @@ -29,7 +29,7 @@ fn ged_write_evt(ts: &TestState, val: u32) { } fn ged_args(base_args: &mut Vec<&str>) { - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); base_args.append(&mut args); args = "-no-shutdown".split(' ').collect(); base_args.append(&mut args); diff --git a/tests/mod_test/tests/aarch64/pl031_test.rs b/tests/mod_test/tests/aarch64/pl031_test.rs index 06807b1b5..a0b0a4340 100644 --- a/tests/mod_test/tests/aarch64/pl031_test.rs +++ b/tests/mod_test/tests/aarch64/pl031_test.rs @@ -16,7 +16,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use rand::{thread_rng, Rng}; use devices::legacy::{RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; const RTC_ADDR_BASE: u64 = 0x0901_0000; @@ -44,7 +44,7 @@ fn pl031_write_reg(ts: &TestState, reg: u64, val: u32) { } fn set_up() -> TestState { - let extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + let extra_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); test_init(extra_args) } diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index c61e40dd7..16203f8f0 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -23,7 +23,7 @@ use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::{TestVirtQueue, TestVringDescEntry, VirtioDeviceOps}; use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use util::{byte_code::ByteCode, offset_of}; const BALLOON_F_DEFLATE_ON_OOM_TEST: u32 = 2; @@ -92,12 +92,11 @@ impl VirtioBalloonTest { let mut auto_switch = String::from("false"); let mem_path = format!("-mem-path /tmp/stratovirt/hugepages"); - let mut args: Vec<&str> = "-machine".split(' ').collect(); + let mut machine_args = MACHINE_TYPE_ARG.to_string(); if shared { - args.push("virt,mem-share=on"); - } else { - args.push("virt"); + machine_args.push_str(",mem-share=on"); } + let mut args: Vec<&str> = machine_args.split(' ').collect(); extra_args.append(&mut args); let mem_args = format!("-m {}", memsize); @@ -168,7 +167,7 @@ impl VirtioBalloonTest { pub fn numa_node_new() -> Self { let mut args: Vec<&str> = Vec::new(); - let mut extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut extra_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); args.append(&mut extra_args); let cpu = 8; @@ -552,7 +551,7 @@ fn balloon_feature_001() { let pci_fn: u8 = 0x0; let mut extra_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); let mem_args = format!("-m {}", 128); @@ -599,7 +598,7 @@ fn balloon_feature_002() { let pci_fn: u8 = 0x0; let mut extra_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); let mem_args = format!("-m {}", 128); diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 7398a71cc..5a5d1d3bc 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -16,7 +16,7 @@ use serde_json::{json, Value::String as JsonString}; use mod_test::{ libdriver::{machine::TestStdMachine, malloc::GuestAllocator}, - libtest::{test_init, TestState}, + libtest::{test_init, TestState, MACHINE_TYPE_ARG}, }; pub struct MemoryTest { @@ -610,7 +610,7 @@ fn ram_readwrite_exception() { #[test] fn ram_readwrite_numa() { let mut args: Vec<&str> = Vec::new(); - let mut extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut extra_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); args.append(&mut extra_args); let cpu = 8; @@ -666,7 +666,7 @@ fn ram_readwrite_numa() { #[test] fn ram_readwrite_numa1() { let mut args: Vec<&str> = Vec::new(); - let mut extra_args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut extra_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); args.append(&mut extra_args); let cpu = 8; diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 8aa088912..aa33f02f9 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -28,7 +28,7 @@ use mod_test::libdriver::virtio::{ VIRTIO_RING_F_EVENT_IDX, VRING_DESC_F_WRITE, VRING_DESC_SIZE, }; use mod_test::libdriver::virtio_pci_modern::{TestVirtioPciDev, VirtioPciCommonCfg}; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use util::byte_code::ByteCode; use util::offset_of; @@ -413,7 +413,7 @@ pub fn create_net( let pci_fn: u8 = 0x0; let mut extra_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); let mut iothread_arg = ""; diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 5cd2454bc..d9981f8bb 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -26,7 +26,7 @@ use mod_test::libdriver::virtio_block::{ VIRTIO_BLK_T_OUT, }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use mod_test::utils::{cleanup_img, create_img, read_le_u16, ImageType, TEST_IMAGE_SIZE}; const VIRTIO_PCI_VENDOR: u16 = 0x1af4; @@ -68,7 +68,10 @@ fn fmt_demo_deves(cfg: DemoDev, num: u8) -> String { fn init_demo_dev(cfg: DemoDev, dev_num: u8) -> (Rc>, Rc>) { let mut demo_dev_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt -D /tmp/oscar.log".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); + demo_dev_args.append(&mut args); + + let mut args: Vec<&str> = "-D /tmp/oscar.log".split(' ').collect(); demo_dev_args.append(&mut args); let demo_str = fmt_demo_deves(cfg.clone(), dev_num); @@ -336,7 +339,7 @@ fn create_machine( ) { let mut extra_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); for device_arg in device_args.iter() { diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index 3f969d3b9..6a7d76c01 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -25,7 +25,7 @@ use core::time; use devices::misc::scream::{ShmemHeader, ShmemStreamFmt, ShmemStreamHeader, SCREAM_MAGIC}; use mod_test::{ libdriver::{ivshmem::TestIvshmemDev, machine::TestStdMachine}, - libtest::{test_init, TestState}, + libtest::{test_init, TestState, MACHINE_TYPE_ARG}, utils::get_rand_str, }; use util::{num_ops::read_data_u32, offset_of}; @@ -60,7 +60,7 @@ fn set_up( record_path: String, ) -> (Rc>, Rc>) { let mut extra_args: Vec<&str> = Vec::new(); - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); extra_args.append(&mut args); diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 7ca9757ed..0498adaea 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -25,7 +25,7 @@ use mod_test::libdriver::virtio::{ VIRTIO_F_BAD_FEATURE, VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, }; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use mod_test::utils::{cleanup_img, create_img, ImageType, TEST_IMAGE_SIZE}; use util::aio::{aio_probe, AioEngine}; use util::byte_code::ByteCode; @@ -592,7 +592,7 @@ fn scsi_test_init( Rc>, Rc>, ) { - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); let pci_fn = 0; let pci_slot = 0x4; diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index 47cfdc4bd..b33f31e64 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -27,7 +27,7 @@ use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libdriver::virtio::{TestVirtQueue, VirtioDeviceOps, VIRTIO_CONFIG_S_NEEDS_RESET}; use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use util::byte_code::ByteCode; const TIMEOUT_US: u64 = 15 * 1000 * 1000; @@ -402,7 +402,7 @@ impl SerialTest { } fn create_serial(ports_config: Vec, pci_slot: u8, pci_fn: u8) -> SerialTest { - let mut args: Vec<&str> = "-machine virt".split(' ').collect(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); let serial_pci_args = format!( "-device {},id=serial0,bus=pcie.0,addr={}.0", "virtio-serial-pci", pci_slot diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 2a8d521ac..91ca0cb49 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -24,7 +24,7 @@ use mod_test::libdriver::{ virtio_pci_modern::TestVirtioPciDev, virtiofs::*, }; -use mod_test::libtest::{test_init, TestState}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use mod_test::utils::get_rand_str; use util::byte_code::ByteCode; use util::offset_of; @@ -112,7 +112,8 @@ impl VirtioFsTest { let pci_slot: u8 = 0x4; let pci_fn: u8 = 0x0; - let mut args = "-D -machine virt,mem-share=on".to_string(); + let mut args = format!("-D {},mem-share=on", MACHINE_TYPE_ARG); + let mem_args = format!(" -m {}", memsize); args.push_str(&mem_args); let chardev_args = format!( -- Gitee From db2888450a66ebf95704090bb5032055ec15424b Mon Sep 17 00:00:00 2001 From: prostous Date: Mon, 2 Oct 2023 20:31:49 +0300 Subject: [PATCH 1447/2187] chardev_backend: handle input data on connection closed For chardev (socket backend) fixed input data handling on client close connection: - if there is no receiver to handle input data then drop data and close socket - otherwise read all available data from socket before close it Signed-off-by: prostous Signed-off-by: Keqian Zhu --- chardev_backend/src/chardev.rs | 80 ++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 1cfa52f04..9cd2e99a5 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -337,10 +337,41 @@ fn get_socket_notifier(chardev: Arc>) -> Option { dev.lock().unwrap().chardev_notify(ChardevStatus::Open); } - let cloned_chardev = cloned_chardev.clone(); - let inner_handler: Rc = Rc::new(move |event, _| { - if event == EventSet::IN { - let locked_chardev = cloned_chardev.lock().unwrap(); + let handling_chardev = cloned_chardev.clone(); + let close_connection = Rc::new(move || { + let mut locked_chardev = handling_chardev.lock().unwrap(); + let notify_dev = locked_chardev.dev.clone(); + locked_chardev.input = None; + locked_chardev.output = None; + locked_chardev.stream_fd = None; + info!( + "Chardev \'{}\' event, connection closed: {}", + &locked_chardev.id, connection_info + ); + drop(locked_chardev); + + if let Some(dev) = notify_dev { + dev.lock().unwrap().chardev_notify(ChardevStatus::Close); + } + + // Note: we use stream_arc variable here because we want to capture it and prolongate + // its lifetime with this notifier callback lifetime. It allows us to ensure + // that socket fd be valid until we unregister it from epoll_fd subscription. + let stream_fd = stream_arc.lock().unwrap().as_raw_fd(); + Some(gen_delete_notifiers(&[stream_fd])) + }); + + let handling_chardev = cloned_chardev.clone(); + let input_handler: Rc = Rc::new(move |event, _| { + let locked_chardev = handling_chardev.lock().unwrap(); + + let peer_disconnected = event & EventSet::HANG_UP == EventSet::HANG_UP; + if peer_disconnected && locked_chardev.receiver.is_none() { + return close_connection(); + } + + let input_ready = event & EventSet::IN == EventSet::IN; + if input_ready { if locked_chardev.receiver.is_none() { error!( "Failed to read input data from chardev \'{}\', receiver is none", @@ -359,43 +390,28 @@ fn get_socket_notifier(chardev: Arc>) -> Option { } let mut buffer = vec![0_u8; buff_size]; - if let Ok(bytes_count) = input.lock().unwrap().chr_read_raw(&mut buffer) { - locked_receiver.receive(&buffer[..bytes_count]); + let mut locked_input = input.lock().unwrap(); + if let Ok(bytes_count) = locked_input.chr_read_raw(&mut buffer) { + if bytes_count > 0 { + locked_receiver.receive(&buffer[..bytes_count]); + } else { + drop(locked_receiver); + drop(locked_input); + return close_connection(); + } } else { let os_error = std::io::Error::last_os_error(); if os_error.kind() != std::io::ErrorKind::WouldBlock { - let locked_chardev = cloned_chardev.lock().unwrap(); + let locked_chardev = handling_chardev.lock().unwrap(); error!( "Failed to read input data from chardev \'{}\', {}", &locked_chardev.id, &os_error ); } } - None - } else if event & EventSet::HANG_UP == EventSet::HANG_UP { - let mut locked_chardev = cloned_chardev.lock().unwrap(); - let notify_dev = locked_chardev.dev.clone(); - locked_chardev.input = None; - locked_chardev.output = None; - locked_chardev.stream_fd = None; - info!( - "Chardev \'{}\' event, connection closed: {}", - &locked_chardev.id, connection_info - ); - drop(locked_chardev); - - if let Some(dev) = notify_dev { - dev.lock().unwrap().chardev_notify(ChardevStatus::Close); - } - - // Note: we use stream_arc variable here because we want to capture it and prolongate - // its lifetime with this notifier callback lifetime. It allows us to ensure - // that socket fd be valid until we unregister it from epoll_fd subscription. - let stream_fd = stream_arc.lock().unwrap().as_raw_fd(); - Some(gen_delete_notifiers(&[stream_fd])) - } else { - None } + + None }); Some(vec![EventNotifier::new( @@ -403,7 +419,7 @@ fn get_socket_notifier(chardev: Arc>) -> Option { stream_fd, Some(listener_fd), EventSet::IN | EventSet::HANG_UP, - vec![inner_handler], + vec![input_handler], )]) }); -- Gitee From 80ceddaef9dbbd2ae9fb2663c3e25d388c647f9c Mon Sep 17 00:00:00 2001 From: prostous Date: Wed, 4 Oct 2023 14:00:27 +0300 Subject: [PATCH 1448/2187] devices: fixed data drop in legacy serial There was an issue that method Serial::remain_size returns capacity of a buffer instead of free space in it while method Serial::receive checks that the buffer has enough space to place data into it. Due to this inconsistency client could pass into Serial::receive method a data which size exceeds available space in serial's buffer and as a result this data be dropped. Signed-off-by: prostous Signed-off-by: Keqian Zhu --- devices/src/legacy/serial.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index a5d27ce69..3b864d5a0 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -360,7 +360,11 @@ impl InputReceiver for Serial { } fn remain_size(&mut self) -> usize { - RECEIVER_BUFF_SIZE + if (self.state.mcr & UART_MCR_LOOP == 0) && (self.rbr.len() < RECEIVER_BUFF_SIZE) { + RECEIVER_BUFF_SIZE - self.rbr.len() + } else { + 0 + } } } -- Gitee From 3489f2ff470adb5accd0e4ffce1bcc58209b0577 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 19 Nov 2023 13:01:49 +0800 Subject: [PATCH 1449/2187] virtio-serial: Change log level in get_input_avail_bytes There is log storm when guest disconnected. Signed-off-by: Keqian Zhu --- virtio/src/device/serial.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 52130258e..dd955286c 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -516,7 +516,7 @@ impl SerialPortHandler { fn get_input_avail_bytes(&mut self, max_size: usize) -> usize { let port = self.port.as_ref(); if port.is_none() || !port.unwrap().lock().unwrap().guest_connected { - warn!("virtio-serial port is none or disconnected"); + debug!("virtio-serial port is none or disconnected"); return 0; } -- Gitee From c5c51bbcadc45ea07b99a7a145446d075b15727c Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 19 Nov 2023 15:19:26 +0800 Subject: [PATCH 1450/2187] fix compile warning :unused doc comment in the stable version of rustc. Signed-off-by: Yan Wen --- devices/src/usb/keyboard.rs | 2 +- devices/src/usb/tablet.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index f46c8a4b2..447783b0f 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -79,7 +79,7 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { iInterface: 0, }, other_desc: vec![Arc::new(UsbDescOther { - /// HID descriptor + // HID descriptor data: vec![0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0], })], endpoints: vec![Arc::new(UsbDescEndpoint { diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 13d040646..764c2ec93 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -85,7 +85,7 @@ static DESC_IFACE_TABLET: Lazy> = Lazy::new(|| { iInterface: 0, }, other_desc: vec![Arc::new(UsbDescOther { - /// HID descriptor + // HID descriptor data: vec![0x09, 0x21, 0x01, 0x0, 0x0, 0x01, 0x22, 89, 0x0], })], endpoints: vec![Arc::new(UsbDescEndpoint { -- Gitee From 77562c56abfc7e918d7bf1ef4455b39e7df2e4dd Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Oct 2023 11:03:51 +0800 Subject: [PATCH 1451/2187] qmp: QmpCommand use macro define QMP command enum definition example: command("name", arguments, ..) Signed-off-by: yezengruan --- machine_manager/src/qmp/qmp_schema.rs | 480 +++++--------------------- 1 file changed, 82 insertions(+), 398 deletions(-) diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 7c6c39efc..81b15b0e7 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -53,404 +53,88 @@ impl QmpErrorClass { } } -/// A enum to store all command struct -#[derive(Debug, Clone, Serialize, Deserialize, EnumIter, EnumVariantNames, EnumString)] -#[serde(tag = "execute")] -#[serde(deny_unknown_fields)] -pub enum QmpCommand { - #[serde(rename = "qmp_capabilities")] - qmp_capabilities { - #[serde(default)] - arguments: qmp_capabilities, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - quit { - #[serde(default)] - arguments: quit, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - stop { - #[serde(default)] - arguments: stop, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - cont { - #[serde(default)] - arguments: cont, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - system_powerdown { - #[serde(default)] - arguments: system_powerdown, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - system_reset { - #[serde(default)] - arguments: system_reset, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - device_add { - arguments: Box, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - device_del { - arguments: device_del, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "chardev-add")] - chardev_add { - arguments: chardev_add, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "chardev-remove")] - chardev_remove { - arguments: chardev_remove, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - netdev_add { - arguments: Box, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - netdev_del { - arguments: netdev_del, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - cameradev_add { - arguments: cameradev_add, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - cameradev_del { - arguments: cameradev_del, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-hotpluggable-cpus")] - #[strum(serialize = "query-hotpluggable-cpus")] - query_hotpluggable_cpus { - #[serde(default)] - arguments: query_hotpluggable_cpus, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-cpus")] - #[strum(serialize = "query-cpus")] - query_cpus { - #[serde(default)] - arguments: query_cpus, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-status")] - query_status { - #[serde(default)] - arguments: query_status, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - getfd { - arguments: getfd, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "blockdev-add")] - blockdev_add { - arguments: Box, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "blockdev-del")] - blockdev_del { - arguments: blockdev_del, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "balloon")] - balloon { - #[serde(default)] - arguments: balloon, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-mem")] - query_mem { - #[serde(default)] - arguments: query_mem, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-mem-gpa")] - query_mem_gpa { - #[serde(default)] - arguments: query_mem_gpa, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-balloon")] - query_balloon { - #[serde(default)] - arguments: query_balloon, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-vnc")] - #[strum(serialize = "query-vnc")] - query_vnc { - #[serde(default)] - arguments: query_vnc, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "migrate")] - migrate { - arguments: migrate, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-migrate")] - query_migrate { - #[serde(default)] - arguments: query_migrate, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "migrate_cancel")] - cancel_migrate { - #[serde(default)] - arguments: cancel_migrate, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-version")] - query_version { - #[serde(default)] - arguments: query_version, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-commands")] - query_commands { - #[serde(default)] - arguments: query_commands, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-target")] - query_target { - #[serde(default)] - arguments: query_target, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-kvm")] - query_kvm { - #[serde(default)] - arguments: query_kvm, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-machines")] - query_machines { - #[serde(default)] - arguments: query_machines, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-events")] - #[strum(serialize = "query-events")] - query_events { - #[serde(default)] - arguments: query_events, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "qom-list-types")] - list_type { - #[serde(default)] - arguments: list_type, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "device-list-properties")] - device_list_properties { - #[serde(default)] - arguments: device_list_properties, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "block-commit")] - #[strum(serialize = "block-commit")] - block_commit { - #[serde(default)] - arguments: block_commit, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-tpm-models")] - query_tpm_models { - #[serde(default)] - arguments: query_tpm_models, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-tpm-types")] - query_tpm_types { - #[serde(default)] - arguments: query_tpm_types, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-command-line-options")] - query_command_line_options { - #[serde(default)] - arguments: query_command_line_options, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-migrate-capabilities")] - query_migrate_capabilities { - #[serde(default)] - arguments: query_migrate_capabilities, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-qmp-schema")] - query_qmp_schema { - #[serde(default)] - arguments: query_qmp_schema, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-sev-capabilities")] - query_sev_capabilities { - #[serde(default)] - arguments: query_sev_capabilities, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-chardev")] - #[strum(serialize = "query-chardev")] - query_chardev { - #[serde(default)] - arguments: query_chardev, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "qom-list")] - #[strum(serialize = "qom-list")] - qom_list { - #[serde(default)] - arguments: qom_list, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "qom_get")] - #[strum(serialize = "qom_get")] - qom_get { - #[serde(default)] - arguments: qom_get, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-block")] - #[strum(serialize = "query-block")] - query_block { - #[serde(default)] - arguments: query_block, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-named-block-nodes")] - #[strum(serialize = "query-named-block-nodes")] - query_named_block_nodes { - #[serde(default)] - arguments: query_named_block_nodes, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-blockstats")] - #[strum(serialize = "query-blockstats")] - query_blockstats { - #[serde(default)] - arguments: query_blockstats, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-block-jobs")] - #[strum(serialize = "query-block-jobs")] - query_block_jobs { - #[serde(default)] - arguments: query_block_jobs, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-gic-capabilities")] - #[strum(serialize = "query-gic-capabilities")] - query_gic_capabilities { - #[serde(default)] - arguments: query_gic_capabilities, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-iothreads")] - #[strum(serialize = "query-iothreads")] - query_iothreads { - #[serde(default)] - arguments: query_iothreads, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "update_region")] - #[strum(serialize = "update_region")] - update_region { - #[serde(default)] - arguments: update_region, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - input_event { - #[serde(default)] - arguments: input_event, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "human-monitor-command")] - human_monitor_command { - arguments: human_monitor_command, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "blockdev-snapshot-internal-sync")] - blockdev_snapshot_internal_sync { - arguments: blockdev_snapshot_internal, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "blockdev-snapshot-delete-internal-sync")] - blockdev_snapshot_delete_internal_sync { - arguments: blockdev_snapshot_internal, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, - #[serde(rename = "query-vcpu-reg")] - query_vcpu_reg { - arguments: query_vcpu_reg, - #[serde(default, skip_serializing_if = "Option::is_none")] - id: Option, - }, -} +macro_rules! define_qmp_command_enum { + ($($command:ident($name:expr, $args_type:ty $(, $serde_default:ident)?)),*) => { + /// A enum to store all command struct + #[derive(Debug, Clone, Serialize, Deserialize, EnumIter, EnumVariantNames, EnumString)] + #[serde(tag = "execute")] + #[serde(deny_unknown_fields)] + pub enum QmpCommand { + $( + #[serde(rename = $name)] + #[strum(serialize = $name)] + $command { + $(#[serde($serde_default)])? + arguments: $args_type, + #[serde(default, skip_serializing_if = "Option::is_none")] + id: Option, + }, + )* + } + }; +} + +// QMP command enum definition example: command("name", arguments, ..) +define_qmp_command_enum!( + qmp_capabilities("qmp_capabilities", qmp_capabilities, default), + quit("quit", quit, default), + stop("stop", stop, default), + cont("cont", cont, default), + system_powerdown("system_powerdown", system_powerdown, default), + system_reset("system_reset", system_reset, default), + device_add("device_add", Box), + device_del("device_del", device_del), + chardev_add("chardev-add", chardev_add), + chardev_remove("chardev-remove", chardev_remove), + netdev_add("netdev_add", Box), + netdev_del("netdev_del", netdev_del), + cameradev_add("cameradev_add", cameradev_add), + cameradev_del("cameradev_del", cameradev_del), + query_hotpluggable_cpus("query-hotpluggable-cpus", query_hotpluggable_cpus, default), + query_cpus("query-cpus", query_cpus, default), + query_status("query-status", query_status, default), + getfd("getfd", getfd), + blockdev_add("blockdev-add", Box), + blockdev_del("blockdev-del", blockdev_del), + balloon("balloon", balloon, default), + query_mem("query-mem", query_mem, default), + query_mem_gpa("query-mem-gpa", query_mem_gpa, default), + query_balloon("query-balloon", query_balloon, default), + query_vnc("query-vnc", query_vnc, default), + migrate("migrate", migrate), + query_migrate("query-migrate", query_migrate, default), + cancel_migrate("migrate_cancel", cancel_migrate, default), + query_version("query-version", query_version, default), + query_commands("query-commands", query_commands, default), + query_target("query-target", query_target, default), + query_kvm("query-kvm", query_kvm, default), + query_machines("query-machines", query_machines, default), + query_events("query-events", query_events, default), + list_type("qom-list-types", list_type, default), + device_list_properties("device-list-properties", device_list_properties, default), + block_commit("block-commit", block_commit, default), + query_tpm_models("query-tpm-models", query_tpm_models, default), + query_tpm_types("query-tpm-types", query_tpm_types, default), + query_command_line_options("query-command-line-options", query_command_line_options, default), + query_migrate_capabilities("query-migrate-capabilities", query_migrate_capabilities, default), + query_qmp_schema("query-qmp-schema", query_qmp_schema, default), + query_sev_capabilities("query-sev-capabilities", query_sev_capabilities, default), + query_chardev("query-chardev", query_chardev, default), + qom_list("qom-list", qom_list, default), + qom_get("qom-get", qom_get, default), + query_block("query-block", query_block, default), + query_named_block_nodes("query-named-block-nodes", query_named_block_nodes, default), + query_blockstats("query-blockstats", query_blockstats, default), + query_block_jobs("query-block-jobs", query_block_jobs, default), + query_gic_capabilities("query-gic-capabilities", query_gic_capabilities, default), + query_iothreads("query-iothreads", query_iothreads, default), + update_region("update_region", update_region, default), + input_event("input_event", input_event, default), + human_monitor_command("human-monitor-command", human_monitor_command), + blockdev_snapshot_internal_sync("blockdev-snapshot-internal-sync", blockdev_snapshot_internal), + blockdev_snapshot_delete_internal_sync("blockdev-snapshot-delete-internal-sync", blockdev_snapshot_internal), + query_vcpu_reg("query-vcpu-reg", query_vcpu_reg) +); /// Command trait for Deserialize and find back Response. trait Command: Serialize { -- Gitee From b464e9d9fe1cb05b863b8e17f93ffaee71921236 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 9 Oct 2023 20:16:37 +0800 Subject: [PATCH 1452/2187] qmp: generate command impl by macro Signed-off-by: yezengruan --- machine_manager/src/qmp/qmp_schema.rs | 483 ++++---------------------- 1 file changed, 65 insertions(+), 418 deletions(-) diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 81b15b0e7..b8af2a13e 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -142,6 +142,18 @@ trait Command: Serialize { fn back(self) -> Self::Res; } +macro_rules! generate_command_impl { + ($name:ident, $res_type:ty) => { + impl Command for $name { + type Res = $res_type; + + fn back(self) -> Self::Res { + Default::default() + } + } + }; +} + /// qmp_capabilities /// /// Enable QMP capabilities. @@ -155,14 +167,7 @@ trait Command: Serialize { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct qmp_capabilities {} - -impl Command for qmp_capabilities { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(qmp_capabilities, Empty); /// quit /// @@ -180,14 +185,7 @@ impl Command for qmp_capabilities { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct quit {} - -impl Command for quit { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(quit, Empty); /// stop /// @@ -202,14 +200,7 @@ impl Command for quit { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct stop {} - -impl Command for stop { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(stop, Empty); /// cont /// @@ -224,14 +215,7 @@ impl Command for stop { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct cont {} - -impl Command for cont { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(cont, Empty); /// system_powerdown /// @@ -246,14 +230,7 @@ impl Command for cont { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct system_powerdown {} - -impl Command for system_powerdown { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(system_powerdown, Empty); /// system_reset /// @@ -268,14 +245,7 @@ impl Command for system_powerdown { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct system_reset {} - -impl Command for system_reset { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(system_reset, Empty); /// device_add /// @@ -350,14 +320,7 @@ pub struct device_add { } pub type DeviceAddArgument = device_add; - -impl Command for device_add { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(device_add, Empty); /// update_region /// @@ -408,14 +371,7 @@ pub struct update_region { } pub type UpdateRegionArgument = update_region; - -impl Command for update_region { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(update_region, Empty); #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -479,14 +435,7 @@ pub struct blockdev_add { } pub type BlockDevAddArgument = blockdev_add; - -impl Command for blockdev_add { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(blockdev_add, Empty); /// netdev_add /// @@ -526,14 +475,7 @@ pub struct netdev_add { } pub type NetDevAddArgument = netdev_add; - -impl Command for netdev_add { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(netdev_add, Empty); /// cameradev_add /// @@ -560,14 +502,7 @@ pub struct cameradev_add { } pub type CameraDevAddArgument = cameradev_add; - -impl Command for cameradev_add { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(cameradev_add, Empty); #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -626,14 +561,7 @@ pub struct chardev_add { } pub type CharDevAddArgument = chardev_add; - -impl Command for chardev_add { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(chardev_add, Empty); /// chardev-remove /// @@ -658,14 +586,7 @@ impl Command for chardev_add { pub struct chardev_remove { pub id: String, } - -impl Command for chardev_remove { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(chardev_remove, Empty); /// device_del /// @@ -700,14 +621,7 @@ impl Command for chardev_remove { pub struct device_del { pub id: String, } - -impl Command for device_del { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(device_del, Empty); #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -715,14 +629,7 @@ pub struct blockdev_del { #[serde(rename = "node-name")] pub node_name: String, } - -impl Command for blockdev_del { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(blockdev_del, Empty); /// netdev_del /// @@ -747,14 +654,7 @@ impl Command for blockdev_del { pub struct netdev_del { pub id: String, } - -impl Command for netdev_del { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(netdev_del, Empty); /// cameradev_del /// @@ -779,14 +679,7 @@ impl Command for netdev_del { pub struct cameradev_del { pub id: String, } - -impl Command for cameradev_del { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(cameradev_del, Empty); /// query-hotpluggable-cpus: /// @@ -814,14 +707,7 @@ impl Command for cameradev_del { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct query_hotpluggable_cpus {} - -impl Command for query_hotpluggable_cpus { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_hotpluggable_cpus, Vec); #[allow(clippy::upper_case_acronyms)] #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -897,14 +783,7 @@ pub struct CpuInstanceProperties { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct query_cpus {} - -impl Command for query_cpus { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_cpus, Vec); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CpuInfoCommon { @@ -968,14 +847,7 @@ pub struct CpuInfoArm {} #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct query_status {} - -impl Command for query_status { - type Res = StatusInfo; - - fn back(self) -> StatusInfo { - Default::default() - } -} +generate_command_impl!(query_status, StatusInfo); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct StatusInfo { @@ -1038,42 +910,21 @@ pub struct migrate { #[serde(rename = "uri")] pub uri: String, } - -impl Command for migrate { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(migrate, Empty); /// query-migrate: /// /// Returns information about current migration. #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_migrate {} - -impl Command for query_migrate { - type Res = MigrationInfo; - - fn back(self) -> MigrationInfo { - Default::default() - } -} +generate_command_impl!(query_migrate, MigrationInfo); /// cancel-migrate: /// /// Cancel migrate the current VM. #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct cancel_migrate {} - -impl Command for cancel_migrate { - type Res = MigrationInfo; - - fn back(self) -> MigrationInfo { - Default::default() - } -} +generate_command_impl!(cancel_migrate, MigrationInfo); #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct MigrationInfo { @@ -1101,14 +952,7 @@ pub struct getfd { #[serde(rename = "fdname")] pub fd_name: String, } - -impl Command for getfd { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(getfd, Empty); /// Shutdown /// @@ -1245,12 +1089,7 @@ pub enum QmpEvent { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_balloon {} -impl Command for query_balloon { - type Res = BalloonInfo; - fn back(self) -> BalloonInfo { - Default::default() - } -} +generate_command_impl!(query_balloon, BalloonInfo); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct BalloonInfo { @@ -1281,12 +1120,7 @@ pub struct BalloonInfo { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_vnc {} -impl Command for query_vnc { - type Res = VncInfo; - fn back(self) -> VncInfo { - Default::default() - } -} +generate_command_impl!(query_vnc, VncInfo); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct VncInfo { @@ -1338,13 +1172,7 @@ pub struct balloon { #[serde(rename = "value")] pub value: u64, } - -impl Command for balloon { - type Res = Empty; - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(balloon, Empty); /// version: /// @@ -1358,14 +1186,7 @@ impl Command for balloon { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_version {} - -impl Command for query_version { - type Res = Version; - - fn back(self) -> Version { - Default::default() - } -} +generate_command_impl!(query_version, Version); /// Query commands: /// @@ -1392,20 +1213,13 @@ impl Command for query_version { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_commands {} +generate_command_impl!(query_commands, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct Cmd { pub name: String, } -impl Command for query_commands { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Query target: /// /// Query the target platform where the StratoVirt is running. @@ -1423,20 +1237,13 @@ impl Command for query_commands { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_target {} +generate_command_impl!(query_target, Target); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct Target { pub arch: String, } -impl Command for query_target { - type Res = Target; - - fn back(self) -> Target { - Default::default() - } -} - /// Query machines: /// /// Query machine information. @@ -1451,6 +1258,7 @@ impl Command for query_target { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_machines {} +generate_command_impl!(query_machines, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct MachineInfo { @@ -1464,14 +1272,6 @@ pub struct MachineInfo { pub deprecated: bool, } -impl Command for query_machines { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Query events: /// /// Query all events of StratoVirt. @@ -1491,14 +1291,7 @@ pub struct Events { #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_events {} - -impl Command for query_events { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_events, Vec); /// Query KVM: /// @@ -1512,6 +1305,7 @@ impl Command for query_events { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_kvm {} +generate_command_impl!(query_kvm, KvmInfo); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct KvmInfo { @@ -1519,14 +1313,6 @@ pub struct KvmInfo { pub present: bool, } -impl Command for query_kvm { - type Res = KvmInfo; - - fn back(self) -> KvmInfo { - Default::default() - } -} - /// List all Qom type. /// /// # Example @@ -1540,6 +1326,7 @@ impl Command for query_kvm { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct list_type {} +generate_command_impl!(list_type, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct TypeLists { @@ -1553,14 +1340,6 @@ impl TypeLists { } } -impl Command for list_type { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Get device list properties. /// /// # Example @@ -1573,6 +1352,7 @@ impl Command for list_type { pub struct device_list_properties { pub typename: String, } +generate_command_impl!(device_list_properties, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct DeviceProps { @@ -1581,24 +1361,9 @@ pub struct DeviceProps { pub prop_type: String, } -impl Command for device_list_properties { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct block_commit {} - -impl Command for block_commit { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(block_commit, Vec); /// Query tpm models of StratoVirt. /// @@ -1610,14 +1375,7 @@ impl Command for block_commit { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_tpm_models {} - -impl Command for query_tpm_models { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_tpm_models, Vec); /// Query target of StratoVirt. /// @@ -1629,14 +1387,7 @@ impl Command for query_tpm_models { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_tpm_types {} - -impl Command for query_tpm_types { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_tpm_types, Vec); /// Query command line options. /// @@ -1648,6 +1399,7 @@ impl Command for query_tpm_types { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_command_line_options {} +generate_command_impl!(query_command_line_options, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct CmdParameter { @@ -1663,14 +1415,6 @@ pub struct CmdLine { pub option: String, } -impl Command for query_command_line_options { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Query capabilities of migration. /// /// # Example @@ -1681,6 +1425,7 @@ impl Command for query_command_line_options { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_migrate_capabilities {} +generate_command_impl!(query_migrate_capabilities, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct MigrateCapabilities { @@ -1688,14 +1433,6 @@ pub struct MigrateCapabilities { pub capability: String, } -impl Command for query_migrate_capabilities { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Query target of StratoVirt. /// /// # Example @@ -1706,14 +1443,7 @@ impl Command for query_migrate_capabilities { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_qmp_schema {} - -impl Command for query_qmp_schema { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(query_qmp_schema, Empty); /// Query capabilities of sev. /// @@ -1725,14 +1455,7 @@ impl Command for query_qmp_schema { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_sev_capabilities {} - -impl Command for query_sev_capabilities { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(query_sev_capabilities, Empty); /// List all Qom. /// @@ -1744,6 +1467,7 @@ impl Command for query_sev_capabilities { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct qom_list {} +generate_command_impl!(qom_list, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct PropList { @@ -1752,14 +1476,6 @@ pub struct PropList { pub prop_type: String, } -impl Command for qom_list { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Query char devices. /// /// # Example @@ -1770,6 +1486,7 @@ impl Command for qom_list { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_chardev {} +generate_command_impl!(query_chardev, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct ChardevInfo { @@ -1779,14 +1496,6 @@ pub struct ChardevInfo { pub label: String, } -impl Command for query_chardev { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Get qom properties. /// /// # Example @@ -1797,14 +1506,7 @@ impl Command for query_chardev { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct qom_get {} - -impl Command for qom_get { - type Res = bool; - - fn back(self) -> bool { - Default::default() - } -} +generate_command_impl!(qom_get, bool); /// Query blocks of StratoVirt. /// @@ -1816,14 +1518,7 @@ impl Command for qom_get { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_block {} - -impl Command for query_block { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_block, Vec); /// Query named block node. /// @@ -1835,14 +1530,7 @@ impl Command for query_block { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_named_block_nodes {} - -impl Command for query_named_block_nodes { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_named_block_nodes, Vec); /// Query status of blocks. /// @@ -1854,14 +1542,7 @@ impl Command for query_named_block_nodes { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_blockstats {} - -impl Command for query_blockstats { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_blockstats, Vec); /// Query jobs of blocks. /// @@ -1873,14 +1554,7 @@ impl Command for query_blockstats { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_block_jobs {} - -impl Command for query_block_jobs { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(query_block_jobs, Vec); /// Query capabilities of gic. /// @@ -1892,6 +1566,7 @@ impl Command for query_block_jobs { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_gic_capabilities {} +generate_command_impl!(query_gic_capabilities, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct GicCap { @@ -1900,14 +1575,6 @@ pub struct GicCap { kernel: bool, } -impl Command for query_gic_capabilities { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} - /// Query information of iothreads. /// /// # Example @@ -1918,6 +1585,7 @@ impl Command for query_gic_capabilities { /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_iothreads {} +generate_command_impl!(query_iothreads, Vec); #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct IothreadInfo { @@ -1932,13 +1600,6 @@ pub struct IothreadInfo { pub id: String, } -impl Command for query_iothreads { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} /// input_event /// /// # Arguments @@ -1958,14 +1619,7 @@ pub struct input_event { pub key: String, pub value: String, } - -impl Command for input_event { - type Res = Vec; - - fn back(self) -> Vec { - Default::default() - } -} +generate_command_impl!(input_event, Vec); /// human-monitor-command /// @@ -2072,13 +1726,7 @@ pub struct SnapshotInfo { #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct query_mem {} -impl Command for query_mem { - type Res = Empty; - - fn back(self) -> Empty { - Default::default() - } -} +generate_command_impl!(query_mem, Empty); /// query-vcpu-reg /// @@ -2119,7 +1767,6 @@ pub struct query_mem_gpa { #[serde(rename = "gpa")] pub gpa: String, } - pub type QueryMemGpaArgument = query_mem_gpa; #[cfg(test)] -- Gitee From b7b375755c2e016aa28025e8a703bb8e730888ea Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 13 Oct 2023 17:06:06 +0800 Subject: [PATCH 1453/2187] qmp: QmpEvent use macro define QMP event enum definition example: event("name", data, ..) Signed-off-by: yezengruan --- machine_manager/src/qmp/qmp_schema.rs | 228 ++++++++++++-------------- 1 file changed, 109 insertions(+), 119 deletions(-) diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index b8af2a13e..7ebe14d18 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -954,125 +954,6 @@ pub struct getfd { } generate_command_impl!(getfd, Empty); -/// Shutdown -/// -/// Emitted when the virtual machine has shut down, indicating that StratoVirt is -/// about to exit. -/// -/// # Notes -/// -/// If the command-line option "-no-shutdown" has been specified, StratoVirt -/// will not exit, and a STOP event will eventually follow the SHUTDOWN event -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct Shutdown { - /// If true, the shutdown was triggered by a guest request (such as - /// a guest-initiated ACPI shutdown request or other hardware-specific - /// action) rather than a host request (such as sending StratoVirt a SIGINT). - #[serde(rename = "guest")] - pub guest: bool, - pub reason: String, -} - -/// Reset -/// -/// Emitted when the virtual machine is reset -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct Reset { - /// If true, the reset was triggered by a guest request (such as - /// a guest-initiated ACPI reboot request or other hardware-specific action - /// ) rather than a host request (such as the QMP command system_reset). - #[serde(rename = "guest")] - pub guest: bool, -} - -/// Stop -/// -/// Emitted when the virtual machine is stopped -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct Stop {} - -/// Resume -/// -/// Emitted when the virtual machine resumes execution -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct Resume {} - -/// Powerdown -/// -/// Emitted when the virtual machine powerdown execution -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct Powerdown {} - -/// DeviceDeleted -/// -/// Emitted whenever the device removal completion is acknowledged by the guest. -/// At this point, it's safe to reuse the specified device ID. Device removal can -/// be initiated by the guest or by HMP/QMP commands. -/// -/// # Examples -/// -/// ```text -/// <- { "event": "DEVICE_DELETED", -/// "data": { "device": "virtio-net-mmio-0", -/// "path": "/machine/peripheral/virtio-net-mmio-0" }, -/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -/// ``` -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct DeviceDeleted { - /// Device name. - #[serde(rename = "device", default, skip_serializing_if = "Option::is_none")] - pub device: Option, - /// Device path. - #[serde(rename = "path")] - pub path: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, EnumIter, EnumVariantNames, EnumString)] -#[serde(tag = "event")] -pub enum QmpEvent { - #[serde(rename = "SHUTDOWN")] - Shutdown { - data: Shutdown, - timestamp: TimeStamp, - }, - #[serde(rename = "RESET")] - Reset { data: Reset, timestamp: TimeStamp }, - #[serde(rename = "STOP")] - Stop { - #[serde(default)] - data: Stop, - timestamp: TimeStamp, - }, - #[serde(rename = "RESUME")] - Resume { - #[serde(default)] - data: Resume, - timestamp: TimeStamp, - }, - #[serde(rename = "POWERDOWN")] - Powerdown { - #[serde(default)] - data: Powerdown, - timestamp: TimeStamp, - }, - #[serde(rename = "DEVICE_DELETED")] - DeviceDeleted { - data: DeviceDeleted, - timestamp: TimeStamp, - }, - #[serde(rename = "BALLOON_CHANGED")] - BalloonChanged { - data: BalloonInfo, - timestamp: TimeStamp, - }, -} - /// query-balloon: /// /// Query the actual size of memory of VM. @@ -1769,6 +1650,115 @@ pub struct query_mem_gpa { } pub type QueryMemGpaArgument = query_mem_gpa; +macro_rules! define_qmp_event_enum { + ($($event:ident($name:expr, $data_type:ty $(, $serde_default:ident)?)),*) => { + /// A enum to store all event struct + #[derive(Debug, Clone, Serialize, Deserialize, EnumIter, EnumVariantNames, EnumString)] + #[serde(tag = "event")] + pub enum QmpEvent { + $( + #[serde(rename = $name)] + #[strum(serialize = $name)] + $event { + $(#[serde($serde_default)])? + data: $data_type, + timestamp: TimeStamp, + }, + )* + } + }; +} + +// QMP event enum definition example: event("name", data, ..) +define_qmp_event_enum!( + Shutdown("SHUTDOWN", Shutdown), + Reset("RESET", Reset), + Stop("STOP", Stop, default), + Resume("RESUME", Resume, default), + Powerdown("POWERDOWN", Powerdown, default), + DeviceDeleted("DEVICE_DELETED", DeviceDeleted), + BalloonChanged("BALLOON_CHANGED", BalloonInfo) +); + +/// Shutdown +/// +/// Emitted when the virtual machine has shut down, indicating that StratoVirt is +/// about to exit. +/// +/// # Notes +/// +/// If the command-line option "-no-shutdown" has been specified, StratoVirt +/// will not exit, and a STOP event will eventually follow the SHUTDOWN event +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct Shutdown { + /// If true, the shutdown was triggered by a guest request (such as + /// a guest-initiated ACPI shutdown request or other hardware-specific + /// action) rather than a host request (such as sending StratoVirt a SIGINT). + #[serde(rename = "guest")] + pub guest: bool, + pub reason: String, +} + +/// Reset +/// +/// Emitted when the virtual machine is reset +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct Reset { + /// If true, the reset was triggered by a guest request (such as + /// a guest-initiated ACPI reboot request or other hardware-specific action + /// ) rather than a host request (such as the QMP command system_reset). + #[serde(rename = "guest")] + pub guest: bool, +} + +/// Stop +/// +/// Emitted when the virtual machine is stopped +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct Stop {} + +/// Resume +/// +/// Emitted when the virtual machine resumes execution +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct Resume {} + +/// Powerdown +/// +/// Emitted when the virtual machine powerdown execution +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct Powerdown {} + +/// DeviceDeleted +/// +/// Emitted whenever the device removal completion is acknowledged by the guest. +/// At this point, it's safe to reuse the specified device ID. Device removal can +/// be initiated by the guest or by HMP/QMP commands. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "DEVICE_DELETED", +/// "data": { "device": "virtio-net-mmio-0", +/// "path": "/machine/peripheral/virtio-net-mmio-0" }, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct DeviceDeleted { + /// Device name. + #[serde(rename = "device", default, skip_serializing_if = "Option::is_none")] + pub device: Option, + /// Device path. + #[serde(rename = "path")] + pub path: String, +} + #[cfg(test)] mod tests { use super::*; -- Gitee From bdf10a3f6f58bbf3d0df21ce8411480c9f4eec38 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 13 Oct 2023 20:05:31 +0800 Subject: [PATCH 1454/2187] docs: update comments for QMP Add some QMP comments and documentation, and unify their format. Signed-off-by: yezengruan --- docs/qmp.md | 162 +++++++---- machine_manager/src/qmp/qmp_schema.rs | 388 ++++++++++++++++---------- 2 files changed, 350 insertions(+), 200 deletions(-) diff --git a/docs/qmp.md b/docs/qmp.md index da806102e..ee4e8ec98 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -63,7 +63,7 @@ $ ncat ip port Once connection is built, you will receive a `greeting` message from StratoVirt. ```json -{"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} +{ "QMP": { "version": { "StratoVirt": { "micro":1, "minor":0, "major":0 }, "package":"" }, "capabilities":[] } } ``` Now you can input QMP command to control StratoVirt. @@ -97,8 +97,8 @@ Add a block backend. #### Example ```json --> {"execute": "blockdev-add", "arguments": {"node-name": "drive-0", "file": {"driver": "file", "filename": "/path/to/block", "aio": native}, "cache": {"direct": true}, "read-only": false}} -<- {"return": {}} +-> { "execute": "blockdev-add", "arguments": { "node-name": "drive-0", "file": { "driver": "file", "filename": "/path/to/block", "aio": native }, "cache": { "direct": true }, "read-only": false } } +<- { "return": {} } ``` ### blockdev-del @@ -112,8 +112,8 @@ Remove a block backend. #### Example ```json --> {"execute": "blockdev-del", "arguments": {"node-name": "drive-0"}} -<- {"return": {}} +-> { "execute": "blockdev-del", "arguments": { "node-name": "drive-0" } } +<- { "return": {} } ``` ## Net device backend management @@ -147,8 +147,8 @@ Add a network backend. #### Example ```json --> {"execute":"netdev_add", "arguments":{"id":"net-0", "ifname":"tap0"}} -<- {"return": {}} +-> { "execute": "netdev_add", "arguments": { "id": "net-0", "ifname": "tap0" } } +<- { "return": {} } ``` ### netdev_del @@ -162,8 +162,8 @@ Remove a network backend. #### Example ```json --> {"execute": "netdev_del", "arguments": {"id": "net-0"}} -<- {"return": {}} +-> { "execute": "netdev_del", "arguments": { "id": "net-0" } } +<- { "return": {} } ``` ## Camera device backend management @@ -185,8 +185,8 @@ Add a camera backend. #### Example ```json --> {"execute":"cameradev_add", "arguments":{"id":"cam-0", "driver": "v4l2", "path":"/dev/video0"}} -<- {"return": {}} +-> { "execute": "cameradev_add", "arguments": { "id": "cam-0", "driver": "v4l2", "path": "/dev/video0" } } +<- { "return": {} } ``` ### cameradev_del @@ -204,8 +204,8 @@ Remove a camera backend. #### Example ```json --> {"execute": "cameradev_del", "arguments": {"id": "cam-0"}} -<- {"return": {}} +-> { "execute": "cameradev_del", "arguments": { "id": "cam-0" } } +<- { "return": {} } ``` ## Character device backend management @@ -230,8 +230,8 @@ Add a character device backend. #### Example ```json --> {"execute":"chardev-add", "arguments": {"id": "chardev_id", "backend": {"type": "socket", "data": {"addr": {"type": "unix", "data": {"path": "/path/to/socket"}}, "server": false}}}} -<- {"return": {}} +-> { "execute": "chardev-add", "arguments": { "id": "chardev_id", "backend": { "type": "socket", "data": { "addr": { "type": "unix", "data": { "path": "/path/to/socket" } }, "server": false } } } } +<- { "return": {} } ``` ### chardev-remove @@ -245,8 +245,8 @@ Remove a character device backend. #### Example ```json --> {"execute": "chardev-remove", "arguments": {"id": "chardev_id"}} -<- {"return": {}} +-> { "execute": "chardev-remove", "arguments": { "id": "chardev_id" } } +<- { "return": {} } ``` ## Hot plug management @@ -282,8 +282,8 @@ Add a device. #### Example ```json --> {"execute":"device_add", "arguments":{"id":"net-0", "driver":"virtio-net-mmio", "addr":"0x0"}} -<- {"return": {}} +-> { "execute": "device_add", "arguments": { "id": "net-0", "driver": "virtio-net-mmio", "addr": "0x0" } } +<- { "return": {} } ``` ### device_del @@ -301,9 +301,9 @@ Remove a device from a guest. #### Example ```json --> {"execute": "device_del", "arguments": {"id": "net-0"}} -<- {"event":"DEVICE_DELETED","data":{"device":"net-0","path":"net-0"},"timestamp":{"seconds":1614310541,"microseconds":554250}} -<- {"return": {}} +-> { "execute": "device_del", "arguments": { "id": "net-0" } } +<- { "event": "DEVICE_DELETED", "data": { "device": "net-0", "path": "net-0" }, "timestamp": { "seconds": 1614310541, "microseconds": 554250 } } +<- { "return": {} } ``` ## Lifecycle Management @@ -318,9 +318,9 @@ Stop all guest VCPUs execution. #### Example ```json --> {"execute":"stop"} -<- {"event":"STOP","data":{},"timestamp":{"seconds":1583908726,"microseconds":162739}} -<- {"return":{}} +-> { "execute": "stop" } +<- { "event": "STOP", "data": {}, "timestamp": { "seconds": 1583908726, "microseconds": 162739 } } +<- { "return": {} } ``` ### cont @@ -330,9 +330,9 @@ Resume all guest VCPUs execution. #### Example ```json --> {"execute":"cont"} -<- {"event":"RESUME","data":{},"timestamp":{"seconds":1583908853,"microseconds":411394}} -<- {"return":{}} +-> { "execute": "cont" } +<- { "event": "RESUME", "data": {}, "timestamp": { "seconds": 1583908853, "microseconds": 411394 } } +<- { "return": {} } ``` ### system_reset @@ -342,21 +342,21 @@ Reset all guest VCPUs execution. #### Example ```json --> {"execute":"system_reset"} -<- {"return":{}} -<- {"event":"RESET","data":{"guest":true},"timestamp":{"seconds":1677381086,"microseconds":432033}} +-> { "execute": "system_reset" } +<- { "return": {} } +<- { "event": "RESET", "data": { "guest": true }, "timestamp": { "seconds": 1677381086, "microseconds": 432033 } } ``` ### system_powerdown Requests that a guest perform a powerdown operation. -### Example +#### Example ```json --> {"execute":"system_powerdown"} -<- {"return":{}} -<- {"event":"POWERDOWN","data":{},"timestamp":{"seconds":1677850193,"microseconds":617907}} +-> { "execute": "system_powerdown" } +<- { "return": {} } +<- { "event": "POWERDOWN", "data": {}, "timestamp": { "seconds": 1677850193, "microseconds": 617907 } } ``` ### quit @@ -366,9 +366,9 @@ This command will cause StratoVirt process to exit gracefully. #### Example ```json --> {"execute":"quit"} -<- {"return":{}} -<- {"event":"SHUTDOWN","data":{"guest":false,"reason":"host-qmp-quit"},"timestamp":{"ds":1590563776,"microseconds":519808}} +-> { "execute": "quit" } +<- { "return": {} } +<- { "event": "SHUTDOWN", "data": { "guest": false, "reason": "host-qmp-quit" }, "timestamp": { "ds": 1590563776, "microseconds": 519808 } } ``` ### query-status @@ -378,22 +378,11 @@ Query the running status of all VCPUs. #### Example ```json --> { "execute": "query-status" } -<- { "return": { "running": true,"singlestep": false,"status": "running" } } +-> {"execute": "query-status"} +<- {"return": { "running": true,"singlestep": false,"status": "running"}} ``` -### getfd - -Receive a file descriptor via SCM rights and assign it a name. - -#### Example - -```json --> { "execute": "getfd", "arguments": { "fdname": "fd1" } } -<- { "return": {} } -``` - -## balloon +## Balloon device backend management With QMP command you can set target memory size of guest and get memory size of guest. @@ -409,7 +398,7 @@ Set target memory size of guest. ```json -> { "execute": "balloon", "arguments": { "value": 2147483648 } } -<- {"return":{}} +<- { "return": {} } ``` ### query-balloon @@ -420,7 +409,7 @@ Get memory size of guest. ```json -> { "execute": "query-balloon" } -<- {"return":{"actual":2147483648}} +<- { "return": { "actual": 2147483648 } } ``` ## Migration @@ -436,8 +425,8 @@ Take a snapshot of the VM into the specified directory. #### Example ```json --> {"execute":"migrate", "arguments":{"uri":"file:path/to/template"}} -<- {"return":{}} +-> { "execute": "migrate", "arguments": { "uri": "file:path/to/template" } } +<- { "return": {} } ``` ### query-migrate @@ -457,8 +446,42 @@ Now there are 5 states during snapshot: #### Example ```json --> {"execute":"query-migrate"} -<- {"return":{"status":"completed"}} +-> { "execute": "query-migrate" } +<- { "return": { "status": "completed" } } +``` + +## Snapshot + +### blockdev-snapshot-internal-sync + +Create disk internal snapshot. + +#### Arguments + +* `device` - the valid block device. +* `name` - the snapshot name. + +#### Example + +```json +-> { "execute": "blockdev-snapshot-internal-sync", "arguments": { "device": "disk0", "name": "snapshot1" } } +<- { "return": {} } +``` + +### blockdev-snapshot-delete-internal-sync + +Delete disk internal snapshot. + +#### Arguments + +* `device` - the valid block device. +* `name` - the snapshot name. + +#### Example + +```json +-> { "execute": "blockdev-snapshot-delete-internal-sync", "arguments": { "device": "disk0", "name": "snapshot1" } } +<- { "return": { "id": "1", "name": "snapshot0", "vm-state-size": 0, "date-sec": 1000012, "date-nsec": 10, "vm-clock-sec": 100, vm-clock-nsec": 20, "icount": 220414 } } ``` ## Debug @@ -499,11 +522,32 @@ Query the value of the guest physical address. <- {"return": "B9000001"} ``` +## Others + +### getfd + +Receive a file descriptor via SCM rights and assign it a name. + +#### Example + +```json +-> { "execute": "getfd", "arguments": { "fdname": "fd1" } } +<- { "return": {} } + ``` + ## Event Notification When some events happen, connected client will receive QMP events. -Now StratoVirt supports four events: `SHUTDOWN`, `STOP`, `RESUME`, `DEVICE_DELETED`. +Now StratoVirt supports these events: + +- `SHUTDOWN`: Emitted when the virtual machine has shut down, indicating that StratoVirt is about to exit. +- `RESET`: Emitted when the virtual machine is reset. +- `STOP`: Emitted when the virtual machine is stopped. +- `RESUME`: Emitted when the virtual machine resumes execution. +- `POWERDOWN`: Emitted when the virtual machine powerdown execution. +- `DEVICE_DELETED`: Emitted whenever the device removal completion is acknowledged by the guest. +- `BALLOON_CHANGED`: Emitted when the virtual machine changes the actual BALLOON level. ## Flow control diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 7ebe14d18..879748901 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -180,7 +180,7 @@ generate_command_impl!(qmp_capabilities, Empty); /// /// ```text /// -> { "execute": "quit" } -/// <- { "return": {}} +/// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -189,7 +189,7 @@ generate_command_impl!(quit, Empty); /// stop /// -/// Stop all guest VCPU execution +/// Stop all guest VCPU execution. /// /// # Examples /// @@ -261,7 +261,7 @@ generate_command_impl!(system_reset, Empty); /// /// ```text /// -> { "execute": "device_add", -/// "arguments": { "id": "net-0", "driver": "virtio-net-mmio", "addr": "0x0"}} +/// "arguments": { "id": "net-0", "driver": "virtio-net-mmio", "addr": "0x0" } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -342,7 +342,8 @@ generate_command_impl!(device_add, Empty); /// /// ```text /// -> { "execute": "update_region", -/// "arguments": { "update_type": "add", "region_type": "io_region", "offset": 0, "size": 4096, "priority": 99 }} +/// "arguments": { "update_type": "add", "region_type": "io_region", +/// "offset": 0, "size": 4096, "priority": 99 } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -405,9 +406,9 @@ pub struct CacheOptions { /// /// ```text /// -> { "execute": "blockdev_add", -/// "arguments": {"node-name": "drive-0", -/// "file": {"driver": "file", "filename": "/path/to/block"}, -/// "cache": {"direct": true}, "read-only": false }} +/// "arguments": { "node-name": "drive-0", +/// "file": { "driver": "file", "filename": "/path/to/block" }, +/// "cache": { "direct": true }, "read-only": false } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -451,7 +452,7 @@ generate_command_impl!(blockdev_add, Empty); /// /// ```text /// -> { "execute": "netdev_add", -/// "arguments": {"id": "net-0", "ifname": "tap0", "fds": 123 }} +/// "arguments": { "id": "net-0", "ifname": "tap0", "fds": 123 } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -485,12 +486,11 @@ generate_command_impl!(netdev_add, Empty); /// * `path` - the backend camera file, eg. /dev/video0. /// * `driver` - the backend type, eg. v4l2. /// -/// /// # Examples /// /// ```text /// -> { "execute": "cameradev_add", -/// "arguments": {"id": "cam0", "driver": "v4l2", "path": "/dev/video0" }} +/// "arguments": { "id": "cam0", "driver": "v4l2", "path": "/dev/video0" } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -549,8 +549,8 @@ pub struct BackendOptions { /// ```text /// -> { "execute": "chardev-add", /// "arguments": { "id": "chardev_id", "backend": { "type": "socket", "data": { -/// "addr": { "type": "unix", "data": { "path": "/path/to/socket" } }, -/// "server": false }}}} +/// "addr": { "type": "unix", "data": { "path": "/path/to/socket" } }, +/// "server": false } } } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -573,7 +573,7 @@ generate_command_impl!(chardev_add, Empty); /// /// # Errors /// -/// If `id` is not a valid chardev backend, DeviceNotFound +/// If `id` is not a valid chardev backend, DeviceNotFound. /// /// # Examples /// @@ -612,8 +612,7 @@ generate_command_impl!(chardev_remove, Empty); /// # Examples /// /// ```text -/// -> { "execute": "device_del", -/// "arguments": { "id": "net-0" } } +/// -> { "execute": "device_del", "arguments": { "id": "net-0" } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -623,6 +622,24 @@ pub struct device_del { } generate_command_impl!(device_del, Empty); +/// blockdev-del +/// +/// Remove a block device. +/// +/// # Arguments +/// +/// * `node_name` - The name of the device node to remove. +/// +/// # Errors +/// +/// If `node_name` is not a valid device, DeviceNotFound. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "blockdev-del", "arguments": { "node-name": "node0" } } +/// <- { "return": {} } +/// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct blockdev_del { @@ -641,7 +658,7 @@ generate_command_impl!(blockdev_del, Empty); /// /// # Errors /// -/// If `id` is not a valid network backend, DeviceNotFound +/// If `id` is not a valid network backend, DeviceNotFound. /// /// # Examples /// @@ -666,7 +683,7 @@ generate_command_impl!(netdev_del, Empty); /// /// # Errors /// -/// If `id` is not a valid camera backend, DeviceNotFound +/// If `id` is not a valid camera backend, DeviceNotFound. /// /// # Examples /// @@ -681,7 +698,9 @@ pub struct cameradev_del { } generate_command_impl!(cameradev_del, Empty); -/// query-hotpluggable-cpus: +/// query-hotpluggable-cpus +/// +/// Query which CPU types could be plugged. /// /// # Returns /// @@ -692,17 +711,16 @@ generate_command_impl!(cameradev_del, Empty); /// For pc machine type started with -smp 1,maxcpus=2: /// ```text /// -> { "execute": "query-hotpluggable-cpus" } -/// <- {"return": [ +/// <- { "return": [ /// { /// "type": host-x-cpu", "vcpus-count": 1, -/// "props": {"core-id": 0, "socket-id": 1, "thread-id": 0} +/// "props": {"core-id": 0, "socket-id": 1, "thread-id": 0 } /// }, /// { /// "qom-path": "/machine/unattached/device[0]", /// "type": "host-x-cpu", "vcpus-count": 1, -/// "props": {"core-id": 0, "socket-id": 0, "thread-id": 0} -/// } -/// ]} +/// "props": { "core-id": 0, "socket-id": 0, "thread-id": 0 } +/// } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -744,7 +762,7 @@ pub struct CpuInstanceProperties { pub core_id: Option, } -/// query-cpus: +/// query-cpus /// /// This command causes vCPU threads to exit to userspace, which causes /// a small interruption to guest CPU execution. This will have a negative @@ -776,9 +794,7 @@ pub struct CpuInstanceProperties { /// "qom_path":"/machine/unattached/device[2]", /// "arch":"x86", /// "thread_id":3135 -/// } -/// ] -/// } +/// } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -840,9 +856,7 @@ pub struct CpuInfoArm {} /// /// ```text /// -> { "execute": "query-status" } -/// <- { "return": { "running": true, -/// "singlestep": false, -/// "status": "running" } } +/// <- { "return": { "running": true, "singlestep": false, "status": "running" } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -905,6 +919,13 @@ pub enum RunState { /// # Arguments /// /// * `uri` - the Uniform Resource Identifier of the destination VM or file. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } +/// <- { "return": {} } +/// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct migrate { #[serde(rename = "uri")] @@ -912,16 +933,30 @@ pub struct migrate { } generate_command_impl!(migrate, Empty); -/// query-migrate: +/// query-migrate /// /// Returns information about current migration. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "query-migrate" } +/// <- { "return": {} } +/// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_migrate {} generate_command_impl!(query_migrate, MigrationInfo); -/// cancel-migrate: +/// migrate_cancel /// /// Cancel migrate the current VM. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "migrate_cancel" } +/// <- { "return": {} } +/// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct cancel_migrate {} generate_command_impl!(cancel_migrate, MigrationInfo); @@ -934,7 +969,7 @@ pub struct MigrationInfo { /// getfd /// -/// Receive a file descriptor via SCM rights and assign it a name +/// Receive a file descriptor via SCM rights and assign it a name. /// /// # Arguments /// @@ -954,19 +989,19 @@ pub struct getfd { } generate_command_impl!(getfd, Empty); -/// query-balloon: +/// query-balloon /// /// Query the actual size of memory of VM. /// /// # Returns /// -/// `BalloonInfo` includs the actual size of memory +/// `BalloonInfo` includs the actual size of memory. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-balloon" } -/// <- {"return":{"actual":8589934592}} +/// <- { "return": { "actual": 8589934592 } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_balloon {} @@ -977,7 +1012,8 @@ pub struct BalloonInfo { pub actual: u64, } -/// query-vnc: +/// query-vnc +/// /// Information about current VNC server. /// /// # Examples @@ -985,7 +1021,7 @@ pub struct BalloonInfo { /// For pc machine type started with -vnc ip:port(for example: 0.0.0.0:0): /// ```text /// -> { "execute": "query-vnc" } -/// <- {"return": { +/// <- { "return": { /// "enabled": true, /// "host": "0.0.0.0", /// "service": "50401", @@ -995,9 +1031,7 @@ pub struct BalloonInfo { /// "host": "127.0.0.1", /// "service": "50401", /// "family": "ipv4", -/// ] -/// } -/// } +/// ] } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_vnc {} @@ -1029,7 +1063,7 @@ pub struct VncClientInfo { pub family: String, } -/// balloon: +/// balloon /// /// Advice VM to change memory size with the argument `value`. /// @@ -1039,14 +1073,14 @@ pub struct VncClientInfo { /// /// # Notes /// -/// This is only an advice instead of command to VM, -/// therefore, the VM changes its memory according to `value` and its condation. +/// This is only an advice instead of command to VM, therefore, the VM changes +/// its memory according to `value` and its condation. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "balloon", "arguments": { "value": 589934492 } } -/// <- {"return":{}} +/// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct balloon { @@ -1055,42 +1089,37 @@ pub struct balloon { } generate_command_impl!(balloon, Empty); -/// version: +/// query-version /// /// Query version of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-version" } -/// <- {"return":{"version":{"qemu":{"minor":1,"micro":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +/// <- { "return": { +/// "version": { "qemu": { "minor": 1, "micro": 0, "major": 5 }, +/// "package": "StratoVirt-2.3.0" }, +/// "capabilities": [] } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_version {} generate_command_impl!(query_version, Version); -/// Query commands: +/// query-commands /// /// Query all qmp commands of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-commands" } -/// <- {"return":[{"name":"qmp_capabilities"},{"name":"quit"},{"name":"stop"},{"name":"cont"}, -/// {"name":"system_powerdown"},{"name":"system_reset"},{"name":"device_add"},{"name":"device_del"}, -/// {"name":"chardev_add"},{"name":"chardev_remove"},{"name":"netdev_add"},{"name":"netdev_del"}, -/// {"name":"cameradev_add"},{"name":"cameradev_del"},{"name":"query-hotpluggable-cpus"}, -/// {"name":"query-cpus"},{"name":"query_status"},{"name":"getfd"},{"name":"blockdev_add"}, -/// {"name":"blockdev_del"},{"name":"balloon"},{"name":"query_balloon"},{"name":"query-vnc"}, -/// {"name":"migrate"},{"name":"query_migrate"},{"name":"cancel_migrate"},{"name":"query_version"}, -/// {"name":"query_commands"},{"name":"query_target"},{"name":"query_kvm"},{"name":"query_machines"}, -/// {"name":"query-events"},{"name":"list_type"},{"name":"device_list_properties"},{"name":"block-commit"}, -/// {"name":"query_tpm_models"},{"name":"query_tpm_types"},{"name":"query_command_line_options"}, -/// {"name":"query_migrate_capabilities"},{"name":"query_qmp_schema"},{"name":"query_sev_capabilities"}, -/// {"name":"query-chardev"},{"name":"qom-list"},{"name":"qom_get"},{"name":"query-block"},{"name":"query-named-block-nodes"}, -/// {"name":"query-blockstats"},{"name":"query-block-jobs"},{"name":"query-gic-capabilities"},{"name":"query-iothreads"}, -/// {"name":"update_region"},{"name":"input_event"},{"name":"human_monitor_command"}]} +/// <- { "return": [ { "name": "qmp_capabilities" }, { "name": "quit" }, { "name": "stop" }, +/// { "name": "cont" }, { "name": "system_powerdown" }, { "name": "system_reset" }, +/// { "name": "device_add" }, { "name": "device_del" }, { "name": "chardev_add" }, +/// { "name": "chardev_remove" }, { "name": "netdev_add" }, { "name": "netdev_del" }, +/// { "name" : "cameradev_add" }, { "name": "cameradev_del" }, +/// { "name": "query-hotpluggable-cpus" }, { "name": "query-cpus" } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_commands {} @@ -1101,20 +1130,20 @@ pub struct Cmd { pub name: String, } -/// Query target: +/// query-target /// /// Query the target platform where the StratoVirt is running. /// -/// # Example +/// # Examples /// /// ```text /// # for X86 platform. /// -> { "execute": "query-target" } -/// <- {"return":{"arch":"x86_64"}} +/// <- { "return": { "arch": "x86_64" } } /// /// # for Aarch64 platform. /// -> { "execute": "query-target" } -/// <- {"return":{"arch":"aarch64"}} +/// <- { "return": { "arch": "aarch64" } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_target {} @@ -1125,17 +1154,20 @@ pub struct Target { pub arch: String, } -/// Query machines: +/// query-machines /// /// Query machine information. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-machines" } -/// <- {"return":[{"cpu-max":255,"deprecated":false,"hotpluggable-cpus":true,"name":"none","numa-mem-supported":false}, -/// {"cpu-max":255,"deprecated":false,"hotpluggable-cpus":true,"name":"microvm","numa-mem-supported":false}, -/// {"cpu-max":255,"deprecated":false,"hotpluggable-cpus":true,"name":"standardvm","numa-mem-supported":false}]} +/// <- { "return": [ { "cpu-max": 255, "deprecated": false, "hotpluggable-cpus": true, +/// "name": "none", "numa-mem-supported": false }, +/// { "cpu-max": 255, "deprecated": false, "hotpluggable-cpus": true, +/// "name": "microvm", "numa-mem-supported": false }, +/// { "cpu-max": 255, "deprecated": false, "hotpluggable-cpus": true, +/// "name": "standardvm", "numa-mem-supported": false } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_machines {} @@ -1153,17 +1185,17 @@ pub struct MachineInfo { pub deprecated: bool, } -/// Query events: +/// query-events /// /// Query all events of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-events" } -/// <- {"return":[{"name":"Shutdown"},{"name":"Reset"}, -/// {"name":"Stop"},{"name":"Resume"},{"name":"DeviceDeleted"}, -/// {"name":"BalloonChanged"}]} +/// <- { "return": [ { "name": "Shutdown" }, { "name": "Reset" }, +/// { "name": "Stop" }, { "name": "Resume" }, { "name": "DeviceDeleted" }, +/// { "name": "BalloonChanged" } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct Events { @@ -1174,15 +1206,15 @@ pub struct Events { pub struct query_events {} generate_command_impl!(query_events, Vec); -/// Query KVM: +/// query-kvm /// /// Query if KVM is enabled. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-kvm" } -/// <- {"return":{"enabled":true,"present":true}} +/// <- { "return": { "enabled": true, "present": true } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_kvm {} @@ -1194,16 +1226,18 @@ pub struct KvmInfo { pub present: bool, } -/// List all Qom type. +/// qom-list-types /// -/// # Example +/// This command will return a list of types given search parameters. +/// +/// # Examples /// /// ```text /// -> { "execute": "qom-list-types" } -/// <- {"return":[{"name":"ioh3420","parent":"pcie-root-port-base"}, -/// {"name":"pcie-root-port","parent":"pcie-root-port-base"}, -/// {"name":"pcie-pci-bridge","parent":"base-pci-bridge"}, -/// {"name":"pci-bridge","parent":"base-pci-bridge"}]} +/// <- { "return": [ { "name": "ioh3420", "parent": "pcie-root-port-base" }, +/// { "name": "pcie-root-port", "parent": "pcie-root-port-base" }, +/// { "name": "pcie-pci-bridge", "parent": "base-pci-bridge" }, +/// { "name": "pci-bridge", "parent": "base-pci-bridge" } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct list_type {} @@ -1221,13 +1255,15 @@ impl TypeLists { } } -/// Get device list properties. +/// device-list-properties /// -/// # Example +/// List properties associated with a device. +/// +/// # Examples /// /// ```text -/// -> { "execute": "device-list-properties", "arguments": {"typename": "virtio-blk-pci"} } -/// <- {"return":[]} +/// -> { "execute": "device-list-properties", "arguments": { "typename": "virtio-blk-pci" } } +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct device_list_properties { @@ -1246,37 +1282,43 @@ pub struct DeviceProps { pub struct block_commit {} generate_command_impl!(block_commit, Vec); +/// query-tpm-models +/// /// Query tpm models of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-tpm-models" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_tpm_models {} generate_command_impl!(query_tpm_models, Vec); +/// query-tpm-types +/// /// Query target of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-tpm-types" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_tpm_types {} generate_command_impl!(query_tpm_types, Vec); +/// query-command-line-options +/// /// Query command line options. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-command-line-options" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_command_line_options {} @@ -1296,13 +1338,15 @@ pub struct CmdLine { pub option: String, } +/// query-migrate-capabilities +/// /// Query capabilities of migration. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-migrate-capabilities" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_migrate_capabilities {} @@ -1314,37 +1358,43 @@ pub struct MigrateCapabilities { pub capability: String, } +/// query-qmp-schema +/// /// Query target of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-qmp-schema" } -/// <- {"return":{}} +/// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_qmp_schema {} generate_command_impl!(query_qmp_schema, Empty); +/// query-sev-capabilities +/// /// Query capabilities of sev. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-sev-capabilities" } -/// <- {"return":{}} +/// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_sev_capabilities {} generate_command_impl!(query_sev_capabilities, Empty); +/// qom-list +/// /// List all Qom. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "qom-list" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct qom_list {} @@ -1357,13 +1407,15 @@ pub struct PropList { pub prop_type: String, } +/// query-chardev +/// /// Query char devices. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-chardev" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_chardev {} @@ -1377,73 +1429,85 @@ pub struct ChardevInfo { pub label: String, } +/// qom-get +/// /// Get qom properties. /// -/// # Example +/// # Examples /// /// ```text -/// -> { "execute": "qom_get" } -/// <- {"return":[]} +/// -> { "execute": "qom-get" } +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct qom_get {} generate_command_impl!(qom_get, bool); +/// query-block +/// /// Query blocks of StratoVirt. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-block" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_block {} generate_command_impl!(query_block, Vec); +/// query-named-block-nodes +/// /// Query named block node. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-named-block-nodes" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_named_block_nodes {} generate_command_impl!(query_named_block_nodes, Vec); +/// query-blockstats +/// /// Query status of blocks. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-blockstats" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_blockstats {} generate_command_impl!(query_blockstats, Vec); +/// query-block-jobs +/// /// Query jobs of blocks. /// -/// # Example +/// # Examples /// /// ```text -/// -> { "execute": "query-block_jobs" } -/// <- {"return":[]} +/// -> { "execute": "query-block-jobs" } +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_block_jobs {} generate_command_impl!(query_block_jobs, Vec); +/// query-gic-capabilities +/// /// Query capabilities of gic. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-gic-capabilities" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_gic_capabilities {} @@ -1456,13 +1520,15 @@ pub struct GicCap { kernel: bool, } +/// query-iothreads +/// /// Query information of iothreads. /// -/// # Example +/// # Examples /// /// ```text /// -> { "execute": "query-iothreads" } -/// <- {"return":[]} +/// <- { "return": [] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct query_iothreads {} @@ -1487,12 +1553,12 @@ pub struct IothreadInfo { /// /// * `key` - the input type such as 'keyboard' or 'pointer'. /// * `value` - the input value. - +/// /// # Examples /// /// ```text /// -> { "execute": "input_event", -/// "arguments": { "key": "pointer", "value": "100,200,1" }} +/// "arguments": { "key": "pointer", "value": "100,200,1" } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -1513,7 +1579,7 @@ generate_command_impl!(input_event, Vec); /// ```text /// -> { "execute": "human-monitor-command", /// "arguments": { "command-line": "drive_add dummy -/// file=/path/to/file,format=raw,if=none,id=drive-id" }} +/// file=/path/to/file,format=raw,if=none,id=drive-id" } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -1537,8 +1603,7 @@ pub type HumanMonitorCmdArgument = human_monitor_command; /// /// ```text /// -> { "execute": "blockdev-snapshot-internal-sync", -/// "arguments": { "device": "disk0", -/// "name": "snapshot1" }} +/// "arguments": { "device": "disk0", "name": "snapshot1" } } /// <- { "return": {} } /// ``` /// @@ -1555,8 +1620,7 @@ pub type HumanMonitorCmdArgument = human_monitor_command; /// /// ```text /// -> { "execute": "blockdev-snapshot-delete-internal-sync", -/// "arguments": { "device": "disk0", -/// "name": "snapshot1" }} +/// "arguments": { "device": "disk0", "name": "snapshot1" } } /// <- { "return": { /// "id": "1", /// "name": "snapshot0", @@ -1566,7 +1630,7 @@ pub type HumanMonitorCmdArgument = human_monitor_command; /// "vm-clock-sec": 100, /// "vm-clock-nsec": 20, /// "icount": 220414 -/// } } +/// } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -1596,13 +1660,13 @@ pub struct SnapshotInfo { /// query-mem /// -/// This command +/// Query memory address space flat. /// /// # Examples /// /// ```text /// -> { "execute": "query-mem" } -/// <- { "return": {}} +/// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -1611,6 +1675,8 @@ generate_command_impl!(query_mem, Empty); /// query-vcpu-reg /// +/// Query vcpu register value. +/// /// # Arguments /// /// * `addr` - the register addr will be query. @@ -1634,7 +1700,7 @@ pub type QueryVcpuRegArgument = query_vcpu_reg; /// query-mem-gpa /// -/// This command +/// Query the value of the guest physical address. /// /// # Examples /// @@ -1688,7 +1754,15 @@ define_qmp_event_enum!( /// # Notes /// /// If the command-line option "-no-shutdown" has been specified, StratoVirt -/// will not exit, and a STOP event will eventually follow the SHUTDOWN event +/// will not exit, and a STOP event will eventually follow the SHUTDOWN event. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "SHUTDOWN", +/// "data": { "guest": true, "reason": "guest-shutdown" }, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct Shutdown { @@ -1702,7 +1776,15 @@ pub struct Shutdown { /// Reset /// -/// Emitted when the virtual machine is reset +/// Emitted when the virtual machine is reset. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "RESET", +/// "data": { "guest": false }, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct Reset { @@ -1715,21 +1797,45 @@ pub struct Reset { /// Stop /// -/// Emitted when the virtual machine is stopped +/// Emitted when the virtual machine is stopped. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "STOP", +/// "data": {}, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct Stop {} /// Resume /// -/// Emitted when the virtual machine resumes execution +/// Emitted when the virtual machine resumes execution. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "RESUME", +/// "data": {}, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct Resume {} /// Powerdown /// -/// Emitted when the virtual machine powerdown execution +/// Emitted when the virtual machine powerdown execution. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "POWERDOWN", +/// "data": {}, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct Powerdown {} -- Gitee From b624c6becc357aefb6413ed64c9f4196ca2d0ac5 Mon Sep 17 00:00:00 2001 From: prostous Date: Tue, 3 Oct 2023 12:17:38 +0300 Subject: [PATCH 1455/2187] util: impoved EventLoopContext::modify_event Impoved util's method EventLoopContext::modify_event. Previously it supported only ability to dynamically change list of event handlers. Now it supports also ability to dynamically modify set of waiting events for specified file descriptor. Signed-off-by: prostous --- ui/src/vnc/auth_vencrypt.rs | 4 ++-- util/src/loop_context.rs | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index c5f3a845b..e907f137a 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -190,7 +190,7 @@ impl ClientIoHandler { NotifierOperation::Modify, self.stream.as_raw_fd(), None, - EventSet::IN | EventSet::READ_HANG_UP, + EventSet::empty(), handlers, )], None, @@ -213,7 +213,7 @@ impl ClientIoHandler { NotifierOperation::Modify, self.stream.as_raw_fd(), None, - EventSet::IN | EventSet::READ_HANG_UP, + EventSet::empty(), handlers, )], None, diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 5fe1263b4..01373c0c5 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -375,13 +375,24 @@ impl EventLoopContext { Ok(()) } - /// change the callback for event fn modify_event(&mut self, mut event: EventNotifier) -> Result<()> { let mut events_map = self.events.write().unwrap(); match events_map.get_mut(&event.raw_fd) { Some(notifier) => { - notifier.handlers.clear(); - notifier.handlers.append(&mut event.handlers); + let events_specified = !event.event.is_empty(); + if events_specified && event.event != notifier.event { + self.epoll.ctl( + ControlOperation::Modify, + notifier.raw_fd, + EpollEvent::new(event.event, &**notifier as *const _ as u64), + )?; + notifier.event = event.event; + } + let handlers_specified = !event.handlers.is_empty(); + if handlers_specified { + notifier.handlers.clear(); + notifier.handlers.append(&mut event.handlers); + } } _ => { return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); -- Gitee From b190ca9672cb9a507984b1b3c21e0da5394c88a7 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 19 Nov 2023 17:07:40 +0800 Subject: [PATCH 1456/2187] mod_test: remove temp file test.fd after mod test File left after mod test, let's remove it: - tests/mod_test/test.fd Signed-off-by: yezengruan --- tests/mod_test/tests/balloon_test.rs | 3 ++- tests/mod_test/tests/memory_test.rs | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 16203f8f0..f132d4456 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::cell::RefCell; -use std::fs::File; +use std::fs::{remove_file, File}; use std::io::{self, BufRead, BufReader}; use std::process::Command; use std::rc::Rc; @@ -1134,4 +1134,5 @@ fn balloon_numa1() { ); balloon.state.borrow_mut().stop(); + remove_file("test.fd").unwrap(); } diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 5a5d1d3bc..421c44368 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -10,7 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{cell::RefCell, fs::File, process::Command, rc::Rc, string::String}; +use std::cell::RefCell; +use std::fs::{remove_file, File}; +use std::process::Command; +use std::rc::Rc; +use std::string::String; use serde_json::{json, Value::String as JsonString}; @@ -30,10 +34,6 @@ const ADDRESS_BASE: u64 = 0x4000_0000; const ROM_DEV_PATH: &str = "rom_dev_file.fd"; const RAM_DEV_PATH: &str = "ram_dev_file.fd"; -fn remove_file(path: String) { - let _output = Command::new("rm").arg("-f").arg(path).output(); -} - impl MemoryTest { pub fn new( memsize: u64, @@ -290,7 +290,7 @@ fn rom_device_region_readwrite() { .borrow_mut() .memread(addr, std::mem::size_of::() as u64); assert_eq!(ret, [0x02u8; 8]); - remove_file(ROM_DEV_PATH.to_string()); + remove_file(ROM_DEV_PATH).unwrap(); // Write overflow memory_test @@ -329,7 +329,7 @@ fn rom_device_region_readwrite() { .borrow_mut() .memread(addr, std::mem::size_of::() as u64); assert_eq!(ret, [0x00u8; 8]); - remove_file(ROM_DEV_PATH.to_string()); + remove_file(ROM_DEV_PATH).unwrap(); memory_test.state.borrow_mut().stop(); } @@ -391,7 +391,7 @@ fn ram_device_region_readwrite() { .borrow_mut() .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"delete\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); - remove_file(RAM_DEV_PATH.to_string()); + remove_file(RAM_DEV_PATH).unwrap(); memory_test.state.borrow_mut().stop(); } @@ -724,6 +724,7 @@ fn ram_readwrite_numa1() { test_state.borrow_mut().qmp(&qmp_str); test_state.borrow_mut().qmp("{\"execute\": \"query-mem\"}"); - remove_file(RAM_DEV_PATH.to_string()); + remove_file(RAM_DEV_PATH).unwrap(); test_state.borrow_mut().stop(); + remove_file("test.fd").unwrap(); } -- Gitee From 0c3b639683baccf2f76cd6a0e6f353b184e60004 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sun, 19 Nov 2023 17:42:14 +0800 Subject: [PATCH 1457/2187] chardev: Fix busy-loop in input handling It is possible that input data has arrived but receiver is not ready or receiver buffer is full, so we can't handle this input data. Previously this situation handled by busy-loop when event EventSet::IN triggered constantly until we can handle it. Now instead of busy-loop approach we unsubscribe from event EventSet::IN and return this subscription after 50ms. Signed-off-by: Keqian Zhu --- chardev_backend/src/chardev.rs | 80 +++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 9cd2e99a5..4a4056513 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -12,16 +12,18 @@ use std::fs::{read_link, File, OpenOptions}; use std::io::{Stdin, Stdout}; -use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::path::PathBuf; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use std::time::Duration; use anyhow::{bail, Context, Result}; use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; -use log::{error, info}; +use log::{error, info, warn}; use vmm_sys_util::epoll::EventSet; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{PathInfo, PTY_PATH}; use machine_manager::{ config::{ChardevConfig, ChardevType}, @@ -35,6 +37,9 @@ use util::set_termi_raw_mode; use util::socket::{SocketListener, SocketStream}; use util::unix::limit_permission; +/// When the receiver is not setup or remain_size is 0, we will delay the listen. +const HANDLE_INPUT_DELAY: Duration = Duration::from_millis(50); + /// Provide the trait that helps handle the input data. pub trait InputReceiver: Send { /// Handle the input data and trigger interrupt if necessary. @@ -71,6 +76,9 @@ pub struct Chardev { receiver: Option>>, /// Used to notify device the socket is opened or closed. dev: Option>>, + /// Timer used to relisten on input stream. When the receiver + /// is not setup or remain_size is 0, we will delay the listen. + relisten_timer: Option, } impl Chardev { @@ -84,6 +92,7 @@ impl Chardev { stream_fd: None, receiver: None, dev: None, + relisten_timer: None, } } @@ -180,6 +189,45 @@ impl Chardev { pub fn set_device(&mut self, dev: Arc>) { self.dev = Some(dev.clone()); } + + fn cancel_relisten_timer(&mut self) { + let main_loop = EventLoop::get_ctx(None).unwrap(); + if let Some(timer_id) = self.relisten_timer { + main_loop.timer_del(timer_id); + self.relisten_timer = None; + } + } + + fn set_relisten_timer(&mut self, input_fd: RawFd) -> EventNotifier { + let relisten_fn = Box::new(move || { + let res = EventLoop::update_event( + vec![EventNotifier::new( + NotifierOperation::Modify, + input_fd, + None, + EventSet::IN | EventSet::HANG_UP, + vec![], + )], + None, + ); + if let Err(e) = res { + error!("Failed to relisten on fd {input_fd}: {e:?}"); + } + }); + + let main_loop = EventLoop::get_ctx(None).unwrap(); + let timer_id = main_loop.timer_add(relisten_fn, HANDLE_INPUT_DELAY); + self.cancel_relisten_timer(); + self.relisten_timer = Some(timer_id); + + EventNotifier::new( + NotifierOperation::Modify, + input_fd, + None, + EventSet::HANG_UP, + vec![], + ) + } } fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { @@ -256,14 +304,17 @@ fn get_terminal_notifier(chardev: Arc>) -> Option } let cloned_chardev = chardev.clone(); + let input_fd = input.unwrap().lock().unwrap().as_raw_fd(); + let event_handler: Rc = Rc::new(move |_, _| { - let locked_chardev = cloned_chardev.lock().unwrap(); + let mut locked_chardev = cloned_chardev.lock().unwrap(); if locked_chardev.receiver.is_none() { - error!( + warn!( "Failed to read input data from chardev \'{}\', receiver is none", &locked_chardev.id ); - return None; + let cancel_listen = locked_chardev.set_relisten_timer(input_fd); + return Some(vec![cancel_listen]); } let receiver = locked_chardev.receiver.clone().unwrap(); let input = locked_chardev.input.clone().unwrap(); @@ -272,7 +323,10 @@ fn get_terminal_notifier(chardev: Arc>) -> Option let mut locked_receiver = receiver.lock().unwrap(); let buff_size = locked_receiver.remain_size(); if buff_size == 0 { - return None; + drop(locked_receiver); + let mut locked_chardev = cloned_chardev.lock().unwrap(); + let cancel_listen = locked_chardev.set_relisten_timer(input_fd); + return Some(vec![cancel_listen]); } let mut buffer = vec![0_u8; buff_size]; @@ -289,7 +343,6 @@ fn get_terminal_notifier(chardev: Arc>) -> Option None }); - let input_fd = input.unwrap().lock().unwrap().as_raw_fd(); Some(EventNotifier::new( NotifierOperation::AddShared, input_fd, @@ -344,6 +397,7 @@ fn get_socket_notifier(chardev: Arc>) -> Option { locked_chardev.input = None; locked_chardev.output = None; locked_chardev.stream_fd = None; + locked_chardev.cancel_relisten_timer(); info!( "Chardev \'{}\' event, connection closed: {}", &locked_chardev.id, connection_info @@ -363,7 +417,7 @@ fn get_socket_notifier(chardev: Arc>) -> Option { let handling_chardev = cloned_chardev.clone(); let input_handler: Rc = Rc::new(move |event, _| { - let locked_chardev = handling_chardev.lock().unwrap(); + let mut locked_chardev = handling_chardev.lock().unwrap(); let peer_disconnected = event & EventSet::HANG_UP == EventSet::HANG_UP; if peer_disconnected && locked_chardev.receiver.is_none() { @@ -373,11 +427,12 @@ fn get_socket_notifier(chardev: Arc>) -> Option { let input_ready = event & EventSet::IN == EventSet::IN; if input_ready { if locked_chardev.receiver.is_none() { - error!( + warn!( "Failed to read input data from chardev \'{}\', receiver is none", &locked_chardev.id ); - return None; + let cancel_listen = locked_chardev.set_relisten_timer(stream_fd); + return Some(vec![cancel_listen]); } let receiver = locked_chardev.receiver.clone().unwrap(); let input = locked_chardev.input.clone().unwrap(); @@ -386,7 +441,10 @@ fn get_socket_notifier(chardev: Arc>) -> Option { let mut locked_receiver = receiver.lock().unwrap(); let buff_size = locked_receiver.remain_size(); if buff_size == 0 { - return None; + drop(locked_receiver); + let mut locked_chardev = handling_chardev.lock().unwrap(); + let cancel_listen = locked_chardev.set_relisten_timer(stream_fd); + return Some(vec![cancel_listen]); } let mut buffer = vec![0_u8; buff_size]; -- Gitee From ae1927c4c080a53828af2deedf6a06e7daaeaee2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 Nov 2023 18:19:28 +0800 Subject: [PATCH 1458/2187] virtiofs_test: Adapt to Community Edition virtiofsd 1. Adaptation of command line parameters. 2. Block test cases which have unsupported messages by using `#[ignore]`. 3. Virtiofsd currently does not support illegal size messages. Shield this type of use case and open it after virtiofsd supports it in the future. Signed-off-by: liuxiangdong --- tests/mod_test/src/libdriver/virtiofs.rs | 34 ++++++- tests/mod_test/tests/virtiofs_test.rs | 114 ++++++++++++----------- 2 files changed, 92 insertions(+), 56 deletions(-) diff --git a/tests/mod_test/src/libdriver/virtiofs.rs b/tests/mod_test/src/libdriver/virtiofs.rs index c6a95462e..2dca34fde 100644 --- a/tests/mod_test/src/libdriver/virtiofs.rs +++ b/tests/mod_test/src/libdriver/virtiofs.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::mem::size_of; + use util::byte_code::ByteCode; pub const FUSE_LOOKUP: u32 = 1; @@ -302,6 +304,10 @@ impl FuseMknodIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + size_of::() * 4 + self.name.len() + 1 + } } #[repr(C)] @@ -322,6 +328,10 @@ impl FuseRenameIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + size_of::() + self.oldname.len() + self.newname.len() + 2 + } } #[repr(C)] @@ -339,6 +349,10 @@ impl FuseLinkIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + size_of::() + self.newname.len() + 1 + } } #[repr(C)] @@ -491,6 +505,10 @@ impl FuseSetxattrIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + size_of::() * 2 + self.name.len() + self.value.len() + 2 + } } #[repr(C)] @@ -510,6 +528,10 @@ impl FuseGetxattrIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + size_of::() * 2 + self.name.len() + 1 + } } pub struct FuseRemoveXattrIn { @@ -728,6 +750,10 @@ impl FuseUnlinkrIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + self.name.len() + 1 + } } #[repr(C)] @@ -765,10 +791,14 @@ impl FuseMkdirIn { bytes.append(&mut vec![0]); bytes } + + pub fn len(&self) -> usize { + size_of::() * 2 + self.name.len() + 1 + } } pub enum SeccompAction { - Allow, + None, Kill, Log, Trap, @@ -780,7 +810,7 @@ impl std::fmt::Display for SeccompAction { f, "{}", match self { - SeccompAction::Allow => "allow", + SeccompAction::None => "none", SeccompAction::Kill => "kill", SeccompAction::Log => "log", SeccompAction::Trap => "trap", diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 91ca0cb49..cb0fa9dc5 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -236,31 +236,19 @@ impl VirtioFsTest { sandbox: Option, modcaps: Option<&str>, rlimit_nofile: Option, - logfile: bool, + xattr: bool, ) -> (String, String, String) { let binary_path = env::var("VHOST_USER_FS_BINARY").unwrap(); let (virtiofs_test_dir, virtiofs_shared_dir, virtiofs_test_file) = env_prepare(dir_temp); + let virtiofs_sock = format!("{}/virtiofs.sock", virtiofs_shared_dir); - let absolute_virtiofs_sock = format!("{}/virtiofs.sock", virtiofs_shared_dir); - - // Use shared directory as root directory when sandbox is enabled. - let virtiofs_sock = if sandbox.is_some() { - "/virtiofs.sock".to_string() - } else { - absolute_virtiofs_sock.clone() - }; - - let mut args = "-D".to_string(); - if logfile { - let filepath = format!(" {}/fs.log", virtiofs_test_dir); - args.push_str(&filepath); - } + let mut args = "--log-level info".to_string(); if seccomp.is_some() { - let seccomp_args = format!(" -seccomp {}", seccomp.unwrap()); + let seccomp_args = format!(" --seccomp {}", seccomp.unwrap()); args.push_str(&seccomp_args); } if sandbox.is_some() { - let sandbox_args = format!(" -sandbox {}", sandbox.unwrap()); + let sandbox_args = format!(" --sandbox {}", sandbox.unwrap()); args.push_str(&sandbox_args); } if modcaps.is_some() { @@ -268,23 +256,26 @@ impl VirtioFsTest { args.push_str(&modcaps_args); } if rlimit_nofile.is_some() { - let rlimit_args = format!(" -rlimit-nofile {}", rlimit_nofile.unwrap()); + let rlimit_args = format!(" --rlimit-nofile {}", rlimit_nofile.unwrap()); args.push_str(&rlimit_args); } + if xattr { + args.push_str(" --xattr"); + } let args_vec: Vec<&str> = args.trim().split(' ').collect(); Command::new(binary_path) - .arg("-source") + .arg("--shared-dir") .arg(virtiofs_shared_dir) - .arg("-socket-path") + .arg("--socket-path") .arg(virtiofs_sock.clone()) .args(args_vec) .spawn() .unwrap(); // Wait totally 10s for that the vhost user fs socket is being created. - let path = absolute_virtiofs_sock.clone(); + let path = virtiofs_sock.clone(); let sock_path = Path::new(&path); for _ in 0..100 { if sock_path.exists() { @@ -294,11 +285,7 @@ impl VirtioFsTest { std::thread::sleep(std::time::Duration::from_millis(100)); } - ( - virtiofs_test_dir, - absolute_virtiofs_sock, - virtiofs_test_file, - ) + (virtiofs_test_dir, virtiofs_sock, virtiofs_test_file) } fn vhost_user_fs_start() -> (String, String, String) { @@ -319,7 +306,6 @@ impl VirtioFsTest { let sock_path = Path::new(&path_clone); assert_eq!(sock_path.exists(), true); self.state.borrow_mut().stop(); - assert_eq!(sock_path.exists(), false); } else { self.state.borrow_mut().stop(); } @@ -451,23 +437,25 @@ fn fuse_open_dir(fs: &VirtioFsTest, nodeid: u64) -> u64 { openout.fh } +// Note: Virtiofsd doesn't support illegal size message now, so trim will only support 0 until virtiofsd modification. fn fuse_lseek( fs: &VirtioFsTest, nodeid: u64, fh: u64, trim: usize, ) -> (FuseOutHeader, FuseLseekOut) { - let len = (size_of::() + size_of::()) as u32; - let fuse_in_head = FuseInHeader::new(len, FUSE_LSEEK, 0, nodeid, 0, 0, 0, 0); let fuse_lseek_in = FuseLseekIn { fh, offset: 0, whence: SEEK_END, padding: 0, }; + let lseek_in_len = fuse_lseek_in.as_bytes().len(); + let trim_lseek_in_len = lseek_in_len - trim; let fuse_out_head = FuseOutHeader::default(); let fuse_lseek_out = FuseLseekOut::default(); - let lseek_in_len = fuse_lseek_in.as_bytes().len(); + let len = (size_of::() + trim_lseek_in_len) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_LSEEK, 0, nodeid, 0, 0, 0, 0); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( &fuse_in_head.as_bytes(), &fuse_lseek_in.as_bytes()[0..lseek_in_len - trim], @@ -513,7 +501,7 @@ fn mount_test() { true, Some(SeccompAction::Kill), Some(SandBoxMechanism::Namespace), - Some("--modcaps=-LEASE,+KILL"), + Some("--modcaps=-LEASE:+KILL"), None, false, ); @@ -535,9 +523,9 @@ fn umount_test() { let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::vhost_user_fs_start_with_config( true, - Some(SeccompAction::Allow), + Some(SeccompAction::None), Some(SandBoxMechanism::Chroot), - Some("--modcaps=-LEASE,+KILL"), + Some("--modcaps=-LEASE:+KILL"), None, false, ); @@ -567,7 +555,7 @@ fn mkdir_test() { None, None, Some(4096), // Test rlimit_nofile config. -rlimit_nofile 4096. - true, + false, ); // start vm. @@ -575,13 +563,13 @@ fn mkdir_test() { fuse_init(&fs); // do request. - let len = (size_of::() + size_of::()) as u32; - let fuse_in_head = FuseInHeader::new(len, FUSE_MKDIR, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_mkdir_in = FuseMkdirIn { mode: 0o777, // Directory right: 777. umask: 0, name: String::from("dir"), }; + let len = (size_of::() + fuse_mkdir_in.len()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_MKDIR, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let fuse_mkdir_out = FuseEntryOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( @@ -717,7 +705,9 @@ fn invalid_fuse_test() { fs.testcase_end(virtiofs_test_dir); } +// Note: Virtiofsd does not support illegal size message, block this test case. #[test] +#[ignore] fn missing_fuseinbody_fuseoutbody_virtiorequest_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = @@ -1075,11 +1065,11 @@ fn rmdir_test() { fuse_lookup(&fs, "dir".to_string()); // rmdir request. - let len = (size_of::() + size_of::()) as u32; - let fuse_in_head = FuseInHeader::new(len, FUSE_RMDIR, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_unlink_in = FuseUnlinkrIn { name: String::from("dir"), }; + let len = (size_of::() + fuse_unlink_in.len()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_RMDIR, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let fuse_unlink_out = FuseEntryOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( @@ -1207,8 +1197,10 @@ fn fallocate_test() { fs.testcase_end(virtiofs_test_dir); } +// Note: Virtiofsd does not support `GETLK` message, block this test case. // fcntl() function test. #[test] +#[ignore] fn posix_file_lock_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = @@ -1293,20 +1285,21 @@ fn mknod_test() { fuse_init(&fs); // FUSE_MKNOD. - let len = (size_of::() + size_of::()) as u32; - let fuse_in_head = FuseInHeader::new(len, FUSE_MKNOD, 4, PARENT_NODEID, 0, 0, 0, 0); - let fuse_init_in = FuseMknodIn { + let fuse_mknod_in = FuseMknodIn { mode: 0o666, // right mode 666. rdev: 0, umask: 0, padding: 0, name: String::from("node"), }; + let len = (size_of::() + fuse_mknod_in.len()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_MKNOD, 4, PARENT_NODEID, 0, 0, 0, 0); + let fuse_out_head = FuseOutHeader::default(); let fuse_init_out = FuseEntryOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( &fuse_in_head.as_bytes(), - &fuse_init_in.as_bytes(), + &fuse_mknod_in.as_bytes(), &fuse_out_head.as_bytes(), &fuse_init_out.as_bytes(), ); @@ -1555,13 +1548,13 @@ fn rename_test() { // FUSE_RENAME. Rename testfile to file. fuse_lookup(&fs, TEST_FILE_NAME.to_string()); - let len = (size_of::() + size_of::()) as u32; - let fuse_in_head = FuseInHeader::new(len, FUSE_RENAME, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_rename_in = FuseRenameIn { newdir: PARENT_NODEID, oldname: TEST_FILE_NAME.to_string(), newname: "file".to_string(), }; + let len = (size_of::() + fuse_rename_in.len()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_RENAME, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( Some(&fuse_in_head.as_bytes()), @@ -1594,12 +1587,12 @@ fn link_test() { // FUSE_LINK. let oldnodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); - let len = (size_of::() + size_of::()) as u32; - let fuse_in_head = FuseInHeader::new(len, FUSE_LINK, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_rename_in = FuseLinkIn { oldnodeid, newname: "file_link".to_string(), }; + let len = (size_of::() + fuse_rename_in.len()) as u32; + let fuse_in_head = FuseInHeader::new(len, FUSE_LINK, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let fuse_entry_out = FuseEntryOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( @@ -1740,7 +1733,7 @@ fn fuse_setxattr(fs: &VirtioFsTest, name: String, value: String, nodeid: u64) -> let len = (size_of::() + 8 + name.len() + value.len() + 2) as u32; let fuse_in_head = FuseInHeader::new(len, FUSE_SETXATTR, 4, nodeid, 0, 0, 0, 0); let fuse_setxattr_in = FuseSetxattrIn { - size: value.len() as u32, + size: value.len() as u32 + 1, flags: XATTR_CREATE, name, value, @@ -1805,7 +1798,7 @@ fn fuse_listxattr(fs: &VirtioFsTest, nodeid: u64) -> (FuseOutHeader, u64) { fn regularfile_xattr_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, false); + VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, true); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1863,7 +1856,7 @@ fn regularfile_xattr_test() { fn character_file_xattr_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, false); + VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, true); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1921,7 +1914,11 @@ fn virtio_fs_fuse_lseek_test() { assert_eq!(out_header.error, 0); // FUSE_LSEEK. - assert_ne!(fuse_lseek(&fs, nodeid, fh + 1, 1).0.error, 0); + /* + Block this test until virtiofsd support illegal size message. + + assert_ne!(fuse_lseek(&fs, nodeid, fh + 1, 1).0.error, 0); + */ assert_ne!(fuse_lseek(&fs, nodeid, fh + 1, 0).0.error, 0); assert_eq!(fuse_lseek(&fs, nodeid, fh, 0).0.error, 0); @@ -1929,6 +1926,7 @@ fn virtio_fs_fuse_lseek_test() { fs.testcase_end(virtiofs_test_dir); } +// Note: Virtiofsd doesn't support illegal size message now, so trim will only support 0 until virtiofsd modification. fn fuse_batch_forget(fs: &VirtioFsTest, nodeid: u64, trim: usize) { let len = size_of::() + size_of::() + size_of::(); @@ -1967,10 +1965,16 @@ fn virtio_fs_fuse_batch_forget_test() { let nodeid = fuse_lookup(&fs, TEST_FILE_NAME.to_string()); // FUSE_BATCH_FORGET. - // Incomplete FuseBatchForgetIn. - fuse_batch_forget(&fs, nodeid, size_of::() + 1); - // Incomplete FuseForgetDataIn. - fuse_batch_forget(&fs, nodeid, size_of::() - 1); + + /* + Block these two test until virtiofsd support illegal size message. + + // Incomplete FuseBatchForgetIn. + fuse_batch_forget(&fs, nodeid, size_of::() + 1); + // Incomplete FuseForgetDataIn. + fuse_batch_forget(&fs, nodeid, size_of::() - 1); + */ + // Normal test. fuse_batch_forget(&fs, nodeid, 0); @@ -1978,8 +1982,10 @@ fn virtio_fs_fuse_batch_forget_test() { fs.testcase_end(virtiofs_test_dir); } +// Note: Virtiofsd does not support `SETLK` and `SETLKW` message, block this test case. // flock. #[test] +#[ignore] fn virtio_fs_fuse_setlkw_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = -- Gitee From 2e5b4babe2643a8d2801b0b0fd202187bffdbcac Mon Sep 17 00:00:00 2001 From: Alisa Wang Date: Mon, 20 Nov 2023 19:12:29 +0800 Subject: [PATCH 1459/2187] Test: Use the "ip link" command instead of "brctl" to create/delete a network bridge Signed-off-by: Alisa Wang --- tests/mod_test/tests/net_test.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index aa33f02f9..2c84f86e4 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -361,7 +361,7 @@ fn execute_cmd_checked(cmd: String) { fn create_tap(id: u8, mq: bool) { let br_name = "mst_net_qbr".to_string() + &id.to_string(); let tap_name = "mst_net_qtap".to_string() + &id.to_string(); - execute_cmd_checked("brctl addbr ".to_string() + &br_name); + execute_cmd_checked("ip link add name ".to_string() + &br_name + &" type bridge".to_string()); if mq { execute_cmd_checked( "ip tuntap add ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), @@ -369,7 +369,9 @@ fn create_tap(id: u8, mq: bool) { } else { execute_cmd_checked("ip tuntap add ".to_string() + &tap_name + &" mode tap".to_string()); } - execute_cmd_checked("brctl addif ".to_string() + &br_name + &" ".to_string() + &tap_name); + execute_cmd_checked( + "ip link set ".to_string() + &tap_name + &" master ".to_string() + &br_name, + ); execute_cmd_checked("ip link set ".to_string() + &br_name + &" up".to_string()); execute_cmd_checked("ip link set ".to_string() + &tap_name + &" up".to_string()); execute_cmd_checked( @@ -394,7 +396,7 @@ fn clear_tap(id: u8, mq: bool) { } else { execute_cmd_unchecked("ip tuntap del ".to_string() + &tap_name + &" mode tap".to_string()); } - execute_cmd_unchecked("brctl delbr ".to_string() + &br_name); + execute_cmd_unchecked("ip link delete ".to_string() + &br_name + &" type bridge".to_string()); } #[allow(unused)] -- Gitee From 984bc6cd6def53b157ace0ee4968c7b278bd901b Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 20 Nov 2023 21:20:52 +0800 Subject: [PATCH 1460/2187] timer: Fix possible accident removal of timer Use memory location to locate a timer is unsafe, as the old timer may has been expired and a new timer use the same memory location, so when we delete a timer, the new timer is deleted. This commit use incremental number to identify a timer and u64 is big enough for use. Signed-off-by: Keqian Zhu --- util/src/loop_context.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 01373c0c5..7fc7915bd 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -15,7 +15,7 @@ use std::fmt; use std::fmt::Debug; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; @@ -165,6 +165,8 @@ struct Timer { func: Box, /// Given the real time when the `func` will be called. expire_time: Instant, + /// Timer id. + id: u64, } impl Timer { @@ -174,9 +176,13 @@ impl Timer { /// /// * `func` - the function will be called later. /// * `delay` - delay time to call the function. - pub fn new(func: Box, delay: Duration) -> Self { + pub fn new(func: Box, delay: Duration, id: u64) -> Self { let expire_time = get_current_time() + delay; - Timer { func, expire_time } + Timer { + func, + expire_time, + id, + } } } @@ -202,6 +208,8 @@ pub struct EventLoopContext { ready_events: Vec, /// Timer list timers: Arc>>>, + /// The next timer id to be used. + timer_next_id: AtomicU64, /// Record VM clock state. pub clock_state: Arc>, } @@ -223,6 +231,7 @@ impl EventLoopContext { gc: Arc::new(RwLock::new(Vec::new())), ready_events: vec![EpollEvent::default(); READY_EVENT_MAX], timers: Arc::new(Mutex::new(Vec::new())), + timer_next_id: AtomicU64::new(0), clock_state: Arc::new(Mutex::new(ClockState::default())), }; ctx.init_kick(); @@ -520,11 +529,12 @@ impl EventLoopContext { /// * `func` - the function will be called later. /// * `delay` - delay time. pub fn timer_add(&mut self, func: Box, delay: Duration) -> u64 { - let timer = Box::new(Timer::new(func, delay)); - let timer_id = timer.as_ref() as *const _ as u64; - // insert in order of expire_time let mut timers = self.timers.lock().unwrap(); + + let timer_id = self.timer_next_id.fetch_add(1, Ordering::SeqCst); + let timer = Box::new(Timer::new(func, delay, timer_id)); + let mut index = timers.len(); for (i, t) in timers.iter().enumerate() { if timer.expire_time < t.expire_time { @@ -542,7 +552,7 @@ impl EventLoopContext { pub fn timer_del(&mut self, timer_id: u64) { let mut timers = self.timers.lock().unwrap(); for (i, t) in timers.iter().enumerate() { - if timer_id == t.as_ref() as *const _ as u64 { + if timer_id == t.id { timers.remove(i); break; } -- Gitee From 1ccc2bbd697d6c612e7c3d042ca0a938140177be Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 11 Nov 2023 06:46:14 +0800 Subject: [PATCH 1461/2187] Refactory: reduce potential risks of unsafe 1. Reduce unsafe code blocks. 2. Modify incorrect safety comments. Signed-off-by: Xiao Ye --- chardev_backend/src/chardev.rs | 5 +- cpu/src/aarch64/mod.rs | 1 + devices/src/camera_backend/v4l2.rs | 7 +++ devices/src/usb/usbhost/host_usblib.rs | 3 +- util/src/link_list.rs | 71 ++++++++++++++------------ 5 files changed, 50 insertions(+), 37 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 1cfa52f04..5bdc97a86 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -103,8 +103,9 @@ impl Chardev { label: self.id.clone(), }; PTY_PATH.lock().unwrap().push(path_info); - // Safe because `master_arc` is the only one owner for the file descriptor. - let master_arc = unsafe { Arc::new(Mutex::new(File::from_raw_fd(master))) }; + // SAFETY: master was created in the function of set_pty_raw_mode, + // the value can be guaranteed to be legal. + let master_arc = Arc::new(Mutex::new(unsafe { File::from_raw_fd(master) })); self.input = Some(master_arc.clone()); self.output = Some(master_arc); } diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 37dbc9565..ea1dc5df8 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -298,6 +298,7 @@ impl CPU { addr: 0, flags: 0, }; + // SAFETY: the fd can be guaranteed to be legal during creation. let vcpu_device = unsafe { DeviceFd::from_raw_fd(self.fd.as_raw_fd()) }; vcpu_device .has_device_attr(&pmu_attr) diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 9b7041076..c1106a2f5 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -175,6 +175,8 @@ impl V4l2CameraBackend { if frmsize.type_ != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { continue; } + // SAFETY: there are two enumeration types for v4l2_frmivalenum__bindgen_ty_1: discrete and stepwise. + // Parsing will not result in undefined value. let width = unsafe { frmsize.__bindgen_anon_1.discrete.width }; let height = unsafe { frmsize.__bindgen_anon_1.discrete.height }; let interval_list = self.list_frame_interval(pixfmt, width, height)?; @@ -209,6 +211,8 @@ impl V4l2CameraBackend { if frame_val.type_ != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { continue; } + // SAFETY: there are two enumeration types for v4l2_frmivalenum__bindgen_ty_1: discrete and stepwise. + // Parsing will not result in undefined value. let numerator = unsafe { frame_val.__bindgen_anon_1.discrete.numerator }; let denominator = unsafe { frame_val.__bindgen_anon_1.discrete.denominator }; if denominator == 0 { @@ -259,6 +263,9 @@ impl CameraBackend for V4l2CameraBackend { let mut parm = new_init::(); parm.type_ = v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; let interval = cam_fmt.get_frame_intervals()?; + // SAFETY: there are two enumeration types for v4l2_streamparm__bindgen_ty_1: + // v4l2_captureparm and v4l2_outputparm. They have same length in memory and + // parsing will not result in undefined value. unsafe { parm.parm.capture.timeperframe.numerator = 30; parm.parm.capture.timeperframe.denominator = diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 83507513b..33d7cd920 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -240,7 +240,8 @@ extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { } extern "system" fn req_complete_iso(host_transfer: *mut libusb_transfer) { - if unsafe { (*host_transfer).user_data.is_null() } { + // SAFETY: the pointer has been verified. + if host_transfer.is_null() || unsafe { (*host_transfer).user_data.is_null() } { free_host_transfer(host_transfer); return; } diff --git a/util/src/link_list.rs b/util/src/link_list.rs index 55f91a564..264b5d6fd 100644 --- a/util/src/link_list.rs +++ b/util/src/link_list.rs @@ -57,63 +57,64 @@ impl List { pub fn add_tail(&mut self, mut node: Box>) { node.prev = self.tail; node.next = None; - unsafe { - let node = NonNull::new(Box::into_raw(node)); - if let Some(mut t) = self.tail { - t.as_mut().next = node; - } else { - self.head = node; - self.tail = node; - } + let node = NonNull::new(Box::into_raw(node)); + if let Some(mut t) = self.tail { + // SAFETY: t is guaranteed not to be null. + unsafe { t.as_mut() }.next = node; + } else { + self.head = node; self.tail = node; - self.len += 1; } + + self.tail = node; + self.len += 1; } #[inline] pub fn add_head(&mut self, mut node: Box>) { node.prev = None; node.next = self.head; - unsafe { - let node = NonNull::new(Box::into_raw(node)); - if let Some(mut h) = self.head { - h.as_mut().prev = node; - } else { - self.head = node; - self.tail = node; - } - + let node = NonNull::new(Box::into_raw(node)); + if let Some(mut h) = self.head { + // SAFETY: h is guaranteed not to be null. + unsafe { h.as_mut() }.prev = node; + } else { self.head = node; - self.len += 1; + self.tail = node; } + + self.head = node; + self.len += 1; } #[inline] pub fn unlink(&mut self, node: &Node) { - unsafe { - match node.prev { - Some(mut p) => p.as_mut().next = node.next, - None => self.head = node.next, - } + match node.prev { + // SAFETY: p is guaranteed not to be null. + Some(mut p) => unsafe { p.as_mut() }.next = node.next, + None => self.head = node.next, + } - match node.next { - Some(mut n) => n.as_mut().prev = node.prev, - None => self.tail = node.prev, - } + match node.next { + // SAFETY: n is guaranteed not to be null. + Some(mut n) => unsafe { n.as_mut() }.prev = node.prev, + None => self.tail = node.prev, } self.len -= 1; } #[inline] pub fn pop_tail(&mut self) -> Option>> { - self.tail.map(|node| unsafe { - let node = Box::from_raw(node.as_ptr()); + self.tail.map(|node| { + // SAFETY: node is guaranteed not to be null. + let node = unsafe { Box::from_raw(node.as_ptr()) }; self.tail = node.prev; match self.tail { None => self.head = None, - Some(mut t) => t.as_mut().next = None, + // SAFETY: t is guaranteed not to be null. + Some(mut t) => unsafe { t.as_mut() }.next = None, } self.len -= 1; @@ -123,13 +124,15 @@ impl List { #[inline] pub fn pop_head(&mut self) -> Option>> { - self.head.map(|node| unsafe { - let node = Box::from_raw(node.as_ptr()); + self.head.map(|node| { + // SAFETY: node is guaranteed not to be null. + let node = unsafe { Box::from_raw(node.as_ptr()) }; self.head = node.next; match self.head { None => self.tail = None, - Some(mut h) => h.as_mut().prev = None, + // SAFETY: h is guaranteed not to be null. + Some(mut h) => unsafe { h.as_mut() }.prev = None, } self.len -= 1; -- Gitee From c9fd3a0bb643bb78dd41aedb19fa002f1dfe6fba Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 11 Nov 2023 04:33:46 +0800 Subject: [PATCH 1462/2187] Refactory: modify the qmp module Add verification for size parameter of qmp command of update_region, as the invalid value may result in allocating memory beyond the limit. Signed-off-by: Xiao Ye --- address_space/src/host_mmap.rs | 4 ++++ machine/src/standard_vm/mod.rs | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 02da2f6b1..4220c70dc 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -253,12 +253,14 @@ pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Resu } else if mem_config.mem_share { let anon_mem_name = String::from("stratovirt_anon_mem"); + // SAFETY: the parameters is constant. let anon_fd = unsafe { libc::syscall(libc::SYS_memfd_create, anon_mem_name.as_ptr(), 0) } as RawFd; if anon_fd < 0 { return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } + // SAFETY: the parameters is constant. let anon_file = unsafe { File::from_raw_fd(anon_fd) }; anon_file .set_len(mem_config.mem_size) @@ -300,12 +302,14 @@ pub fn create_backend_mem(mem_config: &MemZoneConfig, thread_num: u8) -> Result< if mem_config.memfd { let anon_mem_name = String::from("stratovirt_anon_mem"); + // SAFETY: the parameters is constant. let anon_fd = unsafe { libc::syscall(libc::SYS_memfd_create, anon_mem_name.as_ptr(), 0) } as RawFd; if anon_fd < 0 { return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } + // SAFETY: the parameters is constant. let anon_file = unsafe { File::from_raw_fd(anon_fd) }; anon_file .set_len(mem_config.size) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 5fe697190..669cb83f6 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -84,6 +84,8 @@ use virtio::{ #[cfg(target_arch = "x86_64")] use x86_64::{LayoutEntryType, MEM_LAYOUT}; +const MAX_REGION_SIZE: u64 = 65536; + trait StdMachineOps: AcpiBuilder + MachineOps { fn init_pci_host(&self) -> Result<()>; @@ -1594,6 +1596,13 @@ impl DeviceInterface for StdMachine { } fn update_region(&mut self, args: UpdateRegionArgument) -> Response { + if args.size >= MAX_REGION_SIZE { + let err_resp = qmp_schema::QmpErrorClass::GenericError(format!( + "Region size {} is out of range", + args.size + )); + return Response::create_error_response(err_resp, None); + } #[derive(Default)] struct DummyDevice { head: u64, -- Gitee From acc6a5322e3b0af3ad1e4c6e38bf311426975ab3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 11 Nov 2023 01:35:30 +0800 Subject: [PATCH 1463/2187] Refactory: Optimze the using of pixman function Signed-off-by: Xiao Ye --- virtio/src/device/gpu.rs | 144 ++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index ca34c7abb..f8acf9545 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -44,7 +44,10 @@ use ui::console::{ display_replace_surface, display_set_major_screen, get_run_stage, set_run_stage, ConsoleType, DisplayConsole, DisplayMouse, DisplaySurface, HardWareOperations, VmRunningStage, }; -use ui::pixman::unref_pixman_image; +use ui::pixman::{ + create_pixman_image, get_image_data, get_image_format, get_image_height, get_image_stride, + get_image_width, ref_pixman_image, unref_pixman_image, +}; use util::aio::{iov_from_buf_direct, iov_to_buf_direct, Iovec}; use util::byte_code::ByteCode; use util::edid::EdidInfo; @@ -52,11 +55,9 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; use util::pixman::{ - pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits, pixman_image_get_data, - pixman_image_get_format, pixman_image_get_height, pixman_image_get_stride, - pixman_image_get_width, pixman_image_ref, pixman_image_set_destroy_function, pixman_image_t, - pixman_image_unref, pixman_region16_t, pixman_region_extents, pixman_region_fini, - pixman_region_init, pixman_region_init_rect, pixman_region_intersect, pixman_region_translate, + pixman_format_bpp, pixman_format_code_t, pixman_image_set_destroy_function, pixman_image_t, + pixman_region16_t, pixman_region_extents, pixman_region_fini, pixman_region_init, + pixman_region_init_rect, pixman_region_intersect, pixman_region_translate, virtio_gpu_unref_resource_callback, }; @@ -482,30 +483,33 @@ fn create_surface( res_data_offset: *mut u32, ) -> DisplaySurface { let mut surface = DisplaySurface::default(); + let rect = create_pixman_image( + pixman_format, + info_set_scanout.rect.width as i32, + info_set_scanout.rect.height as i32, + res_data_offset, + pixman_stride, + ); + ref_pixman_image(res.pixman_image); + // SAFETY: the param of create operation for image has been checked. unsafe { - let rect = pixman_image_create_bits( - pixman_format, - info_set_scanout.rect.width as i32, - info_set_scanout.rect.height as i32, - res_data_offset, - pixman_stride, - ); - pixman_image_ref(res.pixman_image); pixman_image_set_destroy_function( rect, Some(virtio_gpu_unref_resource_callback), res.pixman_image.cast(), ); - surface.format = pixman_image_get_format(rect); - surface.image = pixman_image_ref(rect); - if !surface.image.is_null() { - // update surface in scanout. - scanout.surface = Some(surface); - pixman_image_unref(rect); - display_replace_surface(&scanout.con, scanout.surface) - .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); - } - }; + } + surface.format = pixman_format; + surface.image = ref_pixman_image(rect); + + if !surface.image.is_null() { + // Update surface in scanout. + scanout.surface = Some(surface); + unref_pixman_image(rect); + display_replace_surface(&scanout.con, scanout.surface) + .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); + } + surface } @@ -699,14 +703,15 @@ impl GpuIoHandler { let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; let mse = scanout.mouse.as_mut().unwrap(); let mse_data_size = mse.data.len(); + let res_width = get_image_width(res.pixman_image); + let res_height = get_image_height(res.pixman_image); + if res_width as u32 != mse.width || res_height as u32 != mse.height { + return; + } + let res_data_ptr = get_image_data(res.pixman_image) as *mut u8; + // SAFETY: the length of the source and dest pointers can be ensured to be same, + // and equal to mse_data_size. unsafe { - let res_width = pixman_image_get_width(res.pixman_image); - let res_height = pixman_image_get_height(res.pixman_image); - if res_width as u32 != mse.width || res_height as u32 != mse.height { - return; - } - - let res_data_ptr = pixman_image_get_data(res.pixman_image) as *mut u8; ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); } @@ -883,15 +888,13 @@ impl GpuIoHandler { .filter(|&sum| sum <= self.max_hostmem) .is_some() { - res.pixman_image = unsafe { - pixman_image_create_bits( - pixman_format, - info_create_2d.width as i32, - info_create_2d.height as i32, - ptr::null_mut(), - 0, - ) - } + res.pixman_image = create_pixman_image( + pixman_format, + info_create_2d.width as i32, + info_create_2d.height as i32, + ptr::null_mut(), + 0, + ); } if res.pixman_image.is_null() { error!( @@ -983,22 +986,23 @@ impl GpuIoHandler { return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } - let pixman_format = unsafe { pixman_image_get_format(res.pixman_image) }; + let pixman_format = get_image_format(res.pixman_image); let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; - let pixman_stride = unsafe { pixman_image_get_stride(res.pixman_image) }; + let pixman_stride = get_image_stride(res.pixman_image); let offset = info_set_scanout.rect.x_coord * bpp + info_set_scanout.rect.y_coord * pixman_stride as u32; let res_data = if info_set_scanout.resource_id & VIRTIO_GPU_RES_FRAMEBUF != 0 { res.iov[0].iov_base as *mut u32 } else { - unsafe { pixman_image_get_data(res.pixman_image) } + get_image_data(res.pixman_image) }; + // SAFETY: the offset is within the legal address. let res_data_offset = unsafe { res_data.offset(offset as isize) }; // Create surface for the scanout. let scanout = &mut self.scanouts[info_set_scanout.scanout_id as usize]; if scanout.surface.is_none() - || unsafe { pixman_image_get_data(scanout.surface.unwrap().image) } != res_data_offset + || get_image_data(scanout.surface.unwrap().image) != res_data_offset || scanout.width != info_set_scanout.rect.width || scanout.height != info_set_scanout.rect.height { @@ -1059,9 +1063,10 @@ impl GpuIoHandler { return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } + let mut flush_reg = pixman_region16_t::default(); + let flush_reg_ptr = &mut flush_reg as *mut pixman_region16_t; + // SAFETY: rect information has been checked. unsafe { - let mut flush_reg = pixman_region16_t::default(); - let flush_reg_ptr = &mut flush_reg as *mut pixman_region16_t; pixman_region_init_rect( flush_reg_ptr, info_res_flush.rect.x_coord as i32, @@ -1069,17 +1074,21 @@ impl GpuIoHandler { info_res_flush.rect.width, info_res_flush.rect.height, ); - for i in 0..self.num_scanouts { - // Flushes any scanouts the resource is being used on. - if res.scanouts_bitmask & (1 << i) == 0 { - continue; - } - let scanout = &self.scanouts[i as usize]; + } - let mut rect_reg = pixman_region16_t::default(); - let mut final_reg = pixman_region16_t::default(); - let rect_reg_ptr = &mut rect_reg as *mut pixman_region16_t; - let final_reg_ptr = &mut final_reg as *mut pixman_region16_t; + for i in 0..self.num_scanouts { + // Flushes any scanouts the resource is being used on. + if res.scanouts_bitmask & (1 << i) == 0 { + continue; + } + let scanout = &self.scanouts[i as usize]; + + let mut rect_reg = pixman_region16_t::default(); + let mut final_reg = pixman_region16_t::default(); + let rect_reg_ptr = &mut rect_reg as *mut pixman_region16_t; + let final_reg_ptr = &mut final_reg as *mut pixman_region16_t; + // SAFETY: the pointer is not empty. + unsafe { pixman_region_init(final_reg_ptr); pixman_region_init_rect( rect_reg_ptr, @@ -1102,8 +1111,13 @@ impl GpuIoHandler { pixman_region_fini(rect_reg_ptr); pixman_region_fini(final_reg_ptr); } + } + + // SAFETY: it can ensured that the pointer is not empty. + unsafe { pixman_region_fini(flush_reg_ptr); } + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } @@ -1145,19 +1159,11 @@ impl GpuIoHandler { res_idx: usize, ) -> Result<()> { let res = &self.resources_list[res_idx]; - let pixman_format; - let width; - let bpp; - let stride; - let data; - // SAFETY: res.pixman_image has been checked valid. - unsafe { - pixman_format = pixman_image_get_format(res.pixman_image); - width = pixman_image_get_width(res.pixman_image) as u32; - bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; - stride = pixman_image_get_stride(res.pixman_image) as u32; - data = pixman_image_get_data(res.pixman_image).cast() as *mut u8; - } + let pixman_format = get_image_format(res.pixman_image); + let width = get_image_width(res.pixman_image) as u32; + let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + let stride = get_image_stride(res.pixman_image) as u32; + let data = get_image_data(res.pixman_image).cast() as *mut u8; // When the dedicated area is continuous. if trans_info.rect.x_coord == 0 && trans_info.rect.width == width { -- Gitee From 65ea6438f85b0065438db97656c9185878b5361d Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 4 Nov 2023 18:39:44 +0800 Subject: [PATCH 1464/2187] vhost-user-fs: Delete vhost_user_fs binary There is a new Rust implementation of virtiofsd at "https://gitlab.com/virtio-fs/virtiofsd", it's marked as stable. Signed-off-by: Keqian Zhu --- Cargo.lock | 32 - Cargo.toml | 1 - docs/config_guidebook.md | 26 +- vhost_user_fs/Cargo.toml | 25 - vhost_user_fs/src/cmdline.rs | 173 --- vhost_user_fs/src/error.rs | 32 - vhost_user_fs/src/fs.rs | 1967 ------------------------ vhost_user_fs/src/fs_ops.rs | 863 ----------- vhost_user_fs/src/fuse_msg.rs | 1004 ------------ vhost_user_fs/src/fuse_proc.rs | 1761 --------------------- vhost_user_fs/src/fuse_req.rs | 174 --- vhost_user_fs/src/main.rs | 220 --- vhost_user_fs/src/sandbox.rs | 347 ----- vhost_user_fs/src/securecomputing.rs | 151 -- vhost_user_fs/src/vhost_user_fs.rs | 169 -- vhost_user_fs/src/vhost_user_server.rs | 482 ------ vhost_user_fs/src/virtio_fs.rs | 478 ------ 17 files changed, 6 insertions(+), 7899 deletions(-) delete mode 100644 vhost_user_fs/Cargo.toml delete mode 100644 vhost_user_fs/src/cmdline.rs delete mode 100644 vhost_user_fs/src/error.rs delete mode 100644 vhost_user_fs/src/fs.rs delete mode 100644 vhost_user_fs/src/fs_ops.rs delete mode 100644 vhost_user_fs/src/fuse_msg.rs delete mode 100644 vhost_user_fs/src/fuse_proc.rs delete mode 100644 vhost_user_fs/src/fuse_req.rs delete mode 100644 vhost_user_fs/src/main.rs delete mode 100644 vhost_user_fs/src/sandbox.rs delete mode 100644 vhost_user_fs/src/securecomputing.rs delete mode 100644 vhost_user_fs/src/vhost_user_fs.rs delete mode 100644 vhost_user_fs/src/vhost_user_server.rs delete mode 100644 vhost_user_fs/src/virtio_fs.rs diff --git a/Cargo.lock b/Cargo.lock index f020351dd..9b2b4cc00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,16 +219,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "capng" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a26766f93f07f7e8b8309ed2824fa2a68f5d12d219de855e24688e9fbe89e85" -dependencies = [ - "bitflags", - "libc", -] - [[package]] name = "cc" version = "1.0.73" @@ -1806,28 +1796,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce" -[[package]] -name = "vhost_user_fs" -version = "2.3.0" -dependencies = [ - "acpi", - "address_space", - "anyhow", - "capng", - "devices", - "hypervisor", - "libc", - "log", - "machine_manager", - "migration", - "migration_derive", - "nix 0.26.2", - "thiserror", - "util", - "virtio", - "vmm-sys-util", -] - [[package]] name = "virtio" version = "2.3.0" diff --git a/Cargo.toml b/Cargo.toml index 930744436..ddff7afa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ util = { path = "util" } [workspace] members = [ - "vhost_user_fs", "ozone", "image", "tests/mod_test", diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 2f42c7953..4ebd70a10 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1059,27 +1059,13 @@ Three properties can be set for virtio fs device. ``` #### 2.17.2 vhost_user_fs -The vhost-user filesystem device contains virtio fs device and the vhost-user server which can be connected with the vhost-user client in StratoVirt through socket. - -Seven properties are supported for vhost_user_fs. -* source: Shared directory path. -* socket-path: vhost user socket path. -* rlimit-nofile: Set maximum number of file descriptors, The limit of file resources which can be opened for the process. -* D: log file path. -* seccomp: Action to take when seccomp finds a not allowed syscall (allow, kill, log, trap). - - **allow**: The seccomp filter will have no effect on the thread calling the syscall if it matches the filter rule. - - **kill**: The process will be killed by the kernel when it calls a syscall that matches the filter rule. - - **log**: The seccomp filter will have no effect on the thread calling the syscall if it matches the filter rule but the syscall will be logged. - - **trap**: The thread will throw a SIGSYS signal when it calls a syscall that matches the filter rule. -* sandbox: Sandbox mechanism to isolate the daemon process (chroot, namespace). - - **chroot**: The program invokes `chroot(2)` to make the shared directory tree its root when it does not have permission to create namespaces itself. - - **namespace**: The program invodes `pivot_root(2)` to make the shared directory tree its root. -* modcaps: Add/delete capabilities, For example, `--modcaps=-LEASE,+KILL` stands for delete CAP_LEASE, add CAP_KILL. Capabilityes list do not need prefix `CAP_`. - -*How to start vhost_user_fs process?* + +Note: The vhost_user_fs binary of StratoVirt has been removed. As there is a new Rust implementation of virtiofsd at "https://gitlab.com/virtio-fs/virtiofsd", it's marked as stable and existing project should consider to use it instead. + +*How to setup file sharing based on StratoVirt and virtiofsd?* ```shell -host# ./path/to/vhost_user_fs -source /tmp/shared -socket-path /tmp/shared/virtio_fs.sock -D +host# Setup virtiofsd server, refer to "https://gitlab.com/virtio-fs/virtiofsd/-/blob/main/README.md" host# stratovirt \ -machine type=q35,dump-guest-core=off,mem-share=on \ @@ -1091,7 +1077,7 @@ host# stratovirt \ -qmp unix:/tmp/qmp2.socket,server,nowait \ -drive id=drive_id,file=,direct=on \ -device virtio-blk-pci,drive=drive_id,bug=pcie.0,addr=1,id=blk -serial stdio -disable-seccomp \ - -chardev socket,id=virtio_fs,path=/tmp/shared/virtio_fs.sock,server,nowait \ + -chardev socket,id=virtio_fs,path=/path/to/virtiofsd.sock,server,nowait \ -device vhost-user-fs-pci,id=device_id,chardev=virtio_fs,tag=myfs,bus=pcie.0,addr=0x7 guest# mount -t virtiofs myfs /mnt diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml deleted file mode 100644 index dfb88130c..000000000 --- a/vhost_user_fs/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "vhost_user_fs" -version = "2.3.0" -authors = ["Huawei StratoVirt Team"] -edition = "2021" -license = "Mulan PSL v2" -description = "Provide virtio fs for VM" - -[dependencies] -capng = "0.2.3" -log = "0.4" -libc = "0.2" -nix = "0.26.2" -thiserror = "1.0" -anyhow = "1.0" -vmm-sys-util = "0.11.1" -address_space = { path = "../address_space" } -hypervisor = { path = "../hypervisor" } -machine_manager = { path = "../machine_manager" } -migration = { path = "../migration" } -migration_derive = { path = "../migration/migration_derive" } -util = { path = "../util" } -acpi = { path = "../acpi" } -devices = {path = "../devices"} -virtio = {path = "../virtio"} diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs deleted file mode 100644 index a8745bbff..000000000 --- a/vhost_user_fs/src/cmdline.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::ffi::CString; -use std::fs::File; -use std::{fs, path::PathBuf}; - -use anyhow::{bail, Context, Result}; - -use crate::fs_ops::open; -use crate::fuse_msg::FUSE_OK; -use util::arg_parser::{Arg, ArgMatches, ArgParser}; - -const MAX_PATH_LENGTH: usize = 4096; -// Maximum length of the socket path is restricted by linux. -const MAX_SOCK_PATH_LENGTH: usize = 108; - -/// This function is to define all command line arguments. -pub fn create_args_parser<'a>() -> ArgParser<'a> { - ArgParser::new("VhostUserFs") - .version(util::VERSION) - .author("Huawei Technologies Co., Ltd") - .about("The process of Virtio fs for StratoVirt.") - .arg( - Arg::with_name("source dir") - .long("source") - .value_name("shared_path") - .help("set source shared directory in host") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("socket path") - .long("socket-path") - .value_name("socket_path") - .help("vhost-user socket path which communicates with StratoVirt") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("rlimit nofile") - .long("rlimit-nofile") - .value_name("num") - .help("set file resource limits for the process") - .takes_value(true), - ) - .arg( - Arg::with_name("display log") - .long("D") - .value_name("log_path") - .help("output log to logfile") - .takes_value(true) - .can_no_value(true), - ) - .arg( - Arg::with_name("seccomp") - .long("seccomp") - .value_name("[allow | kill | log | trap]") - .help("limit syscall(allow, kill, log, trap) eg: -seccomp kill") - .takes_value(true) - .possible_values(vec!["allow", "kill", "log", "trap"]), - ) - .arg( - Arg::with_name("sandbox") - .long("sandbox") - .value_name("[chroot | namespace]") - .help("isolate the daemon process(chroot, namespace). eg: -sandbox namespace") - .takes_value(true) - .possible_values(vec!["namespace", "chroot"]), - ) - .arg( - Arg::with_name("modcaps") - .opt_long("modcaps") - .value_name("capabilities_list") - .help("modify the list of capabilities. eg: --modcaps=-LEASE,+KILL") - .takes_value(true), - ) -} - -/// Filesystem configuration parsed from command line for the process. -#[derive(Debug, Default)] -pub struct FsConfig { - /// Source directory in host which can be accessed by guest. - pub source_dir: String, - /// The path of socket file which communicates with StratoVirt. - pub sock_path: String, - /// The limit of file resources which can be opened for the process. - pub rlimit_nofile: Option, - /// The path of root directory. - pub root_dir: String, - /// File object for /proc/self/fd. - pub proc_dir_opt: Option, -} - -impl FsConfig { - fn check_config(&self) -> Result<()> { - if self.source_dir.len() > MAX_PATH_LENGTH { - bail!( - "The length of source directory is too long {}", - self.source_dir.len() - ); - } - - if self.sock_path.len() > MAX_SOCK_PATH_LENGTH { - bail!( - "The length of socket file path is too long {}", - self.sock_path.len() - ); - } - - if fs::metadata(&self.source_dir).is_err() { - bail!("Failed to stat source directory {}", self.source_dir); - } - let source_dir = PathBuf::from(&self.source_dir); - if !source_dir.is_dir() { - bail!( - "The source directory {} is not a directory", - self.source_dir - ); - } - - Ok(()) - } -} - -/// Construct a filesystem configuration parsed from command line. -/// -/// # Arguments -/// * `args` - The collection of information about the arguments from command line. -pub fn create_fs_config(args: &ArgMatches) -> Result { - let mut fs_config = FsConfig::default(); - - if let Some(source_dir) = args.value_of("source dir") { - fs_config.source_dir = source_dir; - } - - if let Some(sock_path) = args.value_of("socket path") { - fs_config.sock_path = sock_path; - } - - if let Some(rlimit_nofile) = args.value_of("rlimit nofile") { - let limit = rlimit_nofile - .parse::() - .with_context(|| "Failed to parse rlimit nofile")?; - fs_config.rlimit_nofile = Some(limit); - } - - let (proc_dir_opt, ret) = open(CString::new("/proc/self/fd").unwrap(), libc::O_PATH); - if ret != FUSE_OK { - bail!("Failed to open proc dir"); - } - fs_config.proc_dir_opt = proc_dir_opt; - - fs_config.root_dir = fs_config.source_dir.clone(); - if args.value_of("sandbox").is_some() { - fs_config.root_dir = "/".to_string(); - } - - fs_config - .check_config() - .with_context(|| "Precheck failed, Config is unhealthy, stop running")?; - - Ok(fs_config) -} diff --git a/vhost_user_fs/src/error.rs b/vhost_user_fs/src/error.rs deleted file mode 100644 index b8913f199..000000000 --- a/vhost_user_fs/src/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum VhostUserFsError { - #[error("Util")] - Util { - #[from] - source: util::error::UtilError, - }, - #[error("Virtio")] - Virtio { - #[from] - source: virtio::error::VirtioError, - }, - #[error("AddressSpace")] - AddressSpace { - #[from] - source: address_space::error::AddressSpaceError, - }, -} diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs deleted file mode 100644 index e6184cd2b..000000000 --- a/vhost_user_fs/src/fs.rs +++ /dev/null @@ -1,1967 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::collections::{BTreeMap, HashMap}; -use std::ffi::CString; -use std::fs::{read_to_string, File}; -use std::mem; -use std::os::unix::io::{AsRawFd, RawFd}; - -use anyhow::{bail, Context, Result}; - -use super::fs_ops::*; -use super::fuse_msg::*; -use crate::cmdline::FsConfig; -use util::byte_code::ByteCode; -use util::num_ops::round_up; - -/// The map length used to extend the file/inode map. -const MAP_EXTEND_LENGTH: usize = 256; -const F_RDLCK: u32 = 0; -const F_WDLCK: u32 = 1; -const F_UNLCK: u32 = 2; -const RLIMIT_NOFILE_MIN: u64 = 20; -/// The inode 0 is reserved, 1 is root inode. -const ROOT_INODE: usize = 1; - -/// Used as the key of the inode. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] -struct StatKey { - /// Inode number. - ino: libc::ino64_t, - /// ID of device containing file. - dev: libc::dev_t, -} - -/// The entry of the inode/file. -struct Entry { - /// The value stored in the Entry. - value: Option, - /// If the entry is used or not. - used: bool, - /// The next free entry. - free_next: usize, -} - -/// The map used to store inodes/files. -struct Map { - /// The vector used to store Entry. - list: Vec>, - /// The first free entry in list. - free_head: usize, -} - -impl Map { - fn new() -> Self { - Map { - list: Vec::new(), - free_head: ROOT_INODE, - } - } - - fn destroy_map(&mut self) { - self.list = Vec::new(); - self.free_head = ROOT_INODE; - } - - /// Add MAP_EXTEND_LENGTH elems to the map. - fn extend_map(&mut self) { - let mut next = self.list.len(); - - for _ in 0..MAP_EXTEND_LENGTH { - next += 1; - self.list.push(Entry { - value: None, - used: false, - free_next: next, - }); - } - } - - /// Add entry to the map. - fn get_map(&mut self, value: T) -> usize { - let id = self.free_head; - if id == ROOT_INODE || id == self.list.len() { - self.extend_map(); - } - - match self.list.get_mut(id) { - Some(e) => { - e.value = Some(value); - e.used = true; - self.free_head = e.free_next; - - id - } - None => 0, - } - } - - /// Delete entry from the map. - fn put_map(&mut self, id: usize) { - if id >= self.list.len() { - return; - } - - if let Some(e) = self.list.get_mut(id) { - if !e.used { - return; - } - - e.value = None; - e.used = false; - e.free_next = self.free_head; - self.free_head = id - } - } - - fn get_value(&self, id: usize) -> Option<&T> { - if let Some(e) = self.list.get(id) { - e.value.as_ref() - } else { - None - } - } - - fn get_value_mut(&mut self, id: usize) -> Option<&mut T> { - if let Some(e) = self.list.get_mut(id) { - e.value.as_mut() - } else { - None - } - } -} - -/// Used to lock file. -struct FileLock { - /// The owner of the lock - lock_owner: u64, - /// The file which is locked - file: File, -} - -impl FileLock { - fn new(file: File, lock_owner: u64) -> Self { - FileLock { lock_owner, file } - } -} - -impl Clone for FileLock { - fn clone(&self) -> Self { - FileLock { - lock_owner: self.lock_owner, - file: self.file.try_clone().unwrap(), - } - } -} - -/// The inode info. -struct Inode { - /// The inode file. - file: File, - /// The refcount of the inode. - nlookup: u64, - /// Store the map index of the inode. - node_id: usize, - /// the file type. - file_type: u32, - /// The key of the inode. - key: StatKey, - /// The locks on the file of the Inode. - locks: HashMap, -} - -impl Inode { - fn new(file: File, nlookup: u64, node_id: usize, file_type: u32, key: StatKey) -> Self { - Inode { - file, - nlookup, - node_id, - file_type, - key, - locks: HashMap::new(), - } - } - - fn as_raw_fd(&self) -> RawFd { - self.file.as_raw_fd() - } -} - -impl Clone for Inode { - fn clone(&self) -> Self { - Inode { - file: self.file.try_clone().unwrap(), - nlookup: self.nlookup, - node_id: self.node_id, - file_type: self.file_type, - key: self.key, - locks: self.locks.clone(), - } - } -} - -fn array_to_cstring( - #[cfg(target_arch = "x86_64")] array: &[i8], - #[cfg(target_arch = "aarch64")] array: &[u8], -) -> Result<(usize, CString)> { - let mut vec = Vec::new(); - for item in array { - if *item == 0 { - break; - } - vec.push(*item as u8); - } - - let len = vec.len(); - if len == 0 { - bail!("convert array to CString failed") - } - - let cstring = match CString::new(vec) { - Ok(c) => c, - Err(_) => bail!("convert array to CString failed"), - }; - - Ok((len, cstring)) -} - -fn path_is_dot(path: &CString) -> bool { - let bytes = path.as_bytes(); - if bytes.len() == 1 && bytes[0] == b'.' { - return true; - } - - false -} - -fn path_is_dotdot(path: &CString) -> bool { - let bytes = path.as_bytes(); - if bytes.len() == 2 && bytes[0] == b'.' && bytes[1] == b'.' { - return true; - } - - false -} - -/// Set file resources limits for the process. The limit value -/// must be more than or equal to 20 to ensure that the process -/// can start normally. The limit value must be less than or equal -/// to the value of "/proc/sys/fs/file-max" and "/proc/sys/fs/nr_open". -/// -/// # Arguments -/// -/// * `limit` - The limit value which needs to be set. -pub fn set_rlimit_nofile(limit: u64) -> Result<()> { - if limit < RLIMIT_NOFILE_MIN { - bail!( - "The limit {} exceeds minimum of files {}", - limit, - RLIMIT_NOFILE_MIN - ); - } - - let max_file_str = - read_to_string("/proc/sys/fs/file-max").with_context(|| "Failed to read file-max")?; - let max_file = max_file_str - .trim() - .parse::() - .with_context(|| "Failed to convert the string of max files")?; - if limit > max_file { - bail!("The limit {} exceeds maximum of files {}", limit, max_file); - } - - let nr_open_str = - read_to_string("/proc/sys/fs/nr_open").with_context(|| "Failed to read nr_open")?; - let max_file = nr_open_str - .trim() - .parse::() - .with_context(|| "Failed to convert the string of nr_open")?; - if limit > max_file { - bail!( - "The limit {} exceeds maximum of nr_open {}", - limit, - max_file - ); - } - - let ret = set_rlimit(limit, limit); - if ret != FUSE_OK { - bail!("Failed to set rlimit, err: {}", ret); - } - - Ok(()) -} - -/// The management structure of filesystem that contains the management of inodes -/// and the information of files in host directory which needs to be shared. -pub struct FileSystem { - root_inode: Inode, - inodes: BTreeMap, - inode_key_map: Map, - file_map: Map, - proc_dir: File, -} - -impl FileSystem { - /// Create a filesystem management structure. - /// - /// # Arguments - /// - /// * `source_dir` - The path of the host directory which needs to be shared. - pub fn new(fs_config: FsConfig) -> Result { - let root_dir = fs_config.root_dir.clone(); - let (root_file_opt, ret) = open(CString::new(root_dir).unwrap(), libc::O_PATH); - if ret != FUSE_OK { - bail!("Failed to open root file {}", fs_config.root_dir); - } - let root_file = root_file_opt.unwrap(); - let (stat, ret) = fstat_at( - &root_file, - CString::new("").unwrap(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - bail!("Failed to get stat of root file {}", fs_config.root_dir); - } - let key = StatKey { - ino: stat.st_ino, - dev: stat.st_dev, - }; - let mut inode_key_map = Map::new(); - let root_id = inode_key_map.get_map(key); - // The default folder nlookup is 2 - let root_inode = Inode::new(root_file, 2, root_id, libc::S_IFDIR, key); - let mut inodes = BTreeMap::new(); - inodes.insert(key, root_inode.clone()); - Ok(FileSystem { - root_inode, - inodes, - inode_key_map, - file_map: Map::new(), - proc_dir: fs_config.proc_dir_opt.unwrap(), - }) - } - - fn find_inode(&self, node_id: usize) -> Option<&Inode> { - match self.inode_key_map.get_value(node_id) { - Some(k) => self.inodes.get(k), - _ => None, - } - } - - fn find_mut_inode(&mut self, node_id: usize) -> Option<&mut Inode> { - match self.inode_key_map.get_value(node_id) { - Some(k) => self.inodes.get_mut(k), - _ => None, - } - } - - fn unref_inode(&mut self, inode: &mut Inode, count: u64) { - if count > inode.nlookup { - inode.nlookup = 0; - } else { - inode.nlookup -= count; - } - - if inode.nlookup == 0 { - self.inodes.remove(&inode.key); - self.inode_key_map.put_map(inode.node_id); - } else if let Some(inode_) = self.find_mut_inode(inode.node_id) { - inode_.nlookup = inode.nlookup; - } - } - - fn create_file_lock(&mut self, node_id: usize, owner: u64) -> (Option, i32) { - let proc_file = self.proc_dir.try_clone().unwrap(); - let inode = match self.find_mut_inode(node_id) { - Some(inode_) => inode_, - None => return (None, libc::EBADF), - }; - - if let Some(lock) = inode.locks.get_mut(&owner) { - return (Some(lock.file.try_clone().unwrap()), FUSE_OK); - } - - if inode.file_type & libc::S_IFDIR == 0 && inode.file_type & libc::S_IFREG == 0 { - return (None, libc::EBADF); - } - - let (file_opt, ret) = open_at( - &proc_file, - CString::new(format!("{}", inode.as_raw_fd())).unwrap(), - libc::O_RDWR, - 0, - ); - - if ret != FUSE_OK { - return (None, ret); - } - - let file = file_opt.unwrap().try_clone().unwrap(); - let file_lock = FileLock::new(file.try_clone().unwrap(), owner); - inode.locks.insert(owner, file_lock); - - (Some(file), FUSE_OK) - } - - fn delete_file_lock(&mut self, node_id: usize, owner: u64) -> i32 { - let inode = match self.find_mut_inode(node_id) { - Some(inode_) => inode_, - None => return libc::EBADF, - }; - - inode.locks.remove(&owner); - - FUSE_OK - } - - fn internal_lookup( - &mut self, - parent_inode: &Inode, - name: CString, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let mut son_name = name.clone(); - if parent_inode.node_id == self.root_inode.node_id && path_is_dotdot(&name) { - son_name = CString::new(".").unwrap(); - } - - let (stat, ret) = fstat_at( - &parent_inode.file, - son_name.clone(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - - let key = StatKey { - ino: stat.st_ino, - dev: stat.st_dev, - }; - if let Some(inode) = self.inodes.get_mut(&key) { - inode.nlookup += 1; - *node_id = inode.node_id as u64; - } else { - let (file_opt, ret) = open_at( - &parent_inode.file, - son_name, - libc::O_PATH | libc::O_NOFOLLOW, - 0, - ); - if ret != FUSE_OK { - return ret; - } - - let map_id = self.inode_key_map.get_map(key); - if let Some(file) = file_opt { - self.inodes.insert( - key, - Inode::new(file, 1, map_id, stat.st_mode & libc::S_IFMT, key), - ); - } - *node_id = map_id as u64; - }; - - *fuse_attr = FuseAttr::from_stat(stat); - - FUSE_OK - } - - /// Look up the directory or file information by name and reply attributes. - /// - /// # Arguments - /// - /// * `parent_nodeid` - The parent node id that is the starting directory to look up. - /// * `name` - The name that needs to be looked up. - /// * `node_id` - The node id that needs to be looked up by name. - /// * `fuse_attr` - The attributes that needs to be looked up by name. - pub fn lookup( - &mut self, - parent_nodeid: usize, - name: CString, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let inode = match self.find_inode(parent_nodeid) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - self.internal_lookup(&inode, name, node_id, fuse_attr) - } - - /// When the nlookup of inode is reduced to 0, delete the inode from the management structure. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to find the inode. - /// * `nlookup` - The number of nlookup for inode needs to be reduced. - pub fn forget(&mut self, node_id: usize, nlookup: u64) -> i32 { - let mut inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - self.unref_inode(&mut inode, nlookup); - FUSE_OK - } - - /// Get the attributes of a file or directory. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to find the inode. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn getattr(&mut self, node_id: usize, fuse_attr: &mut FuseAttr) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - let (stat, ret) = fstat_at( - &inode.file, - CString::new("").unwrap(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - *fuse_attr = FuseAttr::from_stat(stat); - FUSE_OK - } - - /// Set the attributes of a file or directory. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to find the inode. - /// * `attr` - The attributes will be set to the found inode. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn setattr( - &mut self, - node_id: usize, - attr: &FuseSetattrIn, - fuse_attr: &mut FuseAttr, - ) -> i32 { - if attr.valid & FUSE_SET_ATTR_MODE != 0 { - if attr.valid & FATTR_FH != 0 { - match self.file_map.get_value(attr.fh as usize) { - Some(file) => { - let ret = fchmod(file, attr.mode); - if ret != FUSE_OK { - return ret; - } - } - _ => { - return libc::EBADF; - } - }; - } else { - match self.find_inode(node_id) { - Some(i) => { - let ret = fchmod_at( - &self.proc_dir, - CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), - attr.mode, - ); - if ret != FUSE_OK { - return ret; - } - } - _ => { - return libc::EBADF; - } - }; - } - } - - if attr.valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID) != 0 { - let uid = if attr.valid & FUSE_SET_ATTR_UID != 0 { - attr.uid - } else { - u32::MAX - }; - - let gid = if attr.valid & FUSE_SET_ATTR_GID != 0 { - attr.gid - } else { - u32::MAX - }; - - match self.find_inode(node_id) { - Some(i) => { - let ret = fchown_at( - &i.file, - CString::new("").unwrap(), - uid, - gid, - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - } - _ => { - return libc::EBADF; - } - }; - } - - if attr.valid & FUSE_SET_ATTR_SIZE != 0 { - if attr.valid & FATTR_FH != 0 { - match self.file_map.get_value(attr.fh as usize) { - Some(file) => { - let ret = ftruncate(file, attr.size); - if ret != FUSE_OK { - return ret; - } - } - _ => { - return libc::EBADF; - } - }; - } else { - match self.find_inode(node_id) { - Some(i) => { - if i.file_type & libc::S_IFREG == 0 && i.file_type & libc::S_IFDIR == 0 { - return libc::EBADF; - } - - let (file_opt, ret) = open_at( - &self.proc_dir, - CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), - libc::O_RDWR, - 0, - ); - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - let ret = ftruncate(&file, attr.size); - if ret != FUSE_OK { - return ret; - } - } - } - _ => { - return libc::EBADF; - } - }; - } - } - - if attr.valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME) != 0 { - let (a_sec, a_nsec) = if attr.valid & FUSE_SET_ATTR_ATIME_NOW != 0 { - (0, libc::UTIME_NOW) - } else if attr.valid & FUSE_SET_ATTR_ATIME != 0 { - (attr.atime, attr.atimensec as i64) - } else { - (0, libc::UTIME_OMIT) - }; - - let (m_sec, m_nsec) = if attr.valid & FUSE_SET_ATTR_MTIME_NOW != 0 { - (0, libc::UTIME_NOW) - } else if attr.valid & FUSE_SET_ATTR_MTIME != 0 { - (attr.mtime, attr.mtimensec as i64) - } else { - (0, libc::UTIME_OMIT) - }; - - if attr.valid & FATTR_FH != 0 { - match self.file_map.get_value(attr.fh as usize) { - Some(file) => { - let ret = futimens(file, a_sec, a_nsec, m_sec, m_nsec); - if ret != FUSE_OK { - return ret; - } - } - _ => { - return libc::EBADF; - } - }; - } else { - match self.find_inode(node_id) { - Some(i) => { - let ret = utimensat( - &self.proc_dir, - CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), - a_sec, - a_nsec, - m_sec, - m_nsec, - 0, - ); - if ret != FUSE_OK { - return ret; - } - } - _ => { - return libc::EBADF; - } - }; - } - } - - self.getattr(node_id, fuse_attr) - } - - /// Get the contexts of the symbolic link into the buffer. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to find the inode. - /// * `buff` - The buffer is saved by the contexts of the symbolic link. - pub fn readlink(&self, node_id: usize, buff: &mut Vec) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - let (buf_opt, ret) = readlinkat(&inode.file, CString::new("").unwrap()); - if ret != FUSE_OK { - return ret; - } - - if let Some(mut buf) = buf_opt { - buff.append(&mut buf); - } else { - return libc::EBADF; - } - - FUSE_OK - } - - /// Get the contexts of the symbolic link into the buffer. - /// - /// # Arguments - /// - /// * `in_header` - The in_header of fuse message used to get uid and gid. - /// * `name` - The target link name used to create a symbolic link. - /// * `link_name` - The link name that will be created a symbolic link. - /// * `node_id` - The node id that is found by name. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn symlink( - &mut self, - in_header: &FuseInHeader, - name: CString, - link_name: CString, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let parent_inode = match self.find_inode(in_header.nodeid as usize) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - - let mut old_uid = 0_u32; - let mut old_gid = 0_u32; - let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); - if ret != FUSE_OK { - return ret; - } - - let ret = symlinkat(&parent_inode.file, name.clone(), link_name); - - recover_uid_gid(old_uid, old_gid); - - if ret != FUSE_OK { - return ret; - } - - self.internal_lookup(&parent_inode, name, node_id, fuse_attr) - } - - /// Create a file system node(file, device special file or named pipe) by the path name - /// with the mode and dev in the mknod information. - /// - /// # Arguments - /// - /// * `in_header` - The in_header of fuse message used to get uid and gid. - /// * `mknod_in` - The information of mknod to get the permissions and dev. - /// * `name` - The path name used to create a file system node. - /// * `node_id` - The node id that is found by name. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn mknod( - &mut self, - in_header: &FuseInHeader, - mknod_in: &FuseMknodIn, - name: CString, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let parent_inode = match self.find_inode(in_header.nodeid as usize) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - - let mut old_uid = 0_u32; - let mut old_gid = 0_u32; - let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); - if ret != FUSE_OK { - return ret; - } - - let ret = mknodat( - &parent_inode.file, - name.clone(), - mknod_in.mode & !mknod_in.umask, - mknod_in.rdev, - ); - - recover_uid_gid(old_uid, old_gid); - - if ret != FUSE_OK { - return ret; - } - - self.internal_lookup(&parent_inode, name, node_id, fuse_attr) - } - - /// Create a directory by the name with the permissions in the mkdir information. - /// - /// # Arguments - /// - /// * `in_header` - The in_header of fuse message used to get uid and gid. - /// * `mkdir_in` - The information of mkdir used to get permissions. - /// * `name` - The path name that will be created a directory. - /// * `node_id` - The node id that is found by the path name. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn mkdir( - &mut self, - in_header: &FuseInHeader, - mkdir_in: &FuseMkdirIn, - name: CString, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let parent_dir = match self.find_inode(in_header.nodeid as usize) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - - let mut old_uid = 0_u32; - let mut old_gid = 0_u32; - let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); - if ret != FUSE_OK { - return ret; - } - - let ret = mkdir_at( - &parent_dir.file, - name.clone(), - mkdir_in.mode & !mkdir_in.umask, - ); - - recover_uid_gid(old_uid, old_gid); - - if ret != FUSE_OK { - return ret; - } - - self.internal_lookup(&parent_dir, name, node_id, fuse_attr) - } - - /// Delete a name from the host filesystem. - /// - /// # Arguments - /// - /// * `parent_nodeid` - The parent node id that is the starting directory to look up - /// in the management of filesystem. - /// * `name` - The name will be deleted. - pub fn unlink(&mut self, parent_nodeid: usize, name: CString) -> i32 { - let parent_inode = match self.find_inode(parent_nodeid) { - Some(i) => i.clone(), - None => return libc::EBADF, - }; - - let (stat, ret) = fstat_at( - &parent_inode.file, - name.clone(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - - let key = StatKey { - ino: stat.st_ino, - dev: stat.st_dev, - }; - - match self.inodes.get(&key) { - Some(i) => i.clone(), - None => return libc::EIO, - }; - - let ret = unlinkat(&parent_inode.file, name, 0); - if ret != FUSE_OK { - return ret; - } - - FUSE_OK - } - - /// Delete a directory from the host filesystem by the path name. - /// - /// # Arguments - /// - /// * `parent_nodeid` - The parent node id that is the starting directory to look up - /// in the management of filesystem. - /// * `name` - The path name of the directory will be deleted. - pub fn rmdir(&mut self, parent_nodeid: usize, name: CString) -> i32 { - let parent_inode = match self.find_inode(parent_nodeid) { - Some(i) => i.clone(), - None => return libc::EBADF, - }; - - let (stat, ret) = fstat_at( - &parent_inode.file, - name.clone(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - - let key = StatKey { - ino: stat.st_ino, - dev: stat.st_dev, - }; - - match self.inodes.get(&key) { - Some(i) => i.clone(), - None => return libc::EIO, - }; - - let ret = unlinkat(&parent_inode.file, name, libc::AT_REMOVEDIR); - if ret != FUSE_OK { - return ret; - } - - FUSE_OK - } - - /// Rename the old path name to the new path name in the host filesystem.. - /// - /// # Arguments - /// - /// * `parent_nodeid` - The parent node id that is the starting directory to look up - /// for old path name in the management of filesystem. - /// * `oldname` - The old path name that is relative to the directory of parent node. - /// * `newparent_nodeid` - The new parent node id that is the starting directory to - /// look up for new path name in the management of filesystem. - /// * `newname` - The new path name that is relative to the directory of new parent node. - pub fn rename( - &self, - parent_nodeid: usize, - oldname: CString, - newparent_nodeid: usize, - newname: CString, - ) -> i32 { - let parent_inode = match self.find_inode(parent_nodeid) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - let newparent_inode = match self.find_inode(newparent_nodeid) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - let (stat, ret) = fstat_at( - &parent_inode.file, - oldname.clone(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - - let key = StatKey { - ino: stat.st_ino, - dev: stat.st_dev, - }; - - match self.inodes.get(&key) { - Some(_) => {} - None => return libc::EIO, - }; - - rename(&parent_inode.file, oldname, &newparent_inode.file, newname) - } - - /// Create a new link to an existing file for the host filesystem. - /// - /// # Arguments - /// - /// * `parent_nodeid` - The parent node id that is the starting directory to look up. - /// * `old_nodeid` - The old node id in the management of filesystem. - /// * `name` - The path name that is relative to the directory of parent node. - /// * `node_id` - The node id that is found by the path name in the management of filesystem. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn link( - &mut self, - parent_nodeid: usize, - old_nodeid: usize, - name: CString, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let proc_file = self.proc_dir.try_clone().unwrap(); - let parent_inode = match self.find_inode(parent_nodeid) { - Some(i) => i.clone(), - None => return libc::EBADF, - }; - - let inode = match self.find_mut_inode(old_nodeid) { - Some(inode_) => inode_, - None => return libc::EBADF, - }; - - let ret = linkat( - &proc_file, - CString::new(format!("{}", inode.as_raw_fd())).unwrap(), - &parent_inode.file, - name, - libc::AT_SYMLINK_FOLLOW, - ); - if ret != FUSE_OK { - return ret; - } - - let (stat, ret) = fstat_at( - &inode.file, - CString::new("").unwrap(), - libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, - ); - - if ret != FUSE_OK { - return ret; - } - - *fuse_attr = FuseAttr::from_stat(stat); - *node_id = inode.node_id as u64; - inode.nlookup += 1; - - FUSE_OK - } - - /// Open the file with the node id in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode. - /// * `flags` - The flags used to open the file. - /// * `fh` - The file handler is returned in the management of filesystem. - pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 { - // File creation should be done with create and mknod fuse messages. - if (flags & (libc::O_CREAT as u32 | libc::O_TMPFILE as u32)) != 0 { - return libc::EINVAL; - } - - let (inode_fd, file_type) = match self.find_inode(node_id) { - Some(i) => (i.as_raw_fd(), i.file_type), - None => { - return libc::EBADF; - } - }; - - if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { - return libc::EBADF; - } - - let (file_opt, ret) = open_at( - &self.proc_dir, - CString::new(format!("{}", inode_fd)).unwrap(), - (flags as i32) & !libc::O_NOFOLLOW, - 0, - ); - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64; - } - - FUSE_OK - } - - /// Read the file descriptor by file handler in the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - /// * `fd` - The file descriptor in the host filesystem. - pub fn read(&mut self, fh: usize, fd: &mut RawFd) -> i32 { - match self.file_map.get_value(fh) { - Some(file) => { - *fd = file.as_raw_fd(); - } - _ => { - return libc::EBADF; - } - } - - FUSE_OK - } - - /// write the file descriptor by file handler in the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - /// * `fd` - The file descriptor in the host filesystem. - pub fn write(&mut self, fh: usize, fd: &mut RawFd) -> i32 { - match self.file_map.get_value(fh) { - Some(file) => { - *fd = file.as_raw_fd(); - } - _ => { - return libc::EBADF; - } - } - - FUSE_OK - } - - /// Get the information about a mounted filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode. - /// * `fuse_statfs` - The information about the mounted filesystem is - /// returned by the found inode. - pub fn statfs(&mut self, node_id: usize, fuse_statfs: &mut FuseStatfsOut) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - let (stat, ret) = fstat_vfs(&inode.file); - if ret != FUSE_OK { - return ret; - } - - *fuse_statfs = FuseStatfsOut::from_stat(stat); - - FUSE_OK - } - - /// Release the file with file handler in the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - pub fn release(&mut self, fh: usize) -> i32 { - self.file_map.put_map(fh); - - FUSE_OK - } - - /// Transfer the file data to the storage device with file handler in - /// the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - /// * `datasync` - The datasync indicates whether to use the fdatasync - /// or fsync interface. - pub fn fsyncfile(&self, fh: usize, datasync: bool) -> i32 { - let mut ret = FUSE_OK; - - if fh == u64::max_value() as usize { - let (inode_fd, file_type) = match self.find_inode(fh) { - Some(i) => (i.as_raw_fd(), i.file_type), - None => { - return libc::EBADF; - } - }; - - if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { - return libc::EBADF; - } - - let (file_opt, ret_) = open_at( - &self.proc_dir, - CString::new(format!("{}", inode_fd)).unwrap(), - libc::O_RDWR, - 0, - ); - if ret_ != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - ret = fsync(&file, datasync); - } else { - return libc::EBADF; - } - } else { - match self.file_map.get_value(fh) { - Some(file) => { - ret = fsync(file, datasync); - } - _ => { - return libc::EBADF; - } - } - } - - ret - } - - /// Set an extended attribute identified by name and associated with the node id - /// in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode. - /// * `name` - The name associated with inode. - /// * `value` - The value of the extended attribute. - /// * `size` - The size of the value string. - /// * `flags` - The flags used to set an extended attribute. - pub fn setxattr( - &self, - node_id: usize, - name: CString, - value: CString, - size: u32, - flags: u32, - ) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { - let (file_opt, ret_) = open_at( - &self.proc_dir, - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - libc::O_RDONLY, - 0, - ); - if ret_ != FUSE_OK { - return ret_; - } - - if let Some(file) = file_opt { - fset_xattr(&file, name, value, size, flags) - } else { - libc::EBADF - } - } else { - if fchdir(&self.proc_dir) != FUSE_OK { - panic!("setxattr: failed to change process directory"); - } - - let ret_ = set_xattr( - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - name, - value, - size, - flags, - ); - - if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("setxattr: failed to change directory of root inode"); - } - - ret_ - } - } - - /// Get an extended attribute identified by name and associated with the node id - /// in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode. - /// * `name` - The name associated with inode. - /// * `size` - The size of the buffer. - /// * `buff` - The buffer of the extended attribute. - pub fn getxattr(&self, node_id: usize, name: CString, size: u32, buff: &mut Vec) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { - let (file_opt, ret) = open_at( - &self.proc_dir, - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - libc::O_RDONLY, - 0, - ); - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - let (buf_opt, ret) = fget_xattr(&file, name, size as usize); - if ret != FUSE_OK { - return ret; - } - if let Some(mut buf) = buf_opt { - buff.append(&mut buf); - } - } else { - return libc::EBADF; - } - } else { - if fchdir(&self.proc_dir) != FUSE_OK { - panic!("getxattr: failed to change process directory"); - } - - let (buf_opt, ret) = get_xattr( - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - name, - size as usize, - ); - - if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("getxattr: failed to change directory of root inode"); - } - if ret != FUSE_OK { - return ret; - } - if let Some(mut buf) = buf_opt { - buff.append(&mut buf); - } - } - - FUSE_OK - } - - /// List extended attribute names associated with the node id - /// in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode. - /// * `size` - The size of the buffer. - /// * `buff` - The buffer of the extended attribute. - pub fn listxattr(&self, node_id: usize, size: u32, buff: &mut Vec) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { - let (file_opt, ret) = open_at( - &self.proc_dir, - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - libc::O_RDONLY, - 0, - ); - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - let (buf_opt, ret) = flist_xattr(&file, size as usize); - if ret != FUSE_OK { - return ret; - } - if let Some(mut buf) = buf_opt { - buff.append(&mut buf); - } - } else { - return libc::EBADF; - } - } else { - if fchdir(&self.proc_dir) != FUSE_OK { - panic!("listxattr: failed to change process directory"); - } - - let (buf_opt, ret) = list_xattr( - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - size as usize, - ); - - if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("listxattr: failed to change directory of root inode"); - } - if ret != FUSE_OK { - return ret; - } - if let Some(mut buf) = buf_opt { - buff.append(&mut buf); - } - } - - FUSE_OK - } - - /// Remove an extended attribute identified by name and associated with the node id - /// in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode in the management of filesystem. - /// * `name` - The name associated with inode. - pub fn removexattr(&self, node_id: usize, name: CString) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { - let (file_opt, ret) = open_at( - &self.proc_dir, - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - libc::O_RDONLY, - 0, - ); - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - let ret = fremove_xattr(&file, name); - if ret != FUSE_OK { - return ret; - } - } else { - return libc::EBADF; - } - } else { - if fchdir(&self.proc_dir) != FUSE_OK { - panic!("removexattr: failed to change process directory"); - } - - let ret = remove_xattr( - CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), - name, - ); - - if fchdir(&self.root_inode.file) != FUSE_OK { - panic!("removexattr: failed to change directory of root inode"); - } - if ret != FUSE_OK { - return ret; - } - } - - FUSE_OK - } - - /// Delete the file lock by the node id in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode in the management of filesystem. - /// * `owner` - The name associated with inode. - pub fn flush(&mut self, node_id: usize, owner: u64) -> i32 { - self.delete_file_lock(node_id, owner) - } - - /// Initialize fuse message for getting supported features in the process. - /// - /// # Arguments - /// - /// * `flags` - The supported features in StratoVirt. - /// * `support_flags` - The supported features in the process. - pub fn init(&self, flags: u32, support_flags: &mut u32) { - if flags & FUSE_MAX_PAGES != 0 { - *support_flags |= FUSE_MAX_PAGES; - } - if flags & FUSE_CAP_ASYNC_READ != 0 { - *support_flags |= FUSE_ASYNC_READ; - } - if flags & FUSE_CAP_PARALLEL_DIROPS != 0 { - *support_flags |= FUSE_PARALLEL_DIROPS; - } - if flags & FUSE_CAP_POSIX_LOCKS != 0 { - *support_flags |= FUSE_POSIX_LOCKS; - } - if flags & FUSE_CAP_ATOMIC_O_TRUNC != 0 { - *support_flags |= FUSE_ATOMIC_O_TRUNC; - } - if flags & FUSE_CAP_EXPORT_SUPPORT != 0 { - *support_flags |= FUSE_EXPORT_SUPPORT; - } - if flags & FUSE_CAP_DONT_MASK != 0 { - *support_flags |= FUSE_DONT_MASK; - } - if flags & FUSE_CAP_FLOCK_LOCKS != 0 { - *support_flags |= FUSE_FLOCK_LOCKS; - } - if flags & FUSE_CAP_AUTO_INVAL_DATA != 0 { - *support_flags |= FUSE_AUTO_INVAL_DATA; - } - if flags & FUSE_CAP_READDIRPLUS != 0 { - *support_flags |= FUSE_DO_READDIRPLUS; - } - if flags & FUSE_CAP_READDIRPLUS_AUTO != 0 { - *support_flags |= FUSE_READDIRPLUS_AUTO; - } - if flags & FUSE_CAP_ASYNC_DIO != 0 { - *support_flags |= FUSE_ASYNC_DIO; - } - - if flags & FUSE_CAP_POSIX_ACL != 0 { - *support_flags |= FUSE_POSIX_ACL; - } - - umask(0o000); - } - - /// Open a directory with the node id in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode in the management of filesystem. - /// * `dir_fh` - The directory handler is returned in the management of filesystem. - pub fn opendir(&mut self, node_id: usize, dir_fh: &mut u64) -> i32 { - let inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - let (file_opt, ret) = open_at(&inode.file, CString::new(".").unwrap(), libc::O_RDONLY, 0); - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - *dir_fh = self.file_map.get_map(file) as u64; - return FUSE_OK; - } - - libc::EBADF - } - - /// read a directory stream with the directory handler in the host filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode in the management of filesystem. - /// * `dh` - The directory handler in the management of filesystem. - /// * `size` - The size of the buffer. - /// * `offset` - The offset indicates it opens a directory stream with the offset. - /// * `plus` - The plus indicates it uses FuseDirentplus struct to the buffer. - /// * `buff` - The buffer of all FuseDirent structs or FuseDirentplus structs. - pub fn readdir( - &mut self, - node_id: usize, - dh: usize, - size: u32, - offset: u64, - plus: bool, - buff: &mut Vec, - ) -> i32 { - let dir_inode = match self.find_inode(node_id) { - Some(i) => i.clone(), - None => { - return libc::EBADF; - } - }; - - let mut dirp = match self.file_map.get_value_mut(dh) { - Some(file) => { - let (dirp_opt, ret) = fdopen_dir(file.as_raw_fd()); - if ret != FUSE_OK { - return libc::EBADF; - } - dirp_opt.unwrap() - } - _ => { - return libc::EBADF; - } - }; - - seek_dir(&mut dirp, offset); - - let mut remain = size; - let mut son_nodeid = 0_u64; - loop { - let (dirent_opt, ret) = read_dir(&mut dirp); - if ret != FUSE_OK { - return ret; - } - let direntp = dirent_opt.unwrap(); - if direntp.is_null() { - break; - } - - // The above code has checked the validity of direntp, so it is safe for *direntp. - let dirent = unsafe { *direntp }; - - let (name_len, son_name) = match array_to_cstring(&dirent.d_name[..]) { - Ok(v) => v, - Err(_) => { - continue; - } - }; - - let only_entry_size = if plus { - mem::size_of::() - } else { - mem::size_of::() - }; - - let (entry_size, gap) = match round_up((only_entry_size + name_len) as u64, 8) { - Some(v) => (v as u32, v as usize - (only_entry_size + name_len)), - _ => { - return libc::EINVAL; - } - }; - if entry_size > remain { - if son_nodeid != 0 { - self.forget(son_nodeid as usize, 1); - } - break; - } - - let mut fuse_dirent = FuseDirent { - ino: dirent.d_ino, - off: dirent.d_off as u64, - namelen: name_len as u32, - type_: dirent.d_type as u32 & (libc::S_IFMT >> 12), - name: [0u8; 0], - }; - - if dir_inode.node_id == self.root_inode.node_id && path_is_dotdot(&son_name) { - fuse_dirent.ino = self.root_inode.key.ino; - fuse_dirent.type_ = libc::DT_DIR as u32 & (libc::S_IFMT >> 12); - } - - if plus { - son_nodeid = 0; - let mut son_attr = FuseAttr::default(); - if !path_is_dot(&son_name) && !path_is_dotdot(&son_name) { - let ret = self.internal_lookup( - &dir_inode, - son_name.clone(), - &mut son_nodeid, - &mut son_attr, - ); - if ret != FUSE_OK { - return ret; - } - } - - buff.extend_from_slice( - FuseDirentplus { - entry_out: FuseEntryOut { - nodeid: son_nodeid, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: son_attr, - }, - dirent: fuse_dirent, - } - .as_bytes(), - ); - } else { - buff.extend_from_slice(fuse_dirent.as_bytes()); - }; - - buff.extend_from_slice(son_name.as_bytes()); - if gap > 0 { - buff.append(&mut vec![0u8; gap]); - } - - remain -= entry_size; - } - - FUSE_OK - } - - /// Release a directory in the management of filesystem. - /// - /// # Arguments - /// - /// * `dir_fh` - The directory handler in the management of filesystem. - pub fn releasedir(&mut self, dir_fh: usize) -> i32 { - self.file_map.put_map(dir_fh); - - FUSE_OK - } - - /// Transfer the directory data to the storage device with directory handler in - /// the management of filesystem. - /// - /// # Arguments - /// - /// * `dir_fh` - The directory handler in the management of filesystem. - /// * `datasync` - The datasync indicates whether to use the fdatasync - /// or fsync interface. - pub fn fsyncdir(&self, dir_fh: usize, datasync: bool) -> i32 { - if let Some(file) = self.file_map.get_value(dir_fh) { - let ret = fsync(file, datasync); - if ret != FUSE_OK { - return ret; - } - } else { - return libc::EBADF; - } - - FUSE_OK - } - - /// Create the POSIX file lock with the node id in the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode in the management of filesystem. - /// * `owner` - The unique index for file lock in the inode. - /// * `file_lock_in` - The information of file lock will be set. - /// * `file_lock_out` - The information of file lock will be returned. - pub fn getlk( - &mut self, - node_id: usize, - owner: u64, - file_lock_in: &FuseFileLock, - file_lock_out: &mut FuseFileLock, - ) -> i32 { - let (file_opt, ret) = self.create_file_lock(node_id, owner); - if ret != FUSE_OK { - return ret; - } - - let ret = fcntl_flock( - &file_opt.unwrap(), - libc::F_GETLK, - file_lock_in, - file_lock_out, - ); - if ret != FUSE_OK { - return ret; - } - - FUSE_OK - } - - /// Lock the file or unlock the file by POSIX lock with the node id in - /// the management of filesystem. - /// - /// # Arguments - /// - /// * `node_id` - The node id used to look up the inode in the management of filesystem. - /// * `owner` - The unique index for file lock in the inode. - /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock. - /// * `file_lock_in` - The information of file lock will be set. - pub fn setlk( - &mut self, - node_id: usize, - owner: u64, - is_blocking: bool, - file_lock_in: &FuseFileLock, - ) -> i32 { - if is_blocking { - return libc::EOPNOTSUPP; - } - - let (file_opt, ret) = self.create_file_lock(node_id, owner); - if ret != FUSE_OK { - return ret; - } - - let mut file_lock_out = FuseFileLock::default(); - let ret = fcntl_flock( - &file_opt.unwrap(), - libc::F_SETLK, - file_lock_in, - &mut file_lock_out, - ); - if ret != FUSE_OK { - return ret; - } - - FUSE_OK - } - - /// Lock the file or unlock the file by BSD lock with file handler in - /// the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - /// * `lock_type` - The lock type contains the type of read lock, write lock and unlocking. - /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock. - pub fn flock(&self, fh: usize, lock_type: u32, is_blocking: bool) -> i32 { - let mut operation: i32 = 0; - - if lock_type == F_RDLCK { - operation = libc::LOCK_SH; - } else if lock_type == F_WDLCK { - operation = libc::LOCK_EX; - } else if lock_type == F_UNLCK { - operation = libc::LOCK_UN; - } - - if !is_blocking { - operation |= libc::LOCK_NB; - } - - if let Some(file) = self.file_map.get_value(fh) { - let ret = flock(file, operation); - if ret != FUSE_OK { - return ret; - } - } else { - return libc::EBADF; - } - - FUSE_OK - } - - /// Create a file with name in the management of filesystem. - /// - /// # Arguments - /// - /// * `in_header` - The in_header of fuse message used to get uid and gid. - /// * `create_in` - The information of creating a file contains the flags, mode and umask. - /// * `name` - The string of name used to create a file. - /// * `fh` - The file handler is returned in the management of filesystem. - /// * `node_id` - The node id that is found by the name in the management of filesystem. - /// * `fuse_attr` - The attributes will be returned by the found inode. - pub fn create( - &mut self, - in_header: &FuseInHeader, - create_in: &FuseCreateIn, - name: CString, - fh: &mut u64, - node_id: &mut u64, - fuse_attr: &mut FuseAttr, - ) -> i32 { - let parent_dir = match self.find_inode(in_header.nodeid as usize) { - Some(i) => i.clone(), - _ => { - return libc::EBADF; - } - }; - - let mut old_uid = 0_u32; - let mut old_gid = 0_u32; - let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); - if ret != FUSE_OK { - return ret; - } - - let (file_opt, ret) = open_at( - &parent_dir.file, - name.clone(), - (create_in.flags as i32 | libc::O_CREAT) & !libc::O_NOFOLLOW, - create_in.mode & !(create_in.umask & 0o777), - ); - - recover_uid_gid(old_uid, old_gid); - - if ret != FUSE_OK { - return ret; - } - - if let Some(file) = file_opt { - *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64; - } - - self.internal_lookup(&parent_dir, name, node_id, fuse_attr) - } - - /// Destroy the management of filesystem, except for the root inode. - pub fn destroy(&mut self) -> i32 { - let root_key = self.root_inode.key; - - self.inode_key_map.destroy_map(); - self.file_map.destroy_map(); - self.inodes = BTreeMap::new(); - - // Need to add root_inode back to the inode table and inode_key_map table for - // the filesystem function - let root_id = self.inode_key_map.get_map(root_key); - self.root_inode.node_id = root_id; - self.inodes.insert(root_key, self.root_inode.clone()); - - FUSE_OK - } - - /// Allocate the disk space with file handler in the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - /// * `mode` - The mode determines the operation to be performed on the given range. - /// * `offset` - The offset in the file. - /// * `length` - The length that needs to be allocated. - pub fn fallocate(&self, fh: usize, mode: u32, offset: u64, length: u64) -> i32 { - if let Some(file) = self.file_map.get_value(fh) { - let ret = fallocate(file, mode, offset, length); - if ret != FUSE_OK { - return ret; - } - } else { - return libc::EBADF; - } - - FUSE_OK - } - - /// Reposition the file offset of the open file with file handler - /// in the management of filesystem. - /// - /// # Arguments - /// - /// * `fh` - The file handler in the management of filesystem. - /// * `offset` - The offset in the file used together with the whence. - /// * `whence` - The whence determines the operation to be performed in the file. - /// * `outoffset` - The offset from the beginning of the file is returned. - pub fn lseek(&self, fh: usize, offset: u64, whence: u32, outoffset: &mut u64) -> i32 { - if let Some(file) = self.file_map.get_value(fh) { - let (offset_tmp, ret) = lseek(file, offset, whence); - if ret != FUSE_OK { - return ret; - } - *outoffset = offset_tmp; - } else { - return libc::EBADF; - } - - FUSE_OK - } -} diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs deleted file mode 100644 index 57c06d74e..000000000 --- a/vhost_user_fs/src/fs_ops.rs +++ /dev/null @@ -1,863 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::ffi::CString; -use std::fs::File; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; - -use super::fuse_msg::*; - -const MAX_PATH_LEN: usize = 4096; -const OFFSET_MAX: u64 = 0x7fffffffffffffff; - -/// The pointer to open a directory. -pub type DirPtr = *mut libc::DIR; -/// The pointer to a directory entry in the directory stream. -pub type DirentPtr = *mut libc::dirent; - -/// Get the information of a file with path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `flags` - The flags used to get the information of the file. -pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { - let mut stat: libc::stat = unsafe { std::mem::zeroed() }; - - nix::errno::Errno::clear(); - if unsafe { libc::fstatat(file.as_raw_fd(), name.as_ptr(), &mut stat, flags) } < 0 { - return (stat, nix::errno::errno()); - } - - (stat, FUSE_OK) -} - -/// Open a file with the path name. -/// -/// # Arguments -/// -/// * `name` - The path name in the host filesystem. -/// * `flags` - The flags used to open a file. -pub fn open(name: CString, flags: i32) -> (Option, i32) { - nix::errno::Errno::clear(); - let fd = unsafe { libc::open(name.as_ptr(), flags) }; - if fd < 0 { - return (None, nix::errno::errno()); - } - - let file = unsafe { File::from_raw_fd(fd) }; - - (Some(file), FUSE_OK) -} - -/// Open a file with path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `mode` - The mode used to open a file. -pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option, i32) { - nix::errno::Errno::clear(); - - let fd = unsafe { libc::openat(file.as_raw_fd(), name.as_ptr(), flags, mode) }; - if fd < 0 { - return (None, nix::errno::errno()); - } - - let file = unsafe { File::from_raw_fd(fd) }; - - (Some(file), FUSE_OK) -} - -/// Change permissions of a file. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `mode` - The mode indicates the permissions of the file will be set. -pub fn fchmod(file: &File, mode: u32) -> i32 { - nix::errno::Errno::clear(); - if unsafe { libc::fchmod(file.as_raw_fd(), mode) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Change permissions of a file with path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `mode` - The mode indicates the permissions of the file will be set. -pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { - nix::errno::Errno::clear(); - if unsafe { libc::fchmodat(file.as_raw_fd(), name.as_ptr(), mode, 0) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Change the owner and group of a file with path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `uid` - The user id will be set. -/// * `gid` - The group id will be set. -/// * `flags` - The flags indicates the action of file will be set. -pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> i32 { - nix::errno::Errno::clear(); - if unsafe { libc::fchownat(file.as_raw_fd(), name.as_ptr(), uid, gid, flags) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Truncate file to specified length. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `size` - The size of truncating file. -pub fn ftruncate(file: &File, size: u64) -> i32 { - nix::errno::Errno::clear(); - if unsafe { libc::ftruncate(file.as_raw_fd(), size as i64) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Update the timestamps of a file with nanosecond precision. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `a_sec` - The second of last access time. -/// * `a_nsec` - The nanosecond of last access time. -/// * `m_sec` - The second of last modification time. -/// * `m_nsec` - The nanosecond of last modification time. -pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) -> i32 { - let tv = vec![ - libc::timespec { - tv_sec: a_sec as i64, - tv_nsec: a_nsec, - }, - libc::timespec { - tv_sec: m_sec as i64, - tv_nsec: m_nsec, - }, - ]; - - nix::errno::Errno::clear(); - if unsafe { libc::futimens(file.as_raw_fd(), tv.as_ptr()) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Update the timestamps with nanosecond precision by path name that is relative to the starting -/// directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `a_sec` - The second of last access time. -/// * `a_nsec` - The nanosecond of last access time. -/// * `m_sec` - The second of last modification time. -/// * `m_nsec` - The nanosecond of last modification time. -pub fn utimensat( - file: &File, - name: CString, - a_sec: u64, - a_nsec: i64, - m_sec: u64, - m_nsec: i64, - flags: i32, -) -> i32 { - let tv = vec![ - libc::timespec { - tv_sec: a_sec as i64, - tv_nsec: a_nsec, - }, - libc::timespec { - tv_sec: m_sec as i64, - tv_nsec: m_nsec, - }, - ]; - - nix::errno::Errno::clear(); - if unsafe { libc::utimensat(file.as_raw_fd(), name.as_ptr(), tv.as_ptr(), flags) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Read value of a symbolic link by path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { - let mut buf = vec![0; MAX_PATH_LEN + 1]; - - nix::errno::Errno::clear(); - let ret = unsafe { - libc::readlinkat( - file.as_raw_fd(), - path.as_ptr(), - buf.as_mut_ptr() as *mut libc::c_char, - buf.len(), - ) - }; - - if ret == -1 { - return (None, nix::errno::errno()); - } - - buf.resize(ret as usize, 0); - - (Some(buf), FUSE_OK) -} - -/// Creates a symbolic link to the target path name. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `link_name` - The link name is new path name for the target path name. -pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { - nix::errno::Errno::clear(); - - let ret = unsafe { libc::symlinkat(link_name.as_ptr(), file.as_raw_fd(), name.as_ptr()) }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Change user id and group id in the process. -/// -/// # Arguments -/// -/// * `new_uid` - The user id will be changed to the current user id. -/// * `new_gid` - The group id will be changed to the current group id. -/// * `old_uid` - The old user id will be returned. -/// * `old_gid` - The old group id will be returned. -pub fn change_uid_gid(new_uid: u32, new_gid: u32, old_uid: &mut u32, old_gid: &mut u32) -> i32 { - let current_uid = unsafe { libc::geteuid() }; - let current_gid = unsafe { libc::getegid() }; - - nix::errno::Errno::clear(); - let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, new_gid, -1) }; - if ret == -1 { - return nix::errno::errno(); - } - - nix::errno::Errno::clear(); - let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, new_uid, -1) }; - if ret == -1 { - unsafe { libc::syscall(libc::SYS_setresgid, -1, current_gid, -1) }; - - return nix::errno::errno(); - } - - *old_uid = current_uid; - *old_gid = current_gid; - FUSE_OK -} - -/// Recover user id and group id in the process. -/// -/// # Arguments -/// -/// * `old_uid` - The old user id will be recovered in the process. -/// * `old_gid` - The old group id will be recovered in the process. -pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { - let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, old_uid, -1) }; - if ret == -1 { - panic!("Failed to recover uid {} {}", old_uid, old_gid); - } - - let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, old_gid, -1) }; - if ret == -1 { - panic!("Failed to recover gid {} {}", old_uid, old_gid); - } - - FUSE_OK -} - -/// Create a special or ordinary file by path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `mode` - The mode indicates both the file mode to use and the type of node to be created. -/// * `rdev` - The rdev indicates the major and minor numbers of the special file. -pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { - nix::errno::Errno::clear(); - - let ret = unsafe { libc::mknodat(file.as_raw_fd(), name.as_ptr(), mode, rdev as u64) }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Create a directory by path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `mode` - The mode indicates the permissions of the new directory. -pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { - nix::errno::Errno::clear(); - if unsafe { libc::mkdirat(file.as_raw_fd(), name.as_ptr(), mode) } < 0 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Delete a name in host filesystem by path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the file descriptor of starting directory to look up for the -/// file. -/// * `name` - The name indicates the file path is relative to the starting directory. -/// * `flags` - The flags indicates the operation of deleting a name. -pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { - nix::errno::Errno::clear(); - - let ret = unsafe { libc::unlinkat(file.as_raw_fd(), name.as_ptr(), flags) }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Modify a name in host filesystem by path name that is relative to the starting directory. -/// -/// # Arguments -/// -/// * `olddir` - The directory file handler saves the file descriptor of starting directory to look -/// up for the old file. -/// * `name` - The name indicates the file path is relative to the starting of old directory. -/// * `newdir` - The directory file handler saves the file descriptor of starting directory to look -/// up for the new file. -/// * `newname` - The name indicates the file path is relative to the starting of new directory. -pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> i32 { - nix::errno::Errno::clear(); - - let ret = unsafe { - libc::renameat( - olddir.as_raw_fd(), - name.as_ptr(), - newdir.as_raw_fd(), - newname.as_ptr(), - ) - }; - - if ret != FUSE_OK { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Change a name for file in host filesystem by path name that is relative to the starting -/// directory. -/// -/// # Arguments -/// -/// * `old_file` - The file handler saves the file descriptor of starting old directory to look up -/// for the file. -/// * `old_name` - The name indicates the file path is relative to the starting of old directory. -/// * `new_file` - The file handler saves the file descriptor of starting new directory to look up -/// for the file. -/// * `new_name` - The name indicates the file path is relative to the starting of new directory. -/// * `flags` - The flags indicates the operation of change a name. -pub fn linkat( - old_file: &File, - old_name: CString, - new_file: &File, - new_name: CString, - flags: i32, -) -> i32 { - nix::errno::Errno::clear(); - - let ret = unsafe { - libc::linkat( - old_file.as_raw_fd(), - old_name.as_ptr(), - new_file.as_raw_fd(), - new_name.as_ptr(), - flags, - ) - }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Get the information about a mounted filesystem. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { - let mut stat: libc::statvfs = unsafe { std::mem::zeroed() }; - - nix::errno::Errno::clear(); - if unsafe { libc::fstatvfs(file.as_raw_fd(), &mut stat) } < 0 { - return (stat, nix::errno::errno()); - } - - (stat, FUSE_OK) -} - -/// Synchronize the data of file to storage device. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `datasync` - The datasync indicates whether to use the fdatasync or fsync interface. -pub fn fsync(file: &File, datasync: bool) -> i32 { - nix::errno::Errno::clear(); - - let ret = if datasync { - unsafe { libc::fdatasync(file.as_raw_fd()) } - } else { - unsafe { libc::fsync(file.as_raw_fd()) } - }; - - if ret != FUSE_OK { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Change current working directory. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open directory descriptor. -pub fn fchdir(file: &File) -> i32 { - nix::errno::Errno::clear(); - let ret = unsafe { libc::fchdir(file.as_raw_fd()) }; - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Set an extended attribute value. -/// -/// # Arguments -/// -/// * `path` - The path in the host filesystem. -/// * `name` - The name of extended attribute. -/// * `value` - The value of extended attribute. -/// * `size` - The size of the string of value. -/// * `flags` - The flags indicates the attribute will be set. -pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: u32) -> i32 { - nix::errno::Errno::clear(); - let ret = unsafe { - libc::setxattr( - path.as_ptr(), - name.as_ptr(), - value.as_ptr() as *const libc::c_void, - size as usize, - flags as i32, - ) - }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Set an extended attribute value by the open file file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `name` - The name of extended attribute. -/// * `value` - The value of extended attribute. -/// * `size` - The size of the string of value. -/// * `flags` - The flags indicates the attribute will be set. -pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: u32) -> i32 { - nix::errno::Errno::clear(); - let ret = unsafe { - libc::fsetxattr( - file.as_raw_fd(), - name.as_ptr(), - value.as_ptr() as *const libc::c_void, - size as usize, - flags as i32, - ) - }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Get an extended attribute value. -/// -/// # Arguments -/// -/// * `path` - The path in the host filesystem. -/// * `name` - The name of extended attribute. -/// * `size` - The size of the extended attribute value that needs to be get. -pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, i32) { - let mut buf = vec![0; size]; - - nix::errno::Errno::clear(); - let ret = unsafe { - libc::getxattr( - path.as_ptr(), - name.as_ptr(), - buf.as_mut_ptr() as *mut libc::c_void, - size, - ) - }; - if ret == -1 { - return (None, nix::errno::errno()); - } - - buf.resize(ret as usize, 0); - - (Some(buf), FUSE_OK) -} - -/// Get an extended attribute value by the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `name` - The name of extended attribute. -/// * `size` - The size of the extended attribute value that needs to be get. -pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, i32) { - let mut buf = vec![0; size]; - - nix::errno::Errno::clear(); - let ret = unsafe { - libc::fgetxattr( - file.as_raw_fd(), - name.as_ptr(), - buf.as_mut_ptr() as *mut libc::c_void, - size, - ) - }; - if ret == -1 { - return (None, nix::errno::errno()); - } - - buf.resize(ret as usize, 0); - - (Some(buf), FUSE_OK) -} - -/// List extended attribute names. -/// -/// # Arguments -/// -/// * `path` - The path in the host filesystem. -/// * `size` - The size of the extended attribute names that needs to be get. -pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { - let mut buf = vec![0; size]; - - nix::errno::Errno::clear(); - let ret = - unsafe { libc::listxattr(path.as_ptr(), buf.as_mut_ptr() as *mut libc::c_char, size) }; - if ret == -1 { - return (None, nix::errno::errno()); - } - - buf.resize(ret as usize, 0); - - (Some(buf), FUSE_OK) -} - -/// List extended attribute names by the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `size` - The size of the extended attribute names that needs to be get. -pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { - let mut buf = vec![0; size]; - - nix::errno::Errno::clear(); - let ret = unsafe { - libc::flistxattr( - file.as_raw_fd(), - buf.as_mut_ptr() as *mut libc::c_char, - size, - ) - }; - if ret == -1 { - return (None, nix::errno::errno()); - } - - buf.resize(ret as usize, 0); - - (Some(buf), FUSE_OK) -} - -/// Remove an extended attribute value. -/// -/// # Arguments -/// -/// * `path` - The path in the host filesystem. -/// * `name` - The name of the extended attribute value. -pub fn remove_xattr(path: CString, name: CString) -> i32 { - nix::errno::Errno::clear(); - let ret = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) }; - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Remove an extended attribute value by the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `name` - The name of the extended attribute value. -pub fn fremove_xattr(file: &File, name: CString) -> i32 { - nix::errno::Errno::clear(); - let ret = unsafe { libc::fremovexattr(file.as_raw_fd(), name.as_ptr()) }; - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Set mask for file mode creation. -/// -/// # Arguments -/// -/// * `umask` - The umask indicates the permissions in the umask are turned off. -pub fn umask(umask: u32) { - unsafe { libc::umask(umask) }; -} - -/// Open a directory stream by the file descriptor. -/// -/// # Arguments -/// -/// * `fd` - The open file descriptor used to open a directory stream. -pub fn fdopen_dir(fd: RawFd) -> (Option, i32) { - nix::errno::Errno::clear(); - - let dirp = unsafe { libc::fdopendir(fd) }; - if nix::errno::errno() != 0 { - return (None, nix::errno::errno()); - } - - (Some(dirp), FUSE_OK) -} - -/// Set the position of the next readdir() in the directory stream. -/// -/// # Arguments -/// -/// * `dirp` - The pointer to open a directory. -/// * `offset` - The position of the next readdir(). -pub fn seek_dir(dirp: &mut DirPtr, offset: u64) { - unsafe { - libc::seekdir(*dirp, offset as i64); - }; -} - -/// Read a directory entry in the directory stream. -/// -/// # Arguments -/// -/// * `dirp` - The pointer to open a directory. -pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { - nix::errno::Errno::clear(); - - let direntp = unsafe { libc::readdir(*dirp) }; - if nix::errno::errno() != 0 { - return (None, nix::errno::errno()); - } - - (Some(direntp), FUSE_OK) -} - -/// Lock the file or unlock the file by BSD lock by the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `operation` - The operation of lock type. -pub fn flock(file: &File, operation: i32) -> i32 { - nix::errno::Errno::clear(); - let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Lock the file or unlock the file by POSIX lock by the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `cmd` - The command for creating, lock and unlock in POSIX lock. -/// * `file_lock` - The information of file lock will be set. -/// * `file_lock_out` - The information of file lock will be returned. -pub fn fcntl_flock( - file: &File, - cmd: i32, - file_lock: &FuseFileLock, - file_lock_out: &mut FuseFileLock, -) -> i32 { - let mut flock: libc::flock = unsafe { std::mem::zeroed() }; - - flock.l_type = file_lock.lock_type as i16; - flock.l_whence = libc::SEEK_SET as i16; - flock.l_start = file_lock.start as i64; - flock.l_pid = file_lock.pid as i32; - - if file_lock.end == OFFSET_MAX { - flock.l_len = 0; - } else { - flock.l_len = file_lock.end as i64 - file_lock.start as i64 + 1; - } - - nix::errno::Errno::clear(); - let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &mut flock) }; - - if ret == -1 { - return nix::errno::errno(); - } - - file_lock_out.lock_type = flock.l_type as u32; - if flock.l_type != libc::F_ULOCK as i16 { - file_lock_out.start = flock.l_start as u64; - if flock.l_len == 0 { - file_lock_out.end = OFFSET_MAX; - } else { - file_lock_out.end = (flock.l_start + flock.l_len - 1) as u64; - } - } - - file_lock_out.pid = flock.l_pid as u32; - - FUSE_OK -} - -/// Allocate the disk space with the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `mode` - The mode determines the operation to be performed on the given range. -/// * `offset` - The offset in the file. -/// * `length` - The length that needs to be allocated. -pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { - nix::errno::Errno::clear(); - - let ret = - unsafe { libc::fallocate(file.as_raw_fd(), mode as i32, offset as i64, length as i64) }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} - -/// Reposition the file offset of the open file descriptor. -/// -/// # Arguments -/// -/// * `file` - The file handler saves the open file descriptor. -/// * `offset` - The offset in the file used together with the whence. -/// * `whence` - The length that needs to be allocated. -pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { - nix::errno::Errno::clear(); - let ret = unsafe { libc::lseek(file.as_raw_fd(), offset as i64, whence as i32) }; - - if ret == -1 { - return (0, nix::errno::errno()); - } - - (ret as u64, FUSE_OK) -} - -/// Set file resource limits. -/// -/// # Arguments -/// -/// * `rlim_cur` - The soft limit of the file resource. -/// * `rlim_max` - The hard limit of the file resource. -pub fn set_rlimit(rlim_cur: u64, rlim_max: u64) -> i32 { - let limit = libc::rlimit { rlim_cur, rlim_max }; - - nix::errno::Errno::clear(); - let ret = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &limit) }; - - if ret == -1 { - return nix::errno::errno(); - } - - FUSE_OK -} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs deleted file mode 100644 index 02c8b8357..000000000 --- a/vhost_user_fs/src/fuse_msg.rs +++ /dev/null @@ -1,1004 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::cmp; -use std::collections::VecDeque; -use std::ffi::CString; -use std::mem::size_of; -use std::sync::Arc; - -use anyhow::{bail, Context, Result}; -use log::error; - -use address_space::AddressSpace; -use util::byte_code::ByteCode; -use virtio::ElemIovec; - -pub const FUSE_LOOKUP: u32 = 1; -pub const FUSE_FORGET: u32 = 2; -pub const FUSE_GETATTR: u32 = 3; -pub const FUSE_SETATTR: u32 = 4; -pub const FUSE_READLINK: u32 = 5; -pub const FUSE_SYMLINK: u32 = 6; -pub const FUSE_MKNOD: u32 = 8; -pub const FUSE_MKDIR: u32 = 9; -pub const FUSE_UNLINK: u32 = 10; -pub const FUSE_RMDIR: u32 = 11; -pub const FUSE_RENAME: u32 = 12; -pub const FUSE_LINK: u32 = 13; -pub const FUSE_OPEN: u32 = 14; -pub const FUSE_READ: u32 = 15; -pub const FUSE_WRITE: u32 = 16; -pub const FUSE_STATFS: u32 = 17; -pub const FUSE_RELEASE: u32 = 18; -pub const FUSE_FSYNC: u32 = 20; -pub const FUSE_SETXATTR: u32 = 21; -pub const FUSE_GETXATTR: u32 = 22; -pub const FUSE_LISTXATTR: u32 = 23; -pub const FUSE_REMOVEXATTR: u32 = 24; -pub const FUSE_FLUSH: u32 = 25; -pub const FUSE_INIT: u32 = 26; -pub const FUSE_OPENDIR: u32 = 27; -pub const FUSE_READDIR: u32 = 28; -pub const FUSE_RELEASEDIR: u32 = 29; -pub const FUSE_FSYNCDIR: u32 = 30; -pub const FUSE_GETLK: u32 = 31; -pub const FUSE_SETLK: u32 = 32; -pub const FUSE_SETLKW: u32 = 33; -pub const FUSE_ACCESS: u32 = 34; -pub const FUSE_CREATE: u32 = 35; -pub const FUSE_INTERRUPT: u32 = 36; -pub const FUSE_BMAP: u32 = 37; -pub const FUSE_DESTROY: u32 = 38; -pub const FUSE_IOCTL: u32 = 39; -pub const FUSE_POLL: u32 = 40; -pub const FUSE_NOTIFY_REPLY: u32 = 41; -pub const FUSE_BATCH_FORGET: u32 = 42; -pub const FUSE_FALLOCATE: u32 = 43; -pub const FUSE_READDIRPLUS: u32 = 44; -pub const FUSE_RENAME2: u32 = 45; -pub const FUSE_LSEEK: u32 = 46; -pub const FUSE_COPY_FILE_RANGE: u32 = 47; -pub const FUSE_SETUPMAPPING: u32 = 48; -pub const FUSE_REMOVEMAPPING: u32 = 49; - -/// The kernel version which is supported by fuse messages. -pub const FUSE_KERNEL_VERSION: u32 = 7; -/// The minor version which is supported by fuse messages. -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; - -/// The capability bit supports asynchronous read requests. -pub const FUSE_CAP_ASYNC_READ: u32 = 1 << 0; -/// The capability bit supports posix file locks. -pub const FUSE_CAP_POSIX_LOCKS: u32 = 1 << 1; -/// The capability bit supports the O_TRUNC open flag. -pub const FUSE_CAP_ATOMIC_O_TRUNC: u32 = 1 << 3; -/// The capability bit supports lookups of "." and "..". -pub const FUSE_CAP_EXPORT_SUPPORT: u32 = 1 << 4; -/// The capability bit don't apply umask to file mode on create operation. -pub const FUSE_CAP_DONT_MASK: u32 = 1 << 6; -/// The capability bit supports BSD file locks. -pub const FUSE_CAP_FLOCK_LOCKS: u32 = 1 << 10; -/// The capability bit automatically checks invalid cached file. -pub const FUSE_CAP_AUTO_INVAL_DATA: u32 = 1 << 12; -/// The capability bit supports readdirplus. -pub const FUSE_CAP_READDIRPLUS: u32 = 1 << 13; -/// The capability bit supports adaptive readdirplus. -pub const FUSE_CAP_READDIRPLUS_AUTO: u32 = 1 << 14; -/// The capability bit supports asynchronous direct I/O submission. -pub const FUSE_CAP_ASYNC_DIO: u32 = 1 << 15; -/// The capability bit supports for parallel directory operations. -pub const FUSE_CAP_PARALLEL_DIROPS: u32 = 1 << 18; -/// The capability bit supports POSIX ACLs. -pub const FUSE_CAP_POSIX_ACL: u32 = 1 << 19; - -/// The supported bit that supports asynchronous read requests. -pub const FUSE_ASYNC_READ: u32 = 1 << 0; -/// The supported bit that supports posix file locks. -pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; -/// The supported bit that supports the O_TRUNC open flag. -pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; -/// The supported bit that supports lookups of "." and "..". -pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; -/// The supported bit that don't apply umask to file mode on create operation. -pub const FUSE_DONT_MASK: u32 = 1 << 6; -/// The supported bit that supports BSD file locks. -pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; -/// The supported bit that automatically checks invalid cached file. -pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; -/// The supported bit that supports readdirplus. -pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; -/// The supported bit that supports adaptive readdirplus. -pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; -/// The supported bit that supports asynchronous direct I/O submission. -pub const FUSE_ASYNC_DIO: u32 = 1 << 15; -/// The capability bit that supports for parallel directory operations. -pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; -/// The capability bit that supports POSIX ACLs. -pub const FUSE_POSIX_ACL: u32 = 1 << 20; -/// The capability bit that needs to reply the max number of pages in init fuse message. -pub const FUSE_MAX_PAGES: u32 = 1 << 22; - -pub const FATTR_MODE: u32 = 1 << 0; -pub const FATTR_UID: u32 = 1 << 1; -pub const FATTR_GID: u32 = 1 << 2; -pub const FATTR_SIZE: u32 = 1 << 3; -pub const FATTR_ATIME: u32 = 1 << 4; -pub const FATTR_MTIME: u32 = 1 << 5; -pub const FATTR_FH: u32 = 1 << 6; -pub const FATTR_ATIME_NOW: u32 = 1 << 7; -pub const FATTR_MTIME_NOW: u32 = 1 << 8; -pub const FATTR_LOCKOWNER: u32 = 1 << 9; -pub const FATTR_CTIME: u32 = 1 << 10; - -/// Successfully process the fuse message. -pub const FUSE_OK: i32 = 0; -pub const FUSE_SET_ATTR_MODE: u32 = 1 << 0; -pub const FUSE_SET_ATTR_UID: u32 = 1 << 1; -pub const FUSE_SET_ATTR_GID: u32 = 1 << 2; -pub const FUSE_SET_ATTR_SIZE: u32 = 1 << 3; -pub const FUSE_SET_ATTR_ATIME: u32 = 1 << 4; -pub const FUSE_SET_ATTR_MTIME: u32 = 1 << 5; -pub const FUSE_SET_ATTR_ATIME_NOW: u32 = 1 << 7; -pub const FUSE_SET_ATTR_MTIME_NOW: u32 = 1 << 8; -pub const FUSE_SET_ATTR_CTIME: u32 = 1 << 10; - -/// Get the buffers from the element of virtio queue to parse fuse message or -/// reply fuse message. -pub struct FuseBuffer { - /// Get the buffers to read or write from the element of virtio queue. - pub bufs: VecDeque, - /// The total size of the buffers from the element of virtio queue. - bytes_total: usize, - /// The processed bytes to read or write fuse message. - bytes_processed: usize, -} - -impl FuseBuffer { - /// Construct a fuse buffer to process fuse message. - /// - /// # Arguments - /// - /// * `elem_iovec` - The vectors of IO vector element from virtio queue. - pub fn new(elem_iovec: &[ElemIovec]) -> Self { - let mut bytes_total = 0; - let mut bufs = VecDeque::new(); - - for iov in elem_iovec { - bytes_total += iov.len as usize; - bufs.push_back(*iov); - } - - FuseBuffer { - bufs, - bytes_total, - bytes_processed: 0_usize, - } - } - - /// Read the CString ending with '\0' from the fuse buffers. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space mapped with StratoVirt. - pub fn read_cstring(&mut self, sys_mem: &Arc) -> Result { - let bytes_remain = self.bytes_total - self.bytes_processed; - let mut buffer = vec![0; bytes_remain]; - - let mut offset = 0_usize; - for buf in &self.bufs { - let mut slice = &mut buffer[offset..]; - let read_count = cmp::min(slice.len(), buf.len as usize); - sys_mem - .read(&mut slice, buf.addr, read_count as u64) - .with_context(|| "Failed to read buffer for fuse req")?; - offset += read_count; - } - - let pos = match buffer.iter().position(|c| *c == b'\0') { - Some(p) => p + 1, - None => bail!("It is not a string"), - }; - - let str_slice = buffer.as_slice(); - let cstring = unsafe { CString::from_vec_unchecked(str_slice[0..pos].to_vec()) }; - - // Remove the processed bytes in self.bufs. - let mut need_read_count = pos; - let bufs = self.bufs.clone(); - for buf in bufs { - let read_count = cmp::min(need_read_count, buf.len as usize); - self.bytes_processed += read_count; - - if let Some(buftmp) = self.bufs.pop_front() { - if read_count < buftmp.len as usize { - // Add the remain length to the head of self.bufs. - let len = buftmp.len - read_count as u32; - let addr = buftmp.addr.unchecked_add(read_count as u64); - let remain = ElemIovec { addr, len }; - self.bufs.push_front(remain); - break; - } else { - need_read_count -= buftmp.len as usize; - } - } - } - - Ok(cstring) - } - - pub fn read_slice( - &mut self, - sys_mem: &Arc, - dst: &mut [u8], - count: usize, - ) -> Result<()> { - if dst.len() != count { - bail!( - "The length {} of dst slice is not equal to the count {}", - dst.len(), - count - ); - } - - let read_end = match self.bytes_processed.checked_add(count) { - Some(end_) => end_, - None => bail!("The read count {} {} overflow", count, self.bytes_processed), - }; - - if read_end > self.bytes_total { - bail!( - "The read count {} exceeds maximum {}", - read_end, - self.bytes_total - ); - } - - let bufs = self.bufs.clone(); - let mut offset = 0_usize; - for buf in bufs { - let mut slice = &mut dst[offset..]; - let read_count = cmp::min(slice.len(), buf.len as usize); - - sys_mem - .read(&mut slice, buf.addr, read_count as u64) - .with_context(|| "Failed to read buffer for fuse req")?; - self.bytes_processed += read_count; - offset += read_count; - - // Remove the processed bytes in self.bufs. - if let Some(buftmp) = self.bufs.pop_front() { - if read_count < buftmp.len as usize { - // Add the remain length to the head of self.bufs. - let len = buftmp.len - read_count as u32; - let addr = buftmp.addr.unchecked_add(read_count as u64); - let remain = ElemIovec { addr, len }; - self.bufs.push_front(remain); - break; - } - } - } - - Ok(()) - } - - /// read an object from the fuse buffers. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space mapped with StratoVirt. - /// - /// # Note - /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn read_obj(&mut self, sys_mem: &Arc) -> Result { - let mut obj = T::default(); - self.read_slice(sys_mem, obj.as_mut_bytes(), size_of::())?; - Ok(obj) - } - - fn write_slice(&mut self, sys_mem: &Arc, src: &[u8], count: usize) -> Result<()> { - if src.len() != count { - bail!( - "The length {} of src slice is not equal to the count {}", - src.len(), - count - ); - } - - let write_end = match self.bytes_processed.checked_add(count) { - Some(end_) => end_, - None => bail!("The read count {} {} overflow", count, self.bytes_processed), - }; - - if write_end > self.bytes_total { - bail!( - "The read count {} exceeds maximum {}", - write_end, - self.bytes_total - ); - } - - let bufs = self.bufs.clone(); - let mut offset = 0_usize; - for buf in bufs { - let mut slice = &src[offset..]; - let write_count = cmp::min(slice.len(), buf.len as usize); - - sys_mem - .write(&mut slice, buf.addr, write_count as u64) - .with_context(|| "Failed to read buffer for fuse req")?; - self.bytes_processed += write_count; - offset += write_count; - - // Remove the processed bytes in self.bufs. - if let Some(buftmp) = self.bufs.pop_front() { - if write_count < buftmp.len as usize { - // Add the remain length to the head of self.bufs. - let len = buftmp.len - write_count as u32; - let addr = buftmp.addr.unchecked_add(write_count as u64); - let remain = ElemIovec { addr, len }; - self.bufs.push_front(remain); - break; - } - } - } - - Ok(()) - } - - /// write an object to the fuse buffers. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space mapped with StratoVirt. - /// * `data` - The object the will be written to the fuse buffers. - /// - /// # Note - /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn write_obj(&mut self, sys_mem: &Arc, data: &T) -> Result<()> { - self.write_slice(sys_mem, data.as_bytes(), size_of::()) - } - - /// Process the data for host file. if is_read is true, writing the data which is read from host - /// file to the fuse buffers. if is_read is false, writing the data which is read from the fuse - /// buffers to host file. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space mapped with StratoVirt. - /// * `fd` - The file descriptor in host. - /// * `offset` - The offset which needs to be read and written in host file. - /// * `size` - The size which needs to be read and written in host file. - /// * `is_read` - If it is true, writing the data which is read from host file to the fuse - /// buffers. - /// If it is false, writing the data which is read from the fuse buffers to host file. - pub fn access_file( - &mut self, - sys_mem: &Arc, - fd: i32, - offset: u64, - size: u32, - is_read: bool, - ) -> Result { - let mut remain_len = size; - let mut file_off = offset; - - let mut index = 0; - let mut bufs = self.bufs.clone(); - - loop { - if index >= bufs.len() { - bail!("{} out of bufs's index", index); - } - - let buf = bufs - .get_mut(index) - .with_context(|| format!("{} out of bufs's bound", index))?; - - let len = if remain_len < buf.len { - remain_len - } else { - buf.len - }; - - let mut iov = Vec::new(); - let hvas = sys_mem.get_address_map(buf.addr, len as u64)?; - for addr in hvas.into_iter() { - iov.push(libc::iovec { - iov_base: addr.iov_base as *mut libc::c_void, - iov_len: addr.iov_len as usize, - }) - } - - let ret = unsafe { - if is_read { - libc::preadv(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) - } else { - libc::pwritev(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) - } - } as u32; - if ret == u32::MAX { - bail!("read file error"); - } - - remain_len -= ret; - file_off += ret as u64; - - self.bufs.pop_front(); - if ret < len { - buf.addr.0 += ret as u64; - buf.len -= ret; - - self.bufs.push_front(*buf); - } else { - index += 1; - } - - if ret == 0 || remain_len == 0 { - break; // finish. - } - } - - Ok(size - remain_len) - } -} - -/// Save the address and the length for replying fuse message. -pub struct FuseIovec<'a> { - body: &'a [u8], - len: usize, -} - -impl<'a> FuseIovec<'a> { - /// Convert an object to the struct of FuseIovec for replying fuse message. - /// - /// # Arguments - /// - /// * `obj` - The object the will be converted to the struct of FuseIovec. - /// - /// # Note - /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn from_obj(obj: &'a T) -> Self { - let body = obj.as_bytes(); - FuseIovec { - body, - len: body.len(), - } - } - - /// Convert a slice to the struct of FuseIovec for replying fuse message. - /// - /// # Arguments - /// - /// * `obj` - The slice the will be converted to the struct of FuseIovec. - pub fn from_slice(body: &'a [u8]) -> Self { - FuseIovec { - body, - len: body.len(), - } - } -} - -/// Reply the fuse messages by writing the data to the writable fuse buffers. -/// -/// # Arguments -/// -/// * `writer` - The writable fuse buffers. -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `in_header` - The in_header reading from the read-only fuse buffers. -/// * `err` - The error number for processing the fuse message. If it is ok, set error number to 0. -/// If it is false, set error number from linux. -/// * `body_opt` - The body for replying the fuse message needs to be written to fuse buffers. -/// * `body_len` - The length of body for replying the fuse message. if the body is none, set the -/// length to 0. -pub fn reply_fuse_msg( - writer: &mut FuseBuffer, - sys_mem: &Arc, - in_header: &FuseInHeader, - err: i32, - body_opt: Option>, - body_len: usize, -) -> u32 { - let len = size_of::() + body_len; - let mut written_len = len as u32; - - let fuse_out_header = FuseOutHeader { - len: len as u32, - error: -err, - unique: in_header.unique, - }; - - if let Err(e) = writer.write_obj(sys_mem, &fuse_out_header) { - error!( - "Failed to write out_header of fuse msg {}, {:?}", - in_header.opcode, e, - ); - written_len = 0_u32; - }; - - // write the body of fuse message in address space. - if let Some(body) = body_opt { - for fuse_iov in body.iter() { - if let Err(e) = writer.write_slice(sys_mem, fuse_iov.body, fuse_iov.len) { - error!( - "Failed to write the body of fuse msg {}, {:?}", - in_header.opcode, e, - ); - written_len = 0_u32; - } - } - } - - written_len -} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseInHeader { - pub len: u32, - pub opcode: u32, - pub unique: u64, - pub nodeid: u64, - pub uid: u32, - pub gid: u32, - pub pid: u32, - pub padding: u32, -} - -impl ByteCode for FuseInHeader {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseOutHeader { - pub len: u32, - pub error: i32, - pub unique: u64, -} - -impl ByteCode for FuseOutHeader {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseAttr { - pub ino: u64, - pub size: u64, - pub blocks: u64, - pub atime: u64, - pub mtime: u64, - pub ctime: u64, - pub atimensec: u32, - pub mtimensec: u32, - pub ctimensec: u32, - pub mode: u32, - pub nlink: u32, - pub uid: u32, - pub gid: u32, - pub rdev: u32, - pub blksize: u32, - pub flags: u32, -} - -impl ByteCode for FuseAttr {} - -impl FuseAttr { - pub fn from_stat(stat: libc::stat) -> Self { - FuseAttr { - ino: stat.st_ino, - size: stat.st_size as u64, - blocks: stat.st_blocks as u64, - atime: stat.st_atime as u64, - mtime: stat.st_mtime as u64, - ctime: stat.st_ctime as u64, - atimensec: stat.st_atime_nsec as u32, - mtimensec: stat.st_mtime_nsec as u32, - ctimensec: stat.st_ctime_nsec as u32, - mode: stat.st_mode, - nlink: stat.st_nlink as u32, - uid: stat.st_uid, - gid: stat.st_gid, - rdev: stat.st_rdev as u32, - blksize: stat.st_blksize as u32, - flags: 0, - } - } -} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseEntryOut { - pub nodeid: u64, - pub generation: u64, - pub entry_valid: u64, - pub attr_valid: u64, - pub entry_valid_nsec: u32, - pub attr_valid_nsec: u32, - pub attr: FuseAttr, -} - -impl ByteCode for FuseEntryOut {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseForgetIn { - pub nlookup: u64, -} - -impl ByteCode for FuseForgetIn {} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseAttrOut { - pub attr_valid: u64, - pub attr_valid_nsec: u32, - pub dummy: u32, - pub attr: FuseAttr, -} - -impl ByteCode for FuseAttrOut {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseGetAttrIn { - pub getattr_flags: u32, - pub dummy: u32, - pub fh: u64, -} - -impl ByteCode for FuseGetAttrIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseSetattrIn { - pub valid: u32, - pub padding: u32, - pub fh: u64, - pub size: u64, - pub lock_owner: u64, - pub atime: u64, - pub mtime: u64, - pub ctime: u64, - pub atimensec: u32, - pub mtimensec: u32, - pub ctimensec: u32, - pub mode: u32, - pub unused4: u32, - pub uid: u32, - pub gid: u32, - pub unused5: u32, -} - -impl ByteCode for FuseSetattrIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseMknodIn { - pub mode: u32, - pub rdev: u32, - pub umask: u32, - pub padding: u32, -} - -impl ByteCode for FuseMknodIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseMkdirIn { - pub mode: u32, - pub umask: u32, -} - -impl ByteCode for FuseMkdirIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseRenameIn { - pub newdir: u64, -} - -impl ByteCode for FuseRenameIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseLinkIn { - pub oldnodeid: u64, -} - -impl ByteCode for FuseLinkIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseOpenIn { - pub flags: u32, - pub unused: u32, -} - -impl ByteCode for FuseOpenIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseOpenOut { - pub fh: u64, - pub open_flags: u32, - pub padding: u32, -} - -impl ByteCode for FuseOpenOut {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseReadIn { - pub fh: u64, - pub offset: u64, - pub size: u32, - pub read_flags: u32, - pub lock_owner: u64, - pub flags: u32, - pub padding: u32, -} - -impl ByteCode for FuseReadIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseWriteIn { - pub fh: u64, - pub offset: u64, - pub size: u32, - pub write_flags: u32, - pub lock_owner: u64, - pub flags: u32, - pub padding: u32, -} - -impl ByteCode for FuseWriteIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseWriteOut { - pub size: u32, - pub padding: u32, -} - -impl ByteCode for FuseWriteOut {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseKstatfs { - pub blocks: u64, - pub bfree: u64, - pub bavail: u64, - pub files: u64, - pub ffree: u64, - pub bsize: u32, - pub namelen: u32, - pub frsize: u32, - pub padding: u32, - pub spare: [u32; 6], -} - -impl ByteCode for FuseKstatfs {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseStatfsOut { - pub st: FuseKstatfs, -} - -impl ByteCode for FuseStatfsOut {} - -impl FuseStatfsOut { - pub fn from_stat(stat: libc::statvfs) -> Self { - let st = FuseKstatfs { - blocks: stat.f_blocks, - bfree: stat.f_bfree, - bavail: stat.f_bavail, - files: stat.f_files, - ffree: stat.f_ffree, - bsize: stat.f_bsize as u32, - namelen: stat.f_namemax as u32, - frsize: stat.f_frsize as u32, - ..Default::default() - }; - - FuseStatfsOut { st } - } -} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseReleaseIn { - pub fh: u64, - pub flags: u32, - pub release_flags: u32, - pub lock_owner: u64, -} - -impl ByteCode for FuseReleaseIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseFsyncIn { - pub fh: u64, - pub fsync_flags: u32, - pub padding: u32, -} - -impl ByteCode for FuseFsyncIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseSetxattrIn { - pub size: u32, - pub flags: u32, -} - -impl ByteCode for FuseSetxattrIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseGetxattrIn { - pub size: u32, - pub padding: u32, -} - -impl ByteCode for FuseGetxattrIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseInitIn { - pub major: u32, - pub minor: u32, - pub max_readahead: u32, - pub flags: u32, -} - -impl ByteCode for FuseInitIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseInitOut { - pub major: u32, - pub minor: u32, - pub max_readahead: u32, - pub flags: u32, - pub max_background: u16, - pub congestion_threshold: u16, - pub max_write: u32, - pub time_gran: u32, - pub max_pages: u16, - pub map_alignment: u16, - pub unused: [u32; 8], -} - -impl ByteCode for FuseInitOut {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseDirent { - pub ino: u64, - pub off: u64, - pub namelen: u32, - pub type_: u32, - pub name: [u8; 0], -} - -impl ByteCode for FuseDirent {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseDirentplus { - pub entry_out: FuseEntryOut, - pub dirent: FuseDirent, -} - -impl ByteCode for FuseDirentplus {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseFlushIn { - pub fh: u64, - pub unused: u32, - pub padding: u32, - pub lock_owner: u64, -} - -impl ByteCode for FuseFlushIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseFileLock { - pub start: u64, - pub end: u64, - pub lock_type: u32, - pub pid: u32, -} - -impl ByteCode for FuseFileLock {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseLkIn { - pub fh: u64, - pub owner: u64, - pub lk: FuseFileLock, - pub lk_flags: u32, - pub padding: u32, -} - -impl ByteCode for FuseLkIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseLkOut { - pub lk: FuseFileLock, -} - -impl ByteCode for FuseLkOut {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseCreateIn { - pub flags: u32, - pub mode: u32, - pub umask: u32, - pub padding: u32, -} - -impl ByteCode for FuseCreateIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseBatchForgetIn { - pub count: u32, - pub dummy: u32, -} - -impl ByteCode for FuseBatchForgetIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseForgetDataIn { - pub ino: u64, - pub nlookup: u64, -} - -impl ByteCode for FuseForgetDataIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseFallocateIn { - pub fh: u64, - pub offset: u64, - pub length: u64, - pub mode: u32, - pub padding: u32, -} - -impl ByteCode for FuseFallocateIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseLseekIn { - pub fh: u64, - pub offset: u64, - pub whence: u32, - pub padding: u32, -} - -impl ByteCode for FuseLseekIn {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct FuseLseekOut { - pub offset: u64, -} - -impl ByteCode for FuseLseekOut {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs deleted file mode 100644 index 802fc133a..000000000 --- a/vhost_user_fs/src/fuse_proc.rs +++ /dev/null @@ -1,1761 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::convert::TryInto; -use std::ffi::CString; -use std::mem; -use std::sync::{Arc, Mutex}; - -use log::error; - -use super::fs::FileSystem; -use super::fuse_msg::*; -use address_space::AddressSpace; - -const MAX_WRITE_SIZE: u32 = 1 << 20; - -fn is_safe_path(path: &CString) -> bool { - let path_str = match path.clone().into_string() { - Ok(str_) => str_, - Err(_e) => return false, - }; - - if path_str.find('/').is_some() { - return false; - } - - // Check if the path is "." or ".." - let bytes = path_str.as_bytes(); - if bytes[0] == 0x2e && (bytes[1] == 0x0 || (bytes[1] == 0x2e && bytes[2] == 0x0)) { - return false; - } - - true -} - -fn is_empty_path(path: &CString) -> bool { - let bytes = path.clone().into_bytes(); - - bytes[0] == 0x0 -} - -/// Process the fuse message of FUSE_LOOKUP. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_lookup( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for lookup, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut fuse_attr = FuseAttr::default(); - let mut node_id = 0_u64; - let ret = fs.lock().unwrap().lookup( - in_header.nodeid as usize, - name, - &mut node_id, - &mut fuse_attr, - ); - - if ret == FUSE_OK { - let entry_out = FuseEntryOut { - nodeid: node_id, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: fuse_attr, - }; - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&entry_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_FORGET. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_forget( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let forget_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for forget_in, {:?}", e); - return 0_u32; - } - }; - - fs.lock() - .unwrap() - .forget(in_header.nodeid as usize, forget_in.nlookup); - 0_u32 -} - -/// Process the fuse message of FUSE_GETATTR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_getattr( - sys_mem: &Arc, - fs: Arc>, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let mut fuse_attr = FuseAttr::default(); - let ret = fs - .lock() - .unwrap() - .getattr(in_header.nodeid as usize, &mut fuse_attr); - if ret == 0 { - let attr_out = FuseAttrOut { - attr_valid: 1, - attr_valid_nsec: 0, - dummy: 0, - attr: fuse_attr, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&attr_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_SETATTR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_setattr( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let setattr_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for setattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut fuse_attr = FuseAttr::default(); - let ret = fs - .lock() - .unwrap() - .setattr(in_header.nodeid as usize, &setattr_in, &mut fuse_attr); - if ret == FUSE_OK { - let attr_out = FuseAttrOut { - attr_valid: 1, - attr_valid_nsec: 0, - dummy: 0, - attr: fuse_attr, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&attr_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_READLINK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_readlink( - sys_mem: &Arc, - fs: Arc>, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let mut buff = Vec::new(); - let ret = fs - .lock() - .unwrap() - .readlink(in_header.nodeid as usize, &mut buff); - - if ret == FUSE_OK { - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_slice(buff.as_slice())]), - buff.len(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_SYMLINK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_symlink( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for symlink, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let link_name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read link name for symlink, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&link_name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&link_name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let mut node_id = 0_u64; - let mut fuse_attr = FuseAttr::default(); - let ret = fs - .lock() - .unwrap() - .symlink(in_header, name, link_name, &mut node_id, &mut fuse_attr); - if ret == FUSE_OK { - let entry_out = FuseEntryOut { - nodeid: node_id, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: fuse_attr, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_obj(&entry_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_MKNOD. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_mknod( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let mknod_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for mknod_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for mknod, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let mut node_id = 0_u64; - let mut fuse_attr = FuseAttr::default(); - let ret = fs - .lock() - .unwrap() - .mknod(in_header, &mknod_in, name, &mut node_id, &mut fuse_attr); - if ret == FUSE_OK { - let entry_out = FuseEntryOut { - nodeid: node_id, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: fuse_attr, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_obj(&entry_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_MKDIR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_mkdir( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let mkdir_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for mkdir_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for mkdir, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let mut node_id = 0_u64; - let mut fuse_attr = FuseAttr::default(); - let ret = fs - .lock() - .unwrap() - .mkdir(in_header, &mkdir_in, name, &mut node_id, &mut fuse_attr); - if ret == FUSE_OK { - let entry_out = FuseEntryOut { - nodeid: node_id, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: fuse_attr, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_obj(&entry_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_UNLINK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_unlink( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for unlink, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let ret = fs.lock().unwrap().unlink(in_header.nodeid as usize, name); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_RMDIR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_rmdir( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for rmdir, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let ret = fs.lock().unwrap().rmdir(in_header.nodeid as usize, name); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_RENAME. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_rename( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let rename_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for rename_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let oldname = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read old name for rename, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let newname = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read new name for rename, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&oldname) || is_empty_path(&newname) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&oldname) || !is_safe_path(&newname) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let ret = fs.lock().unwrap().rename( - in_header.nodeid as usize, - oldname, - rename_in.newdir as usize, - newname, - ); - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_LINK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_link( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let link_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for link_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for link, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let mut fuse_attr = FuseAttr::default(); - let mut node_id = 0_u64; - let ret = fs.lock().unwrap().link( - in_header.nodeid as usize, - link_in.oldnodeid as usize, - name, - &mut node_id, - &mut fuse_attr, - ); - - if ret == FUSE_OK { - let entry_out = FuseEntryOut { - nodeid: node_id, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: fuse_attr, - }; - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&entry_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_OPEN. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_open( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let open_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for open_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut fh = 0_u64; - let ret = fs - .lock() - .unwrap() - .open(in_header.nodeid as usize, open_in.flags, &mut fh); - if ret == FUSE_OK { - let open_out = FuseOpenOut { - fh, - open_flags: 0, - padding: 0, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&open_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_READ. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_read( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let read_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for read_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut fd = 0; - let ret = fs.lock().unwrap().read(read_in.fh as usize, &mut fd); - if ret == FUSE_OK { - let buf_header = match writer.bufs.get(0) { - Some(b) => *b, - None => { - error!("Failed to get the address of out_header"); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut f_ret = reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize); - - match writer.access_file(sys_mem, fd, read_in.offset, read_in.size, true) { - Ok(size) => { - f_ret += size; - // write size to FuseOutHeader.len - sys_mem.write_object(&f_ret, buf_header.addr).unwrap(); - } - Err(e) => { - error!("Failed to access file for reading, {:?}", e); - } - }; - - f_ret - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_WRITE. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_write( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let write_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for write_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut fd = 0; - let ret = fs.lock().unwrap().write(write_in.fh as usize, &mut fd); - if ret == FUSE_OK { - match reader.access_file(sys_mem, fd, write_in.offset, write_in.size, false) { - Ok(size) => { - let write_out = FuseWriteOut { size, padding: 0 }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&write_out)]), - mem::size_of::(), - ) - } - Err(e) => { - error!("Failed to access file for writing, {:?}", e); - reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize) - } - } - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_STATFS. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_statfs( - sys_mem: &Arc, - fs: Arc>, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let mut statfs = FuseStatfsOut::default(); - let ret = fs - .lock() - .unwrap() - .statfs(in_header.nodeid as usize, &mut statfs); - if ret == FUSE_OK { - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&statfs)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_RELEASE. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_release( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let release_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for release_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let ret = fs.lock().unwrap().release(release_in.fh as usize); - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_FSYNC. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_fsync( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let fsync_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read name for fsync_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let datasync = fsync_in.fsync_flags & 0x1 == 0x1; - - let ret = fs.lock().unwrap().fsyncfile(fsync_in.fh as usize, datasync); - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_SETXATTR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_setxattr( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let setxattr_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for setxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for setxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut value = Vec::new(); - value.resize(setxattr_in.size as usize, 0); - - if let Err(e) = reader.read_slice(sys_mem, &mut value, setxattr_in.size as usize) { - error!("Failed to read value for setxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let cvalue = CString::new(value).unwrap_or_else(|_| CString::from(Vec::new())); - - let ret = fs.lock().unwrap().setxattr( - in_header.nodeid as usize, - name, - cvalue, - setxattr_in.size, - setxattr_in.flags, - ); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_GETXATTR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_getxattr( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let getxattr_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for getxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for getxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut buff = Vec::new(); - let ret = - fs.lock() - .unwrap() - .getxattr(in_header.nodeid as usize, name, getxattr_in.size, &mut buff); - if ret == FUSE_OK { - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_slice(buff.as_slice())]), - buff.len(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_LISTXATTR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_listxattr( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let getxattr_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for listxattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut buff = Vec::new(); - let ret = fs - .lock() - .unwrap() - .listxattr(in_header.nodeid as usize, getxattr_in.size, &mut buff); - if ret == FUSE_OK { - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_slice(buff.as_slice())]), - buff.len(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_REMOVEXATTR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_removexattr( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let name = match reader.read_cstring(sys_mem) { - Ok(s) => s, - Err(e) => { - error!("Failed to read name for removexattr_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let ret = fs - .lock() - .unwrap() - .removexattr(in_header.nodeid as usize, name); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_FLUSH. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_flush( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let flush_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for flush_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let ret = fs - .lock() - .unwrap() - .flush(in_header.nodeid as usize, flush_in.lock_owner); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_INIT. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_init( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let init_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for init_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut support_flags = 0_u32; - fs.lock().unwrap().init(init_in.flags, &mut support_flags); - let pagesize: u32 = unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }; - let init_out = FuseInitOut { - major: FUSE_KERNEL_VERSION, - minor: FUSE_KERNEL_MINOR_VERSION, - max_readahead: init_in.max_readahead, - flags: support_flags, - max_background: 0, - congestion_threshold: 0, - max_write: MAX_WRITE_SIZE, - // Granularity of c/m/atime in ns (cannot be worse than a second), default 1. - time_gran: 1, - max_pages: ((MAX_WRITE_SIZE + pagesize - 1) / pagesize) as u16, - map_alignment: 0, - ..Default::default() - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_obj(&init_out)]), - mem::size_of::(), - ) -} - -/// Process the fuse message of FUSE_OPENDIR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_opendir( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let _open_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for opendir_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut dir_fh = 0_u64; - let ret = fs - .lock() - .unwrap() - .opendir(in_header.nodeid as usize, &mut dir_fh); - if ret == FUSE_OK { - let open_out = FuseOpenOut { - fh: dir_fh, - open_flags: 0, - padding: 0, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_obj(&open_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_READDIR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_readdir( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let read_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for readdir_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut buff = Vec::new(); - let ret = fs.lock().unwrap().readdir( - in_header.nodeid as usize, - read_in.fh as usize, - read_in.size, - read_in.offset, - false, - &mut buff, - ); - if ret == FUSE_OK { - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_slice(buff.as_slice())]), - buff.len(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_RELEASEDIR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_releasedir( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let release_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for releasedir_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let ret = fs.lock().unwrap().releasedir(release_in.fh as usize); - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_FSYNCDIR. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_fsyncdir( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let fsync_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for fsync_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let datasync = fsync_in.fsync_flags & 0x1 == 0x1; - let ret = fs.lock().unwrap().fsyncdir(fsync_in.fh as usize, datasync); - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_GETLK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_getlk( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let lk_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for get_lk_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut file_lock = FuseFileLock::default(); - let ret = fs.lock().unwrap().getlk( - in_header.nodeid as usize, - lk_in.owner, - &lk_in.lk, - &mut file_lock, - ); - - if ret == FUSE_OK { - let lk_out = FuseLkOut { lk: file_lock }; - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_obj(&lk_out)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -fn do_fuse_setlk_common( - sys_mem: &Arc, - fs: Arc>, - lk_in: &FuseLkIn, - is_blocking: bool, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let ret = fs.lock().unwrap().setlk( - in_header.nodeid as usize, - lk_in.owner, - is_blocking, - &lk_in.lk, - ); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -fn do_fuse_flock( - sys_mem: &Arc, - fs: Arc>, - lk_in: &FuseLkIn, - is_blocking: bool, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let ret = fs - .lock() - .unwrap() - .flock(lk_in.fh as usize, lk_in.lk.lock_type, is_blocking); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -const FUSE_LOCK_FLOCK: u32 = (1 << 0) as u32; - -/// Process the fuse message of FUSE_SETLK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_setlk( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let lk_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for set_lk_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if lk_in.lk_flags & FUSE_LOCK_FLOCK == FUSE_LOCK_FLOCK { - do_fuse_flock(sys_mem, fs, &lk_in, false, writer, in_header) - } else { - do_fuse_setlk_common(sys_mem, fs, &lk_in, false, writer, in_header) - } -} - -/// Process the fuse message of FUSE_SETLKW. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_setlkw( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let lk_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for setlkw_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if lk_in.lk_flags & FUSE_LOCK_FLOCK == FUSE_LOCK_FLOCK { - do_fuse_flock(sys_mem, fs, &lk_in, true, writer, in_header) - } else { - do_fuse_setlk_common(sys_mem, fs, &lk_in, true, writer, in_header) - } -} - -/// Process the fuse message of FUSE_CREATE. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_create( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let create_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for create_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let name = match reader.read_cstring(sys_mem) { - Ok(string) => string, - Err(e) => { - error!("Failed to read name for creating file, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - if is_empty_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::ENOENT, None, 0_usize); - } - - if !is_safe_path(&name) { - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - - let mut fh = 0_u64; - let mut node_id = 0_u64; - let mut fuse_attr = FuseAttr::default(); - let ret = fs.lock().unwrap().create( - in_header, - &create_in, - name, - &mut fh, - &mut node_id, - &mut fuse_attr, - ); - if ret == FUSE_OK { - let entry_out = FuseEntryOut { - nodeid: node_id, - generation: 0, - entry_valid: 0, - entry_valid_nsec: 0, - attr_valid: 0, - attr_valid_nsec: 0, - attr: fuse_attr, - }; - - let open_out = FuseOpenOut { - fh, - open_flags: 0, - padding: 0, - }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![ - FuseIovec::from_obj(&entry_out), - FuseIovec::from_obj(&open_out), - ]), - mem::size_of::() + mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_DESTROY. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_destroy( - sys_mem: &Arc, - fs: Arc>, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let ret = fs.lock().unwrap().destroy(); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_BATCH_FORGET. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -pub fn do_fuse_batch_forget( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, -) -> u32 { - let batch_forget_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for batch_forget_in, {:?}", e); - return 0_u32; - } - }; - - for _i in 0..batch_forget_in.count as usize { - let forget_data_in = match reader.read_obj::(sys_mem) { - Ok(data) => data, - Err(e) => { - error!("Failed to read object for forget_date_in, {:?}", e); - return 0; - } - }; - - fs.lock() - .unwrap() - .forget(forget_data_in.ino as usize, forget_data_in.nlookup); - } - - 0_u32 -} - -/// Process the fuse message of FUSE_FALLOCATE. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_fallocate( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let fallocate_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for fallocate_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let ret = fs.lock().unwrap().fallocate( - fallocate_in.fh as usize, - fallocate_in.mode, - fallocate_in.offset, - fallocate_in.length, - ); - - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) -} - -/// Process the fuse message of FUSE_READDIRPLUS. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_readdirplus( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let read_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for readdirplus_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut buff = Vec::new(); - let ret = fs.lock().unwrap().readdir( - in_header.nodeid as usize, - read_in.fh as usize, - read_in.size, - read_in.offset, - true, - &mut buff, - ); - if ret == FUSE_OK { - reply_fuse_msg( - writer, - sys_mem, - in_header, - FUSE_OK, - Some(vec![FuseIovec::from_slice(buff.as_slice())]), - buff.len(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_LSEEK. -/// -/// # Arguments -/// -/// * `sys_mem` - Address space mapped with StratoVirt. -/// * `fs` - The management of userspace filesystem. -/// * `reader` - The read-only buffers parsed from the element of virtio queue. -/// * `writer` - The write-only buffers parsed from the element of virtio queue. -/// * `in_header` - The in_header reading from the read-only buffers. -pub fn do_fuse_lseek( - sys_mem: &Arc, - fs: Arc>, - reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - let lseek_in = match reader.read_obj::(sys_mem) { - Ok(d) => d, - Err(e) => { - error!("Failed to read object for lseek_in, {:?}", e); - return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); - } - }; - - let mut outoffset = 0_u64; - let ret = fs.lock().unwrap().lseek( - lseek_in.fh as usize, - lseek_in.offset, - lseek_in.whence, - &mut outoffset, - ); - - if ret == FUSE_OK { - let lseekout = FuseLseekOut { offset: outoffset }; - - reply_fuse_msg( - writer, - sys_mem, - in_header, - ret, - Some(vec![FuseIovec::from_obj(&lseekout)]), - mem::size_of::(), - ) - } else { - reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) - } -} - -/// Process the fuse message of FUSE_IOCTL. -/// Currently not supported, and ENOSYS is directly returned. -/// Normally the VM should not use ioctl to modify files, but it can be useful -/// in some cases. For example: to modify inode attrs, witch is required for per -/// inode DAX. We set aside the ioctl interface, and to implement it in the future -/// if needed. -pub fn do_fuse_ioctl( - sys_mem: &Arc, - _fs: Arc>, - _reader: &mut FuseBuffer, - writer: &mut FuseBuffer, - in_header: &FuseInHeader, -) -> u32 { - reply_fuse_msg(writer, sys_mem, in_header, libc::ENOSYS, None, 0_usize) -} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs deleted file mode 100644 index bb50e9de1..000000000 --- a/vhost_user_fs/src/fuse_req.rs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::sync::{Arc, Mutex}; - -use log::error; - -use super::fs::FileSystem; -use super::fuse_msg::*; -use super::fuse_proc::*; -use address_space::AddressSpace; -use virtio::Element; - -/// The request of fuse message parsed from virtio queue. -pub struct FuseReq { - desc_index: u16, - reader: FuseBuffer, - writer: FuseBuffer, -} - -impl FuseReq { - /// Construct a request of fuse message by the element from virtio queue. - /// - /// # Arguments - /// - /// * `elem` - The element parsed from virtio queue. - pub fn new(elem: &Element) -> Self { - FuseReq { - desc_index: elem.index, - reader: FuseBuffer::new(&elem.out_iovec), - writer: FuseBuffer::new(&elem.in_iovec), - } - } - - /// The function to deal with fuse message from the request. - /// - /// # Arguments - /// - /// * `sys_mem` - Address space mapped with StratoVirt. - /// * `fs` - The management of userspace filesystem. - pub fn execute( - &mut self, - sys_mem: &Arc, - fs: Arc>, - ) -> (u16, u32) { - let in_header = match self.reader.read_obj::(sys_mem) { - Ok(data) => data, - Err(err) => { - error!("Failed to read the header of fuse msg, {:?}", err); - return (self.desc_index, 0); - } - }; - - let written_len = match in_header.opcode { - FUSE_LOOKUP => { - do_fuse_lookup(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_FORGET => do_fuse_forget(sys_mem, fs, &mut self.reader, &in_header), - FUSE_GETATTR => do_fuse_getattr(sys_mem, fs, &mut self.writer, &in_header), - FUSE_SETATTR => { - do_fuse_setattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_READLINK => do_fuse_readlink(sys_mem, fs, &mut self.writer, &in_header), - FUSE_SYMLINK => { - do_fuse_symlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_MKNOD => { - do_fuse_mknod(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_MKDIR => { - do_fuse_mkdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_UNLINK => { - do_fuse_unlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_RMDIR => { - do_fuse_rmdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_RENAME => { - do_fuse_rename(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_LINK => do_fuse_link(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), - FUSE_OPEN => do_fuse_open(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), - FUSE_READ => do_fuse_read(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), - FUSE_WRITE => { - do_fuse_write(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_STATFS => do_fuse_statfs(sys_mem, fs, &mut self.writer, &in_header), - FUSE_RELEASE => { - do_fuse_release(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_FSYNC => { - do_fuse_fsync(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_SETXATTR => { - do_fuse_setxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_GETXATTR => { - do_fuse_getxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_LISTXATTR => { - do_fuse_listxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_REMOVEXATTR => { - do_fuse_removexattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_FLUSH => { - do_fuse_flush(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_INIT => do_fuse_init(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), - FUSE_OPENDIR => { - do_fuse_opendir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_READDIR => { - do_fuse_readdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_RELEASEDIR => { - do_fuse_releasedir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_FSYNCDIR => { - do_fuse_fsyncdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_GETLK => { - do_fuse_getlk(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_SETLK => { - do_fuse_setlk(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_SETLKW => { - do_fuse_setlkw(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_CREATE => { - do_fuse_create(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_DESTROY => do_fuse_destroy(sys_mem, fs, &mut self.writer, &in_header), - FUSE_BATCH_FORGET => do_fuse_batch_forget(sys_mem, fs, &mut self.reader), - FUSE_FALLOCATE => { - do_fuse_fallocate(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_READDIRPLUS => { - do_fuse_readdirplus(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_LSEEK => { - do_fuse_lseek(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - FUSE_IOCTL => { - do_fuse_ioctl(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) - } - _ => { - error!("The fuse msg {} is unsupported", in_header.opcode); - reply_fuse_msg( - &mut self.writer, - sys_mem, - &in_header, - libc::ENOSYS, - None, - 0_usize, - ) - } - }; - - // return the index of element and the length which is written - (self.desc_index, written_len) - } -} diff --git a/vhost_user_fs/src/main.rs b/vhost_user_fs/src/main.rs deleted file mode 100644 index de575d160..000000000 --- a/vhost_user_fs/src/main.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod cmdline; -pub mod error; -pub mod fs; -pub mod fs_ops; -pub mod fuse_msg; -pub mod fuse_proc; -pub mod fuse_req; -pub mod sandbox; -pub mod securecomputing; -pub mod vhost_user_fs; -pub mod vhost_user_server; -pub mod virtio_fs; - -use std::collections::HashSet; -use std::io::Write; -use std::sync::{Arc, Mutex}; - -use anyhow::{bail, Context, Result}; -use log::{error, info}; -use thiserror::Error; - -use crate::cmdline::{create_args_parser, create_fs_config, FsConfig}; -use crate::sandbox::Sandbox; -use crate::securecomputing::{seccomp_filter, string_to_seccompopt}; -use crate::vhost_user_fs::VhostUserFs; -use machine_manager::event_loop::EventLoop; -use machine_manager::signal_handler; -use machine_manager::temp_cleaner::TempCleaner; -use util::arg_parser::ArgMatches; -use util::{arg_parser, logger, seccomp::SeccompOpt}; - -#[derive(Error, Debug)] -enum MainError { - #[error("VhostUserFs")] - VhostUserFs { - #[from] - source: crate::error::VhostUserFsError, - }, - #[error("Util")] - Util { - #[from] - source: util::error::UtilError, - }, - #[error("Io")] - Io { - #[from] - source: std::io::Error, - }, -} - -trait ExitCode { - /// Returns the value to use as the exit status. - fn code(self) -> i32; -} - -impl ExitCode for i32 { - fn code(self) -> i32 { - self - } -} - -impl ExitCode for () { - fn code(self) -> i32 { - 0 - } -} - -fn main() { - ::std::process::exit(match run() { - Ok(ret) => ExitCode::code(ret), - Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) - .expect("Error writing to stderr"); - - 1 - } - }); -} - -fn parse_capabilities(cmd_args: &arg_parser::ArgMatches) -> Result> { - let mut add_caps = HashSet::new(); - - add_caps.insert("CHOWN".to_string()); - add_caps.insert("DAC_OVERRIDE".to_string()); - add_caps.insert("FOWNER".to_string()); - add_caps.insert("FSETID".to_string()); - add_caps.insert("SETGID".to_string()); - add_caps.insert("SETUID".to_string()); - add_caps.insert("MKNOD".to_string()); - add_caps.insert("SETFCAP".to_string()); - - if let Some(capabilities_str) = cmd_args.value_of("modcaps") { - let cut = &capabilities_str; - for s in cut.split(',').map(str::to_string) { - if s.is_empty() { - bail!("empty capability"); - } - let (addorsub, capability_literal) = s.split_at(1); - let capability = capability_literal.to_uppercase().to_string(); - if capng::name_to_capability(capability.as_str()).is_err() { - bail!("invalid capability {}", s); - } - match addorsub { - "+" => { - info!("add capability:{}", &capability); - add_caps.insert(capability); - } - "-" => { - info!("del capability:{}", &capability); - add_caps.remove(&capability); - } - _ => bail!("The first char before capability name must be + or - "), - } - } - } - Ok(add_caps) -} - -fn run() -> Result<()> { - let cmd_args = create_args_parser().get_matches()?; - - let logfile_path = cmd_args.value_of("display log").unwrap_or_default(); - logger::init_log(logfile_path)?; - - signal_handler::register_kill_signal(); - set_panic_hook(); - match real_main(&cmd_args) { - Ok(()) => info!("EventLoop over, Vm exit"), - Err(ref e) => { - error!("{:?}", e); - } - } - - Ok(()) -} - -fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { - TempCleaner::object_init(); - - let mut fsconfig: FsConfig = create_fs_config(cmd_args)?; - info!("FsConfig is {:?}", fsconfig); - - let source_dir = cmd_args.value_of("source dir").unwrap(); - let mut sandbox = Sandbox::new(source_dir); - if let Some(sandbox_value) = cmd_args.value_of("sandbox") { - match sandbox_value.as_str() { - "chroot" => sandbox.enable_chroot(), - "namespace" => sandbox.enable_namespace(), - _ => Ok(()), - }?; - }; - if sandbox.proc_self_fd.is_some() { - fsconfig.proc_dir_opt = sandbox.proc_self_fd; - } - - if let Some(seccomp) = cmd_args.value_of("seccomp") { - let seccomp_opt = string_to_seccompopt(seccomp); - match seccomp_opt { - SeccompOpt::Allow => {} - _ => seccomp_filter(seccomp_opt).unwrap(), - } - } - update_capabilities(cmd_args)?; - EventLoop::object_init(&None)?; - - let vhost_user_fs = Arc::new(Mutex::new( - VhostUserFs::new(fsconfig).with_context(|| "Failed to create vhost use fs")?, - )); - EventLoop::set_manager(vhost_user_fs.clone(), None); - - vhost_user_fs - .lock() - .unwrap() - .add_event_notifier() - .with_context(|| "Failed to add event")?; - - EventLoop::loop_run().with_context(|| "EventLoop exits unexpectedly: error occurs")?; - Ok(()) -} - -fn update_capabilities(cmd_args: &ArgMatches) -> Result<()> { - let add = parse_capabilities(cmd_args)?; - capng::clear(capng::Set::BOTH); - if let Err(e) = capng::updatev( - capng::Action::ADD, - capng::Type::PERMITTED | capng::Type::EFFECTIVE, - add.iter().map(String::as_str).collect(), - ) { - bail!("can't set up the child capabilities: {}", e); - } - if let Err(e) = capng::apply(capng::Set::BOTH) { - bail!("can't apply the child capabilities: {}", e); - } - Ok(()) -} - -fn set_panic_hook() { - std::panic::set_hook(Box::new(|panic_msg| { - TempCleaner::clean(); - let panic_file = panic_msg.location().map_or("", |loc| loc.file()); - let panic_line = panic_msg.location().map_or(0, |loc| loc.line()); - if let Some(msg) = panic_msg.payload().downcast_ref::<&str>() { - error!("Panic at [{}: {}]: {}.", panic_file, panic_line, msg); - } else { - error!("Panic at [{}: {}].", panic_file, panic_line); - } - })); -} diff --git a/vhost_user_fs/src/sandbox.rs b/vhost_user_fs/src/sandbox.rs deleted file mode 100644 index 115057658..000000000 --- a/vhost_user_fs/src/sandbox.rs +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::ffi::CString; -use std::fs; -use std::fs::File; -use std::os::unix::io::FromRawFd; - -use anyhow::{bail, Result}; -use log::error; - -/// Sandbox mechanism to isolate process. -pub struct Sandbox { - /// Source directory in host which can be accessed by guest. - pub source_dir: String, - /// File object for /proc/self/fd. - pub proc_self_fd: Option, -} - -impl Sandbox { - pub fn new(source_dir: String) -> Self { - Sandbox { - source_dir, - proc_self_fd: None, - } - } - - /// In "chroot" sandbox mode. - /// The program invokes chroot(2) to make the shared directory tree its root. - pub fn enable_chroot(&mut self) -> Result<()> { - if unsafe { libc::geteuid() } != 0 { - bail!("chroot/setgroups must be privileged user"); - } - - let cstr = CString::new("/proc/self/fd").unwrap(); - let open_ans = unsafe { libc::open(cstr.as_ptr(), libc::O_PATH) }; - if open_ans == -1 { - bail!("open /proc/self/fd failed"); - } - self.proc_self_fd = Some(unsafe { File::from_raw_fd(open_ans) }); - - drop_groups()?; - - let source_dir = CString::new(self.source_dir.clone()).unwrap(); - if unsafe { libc::chroot(source_dir.as_ptr()) } == -1 { - bail!("change root fail"); - } - let root_dir = CString::new("/").unwrap(); - if unsafe { libc::chdir(root_dir.as_ptr()) } == -1 { - bail!("change root directory fail"); - } - Ok(()) - } - - /// In "namespace" sandbox mode. - /// The program switches into a new file system namespace and invokes pivot_root(2) to make the - /// shared directory tree its root. - pub fn enable_namespace(&mut self) -> Result<()> { - let mut flags = libc::CLONE_NEWPID | libc::CLONE_NEWNS | libc::CLONE_NEWNET; - let euid = unsafe { libc::geteuid() }; - let egid = unsafe { libc::getegid() }; - if euid == 0 { - // An unprivileged user do not have permission to call setgroups. - drop_groups()?; - } else { - flags |= libc::CLONE_NEWUSER; - } - - if unsafe { libc::unshare(flags) } == -1 { - bail!("unshare fail"); - } - let pid = unsafe { libc::getpid() }; - // Get parent's pid and wrap it in file Object to ensure that is auto-closed. - let pidfd_open = unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) as libc::c_int }; - if pidfd_open == -1 { - bail!("pidfd_open fail"); - } - struct PidFd(File); - let _pidfd = unsafe { PidFd(File::from_raw_fd(pidfd_open)) }; - - let fork_ans = unsafe { libc::fork() }; - match fork_ans { - -1 => bail!("fork fail"), - 0 => self.do_namespace_in_child_process(euid, egid, pidfd_open)?, - _ => self.parent_process_wait_child(fork_ans)?, - } - Ok(()) - } - - fn do_namespace_in_child_process( - &mut self, - euid: u32, - egid: u32, - pidfd_open: i32, - ) -> Result<()> { - // If vhost_user_fs/src/set_signal_handlers do not register SIGTERM. - // Child process became orphan process when parent process died. - // Because child process can not receive signal notification from parent process. - // This is the signal that the calling process will get when its parent died. - if unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) } == -1 { - bail!("prctl fail"); - } - // The parent maybe died before before prctl. - let mut pollfd = libc::pollfd { - fd: pidfd_open, - events: libc::POLLIN, - revents: 0, - }; - let poll_ans = unsafe { libc::poll(&mut pollfd, 1, 0) }; - if poll_ans == -1 { - bail!("pollfd fail"); - } else if poll_ans != 0 { - bail!("original parent process died"); - } - // An unprivileged user set uid/gid mapping in user namespace. - if euid != 0 { - self.id_mapping(euid, egid); - } - // Open fd to '/proc/self' so we can later open '/proc/self/mountinfo'. - let cstr = CString::new("/proc/self").unwrap(); - let open_fd = unsafe { libc::open(cstr.as_ptr(), libc::O_PATH) }; - if open_fd < 0 { - bail!("open /proc/self fail"); - } - // Changing into file object to ensure it will closed when this function returns. - let _open_fd_file = unsafe { File::from_raw_fd(open_fd) }; - // Ensure changes in child mount namespace do not affect parent mount namespace. - self.change_propagation()?; - // mount /proc in this context. - self.mount_proc()?; - // Bind-mount '/proc/self/fd' onto '/proc' to preventing access to ancestor directories. - self.proc_self_fd_bind_proc()?; - // Bind-mount 'source_dir' on itself so we can use as new root on 'pivot_root'. - self.bind_source_dir()?; - // Get a fd to old root. - let cstr = CString::new("/").unwrap(); - let root_dir_fd = unsafe { - libc::open( - cstr.as_ptr(), - libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC, - ) - }; - if root_dir_fd < 0 { - bail!("open root_dir fail"); - } - // Get a fd to new root. - let cstr = CString::new(self.source_dir.as_str()).unwrap(); - let source_dir_fd = unsafe { - libc::open( - cstr.as_ptr(), - libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC, - ) - }; - if source_dir_fd < 0 { - bail!("open source_dir fail"); - } - // Switch to new root then call pivot_root. - if unsafe { libc::fchdir(source_dir_fd) } == -1 { - bail!("fchdir fail"); - } - // Use '.' as both old and new root. - let cstr = CString::new(".").unwrap(); - if unsafe { libc::syscall(libc::SYS_pivot_root, cstr.as_ptr(), cstr.as_ptr()) } == -1 { - bail!("pivot_root fail"); - } - // Switch to old root then umount it. - if unsafe { libc::fchdir(root_dir_fd) } == -1 { - bail!("change to root_dir fail"); - } - // Clean up old root to avoid mount namespace propagation. - self.clean_old_root()?; - // Umount old root. - let cstr = CString::new(".").unwrap(); - if unsafe { libc::umount2(cstr.as_ptr(), libc::MNT_DETACH) } == -1 { - bail!("umount2 old root fail"); - } - if unsafe { libc::fchdir(source_dir_fd) } == -1 { - bail!("change to root_dir fail"); - } - if unsafe { libc::close(source_dir_fd) } == -1 { - bail!("close source_dir fail"); - } - if unsafe { libc::close(root_dir_fd) } == -1 { - bail!("close root_dir fail"); - } - Ok(()) - } - - pub fn parent_process_wait_child(&self, fork_ans: i32) -> Result<()> { - capng::clear(capng::Set::BOTH); - if let Err(err) = capng::apply(capng::Set::BOTH) { - error!("apply fail {:?}", err); - } - - let mut wstatus = 0; - if fork_ans != unsafe { libc::waitpid(fork_ans, &mut wstatus, 0) } { - bail!("waitpid fail"); - } - let exit_code = if libc::WIFEXITED(wstatus) { - // The child terminated normally return true. - libc::WEXITSTATUS(wstatus) - } else if libc::WIFSIGNALED(wstatus) { - // Child process was terminated by a signal return true. - let signal = libc::WTERMSIG(wstatus); - error!("Child process was terminated by a signal: {}", signal); - -signal - } else { - error!("exit failed: {:#X}", wstatus); - libc::EXIT_FAILURE - }; - bail!("exit_code {}", exit_code); - } - - pub fn clean_old_root(&self) -> Result<()> { - let cstr = CString::new("").unwrap(); - let cstr2 = CString::new(".").unwrap(); - if unsafe { - libc::mount( - cstr.as_ptr(), - cstr2.as_ptr(), - cstr.as_ptr(), - libc::MS_SLAVE | libc::MS_REC, - std::ptr::null(), - ) - } == -1 - { - bail!("changing the propagation type of mounts in the new namespace fail"); - } - Ok(()) - } - - pub fn bind_source_dir(&self) -> Result<()> { - let cstr = CString::new(self.source_dir.as_str()).unwrap(); - let cstr2 = CString::new("").unwrap(); - if unsafe { - libc::mount( - cstr.as_ptr(), - cstr.as_ptr(), - cstr2.as_ptr(), - libc::MS_BIND | libc::MS_REC, - std::ptr::null(), - ) - } == -1 - { - bail!("mount --bind source_dir source_dir fail"); - } - Ok(()) - } - - fn proc_self_fd_bind_proc(&mut self) -> Result<()> { - let cstr = CString::new("/proc/self/fd").unwrap(); - let cstr2 = CString::new("/proc").unwrap(); - let cstr3 = CString::new("").unwrap(); - if unsafe { - libc::mount( - cstr.as_ptr(), - cstr2.as_ptr(), - cstr3.as_ptr(), - libc::MS_BIND, - std::ptr::null(), - ) - } == -1 - { - bail!("mount --bind /proc/self/fd /proc fail"); - } - - let cstr = CString::new("/proc").unwrap(); - let open_ans = unsafe { libc::open(cstr.as_ptr(), libc::O_PATH) }; - if open_ans == -1 { - bail!("open /proc failed"); - } - self.proc_self_fd = Some(unsafe { File::from_raw_fd(open_ans) }); - Ok(()) - } - - fn mount_proc(&self) -> Result<()> { - let cstr = CString::new("proc").unwrap(); - let cstr2 = CString::new("/proc").unwrap(); - let cstr3 = CString::new("proc").unwrap(); - if unsafe { - libc::mount( - cstr.as_ptr(), - cstr2.as_ptr(), - cstr3.as_ptr(), - libc::MS_NODEV | libc::MS_NOEXEC | libc::MS_NOSUID | libc::MS_RELATIME, - std::ptr::null(), - ) - } == -1 - { - bail!("mount /proc fail"); - } - Ok(()) - } - - fn change_propagation(&self) -> Result<()> { - let cstr = CString::new("").unwrap(); - let cstr2 = CString::new("/").unwrap(); - if unsafe { - libc::mount( - cstr.as_ptr(), - cstr2.as_ptr(), - cstr.as_ptr(), - libc::MS_SLAVE | libc::MS_REC, - std::ptr::null(), - ) - } == -1 - { - bail!("changing the propagation type of mounts in the new namespace fail"); - } - Ok(()) - } - - fn id_mapping(&self, euid: u32, egid: u32) { - // The setgroups file can only be written to before the group-ID mapping has been set. - let _result1 = fs::write("/proc/self/setgroups", "deny"); - // Format: id_in_namespace id_out_namespace length. - let uid_map_string = format!("{} {} {}", euid, euid, 1); - let gid_map_string = format!("{} {} {}", egid, egid, 1); - let _result2 = fs::write("/proc/self/uid_map", uid_map_string); - let _result3 = fs::write("/proc/self/gid_map", gid_map_string); - } -} - -pub fn drop_groups() -> Result<()> { - // The total number of supplementary group IDs for the process is returned. - let group_num = unsafe { libc::getgroups(0, std::ptr::null_mut()) }; - if group_num == -1 { - bail!("getgroups fail"); - } else if group_num > 0 { - // Sets the supplementary group IDs for the calling process. Appropriate privileges are - // required. A process can drop all of its supplementary groups with the - // call:setgroups(0, NULL). - if unsafe { libc::setgroups(0, std::ptr::null()) } == -1 { - bail!("setgroups fail"); - } - } - Ok(()) -} diff --git a/vhost_user_fs/src/securecomputing.rs b/vhost_user_fs/src/securecomputing.rs deleted file mode 100644 index f6178b80d..000000000 --- a/vhost_user_fs/src/securecomputing.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{Context, Result}; - -use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; - -fn syscall_whitelist() -> Vec { - let mut v = vec![libc::SYS_accept4]; - v.push(libc::SYS_brk); - v.push(libc::SYS_bind); - v.push(libc::SYS_capget); - v.push(libc::SYS_capset); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_chmod); - v.push(libc::SYS_clock_gettime); - v.push(libc::SYS_clone); - v.push(libc::SYS_clone3); - v.push(libc::SYS_close); - v.push(libc::SYS_copy_file_range); - v.push(libc::SYS_dup); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_epoll_create); - v.push(libc::SYS_epoll_create1); - v.push(libc::SYS_epoll_ctl); - v.push(libc::SYS_epoll_pwait); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_epoll_wait); - v.push(libc::SYS_eventfd2); - v.push(libc::SYS_exit); - v.push(libc::SYS_exit_group); - v.push(libc::SYS_fallocate); - v.push(libc::SYS_fchdir); - v.push(libc::SYS_fchmod); - v.push(libc::SYS_fchmodat); - v.push(libc::SYS_fchownat); - v.push(libc::SYS_fcntl); - v.push(libc::SYS_fdatasync); - v.push(libc::SYS_fgetxattr); - v.push(libc::SYS_flistxattr); - v.push(libc::SYS_flock); - v.push(libc::SYS_fremovexattr); - v.push(libc::SYS_fsetxattr); - v.push(libc::SYS_fstat); - v.push(libc::SYS_fstatfs); - v.push(libc::SYS_fsync); - v.push(libc::SYS_ftruncate); - v.push(libc::SYS_futex); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_getdents); - v.push(libc::SYS_getdents64); - v.push(libc::SYS_getegid); - v.push(libc::SYS_geteuid); - v.push(libc::SYS_getpid); - v.push(libc::SYS_getrandom); - v.push(libc::SYS_gettid); - v.push(libc::SYS_gettimeofday); - v.push(libc::SYS_getxattr); - v.push(libc::SYS_linkat); - v.push(libc::SYS_listen); - v.push(libc::SYS_listxattr); - v.push(libc::SYS_lseek); - v.push(libc::SYS_madvise); - v.push(libc::SYS_mkdirat); - v.push(libc::SYS_mknodat); - v.push(libc::SYS_mmap); - v.push(libc::SYS_mprotect); - v.push(libc::SYS_mremap); - v.push(libc::SYS_munmap); - v.push(libc::SYS_name_to_handle_at); - v.push(libc::SYS_newfstatat); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_open); - v.push(libc::SYS_openat); - v.push(libc::SYS_open_by_handle_at); - v.push(libc::SYS_prctl); - v.push(libc::SYS_preadv); - v.push(libc::SYS_pread64); - v.push(libc::SYS_pwritev); - v.push(libc::SYS_pwrite64); - v.push(libc::SYS_read); - v.push(libc::SYS_readlinkat); - v.push(libc::SYS_recvmsg); - v.push(libc::SYS_renameat); - v.push(libc::SYS_renameat2); - v.push(libc::SYS_removexattr); - v.push(libc::SYS_rt_sigaction); - v.push(libc::SYS_rt_sigprocmask); - v.push(libc::SYS_rt_sigreturn); - v.push(libc::SYS_sched_getaffinity); - v.push(libc::SYS_sendmsg); - v.push(libc::SYS_sendto); - v.push(libc::SYS_setresgid); - v.push(libc::SYS_setresuid); - v.push(libc::SYS_set_robust_list); - v.push(libc::SYS_setxattr); - v.push(libc::SYS_sigaltstack); - v.push(libc::SYS_socket); - v.push(libc::SYS_statx); - v.push(libc::SYS_symlinkat); - v.push(libc::SYS_syncfs); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_time); - v.push(libc::SYS_tgkill); - v.push(libc::SYS_umask); - #[cfg(target_arch = "x86_64")] - v.push(libc::SYS_unlink); - v.push(libc::SYS_unlinkat); - v.push(libc::SYS_unshare); - v.push(libc::SYS_utimensat); - v.push(libc::SYS_write); - v.push(libc::SYS_writev); - v -} - -/// Enable seccomp to limit syscall. -/// -/// # Arguments -/// -/// * `action` - The default action. -pub fn seccomp_filter(action: SeccompOpt) -> Result<()> { - let mut seccomp_filter = SyscallFilter::new(action); - let allowed_syscalls = syscall_whitelist(); - for call in allowed_syscalls { - seccomp_filter.push(&mut BpfRule::new(call)); - } - seccomp_filter - .realize() - .with_context(|| "Failed to realize seccomp filter.")?; - Ok(()) -} - -pub fn string_to_seccompopt(string: String) -> SeccompOpt { - let str = string.as_str(); - match str { - "kill" => SeccompOpt::Kill, - "log" => SeccompOpt::Log, - "trap" => SeccompOpt::Trap, - "allow" => SeccompOpt::Allow, - _ => SeccompOpt::Kill, - } -} diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs deleted file mode 100644 index d80d02e87..000000000 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::{ - rc::Rc, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, -}; - -use anyhow::{Context, Result}; -use log::error; -use vmm_sys_util::epoll::EventSet; - -use super::cmdline::FsConfig; -use super::fs::set_rlimit_nofile; -use super::vhost_user_server::VhostUserServerHandler; -use super::virtio_fs::VirtioFs; -use machine_manager::{event_loop::EventLoop, temp_cleaner::TempCleaner}; -use util::loop_context::{ - EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, -}; - -/// The vhost-user filesystem device contains virtio fs device and the vhost-user -/// server which can be connected with the vhost-user client in StratoVirt. -#[derive(Clone)] -pub struct VhostUserFs { - /// Used to communicate with StratoVirt. - server_handler: VhostUserServerHandler, - /// Used to determine whether the process should be terminated. - should_exit: Arc, -} - -trait CreateEventNotifier { - fn create_event_notifier( - &mut self, - server_handler: Arc>, - ) -> Option>; -} - -impl CreateEventNotifier for VhostUserServerHandler { - fn create_event_notifier( - &mut self, - server_handler: Arc>, - ) -> Option> { - if let Err(e) = self.sock.domain.accept() { - error!("Failed to accept the socket for vhost user server, {:?}", e); - return None; - } - - let mut notifiers = Vec::new(); - - let should_exit = self.should_exit.clone(); - let handler: Rc = Rc::new(move |event, _| { - if event == EventSet::IN { - let mut lock_server_handler = server_handler.lock().unwrap(); - if let Err(e) = lock_server_handler.handle_request() { - error!("Failed to handle request for vhost user server, {:?}", e); - } - } - if event & EventSet::HANG_UP == EventSet::HANG_UP { - should_exit.store(true, Ordering::Release); - } - None - }); - let notifier = EventNotifier::new( - NotifierOperation::AddShared, - self.sock.domain.get_stream_raw_fd(), - None, - EventSet::IN | EventSet::HANG_UP, - vec![handler], - ); - notifiers.push(notifier); - - Some(notifiers) - } -} - -impl EventNotifierHelper for VhostUserServerHandler { - fn internal_notifiers(server_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let server_handler_clone = server_handler.clone(); - let handler: Rc = Rc::new(move |_, _| { - server_handler_clone - .lock() - .unwrap() - .create_event_notifier(server_handler_clone.clone()) - }); - let notifier = EventNotifier::new( - NotifierOperation::AddShared, - server_handler - .lock() - .unwrap() - .sock - .domain - .get_listener_raw_fd(), - None, - EventSet::IN, - vec![handler], - ); - notifiers.push(notifier); - - notifiers - } -} - -impl VhostUserFs { - /// Create a new vhost-user filesystem device. - /// - /// # Arguments - /// - /// * `fs_config` - Configuration of the vhost-user filesystem device. - pub fn new(fs_config: FsConfig) -> Result { - let should_exit = Arc::new(AtomicBool::new(false)); - - if let Some(limit) = fs_config.rlimit_nofile { - set_rlimit_nofile(limit) - .with_context(|| format!("Failed to set rlimit nofile {}", limit))?; - } - - let sock_path = fs_config.sock_path.clone(); - let virtio_fs = Arc::new(Mutex::new( - VirtioFs::new(fs_config).with_context(|| "Failed to create virtio fs")?, - )); - - let server_handler = - VhostUserServerHandler::new(sock_path.as_str(), virtio_fs, should_exit.clone()) - .with_context(|| "Failed to create vhost user server")?; - - Ok(VhostUserFs { - server_handler, - should_exit, - }) - } - - /// Add events to epoll handler for the vhost-user filesystem device. - pub fn add_event_notifier(&self) -> Result<()> { - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new( - self.server_handler.clone(), - ))), - None, - )?; - - Ok(()) - } -} - -impl EventLoopManager for VhostUserFs { - fn loop_should_exit(&self) -> bool { - self.should_exit.load(Ordering::Acquire) - } - - fn loop_cleanup(&self) -> util::Result<()> { - TempCleaner::clean(); - Ok(()) - } -} diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs deleted file mode 100644 index f79534a42..000000000 --- a/vhost_user_fs/src/vhost_user_server.rs +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::mem::size_of; -use std::os::unix::io::RawFd; -use std::slice; -use std::sync::{atomic::AtomicBool, Arc, Mutex}; - -use anyhow::{bail, Context, Result}; -use log::error; - -use machine_manager::temp_cleaner::TempCleaner; -use util::unix::limit_permission; -use virtio::vhost::user::{ - RegionMemInfo, VhostUserHdrFlag, VhostUserMemHdr, VhostUserMsgHdr, VhostUserMsgReq, - VhostUserVringAddr, VhostUserVringState, MAX_ATTACHED_FD_ENTRIES, -}; -use virtio::VhostUser::VhostUserSock; - -/// The trait for dealing with vhost-user request in the server. -pub trait VhostUserReqHandler: Send + Sync { - /// Set the current process as the owner of this file descriptor. - fn set_owner(&mut self) -> Result<()>; - - /// Get a bitmask of supported virtio/vhost features. - fn get_features(&self) -> Result; - - /// Inform the vhost subsystem which features to enable. - /// - /// # Arguments - /// - /// * `features` - The features from the vhost-user client in StratoVirt. - fn set_features(&mut self, features: u64) -> Result<()>; - - /// Set the guest memory mappings for vhost to use. - /// - /// # Arguments - /// - /// * `regions` - The slice of memory region information for the message of memory table. - /// * `fds` - The files descriptors are used to map shared memory for the process and - /// StratoVirt. - fn set_mem_table(&mut self, regions: &[RegionMemInfo], fds: &[RawFd]) -> Result<()>; - - /// Set the size of descriptors in the virtio queue. - /// - /// # Arguments - /// - /// * `queue_index` - The index of virtio queue. - /// * `num` - The total size of virtio queue. - fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>; - - /// Set the addresses for a given virtio queue. - /// - /// # Arguments - /// - /// * `queue_index` - The index of virtio queue. - /// * `flags` - Option flags. - /// * `desc_table` - The start address of descriptor table. - /// * `used_ring` - The start address of used ring. - /// * `avail_ring` - The start address of avail ring. - /// * `log` - The start address of log. - fn set_vring_addr( - &mut self, - queue_index: usize, - flags: u32, - desc_table: u64, - used_ring: u64, - avail_ring: u64, - log: u64, - ) -> Result<()>; - - /// Set the first index to look for available descriptors. - /// - /// # Arguments - /// - /// * `queue_index` - The index of virtio queue. - /// * `num` - the first index to look for available descriptors. - fn set_vring_base(&mut self, queue_index: usize, num: u16) -> Result<()>; - - /// Set the eventfd to trigger when buffers need to be processed - /// by the guest. - /// - /// # Arguments - /// - /// * `queue_index` - The index of virtio queue. - /// * `fd` - The files descriptor used to notify the guest. - fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; - - /// Set the eventfd that will be signaled by the guest when buffers - /// need to be processed by the host. - /// - /// # Arguments - /// - /// * `queue_index` - The index of virtio queue. - /// * `fd` - The files descriptor used to notify the host. - fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; - - /// set the status of virtio queue. - /// - /// # Arguments - /// - /// * `queue_index` - The index of virtio queue. - /// * `status` - The status of virtio queue. - fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()>; -} - -/// The vhost-user server handler can communicate with StratoVirt and set the data of requests -/// to the backend. -#[derive(Clone)] -pub struct VhostUserServerHandler { - /// The information of socket used to communicate with StratoVirt. - pub sock: VhostUserSock, - /// The backend used to save the data of requests from StratoVirt. - backend: Arc>, - /// Used to determine whether the process should be terminated. - pub should_exit: Arc, -} - -fn close_fds(fds: Vec) { - for fd in fds { - let _ = unsafe { libc::close(fd) }; - } -} - -fn is_invalid_fds(hdr: &mut VhostUserMsgHdr, rfds: Option>) -> Result<()> { - match VhostUserMsgReq::from(hdr.request) { - VhostUserMsgReq::SetMemTable => Ok(()), - VhostUserMsgReq::SetVringCall => Ok(()), - VhostUserMsgReq::SetVringKick => Ok(()), - VhostUserMsgReq::SetSlaveReqFd => Ok(()), - _ => { - if rfds.is_some() { - if let Some(fds) = rfds { - close_fds(fds); - } - bail!("The fds is invalid, request: {}", hdr.request); - } else { - Ok(()) - } - } - } -} - -impl VhostUserServerHandler { - /// Construct a vhost-user server handler - /// - /// # Arguments - /// - /// * `path` - The path of unix socket file which communicates with StratoVirt. - /// * `backend` - The trait of backend used to save the data of requests from StratoVirt. - pub fn new( - path: &str, - backend: Arc>, - should_exit: Arc, - ) -> Result { - let mut sock = VhostUserSock::new(path); - sock.domain - .bind(false) - .with_context(|| format!("Failed to bind for vhost user server {}", path))?; - TempCleaner::add_path(path.to_string()); - limit_permission(path).with_context(|| format!("Failed to limit permission {}", path))?; - - Ok(VhostUserServerHandler { - sock, - backend, - should_exit, - }) - } - - fn recv_hdr_and_fds(&mut self) -> Result<(VhostUserMsgHdr, Option>)> { - let mut hdr = VhostUserMsgHdr::default(); - let body_opt: Option<&mut u32> = None; - let payload_opt: Option<&mut [u8]> = None; - let mut fds = vec![0; MAX_ATTACHED_FD_ENTRIES]; - - let (rcv_len, fds_num) = self - .sock - .recv_msg(Some(&mut hdr), body_opt, payload_opt, &mut fds) - .with_context(|| "Failed to recv hdr and fds")?; - - if rcv_len != size_of::() { - bail!( - "The received length {} is invalid, expect {}", - rcv_len, - size_of::() - ); - } else if hdr.is_invalid() { - bail!( - "The header of vhost user msg is invalid, request: {}, size: {}, flags: {}", - hdr.request, - hdr.size, - hdr.flags - ); - } - - let rfds = match fds_num { - 0 => None, - n => { - let mut fds_temp = Vec::with_capacity(n); - fds_temp.extend_from_slice(&fds[0..n]); - Some(fds_temp) - } - }; - - is_invalid_fds(&mut hdr, rfds.clone())?; - - Ok((hdr, rfds)) - } - - fn recv_body(&mut self, len: usize) -> Result<(usize, Vec)> { - let mut rbuf = vec![0u8; len]; - let body_opt: Option<&mut u32> = None; - let hdr_opt: Option<&mut VhostUserMsgHdr> = None; - - let (rcv_len, _) = self - .sock - .recv_msg(hdr_opt, body_opt, Some(&mut rbuf), &mut []) - .with_context(|| "Failed to recv msg body")?; - - if rcv_len != len { - bail!( - "The length of msg body {} is invalid, expected {}", - rcv_len, - len - ); - } - - Ok((rcv_len, rbuf)) - } - - fn get_msg_body<'a, D: Sized>( - &self, - hdr: &VhostUserMsgHdr, - buf: &'a [u8], - len: usize, - ) -> Result<&'a D> { - if !self.is_valid_request(hdr, len, size_of::()) { - bail!( - "Failed to get msg body for request {}, len {}, payload size {}, hdr.size {}", - hdr.request, - len, - size_of::(), - hdr.size - ); - } - - let body = unsafe { &*(buf.as_ptr() as *const D) }; - Ok(body) - } - - fn send_ack_msg(&mut self, request: u32, res: D, fds: &[RawFd]) -> Result<()> { - let hdr = VhostUserMsgHdr::new( - request, - VhostUserHdrFlag::Reply as u32, - size_of::() as u32, - ); - let payload_opt: Option<&[u8]> = None; - - self.sock - .send_msg(Some(&hdr), Some(&res), payload_opt, fds) - .with_context(|| "Failed to send ack msg")?; - - Ok(()) - } - - #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] - fn set_msg_mem_table( - &mut self, - hdr: &VhostUserMsgHdr, - buf: &[u8], - len: usize, - fds_opt: Option>, - ) -> Result<()> { - if len < size_of::() { - if let Some(fds) = fds_opt { - close_fds(fds); - } - bail!("The header length of mem table is invalid {}", len); - } - - let memhdrsize = size_of::(); - let memhdr = unsafe { &*(buf.as_ptr() as *const VhostUserMemHdr) }; - let total_size = (memhdr.nregions as usize * size_of::()) + memhdrsize; - if (hdr.size as usize) != total_size { - if let Some(fds) = fds_opt { - close_fds(fds); - } - bail!( - "The body length of mem table is invalid {}, expected {}", - total_size, - hdr.size, - ); - } - - let regions = unsafe { - slice::from_raw_parts( - buf.as_ptr().add(memhdrsize) as *const RegionMemInfo, - memhdr.nregions as usize, - ) - }; - - if let Some(fds) = fds_opt { - let fds_len = fds.len(); - if fds_len != (memhdr.nregions as usize) { - close_fds(fds); - bail!( - "The length of fds {} for mem table is invalid, expected {}", - fds_len, - memhdr.nregions - ); - } - self.backend.lock().unwrap().set_mem_table(regions, &fds)?; - } else { - bail!("The fds of mem table is null"); - } - - Ok(()) - } - - fn is_valid_request(&self, hdr: &VhostUserMsgHdr, size: usize, expected: usize) -> bool { - (hdr.size as usize == expected) && (size == expected) && !hdr.is_reply() - } - - fn process_request( - &mut self, - hdr: &VhostUserMsgHdr, - buf: &[u8], - len: usize, - rfds: Option>, - ) -> Result<()> { - match VhostUserMsgReq::from(hdr.request) { - VhostUserMsgReq::GetFeatures => { - if !self.is_valid_request(hdr, len, 0) { - bail!("Invalid request size of GetFeatures"); - } - - let features = self.backend.lock().unwrap().get_features()?; - if hdr.need_reply() { - self.send_ack_msg(VhostUserMsgReq::GetFeatures as u32, features, &[]) - .with_context(|| "Failed to send ack msg for getting features")?; - } - } - VhostUserMsgReq::SetFeatures => { - let features = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting features")?; - self.backend.lock().unwrap().set_features(*features)?; - } - VhostUserMsgReq::SetOwner => { - if !self.is_valid_request(hdr, len, 0) { - bail!("Invalid request size of SetOwner"); - } - - self.backend.lock().unwrap().set_owner()?; - } - VhostUserMsgReq::SetMemTable => { - let ret = match self.set_msg_mem_table(hdr, buf, len, rfds) { - Err(ref e) => { - error!("Failed to set mem table {:?}", e); - 1u64 - } - Ok(_) => 0u64, - }; - if hdr.need_reply() { - self.send_ack_msg(VhostUserMsgReq::SetMemTable as u32, ret, &[]) - .with_context(|| "Failed to send ack msg for setting mem table")?; - } - } - VhostUserMsgReq::SetVringNum => { - let vringstate = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting vring num")?; - self.backend - .lock() - .unwrap() - .set_vring_num(vringstate.index as usize, vringstate.value as u16)?; - } - VhostUserMsgReq::SetVringAddr => { - let vringaddr = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting vring addr")?; - self.backend.lock().unwrap().set_vring_addr( - vringaddr.index as usize, - vringaddr.flags, - vringaddr.desc_user_addr, - vringaddr.used_user_addr, - vringaddr.avail_user_addr, - vringaddr.log_guest_addr, - )?; - } - VhostUserMsgReq::SetVringBase => { - let vringstate = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting vring base")?; - self.backend - .lock() - .unwrap() - .set_vring_base(vringstate.index as usize, vringstate.value as u16)?; - } - VhostUserMsgReq::SetVringEnable => { - let vringstate = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting vring enable")?; - self.backend - .lock() - .unwrap() - .set_vring_enable(vringstate.index as usize, vringstate.value)?; - } - VhostUserMsgReq::SetVringKick => { - let index = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting vring kick")?; - if let Some(fds) = rfds { - let fds_len = fds.len(); - if fds_len != 1 { - close_fds(fds); - bail!("The length {} of fds for kicking is invalid", fds_len); - } - self.backend - .lock() - .unwrap() - .set_vring_kick(*index as usize, fds[0])?; - } else { - bail!("The length of fds for kicking is null"); - } - } - VhostUserMsgReq::SetVringCall => { - let index = self - .get_msg_body::(hdr, buf, len) - .with_context(|| "Failed to get msg body for setting vring call")?; - if let Some(fds) = rfds { - let fds_len = fds.len(); - if fds_len != 1 { - close_fds(fds); - bail!("The length {} of fds for calling is invalid", fds_len); - } - self.backend - .lock() - .unwrap() - .set_vring_call(*index as usize, fds[0])?; - } else { - bail!("The length of fds for calling is null"); - } - } - _ => { - bail!("The request {} is unknown", hdr.request); - } - }; - - Ok(()) - } - - /// The function used to process requests from StratoVirt. - pub fn handle_request(&mut self) -> Result<()> { - let (hdr, rfds) = self - .recv_hdr_and_fds() - .with_context(|| "Failed to recv header and fds")?; - - let (len, buf) = match hdr.size { - 0 => (0, vec![0u8; 0]), - _ => { - let (rcv_len, rbuf) = self - .recv_body(hdr.size as usize) - .with_context(|| "Failed to recv msg body")?; - (rcv_len, rbuf) - } - }; - - self.process_request(&hdr, &buf, len, rfds) - .with_context(|| format!("Failed to process the request {}", hdr.request))?; - Ok(()) - } -} diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs deleted file mode 100644 index 47943e90b..000000000 --- a/vhost_user_fs/src/virtio_fs.rs +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -/// The num of high priority queue. -const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; -/// The num of request queue. -const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; -/// The max queue size. -const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; -const VIRTIO_FS_VRING_IDX_MASK: usize = 0xff; -/// For VHOST_USER_SET_VRING_KICK and VHOST_USER_SET_VRING_CALL and VHOST_USER_SET_ -/// VRING_ERR, Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid -/// FD flag. This flag is set when there is no file descriptor in the ancillary data. -/// This signals that polling should be used instead of waiting for the kick. -use std::fs::File; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; - -use anyhow::{bail, Context, Result}; -use log::error; -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; - -use super::fs::FileSystem; -use super::fuse_req::FuseReq; -use super::vhost_user_server::VhostUserReqHandler; -use crate::cmdline::FsConfig; -use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; -use machine_manager::event_loop::EventLoop; -use util::loop_context::{ - gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, - NotifierOperation, -}; -use virtio::{ - vhost::user::RegionMemInfo, virtio_has_feature, Queue, QueueConfig, - VhostUser::VHOST_USER_F_PROTOCOL_FEATURES, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, -}; - -const VIRTIO_FS_VRING_NO_FD_MASK: usize = 0x1 << 8; - -struct FsIoHandler { - queue: Queue, - kick_evt: Arc, - call_evt: Arc, - mem_space: Arc, - driver_features: u64, - fs: Arc>, -} - -impl FsIoHandler { - fn new( - queue_config: QueueConfig, - kick_evt: Arc, - call_evt: Arc, - mem_space: &Arc, - driver_features: u64, - fs: Arc>, - ) -> Result { - let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING) - .with_context(|| "Failed to create virtual queue")?; - if !queue.is_valid(mem_space) { - bail!("Invalid queue for fs handler"); - } - - Ok(FsIoHandler { - queue, - kick_evt, - call_evt, - mem_space: mem_space.clone(), - driver_features, - fs, - }) - } - - fn process_queue(&mut self) -> Result<()> { - loop { - let elem = self - .queue - .vring - .pop_avail(&self.mem_space, self.driver_features) - .with_context(|| "Failed to pop avail ring for process virtiofs queue")?; - - if elem.desc_num == 0 { - break; - } - - let mut req = FuseReq::new(&elem); - let (index, len) = req.execute(&self.mem_space, self.fs.clone()); - self.queue.vring.add_used(&self.mem_space, index, len)?; - - if self - .queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { - self.call_evt - .write(1) - .with_context(|| "Failed to write call fd")?; - } - } - - Ok(()) - } - - fn delete_notifiers(&self) -> Vec { - gen_delete_notifiers(&[self.kick_evt.as_raw_fd()]) - } -} - -impl EventNotifierHelper for FsIoHandler { - fn internal_notifiers(fs_handler: Arc>) -> Vec { - let mut notifiers = Vec::new(); - - let fs_handler_clone = fs_handler.clone(); - let handler: Rc = Rc::new(move |_, fd: RawFd| { - read_fd(fd); - if let Err(e) = fs_handler_clone.lock().unwrap().process_queue() { - error!("Failed to process fuse msg, {:?}", e); - } - None - }); - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - fs_handler.lock().unwrap().kick_evt.as_raw_fd(), - None, - EventSet::IN, - vec![handler], - )); - - notifiers - } -} - -struct QueueInfo { - config: QueueConfig, - kick_evt: Option>, - call_evt: Option>, -} - -impl QueueInfo { - fn new(queue_size: u16) -> Self { - QueueInfo { - config: QueueConfig::new(queue_size), - kick_evt: None, - call_evt: None, - } - } -} - -struct VirtioFsConfig { - device_features: u64, - driver_features: u64, - queues_info: Vec, - mem_regions: Vec, -} - -impl VirtioFsConfig { - fn new() -> Self { - let device_features = 1_u64 << VIRTIO_F_VERSION_1 - | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC - | 1_u64 << VIRTIO_F_RING_EVENT_IDX; - - let mut queues_info = Vec::new(); - for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { - queues_info.push(QueueInfo::new(VIRTIO_FS_MAX_QUEUE_SIZE)); - } - - VirtioFsConfig { - device_features, - driver_features: 0_u64, - queues_info, - mem_regions: Vec::new(), - } - } - - fn get_mut_queue_config(&mut self, queue_index: usize) -> Result<&mut QueueInfo> { - self.queues_info - .get_mut(queue_index) - .with_context(|| format!("The select index of queue {} overflows", queue_index)) - } -} - -/// The virtio fs device contains the configuration of virtio fs, the management of -/// userspace filesystem and the handler used to process requests in virtio queue -/// from the guest. -pub struct VirtioFs { - /// The config of virtio-fs. - config: VirtioFsConfig, - /// Fs handlers of I/O request. - fs_handlers: Vec>>>, - /// Address space mapped witch StratoVirt. - sys_mem: Arc, - /// File system used to store inode and file information. - fs: Arc>, - /// The quest memory region information. - mem_info: Vec, -} - -impl VirtioFs { - /// Construct a virtio fs device by the path of source directory shared in host. - /// - /// # Arguments - /// - /// * `source_dir` - The path of source directory shared in host. - pub fn new(fs_config: FsConfig) -> Result { - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "VirtioFsMem"), - "VirtioFsMem", - None, - ) - .with_context(|| "Failed to create address space")?; - - let mut fs_handlers = Vec::new(); - for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { - fs_handlers.push(None); - } - - let fs = Arc::new(Mutex::new( - FileSystem::new(fs_config).with_context(|| "Failed to create file system")?, - )); - - Ok(VirtioFs { - config: VirtioFsConfig::new(), - fs_handlers, - sys_mem, - fs, - mem_info: Vec::new(), - }) - } - - fn get_guest_address(&self, addr: u64) -> Result { - for (_, info) in self.mem_info.iter().enumerate() { - if addr >= info.userspace_addr && addr < info.userspace_addr + info.memory_size { - return Ok(info.guest_phys_addr + addr - info.userspace_addr); - } - } - - bail!("Failed to find the guest address for addr: 0x{:X}", addr); - } - - fn register_fs_handler(&mut self, queue_index: usize) -> Result<()> { - // Before setting up new notifiers, we should remove old ones. - self.unregister_fs_handler(queue_index)?; - - let driver_features = self.config.driver_features; - let mut queue_info = self.config.get_mut_queue_config(queue_index)?; - if queue_info.kick_evt.is_none() || queue_info.call_evt.is_none() { - bail!( - "The event for kicking {} or calling {} is none", - queue_info.kick_evt.is_none(), - queue_info.call_evt.is_none(), - ); - } - queue_info.config.ready = true; - - let fs_handler = Arc::new(Mutex::new( - FsIoHandler::new( - queue_info.config, - queue_info.kick_evt.as_ref().unwrap().clone(), - queue_info.call_evt.as_ref().unwrap().clone(), - &self.sys_mem, - driver_features, - self.fs.clone(), - ) - .with_context(|| "Failed to create fs handler")?, - )); - - self.fs_handlers[queue_index] = Some(fs_handler.clone()); - EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) - .with_context(|| "Failed to update event for queue status which is ready")?; - - Ok(()) - } - - fn unregister_fs_handler(&mut self, queue_index: usize) -> Result<()> { - if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { - EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) - .with_context(|| "Failed to update event for queue status which is not ready")?; - }; - let mut queue_info = self.config.get_mut_queue_config(queue_index)?; - queue_info.config.ready = false; - - Ok(()) - } -} - -impl VhostUserReqHandler for VirtioFs { - fn set_owner(&mut self) -> Result<()> { - Ok(()) - } - - fn get_features(&self) -> Result { - Ok(self.config.device_features) - } - - fn set_features(&mut self, features: u64) -> Result<()> { - self.config.driver_features = features; - Ok(()) - } - - fn set_mem_table(&mut self, regions: &[RegionMemInfo], fds: &[RawFd]) -> Result<()> { - if !self.config.mem_regions.is_empty() { - for region in &self.config.mem_regions { - if let Err(e) = self.sys_mem.root().delete_subregion(region) { - error!("Failed to delete subregion for setting mem table, {:?}", e); - } - } - self.config.mem_regions = Vec::new(); - } - - self.mem_info = regions.to_vec(); - - for (index, region_config) in regions.iter().enumerate() { - let file = unsafe { File::from_raw_fd(fds[index]) }; - let fileback = FileBackend { - file: Arc::new(file), - offset: region_config.mmap_offset, - page_size: 0_u64, - }; - - let mmap = Arc::new( - HostMemMapping::new( - GuestAddress(region_config.guest_phys_addr), - None, - region_config.memory_size, - Some(fileback), - false, - true, - false, - ) - .with_context(|| - format!("Failed to create the mapping of host memory for setting mem table, addr: 0x{:X}, size: {}, offset: {}", - region_config.guest_phys_addr, region_config.memory_size, region_config.mmap_offset, - ) - )? - ); - - let region = Region::init_ram_region(mmap.clone(), "VirtioFsRam"); - self.sys_mem - .root() - .add_subregion(region.clone(), mmap.start_address().raw_value()) - .with_context(|| "Failed to add subregion for setting mem table")?; - - self.config.mem_regions.push(region); - } - - Ok(()) - } - - fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()> { - self.config - .get_mut_queue_config(queue_index) - .map(|queue_info| { - queue_info.config.size = num; - }) - .with_context(|| { - format!( - "Failed to set vring num, index: {}, num: {}", - queue_index, num, - ) - })?; - Ok(()) - } - - fn set_vring_addr( - &mut self, - queue_index: usize, - _flags: u32, - desc_table: u64, - used_ring: u64, - avail_ring: u64, - _log: u64, - ) -> Result<()> { - let cloned_mem_space = self.sys_mem.clone(); - - let desc_addr = self.get_guest_address(desc_table)?; - let used_addr = self.get_guest_address(used_ring)?; - let avail_addr = self.get_guest_address(avail_ring)?; - - if let Err(_ret) = self - .config - .get_mut_queue_config(queue_index) - .map(|queue_info| { - queue_info.config.desc_table = GuestAddress(desc_addr); - queue_info.config.addr_cache.desc_table_host = cloned_mem_space - .get_host_address(GuestAddress(desc_addr)) - .unwrap_or(0); - - queue_info.config.avail_ring = GuestAddress(avail_addr); - queue_info.config.addr_cache.avail_ring_host = cloned_mem_space - .get_host_address(GuestAddress(avail_addr)) - .unwrap_or(0); - - queue_info.config.used_ring = GuestAddress(used_addr); - queue_info.config.addr_cache.used_ring_host = cloned_mem_space - .get_host_address(GuestAddress(used_addr)) - .unwrap_or(0); - - if queue_info.config.addr_cache.desc_table_host == 0 - || queue_info.config.addr_cache.avail_ring_host == 0 - || queue_info.config.addr_cache.used_ring_host == 0 - { - return Err(()); - } - - Ok(()) - }) - { - bail!( - "Failed to set vring addr, got host address failed. Index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", - queue_index, - desc_addr, - avail_addr, - used_addr - ); - } - - Ok(()) - } - - fn set_vring_base(&mut self, _queue_index: usize, _num: u16) -> Result<()> { - Ok(()) - } - - fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { - if (queue_index & VIRTIO_FS_VRING_NO_FD_MASK) != 0 { - bail!("The polling mode is not supported"); - } - let index = queue_index & VIRTIO_FS_VRING_IDX_MASK; - self.config - .get_mut_queue_config(index) - .map(|queue_info| { - let call_evt = unsafe { EventFd::from_raw_fd(fd) }; - queue_info.call_evt = Some(Arc::new(call_evt)); - }) - .with_context(|| format!("Failed to set vring call, index: {}", index))?; - - if !virtio_has_feature(self.config.driver_features, VHOST_USER_F_PROTOCOL_FEATURES) { - self.register_fs_handler(queue_index)?; - } - - Ok(()) - } - - fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { - if (queue_index & VIRTIO_FS_VRING_NO_FD_MASK) != 0 { - bail!("The polling mode is not supported"); - } - let index = queue_index & VIRTIO_FS_VRING_IDX_MASK; - self.config - .get_mut_queue_config(index) - .map(|queue_info| { - let kick_evt = unsafe { EventFd::from_raw_fd(fd) }; - queue_info.kick_evt = Some(Arc::new(kick_evt)); - }) - .with_context(|| format!("Failed to set vring kick, index: {}", index))?; - Ok(()) - } - - fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()> { - if status == 1 { - self.register_fs_handler(queue_index)?; - } else { - self.unregister_fs_handler(queue_index)?; - } - Ok(()) - } -} -- Gitee From d5b3f7bcdc41c479da75f9b38fdabfeaf0a94e05 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Sep 2023 12:32:26 +0800 Subject: [PATCH 1465/2187] Input: adjust the interface of input module 1. Split the interface of do_point_event into send_point_event and sync in PointerOpts trait. 2. Add move and button events, where button events does not include coordinate message. Signed-off-by: Xiao Ye --- .../src/pci/demo_device/kbd_pointer_device.rs | 46 +++++- devices/src/usb/tablet.rs | 86 +++++++++--- machine/src/standard_vm/mod.rs | 12 +- ui/src/gtk/draw.rs | 73 +++------- ui/src/input.rs | 131 +++++++++++++++--- ui/src/vnc/client_io.rs | 22 ++- 6 files changed, 269 insertions(+), 101 deletions(-) diff --git a/devices/src/pci/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs index 3836be672..ce5549871 100644 --- a/devices/src/pci/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -23,7 +23,7 @@ use once_cell::sync::Lazy; use super::DeviceTypeOperation; use address_space::{AddressSpace, GuestAddress}; -use ui::input::{register_keyboard, register_pointer, KeyboardOpts, PointerOpts}; +use ui::input::{register_keyboard, register_pointer, Axis, InputType, KeyboardOpts, PointerOpts}; static MEM_ADDR: Lazy>> = Lazy::new(|| { Arc::new(Mutex::new(MemSpace { @@ -78,7 +78,11 @@ impl DemoKbdMouse { kbd_name: "test-pci-keyboard".to_string(), pointer_name: "test-pci-pointer".to_string(), test_kbd: Arc::new(Mutex::new(TestPciKbd {})), - test_pointer: Arc::new(Mutex::new(TestPciPointer {})), + test_pointer: Arc::new(Mutex::new(TestPciPointer { + x: 0, + y: 0, + button: 0, + })), } } } @@ -97,17 +101,45 @@ impl KeyboardOpts for TestPciKbd { } } -pub struct TestPciPointer {} +pub struct TestPciPointer { + pub x: u32, + pub y: u32, + pub button: u32, +} impl PointerOpts for TestPciPointer { - fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { + fn update_point_state(&mut self, input_event: ui::input::InputEvent) -> Result<()> { + match input_event.input_type { + InputType::MoveEvent => match input_event.move_event.axis { + Axis::X => self.x = input_event.move_event.data, + Axis::Y => self.y = input_event.move_event.data, + }, + InputType::ButtonEvent => { + if input_event.button_event.down { + self.button |= input_event.button_event.button & 0x7; + } else { + self.button &= !(input_event.button_event.button & 0x7); + } + } + _ => { + bail!("Input type: {:?} is unsupported", input_event.input_type); + } + } + Ok(()) + } + + fn sync(&mut self) -> Result<()> { let msg = PointerMessage { event_type: InputEvent::PointerEvent, - button, - x, - y, + button: self.button, + x: self.x, + y: self.y, ..Default::default() }; + self.x = 0; + self.y = 0; + self.button = 0; + MEM_ADDR.lock().unwrap().send_kbdmouse_message(&msg) } } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 13d040646..e1854cd00 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -13,7 +13,7 @@ use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; -use anyhow::Result; +use anyhow::{bail, Result}; use log::{debug, info, warn}; use once_cell::sync::Lazy; @@ -28,8 +28,9 @@ use super::{ UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; use ui::input::{ - register_pointer, unregister_pointer, PointerOpts, INPUT_BUTTON_WHEEL_DOWN, - INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, + register_pointer, unregister_pointer, Axis, InputEvent, InputType, PointerOpts, + INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, + INPUT_BUTTON_WHEEL_UP, }; const INPUT_BUTTON_MASK: u32 = 0x7; @@ -133,7 +134,7 @@ pub struct UsbTabletAdapter { } impl PointerOpts for UsbTabletAdapter { - fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { + fn update_point_state(&mut self, input_event: InputEvent) -> Result<()> { let mut locked_tablet = self.tablet.lock().unwrap(); if locked_tablet.hid.num >= QUEUE_LENGTH { debug!("Pointer queue is full!"); @@ -142,23 +143,68 @@ impl PointerOpts for UsbTabletAdapter { } let index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; let evt = &mut locked_tablet.hid.pointer.queue[index]; - if button & INPUT_BUTTON_WHEEL_UP == INPUT_BUTTON_WHEEL_UP { - evt.v_wheel = 1; - } else if button & INPUT_BUTTON_WHEEL_DOWN == INPUT_BUTTON_WHEEL_DOWN { - evt.v_wheel = -1; - } else { - evt.v_wheel = 0; - } - if button & INPUT_BUTTON_WHEEL_LEFT == INPUT_BUTTON_WHEEL_LEFT { - evt.h_wheel = -1; - } else if button & INPUT_BUTTON_WHEEL_RIGHT == INPUT_BUTTON_WHEEL_RIGHT { - evt.h_wheel = 1; - } else { - evt.h_wheel = 0; + + match input_event.input_type { + InputType::ButtonEvent => { + let button_event = &input_event.button_event; + if button_event.down { + if button_event.button & INPUT_BUTTON_WHEEL_LEFT != 0 { + evt.h_wheel = -1; + } else if button_event.button & INPUT_BUTTON_WHEEL_RIGHT != 0 { + evt.h_wheel = 1; + } else { + evt.h_wheel = 0; + } + + if button_event.button & INPUT_BUTTON_WHEEL_UP != 0 { + evt.v_wheel = 1; + } else if button_event.button & INPUT_BUTTON_WHEEL_DOWN != 0 { + evt.v_wheel = -1; + } else { + evt.v_wheel = 0; + } + + evt.button_state |= button_event.button & INPUT_BUTTON_MASK; + } else { + evt.button_state &= !(button_event.button & INPUT_BUTTON_MASK); + } + } + InputType::MoveEvent => { + let move_event = &input_event.move_event; + match move_event.axis { + Axis::X => evt.pos_x = min(move_event.data as u32, INPUT_COORDINATES_MAX), + Axis::Y => evt.pos_y = min(move_event.data as u32, INPUT_COORDINATES_MAX), + } + } + _ => bail!( + "Input type: {:?} is unsupported by usb tablet!", + input_event.input_type + ), + }; + + Ok(()) + } + + fn sync(&mut self) -> Result<()> { + let mut locked_tablet = self.tablet.lock().unwrap(); + + // The last evt is used to save the latest button state, + // so the max number of events can be cached at one time is QUEUE_LENGTH - 1. + if locked_tablet.hid.num == QUEUE_LENGTH - 1 { + return Ok(()); } - evt.button_state = button & INPUT_BUTTON_MASK; - evt.pos_x = min(x, INPUT_COORDINATES_MAX); - evt.pos_y = min(y, INPUT_COORDINATES_MAX); + let curr_index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; + let next_index = (curr_index + 1) & QUEUE_MASK as usize; + let curr_evt = locked_tablet.hid.pointer.queue[curr_index]; + let next_evt = &mut locked_tablet.hid.pointer.queue[next_index]; + + // Update the status of the next event in advance. + next_evt.v_wheel = 0; + next_evt.h_wheel = 0; + next_evt.button_state = curr_evt.button_state; + next_evt.pos_x = curr_evt.pos_x; + next_evt.pos_y = curr_evt.pos_y; + locked_tablet.hid.num += 1; drop(locked_tablet); let clone_tablet = self.tablet.clone(); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 5fe697190..451216257 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -70,7 +70,7 @@ use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::MigrationManager; -use ui::input::{key_event, point_event}; +use ui::input::{input_button, input_move_abs, input_point_sync, key_event, Axis}; #[cfg(feature = "vnc")] use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; @@ -2095,13 +2095,19 @@ fn send_input_event(key: String, value: String) -> Result<()> { } "pointer" => { let vec: Vec<&str> = value.split(',').collect(); - if vec.len() != 3 { + // There are four expected parameters for input_event, + // includes: x, y, button and down. + if vec.len() != 4 { bail!("Invalid pointer format: {}", value); } let x = vec[0].parse::()?; let y = vec[1].parse::()?; let btn = vec[2].parse::()?; - point_event(btn, x, y)?; + let down = vec[3].parse::()?; + input_move_abs(Axis::X, x)?; + input_move_abs(Axis::Y, y)?; + input_button(btn, down != 0)?; + input_point_sync()?; } _ => { bail!("Invalid input type: {}", key); diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 86c0866ea..a964eba9e 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -28,9 +28,10 @@ use crate::{ console::graphic_hardware_ui_info, gtk::GtkDisplayScreen, input::{ - self, point_event, press_mouse, release_all_key, update_key_state, ABS_MAX, - INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, - INPUT_BUTTON_WHEEL_UP, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, + self, input_button, input_move_abs, input_point_sync, press_mouse, release_all_key, + update_key_state, Axis, ABS_MAX, INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, + INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, + INPUT_POINT_RIGHT, }, }; @@ -63,7 +64,7 @@ pub(crate) fn set_callback_for_draw_area( ); draw_area.connect_scroll_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, scroll_event| { - da_scroll_callback(&gs, scroll_event).unwrap_or_else(|e| error!("Scroll event: {}", e)); + da_scroll_callback(scroll_event).unwrap_or_else(|e| error!("Scroll event: {}", e)); Inhibit(false) }), ); @@ -218,11 +219,9 @@ fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; - point_event( - borrowed_gs.click_state.button_mask as u32, - standard_x as u32, - standard_y as u32, - ) + input_move_abs(Axis::X, standard_x as u32)?; + input_move_abs(Axis::Y, standard_y as u32)?; + input_point_sync() } fn da_pointer_callback( @@ -237,49 +236,24 @@ fn da_pointer_callback( _ => return Ok(()), }; - let (width, height) = match &borrowed_gs.cairo_image { - Some(image) => (image.width() as f64, image.height() as f64), - None => return Ok(()), - }; - - let (x, y) = button_event.position(); - let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; - - let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; - let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; - match button_event.event_type() { gdk::EventType::ButtonRelease => { - borrowed_gs.click_state.button_mask = 0; - point_event( - borrowed_gs.click_state.button_mask as u32, - standard_x as u32, - standard_y as u32, - ) + input_button(borrowed_gs.click_state.button_mask as u32, false)?; + input_point_sync() + } + gdk::EventType::ButtonPress => { + input_button(borrowed_gs.click_state.button_mask as u32, true)?; + input_point_sync() + } + gdk::EventType::DoubleButtonPress => { + press_mouse(borrowed_gs.click_state.button_mask as u32)?; + press_mouse(borrowed_gs.click_state.button_mask as u32) } - gdk::EventType::ButtonPress => point_event( - borrowed_gs.click_state.button_mask as u32, - standard_x as u32, - standard_y as u32, - ), - gdk::EventType::DoubleButtonPress => press_mouse( - borrowed_gs.click_state.button_mask as u32, - standard_x as u32, - standard_y as u32, - ), _ => Ok(()), } } -fn da_scroll_callback( - gs: &Rc>, - scroll_event: &gdk::EventScroll, -) -> Result<()> { - let borrowed_gs = gs.borrow(); - let (width, height) = match &borrowed_gs.cairo_image { - Some(image) => (image.width() as f64, image.height() as f64), - None => return Ok(()), - }; +fn da_scroll_callback(scroll_event: &gdk::EventScroll) -> Result<()> { let button_mask = match scroll_event.direction() { ScrollDirection::Up => INPUT_BUTTON_WHEEL_UP, ScrollDirection::Down => INPUT_BUTTON_WHEEL_DOWN, @@ -301,11 +275,10 @@ fn da_scroll_callback( _ => 0x0, }; - let standard_x = ((borrowed_gs.click_state.last_x as u64 * ABS_MAX) / width as u64) as u16; - let standard_y = ((borrowed_gs.click_state.last_y as u64 * ABS_MAX) / height as u64) as u16; - drop(borrowed_gs); - point_event(button_mask, standard_x as u32, standard_y as u32)?; - Ok(()) + input_button(button_mask, true)?; + input_point_sync()?; + input_button(button_mask, false)?; + input_point_sync() } /// Draw_area callback func for draw signal. diff --git a/ui/src/input.rs b/ui/src/input.rs index b72cb7642..818766b78 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -68,6 +68,56 @@ static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Input static LED_STATE: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(LedState::default()))); +#[derive(Debug)] +pub enum InputType { + KeyEvent, + MoveEvent, + ButtonEvent, +} + +#[derive(Default)] +pub enum Axis { + #[default] + X, + Y, +} + +#[derive(Default)] +pub struct MoveEvent { + pub axis: Axis, + pub data: u32, +} + +#[derive(Default)] +pub struct ButtonEvent { + pub button: u32, + pub down: bool, +} + +#[derive(Default)] +pub struct KeyEvent { + pub keycode: u16, + pub down: bool, +} + +pub struct InputEvent { + pub input_type: InputType, + pub move_event: MoveEvent, + pub button_event: ButtonEvent, + pub key_event: KeyEvent, +} + +impl InputEvent { + fn new(input_type: InputType) -> Self { + Self { + input_type, + move_event: MoveEvent::default(), + button_event: ButtonEvent::default(), + key_event: KeyEvent::default(), + } + } +} + // Keyboard Modifier State pub enum KeyboardModifier { KeyModNone = 0, @@ -290,33 +340,56 @@ pub fn unregister_pointer(device: &str) { INPUTS.lock().unwrap().unregister_mouse(device); } -pub fn key_event(keycode: u16, down: bool) -> Result<()> { - let kbd = INPUTS.lock().unwrap().get_active_kbd(); - if let Some(k) = kbd { - k.lock().unwrap().do_key_event(keycode, down)?; +pub fn input_move_abs(axis: Axis, data: u32) -> Result<()> { + let mut input_event = InputEvent::new(InputType::MoveEvent); + let move_event = MoveEvent { axis, data }; + input_event.move_event = move_event; + + let mouse = INPUTS.lock().unwrap().get_active_mouse(); + if let Some(m) = mouse { + m.lock().unwrap().update_point_state(input_event)?; } + Ok(()) } -/// A complete mouse click event. -pub fn press_mouse(button: u32, x: u32, y: u32) -> Result<()> { +pub fn input_button(button: u32, down: bool) -> Result<()> { + let mut input_event = InputEvent::new(InputType::ButtonEvent); + let button_event = ButtonEvent { button, down }; + input_event.button_event = button_event; + let mouse = INPUTS.lock().unwrap().get_active_mouse(); if let Some(m) = mouse { - let mut locked_mouse = m.lock().unwrap(); - locked_mouse.do_point_event(button, x, y)?; - locked_mouse.do_point_event(0, x, y)? + m.lock().unwrap().update_point_state(input_event)?; } + Ok(()) } -pub fn point_event(button: u32, x: u32, y: u32) -> Result<()> { +pub fn input_point_sync() -> Result<()> { let mouse = INPUTS.lock().unwrap().get_active_mouse(); if let Some(m) = mouse { - m.lock().unwrap().do_point_event(button, x, y)?; + m.lock().unwrap().sync()?; + } + Ok(()) +} + +pub fn key_event(keycode: u16, down: bool) -> Result<()> { + let kbd = INPUTS.lock().unwrap().get_active_kbd(); + if let Some(k) = kbd { + k.lock().unwrap().do_key_event(keycode, down)?; } Ok(()) } +/// A complete mouse click event. +pub fn press_mouse(button: u32) -> Result<()> { + input_button(button, true)?; + input_point_sync()?; + input_button(button, false)?; + input_point_sync() +} + /// 1. Keep the key state in keyboard_state. /// 2. Sync the caps lock and num lock state to guest. pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { @@ -398,11 +471,14 @@ pub trait KeyboardOpts: Send { } pub trait PointerOpts: Send { - fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()>; + fn update_point_state(&mut self, input_event: InputEvent) -> Result<()>; + fn sync(&mut self) -> Result<()>; } #[cfg(test)] mod tests { + use anyhow::bail; + #[cfg(feature = "keycode")] use crate::keycode::KeyCode; static TEST_INPUT: Lazy>> = @@ -468,11 +544,27 @@ mod tests { x: u32, y: u32, } + impl PointerOpts for TestTablet { - fn do_point_event(&mut self, button: u32, x: u32, y: u32) -> Result<()> { - self.button = button; - self.x = x; - self.y = y; + fn update_point_state(&mut self, input_event: InputEvent) -> Result<()> { + match input_event.input_type { + InputType::MoveEvent => match input_event.move_event.axis { + Axis::X => self.x = input_event.move_event.data, + Axis::Y => self.y = input_event.move_event.data, + }, + InputType::ButtonEvent => { + if input_event.button_event.down { + self.button |= input_event.button_event.button; + } else { + self.button &= !(input_event.button_event.button & 0x7); + } + } + _ => bail!("Input type: {:?} is unsupported", input_event.input_type), + } + Ok(()) + } + + fn sync(&mut self) -> Result<()> { Ok(()) } } @@ -493,7 +585,12 @@ mod tests { assert_eq!(test_mouse.lock().unwrap().x, 0); assert_eq!(test_mouse.lock().unwrap().y, 0); register_pointer("TestPointer", test_mouse.clone()); - assert!(point_event(1, 54, 12).is_ok()); + + assert!(input_move_abs(Axis::X, 54).is_ok()); + assert!(input_move_abs(Axis::Y, 12).is_ok()); + assert!(input_button(1, true).is_ok()); + assert!(input_point_sync().is_ok()); + assert_eq!(test_mouse.lock().unwrap().button, 1); assert_eq!(test_mouse.lock().unwrap().x, 54); assert_eq!(test_mouse.lock().unwrap().y, 12); diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index a418b38e4..e2fb00eea 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -30,9 +30,10 @@ use crate::{ console::console_select, error::VncError, input::{ - key_event, keyboard_modifier_get, keyboard_state_reset, point_event, update_key_state, - KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, - INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, UPPERCASE_TO_LOWERCASE, + input_button, input_move_abs, input_point_sync, key_event, keyboard_modifier_get, + keyboard_state_reset, update_key_state, Axis, KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, + INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, + UPPERCASE_TO_LOWERCASE, }, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, @@ -182,6 +183,8 @@ pub struct DisplayMode { pub client_be: bool, /// The pixel need to convert. pub convert: bool, + /// Last button state. + pub last_button: u8, /// Image pixel format in pixman. pub pf: PixelFormat, } @@ -195,6 +198,7 @@ impl DisplayMode { enc, client_be, convert, + last_button: 0, pf, } } @@ -971,6 +975,7 @@ impl ClientIoHandler { y = ((y as u64 * ABS_MAX) / height as u64) as u16; // ASCII -> HidCode. + let last_button = self.client.client_dpm.lock().unwrap().last_button; let button_mask: u8 = match buf[1] { INPUT_POINT_LEFT => 0x01, INPUT_POINT_MIDDLE => 0x04, @@ -978,7 +983,16 @@ impl ClientIoHandler { _ => buf[1], }; - point_event(button_mask as u32, x as u32, y as u32)?; + if last_button != button_mask { + self.client.client_dpm.lock().unwrap().last_button = button_mask; + input_button(last_button as u32, false)?; + input_button(button_mask as u32, true)?; + } + + input_move_abs(Axis::X, x as u32)?; + input_move_abs(Axis::Y, y as u32)?; + input_point_sync()?; + self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); Ok(()) } -- Gitee From e80c56fa93742420ab01773881124ba55935fd2a Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Sep 2023 17:51:59 +0800 Subject: [PATCH 1466/2187] GTK: delete click state The input interface has been changed, so there is no need to save the last button information in gtk. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 20 ++++++++------------ ui/src/gtk/mod.rs | 23 ++++++----------------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index a964eba9e..915bb20ab 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -52,13 +52,13 @@ pub(crate) fn set_callback_for_draw_area( ); draw_area.connect_button_press_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, button_event| { - da_pointer_callback(&gs, button_event).unwrap_or_else(|e| error!("Press event: {}", e)); + da_pointer_callback(button_event).unwrap_or_else(|e| error!("Press event: {}", e)); Inhibit(false) }), ); draw_area.connect_button_release_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, button_event| { - da_pointer_callback(&gs, button_event).unwrap_or_else(|e| error!("Release event: {}", e)); + da_pointer_callback(button_event).unwrap_or_else(|e| error!("Release event: {}", e)); Inhibit(false) }), ); @@ -224,12 +224,8 @@ fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) input_point_sync() } -fn da_pointer_callback( - gs: &Rc>, - button_event: &gdk::EventButton, -) -> Result<()> { - let mut borrowed_gs = gs.borrow_mut(); - borrowed_gs.click_state.button_mask = match button_event.button() { +fn da_pointer_callback(button_event: &gdk::EventButton) -> Result<()> { + let button_mask = match button_event.button() { 1 => INPUT_POINT_LEFT, 2 => INPUT_POINT_RIGHT, 3 => INPUT_POINT_MIDDLE, @@ -238,16 +234,16 @@ fn da_pointer_callback( match button_event.event_type() { gdk::EventType::ButtonRelease => { - input_button(borrowed_gs.click_state.button_mask as u32, false)?; + input_button(button_mask as u32, false)?; input_point_sync() } gdk::EventType::ButtonPress => { - input_button(borrowed_gs.click_state.button_mask as u32, true)?; + input_button(button_mask as u32, true)?; input_point_sync() } gdk::EventType::DoubleButtonPress => { - press_mouse(borrowed_gs.click_state.button_mask as u32)?; - press_mouse(borrowed_gs.click_state.button_mask as u32) + press_mouse(button_mask as u32)?; + press_mouse(button_mask as u32) } _ => Ok(()), } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 8d1ce7382..ab48897c7 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -37,7 +37,7 @@ use gtk::{ }, Application, ApplicationWindow, DrawingArea, HeaderBar, Label, RadioMenuItem, }; -use log::{debug, error}; +use log::error; use vmm_sys_util::eventfd::EventFd; use crate::{ @@ -99,13 +99,6 @@ pub enum ZoomOperate { BestFit, } -#[derive(Default)] -pub struct ClickState { - pub button_mask: u8, - pub last_x: f64, - pub last_y: f64, -} - #[derive(Debug, PartialEq)] enum DisplayEventType { DisplaySwitch, @@ -358,7 +351,6 @@ pub struct GtkDisplayScreen { source_surface: DisplaySurface, transfer_surface: Option, cairo_image: Option, - click_state: ClickState, con: Weak>, dcl: Weak>, scale_mode: Rc>, @@ -409,7 +401,6 @@ impl GtkDisplayScreen { source_surface: surface, transfer_surface: None, cairo_image, - click_state: ClickState::default(), con, dcl, scale_mode, @@ -438,7 +429,7 @@ impl GtkDisplayScreen { /// 2. There may be unfilled areas between the window and the image. /// Input: relative coordinates of window. /// Output: relative coordinates of images. - fn convert_coord(&mut self, x: f64, y: f64) -> Result<(f64, f64)> { + fn convert_coord(&mut self, mut x: f64, mut y: f64) -> Result<(f64, f64)> { let (surface_width, surface_height) = match &self.cairo_image { Some(image) => (image.width(), image.height()), None => bail!("No display image."), @@ -457,10 +448,10 @@ impl GtkDisplayScreen { None => bail!("No display window."), }; - if x.lt(&0.0) || x.gt(&window_width) || y.lt(&0.0) || y.gt(&window_height) { - debug!("x {} or y {} out of range, use last value.", x, y); - return Ok((self.click_state.last_x, self.click_state.last_y)); - } + x = x.max(0.0); + x = x.min(window_width); + y = y.max(0.0); + y = y.min(window_height); // There may be unfilled areas between the window and the image. let (mut mx, mut my) = (0.0, 0.0); @@ -472,8 +463,6 @@ impl GtkDisplayScreen { } let real_x = ((x - mx) / self.scale_x) * scale_factor; let real_y = ((y - my) / self.scale_y) * scale_factor; - self.click_state.last_x = real_x; - self.click_state.last_y = real_y; Ok((real_x, real_y)) } -- Gitee From 73f8648a542e27f220824c7b3c09621344e32c70 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Sep 2023 12:35:26 +0800 Subject: [PATCH 1467/2187] Input: adapt the usb test Adapt the usb test due to the key interface changes. Signed-off-by: Xiao Ye --- tests/mod_test/src/libdriver/usb.rs | 8 +-- tests/mod_test/tests/usb_test.rs | 92 +++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 8a18351c9..2e14f18ac 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -2101,8 +2101,8 @@ impl TestXhciPciDevice { } pub fn test_pointer_event(&mut self, slot_id: u32, test_state: Rc>) { - qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); - qmp_send_pointer_event(test_state.borrow_mut(), 200, 100, 1); + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0, true); + qmp_send_pointer_event(test_state.borrow_mut(), 200, 100, 1, true); self.queue_multi_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN, 2); self.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); @@ -2462,8 +2462,8 @@ pub fn qmp_send_multi_key_event(test_state: Rc>, key_list: &[ } } -pub fn qmp_send_pointer_event(test_state: RefMut, x: i32, y: i32, btn: i32) { - let value_str = format!("{},{},{}", x, y, btn); +pub fn qmp_send_pointer_event(test_state: RefMut, x: i32, y: i32, btn: i32, down: bool) { + let value_str = format!("{},{},{},{}", x, y, btn, if down { 1 } else { 0 }); let mut str = "{\"execute\": \"input_event\", \"arguments\": { \"key\": \"pointer\", \"value\":\"" .to_string(); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index aa4c20fd0..6739cd8ac 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -2008,17 +2008,21 @@ fn test_xhci_tablet_basic() { let cnt = 10; for i in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), i * 10, i * 20, i % 3); + qmp_send_pointer_event(test_state.borrow_mut(), i * 10, i * 20, i % 3, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + qmp_send_pointer_event(test_state.borrow_mut(), i * 10, i * 20, i % 3, false); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); for i in 0..cnt { - let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); + let press_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + let release_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(press_evt.ccode, TRBCCode::Success as u32); + assert_eq!(release_evt.ccode, TRBCCode::Success as u32); + let press_buf = xhci.get_transfer_data_indirect(press_evt.ptr, HID_POINTER_LEN); assert_eq!( - buf, + press_buf, [ i as u8 % 3, (i * 10) as u8, @@ -2029,30 +2033,59 @@ fn test_xhci_tablet_basic() { 0 ] ); + let release_buf = xhci.get_transfer_data_indirect(release_evt.ptr, HID_POINTER_LEN); + assert_eq!( + release_buf, + [ + 0, + (i * 10) as u8, + (i * 10 >> 8) as u8, + (i * 20) as u8, + (i * 20 >> 8) as u8, + 0, + 0 + ] + ) } + // INPUT_BUTTON_WHEEL_LEFT + INPUT_BUTTON_WHEEL_UP. for _ in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x28); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x28, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x28, false); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); for _ in 0..cnt { - let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 10, 0, 20, 0, 1, 255]); + let press_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + let release_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(press_evt.ccode, TRBCCode::Success as u32); + assert_eq!(release_evt.ccode, TRBCCode::Success as u32); + + let press_buf = xhci.get_transfer_data_indirect(press_evt.ptr, HID_POINTER_LEN); + assert_eq!(press_buf, [0, 10, 0, 20, 0, 1, 255]); + let release_buf = xhci.get_transfer_data_indirect(release_evt.ptr, HID_POINTER_LEN); + assert_eq!(release_buf, [0, 10, 0, 20, 0, 0, 0]); } + // INPUT_BUTTON_WHEEL_RIGHT + INPUT_BUTTON_WHEEL_DOWN. for _ in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x50); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x50, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x50, false); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); for _ in 0..cnt { - let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [0, 10, 0, 20, 0, 255, 1]); + let press_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + let release_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(press_evt.ccode, TRBCCode::Success as u32); + assert_eq!(release_evt.ccode, TRBCCode::Success as u32); + + let press_buf = xhci.get_transfer_data_indirect(press_evt.ptr, HID_POINTER_LEN); + assert_eq!(press_buf, [0, 10, 0, 20, 0, 255, 1]); + let release_buf = xhci.get_transfer_data_indirect(release_evt.ptr, HID_POINTER_LEN); + assert_eq!(release_buf, [0, 10, 0, 20, 0, 0, 0]); } test_state.borrow_mut().stop(); } @@ -2073,7 +2106,7 @@ fn test_xhci_tablet_over_hid_buffer() { const HID_BUFFER_SIZE: u32 = 16; let event_cnt = 20; for i in 0..event_cnt { - qmp_send_pointer_event(test_state.borrow_mut(), i, i + 100, 0); + qmp_send_pointer_event(test_state.borrow_mut(), i, i + 100, 0, true); } xhci.queue_multi_indirect_td( slot_id, @@ -2083,7 +2116,7 @@ fn test_xhci_tablet_over_hid_buffer() { ); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); for i in 0..event_cnt as u32 { - if i < HID_BUFFER_SIZE { + if i < HID_BUFFER_SIZE - 1 { let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); @@ -2116,7 +2149,7 @@ fn test_xhci_tablet_over_ring_limit() { let test_cnt = 3; for i in 0..test_cnt { for _ in 0..transfer_limit { - qmp_send_pointer_event(test_state.borrow_mut(), 50, 100, 0); + qmp_send_pointer_event(test_state.borrow_mut(), 50, 100, 0, true); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); @@ -2158,13 +2191,20 @@ fn test_xhci_tablet_invalid_value() { let port_id = 1; let slot_id = xhci.init_device(port_id); - qmp_send_pointer_event(test_state.borrow_mut(), 0xfffff, 0xfffff, 0xff); + qmp_send_pointer_event(test_state.borrow_mut(), 0xfffff, 0xfffff, 0xff, true); + xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + qmp_send_pointer_event(test_state.borrow_mut(), 0xfffff, 0xfffff, 0xff, false); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); + xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); - let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); - assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_POINTER_LEN); - assert_eq!(buf, [7, 255, 127, 255, 127, 1, 255]); + let press_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(press_evt.ccode, TRBCCode::Success as u32); + let press_buf = xhci.get_transfer_data_indirect(press_evt.ptr, HID_POINTER_LEN); + assert_eq!(press_buf, [7, 255, 127, 255, 127, 1, 255]); + let release_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); + assert_eq!(release_evt.ccode, TRBCCode::Success as u32); + let release_buf = xhci.get_transfer_data_indirect(release_evt.ptr, HID_POINTER_LEN); + assert_eq!(release_buf, [0, 255, 127, 255, 127, 0, 0]); xhci.test_pointer_event(slot_id, test_state.clone()); test_state.borrow_mut().stop(); @@ -2377,7 +2417,7 @@ fn test_xhci_disable_interrupt() { let slot_id = xhci.init_device(port_id); // Case: disable USB_CMD_INTE - qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0, true); xhci.queue_direct_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); xhci.oper_regs_write(XHCI_OPER_REG_USBCMD, value & !USB_CMD_INTE); @@ -2391,7 +2431,7 @@ fn test_xhci_disable_interrupt() { assert_eq!(buf, [0, 100, 0, 200, 0, 0, 0]); // Case: disable IMAN_IE - qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0, true); xhci.queue_direct_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); let value = xhci.interrupter_regs_read(PRIMARY_INTERRUPTER_ID as u64, XHCI_INTR_REG_IMAN as u64); @@ -2512,7 +2552,7 @@ fn test_xhci_tablet_invalid_trb() { let port_id = 1; let slot_id = xhci.init_device(port_id); - qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0); + qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0, true); // Invalid address in TRB. let mut trb = TestNormalTRB::generate_normal_td(0, 6); trb.set_pointer(0); -- Gitee From 8221853149bd4d5282184f762fcb42394f83c4b4 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 21 Nov 2023 09:41:51 +0800 Subject: [PATCH 1468/2187] address-space: fixup codestyle Signed-off-by: Zhao Yi Min --- address_space/src/host_mmap.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 02da2f6b1..f29db4970 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -139,10 +139,10 @@ impl FileBackend { } } else if old_file_len < file_len { bail!( - "Backing file {} does not has sufficient resource for allocating RAM (size is 0x{:X})", - file_path, - file_len - ); + "Backing file {} does not has sufficient resource for allocating RAM (size is 0x{:X})", + file_path, + file_len + ); } Ok(FileBackend { -- Gitee From 4d223bf261a6bf12e9909bda6ea2a1e8020643f2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 21 Nov 2023 06:03:38 +0800 Subject: [PATCH 1469/2187] VNC: fix some input error in vnc If the input state combines several button, updating all state at once will have problems. So change to update the status of each button one by one. Signed-off-by: Xiao Ye --- ui/src/input.rs | 11 ++++++----- ui/src/vnc/client_io.rs | 30 ++++++++++++++++++------------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/ui/src/input.rs b/ui/src/input.rs index 818766b78..b2d2f3a2a 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -27,6 +27,12 @@ pub const ABS_MAX: u64 = 0x7fff; pub const INPUT_POINT_LEFT: u8 = 0x01; pub const INPUT_POINT_MIDDLE: u8 = 0x02; pub const INPUT_POINT_RIGHT: u8 = 0x04; +pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; +pub const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; +pub const INPUT_BUTTON_WHEEL_LEFT: u32 = 0x20; +pub const INPUT_BUTTON_WHEEL_RIGHT: u32 = 0x40; +pub const INPUT_BUTTON_MAX_NUM: u32 = 7; + // ASCII value. pub const ASCII_A: i32 = 65; pub const ASCII_Z: i32 = 90; @@ -57,11 +63,6 @@ const KEYCODE_KP_DECIMAL: u16 = 0x53; const NUM_LOCK_LED: u8 = 0x1; const CAPS_LOCK_LED: u8 = 0x2; pub const SCROLL_LOCK_LED: u8 = 0x4; -/// Input button state. -pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; -pub const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; -pub const INPUT_BUTTON_WHEEL_LEFT: u32 = 0x20; -pub const INPUT_BUTTON_WHEEL_RIGHT: u32 = 0x40; static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index e2fb00eea..c1ac1d6f8 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -32,8 +32,8 @@ use crate::{ input::{ input_button, input_move_abs, input_point_sync, key_event, keyboard_modifier_get, keyboard_state_reset, update_key_state, Axis, KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, - INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, - UPPERCASE_TO_LOWERCASE, + INPUT_BUTTON_MAX_NUM, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_1, + KEYCODE_9, UPPERCASE_TO_LOWERCASE, }, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, @@ -975,18 +975,24 @@ impl ClientIoHandler { y = ((y as u64 * ABS_MAX) / height as u64) as u16; // ASCII -> HidCode. + let new_button = buf[1]; let last_button = self.client.client_dpm.lock().unwrap().last_button; - let button_mask: u8 = match buf[1] { - INPUT_POINT_LEFT => 0x01, - INPUT_POINT_MIDDLE => 0x04, - INPUT_POINT_RIGHT => 0x02, - _ => buf[1], - }; + if last_button != new_button { + for bit in 0..INPUT_BUTTON_MAX_NUM { + let button_mask = 1 << bit; + if last_button & button_mask == new_button & button_mask { + continue; + } - if last_button != button_mask { - self.client.client_dpm.lock().unwrap().last_button = button_mask; - input_button(last_button as u32, false)?; - input_button(button_mask as u32, true)?; + let button = match button_mask { + INPUT_POINT_LEFT => 0x01, + INPUT_POINT_MIDDLE => 0x04, + INPUT_POINT_RIGHT => 0x02, + _ => button_mask, + }; + input_button(button as u32, new_button & button_mask != 0)?; + } + self.client.client_dpm.lock().unwrap().last_button = new_button; } input_move_abs(Axis::X, x as u32)?; -- Gitee From 52ed59da9616806abace89a42a65ba8a5d856a6a Mon Sep 17 00:00:00 2001 From: li-huachao Date: Tue, 21 Nov 2023 16:44:47 +0800 Subject: [PATCH 1470/2187] memory MST: change rom/ram_device region offset to 4GB When registering a rom/ram_device type region, on some platforms, 1TB may exceed the hypervisor limit. We modified it to 4GB. --- tests/mod_test/tests/memory_test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 421c44368..155ce51a9 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -264,7 +264,7 @@ fn region_update_exception() { #[test] fn rom_device_region_readwrite() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); - let addr = 0x100_0000_0000; // 1TB + let addr = 0x1_0000_0000; // 4GB // Add a dummy rom device by qmp. The function of the device is to multiply the written value by // 2 through the write interface and save it, and read the saved value through the read @@ -275,7 +275,7 @@ fn rom_device_region_readwrite() { "{{ \"execute\": \"update_region\", \"arguments\": {{ \"update_type\": \"add\", \"region_type\": \"rom_device_region\", - \"offset\": 1099511627776, + \"offset\": 4294967296, \"size\": 4096, \"priority\": 99, \"read_only_mode\": false, @@ -314,7 +314,7 @@ fn rom_device_region_readwrite() { "{{ \"execute\": \"update_region\", \"arguments\": {{ \"update_type\": \"add\", \"region_type\": \"rom_device_region\", - \"offset\": 1099511627776, + \"offset\": 4294967296, \"size\": 4096, \"priority\": 99, \"read_only_mode\": true, @@ -349,7 +349,7 @@ fn rom_device_region_readwrite() { #[test] fn ram_device_region_readwrite() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); - let addr = 0x100_0000_0000; // 1TB + let addr = 0x1_0000_0000; // 4GB let file = File::create(&RAM_DEV_PATH).unwrap(); file.set_len(PAGE_SIZE).unwrap(); @@ -357,7 +357,7 @@ fn ram_device_region_readwrite() { "{{ \"execute\": \"update_region\", \"arguments\": {{ \"update_type\": \"add\", \"region_type\": \"ram_device_region\", - \"offset\": 1099511627776, + \"offset\": 4294967296, \"size\": 4096, \"priority\": 99, \"device_fd_path\": {:?} }} }}", @@ -389,7 +389,7 @@ fn ram_device_region_readwrite() { memory_test .state .borrow_mut() - .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"delete\", \"region_type\": \"ram_device_region\", \"offset\": 1099511627776, \"size\": 4096, \"priority\": 99 }}"); + .qmp("{ \"execute\": \"update_region\", \"arguments\": { \"update_type\": \"delete\", \"region_type\": \"ram_device_region\", \"offset\": 4294967296, \"size\": 4096, \"priority\": 99 }}"); remove_file(RAM_DEV_PATH).unwrap(); -- Gitee From 1eba622e7ed04c8d280f6094f09f409f28b4c8ef Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 21 Nov 2023 15:59:05 +0800 Subject: [PATCH 1471/2187] address-space: optimize new_mem() Optimize new_mem() by removing unsafe call. Signed-off-by: Zhao Yi Min --- address_space/src/host_mmap.rs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index f29db4970..6c5d4ebd4 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -19,8 +19,7 @@ use std::thread; use anyhow::{bail, Context, Result}; use log::{error, info}; use nix::sys::statfs::fstatfs; -use nix::unistd::sysconf; -use nix::unistd::SysconfVar; +use nix::unistd::{mkstemp, sysconf, unlink, SysconfVar}; use crate::{AddressRange, GuestAddress, Region}; use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; @@ -87,30 +86,25 @@ impl FileBackend { // The last six characters of template file must be "XXXXXX" for `mkstemp` // function to create unique temporary file. let fs_path = format!("{}{}", file_path, "/stratovirt_backmem_XXXXXX"); - let fs_cstr = std::ffi::CString::new(fs_path.clone()).unwrap().into_raw(); - let raw_fd = unsafe { libc::mkstemp(fs_cstr) }; - if raw_fd < 0 { - // SAFETY: fs_cstr obtained by calling CString::into_raw. - unsafe { - drop(std::ffi::CString::from_raw(fs_cstr)); + let (raw_fd, fs_tmp_path) = match mkstemp(fs_path.as_str()) { + Ok((fd, p)) => (fd, p), + Err(_) => { + return Err(std::io::Error::last_os_error()).with_context(|| { + format!("Failed to create file in directory: {} ", file_path) + }); } - return Err(std::io::Error::last_os_error()).with_context(|| { - format!("Failed to create file in directory: {} ", file_path) - }); - } + }; - if unsafe { libc::unlink(fs_cstr) } != 0 { + if unlink(fs_tmp_path.as_path()).is_err() { error!( - "Failed to unlink file \"{}\", error: {:?}", - fs_path, + "Failed to unlink file \"{:?}\", error: {:?}", + fs_tmp_path.as_path(), std::io::Error::last_os_error() - ); - } - // SAFETY: fs_cstr obtained by calling CString::into_raw. - unsafe { - drop(std::ffi::CString::from_raw(fs_cstr)); + ) } + + // SAFETY: only one FileBackend instance has the ownership of the file descriptor unsafe { File::from_raw_fd(raw_fd) } } else { need_unlink = !path.exists(); -- Gitee From 0b634049ae6d57bf2cb18c223082a4c9d8173380 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 21 Nov 2023 13:15:06 +0800 Subject: [PATCH 1472/2187] GTK: supports scrolling in x-axis direction Supports scrolling in x-axis direction by touchpad in GTK. Signed-off-by: Xiao Ye --- ui/src/gtk/draw.rs | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 915bb20ab..e888f88bc 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -250,31 +250,36 @@ fn da_pointer_callback(button_event: &gdk::EventButton) -> Result<()> { } fn da_scroll_callback(scroll_event: &gdk::EventScroll) -> Result<()> { - let button_mask = match scroll_event.direction() { - ScrollDirection::Up => INPUT_BUTTON_WHEEL_UP, - ScrollDirection::Down => INPUT_BUTTON_WHEEL_DOWN, - ScrollDirection::Left => INPUT_BUTTON_WHEEL_LEFT, - ScrollDirection::Right => INPUT_BUTTON_WHEEL_RIGHT, + match scroll_event.direction() { + ScrollDirection::Up => press_mouse(INPUT_BUTTON_WHEEL_UP), + ScrollDirection::Down => press_mouse(INPUT_BUTTON_WHEEL_DOWN), + ScrollDirection::Left => press_mouse(INPUT_BUTTON_WHEEL_LEFT), + ScrollDirection::Right => press_mouse(INPUT_BUTTON_WHEEL_RIGHT), ScrollDirection::Smooth => match scroll_event.scroll_deltas() { - Some((_, delta_y)) => { - if delta_y == 0.0 { + Some((delta_x, delta_y)) => { + if delta_x.eq(&0.0) && delta_y.eq(&0.0) { return Ok(()); } - if delta_y > 0.0 { - INPUT_BUTTON_WHEEL_DOWN - } else { - INPUT_BUTTON_WHEEL_UP + + // Horizontal scrolling. + if delta_x.gt(&0.0) { + press_mouse(INPUT_BUTTON_WHEEL_RIGHT)?; + } else if delta_x.lt(&0.0) { + press_mouse(INPUT_BUTTON_WHEEL_LEFT)?; + } + + // Vertical scrolling. + if delta_y.gt(&0.0) { + press_mouse(INPUT_BUTTON_WHEEL_DOWN)?; + } else if delta_y.lt(&0.0) { + press_mouse(INPUT_BUTTON_WHEEL_UP)?; } + Ok(()) } - None => return Ok(()), + None => Ok(()), }, - _ => 0x0, - }; - - input_button(button_mask, true)?; - input_point_sync()?; - input_button(button_mask, false)?; - input_point_sync() + _ => Ok(()), + } } /// Draw_area callback func for draw signal. -- Gitee From e28981eb830f64febdf3f99c366a8c95ef20c69d Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Wed, 22 Nov 2023 15:30:15 +0800 Subject: [PATCH 1473/2187] virtio-gpu: Add some log Signed-off-by: Meiling Wang --- virtio/src/device/gpu.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index f8acf9545..7129d2146 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; use anyhow::{anyhow, bail, Context, Result}; -use log::{error, warn}; +use log::{error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ @@ -114,7 +114,7 @@ trait CtrlHdr { } #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuCtrlHdr { hdr_type: u32, flags: u32, @@ -132,7 +132,7 @@ impl CtrlHdr for VirtioGpuCtrlHdr { } #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuRect { x_coord: u32, y_coord: u32, @@ -143,7 +143,7 @@ struct VirtioGpuRect { impl ByteCode for VirtioGpuRect {} #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuDisplayOne { rect: VirtioGpuRect, enabled: u32, @@ -153,7 +153,7 @@ struct VirtioGpuDisplayOne { impl ByteCode for VirtioGpuDisplayOne {} #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] struct VirtioGpuDisplayInfo { header: VirtioGpuCtrlHdr, pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_OUTPUTS], @@ -813,6 +813,7 @@ impl GpuIoHandler { } } drop(output_states_lock); + info!("virtio-gpu get the display info {:?}", display_info); self.send_response(req, &mut display_info) } @@ -1316,7 +1317,13 @@ impl GpuIoHandler { VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => self.cmd_resource_attach_backing(req), VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => self.cmd_resource_detach_backing(req), VIRTIO_GPU_CMD_GET_EDID => self.cmd_get_edid(req), - _ => self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req), + _ => { + error!( + "Failed to process unsupported command: {}", + req.header.hdr_type + ); + self.response_nodata(VIRTIO_GPU_RESP_ERR_UNSPEC, req) + } } { error!("Fail to handle GPU request, {:?}.", e); } @@ -1637,6 +1644,7 @@ impl VirtioDevice for Gpu { let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); register_event_helper(notifiers, None, &mut self.base.deactivate_evts)?; + info!("virtio-gpu has been activated"); Ok(()) } @@ -1646,6 +1654,9 @@ impl VirtioDevice for Gpu { display_set_major_screen("ramfb")?; set_run_stage(VmRunningStage::Bios); } - unregister_event_helper(None, &mut self.base.deactivate_evts) + + let result = unregister_event_helper(None, &mut self.base.deactivate_evts); + info!("virtio-gpu deactivate {:?}", result); + result } } -- Gitee From 88a4e444862f94045b80f07c506b5dcbf7723b5a Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 22 Nov 2023 18:25:50 +0800 Subject: [PATCH 1474/2187] chardev: fixup hang issue If peer is disconnected or receiver is none but input handler is called, this will trigger close_connection(). It results double lock. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 207ff3407..8bd9d84f9 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -422,6 +422,7 @@ fn get_socket_notifier(chardev: Arc>) -> Option { let peer_disconnected = event & EventSet::HANG_UP == EventSet::HANG_UP; if peer_disconnected && locked_chardev.receiver.is_none() { + drop(locked_chardev); return close_connection(); } -- Gitee From 951a61bf20bc5979d8994bd04c5dccf048724f64 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 23 Nov 2023 21:04:20 +0800 Subject: [PATCH 1475/2187] block_backend: fix a concurrent bug before main thread and io thread Fix d261ddfd qcow2: don't attempt to acquire locks during the exit phase. When stratovirt received signal of SIGTERM or normal shutdown vm, it will call qcow2 cleaner registed in GLOBAL_TEMP_CLEANER to cleanup qcow2 resources. In both scenarios, the cleaner will be called, and it will not grab the qcow2_driver lock to call qcow2 flush metadata function which may be also called in the disk iothread which has not quit. It may cause some unexpected errors. Fix it in these steps: 1. When received signal, just remeber it and handle it later in main thread. 2. Do not get qcow2 driver lock while drain io reqeust. And grab the qcow2 driver lock when flush metadata which will not cause deadlock. Signed-off-by: Yan Wang --- block_backend/src/file.rs | 2 +- block_backend/src/lib.rs | 41 +++++++-------- block_backend/src/qcow2/mod.rs | 9 +++- block_backend/src/raw.rs | 9 +++- machine_manager/src/event_loop.rs | 6 +++ machine_manager/src/signal_handler.rs | 74 ++++++++++++++++++--------- src/main.rs | 3 +- 7 files changed, 93 insertions(+), 51 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 41ee8ab0f..0d6e9dd3e 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -54,7 +54,7 @@ impl CombineRequest { pub struct FileDriver { pub file: File, aio: Rc>>, - incomplete: Arc, + pub incomplete: Arc, delete_evts: Vec, block_prop: BlockProperty, } diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 2d538222d..2e608422a 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -16,7 +16,11 @@ pub mod raw; use std::{ fs::File, - sync::{atomic::AtomicBool, Arc, Mutex}, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, Mutex, + }, + thread::yield_now, }; use anyhow::{bail, Context, Result}; @@ -311,6 +315,8 @@ pub trait BlockDriverOps: Send { fn drain_request(&self); + fn get_inflight(&self) -> Arc; + fn register_io_event( &mut self, device_broken: Arc, @@ -360,28 +366,19 @@ pub fn create_block_backend( let drain = prop.iothread.is_some(); let cloned_drive_id = prop.id.clone(); - // `qcow2` address has changed after Arc::new(), so we should use the new one. - let exit_qcow2 = new_qcow2.lock().unwrap(); - let exit_qcow2_ptr = &(*exit_qcow2) as *const Qcow2Driver as u64; - drop(exit_qcow2); let exit_notifier = Arc::new(move || { - // Note: Increase the reference count of `qcow2`` to avoid abnormal kill during the - // hot-unplug process, where a reference count of 0 leads to null pointer access. - let qcow2_h = cloned_qcow2.upgrade(); - if qcow2_h.is_none() { - return; - } - - // SAFETY: This callback is called only when the vm exits abnormally. - let qcow2 = unsafe { - &mut std::slice::from_raw_parts_mut(exit_qcow2_ptr as *mut Qcow2Driver, 1)[0] - }; - info!("clean up qcow2 {:?} resources.", cloned_drive_id); - if let Err(e) = qcow2.flush() { - error!("Failed to flush qcow2 {:?}", e); - } - if drain { - qcow2.drain_request(); + if let Some(qcow2) = cloned_qcow2.upgrade() { + info!("clean up qcow2 {:?} resources.", cloned_drive_id); + if drain { + info!("Drain the inflight IO for drive \"{}\"", cloned_drive_id); + let incomplete = qcow2.lock().unwrap().get_inflight(); + while incomplete.load(Ordering::SeqCst) != 0 { + yield_now(); + } + } + if let Err(e) = qcow2.lock().unwrap().flush() { + error!("Failed to flush qcow2 {:?}", e); + } } }) as Arc; TempCleaner::add_exit_notifier(prop.id, exit_notifier); diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index c32e06c1d..01889c1dc 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -25,7 +25,10 @@ use std::{ mem::size_of, os::unix::io::{AsRawFd, RawFd}, rc::Rc, - sync::{atomic::AtomicBool, Arc, Mutex, Weak}, + sync::{ + atomic::{AtomicBool, AtomicU64}, + Arc, Mutex, Weak, + }, time::Duration, }; @@ -1772,6 +1775,10 @@ impl BlockDriverOps for Qcow2Driver { self.driver.drain_request(); } + fn get_inflight(&self) -> Arc { + self.driver.incomplete.clone() + } + fn register_io_event( &mut self, broken: Arc, diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index aece80414..edf72e2ff 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -12,7 +12,10 @@ use std::{ fs::File, - sync::{atomic::AtomicBool, Arc, Mutex}, + sync::{ + atomic::{AtomicBool, AtomicU64}, + Arc, Mutex, + }, }; use anyhow::{bail, Result}; @@ -103,6 +106,10 @@ impl BlockDriverOps for RawDriver { self.driver.drain_request(); } + fn get_inflight(&self) -> Arc { + self.driver.incomplete.clone() + } + fn register_io_event( &mut self, broken: Arc, diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index f85982596..850c9040e 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -21,6 +21,7 @@ use log::info; use super::config::IothreadConfig; use crate::machine::IOTHREADS; use crate::qmp::qmp_schema::IothreadInfo; +use crate::signal_handler::get_signal; use util::loop_context::{ gen_delete_notifiers, get_notifiers_fds, EventLoopContext, EventLoopManager, EventNotifier, }; @@ -148,6 +149,11 @@ impl EventLoop { unsafe { if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { loop { + let sig_num = get_signal(); + if sig_num != 0 { + info!("MainLoop exits due to receive signal {}", sig_num); + return Ok(()); + } if !event_loop.main_loop.run()? { info!("MainLoop exits due to guest internal operation."); return Ok(()); diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 5ab376acb..49ebffad9 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -10,28 +10,25 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::io::Write; +use std::{ + io::Write, + sync::atomic::{AtomicI32, Ordering}, +}; use libc::{c_int, c_void, siginfo_t}; use vmm_sys_util::signal::register_signal_handler; use crate::{ event, + event_loop::EventLoop, qmp::{qmp_channel::QmpChannel, qmp_schema}, - temp_cleaner::TempCleaner, }; use util::set_termi_canon_mode; -const VM_EXIT_SUCCESS: i32 = 0; pub const VM_EXIT_GENE_ERR: i32 = 1; const SYSTEMCALL_OFFSET: isize = 6; -fn basic_clean() { - // clean temporary file - TempCleaner::clean(); - - set_termi_canon_mode().expect("Failed to set terminal to canon mode."); -} +static mut RECEIVED_SIGNAL: AtomicI32 = AtomicI32::new(0); pub fn exit_with_code(code: i32) { // Safe, because the basic_clean function has been executed before exit. @@ -40,27 +37,55 @@ pub fn exit_with_code(code: i32) { } } -extern "C" fn handle_signal_kill(num: c_int, _: *mut siginfo_t, _: *mut c_void) { - if QmpChannel::is_connected() { - let shutdown_msg = qmp_schema::Shutdown { - guest: false, - reason: "Guest shutdown by signal ".to_string() + &num.to_string(), - }; - event!(Shutdown; shutdown_msg); +pub fn set_signal(num: c_int) { + /* + * Other three signals need to send shutdown message more than SIGSYS. + * So, if received other three signals, it should replace the SIGSYS + * which has been received before. The SIGTERM/SIGINT/SIGHUP has the + * same treatment, if received one of them, no need to replace it. + */ + if [0, libc::SIGSYS].contains(&get_signal()) { + // SAFETY: just write a global variable. + unsafe { + RECEIVED_SIGNAL.store(num, Ordering::SeqCst); + } + EventLoop::get_ctx(None).unwrap().kick(); } +} + +pub fn get_signal() -> i32 { + // SAFETY: just read a global variable. + unsafe { RECEIVED_SIGNAL.load(Ordering::SeqCst) } +} + +pub fn handle_signal() { + let sig_num = get_signal(); + if sig_num != 0 { + set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); + if [libc::SIGTERM, libc::SIGINT, libc::SIGHUP].contains(&sig_num) + && QmpChannel::is_connected() + { + let shutdown_msg = qmp_schema::Shutdown { + guest: false, + reason: "Guest shutdown by signal ".to_string() + &sig_num.to_string(), + }; + event!(Shutdown; shutdown_msg); + } + } +} - basic_clean(); +extern "C" fn receive_signal_kill(num: c_int, _: *mut siginfo_t, _: *mut c_void) { + set_signal(num); write!( &mut std::io::stderr(), "Received kill signal, signal number: {} \r\n", num ) .expect("Failed to write to stderr"); - exit_with_code(VM_EXIT_SUCCESS); } -extern "C" fn handle_signal_sys(_: c_int, info: *mut siginfo_t, _: *mut c_void) { - basic_clean(); +extern "C" fn receive_signal_sys(num: c_int, info: *mut siginfo_t, _: *mut c_void) { + set_signal(num); let badcall = unsafe { *(info as *const i32).offset(SYSTEMCALL_OFFSET) as usize }; write!( &mut std::io::stderr(), @@ -68,17 +93,16 @@ extern "C" fn handle_signal_sys(_: c_int, info: *mut siginfo_t, _: *mut c_void) badcall ) .expect("Failed to write to stderr"); - exit_with_code(VM_EXIT_GENE_ERR); } /// Register kill signal handler. Signals supported now are SIGTERM and SIGSYS. pub fn register_kill_signal() { - register_signal_handler(libc::SIGTERM, handle_signal_kill) + register_signal_handler(libc::SIGTERM, receive_signal_kill) .expect("Register signal handler for SIGTERM failed!"); - register_signal_handler(libc::SIGSYS, handle_signal_sys) + register_signal_handler(libc::SIGSYS, receive_signal_sys) .expect("Register signal handler for SIGSYS failed!"); - register_signal_handler(libc::SIGINT, handle_signal_kill) + register_signal_handler(libc::SIGINT, receive_signal_kill) .expect("Register signal handler for SIGINT failed!"); - register_signal_handler(libc::SIGHUP, handle_signal_kill) + register_signal_handler(libc::SIGHUP, receive_signal_kill) .expect("Register signal handler for SIGHUP failed!"); } diff --git a/src/main.rs b/src/main.rs index 0e25d354d..18eee88e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ use machine_manager::{ event_loop::EventLoop, qmp::qmp_channel::QmpChannel, qmp::qmp_socket::Socket, - signal_handler::{exit_with_code, register_kill_signal, VM_EXIT_GENE_ERR}, + signal_handler::{exit_with_code, handle_signal, register_kill_signal, VM_EXIT_GENE_ERR}, temp_cleaner::TempCleaner, test_server::TestSock, }; @@ -120,6 +120,7 @@ fn run() -> Result<()> { info!("MainLoop over, Vm exit"); // clean temporary file TempCleaner::clean(); + handle_signal(); } Err(ref e) => { set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); -- Gitee From a48231bd690fb745d2d0f2baf3b1e3ee839dc6f4 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 23 Nov 2023 21:05:37 +0800 Subject: [PATCH 1476/2187] block_backend: fix several potential problems when flushing metadata 1. Grab the upgrade qcow2_driver in the whole lifecycle of qcow2_flush_metadata(); 2. Add drive id to the log; Signed-off-by: Yan Wang --- block_backend/src/lib.rs | 4 ++-- block_backend/src/qcow2/mod.rs | 23 +++++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 2e608422a..5fe2c9dc6 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -381,10 +381,10 @@ pub fn create_block_backend( } } }) as Arc; - TempCleaner::add_exit_notifier(prop.id, exit_notifier); + TempCleaner::add_exit_notifier(prop.id.clone(), exit_notifier); // Add timer for flushing qcow2 metadata. - qcow2_flush_metadata(Arc::downgrade(&new_qcow2)); + qcow2_flush_metadata(Arc::downgrade(&new_qcow2), prop.id); Ok(new_qcow2) } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 01889c1dc..e6613bf6c 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -237,20 +237,27 @@ impl Drop for Qcow2Driver { } /// Add timer for flushing qcow2 metadata. -pub fn qcow2_flush_metadata(qcow2_driver: Weak>>) { - if qcow2_driver.upgrade().is_none() { - info!("Qcow2 flush metadata timer exit"); +pub fn qcow2_flush_metadata( + qcow2_driver: Weak>>, + drive_id: String, +) { + let qcow2_d = qcow2_driver.upgrade(); + if qcow2_d.is_none() { + info!("Qcow2 for drive \"{}\" flush metadata timer exit", drive_id); return; } - let driver = qcow2_driver.upgrade().unwrap(); + let driver = qcow2_d.unwrap(); let mut locked_driver = driver.lock().unwrap(); - locked_driver - .flush() - .unwrap_or_else(|e| error!("Flush qcow2 metadata failed, {:?}", e)); + locked_driver.flush().unwrap_or_else(|e| { + error!( + "Flush qcow2 metadata failed for drive {}, {:?}", + drive_id, e + ) + }); let flush_func = Box::new(move || { - qcow2_flush_metadata(qcow2_driver.clone()); + qcow2_flush_metadata(qcow2_driver.clone(), drive_id.clone()); }); let iothread = locked_driver.sync_aio.borrow().prop.iothread.clone(); EventLoop::get_ctx(iothread.as_ref()).unwrap().timer_add( -- Gitee From bd2b4922a8b04a59c5abec1f32e93a6ec514fd81 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Nov 2023 09:19:52 +0800 Subject: [PATCH 1477/2187] bugfix: delete fwcfg_dev from X86 StdMachine MachineBase already contains fwcfg_dev. Therefore, fwcfg_dev is deleted from X86 StdMachine. Signed-off-by: Mingwang Li --- machine/src/standard_vm/x86_64/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index b9b68f075..142e0cdc1 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -121,8 +121,6 @@ pub struct StdMachine { shutdown_req: Arc, /// List contains the boot order of boot devices. boot_order_list: Arc>>, - /// FwCfg device. - fwcfg_dev: Option>>, } impl StdMachine { @@ -158,7 +156,6 @@ impl StdMachine { })?, ), boot_order_list: Arc::new(Mutex::new(Vec::new())), - fwcfg_dev: None, }) } @@ -267,7 +264,7 @@ impl StdMachineOps for StdMachine { let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.base.sysbus) .with_context(|| "Failed to realize fwcfg device")?; - self.fwcfg_dev = Some(fwcfg_dev.clone()); + self.base.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(Some(fwcfg_dev)) } -- Gitee From 623e8dd18fecc3904992433a0d1132e77b755575 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 24 Nov 2023 09:22:52 +0800 Subject: [PATCH 1478/2187] PCI: Refactor some public functions Refactor some public functions as the methods of a certain class so as to be more object-oriented. Signed-off-by: Jinhao Gao --- devices/src/pci/bus.rs | 7 ++ devices/src/pci/config.rs | 4 +- devices/src/pci/mod.rs | 2 +- devices/src/pci/msix.rs | 124 +++++++++++++---------------- devices/src/usb/xhci/xhci_pci.rs | 3 +- vfio/src/vfio_pci.rs | 16 ++-- virtio/src/transport/virtio_pci.rs | 8 +- 7 files changed, 82 insertions(+), 82 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 0fa061395..b02159031 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; +use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; @@ -241,6 +242,12 @@ impl PciBus { .unwrap() .read_config(offset, data); } + + pub fn update_dev_id(&self, devfn: u8, dev_id: &Arc) { + let bus_num = self.number(SECONDARY_BUS_NUM as usize); + let device_id = ((bus_num as u16) << 8) | (devfn as u16); + dev_id.store(device_id, Ordering::Release); + } } #[cfg(test)] diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 7af249346..b035c70b1 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -17,7 +17,7 @@ use anyhow::{anyhow, Context, Result}; use log::{error, warn}; use crate::pci::intx::Intx; -use crate::pci::msix::{is_msix_enabled, Msix, MSIX_TABLE_ENTRY_SIZE}; +use crate::pci::msix::{Msix, MSIX_TABLE_ENTRY_SIZE}; use crate::pci::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, PciError, BDF_FUNC_SHIFT, @@ -625,7 +625,7 @@ impl PciConfig { } if let Some(msix) = &mut self.msix { - if is_msix_enabled(msix.lock().unwrap().msix_cap_offset as usize, &self.config) { + if msix.lock().unwrap().is_enabled(&self.config) { if let Some(intx) = &self.intx { intx.lock().unwrap().notify(0); } diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 1bb09976f..e9ba0c48a 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -29,7 +29,7 @@ pub use config::{PciConfig, INTERRUPT_PIN}; pub use error::PciError; pub use host::PciHost; pub use intx::{init_intx, InterruptHandler, PciIntxState}; -pub use msix::{init_msix, is_msix_enabled}; +pub use msix::init_msix; pub use root_port::RootPort; use std::{ diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 12606614f..6d92a06b9 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -13,18 +13,14 @@ use std::cmp::max; use std::collections::HashMap; use std::sync::atomic::{AtomicU16, Ordering}; -use std::sync::{Arc, Mutex, Weak}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; -use crate::pci::config::{ - CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO, SECONDARY_BUS_NUM, -}; -use crate::pci::{ - le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, PciBus, -}; +use crate::pci::config::{CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO}; +use crate::pci::{le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64}; use address_space::{GuestAddress, Region, RegionOps}; use hypervisor::kvm::{MsiVector, KVM_FDS}; use migration::{ @@ -136,6 +132,24 @@ impl Msix { self.mask_all_vectors(); } + pub fn is_enabled(&self, config: &[u8]) -> bool { + let offset: usize = self.msix_cap_offset as usize + MSIX_CAP_CONTROL as usize; + let msix_ctl = le_read_u16(config, offset).unwrap(); + if msix_ctl & MSIX_CAP_ENABLE > 0 { + return true; + } + false + } + + pub fn is_func_masked(&self, config: &[u8]) -> bool { + let offset: usize = self.msix_cap_offset as usize + MSIX_CAP_CONTROL as usize; + let msix_ctl = le_read_u16(config, offset).unwrap(); + if msix_ctl & MSIX_CAP_FUNC_MASK > 0 { + return true; + } + false + } + fn mask_all_vectors(&mut self) { let nr_vectors: usize = self.table.len() / MSIX_TABLE_ENTRY_SIZE as usize; for v in 0..nr_vectors { @@ -445,6 +459,35 @@ impl Msix { } } + pub fn send_msix(&self, vector: u16, dev_id: u16) { + let msg = self.get_message(vector); + #[cfg(target_arch = "aarch64")] + let flags: u32 = kvm_bindings::KVM_MSI_VALID_DEVID; + #[cfg(target_arch = "x86_64")] + let flags: u32 = 0; + + let kvm_msi = kvm_bindings::kvm_msi { + address_lo: msg.address_lo, + address_hi: msg.address_hi, + data: msg.data, + flags, + devid: dev_id as u32, + pad: [0; 12], + }; + + if is_test_enabled() { + let data = msg.data; + let mut addr: u64 = msg.address_hi as u64; + addr = (addr << 32) + msg.address_lo as u64; + add_msix_msg(addr, data); + return; + } + + if let Err(e) = KVM_FDS.load().vm_fd.as_ref().unwrap().signal_msi(kvm_msi) { + error!("Send msix error: {:?}", e); + }; + } + pub fn notify(&mut self, vector: u16, dev_id: u16) { if vector >= self.table.len() as u16 / MSIX_TABLE_ENTRY_SIZE { warn!("Invalid msix vector {}.", vector); @@ -456,7 +499,7 @@ impl Msix { return; } - send_msix(self.get_message(vector), dev_id); + self.send_msix(vector, dev_id); } pub fn write_config(&mut self, config: &[u8], dev_id: u16, offset: usize, data: &[u8]) { @@ -469,8 +512,8 @@ impl Msix { return; } - let masked: bool = is_msix_func_masked(self.msix_cap_offset as usize, config); - let enabled: bool = is_msix_enabled(self.msix_cap_offset as usize, config); + let masked: bool = self.is_func_masked(config); + let enabled: bool = self.is_enabled(config); let mask_state_changed = !((self.func_masked == masked) && (self.enabled == enabled)); self.func_masked = masked; @@ -481,7 +524,7 @@ impl Msix { for v in 0..max_vectors_nr { if !self.is_vector_masked(v) && self.is_vector_pending(v) { self.clear_pending_vector(v); - send_msix(self.get_message(v), dev_id); + self.send_msix(v, dev_id); } } } @@ -563,7 +606,7 @@ impl MigrationHook for Msix { if self.is_vector_pending(vector) { self.clear_pending_vector(vector); - send_msix(msg, self.dev_id.load(Ordering::Acquire)); + self.send_msix(vector, self.dev_id.load(Ordering::Acquire)); } } } @@ -572,52 +615,6 @@ impl MigrationHook for Msix { } } -pub fn is_msix_enabled(msix_cap_offset: usize, config: &[u8]) -> bool { - let offset: usize = msix_cap_offset + MSIX_CAP_CONTROL as usize; - let msix_ctl = le_read_u16(config, offset).unwrap(); - if msix_ctl & MSIX_CAP_ENABLE > 0 { - return true; - } - false -} - -fn is_msix_func_masked(msix_cap_offset: usize, config: &[u8]) -> bool { - let offset: usize = msix_cap_offset + MSIX_CAP_CONTROL as usize; - let msix_ctl = le_read_u16(config, offset).unwrap(); - if msix_ctl & MSIX_CAP_FUNC_MASK > 0 { - return true; - } - false -} - -fn send_msix(msg: Message, dev_id: u16) { - #[cfg(target_arch = "aarch64")] - let flags: u32 = kvm_bindings::KVM_MSI_VALID_DEVID; - #[cfg(target_arch = "x86_64")] - let flags: u32 = 0; - - let kvm_msi = kvm_bindings::kvm_msi { - address_lo: msg.address_lo, - address_hi: msg.address_hi, - data: msg.data, - flags, - devid: dev_id as u32, - pad: [0; 12], - }; - - if is_test_enabled() { - let data = msg.data; - let mut addr: u64 = msg.address_hi as u64; - addr = (addr << 32) + msg.address_lo as u64; - add_msix_msg(addr, data); - return; - } - - if let Err(e) = KVM_FDS.load().vm_fd.as_ref().unwrap().signal_msi(kvm_msi) { - error!("Send msix error: {:?}", e); - }; -} - /// MSI-X initialization. /// /// # Arguments @@ -708,17 +705,6 @@ pub fn init_msix( Ok(()) } -pub fn update_dev_id(parent_bus: &Weak>, devfn: u8, dev_id: &Arc) { - let bus_num = parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .number(SECONDARY_BUS_NUM as usize); - let device_id = ((bus_num as u16) << 8) | (devfn as u16); - dev_id.store(device_id, Ordering::Release); -} - #[cfg(test)] mod tests { use super::*; diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 84d84e0da..fe3a5f380 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -31,7 +31,6 @@ use crate::pci::config::{ PciConfig, RegionType, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_XHCI, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use crate::pci::msix::update_dev_id; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; use crate::usb::UsbDevice; use crate::{Device, DeviceBase}; @@ -346,9 +345,9 @@ impl PciDevOps for XhciPciDevice { } fn write_config(&mut self, offset: usize, data: &[u8]) { - update_dev_id(&self.base.parent_bus, self.base.devfn, &self.dev_id); let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); + locked_parent_bus.update_dev_id(self.base.devfn, &self.dev_id); self.base.config.write( offset, diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index f723d414b..a5c5f7267 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -36,9 +36,8 @@ use devices::pci::config::{ PCI_CONFIG_SPACE_SIZE, REG_SIZE, }; use devices::pci::msix::{ - is_msix_enabled, update_dev_id, Msix, MSIX_CAP_CONTROL, MSIX_CAP_ENABLE, MSIX_CAP_FUNC_MASK, - MSIX_CAP_ID, MSIX_CAP_SIZE, MSIX_CAP_TABLE, MSIX_TABLE_BIR, MSIX_TABLE_ENTRY_SIZE, - MSIX_TABLE_OFFSET, MSIX_TABLE_SIZE_MAX, + Msix, MSIX_CAP_CONTROL, MSIX_CAP_ENABLE, MSIX_CAP_FUNC_MASK, MSIX_CAP_ID, MSIX_CAP_SIZE, + MSIX_CAP_TABLE, MSIX_TABLE_BIR, MSIX_TABLE_ENTRY_SIZE, MSIX_TABLE_OFFSET, MSIX_TABLE_SIZE_MAX, }; use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, @@ -501,7 +500,8 @@ impl VfioPciDevice { } let entry = locked_msix.get_message(vector as u16); - update_dev_id(&parent_bus, devfn, &dev_id); + let parent_bus = parent_bus.upgrade().unwrap(); + parent_bus.lock().unwrap().update_dev_id(devfn, &dev_id); let msix_vector = MsiVector { msg_addr_lo: entry.address_lo, msg_addr_hi: entry.address_hi, @@ -977,7 +977,9 @@ impl PciDevOps for VfioPciDevice { .msix .as_ref() .map_or(0, |m| m.lock().unwrap().msix_cap_offset as usize); - let was_enable = is_msix_enabled(cap_offset, &self.base.config.config); + let was_enable = self.base.config.msix.as_ref().map_or(false, |m| { + m.lock().unwrap().is_enabled(&self.base.config.config) + }); let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); self.base.config.write( @@ -998,7 +1000,9 @@ impl PciDevOps for VfioPciDevice { } } } else if ranges_overlap(offset, size, cap_offset, MSIX_CAP_SIZE as usize).unwrap() { - let is_enable = is_msix_enabled(cap_offset, &self.base.config.config); + let is_enable = self.base.config.msix.as_ref().map_or(false, |m| { + m.lock().unwrap().is_enabled(&self.base.config.config) + }); if !was_enable && is_enable { if let Err(e) = self.vfio_enable_msix() { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 97051ed6c..1b7dfb040 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -40,7 +40,7 @@ use devices::pci::config::{ PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, STATUS_INTERRUPT, SUBSYSTEM_ID, SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use devices::pci::msix::{update_dev_id, MsixState}; +use devices::pci::msix::MsixState; use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, @@ -459,7 +459,11 @@ impl VirtioPciDevice { } locked_dev.virtio_base_mut().queues = queues; - update_dev_id(&self.base.parent_bus, self.base.devfn, &self.dev_id); + let parent = self.base.parent_bus.upgrade().unwrap(); + parent + .lock() + .unwrap() + .update_dev_id(self.base.devfn, &self.dev_id); if self.need_irqfd { let mut queue_num = locked_dev.queue_num(); // No need to create call event for control queue. -- Gitee From fcb4034a86b01b54605022f424fa0a6d05376431 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Fri, 24 Nov 2023 14:52:38 +0800 Subject: [PATCH 1479/2187] gtk: fix cursor overlap When guest os use software cursor as Windows use big-size cursor, it will not set gtk cursor image proactively. This lead us to see that the cursor overlaps. As a special gtk app, the guest os manages its own cursor. The gtk default cursor should be set transparent at first. Signed-off-by: Binfeng Wu --- ui/src/gtk/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 8d1ce7382..24b31ae5b 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -355,6 +355,7 @@ pub struct GtkDisplayScreen { dev_name: String, show_menu: RadioMenuItem, draw_area: DrawingArea, + cursor_trsp: bool, // GTK own default cursor transparent or not source_surface: DisplaySurface, transfer_surface: Option, cairo_image: Option, @@ -405,6 +406,7 @@ impl GtkDisplayScreen { window, dev_name, draw_area: DrawingArea::default(), + cursor_trsp: false, show_menu: RadioMenuItem::default(), source_surface: surface, transfer_surface: None, @@ -951,6 +953,17 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { borrowed_gs.scale_x = window_width / surface_width as f64; borrowed_gs.scale_y = window_height / surface_height as f64; } + + // Vm desktop manage its own cursor, gtk cursor need to be trsp firstly. + if !borrowed_gs.cursor_trsp { + if let Some(win) = borrowed_gs.draw_area.window() { + let dpy = borrowed_gs.window.display(); + let gtk_cursor = gdk::Cursor::for_display(&dpy, gdk::CursorType::BlankCursor); + win.set_cursor(gtk_cursor.as_ref()); + } + borrowed_gs.cursor_trsp = true; + } + drop(borrowed_gs); if need_resize { -- Gitee From cfc59abfb384534070e74ddac56224876937dae8 Mon Sep 17 00:00:00 2001 From: dehengli Date: Sun, 26 Nov 2023 09:30:27 +0800 Subject: [PATCH 1480/2187] machine: config max cpus for x86_64 machine 1. Set max_cpus in fwcfg device. 2. Add non boot cpu lapic in MADT table. 3. Use max_vcpus instead of nr_vcpus in X86CPUState. Signed-off-by: dehengli --- cpu/src/x86_64/mod.rs | 14 +++++++------- machine/src/lib.rs | 4 +++- machine/src/micro_vm/mod.rs | 1 + machine/src/standard_vm/mod.rs | 6 +++++- machine/src/standard_vm/x86_64/mod.rs | 26 ++++++++++++++++++++++---- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 17faff2a2..4c1adaec3 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -109,7 +109,7 @@ impl X86CPUTopology { #[derive(Copy, Clone, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] pub struct X86CPUState { - nr_vcpus: u32, + max_vcpus: u32, nr_threads: u32, nr_cores: u32, nr_dies: u32, @@ -134,8 +134,8 @@ impl X86CPUState { /// # Arguments /// /// * `vcpu_id` - ID of this `CPU`. - /// * `nr_vcpus` - Number of vcpus. - pub fn new(vcpu_id: u32, nr_vcpus: u32) -> Self { + /// * `max_vcpus` - Number of vcpus. + pub fn new(vcpu_id: u32, max_vcpus: u32) -> Self { let mp_state = kvm_mp_state { mp_state: if vcpu_id == 0 { KVM_MP_STATE_RUNNABLE @@ -145,7 +145,7 @@ impl X86CPUState { }; X86CPUState { apic_id: vcpu_id, - nr_vcpus, + max_vcpus, mp_state, nr_threads: 1, nr_cores: 1, @@ -157,7 +157,7 @@ impl X86CPUState { pub fn set(&mut self, cpu_state: &Arc>) { let locked_cpu_state = cpu_state.lock().unwrap(); - self.nr_vcpus = locked_cpu_state.nr_vcpus; + self.max_vcpus = locked_cpu_state.max_vcpus; self.apic_id = locked_cpu_state.apic_id; self.regs = locked_cpu_state.regs; self.sregs = locked_cpu_state.sregs; @@ -468,8 +468,8 @@ impl X86CPUState { &mut entry.edx, ); entry.eax &= !0xfc00_0000; - if entry.eax & 0x0001_ffff != 0 && self.nr_vcpus > 1 { - entry.eax |= (self.nr_vcpus - 1) << 26; + if entry.eax & 0x0001_ffff != 0 && self.max_vcpus > 1 { + entry.eax |= (self.max_vcpus - 1) << 26; } } 6 => { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e866ed58b..48887f005 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -415,10 +415,12 @@ pub trait MachineOps { /// /// * `vm` - `MachineInterface` to obtain functions cpu can use. /// * `nr_cpus` - The number of vcpus. + /// * `max_cpus` - The max number of vcpus. /// * `boot_cfg` - Boot message generated by reading boot source to guest memory. fn init_vcpu( vm: Arc>, nr_cpus: u8, + #[cfg(target_arch = "x86_64")] max_cpus: u8, topology: &CPUTopology, boot_cfg: &Option, #[cfg(target_arch = "aarch64")] vcpu_cfg: &Option, @@ -439,7 +441,7 @@ pub trait MachineOps { #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); #[cfg(target_arch = "x86_64")] - let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(nr_cpus)); + let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(max_cpus)); let cpu = Arc::new(CPU::new( Arc::new(vcpu_fd), diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c105a644e..457fc2f07 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -702,6 +702,7 @@ impl MachineOps for LightMachine { locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), vm_config.machine_config.nr_cpus, + vm_config.machine_config.max_cpus, &topology, &boot_config, )?); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index a9c74ff8a..66991d6c0 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -189,7 +189,11 @@ trait StdMachineOps: AcpiBuilder + MachineOps { Ok(()) } - fn add_fwcfg_device(&mut self, _nr_cpus: u8) -> Result>>> { + fn add_fwcfg_device( + &mut self, + _nr_cpus: u8, + #[cfg(target_arch = "x86_64")] _max_cpus: u8, + ) -> Result>>> { bail!("Not implemented"); } diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 142e0cdc1..6c5090c3d 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -251,10 +251,14 @@ impl StdMachineOps for StdMachine { Ok(()) } - fn add_fwcfg_device(&mut self, nr_cpus: u8) -> super::Result>>> { + fn add_fwcfg_device( + &mut self, + nr_cpus: u8, + max_cpus: u8, + ) -> super::Result>>> { let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone()); fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; - fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, nr_cpus.as_bytes().to_vec())?; + fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, max_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; let boot_order = Vec::::new(); @@ -383,6 +387,7 @@ impl MachineOps for StdMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; + let max_cpus = vm_config.machine_config.max_cpus; let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; @@ -405,7 +410,7 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; - let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; + let fwcfg = locked_vm.add_fwcfg_device(nr_cpus, max_cpus)?; let migrate = locked_vm.get_migrate_info(); let boot_config = if migrate.0 == MigrateMode::Unknown { @@ -421,6 +426,7 @@ impl MachineOps for StdMachine { locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), nr_cpus, + max_cpus, &topology, &boot_config, )?); @@ -649,8 +655,20 @@ impl AcpiBuilder for StdMachine { madt.append_child(&lapic.aml_bytes()); }); + // Add non boot cpu lapic. + for cpuid in self.base.cpu_topo.nrcpus as u8..self.base.cpu_topo.max_cpus { + let lapic = AcpiLocalApic { + type_id: 0, + length: size_of::() as u8, + processor_uid: cpuid, + apic_id: cpuid, + flags: 2, // Flags: hotplug enabled. + }; + madt.append_child(&lapic.aml_bytes()); + } + let madt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &madt) - .with_context(|| "Fail to add DSTD table to loader")?; + .with_context(|| "Fail to add MADT table to loader")?; Ok(madt_begin) } -- Gitee From 4d18e354b5a064df0c1adfffeefe311aee69ed95 Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 21 Nov 2023 09:36:00 +0800 Subject: [PATCH 1481/2187] device: extend ged event and enable x86_64 ged device 1. Extend ged event, add cpu resize event. 2. X86_64 enable add_ged_device functions and add Ged address space in memory layout. Signed-off-by: dehengli --- devices/src/acpi/ged.rs | 79 +++++++++++++++++++++++++- machine/src/lib.rs | 2 - machine/src/micro_vm/mod.rs | 1 - machine/src/standard_vm/aarch64/mod.rs | 4 +- machine/src/standard_vm/x86_64/mod.rs | 32 +++++++++++ machine_manager/src/qmp/qmp_schema.rs | 16 ++++++ 6 files changed, 126 insertions(+), 8 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index d22701cdd..a208762f8 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -27,8 +27,11 @@ use acpi::{ AmlEqual, AmlExtendedInterrupt, AmlField, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUnit, AmlFieldUpdateRule, AmlIf, AmlIntShare, AmlInteger, AmlLocal, AmlMethod, AmlName, AmlNameDecl, AmlNotify, AmlOpRegion, AmlResTemplate, AmlResourceUsage, AmlScopeBuilder, AmlStore, AmlString, - INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; +#[cfg(target_arch = "x86_64")] +use acpi::{AmlCallWithArgs1, AmlOne}; +#[cfg(target_arch = "aarch64")] +use acpi::{INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT}; use address_space::GuestAddress; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -43,11 +46,31 @@ pub enum AcpiEvent { AcadSt = 2, BatteryInf = 4, BatterySt = 8, + CpuResize = 16, } const AML_GED_EVT_REG: &str = "EREG"; const AML_GED_EVT_SEL: &str = "ESEL"; +pub struct GedEvent { + power_button: Arc, + #[cfg(target_arch = "x86_64")] + cpu_resize: Arc, +} + +impl GedEvent { + pub fn new( + power_button: Arc, + #[cfg(target_arch = "x86_64")] cpu_resize: Arc, + ) -> GedEvent { + GedEvent { + power_button, + #[cfg(target_arch = "x86_64")] + cpu_resize, + } + } +} + #[derive(Clone)] pub struct Ged { base: SysBusDevBase, @@ -69,7 +92,7 @@ impl Ged { pub fn realize( mut self, sysbus: &mut SysBus, - power_button: Arc, + ged_event: GedEvent, battery_present: bool, region_base: u64, region_size: u64, @@ -83,8 +106,11 @@ impl Ged { sysbus.attach_device(&dev, region_base, region_size, "Ged")?; let ged = dev.lock().unwrap(); - ged.register_acpi_powerdown_event(power_button) + ged.register_acpi_powerdown_event(ged_event.power_button) .with_context(|| "Failed to register ACPI powerdown event.")?; + #[cfg(target_arch = "x86_64")] + ged.register_acpi_cpu_resize_event(ged_event.cpu_resize) + .with_context(|| "Failed to register ACPI cpu resize event.")?; Ok(dev.clone()) } @@ -116,6 +142,35 @@ impl Ged { Ok(()) } + #[cfg(target_arch = "x86_64")] + fn register_acpi_cpu_resize_event(&self, cpu_resize: Arc) -> Result<()> { + let cpu_resize_fd = cpu_resize.as_raw_fd(); + let clone_ged = self.clone(); + let cpu_resize_handler: Rc = Rc::new(move |_, _| { + read_fd(cpu_resize_fd); + clone_ged + .notification_type + .store(AcpiEvent::CpuResize as u32, Ordering::SeqCst); + clone_ged.inject_interrupt(); + if QmpChannel::is_connected() { + event!(CpuResize); + } + None + }); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + cpu_resize_fd, + None, + EventSet::IN, + vec![cpu_resize_handler], + ); + + EventLoop::update_event(vec![notifier], None) + .with_context(|| "Failed to register cpu resize notifier.")?; + Ok(()) + } + pub fn inject_acpi_event(&self, evt: AcpiEvent) { self.notification_type .fetch_or(evt as u32, Ordering::SeqCst); @@ -178,7 +233,10 @@ impl AmlBuilder for Ged { let mut res = AmlResTemplate::new(); // SPI start at interrupt number 32 on aarch64 platform. + #[cfg(target_arch = "aarch64")] let irq_base = INTERRUPT_PPIS_COUNT + INTERRUPT_SGIS_COUNT; + #[cfg(target_arch = "x86_64")] + let irq_base = 0; res.append_child(AmlExtendedInterrupt::new( AmlResourceUsage::Consumer, AmlEdgeLevel::Edge, @@ -237,6 +295,21 @@ impl AmlBuilder for Ged { method.append_child(if_scope); } + #[cfg(target_arch = "x86_64")] + { + // Call cpu hot(un)plug method. + let mut cpu_if_scope = AmlIf::new(AmlEqual::new( + AmlAnd::new( + AmlLocal(0), + AmlInteger(AcpiEvent::CpuResize as u64), + AmlLocal(1), + ), + AmlInteger(AcpiEvent::CpuResize as u64), + )); + cpu_if_scope.append_child(AmlCallWithArgs1::new("\\_SB.PRES.CSCN", AmlOne)); + method.append_child(cpu_if_scope); + } + acpi_dev.append_child(method); acpi_dev.aml_bytes() diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 48887f005..9bb018f2d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -526,7 +526,6 @@ pub trait MachineOps { fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] mem_size: u64) -> Result<()>; /// Add Generic event device. - #[cfg(target_arch = "aarch64")] fn add_ged_device(&mut self) -> Result<()>; /// Add serial device. @@ -1707,7 +1706,6 @@ pub trait MachineOps { ) .with_context(|| MachineError::AddDevErr("RTC".to_string()))?; - #[cfg(target_arch = "aarch64")] self.add_ged_device() .with_context(|| MachineError::AddDevErr("Ged".to_string()))?; diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index 457fc2f07..bedb272e4 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -559,7 +559,6 @@ impl MachineOps for LightMachine { Ok(()) } - #[cfg(target_arch = "aarch64")] fn add_ged_device(&mut self) -> MachineResult<()> { Ok(()) } diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 078aedecc..678dcbbbb 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -41,7 +41,7 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, Region}; use cpu::{CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE}; -use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged}; +use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged, GedEvent}; use devices::acpi::power::PowerDev; #[cfg(feature = "ramfb")] use devices::legacy::Ramfb; @@ -499,7 +499,7 @@ impl MachineOps for StdMachine { let ged_dev = ged .realize( &mut self.base.sysbus, - self.power_button.clone(), + GedEvent::new(self.power_button.clone()), battery_present, MEM_LAYOUT[LayoutEntryType::Ged as usize].0, MEM_LAYOUT[LayoutEntryType::Ged as usize].1, diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 6c5090c3d..585fca6a6 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -36,6 +36,7 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology}; +use devices::acpi::ged::{Ged, GedEvent}; use devices::legacy::{ error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, @@ -75,6 +76,7 @@ pub enum LayoutEntryType { MemBelow4g = 0_usize, PcieEcam, PcieMmio, + GedMmio, Mmio, IoApic, LocalApic, @@ -87,6 +89,7 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0, 0x8000_0000), // MemBelow4g (0xB000_0000, 0x1000_0000), // PcieEcam (0xC000_0000, 0x3000_0000), // PcieMmio + (0xF000_0000, 0x04), // GedMmio (0xF010_0000, 0x200), // Mmio (0xFEC0_0000, 0x10_0000), // IoApic (0xFEE0_0000, 0x10_0000), // LocalApic @@ -119,6 +122,10 @@ pub struct StdMachine { reset_req: Arc, /// Shutdown_req, handle VM 'ShutDown' event. shutdown_req: Arc, + /// VM power button, handle VM `Powerdown` event. + power_button: Arc, + /// CPU Resize request, handle vm cpu hot(un)plug event. + cpu_resize_req: Arc, /// List contains the boot order of boot devices. boot_order_list: Arc>>, } @@ -155,6 +162,14 @@ impl StdMachine { MachineError::InitEventFdErr("shutdown request".to_string()) })?, ), + power_button: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("power button".to_string()))?, + ), + cpu_resize_req: Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("cpu resize".to_string()))?, + ), boot_order_list: Arc::new(Mutex::new(Vec::new())), }) } @@ -371,6 +386,23 @@ impl MachineOps for StdMachine { Ok(()) } + fn add_ged_device(&mut self) -> Result<()> { + let ged = Ged::default(); + let region_base: u64 = MEM_LAYOUT[LayoutEntryType::GedMmio as usize].0; + let region_size: u64 = MEM_LAYOUT[LayoutEntryType::GedMmio as usize].1; + + let ged_event = GedEvent::new(self.power_button.clone(), self.cpu_resize_req.clone()); + ged.realize( + &mut self.base.sysbus, + ged_event, + false, + region_base, + region_size, + ) + .with_context(|| "Failed to realize Ged")?; + Ok(()) + } + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 879748901..82ae6425f 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1742,6 +1742,7 @@ define_qmp_event_enum!( Stop("STOP", Stop, default), Resume("RESUME", Resume, default), Powerdown("POWERDOWN", Powerdown, default), + CpuResize("CPU_RESIZE", CpuResize, default), DeviceDeleted("DEVICE_DELETED", DeviceDeleted), BalloonChanged("BALLOON_CHANGED", BalloonInfo) ); @@ -1840,6 +1841,21 @@ pub struct Resume {} #[serde(deny_unknown_fields)] pub struct Powerdown {} +/// CpuResize +/// +/// Emitted when the virtual machine cpu hot(un)plug execution. +/// +/// # Examples +/// +/// ```text +/// <- { "event": "CPU_RESIZE", +/// "data": {}, +/// "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct CpuResize {} + /// DeviceDeleted /// /// Emitted whenever the device removal completion is acknowledged by the guest. -- Gitee From 71d1e5e9b8fbe3bb881d4718b452c7fd2795f0bb Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 28 Nov 2023 14:09:01 +0800 Subject: [PATCH 1482/2187] device: add cpu controller device for x86_64 standard machine 1. Add Cpu controller to handle ged cpu resize(hotplug/hotpunplug) event. 2. X86_64 standard machine add cpu controller address space in memory layout and init cpu controller when vm realize. Signed-off-by: dehengli --- Cargo.lock | 1 + acpi/src/aml_compiler.rs | 3 +- devices/Cargo.toml | 1 + devices/src/acpi/cpu_controller.rs | 574 ++++++++++++++++++++++++++ devices/src/acpi/mod.rs | 2 + machine/src/standard_vm/mod.rs | 32 ++ machine/src/standard_vm/x86_64/mod.rs | 44 ++ 7 files changed, 655 insertions(+), 2 deletions(-) create mode 100644 devices/src/acpi/cpu_controller.rs diff --git a/Cargo.lock b/Cargo.lock index 9b2b4cc00..660f68df0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,6 +332,7 @@ dependencies = [ "byteorder", "cairo-rs", "chardev_backend", + "cpu", "drm-fourcc", "hypervisor", "kvm-bindings", diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index 760c6f820..2ad8d51ae 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -1169,9 +1169,8 @@ pub struct AmlRelease { mutex: Vec, } -#[cfg(test)] impl AmlRelease { - fn new(mtx: T) -> AmlRelease { + pub fn new(mtx: T) -> AmlRelease { AmlRelease { mutex: mtx.aml_bytes(), } diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 72918c0ac..6bbb2336c 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -22,6 +22,7 @@ serde_json = "1.0" rand = "0.8.5" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } +cpu = { path = "../cpu" } hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs new file mode 100644 index 000000000..15190d921 --- /dev/null +++ b/devices/src/acpi/cpu_controller.rs @@ -0,0 +1,574 @@ +// Copyright (c) 2023 China Telecom Co.,Ltd. All rights reserved. +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; +use log::{error, info}; +use vmm_sys_util::eventfd::EventFd; + +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; +use crate::{Device, DeviceBase}; +use acpi::{ + AcpiError, AcpiLocalApic, AmlAcquire, AmlAddressSpaceType, AmlArg, AmlBuffer, AmlBuilder, + AmlCallWithArgs1, AmlCallWithArgs2, AmlCallWithArgs4, AmlDevice, AmlEisaId, AmlEqual, AmlField, + AmlFieldAccessType, AmlFieldLockRule, AmlFieldUnit, AmlFieldUpdateRule, AmlIf, AmlInteger, + AmlLocal, AmlMethod, AmlMutex, AmlName, AmlNameDecl, AmlNotify, AmlOne, AmlOpRegion, + AmlQWordDesc, AmlRelease, AmlResTemplate, AmlReturn, AmlScopeBuilder, AmlStore, AmlString, + AmlZero, +}; +use address_space::GuestAddress; +use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuLifecycleState, CPU}; +use migration::MigrationManager; + +const CPU_ENABLE_FLAG: u8 = 1; +const CPU_INSERTING_FLAG: u8 = 2; +const CPU_REMOVING_FLAG: u8 = 4; +const CPU_EJECT_FLAG: u8 = 8; + +const CPU_SELECTION_OFFSET: u64 = 0; +const CPU_STATUS_OFFSET: u64 = 1; +const CPU_EVENT_CODE_OFFSET: u64 = 2; + +const MADT_CPU_ENABLE_FLAG: usize = 0; + +#[derive(Clone)] +pub struct CpuConfig { + // Boot config. + boot_config: CPUBootConfig, + // Cpu topology. + cpu_topology: CPUTopology, +} + +impl CpuConfig { + pub fn new(boot_config: CPUBootConfig, cpu_topology: CPUTopology) -> Self { + CpuConfig { + boot_config, + cpu_topology, + } + } +} + +#[derive(Clone, Default)] +pub struct CpuController { + base: SysBusDevBase, + max_cpus: u8, + // Hotplug options: + // true - hotplug a vcpu. + // false - hotunplug a vcpu. + // None - nothing. + hotplug: Option>, + // Device id of vcpu need to hotplug. + device_id: String, + // Vcpu id need to hotplug or hotunplug. + vcpu_id: Arc, + // Map of hotplug vcpu id and device id. + id_map: HashMap, + // Map of all vcpu id and vcpu of vm. + vcpu_map: HashMap>, + // Acpi selected cpu id (for status check). + selected_cpu: u8, + // Cpu config information. + cpu_config: Option, + // Hotplug cpu request. + hotplug_cpu_req: Option>, +} + +impl CpuController { + pub fn realize( + mut self, + sysbus: &mut SysBus, + max_cpus: u8, + region_base: u64, + region_size: u64, + cpu_config: CpuConfig, + hotplug_cpu_req: Arc, + ) -> Result>> { + self.max_cpus = max_cpus; + self.cpu_config = Some(cpu_config); + self.hotplug_cpu_req = Some(hotplug_cpu_req); + self.set_sys_resource(sysbus, region_base, region_size) + .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; + let dev = Arc::new(Mutex::new(self)); + let ret_dev = dev.clone(); + sysbus.attach_device(&dev, region_base, region_size, "CPUController")?; + Ok(ret_dev) + } + + fn eject_cpu(&mut self, vcpu_id: u8) -> Result<()> { + let vcpu = self.vcpu_map.get(&vcpu_id).unwrap(); + vcpu.destroy()?; + self.id_map.insert(vcpu_id, "".to_string()); + Ok(()) + } + + fn get_cpu_state(&self, vcpu_id: u8) -> Result { + if let Some(vcpu) = self.vcpu_map.get(&vcpu_id) { + let (vcpu_state, _) = vcpu.state(); + Ok(*vcpu_state.lock().unwrap()) + } else { + Ok(CpuLifecycleState::Stopped) + } + } + + pub fn check_id_existed(&self, input_device_id: &str, input_vcpu_id: u8) -> Result<()> { + for (vcpu_id, id) in &self.id_map { + if id == input_device_id { + bail!("Device id {} already existed.", input_device_id) + } + // If vcpu id exist and device id is not empty, this vcpu is running. + if vcpu_id == &input_vcpu_id && !id.is_empty() { + bail!("Cpu-id {} is running, device id is {}.", input_vcpu_id, id) + } + } + Ok(()) + } + + pub fn find_cpu_by_device_id(&self, input_device_id: &str) -> Option { + for (vcpu_id, id) in &self.id_map { + if id == input_device_id { + return Some(*vcpu_id); + } + } + None + } + + pub fn find_reusable_vcpu(&mut self) -> Option> { + // If vcpu id exist and device id is empty, this vcpu is hotunplugged, can be reused. + let input_vcpu_id = self.vcpu_id.load(Ordering::SeqCst); + for (vcpu_id, device_id) in &self.id_map { + if vcpu_id == &input_vcpu_id && device_id.is_empty() { + let vcpu = self.vcpu_map.get(vcpu_id).unwrap().clone(); + return Some(vcpu); + } + } + None + } + + pub fn get_boot_config(&self) -> &CPUBootConfig { + &self.cpu_config.as_ref().unwrap().boot_config + } + + pub fn get_hotplug_cpu_info(&self) -> (String, u8) { + let device_id = self.device_id.clone(); + let vcpu_id = self.vcpu_id.load(Ordering::SeqCst); + (device_id, vcpu_id) + } + + pub fn set_hotplug_cpu_info(&mut self, device_id: String, vcpu_id: u8) -> Result<()> { + self.device_id = device_id; + self.vcpu_id.store(vcpu_id, Ordering::SeqCst); + Ok(()) + } + + pub fn get_topology_config(&self) -> &CPUTopology { + &self.cpu_config.as_ref().unwrap().cpu_topology + } + + pub fn setup_reuse_vcpu(&mut self, vcpu: Arc) -> Result<()> { + let device_id = self.device_id.clone(); + let vcpu_id = self.vcpu_id.load(Ordering::SeqCst); + let (state, _) = vcpu.state(); + let mut vcpu_state = state.lock().unwrap(); + *vcpu_state = CpuLifecycleState::Created; + drop(vcpu_state); + + MigrationManager::register_cpu_instance(cpu::ArchCPU::descriptor(), vcpu, vcpu_id); + if let Some(plug) = &self.hotplug { + plug.store(true, Ordering::SeqCst); + } else { + self.hotplug = Some(Arc::new(AtomicBool::new(true))); + } + self.id_map.insert(vcpu_id, device_id); + Ok(()) + } + + pub fn set_boot_vcpu(&mut self, boot_vcpus: Vec>) -> Result<()> { + for (k, v) in boot_vcpus.iter().enumerate() { + self.vcpu_map.insert(k.try_into().unwrap(), v.clone()); + } + Ok(()) + } + + pub fn setup_hotplug_vcpu( + &mut self, + device_id: String, + vcpu_id: u8, + vcpu: Arc, + ) -> Result<()> { + // Register vcpu instance. + MigrationManager::register_cpu_instance(cpu::ArchCPU::descriptor(), vcpu.clone(), vcpu_id); + // Set operate. + if let Some(plug) = &self.hotplug { + plug.store(true, Ordering::SeqCst); + } else { + self.hotplug = Some(Arc::new(AtomicBool::new(true))); + } + self.id_map.insert(vcpu_id, device_id); + self.vcpu_map.insert(vcpu_id, vcpu); + Ok(()) + } + + pub fn set_hotunplug_cpu(&mut self, vcpu_id: u8) -> Result<()> { + if let Some(plug) = &self.hotplug { + plug.store(false, Ordering::SeqCst); + } else { + self.hotplug = Some(Arc::new(AtomicBool::new(false))); + } + self.vcpu_id.store(vcpu_id, Ordering::SeqCst); + Ok(()) + } + + pub fn trigger_hotplug_cpu(&mut self) -> Result<()> { + self.hotplug_cpu_req + .as_ref() + .unwrap() + .write(1) + .with_context(|| "Failed to write cpu hotplug request.")?; + Ok(()) + } +} + +impl Device for CpuController { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } +} + +impl SysBusDevOps for CpuController { + fn sysbusdev_base(&self) -> &SysBusDevBase { + &self.base + } + + fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { + &mut self.base + } + + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { + data[0] = 0; + match offset { + CPU_SELECTION_OFFSET => { + let vcpu_id = self.vcpu_id.load(Ordering::SeqCst); + data[0] = vcpu_id; + } + CPU_STATUS_OFFSET => { + let state = self.get_cpu_state(self.selected_cpu).unwrap(); + if state == CpuLifecycleState::Running { + data[0] |= CPU_ENABLE_FLAG; + } + + if let Some(hotplug) = &self.hotplug { + if hotplug.load(Ordering::SeqCst) { + data[0] |= CPU_INSERTING_FLAG; + } else { + data[0] |= CPU_REMOVING_FLAG; + } + } + } + _ => { + error!("Unexpected offset for accessing CpuController: {}", offset); + return false; + } + } + true + } + + fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { + match offset { + CPU_SELECTION_OFFSET => self.selected_cpu = data[0], + CPU_STATUS_OFFSET => { + match data[0] { + // Reset hotplug flag after cpu inserting notified. + CPU_INSERTING_FLAG => self.hotplug = None, + + // Reset hotplug flag after cpu removing notified. + CPU_REMOVING_FLAG => self.hotplug = None, + + // Eject vcpu after guest os eject cpu device. + CPU_EJECT_FLAG => { + let vcpu_id = self.vcpu_id.load(Ordering::SeqCst); + if let Err(_e) = self.eject_cpu(vcpu_id) { + error!("Eject cpu-{} failed", vcpu_id) + } + } + _ => { + error!( + "Unexpected data[0] value for cpu status offset: {}", + data[0] + ); + return false; + } + } + } + CPU_EVENT_CODE_OFFSET => { + info!("Receive _OST event code {}", data[0]); + } + _ => { + error!( + "Unexpected offset for write CpuController device: {}", + offset + ); + return false; + } + } + true + } + + fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + Some(&mut self.base.res) + } +} + +impl AmlBuilder for CpuController { + fn aml_bytes(&self) -> Vec { + let res = self.base.res; + let mut cpu_hotplug_controller = AmlDevice::new("PRES"); + cpu_hotplug_controller.append_child(AmlNameDecl::new("_HID", AmlEisaId::new("PNP0A06"))); + cpu_hotplug_controller.append_child(AmlNameDecl::new( + "_UID", + AmlString("CPU Hotplug Controller".into()), + )); + cpu_hotplug_controller.append_child(AmlMutex::new("CPLK", 0)); + let mut crs = AmlResTemplate::new(); + crs.append_child(AmlQWordDesc::new_memory( + acpi::AmlAddressSpaceDecode::Positive, + acpi::AmlCacheable::Cacheable, + acpi::AmlReadAndWrite::ReadWrite, + 0, + res.region_base, + res.region_base + res.region_size - 1, + 0, + res.region_size, + )); + cpu_hotplug_controller.append_child(AmlNameDecl::new("_CRS", crs)); + let prst = AmlOpRegion::new( + "PRST", + AmlAddressSpaceType::SystemMemory, + res.region_base, + res.region_size, + ); + cpu_hotplug_controller.append_child(prst); + + let mut prst_field = AmlField::new( + "PRST", + AmlFieldAccessType::Byte, + AmlFieldLockRule::NoLock, + AmlFieldUpdateRule::WriteAsZeros, + ); + + prst_field.append_child(AmlFieldUnit::new("CPID".into(), 8)); + prst_field.append_child(AmlFieldUnit::new("CPEN".into(), 1)); + prst_field.append_child(AmlFieldUnit::new("CINS".into(), 1)); + prst_field.append_child(AmlFieldUnit::new("CRMV".into(), 1)); + prst_field.append_child(AmlFieldUnit::new("CEJ_".into(), 1)); + prst_field.append_child(AmlFieldUnit::new(None, 4)); + prst_field.append_child(AmlFieldUnit::new("CEVC".into(), 8)); + + cpu_hotplug_controller.append_child(prst_field); + cpu_hotplug_controller.append_child(AmlCpuStatusMethod {}); + cpu_hotplug_controller.append_child(AmlCpuStatusIndicationMethod {}); + cpu_hotplug_controller.append_child(AmlCpuEjectMethod {}); + cpu_hotplug_controller.append_child(AmlCpuNotifyMethod { + cpus_count: self.max_cpus, + }); + cpu_hotplug_controller.append_child(AmlCpuResizeMethod {}); + + for cpu_id in 0..self.max_cpus { + cpu_hotplug_controller.append_child(AmlCpu { + cpu_id, + dynamic: true, + }) + } + cpu_hotplug_controller.aml_bytes() + } +} + +pub struct AmlCpu { + pub cpu_id: u8, + pub dynamic: bool, +} + +impl AmlCpu { + fn generate_mat(&self) -> Vec { + let lapic = AcpiLocalApic { + type_id: 0, + length: 8, + processor_uid: self.cpu_id, + apic_id: self.cpu_id, + flags: 1 << MADT_CPU_ENABLE_FLAG, + }; + + let mut mat_data: Vec = Vec::new(); + mat_data.resize(std::mem::size_of_val(&lapic), 0); + // SAFETY: mat_data is large enough to hold lapic. + unsafe { *(mat_data.as_mut_ptr() as *mut AcpiLocalApic) = lapic }; + + mat_data + } + + fn sta_method(&self, return_value: Option) -> AmlMethod { + let mut sta_method = AmlMethod::new("_STA", 0, false); + if let Some(value) = return_value { + sta_method.append_child(AmlReturn::with_value(AmlInteger(value))); + } else { + let call_method_csta = AmlCallWithArgs1::new("CSTA", AmlInteger(self.cpu_id.into())); + sta_method.append_child(AmlReturn::with_value(call_method_csta)); + } + sta_method + } + + fn mat_name(&self) -> AmlNameDecl { + let mat_buffer = AmlBuffer(self.generate_mat()); + AmlNameDecl::new("_MAT", mat_buffer) + } + + fn ost_method(&self) -> AmlMethod { + let mut ost_method = AmlMethod::new("_OST", 3, false); + ost_method.append_child(AmlReturn::with_value(AmlCallWithArgs4::new( + "COST", + AmlInteger(self.cpu_id.into()), + AmlArg(0), + AmlArg(1), + AmlArg(2), + ))); + ost_method + } + + fn ej0_method(&self) -> AmlMethod { + let mut ej0_method = AmlMethod::new("_EJ0", 1, false); + ej0_method.append_child(AmlCallWithArgs1::new( + "CEJ0", + AmlInteger(self.cpu_id.into()), + )); + ej0_method + } +} + +impl AmlBuilder for AmlCpu { + fn aml_bytes(&self) -> Vec { + let mut cpu_device = AmlDevice::new(format!("C{:03}", self.cpu_id).as_str()); + cpu_device.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0007".into()))); + cpu_device.append_child(AmlNameDecl::new("_UID", AmlInteger(self.cpu_id.into()))); + cpu_device.append_child(AmlNameDecl::new("_PXM", AmlInteger(0u64))); + if self.dynamic { + { + cpu_device.append_child(self.sta_method(None)); + cpu_device.append_child(self.mat_name()); + cpu_device.append_child(self.ost_method()); + cpu_device.append_child(self.ej0_method()); + } + } else { + cpu_device.append_child(self.sta_method(Some(0xfu64))); + cpu_device.append_child(self.mat_name()); + } + cpu_device.aml_bytes() + } +} + +pub struct AmlCpuStatusIndicationMethod {} + +impl AmlBuilder for AmlCpuStatusIndicationMethod { + fn aml_bytes(&self) -> Vec { + let mut cpu_status_indication_method = AmlMethod::new("COST", 4, false); + cpu_status_indication_method + .append_child(AmlAcquire::new(AmlName("\\_SB.PRES.CPLK".into()), 0xffff)); + cpu_status_indication_method + .append_child(AmlStore::new(AmlArg(2), AmlName("\\_SB.PRES.CEVC".into()))); + cpu_status_indication_method + .append_child(AmlRelease::new(AmlName("\\_SB.PRES.CPLK".to_string()))); + cpu_status_indication_method.aml_bytes() + } +} + +pub struct AmlCpuNotifyMethod { + pub cpus_count: u8, +} + +impl AmlBuilder for AmlCpuNotifyMethod { + fn aml_bytes(&self) -> Vec { + let mut cpu_notify_method = AmlMethod::new("CTFY", 2, true); + for cpu_id in 0..self.cpus_count { + let mut if_scope = AmlIf::new(AmlEqual::new(AmlArg(0), AmlInteger(cpu_id.into()))); + if_scope.append_child(AmlNotify::new( + AmlName(format!("C{:03}", cpu_id)), + AmlArg(1), + )); + cpu_notify_method.append_child(if_scope); + } + cpu_notify_method.aml_bytes() + } +} + +pub struct AmlCpuStatusMethod {} + +impl AmlBuilder for AmlCpuStatusMethod { + fn aml_bytes(&self) -> Vec { + let mut csta_method = AmlMethod::new("CSTA", 1, true); + csta_method.append_child(AmlAcquire::new(AmlName("\\_SB.PRES.CPLK".into()), 0xffff)); + csta_method.append_child(AmlStore::new(AmlZero, AmlLocal(0))); + csta_method.append_child(AmlStore::new(AmlArg(0), AmlName("\\_SB.PRES.CPID".into()))); + + let mut if_scope = AmlIf::new(AmlEqual::new(AmlName("\\_SB.PRES.CPEN".into()), AmlOne)); + if_scope.append_child(AmlStore::new(AmlInteger(0xfu64), AmlLocal(0))); + csta_method.append_child(if_scope); + csta_method.append_child(AmlRelease::new(AmlName("\\_SB.PRES.CPLK".to_string()))); + csta_method.append_child(AmlReturn::with_value(AmlLocal(0))); + csta_method.aml_bytes() + } +} + +pub struct AmlCpuEjectMethod {} + +impl AmlBuilder for AmlCpuEjectMethod { + fn aml_bytes(&self) -> Vec { + let mut eject_method = AmlMethod::new("CEJ0", 1, true); + eject_method.append_child(AmlAcquire::new(AmlName("\\_SB.PRES.CPLK".into()), 0xffff)); + eject_method.append_child(AmlStore::new(AmlOne, AmlName("\\_SB.PRES.CEJ_".into()))); + eject_method.append_child(AmlRelease::new(AmlName("\\_SB.PRES.CPLK".to_string()))); + eject_method.aml_bytes() + } +} + +pub struct AmlCpuResizeMethod {} + +impl AmlBuilder for AmlCpuResizeMethod { + fn aml_bytes(&self) -> Vec { + let mut cscn_method = AmlMethod::new("CSCN", 1, true); + cscn_method.append_child(AmlAcquire::new(AmlName("\\_SB.PRES.CPLK".into()), 0xffff)); + cscn_method.append_child(AmlStore::new( + AmlName("\\_SB.PRES.CPID".into()), + AmlLocal(0), + )); + + let mut if_plug_scope = + AmlIf::new(AmlEqual::new(AmlName("\\_SB.PRES.CINS".into()), AmlOne)); + if_plug_scope.append_child(AmlCallWithArgs2::new("CTFY", AmlLocal(0), AmlOne)); + if_plug_scope.append_child(AmlStore::new(AmlOne, AmlName("\\_SB.PRES.CINS".into()))); + cscn_method.append_child(if_plug_scope); + + let mut if_unplug_scope = + AmlIf::new(AmlEqual::new(AmlName("\\_SB.PRES.CRMV".into()), AmlOne)); + if_unplug_scope.append_child(AmlCallWithArgs2::new("CTFY", AmlLocal(0), AmlInteger(3u64))); + if_unplug_scope.append_child(AmlStore::new(AmlOne, AmlName("\\_SB.PRES.CRMV".into()))); + cscn_method.append_child(if_unplug_scope); + + cscn_method.append_child(AmlRelease::new(AmlName("\\_SB.PRES.CPLK".to_string()))); + cscn_method.aml_bytes() + } +} diff --git a/devices/src/acpi/mod.rs b/devices/src/acpi/mod.rs index 563ce1fa5..a97e09754 100644 --- a/devices/src/acpi/mod.rs +++ b/devices/src/acpi/mod.rs @@ -10,5 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(target_arch = "x86_64")] +pub mod cpu_controller; pub mod ged; pub mod power; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 66991d6c0..081f062b5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -197,6 +197,38 @@ trait StdMachineOps: AcpiBuilder + MachineOps { bail!("Not implemented"); } + /// Register event notifier for hotplug vcpu event. + /// + /// # Arguments + /// + /// * `resize_req` - Eventfd of the cpu hotplug request. + /// * `clone_vm` - Reference of the StdMachine. + #[cfg(target_arch = "x86_64")] + fn register_hotplug_vcpu_event( + &self, + hotplug_req: Arc, + clone_vm: Arc>, + ) -> MachineResult<()> { + let hotplug_req_fd = hotplug_req.as_raw_fd(); + let hotplug_req_handler: Rc = Rc::new(move |_, _| { + read_fd(hotplug_req_fd); + if let Err(e) = StdMachine::handle_hotplug_vcpu_request(&clone_vm) { + error!("Fail to hotplug vcpu, {}", e); + } + None + }); + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + hotplug_req_fd, + None, + EventSet::IN, + vec![hotplug_req_handler], + ); + EventLoop::update_event(vec![notifier], None) + .with_context(|| "Failed to register event notifier.")?; + Ok(()) + } + /// Register event notifier for reset of standard machine. /// /// # Arguments diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 585fca6a6..dd848d539 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -36,6 +36,7 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; use cpu::{CPUBootConfig, CPUInterface, CPUTopology}; +use devices::acpi::cpu_controller::{CpuConfig, CpuController}; use devices::acpi::ged::{Ged, GedEvent}; use devices::legacy::{ error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, @@ -77,6 +78,7 @@ pub enum LayoutEntryType { PcieEcam, PcieMmio, GedMmio, + CpuController, Mmio, IoApic, LocalApic, @@ -90,6 +92,7 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ (0xB000_0000, 0x1000_0000), // PcieEcam (0xC000_0000, 0x3000_0000), // PcieMmio (0xF000_0000, 0x04), // GedMmio + (0xF000_0004, 0x03), // CpuController (0xF010_0000, 0x200), // Mmio (0xFEC0_0000, 0x10_0000), // IoApic (0xFEE0_0000, 0x10_0000), // LocalApic @@ -128,6 +131,8 @@ pub struct StdMachine { cpu_resize_req: Arc, /// List contains the boot order of boot devices. boot_order_list: Arc>>, + /// Cpu Controller. + cpu_controller: Option>>, } impl StdMachine { @@ -171,6 +176,7 @@ impl StdMachine { .with_context(|| MachineError::InitEventFdErr("cpu resize".to_string()))?, ), boot_order_list: Arc::new(Mutex::new(Vec::new())), + cpu_controller: None, }) } @@ -226,6 +232,42 @@ impl StdMachine { pub fn get_vcpu_reg_val(&self, _addr: u64, _vcpu: usize) -> Option { None } + + fn init_cpu_controller( + &mut self, + boot_config: CPUBootConfig, + cpu_topology: CPUTopology, + vm: Arc>, + ) -> Result<()> { + let cpu_controller: CpuController = Default::default(); + + let region_base: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].0; + let region_size: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].1; + let cpu_config = CpuConfig::new(boot_config, cpu_topology); + let hotplug_cpu_req = Arc::new( + EventFd::new(libc::EFD_NONBLOCK) + .with_context(|| MachineError::InitEventFdErr("hotplug cpu".to_string()))?, + ); + + let realize_controller = cpu_controller + .realize( + &mut self.base.sysbus, + self.base.cpu_topo.max_cpus, + region_base, + region_size, + cpu_config, + hotplug_cpu_req.clone(), + ) + .with_context(|| "Failed to realize Cpu Controller")?; + + let mut lock_controller = realize_controller.lock().unwrap(); + lock_controller.set_boot_vcpu(self.base.cpus.clone())?; + drop(lock_controller); + + self.register_hotplug_vcpu_event(hotplug_cpu_req, vm)?; + self.cpu_controller = Some(realize_controller); + Ok(()) + } } impl StdMachineOps for StdMachine { @@ -463,6 +505,8 @@ impl MachineOps for StdMachine { &boot_config, )?); + locked_vm.init_cpu_controller(boot_config.unwrap(), topology, vm.clone())?; + if migrate.0 == MigrateMode::Unknown { if let Some(fw_cfg) = fwcfg { locked_vm -- Gitee From 8531b981aea0813c476aacc0649d342ea590ca30 Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 28 Nov 2023 13:56:39 +0800 Subject: [PATCH 1483/2187] machine: x86_64 standard machine realize cpu hotplug Realize cpu hotplug feature with qmp device_add command on x86_64 standard machine. Signed-off-by: dehengli --- machine/src/lib.rs | 56 ++++++++++++++++++-------- machine/src/standard_vm/mod.rs | 58 +++++++++++++++++++++++++++ machine/src/standard_vm/x86_64/mod.rs | 56 +++++++++++++++++++++++++- machine_manager/src/qmp/qmp_schema.rs | 2 + 4 files changed, 154 insertions(+), 18 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9bb018f2d..e1700b509 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -409,6 +409,42 @@ pub trait MachineOps { self.get_vm_ram().mtree(0_u32); } + /// Create vcpu for virtual machine. + /// + /// # Arguments + /// + /// * `vcpu_id` - The id number of vcpu. + /// * `vm` - `MachineInterface` to obtain functions cpu can use. + /// * `max_cpus` - max cpu number of virtual machine. + fn create_vcpu( + vcpu_id: u8, + vm: Arc>, + #[cfg(target_arch = "x86_64")] max_cpus: u8, + ) -> Result> + where + Self: Sized, + { + let vcpu_fd = KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_vcpu(vcpu_id as u64) + .with_context(|| "Create vcpu failed")?; + #[cfg(target_arch = "aarch64")] + let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); + #[cfg(target_arch = "x86_64")] + let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(max_cpus)); + + let cpu = Arc::new(CPU::new( + Arc::new(vcpu_fd), + vcpu_id, + Arc::new(Mutex::new(arch_cpu)), + vm.clone(), + )); + Ok(cpu) + } + /// Init vcpu register with boot message. /// /// # Arguments @@ -431,24 +467,12 @@ pub trait MachineOps { let mut cpus = Vec::>::new(); for vcpu_id in 0..nr_cpus { - let vcpu_fd = KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(vcpu_id as u64) - .with_context(|| "Create vcpu failed")?; - #[cfg(target_arch = "aarch64")] - let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); - #[cfg(target_arch = "x86_64")] - let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(max_cpus)); - - let cpu = Arc::new(CPU::new( - Arc::new(vcpu_fd), + let cpu = Self::create_vcpu( vcpu_id, - Arc::new(Mutex::new(arch_cpu)), vm.clone(), - )); + #[cfg(target_arch = "x86_64")] + max_cpus, + )?; cpus.push(cpu.clone()); MigrationManager::register_cpu_instance(cpu::ArchCPU::descriptor(), cpu, vcpu_id); diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index 081f062b5..d83410035 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -54,6 +54,8 @@ use address_space::{ AddressRange, FileBackend, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, }; use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; +#[cfg(target_arch = "x86_64")] +use devices::acpi::cpu_controller::CpuController; use devices::legacy::FwCfgOps; use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; use devices::pci::PciBus; @@ -197,6 +199,18 @@ trait StdMachineOps: AcpiBuilder + MachineOps { bail!("Not implemented"); } + /// Get cpu controller. + #[cfg(target_arch = "x86_64")] + fn get_cpu_controller(&self) -> &Arc>; + + /// Add new vcpu device. + /// + /// # Arguments + /// + /// * `clone_vm` - Reference of the StdMachine. + #[cfg(target_arch = "x86_64")] + fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()>; + /// Register event notifier for hotplug vcpu event. /// /// # Arguments @@ -1169,6 +1183,39 @@ impl StdMachine { shutdown_req, ); } + + #[cfg(target_arch = "x86_64")] + fn plug_cpu_device(&mut self, args: &qmp_schema::DeviceAddArgument) -> Result<()> { + if self.get_numa_nodes().is_some() { + bail!("Not support to hotplug/hotunplug cpu in numa architecture now.") + } + let device_id = args.id.clone(); + if device_id.is_empty() { + bail!("Device id can't be empty.") + } + + if let Some(cpu_id) = args.cpu_id { + let nr_cpus = self.get_cpu_topo().nrcpus; + let max_cpus = self.get_cpu_topo().max_cpus; + + if nr_cpus == max_cpus { + bail!("There is no hotpluggable cpu-id for this VM.") + } + if cpu_id < nr_cpus { + bail!("Cpu-id {} already exist.", cpu_id) + } + if cpu_id >= max_cpus { + bail!("Max cpu-id is {}", max_cpus - 1) + } + + let mut locked_controller = self.get_cpu_controller().lock().unwrap(); + locked_controller.check_id_existed(&device_id, cpu_id)?; + locked_controller.set_hotplug_cpu_info(device_id, cpu_id)?; + locked_controller.trigger_hotplug_cpu() + } else { + bail!("Argument cpu-id is required.") + } + } } impl DeviceInterface for StdMachine { @@ -1359,6 +1406,17 @@ impl DeviceInterface for StdMachine { } return Response::create_empty_response(); } + #[cfg(target_arch = "x86_64")] + "generic-x86-cpu" => { + if let Err(e) = self.plug_cpu_device(args.as_ref()) { + error!("{:?}", e); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ); + } + return Response::create_empty_response(); + } _ => { let err_str = format!("Failed to add device: Driver {} is not support", driver); return Response::create_error_response( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index dd848d539..4bfd30d56 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -18,7 +18,7 @@ mod syscall; use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Barrier, Mutex}; use anyhow::{bail, Context, Result}; use log::{error, info}; @@ -35,7 +35,7 @@ use acpi::{ }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; -use cpu::{CPUBootConfig, CPUInterface, CPUTopology}; +use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CPU}; use devices::acpi::cpu_controller::{CpuConfig, CpuController}; use devices::acpi::ged::{Ged, GedEvent}; use devices::legacy::{ @@ -233,6 +233,12 @@ impl StdMachine { None } + pub fn handle_hotplug_vcpu_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); + locked_vm.add_vcpu_device(vm.clone())?; + Ok(()) + } + fn init_cpu_controller( &mut self, boot_config: CPUBootConfig, @@ -329,6 +335,52 @@ impl StdMachineOps for StdMachine { Ok(Some(fwcfg_dev)) } + + fn get_cpu_controller(&self) -> &Arc> { + self.cpu_controller.as_ref().unwrap() + } + + fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()> { + let mut locked_controller = self.cpu_controller.as_ref().unwrap().lock().unwrap(); + let device_id; + let vcpu_id; + (device_id, vcpu_id) = locked_controller.get_hotplug_cpu_info(); + + // Check if there is a reusable CPU, and if not, create a new one. + let vcpu = if let Some(reuse_vcpu) = locked_controller.find_reusable_vcpu() { + locked_controller.setup_reuse_vcpu(reuse_vcpu.clone())?; + reuse_vcpu + } else { + let boot_cfg = locked_controller.get_boot_config(); + let topology = locked_controller.get_topology_config(); + + let vcpu = ::create_vcpu( + vcpu_id, + clone_vm, + self.base.cpu_topo.max_cpus, + )?; + vcpu.realize(boot_cfg, topology).with_context(|| { + format!( + "Failed to realize arch cpu register/features for CPU {}/KVM", + vcpu_id + ) + })?; + + locked_controller.setup_hotplug_vcpu(device_id, vcpu_id, vcpu.clone())?; + self.base.cpus.push(vcpu.clone()); + vcpu + }; + // Start vcpu. + let cpu_thread_barrier = Arc::new(Barrier::new(1)); + if let Err(e) = CPU::start(vcpu, cpu_thread_barrier, false) { + bail!("Failed to run vcpu-{}, {:?}", vcpu_id, e) + }; + // Trigger GED cpu resize event. + self.cpu_resize_req + .write(1) + .with_context(|| "Failed to write cpu resize request.")?; + Ok(()) + } } impl MachineOps for StdMachine { diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 82ae6425f..a094ad45e 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -317,6 +317,8 @@ pub struct device_add { pub productid: Option, pub isobufs: Option, pub isobsize: Option, + #[serde(rename = "cpu-id")] + pub cpu_id: Option, } pub type DeviceAddArgument = device_add; -- Gitee From 8cfefc89081f83aa684bbf34cdbebf6c9cce4390 Mon Sep 17 00:00:00 2001 From: dehengli Date: Sat, 25 Nov 2023 23:25:53 +0800 Subject: [PATCH 1484/2187] machine: x86_64 standard machine realize cpu hotunplug Realize cpu hotunplug feature with qmp device_del command on x86_64 standard machine. Signed-off-by: dehengli --- machine/src/standard_vm/mod.rs | 30 ++++++++++++++++++++++++++- machine/src/standard_vm/x86_64/mod.rs | 19 +++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index d83410035..0f5b94340 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -243,6 +243,22 @@ trait StdMachineOps: AcpiBuilder + MachineOps { Ok(()) } + /// Remove vcpu device. + /// + /// # Arguments + /// + /// * `vcpu_id` - The id number of vcpu. + #[cfg(target_arch = "x86_64")] + fn remove_vcpu_device(&mut self, vcpu_id: u8) -> Result<()>; + + /// Find cpu id by device id. + /// + /// # Arguments + /// + /// * `device_id` - The name of vcpu device. + #[cfg(target_arch = "x86_64")] + fn find_cpu_id_by_device_id(&mut self, device_id: &str) -> Option; + /// Register event notifier for reset of standard machine. /// /// # Arguments @@ -1486,7 +1502,19 @@ impl DeviceInterface for StdMachine { } drop(locked_pci_host); - // The device is not a pci device, assume it is a usb device. + // Assume it is a cpu device, try to find this device in cpu device. + #[cfg(target_arch = "x86_64")] + if let Some(cpu_id) = self.find_cpu_id_by_device_id(&device_id) { + return match self.remove_vcpu_device(cpu_id) { + Ok(()) => Response::create_empty_response(), + Err(e) => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(e.to_string()), + None, + ), + }; + } + + // The device is not a pci device and not a cpu device, assume it is a usb device. match self.handle_unplug_usb_request(device_id) { Ok(()) => Response::create_empty_response(), Err(e) => Response::create_error_response( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index 4bfd30d56..af8c270de 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -381,6 +381,25 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to write cpu resize request.")?; Ok(()) } + + fn remove_vcpu_device(&mut self, vcpu_id: u8) -> Result<()> { + if self.base.numa_nodes.is_some() { + bail!("Not support to hotunplug cpu in numa architecture now.") + } + let mut locked_controller = self.cpu_controller.as_ref().unwrap().lock().unwrap(); + + // Trigger GED cpu resize event. + locked_controller.set_hotunplug_cpu(vcpu_id)?; + self.cpu_resize_req + .write(1) + .with_context(|| "Failed to write cpu resize request.")?; + Ok(()) + } + + fn find_cpu_id_by_device_id(&mut self, device_id: &str) -> Option { + let locked_controller = self.cpu_controller.as_ref().unwrap().lock().unwrap(); + locked_controller.find_cpu_by_device_id(device_id) + } } impl MachineOps for StdMachine { -- Gitee From cf763fec2761411271b5349aee1350cd3dbe3c50 Mon Sep 17 00:00:00 2001 From: dehengli Date: Tue, 28 Nov 2023 14:17:30 +0800 Subject: [PATCH 1485/2187] tests: add cpu hotplug test Enable x86_64 mod_test and add cpu hotplug test. Signed-off-by: dehengli --- tests/mod_test/tests/mod.rs | 2 + .../mod_test/tests/x86_64/cpu_hotplug_test.rs | 248 ++++++++++++++++++ tests/mod_test/tests/x86_64/mod.rs | 14 + util/src/test_helper.rs | 23 +- 4 files changed, 273 insertions(+), 14 deletions(-) create mode 100644 tests/mod_test/tests/x86_64/cpu_hotplug_test.rs create mode 100644 tests/mod_test/tests/x86_64/mod.rs diff --git a/tests/mod_test/tests/mod.rs b/tests/mod_test/tests/mod.rs index 826c4d47a..6c134f721 100644 --- a/tests/mod_test/tests/mod.rs +++ b/tests/mod_test/tests/mod.rs @@ -29,3 +29,5 @@ mod virtio_gpu_test; mod virtio_test; mod virtiofs_test; mod vnc_test; +#[cfg(target_arch = "x86_64")] +mod x86_64; diff --git a/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs b/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs new file mode 100644 index 000000000..f21c2dd1a --- /dev/null +++ b/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs @@ -0,0 +1,248 @@ +// Copyright (c) 2023 China Telecom Co.,Ltd. All rights reserved. +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::borrow::BorrowMut; +use std::thread::sleep; +use std::time::Duration; + +use serde_json::{json, Value}; + +use machine::standard_vm::x86_64::{LayoutEntryType, MEM_LAYOUT}; +use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; + +const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::GedMmio as usize].0; + +fn ged_read_evt(ts: &TestState) -> u32 { + ts.readl(GED_ADDR_BASE) +} + +fn set_up(cpu: u8, max_cpus: Option) -> TestState { + // Vm extra_args. + let mut extra_args: Vec<&str> = Vec::new(); + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); + extra_args.append(&mut args); + + let cpu_args = if let Some(max_cpus) = max_cpus { + format!("-smp {},maxcpus={}", cpu, max_cpus) + } else { + format!("-smp {}", cpu) + }; + args = cpu_args[..].split(' ').collect(); + extra_args.append(&mut args); + + let mem_args = format!("-m 512"); + args = mem_args[..].split(' ').collect(); + extra_args.append(&mut args); + + extra_args.push("-append"); + extra_args.push("root=/dev/vda panic=1"); + + let uefi_drive = + format!("-drive file=/usr/share/edk2/ovmf/OVMF_CODE.fd,if=pflash,unit=0,readonly=true"); + args = uefi_drive[..].split(' ').collect(); + extra_args.append(&mut args); + + let root_device = format!("-device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.1"); + args = root_device[..].split(' ').collect(); + extra_args.append(&mut args); + + args = "-disable-seccomp -daemonize".split(' ').collect(); + extra_args.append(&mut args); + + test_init(extra_args) +} + +fn hotplug_cpu(test_state: &mut TestState, id: &str, cpu_id: u8) -> Value { + test_state.borrow_mut().qmp(&format!( + "{{\"execute\": \"device_add\",\"arguments\": {{ \"id\": \"{id}\", \"driver\": \"generic-x86-cpu\", \"cpu-id\": {cpu_id}}}}}" + )) +} + +fn hotunplug_cpu(test_state: &mut TestState, id: &str) -> Value { + test_state.borrow_mut().qmp(&format!( + "{{\"execute\": \"device_del\", \"arguments\": {{\"id\": \"{id}\"}}}}" + )) +} + +fn assert_response(result: Value, index: &str, expect: Option) { + if index == "return" { + assert_eq!(*result.get("return").unwrap(), json!({})) + } else { + assert_eq!( + result["error"]["desc"].as_str().unwrap().to_string(), + expect.unwrap(), + ) + } +} + +/// Normal cpu hotplug. +/// TestStep: +/// 1. Send id vcpu-1 and cpu-id 1 hotplug qmp command. +/// 2. Read ged event, expect 16. +/// 3. Destroy VM. +/// Expect: +/// 1/2/3: success. +#[test] +fn normal_hotplug_cpu() { + let mut ts = set_up(1, Some(2)); + + let ret = hotplug_cpu(&mut ts, "vcpu-1", 1); + assert_response(ret, "return", None); + sleep(Duration::from_micros(200)); + + let event = ged_read_evt(&ts); + assert_eq!(event, 16); + + ts.borrow_mut().stop(); +} + +/// Normal cpu hotunplug. +/// TestStep: +/// 1. Send id vcpu-1 and cpu-id 1 hotplug qmp command. +/// 2. Send id vcpu-1 hotunplug qmp command. +/// 3. Read ged event, expect 16. +/// 4. Destroy VM. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn normal_hotunplug_cpu() { + let mut ts = set_up(1, Some(2)); + + // Hotplug vcpu-1. + let ret = hotplug_cpu(&mut ts, "vcpu-1", 1); + assert_response(ret, "return", None); + ts.qmp_read(); + + // Hotunplug vcpu-1. + let ret = hotunplug_cpu(&mut ts, "vcpu-1"); + assert_response(ret, "return", None); + + let event = ged_read_evt(&ts); + assert_eq!(event, 16); + + ts.borrow_mut().stop(); +} + +/// Hotplug cpu with an existed id. +/// TestStep: +/// 1. Send id vcpu-1 and cpu-id 1 hotplug qmp command. +/// 2. Send id vcpu-1 and cpu-id 2 hotplug qmp command. +/// 3. Destroy VM. +/// Expect: +/// 1/3: Success. +/// 2: Failed. +#[test] +fn existed_id_hotplug_cpu() { + let mut ts = set_up(1, Some(3)); + + // Hotplug vcpu-1. + let ret = hotplug_cpu(&mut ts, "vcpu-1", 1); + assert_response(ret, "return", None); + ts.qmp_read(); + + // Hotplug vcpu-1. + let ret = hotplug_cpu(&mut ts, "vcpu-1", 2); + assert_response( + ret, + "error", + Some("Device id vcpu-1 already existed.".to_string()), + ); + + ts.borrow_mut().stop(); +} + +/// Hotplug cpu with an existed cpu id. +/// TestStep: +/// 1. Send id vcpu-1 and cpu-id 1 hotplug qmp command. +/// 2. Send id vcpu-2 and cpu-id 1 hotplug qmp command. +/// 3. Destroy VM. +/// Expect: +/// 1/3: Success. +/// 2: Failed. +#[test] +fn existed_cpuid_hotplug_cpu() { + let mut ts = set_up(1, Some(3)); + + let ret = hotplug_cpu(&mut ts, "vcpu-1", 1); + assert_response(ret, "return", None); + ts.qmp_read(); + + let ret = hotplug_cpu(&mut ts, "vcpu-2", 1); + assert_response( + ret, + "error", + Some("Cpu-id 1 is running, device id is vcpu-1.".to_string()), + ); + + ts.borrow_mut().stop(); +} + +/// Hotplug cpu with empty id. +/// TestStep: +/// 1. Send empty id and cpu-id 1 hotplug qmp command. +/// 2. Destroy VM. +/// Expect: +/// 2: Success. +/// 1: Failed. +#[test] +fn empty_id_hotplug_cpu() { + let mut ts = set_up(1, Some(2)); + + let ret = hotplug_cpu(&mut ts, "", 1); + assert_response(ret, "error", Some("Device id is empty".to_string())); + + ts.borrow_mut().stop(); +} + +/// Hotplug cpu with an overrange cpu id. +/// TestStep: +/// 1. Send id vcpu-1 and cpu-id 1 hotplug qmp command. +/// 2. Send id vcpu-2 and cpu-id 2 hotplug qmp command. +/// 3. Destroy VM. +/// Expect: +/// 1/3: Success. +/// 2: Failed. +#[test] +fn overrange_hotplug_cpu() { + let mut ts = set_up(1, Some(2)); + + let ret = hotplug_cpu(&mut ts, "vcpu-1", 1); + assert_response(ret, "return", None); + ts.qmp_read(); + + let ret = hotplug_cpu(&mut ts, "vcpu-2", 2); + assert_response(ret, "error", Some("Max cpu-id is 1".to_string())); + + ts.borrow_mut().stop(); +} + +/// Hotplug cpu when max_cpus is not explicitly configured. +/// TestSetp: +/// 1. Send id vcpu-1 and cpu-id 1 hotplug qmp command. +/// 2. Destroy VM. +/// Expect: +/// 2: Success. +/// 1: Failed. +#[test] +fn without_config_max_cpus_hotplug_cpu() { + let mut ts = set_up(1, None); + + let ret = hotplug_cpu(&mut ts, "vcpu-1", 1); + assert_response( + ret, + "error", + Some("There is no hotpluggable cpu-id for this VM.".to_string()), + ); + + ts.borrow_mut().stop(); +} diff --git a/tests/mod_test/tests/x86_64/mod.rs b/tests/mod_test/tests/x86_64/mod.rs new file mode 100644 index 000000000..42cf6f78c --- /dev/null +++ b/tests/mod_test/tests/x86_64/mod.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 China Telecom Co.,Ltd. All rights reserved. +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod cpu_hotplug_test; diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs index e55669d7c..adcbd28a0 100644 --- a/util/src/test_helper.rs +++ b/util/src/test_helper.rs @@ -46,20 +46,15 @@ static TEST_MSIX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec:: static TEST_INTX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); pub fn set_test_enabled() { - #[cfg(target_arch = "x86_64")] - panic!("module test framework does not support x86_64."); - #[cfg(target_arch = "aarch64")] - { - if let Err(_e) = TEST_ENABLED.set(true) { - panic!("Failed to enable test server."); - } - if let Err(_e) = TEST_BASE_TIME.set(Instant::now()) { - panic!("Failed to initialize clock"); - } - unsafe { - if TEST_CLOCK.is_none() { - TEST_CLOCK = Some(Arc::new(RwLock::new(0))); - } + if let Err(_e) = TEST_ENABLED.set(true) { + panic!("Failed to enable test server."); + } + if let Err(_e) = TEST_BASE_TIME.set(Instant::now()) { + panic!("Failed to initialize clock"); + } + unsafe { + if TEST_CLOCK.is_none() { + TEST_CLOCK = Some(Arc::new(RwLock::new(0))); } } } -- Gitee From bf6b0340eb8943df3eac9de4401deae7e3945918 Mon Sep 17 00:00:00 2001 From: dehengli Date: Sat, 18 Nov 2023 20:17:57 +0800 Subject: [PATCH 1486/2187] docs: add documents for cpu hotplug Add documents for x86_64 standard machine cpu hotplug and hotunplug feature. Signed-off-by: dehengli --- docs/cpu_hotplug.ch.md | 60 ++++++++++++++++++++++++++++++++++++++++++ docs/cpu_hotplug.md | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 docs/cpu_hotplug.ch.md create mode 100644 docs/cpu_hotplug.md diff --git a/docs/cpu_hotplug.ch.md b/docs/cpu_hotplug.ch.md new file mode 100644 index 000000000..4de478a13 --- /dev/null +++ b/docs/cpu_hotplug.ch.md @@ -0,0 +1,60 @@ +# CPU热插拔 + +StratoVirt支持对一个运行中的虚机进行CPU的热插入和热拔出。该功能可以动态调整虚机的CPU资源。目前,该功能只支持x86_64的标准虚机,并且不包含NUMA架构。 + +## 创建虚机 + +首先,创建一台虚机。 + +```shell +$ ./stratovirt \ + -machine q35 \ + -smp [cpus=],maxcpus= \ + -m 512 \ + -kernel path/to/kernel \ + -append "console=ttyS0 root=/dev/vda reboot=k panic=1" \ + -drive file=path/to/OVMF_CODE.fd,if=pflash,unit=0,readonly=true \ + -device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.1 \ + -drive file=path/to/rootfs,id=rootfs,readonly=true \ + -device virtio-blk-pci,drive=rootfs,bus=pcie.1,addr=0x0.0x0,id=blk-0 \ + -qmp unix:path/to/api/socket,server,nowait \ + -serial stdio +``` + +- `cpus`:设置虚机的启动CPU数量为'n'(默认: 1)。 `cpus`参数所设置的CPU会在虚机启动后全部上线运行,并且这些CPU不支持热拔出。 +- `maxcpus`:设置虚机的总CPU数量, 包含了在线和离线的CPU, 离线CPU的数量也就是支持热插拔的CPU, `maxcpus`的数量不能小于`cpus`。 + +## 热插入CPU + +虚机启动后,通过QMP热插入CPU + +```shell +$ ncat -U /path/to/api/socket +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +-> {"execute": "device_add","arguments": { "id": "device-id", "driver": "generic-x86-cpu", "cpu-id": cpuid }} +<- {"return":{}} +<- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} +``` + +- `id`: CPU设备的ID, 该ID应该为全局唯一的字符串。 +- `cpu-id`: CPU的编号,编号的取值范围是[`cpus`, `maxcpus`)内的整数。 + +## 热拔出CPU + +通过QMP热拔出CPU: + +```shell +$ ncat -U /path/to/api/socket +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +-> {"execute": "device_del", "arguments": { "id": "device-id"}} +<- {"return":{}} +<- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} +``` + +## 限制 + +CPU热插拔支持的虚机类型: +- `q35` (on x86_64 platform) + +CPU热插拔不支持的设备和特性: +- `numa` diff --git a/docs/cpu_hotplug.md b/docs/cpu_hotplug.md new file mode 100644 index 000000000..4cfdc526c --- /dev/null +++ b/docs/cpu_hotplug.md @@ -0,0 +1,60 @@ +# CPU hotplug and hotunplug + +StratoVirt support to hot(un)plug CPU to a running VM. This feature supports dynamic adjustment of CPU resources of VM. Currently, only standard VM with x86_64 architecture is supported, and NUMA architecture is not supported. + +## Create VM + +First, we create a StratoVirt VM. + +```shell +$ ./stratovirt \ + -machine q35 \ + -smp [cpus=],maxcpus= \ + -m 512 \ + -kernel path/to/kernel \ + -append "console=ttyS0 root=/dev/vda reboot=k panic=1" \ + -drive file=path/to/OVMF_CODE.fd,if=pflash,unit=0,readonly=true \ + -device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.1 \ + -drive file=path/to/rootfs,id=rootfs,readonly=true \ + -device virtio-blk-pci,drive=rootfs,bus=pcie.1,addr=0x0.0x0,id=blk-0 \ + -qmp unix:path/to/api/socket,server,nowait \ + -serial stdio +``` + +- `cpus`: Set the number of CPUs to 'n' (default: 1). The number of `cpus` will all online after VM booted, and can't be hotunplugged. +- `maxcpus`: Set the number of total CPUs, including online and offline CPUs. The number of offline CPUs is also the number of CPUs that support hotplug. The number of `maxcpus` should not less than `cpus`. + +## Hotplug CPU + +After the VM boot up, hotplug CPU with QMP: + +```shell +$ ncat -U path/to/api/socket +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +-> {"execute": "device_add","arguments": { "id": "device-id", "driver": "generic-x86-cpu", "cpu-id": cpuid }} +<- {"return":{}} +<- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} +``` + +- `id`: The ID of the CPU device, which should be a globally unique string. +- `cpu-id`: The number of the CPU, which can be an integer in the range of [`cpus`, `maxcpus`) + +## Hotunplug CPU + +hotunplug CPU with QMP: + +```shell +$ ncat -U path/to/api/socket +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +-> {"execute": "device_del", "arguments": { "id": "device-id"}} +<- {"return":{}} +<- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} +``` + +## Limitations + +CPU hot(un)plug support machine type: +- `q35` (on x86_64 platform) + +Some devices and feature don't support to be CPU hotplug yet: +- `numa` -- Gitee From a2a7498e8ea1a4a30a0f37abfb8c340f2422f5f2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 29 Nov 2023 03:49:52 +0800 Subject: [PATCH 1487/2187] scsi: don't apply for vector based on the xfer field Guest may maliciously inject incorrect scsi instructions, eg: xfer=0 for commands which need some transfer data. So, don't apply for memory space based on the `xfer`. Now, only INQUIRY command poses such a risk. Fix it. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index ee78f472a..d90663eeb 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -1088,7 +1088,7 @@ fn scsi_command_emulate_inquiry( } let buflen = cmp::min(cmd.xfer, SCSI_MAX_INQUIRY_LEN); - let mut outbuf: Vec = vec![0; buflen as usize]; + let mut outbuf: Vec = vec![0; SCSI_MAX_INQUIRY_LEN as usize]; let dev_lock = dev.lock().unwrap(); -- Gitee From 86b6f672dadc888fc9cda23e0b38179490098e06 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 27 Nov 2023 20:49:50 +0800 Subject: [PATCH 1488/2187] virtio-gpu: support monochrome cursor Parses the monochrome format into RGBA format, need support from guest driver. NOTICE: Inverted format is hard to implement in virtio-gpu, set it to black as QEMU do. Signed-off-by: Binfeng Wu --- virtio/src/device/gpu.rs | 186 +++++++++++++++++++++++++++++++-------- virtio/src/lib.rs | 2 + 2 files changed, 152 insertions(+), 36 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 7129d2146..1fe7999ca 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -30,7 +30,7 @@ use crate::{ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, - VIRTIO_GPU_F_EDID, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + VIRTIO_GPU_F_EDID, VIRTIO_GPU_F_MONOCHROME, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, @@ -82,6 +82,7 @@ struct GpuResource { scanouts_bitmask: u32, host_mem: u64, pixman_image: *mut pixman_image_t, + monochrome_cursor: Vec, } impl Default for GpuResource { @@ -95,6 +96,7 @@ impl Default for GpuResource { scanouts_bitmask: 0, host_mem: 0, pixman_image: ptr::null_mut(), + monochrome_cursor: Vec::new(), } } } @@ -522,7 +524,9 @@ const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67; const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68; const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121; const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; +const VIRTIO_GPU_FORMAT_MONOCHROME: u32 = 500; pub const VIRTIO_GPU_FORMAT_INVALID_UNORM: u32 = 135; +const VIRTIO_GPU_CURSOR_SIZE: usize = 64; pub fn get_pixman_format(format: u32) -> Result { match format { @@ -540,10 +544,92 @@ pub fn get_pixman_format(format: u32) -> Result { } } -pub fn get_image_hostmem(format: pixman_format_code_t, width: u32, height: u32) -> u64 { - let bpp = pixman_format_bpp(format as u32); - let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); - height as u64 * stride +// update curosr from monochrome source +// https://learn.microsoft.com/en-us/windows-hardware/drivers/display/drawing-monochrome-pointers +pub fn set_monochrome_cursor(cursor: &mut [u8], source: &[u8], width: usize, height: usize) { + let pixels_num = width * height; + let mask_value_size = pixels_num / 8; + let and_mask_value = &source[0..mask_value_size]; + let xor_mask_value = &source[mask_value_size..mask_value_size * 2]; + // Bytes per line + let bpl = VIRTIO_GPU_CURSOR_SIZE / 8; + // Bytes per pixel for cursor img, which expected export in RGBA format + let bpp = 4; + + for row in 0..VIRTIO_GPU_CURSOR_SIZE { + for col in 0..bpl { + for i in 0..8 { + let cursor_index = (row * VIRTIO_GPU_CURSOR_SIZE + col * 8 + i) * bpp; + + if row >= height || col * bpl >= width { + cursor[cursor_index] = 0x00; + cursor[cursor_index + 1] = 0x00; + cursor[cursor_index + 2] = 0x00; + cursor[cursor_index + 3] = 0x00; + continue; + } + + let mask_index: u8 = 0x80 >> i; + let and_v = (and_mask_value[row * (width / 8) + col] & mask_index) != 0; + let xor_v = (xor_mask_value[row * (width / 8) + col] & mask_index) != 0; + + if !and_v && !xor_v { + cursor[cursor_index] = 0x00; + cursor[cursor_index + 1] = 0x00; + cursor[cursor_index + 2] = 0x00; + cursor[cursor_index + 3] = 0xff; + } else if !and_v && xor_v { + cursor[cursor_index] = 0xff; + cursor[cursor_index + 1] = 0xff; + cursor[cursor_index + 2] = 0xff; + cursor[cursor_index + 3] = 0xff; + } else if and_v && !xor_v { + cursor[cursor_index] = 0x00; + cursor[cursor_index + 1] = 0x00; + cursor[cursor_index + 2] = 0x00; + cursor[cursor_index + 3] = 0x00; + } else { + // for inverted, in graphic is hard to get background color, just make it black. + cursor[cursor_index] = 0x00; + cursor[cursor_index + 1] = 0x00; + cursor[cursor_index + 2] = 0x00; + cursor[cursor_index + 3] = 0xff; + } + } + } + } +} + +pub fn cal_image_hostmem(format: u32, width: u32, height: u32) -> (Option, u32) { + // Expected monochrome cursor is 8 pixel aligned. + if format == VIRTIO_GPU_FORMAT_MONOCHROME { + if width as usize > VIRTIO_GPU_CURSOR_SIZE + || height as usize > VIRTIO_GPU_CURSOR_SIZE + || width % 8 != 0 + || height % 8 != 0 + { + error!( + "GuestError: monochrome cursor use invalid size: {} {}.", + width, height + ); + (None, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER) + } else { + let mem = (width * height / 8 * 2) as usize; + (Some(mem), 0) + } + } else { + let pixman_format = match get_pixman_format(format) { + Ok(f) => f, + Err(e) => { + error!("GuestError: {:?}", e); + return (None, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); + } + }; + let bpp = pixman_format_bpp(pixman_format as u32); + let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); + let mem = (height as u64 * stride) as usize; + (Some(mem), 0) + } } fn is_rect_in_resource(rect: &VirtioGpuRect, res: &GpuResource) -> bool { @@ -678,7 +764,9 @@ impl GpuIoHandler { } Some(res_idx) => { let res = &self.resources_list[res_idx]; - if res.iov.is_empty() || res.pixman_image.is_null() { + if res.iov.is_empty() + || (res.pixman_image.is_null() && res.monochrome_cursor.is_empty()) + { error!( "GuestError: The resource_id {} in {} request has no backing storage.", res_id, caller, @@ -703,16 +791,26 @@ impl GpuIoHandler { let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; let mse = scanout.mouse.as_mut().unwrap(); let mse_data_size = mse.data.len(); - let res_width = get_image_width(res.pixman_image); - let res_height = get_image_height(res.pixman_image); - if res_width as u32 != mse.width || res_height as u32 != mse.height { - return; - } - let res_data_ptr = get_image_data(res.pixman_image) as *mut u8; - // SAFETY: the length of the source and dest pointers can be ensured to be same, - // and equal to mse_data_size. - unsafe { - ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); + + if res.format == VIRTIO_GPU_FORMAT_MONOCHROME { + set_monochrome_cursor( + &mut mse.data, + &res.monochrome_cursor, + res.width as usize, + res.height as usize, + ); + } else { + let res_width = get_image_width(res.pixman_image); + let res_height = get_image_height(res.pixman_image); + if res_width as u32 != mse.width || res_height as u32 != mse.height { + return; + } + let res_data_ptr = get_image_data(res.pixman_image) as *mut u8; + // SAFETY: the length of the source and dest pointers can be ensured to be same, + // and equal to mse_data_size. + unsafe { + ptr::copy(res_data_ptr, mse.data.as_mut_ptr(), mse_data_size); + } } // Windows front-end driver does not deliver data in format sequence. @@ -734,7 +832,12 @@ impl GpuIoHandler { let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; match &mut scanout.mouse { None => { - let mouse = DisplayMouse::new(64, 64, info_cursor.hot_x, info_cursor.hot_y); + let mouse = DisplayMouse::new( + VIRTIO_GPU_CURSOR_SIZE as u32, + VIRTIO_GPU_CURSOR_SIZE as u32, + info_cursor.hot_x, + info_cursor.hot_y, + ); scanout.mouse = Some(mouse); } Some(mouse) => { @@ -872,16 +975,12 @@ impl GpuIoHandler { resource_id: info_create_2d.resource_id, ..Default::default() }; - let pixman_format = match get_pixman_format(res.format) { - Ok(f) => f, - Err(e) => { - error!("GuestError: {:?}", e); - return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); - } - }; - res.host_mem = - get_image_hostmem(pixman_format, info_create_2d.width, info_create_2d.height); + let (mem, error) = cal_image_hostmem(res.format, res.width, res.height); + if mem.is_none() { + return self.response_nodata(error, req); + } + res.host_mem = mem.unwrap() as u64; if res .host_mem @@ -889,15 +988,20 @@ impl GpuIoHandler { .filter(|&sum| sum <= self.max_hostmem) .is_some() { - res.pixman_image = create_pixman_image( - pixman_format, - info_create_2d.width as i32, - info_create_2d.height as i32, - ptr::null_mut(), - 0, - ); + if res.format == VIRTIO_GPU_FORMAT_MONOCHROME { + res.monochrome_cursor = vec![0_u8; (res.width * res.height / 8 * 2) as usize]; + } else { + res.pixman_image = create_pixman_image( + get_pixman_format(res.format).unwrap(), + info_create_2d.width as i32, + info_create_2d.height as i32, + ptr::null_mut(), + 0, + ); + } } - if res.pixman_image.is_null() { + + if res.monochrome_cursor.is_empty() && res.pixman_image.is_null() { error!( "GuestError: Fail to create resource(id {}, width {}, height {}) on host.", res.resource_id, res.width, res.height @@ -923,7 +1027,6 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_index]; unref_pixman_image(res.pixman_image); self.used_hostmem -= res.host_mem; - res.iov.clear(); self.resources_list.remove(res_index); } @@ -1159,13 +1262,21 @@ impl GpuIoHandler { trans_info: &VirtioGpuTransferToHost2d, res_idx: usize, ) -> Result<()> { - let res = &self.resources_list[res_idx]; + let res = &mut self.resources_list[res_idx]; let pixman_format = get_image_format(res.pixman_image); let width = get_image_width(res.pixman_image) as u32; let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; let stride = get_image_stride(res.pixman_image) as u32; let data = get_image_data(res.pixman_image).cast() as *mut u8; + if res.format == VIRTIO_GPU_FORMAT_MONOCHROME { + let v = iov_to_buf_direct(&res.iov, 0, &mut res.monochrome_cursor)?; + if v != res.monochrome_cursor.len() { + error!("No enough data is copied for transfer_to_host_2d with monochrome"); + } + return Ok(()); + } + // When the dedicated area is continuous. if trans_info.rect.x_coord == 0 && trans_info.rect.width == width { let offset_dst = (trans_info.rect.y_coord * stride) as usize; @@ -1560,6 +1671,9 @@ impl VirtioDevice for Gpu { if self.cfg.edid { self.base.device_features |= 1 << VIRTIO_GPU_F_EDID; } + + self.base.device_features |= 1 << VIRTIO_GPU_F_MONOCHROME; + self.build_device_config_space(); Ok(()) } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index ce387e8b3..3fd82bfe3 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -167,6 +167,8 @@ pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14; pub const VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP: u32 = 1; /// GPU EDID feature is supported. pub const VIRTIO_GPU_F_EDID: u32 = 1; +/// TODO: need to change to 5 or bigger +pub const VIRTIO_GPU_F_MONOCHROME: u32 = 4; /// The device sets control ok status to driver. pub const VIRTIO_NET_OK: u8 = 0; -- Gitee From 215bdd7ee41e387372ca95e03efc32e303e7609a Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Mon, 27 Nov 2023 22:16:46 +0800 Subject: [PATCH 1489/2187] virtio-gpu-test: adapt to monochrome cursor change Last patch use cal_image_hostmem to adapt monochrome cursor img. Now change to cal_image_hostmem in virtio-gpu-test. Signed-off-by: Binfeng Wu --- tests/mod_test/tests/virtio_gpu_test.rs | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index 2bc1b6d21..8aded9ca2 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -26,12 +26,11 @@ use mod_test::libdriver::virtio_gpu::{ use mod_test::libdriver::virtio_gpu::{set_up, tear_down}; use util::byte_code::ByteCode; use virtio::{ - get_image_hostmem, get_pixman_format, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, - VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_FORMAT_INVALID_UNORM, - VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, - VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, - VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, - VIRTIO_GPU_RESP_OK_NODATA, + cal_image_hostmem, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_FORMAT_INVALID_UNORM, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, }; const D_RES_ID: u32 = 1; @@ -50,8 +49,8 @@ const D_INVALID_NR_ENTRIES: u32 = 1 + 16384; #[test] fn image_display_fun() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -193,8 +192,8 @@ fn cursor_display_fun() { let image_1: Vec = vec![1 as u8; D_IMG_SIZE as usize]; let image_byte_1 = vec![1 as u8; 1]; - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -254,8 +253,8 @@ fn cursor_display_fun() { #[test] fn resource_create_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -315,8 +314,9 @@ fn resource_create_dfx() { #[test] fn resource_destroy_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; + let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; let (dpy, gpu) = set_up(&gpu_cfg); @@ -357,8 +357,8 @@ fn resource_destroy_dfx() { #[test] fn resource_attach_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -424,8 +424,8 @@ fn resource_attach_dfx() { #[test] fn resource_detach_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -460,8 +460,8 @@ fn resource_detach_dfx() { #[test] fn resource_transfer_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -537,8 +537,8 @@ fn resource_transfer_dfx() { #[test] fn scanout_set_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -614,8 +614,8 @@ fn scanout_set_dfx() { #[test] fn scanout_flush_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -664,8 +664,8 @@ fn scanout_flush_dfx() { #[test] fn cursor_update_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -703,8 +703,8 @@ fn cursor_update_dfx() { #[test] fn invalid_cmd_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; @@ -720,8 +720,8 @@ fn invalid_cmd_dfx() { #[test] fn crash_dfx() { - let pixman_format = get_pixman_format(D_FMT).unwrap(); - let image_size = get_image_hostmem(pixman_format, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); gpu_cfg.max_hostmem = image_size; -- Gitee From ff20c0f7b37f44f3cacf4513c11627baa1770ad3 Mon Sep 17 00:00:00 2001 From: wangmeiling Date: Thu, 16 Nov 2023 10:36:21 +0800 Subject: [PATCH 1490/2187] virtio-gpu: Support export display images of windows In order to check display problems easily, exporting windows png with qmp. Signed-off-by: Meiling Wang --- Cargo.lock | 1 + docs/qmp.md | 11 ++++++ machine/src/micro_vm/mod.rs | 9 +++++ machine/src/standard_vm/mod.rs | 16 ++++++++ machine_manager/src/machine.rs | 3 ++ machine_manager/src/qmp/qmp_schema.rs | 25 +++++++++++++ machine_manager/src/qmp/qmp_socket.rs | 1 + ui/Cargo.toml | 3 +- ui/src/console.rs | 2 +- ui/src/gtk/mod.rs | 54 +++++++++++++++++++++++++++ 10 files changed, 123 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b2b4cc00..af67b776e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,6 +1677,7 @@ version = "2.3.0" dependencies = [ "anyhow", "bitintr", + "cairo-rs", "gettext-rs", "gtk", "libc", diff --git a/docs/qmp.md b/docs/qmp.md index ee4e8ec98..a33e41109 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -522,6 +522,17 @@ Query the value of the guest physical address. <- {"return": "B9000001"} ``` +### query-display-image + +Query the display image of virtiogpu. Currently only stdvm and gtk supports. + +#### Example + +```json +-> { "execute": "query-display-image" } +<- { "return": { "fileDir": "/tmp/stratovirt-images", "isSuccess": true } } +``` + ## Others ### getfd diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_vm/mod.rs index c105a644e..ddefa1fe6 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_vm/mod.rs @@ -996,6 +996,15 @@ impl DeviceInterface for LightMachine { ) } + fn query_display_image(&self) -> Response { + Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "query-display-image is not supported".to_string(), + ), + None, + ) + } + fn device_add(&mut self, args: Box) -> Response { // get slot of bus by addr or lun let mut slot = 0; diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index a9c74ff8a..8c8488af7 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -70,6 +70,8 @@ use machine_manager::machine::{DeviceInterface, KvmVmState}; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::MigrationManager; +#[cfg(feature = "gtk")] +use ui::gtk::qmp_query_display_image; use ui::input::{input_button, input_move_abs, input_point_sync, key_event, Axis}; #[cfg(feature = "vnc")] use ui::vnc::qmp_query_vnc; @@ -1242,6 +1244,20 @@ impl DeviceInterface for StdMachine { ) } + fn query_display_image(&self) -> Response { + let mut _err = String::from("The gtk feature is not supported"); + #[cfg(feature = "gtk")] + match qmp_query_display_image() { + Ok(gpu_info) => { + return Response::create_response(serde_json::to_value(gpu_info).unwrap(), None); + } + Err(e) => { + _err = format!("Failed to query_display_image: {:?}", e); + } + }; + Response::create_error_response(qmp_schema::QmpErrorClass::GenericError(_err), None) + } + fn device_add(&mut self, args: Box) -> Response { if let Err(e) = self.check_device_id_existed(&args.id) { return Response::create_error_response( diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 12e5052eb..4c47199c7 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -202,6 +202,9 @@ pub trait DeviceInterface { /// Query the info of vnc server. fn query_vnc(&self) -> Response; + /// Query display of stratovirt. + fn query_display_image(&self) -> Response; + /// Set balloon's size. fn balloon(&self, size: u64) -> Response; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 879748901..d27ea11bc 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -101,6 +101,7 @@ define_qmp_command_enum!( query_mem_gpa("query-mem-gpa", query_mem_gpa, default), query_balloon("query-balloon", query_balloon, default), query_vnc("query-vnc", query_vnc, default), + query_display_image("query-display-image", query_display_image, default), migrate("migrate", migrate), query_migrate("query-migrate", query_migrate, default), cancel_migrate("migrate_cancel", cancel_migrate, default), @@ -1063,6 +1064,30 @@ pub struct VncClientInfo { pub family: String, } +/// query-display-image +/// +/// Information about image of stratovirt. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "query-display-image" } +/// <- { "return": { +/// "fileDir": /tmp/stratovirt-images, +/// "isSuccess": true, } } +/// `` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct query_display_image {} +generate_command_impl!(query_display_image, GpuInfo); + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct GpuInfo { + #[serde(rename = "isSuccess")] + pub isSuccess: bool, + #[serde(rename = "fileDir")] + pub fileDir: String, +} + /// balloon /// /// Advice VM to change memory size with the argument `value`. diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 9dd2beb9c..911906f66 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -463,6 +463,7 @@ fn qmp_command_exec( (query_balloon, query_balloon), (query_mem, query_mem), (query_vnc, query_vnc), + (query_display_image, query_display_image), (list_type, list_type), (query_hotpluggable_cpus, query_hotpluggable_cpus); (input_event, input_event, key, value), diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 0a76ec2d3..2874b9525 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -18,6 +18,7 @@ sscanf = "0.4.1" bitintr = "0.3.0" gtk = { version = "0.17.1", optional = true } gettext-rs = { version = "0.7.0", features = ["gettext-system"], optional = true } +cairo-rs = { version = "0.17.10", features = ["png"], optional = true } rustls = { version = "0.21.1", optional = true } rustls-pemfile = { version = "1.0.2", optional = true } sasl2-sys = { version = "0.1.20", optional = true } @@ -28,5 +29,5 @@ util = { path = "../util" } keycode = [] pixman = ["util/pixman"] console = ["pixman"] -gtk = ["console", "keycode", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] +gtk = ["console", "keycode", "dep:cairo-rs", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] vnc = ["console", "keycode", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] diff --git a/ui/src/console.rs b/ui/src/console.rs index eca469003..815abeb05 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -199,7 +199,7 @@ pub struct DisplayConsole { pub console_list: Weak>, pub dev_opts: Arc, pub timer_id: Option, - active: bool, + pub active: bool, } impl DisplayConsole { diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index ab48897c7..672eb483e 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -17,6 +17,8 @@ use std::{ cell::RefCell, cmp, collections::HashMap, + env, fs, + path::Path, ptr, rc::Rc, sync::{Arc, Mutex, Weak}, @@ -55,7 +57,9 @@ use crate::{ }, }; use machine_manager::config::{DisplayConfig, UiContext}; +use machine_manager::qmp::qmp_schema::GpuInfo; use util::pixman::{pixman_format_code_t, pixman_image_composite, pixman_op_t}; +use util::time::gettime; const CHANNEL_BOUND: usize = 1024; /// Width of default window. @@ -1019,3 +1023,53 @@ pub(crate) fn renew_image(gs: &Rc>) -> Result<()> { .queue_draw_area(0, 0, width as i32, height as i32); Ok(()) } + +pub fn qmp_query_display_image() -> Result { + let mut gpu_info = GpuInfo::default(); + let console_list = get_active_console(); + for con in console_list { + let c = match con.upgrade() { + Some(c) => c, + None => continue, + }; + let mut locked_con = c.lock().unwrap(); + if !locked_con.active { + continue; + } + let dev_name = &locked_con.dev_name.clone(); + + if let Some(surface) = &mut locked_con.surface { + // SAFETY: The image is created within the function, it can be ensure + // that the data ptr is not nullptr and the image size matches the image data. + let cairo_image = unsafe { + ImageSurface::create_for_data_unsafe( + surface.data() as *mut u8, + Format::Rgb24, + surface.width(), + surface.height(), + surface.stride(), + ) + }?; + let mut file = create_file(&mut gpu_info, dev_name)?; + cairo_image.write_to_png(&mut file)?; + }; + } + gpu_info.isSuccess = true; + Ok(gpu_info) +} + +fn create_file(gpu_info: &mut GpuInfo, dev_name: &String) -> Result { + let temp_dir = env::temp_dir().display().to_string(); + let binding = temp_dir + "/stratovirt-images"; + let path = Path::new(&binding); + + if !path.exists() { + fs::create_dir(path)?; + } + let file_dir = path.display().to_string(); + gpu_info.fileDir = file_dir.clone(); + let nsec = gettime().1; + let file_name = file_dir + "/stratovirt-display-" + dev_name + "-" + &nsec.to_string() + ".png"; + let file = fs::File::create(file_name)?; + Ok(file) +} -- Gitee From 7534e16d021d2919329038e91eb0d7dcdf5e3e65 Mon Sep 17 00:00:00 2001 From: Li HuaChao Date: Mon, 27 Nov 2023 12:16:42 +0800 Subject: [PATCH 1491/2187] SMBIOS: support table 19 for aarch64 Add memory array mapped address to support smbios table 19 for aarch64. --- machine/src/standard_vm/aarch64/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 678dcbbbb..4aca55d72 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -622,11 +622,14 @@ impl MachineOps for StdMachine { // If it is direct kernel boot mode, the ACPI can not be enabled. if migrate.0 == MigrateMode::Unknown { if let Some(fw_cfg) = fwcfg { + let mut mem_array = Vec::new(); + let mem_size = vm_config.machine_config.mem_config.mem_size; + mem_array.push((MEM_LAYOUT[LayoutEntryType::Mem as usize].0, mem_size)); locked_vm .build_acpi_tables(&fw_cfg) .with_context(|| "Failed to create ACPI tables")?; locked_vm - .build_smbios(&fw_cfg, Vec::new()) + .build_smbios(&fw_cfg, mem_array) .with_context(|| "Failed to create smbios tables")?; } } -- Gitee From 2437b574e00f29f545116c2a856c9d1535e10f26 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 23 Nov 2023 20:19:45 +0800 Subject: [PATCH 1492/2187] mod_test: rename vhost_user_fs to virtiofsd vhost-user-fs has been deleted(65ea6438f85b), let's rename vhost_user_fs to virtiofsd in mod test. Signed-off-by: yezengruan --- tests/mod_test/tests/virtiofs_test.rs | 86 ++++++++++----------------- 1 file changed, 32 insertions(+), 54 deletions(-) diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index cb0fa9dc5..816adf5a1 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -230,7 +230,7 @@ impl VirtioFsTest { (fuseoutheader_addr.unwrap(), fuseoutbody_addr.unwrap()) } - fn vhost_user_fs_start_with_config( + fn virtiofsd_start_with_config( dir_temp: bool, seccomp: Option, sandbox: Option, @@ -238,7 +238,7 @@ impl VirtioFsTest { rlimit_nofile: Option, xattr: bool, ) -> (String, String, String) { - let binary_path = env::var("VHOST_USER_FS_BINARY").unwrap(); + let binary_path = env::var("VIRTIOFSD_BINARY").unwrap(); let (virtiofs_test_dir, virtiofs_shared_dir, virtiofs_test_file) = env_prepare(dir_temp); let virtiofs_sock = format!("{}/virtiofs.sock", virtiofs_shared_dir); @@ -288,8 +288,8 @@ impl VirtioFsTest { (virtiofs_test_dir, virtiofs_sock, virtiofs_test_file) } - fn vhost_user_fs_start() -> (String, String, String) { - VirtioFsTest::vhost_user_fs_start_with_config(true, None, None, None, None, false) + fn virtiofsd_start() -> (String, String, String) { + VirtioFsTest::virtiofsd_start_with_config(true, None, None, None, None, false) } fn testcase_end(&self, test_dir: String) { @@ -497,7 +497,7 @@ fn fuse_getattr(fs: &VirtioFsTest, nodeid: u64, fh: u64) -> (FuseOutHeader, Fuse fn mount_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config( + VirtioFsTest::virtiofsd_start_with_config( true, Some(SeccompAction::Kill), Some(SandBoxMechanism::Namespace), @@ -521,7 +521,7 @@ fn mount_test() { fn umount_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config( + VirtioFsTest::virtiofsd_start_with_config( true, Some(SeccompAction::None), Some(SandBoxMechanism::Chroot), @@ -549,7 +549,7 @@ fn umount_test() { fn mkdir_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config( + VirtioFsTest::virtiofsd_start_with_config( true, Some(SeccompAction::Log), None, @@ -598,7 +598,7 @@ fn mkdir_test() { fn sync_fun() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config( + VirtioFsTest::virtiofsd_start_with_config( true, Some(SeccompAction::Trap), None, @@ -642,8 +642,7 @@ fn sync_fun() { #[test] fn syncdir_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -677,8 +676,7 @@ fn syncdir_test() { #[test] fn invalid_fuse_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -710,8 +708,7 @@ fn invalid_fuse_test() { #[ignore] fn missing_fuseinbody_fuseoutbody_virtiorequest_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -738,8 +735,7 @@ fn missing_fuseinbody_fuseoutbody_virtiorequest_test() { #[test] fn virtiofs_device_config_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -791,8 +787,7 @@ fn virtiofs_device_config_test() { #[test] fn ls_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -906,8 +901,7 @@ fn fuse_setattr( #[test] fn setattr_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1006,8 +1000,7 @@ fn setattr_test() { #[test] fn unlink_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1047,8 +1040,7 @@ fn unlink_test() { #[test] fn rmdir_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); let mut dir = virtiofs_test_dir.clone(); dir.push_str("/shared/dir"); @@ -1096,8 +1088,7 @@ fn rmdir_test() { #[test] fn symlink_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1162,8 +1153,7 @@ fn symlink_test() { #[test] fn fallocate_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1203,8 +1193,7 @@ fn fallocate_test() { #[ignore] fn posix_file_lock_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1277,8 +1266,7 @@ fn posix_file_lock_test() { #[test] fn mknod_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1442,8 +1430,7 @@ fn create_file(fs: &VirtioFsTest, name: String) -> (FuseOutHeader, FuseCreateOut fn writefile_fun() { let file = "text.txt".to_string(); // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1508,8 +1495,7 @@ fn openfile_test() { let file = TEST_FILE_NAME.to_string(); // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1539,8 +1525,7 @@ fn openfile_test() { #[test] fn rename_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1577,8 +1562,7 @@ fn rename_test() { #[test] fn link_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1616,8 +1600,7 @@ fn link_test() { #[test] fn statfs_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1646,8 +1629,7 @@ fn statfs_test() { #[test] fn virtio_fs_fuse_ioctl_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1679,8 +1661,7 @@ fn virtio_fs_fuse_ioctl_test() { #[test] fn virtio_fs_fuse_abnormal_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1798,7 +1779,7 @@ fn fuse_listxattr(fs: &VirtioFsTest, nodeid: u64) -> (FuseOutHeader, u64) { fn regularfile_xattr_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, true); + VirtioFsTest::virtiofsd_start_with_config(false, None, None, None, None, true); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1856,7 +1837,7 @@ fn regularfile_xattr_test() { fn character_file_xattr_test() { // start virtiofsd process. let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start_with_config(false, None, None, None, None, true); + VirtioFsTest::virtiofsd_start_with_config(false, None, None, None, None, true); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1895,8 +1876,7 @@ fn character_file_xattr_test() { #[test] fn virtio_fs_fuse_lseek_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1952,8 +1932,7 @@ fn fuse_batch_forget(fs: &VirtioFsTest, nodeid: u64, trim: usize) { #[test] fn virtio_fs_fuse_batch_forget_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); @@ -1988,8 +1967,7 @@ fn virtio_fs_fuse_batch_forget_test() { #[ignore] fn virtio_fs_fuse_setlkw_test() { // start virtiofsd process. - let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = - VirtioFsTest::vhost_user_fs_start(); + let (virtiofs_test_dir, virtiofs_sock, _virtiofs_test_file) = VirtioFsTest::virtiofsd_start(); // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); -- Gitee From 02776a541766f5440b9ec8076eea4a06092008c1 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 23 Nov 2023 20:20:22 +0800 Subject: [PATCH 1493/2187] docs: add test docs for stratovirt Add docs for stratovirt unit test and mod test. Signed-off-by: yezengruan --- docs/test.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/test.md diff --git a/docs/test.md b/docs/test.md new file mode 100644 index 000000000..c12e17b0c --- /dev/null +++ b/docs/test.md @@ -0,0 +1,44 @@ +# StratoVirt test + +StratoVirt supports two test modes: unit test and mod test. It should be noted that mod test is not fully supported on the x86_64 architecture now. + +## Unit test + +Unit tests are Rust functions that verify that the non-test code is functioning in the expected manner. We recommend performing unit test execution separately, run StratoVirt unit test as follows: + +```shell +$ cargo test --workspace --exclude mod_test -- --nocapture --test-threads=1 +``` + +## Mod test + +StratoVirt mod test is an integrated testing method. During the test, the StratoVirt process will be started as the server and communicate through socket and QMP to test the StratoVirt module function. + +Before running mod test, we need to compile `stratovirt` and `virtiofsd` first, and then export the environment variables `STRATOVIRT_BINARY` and `VIRTIOFSD_BINARY`. + +Build StratoVirt: + +```shell +$ cargo build --workspace --bins --release --target=aarch64-unknown-linux-gnu --all-features +``` + +Build virtiofsd: + +```shell +$ git clone https://gitlab.com/virtio-fs/virtiofsd.git +$ cd virtiofsd +$ cargo build --release +``` + +Export the environment variables `STRATOVIRT_BINARY` and `VIRTIOFSD_BINARY`: + +```shell +$ export STRATOVIRT_BINARY="/path/to/stratovirt" +$ export VIRTIOFSD_BINARY="/path/to/virtiofsd" +``` + +Run StratoVirt mod test as follows: + +```shell +$ cargo test --all-features -p mod_test -- --nocapture --test-threads=1 +``` -- Gitee From 7d7effbac2646e346cfd261bca459369dbf0a01b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 28 Nov 2023 22:59:53 +0800 Subject: [PATCH 1494/2187] fmt: replace try_into with as Reduce the use of `try_into().unwrap()`, replace `try_into` with `as`, the code will be clearer. Signed-off-by: yezengruan --- devices/src/acpi/ged.rs | 2 +- devices/src/acpi/power.rs | 2 +- devices/src/usb/tablet.rs | 4 +-- machine/src/standard_vm/x86_64/mod.rs | 2 +- tests/mod_test/src/libdriver/pci.rs | 12 ++++----- tests/mod_test/src/libdriver/virtio.rs | 6 ++--- tests/mod_test/src/libdriver/virtio_block.rs | 8 +++--- tests/mod_test/tests/net_test.rs | 26 ++++++-------------- tests/mod_test/tests/pci_test.rs | 5 ++-- tests/mod_test/tests/scsi_test.rs | 9 +++---- tests/mod_test/tests/serial_test.rs | 9 ++----- tests/mod_test/tests/virtiofs_test.rs | 7 +----- ui/src/pixman.rs | 4 +-- ui/src/vnc/auth_sasl.rs | 2 +- 14 files changed, 36 insertions(+), 62 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index a208762f8..576294558 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -99,7 +99,7 @@ impl Ged { ) -> Result>> { self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); self.set_sys_resource(sysbus, region_base, region_size) - .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; + .with_context(|| AcpiError::Alignment(region_size as u32))?; self.battery_present = battery_present; let dev = Arc::new(Mutex::new(self)); diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 87c8bc70e..aa57df4cd 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -182,7 +182,7 @@ impl PowerDev { region_size: u64, ) -> Result<()> { self.set_sys_resource(sysbus, region_base, region_size) - .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; + .with_context(|| AcpiError::Alignment(region_size as u32))?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size, "PowerDev")?; diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index e2c7de045..bb93024ac 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -172,8 +172,8 @@ impl PointerOpts for UsbTabletAdapter { InputType::MoveEvent => { let move_event = &input_event.move_event; match move_event.axis { - Axis::X => evt.pos_x = min(move_event.data as u32, INPUT_COORDINATES_MAX), - Axis::Y => evt.pos_y = min(move_event.data as u32, INPUT_COORDINATES_MAX), + Axis::X => evt.pos_x = min(move_event.data, INPUT_COORDINATES_MAX), + Axis::Y => evt.pos_y = min(move_event.data, INPUT_COORDINATES_MAX), } } _ => bail!( diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index af8c270de..ce5dd75d8 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -803,7 +803,7 @@ impl AcpiBuilder for StdMachine { }); // Add non boot cpu lapic. - for cpuid in self.base.cpu_topo.nrcpus as u8..self.base.cpu_topo.max_cpus { + for cpuid in self.base.cpu_topo.nrcpus..self.base.cpu_topo.max_cpus { let lapic = AcpiLocalApic { type_id: 0, length: size_of::() as u8, diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 3e6cb540e..e78f7d67a 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -198,18 +198,18 @@ impl TestPciDev { self.msix_table_bar = if let Some(addr) = bar_addr { addr } else { - self.io_map(bar_table.try_into().unwrap()) + self.io_map(bar_table as u8) }; - self.msix_table_off = (table & !PCI_MSIX_TABLE_BIR).try_into().unwrap(); + self.msix_table_off = (table & !PCI_MSIX_TABLE_BIR) as u64; let table = self.config_readl(addr + PCI_MSIX_PBA); let bar_pba = table & PCI_MSIX_TABLE_BIR; if bar_pba != bar_table { - self.msix_pba_bar = self.io_map(bar_pba.try_into().unwrap()); + self.msix_pba_bar = self.io_map(bar_pba as u8); } else { self.msix_pba_bar = self.msix_table_bar; } - self.msix_pba_off = (table & !PCI_MSIX_TABLE_BIR).try_into().unwrap(); + self.msix_pba_off = (table & !PCI_MSIX_TABLE_BIR) as u64; self.msix_enabled = true; } @@ -419,12 +419,12 @@ impl PciMsixOps for TestPciDev { self.io_writel( msix_table_bar, offset + PCI_MSIX_ENTRY_LOWER_ADDR, - msix_addr.try_into().unwrap(), + msix_addr as u32, ); self.io_writel( msix_table_bar, offset + PCI_MSIX_ENTRY_UPPER_ADDR, - (msix_addr >> 32).try_into().unwrap(), + (msix_addr >> 32) as u32, ); self.io_writel(msix_table_bar, offset + PCI_MSIX_ENTRY_DATA, msix_data); diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index b7db01a55..1c9297b7d 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -220,7 +220,7 @@ impl TestVringIndirectDesc { self.elem = elem; self.desc = alloc .borrow_mut() - .alloc((size_of::() * elem as usize).try_into().unwrap()); + .alloc((size_of::() * elem as usize) as u64); for i in 0..elem - 1 { test_state @@ -331,7 +331,7 @@ impl TestVirtQueue { test_state.borrow().writew( self.desc + (size_of::() * i as usize + offset_of!(VringDesc, next)) as u64, - (i + 1).try_into().unwrap(), + (i + 1) as u16, ); } @@ -376,7 +376,7 @@ impl TestVirtQueue { let features = virtio_dev.get_guest_features(); virtio_dev.queue_select(index); - let queue_size = virtio_dev.get_queue_size().try_into().unwrap(); + let queue_size = virtio_dev.get_queue_size() as u32; assert!(queue_size != 0); assert!(queue_size & (queue_size - 1) == 0); diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index 2896a5396..67340432f 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -183,11 +183,9 @@ pub fn virtio_blk_request( } } - let addr = alloc.borrow_mut().alloc( - (size_of::() + data_size + 512) - .try_into() - .unwrap(), - ); + let addr = alloc + .borrow_mut() + .alloc((size_of::() + data_size + 512) as u64); let data_addr = if align { round_up(addr + REQ_ADDR_LEN as u64, 512).unwrap() diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 2c84f86e4..3ec79a1eb 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -509,7 +509,7 @@ fn fill_rx_vq( ) { let size = vq.borrow().size; for _ in 0..size { - let addr = alloc.borrow_mut().alloc(MAX_PACKET_LEN).try_into().unwrap(); + let addr = alloc.borrow_mut().alloc(MAX_PACKET_LEN); vq.borrow_mut() .add(test_state.clone(), addr, MAX_PACKET_LEN as u32, true); } @@ -671,7 +671,7 @@ fn send_request( request: &[u8], ) { let length = request.len() as u64; - let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + let addr = alloc.borrow_mut().alloc(length); let k_bytes = 1024; let num_k = length / k_bytes; @@ -867,9 +867,7 @@ fn virtio_net_ctrl_mq_test() { // The message: CtrlHdr, vq_pairs, ack. let addr = alloc .borrow_mut() - .alloc(size_of::() as u64 + 2 + 1) - .try_into() - .unwrap(); + .alloc(size_of::() as u64 + 2 + 1); let mut cmd = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET as u8; if test_type == 2 { @@ -1073,11 +1071,7 @@ fn send_ctrl_vq_request( ack: u8, ) { let ctrl_vq = &vqs[2]; - let addr = alloc - .borrow_mut() - .alloc(ctrl_data.len() as u64) - .try_into() - .unwrap(); + let addr = alloc.borrow_mut().alloc(ctrl_data.len() as u64); test_state.borrow().memwrite(addr, &ctrl_data); let data_entries: Vec = vec![ TestVringDescEntry { @@ -1668,11 +1662,7 @@ fn virtio_net_ctrl_abnormal_test() { let test_num = 2; for i in 0..test_num { let ctrl_vq = &vqs[2]; - let addr = alloc - .borrow_mut() - .alloc(ctrl_data.len() as u64) - .try_into() - .unwrap(); + let addr = alloc.borrow_mut().alloc(ctrl_data.len() as u64); test_state.borrow().memwrite(addr, &ctrl_data); // ctrl_rx_info.switch: u8 @@ -1759,7 +1749,7 @@ fn virtio_net_abnormal_rx_tx_test() { let size = net.borrow().get_queue_size(); assert_eq!(size, QUEUE_SIZE_NET); for _ in 0..size { - let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + let addr = alloc.borrow_mut().alloc(length); test_state.borrow().memwrite(addr, &request.as_bytes()); vqs[1] .borrow_mut() @@ -1839,7 +1829,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { let request = get_arp_request(id); let length = request.as_bytes().len() as u64; - let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + let addr = alloc.borrow_mut().alloc(length); test_state.borrow().memwrite(addr, &request.as_bytes()); vqs[1] .borrow_mut() @@ -2097,7 +2087,7 @@ fn virtio_net_abnormal_rx_tx_test_3() { let request = get_arp_request(id); let length = request.as_bytes().len() as u64; - let addr = alloc.borrow_mut().alloc(length).try_into().unwrap(); + let addr = alloc.borrow_mut().alloc(length); test_state.borrow().memwrite(addr, &request.as_bytes()); vqs[1] .borrow_mut() diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index d9981f8bb..e23d67b12 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -153,7 +153,7 @@ fn build_root_port_args(root_port_nums: u8) -> Vec { multifunc = true; } - let mut root_port_args: Vec = Vec::with_capacity(root_port_nums.try_into().unwrap()); + let mut root_port_args: Vec = Vec::with_capacity(root_port_nums as usize); let mut addr = 1; let mut func = 0; for bus in 1..=root_port_nums { @@ -192,8 +192,7 @@ fn build_blk_args( assert!(blk_nums < MAX_DEVICE_NUM); } - let mut blk_args: Vec<(String, u8, u8, u8, u8, bool)> = - Vec::with_capacity(blk_nums.try_into().unwrap()); + let mut blk_args: Vec<(String, u8, u8, u8, u8, bool)> = Vec::with_capacity(blk_nums as usize); let mut slot = 0; let mut func = 0; let mut nums = 0; diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 0498adaea..aecb4b808 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -187,10 +187,7 @@ impl VirtioScsiTest { // Request Header. let cmdreq_len = size_of::() as u64; - let req_addr = self - .alloc - .borrow_mut() - .alloc(cmdreq_len.try_into().unwrap()); + let req_addr = self.alloc.borrow_mut().alloc(cmdreq_len); let req_bytes = req.as_bytes(); self.state.borrow().memwrite(req_addr, req_bytes); @@ -204,7 +201,7 @@ impl VirtioScsiTest { if let Some(data) = data_out { let out_len = data.len() as u32; let out_bytes = data.as_bytes().to_vec(); - let out_addr = self.alloc.borrow_mut().alloc(out_len.try_into().unwrap()); + let out_addr = self.alloc.borrow_mut().alloc(out_len as u64); self.state.borrow().memwrite(out_addr, out_bytes.as_slice()); data_entries.push(TestVringDescEntry { data: out_addr, @@ -218,7 +215,7 @@ impl VirtioScsiTest { let resp_addr = self .alloc .borrow_mut() - .alloc((cmdresp_len + data_in_len as u64).try_into().unwrap()); + .alloc(cmdresp_len + data_in_len as u64); let resp_bytes = resp.as_bytes(); self.state.borrow().memwrite(resp_addr, resp_bytes); diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index b33f31e64..ac46ce516 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -460,13 +460,8 @@ fn create_serial(ports_config: Vec, pci_slot: u8, pci_fn: u8) -> Ser } fn verify_output_data(test_state: Rc>, addr: u64, len: u32, test_data: &String) { - let mut data_buf: Vec = Vec::with_capacity(len.try_into().unwrap()); - data_buf.append( - test_state - .borrow() - .memread(addr, len.try_into().unwrap()) - .as_mut(), - ); + let mut data_buf: Vec = Vec::with_capacity(len as usize); + data_buf.append(test_state.borrow().memread(addr, len as u64).as_mut()); let data = String::from_utf8(data_buf).unwrap(); assert_eq!(data, *test_data); } diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 816adf5a1..544c88083 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -155,12 +155,7 @@ impl VirtioFsTest { ) -> Option { if let Some(member) = reqmember { let member_size = member.len() as u64; - let member_addr = self - .allocator - .borrow_mut() - .alloc(member_size) - .try_into() - .unwrap(); + let member_addr = self.allocator.borrow_mut().alloc(member_size); self.state.borrow().memwrite(member_addr, &member); data_entries.push(TestVringDescEntry { data: member_addr, diff --git a/ui/src/pixman.rs b/ui/src/pixman.rs index 087abfe94..dd01d3af4 100644 --- a/ui/src/pixman.rs +++ b/ui/src/pixman.rs @@ -622,8 +622,8 @@ pub fn pixman_glyph_from_vgafont(height: i32, ch: u32) -> *mut pixman_image_t { ); let data = pixman_image_get_data(glyph) as *mut u8; let mut data_index: usize = 0; - let mut font_index: usize = (height * ch as i32).try_into().unwrap(); - let slice = std::slice::from_raw_parts_mut(data, (height * 8).try_into().unwrap()); + let mut font_index: usize = (height * ch as i32) as usize; + let slice = std::slice::from_raw_parts_mut(data, (height * 8) as usize); for _y in 0..height { for _x in 0..8 { diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 0207ee284..7acee827d 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -389,7 +389,7 @@ impl ClientIoHandler { unsafe { err = sasl_setprop( security.saslconfig.sasl_conn, - SASL_SEC_PROPS.try_into()?, + SASL_SEC_PROPS as i32, props as *const c_void, ); } -- Gitee From 92def5bbd36e79557bed8b6b5d18a2a9a04e6894 Mon Sep 17 00:00:00 2001 From: li-huachao Date: Thu, 7 Dec 2023 09:23:15 +0800 Subject: [PATCH 1495/2187] fwcfg MST: Adapting table length fixes tests fail After adding the contents of table 19, we need to adapt to the mst test case and modify the expected value of the entire table length. --- tests/mod_test/tests/fwcfg_test.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index 9827d9dbe..f82083256 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -258,7 +258,7 @@ fn test_smbios_type0() { assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); assert_eq!(read_data[6], 24 as u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); - assert_eq!(talble_len, 339); + assert_eq!(talble_len, 372); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); let talbles_size = test_state.borrow().fw_cfg_read_file( @@ -322,7 +322,7 @@ fn test_smbios_type1() { assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); assert_eq!(read_data[6], 24 as u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); - assert_eq!(talble_len, 381); + assert_eq!(talble_len, 414); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); let talbles_size = test_state.borrow().fw_cfg_read_file( @@ -662,7 +662,7 @@ fn test_smbios_type17() { assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); assert_eq!(read_data[6], 24 as u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); - assert_eq!(talble_len, 434); + assert_eq!(talble_len, 467); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); let talbles_size = test_state.borrow().fw_cfg_read_file( -- Gitee From 29a838eb756c4be82c1d07e84ae063d5b1498b90 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 8 Dec 2023 15:15:33 +0800 Subject: [PATCH 1496/2187] scream: fixup the issue of access violation The header of shmem is shared with the guest driver. It has risk that the guest driver might write wrong data to the header so that the backend might write to outside of the shared memory. Let's check the the validation of audio base and size. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 61 ++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 4fc0a5258..870bef8cb 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -81,7 +81,7 @@ pub struct ShmemStreamHeader { } impl ShmemStreamHeader { - pub fn check(&self, shmem_size: u64, last_end: u64) -> bool { + pub fn check(&self, last_end: u64) -> bool { if (self.offset as u64) < last_end { warn!( "Guest set bad offset {} exceeds last stream buffer end {}", @@ -89,15 +89,6 @@ impl ShmemStreamHeader { ); } - let boundary = self.offset as u64 + self.chunk_size as u64 * self.max_chunks as u64; - if boundary > shmem_size { - error!( - "Guest set bad stream params: offset {:x} max chunk num is {}, chunk size is {}", - self.offset, self.max_chunks, self.chunk_size - ); - return false; - } - if self.chunk_idx > self.max_chunks { error!( "The chunk index of stream {} exceeds the maximum number of chunks {}", @@ -183,7 +174,6 @@ impl StreamData { dir: ScreamDirection, poll_delay_us: u64, hva: u64, - shmem_size: u64, ) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the scream realize. @@ -219,7 +209,7 @@ impl StreamData { + header.play.chunk_size as u64 * header.play.max_chunks as u64; } - if !stream_header.check(shmem_size, last_end) { + if !stream_header.check(last_end) { continue; } @@ -233,14 +223,36 @@ impl StreamData { } } - fn update_buffer_by_chunk_idx(&mut self, hva: u64, stream_header: &ShmemStreamHeader) { + fn update_buffer_by_chunk_idx( + &mut self, + hva: u64, + shmem_size: u64, + stream_header: &ShmemStreamHeader, + ) -> bool { self.audio_size = stream_header.chunk_size; self.audio_base = hva + stream_header.offset as u64 + (stream_header.chunk_size as u64) * (self.chunk_idx as u64); + + if (self.audio_base + self.audio_size as u64) > (hva + shmem_size) { + error!( + "Scream: wrong header: offset {} chunk_idx {} chunk_size {} max_chunks {}", + stream_header.offset, + stream_header.chunk_idx, + stream_header.chunk_size, + stream_header.max_chunks, + ); + return false; + } + true } - fn playback_trans(&mut self, hva: u64, interface: Arc>) { + fn playback_trans( + &mut self, + hva: u64, + shmem_size: u64, + interface: Arc>, + ) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; @@ -257,19 +269,28 @@ impl StreamData { self.chunk_idx = (self.chunk_idx + 1) % play.max_chunks; } - self.update_buffer_by_chunk_idx(hva, play); + if !self.update_buffer_by_chunk_idx(hva, shmem_size, play) { + return; + } interface.lock().unwrap().send(self); } } - fn capture_trans(&mut self, hva: u64, interface: Arc>) { + fn capture_trans( + &mut self, + hva: u64, + shmem_size: u64, + interface: Arc>, + ) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let capt = &mut header.capt; while capt.is_started != 0 { - self.update_buffer_by_chunk_idx(hva, capt); + if !self.update_buffer_by_chunk_idx(hva, shmem_size, capt) { + return; + } if interface.lock().unwrap().receive(self) { self.chunk_idx = (self.chunk_idx + 1) % capt.max_chunks; @@ -334,10 +355,9 @@ impl Scream { ScreamDirection::Playback, POLL_DELAY_US, hva, - shmem_size, ); - play_data.playback_trans(hva, clone_interface.clone()); + play_data.playback_trans(hva, shmem_size, clone_interface.clone()); } }) .with_context(|| "Failed to create thread scream")?; @@ -360,10 +380,9 @@ impl Scream { ScreamDirection::Record, POLL_DELAY_US, hva, - shmem_size, ); - capt_data.capture_trans(hva, clone_interface.clone()); + capt_data.capture_trans(hva, shmem_size, clone_interface.clone()); } }) .with_context(|| "Failed to create thread scream")?; -- Gitee From 06fcf29c02666e8fbd9b382f13d1c17970c9a4f6 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 24 Nov 2023 14:48:47 +0800 Subject: [PATCH 1497/2187] machine: Microvms are split based on the architecture. Signed-off-by: Mingwang Li --- machine/src/arch/aarch64/micro.rs | 309 +++++++++++ .../error.rs => arch/aarch64/mod.rs} | 36 +- .../src/{standard_vm/error.rs => arch/mod.rs} | 55 +- machine/src/arch/x86_64/micro.rs | 260 +++++++++ machine/src/arch/x86_64/mod.rs | 13 + machine/src/error.rs | 35 +- machine/src/lib.rs | 13 +- machine/src/{micro_vm => micro_common}/mod.rs | 501 ++---------------- .../src/{micro_vm => micro_common}/syscall.rs | 65 +-- machine/src/micro_vm/mem_layout.rs | 63 --- machine/src/standard_vm/aarch64/mod.rs | 9 +- machine/src/standard_vm/mod.rs | 4 +- machine/src/standard_vm/x86_64/mod.rs | 7 +- 13 files changed, 695 insertions(+), 675 deletions(-) create mode 100644 machine/src/arch/aarch64/micro.rs rename machine/src/{micro_vm/error.rs => arch/aarch64/mod.rs} (36%) rename machine/src/{standard_vm/error.rs => arch/mod.rs} (30%) create mode 100644 machine/src/arch/x86_64/micro.rs create mode 100644 machine/src/arch/x86_64/mod.rs rename machine/src/{micro_vm => micro_common}/mod.rs (66%) rename machine/src/{micro_vm => micro_common}/syscall.rs (77%) delete mode 100644 machine/src/micro_vm/mem_layout.rs diff --git a/machine/src/arch/aarch64/micro.rs b/machine/src/arch/aarch64/micro.rs new file mode 100644 index 000000000..70949c420 --- /dev/null +++ b/machine/src/arch/aarch64/micro.rs @@ -0,0 +1,309 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; + +use crate::{ + micro_common::{ + syscall::syscall_whitelist, trace_cpu_topo, trace_replaceable_info, trace_sysbus, + trace_vm_state, + }, + MachineBase, MachineError, +}; +use crate::{LightMachine, MachineOps}; +use address_space::{AddressSpace, GuestAddress, Region}; +use cpu::CPUTopology; +use devices::{ + legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX, +}; +use hypervisor::kvm::*; +use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; +use migration::{MigrationManager, MigrationStatus}; +use util::{ + device_tree::{self, CompileFDT, FdtBuilder}, + seccomp::{BpfRule, SeccompCmpOpt}, +}; +use virtio::VirtioMmioDevice; + +#[repr(usize)] +pub enum LayoutEntryType { + GicDist, + GicCpu, + GicIts, + GicRedist, + Uart, + Rtc, + Mmio, + Mem, + HighGicRedist, +} + +pub const MEM_LAYOUT: &[(u64, u64)] = &[ + (0x0800_0000, 0x0001_0000), // GicDist + (0x0801_0000, 0x0001_0000), // GicCpu + (0x0808_0000, 0x0002_0000), // GicIts + (0x080A_0000, 0x00F6_0000), // GicRedist (max 123 redistributors) + (0x0900_0000, 0x0000_1000), // Uart + (0x0901_0000, 0x0000_1000), // Rtc + (0x0A00_0000, 0x0000_0200), // Mmio + (0x4000_0000, 0x80_0000_0000), // Mem + (256 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) +]; + +impl MachineOps for LightMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + + fn machine_base_mut(&mut self) -> &mut MachineBase { + &mut self.base + } + + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { + let vm_ram = self.get_vm_ram(); + let layout_size = MEM_LAYOUT[LayoutEntryType::Mem as usize].1; + let ram = Region::init_alias_region( + vm_ram.clone(), + 0, + std::cmp::min(layout_size, mem_size), + "pc_ram", + ); + sys_mem + .root() + .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) + } + + fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { + let v3 = ICGICv3Config { + msi: true, + dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], + redist_region_ranges: vec![ + MEM_LAYOUT[LayoutEntryType::GicRedist as usize], + MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize], + ], + its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), + }; + let v2 = ICGICv2Config { + dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], + cpu_range: MEM_LAYOUT[LayoutEntryType::GicCpu as usize], + v2m_range: None, + sys_mem: None, + }; + // Passing both v2 and v3, leave GIC self to decide which one to use. + let intc_conf = ICGICConfig { + version: None, + vcpu_count, + max_irq: GIC_IRQ_MAX, + v3: Some(v3), + v2: Some(v2), + }; + let irq_chip = InterruptController::new(&intc_conf)?; + self.base.irq_chip = Some(Arc::new(irq_chip)); + self.base.irq_chip.as_ref().unwrap().realize() + } + + fn add_rtc_device(&mut self) -> Result<()> { + PL031::realize( + PL031::default(), + &mut self.base.sysbus, + MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, + MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, + ) + .with_context(|| "Failed to realize pl031.") + } + + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { + use devices::legacy::PL011; + + let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; + let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; + + let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; + pl011 + .realize( + &mut self.base.sysbus, + region_base, + region_size, + &self.base.boot_source, + ) + .with_context(|| "Failed to realize PL011") + } + + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); + + trace_sysbus(&locked_vm.base.sysbus); + trace_vm_state(&locked_vm.base.vm_state); + + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); + trace_cpu_topo(&topology); + locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.init_memory( + &vm_config.machine_config.mem_config, + &locked_vm.base.sys_mem, + vm_config.machine_config.nr_cpus, + )?; + + let migrate_info = locked_vm.get_migrate_info(); + + let (boot_config, cpu_config) = if migrate_info.0 == MigrateMode::Unknown { + ( + Some( + locked_vm + .load_boot_source(None, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?, + ), + Some(locked_vm.load_cpu_features(vm_config)?), + ) + } else { + (None, None) + }; + + // vCPUs init,and apply CPU features (for aarch64) + locked_vm.base.cpus.extend(::init_vcpu( + vm.clone(), + vm_config.machine_config.nr_cpus, + &topology, + &boot_config, + &cpu_config, + )?); + + locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + + locked_vm.cpu_post_init(&cpu_config)?; + + // Add mmio devices + locked_vm + .create_replaceable_devices() + .with_context(|| "Failed to create replaceable devices.")?; + locked_vm.add_devices(vm_config)?; + trace_replaceable_info(&locked_vm.replaceable_info); + + if let Some(boot_cfg) = boot_config { + let mut fdt_helper = FdtBuilder::new(); + locked_vm + .generate_fdt_node(&mut fdt_helper) + .with_context(|| MachineError::GenFdtErr)?; + let fdt_vec = fdt_helper.finish()?; + locked_vm + .base + .sys_mem + .write( + &mut fdt_vec.as_slice(), + GuestAddress(boot_cfg.fdt_addr), + fdt_vec.len() as u64, + ) + .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; + } + + MigrationManager::register_vm_instance(vm.clone()); + if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { + bail!("Failed to set migration status {}", e); + } + + Ok(()) + } + + fn add_virtio_mmio_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + self.add_virtio_mmio_net(vm_config, cfg_args) + } + + fn add_virtio_mmio_block(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + self.add_virtio_mmio_block(vm_config, cfg_args) + } + + fn realize_virtio_mmio_device( + &mut self, + dev: VirtioMmioDevice, + ) -> Result>> { + self.realize_virtio_mmio_device(dev) + } + + fn syscall_whitelist(&self) -> Vec { + syscall_whitelist() + } +} + +pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { + bpf_rule + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_ONE_REG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEVICE_ATTR() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) +} + +pub(crate) fn arch_syscall_whitelist() -> Vec { + vec![ + BpfRule::new(libc::SYS_epoll_pwait), + BpfRule::new(libc::SYS_newfstatat), + BpfRule::new(libc::SYS_unlinkat), + BpfRule::new(libc::SYS_mkdirat), + ] +} + +/// Trait that helps to generate all nodes in device-tree. +#[allow(clippy::upper_case_acronyms)] +trait CompileFDTHelper { + /// Function that helps to generate memory nodes. + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + /// Function that helps to generate the chosen node. + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; +} + +impl CompileFDTHelper for LightMachine { + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let mem_size = self.base.sys_mem.memory_end_address().raw_value() + - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let node = "memory"; + let memory_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; + fdt.end_node(memory_node_dep) + } + + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + let node = "chosen"; + let boot_source = self.base.boot_source.lock().unwrap(); + + let chosen_node_dep = fdt.begin_node(node)?; + let cmdline = &boot_source.kernel_cmdline.to_string(); + fdt.set_property_string("bootargs", cmdline.as_str())?; + + let pl011_property_string = + format!("/pl011@{:x}", MEM_LAYOUT[LayoutEntryType::Uart as usize].0); + fdt.set_property_string("stdout-path", &pl011_property_string)?; + + match &boot_source.initrd { + Some(initrd) => { + fdt.set_property_u64("linux,initrd-start", initrd.initrd_addr)?; + fdt.set_property_u64("linux,initrd-end", initrd.initrd_addr + initrd.initrd_size)?; + } + None => {} + } + fdt.end_node(chosen_node_dep) + } +} + +impl device_tree::CompileFDT for LightMachine { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + let node_dep = fdt.begin_node("")?; + self.base.generate_fdt_node(fdt)?; + self.generate_memory_node(fdt)?; + self.generate_chosen_node(fdt)?; + fdt.end_node(node_dep) + } +} diff --git a/machine/src/micro_vm/error.rs b/machine/src/arch/aarch64/mod.rs similarity index 36% rename from machine/src/micro_vm/error.rs rename to machine/src/arch/aarch64/mod.rs index 26491cc7c..04b85ef67 100644 --- a/machine/src/micro_vm/error.rs +++ b/machine/src/arch/aarch64/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -10,36 +10,4 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum MicroVmError { - #[error("Util")] - Util { - #[from] - source: util::error::UtilError, - }, - #[error("Util")] - Virtio { - #[from] - source: virtio::error::VirtioError, - }, - #[error("Util")] - Io { - #[from] - source: std::io::Error, - }, - #[error("Util")] - Nul { - #[from] - source: std::ffi::NulError, - }, - #[error("A maximum of {0} {1} replaceable devices are supported.")] - RplDevLmtErr(String, usize), - #[error("The device type is {0}, but the target config is not for this type.")] - DevTypeErr(String), - #[error("{0}: failed to update config.")] - UpdCfgErr(String), - #[error("Failed to realize virtio mmio.")] - RlzVirtioMmioErr, -} +pub mod micro; diff --git a/machine/src/standard_vm/error.rs b/machine/src/arch/mod.rs similarity index 30% rename from machine/src/standard_vm/error.rs rename to machine/src/arch/mod.rs index f0384c50a..bd98a7548 100644 --- a/machine/src/standard_vm/error.rs +++ b/machine/src/arch/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -10,52 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use thiserror::Error; - -#[allow(clippy::upper_case_acronyms)] -#[derive(Error, Debug)] -pub enum StandardVmError { - #[error("")] - AddressSpace { - #[from] - source: address_space::error::AddressSpaceError, - }, - #[error("")] - Cpu { - #[from] - source: cpu::error::CpuError, - }, - #[error("")] - Legacy { - #[from] - source: devices::legacy::error::LegacyError, - }, - #[error("")] - PciErr { - #[from] - source: devices::pci::error::PciError, - }, - #[error("")] - Acpi { - #[from] - source: acpi::error::AcpiError, - }, - #[error("")] - MachineManager { - #[from] - source: machine_manager::config::error::ConfigError, - }, - #[error("")] - Io { - #[from] - source: std::io::Error, - }, - #[error("Failed to init PCIe host.")] - InitPCIeHostErr, - #[error("Failed to open file: {0}.")] - OpenFileErr(String), - #[error("Failed to init pflash device.")] - InitPflashErr, - #[error("Failed to realize pflash device.")] - RlzPflashErr, -} +#[cfg(target_arch = "aarch64")] +pub mod aarch64; +#[cfg(target_arch = "x86_64")] +pub mod x86_64; diff --git a/machine/src/arch/x86_64/micro.rs b/machine/src/arch/x86_64/micro.rs new file mode 100644 index 000000000..9070fa0a4 --- /dev/null +++ b/machine/src/arch/x86_64/micro.rs @@ -0,0 +1,260 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; + +use crate::vm_state; +use crate::{ + micro_common::{ + syscall::syscall_whitelist, trace_cpu_topo, trace_replaceable_info, trace_sysbus, + trace_vm_state, + }, + LightMachine, MachineBase, MachineError, MachineOps, +}; +use address_space::{AddressSpace, Region}; +use cpu::{CPUBootConfig, CPUTopology}; +use devices::legacy::FwCfgOps; +use hypervisor::kvm::KVM_FDS; +use hypervisor::kvm::*; +use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; +use migration::{MigrationManager, MigrationStatus}; +use util::seccomp::{BpfRule, SeccompCmpOpt}; +use virtio::VirtioMmioDevice; + +#[repr(usize)] +pub enum LayoutEntryType { + MemBelow4g = 0_usize, + Mmio, + IoApic, + LocalApic, + IdentTss, + MemAbove4g, +} + +pub const MEM_LAYOUT: &[(u64, u64)] = &[ + (0, 0xC000_0000), // MemBelow4g + (0xF010_0000, 0x200), // Mmio + (0xFEC0_0000, 0x10_0000), // IoApic + (0xFEE0_0000, 0x10_0000), // LocalApic + (0xFEF0_C000, 0x4000), // Identity map address and TSS + (0x1_0000_0000, 0x80_0000_0000), // MemAbove4g +]; + +impl MachineOps for LightMachine { + fn machine_base(&self) -> &MachineBase { + &self.base + } + + fn machine_base_mut(&mut self) -> &mut MachineBase { + &mut self.base + } + + fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { + let vm_ram = self.get_vm_ram(); + let below4g_size = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + let below4g_ram = Region::init_alias_region( + vm_ram.clone(), + 0, + std::cmp::min(below4g_size, mem_size), + "below4g_ram", + ); + sys_mem.root().add_subregion( + below4g_ram, + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, + )?; + + if mem_size > below4g_size { + let above4g_ram = Region::init_alias_region( + vm_ram.clone(), + below4g_size, + mem_size - below4g_size, + "above4g_ram", + ); + let above4g_start = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; + sys_mem.root().add_subregion(above4g_ram, above4g_start)?; + } + + Ok(()) + } + + fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_irq_chip() + .with_context(|| MachineError::CrtIrqchipErr) + } + + fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { + use boot_loader::{load_linux, BootLoaderConfig}; + + let boot_source = self.base.boot_source.lock().unwrap(); + let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); + + let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; + let gap_end = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; + let bootloader_config = BootLoaderConfig { + kernel: boot_source.kernel_file.clone(), + initrd, + kernel_cmdline: boot_source.kernel_cmdline.to_string(), + cpu_count: self.base.cpu_topo.nrcpus, + gap_range: (gap_start, gap_end - gap_start), + ioapic_addr: MEM_LAYOUT[LayoutEntryType::IoApic as usize].0 as u32, + lapic_addr: MEM_LAYOUT[LayoutEntryType::LocalApic as usize].0 as u32, + ident_tss_range: None, + prot64_mode: true, + }; + let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) + .with_context(|| MachineError::LoadKernErr)?; + + Ok(CPUBootConfig { + prot64_mode: true, + boot_ip: layout.boot_ip, + boot_sp: layout.boot_sp, + boot_selector: layout.boot_selector, + zero_page: layout.zero_page_addr, + code_segment: layout.segments.code_segment, + data_segment: layout.segments.data_segment, + gdt_base: layout.segments.gdt_base, + gdt_size: layout.segments.gdt_limit, + idt_base: layout.segments.idt_base, + idt_size: layout.segments.idt_limit, + pml4_start: layout.boot_pml4_addr, + }) + } + + fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { + use devices::legacy::{Serial, SERIAL_ADDR}; + + let region_base: u64 = SERIAL_ADDR; + let region_size: u64 = 8; + let serial = Serial::new(config.clone()); + serial + .realize(&mut self.base.sysbus, region_base, region_size) + .with_context(|| "Failed to realize serial device.") + } + + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); + + trace_sysbus(&locked_vm.base.sysbus); + trace_vm_state(&locked_vm.base.vm_state); + + let topology = CPUTopology::new().set_topology(( + vm_config.machine_config.nr_threads, + vm_config.machine_config.nr_cores, + vm_config.machine_config.nr_dies, + )); + trace_cpu_topo(&topology); + locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.init_memory( + &vm_config.machine_config.mem_config, + &locked_vm.base.sys_io, + &locked_vm.base.sys_mem, + vm_config.machine_config.nr_cpus, + )?; + + let migrate_info = locked_vm.get_migrate_info(); + + locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + locked_vm.arch_init(MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0)?; + + // Add mmio devices + locked_vm + .create_replaceable_devices() + .with_context(|| "Failed to create replaceable devices.")?; + locked_vm.add_devices(vm_config)?; + trace_replaceable_info(&locked_vm.replaceable_info); + + let boot_config = if migrate_info.0 == MigrateMode::Unknown { + Some(locked_vm.load_boot_source(None)?) + } else { + None + }; + + locked_vm.base.cpus.extend(::init_vcpu( + vm.clone(), + vm_config.machine_config.nr_cpus, + vm_config.machine_config.max_cpus, + &topology, + &boot_config, + )?); + + MigrationManager::register_vm_instance(vm.clone()); + MigrationManager::register_kvm_instance( + vm_state::KvmDeviceState::descriptor(), + Arc::new(vm_state::KvmDevice {}), + ); + if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { + bail!("Failed to set migration status {}", e); + } + + Ok(()) + } + + fn add_virtio_mmio_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + self.add_virtio_mmio_net(vm_config, cfg_args) + } + + fn add_virtio_mmio_block(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + self.add_virtio_mmio_block(vm_config, cfg_args) + } + + fn realize_virtio_mmio_device( + &mut self, + dev: VirtioMmioDevice, + ) -> Result>> { + self.realize_virtio_mmio_device(dev) + } + + fn syscall_whitelist(&self) -> Vec { + syscall_whitelist() + } +} + +pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { + bpf_rule + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_PIT2() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_CLOCK() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_IRQCHIP() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XSAVE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEBUGREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XCRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_LAPIC() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) +} + +pub(crate) fn arch_syscall_whitelist() -> Vec { + vec![ + #[cfg(not(target_env = "gnu"))] + BpfRule::new(libc::SYS_epoll_pwait), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_epoll_wait), + BpfRule::new(libc::SYS_open), + #[cfg(target_env = "musl")] + BpfRule::new(libc::SYS_stat), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_newfstatat), + BpfRule::new(libc::SYS_unlink), + BpfRule::new(libc::SYS_mkdir), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_readlink), + ] +} diff --git a/machine/src/arch/x86_64/mod.rs b/machine/src/arch/x86_64/mod.rs new file mode 100644 index 000000000..04b85ef67 --- /dev/null +++ b/machine/src/arch/x86_64/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod micro; diff --git a/machine/src/error.rs b/machine/src/error.rs index 60877f852..9c5580db0 100644 --- a/machine/src/error.rs +++ b/machine/src/error.rs @@ -14,6 +14,11 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum MachineError { + #[error("")] + Cpu { + #[from] + source: cpu::error::CpuError, + }, #[error("AddressSpace")] AddressSpace { #[from] @@ -30,21 +35,21 @@ pub enum MachineError { #[from] source: devices::legacy::error::LegacyError, }, - #[error("MicroVm")] - MicroVm { - #[from] - source: super::micro_vm::error::MicroVmError, - }, - #[error("StdVm")] - StdVm { + #[error("")] + PciErr { #[from] - source: super::standard_vm::error::StandardVmError, + source: devices::pci::error::PciError, }, #[error("Util")] Util { #[from] source: util::error::UtilError, }, + #[error("")] + Acpi { + #[from] + source: acpi::error::AcpiError, + }, #[error("Virtio")] Virtio { #[from] @@ -65,6 +70,8 @@ pub enum MachineError { #[from] source: std::io::Error, }, + #[error("Failed to init PCIe host.")] + InitPCIeHostErr, #[error("Failed to add {0} device.")] AddDevErr(String), #[error("Failed to load kernel.")] @@ -107,4 +114,16 @@ pub enum MachineError { ResumeVcpuErr(u8), #[error("Failed to destroy vcpu{0}.")] DestroyVcpuErr(u8), + #[error("A maximum of {0} {1} replaceable devices are supported.")] + RplDevLmtErr(String, usize), + #[error("The device type is {0}, but the target config is not for this type.")] + DevTypeErr(String), + #[error("{0}: failed to update config.")] + UpdCfgErr(String), + #[error("Failed to open file: {0}.")] + OpenFileErr(String), + #[error("Failed to init pflash device.")] + InitPflashErr, + #[error("Failed to realize pflash device.")] + RlzPflashErr, } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e1700b509..e85655c0b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,19 +10,20 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod arch; pub mod error; pub mod standard_vm; #[cfg(target_arch = "aarch64")] mod fdt; -mod micro_vm; +mod micro_common; #[cfg(target_arch = "x86_64")] mod vm_state; pub use anyhow::Result; pub use crate::error::MachineError; -pub use micro_vm::LightMachine; +pub use micro_common::LightMachine; pub use standard_vm::StdMachine; use std::collections::{BTreeMap, HashMap}; @@ -547,10 +548,14 @@ pub trait MachineOps { fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()>; /// Add RTC device. - fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] mem_size: u64) -> Result<()>; + fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] _mem_size: u64) -> Result<()> { + Ok(()) + } /// Add Generic event device. - fn add_ged_device(&mut self) -> Result<()>; + fn add_ged_device(&mut self) -> Result<()> { + Ok(()) + } /// Add serial device. /// diff --git a/machine/src/micro_vm/mod.rs b/machine/src/micro_common/mod.rs similarity index 66% rename from machine/src/micro_vm/mod.rs rename to machine/src/micro_common/mod.rs index e6c3e2cd0..ae33c62c9 100644 --- a/machine/src/micro_vm/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -28,12 +28,7 @@ //! - `x86_64` //! - `aarch64` -pub mod error; - -mod mem_layout; -mod syscall; - -pub use error::MicroVmError; +pub mod syscall; use std::fmt; use std::fmt::Debug; @@ -45,27 +40,16 @@ use std::vec::Vec; use anyhow::{anyhow, bail, Context, Result}; use log::{error, info}; -use super::Result as MachineResult; -use super::{error::MachineError, MachineOps}; -#[cfg(target_arch = "x86_64")] -use crate::vm_state; -use crate::MachineBase; -use address_space::{AddressSpace, GuestAddress, Region}; +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::micro::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] -use cpu::CPUBootConfig; +use crate::arch::x86_64::micro::{LayoutEntryType, MEM_LAYOUT}; +use crate::{MachineBase, MachineError, MachineOps}; use cpu::{CPUTopology, CpuLifecycleState}; -#[cfg(target_arch = "x86_64")] -use devices::legacy::FwCfgOps; -#[cfg(target_arch = "aarch64")] -use devices::legacy::PL031; use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; -#[cfg(target_arch = "aarch64")] -use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX}; -#[cfg(target_arch = "x86_64")] -use hypervisor::kvm::KVM_FDS; use machine_manager::config::{ parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, ConfigCheck, DiskFormat, MigrateMode, - NetworkInterfaceConfig, SerialConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, + NetworkInterfaceConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -76,15 +60,9 @@ use machine_manager::machine::{ use machine_manager::qmp::{ qmp_channel::QmpChannel, qmp_response::Response, qmp_schema, qmp_schema::UpdateRegionArgument, }; -use mem_layout::{LayoutEntryType, MEM_LAYOUT}; -use migration::{MigrationManager, MigrationStatus}; -use syscall::syscall_whitelist; +use migration::MigrationManager; use util::aio::WriteZeroesState; -#[cfg(target_arch = "aarch64")] -use util::device_tree::{self, CompileFDT, FdtBuilder}; -use util::{ - loop_context::EventLoopManager, num_ops::str_to_usize, seccomp::BpfRule, set_termi_canon_mode, -}; +use util::{loop_context::EventLoopManager, num_ops::str_to_usize, set_termi_canon_mode}; use virtio::{ create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, @@ -126,15 +104,15 @@ impl fmt::Debug for MmioReplaceableDevInfo { // The gather of config, info and count of all replaceable devices. #[derive(Debug)] -struct MmioReplaceableInfo { +pub(crate) struct MmioReplaceableInfo { // The arrays of all replaceable configs. configs: Arc>>, // The arrays of all replaceable device information. devices: Arc>>, // The count of block device which is plugin. - block_count: usize, + pub(crate) block_count: usize, // The count of network device which is plugin. - net_count: usize, + pub(crate) net_count: usize, } impl MmioReplaceableInfo { @@ -151,9 +129,9 @@ impl MmioReplaceableInfo { /// A wrapper around creating and using a kvm-based micro VM. pub struct LightMachine { // Machine base members. - base: MachineBase, + pub(crate) base: MachineBase, // All replaceable device information. - replaceable_info: MmioReplaceableInfo, + pub(crate) replaceable_info: MmioReplaceableInfo, } impl LightMachine { @@ -162,7 +140,7 @@ impl LightMachine { /// # Arguments /// /// * `vm_config` - Represents the configuration for VM. - pub fn new(vm_config: &VmConfig) -> MachineResult { + pub fn new(vm_config: &VmConfig) -> Result { let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = ( MEM_LAYOUT[LayoutEntryType::Mmio as usize].0, @@ -176,7 +154,7 @@ impl LightMachine { }) } - fn create_replaceable_devices(&mut self) -> Result<()> { + pub(crate) fn create_replaceable_devices(&mut self) -> Result<()> { let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { let block = Arc::new(Mutex::new(Block::new( @@ -227,7 +205,7 @@ impl LightMachine { #[cfg(target_arch = "x86_64")] &self.base.boot_source, ) - .with_context(|| MicroVmError::RlzVirtioMmioErr)?, + .with_context(|| MachineError::RlzVirtioMmioErr)?, &id.to_string(), ); region_base += region_size; @@ -236,7 +214,7 @@ impl LightMachine { Ok(()) } - fn fill_replaceable_device( + pub(crate) fn fill_replaceable_device( &mut self, id: &str, dev_config: Arc, @@ -255,18 +233,17 @@ impl LightMachine { .lock() .unwrap() .update_config(Some(dev_config.clone())) - .with_context(|| MicroVmError::UpdCfgErr(id.to_string()))?; + .with_context(|| MachineError::UpdCfgErr(id.to_string()))?; } - self.add_replaceable_config(id, dev_config)?; - Ok(()) + self.add_replaceable_config(id, dev_config) } fn add_replaceable_config(&self, id: &str, dev_config: Arc) -> Result<()> { let mut configs_lock = self.replaceable_info.configs.lock().unwrap(); let limit = MMIO_REPLACEABLE_BLK_NR + MMIO_REPLACEABLE_NET_NR; if configs_lock.len() >= limit { - return Err(anyhow!(MicroVmError::RplDevLmtErr("".to_string(), limit))); + return Err(anyhow!(MachineError::RplDevLmtErr("".to_string(), limit))); } for config in configs_lock.iter() { @@ -302,24 +279,24 @@ impl LightMachine { let cfg_any = dev_config.as_ref().unwrap().as_any(); let index = if driver.contains("net") { if slot >= MMIO_REPLACEABLE_NET_NR { - return Err(anyhow!(MicroVmError::RplDevLmtErr( + return Err(anyhow!(MachineError::RplDevLmtErr( "net".to_string(), MMIO_REPLACEABLE_NET_NR ))); } if cfg_any.downcast_ref::().is_none() { - return Err(anyhow!(MicroVmError::DevTypeErr("net".to_string()))); + return Err(anyhow!(MachineError::DevTypeErr("net".to_string()))); } slot + MMIO_REPLACEABLE_BLK_NR } else if driver.contains("blk") { if slot >= MMIO_REPLACEABLE_BLK_NR { - return Err(anyhow!(MicroVmError::RplDevLmtErr( + return Err(anyhow!(MachineError::RplDevLmtErr( "block".to_string(), MMIO_REPLACEABLE_BLK_NR ))); } if cfg_any.downcast_ref::().is_none() { - return Err(anyhow!(MicroVmError::DevTypeErr("blk".to_string()))); + return Err(anyhow!(MachineError::DevTypeErr("blk".to_string()))); } slot } else { @@ -340,7 +317,7 @@ impl LightMachine { .lock() .unwrap() .update_config(dev_config) - .with_context(|| MicroVmError::UpdCfgErr(id.to_string()))?; + .with_context(|| MachineError::UpdCfgErr(id.to_string()))?; } Ok(()) } @@ -371,7 +348,7 @@ impl LightMachine { .lock() .unwrap() .update_config(None) - .with_context(|| MicroVmError::UpdCfgErr(id.to_string()))?; + .with_context(|| MachineError::UpdCfgErr(id.to_string()))?; } } @@ -380,229 +357,12 @@ impl LightMachine { } Ok(id.to_string()) } -} - -impl MachineOps for LightMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - - fn machine_base_mut(&mut self) -> &mut MachineBase { - &mut self.base - } - - fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { - let vm_ram = self.get_vm_ram(); - - #[cfg(target_arch = "aarch64")] - { - let layout_size = MEM_LAYOUT[LayoutEntryType::Mem as usize].1; - let ram = Region::init_alias_region( - vm_ram.clone(), - 0, - std::cmp::min(layout_size, mem_size), - "pc_ram", - ); - sys_mem - .root() - .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?; - } - #[cfg(target_arch = "x86_64")] - { - let below4g_size = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; - - let below4g_ram = Region::init_alias_region( - vm_ram.clone(), - 0, - std::cmp::min(below4g_size, mem_size), - "below4g_ram", - ); - sys_mem.root().add_subregion( - below4g_ram, - MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, - )?; - - if mem_size > below4g_size { - let above4g_ram = Region::init_alias_region( - vm_ram.clone(), - below4g_size, - mem_size - below4g_size, - "above4g_ram", - ); - let above4g_start = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; - sys_mem.root().add_subregion(above4g_ram, above4g_start)?; - } - } - Ok(()) - } - - #[cfg(target_arch = "x86_64")] - fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> MachineResult<()> { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_irq_chip() - .with_context(|| MachineError::CrtIrqchipErr)?; - Ok(()) - } - - #[cfg(target_arch = "aarch64")] - fn init_interrupt_controller(&mut self, vcpu_count: u64) -> MachineResult<()> { - // Interrupt Controller Chip init - let v3 = ICGICv3Config { - msi: true, - dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], - redist_region_ranges: vec![ - MEM_LAYOUT[LayoutEntryType::GicRedist as usize], - MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize], - ], - its_range: Some(MEM_LAYOUT[LayoutEntryType::GicIts as usize]), - }; - let v2 = ICGICv2Config { - dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], - cpu_range: MEM_LAYOUT[LayoutEntryType::GicCpu as usize], - v2m_range: None, - sys_mem: None, - }; - // Passing both v2 and v3, leave GIC self to decide which one to use. - let intc_conf = ICGICConfig { - version: None, - vcpu_count, - max_irq: GIC_IRQ_MAX, - v3: Some(v3), - v2: Some(v2), - }; - let irq_chip = InterruptController::new(&intc_conf)?; - self.base.irq_chip = Some(Arc::new(irq_chip)); - self.base.irq_chip.as_ref().unwrap().realize()?; - Ok(()) - } - - #[cfg(target_arch = "x86_64")] - fn load_boot_source( - &self, - fwcfg: Option<&Arc>>, - ) -> MachineResult { - use boot_loader::{load_linux, BootLoaderConfig}; - - let boot_source = self.base.boot_source.lock().unwrap(); - let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); - - let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 - + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; - let gap_end = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; - let bootloader_config = BootLoaderConfig { - kernel: boot_source.kernel_file.clone(), - initrd, - kernel_cmdline: boot_source.kernel_cmdline.to_string(), - cpu_count: self.base.cpu_topo.nrcpus, - gap_range: (gap_start, gap_end - gap_start), - ioapic_addr: MEM_LAYOUT[LayoutEntryType::IoApic as usize].0 as u32, - lapic_addr: MEM_LAYOUT[LayoutEntryType::LocalApic as usize].0 as u32, - ident_tss_range: None, - prot64_mode: true, - }; - let layout = load_linux(&bootloader_config, &self.base.sys_mem, fwcfg) - .with_context(|| MachineError::LoadKernErr)?; - - Ok(CPUBootConfig { - prot64_mode: true, - boot_ip: layout.boot_ip, - boot_sp: layout.boot_sp, - boot_selector: layout.boot_selector, - zero_page: layout.zero_page_addr, - code_segment: layout.segments.code_segment, - data_segment: layout.segments.data_segment, - gdt_base: layout.segments.gdt_base, - gdt_size: layout.segments.gdt_limit, - idt_base: layout.segments.idt_base, - idt_size: layout.segments.idt_limit, - pml4_start: layout.boot_pml4_addr, - }) - } - - fn realize_virtio_mmio_device( - &mut self, - dev: VirtioMmioDevice, - ) -> MachineResult>> { - let region_base = self.base.sysbus.min_free_base; - let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; - let realized_virtio_mmio_device = VirtioMmioDevice::realize( - dev, - &mut self.base.sysbus, - region_base, - region_size, - #[cfg(target_arch = "x86_64")] - &self.base.boot_source, - ) - .with_context(|| MicroVmError::RlzVirtioMmioErr)?; - self.base.sysbus.min_free_base += region_size; - Ok(realized_virtio_mmio_device) - } - - #[cfg(target_arch = "aarch64")] - fn add_rtc_device(&mut self) -> MachineResult<()> { - PL031::realize( - PL031::default(), - &mut self.base.sysbus, - MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, - MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, - ) - .with_context(|| "Failed to realize pl031.")?; - Ok(()) - } - - #[cfg(target_arch = "x86_64")] - fn add_rtc_device(&mut self, _mem_size: u64) -> MachineResult<()> { - Ok(()) - } - - fn add_ged_device(&mut self) -> MachineResult<()> { - Ok(()) - } - - #[cfg(target_arch = "aarch64")] - fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { - use devices::legacy::PL011; - - let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; - let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; - - let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; - let base = self.machine_base_mut(); - pl011 - .realize( - &mut base.sysbus, - region_base, - region_size, - &base.boot_source, - ) - .with_context(|| "Failed to realize PL011") - } - - #[cfg(target_arch = "x86_64")] - fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { - use devices::legacy::{Serial, SERIAL_ADDR}; - - let region_base: u64 = SERIAL_ADDR; - let region_size: u64 = 8; - let serial = Serial::new(config.clone()); - serial - .realize( - &mut self.machine_base_mut().sysbus, - region_base, - region_size, - ) - .with_context(|| "Failed to realize serial device.") - } - fn add_virtio_mmio_net( + pub(crate) fn add_virtio_mmio_net( &mut self, vm_config: &mut VmConfig, cfg_args: &str, - ) -> MachineResult<()> { + ) -> Result<()> { let device_cfg = parse_net(vm_config, cfg_args)?; if device_cfg.vhost_type.is_some() { let device = if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { @@ -633,11 +393,11 @@ impl MachineOps for LightMachine { Ok(()) } - fn add_virtio_mmio_block( + pub(crate) fn add_virtio_mmio_block( &mut self, vm_config: &mut VmConfig, cfg_args: &str, - ) -> MachineResult<()> { + ) -> Result<()> { let device_cfg = parse_blk(vm_config, cfg_args, None)?; if self.replaceable_info.block_count >= MMIO_REPLACEABLE_BLK_NR { bail!( @@ -651,125 +411,23 @@ impl MachineOps for LightMachine { Ok(()) } - fn syscall_whitelist(&self) -> Vec { - syscall_whitelist() - } - - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> MachineResult<()> { - let mut locked_vm = vm.lock().unwrap(); - - // trace for lightmachine - trace_sysbus(&locked_vm.base.sysbus); - trace_vm_state(&locked_vm.base.vm_state); - - let topology = CPUTopology::new().set_topology(( - vm_config.machine_config.nr_threads, - vm_config.machine_config.nr_cores, - vm_config.machine_config.nr_dies, - )); - trace_cpu_topo(&topology); - locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; - locked_vm.init_memory( - &vm_config.machine_config.mem_config, + pub(crate) fn realize_virtio_mmio_device( + &mut self, + dev: VirtioMmioDevice, + ) -> Result>> { + let region_base = self.base.sysbus.min_free_base; + let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; + let realized_virtio_mmio_device = VirtioMmioDevice::realize( + dev, + &mut self.base.sysbus, + region_base, + region_size, #[cfg(target_arch = "x86_64")] - &locked_vm.base.sys_io, - &locked_vm.base.sys_mem, - vm_config.machine_config.nr_cpus, - )?; - - let migrate_info = locked_vm.get_migrate_info(); - - #[cfg(target_arch = "x86_64")] - { - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; - locked_vm.arch_init(MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0)?; - - // Add mmio devices - locked_vm - .create_replaceable_devices() - .with_context(|| "Failed to create replaceable devices.")?; - locked_vm.add_devices(vm_config)?; - trace_replaceable_info(&locked_vm.replaceable_info); - - let boot_config = if migrate_info.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(None)?) - } else { - None - }; - - // vCPUs init - locked_vm.base.cpus.extend(::init_vcpu( - vm.clone(), - vm_config.machine_config.nr_cpus, - vm_config.machine_config.max_cpus, - &topology, - &boot_config, - )?); - } - - #[cfg(target_arch = "aarch64")] - { - let (boot_config, cpu_config) = if migrate_info.0 == MigrateMode::Unknown { - ( - Some( - locked_vm - .load_boot_source(None, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?, - ), - Some(locked_vm.load_cpu_features(vm_config)?), - ) - } else { - (None, None) - }; - - // vCPUs init,and apply CPU features (for aarch64) - locked_vm.base.cpus.extend(::init_vcpu( - vm.clone(), - vm_config.machine_config.nr_cpus, - &topology, - &boot_config, - &cpu_config, - )?); - - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; - - locked_vm.cpu_post_init(&cpu_config)?; - - // Add mmio devices - locked_vm - .create_replaceable_devices() - .with_context(|| "Failed to create replaceable devices.")?; - locked_vm.add_devices(vm_config)?; - trace_replaceable_info(&locked_vm.replaceable_info); - - if let Some(boot_cfg) = boot_config { - let mut fdt_helper = FdtBuilder::new(); - locked_vm - .generate_fdt_node(&mut fdt_helper) - .with_context(|| MachineError::GenFdtErr)?; - let fdt_vec = fdt_helper.finish()?; - locked_vm - .base - .sys_mem - .write( - &mut fdt_vec.as_slice(), - GuestAddress(boot_cfg.fdt_addr), - fdt_vec.len() as u64, - ) - .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; - } - } - - MigrationManager::register_vm_instance(vm.clone()); - #[cfg(target_arch = "x86_64")] - MigrationManager::register_kvm_instance( - vm_state::KvmDeviceState::descriptor(), - Arc::new(vm_state::KvmDevice {}), - ); - if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { - bail!("Failed to set migration status {}", e); - } - - Ok(()) + &self.base.boot_source, + ) + .with_context(|| MachineError::RlzVirtioMmioErr)?; + self.base.sysbus.min_free_base += region_size; + Ok(realized_virtio_mmio_device) } } @@ -842,6 +500,8 @@ impl MachineAddressInterface for LightMachine { #[cfg(target_arch = "x86_64")] fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { + use address_space::GuestAddress; + let count = data.len() as u64; self.base .sys_io @@ -1291,77 +951,20 @@ impl EventLoopManager for LightMachine { } } -/// Trait that helps to generate all nodes in device-tree. -#[allow(clippy::upper_case_acronyms)] -#[cfg(target_arch = "aarch64")] -trait CompileFDTHelper { - /// Function that helps to generate memory nodes. - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; - /// Function that helps to generate the chosen node. - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; -} - -#[cfg(target_arch = "aarch64")] -impl CompileFDTHelper for LightMachine { - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.base.sys_mem.memory_end_address().raw_value() - - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let node = "memory"; - let memory_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; - fdt.end_node(memory_node_dep) - } - - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - let node = "chosen"; - let boot_source = self.base.boot_source.lock().unwrap(); - - let chosen_node_dep = fdt.begin_node(node)?; - let cmdline = &boot_source.kernel_cmdline.to_string(); - fdt.set_property_string("bootargs", cmdline.as_str())?; - - let pl011_property_string = - format!("/pl011@{:x}", MEM_LAYOUT[LayoutEntryType::Uart as usize].0); - fdt.set_property_string("stdout-path", &pl011_property_string)?; - - match &boot_source.initrd { - Some(initrd) => { - fdt.set_property_u64("linux,initrd-start", initrd.initrd_addr)?; - fdt.set_property_u64("linux,initrd-end", initrd.initrd_addr + initrd.initrd_size)?; - } - None => {} - } - fdt.end_node(chosen_node_dep) - } -} - -#[cfg(target_arch = "aarch64")] -impl device_tree::CompileFDT for LightMachine { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { - let node_dep = fdt.begin_node("")?; - self.base.generate_fdt_node(fdt)?; - self.generate_memory_node(fdt)?; - self.generate_chosen_node(fdt)?; - fdt.end_node(node_dep) - } -} - /// Trace descriptions for some devices at stratovirt startup. -fn trace_cpu_topo(cpu_topo: &CPUTopology) { +pub(crate) fn trace_cpu_topo(cpu_topo: &CPUTopology) { util::ftrace!(trace_cpu_topo, "{:#?}", cpu_topo); } -fn trace_sysbus(sysbus: &SysBus) { +pub(crate) fn trace_sysbus(sysbus: &SysBus) { util::ftrace!(trace_sysbus, "{:?}", sysbus); } -fn trace_replaceable_info(replaceable_info: &MmioReplaceableInfo) { +pub(crate) fn trace_replaceable_info(replaceable_info: &MmioReplaceableInfo) { util::ftrace!(trace_replaceable_info, "{:?}", replaceable_info); } -fn trace_vm_state(vm_state: &Arc<(Mutex, Condvar)>) { +pub(crate) fn trace_vm_state(vm_state: &Arc<(Mutex, Condvar)>) { util::ftrace!(trace_vm_state, "{:#?}", vm_state); } diff --git a/machine/src/micro_vm/syscall.rs b/machine/src/micro_common/syscall.rs similarity index 77% rename from machine/src/micro_vm/syscall.rs rename to machine/src/micro_common/syscall.rs index f3bf3553b..a5f80dca2 100644 --- a/machine/src/micro_vm/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -10,6 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; @@ -41,23 +45,15 @@ const KVM_RUN: u32 = 0xae80; /// Create a syscall whitelist for seccomp. /// /// # Notes -/// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 60 syscalls -/// * x86_64-unknown-musl: 58 syscalls -/// * aarch64-unknown-gnu: 58 syscalls -/// * aarch64-unknown-musl: 57 syscalls +/// /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { - vec![ + let mut syscall = vec![ BpfRule::new(libc::SYS_read), BpfRule::new(libc::SYS_readv), BpfRule::new(libc::SYS_write), BpfRule::new(libc::SYS_writev), ioctl_allow_list(), - #[cfg(not(all(target_env = "gnu", target_arch = "x86_64")))] - BpfRule::new(libc::SYS_epoll_pwait), - #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] - BpfRule::new(libc::SYS_epoll_wait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), BpfRule::new(libc::SYS_io_destroy), @@ -83,8 +79,6 @@ pub fn syscall_whitelist() -> Vec { .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETLK as u32), BpfRule::new(libc::SYS_flock), BpfRule::new(libc::SYS_rt_sigprocmask), - #[cfg(target_arch = "x86_64")] - BpfRule::new(libc::SYS_open), BpfRule::new(libc::SYS_openat), BpfRule::new(libc::SYS_sigaltstack), BpfRule::new(libc::SYS_mmap), @@ -107,23 +101,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_pwrite64), BpfRule::new(libc::SYS_pwritev), BpfRule::new(libc::SYS_statx), - #[cfg(all(target_env = "musl", target_arch = "x86_64"))] - BpfRule::new(libc::SYS_stat), - #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] - BpfRule::new(libc::SYS_newfstatat), - #[cfg(target_arch = "aarch64")] - BpfRule::new(libc::SYS_newfstatat), - #[cfg(target_arch = "x86_64")] - BpfRule::new(libc::SYS_unlink), - #[cfg(target_arch = "aarch64")] - BpfRule::new(libc::SYS_unlinkat), BpfRule::new(libc::SYS_renameat), - #[cfg(target_arch = "x86_64")] - BpfRule::new(libc::SYS_mkdir), - #[cfg(target_arch = "aarch64")] - BpfRule::new(libc::SYS_mkdirat), - #[cfg(all(target_env = "gnu", target_arch = "x86_64"))] - BpfRule::new(libc::SYS_readlink), BpfRule::new(libc::SYS_getrandom), BpfRule::new(libc::SYS_fallocate), BpfRule::new(libc::SYS_socket), @@ -133,7 +111,10 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_clock_gettime), madvise_rule(), - ] + ]; + syscall.append(&mut arch_syscall_whitelist()); + + syscall } /// Create a syscall bpf rule for syscall `ioctl`. @@ -166,31 +147,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32); - ioctl_arch_allow_list(bpf_rule) -} - -#[cfg(target_arch = "x86_64")] -fn ioctl_arch_allow_list(bpf_rule: BpfRule) -> BpfRule { - bpf_rule - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_PIT2() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_CLOCK() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_IRQCHIP() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XSAVE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEBUGREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XCRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_LAPIC() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) -} - -#[cfg(target_arch = "aarch64")] -fn ioctl_arch_allow_list(bpf_rule: BpfRule) -> BpfRule { - bpf_rule - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_ONE_REG() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEVICE_ATTR() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) + arch_ioctl_allow_list(bpf_rule) } fn madvise_rule() -> BpfRule { diff --git a/machine/src/micro_vm/mem_layout.rs b/machine/src/micro_vm/mem_layout.rs deleted file mode 100644 index 3f26013e7..000000000 --- a/machine/src/micro_vm/mem_layout.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -/// The type of memory layout entry on aarch64 -#[cfg(target_arch = "aarch64")] -#[repr(usize)] -pub enum LayoutEntryType { - GicDist, - GicCpu, - GicIts, - GicRedist, - Uart, - Rtc, - Mmio, - Mem, - HighGicRedist, -} - -/// Layout of aarch64 -#[cfg(target_arch = "aarch64")] -pub const MEM_LAYOUT: &[(u64, u64)] = &[ - (0x0800_0000, 0x0001_0000), // GicDist - (0x0801_0000, 0x0001_0000), // GicCpu - (0x0808_0000, 0x0002_0000), // GicIts - (0x080A_0000, 0x00F6_0000), // GicRedist (max 123 redistributors) - (0x0900_0000, 0x0000_1000), // Uart - (0x0901_0000, 0x0000_1000), // Rtc - (0x0A00_0000, 0x0000_0200), // Mmio - (0x4000_0000, 0x80_0000_0000), // Mem - (256 << 30, 0x200_0000), // HighGicRedist, (where remaining redistributors locates) -]; - -/// The type of memory layout entry on x86_64 -#[cfg(target_arch = "x86_64")] -#[repr(usize)] -pub enum LayoutEntryType { - MemBelow4g = 0_usize, - Mmio, - IoApic, - LocalApic, - IdentTss, - MemAbove4g, -} - -/// Layout of x86_64 -#[cfg(target_arch = "x86_64")] -pub const MEM_LAYOUT: &[(u64, u64)] = &[ - (0, 0xC000_0000), // MemBelow4g - (0xF010_0000, 0x200), // Mmio - (0xFEC0_0000, 0x10_0000), // IoApic - (0xFEE0_0000, 0x10_0000), // LocalApic - (0xFEF0_C000, 0x4000), // Identity map address and TSS - (0x1_0000_0000, 0x80_0000_0000), // MemAbove4g -]; diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/standard_vm/aarch64/mod.rs index 4aca55d72..1bc79904a 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/standard_vm/aarch64/mod.rs @@ -538,8 +538,6 @@ impl MachineOps for StdMachine { } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { - use super::error::StandardVmError as StdErrorKind; - let nr_cpus = vm_config.machine_config.nr_cpus; let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; @@ -565,7 +563,7 @@ impl MachineOps for StdMachine { locked_vm .init_pci_host() - .with_context(|| StdErrorKind::InitPCIeHostErr)?; + .with_context(|| MachineError::InitPCIeHostErr)?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; let migrate = locked_vm.get_migrate_info(); @@ -658,7 +656,6 @@ impl MachineOps for StdMachine { } fn add_pflash_device(&mut self, configs: &[PFlashConfig]) -> Result<()> { - use super::error::StandardVmError as StdErrorKind; let mut configs_vec = configs.to_vec(); configs_vec.sort_by_key(|c| c.unit); let sector_len: u32 = 1024 * 256; @@ -675,9 +672,9 @@ impl MachineOps for StdMachine { }; let pflash = PFlash::new(flash_size, &fd, sector_len, 4, 2, read_only) - .with_context(|| StdErrorKind::InitPflashErr)?; + .with_context(|| MachineError::InitPflashErr)?; PFlash::realize(pflash, &mut self.base.sysbus, flash_base, flash_size, fd) - .with_context(|| StdErrorKind::RlzPflashErr)?; + .with_context(|| MachineError::RlzPflashErr)?; flash_base += flash_size; } diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index de5fb9d2a..d37983576 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -12,16 +12,14 @@ #[cfg(target_arch = "aarch64")] pub mod aarch64; -pub mod error; - #[cfg(target_arch = "x86_64")] pub mod x86_64; pub use anyhow::Result; +pub use crate::error::MachineError; #[cfg(target_arch = "aarch64")] pub use aarch64::StdMachine; -pub use error::StandardVmError; #[cfg(target_arch = "x86_64")] pub use x86_64::StdMachine; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/standard_vm/x86_64/mod.rs index af8c270de..b4c4b65d4 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/standard_vm/x86_64/mod.rs @@ -24,7 +24,6 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use vmm_sys_util::eventfd::EventFd; -use super::error::StandardVmError; use super::{AcpiBuilder, StdMachineOps}; use crate::error::MachineError; use crate::{vm_state, MachineBase, MachineOps}; @@ -549,7 +548,7 @@ impl MachineOps for StdMachine { locked_vm .init_pci_host() - .with_context(|| StandardVmError::InitPCIeHostErr)?; + .with_context(|| MachineError::InitPCIeHostErr)?; locked_vm .init_ich9_lpc(clone_vm) .with_context(|| "Fail to init LPC bridge")?; @@ -680,7 +679,7 @@ impl MachineOps for StdMachine { 1_u32, config.read_only, ) - .with_context(|| StandardVmError::InitPflashErr)?; + .with_context(|| MachineError::InitPflashErr)?; PFlash::realize( pflash, &mut self.base.sysbus, @@ -688,7 +687,7 @@ impl MachineOps for StdMachine { pfl_size, backend, ) - .with_context(|| StandardVmError::RlzPflashErr)?; + .with_context(|| MachineError::RlzPflashErr)?; flash_end -= pfl_size; } -- Gitee From 52ad482e582d7e6fa71a5c7cb2afe1168d797d7c Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 7 Dec 2023 17:33:58 +0800 Subject: [PATCH 1498/2187] machine: split the standard machine into architecture-related Signed-off-by: Mingwang Li --- docs/config_guidebook.md | 13 - machine/src/{ => arch/aarch64}/fdt.rs | 0 machine/src/arch/aarch64/mod.rs | 4 + .../aarch64/pci_host_root.rs | 0 .../mod.rs => arch/aarch64/standard.rs} | 101 ++--- .../{standard_vm => arch}/x86_64/ich9_lpc.rs | 5 +- .../src/{standard_vm => arch}/x86_64/mch.rs | 2 +- machine/src/arch/x86_64/mod.rs | 4 + .../x86_64/mod.rs => arch/x86_64/standard.rs} | 123 ++++--- machine/src/lib.rs | 10 +- .../{standard_vm => standard_common}/mod.rs | 88 +++-- .../aarch64 => standard_common}/syscall.rs | 45 +-- machine/src/standard_vm/x86_64/syscall.rs | 345 ------------------ tests/mod_test/src/libdriver/fwcfg.rs | 2 +- tests/mod_test/src/libdriver/pci_bus.rs | 4 +- tests/mod_test/tests/aarch64/acpi_test.rs | 2 +- tests/mod_test/tests/aarch64/ged_test.rs | 2 +- 17 files changed, 214 insertions(+), 536 deletions(-) rename machine/src/{ => arch/aarch64}/fdt.rs (100%) rename machine/src/{standard_vm => arch}/aarch64/pci_host_root.rs (100%) rename machine/src/{standard_vm/aarch64/mod.rs => arch/aarch64/standard.rs} (95%) rename machine/src/{standard_vm => arch}/x86_64/ich9_lpc.rs (99%) rename machine/src/{standard_vm => arch}/x86_64/mch.rs (99%) rename machine/src/{standard_vm/x86_64/mod.rs => arch/x86_64/standard.rs} (91%) rename machine/src/{standard_vm => standard_common}/mod.rs (98%) rename machine/src/{standard_vm/aarch64 => standard_common}/syscall.rs (91%) delete mode 100644 machine/src/standard_vm/x86_64/syscall.rs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 4ebd70a10..b0c45e379 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1160,19 +1160,6 @@ One property can be set: StratoVirt use [seccomp(2)](https://man7.org/linux/man-pages/man2/seccomp.2.html) to limit the syscalls in StratoVirt process by default. It will make a slight influence on performance to StratoVirt. -* x86_64 - -| Number of Syscalls | GNU Toolchain | MUSL Toolchain | -| :----------------: | :-----------: | :------------: | -| microvm | 51 | 50 | -| q35 | 85 | 65 | - -* aarch64 - -| Number of Syscalls | GNU Toolchain | MUSL Toolchain | -| :----------------: | :-----------: | :------------: | -| microvm | 49 | 49 | -| virt | 84 | 62 | If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. ```shell diff --git a/machine/src/fdt.rs b/machine/src/arch/aarch64/fdt.rs similarity index 100% rename from machine/src/fdt.rs rename to machine/src/arch/aarch64/fdt.rs diff --git a/machine/src/arch/aarch64/mod.rs b/machine/src/arch/aarch64/mod.rs index 04b85ef67..ee107ad49 100644 --- a/machine/src/arch/aarch64/mod.rs +++ b/machine/src/arch/aarch64/mod.rs @@ -11,3 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod micro; +pub mod standard; + +mod fdt; +mod pci_host_root; diff --git a/machine/src/standard_vm/aarch64/pci_host_root.rs b/machine/src/arch/aarch64/pci_host_root.rs similarity index 100% rename from machine/src/standard_vm/aarch64/pci_host_root.rs rename to machine/src/arch/aarch64/pci_host_root.rs diff --git a/machine/src/standard_vm/aarch64/mod.rs b/machine/src/arch/aarch64/standard.rs similarity index 95% rename from machine/src/standard_vm/aarch64/mod.rs rename to machine/src/arch/aarch64/standard.rs index 1bc79904a..007d057a3 100644 --- a/machine/src/standard_vm/aarch64/mod.rs +++ b/machine/src/arch/aarch64/standard.rs @@ -10,9 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod pci_host_root; -mod syscall; - pub use crate::error::MachineError; use std::mem::size_of; @@ -24,7 +21,7 @@ use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; use log::{error, info, warn}; use vmm_sys_util::eventfd::EventFd; -use super::{AcpiBuilder, Result as StdResult, StdMachineOps}; +use crate::standard_common::{AcpiBuilder, StdMachineOps}; use crate::{MachineBase, MachineOps}; use acpi::{ processor_append_priv_res, AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, @@ -41,6 +38,8 @@ use acpi::{ use address_space::{AddressSpace, GuestAddress, Region}; use cpu::{CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE}; +use super::pci_host_root::PciHostRoot; +use crate::standard_common::syscall::syscall_whitelist; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged, GedEvent}; use devices::acpi::power::PowerDev; #[cfg(feature = "ramfb")] @@ -51,7 +50,7 @@ use devices::legacy::{ use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use devices::sysbus::SysBusDevType; use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; -use hypervisor::kvm::KVM_FDS; +use hypervisor::kvm::*; #[cfg(feature = "ramfb")] use machine_manager::config::parse_ramfb; use machine_manager::config::ShutdownAction; @@ -63,13 +62,11 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, - MachineLifecycle, MachineTestInterface, MigrateInterface, + KvmVmState, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, + MigrateInterface, }; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; -use pci_host_root::PciHostRoot; -use syscall::syscall_whitelist; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; #[cfg(feature = "vnc")] @@ -77,7 +74,7 @@ use ui::vnc::vnc_init; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::EventLoopManager; -use util::seccomp::BpfRule; +use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::set_termi_canon_mode; /// The type of memory layout entry on aarch64 @@ -333,7 +330,7 @@ impl StdMachine { } impl StdMachineOps for StdMachine { - fn init_pci_host(&self) -> StdResult<()> { + fn init_pci_host(&self) -> Result<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); let mmconfig_region = Region::init_io_region( @@ -353,12 +350,10 @@ impl StdMachineOps for StdMachine { let pcihost_root = PciHostRoot::new(root_bus); pcihost_root .realize() - .with_context(|| "Failed to realize pcihost root device.")?; - - Ok(()) + .with_context(|| "Failed to realize pcihost root device.") } - fn add_fwcfg_device(&mut self, nr_cpus: u8) -> StdResult>>> { + fn add_fwcfg_device(&mut self, nr_cpus: u8) -> Result>>> { if self.base.vm_config.lock().unwrap().pflashs.is_none() { return Ok(None); } @@ -429,8 +424,7 @@ impl MachineOps for StdMachine { ); sys_mem .root() - .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?; - Ok(()) + .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { @@ -489,8 +483,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, ) - .with_context(|| "Failed to realize PL031")?; - Ok(()) + .with_context(|| "Failed to realize PL031") } fn add_ged_device(&mut self) -> Result<()> { @@ -529,8 +522,7 @@ impl MachineOps for StdMachine { region_size, &self.base.boot_source, ) - .with_context(|| "Failed to realize PL011")?; - Ok(()) + .with_context(|| "Failed to realize PL011") } fn syscall_whitelist(&self) -> Vec { @@ -718,11 +710,10 @@ impl MachineOps for StdMachine { let mut ramfb = Ramfb::new(sys_mem.clone(), install); ramfb.ramfb_state.setup(&fwcfg_dev)?; - ramfb.realize(&mut self.base.sysbus)?; - Ok(()) + ramfb.realize(&mut self.base.sysbus) } - fn get_pci_host(&mut self) -> StdResult<&Arc>> { + fn get_pci_host(&mut self) -> Result<&Arc>> { Ok(&self.pci_host) } @@ -731,12 +722,48 @@ impl MachineOps for StdMachine { } } +pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { + bpf_rule + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_ONE_REG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEVICE_ATTR() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32) +} + +pub(crate) fn arch_syscall_whitelist() -> Vec { + vec![ + BpfRule::new(libc::SYS_epoll_pwait), + BpfRule::new(libc::SYS_mkdirat), + BpfRule::new(libc::SYS_unlinkat), + BpfRule::new(libc::SYS_clone), + BpfRule::new(libc::SYS_rt_sigaction), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rseq), + #[cfg(target_env = "gnu")] + BpfRule::new(223), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_listen), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fchmodat), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmctl), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmat), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_shmdt), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_lremovexattr), + ] +} + impl AcpiBuilder for StdMachine { fn build_gtdt_table( &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut gtdt = AcpiTable::new(*b"GTDT", 2, *b"STRATO", *b"VIRTGTDT", 1); gtdt.set_table_len(96); @@ -769,7 +796,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { // Table format described at: // https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/acpi-debug-port-table @@ -852,7 +879,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut iort = AcpiTable::new(*b"IORT", 2, *b"STRATO", *b"VIRTIORT", 1); iort.set_table_len(128); @@ -895,7 +922,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut spcr = AcpiTable::new(*b"SPCR", 2, *b"STRATO", *b"VIRTSPCR", 1); spcr.set_table_len(80); @@ -939,7 +966,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -971,7 +998,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.set_table_len(44); @@ -1072,7 +1099,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); // Reserved srat.append_child(&[1_u8; 4_usize]); @@ -1095,7 +1122,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut pptt = AcpiTable::new(*b"PPTT", 2, *b"STRATO", *b"VIRTPPTT", 1); let mut uid = 0; self.build_pptt_sockets(&mut pptt, &mut uid); @@ -1183,16 +1210,6 @@ impl MachineLifecycle for StdMachine { } } -impl MachineAddressInterface for StdMachine { - fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { - self.machine_base().mmio_read(addr, data) - } - - fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { - self.machine_base().mmio_write(addr, data) - } -} - impl MigrateInterface for StdMachine { fn migrate(&self, uri: String) -> Response { match parse_incoming_uri(&uri) { diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/arch/x86_64/ich9_lpc.rs similarity index 99% rename from machine/src/standard_vm/x86_64/ich9_lpc.rs rename to machine/src/arch/x86_64/ich9_lpc.rs index 3022839c6..5423a16bc 100644 --- a/machine/src/standard_vm/x86_64/ich9_lpc.rs +++ b/machine/src/arch/x86_64/ich9_lpc.rs @@ -15,12 +15,11 @@ use std::sync::{ Arc, Mutex, Weak, }; -use anyhow::Context; +use anyhow::{Context, Result}; use log::error; use vmm_sys_util::eventfd::EventFd; -use super::VENDOR_ID_INTEL; -use crate::standard_vm::Result; +use crate::arch::x86_64::standard::VENDOR_ID_INTEL; use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use devices::pci::config::{ diff --git a/machine/src/standard_vm/x86_64/mch.rs b/machine/src/arch/x86_64/mch.rs similarity index 99% rename from machine/src/standard_vm/x86_64/mch.rs rename to machine/src/arch/x86_64/mch.rs index 104eeacb0..aa00152c9 100644 --- a/machine/src/standard_vm/x86_64/mch.rs +++ b/machine/src/arch/x86_64/mch.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; use log::error; -use super::VENDOR_ID_INTEL; +use crate::arch::x86_64::standard::VENDOR_ID_INTEL; use address_space::{Region, RegionOps}; use devices::pci::{ config::{ diff --git a/machine/src/arch/x86_64/mod.rs b/machine/src/arch/x86_64/mod.rs index 04b85ef67..47b4ecbe1 100644 --- a/machine/src/arch/x86_64/mod.rs +++ b/machine/src/arch/x86_64/mod.rs @@ -10,4 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod ich9_lpc; pub mod micro; +pub mod standard; + +mod mch; diff --git a/machine/src/standard_vm/x86_64/mod.rs b/machine/src/arch/x86_64/standard.rs similarity index 91% rename from machine/src/standard_vm/x86_64/mod.rs rename to machine/src/arch/x86_64/standard.rs index b4c4b65d4..0724c51ac 100644 --- a/machine/src/standard_vm/x86_64/mod.rs +++ b/machine/src/arch/x86_64/standard.rs @@ -10,11 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub(crate) mod ich9_lpc; - -mod mch; -mod syscall; - use std::io::{Seek, SeekFrom}; use std::mem::size_of; use std::ops::Deref; @@ -24,8 +19,11 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use vmm_sys_util::eventfd::EventFd; -use super::{AcpiBuilder, StdMachineOps}; +use super::ich9_lpc; +use super::mch::Mch; use crate::error::MachineError; +use crate::standard_common::syscall::syscall_whitelist; +use crate::standard_common::{AcpiBuilder, StdMachineOps}; use crate::{vm_state, MachineBase, MachineOps}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, @@ -42,7 +40,7 @@ use devices::legacy::{ SERIAL_ADDR, }; use devices::pci::{PciDevOps, PciHost}; -use hypervisor::kvm::KVM_FDS; +use hypervisor::kvm::*; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ @@ -51,22 +49,21 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, - MachineLifecycle, MachineTestInterface, MigrateInterface, + KvmVmState, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, + MigrateInterface, }; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; -use mch::Mch; use migration::{MigrationManager, MigrationStatus}; -use syscall::syscall_whitelist; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; #[cfg(feature = "vnc")] use ui::vnc::vnc_init; +use util::seccomp::SeccompCmpOpt; use util::{ byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, }; -const VENDOR_ID_INTEL: u16 = 0x8086; +pub(crate) const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; const HOLE_640K_END: u64 = 0x0010_0000; @@ -224,8 +221,7 @@ impl StdMachine { .with_context(|| "Fail to register reset event in LPC")?; self.register_shutdown_event(ich.shutdown_req.clone(), clone_vm) .with_context(|| "Fail to register shutdown event in LPC")?; - ich.realize()?; - Ok(()) + ich.realize() } pub fn get_vcpu_reg_val(&self, _addr: u64, _vcpu: usize) -> Option { @@ -234,8 +230,7 @@ impl StdMachine { pub fn handle_hotplug_vcpu_request(vm: &Arc>) -> Result<()> { let mut locked_vm = vm.lock().unwrap(); - locked_vm.add_vcpu_device(vm.clone())?; - Ok(()) + locked_vm.add_vcpu_device(vm.clone()) } fn init_cpu_controller( @@ -309,15 +304,14 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to register CONFIG_DATA port in I/O space.")?; let mch = Mch::new(root_bus, mmconfig_region, mmconfig_region_ops); - mch.realize()?; - Ok(()) + mch.realize() } fn add_fwcfg_device( &mut self, nr_cpus: u8, max_cpus: u8, - ) -> super::Result>>> { + ) -> Result>>> { let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone()); fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, max_cpus.as_bytes().to_vec())?; @@ -377,8 +371,7 @@ impl StdMachineOps for StdMachine { // Trigger GED cpu resize event. self.cpu_resize_req .write(1) - .with_context(|| "Failed to write cpu resize request.")?; - Ok(()) + .with_context(|| "Failed to write cpu resize request.") } fn remove_vcpu_device(&mut self, vcpu_id: u8) -> Result<()> { @@ -391,8 +384,7 @@ impl StdMachineOps for StdMachine { locked_controller.set_hotunplug_cpu(vcpu_id)?; self.cpu_resize_req .write(1) - .with_context(|| "Failed to write cpu resize request.")?; - Ok(()) + .with_context(|| "Failed to write cpu resize request.") } fn find_cpu_id_by_device_id(&mut self, device_id: &str) -> Option { @@ -452,8 +444,7 @@ impl MachineOps for StdMachine { .lock() .unwrap() .init_irq_route_table(); - KVM_FDS.load().commit_irq_routing()?; - Ok(()) + KVM_FDS.load().commit_irq_routing() } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { @@ -493,9 +484,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); - RTC::realize(rtc, &mut self.base.sysbus).with_context(|| "Failed to realize RTC device")?; - - Ok(()) + RTC::realize(rtc, &mut self.base.sysbus).with_context(|| "Failed to realize RTC device") } fn add_ged_device(&mut self) -> Result<()> { @@ -730,12 +719,66 @@ impl MachineOps for StdMachine { } } +pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { + bpf_rule + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_PIT2() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_CLOCK() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_IRQCHIP() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XSAVE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEBUGREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XCRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_LAPIC() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SUPPORTED_CPUID() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_CPUID2() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_REGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XSAVE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XCRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_DEBUGREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) +} + +pub(crate) fn arch_syscall_whitelist() -> Vec { + vec![ + #[cfg(not(target_env = "gnu"))] + BpfRule::new(libc::SYS_epoll_pwait), + BpfRule::new(libc::SYS_epoll_wait), + BpfRule::new(libc::SYS_open), + #[cfg(target_env = "musl")] + BpfRule::new(libc::SYS_stat), + BpfRule::new(libc::SYS_mkdir), + BpfRule::new(libc::SYS_unlink), + BpfRule::new(libc::SYS_readlink), + #[cfg(target_env = "musl")] + BpfRule::new(libc::SYS_clone), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_clone3), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rt_sigaction), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_poll), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_access), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_sched_setattr), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_fadvise64), + ] +} + impl AcpiBuilder for StdMachine { fn build_dsdt_table( &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); // 1. CPU info. @@ -773,7 +816,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut madt = AcpiTable::new(*b"APIC", 5, *b"STRATO", *b"VIRTAPIC", 1); madt.append_child(LAPIC_BASE_ADDR.as_bytes()); @@ -917,7 +960,7 @@ impl AcpiBuilder for StdMachine { &self, acpi_data: &Arc>>, loader: &mut TableLoader, - ) -> super::Result { + ) -> Result { let mut srat = AcpiTable::new(*b"SRAT", 1, *b"STRATO", *b"VIRTSRAT", 1); srat.append_child(&[1_u8; 4_usize]); srat.append_child(&[0_u8; 8_usize]); @@ -990,24 +1033,6 @@ impl MachineLifecycle for StdMachine { } } -impl MachineAddressInterface for StdMachine { - fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { - self.machine_base().pio_in(addr, data) - } - - fn pio_out(&self, addr: u64, data: &[u8]) -> bool { - self.machine_base().pio_out(addr, data) - } - - fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { - self.machine_base().mmio_read(addr, data) - } - - fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { - self.machine_base().mmio_write(addr, data) - } -} - impl MigrateInterface for StdMachine { fn migrate(&self, uri: String) -> Response { match parse_incoming_uri(&uri) { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e85655c0b..0f91bcede 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -12,10 +12,8 @@ pub mod arch; pub mod error; -pub mod standard_vm; +pub mod standard_common; -#[cfg(target_arch = "aarch64")] -mod fdt; mod micro_common; #[cfg(target_arch = "x86_64")] mod vm_state; @@ -24,7 +22,7 @@ pub use anyhow::Result; pub use crate::error::MachineError; pub use micro_common::LightMachine; -pub use standard_vm::StdMachine; +pub use standard_common::StdMachine; use std::collections::{BTreeMap, HashMap}; use std::fs::{remove_file, File}; @@ -94,7 +92,7 @@ use machine_manager::config::{ use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; -use standard_vm::Result as StdResult; +use standard_common::Result as StdResult; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; use util::file::{clear_file, lock_file, unlock_file}; @@ -233,7 +231,7 @@ impl MachineBase { #[cfg(target_arch = "x86_64")] fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { - use standard_vm::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET; + use crate::arch::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET; let count = data.len() as u64; if addr == SLEEP_CTRL_OFFSET as u64 { diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_common/mod.rs similarity index 98% rename from machine/src/standard_vm/mod.rs rename to machine/src/standard_common/mod.rs index d37983576..8452732b5 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -10,18 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(target_arch = "aarch64")] -pub mod aarch64; -#[cfg(target_arch = "x86_64")] -pub mod x86_64; +pub mod syscall; pub use anyhow::Result; -pub use crate::error::MachineError; #[cfg(target_arch = "aarch64")] -pub use aarch64::StdMachine; +pub use crate::arch::aarch64::standard::StdMachine; #[cfg(target_arch = "x86_64")] -pub use x86_64::StdMachine; +pub use crate::arch::x86_64::standard::StdMachine; +pub use crate::error::MachineError; use std::mem::size_of; use std::ops::Deref; @@ -36,12 +33,15 @@ use log::error; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] -use self::x86_64::ich9_lpc::{PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET}; -use super::Result as MachineResult; +use crate::arch::x86_64::ich9_lpc::{ + PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET, +}; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; use crate::MachineOps; -#[cfg(target_arch = "aarch64")] -use aarch64::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] use acpi::AcpiGenericAddress; use acpi::{ @@ -65,8 +65,9 @@ use machine_manager::config::{ NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, M, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; -use machine_manager::machine::MachineLifecycle; -use machine_manager::machine::{DeviceInterface, KvmVmState}; +use machine_manager::machine::{ + DeviceInterface, KvmVmState, MachineAddressInterface, MachineLifecycle, +}; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::MigrationManager; @@ -83,12 +84,10 @@ use virtio::{ ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, VhostKern, VhostUser, VirtioDevice, VirtioNetState, VirtioPciDevice, }; -#[cfg(target_arch = "x86_64")] -use x86_64::{LayoutEntryType, MEM_LAYOUT}; const MAX_REGION_SIZE: u64 = 65536; -trait StdMachineOps: AcpiBuilder + MachineOps { +pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn init_pci_host(&self) -> Result<()>; /// Build all ACPI tables and RSDP, and add them to FwCfg as file entries. @@ -222,7 +221,7 @@ trait StdMachineOps: AcpiBuilder + MachineOps { &self, hotplug_req: Arc, clone_vm: Arc>, - ) -> MachineResult<()> { + ) -> Result<()> { let hotplug_req_fd = hotplug_req.as_raw_fd(); let hotplug_req_handler: Rc = Rc::new(move |_, _| { read_fd(hotplug_req_fd); @@ -239,8 +238,7 @@ trait StdMachineOps: AcpiBuilder + MachineOps { vec![hotplug_req_handler], ); EventLoop::update_event(vec![notifier], None) - .with_context(|| "Failed to register event notifier.")?; - Ok(()) + .with_context(|| "Failed to register event notifier.") } /// Remove vcpu device. @@ -269,7 +267,7 @@ trait StdMachineOps: AcpiBuilder + MachineOps { &self, reset_req: Arc, clone_vm: Arc>, - ) -> MachineResult<()> { + ) -> Result<()> { let reset_req_fd = reset_req.as_raw_fd(); let reset_req_handler: Rc = Rc::new(move |_, _| { read_fd(reset_req_fd); @@ -287,15 +285,14 @@ trait StdMachineOps: AcpiBuilder + MachineOps { vec![reset_req_handler], ); EventLoop::update_event(vec![notifier], None) - .with_context(|| "Failed to register event notifier.")?; - Ok(()) + .with_context(|| "Failed to register event notifier.") } fn register_pause_event( &self, pause_req: Arc, clone_vm: Arc>, - ) -> MachineResult<()> { + ) -> Result<()> { let pause_req_fd = pause_req.as_raw_fd(); let pause_req_handler: Rc = Rc::new(move |_, _| { let _ret = pause_req.read(); @@ -313,15 +310,14 @@ trait StdMachineOps: AcpiBuilder + MachineOps { vec![pause_req_handler], ); EventLoop::update_event(vec![notifier], None) - .with_context(|| "Failed to register event notifier.")?; - Ok(()) + .with_context(|| "Failed to register event notifier.") } fn register_resume_event( &self, resume_req: Arc, clone_vm: Arc>, - ) -> MachineResult<()> { + ) -> Result<()> { let resume_req_fd = resume_req.as_raw_fd(); let resume_req_handler: Rc = Rc::new(move |_, _| { let _ret = resume_req.read(); @@ -339,15 +335,14 @@ trait StdMachineOps: AcpiBuilder + MachineOps { vec![resume_req_handler], ); EventLoop::update_event(vec![notifier], None) - .with_context(|| "Failed to register event notifier.")?; - Ok(()) + .with_context(|| "Failed to register event notifier.") } fn register_shutdown_event( &self, shutdown_req: Arc, clone_vm: Arc>, - ) -> MachineResult<()> { + ) -> Result<()> { use util::loop_context::gen_delete_notifiers; let shutdown_req_fd = shutdown_req.as_raw_fd(); @@ -367,15 +362,14 @@ trait StdMachineOps: AcpiBuilder + MachineOps { vec![shutdown_req_handler], ); EventLoop::update_event(vec![notifier], None) - .with_context(|| "Failed to register event notifier.")?; - Ok(()) + .with_context(|| "Failed to register event notifier.") } } /// Trait that helps to build ACPI tables. /// Standard machine struct should at least implement `build_dsdt_table`, `build_madt_table` /// and `build_mcfg_table` function. -trait AcpiBuilder { +pub(crate) trait AcpiBuilder { /// Add ACPI table to the end of table loader, returns the offset of ACPI table in `acpi_data`. /// /// # Arguments @@ -1154,9 +1148,7 @@ impl StdMachine { fn handle_unplug_usb_request(&mut self, id: String) -> Result<()> { let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); - self.detach_usb_from_xhci_controller(&mut locked_vmconfig, id)?; - - Ok(()) + self.detach_usb_from_xhci_controller(&mut locked_vmconfig, id) } fn plug_vfio_pci_device( @@ -1175,14 +1167,12 @@ impl StdMachine { let sysfsdev = args.sysfsdev.as_ref().map_or("", String::as_str); let multifunc = args.multifunction.unwrap_or(false); self.create_vfio_pci_device(&args.id, bdf, host, sysfsdev, multifunc) - .with_context(|| "Failed to plug vfio-pci device.")?; - - Ok(()) + .with_context(|| "Failed to plug vfio-pci device.") } /// When windows emu exits, stratovirt should exits too. #[cfg(feature = "windows_emu_pid")] - fn watch_windows_emu_pid( + pub(crate) fn watch_windows_emu_pid( &self, vm_config: &VmConfig, power_button: Arc, @@ -1234,6 +1224,26 @@ impl StdMachine { } } +impl MachineAddressInterface for StdMachine { + #[cfg(target_arch = "x86_64")] + fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().pio_in(addr, data) + } + + #[cfg(target_arch = "x86_64")] + fn pio_out(&self, addr: u64, data: &[u8]) -> bool { + self.machine_base().pio_out(addr, data) + } + + fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { + self.machine_base().mmio_read(addr, data) + } + + fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { + self.machine_base().mmio_write(addr, data) + } +} + impl DeviceInterface for StdMachine { fn query_status(&self) -> Response { let vm_state = self.get_vm_state(); diff --git a/machine/src/standard_vm/aarch64/syscall.rs b/machine/src/standard_common/syscall.rs similarity index 91% rename from machine/src/standard_vm/aarch64/syscall.rs rename to machine/src/standard_common/syscall.rs index 7a2997a0a..01ef497e6 100644 --- a/machine/src/standard_vm/aarch64/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -10,6 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(target_arch = "aarch64")] +use crate::arch::aarch64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86_64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; @@ -54,18 +58,15 @@ const KVM_RUN: u32 = 0xae80; /// Create a syscall allowlist for seccomp. /// /// # Notes -/// This allowlist limit syscall with: -/// * aarch64-unknown-gnu: 100 syscalls -/// * aarch64-unknown-musl: 64 syscalls +/// /// To reduce performance losses, the syscall rules is ordered by frequency. pub fn syscall_whitelist() -> Vec { - vec![ + let mut syscall = vec![ BpfRule::new(libc::SYS_read), BpfRule::new(libc::SYS_readv), BpfRule::new(libc::SYS_write), BpfRule::new(libc::SYS_writev), ioctl_allow_list(), - BpfRule::new(libc::SYS_epoll_pwait), BpfRule::new(libc::SYS_io_getevents), BpfRule::new(libc::SYS_io_submit), BpfRule::new(libc::SYS_io_destroy), @@ -120,8 +121,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_pwrite64), BpfRule::new(libc::SYS_pwritev), BpfRule::new(libc::SYS_statx), - BpfRule::new(libc::SYS_mkdirat), - BpfRule::new(libc::SYS_unlinkat), madvise_rule(), BpfRule::new(libc::SYS_msync), BpfRule::new(libc::SYS_readlinkat), @@ -131,7 +130,6 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_bind), BpfRule::new(libc::SYS_connect), BpfRule::new(libc::SYS_getcwd), - BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_prctl), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_getsockname), @@ -166,15 +164,12 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_getrandom), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_shutdown), - BpfRule::new(libc::SYS_rt_sigaction), BpfRule::new(libc::SYS_setsockopt), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_set_robust_list), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_sched_getaffinity), #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_rseq), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_pipe2), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_memfd_create), @@ -190,22 +185,11 @@ pub fn syscall_whitelist() -> Vec { #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_fstatfs), #[cfg(target_env = "gnu")] - BpfRule::new(223), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_listen), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_fchmodat), - #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_shmget), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_shmctl), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_shmat), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_shmdt), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_lremovexattr), - ] + ]; + syscall.append(&mut arch_syscall_whitelist()); + + syscall } /// Create a syscall bpf rule for syscall `ioctl`. @@ -261,13 +245,8 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_ONE_REG() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEVICE_ATTR() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_ARM_VCPU_INIT() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQ_LINE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32); + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32); + let bpf_rule = arch_ioctl_allow_list(bpf_rule); #[cfg(feature = "usb_camera_v4l2")] let bpf_rule = bpf_rule diff --git a/machine/src/standard_vm/x86_64/syscall.rs b/machine/src/standard_vm/x86_64/syscall.rs deleted file mode 100644 index 83e1c8d48..000000000 --- a/machine/src/standard_vm/x86_64/syscall.rs +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use hypervisor::kvm::*; -use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; -#[cfg(feature = "usb_camera_v4l2")] -use util::v4l2::{ - VIDIOC_DQBUF, VIDIOC_ENUM_FMT, VIDIOC_ENUM_FRAMEINTERVALS, VIDIOC_ENUM_FRAMESIZES, - VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, - VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, -}; -use vfio::{ - VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, - VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, - VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, - VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, -}; -use virtio::VhostKern::*; - -/// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/futex.h -const FUTEX_WAIT: u32 = 0; -const FUTEX_WAKE: u32 = 1; -const FUTEX_CMP_REQUEUE: u32 = 4; -const FUTEX_WAKE_OP: u32 = 5; -const FUTEX_WAIT_BITSET: u32 = 9; -const FUTEX_PRIVATE_FLAG: u32 = 128; -#[cfg(target_env = "gnu")] -const FUTEX_CLOCK_REALTIME: u32 = 256; -const FUTEX_WAIT_PRIVATE: u32 = FUTEX_WAIT | FUTEX_PRIVATE_FLAG; -const FUTEX_WAKE_PRIVATE: u32 = FUTEX_WAKE | FUTEX_PRIVATE_FLAG; -const FUTEX_CMP_REQUEUE_PRIVATE: u32 = FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG; -const FUTEX_WAKE_OP_PRIVATE: u32 = FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG; -const FUTEX_WAIT_BITSET_PRIVATE: u32 = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG; - -// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/ioctls.h -const TCGETS: u32 = 0x5401; -const TCSETS: u32 = 0x5402; -const TIOCGWINSZ: u32 = 0x5413; -const FIONREAD: u32 = 0x541B; -const FIOCLEX: u32 = 0x5451; -const FIONBIO: u32 = 0x5421; -const KVM_RUN: u32 = 0xae80; - -/// Create a syscall whitelist for seccomp. -/// -/// # Notes -/// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 98 syscalls -/// * x86_64-unknown-musl: 67 syscalls -/// To reduce performance losses, the syscall rules is ordered by frequency. -pub fn syscall_whitelist() -> Vec { - vec![ - BpfRule::new(libc::SYS_read), - BpfRule::new(libc::SYS_readv), - BpfRule::new(libc::SYS_write), - BpfRule::new(libc::SYS_writev), - ioctl_allow_list(), - #[cfg(not(target_env = "gnu"))] - BpfRule::new(libc::SYS_epoll_pwait), - BpfRule::new(libc::SYS_epoll_wait), - BpfRule::new(libc::SYS_io_getevents), - BpfRule::new(libc::SYS_io_submit), - BpfRule::new(libc::SYS_io_destroy), - BpfRule::new(libc::SYS_io_uring_setup), - BpfRule::new(libc::SYS_io_uring_register), - BpfRule::new(libc::SYS_io_uring_enter), - BpfRule::new(libc::SYS_dup), - BpfRule::new(libc::SYS_close), - BpfRule::new(libc::SYS_eventfd2), - BpfRule::new(libc::SYS_epoll_ctl), - BpfRule::new(libc::SYS_ppoll), - BpfRule::new(libc::SYS_fdatasync), - BpfRule::new(libc::SYS_recvmsg), - BpfRule::new(libc::SYS_sendmsg), - BpfRule::new(libc::SYS_sendto), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_sendmmsg), - BpfRule::new(libc::SYS_recvfrom), - BpfRule::new(libc::SYS_mremap), - BpfRule::new(libc::SYS_io_setup), - BpfRule::new(libc::SYS_brk), - BpfRule::new(libc::SYS_fcntl) - .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_DUPFD_CLOEXEC as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFD as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFD as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETLK as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_GETFL as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, libc::F_SETFL as u32), - BpfRule::new(libc::SYS_flock), - BpfRule::new(libc::SYS_rt_sigprocmask), - BpfRule::new(libc::SYS_open), - BpfRule::new(libc::SYS_openat), - BpfRule::new(libc::SYS_sigaltstack), - BpfRule::new(libc::SYS_mmap), - BpfRule::new(libc::SYS_mprotect), - BpfRule::new(libc::SYS_munmap), - BpfRule::new(libc::SYS_accept4), - BpfRule::new(libc::SYS_lseek), - futex_rule(), - BpfRule::new(libc::SYS_exit), - BpfRule::new(libc::SYS_exit_group), - BpfRule::new(libc::SYS_rt_sigreturn), - #[cfg(target_env = "musl")] - BpfRule::new(libc::SYS_tkill), - #[cfg(target_env = "musl")] - BpfRule::new(libc::SYS_stat), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_tgkill), - BpfRule::new(libc::SYS_gettid), - BpfRule::new(libc::SYS_getpid), - BpfRule::new(libc::SYS_fstat), - BpfRule::new(libc::SYS_newfstatat), - BpfRule::new(libc::SYS_pread64), - BpfRule::new(libc::SYS_preadv), - BpfRule::new(libc::SYS_pwrite64), - BpfRule::new(libc::SYS_pwritev), - BpfRule::new(libc::SYS_statx), - BpfRule::new(libc::SYS_mkdir), - BpfRule::new(libc::SYS_unlink), - madvise_rule(), - BpfRule::new(libc::SYS_msync), - BpfRule::new(libc::SYS_readlinkat), - BpfRule::new(libc::SYS_renameat), - BpfRule::new(libc::SYS_readlink), - BpfRule::new(libc::SYS_socket), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_bind), - BpfRule::new(libc::SYS_connect), - BpfRule::new(libc::SYS_getcwd), - #[cfg(target_env = "musl")] - BpfRule::new(libc::SYS_clone), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_clone3), - BpfRule::new(libc::SYS_prctl), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getsockname), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getpeername), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_nanosleep), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_clock_nanosleep), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getuid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_geteuid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getgid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getegid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_gettid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getdents64), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_clock_gettime), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getsockopt), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_uname), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_sysinfo), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_faccessat), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_shutdown), - BpfRule::new(libc::SYS_getrandom), - BpfRule::new(libc::SYS_setsockopt), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_rt_sigaction), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_set_robust_list), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_sched_getaffinity), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_pipe2), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_memfd_create), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_ftruncate), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_futex), - BpfRule::new(libc::SYS_fallocate), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_poll), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_access), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_sched_setattr), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getresuid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_getresgid), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_fstatfs), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_fadvise64), - #[cfg(target_env = "gnu")] - BpfRule::new(libc::SYS_shmget), - ] -} - -/// Create a syscall bpf rule for syscall `ioctl`. -fn ioctl_allow_list() -> BpfRule { - let bpf_rule = BpfRule::new(libc::SYS_ioctl) - .add_constraint(SeccompCmpOpt::Eq, 1, TCGETS) - .add_constraint(SeccompCmpOpt::Eq, 1, TCSETS) - .add_constraint(SeccompCmpOpt::Eq, 1, TIOCGWINSZ) - .add_constraint(SeccompCmpOpt::Eq, 1, FIOCLEX) - .add_constraint(SeccompCmpOpt::Eq, 1, FIONBIO) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_RUN) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_DEVICE_ATTR) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_USER_MEMORY_REGION) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IOEVENTFD) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SIGNAL_MSI) - .add_constraint(SeccompCmpOpt::Eq, 1, FIONREAD) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_VSOCK_SET_GUEST_CID() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_VSOCK_SET_RUNNING() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_CALL() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_NUM() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_GET_VRING_BASE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_ADDR() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_BASE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_VRING_KICK() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_OWNER() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_FEATURES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_GET_FEATURES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_SET_MEM_TABLE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_NET_SET_BACKEND() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_GET_FEATURES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VHOST_RESET_OWNER() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, TUNGETFEATURES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETIFF() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_SET_IRQS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_GET_STATUS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GET_API_VERSION() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_CHECK_EXTENSION() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_SET_CONTAINER() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_SET_IOMMU() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_CREATE_DEVICE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_IOMMU_MAP_DMA() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_IOMMU_UNMAP_DMA() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_GET_DEVICE_FD() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_INFO() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_RESET() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_REGION_INFO() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_IRQ_INFO() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_PIT2() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_CLOCK() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_IRQCHIP() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XSAVE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEBUGREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XCRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_LAPIC() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SUPPORTED_CPUID() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_CPUID2() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_SREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_REGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XSAVE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XCRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_DEBUGREGS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32); - - #[cfg(feature = "usb_camera_v4l2")] - let bpf_rule = bpf_rule - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYCAP() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FMT() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_G_FMT() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_FMT() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_REQBUFS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QUERYBUF() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_QBUF() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_DQBUF() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMON() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_STREAMOFF() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_S_PARM() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMESIZES() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32); - - bpf_rule -} - -fn madvise_rule() -> BpfRule { - #[cfg(target_env = "musl")] - return BpfRule::new(libc::SYS_madvise) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); - #[cfg(target_env = "gnu")] - return BpfRule::new(libc::SYS_madvise) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) - .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); -} - -fn futex_rule() -> BpfRule { - #[cfg(target_env = "musl")] - return BpfRule::new(libc::SYS_futex) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAKE_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAIT_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_CMP_REQUEUE_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAKE_OP_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAIT_BITSET_PRIVATE); - #[cfg(target_env = "gnu")] - return BpfRule::new(libc::SYS_futex) - .add_constraint( - SeccompCmpOpt::Eq, - 1, - FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME, - ) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAKE_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAIT_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_CMP_REQUEUE_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAKE_OP_PRIVATE) - .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAIT_BITSET_PRIVATE); -} diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs index 7774dbded..eb400f55c 100644 --- a/tests/mod_test/src/libdriver/fwcfg.rs +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -17,7 +17,7 @@ use crate::libtest::{TestState, MACHINE_TYPE_ARG}; use crate::utils::{swap_u16, swap_u32, swap_u64}; use devices::legacy::FwCfgEntryType; #[cfg(target_arch = "aarch64")] -use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "aarch64")] pub const FW_CFG_BASE: u64 = MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0; diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 74773d50d..5e110f4d8 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -17,9 +17,9 @@ use crate::libdriver::pci::*; use crate::libtest::TestState; use crate::utils::{read_le_u16, read_le_u32, read_le_u64}; #[cfg(target_arch = "aarch64")] -use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] -use machine::standard_vm::x86_64::{LayoutEntryType, MEM_LAYOUT}; +use machine::arch::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; const PCIE_MMIO_BASE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; const PCIE_MMIO_SIZE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 174d82128..e15eb9a12 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -18,7 +18,7 @@ use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicRedistributor, AcpiRsdp, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTableHeader, CacheHierarchyNode, ProcessorHierarchyNode, }; -use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; use mod_test::libdriver::fwcfg::bios_args; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; diff --git a/tests/mod_test/tests/aarch64/ged_test.rs b/tests/mod_test/tests/aarch64/ged_test.rs index 9de204bf0..e22e299c1 100644 --- a/tests/mod_test/tests/aarch64/ged_test.rs +++ b/tests/mod_test/tests/aarch64/ged_test.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use machine::standard_vm::aarch64::{LayoutEntryType, MEM_LAYOUT}; +use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; pub const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::Ged as usize].0; -- Gitee From 7ba0791755c9a40dafc63f06b560c768256257dc Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 9 Dec 2023 17:17:33 +0800 Subject: [PATCH 1499/2187] machine: simplified directory structure The directory structure is simplified as follows: machine src aarch64 x86_64 micro_common standard_common ... Signed-off-by: Mingwang Li --- machine/src/{arch => }/aarch64/fdt.rs | 0 machine/src/{arch => }/aarch64/micro.rs | 0 machine/src/{arch => }/aarch64/mod.rs | 0 machine/src/{arch => }/aarch64/pci_host_root.rs | 0 machine/src/{arch => }/aarch64/standard.rs | 0 machine/src/arch/mod.rs | 16 ---------------- machine/src/lib.rs | 7 +++++-- machine/src/micro_common/mod.rs | 4 ++-- machine/src/micro_common/syscall.rs | 4 ++-- machine/src/standard_common/mod.rs | 12 ++++++------ machine/src/standard_common/syscall.rs | 4 ++-- machine/src/{arch => }/x86_64/ich9_lpc.rs | 2 +- machine/src/{arch => }/x86_64/mch.rs | 2 +- machine/src/{arch => }/x86_64/micro.rs | 0 machine/src/{arch => }/x86_64/mod.rs | 0 machine/src/{arch => }/x86_64/standard.rs | 0 tests/mod_test/src/libdriver/fwcfg.rs | 2 +- tests/mod_test/src/libdriver/pci_bus.rs | 4 ++-- tests/mod_test/tests/aarch64/acpi_test.rs | 2 +- tests/mod_test/tests/aarch64/ged_test.rs | 2 +- 20 files changed, 24 insertions(+), 37 deletions(-) rename machine/src/{arch => }/aarch64/fdt.rs (100%) rename machine/src/{arch => }/aarch64/micro.rs (100%) rename machine/src/{arch => }/aarch64/mod.rs (100%) rename machine/src/{arch => }/aarch64/pci_host_root.rs (100%) rename machine/src/{arch => }/aarch64/standard.rs (100%) delete mode 100644 machine/src/arch/mod.rs rename machine/src/{arch => }/x86_64/ich9_lpc.rs (99%) rename machine/src/{arch => }/x86_64/mch.rs (99%) rename machine/src/{arch => }/x86_64/micro.rs (100%) rename machine/src/{arch => }/x86_64/mod.rs (100%) rename machine/src/{arch => }/x86_64/standard.rs (100%) diff --git a/machine/src/arch/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs similarity index 100% rename from machine/src/arch/aarch64/fdt.rs rename to machine/src/aarch64/fdt.rs diff --git a/machine/src/arch/aarch64/micro.rs b/machine/src/aarch64/micro.rs similarity index 100% rename from machine/src/arch/aarch64/micro.rs rename to machine/src/aarch64/micro.rs diff --git a/machine/src/arch/aarch64/mod.rs b/machine/src/aarch64/mod.rs similarity index 100% rename from machine/src/arch/aarch64/mod.rs rename to machine/src/aarch64/mod.rs diff --git a/machine/src/arch/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs similarity index 100% rename from machine/src/arch/aarch64/pci_host_root.rs rename to machine/src/aarch64/pci_host_root.rs diff --git a/machine/src/arch/aarch64/standard.rs b/machine/src/aarch64/standard.rs similarity index 100% rename from machine/src/arch/aarch64/standard.rs rename to machine/src/aarch64/standard.rs diff --git a/machine/src/arch/mod.rs b/machine/src/arch/mod.rs deleted file mode 100644 index bd98a7548..000000000 --- a/machine/src/arch/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -#[cfg(target_arch = "aarch64")] -pub mod aarch64; -#[cfg(target_arch = "x86_64")] -pub mod x86_64; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 0f91bcede..2e4a0ff17 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -10,9 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod arch; +#[cfg(target_arch = "aarch64")] +pub mod aarch64; pub mod error; pub mod standard_common; +#[cfg(target_arch = "x86_64")] +pub mod x86_64; mod micro_common; #[cfg(target_arch = "x86_64")] @@ -231,7 +234,7 @@ impl MachineBase { #[cfg(target_arch = "x86_64")] fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { - use crate::arch::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET; + use crate::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET; let count = data.len() as u64; if addr == SLEEP_CTRL_OFFSET as u64 { diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index ae33c62c9..df0b8935e 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -41,9 +41,9 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{error, info}; #[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::micro::{LayoutEntryType, MEM_LAYOUT}; +use crate::aarch64::micro::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::micro::{LayoutEntryType, MEM_LAYOUT}; +use crate::x86_64::micro::{LayoutEntryType, MEM_LAYOUT}; use crate::{MachineBase, MachineError, MachineOps}; use cpu::{CPUTopology, CpuLifecycleState}; use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index a5f80dca2..027bd2a76 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -11,9 +11,9 @@ // See the Mulan PSL v2 for more details. #[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; +use crate::aarch64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; +use crate::x86_64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 8452732b5..111e78936 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -15,10 +15,10 @@ pub mod syscall; pub use anyhow::Result; #[cfg(target_arch = "aarch64")] -pub use crate::arch::aarch64::standard::StdMachine; -#[cfg(target_arch = "x86_64")] -pub use crate::arch::x86_64::standard::StdMachine; +pub use crate::aarch64::standard::StdMachine; pub use crate::error::MachineError; +#[cfg(target_arch = "x86_64")] +pub use crate::x86_64::standard::StdMachine; use std::mem::size_of; use std::ops::Deref; @@ -34,13 +34,13 @@ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; +use crate::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::ich9_lpc::{ +use crate::x86_64::ich9_lpc::{ PM_CTRL_OFFSET, PM_EVENT_OFFSET, RST_CTRL_OFFSET, SLEEP_CTRL_OFFSET, }; #[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; +use crate::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; use crate::MachineOps; #[cfg(target_arch = "x86_64")] use acpi::AcpiGenericAddress; diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index 01ef497e6..e7b985226 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -11,9 +11,9 @@ // See the Mulan PSL v2 for more details. #[cfg(target_arch = "aarch64")] -use crate::arch::aarch64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; +use crate::aarch64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] -use crate::arch::x86_64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; +use crate::x86_64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; diff --git a/machine/src/arch/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs similarity index 99% rename from machine/src/arch/x86_64/ich9_lpc.rs rename to machine/src/x86_64/ich9_lpc.rs index 5423a16bc..08a3aec30 100644 --- a/machine/src/arch/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -19,7 +19,7 @@ use anyhow::{Context, Result}; use log::error; use vmm_sys_util::eventfd::EventFd; -use crate::arch::x86_64::standard::VENDOR_ID_INTEL; +use crate::x86_64::standard::VENDOR_ID_INTEL; use acpi::{AcpiPMTimer, AcpiPmCtrl, AcpiPmEvent}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use devices::pci::config::{ diff --git a/machine/src/arch/x86_64/mch.rs b/machine/src/x86_64/mch.rs similarity index 99% rename from machine/src/arch/x86_64/mch.rs rename to machine/src/x86_64/mch.rs index aa00152c9..9af04b94e 100644 --- a/machine/src/arch/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; use log::error; -use crate::arch::x86_64::standard::VENDOR_ID_INTEL; +use crate::x86_64::standard::VENDOR_ID_INTEL; use address_space::{Region, RegionOps}; use devices::pci::{ config::{ diff --git a/machine/src/arch/x86_64/micro.rs b/machine/src/x86_64/micro.rs similarity index 100% rename from machine/src/arch/x86_64/micro.rs rename to machine/src/x86_64/micro.rs diff --git a/machine/src/arch/x86_64/mod.rs b/machine/src/x86_64/mod.rs similarity index 100% rename from machine/src/arch/x86_64/mod.rs rename to machine/src/x86_64/mod.rs diff --git a/machine/src/arch/x86_64/standard.rs b/machine/src/x86_64/standard.rs similarity index 100% rename from machine/src/arch/x86_64/standard.rs rename to machine/src/x86_64/standard.rs diff --git a/tests/mod_test/src/libdriver/fwcfg.rs b/tests/mod_test/src/libdriver/fwcfg.rs index eb400f55c..3941d9588 100644 --- a/tests/mod_test/src/libdriver/fwcfg.rs +++ b/tests/mod_test/src/libdriver/fwcfg.rs @@ -17,7 +17,7 @@ use crate::libtest::{TestState, MACHINE_TYPE_ARG}; use crate::utils::{swap_u16, swap_u32, swap_u64}; use devices::legacy::FwCfgEntryType; #[cfg(target_arch = "aarch64")] -use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; +use machine::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "aarch64")] pub const FW_CFG_BASE: u64 = MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0; diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 5e110f4d8..1a146ebf2 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -17,9 +17,9 @@ use crate::libdriver::pci::*; use crate::libtest::TestState; use crate::utils::{read_le_u16, read_le_u32, read_le_u64}; #[cfg(target_arch = "aarch64")] -use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; +use machine::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] -use machine::arch::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; +use machine::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; const PCIE_MMIO_BASE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].0; const PCIE_MMIO_SIZE: u64 = MEM_LAYOUT[LayoutEntryType::PcieMmio as usize].1; diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index e15eb9a12..9478f2cad 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -18,7 +18,7 @@ use acpi::{ AcpiGicCpu, AcpiGicDistributor, AcpiGicRedistributor, AcpiRsdp, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTableHeader, CacheHierarchyNode, ProcessorHierarchyNode, }; -use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; +use machine::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; use mod_test::libdriver::fwcfg::bios_args; use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; diff --git a/tests/mod_test/tests/aarch64/ged_test.rs b/tests/mod_test/tests/aarch64/ged_test.rs index e22e299c1..218957174 100644 --- a/tests/mod_test/tests/aarch64/ged_test.rs +++ b/tests/mod_test/tests/aarch64/ged_test.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use machine::arch::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; +use machine::aarch64::standard::{LayoutEntryType, MEM_LAYOUT}; use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; pub const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::Ged as usize].0; -- Gitee From 5a34933bc04073f1604203fcf25e6769444fa1c2 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 9 Dec 2023 13:22:38 +0800 Subject: [PATCH 1500/2187] scream: gracefully process chunk idx increasement The chunk idx might potentially overflow while increasing. Therefore use wrapping_add/sub to process chunk idx increasement. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 870bef8cb..7fbd309b3 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -263,8 +263,15 @@ impl StreamData { // If the difference between the currently processed chunk_idx and the chunk_idx in // the shared memory is greater than 4, the processing of the backend device is too // slow and the backward data is skipped. - if (play.chunk_idx + play.max_chunks - self.chunk_idx) % play.max_chunks > 4 { - self.chunk_idx = (play.chunk_idx + play.max_chunks - 1) % play.max_chunks; + if play + .chunk_idx + .wrapping_add(play.max_chunks) + .wrapping_sub(self.chunk_idx) + % play.max_chunks + > 4 + { + self.chunk_idx = + play.chunk_idx.wrapping_add(play.max_chunks).wrapping_sub(1) % play.max_chunks; } else { self.chunk_idx = (self.chunk_idx + 1) % play.max_chunks; } -- Gitee From b97450a98507d750277c2c9bcb910c58b20d4ec9 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 7 Dec 2023 21:47:50 +0800 Subject: [PATCH 1501/2187] cpu: define the encoding of kvm aarch64 system registers Signed-off-by: yezengruan --- cpu/src/aarch64/mod.rs | 15 ++++-------- cpu/src/aarch64/regs.rs | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 cpu/src/aarch64/regs.rs diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index ea1dc5df8..5bf165983 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -12,6 +12,7 @@ pub mod caps; mod core_regs; +mod regs; pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; @@ -31,6 +32,7 @@ use kvm_ioctls::{DeviceFd, VcpuFd}; use self::caps::CpregListEntry; use self::core_regs::{get_core_regs, set_core_regs}; +use self::regs::{KVM_REG_ARM_MPIDR_EL1, KVM_REG_ARM_TIMER_CNT}; use crate::CPU; use hypervisor::kvm::KVM_FDS; use migration::{ @@ -51,13 +53,6 @@ const PSR_D_BIT: u64 = 0x0000_0200; // [40:63] bit reserved on AArch64 Architecture, const UNINIT_MPIDR: u64 = 0xFFFF_FF00_0000_0000; -// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h#L130 -// MPIDR - Multiprocessor Affinity Register. -const SYS_MPIDR_EL1: u64 = 0x6030_0000_0013_c005; -// Counter-timer Virtual Count register: Due to the API interface problem, the encode of -// this register is SYS_CNTV_CVAL_EL0. -const SYS_CNTV_CNT_EL0: u64 = 0x6030_0000_0013_df1a; - const KVM_MAX_CPREG_ENTRIES: usize = 500; /// Interrupt ID for pmu. @@ -196,7 +191,7 @@ impl ArmCPUState { .vcpu_init(&self.kvi) .with_context(|| "Failed to init kvm vcpu")?; self.mpidr = vcpu_fd - .get_one_reg(SYS_MPIDR_EL1) + .get_one_reg(KVM_REG_ARM_MPIDR_EL1) .with_context(|| "Failed to get mpidr")? as u64; self.features = *vcpu_config; @@ -272,7 +267,7 @@ impl ArmCPUState { /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn set_virtual_timer_cnt(&self, vcpu_fd: &Arc) -> Result<()> { vcpu_fd - .set_one_reg(SYS_CNTV_CNT_EL0, self.vtimer_cnt as u128) + .set_one_reg(KVM_REG_ARM_TIMER_CNT, self.vtimer_cnt as u128) .with_context(|| "Failed to set virtual timer count") } @@ -283,7 +278,7 @@ impl ArmCPUState { /// * `vcpu_fd` - Vcpu file descriptor in kvm. pub fn get_virtual_timer_cnt(&mut self, vcpu_fd: &Arc) -> Result<()> { self.vtimer_cnt = vcpu_fd - .get_one_reg(SYS_CNTV_CNT_EL0) + .get_one_reg(KVM_REG_ARM_TIMER_CNT) .with_context(|| "Failed to get virtual timer count")? as u64; Ok(()) } diff --git a/cpu/src/aarch64/regs.rs b/cpu/src/aarch64/regs.rs new file mode 100644 index 000000000..1f8584f15 --- /dev/null +++ b/cpu/src/aarch64/regs.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use kvm_bindings::{ + KVM_REG_ARM64, KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK, + KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT, + KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK, + KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT, + KVM_REG_SIZE_U64, +}; + +// Arm Architecture Reference Manual defines the encoding of AArch64 system registers: +// (Ref: ARMv8 ARM, Section: "System instruction class encoding overview") +// While KVM defines another ID for each AArch64 system register, which is used in calling +// `KVM_G/SET_ONE_REG` to access a system register of a guest. A mapping exists between the +// Arm standard encoding and the KVM ID. +// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/kvm.h#L216 +#[macro_export] +macro_rules! arm64_sys_reg { + ($op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => { + KVM_REG_SIZE_U64 + | KVM_REG_ARM64 + | KVM_REG_ARM64_SYSREG as u64 + | (((($op0 as u32) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK) + as u64) + | (((($op1 as u32) << KVM_REG_ARM64_SYSREG_OP1_SHIFT) & KVM_REG_ARM64_SYSREG_OP1_MASK) + as u64) + | (((($crn as u32) << KVM_REG_ARM64_SYSREG_CRN_SHIFT) & KVM_REG_ARM64_SYSREG_CRN_MASK) + as u64) + | (((($crm as u32) << KVM_REG_ARM64_SYSREG_CRM_SHIFT) & KVM_REG_ARM64_SYSREG_CRM_MASK) + as u64) + | (((($op2 as u32) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK) + as u64) + }; +} + +// The following system register codes can be found at this website: +// https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h + +// MPIDR - Multiprocessor Affinity Register(SYS_MPIDR_EL1). +pub(crate) const KVM_REG_ARM_MPIDR_EL1: u64 = arm64_sys_reg!(3, 0, 0, 0, 5); + +// Counter-timer Virtual Count register: Due to the API interface problem, the encode of +// this register is SYS_CNTV_CVAL_EL0. +pub(crate) const KVM_REG_ARM_TIMER_CNT: u64 = arm64_sys_reg!(3, 3, 14, 3, 2); -- Gitee From 8415f78b4d99f0bcfbd492480a36fb0a9763c0ea Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 13 Dec 2023 06:04:03 +0800 Subject: [PATCH 1502/2187] Image: set disk discard enabled in offline snapshot Set disk discard enabled in offline snapshot. Signed-off-by: Xiao Ye --- image/src/img.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/image/src/img.rs b/image/src/img.rs index 82b428391..ecc1e4215 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -28,7 +28,7 @@ use block_backend::{ }; use machine_manager::config::{memory_unit_conversion, DiskFormat}; use util::{ - aio::{Aio, AioEngine}, + aio::{Aio, AioEngine, WriteZeroesState}, file::{lock_file, open_file, unlock_file}, }; @@ -330,6 +330,9 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { // Create qcow2 driver. let mut qcow2_conf = BlockProperty::default(); qcow2_conf.format = DiskFormat::Qcow2; + qcow2_conf.discard = true; + qcow2_conf.write_zeroes = WriteZeroesState::Unmap; + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); let mut qcow2_driver = Qcow2Driver::new(image_file.file.try_clone()?, aio, qcow2_conf.clone())?; qcow2_driver.load_metadata(qcow2_conf)?; -- Gitee From 13278c11b8d409c7cfe8f1ce6f061c32c5936749 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 13 Dec 2023 06:08:24 +0800 Subject: [PATCH 1503/2187] Image: format output of snapshot querying Format output of snapshot querying. Signed-off-by: Xiao Ye --- image/src/img.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/image/src/img.rs b/image/src/img.rs index ecc1e4215..2a3fc39b8 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -343,7 +343,8 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { } Some(SnapshotOperation::List) => { let info = qcow2_driver.list_snapshots(); - println!("{}", info); + println!("Snapshot list:"); + print!("{}", info); } Some(SnapshotOperation::Delete) => { qcow2_driver.delete_snapshot(snapshot_name)?; -- Gitee From 0de7d91389af97f34eb91727381d2aa1dfe91f44 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 20:00:58 +0800 Subject: [PATCH 1504/2187] Refactor: reduce unsafe code blocks Reduce the unsafe code blocks and add the verification for null pointer. Signed-off-by: Xiao Ye --- ui/src/console.rs | 5 +++- ui/src/pixman.rs | 67 +++++++++++++++++++++++++---------------------- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index 815abeb05..74635e4e2 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -788,7 +788,10 @@ pub fn create_msg_surface(width: i32, height: i32, msg: String) -> Option *mut pixman_image_t { - let glyph; +pub fn pixman_glyph_from_vgafont(height: u32, ch: u32) -> *mut pixman_image_t { + let glyph = create_pixman_image( + pixman_format_code_t::PIXMAN_a8, + 8, + height as i32, + ptr::null_mut(), + 0, + ); + let data = get_image_data(glyph) as *mut u8; + if data.is_null() { + return glyph; + } + let mut data_index: usize = 0; + let mut font_index: usize = height as usize * ch as usize; + let slice = + // SAFETY: todo! unsafe { - glyph = create_pixman_image( - pixman_format_code_t::PIXMAN_a8, - 8, - height, - ptr::null_mut(), - 0, - ); - let data = pixman_image_get_data(glyph) as *mut u8; - let mut data_index: usize = 0; - let mut font_index: usize = (height * ch as i32) as usize; - let slice = std::slice::from_raw_parts_mut(data, (height * 8) as usize); - - for _y in 0..height { - for _x in 0..8 { - if VGA_FONTS[font_index] & (1 << (7 - _x)) > 0 { - slice[data_index] = 0xff; - } else { - slice[data_index] = 0x00; - }; - - data_index += 1; - } - font_index += 1; + std::slice::from_raw_parts_mut(data, height as usize * 8 ) + }; + + for _ in 0..height { + for x in 0..8 { + if VGA_FONTS[font_index] & (1 << (7 - x)) > 0 { + slice[data_index] = 0xff; + } else { + slice[data_index] = 0x00; + }; + data_index += 1; } + font_index += 1; } glyph } @@ -650,14 +653,14 @@ pub fn pixman_glyph_render( cw: i32, ch: i32, ) { - unsafe { - // for not_unsafe_ptr_arg_deref check, need declare here - let glyph = glyph as *mut pixman_image_t; - let surface = surface as *mut pixman_image_t; - let fgcolor = fgcolor as *const pixman_color_t; - let bgcolor = bgcolor as *const pixman_color_t; - let (x, y) = rec; + let glyph = glyph as *mut pixman_image_t; + let surface = surface as *mut pixman_image_t; + let fgcolor = fgcolor as *const pixman_color_t; + let bgcolor = bgcolor as *const pixman_color_t; + let (x, y) = rec; + // SAFETY: todo! + unsafe { let ifg = pixman_image_create_solid_fill(fgcolor); let ibg = pixman_image_create_solid_fill(bgcolor); -- Gitee From d2acf9f03c55f8cbe95f75962481980ce81ea473 Mon Sep 17 00:00:00 2001 From: dehengli Date: Wed, 13 Dec 2023 13:45:36 +0800 Subject: [PATCH 1505/2187] machine: fix ACPI FADT and DSDT Error. 1. Fix FADT PM1a_EVT_BLK, PM1a_CNT_BLK and PM_TMR_BLK zero length error. 2. Remove nr cpu info generate in build dsdt table function, all cpu info generate by cpu controller. Signed-off-by: dehengli --- machine/src/standard_common/mod.rs | 9 +++++++++ machine/src/x86_64/standard.rs | 20 +++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 111e78936..889506f23 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -598,6 +598,15 @@ pub(crate) trait AcpiBuilder { // PM_TMR_BLK bit, offset is 76. #[cfg(target_arch = "x86_64")] fadt.set_field(76, 0x608); + // PM1_EVT_LEN, offset is 88. + #[cfg(target_arch = "x86_64")] + fadt.set_field(88, 4); + // PM1_CNT_LEN, offset is 89. + #[cfg(target_arch = "x86_64")] + fadt.set_field(89, 2); + // PM_TMR_LEN, offset is 91. + #[cfg(target_arch = "x86_64")] + fadt.set_field(91, 4); #[cfg(target_arch = "aarch64")] { // FADT flag: enable HW_REDUCED_ACPI bit on aarch64 plantform. diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 1dcdc8355..110fa2bd8 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -27,8 +27,8 @@ use crate::standard_common::{AcpiBuilder, StdMachineOps}; use crate::{vm_state, MachineBase, MachineOps}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, - AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, - AmlString, TableLoader, IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, + AmlBuilder, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, TableLoader, + IOAPIC_BASE_ADDR, LAPIC_BASE_ADDR, }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use boot_loader::{load_linux, BootLoaderConfig}; @@ -781,25 +781,15 @@ impl AcpiBuilder for StdMachine { ) -> Result { let mut dsdt = AcpiTable::new(*b"DSDT", 2, *b"STRATO", *b"VIRTDSDT", 1); - // 1. CPU info. - let cpus_count = self.base.cpus.len() as u64; + // 1. Create pci host bridge node. let mut sb_scope = AmlScope::new("\\_SB"); - for cpu_id in 0..cpus_count { - let mut dev = AmlDevice::new(format!("C{:03}", cpu_id).as_str()); - dev.append_child(AmlNameDecl::new("_HID", AmlString("ACPI0007".to_string()))); - dev.append_child(AmlNameDecl::new("_UID", AmlInteger(cpu_id))); - dev.append_child(AmlNameDecl::new("_PXM", AmlInteger(0))); - sb_scope.append_child(dev); - } - - // 2. Create pci host bridge node. sb_scope.append_child(self.pci_host.lock().unwrap().clone()); dsdt.append_child(sb_scope.aml_bytes().as_slice()); - // 3. Info of devices attached to system bus. + // 2. Info of devices attached to system bus. dsdt.append_child(self.base.sysbus.aml_bytes().as_slice()); - // 4. Add _S5 sleep state. + // 3. Add _S5 sleep state. let mut package = AmlPackage::new(4); package.append_child(AmlInteger(5)); package.append_child(AmlInteger(0)); -- Gitee From fd3f5a0c9a8daaf67ec2383f530663de9f1ebb8e Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 13 Dec 2023 16:58:31 +0800 Subject: [PATCH 1506/2187] CPU: delete cpu_features from Machinebase cpu_features are not closely related to MachineBase. So deleted it. Signed-off-by: Mingwang Li --- cpu/src/aarch64/mod.rs | 5 +++++ machine/src/aarch64/fdt.rs | 2 +- machine/src/lib.rs | 5 ----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 5bf165983..58edb6b1c 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -282,6 +282,11 @@ impl ArmCPUState { .with_context(|| "Failed to get virtual timer count")? as u64; Ok(()) } + + /// Get cpu features. + pub fn get_features(&self) -> &ArmCPUFeatures { + &self.features + } } impl CPU { diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index 88f4d0eb5..57f5a642e 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -227,7 +227,7 @@ impl CompileFDTHelper for MachineBase { fdt.end_node(cpus_node_dep)?; - if self.cpu_features.pmu { + if self.cpus[0].arch().lock().unwrap().get_features().pmu { generate_pmu_node(fdt)?; } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 2e4a0ff17..c7163a8ff 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -120,9 +120,6 @@ pub struct MachineBase { cpu_topo: CpuTopology, /// `vCPU` devices. cpus: Vec>, - /// `vCPU` family and feature configuration. Only supports aarch64 currently. - #[cfg(target_arch = "aarch64")] - cpu_features: CPUFeatures, /// Interrupt controller device. #[cfg(target_arch = "aarch64")] irq_chip: Option>, @@ -194,8 +191,6 @@ impl MachineBase { cpu_topo, cpus: Vec::new(), #[cfg(target_arch = "aarch64")] - cpu_features: (&vm_config.machine_config.cpu_config).into(), - #[cfg(target_arch = "aarch64")] irq_chip: None, sys_mem, #[cfg(target_arch = "x86_64")] -- Gitee From b967b45aef2594180a77e861bca3666e54a8705a Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Wed, 13 Dec 2023 20:58:01 +0800 Subject: [PATCH 1507/2187] Updated documentation tutorials. Signed-off-by: Yan Wen --- README.ch.md | 2 +- docs/mk_initrd.ch.md | 4 ++-- docs/mk_initrd.md | 4 ++-- docs/quickstart.ch.md | 16 ++++++++-------- docs/quickstart.md | 14 +++++++------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.ch.md b/README.ch.md index 5ffdd03d5..68f14aa99 100644 --- a/README.ch.md +++ b/README.ch.md @@ -57,7 +57,7 @@ $ ./target/release/stratovirt \ -serial stdio ``` -关于制作rootfs镜像、编译内核镜像以及编译StratoVirt的详细指导,请参考[StratoVirt Quickstart](./docs/quickstart.md)。 +关于制作rootfs镜像、编译内核镜像以及编译StratoVirt的详细指导,请参考[StratoVirt Quickstart](./docs/quickstart.ch.md)。 StratoVirt所支持更多特性,详细指导请参考[Configuration Guidebook](docs/config_guidebook.md)。 diff --git a/docs/mk_initrd.ch.md b/docs/mk_initrd.ch.md index d11802f70..c57390ac0 100644 --- a/docs/mk_initrd.ch.md +++ b/docs/mk_initrd.ch.md @@ -3,8 +3,8 @@ ## 1. 下载Busybox源码,然后解压 ``` shell -wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2 -tar -xjf busybox-1.31.1.tar.bz2 +wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 +tar -xjf busybox-1.36.1.tar.bz2 ``` ## 2. 编译BusyBox diff --git a/docs/mk_initrd.md b/docs/mk_initrd.md index 79d880212..2bcca4aa7 100644 --- a/docs/mk_initrd.md +++ b/docs/mk_initrd.md @@ -3,8 +3,8 @@ ## 1. Download BusyBox, then decompression ``` shell -wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2 -tar -xjf busybox-1.31.1.tar.bz2 +wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2 +tar -xjf busybox-1.36.1.tar.bz2 ``` ## 2. Compile BusyBox diff --git a/docs/quickstart.ch.md b/docs/quickstart.ch.md index d690fe6d3..7f8892c5a 100644 --- a/docs/quickstart.ch.md +++ b/docs/quickstart.ch.md @@ -26,7 +26,7 @@ $ sudo yum install stratovirt 安装完成后可以找到StratoVirt二进制的路径: `/usr/bin/stratovirt`. -如果需要自己构建StratoVirt二进制, 可以参考[构建指导](./build_guide_ch.md). +如果需要自己构建StratoVirt二进制, 可以参考[构建指导](./build_guide.ch.md). ## 3. 运行StratoVirt @@ -34,10 +34,10 @@ $ sudo yum install stratovirt StratoVirt当前提供了两种虚拟机:微虚拟机和标准虚拟机(x86_64平台q35主板和aarch平台的virt主板)。 作为快速入门,以下展示启动微虚拟机。 首先,需要PE格式的Linux内核二进制和ext4文件系统镜像(作为rootfs)。 -* `x86_64` 启动资源: [内核二进制](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/x86_64/vmlinux.bin) -and [rootfs镜像](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/x86_64/openEuler-21.03-stratovirt-x86_64.img.xz). -* `aarch64` 启动资源: [内核二进制](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/aarch64/vmlinux.bin) -and [rootfs镜像](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/aarch64/openEuler-21.03-stratovirt-aarch64.img.xz). +* `x86_64` 启动资源: [内核二进制](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/x86_64/vmlinux.bin) +and [rootfs镜像](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/x86_64/openEuler-22.03-LTS-stratovirt-x86_64.img.xz). +* `aarch64` 启动资源: [内核二进制](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/aarch64/vmlinux.bin) +and [rootfs镜像](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/aarch64/openEuler-22.03-LTS-stratovirt-aarch64.img.xz). 也可以通过以下的shell脚本获取内核二进制和rootfs镜像: @@ -45,11 +45,11 @@ and [rootfs镜像](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/aar arch=`uname -m` dest_kernel="vmlinux.bin" dest_rootfs="rootfs.ext4" -image_bucket_url="https://repo.openeuler.org/openEuler-21.03/stratovirt_img" +image_bucket_url="https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img" if [ ${arch} = "x86_64" ] || [ ${arch} = "aarch64" ]; then kernel="${image_bucket_url}/${arch}/vmlinux.bin" - rootfs="${image_bucket_url}/${arch}/openEuler-21.03-stratovirt-${arch}.img.xz" + rootfs="${image_bucket_url}/${arch}/openEuler-22.03-LTS-stratovirt-${arch}.img.xz" else echo "Cannot run StratoVirt on ${arch} architecture!" exit 1 @@ -88,7 +88,7 @@ rm -f ${socket_path} -serial stdio ``` -在标准输入输出串口上提示登入客户机。 如果使用我们提供的`openEuler-21.03-stratovirt-aarch64.img`镜像, +在标准输入输出串口上提示登入客户机。 如果使用我们提供的`openEuler-22.03-LTS-stratovirt.img`镜像, 可以使用用户名`root`和密码`openEuler12#$`进行登入。 如果想要停止客户机,可以通过在客户机内部输入`reboot`命令来实际关闭StratoVirt。 diff --git a/docs/quickstart.md b/docs/quickstart.md index c207be288..80275cef5 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -37,10 +37,10 @@ are microvm and standard_vm("q35" on x86_64 platform and "virt" on aarch64 platf As a quick start, we show how to start a VM with microvm. First, you will need an PE format Linux kernel binary, and an ext4 file system image (as rootfs). -* `x86_64` boot source: [kernel](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/x86_64/vmlinux.bin) -and [rootfs](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/x86_64/openEuler-21.03-stratovirt-x86_64.img.xz). -* `aarch64` boot source: [kernel](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/aarch64/vmlinux.bin) -and [rootfs](https://repo.openeuler.org/openEuler-21.03/stratovirt_img/aarch64/openEuler-21.03-stratovirt-aarch64.img.xz). +* `x86_64` boot source: [kernel](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/x86_64/vmlinux.bin) +and [rootfs](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/x86_64/openEuler-22.03-LTS-stratovirt-x86_64.img.xz). +* `aarch64` boot source: [kernel](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/aarch64/vmlinux.bin) +and [rootfs](https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img/aarch64/openEuler-22.03-LTS-stratovirt-aarch64.img.xz). Or get the kernel and rootfs with shell: @@ -48,11 +48,11 @@ Or get the kernel and rootfs with shell: arch=`uname -m` dest_kernel="vmlinux.bin" dest_rootfs="rootfs.ext4" -image_bucket_url="https://repo.openeuler.org/openEuler-21.03/stratovirt_img" +image_bucket_url="https://repo.openeuler.org/openEuler-22.03-LTS/stratovirt_img" if [ ${arch} = "x86_64" ] || [ ${arch} = "aarch64" ]; then kernel="${image_bucket_url}/${arch}/vmlinux.bin" - rootfs="${image_bucket_url}/${arch}/openEuler-21.03-stratovirt-${arch}.img.xz" + rootfs="${image_bucket_url}/${arch}/openEuler-22.03-LTS-stratovirt-${arch}.img.xz" else echo "Cannot run StratoVirt on ${arch} architecture!" exit 1 @@ -92,7 +92,7 @@ rm -f ${socket_path} ``` You should now see a serial in stdio prompting you to log into the guest machine. -If you used our `openEuler-21.03-stratovirt-aarch64.img` image, you can login as +If you used our `openEuler-22.03-LTS-stratovirt.img` image, you can login as `root`, using the password `openEuler12#$`. If you want to quit the guest machine, using a `reboot` command inside the guest -- Gitee From 7d6f6a210679104f6b6677c3b79aca805f3e1d89 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 21 Nov 2023 22:20:14 +0800 Subject: [PATCH 1508/2187] Refactor: replace unsafe code block of fallocate Replace the unsafe block code of fallocte with the safe function import from vmm_sys_util. Signed-off-by: Xiao Ye --- util/src/aio/mod.rs | 10 ++++---- util/src/aio/raw.rs | 61 ++++++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 77d2e1458..0498c27a0 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -600,10 +600,10 @@ impl Aio { fn discard_sync(&mut self, cb: AioCb) -> Result<()> { let ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); - if ret < 0 && ret != -libc::ENOTSUP as i64 { + if ret < 0 && ret != -libc::ENOTSUP { error!("Failed to do sync discard."); } - (self.complete_func)(&cb, ret) + (self.complete_func)(&cb, ret as i64) } fn write_zeroes_sync(&mut self, mut cb: AioCb) -> Result<()> { @@ -611,11 +611,11 @@ impl Aio { if cb.opcode == OpCode::WriteZeroesUnmap { ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); if ret == 0 { - return (self.complete_func)(&cb, ret); + return (self.complete_func)(&cb, ret as i64); } } ret = raw_write_zeroes(cb.file_fd, cb.offset, cb.nbytes); - if ret == -libc::ENOTSUP as i64 && !cb.iovec.is_empty() { + if ret == -libc::ENOTSUP && !cb.iovec.is_empty() { cb.opcode = OpCode::Pwritev; cb.write_zeroes = WriteZeroesState::Off; return self.submit_request(cb); @@ -624,7 +624,7 @@ impl Aio { if ret < 0 { error!("Failed to do sync write zeroes."); } - (self.complete_func)(&cb, ret) + (self.complete_func)(&cb, ret as i64) } } diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 8db7e7788..69cb0bae5 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -12,11 +12,9 @@ use std::os::unix::io::RawFd; -use libc::{ - c_int, c_void, fallocate, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t, - FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE, FALLOC_FL_ZERO_RANGE, -}; +use libc::{c_int, c_void, fdatasync, iovec, off_t, pread, preadv, pwrite, pwritev, size_t}; use log::error; +use vmm_sys_util::fallocate::{fallocate, FallocateMode}; use super::Iovec; @@ -141,44 +139,57 @@ pub fn raw_datasync(fd: RawFd) -> i64 { ret } -pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i64 { - let ret = do_fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, size); - if ret < 0 && ret != -libc::ENOTSUP as i64 { - error!( - "Failed to fallocate for {}, errno {}.", - fd, - nix::errno::errno(), - ); +pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i32 { + let ret = do_fallocate(fd, FallocateMode::PunchHole, true, offset as u64, size); + + if ret < 0 && ret != -libc::ENOTSUP { + error!("Failed to fallocate for {}, errno {}.", fd, ret); } ret } -pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i64 { - let ret = do_fallocate(fd, FALLOC_FL_ZERO_RANGE, offset, size); - if ret < 0 && ret != -libc::ENOTSUP as i64 { +pub fn raw_write_zeroes(fd: RawFd, offset: usize, size: u64) -> i32 { + let ret = do_fallocate(fd, FallocateMode::ZeroRange, false, offset as u64, size); + + if ret < 0 && ret != -libc::ENOTSUP { error!( "Failed to fallocate zero range for fd {}, errno {}.", - fd, - nix::errno::errno(), + fd, ret, ); } ret } -fn do_fallocate(fd: RawFd, mode: i32, offset: usize, size: u64) -> i64 { +fn do_fallocate( + fd: RawFd, + fallocate_mode: FallocateMode, + keep_size: bool, + offset: u64, + size: u64, +) -> i32 { + let mut ret = 0; loop { - // SAFETY: fd is valid. - let ret = unsafe { fallocate(fd as c_int, mode, offset as i64, size as i64) as i64 }; + let mode = match &fallocate_mode { + FallocateMode::PunchHole => FallocateMode::PunchHole, + FallocateMode::ZeroRange => FallocateMode::ZeroRange, + }; + + if let Err(e) = fallocate(&fd, mode, keep_size, offset, size) { + ret = e.errno() + }; + if ret == 0 { - return 0; + return ret; } - if nix::errno::errno() != libc::EINTR { + + if ret != libc::EINTR { break; } } - if [libc::ENODEV, libc::ENOSYS, libc::EOPNOTSUPP, libc::ENOTTY].contains(&nix::errno::errno()) { - return -libc::ENOTSUP as i64; + if [libc::ENODEV, libc::ENOSYS, libc::EOPNOTSUPP, libc::ENOTTY].contains(&ret) { + ret = libc::ENOTSUP; } - -nix::errno::errno() as i64 + + -ret } -- Gitee From 578416c434999d47c22a329f16cb6c148d286cd4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 00:54:30 +0800 Subject: [PATCH 1509/2187] Refactor: reduce the unsafe code block in host_mmap Replace the unsafe code block of SYS_memfd_create with safe function of memfd_create. Signed-off-by: Xiao Ye --- address_space/src/host_mmap.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index c945c99cb..3436f7ccc 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -11,13 +11,15 @@ // See the Mulan PSL v2 for more details. use std::cmp::min; +use std::ffi::CString; use std::fs::{remove_file, File}; -use std::os::unix::io::{FromRawFd, RawFd}; +use std::os::unix::io::FromRawFd; use std::sync::Arc; use std::thread; use anyhow::{bail, Context, Result}; use log::{error, info}; +use nix::sys::memfd::{memfd_create, MemFdCreateFlag}; use nix::sys::statfs::fstatfs; use nix::unistd::{mkstemp, sysconf, unlink, SysconfVar}; @@ -245,11 +247,10 @@ pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Resu .with_context(|| "Failed to create file that backs memory")?, ); } else if mem_config.mem_share { - let anon_mem_name = String::from("stratovirt_anon_mem"); - - // SAFETY: the parameters is constant. - let anon_fd = - unsafe { libc::syscall(libc::SYS_memfd_create, anon_mem_name.as_ptr(), 0) } as RawFd; + let anon_fd = memfd_create( + &CString::new("stratovirt_anon_mem")?, + MemFdCreateFlag::empty(), + )?; if anon_fd < 0 { return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } @@ -294,11 +295,10 @@ pub fn create_backend_mem(mem_config: &MemZoneConfig, thread_num: u8) -> Result< let mut f_back: Option = None; if mem_config.memfd { - let anon_mem_name = String::from("stratovirt_anon_mem"); - - // SAFETY: the parameters is constant. - let anon_fd = - unsafe { libc::syscall(libc::SYS_memfd_create, anon_mem_name.as_ptr(), 0) } as RawFd; + let anon_fd = memfd_create( + &CString::new("stratovirt_anon_mem")?, + MemFdCreateFlag::empty(), + )?; if anon_fd < 0 { return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } -- Gitee From 50c5fbbde952a4e1248798dddd3b6a63e4cff91d Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 01:52:54 +0800 Subject: [PATCH 1510/2187] Refactor: reduce the unsafe code block Replace some unsafe code block with safe function in nix. Signed-off-by: Xiao Ye --- Cargo.lock | 3 ++ block_backend/src/qcow2/mod.rs | 2 +- chardev_backend/Cargo.toml | 1 + chardev_backend/src/chardev.rs | 77 +++++++++++----------------------- cpu/Cargo.toml | 1 + cpu/src/lib.rs | 3 +- ozone/Cargo.toml | 1 + ozone/src/handler.rs | 4 +- ui/src/gtk/mod.rs | 2 +- util/src/file.rs | 17 ++++---- util/src/lib.rs | 36 +++++++--------- util/src/logger.rs | 13 +++--- util/src/tap.rs | 9 ++-- util/src/time.rs | 17 ++++---- util/src/unix.rs | 16 ++++--- 15 files changed, 90 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43c592ba2..b87d5d89a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -258,6 +258,7 @@ dependencies = [ "libc", "log", "machine_manager", + "nix 0.26.2", "util", "vmm-sys-util", ] @@ -315,6 +316,7 @@ dependencies = [ "machine_manager", "migration", "migration_derive", + "nix 0.26.2", "thiserror", "util", "vmm-sys-util", @@ -1132,6 +1134,7 @@ version = "2.3.0" dependencies = [ "anyhow", "libc", + "nix 0.26.2", "thiserror", "util", ] diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index e6613bf6c..1b3929840 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -959,7 +959,7 @@ impl Qcow2Driver { self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 1)?; // Alloc new snapshot table. - let (date_sec, date_nsec) = gettime(); + let (date_sec, date_nsec) = gettime()?; let snap = QcowSnapshot { l1_table_offset: new_l1_table_offset, l1_size: self.header.l1_size, diff --git a/chardev_backend/Cargo.toml b/chardev_backend/Cargo.toml index 935988619..a162e58d6 100644 --- a/chardev_backend/Cargo.toml +++ b/chardev_backend/Cargo.toml @@ -10,5 +10,6 @@ vmm-sys-util = "0.11.0" anyhow = "1.0" log = "0.4" libc = "0.2" +nix = "0.26.2" machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 8bd9d84f9..1860656f2 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -19,8 +19,10 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use anyhow::{bail, Context, Result}; -use libc::{cfmakeraw, tcgetattr, tcsetattr, termios}; use log::{error, info, warn}; +use nix::fcntl::{fcntl, FcntlArg, OFlag}; +use nix::pty::openpty; +use nix::sys::termios::{cfmakeraw, tcgetattr, tcsetattr, SetArg, Termios}; use vmm_sys_util::epoll::EventSet; use machine_manager::event_loop::EventLoop; @@ -232,58 +234,30 @@ impl Chardev { } fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { - let mut master: libc::c_int = 0; - let master_ptr: *mut libc::c_int = &mut master; - let mut slave: libc::c_int = 0; - let slave_ptr: *mut libc::c_int = &mut slave; - // Safe because this only create a new pseudoterminal and set the master and slave fd. - let ret = { - unsafe { - libc::openpty( - master_ptr, - slave_ptr, - std::ptr::null_mut(), - std::ptr::null_mut(), - std::ptr::null_mut(), - ) - } + let (master, slave) = match openpty(None, None) { + Ok(res) => (res.master, res.slave), + Err(e) => bail!("Failed to open pty, error is {:?}", e), }; - if ret < 0 { - bail!( - "Failed to open pty, error is {}", - std::io::Error::last_os_error() - ) - } + let proc_path = PathBuf::from(format!("/proc/self/fd/{}", slave)); let path = read_link(proc_path).with_context(|| "Failed to read slave pty link")?; - // Safe because this only set the `old_termios` struct to zero. - let mut old_termios: termios = unsafe { std::mem::zeroed() }; - // Safe because this only get the current mode of slave pty and save it. - let ret = unsafe { tcgetattr(slave, &mut old_termios as *mut _) }; - if ret < 0 { - bail!( - "Failed to get mode of pty, error is {}", - std::io::Error::last_os_error() - ); - } - let mut new_termios: termios = old_termios; - // Safe because this function only change the `new_termios` argument. - unsafe { cfmakeraw(&mut new_termios as *mut _) }; - // Safe because this function only set the slave pty to raw mode. - let ret = unsafe { tcsetattr(slave, libc::TCSAFLUSH, &new_termios as *const _) }; - if ret < 0 { - bail!( - "Failed to set pty to raw mode, error is {}", - std::io::Error::last_os_error() - ); + + let mut new_termios: Termios = match tcgetattr(slave) { + Ok(tm) => tm, + Err(e) => bail!("Failed to get mode of pty, error is {:?}", e), + }; + + cfmakeraw(&mut new_termios); + + if let Err(e) = tcsetattr(slave, SetArg::TCSAFLUSH, &new_termios) { + bail!("Failed to set pty to raw mode, error is {:?}", e); } - // SAFETY: master is got from openpty. - let ret = unsafe { libc::fcntl(master, libc::F_SETFL, libc::O_NONBLOCK) }; - if ret < 0 { + let fcnt_arg = FcntlArg::F_SETFL(OFlag::from_bits(libc::O_NONBLOCK).unwrap()); + if let Err(e) = fcntl(master, fcnt_arg) { bail!( - "Failed to set pty master to nonblocking mode, error is {}", - std::io::Error::last_os_error() + "Failed to set pty master to nonblocking mode, error is {:?}", + e ); } @@ -512,13 +486,10 @@ impl EventNotifierHelper for Chardev { /// Provide backend trait object receiving the input from the guest. pub trait CommunicatInInterface: std::marker::Send + std::os::unix::io::AsRawFd { fn chr_read_raw(&mut self, buf: &mut [u8]) -> Result { - use libc::read; - // Safe because this only read the bytes from terminal within the buffer. - let ret = unsafe { read(self.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len()) }; - if ret < 0 { - bail!("Failed to read buffer"); + match nix::unistd::read(self.as_raw_fd(), buf) { + Err(e) => bail!("Failed to read buffer: {:?}", e), + Ok(bytes) => Ok(bytes), } - Ok(ret as usize) } } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 03d901f49..0cedc1eb3 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -13,6 +13,7 @@ kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } kvm-ioctls = "0.13.0" log = "0.4" libc = "0.2" +nix = "0.26.2" vmm-sys-util = "0.11.1" hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index ff6834f3c..1406e30c2 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -67,6 +67,7 @@ use anyhow::{anyhow, Context, Result}; use kvm_ioctls::{VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info, warn}; +use nix::unistd::gettid; use vmm_sys_util::signal::{register_signal_handler, Killable}; use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; @@ -256,7 +257,7 @@ impl CPU { /// Set thread id for `CPU`. fn set_tid(&self) { - *self.tid.lock().unwrap() = Some(util::unix::gettid()); + *self.tid.lock().unwrap() = Some(gettid().as_raw() as u64); } } diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index 4311a5206..615e931d7 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -10,5 +10,6 @@ license = "Mulan PSL v2" thiserror = "1.0" anyhow = "1.0" libc = "0.2" +nix = "0.26.2" util = { path = "../util" } diff --git a/ozone/src/handler.rs b/ozone/src/handler.rs index e7570c55d..b72b09ccc 100644 --- a/ozone/src/handler.rs +++ b/ozone/src/handler.rs @@ -19,6 +19,7 @@ use std::{ }; use anyhow::{anyhow, bail, Context, Result}; +use nix::fcntl::{fcntl, FcntlArg}; use crate::cgroup::{self, init_cgroup, parse_cgroup, CgroupCfg}; use crate::OzoneError; @@ -337,8 +338,7 @@ fn disinfect_process() -> Result<()> { } for fd in open_fds { - let ret = unsafe { libc::fcntl(fd, libc::F_GETFD) }; - if ret != -1 { + if fcntl(fd, FcntlArg::F_GETFD).is_ok() { syscall::close(fd).with_context(|| format!("Failed to close fd: {}", fd))? } } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 1d1b8371d..4daabc1b3 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -1081,7 +1081,7 @@ fn create_file(gpu_info: &mut GpuInfo, dev_name: &String) -> Result { } let file_dir = path.display().to_string(); gpu_info.fileDir = file_dir.clone(); - let nsec = gettime().1; + let nsec = gettime()?.1; let file_name = file_dir + "/stratovirt-display-" + dev_name + "-" + &nsec.to_string() + ".png"; let file = fs::File::create(file_name)?; Ok(file) diff --git a/util/src/file.rs b/util/src/file.rs index 836dba131..c4eaeabc4 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -16,6 +16,8 @@ use std::os::unix::io::AsRawFd; use std::path::Path; use anyhow::{bail, Context, Ok, Result}; +use nix::fcntl::{fcntl, FcntlArg}; +use nix::unistd::getpid; const MIN_FILE_ALIGN: u32 = 512; const MAX_FILE_ALIGN: u32 = 4096; @@ -111,11 +113,10 @@ fn do_fcntl_lock( flock: libc::flock, is_lock: bool, ) -> Result<()> { - // SAFETY: the file has a valid raw fd. - let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLK, &flock) }; - if ret >= 0 { - return Ok(()); - } + let err = match fcntl(file.as_raw_fd(), FcntlArg::F_SETLK(&flock)) { + Err(e) => e, + _ => return Ok(()), + }; if is_lock { bail!( @@ -123,13 +124,13 @@ fn do_fcntl_lock( another process using the same file? Error: {}", lockname, path, - std::io::Error::last_os_error(), + err as i32, ); } else { bail!( "Failed to release lock on file: {}. Error: {}", path, - std::io::Error::last_os_error(), + err as i32, ); } } @@ -141,7 +142,7 @@ fn lock_or_unlock_file( lock_name: &str, is_lock: bool, ) -> Result<()> { - let pid = unsafe { libc::getpid() }; + let pid = getpid().as_raw(); let mut flock = libc::flock { l_whence: libc::SEEK_SET as i16, l_len: 1, diff --git a/util/src/lib.rs b/util/src/lib.rs index ecf8ded33..f62fb1e43 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -47,8 +47,8 @@ pub use error::UtilError; use std::{any::Any, sync::Mutex}; -use libc::{tcgetattr, tcsetattr, termios, OPOST, TCSANOW}; use log::debug; +use nix::sys::termios::{cfmakeraw, tcgetattr, tcsetattr, OutputFlags, SetArg, Termios}; use once_cell::sync::Lazy; use vmm_sys_util::terminal::Terminal; @@ -59,27 +59,23 @@ pub const VERSION: &str = concat!( include_str!(concat!(env!("OUT_DIR"), "/GIT_COMMIT")) ); -pub static TERMINAL_MODE: Lazy>> = Lazy::new(|| Mutex::new(None)); +pub static TERMINAL_MODE: Lazy>> = Lazy::new(|| Mutex::new(None)); pub fn set_termi_raw_mode() -> std::io::Result<()> { let tty_fd = std::io::stdin().lock().tty_fd(); - // Safe because this only set the `old_term_mode` struct to zero. - let mut old_term_mode: termios = unsafe { std::mem::zeroed() }; - // Safe because this only get stdin's current mode and save it. - let ret = unsafe { tcgetattr(tty_fd, &mut old_term_mode as *mut _) }; - if ret < 0 { - return Err(std::io::Error::last_os_error()); - } - *TERMINAL_MODE.lock().unwrap() = Some(old_term_mode); - - let mut new_term_mode: termios = old_term_mode; - // Safe because this function only change the `new_term_mode` argument. - unsafe { libc::cfmakeraw(&mut new_term_mode as *mut _) }; - new_term_mode.c_oflag |= OPOST; - // Safe because this function only set the stdin to raw mode. - let ret = unsafe { tcsetattr(tty_fd, TCSANOW, &new_term_mode as *const _) }; - if ret < 0 { + let old_term_mode = match tcgetattr(tty_fd) { + Ok(tm) => tm, + Err(_) => return Err(std::io::Error::last_os_error()), + }; + + *TERMINAL_MODE.lock().unwrap() = Some(old_term_mode.clone()); + + let mut new_term_mode = old_term_mode; + cfmakeraw(&mut new_term_mode); + new_term_mode.output_flags = new_term_mode.output_flags.union(OutputFlags::OPOST); + + if tcsetattr(tty_fd, SetArg::TCSANOW, &new_term_mode).is_err() { return Err(std::io::Error::last_os_error()); } @@ -89,9 +85,7 @@ pub fn set_termi_raw_mode() -> std::io::Result<()> { pub fn set_termi_canon_mode() -> std::io::Result<()> { let tty_fd = std::io::stdin().lock().tty_fd(); if let Some(old_term_mode) = TERMINAL_MODE.lock().unwrap().as_ref() { - // Safe because this only recover the stdin's mode. - let ret = unsafe { tcsetattr(tty_fd, TCSANOW, old_term_mode as *const _) }; - if ret < 0 { + if tcsetattr(tty_fd, SetArg::TCSANOW, old_term_mode).is_err() { return Err(std::io::Error::last_os_error()); } } else { diff --git a/util/src/logger.rs b/util/src/logger.rs index 7cb5e8e3d..889e788f9 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -20,9 +20,9 @@ use std::time::UNIX_EPOCH; use anyhow::{Context, Result}; use log::{Level, LevelFilter, Log, Metadata, Record}; +use nix::unistd::{getpid, gettid}; use crate::time::{get_format_time, gettime}; -use crate::unix::gettid; // Max size of the log file is 100MB. const LOG_ROTATE_SIZE_MAX: usize = 100 * 1024 * 1024; @@ -30,7 +30,10 @@ const LOG_ROTATE_SIZE_MAX: usize = 100 * 1024 * 1024; const LOG_ROTATE_COUNT_MAX: u32 = 7; fn format_now() -> String { - let (sec, nsec) = gettime(); + let (sec, nsec) = gettime().unwrap_or_else(|e| { + println!("{:?}", e); + (0, 0) + }); let format_time = get_format_time(sec as i64); format!( @@ -59,7 +62,7 @@ impl FileRotate { } self.current_size += Wrapping(size_inc); - let sec = gettime().0; + let sec = gettime()?.0; let today = get_format_time(sec as i64)[2]; if self.current_size < Wrapping(LOG_ROTATE_SIZE_MAX) && self.create_day == today { return Ok(()); @@ -114,8 +117,8 @@ impl Log for VmLogger { return; } - let pid = unsafe { libc::getpid() }; - let tid = gettid(); + let pid = getpid().as_raw(); + let tid = gettid().as_raw(); let formatmsg = format_args!( "{:<5}: [{}][{}][{}: {}]:{}: {}\n", format_now(), diff --git a/util/src/tap.rs b/util/src/tap.rs index a6480542b..0575289d2 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -18,6 +18,7 @@ use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use log::error; +use nix::fcntl::{fcntl, FcntlArg, OFlag}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; @@ -95,10 +96,10 @@ impl Tap { file = file_; } else if let Some(fd) = fd { - file = unsafe { - libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK); - File::from_raw_fd(fd) - }; + let fcnt_arg = FcntlArg::F_SETFL(OFlag::from_bits(libc::O_NONBLOCK).unwrap()); + fcntl(fd, fcnt_arg)?; + // SAFETY: the fd has been verified. + file = unsafe { File::from_raw_fd(fd) }; } else { return Err(anyhow!( "Open tap failed, unsupported operation, error is {}", diff --git a/util/src/time.rs b/util/src/time.rs index 7c8f5c0b9..a7981fcb7 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -10,6 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::{bail, Result}; +use nix::time::{clock_gettime, ClockId}; + pub const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000; /// Converts date to seconds since 1970-01-01 00:00:00. @@ -30,17 +33,11 @@ pub fn mktime64(year: u64, mon: u64, day: u64, hour: u64, min: u64, sec: u64) -> } /// Get wall time. -pub fn gettime() -> (u32, u32) { - let mut ts = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; - - unsafe { - libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts); +pub fn gettime() -> Result<(u32, u32)> { + match clock_gettime(ClockId::CLOCK_REALTIME) { + Ok(ts) => Ok((ts.tv_sec() as u32, ts.tv_nsec() as u32)), + Err(e) => bail!("clock_gettime failed: {:?}", e), } - - (ts.tv_sec as u32, ts.tv_nsec as u32) } /// Convert wall time to year/month/day/hour/minute/second format. diff --git a/util/src/unix.rs b/util/src/unix.rs index f0ba2348b..2d8cbc22f 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -23,14 +23,10 @@ use libc::{ MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, }; use log::error; +use nix::unistd::{sysconf, SysconfVar}; use crate::UtilError; -/// This function returns the caller's thread ID(TID). -pub fn gettid() -> u64 { - unsafe { libc::syscall(libc::SYS_gettid) as u64 } -} - /// This function used to remove group and others permission using libc::chmod. pub fn limit_permission(path: &str) -> Result<()> { let file_path = path.as_bytes().to_vec(); @@ -46,7 +42,15 @@ pub fn limit_permission(path: &str) -> Result<()> { /// Gets the page size of host. pub fn host_page_size() -> u64 { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as u64 } + let page_size = match sysconf(SysconfVar::PAGE_SIZE) { + Ok(Some(size)) => size, + Ok(None) => 0, + Err(e) => { + error!("Get host page size failed: {:?}", e); + 0 + } + }; + page_size as u64 } /// Parse unix uri to unix path. -- Gitee From c3d3a467c36a8c3e76eee4f90c046f7690048d74 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 14 Dec 2023 21:14:08 +0800 Subject: [PATCH 1511/2187] test: fix cpu hotplug test build failed Fixes: 7ba0791755c9 ("machine: simplified directory structure") Signed-off-by: yezengruan --- tests/mod_test/tests/x86_64/cpu_hotplug_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs b/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs index f21c2dd1a..b3b14123d 100644 --- a/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs +++ b/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs @@ -17,7 +17,7 @@ use std::time::Duration; use serde_json::{json, Value}; -use machine::standard_vm::x86_64::{LayoutEntryType, MEM_LAYOUT}; +use machine::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; const GED_ADDR_BASE: u64 = MEM_LAYOUT[LayoutEntryType::GedMmio as usize].0; -- Gitee From 6c6d21c69120713dcd78d9b91fc8f59351b8b5c5 Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 22 Nov 2023 10:19:52 +0800 Subject: [PATCH 1512/2187] Refactor: add document for unsafe code block Fix the warning created by clippy::undocumented_unsafe_blocks. Signed-off-by: Xiao Ye --- address_space/src/address_space.rs | 2 + address_space/src/host_mmap.rs | 14 ++++--- address_space/src/listener.rs | 28 +++++++++----- address_space/src/region.rs | 6 +++ block_backend/src/qcow2/mod.rs | 1 + block_backend/src/raw.rs | 1 + boot_loader/src/x86_64/bootparam.rs | 2 + cpu/src/aarch64/mod.rs | 2 +- cpu/src/x86_64/cpuid.rs | 1 + cpu/src/x86_64/mod.rs | 2 +- devices/src/camera_backend/v4l2.rs | 6 ++- devices/src/legacy/pflash.rs | 9 +++-- devices/src/legacy/ramfb.rs | 5 ++- devices/src/misc/scream/audio_demo.rs | 2 + devices/src/misc/scream/mod.rs | 1 + devices/src/pci/demo_device/dpy_device.rs | 7 +++- devices/src/pci/demo_device/gpu_device.rs | 2 + devices/src/scsi/bus.rs | 4 +- devices/src/scsi/disk.rs | 1 + devices/src/usb/usbhost/host_usblib.rs | 13 +++++-- devices/src/usb/usbhost/mod.rs | 15 ++++++-- devices/src/usb/xhci/xhci_controller.rs | 2 + hypervisor/src/kvm/mod.rs | 6 +-- machine/src/standard_vm/mod.rs | 1 + machine_manager/src/qmp/qmp_channel.rs | 4 ++ machine_manager/src/signal_handler.rs | 3 +- machine_manager/src/socket.rs | 14 +++++-- machine_manager/src/temp_cleaner.rs | 10 +++++ migration/src/general.rs | 15 +++++--- migration/src/migration.rs | 28 +++++++++----- migration/src/protocol.rs | 14 +++++-- ui/src/gtk/menu.rs | 5 ++- ui/src/gtk/mod.rs | 8 ++-- ui/src/pixman.rs | 20 ++++++++-- ui/src/vnc/auth_sasl.rs | 4 ++ ui/src/vnc/mod.rs | 4 +- ui/src/vnc/server_io.rs | 12 +++--- util/src/aio/libaio.rs | 5 ++- util/src/aio/mod.rs | 4 +- util/src/byte_code.rs | 2 + util/src/daemonize.rs | 6 +++ util/src/offsetof.rs | 4 +- util/src/pixman.rs | 1 + util/src/seccomp.rs | 2 + util/src/syscall.rs | 4 ++ util/src/tap.rs | 13 +++++-- util/src/test_helper.rs | 4 ++ util/src/time.rs | 2 + util/src/unix.rs | 45 +++++++++++++++-------- util/src/v4l2.rs | 17 +++++++++ vfio/src/vfio_dev.rs | 40 +++++++++++--------- vfio/src/vfio_pci.rs | 2 +- virtio/src/device/balloon.rs | 12 +++--- virtio/src/device/gpu.rs | 9 +++-- virtio/src/vhost/kernel/mod.rs | 26 ++++++++++++- virtio/src/vhost/kernel/net.rs | 1 + virtio/src/vhost/kernel/vsock.rs | 2 + virtio/src/vhost/user/client.rs | 22 +++++++---- 58 files changed, 359 insertions(+), 138 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index e6963e930..f646c615f 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -677,6 +677,7 @@ impl AddressSpace { // Mark vmm dirty page manually if live migration is active. MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); + // SAFETY: The host addr is managed by memory space, it has been verified. let mut dst = unsafe { std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; @@ -714,6 +715,7 @@ impl AddressSpace { pub fn read_object_direct(&self, host_addr: u64) -> Result { let mut obj = T::default(); let mut dst = obj.as_mut_bytes(); + // SAFETY: host_addr is managed by address_space, it has been verified for legality. let src = unsafe { std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) }; diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 3436f7ccc..0bc8b3cd0 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -184,7 +184,7 @@ fn max_nr_threads(nr_vcpus: u8) -> u8 { fn touch_pages(start: u64, page_size: u64, nr_pages: u64) { let mut addr = start; for _i in 0..nr_pages { - // Safe, because the data read from raw pointer is written to the same address. + // SAFETY: The data read from raw pointer is written to the same address. unsafe { let read_addr = addr as *mut u8; let data: u8 = *read_addr; @@ -255,7 +255,7 @@ pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Resu return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } - // SAFETY: the parameters is constant. + // SAFETY: The parameters is constant. let anon_file = unsafe { File::from_raw_fd(anon_fd) }; anon_file .set_len(mem_config.mem_size) @@ -303,7 +303,7 @@ pub fn create_backend_mem(mem_config: &MemZoneConfig, thread_num: u8) -> Result< return Err(std::io::Error::last_os_error()).with_context(|| "Failed to create memfd"); } - // SAFETY: the parameters is constant. + // SAFETY: The parameters is constant. let anon_file = unsafe { File::from_raw_fd(anon_fd) }; anon_file .set_len(mem_config.size) @@ -394,10 +394,11 @@ pub struct HostMemMapping { is_share: bool, } -// Send and Sync is not auto-implemented for raw pointer type -// implementing them is safe because field of HostMemMapping won't change once initialized, -// only access(r/w) is permitted +// SAFETY: Send and Sync is not auto-implemented for raw pointer type, +// implementing them is safe because field of HostMemMapping won't change +// once initialized, only access(r/w) is permitted unsafe impl Send for HostMemMapping {} +// SAFETY: Same reason as above. unsafe impl Sync for HostMemMapping {} impl HostMemMapping { @@ -476,6 +477,7 @@ impl HostMemMapping { impl Drop for HostMemMapping { /// Release the memory mapping. fn drop(&mut self) { + // SAFETY: self.host_addr and self.size has already been verified during initialization. unsafe { libc::munmap( self.host_addr as *mut libc::c_void, diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 044b11663..117d74187 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -256,12 +256,16 @@ impl KvmMemoryListener { userspace_addr: aligned_hva, flags, }; - unsafe { - KVM_FDS - .load() - .add_mem_slot(kvm_region) - .with_context(|| "Failed to add memory slot to kvm")?; + KVM_FDS + .load() + .add_mem_slot(kvm_region) + .with_context(|| "Failed to add memory slot to kvm")?; + + // SAFETY: All parameters in the struct of kvm_region are valid, + // it can be guaranteed that calling the ioctl_with_ref in the function + // of set_user_memory_region is safe. + unsafe { KVM_FDS .load() .vm_fd @@ -316,12 +320,16 @@ impl KvmMemoryListener { userspace_addr: mem_slot.host_addr, flags: 0, }; - unsafe { - KVM_FDS - .load() - .remove_mem_slot(kvm_region) - .with_context(|| "Failed to remove memory slot to kvm")?; + KVM_FDS + .load() + .remove_mem_slot(kvm_region) + .with_context(|| "Failed to remove memory slot to kvm")?; + + // SAFETY: All parameters in the struct of kvm_region are valid, + // it can be guaranteed that calling the ioctl_with_ref in the function + // of set_user_memory_region is safe. + unsafe { KVM_FDS .load() .vm_fd diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 6651ca83d..9c9404ac5 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -574,6 +574,8 @@ impl Region { AddressSpaceError::InvalidOffset(offset, count, self.size()) })?; let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); + // SAFETY: host_addr is managed by mem_mapping, it can be guaranteed to be legal, + // the legality of offset and count has been verified. let slice = unsafe { std::slice::from_raw_parts((host_addr + offset) as *const u8, count as usize) }; @@ -586,6 +588,8 @@ impl Region { })?; if self.rom_dev_romd.as_ref().load(Ordering::SeqCst) { let host_addr = self.mem_mapping.as_ref().unwrap().host_address(); + // SAFETY: host_addr is managed by mem_mapping, it can be guaranteed to be legal, + // the legality of offset and count has been verified. let read_ret = unsafe { std::slice::from_raw_parts( (host_addr + offset) as *const u8, @@ -673,6 +677,8 @@ impl Region { // Mark vmm dirty page manually if live migration is active. MigrationManager::mark_dirty_log(host_addr + offset, count); + // SAFETY: host_addr is managed by mem_mapping, it can be guaranteed to be legal, + // the legality of offset and count has been verified. let slice = unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, count as usize) }; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 1b3929840..f7ca80ef6 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1407,6 +1407,7 @@ impl InternalSnapshotOps for Qcow2Driver { // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. // We use Arc>> to allow used in multi-threading. unsafe impl Send for Qcow2Driver {} +// SAFETY: The reason is same as above. unsafe impl Sync for Qcow2Driver {} impl Qcow2Driver { diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index edf72e2ff..12e7a4ffc 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -34,6 +34,7 @@ pub struct RawDriver { // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. // We use Arc>> to allow used in multi-threading. unsafe impl Send for RawDriver {} +// SAFETY: The reason is same as above. unsafe impl Sync for RawDriver {} impl RawDriver { diff --git a/boot_loader/src/x86_64/bootparam.rs b/boot_loader/src/x86_64/bootparam.rs index 7efb5ab57..b51f792ae 100644 --- a/boot_loader/src/x86_64/bootparam.rs +++ b/boot_loader/src/x86_64/bootparam.rs @@ -171,6 +171,8 @@ impl ByteCode for BootParams {} impl Default for BootParams { fn default() -> Self { + // SAFETY: The function of default is only used in trait of ByteCode, + // it can be sure all member variables will be initialized later. unsafe { ::std::mem::zeroed() } } } diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index ea1dc5df8..486d18325 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -298,7 +298,7 @@ impl CPU { addr: 0, flags: 0, }; - // SAFETY: the fd can be guaranteed to be legal during creation. + // SAFETY: The fd can be guaranteed to be legal during creation. let vcpu_device = unsafe { DeviceFd::from_raw_fd(self.fd.as_raw_fd()) }; vcpu_device .has_device_attr(&pmu_attr) diff --git a/cpu/src/x86_64/cpuid.rs b/cpu/src/x86_64/cpuid.rs index 1084b271b..58ecfbd8d 100644 --- a/cpu/src/x86_64/cpuid.rs +++ b/cpu/src/x86_64/cpuid.rs @@ -20,6 +20,7 @@ pub fn host_cpuid( ecx: *mut u32, edx: *mut u32, ) { + // SAFETY: cpuid is created in get_supported_cpuid(). unsafe { let cpuid = __cpuid_count(leaf, subleaf); diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 4c1adaec3..a08047d58 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -266,7 +266,7 @@ impl X86CPUState { .get_lapic() .with_context(|| format!("Failed to get lapic for CPU {}/KVM", self.apic_id))?; - // The member regs in struct kvm_lapic_state is a u8 array with 1024 entries, + // SAFETY: The member regs in struct kvm_lapic_state is a u8 array with 1024 entries, // so it's saft to cast u8 pointer to u32 at position APIC_LVT0 and APIC_LVT1. // Safe because all value in this unsafe block is certain. unsafe { diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index c1106a2f5..897cd6015 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -175,9 +175,10 @@ impl V4l2CameraBackend { if frmsize.type_ != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { continue; } - // SAFETY: there are two enumeration types for v4l2_frmivalenum__bindgen_ty_1: discrete and stepwise. + // SAFETY: There are two enumeration types for v4l2_frmivalenum__bindgen_ty_1: discrete and stepwise. // Parsing will not result in undefined value. let width = unsafe { frmsize.__bindgen_anon_1.discrete.width }; + // SAFETY: The reason is same as above. let height = unsafe { frmsize.__bindgen_anon_1.discrete.height }; let interval_list = self.list_frame_interval(pixfmt, width, height)?; for interval in interval_list { @@ -211,9 +212,10 @@ impl V4l2CameraBackend { if frame_val.type_ != v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE { continue; } - // SAFETY: there are two enumeration types for v4l2_frmivalenum__bindgen_ty_1: discrete and stepwise. + // SAFETY: There are two enumeration types for v4l2_frmivalenum__bindgen_ty_1: discrete and stepwise. // Parsing will not result in undefined value. let numerator = unsafe { frame_val.__bindgen_anon_1.discrete.numerator }; + // SAFETY: The reason is as same above. let denominator = unsafe { frame_val.__bindgen_anon_1.discrete.denominator }; if denominator == 0 { warn!( diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index d3d51099b..c15c0afc2 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -329,8 +329,9 @@ impl PFlash { let addr: u64 = mr .get_host_address() .with_context(|| "Failed to get host address.")?; - let ret = unsafe { - // Safe as addr and size are valid. + let ret = + // SAFETY: addr and size are valid. + unsafe { libc::msync( addr as *mut libc::c_void, size as libc::size_t, @@ -355,8 +356,8 @@ impl PFlash { ))); } let host_addr = mr.get_host_address().unwrap(); - // Safe because host_addr of the region is local allocated and sanity has been checked. let src = + // SAFETY: host_addr of the region is local allocated and sanity has been checked. unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; data.as_mut() .write_all(src) @@ -376,8 +377,8 @@ impl PFlash { ))); } let host_addr = mr.get_host_address().unwrap(); - // Safe because host_addr of the region is local allocated and sanity has been checked. let mut dst = + // SAFETY: host_addr of the region is local allocated and sanity has been checked. unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; dst.write_all(data) .with_context(|| "Failed to write data to PFlash Rom")?; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 2d0f3978e..ab306fb0c 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -64,6 +64,7 @@ pub struct RamfbState { // modified by guest OS and accessed by vnc. So implement Sync and // Send is safe. unsafe impl Sync for RamfbState {} +// SAFETY: The reason is same as above. unsafe impl Send for RamfbState {} impl RamfbState { @@ -286,9 +287,9 @@ impl AmlBuilder for Ramfb { } fn set_press_event(install: Arc, data: *const u8) { - // SAFETY: data is the raw pointer of framebuffer. EDKII has malloc the memory of - // the framebuffer. So dereference the data is safe. let black_screen = + // SAFETY: data is the raw pointer of framebuffer. EDKII has malloc the memory of + // the framebuffer. So dereference the data is safe. unsafe { !data.is_null() && *data == 0 && *data.offset(1) == 0 && *data.offset(2) == 0 }; if install.load(Ordering::Acquire) && black_screen { let set_press_func = Box::new(move || { diff --git a/devices/src/misc/scream/audio_demo.rs b/devices/src/misc/scream/audio_demo.rs index 02f0435f6..9ce29e955 100644 --- a/devices/src/misc/scream/audio_demo.rs +++ b/devices/src/misc/scream/audio_demo.rs @@ -47,6 +47,7 @@ impl AudioDemo { impl AudioInterface for AudioDemo { fn send(&mut self, recv_data: &StreamData) { + // SAFETY: Audio demo device is only used for test. let data = unsafe { std::slice::from_raw_parts( recv_data.audio_base as *const u8, @@ -65,6 +66,7 @@ impl AudioInterface for AudioDemo { fn receive(&mut self, recv_data: &StreamData) -> bool { thread::sleep(time::Duration::from_millis(20)); + // SAFETY: Audio demo device is only used for test. let data = unsafe { std::slice::from_raw_parts_mut( recv_data.audio_base as *mut u8, diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 4fc0a5258..a21c09e8f 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -200,6 +200,7 @@ impl StreamData { while header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { thread::sleep(time::Duration::from_millis(10)); header = + // SAFETY: hva is allocated by libc:::mmap, it can be guaranteed to be legal. &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; } self.init(stream_header); diff --git a/devices/src/pci/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs index b46a61a6d..9b3f5ce0d 100644 --- a/devices/src/pci/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -40,7 +40,9 @@ use ui::{ }; use util::pixman::{pixman_format_bpp, pixman_image_get_stride, pixman_image_t}; +// SAFETY: Demo device is only used for test. unsafe impl Send for Surface {} +// SAFETY: Demo device is only used for test. unsafe impl Sync for Surface {} pub struct Surface { pub pixman_image: *mut pixman_image_t, @@ -88,6 +90,7 @@ impl DisplayChangeListenerOperations for DpyInterface { let res_data_ptr = get_image_data(surface.image) as *mut u8; let height = get_image_height(surface.image); let stride; + // SAFETY: Demo device is only used for test. unsafe { stride = pixman_image_get_stride(surface.image); } @@ -95,6 +98,7 @@ impl DisplayChangeListenerOperations for DpyInterface { let size = height * stride; let mut data: Vec = vec![0u8; size as usize]; + // SAFETY: Demo device is only used for test. unsafe { ptr::copy(res_data_ptr, data.as_mut_ptr(), size as usize); } @@ -118,6 +122,7 @@ impl DisplayChangeListenerOperations for DpyInterface { let bpp = pixman_format_bpp(get_image_format(ds.pixman_image) as u32); let stride; + // SAFETY: Demo device is only used for test. unsafe { stride = pixman_image_get_stride(ds.pixman_image); } @@ -132,7 +137,7 @@ impl DisplayChangeListenerOperations for DpyInterface { offset + count, ds.image[offset as usize] ); - + // SAFETY: Demo device is only used for test. unsafe { ptr::copy( res_data_ptr.add(offset as usize), diff --git a/devices/src/pci/demo_device/gpu_device.rs b/devices/src/pci/demo_device/gpu_device.rs index 9168e0582..7abef7125 100644 --- a/devices/src/pci/demo_device/gpu_device.rs +++ b/devices/src/pci/demo_device/gpu_device.rs @@ -74,6 +74,7 @@ pub struct DemoGpu { pub surface: Option, mouse: Option, } +// SAFETY: Demo device is only used for test. unsafe impl Send for DemoGpu {} impl DemoGpu { @@ -154,6 +155,7 @@ impl DemoGpu { let tmp_ptr = ptr as usize + 4 * j as usize; let rand_factor = (i * j) as usize; let len = UPDATE_FACTOR.len(); + // SAFETY: Demo device is only used for test. unsafe { // byte reverse by ^. *(tmp_ptr as *mut u8) ^= UPDATE_FACTOR[rand_factor % len]; diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index d90663eeb..cc091455b 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -709,7 +709,9 @@ impl ScsiRequest { } fn write_buf_mem(buf: &[u8], max: u64, hva: u64) -> Result { - let mut slice = unsafe { + let mut slice = + // SAFETY: The hva is managed by Address Space, it can be guaranteed to be legal. + unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, cmp::min(buf.len(), max as usize)) }; let size = (&mut slice) diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 44e678de5..52ad0534f 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -123,6 +123,7 @@ pub struct ScsiDevice { // SAFETY: the devices attached in one scsi controller will process IO in the same thread. unsafe impl Send for ScsiDevice {} +// SAFETY: The reason is same as above. unsafe impl Sync for ScsiDevice {} impl ScsiDevice { diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 33d7cd920..3ee20a0b9 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -266,6 +266,7 @@ pub fn fill_transfer_by_type( ) { // SAFETY: node only deleted when request completed. let packet = unsafe { (*node).value.packet.clone() }; + // SAFETY: the reason is same as above. let buffer_ptr = unsafe { (*node).value.buffer.as_mut_ptr() }; let size = packet.lock().unwrap().get_iovecs_size(); @@ -277,7 +278,9 @@ pub fn fill_transfer_by_type( // SAFETY: have checked the validity of parameters of libusb_fill_*_transfer // before call libusb_fill_*_transfer. match transfer_type { - TransferType::Control => unsafe { + TransferType::Control => + // SAFETY: the reason is as shown above. + unsafe { libusb1_sys::libusb_fill_control_transfer( transfer, handle.unwrap().as_raw(), @@ -287,7 +290,9 @@ pub fn fill_transfer_by_type( CONTROL_TIMEOUT, ); }, - TransferType::Bulk => unsafe { + TransferType::Bulk => + // SAFETY: the reason is as shown above. + unsafe { libusb1_sys::libusb_fill_bulk_transfer( transfer, handle.unwrap().as_raw(), @@ -299,7 +304,9 @@ pub fn fill_transfer_by_type( BULK_TIMEOUT, ); }, - TransferType::Interrupt => unsafe { + TransferType::Interrupt => + // SAFETY: the reason is as shown above. + unsafe { libusb1_sys::libusb_fill_interrupt_transfer( transfer, handle.unwrap().as_raw(), diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 879273e17..0622d5176 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -147,7 +147,11 @@ impl UsbHostRequest { } } +// SAFETY: The UsbHostRequest is created in main thread and then be passed to the +// libUSB thread. Once this data is processed, it is cleaned up. So there will be +// no problem with data sharing or synchronization. unsafe impl Sync for UsbHostRequest {} +// SAFETY: The reason is same as above. unsafe impl Send for UsbHostRequest {} pub struct IsoTransfer { @@ -235,9 +239,9 @@ impl IsoTransfer { // and packet is guaranteed to be not out of boundary. unsafe { libusb_get_iso_packet_buffer_simple(self.host_transfer, self.packet) }; - // SAFETY: buffer is already allocated and size will not be exceed - // the size of buffer. lockecd_packet.transfer_packet( + // SAFETY: buffer is already allocated and size will not be exceed + // the size of buffer. unsafe { std::slice::from_raw_parts_mut(buffer, size) }, size, ); @@ -248,7 +252,9 @@ impl IsoTransfer { } } +// SAFETY: The operation of libusb_transfer is protected by lock. unsafe impl Sync for IsoTransfer {} +// SAFETY: The reason is same as above. unsafe impl Send for IsoTransfer {} pub struct IsoQueue { @@ -360,6 +366,7 @@ pub struct UsbHost { // SAFETY: Send and Sync is not auto-implemented for util::link_list::List. // Implementing them is safe because List add Mutex. unsafe impl Sync for UsbHost {} +// SAFETY: The reason is same as above. unsafe impl Send for UsbHost {} impl UsbHost { @@ -586,9 +593,9 @@ impl UsbHost { fn register_exit(&mut self) { let exit = self as *const Self as u64; let exit_notifier = Arc::new(move || { - // SAFETY: This callback is deleted after the device hot-unplug, so it is called only - // when the vm exits abnormally. let usb_host = + // SAFETY: This callback is deleted after the device hot-unplug, so it is called only + // when the vm exits abnormally. &mut unsafe { std::slice::from_raw_parts_mut(exit as *mut UsbHost, 1) }[0]; usb_host.release_dev_to_host(); }) as Arc; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a49813d99..99b26c353 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -662,10 +662,12 @@ impl DwordOrder for XhciEpCtx {} pub trait DwordOrder: Default + Copy + Send + Sync { fn as_dwords(&self) -> &[u32] { + // SAFETY: Tt can be guaranteed that self has been initialized. unsafe { from_raw_parts(self as *const Self as *const u32, size_of::() / 4) } } fn as_mut_dwords(&mut self) -> &mut [u32] { + // SAFETY: Tt can be guaranteed that self has been initialized. unsafe { from_raw_parts_mut(self as *mut Self as *mut u32, size_of::() / 4) } } } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index db27fa433..16361f4d9 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -140,7 +140,7 @@ impl KVMFds { std::cmp::max(align_of::(), align_of::()), )?; - // Safe because data in `routes` is reliable. + // SAFETY: data in `routes` is reliable. unsafe { let irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; if irq_routing.is_null() { @@ -191,7 +191,7 @@ impl KVMFds { pub fn start_dirty_log(&self) -> Result<()> { for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { region.flags = KVM_MEM_LOG_DIRTY_PAGES; - // Safe because region from `KVMFds` is reliable. + // SAFETY: region from `KVMFds` is reliable. unsafe { self.vm_fd .as_ref() @@ -213,7 +213,7 @@ impl KVMFds { pub fn stop_dirty_log(&self) -> Result<()> { for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { region.flags = 0; - // Safe because region from `KVMFds` is reliable. + // SAFETY: region from `KVMFds` is reliable. unsafe { self.vm_fd .as_ref() diff --git a/machine/src/standard_vm/mod.rs b/machine/src/standard_vm/mod.rs index de5fb9d2a..9fcd695c6 100644 --- a/machine/src/standard_vm/mod.rs +++ b/machine/src/standard_vm/mod.rs @@ -1765,6 +1765,7 @@ impl DeviceInterface for StdMachine { let ptr: *const u8 = data.as_ptr(); let ptr: *const u64 = ptr as *const u64; + // SAFETY: The ptr can be guaranteed not empty. self.head = unsafe { *ptr } * 2; true } diff --git a/machine_manager/src/qmp/qmp_channel.rs b/machine_manager/src/qmp/qmp_channel.rs index 42ee1afca..42b0f441e 100644 --- a/machine_manager/src/qmp/qmp_channel.rs +++ b/machine_manager/src/qmp/qmp_channel.rs @@ -94,6 +94,8 @@ pub struct QmpChannel { impl QmpChannel { /// Constructs a `QmpChannel` in global `QMP_CHANNEL`. pub fn object_init() { + // SAFETY: Global variable QMP_CHANNEL is only used in the main thread, + // so there are no competition or synchronization. unsafe { if QMP_CHANNEL.is_none() { QMP_CHANNEL = Some(Arc::new(QmpChannel { @@ -168,6 +170,8 @@ impl QmpChannel { } fn inner() -> &'static std::sync::Arc { + // SAFETY: Global variable QMP_CHANNEL is only used in the main thread, + // so there are no competition or synchronization. unsafe { match &QMP_CHANNEL { Some(channel) => channel, diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 49ebffad9..6b922aac5 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -31,7 +31,7 @@ const SYSTEMCALL_OFFSET: isize = 6; static mut RECEIVED_SIGNAL: AtomicI32 = AtomicI32::new(0); pub fn exit_with_code(code: i32) { - // Safe, because the basic_clean function has been executed before exit. + // SAFETY: The basic_clean function has been executed before exit. unsafe { libc::_exit(code); } @@ -86,6 +86,7 @@ extern "C" fn receive_signal_kill(num: c_int, _: *mut siginfo_t, _: *mut c_void) extern "C" fn receive_signal_sys(num: c_int, info: *mut siginfo_t, _: *mut c_void) { set_signal(num); + // SAFETY: The safety of this function is guaranteed by caller. let badcall = unsafe { *(info as *const i32).offset(SYSTEMCALL_OFFSET) as usize }; write!( &mut std::io::stderr(), diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 33535c3b9..6bca5b216 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -95,11 +95,13 @@ impl SocketRWHandler { fn parse_fd(&mut self, mhdr: &msghdr) { // At least it should has one RawFd. + // SAFETY: The input parameter is constant. let min_cmsg_len = unsafe { CMSG_LEN(size_of::() as u32) as u64 }; if (mhdr.msg_controllen as u64) < min_cmsg_len { return; } + // SAFETY: The pointer of mhdr can be guaranteed not null. let mut cmsg_hdr = unsafe { CMSG_FIRSTHDR(mhdr as *const msghdr).as_ref() }; while cmsg_hdr.is_some() { let scm = cmsg_hdr.unwrap(); @@ -107,6 +109,7 @@ impl SocketRWHandler { && scm.cmsg_type == SCM_RIGHTS && scm.cmsg_len as u64 >= min_cmsg_len { + // SAFETY: The pointer of scm can be guaranteed not null. let fds = unsafe { let fd_num = (scm.cmsg_len as u64 - CMSG_LEN(0) as u64) as usize / size_of::(); @@ -114,6 +117,7 @@ impl SocketRWHandler { }; self.scm_fd.append(&mut fds.to_vec()); } + // SAFETY: The pointer of mhdr can be guaranteed not null. cmsg_hdr = unsafe { CMSG_NXTHDR(mhdr as *const msghdr, scm).as_ref() }; } } @@ -137,9 +141,10 @@ impl SocketRWHandler { }; let mut cmsg_space = [0_u8; MAX_RECV_FDS_LEN]; loop { - // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be - // initialized in normal way. - let mut mhdr: msghdr = unsafe { std::mem::zeroed() }; + let mut mhdr: msghdr = + // SAFETY: In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be + // initialized in normal way. + unsafe { std::mem::zeroed() }; mhdr.msg_name = std::ptr::null_mut(); mhdr.msg_namelen = 0; mhdr.msg_iov = &mut iov as *mut iovec; @@ -150,6 +155,7 @@ impl SocketRWHandler { // MSG_DONTWAIT: Enables nonblocking operation, if the operation would block the call // fails with the error EAGAIN or EWOULDBLOCK. When this error occurs, break loop + // SAFETY: The pointer of mhdr can been guaranteed not null. let ret = unsafe { recvmsg(self.socket_fd, &mut mhdr, MSG_DONTWAIT) }; // when use tcpsocket client and exit with ctrl+c, ret value will return 0 and get // error WouldBlock or BrokenPipe, so we should handle this 0 to break this loop. @@ -198,6 +204,7 @@ impl SocketRWHandler { // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. + // SAFETY: The member variables of mhdr have been initialization later. let mut mhdr: msghdr = unsafe { std::mem::zeroed() }; mhdr.msg_name = std::ptr::null_mut(); mhdr.msg_namelen = 0; @@ -207,6 +214,7 @@ impl SocketRWHandler { mhdr.msg_controllen = 0; mhdr.msg_flags = 0; + // SAFETY: The buffer address and length recorded in mhdr are both legal. if unsafe { sendmsg(self.socket_fd, &mhdr, MSG_NOSIGNAL) } == -1 { Err(Error::new( ErrorKind::BrokenPipe, diff --git a/machine_manager/src/temp_cleaner.rs b/machine_manager/src/temp_cleaner.rs index 0198808f0..2bdec2954 100644 --- a/machine_manager/src/temp_cleaner.rs +++ b/machine_manager/src/temp_cleaner.rs @@ -31,6 +31,8 @@ pub struct TempCleaner { impl TempCleaner { pub fn object_init() { + // SAFETY: This global variable is only used in single thread, + // so there is no data competition or synchronization problem. unsafe { if GLOBAL_TEMP_CLEANER.is_none() { GLOBAL_TEMP_CLEANER = Some(TempCleaner { @@ -43,6 +45,8 @@ impl TempCleaner { /// Add to be removed file path pub fn add_path(path: String) { + // SAFETY: This global variable is only used in single thread, + // so there is no data competition or synchronization problem. unsafe { if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { tmp.paths.push(path); @@ -52,6 +56,8 @@ impl TempCleaner { /// Add exit notifier. pub fn add_exit_notifier(id: String, exit: Arc) { + // SAFETY: This global variable is only used in single thread, + // so there is no data competition or synchronization problem. unsafe { if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { tmp.notifiers.insert(id, exit); @@ -61,6 +67,8 @@ impl TempCleaner { /// Remove exit notifier by id. pub fn remove_exit_notifier(id: &str) { + // SAFETY: This global variable is only used in single thread, + // so there is no data competition or synchronization problem. unsafe { if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { tmp.notifiers.remove(id); @@ -106,6 +114,8 @@ impl TempCleaner { /// Clean the resources pub fn clean() { + // SAFETY: This global variable is only used in single thread, + // so there is no data competition or synchronization problem. unsafe { if let Some(tmp) = GLOBAL_TEMP_CLEANER.as_mut() { tmp.clean_files(); diff --git a/migration/src/general.rs b/migration/src/general.rs index 950e613d9..0777e0de1 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -165,12 +165,15 @@ impl MigrationManager { desc_db: &HashMap, ) -> Result<(Vec, u64)> { let mut instance = Instance::default(); - fd.read_exact(unsafe { - std::slice::from_raw_parts_mut( - &mut instance as *mut Instance as *mut u8, - size_of::(), - ) - }) + fd.read_exact( + // SAFETY: The pointer of instance can guaranteed not null. + unsafe { + std::slice::from_raw_parts_mut( + &mut instance as *mut Instance as *mut u8, + size_of::(), + ) + }, + ) .with_context(|| "Failed to read instance of object")?; let locked_desc_db = MIGRATION_MANAGER.desc_db.read().unwrap(); diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 98e24ca17..2f662b688 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -331,12 +331,17 @@ impl MigrationManager { let mut blocks = Vec::::new(); blocks.resize_with(len as usize / (size_of::()), Default::default); - fd.read_exact(unsafe { - std::slice::from_raw_parts_mut( - blocks.as_ptr() as *mut MemBlock as *mut u8, - len as usize, - ) - })?; + fd.read_exact( + // SAFETY: + // 1. The pointer of blocks can be guaranteed not null. + // 2. The range of len has been limited. + unsafe { + std::slice::from_raw_parts_mut( + blocks.as_ptr() as *mut MemBlock as *mut u8, + len as usize, + ) + }, + )?; if let Some(locked_memory) = &MIGRATION_MANAGER.vmm.read().unwrap().memory { for block in blocks.iter() { @@ -367,9 +372,14 @@ impl MigrationManager { { let len = size_of::() * blocks.len(); Request::send_msg(fd, TransStatus::Memory, len as u64)?; - fd.write_all(unsafe { - std::slice::from_raw_parts(blocks.as_ptr() as *const MemBlock as *const u8, len) - })?; + fd.write_all( + // SAFETY: + // 1. The pointer of blocks can be guaranteed not null. + // 2. The len is constant. + unsafe { + std::slice::from_raw_parts(blocks.as_ptr() as *const MemBlock as *const u8, len) + }, + )?; if let Some(locked_memory) = &MIGRATION_MANAGER.vmm.read().unwrap().memory { for block in blocks.iter() { diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index fbaaa8371..0a00aa6cd 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -187,6 +187,7 @@ impl Request { pub fn send_msg(fd: &mut dyn Write, status: TransStatus, length: u64) -> Result<()> { let request = Request { length, status }; let data = + // SAFETY: The pointer of request can be guaranteed not null. unsafe { from_raw_parts(&request as *const Self as *const u8, size_of::()) }; fd.write_all(data) .with_context(|| format!("Failed to write request data {:?}", data))?; @@ -205,7 +206,9 @@ impl Request { /// The socket file descriptor is broken. pub fn recv_msg(fd: &mut dyn Read) -> Result { let mut request = Request::default(); - let data = unsafe { + let data = + // SAFETY: The pointer of request can be guaranteed not null. + unsafe { from_raw_parts_mut(&mut request as *mut Request as *mut u8, size_of::()) }; fd.read_exact(data) @@ -239,6 +242,7 @@ impl Response { pub fn send_msg(fd: &mut dyn Write, status: TransStatus) -> Result<()> { let response = Response { status }; let data = + // SAFETY: The pointer of response can be guaranteed not null. unsafe { from_raw_parts(&response as *const Self as *const u8, size_of::()) }; fd.write_all(data) .with_context(|| format!("Failed to write response data {:?}", data))?; @@ -257,7 +261,9 @@ impl Response { /// The socket file descriptor is broken. pub fn recv_msg(fd: &mut dyn Read) -> Result { let mut response = Response::default(); - let data = unsafe { + let data = + // SAFETY: The pointer of response can be guaranteed not null. + unsafe { from_raw_parts_mut(&mut response as *mut Response as *mut u8, size_of::()) }; fd.read_exact(data) @@ -326,11 +332,11 @@ impl EndianType { fn cpu_model() -> [u8; 16] { use core::arch::x86_64::__cpuid_count; - // Safe because we only use cpuid for cpu info in x86_64. + // SAFETY: We only use cpuid for cpu info in x86_64. let result = unsafe { __cpuid_count(EAX_VENDOR_INFO, 0) }; let vendor_slice = [result.ebx, result.edx, result.ecx]; - // Safe because we known those brand string length. + // SAFETY: We known those brand string length. let vendor_array = unsafe { let brand_string_start = vendor_slice.as_ptr() as *const u8; std::slice::from_raw_parts(brand_string_start, 3 * 4) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index 0c451b191..e89b7a3ea 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -235,6 +235,7 @@ impl GtkMenu { // Disable the default F10 menu shortcut. if let Some(setting) = self.window.settings() { + // SAFETY: self.windows can be guaranteed to be legal. unsafe { gtk::glib::gobject_ffi::g_object_set_property( setting.as_ptr() as *mut GObject, @@ -383,7 +384,7 @@ fn window_close_callback(gd: &Rc>) -> Result<()> { )); if let Some(button_yes) = &dialog.widget_for_response(gtk::ResponseType::Yes) { let label: &str = &gettext("Yes"); - // SAFETY: it can be ensure that the pointer is not empty. + // SAFETY: Tt can be ensure that the pointer is not empty. unsafe { let button: *mut GtkWidget = button_yes.as_ptr(); gtk_button_set_label(button as *mut GtkButton, label.to_glib_none().0); @@ -391,7 +392,7 @@ fn window_close_callback(gd: &Rc>) -> Result<()> { } if let Some(button_no) = dialog.widget_for_response(gtk::ResponseType::No) { let label: &str = &gettext("No"); - // SAFETY: it can be ensure that the pointer is not empty. + // SAFETY: Tt can be ensure that the pointer is not empty. unsafe { let button: *mut GtkWidget = button_no.as_ptr(); gtk_button_set_label(button as *mut GtkButton, label.to_glib_none().0); diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 4daabc1b3..cc2467009 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -875,10 +875,11 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { if borrowed_gs.source_surface.format == pixman_format_code_t::PIXMAN_x8r8g8b8 { let data = get_image_data(borrowed_gs.source_surface.image) as *mut u8; + borrowed_gs.cairo_image = // SAFETY: // 1. It can be sure that the ptr of data is not nullptr. // 2. The copy range will not exceed the image data. - borrowed_gs.cairo_image = unsafe { + unsafe { ImageSurface::create_for_data_unsafe( data as *mut u8, Format::Rgb24, @@ -897,11 +898,12 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { surface_stride, ); + let data = get_image_data(transfer_image) as *mut u8; + borrowed_gs.cairo_image = // SAFETY: // 1. It can be sure that the ptr of data is not nullptr. // 2. The copy range will not exceed the image data. - let data = get_image_data(transfer_image) as *mut u8; - borrowed_gs.cairo_image = unsafe { + unsafe { ImageSurface::create_for_data_unsafe( data as *mut u8, Format::Rgb24, diff --git a/ui/src/pixman.rs b/ui/src/pixman.rs index 853380af8..8c5a20087 100644 --- a/ui/src/pixman.rs +++ b/ui/src/pixman.rs @@ -119,6 +119,8 @@ pub fn get_image_width(image: *mut pixman_image_t) -> i32 { if image.is_null() { return 0; } + + // SAFETY: The reason is the same as above. unsafe { pixman_image_get_width(image as *mut pixman_image_t) } } @@ -126,6 +128,8 @@ pub fn get_image_height(image: *mut pixman_image_t) -> i32 { if image.is_null() { return 0; } + + // SAFETY: The reason is the same as above. unsafe { pixman_image_get_height(image as *mut pixman_image_t) } } @@ -133,6 +137,8 @@ pub fn get_image_stride(image: *mut pixman_image_t) -> i32 { if image.is_null() { return 0; } + + // SAFETY: The reason is the same as above. unsafe { pixman_image_get_stride(image as *mut pixman_image_t) } } @@ -140,6 +146,8 @@ pub fn get_image_data(image: *mut pixman_image_t) -> *mut u32 { if image.is_null() { return ptr::null_mut() as *mut u32; } + + // SAFETY: The reason is the same as above. unsafe { pixman_image_get_data(image as *mut pixman_image_t) } } @@ -147,6 +155,8 @@ pub fn get_image_format(image: *mut pixman_image_t) -> pixman_format_code_t { if image.is_null() { return pixman_format_code_t::PIXMAN_x8r8g8b8; } + + // SAFETY: The reason is the same as above. unsafe { pixman_image_get_format(image as *mut pixman_image_t) } } @@ -161,6 +171,7 @@ pub fn create_pixman_image( return ptr::null_mut() as *mut pixman_image_t; } + // SAFETY: The reason is the same as above. unsafe { pixman_image_create_bits(image_format, width, height, image_data as *mut u32, stride) } } @@ -177,6 +188,7 @@ pub fn unref_pixman_image(image: *mut pixman_image_t) { if image.is_null() { return; } + // SAFETY: The reason is the same as above. unsafe { pixman_image_unref(image as *mut pixman_image_t) }; } @@ -188,6 +200,7 @@ pub fn ref_pixman_image(image: *mut pixman_image_t) -> *mut pixman_image_t { if image.is_null() { return ptr::null_mut() as *mut pixman_image_t; } + // SAFETY: The reason is the same as above. unsafe { pixman_image_ref(image as *mut pixman_image_t) } } @@ -199,6 +212,7 @@ pub fn pixman_image_linebuf_create( if !(0..MAX_IMAGE_SIZE).contains(&width) { return ptr::null_mut() as *mut pixman_image_t; } + // SAFETY: The reason is the same as above. unsafe { pixman_image_create_bits(image_format, width, 1, ptr::null_mut(), 0) } } @@ -217,7 +231,7 @@ pub fn pixman_image_linebuf_fill( { return; }; - + // SAFETY: The reason is the same as above. unsafe { pixman_image_composite( pixman_op_t::PIXMAN_OP_SRC, @@ -625,7 +639,7 @@ pub fn pixman_glyph_from_vgafont(height: u32, ch: u32) -> *mut pixman_image_t { let mut data_index: usize = 0; let mut font_index: usize = height as usize * ch as usize; let slice = - // SAFETY: todo! + // SAFETY: The pointer of data can be guaranteed not null. unsafe { std::slice::from_raw_parts_mut(data, height as usize * 8 ) }; @@ -659,7 +673,7 @@ pub fn pixman_glyph_render( let bgcolor = bgcolor as *const pixman_color_t; let (x, y) = rec; - // SAFETY: todo! + // SAFETY: All pointers can be guaranteed not be null. unsafe { let ifg = pixman_image_create_solid_fill(fgcolor); let ibg = pixman_image_create_solid_fill(bgcolor); diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 7acee827d..0e322403c 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -223,6 +223,7 @@ impl ClientIoHandler { &mut serverout_len, ) }, + // SAFETY: The reason is same as above. SaslStage::SaslServerStep => unsafe { sasl_server_step( security.saslconfig.sasl_conn, @@ -244,6 +245,7 @@ impl ClientIoHandler { ))); } if serverout_len > SASL_DATA_MAX_LEN { + // SAFETY: The reason is same as above. unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) } return Err(anyhow!(VncError::AuthFailed( "client_sasl_auth".to_string(), @@ -254,6 +256,7 @@ impl ClientIoHandler { let mut buf = Vec::new(); if serverout_len > 0 { // Authentication related information. + // SAFETY: pointer of serverout can be guaranteed not null. let serverout = unsafe { CStr::from_ptr(serverout as *const c_char) }; let auth_message = String::from(serverout.to_str().unwrap_or("")); buf.append(&mut (serverout_len + 1).to_be_bytes().to_vec()); @@ -325,6 +328,7 @@ impl ClientIoHandler { ))); } let mut saslconfig = SaslConfig::default(); + // SAFETY: The reason is same as above. unsafe { err = sasl_server_new( service.as_ptr(), diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index fb0f56fb4..2cc2a2a40 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -534,7 +534,7 @@ pub fn write_pixel( ) { if !client_dpm.convert { let mut con = vec![0; copy_bytes]; - // SAFETY: it can be ensure the raw pointer will not exceed the range. + // SAFETY: Tt can be ensure the raw pointer will not exceed the range. unsafe { ptr::copy(data_ptr as *mut u8, con.as_mut_ptr(), copy_bytes); } @@ -543,7 +543,7 @@ pub fn write_pixel( let num = copy_bytes >> 2; let ptr = data_ptr as *mut u32; for i in 0..num { - // SAFETY: it can be ensure the raw pointer will not exceed the range. + // SAFETY: Tt can be ensure the raw pointer will not exceed the range. let color = unsafe { *ptr.add(i) }; convert_pixel(client_dpm, buf, color); } diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index a438a5e34..2e91e83bd 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -77,13 +77,11 @@ pub struct VncServer { pub conn_limits: usize, } -// SAFETY: -// 1. The raw pointer in rust doesn't impl Send, the target thread can only read the memory of image -// by this pointer. -// 2. It can be sure that Rc> and Rc> are used only in -// single thread. -// So implement Send and Sync is safe. +// SAFETY: The raw pointer in rust doesn't impl Send, the target thread can only read the +// memory of image by this pointer. unsafe impl Send for VncServer {} +// SAFETY: Tt can be guaranteed that Rc> and Rc> +// are used only in single thread. unsafe impl Sync for VncServer {} impl VncServer { @@ -431,7 +429,7 @@ impl VncSurface { _cmp_bytes = line_bytes as usize - x * cmp_bytes; } - // SAFETY: it can be ensure the raw pointer will not exceed the range. + // SAFETY: Tt can be ensure the raw pointer will not exceed the range. unsafe { if libc::memcmp( s_info.ptr as *mut libc::c_void, diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 915cdf3e1..85fd20c63 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -155,8 +155,8 @@ impl AioContext for LibaioContext { iocbp.push(iocb); } - // SAFETY: self.ctx is generated by SYS_io_setup. let ret = + // SAFETY: self.ctx is generated by SYS_io_setup. unsafe { libc::syscall(libc::SYS_io_submit, self.ctx, iocbp.len(), iocbp.as_ptr()) }; if ret >= 0 { return Ok(ret as usize); @@ -171,8 +171,11 @@ impl AioContext for LibaioContext { let ring = self.ctx as *mut AioRing; // SAFETY: self.ctx is generated by SYS_io_setup. let head = unsafe { (*ring).head }; + // SAFETY: self.ctx is generated by SYS_io_setup. let tail = unsafe { (*ring).tail }; + // SAFETY: self.ctx is generated by SYS_io_setup. let ring_nr = unsafe { (*ring).nr }; + // SAFETY: self.ctx is generated by SYS_io_setup. let io_events: &[IoEvent] = unsafe { (*ring).io_events.as_slice(ring_nr as usize) }; let nr = if tail >= head { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 0498c27a0..9a9547eac 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -248,9 +248,9 @@ impl Aio { .with_context(|| "Failed to round down request length.")?; // Set upper limit of buffer length to avoid OOM. let buff_len = cmp::min(max_len, MAX_LEN_BOUNCE_BUFF); - // SAFETY: we allocate aligned memory and free it later. Alignment is set to - // host page size to decrease the count of allocated pages. let bounce_buffer = + // SAFETY: We allocate aligned memory and free it later. Alignment is set to + // host page size to decrease the count of allocated pages. unsafe { libc::memalign(host_page_size() as usize, buff_len as usize) }; if bounce_buffer.is_null() { error!("Failed to alloc memory for misaligned read/write."); diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 1673aaa73..a818d24ea 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -19,12 +19,14 @@ pub trait ByteCode: Default + Copy + Send + Sync { /// Return the contents of an object (impl trait `ByteCode`) as a slice of bytes. /// the inverse of this function is "from_bytes" fn as_bytes(&self) -> &[u8] { + // SAFETY: The object is guaranteed been initialized already. unsafe { from_raw_parts(self as *const Self as *const u8, size_of::()) } } /// Return the contents of a mutable object (impl trait `ByteCode`) to a mutable slice of bytes. /// the inverse of this function is "from_bytes_mut" fn as_mut_bytes(&mut self) -> &mut [u8] { + // SAFETY: The object is guaranteed been initialized already. unsafe { from_raw_parts_mut(self as *mut Self as *mut u8, size_of::()) } } diff --git a/util/src/daemonize.rs b/util/src/daemonize.rs index a9fe6af46..d62440650 100644 --- a/util/src/daemonize.rs +++ b/util/src/daemonize.rs @@ -70,6 +70,8 @@ fn create_pid_file(path: &str) -> Result<()> { /// /// `DaemonFork` Error, the ret of `libc::fork()` is less than zero. fn fork() -> Result<()> { + // SAFETY: No input parameters in this system call. + // and the return value have been verified later. let ret = unsafe { libc::fork() }; match ret.cmp(&0) { @@ -91,6 +93,8 @@ fn fork() -> Result<()> { /// /// `DaemonSetsid` Error, the ret of `libc::setsid()` is -1 fn set_sid() -> Result<()> { + // SAFETY: No input parameters in this system call. + // and the return value have been verified later. let ret = unsafe { libc::setsid() }; if ret == -1 { @@ -113,6 +117,8 @@ fn set_sid() -> Result<()> { /// `DaemonRedirectStdio` Error, the ret of `libc::open()`, `libc::dup2()`, /// `libc::close()`is -1 fn redirect_stdio(fd: RawFd) -> Result<()> { + // SAFETY: the input parameter for systemctl are constant,and the return + // value have been verified later. unsafe { let devnull_fd = libc::open(b"/dev/null\0" as *const [u8; 10] as _, libc::O_RDWR); diff --git a/util/src/offsetof.rs b/util/src/offsetof.rs index da6f4c4d8..2cc01699b 100644 --- a/util/src/offsetof.rs +++ b/util/src/offsetof.rs @@ -16,11 +16,11 @@ macro_rules! __offset_of { ($type_name:ty, $field:ident) => {{ let tmp = core::mem::MaybeUninit::<$type_name>::uninit(); let outer = tmp.as_ptr(); - // Safe because the pointer is valid and aligned, just not initialised; `addr_of` ensures + // SAFETY: The pointer is valid and aligned, just not initialised; `addr_of` ensures // that we don't actually read from `outer` (which would be UB) nor create an // intermediate reference. let inner = unsafe { core::ptr::addr_of!((*outer).$field) } as *const u8; - // Safe because the two pointers are within the same allocation block. + // SAFETY: Two pointers are within the same allocation block. unsafe { inner.offset_from(outer as *const u8) as usize } }}; } diff --git a/util/src/pixman.rs b/util/src/pixman.rs index d88b646e2..50fc5d28f 100644 --- a/util/src/pixman.rs +++ b/util/src/pixman.rs @@ -192,6 +192,7 @@ pub extern "C" fn virtio_gpu_unref_resource_callback( _image: *mut pixman_image_t, data: *mut libc::c_void, ) { + // SAFETY: The safety of this function is guaranteed by caller. unsafe { pixman_image_unref(data.cast()) }; } diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index c5ea7e34d..41206a3bc 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -439,6 +439,7 @@ impl SyscallFilter { let sock_bpf_vec = self.sock_filters; // This operation can guarantee seccomp make use for all users and subprocess. + // SAFETY: All input parameters are constants. let ret = unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) }; if ret != 0 { bail!("Seccomp: prctl(2) set no new privs failed."); @@ -451,6 +452,7 @@ impl SyscallFilter { let bpf_prog_ptr = &prog as *const SockFProg; // Use seccomp(2) to make bpf rules take effect. + // SAFETY: The pointer of bpf_prog_ptr can be guaranteed not null. let ret = unsafe { libc::syscall( libc::SYS_seccomp, diff --git a/util/src/syscall.rs b/util/src/syscall.rs index 5a6f0ea57..f6088a72f 100644 --- a/util/src/syscall.rs +++ b/util/src/syscall.rs @@ -31,6 +31,10 @@ pub fn mbind( max_node: u64, flags: u32, ) -> Result<()> { + // SAFETY: + // 1. addr is managed by memory mapping, it can be guaranteed legal. + // 2. node_mask was created in function of set_host_memory_policy. + // 3. Upper limit of max_node is MAX_NODES. let res = unsafe { syscall( SYS_mbind, diff --git a/util/src/tap.rs b/util/src/tap.rs index 0575289d2..d59e20a22 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -86,6 +86,7 @@ impl Tap { .open(TUNTAP_PATH) .with_context(|| format!("Open {} failed.", TUNTAP_PATH))?; + // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. let ret = unsafe { ioctl_with_mut_ref(&file_, TUNSETIFF(), &mut if_req) }; if ret < 0 { return Err(anyhow!( @@ -98,7 +99,7 @@ impl Tap { } else if let Some(fd) = fd { let fcnt_arg = FcntlArg::F_SETFL(OFlag::from_bits(libc::O_NONBLOCK).unwrap()); fcntl(fd, fcnt_arg)?; - // SAFETY: the fd has been verified. + // SAFETY: The fd has been verified. file = unsafe { File::from_raw_fd(fd) }; } else { return Err(anyhow!( @@ -108,6 +109,7 @@ impl Tap { } let mut features = 0; + // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. let ret = unsafe { ioctl_with_mut_ref(&file, TUNGETFEATURES(), &mut features) }; if ret < 0 { return Err(anyhow!( @@ -128,6 +130,7 @@ impl Tap { pub fn set_offload(&self, flags: u32) -> Result<()> { let ret = + // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) }; if ret < 0 { return Err(anyhow!("ioctl TUNSETOFFLOAD failed.".to_string())); @@ -137,6 +140,7 @@ impl Tap { } pub fn set_hdr_size(&self, len: u32) -> Result<()> { + // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. let ret = unsafe { ioctl_with_ref(self.file.as_ref(), TUNSETVNETHDRSZ(), &len) }; if ret < 0 { return Err(anyhow!("ioctl TUNSETVNETHDRSZ failed.".to_string())); @@ -147,8 +151,10 @@ impl Tap { pub fn has_ufo(&self) -> bool { let flags = TUN_F_CSUM | TUN_F_UFO; - (unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) }) - >= 0 + ( + // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. + unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) } + ) >= 0 } pub fn set_queue(&mut self, enable: bool) -> i32 { @@ -165,6 +171,7 @@ impl Tap { ifr_flags, }; + // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. let ret = unsafe { ioctl_with_mut_ref(self.file.as_ref(), TUNSETQUEUE(), &mut if_req) }; if ret == 0 { self.enabled = enable; diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs index adcbd28a0..7f69b427a 100644 --- a/util/src/test_helper.rs +++ b/util/src/test_helper.rs @@ -52,6 +52,7 @@ pub fn set_test_enabled() { if let Err(_e) = TEST_BASE_TIME.set(Instant::now()) { panic!("Failed to initialize clock"); } + // SAFETY: This module is only used for test. unsafe { if TEST_CLOCK.is_none() { TEST_CLOCK = Some(Arc::new(RwLock::new(0))); @@ -64,6 +65,7 @@ pub fn is_test_enabled() -> bool { } pub fn set_test_clock(value: u64) { + // SAFETY: This module is only used for test. unsafe { if TEST_CLOCK.is_none() { panic!("TEST_CLOCK has not been initialized."); @@ -78,6 +80,7 @@ pub fn set_test_clock(value: u64) { } pub fn get_test_clock() -> u64 { + // SAFETY: This module is only used for test. unsafe { if TEST_CLOCK.is_none() { panic!("TEST_CLOCK has not been initialized."); @@ -88,6 +91,7 @@ pub fn get_test_clock() -> u64 { } pub fn get_test_time() -> Instant { + // SAFETY: This module is only used for test. unsafe { if TEST_CLOCK.is_none() { panic!("TEST_CLOCK has not been initialized."); diff --git a/util/src/time.rs b/util/src/time.rs index a7981fcb7..e8fd8a568 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -42,7 +42,9 @@ pub fn gettime() -> Result<(u32, u32)> { /// Convert wall time to year/month/day/hour/minute/second format. pub fn get_format_time(sec: i64) -> [i32; 6] { + // SAFETY: No input parameter. let mut ti: libc::tm = unsafe { std::mem::zeroed() }; + // SAFETY: The parameters of sec and ti can be guaranteed not be null. unsafe { libc::localtime_r(&sec, &mut ti); } diff --git a/util/src/unix.rs b/util/src/unix.rs index 2d8cbc22f..d0723e9cc 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -31,6 +31,8 @@ use crate::UtilError; pub fn limit_permission(path: &str) -> Result<()> { let file_path = path.as_bytes().to_vec(); let cstr_file_path = std::ffi::CString::new(file_path).unwrap(); + // SAFETY: The file_path can be guaranteed to be legal, and the + // return value have bee verified later. let ret = unsafe { libc::chmod(cstr_file_path.as_ptr(), 0o600) }; if ret == 0 { @@ -108,7 +110,7 @@ pub fn do_mmap( prot |= libc::PROT_WRITE; } - // Safe because the return value is checked. + // SAFETY: The return value is checked. let hva = unsafe { libc::mmap( std::ptr::null_mut() as *mut libc::c_void, @@ -130,7 +132,7 @@ pub fn do_mmap( } fn set_memory_undumpable(host_addr: *mut libc::c_void, size: u64) { - // Safe because host_addr and size are valid and return value is checked. + // SAFETY: host_addr and size are valid and return value is checked. let ret = unsafe { libc::madvise(host_addr, size as libc::size_t, libc::MADV_DONTDUMP) }; if ret < 0 { error!( @@ -232,8 +234,10 @@ impl UnixSock { } fn cmsg_data(&self, cmsg_buffer: *mut cmsghdr) -> *mut RawFd { - // Safe as parameter is zero. - (cmsg_buffer as *mut u8).wrapping_add(unsafe { CMSG_LEN(0) } as usize) as *mut RawFd + (cmsg_buffer as *mut u8).wrapping_add( + // SAFETY: Parameter is zero. + unsafe { CMSG_LEN(0) } as usize, + ) as *mut RawFd } fn get_next_cmsg( @@ -242,14 +246,14 @@ impl UnixSock { cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr, ) -> *mut cmsghdr { - // Safe to get cmsg_len because the parameter is valid. - let next_cmsg = (cmsg_ptr as *mut u8) - .wrapping_add(unsafe { CMSG_LEN(cmsg.cmsg_len as u32) } as usize) - as *mut cmsghdr; + let next_cmsg = (cmsg_ptr as *mut u8).wrapping_add( + // SAFETY: Safe to get cmsg_len because the parameter is valid. + unsafe { CMSG_LEN(cmsg.cmsg_len as u32) } as usize, + ) as *mut cmsghdr; // Safe to get msg_control because the parameter is valid. let nex_cmsg_pos = (next_cmsg as *mut u8).wrapping_sub(msghdr.msg_control as usize) as u64; - // Safe as parameter is zero. + // SAFETY: Parameter is constant. if nex_cmsg_pos.wrapping_add(unsafe { CMSG_LEN(0) } as u64) > msghdr.msg_controllen as u64 { null_mut() } else { @@ -268,16 +272,17 @@ impl UnixSock { /// /// The socket file descriptor is broken. pub fn send_msg(&self, iovecs: &mut [iovec], out_fds: &[RawFd]) -> std::io::Result { - // SAFETY: we checked the iovecs lens before. + // SAFETY: We checked the iovecs lens before. let iovecs_len = iovecs.len(); - // SATETY: we checked the out_fds lens before. + // SAFETY: We checked the out_fds lens before. let cmsg_len = unsafe { CMSG_LEN((std::mem::size_of_val(out_fds)) as u32) }; - // SAFETY: we checked the out_fds lens before. + // SAFETY: We checked the out_fds lens before. let cmsg_capacity = unsafe { CMSG_SPACE((std::mem::size_of_val(out_fds)) as u32) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. + // SAFETY: The member variable of msg will be assigned value later. let mut msg: msghdr = unsafe { std::mem::zeroed() }; msg.msg_name = null_mut(); msg.msg_namelen = 0; @@ -295,6 +300,7 @@ impl UnixSock { cmsg_level: SOL_SOCKET, cmsg_type: SCM_RIGHTS, }; + // SAFETY: cmsg_buffer was created in this function and can be guaranteed not be null. unsafe { write_unaligned(cmsg_buffer.as_mut_ptr() as *mut cmsghdr, cmsg); @@ -309,8 +315,8 @@ impl UnixSock { msg.msg_controllen = cmsg_capacity as _; } - // Safe as msg parameters are valid. let write_count = + // SAFETY: msg parameters are valid. unsafe { sendmsg(self.sock.as_ref().unwrap().as_raw_fd(), &msg, MSG_NOSIGNAL) }; if write_count == -1 { @@ -341,14 +347,15 @@ impl UnixSock { iovecs: &mut [iovec], in_fds: &mut [RawFd], ) -> std::io::Result<(usize, usize)> { - // SAFETY: we check the iovecs lens before. + // SAFETY: We check the iovecs lens before. let iovecs_len = iovecs.len(); - // SAFETY: we check the in_fds lens before. + // SAFETY: We check the in_fds lens before. let cmsg_capacity = unsafe { CMSG_SPACE((std::mem::size_of_val(in_fds)) as u32) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be // initialized in normal way. + // SAFETY: The member variable of msg will be assigned value later. let mut msg: msghdr = unsafe { std::mem::zeroed() }; msg.msg_name = null_mut(); msg.msg_namelen = 0; @@ -363,7 +370,7 @@ impl UnixSock { msg.msg_controllen = cmsg_capacity as _; } - // Safe as msg parameters are valid. + // SAFETY: msg parameters are valid. let total_read = unsafe { recvmsg( self.sock.as_ref().unwrap().as_raw_fd(), @@ -395,11 +402,17 @@ impl UnixSock { let mut cmsg_ptr = msg.msg_control as *mut cmsghdr; let mut in_fds_count = 0_usize; while !cmsg_ptr.is_null() { + // SAFETY: The pointer of cmsg_ptr was created in this function and + // can be guaranteed not be null. let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() }; if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS { + // SAFETY: Input parameter is constant. let fd_count = (cmsg.cmsg_len as u64 - unsafe { CMSG_LEN(0) } as u64) as usize / size_of::(); + // SAFETY: + // 1. the pointer of cmsg_ptr was created in this function and can be guaranteed not be null. + // 2. the parameter of in_fds has been checked before. unsafe { copy_nonoverlapping( self.cmsg_data(cmsg_ptr), diff --git a/util/src/v4l2.rs b/util/src/v4l2.rs index be3f86084..545e264ea 100644 --- a/util/src/v4l2.rs +++ b/util/src/v4l2.rs @@ -77,6 +77,7 @@ impl V4l2Backend { pub fn query_cap(&self) -> Result { let mut cap = new_init::(); + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_QUERYCAP(), &mut cap) }; if ret < 0 { bail!( @@ -88,6 +89,7 @@ impl V4l2Backend { } pub fn set_format(&self, fmt: &v4l2_format) -> Result<()> { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_S_FMT(), fmt) }; if ret < 0 { bail!( @@ -105,6 +107,7 @@ impl V4l2Backend { let cnt = locked_buf.len() as u32; // Ensure the count is equal to the length of buffer. bufs.count = cnt; + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_REQBUFS(), bufs) }; if ret < 0 { bail!( @@ -118,6 +121,7 @@ impl V4l2Backend { buf.index = i; buf.type_ = bufs.type_; buf.memory = bufs.memory; + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_QUERYBUF(), &buf) }; if ret < 0 { bail!( @@ -127,6 +131,9 @@ impl V4l2Backend { ); } + // SAFETY: + // 1. self.fd is created in function new(). + // 2. buf can be guaranteed not be null. let ret = unsafe { libc::mmap( std::ptr::null_mut() as *mut libc::c_void, @@ -158,6 +165,7 @@ impl V4l2Backend { if buf.is_none() { continue; } + // SAFETY: buf can be guaranteed not be null. let ret = unsafe { libc::munmap( buf.iov_base as *mut libc::c_void, @@ -177,6 +185,7 @@ impl V4l2Backend { } pub fn stream_on(&self, vtype: std::os::raw::c_int) -> Result<()> { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_STREAMON(), &vtype) }; if ret < 0 { bail!( @@ -188,6 +197,7 @@ impl V4l2Backend { } pub fn stream_off(&self, vtype: std::os::raw::c_int) -> Result<()> { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_STREAMOFF(), &vtype) }; if ret < 0 { bail!( @@ -199,6 +209,7 @@ impl V4l2Backend { } pub fn queue_buffer(&self, buf: &v4l2_buffer) -> Result<()> { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_QBUF(), buf) }; if ret < 0 { bail!( @@ -210,6 +221,7 @@ impl V4l2Backend { } pub fn dequeue_buffer(&self, buf: &v4l2_buffer) -> Result { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_DQBUF(), buf) }; if ret < 0 { if nix::errno::errno() == libc::EAGAIN { @@ -224,6 +236,7 @@ impl V4l2Backend { } pub fn enum_format(&self, desc: &mut v4l2_fmtdesc) -> Result { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_ENUM_FMT(), desc) }; if ret < 0 { let err = std::io::Error::last_os_error(); @@ -236,6 +249,7 @@ impl V4l2Backend { } pub fn enum_frame_size(&self, frmsize: &mut v4l2_frmsizeenum) -> Result { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_ENUM_FRAMESIZES(), frmsize) }; if ret < 0 { let err = std::io::Error::last_os_error(); @@ -248,6 +262,7 @@ impl V4l2Backend { } pub fn enum_frame_interval(&self, frame_val: &mut v4l2_frmivalenum) -> Result { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_mut_ref(self, VIDIOC_ENUM_FRAMEINTERVALS(), frame_val) }; if ret < 0 { let err = std::io::Error::last_os_error(); @@ -260,6 +275,7 @@ impl V4l2Backend { } pub fn set_stream_parameter(&self, parm: &v4l2_streamparm) -> Result<()> { + // SAFETY: self.fd is created in function new(). let ret = unsafe { ioctl_with_ref(self, VIDIOC_S_PARM(), parm) }; if ret < 0 { bail!( @@ -279,6 +295,7 @@ impl AsRawFd for V4l2Backend { pub fn new_init() -> T { let mut s = ::std::mem::MaybeUninit::::uninit(); + // SAFETY: s can be guaranteed not be null. unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 0e463104b..70f99625c 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -121,7 +121,7 @@ impl VfioContainer { .open(CONTAINER_PATH) .with_context(|| format!("Failed to open {} for VFIO container.", CONTAINER_PATH))?; - // Ioctl is safe. Called file is `/dev/vfio/vfio` fd and we check the return. + // SAFETY: Called file is `/dev/vfio/vfio` fd and we check the return. let v = unsafe { ioctl(&fd, VFIO_GET_API_VERSION()) }; if v as u32 != vfio::VFIO_API_VERSION { return Err(anyhow!(VfioError::VfioIoctl( @@ -130,8 +130,8 @@ impl VfioContainer { ))); }; - // Ioctl is safe. Called file is `/dev/vfio/vfio` fd and we check the return. let ret = + // SAFETY: Ioctl is safe. Called file is `/dev/vfio/vfio` fd and we check the return. unsafe { ioctl_with_val(&fd, VFIO_CHECK_EXTENSION(), vfio::VFIO_TYPE1v2_IOMMU.into()) }; if ret != 1 { return Err(anyhow!(VfioError::VfioIoctl( @@ -157,7 +157,7 @@ impl VfioContainer { /// * Fail to match IOMMU type. /// * Fail to set container IOMMU. fn set_iommu(&self, val: u32) -> Result<()> { - // Ioctl is safe. Called container file is `/dev/vfio/vfio` fd and we check the return. + // SAFETY: Called container file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_val(&self.fd, VFIO_SET_IOMMU(), val.into()) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -187,7 +187,7 @@ impl VfioContainer { size, }; - // Ioctl is safe. Called container file is `/dev/vfio/vfio` fd and we check the return. + // SAFETY: Called container file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_IOMMU_MAP_DMA(), &map) }; if ret != 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -215,7 +215,7 @@ impl VfioContainer { size, }; - // Ioctl is safe. Called container file is `/dev/vfio/vfio` fd and we check the return. + // SAFETY: Called container file is `/dev/vfio/vfio` fd and we check the return. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_IOMMU_UNMAP_DMA(), &unmap) }; if ret != 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -335,7 +335,7 @@ impl VfioGroup { argsz: size_of::() as u32, flags: 0, }; - // Safe as file is `iommu_group` fd, and we check the return. + // SAFETY: file is `iommu_group` fd, and we check the return. let ret = unsafe { ioctl_with_mut_ref(&file, VFIO_GROUP_GET_STATUS(), &mut status) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -400,7 +400,7 @@ impl VfioGroup { fn set_container(&mut self, container: &Arc>) -> Result<()> { let fd = &container.lock().unwrap().fd.as_raw_fd(); - // Safe as group is the owner of file, and we check the return. + // SAFETY: group is the owner of file, and we check the return. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_GROUP_SET_CONTAINER(), fd) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -415,6 +415,7 @@ impl VfioGroup { fn unset_container(&mut self) { let container = self.container.upgrade().unwrap(); let fd = container.lock().unwrap().fd.as_raw_fd(); + // SAFETY: self.fd was created in function new(). unsafe { ioctl_with_ref(&self.fd, VFIO_GROUP_UNSET_CONTAINER(), &fd) }; self.container = Weak::new(); } @@ -581,7 +582,7 @@ impl VfioDevice { let path: CString = CString::new(dev_name.as_bytes()) .with_context(|| "Failed to convert device name to CString type of data")?; let ptr = path.as_ptr(); - // Safe as group is the owner of file and make sure ptr is valid. + // SAFETY: group is the owner of file and make sure ptr is valid. let fd = unsafe { ioctl_with_ptr(&group.fd, VFIO_GROUP_GET_DEVICE_FD(), ptr) }; if fd < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -590,7 +591,7 @@ impl VfioDevice { ))); } - // Safe as we have verified that fd is a valid FD. + // SAFETY: We have verified that fd is a valid FD. let device = unsafe { File::from_raw_fd(fd) }; Ok((String::from(dev_name), device)) } @@ -603,7 +604,7 @@ impl VfioDevice { num_irqs: 0, }; - // Safe as device is the owner of file, and we will verify the result is valid. + // SAFETY: Device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_mut_ref(device, VFIO_DEVICE_GET_INFO(), &mut dev_info) }; if ret < 0 || (dev_info.flags & vfio::VFIO_DEVICE_FLAGS_PCI) == 0 @@ -635,7 +636,7 @@ impl VfioDevice { let cap_size = (info.argsz - argsz) as usize; let mut new_info = array_to_vec::(cap_size); new_info[0].region_info = info; - // Safe as device is the owner of file, and we will verify the result is valid. + // SAFETY: Device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_mut_ref( &self.fd, @@ -650,14 +651,17 @@ impl VfioDevice { ))); } - // Safe as we make sure there is enough memory space to convert cap info into + // SAFETY: We make sure there is enough memory space to convert cap info into // specific structure. let sparse = unsafe { new_info[0].cap_info.as_ptr() as *mut vfio::vfio_region_info_cap_sparse_mmap }; + // SAFETY: sparse was created in this function and can be guaranteed now be null. if unsafe { (*sparse).header.id } == vfio::VFIO_REGION_INFO_CAP_SPARSE_MMAP as u16 { + // SAFETY: The reason is same as above. let nr_areas = unsafe { (*sparse).nr_areas as usize }; let areas: &mut [vfio::vfio_region_sparse_mmap_area] = + // SAFETY: The reason is same as above. unsafe { (*sparse).areas.as_mut_slice(nr_areas) }; mmaps = Vec::with_capacity(nr_areas); for area in areas.iter() { @@ -686,7 +690,7 @@ impl VfioDevice { offset: 0, }; - // Safe as device is the owner of file, and we will verify the result is valid. + // SAFETY: Device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_mut_ref(&self.fd, VFIO_DEVICE_GET_REGION_INFO(), &mut info) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -735,8 +739,8 @@ impl VfioDevice { count: 0, }; - // Safe as device is the owner of file, and we will verify the result is valid. let ret = + // SAFETY: Device is the owner of file, and we will verify the result is valid. unsafe { ioctl_with_mut_ref(&self.fd, VFIO_DEVICE_GET_IRQ_INFO(), &mut info) }; if ret < 0 { warn!( @@ -802,14 +806,14 @@ impl VfioDevice { irq_set[0].start = start; irq_set[0].count = irq_fds.len() as u32; - // It is safe as enough memory space to save irq_set data. + // SAFETY: It is safe as enough memory space to save irq_set data. let data: &mut [u8] = unsafe { irq_set[0] .data .as_mut_slice(irq_fds.len() * size_of::()) }; LittleEndian::write_i32_into(irq_fds.as_slice(), data); - // Safe as device is the owner of file, and we will verify the result is valid. + // SAFETY: Device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -837,7 +841,7 @@ impl VfioDevice { irq_set[0].start = 0u32; irq_set[0].count = 0u32; - // Safe as device is the owner of file, and we will verify the result is valid. + // SAFETY: Device is the owner of file, and we will verify the result is valid. let ret = unsafe { ioctl_with_ref(&self.fd, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( @@ -850,8 +854,8 @@ impl VfioDevice { } pub fn reset(&self) -> Result<()> { - // Safe as device is the owner of file, and we verify the device supports being reset. if self.dev_info.flags & vfio::VFIO_DEVICE_FLAGS_RESET != 0 { + // SAFETY: Device is the owner of file, and we verify the device supports being reset. let ret = unsafe { ioctl(&self.fd, VFIO_DEVICE_RESET()) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index a5c5f7267..2da4384d5 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -142,8 +142,8 @@ impl VfioPciDevice { }; let locked_dev = self.vfio_device.lock().unwrap(); - // Safe as device is the owner of file, and we will verify the result is valid. let ret = + // SAFETY: Device is the owner of file, and we will verify the result is valid. unsafe { ioctl_with_mut_ref(&locked_dev.fd, VFIO_DEVICE_GET_REGION_INFO(), &mut info) }; if ret < 0 { return Err(anyhow!(VfioError::VfioIoctl( diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 210c470bf..6080e822e 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -167,7 +167,7 @@ fn iov_to_buf( } fn memory_advise(addr: *mut libc::c_void, len: libc::size_t, advice: libc::c_int) { - // Safe, because the memory to be freed is allocated by guest. + // SAFETY: The memory to be freed is allocated by guest. if unsafe { libc::madvise(addr, len, advice) } != 0 { let evt_type = match advice { libc::MADV_DONTNEED => "DONTNEED".to_string(), @@ -694,6 +694,8 @@ impl BalloonIoHandler { } let req = Request::parse(&elem, OUT_IOVEC) .with_context(|| "Fail to parse available descriptor chain")?; + // SAFETY: There is no confliction when writing global variable BALLOON_DEV, in other + // words, this function will not be called simultaneously. if let Some(dev) = unsafe { &BALLOON_DEV } { let mut balloon_dev = dev.lock().unwrap(); for iov in req.iovec.iter() { @@ -923,7 +925,7 @@ impl Balloon { /// Init balloon object for global use. pub fn object_init(dev: Arc>) { - // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other + // SAFETY: there is no confliction when writing global variable BALLOON_DEV, in other // words, this function will not be called simultaneously. unsafe { if BALLOON_DEV.is_none() { @@ -1040,8 +1042,8 @@ impl VirtioDevice for Balloon { fn write_config(&mut self, _offset: u64, data: &[u8]) -> Result<()> { // Guest update actual balloon size - // Safe, because the results will be checked. let old_actual = self.actual.load(Ordering::Acquire); + // SAFETY: The results will be checked. let new_actual = match unsafe { data.align_to::() } { (_, [new_config], _) => *new_config, _ => { @@ -1140,7 +1142,7 @@ impl VirtioDevice for Balloon { } pub fn qmp_balloon(target: u64) -> bool { - // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other + // SAFETY: there is no confliction when writing global variable BALLOON_DEV, in other // words, this function will not be called simultaneously. if let Some(dev) = unsafe { &BALLOON_DEV } { match dev.lock().unwrap().set_guest_memory_size(target) { @@ -1158,7 +1160,7 @@ pub fn qmp_balloon(target: u64) -> bool { } pub fn qmp_query_balloon() -> Option { - // Safe, because there is no confliction when writing global variable BALLOON_DEV, in other + // SAFETY: There is no confliction when writing global variable BALLOON_DEV, in other // words, this function will not be called simultaneously. if let Some(dev) = unsafe { &BALLOON_DEV } { let unlocked_dev = dev.lock().unwrap(); diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 1fe7999ca..b2bf6fedb 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -493,7 +493,7 @@ fn create_surface( pixman_stride, ); ref_pixman_image(res.pixman_image); - // SAFETY: the param of create operation for image has been checked. + // SAFETY: The param of create operation for image has been checked. unsafe { pixman_image_set_destroy_function( rect, @@ -1100,7 +1100,7 @@ impl GpuIoHandler { } else { get_image_data(res.pixman_image) }; - // SAFETY: the offset is within the legal address. + // SAFETY: The offset is within the legal address. let res_data_offset = unsafe { res_data.offset(offset as isize) }; // Create surface for the scanout. @@ -1191,7 +1191,7 @@ impl GpuIoHandler { let mut final_reg = pixman_region16_t::default(); let rect_reg_ptr = &mut rect_reg as *mut pixman_region16_t; let final_reg_ptr = &mut final_reg as *mut pixman_region16_t; - // SAFETY: the pointer is not empty. + // SAFETY: The pointer is not empty. unsafe { pixman_region_init(final_reg_ptr); pixman_region_init_rect( @@ -1217,7 +1217,7 @@ impl GpuIoHandler { } } - // SAFETY: it can ensured that the pointer is not empty. + // SAFETY: Tt can ensured that the pointer is not empty. unsafe { pixman_region_fini(flush_reg_ptr); } @@ -1371,6 +1371,7 @@ impl GpuIoHandler { // SAFETY: Upper limit of ents is 16384. ents.resize(entries as usize, VirtioGpuMemEntry::default()); let ents_buf = + // SAFETY: ents is guaranteed not be null and the range of ents_size has been limited. unsafe { from_raw_parts_mut(ents.as_mut_ptr() as *mut u8, ents_size as usize) }; let v = iov_to_buf_direct(&req.out_iovec, head_size, ents_buf)?; if v as u64 != ents_size { diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 81174d12b..f4ce333dc 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -240,7 +240,9 @@ impl VhostBackend { rawfd: Option, ) -> Result { let fd = match rawfd { - Some(rawfd) => unsafe { File::from_raw_fd(rawfd) }, + Some(rawfd) => + // SAFETY: this fd was configured in cmd line. + unsafe { File::from_raw_fd(rawfd) }, None => OpenOptions::new() .read(true) .write(true) @@ -263,6 +265,8 @@ impl AsRawFd for VhostBackend { impl VhostOps for VhostBackend { fn set_owner(&self) -> Result<()> { + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl(self, VHOST_SET_OWNER()) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -273,6 +277,8 @@ impl VhostOps for VhostBackend { } fn reset_owner(&self) -> Result<()> { + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl(self, VHOST_RESET_OWNER()) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -284,6 +290,8 @@ impl VhostOps for VhostBackend { fn get_features(&self) -> Result { let mut avail_features: u64 = 0; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_mut_ref(self, VHOST_GET_FEATURES(), &mut avail_features) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -294,6 +302,8 @@ impl VhostOps for VhostBackend { } fn set_features(&self, features: u64) -> Result<()> { + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_FEATURES(), &features) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -324,6 +334,8 @@ impl VhostOps for VhostBackend { .copy_from_slice(region.as_bytes()); } + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ptr(self, VHOST_SET_MEM_TABLE(), bytes.as_ptr()) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -338,6 +350,8 @@ impl VhostOps for VhostBackend { index: queue_idx as u32, num: u32::from(num), }; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_NUM(), &vring_state) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -383,6 +397,8 @@ impl VhostOps for VhostBackend { log_guest_addr: 0_u64, }; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -397,6 +413,8 @@ impl VhostOps for VhostBackend { index: queue_idx as u32, num: u32::from(num), }; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_BASE(), &vring_state) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -412,6 +430,8 @@ impl VhostOps for VhostBackend { num: 0, }; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_GET_VRING_BASE(), &vring_state) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -426,6 +446,8 @@ impl VhostOps for VhostBackend { index: queue_idx as u32, fd: fd.as_raw_fd(), }; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_CALL(), &vring_file) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -440,6 +462,8 @@ impl VhostOps for VhostBackend { index: queue_idx as u32, fd: fd.as_raw_fd(), }; + // SAFETY: self.fd was created in function new() and the + // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_KICK(), &vring_file) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 6a11ae902..55a497242 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -62,6 +62,7 @@ impl VhostNetBackend for VhostBackend { fd, }; + // SAFETY: self.fd was created in function new() and the return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_NET_SET_BACKEND(), &vring_file) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 02cffe007..1427592b4 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -49,6 +49,7 @@ trait VhostVsockBackend { impl VhostVsockBackend for VhostBackend { fn set_guest_cid(&self, cid: u64) -> Result<()> { + // SAFETY: self.fd was created in function new() and the return value will be checked later. let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_GUEST_CID(), &cid) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( @@ -60,6 +61,7 @@ impl VhostVsockBackend for VhostBackend { fn set_running(&self, start: bool) -> Result<()> { let on: u32 = u32::from(start); + // SAFETY: self.fd was created in function new() and the return value will be checked later. let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_RUNNING(), &on) }; if ret < 0 { return Err(anyhow!(VirtioError::VhostIoctl( diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 521cca78b..3d18ea5ff 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -485,7 +485,10 @@ impl VhostUserClient { // Expect 1 fd. let mut fds = [RawFd::default()]; let vhost_user_inflight = self.get_inflight_fd(queue_num, queue_size, &mut fds)?; - let file = Arc::new(unsafe { File::from_raw_fd(fds[0]) }); + let file = Arc::new( + // SAFETY: fds[0] create in function of get_inflight_fd. + unsafe { File::from_raw_fd(fds[0]) }, + ); let hva = do_mmap( &Some(file.as_ref()), vhost_user_inflight.mmap_size, @@ -751,12 +754,17 @@ impl VhostUserClient { queue_size, }; let body_opt: Option<&u32> = None; - let payload_opt: Option<&[u8]> = Some(unsafe { - from_raw_parts( - (&inflight as *const VhostUserInflight) as *const u8, - data_len, - ) - }); + let payload_opt: Option<&[u8]> = Some( + // SAFETY: + // 1. inflight can be guaranteed not null. + // 2. data_len is constant. + unsafe { + from_raw_parts( + (&inflight as *const VhostUserInflight) as *const u8, + data_len, + ) + }, + ); let client = self.client.lock().unwrap(); client .sock -- Gitee From d9e43be5fd856151f7df229aca9821a7be6698df Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 20 Dec 2023 19:06:01 +0800 Subject: [PATCH 1513/2187] vnc: fixup the issue of out of bound If num_encoding is larger than incoming data buffer, panic will occur due to ivalid memory access. Signed-off-by: Zhao Yi Min --- ui/src/vnc/client_io.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index c1ac1d6f8..84e92e121 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -802,6 +802,7 @@ impl ClientIoHandler { let mut locked_dpm = self.client.client_dpm.lock().unwrap(); locked_dpm.feature = 0; locked_dpm.enc = 0; + num_encoding = cmp::min(num_encoding as usize, (buf.len() - 4) / 4) as u16; while num_encoding > 0 { let offset = (4 * num_encoding) as usize; let enc = i32::from_be_bytes([ -- Gitee From 4a988ae5fce92bcdb28ce1551fac98a3aa3c85de Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 9 Dec 2023 15:01:10 +0800 Subject: [PATCH 1514/2187] virtio-gpu: fixup integer overflow If format is PIXMAN_a8r8g8b8 and both of width and height are 0xffffffff, stride * height will be overflow. Signed-off-by: Zhao Yi Min --- virtio/src/device/gpu.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index b2bf6fedb..c1efc1329 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -627,8 +627,16 @@ pub fn cal_image_hostmem(format: u32, width: u32, height: u32) -> (Option }; let bpp = pixman_format_bpp(pixman_format as u32); let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); - let mem = (height as u64 * stride) as usize; - (Some(mem), 0) + match stride.checked_mul(height as u64) { + None => { + error!( + "stride * height is overflow: width {} height {} stride {} bpp {}", + width, height, stride, bpp, + ); + (None, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER) + } + Some(v) => (Some(v as usize), 0), + } } } -- Gitee From 66333e43e372ba9828bea39e909163599fb5dbe7 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 9 Dec 2023 17:03:03 +0800 Subject: [PATCH 1515/2187] pflash: fixup 'write_cycle' overflow issue This patch fixes up the issue of write_cycle overflow. Signed-off-by: Zhao Yi Min --- devices/src/legacy/pflash.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index c15c0afc2..131678a08 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -39,7 +39,7 @@ pub struct PFlash { /// This is used to support x16 wide PFlash run in x8 mode. max_device_width: u32, /// If 0, the PFlash is read normally. - write_cycle: i32, + write_cycle: u32, /// PFlash is read only or not. read_only: bool, /// Command to control PFlash. @@ -456,7 +456,7 @@ impl PFlash { return true; } } - self.write_cycle += 1; + self.write_cycle = self.write_cycle.wrapping_add(1); self.cmd = cmd; true } @@ -522,8 +522,8 @@ impl PFlash { error!("Failed to extract bits from u32 value"); return false; }; + self.write_cycle = self.write_cycle.wrapping_add(1); self.counter = value; - self.write_cycle += 1; } 0x60 => { if (cmd == 0xd0) || (cmd == 0x01) { @@ -588,7 +588,7 @@ impl PFlash { self.status |= 0x80; if self.counter == 0 { let mask: u64 = !(self.write_blk_size as u64 - 1); - self.write_cycle += 1; + self.write_cycle = self.write_cycle.wrapping_add(1); if !self.read_only { if let Err(e) = self.update_content(offset & mask, self.write_blk_size) { error!("Failed to update content for PFlash device: {:?}", e); -- Gitee From 3cc23bb4638c12b41da00c3607c4a84e005e577e Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 20 Dec 2023 19:06:54 +0800 Subject: [PATCH 1516/2187] dummy-device: fixup integer overflow issue Fixup the potential overflow issue. Signed-off-by: Zhao Yi Min --- machine/src/standard_common/mod.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index fbe3fbba9..30afc97fa 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -27,6 +27,7 @@ use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::string::String; use std::sync::{Arc, Mutex}; +use std::u64; use anyhow::{bail, Context}; use log::error; @@ -1780,10 +1781,20 @@ impl DeviceInterface for StdMachine { return false; } - let ptr: *const u8 = data.as_ptr(); - let ptr: *const u64 = ptr as *const u64; - // SAFETY: The ptr can be guaranteed not empty. - self.head = unsafe { *ptr } * 2; + let val = match u64::from_bytes(data) { + None => { + error!("DummyDevice: cannot read u64 from data"); + return false; + } + Some(v) => v, + }; + self.head = match val.checked_mul(2) { + None => { + error!("DummyDevice: val({}) * 2 is overflow", val); + return false; + } + Some(v) => v, + }; true } } -- Gitee From 2af5d12880443551ef33fc3f3d6d3f3b77efea55 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Mon, 25 Dec 2023 19:04:06 +0800 Subject: [PATCH 1517/2187] PCI: introduce new function generate_dev_id() in some cases, the device dose not require Arc to hold dev_id. So introduce a new function to just generate dev id number. Signed-off-by: Fan Xuan Zhe --- devices/src/pci/bus.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index b02159031..c4b22e62c 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -243,10 +243,13 @@ impl PciBus { .read_config(offset, data); } - pub fn update_dev_id(&self, devfn: u8, dev_id: &Arc) { + pub fn generate_dev_id(&self, devfn: u8) -> u16 { let bus_num = self.number(SECONDARY_BUS_NUM as usize); - let device_id = ((bus_num as u16) << 8) | (devfn as u16); - dev_id.store(device_id, Ordering::Release); + ((bus_num as u16) << 8) | (devfn as u16) + } + + pub fn update_dev_id(&self, devfn: u8, dev_id: &Arc) { + dev_id.store(self.generate_dev_id(devfn), Ordering::Release); } } -- Gitee From a099ca8faf9b66c5c5063389f1b868c52bb3eda5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 29 Dec 2023 08:52:40 +0800 Subject: [PATCH 1518/2187] docs: add missing quotation JSON string misses quotation, fix it. Signed-off-by: liuxiangdong --- docs/qmp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/qmp.md b/docs/qmp.md index a33e41109..83fd67f10 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -97,7 +97,7 @@ Add a block backend. #### Example ```json --> { "execute": "blockdev-add", "arguments": { "node-name": "drive-0", "file": { "driver": "file", "filename": "/path/to/block", "aio": native }, "cache": { "direct": true }, "read-only": false } } +-> { "execute": "blockdev-add", "arguments": { "node-name": "drive-0", "file": { "driver": "file", "filename": "/path/to/block", "aio": "native" }, "cache": { "direct": true }, "read-only": false } } <- { "return": {} } ``` -- Gitee From eecd55c96d1be0b7730de02f1c38d40bf7f26329 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Mon, 16 Oct 2023 23:02:33 +0800 Subject: [PATCH 1519/2187] Trace: Introduce trace infrastructure This commit supports trace infrastructure, which can automatically generate trace functions based on configuration file in toml format and can be easily extended to support different trace tools. Signed-off-by: Gan Qixin --- Cargo.lock | 23 +++++ Cargo.toml | 3 + trace/Cargo.toml | 18 ++++ trace/src/ftrace.rs | 59 +++++++++++ trace/src/lib.rs | 100 ++++++++++++++++++ trace/trace_generator/Cargo.toml | 17 +++ trace/trace_generator/src/lib.rs | 171 +++++++++++++++++++++++++++++++ 7 files changed, 391 insertions(+) create mode 100644 trace/Cargo.toml create mode 100644 trace/src/ftrace.rs create mode 100644 trace/src/lib.rs create mode 100644 trace/trace_generator/Cargo.toml create mode 100644 trace/trace_generator/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 43c592ba2..02a02077f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1531,6 +1531,7 @@ dependencies = [ "machine", "machine_manager", "thiserror", + "trace", "util", ] @@ -1672,6 +1673,28 @@ dependencies = [ "winnow", ] +[[package]] +name = "trace" +version = "2.3.0" +dependencies = [ + "anyhow", + "lazy_static", + "log", + "regex", + "trace_generator", +] + +[[package]] +name = "trace_generator" +version = "2.3.0" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.18", + "toml", +] + [[package]] name = "ui" version = "2.3.0" diff --git a/Cargo.toml b/Cargo.toml index ddff7afa7..e8d1461cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ log = "0.4" machine = { path = "machine" } machine_manager = { path = "machine_manager" } util = { path = "util" } +trace = { path = "trace" } [workspace] members = [ @@ -33,6 +34,8 @@ gtk = ["machine/gtk"] vnc = ["machine/vnc"] ramfb = ["machine/ramfb"] virtio_gpu = ["machine/virtio_gpu"] +trace_to_logger = ["trace/trace_to_logger"] +trace_to_ftrace = ["trace/trace_to_ftrace"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/trace/Cargo.toml b/trace/Cargo.toml new file mode 100644 index 000000000..55e511c5c --- /dev/null +++ b/trace/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "trace" +version = "2.3.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "Provide tracing infrastructure for StratoVirt" + +[dependencies] +log = "0.4" +lazy_static = "1.4.0" +regex = "1" +anyhow = "1.0" +trace_generator = { path = "trace_generator" } + +[features] +trace_to_logger = [] +trace_to_ftrace = [] diff --git a/trace/src/ftrace.rs b/trace/src/ftrace.rs new file mode 100644 index 000000000..607ce0486 --- /dev/null +++ b/trace/src/ftrace.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::{File, OpenOptions}, + io::{prelude::Write, BufRead, BufReader}, +}; + +pub(crate) fn open_trace_marker() -> File { + let mounts_path: &str = "/proc/mounts"; + let mounts_fd = File::open(mounts_path) + .unwrap_or_else(|e| panic!("Failed to open {}: {:?}", mounts_path, e)); + let mut reader = BufReader::new(mounts_fd); + let target_line = loop { + let mut buffer = String::new(); + reader + .read_line(&mut buffer) + .unwrap_or_else(|e| panic!("Read {} error: {:?}", &mounts_path, e)); + match buffer.as_str() { + "" => { + panic!("Failed to get mount point of tracefs") + } + _ => { + if buffer.contains("tracefs") { + break buffer; + } + } + } + }; + let fields: Vec<&str> = target_line.split(' ').collect(); + let tracefs_mount_point = fields + .get(1) + .unwrap_or_else(|| panic!("Failed to get mount point of tracefs")) + .to_string(); + + let tracing_on_path = format!("{}/tracing_on", tracefs_mount_point); + let mut tracing_on_fd = OpenOptions::new() + .write(true) + .open(&tracing_on_path) + .unwrap_or_else(|e| panic!("Failed to open {}: {:?}", tracing_on_path, e)); + tracing_on_fd + .write_all(b"1") + .unwrap_or_else(|e| panic!("Failed to enable tracing_on: {:?}", e)); + + let trace_marker_path = format!("{}/trace_marker", tracefs_mount_point); + OpenOptions::new() + .write(true) + .open(&trace_marker_path) + .unwrap_or_else(|e| panic!("Failed to open {}: {:?}", trace_marker_path, e)) +} diff --git a/trace/src/lib.rs b/trace/src/lib.rs new file mode 100644 index 000000000..786f82ddc --- /dev/null +++ b/trace/src/lib.rs @@ -0,0 +1,100 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(feature = "trace_to_ftrace")] +pub(crate) mod ftrace; + +use std::{ + fmt, + sync::atomic::{AtomicBool, Ordering}, +}; +#[cfg(feature = "trace_to_ftrace")] +use std::{fs::File, io::Write, sync::Mutex}; + +use anyhow::{Ok, Result}; +use lazy_static::lazy_static; +use regex::Regex; + +use trace_generator::{add_trace_event_to, gen_trace_func, gen_trace_state}; + +struct TraceEvent { + name: String, + get_state: fn() -> bool, + set_state: fn(bool), +} + +impl TraceEvent { + fn new(name: String, get_state: fn() -> bool, set_state: fn(bool)) -> Self { + TraceEvent { + name, + get_state, + set_state, + } + } +} + +#[derive(Default)] +struct TraceEventSet { + event_list: Vec, +} + +impl TraceEventSet { + fn add_trace_event(&mut self, event: TraceEvent) { + self.event_list.push(event); + } + + fn set_state_by_pattern(&self, pattern: String, state: bool) -> Result<()> { + let re = Regex::new(&pattern)?; + for event in &self.event_list { + if re.is_match(&event.name) { + (event.set_state)(state); + } + } + Ok(()) + } + + fn get_state_by_pattern(&self, pattern: String) -> Result> { + let re = Regex::new(&pattern)?; + let mut ret: Vec<(String, bool)> = Vec::new(); + for event in &self.event_list { + if re.is_match(&event.name) { + ret.push((event.name.to_string(), (event.get_state)())); + } + } + Ok(ret) + } +} + +gen_trace_state! {} + +lazy_static! { + static ref TRACE_EVENT_SET: TraceEventSet = { + let mut set = TraceEventSet::default(); + add_trace_event_to!(set); + set + }; +} + +gen_trace_func! {} + +#[cfg(feature = "trace_to_ftrace")] +lazy_static! { + static ref TRACE_MARKER_FD: Mutex = Mutex::new(ftrace::open_trace_marker()); +} + +pub fn get_state_by_pattern(pattern: String) -> Result> { + TRACE_EVENT_SET.get_state_by_pattern(pattern) +} + +pub fn set_state_by_pattern(pattern: String, state: bool) -> Result<()> { + TRACE_EVENT_SET.set_state_by_pattern(pattern, state) +} diff --git a/trace/trace_generator/Cargo.toml b/trace/trace_generator/Cargo.toml new file mode 100644 index 000000000..15feb969c --- /dev/null +++ b/trace/trace_generator/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "trace_generator" +version = "2.3.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" + +[lib] +name = "trace_generator" +proc-macro = true + +[dependencies] +syn = "2.0.18" +quote = "1.0" +proc-macro2 = "1.0" +toml = "0.7" +serde = { version = "1.0", features = ["derive"] } diff --git a/trace/trace_generator/src/lib.rs b/trace/trace_generator/src/lib.rs new file mode 100644 index 000000000..8115ad6d7 --- /dev/null +++ b/trace/trace_generator/src/lib.rs @@ -0,0 +1,171 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{fs, io::Read}; + +use proc_macro::TokenStream; +use quote::quote; +use serde::Deserialize; +use syn::{parse_macro_input, parse_str, Ident, Type}; + +const TRACE_DIR_NAME: &str = "trace_event"; + +#[derive(Debug, Deserialize)] +struct TraceEventDesc { + name: String, + args: String, + message: String, + enabled: bool, +} + +#[derive(Debug, Deserialize)] +struct TraceConf { + events: Vec, +} + +fn get_trace_event_desc() -> TraceConf { + let trace_dir_path = format!( + "{}/{}", + std::env::var("CARGO_MANIFEST_DIR").unwrap(), + TRACE_DIR_NAME + ); + let paths = fs::read_dir(trace_dir_path).unwrap(); + let mut desc = String::new(); + + for path in paths { + let file_path = path.unwrap().path(); + let file_name = file_path.to_str().unwrap(); + if file_name.ends_with(".toml") { + let mut file = fs::File::open(file_path).unwrap(); + file.read_to_string(&mut desc).unwrap(); + } + } + toml::from_str::(&desc).unwrap() +} + +#[proc_macro] +pub fn add_trace_event_to(input: TokenStream) -> TokenStream { + let set = parse_macro_input!(input as Ident); + let events = get_trace_event_desc().events; + let init_code = events.iter().map(|desc| match &desc.enabled { + true => { + let event_name = desc.name.trim(); + let get_func = parse_str::(format!("get_{}_state", event_name).as_str()).unwrap(); + let set_func = parse_str::(format!("set_{}_state", event_name).as_str()).unwrap(); + quote!( + #set.add_trace_event(TraceEvent::new(#event_name.to_string(), #get_func, #set_func)); + ) + } + false => quote!(), + }); + TokenStream::from(quote! { #( #init_code )* }) +} + +#[proc_macro] +pub fn gen_trace_state(_input: TokenStream) -> TokenStream { + let events = get_trace_event_desc().events; + let trace_state = events.iter().map(|desc| { + let event_name = parse_str::(desc.name.trim()).unwrap(); + let state_name = + parse_str::(format!("{}_state", event_name).to_uppercase().as_str()).unwrap(); + let get_func = parse_str::(format!("get_{}_state", event_name).as_str()).unwrap(); + let set_func = parse_str::(format!("set_{}_state", event_name).as_str()).unwrap(); + quote!( + static mut #state_name: AtomicBool = AtomicBool::new(false); + fn #get_func() -> bool { + // SAFETY: AtomicBool can be safely shared between threads. + unsafe { #state_name.load(Ordering::SeqCst) } + } + fn #set_func(val: bool) { + // SAFETY: AtomicBool can be safely shared between threads. + unsafe { #state_name.store(val, Ordering::SeqCst) } + } + ) + }); + + TokenStream::from(quote! { #( #trace_state )* }) +} + +#[proc_macro] +pub fn gen_trace_func(_input: TokenStream) -> TokenStream { + let events = get_trace_event_desc().events; + let trace_func = events.iter().map(|desc| { + let func_name = parse_str::(desc.name.trim()).unwrap(); + + let func_args = match desc.args.is_empty() { + true => quote!(), + false => { + let split_args: Vec<&str> = desc.args.split(',').collect(); + let _args = split_args.iter().map(|arg| { + let (v, t) = arg.split_once(':').unwrap(); + let arg_name = parse_str::(v.trim()).unwrap(); + let arg_type = parse_str::(t.trim()).unwrap(); + quote!( + #arg_name: #arg_type, + ) + }); + quote! { #( #_args )* } + } + }; + + let message_args = match desc.args.is_empty() { + true => quote!(), + false => { + let split_args: Vec<&str> = desc.args.split(',').collect(); + let _args = split_args.iter().map(|arg| { + let (v, _) = arg.split_once(':').unwrap(); + let arg_name = parse_str::(v.trim()).unwrap(); + quote!( + , #arg_name + ) + }); + quote! { #( #_args )* } + } + }; + + let func_body = match desc.enabled { + true => { + let mut _body = quote!(); + let message = format!("[{{}}] {}", desc.message.trim()); + let event_name = desc.name.trim(); + let state_name = parse_str::(format!("{}_state", event_name).to_uppercase().as_str()).unwrap(); + _body = quote!( + #_body + #[cfg(any(feature = "trace_to_logger", feature = "trace_to_ftrace"))] + // SAFETY: AtomicBool can be safely shared between threads. + if unsafe { #state_name.load(Ordering::SeqCst) } { + #[cfg(feature = "trace_to_logger")] + { + log::trace!(#message, #event_name.to_string() #message_args); + } + #[cfg(feature = "trace_to_ftrace")] + { + let trace_info = format!(#message, #event_name.to_string() #message_args); + let _result = TRACE_MARKER_FD.lock().unwrap().write_all(trace_info.as_bytes()); + } + } + ); + _body + } + false => quote!(), + }; + + quote!( + #[inline(always)] + pub fn #func_name(#func_args) { + #func_body + } + ) + }); + + TokenStream::from(quote! { #( #trace_func )* }) +} -- Gitee From 692e01f0fde75f04ca8566d101ce612c3aae967e Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Mon, 16 Oct 2023 23:03:53 +0800 Subject: [PATCH 1520/2187] Trace: Support controlling trace events through cmdline or qmp This commit provides a method to enable or disable trace events. You can use "--events" on the command line, or send "trace-event-set-state" in the qmp socket. Signed-off-by: Gan Qixin --- Cargo.lock | 1 + docs/qmp.md | 31 ++++++++++++++ machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 7 ++- machine_manager/src/config/mod.rs | 61 ++++++++++++++------------- machine_manager/src/qmp/qmp_schema.rs | 56 +++++++++++++++++++++++- machine_manager/src/qmp/qmp_socket.rs | 32 ++++++++++++++ 7 files changed, 155 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02a02077f..ac33d333b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,6 +935,7 @@ dependencies = [ "strum", "strum_macros", "thiserror", + "trace", "util", "vmm-sys-util", ] diff --git a/docs/qmp.md b/docs/qmp.md index a33e41109..af7ef9776 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -533,6 +533,37 @@ Query the display image of virtiogpu. Currently only stdvm and gtk supports. <- { "return": { "fileDir": "/tmp/stratovirt-images", "isSuccess": true } } ``` +### trace-event-get-state + +Query whether the trace event is enabled. + +#### Arguments + +* `name` : Pattern used to match event name. + +#### Example + +```json +-> { "execute": "trace-event-get-state", "arguments": { "name": "event_name" } } +<- { "return": [ { "name": "event_name", "state": "disabled" } ] } +``` + +### trace-event-set-state + +Set the state of trace event. + +#### Arguments + +* `name` : Pattern used to match event name. +* `enable` : Whether to enable trace events. + +#### Example + +```json +-> { "execute": "trace-event-set-state", "arguments": { "name": "event_name","enable": true } } +<- { "return": {} } +``` + ## Others ### getfd diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index c63ef5448..29236e67b 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -20,6 +20,7 @@ strum_macros = "0.24.3" once_cell = "1.18.0" thiserror = "1.0" anyhow = "1.0" +trace = { path = "../trace" } util = { path = "../util" } [features] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index fdff9f035..0c9a5fedc 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -13,7 +13,7 @@ use anyhow::{bail, Context, Result}; use crate::{ - config::{add_trace_events, ChardevType, CmdParser, MachineType, VmConfig}, + config::{parse_trace_options, ChardevType, CmdParser, MachineType, VmConfig}, qmp::qmp_socket::QmpSocketPath, temp_cleaner::TempCleaner, }; @@ -585,9 +585,8 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { #[cfg(feature = "usb_camera")] add_args_to_config_multi!((args.values_of("cameradev")), vm_cfg, add_camera_backend); add_args_to_config_multi!((args.values_of("smbios")), vm_cfg, add_smbios); - - if let Some(s) = args.value_of("trace") { - add_trace_events(&s)?; + if let Some(opt) = args.value_of("trace") { + parse_trace_options(&opt)?; } // Check the mini-set for Vm to start is ok diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 2303e47af..20357f0a4 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -81,19 +81,20 @@ pub use vnc::*; use std::collections::HashMap; use std::fs::File; +use std::io::Read; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; use log::error; use serde::{Deserialize, Serialize}; +use trace::set_state_by_pattern; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::{ file::{get_file_alignment, open_file}, num_ops::str_to_usize, test_helper::is_test_enabled, - trace::enable_trace_events, AsAny, }; @@ -608,16 +609,35 @@ impl From for bool { } } -pub fn add_trace_events(config: &str) -> Result<()> { +fn enable_trace_events(path: &str) -> Result<()> { + let mut file = File::open(path).with_context(|| format!("Failed to open {}", path))?; + let mut buf = String::new(); + file.read_to_string(&mut buf) + .with_context(|| format!("Failed to read {}", path))?; + + let events: Vec<&str> = buf.split('\n').filter(|&s| !s.is_empty()).collect(); + for e in events { + set_state_by_pattern(e.trim().to_string(), true).with_context(|| { + format!( + "Unable to set the state of {} according to {}", + e.trim(), + path + ) + })?; + } + Ok(()) +} + +pub fn parse_trace_options(opt: &str) -> Result<()> { let mut cmd_parser = CmdParser::new("trace"); cmd_parser.push("events"); - cmd_parser.get_parameters(config)?; + cmd_parser.get_parameters(opt)?; - if let Some(file) = cmd_parser.get_value::("events")? { - enable_trace_events(&file)?; - return Ok(()); - } - bail!("trace: events file must be set."); + let path = cmd_parser + .get_value::("events")? + .with_context(|| "trace: events file must be set.")?; + enable_trace_events(&path)?; + Ok(()) } /// This struct is a wrapper for `usize`. @@ -793,27 +813,10 @@ mod tests { } #[test] - fn test_add_trace_events_01() { - assert!(add_trace_events("event=test_trace_events").is_err()); - assert!(add_trace_events("events").is_err()); - assert!(add_trace_events("events=test_trace_events").is_err()); - } - - #[test] - fn test_add_trace_events_02() { - use std::fs::File; - use std::io::Write; - - use util::trace::is_trace_event_enabled; - - let file = "/tmp/test_trace_events"; - let mut fd = File::create(file).unwrap(); - let event = "add_trace_events"; - fd.write(event.as_bytes()).unwrap(); - add_trace_events(format!("events={}", file).as_str()).unwrap(); - - assert!(is_trace_event_enabled(event)); - std::fs::remove_file(file).unwrap(); + fn test_parse_trace_options() { + assert!(parse_trace_options("event=test_trace_events").is_err()); + assert!(parse_trace_options("events").is_err()); + assert!(parse_trace_options("events=test_trace_events").is_err()); } #[test] diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index b20e86a01..19e993109 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -134,7 +134,9 @@ define_qmp_command_enum!( human_monitor_command("human-monitor-command", human_monitor_command), blockdev_snapshot_internal_sync("blockdev-snapshot-internal-sync", blockdev_snapshot_internal), blockdev_snapshot_delete_internal_sync("blockdev-snapshot-delete-internal-sync", blockdev_snapshot_internal), - query_vcpu_reg("query-vcpu-reg", query_vcpu_reg) + query_vcpu_reg("query-vcpu-reg", query_vcpu_reg), + trace_event_get_state("trace-event-get-state", trace_event_get_state), + trace_event_set_state("trace-event-set-state", trace_event_set_state) ); /// Command trait for Deserialize and find back Response. @@ -1908,6 +1910,58 @@ pub struct DeviceDeleted { pub path: String, } +/// trace-event-get-state +/// +/// # Arguments +/// +/// * `name` - event name pattern +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "trace-event-get-state", +/// "arguments": { "name": "event_name" } } +/// <- { "return": [ { "name": "event_name", "state": "disabled" } ] } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct trace_event_get_state { + #[serde(rename = "name")] + pub pattern: String, +} +pub type TraceEventGetArgument = trace_event_get_state; + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct TraceEventInfo { + pub name: String, + pub state: bool, +} + +/// trace-event-set-state +/// +/// # Arguments +/// +/// * `name` - event name pattern +/// * `enable` - whether to enable tracing +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "trace-event-set-state", +/// "arguments": { "name": "event_name", +/// "enable": true } } +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct trace_event_set_state { + #[serde(rename = "name")] + pub pattern: String, + #[serde(rename = "enable")] + pub enable: bool, +} +pub type TraceEventSetArgument = trace_event_set_state; + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 911906f66..53b9944f5 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -500,6 +500,38 @@ fn qmp_command_exec( qmp_response = controller.lock().unwrap().getfd(arguments.fd_name, if_fd); id } + QmpCommand::trace_event_get_state { arguments, id } => { + match trace::get_state_by_pattern(arguments.pattern) { + Ok(events) => { + let mut ret = Vec::new(); + for (name, state) in events { + ret.push(qmp_schema::TraceEventInfo { name, state }); + } + qmp_response = + Response::create_response(serde_json::to_value(ret).unwrap(), None); + } + Err(_) => { + qmp_response = Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "Failed to get trace event state".to_string(), + ), + None, + ) + } + } + id + } + QmpCommand::trace_event_set_state { arguments, id } => { + if trace::set_state_by_pattern(arguments.pattern, arguments.enable).is_err() { + qmp_response = Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "Failed to set trace event state".to_string(), + ), + None, + ) + } + id + } _ => None, } } -- Gitee From 8dc5b05883d9742dc168680f31b770e9932cff28 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Mon, 16 Oct 2023 23:05:51 +0800 Subject: [PATCH 1521/2187] Trace: Trace using the generated function Simply replace existing trace points. Signed-off-by: Gan Qixin --- Cargo.lock | 3 + cpu/Cargo.toml | 1 + cpu/src/lib.rs | 6 +- machine/Cargo.toml | 1 + machine/src/aarch64/micro.rs | 16 ++-- machine/src/micro_common/mod.rs | 29 +------ machine/src/x86_64/micro.rs | 14 ++-- trace/trace_event/cpu.toml | 5 ++ trace/trace_event/machine.toml | 29 +++++++ trace/trace_event/virtio.toml | 11 +++ util/src/lib.rs | 1 - util/src/trace.rs | 134 -------------------------------- virtio/Cargo.toml | 1 + virtio/src/device/balloon.rs | 10 +-- virtio/src/device/block.rs | 17 ++-- virtio/src/device/net.rs | 14 ++-- virtio/src/device/rng.rs | 8 +- virtio/src/device/serial.rs | 7 +- virtio/src/lib.rs | 20 ----- 19 files changed, 88 insertions(+), 239 deletions(-) create mode 100644 trace/trace_event/cpu.toml create mode 100644 trace/trace_event/machine.toml create mode 100644 trace/trace_event/virtio.toml delete mode 100644 util/src/trace.rs diff --git a/Cargo.lock b/Cargo.lock index ac33d333b..dba342a13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,6 +316,7 @@ dependencies = [ "migration", "migration_derive", "thiserror", + "trace", "util", "vmm-sys-util", ] @@ -913,6 +914,7 @@ dependencies = [ "migration_derive", "serde_json", "thiserror", + "trace", "ui", "util", "vfio", @@ -1843,6 +1845,7 @@ dependencies = [ "once_cell", "serde_json", "thiserror", + "trace", "ui", "util", "vmm-sys-util", diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 03d901f49..8005771fc 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -19,6 +19,7 @@ machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } +trace = { path = "../trace" } [features] default = [] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index ff6834f3c..ab854d637 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -267,7 +267,7 @@ impl CPUInterface for CPU { topology: &CPUTopology, #[cfg(target_arch = "aarch64")] config: &CPUFeatures, ) -> Result<()> { - trace_cpu_boot_config(boot); + trace::cpu_boot_config(boot); let (cpu_state, _) = &*self.state; if *cpu_state.lock().unwrap() != CpuLifecycleState::Created { return Err(anyhow!(CpuError::RealizeVcpu(format!( @@ -838,10 +838,6 @@ impl CpuTopology { } } -fn trace_cpu_boot_config(cpu_boot_config: &CPUBootConfig) { - util::ftrace!(trace_CPU_boot_config, "{:#?}", cpu_boot_config); -} - /// Capture the boot signal that trap from guest kernel, and then record /// kernel boot timestamp. #[cfg(feature = "boot_time")] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index c1770f727..e3d174370 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -28,6 +28,7 @@ virtio = { path = "../virtio" } vfio = { path = "../vfio" } block_backend = { path = "../block_backend" } ui = { path = "../ui" } +trace = { path = "../trace" } [features] default = [] diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 70949c420..ab8154080 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -14,13 +14,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use crate::{ - micro_common::{ - syscall::syscall_whitelist, trace_cpu_topo, trace_replaceable_info, trace_sysbus, - trace_vm_state, - }, - MachineBase, MachineError, -}; +use crate::{micro_common::syscall::syscall_whitelist, MachineBase, MachineError}; use crate::{LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; @@ -143,15 +137,15 @@ impl MachineOps for LightMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let mut locked_vm = vm.lock().unwrap(); - trace_sysbus(&locked_vm.base.sysbus); - trace_vm_state(&locked_vm.base.vm_state); + trace::sysbus(&locked_vm.base.sysbus); + trace::vm_state(&locked_vm.base.vm_state); let topology = CPUTopology::new().set_topology(( vm_config.machine_config.nr_threads, vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, )); - trace_cpu_topo(&topology); + trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, @@ -191,7 +185,7 @@ impl MachineOps for LightMachine { .create_replaceable_devices() .with_context(|| "Failed to create replaceable devices.")?; locked_vm.add_devices(vm_config)?; - trace_replaceable_info(&locked_vm.replaceable_info); + trace::replaceable_info(&locked_vm.replaceable_info); if let Some(boot_cfg) = boot_config { let mut fdt_helper = FdtBuilder::new(); diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index df0b8935e..4d4b2df56 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -34,7 +34,7 @@ use std::fmt; use std::fmt::Debug; use std::ops::Deref; use std::os::unix::io::RawFd; -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Mutex}; use std::vec::Vec; use anyhow::{anyhow, bail, Context, Result}; @@ -45,8 +45,8 @@ use crate::aarch64::micro::{LayoutEntryType, MEM_LAYOUT}; #[cfg(target_arch = "x86_64")] use crate::x86_64::micro::{LayoutEntryType, MEM_LAYOUT}; use crate::{MachineBase, MachineError, MachineOps}; -use cpu::{CPUTopology, CpuLifecycleState}; -use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; +use cpu::CpuLifecycleState; +use devices::sysbus::{IRQ_BASE, IRQ_MAX}; use machine_manager::config::{ parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, ConfigCheck, DiskFormat, MigrateMode, NetworkInterfaceConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, @@ -257,7 +257,7 @@ impl LightMachine { dev_config, }; - trace_mmio_replaceable_config(&config); + trace::mmio_replaceable_config(&config); configs_lock.push(config); Ok(()) } @@ -950,24 +950,3 @@ impl EventLoopManager for LightMachine { Ok(()) } } - -/// Trace descriptions for some devices at stratovirt startup. -pub(crate) fn trace_cpu_topo(cpu_topo: &CPUTopology) { - util::ftrace!(trace_cpu_topo, "{:#?}", cpu_topo); -} - -pub(crate) fn trace_sysbus(sysbus: &SysBus) { - util::ftrace!(trace_sysbus, "{:?}", sysbus); -} - -pub(crate) fn trace_replaceable_info(replaceable_info: &MmioReplaceableInfo) { - util::ftrace!(trace_replaceable_info, "{:?}", replaceable_info); -} - -pub(crate) fn trace_vm_state(vm_state: &Arc<(Mutex, Condvar)>) { - util::ftrace!(trace_vm_state, "{:#?}", vm_state); -} - -fn trace_mmio_replaceable_config(config: &MmioReplaceableConfig) { - util::ftrace!(trace_mmio_replaceable_config, "{:#?}", config); -} diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 9070fa0a4..6d419c172 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -16,11 +16,7 @@ use anyhow::{bail, Context, Result}; use crate::vm_state; use crate::{ - micro_common::{ - syscall::syscall_whitelist, trace_cpu_topo, trace_replaceable_info, trace_sysbus, - trace_vm_state, - }, - LightMachine, MachineBase, MachineError, MachineOps, + micro_common::syscall::syscall_whitelist, LightMachine, MachineBase, MachineError, MachineOps, }; use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; @@ -151,15 +147,15 @@ impl MachineOps for LightMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let mut locked_vm = vm.lock().unwrap(); - trace_sysbus(&locked_vm.base.sysbus); - trace_vm_state(&locked_vm.base.vm_state); + trace::sysbus(&locked_vm.base.sysbus); + trace::vm_state(&locked_vm.base.vm_state); let topology = CPUTopology::new().set_topology(( vm_config.machine_config.nr_threads, vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, )); - trace_cpu_topo(&topology); + trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_memory( &vm_config.machine_config.mem_config, @@ -178,7 +174,7 @@ impl MachineOps for LightMachine { .create_replaceable_devices() .with_context(|| "Failed to create replaceable devices.")?; locked_vm.add_devices(vm_config)?; - trace_replaceable_info(&locked_vm.replaceable_info); + trace::replaceable_info(&locked_vm.replaceable_info); let boot_config = if migrate_info.0 == MigrateMode::Unknown { Some(locked_vm.load_boot_source(None)?) diff --git a/trace/trace_event/cpu.toml b/trace/trace_event/cpu.toml new file mode 100644 index 000000000..733362ab3 --- /dev/null +++ b/trace/trace_event/cpu.toml @@ -0,0 +1,5 @@ +[[events]] +name = "cpu_boot_config" +args = "cpu_boot_config: &dyn fmt::Debug" +message = "{:#?}" +enabled = true diff --git a/trace/trace_event/machine.toml b/trace/trace_event/machine.toml new file mode 100644 index 000000000..ff2b6f814 --- /dev/null +++ b/trace/trace_event/machine.toml @@ -0,0 +1,29 @@ +[[events]] +name = "cpu_topo" +args = "cpu_topo: &dyn fmt::Debug" +message = "{:#?}" +enabled = true + +[[events]] +name = "sysbus" +args = "sysbus: &dyn fmt::Debug" +message = "{:#?}" +enabled = true + +[[events]] +name = "replaceable_info" +args = "replaceable_info: &dyn fmt::Debug" +message = "{:#?}" +enabled = true + +[[events]] +name = "vm_state" +args = "vm_state: &dyn fmt::Debug" +message = "{:#?}" +enabled = true + +[[events]] +name = "mmio_replaceable_config" +args = "mmio_replaceable_config: &dyn fmt::Debug" +message = "{:#?}" +enabled = true diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml new file mode 100644 index 000000000..0d00910d3 --- /dev/null +++ b/trace/trace_event/virtio.toml @@ -0,0 +1,11 @@ +[[events]] +name = "virtio_receive_request" +args = "device: String, behaviour: String" +message = "{}: Request received from guest {}, ready to start processing." +enabled = true + +[[events]] +name = "virtio_send_interrupt" +args = "device: String" +message = "{}: StratoVirt processing complete, ready to send interrupt to guest." +enabled = true diff --git a/util/src/lib.rs b/util/src/lib.rs index ecf8ded33..794469447 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -36,7 +36,6 @@ pub mod syscall; pub mod tap; pub mod test_helper; pub mod time; -pub mod trace; pub mod unix; #[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; diff --git a/util/src/trace.rs b/util/src/trace.rs deleted file mode 100644 index dee3acb01..000000000 --- a/util/src/trace.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::collections::HashSet; -use std::fs::{File, OpenOptions}; -use std::io::{prelude::Write, BufRead, BufReader}; -use std::ops::Deref; -use std::sync::Arc; - -use anyhow::{Context, Result}; -use arc_swap::ArcSwap; -use log::error; -use once_cell::sync::Lazy; - -static TRACE_MARKER_FD: Lazy> = Lazy::new(open_trace_marker); -static TRACE_EVENTS: Lazy>> = - Lazy::new(|| ArcSwap::new(Arc::new(HashSet::new()))); - -fn open_trace_marker() -> Option { - let file = "/proc/mounts"; - let proc_mounts_fd = match File::open(file) { - Ok(fd) => fd, - Err(e) => { - error!("Failed to open {}: {:?}", file, e); - return None; - } - }; - let mut reader = BufReader::new(proc_mounts_fd); - let mut buf: String; - loop { - buf = String::new(); - match reader.read_line(&mut buf) { - Ok(_) => { - if buf.contains("tracefs") { - break; - } - } - Err(e) => { - error!("Read {} error: {:?}.", &file, e); - return None; - } - } - } - - let fields: Vec<&str> = buf.split(' ').collect(); - let tracefs_mount_point = match fields.get(1) { - Some(s) => s.to_string(), - None => panic!("Failed to get mount point of tracefs."), - }; - - let tracing_on = format!("{}/tracing_on", tracefs_mount_point); - let mut tracing_on_fd = match OpenOptions::new().write(true).open(&tracing_on) { - Ok(fd) => fd, - Err(e) => { - error!("Failed to open {}: {:?}", tracing_on, e); - return None; - } - }; - if let Err(e) = tracing_on_fd.write(b"1") { - error!("Failed to enable tracing_on: {:?}", e); - return None; - } - - let trace_marker = format!("{}/trace_marker", tracefs_mount_point); - match OpenOptions::new().write(true).open(&trace_marker) { - Ok(fd) => Some(fd), - Err(e) => { - error!("Failed to open {}: {:?}", trace_marker, e); - None - } - } -} - -pub fn write_trace_marker(event: &str, msg: &str) { - if !is_trace_event_enabled(event) { - return; - } - - let msg = format!("[{}] {}", event, msg); - if let Err(e) = TRACE_MARKER_FD.as_ref().unwrap().write(msg.as_bytes()) { - error!("Write trace_marker error: {:?}", e); - } -} - -#[macro_export] -macro_rules! ftrace { - ($func: ident) => { - let func = stringify!($func); - let msg = String::new(); - $crate::trace::write_trace_marker(func, &msg); - }; - ($func: ident, $($arg: tt)*) => { - let func = stringify!($func); - let msg = format!("{}", format_args!($($arg)*)); - $crate::trace::write_trace_marker(func, &msg); - }; -} - -pub fn enable_trace_events(file: &str) -> Result<()> { - let fd = File::open(file).with_context(|| format!("Failed to open {}.", file))?; - let mut reader = BufReader::new(fd); - - loop { - let mut buf = String::new(); - let size = reader - .read_line(&mut buf) - .with_context(|| format!("Read {} error.", file))?; - - if size == 0 { - return Ok(()); - } - - let mut trace_events = TRACE_EVENTS.load().deref().deref().clone(); - trace_events.insert(buf.trim().to_string()); - TRACE_EVENTS.store(Arc::new(trace_events)); - } -} - -pub fn is_trace_event_enabled(event: &str) -> bool { - if TRACE_EVENTS.load().is_empty() { - return false; - } - - TRACE_EVENTS.load().contains(event) -} diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 30a8febc8..24928d042 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -27,6 +27,7 @@ devices = {path = "../devices"} block_backend = {path = "../block_backend"} chardev_backend = {path = "../chardev_backend" } ui = { path = "../ui", features = ["console"], optional = true } +trace = {path = "../trace"} [features] default = [] diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 210c470bf..865d7b128 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -26,8 +26,8 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; use crate::{ error::*, read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, - VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_F_VERSION_1, VIRTIO_TYPE_BALLOON, + VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, + VIRTIO_TYPE_BALLOON, }; use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, @@ -608,10 +608,10 @@ impl BalloonIoHandler { /// balloon. fn process_balloon_queue(&mut self, req_type: bool) -> Result<()> { let queue = if req_type { - self.trace_request("Balloon".to_string(), "to inflate".to_string()); + trace::virtio_receive_request("Balloon".to_string(), "to inflate".to_string()); &self.inf_queue } else { - self.trace_request("Balloon".to_string(), "to deflate".to_string()); + trace::virtio_receive_request("Balloon".to_string(), "to inflate".to_string()); &self.def_queue }; let mut locked_queue = queue.lock().unwrap(); @@ -1176,8 +1176,6 @@ pub fn balloon_allow_list(syscall_allow_list: &mut Vec) { ]) } -impl VirtioTrace for BalloonIoHandler {} - #[cfg(test)] mod tests { pub use super::*; diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 8c5226d6e..4fe0ddb00 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -28,11 +28,11 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ check_config_space_rw, gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, - VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioTrace, - VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, - VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, - VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, + VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_BLK_F_DISCARD, + VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, + VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, + VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; @@ -180,7 +180,7 @@ impl AioCompleteCb { .with_context(|| { VirtioError::InterruptTrigger("blk io completion", VirtioInterruptType::Vring) })?; - self.trace_send_interrupt("Block".to_string()); + trace::virtio_send_interrupt("Block".to_string()); } Ok(()) } @@ -692,7 +692,7 @@ impl BlockIoHandler { } fn process_queue(&mut self) -> Result { - self.trace_request("Block".to_string(), "to IO".to_string()); + trace::virtio_receive_request("Block".to_string(), "to IO".to_string()); let result = self.process_queue_suppress_notify(); if result.is_err() { report_virtio_error( @@ -1326,9 +1326,6 @@ impl StateTransfer for Block { impl MigrationHook for Block {} -impl VirtioTrace for BlockIoHandler {} -impl VirtioTrace for AioCompleteCb {} - #[cfg(test)] mod tests { use std::sync::atomic::{AtomicU32, Ordering}; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 067ec47e4..6bf9e6339 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -29,8 +29,8 @@ use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ check_config_space_rw, iov_discard_front, iov_to_buf, mem_to_buf, read_config_default, report_virtio_error, virtio_has_feature, ElemIovec, Element, Queue, VirtioBase, VirtioDevice, - VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VirtioTrace, - VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, + VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, @@ -775,7 +775,7 @@ impl NetIoHandler { } fn handle_rx(&mut self) -> Result<()> { - self.trace_request("Net".to_string(), "to rx".to_string()); + trace::virtio_receive_request("Net".to_string(), "to rx".to_string()); if self.tap.is_none() { return Ok(()); } @@ -853,7 +853,7 @@ impl NetIoHandler { .with_context(|| { VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) })?; - self.trace_send_interrupt("Net".to_string()); + trace::virtio_send_interrupt("Net".to_string()); } rx_packets += 1; @@ -894,7 +894,7 @@ impl NetIoHandler { } fn handle_tx(&mut self) -> Result<()> { - self.trace_request("Net".to_string(), "to tx".to_string()); + trace::virtio_receive_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); let mut tx_packets = 0; @@ -940,7 +940,7 @@ impl NetIoHandler { .with_context(|| { VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) })?; - self.trace_send_interrupt("Net".to_string()); + trace::virtio_send_interrupt("Net".to_string()); } tx_packets += 1; if tx_packets >= self.queue_size { @@ -1673,8 +1673,6 @@ impl StateTransfer for Net { impl MigrationHook for Net {} -impl VirtioTrace for NetIoHandler {} - #[cfg(test)] mod tests { pub use super::super::*; diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 06313cef2..8a05b02bb 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -25,7 +25,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::error::VirtioError; use crate::{ - ElemIovec, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VirtioTrace, + ElemIovec, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; use address_space::AddressSpace; @@ -92,7 +92,7 @@ impl RngHandler { } fn process_queue(&mut self) -> Result<()> { - self.trace_request("Rng".to_string(), "to IO".to_string()); + trace::virtio_receive_request("Rng".to_string(), "to IO".to_string()); let mut queue_lock = self.queue.lock().unwrap(); let mut need_interrupt = false; @@ -145,7 +145,7 @@ impl RngHandler { .with_context(|| { VirtioError::InterruptTrigger("rng", VirtioInterruptType::Vring) })?; - self.trace_send_interrupt("Rng".to_string()); + trace::virtio_send_interrupt("Rng".to_string()) } Ok(()) @@ -348,8 +348,6 @@ impl StateTransfer for Rng { impl MigrationHook for Rng {} -impl VirtioTrace for RngHandler {} - #[cfg(test)] mod tests { use std::io::Write; diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index dd955286c..ca7b6f0c7 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -26,8 +26,7 @@ use vmm_sys_util::eventfd::EventFd; use crate::{ gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, report_virtio_error, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, - VirtioTrace, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_CONSOLE, + VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; use chardev_backend::chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; @@ -417,7 +416,7 @@ struct SerialControlHandler { impl SerialPortHandler { fn output_handle(&mut self) { - self.trace_request("Serial".to_string(), "to IO".to_string()); + trace::virtio_receive_request("Serial".to_string(), "to IO".to_string()); self.output_handle_internal().unwrap_or_else(|e| { error!("Port handle output error: {:?}", e); @@ -615,8 +614,6 @@ impl SerialPortHandler { } } -impl VirtioTrace for SerialPortHandler {} - impl EventNotifierHelper for SerialPortHandler { fn internal_notifiers(serial_handler: Arc>) -> Vec { let mut notifiers = Vec::new(); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 3fd82bfe3..e31892f72 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -770,26 +770,6 @@ fn read_config_default(config: &[u8], offset: u64, mut data: &mut [u8]) -> Resul Ok(()) } -/// The trait for trace descriptions of virtio device interactions -/// on the front and back ends. -pub trait VirtioTrace { - fn trace_request(&self, device: String, behaviour: String) { - util::ftrace!( - trace_request, - "{} : Request received from Guest {}, ready to start processing.", - device, - behaviour - ); - } - fn trace_send_interrupt(&self, device: String) { - util::ftrace!( - trace_send_interrupt, - "{} : stratovirt processing complete, ready to send interrupt to guest.", - device - ); - } -} - /// The function used to inject interrupt to guest when encounter an virtio error. pub fn report_virtio_error( interrupt_cb: Arc, -- Gitee From d22c956e44bff33305f19365b153a39545b79a72 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 3 Jan 2024 09:22:46 +0800 Subject: [PATCH 1522/2187] Trace: Update trace documentation Add description of how to use the new trace infrastructure. Signed-off-by: Gan Qixin --- docs/trace.md | 80 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/docs/trace.md b/docs/trace.md index 8bd59654b..eec31e74a 100644 --- a/docs/trace.md +++ b/docs/trace.md @@ -3,32 +3,72 @@ This document describes the way for debugging and profiling in StratoVirt and how to use it. -## Ftrace +## Add trace event -Ftrace is a tracer provided by Linux kernel, which can help linux developers to -debug or analyze issues. As ftrace can avoid performance penalty, it's especially -suited for performance issues. +### Modify configuration file -StratoVirt use ftrace by writing trace data to ftrace marker, and developers can -read trace records from *trace* file under mounted ftrace director, -e.g. /sys/kernel/debug/tracing/trace. +First, you need to modify or crate toml file in the trace/trace_event directory to +add a new event in order to generate the trace function. Take virtio_receive_request +as an example: + +```toml +[[events]] +name = "virtio_receive_request" +args = "device: String, behaviour: String" +message = "{}: Request received from guest {}, ready to start processing." +enabled = true +``` -## How to use +In the above configuration, "name" is used to represent the only trace event, +and duplication is not allowed; "message" and "args" will be formatted as +information output by trace; "enabled" indicates whether it is enabled during +compilation. -Trace events are put in StratoVirt by the macro *ftrace!*. The first parameter the -macro receives is name of the trace event. Remaining parameters the macro receives -are the same as *println!* or *format!*, i.e. the first parameter is a format string, -and additional parameters passed replace the {}s within the format string. +### Call trace function +Just call the trace function where needed. ```rust -#[macro_use] -extern crate util; - -fn trace_example() { - ftrace!(trace_example, "Test for tracer."); +fn process_queue(&mut self) -> Result<()> { + trace::virtio_receive_request("Rng".to_string(), "to IO".to_string()); + let mut queue_lock = self.queue.lock().unwrap(); + let mut need_interrupt = false; + ...... } ``` -Trace events in StratoVirt are disabled by default. Users can pass the file listing -enabled events by launching StratoVirt with "-trace events=". The file should -contains one event name per line. +## Trace control interface + +Trace events in StratoVirt are disabled by default. Users can control whether +the trace event is enabled through the command line or qmp command. + +### Command line +Before starting, you can prepare the trace event list that needs to be enabled +and pass it to StratoVirt through [-trace](config_guidebook.md#3-trace). + +### QMP +During the running, you can send the [trace-event-set-state](qmp.md#trace-event-set-state) +command through the qmp socket to enable or disable trace event. Similarly, +using the [trace-event-get-state](qmp.md#trace-event-get-state) command can check +whether the setting is successful. + +## Choose trace backends + +By setting different features during compilation, trace can generate specified +code to support different trace tools. StratoVirt currently supports two kinds +of settings. + +### log + +StratoVirt supports outputting trace to the log file at trace level. Turn on +the **trace_to_logger** feature to use is. + +### ftrace + +Ftrace is a tracer provided by Linux kernel, which can help linux developers to +debug or analyze issues. As ftrace can avoid performance penalty, it's especially +suited for performance issues. + +It can be enabled by turning on the **trace_to_ftrace** feature during compilation. +StratoVirt use ftrace by writing trace data to ftrace marker, and developers can +read trace records from trace file under mounted ftrace director, +e.g. /sys/kernel/debug/tracing/trace. -- Gitee From 860e8a6f6a206b7e904926bdc5c6976d05203cd7 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 3 Jan 2024 12:38:26 +0800 Subject: [PATCH 1523/2187] Trace: Recompile trace lib when event changes Modifying the trace_event file will not trigger recompilation, which may cause compilation to fail. This problem can be solved by setting "cargo:rerun-if-changed" in build.rs. Signed-off-by: Gan Qixin --- trace/build.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 trace/build.rs diff --git a/trace/build.rs b/trace/build.rs new file mode 100644 index 000000000..cac00d636 --- /dev/null +++ b/trace/build.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +fn main() { + println!( + "cargo:rerun-if-changed={}/trace_event", + std::env::var("CARGO_MANIFEST_DIR").unwrap() + ); +} -- Gitee From ac24942678fb4fd2be96ddaa963e4cb97d6bb51f Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 5 Jan 2024 16:31:04 +0800 Subject: [PATCH 1524/2187] cpu: print error num when received an unhandled kvm exit event Signed-off-by: yezengruan --- cpu/src/error.rs | 4 ++-- cpu/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpu/src/error.rs b/cpu/src/error.rs index 4e6517573..e93ff6e24 100644 --- a/cpu/src/error.rs +++ b/cpu/src/error.rs @@ -36,8 +36,8 @@ pub enum CpuError { VcpuHltEvent(u8), #[error("CPU {0}/KVM received an unexpected exit reason: {1}!")] VcpuExitReason(u8, String), - #[error("CPU {0}/KVM received an unhandled kvm exit event!")] - UnhandledKvmExit(u8), + #[error("CPU {0}/KVM received an unhandled kvm exit event: error {1}!")] + UnhandledKvmExit(u8, i32), #[error("Vcpu not present in local thread.")] VcpuLocalThreadNotPresent, #[error("No Machine Interface saved in CPU")] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 1406e30c2..f6c9c8b6f 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -570,7 +570,7 @@ impl CPUInterface for CPU { self.fd.set_kvm_immediate_exit(0); } _ => { - return Err(anyhow!(CpuError::UnhandledKvmExit(self.id()))); + return Err(anyhow!(CpuError::UnhandledKvmExit(self.id(), e.errno()))); } }; } -- Gitee From 5d70ba114725b0f7578192702f54ce5f790b91c1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 6 Jan 2024 16:46:47 +0800 Subject: [PATCH 1525/2187] machine: delete redundant plug_XXX functions for hot-plug The hot-plug processing function is similar to the function of adding devices when starting a virtual machine. Eg: "add_virtio_pci_blk" is similar to "plug_virtio_pci_blk" function. Delete these redundant plug_XXX functions. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 60 +++-- machine/src/standard_common/mod.rs | 306 +++----------------------- machine_manager/src/config/devices.rs | 73 ++++++ machine_manager/src/config/drive.rs | 48 ---- machine_manager/src/config/network.rs | 65 +----- 5 files changed, 159 insertions(+), 393 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5a2de9e63..375d940f8 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1063,7 +1063,12 @@ pub trait MachineOps { Ok(()) } - fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + fn add_virtio_pci_blk( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + hotplug: bool, + ) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( @@ -1100,11 +1105,18 @@ pub trait MachineOps { device, &device_cfg.id, ); - self.reset_bus(&device_cfg.id)?; + if !hotplug { + self.reset_bus(&device_cfg.id)?; + } Ok(()) } - fn add_virtio_pci_scsi(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + fn add_virtio_pci_scsi( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + hotplug: bool, + ) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( @@ -1121,7 +1133,9 @@ pub trait MachineOps { let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) .with_context(|| "Failed to add virtio scsi controller")?; - self.reset_bus(&device_cfg.id)?; + if !hotplug { + self.reset_bus(&device_cfg.id)?; + } device.lock().unwrap().config.boot_prefix = pci_dev.lock().unwrap().get_dev_path(); Ok(()) } @@ -1192,7 +1206,12 @@ pub trait MachineOps { Ok(()) } - fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + fn add_virtio_pci_net( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + hotplug: bool, + ) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_net(vm_config, cfg_args)?; @@ -1220,11 +1239,18 @@ pub trait MachineOps { device }; self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, need_irqfd)?; - self.reset_bus(&device_cfg.id)?; + if !hotplug { + self.reset_bus(&device_cfg.id)?; + } Ok(()) } - fn add_vhost_user_blk_pci(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + fn add_vhost_user_blk_pci( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + hotplug: bool, + ) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( @@ -1250,7 +1276,9 @@ pub trait MachineOps { self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); } } - self.reset_bus(&device_cfg.id)?; + if !hotplug { + self.reset_bus(&device_cfg.id)?; + } Ok(()) } @@ -1298,7 +1326,7 @@ pub trait MachineOps { Ok(()) } - fn add_vfio_device(&mut self, cfg_args: &str) -> Result<()> { + fn add_vfio_device(&mut self, cfg_args: &str, hotplug: bool) -> Result<()> { let device_cfg: VfioConfig = parse_vfio(cfg_args)?; let bdf = get_pci_bdf(cfg_args)?; let multifunc = get_multi_function(cfg_args)?; @@ -1309,7 +1337,9 @@ pub trait MachineOps { &device_cfg.sysfsdev, multifunc, )?; - self.reset_bus(&device_cfg.id)?; + if !hotplug { + self.reset_bus(&device_cfg.id)?; + } Ok(()) } @@ -1774,10 +1804,10 @@ pub trait MachineOps { self.add_virtio_mmio_block(vm_config, cfg_args)?; } "virtio-blk-pci" => { - self.add_virtio_pci_blk(vm_config, cfg_args)?; + self.add_virtio_pci_blk(vm_config, cfg_args, false)?; } "virtio-scsi-pci" => { - self.add_virtio_pci_scsi(vm_config, cfg_args)?; + self.add_virtio_pci_scsi(vm_config, cfg_args, false)?; } "scsi-hd" => { self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_DISK)?; @@ -1789,7 +1819,7 @@ pub trait MachineOps { self.add_virtio_mmio_net(vm_config, cfg_args)?; } "virtio-net-pci" => { - self.add_virtio_pci_net(vm_config, cfg_args)?; + self.add_virtio_pci_net(vm_config, cfg_args, false)?; } "pcie-root-port" => { self.add_pci_root_port(cfg_args)?; @@ -1813,13 +1843,13 @@ pub trait MachineOps { self.add_virtio_rng(vm_config, cfg_args)?; } "vfio-pci" => { - self.add_vfio_device(cfg_args)?; + self.add_vfio_device(cfg_args, false)?; } "vhost-user-blk-device" => { self.add_vhost_user_blk_device(vm_config, cfg_args)?; } "vhost-user-blk-pci" => { - self.add_vhost_user_blk_pci(vm_config, cfg_args)?; + self.add_vhost_user_blk_pci(vm_config, cfg_args, false)?; } "vhost-user-fs-pci" | "vhost-user-fs-device" => { self.add_virtio_fs(vm_config, cfg_args)?; diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 30afc97fa..386812d78 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -61,9 +61,8 @@ use devices::pci::PciBus; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_config; use machine_manager::config::{ - get_chardev_config, get_netdev_config, get_pci_df, memory_unit_conversion, BlkDevConfig, - ChardevType, ConfigCheck, DiskFormat, DriveConfig, ExBool, NetworkInterfaceConfig, NumaNode, - NumaNodes, PciBdf, ScsiCntlrConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, M, MAX_VIRTIO_QUEUE, + get_chardev_config, get_netdev_config, memory_unit_conversion, ConfigCheck, DiskFormat, + DriveConfig, ExBool, NumaNode, NumaNodes, VmConfig, M, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ @@ -71,7 +70,6 @@ use machine_manager::machine::{ }; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; -use migration::MigrationManager; #[cfg(feature = "gtk")] use ui::gtk::qmp_query_display_image; use ui::input::{input_button, input_move_abs, input_point_sync, key_event, Axis}; @@ -80,11 +78,7 @@ use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; use util::byte_code::ByteCode; use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; -use virtio::{ - qmp_balloon, qmp_query_balloon, Block, BlockState, - ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, - VhostKern, VhostUser, VirtioDevice, VirtioNetState, VirtioPciDevice, -}; +use virtio::{qmp_balloon, qmp_query_balloon}; const MAX_REGION_SIZE: u64 = 65536; @@ -871,245 +865,20 @@ pub(crate) trait AcpiBuilder { } } -fn get_device_bdf(bus: Option, addr: Option) -> Result { - let mut pci_bdf = PciBdf { - bus: bus.unwrap_or_else(|| String::from("pcie.0")), - addr: (0, 0), - }; - let addr = addr.unwrap_or_else(|| String::from("0x0")); - pci_bdf.addr = get_pci_df(&addr).with_context(|| "Failed to get device num or function num")?; - Ok(pci_bdf) -} - impl StdMachine { - fn plug_virtio_pci_blk( - &mut self, - pci_bdf: &PciBdf, - args: &qmp_schema::DeviceAddArgument, - ) -> Result<()> { - let multifunction = args.multifunction.unwrap_or(false); - let drive = args.drive.as_ref().with_context(|| "Drive not set")?; - let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); - let vm_config = self.get_vm_config(); - let mut locked_vmconfig = vm_config.lock().unwrap(); - let nr_cpus = locked_vmconfig.machine_config.nr_cpus; - let blk = if let Some(conf) = locked_vmconfig.drives.get(drive) { - let dev = BlkDevConfig { - id: args.id.clone(), - path_on_host: conf.path_on_host.clone(), - read_only: conf.read_only, - direct: conf.direct, - serial_num: args.serial_num.clone(), - iothread: args.iothread.clone(), - iops: conf.iops, - queues: args.queues.unwrap_or_else(|| { - VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) - }), - boot_index: args.boot_index, - chardev: None, - socket_path: None, - aio: conf.aio, - queue_size, - discard: conf.discard, - write_zeroes: conf.write_zeroes, - format: conf.format, - l2_cache_size: conf.l2_cache_size, - refcount_cache_size: conf.refcount_cache_size, - }; - dev.check()?; - dev - } else { - bail!("Drive not found"); - }; - locked_vmconfig.add_blk_device_config(args); - drop(locked_vmconfig); - - if let Some(bootindex) = args.boot_index { - self.check_bootindex(bootindex) - .with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?; - } - - let blk_id = blk.id.clone(); - let blk = Arc::new(Mutex::new(Block::new(blk, self.get_drive_files()))); - let pci_dev = self - .add_virtio_pci_device(&args.id, pci_bdf, blk.clone(), multifunction, false) - .with_context(|| "Failed to add virtio pci block device")?; - - if let Some(bootindex) = args.boot_index { - if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { - self.add_bootindex_devices(bootindex, &dev_path, &args.id); - } - } - - MigrationManager::register_device_instance(BlockState::descriptor(), blk, &blk_id); - Ok(()) - } - - fn plug_virtio_pci_scsi( - &mut self, - pci_bdf: &PciBdf, - args: &qmp_schema::DeviceAddArgument, - ) -> Result<()> { - let multifunction = args.multifunction.unwrap_or(false); - let nr_cpus = self.get_vm_config().lock().unwrap().machine_config.nr_cpus; - let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); - let dev_cfg = ScsiCntlrConfig { - id: args.id.clone(), - iothread: args.iothread.clone(), - queues: args.queues.unwrap_or_else(|| { - VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) - }) as u32, - boot_prefix: None, - queue_size, - }; - dev_cfg.check()?; - - let device = Arc::new(Mutex::new(ScsiCntlr::new(dev_cfg.clone()))); - - let bus_name = format!("{}.0", dev_cfg.id); - scsi_cntlr_create_scsi_bus(&bus_name, &device)?; - - let virtio_pci_dev = self - .add_virtio_pci_device(&args.id, pci_bdf, device.clone(), multifunction, false) - .with_context(|| "Failed to add virtio scsi controller")?; - device.lock().unwrap().config.boot_prefix = virtio_pci_dev.lock().unwrap().get_dev_path(); - - Ok(()) - } - - fn plug_vhost_user_blk_pci( - &mut self, - pci_bdf: &PciBdf, - args: &qmp_schema::DeviceAddArgument, - ) -> Result<()> { - let multifunction = args.multifunction.unwrap_or(false); - let vm_config = self.get_vm_config(); - let locked_vmconfig = vm_config.lock().unwrap(); - let chardev = args.chardev.as_ref().with_context(|| "Chardev not set")?; - let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); - let socket_path = self - .get_socket_path(&locked_vmconfig, chardev.to_string()) - .with_context(|| "Failed to get socket path")?; - let nr_cpus = locked_vmconfig.machine_config.nr_cpus; - let dev = BlkDevConfig { - id: args.id.clone(), - queues: args.queues.unwrap_or_else(|| { - VirtioPciDevice::virtio_pci_auto_queues_num(0, nr_cpus, MAX_VIRTIO_QUEUE) - }), - boot_index: args.boot_index, - chardev: Some(chardev.to_string()), - socket_path, - queue_size, - ..BlkDevConfig::default() - }; - - dev.check()?; - drop(locked_vmconfig); - - let blk = Arc::new(Mutex::new(VhostUser::Block::new(&dev, self.get_sys_mem()))); - self.add_virtio_pci_device(&args.id, pci_bdf, blk, multifunction, true) - .with_context(|| "Failed to add vhost user blk pci device")?; - - Ok(()) - } - - fn get_socket_path(&self, vm_config: &VmConfig, chardev: String) -> Result> { - let char_dev = vm_config - .chardev - .get(&chardev) - .with_context(|| format!("Chardev: {:?} not found for character device", &chardev))?; - - let socket_path = match &char_dev.backend { - ChardevType::UnixSocket { - path, - server, - nowait, - } => { - if *server || *nowait { - bail!( - "Argument \'server\' or \'nowait\' is not needed for chardev \'{}\'", - &chardev - ); - } - Some(path.clone()) - } - _ => { - bail!("Chardev {:?} backend should be unix-socket type.", &chardev); - } - }; - - Ok(socket_path) - } - - fn plug_virtio_pci_net( + fn plug_usb_device( &mut self, - pci_bdf: &PciBdf, + vm_config: &mut VmConfig, args: &qmp_schema::DeviceAddArgument, ) -> Result<()> { - let multifunction = args.multifunction.unwrap_or(false); - let netdev = args.netdev.as_ref().with_context(|| "Netdev not set")?; - let queue_size = args.queue_size.unwrap_or(DEFAULT_VIRTQUEUE_SIZE); - let vm_config = self.get_vm_config(); - let mut locked_vmconfig = vm_config.lock().unwrap(); - let dev = if let Some(conf) = locked_vmconfig.netdevs.get(netdev) { - let mut socket_path: Option = None; - if let Some(chardev) = &conf.chardev { - socket_path = self - .get_socket_path(&locked_vmconfig, (&chardev).to_string()) - .with_context(|| "Failed to get socket path")?; - } - let dev = NetworkInterfaceConfig { - id: args.id.clone(), - host_dev_name: conf.ifname.clone(), - mac: args.mac.clone(), - tap_fds: conf.tap_fds.clone(), - vhost_type: conf.vhost_type.clone(), - vhost_fds: conf.vhost_fds.clone(), - iothread: args.iothread.clone(), - queues: conf.queues, - mq: conf.queues > 2, - socket_path, - queue_size, - }; - dev.check()?; - dev - } else { - bail!("Netdev not found"); - }; - locked_vmconfig.add_net_device_config(args); - drop(locked_vmconfig); - - if dev.vhost_type.is_some() { - let net: Arc> = - if dev.vhost_type == Some(String::from("vhost-kernel")) { - Arc::new(Mutex::new(VhostKern::Net::new(&dev, self.get_sys_mem()))) - } else { - Arc::new(Mutex::new(VhostUser::Net::new(&dev, self.get_sys_mem()))) - }; - self.add_virtio_pci_device(&args.id, pci_bdf, net, multifunction, true) - .with_context(|| "Failed to add vhost-kernel/vhost-user net device")?; - } else { - let net_id = dev.id.clone(); - let net = Arc::new(Mutex::new(virtio::Net::new(dev))); - self.add_virtio_pci_device(&args.id, pci_bdf, net.clone(), multifunction, false) - .with_context(|| "Failed to add virtio net device")?; - MigrationManager::register_device_instance(VirtioNetState::descriptor(), net, &net_id); - } - - Ok(()) - } - - fn plug_usb_device(&mut self, args: &qmp_schema::DeviceAddArgument) -> Result<()> { let driver = args.driver.as_str(); - let vm_config = self.get_vm_config(); - let mut locked_vmconfig = vm_config.lock().unwrap(); let cfg_args = format!("id={}", args.id); match driver { "usb-kbd" => { - self.add_usb_keyboard(&mut locked_vmconfig, &cfg_args)?; + self.add_usb_keyboard(vm_config, &cfg_args)?; } "usb-tablet" => { - self.add_usb_tablet(&mut locked_vmconfig, &cfg_args)?; + self.add_usb_tablet(vm_config, &cfg_args)?; } #[cfg(feature = "usb_camera")] "usb-camera" => { @@ -1120,7 +889,7 @@ impl StdMachine { if let Some(iothread) = args.iothread.as_ref() { cfg_args = format!("{},iothread={}", cfg_args, iothread); } - self.add_usb_camera(&mut locked_vmconfig, &cfg_args)?; + self.add_usb_camera(vm_config, &cfg_args)?; } #[cfg(feature = "usb_host")] "usb-host" => { @@ -1145,7 +914,7 @@ impl StdMachine { cfg_args = format!("{},isobsize={}", cfg_args, args.isobsize.as_ref().unwrap()); } - self.add_usb_host(&mut locked_vmconfig, &cfg_args)?; + self.add_usb_host(vm_config, &cfg_args)?; } _ => { bail!("Invalid usb device driver '{}'", driver); @@ -1161,25 +930,6 @@ impl StdMachine { self.detach_usb_from_xhci_controller(&mut locked_vmconfig, id) } - fn plug_vfio_pci_device( - &mut self, - bdf: &PciBdf, - args: &qmp_schema::DeviceAddArgument, - ) -> Result<()> { - if args.host.is_none() && args.sysfsdev.is_none() { - bail!("Neither option \"host\" nor \"sysfsdev\" was not provided."); - } - if args.host.is_some() && args.sysfsdev.is_some() { - bail!("Both option \"host\" and \"sysfsdev\" was provided."); - } - - let host = args.host.as_ref().map_or("", String::as_str); - let sysfsdev = args.sysfsdev.as_ref().map_or("", String::as_str); - let multifunc = args.multifunction.unwrap_or(false); - self.create_vfio_pci_device(&args.id, bdf, host, sysfsdev, multifunc) - .with_context(|| "Failed to plug vfio-pci device.") - } - /// When windows emu exits, stratovirt should exits too. #[cfg(feature = "windows_emu_pid")] pub(crate) fn watch_windows_emu_pid( @@ -1383,23 +1133,16 @@ impl DeviceInterface for StdMachine { ); } - // Use args.bus.clone() and args.addr.clone() because args borrowed in the following - // process. - let pci_bdf = match get_device_bdf(args.bus.clone(), args.addr.clone()) { - Ok(bdf) => bdf, - Err(e) => { - return Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(e.to_string()), - None, - ) - } - }; - let driver = args.driver.as_str(); + let vm_config = self.get_vm_config(); + let mut locked_vmconfig = vm_config.lock().unwrap(); + let mut vm_config_clone = locked_vmconfig.clone(); match driver { "virtio-blk-pci" => { - if let Err(e) = self.plug_virtio_pci_blk(&pci_bdf, args.as_ref()) { + let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); + if let Err(e) = self.add_virtio_pci_blk(&mut vm_config_clone, &cfg_args, true) { error!("{:?}", e); + locked_vmconfig.del_device_by_id(args.id); let err_str = format!("Failed to add virtio pci blk: {}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(err_str), @@ -1408,8 +1151,10 @@ impl DeviceInterface for StdMachine { } } "virtio-scsi-pci" => { - if let Err(e) = self.plug_virtio_pci_scsi(&pci_bdf, args.as_ref()) { + let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); + if let Err(e) = self.add_virtio_pci_scsi(&mut vm_config_clone, &cfg_args, true) { error!("{:?}", e); + locked_vmconfig.del_device_by_id(args.id); let err_str = format!("Failed to add virtio scsi controller: {}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(err_str), @@ -1418,8 +1163,10 @@ impl DeviceInterface for StdMachine { } } "vhost-user-blk-pci" => { - if let Err(e) = self.plug_vhost_user_blk_pci(&pci_bdf, args.as_ref()) { + let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); + if let Err(e) = self.add_vhost_user_blk_pci(&mut vm_config_clone, &cfg_args, true) { error!("{:?}", e); + locked_vmconfig.del_device_by_id(args.id); let err_str = format!("Failed to add vhost user blk pci: {}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(err_str), @@ -1428,8 +1175,10 @@ impl DeviceInterface for StdMachine { } } "virtio-net-pci" => { - if let Err(e) = self.plug_virtio_pci_net(&pci_bdf, args.as_ref()) { + let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); + if let Err(e) = self.add_virtio_pci_net(&mut vm_config_clone, &cfg_args, true) { error!("{:?}", e); + locked_vmconfig.del_device_by_id(args.id); let err_str = format!("Failed to add virtio pci net: {}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(err_str), @@ -1438,8 +1187,10 @@ impl DeviceInterface for StdMachine { } } "vfio-pci" => { - if let Err(e) = self.plug_vfio_pci_device(&pci_bdf, args.as_ref()) { + let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); + if let Err(e) = self.add_vfio_device(&cfg_args, true) { error!("{:?}", e); + locked_vmconfig.del_device_by_id(args.id); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, @@ -1447,7 +1198,7 @@ impl DeviceInterface for StdMachine { } } "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" => { - if let Err(e) = self.plug_usb_device(args.as_ref()) { + if let Err(e) = self.plug_usb_device(&mut locked_vmconfig, args.as_ref()) { error!("{:?}", e); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), @@ -1475,6 +1226,7 @@ impl DeviceInterface for StdMachine { ); } } + drop(locked_vmconfig); // It's safe to call get_pci_host().unwrap() because it has been checked before. let locked_pci_host = self.get_pci_host().unwrap().lock().unwrap(); diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index daec689cc..ab8a63907 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -14,8 +14,81 @@ use anyhow::Result; use regex::Regex; use super::{CmdParser, VmConfig}; +use crate::qmp::qmp_schema; impl VmConfig { + /// Add config of hot-plugged devices to `VmConfig`. + pub fn add_device_config(&mut self, args: &qmp_schema::DeviceAddArgument) -> String { + let mut device_info = args.driver.clone(); + + device_info = format!("{},id={}", device_info, args.id); + if let Some(addr) = &args.addr { + device_info = format!("{},addr={}", device_info, addr); + } + if let Some(bus) = &args.bus { + device_info = format!("{},bus={}", device_info, bus); + } + if let Some(drive) = &args.drive { + device_info = format!("{},drive={}", device_info, drive); + } + if let Some(mq) = &args.mq { + device_info = format!("{},mq={}", device_info, mq); + } + if let Some(iothread) = &args.iothread { + device_info = format!("{},iothread={}", device_info, iothread); + } + if let Some(multi) = &args.multifunction { + if *multi { + device_info = format!("{},multifunction=on", device_info); + } else { + device_info = format!("{},multifunction=off", device_info); + } + } + if let Some(queues) = &args.queues { + device_info = format!("{},num-queues={}", device_info, queues); + } + if let Some(size) = &args.queue_size { + device_info = format!("{},queue-size={}", device_info, size); + } + + // For net devices only. + if let Some(mac) = &args.mac { + device_info = format!("{},mac={}", device_info, mac); + } + if let Some(netdev) = &args.netdev { + device_info = format!("{},netdev={}", device_info, netdev); + } + if let Some(mq) = &args.mq { + device_info = format!("{},mq={}", device_info, mq); + } + + // For vhost devices only. + if let Some(chardev) = &args.chardev { + device_info = format!("{},chardev={}", device_info, chardev); + } + + // For block devices only. + if let Some(serial_num) = &args.serial_num { + device_info = format!("{},serial={}", device_info, serial_num); + } + if let Some(boot_index) = &args.boot_index { + device_info = format!("{},bootindex={}", device_info, boot_index); + } + + // For vfio devices only. + if let Some(host) = &args.host { + device_info = format!("{},addr={}", device_info, host); + } + if let Some(sysfsdev) = &args.sysfsdev { + device_info = format!("{},addr={}", device_info, sysfsdev); + } + + self.devices + .push((args.driver.clone(), device_info.clone())); + + device_info + } + pub fn add_device(&mut self, device_config: &str) -> Result<()> { let mut cmd_params = CmdParser::new("device"); cmd_params.push(""); diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index a042280d8..e88826a53 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -24,7 +24,6 @@ use crate::config::{ check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; -use crate::qmp::qmp_schema; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; const MAX_SERIAL_NUM: usize = 20; @@ -618,53 +617,6 @@ impl VmConfig { Ok(()) } - /// Add 'pci blk devices' to `VmConfig devices`. - pub fn add_blk_device_config(&mut self, args: &qmp_schema::DeviceAddArgument) { - let mut device_info = args.driver.clone(); - - device_info = format!("{},id={}", device_info, args.id); - - if let Some(drive) = &args.drive { - device_info = format!("{},drive={}", device_info, drive); - } - - if let Some(serial_num) = &args.serial_num { - device_info = format!("{},serial={}", device_info, serial_num); - } - - if let Some(addr) = &args.addr { - device_info = format!("{},addr={}", device_info, addr); - } - if let Some(bus) = &args.bus { - device_info = format!("{},bus={}", device_info, bus); - } - - if args.multifunction.is_some() { - if args.multifunction.unwrap() { - device_info = format!("{},multifunction=on", device_info); - } else { - device_info = format!("{},multifunction=off", device_info); - } - } - - if let Some(iothread) = &args.iothread { - device_info = format!("{},iothread={}", device_info, iothread); - } - - if let Some(mq) = &args.mq { - device_info = format!("{},mq={}", device_info, mq); - } - - if let Some(queues) = &args.queues { - device_info = format!("{},num-queues={}", device_info, queues); - } - - if let Some(boot_index) = &args.boot_index { - device_info = format!("{},bootindex={}", device_info, boot_index); - } - - self.devices.push((args.driver.clone(), device_info)); - } /// Delete drive config in vm config by id. /// /// # Arguments diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index eba1e2c4f..f7d79a13b 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -285,18 +285,18 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result bool { -- Gitee From a8e35fb06f7913e6f671f6e84fa7b08dbf86bfe6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 6 Jan 2024 19:52:30 +0800 Subject: [PATCH 1526/2187] machine: using a unified USB device creating interface Using a unified interface to create USB devices(keyboard/tablet/storage/host/camera). And then we can use this interface when plugging usb devices. Delete useless `plug_usb_xxx`/`add_usb_xxx` functions. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 149 ++++++++++---------------- machine/src/standard_common/mod.rs | 66 ++---------- machine_manager/src/config/devices.rs | 27 +++++ 3 files changed, 87 insertions(+), 155 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 375d940f8..8cb98adc4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1681,89 +1681,62 @@ pub trait MachineOps { Ok(()) } - /// Add usb keyboard. + /// Add usb device. /// /// # Arguments /// - /// * `cfg_args` - Keyboard Configuration. - fn add_usb_keyboard(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_keyboard(cfg_args)?; - // SAFETY: id is already checked not none in parse_usb_keyboard(). - let keyboard = UsbKeyboard::new(device_cfg.id.unwrap()); - let kbd = keyboard - .realize() - .with_context(|| "Failed to realize usb keyboard device")?; - self.attach_usb_to_xhci_controller(vm_config, kbd)?; - Ok(()) - } - - /// Add usb tablet. - /// - /// # Arguments - /// - /// * `cfg_args` - Tablet Configuration. - fn add_usb_tablet(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_tablet(cfg_args)?; - // SAFETY: id is already checked not none in parse_usb_tablet(). - let tablet = UsbTablet::new(device_cfg.id.unwrap()); - let tbt = tablet - .realize() - .with_context(|| "Failed to realize usb tablet device")?; - - self.attach_usb_to_xhci_controller(vm_config, tbt)?; - - Ok(()) - } - - /// Add usb camera. - /// - /// # Arguments - /// - /// * `cfg_args` - Camera Configuration. - #[cfg(feature = "usb_camera")] - fn add_usb_camera(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_camera(vm_config, cfg_args)?; - let camera = UsbCamera::new(device_cfg)?; - let camera = camera.realize()?; - - self.attach_usb_to_xhci_controller(vm_config, camera)?; - - Ok(()) - } - - /// Add usb storage. - /// - /// # Arguments - /// - /// * `cfg_args` - USB Storage Configuration. - fn add_usb_storage(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_storage(vm_config, cfg_args)?; - let storage = UsbStorage::new(device_cfg, self.get_drive_files()); - let stg = storage - .realize() - .with_context(|| "Failed to realize usb storage device")?; - - self.attach_usb_to_xhci_controller(vm_config, stg)?; - - Ok(()) - } - - /// Add usb host. - /// - /// # Arguments - /// - /// * `cfg_args` - USB Host Configuration. - #[cfg(feature = "usb_host")] - fn add_usb_host(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_usb_host(cfg_args)?; - let usbhost = UsbHost::new(device_cfg)?; - - let usbhost = usbhost - .realize() - .with_context(|| "Failed to realize usb host device")?; - - self.attach_usb_to_xhci_controller(vm_config, usbhost)?; + /// * `driver` - USB device class. + /// * `cfg_args` - USB device Configuration. + fn add_usb_device( + &mut self, + driver: &str, + vm_config: &mut VmConfig, + cfg_args: &str, + ) -> Result<()> { + let usb_device = match driver { + "usb-kbd" => { + let device_cfg = parse_usb_keyboard(cfg_args)?; + // SAFETY: id is already checked not none in parse_usb_keyboard(). + let keyboard = UsbKeyboard::new(device_cfg.id.unwrap()); + keyboard + .realize() + .with_context(|| "Failed to realize usb keyboard device")? + } + "usb-tablet" => { + let device_cfg = parse_usb_tablet(cfg_args)?; + // SAFETY: id is already checked not none in parse_usb_tablet(). + let tablet = UsbTablet::new(device_cfg.id.unwrap()); + tablet + .realize() + .with_context(|| "Failed to realize usb tablet device")? + } + #[cfg(feature = "usb_camera")] + "usb-camera" => { + let device_cfg = parse_usb_camera(vm_config, cfg_args)?; + let camera = UsbCamera::new(device_cfg)?; + camera + .realize() + .with_context(|| "Failed to realize usb camera device")? + } + "usb-storage" => { + let device_cfg = parse_usb_storage(vm_config, cfg_args)?; + let storage = UsbStorage::new(device_cfg, self.get_drive_files()); + storage + .realize() + .with_context(|| "Failed to realize usb storage device")? + } + #[cfg(feature = "usb_host")] + "usb-host" => { + let device_cfg = parse_usb_host(cfg_args)?; + let usbhost = UsbHost::new(device_cfg)?; + usbhost + .realize() + .with_context(|| "Failed to realize usb host device")? + } + _ => bail!("Unknown usb device classes."), + }; + self.attach_usb_to_xhci_controller(vm_config, usb_device)?; Ok(()) } @@ -1857,22 +1830,8 @@ pub trait MachineOps { "nec-usb-xhci" => { self.add_usb_xhci(cfg_args)?; } - "usb-kbd" => { - self.add_usb_keyboard(vm_config, cfg_args)?; - } - "usb-tablet" => { - self.add_usb_tablet(vm_config, cfg_args)?; - } - #[cfg(feature = "usb_camera")] - "usb-camera" => { - self.add_usb_camera(vm_config, cfg_args)?; - } - "usb-storage" => { - self.add_usb_storage(vm_config, cfg_args)?; - } - #[cfg(feature = "usb_host")] - "usb-host" => { - self.add_usb_host(vm_config, cfg_args)?; + "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-storage" | "usb-host" => { + self.add_usb_device(&dev.0, vm_config, cfg_args)?; } #[cfg(feature = "virtio_gpu")] "virtio-gpu-pci" => { diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 386812d78..09900acd8 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -60,9 +60,11 @@ use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; use devices::pci::PciBus; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_config; +#[cfg(feature = "windows_emu_pid")] +use machine_manager::config::VmConfig; use machine_manager::config::{ get_chardev_config, get_netdev_config, memory_unit_conversion, ConfigCheck, DiskFormat, - DriveConfig, ExBool, NumaNode, NumaNodes, VmConfig, M, + DriveConfig, ExBool, NumaNode, NumaNodes, M, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ @@ -866,64 +868,6 @@ pub(crate) trait AcpiBuilder { } impl StdMachine { - fn plug_usb_device( - &mut self, - vm_config: &mut VmConfig, - args: &qmp_schema::DeviceAddArgument, - ) -> Result<()> { - let driver = args.driver.as_str(); - let cfg_args = format!("id={}", args.id); - match driver { - "usb-kbd" => { - self.add_usb_keyboard(vm_config, &cfg_args)?; - } - "usb-tablet" => { - self.add_usb_tablet(vm_config, &cfg_args)?; - } - #[cfg(feature = "usb_camera")] - "usb-camera" => { - let mut cfg_args = format!("id={}", args.id); - if let Some(cameradev) = &args.cameradev { - cfg_args = format!("{},cameradev={}", cfg_args, cameradev); - } - if let Some(iothread) = args.iothread.as_ref() { - cfg_args = format!("{},iothread={}", cfg_args, iothread); - } - self.add_usb_camera(vm_config, &cfg_args)?; - } - #[cfg(feature = "usb_host")] - "usb-host" => { - let mut cfg_args = format!("id={}", args.id); - let default_value = "0".to_string(); - let hostbus = args.hostbus.as_ref().unwrap_or(&default_value); - let hostaddr = args.hostaddr.as_ref().unwrap_or(&default_value); - let vendorid = args.vendorid.as_ref().unwrap_or(&default_value); - let productid = args.productid.as_ref().unwrap_or(&default_value); - - cfg_args = format!( - "{},hostbus={},hostaddr={},vendorid={},productid={}", - cfg_args, hostbus, hostaddr, vendorid, productid - ); - if args.hostport.is_some() { - cfg_args = format!("{},hostport={}", cfg_args, args.hostport.as_ref().unwrap()); - } - if args.isobufs.is_some() { - cfg_args = format!("{},isobufs={}", cfg_args, args.isobufs.as_ref().unwrap()); - } - if args.isobsize.is_some() { - cfg_args = format!("{},isobsize={}", cfg_args, args.isobsize.as_ref().unwrap()); - } - - self.add_usb_host(vm_config, &cfg_args)?; - } - _ => { - bail!("Invalid usb device driver '{}'", driver); - } - }; - - Ok(()) - } - fn handle_unplug_usb_request(&mut self, id: String) -> Result<()> { let vm_config = self.get_vm_config(); let mut locked_vmconfig = vm_config.lock().unwrap(); @@ -1198,8 +1142,10 @@ impl DeviceInterface for StdMachine { } } "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" => { - if let Err(e) = self.plug_usb_device(&mut locked_vmconfig, args.as_ref()) { + let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); + if let Err(e) = self.add_usb_device(driver, &mut vm_config_clone, &cfg_args) { error!("{:?}", e); + locked_vmconfig.del_device_by_id(args.id); return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError(e.to_string()), None, diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index ab8a63907..fbb7da05e 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -83,6 +83,33 @@ impl VmConfig { device_info = format!("{},addr={}", device_info, sysfsdev); } + // For usb camera devices only. + if let Some(cameradev) = &args.cameradev { + device_info = format!("{},cameradev={}", device_info, cameradev); + } + + // For usb host devices only. + if args.driver == "usb-host" { + let default_value = "0".to_string(); + let hostbus = args.hostbus.as_ref().unwrap_or(&default_value); + let hostaddr = args.hostaddr.as_ref().unwrap_or(&default_value); + let vendorid = args.vendorid.as_ref().unwrap_or(&default_value); + let productid = args.productid.as_ref().unwrap_or(&default_value); + device_info = format!( + "{},hostbus={},hostaddr={},vendorid={},productid={}", + device_info, hostbus, hostaddr, vendorid, productid + ); + if let Some(hostport) = &args.hostport { + device_info = format!("{},hostport={}", device_info, hostport); + } + if let Some(isobufs) = &args.isobufs { + device_info = format!("{},isobufs={}", device_info, isobufs); + } + if let Some(isobsize) = &args.isobsize { + device_info = format!("{},isobsize={}", device_info, isobsize); + } + } + self.devices .push((args.driver.clone(), device_info.clone())); -- Gitee From 7ebc8b35d139443060cce41d45e98e61507e0644 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Mon, 25 Dec 2023 19:26:28 +0800 Subject: [PATCH 1527/2187] PvPanic: implement a new virtual pci device named PvPanic to give the virtual machine the ability to sense guest os crashes or failures. Signed-off-by: Fan Xuan Zhe --- Cargo.toml | 1 + devices/Cargo.toml | 1 + devices/src/misc/mod.rs | 3 + devices/src/misc/pvpanic.rs | 231 ++++++++++++++++++++++ devices/src/pci/config.rs | 3 + docs/build_guide.md | 1 + docs/config_guidebook.md | 16 ++ machine/Cargo.toml | 1 + machine/src/lib.rs | 22 +++ machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 3 +- machine_manager/src/config/mod.rs | 4 + machine_manager/src/config/pvpanic_pci.rs | 66 +++++++ 13 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 devices/src/misc/pvpanic.rs create mode 100644 machine_manager/src/config/pvpanic_pci.rs diff --git a/Cargo.toml b/Cargo.toml index ddff7afa7..802a9a6da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ default = [] boot_time = ["machine/boot_time"] scream_alsa = ["machine/scream_alsa"] scream_pulseaudio = ["machine/scream_pulseaudio"] +pvpanic = ["machine/pvpanic"] demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] usb_camera_v4l2 = ["machine/usb_camera_v4l2"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 6bbb2336c..eb5eb3d62 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -44,6 +44,7 @@ default = [] scream = ["machine_manager/scream"] scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] +pvpanic = ["machine_manager/pvpanic"] demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] usb_camera = ["dep:cairo-rs", "machine_manager/usb_camera"] diff --git a/devices/src/misc/mod.rs b/devices/src/misc/mod.rs index 79e459724..36c2d9c5b 100644 --- a/devices/src/misc/mod.rs +++ b/devices/src/misc/mod.rs @@ -15,3 +15,6 @@ pub mod scream; #[cfg(feature = "scream")] mod ivshmem; + +#[cfg(feature = "pvpanic")] +pub mod pvpanic; diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs new file mode 100644 index 000000000..d612b22b9 --- /dev/null +++ b/devices/src/misc/pvpanic.rs @@ -0,0 +1,231 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{ + atomic::{AtomicU16, Ordering}, + Arc, Mutex, Weak, +}; + +use anyhow::{bail, Context, Result}; +use log::{debug, error, info}; + +use crate::pci::{ + config::{ + PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, PCI_CONFIG_SPACE_SIZE, + PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, + }, + le_write_u16, PciBus, PciDevBase, PciDevOps, +}; +use crate::{Device, DeviceBase}; +use address_space::{GuestAddress, Region, RegionOps}; +use machine_manager::config::{PvpanicDevConfig, PVPANIC_CRASHLOADED, PVPANIC_PANICKED}; + +const PCI_CLASS_SYSTEM_OTHER: u16 = 0x0880; +const PCI_CLASS_PI: u16 = 0x09; +const PCI_REVISION_ID_PVPANIC: u8 = 1; + +#[cfg(target_arch = "aarch64")] +// param size in Region::init_io_region must greater than 4 +const PVPANIC_REG_BAR_SIZE: u64 = 0x4; +#[cfg(target_arch = "x86_64")] +const PVPANIC_REG_BAR_SIZE: u64 = 0x1; + +#[derive(Copy, Clone)] +pub struct PvPanicState { + supported_features: u32, +} + +impl PvPanicState { + fn new(supported_features: u32) -> Self { + Self { supported_features } + } + + fn handle_event(&self, event: u32) -> Result<()> { + if (event & !(PVPANIC_PANICKED | PVPANIC_CRASHLOADED)) != 0 { + error!("pvpanic: unknown event 0x{:X}", event); + } + + if (event & PVPANIC_PANICKED) == PVPANIC_PANICKED + && (self.supported_features & PVPANIC_PANICKED) == PVPANIC_PANICKED + { + info!("pvpanic: panicked event"); + } + + if (event & PVPANIC_CRASHLOADED) == PVPANIC_CRASHLOADED + && (self.supported_features & PVPANIC_CRASHLOADED) == PVPANIC_CRASHLOADED + { + info!("pvpanic: crashloaded event"); + } + + Ok(()) + } +} + +pub struct PvPanicPci { + base: PciDevBase, + dev_id: AtomicU16, + pvpanic: Arc, +} + +impl PvPanicPci { + pub fn new(config: &PvpanicDevConfig, devfn: u8, parent_bus: Weak>) -> Self { + Self { + base: PciDevBase { + base: DeviceBase::new(config.id.clone(), false), + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), + devfn, + parent_bus, + }, + dev_id: AtomicU16::new(0), + pvpanic: Arc::new(PvPanicState::new(config.supported_features)), + } + } + + fn register_bar(&mut self) -> Result<()> { + let cloned_pvpanic_read = self.pvpanic.clone(); + let bar0_read = Arc::new(move |data: &mut [u8], _: GuestAddress, _: u64| -> bool { + debug!( + "pvpanic: read bar0 called event {}", + cloned_pvpanic_read.supported_features + ); + + data[0] = cloned_pvpanic_read.supported_features as u8; + true + }); + + let cloned_pvpanic_write = self.pvpanic.clone(); + let bar0_write = Arc::new(move |data: &[u8], _: GuestAddress, _: u64| -> bool { + debug!("pvpanic: write bar0 called event {:?}", data); + let val = u8::from_le_bytes(match data.try_into() { + Ok(value) => value, + Err(_) => { + return false; + } + }); + + matches!(cloned_pvpanic_write.handle_event(val as u32), Ok(())) + }); + + let bar0_region_ops = RegionOps { + read: bar0_read, + write: bar0_write, + }; + + let mut bar_region = + Region::init_io_region(PVPANIC_REG_BAR_SIZE, bar0_region_ops, "PvPanic"); + bar_region.set_access_size(1); + + self.base.config.register_bar( + 0, + bar_region, + RegionType::Mem64Bit, + false, + PVPANIC_REG_BAR_SIZE, + ) + } +} + +impl Device for PvPanicPci { + fn device_base(&self) -> &DeviceBase { + &self.base.base + } + + fn device_base_mut(&mut self) -> &mut DeviceBase { + &mut self.base.base + } + + fn hotpluggable(&self) -> bool { + false + } + + fn name(&self) -> String { + "PvPanic".to_string() + } +} + +impl PciDevOps for PvPanicPci { + fn pci_base(&self) -> &PciDevBase { + &self.base + } + + fn pci_base_mut(&mut self) -> &mut PciDevBase { + &mut self.base + } + + fn realize(mut self) -> Result<()> { + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; + le_write_u16( + &mut self.base.config.config, + VENDOR_ID as usize, + PCI_VENDOR_ID_REDHAT, + )?; + + le_write_u16( + &mut self.base.config.config, + DEVICE_ID as usize, + PCI_DEVICE_ID_REDHAT_PVPANIC, + )?; + + self.base.config.config[REVISION_ID] = PCI_REVISION_ID_PVPANIC; + + le_write_u16( + &mut self.base.config.config, + SUB_CLASS_CODE as usize, + PCI_CLASS_SYSTEM_OTHER, + )?; + + self.base.config.config[PCI_CLASS_PI as usize] = 0x00; + + self.base.config.config[HEADER_TYPE as usize] = 0x00; + + self.register_bar() + .with_context(|| "pvpanic: device register bar failed")?; + + // Attach to the PCI bus. + let devfn = self.base.devfn; + let dev = Arc::new(Mutex::new(self)); + let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); + let mut locked_pci_bus = pci_bus.lock().unwrap(); + let device_id = locked_pci_bus.generate_dev_id(devfn); + dev.lock() + .unwrap() + .dev_id + .store(device_id, Ordering::Release); + let pci_device = locked_pci_bus.devices.get(&devfn); + if pci_device.is_none() { + locked_pci_bus.devices.insert(devfn, dev); + } else { + bail!( + "pvpanic: Devfn {:?} has been used by {:?}", + &devfn, + pci_device.unwrap().lock().unwrap().name() + ); + } + + Ok(()) + } + + fn write_config(&mut self, offset: usize, data: &[u8]) { + let parent_bus = self.base.parent_bus.upgrade().unwrap(); + let locked_parent_bus = parent_bus.lock().unwrap(); + + self.base.config.write( + offset, + data, + self.dev_id.load(Ordering::Acquire), + #[cfg(target_arch = "x86_64")] + Some(&locked_parent_bus.io_region), + Some(&locked_parent_bus.mem_region), + ); + } +} diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index b035c70b1..c2e2e6db3 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -300,6 +300,9 @@ pub const PCI_EXP_HP_EV_CCI: u16 = PCI_EXP_SLTCTL_CCIE; // XHCI device id pub const PCI_DEVICE_ID_REDHAT_XHCI: u16 = 0x000d; +// PvPanic device id +pub const PCI_DEVICE_ID_REDHAT_PVPANIC: u16 = 0x0011; + // Device classes and subclasses pub const PCI_CLASS_MEMORY_RAM: u16 = 0x0500; pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; diff --git a/docs/build_guide.md b/docs/build_guide.md index 9932ef6ef..ed2752222 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -60,6 +60,7 @@ List of optional features: - vnc: enable VNC display - ramfb: enable ramfb display device - virtio_gpu: enable virtio-gpu virtualized graphics card +- pvpanic: enable virtualized pvpanic pci device ```shell $ cargo build --workspace --bins --release --features "scream_alsa" diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index b0c45e379..5148cdb2d 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1144,6 +1144,22 @@ Note: Only supported on aarch64. Please see the [4. Build with features](docs/build_guide.md) if you want to enable ramfb. +### 2.21 pvpanic +pvpanic is a virtual pci device. It is used to give the virtual machine the ability to sense guest os crashes or failures. + +Four properties are supported for pvpanic device. +* id: unique device id. +* bus: bus number of the device. +* addr: slot number. +* supported-features: supported features, 0-3 refers to `None`, `panicked`, `crashload` and `panicked and crashload` respectively. 3 is suggested. + +Sample Configuration: +```shell +-device pvpanic,id=,bus=,addr=<0x7>[,supported-features=<0|1|2|3>] +``` + +Please see the [4. Build with features](docs/build_guide.md) if you want to enable pvpanic. + ## 3. Trace Users can specify the configuration file which lists events to trace. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index c1770f727..a9b37867c 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -35,6 +35,7 @@ boot_time = ["cpu/boot_time"] scream = ["devices/scream", "machine_manager/scream"] scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] +pvpanic = ["devices/pvpanic"] demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c7163a8ff..5a2de9e63 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -51,6 +51,8 @@ use address_space::{ use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::FwCfgOps; +#[cfg(feature = "pvpanic")] +use devices::misc::pvpanic::PvPanicPci; #[cfg(feature = "scream")] use devices::misc::scream::Scream; #[cfg(feature = "demo_device")] @@ -75,6 +77,8 @@ use hypervisor::kvm::KVM_FDS; use machine_manager::config::parse_demo_dev; #[cfg(feature = "virtio_gpu")] use machine_manager::config::parse_gpu; +#[cfg(feature = "pvpanic")] +use machine_manager::config::parse_pvpanic; #[cfg(feature = "usb_camera")] use machine_manager::config::parse_usb_camera; #[cfg(feature = "usb_host")] @@ -1045,6 +1049,20 @@ pub trait MachineOps { locked_boot_order_list.retain(|item| item.id != dev_id); } + #[cfg(feature = "pvpanic")] + fn add_pvpanic(&mut self, cfg_args: &str) -> Result<()> { + let bdf = get_pci_bdf(cfg_args)?; + let device_cfg = parse_pvpanic(cfg_args)?; + + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let pcidev = PvPanicPci::new(&device_cfg, devfn, parent_bus); + pcidev + .realize() + .with_context(|| "Failed to realize pvpanic device")?; + + Ok(()) + } + fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; @@ -1842,6 +1860,10 @@ pub trait MachineOps { "ivshmem-scream" => { self.add_ivshmem_scream(vm_config, cfg_args)?; } + #[cfg(feature = "pvpanic")] + "pvpanic" => { + self.add_pvpanic(cfg_args)?; + } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index c63ef5448..f74256492 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -36,3 +36,4 @@ gtk = [] vnc = [] ramfb = [] virtio_gpu = [] +pvpanic = [] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index fdff9f035..e3b049da7 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -254,7 +254,8 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { \n\t\tadd usb storage: -device usb-storage,id=,drive=; \ \n\t\tadd scsi controller: -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction=on|off][,iothread=][,num-queues=]; \ \n\t\tadd scsi hard disk: -device scsi-hd,scsi-id=<0>,bus=,lun=<0>,drive=,id=; \ - \n\t\tadd vhost user fs: -device vhost-user-fs-pci,id=,chardev=,tag=") + \n\t\tadd vhost user fs: -device vhost-user-fs-pci,id=,chardev=,tag=; \ + \n\t\tadd pvpanic: -device pvpanic,id=,bus=,addr=<0x7>[,supported-features=<0|1|2|3>];") .takes_values(true), ) .arg( diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 2303e47af..8363b519a 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -34,6 +34,8 @@ mod machine_config; mod network; mod numa; mod pci; +#[cfg(feature = "pvpanic")] +mod pvpanic_pci; #[cfg(all(feature = "ramfb", target_arch = "aarch64"))] mod ramfb; mod rng; @@ -67,6 +69,8 @@ pub use machine_config::*; pub use network::*; pub use numa::*; pub use pci::*; +#[cfg(feature = "pvpanic")] +pub use pvpanic_pci::*; #[cfg(all(feature = "ramfb", target_arch = "aarch64"))] pub use ramfb::*; pub use rng::*; diff --git a/machine_manager/src/config/pvpanic_pci.rs b/machine_manager/src/config/pvpanic_pci.rs new file mode 100644 index 000000000..d0c3b8723 --- /dev/null +++ b/machine_manager/src/config/pvpanic_pci.rs @@ -0,0 +1,66 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::config::{CmdParser, ConfigCheck}; +use anyhow::{bail, Context, Result}; +use serde::{Deserialize, Serialize}; + +pub const PVPANIC_PANICKED: u32 = 1 << 0; +pub const PVPANIC_CRASHLOADED: u32 = 1 << 1; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PvpanicDevConfig { + pub id: String, + pub supported_features: u32, +} + +impl Default for PvpanicDevConfig { + fn default() -> Self { + PvpanicDevConfig { + id: "".to_string(), + supported_features: PVPANIC_PANICKED | PVPANIC_CRASHLOADED, + } + } +} + +impl ConfigCheck for PvpanicDevConfig { + fn check(&self) -> Result<()> { + Ok(()) + } +} + +pub fn parse_pvpanic(args_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("pvpanic"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("addr") + .push("supported-features"); + cmd_parser.parse(args_config)?; + + let mut pvpanicdevcfg = PvpanicDevConfig::default(); + + if let Some(features) = cmd_parser.get_value::("supported-features")? { + pvpanicdevcfg.supported_features = + match features & !(PVPANIC_PANICKED | PVPANIC_CRASHLOADED) { + 0 => features, + _ => bail!("Unsupported pvpanic device features {}", features), + } + } + + pvpanicdevcfg.id = cmd_parser + .get_value::("id")? + .with_context(|| "No id configured for pvpanic device")?; + + Ok(pvpanicdevcfg) +} -- Gitee From 0ab60fec5e72aaf114d2eba1ef75d74c04e70079 Mon Sep 17 00:00:00 2001 From: Alisa Wang Date: Tue, 9 Jan 2024 14:49:23 +0800 Subject: [PATCH 1528/2187] block_test:fix several MST test cases Fix several MST test cases about virtio_blk discard Signed-off-by: Alisa Wang --- tests/mod_test/tests/block_test.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 36542f3a5..c19c4c78f 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -12,6 +12,7 @@ use std::cell::RefCell; use std::mem::size_of; +use std::os::linux::fs::MetadataExt; use std::process::Command; use std::rc::Rc; use std::time::{Duration, Instant}; @@ -44,6 +45,7 @@ use util::offset_of; use virtio::device::block::VirtioBlkConfig; const TEST_IMAGE_SIZE_1M: u64 = 1024 * 1024; +const DEFAULT_SECTOR_SIZE: u64 = 512; fn execute_cmd(cmd: String) -> Vec { let args = cmd.split(' ').collect::>(); @@ -120,12 +122,15 @@ fn virtio_blk_discard_and_write_zeroes( } fn get_disk_size(img_path: Rc) -> u64 { - let out = execute_cmd(format!("du -shk {}", img_path)); - let str_out = std::str::from_utf8(&out) - .unwrap() - .split('\t') - .collect::>(); - serde_json::from_str::(str_out[0]).unwrap() + let mut file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(img_path.as_ref()) + .unwrap(); + let meta_data = file.metadata().unwrap(); + let blk_size = meta_data.st_blocks() * DEFAULT_SECTOR_SIZE; + blk_size >> 10 } fn virtio_blk_check_discard_config(blk: Rc>) { @@ -1765,7 +1770,7 @@ fn blk_feature_discard() { ); if image_type == ImageType::Raw && status == VIRTIO_BLK_S_OK { let image_size = get_disk_size(image_path.clone()); - assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); + assert_eq!(image_size, full_disk_size - num_sectors as u64 / 2); } else if image_type == ImageType::Qcow2 && status == VIRTIO_BLK_S_OK && (num_sectors as u64 * 512 & CLUSTER_SIZE - 1) == 0 @@ -1935,7 +1940,7 @@ fn blk_feature_write_zeroes() { && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) { let image_size = get_disk_size(image_path.clone()); - assert_eq!(image_size, (TEST_IMAGE_SIZE >> 10) - num_sectors as u64 / 2); + assert_eq!(image_size, full_disk_size - num_sectors as u64 / 2); } else if image_type == ImageType::Qcow2 && status == VIRTIO_BLK_S_OK && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) -- Gitee From 58198b1e4d86cbdd119e1836d2ed8b4d97484536 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 10 Jan 2024 14:56:28 +0800 Subject: [PATCH 1529/2187] test: remove unused code for block mod test Signed-off-by: yezengruan --- tests/mod_test/tests/block_test.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index c19c4c78f..7e1e4b46b 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -13,7 +13,6 @@ use std::cell::RefCell; use std::mem::size_of; use std::os::linux::fs::MetadataExt; -use std::process::Command; use std::rc::Rc; use std::time::{Duration, Instant}; use std::{thread, time}; @@ -47,25 +46,6 @@ use virtio::device::block::VirtioBlkConfig; const TEST_IMAGE_SIZE_1M: u64 = 1024 * 1024; const DEFAULT_SECTOR_SIZE: u64 = 512; -fn execute_cmd(cmd: String) -> Vec { - let args = cmd.split(' ').collect::>(); - if args.len() <= 0 { - return vec![]; - } - - let mut cmd_exe = Command::new(args[0]); - for i in 1..args.len() { - cmd_exe.arg(args[i]); - } - - let output = cmd_exe - .output() - .expect(format!("Failed to execute {}", cmd).as_str()); - println!("{:?}, output: {:?}", args, output); - assert!(output.status.success()); - output.stdout -} - fn virtio_blk_discard_and_write_zeroes( blk: Rc>, test_state: Rc>, @@ -122,7 +102,7 @@ fn virtio_blk_discard_and_write_zeroes( } fn get_disk_size(img_path: Rc) -> u64 { - let mut file = std::fs::OpenOptions::new() + let file = std::fs::OpenOptions::new() .read(true) .write(true) .create(true) -- Gitee From 952b03259e694336c803b75772ad9322d9ec2df4 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sat, 13 Jan 2024 10:17:28 +0800 Subject: [PATCH 1530/2187] fmt: fix a doorbell typos Signed-off-by: yezengruan --- devices/src/usb/xhci/xhci_regs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index c244dddc7..79751223f 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -680,7 +680,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } } -/// Build doorbeell region ops. +/// Build doorbell region ops. pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let doorbell_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { debug!("doorbell read addr {:x} offset {:x}", addr.0, offset); -- Gitee From 3e71e66ac853586839791162e35b83a11b022309 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 14 Jan 2024 22:35:47 +0800 Subject: [PATCH 1531/2187] Replace the ExitCode trait with the std::process::ExitCode. The ExitCode trait is defined in the code to obtain the status code when the main process is terminated. As of rustc 1.61.0, the standard library defines std::process::ExitCode to do the same thing, so the ExitCode trait defined in the code is replaced with std::process::ExitCode. Signed-off-by: Yan Wen --- ozone/src/main.rs | 34 +++++++++------------------------- src/main.rs | 30 +++++++----------------------- virtio/src/lib.rs | 1 - 3 files changed, 16 insertions(+), 49 deletions(-) diff --git a/ozone/src/main.rs b/ozone/src/main.rs index 8c0abb18e..e43add3f5 100644 --- a/ozone/src/main.rs +++ b/ozone/src/main.rs @@ -21,40 +21,24 @@ mod syscall; pub use error::OzoneError; +use std::io::Write; +use std::process::{ExitCode, Termination}; + use anyhow::{Context, Result}; use crate::args::create_args_parser; use crate::handler::OzoneHandler; -pub trait ExitCode { - /// Returns the value to use as the exit status. - fn code(self) -> i32; -} - -impl ExitCode for i32 { - fn code(self) -> i32 { - self - } -} - -impl ExitCode for () { - fn code(self) -> i32 { - 0 - } -} - -fn main() { - use std::io::Write; - - ::std::process::exit(match run() { - Ok(ret) => ExitCode::code(ret), +fn main() -> ExitCode { + match run() { + Ok(ret) => ret.report(), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format_args!("{:?}", e)) + write!(&mut std::io::stderr(), "{}", format_args!("{:?}", e)) .expect("Error writing to stderr"); - 1 + ExitCode::FAILURE } - }); + } } fn run() -> Result<()> { diff --git a/src/main.rs b/src/main.rs index 18eee88e2..13fe59934 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::io::Write; +use std::process::{ExitCode, Termination}; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -57,33 +58,16 @@ enum MainError { }, } -trait ExitCode { - /// Returns the value to use as the exit status. - fn code(self) -> i32; -} - -impl ExitCode for i32 { - fn code(self) -> i32 { - self - } -} - -impl ExitCode for () { - fn code(self) -> i32 { - 0 - } -} - -fn main() { - ::std::process::exit(match run() { - Ok(ret) => ExitCode::code(ret), +fn main() -> ExitCode { + match run() { + Ok(ret) => ret.report(), Err(ref e) => { - write!(&mut ::std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) + write!(&mut std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) .expect("Error writing to stderr"); - 1 + ExitCode::FAILURE } - }); + } } fn run() -> Result<()> { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index e31892f72..3caf4cfbb 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -41,7 +41,6 @@ pub use device::rng::{Rng, RngState}; pub use device::scsi_cntlr as ScsiCntlr; pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; pub use error::VirtioError; -pub use error::*; pub use queue::*; pub use transport::virtio_mmio::{VirtioMmioDevice, VirtioMmioState}; pub use transport::virtio_pci::VirtioPciDevice; -- Gitee From 3cca758a8ec4aeddf21268dd150f6e7695633987 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 14:10:35 +0800 Subject: [PATCH 1532/2187] Trace: add trace for VNC Add trace for VNC Signed-off-by: Xiao Ye --- Cargo.lock | 1 + trace/trace_event/ui.toml | 119 ++++++++++++++++++++++++++++++++++++ ui/Cargo.toml | 1 + ui/src/vnc/auth_sasl.rs | 9 ++- ui/src/vnc/auth_vencrypt.rs | 5 ++ ui/src/vnc/client_io.rs | 8 +++ ui/src/vnc/mod.rs | 12 ++++ ui/src/vnc/server_io.rs | 5 +- 8 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 trace/trace_event/ui.toml diff --git a/Cargo.lock b/Cargo.lock index 1d16e80e9..94debc3c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1721,6 +1721,7 @@ dependencies = [ "serde_json", "sscanf", "thiserror", + "trace", "util", "vmm-sys-util", ] diff --git a/trace/trace_event/ui.toml b/trace/trace_event/ui.toml new file mode 100644 index 000000000..39c84379a --- /dev/null +++ b/trace/trace_event/ui.toml @@ -0,0 +1,119 @@ +[[events]] +name = "vnc_dpy_switch" +args = "old_width: &dyn fmt::Debug, old_height: &dyn fmt::Debug, new_width: &dyn fmt::Debug, new_height: &dyn fmt::Debug" +message = "old_width={:?} old_height={:?}, new_width={:?}, new_height={:?}" +enabled = true + +[[events]] +name = "vnc_dpy_pageflip" +args = "w: &dyn fmt::Debug, h: &dyn fmt::Debug, fmt: &dyn fmt::Debug" +message = "w={:?} h={:?} fmt={:?}" +enabled = true + +[[events]] +name = "vnc_dpy_refresh" +args = "dirty_num: &dyn fmt::Debug, update_interval: &dyn fmt::Debug" +message = "dirty_num={:?} update_interval={:?} ms" +enabled = true + +[[events]] +name = "vnc_dpy_image_update" +args = "x: &dyn fmt::Debug, y: &dyn fmt::Debug, w: &dyn fmt::Debug, h: &dyn fmt::Debug" +message = "x={:?} y={:?} x={:?} y={:?}" +enabled = true + +[[events]] +name = "vnc_dpy_cursor_update" +args = "width: &dyn fmt::Debug, height: &dyn fmt::Debug" +message = "width={:?} height={:?}" +enabled = true + +[[events]] +name = "vnc_server_desktop_resize" +args = "width: &dyn fmt::Debug, height: &dyn fmt::Debug" +message = "width={:?} height={:?}" +enabled = true + +[[events]] +name = "vnc_client_connect" +args = "stream: &dyn fmt::Debug" +message = "stream={:?}" +enabled = true + +[[events]] +name = "vnc_client_handle_version" +args = "major: &usize, minor: &usize" +message = "major={:?} minor={:?}" +enabled = true + +[[events]] +name = "vnc_client_handle_init" +args = "clients: &dyn fmt::Debug, conn_limits: &dyn fmt::Debug" +message = "total clients={:?}, limits={:?}" +enabled = true + +[[events]] +name = "vnc_client_handle_auth" +args = "auth_type: &u8" +message = "auth_type={:?}" +enabled = true + +[[events]] +name = "vnc_client_key_event" +args = "keysym: &dyn fmt::Debug, down: &bool" +message = "keysym={:?} down={:?}" +enabled = true + +[[events]] +name = "vnc_client_point_event" +args = "button: &dyn fmt::Debug, x: &u16, y: &u16" +message = "button={:?} x={:?} y={:?}" +enabled = true + +[[events]] +name = "vnc_client_vencrypt_init" +args = "" +message = "" +enabled = true + +[[events]] +name = "vnc_client_vencrypt_auth" +args = "auth: &dyn fmt::Debug, subauth: &dyn fmt::Debug" +message = "auth={:?}, subauth={:?}" +enabled = true + +[[events]] +name = "vnc_client_tls_handshake_done" +args = "" +message = "" +enabled = true + +[[events]] +name = "vnc_client_get_mechname_length" +args = "len: &u32" +message = "length={:?}" +enabled = true + +[[events]] +name = "vnc_client_get_mechname" +args = "mechname: &dyn fmt::Debug" +message = "mechname={:?}" +enabled = true + +[[events]] +name = "vnc_client_get_authmessage_length" +args = "length: &dyn fmt::Debug" +message = "length={:?}" +enabled = true + +[[events]] +name = "vnc_client_sasl_auth" +args = "result: &dyn fmt::Debug, serverout_len: &dyn fmt::Debug" +message = "result={:?}, serverout_len={:?}" +enabled = true + +[[events]] +name = "vnc_server_send_mech_list" +args = "mech_list: &dyn fmt::Debug" +message = "mech_list={:?}" +enabled = true diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 2874b9525..d55fb71a2 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -24,6 +24,7 @@ rustls-pemfile = { version = "1.0.2", optional = true } sasl2-sys = { version = "0.1.20", optional = true } machine_manager = { path = "../machine_manager" } util = { path = "../util" } +trace = { path = "../trace" } [features] keycode = [] diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 0e322403c..360183c8c 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -49,7 +49,7 @@ pub enum AuthState { } /// Authentication and encryption method. -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub enum SubAuthState { /// Send plain Message + no auth. VncAuthVencryptPlain = 256, @@ -118,6 +118,7 @@ impl ClientIoHandler { pub fn get_mechname_length(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); let len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]); + trace::vnc_client_get_mechname_length(&len); if !(MECHNAME_MIN_LEN..MECHNAME_MAX_LEN).contains(&len) { return Err(anyhow!(VncError::AuthFailed( "get_mechname_length".to_string(), @@ -147,6 +148,7 @@ impl ClientIoHandler { pub fn get_sasl_mechname(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); let mech_name = String::from_utf8_lossy(&buf).to_string(); + trace::vnc_client_get_mechname(&mech_name); let mut security = self.server.security_type.borrow_mut(); let mech_list: Vec<&str> = security.saslconfig.mech_list.split(',').collect(); @@ -174,6 +176,7 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); let buf = [buf[0], buf[1], buf[2], buf[3]]; let len = u32::from_be_bytes(buf); + trace::vnc_client_get_authmessage_length(&len); if len > SASL_DATA_MAX_LEN { return Err(anyhow!(VncError::AuthFailed( @@ -191,7 +194,6 @@ impl ClientIoHandler { /// Receive the authentication information from client and return the result. fn client_sasl_auth(&mut self) -> Result<()> { - info!("Sasl Authentication"); let buf = self.read_incoming_msg(); let mut client_data = buf.to_vec(); @@ -235,6 +237,8 @@ impl ClientIoHandler { }, }; + trace::vnc_client_sasl_auth(&err, &serverout_len); + if err != SASL_OK && err != SASL_CONTINUE { // SAFETY: sasl_dispose() is C function. All parameters passed of the // function have been checked. @@ -439,6 +443,7 @@ impl ClientIoHandler { } // SAFETY: It can be ensure that the pointer of mechlist is not null. let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) }; + trace::vnc_server_send_mech_list(&mech_list); security.saslconfig.mech_list = String::from(mech_list.to_str()?); let mut buf = Vec::new(); let len = security.saslconfig.mech_list.len(); diff --git a/ui/src/vnc/auth_vencrypt.rs b/ui/src/vnc/auth_vencrypt.rs index e907f137a..fc5164f5d 100644 --- a/ui/src/vnc/auth_vencrypt.rs +++ b/ui/src/vnc/auth_vencrypt.rs @@ -90,6 +90,8 @@ pub struct TlsCreds { impl ClientIoHandler { /// Exchange auth version with client pub fn client_vencrypt_init(&mut self) -> Result<()> { + trace::vnc_client_vencrypt_init(); + let buf = self.read_incoming_msg(); let client = self.client.clone(); let subauth = self.server.security_type.borrow().subauth; @@ -124,6 +126,7 @@ impl ClientIoHandler { let auth = u32::from_be_bytes(buf); let client = self.client.clone(); let subauth = self.server.security_type.borrow().subauth; + trace::vnc_client_vencrypt_auth(&auth, &subauth); if auth != subauth as u32 { let mut buf = Vec::new(); @@ -206,6 +209,8 @@ impl ClientIoHandler { } fn tls_handshake_done(&mut self) -> Result<()> { + trace::vnc_client_tls_handshake_done(); + let handler = self.handlers.get("vnc_client_io").unwrap().clone(); let handlers = vec![handler]; EventLoop::update_event( diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 84e92e121..d617349f7 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -521,6 +521,7 @@ impl ClientIoHandler { } }; + trace::vnc_client_handle_version(&ver.0, &ver.1); let mut version = VncVersion::new(ver.0 as u16, ver.1 as u16); if version.major != 3 || ![3, 4, 5, 7, 8].contains(&version.minor) { let mut buf = Vec::new(); @@ -573,6 +574,7 @@ impl ClientIoHandler { let addr = client.addr.clone(); let mut locked_clients = server.client_handlers.lock().unwrap(); let mut len = locked_clients.len() as i32; + trace::vnc_client_handle_init(&len, &server.conn_limits); for client in locked_clients.values_mut() { if len <= server.conn_limits as i32 { break; @@ -612,6 +614,7 @@ impl ClientIoHandler { /// Authentication fn handle_auth(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); + trace::vnc_client_handle_auth(&buf[0]); let auth = self.server.security_type.borrow().auth; let client = self.client.clone(); let version = client.conn_state.lock().unwrap().version.clone(); @@ -925,6 +928,8 @@ impl ClientIoHandler { let down: bool = buf[1] != 0; let org_keysym = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]); let mut keysym = org_keysym; + + trace::vnc_client_key_event(&keysym, &down); let server = self.server.clone(); // Uppercase -> Lowercase. @@ -966,6 +971,7 @@ impl ClientIoHandler { let buf = self.read_incoming_msg(); let mut x = ((buf[2] as u16) << 8) + buf[3] as u16; let mut y = ((buf[4] as u16) << 8) + buf[5] as u16; + trace::vnc_client_point_event(&buf[1], &x, &y); // Window size alignment. let locked_surface = self.server.vnc_surface.lock().unwrap(); @@ -1274,6 +1280,8 @@ pub fn desktop_resize( let locked_surface = server.vnc_surface.lock().unwrap(); let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); + trace::vnc_server_desktop_resize(&width, &height); + if !(0..=MAX_IMAGE_SIZE).contains(&width) || !(0..=MAX_IMAGE_SIZE).contains(&height) { return Err(anyhow!(VncError::InvalidImageSize(width, height))); } diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 2cc2a2a40..3916d8b54 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -107,7 +107,14 @@ impl DisplayChangeListenerOperations for VncInterface { let guest_width: i32 = get_image_width(locked_vnc_surface.guest_image); let guest_height: i32 = get_image_height(locked_vnc_surface.guest_image); + trace::vnc_dpy_switch( + &guest_width, + &guest_height, + &surface.width(), + &surface.height(), + ); if !need_resize { + trace::vnc_dpy_pageflip(&guest_width, &guest_height, &surface.format); set_area_dirty( &mut locked_vnc_surface.guest_dirty_bitmap, 0, @@ -120,6 +127,7 @@ impl DisplayChangeListenerOperations for VncInterface { return Ok(()); } drop(locked_vnc_surface); + update_server_surface(&server)?; let mut locked_handlers = server.client_handlers.lock().unwrap(); @@ -177,6 +185,7 @@ impl DisplayChangeListenerOperations for VncInterface { } } dcl.lock().unwrap().update_interval = update_interval; + trace::vnc_dpy_refresh(&dirty_num, &update_interval); let mut locked_handlers = server.client_handlers.lock().unwrap(); for client in locked_handlers.values_mut() { @@ -189,6 +198,8 @@ impl DisplayChangeListenerOperations for VncInterface { if VNC_SERVERS.lock().unwrap().is_empty() { return Ok(()); } + + trace::vnc_dpy_image_update(&x, &y, &w, &h); let server = VNC_SERVERS.lock().unwrap()[0].clone(); let mut locked_vnc_surface = server.vnc_surface.lock().unwrap(); let g_w = get_image_width(locked_vnc_surface.guest_image); @@ -213,6 +224,7 @@ impl DisplayChangeListenerOperations for VncInterface { let server = VNC_SERVERS.lock().unwrap()[0].clone(); let width = cursor.width as u64; let height = cursor.height as u64; + trace::vnc_dpy_cursor_update(&width, &height); let bpl = round_up_div(width, BIT_PER_BYTE as u64); // Set the bit for mask. let bit_mask: u8 = 0x80; diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 2e91e83bd..23c753fa0 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -22,7 +22,7 @@ use std::{ }; use anyhow::{anyhow, Result}; -use log::{error, info}; +use log::error; use vmm_sys_util::epoll::EventSet; use crate::{ @@ -487,7 +487,8 @@ pub fn handle_connection( stream: TcpStream, addr: SocketAddr, ) -> Result<()> { - info!("New Connection: {:?}", stream); + trace::vnc_client_connect(&stream); + stream .set_nonblocking(true) .expect("set nonblocking failed"); -- Gitee From b33749c68cd517e0baa46e422c739c15eff11793 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 17:09:15 +0800 Subject: [PATCH 1533/2187] Trace: add trace for GTK Add trace for GTK Signed-off-by: Xiao Ye --- trace/trace_event/ui.toml | 84 +++++++++++++++++++++++++++++++++++++++ ui/src/gtk/draw.rs | 9 +++++ ui/src/gtk/mod.rs | 16 ++++++++ 3 files changed, 109 insertions(+) diff --git a/trace/trace_event/ui.toml b/trace/trace_event/ui.toml index 39c84379a..064ba439f 100644 --- a/trace/trace_event/ui.toml +++ b/trace/trace_event/ui.toml @@ -117,3 +117,87 @@ name = "vnc_server_send_mech_list" args = "mech_list: &dyn fmt::Debug" message = "mech_list={:?}" enabled = true + +[[events]] +name = "gtk_enter_callback" +args = "enter_or_leave: &dyn fmt::Debug" +message = "{:?}" +enabled = true + +[[events]] +name = "gtk_dyp_channel_switch" +args = "dev_name: &dyn fmt::Debug" +message = "device_name={:?}" +enabled = true + +[[events]] +name = "gtk_dyp_channel_refresh" +args = "dev_name: &dyn fmt::Debug" +message = "device_name={:?}" +enabled = true + +[[events]] +name = "gtk_dyp_channel_image_update" +args = "dev_name: &dyn fmt::Debug, x: &i32, y: &i32, w: &i32, h: &i32" +message = "device_name={:?} x={:?} y={:?} w={:?} h={:?}" +enabled = true + +[[events]] +name = "gtk_dyp_channel_cursor_update" +args = "dev_name: &dyn fmt::Debug" +message = "device_name={:?}" +enabled = true + +[[events]] +name = "gtk_dyp_switch" +args = "old_width: &dyn fmt::Debug, old_height: &dyn fmt::Debug, new_width: &dyn fmt::Debug, new_height: &dyn fmt::Debug" +message = "old_width={:?} old_height={:?} new_width={:?} new_height={:?}" +enabled = true + +[[events]] +name = "gtk_dyp_update" +args = "x: &dyn fmt::Debug, y: &dyn fmt::Debug, w: &dyn fmt::Debug, h: &dyn fmt::Debug" +message = "x={:?} y={:?} w={:?} h={:?}" +enabled = true + +[[events]] +name = "gtk_dyp_refresh" +args = "" +message = "" +enabled = true + +[[events]] +name = "gtk_dyp_cursor_define" +args = "width: &dyn fmt::Debug, height: &dyn fmt::Debug, hot_x: &dyn fmt::Debug, hot_y: &dyn fmt::Debug, data_len: &dyn fmt::Debug" +message = "width={:?} height={:?} hot_x={:?} hot_y={:?} data_len={:?}" +enabled = true + +[[events]] +name = "gtk_configure_callback" +args = "width: &dyn fmt::Debug, height: &dyn fmt::Debug" +message = "width={:?} height={:?}" +enabled = true + +[[events]] +name = "gtk_key_event_callback" +args = "key_value: &dyn fmt::Debug, press: &dyn fmt::Debug" +message = "key_value={:?} press={:?}" +enabled = true + +[[events]] +name = "gtk_pointer_callback" +args = "button_mask: &dyn fmt::Debug" +message = "button_mask={:?}" +enabled = true + +[[events]] +name = "gtk_cursor_move_event" +args = "x: &dyn fmt::Debug, y: &dyn fmt::Debug" +message = "x={:?} y={:?}" +enabled = true + +[[events]] +name = "gtk_scroll_callback" +args = "direction: &dyn fmt::Debug" +message = "direction={:?}" +enabled = true diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index e888f88bc..72027d944 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -127,6 +127,7 @@ fn da_enter_callback( gs: &Rc>, _event: &gdk::EventCrossing, ) -> Result<()> { + trace::gtk_enter_callback(&"enter".to_string()); update_keyboard_grab(gs, true); Ok(()) } @@ -135,6 +136,7 @@ fn da_leave_callback( gs: &Rc>, _event: &gdk::EventCrossing, ) -> Result<()> { + trace::gtk_enter_callback(&"leave".to_string()); update_keyboard_grab(gs, false); Ok(()) } @@ -159,6 +161,8 @@ fn da_configure_callback( gs: &Rc>, event_configure: &gdk::EventConfigure, ) -> Result<()> { + trace::gtk_configure_callback(&event_configure.size().0, &event_configure.size().1); + let borrowed_gs = gs.borrow(); if !borrowed_gs.scale_mode.borrow().is_free_scale() { return Ok(()); @@ -190,6 +194,7 @@ fn da_key_callback( Some(k) => *k, None => 0, }; + trace::gtk_key_event_callback(&key_value, &press); update_key_state(press, org_key_value, keycode)?; input::key_event(keycode, press)?; Ok(()) @@ -215,6 +220,7 @@ fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) Some(value) => value, None => return Ok(()), }; + trace::gtk_cursor_move_event(&x, &y); let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; @@ -231,6 +237,7 @@ fn da_pointer_callback(button_event: &gdk::EventButton) -> Result<()> { 3 => INPUT_POINT_MIDDLE, _ => return Ok(()), }; + trace::gtk_pointer_callback(&button_mask); match button_event.event_type() { gdk::EventType::ButtonRelease => { @@ -250,6 +257,8 @@ fn da_pointer_callback(button_event: &gdk::EventButton) -> Result<()> { } fn da_scroll_callback(scroll_event: &gdk::EventScroll) -> Result<()> { + trace::gtk_scroll_callback(&scroll_event.direction()); + match scroll_event.direction() { ScrollDirection::Up => press_mouse(INPUT_BUTTON_WHEEL_UP), ScrollDirection::Down => press_mouse(INPUT_BUTTON_WHEEL_DOWN), diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index cc2467009..6354b862b 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -155,6 +155,8 @@ impl GtkInterface { impl DisplayChangeListenerOperations for GtkInterface { fn dpy_switch(&self, _surface: &crate::console::DisplaySurface) -> Result<()> { + trace::gtk_dyp_channel_switch(&self.dev_name.clone()); + let event = DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplaySwitch); self.dce_sender.send(event)?; Ok(()) @@ -164,6 +166,8 @@ impl DisplayChangeListenerOperations for GtkInterface { &self, dcl: &std::sync::Arc>, ) -> Result<()> { + trace::gtk_dyp_channel_refresh(&self.dev_name.clone()); + // The way virtio-gpu devices are used in phase OS and others is different. if self.dev_name.starts_with("virtio-gpu") { if get_run_stage() == VmRunningStage::Os { @@ -182,6 +186,8 @@ impl DisplayChangeListenerOperations for GtkInterface { } fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) -> Result<()> { + trace::gtk_dyp_channel_image_update(&self.dev_name.clone(), &x, &y, &w, &h); + let mut event = DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::DisplayUpdate); event.x = x; @@ -193,6 +199,8 @@ impl DisplayChangeListenerOperations for GtkInterface { } fn dpy_cursor_update(&self, cursor_data: &DisplayMouse) -> Result<()> { + trace::gtk_dyp_channel_cursor_update(&self.dev_name.clone()); + let mut event = DisplayChangeEvent::new(self.dev_name.clone(), DisplayEventType::CursorDefine); event.cursor = Some(cursor_data.clone()); @@ -676,6 +684,8 @@ fn gs_show_menu_callback( /// So, the refresh operation will always check if the image has been switched, if /// the result is yes, then use the switch operation to switch the latest image. fn do_refresh_event(gs: &Rc>) -> Result<()> { + trace::gtk_dyp_refresh(); + let borrowed_gs = gs.borrow(); let active_con = borrowed_gs.con.upgrade(); let con = match active_con { @@ -707,6 +717,8 @@ fn do_cursor_define(gs: &Rc>, event: DisplayChangeEven None => bail!("Invalid Cursor image"), }; + trace::gtk_dyp_cursor_define(&c.width, &c.height, &c.hot_x, &c.hot_y, &c.data.len()); + if c.data.len() < ((c.width * c.height) as usize) * 4 { bail!("Invalid Cursor image"); } @@ -735,6 +747,8 @@ fn do_cursor_define(gs: &Rc>, event: DisplayChangeEven // Update dirty area of image. fn do_update_event(gs: &Rc>, event: DisplayChangeEvent) -> Result<()> { + trace::gtk_dyp_update(&event.x, &event.y, &event.w, &event.h); + let borrowed_gs = gs.borrow(); let active_con = borrowed_gs.con.upgrade(); let con = match active_con { @@ -853,6 +867,8 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { let surface_width = surface.width(); let surface_height = surface.height(); let surface_stride = surface.stride(); + trace::gtk_dyp_switch(&width, &height, &surface_width, &surface_height); + if width != 0 && height != 0 && width == surface_width && height == surface_height { need_resize = false; } -- Gitee From 404a02668e76661765f3258f78bd49bd791e446c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 17:09:37 +0800 Subject: [PATCH 1534/2187] Trace: add trace for Console Add trace for Console Signed-off-by: Xiao Ye --- trace/trace_event/ui.toml | 18 ++++++++++++++++++ ui/src/console.rs | 10 ++++++++++ 2 files changed, 28 insertions(+) diff --git a/trace/trace_event/ui.toml b/trace/trace_event/ui.toml index 064ba439f..091c05648 100644 --- a/trace/trace_event/ui.toml +++ b/trace/trace_event/ui.toml @@ -201,3 +201,21 @@ name = "gtk_scroll_callback" args = "direction: &dyn fmt::Debug" message = "direction={:?}" enabled = true + +[[events]] +name = "console_dpy_refresh" +args = "interval: &dyn fmt::Debug" +message = "interval={:?} ms" +enabled = true + +[[events]] +name = "console_dpy_ui_info" +args = "dev_name: &dyn fmt::Debug, width: &dyn fmt::Debug, height: &dyn fmt::Debug, last_width: &dyn fmt::Debug, last_height: &dyn fmt::Debug" +message = "dev_name={:?} width={:?} height={:?} last_width={:?} last_height={:?}" +enabled = true + +[[events]] +name = "console_select" +args = "con_id: &dyn fmt::Debug" +message = "console id={:?}" +enabled = true diff --git a/ui/src/console.rs b/ui/src/console.rs index 74635e4e2..380d1b7e9 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -368,6 +368,7 @@ fn display_refresh() { interval = dcl_interval } } + trace::console_dpy_refresh(&interval); let mut locked_state = DISPLAY_STATE.lock().unwrap(); locked_state.interval = interval; @@ -530,6 +531,13 @@ pub fn graphic_hardware_ui_info( height: u32, ) -> Result<()> { let mut locked_con = con.lock().unwrap(); + trace::console_dpy_ui_info( + &locked_con.dev_name, + &width, + &height, + &locked_con.ui_info.last_width, + &locked_con.ui_info.last_height, + ); if locked_con.ui_info.last_width == width && locked_con.ui_info.last_height == height { return Ok(()); } @@ -719,6 +727,8 @@ pub fn console_close(console: &Option>>) -> Result<() /// Select the default display device. /// If con_id is none, then do nothing. pub fn console_select(con_id: Option) -> Result<()> { + trace::console_select(&con_id); + let mut locked_consoles = CONSOLES.lock().unwrap(); if locked_consoles.activate_id == con_id { return Ok(()); -- Gitee From 9601d4e6d87115d8b24ae9f3be8e574bde2bcce4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 23 Dec 2023 17:30:29 +0800 Subject: [PATCH 1535/2187] Trace: add trace for hid keyboard and tablet 1. Add trace for hid keyboard and tablet. 2. Add trace for xhci controller. Signed-off-by: Xiao Ye --- devices/src/usb/hid.rs | 12 +- devices/src/usb/keyboard.rs | 4 +- devices/src/usb/tablet.rs | 7 +- devices/src/usb/xhci/xhci_controller.rs | 67 ++++-- devices/src/usb/xhci/xhci_pci.rs | 13 +- devices/src/usb/xhci/xhci_regs.rs | 20 +- devices/src/usb/xhci/xhci_ring.rs | 1 + trace/trace_event/usb.toml | 282 ++++++++++++++++++++++++ 8 files changed, 359 insertions(+), 47 deletions(-) diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 3f05f8970..7f964b881 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -12,7 +12,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use log::{debug, error}; +use log::error; use super::config::*; use super::{UsbDeviceRequest, UsbPacket, UsbPacketStatus}; @@ -267,10 +267,8 @@ impl Hid { let index = key | ((self.keyboard.modifiers as u32 & (1 << 8)) >> 1); let hid_code = HID_CODE[index as usize]; self.keyboard.modifiers &= !(1 << 8); - debug!( - "convert_to_hid_code hid_code {} index {} key {}", - hid_code, index, key - ); + trace::usb_convert_to_hid_code(&hid_code, &index, &key); + if hid_code == 0x0 { return; } @@ -469,7 +467,7 @@ impl Hid { match device_req.request { HID_SET_REPORT => match self.kind { HidType::Keyboard => { - debug!("Keyboard set report {}", data[0]); + trace::usb_keyboard_set_report(&data[0]); set_kbd_led_state(data[0]); } _ => { @@ -507,7 +505,7 @@ impl Hid { let mut buf = Vec::new(); if p.ep_number == 1 { if self.num == 0 { - debug!("No data in usb device."); + trace::usb_no_data_in_usb_device(); p.status = UsbPacketStatus::Nak; return; } diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 8f8fee8f3..2712436d2 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -132,6 +132,8 @@ pub struct UsbKeyboardAdapter { impl KeyboardOpts for UsbKeyboardAdapter { fn do_key_event(&mut self, keycode: u16, down: bool) -> Result<()> { + trace::usb_keyboard_event(&keycode, &down); + let mut scan_codes = Vec::new(); let mut keycode = keycode; if keycode & SCANCODE_GREY != 0 { @@ -146,7 +148,7 @@ impl KeyboardOpts for UsbKeyboardAdapter { let mut locked_kbd = self.usb_kbd.lock().unwrap(); if scan_codes.len() as u32 + locked_kbd.hid.num > QUEUE_LENGTH { - debug!("Keyboard queue is full!"); + trace::usb_keyboard_queue_full(); // Return ok to ignore the request. return Ok(()); } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 67d29035d..ab7485fc4 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -135,9 +135,12 @@ pub struct UsbTabletAdapter { impl PointerOpts for UsbTabletAdapter { fn update_point_state(&mut self, input_event: InputEvent) -> Result<()> { + trace::usb_tablet_update_point_state(&input_event.input_type); + let mut locked_tablet = self.tablet.lock().unwrap(); if locked_tablet.hid.num >= QUEUE_LENGTH { - debug!("Pointer queue is full!"); + trace::usb_tablet_queue_full(); + // Return ok to ignore the request. return Ok(()); } @@ -186,11 +189,13 @@ impl PointerOpts for UsbTabletAdapter { } fn sync(&mut self) -> Result<()> { + trace::usb_tablet_point_sync(); let mut locked_tablet = self.tablet.lock().unwrap(); // The last evt is used to save the latest button state, // so the max number of events can be cached at one time is QUEUE_LENGTH - 1. if locked_tablet.hid.num == QUEUE_LENGTH - 1 { + trace::usb_tablet_queue_full(); return Ok(()); } let curr_index = ((locked_tablet.hid.head + locked_tablet.hid.num) & QUEUE_MASK) as usize; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 86370a70f..75a4ebb7a 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -19,7 +19,7 @@ use std::time::Duration; use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; @@ -169,11 +169,13 @@ impl XhciTransfer { self.status = usb_packet_status_to_trb_code(self.packet.lock().unwrap().status)?; if self.status == TRBCCode::Success { + trace::usb_xhci_xfer_success(&self.packet.lock().unwrap().actual_length); self.submit_transfer()?; self.ep_ring.refresh_dequeue_ptr()?; return Ok(()); } + trace::usb_xhci_xfer_error(&self.packet.lock().unwrap().status); self.report_transfer_error()?; if self.ep_type == EpType::IsoIn || self.ep_type == EpType::IsoOut { @@ -208,7 +210,10 @@ impl XhciTransfer { } TRBType::TrStatus => {} _ => { - debug!("Ignore the TRB, unhandled trb type {:?}", trb.get_type()); + trace::usb_xhci_unimplemented(&format!( + "Ignore the TRB, unhandled trb type {:?}", + trb.get_type() + )); } } if (trb.control & TRB_TR_IOC == TRB_TR_IOC) @@ -762,6 +767,7 @@ impl XhciDevice { } pub fn run(&mut self) { + trace::usb_xhci_run(); self.oper.unset_usb_status_flag(USB_STS_HCH); self.mfindex_start = EventLoop::get_ctx(None).unwrap().get_virtual_clock(); } @@ -797,6 +803,7 @@ impl XhciDevice { } pub fn stop(&mut self) { + trace::usb_xhci_stop(); self.oper.set_usb_status_flag(USB_STS_HCH); self.oper.cmd_ring_ctrl &= !(CMD_RING_CTRL_CRR as u64); } @@ -811,7 +818,7 @@ impl XhciDevice { } pub fn reset(&mut self) { - info!("xhci reset"); + trace::usb_xhci_reset(); self.oper.reset(); for i in 0..self.slots.len() as u32 { if let Err(e) = self.disable_slot(i + 1) { @@ -837,6 +844,7 @@ impl XhciDevice { /// Reset xhci port. pub fn reset_port(&mut self, xhci_port: &Arc>, warm_reset: bool) -> Result<()> { let mut locked_port = xhci_port.lock().unwrap(); + trace::usb_xhci_port_reset(&locked_port.port_id, &warm_reset); let usb_dev = locked_port.dev.as_ref(); if usb_dev.is_none() { // No device, no need to reset. @@ -852,6 +860,7 @@ impl XhciDevice { match speed { USB_SPEED_LOW | USB_SPEED_FULL | USB_SPEED_HIGH | USB_SPEED_SUPER => { locked_port.set_port_link_state(PLS_U0); + trace::usb_xhci_port_link(&locked_port.port_id, &PLS_U0); locked_port.portsc |= PORTSC_PED; } _ => { @@ -870,6 +879,7 @@ impl XhciDevice { if locked_port.portsc & flag == flag { return Ok(()); } + trace::usb_xhci_port_notify(&locked_port.port_id, &flag); locked_port.portsc |= flag; if !self.running() { return Ok(()); @@ -906,10 +916,7 @@ impl XhciDevice { } } locked_port.set_port_link_state(pls); - debug!( - "xhci port update portsc {:x} pls {:x}", - locked_port.portsc, pls - ); + trace::usb_xhci_port_link(&locked_port.port_id, &pls); drop(locked_port); self.oper.set_usb_status_flag(USB_STS_PCD); self.port_notify(port, PORTSC_CSC)?; @@ -1035,7 +1042,7 @@ impl XhciDevice { self.intrs[0].lock().unwrap().send_event(&event)?; } None => { - debug!("No TRB in the cmd ring."); + trace::usb_xhci_unimplemented(&"No TRB in the cmd ring.".to_string()); break; } } @@ -1044,11 +1051,13 @@ impl XhciDevice { } fn enable_slot(&mut self, slot_id: u32) -> TRBCCode { + trace::usb_xhci_enable_slot(&slot_id); self.slots[(slot_id - 1) as usize].enabled = true; TRBCCode::Success } fn disable_slot(&mut self, slot_id: u32) -> Result { + trace::usb_xhci_disable_slot(&slot_id); for i in 1..=self.slots[(slot_id - 1) as usize].endpoints.len() as u32 { self.disable_endpoint(slot_id, i)?; } @@ -1105,6 +1114,7 @@ impl XhciDevice { error!("Failed to found usb port"); return Ok(TRBCCode::TrbError); }; + trace::usb_xhci_address_device(&slot_id, &usb_port.lock().unwrap().port_id); if usb_port.lock().unwrap().dev.is_none() { error!("No device found in usb port."); return Ok(TRBCCode::UsbTransactionError); @@ -1190,6 +1200,7 @@ impl XhciDevice { } fn configure_endpoint(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + trace::usb_xhci_configure_endpoint(&slot_id); let slot_state = self.slots[(slot_id - 1) as usize].get_slot_state_in_context(&self.mem_space)?; if trb.control & TRB_CR_DC == TRB_CR_DC { @@ -1274,6 +1285,7 @@ impl XhciDevice { } fn evaluate_context(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { + trace::usb_xhci_evaluate_context(&slot_id); if !self.slots[(slot_id - 1) as usize].slot_state_is_valid(&self.mem_space)? { error!("Invalid slot state, slot id {}", slot_id); return Ok(TRBCCode::ContextStateError); @@ -1348,6 +1360,7 @@ impl XhciDevice { } fn reset_device(&mut self, slot_id: u32) -> Result { + trace::usb_xhci_reset_device(&slot_id); let mut slot_ctx = XhciSlotCtx::default(); let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; dma_read_u32( @@ -1380,6 +1393,7 @@ impl XhciDevice { input_ctx: DmaAddr, output_ctx: DmaAddr, ) -> Result { + trace::usb_xhci_enable_endpoint(&slot_id, &ep_id); let entry_offset = (ep_id - 1) as u64 * EP_INPUT_CTX_ENTRY_SIZE; let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( @@ -1412,9 +1426,10 @@ impl XhciDevice { } fn disable_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { + trace::usb_xhci_disable_endpoint(&slot_id, &ep_id); let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if !epctx.enabled { - debug!("Endpoint already disabled"); + trace::usb_xhci_unimplemented(&"Endpoint already disabled".to_string()); return Ok(TRBCCode::Success); } self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Invalid)?; @@ -1427,6 +1442,7 @@ impl XhciDevice { } fn stop_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { + trace::usb_xhci_stop_endpoint(&slot_id, &ep_id); if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&ep_id) { error!("Invalid endpoint id"); return Ok(TRBCCode::TrbError); @@ -1450,13 +1466,17 @@ impl XhciDevice { return Ok(TRBCCode::ContextStateError); } if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Stopped)? > 0 { - debug!("endpoint stop when xfers running!"); + trace::usb_xhci_unimplemented(&format!( + "Endpoint stop when xfers running, slot_id {} epid {}", + slot_id, ep_id + )); } self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].set_state(EP_STOPPED)?; Ok(TRBCCode::Success) } fn reset_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result { + trace::usb_xhci_reset_endpoint(&slot_id, &ep_id); if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&ep_id) { error!("Invalid endpoint id {}", ep_id); return Ok(TRBCCode::TrbError); @@ -1500,6 +1520,7 @@ impl XhciDevice { epid: u32, trb: &XhciTRB, ) -> Result { + trace::usb_xhci_set_tr_dequeue(&slotid, &epid, &trb.parameter); if !(ENDPOINT_ID_START..=MAX_ENDPOINTS).contains(&epid) { error!("Invalid endpoint id {}", epid); return Ok(TRBCCode::TrbError); @@ -1543,12 +1564,7 @@ impl XhciDevice { return Ok(()); } - debug!( - "kick_endpoint slotid {} epid {} dequeue {:x}", - slot_id, - ep_id, - epctx.ring.get_dequeue_ptr(), - ); + trace::usb_xhci_ep_kick(&slot_id, &ep_id, &epctx.ring.get_dequeue_ptr()); if self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] .retry .is_some() @@ -1572,11 +1588,11 @@ impl XhciDevice { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; let td = match epctx.ring.fetch_td()? { Some(td) => { - debug!( + trace::usb_xhci_unimplemented(&format!( "fetch transfer trb {:?} ring dequeue {:?}", td, epctx.ring.get_dequeue_ptr(), - ); + )); td } None => { @@ -1593,7 +1609,7 @@ impl XhciDevice { error!("Failed to send event: {:?}", e); } } - debug!("No TD in the transfer ring."); + trace::usb_xhci_unimplemented(&"No TD in the transfer ring.".to_string()); break; } }; @@ -1693,7 +1709,7 @@ impl XhciDevice { if !locked_xfer.iso_xfer && locked_xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { - debug!("USB packet status is NAK"); + trace::usb_xhci_unimplemented(&"USB packet status is NAK".to_string()); // NAK need to retry again. return Ok(false); } @@ -1730,6 +1746,7 @@ impl XhciDevice { /// Control Transfer, TRBs include Setup, Data(option), Status. fn do_ctrl_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + trace::usb_xhci_xfer_start(&xfer.slotid, &xfer.epid); if let Err(e) = self.check_ctrl_transfer(xfer) { error!("Failed to check control transfer {:?}", e); xfer.status = TRBCCode::TrbError; @@ -1837,6 +1854,7 @@ impl XhciDevice { } fn do_data_transfer(&mut self, xfer: &mut XhciTransfer) -> Result<()> { + trace::usb_xhci_xfer_start(&xfer.slotid, &xfer.epid); let epctx = &self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; match epctx.ep_type { EpType::IntrOut | EpType::IntrIn => { @@ -1935,15 +1953,18 @@ impl XhciDevice { /// Update packet status and then submit transfer. fn complete_packet(&mut self, xfer: &mut XhciTransfer) -> Result<()> { if xfer.packet.lock().unwrap().is_async { + trace::usb_xhci_xfer_async(); xfer.running_retry = false; xfer.running_async = true; return Ok(()); } if xfer.packet.lock().unwrap().status == UsbPacketStatus::Nak { + trace::usb_xhci_xfer_nak(); xfer.complete = false; xfer.running_retry = true; return Ok(()); } else { + trace::usb_xhci_xfer_retry(); xfer.complete = true; xfer.running_retry = false; } @@ -1953,7 +1974,7 @@ impl XhciDevice { /// Flush transfer in endpoint in some case such as stop endpoint. fn flush_ep_transfer(&mut self, slotid: u32, epid: u32, report: TRBCCode) -> Result { - debug!("flush_ep_transfer slotid {} epid {}", slotid, epid); + trace::usb_xhci_flush_ep_transfer(&slotid, &epid); let mut cnt = 0; let mut report = report; while let Some(xfer) = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] @@ -2012,10 +2033,10 @@ impl XhciDevice { pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &UsbEndpoint) -> Result<()> { let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); if let Err(e) = self.get_endpoint_ctx(slot_id, ep_id as u32) { - debug!( + trace::usb_xhci_unimplemented(&format!( "Invalid slot id or ep id, maybe device not activated, {:?}", e - ); + )); return Ok(()); } self.kick_endpoint(slot_id, ep_id as u32)?; diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index fe3a5f380..2d6aa3fb8 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -18,7 +18,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; -use log::{debug, error}; +use log::error; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -177,12 +177,11 @@ impl XhciPciDevice { .assign_usb_port(dev) .with_context(|| "No available USB port.")?; locked_xhci.port_update(&usb_port, false)?; - let mut locked_dev = dev.lock().unwrap(); - debug!( - "Attach usb device: xhci port id {} device id {}", - usb_port.lock().unwrap().port_id, - locked_dev.device_id() + trace::usb_xhci_attach_device( + &usb_port.lock().unwrap().port_id, + &dev.lock().unwrap().device_id(), ); + let mut locked_dev = dev.lock().unwrap(); locked_dev.handle_attach()?; locked_dev.set_controller(Arc::downgrade(&self.xhci)); Ok(()) @@ -203,6 +202,7 @@ impl XhciPciDevice { let mut locked_port = usb_port.lock().unwrap(); let dev = locked_port.dev.as_ref().unwrap(); let mut locked_dev = dev.lock().unwrap(); + trace::usb_xhci_detach_device(&locked_port.port_id, &locked_dev.device_id()); locked_dev.usb_device_base_mut().unplugged = true; locked_dev.unrealize()?; drop(locked_dev); @@ -341,6 +341,7 @@ impl PciDevOps for XhciPciDevice { } fn unrealize(&mut self) -> Result<()> { + trace::usb_xhci_exit(); Ok(()) } diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 79751223f..102b87f1d 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -359,7 +359,6 @@ impl XhciInterrupter { pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { let xhci_dev = xhci_dev.clone(); let cap_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { - debug!("cap read {:x} {:x}", addr.0, offset); let locked_dev = xhci_dev.lock().unwrap(); let max_ports = locked_dev.numports_2 + locked_dev.numports_3; let max_intrs = locked_dev.intrs.len() as u32; @@ -406,6 +405,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { 0 } }; + trace::usb_xhci_cap_read(&addr.0, &offset, &value); write_data_u32(data, value) }; @@ -427,7 +427,6 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { let xhci = xhci_dev.clone(); let oper_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { - debug!("oper read {:x} {:x}", addr.0, offset); let locked_xhci = xhci.lock().unwrap(); let value = match offset { XHCI_OPER_REG_USBCMD => locked_xhci.oper.get_usb_cmd(), @@ -455,12 +454,12 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { 0 } }; + trace::usb_xhci_oper_read(&addr.0, &offset, &value); write_data_u32(data, value) }; let xhci = xhci_dev.clone(); let oper_write = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { - debug!("oper write {:x} {:x}", addr.0, offset); let mut value = 0; if !read_data_u32(data, &mut value) { return false; @@ -540,6 +539,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { return false; } }; + trace::usb_xhci_oper_write(&addr.0, &offset, &value); true }; @@ -553,7 +553,6 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { let xhci = xhci_dev.clone(); let runtime_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { - debug!("runtime read {:x} {:x}", addr.0, offset); let mut value = 0; if offset < 0x20 { if offset == 0x0 { @@ -586,6 +585,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } }; } + trace::usb_xhci_runtime_read(&addr.0, &offset, &value); write_data_u32(data, value) }; @@ -595,7 +595,6 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { if !read_data_u32(data, &mut value) { return false; } - debug!("runtime write {:x} {:x} {:x}", addr.0, offset, value); if offset < 0x20 { error!("Runtime write not implemented: offset {}", offset); return false; @@ -671,6 +670,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { ); } }; + trace::usb_xhci_runtime_write(&addr.0, &offset, &value); true }; @@ -683,7 +683,7 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { /// Build doorbell region ops. pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { let doorbell_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { - debug!("doorbell read addr {:x} offset {:x}", addr.0, offset); + trace::usb_xhci_doorbell_read(&addr.0, &offset, &0); write_data_u32(data, 0) }; let xhci = xhci_dev.clone(); @@ -692,7 +692,6 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { if !read_data_u32(data, &mut value) { return false; } - debug!("doorbell write {:x} {:x}", addr.0, offset); if !xhci.lock().unwrap().running() { error!("Failed to write doorbell, XHCI is not running"); return false; @@ -710,6 +709,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { return false; } } + trace::usb_xhci_doorbell_write(&addr.0, &offset, &value); true }; @@ -723,7 +723,6 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { let port = xhci_port.clone(); let port_read = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool { - debug!("port read {:x} {:x}", addr.0, offset); let locked_port = port.lock().unwrap(); let value = match offset { XHCI_PORTSC => locked_port.portsc, @@ -735,6 +734,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { return false; } }; + trace::usb_xhci_port_read(&addr.0, &offset, &value); write_data_u32(data, value) }; @@ -744,7 +744,6 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { if !read_data_u32(data, &mut value) { return false; } - debug!("port write {:x} {:x} {:x}", addr.0, offset, value); match offset { XHCI_PORTSC => { if let Err(e) = xhci_portsc_write(&port, value) { @@ -760,6 +759,7 @@ pub fn build_port_ops(xhci_port: &Arc>) -> RegionOps { return false; } } + trace::usb_xhci_port_write(&addr.0, &offset, &value); true }; @@ -812,12 +812,14 @@ fn xhci_portsc_ls_write(port: &mut UsbPort, old_pls: u32, new_pls: u32) -> u32 { PLS_U0 => { if old_pls != PLS_U0 { port.set_port_link_state(new_pls); + trace::usb_xhci_port_link(&port.port_id, &new_pls); return PORTSC_PLC; } } PLS_U3 => { if old_pls < PLS_U3 { port.set_port_link_state(new_pls); + trace::usb_xhci_port_link(&port.port_id, &new_pls); } } PLS_RESUME => {} diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 6b8ed77d0..f37135721 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -169,6 +169,7 @@ impl XhciTransferRing { let mut trb = read_trb(&self.mem, dequeue)?; trb.addr = dequeue; trb.ccs = ccs; + trace::usb_xhci_fetch_trb(&dequeue, &trb.parameter, &trb.status, &trb.control); let trb_type = trb.get_type(); if trb_type == TRBType::TrLink { link_cnt += 1; diff --git a/trace/trace_event/usb.toml b/trace/trace_event/usb.toml index 412385c4b..49a23cd81 100644 --- a/trace/trace_event/usb.toml +++ b/trace/trace_event/usb.toml @@ -1,3 +1,237 @@ +[[events]] +name = "usb_xhci_exit" +args = "" +message = "=== EXIT ===" +enabled = true + +[[events]] +name = "usb_xhci_run" +args = "" +message = "=== RUN ===" +enabled = true + +[[events]] +name = "usb_xhci_reset" +args = "" +message = "=== RESET ===" +enabled = true + +[[events]] +name = "usb_xhci_stop" +args = "" +message = "=== STOP ===" +enabled = true + +[[events]] +name = "usb_xhci_attach_device" +args = "port_id: &dyn fmt::Debug, device_id: &dyn fmt::Debug" +message = "port_id={:?} device_id={:?}" +enabled = true + +[[events]] +name = "usb_xhci_detach_device" +args = "port_id: &dyn fmt::Debug, device_id: &dyn fmt::Debug" +message = "port_id={:?} device_id={:?}" +enabled = true + +[[events]] +name = "usb_xhci_cap_read" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_oper_read" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_runtime_read" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_runtime_write" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_oper_write" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_doorbell_read" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_doorbell_write" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_port_read" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_port_write" +args = "gpa: &dyn fmt::Debug, offset: &dyn fmt::Debug, value: &dyn fmt::Debug" +message = "gpa={:x?} offset={:04x?} value={:?}" +enabled = true + +[[events]] +name = "usb_xhci_port_link" +args = "port: &dyn fmt::Debug, pls: &dyn fmt::Debug" +message = "port={:?} pls={:?}" +enabled = true + +[[events]] +name = "usb_xhci_ep_kick" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug, dequeue: &dyn fmt::Debug" +message = "slotid={:?} epid={:?} dequeue={:x?}" +enabled = true + +[[events]] +name = "usb_xhci_fetch_trb" +args = "addr: &dyn fmt::Debug, param: &dyn fmt::Debug, status: &dyn fmt::Debug, control: &dyn fmt::Debug" +message = "addr={:x?} param={:?} status={:?} control={:?}" +enabled = true + +[[events]] +name = "usb_xhci_port_reset" +args = "port: &dyn fmt::Debug, warm: &dyn fmt::Debug" +message = "port={:?} warm={:?}" +enabled = true + +[[events]] +name = "usb_xhci_port_notify" +args = "port: &dyn fmt::Debug, bits: &dyn fmt::Debug" +message = "port={:?} bits={:?}" +enabled = true + +[[events]] +name = "usb_xhci_enable_slot" +args = "slotid: &dyn fmt::Debug" +message = "slotid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_disable_slot" +args = "slotid: &dyn fmt::Debug" +message = "slotid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_address_device" +args = "slotid: &dyn fmt::Debug, port: &dyn fmt::Debug" +message = "slotid={:?} port={:?}" +enabled = true + +[[events]] +name = "usb_xhci_configure_endpoint" +args = "slotid: &dyn fmt::Debug" +message = "slotid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_evaluate_context" +args = "slotid: &dyn fmt::Debug" +message = "slotid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_reset_device" +args = "slotid: &dyn fmt::Debug" +message = "slotid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_enable_endpoint" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" +message = "slotid={:?} epid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_disable_endpoint" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" +message = "slotid={:?} epid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_set_tr_dequeue" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug, param: &dyn fmt::Debug" +message = "slotid={:?} epid={:?} param={:?}" +enabled = true + +[[events]] +name = "usb_xhci_stop_endpoint" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" +message = "slotid={:?} epid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_reset_endpoint" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" +message = "slotid={:?} epid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_xfer_start" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" +message = "slotid={:?} epid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_xfer_async" +args = "" +message = "" +enabled = true + +[[events]] +name = "usb_xhci_xfer_nak" +args = "" +message = "" +enabled = true + +[[events]] +name = "usb_xhci_xfer_retry" +args = "" +message = "" +enabled = true + +[[events]] +name = "usb_xhci_xfer_success" +args = "len: &dyn fmt::Debug" +message = "len={:?}" +enabled = true + +[[events]] +name = "usb_xhci_xfer_error" +args = "status: &dyn fmt::Debug" +message = "status={:?}" +enabled = true + +[[events]] +name = "usb_xhci_flush_ep_transfer" +args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" +message = "slotid={:?} epid={:?}" +enabled = true + +[[events]] +name = "usb_xhci_unimplemented" +args = "str: &dyn fmt::Debug" +message = "{:?}" +enabled = true + [[events]] name = "usb_handle_control" args = "device: &str, req: &dyn fmt::Debug" @@ -57,3 +291,51 @@ name = "usb_storage_handle_scsi_request" args = "csw: &dyn fmt::Debug" message = "csw {:?}" enabled = true + +[[events]] +name = "usb_tablet_update_point_state" +args = "input_type: &dyn fmt::Debug" +message = "input_type={:?}" +enabled = true + +[[events]] +name = "usb_tablet_point_sync" +args = "" +message = "input pointer sync" +enabled = true + +[[events]] +name = "usb_tablet_queue_full" +args = "" +message = "pointer queue is full" +enabled = true + +[[events]] +name = "usb_keyboard_event" +args = "keycode: &dyn fmt::Debug, down: &dyn fmt::Debug" +message = "do keyboard event keycode={:?} down={:?}" +enabled = true + +[[events]] +name = "usb_keyboard_queue_full" +args = "" +message = "keyboard queue is full" +enabled = true + +[[events]] +name = "usb_convert_to_hid_code" +args = "hid_code: &dyn fmt::Debug, index: &dyn fmt::Debug, key: &dyn fmt::Debug" +message = "hid_code {:?} index {:?} key {:?}" +enabled = true + +[[events]] +name = "usb_no_data_in_usb_device" +args = "" +message = "no data in usb device." +enabled = true + +[[events]] +name = "usb_keyboard_set_report" +args = "led_state: &dyn fmt::Debug" +message = "led_state={:?}" +enabled = true -- Gitee From 58ce22fccd5138272470a18bbc271334b425857d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 7 Jan 2024 00:20:42 +0800 Subject: [PATCH 1536/2187] trace: add trace for virtio-scsi Add trace for virtio-scsi. Signed-off-by: liuxiangdong --- Cargo.lock | 1 + devices/Cargo.toml | 1 + devices/src/scsi/bus.rs | 32 +++++++----------------- trace/trace_event/scsi.toml | 35 +++++++++++++++++++++++++++ trace/trace_event/virtio.toml | 18 +++++++++++--- virtio/src/device/block.rs | 2 +- virtio/src/device/net.rs | 4 +-- virtio/src/device/rng.rs | 2 +- virtio/src/device/scsi_cntlr.rs | 43 ++++++++++++++++++++++++++++----- 9 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 trace/trace_event/scsi.toml diff --git a/Cargo.lock b/Cargo.lock index 1830d3c48..1d16e80e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,6 +354,7 @@ dependencies = [ "serde", "serde_json", "thiserror", + "trace", "ui", "util", "v4l2-sys-mit", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index eb5eb3d62..7355a6d07 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -38,6 +38,7 @@ alsa = { version = "0.7.0", optional = true } rusb = { version = "0.9", optional = true } libusb1-sys = { version = "0.6.4", optional = true } cairo-rs = { version = "0.17.10", optional = true } +trace = { path = "../trace" } [features] default = [] diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index cc091455b..954674350 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -17,7 +17,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; -use log::{debug, info}; +use log::info; use crate::ScsiDisk::{ ScsiDevice, DEFAULT_SECTOR_SIZE, SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, @@ -399,17 +399,14 @@ impl ScsiBus { for (id, device) in self.devices.iter() { let (target_id, lun_id) = id; if *target_id == target { - debug!( - "Target request, target {}, requested lun {}, found lun {}", - target_id, lun, lun_id - ); + trace::scsi_bus_get_device(*target_id, lun, *lun_id); return Some((*device).clone()); } } // No lun found in requested target. It seems there is no such target requested in // CDB's LUNS bytes. - debug!("Can't find scsi device target {} lun {}", target, lun); + trace::scsi_bus_get_no_device(target, lun); None } } @@ -665,7 +662,7 @@ impl ScsiRequest { } pub fn emulate_execute(mut self) -> Result>> { - debug!("emulate scsi command is {:#x}", self.cmd.op); + trace::scsi_emulate_execute(self.cmd.op); let mut not_supported_flag = false; let mut sense = None; let mut status = GOOD; @@ -686,14 +683,11 @@ impl ScsiRequest { } Err(ref e) => { if not_supported_flag { - debug!("emulation scsi command {:#x} is not supported", self.cmd.op); + trace::scsi_emulate_execute_error(self.cmd.op, &"not supported"); status = CHECK_CONDITION; sense = Some(SCSI_SENSE_INVALID_OPCODE); } else { - debug!( - "Error in processing scsi command {:#x}, err is {:?}", - self.cmd.op, e - ); + trace::scsi_emulate_execute_error(self.cmd.op, e); status = CHECK_CONDITION; sense = Some(SCSI_SENSE_INVALID_FIELD); } @@ -728,14 +722,7 @@ fn outbuf_to_iov(command: u8, outbuf: &[u8], iovec: &[Iovec]) -> Result<()> { return Ok(()); } - debug!( - "cmd is {:x}, outbuf len is {}, iov_len is {}, idx is {}, iovec size is {}", - command, - outbuf.len(), - iov.iov_len, - idx, - iovec.len() - ); + trace::scsi_outbuf_to_iov(command, outbuf.len(), iov.iov_len, idx, iovec.len()); start += write_buf_mem(&outbuf[start..], iov.iov_len, iov.iov_base) .with_context(|| "Failed to write buf for scsi command result iov")?; @@ -1173,13 +1160,12 @@ fn scsi_command_emulate_mode_sense( let block_size = dev_lock.block_size; nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; - debug!( - "MODE SENSE page_code {:x}, page_control {:x}, subpage {:x}, dbd bit {:x}, Allocation length {}", + trace::scsi_emulate_mode_sense( page_code, page_control, cmd.buf[3], cmd.buf[1] & 0x8, - cmd.buf[4] + cmd.buf[4], ); // Device specific paramteter field for direct access block devices: diff --git a/trace/trace_event/scsi.toml b/trace/trace_event/scsi.toml new file mode 100644 index 000000000..d5c9605a9 --- /dev/null +++ b/trace/trace_event/scsi.toml @@ -0,0 +1,35 @@ +[[events]] +name = "scsi_bus_get_device" +args = "target_id: u8, lun: u16, lun_id: u16" +message = "target request, target {}, requested lun {}, found lun {}." +enabled = true + +[[events]] +name = "scsi_bus_get_no_device" +args = "target: u8, lun: u16" +message = "can't find scsi device target {} lun {}." +enabled = true + +[[events]] +name = "scsi_emulate_execute" +args = "op: u8" +message = "emulate scsi command is {:#x}." +enabled = true + +[[events]] +name = "scsi_emulate_execute_error" +args = "op: u8, result: &dyn fmt::Debug" +message = "error in processing scsi command {:#x}, err is {:?}" +enabled = true + +[[events]] +name = "scsi_emulate_mode_sense" +args = "page_code: u8, page_control: u8, subpage: u8, dbd: u8, length: u8" +message = "MODE SENSE page_code {:x}, page_control {:x}, subpage {:x}, dbd bit {:x}, Allocation length {}." +enabled = true + +[[events]] +name = "scsi_outbuf_to_iov" +args = "cmd: u8, outbuf_len: usize, iov_len: u64, idx: usize, iovec_size: usize" +message = "cmd is {:x}, outbuf len is {}, iov_len is {}, idx is {}, iovec size is {}." +enabled = true diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index 0d00910d3..d6215ae11 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -5,7 +5,19 @@ message = "{}: Request received from guest {}, ready to start processing." enabled = true [[events]] -name = "virtio_send_interrupt" -args = "device: String" -message = "{}: StratoVirt processing complete, ready to send interrupt to guest." +name = "virtqueue_send_interrupt" +args = "device: &str, queue: u64" +message = "{}: virtqueue 0x{:X?} processing complete, ready to send interrupt to guest." +enabled = true + +[[events]] +name = "virtio_scsi_handle_cmd_req" +args = "target: u8, lun: u16, tag: u64, cmd: u8" +message = "target={}, lun={}, tag={}, cmd={}." +enabled = true + +[[events]] +name = "virtio_scsi_handle_cmd_resp" +args = "target: u8, lun: u16, tag: u64, status: u8, response: u8" +message = "target={}, lun={}, tag={}, status={}, response={}." enabled = true diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 4fe0ddb00..d7669d645 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -180,7 +180,7 @@ impl AioCompleteCb { .with_context(|| { VirtioError::InterruptTrigger("blk io completion", VirtioInterruptType::Vring) })?; - trace::virtio_send_interrupt("Block".to_string()); + trace::virtqueue_send_interrupt("Block", &*queue_lock as *const _ as u64); } Ok(()) } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 6bf9e6339..17a81b313 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -853,7 +853,7 @@ impl NetIoHandler { .with_context(|| { VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) })?; - trace::virtio_send_interrupt("Net".to_string()); + trace::virtqueue_send_interrupt("Net", &*queue as *const _ as u64); } rx_packets += 1; @@ -940,7 +940,7 @@ impl NetIoHandler { .with_context(|| { VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) })?; - trace::virtio_send_interrupt("Net".to_string()); + trace::virtqueue_send_interrupt("Net", &*queue as *const _ as u64); } tx_packets += 1; if tx_packets >= self.queue_size { diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 8a05b02bb..82b460a05 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -145,7 +145,7 @@ impl RngHandler { .with_context(|| { VirtioError::InterruptTrigger("rng", VirtioInterruptType::Vring) })?; - trace::virtio_send_interrupt("Rng".to_string()) + trace::virtqueue_send_interrupt("Rng", &*queue_lock as *const _ as u64) } Ok(()) diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index dcc0c61fa..d3d6cf427 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -17,7 +17,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ @@ -561,6 +561,7 @@ impl VirtioScsiReq VirtioInterruptType::Vring, ) })?; + trace::virtqueue_send_interrupt("ScsiCntlr", &*queue_lock as *const _ as u64); } Ok(()) @@ -584,6 +585,7 @@ struct ScsiCtrlQueueHandler { impl ScsiCtrlQueueHandler { fn handle_ctrl(&mut self) -> Result<()> { + trace::virtio_receive_request("ScsiCntlr".to_string(), "to ctrl".to_string()); let result = self.handle_ctrl_queue_requests(); if result.is_err() { report_virtio_error( @@ -728,6 +730,13 @@ impl ScsiRequestOps for CmdQueueRequest { } self.resp.response = VIRTIO_SCSI_S_OK; self.resp.status = status; + trace::virtio_scsi_handle_cmd_resp( + self.req.lun[1], + virtio_scsi_get_lun_id(self.req.lun), + self.req.tag, + self.resp.status, + self.resp.response, + ); self.complete()?; Ok(()) @@ -785,6 +794,7 @@ impl EventNotifierHelper for ScsiCmdQueueHandler { impl ScsiCmdQueueHandler { fn handle_cmd(&mut self) -> Result<()> { + trace::virtio_receive_request("ScsiCntlr".to_string(), "to cmd".to_string()); let result = self.handle_cmd_queue_requests(); if result.is_err() { report_virtio_error( @@ -817,7 +827,12 @@ impl ScsiCmdQueueHandler { self.driver_features, &elem, )?; - + trace::virtio_scsi_handle_cmd_req( + cmdq_request.req.lun[1], + virtio_scsi_get_lun_id(cmdq_request.req.lun), + cmdq_request.req.tag, + cmdq_request.req.cdb[0], + ); let mut need_handle = false; self.check_cmd_queue_request(&mut cmdq_request, &mut need_handle)?; if !need_handle { @@ -847,6 +862,13 @@ impl ScsiCmdQueueHandler { // If neither dataout nor datain is empty, return VIRTIO_SCSI_S_FAILURE immediately. qrequest.resp.response = VIRTIO_SCSI_S_FAILURE; qrequest.complete()?; + trace::virtio_scsi_handle_cmd_resp( + qrequest.req.lun[1], + virtio_scsi_get_lun_id(qrequest.req.lun), + qrequest.req.tag, + qrequest.resp.status, + qrequest.resp.response, + ); return Ok(()); } @@ -859,7 +881,13 @@ impl ScsiCmdQueueHandler { // It's not an error! qrequest.resp.response = VIRTIO_SCSI_S_BAD_TARGET; qrequest.complete()?; - debug!("no such scsi device, target {} lun {}", target_id, lun_id); + trace::virtio_scsi_handle_cmd_resp( + qrequest.req.lun[1], + virtio_scsi_get_lun_id(qrequest.req.lun), + qrequest.req.tag, + qrequest.resp.status, + qrequest.resp.response, + ); return Ok(()); } @@ -903,9 +931,12 @@ impl ScsiCmdQueueHandler { // Wrong virtio scsi request which doesn't provide enough datain/dataout buffer. qrequest.resp.response = VIRTIO_SCSI_S_OVERRUN; qrequest.complete()?; - debug!( - "command {:x} requested data's length({}),provided buffer length({})", - sreq.cmd.op, sreq.cmd.xfer, sreq.datalen + trace::virtio_scsi_handle_cmd_resp( + qrequest.req.lun[1], + virtio_scsi_get_lun_id(qrequest.req.lun), + qrequest.req.tag, + qrequest.resp.status, + qrequest.resp.response, ); return Ok(()); } -- Gitee From 374cb0499d31ea4a97d2e5eb225902df44895c27 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 7 Jan 2024 07:36:33 +0800 Subject: [PATCH 1537/2187] trace: add trace for virtio-serial Add trace for virtio-serial. Signed-off-by: liuxiangdong --- trace/trace_event/virtio.toml | 12 ++++++++++++ virtio/src/device/serial.rs | 20 +++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index d6215ae11..4c327f591 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -21,3 +21,15 @@ name = "virtio_scsi_handle_cmd_resp" args = "target: u8, lun: u16, tag: u64, status: u8, response: u8" message = "target={}, lun={}, tag={}, status={}, response={}." enabled = true + +[[events]] +name = "virtio_serial_output_data" +args = "iovec_size: u64, size: u64" +message = "iovec size {}, write size {}." +enabled = true + +[[events]] +name = "virtio_serial_disconnected_port" +args = "" +message = "virtio-serial port is none or disconnected." +enabled = true diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index ca7b6f0c7..1e8bf75c9 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -19,7 +19,7 @@ use std::{cmp, usize}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -417,7 +417,6 @@ struct SerialControlHandler { impl SerialPortHandler { fn output_handle(&mut self) { trace::virtio_receive_request("Serial".to_string(), "to IO".to_string()); - self.output_handle_internal().unwrap_or_else(|e| { error!("Port handle output error: {:?}", e); report_virtio_error( @@ -438,7 +437,6 @@ impl SerialPortHandler { if elem.desc_num == 0 { break; } - debug!("elem desc_unm: {}", elem.desc_num); // Discard requests when there is no port using this queue. Popping elements without // processing means discarding the request. @@ -447,17 +445,17 @@ impl SerialPortHandler { let mut iovec_size = Element::iovec_size(&iovec); while iovec_size > 0 { let mut buffer = [0_u8; BUF_SIZE]; - let size = iov_to_buf(&self.mem_space, &iovec, &mut buffer)?; + let size = iov_to_buf(&self.mem_space, &iovec, &mut buffer)? as u64; - self.write_chardev_msg(&buffer, size); + self.write_chardev_msg(&buffer, size as usize); - iovec = iov_discard_front(&mut iovec, size as u64) + iovec = iov_discard_front(&mut iovec, size) .unwrap_or_default() .to_vec(); // Safety: iovec follows the iov_discard_front operation and // iovec_size always equals Element::iovec_size(&iovec). - iovec_size -= size as u64; - debug!("iovec size {}, write size {}", iovec_size, size); + iovec_size -= size; + trace::virtio_serial_output_data(iovec_size, size); } } @@ -483,6 +481,7 @@ impl SerialPortHandler { VirtioInterruptType::Vring, ) })?; + trace::virtqueue_send_interrupt("Serial", &*queue_lock as *const _ as u64); } Ok(()) @@ -515,7 +514,7 @@ impl SerialPortHandler { fn get_input_avail_bytes(&mut self, max_size: usize) -> usize { let port = self.port.as_ref(); if port.is_none() || !port.unwrap().lock().unwrap().guest_connected { - debug!("virtio-serial port is none or disconnected"); + trace::virtio_serial_disconnected_port(); return 0; } @@ -603,6 +602,7 @@ impl SerialPortHandler { VirtioInterruptType::Vring, ) })?; + trace::virtqueue_send_interrupt("Serial", &*queue_lock as *const _ as u64); } if left == 0 { @@ -724,6 +724,7 @@ impl SerialControlHandler { VirtioInterruptType::Vring, ) })?; + trace::virtqueue_send_interrupt("Serial", &*queue_lock as *const _ as u64); } Ok(()) @@ -875,6 +876,7 @@ impl SerialControlHandler { VirtioInterruptType::Vring, ) })?; + trace::virtqueue_send_interrupt("Serial", &*queue_lock as *const _ as u64); } Ok(()) -- Gitee From c6a80586e0543aa12253c279849828518b32913d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 7 Jan 2024 07:56:15 +0800 Subject: [PATCH 1538/2187] trace: add trace for usb-camera Add trace for usb-camera. Signed-off-by: liuxiangdong --- devices/src/camera_backend/v4l2.rs | 17 +++++------------ devices/src/usb/camera.rs | 14 ++++++++------ devices/src/usb/keyboard.rs | 1 - devices/src/usb/storage.rs | 1 - devices/src/usb/tablet.rs | 1 - devices/src/usb/xhci/xhci_controller.rs | 1 + trace/trace_event/camera.toml | 23 +++++++++++++++++++++++ trace/trace_event/usb.toml | 23 +++++++++++++++++++++++ 8 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 trace/trace_event/camera.toml create mode 100644 trace/trace_event/usb.toml diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 897cd6015..358725480 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -17,7 +17,7 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; use v4l2_sys_mit::{ v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE, v4l2_buffer, v4l2_fmtdesc, v4l2_format, v4l2_frmivalenum, v4l2_frmsizeenum, v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE, @@ -129,7 +129,7 @@ impl V4l2CameraBackend { self.unregister_fd()?; } let backend = self.backend.as_ref().with_context(|| "Backend is none")?; - debug!("Camera {} register fd {}", self.id, backend.as_raw_fd()); + trace::camera_register_fd(&self.id, backend.as_raw_fd()); // Register event notifier for /dev/videoX. let handler = Arc::new(Mutex::new(V4l2IoHandler::new( &self.sample, @@ -152,7 +152,7 @@ impl V4l2CameraBackend { return Ok(()); } let backend = self.backend.as_ref().with_context(|| "Backend is none")?; - debug!("Camera {} unregister fd {}", self.id, backend.as_raw_fd()); + trace::camera_unregister_fd(&self.id, backend.as_raw_fd()); unregister_event_helper(self.iothread.as_ref(), &mut self.delete_evts)?; self.listening = false; Ok(()) @@ -245,11 +245,7 @@ impl CameraBackend for V4l2CameraBackend { // NOTE: Reopen backend to avoid Device or Resource busy. let backend = V4l2Backend::new(self.dev_path.clone(), BUFFER_CNT)?; - debug!( - "Camera {} set format open fd {}", - self.id, - backend.as_raw_fd() - ); + trace::camera_set_format(&self.id, backend.as_raw_fd()); self.backend = Some(Arc::new(backend)); let mut fmt = new_init::(); @@ -381,10 +377,7 @@ impl CameraBackend for V4l2CameraBackend { frm.interval, format_index, frame_index ) })?; - debug!( - "v4l2 get_format_by_index fmt {}, frm {}, info {:?}", - format_index, frame_index, out - ); + trace::camera_get_format_by_index(format_index, frame_index, &out); return Ok(out); } } diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 057ab4a94..2c68864e5 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -19,7 +19,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -678,7 +678,7 @@ impl UsbCamera { .as_mut_bytes() .copy_from_slice(&self.base.data_buf[0..len]); let cs = (device_req.value >> 8) as u8; - debug!("VideoStreamingControl {} {:?}", cs, vs_control); + trace::usb_camera_vs_control_request(cs, &vs_control); match device_req.request { SET_CUR => match cs { VS_PROBE_CONTROL => { @@ -790,7 +790,7 @@ impl UsbDevice for UsbCamera { { Ok(handled) => { if handled { - debug!("Camera control handled by descriptor, return directly."); + trace::usb_camera_handle_control(); return; } } @@ -991,9 +991,11 @@ impl CameraIoHandler { frame_data_size as usize, )?; pkt.actual_length += copied as u32; - debug!( - "Camera handle payload, frame_offset {} payloadoffset {} data_size {} copied {}", - locked_payload.frame_offset, locked_payload.payload_offset, frame_data_size, copied + trace::usb_camera_handle_payload( + locked_payload.frame_offset, + locked_payload.payload_offset, + frame_data_size, + copied, ); locked_payload.frame_offset += frame_data_size as usize; locked_payload.payload_offset += frame_data_size as usize; diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 447783b0f..8f8fee8f3 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -210,7 +210,6 @@ impl UsbDevice for UsbKeyboard { } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { - debug!("handle_control request {:?}", device_req); let mut locked_packet = packet.lock().unwrap(); match self .base diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 856664acc..2a009cd3b 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -549,7 +549,6 @@ impl UsbDevice for UsbStorage { } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { - debug!("Storage device handle_control request {:?}, ", device_req); let mut locked_packet = packet.lock().unwrap(); match self .base diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index bb93024ac..67d29035d 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -250,7 +250,6 @@ impl UsbDevice for UsbTablet { } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { - debug!("handle_control request {:?}", device_req); let mut locked_packet = packet.lock().unwrap(); match self .base diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 99b26c353..86370a70f 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1178,6 +1178,7 @@ impl XhciDevice { Vec::new(), None, ))); + trace::usb_handle_control(&locked_dev.usb_device_base().base.id, &device_req); locked_dev.handle_control(&p, &device_req); } diff --git a/trace/trace_event/camera.toml b/trace/trace_event/camera.toml new file mode 100644 index 000000000..4e0ee65de --- /dev/null +++ b/trace/trace_event/camera.toml @@ -0,0 +1,23 @@ +[[events]] +name = "camera_register_fd" +args = "id: &str, fd: i32" +message = "camera {} register fd {}." +enabled = true + +[[events]] +name = "camera_unregister_fd" +args = "id: &str, fd: i32" +message = "camera {} unregister fd {}." +enabled = true + +[[events]] +name = "camera_set_format" +args = "id: &str, fd: i32" +message = "camera {} set format open fd {}." +enabled = true + +[[events]] +name = "camera_get_format_by_index" +args = "format_index: u8, frame_index: u8, out: &dyn fmt::Debug" +message = "V4l2 fmt {}, frm {}, info {:?}." +enabled = true diff --git a/trace/trace_event/usb.toml b/trace/trace_event/usb.toml new file mode 100644 index 000000000..c6021a003 --- /dev/null +++ b/trace/trace_event/usb.toml @@ -0,0 +1,23 @@ +[[events]] +name = "usb_handle_control" +args = "device: &str, req: &dyn fmt::Debug" +message = "device {} handle control request {:?}." +enabled = true + +[[events]] +name = "usb_camera_vs_control_request" +args = "cs: u8, vs_control: &dyn fmt::Debug" +message = "VideoStreamingControl {} {:?}." +enabled = true + +[[events]] +name = "usb_camera_handle_control" +args = "" +message = "camera control handled by descriptor." +enabled = true + +[[events]] +name = "usb_camera_handle_payload" +args = "frame_offset: usize, payload_offset: usize, data_size: u64, copied: usize" +message = "camera handle payload, frame_offset {} payloadoffset {} data_size {} copied {}." +enabled = true -- Gitee From 2fa7f459793f65d0d1b2bb372b4bb99b657684f7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 22 Jan 2024 12:23:32 +0800 Subject: [PATCH 1539/2187] serial: delete useless architecture specific code Now, PL011 is supported in aarch64 architecture only, and Serial is supported in X86_64 architecture only. Delete useless architecture specific code. Signed-off-by: liuxiangdong --- devices/src/legacy/serial.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index fd9fb1b65..11f2d253e 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -28,8 +28,6 @@ use acpi::{ use address_space::GuestAddress; use chardev_backend::chardev::{Chardev, InputReceiver}; use hypervisor::kvm::KVM_FDS; -#[cfg(target_arch = "aarch64")] -use machine_manager::config::{BootSource, Param}; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; use migration::{ snapshot::SERIAL_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, @@ -138,7 +136,6 @@ impl Serial { sysbus: &mut SysBus, region_base: u64, region_size: u64, - #[cfg(target_arch = "aarch64")] bs: &Arc>, ) -> Result<()> { self.chardev .lock() @@ -157,11 +154,6 @@ impl Serial { dev.clone(), SERIAL_SNAPSHOT_ID, ); - #[cfg(target_arch = "aarch64")] - bs.lock().unwrap().kernel_cmdline.push(Param { - param_type: "earlycon".to_string(), - value: format!("uart,mmio,0x{:08x}", region_base), - }); let locked_dev = dev.lock().unwrap(); locked_dev.chardev.lock().unwrap().set_receiver(&dev); EventLoop::update_event( -- Gitee From 9be35b56c272b29dbb265bee6949f25c25347d6c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 14:35:29 +0800 Subject: [PATCH 1540/2187] trace: add trace point for device pl031 pl031 trace point: pl031_read pl031_write pl031_inject_interrupt Signed-off-by: yezengruan --- devices/src/legacy/pl031.rs | 3 +++ trace/trace_event/device_legacy.toml | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 trace/trace_event/device_legacy.toml diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index de5351f69..637e2e02f 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -127,6 +127,7 @@ impl PL031 { if let Err(e) = evt_fd.write(1) { error!("pl031: failed to write interrupt eventfd ({:?}).", e); } + trace::pl031_inject_interrupt(); return; } error!("pl031: failed to get interrupt event fd."); @@ -170,6 +171,7 @@ impl SysBusDevOps for PL031 { RTC_MIS => value = self.state.risr & self.state.imsr, _ => {} } + trace::pl031_read(offset, value); write_data_u32(data, value) } @@ -177,6 +179,7 @@ impl SysBusDevOps for PL031 { /// Write data to registers by guest. fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { let value = LittleEndian::read_u32(data); + trace::pl031_write(offset, value); match offset { RTC_MR => { diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml new file mode 100644 index 000000000..987bb540e --- /dev/null +++ b/trace/trace_event/device_legacy.toml @@ -0,0 +1,17 @@ +[[events]] +name = "pl031_read" +args = "addr: u64, value: u32" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "pl031_write" +args = "addr: u64, value: u32" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "pl031_inject_interrupt" +args = "" +message = "" +enabled = true -- Gitee From afe51d51fc74c6f7c794b2c25dfc4d9512e7ba73 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 15:12:52 +0800 Subject: [PATCH 1541/2187] trace: add trace point for device rtc rtc trace point: rtc_read rtc_write rtc_inject_interrupt Signed-off-by: yezengruan --- devices/src/legacy/rtc.rs | 3 +++ trace/trace_event/device_legacy.toml | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 10df08446..b1ce1f6e9 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -226,6 +226,7 @@ impl RTC { data[0] = self.cmos_data[self.cur_index as usize]; } } + trace::rtc_read(self.cur_index, data[0]); true } @@ -235,6 +236,7 @@ impl RTC { error!("RTC only supports writing data byte by byte."); return false; } + trace::rtc_write(self.cur_index, data[0]); match self.cur_index { RTC_SECONDS | RTC_MINUTES | RTC_HOURS | RTC_DAY_OF_WEEK | RTC_DAY_OF_MONTH @@ -278,6 +280,7 @@ impl RTC { if let Err(e) = evt_fd.write(1) { error!("cmos rtc: failed to write interrupt eventfd ({:?}).", e); } + trace::rtc_inject_interrupt(); return; } error!("cmos rtc: failed to get interrupt event fd."); diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml index 987bb540e..86ed61397 100644 --- a/trace/trace_event/device_legacy.toml +++ b/trace/trace_event/device_legacy.toml @@ -15,3 +15,21 @@ name = "pl031_inject_interrupt" args = "" message = "" enabled = true + +[[events]] +name = "rtc_read" +args = "addr: u8, value: u8" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "rtc_write" +args = "addr: u8, value: u8" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "rtc_inject_interrupt" +args = "" +message = "" +enabled = true -- Gitee From bba460dfab441db31a147f36575b0d956fb4a70c Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 19:20:34 +0800 Subject: [PATCH 1542/2187] trace: add trace point for device pl011 pl011 trace point: pl011_read pl011_read_fifo pl011_write pl011_interrupt pl011_baudrate_change pl011_receive pl011_receive_full Signed-off-by: yezengruan --- devices/src/legacy/pl011.rs | 8 ++++++ trace/trace_event/device_legacy.toml | 42 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index d7d529f38..d22b2ab69 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -153,6 +153,7 @@ impl PL011 { flag, e, ) } + trace::pl011_interrupt(flag); } } @@ -206,11 +207,13 @@ impl InputReceiver for PL011 { } self.state.rfifo[slot] = *val as u32; self.state.read_count += 1; + trace::pl011_receive(self.state.rfifo[slot], self.state.read_count); } // If in character-mode, or in FIFO-mode and FIFO is full, trigger the interrupt. if ((self.state.lcr & 0x10) == 0) || (self.state.read_count as usize == PL011_FIFO_SIZE) { self.state.flags |= PL011_FLAG_RXFF as u32; + trace::pl011_receive_full(); } if self.state.read_count >= self.state.read_trigger { self.state.int_level |= INT_RX; @@ -268,6 +271,7 @@ impl SysBusDevOps for PL011 { if self.state.read_count == self.state.read_trigger - 1 { self.state.int_level &= !INT_RX; } + trace::pl011_read_fifo(self.state.read_count); self.state.rsr = c >> 8; self.interrupt(); ret = c; @@ -322,6 +326,7 @@ impl SysBusDevOps for PL011 { } } data.copy_from_slice(&ret.as_bytes()[0..data.len()]); + trace::pl011_read(offset, ret); true } @@ -331,6 +336,7 @@ impl SysBusDevOps for PL011 { if !read_data_u32(data, &mut value) { return false; } + trace::pl011_write(offset, value); match offset >> 2 { 0 => { @@ -360,9 +366,11 @@ impl SysBusDevOps for PL011 { } 9 => { self.state.ibrd = value; + trace::pl011_baudrate_change(self.state.ibrd, self.state.fbrd); } 10 => { self.state.fbrd = value; + trace::pl011_baudrate_change(self.state.ibrd, self.state.fbrd); } 11 => { // PL011 works in two modes: character mode or FIFO mode. diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml index 86ed61397..c8fe4315f 100644 --- a/trace/trace_event/device_legacy.toml +++ b/trace/trace_event/device_legacy.toml @@ -33,3 +33,45 @@ name = "rtc_inject_interrupt" args = "" message = "" enabled = true + +[[events]] +name = "pl011_read" +args = "addr: u64, value: u32" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "pl011_read_fifo" +args = "read_count: u32" +message = "FIFO read, read_count now {}" +enabled = true + +[[events]] +name = "pl011_write" +args = "addr: u64, value: u32" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "pl011_interrupt" +args = "flag: u32" +message = "flag 0x{:X}" +enabled = true + +[[events]] +name = "pl011_baudrate_change" +args = "ibrd: u32, fbrd: u32" +message = "ibrd {}, fbrd {}" +enabled = true + +[[events]] +name = "pl011_receive" +args = "value: u32, read_count: u32" +message = "new char 0x{:X}, read_count now {}" +enabled = true + +[[events]] +name = "pl011_receive_full" +args = "" +message = "FIFO now full, RXFF set" +enabled = true -- Gitee From 69c56d80eea169ea5a9586cadbb3b6c6c816ed45 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 19:34:10 +0800 Subject: [PATCH 1543/2187] trace: add trace point for device serial serial trace point: serial_read serial_write serial_update_iir serial_receive Signed-off-by: yezengruan --- devices/src/legacy/serial.rs | 4 ++++ trace/trace_event/device_legacy.toml | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 3b864d5a0..fd9fb1b65 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -195,6 +195,7 @@ impl Serial { } error!("serial: failed to update iir."); } + trace::serial_update_iir(self.state.iir); } // Read one byte data from a certain register selected by `offset`. @@ -258,6 +259,7 @@ impl Serial { } _ => {} } + trace::serial_read(offset, ret); ret } @@ -276,6 +278,7 @@ impl Serial { // * fail to write serial. // * fail to flush serial. fn write_internal(&mut self, offset: u64, data: u8) -> Result<()> { + trace::serial_write(offset, data); match offset { 0 => { if self.state.lcr & UART_LCR_DLAB != 0 { @@ -356,6 +359,7 @@ impl InputReceiver for Serial { self.rbr.extend(data); self.state.lsr |= UART_LSR_DR; self.update_iir(); + trace::serial_receive(data.len()); } } diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml index c8fe4315f..848b04c18 100644 --- a/trace/trace_event/device_legacy.toml +++ b/trace/trace_event/device_legacy.toml @@ -75,3 +75,27 @@ name = "pl011_receive_full" args = "" message = "FIFO now full, RXFF set" enabled = true + +[[events]] +name = "serial_read" +args = "addr: u64, value: u8" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "serial_write" +args = "addr: u64, value: u8" +message = "addr 0x{:X} value 0x{:X}" +enabled = true + +[[events]] +name = "serial_update_iir" +args = "iir: u8" +message = "value 0x{:X}" +enabled = true + +[[events]] +name = "serial_receive" +args = "len: usize" +message = "data length {}" +enabled = true -- Gitee From 7a8b738f70347d89a41afee68549f9459c745bb7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 07:50:42 +0800 Subject: [PATCH 1544/2187] Eventloop: clean the eventloop before existing Set the global variable of EVENT_LOOP to none before existing. Signed-off-by: Xiao Ye --- machine_manager/src/event_loop.rs | 8 ++++++++ src/main.rs | 1 + 2 files changed, 9 insertions(+) diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 3946adf52..7acaca84a 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -164,6 +164,14 @@ impl EventLoop { } } } + + pub fn loop_clean() { + // SAFETY: the main_loop ctx is dedicated for main thread, thus no concurrent + // accessing. + unsafe { + GLOBAL_EVENT_LOOP = None; + } + } } pub fn register_event_helper( diff --git a/src/main.rs b/src/main.rs index 13fe59934..4310ab13f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -222,5 +222,6 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } EventLoop::loop_run().with_context(|| "MainLoop exits unexpectedly: error occurs")?; + EventLoop::loop_clean(); Ok(()) } -- Gitee From da2f193a099af99eba9367a31f6f344a6dc40105 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 07:37:25 +0800 Subject: [PATCH 1545/2187] ThreadPool: add thread pool Add thread pool in the main_loop context. Signed-off-by: Xiao Ye --- util/src/lib.rs | 1 + util/src/loop_context.rs | 12 ++ util/src/thread_pool.rs | 246 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 util/src/thread_pool.rs diff --git a/util/src/lib.rs b/util/src/lib.rs index 11b2de08a..7ca0d3a70 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -35,6 +35,7 @@ pub mod socket; pub mod syscall; pub mod tap; pub mod test_helper; +pub mod thread_pool; pub mod time; pub mod unix; #[cfg(feature = "usb_camera_v4l2")] diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 7fc7915bd..8317f34ab 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -31,6 +31,7 @@ use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; use crate::clock::{get_current_time, ClockState}; +use crate::thread_pool::ThreadPool; use crate::UtilError; const READY_EVENT_MAX: usize = 256; @@ -210,10 +211,20 @@ pub struct EventLoopContext { timers: Arc>>>, /// The next timer id to be used. timer_next_id: AtomicU64, + /// The context for thread pool. + pub thread_pool: Arc, /// Record VM clock state. pub clock_state: Arc>, } +impl Drop for EventLoopContext { + fn drop(&mut self) { + self.thread_pool + .cancel() + .unwrap_or_else(|e| error!("Thread pool cancel error: {:?}", e)); + } +} + // SAFETY: The closure in EventNotifier and Timer doesn't impl Send, they're // not sent between threads actually. unsafe impl Send for EventLoopContext {} @@ -232,6 +243,7 @@ impl EventLoopContext { ready_events: vec![EpollEvent::default(); READY_EVENT_MAX], timers: Arc::new(Mutex::new(Vec::new())), timer_next_id: AtomicU64::new(0), + thread_pool: Arc::new(ThreadPool::default()), clock_state: Arc::new(Mutex::new(ClockState::default())), }; ctx.init_kick(); diff --git a/util/src/thread_pool.rs b/util/src/thread_pool.rs new file mode 100644 index 000000000..df04c4b70 --- /dev/null +++ b/util/src/thread_pool.rs @@ -0,0 +1,246 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Condvar, Mutex}; +use std::thread; +use std::time::Duration; + +use anyhow::{bail, Context, Result}; +use log::error; + +use crate::link_list::{List, Node}; + +const MIN_THREADS: u64 = 1; +const MAX_THREADS: u64 = 64; +type PoolTask = Box; + +pub trait TaskOperation: Sync + Send { + fn run(&mut self); +} + +struct PoolState { + /// The total number of current threads in thread pool. + /// Including the number of threads need to be created and the number of running threads. + total_threads: u64, + /// The current number of blocking threads, they will be blocked + /// until awakened by request_cond or timeout. + blocked_threads: u64, + /// The number of threads need to be created. It could be created + /// in the main loop or another thread in thread pool later. + new_threads: u64, + /// The number of threads that have been created but + /// have not yet entered the work loop. + pending_threads: u64, + /// The minimum number of threads residing in the thread pool. + min_threads: u64, + /// The maximum number of threads that thread pool can create. + max_threads: u64, + /// List of pending tasks in the thread pool. + req_lists: List, +} + +/// SAFETY: All the operations on req_lists are protected by the mutex, +/// so there is no synchronization problem. +unsafe impl Send for PoolState {} + +impl PoolState { + fn new() -> Self { + Self { + total_threads: 0, + blocked_threads: 0, + new_threads: 0, + pending_threads: 0, + min_threads: MIN_THREADS, + max_threads: MAX_THREADS, + req_lists: List::new(), + } + } + + fn spawn_thread_needed(&self) -> bool { + self.blocked_threads == 0 && self.total_threads < self.max_threads + } + + fn is_running(&self) -> bool { + self.total_threads <= self.max_threads + } + + fn spawn_thread(&mut self, pool: Arc) -> Result<()> { + self.total_threads += 1; + self.new_threads += 1; + + if self.pending_threads == 0 { + self.do_spawn_thread(pool)?; + } + Ok(()) + } + + fn do_spawn_thread(&mut self, pool: Arc) -> Result<()> { + if self.new_threads == 0 { + return Ok(()); + } + + self.new_threads -= 1; + self.pending_threads += 1; + thread::Builder::new() + .name("thread-pool".to_string()) + .spawn(move || worker_thread(pool)) + .with_context(|| "Failed to create thread in pool!")?; + Ok(()) + } +} + +pub struct ThreadPool { + /// Data shared by all threads in the pool. + pool_state: Arc>, + /// Notify the thread in the pool that there are some work to do. + request_cond: Condvar, + /// Notify threadpool that the current thread has exited. + stop_cond: Condvar, +} + +impl Default for ThreadPool { + fn default() -> Self { + Self { + pool_state: Arc::new(Mutex::new(PoolState::new())), + request_cond: Condvar::new(), + stop_cond: Condvar::new(), + } + } +} + +impl ThreadPool { + /// Submit task to thread pool. + pub fn submit_task(pool: Arc, task: Box) -> Result<()> { + let mut locked_state = pool.pool_state.lock().unwrap(); + if locked_state.spawn_thread_needed() { + locked_state.spawn_thread(pool.clone())? + } + locked_state.req_lists.add_tail(Box::new(Node::new(task))); + drop(locked_state); + + pool.request_cond.notify_one(); + Ok(()) + } + + /// It should be confirmed that all threads have successfully exited + /// before function return. + pub fn cancel(&self) -> Result<()> { + let mut locked_state = self.pool_state.lock().unwrap(); + locked_state.total_threads -= locked_state.new_threads; + locked_state.new_threads = 0; + locked_state.max_threads = 0; + self.request_cond.notify_all(); + + while locked_state.total_threads > 0 { + match self.stop_cond.wait(locked_state) { + Ok(lock) => locked_state = lock, + Err(e) => bail!("{:?}", e), + } + } + Ok(()) + } +} + +fn worker_thread(pool: Arc) { + let mut locked_state = pool.pool_state.lock().unwrap(); + locked_state.pending_threads -= 1; + locked_state + .do_spawn_thread(pool.clone()) + .unwrap_or_else(|e| error!("Thread pool error: {:?}", e)); + + while locked_state.is_running() { + let result; + + if locked_state.req_lists.len == 0 { + locked_state.blocked_threads += 1; + match pool + .request_cond + .wait_timeout(locked_state, Duration::from_secs(10)) + { + Ok((guard, ret)) => { + locked_state = guard; + result = ret; + } + Err(e) => { + error!("Unknown errors have occurred thread pool: {:?}", e); + locked_state = e.into_inner().0; + break; + } + } + locked_state.blocked_threads -= 1; + + if result.timed_out() + && locked_state.req_lists.len == 0 + && locked_state.total_threads > locked_state.min_threads + { + // If wait time_out and no pending task and current total number + // of threads exceeds the minimum, then exit. + break; + } + + continue; + } + + let mut req = locked_state.req_lists.pop_head().unwrap(); + drop(locked_state); + + (*req.value).run(); + + locked_state = pool.pool_state.lock().unwrap(); + } + locked_state.total_threads -= 1; + + pool.stop_cond.notify_one(); + pool.request_cond.notify_one(); +} + +#[cfg(test)] +mod test { + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::Arc; + use std::{thread, time}; + + use super::{TaskOperation, ThreadPool}; + + struct PoolTask { + count: Arc, + } + + impl TaskOperation for PoolTask { + fn run(&mut self) { + std::thread::sleep(std::time::Duration::from_millis(50)); + self.count.fetch_add(1, Ordering::SeqCst); + } + } + + #[test] + fn test_pool_exit() { + let pool = Arc::new(ThreadPool::default()); + let count = Arc::new(AtomicU64::new(0)); + let begin = time::SystemTime::now(); + for _ in 0..10 { + let task = Box::new(PoolTask { + count: count.clone(), + }); + assert!(ThreadPool::submit_task(pool.clone(), task).is_ok()); + } + + thread::sleep(time::Duration::from_millis(10)); + assert!(pool.cancel().is_ok()); + let end = time::SystemTime::now(); + let duration = end.duration_since(begin).unwrap().as_millis(); + // All tasks are processed in parallel. + assert!(duration < 50 * 10); + // All the task has been finished. + assert_eq!(count.load(Ordering::SeqCst), 10); + } +} -- Gitee From 3e6b434ea3a0e3c3f345a2db173be36a1837ad52 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 9 Jan 2024 21:57:43 +0800 Subject: [PATCH 1546/2187] trace: add trace point for device usb storage usb storage trace point: usb_storage_handle_control usb_storage_handle_data usb_storage_handle_token_out usb_storage_handle_token_in usb_storage_handle_data_inout_packet usb_storage_handle_scsi_request Signed-off-by: yezengruan --- devices/src/usb/storage.rs | 18 ++++++++++-------- trace/trace_event/usb.toml | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 2a009cd3b..b227ecc08 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -17,7 +17,7 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; use once_cell::sync::Lazy; use super::descriptor::{ @@ -371,7 +371,7 @@ impl UsbStorage { let mut cbw_buf = [0_u8; CBW_SIZE as usize]; packet.transfer_packet(&mut cbw_buf, CBW_SIZE as usize); self.state.cbw.convert(&cbw_buf); - debug!("Storage cbw {:?}", self.state.cbw); + trace::usb_storage_handle_token_out(&self.state.cbw); if self.state.cbw.sig != CBW_SIGNATURE { bail!("Bad signature {:x}", self.state.cbw.sig); @@ -426,7 +426,7 @@ impl UsbStorage { let mut csw_buf = [0_u8; CSW_SIZE as usize]; self.state.csw.tag = self.state.cbw.tag; self.state.csw.convert(&mut csw_buf); - debug!("Storage csw {:?}", self.state.csw); + trace::usb_storage_handle_token_in(&self.state.csw); packet.transfer_packet(&mut csw_buf, CSW_SIZE as usize); // Reset UsbStorageState. @@ -460,10 +460,10 @@ impl UsbStorage { } self.state.iovec_len = iovec_len; - debug!("Storage: iovec_len {}.", iovec_len); self.handle_scsi_request(packet)?; packet.actual_length = iovec_len; self.state.mode = UsbMsdMode::Csw; + trace::usb_storage_handle_data_inout_packet(iovec_len); Ok(()) } @@ -504,6 +504,7 @@ impl UsbStorage { let csw_h = &sreq_h.lock().unwrap().upper_req; let csw = csw_h.as_ref().as_any().downcast_ref::().unwrap(); self.state.csw = *csw; + trace::usb_storage_handle_scsi_request(csw); Ok(()) } @@ -556,7 +557,7 @@ impl UsbDevice for UsbStorage { { Ok(handled) => { if handled { - debug!("Storage control handled by descriptor, return directly."); + trace::usb_storage_handle_control(); return; } self.handle_control_packet(&mut locked_packet, device_req) @@ -570,9 +571,10 @@ impl UsbDevice for UsbStorage { fn handle_data(&mut self, packet: &Arc>) { let mut locked_packet = packet.lock().unwrap(); - debug!( - "Storage device handle_data endpoint {}, mode {:?}", - locked_packet.ep_number, self.state.mode + trace::usb_storage_handle_data( + locked_packet.ep_number, + locked_packet.pid, + &self.state.mode, ); let result = match locked_packet.pid as u8 { diff --git a/trace/trace_event/usb.toml b/trace/trace_event/usb.toml index c6021a003..412385c4b 100644 --- a/trace/trace_event/usb.toml +++ b/trace/trace_event/usb.toml @@ -21,3 +21,39 @@ name = "usb_camera_handle_payload" args = "frame_offset: usize, payload_offset: usize, data_size: u64, copied: usize" message = "camera handle payload, frame_offset {} payloadoffset {} data_size {} copied {}." enabled = true + +[[events]] +name = "usb_storage_handle_control" +args = "" +message = "storage control handled by descriptor." +enabled = true + +[[events]] +name = "usb_storage_handle_data" +args = "ep_number: u8, pid: u32, mode: &dyn fmt::Debug" +message = "endpoint {}, pid 0x{:X}, mode {:?}" +enabled = true + +[[events]] +name = "usb_storage_handle_token_out" +args = "cbw: &dyn fmt::Debug" +message = "cbw {:?}" +enabled = true + +[[events]] +name = "usb_storage_handle_token_in" +args = "csw: &dyn fmt::Debug" +message = "csw {:?}" +enabled = true + +[[events]] +name = "usb_storage_handle_data_inout_packet" +args = "len: u32" +message = "iovec length {}" +enabled = true + +[[events]] +name = "usb_storage_handle_scsi_request" +args = "csw: &dyn fmt::Debug" +message = "csw {:?}" +enabled = true -- Gitee From 8d6ddfd1f75a721e54fabeb83b535f7e15bd107e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 10 Jan 2024 15:06:11 +0800 Subject: [PATCH 1547/2187] trace: add trace point for device virtio rng virtio rng trace point: virtio_rng_write_req_data Signed-off-by: yezengruan --- trace/trace_event/virtio.toml | 6 ++++++ virtio/src/device/rng.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index 4c327f591..20e5e0f9f 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -33,3 +33,9 @@ name = "virtio_serial_disconnected_port" args = "" message = "virtio-serial port is none or disconnected." enabled = true + +[[events]] +name = "virtio_rng_write_req_data" +args = "size: u32" +message = "size {}" +enabled = true diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 82b460a05..b63b2ba3b 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -87,6 +87,7 @@ impl RngHandler { offset += iov.len as usize; } + trace::virtio_rng_write_req_data(size); Ok(()) } -- Gitee From 9e2181a52c623e8198bd1af231805fbcc676885a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 12 Jan 2024 08:56:36 +0800 Subject: [PATCH 1548/2187] trace: add trace point for device virtio block virtio block trace point: virtio_blk_process_queue_suppress_notify virtio_blk_complete_request virtio_blk_complete_one_request virtio_blk_execute virtio_blk_handle_discard_write_zeroes_req virtio_blk_merge_req_queue virtio_blk_read_config virtio_blk_write_config Signed-off-by: yezengruan --- trace/trace_event/virtio.toml | 48 +++++++++++++++++++++++++++++++++++ virtio/src/device/block.rs | 11 +++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index 20e5e0f9f..bd84d1163 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -39,3 +39,51 @@ name = "virtio_rng_write_req_data" args = "size: u32" message = "size {}" enabled = true + +[[events]] +name = "virtio_blk_process_queue_suppress_notify" +args = "len: u16" +message = "len {}" +enabled = true + +[[events]] +name = "virtio_blk_complete_request" +args = "status: u8" +message = "status {}" +enabled = true + +[[events]] +name = "virtio_blk_complete_one_request" +args = "index: u16, len: u32" +message = "index {}, len {}" +enabled = true + +[[events]] +name = "virtio_blk_execute" +args = "request_type: u32, len: usize, offset: usize" +message = "request type {}, iovecs len {}, offset {}" +enabled = true + +[[events]] +name = "virtio_blk_handle_discard_write_zeroes_req" +args = "opcode: &dyn fmt::Debug, flags: u32, offset: usize, nbytes: u64" +message = "opcode {:?}, flags {}, offset {}, nbytes {}" +enabled = true + +[[events]] +name = "virtio_blk_merge_req_queue" +args = "can_merge: bool, merged_reqs: u16, merged_iovs: usize, merged_bytes: u64" +message = "can_merge {}, merged_reqs {}, merged_iovs {}, merged_bytes {}" +enabled = true + +[[events]] +name = "virtio_blk_read_config" +args = "offset: u64, data: &[u8]" +message = "offset {}, data {:?}" +enabled = true + +[[events]] +name = "virtio_blk_write_config" +args = "offset: u64, data: &[u8]" +message = "offset {}, data {:?}" +enabled = true diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index d7669d645..976fc4d5e 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -148,6 +148,7 @@ impl AioCompleteCb { } fn complete_request(&self, status: u8) -> Result<()> { + trace::virtio_blk_complete_request(status); let mut req = Some(self.req.as_ref()); while let Some(req_raw) = req { self.complete_one_request(req_raw, status)?; @@ -171,6 +172,7 @@ impl AioCompleteCb { req.desc_index, req.in_len ) })?; + trace::virtio_blk_complete_one_request(req.desc_index, req.in_len); if queue_lock .vring @@ -314,6 +316,7 @@ impl Request { MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); } } + trace::virtio_blk_execute(request_type, iovecs.len(), offset); let serial_num = &iohandler.serial_num; let mut locked_backend = block_backend.lock().unwrap(); @@ -418,6 +421,7 @@ impl Request { let mut locked_backend = block_backend.lock().unwrap(); let offset = (sector as usize) << SECTOR_SHIFT; let nbytes = (num_sectors as u64) << SECTOR_SHIFT; + trace::virtio_blk_handle_discard_write_zeroes_req(&opcode, flags, offset, nbytes); if opcode == OpCode::Discard { if flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP { error!("Discard request must not set unmap flags"); @@ -546,6 +550,7 @@ impl BlockIoHandler { merged_iovs = req_iovs; merged_bytes = req_bytes; } + trace::virtio_blk_merge_req_queue(can_merge, merged_reqs, merged_iovs, merged_bytes); } merge_req_queue @@ -645,6 +650,7 @@ impl BlockIoHandler { *locked_status = BlockStatus::NormalIO; } } + trace::virtio_blk_process_queue_suppress_notify(len); let mut done = false; let start_time = Instant::now(); @@ -1123,13 +1129,16 @@ impl VirtioDevice for Block { fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let config_len = self.get_blk_config_size(); let config = &self.config_space.as_bytes()[..config_len]; - read_config_default(config, offset, data) + read_config_default(config, offset, data)?; + trace::virtio_blk_read_config(offset, data); + Ok(()) } fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { let config_len = self.get_blk_config_size(); let config = &self.config_space.as_bytes()[..config_len]; check_config_space_rw(config, offset, data)?; + trace::virtio_blk_write_config(offset, data); // The only writable field is "writeback", but it's not supported for now, // so do nothing here. Ok(()) -- Gitee From 844b2f22396850a805196f1f6660c886b654799d Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 08:08:37 +0800 Subject: [PATCH 1549/2187] Threads: add an aio type of threads Add an aio type of threads, which submit the io by thread pool. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 4 ++- block_backend/src/qcow2/refcount.rs | 1 + devices/src/scsi/disk.rs | 2 +- image/src/img.rs | 8 ++--- util/src/aio/libaio.rs | 13 +++++++- util/src/aio/mod.rs | 43 +++++++++++++++++++++---- util/src/aio/threads.rs | 50 +++++++++++++++++++++++++++++ util/src/aio/uring.rs | 18 +++++++++-- virtio/src/device/block.rs | 17 ++++++++-- 9 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 util/src/aio/threads.rs diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index f7ca80ef6..1f2170e4b 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -134,7 +134,7 @@ impl SyncAioInfo { pub fn new(fd: RawFd, prop: BlockProperty) -> Result { Ok(Self { - aio: Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off)?, + aio: Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?, fd, prop, }) @@ -1912,6 +1912,7 @@ mod test { let aio = Aio::new( Arc::new(SyncAioInfo::complete_func), util::aio::AioEngine::Off, + None, ) .unwrap(); let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()).unwrap(); @@ -1986,6 +1987,7 @@ mod test { let aio = Aio::new( Arc::new(SyncAioInfo::complete_func), util::aio::AioEngine::Off, + None, ) .unwrap(); let (req_align, buf_align) = get_file_alignment(&image.file, true); diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 41da3cb06..1a3dfdddc 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -784,6 +784,7 @@ mod test { let aio = Aio::new( Arc::new(SyncAioInfo::complete_func), util::aio::AioEngine::Off, + None, ) .unwrap(); let conf = BlockProperty { diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 52ad0534f..11224b92c 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -176,7 +176,7 @@ impl ScsiDevice { self.buf_align = alignments.1; let drive_id = VmConfig::get_drive_id(&drive_files, &self.config.path_on_host)?; - let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type)?; + let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type, None)?; let conf = BlockProperty { id: drive_id, format: self.config.format, diff --git a/image/src/img.rs b/image/src/img.rs index 2a3fc39b8..41d3924ee 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -171,7 +171,7 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { .custom_flags(libc::O_CREAT | libc::O_TRUNC) .open(path.clone())?; - let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off)?; + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; let image_info = match disk_fmt { DiskFormat::Raw => { create_options.conf.format = DiskFormat::Raw; @@ -333,7 +333,7 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { qcow2_conf.discard = true; qcow2_conf.write_zeroes = WriteZeroesState::Unmap; - let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let mut qcow2_driver = Qcow2Driver::new(image_file.file.try_clone()?, aio, qcow2_conf.clone())?; qcow2_driver.load_metadata(qcow2_conf)?; @@ -362,7 +362,7 @@ pub(crate) fn create_qcow2_driver_for_check( file: File, conf: BlockProperty, ) -> Result> { - let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()) .with_context(|| "Failed to create qcow2 driver")?; @@ -479,7 +479,7 @@ mod test { fn create_driver(&self) -> Qcow2Driver<()> { let mut conf = BlockProperty::default(); conf.format = DiskFormat::Qcow2; - let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off).unwrap(); + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let mut qcow2_driver = Qcow2Driver::new(self.file.try_clone().unwrap(), aio, conf.clone()).unwrap(); qcow2_driver.load_metadata(conf).unwrap(); diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 85fd20c63..d4af4df14 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -17,6 +17,7 @@ use anyhow::bail; use kvm_bindings::__IncompleteArrayField; use vmm_sys_util::eventfd::EventFd; +use super::threads::ThreadsAioContext; use super::{AioCb, AioContext, AioEvent, OpCode, Result}; const IOCB_FLAG_RESFD: u32 = 1; @@ -69,6 +70,7 @@ pub(crate) enum IoContext {} pub(crate) struct LibaioContext { ctx: *mut IoContext, + _threads_aio_ctx: ThreadsAioContext, resfd: RawFd, events: Vec, } @@ -109,10 +111,15 @@ impl LibaioContext { Ok(ctx) } - pub fn new(max_size: u32, eventfd: &EventFd) -> Result { + pub fn new( + max_size: u32, + threads_aio_ctx: ThreadsAioContext, + eventfd: &EventFd, + ) -> Result { let ctx = Self::probe(max_size)?; Ok(LibaioContext { ctx, + _threads_aio_ctx: threads_aio_ctx, resfd: eventfd.as_raw_fd(), events: Vec::with_capacity(max_size as usize), }) @@ -167,6 +174,10 @@ impl AioContext for LibaioContext { Ok(0) } + fn submit_threads_pool(&mut self, _iocbp: &[*const AioCb]) -> Result { + todo!() + } + fn get_events(&mut self) -> &[AioEvent] { let ring = self.ctx as *mut AioRing; // SAFETY: self.ctx is generated by SYS_io_setup. diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 9a9547eac..b9294bcb1 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -12,6 +12,7 @@ mod libaio; mod raw; +mod threads; mod uring; pub use raw::*; @@ -32,8 +33,10 @@ use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; use crate::num_ops::{round_down, round_up}; +use crate::thread_pool::ThreadPool; use crate::unix::host_page_size; use libaio::LibaioContext; +use threads::ThreadsAioContext; type CbList = List>; type CbNode = Node>; @@ -44,6 +47,8 @@ const AIO_OFF: &str = "off"; const AIO_NATIVE: &str = "native"; /// Io-uring aio type. const AIO_IOURING: &str = "io_uring"; +/// Aio implemented by thread pool. +const AIO_THREADS: &str = "threads"; /// Max bytes of bounce buffer for IO. const MAX_LEN_BOUNCE_BUFF: u64 = 1 << 20; @@ -56,6 +61,8 @@ pub enum AioEngine { Native = 1, #[serde(alias = "iouring")] IoUring = 2, + #[serde(alias = "threads")] + Threads = 3, } impl FromStr for AioEngine { @@ -66,6 +73,7 @@ impl FromStr for AioEngine { AIO_OFF => Ok(AioEngine::Off), AIO_NATIVE => Ok(AioEngine::Native), AIO_IOURING => Ok(AioEngine::IoUring), + AIO_THREADS => Ok(AioEngine::Threads), _ => Err(()), } } @@ -122,6 +130,8 @@ pub fn get_iov_size(iovecs: &[Iovec]) -> u64 { trait AioContext { /// Submit IO requests to the OS, the nr submitted is returned. fn submit(&mut self, iocbp: &[*const AioCb]) -> Result; + /// Submit Io requests to the thread pool, the nr submitted is returned. + fn submit_threads_pool(&mut self, iocbp: &[*const AioCb]) -> Result; /// Get the IO events of the requests submitted earlier. fn get_events(&mut self) -> &[AioEvent]; } @@ -203,7 +213,6 @@ pub struct Aio { pub fn aio_probe(engine: AioEngine) -> Result<()> { match engine { - AioEngine::Off => {} AioEngine::Native => { let ctx = LibaioContext::probe(1)?; // SAFETY: if no err, ctx is valid. @@ -212,18 +221,39 @@ pub fn aio_probe(engine: AioEngine) -> Result<()> { AioEngine::IoUring => { IoUringContext::probe(1)?; } + _ => {} } Ok(()) } impl Aio { - pub fn new(func: Arc>, engine: AioEngine) -> Result { + pub fn new( + func: Arc>, + engine: AioEngine, + thread_pool: Option>, + ) -> Result { let max_events: usize = 128; let fd = EventFd::new(libc::EFD_NONBLOCK)?; - let ctx: Option>> = match engine { - AioEngine::Off => None, - AioEngine::Native => Some(Box::new(LibaioContext::new(max_events as u32, &fd)?)), - AioEngine::IoUring => Some(Box::new(IoUringContext::new(max_events as u32, &fd)?)), + let ctx: Option>> = if let Some(pool) = thread_pool { + let threads_aio_ctx = ThreadsAioContext::new(max_events as u32, &fd, pool); + match engine { + AioEngine::Native => Some(Box::new(LibaioContext::new( + max_events as u32, + threads_aio_ctx, + &fd, + )?)), + AioEngine::IoUring => Some(Box::new(IoUringContext::new( + max_events as u32, + threads_aio_ctx, + &fd, + )?)), + AioEngine::Threads => Some(Box::new(threads_aio_ctx)), + _ => bail!("Aio type {:?} does not support thread pools", engine), + } + } else if engine == AioEngine::Off { + None + } else { + bail!("Aio type {:?} is lack of thread pool context", engine); }; Ok(Aio { @@ -821,6 +851,7 @@ mod tests { let mut aio = Aio::new( Arc::new(|_: &AioCb, _: i64| -> Result<()> { Ok(()) }), AioEngine::Off, + None, ) .unwrap(); aio.submit_request(aiocb).unwrap(); diff --git a/util/src/aio/threads.rs b/util/src/aio/threads.rs new file mode 100644 index 000000000..16e73fc7d --- /dev/null +++ b/util/src/aio/threads.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex}; + +use vmm_sys_util::eventfd::EventFd; + +use crate::aio::{AioCb, AioContext, AioEvent, Result}; +use crate::thread_pool::ThreadPool; + +pub struct ThreadsAioContext { + _pool: Arc, + _events: Vec, + _complete_list: Arc>>, + _notify_event: Arc>, +} + +impl ThreadsAioContext { + pub fn new(max_size: u32, eventfd: &EventFd, thread_pool: Arc) -> Self { + Self { + _pool: thread_pool, + _complete_list: Arc::new(Mutex::new(Vec::new())), + _notify_event: Arc::new(Mutex::new((*eventfd).try_clone().unwrap())), + _events: Vec::with_capacity(max_size as usize), + } + } +} + +impl AioContext for ThreadsAioContext { + fn submit(&mut self, _iocbp: &[*const AioCb]) -> Result { + todo!() + } + + fn submit_threads_pool(&mut self, _iocbp: &[*const AioCb]) -> Result { + todo!() + } + + fn get_events(&mut self) -> &[AioEvent] { + todo!() + } +} diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 4ae0dc442..f67715453 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -17,11 +17,13 @@ use io_uring::{opcode, squeue, types, IoUring}; use libc; use vmm_sys_util::eventfd::EventFd; +use super::threads::ThreadsAioContext; use super::{AioCb, AioContext, AioEvent, OpCode, Result}; /// The io-uring context. pub(crate) struct IoUringContext { ring: IoUring, + _threads_aio_ctx: ThreadsAioContext, events: Vec, } @@ -30,7 +32,11 @@ impl IoUringContext { IoUring::new(entries).with_context(|| "Failed to create io_uring instance.") } - pub fn new(entries: u32, eventfd: &EventFd) -> Result { + pub fn new( + entries: u32, + threads_aio_ctx: ThreadsAioContext, + eventfd: &EventFd, + ) -> Result { let tmp_entries = entries as i32; // Ensure the power of 2. if (tmp_entries & -tmp_entries) != tmp_entries || tmp_entries == 0 { @@ -42,7 +48,11 @@ impl IoUringContext { .register_eventfd(eventfd.as_raw_fd()) .with_context(|| "Failed to register event fd")?; let events = Vec::with_capacity(entries as usize); - Ok(IoUringContext { ring, events }) + Ok(IoUringContext { + ring, + _threads_aio_ctx: threads_aio_ctx, + events, + }) } } @@ -86,6 +96,10 @@ impl AioContext for IoUringContext { self.ring.submit().with_context(|| "Failed to submit sqe") } + fn submit_threads_pool(&mut self, _iocbp: &[*const AioCb]) -> Result { + todo!() + } + fn get_events(&mut self) -> &[AioEvent] { let queue = self.ring.completion(); self.events.clear(); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 41841986d..3b99ae7c6 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -49,8 +49,8 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::aio::{ - iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, AioReqResult, Iovec, OpCode, - WriteZeroesState, + iov_from_buf_direct, iov_to_buf_direct, raw_datasync, Aio, AioCb, AioEngine, AioReqResult, + Iovec, OpCode, WriteZeroesState, }; use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; @@ -1066,7 +1066,16 @@ impl VirtioDevice for Block { self.buf_align = alignments.1; let drive_id = VmConfig::get_drive_id(&drive_files, &self.blk_cfg.path_on_host)?; - let aio = Aio::new(Arc::new(BlockIoHandler::complete_func), self.blk_cfg.aio)?; + let mut thread_pool = None; + if self.blk_cfg.aio != AioEngine::Off { + thread_pool = Some(EventLoop::get_ctx(None).unwrap().thread_pool.clone()); + } + let aio = Aio::new( + Arc::new(BlockIoHandler::complete_func), + self.blk_cfg.aio, + thread_pool, + )?; + let conf = BlockProperty { id: drive_id, format: self.blk_cfg.format, @@ -1389,6 +1398,7 @@ mod tests { // Use different input parameters to verify block `new()` and `realize()` functionality. #[test] fn test_block_init() { + assert!(EventLoop::object_init(&None).is_ok()); // New block device let mut block = init_default_block(); assert_eq!(block.disk_sectors, 0); @@ -1417,6 +1427,7 @@ mod tests { assert_eq!(block.device_type(), VIRTIO_TYPE_BLOCK); assert_eq!(block.queue_num(), QUEUE_NUM_BLK); assert_eq!(block.queue_size_max(), DEFAULT_VIRTQUEUE_SIZE); + EventLoop::loop_clean(); } // Test `write_config` and `read_config`. The main contests include: compare expect data and -- Gitee From e64b082b9d309d6b56a84ef4b5b7e72b2da2e418 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 15 Jan 2024 08:36:38 +0800 Subject: [PATCH 1550/2187] Threads: modify some baic function on aio. Split the synchronous IO and asynchronous IO from the function of submit_request. Signed-off-by: Xiao Ye --- util/src/aio/mod.rs | 573 +++++++++++++++++++++++--------------------- 1 file changed, 305 insertions(+), 268 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index b9294bcb1..35c3db081 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -136,6 +136,7 @@ trait AioContext { fn get_events(&mut self) -> &[AioEvent]; } +#[derive(Clone)] pub struct AioEvent { pub user_data: u64, pub status: i64, @@ -195,6 +196,264 @@ impl AioCb { } AioReqResult::Done } + + pub fn rw_sync(&self) -> i32 { + let mut ret = match self.opcode { + OpCode::Preadv => raw_readv(self.file_fd, &self.iovec, self.offset as usize), + OpCode::Pwritev => raw_writev(self.file_fd, &self.iovec, self.offset as usize), + _ => -1, + }; + if ret < 0 { + error!("Failed to do sync read/write."); + } else if ret as u64 != self.nbytes { + error!("Incomplete sync read/write."); + ret = -1; + } + ret as i32 + } + + fn flush_sync(&self) -> i32 { + let ret = raw_datasync(self.file_fd); + if ret < 0 { + error!("Failed to do sync flush."); + } + ret as i32 + } + + fn discard_sync(&self) -> i32 { + let ret = raw_discard(self.file_fd, self.offset as usize, self.nbytes); + if ret < 0 && ret != -libc::ENOTSUP { + error!("Failed to do sync discard."); + } + ret + } + + fn write_zeroes_sync(&mut self) -> i32 { + let mut ret; + if self.opcode == OpCode::WriteZeroesUnmap { + ret = raw_discard(self.file_fd, self.offset as usize, self.nbytes); + if ret == 0 { + return ret; + } + } + ret = raw_write_zeroes(self.file_fd, self.offset as usize, self.nbytes); + if ret == -libc::ENOTSUP && !self.iovec.is_empty() { + self.opcode = OpCode::Pwritev; + return self.rw_sync(); + } + + if ret < 0 { + error!("Failed to do sync write zeroes."); + } + + ret + } + + // If the buffer is full with zero and the operation is Pwritev, + // It's equal to write zero operation. + fn try_convert_to_write_zero(&mut self) { + if self.opcode == OpCode::Pwritev + && self.write_zeroes != WriteZeroesState::Off + && iovec_is_zero(&self.iovec) + { + self.opcode = OpCode::WriteZeroes; + if self.write_zeroes == WriteZeroesState::Unmap && self.discard { + self.opcode = OpCode::WriteZeroesUnmap; + } + } + } + + pub fn is_misaligned(&self) -> bool { + if self.direct && (self.opcode == OpCode::Preadv || self.opcode == OpCode::Pwritev) { + if (self.offset as u64) & (self.req_align as u64 - 1) != 0 { + return true; + } + for iov in self.iovec.iter() { + if iov.iov_base & (self.buf_align as u64 - 1) != 0 { + return true; + } + if iov.iov_len & (self.req_align as u64 - 1) != 0 { + return true; + } + } + } + false + } + + pub fn handle_misaligned(&mut self) -> Result { + let max_len = round_down( + self.nbytes + self.req_align as u64 * 2, + self.req_align as u64, + ) + .with_context(|| "Failed to round down request length.")?; + // Set upper limit of buffer length to avoid OOM. + let buff_len = std::cmp::min(max_len, MAX_LEN_BOUNCE_BUFF); + let bounce_buffer = + // SAFETY: We allocate aligned memory and free it later. Alignment is set to + // host page size to decrease the count of allocated pages. + unsafe { libc::memalign(host_page_size() as usize, buff_len as usize) }; + if bounce_buffer.is_null() { + bail!("Failed to alloc memory for misaligned read/write."); + } + + let res = match self.handle_misaligned_rw(bounce_buffer, buff_len) { + Ok(()) => self.nbytes as i32, + Err(e) => { + error!("{:?}", e); + -1 + } + }; + + // SAFETY: the memory is allocated by us and will not be used anymore. + unsafe { libc::free(bounce_buffer) }; + Ok(res) + } + + pub fn handle_misaligned_rw( + &mut self, + bounce_buffer: *mut c_void, + buffer_len: u64, + ) -> Result<()> { + let offset_align = round_down(self.offset as u64, self.req_align as u64) + .with_context(|| "Failed to round down request offset.")?; + let high = self.offset as u64 + self.nbytes; + let high_align = round_up(high, self.req_align as u64) + .with_context(|| "Failed to round up request high edge.")?; + + match self.opcode { + OpCode::Preadv => { + let mut offset = offset_align; + let mut iovecs = &mut self.iovec[..]; + loop { + // Step1: Read file to bounce buffer. + let nbytes = cmp::min(high_align - offset, buffer_len); + let len = raw_read( + self.file_fd, + bounce_buffer as u64, + nbytes as usize, + offset as usize, + ); + if len < 0 { + bail!("Failed to do raw read for misaligned read."); + } + + let real_offset = cmp::max(offset, self.offset as u64); + let real_high = cmp::min(offset + nbytes, high); + let real_nbytes = real_high - real_offset; + if (len as u64) < real_high - offset { + bail!( + "misaligned read len {} less than the nbytes {}", + len, + real_high - offset + ); + } + // SAFETY: the memory is allocated by us. + let src = unsafe { + std::slice::from_raw_parts( + (bounce_buffer as u64 + real_offset - offset) as *const u8, + real_nbytes as usize, + ) + }; + + // Step2: Copy bounce buffer to iovec. + iov_from_buf_direct(iovecs, src).and_then(|v| { + if v == real_nbytes as usize { + Ok(()) + } else { + Err(anyhow!("Failed to copy iovs to buff for misaligned read")) + } + })?; + + // Step3: Adjust offset and iovec for next loop. + offset += nbytes; + if offset >= high_align { + break; + } + iovecs = iov_discard_front_direct(iovecs, real_nbytes) + .with_context(|| "Failed to adjust iovec for misaligned read")?; + } + Ok(()) + } + OpCode::Pwritev => { + // Load the head from file before fill iovec to buffer. + let mut head_loaded = false; + if self.offset as u64 > offset_align { + let len = raw_read( + self.file_fd, + bounce_buffer as u64, + self.req_align as usize, + offset_align as usize, + ); + if len < 0 || len as u32 != self.req_align { + bail!("Failed to load head for misaligned write."); + } + head_loaded = true; + } + // Is head and tail in the same alignment section? + let same_section = (offset_align + self.req_align as u64) >= high; + let need_tail = !(same_section && head_loaded) && (high_align > high); + + let mut offset = offset_align; + let mut iovecs = &mut self.iovec[..]; + loop { + // Step1: Load iovec to bounce buffer. + let nbytes = cmp::min(high_align - offset, buffer_len); + + let real_offset = cmp::max(offset, self.offset as u64); + let real_high = cmp::min(offset + nbytes, high); + let real_nbytes = real_high - real_offset; + + if real_high == high && need_tail { + let len = raw_read( + self.file_fd, + bounce_buffer as u64 + nbytes - self.req_align as u64, + self.req_align as usize, + (offset + nbytes) as usize - self.req_align as usize, + ); + if len < 0 || len as u32 != self.req_align { + bail!("Failed to load tail for misaligned write."); + } + } + + // SAFETY: the memory is allocated by us. + let dst = unsafe { + std::slice::from_raw_parts_mut( + (bounce_buffer as u64 + real_offset - offset) as *mut u8, + real_nbytes as usize, + ) + }; + iov_to_buf_direct(iovecs, 0, dst).and_then(|v| { + if v == real_nbytes as usize { + Ok(()) + } else { + Err(anyhow!("Failed to copy iovs to buff for misaligned write")) + } + })?; + + // Step2: Write bounce buffer to file. + let len = raw_write( + self.file_fd, + bounce_buffer as u64, + nbytes as usize, + offset as usize, + ); + if len < 0 || len as u64 != nbytes { + bail!("Failed to do raw write for misaligned write."); + } + + // Step3: Adjuest offset and iovec for next loop. + offset += nbytes; + if offset >= high_align { + break; + } + iovecs = iov_discard_front_direct(iovecs, real_nbytes) + .with_context(|| "Failed to adjust iovec for misaligned write")?; + } + Ok(()) + } + _ => bail!("Failed to do misaligned rw: unknown cmd type"), + } + } } pub type AioCompleteFunc = fn(&AioCb, i64) -> Result<()>; @@ -273,62 +532,47 @@ impl Aio { } pub fn submit_request(&mut self, mut cb: AioCb) -> Result<()> { - if self.request_misaligned(&cb) { - let max_len = round_down(cb.nbytes + cb.req_align as u64 * 2, cb.req_align as u64) - .with_context(|| "Failed to round down request length.")?; - // Set upper limit of buffer length to avoid OOM. - let buff_len = cmp::min(max_len, MAX_LEN_BOUNCE_BUFF); - let bounce_buffer = - // SAFETY: We allocate aligned memory and free it later. Alignment is set to - // host page size to decrease the count of allocated pages. - unsafe { libc::memalign(host_page_size() as usize, buff_len as usize) }; - if bounce_buffer.is_null() { - error!("Failed to alloc memory for misaligned read/write."); - return (self.complete_func)(&cb, -1); - } + if self.ctx.is_none() { + return self.handle_sync_request(cb); + } + + if cb.is_misaligned() { + return self.submit_thread_pool_async(cb); + } + + cb.try_convert_to_write_zero(); - let res = match self.handle_misaligned_rw(&mut cb, bounce_buffer, buff_len) { - Ok(()) => 0, + if self.engine != AioEngine::Threads + && [OpCode::Preadv, OpCode::Pwritev, OpCode::Fdsync].contains(&cb.opcode) + { + return self.submit_async(cb); + } + + self.submit_thread_pool_async(cb) + } + + fn handle_sync_request(&mut self, mut cb: AioCb) -> Result<()> { + if cb.is_misaligned() { + let ret = match cb.handle_misaligned() { + Ok(ret) => ret, Err(e) => { error!("{:?}", e); -1 } }; - - // SAFETY: the memory is allocated by us and will not be used anymore. - unsafe { libc::free(bounce_buffer) }; - return (self.complete_func)(&cb, res); + return (self.complete_func)(&cb, ret as i64); } - if cb.opcode == OpCode::Pwritev - && cb.write_zeroes != WriteZeroesState::Off - && iovec_is_zero(&cb.iovec) - { - cb.opcode = OpCode::WriteZeroes; - if cb.write_zeroes == WriteZeroesState::Unmap && cb.discard { - cb.opcode = OpCode::WriteZeroesUnmap; - } - } + cb.try_convert_to_write_zero(); - match cb.opcode { - OpCode::Preadv | OpCode::Pwritev => { - if self.ctx.is_some() { - self.rw_async(cb) - } else { - self.rw_sync(cb) - } - } - OpCode::Fdsync => { - if self.ctx.is_some() { - self.flush_async(cb) - } else { - self.flush_sync(cb) - } - } - OpCode::Discard => self.discard_sync(cb), - OpCode::WriteZeroes | OpCode::WriteZeroesUnmap => self.write_zeroes_sync(cb), - OpCode::Noop => Err(anyhow!("Aio opcode is not specified.")), - } + let ret = match cb.opcode { + OpCode::Preadv | OpCode::Pwritev => cb.rw_sync(), + OpCode::Fdsync => cb.flush_sync(), + OpCode::Discard => cb.discard_sync(), + OpCode::WriteZeroes | OpCode::WriteZeroesUnmap => cb.write_zeroes_sync(), + OpCode::Noop => return Err(anyhow!("Aio opcode is not specified.")), + }; + (self.complete_func)(&cb, ret as i64) } pub fn flush_request(&mut self) -> Result<()> { @@ -424,237 +668,30 @@ impl Aio { Ok(()) } - fn rw_async(&mut self, cb: AioCb) -> Result<()> { + fn submit_thread_pool_async(&mut self, cb: AioCb) -> Result<()> { let mut node = Box::new(Node::new(cb)); node.value.user_data = (&mut (*node) as *mut CbNode) as u64; - self.aio_in_queue.add_head(node); + self.ctx + .as_mut() + .unwrap() + .submit_threads_pool(&[&node.value as *const AioCb])?; + self.aio_in_flight.add_head(node); self.incomplete_cnt.fetch_add(1, Ordering::SeqCst); - if self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { - self.process_list()?; - } - Ok(()) } - fn rw_sync(&mut self, cb: AioCb) -> Result<()> { - let mut ret = match cb.opcode { - OpCode::Preadv => raw_readv(cb.file_fd, &cb.iovec, cb.offset), - OpCode::Pwritev => raw_writev(cb.file_fd, &cb.iovec, cb.offset), - _ => -1, - }; - if ret < 0 { - error!("Failed to do sync read/write."); - } else if ret as u64 != cb.nbytes { - error!("Incomplete sync read/write."); - ret = -1; - } - (self.complete_func)(&cb, ret) - } - - fn request_misaligned(&self, cb: &AioCb) -> bool { - if cb.direct && (cb.opcode == OpCode::Preadv || cb.opcode == OpCode::Pwritev) { - if (cb.offset as u64) & (cb.req_align as u64 - 1) != 0 { - return true; - } - for iov in cb.iovec.iter() { - if iov.iov_base & (cb.buf_align as u64 - 1) != 0 { - return true; - } - if iov.iov_len & (cb.req_align as u64 - 1) != 0 { - return true; - } - } - } - false - } - - fn handle_misaligned_rw( - &mut self, - cb: &mut AioCb, - bounce_buffer: *mut c_void, - buffer_len: u64, - ) -> Result<()> { - let offset_align = round_down(cb.offset as u64, cb.req_align as u64) - .with_context(|| "Failed to round down request offset.")?; - let high = cb.offset as u64 + cb.nbytes; - let high_align = round_up(high, cb.req_align as u64) - .with_context(|| "Failed to round up request high edge.")?; - - match cb.opcode { - OpCode::Preadv => { - let mut offset = offset_align; - let mut iovecs = &mut cb.iovec[..]; - loop { - // Step1: Read file to bounce buffer. - let nbytes = cmp::min(high_align - offset, buffer_len); - let len = raw_read( - cb.file_fd, - bounce_buffer as u64, - nbytes as usize, - offset as usize, - ); - if len < 0 { - bail!("Failed to do raw read for misaligned read."); - } - - let real_offset = cmp::max(offset, cb.offset as u64); - let real_high = cmp::min(offset + nbytes, high); - let real_nbytes = real_high - real_offset; - if (len as u64) < real_high - offset { - bail!( - "misaligned read len {} less than the nbytes {}", - len, - real_high - offset - ); - } - // SAFETY: the memory is allocated by us. - let src = unsafe { - std::slice::from_raw_parts( - (bounce_buffer as u64 + real_offset - offset) as *const u8, - real_nbytes as usize, - ) - }; - - // Step2: Copy bounce buffer to iovec. - iov_from_buf_direct(iovecs, src).and_then(|v| { - if v == real_nbytes as usize { - Ok(()) - } else { - Err(anyhow!("Failed to copy iovs to buff for misaligned read")) - } - })?; - - // Step3: Adjust offset and iovec for next loop. - offset += nbytes; - if offset >= high_align { - break; - } - iovecs = iov_discard_front_direct(iovecs, real_nbytes) - .with_context(|| "Failed to adjust iovec for misaligned read")?; - } - Ok(()) - } - OpCode::Pwritev => { - // Load the head from file before fill iovec to buffer. - let mut head_loaded = false; - if cb.offset as u64 > offset_align { - let len = raw_read( - cb.file_fd, - bounce_buffer as u64, - cb.req_align as usize, - offset_align as usize, - ); - if len < 0 || len as u32 != cb.req_align { - bail!("Failed to load head for misaligned write."); - } - head_loaded = true; - } - // Is head and tail in the same alignment section? - let same_section = (offset_align + cb.req_align as u64) >= high; - let need_tail = !(same_section && head_loaded) && (high_align > high); - - let mut offset = offset_align; - let mut iovecs = &mut cb.iovec[..]; - loop { - // Step1: Load iovec to bounce buffer. - let nbytes = cmp::min(high_align - offset, buffer_len); - - let real_offset = cmp::max(offset, cb.offset as u64); - let real_high = cmp::min(offset + nbytes, high); - let real_nbytes = real_high - real_offset; - - if real_high == high && need_tail { - let len = raw_read( - cb.file_fd, - bounce_buffer as u64 + nbytes - cb.req_align as u64, - cb.req_align as usize, - (offset + nbytes) as usize - cb.req_align as usize, - ); - if len < 0 || len as u32 != cb.req_align { - bail!("Failed to load tail for misaligned write."); - } - } - - // SAFETY: the memory is allocated by us. - let dst = unsafe { - std::slice::from_raw_parts_mut( - (bounce_buffer as u64 + real_offset - offset) as *mut u8, - real_nbytes as usize, - ) - }; - iov_to_buf_direct(iovecs, 0, dst).and_then(|v| { - if v == real_nbytes as usize { - Ok(()) - } else { - Err(anyhow!("Failed to copy iovs to buff for misaligned write")) - } - })?; - - // Step2: Write bounce buffer to file. - let len = raw_write( - cb.file_fd, - bounce_buffer as u64, - nbytes as usize, - offset as usize, - ); - if len < 0 || len as u64 != nbytes { - bail!("Failed to do raw write for misaligned write."); - } - - // Step3: Adjuest offset and iovec for next loop. - offset += nbytes; - if offset >= high_align { - break; - } - iovecs = iov_discard_front_direct(iovecs, real_nbytes) - .with_context(|| "Failed to adjust iovec for misaligned write")?; - } - Ok(()) - } - _ => bail!("Failed to do misaligned rw: unknown cmd type"), - } - } - - fn flush_async(&mut self, cb: AioCb) -> Result<()> { - self.rw_async(cb) - } - - fn flush_sync(&mut self, cb: AioCb) -> Result<()> { - let ret = raw_datasync(cb.file_fd); - if ret < 0 { - error!("Failed to do sync flush."); - } - (self.complete_func)(&cb, ret) - } - - fn discard_sync(&mut self, cb: AioCb) -> Result<()> { - let ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); - if ret < 0 && ret != -libc::ENOTSUP { - error!("Failed to do sync discard."); - } - (self.complete_func)(&cb, ret as i64) - } + fn submit_async(&mut self, cb: AioCb) -> Result<()> { + let mut node = Box::new(Node::new(cb)); + node.value.user_data = (&mut (*node) as *mut CbNode) as u64; - fn write_zeroes_sync(&mut self, mut cb: AioCb) -> Result<()> { - let mut ret; - if cb.opcode == OpCode::WriteZeroesUnmap { - ret = raw_discard(cb.file_fd, cb.offset, cb.nbytes); - if ret == 0 { - return (self.complete_func)(&cb, ret as i64); - } - } - ret = raw_write_zeroes(cb.file_fd, cb.offset, cb.nbytes); - if ret == -libc::ENOTSUP && !cb.iovec.is_empty() { - cb.opcode = OpCode::Pwritev; - cb.write_zeroes = WriteZeroesState::Off; - return self.submit_request(cb); + self.aio_in_queue.add_head(node); + self.incomplete_cnt.fetch_add(1, Ordering::SeqCst); + if self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { + self.process_list()?; } - if ret < 0 { - error!("Failed to do sync write zeroes."); - } - (self.complete_func)(&cb, ret as i64) + Ok(()) } } -- Gitee From 76aba9584078b667e89504293b5c80b4b9b8fd05 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Jan 2024 17:56:11 +0800 Subject: [PATCH 1551/2187] Aio: modify the aio of io_uring and libaio Modify the aio of io_uring and libaio, for the request of discard and write_zero, it will be submited to thread pool. Signed-off-by: Xiao Ye --- util/src/aio/libaio.rs | 13 ++++++++----- util/src/aio/threads.rs | 4 ++-- util/src/aio/uring.rs | 13 ++++++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index d4af4df14..2961d2a26 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -70,7 +70,7 @@ pub(crate) enum IoContext {} pub(crate) struct LibaioContext { ctx: *mut IoContext, - _threads_aio_ctx: ThreadsAioContext, + threads_aio_ctx: ThreadsAioContext, resfd: RawFd, events: Vec, } @@ -119,7 +119,7 @@ impl LibaioContext { let ctx = Self::probe(max_size)?; Ok(LibaioContext { ctx, - _threads_aio_ctx: threads_aio_ctx, + threads_aio_ctx, resfd: eventfd.as_raw_fd(), events: Vec::with_capacity(max_size as usize), }) @@ -174,11 +174,15 @@ impl AioContext for LibaioContext { Ok(0) } - fn submit_threads_pool(&mut self, _iocbp: &[*const AioCb]) -> Result { - todo!() + fn submit_threads_pool(&mut self, iocbp: &[*const AioCb]) -> Result { + self.threads_aio_ctx.submit(iocbp) } fn get_events(&mut self) -> &[AioEvent] { + let mut locked_list = self.threads_aio_ctx.complete_list.lock().unwrap(); + self.events = locked_list.drain(0..).collect(); + drop(locked_list); + let ring = self.ctx as *mut AioRing; // SAFETY: self.ctx is generated by SYS_io_setup. let head = unsafe { (*ring).head }; @@ -197,7 +201,6 @@ impl AioContext for LibaioContext { // Avoid speculatively loading ring.io_events before observing tail. fence(Ordering::Acquire); - self.events.clear(); for i in head..(head + nr) { let io_event = &io_events[(i % ring_nr) as usize]; self.events.push(AioEvent { diff --git a/util/src/aio/threads.rs b/util/src/aio/threads.rs index 16e73fc7d..39cd44b1d 100644 --- a/util/src/aio/threads.rs +++ b/util/src/aio/threads.rs @@ -20,7 +20,7 @@ use crate::thread_pool::ThreadPool; pub struct ThreadsAioContext { _pool: Arc, _events: Vec, - _complete_list: Arc>>, + pub complete_list: Arc>>, _notify_event: Arc>, } @@ -28,7 +28,7 @@ impl ThreadsAioContext { pub fn new(max_size: u32, eventfd: &EventFd, thread_pool: Arc) -> Self { Self { _pool: thread_pool, - _complete_list: Arc::new(Mutex::new(Vec::new())), + complete_list: Arc::new(Mutex::new(Vec::new())), _notify_event: Arc::new(Mutex::new((*eventfd).try_clone().unwrap())), _events: Vec::with_capacity(max_size as usize), } diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index f67715453..8f95832e9 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -23,7 +23,7 @@ use super::{AioCb, AioContext, AioEvent, OpCode, Result}; /// The io-uring context. pub(crate) struct IoUringContext { ring: IoUring, - _threads_aio_ctx: ThreadsAioContext, + threads_aio_ctx: ThreadsAioContext, events: Vec, } @@ -50,7 +50,7 @@ impl IoUringContext { let events = Vec::with_capacity(entries as usize); Ok(IoUringContext { ring, - _threads_aio_ctx: threads_aio_ctx, + threads_aio_ctx, events, }) } @@ -96,13 +96,16 @@ impl AioContext for IoUringContext { self.ring.submit().with_context(|| "Failed to submit sqe") } - fn submit_threads_pool(&mut self, _iocbp: &[*const AioCb]) -> Result { - todo!() + fn submit_threads_pool(&mut self, iocbp: &[*const AioCb]) -> Result { + self.threads_aio_ctx.submit(iocbp) } fn get_events(&mut self) -> &[AioEvent] { + let mut locked_list = self.threads_aio_ctx.complete_list.lock().unwrap(); + self.events = locked_list.drain(0..).collect(); + drop(locked_list); + let queue = self.ring.completion(); - self.events.clear(); for cqe in queue { self.events.push(AioEvent { user_data: cqe.user_data(), -- Gitee From 9b4b47b7b1246e7bbb9dbf40f2c132f5129fa873 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Jan 2024 20:13:25 +0800 Subject: [PATCH 1552/2187] Threads: add the operation of threads Add some basic operation fot the aio of threads. Signed-off-by: Xiao Ye --- util/src/aio/threads.rs | 154 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 13 deletions(-) diff --git a/util/src/aio/threads.rs b/util/src/aio/threads.rs index 39cd44b1d..64d605a44 100644 --- a/util/src/aio/threads.rs +++ b/util/src/aio/threads.rs @@ -12,39 +12,167 @@ use std::sync::{Arc, Mutex}; +use log::error; use vmm_sys_util::eventfd::EventFd; -use crate::aio::{AioCb, AioContext, AioEvent, Result}; -use crate::thread_pool::ThreadPool; +use crate::aio::{AioCb, AioContext, AioEvent, Iovec, OpCode, Result, WriteZeroesState}; +use crate::thread_pool::{TaskOperation, ThreadPool}; + +pub struct ThreadsTasks { + io_data: IoData, + pub complete_lists: Arc>>, + notify_event: Arc>, +} + +pub struct IoData { + pub direct: bool, + pub req_align: u32, + pub buf_align: u32, + pub discard: bool, + pub write_zeroes: WriteZeroesState, + pub file_fd: i32, + pub opcode: OpCode, + pub iovec: Vec, + pub offset: usize, + pub nbytes: u64, + pub user_data: u64, +} + +impl IoData { + fn package_aiocb(&self) -> AioCb<()> { + AioCb { + direct: self.direct, + req_align: self.req_align, + buf_align: self.buf_align, + discard: self.discard, + write_zeroes: self.write_zeroes, + file_fd: self.file_fd, + opcode: self.opcode, + iovec: self.iovec.clone(), + offset: self.offset, + nbytes: self.nbytes, + user_data: self.user_data, + iocompletecb: (), + combine_req: None, + } + } +} + +impl ThreadsTasks { + fn complete_func(&self, task: &IoData, res: i32) { + let aio_event = AioEvent { + user_data: task.user_data, + status: 0, + res: res as i64, + }; + self.complete_lists.lock().unwrap().push(aio_event); + self.notify_event + .lock() + .unwrap() + .write(1) + .unwrap_or_else(|e| error!("{:?}", e)); + } +} + +impl TaskOperation for ThreadsTasks { + fn run(&mut self) { + let mut cb = self.io_data.package_aiocb(); + + // Direct io needs to be aligned before io operation. + if cb.is_misaligned() { + let ret = match cb.handle_misaligned() { + Ok(ret) => ret, + Err(e) => { + error!("{:?}", e); + -1 + } + }; + self.complete_func(&self.io_data, ret); + return; + } + + let mut ret = match cb.opcode { + OpCode::Preadv | OpCode::Pwritev => cb.rw_sync(), + OpCode::Discard => cb.discard_sync(), + OpCode::WriteZeroes | OpCode::WriteZeroesUnmap => cb.write_zeroes_sync(), + OpCode::Fdsync => cb.flush_sync(), + _ => -1, + }; + + if [ + OpCode::Discard, + OpCode::WriteZeroes, + OpCode::WriteZeroesUnmap, + OpCode::Fdsync, + ] + .contains(&cb.opcode) + && ret == 0 + { + ret = self.io_data.nbytes as i32; + } + + self.complete_func(&self.io_data, ret as i32); + } +} pub struct ThreadsAioContext { - _pool: Arc, - _events: Vec, + pool: Arc, + events: Vec, pub complete_list: Arc>>, - _notify_event: Arc>, + notify_event: Arc>, } impl ThreadsAioContext { pub fn new(max_size: u32, eventfd: &EventFd, thread_pool: Arc) -> Self { Self { - _pool: thread_pool, + pool: thread_pool, complete_list: Arc::new(Mutex::new(Vec::new())), - _notify_event: Arc::new(Mutex::new((*eventfd).try_clone().unwrap())), - _events: Vec::with_capacity(max_size as usize), + notify_event: Arc::new(Mutex::new((*eventfd).try_clone().unwrap())), + events: Vec::with_capacity(max_size as usize), } } } impl AioContext for ThreadsAioContext { - fn submit(&mut self, _iocbp: &[*const AioCb]) -> Result { - todo!() + fn submit(&mut self, iocbp: &[*const AioCb]) -> Result { + for iocb in iocbp { + // SAFETY: iocb is valid until request is finished. + let cb = unsafe { &*(*iocb) }; + + let io_data = IoData { + opcode: cb.opcode, + file_fd: cb.file_fd, + offset: cb.offset, + nbytes: cb.nbytes, + iovec: cb.iovec.clone(), + direct: cb.direct, + buf_align: cb.buf_align, + req_align: cb.req_align, + discard: cb.discard, + write_zeroes: cb.write_zeroes, + user_data: cb.user_data, + }; + let task = ThreadsTasks { + io_data, + complete_lists: self.complete_list.clone(), + notify_event: self.notify_event.clone(), + }; + + ThreadPool::submit_task(self.pool.clone(), Box::new(task))?; + } + + Ok(iocbp.len()) } - fn submit_threads_pool(&mut self, _iocbp: &[*const AioCb]) -> Result { - todo!() + fn submit_threads_pool(&mut self, iocbp: &[*const AioCb]) -> Result { + self.submit(iocbp) } fn get_events(&mut self) -> &[AioEvent] { - todo!() + let mut locked_list = self.complete_list.lock().unwrap(); + self.events = locked_list.drain(0..).collect(); + drop(locked_list); + + &self.events } } -- Gitee From 7bfa1f12e701c8a821a861b5abe58763fe02819b Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Jan 2024 21:36:47 +0800 Subject: [PATCH 1553/2187] Block: add testcase Add the testcase of different aio type. Signed-off-by: Xiao Ye --- tests/mod_test/tests/block_test.rs | 23 ++++++++++++++++------- util/src/aio/mod.rs | 11 +++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 7e1e4b46b..e7a633aa4 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -1029,23 +1029,32 @@ fn blk_iops() { } } -/// Block device sends I/O request, configured as 'aio=native'. +/// Block device sends I/O request, configured as 'aio=[off|threads|io_uring|native]'. /// TestStep: -/// 1. Init block device, configured as 'aio=native'. +/// 1. Init block device, configured with different aio type. /// 2. Do the I/O request. /// 3. Destroy device. /// Expect: /// 1/2/3: success. #[test] -fn blk_aio_native() { - for image_type in ImageType::IMAGE_TYPE { +fn blk_with_different_aio() { + const BLOCK_DRIVER_CFG: [(ImageType, &str, AioEngine); 6] = [ + (ImageType::Raw, "off", AioEngine::Off), + (ImageType::Qcow2, "off", AioEngine::Off), + (ImageType::Raw, "off", AioEngine::Threads), + (ImageType::Qcow2, "off", AioEngine::Threads), + (ImageType::Raw, "on", AioEngine::Native), + (ImageType::Raw, "on", AioEngine::IoUring), + ]; + + for (image_type, direct, aio_engine) in BLOCK_DRIVER_CFG { println!("Image type: {:?}", image_type); let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1, &image_type)); let device_args = Rc::new(String::from("")); - let drive_args = if aio_probe(AioEngine::Native).is_ok() { - Rc::new(String::from(",direct=on,aio=native")) + let drive_args = if aio_probe(aio_engine).is_ok() { + Rc::new(format!(",direct={},aio={}", direct, aio_engine.to_string())) } else { - Rc::new(String::from(",direct=false")) + continue; }; let other_args = Rc::new(String::from("")); let (blk, test_state, alloc) = create_blk( diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 35c3db081..e571dbbb0 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -79,6 +79,17 @@ impl FromStr for AioEngine { } } +impl ToString for AioEngine { + fn to_string(&self) -> String { + match *self { + AioEngine::Off => "off".to_string(), + AioEngine::Native => "native".to_string(), + AioEngine::IoUring => "io_uring".to_string(), + AioEngine::Threads => "threads".to_string(), + } + } +} + #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum WriteZeroesState { Off, -- Gitee From 3f6da71a5d1ed93f3801bcad3a1ed69bde1df52a Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Jan 2024 12:11:34 +0800 Subject: [PATCH 1554/2187] Aio: aio adapt to scsi Fix the configuration issue with aio=native error in scsi. This error was introduced in 76aba9584078b667e89504293b5c80b4b9b8fd05. Signed-off-by: Xiao Ye --- devices/src/scsi/disk.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 11224b92c..6c4652ed8 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -19,7 +19,8 @@ use crate::ScsiBus::{aio_complete_cb, ScsiBus, ScsiCompleteCb}; use crate::{Device, DeviceBase}; use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; -use util::aio::{Aio, WriteZeroesState}; +use machine_manager::event_loop::EventLoop; +use util::aio::{Aio, AioEngine, WriteZeroesState}; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -176,7 +177,11 @@ impl ScsiDevice { self.buf_align = alignments.1; let drive_id = VmConfig::get_drive_id(&drive_files, &self.config.path_on_host)?; - let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type, None)?; + let mut thread_pool = None; + if self.config.aio_type != AioEngine::Off { + thread_pool = Some(EventLoop::get_ctx(None).unwrap().thread_pool.clone()); + } + let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type, thread_pool)?; let conf = BlockProperty { id: drive_id, format: self.config.format, -- Gitee From 47475b2161b72f639566d8b8eb297b249b88d7a5 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Jan 2024 21:39:36 +0800 Subject: [PATCH 1555/2187] Threadpool: optimize the unit test of thread pool Optimize the unit test of thread pool. It is necessary to ensure that request list is empty before exiting the thread pool. Signed-off-by: Xiao Ye --- util/src/thread_pool.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/util/src/thread_pool.rs b/util/src/thread_pool.rs index df04c4b70..ecfac7ddb 100644 --- a/util/src/thread_pool.rs +++ b/util/src/thread_pool.rs @@ -217,7 +217,7 @@ mod test { impl TaskOperation for PoolTask { fn run(&mut self) { - std::thread::sleep(std::time::Duration::from_millis(50)); + std::thread::sleep(std::time::Duration::from_millis(500)); self.count.fetch_add(1, Ordering::SeqCst); } } @@ -234,12 +234,20 @@ mod test { assert!(ThreadPool::submit_task(pool.clone(), task).is_ok()); } - thread::sleep(time::Duration::from_millis(10)); + // Waiting for creating. + while pool.pool_state.lock().unwrap().req_lists.len != 0 { + thread::sleep(time::Duration::from_millis(10)); + + let now = time::SystemTime::now(); + let duration = now.duration_since(begin).unwrap().as_millis(); + assert!(duration < 500 * 10); + } + assert!(pool.cancel().is_ok()); let end = time::SystemTime::now(); let duration = end.duration_since(begin).unwrap().as_millis(); // All tasks are processed in parallel. - assert!(duration < 50 * 10); + assert!(duration < 500 * 10); // All the task has been finished. assert_eq!(count.load(Ordering::SeqCst), 10); } -- Gitee From 6f77366a3ac5b90069c2e7ee71d4087b314bb02d Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 23 Jan 2024 11:27:11 +0800 Subject: [PATCH 1556/2187] trace: add trace point for device pflash pflash trace point: pflash_device_id pflash_device_info pflash_io_read pflash_io_write pflash_manufacturer_id pflash_mode_read_array pflash_read_data pflash_read_status pflash_read_unknown_state pflash_write pflash_write_block pflash_write_block_erase pflash_write_data Signed-off-by: yezengruan --- devices/src/legacy/pflash.rs | 87 ++++++++++++++++++++-------- trace/trace_event/device_legacy.toml | 78 +++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 25 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 131678a08..4a7f1d3b7 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -15,7 +15,7 @@ use std::io::Write; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; -use log::{debug, error, warn}; +use log::{error, warn}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; @@ -227,6 +227,14 @@ impl PFlash { } fn set_read_array_mode(&mut self, is_illegal_cmd: bool) -> Result<()> { + if is_illegal_cmd { + warn!( + "Unimplemented PFlash cmd sequence (write cycle: 0x{:X}, cmd: 0x{:X})", + self.write_cycle, self.cmd + ); + } + + trace::pflash_mode_read_array(); self.rom .as_ref() .unwrap() @@ -235,13 +243,6 @@ impl PFlash { self.write_cycle = 0; self.cmd = 0x00; - if is_illegal_cmd { - warn!( - "Unimplemented PFlash cmd sequence (write cycle: 0x{:X}, cmd: 0x{:X})", - self.write_cycle, self.cmd - ); - } - Ok(()) } @@ -252,10 +253,16 @@ impl PFlash { // Mask off upper bits, the rest (ident[2] and ident[3]) is not emulated. let mut resp: u32 = match index & 0xFF { - 0 => self.ident[0], - 1 => self.ident[1], + 0 => { + trace::pflash_manufacturer_id(self.ident[0]); + self.ident[0] + } + 1 => { + trace::pflash_device_id(self.ident[1]); + self.ident[1] + } _ => { - debug!("Device ID 2 and 3 are not supported"); + trace::pflash_device_info(index); return Ok(0); } }; @@ -362,11 +369,18 @@ impl PFlash { data.as_mut() .write_all(src) .with_context(|| "Failed to read data from PFlash Rom")?; + trace::pflash_read_data(offset, data.len(), &data[..std::cmp::min(4, data.len())]); Ok(()) } fn write_data(&mut self, data: &[u8], offset: u64) -> Result<()> { + trace::pflash_write_data( + offset, + data.len(), + &data[..std::cmp::min(4, data.len())], + self.counter, + ); // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); if offset + data.len() as u64 > mr.size() { @@ -390,6 +404,7 @@ impl PFlash { match cmd { // cmd 0xf0 is for AMD PFlash. 0x00 | 0xf0 | 0xff => { + trace::pflash_write("read array mode".to_string(), cmd); if let Err(e) = self.set_read_array_mode(false) { error!( "Failed to set read array mode, write cycle 0, cmd 0x{:x}, error is {:?}", @@ -400,10 +415,11 @@ impl PFlash { return true; } 0x10 | 0x40 => { - debug!("PFlash write: Single Byte Program"); + trace::pflash_write("single byte program (0)".to_string(), cmd); } 0x20 => { let offset_mask = offset & !(self.block_len as u64 - 1); + trace::pflash_write_block_erase(offset, self.block_len); if !self.read_only { let all_one = vec![0xff_u8; self.block_len as usize]; if let Err(e) = self.write_data(all_one.as_slice(), offset_mask) { @@ -421,6 +437,7 @@ impl PFlash { self.status |= 0x80; } 0x50 => { + trace::pflash_write("clear status bits".to_string(), cmd); self.status = 0x0; if let Err(e) = self.set_read_array_mode(false) { error!( @@ -432,17 +449,23 @@ impl PFlash { return true; } 0x60 => { - debug!("PFlash write: Block unlock"); + trace::pflash_write("block unlock".to_string(), cmd); + } + 0x70 => { + trace::pflash_write("read status register".to_string(), cmd); + self.cmd = cmd; + return true; } - 0x70 | 0x90 => { - // 0x70: Status Register, 0x90: Read Device ID. + 0x90 => { + trace::pflash_write("read device information".to_string(), cmd); self.cmd = cmd; return true; } 0x98 => { - debug!("PFlash write: CFI query"); + trace::pflash_write("CFI query".to_string(), cmd); } 0xe8 => { + trace::pflash_write("write to buffer".to_string(), cmd); self.status |= 0x80; } _ => { @@ -471,6 +494,7 @@ impl PFlash { ) -> bool { match self.cmd { 0x10 | 0x40 => { + trace::pflash_write("single byte program (1)".to_string(), self.cmd); if !self.read_only { if let Err(e) = self.write_data(data, offset) { error!("Failed to write to PFlash device: {:?}.", e); @@ -522,6 +546,7 @@ impl PFlash { error!("Failed to extract bits from u32 value"); return false; }; + trace::pflash_write_block(value); self.write_cycle = self.write_cycle.wrapping_add(1); self.counter = value; } @@ -539,6 +564,7 @@ impl PFlash { } return true; } else { + trace::pflash_write("unknown (un)blocking command".to_string(), cmd); if let Err(e) = self.set_read_array_mode(true) { error!("Failed to set read array mode, write cycle 1, cmd 0x{:x}, error is {:?}", self.cmd, @@ -560,6 +586,7 @@ impl PFlash { } return true; } + trace::pflash_write("leaving query mode".to_string(), cmd); } _ => { if let Err(e) = self.set_read_array_mode(true) { @@ -588,6 +615,7 @@ impl PFlash { self.status |= 0x80; if self.counter == 0 { let mask: u64 = !(self.write_blk_size as u64 - 1); + trace::pflash_write("block write finished".to_string(), self.cmd); self.write_cycle = self.write_cycle.wrapping_add(1); if !self.read_only { if let Err(e) = self.update_content(offset & mask, self.write_blk_size) { @@ -699,6 +727,7 @@ impl SysBusDevOps for PFlash { } else if self.device_width == 0 && data_len > 2 { ret |= (self.status as u32) << 16; } + trace::pflash_read_status(ret); } 0x90 => { if self.device_width == 0 { @@ -711,9 +740,18 @@ impl SysBusDevOps for PFlash { } match index { - 0 => ret = self.ident[0] << 8 | self.ident[1], - 1 => ret = self.ident[2] << 8 | self.ident[3], - _ => ret = 0, + 0 => { + ret = self.ident[0] << 8 | self.ident[1]; + trace::pflash_manufacturer_id(ret); + } + 1 => { + ret = self.ident[2] << 8 | self.ident[3]; + trace::pflash_device_id(ret); + } + _ => { + ret = 0; + trace::pflash_device_info(index); + } } } else { // If a read request is larger than bank_width of PFlash device, @@ -786,7 +824,7 @@ impl SysBusDevOps for PFlash { } _ => { // This should never happen : reset state & treat it as a read. - error!("PFlash read: unknown command state 0x{:X}", self.cmd); + trace::pflash_read_unknown_state(self.cmd); self.write_cycle = 0; self.cmd = 0x00; if let Err(e) = self.read_data(data, offset) { @@ -795,6 +833,7 @@ impl SysBusDevOps for PFlash { } } + trace::pflash_io_read(offset, data_len, ret, self.cmd, self.write_cycle); write_data_u32(data, ret) } @@ -805,6 +844,7 @@ impl SysBusDevOps for PFlash { } let cmd: u8 = data[0]; let data_len: u8 = data.len() as u8; + trace::pflash_io_write(offset, data_len, value, self.write_cycle); if self.write_cycle == 0 && self @@ -821,17 +861,14 @@ impl SysBusDevOps for PFlash { // - PFlash write // * cmd 0x10 | 0x40 represents single Byte Program. // * cmd 0xe8 represents write to buffer. - // - cmd 0x20 | 0x28 represents PFlash erase (write all 1). + // * cmd 0x20 | 0x28 represents PFlash erase (write all 1). match self.write_cycle { 0 => self.handle_write_first_pass(cmd, offset), 1 => self.handle_write_second_pass(cmd, offset, data, data_len, value), 2 => self.handle_write_third_pass(offset, data), 3 => self.handle_write_fourth_pass(cmd), _ => { - error!( - "PFlash write: invalid write state: write cycle {}", - self.write_cycle - ); + trace::pflash_write("invalid write state".to_string(), cmd); if let Err(e) = self.set_read_array_mode(false) { error!("Failed to set PFlash to read array mode, error is {:?}", e); } diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml index 848b04c18..69d1fc616 100644 --- a/trace/trace_event/device_legacy.toml +++ b/trace/trace_event/device_legacy.toml @@ -99,3 +99,81 @@ name = "serial_receive" args = "len: usize" message = "data length {}" enabled = true + +[[events]] +name = "pflash_device_id" +args = "id: u32" +message = "read device ID: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_device_info" +args = "offset: u64" +message = "read device information offset: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_io_read" +args = "offset: u64, size: u32, value: u32, cmd: u8, wcycle: u32" +message = "offset: 0x{:X}, size: {}, value: 0x{:X}, cmd: 0x{:X}, wcycle: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_io_write" +args = "offset: u64, size: u8, value: u32, wcycle: u32" +message = "offset: 0x{:X}, size: {}, value: 0x{:X}, wcycle: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_manufacturer_id" +args = "id: u32" +message = "read manufacturer ID: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_mode_read_array" +args = "" +message = "read array mode" +enabled = true + +[[events]] +name = "pflash_read_data" +args = "offset: u64, len: usize, value: &[u8]" +message = "data offset: 0x{:X}, length: {}, value: 0x{:X?}" +enabled = true + +[[events]] +name = "pflash_read_status" +args = "status: u32" +message = "status: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_read_unknown_state" +args = "cmd: u8" +message = "unknown command state: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_write" +args = "str: String, cmd: u8" +message = "{}, cmd: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_write_block" +args = "value: u32" +message = "block write: bytes: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_write_block_erase" +args = "offset: u64, len: u32" +message = "block erase offset: 0x{:X}, bytes: 0x{:X}" +enabled = true + +[[events]] +name = "pflash_write_data" +args = "offset: u64, size: usize, value: &[u8], counter: u32" +message = "data offset: 0x{:X}, size: {}, value: 0x{:X?}, counter: 0x{:X}" +enabled = true -- Gitee From e75b880b9a897a8097f420483aad75d00102272b Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sat, 27 Jan 2024 14:57:50 +0800 Subject: [PATCH 1557/2187] syscall: bugfix cargo clippy -- -D warnings error The usb-camera is controlled by the feature. If all features are not enabled for verification, an error is reported at the syscall. Signed-off-by: Mingwang Li --- machine/src/standard_common/syscall.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index e7b985226..ef40b97ff 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -246,7 +246,6 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32); - let bpf_rule = arch_ioctl_allow_list(bpf_rule); #[cfg(feature = "usb_camera_v4l2")] let bpf_rule = bpf_rule @@ -264,7 +263,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMESIZES() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VIDIOC_ENUM_FRAMEINTERVALS() as u32); - bpf_rule + arch_ioctl_allow_list(bpf_rule) } fn madvise_rule() -> BpfRule { -- Gitee From bec50fcbad5d5dc456c2906d2bad99ed34aa5512 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 30 Jan 2024 15:05:11 +0800 Subject: [PATCH 1558/2187] fdt: bugfix fdt table contains an extra pflash node Two pflash devices are created, but only one pflash node is required in the fdt table. Thereforce, the second pflash device needs to be skipped when create fdt table. In addition, delete outdated comments. Signed-off-by: Mingwang Li --- machine/src/aarch64/fdt.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index 57f5a642e..e7a5937b1 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -266,28 +266,29 @@ impl CompileFDTHelper for MachineBase { fdt.set_property_string("method", "hvc")?; fdt.end_node(psci_node_dep)?; + let mut pflash_cnt = 0; for dev in self.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); match locked_dev.sysbusdev_base().dev_type { SysBusDevType::PL011 => { - // SAFETY: Legacy devices guarantee is not empty. generate_serial_device_node(fdt, &locked_dev.sysbusdev_base().res)? } SysBusDevType::Rtc => { - // SAFETY: Legacy devices guarantee is not empty. generate_rtc_device_node(fdt, &locked_dev.sysbusdev_base().res)? } SysBusDevType::VirtioMmio => { - // SAFETY: Legacy devices guarantee is not empty. generate_virtio_devices_node(fdt, &locked_dev.sysbusdev_base().res)? } SysBusDevType::FwCfg => { - // SAFETY: Legacy devices guarantee is not empty. generate_fwcfg_device_node(fdt, &locked_dev.sysbusdev_base().res)?; } SysBusDevType::Flash => { - // SAFETY: Legacy devices guarantee is not empty. - generate_flash_device_node(fdt, &locked_dev.sysbusdev_base().res)?; + // Two pflash devices are created, but only one pflash node is required in the fdt table. + // Thereforce, the second pflash device needs to be skipped. + if pflash_cnt == 0 { + generate_flash_device_node(fdt, &locked_dev.sysbusdev_base().res)?; + pflash_cnt += 1; + } } _ => (), } -- Gitee From da7c57bd416189d63f62817cad85b363c02d5f82 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 30 Jan 2024 06:19:53 +0800 Subject: [PATCH 1559/2187] Coding style: standardize the use of `Result` Most `Result` in the code are from the `Anyhow` lib which is widely used in code. There is no need to introduce the `Result` in mod.rs and then use it in xxx.rs by using `$mod_name::Result` and renaming it. Meanwhile, it does not require `pub use`. Fix these problems and standardize the use of `Result`. Signed-off-by: liuxiangdong --- address_space/src/lib.rs | 2 - cpu/src/aarch64/mod.rs | 4 +- cpu/src/x86_64/mod.rs | 2 +- devices/src/acpi/power.rs | 6 +-- .../src/interrupt_controller/aarch64/gicv2.rs | 4 +- .../src/interrupt_controller/aarch64/gicv3.rs | 4 +- .../src/interrupt_controller/aarch64/mod.rs | 9 ++-- .../src/interrupt_controller/aarch64/state.rs | 11 +++-- devices/src/interrupt_controller/mod.rs | 3 +- devices/src/legacy/mod.rs | 2 - devices/src/legacy/pl011.rs | 4 +- devices/src/legacy/pl031.rs | 4 +- devices/src/legacy/ramfb.rs | 7 ++-- devices/src/legacy/serial.rs | 4 +- devices/src/pci/mod.rs | 3 +- devices/src/pci/msix.rs | 6 +-- devices/src/pci/root_port.rs | 2 +- devices/src/sysbus/mod.rs | 2 +- devices/src/usb/mod.rs | 4 +- hypervisor/src/kvm/vm_state.rs | 6 +-- machine/src/aarch64/fdt.rs | 28 +++++++------ machine/src/aarch64/micro.rs | 10 ++--- machine/src/aarch64/pci_host_root.rs | 6 ++- machine/src/aarch64/standard.rs | 16 ++++---- machine/src/lib.rs | 9 ++-- machine/src/micro_common/mod.rs | 2 +- machine/src/standard_common/mod.rs | 4 +- machine/src/x86_64/ich9_lpc.rs | 6 +-- machine/src/x86_64/mch.rs | 4 +- machine/src/x86_64/standard.rs | 2 +- machine_manager/src/config/mod.rs | 2 +- machine_manager/src/event_loop.rs | 12 +++--- migration/src/lib.rs | 3 +- migration/src/protocol.rs | 20 ++++----- util/src/lib.rs | 2 - vfio/src/vfio_dev.rs | 10 ++--- vfio/src/vfio_pci.rs | 27 ++++++------ virtio/src/device/block.rs | 4 +- virtio/src/device/net.rs | 4 +- virtio/src/device/rng.rs | 4 +- virtio/src/device/serial.rs | 4 +- virtio/src/transport/virtio_mmio.rs | 6 +-- virtio/src/transport/virtio_pci.rs | 41 ++++++++++--------- virtio/src/vhost/kernel/vsock.rs | 14 +++---- virtio/src/vhost/user/client.rs | 4 +- 45 files changed, 157 insertions(+), 176 deletions(-) diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index b35ca5473..f9feddf38 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -89,8 +89,6 @@ mod listener; mod region; mod state; -pub use anyhow::Result; - pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; pub use error::AddressSpaceError; diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 5e90c75bf..236448ba0 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -187,7 +187,7 @@ impl ArmCPUState { } impl StateTransfer for CPU { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { self.hypervisor_cpu .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CoreRegs, &self.caps)?; @@ -210,7 +210,7 @@ impl StateTransfer for CPU { Ok(self.arch_cpu.lock().unwrap().as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::Result<()> { + fn set_state(&self, state: &[u8]) -> Result<()> { let cpu_state = *ArmCPUState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("CPU"))?; diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 1a349f2ed..668f27a3e 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -518,7 +518,7 @@ impl StateTransfer for CPU { Ok(self.arch_cpu.lock().unwrap().as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::Result<()> { + fn set_state(&self, state: &[u8]) -> Result<()> { let cpu_state = *X86CPUState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("CPU"))?; diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 5c0280146..a51071d8b 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -206,11 +206,11 @@ impl PowerDev { } impl StateTransfer for PowerDev { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state.as_mut_bytes().copy_from_slice(state); Ok(()) } @@ -221,7 +221,7 @@ impl StateTransfer for PowerDev { } impl MigrationHook for PowerDev { - fn resume(&mut self) -> migration::Result<()> { + fn resume(&mut self) -> Result<()> { self.send_power_event(AcpiEvent::AcadSt); self.send_power_event(AcpiEvent::BatterySt); Ok(()) diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index 0e4814bba..4a67fa0c3 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Result}; use log::error; -use super::{GICConfig, GICDevice, UtilResult}; +use super::{GICConfig, GICDevice}; use crate::interrupt_controller::InterruptError; use address_space::AddressSpace; use machine_manager::machine::{MachineLifecycle, VmState}; @@ -155,7 +155,7 @@ impl GICDevice for GICv2 { Ok(()) } - fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()> { + fn generate_fdt(&self, fdt: &mut FdtBuilder) -> Result<()> { let gic_reg = vec![ self.dist_guest_region.base, self.dist_guest_region.size, diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 283c3c07d..b9cb520c4 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; use log::{error, info}; -use super::{GICConfig, GICDevice, UtilResult}; +use super::{GICConfig, GICDevice}; use crate::interrupt_controller::error::InterruptError; use machine_manager::machine::{MachineLifecycle, VmState}; use migration::StateTransfer; @@ -272,7 +272,7 @@ impl GICDevice for GICv3 { Ok(()) } - fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()> { + fn generate_fdt(&self, fdt: &mut FdtBuilder) -> Result<()> { let redist_count = self.redist_regions.len() as u32; let mut gic_reg = vec![self.dist_base, self.dist_size]; diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 0bb78e963..ab548604c 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -30,10 +30,7 @@ use anyhow::{anyhow, Context, Result}; use crate::interrupt_controller::error::InterruptError; use machine_manager::machine::{MachineLifecycle, VmState}; -use util::{ - device_tree::{self, FdtBuilder}, - Result as UtilResult, -}; +use util::device_tree::{self, FdtBuilder}; // First 32 are private to each CPU (SGIs and PPIs). pub const GIC_IRQ_INTERNAL: u32 = 32; @@ -85,7 +82,7 @@ pub trait GICDevice: MachineLifecycle { /// # Arguments /// /// * `fdt` - Device tree presented by bytes. - fn generate_fdt(&self, fdt: &mut FdtBuilder) -> UtilResult<()>; + fn generate_fdt(&self, fdt: &mut FdtBuilder) -> Result<()>; /// Get GIC redistributor number. fn get_redist_count(&self) -> u8 { @@ -134,7 +131,7 @@ impl InterruptController { } impl device_tree::CompileFDT for InterruptController { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> UtilResult<()> { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> Result<()> { self.gic.generate_fdt(fdt)?; Ok(()) } diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index bb40c9dd6..926096103 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -12,12 +12,11 @@ use std::mem::size_of; -use anyhow::Context; +use anyhow::{Context, Result}; use libc::c_uint; use super::gicv3::{GICv3, GICv3Its}; use super::GIC_IRQ_INTERNAL; -use crate::interrupt_controller::Result; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -611,7 +610,7 @@ pub struct GICv3State { } impl StateTransfer for GICv3 { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { use migration::MigrationError; let mut state = GICv3State::default(); @@ -650,7 +649,7 @@ impl StateTransfer for GICv3 { Ok(state.as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::Result<()> { + fn set_state(&self, state: &[u8]) -> Result<()> { use migration::error::MigrationError; let state = GICv3State::from_bytes(state).unwrap(); @@ -710,7 +709,7 @@ pub struct GICv3ItsState { } impl StateTransfer for GICv3Its { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { use migration::MigrationError; let mut state = GICv3ItsState::default(); @@ -732,7 +731,7 @@ impl StateTransfer for GICv3Its { Ok(state.as_bytes().to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::Result<()> { + fn set_state(&self, state: &[u8]) -> Result<()> { use migration::MigrationError; let mut its_state = *GICv3ItsState::from_bytes(state) diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 17c278e63..34916629e 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -29,8 +29,6 @@ mod aarch64; mod error; -pub use anyhow::Result; - #[cfg(target_arch = "aarch64")] pub use aarch64::{ GICConfig as ICGICConfig, GICDevice, GICVersion, GICv2, GICv2Access, @@ -41,6 +39,7 @@ pub use error::InterruptError; use std::sync::Arc; +use anyhow::Result; use vmm_sys_util::eventfd::EventFd; use super::pci::MsiVector; diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index b6aa7da41..00632feb4 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -39,8 +39,6 @@ mod ramfb; mod rtc; mod serial; -pub use anyhow::Result; - #[cfg(target_arch = "x86_64")] pub use self::rtc::{RTC, RTC_PORT_INDEX}; pub use error::LegacyError; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 976b17a4e..64923b72d 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -413,11 +413,11 @@ impl SysBusDevOps for PL011 { } impl StateTransfer for PL011 { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *PL011State::from_bytes(state) .with_context(|| MigrationError::FromBytesError("PL011"))?; diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 9535cf3b1..a5790dd6d 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -211,13 +211,13 @@ impl AmlBuilder for PL031 { } impl StateTransfer for PL031 { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let state = self.state; Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *PL031State::from_bytes(state) .with_context(|| MigrationError::FromBytesError("PL031"))?; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index ab306fb0c..470fcd796 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -15,13 +15,12 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; -use anyhow::Context; +use anyhow::{Context, Result}; use drm_fourcc::DrmFourcc; use log::error; use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; -use crate::legacy::Result; -use crate::sysbus::{Result as SysBusResult, SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; @@ -274,7 +273,7 @@ impl SysBusDevOps for Ramfb { false } - fn reset(&mut self) -> SysBusResult<()> { + fn reset(&mut self) -> Result<()> { self.ramfb_state.reset_ramfb_state(); Ok(()) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 7b1a98a26..825aad12e 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -428,7 +428,7 @@ impl AmlBuilder for Serial { } impl StateTransfer for Serial { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let mut state = self.state; let (rbr_state, _) = self.rbr.as_slices(); state.rbr_len = rbr_state.len(); @@ -437,7 +437,7 @@ impl StateTransfer for Serial { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let serial_state = *SerialState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("SERIAL"))?; let mut rbr = VecDeque::::default(); diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 94aae2449..9eefbdb1b 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -22,8 +22,6 @@ mod bus; mod host; mod root_port; -pub use anyhow::{bail, Result}; - pub use bus::PciBus; pub use config::{PciConfig, INTERRUPT_PIN}; pub use error::PciError; @@ -37,6 +35,7 @@ use std::{ sync::{Arc, Mutex, Weak}, }; +use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; use crate::{ diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 00d54a8ff..0469f7f19 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -468,7 +468,7 @@ impl Msix { } impl StateTransfer for Msix { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let mut state = MsixState::default(); for (idx, table_byte) in self.table.iter().enumerate() { @@ -485,7 +485,7 @@ impl StateTransfer for Msix { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let msix_state = *MsixState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("MSIX_DEVICE"))?; @@ -507,7 +507,7 @@ impl StateTransfer for Msix { } impl MigrationHook for Msix { - fn resume(&mut self) -> migration::Result<()> { + fn resume(&mut self) -> Result<()> { if self.enabled && !self.func_masked { for vector in 0..self.table.len() as u16 / MSIX_TABLE_ENTRY_SIZE { if self.is_vector_masked(vector) { diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 9f1f0125b..401302773 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -663,7 +663,7 @@ impl StateTransfer for RootPort { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let root_port_state = *RootPortState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("ROOT_PORT"))?; diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 30cb972d8..ca039aba8 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -12,12 +12,12 @@ pub mod error; -pub use anyhow::{bail, Context, Result}; pub use error::SysBusError; use std::fmt; use std::sync::{Arc, Mutex}; +use anyhow::{bail, Context, Result}; use vmm_sys_util::eventfd::EventFd; use crate::{Device, DeviceBase, IrqState, LineIrqManager, TriggerMode}; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index b7ea219ef..08cef7eb8 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -26,14 +26,12 @@ pub mod xhci; mod descriptor; -pub use anyhow::Result; - pub use error::UsbError; use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{bail, Context}; +use anyhow::{bail, Context, Result}; use log::{debug, error}; use self::descriptor::USB_MAX_INTERFACES; diff --git a/hypervisor/src/kvm/vm_state.rs b/hypervisor/src/kvm/vm_state.rs index 73abfcdae..103a350ca 100644 --- a/hypervisor/src/kvm/vm_state.rs +++ b/hypervisor/src/kvm/vm_state.rs @@ -12,7 +12,7 @@ use std::sync::Arc; -use anyhow::Context; +use anyhow::{Context, Result}; use kvm_bindings::{kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_IRQCHIP_IOAPIC}; use kvm_ioctls::VmFd; @@ -45,7 +45,7 @@ pub struct KvmDeviceState { } impl StateTransfer for KvmDevice { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let vm_fd = self.vm_fd.clone(); // save pit @@ -72,7 +72,7 @@ impl StateTransfer for KvmDevice { .to_vec()) } - fn set_state(&self, state: &[u8]) -> migration::Result<()> { + fn set_state(&self, state: &[u8]) -> Result<()> { let vm_fd = self.vm_fd.clone(); let kvm_state = KvmDeviceState::from_bytes(state) diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index e7a5937b1..eb770eb5e 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use anyhow::Result; + use crate::MachineBase; use cpu::PMU_INTR; use devices::sysbus::{SysBusDevType, SysRes}; @@ -20,7 +22,7 @@ use util::device_tree::{self, FdtBuilder}; /// # Arguments /// /// * `fdt` - Flatted device-tree blob where node will be filled into. -fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { +fn generate_pmu_node(fdt: &mut FdtBuilder) -> Result<()> { let node = "pmu"; let pmu_node_dep = fdt.begin_node(node)?; fdt.set_property_string("compatible", "arm,armv8-pmuv3")?; @@ -42,7 +44,7 @@ fn generate_pmu_node(fdt: &mut FdtBuilder) -> util::Result<()> { /// /// * `dev_info` - Device resource info of serial device. /// * `fdt` - Flatted device-tree blob where serial node will be filled into. -fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { +fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> Result<()> { let node = format!("pl011@{:x}", res.region_base); let serial_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "arm,pl011\0arm,primecell")?; @@ -69,7 +71,7 @@ fn generate_serial_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Resu /// /// * `dev_info` - Device resource info of RTC device. /// * `fdt` - Flatted device-tree blob where RTC node will be filled into. -fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { +fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> Result<()> { let node = format!("pl031@{:x}", res.region_base); let rtc_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "arm,pl031\0arm,primecell\0")?; @@ -93,7 +95,7 @@ fn generate_rtc_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result< /// /// * `dev_info` - Device resource info of Virtio-Mmio device. /// * `fdt` - Flatted device-tree blob where node will be filled into. -fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { +fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> Result<()> { let node = format!("virtio_mmio@{:x}", res.region_base); let virtio_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "virtio,mmio")?; @@ -116,7 +118,7 @@ fn generate_virtio_devices_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Res /// /// * `dev_info` - Device resource info of fw-cfg device. /// * `fdt` - Flatted device-tree blob where fw-cfg node will be filled into. -fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { +fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> Result<()> { let node = format!("fw-cfg@{:x}", res.region_base); let fwcfg_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "qemu,fw-cfg-mmio")?; @@ -130,7 +132,7 @@ fn generate_fwcfg_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Resul /// /// * `dev_info` - Device resource info of fw-cfg device. /// * `flash` - Flatted device-tree blob where fw-cfg node will be filled into. -fn generate_flash_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Result<()> { +fn generate_flash_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> Result<()> { let flash_base = res.region_base; let flash_size = res.region_size; let node = format!("flash@{:x}", flash_base); @@ -148,15 +150,15 @@ fn generate_flash_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> util::Resul #[allow(clippy::upper_case_acronyms)] trait CompileFDTHelper { /// Function that helps to generate cpu nodes. - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> Result<()>; /// Function that helps to generate Virtio-mmio devices' nodes. - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> Result<()>; /// Function that helps to generate numa node distances. - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> Result<()>; } impl CompileFDTHelper for MachineBase { - fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_cpu_nodes(&self, fdt: &mut FdtBuilder) -> Result<()> { let node = "cpus"; let cpus_node_dep = fdt.begin_node(node)?; @@ -234,7 +236,7 @@ impl CompileFDTHelper for MachineBase { Ok(()) } - fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_devices_node(&self, fdt: &mut FdtBuilder) -> Result<()> { // timer let mut cells: Vec = Vec::new(); for &irq in [13, 14, 11, 10].iter() { @@ -297,7 +299,7 @@ impl CompileFDTHelper for MachineBase { Ok(()) } - fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_distance_node(&self, fdt: &mut FdtBuilder) -> Result<()> { if self.numa_nodes.is_none() { return Ok(()); } @@ -330,7 +332,7 @@ impl CompileFDTHelper for MachineBase { } impl device_tree::CompileFDT for MachineBase { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> Result<()> { fdt.set_property_string("compatible", "linux,dummy-virt")?; fdt.set_property_u32("#address-cells", 0x2)?; fdt.set_property_u32("#size-cells", 0x2)?; diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index ef3e85c25..38af54473 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -262,13 +262,13 @@ pub(crate) fn arch_syscall_whitelist() -> Vec { #[allow(clippy::upper_case_acronyms)] trait CompileFDTHelper { /// Function that helps to generate memory nodes. - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> Result<()>; /// Function that helps to generate the chosen node. - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> Result<()>; } impl CompileFDTHelper for LightMachine { - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> Result<()> { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let mem_size = self.base.sys_mem.memory_end_address().raw_value() - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; @@ -279,7 +279,7 @@ impl CompileFDTHelper for LightMachine { fdt.end_node(memory_node_dep) } - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> Result<()> { let node = "chosen"; let boot_source = self.base.boot_source.lock().unwrap(); @@ -303,7 +303,7 @@ impl CompileFDTHelper for LightMachine { } impl device_tree::CompileFDT for LightMachine { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> Result<()> { let node_dep = fdt.begin_node("")?; self.base.generate_fdt_node(fdt)?; self.generate_memory_node(fdt)?; diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index 9b0b3ca46..aec540233 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -12,12 +12,14 @@ use std::sync::{Arc, Mutex, Weak}; +use anyhow::Result; + use devices::pci::{ config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }, - le_write_u16, PciBus, PciDevBase, PciDevOps, Result as PciResult, + le_write_u16, PciBus, PciDevBase, PciDevOps, }; use devices::{Device, DeviceBase}; @@ -60,7 +62,7 @@ impl PciDevOps for PciHostRoot { &mut self.base } - fn realize(mut self) -> PciResult<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 5b5398eb4..128a45d03 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -1243,7 +1243,7 @@ impl EventLoopManager for StdMachine { *vmstate == VmState::Shutdown } - fn loop_cleanup(&self) -> util::Result<()> { + fn loop_cleanup(&self) -> Result<()> { set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; Ok(()) } @@ -1256,15 +1256,15 @@ impl EventLoopManager for StdMachine { #[allow(clippy::upper_case_acronyms)] trait CompileFDTHelper { /// Function that helps to generate memory nodes. - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> Result<()>; /// Function that helps to generate pci node in device-tree. - fn generate_pci_host_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_pci_host_node(&self, fdt: &mut FdtBuilder) -> Result<()>; /// Function that helps to generate the chosen node. - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()>; + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> Result<()>; } impl CompileFDTHelper for StdMachine { - fn generate_pci_host_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_pci_host_node(&self, fdt: &mut FdtBuilder) -> Result<()> { let pcie_ecam_base = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].0; let pcie_ecam_size = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1; let pcie_buses_num = MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1 >> 20; @@ -1333,7 +1333,7 @@ impl CompileFDTHelper for StdMachine { fdt.end_node(pci_node_dep) } - fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> Result<()> { if self.base.numa_nodes.is_none() { let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; let mem_size = self.base.sys_mem.memory_end_address().raw_value() @@ -1363,7 +1363,7 @@ impl CompileFDTHelper for StdMachine { Ok(()) } - fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> Result<()> { let node = "chosen"; let boot_source = self.base.boot_source.lock().unwrap(); @@ -1388,7 +1388,7 @@ impl CompileFDTHelper for StdMachine { } impl device_tree::CompileFDT for StdMachine { - fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_fdt_node(&self, fdt: &mut FdtBuilder) -> Result<()> { let node_dep = fdt.begin_node("")?; self.base.generate_fdt_node(fdt)?; self.generate_memory_node(fdt)?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 3201a9017..e9238a3e2 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -19,8 +19,6 @@ pub mod x86_64; mod micro_common; -pub use anyhow::Result; - pub use crate::error::MachineError; pub use micro_common::LightMachine; pub use standard_common::StdMachine; @@ -35,7 +33,7 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; #[cfg(feature = "windows_emu_pid")] use std::time::Duration; -use anyhow::{anyhow, bail, Context}; +use anyhow::{anyhow, bail, Context, Result}; use log::warn; #[cfg(feature = "windows_emu_pid")] use vmm_sys_util::eventfd::EventFd; @@ -93,7 +91,6 @@ use machine_manager::config::{ use machine_manager::event_loop::EventLoop; use machine_manager::machine::{MachineInterface, VmState}; use migration::{MigrateOps, MigrationManager}; -use standard_common::Result as StdResult; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; use util::file::{clear_file, lock_file, unlock_file}; @@ -825,7 +822,7 @@ pub trait MachineOps { Ok(()) } - fn get_pci_host(&mut self) -> StdResult<&Arc>> { + fn get_pci_host(&mut self) -> Result<&Arc>> { bail!("No pci host found"); } @@ -1323,7 +1320,7 @@ pub trait MachineOps { Ok(()) } - fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> StdResult<(u8, Weak>)> { + fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> Result<(u8, Weak>)> { let pci_host = self.get_pci_host()?; let bus = pci_host.lock().unwrap().root_bus.clone(); let pci_bus = PciBus::find_bus_by_name(&bus, &bdf.bus); diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 6ca6c6db4..c5dcc3d99 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -945,7 +945,7 @@ impl EventLoopManager for LightMachine { *vmstate == VmState::Shutdown } - fn loop_cleanup(&self) -> util::Result<()> { + fn loop_cleanup(&self) -> Result<()> { set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; Ok(()) } diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index d3a47a5aa..ea5d040c6 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -12,8 +12,6 @@ pub mod syscall; -pub use anyhow::Result; - #[cfg(target_arch = "aarch64")] pub use crate::aarch64::standard::StdMachine; pub use crate::error::MachineError; @@ -29,7 +27,7 @@ use std::string::String; use std::sync::{Arc, Mutex}; use std::u64; -use anyhow::{bail, Context}; +use anyhow::{bail, Context, Result}; use log::error; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index 08a3aec30..0a93312ef 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -26,9 +26,7 @@ use devices::pci::config::{ PciConfig, CLASS_CODE_ISA_BRIDGE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; -use devices::pci::{ - le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps, Result as PciResult, -}; +use devices::pci::{le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps}; use devices::{Device, DeviceBase}; use util::byte_code::ByteCode; use util::num_ops::ranges_overlap; @@ -249,7 +247,7 @@ impl PciDevOps for LPCBridge { &mut self.base } - fn realize(mut self) -> PciResult<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index 9af04b94e..5c6f593ec 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -22,7 +22,7 @@ use devices::pci::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }, - le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, Result as PciResult, + le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, }; use devices::{Device, DeviceBase}; use util::num_ops::ranges_overlap; @@ -139,7 +139,7 @@ impl PciDevOps for Mch { &mut self.base } - fn realize(mut self) -> PciResult<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index d65e930bc..4c5476322 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -1057,7 +1057,7 @@ impl EventLoopManager for StdMachine { *vmstate == VmState::Shutdown } - fn loop_cleanup(&self) -> util::Result<()> { + fn loop_cleanup(&self) -> Result<()> { set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; Ok(()) } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index f262b657b..0808a891a 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -412,7 +412,7 @@ impl VmConfig { #[cfg(target_arch = "aarch64")] impl device_tree::CompileFDT for VmConfig { - fn generate_fdt_node(&self, _fdt: &mut FdtBuilder) -> util::Result<()> { + fn generate_fdt_node(&self, _fdt: &mut FdtBuilder) -> Result<()> { Ok(()) } } diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 850c9040e..3946adf52 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -15,7 +15,7 @@ use std::os::unix::prelude::RawFd; use std::sync::{Arc, Mutex}; use std::{process, thread}; -use anyhow::bail; +use anyhow::{bail, Result}; use log::info; use super::config::IothreadConfig; @@ -47,7 +47,7 @@ impl EventLoop { /// # Arguments /// /// * `iothreads` - refer to `-iothread` params - pub fn object_init(iothreads: &Option>) -> util::Result<()> { + pub fn object_init(iothreads: &Option>) -> Result<()> { let mut io_threads = HashMap::new(); if let Some(thrs) = iothreads { for thr in thrs { @@ -129,7 +129,7 @@ impl EventLoop { /// /// * `notifiers` - The wrapper of events will be handled in the event loop specified by name. /// * `name` - specify which event loop to manage - pub fn update_event(notifiers: Vec, name: Option<&String>) -> util::Result<()> { + pub fn update_event(notifiers: Vec, name: Option<&String>) -> Result<()> { if let Some(ctx) = Self::get_ctx(name) { ctx.update_events(notifiers) } else { @@ -143,7 +143,7 @@ impl EventLoop { /// /// Once run main loop, `epoll` in `MainLoopContext` will execute /// `epoll_wait()` function to wait for events. - pub fn loop_run() -> util::Result<()> { + pub fn loop_run() -> Result<()> { // SAFETY: the main_loop ctx is dedicated for main thread, thus no concurrent // accessing. unsafe { @@ -170,7 +170,7 @@ pub fn register_event_helper( notifiers: Vec, ctx_name: Option<&String>, record_evts: &mut Vec, -) -> util::Result<()> { +) -> Result<()> { let mut notifiers_fds = get_notifiers_fds(¬ifiers); EventLoop::update_event(notifiers, ctx_name)?; record_evts.append(&mut notifiers_fds); @@ -180,7 +180,7 @@ pub fn register_event_helper( pub fn unregister_event_helper( ctx_name: Option<&String>, record_evts: &mut Vec, -) -> util::Result<()> { +) -> Result<()> { EventLoop::update_event(gen_delete_notifiers(record_evts), ctx_name)?; record_evts.clear(); Ok(()) diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 24e334b51..39b9563b3 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -21,8 +21,6 @@ pub mod migration; pub mod protocol; pub mod snapshot; -pub use anyhow::Result; - pub use error::MigrationError; pub use manager::{MigrationHook, MigrationManager}; pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; @@ -32,6 +30,7 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use std::{net::TcpStream, os::unix::net::UnixStream, thread}; +use anyhow::Result; use log::error; use machine_manager::qmp::{qmp_response::Response, qmp_schema}; diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 0a00aa6cd..0d57220f0 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -786,11 +786,11 @@ pub mod tests { } impl StateTransfer for DeviceV1 { - fn get_state_vec(&self) -> super::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> super::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *DeviceV1State::from_bytes(state).unwrap(); Ok(()) } @@ -801,11 +801,11 @@ pub mod tests { } impl StateTransfer for DeviceV2 { - fn get_state_vec(&self) -> super::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> super::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *DeviceV2State::from_bytes(state).unwrap(); Ok(()) } @@ -830,11 +830,11 @@ pub mod tests { } impl StateTransfer for DeviceV3 { - fn get_state_vec(&self) -> super::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> super::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *DeviceV3State::from_bytes(state).unwrap(); Ok(()) } @@ -859,11 +859,11 @@ pub mod tests { } impl StateTransfer for DeviceV4 { - fn get_state_vec(&self) -> super::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> super::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *DeviceV4State::from_bytes(state).unwrap(); Ok(()) } @@ -882,11 +882,11 @@ pub mod tests { } impl StateTransfer for DeviceV5 { - fn get_state_vec(&self) -> super::Result> { + fn get_state_vec(&self) -> Result> { Ok(self.state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> super::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *DeviceV5State::from_bytes(state).unwrap(); Ok(()) } diff --git a/util/src/lib.rs b/util/src/lib.rs index a2cf0bd3c..11b2de08a 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -40,8 +40,6 @@ pub mod unix; #[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; -pub use anyhow::Result; - pub use error::UtilError; use std::{any::Any, sync::Mutex}; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index fbbb15413..e83076800 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -226,7 +226,7 @@ impl VfioContainer { Ok(()) } - fn add_listener_region(&self, fr: &FlatRange) -> address_space::Result<()> { + fn add_listener_region(&self, fr: &FlatRange) -> Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } @@ -238,7 +238,7 @@ impl VfioContainer { None => bail!("Failed to get host address"), }; let userspace_addr = hva + fr.offset_in_region; - address_space::Result::with_context( + Result::with_context( self.vfio_dma_map(guest_phys_addr, memory_size, userspace_addr), || { format!( @@ -250,14 +250,14 @@ impl VfioContainer { Ok(()) } - fn del_listener_region(&self, fr: &FlatRange) -> address_space::Result<()> { + fn del_listener_region(&self, fr: &FlatRange) -> Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } let guest_phys_addr = fr.addr_range.base.raw_value(); let size = fr.addr_range.size; - address_space::Result::with_context(self.vfio_dma_unmap(guest_phys_addr, size), || { + Result::with_context(self.vfio_dma_unmap(guest_phys_addr, size), || { format!( "Failed to do dma unmap: gpa 0x{:x}, size 0x{:x}.", guest_phys_addr, size @@ -289,7 +289,7 @@ impl Listener for VfioContainer { range: Option<&FlatRange>, _evtfd: Option<&RegionIoEventFd>, req_type: ListenerReqType, - ) -> address_space::Result<()> { + ) -> Result<()> { match req_type { ListenerReqType::AddRegion => { self.add_listener_region(range.unwrap())?; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 7863080b7..b3ab15598 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -819,20 +819,20 @@ impl PciDevOps for VfioPciDevice { &mut self.base } - fn realize(mut self) -> devices::pci::Result<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; - devices::pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { + Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Failed to reset vfio device" })?; - devices::pci::Result::with_context(self.get_pci_config(), || { + Result::with_context(self.get_pci_config(), || { "Failed to get vfio device pci config space" })?; - devices::pci::Result::with_context(self.pci_config_reset(), || { + Result::with_context(self.pci_config_reset(), || { "Failed to reset vfio device pci config space" })?; - devices::pci::Result::with_context( + Result::with_context( init_multifunction( self.multi_func, &mut self.base.config.config, @@ -855,15 +855,14 @@ impl PciDevOps for VfioPciDevice { self.dev_id = Arc::new(AtomicU16::new(self.set_dev_id(bus_num, self.base.devfn))); } - self.msix_info = Some(devices::pci::Result::with_context( - self.get_msix_info(), - || "Failed to get MSI-X info", - )?); - self.vfio_bars = Arc::new(Mutex::new(devices::pci::Result::with_context( + self.msix_info = Some(Result::with_context(self.get_msix_info(), || { + "Failed to get MSI-X info" + })?); + self.vfio_bars = Arc::new(Mutex::new(Result::with_context( self.bar_region_info(), || "Failed to get bar region info", )?)); - devices::pci::Result::with_context(self.register_bars(), || "Failed to register bars")?; + Result::with_context(self.register_bars(), || "Failed to register bars")?; let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); @@ -883,7 +882,7 @@ impl PciDevOps for VfioPciDevice { Ok(()) } - fn unrealize(&mut self) -> devices::pci::Result<()> { + fn unrealize(&mut self) -> Result<()> { if let Err(e) = VfioPciDevice::unrealize(self) { error!("{:?}", e); bail!("Failed to unrealize vfio-pci."); @@ -1001,8 +1000,8 @@ impl PciDevOps for VfioPciDevice { } } - fn reset(&mut self, _reset_child_device: bool) -> devices::pci::Result<()> { - devices::pci::Result::with_context(self.vfio_device.lock().unwrap().reset(), || { + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + Result::with_context(self.vfio_device.lock().unwrap().reset(), || { "Fail to reset vfio dev" }) } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 976fc4d5e..41841986d 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1308,7 +1308,7 @@ impl VirtioDevice for Block { unsafe impl Sync for Block {} impl StateTransfer for Block { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let state = BlockState { device_features: self.base.device_features, driver_features: self.base.driver_features, @@ -1318,7 +1318,7 @@ impl StateTransfer for Block { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let state = BlockState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("BLOCK"))?; self.base.device_features = state.device_features; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 17a81b313..0cc6aa307 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1642,7 +1642,7 @@ impl VirtioDevice for Net { unsafe impl Sync for Net {} impl StateTransfer for Net { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let state = VirtioNetState { device_features: self.base.device_features, driver_features: self.base.driver_features, @@ -1652,7 +1652,7 @@ impl StateTransfer for Net { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let s_len = std::mem::size_of::(); if state.len() != s_len { bail!("Invalid state length {}, expected {}", state.len(), s_len); diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index b63b2ba3b..c44105241 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -326,7 +326,7 @@ impl VirtioDevice for Rng { } impl StateTransfer for Rng { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let state = RngState { device_features: self.base.device_features, driver_features: self.base.driver_features, @@ -334,7 +334,7 @@ impl StateTransfer for Rng { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let state = RngState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("RNG"))?; self.base.device_features = state.device_features; diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 1e8bf75c9..6aa9b884e 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -306,7 +306,7 @@ impl VirtioDevice for Serial { } impl StateTransfer for Serial { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let state = VirtioSerialState { device_features: self.base.device_features, driver_features: self.base.driver_features, @@ -315,7 +315,7 @@ impl StateTransfer for Serial { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let state = VirtioSerialState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("SERIAL"))?; self.base.device_features = state.device_features; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 32a5451c5..b276ae25c 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -536,14 +536,14 @@ impl acpi::AmlBuilder for VirtioMmioDevice { } impl StateTransfer for VirtioMmioDevice { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let state = VirtioMmioState { virtio_base: self.device.lock().unwrap().virtio_base().get_state(), }; Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let s_len = std::mem::size_of::(); if state.len() != s_len { bail!("Invalid state length {}, expected {}", state.len(), s_len); @@ -567,7 +567,7 @@ impl StateTransfer for VirtioMmioDevice { } impl MigrationHook for VirtioMmioDevice { - fn resume(&mut self) -> migration::Result<()> { + fn resume(&mut self) -> Result<()> { let mut locked_dev = self.device.lock().unwrap(); if !locked_dev.device_activated() { return Ok(()); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index ac73f7562..620d61042 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -15,7 +15,7 @@ use std::mem::size_of; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{anyhow, bail, Context}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, warn}; use machine_manager::config::M; @@ -43,7 +43,7 @@ use devices::pci::config::{ use devices::pci::msix::MsixState; use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, - PciBus, PciDevBase, PciDevOps, PciError, Result as PciResult, + PciBus, PciDevBase, PciDevOps, PciError, }; use devices::{Device, DeviceBase}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -131,7 +131,7 @@ const MAX_FEATURES_SELECT_NUM: u32 = 2; /// The bar0 size of enable_bar0 features const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; -fn init_gpu_bar0(config: &mut PciConfig) -> PciResult<()> { +fn init_gpu_bar0(config: &mut PciConfig) -> Result<()> { let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(0), None, @@ -413,7 +413,7 @@ impl VirtioPciDevice { ret } - fn modern_mem_region_map(&mut self, data: T) -> PciResult { + fn modern_mem_region_map(&mut self, data: T) -> Result { let cap_offset = self.base.config.add_pci_cap( PCI_CAP_ID_VNDR, size_of::() + PCI_CAP_VNDR_AND_NEXT_SIZE as usize, @@ -531,7 +531,7 @@ impl VirtioPciDevice { /// # Arguments /// /// * `offset` - The offset of common config. - fn read_common_config(&self, offset: u64) -> PciResult { + fn read_common_config(&self, offset: u64) -> Result { let locked_device = self.device.lock().unwrap(); let value = match offset { COMMON_DFSELECT_REG => locked_device.hfeatures_sel(), @@ -601,7 +601,7 @@ impl VirtioPciDevice { /// # Errors /// /// Returns Error if the offset is out of bound. - fn write_common_config(&mut self, offset: u64, value: u32) -> PciResult<()> { + fn write_common_config(&mut self, offset: u64, value: u32) -> Result<()> { let mut locked_device = self.device.lock().unwrap(); match offset { COMMON_DFSELECT_REG => { @@ -771,7 +771,7 @@ impl VirtioPciDevice { fn modern_mem_region_init( virtio_pci: Arc>, modern_mem_region: &Region, - ) -> PciResult<()> { + ) -> Result<()> { // 1. PCI common cap sub-region. let common_region_ops = Self::build_common_cfg_ops(virtio_pci.clone()); let common_region = Region::init_io_region( @@ -987,7 +987,7 @@ impl PciDevOps for VirtioPciDevice { &mut self.base } - fn realize(mut self) -> PciResult<()> { + fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -1153,7 +1153,7 @@ impl PciDevOps for VirtioPciDevice { Ok(()) } - fn unrealize(&mut self) -> PciResult<()> { + fn unrealize(&mut self) -> Result<()> { self.device .lock() .unwrap() @@ -1198,7 +1198,7 @@ impl PciDevOps for VirtioPciDevice { self.do_cfg_access(offset, end, true); } - fn reset(&mut self, _reset_child_device: bool) -> PciResult<()> { + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { self.deactivate_device(); self.device .lock() @@ -1237,7 +1237,7 @@ impl PciDevOps for VirtioPciDevice { } impl StateTransfer for VirtioPciDevice { - fn get_state_vec(&self) -> migration::Result> { + fn get_state_vec(&self) -> Result> { let mut state = VirtioPciState { dev_id: self.dev_id.load(Ordering::Acquire), ..Default::default() @@ -1264,7 +1264,7 @@ impl StateTransfer for VirtioPciDevice { Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let pci_state = VirtioPciState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("PCI_DEVICE"))?; @@ -1295,7 +1295,7 @@ impl StateTransfer for VirtioPciDevice { } impl MigrationHook for VirtioPciDevice { - fn resume(&mut self) -> migration::Result<()> { + fn resume(&mut self) -> Result<()> { if !self.device.lock().unwrap().device_activated() { return Ok(()); } @@ -1333,10 +1333,11 @@ impl MigrationHook for VirtioPciDevice { mod tests { use std::sync::{Arc, Mutex}; + use anyhow::Result; use vmm_sys_util::eventfd::EventFd; use super::*; - use crate::{Result as VirtioResult, VirtioBase}; + use crate::VirtioBase; use address_space::{AddressSpace, GuestAddress, HostMemMapping}; use devices::pci::{ config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC}, @@ -1376,20 +1377,20 @@ mod tests { &mut self.base } - fn realize(&mut self) -> VirtioResult<()> { + fn realize(&mut self) -> Result<()> { self.init_config_features()?; Ok(()) } - fn init_config_features(&mut self) -> VirtioResult<()> { + fn init_config_features(&mut self) -> Result<()> { Ok(()) } - fn read_config(&self, _offset: u64, mut _data: &mut [u8]) -> VirtioResult<()> { + fn read_config(&self, _offset: u64, mut _data: &mut [u8]) -> Result<()> { Ok(()) } - fn write_config(&mut self, _offset: u64, _data: &[u8]) -> VirtioResult<()> { + fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { Ok(()) } @@ -1398,12 +1399,12 @@ mod tests { _mem_space: Arc, _interrupt_cb: Arc, _queue_evts: Vec>, - ) -> VirtioResult<()> { + ) -> Result<()> { self.is_activated = true; Ok(()) } - fn deactivate(&mut self) -> VirtioResult<()> { + fn deactivate(&mut self) -> Result<()> { Ok(()) } } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 1427592b4..8d782c623 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -336,8 +336,8 @@ impl VirtioDevice for Vsock { } impl StateTransfer for Vsock { - fn get_state_vec(&self) -> migration::Result> { - migration::Result::with_context(self.backend.as_ref().unwrap().set_running(false), || { + fn get_state_vec(&self) -> Result> { + Result::with_context(self.backend.as_ref().unwrap().set_running(false), || { "Failed to set vsock backend stopping" })?; @@ -351,17 +351,17 @@ impl StateTransfer for Vsock { broken: self.base.broken.load(Ordering::SeqCst), }; - migration::Result::with_context(self.backend.as_ref().unwrap().set_running(true), || { + Result::with_context(self.backend.as_ref().unwrap().set_running(true), || { "Failed to set vsock backend running" })?; - migration::Result::with_context(self.transport_reset(), || { + Result::with_context(self.transport_reset(), || { "Failed to send vsock transport reset event" })?; Ok(state.as_bytes().to_vec()) } - fn set_state_mut(&mut self, state: &[u8]) -> migration::Result<()> { + fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { let state = VsockState::from_bytes(state) .with_context(|| migration::error::MigrationError::FromBytesError("VSOCK"))?; self.base.device_features = state.device_features; @@ -379,8 +379,8 @@ impl StateTransfer for Vsock { impl MigrationHook for Vsock { #[cfg(target_arch = "aarch64")] - fn resume(&mut self) -> migration::Result<()> { - migration::Result::with_context(self.transport_reset(), || { + fn resume(&mut self) -> Result<()> { + Result::with_context(self.transport_reset(), || { "Failed to resume virtio vsock device" })?; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 3d18ea5ff..0b312c2ce 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -224,7 +224,7 @@ impl VhostUserMemInfo { None } - fn add_mem_range(&self, fr: &FlatRange) -> address_space::Result<()> { + fn add_mem_range(&self, fr: &FlatRange) -> Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } @@ -271,7 +271,7 @@ impl VhostUserMemInfo { Ok(()) } - fn delete_mem_range(&self, fr: &FlatRange) -> address_space::Result<()> { + fn delete_mem_range(&self, fr: &FlatRange) -> Result<()> { if fr.owner.region_type() != address_space::RegionType::Ram { return Ok(()); } -- Gitee From 19eabe5fcf4b84cdc2b2c456aff6ff38778b8bc0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Jan 2024 16:44:27 +0800 Subject: [PATCH 1560/2187] machine: obtain device type by cfg_args `cfg_args` is the cmdline of the device, and it starts with device type name. Add function `parse_device_type` to get type name. And then, there is no need to transfer device type when using function `add_XXXX` to add new device. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 246 ++++++++++++++------------ machine/src/standard_common/mod.rs | 2 +- machine_manager/src/config/devices.rs | 20 ++- 3 files changed, 141 insertions(+), 127 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e9238a3e2..62b3a0116 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -79,8 +79,8 @@ use machine_manager::config::parse_usb_host; use machine_manager::config::scream::parse_scream; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, - parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, - parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, + parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, + parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, BootIndexInfo, BootSource, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, @@ -542,31 +542,35 @@ pub trait MachineOps { let device_cfg = parse_vsock(cfg_args)?; let sys_mem = self.get_sys_mem().clone(); let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); - if cfg_args.contains("vhost-vsock-device") { - let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); - MigrationManager::register_device_instance( - VirtioMmioState::descriptor(), - self.realize_virtio_mmio_device(device) - .with_context(|| MachineError::RlzVirtioMmioErr)?, - &device_cfg.id, - ); - } else { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let mut virtio_pci_device = VirtioPciDevice::new( - device_cfg.id.clone(), - devfn, - sys_mem, - vsock.clone(), - parent_bus, - multi_func, - ); - virtio_pci_device.enable_need_irqfd(); - virtio_pci_device - .realize() - .with_context(|| "Failed to add virtio pci vsock device")?; + match parse_device_type(cfg_args)?.as_str() { + "vhost-vsock-device" => { + let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); + MigrationManager::register_device_instance( + VirtioMmioState::descriptor(), + self.realize_virtio_mmio_device(device) + .with_context(|| MachineError::RlzVirtioMmioErr)?, + &device_cfg.id, + ); + } + _ => { + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let mut virtio_pci_device = VirtioPciDevice::new( + device_cfg.id.clone(), + devfn, + sys_mem, + vsock.clone(), + parent_bus, + multi_func, + ); + virtio_pci_device.enable_need_irqfd(); + virtio_pci_device + .realize() + .with_context(|| "Failed to add virtio pci vsock device")?; + } } + MigrationManager::register_device_instance( VhostKern::VsockState::descriptor(), vsock, @@ -640,20 +644,23 @@ pub trait MachineOps { let sys_mem = self.get_sys_mem(); let balloon = Arc::new(Mutex::new(Balloon::new(&device_cfg, sys_mem.clone()))); Balloon::object_init(balloon.clone()); - if cfg_args.contains("virtio-balloon-device") { - let device = VirtioMmioDevice::new(sys_mem, balloon); - self.realize_virtio_mmio_device(device)?; - } else { - let name = device_cfg.id; - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let sys_mem = self.get_sys_mem().clone(); - let virtio_pci_device = - VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func); - virtio_pci_device - .realize() - .with_context(|| "Failed to add virtio pci balloon device")?; + match parse_device_type(cfg_args)?.as_str() { + "virtio-balloon-device" => { + let device = VirtioMmioDevice::new(sys_mem, balloon); + self.realize_virtio_mmio_device(device)?; + } + _ => { + let name = device_cfg.id; + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let sys_mem = self.get_sys_mem().clone(); + let virtio_pci_device = + VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func); + virtio_pci_device + .realize() + .with_context(|| "Failed to add virtio pci balloon device")?; + } } Ok(()) @@ -670,29 +677,32 @@ pub trait MachineOps { let sys_mem = self.get_sys_mem().clone(); let serial = Arc::new(Mutex::new(Serial::new(serial_cfg.clone()))); - if serial_cfg.pci_bdf.is_none() { - let device = VirtioMmioDevice::new(&sys_mem, serial.clone()); - MigrationManager::register_device_instance( - VirtioMmioState::descriptor(), - self.realize_virtio_mmio_device(device) - .with_context(|| MachineError::RlzVirtioMmioErr)?, - &serial_cfg.id, - ); - } else { - let bdf = serial_cfg.pci_bdf.unwrap(); - let multi_func = serial_cfg.multifunction; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let virtio_pci_device = VirtioPciDevice::new( - serial_cfg.id.clone(), - devfn, - sys_mem, - serial.clone(), - parent_bus, - multi_func, - ); - virtio_pci_device - .realize() - .with_context(|| "Failed to add virtio pci serial device")?; + match parse_device_type(cfg_args)?.as_str() { + "virtio-serial-device" => { + let device = VirtioMmioDevice::new(&sys_mem, serial.clone()); + MigrationManager::register_device_instance( + VirtioMmioState::descriptor(), + self.realize_virtio_mmio_device(device) + .with_context(|| MachineError::RlzVirtioMmioErr)?, + &serial_cfg.id, + ); + } + _ => { + let bdf = serial_cfg.pci_bdf.unwrap(); + let multi_func = serial_cfg.multifunction; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let virtio_pci_device = VirtioPciDevice::new( + serial_cfg.id.clone(), + devfn, + sys_mem, + serial.clone(), + parent_bus, + multi_func, + ); + virtio_pci_device + .realize() + .with_context(|| "Failed to add virtio pci serial device")?; + } } MigrationManager::register_device_instance( @@ -797,27 +807,32 @@ pub trait MachineOps { let device_cfg = parse_rng_dev(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); let rng_dev = Arc::new(Mutex::new(Rng::new(device_cfg.clone()))); - if cfg_args.contains("virtio-rng-device") { - let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); - self.realize_virtio_mmio_device(device) - .with_context(|| "Failed to add virtio mmio rng device")?; - } else { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let sys_mem = self.get_sys_mem().clone(); - let vitio_pci_device = VirtioPciDevice::new( - device_cfg.id.clone(), - devfn, - sys_mem, - rng_dev.clone(), - parent_bus, - multi_func, - ); - vitio_pci_device - .realize() - .with_context(|| "Failed to add pci rng device")?; + + match parse_device_type(cfg_args)?.as_str() { + "virtio-rng-device" => { + let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); + self.realize_virtio_mmio_device(device) + .with_context(|| "Failed to add virtio mmio rng device")?; + } + _ => { + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let sys_mem = self.get_sys_mem().clone(); + let vitio_pci_device = VirtioPciDevice::new( + device_cfg.id.clone(), + devfn, + sys_mem, + rng_dev.clone(), + parent_bus, + multi_func, + ); + vitio_pci_device + .realize() + .with_context(|| "Failed to add pci rng device")?; + } } + MigrationManager::register_device_instance(RngState::descriptor(), rng_dev, &device_cfg.id); Ok(()) } @@ -841,25 +856,26 @@ pub trait MachineOps { bail!("When configuring the vhost-user-fs-device or vhost-user-fs-pci device, the memory must be shared."); } - if cfg_args.contains("vhost-user-fs-device") { - let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); - let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); - self.realize_virtio_mmio_device(virtio_mmio_device) - .with_context(|| "Failed to add vhost user fs device")?; - } else if cfg_args.contains("vhost-user-fs-pci") { - let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - - let mut vitio_pci_device = - VirtioPciDevice::new(id_clone, devfn, sys_mem, device, parent_bus, multi_func); - vitio_pci_device.enable_need_irqfd(); - vitio_pci_device - .realize() - .with_context(|| "Failed to add pci fs device")?; - } else { - bail!("error device type"); + match parse_device_type(cfg_args)?.as_str() { + "vhost-user-fs-device" => { + let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); + let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); + self.realize_virtio_mmio_device(virtio_mmio_device) + .with_context(|| "Failed to add vhost user fs device")?; + } + _ => { + let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); + let bdf = get_pci_bdf(cfg_args)?; + let multi_func = get_multi_function(cfg_args)?; + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + + let mut vitio_pci_device = + VirtioPciDevice::new(id_clone, devfn, sys_mem, device, parent_bus, multi_func); + vitio_pci_device.enable_need_irqfd(); + vitio_pci_device + .realize() + .with_context(|| "Failed to add pci fs device")?; + } } Ok(()) @@ -1103,13 +1119,12 @@ pub trait MachineOps { Ok(()) } - fn add_scsi_device( - &mut self, - vm_config: &mut VmConfig, - cfg_args: &str, - scsi_type: u32, - ) -> Result<()> { + fn add_scsi_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_scsi_device(vm_config, cfg_args)?; + let scsi_type = match parse_device_type(cfg_args)?.as_str() { + "scsi-hd" => SCSI_TYPE_DISK, + _ => SCSI_TYPE_ROM, + }; if let Some(bootindex) = device_cfg.boot_index { self.check_bootindex(bootindex) .with_context(|| "Failed to add scsi device for invalid bootindex")?; @@ -1654,13 +1669,8 @@ pub trait MachineOps { /// /// * `driver` - USB device class. /// * `cfg_args` - USB device Configuration. - fn add_usb_device( - &mut self, - driver: &str, - vm_config: &mut VmConfig, - cfg_args: &str, - ) -> Result<()> { - let usb_device = match driver { + fn add_usb_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + let usb_device = match parse_device_type(cfg_args)?.as_str() { "usb-kbd" => { let device_cfg = parse_usb_keyboard(cfg_args)?; // SAFETY: id is already checked not none in parse_usb_keyboard(). @@ -1750,10 +1760,10 @@ pub trait MachineOps { self.add_virtio_pci_scsi(vm_config, cfg_args, false)?; } "scsi-hd" => { - self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_DISK)?; + self.add_scsi_device(vm_config, cfg_args)?; } "scsi-cd" => { - self.add_scsi_device(vm_config, cfg_args, SCSI_TYPE_ROM)?; + self.add_scsi_device(vm_config, cfg_args)?; } "virtio-net-device" => { self.add_virtio_mmio_net(vm_config, cfg_args)?; @@ -1798,7 +1808,7 @@ pub trait MachineOps { self.add_usb_xhci(cfg_args)?; } "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-storage" | "usb-host" => { - self.add_usb_device(&dev.0, vm_config, cfg_args)?; + self.add_usb_device(vm_config, cfg_args)?; } #[cfg(feature = "virtio_gpu")] "virtio-gpu-pci" => { diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index ea5d040c6..4b831657d 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1141,7 +1141,7 @@ impl DeviceInterface for StdMachine { } "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" => { let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); - if let Err(e) = self.add_usb_device(driver, &mut vm_config_clone, &cfg_args) { + if let Err(e) = self.add_usb_device(&mut vm_config_clone, &cfg_args) { error!("{:?}", e); locked_vmconfig.del_device_by_id(args.id); return Response::create_error_response( diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index fbb7da05e..cf42739b4 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::Result; +use anyhow::{Context, Result}; use regex::Regex; use super::{CmdParser, VmConfig}; @@ -117,13 +117,8 @@ impl VmConfig { } pub fn add_device(&mut self, device_config: &str) -> Result<()> { - let mut cmd_params = CmdParser::new("device"); - cmd_params.push(""); - - cmd_params.get_parameters(device_config)?; - if let Some(device_type) = cmd_params.get_value::("")? { - self.devices.push((device_type, device_config.to_string())); - } + let device_type = parse_device_type(device_config)?; + self.devices.push((device_type, device_config.to_string())); Ok(()) } @@ -141,6 +136,15 @@ impl VmConfig { } } +pub fn parse_device_type(device_config: &str) -> Result { + let mut cmd_params = CmdParser::new("device"); + cmd_params.push(""); + cmd_params.get_parameters(device_config)?; + cmd_params + .get_value::("")? + .with_context(|| "Missing driver field.") +} + pub fn parse_device_id(device_config: &str) -> Result { let mut cmd_parser = CmdParser::new("device"); cmd_parser.push("id"); -- Gitee From 3d2d8d9d98c89ae8a922be48aa9a4fac6399b605 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 30 Jan 2024 03:58:05 +0800 Subject: [PATCH 1561/2187] machine: replace `match` branch with macros Replace match branch in add_devices with macros create_device_add_matches. No functional change. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 130 +++++++++++++++++---------------------------- 1 file changed, 50 insertions(+), 80 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 62b3a0116..b3ec5f98f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -260,6 +260,29 @@ impl MachineBase { } } +macro_rules! create_device_add_matches { + ( $command:expr; $controller: expr; + $(($($driver_name:tt)|+, $function_name:tt, $($arg:tt),*)),*; + $(#[cfg(feature = $feature: tt)] + ($driver_name1:tt, $function_name1:tt, $($arg1:tt),*)),* + ) => { + match $command { + $( + $($driver_name)|+ => { + $controller.$function_name($($arg),*)?; + }, + )* + $( + #[cfg(feature = $feature)] + $driver_name1 => { + $controller.$function_name1($($arg1),*)?; + }, + )* + _ => bail!("Unsupported device: {:?}", $command), + } + }; +} + pub trait MachineOps { fn machine_base(&self) -> &MachineBase; @@ -1749,91 +1772,38 @@ pub trait MachineOps { let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; - match dev.0.as_str() { - "virtio-blk-device" => { - self.add_virtio_mmio_block(vm_config, cfg_args)?; - } - "virtio-blk-pci" => { - self.add_virtio_pci_blk(vm_config, cfg_args, false)?; - } - "virtio-scsi-pci" => { - self.add_virtio_pci_scsi(vm_config, cfg_args, false)?; - } - "scsi-hd" => { - self.add_scsi_device(vm_config, cfg_args)?; - } - "scsi-cd" => { - self.add_scsi_device(vm_config, cfg_args)?; - } - "virtio-net-device" => { - self.add_virtio_mmio_net(vm_config, cfg_args)?; - } - "virtio-net-pci" => { - self.add_virtio_pci_net(vm_config, cfg_args, false)?; - } - "pcie-root-port" => { - self.add_pci_root_port(cfg_args)?; - } - "vhost-vsock-pci" | "vhost-vsock-device" => { - self.add_virtio_vsock(cfg_args)?; - } - "virtio-balloon-device" | "virtio-balloon-pci" => { - self.add_virtio_balloon(vm_config, cfg_args)?; - } - "virtio-serial-device" | "virtio-serial-pci" => { - self.add_virtio_serial(vm_config, cfg_args)?; - } - "virtconsole" => { - self.add_virtio_serial_port(vm_config, cfg_args, true)?; - } - "virtserialport" => { - self.add_virtio_serial_port(vm_config, cfg_args, false)?; - } - "virtio-rng-device" | "virtio-rng-pci" => { - self.add_virtio_rng(vm_config, cfg_args)?; - } - "vfio-pci" => { - self.add_vfio_device(cfg_args, false)?; - } - "vhost-user-blk-device" => { - self.add_vhost_user_blk_device(vm_config, cfg_args)?; - } - "vhost-user-blk-pci" => { - self.add_vhost_user_blk_pci(vm_config, cfg_args, false)?; - } - "vhost-user-fs-pci" | "vhost-user-fs-device" => { - self.add_virtio_fs(vm_config, cfg_args)?; - } - "nec-usb-xhci" => { - self.add_usb_xhci(cfg_args)?; - } - "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-storage" | "usb-host" => { - self.add_usb_device(vm_config, cfg_args)?; - } + + create_device_add_matches!( + dev.0.as_str(); self; + ("virtio-blk-device", add_virtio_mmio_block, vm_config, cfg_args), + ("virtio-blk-pci", add_virtio_pci_blk, vm_config, cfg_args, false), + ("virtio-scsi-pci", add_virtio_pci_scsi, vm_config, cfg_args, false), + ("scsi-hd" | "scsi-cd", add_scsi_device, vm_config, cfg_args), + ("virtio-net-device", add_virtio_mmio_net, vm_config, cfg_args), + ("virtio-net-pci", add_virtio_pci_net, vm_config, cfg_args, false), + ("pcie-root-port", add_pci_root_port, cfg_args), + ("vhost-vsock-pci" | "vhost-vsock-device", add_virtio_vsock, cfg_args), + ("virtio-balloon-device" | "virtio-balloon-pci", add_virtio_balloon, vm_config, cfg_args), + ("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args), + ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args, true), + ("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args), + ("vfio-pci", add_vfio_device, cfg_args, false), + ("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args), + ("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false), + ("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args), + ("nec-usb-xhci", add_usb_xhci, cfg_args), + ("usb-kbd" | "usb-storage" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); #[cfg(feature = "virtio_gpu")] - "virtio-gpu-pci" => { - self.add_virtio_pci_gpu(cfg_args)?; - } + ("virtio-gpu-pci", add_virtio_pci_gpu, cfg_args), #[cfg(feature = "ramfb")] - "ramfb" => { - self.add_ramfb(cfg_args)?; - } + ("ramfb", add_ramfb, cfg_args), #[cfg(feature = "demo_device")] - "pcie-demo-dev" => { - self.add_demo_dev(vm_config, cfg_args)?; - } + ("pcie-demo-dev", add_demo_dev, vm_config, cfg_args), #[cfg(feature = "scream")] - "ivshmem-scream" => { - self.add_ivshmem_scream(vm_config, cfg_args)?; - } + ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args), #[cfg(feature = "pvpanic")] - "pvpanic" => { - self.add_pvpanic(cfg_args)?; - } - _ => { - bail!("Unsupported device: {:?}", dev.0.as_str()); - } - } + ("pvpanic", add_pvpanic, cfg_args) + ); } Ok(()) -- Gitee From 234a769bc5253684e5e3e7f8063ae4c7712f73f7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 1 Feb 2024 06:42:16 +0800 Subject: [PATCH 1562/2187] hypervisor: shield two warnings during test execution Shield two warnings during test execution: warning: unused import: `util::test_helper::is_test_enabled` --> hypervisor/src/kvm/mod.rs:73:5 | 73 | use util::test_helper::is_test_enabled; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: associated function `kvm_vcpu_exec` is never used --> hypervisor/src/kvm/mod.rs:406:8 | 406 | fn kvm_vcpu_exec(&self, cpu: Arc) -> Result { | ^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default Signed-off-by: liuxiangdong --- hypervisor/src/kvm/mod.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 786c65961..459efd344 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -34,11 +34,14 @@ use std::sync::{Arc, Barrier, Mutex}; use std::thread; use std::time::Duration; -use anyhow::{anyhow, bail, Context, Result}; +#[cfg(not(test))] +use anyhow::anyhow; +use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; -use kvm_ioctls::DeviceFd; -use kvm_ioctls::{Cap, Kvm, VcpuExit, VcpuFd, VmFd}; +#[cfg(not(test))] +use kvm_ioctls::VcpuExit; +use kvm_ioctls::{Cap, DeviceFd, Kvm, VcpuFd, VmFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info}; use vmm_sys_util::{ @@ -55,8 +58,10 @@ use address_space::{AddressSpace, Listener}; use cpu::capture_boot_signal; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; +#[cfg(not(test))] +use cpu::CpuError; use cpu::{ - ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuError, + ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuLifecycleState, RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, }; use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, TriggerMode}; @@ -70,6 +75,7 @@ use machine_manager::machine::HypervisorType; #[cfg(target_arch = "aarch64")] use migration::snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}; use migration::{MigrateMemSlot, MigrateOps, MigrationManager}; +#[cfg(not(test))] use util::test_helper::is_test_enabled; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h @@ -403,6 +409,7 @@ impl KvmCpu { Ok(()) } + #[cfg(not(test))] fn kvm_vcpu_exec(&self, cpu: Arc) -> Result { let vm = cpu .vm() -- Gitee From 202631d23f7f665c7a56fa71bb9173af3613be34 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 1 Feb 2024 07:23:25 +0800 Subject: [PATCH 1563/2187] machine: fix virtconsole/virtserialport parameter parsing error Fix error in `add_devices` because it passes a wrong `is_console`. Don't transfer `is_console` and get it by `parse_device_type`. Fix: 3d2d8d9d98c8(machine: replace `match` branch with macros) Signed-off-by: liuxiangdong --- machine/src/lib.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index b3ec5f98f..183a6a8ad 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -744,12 +744,7 @@ pub trait MachineOps { /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. /// * `is_console` - Whether this virtio serial port is a console port. - fn add_virtio_serial_port( - &mut self, - vm_config: &mut VmConfig, - cfg_args: &str, - is_console: bool, - ) -> Result<()> { + fn add_virtio_serial_port(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let serial_cfg = vm_config .virtio_serial .as_ref() @@ -793,6 +788,7 @@ pub trait MachineOps { let mut virtio_dev_h = virtio_dev.lock().unwrap(); let serial = virtio_dev_h.as_any_mut().downcast_mut::().unwrap(); + let is_console = matches!(parse_device_type(cfg_args)?.as_str(), "virtconsole"); let free_port0 = find_port_by_nr(&serial.ports, 0).is_none(); // Note: port 0 is reserved for a virtconsole. let free_nr = get_max_nr(&serial.ports) + 1; @@ -1785,7 +1781,7 @@ pub trait MachineOps { ("vhost-vsock-pci" | "vhost-vsock-device", add_virtio_vsock, cfg_args), ("virtio-balloon-device" | "virtio-balloon-pci", add_virtio_balloon, vm_config, cfg_args), ("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args), - ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args, true), + ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args), ("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args), ("vfio-pci", add_vfio_device, cfg_args, false), ("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args), -- Gitee From 6a2979a9181fe3229a27ec04fa523db189be7527 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 6 Jan 2024 14:51:41 +0800 Subject: [PATCH 1564/2187] Hypervisor: Introduce an abstract hypervisor layer to replace the default KVM interface This commit introduces a new abstract hypervisor layer, providing a consistent interface for managing virtual machines across different hypervisors. The all functionality of previous default KVM interface will be migrated to the new abstract layer. This abstraction allows for easier integration of additional hypervisor types in the future and improves code maintainability. Signed-off-by: Jinhao Gao --- Cargo.lock | 12 ++++ hypervisor/src/kvm/mod.rs | 4 +- hypervisor_refactor/Cargo.toml | 13 +++++ hypervisor_refactor/src/error.rs | 27 +++++++++ hypervisor_refactor/src/kvm/aarch64/mod.rs | 21 +++++++ hypervisor_refactor/src/kvm/mod.rs | 64 ++++++++++++++++++++++ hypervisor_refactor/src/kvm/x86_64/mod.rs | 42 ++++++++++++++ hypervisor_refactor/src/lib.rs | 32 +++++++++++ machine/Cargo.toml | 1 + machine/src/aarch64/micro.rs | 1 + machine/src/aarch64/standard.rs | 1 + machine/src/lib.rs | 37 ++++--------- machine/src/x86_64/micro.rs | 2 +- machine/src/x86_64/standard.rs | 2 +- machine_manager/src/machine.rs | 7 +++ 15 files changed, 237 insertions(+), 29 deletions(-) create mode 100644 hypervisor_refactor/Cargo.toml create mode 100644 hypervisor_refactor/src/error.rs create mode 100644 hypervisor_refactor/src/kvm/aarch64/mod.rs create mode 100644 hypervisor_refactor/src/kvm/mod.rs create mode 100644 hypervisor_refactor/src/kvm/x86_64/mod.rs create mode 100644 hypervisor_refactor/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 94debc3c9..f6d686da3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -735,6 +735,17 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "hypervisor_refactor" +version = "2.3.0" +dependencies = [ + "anyhow", + "kvm-bindings", + "kvm-ioctls", + "machine_manager", + "thiserror", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -909,6 +920,7 @@ dependencies = [ "cpu", "devices", "hypervisor", + "hypervisor_refactor", "kvm-bindings", "libc", "log", diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 16361f4d9..99ef2b9cc 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -100,7 +100,7 @@ ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); #[derive(Default)] pub struct KVMFds { pub fd: Option, - pub vm_fd: Option, + pub vm_fd: Option>, pub irq_route_table: Mutex, pub mem_slots: Arc>>, } @@ -119,7 +119,7 @@ impl KVMFds { let irq_route_table = Mutex::new(IrqRouteTable::new(&fd)); KVMFds { fd: Some(fd), - vm_fd: Some(vm_fd), + vm_fd: Some(Arc::new(vm_fd)), irq_route_table, mem_slots: Arc::new(Mutex::new(HashMap::new())), } diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor_refactor/Cargo.toml new file mode 100644 index 000000000..dce1835f7 --- /dev/null +++ b/hypervisor_refactor/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "hypervisor_refactor" +version = "2.3.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" + +[dependencies] +anyhow = "1.0" +thiserror = "1.0" +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.13.0" +machine_manager = { path = "../machine_manager" } diff --git a/hypervisor_refactor/src/error.rs b/hypervisor_refactor/src/error.rs new file mode 100644 index 000000000..f761f550a --- /dev/null +++ b/hypervisor_refactor/src/error.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Error, Debug)] +pub enum HypervisorError { + #[cfg(target_arch = "x86_64")] + #[error("Failed to set identity map address.")] + SetIdentityMapAddr, + #[cfg(target_arch = "x86_64")] + #[error("Failed to set tss address.")] + SetTssErr, + #[cfg(target_arch = "x86_64")] + #[error("Failed to create PIT.")] + CrtPitErr, +} diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor_refactor/src/kvm/aarch64/mod.rs new file mode 100644 index 000000000..92c0c1329 --- /dev/null +++ b/hypervisor_refactor/src/kvm/aarch64/mod.rs @@ -0,0 +1,21 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::Result; + +use crate::kvm::KvmHypervisor; + +impl KvmHypervisor { + pub fn arch_init(&self) -> Result<()> { + Ok(()) + } +} diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs new file mode 100644 index 000000000..5e10721dd --- /dev/null +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(target_arch = "aarch64")] +pub mod aarch64; + +#[cfg(target_arch = "x86_64")] +pub mod x86_64; + +use std::sync::Arc; + +use anyhow::{bail, Result}; +use kvm_ioctls::{Kvm, VmFd}; + +use super::HypervisorOps; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Default)] +pub struct KvmHypervisor { + pub fd: Option, + pub vm_fd: Option>, +} + +impl KvmHypervisor { + pub fn new(kvm_vm_fd: Option>) -> Result { + match Kvm::new() { + Ok(kvm_fd) => { + let vm_fd: Option> = if kvm_vm_fd.is_some() { + kvm_vm_fd + } else { + Some(Arc::new(match kvm_fd.create_vm() { + Ok(fd) => fd, + Err(e) => { + bail!("Failed to create VM in KVM: {:?}", e); + } + })) + }; + Ok(KvmHypervisor { + fd: Some(kvm_fd), + vm_fd, + }) + } + Err(e) => { + bail!("Failed to open /dev/kvm: {:?}", e) + } + } + } +} + +impl HypervisorOps for KvmHypervisor { + fn init_machine(&self) -> Result<()> { + self.arch_init()?; + Ok(()) + } +} diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor_refactor/src/kvm/x86_64/mod.rs new file mode 100644 index 000000000..eec24d7d8 --- /dev/null +++ b/hypervisor_refactor/src/kvm/x86_64/mod.rs @@ -0,0 +1,42 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{Context, Result}; +use kvm_bindings::*; + +use super::HypervisorError; +use crate::kvm::KvmHypervisor; + +impl KvmHypervisor { + pub fn arch_init(&self) -> Result<()> { + // The identity_addr is set in the memory layout of x86 machine. + let identity_addr: u64 = 0xFEF0_C000; + let vm_fd = self.vm_fd.as_ref().unwrap(); + + vm_fd + .set_identity_map_address(identity_addr) + .with_context(|| HypervisorError::SetIdentityMapAddr)?; + + // Page table takes 1 page, TSS takes the following 3 pages. + vm_fd + .set_tss_address((identity_addr + 0x1000) as usize) + .with_context(|| HypervisorError::SetTssErr)?; + + let pit_config = kvm_pit_config { + flags: KVM_PIT_SPEAKER_DUMMY, + pad: Default::default(), + }; + vm_fd + .create_pit2(pit_config) + .with_context(|| HypervisorError::CrtPitErr) + } +} diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs new file mode 100644 index 000000000..bda5dae3d --- /dev/null +++ b/hypervisor_refactor/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! This crate offers interfaces for different kinds of hypervisors, such as KVM. + +pub mod error; +pub mod kvm; + +pub use error::HypervisorError; + +use std::any::Any; + +use anyhow::Result; + +use machine_manager::machine::HypervisorType; + +pub trait HypervisorOps: Send + Sync + Any { + fn get_hypervisor_type(&self) -> HypervisorType { + HypervisorType::Kvm + } + + fn init_machine(&self) -> Result<()>; +} diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 081d2dc83..e0ca96814 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -20,6 +20,7 @@ boot_loader = { path = "../boot_loader" } cpu = { path = "../cpu" } devices = { path = "../devices" } hypervisor = { path = "../hypervisor" } +hypervisor_refactor = { path = "../hypervisor_refactor"} machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index ab8154080..7e53374a1 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -147,6 +147,7 @@ impl MachineOps for LightMachine { )); trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.hypervisor.lock().unwrap().init_machine()?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.base.sys_mem, diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 007d057a3..fa5b7bc35 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -547,6 +547,7 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to register resume event")?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.hypervisor.lock().unwrap().init_machine()?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.base.sys_mem, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8cb98adc4..fd06334c1 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -73,6 +73,7 @@ use devices::usb::{ use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::kvm::KVM_FDS; +use hypervisor_refactor::{kvm::KvmHypervisor, HypervisorOps}; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; #[cfg(feature = "virtio_gpu")] @@ -148,6 +149,8 @@ pub struct MachineBase { fwcfg_dev: Option>>, /// machine all backend memory region tree machine_ram: Arc, + /// machine hypervisor. + hypervisor: Arc>, } impl MachineBase { @@ -191,6 +194,10 @@ impl MachineBase { mmio_region, ); + let hypervisor = Arc::new(Mutex::new(KvmHypervisor::new( + KVM_FDS.load().vm_fd.clone(), + )?)); + Ok(MachineBase { cpu_topo, cpus: Vec::new(), @@ -207,6 +214,7 @@ impl MachineBase { drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), fwcfg_dev: None, machine_ram, + hypervisor, }) } @@ -499,31 +507,6 @@ pub trait MachineOps { Ok(cpus) } - #[cfg(target_arch = "x86_64")] - fn arch_init(&self, identity_addr: u64) -> Result<()> { - use kvm_bindings::{kvm_pit_config, KVM_PIT_SPEAKER_DUMMY}; - - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - - vm_fd - .set_identity_map_address(identity_addr) - .with_context(|| MachineError::SetIdentityMapAddr)?; - - // Page table takes 1 page, TSS takes the following 3 pages. - vm_fd - .set_tss_address((identity_addr + 0x1000) as usize) - .with_context(|| MachineError::SetTssErr)?; - - let pit_config = kvm_pit_config { - flags: KVM_PIT_SPEAKER_DUMMY, - pad: Default::default(), - }; - vm_fd - .create_pit2(pit_config) - .with_context(|| MachineError::CrtPitErr) - } - /// Must be called after the CPUs have been realized and GIC has been created. /// /// # Arguments @@ -652,6 +635,10 @@ pub trait MachineOps { &self.machine_base().numa_nodes } + fn get_hypervisor(&self) -> Arc> { + self.machine_base().hypervisor.clone() + } + /// Get migration mode and path from VM config. There are four modes in total: /// Tcp, Unix, File and Unknown. fn get_migrate_info(&self) -> Incoming { diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 6d419c172..b706c938d 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -157,6 +157,7 @@ impl MachineOps for LightMachine { )); trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.hypervisor.lock().unwrap().init_machine()?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.base.sys_io, @@ -167,7 +168,6 @@ impl MachineOps for LightMachine { let migrate_info = locked_vm.get_migrate_info(); locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; - locked_vm.arch_init(MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0)?; // Add mmio devices locked_vm diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 110fa2bd8..572318b2e 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -525,6 +525,7 @@ impl MachineOps for StdMachine { let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.base.hypervisor.lock().unwrap().init_machine()?; locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.base.sys_io, @@ -533,7 +534,6 @@ impl MachineOps for StdMachine { )?; locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; - locked_vm.arch_init(MEM_LAYOUT[LayoutEntryType::IdentTss as usize].0)?; locked_vm .init_pci_host() diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 4c47199c7..a012bf206 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -43,6 +43,13 @@ pub enum KvmVmState { Shutdown = 6, } +/// Type for Hypervisor. +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] +pub enum HypervisorType { + #[default] + Kvm, +} + /// Trait to handle virtual machine lifecycle. /// /// # Notes -- Gitee From db6c804d06c60f4df8f004f241a5478c7fd4c166 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 22 Dec 2023 17:19:36 +0800 Subject: [PATCH 1565/2187] Refactor: Replace KvmVmState with VmState as a generic VM state Introduces a new VM state representation, VmState, to replace the previous KvmVmState. The VmState abstraction provides a unified and extensible approach for representing the state of virtual machines across different hypervisor types. Signed-off-by: Jinhao Gao --- cpu/src/lib.rs | 4 +- .../src/interrupt_controller/aarch64/gicv2.rs | 14 +++--- .../src/interrupt_controller/aarch64/gicv3.rs | 14 +++--- .../src/interrupt_controller/aarch64/mod.rs | 5 +- machine/src/aarch64/standard.rs | 14 +++--- machine/src/lib.rs | 46 +++++++++---------- machine/src/micro_common/mod.rs | 18 ++++---- machine/src/standard_common/mod.rs | 6 +-- machine/src/x86_64/standard.rs | 14 +++--- machine_manager/src/machine.rs | 24 +++++----- 10 files changed, 79 insertions(+), 80 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index b2d7843c0..40218d878 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -860,7 +860,7 @@ mod tests { use super::*; use hypervisor::kvm::{KVMFds, KVM_FDS}; use machine_manager::machine::{ - KvmVmState, MachineAddressInterface, MachineInterface, MachineLifecycle, + MachineAddressInterface, MachineInterface, MachineLifecycle, VmState, }; struct TestVm { @@ -886,7 +886,7 @@ mod tests { } impl MachineLifecycle for TestVm { - fn notify_lifecycle(&self, _old: KvmVmState, _new: KvmVmState) -> bool { + fn notify_lifecycle(&self, _old: VmState, _new: VmState) -> bool { true } } diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index 4727d97e5..319eb4f5a 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -21,7 +21,7 @@ use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; use crate::interrupt_controller::InterruptError; use address_space::AddressSpace; use hypervisor::kvm::KVM_FDS; -use machine_manager::machine::{KvmVmState, MachineLifecycle}; +use machine_manager::machine::{MachineLifecycle, VmState}; use util::device_tree::{self, FdtBuilder}; // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. @@ -81,7 +81,7 @@ pub struct GICv2 { /// Guest physical address space of the GICv2 distributor register mappings. dist_guest_region: GicDistGuestRegion, /// Lifecycle state for GICv2. - state: Arc>, + state: Arc>, } impl GICv2 { @@ -123,7 +123,7 @@ impl GICv2 { nr_irqs: config.max_irq, cpu_interface_region, dist_guest_region, - state: Arc::new(Mutex::new(KvmVmState::Created)), + state: Arc::new(Mutex::new(VmState::Created)), }; Ok(gicv2) @@ -170,7 +170,7 @@ impl GICv2 { ) .with_context(|| "Failed to set GICv2 attribute: cpu address")?; - *self.state.lock().unwrap() = KvmVmState::Running; + *self.state.lock().unwrap() = VmState::Running; Ok(()) } @@ -192,14 +192,14 @@ impl MachineLifecycle for GICv2 { ) .is_ok() { - *self.state.lock().unwrap() = KvmVmState::Running; + *self.state.lock().unwrap() = VmState::Running; true } else { false } } - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { let state = self.state.lock().unwrap(); if *state != old { error!("GICv2 lifecycle error: state check failed."); @@ -208,7 +208,7 @@ impl MachineLifecycle for GICv2 { drop(state); match (old, new) { - (KvmVmState::Running, KvmVmState::Paused) => self.pause(), + (VmState::Running, VmState::Paused) => self.pause(), _ => true, } } diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 2ebdf1e8f..16d13a241 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -22,7 +22,7 @@ use super::{ }; use crate::interrupt_controller::error::InterruptError; use hypervisor::kvm::KVM_FDS; -use machine_manager::machine::{KvmVmState, MachineLifecycle}; +use machine_manager::machine::{MachineLifecycle, VmState}; use migration::{ snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, MigrationManager, StateTransfer, @@ -98,7 +98,7 @@ pub struct GICv3 { /// GICv3 distributor region size. dist_size: u64, /// Lifecycle state for GICv3. - state: Arc>, + state: Arc>, } impl GICv3 { @@ -159,7 +159,7 @@ impl GICv3 { redist_regions, dist_base: v3config.dist_range.0, dist_size: v3config.dist_range.1, - state: Arc::new(Mutex::new(KvmVmState::Created)), + state: Arc::new(Mutex::new(VmState::Created)), }; if let Some(its_range) = v3config.its_range { @@ -237,7 +237,7 @@ impl GICv3 { .with_context(|| "KVM failed to initialize GICv3")?; let mut state = self.state.lock().unwrap(); - *state = KvmVmState::Running; + *state = VmState::Running; Ok(()) } @@ -285,12 +285,12 @@ impl MachineLifecycle for GICv3 { } let mut state = self.state.lock().unwrap(); - *state = KvmVmState::Running; + *state = VmState::Running; true } - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { let state = self.state.lock().unwrap(); if *state != old { error!("GICv3 lifecycle error: state check failed."); @@ -299,7 +299,7 @@ impl MachineLifecycle for GICv3 { drop(state); match (old, new) { - (KvmVmState::Running, KvmVmState::Paused) => self.pause(), + (VmState::Running, VmState::Paused) => self.pause(), _ => true, } } diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index bfad965df..046d0c403 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -24,7 +24,7 @@ use anyhow::{anyhow, Context, Result}; use kvm_ioctls::DeviceFd; use crate::interrupt_controller::error::InterruptError; -use machine_manager::machine::{KvmVmState, MachineLifecycle}; +use machine_manager::machine::{MachineLifecycle, VmState}; use util::{ device_tree::{self, FdtBuilder}, Result as UtilResult, @@ -182,8 +182,7 @@ impl InterruptController { /// Change `InterruptController` lifecycle state to `Stopped`. pub fn stop(&self) { - self.gic - .notify_lifecycle(KvmVmState::Running, KvmVmState::Paused); + self.gic.notify_lifecycle(VmState::Running, VmState::Paused); } pub fn get_redist_count(&self) -> u8 { diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index fa5b7bc35..274273ecc 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -62,8 +62,8 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - KvmVmState, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, - MigrateInterface, + MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, + MigrateInterface, VmState, }; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; @@ -1135,7 +1135,7 @@ impl AcpiBuilder for StdMachine { impl MachineLifecycle for StdMachine { fn pause(&self) -> bool { - if self.notify_lifecycle(KvmVmState::Running, KvmVmState::Paused) { + if self.notify_lifecycle(VmState::Running, VmState::Paused) { event!(Stop); true } else { @@ -1144,7 +1144,7 @@ impl MachineLifecycle for StdMachine { } fn resume(&self) -> bool { - if !self.notify_lifecycle(KvmVmState::Paused, KvmVmState::Running) { + if !self.notify_lifecycle(VmState::Paused, VmState::Running) { return false; } event!(Resume); @@ -1157,7 +1157,7 @@ impl MachineLifecycle for StdMachine { *state }; - if !self.notify_lifecycle(vmstate, KvmVmState::Shutdown) { + if !self.notify_lifecycle(vmstate, VmState::Shutdown) { warn!("Failed to destroy guest, destroy continue."); if self.shutdown_req.write(1).is_err() { error!("Failed to send shutdown request.") @@ -1196,7 +1196,7 @@ impl MachineLifecycle for StdMachine { true } - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { if let Err(e) = self.vm_state_transfer( &self.base.cpus, &self.base.irq_chip, @@ -1240,7 +1240,7 @@ impl MachineTestInterface for StdMachine {} impl EventLoopManager for StdMachine { fn loop_should_exit(&self) -> bool { let vmstate = self.base.vm_state.deref().0.lock().unwrap(); - *vmstate == KvmVmState::Shutdown + *vmstate == VmState::Shutdown } fn loop_cleanup(&self) -> util::Result<()> { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fd06334c1..05f7a2c66 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -98,7 +98,7 @@ use machine_manager::config::{ parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, }; use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{KvmVmState, MachineInterface}; +use machine_manager::machine::{MachineInterface, VmState}; use migration::MigrationManager; use standard_common::Result as StdResult; #[cfg(feature = "windows_emu_pid")] @@ -136,7 +136,7 @@ pub struct MachineBase { /// System bus. sysbus: SysBus, /// VM running state. - vm_state: Arc<(Mutex, Condvar)>, + vm_state: Arc<(Mutex, Condvar)>, /// Vm boot_source config. boot_source: Arc>, /// All configuration information of virtual machine. @@ -207,7 +207,7 @@ impl MachineBase { #[cfg(target_arch = "x86_64")] sys_io, sysbus, - vm_state: Arc::new((Mutex::new(KvmVmState::Created), Condvar::new())), + vm_state: Arc::new((Mutex::new(VmState::Created), Condvar::new())), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_config: Arc::new(Mutex::new(vm_config.clone())), numa_nodes: None, @@ -623,7 +623,7 @@ pub trait MachineOps { self.machine_base().vm_config.clone() } - fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { + fn get_vm_state(&self) -> &Arc<(Mutex, Condvar)> { &self.machine_base().vm_state } @@ -1928,7 +1928,7 @@ pub trait MachineOps { // Lock the added file if VM is running. let drive_file = drive_files.get_mut(path).unwrap(); let vm_state = self.get_vm_state().deref().0.lock().unwrap(); - if *vm_state == KvmVmState::Running && !drive_file.locked { + if *vm_state == VmState::Running && !drive_file.locked { if let Err(e) = lock_file(&drive_file.file, path, read_only) { VmConfig::remove_drive_file(&mut drive_files, path)?; return Err(e); @@ -1998,8 +1998,8 @@ pub trait MachineOps { /// /// * `paused` - After started, paused all vcpu or not. /// * `cpus` - Cpus vector restore cpu structure. - /// * `vm_state` - Vm kvm vm state. - fn vm_start(&self, paused: bool, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { + /// * `vm_state` - Vm state. + fn vm_start(&self, paused: bool, cpus: &[Arc], vm_state: &mut VmState) -> Result<()> { if !paused { EventLoop::get_ctx(None).unwrap().enable_clock(); self.active_drive_files()?; @@ -2016,9 +2016,9 @@ pub trait MachineOps { } if paused { - *vm_state = KvmVmState::Paused; + *vm_state = VmState::Paused; } else { - *vm_state = KvmVmState::Running; + *vm_state = VmState::Running; } cpus_thread_barrier.wait(); @@ -2030,12 +2030,12 @@ pub trait MachineOps { /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. - /// * `vm_state` - Vm kvm vm state. + /// * `vm_state` - Vm state. fn vm_pause( &self, cpus: &[Arc], #[cfg(target_arch = "aarch64")] irq_chip: &Option>, - vm_state: &mut KvmVmState, + vm_state: &mut VmState, ) -> Result<()> { EventLoop::get_ctx(None).unwrap().disable_clock(); @@ -2052,7 +2052,7 @@ pub trait MachineOps { // SAFETY: ARM architecture must have interrupt controllers in user mode. irq_chip.as_ref().unwrap().stop(); - *vm_state = KvmVmState::Paused; + *vm_state = VmState::Paused; Ok(()) } @@ -2062,8 +2062,8 @@ pub trait MachineOps { /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. - /// * `vm_state` - Vm kvm vm state. - fn vm_resume(&self, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { + /// * `vm_state` - Vm state. + fn vm_resume(&self, cpus: &[Arc], vm_state: &mut VmState) -> Result<()> { EventLoop::get_ctx(None).unwrap().enable_clock(); self.active_drive_files()?; @@ -2075,7 +2075,7 @@ pub trait MachineOps { } } - *vm_state = KvmVmState::Running; + *vm_state = VmState::Running; Ok(()) } @@ -2085,14 +2085,14 @@ pub trait MachineOps { /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. - /// * `vm_state` - Vm kvm vm state. - fn vm_destroy(&self, cpus: &[Arc], vm_state: &mut KvmVmState) -> Result<()> { + /// * `vm_state` - Vm state. + fn vm_destroy(&self, cpus: &[Arc], vm_state: &mut VmState) -> Result<()> { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.destroy() .with_context(|| format!("Failed to destroy vcpu{}", cpu_index))?; } - *vm_state = KvmVmState::Shutdown; + *vm_state = VmState::Shutdown; Ok(()) } @@ -2102,18 +2102,18 @@ pub trait MachineOps { /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. - /// * `vm_state` - Vm kvm vm state. + /// * `vm_state` - Vm state. /// * `old_state` - Old vm state want to leave. /// * `new_state` - New vm state want to transfer to. fn vm_state_transfer( &self, cpus: &[Arc], #[cfg(target_arch = "aarch64")] irq_chip: &Option>, - vm_state: &mut KvmVmState, - old_state: KvmVmState, - new_state: KvmVmState, + vm_state: &mut VmState, + old_state: VmState, + new_state: VmState, ) -> Result<()> { - use KvmVmState::*; + use VmState::*; if *vm_state != old_state { bail!("Vm lifecycle error: state check failed."); diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 4d4b2df56..6ca6c6db4 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -54,8 +54,8 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, - MachineInterface, MachineLifecycle, MigrateInterface, + DeviceInterface, MachineAddressInterface, MachineExternalInterface, MachineInterface, + MachineLifecycle, MigrateInterface, VmState, }; use machine_manager::qmp::{ qmp_channel::QmpChannel, qmp_response::Response, qmp_schema, qmp_schema::UpdateRegionArgument, @@ -433,7 +433,7 @@ impl LightMachine { impl MachineLifecycle for LightMachine { fn pause(&self) -> bool { - if self.notify_lifecycle(KvmVmState::Running, KvmVmState::Paused) { + if self.notify_lifecycle(VmState::Running, VmState::Paused) { event!(Stop); true } else { @@ -442,7 +442,7 @@ impl MachineLifecycle for LightMachine { } fn resume(&self) -> bool { - if !self.notify_lifecycle(KvmVmState::Paused, KvmVmState::Running) { + if !self.notify_lifecycle(VmState::Paused, VmState::Running) { return false; } @@ -456,7 +456,7 @@ impl MachineLifecycle for LightMachine { *state }; - if !self.notify_lifecycle(vmstate, KvmVmState::Shutdown) { + if !self.notify_lifecycle(vmstate, VmState::Shutdown) { return false; } @@ -476,7 +476,7 @@ impl MachineLifecycle for LightMachine { self.destroy() } - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { if let Err(e) = self.vm_state_transfer( &self.base.cpus, #[cfg(target_arch = "aarch64")] @@ -522,12 +522,12 @@ impl DeviceInterface for LightMachine { fn query_status(&self) -> Response { let vmstate = self.get_vm_state().deref().0.lock().unwrap(); let qmp_state = match *vmstate { - KvmVmState::Running => qmp_schema::StatusInfo { + VmState::Running => qmp_schema::StatusInfo { singlestep: false, running: true, status: qmp_schema::RunState::running, }, - KvmVmState::Paused => qmp_schema::StatusInfo { + VmState::Paused => qmp_schema::StatusInfo { singlestep: false, running: false, status: qmp_schema::RunState::paused, @@ -942,7 +942,7 @@ impl MachineExternalInterface for LightMachine {} impl EventLoopManager for LightMachine { fn loop_should_exit(&self) -> bool { let vmstate = self.base.vm_state.deref().0.lock().unwrap(); - *vmstate == KvmVmState::Shutdown + *vmstate == VmState::Shutdown } fn loop_cleanup(&self) -> util::Result<()> { diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 09900acd8..d3a47a5aa 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -68,7 +68,7 @@ use machine_manager::config::{ }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - DeviceInterface, KvmVmState, MachineAddressInterface, MachineLifecycle, + DeviceInterface, MachineAddressInterface, MachineLifecycle, VmState, }; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; @@ -953,12 +953,12 @@ impl DeviceInterface for StdMachine { let vm_state = self.get_vm_state(); let vmstate = vm_state.deref().0.lock().unwrap(); let qmp_state = match *vmstate { - KvmVmState::Running => qmp_schema::StatusInfo { + VmState::Running => qmp_schema::StatusInfo { singlestep: false, running: true, status: qmp_schema::RunState::running, }, - KvmVmState::Paused => qmp_schema::StatusInfo { + VmState::Paused => qmp_schema::StatusInfo { singlestep: false, running: false, status: qmp_schema::RunState::paused, diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 572318b2e..a549a4755 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -49,8 +49,8 @@ use machine_manager::config::{ use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - KvmVmState, MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, - MigrateInterface, + MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, + MigrateInterface, VmState, }; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; @@ -969,7 +969,7 @@ impl AcpiBuilder for StdMachine { impl MachineLifecycle for StdMachine { fn pause(&self) -> bool { - if self.notify_lifecycle(KvmVmState::Running, KvmVmState::Paused) { + if self.notify_lifecycle(VmState::Running, VmState::Paused) { event!(Stop); true } else { @@ -978,7 +978,7 @@ impl MachineLifecycle for StdMachine { } fn resume(&self) -> bool { - if !self.notify_lifecycle(KvmVmState::Paused, KvmVmState::Running) { + if !self.notify_lifecycle(VmState::Paused, VmState::Running) { return false; } event!(Resume); @@ -991,7 +991,7 @@ impl MachineLifecycle for StdMachine { *state }; - if !self.notify_lifecycle(vmstate, KvmVmState::Shutdown) { + if !self.notify_lifecycle(vmstate, VmState::Shutdown) { return false; } @@ -1009,7 +1009,7 @@ impl MachineLifecycle for StdMachine { true } - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool { + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { if let Err(e) = self.vm_state_transfer( &self.base.cpus, &mut self.base.vm_state.0.lock().unwrap(), @@ -1052,7 +1052,7 @@ impl MachineTestInterface for StdMachine {} impl EventLoopManager for StdMachine { fn loop_should_exit(&self) -> bool { let vmstate = self.base.vm_state.deref().0.lock().unwrap(); - *vmstate == KvmVmState::Shutdown + *vmstate == VmState::Shutdown } fn loop_cleanup(&self) -> util::Result<()> { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index a012bf206..472ca6286 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -32,9 +32,9 @@ pub struct PathInfo { pub label: String, } -/// State for KVM VM. +/// State for VM. #[derive(PartialEq, Eq, Copy, Clone, Debug)] -pub enum KvmVmState { +pub enum VmState { Created = 1, Running = 2, InMigrating = 3, @@ -60,7 +60,7 @@ pub enum HypervisorType { /// `Created` --`(start)`--> `Running` /// `Running` --`(pause)`--> `Paused` /// `Paused` --`(resume)`--> `Running` -/// `KVM_VMSTATE_*` --`(destroy)`--> `None` +/// `VMSTATE_*` --`(destroy)`--> `None` /// /// **Notice**: /// 1. Migrate state(`Migrated` and `InMigrating`), @@ -74,42 +74,42 @@ pub enum HypervisorType { pub trait MachineLifecycle { /// Start VM or Device, VM or Device enter running state after this call return. fn start(&self) -> bool { - self.notify_lifecycle(KvmVmState::Created, KvmVmState::Paused) + self.notify_lifecycle(VmState::Created, VmState::Paused) } /// Pause VM or Device, VM or Device will temporarily stored in memory until it resumed /// or destroyed. fn pause(&self) -> bool { - self.notify_lifecycle(KvmVmState::Running, KvmVmState::Paused) + self.notify_lifecycle(VmState::Running, VmState::Paused) } /// Resume VM or Device, resume VM state to running state after this call return. fn resume(&self) -> bool { - self.notify_lifecycle(KvmVmState::Paused, KvmVmState::Running) + self.notify_lifecycle(VmState::Paused, VmState::Running) } /// Close VM or Device, stop running. fn destroy(&self) -> bool { - self.notify_lifecycle(KvmVmState::Running, KvmVmState::Shutdown) + self.notify_lifecycle(VmState::Running, VmState::Shutdown) } /// Close VM by power_button. fn powerdown(&self) -> bool { - self.notify_lifecycle(KvmVmState::Running, KvmVmState::Shutdown) + self.notify_lifecycle(VmState::Running, VmState::Shutdown) } /// Reset VM, stop running and restart a new VM. fn reset(&mut self) -> bool { - self.notify_lifecycle(KvmVmState::Running, KvmVmState::Shutdown) + self.notify_lifecycle(VmState::Running, VmState::Shutdown) } /// When VM or Device life state changed, notify concerned entry. /// /// # Arguments /// - /// * `old` - The current `KvmVmState`. - /// * `new` - The new `KvmVmState` expected to transform. - fn notify_lifecycle(&self, old: KvmVmState, new: KvmVmState) -> bool; + /// * `old` - The current `VmState`. + /// * `new` - The new `VmState` expected to transform. + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool; /// Get shutdown_action to determine the poweroff operation. fn get_shutdown_action(&self) -> ShutdownAction { -- Gitee From c7b21dd6657f46c449d1fa52986b309d9bc2b931 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 22 Dec 2023 17:29:35 +0800 Subject: [PATCH 1566/2187] Memory: Move memory functionalities of hypervisor-related to the hypervisor layer Aims to refactor the codebase by moving the memory-related functionalities that were previously specific to KVM to the hypervisor layer. This includes the registration of memory listeners and the handling of dirty page tracking during live migration. Signed-off-by: Jinhao Gao --- Cargo.lock | 7 + address_space/src/lib.rs | 5 +- address_space/src/listener.rs | 813 +--------------------- address_space/src/region.rs | 3 +- hypervisor/src/kvm/mod.rs | 93 --- hypervisor_refactor/Cargo.toml | 6 + hypervisor_refactor/src/kvm/listener.rs | 807 +++++++++++++++++++++ hypervisor_refactor/src/kvm/mod.rs | 120 +++- hypervisor_refactor/src/kvm/x86_64/mod.rs | 10 +- hypervisor_refactor/src/lib.rs | 12 +- machine/src/aarch64/micro.rs | 5 +- machine/src/aarch64/standard.rs | 5 +- machine/src/lib.rs | 28 +- machine/src/standard_common/syscall.rs | 1 + machine/src/x86_64/micro.rs | 6 +- machine/src/x86_64/standard.rs | 6 +- machine_manager/Cargo.toml | 1 + migration/src/lib.rs | 25 + migration/src/manager.rs | 13 + migration/src/migration.rs | 77 +- 20 files changed, 1070 insertions(+), 973 deletions(-) create mode 100644 hypervisor_refactor/src/kvm/listener.rs diff --git a/Cargo.lock b/Cargo.lock index f6d686da3..da0676ce0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,11 +739,17 @@ dependencies = [ name = "hypervisor_refactor" version = "2.3.0" dependencies = [ + "address_space", "anyhow", "kvm-bindings", "kvm-ioctls", + "libc", + "log", "machine_manager", + "migration", "thiserror", + "util", + "vmm-sys-util", ] [[package]] @@ -943,6 +949,7 @@ version = "2.3.0" dependencies = [ "anyhow", "hex", + "kvm-bindings", "libc", "log", "once_cell", diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index 3507e769d..b35ca5473 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -95,10 +95,7 @@ pub use crate::address_space::{AddressSpace, RegionCache}; pub use address::{AddressRange, GuestAddress}; pub use error::AddressSpaceError; pub use host_mmap::{create_backend_mem, create_default_mem, FileBackend, HostMemMapping}; -#[cfg(target_arch = "x86_64")] -pub use listener::KvmIoListener; -pub use listener::KvmMemoryListener; -pub use listener::{Listener, ListenerReqType}; +pub use listener::{Listener, ListenerReqType, MemSlot}; pub use region::{FlatRange, Region, RegionIoEventFd, RegionType}; /// Read data from Region to argument `data`, diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 117d74187..586192ceb 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -10,17 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::atomic::{AtomicU32, Ordering}; -use std::sync::{Arc, Mutex}; +use anyhow::Result; -use anyhow::{anyhow, bail, Context, Result}; -use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_READONLY}; -use kvm_ioctls::{IoEventAddress, NoDatamatch}; -use log::{debug, warn}; - -use crate::{AddressRange, AddressSpaceError, FlatRange, RegionIoEventFd, RegionType}; -use hypervisor::kvm::KVM_FDS; -use util::{num_ops::round_down, unix::host_page_size}; +use crate::{FlatRange, RegionIoEventFd}; /// Request type of listener. #[derive(Debug, Copy, Clone)] @@ -67,805 +59,14 @@ pub trait Listener: Send + Sync { /// Records information that manage the slot resource and current usage. #[derive(Default, Copy, Clone)] -struct MemSlot { +pub struct MemSlot { /// Index of a memory slot. - index: u32, + pub index: u32, /// Guest address. - guest_addr: u64, + pub guest_addr: u64, /// Size of memory. /// size = 0 represents no-region use this slot. - size: u64, + pub size: u64, /// Host address. - host_addr: u64, -} - -/// Kvm memory listener. -#[derive(Clone)] -pub struct KvmMemoryListener { - /// Id of AddressSpace. - as_id: Arc, - /// Record all MemSlots. - slots: Arc>>, - /// Whether enabled as a memory listener. - enabled: bool, -} - -impl KvmMemoryListener { - /// Create a new KvmMemoryListener for a VM. - /// - /// # Arguments - /// - /// * `nr_slots` - Number of slots. - pub fn new(nr_slots: u32) -> KvmMemoryListener { - KvmMemoryListener { - as_id: Arc::new(AtomicU32::new(0)), - slots: Arc::new(Mutex::new(vec![MemSlot::default(); nr_slots as usize])), - enabled: false, - } - } - - /// Find a free slot and fills it with given arguments. - /// - /// # Arguments - /// - /// * `guest_addr` - Guest address. - /// * `size` - Size of slot. - /// * `host_addr` - Host address. - /// - /// # Errors - /// - /// Return Error if - /// * No available Kvm slot. - /// * Given memory slot overlap with existed one. - fn get_free_slot(&self, guest_addr: u64, size: u64, host_addr: u64) -> Result { - let mut slots = self.slots.lock().unwrap(); - - // check if the given address range overlaps with exist ones - let range = AddressRange::from((guest_addr, size)); - slots.iter().try_for_each::<_, Result<()>>(|s| { - if AddressRange::from((s.guest_addr, s.size)) - .find_intersection(range) - .is_some() - { - return Err(anyhow!(AddressSpaceError::KvmSlotOverlap { - add: (guest_addr, size), - exist: (s.guest_addr, s.size) - })); - } - Ok(()) - })?; - - for (index, slot) in slots.iter_mut().enumerate() { - if slot.size == 0 { - slot.index = index as u32; - slot.guest_addr = guest_addr; - slot.size = size; - slot.host_addr = host_addr; - return Ok(slot.index); - } - } - - Err(anyhow!(AddressSpaceError::NoAvailKvmSlot(slots.len()))) - } - - /// Delete a slot after finding it according to the given arguments. - /// Return the deleted one if succeed. - /// - /// # Arguments - /// - /// * `addr` - Guest address of slot. - /// * `size` - Size of slots. - /// - /// # Errors - /// - /// Return Error if no Kem slot matched. - fn delete_slot(&self, addr: u64, size: u64) -> Result { - let mut slots = self.slots.lock().unwrap(); - for slot in slots.iter_mut() { - if slot.guest_addr == addr && slot.size == size { - // set slot size to zero, so it can be reused later - slot.size = 0; - return Ok(*slot); - } - } - Err(anyhow!(AddressSpaceError::NoMatchedKvmSlot(addr, size))) - } - - /// Align a piece of memory segment according to `alignment`, - /// return AddressRange after aligned. - /// - /// # Arguments - /// - /// * `range` - One piece of memory segment. - /// * `alignment` - Alignment base. - /// - /// # Errors - /// - /// Return Error if Memslot size is zero after aligned. - fn align_mem_slot(range: AddressRange, alignment: u64) -> Result { - let aligned_addr = range - .base - .align_up(alignment) - .with_context(|| AddressSpaceError::AddrAlignUp(range.base.raw_value(), alignment))?; - - let aligned_size = range - .size - .checked_sub(aligned_addr.offset_from(range.base)) - .and_then(|sz| round_down(sz, alignment)) - .filter(|&sz| sz > 0_u64) - .with_context(|| - format!("Mem slot size is zero after aligned, addr 0x{:X}, size 0x{:X}, alignment 0x{:X}", - range.base.raw_value(), range.size, alignment) - )?; - - Ok(AddressRange::new(aligned_addr, aligned_size)) - } - - /// Callback function for adding Region, which only care about Ram-type Region yet. - /// - /// # Arguments - /// - /// * `flat_range` - Corresponding FlatRange of new-added region. - /// - /// # Errors - /// - /// Return Error if fail to delete kvm_mem_slot. - fn add_region(&self, flat_range: &FlatRange) -> Result<()> { - if flat_range.owner.region_type() == RegionType::RomDevice - && !flat_range.owner.get_rom_device_romd().unwrap() - { - if let Err(ref e) = self.delete_region(flat_range) { - warn!( - "Rom-device Region changes to IO mode, Failed to delete region: {:?}", - e - ); - } - return Ok(()); - } - - if flat_range.owner.region_type() != RegionType::Ram - && flat_range.owner.region_type() != RegionType::RomDevice - && flat_range.owner.region_type() != RegionType::RamDevice - { - return Ok(()); - } - - let (aligned_addr, aligned_size) = - Self::align_mem_slot(flat_range.addr_range, host_page_size()) - .map(|r| (r.base, r.size)) - .with_context(|| "Failed to align mem slot")?; - let align_adjust = aligned_addr.raw_value() - flat_range.addr_range.base.raw_value(); - - // `unwrap()` won't fail because Ram-type Region definitely has hva - let aligned_hva = flat_range.owner.get_host_address().unwrap() - + flat_range.offset_in_region - + align_adjust; - - let slot_idx = self - .get_free_slot(aligned_addr.raw_value(), aligned_size, aligned_hva) - .with_context(|| "Failed to get available KVM mem slot")?; - - let mut flags = 0_u32; - if flat_range.owner.get_rom_device_romd().unwrap_or(false) { - flags |= KVM_MEM_READONLY; - } - let kvm_region = kvm_userspace_memory_region { - slot: slot_idx | (self.as_id.load(Ordering::SeqCst) << 16), - guest_phys_addr: aligned_addr.raw_value(), - memory_size: aligned_size, - userspace_addr: aligned_hva, - flags, - }; - - KVM_FDS - .load() - .add_mem_slot(kvm_region) - .with_context(|| "Failed to add memory slot to kvm")?; - - // SAFETY: All parameters in the struct of kvm_region are valid, - // it can be guaranteed that calling the ioctl_with_ref in the function - // of set_user_memory_region is safe. - unsafe { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .set_user_memory_region(kvm_region) - .or_else(|e| { - self.delete_slot(aligned_addr.raw_value(), aligned_size) - .with_context(|| "Failed to delete Kvm mem slot")?; - Err(e).with_context(|| { - format!( - "KVM register memory region failed: addr 0x{:X}, size 0x{:X}", - aligned_addr.raw_value(), - aligned_size - ) - }) - })?; - } - Ok(()) - } - - /// Callback function for deleting Region, which only care about Ram-type Region yet. - /// - /// # Arguments - /// - /// * `flat_range` - Corresponding FlatRange of new-deleted region. - fn delete_region(&self, flat_range: &FlatRange) -> Result<()> { - if flat_range.owner.region_type() != RegionType::Ram - && flat_range.owner.region_type() != RegionType::RomDevice - && flat_range.owner.region_type() != RegionType::RamDevice - { - return Ok(()); - } - - let (aligned_addr, aligned_size) = - Self::align_mem_slot(flat_range.addr_range, host_page_size()) - .map(|r| (r.base, r.size)) - .with_context(|| "Failed to align mem slot")?; - - let mem_slot = match self.delete_slot(aligned_addr.raw_value(), aligned_size) { - Ok(m) => m, - Err(_) => { - debug!("no match mem slot registered to KVM, just return"); - return Ok(()); - } - }; - - let kvm_region = kvm_userspace_memory_region { - slot: mem_slot.index | (self.as_id.load(Ordering::SeqCst) << 16), - guest_phys_addr: mem_slot.guest_addr, - memory_size: 0_u64, - userspace_addr: mem_slot.host_addr, - flags: 0, - }; - - KVM_FDS - .load() - .remove_mem_slot(kvm_region) - .with_context(|| "Failed to remove memory slot to kvm")?; - - // SAFETY: All parameters in the struct of kvm_region are valid, - // it can be guaranteed that calling the ioctl_with_ref in the function - // of set_user_memory_region is safe. - unsafe { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .set_user_memory_region(kvm_region) - .with_context(|| { - format!( - "KVM unregister memory region failed: addr 0x{:X}", - aligned_addr.raw_value(), - ) - })?; - } - - Ok(()) - } - - /// Register a IoEvent to `/dev/kvm`. - /// - /// # Arguments - /// - /// * `ioevtfd` - IoEvent would be added. - /// - /// # Errors - /// - /// Return Error if the length of ioeventfd data is unexpected or syscall failed. - fn add_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let io_addr = IoEventAddress::Mmio(ioevtfd.addr_range.base.raw_value()); - let ioctl_ret = if ioevtfd.data_match { - let length = ioevtfd.addr_range.size; - match length { - 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), - 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), - _ => bail!("Unexpected ioeventfd data length {}", length), - } - } else { - vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) - }; - - ioctl_ret.with_context(|| { - format!( - "KVM register ioeventfd failed, mmio addr 0x{:X}, size 0x{:X}, data_match {}", - ioevtfd.addr_range.base.raw_value(), - ioevtfd.addr_range.size, - if ioevtfd.data_match { - ioevtfd.data - } else { - u64::MAX - } - ) - })?; - - Ok(()) - } - - /// Deletes `ioevtfd` from `/dev/kvm` - /// - /// # Arguments - /// - /// * `ioevtfd` - IoEvent would be deleted. - fn delete_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let io_addr = IoEventAddress::Mmio(ioevtfd.addr_range.base.raw_value()); - let ioctl_ret = if ioevtfd.data_match { - let length = ioevtfd.addr_range.size; - match length { - 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), - 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), - _ => bail!("Unexpected ioeventfd data length {}", length), - } - } else { - vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) - }; - - ioctl_ret.with_context(|| { - format!( - "KVM unregister ioeventfd failed: mmio addr 0x{:X}, size 0x{:X}, data_match {}", - ioevtfd.addr_range.base.raw_value(), - ioevtfd.addr_range.size, - if ioevtfd.data_match { - ioevtfd.data - } else { - u64::MAX - } - ) - })?; - - Ok(()) - } -} - -impl Listener for KvmMemoryListener { - /// Get default priority. - fn priority(&self) -> i32 { - 10_i32 - } - - /// Is this listener enabled to call. - fn enabled(&self) -> bool { - self.enabled - } - - /// Enable listener for address space. - fn enable(&mut self) { - self.enabled = true; - } - - /// Disable listener for address space. - fn disable(&mut self) { - self.enabled = false; - } - - /// Deal with the request. - /// - /// # Arguments - /// - /// * `flat_range` - FlatRange would be used to find the region. - /// * `evtfd` - IoEvent of Region. - /// * `req_type` - Request type. - /// - /// # Errors - /// - /// Returns Error if - /// * Both `flat_range` and `evtfd' are not provided. - fn handle_request( - &self, - flat_range: Option<&FlatRange>, - evtfd: Option<&RegionIoEventFd>, - req_type: ListenerReqType, - ) -> Result<()> { - let req_ret = - match req_type { - ListenerReqType::AddRegion => self - .add_region(flat_range.with_context(|| "No FlatRange for AddRegion request")?), - ListenerReqType::DeleteRegion => self.delete_region( - flat_range.with_context(|| "No FlatRange for DeleteRegion request")?, - ), - ListenerReqType::AddIoeventfd => self - .add_ioeventfd(evtfd.with_context(|| "No IoEventFd for AddIoeventfd request")?), - ListenerReqType::DeleteIoeventfd => self.delete_ioeventfd( - evtfd.with_context(|| "No IoEventFd for DeleteIoeventfd request")?, - ), - }; - - req_ret.with_context(|| AddressSpaceError::ListenerRequest(req_type)) - } -} - -#[cfg(target_arch = "x86_64")] -#[derive(Default)] -pub struct KvmIoListener { - /// Whether enabled as a IO listener. - enabled: bool, -} - -#[cfg(target_arch = "x86_64")] -impl KvmIoListener { - /// Register a IoEvent to `/dev/kvm`. - /// - /// # Arguments - /// - /// * `ioevtfd` - IoEvent of Region. - /// - /// # Errors - /// - /// Return Error if the length of ioeventfd data is unexpected or syscall failed. - fn add_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let io_addr = IoEventAddress::Pio(ioevtfd.addr_range.base.raw_value()); - let ioctl_ret = if ioevtfd.data_match { - let length = ioevtfd.addr_range.size; - match length { - 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), - 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), - _ => bail!("unexpected ioeventfd data length {}", length), - } - } else { - vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) - }; - - ioctl_ret.with_context(|| { - format!( - "KVM register ioeventfd failed: io addr 0x{:X}, size 0x{:X}, data_match {}", - ioevtfd.addr_range.base.raw_value(), - ioevtfd.addr_range.size, - if ioevtfd.data_match { - ioevtfd.data - } else { - u64::MAX - } - ) - })?; - - Ok(()) - } - - /// Delete an IoEvent from `/dev/kvm`. - /// - /// # Arguments - /// - /// * `ioevtfd` - IoEvent of Region. - fn delete_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - let io_addr = IoEventAddress::Pio(ioevtfd.addr_range.base.raw_value()); - let ioctl_ret = if ioevtfd.data_match { - let length = ioevtfd.addr_range.size; - match length { - 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), - 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), - 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), - _ => bail!("Unexpected ioeventfd data length {}", length), - } - } else { - vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) - }; - - ioctl_ret.with_context(|| { - format!( - "KVM unregister ioeventfd failed: io addr 0x{:X}, size 0x{:X}, data_match {}", - ioevtfd.addr_range.base.raw_value(), - ioevtfd.addr_range.size, - if ioevtfd.data_match { - ioevtfd.data - } else { - u64::MAX - } - ) - })?; - - Ok(()) - } -} - -/// Kvm io listener. -#[cfg(target_arch = "x86_64")] -impl Listener for KvmIoListener { - /// Get the default priority. - fn priority(&self) -> i32 { - 10_i32 - } - - /// Is this listener enabled to call. - fn enabled(&self) -> bool { - self.enabled - } - - /// Enable listener for address space. - fn enable(&mut self) { - self.enabled = true; - } - - /// Disable listener for address space. - fn disable(&mut self) { - self.enabled = false; - } - - /// Deal with the request. - /// - /// # Arguments - /// - /// * `_range` - Corresponding FlatRange of new-added/deleted region. - /// * `evtfd` - IoEvent of Region. - /// * `req_type` - Request type. - fn handle_request( - &self, - _range: Option<&FlatRange>, - evtfd: Option<&RegionIoEventFd>, - req_type: ListenerReqType, - ) -> Result<()> { - let handle_ret = match req_type { - ListenerReqType::AddIoeventfd => { - self.add_ioeventfd(evtfd.with_context(|| "No IoEventFd for AddIoeventfd request")?) - } - ListenerReqType::DeleteIoeventfd => self.delete_ioeventfd( - evtfd.with_context(|| "No IoEventFd for DeleteIoeventfd request")?, - ), - _ => return Ok(()), - }; - - handle_ret.with_context(|| AddressSpaceError::ListenerRequest(req_type)) - } -} - -#[cfg(test)] -mod test { - use libc::EFD_NONBLOCK; - use vmm_sys_util::eventfd::EventFd; - - use super::*; - use crate::{GuestAddress, HostMemMapping, Region, RegionIoEventFd}; - use hypervisor::kvm::KVMFds; - - fn generate_region_ioeventfd>(addr: u64, datamatch: T) -> RegionIoEventFd { - let data = datamatch.into(); - RegionIoEventFd { - fd: Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()), - addr_range: AddressRange::from((addr, std::mem::size_of::() as u64)), - data_match: data != 0, - data, - } - } - - fn create_ram_range(addr: u64, size: u64, offset_in_region: u64) -> FlatRange { - let mem_mapping = Arc::new( - HostMemMapping::new(GuestAddress(addr), None, size, None, false, false, false).unwrap(), - ); - FlatRange { - addr_range: AddressRange::new( - mem_mapping.start_address().unchecked_add(offset_in_region), - mem_mapping.size() - offset_in_region, - ), - owner: Region::init_ram_region(mem_mapping.clone(), "ram"), - offset_in_region, - rom_dev_romd: None, - } - } - - #[test] - fn test_alloc_slot() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let kml = KvmMemoryListener::new(4); - let host_addr = 0u64; - - assert_eq!(kml.get_free_slot(0, 100, host_addr).unwrap(), 0); - assert_eq!(kml.get_free_slot(200, 100, host_addr).unwrap(), 1); - assert_eq!(kml.get_free_slot(300, 100, host_addr).unwrap(), 2); - assert_eq!(kml.get_free_slot(500, 100, host_addr).unwrap(), 3); - assert!(kml.get_free_slot(200, 100, host_addr).is_err()); - // no available KVM mem slot - assert!(kml.get_free_slot(600, 100, host_addr).is_err()); - - kml.delete_slot(200, 100).unwrap(); - assert!(kml.delete_slot(150, 100).is_err()); - assert!(kml.delete_slot(700, 100).is_err()); - assert_eq!(kml.get_free_slot(200, 100, host_addr).unwrap(), 1); - } - - #[test] - fn test_add_del_ram_region() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let kml = KvmMemoryListener::new(34); - let ram_size = host_page_size(); - let ram_fr1 = create_ram_range(0, ram_size, 0); - - kml.handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) - .unwrap(); - // flat-range already added, adding again should make an error - assert!(kml - .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) - .is_err()); - assert!(kml - .handle_request(Some(&ram_fr1), None, ListenerReqType::DeleteRegion) - .is_ok()); - // flat-range already deleted, deleting again should make an error - assert!(kml - .handle_request(Some(&ram_fr1), None, ListenerReqType::DeleteRegion) - .is_ok()); - } - - #[test] - fn test_add_region_align() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let kml = KvmMemoryListener::new(34); - // flat-range not aligned - let page_size = host_page_size(); - let ram_fr2 = create_ram_range(page_size, 2 * page_size, 1000); - assert!(kml - .handle_request(Some(&ram_fr2), None, ListenerReqType::AddRegion) - .is_ok()); - - // flat-range size is zero after aligned, this step should make an error - let ram_fr3 = create_ram_range(page_size, page_size, 1000); - assert!(kml - .handle_request(Some(&ram_fr3), None, ListenerReqType::AddRegion) - .is_err()); - } - - #[test] - fn test_add_del_ioeventfd() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let kml = KvmMemoryListener::new(34); - let evtfd = generate_region_ioeventfd(4, NoDatamatch); - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_ok()); - // The evtfd already added, adding again should make an error. - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_err()); - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_ok()); - // The evtfd already deleted, deleting again should cause an error. - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_err()); - - // Register an ioeventfd with data-match. - let evtfd = generate_region_ioeventfd(64, 4_u64); - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_ok()); - - // Register an ioeventfd which has same address with previously registered ones will cause - // an error. - let same_addred_evtfd = generate_region_ioeventfd(64, 4_u64); - assert!(kml - .handle_request( - None, - Some(&same_addred_evtfd), - ListenerReqType::AddIoeventfd - ) - .is_err()); - - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_ok()); - } - - #[test] - fn test_ioeventfd_with_data_match() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let kml = KvmMemoryListener::new(34); - let evtfd_addr = 0x1000_u64; - let mut evtfd = generate_region_ioeventfd(evtfd_addr, 64_u32); - evtfd.addr_range.size = 3_u64; - // Matched data's length must be 2, 4 or 8. - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_err()); - - let evtfd = generate_region_ioeventfd(evtfd_addr, 64_u32); - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_ok()); - - // Delete ioeventfd with wrong address will cause an error. - let mut evtfd_to_del = evtfd.clone(); - evtfd_to_del.addr_range.base.0 = evtfd_to_del.addr_range.base.0 - 2; - assert!(kml - .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) - .is_err()); - - // Delete ioeventfd with inconsistent data-match will cause error. - let mut evtfd_to_del = evtfd.clone(); - evtfd_to_del.data_match = false; - assert!(kml - .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) - .is_err()); - - // Delete ioeventfd with inconsistent matched data will cause an error. - let mut evtfd_to_del = evtfd.clone(); - evtfd_to_del.data = 128_u64; - assert!(kml - .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) - .is_err()); - - // Delete it successfully. - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_ok()); - - // Delete a not-exist ioeventfd will cause an error. - assert!(kml - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_err()); - } - - #[test] - #[cfg(target_arch = "x86_64")] - fn test_kvm_io_listener() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let iol = KvmIoListener::default(); - let evtfd = generate_region_ioeventfd(4, NoDatamatch); - assert!(iol - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_ok()); - // evtfd already added, adding again should make an error. - assert!(iol - .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) - .is_err()); - assert!(iol - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_ok()); - // evtfd already deleted, deleting again should make an error. - assert!(iol - .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) - .is_err()); - - // Matched data's length must be 2, 4 or 8. - let mut evtfd_match = generate_region_ioeventfd(4, 64_u32); - evtfd_match.addr_range.size = 3; - assert!(iol - .handle_request(None, Some(&evtfd_match), ListenerReqType::AddIoeventfd) - .is_err()); - evtfd_match.addr_range.size = 4; - assert!(iol - .handle_request(None, Some(&evtfd_match), ListenerReqType::AddIoeventfd) - .is_ok()); - } + pub host_addr: u64, } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 9c9404ac5..f5e7c77ab 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -725,7 +725,8 @@ impl Region { } /// Get the ioeventfds within this Region, - /// these fds will be register to `KVM` and used for guest notifier. + /// these fds will be register to `Hypervisor` supporting + /// ioeventfd and used for guest notifier. pub fn ioeventfds(&self) -> Vec { self.io_evtfds.lock().unwrap().to_vec() } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 99ef2b9cc..e61c0041c 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -14,13 +14,11 @@ mod interrupt; pub use interrupt::MsiVector; -use std::collections::HashMap; use std::mem::{align_of, size_of}; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use arc_swap::ArcSwap; -use kvm_bindings::kvm_userspace_memory_region as MemorySlot; use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; use log::error; @@ -33,8 +31,6 @@ use interrupt::{IrqRoute, IrqRouteEntry, IrqRouteTable}; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; -pub const KVM_SET_USER_MEMORY_REGION: u32 = 0x4020_ae46; -pub const KVM_IOEVENTFD: u32 = 0x4040_ae79; pub const KVM_SIGNAL_MSI: u32 = 0x4020_aea5; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h @@ -93,7 +89,6 @@ ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); -ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); #[allow(clippy::upper_case_acronyms)] @@ -102,7 +97,6 @@ pub struct KVMFds { pub fd: Option, pub vm_fd: Option>, pub irq_route_table: Mutex, - pub mem_slots: Arc>>, } impl KVMFds { @@ -121,7 +115,6 @@ impl KVMFds { fd: Some(fd), vm_fd: Some(Arc::new(vm_fd)), irq_route_table, - mem_slots: Arc::new(Mutex::new(HashMap::new())), } } Err(e) => { @@ -186,92 +179,6 @@ impl KVMFds { .set_irq_line(irq, level) .with_context(|| format!("Failed to set irq {} level {:?}.", irq, level)) } - - /// Start dirty page tracking in kvm. - pub fn start_dirty_log(&self) -> Result<()> { - for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { - region.flags = KVM_MEM_LOG_DIRTY_PAGES; - // SAFETY: region from `KVMFds` is reliable. - unsafe { - self.vm_fd - .as_ref() - .unwrap() - .set_user_memory_region(*region) - .with_context(|| { - format!( - "Failed to start dirty log, error is {}", - std::io::Error::last_os_error() - ) - })?; - } - } - - Ok(()) - } - - /// Stop dirty page tracking in kvm. - pub fn stop_dirty_log(&self) -> Result<()> { - for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { - region.flags = 0; - // SAFETY: region from `KVMFds` is reliable. - unsafe { - self.vm_fd - .as_ref() - .unwrap() - .set_user_memory_region(*region) - .with_context(|| { - format!( - "Failed to stop dirty log, error is {}", - std::io::Error::last_os_error() - ) - })?; - } - } - - Ok(()) - } - - /// Get dirty page bitmap in kvm. - pub fn get_dirty_log(&self, slot: u32, mem_size: u64) -> Result> { - let res = self - .vm_fd - .as_ref() - .unwrap() - .get_dirty_log(slot, mem_size as usize) - .with_context(|| { - format!( - "Failed to get dirty log, error is {}", - std::io::Error::last_os_error() - ) - })?; - - Ok(res) - } - - /// Add ram memory region to `KVMFds` structure. - pub fn add_mem_slot(&self, mem_slot: MemorySlot) -> Result<()> { - if mem_slot.flags & KVM_MEM_READONLY != 0 { - return Ok(()); - } - - let mut locked_slots = self.mem_slots.as_ref().lock().unwrap(); - locked_slots.insert(mem_slot.slot, mem_slot); - - Ok(()) - } - - /// Remove ram memory region from `KVMFds` structure. - pub fn remove_mem_slot(&self, mem_slot: MemorySlot) -> Result<()> { - let mut locked_slots = self.mem_slots.as_ref().lock().unwrap(); - locked_slots.remove(&mem_slot.slot); - - Ok(()) - } - - /// Get ram memory region from `KVMFds` structure. - pub fn get_mem_slots(&self) -> Arc>> { - self.mem_slots.clone() - } } pub static KVM_FDS: Lazy> = Lazy::new(|| ArcSwap::from(Arc::new(KVMFds::new()))); diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor_refactor/Cargo.toml index dce1835f7..1ebbee623 100644 --- a/hypervisor_refactor/Cargo.toml +++ b/hypervisor_refactor/Cargo.toml @@ -10,4 +10,10 @@ anyhow = "1.0" thiserror = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } kvm-ioctls = "0.13.0" +libc = "0.2" +log = "0.4" +vmm-sys-util = "0.11.1" +address_space = { path = "../address_space" } machine_manager = { path = "../machine_manager" } +migration = { path = "../migration" } +util = { path = "../util" } diff --git a/hypervisor_refactor/src/kvm/listener.rs b/hypervisor_refactor/src/kvm/listener.rs new file mode 100644 index 000000000..15a02c203 --- /dev/null +++ b/hypervisor_refactor/src/kvm/listener.rs @@ -0,0 +1,807 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::{Arc, Mutex}; + +use anyhow::{anyhow, bail, Context, Result}; +use kvm_bindings::*; +use kvm_bindings::{kvm_userspace_memory_region as KvmMemSlot, KVM_MEM_READONLY}; +use kvm_ioctls::{IoEventAddress, NoDatamatch, VmFd}; +use log::{debug, warn}; + +use address_space::{ + AddressRange, AddressSpaceError, FlatRange, Listener, ListenerReqType, MemSlot, + RegionIoEventFd, RegionType, +}; +use util::{num_ops::round_down, unix::host_page_size}; + +#[derive(Clone)] +pub struct KvmMemoryListener { + vm_fd: Option>, + /// Id of AddressSpace. + as_id: Arc, + /// Record all MemSlots. + slots: Arc>>, + /// Memory slot registered in kvm. + kvm_memslots: Arc>>, + /// Whether enabled as a memory listener. + enabled: bool, +} + +impl KvmMemoryListener { + /// Create a new KvmMemoryListener for a VM. + /// + /// # Arguments + /// + /// * `nr_slots` - Number of slots. + pub fn new( + nr_slots: u32, + vm_fd: Option>, + kvm_memslots: Arc>>, + ) -> KvmMemoryListener { + KvmMemoryListener { + vm_fd, + as_id: Arc::new(AtomicU32::new(0)), + slots: Arc::new(Mutex::new(vec![MemSlot::default(); nr_slots as usize])), + kvm_memslots, + enabled: false, + } + } + + /// Find a free slot and fills it with given arguments. + /// + /// # Arguments + /// + /// * `guest_addr` - Guest address. + /// * `size` - Size of slot. + /// * `host_addr` - Host address. + /// + /// # Errors + /// + /// Return Error if + /// * No available Kvm slot. + /// * Given memory slot overlap with existed one. + fn get_free_slot(&self, guest_addr: u64, size: u64, host_addr: u64) -> Result { + let mut slots = self.slots.lock().unwrap(); + + // check if the given address range overlaps with exist ones + let range = AddressRange::from((guest_addr, size)); + slots.iter().try_for_each::<_, Result<()>>(|s| { + if AddressRange::from((s.guest_addr, s.size)) + .find_intersection(range) + .is_some() + { + return Err(anyhow!(AddressSpaceError::KvmSlotOverlap { + add: (guest_addr, size), + exist: (s.guest_addr, s.size) + })); + } + Ok(()) + })?; + + for (index, slot) in slots.iter_mut().enumerate() { + if slot.size == 0 { + slot.index = index as u32; + slot.guest_addr = guest_addr; + slot.size = size; + slot.host_addr = host_addr; + return Ok(slot.index); + } + } + + Err(anyhow!(AddressSpaceError::NoAvailKvmSlot(slots.len()))) + } + + /// Delete a slot after finding it according to the given arguments. + /// Return the deleted one if succeed. + /// + /// # Arguments + /// + /// * `addr` - Guest address of slot. + /// * `size` - Size of slots. + /// + /// # Errors + /// + /// Return Error if no Kem slot matched. + fn delete_slot(&self, addr: u64, size: u64) -> Result { + let mut slots = self.slots.lock().unwrap(); + for slot in slots.iter_mut() { + if slot.guest_addr == addr && slot.size == size { + // set slot size to zero, so it can be reused later + slot.size = 0; + return Ok(*slot); + } + } + Err(anyhow!(AddressSpaceError::NoMatchedKvmSlot(addr, size))) + } + + /// Align a piece of memory segment according to `alignment`, + /// return AddressRange after aligned. + /// + /// # Arguments + /// + /// * `range` - One piece of memory segment. + /// * `alignment` - Alignment base. + /// + /// # Errors + /// + /// Return Error if Memslot size is zero after aligned. + fn align_mem_slot(range: AddressRange, alignment: u64) -> Result { + let aligned_addr = range + .base + .align_up(alignment) + .with_context(|| AddressSpaceError::AddrAlignUp(range.base.raw_value(), alignment))?; + + let aligned_size = range + .size + .checked_sub(aligned_addr.offset_from(range.base)) + .and_then(|sz| round_down(sz, alignment)) + .filter(|&sz| sz > 0_u64) + .with_context(|| + format!("Mem slot size is zero after aligned, addr 0x{:X}, size 0x{:X}, alignment 0x{:X}", + range.base.raw_value(), range.size, alignment) + )?; + + Ok(AddressRange::new(aligned_addr, aligned_size)) + } + + /// Callback function for adding Region, which only care about Ram-type Region yet. + /// + /// # Arguments + /// + /// * `flat_range` - Corresponding FlatRange of new-added region. + /// + /// # Errors + /// + /// Return Error if fail to delete kvm_mem_slot. + fn add_region(&self, flat_range: &FlatRange) -> Result<()> { + if flat_range.owner.region_type() == RegionType::RomDevice + && !flat_range.owner.get_rom_device_romd().unwrap() + { + if let Err(ref e) = self.delete_region(flat_range) { + warn!( + "Rom-device Region changes to IO mode, Failed to delete region: {:?}", + e + ); + } + return Ok(()); + } + + if flat_range.owner.region_type() != RegionType::Ram + && flat_range.owner.region_type() != RegionType::RomDevice + && flat_range.owner.region_type() != RegionType::RamDevice + { + return Ok(()); + } + + let (aligned_addr, aligned_size) = + Self::align_mem_slot(flat_range.addr_range, host_page_size()) + .map(|r| (r.base, r.size)) + .with_context(|| "Failed to align mem slot")?; + let align_adjust = aligned_addr.raw_value() - flat_range.addr_range.base.raw_value(); + + // `unwrap()` won't fail because Ram-type Region definitely has hva + let aligned_hva = flat_range.owner.get_host_address().unwrap() + + flat_range.offset_in_region + + align_adjust; + + let slot_idx = self + .get_free_slot(aligned_addr.raw_value(), aligned_size, aligned_hva) + .with_context(|| "Failed to get available KVM mem slot")?; + + let mut flags = 0_u32; + if flat_range.owner.get_rom_device_romd().unwrap_or(false) { + flags |= KVM_MEM_READONLY; + } + let kvm_region = KvmMemSlot { + slot: slot_idx | (self.as_id.load(Ordering::SeqCst) << 16), + guest_phys_addr: aligned_addr.raw_value(), + memory_size: aligned_size, + userspace_addr: aligned_hva, + flags, + }; + + if kvm_region.flags & KVM_MEM_READONLY == 0 { + let mut locked_slots = self.kvm_memslots.as_ref().lock().unwrap(); + locked_slots.insert(kvm_region.slot, kvm_region); + } + + // SAFETY: All parameters in the struct of kvm_region are valid, + // it can be guaranteed that calling the ioctl_with_ref in the function + // of set_user_memory_region is safe. + unsafe { + self.vm_fd + .as_ref() + .unwrap() + .set_user_memory_region(kvm_region) + .or_else(|e| { + self.delete_slot(aligned_addr.raw_value(), aligned_size) + .with_context(|| "Failed to delete Kvm mem slot")?; + Err(e).with_context(|| { + format!( + "KVM register memory region failed: addr 0x{:X}, size 0x{:X}", + aligned_addr.raw_value(), + aligned_size + ) + }) + }) + } + } + + /// Callback function for deleting Region, which only care about Ram-type Region yet. + /// + /// # Arguments + /// + /// * `flat_range` - Corresponding FlatRange of new-deleted region. + fn delete_region(&self, flat_range: &FlatRange) -> Result<()> { + if flat_range.owner.region_type() != RegionType::Ram + && flat_range.owner.region_type() != RegionType::RomDevice + && flat_range.owner.region_type() != RegionType::RamDevice + { + return Ok(()); + } + + let (aligned_addr, aligned_size) = + Self::align_mem_slot(flat_range.addr_range, host_page_size()) + .map(|r| (r.base, r.size)) + .with_context(|| "Failed to align mem slot")?; + + let mem_slot = match self.delete_slot(aligned_addr.raw_value(), aligned_size) { + Ok(m) => m, + Err(_) => { + debug!("no match mem slot registered to KVM, just return"); + return Ok(()); + } + }; + + let kvm_region = kvm_userspace_memory_region { + slot: mem_slot.index | (self.as_id.load(Ordering::SeqCst) << 16), + guest_phys_addr: mem_slot.guest_addr, + memory_size: 0_u64, + userspace_addr: mem_slot.host_addr, + flags: 0, + }; + + let mut locked_slots = self.kvm_memslots.lock().unwrap(); + locked_slots.remove(&kvm_region.slot); + + // SAFETY: All parameters in the struct of kvm_region are valid, + // it can be guaranteed that calling the ioctl_with_ref in the function + // of set_user_memory_region is safe. + unsafe { + self.vm_fd + .as_ref() + .unwrap() + .set_user_memory_region(kvm_region) + .with_context(|| { + format!( + "KVM unregister memory region failed: addr 0x{:X}", + aligned_addr.raw_value(), + ) + }) + } + } + + /// Register a IoEvent to `/dev/kvm`. + /// + /// # Arguments + /// + /// * `ioevtfd` - IoEvent would be added. + /// + /// # Errors + /// + /// Return Error if the length of ioeventfd data is unexpected or syscall failed. + fn add_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { + let vm_fd = self.vm_fd.as_ref().unwrap(); + let io_addr = IoEventAddress::Mmio(ioevtfd.addr_range.base.raw_value()); + let ioctl_ret = if ioevtfd.data_match { + let length = ioevtfd.addr_range.size; + match length { + 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), + 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), + 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), + _ => bail!("Unexpected ioeventfd data length {}", length), + } + } else { + vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) + }; + + ioctl_ret.with_context(|| { + format!( + "KVM register ioeventfd failed, mmio addr 0x{:X}, size 0x{:X}, data_match {}", + ioevtfd.addr_range.base.raw_value(), + ioevtfd.addr_range.size, + if ioevtfd.data_match { + ioevtfd.data + } else { + u64::MAX + } + ) + }) + } + + /// Deletes `ioevtfd` from `/dev/kvm` + /// + /// # Arguments + /// + /// * `ioevtfd` - IoEvent would be deleted. + fn delete_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { + let vm_fd = self.vm_fd.as_ref().unwrap(); + let io_addr = IoEventAddress::Mmio(ioevtfd.addr_range.base.raw_value()); + let ioctl_ret = if ioevtfd.data_match { + let length = ioevtfd.addr_range.size; + match length { + 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), + 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), + 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), + _ => bail!("Unexpected ioeventfd data length {}", length), + } + } else { + vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) + }; + + ioctl_ret.with_context(|| { + format!( + "KVM unregister ioeventfd failed: mmio addr 0x{:X}, size 0x{:X}, data_match {}", + ioevtfd.addr_range.base.raw_value(), + ioevtfd.addr_range.size, + if ioevtfd.data_match { + ioevtfd.data + } else { + u64::MAX + } + ) + }) + } +} + +impl Listener for KvmMemoryListener { + /// Get default priority. + fn priority(&self) -> i32 { + 10_i32 + } + + /// Is this listener enabled to call. + fn enabled(&self) -> bool { + self.enabled + } + + /// Enable listener for address space. + fn enable(&mut self) { + self.enabled = true; + } + + /// Disable listener for address space. + fn disable(&mut self) { + self.enabled = false; + } + + /// Deal with the request. + /// + /// # Arguments + /// + /// * `flat_range` - FlatRange would be used to find the region. + /// * `evtfd` - IoEvent of Region. + /// * `req_type` - Request type. + /// + /// # Errors + /// + /// Returns Error if + /// * Both `flat_range` and `evtfd' are not provided. + fn handle_request( + &self, + flat_range: Option<&FlatRange>, + evtfd: Option<&RegionIoEventFd>, + req_type: ListenerReqType, + ) -> Result<()> { + let req_ret = + match req_type { + ListenerReqType::AddRegion => self + .add_region(flat_range.with_context(|| "No FlatRange for AddRegion request")?), + ListenerReqType::DeleteRegion => self.delete_region( + flat_range.with_context(|| "No FlatRange for DeleteRegion request")?, + ), + ListenerReqType::AddIoeventfd => self + .add_ioeventfd(evtfd.with_context(|| "No IoEventFd for AddIoeventfd request")?), + ListenerReqType::DeleteIoeventfd => self.delete_ioeventfd( + evtfd.with_context(|| "No IoEventFd for DeleteIoeventfd request")?, + ), + }; + + req_ret.with_context(|| AddressSpaceError::ListenerRequest(req_type)) + } +} + +#[cfg(target_arch = "x86_64")] +#[derive(Default)] +pub struct KvmIoListener { + vm_fd: Option>, + /// Whether enabled as a IO listener. + enabled: bool, +} + +#[cfg(target_arch = "x86_64")] +impl KvmIoListener { + pub fn new(vm_fd: Option>) -> KvmIoListener { + KvmIoListener { + vm_fd, + enabled: false, + } + } + /// Register a IoEvent to `/dev/kvm`. + /// + /// # Arguments + /// + /// * `ioevtfd` - IoEvent of Region. + /// + /// # Errors + /// + /// Return Error if the length of ioeventfd data is unexpected or syscall failed. + fn add_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { + let vm_fd = self.vm_fd.as_ref().unwrap(); + let io_addr = IoEventAddress::Pio(ioevtfd.addr_range.base.raw_value()); + let ioctl_ret = if ioevtfd.data_match { + let length = ioevtfd.addr_range.size; + match length { + 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), + 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), + 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), + _ => bail!("unexpected ioeventfd data length {}", length), + } + } else { + vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) + }; + + ioctl_ret.with_context(|| { + format!( + "KVM register ioeventfd failed: io addr 0x{:X}, size 0x{:X}, data_match {}", + ioevtfd.addr_range.base.raw_value(), + ioevtfd.addr_range.size, + if ioevtfd.data_match { + ioevtfd.data + } else { + u64::MAX + } + ) + }) + } + + /// Delete an IoEvent from `/dev/kvm`. + /// + /// # Arguments + /// + /// * `ioevtfd` - IoEvent of Region. + fn delete_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { + let vm_fd = self.vm_fd.as_ref().unwrap(); + let io_addr = IoEventAddress::Pio(ioevtfd.addr_range.base.raw_value()); + let ioctl_ret = if ioevtfd.data_match { + let length = ioevtfd.addr_range.size; + match length { + 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), + 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), + 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), + _ => bail!("Unexpected ioeventfd data length {}", length), + } + } else { + vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, NoDatamatch) + }; + + ioctl_ret.with_context(|| { + format!( + "KVM unregister ioeventfd failed: io addr 0x{:X}, size 0x{:X}, data_match {}", + ioevtfd.addr_range.base.raw_value(), + ioevtfd.addr_range.size, + if ioevtfd.data_match { + ioevtfd.data + } else { + u64::MAX + } + ) + }) + } +} + +/// Kvm io listener. +#[cfg(target_arch = "x86_64")] +impl Listener for KvmIoListener { + /// Get the default priority. + fn priority(&self) -> i32 { + 10_i32 + } + + /// Is this listener enabled to call. + fn enabled(&self) -> bool { + self.enabled + } + + /// Enable listener for address space. + fn enable(&mut self) { + self.enabled = true; + } + + /// Disable listener for address space. + fn disable(&mut self) { + self.enabled = false; + } + + /// Deal with the request. + /// + /// # Arguments + /// + /// * `_range` - Corresponding FlatRange of new-added/deleted region. + /// * `evtfd` - IoEvent of Region. + /// * `req_type` - Request type. + fn handle_request( + &self, + _range: Option<&FlatRange>, + evtfd: Option<&RegionIoEventFd>, + req_type: ListenerReqType, + ) -> Result<()> { + let handle_ret = match req_type { + ListenerReqType::AddIoeventfd => { + self.add_ioeventfd(evtfd.with_context(|| "No IoEventFd for AddIoeventfd request")?) + } + ListenerReqType::DeleteIoeventfd => self.delete_ioeventfd( + evtfd.with_context(|| "No IoEventFd for DeleteIoeventfd request")?, + ), + _ => return Ok(()), + }; + + handle_ret.with_context(|| AddressSpaceError::ListenerRequest(req_type)) + } +} + +#[cfg(test)] +mod test { + use libc::EFD_NONBLOCK; + use vmm_sys_util::eventfd::EventFd; + + use crate::kvm::KvmHypervisor; + + use super::*; + use address_space::{GuestAddress, HostMemMapping, Region, RegionIoEventFd}; + + fn generate_region_ioeventfd>(addr: u64, datamatch: T) -> RegionIoEventFd { + let data = datamatch.into(); + RegionIoEventFd { + fd: Arc::new(EventFd::new(EFD_NONBLOCK).unwrap()), + addr_range: AddressRange::from((addr, std::mem::size_of::() as u64)), + data_match: data != 0, + data, + } + } + + fn create_ram_range(addr: u64, size: u64, offset_in_region: u64) -> FlatRange { + let mem_mapping = Arc::new( + HostMemMapping::new(GuestAddress(addr), None, size, None, false, false, false).unwrap(), + ); + FlatRange { + addr_range: AddressRange::new( + mem_mapping.start_address().unchecked_add(offset_in_region), + mem_mapping.size() - offset_in_region, + ), + owner: Region::init_ram_region(mem_mapping.clone(), "ram"), + offset_in_region, + rom_dev_romd: None, + } + } + + #[test] + fn test_alloc_slot() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let kml = KvmMemoryListener::new(4, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let host_addr = 0u64; + + assert_eq!(kml.get_free_slot(0, 100, host_addr).unwrap(), 0); + assert_eq!(kml.get_free_slot(200, 100, host_addr).unwrap(), 1); + assert_eq!(kml.get_free_slot(300, 100, host_addr).unwrap(), 2); + assert_eq!(kml.get_free_slot(500, 100, host_addr).unwrap(), 3); + assert!(kml.get_free_slot(200, 100, host_addr).is_err()); + // no available KVM mem slot + assert!(kml.get_free_slot(600, 100, host_addr).is_err()); + + kml.delete_slot(200, 100).unwrap(); + assert!(kml.delete_slot(150, 100).is_err()); + assert!(kml.delete_slot(700, 100).is_err()); + assert_eq!(kml.get_free_slot(200, 100, host_addr).unwrap(), 1); + } + + #[test] + fn test_add_del_ram_region() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let ram_size = host_page_size(); + let ram_fr1 = create_ram_range(0, ram_size, 0); + + kml.handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) + .unwrap(); + // flat-range already added, adding again should make an error + assert!(kml + .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) + .is_err()); + assert!(kml + .handle_request(Some(&ram_fr1), None, ListenerReqType::DeleteRegion) + .is_ok()); + // flat-range already deleted, deleting again should make an error + assert!(kml + .handle_request(Some(&ram_fr1), None, ListenerReqType::DeleteRegion) + .is_ok()); + } + + #[test] + fn test_add_region_align() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + // flat-range not aligned + let page_size = host_page_size(); + let ram_fr2 = create_ram_range(page_size, 2 * page_size, 1000); + assert!(kml + .handle_request(Some(&ram_fr2), None, ListenerReqType::AddRegion) + .is_ok()); + + // flat-range size is zero after aligned, this step should make an error + let ram_fr3 = create_ram_range(page_size, page_size, 1000); + assert!(kml + .handle_request(Some(&ram_fr3), None, ListenerReqType::AddRegion) + .is_err()); + } + + #[test] + fn test_add_del_ioeventfd() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let evtfd = generate_region_ioeventfd(4, NoDatamatch); + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_ok()); + // The evtfd already added, adding again should make an error. + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_err()); + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_ok()); + // The evtfd already deleted, deleting again should cause an error. + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_err()); + + // Register an ioeventfd with data-match. + let evtfd = generate_region_ioeventfd(64, 4_u64); + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_ok()); + + // Register an ioeventfd which has same address with previously registered ones will cause + // an error. + let same_addred_evtfd = generate_region_ioeventfd(64, 4_u64); + assert!(kml + .handle_request( + None, + Some(&same_addred_evtfd), + ListenerReqType::AddIoeventfd + ) + .is_err()); + + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_ok()); + } + + #[test] + fn test_ioeventfd_with_data_match() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let evtfd_addr = 0x1000_u64; + let mut evtfd = generate_region_ioeventfd(evtfd_addr, 64_u32); + evtfd.addr_range.size = 3_u64; + // Matched data's length must be 2, 4 or 8. + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_err()); + + let evtfd = generate_region_ioeventfd(evtfd_addr, 64_u32); + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_ok()); + + // Delete ioeventfd with wrong address will cause an error. + let mut evtfd_to_del = evtfd.clone(); + evtfd_to_del.addr_range.base.0 = evtfd_to_del.addr_range.base.0 - 2; + assert!(kml + .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) + .is_err()); + + // Delete ioeventfd with inconsistent data-match will cause error. + let mut evtfd_to_del = evtfd.clone(); + evtfd_to_del.data_match = false; + assert!(kml + .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) + .is_err()); + + // Delete ioeventfd with inconsistent matched data will cause an error. + let mut evtfd_to_del = evtfd.clone(); + evtfd_to_del.data = 128_u64; + assert!(kml + .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) + .is_err()); + + // Delete it successfully. + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_ok()); + + // Delete a not-exist ioeventfd will cause an error. + assert!(kml + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_err()); + } + + #[test] + #[cfg(target_arch = "x86_64")] + fn test_kvm_io_listener() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let iol = KvmIoListener::new(kvm_hyp.vm_fd.clone()); + let evtfd = generate_region_ioeventfd(4, NoDatamatch); + assert!(iol + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_ok()); + // evtfd already added, adding again should make an error. + assert!(iol + .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) + .is_err()); + assert!(iol + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_ok()); + // evtfd already deleted, deleting again should make an error. + assert!(iol + .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) + .is_err()); + + // Matched data's length must be 2, 4 or 8. + let mut evtfd_match = generate_region_ioeventfd(4, 64_u32); + evtfd_match.addr_range.size = 3; + assert!(iol + .handle_request(None, Some(&evtfd_match), ListenerReqType::AddIoeventfd) + .is_err()); + evtfd_match.addr_range.size = 4; + assert!(iol + .handle_request(None, Some(&evtfd_match), ListenerReqType::AddIoeventfd) + .is_ok()); + } +} diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index 5e10721dd..b8fc95dfa 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -16,18 +16,35 @@ pub mod aarch64; #[cfg(target_arch = "x86_64")] pub mod x86_64; -use std::sync::Arc; +mod listener; -use anyhow::{bail, Result}; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Context, Result}; +use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; +use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; +use self::listener::KvmMemoryListener; use super::HypervisorOps; +use address_space::{AddressSpace, Listener}; +use migration::{MigrateMemSlot, MigrateOps}; + +// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h +pub const KVM_SET_USER_MEMORY_REGION: u32 = 0x4020_ae46; +pub const KVM_IOEVENTFD: u32 = 0x4040_ae79; + +// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h +ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] pub struct KvmHypervisor { pub fd: Option, pub vm_fd: Option>, + pub mem_slots: Arc>>, } impl KvmHypervisor { @@ -47,6 +64,7 @@ impl KvmHypervisor { Ok(KvmHypervisor { fd: Some(kvm_fd), vm_fd, + mem_slots: Arc::new(Mutex::new(HashMap::new())), }) } Err(e) => { @@ -54,11 +72,107 @@ impl KvmHypervisor { } } } + + fn create_memory_listener(&self) -> Arc> { + Arc::new(Mutex::new(KvmMemoryListener::new( + self.fd.as_ref().unwrap().get_nr_memslots() as u32, + self.vm_fd.clone(), + self.mem_slots.clone(), + ))) + } } impl HypervisorOps for KvmHypervisor { - fn init_machine(&self) -> Result<()> { + fn init_machine( + &self, + #[cfg(target_arch = "x86_64")] sys_io: &Arc, + sys_mem: &Arc, + ) -> Result<()> { self.arch_init()?; + + sys_mem + .register_listener(self.create_memory_listener()) + .with_context(|| "Failed to register hypervisor listener for memory space.")?; + #[cfg(target_arch = "x86_64")] + sys_io + .register_listener(self.create_io_listener()) + .with_context(|| "Failed to register hypervisor listener for I/O address space.")?; + + Ok(()) + } +} + +impl MigrateOps for KvmHypervisor { + /// Get ram memory region from `KvmHypervisor` structure. + fn get_mem_slots(&self) -> Arc>> { + let mut mgt_mem_slots = HashMap::new(); + for (_, slot) in self.mem_slots.lock().unwrap().iter() { + let mem_slot = MigrateMemSlot { + slot: slot.slot, + guest_phys_addr: slot.guest_phys_addr, + userspace_addr: slot.userspace_addr, + memory_size: slot.memory_size, + }; + mgt_mem_slots.insert(slot.slot, mem_slot); + } + Arc::new(Mutex::new(mgt_mem_slots)) + } + + /// Get dirty page bitmap in kvm. + fn get_dirty_log(&self, slot: u32, mem_size: u64) -> Result> { + self.vm_fd + .as_ref() + .unwrap() + .get_dirty_log(slot, mem_size as usize) + .with_context(|| { + format!( + "Failed to get dirty log, error is {}", + std::io::Error::last_os_error() + ) + }) + } + + /// Start dirty page tracking in kvm. + fn start_dirty_log(&self) -> Result<()> { + for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { + region.flags = KVM_MEM_LOG_DIRTY_PAGES; + // SAFETY: region from `KvmHypervisor` is reliable. + unsafe { + self.vm_fd + .as_ref() + .unwrap() + .set_user_memory_region(*region) + .with_context(|| { + format!( + "Failed to start dirty log, error is {}", + std::io::Error::last_os_error() + ) + })?; + } + } + + Ok(()) + } + + /// Stop dirty page tracking in kvm. + fn stop_dirty_log(&self) -> Result<()> { + for (_, region) in self.mem_slots.lock().unwrap().iter_mut() { + region.flags = 0; + // SAFETY: region from `KvmHypervisor` is reliable. + unsafe { + self.vm_fd + .as_ref() + .unwrap() + .set_user_memory_region(*region) + .with_context(|| { + format!( + "Failed to stop dirty log, error is {}", + std::io::Error::last_os_error() + ) + })?; + } + } + Ok(()) } } diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor_refactor/src/kvm/x86_64/mod.rs index eec24d7d8..415f6e145 100644 --- a/hypervisor_refactor/src/kvm/x86_64/mod.rs +++ b/hypervisor_refactor/src/kvm/x86_64/mod.rs @@ -10,11 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::{Arc, Mutex}; + use anyhow::{Context, Result}; use kvm_bindings::*; -use super::HypervisorError; +use crate::kvm::listener::KvmIoListener; use crate::kvm::KvmHypervisor; +use crate::HypervisorError; +use address_space::Listener; impl KvmHypervisor { pub fn arch_init(&self) -> Result<()> { @@ -39,4 +43,8 @@ impl KvmHypervisor { .create_pit2(pit_config) .with_context(|| HypervisorError::CrtPitErr) } + + pub fn create_io_listener(&self) -> Arc> { + Arc::new(Mutex::new(KvmIoListener::new(self.vm_fd.clone()))) + } } diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index bda5dae3d..ba66bc737 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -15,12 +15,14 @@ pub mod error; pub mod kvm; -pub use error::HypervisorError; - use std::any::Any; +use std::sync::Arc; + +pub use error::HypervisorError; use anyhow::Result; +use address_space::AddressSpace; use machine_manager::machine::HypervisorType; pub trait HypervisorOps: Send + Sync + Any { @@ -28,5 +30,9 @@ pub trait HypervisorOps: Send + Sync + Any { HypervisorType::Kvm } - fn init_machine(&self) -> Result<()>; + fn init_machine( + &self, + #[cfg(target_arch = "x86_64")] sys_io: &Arc, + sys_mem: &Arc, + ) -> Result<()>; } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 7e53374a1..51d8f8a47 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -147,7 +147,9 @@ impl MachineOps for LightMachine { )); trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; - locked_vm.base.hypervisor.lock().unwrap().init_machine()?; + let locked_hypervisor = locked_vm.base.hypervisor.lock().unwrap(); + locked_hypervisor.init_machine(&locked_vm.base.sys_mem)?; + drop(locked_hypervisor); locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.base.sys_mem, @@ -206,6 +208,7 @@ impl MachineOps for LightMachine { } MigrationManager::register_vm_instance(vm.clone()); + MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 274273ecc..9abb09a0b 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -547,7 +547,9 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to register resume event")?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; - locked_vm.base.hypervisor.lock().unwrap().init_machine()?; + let locked_hypervisor = locked_vm.base.hypervisor.lock().unwrap(); + locked_hypervisor.init_machine(&locked_vm.base.sys_mem)?; + drop(locked_hypervisor); locked_vm.init_memory( &vm_config.machine_config.mem_config, &locked_vm.base.sys_mem, @@ -642,6 +644,7 @@ impl MachineOps for StdMachine { MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); + MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 05f7a2c66..639516974 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -42,11 +42,7 @@ use log::warn; #[cfg(feature = "windows_emu_pid")] use vmm_sys_util::eventfd::EventFd; -#[cfg(target_arch = "x86_64")] -use address_space::KvmIoListener; -use address_space::{ - create_backend_mem, create_default_mem, AddressSpace, GuestAddress, KvmMemoryListener, Region, -}; +use address_space::{create_backend_mem, create_default_mem, AddressSpace, GuestAddress, Region}; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; @@ -99,7 +95,7 @@ use machine_manager::config::{ }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{MachineInterface, VmState}; -use migration::MigrationManager; +use migration::{MigrateOps, MigrationManager}; use standard_common::Result as StdResult; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; @@ -151,6 +147,8 @@ pub struct MachineBase { machine_ram: Arc, /// machine hypervisor. hypervisor: Arc>, + /// migrate hypervisor. + migration_hypervisor: Arc>, } impl MachineBase { @@ -214,7 +212,8 @@ impl MachineBase { drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), fwcfg_dev: None, machine_ram, - hypervisor, + hypervisor: hypervisor.clone(), + migration_hypervisor: hypervisor, }) } @@ -379,29 +378,14 @@ pub trait MachineOps { fn init_memory( &self, mem_config: &MachineMemConfig, - #[cfg(target_arch = "x86_64")] sys_io: &Arc, sys_mem: &Arc, nr_cpus: u8, ) -> Result<()> { - // KVM_CREATE_VM system call is invoked when KVM_FDS is used for the first time. The system - // call registers some notifier functions in the KVM, which are frequently triggered when - // doing memory prealloc.To avoid affecting memory prealloc performance, create_host_mmaps - // needs to be invoked first. let migrate_info = self.get_migrate_info(); if migrate_info.0 != MigrateMode::File { self.create_machine_ram(mem_config, nr_cpus)?; } - sys_mem - .register_listener(Arc::new(Mutex::new(KvmMemoryListener::new( - KVM_FDS.load().fd.as_ref().unwrap().get_nr_memslots() as u32, - )))) - .with_context(|| "Failed to register KVM listener for memory space.")?; - #[cfg(target_arch = "x86_64")] - sys_io - .register_listener(Arc::new(Mutex::new(KvmIoListener::default()))) - .with_context(|| "Failed to register KVM listener for I/O address space.")?; - if migrate_info.0 != MigrateMode::File { self.init_machine_ram(sys_mem, mem_config.mem_size)?; } diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index ef40b97ff..cb3e4c733 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -15,6 +15,7 @@ use crate::aarch64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] use crate::x86_64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; use hypervisor::kvm::*; +use hypervisor_refactor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; #[cfg(feature = "usb_camera_v4l2")] diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index b706c938d..233a24762 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -157,10 +157,11 @@ impl MachineOps for LightMachine { )); trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; - locked_vm.base.hypervisor.lock().unwrap().init_machine()?; + let locked_hypervisor = locked_vm.base.hypervisor.lock().unwrap(); + locked_hypervisor.init_machine(&locked_vm.base.sys_io, &locked_vm.base.sys_mem)?; + drop(locked_hypervisor); locked_vm.init_memory( &vm_config.machine_config.mem_config, - &locked_vm.base.sys_io, &locked_vm.base.sys_mem, vm_config.machine_config.nr_cpus, )?; @@ -195,6 +196,7 @@ impl MachineOps for LightMachine { vm_state::KvmDeviceState::descriptor(), Arc::new(vm_state::KvmDevice {}), ); + MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index a549a4755..44339ec2c 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -525,10 +525,11 @@ impl MachineOps for StdMachine { let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; - locked_vm.base.hypervisor.lock().unwrap().init_machine()?; + let locked_hypervisor = locked_vm.base.hypervisor.lock().unwrap(); + locked_hypervisor.init_machine(&locked_vm.base.sys_io, &locked_vm.base.sys_mem)?; + drop(locked_hypervisor); locked_vm.init_memory( &vm_config.machine_config.mem_config, - &locked_vm.base.sys_io, &locked_vm.base.sys_mem, nr_cpus, )?; @@ -613,6 +614,7 @@ impl MachineOps for StdMachine { vm_state::KvmDeviceState::descriptor(), Arc::new(vm_state::KvmDevice {}), ); + MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 8797af7a5..e758bc7dd 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -21,6 +21,7 @@ once_cell = "1.18.0" thiserror = "1.0" anyhow = "1.0" trace = { path = "../trace" } +kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } util = { path = "../util" } [features] diff --git a/migration/src/lib.rs b/migration/src/lib.rs index c7243aea1..f431d479a 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -27,6 +27,8 @@ pub use error::MigrationError; pub use manager::{MigrationHook, MigrationManager}; pub use protocol::{DeviceStateDesc, FieldDesc, MemBlock, MigrationStatus, StateTransfer}; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; use std::time::Duration; use std::{net::TcpStream, os::unix::net::UnixStream, thread}; @@ -34,6 +36,19 @@ use log::error; use machine_manager::qmp::{qmp_response::Response, qmp_schema}; +#[derive(Default, Copy, Clone)] +pub struct MigrateMemSlot { + /// number of a memory slot. + pub slot: u32, + /// Guest address. + pub guest_phys_addr: u64, + /// Host address. + pub userspace_addr: u64, + /// Size of memory. + /// size = 0 represents no-region use this slot. + pub memory_size: u64, +} + /// Start to snapshot VM. /// /// # Arguments @@ -165,3 +180,13 @@ pub fn cancel_migrate() -> Response { Response::create_empty_response() } + +pub trait MigrateOps: Send + Sync { + fn get_mem_slots(&self) -> Arc>>; + + fn get_dirty_log(&self, _slot: u32, _mem_size: u64) -> Result>; + + fn start_dirty_log(&self) -> Result<()>; + + fn stop_dirty_log(&self) -> Result<()>; +} diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 0f0155018..d381ae312 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -24,6 +24,7 @@ use once_cell::sync::Lazy; use crate::general::translate_id; use crate::migration::DirtyBitmap; use crate::protocol::{DeviceStateDesc, MemBlock, MigrationStatus, StateTransfer}; +use crate::MigrateOps; use machine_manager::config::VmConfig; use machine_manager::machine::MachineLifecycle; use util::byte_code::ByteCode; @@ -177,6 +178,8 @@ pub struct Vmm { #[cfg(target_arch = "x86_64")] /// Trait to represent kvm device. pub kvm: Option>, + /// The vector of the object implementing MigrateOps trait. + pub mgt_object: Option>>, } /// Limit of migration. @@ -359,6 +362,16 @@ impl MigrationManager { locked_vmm.gic_group.insert(translate_id(id), gic); } + /// Register migration instance to vmm. + /// + /// # Arguments + /// + /// * `mgt_object` - object with MigrateOps trait. + pub fn register_migration_instance(mgt_object: Arc>) { + let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); + locked_vmm.mgt_object = Some(mgt_object); + } + /// Unregister transport instance from vmm. /// /// # Arguments diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 2f662b688..2782d9cd8 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -17,14 +17,13 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; use anyhow::{anyhow, bail, Context, Result}; -use kvm_bindings::kvm_userspace_memory_region as MemorySlot; use log::{info, warn}; use crate::general::Lifecycle; use crate::manager::MIGRATION_MANAGER; use crate::protocol::{MemBlock, MigrationStatus, Request, Response, TransStatus}; +use crate::MigrateMemSlot; use crate::{MigrationError, MigrationManager}; -use hypervisor::kvm::KVM_FDS; use machine_manager::config::{get_pci_bdf, PciBdf, VmConfig}; use util::unix::host_page_size; @@ -411,17 +410,17 @@ impl MigrationManager { T: Read + Write, { let mut blocks: Vec = Vec::new(); - let slots = KVM_FDS.load().get_mem_slots(); - for (_, slot) in slots.lock().unwrap().iter() { - blocks.push(MemBlock { - gpa: slot.guest_phys_addr, - len: slot.memory_size, - }); + if let Some(mgt_object) = &MIGRATION_MANAGER.vmm.read().unwrap().mgt_object { + let slots = mgt_object.lock().unwrap().get_mem_slots(); + for (_, slot) in slots.lock().unwrap().iter() { + blocks.push(MemBlock { + gpa: slot.guest_phys_addr, + len: slot.memory_size, + }); + } } - Self::send_memory(fd, blocks)?; - - Ok(()) + Self::send_memory(fd, blocks) } /// Send dirty memory data to destination VM. @@ -434,10 +433,12 @@ impl MigrationManager { T: Read + Write, { let mut blocks: Vec = Vec::new(); - let mem_slots = KVM_FDS.load().get_mem_slots(); - for (_, slot) in mem_slots.lock().unwrap().iter() { - let sub_blocks: Vec = Self::get_dirty_log(slot)?; - blocks.extend(sub_blocks); + if let Some(mgt_object) = &MIGRATION_MANAGER.vmm.read().unwrap().mgt_object { + let mem_slots = mgt_object.lock().unwrap().get_mem_slots(); + for (_, slot) in mem_slots.lock().unwrap().iter() { + let sub_blocks: Vec = Self::get_dirty_log(slot)?; + blocks.extend(sub_blocks); + } } if blocks.is_empty() { @@ -669,43 +670,48 @@ impl DirtyBitmap { } pub trait Migratable { - /// Start the dirty log in the kvm and vmm. + /// Start the dirty log in the migration objects and vmm. fn start_dirty_log() -> Result<()> { // Create dirty bitmaps for vmm. let mut bitmaps = HashMap::::new(); - let mem_slots = KVM_FDS.load().get_mem_slots(); - for (_, slot) in mem_slots.lock().unwrap().iter() { - let bitmap = - DirtyBitmap::new(slot.guest_phys_addr, slot.userspace_addr, slot.memory_size); - bitmaps.insert(slot.slot, bitmap); + if let Some(mgt_object) = &MIGRATION_MANAGER.vmm.read().unwrap().mgt_object { + let mem_slots = mgt_object.lock().unwrap().get_mem_slots(); + for (_, slot) in mem_slots.lock().unwrap().iter() { + let bitmap = + DirtyBitmap::new(slot.guest_phys_addr, slot.userspace_addr, slot.memory_size); + bitmaps.insert(slot.slot, bitmap); + } + + // Start logging dirty memory in migration object. + mgt_object.lock().unwrap().start_dirty_log()?; } + let mut vm_bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); *vm_bitmaps = bitmaps; - // Start logging dirty memory in kvm. - KVM_FDS.load().start_dirty_log()?; - Ok(()) } - /// Stop the dirty log in the kvm and vmm. + /// Stop the dirty log in the migration objects and vmm. fn stop_dirty_log() -> Result<()> { // Clear dirty bitmaps from vmm. let mut vm_bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); *vm_bitmaps = HashMap::new(); - // Stop logging dirty memory in kvm. - KVM_FDS.load().stop_dirty_log()?; + if let Some(mgt_object) = &MIGRATION_MANAGER.vmm.read().unwrap().mgt_object { + // Stop logging dirty memory in migration object. + mgt_object.lock().unwrap().stop_dirty_log()?; + } Ok(()) } - /// Collect the dirty log from kvm and vmm. + /// Collect the dirty log from migration object and vmm. /// /// # Arguments /// /// * `slot` - The memory slot. - fn get_dirty_log(slot: &MemorySlot) -> Result> { + fn get_dirty_log(slot: &MigrateMemSlot) -> Result> { // Get dirty memory from vmm. let mut vmm_dirty_bitmap = Vec::new(); let bitmaps = MIGRATION_MANAGER.vmm_bitmaps.write().unwrap(); @@ -715,9 +721,12 @@ pub trait Migratable { } } - // Get dirty memory from kvm. - let vm_dirty_bitmap = KVM_FDS - .load() + // Get dirty memory from migration objects. + let vmm = MIGRATION_MANAGER.vmm.read().unwrap(); + let mgt_object = vmm.mgt_object.as_ref().unwrap(); + let vm_dirty_bitmap = mgt_object + .lock() + .unwrap() .get_dirty_log(slot.slot, slot.memory_size) .unwrap(); @@ -751,11 +760,11 @@ pub trait Migratable { } } - /// sync the dirty log from kvm bitmaps. + /// sync the dirty log from migration object bitmaps. /// /// # Arguments /// - /// * `bitmap` - dirty bitmap from kvm. + /// * `bitmap` - dirty bitmap from migration object. /// * `addr` - Start address of memory slot. fn sync_dirty_bitmap(bitmap: Vec, addr: u64) -> Vec { let page_size = host_page_size(); -- Gitee From 74b0f267417adc559085e1baa5f8c6375fd366b6 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 25 Dec 2023 15:47:11 +0800 Subject: [PATCH 1567/2187] InterruptController: Abstract interfaces related to the interrupt controller of the hypervisor Focuses on redesigning the hypervisor's interfaces for managing interrupt controllers and the interrupt routing table, while eliminating decoupling to the KVM hypervisor. Signed-off-by: Jinhao Gao --- Cargo.lock | 2 + .../src/interrupt_controller/aarch64/gicv2.rs | 193 +------ .../src/interrupt_controller/aarch64/gicv3.rs | 490 ++++-------------- .../src/interrupt_controller/aarch64/mod.rs | 93 +--- .../src/interrupt_controller/aarch64/state.rs | 4 +- devices/src/interrupt_controller/error.rs | 2 - devices/src/interrupt_controller/mod.rs | 14 +- devices/src/lib.rs | 5 +- hypervisor/src/kvm/interrupt.rs | 15 +- hypervisor/src/kvm/mod.rs | 4 - hypervisor_refactor/Cargo.toml | 2 + hypervisor_refactor/src/error.rs | 5 + hypervisor_refactor/src/kvm/aarch64/gicv2.rs | 164 ++++++ hypervisor_refactor/src/kvm/aarch64/gicv3.rs | 380 ++++++++++++++ hypervisor_refactor/src/kvm/aarch64/mod.rs | 52 +- hypervisor_refactor/src/kvm/interrupt.rs | 191 +++++++ hypervisor_refactor/src/kvm/mod.rs | 106 +++- hypervisor_refactor/src/lib.rs | 17 +- machine/src/aarch64/micro.rs | 17 +- machine/src/aarch64/standard.rs | 21 +- machine/src/lib.rs | 2 +- machine/src/micro_common/syscall.rs | 1 + machine/src/x86_64/micro.rs | 16 +- machine/src/x86_64/standard.rs | 21 +- 24 files changed, 1108 insertions(+), 709 deletions(-) create mode 100644 hypervisor_refactor/src/kvm/aarch64/gicv2.rs create mode 100644 hypervisor_refactor/src/kvm/aarch64/gicv3.rs create mode 100644 hypervisor_refactor/src/kvm/interrupt.rs diff --git a/Cargo.lock b/Cargo.lock index da0676ce0..02caa39e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,12 +741,14 @@ version = "2.3.0" dependencies = [ "address_space", "anyhow", + "devices", "kvm-bindings", "kvm-ioctls", "libc", "log", "machine_manager", "migration", + "migration_derive", "thiserror", "util", "vmm-sys-util", diff --git a/devices/src/interrupt_controller/aarch64/gicv2.rs b/devices/src/interrupt_controller/aarch64/gicv2.rs index 319eb4f5a..0e4814bba 100644 --- a/devices/src/interrupt_controller/aarch64/gicv2.rs +++ b/devices/src/interrupt_controller/aarch64/gicv2.rs @@ -13,20 +13,18 @@ use std::marker::{Send, Sync}; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::DeviceFd; +use anyhow::{anyhow, Result}; use log::error; -use super::{GICConfig, GICDevice, KvmDevice, UtilResult}; +use super::{GICConfig, GICDevice, UtilResult}; use crate::interrupt_controller::InterruptError; use address_space::AddressSpace; -use hypervisor::kvm::KVM_FDS; use machine_manager::machine::{MachineLifecycle, VmState}; use util::device_tree::{self, FdtBuilder}; // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. -const KVM_VGIC_V2_DIST_SIZE: u64 = 0x1000; -const KVM_VGIC_V2_CPU_SIZE: u64 = 0x2000; +const VGIC_V2_DIST_SIZE: u64 = 0x1000; +const VGIC_V2_CPU_SIZE: u64 = 0x2000; /// Configure a v2 Interrupt controller. pub struct GICv2Config { @@ -41,7 +39,9 @@ pub struct GICv2Config { } /// Access wrapper for GICv2. -pub trait GICv2Access { +pub trait GICv2Access: Send + Sync { + fn init_gic(&self, nr_irqs: u32, dist_base: u64, cpu_if_base: u64) -> Result<()>; + /// Returns `gicr_attr` of `vCPU`. fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64; @@ -54,8 +54,11 @@ pub trait GICv2Access { gicc_value: &mut u64, write: bool, ) -> Result<()>; + + fn pause(&self) -> Result<()>; } +#[derive(Default)] struct GicCpuInterfaceRegion { /// Base address. base: u64, @@ -63,6 +66,7 @@ struct GicCpuInterfaceRegion { size: u64, } +#[derive(Default)] struct GicDistGuestRegion { /// Base address. base: u64, @@ -72,8 +76,8 @@ struct GicDistGuestRegion { /// A wrapper around creating and managing a `GICv2`. pub struct GICv2 { - /// The fd for the GICv2 device. - fd: DeviceFd, + /// The handler for the GICv2 device to access the corresponding device in hypervisor. + pub hypervisor_gic: Arc, /// Maximum irq number. nr_irqs: u32, /// GICv2 cpu interface region. @@ -85,7 +89,7 @@ pub struct GICv2 { } impl GICv2 { - fn new(config: &GICConfig) -> Result { + pub fn new(hypervisor_gic: Arc, config: &GICConfig) -> Result { let v2config = match config.v2.as_ref() { Some(v2) => v2, None => { @@ -95,103 +99,28 @@ impl GICv2 { } }; - let mut gic_device = kvm_bindings::kvm_create_device { - type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, - fd: 0, - flags: 0, - }; - - let gic_fd = KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_device(&mut gic_device) - .with_context(|| "Failed to create GICv2 device")?; - let cpu_interface_region = GicCpuInterfaceRegion { - base: v2config.dist_range.0 + KVM_VGIC_V2_DIST_SIZE, - size: KVM_VGIC_V2_CPU_SIZE, + base: v2config.dist_range.0 + VGIC_V2_DIST_SIZE, + size: VGIC_V2_CPU_SIZE, }; let dist_guest_region = GicDistGuestRegion { base: v2config.dist_range.0, size: v2config.dist_range.1, }; - let gicv2 = GICv2 { - fd: gic_fd, + Ok(GICv2 { + hypervisor_gic, nr_irqs: config.max_irq, cpu_interface_region, dist_guest_region, state: Arc::new(Mutex::new(VmState::Created)), - }; - - Ok(gicv2) - } - - fn realize(&self) -> Result<()> { - KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; - - // Init the interrupt number support by the GIC. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, - 0, - &self.nr_irqs as *const u32 as u64, - true, - ) - .with_context(|| "Failed to set GICv2 attribute: irqs")?; - - // Finalize the GIC. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), - 0, - true, - ) - .with_context(|| "KVM failed to initialize GICv2")?; - - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST), - &self.dist_guest_region.base as *const u64 as u64, - true, - ) - .with_context(|| "Failed to set GICv2 attribute: distributor address")?; - - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU), - &self.cpu_interface_region.base as *const u64 as u64, - true, - ) - .with_context(|| "Failed to set GICv2 attribute: cpu address")?; - - *self.state.lock().unwrap() = VmState::Running; - - Ok(()) - } - - fn device_fd(&self) -> &DeviceFd { - &self.fd + }) } } impl MachineLifecycle for GICv2 { fn pause(&self) -> bool { - // VM change state will flush REDIST pending tables into guest RAM. - if KvmDevice::kvm_device_access( - self.device_fd(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, - 0, - true, - ) - .is_ok() - { + if self.hypervisor_gic.pause().is_ok() { *self.state.lock().unwrap() = VmState::Running; true } else { @@ -214,52 +143,14 @@ impl MachineLifecycle for GICv2 { } } -impl GICv2Access for GICv2 { - fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { - (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) - & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK) - | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) - & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) - } - - fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, - offset, - gicd_value as *mut u32 as u64, - write, - ) - .with_context(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) - } - - fn access_gic_cpu( - &self, - offset: u64, - cpu: usize, - gicc_value: &mut u64, - write: bool, - ) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, - self.vcpu_gicr_attr(offset, cpu), - gicc_value as *mut u64 as u64, - write, - ) - .with_context(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) - } -} - impl GICDevice for GICv2 { - fn create_device(gic_conf: &GICConfig) -> Result> { - let gicv2 = Arc::new(GICv2::new(gic_conf)?); - - Ok(gicv2) - } - fn realize(&self) -> Result<()> { - self.realize().with_context(|| "Failed to realize GICv2")?; + let dist_base = self.dist_guest_region.base; + let cpu_if_base = self.cpu_interface_region.base; + self.hypervisor_gic + .init_gic(self.nr_irqs, dist_base, cpu_if_base)?; + + *self.state.lock().unwrap() = VmState::Running; Ok(()) } @@ -294,35 +185,3 @@ impl GICDevice for GICv2 { Ok(()) } } - -#[cfg(test)] -mod tests { - use super::super::GICVersion; - use super::super::GICv2Config; - use super::*; - use crate::GIC_IRQ_MAX; - use hypervisor::kvm::KVMFds; - - #[test] - fn test_create_gicv2() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let gic_conf = GICConfig { - version: Some(GICVersion::GICv2), - vcpu_count: 4, - max_irq: GIC_IRQ_MAX, - v2: Some(GICv2Config { - dist_range: (0x0800_0000, 0x0001_0000), - cpu_range: (0x080A_0000, 0x00F6_0000), - v2m_range: None, - sys_mem: None, - }), - v3: None, - }; - assert!(GICv2::new(&gic_conf).is_ok()); - } -} diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 16d13a241..283c3c07d 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -13,25 +13,17 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::DeviceFd; use log::{error, info}; -use super::{ - state::{GICv3ItsState, GICv3State}, - GICConfig, GICDevice, KvmDevice, UtilResult, -}; +use super::{GICConfig, GICDevice, UtilResult}; use crate::interrupt_controller::error::InterruptError; -use hypervisor::kvm::KVM_FDS; use machine_manager::machine::{MachineLifecycle, VmState}; -use migration::{ - snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, - MigrationManager, StateTransfer, -}; +use migration::StateTransfer; use util::device_tree::{self, FdtBuilder}; // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. const SZ_64K: u64 = 0x0001_0000; -const KVM_VGIC_V3_REDIST_SIZE: u64 = 2 * SZ_64K; +const VGIC_V3_REDIST_SIZE: u64 = 2 * SZ_64K; /// Configure a v3 Interrupt controller. pub struct GICv3Config { @@ -46,7 +38,14 @@ pub struct GICv3Config { } /// Access wrapper for GICv3. -pub trait GICv3Access { +pub trait GICv3Access: Send + Sync { + fn init_gic( + &self, + nr_irqs: u32, + redist_regions: Vec, + dist_base: u64, + ) -> Result<()>; + /// Returns `gicr_attr` of `vCPU`. fn vcpu_gicr_attr(&self, cpu: usize) -> u64; @@ -69,21 +68,24 @@ pub trait GICv3Access { ) -> Result<()>; fn access_gic_line_level(&self, offset: u64, gicll_value: &mut u32, write: bool) -> Result<()>; + + fn pause(&self) -> Result<()>; } -struct GicRedistRegion { +#[derive(Clone, Copy)] +pub struct GicRedistRegion { /// Base address. - base: u64, + pub base: u64, /// Size of redistributor region. size: u64, /// Attribute of redistributor region. - base_attr: u64, + pub base_attr: u64, } /// A wrapper around creating and managing a `GICv3`. pub struct GICv3 { - /// The fd for the GICv3 device. - fd: DeviceFd, + /// The handler for the GICv3 device to access the corresponding device in hypervisor. + pub hypervisor_gic: Arc, /// Number of vCPUs, determines the number of redistributor and CPU interface. pub(crate) vcpu_count: u64, /// GICv3 ITS device. @@ -91,7 +93,7 @@ pub struct GICv3 { /// Maximum irq number. pub(crate) nr_irqs: u32, /// GICv3 redistributor info, support multiple redistributor regions. - redist_regions: Vec, + pub redist_regions: Vec, /// Base address in the guest physical address space of the GICv3 distributor /// register mappings. dist_base: u64, @@ -102,7 +104,11 @@ pub struct GICv3 { } impl GICv3 { - fn new(config: &GICConfig) -> Result { + pub fn new( + hypervisor_gic: Arc, + its_handler: Arc, + config: &GICConfig, + ) -> Result { let v3config = match config.v3.as_ref() { Some(v3) => v3, None => { @@ -111,27 +117,11 @@ impl GICv3 { ))) } }; - let mut gic_device = kvm_bindings::kvm_create_device { - type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, - fd: 0, - flags: 0, - }; - - let gic_fd = match KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_device(&mut gic_device) - { - Ok(fd) => fd, - Err(e) => return Err(anyhow!(InterruptError::CreateKvmDevice(e))), - }; // Calculate GIC redistributor regions' address range according to vcpu count. let base = v3config.redist_region_ranges[0].0; let size = v3config.redist_region_ranges[0].1; - let redist_capability = size / KVM_VGIC_V3_REDIST_SIZE; + let redist_capability = size / VGIC_V3_REDIST_SIZE; let redist_region_count = std::cmp::min(config.vcpu_count, redist_capability); let mut redist_regions = vec![GicRedistRegion { base, @@ -146,13 +136,13 @@ impl GICv3 { redist_regions.push(GicRedistRegion { base: high_redist_base, - size: high_redist_region_count * KVM_VGIC_V3_REDIST_SIZE, + size: high_redist_region_count * VGIC_V3_REDIST_SIZE, base_attr: high_redist_attr, }) } let mut gicv3 = GICv3 { - fd: gic_fd, + hypervisor_gic, vcpu_count: config.vcpu_count, nr_irqs: config.max_irq, its_dev: None, @@ -163,89 +153,12 @@ impl GICv3 { }; if let Some(its_range) = v3config.its_range { - gicv3.its_dev = Some(Arc::new( - GICv3Its::new(&its_range).with_context(|| "Failed to create ITS")?, - )); + gicv3.its_dev = Some(Arc::new(GICv3Its::new(its_handler, &its_range))); } Ok(gicv3) } - fn realize(&self) -> Result<()> { - if self.redist_regions.len() > 1 { - KvmDevice::kvm_device_check( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION as u64, - ) - .with_context(|| { - "Multiple redistributors are acquired while KVM does not provide support." - })?; - } - - if self.redist_regions.len() == 1 { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST), - &self.redist_regions.get(0).unwrap().base as *const u64 as u64, - true, - ) - .with_context(|| "Failed to set GICv3 attribute: redistributor address")?; - } else { - for redist in &self.redist_regions { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION), - &redist.base_attr as *const u64 as u64, - true, - ) - .with_context(|| "Failed to set GICv3 attribute: redistributor region address")?; - } - } - - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST), - &self.dist_base as *const u64 as u64, - true, - ) - .with_context(|| "Failed to set GICv3 attribute: distributor address")?; - - KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; - - // Init the interrupt number support by the GIC. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, - 0, - &self.nr_irqs as *const u32 as u64, - true, - ) - .with_context(|| "Failed to set GICv3 attribute: irqs")?; - - // Finalize the GIC. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), - 0, - true, - ) - .with_context(|| "KVM failed to initialize GICv3")?; - - let mut state = self.state.lock().unwrap(); - *state = VmState::Running; - - Ok(()) - } - - fn device_fd(&self) -> &DeviceFd { - &self.fd - } - fn reset_its_state(&self) -> Result<()> { if let Some(its) = &self.its_dev { its.reset()?; @@ -259,26 +172,64 @@ impl GICv3 { self.set_state(&reset_state) .with_context(|| "Failed to reset gic") } + + pub(crate) fn access_gic_distributor( + &self, + offset: u64, + gicd_value: &mut u32, + write: bool, + ) -> Result<()> { + self.hypervisor_gic + .access_gic_distributor(offset, gicd_value, write) + } + + pub(crate) fn access_gic_redistributor( + &self, + offset: u64, + cpu: usize, + gicr_value: &mut u32, + write: bool, + ) -> Result<()> { + self.hypervisor_gic + .access_gic_redistributor(offset, cpu, gicr_value, write) + } + + pub(crate) fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()> { + self.hypervisor_gic + .access_gic_cpu(offset, cpu, gicc_value, write) + } + + pub(crate) fn access_gic_line_level( + &self, + offset: u64, + gicll_value: &mut u32, + write: bool, + ) -> Result<()> { + self.hypervisor_gic + .access_gic_line_level(offset, gicll_value, write) + } } impl MachineLifecycle for GICv3 { fn pause(&self) -> bool { // VM change state will flush REDIST pending tables into guest RAM. - if KvmDevice::kvm_device_access( - self.device_fd(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, - 0, - true, - ) - .is_err() - { + if let Err(e) = self.hypervisor_gic.pause() { + error!( + "Failed to flush REDIST pending tables into guest RAM, error: {:?}", + e + ); return false; } // The ITS tables need to be flushed into guest RAM before VM pause. if let Some(its_dev) = &self.its_dev { - if let Err(e) = its_dev.access_gic_its_tables(true) { + if let Err(e) = its_dev.its_handler.access_gic_its_tables(true) { error!("Failed to access GIC ITS tables, error: {:?}", e); return false; } @@ -305,111 +256,19 @@ impl MachineLifecycle for GICv3 { } } -impl GICv3Access for GICv3 { - fn vcpu_gicr_attr(&self, cpu: usize) -> u64 { - let clustersz = 16; - - let aff1 = (cpu / clustersz) as u64; - let aff0 = (cpu % clustersz) as u64; - - let affid = (aff1 << 8) | aff0; - let cpu_affid: u64 = ((affid & 0xFF_0000_0000) >> 8) | (affid & 0xFF_FFFF); - - let last = u64::from((self.vcpu_count - 1) == cpu as u64); - - ((cpu_affid << 32) | (1 << 24) | (1 << 8) | (last << 4)) - & kvm_bindings::KVM_DEV_ARM_VGIC_V3_MPIDR_MASK as u64 - } - - fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, - offset, - gicd_value as *mut u32 as u64, - write, - ) - .with_context(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) - } - - fn access_gic_redistributor( - &self, - offset: u64, - cpu: usize, - gicr_value: &mut u32, - write: bool, - ) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, - self.vcpu_gicr_attr(cpu) | offset, - gicr_value as *mut u32 as u64, - write, - ) - .with_context(|| { - format!( - "Failed to access gic redistributor for offset 0x{:x}", - offset - ) - }) - } - - fn access_gic_cpu( - &self, - offset: u64, - cpu: usize, - gicc_value: &mut u64, - write: bool, - ) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, - self.vcpu_gicr_attr(cpu) | offset, - gicc_value as *mut u64 as u64, - write, - ) - .with_context(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) - } - - fn access_gic_line_level(&self, offset: u64, gicll_value: &mut u32, write: bool) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, - self.vcpu_gicr_attr(0) | offset, - gicll_value as *mut u32 as u64, - write, - ) - } -} - impl GICDevice for GICv3 { - fn create_device( - gic_conf: &GICConfig, - ) -> Result> { - let gicv3 = Arc::new(GICv3::new(gic_conf)?); - if gicv3.its_dev.is_some() { - MigrationManager::register_gic_instance( - GICv3ItsState::descriptor(), - gicv3.its_dev.as_ref().unwrap().clone(), - GICV3_ITS_SNAPSHOT_ID, - ); - } - MigrationManager::register_gic_instance( - GICv3State::descriptor(), - gicv3.clone(), - GICV3_SNAPSHOT_ID, - ); - - Ok(gicv3) - } - fn realize(&self) -> Result<()> { - self.realize().with_context(|| "Failed to realize GICv3")?; + self.hypervisor_gic + .init_gic(self.nr_irqs, self.redist_regions.clone(), self.dist_base) + .with_context(|| "Failed to init GICv3")?; if let Some(its) = &self.its_dev { its.realize().with_context(|| "Failed to realize ITS")?; } + let mut state = self.state.lock().unwrap(); + *state = VmState::Running; + Ok(()) } @@ -457,9 +316,9 @@ impl GICDevice for GICv3 { } fn reset(&self) -> Result<()> { - info!("Reset gicv3"); - + info!("Reset gicv3its"); self.reset_its_state()?; + info!("Reset gicv3"); self.reset_gic_state() } @@ -468,9 +327,19 @@ impl GICDevice for GICv3 { } } +pub trait GICv3ItsAccess: Send + Sync { + fn init_gic_its(&self, msi_base: u64) -> Result<()>; + + fn access_gic_its(&self, attr: u32, its_value: &mut u64, write: bool) -> Result<()>; + + fn access_gic_its_tables(&self, save: bool) -> Result<()>; + + fn reset(&self) -> Result<()>; +} + pub struct GICv3Its { - /// The fd for the GICv3Its device - fd: DeviceFd, + /// The handler for the GICv3Its device to access the corresponding device in hypervisor. + pub its_handler: Arc, /// Base address in the guest physical address space of the GICv3 ITS /// control register frame. @@ -481,178 +350,29 @@ pub struct GICv3Its { } impl GICv3Its { - fn new(its_range: &(u64, u64)) -> Result { - let mut its_device = kvm_bindings::kvm_create_device { - type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS, - fd: 0, - flags: 0, - }; - - let its_fd = match KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_device(&mut its_device) - { - Ok(fd) => fd, - Err(e) => return Err(anyhow!(InterruptError::CreateKvmDevice(e))), - }; - - Ok(GICv3Its { - fd: its_fd, + fn new(its_handler: Arc, its_range: &(u64, u64)) -> Self { + GICv3Its { + its_handler, msi_base: its_range.0, msi_size: its_range.1, - }) + } } fn realize(&self) -> Result<()> { - KvmDevice::kvm_device_check( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), - ) - .with_context(|| "ITS address attribute is not supported for KVM")?; - - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), - &self.msi_base as *const u64 as u64, - true, - ) - .with_context(|| "Failed to set ITS attribute: ITS address")?; - - // Finalize the GIC Its. - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), - &self.msi_base as *const u64 as u64, - true, - ) - .with_context(|| "KVM failed to initialize ITS")?; + self.its_handler.init_gic_its(self.msi_base)?; Ok(()) } pub(crate) fn access_gic_its(&self, attr: u32, its_value: &mut u64, write: bool) -> Result<()> { - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - attr as u64, - its_value as *const u64 as u64, - write, - ) + self.its_handler.access_gic_its(attr, its_value, write) } pub(crate) fn access_gic_its_tables(&self, save: bool) -> Result<()> { - let attr = if save { - kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES as u64 - } else { - kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES as u64 - }; - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - attr, - std::ptr::null::() as u64, - true, - ) + self.its_handler.access_gic_its_tables(save) } pub(crate) fn reset(&self) -> Result<()> { - info!("Reset gicv3 its"); - KvmDevice::kvm_device_access( - &self.fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - u64::from(kvm_bindings::KVM_DEV_ARM_ITS_CTRL_RESET), - std::ptr::null::() as u64, - true, - ) - .with_context(|| "Failed to reset ITS") - } -} - -#[cfg(test)] -mod tests { - use super::super::GICVersion; - use super::super::GICv3Config; - use super::*; - use crate::GIC_IRQ_MAX; - use hypervisor::kvm::KVMFds; - - #[test] - fn test_create_gicv3() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let gic_conf = GICConfig { - version: Some(GICVersion::GICv3), - vcpu_count: 4, - max_irq: GIC_IRQ_MAX, - v2: None, - v3: Some(GICv3Config { - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, - }), - }; - assert!(GICv3::new(&gic_conf).is_ok()); - } - - #[test] - fn test_create_gic_device() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let gic_config = GICConfig { - version: Some(GICVersion::GICv3), - vcpu_count: 4_u64, - max_irq: GIC_IRQ_MAX, - v2: None, - v3: Some(GICv3Config { - msi: false, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], - its_range: None, - }), - }; - let gic = GICv3::new(&gic_config).unwrap(); - assert!(gic.its_dev.is_none()); - assert!(GICv3::new(&gic_config).is_err()); - } - - #[test] - fn test_gic_redist_regions() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let gic_config = GICConfig { - version: Some(GICVersion::GICv3), - vcpu_count: 210_u64, - max_irq: GIC_IRQ_MAX, - v3: Some(GICv3Config { - msi: true, - dist_range: (0x0800_0000, 0x0001_0000), - redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], - its_range: Some((0x0808_0000, 0x0002_0000)), - }), - v2: None, - }; - let gic = GICv3::new(&gic_config).unwrap(); - - assert!(gic.its_dev.is_some()); - assert_eq!(gic.redist_regions.len(), 2); + self.its_handler.reset() } } diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 046d0c403..0bb78e963 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -12,16 +12,21 @@ mod gicv2; mod gicv3; -#[allow(dead_code)] mod state; -pub use gicv2::{GICv2, GICv2Config}; -pub use gicv3::{GICv3, GICv3Config}; +pub use gicv2::GICv2; +pub use gicv2::GICv2Access; +pub use gicv2::GICv2Config; +pub use gicv3::GICv3; +pub use gicv3::GICv3Access; +pub use gicv3::GICv3Config; +pub use gicv3::GICv3ItsAccess; +pub use gicv3::GicRedistRegion; +pub use state::{GICv3ItsState, GICv3State}; use std::sync::Arc; use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::DeviceFd; use crate::interrupt_controller::error::InterruptError; use machine_manager::machine::{MachineLifecycle, VmState}; @@ -41,49 +46,6 @@ pub enum GICVersion { GICv3, } -/// A wrapper for kvm_based device check and access. -pub struct KvmDevice; - -impl KvmDevice { - fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr: 0, - flags: 0, - }; - fd.has_device_attr(&attr) - .with_context(|| "Failed to check device attributes for GIC.")?; - Ok(()) - } - - fn kvm_device_access( - fd: &DeviceFd, - group: u32, - attr: u64, - addr: u64, - write: bool, - ) -> Result<()> { - let attr = kvm_bindings::kvm_device_attr { - group, - attr, - addr, - flags: 0, - }; - - if write { - fd.set_device_attr(&attr) - .with_context(|| "Failed to set device attributes for GIC.")?; - } else { - let mut attr = attr; - fd.get_device_attr(&mut attr) - .with_context(|| "Failed to get device attributes for GIC.")?; - }; - - Ok(()) - } -} - pub struct GICConfig { /// Config GIC version pub version: Option, @@ -98,7 +60,7 @@ pub struct GICConfig { } impl GICConfig { - fn check_sanity(&self) -> Result<()> { + pub fn check_sanity(&self) -> Result<()> { if self.max_irq <= GIC_IRQ_INTERNAL { return Err(anyhow!(InterruptError::InvalidConfig( "GIC irq numbers need above 32".to_string() @@ -110,19 +72,7 @@ impl GICConfig { /// A wrapper for `GIC` must perform the function. pub trait GICDevice: MachineLifecycle { - /// Constructs a kvm_based `GIC` device. - /// - /// # Arguments - /// - /// * `vm` - File descriptor for vmfd. - /// * `gic_conf` - Configuration for `GIC`. - fn create_device( - gic_conf: &GICConfig, - ) -> Result> - where - Self: Sized; - - /// Realize function for kvm_based `GIC` device. + /// Realize function for hypervisor_based `GIC` device. fn realize(&self) -> Result<()>; /// Reset 'GIC' @@ -143,29 +93,22 @@ pub trait GICDevice: MachineLifecycle { } } -/// A wrapper around creating and using a kvm-based interrupt controller. +#[derive(Clone)] +/// A wrapper around creating and using a hypervisor-based interrupt controller. pub struct InterruptController { gic: Arc, } impl InterruptController { - /// Constructs a new kvm_based `InterruptController`. + /// Constructs a new hypervisor_based `InterruptController`. /// /// # Arguments /// /// * `gic_conf` - Configuration for `GIC`. - pub fn new(gic_conf: &GICConfig) -> Result { - gic_conf.check_sanity()?; - let gic = match &gic_conf.version { - Some(GICVersion::GICv3) => GICv3::create_device(gic_conf), - Some(GICVersion::GICv2) => GICv2::create_device(gic_conf), - // Try v3 first if no version specified. - None => GICv3::create_device(gic_conf).or_else(|_| GICv2::create_device(gic_conf)), - }; - let intc = InterruptController { - gic: gic.with_context(|| "Failed to realize GIC")?, - }; - Ok(intc) + pub fn new( + gic: Arc, + ) -> InterruptController { + InterruptController { gic } } pub fn realize(&self) -> Result<()> { diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index dff4adb60..bb40c9dd6 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -15,14 +15,14 @@ use std::mem::size_of; use anyhow::Context; use libc::c_uint; -use super::gicv3::{GICv3, GICv3Access, GICv3Its}; +use super::gicv3::{GICv3, GICv3Its}; use super::GIC_IRQ_INTERNAL; use crate::interrupt_controller::Result; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -/// Register data length can be get by `get_device_attr/set_device_attr` in kvm once. +/// Register data length can be get in hypervisor once. const REGISTER_SIZE: u64 = size_of::() as u64; /// Distributor registers, as offsets from the distributor base address diff --git a/devices/src/interrupt_controller/error.rs b/devices/src/interrupt_controller/error.rs index b008d64bd..191d6f7b7 100644 --- a/devices/src/interrupt_controller/error.rs +++ b/devices/src/interrupt_controller/error.rs @@ -16,6 +16,4 @@ use thiserror::Error; pub enum InterruptError { #[error("Invalid GIC config: {0}")] InvalidConfig(String), - #[error("Failed to create KVM device: {0:#?}.")] - CreateKvmDevice(kvm_ioctls::Error), } diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 7738ae6af..32b3320e5 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -17,7 +17,7 @@ //! ## Design //! //! This module offers support for: -//! 1. Create kvm-based interrupt controller. +//! 1. Create hypervisor-based interrupt controller. //! 2. Manager lifecycle for `GIC`. //! //! ## Platform Support @@ -30,10 +30,10 @@ mod error; pub use anyhow::Result; -pub use aarch64::GICConfig as ICGICConfig; -pub use aarch64::GICv2Config as ICGICv2Config; -pub use aarch64::GICv3Config as ICGICv3Config; -pub use aarch64::InterruptController; -pub use aarch64::GIC_IRQ_INTERNAL; -pub use aarch64::GIC_IRQ_MAX; +#[cfg(target_arch = "aarch64")] +pub use aarch64::{ + GICConfig as ICGICConfig, GICDevice, GICVersion, GICv2, GICv2Access, + GICv2Config as ICGICv2Config, GICv3, GICv3Access, GICv3Config as ICGICv3Config, GICv3ItsAccess, + GICv3ItsState, GICv3State, GicRedistRegion, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX, +}; pub use error::InterruptError; diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 0e5ec2091..1611a4ef9 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -32,8 +32,9 @@ mod interrupt_controller; #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ - ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, InterruptError as IntCtrlErrs, - GIC_IRQ_INTERNAL, GIC_IRQ_MAX, + GICDevice, GICVersion, GICv2, GICv2Access, GICv3, GICv3Access, GICv3ItsAccess, GICv3ItsState, + GICv3State, GicRedistRegion, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, + InterruptError as IntCtrlErrs, GIC_IRQ_INTERNAL, GIC_IRQ_MAX, }; pub use legacy::error::LegacyError as LegacyErrs; pub use scsi::bus as ScsiBus; diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index d0ad55ea9..3a9cc6a42 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -11,9 +11,8 @@ // See the Mulan PSL v2 for more details. use anyhow::{Context, Result}; -use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; +use kvm_bindings::{KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm}; -use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; use util::bitmap::Bitmap; @@ -22,8 +21,6 @@ pub(crate) type IrqRouteEntry = kvm_bindings::kvm_irq_routing_entry; type IrqRouteChip = kvm_bindings::kvm_irq_routing_irqchip; type IrqChip = kvm_bindings::kvm_irq_routing_entry__bindgen_ty_1; -ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); - #[cfg(target_arch = "x86_64")] const IOAPIC_NUM_PINS: u32 = 24; #[cfg(target_arch = "x86_64")] @@ -221,16 +218,6 @@ pub struct MsiVector { #[cfg(test)] mod tests { use super::super::KVMFds; - use super::get_maximum_gsi_cnt; - - #[test] - fn test_get_maximum_gsi_cnt() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - assert!(get_maximum_gsi_cnt(kvm_fds.fd.as_ref().unwrap()) > 0); - } #[test] fn test_alloc_and_release_gsi() { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index e61c0041c..c639b5e01 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -30,7 +30,6 @@ use vmm_sys_util::{ use interrupt::{IrqRoute, IrqRouteEntry, IrqRouteTable}; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h -pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; pub const KVM_SIGNAL_MSI: u32 = 0x4020_aea5; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h @@ -78,14 +77,11 @@ ioctl_ior_nr!(KVM_GET_DEBUGREGS, KVMIO, 0xa1, kvm_debugregs); ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); #[cfg(target_arch = "x86_64")] ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); -ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); #[cfg(target_arch = "aarch64")] -ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); -#[cfg(target_arch = "aarch64")] ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); #[cfg(target_arch = "aarch64")] ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor_refactor/Cargo.toml index 1ebbee623..f2e19a633 100644 --- a/hypervisor_refactor/Cargo.toml +++ b/hypervisor_refactor/Cargo.toml @@ -14,6 +14,8 @@ libc = "0.2" log = "0.4" vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } +devices = { path = "../devices" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } +migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } diff --git a/hypervisor_refactor/src/error.rs b/hypervisor_refactor/src/error.rs index f761f550a..829a8bbe0 100644 --- a/hypervisor_refactor/src/error.rs +++ b/hypervisor_refactor/src/error.rs @@ -24,4 +24,9 @@ pub enum HypervisorError { #[cfg(target_arch = "x86_64")] #[error("Failed to create PIT.")] CrtPitErr, + #[error("Failed to create irq chip.")] + #[cfg(target_arch = "x86_64")] + CrtIrqchipErr, + #[error("Failed to create KVM device: {0:#?}.")] + CreateKvmDevice(kvm_ioctls::Error), } diff --git a/hypervisor_refactor/src/kvm/aarch64/gicv2.rs b/hypervisor_refactor/src/kvm/aarch64/gicv2.rs new file mode 100644 index 000000000..3cc04599f --- /dev/null +++ b/hypervisor_refactor/src/kvm/aarch64/gicv2.rs @@ -0,0 +1,164 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::Arc; + +use anyhow::{anyhow, Context, Result}; +use kvm_ioctls::{DeviceFd, VmFd}; + +use super::KvmDevice; +use crate::error::HypervisorError; +use devices::GICv2Access; + +pub struct KvmGICv2 { + fd: DeviceFd, +} + +impl KvmGICv2 { + pub fn new(vm_fd: Arc) -> Result { + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2, + fd: 0, + flags: 0, + }; + + let gic_fd = match vm_fd.create_device(&mut gic_device) { + Ok(fd) => fd, + Err(e) => return Err(anyhow!(HypervisorError::CreateKvmDevice(e))), + }; + + Ok(Self { fd: gic_fd }) + } +} + +impl GICv2Access for KvmGICv2 { + fn init_gic(&self, nr_irqs: u32, dist_base: u64, cpu_if_base: u64) -> Result<()> { + KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; + + // Init the interrupt number support by the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, + &nr_irqs as *const u32 as u64, + true, + ) + .with_context(|| "Failed to set GICv2 attribute: irqs")?; + + // Finalize the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + 0, + true, + ) + .with_context(|| "KVM failed to initialize GICv2")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_DIST), + &dist_base as *const u64 as u64, + true, + ) + .with_context(|| "Failed to set GICv2 attribute: distributor address")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V2_ADDR_TYPE_CPU), + &cpu_if_base as *const u64 as u64, + true, + ) + .with_context(|| "Failed to set GICv2 attribute: cpu address") + } + + fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { + (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK) + | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) + & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) + } + + fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + offset, + gicd_value as *mut u32 as u64, + write, + ) + .with_context(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) + } + + fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + self.vcpu_gicr_attr(offset, cpu), + gicc_value as *mut u64 as u64, + write, + ) + .with_context(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) + } + + fn pause(&self) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, + 0, + true, + ) + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use devices::{GICDevice, GICVersion, GICv2, ICGICConfig, ICGICv2Config, GIC_IRQ_MAX}; + + use crate::kvm::aarch64::gicv2::KvmGICv2; + use crate::kvm::KvmHypervisor; + + #[test] + fn test_create_kvm_gicv2() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let gic_conf = ICGICConfig { + version: Some(GICVersion::GICv2), + vcpu_count: 4, + max_irq: GIC_IRQ_MAX, + v2: Some(ICGICv2Config { + dist_range: (0x0800_0000, 0x0001_0000), + cpu_range: (0x080A_0000, 0x00F6_0000), + v2m_range: None, + sys_mem: None, + }), + v3: None, + }; + let hypervisor_gic = KvmGICv2::new(kvm_hyp.vm_fd.clone().unwrap()).unwrap(); + let gic = GICv2::new(Arc::new(hypervisor_gic), &gic_conf).unwrap(); + assert!(gic.realize().is_ok()); + } +} diff --git a/hypervisor_refactor/src/kvm/aarch64/gicv3.rs b/hypervisor_refactor/src/kvm/aarch64/gicv3.rs new file mode 100644 index 000000000..6a8d09e1b --- /dev/null +++ b/hypervisor_refactor/src/kvm/aarch64/gicv3.rs @@ -0,0 +1,380 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::Arc; + +use anyhow::{anyhow, Context, Result}; +use kvm_ioctls::{DeviceFd, VmFd}; +use log::info; + +use super::KvmDevice; +use crate::error::HypervisorError; +use devices::{GICv3Access, GICv3ItsAccess, GicRedistRegion}; + +pub struct KvmGICv3 { + fd: DeviceFd, + vcpu_count: u64, +} + +impl KvmGICv3 { + pub fn new(vm_fd: Arc, vcpu_count: u64) -> Result { + let mut gic_device = kvm_bindings::kvm_create_device { + type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3, + fd: 0, + flags: 0, + }; + + let gic_fd = match vm_fd.create_device(&mut gic_device) { + Ok(fd) => fd, + Err(e) => return Err(anyhow!(HypervisorError::CreateKvmDevice(e))), + }; + + Ok(Self { + fd: gic_fd, + vcpu_count, + }) + } +} + +impl GICv3Access for KvmGICv3 { + fn init_gic( + &self, + nr_irqs: u32, + redist_regions: Vec, + dist_base: u64, + ) -> Result<()> { + if redist_regions.len() > 1 { + KvmDevice::kvm_device_check( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION as u64, + ) + .with_context(|| { + "Multiple redistributors are acquired while KVM does not provide support." + })?; + } + + if redist_regions.len() == 1 { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST), + &redist_regions.get(0).unwrap().base as *const u64 as u64, + true, + ) + .with_context(|| "Failed to set GICv3 attribute: redistributor address")?; + } else { + for redist in &redist_regions { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION), + &redist.base_attr as *const u64 as u64, + true, + ) + .with_context(|| "Failed to set GICv3 attribute: redistributor region address")?; + } + } + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST), + &dist_base as *const u64 as u64, + true, + ) + .with_context(|| "Failed to set GICv3 attribute: distributor address")?; + + KvmDevice::kvm_device_check(&self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)?; + + // Init the interrupt number support by the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, + &nr_irqs as *const u32 as u64, + true, + ) + .with_context(|| "Failed to set GICv3 attribute: irqs")?; + + // Finalize the GIC. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + 0, + true, + ) + .with_context(|| "KVM failed to initialize GICv3") + } + + fn vcpu_gicr_attr(&self, cpu: usize) -> u64 { + let clustersz = 16; + + let aff1 = (cpu / clustersz) as u64; + let aff0 = (cpu % clustersz) as u64; + + let affid = (aff1 << 8) | aff0; + let cpu_affid: u64 = ((affid & 0xFF_0000_0000) >> 8) | (affid & 0xFF_FFFF); + + let last = u64::from((self.vcpu_count - 1) == cpu as u64); + + ((cpu_affid << 32) | (1 << 24) | (1 << 8) | (last << 4)) + & kvm_bindings::KVM_DEV_ARM_VGIC_V3_MPIDR_MASK as u64 + } + + fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + offset, + gicd_value as *mut u32 as u64, + write, + ) + .with_context(|| format!("Failed to access gic distributor for offset 0x{:x}", offset)) + } + + fn access_gic_redistributor( + &self, + offset: u64, + cpu: usize, + gicr_value: &mut u32, + write: bool, + ) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, + self.vcpu_gicr_attr(cpu) | offset, + gicr_value as *mut u32 as u64, + write, + ) + .with_context(|| { + format!( + "Failed to access gic redistributor for offset 0x{:x}", + offset + ) + }) + } + + fn access_gic_cpu( + &self, + offset: u64, + cpu: usize, + gicc_value: &mut u64, + write: bool, + ) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, + self.vcpu_gicr_attr(cpu) | offset, + gicc_value as *mut u64 as u64, + write, + ) + .with_context(|| format!("Failed to access gic cpu for offset 0x{:x}", offset)) + } + + fn access_gic_line_level(&self, offset: u64, gicll_value: &mut u32, write: bool) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, + self.vcpu_gicr_attr(0) | offset, + gicll_value as *mut u32 as u64, + write, + ) + } + + fn pause(&self) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, + 0, + true, + ) + } +} + +pub struct KvmGICv3Its { + fd: DeviceFd, +} + +impl KvmGICv3Its { + pub fn new(vm_fd: Arc) -> Result { + let mut its_device = kvm_bindings::kvm_create_device { + type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS, + fd: 0, + flags: 0, + }; + + let its_fd = match vm_fd.create_device(&mut its_device) { + Ok(fd) => fd, + Err(e) => return Err(anyhow!(HypervisorError::CreateKvmDevice(e))), + }; + + Ok(Self { fd: its_fd }) + } +} + +impl GICv3ItsAccess for KvmGICv3Its { + fn init_gic_its(&self, msi_base: u64) -> Result<()> { + KvmDevice::kvm_device_check( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), + ) + .with_context(|| "ITS address attribute is not supported for KVM")?; + + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), + &msi_base as *const u64 as u64, + true, + ) + .with_context(|| "Failed to set ITS attribute: ITS address")?; + + // Finalize the GIC Its. + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + &msi_base as *const u64 as u64, + true, + ) + .with_context(|| "KVM failed to initialize ITS")?; + + Ok(()) + } + + fn access_gic_its(&self, attr: u32, its_value: &mut u64, write: bool) -> Result<()> { + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + attr as u64, + its_value as *const u64 as u64, + write, + ) + } + + fn access_gic_its_tables(&self, save: bool) -> Result<()> { + let attr = if save { + kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES as u64 + } else { + kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES as u64 + }; + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + attr, + std::ptr::null::() as u64, + true, + ) + } + + fn reset(&self) -> Result<()> { + info!("Reset gicv3 its"); + KvmDevice::kvm_device_access( + &self.fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_ITS_CTRL_RESET), + std::ptr::null::() as u64, + true, + ) + .with_context(|| "Failed to reset ITS") + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use crate::kvm::aarch64::gicv3::{KvmGICv3, KvmGICv3Its}; + use crate::kvm::KvmHypervisor; + use devices::{GICDevice, GICVersion, GICv3, ICGICConfig, ICGICv3Config, GIC_IRQ_MAX}; + + #[test] + fn test_create_kvm_gicv3() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + assert!(KvmGICv3::new(kvm_hyp.vm_fd.clone().unwrap(), 4).is_ok()); + } + + #[test] + fn test_create_kvm_gicv3its() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + assert!(KvmGICv3Its::new(kvm_hyp.vm_fd.clone().unwrap()).is_ok()); + } + + #[test] + fn test_realize_gic_device_without_its() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let gic_config = ICGICConfig { + version: Some(GICVersion::GICv3), + vcpu_count: 4_u64, + max_irq: GIC_IRQ_MAX, + v2: None, + v3: Some(ICGICv3Config { + msi: false, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000)], + its_range: None, + }), + }; + + let hypervisor_gic = + KvmGICv3::new(kvm_hyp.vm_fd.clone().unwrap(), gic_config.vcpu_count).unwrap(); + let its_handler = KvmGICv3Its::new(kvm_hyp.vm_fd.clone().unwrap()).unwrap(); + let gic = GICv3::new(Arc::new(hypervisor_gic), Arc::new(its_handler), &gic_config).unwrap(); + assert!(gic.realize().is_ok()); + assert!(gic.its_dev.is_none()); + } + + #[test] + fn test_gic_redist_regions() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let gic_config = ICGICConfig { + version: Some(GICVersion::GICv3), + vcpu_count: 210_u64, + max_irq: GIC_IRQ_MAX, + v2: None, + v3: Some(ICGICv3Config { + msi: true, + dist_range: (0x0800_0000, 0x0001_0000), + redist_region_ranges: vec![(0x080A_0000, 0x00F6_0000), (256 << 30, 0x200_0000)], + its_range: Some((0x0808_0000, 0x0002_0000)), + }), + }; + + let hypervisor_gic = + KvmGICv3::new(kvm_hyp.vm_fd.clone().unwrap(), gic_config.vcpu_count).unwrap(); + let its_handler = KvmGICv3Its::new(kvm_hyp.vm_fd.clone().unwrap()).unwrap(); + let gic = GICv3::new(Arc::new(hypervisor_gic), Arc::new(its_handler), &gic_config).unwrap(); + assert!(gic.realize().is_ok()); + assert!(gic.its_dev.is_some()); + assert_eq!(gic.redist_regions.len(), 2); + } +} diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor_refactor/src/kvm/aarch64/mod.rs index 92c0c1329..aea936cd1 100644 --- a/hypervisor_refactor/src/kvm/aarch64/mod.rs +++ b/hypervisor_refactor/src/kvm/aarch64/mod.rs @@ -10,10 +10,60 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::Result; +pub mod gicv2; +pub mod gicv3; + +use anyhow::{Context, Result}; +use kvm_bindings::*; +use kvm_ioctls::DeviceFd; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; use crate::kvm::KvmHypervisor; +ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); + +/// A wrapper for kvm_based device check and access. +pub struct KvmDevice; +impl KvmDevice { + fn kvm_device_check(fd: &DeviceFd, group: u32, attr: u64) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr: 0, + flags: 0, + }; + fd.has_device_attr(&attr) + .with_context(|| "Failed to check device attributes.")?; + Ok(()) + } + + fn kvm_device_access( + fd: &DeviceFd, + group: u32, + attr: u64, + addr: u64, + write: bool, + ) -> Result<()> { + let attr = kvm_bindings::kvm_device_attr { + group, + attr, + addr, + flags: 0, + }; + + if write { + fd.set_device_attr(&attr) + .with_context(|| "Failed to set device attributes.")?; + } else { + let mut attr = attr; + fd.get_device_attr(&mut attr) + .with_context(|| "Failed to get device attributes.")?; + }; + + Ok(()) + } +} + impl KvmHypervisor { pub fn arch_init(&self) -> Result<()> { Ok(()) diff --git a/hypervisor_refactor/src/kvm/interrupt.rs b/hypervisor_refactor/src/kvm/interrupt.rs new file mode 100644 index 000000000..fe01e26ca --- /dev/null +++ b/hypervisor_refactor/src/kvm/interrupt.rs @@ -0,0 +1,191 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem::{align_of, size_of}; +use std::sync::Arc; + +use anyhow::{bail, Context, Result}; +use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP}; +use kvm_ioctls::{Cap, Kvm, VmFd}; +use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; + +use util::bitmap::Bitmap; + +pub(crate) type IrqRoute = kvm_bindings::kvm_irq_routing; +pub(crate) type IrqRouteEntry = kvm_bindings::kvm_irq_routing_entry; +type IrqRouteChip = kvm_bindings::kvm_irq_routing_irqchip; +type IrqChip = kvm_bindings::kvm_irq_routing_entry__bindgen_ty_1; + +ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03); + +#[cfg(target_arch = "x86_64")] +const IOAPIC_NUM_PINS: u32 = 24; +#[cfg(target_arch = "x86_64")] +const PIC_MASTER_PINS: u32 = 8; +#[cfg(target_arch = "x86_64")] +const PIC_SLACE_PINS: u32 = 8; +#[cfg(target_arch = "aarch64")] +const IOCHIP_NUM_PINS: u32 = 192; +#[cfg(target_arch = "aarch64")] +const KVM_IRQCHIP: u32 = 0; + +/// Return the max number kvm supports. +fn get_maximum_gsi_cnt(kvmfd: &Kvm) -> u32 { + let mut gsi_count = kvmfd.check_extension_int(Cap::IrqRouting); + if gsi_count < 0 { + gsi_count = 0; + } + + gsi_count as u32 +} + +/// Return `IrqRouteEntry` according to gsi, irqchip kind and pin. +fn create_irq_route_entry(gsi: u32, irqchip: u32, pin: u32) -> IrqRouteEntry { + let irq_route_chip = IrqRouteChip { irqchip, pin }; + let irq_chip = IrqChip { + irqchip: irq_route_chip, + }; + IrqRouteEntry { + gsi, + type_: KVM_IRQ_ROUTING_IRQCHIP, + flags: 0, + pad: 0, + u: irq_chip, + } +} + +/// Offer the irq gsi table for a current kvm vm instance. +pub struct IrqRouteTable { + pub irq_routes: Vec, + gsi_bitmap: Bitmap, +} + +impl Default for IrqRouteTable { + fn default() -> Self { + IrqRouteTable { + irq_routes: Vec::::new(), + gsi_bitmap: Bitmap::::new(0), + } + } +} + +impl IrqRouteTable { + /// Allocate a new irq route table. + pub fn new(kvmfd: &Kvm) -> Self { + let gsi_count = get_maximum_gsi_cnt(kvmfd); + + IrqRouteTable { + irq_routes: Vec::::new(), + gsi_bitmap: Bitmap::::new(gsi_count as usize), + } + } + + /// Init irq route table in arch x86_64. + #[cfg(target_arch = "x86_64")] + pub fn init_irq_route_table(&mut self) { + // On x86, use `kvm_create_irqchip` to create an interrupt + // controller module in the kernel. It creates a virtual PIC, a virtual ioapic, + // and sets up future vcpus to have a local APIC. IRQ routing for GSIs 0-15 is set + // to both PIC and IOAPIC. GSI 16-23 only go to the IOAPIC. + for i in 0..IOAPIC_NUM_PINS { + if i < PIC_MASTER_PINS { + self.irq_routes.push(create_irq_route_entry( + i, + kvm_bindings::KVM_IRQCHIP_PIC_MASTER, + i, + )); + } else if i < PIC_MASTER_PINS + PIC_SLACE_PINS { + self.irq_routes.push(create_irq_route_entry( + i, + kvm_bindings::KVM_IRQCHIP_PIC_SLAVE, + i - PIC_MASTER_PINS, + )); + } + self.irq_routes.push(create_irq_route_entry( + i, + kvm_bindings::KVM_IRQCHIP_IOAPIC, + i, + )); + // This unwrap() will never fail, it is safe. + self.gsi_bitmap.set(i as usize).unwrap(); + } + } + + /// Init irq route table in arch aarch64. + #[cfg(target_arch = "aarch64")] + pub fn init_irq_route_table(&mut self) { + for i in 0..IOCHIP_NUM_PINS { + self.irq_routes + .push(create_irq_route_entry(i, KVM_IRQCHIP, i)); + // This unwrap() will never fail, it is safe. + self.gsi_bitmap.set(i as usize).unwrap(); + } + } + + /// Get `IrqRouteEntry` by given gsi number. + /// A gsi number may have several entries. If no gsi number in table, is will + /// return an empty vector. + pub fn get_irq_route_entry(&self, gsi: u32) -> Vec { + let mut entries = Vec::new(); + for entry in self.irq_routes.iter() { + if gsi == entry.gsi { + entries.push(*entry); + } + } + + entries + } + + /// Sets the gsi routing table entries. It will overwrite previously set entries. + pub fn commit_irq_routing(&self, vm_fd: &Arc) -> Result<()> { + let routes = self.irq_routes.clone(); + + let layout = std::alloc::Layout::from_size_align( + size_of::() + routes.len() * size_of::(), + std::cmp::max(align_of::(), align_of::()), + )?; + + // SAFETY: data in `routes` is reliable. + unsafe { + let irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; + if irq_routing.is_null() { + bail!("Failed to alloc irq routing"); + } + (*irq_routing).nr = routes.len() as u32; + (*irq_routing).flags = 0; + let entries: &mut [IrqRouteEntry] = (*irq_routing).entries.as_mut_slice(routes.len()); + entries.copy_from_slice(&routes); + + let ret = vm_fd + .set_gsi_routing(&*irq_routing) + .with_context(|| "Failed to set gsi routing"); + + std::alloc::dealloc(irq_routing as *mut u8, layout); + ret + } + } +} + +#[cfg(test)] +mod tests { + use super::get_maximum_gsi_cnt; + use crate::kvm::KvmHypervisor; + + #[test] + fn test_get_maximum_gsi_cnt() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + assert!(get_maximum_gsi_cnt(kvm_hyp.fd.as_ref().unwrap()) > 0); + } +} diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index b8fc95dfa..0a23360af 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -12,12 +12,17 @@ #[cfg(target_arch = "aarch64")] pub mod aarch64; - #[cfg(target_arch = "x86_64")] pub mod x86_64; +mod interrupt; mod listener; +#[cfg(target_arch = "aarch64")] +pub use aarch64::gicv2::KvmGICv2; +#[cfg(target_arch = "aarch64")] +pub use aarch64::gicv3::{KvmGICv3, KvmGICv3Its}; + use std::collections::HashMap; use std::sync::{Arc, Mutex}; @@ -25,19 +30,34 @@ use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; -use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use self::listener::KvmMemoryListener; use super::HypervisorOps; +#[cfg(target_arch = "x86_64")] +use crate::HypervisorError; use address_space::{AddressSpace, Listener}; +#[cfg(target_arch = "aarch64")] +use devices::{ + GICVersion, GICv2, GICv3, GICv3ItsState, GICv3State, ICGICConfig, InterruptController, +}; +use interrupt::IrqRouteTable; +use machine_manager::config::{MachineType, VmConfig}; +#[cfg(target_arch = "aarch64")] +use migration::{ + snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, + MigrationManager, +}; use migration::{MigrateMemSlot, MigrateOps}; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h +pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; pub const KVM_SET_USER_MEMORY_REGION: u32 = 0x4020_ae46; pub const KVM_IOEVENTFD: u32 = 0x4040_ae79; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); +ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] @@ -45,6 +65,9 @@ pub struct KvmHypervisor { pub fd: Option, pub vm_fd: Option>, pub mem_slots: Arc>>, + #[cfg(target_arch = "aarch64")] + pub irq_chip: Option>, + pub irq_route_table: Option>, } impl KvmHypervisor { @@ -61,10 +84,14 @@ impl KvmHypervisor { } })) }; + let irq_route_table = Mutex::new(IrqRouteTable::new(&kvm_fd)); Ok(KvmHypervisor { fd: Some(kvm_fd), vm_fd, mem_slots: Arc::new(Mutex::new(HashMap::new())), + #[cfg(target_arch = "aarch64")] + irq_chip: None, + irq_route_table: Some(irq_route_table), }) } Err(e) => { @@ -80,6 +107,13 @@ impl KvmHypervisor { self.mem_slots.clone(), ))) } + + fn init_irq_route_table(&self) -> Result<()> { + let irq_route_table = self.irq_route_table.as_ref().unwrap(); + let mut locked_irq_route_table = irq_route_table.lock().unwrap(); + locked_irq_route_table.init_irq_route_table(); + locked_irq_route_table.commit_irq_routing(self.vm_fd.as_ref().unwrap()) + } } impl HypervisorOps for KvmHypervisor { @@ -100,6 +134,74 @@ impl HypervisorOps for KvmHypervisor { Ok(()) } + + #[cfg(target_arch = "aarch64")] + fn create_interrupt_controller( + &mut self, + gic_conf: &ICGICConfig, + vm_config: &VmConfig, + ) -> Result> { + gic_conf.check_sanity()?; + + let create_gicv3 = || { + let hypervisor_gic = KvmGICv3::new(self.vm_fd.clone().unwrap(), gic_conf.vcpu_count)?; + let its_handler = KvmGICv3Its::new(self.vm_fd.clone().unwrap())?; + let gicv3 = Arc::new(GICv3::new( + Arc::new(hypervisor_gic), + Arc::new(its_handler), + gic_conf, + )?); + if let Some(its_dev) = gicv3.its_dev.clone() { + MigrationManager::register_gic_instance( + GICv3ItsState::descriptor(), + its_dev, + GICV3_ITS_SNAPSHOT_ID, + ); + } + + MigrationManager::register_gic_instance( + GICv3State::descriptor(), + gicv3.clone(), + GICV3_SNAPSHOT_ID, + ); + + Ok(Arc::new(InterruptController::new(gicv3))) + }; + + let create_gicv2 = || { + let hypervisor_gic = KvmGICv2::new(self.vm_fd.clone().unwrap())?; + let gicv2 = Arc::new(GICv2::new(Arc::new(hypervisor_gic), gic_conf)?); + Ok(Arc::new(InterruptController::new(gicv2))) + }; + + let interrupt_controller = match &gic_conf.version { + Some(GICVersion::GICv3) => create_gicv3(), + Some(GICVersion::GICv2) => create_gicv2(), + // Try v3 by default if no version specified. + None => create_gicv3().or_else(|_| create_gicv2()), + }; + + if vm_config.machine_config.mach_type == MachineType::StandardVm { + self.init_irq_route_table()?; + } + + interrupt_controller + } + + #[cfg(target_arch = "x86_64")] + fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()> { + self.vm_fd + .as_ref() + .unwrap() + .create_irq_chip() + .with_context(|| HypervisorError::CrtIrqchipErr)?; + + if vm_config.machine_config.mach_type == MachineType::StandardVm { + self.init_irq_route_table()?; + } + + Ok(()) + } } impl MigrateOps for KvmHypervisor { diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index ba66bc737..cc8a78d6e 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -15,14 +15,17 @@ pub mod error; pub mod kvm; +pub use error::HypervisorError; + use std::any::Any; use std::sync::Arc; -pub use error::HypervisorError; - use anyhow::Result; use address_space::AddressSpace; +#[cfg(target_arch = "aarch64")] +use devices::{ICGICConfig, InterruptController}; +use machine_manager::config::VmConfig; use machine_manager::machine::HypervisorType; pub trait HypervisorOps: Send + Sync + Any { @@ -35,4 +38,14 @@ pub trait HypervisorOps: Send + Sync + Any { #[cfg(target_arch = "x86_64")] sys_io: &Arc, sys_mem: &Arc, ) -> Result<()>; + + #[cfg(target_arch = "aarch64")] + fn create_interrupt_controller( + &mut self, + gic_conf: &ICGICConfig, + vm_config: &VmConfig, + ) -> Result>; + + #[cfg(target_arch = "x86_64")] + fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()>; } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 51d8f8a47..001c83c32 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -18,10 +18,9 @@ use crate::{micro_common::syscall::syscall_whitelist, MachineBase, MachineError} use crate::{LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; -use devices::{ - legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, GIC_IRQ_MAX, -}; +use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; use hypervisor::kvm::*; +use hypervisor_refactor::kvm::aarch64::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::{ @@ -78,7 +77,7 @@ impl MachineOps for LightMachine { .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } - fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { + fn init_interrupt_controller(&mut self, vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], @@ -102,8 +101,11 @@ impl MachineOps for LightMachine { v3: Some(v3), v2: Some(v2), }; - let irq_chip = InterruptController::new(&intc_conf)?; - self.base.irq_chip = Some(Arc::new(irq_chip)); + + let hypervisor = self.get_hypervisor(); + let mut locked_hypervisor = hypervisor.lock().unwrap(); + self.base.irq_chip = + Some(locked_hypervisor.create_interrupt_controller(&intc_conf, vm_config)?); self.base.irq_chip.as_ref().unwrap().realize() } @@ -179,7 +181,8 @@ impl MachineOps for LightMachine { &cpu_config, )?); - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + locked_vm + .init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus), vm_config)?; locked_vm.cpu_post_init(&cpu_config)?; diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 9abb09a0b..ff1334006 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -49,8 +49,9 @@ use devices::legacy::{ }; use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; use devices::sysbus::SysBusDevType; -use devices::{ICGICConfig, ICGICv3Config, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; +use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; use hypervisor::kvm::*; +use hypervisor_refactor::kvm::aarch64::*; #[cfg(feature = "ramfb")] use machine_manager::config::parse_ramfb; use machine_manager::config::ShutdownAction; @@ -427,7 +428,7 @@ impl MachineOps for StdMachine { .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } - fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { + fn init_interrupt_controller(&mut self, vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], @@ -444,16 +445,12 @@ impl MachineOps for StdMachine { v2: None, v3: Some(v3), }; - let irq_chip = InterruptController::new(&intc_conf)?; - self.base.irq_chip = Some(Arc::new(irq_chip)); + + let hypervisor = self.get_hypervisor(); + let mut locked_hypervisor = hypervisor.lock().unwrap(); + self.base.irq_chip = + Some(locked_hypervisor.create_interrupt_controller(&intc_conf, vm_config)?); self.base.irq_chip.as_ref().unwrap().realize()?; - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .init_irq_route_table(); - KVM_FDS.load().commit_irq_routing()?; let root_bus = &self.pci_host.lock().unwrap().root_bus; let irq_handler = Box::new(move |gsi: u32, level: bool| -> Result<()> { @@ -586,7 +583,7 @@ impl MachineOps for StdMachine { )?); // Interrupt Controller Chip init - locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus), vm_config)?; locked_vm.cpu_post_init(&cpu_config)?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 639516974..26484ea04 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -512,7 +512,7 @@ pub trait MachineOps { /// # Arguments /// /// * `vcpu_count` - The number of vcpu. - fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()>; + fn init_interrupt_controller(&mut self, vcpu_count: u64, vm_config: &VmConfig) -> Result<()>; /// Add RTC device. fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] _mem_size: u64) -> Result<()> { diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index 027bd2a76..be0a42867 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -15,6 +15,7 @@ use crate::aarch64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] use crate::x86_64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; use hypervisor::kvm::*; +use hypervisor_refactor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; use virtio::VhostKern::*; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 233a24762..54b8f8fe4 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -21,7 +21,6 @@ use crate::{ use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; use devices::legacy::FwCfgOps; -use hypervisor::kvm::KVM_FDS; use hypervisor::kvm::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; @@ -84,14 +83,10 @@ impl MachineOps for LightMachine { Ok(()) } - fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_irq_chip() - .with_context(|| MachineError::CrtIrqchipErr) + fn init_interrupt_controller(&mut self, _vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { + let hypervisor = self.get_hypervisor(); + let mut locked_hypervisor = hypervisor.lock().unwrap(); + locked_hypervisor.create_interrupt_controller(vm_config) } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { @@ -168,7 +163,8 @@ impl MachineOps for LightMachine { let migrate_info = locked_vm.get_migrate_info(); - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; + locked_vm + .init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus), vm_config)?; // Add mmio devices locked_vm diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 44339ec2c..729a3b210 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -430,21 +430,10 @@ impl MachineOps for StdMachine { Ok(()) } - fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_irq_chip() - .with_context(|| MachineError::CrtIrqchipErr)?; - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .init_irq_route_table(); - KVM_FDS.load().commit_irq_routing() + fn init_interrupt_controller(&mut self, _vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { + let hypervisor = self.get_hypervisor(); + let mut locked_hypervisor = hypervisor.lock().unwrap(); + locked_hypervisor.create_interrupt_controller(vm_config) } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { @@ -534,7 +523,7 @@ impl MachineOps for StdMachine { nr_cpus, )?; - locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus), vm_config)?; locked_vm .init_pci_host() -- Gitee From 4bf150d2066c6bd153c073cd36a379540074ebe6 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 20:41:47 +0800 Subject: [PATCH 1568/2187] CPU: Introduce CPU Hypervisor Abstraction Layer Introduces a new CPU Hypervisor Abstraction Layer to decouple CPU management from the KVM hypervisor. Mainly Implement checking extention interface and getting hypervisor type interfaces currently. Signed-off-by: Jinhao Gao --- Cargo.lock | 1 + cpu/src/aarch64/caps.rs | 21 ++++++------ cpu/src/lib.rs | 36 +++++++++++++++++--- cpu/src/x86_64/caps.rs | 16 +++++---- hypervisor_refactor/Cargo.toml | 1 + hypervisor_refactor/src/kvm/aarch64/mod.rs | 8 ++++- hypervisor_refactor/src/kvm/mod.rs | 38 ++++++++++++++++++++-- hypervisor_refactor/src/kvm/x86_64/mod.rs | 10 +++++- hypervisor_refactor/src/lib.rs | 4 +++ machine/src/aarch64/micro.rs | 2 ++ machine/src/aarch64/standard.rs | 2 ++ machine/src/lib.rs | 11 ++++++- machine/src/x86_64/micro.rs | 2 ++ machine/src/x86_64/standard.rs | 5 +++ 14 files changed, 130 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02caa39e3..df3283b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,6 +741,7 @@ version = "2.3.0" dependencies = [ "address_space", "anyhow", + "cpu", "devices", "kvm-bindings", "kvm-ioctls", diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index 14a92e811..a90311c41 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -10,13 +10,16 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::Arc; + use kvm_bindings::{ KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64, }; -use kvm_ioctls::{Cap, Kvm, VcpuFd}; +use kvm_ioctls::{Cap, VcpuFd}; use super::core_regs::Result; +use crate::CPUHypervisorOps; use machine_manager::config::{CpuConfig, PmuConfig}; // Capabilities for ARM cpu. @@ -32,16 +35,14 @@ pub struct ArmCPUCaps { impl ArmCPUCaps { /// Initialize ArmCPUCaps instance. - pub fn init_capabilities() -> Self { - let kvm = Kvm::new().unwrap(); - + pub fn init_capabilities(hypervisor_cpu: Arc) -> Self { ArmCPUCaps { - irq_chip: kvm.check_extension(Cap::Irqchip), - ioevent_fd: kvm.check_extension(Cap::Ioeventfd), - irq_fd: kvm.check_extension(Cap::Irqfd), - user_mem: kvm.check_extension(Cap::UserMemory), - psci02: kvm.check_extension(Cap::ArmPsci02), - mp_state: kvm.check_extension(Cap::MpState), + irq_chip: hypervisor_cpu.check_extension(Cap::Irqchip), + ioevent_fd: hypervisor_cpu.check_extension(Cap::Ioeventfd), + irq_fd: hypervisor_cpu.check_extension(Cap::Irqfd), + user_mem: hypervisor_cpu.check_extension(Cap::UserMemory), + psci02: hypervisor_cpu.check_extension(Cap::ArmPsci02), + mp_state: hypervisor_cpu.check_extension(Cap::MpState), } } } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 40218d878..960cb5471 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -50,6 +50,7 @@ pub use aarch64::PMU_INTR; #[cfg(target_arch = "aarch64")] pub use aarch64::PPI_BASE; pub use error::CpuError; + #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] @@ -64,7 +65,7 @@ use std::thread; use std::time::Duration; use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::{VcpuExit, VcpuFd}; +use kvm_ioctls::{Cap, VcpuExit, VcpuFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info, warn}; use nix::unistd::gettid; @@ -72,7 +73,7 @@ use vmm_sys_util::signal::{register_signal_handler, Killable}; use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; -use machine_manager::machine::MachineInterface; +use machine_manager::machine::{HypervisorType, MachineInterface}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; #[cfg(not(test))] use util::test_helper::is_test_enabled; @@ -165,6 +166,14 @@ pub trait CPUInterface { fn kvm_vcpu_exec(&self) -> Result; } +pub trait CPUHypervisorOps: Send + Sync { + fn get_hypervisor_type(&self) -> HypervisorType; + + fn check_extension(&self, cap: Cap) -> bool; + + fn get_msr_index_list(&self) -> Vec; +} + /// `CPU` is a wrapper around creating and using a kvm-based VCPU. #[allow(clippy::upper_case_acronyms)] pub struct CPU { @@ -188,6 +197,8 @@ pub struct CPU { boot_state: Arc>, /// Sync the pause state of vCPU in kvm and userspace. pause_signal: Arc, + /// Interact between the vCPU and hypervisor. + pub hypervisor_cpu: Arc, } impl CPU { @@ -200,6 +211,7 @@ impl CPU { /// * `arch_cpu` - Architecture special `CPU` property. /// * `vm` - The virtual machine this `CPU` gets attached to. pub fn new( + hypervisor_cpu: Arc, vcpu_fd: Arc, id: u8, arch_cpu: Arc>, @@ -213,9 +225,10 @@ impl CPU { task: Arc::new(Mutex::new(None)), tid: Arc::new(Mutex::new(None)), vm: Arc::downgrade(&vm), - caps: CPUCaps::init_capabilities(), + caps: CPUCaps::init_capabilities(hypervisor_cpu.clone()), boot_state: Arc::new(Mutex::new(ArchCPU::default())), pause_signal: Arc::new(AtomicBool::new(false)), + hypervisor_cpu, } } @@ -259,6 +272,11 @@ impl CPU { fn set_tid(&self) { *self.tid.lock().unwrap() = Some(gettid().as_raw() as u64); } + + /// Get the hypervisor of this `CPU`. + pub fn hypervisor_cpu(&self) -> &Arc { + &self.hypervisor_cpu + } } impl CPUInterface for CPU { @@ -333,9 +351,11 @@ impl CPUInterface for CPU { } let local_cpu = cpu.clone(); + let hypervisor_cpu = cpu.hypervisor_cpu().clone(); + let hyp_type = hypervisor_cpu.get_hypervisor_type(); let cpu_thread_worker = CPUThreadWorker::new(cpu); let handle = thread::Builder::new() - .name(format!("CPU {}/KVM", local_cpu.id)) + .name(format!("CPU {}/{:?}", local_cpu.id, hyp_type)) .spawn(move || { if let Err(e) = cpu_thread_worker.handle(thread_barrier) { error!( @@ -344,7 +364,13 @@ impl CPUInterface for CPU { ); } }) - .with_context(|| format!("Failed to create thread for CPU {}/KVM", local_cpu.id()))?; + .with_context(|| { + format!( + "Failed to create thread for CPU {}/{:?}", + local_cpu.id(), + hyp_type + ) + })?; local_cpu.set_task(Some(handle)); Ok(()) } diff --git a/cpu/src/x86_64/caps.rs b/cpu/src/x86_64/caps.rs index a69c4f5e9..3b896c6fc 100644 --- a/cpu/src/x86_64/caps.rs +++ b/cpu/src/x86_64/caps.rs @@ -10,10 +10,14 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::Arc; + use kvm_bindings::{kvm_msr_entry, Msrs}; -use kvm_ioctls::{Cap, Kvm}; +use kvm_ioctls::Cap; use vmm_sys_util::fam::Error; +use crate::CPUHypervisorOps; + /// See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/msr-index.h#L558 const MSR_IA32_MISC_ENABLE: ::std::os::raw::c_uint = 0x1a0; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/msr-index.h#L597 @@ -35,13 +39,11 @@ pub struct X86CPUCaps { impl X86CPUCaps { /// Initialize X86CPUCaps instance. - pub fn init_capabilities() -> Self { - let kvm = Kvm::new().unwrap(); - + pub fn init_capabilities(hypervisor_cpu: Arc) -> Self { X86CPUCaps { - has_xsave: kvm.check_extension(Cap::Xsave), - has_xcrs: kvm.check_extension(Cap::Xcrs), - supported_msrs: kvm.get_msr_index_list().unwrap().as_slice().to_vec(), + has_xsave: hypervisor_cpu.check_extension(Cap::Xsave), + has_xcrs: hypervisor_cpu.check_extension(Cap::Xcrs), + supported_msrs: hypervisor_cpu.get_msr_index_list(), } } diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor_refactor/Cargo.toml index f2e19a633..4caf3147e 100644 --- a/hypervisor_refactor/Cargo.toml +++ b/hypervisor_refactor/Cargo.toml @@ -14,6 +14,7 @@ libc = "0.2" log = "0.4" vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } +cpu = { path = "../cpu" } devices = { path = "../devices" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor_refactor/src/kvm/aarch64/mod.rs index aea936cd1..c36845fe2 100644 --- a/hypervisor_refactor/src/kvm/aarch64/mod.rs +++ b/hypervisor_refactor/src/kvm/aarch64/mod.rs @@ -18,7 +18,7 @@ use kvm_bindings::*; use kvm_ioctls::DeviceFd; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; -use crate::kvm::KvmHypervisor; +use crate::kvm::{KvmCpu, KvmHypervisor}; ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); @@ -69,3 +69,9 @@ impl KvmHypervisor { Ok(()) } } + +impl KvmCpu { + pub fn arch_get_msr_index_list(&self) -> Vec { + Vec::new() + } +} diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index 0a23360af..03d9d1f52 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -29,7 +29,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; -use kvm_ioctls::{Kvm, VmFd}; +use kvm_ioctls::{Cap, Kvm, VmFd}; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use self::listener::KvmMemoryListener; @@ -37,12 +37,16 @@ use super::HypervisorOps; #[cfg(target_arch = "x86_64")] use crate::HypervisorError; use address_space::{AddressSpace, Listener}; +use cpu::CPUHypervisorOps; #[cfg(target_arch = "aarch64")] use devices::{ GICVersion, GICv2, GICv3, GICv3ItsState, GICv3State, ICGICConfig, InterruptController, }; use interrupt::IrqRouteTable; -use machine_manager::config::{MachineType, VmConfig}; +use machine_manager::{ + config::{MachineType, VmConfig}, + machine::HypervisorType, +}; #[cfg(target_arch = "aarch64")] use migration::{ snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, @@ -202,6 +206,13 @@ impl HypervisorOps for KvmHypervisor { Ok(()) } + + fn create_hypervisor_cpu( + &self, + _vcpu_id: u8, + ) -> Result> { + Ok(Arc::new(KvmCpu::new())) + } } impl MigrateOps for KvmHypervisor { @@ -278,3 +289,26 @@ impl MigrateOps for KvmHypervisor { Ok(()) } } + +pub struct KvmCpu {} + +impl KvmCpu { + pub fn new() -> Self { + Self {} + } +} + +impl CPUHypervisorOps for KvmCpu { + fn get_hypervisor_type(&self) -> HypervisorType { + HypervisorType::Kvm + } + + fn check_extension(&self, cap: Cap) -> bool { + let kvm = Kvm::new().unwrap(); + kvm.check_extension(cap) + } + + fn get_msr_index_list(&self) -> Vec { + self.arch_get_msr_index_list() + } +} diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor_refactor/src/kvm/x86_64/mod.rs index 415f6e145..db9e5e2d2 100644 --- a/hypervisor_refactor/src/kvm/x86_64/mod.rs +++ b/hypervisor_refactor/src/kvm/x86_64/mod.rs @@ -14,9 +14,10 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use kvm_bindings::*; +use kvm_ioctls::Kvm; use crate::kvm::listener::KvmIoListener; -use crate::kvm::KvmHypervisor; +use crate::kvm::{KvmCpu, KvmHypervisor}; use crate::HypervisorError; use address_space::Listener; @@ -48,3 +49,10 @@ impl KvmHypervisor { Arc::new(Mutex::new(KvmIoListener::new(self.vm_fd.clone()))) } } + +impl KvmCpu { + pub fn arch_get_msr_index_list(&self) -> Vec { + let kvm = Kvm::new().unwrap(); + kvm.get_msr_index_list().unwrap().as_slice().to_vec() + } +} diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index cc8a78d6e..6acd86072 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -23,6 +23,7 @@ use std::sync::Arc; use anyhow::Result; use address_space::AddressSpace; +use cpu::CPUHypervisorOps; #[cfg(target_arch = "aarch64")] use devices::{ICGICConfig, InterruptController}; use machine_manager::config::VmConfig; @@ -48,4 +49,7 @@ pub trait HypervisorOps: Send + Sync + Any { #[cfg(target_arch = "x86_64")] fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()>; + + fn create_hypervisor_cpu(&self, vcpu_id: u8) + -> Result>; } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 001c83c32..0b2a152e0 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -172,9 +172,11 @@ impl MachineOps for LightMachine { (None, None) }; + let hypervisor = locked_vm.base.hypervisor.clone(); // vCPUs init,and apply CPU features (for aarch64) locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), + hypervisor, vm_config.machine_config.nr_cpus, &topology, &boot_config, diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index ff1334006..6c5a3349e 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -574,8 +574,10 @@ impl MachineOps for StdMachine { None }; + let hypervisor = locked_vm.base.hypervisor.clone(); locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), + hypervisor, nr_cpus, &CPUTopology::new(), &boot_config, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 26484ea04..d55d5fa9a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -45,7 +45,7 @@ use vmm_sys_util::eventfd::EventFd; use address_space::{create_backend_mem, create_default_mem, AddressSpace, GuestAddress, Region}; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; -use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPUTopology, CpuTopology, CPU}; +use cpu::{ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(feature = "pvpanic")] use devices::misc::pvpanic::PvPanicPci; @@ -412,6 +412,7 @@ pub trait MachineOps { fn create_vcpu( vcpu_id: u8, vm: Arc>, + hypervisor: Arc>, #[cfg(target_arch = "x86_64")] max_cpus: u8, ) -> Result> where @@ -424,12 +425,18 @@ pub trait MachineOps { .unwrap() .create_vcpu(vcpu_id as u64) .with_context(|| "Create vcpu failed")?; + + let locked_hypervisor = hypervisor.lock().unwrap(); + let hypervisor_cpu: Arc = + locked_hypervisor.create_hypervisor_cpu(vcpu_id)?; + #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); #[cfg(target_arch = "x86_64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(max_cpus)); let cpu = Arc::new(CPU::new( + hypervisor_cpu, Arc::new(vcpu_fd), vcpu_id, Arc::new(Mutex::new(arch_cpu)), @@ -448,6 +455,7 @@ pub trait MachineOps { /// * `boot_cfg` - Boot message generated by reading boot source to guest memory. fn init_vcpu( vm: Arc>, + hypervisor: Arc>, nr_cpus: u8, #[cfg(target_arch = "x86_64")] max_cpus: u8, topology: &CPUTopology, @@ -463,6 +471,7 @@ pub trait MachineOps { let cpu = Self::create_vcpu( vcpu_id, vm.clone(), + hypervisor.clone(), #[cfg(target_arch = "x86_64")] max_cpus, )?; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 54b8f8fe4..b97ae25c1 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -179,8 +179,10 @@ impl MachineOps for LightMachine { None }; + let hypervisor = locked_vm.base.hypervisor.clone(); locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), + hypervisor, vm_config.machine_config.nr_cpus, vm_config.machine_config.max_cpus, &topology, diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 729a3b210..75ed21240 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -347,9 +347,11 @@ impl StdMachineOps for StdMachine { let boot_cfg = locked_controller.get_boot_config(); let topology = locked_controller.get_topology_config(); + let hypervisor = clone_vm.lock().unwrap().base.hypervisor.clone(); let vcpu = ::create_vcpu( vcpu_id, clone_vm, + hypervisor, self.base.cpu_topo.max_cpus, )?; vcpu.realize(boot_cfg, topology).with_context(|| { @@ -546,8 +548,11 @@ impl MachineOps for StdMachine { vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, )); + + let hypervisor = locked_vm.base.hypervisor.clone(); locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), + hypervisor, nr_cpus, max_cpus, &topology, -- Gitee From 1e5d6338ba0965ea6b2ac8a51dde2e9293d767ce Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 20:57:37 +0800 Subject: [PATCH 1569/2187] CPU: Use Kvm CPU register structure as a general CPU register Temporarily using KVM CPU register structure as a generic CPU registers. Future work includes abstracting and refactoring all CPU registers to be independent of specific hypervisors. Signed-off-by: Jinhao Gao --- cpu/src/aarch64/mod.rs | 29 ++++++++++---------- cpu/src/x86_64/mod.rs | 61 ++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 373b797a1..6ff50c61e 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -24,9 +24,10 @@ use std::{ use anyhow::{Context, Result}; use kvm_bindings::{ - kvm_device_attr, kvm_mp_state, kvm_regs, kvm_vcpu_events, kvm_vcpu_init, RegList, - KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, KVM_ARM_VCPU_PMU_V3_IRQ, - KVM_MP_STATE_RUNNABLE, KVM_MP_STATE_STOPPED, + kvm_device_attr, kvm_mp_state as MpState, kvm_regs as Regs, kvm_vcpu_events as VcpuEvents, + kvm_vcpu_init as VcpuInit, RegList, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, + KVM_ARM_VCPU_PMU_V3_IRQ, KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, + KVM_MP_STATE_STOPPED as MP_STATE_STOPPED, }; use kvm_ioctls::{DeviceFd, VcpuFd}; @@ -99,13 +100,13 @@ pub struct ArmCPUState { /// for scheduling purposes. mpidr: u64, /// Used to pass vcpu target and supported features to kvm. - kvi: kvm_vcpu_init, + kvi: VcpuInit, /// Vcpu core registers. - core_regs: kvm_regs, + core_regs: Regs, /// Vcpu cpu events register. - cpu_events: kvm_vcpu_events, + cpu_events: VcpuEvents, /// Vcpu mpstate register. - mp_state: kvm_mp_state, + mp_state: MpState, /// The length of Cpreg. cpreg_len: usize, /// The list of Cpreg. @@ -123,11 +124,11 @@ impl ArmCPUState { /// /// * `vcpu_id` - ID of this `CPU`. pub fn new(vcpu_id: u32) -> Self { - let mp_state = kvm_mp_state { + let mp_state = MpState { mp_state: if vcpu_id == 0 { - KVM_MP_STATE_RUNNABLE + MP_STATE_RUNNABLE } else { - KVM_MP_STATE_STOPPED + MP_STATE_STOPPED }, }; @@ -237,12 +238,12 @@ impl ArmCPUState { } /// Get core_regs value. - pub fn core_regs(&self) -> kvm_regs { + pub fn core_regs(&self) -> Regs { self.core_regs } /// Get kvm_vcpu_init. - pub fn kvi(&self) -> kvm_vcpu_init { + pub fn kvi(&self) -> VcpuInit { self.kvi } @@ -333,8 +334,8 @@ impl StateTransfer for CPU { cpu_state_locked.core_regs = get_core_regs(&self.fd)?; if self.caps.mp_state { let mut mp_state = self.fd.get_mp_state()?; - if mp_state.mp_state != KVM_MP_STATE_STOPPED { - mp_state.mp_state = KVM_MP_STATE_RUNNABLE; + if mp_state.mp_state != MP_STATE_STOPPED { + mp_state.mp_state = MP_STATE_RUNNABLE; } cpu_state_locked.mp_state = mp_state; } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index a08047d58..493fb60e4 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -18,10 +18,13 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use kvm_bindings::{ - kvm_cpuid_entry2, kvm_debugregs, kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, - kvm_regs, kvm_segment, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, CpuId, Msrs, - KVM_CPUID_FLAG_SIGNIFCANT_INDEX, KVM_MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE, - KVM_MP_STATE_UNINITIALIZED, + kvm_cpuid_entry2 as CpuidEntry2, kvm_debugregs as DebugRegs, kvm_fpu as Fpu, + kvm_lapic_state as LapicState, kvm_mp_state as MpState, kvm_msr_entry as MsrEntry, + kvm_regs as Regs, kvm_segment as Segment, kvm_sregs as Sregs, kvm_vcpu_events as VcpuEvents, + kvm_xcrs as Xcrs, kvm_xsave as Xsave, CpuId, Msrs, + KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_SIGNIFICANT_INDEX, + KVM_MAX_CPUID_ENTRIES as MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, + KVM_MP_STATE_UNINITIALIZED as MP_STATE_UNINITIALIZED, }; use kvm_ioctls::{Kvm, VcpuFd}; @@ -73,8 +76,8 @@ pub struct X86CPUBootConfig { /// zero page address, as the second parameter of __startup_64 /// arch/x86/kernel/head_64.S:86 pub zero_page: u64, - pub code_segment: kvm_segment, - pub data_segment: kvm_segment, + pub code_segment: Segment, + pub data_segment: Segment, pub gdt_base: u64, pub gdt_size: u16, pub idt_base: u64, @@ -115,17 +118,17 @@ pub struct X86CPUState { nr_dies: u32, nr_sockets: u32, apic_id: u32, - regs: kvm_regs, - sregs: kvm_sregs, - fpu: kvm_fpu, - mp_state: kvm_mp_state, - lapic: kvm_lapic_state, + regs: Regs, + sregs: Sregs, + fpu: Fpu, + mp_state: MpState, + lapic: LapicState, msr_len: usize, - msr_list: [kvm_msr_entry; 256], - cpu_events: kvm_vcpu_events, - xsave: kvm_xsave, - xcrs: kvm_xcrs, - debugregs: kvm_debugregs, + msr_list: [MsrEntry; 256], + cpu_events: VcpuEvents, + xsave: Xsave, + xcrs: Xcrs, + debugregs: DebugRegs, } impl X86CPUState { @@ -136,11 +139,11 @@ impl X86CPUState { /// * `vcpu_id` - ID of this `CPU`. /// * `max_vcpus` - Number of vcpus. pub fn new(vcpu_id: u32, max_vcpus: u32) -> Self { - let mp_state = kvm_mp_state { + let mp_state = MpState { mp_state: if vcpu_id == 0 { - KVM_MP_STATE_RUNNABLE + MP_STATE_RUNNABLE } else { - KVM_MP_STATE_UNINITIALIZED + MP_STATE_UNINITIALIZED }, }; X86CPUState { @@ -266,7 +269,7 @@ impl X86CPUState { .get_lapic() .with_context(|| format!("Failed to get lapic for CPU {}/KVM", self.apic_id))?; - // SAFETY: The member regs in struct kvm_lapic_state is a u8 array with 1024 entries, + // SAFETY: The member regs in struct LapicState is a u8 array with 1024 entries, // so it's saft to cast u8 pointer to u32 at position APIC_LVT0 and APIC_LVT1. // Safe because all value in this unsafe block is certain. unsafe { @@ -286,7 +289,7 @@ impl X86CPUState { } fn setup_regs(&mut self, boot_config: &X86CPUBootConfig) { - self.regs = kvm_regs { + self.regs = Regs { rflags: 0x0002, // Means processor has been initialized rip: boot_config.boot_ip, rsp: boot_config.boot_sp, @@ -368,7 +371,7 @@ impl X86CPUState { // arch/x86/include/asm/fpu/types.h const MXCSR_DEFAULT: u32 = 0x1f80; - self.fpu = kvm_fpu { + self.fpu = Fpu { fcw: 0x37f, mxcsr: MXCSR_DEFAULT, ..Default::default() @@ -384,7 +387,7 @@ impl X86CPUState { _ => 0u64, }; - self.msr_list[index] = kvm_msr_entry { + self.msr_list[index] = MsrEntry { index: *msr, data, ..Default::default() @@ -411,7 +414,7 @@ impl X86CPUState { } } for index in 0..4 { - let entry = kvm_cpuid_entry2 { + let entry = CpuidEntry2 { function: 0x1f, index, ..Default::default() @@ -430,7 +433,7 @@ impl X86CPUState { _ => bail!("setup_cpuid: Open /dev/kvm failed"), }; let mut cpuid = sys_fd - .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) + .get_supported_cpuid(CPUID_FLAG_SIGNIFICANT_INDEX) .with_context(|| { format!("Failed to get supported cpuid for CPU {}/KVM", self.apic_id) })?; @@ -517,7 +520,7 @@ impl X86CPUState { entry.edx = self.apic_id; entry.ecx = entry.index & 0xff; - entry.flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + entry.flags = CPUID_FLAG_SIGNIFICANT_INDEX; match entry.index { 0 => { @@ -612,7 +615,7 @@ impl MigrationHook for CPU {} mod test { use super::*; use hypervisor::kvm::{KVMFds, KVM_FDS}; - use kvm_bindings::kvm_segment; + use kvm_bindings::Segment; use std::sync::Arc; #[test] @@ -623,7 +626,7 @@ mod test { } KVM_FDS.store(Arc::new(kvm_fds)); - let code_seg = kvm_segment { + let code_seg = Segment { base: 0, limit: 1048575, selector: 16, @@ -638,7 +641,7 @@ mod test { unusable: 0, padding: 0, }; - let data_seg = kvm_segment { + let data_seg = Segment { base: 0, limit: 1048575, selector: 24, -- Gitee From 25e854396a7c9b16778107f54e890b17640ea831 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 21:20:14 +0800 Subject: [PATCH 1570/2187] Hypervisor: Move some ioctl definitions to hypervisor_refactor Move the ioctl definitions related to operating the CPU register status from the hypervisor directory to the hypervisor_refactor directory. Signed-off-by: Jinhao Gao --- hypervisor/src/kvm/mod.rs | 53 +--------------------- hypervisor_refactor/src/kvm/aarch64/mod.rs | 6 ++- hypervisor_refactor/src/kvm/mod.rs | 9 +++- hypervisor_refactor/src/kvm/x86_64/mod.rs | 20 ++++++++ machine/src/aarch64/micro.rs | 1 - machine/src/micro_common/syscall.rs | 1 - machine/src/x86_64/micro.rs | 2 + machine/src/x86_64/standard.rs | 2 + 8 files changed, 38 insertions(+), 56 deletions(-) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index c639b5e01..1fe70b851 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -23,9 +23,7 @@ use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; use log::error; use once_cell::sync::Lazy; -use vmm_sys_util::{ - eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, -}; +use vmm_sys_util::{eventfd::EventFd, ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use interrupt::{IrqRoute, IrqRouteEntry, IrqRouteTable}; @@ -35,56 +33,7 @@ pub const KVM_SIGNAL_MSI: u32 = 0x4020_aea5; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); -ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00); -ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); -ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events); -#[cfg(target_arch = "x86_64")] -ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); -#[cfg(target_arch = "x86_64")] -ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); -#[cfg(target_arch = "x86_64")] -ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2); -ioctl_ior_nr!(KVM_GET_CLOCK, KVMIO, 0x7c, kvm_clock_data); ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip); -ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); -ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); -#[cfg(target_arch = "x86_64")] -ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave); -ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu); -#[cfg(target_arch = "x86_64")] -ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs); -#[cfg(target_arch = "x86_64")] -ioctl_ior_nr!(KVM_GET_DEBUGREGS, KVMIO, 0xa1, kvm_debugregs); -#[cfg(target_arch = "x86_64")] -ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); -#[cfg(target_arch = "x86_64")] -ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); -#[cfg(target_arch = "aarch64")] -ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); -#[cfg(target_arch = "aarch64")] -ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); -#[cfg(target_arch = "aarch64")] -ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); -#[cfg(target_arch = "aarch64")] -ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); #[allow(clippy::upper_case_acronyms)] diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor_refactor/src/kvm/aarch64/mod.rs index c36845fe2..a71458adc 100644 --- a/hypervisor_refactor/src/kvm/aarch64/mod.rs +++ b/hypervisor_refactor/src/kvm/aarch64/mod.rs @@ -16,11 +16,15 @@ pub mod gicv3; use anyhow::{Context, Result}; use kvm_bindings::*; use kvm_ioctls::DeviceFd; -use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use crate::kvm::{KvmCpu, KvmHypervisor}; ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); +ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); +ioctl_iow_nr!(KVM_SET_ONE_REG, KVMIO, 0xac, kvm_one_reg); +ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list); +ioctl_iow_nr!(KVM_ARM_VCPU_INIT, KVMIO, 0xae, kvm_vcpu_init); /// A wrapper for kvm_based device check and access. pub struct KvmDevice; diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index 03d9d1f52..fb4826fb9 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -30,7 +30,7 @@ use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; use kvm_ioctls::{Cap, Kvm, VmFd}; -use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; +use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; use self::listener::KvmMemoryListener; use super::HypervisorOps; @@ -62,6 +62,13 @@ pub const KVM_IOEVENTFD: u32 = 0x4040_ae79; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device); +ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00); +ioctl_ior_nr!(KVM_GET_MP_STATE, KVMIO, 0x98, kvm_mp_state); +ioctl_ior_nr!(KVM_GET_VCPU_EVENTS, KVMIO, 0x9f, kvm_vcpu_events); +ioctl_ior_nr!(KVM_GET_CLOCK, KVMIO, 0x7c, kvm_clock_data); +ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); +ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); +ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor_refactor/src/kvm/x86_64/mod.rs index db9e5e2d2..b5ad027b1 100644 --- a/hypervisor_refactor/src/kvm/x86_64/mod.rs +++ b/hypervisor_refactor/src/kvm/x86_64/mod.rs @@ -15,12 +15,32 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use kvm_bindings::*; use kvm_ioctls::Kvm; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; use crate::kvm::listener::KvmIoListener; use crate::kvm::{KvmCpu, KvmHypervisor}; use crate::HypervisorError; use address_space::Listener; +// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h +ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); +ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); +ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); +ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); +ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); +ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave); +ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); +ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs); +ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); +ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); +ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); +ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2); +ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave); +ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs); +ioctl_ior_nr!(KVM_GET_DEBUGREGS, KVMIO, 0xa1, kvm_debugregs); +ioctl_ior_nr!(KVM_GET_LAPIC, KVMIO, 0x8e, kvm_lapic_state); +ioctl_iowr_nr!(KVM_GET_MSRS, KVMIO, 0x88, kvm_msrs); + impl KvmHypervisor { pub fn arch_init(&self) -> Result<()> { // The identity_addr is set in the memory layout of x86 machine. diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 0b2a152e0..bc5b153a0 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -19,7 +19,6 @@ use crate::{LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; -use hypervisor::kvm::*; use hypervisor_refactor::kvm::aarch64::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index be0a42867..75c85af43 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -14,7 +14,6 @@ use crate::aarch64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] use crate::x86_64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; -use hypervisor::kvm::*; use hypervisor_refactor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index b97ae25c1..ec5b586c7 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -22,6 +22,8 @@ use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; use devices::legacy::FwCfgOps; use hypervisor::kvm::*; +use hypervisor_refactor::kvm::x86_64::*; +use hypervisor_refactor::kvm::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::seccomp::{BpfRule, SeccompCmpOpt}; diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 75ed21240..dd989eb6a 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -41,6 +41,8 @@ use devices::legacy::{ }; use devices::pci::{PciDevOps, PciHost}; use hypervisor::kvm::*; +use hypervisor_refactor::kvm::x86_64::*; +use hypervisor_refactor::kvm::*; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ -- Gitee From 91581fd11765ffbd96b8c07f15f60a8a2fb2d9dd Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 21:36:44 +0800 Subject: [PATCH 1571/2187] CPU: Add the interface of initializing pmu for CPUHypervisorOps Move the CPU interface of initializing pmu to CPUHypervisorOps. Signed-off-by: Jinhao Gao --- cpu/src/aarch64/mod.rs | 53 +++------------------- cpu/src/lib.rs | 3 ++ hypervisor_refactor/src/kvm/aarch64/mod.rs | 38 ++++++++++++++++ hypervisor_refactor/src/kvm/mod.rs | 18 ++++++-- hypervisor_refactor/src/kvm/x86_64/mod.rs | 4 ++ hypervisor_refactor/src/lib.rs | 8 +++- machine/src/lib.rs | 22 +++++---- 7 files changed, 82 insertions(+), 64 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 6ff50c61e..937f77c14 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -16,20 +16,15 @@ mod regs; pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; -use std::{ - mem::forget, - os::unix::prelude::{AsRawFd, FromRawFd}, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use kvm_bindings::{ - kvm_device_attr, kvm_mp_state as MpState, kvm_regs as Regs, kvm_vcpu_events as VcpuEvents, - kvm_vcpu_init as VcpuInit, RegList, KVM_ARM_VCPU_PMU_V3_CTRL, KVM_ARM_VCPU_PMU_V3_INIT, - KVM_ARM_VCPU_PMU_V3_IRQ, KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, + kvm_mp_state as MpState, kvm_regs as Regs, kvm_vcpu_events as VcpuEvents, + kvm_vcpu_init as VcpuInit, RegList, KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, KVM_MP_STATE_STOPPED as MP_STATE_STOPPED, }; -use kvm_ioctls::{DeviceFd, VcpuFd}; +use kvm_ioctls::VcpuFd; use self::caps::CpregListEntry; use self::core_regs::{get_core_regs, set_core_regs}; @@ -290,43 +285,6 @@ impl ArmCPUState { } } -impl CPU { - /// Init PMU for ARM CPU - pub fn init_pmu(&self) -> Result<()> { - let pmu_attr = kvm_device_attr { - group: KVM_ARM_VCPU_PMU_V3_CTRL, - attr: KVM_ARM_VCPU_PMU_V3_INIT as u64, - addr: 0, - flags: 0, - }; - // SAFETY: The fd can be guaranteed to be legal during creation. - let vcpu_device = unsafe { DeviceFd::from_raw_fd(self.fd.as_raw_fd()) }; - vcpu_device - .has_device_attr(&pmu_attr) - .with_context(|| "Kernel does not support PMU for vCPU")?; - // Set IRQ 23, PPI 7 for PMU. - let irq = PMU_INTR + PPI_BASE; - let pmu_irq_attr = kvm_device_attr { - group: KVM_ARM_VCPU_PMU_V3_CTRL, - attr: KVM_ARM_VCPU_PMU_V3_IRQ as u64, - addr: &irq as *const u32 as u64, - flags: 0, - }; - - vcpu_device - .set_device_attr(&pmu_irq_attr) - .with_context(|| "Failed to set IRQ for PMU")?; - // Init PMU after setting IRQ. - vcpu_device - .set_device_attr(&pmu_attr) - .with_context(|| "Failed to enable PMU for vCPU")?; - // forget `vcpu_device` to avoid fd close on exit, as DeviceFd is backed by File. - forget(vcpu_device); - - Ok(()) - } -} - impl StateTransfer for CPU { fn get_state_vec(&self) -> migration::Result> { let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); @@ -369,7 +327,8 @@ impl StateTransfer for CPU { self.fd.vcpu_init(&cpu_state.kvi)?; if cpu_state.features.pmu { - self.init_pmu() + self.hypervisor_cpu + .init_pmu() .with_context(|| MigrationError::FromBytesError("Failed to init pmu."))?; } Ok(()) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 960cb5471..a3bb8e6da 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -172,6 +172,9 @@ pub trait CPUHypervisorOps: Send + Sync { fn check_extension(&self, cap: Cap) -> bool; fn get_msr_index_list(&self) -> Vec; + + #[cfg(target_arch = "aarch64")] + fn init_pmu(&self) -> Result<()>; } /// `CPU` is a wrapper around creating and using a kvm-based VCPU. diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor_refactor/src/kvm/aarch64/mod.rs index a71458adc..9a15b0654 100644 --- a/hypervisor_refactor/src/kvm/aarch64/mod.rs +++ b/hypervisor_refactor/src/kvm/aarch64/mod.rs @@ -13,12 +13,16 @@ pub mod gicv2; pub mod gicv3; +use std::mem::forget; +use std::os::unix::io::{AsRawFd, FromRawFd}; + use anyhow::{Context, Result}; use kvm_bindings::*; use kvm_ioctls::DeviceFd; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use crate::kvm::{KvmCpu, KvmHypervisor}; +use cpu::{PMU_INTR, PPI_BASE}; ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); @@ -78,4 +82,38 @@ impl KvmCpu { pub fn arch_get_msr_index_list(&self) -> Vec { Vec::new() } + + pub fn arch_init_pmu(&self) -> Result<()> { + let pmu_attr = kvm_device_attr { + group: KVM_ARM_VCPU_PMU_V3_CTRL, + attr: KVM_ARM_VCPU_PMU_V3_INIT as u64, + addr: 0, + flags: 0, + }; + // SAFETY: The fd can be guaranteed to be legal during creation. + let vcpu_device = unsafe { DeviceFd::from_raw_fd(self.fd.as_raw_fd()) }; + vcpu_device + .has_device_attr(&pmu_attr) + .with_context(|| "Kernel does not support PMU for vCPU")?; + // Set IRQ 23, PPI 7 for PMU. + let irq = PMU_INTR + PPI_BASE; + let pmu_irq_attr = kvm_device_attr { + group: KVM_ARM_VCPU_PMU_V3_CTRL, + attr: KVM_ARM_VCPU_PMU_V3_IRQ as u64, + addr: &irq as *const u32 as u64, + flags: 0, + }; + + vcpu_device + .set_device_attr(&pmu_irq_attr) + .with_context(|| "Failed to set IRQ for PMU")?; + // Init PMU after setting IRQ. + vcpu_device + .set_device_attr(&pmu_attr) + .with_context(|| "Failed to enable PMU for vCPU")?; + // forget `vcpu_device` to avoid fd close on exit, as DeviceFd is backed by File. + forget(vcpu_device); + + Ok(()) + } } diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index fb4826fb9..ad626e0a5 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -29,7 +29,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; -use kvm_ioctls::{Cap, Kvm, VmFd}; + +use kvm_ioctls::{Cap, Kvm, VcpuFd, VmFd}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; use self::listener::KvmMemoryListener; @@ -217,8 +218,9 @@ impl HypervisorOps for KvmHypervisor { fn create_hypervisor_cpu( &self, _vcpu_id: u8, + vcpu_fd: Arc, ) -> Result> { - Ok(Arc::new(KvmCpu::new())) + Ok(Arc::new(KvmCpu::new(vcpu_fd))) } } @@ -297,11 +299,13 @@ impl MigrateOps for KvmHypervisor { } } -pub struct KvmCpu {} +pub struct KvmCpu { + fd: Arc, +} impl KvmCpu { - pub fn new() -> Self { - Self {} + pub fn new(vcpu_fd: Arc) -> Self { + Self { fd: vcpu_fd } } } @@ -318,4 +322,8 @@ impl CPUHypervisorOps for KvmCpu { fn get_msr_index_list(&self) -> Vec { self.arch_get_msr_index_list() } + + fn init_pmu(&self) -> Result<()> { + self.arch_init_pmu() + } } diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor_refactor/src/kvm/x86_64/mod.rs index b5ad027b1..f7894c0d3 100644 --- a/hypervisor_refactor/src/kvm/x86_64/mod.rs +++ b/hypervisor_refactor/src/kvm/x86_64/mod.rs @@ -75,4 +75,8 @@ impl KvmCpu { let kvm = Kvm::new().unwrap(); kvm.get_msr_index_list().unwrap().as_slice().to_vec() } + + pub fn arch_init_pmu(&self) -> Result<()> { + Ok(()) + } } diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index 6acd86072..d94d31d62 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -16,6 +16,7 @@ pub mod error; pub mod kvm; pub use error::HypervisorError; +use kvm_ioctls::VcpuFd; use std::any::Any; use std::sync::Arc; @@ -50,6 +51,9 @@ pub trait HypervisorOps: Send + Sync + Any { #[cfg(target_arch = "x86_64")] fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()>; - fn create_hypervisor_cpu(&self, vcpu_id: u8) - -> Result>; + fn create_hypervisor_cpu( + &self, + vcpu_id: u8, + vcpu_fd: Arc, + ) -> Result>; } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d55d5fa9a..9bfc50575 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -418,17 +418,19 @@ pub trait MachineOps { where Self: Sized, { - let vcpu_fd = KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(vcpu_id as u64) - .with_context(|| "Create vcpu failed")?; + let vcpu_fd = Arc::new( + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_vcpu(vcpu_id as u64) + .with_context(|| "Create vcpu failed")?, + ); let locked_hypervisor = hypervisor.lock().unwrap(); let hypervisor_cpu: Arc = - locked_hypervisor.create_hypervisor_cpu(vcpu_id)?; + locked_hypervisor.create_hypervisor_cpu(vcpu_id, vcpu_fd.clone())?; #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); @@ -437,7 +439,7 @@ pub trait MachineOps { let cpu = Arc::new(CPU::new( hypervisor_cpu, - Arc::new(vcpu_fd), + vcpu_fd, vcpu_id, Arc::new(Mutex::new(arch_cpu)), vm.clone(), @@ -510,7 +512,7 @@ pub trait MachineOps { let features = vcpu_cfg.unwrap_or_default(); if features.pmu { for cpu in self.machine_base().cpus.iter() { - cpu.init_pmu()?; + cpu.hypervisor_cpu.init_pmu()?; } } Ok(()) -- Gitee From 474cae28359a5788d424fa76448fe48d254dd07f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 22 Jan 2024 22:26:35 +0800 Subject: [PATCH 1572/2187] CPU: Move the interface of operating CPU register to CPUhypervisorOps Original CPU register operation interface depended on vcpu_fd. Now, by abstracting the CPUHypervisorOps structure, the CPU register operation interface has been moved to CPUHypervisorOps, replacing the dependency on vcpu_fd. This achieves decoupling from KVM. Signed-off-by: Jinhao Gao --- cpu/src/aarch64/caps.rs | 57 +--- cpu/src/aarch64/core_regs.rs | 91 ------ cpu/src/aarch64/mod.rs | 186 +++--------- cpu/src/aarch64/regs.rs | 54 ---- cpu/src/lib.rs | 81 ++--- cpu/src/x86_64/mod.rs | 180 ++++------- hypervisor_refactor/src/kvm/aarch64/mod.rs | 334 ++++++++++++++++++++- hypervisor_refactor/src/kvm/mod.rs | 70 ++++- hypervisor_refactor/src/kvm/x86_64/mod.rs | 209 ++++++++++++- machine/src/aarch64/standard.rs | 4 +- 10 files changed, 748 insertions(+), 518 deletions(-) delete mode 100644 cpu/src/aarch64/regs.rs diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index a90311c41..2dc9dd362 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -12,13 +12,8 @@ use std::sync::Arc; -use kvm_bindings::{ - KVM_REG_ARM_COPROC_MASK, KVM_REG_ARM_CORE, KVM_REG_SIZE_MASK, KVM_REG_SIZE_U32, - KVM_REG_SIZE_U64, -}; -use kvm_ioctls::{Cap, VcpuFd}; +use kvm_ioctls::Cap; -use super::core_regs::Result; use crate::CPUHypervisorOps; use machine_manager::config::{CpuConfig, PmuConfig}; @@ -69,53 +64,3 @@ pub struct CpregListEntry { pub reg_id: u64, pub value: u128, } - -impl CpregListEntry { - fn cpreg_tuples_entry(&self) -> bool { - (self.reg_id & KVM_REG_ARM_COPROC_MASK as u64) == (KVM_REG_ARM_CORE as u64) - } - - fn normal_cpreg_entry(&self) -> bool { - if self.cpreg_tuples_entry() { - return false; - } - - ((self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) - || ((self.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) - } - - /// Validate cpreg_list's tuples entry and normal entry. - pub fn validate(&self) -> bool { - if self.cpreg_tuples_entry() { - return true; - } - - self.normal_cpreg_entry() - } - - /// Get Cpreg value from Kvm. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - pub fn get_cpreg(&mut self, vcpu_fd: &VcpuFd) -> Result<()> { - if self.normal_cpreg_entry() { - self.value = vcpu_fd.get_one_reg(self.reg_id)?; - } - - Ok(()) - } - - /// Set Cpreg value to Kvm. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - pub fn set_cpreg(&self, vcpu_fd: &VcpuFd) -> Result<()> { - if self.normal_cpreg_entry() { - vcpu_fd.set_one_reg(self.reg_id, self.value)?; - } - - Ok(()) - } -} diff --git a/cpu/src/aarch64/core_regs.rs b/cpu/src/aarch64/core_regs.rs index ee8a8ba29..6d49713f1 100644 --- a/cpu/src/aarch64/core_regs.rs +++ b/cpu/src/aarch64/core_regs.rs @@ -16,16 +16,9 @@ use kvm_bindings::{ kvm_regs, user_fpsimd_state, user_pt_regs, KVM_NR_SPSR, KVM_REG_ARM64, KVM_REG_ARM_CORE, KVM_REG_SIZE_U128, KVM_REG_SIZE_U32, KVM_REG_SIZE_U64, }; -use kvm_ioctls::VcpuFd; -use vmm_sys_util::errno; use util::offset_of; -pub type Result = std::result::Result; - -const KVM_NR_REGS: u64 = 31; -const KVM_NR_FP_REGS: u64 = 32; - /// AArch64 cpu core register. /// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/kvm.h#L50 /// User structures for general purpose, floating point and debug registers. @@ -126,87 +119,3 @@ impl From for u64 { | (reg_offset / size_of::()) as u64 } } - -/// Returns the vcpu's current `core_register`. -/// -/// The register state is gotten from `KVM_GET_ONE_REG` api in KVM. -/// -/// # Arguments -/// -/// * `vcpu_fd` - the VcpuFd in KVM mod. -pub fn get_core_regs(vcpu_fd: &VcpuFd) -> Result { - let mut core_regs = kvm_regs::default(); - - core_regs.regs.sp = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; - core_regs.sp_el1 = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())? as u64; - core_regs.regs.pstate = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())? as u64; - core_regs.regs.pc = vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())? as u64; - core_regs.elr_el1 = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())? as u64; - - for i in 0..KVM_NR_REGS as usize { - core_regs.regs.regs[i] = - vcpu_fd.get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())? as u64; - } - - for i in 0..KVM_NR_SPSR as usize { - core_regs.spsr[i] = vcpu_fd.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; - } - - for i in 0..KVM_NR_FP_REGS as usize { - core_regs.fp_regs.vregs[i] = - vcpu_fd.get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; - } - - core_regs.fp_regs.fpsr = vcpu_fd.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())? as u32; - core_regs.fp_regs.fpcr = vcpu_fd.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())? as u32; - - Ok(core_regs) -} - -/// Sets the vcpu's current "core_register" -/// -/// The register state is gotten from `KVM_SET_ONE_REG` api in KVM. -/// -/// # Arguments -/// -/// * `vcpu_fd` - the VcpuFd in KVM mod. -/// * `core_regs` - kvm_regs state to be written. -pub fn set_core_regs(vcpu_fd: &VcpuFd, core_regs: kvm_regs) -> Result<()> { - vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; - vcpu_fd.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; - vcpu_fd.set_one_reg( - Arm64CoreRegs::UserPTRegPState.into(), - core_regs.regs.pstate as u128, - )?; - vcpu_fd.set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc as u128)?; - vcpu_fd.set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1 as u128)?; - - for i in 0..KVM_NR_REGS as usize { - vcpu_fd.set_one_reg( - Arm64CoreRegs::UserPTRegRegs(i).into(), - core_regs.regs.regs[i] as u128, - )?; - } - - for i in 0..KVM_NR_SPSR as usize { - vcpu_fd.set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; - } - - for i in 0..KVM_NR_FP_REGS as usize { - vcpu_fd.set_one_reg( - Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), - core_regs.fp_regs.vregs[i], - )?; - } - - vcpu_fd.set_one_reg( - Arm64CoreRegs::UserFPSIMDStateFpsr.into(), - core_regs.fp_regs.fpsr as u128, - )?; - vcpu_fd.set_one_reg( - Arm64CoreRegs::UserFPSIMDStateFpcr.into(), - core_regs.fp_regs.fpcr as u128, - )?; - - Ok(()) -} diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 937f77c14..5e90c75bf 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -12,25 +12,20 @@ pub mod caps; mod core_regs; -mod regs; +pub use self::caps::CpregListEntry; pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; +pub use self::core_regs::Arm64CoreRegs; use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use kvm_bindings::{ kvm_mp_state as MpState, kvm_regs as Regs, kvm_vcpu_events as VcpuEvents, - kvm_vcpu_init as VcpuInit, RegList, KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, - KVM_MP_STATE_STOPPED as MP_STATE_STOPPED, + KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, KVM_MP_STATE_STOPPED as MP_STATE_STOPPED, }; -use kvm_ioctls::VcpuFd; -use self::caps::CpregListEntry; -use self::core_regs::{get_core_regs, set_core_regs}; -use self::regs::{KVM_REG_ARM_MPIDR_EL1, KVM_REG_ARM_TIMER_CNT}; use crate::CPU; -use hypervisor::kvm::KVM_FDS; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; @@ -49,8 +44,6 @@ const PSR_D_BIT: u64 = 0x0000_0200; // [40:63] bit reserved on AArch64 Architecture, const UNINIT_MPIDR: u64 = 0xFFFF_FF00_0000_0000; -const KVM_MAX_CPREG_ENTRIES: usize = 500; - /// Interrupt ID for pmu. /// See: https://developer.arm.com/documentation/den0094/b/ /// And: https://developer.arm.com/documentation/dai0492/b/ @@ -70,6 +63,15 @@ pub struct ArmCPUBootConfig { pub boot_pc: u64, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ArmRegsIndex { + CoreRegs, + MpState, + VcpuEvents, + CpregList, + VtimerCount, +} + #[derive(Default, Copy, Clone, Debug)] pub struct ArmCPUTopology {} @@ -89,27 +91,25 @@ impl ArmCPUTopology { #[desc_version(compat_version = "0.1.0")] pub struct ArmCPUState { /// The vcpu id, `0` means primary CPU. - apic_id: u32, + pub apic_id: u32, /// MPIDR register value of this vcpu, /// The MPIDR provides an additional processor identification mechanism /// for scheduling purposes. - mpidr: u64, - /// Used to pass vcpu target and supported features to kvm. - kvi: VcpuInit, + pub mpidr: u64, /// Vcpu core registers. - core_regs: Regs, + pub core_regs: Regs, /// Vcpu cpu events register. - cpu_events: VcpuEvents, + pub cpu_events: VcpuEvents, /// Vcpu mpstate register. - mp_state: MpState, + pub mp_state: MpState, /// The length of Cpreg. - cpreg_len: usize, + pub cpreg_len: usize, /// The list of Cpreg. - cpreg_list: [CpregListEntry; 512], + pub cpreg_list: [CpregListEntry; 512], /// Vcpu features - features: ArmCPUFeatures, + pub features: ArmCPUFeatures, /// Virtual timer count. - vtimer_cnt: u64, + pub vtimer_cnt: u64, } impl ArmCPUState { @@ -139,7 +139,6 @@ impl ArmCPUState { let locked_cpu_state = cpu_state.lock().unwrap(); self.apic_id = locked_cpu_state.apic_id; self.mpidr = locked_cpu_state.mpidr; - self.kvi = locked_cpu_state.kvi; self.core_regs = locked_cpu_state.core_regs; self.cpu_events = locked_cpu_state.cpu_events; self.mp_state = locked_cpu_state.mp_state; @@ -148,53 +147,6 @@ impl ArmCPUState { self.features = locked_cpu_state.features; } - /// Set register value in `ArmCPUState` according to `boot_config`. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - /// * `boot_config` - Boot message from boot_loader. - pub fn set_boot_config( - &mut self, - vcpu_fd: &Arc, - boot_config: &ArmCPUBootConfig, - vcpu_config: &ArmCPUFeatures, - ) -> Result<()> { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .get_preferred_target(&mut self.kvi) - .with_context(|| "Failed to get kvm vcpu preferred target")?; - - // support PSCI 0.2 - // We already checked that the capability is supported. - self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2; - // Non-boot cpus are powered off initially. - if self.apic_id != 0 { - self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; - } - - // Enable PMU from config. - if vcpu_config.pmu { - self.kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; - } - - self.set_core_reg(boot_config); - - vcpu_fd - .vcpu_init(&self.kvi) - .with_context(|| "Failed to init kvm vcpu")?; - self.mpidr = vcpu_fd - .get_one_reg(KVM_REG_ARM_MPIDR_EL1) - .with_context(|| "Failed to get mpidr")? as u64; - - self.features = *vcpu_config; - - Ok(()) - } - /// Set cpu topology /// /// # Arguments @@ -204,29 +156,6 @@ impl ArmCPUState { Ok(()) } - /// Reset register value in `Kvm` with `ArmCPUState`. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - pub fn reset_vcpu(&self, vcpu_fd: &Arc) -> Result<()> { - set_core_regs(vcpu_fd, self.core_regs) - .with_context(|| format!("Failed to set core register for CPU {}", self.apic_id))?; - vcpu_fd - .set_mp_state(self.mp_state) - .with_context(|| format!("Failed to set mpstate for CPU {}", self.apic_id))?; - for cpreg in self.cpreg_list[0..self.cpreg_len].iter() { - cpreg - .set_cpreg(&vcpu_fd.clone()) - .with_context(|| format!("Failed to set cpreg for CPU {}", self.apic_id))?; - } - vcpu_fd - .set_vcpu_events(&self.cpu_events) - .with_context(|| format!("Failed to set vcpu event for CPU {}", self.apic_id))?; - - Ok(()) - } - /// Get mpidr value. pub fn mpidr(&self) -> u64 { self.mpidr @@ -237,12 +166,7 @@ impl ArmCPUState { self.core_regs } - /// Get kvm_vcpu_init. - pub fn kvi(&self) -> VcpuInit { - self.kvi - } - - fn set_core_reg(&mut self, boot_config: &ArmCPUBootConfig) { + pub fn set_core_reg(&mut self, boot_config: &ArmCPUBootConfig) { // Set core regs. self.core_regs.regs.pstate = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h; self.core_regs.regs.regs[1] = 0; @@ -256,29 +180,6 @@ impl ArmCPUState { } } - /// Set virtual timer Count register value to `Kvm` with `ArmCPUState`. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - pub fn set_virtual_timer_cnt(&self, vcpu_fd: &Arc) -> Result<()> { - vcpu_fd - .set_one_reg(KVM_REG_ARM_TIMER_CNT, self.vtimer_cnt as u128) - .with_context(|| "Failed to set virtual timer count") - } - - /// Get virtual timer Count register value from `Kvm` with `ArmCPUState`. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - pub fn get_virtual_timer_cnt(&mut self, vcpu_fd: &Arc) -> Result<()> { - self.vtimer_cnt = vcpu_fd - .get_one_reg(KVM_REG_ARM_TIMER_CNT) - .with_context(|| "Failed to get virtual timer count")? as u64; - Ok(()) - } - /// Get cpu features. pub fn get_features(&self) -> &ArmCPUFeatures { &self.features @@ -287,34 +188,26 @@ impl ArmCPUState { impl StateTransfer for CPU { fn get_state_vec(&self) -> migration::Result> { - let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); + self.hypervisor_cpu + .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CoreRegs, &self.caps)?; - cpu_state_locked.core_regs = get_core_regs(&self.fd)?; if self.caps.mp_state { - let mut mp_state = self.fd.get_mp_state()?; - if mp_state.mp_state != MP_STATE_STOPPED { - mp_state.mp_state = MP_STATE_RUNNABLE; - } - cpu_state_locked.mp_state = mp_state; - } + self.hypervisor_cpu.get_regs( + self.arch_cpu.clone(), + ArmRegsIndex::MpState, + &self.caps, + )?; + }; - let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES)?; - self.fd.get_reg_list(&mut cpreg_list)?; - cpu_state_locked.cpreg_len = 0; - for (index, cpreg) in cpreg_list.as_slice().iter().enumerate() { - let mut cpreg_entry = CpregListEntry { - reg_id: *cpreg, - value: 0, - }; - if cpreg_entry.validate() { - cpreg_entry.get_cpreg(&self.fd.clone())?; - cpu_state_locked.cpreg_list[index] = cpreg_entry; - cpu_state_locked.cpreg_len += 1; - } - } - cpu_state_locked.cpu_events = self.fd.get_vcpu_events()?; + self.hypervisor_cpu + .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CpregList, &self.caps)?; + self.hypervisor_cpu.get_regs( + self.arch_cpu.clone(), + ArmRegsIndex::VcpuEvents, + &self.caps, + )?; - Ok(cpu_state_locked.as_bytes().to_vec()) + Ok(self.arch_cpu.lock().unwrap().as_bytes().to_vec()) } fn set_state(&self, state: &[u8]) -> migration::Result<()> { @@ -323,8 +216,9 @@ impl StateTransfer for CPU { let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); *cpu_state_locked = cpu_state; + drop(cpu_state_locked); - self.fd.vcpu_init(&cpu_state.kvi)?; + self.hypervisor_cpu.vcpu_init()?; if cpu_state.features.pmu { self.hypervisor_cpu diff --git a/cpu/src/aarch64/regs.rs b/cpu/src/aarch64/regs.rs deleted file mode 100644 index 1f8584f15..000000000 --- a/cpu/src/aarch64/regs.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use kvm_bindings::{ - KVM_REG_ARM64, KVM_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG_CRM_MASK, - KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT, - KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK, - KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT, - KVM_REG_SIZE_U64, -}; - -// Arm Architecture Reference Manual defines the encoding of AArch64 system registers: -// (Ref: ARMv8 ARM, Section: "System instruction class encoding overview") -// While KVM defines another ID for each AArch64 system register, which is used in calling -// `KVM_G/SET_ONE_REG` to access a system register of a guest. A mapping exists between the -// Arm standard encoding and the KVM ID. -// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/kvm.h#L216 -#[macro_export] -macro_rules! arm64_sys_reg { - ($op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => { - KVM_REG_SIZE_U64 - | KVM_REG_ARM64 - | KVM_REG_ARM64_SYSREG as u64 - | (((($op0 as u32) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK) - as u64) - | (((($op1 as u32) << KVM_REG_ARM64_SYSREG_OP1_SHIFT) & KVM_REG_ARM64_SYSREG_OP1_MASK) - as u64) - | (((($crn as u32) << KVM_REG_ARM64_SYSREG_CRN_SHIFT) & KVM_REG_ARM64_SYSREG_CRN_MASK) - as u64) - | (((($crm as u32) << KVM_REG_ARM64_SYSREG_CRM_SHIFT) & KVM_REG_ARM64_SYSREG_CRM_MASK) - as u64) - | (((($op2 as u32) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK) - as u64) - }; -} - -// The following system register codes can be found at this website: -// https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h - -// MPIDR - Multiprocessor Affinity Register(SYS_MPIDR_EL1). -pub(crate) const KVM_REG_ARM_MPIDR_EL1: u64 = arm64_sys_reg!(3, 0, 0, 0, 5); - -// Counter-timer Virtual Count register: Due to the API interface problem, the encode of -// this register is SYS_CNTV_CVAL_EL0. -pub(crate) const KVM_REG_ARM_TIMER_CNT: u64 = arm64_sys_reg!(3, 3, 14, 3, 2); diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index a3bb8e6da..c163d73f6 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -35,6 +35,8 @@ mod aarch64; #[cfg(target_arch = "x86_64")] mod x86_64; +#[cfg(target_arch = "aarch64")] +pub use aarch64::Arm64CoreRegs; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUBootConfig as CPUBootConfig; #[cfg(target_arch = "aarch64")] @@ -46,17 +48,24 @@ pub use aarch64::ArmCPUState as ArchCPU; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUTopology as CPUTopology; #[cfg(target_arch = "aarch64")] +pub use aarch64::ArmRegsIndex as RegsIndex; +#[cfg(target_arch = "aarch64")] +pub use aarch64::CpregListEntry; +#[cfg(target_arch = "aarch64")] pub use aarch64::PMU_INTR; #[cfg(target_arch = "aarch64")] pub use aarch64::PPI_BASE; pub use error::CpuError; - +#[cfg(target_arch = "x86_64")] +pub use x86_64::caps::X86CPUCaps as CPUCaps; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUState as ArchCPU; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUTopology as CPUTopology; +#[cfg(target_arch = "x86_64")] +pub use x86_64::X86RegsIndex as RegsIndex; use std::cell::RefCell; use std::sync::atomic::{fence, AtomicBool, Ordering}; @@ -77,8 +86,6 @@ use machine_manager::machine::{HypervisorType, MachineInterface}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; #[cfg(not(test))] use util::test_helper::is_test_enabled; -#[cfg(target_arch = "x86_64")] -use x86_64::caps::X86CPUCaps as CPUCaps; // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal // number should be assigned to SIGRTMIN + n, (n = 0...30). @@ -173,8 +180,29 @@ pub trait CPUHypervisorOps: Send + Sync { fn get_msr_index_list(&self) -> Vec; - #[cfg(target_arch = "aarch64")] fn init_pmu(&self) -> Result<()>; + + fn vcpu_init(&self) -> Result<()>; + + fn set_boot_config( + &self, + arch_cpu: Arc>, + boot_config: &CPUBootConfig, + #[cfg(target_arch = "aarch64")] vcpu_config: &CPUFeatures, + ) -> Result<()>; + + fn get_one_reg(&self, reg_id: u64) -> Result; + + fn get_regs( + &self, + arch_cpu: Arc>, + regs_index: RegsIndex, + caps: &CPUCaps, + ) -> Result<()>; + + fn set_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()>; + + fn reset_vcpu(&self, cpu: Arc) -> Result<()>; } /// `CPU` is a wrapper around creating and using a kvm-based VCPU. @@ -185,7 +213,7 @@ pub struct CPU { /// The file descriptor of this kvm-based VCPU. fd: Arc, /// Architecture special CPU property. - arch_cpu: Arc>, + pub arch_cpu: Arc>, /// LifeCycle state of kvm-based VCPU. state: Arc<(Mutex, Condvar)>, /// The thread handler of this virtual CPU. @@ -195,7 +223,7 @@ pub struct CPU { /// The VM combined by this VCPU. vm: Weak>, /// The capability of VCPU. - caps: CPUCaps, + pub caps: CPUCaps, /// The state backup of architecture CPU right before boot. boot_state: Arc>, /// Sync the pause state of vCPU in kvm and userspace. @@ -298,11 +326,9 @@ impl CPUInterface for CPU { )))); } - self.arch_cpu - .lock() - .unwrap() + self.hypervisor_cpu .set_boot_config( - &self.fd, + self.arch_cpu.clone(), boot, #[cfg(target_arch = "aarch64")] config, @@ -321,11 +347,8 @@ impl CPUInterface for CPU { fn resume(&self) -> Result<()> { #[cfg(target_arch = "aarch64")] - self.arch() - .lock() - .unwrap() - .set_virtual_timer_cnt(self.fd())?; - + self.hypervisor_cpu + .set_regs(self.arch_cpu.clone(), RegsIndex::VtimerCount)?; let (cpu_state_locked, cvar) = &*self.state; let mut cpu_state = cpu_state_locked.lock().unwrap(); if *cpu_state == CpuLifecycleState::Running { @@ -433,10 +456,8 @@ impl CPUInterface for CPU { } #[cfg(target_arch = "aarch64")] - self.arch() - .lock() - .unwrap() - .get_virtual_timer_cnt(self.fd())?; + self.hypervisor_cpu + .get_regs(self.arch_cpu.clone(), RegsIndex::VtimerCount, &self.caps)?; Ok(()) } @@ -633,11 +654,11 @@ impl CPUThreadWorker { fn run_on_local_thread_vcpu(func: F) -> Result<()> where - F: FnOnce(&CPU), + F: FnOnce(Arc), { Self::LOCAL_THREAD_VCPU.with(|thread_vcpu| { if let Some(local_thread_vcpu) = thread_vcpu.borrow().as_ref() { - func(&local_thread_vcpu.thread_cpu); + func(local_thread_vcpu.thread_cpu.clone()); Ok(()) } else { Err(anyhow!(CpuError::VcpuLocalThreadNotPresent)) @@ -659,11 +680,8 @@ impl CPUThreadWorker { } VCPU_RESET_SIGNAL => { let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - if let Err(e) = vcpu.arch_cpu.lock().unwrap().reset_vcpu( - &vcpu.fd, - #[cfg(target_arch = "x86_64")] - &vcpu.caps, - ) { + let cpu = vcpu.clone(); + if let Err(e) = vcpu.hypervisor_cpu().reset_vcpu(cpu) { error!("Failed to reset vcpu state: {:?}", e) } }); @@ -721,16 +739,11 @@ impl CPUThreadWorker { // The vcpu thread is going to run, // reset its running environment. + let cpu = self.thread_cpu.clone(); #[cfg(not(test))] self.thread_cpu - .arch_cpu - .lock() - .unwrap() - .reset_vcpu( - &self.thread_cpu.fd, - #[cfg(target_arch = "x86_64")] - &self.thread_cpu.caps, - ) + .hypervisor_cpu + .reset_vcpu(cpu) .with_context(|| "Failed to reset for cpu register state")?; // Wait for all vcpu to complete the running diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 493fb60e4..8a48b508d 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -16,17 +16,16 @@ mod cpuid; use std::sync::{Arc, Mutex}; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use kvm_bindings::{ kvm_cpuid_entry2 as CpuidEntry2, kvm_debugregs as DebugRegs, kvm_fpu as Fpu, kvm_lapic_state as LapicState, kvm_mp_state as MpState, kvm_msr_entry as MsrEntry, kvm_regs as Regs, kvm_segment as Segment, kvm_sregs as Sregs, kvm_vcpu_events as VcpuEvents, - kvm_xcrs as Xcrs, kvm_xsave as Xsave, CpuId, Msrs, + kvm_xcrs as Xcrs, kvm_xsave as Xsave, CpuId, KVM_CPUID_FLAG_SIGNIFCANT_INDEX as CPUID_FLAG_SIGNIFICANT_INDEX, - KVM_MAX_CPUID_ENTRIES as MAX_CPUID_ENTRIES, KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, + KVM_MP_STATE_RUNNABLE as MP_STATE_RUNNABLE, KVM_MP_STATE_UNINITIALIZED as MP_STATE_UNINITIALIZED, }; -use kvm_ioctls::{Kvm, VcpuFd}; use self::cpuid::host_cpuid; use crate::CPU; @@ -62,6 +61,20 @@ const ECX_THREAD: u32 = 1u32 << 8; const ECX_CORE: u32 = 2u32 << 8; const ECX_DIE: u32 = 5u32 << 8; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum X86RegsIndex { + Regs, + Sregs, + Fpu, + MpState, + LapicState, + MsrEntry, + VcpuEvents, + Xsave, + Xcrs, + DebugRegs, +} + /// X86 CPU booting configure information #[allow(clippy::upper_case_acronyms)] #[derive(Default, Clone, Debug)] @@ -117,18 +130,18 @@ pub struct X86CPUState { nr_cores: u32, nr_dies: u32, nr_sockets: u32, - apic_id: u32, - regs: Regs, - sregs: Sregs, - fpu: Fpu, - mp_state: MpState, - lapic: LapicState, - msr_len: usize, - msr_list: [MsrEntry; 256], - cpu_events: VcpuEvents, - xsave: Xsave, - xcrs: Xcrs, - debugregs: DebugRegs, + pub apic_id: u32, + pub regs: Regs, + pub sregs: Sregs, + pub fpu: Fpu, + pub mp_state: MpState, + pub lapic: LapicState, + pub msr_len: usize, + pub msr_list: [MsrEntry; 256], + pub cpu_events: VcpuEvents, + pub xsave: Xsave, + pub xcrs: Xcrs, + pub debugregs: DebugRegs, } impl X86CPUState { @@ -175,26 +188,6 @@ impl X86CPUState { self.debugregs = locked_cpu_state.debugregs; } - /// Set register value in `X86CPUState` according to `boot_config`. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - /// * `boot_config` - Boot message from boot_loader. - pub fn set_boot_config( - &mut self, - vcpu_fd: &Arc, - boot_config: &X86CPUBootConfig, - ) -> Result<()> { - self.setup_lapic(vcpu_fd)?; - self.setup_regs(boot_config); - self.setup_sregs(vcpu_fd, boot_config)?; - self.setup_fpu(); - self.setup_msrs(); - - Ok(()) - } - /// Set cpu topology /// /// # Arguments @@ -207,56 +200,7 @@ impl X86CPUState { Ok(()) } - /// Reset register value with `X86CPUState`. - /// - /// # Arguments - /// - /// * `vcpu_fd` - Vcpu file descriptor in kvm. - /// * `caps` - Vcpu capabilities in kvm. - pub fn reset_vcpu(&self, vcpu_fd: &Arc, caps: &caps::X86CPUCaps) -> Result<()> { - self.setup_cpuid(vcpu_fd) - .with_context(|| format!("Failed to set cpuid for CPU {}", self.apic_id))?; - - vcpu_fd - .set_mp_state(self.mp_state) - .with_context(|| format!("Failed to set mpstate for CPU {}", self.apic_id))?; - vcpu_fd - .set_sregs(&self.sregs) - .with_context(|| format!("Failed to set sregs for CPU {}", self.apic_id))?; - vcpu_fd - .set_regs(&self.regs) - .with_context(|| format!("Failed to set regs for CPU {}", self.apic_id))?; - if caps.has_xsave { - vcpu_fd - .set_xsave(&self.xsave) - .with_context(|| format!("Failed to set xsave for CPU {}", self.apic_id))?; - } else { - vcpu_fd - .set_fpu(&self.fpu) - .with_context(|| format!("Failed to set fpu for CPU {}", self.apic_id))?; - } - if caps.has_xcrs { - vcpu_fd - .set_xcrs(&self.xcrs) - .with_context(|| format!("Failed to set xcrs for CPU {}", self.apic_id))?; - } - vcpu_fd - .set_debug_regs(&self.debugregs) - .with_context(|| format!("Failed to set debug register for CPU {}", self.apic_id))?; - vcpu_fd - .set_lapic(&self.lapic) - .with_context(|| format!("Failed to set lapic for CPU {}", self.apic_id))?; - vcpu_fd - .set_msrs(&Msrs::from_entries(&self.msr_list[0..self.msr_len])?) - .with_context(|| format!("Failed to set msrs for CPU {}", self.apic_id))?; - vcpu_fd - .set_vcpu_events(&self.cpu_events) - .with_context(|| format!("Failed to set vcpu events for CPU {}", self.apic_id))?; - - Ok(()) - } - - fn setup_lapic(&mut self, vcpu_fd: &Arc) -> Result<()> { + pub fn setup_lapic(&mut self, lapic: LapicState) -> Result<()> { // Disable nmi and external interrupt before enter protected mode // See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/apicdef.h const APIC_LVT0: usize = 0x350; @@ -265,9 +209,7 @@ impl X86CPUState { const APIC_MODE_EXTINT: u32 = 0x7; const APIC_ID: usize = 0x20; - self.lapic = vcpu_fd - .get_lapic() - .with_context(|| format!("Failed to get lapic for CPU {}/KVM", self.apic_id))?; + self.lapic = lapic; // SAFETY: The member regs in struct LapicState is a u8 array with 1024 entries, // so it's saft to cast u8 pointer to u32 at position APIC_LVT0 and APIC_LVT1. @@ -288,7 +230,7 @@ impl X86CPUState { Ok(()) } - fn setup_regs(&mut self, boot_config: &X86CPUBootConfig) { + pub fn setup_regs(&mut self, boot_config: &X86CPUBootConfig) { self.regs = Regs { rflags: 0x0002, // Means processor has been initialized rip: boot_config.boot_ip, @@ -299,10 +241,8 @@ impl X86CPUState { }; } - fn setup_sregs(&mut self, vcpu_fd: &Arc, boot_config: &X86CPUBootConfig) -> Result<()> { - self.sregs = vcpu_fd - .get_sregs() - .with_context(|| format!("Failed to get sregs for CPU {}/KVM", self.apic_id))?; + pub fn setup_sregs(&mut self, sregs: Sregs, boot_config: &X86CPUBootConfig) -> Result<()> { + self.sregs = sregs; self.sregs.cs.base = (boot_config.boot_selector as u64) << 4; self.sregs.cs.selector = boot_config.boot_selector; @@ -324,7 +264,7 @@ impl X86CPUState { Ok(()) } - fn set_prot64_sregs(&mut self, boot_config: &X86CPUBootConfig) { + pub fn set_prot64_sregs(&mut self, boot_config: &X86CPUBootConfig) { // X86_CR0_PE: Protection Enable // EFER_LME: Long mode enable // EFER_LMA: Long mode active @@ -366,7 +306,7 @@ impl X86CPUState { self.sregs.cr0 |= X86_CR0_PG; } - fn setup_fpu(&mut self) { + pub fn setup_fpu(&mut self) { // Default value for fxregs_state.mxcsr // arch/x86/include/asm/fpu/types.h const MXCSR_DEFAULT: u32 = 0x1f80; @@ -378,7 +318,7 @@ impl X86CPUState { }; } - fn setup_msrs(&mut self) { + pub fn setup_msrs(&mut self) { // Enable fasting-string operation to improve string // store operations. for (index, msr) in MSR_LIST.iter().enumerate() { @@ -396,7 +336,7 @@ impl X86CPUState { } } - fn adjust_cpuid(&self, cpuid: &mut CpuId) -> Result<()> { + pub fn adjust_cpuid(&self, cpuid: &mut CpuId) -> Result<()> { if self.nr_dies < 2 { return Ok(()); } @@ -424,20 +364,11 @@ impl X86CPUState { Ok(()) } - fn setup_cpuid(&self, vcpu_fd: &Arc) -> Result<()> { + pub fn setup_cpuid(&self, cpuid: &mut CpuId) -> Result<()> { let core_offset = 32u32 - (self.nr_threads - 1).leading_zeros(); let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset; let pkg_offset = (32u32 - (self.nr_dies - 1).leading_zeros()) + die_offset; - let sys_fd = match Kvm::new() { - Ok(fd) => fd, - _ => bail!("setup_cpuid: Open /dev/kvm failed"), - }; - let mut cpuid = sys_fd - .get_supported_cpuid(CPUID_FLAG_SIGNIFICANT_INDEX) - .with_context(|| { - format!("Failed to get supported cpuid for CPU {}/KVM", self.apic_id) - })?; - self.adjust_cpuid(&mut cpuid)?; + self.adjust_cpuid(cpuid)?; let entries = cpuid.as_mut_slice(); for entry in entries.iter_mut() { @@ -560,38 +491,31 @@ impl X86CPUState { } } - vcpu_fd - .set_cpuid2(&cpuid) - .with_context(|| format!("Failed to set cpuid for CPU {}/KVM", self.apic_id))?; Ok(()) } } impl StateTransfer for CPU { fn get_state_vec(&self) -> Result> { - let mut msr_entries = self.caps.create_msr_entries()?; - let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); + let hypervisor_cpu = self.hypervisor_cpu(); - cpu_state_locked.mp_state = self.fd.get_mp_state()?; - cpu_state_locked.regs = self.fd.get_regs()?; - cpu_state_locked.sregs = self.fd.get_sregs()?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::MpState, &self.caps)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Regs, &self.caps)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Sregs, &self.caps)?; if self.caps.has_xsave { - cpu_state_locked.xsave = self.fd.get_xsave()?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Xsave, &self.caps)?; } else { - cpu_state_locked.fpu = self.fd.get_fpu()?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Fpu, &self.caps)?; } if self.caps.has_xcrs { - cpu_state_locked.xcrs = self.fd.get_xcrs()?; - } - cpu_state_locked.debugregs = self.fd.get_debug_regs()?; - cpu_state_locked.lapic = self.fd.get_lapic()?; - cpu_state_locked.msr_len = self.fd.get_msrs(&mut msr_entries)?; - for (i, entry) in msr_entries.as_slice().iter().enumerate() { - cpu_state_locked.msr_list[i] = *entry; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Xcrs, &self.caps)?; } - cpu_state_locked.cpu_events = self.fd.get_vcpu_events()?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::DebugRegs, &self.caps)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::LapicState, &self.caps)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::MsrEntry, &self.caps)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::VcpuEvents, &self.caps)?; - Ok(cpu_state_locked.as_bytes().to_vec()) + Ok(self.arch_cpu.lock().unwrap().as_bytes().to_vec()) } fn set_state(&self, state: &[u8]) -> migration::Result<()> { diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor_refactor/src/kvm/aarch64/mod.rs index 9a15b0654..ac29a1557 100644 --- a/hypervisor_refactor/src/kvm/aarch64/mod.rs +++ b/hypervisor_refactor/src/kvm/aarch64/mod.rs @@ -15,6 +15,7 @@ pub mod gicv3; use std::mem::forget; use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use kvm_bindings::*; @@ -22,7 +23,49 @@ use kvm_ioctls::DeviceFd; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use crate::kvm::{KvmCpu, KvmHypervisor}; -use cpu::{PMU_INTR, PPI_BASE}; +use cpu::{ + ArchCPU, Arm64CoreRegs, CPUBootConfig, CPUCaps, CPUFeatures, CpregListEntry, RegsIndex, CPU, + PMU_INTR, PPI_BASE, +}; + +// Arm Architecture Reference Manual defines the encoding of AArch64 system registers: +// (Ref: ARMv8 ARM, Section: "System instruction class encoding overview") +// While KVM defines another ID for each AArch64 system register, which is used in calling +// `KVM_G/SET_ONE_REG` to access a system register of a guest. A mapping exists between the +// Arm standard encoding and the KVM ID. +// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/kvm.h#L216 +#[macro_export] +macro_rules! arm64_sys_reg { + ($op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => { + KVM_REG_SIZE_U64 + | KVM_REG_ARM64 + | KVM_REG_ARM64_SYSREG as u64 + | (((($op0 as u32) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK) + as u64) + | (((($op1 as u32) << KVM_REG_ARM64_SYSREG_OP1_SHIFT) & KVM_REG_ARM64_SYSREG_OP1_MASK) + as u64) + | (((($crn as u32) << KVM_REG_ARM64_SYSREG_CRN_SHIFT) & KVM_REG_ARM64_SYSREG_CRN_MASK) + as u64) + | (((($crm as u32) << KVM_REG_ARM64_SYSREG_CRM_SHIFT) & KVM_REG_ARM64_SYSREG_CRM_MASK) + as u64) + | (((($op2 as u32) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK) + as u64) + }; +} + +// The following system register codes can be found at this website: +// https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h + +// MPIDR - Multiprocessor Affinity Register(SYS_MPIDR_EL1). +pub const KVM_REG_ARM_MPIDR_EL1: u64 = arm64_sys_reg!(3, 0, 0, 0, 5); + +// Counter-timer Virtual Count register: Due to the API interface problem, the encode of +// this register is SYS_CNTV_CVAL_EL0. +pub const KVM_REG_ARM_TIMER_CNT: u64 = arm64_sys_reg!(3, 3, 14, 3, 2); + +pub const KVM_MAX_CPREG_ENTRIES: usize = 500; +const KVM_NR_REGS: u64 = 31; +const KVM_NR_FP_REGS: u64 = 32; ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr); ioctl_iow_nr!(KVM_GET_ONE_REG, KVMIO, 0xab, kvm_one_reg); @@ -116,4 +159,293 @@ impl KvmCpu { Ok(()) } + + pub fn arch_vcpu_init(&self) -> Result<()> { + self.fd + .vcpu_init(&self.kvi.lock().unwrap()) + .with_context(|| "Failed to init kvm vcpu") + } + + pub fn arch_set_boot_config( + &self, + arch_cpu: Arc>, + boot_config: &CPUBootConfig, + vcpu_config: &CPUFeatures, + ) -> Result<()> { + let mut kvi = self.kvi.lock().unwrap(); + self.vm_fd + .as_ref() + .unwrap() + .get_preferred_target(&mut kvi) + .with_context(|| "Failed to get kvm vcpu preferred target")?; + + // support PSCI 0.2 + // We already checked that the capability is supported. + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2; + // Non-boot cpus are powered off initially. + if arch_cpu.lock().unwrap().apic_id != 0 { + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF; + } + + // Enable PMU from config. + if vcpu_config.pmu { + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; + } + drop(kvi); + + arch_cpu.lock().unwrap().set_core_reg(boot_config); + + self.arch_vcpu_init()?; + + arch_cpu.lock().unwrap().mpidr = + self.fd + .get_one_reg(KVM_REG_ARM_MPIDR_EL1) + .with_context(|| "Failed to get mpidr")? as u64; + + arch_cpu.lock().unwrap().features = *vcpu_config; + + Ok(()) + } + + pub fn arch_get_one_reg(&self, reg_id: u64) -> Result { + Ok(self.fd.get_one_reg(reg_id)?) + } + + pub fn arch_get_regs( + &self, + arch_cpu: Arc>, + regs_index: RegsIndex, + _caps: &CPUCaps, + ) -> Result<()> { + let mut locked_arch_cpu = arch_cpu.lock().unwrap(); + + match regs_index { + RegsIndex::CoreRegs => { + locked_arch_cpu.core_regs = self.get_core_regs()?; + } + RegsIndex::MpState => { + let mut mp_state = self.fd.get_mp_state()?; + if mp_state.mp_state != KVM_MP_STATE_STOPPED { + mp_state.mp_state = KVM_MP_STATE_RUNNABLE; + } + locked_arch_cpu.mp_state = mp_state; + } + RegsIndex::VcpuEvents => { + locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; + } + RegsIndex::CpregList => { + let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES)?; + self.fd.get_reg_list(&mut cpreg_list)?; + locked_arch_cpu.cpreg_len = 0; + for (index, cpreg) in cpreg_list.as_slice().iter().enumerate() { + let mut cpreg_entry = CpregListEntry { + reg_id: *cpreg, + value: 0, + }; + if self.validate(&cpreg_entry) { + self.get_cpreg(&mut cpreg_entry)?; + locked_arch_cpu.cpreg_list[index] = cpreg_entry; + locked_arch_cpu.cpreg_len += 1; + } + } + } + RegsIndex::VtimerCount => { + locked_arch_cpu.vtimer_cnt = self + .fd + .get_one_reg(KVM_REG_ARM_TIMER_CNT) + .with_context(|| "Failed to get virtual timer count")? + as u64; + } + } + + Ok(()) + } + + pub fn arch_set_regs( + &self, + arch_cpu: Arc>, + regs_index: RegsIndex, + ) -> Result<()> { + let locked_arch_cpu = arch_cpu.lock().unwrap(); + let apic_id = locked_arch_cpu.apic_id; + match regs_index { + RegsIndex::CoreRegs => { + self.set_core_regs(locked_arch_cpu.core_regs) + .with_context(|| format!("Failed to set core register for CPU {}", apic_id))?; + } + RegsIndex::MpState => { + self.fd + .set_mp_state(locked_arch_cpu.mp_state) + .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + } + RegsIndex::VcpuEvents => { + self.fd + .set_vcpu_events(&locked_arch_cpu.cpu_events) + .with_context(|| format!("Failed to set vcpu event for CPU {}", apic_id))?; + } + RegsIndex::CpregList => { + for cpreg in locked_arch_cpu.cpreg_list[0..locked_arch_cpu.cpreg_len].iter() { + self.set_cpreg(cpreg) + .with_context(|| format!("Failed to set cpreg for CPU {}", apic_id))?; + } + } + RegsIndex::VtimerCount => { + self.fd + .set_one_reg(KVM_REG_ARM_TIMER_CNT, locked_arch_cpu.vtimer_cnt as u128) + .with_context(|| "Failed to set virtual timer count")?; + } + } + + Ok(()) + } + + /// Returns the vcpu's current `core_register`. + /// + /// The register state is gotten from `KVM_GET_ONE_REG` api in KVM. + /// + /// # Arguments + /// + /// * `vcpu_fd` - the VcpuFd in KVM mod. + pub fn get_core_regs(&self) -> Result { + let mut core_regs = kvm_regs::default(); + + core_regs.regs.sp = self.fd.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; + core_regs.sp_el1 = self.fd.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())? as u64; + core_regs.regs.pstate = self.fd.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())? as u64; + core_regs.regs.pc = self.fd.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())? as u64; + core_regs.elr_el1 = self.fd.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())? as u64; + + for i in 0..KVM_NR_REGS as usize { + core_regs.regs.regs[i] = + self.fd + .get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())? as u64; + } + + for i in 0..KVM_NR_SPSR as usize { + core_regs.spsr[i] = self.fd.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; + } + + for i in 0..KVM_NR_FP_REGS as usize { + core_regs.fp_regs.vregs[i] = self + .fd + .get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; + } + + core_regs.fp_regs.fpsr = + self.fd + .get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())? as u32; + core_regs.fp_regs.fpcr = + self.fd + .get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())? as u32; + + Ok(core_regs) + } + + /// Sets the vcpu's current "core_register" + /// + /// The register state is gotten from `KVM_SET_ONE_REG` api in KVM. + /// + /// # Arguments + /// + /// * `vcpu_fd` - the VcpuFd in KVM mod. + /// * `core_regs` - kvm_regs state to be written. + pub fn set_core_regs(&self, core_regs: kvm_regs) -> Result<()> { + self.fd + .set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; + self.fd + .set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; + self.fd.set_one_reg( + Arm64CoreRegs::UserPTRegPState.into(), + core_regs.regs.pstate as u128, + )?; + self.fd + .set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc as u128)?; + self.fd + .set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1 as u128)?; + + for i in 0..KVM_NR_REGS as usize { + self.fd.set_one_reg( + Arm64CoreRegs::UserPTRegRegs(i).into(), + core_regs.regs.regs[i] as u128, + )?; + } + + for i in 0..KVM_NR_SPSR as usize { + self.fd + .set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; + } + + for i in 0..KVM_NR_FP_REGS as usize { + self.fd.set_one_reg( + Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), + core_regs.fp_regs.vregs[i], + )?; + } + + self.fd.set_one_reg( + Arm64CoreRegs::UserFPSIMDStateFpsr.into(), + core_regs.fp_regs.fpsr as u128, + )?; + self.fd.set_one_reg( + Arm64CoreRegs::UserFPSIMDStateFpcr.into(), + core_regs.fp_regs.fpcr as u128, + )?; + + Ok(()) + } + + fn cpreg_tuples_entry(&self, cpreg_list_entry: &CpregListEntry) -> bool { + (cpreg_list_entry.reg_id & KVM_REG_ARM_COPROC_MASK as u64) == (KVM_REG_ARM_CORE as u64) + } + + pub fn normal_cpreg_entry(&self, cpreg_list_entry: &CpregListEntry) -> bool { + if self.cpreg_tuples_entry(cpreg_list_entry) { + return false; + } + + ((cpreg_list_entry.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) + || ((cpreg_list_entry.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) + } + + /// Validate cpreg_list's tuples entry and normal entry. + pub fn validate(&self, cpreg_list_entry: &CpregListEntry) -> bool { + if self.cpreg_tuples_entry(cpreg_list_entry) { + return true; + } + + self.normal_cpreg_entry(cpreg_list_entry) + } + + pub fn get_cpreg(&self, cpreg_list_entry: &mut CpregListEntry) -> Result<()> { + if self.normal_cpreg_entry(cpreg_list_entry) { + cpreg_list_entry.value = self.fd.get_one_reg(cpreg_list_entry.reg_id)?; + } + + Ok(()) + } + + pub fn set_cpreg(&self, cpreg: &CpregListEntry) -> Result<()> { + if self.normal_cpreg_entry(cpreg) { + self.fd.set_one_reg(cpreg.reg_id, cpreg.value)?; + } + + Ok(()) + } + + pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { + let locked_arch_cpu = cpu.arch_cpu.lock().unwrap(); + let apic_id = locked_arch_cpu.apic_id; + self.set_core_regs(locked_arch_cpu.core_regs) + .with_context(|| format!("Failed to set core register for CPU {}", apic_id))?; + self.fd + .set_mp_state(locked_arch_cpu.mp_state) + .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + for cpreg in locked_arch_cpu.cpreg_list[0..locked_arch_cpu.cpreg_len].iter() { + self.set_cpreg(cpreg) + .with_context(|| format!("Failed to set cpreg for CPU {}", apic_id))?; + } + self.fd + .set_vcpu_events(&locked_arch_cpu.cpu_events) + .with_context(|| format!("Failed to set vcpu event for CPU {}", apic_id)) + } } diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index ad626e0a5..25a8df945 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -18,6 +18,8 @@ pub mod x86_64; mod interrupt; mod listener; +#[cfg(target_arch = "aarch64")] +pub use self::aarch64::{KVM_REG_ARM_MPIDR_EL1, KVM_REG_ARM_TIMER_CNT}; #[cfg(target_arch = "aarch64")] pub use aarch64::gicv2::KvmGICv2; #[cfg(target_arch = "aarch64")] @@ -29,7 +31,6 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; - use kvm_ioctls::{Cap, Kvm, VcpuFd, VmFd}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; @@ -38,7 +39,9 @@ use super::HypervisorOps; #[cfg(target_arch = "x86_64")] use crate::HypervisorError; use address_space::{AddressSpace, Listener}; -use cpu::CPUHypervisorOps; +#[cfg(target_arch = "aarch64")] +use cpu::CPUFeatures; +use cpu::{ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, RegsIndex, CPU}; #[cfg(target_arch = "aarch64")] use devices::{ GICVersion, GICv2, GICv3, GICv3ItsState, GICv3State, ICGICConfig, InterruptController, @@ -220,7 +223,11 @@ impl HypervisorOps for KvmHypervisor { _vcpu_id: u8, vcpu_fd: Arc, ) -> Result> { - Ok(Arc::new(KvmCpu::new(vcpu_fd))) + Ok(Arc::new(KvmCpu::new( + #[cfg(target_arch = "aarch64")] + self.vm_fd.clone(), + vcpu_fd, + ))) } } @@ -300,12 +307,26 @@ impl MigrateOps for KvmHypervisor { } pub struct KvmCpu { + #[cfg(target_arch = "aarch64")] + vm_fd: Option>, fd: Arc, + #[cfg(target_arch = "aarch64")] + /// Used to pass vcpu target and supported features to kvm. + pub kvi: Mutex, } impl KvmCpu { - pub fn new(vcpu_fd: Arc) -> Self { - Self { fd: vcpu_fd } + pub fn new( + #[cfg(target_arch = "aarch64")] vm_fd: Option>, + vcpu_fd: Arc, + ) -> Self { + Self { + #[cfg(target_arch = "aarch64")] + vm_fd, + fd: vcpu_fd, + #[cfg(target_arch = "aarch64")] + kvi: Mutex::new(kvm_vcpu_init::default()), + } } } @@ -326,4 +347,43 @@ impl CPUHypervisorOps for KvmCpu { fn init_pmu(&self) -> Result<()> { self.arch_init_pmu() } + + fn vcpu_init(&self) -> Result<()> { + self.arch_vcpu_init() + } + + fn set_boot_config( + &self, + arch_cpu: Arc>, + boot_config: &CPUBootConfig, + #[cfg(target_arch = "aarch64")] vcpu_config: &CPUFeatures, + ) -> Result<()> { + #[cfg(target_arch = "aarch64")] + return self.arch_set_boot_config(arch_cpu, boot_config, vcpu_config); + #[cfg(target_arch = "x86_64")] + return self.arch_set_boot_config(arch_cpu, boot_config); + } + + fn get_one_reg(&self, reg_id: u64) -> Result { + self.arch_get_one_reg(reg_id) + } + + fn get_regs( + &self, + arch_cpu: Arc>, + regs_index: RegsIndex, + caps: &CPUCaps, + ) -> Result<()> { + self.arch_get_regs(arch_cpu, regs_index, caps) + } + + fn set_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()> { + self.arch_set_regs(arch_cpu, regs_index) + } + + fn reset_vcpu(&self, cpu: Arc) -> Result<()> { + self.arch_reset_vcpu(cpu)?; + + Ok(()) + } } diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor_refactor/src/kvm/x86_64/mod.rs index f7894c0d3..0cb1fe3a8 100644 --- a/hypervisor_refactor/src/kvm/x86_64/mod.rs +++ b/hypervisor_refactor/src/kvm/x86_64/mod.rs @@ -12,7 +12,7 @@ use std::sync::{Arc, Mutex}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use kvm_bindings::*; use kvm_ioctls::Kvm; use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; @@ -21,6 +21,7 @@ use crate::kvm::listener::KvmIoListener; use crate::kvm::{KvmCpu, KvmHypervisor}; use crate::HypervisorError; use address_space::Listener; +use cpu::{ArchCPU, CPUBootConfig, CPUCaps, RegsIndex, CPU}; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); @@ -79,4 +80,210 @@ impl KvmCpu { pub fn arch_init_pmu(&self) -> Result<()> { Ok(()) } + + pub fn arch_vcpu_init(&self) -> Result<()> { + Ok(()) + } + + pub fn arch_set_boot_config( + &self, + arch_cpu: Arc>, + boot_config: &CPUBootConfig, + ) -> Result<()> { + let mut locked_arch_cpu = arch_cpu.lock().unwrap(); + let apic_id = locked_arch_cpu.apic_id; + let lapic = self + .fd + .get_lapic() + .with_context(|| format!("Failed to get lapic for CPU {}/KVM", apic_id))?; + locked_arch_cpu.setup_lapic(lapic)?; + locked_arch_cpu.setup_regs(boot_config); + let sregs = self + .fd + .get_sregs() + .with_context(|| format!("Failed to get sregs for CPU {}/KVM", apic_id))?; + locked_arch_cpu.setup_sregs(sregs, boot_config)?; + locked_arch_cpu.setup_fpu(); + locked_arch_cpu.setup_msrs(); + + Ok(()) + } + + pub fn arch_get_one_reg(&self, _reg_id: u64) -> Result { + Ok(0) + } + + pub fn arch_get_regs( + &self, + arch_cpu: Arc>, + regs_index: RegsIndex, + caps: &CPUCaps, + ) -> Result<()> { + let mut msr_entries = caps.create_msr_entries()?; + let mut locked_arch_cpu = arch_cpu.lock().unwrap(); + match regs_index { + RegsIndex::Regs => { + locked_arch_cpu.regs = self.fd.get_regs()?; + } + RegsIndex::Sregs => { + locked_arch_cpu.sregs = self.fd.get_sregs()?; + } + RegsIndex::Fpu => { + locked_arch_cpu.fpu = self.fd.get_fpu()?; + } + RegsIndex::MpState => { + locked_arch_cpu.mp_state = self.fd.get_mp_state()?; + } + RegsIndex::LapicState => { + locked_arch_cpu.lapic = self.fd.get_lapic()?; + } + RegsIndex::MsrEntry => { + locked_arch_cpu.msr_len = self.fd.get_msrs(&mut msr_entries)?; + for (i, entry) in msr_entries.as_slice().iter().enumerate() { + locked_arch_cpu.msr_list[i] = *entry; + } + } + RegsIndex::VcpuEvents => { + locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; + } + RegsIndex::Xsave => { + locked_arch_cpu.xsave = self.fd.get_xsave()?; + } + RegsIndex::Xcrs => { + locked_arch_cpu.xcrs = self.fd.get_xcrs()?; + } + RegsIndex::DebugRegs => { + locked_arch_cpu.debugregs = self.fd.get_debug_regs()?; + } + } + + Ok(()) + } + + pub fn arch_set_regs( + &self, + arch_cpu: Arc>, + regs_index: RegsIndex, + ) -> Result<()> { + let locked_arch_cpu = arch_cpu.lock().unwrap(); + let apic_id = locked_arch_cpu.apic_id; + match regs_index { + RegsIndex::Regs => { + self.fd + .set_regs(&locked_arch_cpu.regs) + .with_context(|| format!("Failed to set regs for CPU {}", apic_id))?; + } + RegsIndex::Sregs => { + self.fd + .set_sregs(&locked_arch_cpu.sregs) + .with_context(|| format!("Failed to set sregs for CPU {}", apic_id))?; + } + RegsIndex::Fpu => { + self.fd + .set_fpu(&locked_arch_cpu.fpu) + .with_context(|| format!("Failed to set fpu for CPU {}", apic_id))?; + } + RegsIndex::MpState => { + self.fd + .set_mp_state(locked_arch_cpu.mp_state) + .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + } + RegsIndex::LapicState => { + self.fd + .set_lapic(&locked_arch_cpu.lapic) + .with_context(|| format!("Failed to set lapic for CPU {}", apic_id))?; + } + RegsIndex::MsrEntry => { + self.fd + .set_msrs(&Msrs::from_entries( + &locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], + )?) + .with_context(|| format!("Failed to set msrs for CPU {}", apic_id))?; + } + RegsIndex::VcpuEvents => { + self.fd + .set_vcpu_events(&locked_arch_cpu.cpu_events) + .with_context(|| format!("Failed to set vcpu events for CPU {}", apic_id))?; + } + RegsIndex::Xsave => { + self.fd + .set_xsave(&locked_arch_cpu.xsave) + .with_context(|| format!("Failed to set xsave for CPU {}", apic_id))?; + } + RegsIndex::Xcrs => { + self.fd + .set_xcrs(&locked_arch_cpu.xcrs) + .with_context(|| format!("Failed to set xcrs for CPU {}", apic_id))?; + } + RegsIndex::DebugRegs => { + self.fd + .set_debug_regs(&locked_arch_cpu.debugregs) + .with_context(|| format!("Failed to set debug register for CPU {}", apic_id))?; + } + } + + Ok(()) + } + + pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { + let caps = &cpu.caps; + let locked_arch_cpu = cpu.arch_cpu.lock().unwrap(); + let apic_id = locked_arch_cpu.apic_id; + + let sys_fd = match Kvm::new() { + Ok(fd) => fd, + _ => bail!("setup_cpuid: Open /dev/kvm failed"), + }; + let mut cpuid = sys_fd + .get_supported_cpuid(KVM_MAX_CPUID_ENTRIES) + .with_context(|| format!("Failed to get supported cpuid for CPU {}/KVM", apic_id))?; + + locked_arch_cpu + .setup_cpuid(&mut cpuid) + .with_context(|| format!("Failed to set cpuid for CPU {}", apic_id))?; + + self.fd + .set_cpuid2(&cpuid) + .with_context(|| format!("Failed to set cpuid for CPU {}/KVM", apic_id))?; + + self.fd + .set_mp_state(locked_arch_cpu.mp_state) + .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + self.fd + .set_sregs(&locked_arch_cpu.sregs) + .with_context(|| format!("Failed to set sregs for CPU {}", apic_id))?; + self.fd + .set_regs(&locked_arch_cpu.regs) + .with_context(|| format!("Failed to set regs for CPU {}", apic_id))?; + if caps.has_xsave { + self.fd + .set_xsave(&locked_arch_cpu.xsave) + .with_context(|| format!("Failed to set xsave for CPU {}", apic_id))?; + } else { + self.fd + .set_fpu(&locked_arch_cpu.fpu) + .with_context(|| format!("Failed to set fpu for CPU {}", apic_id))?; + } + if caps.has_xcrs { + self.fd + .set_xcrs(&locked_arch_cpu.xcrs) + .with_context(|| format!("Failed to set xcrs for CPU {}", apic_id))?; + } + self.fd + .set_debug_regs(&locked_arch_cpu.debugregs) + .with_context(|| format!("Failed to set debug register for CPU {}", apic_id))?; + self.fd + .set_lapic(&locked_arch_cpu.lapic) + .with_context(|| format!("Failed to set lapic for CPU {}", apic_id))?; + self.fd + .set_msrs(&Msrs::from_entries( + &locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], + )?) + .with_context(|| format!("Failed to set msrs for CPU {}", apic_id))?; + self.fd + .set_vcpu_events(&locked_arch_cpu.cpu_events) + .with_context(|| format!("Failed to set vcpu events for CPU {}", apic_id))?; + + Ok(()) + } } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 6c5a3349e..9a1a8ed70 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -212,8 +212,8 @@ impl StdMachine { if cpu_index == 0 { fdt_addr = cpu.arch().lock().unwrap().core_regs().regs.regs[0]; } - cpu.fd() - .vcpu_init(&cpu.arch().lock().unwrap().kvi()) + cpu.hypervisor_cpu() + .vcpu_init() .with_context(|| "Failed to init vcpu fd")?; } -- Gitee From d53f015699aff47ee34996691a61cdf37678dd03 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 23 Jan 2024 11:01:35 +0800 Subject: [PATCH 1573/2187] CPU: Refactor the running method of the vCPU thread Due to different hypervisors having their own thread initialization and execution process, the thread execution method for vCPU has been abstracted into the CPUHypervisorOps interface. This allows hypervisors to define their own thread execution process. Signed-off-by: Jinhao Gao --- Cargo.lock | 1 + cpu/src/error.rs | 2 +- cpu/src/lib.rs | 406 ++--------------------- cpu/src/x86_64/mod.rs | 106 ------ hypervisor_refactor/Cargo.toml | 1 + hypervisor_refactor/src/kvm/mod.rs | 505 ++++++++++++++++++++++++++++- 6 files changed, 539 insertions(+), 482 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df3283b22..5321b550f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -743,6 +743,7 @@ dependencies = [ "anyhow", "cpu", "devices", + "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", diff --git a/cpu/src/error.rs b/cpu/src/error.rs index e93ff6e24..21187e582 100644 --- a/cpu/src/error.rs +++ b/cpu/src/error.rs @@ -37,7 +37,7 @@ pub enum CpuError { #[error("CPU {0}/KVM received an unexpected exit reason: {1}!")] VcpuExitReason(u8, String), #[error("CPU {0}/KVM received an unhandled kvm exit event: error {1}!")] - UnhandledKvmExit(u8, i32), + UnhandledHypervisorExit(u8, i32), #[error("Vcpu not present in local thread.")] VcpuLocalThreadNotPresent, #[error("No Machine Interface saved in CPU")] diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index c163d73f6..1e3798327 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -68,35 +68,32 @@ pub use x86_64::X86CPUTopology as CPUTopology; pub use x86_64::X86RegsIndex as RegsIndex; use std::cell::RefCell; -use std::sync::atomic::{fence, AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; use std::time::Duration; use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::{Cap, VcpuExit, VcpuFd}; -use libc::{c_int, c_void, siginfo_t}; +use kvm_ioctls::{Cap, VcpuFd}; use log::{error, info, warn}; use nix::unistd::gettid; -use vmm_sys_util::signal::{register_signal_handler, Killable}; +use vmm_sys_util::signal::Killable; use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; use machine_manager::machine::{HypervisorType, MachineInterface}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; -#[cfg(not(test))] -use util::test_helper::is_test_enabled; // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal // number should be assigned to SIGRTMIN + n, (n = 0...30). #[cfg(not(target_env = "musl"))] -const VCPU_TASK_SIGNAL: i32 = 34; +pub const VCPU_TASK_SIGNAL: i32 = 34; #[cfg(target_env = "musl")] -const VCPU_TASK_SIGNAL: i32 = 35; +pub const VCPU_TASK_SIGNAL: i32 = 35; #[cfg(not(target_env = "musl"))] -const VCPU_RESET_SIGNAL: i32 = 35; +pub const VCPU_RESET_SIGNAL: i32 = 35; #[cfg(target_env = "musl")] -const VCPU_RESET_SIGNAL: i32 = 36; +pub const VCPU_RESET_SIGNAL: i32 = 36; /// Watch `0x3ff` IO port to record the magic value trapped from guest kernel. #[cfg(all(target_arch = "x86_64", feature = "boot_time"))] @@ -168,9 +165,6 @@ pub trait CPUInterface { /// Make `CPU` destroy because of guest inner reset. fn guest_reset(&self) -> Result<()>; - - /// Handle vcpu event from `kvm`. - fn kvm_vcpu_exec(&self) -> Result; } pub trait CPUHypervisorOps: Send + Sync { @@ -203,19 +197,27 @@ pub trait CPUHypervisorOps: Send + Sync { fn set_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()>; fn reset_vcpu(&self, cpu: Arc) -> Result<()>; + + fn vcpu_exec( + &self, + cpu_thread_worker: CPUThreadWorker, + thread_barrier: Arc, + ) -> Result<()>; + + fn kick(&self) -> Result<()>; } /// `CPU` is a wrapper around creating and using a kvm-based VCPU. #[allow(clippy::upper_case_acronyms)] pub struct CPU { /// ID of this virtual CPU, `0` means this cpu is primary `CPU`. - id: u8, + pub id: u8, /// The file descriptor of this kvm-based VCPU. fd: Arc, /// Architecture special CPU property. pub arch_cpu: Arc>, /// LifeCycle state of kvm-based VCPU. - state: Arc<(Mutex, Condvar)>, + pub state: Arc<(Mutex, Condvar)>, /// The thread handler of this virtual CPU. task: Arc>>>, /// The thread tid of this VCPU. @@ -300,7 +302,7 @@ impl CPU { } /// Set thread id for `CPU`. - fn set_tid(&self) { + pub fn set_tid(&self) { *self.tid.lock().unwrap() = Some(gettid().as_raw() as u64); } @@ -308,6 +310,14 @@ impl CPU { pub fn hypervisor_cpu(&self) -> &Arc { &self.hypervisor_cpu } + + pub fn vm(&self) -> Weak> { + self.vm.clone() + } + + pub fn pause_signal(&self) -> Arc { + self.pause_signal.clone() + } } impl CPUInterface for CPU { @@ -377,25 +387,19 @@ impl CPUInterface for CPU { } let local_cpu = cpu.clone(); + let cpu_id = cpu.id(); let hypervisor_cpu = cpu.hypervisor_cpu().clone(); let hyp_type = hypervisor_cpu.get_hypervisor_type(); let cpu_thread_worker = CPUThreadWorker::new(cpu); let handle = thread::Builder::new() .name(format!("CPU {}/{:?}", local_cpu.id, hyp_type)) .spawn(move || { - if let Err(e) = cpu_thread_worker.handle(thread_barrier) { - error!( - "Some error occurred in cpu{} thread: {:?}", - cpu_thread_worker.thread_cpu.id, e - ); + if let Err(e) = hypervisor_cpu.vcpu_exec(cpu_thread_worker, thread_barrier) { + error!("Some error occurred in cpu{} thread: {:?}", cpu_id, e); } }) .with_context(|| { - format!( - "Failed to create thread for CPU {}/{:?}", - local_cpu.id(), - hyp_type - ) + format!("Failed to create thread for CPU {}/{:?}", cpu_id, hyp_type) })?; local_cpu.set_task(Some(handle)); Ok(()) @@ -526,113 +530,12 @@ impl CPUInterface for CPU { Ok(()) } - - fn kvm_vcpu_exec(&self) -> Result { - let vm = self - .vm - .upgrade() - .with_context(|| CpuError::NoMachineInterface)?; - - match self.fd.run() { - Ok(run) => match run { - #[cfg(target_arch = "x86_64")] - VcpuExit::IoIn(addr, data) => { - vm.lock().unwrap().pio_in(u64::from(addr), data); - } - #[cfg(target_arch = "x86_64")] - VcpuExit::IoOut(addr, data) => { - #[cfg(feature = "boot_time")] - capture_boot_signal(addr as u64, data); - - vm.lock().unwrap().pio_out(u64::from(addr), data); - } - VcpuExit::MmioRead(addr, data) => { - vm.lock().unwrap().mmio_read(addr, data); - } - VcpuExit::MmioWrite(addr, data) => { - #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] - capture_boot_signal(addr, data); - - vm.lock().unwrap().mmio_write(addr, data); - } - #[cfg(target_arch = "x86_64")] - VcpuExit::Hlt => { - info!("Vcpu{} received KVM_EXIT_HLT signal", self.id()); - return Err(anyhow!(CpuError::VcpuHltEvent(self.id()))); - } - #[cfg(target_arch = "x86_64")] - VcpuExit::Shutdown => { - info!("Vcpu{} received an KVM_EXIT_SHUTDOWN signal", self.id()); - self.guest_shutdown()?; - - return Ok(false); - } - #[cfg(target_arch = "aarch64")] - VcpuExit::SystemEvent(event, flags) => { - if event == kvm_bindings::KVM_SYSTEM_EVENT_SHUTDOWN { - info!( - "Vcpu{} received an KVM_SYSTEM_EVENT_SHUTDOWN signal", - self.id() - ); - self.guest_shutdown() - .with_context(|| "Some error occurred in guest shutdown")?; - return Ok(true); - } else if event == kvm_bindings::KVM_SYSTEM_EVENT_RESET { - info!( - "Vcpu{} received an KVM_SYSTEM_EVENT_RESET signal", - self.id() - ); - self.guest_reset() - .with_context(|| "Some error occurred in guest reset")?; - return Ok(true); - } else { - error!( - "Vcpu{} received unexpected system event with type 0x{:x}, flags 0x{:x}", - self.id(), - event, - flags - ); - } - return Ok(false); - } - VcpuExit::FailEntry(reason, cpuid) => { - info!( - "Vcpu{} received KVM_EXIT_FAIL_ENTRY signal. the vcpu could not be run due to unknown reasons({})", - cpuid, reason - ); - return Ok(false); - } - VcpuExit::InternalError => { - info!("Vcpu{} received KVM_EXIT_INTERNAL_ERROR signal", self.id()); - return Ok(false); - } - r => { - return Err(anyhow!(CpuError::VcpuExitReason( - self.id(), - format!("{:?}", r) - ))); - } - }, - Err(ref e) => { - match e.errno() { - libc::EAGAIN => {} - libc::EINTR => { - self.fd.set_kvm_immediate_exit(0); - } - _ => { - return Err(anyhow!(CpuError::UnhandledKvmExit(self.id(), e.errno()))); - } - }; - } - } - Ok(true) - } } /// The struct to handle events in cpu thread. #[allow(clippy::upper_case_acronyms)] -struct CPUThreadWorker { - thread_cpu: Arc, +pub struct CPUThreadWorker { + pub thread_cpu: Arc, } impl CPUThreadWorker { @@ -644,7 +547,7 @@ impl CPUThreadWorker { } /// Init vcpu thread static variable. - fn init_local_thread_vcpu(&self) { + pub fn init_local_thread_vcpu(&self) { Self::LOCAL_THREAD_VCPU.with(|thread_vcpu| { *thread_vcpu.borrow_mut() = Some(CPUThreadWorker { thread_cpu: self.thread_cpu.clone(), @@ -652,7 +555,7 @@ impl CPUThreadWorker { }) } - fn run_on_local_thread_vcpu(func: F) -> Result<()> + pub fn run_on_local_thread_vcpu(func: F) -> Result<()> where F: FnOnce(Arc), { @@ -666,40 +569,8 @@ impl CPUThreadWorker { }) } - /// Init signal for `CPU` event. - fn init_signals() -> Result<()> { - extern "C" fn handle_signal(signum: c_int, _: *mut siginfo_t, _: *mut c_void) { - match signum { - VCPU_TASK_SIGNAL => { - let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - vcpu.fd().set_kvm_immediate_exit(1); - // Setting pause_signal to be `true` if kvm changes vCPU to pause state. - vcpu.pause_signal.store(true, Ordering::SeqCst); - fence(Ordering::Release) - }); - } - VCPU_RESET_SIGNAL => { - let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - let cpu = vcpu.clone(); - if let Err(e) = vcpu.hypervisor_cpu().reset_vcpu(cpu) { - error!("Failed to reset vcpu state: {:?}", e) - } - }); - } - _ => {} - } - } - - register_signal_handler(VCPU_TASK_SIGNAL, handle_signal) - .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; - register_signal_handler(VCPU_RESET_SIGNAL, handle_signal) - .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; - - Ok(()) - } - /// Judge whether the kvm vcpu is ready to emulate. - fn ready_for_running(&self) -> Result { + pub fn ready_for_running(&self) -> Result { let mut flag = 0_u32; let (cpu_state_locked, cvar) = &*self.thread_cpu.state; let mut cpu_state = cpu_state_locked.lock().unwrap(); @@ -727,59 +598,6 @@ impl CPUThreadWorker { } } } - - /// Handle the all events in vcpu thread. - fn handle(&self, thread_barrier: Arc) -> Result<()> { - self.init_local_thread_vcpu(); - if let Err(e) = Self::init_signals() { - error!("Failed to init cpu{} signal:{:?}", self.thread_cpu.id, e); - } - - self.thread_cpu.set_tid(); - - // The vcpu thread is going to run, - // reset its running environment. - let cpu = self.thread_cpu.clone(); - #[cfg(not(test))] - self.thread_cpu - .hypervisor_cpu - .reset_vcpu(cpu) - .with_context(|| "Failed to reset for cpu register state")?; - - // Wait for all vcpu to complete the running - // environment initialization. - thread_barrier.wait(); - - info!("vcpu{} start running", self.thread_cpu.id); - while let Ok(true) = self.ready_for_running() { - #[cfg(not(test))] - { - if is_test_enabled() { - thread::sleep(Duration::from_millis(5)); - continue; - } - if !self - .thread_cpu - .kvm_vcpu_exec() - .with_context(|| format!("VCPU {}/KVM emulate error!", self.thread_cpu.id()))? - { - break; - } - } - #[cfg(test)] - { - thread::sleep(Duration::from_millis(5)); - } - } - - // The vcpu thread is about to exit, marking the state - // of the CPU state as Stopped. - let (cpu_state, cvar) = &*self.thread_cpu.state; - *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; - cvar.notify_one(); - - Ok(()) - } } /// The wrapper for topology for VCPU. @@ -884,7 +702,7 @@ impl CpuTopology { /// Capture the boot signal that trap from guest kernel, and then record /// kernel boot timestamp. #[cfg(feature = "boot_time")] -fn capture_boot_signal(addr: u64, data: &[u8]) { +pub fn capture_boot_signal(addr: u64, data: &[u8]) { if addr == MAGIC_SIGNAL_GUEST_BOOT { if data[0] == MAGIC_VALUE_SIGNAL_GUEST_BOOT_START { info!("Kernel starts to boot!"); @@ -897,160 +715,8 @@ fn capture_boot_signal(addr: u64, data: &[u8]) { #[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; - use std::time::Duration; use super::*; - use hypervisor::kvm::{KVMFds, KVM_FDS}; - use machine_manager::machine::{ - MachineAddressInterface, MachineInterface, MachineLifecycle, VmState, - }; - - struct TestVm { - #[cfg(target_arch = "x86_64")] - pio_in: Arc)>>>, - #[cfg(target_arch = "x86_64")] - pio_out: Arc)>>>, - mmio_read: Arc)>>>, - mmio_write: Arc)>>>, - } - - impl TestVm { - fn new() -> Self { - TestVm { - #[cfg(target_arch = "x86_64")] - pio_in: Arc::new(Mutex::new(Vec::new())), - #[cfg(target_arch = "x86_64")] - pio_out: Arc::new(Mutex::new(Vec::new())), - mmio_read: Arc::new(Mutex::new(Vec::new())), - mmio_write: Arc::new(Mutex::new(Vec::new())), - } - } - } - - impl MachineLifecycle for TestVm { - fn notify_lifecycle(&self, _old: VmState, _new: VmState) -> bool { - true - } - } - - impl MachineAddressInterface for TestVm { - #[cfg(target_arch = "x86_64")] - fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { - self.pio_in.lock().unwrap().push((addr, data.to_vec())); - true - } - - #[cfg(target_arch = "x86_64")] - fn pio_out(&self, addr: u64, data: &[u8]) -> bool { - self.pio_out.lock().unwrap().push((addr, data.to_vec())); - true - } - - fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { - #[cfg(target_arch = "aarch64")] - { - data[3] = 0x0; - data[2] = 0x0; - data[1] = 0x5; - data[0] = 0x6; - } - self.mmio_read.lock().unwrap().push((addr, data.to_vec())); - true - } - - fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { - self.mmio_write.lock().unwrap().push((addr, data.to_vec())); - true - } - } - - impl MachineInterface for TestVm {} - - #[test] - #[allow(unused)] - fn test_cpu_lifecycle() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let vm = Arc::new(Mutex::new(TestVm::new())); - let cpu = CPU::new( - Arc::new( - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(0) - .unwrap(), - ), - 0, - Arc::new(Mutex::new(ArchCPU::default())), - vm.clone(), - ); - let (cpu_state, _) = &*cpu.state; - assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Created); - drop(cpu_state); - - let cpus_thread_barrier = Arc::new(Barrier::new(2)); - let cpu_thread_barrier = cpus_thread_barrier.clone(); - - #[cfg(target_arch = "aarch64")] - { - let mut kvi = kvm_bindings::kvm_vcpu_init::default(); - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .get_preferred_target(&mut kvi) - .unwrap(); - kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2; - cpu.fd.vcpu_init(&kvi).unwrap(); - } - - // Test cpu life cycle as: - // Created -> Paused -> Running -> Paused -> Running -> Destroy - let cpu_arc = Arc::new(cpu); - CPU::start(cpu_arc.clone(), cpu_thread_barrier, true).unwrap(); - - // Wait for CPU thread init signal hook - std::thread::sleep(Duration::from_millis(50)); - cpus_thread_barrier.wait(); - let (cpu_state, _) = &*cpu_arc.state; - assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Paused); - drop(cpu_state); - - assert!(cpu_arc.resume().is_ok()); - - // Wait for CPU finish state change. - std::thread::sleep(Duration::from_millis(50)); - let (cpu_state, _) = &*cpu_arc.state; - assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Running); - drop(cpu_state); - - assert!(cpu_arc.pause().is_ok()); - - // Wait for CPU finish state change. - std::thread::sleep(Duration::from_millis(50)); - let (cpu_state, _) = &*cpu_arc.state; - assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Paused); - drop(cpu_state); - - assert!(cpu_arc.resume().is_ok()); - // Wait for CPU finish state change. - std::thread::sleep(Duration::from_millis(50)); - - assert!(cpu_arc.destroy().is_ok()); - - // Wait for CPU finish state change. - std::thread::sleep(Duration::from_millis(50)); - let (cpu_state, _) = &*cpu_arc.state; - assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Stopped); - drop(cpu_state); - } #[test] fn test_cpu_get_topu() { diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 8a48b508d..1a349f2ed 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -534,109 +534,3 @@ impl StateTransfer for CPU { } impl MigrationHook for CPU {} - -#[cfg(test)] -mod test { - use super::*; - use hypervisor::kvm::{KVMFds, KVM_FDS}; - use kvm_bindings::Segment; - use std::sync::Arc; - - #[test] - fn test_x86_64_cpu() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - KVM_FDS.store(Arc::new(kvm_fds)); - - let code_seg = Segment { - base: 0, - limit: 1048575, - selector: 16, - type_: 11, - present: 1, - dpl: 0, - db: 0, - s: 1, - l: 1, - g: 1, - avl: 0, - unusable: 0, - padding: 0, - }; - let data_seg = Segment { - base: 0, - limit: 1048575, - selector: 24, - type_: 3, - present: 1, - dpl: 0, - db: 1, - s: 1, - l: 0, - g: 1, - avl: 0, - unusable: 0, - padding: 0, - }; - let cpu_config = X86CPUBootConfig { - prot64_mode: true, - boot_ip: 0, - boot_sp: 0, - boot_selector: 0, - zero_page: 0x0000_7000, - code_segment: code_seg, - data_segment: data_seg, - gdt_base: 0x500u64, - gdt_size: 16, - idt_base: 0x520u64, - idt_size: 8, - pml4_start: 0x0000_9000, - }; - - // For `get_lapic` in realize function to work, - // you need to create a irq_chip for VM before creating the VCPU. - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); - vm_fd.create_irq_chip().unwrap(); - let vcpu = Arc::new(vm_fd.create_vcpu(0).unwrap()); - let mut x86_cpu = X86CPUState::new(0, 1); - // test `set_boot_config` function - assert!(x86_cpu.set_boot_config(&vcpu, &cpu_config).is_ok()); - - // test setup special registers - let cpu_caps = caps::X86CPUCaps::init_capabilities(); - assert!(x86_cpu.reset_vcpu(&vcpu, &cpu_caps).is_ok()); - let x86_sregs = vcpu.get_sregs().unwrap(); - assert_eq!(x86_sregs.cs, code_seg); - assert_eq!(x86_sregs.ds, data_seg); - assert_eq!(x86_sregs.es, data_seg); - assert_eq!(x86_sregs.fs, data_seg); - assert_eq!(x86_sregs.gs, data_seg); - assert_eq!(x86_sregs.ss, data_seg); - assert_eq!(x86_sregs.gdt.base, cpu_config.gdt_base); - assert_eq!(x86_sregs.gdt.limit, cpu_config.gdt_size); - assert_eq!(x86_sregs.idt.base, cpu_config.idt_base); - assert_eq!(x86_sregs.idt.limit, cpu_config.idt_size); - assert_eq!(x86_sregs.cr0 & 0x1, 1); - assert_eq!((x86_sregs.cr0 & 0x8000_0000) >> 31, 1); - assert_eq!(x86_sregs.cr3, cpu_config.pml4_start); - assert_eq!((x86_sregs.cr4 & 0x20) >> 5, 1); - assert_eq!((x86_sregs.efer & 0x700) >> 8, 5); - - // test setup_regs function - let x86_regs = vcpu.get_regs().unwrap(); - assert_eq!(x86_regs.rflags, 0x0002); - assert_eq!(x86_regs.rip, 0); - assert_eq!(x86_regs.rsp, 0); - assert_eq!(x86_regs.rbp, 0); - assert_eq!(x86_regs.rsi, 0x0000_7000); - - // test setup_fpu function - if !cpu_caps.has_xsave { - let x86_fpu = vcpu.get_fpu().unwrap(); - assert_eq!(x86_fpu.fcw, 0x37f); - } - } -} diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor_refactor/Cargo.toml index 4caf3147e..0439d1494 100644 --- a/hypervisor_refactor/Cargo.toml +++ b/hypervisor_refactor/Cargo.toml @@ -16,6 +16,7 @@ vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } cpu = { path = "../cpu" } devices = { path = "../devices" } +hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index 25a8df945..f42f270d1 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -26,22 +26,35 @@ pub use aarch64::gicv2::KvmGICv2; pub use aarch64::gicv3::{KvmGICv3, KvmGICv3Its}; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::atomic::{fence, Ordering}; +use std::sync::{Arc, Barrier, Mutex}; +use std::thread; +use std::time::Duration; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; -use kvm_ioctls::{Cap, Kvm, VcpuFd, VmFd}; -use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; +use kvm_ioctls::{Cap, Kvm, VcpuExit, VcpuFd, VmFd}; +use libc::{c_int, c_void, siginfo_t}; +use log::{error, info}; +use vmm_sys_util::{ + ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, + signal::register_signal_handler, +}; use self::listener::KvmMemoryListener; use super::HypervisorOps; #[cfg(target_arch = "x86_64")] use crate::HypervisorError; use address_space::{AddressSpace, Listener}; +#[cfg(feature = "boot_time")] +use cpu::capture_boot_signal; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; -use cpu::{ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, RegsIndex, CPU}; +use cpu::{ + ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuError, + CpuLifecycleState, RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, +}; #[cfg(target_arch = "aarch64")] use devices::{ GICVersion, GICv2, GICv3, GICv3ItsState, GICv3State, ICGICConfig, InterruptController, @@ -57,6 +70,7 @@ use migration::{ MigrationManager, }; use migration::{MigrateMemSlot, MigrateOps}; +use util::test_helper::is_test_enabled; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; @@ -328,6 +342,138 @@ impl KvmCpu { kvi: Mutex::new(kvm_vcpu_init::default()), } } + + /// Init signal for `CPU` event. + fn init_signals(&self) -> Result<()> { + extern "C" fn handle_signal(signum: c_int, _: *mut siginfo_t, _: *mut c_void) { + match signum { + VCPU_TASK_SIGNAL => { + let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { + vcpu.hypervisor_cpu().kick().unwrap(); + // Setting pause_signal to be `true` if kvm changes vCPU to pause state. + vcpu.pause_signal().store(true, Ordering::SeqCst); + fence(Ordering::Release) + }); + } + VCPU_RESET_SIGNAL => { + let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { + if let Err(e) = vcpu.hypervisor_cpu().reset_vcpu(vcpu.clone()) { + error!("Failed to reset vcpu state: {:?}", e) + } + }); + } + _ => {} + } + } + + register_signal_handler(VCPU_TASK_SIGNAL, handle_signal) + .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; + register_signal_handler(VCPU_RESET_SIGNAL, handle_signal) + .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; + + Ok(()) + } + + fn kvm_vcpu_exec(&self, cpu: Arc) -> Result { + let vm = cpu + .vm() + .upgrade() + .with_context(|| CpuError::NoMachineInterface)?; + + match self.fd.run() { + Ok(run) => match run { + #[cfg(target_arch = "x86_64")] + VcpuExit::IoIn(addr, data) => { + vm.lock().unwrap().pio_in(u64::from(addr), data); + } + #[cfg(target_arch = "x86_64")] + VcpuExit::IoOut(addr, data) => { + #[cfg(feature = "boot_time")] + capture_boot_signal(addr as u64, data); + + vm.lock().unwrap().pio_out(u64::from(addr), data); + } + VcpuExit::MmioRead(addr, data) => { + vm.lock().unwrap().mmio_read(addr, data); + } + VcpuExit::MmioWrite(addr, data) => { + #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] + capture_boot_signal(addr, data); + + vm.lock().unwrap().mmio_write(addr, data); + } + #[cfg(target_arch = "x86_64")] + VcpuExit::Hlt => { + info!("Vcpu{} received KVM_EXIT_HLT signal", cpu.id); + return Err(anyhow!(CpuError::VcpuHltEvent(cpu.id))); + } + #[cfg(target_arch = "x86_64")] + VcpuExit::Shutdown => { + info!("Vcpu{} received an KVM_EXIT_SHUTDOWN signal", cpu.id); + cpu.guest_shutdown()?; + + return Ok(false); + } + #[cfg(target_arch = "aarch64")] + VcpuExit::SystemEvent(event, flags) => { + if event == kvm_bindings::KVM_SYSTEM_EVENT_SHUTDOWN { + info!( + "Vcpu{} received an KVM_SYSTEM_EVENT_SHUTDOWN signal", + cpu.id() + ); + cpu.guest_shutdown() + .with_context(|| "Some error occurred in guest shutdown")?; + return Ok(true); + } else if event == kvm_bindings::KVM_SYSTEM_EVENT_RESET { + info!("Vcpu{} received an KVM_SYSTEM_EVENT_RESET signal", cpu.id()); + cpu.guest_reset() + .with_context(|| "Some error occurred in guest reset")?; + return Ok(true); + } else { + error!( + "Vcpu{} received unexpected system event with type 0x{:x}, flags 0x{:x}", + cpu.id(), + event, + flags + ); + } + return Ok(false); + } + VcpuExit::FailEntry(reason, cpuid) => { + info!( + "Vcpu{} received KVM_EXIT_FAIL_ENTRY signal. the vcpu could not be run due to unknown reasons({})", + cpuid, reason + ); + return Ok(false); + } + VcpuExit::InternalError => { + info!("Vcpu{} received KVM_EXIT_INTERNAL_ERROR signal", cpu.id()); + return Ok(false); + } + r => { + return Err(anyhow!(CpuError::VcpuExitReason( + cpu.id(), + format!("{:?}", r) + ))); + } + }, + Err(ref e) => { + match e.errno() { + libc::EAGAIN => {} + libc::EINTR => { + self.fd.set_kvm_immediate_exit(0); + } + _ => { + return Err(anyhow!(CpuError::UnhandledHypervisorExit( + cpu.id(), + e.errno() + ))); + } + }; + } + } + Ok(true) + } } impl CPUHypervisorOps for KvmCpu { @@ -386,4 +532,353 @@ impl CPUHypervisorOps for KvmCpu { Ok(()) } + + fn vcpu_exec( + &self, + cpu_thread_worker: CPUThreadWorker, + thread_barrier: Arc, + ) -> Result<()> { + cpu_thread_worker.init_local_thread_vcpu(); + if let Err(e) = self.init_signals() { + error!( + "Failed to init cpu{} signal:{:?}", + cpu_thread_worker.thread_cpu.id, e + ); + } + + cpu_thread_worker.thread_cpu.set_tid(); + + #[cfg(not(test))] + self.reset_vcpu(cpu_thread_worker.thread_cpu.clone())?; + + // Wait for all vcpu to complete the running + // environment initialization. + thread_barrier.wait(); + + info!("vcpu{} start running", cpu_thread_worker.thread_cpu.id); + while let Ok(true) = cpu_thread_worker.ready_for_running() { + #[cfg(not(test))] + { + if is_test_enabled() { + thread::sleep(Duration::from_millis(5)); + continue; + } + if !self + .kvm_vcpu_exec(cpu_thread_worker.thread_cpu.clone()) + .with_context(|| { + format!( + "VCPU {}/KVM emulate error!", + cpu_thread_worker.thread_cpu.id() + ) + })? + { + break; + } + } + #[cfg(test)] + { + thread::sleep(Duration::from_millis(5)); + } + } + + // The vcpu thread is about to exit, marking the state + // of the CPU state as Stopped. + let (cpu_state, cvar) = &*cpu_thread_worker.thread_cpu.state; + *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; + cvar.notify_one(); + + Ok(()) + } + + fn kick(&self) -> Result<()> { + self.fd.set_kvm_immediate_exit(1); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use std::sync::{Arc, Mutex}; + use std::time::Duration; + + #[cfg(target_arch = "x86_64")] + use kvm_bindings::kvm_segment; + + #[cfg(target_arch = "x86_64")] + use cpu::{ArchCPU, CPUBootConfig, CPUCaps}; + use hypervisor::kvm::KVM_FDS; + use machine_manager::machine::{ + MachineAddressInterface, MachineInterface, MachineLifecycle, VmState, + }; + + use super::*; + + struct TestVm { + #[cfg(target_arch = "x86_64")] + pio_in: Arc)>>>, + #[cfg(target_arch = "x86_64")] + pio_out: Arc)>>>, + mmio_read: Arc)>>>, + mmio_write: Arc)>>>, + } + + impl TestVm { + fn new() -> Self { + TestVm { + #[cfg(target_arch = "x86_64")] + pio_in: Arc::new(Mutex::new(Vec::new())), + #[cfg(target_arch = "x86_64")] + pio_out: Arc::new(Mutex::new(Vec::new())), + mmio_read: Arc::new(Mutex::new(Vec::new())), + mmio_write: Arc::new(Mutex::new(Vec::new())), + } + } + } + + impl MachineLifecycle for TestVm { + fn notify_lifecycle(&self, _old: VmState, _new: VmState) -> bool { + true + } + } + + impl MachineAddressInterface for TestVm { + #[cfg(target_arch = "x86_64")] + fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { + self.pio_in.lock().unwrap().push((addr, data.to_vec())); + true + } + + #[cfg(target_arch = "x86_64")] + fn pio_out(&self, addr: u64, data: &[u8]) -> bool { + self.pio_out.lock().unwrap().push((addr, data.to_vec())); + true + } + + fn mmio_read(&self, addr: u64, data: &mut [u8]) -> bool { + #[cfg(target_arch = "aarch64")] + { + data[3] = 0x0; + data[2] = 0x0; + data[1] = 0x5; + data[0] = 0x6; + } + self.mmio_read.lock().unwrap().push((addr, data.to_vec())); + true + } + + fn mmio_write(&self, addr: u64, data: &[u8]) -> bool { + self.mmio_write.lock().unwrap().push((addr, data.to_vec())); + true + } + } + + impl MachineInterface for TestVm {} + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_x86_64_kvm_cpu() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let vm = Arc::new(Mutex::new(TestVm::new())); + + let code_seg = kvm_segment { + base: 0, + limit: 1048575, + selector: 16, + type_: 11, + present: 1, + dpl: 0, + db: 0, + s: 1, + l: 1, + g: 1, + avl: 0, + unusable: 0, + padding: 0, + }; + let data_seg = kvm_segment { + base: 0, + limit: 1048575, + selector: 24, + type_: 3, + present: 1, + dpl: 0, + db: 1, + s: 1, + l: 0, + g: 1, + avl: 0, + unusable: 0, + padding: 0, + }; + let cpu_config = CPUBootConfig { + prot64_mode: true, + boot_ip: 0, + boot_sp: 0, + boot_selector: 0, + zero_page: 0x0000_7000, + code_segment: code_seg, + data_segment: data_seg, + gdt_base: 0x500u64, + gdt_size: 16, + idt_base: 0x520u64, + idt_size: 8, + pml4_start: 0x0000_9000, + }; + + // For `get_lapic` in realize function to work, + // you need to create a irq_chip for VM before creating the VCPU. + let vm_fd = kvm_hyp.vm_fd.as_ref().unwrap(); + vm_fd.create_irq_chip().unwrap(); + let vcpu_fd = Arc::new( + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_vcpu(0) + .unwrap(), + ); + let hypervisor_cpu = Arc::new(KvmCpu::new( + #[cfg(target_arch = "aarch64")] + kvm_hyp.vm_fd.clone(), + vcpu_fd.clone(), + )); + let x86_cpu = Arc::new(Mutex::new(ArchCPU::new(0, 1))); + let cpu = CPU::new(hypervisor_cpu.clone(), vcpu_fd, 0, x86_cpu, vm.clone()); + // test `set_boot_config` function + assert!(hypervisor_cpu + .set_boot_config(cpu.arch().clone(), &cpu_config) + .is_ok()); + + // test setup special registers + let cpu_caps = CPUCaps::init_capabilities(hypervisor_cpu.clone()); + assert!(hypervisor_cpu.reset_vcpu(Arc::new(cpu)).is_ok()); + let x86_sregs = hypervisor_cpu.fd.get_sregs().unwrap(); + assert_eq!(x86_sregs.cs, code_seg); + assert_eq!(x86_sregs.ds, data_seg); + assert_eq!(x86_sregs.es, data_seg); + assert_eq!(x86_sregs.fs, data_seg); + assert_eq!(x86_sregs.gs, data_seg); + assert_eq!(x86_sregs.ss, data_seg); + assert_eq!(x86_sregs.gdt.base, cpu_config.gdt_base); + assert_eq!(x86_sregs.gdt.limit, cpu_config.gdt_size); + assert_eq!(x86_sregs.idt.base, cpu_config.idt_base); + assert_eq!(x86_sregs.idt.limit, cpu_config.idt_size); + assert_eq!(x86_sregs.cr0 & 0x1, 1); + assert_eq!((x86_sregs.cr0 & 0x8000_0000) >> 31, 1); + assert_eq!(x86_sregs.cr3, cpu_config.pml4_start); + assert_eq!((x86_sregs.cr4 & 0x20) >> 5, 1); + assert_eq!((x86_sregs.efer & 0x700) >> 8, 5); + + // test setup_regs function + let x86_regs = hypervisor_cpu.fd.get_regs().unwrap(); + assert_eq!(x86_regs.rflags, 0x0002); + assert_eq!(x86_regs.rip, 0); + assert_eq!(x86_regs.rsp, 0); + assert_eq!(x86_regs.rbp, 0); + assert_eq!(x86_regs.rsi, 0x0000_7000); + + // test setup_fpu function + if !cpu_caps.has_xsave { + let x86_fpu = hypervisor_cpu.fd.get_fpu().unwrap(); + assert_eq!(x86_fpu.fcw, 0x37f); + } + } + + #[test] + #[allow(unused)] + fn test_cpu_lifecycle_with_kvm() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + + let vcpu_fd = Arc::new( + KVM_FDS + .load() + .vm_fd + .as_ref() + .unwrap() + .create_vcpu(0) + .unwrap(), + ); + let hypervisor_cpu = Arc::new(KvmCpu::new( + #[cfg(target_arch = "aarch64")] + kvm_hyp.vm_fd.clone(), + vcpu_fd.clone(), + )); + + let vm = Arc::new(Mutex::new(TestVm::new())); + let cpu = CPU::new( + hypervisor_cpu.clone(), + vcpu_fd.clone(), + 0, + Arc::new(Mutex::new(ArchCPU::default())), + vm.clone(), + ); + let (cpu_state, _) = &*cpu.state; + assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Created); + drop(cpu_state); + + let cpus_thread_barrier = Arc::new(Barrier::new(2)); + let cpu_thread_barrier = cpus_thread_barrier.clone(); + + #[cfg(target_arch = "aarch64")] + { + let mut kvi = kvm_bindings::kvm_vcpu_init::default(); + kvm_hyp + .vm_fd + .as_ref() + .unwrap() + .get_preferred_target(&mut kvi) + .unwrap(); + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2; + *hypervisor_cpu.kvi.lock().unwrap() = kvi; + hypervisor_cpu.vcpu_init().unwrap(); + } + + // Test cpu life cycle as: + // Created -> Paused -> Running -> Paused -> Running -> Destroy + let cpu_arc = Arc::new(cpu); + CPU::start(cpu_arc.clone(), cpu_thread_barrier, true).unwrap(); + + // Wait for CPU thread init signal hook + std::thread::sleep(Duration::from_millis(50)); + cpus_thread_barrier.wait(); + let (cpu_state, _) = &*cpu_arc.state; + assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Paused); + drop(cpu_state); + + assert!(cpu_arc.resume().is_ok()); + + // Wait for CPU finish state change. + std::thread::sleep(Duration::from_millis(50)); + let (cpu_state, _) = &*cpu_arc.state; + assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Running); + drop(cpu_state); + + assert!(cpu_arc.pause().is_ok()); + + // Wait for CPU finish state change. + std::thread::sleep(Duration::from_millis(50)); + let (cpu_state, _) = &*cpu_arc.state; + assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Paused); + drop(cpu_state); + + assert!(cpu_arc.resume().is_ok()); + // Wait for CPU finish state change. + std::thread::sleep(Duration::from_millis(50)); + + assert!(cpu_arc.destroy().is_ok()); + + // Wait for CPU finish state change. + std::thread::sleep(Duration::from_millis(50)); + let (cpu_state, _) = &*cpu_arc.state; + assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Stopped); + drop(cpu_state); + } } -- Gitee From c1f412dd91fb5d460779a37c231f697799463ca7 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 23 Jan 2024 11:13:45 +0800 Subject: [PATCH 1574/2187] CPU: Delete the vcpu_fd field in CPU The interface for CPU interaction with the hypervisor has been fully abstracted into the CPUHypervisorOps interface. As a result, the vcpu_fd has been removed, successfully decoupling the CPU module from KVM. Signed-off-by: Jinhao Gao --- Cargo.lock | 1 - cpu/src/lib.rs | 11 +------- hypervisor_refactor/Cargo.toml | 1 - hypervisor_refactor/src/kvm/mod.rs | 44 ++++++++++-------------------- hypervisor_refactor/src/lib.rs | 8 ++---- machine/src/aarch64/standard.rs | 4 +-- machine/src/lib.rs | 13 +-------- 7 files changed, 20 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5321b550f..df3283b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -743,7 +743,6 @@ dependencies = [ "anyhow", "cpu", "devices", - "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 1e3798327..30c7e48d4 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -74,7 +74,7 @@ use std::thread; use std::time::Duration; use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::{Cap, VcpuFd}; +use kvm_ioctls::Cap; use log::{error, info, warn}; use nix::unistd::gettid; use vmm_sys_util::signal::Killable; @@ -212,8 +212,6 @@ pub trait CPUHypervisorOps: Send + Sync { pub struct CPU { /// ID of this virtual CPU, `0` means this cpu is primary `CPU`. pub id: u8, - /// The file descriptor of this kvm-based VCPU. - fd: Arc, /// Architecture special CPU property. pub arch_cpu: Arc>, /// LifeCycle state of kvm-based VCPU. @@ -245,14 +243,12 @@ impl CPU { /// * `vm` - The virtual machine this `CPU` gets attached to. pub fn new( hypervisor_cpu: Arc, - vcpu_fd: Arc, id: u8, arch_cpu: Arc>, vm: Arc>, ) -> Self { CPU { id, - fd: vcpu_fd, arch_cpu, state: Arc::new((Mutex::new(CpuLifecycleState::Created), Condvar::new())), task: Arc::new(Mutex::new(None)), @@ -274,11 +270,6 @@ impl CPU { self.id } - /// Get this `CPU`'s file descriptor. - pub fn fd(&self) -> &Arc { - &self.fd - } - /// Get this `CPU`'s state. pub fn state(&self) -> &(Mutex, Condvar) { self.state.as_ref() diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor_refactor/Cargo.toml index 0439d1494..4caf3147e 100644 --- a/hypervisor_refactor/Cargo.toml +++ b/hypervisor_refactor/Cargo.toml @@ -16,7 +16,6 @@ vmm-sys-util = "0.11.1" address_space = { path = "../address_space" } cpu = { path = "../cpu" } devices = { path = "../devices" } -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index f42f270d1..99744bbaa 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -234,9 +234,14 @@ impl HypervisorOps for KvmHypervisor { fn create_hypervisor_cpu( &self, - _vcpu_id: u8, - vcpu_fd: Arc, + vcpu_id: u8, ) -> Result> { + let vcpu_fd = self + .vm_fd + .as_ref() + .unwrap() + .create_vcpu(vcpu_id as u64) + .with_context(|| "Create vcpu failed")?; Ok(Arc::new(KvmCpu::new( #[cfg(target_arch = "aarch64")] self.vm_fd.clone(), @@ -330,14 +335,11 @@ pub struct KvmCpu { } impl KvmCpu { - pub fn new( - #[cfg(target_arch = "aarch64")] vm_fd: Option>, - vcpu_fd: Arc, - ) -> Self { + pub fn new(#[cfg(target_arch = "aarch64")] vm_fd: Option>, vcpu_fd: VcpuFd) -> Self { Self { #[cfg(target_arch = "aarch64")] vm_fd, - fd: vcpu_fd, + fd: Arc::new(vcpu_fd), #[cfg(target_arch = "aarch64")] kvi: Mutex::new(kvm_vcpu_init::default()), } @@ -606,7 +608,6 @@ mod test { #[cfg(target_arch = "x86_64")] use cpu::{ArchCPU, CPUBootConfig, CPUCaps}; - use hypervisor::kvm::KVM_FDS; use machine_manager::machine::{ MachineAddressInterface, MachineInterface, MachineLifecycle, VmState, }; @@ -733,22 +734,14 @@ mod test { // you need to create a irq_chip for VM before creating the VCPU. let vm_fd = kvm_hyp.vm_fd.as_ref().unwrap(); vm_fd.create_irq_chip().unwrap(); - let vcpu_fd = Arc::new( - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(0) - .unwrap(), - ); + let vcpu_fd = kvm_hyp.vm_fd.as_ref().unwrap().create_vcpu(0).unwrap(); let hypervisor_cpu = Arc::new(KvmCpu::new( #[cfg(target_arch = "aarch64")] kvm_hyp.vm_fd.clone(), - vcpu_fd.clone(), + vcpu_fd, )); let x86_cpu = Arc::new(Mutex::new(ArchCPU::new(0, 1))); - let cpu = CPU::new(hypervisor_cpu.clone(), vcpu_fd, 0, x86_cpu, vm.clone()); + let cpu = CPU::new(hypervisor_cpu.clone(), 0, x86_cpu, vm.clone()); // test `set_boot_config` function assert!(hypervisor_cpu .set_boot_config(cpu.arch().clone(), &cpu_config) @@ -797,25 +790,16 @@ mod test { return; } - let vcpu_fd = Arc::new( - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(0) - .unwrap(), - ); + let vcpu_fd = kvm_hyp.vm_fd.as_ref().unwrap().create_vcpu(0).unwrap(); let hypervisor_cpu = Arc::new(KvmCpu::new( #[cfg(target_arch = "aarch64")] kvm_hyp.vm_fd.clone(), - vcpu_fd.clone(), + vcpu_fd, )); let vm = Arc::new(Mutex::new(TestVm::new())); let cpu = CPU::new( hypervisor_cpu.clone(), - vcpu_fd.clone(), 0, Arc::new(Mutex::new(ArchCPU::default())), vm.clone(), diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index d94d31d62..6acd86072 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -16,7 +16,6 @@ pub mod error; pub mod kvm; pub use error::HypervisorError; -use kvm_ioctls::VcpuFd; use std::any::Any; use std::sync::Arc; @@ -51,9 +50,6 @@ pub trait HypervisorOps: Send + Sync + Any { #[cfg(target_arch = "x86_64")] fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()>; - fn create_hypervisor_cpu( - &self, - vcpu_id: u8, - vcpu_fd: Arc, - ) -> Result>; + fn create_hypervisor_cpu(&self, vcpu_id: u8) + -> Result>; } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 9a1a8ed70..142874521 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -214,7 +214,7 @@ impl StdMachine { } cpu.hypervisor_cpu() .vcpu_init() - .with_context(|| "Failed to init vcpu fd")?; + .with_context(|| "Failed to init vcpu")?; } locked_vm @@ -316,7 +316,7 @@ impl StdMachine { self.pause(); } - let value = match vcpu.fd().get_one_reg(addr) { + let value = match vcpu.hypervisor_cpu.get_one_reg(addr) { Ok(value) => Some(value), _ => None, }; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9bfc50575..dace4f746 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -418,19 +418,9 @@ pub trait MachineOps { where Self: Sized, { - let vcpu_fd = Arc::new( - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_vcpu(vcpu_id as u64) - .with_context(|| "Create vcpu failed")?, - ); - let locked_hypervisor = hypervisor.lock().unwrap(); let hypervisor_cpu: Arc = - locked_hypervisor.create_hypervisor_cpu(vcpu_id, vcpu_fd.clone())?; + locked_hypervisor.create_hypervisor_cpu(vcpu_id)?; #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); @@ -439,7 +429,6 @@ pub trait MachineOps { let cpu = Arc::new(CPU::new( hypervisor_cpu, - vcpu_fd, vcpu_id, Arc::new(Mutex::new(arch_cpu)), vm.clone(), -- Gitee From 5c5d8e0a9dd4ff9b38ddb92c956bad1b3627733f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 23 Jan 2024 11:26:14 +0800 Subject: [PATCH 1575/2187] CPU: Replace comment related to kvm with hypervisor. Modify the comment related to kvm. Signed-off-by: Jinhao Gao --- cpu/src/error.rs | 18 +++++++++--------- cpu/src/lib.rs | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpu/src/error.rs b/cpu/src/error.rs index 21187e582..3f91fffd7 100644 --- a/cpu/src/error.rs +++ b/cpu/src/error.rs @@ -20,23 +20,23 @@ pub enum CpuError { source: vmm_sys_util::errno::Error, }, - #[error("Failed to create kvm vcpu: {0}!")] + #[error("Failed to create vcpu: {0}!")] CreateVcpu(String), - #[error("Failed to configure kvm vcpu: {0}!")] + #[error("Failed to configure vcpu: {0}!")] RealizeVcpu(String), - #[error("Failed to starting kvm vcpu: {0}!")] + #[error("Failed to starting vcpu: {0}!")] StartVcpu(String), - #[error("Failed to stopping kvm vcpu: {0}!")] + #[error("Failed to stopping vcpu: {0}!")] StopVcpu(String), - #[error("Failed to kick kvm vcpu: {0}!")] + #[error("Failed to kick vcpu: {0}!")] KickVcpu(String), - #[error("Failed to destroy kvm vcpu: {0}!")] + #[error("Failed to destroy vcpu: {0}!")] DestroyVcpu(String), - #[error("CPU {0}/KVM halted!")] + #[error("CPU {0} halted!")] VcpuHltEvent(u8), - #[error("CPU {0}/KVM received an unexpected exit reason: {1}!")] + #[error("CPU {0} received an unexpected exit reason: {1}!")] VcpuExitReason(u8, String), - #[error("CPU {0}/KVM received an unhandled kvm exit event: error {1}!")] + #[error("CPU {0} received an unhandled exit event: error {1}!")] UnhandledHypervisorExit(u8, i32), #[error("Vcpu not present in local thread.")] VcpuLocalThreadNotPresent, diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 30c7e48d4..cd024b421 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -134,7 +134,7 @@ pub trait CPUInterface { #[cfg(target_arch = "aarch64")] features: &CPUFeatures, ) -> Result<()>; - /// Start `CPU` thread and run virtual CPU in kvm. + /// Start `CPU` thread and run virtual CPU in hypervisor. /// /// # Arguments /// @@ -145,7 +145,7 @@ pub trait CPUInterface { where Self: std::marker::Sized; - /// Kick `CPU` to exit kvm emulation. + /// Kick `CPU` to exit hypervisor emulation. fn kick(&self) -> Result<()>; /// Make `CPU` lifecycle from `Running` to `Paused`. @@ -207,14 +207,14 @@ pub trait CPUHypervisorOps: Send + Sync { fn kick(&self) -> Result<()>; } -/// `CPU` is a wrapper around creating and using a kvm-based VCPU. +/// `CPU` is a wrapper around creating and using a hypervisor-based VCPU. #[allow(clippy::upper_case_acronyms)] pub struct CPU { /// ID of this virtual CPU, `0` means this cpu is primary `CPU`. pub id: u8, /// Architecture special CPU property. pub arch_cpu: Arc>, - /// LifeCycle state of kvm-based VCPU. + /// LifeCycle state of hypervisor-based VCPU. pub state: Arc<(Mutex, Condvar)>, /// The thread handler of this virtual CPU. task: Arc>>>, @@ -226,7 +226,7 @@ pub struct CPU { pub caps: CPUCaps, /// The state backup of architecture CPU right before boot. boot_state: Arc>, - /// Sync the pause state of vCPU in kvm and userspace. + /// Sync the pause state of vCPU in hypervisor and userspace. pause_signal: Arc, /// Interact between the vCPU and hypervisor. pub hypervisor_cpu: Arc, @@ -443,7 +443,7 @@ impl CPUInterface for CPU { } } - // It shall wait for the vCPU pause state from kvm exits. + // It shall wait for the vCPU pause state from hypervisor exits. loop { if self.pause_signal.load(Ordering::SeqCst) { break; @@ -560,7 +560,7 @@ impl CPUThreadWorker { }) } - /// Judge whether the kvm vcpu is ready to emulate. + /// Judge whether the hypervisor vcpu is ready to emulate. pub fn ready_for_running(&self) -> Result { let mut flag = 0_u32; let (cpu_state_locked, cvar) = &*self.thread_cpu.state; -- Gitee From c491725bbd3250dc727ae5c58375193cb601acdf Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 3 Jan 2024 20:13:05 +0800 Subject: [PATCH 1576/2187] InterruptManager: Introduce the Interrupt Manager in Machine Introduce two interrupt managers in the machine layer, which manage interrupt routing and dispatching. It includes legacy and MSI interrupt managers for handling line and message interrupts, respectively. The KVMHypervisor now handles the management of the interrupt route table, decoupling it from the KVM implementation. Signed-off-by: Jinhao Gao --- devices/src/acpi/cpu_controller.rs | 2 +- devices/src/acpi/ged.rs | 11 +- devices/src/acpi/power.rs | 2 +- devices/src/interrupt_controller/mod.rs | 126 +++++++++++ devices/src/legacy/fwcfg.rs | 8 +- devices/src/legacy/pflash.rs | 4 +- devices/src/legacy/pl011.rs | 14 +- devices/src/legacy/pl031.rs | 16 +- devices/src/legacy/rtc.rs | 16 +- devices/src/legacy/serial.rs | 20 +- devices/src/lib.rs | 5 +- devices/src/pci/bus.rs | 14 ++ devices/src/pci/demo_device/mod.rs | 10 +- devices/src/pci/intx.rs | 8 +- devices/src/pci/mod.rs | 11 +- devices/src/pci/msix.rs | 269 ++++++++--------------- devices/src/pci/root_port.rs | 17 +- devices/src/sysbus/mod.rs | 65 +++--- devices/src/usb/xhci/xhci_pci.rs | 3 +- hypervisor/src/kvm/interrupt.rs | 252 --------------------- hypervisor/src/kvm/mod.rs | 80 +------ hypervisor_refactor/src/kvm/interrupt.rs | 116 ++++++++-- hypervisor_refactor/src/kvm/mod.rs | 240 +++++++++++++++++--- hypervisor_refactor/src/lib.rs | 7 +- machine/src/aarch64/micro.rs | 14 +- machine/src/aarch64/standard.rs | 48 ++-- machine/src/lib.rs | 2 +- machine/src/standard_common/syscall.rs | 1 - machine/src/x86_64/micro.rs | 13 +- machine/src/x86_64/standard.rs | 14 +- vfio/src/vfio_pci.rs | 71 +++--- virtio/src/transport/virtio_mmio.rs | 20 +- virtio/src/transport/virtio_pci.rs | 13 +- 33 files changed, 731 insertions(+), 781 deletions(-) delete mode 100644 hypervisor/src/kvm/interrupt.rs diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index 15190d921..1259e8d2d 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -330,7 +330,7 @@ impl SysBusDevOps for CpuController { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 576294558..f50e1b118 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -16,7 +16,6 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; -use log::error; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -176,14 +175,6 @@ impl Ged { .fetch_or(evt as u32, Ordering::SeqCst); self.inject_interrupt(); } - - fn inject_interrupt(&self) { - if let Some(evt_fd) = self.interrupt_evt() { - evt_fd - .write(1) - .unwrap_or_else(|e| error!("ged: failed to write interrupt eventfd ({:?}).", e)); - } - } } impl Device for Ged { @@ -219,7 +210,7 @@ impl SysBusDevOps for Ged { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index aa57df4cd..5c0280146 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -260,7 +260,7 @@ impl SysBusDevOps for PowerDev { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 32b3320e5..17c278e63 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -25,6 +25,7 @@ //! - `aarch64` #[allow(clippy::upper_case_acronyms)] +#[cfg(target_arch = "aarch64")] mod aarch64; mod error; @@ -37,3 +38,128 @@ pub use aarch64::{ GICv3ItsState, GICv3State, GicRedistRegion, InterruptController, GIC_IRQ_INTERNAL, GIC_IRQ_MAX, }; pub use error::InterruptError; + +use std::sync::Arc; + +use vmm_sys_util::eventfd::EventFd; + +use super::pci::MsiVector; + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub enum TriggerMode { + Level, + #[default] + Edge, +} + +pub trait LineIrqManager: Send + Sync { + fn irqfd_enable(&self) -> bool; + + fn register_irqfd( + &self, + _irq_fd: Arc, + _irq: u32, + _trigger_mode: TriggerMode, + ) -> Result<()> { + Ok(()) + } + + fn unregister_irqfd(&self, _irq_fd: Arc, _irq: u32) -> Result<()> { + Ok(()) + } + + fn set_irq_line(&self, _irq: u32, _level: bool) -> Result<()> { + Ok(()) + } + + fn write_irqfd(&self, _irq_fd: Arc) -> Result<()> { + Ok(()) + } +} + +pub trait MsiIrqManager: Send + Sync { + fn allocate_irq(&self, _vector: MsiVector) -> Result { + Ok(0) + } + + fn release_irq(&self, _irq: u32) -> Result<()> { + Ok(()) + } + + fn register_irqfd(&self, _irq_fd: Arc, _irq: u32) -> Result<()> { + Ok(()) + } + + fn unregister_irqfd(&self, _irq_fd: Arc, _irq: u32) -> Result<()> { + Ok(()) + } + + fn trigger( + &self, + _irq_fd: Option>, + _vector: MsiVector, + _dev_id: u32, + ) -> Result<()> { + Ok(()) + } + + fn update_route_table(&self, _gsi: u32, _vector: MsiVector) -> Result<()> { + Ok(()) + } +} + +pub struct IrqManager { + pub line_irq_manager: Option>, + pub msi_irq_manager: Option>, +} + +#[derive(Default, Clone)] +pub struct IrqState { + pub irq: u32, + irq_fd: Option>, + irq_handler: Option>, + trigger_mode: TriggerMode, +} + +impl IrqState { + pub fn new( + irq: u32, + irq_fd: Option>, + irq_handler: Option>, + trigger_mode: TriggerMode, + ) -> Self { + IrqState { + irq, + irq_fd, + irq_handler, + trigger_mode, + } + } + + pub fn register_irq(&mut self) -> Result<()> { + let irq_handler = self.irq_handler.as_ref().unwrap(); + if !irq_handler.irqfd_enable() { + self.irq_fd = None; + return Ok(()); + } + + if let Some(irqfd) = self.irq_fd.clone() { + irq_handler.register_irqfd(irqfd, self.irq, self.trigger_mode.clone())?; + } + + Ok(()) + } + + pub fn trigger_irq(&self) -> Result<()> { + let irq_handler = self.irq_handler.as_ref().unwrap(); + if let Some(irq_fd) = &self.irq_fd { + return irq_handler.write_irqfd(irq_fd.clone()); + } + if self.trigger_mode == TriggerMode::Edge { + irq_handler.set_irq_line(self.irq, true)?; + irq_handler.set_irq_line(self.irq, false) + } else { + irq_handler.set_irq_line(self.irq, true) + } + } +} diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 8c476b376..2abf4368e 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -980,7 +980,7 @@ impl SysBusDevOps for FwCfgMem { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } @@ -990,7 +990,7 @@ impl SysBusDevOps for FwCfgMem { region_base: u64, region_size: u64, ) -> Result<()> { - let res = self.get_sys_resource().unwrap(); + let res = self.get_sys_resource_mut().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) @@ -1165,7 +1165,7 @@ impl SysBusDevOps for FwCfgIO { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } @@ -1175,7 +1175,7 @@ impl SysBusDevOps for FwCfgIO { region_base: u64, region_size: u64, ) -> Result<()> { - let res = self.get_sys_resource().unwrap(); + let res = self.get_sys_resource_mut().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 4a7f1d3b7..88fdbad0c 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -877,7 +877,7 @@ impl SysBusDevOps for PFlash { } } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } @@ -887,7 +887,7 @@ impl SysBusDevOps for PFlash { region_base: u64, region_size: u64, ) -> Result<()> { - let res = self.get_sys_resource().unwrap(); + let res = self.get_sys_resource_mut().unwrap(); res.region_base = region_base; res.region_size = region_size; res.irq = 0; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index d22b2ab69..976b17a4e 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -132,27 +132,21 @@ impl PL011 { pub fn new(cfg: SerialConfig) -> Result { Ok(PL011 { base: SysBusDevBase { - base: DeviceBase::default(), dev_type: SysBusDevType::PL011, - res: SysRes::default(), interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), + ..Default::default() }, state: PL011State::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), }) } - fn interrupt(&self) { + fn interrupt(&mut self) { let irq_mask = INT_E | INT_MS | INT_RT | INT_TX | INT_RX; let flag = self.state.int_level & self.state.int_enabled; if flag & irq_mask != 0 { - if let Err(e) = self.interrupt_evt().unwrap().write(1) { - error!( - "Failed to trigger interrupt for PL011, flag is 0x{:x}, error is {:?}", - flag, e, - ) - } + self.inject_interrupt(); trace::pl011_interrupt(flag); } } @@ -413,7 +407,7 @@ impl SysBusDevOps for PL011 { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 637e2e02f..9535cf3b1 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -15,7 +15,6 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use anyhow::{Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::error; use vmm_sys_util::eventfd::EventFd; use super::error::LegacyError; @@ -121,17 +120,6 @@ impl PL031 { fn get_current_value(&self) -> u32 { (self.base_time.elapsed().as_secs() as u128 + self.tick_offset as u128) as u32 } - - fn inject_interrupt(&self) { - if let Some(evt_fd) = self.interrupt_evt() { - if let Err(e) = evt_fd.write(1) { - error!("pl031: failed to write interrupt eventfd ({:?}).", e); - } - trace::pl031_inject_interrupt(); - return; - } - error!("pl031: failed to get interrupt event fd."); - } } impl Device for PL031 { @@ -198,10 +186,12 @@ impl SysBusDevOps for PL031 { RTC_IMSC => { self.state.imsr = value & 1; self.inject_interrupt(); + trace::pl031_inject_interrupt(); } RTC_ICR => { self.state.risr = 0; self.inject_interrupt(); + trace::pl031_inject_interrupt(); } _ => {} } @@ -209,7 +199,7 @@ impl SysBusDevOps for PL031 { true } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index b1ce1f6e9..8334c2d19 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -120,7 +120,6 @@ impl RTC { pub fn new() -> Result { let mut rtc = RTC { base: SysBusDevBase { - base: DeviceBase::default(), dev_type: SysBusDevType::Rtc, res: SysRes { region_base: RTC_PORT_INDEX, @@ -128,6 +127,7 @@ impl RTC { irq: -1, }, interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), + ..Default::default() }, cmos_data: [0_u8; 128], cur_index: 0_u8, @@ -216,6 +216,7 @@ impl RTC { if self.update_in_progress() { data[0] |= REG_A_UIP; self.inject_interrupt(); + trace::rtc_inject_interrupt(); } } RTC_REG_C => { @@ -275,17 +276,6 @@ impl RTC { Ok(()) } - fn inject_interrupt(&self) { - if let Some(evt_fd) = self.interrupt_evt() { - if let Err(e) = evt_fd.write(1) { - error!("cmos rtc: failed to write interrupt eventfd ({:?}).", e); - } - trace::rtc_inject_interrupt(); - return; - } - error!("cmos rtc: failed to get interrupt event fd."); - } - /// Get current clock value. fn get_current_value(&self) -> i64 { (self.base_time.elapsed().as_secs() as i128 + self.tick_offset as i128) as i64 @@ -401,7 +391,7 @@ impl SysBusDevOps for RTC { } } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 11f2d253e..7b1a98a26 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -27,7 +27,6 @@ use acpi::{ }; use address_space::GuestAddress; use chardev_backend::chardev::{Chardev, InputReceiver}; -use hypervisor::kvm::KVM_FDS; use machine_manager::{config::SerialConfig, event_loop::EventLoop}; use migration::{ snapshot::SERIAL_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, @@ -179,13 +178,7 @@ impl Serial { self.state.iir = iir; if iir != UART_IIR_NO_INT { - if let Some(evt) = self.interrupt_evt() { - if let Err(e) = evt.write(1) { - error!("serial: failed to write interrupt eventfd ({:?}).", e); - } - return; - } - error!("serial: failed to update iir."); + self.inject_interrupt(); } trace::serial_update_iir(self.state.iir); } @@ -397,16 +390,11 @@ impl SysBusDevOps for Serial { } } - fn set_irq(&mut self, _sysbus: &mut SysBus) -> Result { - let mut irq: i32 = -1; - if let Some(e) = self.interrupt_evt() { - irq = UART_IRQ; - KVM_FDS.load().register_irqfd(&e, irq as u32)?; - } - Ok(irq) + fn get_irq(&self, _sysbus: &mut SysBus) -> Result { + Ok(UART_IRQ) } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 1611a4ef9..a155ff643 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -19,6 +19,7 @@ pub mod acpi; #[cfg(feature = "usb_camera")] pub mod camera_backend; +pub mod interrupt_controller; pub mod legacy; pub mod misc; pub mod pci; @@ -27,15 +28,13 @@ pub mod smbios; pub mod sysbus; pub mod usb; -#[cfg(target_arch = "aarch64")] -mod interrupt_controller; - #[cfg(target_arch = "aarch64")] pub use interrupt_controller::{ GICDevice, GICVersion, GICv2, GICv2Access, GICv3, GICv3Access, GICv3ItsAccess, GICv3ItsState, GICv3State, GicRedistRegion, ICGICConfig, ICGICv2Config, ICGICv3Config, InterruptController, InterruptError as IntCtrlErrs, GIC_IRQ_INTERNAL, GIC_IRQ_MAX, }; +pub use interrupt_controller::{IrqManager, IrqState, LineIrqManager, MsiIrqManager, TriggerMode}; pub use legacy::error::LegacyError as LegacyErrs; pub use scsi::bus as ScsiBus; pub use scsi::disk as ScsiDisk; diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index c4b22e62c..b0f5dc801 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -22,6 +22,7 @@ use super::{ hotplug::HotplugOps, PciDevOps, PciIntxState, }; +use crate::MsiIrqManager; use address_space::Region; type DeviceBusInfo = (Arc>, Arc>); @@ -45,6 +46,7 @@ pub struct PciBus { pub hotplug_controller: Option>>, /// Interrupt info related to INTx. pub intx_state: Option>>, + pub msi_irq_manager: Option>, } impl PciBus { @@ -70,6 +72,7 @@ impl PciBus { mem_region, hotplug_controller: None, intx_state: None, + msi_irq_manager: None, } } @@ -251,6 +254,17 @@ impl PciBus { pub fn update_dev_id(&self, devfn: u8, dev_id: &Arc) { dev_id.store(self.generate_dev_id(devfn), Ordering::Release); } + + pub fn get_msi_irq_manager(&self) -> Option> { + match &self.parent_bridge { + Some(parent_bridge) => { + let parent_bridge = parent_bridge.upgrade().unwrap(); + let locked_parent_bridge = parent_bridge.lock().unwrap(); + locked_parent_bridge.get_msi_irq_manager() + } + None => self.msi_irq_manager.clone(), + } + } } #[cfg(test)] diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index a94eea6f9..778953667 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -192,15 +192,7 @@ impl PciDevOps for DemoDev { fn realize(mut self) -> Result<()> { self.init_pci_config()?; if self.cmd_cfg.bar_num > 0 { - init_msix( - 0, - 1, - &mut self.base.config, - self.dev_id.clone(), - &self.base.base.id, - None, - None, - )?; + init_msix(&mut self.base, 0, 1, self.dev_id.clone(), None, None)?; } self.register_data_handling_bar()?; diff --git a/devices/src/pci/intx.rs b/devices/src/pci/intx.rs index 503f7c975..c450ff633 100644 --- a/devices/src/pci/intx.rs +++ b/devices/src/pci/intx.rs @@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use log::error; +use crate::interrupt_controller::LineIrqManager; use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_INTR_BASE, PCI_PIN_NUM}; use util::test_helper::{is_test_enabled, trigger_intx}; @@ -27,11 +28,11 @@ pub struct PciIntxState { /// INTx IRQ numbers to be asserted of every INTx virtual interrupt line. pub irq_count: [i8; PCI_PIN_NUM as usize], /// Handler of asserting the INTx IRQ. - pub irq_handler: InterruptHandler, + pub irq_handler: Arc, } impl PciIntxState { - pub fn new(gsi_base: u32, irq_handler: InterruptHandler) -> Self { + pub fn new(gsi_base: u32, irq_handler: Arc) -> Self { Self { gsi_base, irq_count: [0; PCI_PIN_NUM as usize], @@ -100,7 +101,8 @@ impl Intx { return; } - if let Err(e) = (locked_intx_state.irq_handler)(irq, level) { + let irq_handler = &locked_intx_state.irq_handler; + if let Err(e) = irq_handler.set_irq_line(irq, level) { error!( "Failed to set irq {} level {} of device {}: {}.", irq, level, self.device_name, e diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index e9ba0c48a..94aae2449 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -29,7 +29,7 @@ pub use config::{PciConfig, INTERRUPT_PIN}; pub use error::PciError; pub use host::PciHost; pub use intx::{init_intx, InterruptHandler, PciIntxState}; -pub use msix::init_msix; +pub use msix::{init_msix, MsiVector}; pub use root_port::RootPort; use std::{ @@ -39,7 +39,10 @@ use std::{ use byteorder::{ByteOrder, LittleEndian}; -use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use crate::{ + pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}, + MsiIrqManager, +}; use crate::{Device, DeviceBase}; use util::AsAny; @@ -262,6 +265,10 @@ pub trait PciDevOps: Device + Send + AsAny { fn get_intx_state(&self) -> Option>> { None } + + fn get_msi_irq_manager(&self) -> Option> { + None + } } /// Init multifunction for pci devices. diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 6d92a06b9..00d54a8ff 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -19,10 +19,12 @@ use anyhow::{bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; -use crate::pci::config::{CapId, PciConfig, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO}; -use crate::pci::{le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64}; +use crate::pci::config::{CapId, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO}; +use crate::pci::{ + le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, PciDevBase, +}; +use crate::MsiIrqManager; use address_space::{GuestAddress, Region, RegionOps}; -use hypervisor::kvm::{MsiVector, KVM_FDS}; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; @@ -50,6 +52,17 @@ pub const MSIX_CAP_ID: u8 = 0x11; pub const MSIX_CAP_TABLE: u8 = 0x04; const MSIX_CAP_PBA: u8 = 0x08; +/// Basic data for msi vector. +#[derive(Copy, Clone, Default)] +pub struct MsiVector { + pub msg_addr_lo: u32, + pub msg_addr_hi: u32, + pub msg_data: u32, + pub masked: bool, + #[cfg(target_arch = "aarch64")] + pub dev_id: u32, +} + /// MSI-X message structure. #[derive(Copy, Clone)] pub struct Message { @@ -94,6 +107,7 @@ pub struct Msix { pub dev_id: Arc, /// Maintains a list of GSI with irqfds that are registered to kvm. gsi_msi_routes: HashMap, + pub msi_irq_manager: Option>, } impl Msix { @@ -110,6 +124,7 @@ impl Msix { pba_size: u32, msix_cap_offset: u16, dev_id: Arc, + msi_irq_manager: Option>, ) -> Self { let mut msix = Msix { table: vec![0; table_size as usize], @@ -119,6 +134,7 @@ impl Msix { msix_cap_offset, dev_id, gsi_msi_routes: HashMap::new(), + msi_irq_manager, }; msix.mask_all_vectors(); msix @@ -218,50 +234,21 @@ impl Msix { dev_id: self.dev_id.load(Ordering::Acquire) as u32, }; + let irq_manager = self.msi_irq_manager.as_ref().unwrap(); + if is_masked { - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .unregister_irqfd(route.irq_fd.as_ref(), route.gsi as u32) - .map_err(|e| { - error!("Failed to unregister irq, error is {:?}", e); - e - })?; + irq_manager.unregister_irqfd(route.irq_fd.clone(), route.gsi as u32)?; } else { let msg = &route.msg; if msg.data != entry.data || msg.address_lo != entry.address_lo || msg.address_hi != entry.address_hi { - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .update_msi_route(route.gsi as u32, msix_vector) - .map_err(|e| { - error!("Failed to update MSI-X route, error is {:?}", e); - e - })?; - KVM_FDS.load().commit_irq_routing().map_err(|e| { - error!("Failed to commit irq routing, error is {:?}", e); - e - })?; + irq_manager.update_route_table(route.gsi as u32, msix_vector)?; route.msg = entry; } - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .register_irqfd(route.irq_fd.as_ref(), route.gsi as u32) - .map_err(|e| { - error!("Failed to register irq, error is {:?}", e); - e - })?; + irq_manager.register_irqfd(route.irq_fd.clone(), route.gsi as u32)?; } Ok(()) } @@ -277,43 +264,10 @@ impl Msix { dev_id: self.dev_id.load(Ordering::Acquire) as u32, }; - let gsi = KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .allocate_gsi() - .map_err(|e| { - error!("Failed to allocate gsi, error is {:?}", e); - e - })?; - - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .add_msi_route(gsi, msix_vector) - .map_err(|e| { - error!("Failed to add MSI-X route, error is {:?}", e); - e - })?; - - KVM_FDS.load().commit_irq_routing().map_err(|e| { - error!("Failed to commit irq routing, error is {:?}", e); - e - })?; - - KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .register_irqfd(call_fd.as_ref(), gsi) - .map_err(|e| { - error!("Failed to register irq, error is {:?}", e); - e - })?; + let irq_manager = self.msi_irq_manager.as_ref().unwrap(); + + let gsi = irq_manager.allocate_irq(msix_vector)?; + irq_manager.register_irqfd(call_fd.clone(), gsi)?; let gsi_route = GsiMsiRoute { irq_fd: call_fd, @@ -325,25 +279,10 @@ impl Msix { } pub fn unregister_irqfd(&mut self) -> Result<()> { + let irq_manager = &self.msi_irq_manager.as_ref().unwrap(); for (_, route) in self.gsi_msi_routes.iter() { - KVM_FDS - .load() - .unregister_irqfd(route.irq_fd.as_ref(), route.gsi as u32) - .map_err(|e| { - error!("Failed to unregister irq, error is {:?}", e); - e - })?; - - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .release_gsi(route.gsi as u32) - .map_err(|e| { - error!("Failed to release gsi, error is {:?}", e); - e - })?; + irq_manager.unregister_irqfd(route.irq_fd.clone(), route.gsi as u32)?; + irq_manager.release_irq(route.gsi as u32)?; } self.gsi_msi_routes.clear(); Ok(()) @@ -461,19 +400,6 @@ impl Msix { pub fn send_msix(&self, vector: u16, dev_id: u16) { let msg = self.get_message(vector); - #[cfg(target_arch = "aarch64")] - let flags: u32 = kvm_bindings::KVM_MSI_VALID_DEVID; - #[cfg(target_arch = "x86_64")] - let flags: u32 = 0; - - let kvm_msi = kvm_bindings::kvm_msi { - address_lo: msg.address_lo, - address_hi: msg.address_hi, - data: msg.data, - flags, - devid: dev_id as u32, - pad: [0; 12], - }; if is_test_enabled() { let data = msg.data; @@ -483,7 +409,17 @@ impl Msix { return; } - if let Err(e) = KVM_FDS.load().vm_fd.as_ref().unwrap().signal_msi(kvm_msi) { + let msix_vector = MsiVector { + msg_addr_lo: msg.address_lo, + msg_addr_hi: msg.address_hi, + msg_data: msg.data, + masked: false, + #[cfg(target_arch = "aarch64")] + dev_id: dev_id as u32, + }; + + let irq_manager = self.msi_irq_manager.as_ref().unwrap(); + if let Err(e) = irq_manager.trigger(None, msix_vector, dev_id as u32) { error!("Send msix error: {:?}", e); }; } @@ -581,28 +517,16 @@ impl MigrationHook for Msix { let msg = self.get_message(vector); // update and commit irq routing - { - let kvm_fds = KVM_FDS.load(); - let mut locked_irq_table = kvm_fds.irq_route_table.lock().unwrap(); - let allocated_gsi = match locked_irq_table.allocate_gsi() { - Ok(gsi) => gsi, - Err(e) => bail!("Failed to allocate new gsi: {}", e), - }; - let msi_vector = MsiVector { - msg_addr_hi: msg.address_hi, - msg_addr_lo: msg.address_lo, - msg_data: msg.data, - masked: false, - #[cfg(target_arch = "aarch64")] - dev_id: self.dev_id.load(Ordering::Acquire) as u32, - }; - if let Err(e) = locked_irq_table.add_msi_route(allocated_gsi, msi_vector) { - bail!("Failed to add msi route to global irq routing table: {}", e); - } - } - if let Err(e) = KVM_FDS.load().commit_irq_routing() { - bail!("Failed to commit irq routing: {}", e); - } + let msi_vector = MsiVector { + msg_addr_hi: msg.address_hi, + msg_addr_lo: msg.address_lo, + msg_data: msg.data, + masked: false, + #[cfg(target_arch = "aarch64")] + dev_id: self.dev_id.load(Ordering::Acquire) as u32, + }; + let irq_manager = self.msi_irq_manager.as_ref().unwrap(); + irq_manager.allocate_irq(msi_vector)?; if self.is_vector_pending(vector) { self.clear_pending_vector(vector); @@ -619,23 +543,23 @@ impl MigrationHook for Msix { /// /// # Arguments /// +/// * `pcidev_base ` - The Base of PCI device /// * `bar_id` - BAR id. /// * `vector_nr` - The number of vector. -/// * `config` - The PCI config. /// * `dev_id` - Dev id. -/// * `_id` - MSI-X id used in MigrationManager. /// * `parent_region` - Parent region which the MSI-X region registered. If none, registered in BAR. /// * `offset_opt` - Offset of table(table_offset) and Offset of pba(pba_offset). Set the /// table_offset and pba_offset together. pub fn init_msix( + pcidev_base: &mut PciDevBase, bar_id: usize, vector_nr: u32, - config: &mut PciConfig, dev_id: Arc, - _id: &str, parent_region: Option<&Region>, offset_opt: Option<(u32, u32)>, ) -> Result<()> { + let config = &mut pcidev_base.config; + let parent_bus = &pcidev_base.parent_bus; if vector_nr == 0 || vector_nr > MSIX_TABLE_SIZE_MAX as u32 + 1 { bail!( "invalid msix vectors, which should be in [1, {}]", @@ -669,11 +593,20 @@ pub fn init_msix( offset = msix_cap_offset + MSIX_CAP_PBA as usize; le_write_u32(&mut config.config, offset, pba_offset | bar_id as u32)?; + let msi_irq_manager = if let Some(pci_bus) = parent_bus.upgrade() { + let locked_pci_bus = pci_bus.lock().unwrap(); + locked_pci_bus.get_msi_irq_manager() + } else { + error!("Msi irq controller is none"); + None + }; + let msix = Arc::new(Mutex::new(Msix::new( table_size, pba_size, msix_cap_offset as u16, dev_id.clone(), + msi_irq_manager, ))); if let Some(region) = parent_region { Msix::register_memory_region( @@ -700,54 +633,45 @@ pub fn init_msix( config.msix = Some(msix.clone()); #[cfg(not(test))] - MigrationManager::register_device_instance(MsixState::descriptor(), msix, _id); + MigrationManager::register_device_instance(MsixState::descriptor(), msix, &pcidev_base.base.id); Ok(()) } #[cfg(test)] mod tests { + use std::sync::Weak; + use super::*; - use crate::pci::config::PCI_CONFIG_SPACE_SIZE; + use crate::{ + pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}, + DeviceBase, + }; #[test] fn test_init_msix() { - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2); - + let mut base = PciDevBase { + base: DeviceBase::new("msix".to_string(), false), + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), + devfn: 1, + parent_bus: Weak::new(), + }; // Too many vectors. assert!(init_msix( + &mut base, 0, MSIX_TABLE_SIZE_MAX as u32 + 2, - &mut pci_config, Arc::new(AtomicU16::new(0)), - "msix", None, None, ) .is_err()); // No vector. - assert!(init_msix( - 0, - 0, - &mut pci_config, - Arc::new(AtomicU16::new(0)), - "msix", - None, - None, - ) - .is_err()); + assert!(init_msix(&mut base, 0, 0, Arc::new(AtomicU16::new(0)), None, None,).is_err()); - init_msix( - 1, - 2, - &mut pci_config, - Arc::new(AtomicU16::new(0)), - "msix", - None, - None, - ) - .unwrap(); + init_msix(&mut base, 1, 2, Arc::new(AtomicU16::new(0)), None, None).unwrap(); + let pci_config = base.config; let msix_cap_start = 64_u8; assert_eq!(pci_config.last_cap_end, 64 + MSIX_CAP_SIZE as u16); // Capabilities pointer @@ -777,6 +701,7 @@ mod tests { 64, 64, Arc::new(AtomicU16::new(0)), + None, ); assert!(msix.table[MSIX_TABLE_VEC_CTL as usize] & MSIX_TABLE_MASK_BIT > 0); @@ -798,6 +723,7 @@ mod tests { 64, 64, Arc::new(AtomicU16::new(0)), + None, ); msix.set_pending_vector(0); @@ -813,6 +739,7 @@ mod tests { 64, 64, Arc::new(AtomicU16::new(0)), + None, ); le_write_u32(&mut msix.table, 0, 0x1000_0000).unwrap(); le_write_u32(&mut msix.table, 4, 0x2000_0000).unwrap(); @@ -826,26 +753,22 @@ mod tests { #[test] fn test_write_config() { - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2); - init_msix( - 0, - 2, - &mut pci_config, - Arc::new(AtomicU16::new(0)), - "msix", - None, - None, - ) - .unwrap(); - let msix = pci_config.msix.as_ref().unwrap(); + let mut base = PciDevBase { + base: DeviceBase::new("msix".to_string(), false), + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), + devfn: 1, + parent_bus: Weak::new(), + }; + init_msix(&mut base, 0, 2, Arc::new(AtomicU16::new(0)), None, None).unwrap(); + let msix = base.config.msix.as_ref().unwrap(); let mut locked_msix = msix.lock().unwrap(); locked_msix.enabled = false; let offset = locked_msix.msix_cap_offset as usize + MSIX_CAP_CONTROL as usize; - let val = le_read_u16(&pci_config.config, offset).unwrap(); - le_write_u16(&mut pci_config.config, offset, val | MSIX_CAP_ENABLE).unwrap(); + let val = le_read_u16(&base.config.config, offset).unwrap(); + le_write_u16(&mut base.config.config, offset, val | MSIX_CAP_ENABLE).unwrap(); locked_msix.set_pending_vector(0); locked_msix.write_config( - &pci_config.config, + &base.config.config, 0, offset, &[0, val as u8 | MSIX_CAP_ENABLE as u8], diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 055ca1537..9f1f0125b 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -37,7 +37,7 @@ use crate::pci::{init_multifunction, PciDevBase, PciError, PciIntxState, INTERRU use crate::pci::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, PciDevOps, }; -use crate::{Device, DeviceBase}; +use crate::{Device, DeviceBase, MsiIrqManager}; use address_space::Region; use machine_manager::qmp::qmp_channel::send_device_deleted_msg; use migration::{ @@ -374,15 +374,7 @@ impl PciDevOps for RootPort { )?; self.dev_id.store(self.base.devfn as u16, Ordering::SeqCst); - init_msix( - 0, - 1, - &mut self.base.config, - self.dev_id.clone(), - &self.base.base.id, - None, - None, - )?; + init_msix(&mut self.base, 0, 1, self.dev_id.clone(), None, None)?; init_intx( self.name(), @@ -556,6 +548,11 @@ impl PciDevOps for RootPort { None } + + fn get_msi_irq_manager(&self) -> Option> { + let msix = self.base.config.msix.as_ref().unwrap(); + msix.lock().unwrap().msi_irq_manager.clone() + } } impl HotplugOps for RootPort { diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index a32792612..30cb972d8 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -20,10 +20,9 @@ use std::sync::{Arc, Mutex}; use vmm_sys_util::eventfd::EventFd; -use crate::{Device, DeviceBase}; +use crate::{Device, DeviceBase, IrqState, LineIrqManager, TriggerMode}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; -use hypervisor::kvm::KVM_FDS; use util::AsAny; // Now that the serial device use a hardcoded IRQ number (4), and the starting @@ -48,6 +47,7 @@ pub struct SysBus { pub min_free_irq: i32, pub mmio_region: (u64, u64), pub min_free_base: u64, + pub irq_manager: Option>, } impl fmt::Debug for SysBus { @@ -91,6 +91,7 @@ impl SysBus { min_free_irq: free_irqs.0, mmio_region, min_free_base: mmio_region.0, + irq_manager: None, } } @@ -203,7 +204,7 @@ impl Default for SysRes { } #[allow(clippy::upper_case_acronyms)] -#[derive(Eq, PartialEq, Clone, Copy)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SysBusDevType { Serial, Rtc, @@ -226,6 +227,8 @@ pub struct SysBusDevBase { pub res: SysRes, /// Interrupt event file descriptor. pub interrupt_evt: Option>, + /// Interrupt state. + pub irq_state: IrqState, } impl Default for SysBusDevBase { @@ -235,6 +238,7 @@ impl Default for SysBusDevBase { dev_type: SysBusDevType::Others, res: SysRes::default(), interrupt_evt: None, + irq_state: IrqState::default(), } } } @@ -242,26 +246,8 @@ impl Default for SysBusDevBase { impl SysBusDevBase { pub fn new(dev_type: SysBusDevType) -> SysBusDevBase { Self { - base: DeviceBase::default(), dev_type, - res: SysRes::default(), - interrupt_evt: None, - } - } - - fn set_irq(&mut self, sysbus: &mut SysBus) -> Result { - let irq = sysbus.min_free_irq; - if irq > sysbus.free_irqs.1 { - bail!("IRQ number exhausted."); - } - - match &self.interrupt_evt { - None => Ok(-1_i32), - Some(evt) => { - KVM_FDS.load().register_irqfd(evt, irq as u32)?; - sysbus.min_free_irq = irq + 1; - Ok(irq) - } + ..Default::default() } } @@ -304,11 +290,17 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder + AsAny { self.sysbusdev_base().interrupt_evt.clone() } - fn set_irq(&mut self, sysbus: &mut SysBus) -> Result { - self.sysbusdev_base_mut().set_irq(sysbus) + fn get_irq(&self, sysbus: &mut SysBus) -> Result { + let irq = sysbus.min_free_irq; + if irq > sysbus.free_irqs.1 { + bail!("IRQ number exhausted."); + } + + sysbus.min_free_irq = irq + 1; + Ok(irq) } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { None } @@ -318,14 +310,31 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder + AsAny { region_base: u64, region_size: u64, ) -> Result<()> { - let irq = self.set_irq(sysbus)?; - self.get_sys_resource() - .with_context(|| "Failed to get sys resource.")?; + let irq = self.get_irq(sysbus)?; + let interrupt_evt = self.sysbusdev_base().interrupt_evt.clone(); + let irq_manager = sysbus.irq_manager.clone(); + + self.sysbusdev_base_mut().irq_state = + IrqState::new(irq as u32, interrupt_evt, irq_manager, TriggerMode::Edge); + let irq_state = &mut self.sysbusdev_base_mut().irq_state; + irq_state.register_irq()?; + self.sysbusdev_base_mut() .set_sys(irq, region_base, region_size); Ok(()) } + fn inject_interrupt(&self) { + let irq_state = &self.sysbusdev_base().irq_state; + irq_state.trigger_irq().unwrap_or_else(|e| { + log::error!( + "Device {:?} failed to inject interrupt: {:?}", + self.sysbusdev_base().dev_type, + e + ) + }); + } + fn reset(&mut self) -> Result<()> { Ok(()) } diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 2d6aa3fb8..c7c7896f3 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -276,11 +276,10 @@ impl PciDevOps for XhciPciDevice { let intrs_num = self.xhci.lock().unwrap().intrs.len() as u32; init_msix( + &mut self.base, 0_usize, intrs_num, - &mut self.base.config, self.dev_id.clone(), - &self.base.base.id, Some(&self.mem_region), Some((XHCI_MSIX_TABLE_OFFSET, XHCI_MSIX_PBA_OFFSET)), )?; diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs deleted file mode 100644 index 3a9cc6a42..000000000 --- a/hypervisor/src/kvm/interrupt.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{Context, Result}; -use kvm_bindings::{KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; -use kvm_ioctls::{Cap, Kvm}; - -use util::bitmap::Bitmap; - -pub(crate) type IrqRoute = kvm_bindings::kvm_irq_routing; -pub(crate) type IrqRouteEntry = kvm_bindings::kvm_irq_routing_entry; -type IrqRouteChip = kvm_bindings::kvm_irq_routing_irqchip; -type IrqChip = kvm_bindings::kvm_irq_routing_entry__bindgen_ty_1; - -#[cfg(target_arch = "x86_64")] -const IOAPIC_NUM_PINS: u32 = 24; -#[cfg(target_arch = "x86_64")] -const PIC_MASTER_PINS: u32 = 8; -#[cfg(target_arch = "x86_64")] -const PIC_SLACE_PINS: u32 = 8; -#[cfg(target_arch = "aarch64")] -const IOCHIP_NUM_PINS: u32 = 192; -#[cfg(target_arch = "aarch64")] -const KVM_IRQCHIP: u32 = 0; - -/// Return the max number kvm supports. -fn get_maximum_gsi_cnt(kvmfd: &Kvm) -> u32 { - let mut gsi_count = kvmfd.check_extension_int(Cap::IrqRouting); - if gsi_count < 0 { - gsi_count = 0; - } - - gsi_count as u32 -} - -/// Return `IrqRouteEntry` according to gsi, irqchip kind and pin. -fn create_irq_route_entry(gsi: u32, irqchip: u32, pin: u32) -> IrqRouteEntry { - let irq_route_chip = IrqRouteChip { irqchip, pin }; - let irq_chip = IrqChip { - irqchip: irq_route_chip, - }; - IrqRouteEntry { - gsi, - type_: KVM_IRQ_ROUTING_IRQCHIP, - flags: 0, - pad: 0, - u: irq_chip, - } -} - -/// Offer the irq gsi table for a current kvm vm instance. -pub struct IrqRouteTable { - pub irq_routes: Vec, - gsi_bitmap: Bitmap, -} - -impl Default for IrqRouteTable { - fn default() -> Self { - IrqRouteTable { - irq_routes: Vec::::new(), - gsi_bitmap: Bitmap::::new(0), - } - } -} - -impl IrqRouteTable { - /// Allocate a new irq route table. - pub fn new(kvmfd: &Kvm) -> Self { - let gsi_count = get_maximum_gsi_cnt(kvmfd); - - IrqRouteTable { - irq_routes: Vec::::new(), - gsi_bitmap: Bitmap::::new(gsi_count as usize), - } - } - - /// Init irq route table in arch x86_64. - #[cfg(target_arch = "x86_64")] - pub fn init_irq_route_table(&mut self) { - // On x86, use `kvm_create_irqchip` to create an interrupt - // controller module in the kernel. It creates a virtual PIC, a virtual ioapic, - // and sets up future vcpus to have a local APIC. IRQ routing for GSIs 0-15 is set - // to both PIC and IOAPIC. GSI 16-23 only go to the IOAPIC. - for i in 0..IOAPIC_NUM_PINS { - if i < PIC_MASTER_PINS { - self.irq_routes.push(create_irq_route_entry( - i, - kvm_bindings::KVM_IRQCHIP_PIC_MASTER, - i, - )); - } else if i < PIC_MASTER_PINS + PIC_SLACE_PINS { - self.irq_routes.push(create_irq_route_entry( - i, - kvm_bindings::KVM_IRQCHIP_PIC_SLAVE, - i - PIC_MASTER_PINS, - )); - } - self.irq_routes.push(create_irq_route_entry( - i, - kvm_bindings::KVM_IRQCHIP_IOAPIC, - i, - )); - // This unwrap() will never fail, it is safe. - self.gsi_bitmap.set(i as usize).unwrap(); - } - } - - /// Init irq route table in arch aarch64. - #[cfg(target_arch = "aarch64")] - pub fn init_irq_route_table(&mut self) { - for i in 0..IOCHIP_NUM_PINS { - self.irq_routes - .push(create_irq_route_entry(i, KVM_IRQCHIP, i)); - // This unwrap() will never fail, it is safe. - self.gsi_bitmap.set(i as usize).unwrap(); - } - } - - /// Add msi irq route to irq routing table. - pub fn add_msi_route(&mut self, gsi: u32, msi_vector: MsiVector) -> Result<()> { - let mut kroute = IrqRouteEntry { - gsi, - type_: KVM_IRQ_ROUTING_MSI, - flags: 0, - ..Default::default() - }; - kroute.u.msi.address_lo = msi_vector.msg_addr_lo; - kroute.u.msi.address_hi = msi_vector.msg_addr_hi; - kroute.u.msi.data = msi_vector.msg_data; - #[cfg(target_arch = "aarch64")] - { - kroute.flags = kvm_bindings::KVM_MSI_VALID_DEVID; - kroute.u.msi.__bindgen_anon_1.devid = msi_vector.dev_id; - } - self.irq_routes.push(kroute); - - Ok(()) - } - - fn remove_irq_route(&mut self, gsi: u32) { - while let Some((index, _)) = self - .irq_routes - .iter() - .enumerate() - .find(|(_, e)| e.gsi == gsi) - { - self.irq_routes.remove(index); - } - } - - /// Update msi irq route to irq routing table. - pub fn update_msi_route(&mut self, gsi: u32, msi_vector: MsiVector) -> Result<()> { - self.remove_irq_route(gsi); - self.add_msi_route(gsi, msi_vector) - .with_context(|| "Failed to add msi route")?; - - Ok(()) - } - - /// Allocate free gsi number. - pub fn allocate_gsi(&mut self) -> Result { - let free_gsi = self - .gsi_bitmap - .find_next_zero(0) - .with_context(|| "Failed to get new free gsi")?; - self.gsi_bitmap.set(free_gsi)?; - Ok(free_gsi as u32) - } - - /// Release gsi number to free. - /// - /// # Notions - /// - /// If registered irqfd with this gsi, it's necessary to unregister irqfd first. - pub fn release_gsi(&mut self, gsi: u32) -> Result<()> { - self.gsi_bitmap - .clear(gsi as usize) - .with_context(|| "Failed to release gsi")?; - self.remove_irq_route(gsi); - Ok(()) - } - - /// Get `IrqRouteEntry` by given gsi number. - /// A gsi number may have several entries. If no gsi number in table, is will - /// return an empty vector. - pub fn get_irq_route_entry(&self, gsi: u32) -> Vec { - let mut entries = Vec::new(); - for entry in self.irq_routes.iter() { - if gsi == entry.gsi { - entries.push(*entry); - } - } - - entries - } -} - -/// Basic data for msi vector. -#[derive(Copy, Clone, Default)] -pub struct MsiVector { - pub msg_addr_lo: u32, - pub msg_addr_hi: u32, - pub msg_data: u32, - pub masked: bool, - #[cfg(target_arch = "aarch64")] - pub dev_id: u32, -} - -#[cfg(test)] -mod tests { - use super::super::KVMFds; - - #[test] - fn test_alloc_and_release_gsi() { - let kvm_fds = KVMFds::new(); - if kvm_fds.vm_fd.is_none() { - return; - } - let mut irq_route_table = kvm_fds.irq_route_table.lock().unwrap(); - irq_route_table.init_irq_route_table(); - - #[cfg(target_arch = "x86_64")] - { - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 24); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 25); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 26); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 27); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 28); - assert!(irq_route_table.release_gsi(26).is_ok()); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 26); - } - #[cfg(target_arch = "aarch64")] - { - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 192); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 193); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 194); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 195); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 196); - assert!(irq_route_table.release_gsi(195).is_ok()); - assert_eq!(irq_route_table.allocate_gsi().unwrap(), 195); - } - } -} diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 1fe70b851..d9bf645aa 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -10,38 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod interrupt; +use std::sync::Arc; -pub use interrupt::MsiVector; - -use std::mem::{align_of, size_of}; -use std::sync::{Arc, Mutex}; - -use anyhow::{bail, Context, Result}; use arc_swap::ArcSwap; -use kvm_bindings::*; use kvm_ioctls::{Kvm, VmFd}; use log::error; use once_cell::sync::Lazy; -use vmm_sys_util::{eventfd::EventFd, ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; - -use interrupt::{IrqRoute, IrqRouteEntry, IrqRouteTable}; - -// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h -pub const KVM_SIGNAL_MSI: u32 = 0x4020_aea5; - -// See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h -ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); -ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); -ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip); -ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] pub struct KVMFds { pub fd: Option, pub vm_fd: Option>, - pub irq_route_table: Mutex, } impl KVMFds { @@ -55,11 +35,9 @@ impl KVMFds { return KVMFds::default(); } }; - let irq_route_table = Mutex::new(IrqRouteTable::new(&fd)); KVMFds { fd: Some(fd), vm_fd: Some(Arc::new(vm_fd)), - irq_route_table, } } Err(e) => { @@ -68,62 +46,6 @@ impl KVMFds { } } } - - /// Sets the gsi routing table entries. It will overwrite previously set entries. - pub fn commit_irq_routing(&self) -> Result<()> { - let routes = self.irq_route_table.lock().unwrap().irq_routes.clone(); - - let layout = std::alloc::Layout::from_size_align( - size_of::() + routes.len() * size_of::(), - std::cmp::max(align_of::(), align_of::()), - )?; - - // SAFETY: data in `routes` is reliable. - unsafe { - let irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; - if irq_routing.is_null() { - bail!("Failed to alloc irq routing"); - } - (*irq_routing).nr = routes.len() as u32; - (*irq_routing).flags = 0; - let entries: &mut [IrqRouteEntry] = (*irq_routing).entries.as_mut_slice(routes.len()); - entries.copy_from_slice(&routes); - - let ret = self - .vm_fd - .as_ref() - .unwrap() - .set_gsi_routing(&*irq_routing) - .with_context(|| "Failed to set gsi routing"); - - std::alloc::dealloc(irq_routing as *mut u8, layout); - ret - } - } - - pub fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { - self.vm_fd - .as_ref() - .unwrap() - .register_irqfd(fd, gsi) - .with_context(|| format!("Failed to register irqfd: gsi {}.", gsi)) - } - - pub fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()> { - self.vm_fd - .as_ref() - .unwrap() - .unregister_irqfd(fd, gsi) - .with_context(|| format!("Failed to unregister irqfd: gsi {}.", gsi)) - } - - pub fn set_irq_line(&self, irq: u32, level: bool) -> Result<()> { - self.vm_fd - .as_ref() - .unwrap() - .set_irq_line(irq, level) - .with_context(|| format!("Failed to set irq {} level {:?}.", irq, level)) - } } pub static KVM_FDS: Lazy> = Lazy::new(|| ArcSwap::from(Arc::new(KVMFds::new()))); diff --git a/hypervisor_refactor/src/kvm/interrupt.rs b/hypervisor_refactor/src/kvm/interrupt.rs index fe01e26ca..33d5604ce 100644 --- a/hypervisor_refactor/src/kvm/interrupt.rs +++ b/hypervisor_refactor/src/kvm/interrupt.rs @@ -14,10 +14,11 @@ use std::mem::{align_of, size_of}; use std::sync::Arc; use anyhow::{bail, Context, Result}; -use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP}; +use kvm_bindings::{KVMIO, KVM_IRQ_ROUTING_IRQCHIP, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::{Cap, Kvm, VmFd}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; +use devices::pci::MsiVector; use util::bitmap::Bitmap; pub(crate) type IrqRoute = kvm_bindings::kvm_irq_routing; @@ -131,18 +132,68 @@ impl IrqRouteTable { } } - /// Get `IrqRouteEntry` by given gsi number. - /// A gsi number may have several entries. If no gsi number in table, is will - /// return an empty vector. - pub fn get_irq_route_entry(&self, gsi: u32) -> Vec { - let mut entries = Vec::new(); - for entry in self.irq_routes.iter() { - if gsi == entry.gsi { - entries.push(*entry); - } + /// Add msi irq route to irq routing table. + pub fn add_msi_route(&mut self, gsi: u32, msi_vector: MsiVector) -> Result<()> { + let mut kroute = IrqRouteEntry { + gsi, + type_: KVM_IRQ_ROUTING_MSI, + flags: 0, + ..Default::default() + }; + kroute.u.msi.address_lo = msi_vector.msg_addr_lo; + kroute.u.msi.address_hi = msi_vector.msg_addr_hi; + kroute.u.msi.data = msi_vector.msg_data; + #[cfg(target_arch = "aarch64")] + { + kroute.flags = kvm_bindings::KVM_MSI_VALID_DEVID; + kroute.u.msi.__bindgen_anon_1.devid = msi_vector.dev_id; + } + self.irq_routes.push(kroute); + + Ok(()) + } + + fn remove_irq_route(&mut self, gsi: u32) { + while let Some((index, _)) = self + .irq_routes + .iter() + .enumerate() + .find(|(_, e)| e.gsi == gsi) + { + self.irq_routes.remove(index); } + } + + /// Update msi irq route to irq routing table. + pub fn update_msi_route(&mut self, gsi: u32, msi_vector: MsiVector) -> Result<()> { + self.remove_irq_route(gsi); + self.add_msi_route(gsi, msi_vector) + .with_context(|| "Failed to add msi route")?; + + Ok(()) + } + + /// Allocate free gsi number. + pub fn allocate_gsi(&mut self) -> Result { + let free_gsi = self + .gsi_bitmap + .find_next_zero(0) + .with_context(|| "Failed to get new free gsi")?; + self.gsi_bitmap.set(free_gsi)?; + Ok(free_gsi as u32) + } - entries + /// Release gsi number to free. + /// + /// # Notions + /// + /// If registered irqfd with this gsi, it's necessary to unregister irqfd first. + pub fn release_gsi(&mut self, gsi: u32) -> Result<()> { + self.gsi_bitmap + .clear(gsi as usize) + .with_context(|| "Failed to release gsi")?; + self.remove_irq_route(gsi); + Ok(()) } /// Sets the gsi routing table entries. It will overwrite previously set entries. @@ -177,8 +228,10 @@ impl IrqRouteTable { #[cfg(test)] mod tests { - use super::get_maximum_gsi_cnt; - use crate::kvm::KvmHypervisor; + use std::sync::{Arc, Mutex}; + + use super::{get_maximum_gsi_cnt, IrqRouteTable}; + use crate::kvm::{KVMInterruptManager, KvmHypervisor}; #[test] fn test_get_maximum_gsi_cnt() { @@ -188,4 +241,41 @@ mod tests { } assert!(get_maximum_gsi_cnt(kvm_hyp.fd.as_ref().unwrap()) > 0); } + + #[test] + fn test_alloc_and_release_gsi() { + let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + if kvm_hyp.vm_fd.is_none() { + return; + } + let irq_route_table = Mutex::new(IrqRouteTable::new(&kvm_hyp.fd.as_ref().unwrap())); + let irq_manager = Arc::new(KVMInterruptManager::new( + true, + kvm_hyp.vm_fd.clone().unwrap(), + irq_route_table, + )); + let mut irq_route_table = irq_manager.irq_route_table.lock().unwrap(); + irq_route_table.init_irq_route_table(); + + #[cfg(target_arch = "x86_64")] + { + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 24); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 25); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 26); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 27); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 28); + assert!(irq_route_table.release_gsi(26).is_ok()); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 26); + } + #[cfg(target_arch = "aarch64")] + { + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 192); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 193); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 194); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 195); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 196); + assert!(irq_route_table.release_gsi(195).is_ok()); + assert_eq!(irq_route_table.allocate_gsi().unwrap(), 195); + } + } } diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index 99744bbaa..f9f33d5a2 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -38,7 +38,7 @@ use kvm_ioctls::{Cap, Kvm, VcpuExit, VcpuFd, VmFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info}; use vmm_sys_util::{ - ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, + eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, signal::register_signal_handler, }; @@ -55,15 +55,14 @@ use cpu::{ ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuError, CpuLifecycleState, RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, }; +use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, TriggerMode}; #[cfg(target_arch = "aarch64")] use devices::{ GICVersion, GICv2, GICv3, GICv3ItsState, GICv3State, ICGICConfig, InterruptController, + GIC_IRQ_INTERNAL, }; use interrupt::IrqRouteTable; -use machine_manager::{ - config::{MachineType, VmConfig}, - machine::HypervisorType, -}; +use machine_manager::machine::HypervisorType; #[cfg(target_arch = "aarch64")] use migration::{ snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, @@ -76,6 +75,7 @@ use util::test_helper::is_test_enabled; pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; pub const KVM_SET_USER_MEMORY_REGION: u32 = 0x4020_ae46; pub const KVM_IOEVENTFD: u32 = 0x4040_ae79; +pub const KVM_SIGNAL_MSI: u32 = 0x4020_aea5; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iow_nr!(KVM_GET_DIRTY_LOG, KVMIO, 0x42, kvm_dirty_log); @@ -87,6 +87,10 @@ ioctl_ior_nr!(KVM_GET_CLOCK, KVMIO, 0x7c, kvm_clock_data); ioctl_ior_nr!(KVM_GET_REGS, KVMIO, 0x81, kvm_regs); ioctl_ior_nr!(KVM_GET_SREGS, KVMIO, 0x83, kvm_sregs); ioctl_ior_nr!(KVM_GET_FPU, KVMIO, 0x8c, kvm_fpu); +ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); +ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); +ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip); +ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] @@ -96,7 +100,6 @@ pub struct KvmHypervisor { pub mem_slots: Arc>>, #[cfg(target_arch = "aarch64")] pub irq_chip: Option>, - pub irq_route_table: Option>, } impl KvmHypervisor { @@ -113,14 +116,13 @@ impl KvmHypervisor { } })) }; - let irq_route_table = Mutex::new(IrqRouteTable::new(&kvm_fd)); + Ok(KvmHypervisor { fd: Some(kvm_fd), vm_fd, mem_slots: Arc::new(Mutex::new(HashMap::new())), #[cfg(target_arch = "aarch64")] irq_chip: None, - irq_route_table: Some(irq_route_table), }) } Err(e) => { @@ -136,13 +138,6 @@ impl KvmHypervisor { self.mem_slots.clone(), ))) } - - fn init_irq_route_table(&self) -> Result<()> { - let irq_route_table = self.irq_route_table.as_ref().unwrap(); - let mut locked_irq_route_table = irq_route_table.lock().unwrap(); - locked_irq_route_table.init_irq_route_table(); - locked_irq_route_table.commit_irq_routing(self.vm_fd.as_ref().unwrap()) - } } impl HypervisorOps for KvmHypervisor { @@ -168,7 +163,6 @@ impl HypervisorOps for KvmHypervisor { fn create_interrupt_controller( &mut self, gic_conf: &ICGICConfig, - vm_config: &VmConfig, ) -> Result> { gic_conf.check_sanity()?; @@ -203,32 +197,22 @@ impl HypervisorOps for KvmHypervisor { Ok(Arc::new(InterruptController::new(gicv2))) }; - let interrupt_controller = match &gic_conf.version { + match &gic_conf.version { Some(GICVersion::GICv3) => create_gicv3(), Some(GICVersion::GICv2) => create_gicv2(), // Try v3 by default if no version specified. None => create_gicv3().or_else(|_| create_gicv2()), - }; - - if vm_config.machine_config.mach_type == MachineType::StandardVm { - self.init_irq_route_table()?; } - - interrupt_controller } #[cfg(target_arch = "x86_64")] - fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()> { + fn create_interrupt_controller(&mut self) -> Result<()> { self.vm_fd .as_ref() .unwrap() .create_irq_chip() .with_context(|| HypervisorError::CrtIrqchipErr)?; - if vm_config.machine_config.mach_type == MachineType::StandardVm { - self.init_irq_route_table()?; - } - Ok(()) } @@ -248,6 +232,26 @@ impl HypervisorOps for KvmHypervisor { vcpu_fd, ))) } + + fn create_irq_manager(&mut self) -> Result { + let kvm = Kvm::new().unwrap(); + let irqfd_enable = kvm.check_extension(Cap::Irqfd); + let irq_route_table = Mutex::new(IrqRouteTable::new(self.fd.as_ref().unwrap())); + let irq_manager = Arc::new(KVMInterruptManager::new( + irqfd_enable, + self.vm_fd.clone().unwrap(), + irq_route_table, + )); + let mut locked_irq_route_table = irq_manager.irq_route_table.lock().unwrap(); + locked_irq_route_table.init_irq_route_table(); + locked_irq_route_table.commit_irq_routing(self.vm_fd.as_ref().unwrap())?; + drop(locked_irq_route_table); + + Ok(IrqManager { + line_irq_manager: Some(irq_manager.clone()), + msi_irq_manager: Some(irq_manager), + }) + } } impl MigrateOps for KvmHypervisor { @@ -598,6 +602,186 @@ impl CPUHypervisorOps for KvmCpu { } } +struct KVMInterruptManager { + pub irqfd_cap: bool, + pub vm_fd: Arc, + pub irq_route_table: Mutex, +} + +impl KVMInterruptManager { + pub fn new(irqfd_cap: bool, vm_fd: Arc, irq_route_table: Mutex) -> Self { + KVMInterruptManager { + irqfd_cap, + vm_fd, + irq_route_table, + } + } + + #[cfg(target_arch = "x86_64")] + pub fn arch_map_irq(&self, gsi: u32) -> u32 { + gsi + } + + #[cfg(target_arch = "aarch64")] + pub fn arch_map_irq(&self, gsi: u32) -> u32 { + let irq = gsi + GIC_IRQ_INTERNAL; + let irqtype = KVM_ARM_IRQ_TYPE_SPI; + irqtype << KVM_ARM_IRQ_TYPE_SHIFT | irq + } +} + +impl LineIrqManager for KVMInterruptManager { + fn irqfd_enable(&self) -> bool { + self.irqfd_cap + } + + fn register_irqfd( + &self, + irq_fd: Arc, + irq: u32, + trigger_mode: TriggerMode, + ) -> Result<()> { + if !self.irqfd_cap { + bail!("Hypervisor doesn't support irqfd feature!") + } + + match trigger_mode { + TriggerMode::Edge => { + self.vm_fd + .register_irqfd(&irq_fd, irq as u32) + .map_err(|e| { + error!("Failed to register irq, error is {:?}", e); + e + })?; + } + _ => { + bail!("Unsupported registering irq fd for interrupt of level mode."); + } + } + + Ok(()) + } + + fn unregister_irqfd(&self, irq_fd: Arc, irq: u32) -> Result<()> { + self.vm_fd + .unregister_irqfd(&irq_fd, irq as u32) + .map_err(|e| { + error!("Failed to unregister irq, error is {:?}", e); + e + })?; + + Ok(()) + } + + fn set_irq_line(&self, gsi: u32, level: bool) -> Result<()> { + let kvm_irq = self.arch_map_irq(gsi); + self.vm_fd + .set_irq_line(kvm_irq, level) + .with_context(|| format!("Failed to set irq {} level {:?}.", kvm_irq, level))?; + + Ok(()) + } + + fn write_irqfd(&self, irq_fd: Arc) -> Result<()> { + irq_fd.write(1)?; + + Ok(()) + } +} + +impl MsiIrqManager for KVMInterruptManager { + fn allocate_irq(&self, vector: MsiVector) -> Result { + let mut locked_irq_route_table = self.irq_route_table.lock().unwrap(); + let gsi = locked_irq_route_table.allocate_gsi().map_err(|e| { + error!("Failed to allocate gsi, error is {:?}", e); + e + })?; + + locked_irq_route_table + .add_msi_route(gsi, vector) + .map_err(|e| { + error!("Failed to add MSI-X route, error is {:?}", e); + e + })?; + + locked_irq_route_table + .commit_irq_routing(&self.vm_fd.clone()) + .map_err(|e| { + error!("Failed to commit irq routing, error is {:?}", e); + e + })?; + + Ok(gsi) + } + + fn release_irq(&self, irq: u32) -> Result<()> { + let mut locked_irq_route_table = self.irq_route_table.lock().unwrap(); + + locked_irq_route_table.release_gsi(irq).map_err(|e| { + error!("Failed to release gsi, error is {:?}", e); + e + }) + } + + fn register_irqfd(&self, irq_fd: Arc, irq: u32) -> Result<()> { + self.vm_fd.register_irqfd(&irq_fd, irq).map_err(|e| { + error!("Failed to register irq, error is {:?}", e); + e + })?; + + Ok(()) + } + + fn unregister_irqfd(&self, irq_fd: Arc, irq: u32) -> Result<()> { + self.vm_fd.unregister_irqfd(&irq_fd, irq).map_err(|e| { + error!("Failed to unregister irq, error is {:?}", e); + e + })?; + + Ok(()) + } + + fn trigger(&self, irq_fd: Option>, vector: MsiVector, dev_id: u32) -> Result<()> { + if irq_fd.is_some() { + irq_fd.unwrap().write(1)?; + } else { + #[cfg(target_arch = "aarch64")] + let flags: u32 = kvm_bindings::KVM_MSI_VALID_DEVID; + #[cfg(target_arch = "x86_64")] + let flags: u32 = 0; + + let kvm_msi = kvm_bindings::kvm_msi { + address_lo: vector.msg_addr_lo, + address_hi: vector.msg_addr_hi, + data: vector.msg_data, + flags, + devid: dev_id, + pad: [0; 12], + }; + + self.vm_fd.signal_msi(kvm_msi)?; + } + + Ok(()) + } + + fn update_route_table(&self, gsi: u32, vector: MsiVector) -> Result<()> { + let mut locked_irq_route_table = self.irq_route_table.lock().unwrap(); + locked_irq_route_table + .update_msi_route(gsi, vector) + .map_err(|e| { + error!("Failed to update MSI-X route, error is {:?}", e); + e + })?; + locked_irq_route_table + .commit_irq_routing(&self.vm_fd.clone()) + .map_err(|e| { + error!("Failed to commit irq routing, error is {:?}", e); + e + }) + } +} + #[cfg(test)] mod test { use std::sync::{Arc, Mutex}; diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index 6acd86072..a35f28206 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -24,9 +24,9 @@ use anyhow::Result; use address_space::AddressSpace; use cpu::CPUHypervisorOps; +use devices::IrqManager; #[cfg(target_arch = "aarch64")] use devices::{ICGICConfig, InterruptController}; -use machine_manager::config::VmConfig; use machine_manager::machine::HypervisorType; pub trait HypervisorOps: Send + Sync + Any { @@ -44,12 +44,13 @@ pub trait HypervisorOps: Send + Sync + Any { fn create_interrupt_controller( &mut self, gic_conf: &ICGICConfig, - vm_config: &VmConfig, ) -> Result>; #[cfg(target_arch = "x86_64")] - fn create_interrupt_controller(&mut self, vm_config: &VmConfig) -> Result<()>; + fn create_interrupt_controller(&mut self) -> Result<()>; fn create_hypervisor_cpu(&self, vcpu_id: u8) -> Result>; + + fn create_irq_manager(&mut self) -> Result; } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index bc5b153a0..cd802a723 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -76,7 +76,7 @@ impl MachineOps for LightMachine { .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } - fn init_interrupt_controller(&mut self, vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { + fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], @@ -103,9 +103,12 @@ impl MachineOps for LightMachine { let hypervisor = self.get_hypervisor(); let mut locked_hypervisor = hypervisor.lock().unwrap(); - self.base.irq_chip = - Some(locked_hypervisor.create_interrupt_controller(&intc_conf, vm_config)?); - self.base.irq_chip.as_ref().unwrap().realize() + self.base.irq_chip = Some(locked_hypervisor.create_interrupt_controller(&intc_conf)?); + self.base.irq_chip.as_ref().unwrap().realize()?; + + let irq_manager = locked_hypervisor.create_irq_manager()?; + self.base.sysbus.irq_manager = irq_manager.line_irq_manager; + Ok(()) } fn add_rtc_device(&mut self) -> Result<()> { @@ -182,8 +185,7 @@ impl MachineOps for LightMachine { &cpu_config, )?); - locked_vm - .init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus), vm_config)?; + locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; locked_vm.cpu_post_init(&cpu_config)?; diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 142874521..272fd9705 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -16,8 +16,7 @@ use std::mem::size_of; use std::ops::Deref; use std::sync::{Arc, Mutex}; -use anyhow::{bail, Context, Result}; -use kvm_bindings::{KVM_ARM_IRQ_TYPE_SHIFT, KVM_ARM_IRQ_TYPE_SPI}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, info, warn}; use vmm_sys_util::eventfd::EventFd; @@ -47,11 +46,11 @@ use devices::legacy::Ramfb; use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; -use devices::pci::{InterruptHandler, PciDevOps, PciHost, PciIntxState}; +use devices::pci::{PciDevOps, PciHost, PciIntxState}; use devices::sysbus::SysBusDevType; -use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_INTERNAL, GIC_IRQ_MAX}; -use hypervisor::kvm::*; +use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_MAX}; use hypervisor_refactor::kvm::aarch64::*; +use hypervisor_refactor::kvm::*; #[cfg(feature = "ramfb")] use machine_manager::config::parse_ramfb; use machine_manager::config::ShutdownAction; @@ -428,7 +427,7 @@ impl MachineOps for StdMachine { .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } - fn init_interrupt_controller(&mut self, vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { + fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { let v3 = ICGICv3Config { msi: true, dist_range: MEM_LAYOUT[LayoutEntryType::GicDist as usize], @@ -448,26 +447,25 @@ impl MachineOps for StdMachine { let hypervisor = self.get_hypervisor(); let mut locked_hypervisor = hypervisor.lock().unwrap(); - self.base.irq_chip = - Some(locked_hypervisor.create_interrupt_controller(&intc_conf, vm_config)?); + self.base.irq_chip = Some(locked_hypervisor.create_interrupt_controller(&intc_conf)?); self.base.irq_chip.as_ref().unwrap().realize()?; let root_bus = &self.pci_host.lock().unwrap().root_bus; - let irq_handler = Box::new(move |gsi: u32, level: bool| -> Result<()> { - // The handler is only used to send PCI INTx interrupt. - // PCI INTx interrupt is belong to SPI interrupt type. - let irq = gsi + GIC_IRQ_INTERNAL; - let irqtype = KVM_ARM_IRQ_TYPE_SPI; - let kvm_irq = irqtype << KVM_ARM_IRQ_TYPE_SHIFT | irq; - - KVM_FDS.load().set_irq_line(kvm_irq, level) - }) as InterruptHandler; - - let irq_state = Some(Arc::new(Mutex::new(PciIntxState::new( - IRQ_MAP[IrqEntryType::Pcie as usize].0 as u32, - irq_handler, - )))); - root_bus.lock().unwrap().intx_state = irq_state; + let irq_manager = locked_hypervisor.create_irq_manager()?; + root_bus.lock().unwrap().msi_irq_manager = irq_manager.msi_irq_manager; + let line_irq_manager = irq_manager.line_irq_manager; + if let Some(line_irq_manager) = line_irq_manager.clone() { + let irq_state = Some(Arc::new(Mutex::new(PciIntxState::new( + IRQ_MAP[IrqEntryType::Pcie as usize].0 as u32, + line_irq_manager.clone(), + )))); + root_bus.lock().unwrap().intx_state = irq_state; + } else { + return Err(anyhow!( + "Failed to create intx state: legacy irq manager is none." + )); + } + self.base.sysbus.irq_manager = line_irq_manager; Ok(()) } @@ -585,7 +583,7 @@ impl MachineOps for StdMachine { )?); // Interrupt Controller Chip init - locked_vm.init_interrupt_controller(u64::from(nr_cpus), vm_config)?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; locked_vm.cpu_post_init(&cpu_config)?; @@ -944,7 +942,7 @@ impl AcpiBuilder for StdMachine { for dev in self.base.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); if locked_dev.sysbusdev_base().dev_type == SysBusDevType::PL011 { - uart_irq = locked_dev.sysbusdev_base().res.irq as u32; + uart_irq = locked_dev.sysbusdev_base().irq_state.irq as u32; break; } } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index dace4f746..26d1033cd 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -512,7 +512,7 @@ pub trait MachineOps { /// # Arguments /// /// * `vcpu_count` - The number of vcpu. - fn init_interrupt_controller(&mut self, vcpu_count: u64, vm_config: &VmConfig) -> Result<()>; + fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()>; /// Add RTC device. fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] _mem_size: u64) -> Result<()> { diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index cb3e4c733..7aeeed719 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -14,7 +14,6 @@ use crate::aarch64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] use crate::x86_64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; -use hypervisor::kvm::*; use hypervisor_refactor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index ec5b586c7..012f1a9e7 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -21,7 +21,6 @@ use crate::{ use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; use devices::legacy::FwCfgOps; -use hypervisor::kvm::*; use hypervisor_refactor::kvm::x86_64::*; use hypervisor_refactor::kvm::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; @@ -85,10 +84,15 @@ impl MachineOps for LightMachine { Ok(()) } - fn init_interrupt_controller(&mut self, _vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { + fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { let hypervisor = self.get_hypervisor(); let mut locked_hypervisor = hypervisor.lock().unwrap(); - locked_hypervisor.create_interrupt_controller(vm_config) + locked_hypervisor.create_interrupt_controller()?; + + let irq_manager = locked_hypervisor.create_irq_manager()?; + self.base.sysbus.irq_manager = irq_manager.line_irq_manager; + + Ok(()) } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { @@ -165,8 +169,7 @@ impl MachineOps for LightMachine { let migrate_info = locked_vm.get_migrate_info(); - locked_vm - .init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus), vm_config)?; + locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; // Add mmio devices locked_vm diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index dd989eb6a..299d146c3 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -40,7 +40,6 @@ use devices::legacy::{ SERIAL_ADDR, }; use devices::pci::{PciDevOps, PciHost}; -use hypervisor::kvm::*; use hypervisor_refactor::kvm::x86_64::*; use hypervisor_refactor::kvm::*; #[cfg(feature = "gtk")] @@ -434,10 +433,17 @@ impl MachineOps for StdMachine { Ok(()) } - fn init_interrupt_controller(&mut self, _vcpu_count: u64, vm_config: &VmConfig) -> Result<()> { + fn init_interrupt_controller(&mut self, _vcpu_count: u64) -> Result<()> { let hypervisor = self.get_hypervisor(); let mut locked_hypervisor = hypervisor.lock().unwrap(); - locked_hypervisor.create_interrupt_controller(vm_config) + locked_hypervisor.create_interrupt_controller()?; + + let root_bus = &self.pci_host.lock().unwrap().root_bus; + let irq_manager = locked_hypervisor.create_irq_manager()?; + root_bus.lock().unwrap().msi_irq_manager = irq_manager.msi_irq_manager; + self.base.sysbus.irq_manager = irq_manager.line_irq_manager; + + Ok(()) } fn load_boot_source(&self, fwcfg: Option<&Arc>>) -> Result { @@ -527,7 +533,7 @@ impl MachineOps for StdMachine { nr_cpus, )?; - locked_vm.init_interrupt_controller(u64::from(nr_cpus), vm_config)?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; locked_vm .init_pci_host() diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 2da4384d5..7863080b7 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -43,8 +43,7 @@ use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, pci_ext_cap_next, pci_ext_cap_ver, PciBus, PciDevBase, PciDevOps, }; -use devices::{Device, DeviceBase}; -use hypervisor::kvm::{MsiVector, KVM_FDS}; +use devices::{pci::MsiVector, Device, DeviceBase}; use util::num_ops::ranges_overlap; use util::unix::host_page_size; @@ -461,11 +460,20 @@ impl VfioPciDevice { offset, MSIX_CAP_FUNC_MASK | MSIX_CAP_ENABLE, )?; + + let msi_irq_manager = if let Some(pci_bus) = self.base.parent_bus.upgrade() { + let locked_pci_bus = pci_bus.lock().unwrap(); + locked_pci_bus.get_msi_irq_manager() + } else { + None + }; + let msix = Arc::new(Mutex::new(Msix::new( table_size, table_size / 128, cap_offset as u16, self.dev_id.clone(), + msi_irq_manager, ))); self.base.config.msix = Some(msix.clone()); @@ -490,6 +498,7 @@ impl VfioPciDevice { let parent_bus = self.base.parent_bus.clone(); let dev_id = self.dev_id.clone(); let devfn = self.base.devfn; + let cloned_msix = msix.clone(); let write = move |data: &[u8], _: GuestAddress, offset: u64| -> bool { let mut locked_msix = msix.lock().unwrap(); locked_msix.table[offset as usize..(offset as usize + data.len())] @@ -517,47 +526,24 @@ impl VfioPciDevice { let irq_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); gsi_route.irq_fd = Some(Arc::new(irq_fd)); } + let irq_fd = gsi_route.irq_fd.clone(); + let msi_irq_manager = &cloned_msix.lock().unwrap().msi_irq_manager; + let irq_manager = msi_irq_manager.as_ref().unwrap(); if gsi_route.gsi == -1 { - gsi_route.gsi = match KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .allocate_gsi() - { + gsi_route.gsi = match irq_manager.allocate_irq(msix_vector) { Ok(g) => g as i32, Err(e) => { - error!("Failed to allocate gsi, error is {:?}", e); + error!("Failed to init msix vector {:?}, error is {:?}", vector, e); return true; } }; - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .add_msi_route(gsi_route.gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to add MSI-X route, error is {:?}", e)); - KVM_FDS - .load() - .commit_irq_routing() - .unwrap_or_else(|e| error!("{:?}", e)); - KVM_FDS - .load() - .register_irqfd(gsi_route.irq_fd.as_ref().unwrap(), gsi_route.gsi as u32) + irq_manager + .register_irqfd(irq_fd.unwrap(), gsi_route.gsi as u32) .unwrap_or_else(|e| error!("{:?}", e)); } else { - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .update_msi_route(gsi_route.gsi as u32, msix_vector) - .unwrap_or_else(|e| error!("Failed to update MSI-X route, error is {:?}", e)); - KVM_FDS - .load() - .commit_irq_routing() + irq_manager + .update_route_table(gsi_route.gsi as u32, msix_vector) .unwrap_or_else(|e| error!("{:?}", e)); } @@ -765,22 +751,21 @@ impl VfioPciDevice { fn vfio_unregister_all_irqfd(&mut self) -> Result<()> { let routes = self.gsi_msi_routes.lock().unwrap(); + let msix = self.base.config.msix.as_ref().unwrap(); + let irq_ctrl = &msix.lock().unwrap().msi_irq_manager; for route in routes.iter() { if let Some(fd) = route.irq_fd.as_ref() { - KVM_FDS - .load() - .unregister_irqfd(fd.as_ref(), route.gsi as u32)?; + irq_ctrl + .as_ref() + .unwrap() + .unregister_irqfd(fd.clone(), route.gsi as u32)?; // No need to release gsi. if route.gsi == -1 { continue; } - KVM_FDS - .load() - .irq_route_table - .lock() - .unwrap() - .release_gsi(route.gsi as u32)?; + + irq_ctrl.as_ref().unwrap().release_irq(route.gsi as u32)?; } } Ok(()) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 26e8f0845..32a5451c5 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -140,10 +140,9 @@ impl VirtioMmioDevice { VirtioMmioDevice { base: SysBusDevBase { - base: DeviceBase::default(), dev_type: SysBusDevType::VirtioMmio, - res: SysRes::default(), interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), + ..Default::default() }, device, host_notify_info: HostNotifyInfo::new(queue_num), @@ -159,6 +158,10 @@ impl VirtioMmioDevice { region_size: u64, #[cfg(target_arch = "x86_64")] bs: &Arc>, ) -> Result>> { + if region_base >= sysbus.mmio_region.1 { + bail!("Mmio region space exhausted."); + } + self.set_sys_resource(sysbus, region_base, region_size)?; self.assign_interrupt_cb(); self.device .lock() @@ -166,10 +169,6 @@ impl VirtioMmioDevice { .realize() .with_context(|| "Failed to realize virtio.")?; - if region_base >= sysbus.mmio_region.1 { - bail!("Mmio region space exhausted."); - } - self.set_sys_resource(sysbus, region_base, region_size)?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size, "VirtioMmio")?; @@ -232,7 +231,7 @@ impl VirtioMmioDevice { } fn assign_interrupt_cb(&mut self) { - let interrupt_evt = self.base.interrupt_evt.clone(); + let irq_state = self.base.irq_state.clone(); let locked_dev = self.device.lock().unwrap(); let virtio_base = locked_dev.virtio_base(); let device_status = virtio_base.device_status.clone(); @@ -257,10 +256,7 @@ impl VirtioMmioDevice { VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; interrupt_status.fetch_or(status, Ordering::SeqCst); - let interrupt = interrupt_evt.as_ref().unwrap(); - interrupt - .write(1) - .with_context(|| VirtioError::EventFdWrite)?; + irq_state.trigger_irq()?; Ok(()) }, @@ -528,7 +524,7 @@ impl SysBusDevOps for VirtioMmioDevice { ret } - fn get_sys_resource(&mut self) -> Option<&mut SysRes> { + fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 1b7dfb040..ac73f7562 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1088,11 +1088,10 @@ impl PciDevOps for VirtioPciDevice { let nvectors = self.device.lock().unwrap().queue_num() + 1; init_msix( + &mut self.base, VIRTIO_PCI_MSIX_BAR_IDX as usize, nvectors as u32, - &mut self.base.config, self.dev_id.clone(), - &self.base.base.id, None, None, )?; @@ -1552,13 +1551,11 @@ mod tests { false, ); - let id = virtio_pci.name(); assert!(init_msix( + &mut virtio_pci.base, VIRTIO_PCI_MSIX_BAR_IDX as usize, (virtio_dev.lock().unwrap().queue_num() + 1) as u32, - &mut virtio_pci.base.config, virtio_pci.dev_id.clone(), - &id, None, None, ) @@ -1709,20 +1706,18 @@ mod tests { #[cfg(target_arch = "aarch64")] virtio_pci.base.config.set_interrupt_pin(); - let id = virtio_pci.name(); init_msix( + &mut virtio_pci.base, VIRTIO_PCI_MSIX_BAR_IDX as usize, virtio_pci.device.lock().unwrap().queue_num() as u32 + 1, - &mut virtio_pci.base.config, virtio_pci.dev_id.clone(), - &id, None, None, ) .unwrap(); init_intx( - id, + virtio_pci.name(), &mut virtio_pci.base.config, virtio_pci.base.parent_bus.clone(), virtio_pci.base.devfn, -- Gitee From 4a7a225c76410bbd852047cf1f06f01f6dc21881 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 5 Jan 2024 09:16:21 +0800 Subject: [PATCH 1577/2187] VFIO: Decouple VFIO Devices from KVM Aims to decouple VFIO devices from the KVM implementation by moving the interface to the hypervisor layer. This allows for a more modular and flexible approach to managing vfio devices. Signed-off-by: Jinhao Gao --- Cargo.lock | 2 ++ hypervisor_refactor/src/kvm/mod.rs | 18 ++++++++++++++++++ hypervisor_refactor/src/lib.rs | 3 +++ machine/src/lib.rs | 6 +++++- vfio/Cargo.toml | 2 ++ vfio/src/lib.rs | 26 +------------------------- vfio/src/vfio_dev.rs | 6 +++--- 7 files changed, 34 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df3283b22..82e16796c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1834,10 +1834,12 @@ dependencies = [ "byteorder", "devices", "hypervisor", + "hypervisor_refactor", "kvm-bindings", "kvm-ioctls", "libc", "log", + "machine_manager", "once_cell", "thiserror", "util", diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index f9f33d5a2..bbed604ff 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -34,6 +34,7 @@ use std::time::Duration; use anyhow::{anyhow, bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; +use kvm_ioctls::DeviceFd; use kvm_ioctls::{Cap, Kvm, VcpuExit, VcpuFd, VmFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info}; @@ -252,6 +253,23 @@ impl HypervisorOps for KvmHypervisor { msi_irq_manager: Some(irq_manager), }) } + + fn create_vfio_device(&self) -> Option { + let mut device = kvm_create_device { + type_: kvm_device_type_KVM_DEV_TYPE_VFIO, + fd: 0, + flags: 0, + }; + let vfio_device_fd = match self.vm_fd.as_ref().unwrap().create_device(&mut device) { + Ok(fd) => Some(fd), + Err(_) => { + error!("Failed to create VFIO device."); + None + } + }; + + vfio_device_fd + } } impl MigrateOps for KvmHypervisor { diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor_refactor/src/lib.rs index a35f28206..f53f23267 100644 --- a/hypervisor_refactor/src/lib.rs +++ b/hypervisor_refactor/src/lib.rs @@ -21,6 +21,7 @@ use std::any::Any; use std::sync::Arc; use anyhow::Result; +use kvm_ioctls::DeviceFd; use address_space::AddressSpace; use cpu::CPUHypervisorOps; @@ -53,4 +54,6 @@ pub trait HypervisorOps: Send + Sync + Any { -> Result>; fn create_irq_manager(&mut self) -> Result; + + fn create_vfio_device(&self) -> Option; } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 26d1033cd..b30412bbf 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -104,7 +104,7 @@ use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; -use vfio::{VfioDevice, VfioPciDevice}; +use vfio::{VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(feature = "virtio_gpu")] use virtio::Gpu; use virtio::{ @@ -1298,6 +1298,10 @@ pub trait MachineOps { } fn add_vfio_device(&mut self, cfg_args: &str, hotplug: bool) -> Result<()> { + let hypervisor = self.get_hypervisor(); + let locked_hypervisor = hypervisor.lock().unwrap(); + *KVM_DEVICE_FD.lock().unwrap() = locked_hypervisor.create_vfio_device(); + let device_cfg: VfioConfig = parse_vfio(cfg_args)?; let bdf = get_pci_bdf(cfg_args)?; let multifunc = get_multi_function(cfg_args)?; diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index 5f7611f5c..28b1e1201 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -19,5 +19,7 @@ vfio-bindings = "0.3" once_cell = "1.18.0" address_space = { path = "../address_space" } hypervisor = { path = "../hypervisor" } +hypervisor_refactor = { path = "../hypervisor_refactor"} +machine_manager = { path = "../machine_manager" } util = { path = "../util" } devices = { path = "../devices" } diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 596a22945..d6f114b07 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -28,37 +28,13 @@ use std::collections::HashMap; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; -use kvm_bindings::{kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO}; use kvm_ioctls::DeviceFd; -use log::error; use once_cell::sync::Lazy; -use hypervisor::kvm::KVM_FDS; use vfio_dev::VfioGroup; -pub static KVM_DEVICE_FD: Lazy> = Lazy::new(create_kvm_vfio_device); +pub static KVM_DEVICE_FD: Lazy>> = Lazy::new(|| Mutex::new(None)); pub static CONTAINERS: Lazy>>>> = Lazy::new(|| Mutex::new(HashMap::new())); pub static GROUPS: Lazy>>> = Lazy::new(|| Mutex::new(HashMap::new())); - -fn create_kvm_vfio_device() -> Option { - let mut device = kvm_create_device { - type_: kvm_device_type_KVM_DEV_TYPE_VFIO, - fd: 0, - flags: 0, - }; - match KVM_FDS - .load() - .vm_fd - .as_ref() - .unwrap() - .create_device(&mut device) - { - Ok(fd) => Some(fd), - Err(e) => { - error!("{:?}", e); - None - } - } -} diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index 70f99625c..fbbb15413 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -369,11 +369,11 @@ impl VfioGroup { attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), addr: &self.fd.as_raw_fd() as *const i32 as u64, }; - match KVM_DEVICE_FD.as_ref() { + match KVM_DEVICE_FD.lock().unwrap().as_ref() { Some(fd) => fd .set_device_attr(&attr) .with_context(|| "Failed to add group to kvm device.")?, - None => bail!("Failed to create kvm device."), + None => bail!("Kvm device hasn't been created."), } Ok(()) } @@ -389,7 +389,7 @@ impl VfioGroup { attr: u64::from(KVM_DEV_VFIO_GROUP_DEL), addr: &self.fd.as_raw_fd() as *const i32 as u64, }; - match KVM_DEVICE_FD.as_ref() { + match KVM_DEVICE_FD.lock().unwrap().as_ref() { Some(fd) => fd .set_device_attr(&attr) .with_context(|| "Failed to delete group from kvm device.")?, -- Gitee From 7938b15b7d61f5cbbe3de9b30738ef6bfce2b47b Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 5 Jan 2024 21:19:55 +0800 Subject: [PATCH 1578/2187] Hypervisor: Move the interface of migration related to KvmDevice to hypervisor Introduce a living migration interface for the hypervisor Ops and migrates the existing transfering state interface related to KVM device to the hypervisor refactor module. Signed-off-by: Jinhao Gao --- hypervisor_refactor/src/kvm/mod.rs | 21 +++++++++++++------ .../src/kvm}/vm_state.rs | 20 ++++++++++++------ machine/src/lib.rs | 2 -- machine/src/x86_64/micro.rs | 9 +++----- machine/src/x86_64/standard.rs | 10 ++++----- migration/src/lib.rs | 2 ++ 6 files changed, 38 insertions(+), 26 deletions(-) rename {machine/src => hypervisor_refactor/src/kvm}/vm_state.rs (90%) diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index bbed604ff..94516edb3 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -18,6 +18,9 @@ pub mod x86_64; mod interrupt; mod listener; +#[cfg(target_arch = "x86_64")] +pub mod vm_state; + #[cfg(target_arch = "aarch64")] pub use self::aarch64::{KVM_REG_ARM_MPIDR_EL1, KVM_REG_ARM_TIMER_CNT}; #[cfg(target_arch = "aarch64")] @@ -65,11 +68,8 @@ use devices::{ use interrupt::IrqRouteTable; use machine_manager::machine::HypervisorType; #[cfg(target_arch = "aarch64")] -use migration::{ - snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}, - MigrationManager, -}; -use migration::{MigrateMemSlot, MigrateOps}; +use migration::snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}; +use migration::{MigrateMemSlot, MigrateOps, MigrationManager}; use util::test_helper::is_test_enabled; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h @@ -288,7 +288,6 @@ impl MigrateOps for KvmHypervisor { Arc::new(Mutex::new(mgt_mem_slots)) } - /// Get dirty page bitmap in kvm. fn get_dirty_log(&self, slot: u32, mem_size: u64) -> Result> { self.vm_fd .as_ref() @@ -345,6 +344,16 @@ impl MigrateOps for KvmHypervisor { Ok(()) } + + fn register_instance(&self) -> Result<()> { + #[cfg(target_arch = "x86_64")] + MigrationManager::register_kvm_instance( + vm_state::KvmDeviceState::descriptor(), + Arc::new(vm_state::KvmDevice::new(self.vm_fd.clone().unwrap())), + ); + + Ok(()) + } } pub struct KvmCpu { diff --git a/machine/src/vm_state.rs b/hypervisor_refactor/src/kvm/vm_state.rs similarity index 90% rename from machine/src/vm_state.rs rename to hypervisor_refactor/src/kvm/vm_state.rs index 114c08066..73abfcdae 100644 --- a/machine/src/vm_state.rs +++ b/hypervisor_refactor/src/kvm/vm_state.rs @@ -10,10 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::Arc; + use anyhow::Context; use kvm_bindings::{kvm_clock_data, kvm_irqchip, kvm_pit_state2, KVM_IRQCHIP_IOAPIC}; +use kvm_ioctls::VmFd; -use hypervisor::kvm::KVM_FDS; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; @@ -21,7 +23,15 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; /// Structure to wrapper kvm_device related function. -pub struct KvmDevice {} +pub struct KvmDevice { + vm_fd: Arc, +} + +impl KvmDevice { + pub fn new(vm_fd: Arc) -> Self { + Self { vm_fd } + } +} /// Status of kvm device. /// Kvm device include pit, kvm_clock, irq on x86_64 platform. @@ -36,8 +46,7 @@ pub struct KvmDeviceState { impl StateTransfer for KvmDevice { fn get_state_vec(&self) -> migration::Result> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); + let vm_fd = self.vm_fd.clone(); // save pit let pit_state = vm_fd.get_pit2()?; @@ -64,8 +73,7 @@ impl StateTransfer for KvmDevice { } fn set_state(&self, state: &[u8]) -> migration::Result<()> { - let kvm_fds = KVM_FDS.load(); - let vm_fd = kvm_fds.vm_fd.as_ref().unwrap(); + let vm_fd = self.vm_fd.clone(); let kvm_state = KvmDeviceState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("KVM_DEVICE"))?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index b30412bbf..858c0db59 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -18,8 +18,6 @@ pub mod standard_common; pub mod x86_64; mod micro_common; -#[cfg(target_arch = "x86_64")] -mod vm_state; pub use anyhow::Result; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 012f1a9e7..99bf186c1 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -14,7 +14,6 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use crate::vm_state; use crate::{ micro_common::syscall::syscall_whitelist, LightMachine, MachineBase, MachineError, MachineOps, }; @@ -195,11 +194,9 @@ impl MachineOps for LightMachine { )?); MigrationManager::register_vm_instance(vm.clone()); - MigrationManager::register_kvm_instance( - vm_state::KvmDeviceState::descriptor(), - Arc::new(vm_state::KvmDevice {}), - ); - MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); + let migration_hyp = locked_vm.base.migration_hypervisor.clone(); + migration_hyp.lock().unwrap().register_instance()?; + MigrationManager::register_migration_instance(migration_hyp); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 299d146c3..7d89e489d 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -24,7 +24,7 @@ use super::mch::Mch; use crate::error::MachineError; use crate::standard_common::syscall::syscall_whitelist; use crate::standard_common::{AcpiBuilder, StdMachineOps}; -use crate::{vm_state, MachineBase, MachineOps}; +use crate::{MachineBase, MachineOps}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, AmlBuilder, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, TableLoader, @@ -612,11 +612,9 @@ impl MachineOps for StdMachine { MigrationManager::register_vm_config(locked_vm.get_vm_config()); MigrationManager::register_vm_instance(vm.clone()); - MigrationManager::register_kvm_instance( - vm_state::KvmDeviceState::descriptor(), - Arc::new(vm_state::KvmDevice {}), - ); - MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); + let migration_hyp = locked_vm.base.migration_hypervisor.clone(); + migration_hyp.lock().unwrap().register_instance()?; + MigrationManager::register_migration_instance(migration_hyp); if let Err(e) = MigrationManager::set_status(MigrationStatus::Setup) { bail!("Failed to set migration status {}", e); } diff --git a/migration/src/lib.rs b/migration/src/lib.rs index f431d479a..24e334b51 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -189,4 +189,6 @@ pub trait MigrateOps: Send + Sync { fn start_dirty_log(&self) -> Result<()>; fn stop_dirty_log(&self) -> Result<()>; + + fn register_instance(&self) -> Result<()>; } -- Gitee From 851960b822139234c89a49f96723c8b6105f3adc Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 8 Jan 2024 15:02:08 +0800 Subject: [PATCH 1579/2187] Hypervisor: Remove hypervisor directory Remove the hypervisor directory and cleans up all references to it. All features and functionality previously located in hypervisor have been fully migrated to the hypervisor_refactor directory. So it is safe to remove the directory. Signed-off-by: Jinhao Gao --- Cargo.lock | 22 --------- address_space/Cargo.toml | 1 - cpu/Cargo.toml | 1 - devices/Cargo.toml | 1 - devices/src/sysbus/error.rs | 5 -- hypervisor/Cargo.toml | 17 ------- hypervisor/src/error.rs | 28 ----------- hypervisor/src/kvm/mod.rs | 51 -------------------- hypervisor/src/lib.rs | 18 ------- hypervisor_refactor/src/kvm/aarch64/gicv2.rs | 2 +- hypervisor_refactor/src/kvm/aarch64/gicv3.rs | 8 +-- hypervisor_refactor/src/kvm/interrupt.rs | 4 +- hypervisor_refactor/src/kvm/listener.rs | 12 ++--- hypervisor_refactor/src/kvm/mod.rs | 22 ++++----- machine/Cargo.toml | 1 - machine/src/error.rs | 5 -- machine/src/lib.rs | 5 +- migration/Cargo.toml | 1 - migration/src/error.rs | 5 -- vfio/Cargo.toml | 1 - vfio/src/error.rs | 5 -- virtio/Cargo.toml | 1 - 22 files changed, 23 insertions(+), 193 deletions(-) delete mode 100644 hypervisor/Cargo.toml delete mode 100644 hypervisor/src/error.rs delete mode 100644 hypervisor/src/kvm/mod.rs delete mode 100644 hypervisor/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 82e16796c..b86f26ecb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,6 @@ version = "2.3.0" dependencies = [ "anyhow", "arc-swap", - "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", @@ -308,7 +307,6 @@ name = "cpu" version = "2.3.0" dependencies = [ "anyhow", - "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", @@ -337,7 +335,6 @@ dependencies = [ "chardev_backend", "cpu", "drm-fourcc", - "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", @@ -720,21 +717,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hypervisor" -version = "2.3.0" -dependencies = [ - "anyhow", - "arc-swap", - "kvm-bindings", - "kvm-ioctls", - "log", - "once_cell", - "thiserror", - "util", - "vmm-sys-util", -] - [[package]] name = "hypervisor_refactor" version = "2.3.0" @@ -928,7 +910,6 @@ dependencies = [ "boot_loader", "cpu", "devices", - "hypervisor", "hypervisor_refactor", "kvm-bindings", "libc", @@ -1005,7 +986,6 @@ name = "migration" version = "2.3.0" dependencies = [ "anyhow", - "hypervisor", "kvm-bindings", "kvm-ioctls", "log", @@ -1833,7 +1813,6 @@ dependencies = [ "anyhow", "byteorder", "devices", - "hypervisor", "hypervisor_refactor", "kvm-bindings", "kvm-ioctls", @@ -1864,7 +1843,6 @@ dependencies = [ "byteorder", "chardev_backend", "devices", - "hypervisor", "kvm-ioctls", "libc", "log", diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 83e964ff5..997abd3ec 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -16,7 +16,6 @@ arc-swap = "1.6.0" nix = "0.26.2" thiserror = "1.0" anyhow = "1.0" -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 1dccfcf54..b48b16d0d 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -15,7 +15,6 @@ log = "0.4" libc = "0.2" nix = "0.26.2" vmm-sys-util = "0.11.1" -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 7355a6d07..52f64fb61 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -23,7 +23,6 @@ rand = "0.8.5" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } cpu = { path = "../cpu" } -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/devices/src/sysbus/error.rs b/devices/src/sysbus/error.rs index 4ba9fd753..473a2b327 100644 --- a/devices/src/sysbus/error.rs +++ b/devices/src/sysbus/error.rs @@ -19,11 +19,6 @@ pub enum SysBusError { #[from] source: address_space::error::AddressSpaceError, }, - #[error("Hypervisor")] - Hypervisor { - #[from] - source: hypervisor::error::HypervisorError, - }, #[error("KvmIoctl")] KvmIoctl { #[from] diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml deleted file mode 100644 index abf694fb5..000000000 --- a/hypervisor/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "hypervisor" -version = "2.3.0" -authors = ["Huawei StratoVirt Team"] -edition = "2021" -license = "Mulan PSL v2" - -[dependencies] -arc-swap = "1.6.0" -thiserror = "1.0" -anyhow = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" -log = "0.4" -vmm-sys-util = "0.11.1" -once_cell = "1.18.0" -util = { path = "../util" } diff --git a/hypervisor/src/error.rs b/hypervisor/src/error.rs deleted file mode 100644 index 291a053c7..000000000 --- a/hypervisor/src/error.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use thiserror::Error; - -#[allow(clippy::upper_case_acronyms)] -#[derive(Error, Debug)] -pub enum HypervisorError { - #[error("Util error")] - Util { - #[from] - source: util::error::UtilError, - }, - #[error("KvmIoctl")] - KvmIoctl { - #[from] - source: kvm_ioctls::Error, - }, -} diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs deleted file mode 100644 index d9bf645aa..000000000 --- a/hypervisor/src/kvm/mod.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::sync::Arc; - -use arc_swap::ArcSwap; -use kvm_ioctls::{Kvm, VmFd}; -use log::error; -use once_cell::sync::Lazy; - -#[allow(clippy::upper_case_acronyms)] -#[derive(Default)] -pub struct KVMFds { - pub fd: Option, - pub vm_fd: Option>, -} - -impl KVMFds { - pub fn new() -> Self { - match Kvm::new() { - Ok(fd) => { - let vm_fd = match fd.create_vm() { - Ok(vm_fd) => vm_fd, - Err(e) => { - error!("Failed to create VM in KVM: {:?}", e); - return KVMFds::default(); - } - }; - KVMFds { - fd: Some(fd), - vm_fd: Some(Arc::new(vm_fd)), - } - } - Err(e) => { - error!("Failed to open /dev/kvm: {:?}", e); - KVMFds::default() - } - } - } -} - -pub static KVM_FDS: Lazy> = Lazy::new(|| ArcSwap::from(Arc::new(KVMFds::new()))); diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs deleted file mode 100644 index efe24a2d7..000000000 --- a/hypervisor/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -//! This crate offers interfaces for different kinds of hypervisors, such as KVM. - -pub mod error; -pub mod kvm; - -pub use error::HypervisorError; diff --git a/hypervisor_refactor/src/kvm/aarch64/gicv2.rs b/hypervisor_refactor/src/kvm/aarch64/gicv2.rs index 3cc04599f..abe70dd4f 100644 --- a/hypervisor_refactor/src/kvm/aarch64/gicv2.rs +++ b/hypervisor_refactor/src/kvm/aarch64/gicv2.rs @@ -140,7 +140,7 @@ mod tests { #[test] fn test_create_kvm_gicv2() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/hypervisor_refactor/src/kvm/aarch64/gicv3.rs b/hypervisor_refactor/src/kvm/aarch64/gicv3.rs index 6a8d09e1b..942c2b6e6 100644 --- a/hypervisor_refactor/src/kvm/aarch64/gicv3.rs +++ b/hypervisor_refactor/src/kvm/aarch64/gicv3.rs @@ -303,7 +303,7 @@ mod tests { #[test] fn test_create_kvm_gicv3() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -313,7 +313,7 @@ mod tests { #[test] fn test_create_kvm_gicv3its() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -323,7 +323,7 @@ mod tests { #[test] fn test_realize_gic_device_without_its() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -351,7 +351,7 @@ mod tests { #[test] fn test_gic_redist_regions() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/hypervisor_refactor/src/kvm/interrupt.rs b/hypervisor_refactor/src/kvm/interrupt.rs index 33d5604ce..6b9d9ff65 100644 --- a/hypervisor_refactor/src/kvm/interrupt.rs +++ b/hypervisor_refactor/src/kvm/interrupt.rs @@ -235,7 +235,7 @@ mod tests { #[test] fn test_get_maximum_gsi_cnt() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -244,7 +244,7 @@ mod tests { #[test] fn test_alloc_and_release_gsi() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/hypervisor_refactor/src/kvm/listener.rs b/hypervisor_refactor/src/kvm/listener.rs index 15a02c203..117ab19bc 100644 --- a/hypervisor_refactor/src/kvm/listener.rs +++ b/hypervisor_refactor/src/kvm/listener.rs @@ -599,7 +599,7 @@ mod test { #[test] fn test_alloc_slot() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -623,7 +623,7 @@ mod test { #[test] fn test_add_del_ram_region() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -649,7 +649,7 @@ mod test { #[test] fn test_add_region_align() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -671,7 +671,7 @@ mod test { #[test] fn test_add_del_ioeventfd() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -717,7 +717,7 @@ mod test { #[test] fn test_ioeventfd_with_data_match() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -771,7 +771,7 @@ mod test { #[test] #[cfg(target_arch = "x86_64")] fn test_kvm_io_listener() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor_refactor/src/kvm/mod.rs index 94516edb3..786c65961 100644 --- a/hypervisor_refactor/src/kvm/mod.rs +++ b/hypervisor_refactor/src/kvm/mod.rs @@ -104,19 +104,15 @@ pub struct KvmHypervisor { } impl KvmHypervisor { - pub fn new(kvm_vm_fd: Option>) -> Result { + pub fn new() -> Result { match Kvm::new() { Ok(kvm_fd) => { - let vm_fd: Option> = if kvm_vm_fd.is_some() { - kvm_vm_fd - } else { - Some(Arc::new(match kvm_fd.create_vm() { - Ok(fd) => fd, - Err(e) => { - bail!("Failed to create VM in KVM: {:?}", e); - } - })) - }; + let vm_fd: Option> = Some(Arc::new(match kvm_fd.create_vm() { + Ok(fd) => fd, + Err(e) => { + bail!("Failed to create VM in KVM: {:?}", e); + } + })); Ok(KvmHypervisor { fd: Some(kvm_fd), @@ -889,7 +885,7 @@ mod test { #[cfg(target_arch = "x86_64")] #[test] fn test_x86_64_kvm_cpu() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } @@ -996,7 +992,7 @@ mod test { #[test] #[allow(unused)] fn test_cpu_lifecycle_with_kvm() { - let kvm_hyp = KvmHypervisor::new(None).unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/machine/Cargo.toml b/machine/Cargo.toml index e0ca96814..0a1e80b13 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -19,7 +19,6 @@ address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } cpu = { path = "../cpu" } devices = { path = "../devices" } -hypervisor = { path = "../hypervisor" } hypervisor_refactor = { path = "../hypervisor_refactor"} machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/machine/src/error.rs b/machine/src/error.rs index 9c5580db0..c8fb8ee67 100644 --- a/machine/src/error.rs +++ b/machine/src/error.rs @@ -60,11 +60,6 @@ pub enum MachineError { #[from] source: machine_manager::config::error::ConfigError, }, - #[error("Hypervisor")] - Hypervisor { - #[from] - source: hypervisor::error::HypervisorError, - }, #[error("Io")] Io { #[from] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 858c0db59..6b81d5098 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -66,7 +66,6 @@ use devices::usb::{ #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; -use hypervisor::kvm::KVM_FDS; use hypervisor_refactor::{kvm::KvmHypervisor, HypervisorOps}; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; @@ -190,9 +189,7 @@ impl MachineBase { mmio_region, ); - let hypervisor = Arc::new(Mutex::new(KvmHypervisor::new( - KVM_FDS.load().vm_fd.clone(), - )?)); + let hypervisor = Arc::new(Mutex::new(KvmHypervisor::new()?)); Ok(MachineBase { cpu_topo, diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 4ee5b5ba7..5a9a00aa8 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -14,7 +14,6 @@ log = "0.4" thiserror = "1.0" anyhow = "1.0" util = {path = "../util"} -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } [dev-dependencies] diff --git a/migration/src/error.rs b/migration/src/error.rs index 3c7014dc3..4ef22d410 100644 --- a/migration/src/error.rs +++ b/migration/src/error.rs @@ -21,11 +21,6 @@ pub enum MigrationError { #[from] source: util::error::UtilError, }, - #[error("HypervisorError")] - Hypervisor { - #[from] - source: hypervisor::error::HypervisorError, - }, #[error("Io")] Io { #[from] diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index 28b1e1201..eab986748 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -18,7 +18,6 @@ vmm-sys-util = "0.11.1" vfio-bindings = "0.3" once_cell = "1.18.0" address_space = { path = "../address_space" } -hypervisor = { path = "../hypervisor" } hypervisor_refactor = { path = "../hypervisor_refactor"} machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/vfio/src/error.rs b/vfio/src/error.rs index 4373ec717..7f9ff5ae2 100644 --- a/vfio/src/error.rs +++ b/vfio/src/error.rs @@ -24,11 +24,6 @@ pub enum VfioError { #[from] source: address_space::error::AddressSpaceError, }, - #[error("Hypervisor")] - Hypervisor { - #[from] - source: hypervisor::error::HypervisorError, - }, #[error("Failed to add sub region at the BAR {0} in memory space.")] AddRegBar(usize), #[error("Vfio ioctl failed: {0}, error is: {1:?}")] diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 24928d042..ba0d0e93e 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -17,7 +17,6 @@ serde_json = "1.0" vmm-sys-util = "0.11.1" once_cell = "1.18.0" address_space = { path = "../address_space" } -hypervisor = { path = "../hypervisor" } machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } -- Gitee From d26964da53177b74ee79787d475673731f0d5a58 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 8 Jan 2024 15:25:59 +0800 Subject: [PATCH 1580/2187] Hypervisor: Rename hypervisor_refactor crate to hypervisor Rename the hypervisor_refactor directory to hypervisor, signifying the completion of the restructuring effort on the Hypervisor abstract layer. Signed-off-by: Jinhao Gao --- Cargo.lock | 6 +++--- {hypervisor_refactor => hypervisor}/Cargo.toml | 2 +- {hypervisor_refactor => hypervisor}/src/error.rs | 0 .../src/kvm/aarch64/gicv2.rs | 0 .../src/kvm/aarch64/gicv3.rs | 0 {hypervisor_refactor => hypervisor}/src/kvm/aarch64/mod.rs | 0 {hypervisor_refactor => hypervisor}/src/kvm/interrupt.rs | 0 {hypervisor_refactor => hypervisor}/src/kvm/listener.rs | 0 {hypervisor_refactor => hypervisor}/src/kvm/mod.rs | 0 {hypervisor_refactor => hypervisor}/src/kvm/vm_state.rs | 0 {hypervisor_refactor => hypervisor}/src/kvm/x86_64/mod.rs | 0 {hypervisor_refactor => hypervisor}/src/lib.rs | 0 machine/Cargo.toml | 2 +- machine/src/aarch64/micro.rs | 2 +- machine/src/aarch64/standard.rs | 4 ++-- machine/src/lib.rs | 2 +- machine/src/micro_common/syscall.rs | 2 +- machine/src/standard_common/syscall.rs | 2 +- machine/src/x86_64/micro.rs | 4 ++-- machine/src/x86_64/standard.rs | 4 ++-- vfio/Cargo.toml | 2 +- 21 files changed, 16 insertions(+), 16 deletions(-) rename {hypervisor_refactor => hypervisor}/Cargo.toml (95%) rename {hypervisor_refactor => hypervisor}/src/error.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/aarch64/gicv2.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/aarch64/gicv3.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/aarch64/mod.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/interrupt.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/listener.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/mod.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/vm_state.rs (100%) rename {hypervisor_refactor => hypervisor}/src/kvm/x86_64/mod.rs (100%) rename {hypervisor_refactor => hypervisor}/src/lib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index b86f26ecb..65fde73b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -718,7 +718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hypervisor_refactor" +name = "hypervisor" version = "2.3.0" dependencies = [ "address_space", @@ -910,7 +910,7 @@ dependencies = [ "boot_loader", "cpu", "devices", - "hypervisor_refactor", + "hypervisor", "kvm-bindings", "libc", "log", @@ -1813,7 +1813,7 @@ dependencies = [ "anyhow", "byteorder", "devices", - "hypervisor_refactor", + "hypervisor", "kvm-bindings", "kvm-ioctls", "libc", diff --git a/hypervisor_refactor/Cargo.toml b/hypervisor/Cargo.toml similarity index 95% rename from hypervisor_refactor/Cargo.toml rename to hypervisor/Cargo.toml index 4caf3147e..a2f2d0dff 100644 --- a/hypervisor_refactor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "hypervisor_refactor" +name = "hypervisor" version = "2.3.0" authors = ["Huawei StratoVirt Team"] edition = "2021" diff --git a/hypervisor_refactor/src/error.rs b/hypervisor/src/error.rs similarity index 100% rename from hypervisor_refactor/src/error.rs rename to hypervisor/src/error.rs diff --git a/hypervisor_refactor/src/kvm/aarch64/gicv2.rs b/hypervisor/src/kvm/aarch64/gicv2.rs similarity index 100% rename from hypervisor_refactor/src/kvm/aarch64/gicv2.rs rename to hypervisor/src/kvm/aarch64/gicv2.rs diff --git a/hypervisor_refactor/src/kvm/aarch64/gicv3.rs b/hypervisor/src/kvm/aarch64/gicv3.rs similarity index 100% rename from hypervisor_refactor/src/kvm/aarch64/gicv3.rs rename to hypervisor/src/kvm/aarch64/gicv3.rs diff --git a/hypervisor_refactor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs similarity index 100% rename from hypervisor_refactor/src/kvm/aarch64/mod.rs rename to hypervisor/src/kvm/aarch64/mod.rs diff --git a/hypervisor_refactor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs similarity index 100% rename from hypervisor_refactor/src/kvm/interrupt.rs rename to hypervisor/src/kvm/interrupt.rs diff --git a/hypervisor_refactor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs similarity index 100% rename from hypervisor_refactor/src/kvm/listener.rs rename to hypervisor/src/kvm/listener.rs diff --git a/hypervisor_refactor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs similarity index 100% rename from hypervisor_refactor/src/kvm/mod.rs rename to hypervisor/src/kvm/mod.rs diff --git a/hypervisor_refactor/src/kvm/vm_state.rs b/hypervisor/src/kvm/vm_state.rs similarity index 100% rename from hypervisor_refactor/src/kvm/vm_state.rs rename to hypervisor/src/kvm/vm_state.rs diff --git a/hypervisor_refactor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs similarity index 100% rename from hypervisor_refactor/src/kvm/x86_64/mod.rs rename to hypervisor/src/kvm/x86_64/mod.rs diff --git a/hypervisor_refactor/src/lib.rs b/hypervisor/src/lib.rs similarity index 100% rename from hypervisor_refactor/src/lib.rs rename to hypervisor/src/lib.rs diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 0a1e80b13..8f5892513 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -19,7 +19,7 @@ address_space = { path = "../address_space" } boot_loader = { path = "../boot_loader" } cpu = { path = "../cpu" } devices = { path = "../devices" } -hypervisor_refactor = { path = "../hypervisor_refactor"} +hypervisor = { path = "../hypervisor"} machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index cd802a723..ef3e85c25 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -19,7 +19,7 @@ use crate::{LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; -use hypervisor_refactor::kvm::aarch64::*; +use hypervisor::kvm::aarch64::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::{ diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 272fd9705..5b5398eb4 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -49,8 +49,8 @@ use devices::legacy::{ use devices::pci::{PciDevOps, PciHost, PciIntxState}; use devices::sysbus::SysBusDevType; use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_MAX}; -use hypervisor_refactor::kvm::aarch64::*; -use hypervisor_refactor::kvm::*; +use hypervisor::kvm::aarch64::*; +use hypervisor::kvm::*; #[cfg(feature = "ramfb")] use machine_manager::config::parse_ramfb; use machine_manager::config::ShutdownAction; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6b81d5098..3201a9017 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -66,7 +66,7 @@ use devices::usb::{ #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; -use hypervisor_refactor::{kvm::KvmHypervisor, HypervisorOps}; +use hypervisor::{kvm::KvmHypervisor, HypervisorOps}; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; #[cfg(feature = "virtio_gpu")] diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index 75c85af43..027bd2a76 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -14,7 +14,7 @@ use crate::aarch64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] use crate::x86_64::micro::{arch_ioctl_allow_list, arch_syscall_whitelist}; -use hypervisor_refactor::kvm::*; +use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; use virtio::VhostKern::*; diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index 7aeeed719..ef40b97ff 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -14,7 +14,7 @@ use crate::aarch64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; #[cfg(target_arch = "x86_64")] use crate::x86_64::standard::{arch_ioctl_allow_list, arch_syscall_whitelist}; -use hypervisor_refactor::kvm::*; +use hypervisor::kvm::*; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::tap::{TUNGETFEATURES, TUNSETIFF, TUNSETOFFLOAD, TUNSETQUEUE, TUNSETVNETHDRSZ}; #[cfg(feature = "usb_camera_v4l2")] diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 99bf186c1..7d4619b17 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -20,8 +20,8 @@ use crate::{ use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; use devices::legacy::FwCfgOps; -use hypervisor_refactor::kvm::x86_64::*; -use hypervisor_refactor::kvm::*; +use hypervisor::kvm::x86_64::*; +use hypervisor::kvm::*; use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::seccomp::{BpfRule, SeccompCmpOpt}; diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 7d89e489d..d65e930bc 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -40,8 +40,8 @@ use devices::legacy::{ SERIAL_ADDR, }; use devices::pci::{PciDevOps, PciHost}; -use hypervisor_refactor::kvm::x86_64::*; -use hypervisor_refactor::kvm::*; +use hypervisor::kvm::x86_64::*; +use hypervisor::kvm::*; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index eab986748..bd8fd6b41 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -18,7 +18,7 @@ vmm-sys-util = "0.11.1" vfio-bindings = "0.3" once_cell = "1.18.0" address_space = { path = "../address_space" } -hypervisor_refactor = { path = "../hypervisor_refactor"} +hypervisor = { path = "../hypervisor"} machine_manager = { path = "../machine_manager" } util = { path = "../util" } devices = { path = "../devices" } -- Gitee From 3dbc8583c454bbda6681446e6e250a2a1f1d784f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 2 Feb 2024 07:39:33 +0800 Subject: [PATCH 1581/2187] machine: allow multi-features in `create_device_add_matches` Commit 3d2d8d9d98c8(machine: replace `match` branch with macros) introduced macros `create_device_add_matches` but it allows only one feature (eg: `#[cfg(feature = XXXX)]`) for one device class. Modify it to allow more features for the furture. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 183a6a8ad..96b6ade8c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -263,7 +263,7 @@ impl MachineBase { macro_rules! create_device_add_matches { ( $command:expr; $controller: expr; $(($($driver_name:tt)|+, $function_name:tt, $($arg:tt),*)),*; - $(#[cfg(feature = $feature: tt)] + $(#[cfg($($features: tt)*)] ($driver_name1:tt, $function_name1:tt, $($arg1:tt),*)),* ) => { match $command { @@ -273,7 +273,7 @@ macro_rules! create_device_add_matches { }, )* $( - #[cfg(feature = $feature)] + #[cfg($($features)*)] $driver_name1 => { $controller.$function_name1($($arg1),*)?; }, -- Gitee From c670fee7cc3c94b3c8e51e8145932e988e30b1e2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 2 Feb 2024 07:57:32 +0800 Subject: [PATCH 1582/2187] machine: delete useless annotation Delete useless annotation for `add_virtio_serial_port` function. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 96b6ade8c..177366441 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -743,7 +743,6 @@ pub trait MachineOps { /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. - /// * `is_console` - Whether this virtio serial port is a console port. fn add_virtio_serial_port(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let serial_cfg = vm_config .virtio_serial -- Gitee From 0cebfc701da7a24075deefefa5ebd01a0b0b76b0 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 2 Feb 2024 16:59:20 +0800 Subject: [PATCH 1583/2187] PvPanic: remove name() and hotpluggable() remove name() and hotpluggable(), because their implementaion is functionally consistent with default trait implementation. Signed-off-by: Fan Xuan Zhe --- devices/src/misc/pvpanic.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index d612b22b9..32c093cbb 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -142,14 +142,6 @@ impl Device for PvPanicPci { fn device_base_mut(&mut self) -> &mut DeviceBase { &mut self.base.base } - - fn hotpluggable(&self) -> bool { - false - } - - fn name(&self) -> String { - "PvPanic".to_string() - } } impl PciDevOps for PvPanicPci { -- Gitee From 9dda3be3bf88bbe2e08c2da30850fe61e50954d5 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 2 Feb 2024 17:55:26 +0800 Subject: [PATCH 1584/2187] PvPanic: driver auto match bug fix By configuring the Subsystem vendor ID and Subsystem ID attributes in the configuration space of the PvPanic device, we solve the problem that the guest os cannot match the driver for automic installation after discovering the device. Signed-off-by: Fan Xuan Zhe --- devices/src/misc/pvpanic.rs | 27 ++++++++++++++++++++------- devices/src/pci/config.rs | 2 ++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 32c093cbb..d62b8a62e 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -20,8 +20,10 @@ use log::{debug, error, info}; use crate::pci::{ config::{ - PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, PCI_CONFIG_SPACE_SIZE, - PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, + PciConfig, RegionType, CLASS_PI, DEVICE_ID, HEADER_TYPE, PCI_CLASS_SYSTEM_OTHER, + PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_SUBDEVICE_ID_QEMU, + PCI_VENDOR_ID_REDHAT, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUBSYSTEM_ID, + SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }, le_write_u16, PciBus, PciDevBase, PciDevOps, }; @@ -29,9 +31,8 @@ use crate::{Device, DeviceBase}; use address_space::{GuestAddress, Region, RegionOps}; use machine_manager::config::{PvpanicDevConfig, PVPANIC_CRASHLOADED, PVPANIC_PANICKED}; -const PCI_CLASS_SYSTEM_OTHER: u16 = 0x0880; -const PCI_CLASS_PI: u16 = 0x09; -const PCI_REVISION_ID_PVPANIC: u8 = 1; +const PVPANIC_PCI_REVISION_ID: u8 = 1; +const PVPANIC_PCI_VENDOR_ID: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; #[cfg(target_arch = "aarch64")] // param size in Region::init_io_region must greater than 4 @@ -168,7 +169,7 @@ impl PciDevOps for PvPanicPci { PCI_DEVICE_ID_REDHAT_PVPANIC, )?; - self.base.config.config[REVISION_ID] = PCI_REVISION_ID_PVPANIC; + self.base.config.config[REVISION_ID] = PVPANIC_PCI_REVISION_ID; le_write_u16( &mut self.base.config.config, @@ -176,7 +177,19 @@ impl PciDevOps for PvPanicPci { PCI_CLASS_SYSTEM_OTHER, )?; - self.base.config.config[PCI_CLASS_PI as usize] = 0x00; + le_write_u16( + &mut self.base.config.config, + SUBSYSTEM_VENDOR_ID, + PVPANIC_PCI_VENDOR_ID, + )?; + + le_write_u16( + &mut self.base.config.config, + SUBSYSTEM_ID, + PCI_SUBDEVICE_ID_QEMU, + )?; + + self.base.config.config[CLASS_PI as usize] = 0x00; self.base.config.config[HEADER_TYPE as usize] = 0x00; diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index c2e2e6db3..561386561 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -41,6 +41,7 @@ pub const DEVICE_ID: u8 = 0x02; /// Command register. pub const COMMAND: u8 = 0x04; pub const REVISION_ID: usize = 0x08; +pub const CLASS_PI: u16 = 0x09; /// Sub-Class Code Register. pub const SUB_CLASS_CODE: u8 = 0x0a; pub const SUBSYSTEM_VENDOR_ID: usize = 0x2c; @@ -306,6 +307,7 @@ pub const PCI_DEVICE_ID_REDHAT_PVPANIC: u16 = 0x0011; // Device classes and subclasses pub const PCI_CLASS_MEMORY_RAM: u16 = 0x0500; pub const PCI_CLASS_SERIAL_USB: u16 = 0x0c03; +pub const PCI_CLASS_SYSTEM_OTHER: u16 = 0x0880; /// Type of bar region. #[derive(PartialEq, Eq, Debug, Copy, Clone)] -- Gitee From b07b899be0e6e388c3b9e1fd4046097ff130bcb3 Mon Sep 17 00:00:00 2001 From: Binfeng Wu Date: Thu, 18 Jan 2024 11:19:29 +0800 Subject: [PATCH 1585/2187] trace: add virtio gpu trace point Add virtio gpu trace points which is necessary, and add a print when windows size was resized. Signed-off-by: Binfeng Wu --- trace/trace_event/virtio.toml | 66 +++++++++++++++++++++++++++++++++++ virtio/src/device/gpu.rs | 45 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index bd84d1163..efe78b987 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -87,3 +87,69 @@ name = "virtio_blk_write_config" args = "offset: u64, data: &[u8]" message = "offset {}, data {:?}" enabled = true + +[[events]] +name = "virtio_gpu_update_cursor" +args = "scanout: u32, x: u32, y: u32, res: u32, cmd: &str" +message = "scanout {}, x {}, y {}, resource {}, type {}." +enabled = true + +[[events]] +name = "virtio_gpu_get_edid" +args = "scanout: u32" +message = "scanout {}." +enabled = true + +[[events]] +name = "virtio_gpu_resource_create_2d" +args = "res: u32, fmt: u32, w: u32, h: u32" +message = "resource: {}, format {}, width {}, height {}." +enabled = true + +[[events]] +name = "virtio_gpu_resource_unref" +args = "res: u32" +message = "resource: {}." +enabled = true + +[[events]] +name = "virtio_gpu_xfer_toh_2d" +args = "res: u32" +message = "resource: {}." +enabled = true + +[[events]] +name = "virtio_gpu_resource_flush" +args = "res: u32, w: u32, h: u32, x: u32, y: u32" +message = "resource: {}, width: {}, height: {}, x: {}, y: {}." +enabled = true + +[[events]] +name = "virtio_gpu_set_scanout" +args = "res: u32, scanout: u32, w: u32, h: u32, x: u32, y: u32" +message = "resource: {}, scanout: {}, width: {}, height: {}, x: {}, y: {}." +enabled = true + +[[events]] +name = "virtio_gpu_resource_attach_backing" +args = "res: u32" +message = "resource: {}." +enabled = true + +[[events]] +name = "virtio_gpu_resource_detach_backing" +args = "res: u32" +message = "resource: {}." +enabled = true + +[[events]] +name = "virtio_gpu_init_config_features" +args = "features: u64" +message = "features is {}." +enabled = true + +[[events]] +name = "virtio_gpu_console_hw_update" +args = "con: usize, w: i32, h: i32" +message = "console {} receive hw update request, update size {} {}." +enabled = true diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index c1efc1329..b4e0f9224 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -310,6 +310,7 @@ impl HardWareOperations for GpuOpts { } let width = locked_con.width; let height = locked_con.height; + trace::virtio_gpu_console_hw_update(locked_con.con_id, width, height); drop(locked_con); display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, width, height) .unwrap_or_else(|e| error!("Error occurs during graphic updating: {:?}", e)); @@ -334,6 +335,10 @@ impl HardWareOperations for GpuOpts { if self.interrupt_cb.is_none() { return; } + info!( + "virtio-gpu receive resize request, con {} will be resize to {} {}.", + con_id, width, height + ); let interrupt_cb = self.interrupt_cb.as_ref().unwrap(); if let Err(e) = (interrupt_cb)(&VirtioInterruptType::Config, None, false) { error!( @@ -698,6 +703,7 @@ impl GpuIoHandler { { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| "Failed to trigger interrupt(gpu ctrl)")?; + trace::virtqueue_send_interrupt("Gpu", &*queue_lock as *const _ as u64); } Ok(()) @@ -875,6 +881,17 @@ impl GpuIoHandler { ); return Ok(()); } + trace::virtio_gpu_update_cursor( + info_cursor.pos.scanout_id, + info_cursor.pos.x_coord, + info_cursor.pos.y_coord, + info_cursor.resource_id, + if req.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { + "move" + } else { + "update" + }, + ); let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; if req.header.hdr_type == VIRTIO_GPU_CMD_MOVE_CURSOR { @@ -940,6 +957,7 @@ impl GpuIoHandler { ); return self.response_nodata(VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, req); } + trace::virtio_gpu_get_edid(edid_req.scanouts); let mut edid_resp = VirtioGpuRespEdid::default(); edid_resp.header.hdr_type = VIRTIO_GPU_RESP_OK_EDID; @@ -962,6 +980,12 @@ impl GpuIoHandler { fn cmd_resource_create_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_create_2d = VirtioGpuResourceCreate2d::default(); self.get_request(req, &mut info_create_2d)?; + trace::virtio_gpu_resource_create_2d( + info_create_2d.resource_id, + info_create_2d.format, + info_create_2d.width, + info_create_2d.height, + ); if info_create_2d.resource_id == 0 { error!("GuestError: resource id 0 is not allowed."); @@ -1041,6 +1065,7 @@ impl GpuIoHandler { fn cmd_resource_unref(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_resource_unref = VirtioGpuResourceUnref::default(); self.get_request(req, &mut info_resource_unref)?; + trace::virtio_gpu_resource_unref(info_resource_unref.resource_id); if let Some(res_index) = self.get_resource_idx(info_resource_unref.resource_id) { self.resource_destroy(res_index); @@ -1057,6 +1082,14 @@ impl GpuIoHandler { fn cmd_set_scanout(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_set_scanout = VirtioGpuSetScanout::default(); self.get_request(req, &mut info_set_scanout)?; + trace::virtio_gpu_set_scanout( + info_set_scanout.scanout_id, + info_set_scanout.resource_id, + info_set_scanout.rect.width, + info_set_scanout.rect.height, + info_set_scanout.rect.x_coord, + info_set_scanout.rect.y_coord, + ); if info_set_scanout.scanout_id >= self.num_scanouts { error!( @@ -1154,6 +1187,13 @@ impl GpuIoHandler { fn cmd_resource_flush(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_res_flush = VirtioGpuResourceFlush::default(); self.get_request(req, &mut info_res_flush)?; + trace::virtio_gpu_resource_flush( + info_res_flush.resource_id, + info_res_flush.rect.width, + info_res_flush.rect.height, + info_res_flush.rect.x_coord, + info_res_flush.rect.y_coord, + ); let res_index = self.get_resource_idx(info_res_flush.resource_id); if res_index.is_none() { @@ -1323,6 +1363,7 @@ impl GpuIoHandler { fn cmd_transfer_to_host_2d(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_transfer = VirtioGpuTransferToHost2d::default(); self.get_request(req, &mut info_transfer)?; + trace::virtio_gpu_xfer_toh_2d(info_transfer.resource_id); let (res_idx, error) = self.cmd_transfer_to_host_2d_params_check(&info_transfer); if res_idx.is_none() { @@ -1336,6 +1377,7 @@ impl GpuIoHandler { fn cmd_resource_attach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_attach_backing = VirtioGpuResourceAttachBacking::default(); self.get_request(req, &mut info_attach_backing)?; + trace::virtio_gpu_resource_attach_backing(info_attach_backing.resource_id); let res_idx = self.get_resource_idx(info_attach_backing.resource_id); if res_idx.is_none() { @@ -1412,6 +1454,7 @@ impl GpuIoHandler { fn cmd_resource_detach_backing(&mut self, req: &VirtioGpuRequest) -> Result<()> { let mut info_detach_backing = VirtioGpuResourceDetachBacking::default(); self.get_request(req, &mut info_detach_backing)?; + trace::virtio_gpu_resource_detach_backing(info_detach_backing.resource_id); let (res_idx, error) = self.get_backed_resource_idx( info_detach_backing.resource_id, @@ -1523,6 +1566,7 @@ impl GpuIoHandler { .with_context(|| { VirtioError::InterruptTrigger("gpu cursor", VirtioInterruptType::Vring) })?; + trace::virtqueue_send_interrupt("Cursor", &*queue as *const _ as u64); } } @@ -1683,6 +1727,7 @@ impl VirtioDevice for Gpu { self.base.device_features |= 1 << VIRTIO_GPU_F_MONOCHROME; + trace::virtio_gpu_init_config_features(self.base.device_features); self.build_device_config_space(); Ok(()) } -- Gitee From 70cabf16c9c8c58a4764f62a11f52bb2cff5c251 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 18:06:40 +0800 Subject: [PATCH 1586/2187] OHOS: Port StratoVirt to OHOS Port stratovirt to OHOS. To run stratovirt on OHOS, some codes need to be fixed. This patch will enable basic compling and runing of stratovirt's OHOS version. Signed-off-by: zhanghan64 --- .cargo/config | 4 ++-- address_space/Cargo.toml | 2 +- build.rs | 20 +++++++++++++++++++- chardev_backend/Cargo.toml | 2 +- cpu/Cargo.toml | 2 +- cpu/src/lib.rs | 8 ++++++-- docs/build_guide.ch.md | 12 ++++++++++++ docs/build_guide.md | 12 ++++++++++++ machine/src/micro_common/syscall.rs | 8 ++++---- machine/src/standard_common/syscall.rs | 6 +++--- util/Cargo.toml | 2 +- util/src/unix.rs | 2 +- 12 files changed, 63 insertions(+), 17 deletions(-) diff --git a/.cargo/config b/.cargo/config index dd043fcba..31ec47467 100644 --- a/.cargo/config +++ b/.cargo/config @@ -12,12 +12,12 @@ [build] -[target.'cfg(any(target_arch="aarch64"))'] +[target.'cfg(target_arch = "aarch64", not(target_env = "ohos"))'] rustflags = [ "-C", "link-arg=-lgcc", ] -[target.'cfg(not(target_env = "musl"))'] +[target.'cfg(not(any(target_env = "musl", target_env = "ohos")))'] rustflags = [ "-C", "link-arg=-lpixman-1", ] diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 997abd3ec..9a3cb25e5 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -10,10 +10,10 @@ description = "provide memory management for VM" libc = "0.2" log = "0.4" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } kvm-ioctls = "0.13.0" vmm-sys-util = "0.11.1" arc-swap = "1.6.0" -nix = "0.26.2" thiserror = "1.0" anyhow = "1.0" machine_manager = { path = "../machine_manager" } diff --git a/build.rs b/build.rs index 7dd9fd12a..96f77ffd4 100644 --- a/build.rs +++ b/build.rs @@ -10,8 +10,26 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +fn ohos_env_configure() { + if let Ok(ohos_sdk_path) = std::env::var("OHOS_SDK") { + println!("cargo:rustc-link-arg=--target=aarch64-linux-ohos"); + println!("cargo:rustc-link-arg=--verbose"); + println!("cargo:rustc-link-arg=--sysroot={}/sysroot", ohos_sdk_path); + println!("cargo:rustc-link-arg=-lpixman_static"); + println!( + "cargo:rustc-link-search={}/sysroot/usr/lib/aarch64-linux-ohos", + ohos_sdk_path + ); + } +} + fn main() { - if cfg!(any( + let target_env_ohos = matches!(std::env::var("CARGO_CFG_TARGET_ENV"), Ok(ret) if ret == "ohos"); + + if target_env_ohos { + println!("cargo:rerun-if-env-changed=OHOS_SDK"); + ohos_env_configure(); + } else if cfg!(any( feature = "demo_device", feature = "gtk", feature = "ramfb", diff --git a/chardev_backend/Cargo.toml b/chardev_backend/Cargo.toml index a162e58d6..92fbdab46 100644 --- a/chardev_backend/Cargo.toml +++ b/chardev_backend/Cargo.toml @@ -10,6 +10,6 @@ vmm-sys-util = "0.11.0" anyhow = "1.0" log = "0.4" libc = "0.2" -nix = "0.26.2" +nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } machine_manager = { path = "../machine_manager" } util = { path = "../util" } diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index b48b16d0d..f4a5a6fa3 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -10,10 +10,10 @@ description = "CPU emulation" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } kvm-ioctls = "0.13.0" log = "0.4" libc = "0.2" -nix = "0.26.2" vmm-sys-util = "0.11.1" machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index cd024b421..41c1d1110 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -86,14 +86,18 @@ use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal // number should be assigned to SIGRTMIN + n, (n = 0...30). -#[cfg(not(target_env = "musl"))] +#[cfg(target_env = "gnu")] pub const VCPU_TASK_SIGNAL: i32 = 34; #[cfg(target_env = "musl")] pub const VCPU_TASK_SIGNAL: i32 = 35; -#[cfg(not(target_env = "musl"))] +#[cfg(target_env = "ohos")] +pub const VCPU_TASK_SIGNAL: i32 = 40; +#[cfg(target_env = "gnu")] pub const VCPU_RESET_SIGNAL: i32 = 35; #[cfg(target_env = "musl")] pub const VCPU_RESET_SIGNAL: i32 = 36; +#[cfg(target_env = "ohos")] +pub const VCPU_RESET_SIGNAL: i32 = 41; /// Watch `0x3ff` IO port to record the magic value trapped from guest kernel. #[cfg(all(target_arch = "x86_64", feature = "boot_time"))] diff --git a/docs/build_guide.ch.md b/docs/build_guide.ch.md index f022c364e..b4f506a83 100644 --- a/docs/build_guide.ch.md +++ b/docs/build_guide.ch.md @@ -64,6 +64,18 @@ $ cargo build --workspace --bins --release --target ${arch}-unknown-linux-musl $ cargo build --release --features "scream_alsa" ``` +## 5. OpenHarmony OS版本的编译 + +StratoVirt支持在Openharmony OS(OHOS)的运行。该版本的编译需要一台x64机器,并使用OHOS提供的RUST交叉编译工具链、以及SDK。 + +编译之前,需要把OHOS SDK的路径指定到环境变量OHOS_SDK中。另外,StratoVirt依赖的crate有部分不支持OHOS的编译,需要对其源码做相关修改。 + +编译命令示意如下: + +``` +RUSTFLAGS="-C link-arg=--target=aarch64-linux-ohos -C linker={OHOS_SDK}/llvm/bin/clang" cargo build --target aarch64-linux-ohos --features {FEATURES}" +``` + # 通过容器构建StratoVirt静态链接二进制 ## 1. 检查docker环境 diff --git a/docs/build_guide.md b/docs/build_guide.md index ed2752222..b6afe2321 100644 --- a/docs/build_guide.md +++ b/docs/build_guide.md @@ -66,6 +66,18 @@ List of optional features: $ cargo build --workspace --bins --release --features "scream_alsa" ``` +## 5. Compiling of OpenHarmony OS version + +Stratovirt now can run on OpenHarmony OS(OHOS). Stratovirt, OHOS version, is compiled on x64, and relies on RUST cross compilation toolchain and SDK offered by OHOS. + +Before compiling, specify OHOS SDK path in environment variable OHOS_SDK. Some crates needed by StratoVirt now are not support OHOS platform, adapting is essential. + +Here is a command demo: + +``` +RUSTFLAGS="-C link-arg=--target=aarch64-linux-ohos -C linker={OHOS_SDK}/llvm/bin/clang" cargo build --target aarch64-linux-ohos --features {FEATURES}" +``` + # Build static StratoVirt in containers ## 1. Check docker environment diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index 027bd2a76..836cbe6f7 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -89,7 +89,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_exit), BpfRule::new(libc::SYS_exit_group), BpfRule::new(libc::SYS_rt_sigreturn), - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] BpfRule::new(libc::SYS_tkill), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_tgkill), @@ -151,14 +151,14 @@ fn ioctl_allow_list() -> BpfRule { } fn madvise_rule() -> BpfRule { - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTDUMP as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_REMOVE as u32); - #[cfg(not(target_env = "musl"))] + #[cfg(target_env = "gnu")] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_WILLNEED as u32) @@ -167,7 +167,7 @@ fn madvise_rule() -> BpfRule { } fn futex_rule() -> BpfRule { - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] return BpfRule::new(libc::SYS_futex) .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAKE_PRIVATE) .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAIT_PRIVATE) diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index ef40b97ff..2f975361c 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -108,7 +108,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_exit), BpfRule::new(libc::SYS_exit_group), BpfRule::new(libc::SYS_rt_sigreturn), - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] BpfRule::new(libc::SYS_tkill), BpfRule::new(libc::SYS_newfstatat), #[cfg(target_env = "gnu")] @@ -267,7 +267,7 @@ fn ioctl_allow_list() -> BpfRule { } fn madvise_rule() -> BpfRule { - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] return BpfRule::new(libc::SYS_madvise) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_FREE as u32) .add_constraint(SeccompCmpOpt::Eq, 2, libc::MADV_DONTNEED as u32) @@ -283,7 +283,7 @@ fn madvise_rule() -> BpfRule { } fn futex_rule() -> BpfRule { - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] return BpfRule::new(libc::SYS_futex) .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAIT) .add_constraint(SeccompCmpOpt::Eq, 1, FUTEX_WAKE_PRIVATE) diff --git a/util/Cargo.toml b/util/Cargo.toml index 35c50ab55..61145fac9 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -12,6 +12,7 @@ arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +nix = { version = "0.26.2", default-features = false, features = ["poll", "term", "time", "signal", "fs", "feature"] } kvm-ioctls = "0.13.0" libc = "0.2" log = { version = "0.4", features = ["std"]} @@ -21,7 +22,6 @@ once_cell = "1.18.0" io-uring = "0.6.0" serde = { version = "1.0", features = ["derive"] } v4l2-sys-mit = { version = "0.3.0", optional = true } -nix = "0.26.2" [features] default = [] diff --git a/util/src/unix.rs b/util/src/unix.rs index d0723e9cc..ef1507e98 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -295,7 +295,7 @@ impl UnixSock { if !out_fds.is_empty() { let cmsg = cmsghdr { cmsg_len: cmsg_len as _, - #[cfg(target_env = "musl")] + #[cfg(any(target_env = "musl", target_env = "ohos"))] __pad1: 0, cmsg_level: SOL_SOCKET, cmsg_type: SCM_RIGHTS, -- Gitee From 44d4c99eef37e1b6356e4f7fb0a06559886024ba Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Thu, 1 Feb 2024 18:06:40 +0800 Subject: [PATCH 1587/2187] VNC: Introduce vnc_auth feature Introduce vnc_auth feature for OHOS adaption. Some codes can not run on OHOS, use feature to control compiling. Signed-off-by: zhanghan64 --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine_manager/Cargo.toml | 1 + machine_manager/src/config/mod.rs | 8 +++++++ ui/Cargo.toml | 3 ++- ui/src/vnc/auth_sasl.rs | 10 -------- ui/src/vnc/client_io.rs | 15 +++++++++--- ui/src/vnc/mod.rs | 12 ++++++++++ ui/src/vnc/server_io.rs | 40 +++++++++++++++++++++---------- 9 files changed, 65 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 951638e8b..3b71cf711 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ usb_host = ["machine/usb_host"] usb_camera_v4l2 = ["machine/usb_camera_v4l2"] gtk = ["machine/gtk"] vnc = ["machine/vnc"] +vnc_auth = ["machine/vnc_auth"] ramfb = ["machine/ramfb"] virtio_gpu = ["machine/virtio_gpu"] trace_to_logger = ["trace/trace_to_logger"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 8f5892513..c98cb4be0 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -44,5 +44,6 @@ usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb windows_emu_pid = ["ui/console", "machine_manager/windows_emu_pid"] gtk = ["windows_emu_pid", "ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] +vnc_auth = ["vnc"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index e758bc7dd..f257cf8e9 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -36,6 +36,7 @@ usb_camera_v4l2 = ["usb_camera"] windows_emu_pid = [] gtk = [] vnc = [] +vnc_auth = [] ramfb = [] virtio_gpu = [] pvpanic = [] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 0808a891a..80031e3ab 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -39,11 +39,13 @@ mod pvpanic_pci; #[cfg(all(feature = "ramfb", target_arch = "aarch64"))] mod ramfb; mod rng; +#[cfg(feature = "vnc_auth")] mod sasl_auth; #[cfg(feature = "scream")] pub mod scream; mod scsi; mod smbios; +#[cfg(feature = "vnc_auth")] mod tls_creds; mod usb; mod vfio; @@ -74,9 +76,11 @@ pub use pvpanic_pci::*; #[cfg(all(feature = "ramfb", target_arch = "aarch64"))] pub use ramfb::*; pub use rng::*; +#[cfg(feature = "vnc_auth")] pub use sasl_auth::*; pub use scsi::*; pub use smbios::*; +#[cfg(feature = "vnc_auth")] pub use tls_creds::*; pub use usb::*; pub use vfio::*; @@ -119,7 +123,9 @@ pub const DEFAULT_VIRTQUEUE_SIZE: u16 = 256; pub struct ObjectConfig { pub rng_object: HashMap, pub mem_object: HashMap, + #[cfg(feature = "vnc_auth")] pub tls_object: HashMap, + #[cfg(feature = "vnc_auth")] pub sasl_object: HashMap, } @@ -236,9 +242,11 @@ impl VmConfig { "memory-backend-ram" | "memory-backend-file" | "memory-backend-memfd" => { self.add_mem_zone(object_args, device_type)?; } + #[cfg(feature = "vnc_auth")] "tls-creds-x509" => { self.add_tlscred(object_args)?; } + #[cfg(feature = "vnc_auth")] "authz-simple" => { self.add_saslauth(object_args)?; } diff --git a/ui/Cargo.toml b/ui/Cargo.toml index d55fb71a2..bc6e8a94c 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -31,4 +31,5 @@ keycode = [] pixman = ["util/pixman"] console = ["pixman"] gtk = ["console", "keycode", "dep:cairo-rs", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] -vnc = ["console", "keycode", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc"] +vnc = ["console", "keycode", "machine_manager/vnc"] +vnc_auth = ["vnc", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc_auth"] diff --git a/ui/src/vnc/auth_sasl.rs b/ui/src/vnc/auth_sasl.rs index 360183c8c..6465c012c 100644 --- a/ui/src/vnc/auth_sasl.rs +++ b/ui/src/vnc/auth_sasl.rs @@ -38,16 +38,6 @@ const SASL_DATA_MAX_LEN: u32 = 1024 * 1024; /// Minimum supported encryption length of ssf layer in sasl. const MIN_SSF_LENGTH: usize = 56; -/// Authentication type -#[derive(Clone, Copy)] -pub enum AuthState { - Invalid = 0, - No = 1, - Vnc = 2, - Vencrypt = 19, - Sasl = 20, -} - /// Authentication and encryption method. #[derive(Debug, Clone, Copy)] pub enum SubAuthState { diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index d617349f7..a23b945d6 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -38,9 +38,9 @@ use crate::{ pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, vnc::{ - auth_sasl::AuthState, framebuffer_update, round_up_div, server_io::VncServer, - set_area_dirty, write_pixel, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, - MAX_IMAGE_SIZE, MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, + framebuffer_update, round_up_div, server_io::VncServer, set_area_dirty, write_pixel, + AuthState, BIT_PER_BYTE, DIRTY_PIXELS_NUM, DIRTY_WIDTH_BITS, MAX_IMAGE_SIZE, + MAX_WINDOW_HEIGHT, MIN_OUTPUT_LIMIT, OUTPUT_THROTTLE_SCALE, }, }; use util::{ @@ -401,6 +401,7 @@ pub struct ClientIoHandler { /// Vnc client io handler. pub handlers: HashMap>, /// Tls server connection. + #[cfg(feature = "vnc_auth")] pub tls_conn: Option, /// Message handler. pub msg_handler: fn(&mut ClientIoHandler) -> Result<()>, @@ -423,6 +424,7 @@ impl ClientIoHandler { stream, io_channel, handlers: HashMap::new(), + #[cfg(feature = "vnc_auth")] tls_conn: None, msg_handler: ClientIoHandler::handle_version, expect: 12, @@ -535,7 +537,10 @@ impl ClientIoHandler { version.minor = 3; } self.client.conn_state.lock().unwrap().version = version; + #[cfg(feature = "vnc_auth")] let auth = self.server.security_type.borrow().auth; + #[cfg(not(feature = "vnc_auth"))] + let auth = AuthState::No; if self.client.conn_state.lock().unwrap().version.minor == 3 { match auth { @@ -615,7 +620,10 @@ impl ClientIoHandler { fn handle_auth(&mut self) -> Result<()> { let buf = self.read_incoming_msg(); trace::vnc_client_handle_auth(&buf[0]); + #[cfg(feature = "vnc_auth")] let auth = self.server.security_type.borrow().auth; + #[cfg(not(feature = "vnc_auth"))] + let auth = AuthState::No; let client = self.client.clone(); let version = client.conn_state.lock().unwrap().version.clone(); @@ -635,6 +643,7 @@ impl ClientIoHandler { } self.update_event_handler(1, ClientIoHandler::handle_client_init); } + #[cfg(feature = "vnc_auth")] AuthState::Vencrypt => { // Send VeNCrypt version 0.2. let mut buf = [0u8; 2]; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 3916d8b54..271352f11 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -10,7 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "vnc_auth")] pub mod auth_sasl; +#[cfg(feature = "vnc_auth")] pub mod auth_vencrypt; pub mod client_io; pub mod encoding; @@ -87,6 +89,16 @@ pub const fn round_up(n: u64, d: u64) -> u64 { round_up_div(n, d) * d } +/// Authentication type +#[derive(Clone, Copy)] +pub enum AuthState { + Invalid = 0, + No = 1, + Vnc = 2, + Vencrypt = 19, + Sasl = 20, +} + #[derive(Default)] pub struct VncInterface {} impl DisplayChangeListenerOperations for VncInterface { diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 23c753fa0..76af9e8d4 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -21,26 +21,34 @@ use std::{ sync::{Arc, Mutex, Weak}, }; -use anyhow::{anyhow, Result}; +#[cfg(feature = "vnc_auth")] +use anyhow::anyhow; +use anyhow::Result; use log::error; use vmm_sys_util::epoll::EventSet; use crate::{ console::{DisplayChangeListener, DisplayMouse}, - error::VncError, pixman::{ bytes_per_pixel, get_image_data, get_image_format, get_image_height, get_image_stride, get_image_width, pixman_image_linebuf_create, pixman_image_linebuf_fill, unref_pixman_image, }, vnc::{ - auth_sasl::{AuthState, SaslAuth, SaslConfig, SubAuthState}, - auth_vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, client_io::{vnc_flush, vnc_write, ClientIoHandler, ClientState, IoChannel, RectInfo}, round_up_div, update_server_surface, DIRTY_PIXELS_NUM, MAX_WINDOW_HEIGHT, MAX_WINDOW_WIDTH, VNC_BITMAP_WIDTH, VNC_SERVERS, }, }; +#[cfg(feature = "vnc_auth")] +use crate::{ + error::VncError, + vnc::{ + auth_sasl::{SaslAuth, SaslConfig, SubAuthState}, + auth_vencrypt::{make_vencrypt_config, TlsCreds, ANON_CERT, X509_CERT}, + AuthState, + }, +}; use machine_manager::{ config::{ObjectConfig, VncConfig}, event_loop::EventLoop, @@ -60,6 +68,7 @@ pub struct VncServer { /// Client io handler. pub client_handlers: Arc>>>, /// Security Type for connection. + #[cfg(feature = "vnc_auth")] pub security_type: Rc>, /// Mapping ASCII to keycode. pub keysym2keycode: HashMap, @@ -93,6 +102,7 @@ impl VncServer { ) -> Self { VncServer { client_handlers: Arc::new(Mutex::new(HashMap::new())), + #[cfg(feature = "vnc_auth")] security_type: Rc::new(RefCell::new(SecurityType::default())), keysym2keycode, vnc_surface: Arc::new(Mutex::new(VncSurface::new(guest_image))), @@ -174,6 +184,7 @@ impl ImageInfo { } /// Security type for connection and transport. +#[cfg(feature = "vnc_auth")] pub struct SecurityType { /// Configuration for tls connection. pub tlscreds: Option, @@ -189,6 +200,7 @@ pub struct SecurityType { pub subauth: SubAuthState, } +#[cfg(feature = "vnc_auth")] impl Default for SecurityType { fn default() -> Self { SecurityType { @@ -202,6 +214,7 @@ impl Default for SecurityType { } } +#[cfg(feature = "vnc_auth")] impl SecurityType { // Set security config. fn set_security_config(&mut self, vnc_cfg: &VncConfig, object: &ObjectConfig) -> Result<()> { @@ -522,18 +535,21 @@ pub fn handle_connection( /// /// * `vnc_cfg` - configure of vnc. /// * `object` - configure of sasl and tls. +#[allow(unused_variables)] pub fn make_server_config( server: &Arc, vnc_cfg: &VncConfig, object: &ObjectConfig, ) -> Result<()> { - // Set security config. - server - .security_type - .borrow_mut() - .set_security_config(vnc_cfg, object)?; - // Set auth type. - server.security_type.borrow_mut().set_auth()?; - + #[cfg(feature = "vnc_auth")] + { + // Set security config. + server + .security_type + .borrow_mut() + .set_security_config(vnc_cfg, object)?; + // Set auth type. + server.security_type.borrow_mut().set_auth()?; + } Ok(()) } -- Gitee From fca392b37b058b9f98bc7c9ea7391396c0bb59a8 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 2 Feb 2024 18:06:06 +0800 Subject: [PATCH 1588/2187] PvPanic: Add UT for PvPanic add UT for PvPanic device init and func Signed-off-by: Fan Xuan Zhe --- devices/src/misc/pvpanic.rs | 199 ++++++++++++++++++++++++++++++++++++ devices/src/pci/mod.rs | 2 +- 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index d62b8a62e..005dc154e 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -53,6 +53,7 @@ impl PvPanicState { fn handle_event(&self, event: u32) -> Result<()> { if (event & !(PVPANIC_PANICKED | PVPANIC_CRASHLOADED)) != 0 { error!("pvpanic: unknown event 0x{:X}", event); + bail!("pvpanic: unknown event 0x{:X}", event); } if (event & PVPANIC_PANICKED) == PVPANIC_PANICKED @@ -234,3 +235,201 @@ impl PciDevOps for PvPanicPci { ); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::pci::{host::tests::create_pci_host, le_read_u16, PciHost}; + + fn init_pvpanic_dev(devfn: u8, supported_features: u32, dev_id: &str) -> Arc> { + let pci_host = create_pci_host(); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let config = PvpanicDevConfig { + id: dev_id.to_string(), + supported_features, + }; + let pvpanic_dev = PvPanicPci::new(&config, devfn, root_bus.clone()); + assert_eq!(pvpanic_dev.base.base.id, "pvpanic_test".to_string()); + + pvpanic_dev.realize().unwrap(); + drop(root_bus); + drop(locked_pci_host); + + pci_host + } + + #[test] + fn test_pvpanic_attached() { + let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let pvpanic_dev = root_bus.upgrade().unwrap().lock().unwrap().get_device(0, 7); + assert!(pvpanic_dev.is_some()); + assert_eq!( + pvpanic_dev.unwrap().lock().unwrap().pci_base().base.id, + "pvpanic_test".to_string() + ); + + let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "pvpanic_test"); + assert!(info.is_some()); + let (bus, dev) = info.unwrap(); + assert_eq!(bus.lock().unwrap().name, "pcie.0"); + assert_eq!(dev.lock().unwrap().name(), "pvpanic_test"); + } + + #[test] + fn test_pvpanic_config() { + let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let pvpanic_dev = root_bus + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_device(0, 7) + .unwrap(); + + let info = le_read_u16( + &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, + VENDOR_ID as usize, + ) + .unwrap_or_else(|_| 0); + assert_eq!(info, PCI_VENDOR_ID_REDHAT); + + let info = le_read_u16( + &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, + DEVICE_ID as usize, + ) + .unwrap_or_else(|_| 0); + assert_eq!(info, PCI_DEVICE_ID_REDHAT_PVPANIC); + + let info = le_read_u16( + &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, + SUB_CLASS_CODE as usize, + ) + .unwrap_or_else(|_| 0); + assert_eq!(info, PCI_CLASS_SYSTEM_OTHER); + + let info = le_read_u16( + &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, + SUBSYSTEM_VENDOR_ID, + ) + .unwrap_or_else(|_| 0); + assert_eq!(info, PVPANIC_PCI_VENDOR_ID); + + let info = le_read_u16( + &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, + SUBSYSTEM_ID, + ) + .unwrap_or_else(|_| 0); + assert_eq!(info, PCI_SUBDEVICE_ID_QEMU); + } + + #[test] + fn test_pvpanic_read_features() { + let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let pvpanic_dev = root_bus + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_device(0, 7) + .unwrap(); + + // test read supported_features + let mut data_read = [0xffu8; 1]; + let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + .region + .as_ref() + .unwrap() + .read(&mut data_read.as_mut(), GuestAddress(0), 0, 1); + assert!(result.is_ok()); + assert_eq!( + data_read.to_vec(), + vec![PVPANIC_PANICKED as u8 | PVPANIC_CRASHLOADED as u8] + ); + } + + #[test] + fn test_pvpanic_write_panicked() { + let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let pvpanic_dev = root_bus + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_device(0, 7) + .unwrap(); + + // test write panicked event + let data_write = [PVPANIC_PANICKED as u8; 1]; + let count = data_write.len() as u64; + let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + .region + .as_ref() + .unwrap() + .write(&mut data_write.as_ref(), GuestAddress(0), 0, count); + assert!(result.is_ok()); + } + + #[test] + fn test_pvpanic_write_crashload() { + let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let pvpanic_dev = root_bus + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_device(0, 7) + .unwrap(); + + // test write crashload event + let data_write = [PVPANIC_CRASHLOADED as u8; 1]; + let count = data_write.len() as u64; + let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + .region + .as_ref() + .unwrap() + .write(&mut data_write.as_ref(), GuestAddress(0), 0, count); + assert!(result.is_ok()); + } + + #[test] + fn test_pvpanic_write_unknown() { + let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + + let pvpanic_dev = root_bus + .upgrade() + .unwrap() + .lock() + .unwrap() + .get_device(0, 7) + .unwrap(); + + // test write unknown event + let data_write = [100u8; 1]; + let count = data_write.len() as u64; + let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + .region + .as_ref() + .unwrap() + .write(&mut data_write.as_ref(), GuestAddress(0), 0, count); + assert!(result.is_err()); + } +} diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index e9ba0c48a..67acbbfde 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -14,12 +14,12 @@ pub mod config; #[cfg(feature = "demo_device")] pub mod demo_device; pub mod error; +pub mod host; pub mod hotplug; pub mod intx; pub mod msix; mod bus; -mod host; mod root_port; pub use anyhow::{bail, Result}; -- Gitee From 2cbf3bbe0050ba5be882a94bf0b088ebca4eb0d1 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sun, 4 Feb 2024 20:04:47 +0800 Subject: [PATCH 1589/2187] virtio-net: call handle_rx after queue is not full Due to the EDGE_TIGGERED for tap device, if queue is full and left some packets in the tap device, it may not trigger handle_rx again. These packets may not be processed. So, it shoule call handle_rx to read packet from the tap device after the queue is not full. Signed-off-by: Yan Wang --- virtio/src/device/net.rs | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 0cc6aa307..d8818623f 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -684,14 +684,16 @@ struct RxVirtio { queue_full: bool, queue: Arc>, queue_evt: Arc, + recv_evt: Arc, } impl RxVirtio { - fn new(queue: Arc>, queue_evt: Arc) -> Self { + fn new(queue: Arc>, queue_evt: Arc, recv_evt: Arc) -> Self { RxVirtio { queue_full: false, queue, queue_evt, + recv_evt, } } } @@ -859,9 +861,9 @@ impl NetIoHandler { rx_packets += 1; if rx_packets >= self.queue_size { self.rx - .queue_evt + .recv_evt .write(1) - .with_context(|| "Failed to trigger rx queue event".to_string())?; + .with_context(|| "Failed to trigger tap queue event".to_string())?; break; } } @@ -973,6 +975,7 @@ impl NetIoHandler { let mut notifiers_fds = vec![ locked_net_io.update_evt.as_raw_fd(), locked_net_io.rx.queue_evt.as_raw_fd(), + locked_net_io.rx.recv_evt.as_raw_fd(), locked_net_io.tx.queue_evt.as_raw_fd(), ]; if old_tap_fd != -1 { @@ -1049,6 +1052,16 @@ impl EventNotifierHelper for NetIoHandler { if locked_net_io.device_broken.load(Ordering::SeqCst) { return None; } + + if let Err(ref e) = locked_net_io.rx.recv_evt.write(1) { + error!("Failed to trigger tap receive event, {:?}", e); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + &locked_net_io.device_broken, + ); + } + if let Some(tap) = locked_net_io.tap.as_ref() { if !locked_net_io.is_listening { let notifier = vec![EventNotifier::new( @@ -1059,6 +1072,7 @@ impl EventNotifierHelper for NetIoHandler { Vec::new(), )]; locked_net_io.is_listening = true; + locked_net_io.rx.queue_full = false; return Some(notifier); } } @@ -1118,7 +1132,7 @@ impl EventNotifierHelper for NetIoHandler { } if let Some(tap) = locked_net_io.tap.as_ref() { - if locked_net_io.rx.queue_full { + if locked_net_io.rx.queue_full && locked_net_io.is_listening { let notifier = vec![EventNotifier::new( NotifierOperation::Park, tap.as_raw_fd(), @@ -1127,7 +1141,6 @@ impl EventNotifierHelper for NetIoHandler { Vec::new(), )]; locked_net_io.is_listening = false; - locked_net_io.rx.queue_full = false; return Some(notifier); } } @@ -1136,6 +1149,13 @@ impl EventNotifierHelper for NetIoHandler { let tap_fd = tap.as_raw_fd(); notifiers.push(build_event_notifier( tap_fd, + Some(handler.clone()), + NotifierOperation::AddShared, + EventSet::IN | EventSet::EDGE_TRIGGERED, + )); + let recv_evt_fd = locked_net_io.rx.recv_evt.as_raw_fd(); + notifiers.push(build_event_notifier( + recv_evt_fd, Some(handler), NotifierOperation::AddShared, EventSet::IN | EventSet::EDGE_TRIGGERED, @@ -1540,8 +1560,9 @@ impl VirtioDevice for Net { } let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); + let recv_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); let mut handler = NetIoHandler { - rx: RxVirtio::new(rx_queue, rx_queue_evt), + rx: RxVirtio::new(rx_queue, rx_queue_evt, recv_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), tap: self.taps.as_ref().map(|t| t[index].clone()), tap_fd: -1, -- Gitee From 7944e3704ce3d096ada2920e456c47bed75cb08c Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 14:39:26 +0800 Subject: [PATCH 1590/2187] scream: Add API declarations for OHOS audio framework Introduce OH Audio FrameWork APIs for RUST Signed-off-by: zhanghan64 --- .../src/misc/scream/ohos/ohaudio_bindings.rs | 325 ++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100755 devices/src/misc/scream/ohos/ohaudio_bindings.rs diff --git a/devices/src/misc/scream/ohos/ohaudio_bindings.rs b/devices/src/misc/scream/ohos/ohaudio_bindings.rs new file mode 100755 index 000000000..f5c8d1cdd --- /dev/null +++ b/devices/src/misc/scream/ohos/ohaudio_bindings.rs @@ -0,0 +1,325 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +/// The call was successful. +pub const OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_SUCCESS: OhAudioStreamResult = 0; +#[allow(unused)] +/// This means that the function was executed with an invalid input parameter. +pub const OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_INVALID_PARAM: OhAudioStreamResult = 1; +#[allow(unused)] +/// Execution status exception +pub const OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_ILLEGAL_STATE: OhAudioStreamResult = 2; +#[allow(unused)] +/// An system error has occurred +pub const OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_SYSTEM: OhAudioStreamResult = 3; +/// Define the result of the function execution. +/// +/// @since 10 +pub type OhAudioStreamResult = ::std::os::raw::c_uint; + +/// The type for audio stream is renderer. +pub const OH_AUDIO_STREAM_TYPE_AUDIOSTREAM_TYPE_RERNDERER: OhAudioStreamType = 1; +/// The type for audio stream is capturer. +pub const OH_AUDIO_STREAM_TYPE_AUDIOSTREAM_TYPE_CAPTURER: OhAudioStreamType = 2; +/// Define the audio stream type. +/// +/// @since 10 +pub type OhAudioStreamType = ::std::os::raw::c_uint; + +#[allow(unused)] +pub const OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_U8: OhAudioStreamSampleFormat = 0; +pub const OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S16_LE: OhAudioStreamSampleFormat = 1; +pub const OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S24_LE: OhAudioStreamSampleFormat = 2; +pub const OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S32_LE: OhAudioStreamSampleFormat = 3; +#[allow(unused)] +pub const OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_F32_LE: OhAudioStreamSampleFormat = 4; +/// Define the audio stream sample format. +/// +/// @since 10 +pub type OhAudioStreamSampleFormat = ::std::os::raw::c_uint; + +#[allow(unused)] +pub const OH_AUDIO_STREAM_ENCODING_TYPE_AUDIOSTREAM_ENCODING_TYPE_RAW: OhAudioStreamEncodingType = + 0; +/// Define the audio encoding type. +/// +/// @since 10 +pub type OhAudioStreamEncodingType = ::std::os::raw::c_uint; + +#[allow(unused)] +pub const OH_AUDIO_STREAM_USAGE_AUDIOSTREAM_USAGE_UNKNOWN: OhAudioStreamUsage = 0; +#[allow(unused)] +pub const OH_AUDIO_STREAM_USAGE_AUDIOSTREAM_USAGE_MEDIA: OhAudioStreamUsage = 1; +#[allow(unused)] +pub const OH_AUDIO_STREAM_USAGE_AUDIOSTREAM_USAGE_COMMUNICATION: OhAudioStreamUsage = 2; +/// Define the audio stream usage. +/// Audio stream usage is used to describe what work scenario +/// the current stream is used for. +/// +/// @since 10 +pub type OhAudioStreamUsage = ::std::os::raw::c_uint; + +#[allow(unused)] +pub const OH_AUDIO_STREAM_CONTENT_AUDIOSTREAM_CONTENT_TYPE_UNKNOWN: OhAudioStreamContent = 0; +#[allow(unused)] +pub const OH_AUDIO_STREAM_CONTENT_AUDIOSTREAM_CONTENT_TYPE_SPEECH: OhAudioStreamContent = 1; +#[allow(unused)] +pub const OH_AUDIO_STREAM_CONTENT_AUDIOSTREAM_CONTENT_TYPE_MUSIC: OhAudioStreamContent = 2; +#[allow(unused)] +pub const OH_AUDIO_STREAM_CONTENT_AUDIOSTREAM_CONTENT_TYPE_MOVIE: OhAudioStreamContent = 3; +/// Define the audio stream content. +/// Audio stream content is used to describe the stream data type. +/// +/// @since 10 +pub type OhAudioStreamContent = ::std::os::raw::c_uint; + +#[allow(unused)] +/// This is a normal audio scene. +pub const OH_AUDIO_STREAM_LATENCY_MODE_NORMAL: OhAudioStreamLatencyMode = 0; +#[allow(unused)] +pub const OH_AUDIO_STREAM_LATENCY_MODE_FAST: OhAudioStreamLatencyMode = 1; +/// Define the audio latency mode. +/// +/// @since 10 +pub type OhAudioStreamLatencyMode = ::std::os::raw::c_uint; + +#[allow(unused)] +/// The invalid state. +pub const OH_AUDIO_STREAM_STATE_AUDIOSTREAM_STATE_INVALID: OhAudioStreamState = -1; +#[allow(unused)] +/// The prepared state. +pub const OH_AUDIO_STREAM_STATE_AUDIOSTREAM_STATE_PREPARED: OhAudioStreamState = 0; +#[allow(unused)] +/// The stream is running. +pub const OH_AUDIO_STREAM_STATE_AUDIOSTREAM_STATE_RUNNING: OhAudioStreamState = 1; +#[allow(unused)] +/// The stream is stopped. +pub const OH_AUDIO_STREAM_STATE_AUDIOSTREAM_STATE_STOPPED: OhAudioStreamState = 2; +#[allow(unused)] +/// The stream is paused. +pub const OH_AUDIO_STREAM_STATE_AUDIOSTREAM_STATE_PAUSED: OhAudioStreamState = 3; +#[allow(unused)] +/// The stream is released. +pub const OH_AUDIO_STREAM_STATE_AUDIOSTREAM_STATE_RELEASED: OhAudioStreamState = 4; +#[allow(unused)] +/// The audio stream states +/// +/// @since 10" +pub type OhAudioStreamState = ::std::os::raw::c_int; + +#[allow(unused)] +pub const OH_AUDIO_STREAM_SOURCE_TYPE_AUDIOSTREAM_SOURCE_TYPE_INVALID: OHAudioStreamSourceType = -1; +#[allow(unused)] +pub const OH_AUDIO_STREAM_SOURCE_TYPE_AUDIOSTREAM_SOURCE_TYPE_MIC: OHAudioStreamSourceType = 0; +#[allow(unused)] +pub const OH_AUDIO_STREAM_SOURCE_TYPE_AUDIOSTREAM_SOURCE_TYPE_VOICE_RECOGNITION: + OHAudioStreamSourceType = 1; +#[allow(unused)] +pub const OH_AUDIO_STREAM_SOURCE_TYPE_AUDIOSTREAM_SOURCE_TYPE_VOICE_COMMUNICATION: + OHAudioStreamSourceType = 7; +/// Defines the audio source type. +/// +/// @since 10 +pub type OHAudioStreamSourceType = ::std::os::raw::c_int; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct OH_AudioStreamBuilderStruct { + _unused: [u8; 0], +} +/// Declaring the audio stream builder. +/// The instance of builder is used for creating audio stream. +/// +/// @since 10 +pub type OhAudioStreamBuilder = OH_AudioStreamBuilderStruct; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct OH_AudioRendererStruct { + _unused: [u8; 0], +} +/// Declaring the audio renderer stream. +/// The instance of renderer stream is used for playing audio data. +/// +/// @since 10 +pub type OhAudioRenderer = OH_AudioRendererStruct; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct OH_AudioCapturerStruct { + _unused: [u8; 0], +} +/// Declaring the audio capturer stream. +/// The instance of renderer stream is used for capturing audio data. +/// +/// @since 10 +pub type OhAudioCapturer = OH_AudioCapturerStruct; + +type PlaceHolderFn = std::option::Option i32>; + +/// Declaring the callback struct for renderer stream. +/// +/// @since 10 +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct OhAudioRendererCallbacks { + /// This function pointer will point to the callback function that + /// is used to write audio data + pub oh_audio_renderer_on_write_data: ::std::option::Option< + unsafe extern "C" fn( + renderer: *mut OhAudioRenderer, + userData: *mut ::std::os::raw::c_void, + buffer: *mut ::std::os::raw::c_void, + length: i32, + ) -> i32, + >, + pub oh_audio_renderer_on_stream_event: PlaceHolderFn, + pub oh_audio_renderer_on_interrpt_event: PlaceHolderFn, + pub oh_audio_renderer_on_error: PlaceHolderFn, +} + +/// Declaring the callback struct for capturer stream. +/// +/// @since 10 +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct OhAudioCapturerCallbacks { + /// This function pointer will point to the callback function that + /// is used to read audio data. + pub oh_audio_capturer_on_read_data: ::std::option::Option< + unsafe extern "C" fn( + capturer: *mut OhAudioCapturer, + userData: *mut ::std::os::raw::c_void, + buffer: *mut ::std::os::raw::c_void, + length: i32, + ) -> i32, + >, + pub oh_audio_capturer_on_stream_event: PlaceHolderFn, + pub oh_audio_capturer_on_interrpt_event: PlaceHolderFn, + pub oh_audio_capturer_on_error: PlaceHolderFn, +} + +#[allow(unused)] +#[link(name = "ohaudio")] +extern "C" { + pub fn OH_AudioRenderer_Release(renderer: *mut OhAudioRenderer) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_Start(renderer: *mut OhAudioRenderer) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_Pause(renderer: *mut OhAudioRenderer) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_Stop(renderer: *mut OhAudioRenderer) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_Flush(renderer: *mut OhAudioRenderer) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetCurrentState( + renderer: *mut OhAudioRenderer, + state: *mut OhAudioStreamState, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetSamplingRate( + renderer: *mut OhAudioRenderer, + rate: *mut i32, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetStreamId( + renderer: *mut OhAudioRenderer, + streamId: *mut u32, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetChannelCount( + renderer: *mut OhAudioRenderer, + channelCount: *mut i32, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetSampleFormat( + renderer: *mut OhAudioRenderer, + sampleFormat: *mut OhAudioStreamSampleFormat, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetLatencyMode( + renderer: *mut OhAudioRenderer, + latencyMode: *mut OhAudioStreamLatencyMode, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetRendererInfo( + renderer: *mut OhAudioRenderer, + usage: *mut OhAudioStreamUsage, + content: *mut OhAudioStreamContent, + ) -> OhAudioStreamResult; + pub fn OH_AudioRenderer_GetEncodingType( + renderer: *mut OhAudioRenderer, + encodingType: *mut OhAudioStreamEncodingType, + ) -> OhAudioStreamResult; + /// Create a streamBuilder can be used to open a renderer or capturer client. + /// + /// OH_AudioStreamBuilder_Destroy() must be called when you are done using the builder. + /// + /// @since 10 + /// + /// @param builder The builder reference to the created result. + /// @param type The stream type to be created. {@link #AUDIOSTREAM_TYPE_RERNDERER} or {@link #AUDIOSTREAM_TYPE_CAPTURER} + /// @return {@link #AUDIOSTREAM_SUCCESS} or an undesired error. + pub fn OH_AudioStreamBuilder_Create( + builder: *mut *mut OhAudioStreamBuilder, + type_: OhAudioStreamType, + ) -> OhAudioStreamResult; + /// Destroy a streamBulder. + /// + /// This function must be called when you are done using the builder. + /// + /// @since 10 + /// + /// @param builder Reference provided by OH_AudioStreamBuilder_Create() + /// @return {@link #AUDIOSTREAM_SUCCESS} or au undesired error. + pub fn OH_AudioStreamBuilder_Destroy(builder: *mut OhAudioStreamBuilder) + -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetSamplingRate( + builder: *mut OhAudioStreamBuilder, + rate: i32, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetChannelCount( + builder: *mut OhAudioStreamBuilder, + channelCount: i32, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetSampleFormat( + builder: *mut OhAudioStreamBuilder, + format: OhAudioStreamSampleFormat, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetEncodingType( + builder: *mut OhAudioStreamBuilder, + encodingType: OhAudioStreamEncodingType, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetLatencyMode( + builder: *mut OhAudioStreamBuilder, + latencyMode: OhAudioStreamLatencyMode, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetRendererInfo( + builder: *mut OhAudioStreamBuilder, + usage: OhAudioStreamUsage, + content: OhAudioStreamContent, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetCapturerInfo( + builder: *mut OhAudioStreamBuilder, + sourceType: OHAudioStreamSourceType, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetRendererCallback( + builder: *mut OhAudioStreamBuilder, + callbacks: OhAudioRendererCallbacks, + userData: *mut ::std::os::raw::c_void, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_SetCapturerCallback( + builder: *mut OhAudioStreamBuilder, + callbacks: OhAudioCapturerCallbacks, + userdata: *mut ::std::os::raw::c_void, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_GenerateRenderer( + builder: *mut OhAudioStreamBuilder, + audioRenderer: *mut *mut OhAudioRenderer, + ) -> OhAudioStreamResult; + pub fn OH_AudioStreamBuilder_GenerateCapturer( + builder: *mut OhAudioStreamBuilder, + audioCapturer: *mut *mut OhAudioCapturer, + ) -> OhAudioStreamResult; + pub fn OH_AudioCapturer_Start(capturer: *mut OhAudioCapturer) -> OhAudioStreamResult; + pub fn OH_AudioCapturer_Release(capturer: *mut OhAudioCapturer) -> OhAudioStreamResult; + pub fn OH_AudioCapturer_Stop(capturer: *mut OhAudioCapturer) -> OhAudioStreamResult; +} -- Gitee From bc7599e4995c33a5079f0005d8b5567b85aa61a5 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 31 Jan 2024 14:58:03 +0800 Subject: [PATCH 1591/2187] scream: add basic logic of scream support scream device on OHOS. Add basic defination and APIs. We use OHOS Audio Framework instead of ALSA/Pulseaudio Signed-off-by: zhanghan64 --- Cargo.toml | 1 + devices/Cargo.toml | 1 + devices/src/misc/scream/alsa.rs | 14 +- devices/src/misc/scream/audio_demo.rs | 8 +- devices/src/misc/scream/mod.rs | 26 +- devices/src/misc/scream/ohos/mod.rs | 14 + .../src/misc/scream/ohos/ohaudio_bindings.rs | 4 +- devices/src/misc/scream/ohos/ohaudio_rapi.rs | 355 ++++++++++++++++++ devices/src/misc/scream/pulseaudio.rs | 25 +- machine/Cargo.toml | 1 + machine_manager/Cargo.toml | 1 + 11 files changed, 419 insertions(+), 31 deletions(-) create mode 100755 devices/src/misc/scream/ohos/mod.rs create mode 100755 devices/src/misc/scream/ohos/ohaudio_rapi.rs diff --git a/Cargo.toml b/Cargo.toml index 951638e8b..adb64a405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ default = [] boot_time = ["machine/boot_time"] scream_alsa = ["machine/scream_alsa"] scream_pulseaudio = ["machine/scream_pulseaudio"] +scream_ohaudio = ["machine/scream_ohaudio"] pvpanic = ["machine/pvpanic"] demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 7355a6d07..976959a39 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -45,6 +45,7 @@ default = [] scream = ["machine_manager/scream"] scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] +scream_ohaudio = ["scream", "machine_manager/scream_ohaudio"] pvpanic = ["machine_manager/pvpanic"] demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index 3ce2e0f78..8e3337d1a 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -25,7 +25,7 @@ use log::{debug, error, warn}; use super::{ AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, AUDIO_SAMPLE_RATE_44KHZ, - AUDIO_SAMPLE_RATE_48KHZ, TARGET_LATENCY_MS, WINDOWS_SAMPLE_BASE_RATE, + TARGET_LATENCY_MS, }; const MAX_CHANNELS: u8 = 8; @@ -106,11 +106,7 @@ impl AlsaStreamData { // If audio format changed, reconfigure. self.stream_fmt = recv_data.fmt; - self.rate = if recv_data.fmt.rate >= WINDOWS_SAMPLE_BASE_RATE { - AUDIO_SAMPLE_RATE_44KHZ - } else { - AUDIO_SAMPLE_RATE_48KHZ - } * (recv_data.fmt.rate % WINDOWS_SAMPLE_BASE_RATE) as u32; + self.rate = recv_data.fmt.get_rate(); match recv_data.fmt.size { 16 => { @@ -210,10 +206,10 @@ impl AudioInterface for AlsaStreamData { } } - fn receive(&mut self, recv_data: &StreamData) -> bool { + fn receive(&mut self, recv_data: &StreamData) -> i32 { if !self.check_fmt_update(recv_data) { self.destroy(); - return false; + return 0; } let mut frames = 0; @@ -266,7 +262,7 @@ impl AudioInterface for AlsaStreamData { } } } - true + 1 } fn destroy(&mut self) { diff --git a/devices/src/misc/scream/audio_demo.rs b/devices/src/misc/scream/audio_demo.rs index 9ce29e955..d22b7bc32 100644 --- a/devices/src/misc/scream/audio_demo.rs +++ b/devices/src/misc/scream/audio_demo.rs @@ -64,7 +64,7 @@ impl AudioInterface for AudioDemo { .unwrap_or_else(|e| error!("Failed to flush data to file: {:?}", e)); } - fn receive(&mut self, recv_data: &StreamData) -> bool { + fn receive(&mut self, recv_data: &StreamData) -> i32 { thread::sleep(time::Duration::from_millis(20)); // SAFETY: Audio demo device is only used for test. let data = unsafe { @@ -78,7 +78,11 @@ impl AudioInterface for AudioDemo { 0 }); - size == data.len() + if size == data.len() { + 1 + } else { + 0 + } } fn destroy(&mut self) {} diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 54af9bbe0..ab66dc806 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -13,6 +13,8 @@ #[cfg(feature = "scream_alsa")] mod alsa; mod audio_demo; +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +mod ohos; #[cfg(feature = "scream_pulseaudio")] mod pulseaudio; @@ -150,6 +152,17 @@ impl Default for ShmemStreamFmt { } } +impl ShmemStreamFmt { + pub fn get_rate(&self) -> u32 { + let ret = if self.rate >= WINDOWS_SAMPLE_BASE_RATE { + AUDIO_SAMPLE_RATE_44KHZ + } else { + AUDIO_SAMPLE_RATE_48KHZ + }; + ret * (self.rate % WINDOWS_SAMPLE_BASE_RATE) as u32 + } +} + /// Audio stream data structure. #[derive(Default)] pub struct StreamData { @@ -294,14 +307,18 @@ impl StreamData { // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let capt = &mut header.capt; + let addr = hva + capt.offset as u64; + let mut locked_interface = interface.lock().unwrap(); + locked_interface.pre_receive(addr, capt); while capt.is_started != 0 { if !self.update_buffer_by_chunk_idx(hva, shmem_size, capt) { return; } - if interface.lock().unwrap().receive(self) { - self.chunk_idx = (self.chunk_idx + 1) % capt.max_chunks; + let ret = locked_interface.receive(self); + if ret > 0 { + self.chunk_idx = (self.chunk_idx + ret as u16) % capt.max_chunks; // Make sure chunk_idx write does not bypass audio chunk write. fence(Ordering::SeqCst); @@ -430,6 +447,9 @@ impl Scream { pub trait AudioInterface: Send { fn send(&mut self, recv_data: &StreamData); - fn receive(&mut self, recv_data: &StreamData) -> bool; + // For OHOS's audio task. It confirms shmem info. + #[allow(unused_variables)] + fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) {} + fn receive(&mut self, recv_data: &StreamData) -> i32; fn destroy(&mut self); } diff --git a/devices/src/misc/scream/ohos/mod.rs b/devices/src/misc/scream/ohos/mod.rs new file mode 100755 index 000000000..14c42b713 --- /dev/null +++ b/devices/src/misc/scream/ohos/mod.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod ohaudio_bindings; +mod ohaudio_rapi; diff --git a/devices/src/misc/scream/ohos/ohaudio_bindings.rs b/devices/src/misc/scream/ohos/ohaudio_bindings.rs index f5c8d1cdd..4c11f7848 100755 --- a/devices/src/misc/scream/ohos/ohaudio_bindings.rs +++ b/devices/src/misc/scream/ohos/ohaudio_bindings.rs @@ -175,7 +175,7 @@ pub struct OhAudioRendererCallbacks { /// This function pointer will point to the callback function that /// is used to write audio data pub oh_audio_renderer_on_write_data: ::std::option::Option< - unsafe extern "C" fn( + extern "C" fn( renderer: *mut OhAudioRenderer, userData: *mut ::std::os::raw::c_void, buffer: *mut ::std::os::raw::c_void, @@ -196,7 +196,7 @@ pub struct OhAudioCapturerCallbacks { /// This function pointer will point to the callback function that /// is used to read audio data. pub oh_audio_capturer_on_read_data: ::std::option::Option< - unsafe extern "C" fn( + extern "C" fn( capturer: *mut OhAudioCapturer, userData: *mut ::std::os::raw::c_void, buffer: *mut ::std::os::raw::c_void, diff --git a/devices/src/misc/scream/ohos/ohaudio_rapi.rs b/devices/src/misc/scream/ohos/ohaudio_rapi.rs new file mode 100755 index 000000000..289637e89 --- /dev/null +++ b/devices/src/misc/scream/ohos/ohaudio_rapi.rs @@ -0,0 +1,355 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::c_void; +use std::ptr; + +use log::error; + +use super::ohaudio_bindings as capi; +use crate::misc::scream::{AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ}; + +macro_rules! call_capi { + ( $f: ident ( $($x: expr),* ) ) => { + { + // SAFETY: OH Audio FrameWork's APIs guarantee safety. + let r = unsafe { capi::$f( $($x),* ) }; + if r != capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_SUCCESS { + error!("ohauadio_rapi: failed at {:?}", stringify!($f)); + Err(OAErr::from(r)) + } else { + Ok(()) + } + } + }; +} + +macro_rules! call_capi_nocheck { + ( $f: ident ( $($x: expr),* ) ) => { + // SAFETY: OH Audio FrameWork's APIs guarantee safety. + unsafe { capi::$f( $($x),* ) } + }; +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum OAErr { + Ok, + InvalidParam, + IllegalState, + SysErr, + UnknownErr, +} + +impl std::error::Error for OAErr {} + +impl std::fmt::Display for OAErr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{}", + match self { + OAErr::Ok => "Ok", + OAErr::InvalidParam => "InvalidParam", + OAErr::IllegalState => "IllegalState", + OAErr::SysErr => "SysErr", + OAErr::UnknownErr => "UnknownErr", + } + ) + } +} + +impl From for OAErr { + #[inline] + fn from(c: capi::OhAudioStreamResult) -> Self { + match c { + capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_SUCCESS => Self::Ok, + capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_INVALID_PARAM => Self::InvalidParam, + capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_ILLEGAL_STATE => Self::IllegalState, + capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_SYSTEM => Self::SysErr, + _ => Self::UnknownErr, + } + } +} + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct SampleSize(pub capi::OhAudioStreamType); + +impl TryFrom for SampleSize { + type Error = OAErr; + + #[inline] + fn try_from(s: u8) -> Result { + match s { + 16 => Ok(SampleSize( + capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S16_LE, + )), + 24 => Ok(SampleSize( + capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S24_LE, + )), + 32 => Ok(SampleSize( + capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S32_LE, + )), + _ => Err(OAErr::InvalidParam), + } + } +} + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct SampleRate(pub i32); + +impl TryFrom for SampleRate { + type Error = OAErr; + + #[inline] + fn try_from(value: u32) -> Result { + match value { + AUDIO_SAMPLE_RATE_44KHZ => Ok(SampleRate(value as i32)), + AUDIO_SAMPLE_RATE_48KHZ => Ok(SampleRate(value as i32)), + _ => Err(OAErr::InvalidParam), + } + } +} + +impl Default for SampleRate { + fn default() -> Self { + Self(AUDIO_SAMPLE_RATE_44KHZ as i32) + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, Eq, Default)] +struct AudioSpec { + size: SampleSize, + rate: SampleRate, + channels: u8, +} + +impl PartialEq for AudioSpec { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.size == other.size && self.rate == other.rate && self.channels == other.channels + } +} + +impl AudioSpec { + fn set(&mut self, size: u8, rate: u32, channels: u8) -> Result<(), OAErr> { + self.size = SampleSize::try_from(size)?; + self.rate = SampleRate::try_from(rate)?; + self.channels = channels; + Ok(()) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum AudioStreamType { + Render, + Capturer, +} + +#[allow(clippy::from_over_into)] +impl Into for AudioStreamType { + fn into(self) -> capi::OhAudioStreamType { + match self { + AudioStreamType::Render => capi::OH_AUDIO_STREAM_TYPE_AUDIOSTREAM_TYPE_RERNDERER, + AudioStreamType::Capturer => capi::OH_AUDIO_STREAM_TYPE_AUDIOSTREAM_TYPE_CAPTURER, + } + } +} + +pub enum AudioProcessCb { + CapturerCb( + Option< + extern "C" fn( + capturer: *mut capi::OhAudioCapturer, + userData: *mut c_void, + buffer: *mut c_void, + length: i32, + ) -> i32, + >, + ), + RendererCb( + Option< + extern "C" fn( + renderer: *mut capi::OhAudioRenderer, + userData: *mut c_void, + buffer: *mut c_void, + length: i32, + ) -> i32, + >, + ), +} + +pub struct AudioContext { + stream_type: AudioStreamType, + spec: AudioSpec, + builder: *mut capi::OhAudioStreamBuilder, + capturer: *mut capi::OhAudioCapturer, + renderer: *mut capi::OhAudioRenderer, + userdata: *mut c_void, +} + +impl Drop for AudioContext { + fn drop(&mut self) { + if !self.capturer.is_null() || !self.renderer.is_null() { + self.stop(); + } + if !self.builder.is_null() { + call_capi_nocheck!(OH_AudioStreamBuilder_Destroy(self.builder)); + self.builder = ptr::null_mut(); + } + } +} + +impl AudioContext { + #[inline(always)] + fn set_userdata(&mut self, userdata: *mut c_void) { + self.userdata = userdata; + } + + fn create_builder(&mut self) -> Result<(), OAErr> { + call_capi!(OH_AudioStreamBuilder_Create( + &mut self.builder, + self.stream_type.into() + )) + } + + fn set_sample_rate(&self) -> Result<(), OAErr> { + call_capi!(OH_AudioStreamBuilder_SetSamplingRate( + self.builder, + self.spec.rate.0 + )) + } + + fn set_sample_format(&self) -> Result<(), OAErr> { + call_capi!(OH_AudioStreamBuilder_SetSampleFormat( + self.builder, + self.spec.size.0 + )) + } + + #[allow(unused)] + fn set_latency_mode(&self) -> Result<(), OAErr> { + call_capi!(OH_AudioStreamBuilder_SetLatencyMode( + self.builder, + capi::OH_AUDIO_STREAM_LATENCY_MODE_FAST + )) + } + + fn create_renderer(&mut self, cb: AudioProcessCb) -> Result<(), OAErr> { + let mut cbs = capi::OhAudioRendererCallbacks::default(); + if let AudioProcessCb::RendererCb(f) = cb { + cbs.oh_audio_renderer_on_write_data = f; + } + call_capi!(OH_AudioStreamBuilder_SetRendererCallback( + self.builder, + cbs, + self.userdata + ))?; + call_capi!(OH_AudioStreamBuilder_GenerateRenderer( + self.builder, + &mut self.renderer + )) + } + + fn create_capturer(&mut self, cb: AudioProcessCb) -> Result<(), OAErr> { + let mut cbs = capi::OhAudioCapturerCallbacks::default(); + if let AudioProcessCb::CapturerCb(v) = cb { + cbs.oh_audio_capturer_on_read_data = v; + } + call_capi!(OH_AudioStreamBuilder_SetCapturerCallback( + self.builder, + cbs, + self.userdata + ))?; + call_capi!(OH_AudioStreamBuilder_GenerateCapturer( + self.builder, + &mut self.capturer + )) + } + + fn create_processor(&mut self, cb: AudioProcessCb) -> Result<(), OAErr> { + match self.stream_type { + AudioStreamType::Capturer => self.create_capturer(cb), + AudioStreamType::Render => self.create_renderer(cb), + } + } + + fn start_capturer(&self) -> Result<(), OAErr> { + call_capi!(OH_AudioCapturer_Start(self.capturer)) + } + + fn start_renderer(&self) -> Result<(), OAErr> { + call_capi!(OH_AudioRenderer_Start(self.renderer)) + } + + pub fn new(stream_type: AudioStreamType) -> Self { + Self { + stream_type, + spec: AudioSpec::default(), + builder: ptr::null_mut(), + capturer: ptr::null_mut(), + renderer: ptr::null_mut(), + userdata: std::ptr::null_mut::(), + } + } + + pub fn init( + &mut self, + size: u8, + rate: u32, + channels: u8, + cb: AudioProcessCb, + userdata: *mut c_void, + ) -> Result<(), OAErr> { + self.set_userdata(userdata); + self.create_builder()?; + self.set_fmt(size, rate, channels)?; + self.set_sample_rate()?; + self.set_sample_format()?; + self.create_processor(cb) + } + + pub fn start(&self) -> Result<(), OAErr> { + match self.stream_type { + AudioStreamType::Capturer => self.start_capturer(), + AudioStreamType::Render => self.start_renderer(), + } + } + + pub fn stop(&mut self) { + match self.stream_type { + AudioStreamType::Capturer => { + call_capi_nocheck!(OH_AudioCapturer_Stop(self.capturer)); + call_capi_nocheck!(OH_AudioCapturer_Release(self.capturer)); + self.capturer = ptr::null_mut(); + } + AudioStreamType::Render => { + call_capi_nocheck!(OH_AudioRenderer_Stop(self.renderer)); + call_capi_nocheck!(OH_AudioRenderer_Release(self.renderer)); + self.renderer = ptr::null_mut(); + } + } + } + + pub fn set_fmt(&mut self, size: u8, rate: u32, channels: u8) -> Result<(), OAErr> { + self.spec.set(size, rate, channels) + } + + pub fn check_fmt(&self, size: u8, rate: u32, channels: u8) -> bool { + let mut other = AudioSpec::default(); + other + .set(size, rate, channels) + .map_or(false, |_| (self.spec == other)) + } +} diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 2eeba3545..c42fabacd 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -22,9 +22,7 @@ use pulse::{ time::MicroSeconds, }; -use super::{ - AudioInterface, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, -}; +use super::{AudioInterface, AUDIO_SAMPLE_RATE_44KHZ}; use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData, TARGET_LATENCY_MS}; const MAX_LATENCY_MS: u32 = 100; @@ -164,11 +162,7 @@ impl PulseStreamData { // If audio format changed, reconfigure self.stream_fmt = recv_data.fmt; self.ss.channels = recv_data.fmt.channels; - self.ss.rate = if recv_data.fmt.rate >= WINDOWS_SAMPLE_BASE_RATE { - AUDIO_SAMPLE_RATE_44KHZ - } else { - AUDIO_SAMPLE_RATE_48KHZ - } * (recv_data.fmt.rate % WINDOWS_SAMPLE_BASE_RATE) as u32; + self.ss.rate = recv_data.fmt.get_rate(); match recv_data.fmt.size { 16 => self.ss.format = Format::S16le, @@ -257,11 +251,11 @@ impl AudioInterface for PulseStreamData { } } - fn receive(&mut self, recv_data: &StreamData) -> bool { + fn receive(&mut self, recv_data: &StreamData) -> i32 { self.check_fmt_update(recv_data); if self.simple.is_none() { - return false; + return 0; } // SAFETY: audio_base is the shared memory. It already verifies the validity @@ -276,10 +270,10 @@ impl AudioInterface for PulseStreamData { if let Err(e) = self.simple.as_ref().unwrap().read(data) { error!("PulseAudio read data failed: {:?}", e); self.ss.rate = 0; - return false; + return 0; } - true + 1 } fn destroy(&mut self) { @@ -301,10 +295,11 @@ impl AudioInterface for PulseStreamData { mod tests { use pulse::{channelmap::Position, sample::Format}; - use super::{ - PulseStreamData, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, WINDOWS_SAMPLE_BASE_RATE, + use super::PulseStreamData; + use crate::misc::scream::{ + ScreamDirection, StreamData, AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ, + WINDOWS_SAMPLE_BASE_RATE, }; - use crate::misc::scream::{ScreamDirection, StreamData}; #[test] fn test_channel_map_transfer() { diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 081d2dc83..6fc0d0caf 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -36,6 +36,7 @@ boot_time = ["cpu/boot_time"] scream = ["devices/scream", "machine_manager/scream"] scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] +scream_ohaudio = ["scream", "devices/scream_ohaudio", "machine_manager/scream_ohaudio"] pvpanic = ["devices/pvpanic"] demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 8797af7a5..203c6ac3c 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -28,6 +28,7 @@ default = [] scream = [] scream_alsa = ["scream"] scream_pulseaudio = ["scream"] +scream_ohaudio = ["scream"] demo_device = [] usb_host = [] usb_camera = [] -- Gitee From 146d90d22ac490dcbeccb471c53f6c79223cef14 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 31 Jan 2024 14:58:33 +0800 Subject: [PATCH 1592/2187] scream:Enable scream device on OHOS. Enable scream on OHOS Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 14 +- devices/src/misc/scream/ohos/mod.rs | 2 + devices/src/misc/scream/ohos/ohaudio.rs | 364 ++++++++++++++++++++++++ machine_manager/src/config/scream.rs | 4 + 4 files changed, 379 insertions(+), 5 deletions(-) create mode 100755 devices/src/misc/scream/ohos/ohaudio.rs diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index ab66dc806..921a7b872 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -38,6 +38,8 @@ use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::scream::{ScreamConfig, ScreamInterface}; +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; @@ -154,12 +156,12 @@ impl Default for ShmemStreamFmt { impl ShmemStreamFmt { pub fn get_rate(&self) -> u32 { - let ret = if self.rate >= WINDOWS_SAMPLE_BASE_RATE { + let sample_rate = if self.rate >= WINDOWS_SAMPLE_BASE_RATE { AUDIO_SAMPLE_RATE_44KHZ } else { AUDIO_SAMPLE_RATE_48KHZ }; - ret * (self.rate % WINDOWS_SAMPLE_BASE_RATE) as u32 + sample_rate * (self.rate % WINDOWS_SAMPLE_BASE_RATE) as u32 } } @@ -316,9 +318,9 @@ impl StreamData { return; } - let ret = locked_interface.receive(self); - if ret > 0 { - self.chunk_idx = (self.chunk_idx + ret as u16) % capt.max_chunks; + let recv_chunks_cnt = locked_interface.receive(self); + if recv_chunks_cnt > 0 { + self.chunk_idx = (self.chunk_idx + recv_chunks_cnt as u16) % capt.max_chunks; // Make sure chunk_idx write does not bypass audio chunk write. fence(Ordering::SeqCst); @@ -356,6 +358,8 @@ impl Scream { ScreamInterface::Alsa => Arc::new(Mutex::new(AlsaStreamData::init(name, dir))), #[cfg(feature = "scream_pulseaudio")] ScreamInterface::PulseAudio => Arc::new(Mutex::new(PulseStreamData::init(name, dir))), + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + ScreamInterface::OhAudio => Arc::new(Mutex::new(OhAudio::init(dir))), ScreamInterface::Demo => Arc::new(Mutex::new(AudioDemo::init( dir, self.playback.clone(), diff --git a/devices/src/misc/scream/ohos/mod.rs b/devices/src/misc/scream/ohos/mod.rs index 14c42b713..2e5275ac9 100755 --- a/devices/src/misc/scream/ohos/mod.rs +++ b/devices/src/misc/scream/ohos/mod.rs @@ -10,5 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod ohaudio; + mod ohaudio_bindings; mod ohaudio_rapi; diff --git a/devices/src/misc/scream/ohos/ohaudio.rs b/devices/src/misc/scream/ohos/ohaudio.rs new file mode 100755 index 000000000..c8f45bae5 --- /dev/null +++ b/devices/src/misc/scream/ohos/ohaudio.rs @@ -0,0 +1,364 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::c_void; +use std::sync::{ + atomic::{fence, AtomicI32, Ordering}, + Arc, Mutex, +}; +use std::{cmp, ptr, thread, time}; + +use log::{error, warn}; + +use super::ohaudio_bindings::{OhAudioCapturer, OhAudioRenderer}; +use super::ohaudio_rapi::{AudioContext, AudioProcessCb, AudioStreamType}; +use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, StreamData}; + +trait OhAudioProcess { + fn init(&mut self, stream: &StreamData) -> bool; + fn destroy(&mut self); + fn preprocess(&mut self, _start_addr: u64, _sh_header: &ShmemStreamHeader) {} + fn process(&mut self, recv_data: &StreamData) -> i32; +} + +#[derive(Debug, Clone, Copy)] +struct StreamUnit { + pub addr: u64, + pub len: u64, +} + +const STREAM_DATA_VEC_CAPACITY: usize = 30; + +struct OhAudioRender { + ctx: Option, + stream_data: Arc>>, + prepared_data: u32, + start: bool, +} + +impl Default for OhAudioRender { + fn default() -> OhAudioRender { + OhAudioRender { + ctx: None, + stream_data: Arc::new(Mutex::new(Vec::with_capacity(STREAM_DATA_VEC_CAPACITY))), + prepared_data: 0, + start: false, + } + } +} + +impl OhAudioRender { + #[inline(always)] + fn check_data_ready(&self, recv_data: &StreamData) -> bool { + let size = recv_data.fmt.size as u32 / 8; + let channels = recv_data.fmt.channels as u32; + let rate = recv_data.fmt.get_rate() as u32; + // Wait for data of 500 ms ready. + // FIXME: the value of rate is wrong. + self.prepared_data >= (size * channels * rate / 2) + } + + fn check_fmt_update(&mut self, recv_data: &StreamData) { + if self.ctx.is_some() + && !self.ctx.as_ref().unwrap().check_fmt( + recv_data.fmt.size, + recv_data.fmt.get_rate(), + recv_data.fmt.channels, + ) + { + self.destroy(); + } + } +} + +impl OhAudioProcess for OhAudioRender { + fn init(&mut self, stream: &StreamData) -> bool { + let mut context = AudioContext::new(AudioStreamType::Render); + match context.init( + stream.fmt.size, + stream.fmt.get_rate(), + stream.fmt.channels, + AudioProcessCb::RendererCb(Some(on_write_data_cb)), + ptr::addr_of!(*self) as *mut c_void, + ) { + Ok(()) => self.ctx = Some(context), + Err(e) => { + error!("failed to create oh audio render context: {}", e); + return false; + } + } + match self.ctx.as_ref().unwrap().start() { + Ok(()) => { + self.start = true; + true + } + Err(e) => { + error!("failed to start oh audio renderer: {}", e); + false + } + } + } + + fn destroy(&mut self) { + if self.ctx.is_some() { + if self.start { + self.ctx.as_mut().unwrap().stop(); + self.start = false; + } + self.ctx = None; + } + self.stream_data.lock().unwrap().clear(); + self.prepared_data = 0; + } + + fn process(&mut self, recv_data: &StreamData) -> i32 { + self.check_fmt_update(recv_data); + + fence(Ordering::Acquire); + + let su = StreamUnit { + addr: recv_data.audio_base, + len: recv_data.audio_size as u64, + }; + self.stream_data.lock().unwrap().push(su); + + if !self.start { + self.prepared_data += recv_data.audio_size; + if self.check_data_ready(recv_data) && !self.init(recv_data) { + error!("failed to init oh audio"); + self.destroy(); + } + } + 0 + } +} + +#[derive(Default)] +struct OhAudioCapture { + ctx: Option, + align: u32, + new_chunks: AtomicI32, + shm_addr: u64, + shm_len: u64, + cur_pos: u64, + start: bool, +} + +impl OhAudioCapture { + fn check_fmt_update(&mut self, recv_data: &StreamData) { + if self.ctx.is_none() + || !self.ctx.as_ref().unwrap().check_fmt( + recv_data.fmt.size, + recv_data.fmt.get_rate(), + recv_data.fmt.channels, + ) + { + self.destroy(); + } + } +} + +impl OhAudioProcess for OhAudioCapture { + fn init(&mut self, stream: &StreamData) -> bool { + let mut context = AudioContext::new(AudioStreamType::Capturer); + match context.init( + stream.fmt.size, + stream.fmt.get_rate(), + stream.fmt.channels, + AudioProcessCb::CapturerCb(Some(on_read_data_cb)), + ptr::addr_of!(*self) as *mut c_void, + ) { + Ok(()) => self.ctx = Some(context), + Err(e) => { + error!("failed to create oh audio capturer context: {}", e); + return false; + } + } + match self.ctx.as_ref().unwrap().start() { + Ok(()) => { + self.start = true; + true + } + Err(e) => { + error!("failed to start oh audio capturer: {}", e); + false + } + } + } + + fn destroy(&mut self) { + if self.ctx.is_some() { + if self.start { + self.ctx.as_mut().unwrap().stop(); + self.start = false; + } + self.ctx = None; + } + } + + fn preprocess(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) { + self.align = sh_header.chunk_size; + self.new_chunks.store(0, Ordering::Release); + self.shm_addr = start_addr; + self.shm_len = sh_header.max_chunks as u64 * sh_header.chunk_size as u64; + self.cur_pos = start_addr + sh_header.chunk_idx as u64 * sh_header.chunk_size as u64; + } + + fn process(&mut self, recv_data: &StreamData) -> i32 { + self.check_fmt_update(recv_data); + if !self.start && !self.init(recv_data) { + self.destroy(); + return 0; + } + self.new_chunks.store(0, Ordering::Release); + while self.new_chunks.load(Ordering::Acquire) == 0 { + thread::sleep(time::Duration::from_millis(10)); + } + + self.new_chunks.load(Ordering::Acquire) + } +} + +extern "C" fn on_write_data_cb( + _renderer: *mut OhAudioRenderer, + user_data: *mut ::std::os::raw::c_void, + buffer: *mut ::std::os::raw::c_void, + length: i32, +) -> i32 { + // SAFETY: we make sure that it is OhAudioRender when register callback. + let render = unsafe { + (user_data as *mut OhAudioRender) + .as_mut() + .unwrap_unchecked() + }; + if !render.start { + return 0; + } + + // Copy stream data from shared memory to buffer. + let mut dst_addr = buffer as u64; + let mut left = length as u64; + let mut su_list = render.stream_data.lock().unwrap(); + while left > 0 && su_list.len() > 0 { + let su = &mut su_list[0]; + let len = cmp::min(left, su.len); + + // SAFETY: we checked len. + unsafe { + ptr::copy_nonoverlapping(su.addr as *const u8, dst_addr as *mut u8, len as usize) + }; + + dst_addr += len; + left -= len; + su.len -= len; + if su.len == 0 { + su_list.remove(0); + } else { + su.addr += len; + } + } + if left > 0 { + warn!("data in stream unit list is not enough"); + } + 0 +} + +extern "C" fn on_read_data_cb( + _capturer: *mut OhAudioCapturer, + user_data: *mut ::std::os::raw::c_void, + buffer: *mut ::std::os::raw::c_void, + length: i32, +) -> i32 { + // SAFETY: we make sure that it is OhAudioCapture when register callback. + let capture = unsafe { + (user_data as *mut OhAudioCapture) + .as_mut() + .unwrap_unchecked() + }; + + loop { + if !capture.start { + return 0; + } + if capture.new_chunks.load(Ordering::Acquire) == 0 { + break; + } + } + let old_pos = capture.cur_pos - ((capture.cur_pos - capture.shm_addr) % capture.align as u64); + let buf_end = capture.shm_addr + capture.shm_len; + let mut src_addr = buffer as u64; + let mut left = length as u64; + while left > 0 { + let len = cmp::min(left, buf_end - capture.cur_pos); + // SAFETY: we checked len. + unsafe { + ptr::copy_nonoverlapping( + src_addr as *const u8, + capture.cur_pos as *mut u8, + len as usize, + ) + }; + left -= len; + src_addr += len; + capture.cur_pos += len; + if capture.cur_pos == buf_end { + capture.cur_pos = capture.shm_addr; + } + } + + let new_chunks = match capture.cur_pos <= old_pos { + true => (capture.shm_len - (old_pos - capture.cur_pos)) / capture.align as u64, + false => (capture.cur_pos - old_pos) / capture.align as u64, + }; + capture + .new_chunks + .store(new_chunks as i32, Ordering::Release); + 0 +} + +pub struct OhAudio { + processor: Box, +} + +// SAFETY: OhAudio's 'send' trait is guaranteed by Mutex lock. +unsafe impl Send for OhAudio {} + +impl OhAudio { + pub fn init(dir: ScreamDirection) -> Self { + match dir { + ScreamDirection::Playback => Self { + processor: Box::new(OhAudioRender::default()), + }, + ScreamDirection::Record => Self { + processor: Box::new(OhAudioCapture::default()), + }, + } + } +} + +impl AudioInterface for OhAudio { + fn send(&mut self, recv_data: &StreamData) { + self.processor.process(recv_data); + } + + fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) { + self.processor.preprocess(start_addr, sh_header); + } + + fn receive(&mut self, recv_data: &StreamData) -> i32 { + self.processor.process(recv_data) + } + + fn destroy(&mut self) { + self.processor.destroy(); + } +} diff --git a/machine_manager/src/config/scream.rs b/machine_manager/src/config/scream.rs index d47059859..b06603a7e 100644 --- a/machine_manager/src/config/scream.rs +++ b/machine_manager/src/config/scream.rs @@ -23,6 +23,8 @@ pub enum ScreamInterface { Alsa, #[cfg(feature = "scream_pulseaudio")] PulseAudio, + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + OhAudio, Demo, } @@ -36,6 +38,8 @@ impl FromStr for ScreamInterface { #[cfg(feature = "scream_pulseaudio")] "PulseAudio" => Ok(ScreamInterface::PulseAudio), "Demo" => Ok(ScreamInterface::Demo), + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + "OhAudio" => Ok(ScreamInterface::OhAudio), _ => Err(anyhow!("Unknown scream interface")), } } -- Gitee From eed7fb8643402b8f4870d138a0bb795945cd8cfb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Feb 2024 22:52:34 +0800 Subject: [PATCH 1593/2187] hypervisor/kvm: Upgrade kvm-ioctls version to 0.15.0 Signed-off-by: Yan Wen Signed-off-by: Keqian Zhu --- Cargo.lock | 4 +- address_space/Cargo.toml | 2 +- cpu/Cargo.toml | 2 +- devices/Cargo.toml | 2 +- hypervisor/Cargo.toml | 2 +- hypervisor/src/kvm/aarch64/mod.rs | 75 +++++++++++++++---------------- migration/Cargo.toml | 2 +- util/Cargo.toml | 2 +- vfio/Cargo.toml | 2 +- virtio/Cargo.toml | 2 +- 10 files changed, 47 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65fde73b9..d725b87e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -783,9 +783,9 @@ dependencies = [ [[package]] name = "kvm-ioctls" -version = "0.13.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8dc9c1896e5f144ec5d07169bc29f39a047686d29585a91f30489abfaeb6b" +checksum = "9bdde2b46ee7b6587ef79f751019c4726c4f2d3e4628df5d69f3f9c5cb6c6bd4" dependencies = [ "kvm-bindings", "libc", diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 9a3cb25e5..a60a32231 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -11,7 +11,7 @@ libc = "0.2" log = "0.4" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" vmm-sys-util = "0.11.1" arc-swap = "1.6.0" thiserror = "1.0" diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index f4a5a6fa3..f823e0153 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -11,7 +11,7 @@ thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" log = "0.4" libc = "0.2" vmm-sys-util = "0.11.1" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 52f64fb61..d964215c0 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -11,7 +11,7 @@ thiserror = "1.0" anyhow = "1.0" libc = "0.2" log = "0.4" -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" serde = { version = "1.0", features = ["derive"] } vmm-sys-util = "0.11.1" byteorder = "1.4.3" diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index a2f2d0dff..1b5aa5d25 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -9,7 +9,7 @@ license = "Mulan PSL v2" anyhow = "1.0" thiserror = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" libc = "0.2" log = "0.4" vmm-sys-util = "0.11.1" diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index ac29a1557..41f987618 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -198,8 +198,7 @@ impl KvmCpu { self.arch_vcpu_init()?; arch_cpu.lock().unwrap().mpidr = - self.fd - .get_one_reg(KVM_REG_ARM_MPIDR_EL1) + self.get_one_reg(KVM_REG_ARM_MPIDR_EL1) .with_context(|| "Failed to get mpidr")? as u64; arch_cpu.lock().unwrap().features = *vcpu_config; @@ -207,8 +206,19 @@ impl KvmCpu { Ok(()) } + fn get_one_reg(&self, reg_id: u64) -> Result { + let mut val = [0_u8; 16]; + self.fd.get_one_reg(reg_id, &mut val)?; + Ok(u128::from_le_bytes(val)) + } + + fn set_one_reg(&self, reg_id: u64, val: u128) -> Result<()> { + self.fd.set_one_reg(reg_id, &val.to_le_bytes())?; + Ok(()) + } + pub fn arch_get_one_reg(&self, reg_id: u64) -> Result { - Ok(self.fd.get_one_reg(reg_id)?) + self.get_one_reg(reg_id) } pub fn arch_get_regs( @@ -251,7 +261,6 @@ impl KvmCpu { } RegsIndex::VtimerCount => { locked_arch_cpu.vtimer_cnt = self - .fd .get_one_reg(KVM_REG_ARM_TIMER_CNT) .with_context(|| "Failed to get virtual timer count")? as u64; @@ -290,8 +299,7 @@ impl KvmCpu { } } RegsIndex::VtimerCount => { - self.fd - .set_one_reg(KVM_REG_ARM_TIMER_CNT, locked_arch_cpu.vtimer_cnt as u128) + self.set_one_reg(KVM_REG_ARM_TIMER_CNT, locked_arch_cpu.vtimer_cnt as u128) .with_context(|| "Failed to set virtual timer count")?; } } @@ -309,34 +317,30 @@ impl KvmCpu { pub fn get_core_regs(&self) -> Result { let mut core_regs = kvm_regs::default(); - core_regs.regs.sp = self.fd.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; - core_regs.sp_el1 = self.fd.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())? as u64; - core_regs.regs.pstate = self.fd.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())? as u64; - core_regs.regs.pc = self.fd.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())? as u64; - core_regs.elr_el1 = self.fd.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())? as u64; + core_regs.regs.sp = self.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; + core_regs.sp_el1 = self.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())? as u64; + core_regs.regs.pstate = self.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())? as u64; + core_regs.regs.pc = self.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())? as u64; + core_regs.elr_el1 = self.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())? as u64; for i in 0..KVM_NR_REGS as usize { core_regs.regs.regs[i] = - self.fd - .get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())? as u64; + self.get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())? as u64; } for i in 0..KVM_NR_SPSR as usize { - core_regs.spsr[i] = self.fd.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; + core_regs.spsr[i] = self.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; } for i in 0..KVM_NR_FP_REGS as usize { - core_regs.fp_regs.vregs[i] = self - .fd - .get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; + core_regs.fp_regs.vregs[i] = + self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; } core_regs.fp_regs.fpsr = - self.fd - .get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())? as u32; + self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())? as u32; core_regs.fp_regs.fpcr = - self.fd - .get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())? as u32; + self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())? as u32; Ok(core_regs) } @@ -350,43 +354,38 @@ impl KvmCpu { /// * `vcpu_fd` - the VcpuFd in KVM mod. /// * `core_regs` - kvm_regs state to be written. pub fn set_core_regs(&self, core_regs: kvm_regs) -> Result<()> { - self.fd - .set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; - self.fd - .set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; - self.fd.set_one_reg( + self.set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; + self.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; + self.set_one_reg( Arm64CoreRegs::UserPTRegPState.into(), core_regs.regs.pstate as u128, )?; - self.fd - .set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc as u128)?; - self.fd - .set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1 as u128)?; + self.set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc as u128)?; + self.set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1 as u128)?; for i in 0..KVM_NR_REGS as usize { - self.fd.set_one_reg( + self.set_one_reg( Arm64CoreRegs::UserPTRegRegs(i).into(), core_regs.regs.regs[i] as u128, )?; } for i in 0..KVM_NR_SPSR as usize { - self.fd - .set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; + self.set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; } for i in 0..KVM_NR_FP_REGS as usize { - self.fd.set_one_reg( + self.set_one_reg( Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), core_regs.fp_regs.vregs[i], )?; } - self.fd.set_one_reg( + self.set_one_reg( Arm64CoreRegs::UserFPSIMDStateFpsr.into(), core_regs.fp_regs.fpsr as u128, )?; - self.fd.set_one_reg( + self.set_one_reg( Arm64CoreRegs::UserFPSIMDStateFpcr.into(), core_regs.fp_regs.fpcr as u128, )?; @@ -418,7 +417,7 @@ impl KvmCpu { pub fn get_cpreg(&self, cpreg_list_entry: &mut CpregListEntry) -> Result<()> { if self.normal_cpreg_entry(cpreg_list_entry) { - cpreg_list_entry.value = self.fd.get_one_reg(cpreg_list_entry.reg_id)?; + cpreg_list_entry.value = self.get_one_reg(cpreg_list_entry.reg_id)?; } Ok(()) @@ -426,7 +425,7 @@ impl KvmCpu { pub fn set_cpreg(&self, cpreg: &CpregListEntry) -> Result<()> { if self.normal_cpreg_entry(cpreg) { - self.fd.set_one_reg(cpreg.reg_id, cpreg.value)?; + self.set_one_reg(cpreg.reg_id, cpreg.value)?; } Ok(()) diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 5a9a00aa8..5679aa646 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Huawei StratoVirt Team"] edition = "2021" [dependencies] -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" once_cell = "1.18.0" diff --git a/util/Cargo.toml b/util/Cargo.toml index 61145fac9..f1e1ca1d9 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -13,7 +13,7 @@ thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["poll", "term", "time", "signal", "fs", "feature"] } -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" libc = "0.2" log = { version = "0.4", features = ["std"]} vmm-sys-util = "0.11.1" diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index bd8fd6b41..61f20bd45 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -11,7 +11,7 @@ byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" libc = "0.2" log = "0.4" vmm-sys-util = "0.11.1" diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index ba0d0e93e..ed592f1d6 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -10,7 +10,7 @@ description = "Virtio devices emulation" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-ioctls = "0.13.0" +kvm-ioctls = "0.15.0" libc = "0.2" log = "0.4" serde_json = "1.0" -- Gitee From c6ecdc119df0818cf7a109b183d53915f618247b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 6 Feb 2024 12:34:26 +0800 Subject: [PATCH 1594/2187] fmt: fix some cargo clippy errors error: casting to the same type is unnecessary Signed-off-by: yezengruan --- hypervisor/src/kvm/mod.rs | 20 ++++++++------------ util/src/aio/mod.rs | 10 +++++----- util/src/aio/threads.rs | 2 +- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 459efd344..7224d55fb 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -677,12 +677,10 @@ impl LineIrqManager for KVMInterruptManager { match trigger_mode { TriggerMode::Edge => { - self.vm_fd - .register_irqfd(&irq_fd, irq as u32) - .map_err(|e| { - error!("Failed to register irq, error is {:?}", e); - e - })?; + self.vm_fd.register_irqfd(&irq_fd, irq).map_err(|e| { + error!("Failed to register irq, error is {:?}", e); + e + })?; } _ => { bail!("Unsupported registering irq fd for interrupt of level mode."); @@ -693,12 +691,10 @@ impl LineIrqManager for KVMInterruptManager { } fn unregister_irqfd(&self, irq_fd: Arc, irq: u32) -> Result<()> { - self.vm_fd - .unregister_irqfd(&irq_fd, irq as u32) - .map_err(|e| { - error!("Failed to unregister irq, error is {:?}", e); - e - })?; + self.vm_fd.unregister_irqfd(&irq_fd, irq).map_err(|e| { + error!("Failed to unregister irq, error is {:?}", e); + e + })?; Ok(()) } diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index e571dbbb0..c68f2b1bb 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -210,8 +210,8 @@ impl AioCb { pub fn rw_sync(&self) -> i32 { let mut ret = match self.opcode { - OpCode::Preadv => raw_readv(self.file_fd, &self.iovec, self.offset as usize), - OpCode::Pwritev => raw_writev(self.file_fd, &self.iovec, self.offset as usize), + OpCode::Preadv => raw_readv(self.file_fd, &self.iovec, self.offset), + OpCode::Pwritev => raw_writev(self.file_fd, &self.iovec, self.offset), _ => -1, }; if ret < 0 { @@ -232,7 +232,7 @@ impl AioCb { } fn discard_sync(&self) -> i32 { - let ret = raw_discard(self.file_fd, self.offset as usize, self.nbytes); + let ret = raw_discard(self.file_fd, self.offset, self.nbytes); if ret < 0 && ret != -libc::ENOTSUP { error!("Failed to do sync discard."); } @@ -242,12 +242,12 @@ impl AioCb { fn write_zeroes_sync(&mut self) -> i32 { let mut ret; if self.opcode == OpCode::WriteZeroesUnmap { - ret = raw_discard(self.file_fd, self.offset as usize, self.nbytes); + ret = raw_discard(self.file_fd, self.offset, self.nbytes); if ret == 0 { return ret; } } - ret = raw_write_zeroes(self.file_fd, self.offset as usize, self.nbytes); + ret = raw_write_zeroes(self.file_fd, self.offset, self.nbytes); if ret == -libc::ENOTSUP && !self.iovec.is_empty() { self.opcode = OpCode::Pwritev; return self.rw_sync(); diff --git a/util/src/aio/threads.rs b/util/src/aio/threads.rs index 64d605a44..cab4fe08b 100644 --- a/util/src/aio/threads.rs +++ b/util/src/aio/threads.rs @@ -111,7 +111,7 @@ impl TaskOperation for ThreadsTasks { ret = self.io_data.nbytes as i32; } - self.complete_func(&self.io_data, ret as i32); + self.complete_func(&self.io_data, ret); } } -- Gitee From ac169e698e6a463ba39fdee297bdf992bb8d86ea Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Feb 2024 13:29:42 +0800 Subject: [PATCH 1595/2187] cpu: Move cpu_caps into KvmCpu As it's a thing of KVM. Signed-off-by: Keqian Zhu --- cpu/src/aarch64/caps.rs | 30 -------------- cpu/src/aarch64/mod.rs | 24 ++++------- cpu/src/lib.rs | 21 +--------- cpu/src/x86_64/mod.rs | 27 +++++-------- hypervisor/src/kvm/aarch64/cpu_caps.rs | 40 +++++++++++++++++++ hypervisor/src/kvm/aarch64/mod.rs | 20 +++++----- hypervisor/src/kvm/mod.rs | 33 ++++++--------- .../src/kvm/x86_64/cpu_caps.rs | 16 ++++---- hypervisor/src/kvm/x86_64/mod.rs | 29 +++++++------- 9 files changed, 103 insertions(+), 137 deletions(-) create mode 100644 hypervisor/src/kvm/aarch64/cpu_caps.rs rename cpu/src/x86_64/caps.rs => hypervisor/src/kvm/x86_64/cpu_caps.rs (86%) diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index 2dc9dd362..eda1e701e 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -10,38 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::Arc; - -use kvm_ioctls::Cap; - -use crate::CPUHypervisorOps; use machine_manager::config::{CpuConfig, PmuConfig}; -// Capabilities for ARM cpu. -#[derive(Debug, Clone)] -pub struct ArmCPUCaps { - pub irq_chip: bool, - pub ioevent_fd: bool, - pub irq_fd: bool, - pub user_mem: bool, - pub psci02: bool, - pub mp_state: bool, -} - -impl ArmCPUCaps { - /// Initialize ArmCPUCaps instance. - pub fn init_capabilities(hypervisor_cpu: Arc) -> Self { - ArmCPUCaps { - irq_chip: hypervisor_cpu.check_extension(Cap::Irqchip), - ioevent_fd: hypervisor_cpu.check_extension(Cap::Ioeventfd), - irq_fd: hypervisor_cpu.check_extension(Cap::Irqfd), - user_mem: hypervisor_cpu.check_extension(Cap::UserMemory), - psci02: hypervisor_cpu.check_extension(Cap::ArmPsci02), - mp_state: hypervisor_cpu.check_extension(Cap::MpState), - } - } -} - #[derive(Copy, Clone, Debug, Default)] pub struct ArmCPUFeatures { pub pmu: bool, diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 236448ba0..ec6043c03 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -13,8 +13,8 @@ pub mod caps; mod core_regs; +pub use self::caps::ArmCPUFeatures; pub use self::caps::CpregListEntry; -pub use self::caps::{ArmCPUCaps, ArmCPUFeatures}; pub use self::core_regs::Arm64CoreRegs; use std::sync::{Arc, Mutex}; @@ -189,23 +189,13 @@ impl ArmCPUState { impl StateTransfer for CPU { fn get_state_vec(&self) -> Result> { self.hypervisor_cpu - .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CoreRegs, &self.caps)?; - - if self.caps.mp_state { - self.hypervisor_cpu.get_regs( - self.arch_cpu.clone(), - ArmRegsIndex::MpState, - &self.caps, - )?; - }; - + .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CoreRegs)?; + self.hypervisor_cpu + .get_regs(self.arch_cpu.clone(), ArmRegsIndex::MpState)?; + self.hypervisor_cpu + .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CpregList)?; self.hypervisor_cpu - .get_regs(self.arch_cpu.clone(), ArmRegsIndex::CpregList, &self.caps)?; - self.hypervisor_cpu.get_regs( - self.arch_cpu.clone(), - ArmRegsIndex::VcpuEvents, - &self.caps, - )?; + .get_regs(self.arch_cpu.clone(), ArmRegsIndex::VcpuEvents)?; Ok(self.arch_cpu.lock().unwrap().as_bytes().to_vec()) } diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 41c1d1110..9d6660534 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -40,8 +40,6 @@ pub use aarch64::Arm64CoreRegs; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUBootConfig as CPUBootConfig; #[cfg(target_arch = "aarch64")] -pub use aarch64::ArmCPUCaps as CPUCaps; -#[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUFeatures as CPUFeatures; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUState as ArchCPU; @@ -57,8 +55,6 @@ pub use aarch64::PMU_INTR; pub use aarch64::PPI_BASE; pub use error::CpuError; #[cfg(target_arch = "x86_64")] -pub use x86_64::caps::X86CPUCaps as CPUCaps; -#[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUBootConfig as CPUBootConfig; #[cfg(target_arch = "x86_64")] pub use x86_64::X86CPUState as ArchCPU; @@ -74,7 +70,6 @@ use std::thread; use std::time::Duration; use anyhow::{anyhow, Context, Result}; -use kvm_ioctls::Cap; use log::{error, info, warn}; use nix::unistd::gettid; use vmm_sys_util::signal::Killable; @@ -174,10 +169,6 @@ pub trait CPUInterface { pub trait CPUHypervisorOps: Send + Sync { fn get_hypervisor_type(&self) -> HypervisorType; - fn check_extension(&self, cap: Cap) -> bool; - - fn get_msr_index_list(&self) -> Vec; - fn init_pmu(&self) -> Result<()>; fn vcpu_init(&self) -> Result<()>; @@ -191,12 +182,7 @@ pub trait CPUHypervisorOps: Send + Sync { fn get_one_reg(&self, reg_id: u64) -> Result; - fn get_regs( - &self, - arch_cpu: Arc>, - regs_index: RegsIndex, - caps: &CPUCaps, - ) -> Result<()>; + fn get_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()>; fn set_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()>; @@ -226,8 +212,6 @@ pub struct CPU { tid: Arc>>, /// The VM combined by this VCPU. vm: Weak>, - /// The capability of VCPU. - pub caps: CPUCaps, /// The state backup of architecture CPU right before boot. boot_state: Arc>, /// Sync the pause state of vCPU in hypervisor and userspace. @@ -258,7 +242,6 @@ impl CPU { task: Arc::new(Mutex::new(None)), tid: Arc::new(Mutex::new(None)), vm: Arc::downgrade(&vm), - caps: CPUCaps::init_capabilities(hypervisor_cpu.clone()), boot_state: Arc::new(Mutex::new(ArchCPU::default())), pause_signal: Arc::new(AtomicBool::new(false)), hypervisor_cpu, @@ -456,7 +439,7 @@ impl CPUInterface for CPU { #[cfg(target_arch = "aarch64")] self.hypervisor_cpu - .get_regs(self.arch_cpu.clone(), RegsIndex::VtimerCount, &self.caps)?; + .get_regs(self.arch_cpu.clone(), RegsIndex::VtimerCount)?; Ok(()) } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 668f27a3e..0a8ad1690 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -10,8 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -pub mod caps; - mod cpuid; use std::sync::{Arc, Mutex}; @@ -499,21 +497,16 @@ impl StateTransfer for CPU { fn get_state_vec(&self) -> Result> { let hypervisor_cpu = self.hypervisor_cpu(); - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::MpState, &self.caps)?; - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Regs, &self.caps)?; - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Sregs, &self.caps)?; - if self.caps.has_xsave { - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Xsave, &self.caps)?; - } else { - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Fpu, &self.caps)?; - } - if self.caps.has_xcrs { - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Xcrs, &self.caps)?; - } - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::DebugRegs, &self.caps)?; - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::LapicState, &self.caps)?; - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::MsrEntry, &self.caps)?; - hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::VcpuEvents, &self.caps)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::MpState)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Regs)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Sregs)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Xsave)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Fpu)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::Xcrs)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::DebugRegs)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::LapicState)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::MsrEntry)?; + hypervisor_cpu.get_regs(self.arch_cpu.clone(), X86RegsIndex::VcpuEvents)?; Ok(self.arch_cpu.lock().unwrap().as_bytes().to_vec()) } diff --git a/hypervisor/src/kvm/aarch64/cpu_caps.rs b/hypervisor/src/kvm/aarch64/cpu_caps.rs new file mode 100644 index 000000000..58759a435 --- /dev/null +++ b/hypervisor/src/kvm/aarch64/cpu_caps.rs @@ -0,0 +1,40 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use kvm_ioctls::Cap; +use kvm_ioctls::Kvm; + +// Capabilities for ARM cpu. +#[derive(Debug, Clone)] +pub struct ArmCPUCaps { + pub irq_chip: bool, + pub ioevent_fd: bool, + pub irq_fd: bool, + pub user_mem: bool, + pub psci02: bool, + pub mp_state: bool, +} + +impl ArmCPUCaps { + /// Initialize ArmCPUCaps instance. + pub fn init_capabilities() -> Self { + let kvm = Kvm::new().unwrap(); + ArmCPUCaps { + irq_chip: kvm.check_extension(Cap::Irqchip), + ioevent_fd: kvm.check_extension(Cap::Ioeventfd), + irq_fd: kvm.check_extension(Cap::Irqfd), + user_mem: kvm.check_extension(Cap::UserMemory), + psci02: kvm.check_extension(Cap::ArmPsci02), + mp_state: kvm.check_extension(Cap::MpState), + } + } +} diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 41f987618..c9dc60edd 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod cpu_caps; pub mod gicv2; pub mod gicv3; @@ -24,8 +25,8 @@ use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; use crate::kvm::{KvmCpu, KvmHypervisor}; use cpu::{ - ArchCPU, Arm64CoreRegs, CPUBootConfig, CPUCaps, CPUFeatures, CpregListEntry, RegsIndex, CPU, - PMU_INTR, PPI_BASE, + ArchCPU, Arm64CoreRegs, CPUBootConfig, CPUFeatures, CpregListEntry, RegsIndex, CPU, PMU_INTR, + PPI_BASE, }; // Arm Architecture Reference Manual defines the encoding of AArch64 system registers: @@ -122,10 +123,6 @@ impl KvmHypervisor { } impl KvmCpu { - pub fn arch_get_msr_index_list(&self) -> Vec { - Vec::new() - } - pub fn arch_init_pmu(&self) -> Result<()> { let pmu_attr = kvm_device_attr { group: KVM_ARM_VCPU_PMU_V3_CTRL, @@ -225,7 +222,6 @@ impl KvmCpu { &self, arch_cpu: Arc>, regs_index: RegsIndex, - _caps: &CPUCaps, ) -> Result<()> { let mut locked_arch_cpu = arch_cpu.lock().unwrap(); @@ -234,11 +230,13 @@ impl KvmCpu { locked_arch_cpu.core_regs = self.get_core_regs()?; } RegsIndex::MpState => { - let mut mp_state = self.fd.get_mp_state()?; - if mp_state.mp_state != KVM_MP_STATE_STOPPED { - mp_state.mp_state = KVM_MP_STATE_RUNNABLE; + if self.caps.mp_state { + let mut mp_state = self.fd.get_mp_state()?; + if mp_state.mp_state != KVM_MP_STATE_STOPPED { + mp_state.mp_state = KVM_MP_STATE_RUNNABLE; + } + locked_arch_cpu.mp_state = mp_state; } - locked_arch_cpu.mp_state = mp_state; } RegsIndex::VcpuEvents => { locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 7224d55fb..4edef423c 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -53,6 +53,8 @@ use self::listener::KvmMemoryListener; use super::HypervisorOps; #[cfg(target_arch = "x86_64")] use crate::HypervisorError; +#[cfg(target_arch = "aarch64")] +use aarch64::cpu_caps::ArmCPUCaps as CPUCaps; use address_space::{AddressSpace, Listener}; #[cfg(feature = "boot_time")] use cpu::capture_boot_signal; @@ -61,8 +63,8 @@ use cpu::CPUFeatures; #[cfg(not(test))] use cpu::CpuError; use cpu::{ - ArchCPU, CPUBootConfig, CPUCaps, CPUHypervisorOps, CPUInterface, CPUThreadWorker, - CpuLifecycleState, RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, + ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuLifecycleState, + RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, }; use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, TriggerMode}; #[cfg(target_arch = "aarch64")] @@ -77,6 +79,8 @@ use migration::snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}; use migration::{MigrateMemSlot, MigrateOps, MigrationManager}; #[cfg(not(test))] use util::test_helper::is_test_enabled; +#[cfg(target_arch = "x86_64")] +use x86_64::cpu_caps::X86CPUCaps as CPUCaps; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/asm-generic/kvm.h pub const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; @@ -362,6 +366,8 @@ pub struct KvmCpu { #[cfg(target_arch = "aarch64")] vm_fd: Option>, fd: Arc, + /// The capability of VCPU. + caps: CPUCaps, #[cfg(target_arch = "aarch64")] /// Used to pass vcpu target and supported features to kvm. pub kvi: Mutex, @@ -373,6 +379,7 @@ impl KvmCpu { #[cfg(target_arch = "aarch64")] vm_fd, fd: Arc::new(vcpu_fd), + caps: CPUCaps::init_capabilities(), #[cfg(target_arch = "aarch64")] kvi: Mutex::new(kvm_vcpu_init::default()), } @@ -517,15 +524,6 @@ impl CPUHypervisorOps for KvmCpu { HypervisorType::Kvm } - fn check_extension(&self, cap: Cap) -> bool { - let kvm = Kvm::new().unwrap(); - kvm.check_extension(cap) - } - - fn get_msr_index_list(&self) -> Vec { - self.arch_get_msr_index_list() - } - fn init_pmu(&self) -> Result<()> { self.arch_init_pmu() } @@ -550,13 +548,8 @@ impl CPUHypervisorOps for KvmCpu { self.arch_get_one_reg(reg_id) } - fn get_regs( - &self, - arch_cpu: Arc>, - regs_index: RegsIndex, - caps: &CPUCaps, - ) -> Result<()> { - self.arch_get_regs(arch_cpu, regs_index, caps) + fn get_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()> { + self.arch_get_regs(arch_cpu, regs_index) } fn set_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()> { @@ -817,7 +810,7 @@ mod test { use kvm_bindings::kvm_segment; #[cfg(target_arch = "x86_64")] - use cpu::{ArchCPU, CPUBootConfig, CPUCaps}; + use cpu::{ArchCPU, CPUBootConfig}; use machine_manager::machine::{ MachineAddressInterface, MachineInterface, MachineLifecycle, VmState, }; @@ -958,7 +951,7 @@ mod test { .is_ok()); // test setup special registers - let cpu_caps = CPUCaps::init_capabilities(hypervisor_cpu.clone()); + let cpu_caps = CPUCaps::init_capabilities(); assert!(hypervisor_cpu.reset_vcpu(Arc::new(cpu)).is_ok()); let x86_sregs = hypervisor_cpu.fd.get_sregs().unwrap(); assert_eq!(x86_sregs.cs, code_seg); diff --git a/cpu/src/x86_64/caps.rs b/hypervisor/src/kvm/x86_64/cpu_caps.rs similarity index 86% rename from cpu/src/x86_64/caps.rs rename to hypervisor/src/kvm/x86_64/cpu_caps.rs index 3b896c6fc..e5760de8e 100644 --- a/cpu/src/x86_64/caps.rs +++ b/hypervisor/src/kvm/x86_64/cpu_caps.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan @@ -10,14 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::Arc; - use kvm_bindings::{kvm_msr_entry, Msrs}; use kvm_ioctls::Cap; +use kvm_ioctls::Kvm; use vmm_sys_util::fam::Error; -use crate::CPUHypervisorOps; - /// See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/msr-index.h#L558 const MSR_IA32_MISC_ENABLE: ::std::os::raw::c_uint = 0x1a0; /// See: https://elixir.bootlin.com/linux/v4.19.123/source/arch/x86/include/asm/msr-index.h#L597 @@ -39,11 +36,12 @@ pub struct X86CPUCaps { impl X86CPUCaps { /// Initialize X86CPUCaps instance. - pub fn init_capabilities(hypervisor_cpu: Arc) -> Self { + pub fn init_capabilities() -> Self { + let kvm = Kvm::new().unwrap(); X86CPUCaps { - has_xsave: hypervisor_cpu.check_extension(Cap::Xsave), - has_xcrs: hypervisor_cpu.check_extension(Cap::Xcrs), - supported_msrs: hypervisor_cpu.get_msr_index_list(), + has_xsave: kvm.check_extension(Cap::Xsave), + has_xcrs: kvm.check_extension(Cap::Xcrs), + supported_msrs: kvm.get_msr_index_list().unwrap().as_slice().to_vec(), } } diff --git a/hypervisor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs index 0cb1fe3a8..cc9ceade1 100644 --- a/hypervisor/src/kvm/x86_64/mod.rs +++ b/hypervisor/src/kvm/x86_64/mod.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod cpu_caps; + use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -21,7 +23,7 @@ use crate::kvm::listener::KvmIoListener; use crate::kvm::{KvmCpu, KvmHypervisor}; use crate::HypervisorError; use address_space::Listener; -use cpu::{ArchCPU, CPUBootConfig, CPUCaps, RegsIndex, CPU}; +use cpu::{ArchCPU, CPUBootConfig, RegsIndex, CPU}; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); @@ -72,11 +74,6 @@ impl KvmHypervisor { } impl KvmCpu { - pub fn arch_get_msr_index_list(&self) -> Vec { - let kvm = Kvm::new().unwrap(); - kvm.get_msr_index_list().unwrap().as_slice().to_vec() - } - pub fn arch_init_pmu(&self) -> Result<()> { Ok(()) } @@ -117,9 +114,8 @@ impl KvmCpu { &self, arch_cpu: Arc>, regs_index: RegsIndex, - caps: &CPUCaps, ) -> Result<()> { - let mut msr_entries = caps.create_msr_entries()?; + let mut msr_entries = self.caps.create_msr_entries()?; let mut locked_arch_cpu = arch_cpu.lock().unwrap(); match regs_index { RegsIndex::Regs => { @@ -129,7 +125,9 @@ impl KvmCpu { locked_arch_cpu.sregs = self.fd.get_sregs()?; } RegsIndex::Fpu => { - locked_arch_cpu.fpu = self.fd.get_fpu()?; + if !self.caps.has_xsave { + locked_arch_cpu.fpu = self.fd.get_fpu()?; + } } RegsIndex::MpState => { locked_arch_cpu.mp_state = self.fd.get_mp_state()?; @@ -147,10 +145,14 @@ impl KvmCpu { locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; } RegsIndex::Xsave => { - locked_arch_cpu.xsave = self.fd.get_xsave()?; + if self.caps.has_xsave { + locked_arch_cpu.xsave = self.fd.get_xsave()?; + } } RegsIndex::Xcrs => { - locked_arch_cpu.xcrs = self.fd.get_xcrs()?; + if self.caps.has_xcrs { + locked_arch_cpu.xcrs = self.fd.get_xcrs()?; + } } RegsIndex::DebugRegs => { locked_arch_cpu.debugregs = self.fd.get_debug_regs()?; @@ -226,7 +228,6 @@ impl KvmCpu { } pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { - let caps = &cpu.caps; let locked_arch_cpu = cpu.arch_cpu.lock().unwrap(); let apic_id = locked_arch_cpu.apic_id; @@ -255,7 +256,7 @@ impl KvmCpu { self.fd .set_regs(&locked_arch_cpu.regs) .with_context(|| format!("Failed to set regs for CPU {}", apic_id))?; - if caps.has_xsave { + if self.caps.has_xsave { self.fd .set_xsave(&locked_arch_cpu.xsave) .with_context(|| format!("Failed to set xsave for CPU {}", apic_id))?; @@ -264,7 +265,7 @@ impl KvmCpu { .set_fpu(&locked_arch_cpu.fpu) .with_context(|| format!("Failed to set fpu for CPU {}", apic_id))?; } - if caps.has_xcrs { + if self.caps.has_xcrs { self.fd .set_xcrs(&locked_arch_cpu.xcrs) .with_context(|| format!("Failed to set xcrs for CPU {}", apic_id))?; -- Gitee From 4a2eac4517a74eb8b594458762e100975bec21ad Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Feb 2024 14:38:29 +0800 Subject: [PATCH 1596/2187] deps: Clean up dependency to KVM related crates Signed-off-by: Keqian Zhu --- Cargo.lock | 10 ---------- address_space/Cargo.toml | 2 -- address_space/src/error.rs | 6 ------ block_backend/src/qcow2/snapshot.rs | 2 +- cpu/Cargo.toml | 1 - devices/Cargo.toml | 2 -- devices/src/sysbus/error.rs | 5 ----- hypervisor/src/error.rs | 6 ++++++ hypervisor/src/kvm/listener.rs | 7 ++++--- machine/Cargo.toml | 1 - machine/src/lib.rs | 2 +- machine/src/micro_common/mod.rs | 2 +- machine/src/x86_64/standard.rs | 2 +- machine_manager/Cargo.toml | 1 - migration/Cargo.toml | 1 - migration/src/error.rs | 5 ----- util/Cargo.toml | 1 - util/src/error.rs | 10 ---------- virtio/Cargo.toml | 1 - virtio/src/transport/virtio_pci.rs | 2 +- 20 files changed, 15 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d725b87e6..0b1ce7371 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,8 +20,6 @@ version = "2.3.0" dependencies = [ "anyhow", "arc-swap", - "kvm-bindings", - "kvm-ioctls", "libc", "log", "machine_manager", @@ -308,7 +306,6 @@ version = "2.3.0" dependencies = [ "anyhow", "kvm-bindings", - "kvm-ioctls", "libc", "log", "machine_manager", @@ -335,8 +332,6 @@ dependencies = [ "chardev_backend", "cpu", "drm-fourcc", - "kvm-bindings", - "kvm-ioctls", "libc", "libpulse-binding", "libpulse-simple-binding", @@ -911,7 +906,6 @@ dependencies = [ "cpu", "devices", "hypervisor", - "kvm-bindings", "libc", "log", "machine_manager", @@ -933,7 +927,6 @@ version = "2.3.0" dependencies = [ "anyhow", "hex", - "kvm-bindings", "libc", "log", "once_cell", @@ -986,7 +979,6 @@ name = "migration" version = "2.3.0" dependencies = [ "anyhow", - "kvm-bindings", "kvm-ioctls", "log", "machine_manager", @@ -1767,7 +1759,6 @@ dependencies = [ "byteorder", "io-uring", "kvm-bindings", - "kvm-ioctls", "libc", "log", "nix 0.26.2", @@ -1843,7 +1834,6 @@ dependencies = [ "byteorder", "chardev_backend", "devices", - "kvm-ioctls", "libc", "log", "machine_manager", diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index a60a32231..332da9364 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -9,9 +9,7 @@ description = "provide memory management for VM" [dependencies] libc = "0.2" log = "0.4" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } -kvm-ioctls = "0.15.0" vmm-sys-util = "0.11.1" arc-swap = "1.6.0" thiserror = "1.0" diff --git a/address_space/src/error.rs b/address_space/src/error.rs index d42ebe61f..98e4682c2 100644 --- a/address_space/src/error.rs +++ b/address_space/src/error.rs @@ -42,12 +42,6 @@ pub enum AddressSpaceError { IoAccess(u64, u64, u64), #[error("Wrong region type, {0:#?}")] RegionType(crate::RegionType), - #[error("No available kvm_mem_slot, total count is {0}")] - NoAvailKvmSlot(usize), - #[error("Failed to find matched kvm_mem_slot, addr 0x{0:X}, size 0x{1:X}")] - NoMatchedKvmSlot(u64, u64), - #[error("Added KVM mem range (0x{:X}, 0x{:X}) overlaps with exist one (0x{:X}, 0x{:X})", add.0, add.1, exist.0, exist.1)] - KvmSlotOverlap { add: (u64, u64), exist: (u64, u64) }, #[error("Invalid offset: offset 0x{0:X}, data length 0x{1:X}, region size 0x{2:X}")] InvalidOffset(u64, u64, u64), } diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index 78e7841a0..b5ee4ba37 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -23,7 +23,7 @@ use util::num_ops::round_up; pub const QCOW2_MAX_SNAPSHOTS: usize = 65536; // Length of Qcow2 internal snapshot which doesn't have icount in extra data. -// Qcow2 snapshots created by qemu-kvm/qemu-img(version <= 5.0) may have this format. +// Qcow2 snapshots created by qemu-img(version <= 5.0) may have this format. const SNAPSHOT_EXTRA_DATA_LEN_16: usize = 16; // Length of Qcow2 internal snapshot which has icount in extra data. const SNAPSHOT_EXTRA_DATA_LEN_24: usize = 24; diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index f823e0153..5b93cb411 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -11,7 +11,6 @@ thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } -kvm-ioctls = "0.15.0" log = "0.4" libc = "0.2" vmm-sys-util = "0.11.1" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index d964215c0..5220319a9 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -11,7 +11,6 @@ thiserror = "1.0" anyhow = "1.0" libc = "0.2" log = "0.4" -kvm-ioctls = "0.15.0" serde = { version = "1.0", features = ["derive"] } vmm-sys-util = "0.11.1" byteorder = "1.4.3" @@ -20,7 +19,6 @@ once_cell = "1.18.0" v4l2-sys-mit = { version = "0.3.0", optional = true } serde_json = "1.0" rand = "0.8.5" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } address_space = { path = "../address_space" } cpu = { path = "../cpu" } machine_manager = { path = "../machine_manager" } diff --git a/devices/src/sysbus/error.rs b/devices/src/sysbus/error.rs index 473a2b327..da1c89098 100644 --- a/devices/src/sysbus/error.rs +++ b/devices/src/sysbus/error.rs @@ -19,9 +19,4 @@ pub enum SysBusError { #[from] source: address_space::error::AddressSpaceError, }, - #[error("KvmIoctl")] - KvmIoctl { - #[from] - source: kvm_ioctls::Error, - }, } diff --git a/hypervisor/src/error.rs b/hypervisor/src/error.rs index 829a8bbe0..c5742611e 100644 --- a/hypervisor/src/error.rs +++ b/hypervisor/src/error.rs @@ -29,4 +29,10 @@ pub enum HypervisorError { CrtIrqchipErr, #[error("Failed to create KVM device: {0:#?}.")] CreateKvmDevice(kvm_ioctls::Error), + #[error("No available kvm_mem_slot, total count is {0}")] + NoAvailKvmSlot(usize), + #[error("Failed to find matched kvm_mem_slot, addr 0x{0:X}, size 0x{1:X}")] + NoMatchedKvmSlot(u64, u64), + #[error("Added KVM mem range (0x{:X}, 0x{:X}) overlaps with exist one (0x{:X}, 0x{:X})", add.0, add.1, exist.0, exist.1)] + KvmSlotOverlap { add: (u64, u64), exist: (u64, u64) }, } diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index 117ab19bc..dd137e875 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -20,6 +20,7 @@ use kvm_bindings::{kvm_userspace_memory_region as KvmMemSlot, KVM_MEM_READONLY}; use kvm_ioctls::{IoEventAddress, NoDatamatch, VmFd}; use log::{debug, warn}; +use crate::HypervisorError; use address_space::{ AddressRange, AddressSpaceError, FlatRange, Listener, ListenerReqType, MemSlot, RegionIoEventFd, RegionType, @@ -82,7 +83,7 @@ impl KvmMemoryListener { .find_intersection(range) .is_some() { - return Err(anyhow!(AddressSpaceError::KvmSlotOverlap { + return Err(anyhow!(HypervisorError::KvmSlotOverlap { add: (guest_addr, size), exist: (s.guest_addr, s.size) })); @@ -100,7 +101,7 @@ impl KvmMemoryListener { } } - Err(anyhow!(AddressSpaceError::NoAvailKvmSlot(slots.len()))) + Err(anyhow!(HypervisorError::NoAvailKvmSlot(slots.len()))) } /// Delete a slot after finding it according to the given arguments. @@ -123,7 +124,7 @@ impl KvmMemoryListener { return Ok(*slot); } } - Err(anyhow!(AddressSpaceError::NoMatchedKvmSlot(addr, size))) + Err(anyhow!(HypervisorError::NoMatchedKvmSlot(addr, size))) } /// Align a piece of memory segment according to `alignment`, diff --git a/machine/Cargo.toml b/machine/Cargo.toml index c98cb4be0..ad2b04d7f 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -7,7 +7,6 @@ license = "Mulan PSL v2" description = "Emulation machines" [dependencies] -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } log = "0.4" libc = "0.2" serde_json = "1.0" diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 183a6a8ad..d0b11d482 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -496,7 +496,7 @@ pub trait MachineOps { ) .with_context(|| { format!( - "Failed to realize arch cpu register/features for CPU {}/KVM", + "Failed to realize arch cpu register/features for CPU {}", cpu_index ) })?; diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index c5dcc3d99..2defa1809 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -126,7 +126,7 @@ impl MmioReplaceableInfo { } } -/// A wrapper around creating and using a kvm-based micro VM. +/// A wrapper around creating and using a micro VM. pub struct LightMachine { // Machine base members. pub(crate) base: MachineBase, diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 4c5476322..0000918cc 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -357,7 +357,7 @@ impl StdMachineOps for StdMachine { )?; vcpu.realize(boot_cfg, topology).with_context(|| { format!( - "Failed to realize arch cpu register/features for CPU {}/KVM", + "Failed to realize arch cpu register/features for CPU {}", vcpu_id ) })?; diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index f257cf8e9..aa3217ee9 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -21,7 +21,6 @@ once_cell = "1.18.0" thiserror = "1.0" anyhow = "1.0" trace = { path = "../trace" } -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } util = { path = "../util" } [features] diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 5679aa646..1474dd519 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -9,7 +9,6 @@ kvm-ioctls = "0.15.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" once_cell = "1.18.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } log = "0.4" thiserror = "1.0" anyhow = "1.0" diff --git a/migration/src/error.rs b/migration/src/error.rs index 4ef22d410..4e3e449ec 100644 --- a/migration/src/error.rs +++ b/migration/src/error.rs @@ -26,11 +26,6 @@ pub enum MigrationError { #[from] source: std::io::Error, }, - #[error("Ioctl")] - Ioctl { - #[from] - source: kvm_ioctls::Error, - }, #[error("Json")] Json { #[from] diff --git a/util/Cargo.toml b/util/Cargo.toml index f1e1ca1d9..10471204c 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -13,7 +13,6 @@ thiserror = "1.0" anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["poll", "term", "time", "signal", "fs", "feature"] } -kvm-ioctls = "0.15.0" libc = "0.2" log = { version = "0.4", features = ["std"]} vmm-sys-util = "0.11.1" diff --git a/util/src/error.rs b/util/src/error.rs index 97293e3f8..e59c1ebd7 100644 --- a/util/src/error.rs +++ b/util/src/error.rs @@ -14,16 +14,6 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum UtilError { - #[error("KvmIoctl")] - KvmIoctl { - #[from] - source: kvm_ioctls::Error, - }, - #[error("Io")] - Io { - #[from] - source: std::io::Error, - }, #[error("Nul")] Nul { #[from] diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index ed592f1d6..5fc306acc 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -10,7 +10,6 @@ description = "Virtio devices emulation" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-ioctls = "0.15.0" libc = "0.2" log = "0.4" serde_json = "1.0" diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 620d61042..f84d3b3eb 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -309,7 +309,7 @@ pub struct VirtioPciDevice { interrupt_cb: Option>, /// Multi-Function flag. multi_func: bool, - /// If the device need to register irqfd to kvm. + /// If the device need to register irqfd. need_irqfd: bool, } -- Gitee From 6c7527eac4e176fee83422ddf5b8e01af628ce5f Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Sun, 4 Feb 2024 20:57:41 +0800 Subject: [PATCH 1597/2187] dead_code: delete unnecessary #[cfg(dead_code)] Some object members read and written by memory are prefixed to eliminate dead_code. Signed-off-by: Mingwang Li --- machine/src/standard_common/syscall.rs | 8 ++--- tests/mod_test/tests/balloon_test.rs | 11 +++--- util/src/aio/libaio.rs | 8 +---- util/src/byte_code.rs | 1 - vfio/src/lib.rs | 6 ++-- vfio/src/vfio_dev.rs | 46 -------------------------- vfio/src/vfio_pci.rs | 13 +------- virtio/src/device/balloon.rs | 22 ++++++------ 8 files changed, 23 insertions(+), 92 deletions(-) diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index 2f975361c..9385d7595 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -24,10 +24,9 @@ use util::v4l2::{ VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, }; use vfio::{ - VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_IRQ_INFO, - VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, - VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, - VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, + VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, + VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, + VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, }; use virtio::VhostKern::*; @@ -241,7 +240,6 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_INFO() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_RESET() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_REGION_INFO() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_IRQ_INFO() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index f132d4456..dbcd67fde 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -744,7 +744,7 @@ fn balloon_fpr_001() { fn balloon_fpr_002() { balloon_fpr_fun(false); } -#[allow(dead_code)] + struct VirtioBalloonConfig { /// The target page numbers of balloon device. pub num_pages: u32, @@ -962,11 +962,10 @@ fn balloon_deactive_001() { } #[derive(Clone, Copy, Default)] -#[allow(dead_code)] #[repr(packed(1))] struct BalloonStat { - tag: u16, - val: u64, + _tag: u16, + _val: u64, } impl ByteCode for BalloonStat {} /// balloon device deactive config test @@ -1002,8 +1001,8 @@ fn auto_balloon_test_001() { assert_eq!(interval, MONITOR_INTERVAL_SECOND_DEFAULT); let stat = BalloonStat { - tag: 0, - val: 131070, + _tag: 0, + _val: 131070, }; let msg_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); balloon diff --git a/util/src/aio/libaio.rs b/util/src/aio/libaio.rs index 2961d2a26..46e1d32f5 100644 --- a/util/src/aio/libaio.rs +++ b/util/src/aio/libaio.rs @@ -21,8 +21,6 @@ use super::threads::ThreadsAioContext; use super::{AioCb, AioContext, AioEvent, OpCode, Result}; const IOCB_FLAG_RESFD: u32 = 1; -#[allow(dead_code)] -const IOCB_FLAG_IOPRIO: u32 = 1 << 1; #[repr(C)] #[allow(non_camel_case_types)] @@ -53,14 +51,10 @@ struct IoCb { } #[repr(C)] -#[allow(non_camel_case_types, dead_code)] +#[allow(non_camel_case_types)] #[derive(Copy, Clone)] enum IoCmd { - Pread = 0, - Pwrite = 1, - Fsync = 2, Fdsync = 3, - Noop = 6, Preadv = 7, Pwritev = 8, } diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index a818d24ea..64fe6c9ac 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -78,7 +78,6 @@ mod test { use super::*; #[repr(C)] - #[allow(dead_code)] #[derive(Copy, Clone, Default)] struct TestData { type_id: [u8; 8], diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index d6f114b07..49c77af17 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -18,9 +18,9 @@ mod vfio_pci; pub use error::VfioError; pub use vfio_dev::{ VfioContainer, VfioDevice, VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, - VFIO_DEVICE_GET_IRQ_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, - VFIO_GET_API_VERSION, VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, - VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, + VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, + VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, + VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, }; pub use vfio_pci::VfioPciDevice; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index e83076800..cdba51c7f 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -24,7 +24,6 @@ use byteorder::{ByteOrder, LittleEndian}; use kvm_bindings::{ kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, }; -use log::warn; use vfio_bindings::bindings::vfio; use vmm_sys_util::ioctl::{ ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, @@ -77,11 +76,6 @@ ioctl_io_nr!( vfio::VFIO_TYPE, vfio::VFIO_BASE + 0x08 ); -ioctl_io_nr!( - VFIO_DEVICE_GET_IRQ_INFO, - vfio::VFIO_TYPE, - vfio::VFIO_BASE + 0x09 -); ioctl_io_nr!( VFIO_DEVICE_SET_IRQS, vfio::VFIO_TYPE, @@ -496,13 +490,6 @@ struct VfioRegionWithCap { cap_info: vfio::__IncompleteArrayField, } -#[allow(dead_code)] -pub struct VfioIrq { - count: u32, - flags: u32, - index: u32, -} - impl VfioDevice { pub fn new(path: &Path, mem_as: &Arc) -> Result>> { if !path.exists() { @@ -728,39 +715,6 @@ impl VfioDevice { Ok(regions) } - pub fn get_irqs_info(&self, num_irqs: u32) -> Result> { - let mut irqs: HashMap = HashMap::new(); - - for index in 0..num_irqs { - let mut info = vfio::vfio_irq_info { - argsz: size_of::() as u32, - flags: 0, - index, - count: 0, - }; - - let ret = - // SAFETY: Device is the owner of file, and we will verify the result is valid. - unsafe { ioctl_with_mut_ref(&self.fd, VFIO_DEVICE_GET_IRQ_INFO(), &mut info) }; - if ret < 0 { - warn!( - "VFIO_DEVICE_GET_IRQ_INFO return irq type{} not supported", - index - ); - continue; - } - - let irq = VfioIrq { - flags: info.flags, - count: info.count, - index, - }; - irqs.insert(index, irq); - } - - Ok(irqs) - } - /// Read region information from VFIO device. /// /// # Arguments diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index b3ab15598..d354098ee 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -10,7 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::collections::HashMap; use std::mem::size_of; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::{AtomicU16, Ordering}; @@ -61,9 +60,6 @@ struct VfioMsixInfo { table: MsixTable, // Msix entries. entries: u16, - // Vfio device irq info - #[allow(dead_code)] - vfio_irq: HashMap, } struct VfioBar { @@ -231,14 +227,8 @@ impl VfioPciDevice { Ok(()) } - /// Get MSI-X table, vfio_irq and entry information from vfio device. + /// Get MSI-X table and entry information from vfio device. fn get_msix_info(&mut self) -> Result { - let locked_dev = self.vfio_device.lock().unwrap(); - let n = locked_dev.dev_info.num_irqs; - let vfio_irq = locked_dev - .get_irqs_info(n) - .with_context(|| "Failed to get vfio irqs info")?; - let cap_offset = self.base.config.find_pci_cap(MSIX_CAP_ID); let table = le_read_u32( &self.base.config.config, @@ -265,7 +255,6 @@ impl VfioPciDevice { table_size: (entries * MSIX_TABLE_ENTRY_SIZE) as u64, }, entries, - vfio_irq, }) } diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 83fa7ce34..67204eae1 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -76,31 +76,29 @@ struct GuestIovec { } #[derive(Clone, Copy, Default)] -#[allow(dead_code)] #[repr(packed(1))] struct BalloonStat { _tag: u16, - val: u64, + _val: u64, } /// Balloon configuration, which would be used to transport data between `Guest` and `Host`. #[derive(Copy, Clone, Default)] -#[allow(dead_code)] struct VirtioBalloonConfig { /// The target page numbers of balloon device. - num_pages: u32, + _num_pages: u32, /// Number of pages we've actually got in balloon device. - actual: u32, + _actual: u32, _reserved: u32, _reserved1: u32, /// Buffer percent is a percentage of memory actually needed by /// the applications and services running inside the virtual machine. /// This parameter takes effect only when VIRTIO_BALLOON_F_MESSAGE_VQ is supported. /// Recommended value range: [20, 80] and default is 50. - membuf_percent: u32, + _membuf_percent: u32, /// Monitor interval(second) host wants to adjust VM memory size. /// Recommended value range: [5, 300] and default is 10. - monitor_interval: u32, + _monitor_interval: u32, } impl ByteCode for BalloonStat {} @@ -703,7 +701,7 @@ impl BalloonIoHandler { let ram_size = (balloon_dev.mem_info.lock().unwrap().get_ram_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32; - balloon_dev.set_num_pages(cmp::min(stat.val as u32, ram_size)); + balloon_dev.set_num_pages(cmp::min(stat._val as u32, ram_size)); } } balloon_dev @@ -1021,12 +1019,12 @@ impl VirtioDevice for Balloon { fn read_config(&self, offset: u64, data: &mut [u8]) -> Result<()> { let new_config = VirtioBalloonConfig { - num_pages: self.num_pages, - actual: self.actual.load(Ordering::Acquire), + _num_pages: self.num_pages, + _actual: self.actual.load(Ordering::Acquire), _reserved: 0_u32, _reserved1: 0_u32, - membuf_percent: self.bln_cfg.membuf_percent, - monitor_interval: self.bln_cfg.monitor_interval, + _membuf_percent: self.bln_cfg.membuf_percent, + _monitor_interval: self.bln_cfg.monitor_interval, }; let config_len = -- Gitee From 74ed673cdd8ee7b275b9116c5df7ef74729b9f4d Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 7 Feb 2024 12:15:06 +0800 Subject: [PATCH 1598/2187] scream: bugfix: supplement missing "Default" trait missing derive Default trait for samplesize. Signed-off-by: zhanghan64 --- devices/src/misc/scream/ohos/ohaudio_rapi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohos/ohaudio_rapi.rs b/devices/src/misc/scream/ohos/ohaudio_rapi.rs index 289637e89..5537f2076 100755 --- a/devices/src/misc/scream/ohos/ohaudio_rapi.rs +++ b/devices/src/misc/scream/ohos/ohaudio_rapi.rs @@ -81,7 +81,7 @@ impl From for OAErr { } #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] struct SampleSize(pub capi::OhAudioStreamType); impl TryFrom for SampleSize { -- Gitee From 06b37dc6e8f80db0a6f8c2d67fed3de9e3bb566f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 7 Feb 2024 16:00:50 +0800 Subject: [PATCH 1599/2187] hypervisor: Move kvm reg definition into hypervisor kvm Signed-off-by: Keqian Zhu --- cpu/src/aarch64/mod.rs | 2 - cpu/src/lib.rs | 2 - .../src/kvm}/aarch64/core_regs.rs | 0 hypervisor/src/kvm/aarch64/mod.rs | 43 +++-------------- hypervisor/src/kvm/aarch64/sys_regs.rs | 48 +++++++++++++++++++ hypervisor/src/kvm/mod.rs | 2 - 6 files changed, 54 insertions(+), 43 deletions(-) rename {cpu/src => hypervisor/src/kvm}/aarch64/core_regs.rs (100%) create mode 100644 hypervisor/src/kvm/aarch64/sys_regs.rs diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index ec6043c03..f7b745ef2 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -11,11 +11,9 @@ // See the Mulan PSL v2 for more details. pub mod caps; -mod core_regs; pub use self::caps::ArmCPUFeatures; pub use self::caps::CpregListEntry; -pub use self::core_regs::Arm64CoreRegs; use std::sync::{Arc, Mutex}; diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 9d6660534..eee9bc209 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -35,8 +35,6 @@ mod aarch64; #[cfg(target_arch = "x86_64")] mod x86_64; -#[cfg(target_arch = "aarch64")] -pub use aarch64::Arm64CoreRegs; #[cfg(target_arch = "aarch64")] pub use aarch64::ArmCPUBootConfig as CPUBootConfig; #[cfg(target_arch = "aarch64")] diff --git a/cpu/src/aarch64/core_regs.rs b/hypervisor/src/kvm/aarch64/core_regs.rs similarity index 100% rename from cpu/src/aarch64/core_regs.rs rename to hypervisor/src/kvm/aarch64/core_regs.rs diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index c9dc60edd..f4673f43a 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -14,6 +14,9 @@ pub mod cpu_caps; pub mod gicv2; pub mod gicv3; +mod core_regs; +mod sys_regs; + use std::mem::forget; use std::os::unix::io::{AsRawFd, FromRawFd}; use std::sync::{Arc, Mutex}; @@ -23,47 +26,13 @@ use kvm_bindings::*; use kvm_ioctls::DeviceFd; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; +use self::core_regs::Arm64CoreRegs; +use self::sys_regs::{KVM_REG_ARM_MPIDR_EL1, KVM_REG_ARM_TIMER_CNT}; use crate::kvm::{KvmCpu, KvmHypervisor}; use cpu::{ - ArchCPU, Arm64CoreRegs, CPUBootConfig, CPUFeatures, CpregListEntry, RegsIndex, CPU, PMU_INTR, - PPI_BASE, + ArchCPU, CPUBootConfig, CPUFeatures, CpregListEntry, RegsIndex, CPU, PMU_INTR, PPI_BASE, }; -// Arm Architecture Reference Manual defines the encoding of AArch64 system registers: -// (Ref: ARMv8 ARM, Section: "System instruction class encoding overview") -// While KVM defines another ID for each AArch64 system register, which is used in calling -// `KVM_G/SET_ONE_REG` to access a system register of a guest. A mapping exists between the -// Arm standard encoding and the KVM ID. -// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/kvm.h#L216 -#[macro_export] -macro_rules! arm64_sys_reg { - ($op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => { - KVM_REG_SIZE_U64 - | KVM_REG_ARM64 - | KVM_REG_ARM64_SYSREG as u64 - | (((($op0 as u32) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK) - as u64) - | (((($op1 as u32) << KVM_REG_ARM64_SYSREG_OP1_SHIFT) & KVM_REG_ARM64_SYSREG_OP1_MASK) - as u64) - | (((($crn as u32) << KVM_REG_ARM64_SYSREG_CRN_SHIFT) & KVM_REG_ARM64_SYSREG_CRN_MASK) - as u64) - | (((($crm as u32) << KVM_REG_ARM64_SYSREG_CRM_SHIFT) & KVM_REG_ARM64_SYSREG_CRM_MASK) - as u64) - | (((($op2 as u32) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK) - as u64) - }; -} - -// The following system register codes can be found at this website: -// https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h - -// MPIDR - Multiprocessor Affinity Register(SYS_MPIDR_EL1). -pub const KVM_REG_ARM_MPIDR_EL1: u64 = arm64_sys_reg!(3, 0, 0, 0, 5); - -// Counter-timer Virtual Count register: Due to the API interface problem, the encode of -// this register is SYS_CNTV_CVAL_EL0. -pub const KVM_REG_ARM_TIMER_CNT: u64 = arm64_sys_reg!(3, 3, 14, 3, 2); - pub const KVM_MAX_CPREG_ENTRIES: usize = 500; const KVM_NR_REGS: u64 = 31; const KVM_NR_FP_REGS: u64 = 32; diff --git a/hypervisor/src/kvm/aarch64/sys_regs.rs b/hypervisor/src/kvm/aarch64/sys_regs.rs new file mode 100644 index 000000000..5a63290b0 --- /dev/null +++ b/hypervisor/src/kvm/aarch64/sys_regs.rs @@ -0,0 +1,48 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use kvm_bindings::*; + +// Arm Architecture Reference Manual defines the encoding of AArch64 system registers: +// (Ref: ARMv8 ARM, Section: "System instruction class encoding overview") +// While KVM defines another ID for each AArch64 system register, which is used in calling +// `KVM_G/SET_ONE_REG` to access a system register of a guest. A mapping exists between the +// Arm standard encoding and the KVM ID. +// See: https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/uapi/asm/kvm.h#L216 +#[macro_export] +macro_rules! arm64_sys_reg { + ($op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: tt) => { + KVM_REG_SIZE_U64 + | KVM_REG_ARM64 + | KVM_REG_ARM64_SYSREG as u64 + | (((($op0 as u32) << KVM_REG_ARM64_SYSREG_OP0_SHIFT) & KVM_REG_ARM64_SYSREG_OP0_MASK) + as u64) + | (((($op1 as u32) << KVM_REG_ARM64_SYSREG_OP1_SHIFT) & KVM_REG_ARM64_SYSREG_OP1_MASK) + as u64) + | (((($crn as u32) << KVM_REG_ARM64_SYSREG_CRN_SHIFT) & KVM_REG_ARM64_SYSREG_CRN_MASK) + as u64) + | (((($crm as u32) << KVM_REG_ARM64_SYSREG_CRM_SHIFT) & KVM_REG_ARM64_SYSREG_CRM_MASK) + as u64) + | (((($op2 as u32) << KVM_REG_ARM64_SYSREG_OP2_SHIFT) & KVM_REG_ARM64_SYSREG_OP2_MASK) + as u64) + }; +} + +// The following system register codes can be found at this website: +// https://elixir.bootlin.com/linux/v5.6/source/arch/arm64/include/asm/sysreg.h + +// MPIDR - Multiprocessor Affinity Register(SYS_MPIDR_EL1). +pub const KVM_REG_ARM_MPIDR_EL1: u64 = arm64_sys_reg!(3, 0, 0, 0, 5); + +// Counter-timer Virtual Count register: Due to the API interface problem, the encode of +// this register is SYS_CNTV_CVAL_EL0. +pub const KVM_REG_ARM_TIMER_CNT: u64 = arm64_sys_reg!(3, 3, 14, 3, 2); diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 4edef423c..adbb37f3f 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -21,8 +21,6 @@ mod listener; #[cfg(target_arch = "x86_64")] pub mod vm_state; -#[cfg(target_arch = "aarch64")] -pub use self::aarch64::{KVM_REG_ARM_MPIDR_EL1, KVM_REG_ARM_TIMER_CNT}; #[cfg(target_arch = "aarch64")] pub use aarch64::gicv2::KvmGICv2; #[cfg(target_arch = "aarch64")] -- Gitee From a965f453a1959da636a537d66da3413ff201b30e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 22 Jan 2024 19:53:01 +0800 Subject: [PATCH 1600/2187] test: remove unused import Signed-off-by: yezengruan --- virtio/src/device/net.rs | 1 - virtio/src/device/serial.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index d8818623f..c815e88de 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1696,7 +1696,6 @@ impl MigrationHook for Net {} #[cfg(test)] mod tests { - pub use super::super::*; pub use super::*; #[test] diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 6aa9b884e..5beb3411e 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -941,7 +941,6 @@ impl ChardevNotifyDevice for SerialPort { #[cfg(test)] mod tests { - pub use super::super::*; pub use super::*; use machine_manager::config::PciBdf; -- Gitee From 1187a13a004ec33c9752a33a67106b3733d30795 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Wed, 31 Jan 2024 20:12:16 +0800 Subject: [PATCH 1601/2187] fmt: update trace format for legacy device Modify the legacy device trace format to make it more readable. Signed-off-by: yezengruan --- trace/trace_event/device_legacy.toml | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml index 69d1fc616..dbbb4bb25 100644 --- a/trace/trace_event/device_legacy.toml +++ b/trace/trace_event/device_legacy.toml @@ -1,13 +1,13 @@ [[events]] name = "pl031_read" args = "addr: u64, value: u32" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:04x} value 0x{:08x}" enabled = true [[events]] name = "pl031_write" args = "addr: u64, value: u32" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:04x} value 0x{:08x}" enabled = true [[events]] @@ -19,13 +19,13 @@ enabled = true [[events]] name = "rtc_read" args = "addr: u8, value: u8" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:02x} value 0x{:02x}" enabled = true [[events]] name = "rtc_write" args = "addr: u8, value: u8" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:02x} value 0x{:02x}" enabled = true [[events]] @@ -37,7 +37,7 @@ enabled = true [[events]] name = "pl011_read" args = "addr: u64, value: u32" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:08x} value 0x{:08x}" enabled = true [[events]] @@ -49,13 +49,13 @@ enabled = true [[events]] name = "pl011_write" args = "addr: u64, value: u32" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:08x} value 0x{:08x}" enabled = true [[events]] name = "pl011_interrupt" args = "flag: u32" -message = "flag 0x{:X}" +message = "flag 0x{:08x}" enabled = true [[events]] @@ -67,7 +67,7 @@ enabled = true [[events]] name = "pl011_receive" args = "value: u32, read_count: u32" -message = "new char 0x{:X}, read_count now {}" +message = "new char 0x{:08x}, read_count now {}" enabled = true [[events]] @@ -79,19 +79,19 @@ enabled = true [[events]] name = "serial_read" args = "addr: u64, value: u8" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:08x} value 0x{:02x}" enabled = true [[events]] name = "serial_write" args = "addr: u64, value: u8" -message = "addr 0x{:X} value 0x{:X}" +message = "addr 0x{:08x} value 0x{:02x}" enabled = true [[events]] name = "serial_update_iir" args = "iir: u8" -message = "value 0x{:X}" +message = "value 0x{:02x}" enabled = true [[events]] @@ -103,31 +103,31 @@ enabled = true [[events]] name = "pflash_device_id" args = "id: u32" -message = "read device ID: 0x{:X}" +message = "read device ID: 0x{:04x}" enabled = true [[events]] name = "pflash_device_info" args = "offset: u64" -message = "read device information offset: 0x{:X}" +message = "read device information offset: 0x{:04x}" enabled = true [[events]] name = "pflash_io_read" args = "offset: u64, size: u32, value: u32, cmd: u8, wcycle: u32" -message = "offset: 0x{:X}, size: {}, value: 0x{:X}, cmd: 0x{:X}, wcycle: 0x{:X}" +message = "offset: 0x{:04x}, size: {}, value: 0x{:04x}, cmd: 0x{:02x}, wcycle: {}" enabled = true [[events]] name = "pflash_io_write" args = "offset: u64, size: u8, value: u32, wcycle: u32" -message = "offset: 0x{:X}, size: {}, value: 0x{:X}, wcycle: 0x{:X}" +message = "offset: 0x{:04x}, size: {}, value: 0x{:04x}, wcycle: {}" enabled = true [[events]] name = "pflash_manufacturer_id" args = "id: u32" -message = "read manufacturer ID: 0x{:X}" +message = "read manufacturer ID: 0x{:04x}" enabled = true [[events]] @@ -139,41 +139,41 @@ enabled = true [[events]] name = "pflash_read_data" args = "offset: u64, len: usize, value: &[u8]" -message = "data offset: 0x{:X}, length: {}, value: 0x{:X?}" +message = "data offset: 0x{:04x}, length: {}, value: 0x{:x?}" enabled = true [[events]] name = "pflash_read_status" args = "status: u32" -message = "status: 0x{:X}" +message = "status: 0x{:x}" enabled = true [[events]] name = "pflash_read_unknown_state" args = "cmd: u8" -message = "unknown command state: 0x{:X}" +message = "unknown command state: 0x{:02x}" enabled = true [[events]] name = "pflash_write" args = "str: String, cmd: u8" -message = "{}, cmd: 0x{:X}" +message = "{}, cmd: 0x{:02x}" enabled = true [[events]] name = "pflash_write_block" args = "value: u32" -message = "block write: bytes: 0x{:X}" +message = "block write: bytes: 0x{:x}" enabled = true [[events]] name = "pflash_write_block_erase" args = "offset: u64, len: u32" -message = "block erase offset: 0x{:X}, bytes: 0x{:X}" +message = "block erase offset: 0x{:04x}, bytes: 0x{:x}" enabled = true [[events]] name = "pflash_write_data" args = "offset: u64, size: usize, value: &[u8], counter: u32" -message = "data offset: 0x{:X}, size: {}, value: 0x{:X?}, counter: 0x{:X}" +message = "data offset: 0x{:04x}, size: {}, value: 0x{:x?}, counter: 0x{:04x}" enabled = true -- Gitee From fe9995f7d23a0c21777e5820cfc9adbd55c2b470 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 5 Feb 2024 23:28:33 +0800 Subject: [PATCH 1602/2187] hypervisor: Fix kvm arch_(get|set)_regs Should ensure related caps are enabled before access regs. Signed-off-by: Keqian Zhu --- hypervisor/src/kvm/aarch64/cpu_caps.rs | 2 ++ hypervisor/src/kvm/aarch64/mod.rs | 43 ++++++++++++-------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/hypervisor/src/kvm/aarch64/cpu_caps.rs b/hypervisor/src/kvm/aarch64/cpu_caps.rs index 58759a435..f7876bfff 100644 --- a/hypervisor/src/kvm/aarch64/cpu_caps.rs +++ b/hypervisor/src/kvm/aarch64/cpu_caps.rs @@ -22,6 +22,7 @@ pub struct ArmCPUCaps { pub user_mem: bool, pub psci02: bool, pub mp_state: bool, + pub vcpu_events: bool, } impl ArmCPUCaps { @@ -35,6 +36,7 @@ impl ArmCPUCaps { user_mem: kvm.check_extension(Cap::UserMemory), psci02: kvm.check_extension(Cap::ArmPsci02), mp_state: kvm.check_extension(Cap::MpState), + vcpu_events: kvm.check_extension(Cap::VcpuEvents), } } } diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index f4673f43a..403250a7e 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -208,7 +208,9 @@ impl KvmCpu { } } RegsIndex::VcpuEvents => { - locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; + if self.caps.vcpu_events { + locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; + } } RegsIndex::CpregList => { let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES)?; @@ -250,14 +252,18 @@ impl KvmCpu { .with_context(|| format!("Failed to set core register for CPU {}", apic_id))?; } RegsIndex::MpState => { - self.fd - .set_mp_state(locked_arch_cpu.mp_state) - .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + if self.caps.mp_state { + self.fd + .set_mp_state(locked_arch_cpu.mp_state) + .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + } } RegsIndex::VcpuEvents => { - self.fd - .set_vcpu_events(&locked_arch_cpu.cpu_events) - .with_context(|| format!("Failed to set vcpu event for CPU {}", apic_id))?; + if self.caps.vcpu_events { + self.fd + .set_vcpu_events(&locked_arch_cpu.cpu_events) + .with_context(|| format!("Failed to set vcpu event for CPU {}", apic_id))?; + } } RegsIndex::CpregList => { for cpreg in locked_arch_cpu.cpreg_list[0..locked_arch_cpu.cpreg_len].iter() { @@ -281,7 +287,7 @@ impl KvmCpu { /// # Arguments /// /// * `vcpu_fd` - the VcpuFd in KVM mod. - pub fn get_core_regs(&self) -> Result { + fn get_core_regs(&self) -> Result { let mut core_regs = kvm_regs::default(); core_regs.regs.sp = self.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; @@ -320,7 +326,7 @@ impl KvmCpu { /// /// * `vcpu_fd` - the VcpuFd in KVM mod. /// * `core_regs` - kvm_regs state to be written. - pub fn set_core_regs(&self, core_regs: kvm_regs) -> Result<()> { + fn set_core_regs(&self, core_regs: kvm_regs) -> Result<()> { self.set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; self.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; self.set_one_reg( @@ -399,19 +405,10 @@ impl KvmCpu { } pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { - let locked_arch_cpu = cpu.arch_cpu.lock().unwrap(); - let apic_id = locked_arch_cpu.apic_id; - self.set_core_regs(locked_arch_cpu.core_regs) - .with_context(|| format!("Failed to set core register for CPU {}", apic_id))?; - self.fd - .set_mp_state(locked_arch_cpu.mp_state) - .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; - for cpreg in locked_arch_cpu.cpreg_list[0..locked_arch_cpu.cpreg_len].iter() { - self.set_cpreg(cpreg) - .with_context(|| format!("Failed to set cpreg for CPU {}", apic_id))?; - } - self.fd - .set_vcpu_events(&locked_arch_cpu.cpu_events) - .with_context(|| format!("Failed to set vcpu event for CPU {}", apic_id)) + let arch_cpu = &cpu.arch_cpu; + self.arch_set_regs(arch_cpu.clone(), RegsIndex::CoreRegs)?; + self.arch_set_regs(arch_cpu.clone(), RegsIndex::MpState)?; + self.arch_set_regs(arch_cpu.clone(), RegsIndex::CpregList)?; + self.arch_set_regs(arch_cpu.clone(), RegsIndex::VcpuEvents) } } -- Gitee From 8a76802ac2bf40469c83e85dfdfc9232515a3e8c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 6 Feb 2024 00:13:30 +0800 Subject: [PATCH 1603/2187] hypervisor: Enhance logic of accessing cpreg list Throw error when the cpreg is not normal. And refactor code to make it clear. Signed-off-by: Keqian Zhu --- hypervisor/src/kvm/aarch64/mod.rs | 57 ++++++++++++++----------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 403250a7e..c78b528e5 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -21,7 +21,7 @@ use std::mem::forget; use std::os::unix::io::{AsRawFd, FromRawFd}; use std::sync::{Arc, Mutex}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use kvm_bindings::*; use kvm_ioctls::DeviceFd; use vmm_sys_util::{ioctl_ioc_nr, ioctl_iow_nr, ioctl_iowr_nr}; @@ -221,11 +221,12 @@ impl KvmCpu { reg_id: *cpreg, value: 0, }; - if self.validate(&cpreg_entry) { - self.get_cpreg(&mut cpreg_entry)?; - locked_arch_cpu.cpreg_list[index] = cpreg_entry; - locked_arch_cpu.cpreg_len += 1; + if !self.get_cpreg(&mut cpreg_entry)? { + // We sync these cpreg by hand, such as core regs. + continue; } + locked_arch_cpu.cpreg_list[index] = cpreg_entry; + locked_arch_cpu.cpreg_len += 1; } } RegsIndex::VtimerCount => { @@ -366,42 +367,34 @@ impl KvmCpu { Ok(()) } - fn cpreg_tuples_entry(&self, cpreg_list_entry: &CpregListEntry) -> bool { - (cpreg_list_entry.reg_id & KVM_REG_ARM_COPROC_MASK as u64) == (KVM_REG_ARM_CORE as u64) - } - - pub fn normal_cpreg_entry(&self, cpreg_list_entry: &CpregListEntry) -> bool { - if self.cpreg_tuples_entry(cpreg_list_entry) { - return false; + fn reg_sync_by_cpreg_list(reg_id: u64) -> Result { + let coproc = reg_id & KVM_REG_ARM_COPROC_MASK as u64; + if coproc != KVM_REG_ARM_CORE as u64 { + return Ok(false); } - ((cpreg_list_entry.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) - || ((cpreg_list_entry.reg_id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) - } - - /// Validate cpreg_list's tuples entry and normal entry. - pub fn validate(&self, cpreg_list_entry: &CpregListEntry) -> bool { - if self.cpreg_tuples_entry(cpreg_list_entry) { - return true; + let size = reg_id & KVM_REG_SIZE_MASK; + if size == KVM_REG_SIZE_U32 || size == KVM_REG_SIZE_U64 { + Ok(true) + } else { + bail!("Can't handle size of register in cpreg list"); } - - self.normal_cpreg_entry(cpreg_list_entry) } - pub fn get_cpreg(&self, cpreg_list_entry: &mut CpregListEntry) -> Result<()> { - if self.normal_cpreg_entry(cpreg_list_entry) { - cpreg_list_entry.value = self.get_one_reg(cpreg_list_entry.reg_id)?; + fn get_cpreg(&self, cpreg: &mut CpregListEntry) -> Result { + if !Self::reg_sync_by_cpreg_list(cpreg.reg_id)? { + return Ok(false); } - - Ok(()) + cpreg.value = self.get_one_reg(cpreg.reg_id)?; + Ok(true) } - pub fn set_cpreg(&self, cpreg: &CpregListEntry) -> Result<()> { - if self.normal_cpreg_entry(cpreg) { - self.set_one_reg(cpreg.reg_id, cpreg.value)?; + fn set_cpreg(&self, cpreg: &CpregListEntry) -> Result { + if !Self::reg_sync_by_cpreg_list(cpreg.reg_id)? { + return Ok(false); } - - Ok(()) + self.set_one_reg(cpreg.reg_id, cpreg.value)?; + Ok(true) } pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { -- Gitee From 23b326494da1273006260834f7134b43792d879a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Feb 2024 21:35:34 +0800 Subject: [PATCH 1604/2187] Build: Add a dynamic library related to ohos. Add a dynamic library related to ohos. Signed-off-by: Jinhao Gao --- build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/build.rs b/build.rs index 96f77ffd4..e060c79a5 100644 --- a/build.rs +++ b/build.rs @@ -16,6 +16,7 @@ fn ohos_env_configure() { println!("cargo:rustc-link-arg=--verbose"); println!("cargo:rustc-link-arg=--sysroot={}/sysroot", ohos_sdk_path); println!("cargo:rustc-link-arg=-lpixman_static"); + println!("cargo:rustc-link-lib=hmlibs"); println!( "cargo:rustc-link-search={}/sysroot/usr/lib/aarch64-linux-ohos", ohos_sdk_path -- Gitee From 9eaac7b4dba7f43737f6a7946c5d8737ea3d1732 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 15 Jan 2024 16:53:38 +0800 Subject: [PATCH 1605/2187] Scream: use clap to parse the parameters of the scream device Use clap to parse the parameters of the scream device. Signed-off-by: Mingwang Li Signed-off-by: liuxiangdong --- Cargo.lock | 42 +++++++++++ devices/Cargo.toml | 1 + devices/src/misc/scream/mod.rs | 69 ++++++++++++++--- machine/Cargo.toml | 1 + machine/src/lib.rs | 21 +++--- machine_manager/src/config/mod.rs | 21 +++++- machine_manager/src/config/scream.rs | 107 --------------------------- 7 files changed, 132 insertions(+), 130 deletions(-) delete mode 100644 machine_manager/src/config/scream.rs diff --git a/Cargo.lock b/Cargo.lock index 0b1ce7371..a0b694288 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,40 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "const_format" version = "0.2.31" @@ -330,6 +364,7 @@ dependencies = [ "byteorder", "cairo-rs", "chardev_backend", + "clap", "cpu", "drm-fourcc", "libc", @@ -903,6 +938,7 @@ dependencies = [ "anyhow", "block_backend", "boot_loader", + "clap", "cpu", "devices", "hypervisor", @@ -1126,6 +1162,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "ozone" version = "2.3.0" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index b56665f1f..3f921078d 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -36,6 +36,7 @@ rusb = { version = "0.9", optional = true } libusb1-sys = { version = "0.6.4", optional = true } cairo-rs = { version = "0.17.10", optional = true } trace = { path = "../trace" } +clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } [features] default = [] diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 921a7b872..5428fd5d3 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -20,6 +20,7 @@ mod pulseaudio; use std::{ mem, + str::FromStr, sync::{ atomic::{fence, Ordering}, Arc, Mutex, Weak, @@ -27,7 +28,8 @@ use std::{ thread, }; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use core::time; use log::{error, warn}; @@ -37,7 +39,7 @@ use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; -use machine_manager::config::scream::{ScreamConfig, ScreamInterface}; +use machine_manager::config::{get_pci_df, valid_id}; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] @@ -331,29 +333,72 @@ impl StreamData { } } +#[derive(Clone, Debug)] +enum ScreamInterface { + #[cfg(feature = "scream_alsa")] + Alsa, + #[cfg(feature = "scream_pulseaudio")] + PulseAudio, + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + OhAudio, + Demo, +} + +impl FromStr for ScreamInterface { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + #[cfg(feature = "scream_alsa")] + "ALSA" => Ok(ScreamInterface::Alsa), + #[cfg(feature = "scream_pulseaudio")] + "PulseAudio" => Ok(ScreamInterface::PulseAudio), + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + "OhAudio" => Ok(ScreamInterface::OhAudio), + "Demo" => Ok(ScreamInterface::Demo), + _ => Err(anyhow!("Unknown scream interface")), + } + } +} + +#[derive(Parser, Debug, Clone)] +#[command(name = "ivshmem_scream")] +pub struct ScreamConfig { + #[arg(long, value_parser = valid_id)] + id: String, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + #[arg(long)] + pub memdev: String, + #[arg(long)] + interface: ScreamInterface, + #[arg(long, default_value = "")] + playback: String, + #[arg(long, default_value = "")] + record: String, +} + /// Scream sound card device structure. pub struct Scream { hva: u64, size: u64, - interface: ScreamInterface, - playback: String, - record: String, + config: ScreamConfig, } impl Scream { - pub fn new(size: u64, dev_cfg: &ScreamConfig) -> Self { + pub fn new(size: u64, config: ScreamConfig) -> Self { Self { hva: 0, size, - interface: dev_cfg.interface, - playback: dev_cfg.playback.clone(), - record: dev_cfg.record.clone(), + config, } } #[allow(unused_variables)] fn interface_init(&self, name: &str, dir: ScreamDirection) -> Arc> { - match self.interface { + match self.config.interface { #[cfg(feature = "scream_alsa")] ScreamInterface::Alsa => Arc::new(Mutex::new(AlsaStreamData::init(name, dir))), #[cfg(feature = "scream_pulseaudio")] @@ -362,8 +407,8 @@ impl Scream { ScreamInterface::OhAudio => Arc::new(Mutex::new(OhAudio::init(dir))), ScreamInterface::Demo => Arc::new(Mutex::new(AudioDemo::init( dir, - self.playback.clone(), - self.record.clone(), + self.config.playback.clone(), + self.config.record.clone(), ))), } } diff --git a/machine/Cargo.toml b/machine/Cargo.toml index f8470ea8c..0072b862b 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -28,6 +28,7 @@ vfio = { path = "../vfio" } block_backend = { path = "../block_backend" } ui = { path = "../ui" } trace = { path = "../trace" } +clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } [features] default = [] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a76e09e45..44fb43893 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -34,6 +34,8 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::time::Duration; use anyhow::{anyhow, bail, Context, Result}; +#[cfg(feature = "scream")] +use clap::Parser; use log::warn; #[cfg(feature = "windows_emu_pid")] use vmm_sys_util::eventfd::EventFd; @@ -46,7 +48,7 @@ use devices::legacy::FwCfgOps; #[cfg(feature = "pvpanic")] use devices::misc::pvpanic::PvPanicPci; #[cfg(feature = "scream")] -use devices::misc::scream::Scream; +use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] use devices::pci::demo_device::DemoDev; use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; @@ -76,7 +78,7 @@ use machine_manager::config::parse_usb_camera; #[cfg(feature = "usb_host")] use machine_manager::config::parse_usb_host; #[cfg(feature = "scream")] -use machine_manager::config::scream::parse_scream; +use machine_manager::config::str_slip_to_clap; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, @@ -1568,20 +1570,21 @@ pub trait MachineOps { /// * `cfg_args` - scream configuration. #[cfg(feature = "scream")] fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; + let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let bdf = PciBdf { + bus: config.bus.clone(), + addr: config.addr, + }; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let dev_cfg = - parse_scream(cfg_args).with_context(|| "Failed to parse cmdline for ivshmem")?; - let mem_cfg = vm_config .object .mem_object - .remove(&dev_cfg.memdev) + .remove(&config.memdev) .with_context(|| { format!( "Object for memory-backend-ram {} config not found", - dev_cfg.memdev + config.memdev ) })?; @@ -1589,7 +1592,7 @@ pub trait MachineOps { bail!("Object for share config is not on"); } - let scream = Scream::new(mem_cfg.size, &dev_cfg); + let scream = Scream::new(mem_cfg.size, config); scream .realize(devfn, parent_bus) .with_context(|| "Failed to realize scream device") diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 80031e3ab..fabbfeff1 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -41,8 +41,6 @@ mod ramfb; mod rng; #[cfg(feature = "vnc_auth")] mod sasl_auth; -#[cfg(feature = "scream")] -pub mod scream; mod scsi; mod smbios; #[cfg(feature = "vnc_auth")] @@ -732,6 +730,25 @@ pub fn check_arg_nonexist(arg: Option, name: &str, device: &str) -> Resu Ok(()) } +/// Configure StratoVirt parameters in clap format. +pub fn str_slip_to_clap(args: &str) -> Vec { + let args_vecs = args.split([',', '=']).collect::>(); + let mut itr: Vec = Vec::with_capacity(args_vecs.len()); + for (cnt, param) in args_vecs.iter().enumerate() { + if cnt % 2 == 1 { + itr.push(format!("--{}", param)); + } else { + itr.push(param.to_string()); + } + } + itr +} + +pub fn valid_id(id: &str) -> Result { + check_arg_too_long(id, "id")?; + Ok(id.to_string()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/scream.rs b/machine_manager/src/config/scream.rs deleted file mode 100644 index b06603a7e..000000000 --- a/machine_manager/src/config/scream.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::str::FromStr; - -use anyhow::{anyhow, Context, Result}; -use serde::{Deserialize, Serialize}; - -use super::{pci_args_check, CmdParser}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum ScreamInterface { - #[cfg(feature = "scream_alsa")] - Alsa, - #[cfg(feature = "scream_pulseaudio")] - PulseAudio, - #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] - OhAudio, - Demo, -} - -impl FromStr for ScreamInterface { - type Err = anyhow::Error; - - fn from_str(s: &str) -> std::result::Result { - match s { - #[cfg(feature = "scream_alsa")] - "ALSA" => Ok(ScreamInterface::Alsa), - #[cfg(feature = "scream_pulseaudio")] - "PulseAudio" => Ok(ScreamInterface::PulseAudio), - "Demo" => Ok(ScreamInterface::Demo), - #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] - "OhAudio" => Ok(ScreamInterface::OhAudio), - _ => Err(anyhow!("Unknown scream interface")), - } - } -} - -pub struct ScreamConfig { - pub memdev: String, - pub interface: ScreamInterface, - pub playback: String, - pub record: String, -} - -impl ScreamConfig { - pub fn new() -> Self { - Self { - memdev: "".to_string(), - interface: ScreamInterface::Demo, - playback: "".to_string(), - record: "".to_string(), - } - } -} - -impl Default for ScreamConfig { - fn default() -> Self { - Self::new() - } -} - -pub fn parse_scream(cfg_args: &str) -> Result { - let mut cmd_parser = CmdParser::new("scream"); - cmd_parser - .push("") - .push("memdev") - .push("interface") - .push("playback") - .push("record") - .push("id") - .push("bus") - .push("addr"); - cmd_parser.parse(cfg_args)?; - - pci_args_check(&cmd_parser)?; - - let mut dev_cfg = ScreamConfig::new(); - - dev_cfg.memdev = cmd_parser - .get_value::("memdev")? - .with_context(|| "No memdev configured for scream device")?; - - dev_cfg.interface = cmd_parser - .get_value::("interface")? - .with_context(|| "No interface configured for scream device")?; - - if dev_cfg.interface == ScreamInterface::Demo { - dev_cfg.playback = cmd_parser - .get_value::("playback")? - .with_context(|| "No playback configured for interface")?; - dev_cfg.record = cmd_parser - .get_value::("record")? - .with_context(|| "No record configured for interface")?; - } - - Ok(dev_cfg) -} -- Gitee From 6a9e1fbc6904cc442178a630ef5c83dc633dfbd2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 16 Jan 2024 19:04:37 +0800 Subject: [PATCH 1606/2187] Balloon: use clap to parse the parameters of the balloon Use clap to parse the parameters of the balloon. Signed-off-by: Mingwang Li Signed-off-by: liuxiangdong --- Cargo.lock | 1 + machine/src/lib.rs | 44 +++-- machine_manager/src/config/balloon.rs | 227 -------------------------- machine_manager/src/config/mod.rs | 10 +- virtio/Cargo.toml | 1 + virtio/src/device/balloon.rs | 123 +++++++++----- 6 files changed, 119 insertions(+), 287 deletions(-) delete mode 100644 machine_manager/src/config/balloon.rs diff --git a/Cargo.lock b/Cargo.lock index a0b694288..cd5e5504a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1875,6 +1875,7 @@ dependencies = [ "block_backend", "byteorder", "chardev_backend", + "clap", "devices", "libc", "log", diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 44fb43893..97fea5d76 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -34,7 +34,6 @@ use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::time::Duration; use anyhow::{anyhow, bail, Context, Result}; -#[cfg(feature = "scream")] use clap::Parser; use log::warn; #[cfg(feature = "windows_emu_pid")] @@ -77,15 +76,14 @@ use machine_manager::config::parse_pvpanic; use machine_manager::config::parse_usb_camera; #[cfg(feature = "usb_host")] use machine_manager::config::parse_usb_host; -#[cfg(feature = "scream")] -use machine_manager::config::str_slip_to_clap; use machine_manager::config::{ - complete_numa_node, get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, + complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, - parse_virtio_serial, parse_virtserialport, parse_vsock, BootIndexInfo, BootSource, DriveFile, - Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, - PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, + BootSource, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, + NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + MAX_VIRTIO_QUEUE, }; use machine_manager::config::{ parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, @@ -104,8 +102,8 @@ use vfio::{VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(feature = "virtio_gpu")] use virtio::Gpu; use virtio::{ - balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, Block, BlockState, Rng, - RngState, + balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block, + BlockState, Rng, RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, @@ -665,23 +663,37 @@ pub trait MachineOps { } fn add_virtio_balloon(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_balloon(vm_config, cfg_args)?; + if vm_config.dev_name.get("balloon").is_some() { + bail!("Only one balloon device is supported for each vm."); + } + let config = BalloonConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + vm_config.dev_name.insert("balloon".to_string(), 1); + let sys_mem = self.get_sys_mem(); - let balloon = Arc::new(Mutex::new(Balloon::new(&device_cfg, sys_mem.clone()))); + let balloon = Arc::new(Mutex::new(Balloon::new(config.clone(), sys_mem.clone()))); Balloon::object_init(balloon.clone()); match parse_device_type(cfg_args)?.as_str() { "virtio-balloon-device" => { + if config.addr.is_some() || config.bus.is_some() || config.multifunction.is_some() { + bail!("virtio balloon device config is error!"); + } let device = VirtioMmioDevice::new(sys_mem, balloon); self.realize_virtio_mmio_device(device)?; } _ => { - let name = device_cfg.id; - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; + if config.addr.is_none() || config.bus.is_none() { + bail!("virtio balloon pci config is error!"); + } + let bdf = PciBdf { + bus: config.bus.unwrap(), + addr: config.addr.unwrap(), + }; + let multi_func = config.multifunction.unwrap_or_default(); let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); - let virtio_pci_device = - VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func); + let virtio_pci_device = VirtioPciDevice::new( + config.id, devfn, sys_mem, balloon, parent_bus, multi_func, + ); virtio_pci_device .realize() .with_context(|| "Failed to add virtio pci balloon device")?; diff --git a/machine_manager/src/config/balloon.rs b/machine_manager/src/config/balloon.rs deleted file mode 100644 index d385d49d2..000000000 --- a/machine_manager/src/config/balloon.rs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{anyhow, bail, Result}; -use serde::{Deserialize, Serialize}; - -use super::{pci_args_check, ConfigCheck}; -use crate::config::{check_arg_too_long, CmdParser, ConfigError, ExBool, VmConfig}; - -const MEM_BUFFER_PERCENT_MIN: u32 = 20; -const MEM_BUFFER_PERCENT_MAX: u32 = 80; -const MEM_BUFFER_PERCENT_DEFAULT: u32 = 50; -const MONITOR_INTERVAL_SECOND_MIN: u32 = 5; -const MONITOR_INTERVAL_SECOND_MAX: u32 = 300; -const MONITOR_INTERVAL_SECOND_DEFAULT: u32 = 10; - -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct BalloonConfig { - pub id: String, - pub deflate_on_oom: bool, - pub free_page_reporting: bool, - pub auto_balloon: bool, - pub membuf_percent: u32, - pub monitor_interval: u32, -} - -impl ConfigCheck for BalloonConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "balloon id")?; - - if !self.auto_balloon { - return Ok(()); - } - if self.membuf_percent > MEM_BUFFER_PERCENT_MAX - || self.membuf_percent < MEM_BUFFER_PERCENT_MIN - { - return Err(anyhow!(ConfigError::IllegalValue( - "balloon membuf-percent".to_string(), - MEM_BUFFER_PERCENT_MIN as u64, - false, - MEM_BUFFER_PERCENT_MAX as u64, - false, - ))); - } - if self.monitor_interval > MONITOR_INTERVAL_SECOND_MAX - || self.monitor_interval < MONITOR_INTERVAL_SECOND_MIN - { - return Err(anyhow!(ConfigError::IllegalValue( - "balloon monitor-interval".to_string(), - MONITOR_INTERVAL_SECOND_MIN as u64, - false, - MONITOR_INTERVAL_SECOND_MAX as u64, - false, - ))); - } - Ok(()) - } -} - -pub fn parse_balloon(vm_config: &mut VmConfig, balloon_config: &str) -> Result { - if vm_config.dev_name.get("balloon").is_some() { - bail!("Only one balloon device is supported for each vm."); - } - let mut cmd_parser = CmdParser::new("virtio-balloon"); - cmd_parser - .push("") - .push("bus") - .push("addr") - .push("multifunction") - .push("id") - .push("deflate-on-oom") - .push("free-page-reporting") - .push("auto-balloon") - .push("membuf-percent") - .push("monitor-interval"); - cmd_parser.parse(balloon_config)?; - - pci_args_check(&cmd_parser)?; - let mut balloon = BalloonConfig { - membuf_percent: MEM_BUFFER_PERCENT_DEFAULT, - monitor_interval: MONITOR_INTERVAL_SECOND_DEFAULT, - ..Default::default() - }; - - if let Some(default) = cmd_parser.get_value::("deflate-on-oom")? { - balloon.deflate_on_oom = default.into(); - } - if let Some(default) = cmd_parser.get_value::("free-page-reporting")? { - balloon.free_page_reporting = default.into(); - } - if let Some(id) = cmd_parser.get_value::("id")? { - balloon.id = id; - } - if let Some(default) = cmd_parser.get_value::("auto-balloon")? { - balloon.auto_balloon = default.into(); - } - if let Some(membuf_percent) = cmd_parser.get_value::("membuf-percent")? { - balloon.membuf_percent = membuf_percent; - } - if let Some(monitor_interval) = cmd_parser.get_value::("monitor-interval")? { - balloon.monitor_interval = monitor_interval; - } - balloon.check()?; - vm_config.dev_name.insert("balloon".to_string(), 1); - Ok(balloon) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::get_pci_bdf; - - #[test] - fn test_balloon_config_cmdline_parser() { - let mut vm_config = VmConfig::default(); - let bln_cfg_res = parse_balloon( - &mut vm_config, - "virtio-balloon-device,deflate-on-oom=true,id=balloon0", - ); - assert!(bln_cfg_res.is_ok()); - let balloon_configs = bln_cfg_res.unwrap(); - assert_eq!(balloon_configs.id, "balloon0".to_string()); - assert_eq!(balloon_configs.deflate_on_oom, true); - } - - #[test] - fn test_pci_balloon_config_cmdline_parser() { - let mut vm_config = VmConfig::default(); - let bln_cfg = "virtio-balloon-pci,deflate-on-oom=true,bus=pcie.0,addr=0x1.0x2,id=balloon0"; - let bln_cfg_res = parse_balloon(&mut vm_config, bln_cfg); - assert!(bln_cfg_res.is_ok()); - let balloon_configs = bln_cfg_res.unwrap(); - assert_eq!(balloon_configs.id, "balloon0".to_string()); - assert_eq!(balloon_configs.deflate_on_oom, true); - - let pci_bdf = get_pci_bdf(bln_cfg); - assert!(pci_bdf.is_ok()); - let pci = pci_bdf.unwrap(); - assert_eq!(pci.bus, "pcie.0".to_string()); - assert_eq!(pci.addr, (1, 2)); - - let mut vm_config = VmConfig::default(); - let bln_cfg = "virtio-balloon-pci,deflate-on-oom=true,bus=pcie.0,addr=0x1.0x2,id=balloon0,multifunction=on"; - assert!(parse_balloon(&mut vm_config, bln_cfg).is_ok()); - } - - #[test] - fn test_two_balloon_config_cmdline_parser() { - let mut vm_config = VmConfig::default(); - let bln_cfg_res1 = parse_balloon( - &mut vm_config, - "virtio-balloon-device,deflate-on-oom=true,id=balloon0", - ); - assert!(bln_cfg_res1.is_ok()); - let bln_cfg_res2 = parse_balloon( - &mut vm_config, - "virtio-balloon-device,deflate-on-oom=true,id=balloon1", - ); - assert!(bln_cfg_res2.is_err()); - } - - #[test] - fn test_fpr_balloon_config_cmdline_parser() { - let mut vm_config1 = VmConfig::default(); - let bln_cfg_res1 = parse_balloon( - &mut vm_config1, - "virtio-balloon-device,free-page-reporting=true,id=balloon0", - ); - assert!(bln_cfg_res1.is_ok()); - let balloon_configs1 = bln_cfg_res1.unwrap(); - assert_eq!(balloon_configs1.id, "balloon0".to_string()); - assert_eq!(balloon_configs1.free_page_reporting, true); - - let mut vm_config2 = VmConfig::default(); - let bln_cfg_res2 = parse_balloon( - &mut vm_config2, - "virtio-balloon-device,free-page-reporting=false,id=balloon0", - ); - assert!(bln_cfg_res2.is_ok()); - let balloon_configs2 = bln_cfg_res2.unwrap(); - assert_eq!(balloon_configs2.id, "balloon0".to_string()); - assert_eq!(balloon_configs2.free_page_reporting, false); - - let mut vm_config3 = VmConfig::default(); - let bln_cfg_res3 = parse_balloon( - &mut vm_config3, - "virtio-balloon-pci,free-page-reporting=true,bus=pcie.0,addr=0x1.0x2,id=balloon0", - ); - assert!(bln_cfg_res3.is_ok()); - let balloon_configs3 = bln_cfg_res3.unwrap(); - assert_eq!(balloon_configs3.id, "balloon0".to_string()); - assert_eq!(balloon_configs3.free_page_reporting, true); - - let mut vm_config4 = VmConfig::default(); - let bln_cfg_res4 = parse_balloon( - &mut vm_config4, - "virtio-balloon-pci,free-page-reporting=false,bus=pcie.0,addr=0x1.0x2,id=balloon0", - ); - assert!(bln_cfg_res4.is_ok()); - let balloon_configs4 = bln_cfg_res4.unwrap(); - assert_eq!(balloon_configs4.id, "balloon0".to_string()); - assert_eq!(balloon_configs4.free_page_reporting, false); - - let mut vm_config5 = VmConfig::default(); - let bln_cfg_res5 = parse_balloon(&mut vm_config5, "virtio-balloon-device,id=balloon0"); - assert!(bln_cfg_res5.is_ok()); - let balloon_configs5 = bln_cfg_res5.unwrap(); - assert_eq!(balloon_configs5.id, "balloon0".to_string()); - assert_eq!(balloon_configs5.free_page_reporting, false); - - let mut vm_config6 = VmConfig::default(); - let bln_cfg_res6 = parse_balloon( - &mut vm_config6, - "virtio-balloon-device,free-page-reporting=2,id=balloon0", - ); - assert!(bln_cfg_res6.is_err()); - } -} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index fabbfeff1..ae2fbc28f 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -18,7 +18,6 @@ pub mod error; #[cfg(feature = "vnc")] pub mod vnc; -mod balloon; mod boot_source; mod chardev; #[cfg(feature = "demo_device")] @@ -48,7 +47,6 @@ mod tls_creds; mod usb; mod vfio; -pub use balloon::*; pub use boot_source::*; #[cfg(feature = "usb_camera")] pub use camera::*; @@ -619,6 +617,14 @@ impl From for bool { } } +pub fn parse_bool(s: &str) -> Result { + match s { + "on" => Ok(true), + "off" => Ok(false), + _ => Err(anyhow!("Unknow bool value {s}")), + } +} + fn enable_trace_events(path: &str) -> Result<()> { let mut file = File::open(path).with_context(|| format!("Failed to open {}", path))?; let mut buf = String::new(); diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 5fc306acc..0cf839343 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -26,6 +26,7 @@ block_backend = {path = "../block_backend"} chardev_backend = {path = "../chardev_backend" } ui = { path = "../ui", features = ["console"], optional = true } trace = {path = "../trace"} +clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } [features] default = [] diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 67204eae1..96dae3ecd 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -21,6 +21,7 @@ use std::{ }; use anyhow::{anyhow, Context, Result}; +use clap::{ArgAction, Parser}; use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, timerfd::TimerFd}; @@ -33,7 +34,8 @@ use address_space::{ AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; use machine_manager::{ - config::{BalloonConfig, DEFAULT_VIRTQUEUE_SIZE}, + config::{get_pci_df, parse_bool, DEFAULT_VIRTQUEUE_SIZE}, + config::{valid_id, ConfigCheck, ConfigError}, event, event_loop::{register_event_helper, unregister_event_helper}, qmp::qmp_channel::QmpChannel, @@ -64,6 +66,11 @@ const IN_IOVEC: bool = true; const OUT_IOVEC: bool = false; const BITS_OF_TYPE_U64: u64 = 64; +const MEM_BUFFER_PERCENT_MIN: u32 = 20; +const MEM_BUFFER_PERCENT_MAX: u32 = 80; +const MONITOR_INTERVAL_SECOND_MIN: u32 = 5; +const MONITOR_INTERVAL_SECOND_MAX: u32 = 300; + static mut BALLOON_DEV: Option>> = None; /// IO vector, used to find memory segments. @@ -874,6 +881,60 @@ impl EventNotifierHelper for BalloonIoHandler { } } +#[derive(Parser, Debug, Clone, Default)] +#[command(name = "balloon")] +pub struct BalloonConfig { + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, value_parser = parse_bool, action = ArgAction::Append)] + pub multifunction: Option, + #[arg(long, default_value = "false", action = ArgAction::Append)] + deflate_on_oom: bool, + #[arg(long, default_value = "false", action = ArgAction::Append)] + free_page_reporting: bool, + #[arg(long, default_value = "false", action = ArgAction::Append)] + auto_balloon: bool, + #[arg(long, default_value = "50")] + membuf_percent: u32, + #[arg(long, default_value = "10")] + monitor_interval: u32, +} + +impl ConfigCheck for BalloonConfig { + fn check(&self) -> Result<()> { + if !self.auto_balloon { + return Ok(()); + } + if self.membuf_percent > MEM_BUFFER_PERCENT_MAX + || self.membuf_percent < MEM_BUFFER_PERCENT_MIN + { + return Err(anyhow!(ConfigError::IllegalValue( + "balloon membuf-percent".to_string(), + MEM_BUFFER_PERCENT_MIN as u64, + false, + MEM_BUFFER_PERCENT_MAX as u64, + false, + ))); + } + if self.monitor_interval > MONITOR_INTERVAL_SECOND_MAX + || self.monitor_interval < MONITOR_INTERVAL_SECOND_MIN + { + return Err(anyhow!(ConfigError::IllegalValue( + "balloon monitor-interval".to_string(), + MONITOR_INTERVAL_SECOND_MIN as u64, + false, + MONITOR_INTERVAL_SECOND_MAX as u64, + false, + ))); + } + Ok(()) + } +} + /// A balloon device with some necessary information. pub struct Balloon { /// Virtio device base property. @@ -900,7 +961,7 @@ impl Balloon { /// # Arguments /// /// * `bln_cfg` - Balloon configuration. - pub fn new(bln_cfg: &BalloonConfig, mem_space: Arc) -> Balloon { + pub fn new(bln_cfg: BalloonConfig, mem_space: Arc) -> Balloon { let mut queue_num = QUEUE_NUM_BALLOON; if bln_cfg.free_page_reporting { queue_num += 1; @@ -911,7 +972,7 @@ impl Balloon { Balloon { base: VirtioBase::new(VIRTIO_TYPE_BALLOON, queue_num, DEFAULT_VIRTQUEUE_SIZE), - bln_cfg: bln_cfg.clone(), + bln_cfg, actual: Arc::new(AtomicU32::new(0)), num_pages: 0u32, interrupt_cb: None, @@ -996,6 +1057,7 @@ impl VirtioDevice for Balloon { } fn realize(&mut self) -> Result<()> { + self.bln_cfg.check()?; self.mem_space .register_listener(self.mem_info.clone()) .with_context(|| "Failed to register memory listener defined by balloon device.")?; @@ -1231,14 +1293,11 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; let mem_space = address_space_init(); - let mut bln = Balloon::new(&bln_cfg, mem_space); + let mut bln = Balloon::new(bln_cfg, mem_space); // Test realize function. bln.realize().unwrap(); @@ -1282,14 +1341,11 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space); + let balloon = Balloon::new(bln_cfg, mem_space); let ret_data = [0, 0, 0, 0, 1, 0, 0, 0]; let mut read_data: Vec = vec![0; 8]; let addr = 0x00; @@ -1304,14 +1360,11 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space); + let balloon = Balloon::new(bln_cfg, mem_space); let ret_data = [1, 0, 0, 0, 0, 0, 0, 0]; let mut read_data: Vec = vec![0; 8]; let addr = 0x4; @@ -1326,14 +1379,11 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; let mem_space = address_space_init(); - let balloon = Balloon::new(&bln_cfg, mem_space); + let balloon = Balloon::new(bln_cfg, mem_space); let mut read_data: Vec = vec![0; 8]; let addr: u64 = 0xffff_ffff_ffff_ffff; assert_eq!(balloon.get_balloon_memory_size(), 0); @@ -1347,14 +1397,11 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; let mem_space = address_space_init(); - let mut balloon = Balloon::new(&bln_cfg, mem_space); + let mut balloon = Balloon::new(bln_cfg, mem_space); let write_data = [1, 0, 0, 0]; let addr = 0x00; assert_eq!(balloon.get_balloon_memory_size(), 0); @@ -1368,12 +1415,9 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; - let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); + let mut bln = Balloon::new(bln_cfg, mem_space.clone()); bln.realize().unwrap(); let ram_fr1 = create_flat_range(0, MEMORY_SIZE, 0); let blninfo = BlnMemInfo::new(); @@ -1552,12 +1596,9 @@ mod tests { let bln_cfg = BalloonConfig { id: "bln".to_string(), deflate_on_oom: true, - free_page_reporting: Default::default(), - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; - let mut bln = Balloon::new(&bln_cfg, mem_space.clone()); + let mut bln = Balloon::new(bln_cfg, mem_space.clone()); bln.base.queues = queues; assert!(bln.activate(mem_space, interrupt_cb, queue_evts).is_err()); } @@ -1622,12 +1663,10 @@ mod tests { id: "bln".to_string(), deflate_on_oom: true, free_page_reporting: true, - auto_balloon: false, - membuf_percent: 0, - monitor_interval: 0, + ..Default::default() }; let mem_space = address_space_init(); - let mut bln = Balloon::new(&bln_cfg, mem_space); + let mut bln = Balloon::new(bln_cfg, mem_space); // Test realize function. bln.realize().unwrap(); -- Gitee From be6ef86d2a242abeed54898a0996d2e73395ebf9 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 17 Jan 2024 19:09:39 +0800 Subject: [PATCH 1607/2187] usb-camera: use clap to parse the parameters of the usb-camera config Use clap to parse the parameters of the usb-camera config. Signed-off-by: Mingwang Li Signed-off-by: liuxiangdong --- Cargo.lock | 1 + devices/src/camera_backend/mod.rs | 22 +++---- devices/src/usb/camera.rs | 21 +++++-- machine/src/lib.rs | 18 ++++-- machine_manager/Cargo.toml | 1 + machine_manager/src/config/camera.rs | 89 +++++++--------------------- machine_manager/src/config/mod.rs | 10 ++++ machine_manager/src/config/usb.rs | 76 ------------------------ 8 files changed, 73 insertions(+), 165 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd5e5504a..8f4819711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -962,6 +962,7 @@ name = "machine_manager" version = "2.3.0" dependencies = [ "anyhow", + "clap", "hex", "libc", "log", diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index ae49eac31..28e42f5c6 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -25,7 +25,8 @@ use anyhow::{bail, Context, Result}; use self::demo::DemoCameraBackend; #[cfg(feature = "usb_camera_v4l2")] use self::v4l2::V4l2CameraBackend; -use machine_manager::config::{CamBackendType, ConfigError, UsbCameraConfig}; +use crate::usb::camera::UsbCameraConfig; +use machine_manager::config::{CamBackendType, CameraDevConfig}; use util::aio::Iovec; /// Frame interval in 100ns units. @@ -143,21 +144,20 @@ pub trait CameraBackend: Send + Sync { fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } -pub fn create_cam_backend(config: UsbCameraConfig) -> Result>> { - let cam: Arc> = match config.backend { +pub fn create_cam_backend( + config: UsbCameraConfig, + cameradev: CameraDevConfig, +) -> Result>> { + let cam: Arc> = match cameradev.backend { #[cfg(feature = "usb_camera_v4l2")] CamBackendType::V4l2 => Arc::new(Mutex::new(V4l2CameraBackend::new( - config.drive.id.clone().unwrap(), - config.drive.path.clone().with_context(|| { - ConfigError::FieldIsMissing("path".to_string(), "V4L2".to_string()) - })?, + cameradev.id, + cameradev.path, config.iothread, )?)), CamBackendType::Demo => Arc::new(Mutex::new(DemoCameraBackend::new( - config.id.clone().unwrap(), - config.path.with_context(|| { - ConfigError::FieldIsMissing("path".to_string(), "Demo".to_string()) - })?, + config.id, + cameradev.path, )?)), }; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 2c68864e5..50d007d5f 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -19,6 +19,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; +use clap::Parser; use log::{error, info, warn}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -34,7 +35,8 @@ use crate::usb::descriptor::*; use crate::usb::{ UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; -use machine_manager::config::UsbCameraConfig; +use machine_manager::config::camera::CameraDevConfig; +use machine_manager::config::valid_id; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::aio::{iov_discard_front_direct, Iovec}; use util::byte_code::ByteCode; @@ -92,6 +94,17 @@ const MAX_PAYLOAD: u32 = FRAME_SIZE_1280_720; const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; const USB_CAMERA_BUFFER_LEN: usize = 12 * 1024; +#[derive(Parser, Debug, Clone)] +#[command(name = "usb_camera")] +pub struct UsbCameraConfig { + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub iothread: Option, + #[arg(long)] + pub cameradev: String, +} + pub struct UsbCamera { base: UsbDeviceBase, // general usb device object vs_control: VideoStreamingControl, // video stream control info @@ -490,10 +503,10 @@ impl VideoStreamingControl { } impl UsbCamera { - pub fn new(config: UsbCameraConfig) -> Result { - let camera = create_cam_backend(config.clone())?; + pub fn new(config: UsbCameraConfig, cameradev: CameraDevConfig) -> Result { + let camera = create_cam_backend(config.clone(), cameradev)?; Ok(Self { - base: UsbDeviceBase::new(config.id.unwrap(), USB_CAMERA_BUFFER_LEN), + base: UsbDeviceBase::new(config.id, USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), camera_backend: camera, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 97fea5d76..ad483df7e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -55,7 +55,7 @@ use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] -use devices::usb::camera::UsbCamera; +use devices::usb::camera::{UsbCamera, UsbCameraConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::UsbHost; use devices::usb::{ @@ -66,14 +66,14 @@ use devices::usb::{ use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; use hypervisor::{kvm::KvmHypervisor, HypervisorOps}; +#[cfg(feature = "usb_camera")] +use machine_manager::config::get_cameradev_by_id; #[cfg(feature = "demo_device")] use machine_manager::config::parse_demo_dev; #[cfg(feature = "virtio_gpu")] use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] use machine_manager::config::parse_pvpanic; -#[cfg(feature = "usb_camera")] -use machine_manager::config::parse_usb_camera; #[cfg(feature = "usb_host")] use machine_manager::config::parse_usb_host; use machine_manager::config::{ @@ -1722,8 +1722,16 @@ pub trait MachineOps { } #[cfg(feature = "usb_camera")] "usb-camera" => { - let device_cfg = parse_usb_camera(vm_config, cfg_args)?; - let camera = UsbCamera::new(device_cfg)?; + let config = UsbCameraConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let cameradev = get_cameradev_by_id(vm_config, config.cameradev.clone()) + .with_context(|| { + format!( + "no cameradev found with id {:?} for usb-camera", + config.cameradev + ) + })?; + + let camera = UsbCamera::new(config, cameradev)?; camera .realize() .with_context(|| "Failed to realize usb camera device")? diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 32c49c20a..8988da2dd 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -22,6 +22,7 @@ thiserror = "1.0" anyhow = "1.0" trace = { path = "../trace" } util = { path = "../util" } +clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } [features] default = [] diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index 17c9da6c1..63353f8b3 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -10,22 +10,25 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::path::Path; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use serde::{Deserialize, Serialize}; -use super::error::ConfigError; use crate::{ - config::{check_arg_nonexist, check_id, CmdParser, ConfigCheck, VmConfig}, + config::{existed_path, str_slip_to_clap, valid_id, VmConfig}, qmp::qmp_schema, }; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[command(name = "camera device")] pub struct CameraDevConfig { - pub id: Option, - pub path: Option, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long, value_parser = existed_path)] + pub path: String, + #[arg(long)] pub backend: CamBackendType, } @@ -49,41 +52,17 @@ impl FromStr for CamBackendType { } } -impl CameraDevConfig { - pub fn new() -> CameraDevConfig { - CameraDevConfig { - id: None, - path: None, - backend: CamBackendType::Demo, - } - } -} - -impl Default for CameraDevConfig { - fn default() -> Self { - Self::new() - } -} - impl VmConfig { pub fn add_camera_backend(&mut self, camera_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("cameradev"); - cmd_parser.push("").push("id").push("path"); - cmd_parser.get_parameters(camera_config)?; - - let mut camera_backend = CameraDevConfig::new(); - camera_backend.backend = cmd_parser.get_value::("")?.unwrap(); - camera_backend.id = cmd_parser.get_value::("id")?; - camera_backend.path = cmd_parser.get_value::("path")?; - - camera_backend.check()?; + let cfg = format!("cameradev,backend={}", camera_config); + let config = CameraDevConfig::try_parse_from(str_slip_to_clap(&cfg))?; - self.add_cameradev_with_config(camera_backend) + self.add_cameradev_with_config(config) } fn camera_backend_repeated(&self, id: &str, path: &str, backend: CamBackendType) -> bool { for (key, cam) in self.camera_backend.iter() { - if key != id && cam.backend == backend && cam.path == Some(path.to_string()) { + if key != id && cam.backend == backend && cam.path == *path { return true; } } @@ -92,27 +71,17 @@ impl VmConfig { } pub fn add_cameradev_with_config(&mut self, conf: CameraDevConfig) -> Result<()> { - let cameradev_id = conf - .id - .clone() - .with_context(|| "no id configured for cameradev")?; - let cameradev_path = conf - .path - .clone() - .with_context(|| "no path configured for cameradev")?; - let cameradev_backend = conf.backend; - - let cam = self.camera_backend.get(&cameradev_id); + let cam = self.camera_backend.get(&conf.id); if cam.is_some() { - bail!("cameradev with id {:?} has already existed", cameradev_id); + bail!("cameradev with id {:?} has already existed", conf.id); } - if self.camera_backend_repeated(&cameradev_id, &cameradev_path, cameradev_backend) { + if self.camera_backend_repeated(&conf.id, &conf.path, conf.backend) { bail!("another cameradev has the same backend device"); } - self.camera_backend.insert(cameradev_id, conf); + self.camera_backend.insert(conf.id.clone(), conf); Ok(()) } @@ -127,29 +96,11 @@ impl VmConfig { } } -impl ConfigCheck for CameraDevConfig { - fn check(&self) -> Result<()> { - // Note: backend has already been checked during args parsing. - check_id(self.id.clone(), "cameradev")?; - check_camera_path(self.path.clone()) - } -} - -fn check_camera_path(path: Option) -> Result<()> { - check_arg_nonexist(path.clone(), "path", "cameradev")?; - - let path = path.unwrap(); - if !Path::new(&path).exists() { - bail!(ConfigError::FileNotExist(path)); - } - - Ok(()) -} - pub fn get_cameradev_config(args: qmp_schema::CameraDevAddArgument) -> Result { + let path = args.path.with_context(|| "cameradev config path is null")?; let config = CameraDevConfig { - id: Some(args.id), - path: args.path, + id: args.id, + path, backend: CamBackendType::from_str(&args.driver)?, }; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ae2fbc28f..1ef912d23 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -86,6 +86,7 @@ pub use vnc::*; use std::collections::HashMap; use std::fs::File; use std::io::Read; +use std::path::Path; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; @@ -755,6 +756,15 @@ pub fn valid_id(id: &str) -> Result { Ok(id.to_string()) } +pub fn existed_path(path: &str) -> Result { + let filepath = path.to_string(); + if !Path::new(path).exists() { + bail!(ConfigError::FileNotExist(filepath)); + } + + Ok(filepath) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 4142f8fae..6aed70c9c 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -13,15 +13,11 @@ use anyhow::{anyhow, bail, Context, Result}; use super::error::ConfigError; -#[cfg(feature = "usb_camera")] -use super::get_cameradev_by_id; #[cfg(feature = "usb_host")] use super::UnsignedInteger; use crate::config::{ check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, }; -#[cfg(feature = "usb_camera")] -use crate::config::{CamBackendType, CameraDevConfig}; use util::aio::AioEngine; #[cfg(feature = "usb_host")] @@ -158,37 +154,6 @@ pub fn parse_usb_tablet(conf: &str) -> Result { Ok(dev) } -#[cfg(feature = "usb_camera")] -pub fn parse_usb_camera(vm_config: &mut VmConfig, conf: &str) -> Result { - let mut cmd_parser = CmdParser::new("usb-camera"); - cmd_parser - .push("") - .push("id") - .push("cameradev") - .push("iothread"); - cmd_parser.parse(conf)?; - - let mut dev = UsbCameraConfig::new(); - let drive = cmd_parser - .get_value::("cameradev") - .with_context(|| "`cameradev` is missing for usb-camera")?; - let cameradev = get_cameradev_by_id(vm_config, drive.clone().unwrap()).with_context(|| { - format!( - "no cameradev found with id {:?} for usb-camera", - drive.unwrap() - ) - })?; - - dev.id = cmd_parser.get_value::("id")?; - dev.backend = cameradev.backend; - dev.path = cameradev.path.clone(); - dev.drive = cameradev; - dev.iothread = cmd_parser.get_value::("iothread")?; - - dev.check()?; - Ok(dev) -} - pub fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; check_arg_too_long(&id.unwrap(), "id")?; @@ -196,47 +161,6 @@ pub fn check_id(id: Option, device: &str) -> Result<()> { Ok(()) } -#[cfg(feature = "usb_camera")] -#[derive(Clone, Debug)] -pub struct UsbCameraConfig { - pub id: Option, - pub backend: CamBackendType, - pub path: Option, - pub iothread: Option, - pub drive: CameraDevConfig, -} - -#[cfg(feature = "usb_camera")] -impl UsbCameraConfig { - pub fn new() -> Self { - UsbCameraConfig { - id: None, - backend: CamBackendType::Demo, - path: None, - iothread: None, - drive: CameraDevConfig::new(), - } - } -} - -#[cfg(feature = "usb_camera")] -impl Default for UsbCameraConfig { - fn default() -> Self { - Self::new() - } -} - -#[cfg(feature = "usb_camera")] -impl ConfigCheck for UsbCameraConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-camera")?; - if self.iothread.is_some() { - check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; - } - Ok(()) - } -} - #[derive(Clone, Debug)] pub struct UsbStorageConfig { /// USB Storage device id. -- Gitee From c02b9dc665384deb832a5841be5dea5fbe246e68 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jan 2024 12:30:35 +0800 Subject: [PATCH 1608/2187] num_ops: change str_to_usize to str_to_num The str_to_num can be converted based on the specific type to avoid type mismatch after conversion. Signed-off-by: Mingwang Li Signed-off-by: liuxiangdong --- machine/src/micro_common/mod.rs | 4 +-- machine_manager/src/config/mod.rs | 6 ++-- machine_manager/src/config/pci.rs | 9 +++--- util/src/num_ops.rs | 53 +++++++++++++++++++++---------- virtio/src/device/net.rs | 4 +-- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 2defa1809..b97f046ca 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -62,7 +62,7 @@ use machine_manager::qmp::{ }; use migration::MigrationManager; use util::aio::WriteZeroesState; -use util::{loop_context::EventLoopManager, num_ops::str_to_usize, set_termi_canon_mode}; +use util::{loop_context::EventLoopManager, num_ops::str_to_num, set_termi_canon_mode}; use virtio::{ create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, @@ -669,7 +669,7 @@ impl DeviceInterface for LightMachine { // get slot of bus by addr or lun let mut slot = 0; if let Some(addr) = args.addr { - if let Ok(num) = str_to_usize(addr) { + if let Ok(num) = str_to_num::(&addr) { slot = num; } else { return Response::create_error_response( diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 1ef912d23..66eb53436 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -98,7 +98,7 @@ use trace::set_state_by_pattern; use util::device_tree::{self, FdtBuilder}; use util::{ file::{get_file_alignment, open_file}, - num_ops::str_to_usize, + num_ops::str_to_num, test_helper::is_test_enabled, AsAny, }; @@ -665,8 +665,8 @@ impl FromStr for UnsignedInteger { type Err = (); fn from_str(s: &str) -> std::result::Result { - let value = str_to_usize(s.to_string()) - .map_err(|e| error!("Invalid value {}, error is {:?}", s, e))?; + let value = + str_to_num::(s).map_err(|e| error!("Invalid value {}, error is {:?}", s, e))?; Ok(UnsignedInteger(value)) } } diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index b580e2402..522b4a8d8 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize}; use super::error::ConfigError; use super::{CmdParser, ConfigCheck, UnsignedInteger}; use crate::config::{check_arg_too_long, ExBool}; -use util::num_ops::str_to_usize; +use util::num_ops::str_to_num; /// Basic information of pci devices such as bus number, /// slot number and function number. @@ -77,15 +77,14 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { } let slot = addr_vec.first().unwrap(); - let slot = - str_to_usize(slot.to_string()).with_context(|| format!("Invalid slot num: {}", slot))?; + let slot = str_to_num::(*slot).with_context(|| format!("Invalid slot num: {}", slot))?; if slot > 31 { bail!("Invalid slot num: {}", slot); } let func = if addr_vec.get(1).is_some() { let function = addr_vec.get(1).unwrap(); - str_to_usize(function.to_string()) + str_to_num::(*function) .with_context(|| format!("Invalid function num: {}", function))? } else { 0 @@ -94,7 +93,7 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { bail!("Invalid function num: {}", func); } - Ok((slot as u8, func as u8)) + Ok((slot, func)) } pub fn get_pci_bdf(pci_cfg: &str) -> Result { diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 9db94e5d0..2be535ae5 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::num::ParseIntError; + use anyhow::{Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::error; @@ -409,7 +411,27 @@ pub fn read_data_u16(data: &[u8], value: &mut u16) -> bool { true } -/// Parse a string to a number, decimal and heximal numbers supported now. +pub trait Num { + type ParseIntError; + fn from_str_radix(s: &str, radix: u32) -> Result + where + Self: Sized; +} + +macro_rules! int_trait_impl { + ($name:ident for $($t:ty)*) => ($( + impl $name for $t { + type ParseIntError = ::core::num::ParseIntError; + fn from_str_radix(s: &str, radix: u32) -> Result { + <$t>::from_str_radix(s, radix) + } + } + )*) +} + +int_trait_impl!(Num for u8 u16 usize); + +/// Parse a string to a number, decimal and hexadecimal numbers supported now. /// /// # Arguments /// @@ -419,26 +441,23 @@ pub fn read_data_u16(data: &[u8], value: &mut u16) -> bool { /// /// ```rust /// extern crate util; -/// use util::num_ops::str_to_usize; +/// use util::num_ops::str_to_num; /// -/// let value = str_to_usize("0x17".to_string()).unwrap(); +/// let value = str_to_num::("0x17").unwrap(); /// assert!(value == 0x17); -/// let value = str_to_usize("0X17".to_string()).unwrap(); +/// let value = str_to_num::("0X17").unwrap(); /// assert!(value == 0x17); -/// let value = str_to_usize("17".to_string()).unwrap(); +/// let value = str_to_num::("17").unwrap(); /// assert!(value == 17); /// ``` -pub fn str_to_usize(string_in: String) -> Result { +pub fn str_to_num(s: &str) -> Result { let mut base = 10; - if string_in.starts_with("0x") || string_in.starts_with("0X") { + if s.starts_with("0x") || s.starts_with("0X") { base = 16; } - let without_prefix = string_in - .trim() - .trim_start_matches("0x") - .trim_start_matches("0X"); - let num = usize::from_str_radix(without_prefix, base) - .with_context(|| format!("Invalid num: {}", string_in))?; + let without_prefix = s.trim().trim_start_matches("0x").trim_start_matches("0X"); + let num = + T::from_str_radix(without_prefix, base).with_context(|| format!("Invalid num: {}", s))?; Ok(num) } @@ -639,12 +658,12 @@ mod test { } #[test] - fn test_str_to_usize() { - let value = str_to_usize("0x17".to_string()).unwrap(); + fn test_str_to_num() { + let value = str_to_num::("0x17").unwrap(); assert!(value == 0x17); - let value = str_to_usize("0X17".to_string()).unwrap(); + let value = str_to_num::("0X17").unwrap(); assert!(value == 0x17); - let value = str_to_usize("17".to_string()).unwrap(); + let value = str_to_num::("17").unwrap(); assert!(value == 17); } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index c815e88de..14bce656f 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -59,7 +59,7 @@ use util::loop_context::gen_delete_notifiers; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::num_ops::str_to_usize; +use util::num_ops::str_to_num; use util::tap::{ Tap, IFF_MULTI_QUEUE, TUN_F_CSUM, TUN_F_TSO4, TUN_F_TSO6, TUN_F_TSO_ECN, TUN_F_UFO, }; @@ -1286,7 +1286,7 @@ fn check_mq(dev_name: &str, queue_pair: u16) -> Result<()> { let is_mq = queue_pair > 1; let ifr_flag = fs::read_to_string(tap_path) .with_context(|| "Failed to read content from tun_flags file")?; - let flags = str_to_usize(ifr_flag)? as u16; + let flags = str_to_num::(&ifr_flag)?; if (flags & IFF_MULTI_QUEUE != 0) && !is_mq { bail!(format!( "Tap device supports mq, but command set queue pairs {}.", -- Gitee From 8a2e776d5bb1e1cd7f80bedd40d5dd175518dd4b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jan 2024 14:50:07 +0800 Subject: [PATCH 1609/2187] usb-host: use clap to parse the parameters of the usb-host config Use clap to parse the parameters of the usb-host config. Signed-off-by: Mingwang Li Signed-off-by: liuxiangdong --- devices/src/usb/usbhost/mod.rs | 28 ++++++++++- machine/src/lib.rs | 8 ++-- machine_manager/src/config/usb.rs | 79 ------------------------------- 3 files changed, 29 insertions(+), 86 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 0622d5176..f91de9427 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -21,6 +21,7 @@ use std::{ }; use anyhow::{anyhow, bail, Result}; +use clap::Parser; use libc::c_int; use libusb1_sys::{ libusb_get_iso_packet_buffer_simple, libusb_set_iso_packet_lengths, libusb_transfer, @@ -45,7 +46,7 @@ use crate::usb::{ }; use host_usblib::*; use machine_manager::{ - config::UsbHostConfig, + config::valid_id, event_loop::{register_event_helper, unregister_event_helper}, temp_cleaner::{ExitNotifier, TempCleaner}, }; @@ -53,11 +54,13 @@ use util::{ byte_code::ByteCode, link_list::{List, Node}, loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}, + num_ops::str_to_num, }; const NON_ISO_PACKETS_NUMS: c_int = 0; const HANDLE_TIMEOUT_MS: u64 = 2; const USB_HOST_BUFFER_LEN: usize = 12 * 1024; +const USBHOST_ADDR_MAX: i64 = 127; #[derive(Default, Copy, Clone)] struct InterfaceStatus { @@ -336,6 +339,27 @@ impl IsoQueue { } } +#[derive(Parser, Clone, Debug, Default)] +#[command(name = "usb_host")] +pub struct UsbHostConfig { + #[arg(long, value_parser = valid_id)] + id: String, + #[arg(long, default_value = "0")] + hostbus: u8, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u8).range(..=USBHOST_ADDR_MAX))] + hostaddr: u8, + #[arg(long)] + hostport: Option, + #[arg(long, default_value = "0", value_parser = str_to_num::)] + vendorid: u16, + #[arg(long, default_value = "0", value_parser = str_to_num::)] + productid: u16, + #[arg(long = "isobsize", default_value = "32")] + iso_urb_frames: u32, + #[arg(long = "isobufs", default_value = "4")] + iso_urb_count: u32, +} + /// Abstract object of the host USB device. pub struct UsbHost { base: UsbDeviceBase, @@ -375,7 +399,7 @@ impl UsbHost { context.set_log_level(rusb::LogLevel::None); let iso_urb_frames = config.iso_urb_frames; let iso_urb_count = config.iso_urb_count; - let id = config.id.clone().unwrap(); + let id = config.id.clone(); Ok(Self { config, context, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index ad483df7e..c49a43d0f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -57,7 +57,7 @@ use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; #[cfg(feature = "usb_host")] -use devices::usb::usbhost::UsbHost; +use devices::usb::usbhost::{UsbHost, UsbHostConfig}; use devices::usb::{ keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, UsbDevice, @@ -74,8 +74,6 @@ use machine_manager::config::parse_demo_dev; use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] use machine_manager::config::parse_pvpanic; -#[cfg(feature = "usb_host")] -use machine_manager::config::parse_usb_host; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, @@ -1745,8 +1743,8 @@ pub trait MachineOps { } #[cfg(feature = "usb_host")] "usb-host" => { - let device_cfg = parse_usb_host(cfg_args)?; - let usbhost = UsbHost::new(device_cfg)?; + let config = UsbHostConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let usbhost = UsbHost::new(config)?; usbhost .realize() .with_context(|| "Failed to realize usb host device")? diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 6aed70c9c..c04ab22b1 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -13,16 +13,11 @@ use anyhow::{anyhow, bail, Context, Result}; use super::error::ConfigError; -#[cfg(feature = "usb_host")] -use super::UnsignedInteger; use crate::config::{ check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, }; use util::aio::AioEngine; -#[cfg(feature = "usb_host")] -const USBHOST_ADDR_MAX: u8 = 127; - /// XHCI controller configuration. #[derive(Debug)] pub struct XhciConfig { @@ -233,77 +228,3 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result dev.check()?; Ok(dev) } - -#[derive(Clone, Debug, Default)] -#[cfg(feature = "usb_host")] -pub struct UsbHostConfig { - /// USB Host device id. - pub id: Option, - /// The bus number of the USB Host device. - pub hostbus: u8, - /// The addr number of the USB Host device. - pub hostaddr: u8, - /// The physical port number of the USB host device. - pub hostport: Option, - /// The vendor id of the USB Host device. - pub vendorid: u16, - /// The product id of the USB Host device. - pub productid: u16, - pub iso_urb_frames: u32, - pub iso_urb_count: u32, -} - -#[cfg(feature = "usb_host")] -impl UsbHostConfig { - fn check_range(&self) -> Result<()> { - if self.hostaddr > USBHOST_ADDR_MAX { - bail!("USB Host hostaddr out of range"); - } - Ok(()) - } -} - -#[cfg(feature = "usb_host")] -impl ConfigCheck for UsbHostConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-host")?; - self.check_range() - } -} - -#[cfg(feature = "usb_host")] -pub fn parse_usb_host(cfg_args: &str) -> Result { - let mut cmd_parser = CmdParser::new("usb-host"); - cmd_parser - .push("") - .push("id") - .push("hostbus") - .push("hostaddr") - .push("hostport") - .push("vendorid") - .push("productid") - .push("isobsize") - .push("isobufs"); - - cmd_parser.parse(cfg_args)?; - - let dev = UsbHostConfig { - id: cmd_parser.get_value::("id")?, - hostbus: cmd_parser.get_value::("hostbus")?.unwrap_or(0), - hostaddr: cmd_parser.get_value::("hostaddr")?.unwrap_or(0), - hostport: cmd_parser.get_value::("hostport")?, - vendorid: cmd_parser - .get_value::("vendorid")? - .unwrap_or(UnsignedInteger(0)) - .0 as u16, - productid: cmd_parser - .get_value::("productid")? - .unwrap_or(UnsignedInteger(0)) - .0 as u16, - iso_urb_frames: cmd_parser.get_value::("isobsize")?.unwrap_or(32), - iso_urb_count: cmd_parser.get_value::("isobufs")?.unwrap_or(4), - }; - - dev.check()?; - Ok(dev) -} -- Gitee From 3f5cc042c0bdc0bbd0866d6a83db5edd8ec84bcd Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 19 Feb 2024 10:22:17 +0800 Subject: [PATCH 1610/2187] trace: add trace point for block_backend block_backend trace point: file_driver_func block_read_vectored block_write_vectored block_write_zeroes block_discard qcow2_flush qcow2_process_discards Signed-off-by: Yan Wang --- Cargo.lock | 1 + block_backend/Cargo.toml | 1 + block_backend/src/file.rs | 2 +- block_backend/src/qcow2/mod.rs | 9 +++++ block_backend/src/raw.rs | 7 ++++ trace/trace_event/block_backend.toml | 53 ++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 trace/trace_event/block_backend.toml diff --git a/Cargo.lock b/Cargo.lock index 0b1ce7371..de8764d9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,6 +162,7 @@ dependencies = [ "machine_manager", "once_cell", "thiserror", + "trace", "util", "vmm-sys-util", ] diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index ef4e9509e..2eeeeae81 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -15,3 +15,4 @@ once_cell = "1.18.0" libc = "0.2" machine_manager = { path = "../machine_manager" } util = { path = "../util" } +trace = {path = "../trace"} diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 0d6e9dd3e..1b1e7f005 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -56,7 +56,7 @@ pub struct FileDriver { aio: Rc>>, pub incomplete: Arc, delete_evts: Vec, - block_prop: BlockProperty, + pub block_prop: BlockProperty, } impl FileDriver { diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 1f2170e4b..fe86be3b5 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -306,6 +306,7 @@ impl Qcow2Driver { } pub fn flush(&mut self) -> Result<()> { + trace::qcow2_flush(&self.driver.block_prop.id); self.table.flush()?; self.refcount.flush() } @@ -1478,6 +1479,7 @@ impl Qcow2Driver { } fn process_discards(&mut self, completecb: T, opcode: OpCode, unmap: bool) -> Result<()> { + trace::qcow2_process_discards(&self.driver.block_prop.id, &opcode, unmap); let mut req_list = Vec::new(); for task in self.refcount.discard_list.iter() { req_list.push(CombineRequest { @@ -1625,6 +1627,7 @@ impl BlockDriverOps for Qcow2Driver { let nbytes = get_iov_size(&iovec); self.check_request(offset, nbytes) .with_context(|| " Invalid read request")?; + trace::block_read_vectored(&self.driver.block_prop.id, offset, nbytes); let mut left = iovec; let mut req_list: Vec = Vec::new(); @@ -1658,6 +1661,7 @@ impl BlockDriverOps for Qcow2Driver { let nbytes = get_iov_size(&iovec); self.check_request(offset, nbytes) .with_context(|| " Invalid write request")?; + trace::block_write_vectored(&self.driver.block_prop.id, offset, nbytes); let mut req_list: Vec = Vec::new(); let mut copied = 0; @@ -1695,6 +1699,7 @@ impl BlockDriverOps for Qcow2Driver { } fn datasync(&mut self, completecb: T) -> Result<()> { + trace::block_datasync(&self.driver.block_prop.id); self.flush() .unwrap_or_else(|e| error!("Flush failed when syncing data, {:?}", e)); self.driver.datasync(completecb) @@ -1705,6 +1710,7 @@ impl BlockDriverOps for Qcow2Driver { } fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { + trace::block_discard(&self.driver.block_prop.id, offset, nbytes); // Align to cluster_size. let file_size = self.header.size; let align_size = self.header.cluster_size(); @@ -1733,6 +1739,7 @@ impl BlockDriverOps for Qcow2Driver { completecb: T, unmap: bool, ) -> Result<()> { + trace::block_write_zeroes(&self.driver.block_prop.id, offset, nbytes, unmap); let file_size = self.header.size; let align_size = self.header.cluster_size(); let mut offset_start = std::cmp::min(offset as u64, file_size); @@ -1776,10 +1783,12 @@ impl BlockDriverOps for Qcow2Driver { } fn flush_request(&mut self) -> Result<()> { + trace::block_flush_request(&self.driver.block_prop.id); self.driver.flush_request() } fn drain_request(&self) { + trace::block_drain_request(&self.driver.block_prop.id); self.driver.drain_request(); } diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 12e7a4ffc..1231c1b91 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -60,6 +60,7 @@ impl BlockDriverOps for RawDriver { fn read_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { let nbytes = get_iov_size(&iovec); + trace::block_read_vectored(&self.driver.block_prop.id, offset, nbytes); self.driver.read_vectored( vec![CombineRequest::new(iovec, offset as u64, nbytes)], completecb, @@ -68,6 +69,7 @@ impl BlockDriverOps for RawDriver { fn write_vectored(&mut self, iovec: Vec, offset: usize, completecb: T) -> Result<()> { let nbytes = get_iov_size(&iovec); + trace::block_write_vectored(&self.driver.block_prop.id, offset, nbytes); self.driver.write_vectored( vec![CombineRequest::new(iovec, offset as u64, nbytes)], completecb, @@ -81,6 +83,7 @@ impl BlockDriverOps for RawDriver { completecb: T, unmap: bool, ) -> Result<()> { + trace::block_write_zeroes(&self.driver.block_prop.id, offset, nbytes, unmap); self.driver.write_zeroes( vec![CombineRequest::new(Vec::new(), offset as u64, nbytes)], completecb, @@ -89,6 +92,7 @@ impl BlockDriverOps for RawDriver { } fn discard(&mut self, offset: usize, nbytes: u64, completecb: T) -> Result<()> { + trace::block_discard(&self.driver.block_prop.id, offset, nbytes); self.driver.discard( vec![CombineRequest::new(Vec::new(), offset as u64, nbytes)], completecb, @@ -96,14 +100,17 @@ impl BlockDriverOps for RawDriver { } fn datasync(&mut self, completecb: T) -> Result<()> { + trace::block_datasync(&self.driver.block_prop.id); self.driver.datasync(completecb) } fn flush_request(&mut self) -> Result<()> { + trace::block_flush_request(&self.driver.block_prop.id); self.driver.flush_request() } fn drain_request(&self) { + trace::block_drain_request(&self.driver.block_prop.id); self.driver.drain_request(); } diff --git a/trace/trace_event/block_backend.toml b/trace/trace_event/block_backend.toml new file mode 100644 index 000000000..43bb8d5a3 --- /dev/null +++ b/trace/trace_event/block_backend.toml @@ -0,0 +1,53 @@ +[[events]] +name = "block_read_vectored" +args = "id: &str, offset: usize, nbytes: u64" +message = "read vectored for device \"{}\", offset {}, nbytes {}" +enabled = true + +[[events]] +name = "block_write_vectored" +args = "id: &str, offset: usize, nbytes: u64" +message = "write vectored for device \"{}\", offset {}, nbytes {}" +enabled = true + +[[events]] +name = "block_write_zeroes" +args = "id: &str, offset: usize, nbytes: u64, unmap: bool" +message = "write zeroes for device \"{}\", offset {}, nbytes {}, unmap {}" +enabled = true + +[[events]] +name = "block_discard" +args = "id: &str, offset: usize, nbytes: u64" +message = "discard for device \"{}\", offset {}, nbytes {}" +enabled = true + +[[events]] +name = "block_datasync" +args = "id: &str" +message = "datasync for device \"{}\"" +enabled = true + +[[events]] +name = "block_flush_request" +args = "id: &str" +message = "flush request for device \"{}\"" +enabled = true + +[[events]] +name = "block_drain_request" +args = "id: &str" +message = "drain request for device \"{}\"" +enabled = true + +[[events]] +name = "qcow2_flush" +args = "id: &str" +message = "qcow2 flush cache data for device \"{}\"" +enabled = true + +[[events]] +name = "qcow2_process_discards" +args = "id: &str, opcode: &dyn fmt::Debug, unmap: bool" +message = "qcow2 process discard for device \"{}\", opcode {:?}, unmap {}" +enabled = true -- Gitee From f7e9db1e4389c1ad34d8e2548e79e04c27f15548 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 19 Feb 2024 10:23:39 +0800 Subject: [PATCH 1611/2187] trace: add trace point for aio aio trace point: aio_submit_request Signed-off-by: Yan Wang --- Cargo.lock | 1 + trace/trace_event/aio.toml | 5 +++++ util/Cargo.toml | 1 + util/src/aio/mod.rs | 1 + 4 files changed, 8 insertions(+) create mode 100644 trace/trace_event/aio.toml diff --git a/Cargo.lock b/Cargo.lock index de8764d9c..ca40cb414 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1766,6 +1766,7 @@ dependencies = [ "once_cell", "serde", "thiserror", + "trace", "v4l2-sys-mit", "vmm-sys-util", ] diff --git a/trace/trace_event/aio.toml b/trace/trace_event/aio.toml new file mode 100644 index 000000000..0cb7d2eb3 --- /dev/null +++ b/trace/trace_event/aio.toml @@ -0,0 +1,5 @@ +[[events]] +name = "aio_submit_request" +args = "fd: RawFd, opcode: &dyn fmt::Debug, offset: usize, nbytes: u64" +message = "fd: {}, opcode {:?}, offset {}, nbytes {}" +enabled = true diff --git a/util/Cargo.toml b/util/Cargo.toml index 10471204c..d71bf5c30 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -21,6 +21,7 @@ once_cell = "1.18.0" io-uring = "0.6.0" serde = { version = "1.0", features = ["derive"] } v4l2-sys-mit = { version = "0.3.0", optional = true } +trace = {path = "../trace"} [features] default = [] diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index c68f2b1bb..d8d733da3 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -543,6 +543,7 @@ impl Aio { } pub fn submit_request(&mut self, mut cb: AioCb) -> Result<()> { + trace::aio_submit_request(cb.file_fd, &cb.opcode, cb.offset, cb.nbytes); if self.ctx.is_none() { return self.handle_sync_request(cb); } -- Gitee From 46da74f5991f2b255a0f005bd96105fc3620d67e Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 19 Feb 2024 10:24:43 +0800 Subject: [PATCH 1612/2187] trace: add trace point for virtio/virtio-net virtio/virtio-net trace point: virtio_net_handle_ctrl virtqueue_pop_avail virtqueue_add_used virtqueue_set_avail_event virtio_tpt_common virtio_tpt_read_common_config virtio_tpt_write_common_config virtio_tpt_read_config virtio_tpt_write_config Signed-off-by: Yan Wang --- trace/src/lib.rs | 1 + trace/trace_event/virtio.toml | 54 +++++++++++++++++++++++++++++ virtio/src/device/net.rs | 2 ++ virtio/src/queue/split.rs | 8 +++++ virtio/src/transport/virtio_mmio.rs | 5 +++ virtio/src/transport/virtio_pci.rs | 8 +++++ 6 files changed, 78 insertions(+) diff --git a/trace/src/lib.rs b/trace/src/lib.rs index 786f82ddc..7aceb1d5f 100644 --- a/trace/src/lib.rs +++ b/trace/src/lib.rs @@ -15,6 +15,7 @@ pub(crate) mod ftrace; use std::{ fmt, + os::unix::io::RawFd, sync::atomic::{AtomicBool, Ordering}, }; #[cfg(feature = "trace_to_ftrace")] diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index efe78b987..5d172a0f7 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -153,3 +153,57 @@ name = "virtio_gpu_console_hw_update" args = "con: usize, w: i32, h: i32" message = "console {} receive hw update request, update size {} {}." enabled = true + +[[events]] +name = "virtio_net_handle_ctrl" +args = "class: u8, cmd: u8" +message = "class {}, cmd {}" +enabled = true + +[[events]] +name = "virtqueue_pop_avail" +args = "vring: u64, in_num: usize, out_num: usize" +message = "virtqueue {:#X} pop avail elem, in_iov length {}, out_iov length {}" +enabled = true + +[[events]] +name = "virtqueue_add_used" +args = "vring: u64, next_used: u64, index: u16, len: u32" +message = "virtqueue {:#X} add used elem, used index {}, desc index {}, len {}" +enabled = true + +[[events]] +name = "virtqueue_set_avail_event" +args = "vring: u64, event_idx: u16" +message = "virtqueue {:#X} set avail event idx {}" +enabled = true + +[[events]] +name = "virtio_tpt_common" +args = "func: &str, id: &str" +message = "{} for device {}" +enabled = true + +[[events]] +name = "virtio_tpt_read_common_config" +args = "id: &str, offset: u64" +message = "read common config for {}, offset is {:#X}" +enabled = true + +[[events]] +name = "virtio_tpt_write_common_config" +args = "id: &str, offset: u64, value: u32" +message = "write common config for {}, offset is {:#X}, value is {:#X}" +enabled = true + +[[events]] +name = "virtio_tpt_read_config" +args = "id: &str, offset: u64, len: usize" +message = "read config for {}, offset is {:#X}, len is {}" +enabled = true + +[[events]] +name = "virtio_tpt_write_config" +args = "id: &str, offset: u64, data: &[u8]" +message = "write config for {}, offset is {:#X}, data is {:X?}" +enabled = true diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index c815e88de..53d50b4bc 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -565,6 +565,7 @@ impl NetCtrlHandler { ) .with_context(|| "Failed to get control header")?; + trace::virtio_net_handle_ctrl(ctrl_hdr.class, ctrl_hdr.cmd); match ctrl_hdr.class { VIRTIO_NET_CTRL_RX => { ack = self @@ -629,6 +630,7 @@ impl NetCtrlHandler { .with_context(|| { VirtioError::InterruptTrigger("ctrl", VirtioInterruptType::Vring) })?; + trace::virtqueue_send_interrupt("Net", &*locked_queue as *const _ as u64); } } diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 8e6354854..31b2fe306 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -556,6 +556,7 @@ impl SplitVring { /// Set the avail idx to the field of the event index for the available ring. fn set_avail_event(&self, sys_mem: &Arc, event_idx: u16) -> Result<()> { + trace::virtqueue_set_avail_event(self as *const _ as u64, event_idx); let avail_event_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(self.actual_size()); @@ -828,6 +829,12 @@ impl VringOps for SplitVring { self.get_vring_element(sys_mem, features, &mut element) .with_context(|| "Failed to get vring element")?; + trace::virtqueue_pop_avail( + &*self as *const _ as u64, + element.in_iovec.len(), + element.out_iovec.len(), + ); + Ok(element) } @@ -841,6 +848,7 @@ impl VringOps for SplitVring { } let next_used = u64::from(self.next_used.0 % self.actual_size()); + trace::virtqueue_add_used(&*self as *const _ as u64, next_used, index, len); let used_elem_addr = self.addr_cache.used_ring_host + VRING_FLAGS_AND_IDX_LEN + next_used * USEDELEM_LEN; let used_elem = UsedElem { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index b276ae25c..260c0dee1 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -188,6 +188,7 @@ impl VirtioMmioDevice { /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate(&mut self) -> Result<()> { + trace::virtio_tpt_common("activate", &self.base.base.id); let mut locked_dev = self.device.lock().unwrap(); let queue_num = locked_dev.queue_num(); let queue_type = locked_dev.queue_type(); @@ -271,6 +272,7 @@ impl VirtioMmioDevice { /// /// * `offset` - The offset of common config. fn read_common_config(&mut self, offset: u64) -> Result { + trace::virtio_tpt_read_common_config(&self.base.base.id, offset); let locked_device = self.device.lock().unwrap(); let value = match offset { MAGIC_VALUE_REG => MMIO_MAGIC_VALUE, @@ -316,6 +318,7 @@ impl VirtioMmioDevice { /// /// Returns Error if the offset is out of bound. fn write_common_config(&mut self, offset: u64, value: u32) -> Result<()> { + trace::virtio_tpt_write_common_config(&self.base.base.id, offset, value); let mut locked_device = self.device.lock().unwrap(); match offset { DEVICE_FEATURES_SEL_REG => locked_device.set_hfeatures_sel(value), @@ -399,6 +402,7 @@ impl SysBusDevOps for VirtioMmioDevice { /// Read data by virtio driver from VM. fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { + trace::virtio_tpt_read_config(&self.base.base.id, offset, data.len()); match offset { 0x00..=0xff if data.len() == 4 => { let value = match self.read_common_config(offset) { @@ -444,6 +448,7 @@ impl SysBusDevOps for VirtioMmioDevice { /// Write data by virtio driver from VM. fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { + trace::virtio_tpt_write_config(&self.base.base.id, offset, data); match offset { 0x00..=0xff if data.len() == 4 => { let value = LittleEndian::read_u32(data); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index f84d3b3eb..d3dcf2ba4 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -427,6 +427,7 @@ impl VirtioPciDevice { } fn activate_device(&self) -> bool { + trace::virtio_tpt_common("activate_device", &self.base.base.id); let mut locked_dev = self.device.lock().unwrap(); if locked_dev.device_activated() { return true; @@ -499,6 +500,7 @@ impl VirtioPciDevice { } fn deactivate_device(&self) -> bool { + trace::virtio_tpt_common("deactivate_device", &self.base.base.id); if self.need_irqfd && self.base.config.msix.is_some() { let msix = self.base.config.msix.as_ref().unwrap(); if msix.lock().unwrap().unregister_irqfd().is_err() { @@ -532,6 +534,7 @@ impl VirtioPciDevice { /// /// * `offset` - The offset of common config. fn read_common_config(&self, offset: u64) -> Result { + trace::virtio_tpt_read_common_config(&self.base.base.id, offset); let locked_device = self.device.lock().unwrap(); let value = match offset { COMMON_DFSELECT_REG => locked_device.hfeatures_sel(), @@ -602,6 +605,7 @@ impl VirtioPciDevice { /// /// Returns Error if the offset is out of bound. fn write_common_config(&mut self, offset: u64, value: u32) -> Result<()> { + trace::virtio_tpt_write_common_config(&self.base.base.id, offset, value); let mut locked_device = self.device.lock().unwrap(); match offset { COMMON_DFSELECT_REG => { @@ -1154,6 +1158,7 @@ impl PciDevOps for VirtioPciDevice { } fn unrealize(&mut self) -> Result<()> { + trace::virtio_tpt_common("unrealize", &self.base.base.id); self.device .lock() .unwrap() @@ -1170,6 +1175,7 @@ impl PciDevOps for VirtioPciDevice { } fn read_config(&mut self, offset: usize, data: &mut [u8]) { + trace::virtio_tpt_read_config(&self.base.base.id, offset as u64, data.len()); self.do_cfg_access(offset, offset + data.len(), false); self.base.config.read(offset, data); } @@ -1184,6 +1190,7 @@ impl PciDevOps for VirtioPciDevice { ); return; } + trace::virtio_tpt_write_config(&self.base.base.id, offset as u64, data); let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); @@ -1199,6 +1206,7 @@ impl PciDevOps for VirtioPciDevice { } fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + trace::virtio_tpt_common("reset", &self.base.base.id); self.deactivate_device(); self.device .lock() -- Gitee From b6018f8bea441af10399b162ad2f392b4d191eea Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 20 Feb 2024 16:35:20 +0800 Subject: [PATCH 1613/2187] fmt: fix some cargo clippy errors error: casting to the same type is unnecessary Signed-off-by: AlisaWang --- address_space/src/host_mmap.rs | 2 +- devices/src/misc/scream/ohos/ohaudio.rs | 6 +++--- machine/src/aarch64/standard.rs | 2 +- util/src/unix.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index 0bc8b3cd0..ed2ce2e98 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -144,7 +144,7 @@ impl FileBackend { Ok(FileBackend { file: Arc::new(file), offset: 0_u64, - page_size: fstat.optimal_transfer_size() as u64, + page_size: fstat.optimal_transfer_size() as _, }) } } diff --git a/devices/src/misc/scream/ohos/ohaudio.rs b/devices/src/misc/scream/ohos/ohaudio.rs index c8f45bae5..56301a569 100755 --- a/devices/src/misc/scream/ohos/ohaudio.rs +++ b/devices/src/misc/scream/ohos/ohaudio.rs @@ -61,7 +61,7 @@ impl OhAudioRender { fn check_data_ready(&self, recv_data: &StreamData) -> bool { let size = recv_data.fmt.size as u32 / 8; let channels = recv_data.fmt.channels as u32; - let rate = recv_data.fmt.get_rate() as u32; + let rate = recv_data.fmt.get_rate(); // Wait for data of 500 ms ready. // FIXME: the value of rate is wrong. self.prepared_data >= (size * channels * rate / 2) @@ -336,10 +336,10 @@ impl OhAudio { pub fn init(dir: ScreamDirection) -> Self { match dir { ScreamDirection::Playback => Self { - processor: Box::new(OhAudioRender::default()), + processor: Box::::default(), }, ScreamDirection::Record => Self { - processor: Box::new(OhAudioCapture::default()), + processor: Box::::default(), }, } } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 128a45d03..af5a7304d 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -942,7 +942,7 @@ impl AcpiBuilder for StdMachine { for dev in self.base.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); if locked_dev.sysbusdev_base().dev_type == SysBusDevType::PL011 { - uart_irq = locked_dev.sysbusdev_base().irq_state.irq as u32; + uart_irq = locked_dev.sysbusdev_base().irq_state.irq as _; break; } } diff --git a/util/src/unix.rs b/util/src/unix.rs index ef1507e98..943cb8b83 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -248,7 +248,7 @@ impl UnixSock { ) -> *mut cmsghdr { let next_cmsg = (cmsg_ptr as *mut u8).wrapping_add( // SAFETY: Safe to get cmsg_len because the parameter is valid. - unsafe { CMSG_LEN(cmsg.cmsg_len as u32) } as usize, + unsafe { CMSG_LEN(cmsg.cmsg_len as _) } as usize, ) as *mut cmsghdr; // Safe to get msg_control because the parameter is valid. let nex_cmsg_pos = (next_cmsg as *mut u8).wrapping_sub(msghdr.msg_control as usize) as u64; -- Gitee From d93594be4b4992475af263458008e7afeb19728c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Feb 2024 16:42:47 +0800 Subject: [PATCH 1614/2187] CPU: Refactor partial vCPU Lifecycle Interfaces Refactor vCPU lifecycle interfaces to accommodate hypervisor-specific management: delegate kick, reset, pause, and resume interfaces to hypervisor-specific implementations. Signed-off-by: Jinhao Gao --- cpu/src/lib.rs | 91 +++++++++++------------------- hypervisor/src/kvm/mod.rs | 114 ++++++++++++++++++++++++++++++++++---- 2 files changed, 135 insertions(+), 70 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index eee9bc209..b3b60378d 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -62,7 +62,7 @@ pub use x86_64::X86CPUTopology as CPUTopology; pub use x86_64::X86RegsIndex as RegsIndex; use std::cell::RefCell; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; use std::time::Duration; @@ -70,7 +70,6 @@ use std::time::Duration; use anyhow::{anyhow, Context, Result}; use log::{error, info, warn}; use nix::unistd::gettid; -use vmm_sys_util::signal::Killable; use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; @@ -193,6 +192,27 @@ pub trait CPUHypervisorOps: Send + Sync { ) -> Result<()>; fn kick(&self) -> Result<()>; + + fn kick_vcpu_thread( + &self, + task: Arc>>>, + state: Arc<(Mutex, Condvar)>, + ) -> Result<()>; + + fn pause( + &self, + task: Arc>>>, + state: Arc<(Mutex, Condvar)>, + pause_signal: Arc, + ) -> Result<()>; + + fn resume( + &self, + state: Arc<(Mutex, Condvar)>, + pause_signal: Arc, + ) -> Result<()>; + + fn reset(&self, task: Arc>>>) -> Result<()>; } /// `CPU` is a wrapper around creating and using a hypervisor-based VCPU. @@ -335,18 +355,9 @@ impl CPUInterface for CPU { #[cfg(target_arch = "aarch64")] self.hypervisor_cpu .set_regs(self.arch_cpu.clone(), RegsIndex::VtimerCount)?; - let (cpu_state_locked, cvar) = &*self.state; - let mut cpu_state = cpu_state_locked.lock().unwrap(); - if *cpu_state == CpuLifecycleState::Running { - warn!("vcpu{} in running state, no need to resume", self.id()); - return Ok(()); - } - *cpu_state = CpuLifecycleState::Running; - self.pause_signal.store(false, Ordering::SeqCst); - drop(cpu_state); - cvar.notify_one(); - Ok(()) + self.hypervisor_cpu + .resume(self.state.clone(), self.pause_signal.clone()) } fn start(cpu: Arc, thread_barrier: Arc, paused: bool) -> Result<()> { @@ -382,58 +393,20 @@ impl CPUInterface for CPU { } fn reset(&self) -> Result<()> { - let task = self.task.lock().unwrap(); - match task.as_ref() { - Some(thread) => thread - .kill(VCPU_RESET_SIGNAL) - .with_context(|| CpuError::KickVcpu("Fail to reset vcpu".to_string())), - None => { - warn!("VCPU thread not started, no need to reset"); - Ok(()) - } - } + self.hypervisor_cpu.reset(self.task.clone()) } fn kick(&self) -> Result<()> { - let task = self.task.lock().unwrap(); - match task.as_ref() { - Some(thread) => thread - .kill(VCPU_TASK_SIGNAL) - .with_context(|| CpuError::KickVcpu("Fail to kick vcpu".to_string())), - None => { - warn!("VCPU thread not started, no need to kick"); - Ok(()) - } - } + self.hypervisor_cpu + .kick_vcpu_thread(self.task.clone(), self.state.clone()) } fn pause(&self) -> Result<()> { - let task = self.task.lock().unwrap(); - let (cpu_state, cvar) = &*self.state; - - if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { - *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; - cvar.notify_one() - } - - match task.as_ref() { - Some(thread) => { - if let Err(e) = thread.kill(VCPU_TASK_SIGNAL) { - return Err(anyhow!(CpuError::StopVcpu(format!("{:?}", e)))); - } - } - None => { - warn!("vCPU thread not started, no need to stop"); - return Ok(()); - } - } - - // It shall wait for the vCPU pause state from hypervisor exits. - loop { - if self.pause_signal.load(Ordering::SeqCst) { - break; - } - } + self.hypervisor_cpu.pause( + self.task.clone(), + self.state.clone(), + self.pause_signal.clone(), + )?; #[cfg(target_arch = "aarch64")] self.hypervisor_cpu diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index adbb37f3f..300c8bf96 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -27,12 +27,11 @@ pub use aarch64::gicv2::KvmGICv2; pub use aarch64::gicv3::{KvmGICv3, KvmGICv3Its}; use std::collections::HashMap; -use std::sync::atomic::{fence, Ordering}; -use std::sync::{Arc, Barrier, Mutex}; +use std::sync::atomic::{fence, AtomicBool, Ordering}; +use std::sync::{Arc, Barrier, Condvar, Mutex}; use std::thread; use std::time::Duration; -#[cfg(not(test))] use anyhow::anyhow; use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; @@ -41,10 +40,11 @@ use kvm_bindings::*; use kvm_ioctls::VcpuExit; use kvm_ioctls::{Cap, DeviceFd, Kvm, VcpuFd, VmFd}; use libc::{c_int, c_void, siginfo_t}; -use log::{error, info}; +use log::{error, info, warn}; use vmm_sys_util::{ - eventfd::EventFd, ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, - signal::register_signal_handler, + eventfd::EventFd, + ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr, + signal::{register_signal_handler, Killable}, }; use self::listener::KvmMemoryListener; @@ -58,11 +58,9 @@ use address_space::{AddressSpace, Listener}; use cpu::capture_boot_signal; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; -#[cfg(not(test))] -use cpu::CpuError; use cpu::{ - ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuLifecycleState, - RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, + ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuError, + CpuLifecycleState, RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, }; use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, TriggerMode}; #[cfg(target_arch = "aarch64")] @@ -232,6 +230,7 @@ impl HypervisorOps for KvmHypervisor { .create_vcpu(vcpu_id as u64) .with_context(|| "Create vcpu failed")?; Ok(Arc::new(KvmCpu::new( + vcpu_id, #[cfg(target_arch = "aarch64")] self.vm_fd.clone(), vcpu_fd, @@ -361,6 +360,7 @@ impl MigrateOps for KvmHypervisor { } pub struct KvmCpu { + id: u8, #[cfg(target_arch = "aarch64")] vm_fd: Option>, fd: Arc, @@ -372,8 +372,13 @@ pub struct KvmCpu { } impl KvmCpu { - pub fn new(#[cfg(target_arch = "aarch64")] vm_fd: Option>, vcpu_fd: VcpuFd) -> Self { + pub fn new( + id: u8, + #[cfg(target_arch = "aarch64")] vm_fd: Option>, + vcpu_fd: VcpuFd, + ) -> Self { Self { + id, #[cfg(target_arch = "aarch64")] vm_fd, fd: Arc::new(vcpu_fd), @@ -621,6 +626,91 @@ impl CPUHypervisorOps for KvmCpu { self.fd.set_kvm_immediate_exit(1); Ok(()) } + + fn kick_vcpu_thread( + &self, + task: Arc>>>, + _state: Arc<(Mutex, Condvar)>, + ) -> Result<()> { + let task = task.lock().unwrap(); + match task.as_ref() { + Some(thread) => thread + .kill(VCPU_TASK_SIGNAL) + .with_context(|| CpuError::KickVcpu("Fail to kick vcpu".to_string())), + None => { + warn!("VCPU thread not started, no need to kick"); + Ok(()) + } + } + } + + fn pause( + &self, + task: Arc>>>, + state: Arc<(Mutex, Condvar)>, + pause_signal: Arc, + ) -> Result<()> { + let task = task.lock().unwrap(); + let (cpu_state, cvar) = &*state; + + if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { + *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; + cvar.notify_one() + } + + match task.as_ref() { + Some(thread) => { + if let Err(e) = thread.kill(VCPU_TASK_SIGNAL) { + return Err(anyhow!(CpuError::StopVcpu(format!("{:?}", e)))); + } + } + None => { + warn!("vCPU thread not started, no need to stop"); + return Ok(()); + } + } + + // It shall wait for the vCPU pause state from hypervisor exits. + loop { + if pause_signal.load(Ordering::SeqCst) { + break; + } + } + + Ok(()) + } + + fn resume( + &self, + state: Arc<(Mutex, Condvar)>, + pause_signal: Arc, + ) -> Result<()> { + let (cpu_state_locked, cvar) = &*state; + let mut cpu_state = cpu_state_locked.lock().unwrap(); + if *cpu_state == CpuLifecycleState::Running { + warn!("vcpu{} in running state, no need to resume", self.id); + return Ok(()); + } + + *cpu_state = CpuLifecycleState::Running; + pause_signal.store(false, Ordering::SeqCst); + drop(cpu_state); + cvar.notify_one(); + Ok(()) + } + + fn reset(&self, task: Arc>>>) -> Result<()> { + let task = task.lock().unwrap(); + match task.as_ref() { + Some(thread) => thread + .kill(VCPU_RESET_SIGNAL) + .with_context(|| CpuError::KickVcpu("Fail to reset vcpu".to_string())), + None => { + warn!("VCPU thread not started, no need to reset"); + Ok(()) + } + } + } } struct KVMInterruptManager { @@ -937,6 +1027,7 @@ mod test { vm_fd.create_irq_chip().unwrap(); let vcpu_fd = kvm_hyp.vm_fd.as_ref().unwrap().create_vcpu(0).unwrap(); let hypervisor_cpu = Arc::new(KvmCpu::new( + 0, #[cfg(target_arch = "aarch64")] kvm_hyp.vm_fd.clone(), vcpu_fd, @@ -993,6 +1084,7 @@ mod test { let vcpu_fd = kvm_hyp.vm_fd.as_ref().unwrap().create_vcpu(0).unwrap(); let hypervisor_cpu = Arc::new(KvmCpu::new( + 0, #[cfg(target_arch = "aarch64")] kvm_hyp.vm_fd.clone(), vcpu_fd, -- Gitee From b052d7996c4ad421bd5909d8d6a82280f9b8b589 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 8 Feb 2024 12:35:31 +0800 Subject: [PATCH 1615/2187] arm/cpu: Enable SVE support based on KVM and migration is not supported when sve enabled. Signed-off-by: Keqian Zhu --- cpu/src/aarch64/caps.rs | 7 +++++- docs/config_guidebook.md | 3 ++- docs/migration.md | 1 + docs/snapshot.md | 1 + hypervisor/src/kvm/aarch64/cpu_caps.rs | 2 ++ hypervisor/src/kvm/aarch64/mod.rs | 12 ++++++++++ machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/machine_config.rs | 23 ++++++++++++++++++++ 8 files changed, 48 insertions(+), 3 deletions(-) diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index eda1e701e..bd1bea4a2 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use machine_manager::config::{CpuConfig, PmuConfig}; +use machine_manager::config::{CpuConfig, PmuConfig, SveConfig}; #[derive(Copy, Clone, Debug, Default)] pub struct ArmCPUFeatures { pub pmu: bool, + pub sve: bool, } impl From<&CpuConfig> for ArmCPUFeatures { @@ -24,6 +25,10 @@ impl From<&CpuConfig> for ArmCPUFeatures { PmuConfig::On => true, PmuConfig::Off => false, }, + sve: match &conf.sve { + SveConfig::On => true, + SveConfig::Off => false, + }, } } } diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 5148cdb2d..c9253512c 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -57,10 +57,11 @@ Currently, these options are supported. * CPU Family: Set the CPU family for VM, default to `host`, and this is the only supported variant currently. * pmu: This enables armv8 PMU for VM. Should be `off` or `on`, default to `off`. (Currently only supported on aarch64) +* sve: This enables SVE feature for VM. Should be `off` or `on`, default to `off`. (Currently only supported on aarch64) ```shell # cmdline --cpu host[,pmu={on|off}] +-cpu host[,pmu={on|off}][,sve={on|off}] ``` ### 1.3 Memory diff --git a/docs/migration.md b/docs/migration.md index 68e8fa7b7..75c6c6651 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -107,6 +107,7 @@ Some devices and feature don't support to be migration yet: - `balloon` - `mem-shared`,`backend file of memory` - `pmu` +- `sve` - `gic-version=2` Some device attributes can't be changed: diff --git a/docs/snapshot.md b/docs/snapshot.md index 515f40c57..6a9c97f73 100644 --- a/docs/snapshot.md +++ b/docs/snapshot.md @@ -87,6 +87,7 @@ Some devices and feature don't support to be snapshot yet: - `balloon` - `hugepage`,`mem-shared`,`backend file of memory` - `pmu` +- `sve` - `gic-version=2` Some device attributes can't be changed: diff --git a/hypervisor/src/kvm/aarch64/cpu_caps.rs b/hypervisor/src/kvm/aarch64/cpu_caps.rs index f7876bfff..955ce5e80 100644 --- a/hypervisor/src/kvm/aarch64/cpu_caps.rs +++ b/hypervisor/src/kvm/aarch64/cpu_caps.rs @@ -23,6 +23,7 @@ pub struct ArmCPUCaps { pub psci02: bool, pub mp_state: bool, pub vcpu_events: bool, + pub sve: bool, } impl ArmCPUCaps { @@ -37,6 +38,7 @@ impl ArmCPUCaps { psci02: kvm.check_extension(Cap::ArmPsci02), mp_state: kvm.check_extension(Cap::MpState), vcpu_events: kvm.check_extension(Cap::VcpuEvents), + sve: kvm.check_extension(Cap::ArmSve), } } } diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index c78b528e5..04831e058 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -157,12 +157,24 @@ impl KvmCpu { if vcpu_config.pmu { kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; } + // Enable SVE from config. + if vcpu_config.sve { + if !self.caps.sve { + bail!("SVE is not supported by KVM"); + } + kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_SVE; + } drop(kvi); arch_cpu.lock().unwrap().set_core_reg(boot_config); self.arch_vcpu_init()?; + if vcpu_config.sve { + self.fd + .vcpu_finalize(&(kvm_bindings::KVM_ARM_VCPU_SVE as i32))?; + } + arch_cpu.lock().unwrap().mpidr = self.get_one_reg(KVM_REG_ARM_MPIDR_EL1) .with_context(|| "Failed to get mpidr")? as u64; diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index fbfd7b033..0dfeeccef 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -114,7 +114,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .arg( Arg::with_name("cpu") .long("cpu") - .value_name("host[,pmu=on|off]") + .value_name("host[,pmu=on|off][,sve=on|off]") .help("set CPU model and features.") .can_no_value(false) .takes_value(true) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 8195b965a..c1a6fb76f 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -138,6 +138,7 @@ impl Default for MachineMemConfig { #[derive(Clone, Debug, Serialize, Deserialize, Default)] pub struct CpuConfig { pub pmu: PmuConfig, + pub sve: SveConfig, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] @@ -147,6 +148,13 @@ pub enum PmuConfig { Off, } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] +pub enum SveConfig { + On, + #[default] + Off, +} + #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ShutdownAction { #[default] @@ -400,7 +408,9 @@ impl VmConfig { let mut cmd_parser = CmdParser::new("cpu"); cmd_parser.push(""); cmd_parser.push("pmu"); + cmd_parser.push("sve"); cmd_parser.parse(features)?; + // Check PMU when actually enabling PMU. if let Some(k) = cmd_parser.get_value::("pmu")? { self.machine_config.cpu_config.pmu = match k.as_ref() { @@ -409,6 +419,15 @@ impl VmConfig { _ => bail!("Invalid PMU option,must be one of \'on\" or \"off\"."), } } + + if let Some(k) = cmd_parser.get_value::("sve")? { + self.machine_config.cpu_config.sve = match k.as_ref() { + "on" => SveConfig::On, + "off" => SveConfig::Off, + _ => bail!("Invalid SVE option, must be one of \"on\" or \"off\"."), + } + } + Ok(()) } @@ -1132,5 +1151,9 @@ mod tests { assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::On); vm_config.add_cpu_feature("pmu=on").unwrap(); assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::On); + vm_config.add_cpu_feature("sve=on").unwrap(); + assert!(vm_config.machine_config.cpu_config.sve == SveConfig::On); + vm_config.add_cpu_feature("sve=off").unwrap(); + assert!(vm_config.machine_config.cpu_config.sve == SveConfig::Off); } } -- Gitee From 87fa2c3febdd77182b985b145c67a5b4aaead0fb Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 8 Feb 2024 13:10:30 +0800 Subject: [PATCH 1616/2187] CPU/KVM: Check PMUv3 cap before enable pmu feature Signed-off-by: Keqian Zhu --- hypervisor/src/kvm/aarch64/cpu_caps.rs | 2 ++ hypervisor/src/kvm/aarch64/mod.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/hypervisor/src/kvm/aarch64/cpu_caps.rs b/hypervisor/src/kvm/aarch64/cpu_caps.rs index 955ce5e80..9d60ae221 100644 --- a/hypervisor/src/kvm/aarch64/cpu_caps.rs +++ b/hypervisor/src/kvm/aarch64/cpu_caps.rs @@ -23,6 +23,7 @@ pub struct ArmCPUCaps { pub psci02: bool, pub mp_state: bool, pub vcpu_events: bool, + pub pmuv3: bool, pub sve: bool, } @@ -38,6 +39,7 @@ impl ArmCPUCaps { psci02: kvm.check_extension(Cap::ArmPsci02), mp_state: kvm.check_extension(Cap::MpState), vcpu_events: kvm.check_extension(Cap::VcpuEvents), + pmuv3: kvm.check_extension(Cap::ArmPmuV3), sve: kvm.check_extension(Cap::ArmSve), } } diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 04831e058..95ab698ff 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -155,6 +155,9 @@ impl KvmCpu { // Enable PMU from config. if vcpu_config.pmu { + if !self.caps.pmuv3 { + bail!("PMUv3 is not supported by KVM"); + } kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PMU_V3; } // Enable SVE from config. -- Gitee From 9e12f7e79215a9b1d421d5c3c20b9329bb11d5c1 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 15:02:38 +0800 Subject: [PATCH 1617/2187] OHUI: add UDS channel support Add UDS funtions for UI module on OHOS Signed-off-by: zhanghan64 --- ui/src/ohui_srv/channel.rs | 139 +++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100755 ui/src/ohui_srv/channel.rs diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs new file mode 100755 index 000000000..6443b3408 --- /dev/null +++ b/ui/src/ohui_srv/channel.rs @@ -0,0 +1,139 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// Stratovirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::c_void; +use std::os::unix::io::RawFd; +use std::sync::RwLock; + +use anyhow::Result; +use libc::iovec; +use log::error; + +use super::msg::*; +use util::byte_code::ByteCode; +use util::unix::UnixSock; + +pub struct OhUiChannel { + pub sock: RwLock, + pub path: String, +} + +impl OhUiChannel { + pub fn new(path: &str) -> Self { + OhUiChannel { + sock: RwLock::new(UnixSock::new(path)), + path: String::from(path), + } + } + + pub fn bind(&self) -> Result<()> { + self.sock.write().unwrap().bind(true) + } + + pub fn get_listener_raw_fd(&self) -> RawFd { + self.sock.read().unwrap().get_listener_raw_fd() + } + + pub fn get_stream_raw_fd(&self) -> RawFd { + self.sock.read().unwrap().get_stream_raw_fd() + } + + pub fn set_nonblocking(&self, nb: bool) -> Result<()> { + self.sock.read().unwrap().set_nonblocking(nb) + } + + pub fn set_listener_nonblocking(&self, nb: bool) -> Result<()> { + self.sock.read().unwrap().listen_set_nonblocking(nb) + } + + pub fn accept(&self) -> Result<()> { + self.sock.write().unwrap().accept() + } + + pub fn send(&self, data: *const u8, len: usize) -> Result { + let mut iovs = Vec::with_capacity(1); + iovs.push(iovec { + iov_base: data as *mut c_void, + iov_len: len, + }); + let ret = self.sock.read().unwrap().send_msg(&mut iovs, &[])?; + Ok(ret) + } + + pub fn send_by_obj(&self, obj: &T) -> Result<()> { + let slice = obj.as_bytes(); + let mut left = slice.len(); + let mut count = 0_usize; + + while left > 0 { + let buf = &slice[count..]; + match self.send(buf.as_ptr(), left) { + Ok(n) => { + left -= n; + count += n; + } + Err(e) => { + if std::io::Error::last_os_error().raw_os_error().unwrap() == libc::EAGAIN { + continue; + } + return Err(e); + } + } + } + Ok(()) + } + + pub fn send_message( + &self, + t: EventType, + body: &T, + ) -> Result<()> { + let hdr = EventMsgHdr::new(t); + self.send_by_obj(&hdr)?; + self.send_by_obj(body) + } + + pub fn recv_slice(&self, data: &mut [u8]) -> Result { + let len = data.len(); + if len == 0 { + return Ok(0); + } + let ret = self.recv(data.as_mut_ptr(), len); + match ret { + Ok(n) => Ok(n), + Err(e) => { + if std::io::Error::last_os_error() + .raw_os_error() + .unwrap_or(libc::EIO) + != libc::EAGAIN + { + error!("recv_slice(): error occurred: {}", e); + } + Ok(0) + } + } + } + + pub fn recv(&self, data: *mut u8, len: usize) -> Result { + let mut iovs = Vec::with_capacity(1); + iovs.push(iovec { + iov_base: data as *mut c_void, + iov_len: len, + }); + + let ret = self.sock.read().unwrap().recv_msg(&mut iovs, &mut []); + match ret { + Ok((n, _)) => Ok(n), + Err(e) => Err(e.into()), + } + } +} -- Gitee From 9183d346ede4f1e7c6e096101e0a66729ce988bd Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 21 Feb 2024 23:09:58 +0800 Subject: [PATCH 1618/2187] migration: bugfix for mgiration 1. fix get/set cpreg 2. realize vcpu for destination VM 3. create fdt and acpi for destination VM for reboot Signed-off-by: Mingwang Li --- cpu/src/aarch64/mod.rs | 7 --- hypervisor/src/kvm/aarch64/mod.rs | 7 +-- machine/src/aarch64/micro.rs | 48 ++++++++------------- machine/src/aarch64/standard.rs | 72 ++++++++++++------------------- machine/src/lib.rs | 35 +++++++-------- machine/src/x86_64/micro.rs | 11 +---- machine/src/x86_64/standard.rs | 53 ++++++++++------------- 7 files changed, 90 insertions(+), 143 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index f7b745ef2..3aebcbc0a 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -206,13 +206,6 @@ impl StateTransfer for CPU { *cpu_state_locked = cpu_state; drop(cpu_state_locked); - self.hypervisor_cpu.vcpu_init()?; - - if cpu_state.features.pmu { - self.hypervisor_cpu - .init_pmu() - .with_context(|| MigrationError::FromBytesError("Failed to init pmu."))?; - } Ok(()) } diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index c78b528e5..24614f51a 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -216,7 +216,7 @@ impl KvmCpu { let mut cpreg_list = RegList::new(KVM_MAX_CPREG_ENTRIES)?; self.fd.get_reg_list(&mut cpreg_list)?; locked_arch_cpu.cpreg_len = 0; - for (index, cpreg) in cpreg_list.as_slice().iter().enumerate() { + for cpreg in cpreg_list.as_slice() { let mut cpreg_entry = CpregListEntry { reg_id: *cpreg, value: 0, @@ -225,6 +225,7 @@ impl KvmCpu { // We sync these cpreg by hand, such as core regs. continue; } + let index = locked_arch_cpu.cpreg_len; locked_arch_cpu.cpreg_list[index] = cpreg_entry; locked_arch_cpu.cpreg_len += 1; } @@ -368,8 +369,8 @@ impl KvmCpu { } fn reg_sync_by_cpreg_list(reg_id: u64) -> Result { - let coproc = reg_id & KVM_REG_ARM_COPROC_MASK as u64; - if coproc != KVM_REG_ARM_CORE as u64 { + let coproc = reg_id as u32 & KVM_REG_ARM_COPROC_MASK; + if coproc == KVM_REG_ARM_CORE { return Ok(false); } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 38af54473..d7e1b1d4c 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -20,7 +20,7 @@ use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; use hypervisor::kvm::aarch64::*; -use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; +use machine_manager::config::{SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::{ device_tree::{self, CompileFDT, FdtBuilder}, @@ -160,19 +160,9 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_cpus, )?; - let migrate_info = locked_vm.get_migrate_info(); - - let (boot_config, cpu_config) = if migrate_info.0 == MigrateMode::Unknown { - ( - Some( - locked_vm - .load_boot_source(None, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?, - ), - Some(locked_vm.load_cpu_features(vm_config)?), - ) - } else { - (None, None) - }; + let boot_config = + locked_vm.load_boot_source(None, MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?; + let cpu_config = locked_vm.load_cpu_features(vm_config)?; let hypervisor = locked_vm.base.hypervisor.clone(); // vCPUs init,and apply CPU features (for aarch64) @@ -196,22 +186,20 @@ impl MachineOps for LightMachine { locked_vm.add_devices(vm_config)?; trace::replaceable_info(&locked_vm.replaceable_info); - if let Some(boot_cfg) = boot_config { - let mut fdt_helper = FdtBuilder::new(); - locked_vm - .generate_fdt_node(&mut fdt_helper) - .with_context(|| MachineError::GenFdtErr)?; - let fdt_vec = fdt_helper.finish()?; - locked_vm - .base - .sys_mem - .write( - &mut fdt_vec.as_slice(), - GuestAddress(boot_cfg.fdt_addr), - fdt_vec.len() as u64, - ) - .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; - } + let mut fdt_helper = FdtBuilder::new(); + locked_vm + .generate_fdt_node(&mut fdt_helper) + .with_context(|| MachineError::GenFdtErr)?; + let fdt_vec = fdt_helper.finish()?; + locked_vm + .base + .sys_mem + .write( + &mut fdt_vec.as_slice(), + GuestAddress(boot_config.fdt_addr), + fdt_vec.len() as u64, + ) + .with_context(|| MachineError::WrtFdtErr(boot_config.fdt_addr, fdt_vec.len()))?; MigrationManager::register_vm_instance(vm.clone()); MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index af5a7304d..059d8a6db 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -556,21 +556,9 @@ impl MachineOps for StdMachine { .with_context(|| MachineError::InitPCIeHostErr)?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus)?; - let migrate = locked_vm.get_migrate_info(); - let boot_config = - if migrate.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source( - fwcfg.as_ref(), - MEM_LAYOUT[LayoutEntryType::Mem as usize].0, - )?) - } else { - None - }; - let cpu_config = if migrate.0 == MigrateMode::Unknown { - Some(locked_vm.load_cpu_features(vm_config)?) - } else { - None - }; + let boot_config = locked_vm + .load_boot_source(fwcfg.as_ref(), MEM_LAYOUT[LayoutEntryType::Mem as usize].0)?; + let cpu_config = locked_vm.load_cpu_features(vm_config)?; let hypervisor = locked_vm.base.hypervisor.clone(); locked_vm.base.cpus.extend(::init_vcpu( @@ -591,37 +579,33 @@ impl MachineOps for StdMachine { .add_devices(vm_config) .with_context(|| "Failed to add devices")?; - if let Some(boot_cfg) = boot_config { - let mut fdt_helper = FdtBuilder::new(); - locked_vm - .generate_fdt_node(&mut fdt_helper) - .with_context(|| MachineError::GenFdtErr)?; - let fdt_vec = fdt_helper.finish()?; - locked_vm.dtb_vec = fdt_vec.clone(); - locked_vm - .base - .sys_mem - .write( - &mut fdt_vec.as_slice(), - GuestAddress(boot_cfg.fdt_addr), - fdt_vec.len() as u64, - ) - .with_context(|| MachineError::WrtFdtErr(boot_cfg.fdt_addr, fdt_vec.len()))?; - } + let mut fdt_helper = FdtBuilder::new(); + locked_vm + .generate_fdt_node(&mut fdt_helper) + .with_context(|| MachineError::GenFdtErr)?; + let fdt_vec = fdt_helper.finish()?; + locked_vm.dtb_vec = fdt_vec.clone(); + locked_vm + .base + .sys_mem + .write( + &mut fdt_vec.as_slice(), + GuestAddress(boot_config.fdt_addr), + fdt_vec.len() as u64, + ) + .with_context(|| MachineError::WrtFdtErr(boot_config.fdt_addr, fdt_vec.len()))?; // If it is direct kernel boot mode, the ACPI can not be enabled. - if migrate.0 == MigrateMode::Unknown { - if let Some(fw_cfg) = fwcfg { - let mut mem_array = Vec::new(); - let mem_size = vm_config.machine_config.mem_config.mem_size; - mem_array.push((MEM_LAYOUT[LayoutEntryType::Mem as usize].0, mem_size)); - locked_vm - .build_acpi_tables(&fw_cfg) - .with_context(|| "Failed to create ACPI tables")?; - locked_vm - .build_smbios(&fw_cfg, mem_array) - .with_context(|| "Failed to create smbios tables")?; - } + if let Some(fw_cfg) = fwcfg { + let mut mem_array = Vec::new(); + let mem_size = vm_config.machine_config.mem_config.mem_size; + mem_array.push((MEM_LAYOUT[LayoutEntryType::Mem as usize].0, mem_size)); + locked_vm + .build_acpi_tables(&fw_cfg) + .with_context(|| "Failed to create ACPI tables")?; + locked_vm + .build_smbios(&fw_cfg, mem_array) + .with_context(|| "Failed to create smbios tables")?; } locked_vm diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c49a43d0f..ddaec6c7a 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -463,8 +463,8 @@ pub trait MachineOps { nr_cpus: u8, #[cfg(target_arch = "x86_64")] max_cpus: u8, topology: &CPUTopology, - boot_cfg: &Option, - #[cfg(target_arch = "aarch64")] vcpu_cfg: &Option, + boot_cfg: &CPUBootConfig, + #[cfg(target_arch = "aarch64")] vcpu_cfg: &CPUFeatures, ) -> Result>> where Self: Sized, @@ -484,21 +484,19 @@ pub trait MachineOps { MigrationManager::register_cpu_instance(cpu::ArchCPU::descriptor(), cpu, vcpu_id); } - if let Some(boot_config) = boot_cfg { - for (cpu_index, cpu) in cpus.iter().enumerate() { - cpu.realize( - boot_config, - topology, - #[cfg(target_arch = "aarch64")] - &vcpu_cfg.unwrap_or_default(), + for (cpu_index, cpu) in cpus.iter().enumerate() { + cpu.realize( + boot_cfg, + topology, + #[cfg(target_arch = "aarch64")] + vcpu_cfg, + ) + .with_context(|| { + format!( + "Failed to realize arch cpu register/features for CPU {}", + cpu_index ) - .with_context(|| { - format!( - "Failed to realize arch cpu register/features for CPU {}", - cpu_index - ) - })?; - } + })?; } Ok(cpus) @@ -510,9 +508,8 @@ pub trait MachineOps { /// /// * `CPUFeatures` - The features of vcpu. #[cfg(target_arch = "aarch64")] - fn cpu_post_init(&self, vcpu_cfg: &Option) -> Result<()> { - let features = vcpu_cfg.unwrap_or_default(); - if features.pmu { + fn cpu_post_init(&self, vcpu_cfg: &CPUFeatures) -> Result<()> { + if vcpu_cfg.pmu { for cpu in self.machine_base().cpus.iter() { cpu.hypervisor_cpu.init_pmu()?; } diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 7d4619b17..ed2906517 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -22,7 +22,7 @@ use cpu::{CPUBootConfig, CPUTopology}; use devices::legacy::FwCfgOps; use hypervisor::kvm::x86_64::*; use hypervisor::kvm::*; -use machine_manager::config::{MigrateMode, SerialConfig, VmConfig}; +use machine_manager::config::{SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::seccomp::{BpfRule, SeccompCmpOpt}; use virtio::VirtioMmioDevice; @@ -166,8 +166,6 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_cpus, )?; - let migrate_info = locked_vm.get_migrate_info(); - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; // Add mmio devices @@ -177,12 +175,7 @@ impl MachineOps for LightMachine { locked_vm.add_devices(vm_config)?; trace::replaceable_info(&locked_vm.replaceable_info); - let boot_config = if migrate_info.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(None)?) - } else { - None - }; - + let boot_config = locked_vm.load_boot_source(None)?; let hypervisor = locked_vm.base.hypervisor.clone(); locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 0000918cc..25d147e65 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -544,19 +544,12 @@ impl MachineOps for StdMachine { locked_vm.add_devices(vm_config)?; let fwcfg = locked_vm.add_fwcfg_device(nr_cpus, max_cpus)?; - - let migrate = locked_vm.get_migrate_info(); - let boot_config = if migrate.0 == MigrateMode::Unknown { - Some(locked_vm.load_boot_source(fwcfg.as_ref())?) - } else { - None - }; + let boot_config = locked_vm.load_boot_source(fwcfg.as_ref())?; let topology = CPUTopology::new().set_topology(( vm_config.machine_config.nr_threads, vm_config.machine_config.nr_cores, vm_config.machine_config.nr_dies, )); - let hypervisor = locked_vm.base.hypervisor.clone(); locked_vm.base.cpus.extend(::init_vcpu( vm.clone(), @@ -567,32 +560,30 @@ impl MachineOps for StdMachine { &boot_config, )?); - locked_vm.init_cpu_controller(boot_config.unwrap(), topology, vm.clone())?; - - if migrate.0 == MigrateMode::Unknown { - if let Some(fw_cfg) = fwcfg { - locked_vm - .build_acpi_tables(&fw_cfg) - .with_context(|| "Failed to create ACPI tables")?; - let mut mem_array = Vec::new(); - let mem_size = vm_config.machine_config.mem_config.mem_size; - let below_size = - std::cmp::min(MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, mem_size); + locked_vm.init_cpu_controller(boot_config, topology, vm.clone())?; + + if let Some(fw_cfg) = fwcfg { + locked_vm + .build_acpi_tables(&fw_cfg) + .with_context(|| "Failed to create ACPI tables")?; + let mut mem_array = Vec::new(); + let mem_size = vm_config.machine_config.mem_config.mem_size; + let below_size = + std::cmp::min(MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, mem_size); + mem_array.push(( + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, + below_size, + )); + if mem_size > below_size { mem_array.push(( - MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0, - below_size, + MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0, + mem_size - below_size, )); - if mem_size > below_size { - mem_array.push(( - MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0, - mem_size - below_size, - )); - } - - locked_vm - .build_smbios(&fw_cfg, mem_array) - .with_context(|| "Failed to create smbios tables")?; } + + locked_vm + .build_smbios(&fw_cfg, mem_array) + .with_context(|| "Failed to create smbios tables")?; } locked_vm -- Gitee From 298e758dd31369cb220842036058544e2f6c30b0 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 22 Feb 2024 09:33:46 +0800 Subject: [PATCH 1619/2187] CPU: Refactor the interfaces related to reset Signed-off-by: Jinhao Gao --- cpu/src/lib.rs | 25 ++++----------- hypervisor/src/kvm/aarch64/mod.rs | 7 ++++- hypervisor/src/kvm/mod.rs | 51 ++++++++++--------------------- hypervisor/src/kvm/x86_64/mod.rs | 7 ++++- machine/src/aarch64/standard.rs | 5 +-- machine/src/x86_64/standard.rs | 4 +-- 6 files changed, 36 insertions(+), 63 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index b3b60378d..dd689d96f 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -84,12 +84,6 @@ pub const VCPU_TASK_SIGNAL: i32 = 34; pub const VCPU_TASK_SIGNAL: i32 = 35; #[cfg(target_env = "ohos")] pub const VCPU_TASK_SIGNAL: i32 = 40; -#[cfg(target_env = "gnu")] -pub const VCPU_RESET_SIGNAL: i32 = 35; -#[cfg(target_env = "musl")] -pub const VCPU_RESET_SIGNAL: i32 = 36; -#[cfg(target_env = "ohos")] -pub const VCPU_RESET_SIGNAL: i32 = 41; /// Watch `0x3ff` IO port to record the magic value trapped from guest kernel. #[cfg(all(target_arch = "x86_64", feature = "boot_time"))] @@ -153,9 +147,6 @@ pub trait CPUInterface { /// Make `CPU` lifecycle to `Stopping`, then `Stopped`. fn destroy(&self) -> Result<()>; - /// Reset registers value for `CPU`. - fn reset(&self) -> Result<()>; - /// Make `CPU` destroy because of guest inner shutdown. fn guest_shutdown(&self) -> Result<()>; @@ -183,6 +174,8 @@ pub trait CPUHypervisorOps: Send + Sync { fn set_regs(&self, arch_cpu: Arc>, regs_index: RegsIndex) -> Result<()>; + fn put_register(&self, cpu: Arc) -> Result<()>; + fn reset_vcpu(&self, cpu: Arc) -> Result<()>; fn vcpu_exec( @@ -211,8 +204,6 @@ pub trait CPUHypervisorOps: Send + Sync { state: Arc<(Mutex, Condvar)>, pause_signal: Arc, ) -> Result<()>; - - fn reset(&self, task: Arc>>>) -> Result<()>; } /// `CPU` is a wrapper around creating and using a hypervisor-based VCPU. @@ -266,10 +257,6 @@ impl CPU { } } - pub fn set_to_boot_state(&self) { - self.arch_cpu.lock().unwrap().set(&self.boot_state); - } - /// Get this `CPU`'s ID. pub fn id(&self) -> u8 { self.id @@ -311,6 +298,10 @@ impl CPU { self.vm.clone() } + pub fn boot_state(&self) -> Arc> { + self.boot_state.clone() + } + pub fn pause_signal(&self) -> Arc { self.pause_signal.clone() } @@ -392,10 +383,6 @@ impl CPUInterface for CPU { Ok(()) } - fn reset(&self) -> Result<()> { - self.hypervisor_cpu.reset(self.task.clone()) - } - fn kick(&self) -> Result<()> { self.hypervisor_cpu .kick_vcpu_thread(self.task.clone(), self.state.clone()) diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index c78b528e5..5c12d24ac 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -397,11 +397,16 @@ impl KvmCpu { Ok(true) } - pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { + pub fn arch_put_register(&self, cpu: Arc) -> Result<()> { let arch_cpu = &cpu.arch_cpu; self.arch_set_regs(arch_cpu.clone(), RegsIndex::CoreRegs)?; self.arch_set_regs(arch_cpu.clone(), RegsIndex::MpState)?; self.arch_set_regs(arch_cpu.clone(), RegsIndex::CpregList)?; self.arch_set_regs(arch_cpu.clone(), RegsIndex::VcpuEvents) } + + pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { + cpu.arch_cpu.lock().unwrap().set(&cpu.boot_state()); + self.arch_vcpu_init() + } } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 300c8bf96..ddaef98f7 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -60,7 +60,7 @@ use cpu::capture_boot_signal; use cpu::CPUFeatures; use cpu::{ ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUThreadWorker, CpuError, - CpuLifecycleState, RegsIndex, CPU, VCPU_RESET_SIGNAL, VCPU_TASK_SIGNAL, + CpuLifecycleState, RegsIndex, CPU, VCPU_TASK_SIGNAL, }; use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, TriggerMode}; #[cfg(target_arch = "aarch64")] @@ -391,30 +391,18 @@ impl KvmCpu { /// Init signal for `CPU` event. fn init_signals(&self) -> Result<()> { extern "C" fn handle_signal(signum: c_int, _: *mut siginfo_t, _: *mut c_void) { - match signum { - VCPU_TASK_SIGNAL => { - let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - vcpu.hypervisor_cpu().kick().unwrap(); - // Setting pause_signal to be `true` if kvm changes vCPU to pause state. - vcpu.pause_signal().store(true, Ordering::SeqCst); - fence(Ordering::Release) - }); - } - VCPU_RESET_SIGNAL => { - let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - if let Err(e) = vcpu.hypervisor_cpu().reset_vcpu(vcpu.clone()) { - error!("Failed to reset vcpu state: {:?}", e) - } - }); - } - _ => {} + if signum == VCPU_TASK_SIGNAL { + let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { + vcpu.hypervisor_cpu().kick().unwrap(); + // Setting pause_signal to be `true` if kvm changes vCPU to pause state. + vcpu.pause_signal().store(true, Ordering::SeqCst); + fence(Ordering::Release) + }); } } register_signal_handler(VCPU_TASK_SIGNAL, handle_signal) .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; - register_signal_handler(VCPU_RESET_SIGNAL, handle_signal) - .with_context(|| "Failed to register VCPU_TASK_SIGNAL signal.")?; Ok(()) } @@ -559,6 +547,12 @@ impl CPUHypervisorOps for KvmCpu { self.arch_set_regs(arch_cpu, regs_index) } + fn put_register(&self, cpu: Arc) -> Result<()> { + self.arch_put_register(cpu)?; + + Ok(()) + } + fn reset_vcpu(&self, cpu: Arc) -> Result<()> { self.arch_reset_vcpu(cpu)?; @@ -581,7 +575,7 @@ impl CPUHypervisorOps for KvmCpu { cpu_thread_worker.thread_cpu.set_tid(); #[cfg(not(test))] - self.reset_vcpu(cpu_thread_worker.thread_cpu.clone())?; + self.put_register(cpu_thread_worker.thread_cpu.clone())?; // Wait for all vcpu to complete the running // environment initialization. @@ -698,19 +692,6 @@ impl CPUHypervisorOps for KvmCpu { cvar.notify_one(); Ok(()) } - - fn reset(&self, task: Arc>>>) -> Result<()> { - let task = task.lock().unwrap(); - match task.as_ref() { - Some(thread) => thread - .kill(VCPU_RESET_SIGNAL) - .with_context(|| CpuError::KickVcpu("Fail to reset vcpu".to_string())), - None => { - warn!("VCPU thread not started, no need to reset"); - Ok(()) - } - } - } } struct KVMInterruptManager { @@ -1041,7 +1022,7 @@ mod test { // test setup special registers let cpu_caps = CPUCaps::init_capabilities(); - assert!(hypervisor_cpu.reset_vcpu(Arc::new(cpu)).is_ok()); + assert!(hypervisor_cpu.put_register(Arc::new(cpu)).is_ok()); let x86_sregs = hypervisor_cpu.fd.get_sregs().unwrap(); assert_eq!(x86_sregs.cs, code_seg); assert_eq!(x86_sregs.ds, data_seg); diff --git a/hypervisor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs index cc9ceade1..b574b850a 100644 --- a/hypervisor/src/kvm/x86_64/mod.rs +++ b/hypervisor/src/kvm/x86_64/mod.rs @@ -227,7 +227,7 @@ impl KvmCpu { Ok(()) } - pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { + pub fn arch_put_register(&self, cpu: Arc) -> Result<()> { let locked_arch_cpu = cpu.arch_cpu.lock().unwrap(); let apic_id = locked_arch_cpu.apic_id; @@ -287,4 +287,9 @@ impl KvmCpu { Ok(()) } + + pub fn arch_reset_vcpu(&self, cpu: Arc) -> Result<()> { + cpu.arch_cpu.lock().unwrap().set(&cpu.boot_state()); + self.arch_put_register(cpu) + } } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index af5a7304d..1851d89b8 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -207,13 +207,10 @@ impl StdMachine { cpu.pause() .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; - cpu.set_to_boot_state(); + cpu.hypervisor_cpu.reset_vcpu(cpu.clone())?; if cpu_index == 0 { fdt_addr = cpu.arch().lock().unwrap().core_regs().regs.regs[0]; } - cpu.hypervisor_cpu() - .vcpu_init() - .with_context(|| "Failed to init vcpu")?; } locked_vm diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 0000918cc..dc46e3859 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -184,7 +184,7 @@ impl StdMachine { cpu.pause() .with_context(|| format!("Failed to pause vcpu{}", cpu_index))?; - cpu.set_to_boot_state(); + cpu.hypervisor_cpu.reset_vcpu(cpu.clone())?; } locked_vm @@ -200,8 +200,6 @@ impl StdMachine { } for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { - cpu.reset() - .with_context(|| format!("Failed to reset vcpu{}", cpu_index))?; cpu.resume() .with_context(|| format!("Failed to resume vcpu{}", cpu_index))?; } -- Gitee From 04e55be4917e016c0e19c1edd633a6eebf47ed2f Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 31 Jan 2024 20:30:27 +0800 Subject: [PATCH 1620/2187] OHUI: Add basic definations Add basic definations of OHUI server module. OHUI server as UDS's server end, process UI related message (such as mouse & keyboard event). Signed-off-by: zhanghan64 --- Cargo.toml | 1 + docs/config_guidebook.md | 17 ++++ machine/Cargo.toml | 1 + ui/src/gtk/mod.rs | 4 +- ui/src/input.rs | 11 ++- ui/src/keycode.rs | 131 ++++++++++++++++++++++++++- ui/src/lib.rs | 2 + ui/src/ohui_srv/mod.rs | 14 +++ ui/src/ohui_srv/msg.rs | 188 +++++++++++++++++++++++++++++++++++++++ ui/src/vnc/mod.rs | 4 +- util/src/unix.rs | 16 ++++ 11 files changed, 381 insertions(+), 8 deletions(-) create mode 100755 ui/src/ohui_srv/mod.rs create mode 100755 ui/src/ohui_srv/msg.rs diff --git a/Cargo.toml b/Cargo.toml index 5ddecb423..c3593a065 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ usb_camera_v4l2 = ["machine/usb_camera_v4l2"] gtk = ["machine/gtk"] vnc = ["machine/vnc"] vnc_auth = ["machine/vnc_auth"] +ohui_srv = ["machine/ohui_srv"] ramfb = ["machine/ramfb"] virtio_gpu = ["machine/virtio_gpu"] trace_to_logger = ["trace/trace_to_logger"] diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 5148cdb2d..2099b3bb4 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -969,6 +969,8 @@ It determines the order of bootable devices which firmware will use for booting Multiple display methods are supported by stratovirt, including `GTK` and `VNC`, which allows users to interact with virtual machine. +Display on OpenHarmony OS(OHOS) is also supported, while a client program need to be implemented. + #### 2.16.1 GTK Graphical interface drawn by gtk toolkits. Visit [GTK](https://www.gtk.org) for more details. @@ -1040,6 +1042,21 @@ Note: 1. Only one client can be connected at the same time. Follow-up clients co Please see the [4. Build with features](docs/build_guide.md) if you want to enable VNC. +#### 2.16.2 OHUI server + +OHUI server support display on OHOS. It relies on UDS for communication with OHUI client. Basically speaking, it works like VNC. +Client gets keyboard and mouse's action and sends it to server, and also draws VM's image on screen. +Server processes keyboard and mouse's action, and transfer VM's image. + +Sample Configuration: + +```shell +[-object iothread,id=] +-display ohui[,iothread=,socks-path=] +``` + +Note: "socks-path" specifies where UDS file is. It's "/tmp" by default. + ### 2.17 Virtio-fs Virtio-fs is a shared file system that lets virtual machines access a directory tree on the host. Unlike existing approaches, it is designed to offer local file system semantics and performance. diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 0072b862b..38c374f61 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -46,5 +46,6 @@ windows_emu_pid = ["ui/console", "machine_manager/windows_emu_pid"] gtk = ["windows_emu_pid", "ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] vnc_auth = ["vnc"] +ohui_srv = ["ui/ohui_srv"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 6354b862b..ced3792bd 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -50,7 +50,7 @@ use crate::{ DEFAULT_SURFACE_WIDTH, DISPLAY_UPDATE_INTERVAL_DEFAULT, }, gtk::{draw::set_callback_for_draw_area, menu::GtkMenu}, - keycode::KeyCode, + keycode::{DpyMod, KeyCode}, pixman::{ create_pixman_image, get_image_data, get_image_height, get_image_width, ref_pixman_image, unref_pixman_image, @@ -235,7 +235,7 @@ impl GtkDisplay { free_scale: true, })); // Mapping ASCII to keycode. - let keysym2keycode = Rc::new(RefCell::new(KeyCode::keysym_to_qkeycode())); + let keysym2keycode = Rc::new(RefCell::new(KeyCode::keysym_to_qkeycode(DpyMod::Gtk))); Self { gtk_menu, scale_mode, diff --git a/ui/src/input.rs b/ui/src/input.rs index b2d2f3a2a..cfad190d8 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -422,6 +422,13 @@ pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { .keyboard_state_update(keycode, down) } +pub fn keyboard_update(down: bool, keycode: u16) -> Result<()> { + let mut locked_input = INPUTS.lock().unwrap(); + locked_input + .keyboard_state + .keyboard_state_update(keycode, down) +} + /// Release all pressed key. pub fn release_all_key() -> Result<()> { let mut locked_input = INPUTS.lock().unwrap(); @@ -481,7 +488,7 @@ mod tests { use anyhow::bail; #[cfg(feature = "keycode")] - use crate::keycode::KeyCode; + use crate::keycode::{DpyMod, KeyCode}; static TEST_INPUT: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(TestInput::default()))); @@ -612,7 +619,7 @@ mod tests { test_input.register_input(); let test_kdb = test_input.kbd.clone(); - let keysym2qkeycode = KeyCode::keysym_to_qkeycode(); + let keysym2qkeycode = KeyCode::keysym_to_qkeycode(DpyMod::Gtk); // ["0", "a", "space"] let keysym_lists: Vec = vec![0x0030, 0x0061, 0x0020]; let keycode_lists: Vec = keysym_lists diff --git a/ui/src/keycode.rs b/ui/src/keycode.rs index 507f5a1d8..18bbaa7c5 100644 --- a/ui/src/keycode.rs +++ b/ui/src/keycode.rs @@ -12,7 +12,18 @@ use std::collections::HashMap; +pub enum DpyMod { + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + Ohui, + #[cfg(feature = "gtk")] + Gtk, + #[cfg(feature = "vnc")] + Vnc, +} + +#[allow(unused)] #[derive(Clone, Copy, Debug)] +// Some of KeyCodes are not used on OHOS. pub enum KeyCode { Escape, Key1, @@ -368,10 +379,16 @@ impl KeyCode { } } - pub fn keysym_to_qkeycode() -> HashMap { + pub fn keysym_to_qkeycode(mode: DpyMod) -> HashMap { let mut keysym2qkeycode: HashMap = HashMap::new(); + let keycodes = match mode { + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + DpyMod::Ohui => KEY_CODE_OH.as_ref(), + #[cfg(any(feature = "gtk", feature = "vnc"))] + _ => KEY_CODE_ASCLL.as_ref(), + }; // Mapping ASCII to keycode. - for &(keycode, keysym) in KEY_CODE_ASCLL.iter() { + for &(keycode, keysym) in keycodes.iter() { let qkeycode = keycode.to_key_num(); keysym2qkeycode.insert(keysym, qkeycode); } @@ -379,6 +396,116 @@ impl KeyCode { } } +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +const KEY_CODE_OH: [(KeyCode, u16); 105] = [ + (KeyCode::Key0, 0x07D0), + (KeyCode::Key1, 0x07D1), + (KeyCode::Key2, 0x07D2), + (KeyCode::Key3, 0x07D3), + (KeyCode::Key4, 0x07D4), + (KeyCode::Key5, 0x07D5), + (KeyCode::Key6, 0x07D6), + (KeyCode::Key7, 0x07D7), + (KeyCode::Key8, 0x07D8), + (KeyCode::Key9, 0x07D9), + (KeyCode::Minus, 0x0809), + (KeyCode::Equal, 0x080A), + (KeyCode::BackSpace, 0x0807), + (KeyCode::Tab, 0x0801), + (KeyCode::Keya, 0x07E1), + (KeyCode::Keyb, 0x07E2), + (KeyCode::Keyc, 0x07E3), + (KeyCode::Keyd, 0x07E4), + (KeyCode::Keye, 0x07E5), + (KeyCode::Keyf, 0x07E6), + (KeyCode::Keyg, 0x07E7), + (KeyCode::Keyh, 0x07E8), + (KeyCode::Keyi, 0x07E9), + (KeyCode::Keyj, 0x07EA), + (KeyCode::Keyk, 0x07EB), + (KeyCode::Keyl, 0x07EC), + (KeyCode::Keym, 0x07ED), + (KeyCode::Keyn, 0x07EE), + (KeyCode::Keyo, 0x07EF), + (KeyCode::Keyp, 0x07F0), + (KeyCode::Keyq, 0x07F1), + (KeyCode::Keyr, 0x07F2), + (KeyCode::Keys, 0x07F3), + (KeyCode::Keyt, 0x07F4), + (KeyCode::Keyu, 0x07F5), + (KeyCode::Keyv, 0x07F6), + (KeyCode::Keyw, 0x07F7), + (KeyCode::Keyx, 0x07F8), + (KeyCode::Keyy, 0x07F9), + (KeyCode::Keyz, 0x07FA), + (KeyCode::Space, 0x0802), + (KeyCode::Comma, 0x07FB), + (KeyCode::Slash, 0x0810), + (KeyCode::Down, 0x07DD), + (KeyCode::Left, 0x07DE), + (KeyCode::End, 0x0822), + (KeyCode::Escape, 0x0816), + (KeyCode::Period, 0x07FC), + (KeyCode::Up, 0x07DC), + (KeyCode::Apostrophe, 0x080F), + (KeyCode::Semicolon, 0x080E), + (KeyCode::BackSlash, 0x080D), + (KeyCode::Braceleft, 0x080B), + (KeyCode::Braceright, 0x080C), + (KeyCode::AltR, 0x07FE), + (KeyCode::Return, 0x0806), + (KeyCode::Grave, 0x0808), + (KeyCode::Home, 0x0821), + (KeyCode::SysReq, 0x081F), + (KeyCode::Right, 0x07DF), + (KeyCode::Menu, 0x09A2), + (KeyCode::Prior, 0x0814), + (KeyCode::Insert, 0x0823), + (KeyCode::NumLock, 0x0836), + (KeyCode::Next, 0x0815), + (KeyCode::KPAdd, 0x0844), + (KeyCode::KPMultiply, 0x0842), + (KeyCode::KPEnter, 0x0847), + (KeyCode::Pause, 0x0820), + (KeyCode::ScrollLock, 0x081B), + (KeyCode::SuperL, 0x081C), + (KeyCode::SuperR, 0x081D), + (KeyCode::KPDecimal, 0x0845), + (KeyCode::KPSubtract, 0x0843), + (KeyCode::KPDivide, 0x0841), + (KeyCode::KP0, 0x0837), + (KeyCode::KP1, 0x0838), + (KeyCode::KP2, 0x0839), + (KeyCode::KP3, 0x083A), + (KeyCode::KP4, 0x083B), + (KeyCode::KP5, 0x083C), + (KeyCode::KP6, 0x083D), + (KeyCode::KP7, 0x083E), + (KeyCode::KP8, 0x083F), + (KeyCode::KP9, 0x0840), + (KeyCode::KPEqual, 0x0848), + (KeyCode::F1, 0x082A), + (KeyCode::F2, 0x082B), + (KeyCode::F3, 0x082C), + (KeyCode::F4, 0x082D), + (KeyCode::F5, 0x082E), + (KeyCode::F6, 0x082F), + (KeyCode::F7, 0x0830), + (KeyCode::F8, 0x0831), + (KeyCode::F9, 0x0832), + (KeyCode::F10, 0x0833), + (KeyCode::F11, 0x0834), + (KeyCode::F12, 0x0835), + (KeyCode::ShiftL, 0x7FF), + (KeyCode::ShiftR, 0x0800), + (KeyCode::ControlL, 0x0818), + (KeyCode::ControlR, 0x0819), + (KeyCode::CapsLock, 0x081A), + (KeyCode::AltL, 0x07FD), + (KeyCode::Delete, 0x0817), +]; + +#[cfg(any(feature = "gtk", feature = "vnc"))] const KEY_CODE_ASCLL: [(KeyCode, u16); 173] = [ (KeyCode::Space, 0x0020), (KeyCode::Exclam, 0x0021), diff --git a/ui/src/lib.rs b/ui/src/lib.rs index a81b84e2c..fdc805610 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -18,6 +18,8 @@ pub mod gtk; pub mod input; #[cfg(feature = "keycode")] mod keycode; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +pub mod ohui_srv; #[cfg(feature = "pixman")] pub mod pixman; pub mod utils; diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs new file mode 100755 index 000000000..723b20f7a --- /dev/null +++ b/ui/src/ohui_srv/mod.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// Stratovirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod channel; +pub mod msg; diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs new file mode 100755 index 000000000..e61270dda --- /dev/null +++ b/ui/src/ohui_srv/msg.rs @@ -0,0 +1,188 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// Stratovirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem::size_of; + +use util::byte_code::ByteCode; + +pub const CLIENT_FOCUSOUT_EVENT: u32 = 0x1; +pub const CLIENT_PRESS_BTN: u32 = 0x1; +pub const CLIENT_RELEASE_BTN: u32 = 0x0; +pub const CLIENT_WHEEL_UP: u32 = 0x1; +pub const CLIENT_WHEEL_DOWN: u32 = 0x2; +pub const CLIENT_WHEEL_LEFT: u32 = 0x3; +pub const CLIENT_WHEEL_RIGHT: u32 = 0x4; +pub const EVENT_MSG_HDR_SIZE: u32 = size_of::() as u32; + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub enum EventType { + WindowInfo, + MouseButton, + MouseMotion, + Keyboard, + Scroll, + Ledstate, + FrameBufferDirty, + Greet, + CursorDefine, + Focus, + VmCtrlInfo, + #[default] + Max, +} + +impl ByteCode for EventType {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct WindowInfoEvent { + pub width: u32, + pub height: u32, +} + +impl ByteCode for WindowInfoEvent {} + +impl WindowInfoEvent { + pub fn new(width: u32, height: u32) -> Self { + WindowInfoEvent { width, height } + } +} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct HWCursorEvent { + pub w: u32, + pub h: u32, + pub hot_x: u32, + pub hot_y: u32, + pub size_per_pixel: u32, +} + +impl HWCursorEvent { + pub fn new(w: u32, h: u32, hot_x: u32, hot_y: u32, size_per_pixel: u32) -> Self { + HWCursorEvent { + w, + h, + hot_x, + hot_y, + size_per_pixel, + } + } +} + +impl ByteCode for HWCursorEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct MouseButtonEvent { + pub button: u32, + pub btn_action: u32, +} + +impl ByteCode for MouseButtonEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct MouseMotionEvent { + pub x: f64, + pub y: f64, +} + +impl ByteCode for MouseMotionEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct KeyboardEvent { + pub key_action: u16, + pub keycode: u16, +} + +impl ByteCode for KeyboardEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct ScrollEvent { + pub direction: u32, + pub delta_x: f64, + pub delta_y: f64, +} + +impl ByteCode for ScrollEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct LedstateEvent { + pub state: u32, +} + +impl ByteCode for LedstateEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FocusEvent { + pub state: u32, +} + +impl ByteCode for FocusEvent {} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FrameBufferDirtyEvent { + x: u32, + y: u32, + w: u32, + h: u32, +} + +impl ByteCode for FrameBufferDirtyEvent {} + +impl FrameBufferDirtyEvent { + pub fn new(x: u32, y: u32, w: u32, h: u32) -> Self { + FrameBufferDirtyEvent { x, y, w, h } + } +} + +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct EventMsgHdr { + pub magic: u32, + pub size: u32, + pub event_type: EventType, +} + +impl ByteCode for EventMsgHdr {} + +impl EventMsgHdr { + pub fn new(event_type: EventType) -> EventMsgHdr { + EventMsgHdr { + magic: 0, + size: event_msg_data_len(event_type) as u32, + event_type, + } + } +} + +pub fn event_msg_data_len(event_type: EventType) -> usize { + match event_type { + EventType::WindowInfo => size_of::(), + EventType::MouseButton => size_of::(), + EventType::MouseMotion => size_of::(), + EventType::Keyboard => size_of::(), + EventType::Scroll => size_of::(), + EventType::Focus => size_of::(), + EventType::FrameBufferDirty => size_of::(), + EventType::CursorDefine => size_of::(), + EventType::Ledstate => size_of::(), + _ => 0, + } +} diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 271352f11..9f0a7e93d 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -37,7 +37,7 @@ use crate::{ DISPLAY_UPDATE_INTERVAL_DEFAULT, DISPLAY_UPDATE_INTERVAL_INC, DISPLAY_UPDATE_INTERVAL_MAX, }, error::VncError, - keycode::KeyCode, + keycode::{DpyMod, KeyCode}, pixman::{ bytes_per_pixel, create_pixman_image, get_image_data, get_image_height, get_image_stride, get_image_width, ref_pixman_image, unref_pixman_image, @@ -304,7 +304,7 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { .expect("Set noblocking for vnc socket failed"); // Mapping ASCII to keycode. - let keysym2keycode = KeyCode::keysym_to_qkeycode(); + let keysym2keycode = KeyCode::keysym_to_qkeycode(DpyMod::Vnc); let vnc_opts = Arc::new(VncInterface::default()); let dcl = Arc::new(Mutex::new(DisplayChangeListener::new(None, vnc_opts))); diff --git a/util/src/unix.rs b/util/src/unix.rs index 943cb8b83..d71e3c826 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -223,6 +223,22 @@ impl UnixSock { Ok(()) } + pub fn listen_set_nonblocking(&self, nonblocking: bool) -> Result<()> { + self.listener + .as_ref() + .unwrap() + .set_nonblocking(nonblocking) + .with_context(|| "couldn't set nonblocking for unix sock listener") + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { + self.sock + .as_ref() + .unwrap() + .set_nonblocking(nonblocking) + .with_context(|| "couldn't set nonblocking") + } + /// Get Stream's fd from `UnixSock`. pub fn get_stream_raw_fd(&self) -> RawFd { self.sock.as_ref().unwrap().as_raw_fd() -- Gitee From facf39367e7512a87643cdc9f0e1acc933149e51 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sun, 4 Feb 2024 14:30:18 +0800 Subject: [PATCH 1621/2187] OHUI: Add basic OHUI logic Add basic OHUI process logic Signed-off-by: zhanghan64 --- ui/src/ohui_srv/mod.rs | 486 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 723b20f7a..ec053772a 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -12,3 +12,489 @@ pub mod channel; pub mod msg; +pub mod msg_handle; + +use std::mem::size_of; +use std::os::unix::io::RawFd; +use std::path::Path; +use std::ptr; +use std::rc::Rc; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, RwLock, +}; + +use anyhow::{anyhow, Result}; +use log::{error, info}; +use once_cell::sync::OnceCell; +use vmm_sys_util::epoll::EventSet; + +use crate::{ + console::{ + graphic_hardware_update, register_display, DisplayChangeListener, + DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, + DISPLAY_UPDATE_INTERVAL_DEFAULT, + }, + pixman::{bytes_per_pixel, get_image_data, ref_pixman_image, unref_pixman_image}, +}; +use address_space::FileBackend; +use channel::*; +use machine_manager::{ + config::{DisplayConfig, VIRTIO_GPU_ENABLE_BAR0_SIZE}, + event_loop::register_event_helper, + temp_cleaner::TempCleaner, +}; +use msg_handle::*; +use util::{ + loop_context::{ + gen_delete_notifiers, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, + }, + pixman::{pixman_format_code_t, pixman_image_t}, + unix::do_mmap, +}; + +#[derive(Debug, Clone)] +struct GuestSurface { + /// Image from display device. + guest_image: *mut pixman_image_t, + /// Image format of pixman. + guest_format: pixman_format_code_t, + stride: i32, + width: i32, + height: i32, +} + +// SAFETY: Send and Sync is not auto-implemented for `*mut pixman_image_t` type. +// implementing them is safe because GuestSurface will be protected by +// RwLock +unsafe impl Sync for GuestSurface {} +// SAFETY: Same as 'Sync for GuestSurface' +unsafe impl Send for GuestSurface {} + +impl GuestSurface { + fn new() -> GuestSurface { + GuestSurface { + guest_image: ptr::null_mut::(), + guest_format: pixman_format_code_t::PIXMAN_x8r8g8b8, + stride: 0, + width: 0, + height: 0, + } + } +} + +const CURSOR_SIZE: u64 = 16 * 1024; + +pub struct OhUiServer { + // framebuffer passthru to the guest + passthru: OnceCell, + // guest surface for framebuffer + surface: RwLock, + // transfer channel via unix sock + channel: Arc, + // message handler + msg_handler: OhUiMsgHandler, + // connected or not + connected: AtomicBool, + // iothread processing unix socket + iothread: OnceCell>, + // address of cursor buffer + cursorbuffer: u64, + //address of framebuffer + framebuffer: u64, + // framebuffer file backend + fb_file: Option, +} + +impl OhUiServer { + fn init_channel(path: &String) -> Result> { + let file_path = Path::new(path.as_str()).join("ohui.sock"); + let sock_file = file_path + .to_str() + .ok_or_else(|| anyhow!("init_channel: Failed to get str from {}", path))?; + TempCleaner::add_path(sock_file.to_string()); + Ok(Arc::new(OhUiChannel::new(sock_file))) + } + + fn init_fb_file(path: &String) -> Result<(Option, u64)> { + let file_path = Path::new(path.as_str()).join("ohui-fb"); + let fb_file = file_path + .to_str() + .ok_or_else(|| anyhow!("init_fb_file: Failed to get str from {}", path))?; + let fb_backend = FileBackend::new_mem(fb_file, VIRTIO_GPU_ENABLE_BAR0_SIZE)?; + TempCleaner::add_path(fb_file.to_string()); + + let host_addr = do_mmap( + &Some(fb_backend.file.as_ref()), + VIRTIO_GPU_ENABLE_BAR0_SIZE, + 0, + false, + true, + false, + )?; + + Ok((Some(fb_backend), host_addr)) + } + + fn init_cursor_file(path: &String) -> Result { + let file_path = Path::new(path.as_str()).join("ohui-cursor"); + let cursor_file = file_path + .to_str() + .ok_or_else(|| anyhow!("init_cursor_file: Failed to get str from {}", path))?; + let cursor_backend = FileBackend::new_mem(cursor_file, CURSOR_SIZE)?; + TempCleaner::add_path(cursor_file.to_string()); + + let cursorbuffer = do_mmap( + &Some(cursor_backend.file.as_ref()), + CURSOR_SIZE, + 0, + false, + true, + false, + )?; + + Ok(cursorbuffer) + } + + pub fn new(path: String) -> Result { + let channel = Self::init_channel(&path)?; + let (fb_file, framebuffer) = Self::init_fb_file(&path)?; + let cursorbuffer = Self::init_cursor_file(&path)?; + + Ok(OhUiServer { + passthru: OnceCell::new(), + surface: RwLock::new(GuestSurface::new()), + channel: channel.clone(), + msg_handler: OhUiMsgHandler::new(channel), + connected: AtomicBool::new(false), + iothread: OnceCell::new(), + cursorbuffer, + framebuffer, + fb_file, + }) + } + + pub fn set_passthru(&self, passthru: bool) { + self.passthru + .set(passthru) + .unwrap_or_else(|_| error!("Failed to initialize passthru of OHUI Server.")); + } + + #[inline(always)] + fn get_channel(&self) -> &OhUiChannel { + self.channel.as_ref() + } + + #[inline(always)] + pub fn get_ohui_fb(&self) -> Option { + self.fb_file.clone() + } + + fn handle_recv(&self) -> Result<()> { + if !self.connected() { + return Err(anyhow!("connection has not establish".to_string())); + } + self.msg_handler.handle_msg() + } + + fn raw_update_dirty_area( + &self, + surface_data: *mut u32, + stride: i32, + pos: (i32, i32), + size: (i32, i32), + ) { + let (x, y) = pos; + let (w, h) = size; + + if self.framebuffer == 0 || *self.passthru.get_or_init(|| false) { + return; + } + + let offset = (x * bytes_per_pixel() as i32 + y * stride) as u64; + let mut src_ptr = surface_data as u64 + offset; + let mut dst_ptr = self.framebuffer + offset; + + for _ in 0..h { + // SAFETY: it can be ensure the raw pointer will not exceed the range. + unsafe { + ptr::copy_nonoverlapping( + src_ptr as *const u8, + dst_ptr as *mut u8, + w as usize * bytes_per_pixel(), + ); + } + src_ptr += stride as u64; + dst_ptr += stride as u64; + } + } + + fn send_window_info(&self) { + let locked_surface = self.surface.read().unwrap(); + + if locked_surface.guest_image.is_null() { + return; + } + + self.msg_handler + .send_windowinfo(locked_surface.width as u32, locked_surface.height as u32); + } + + #[inline(always)] + fn connected(&self) -> bool { + self.connected.load(Ordering::Relaxed) + } + + #[inline(always)] + fn set_connect(&self, conn: bool) { + self.connected.store(conn, Ordering::Relaxed); + } + + fn set_iothread(&self, iothread: Option) { + if self.iothread.set(iothread).is_err() { + error!("Failed to initialize iothread of OHUI Server."); + } + } +} + +impl DisplayChangeListenerOperations for OhUiServer { + fn dpy_switch(&self, surface: &DisplaySurface) -> Result<()> { + let mut locked_surface = self.surface.write().unwrap(); + + unref_pixman_image(locked_surface.guest_image); + + locked_surface.guest_image = ref_pixman_image(surface.image); + locked_surface.guest_format = surface.format; + locked_surface.stride = surface.stride(); + locked_surface.width = surface.width(); + locked_surface.height = surface.height(); + drop(locked_surface); + let locked_surface = self.surface.read().unwrap(); + self.raw_update_dirty_area( + get_image_data(locked_surface.guest_image), + locked_surface.stride, + (0, 0), + (locked_surface.width, locked_surface.height), + ); + + if !self.connected() { + return Ok(()); + } + self.msg_handler + .send_windowinfo(locked_surface.width as u32, locked_surface.height as u32); + Ok(()) + } + + fn dpy_refresh(&self, dcl: &Arc>) -> Result<()> { + let con_id = dcl.lock().unwrap().con_id; + graphic_hardware_update(con_id); + Ok(()) + } + + fn dpy_image_update(&self, x: i32, y: i32, w: i32, h: i32) -> Result<()> { + if !self.connected() { + return Ok(()); + } + + let locked_surface = self.surface.read().unwrap(); + if locked_surface.guest_image.is_null() { + return Ok(()); + } + + self.raw_update_dirty_area( + get_image_data(locked_surface.guest_image), + locked_surface.stride, + (x, y), + (w, h), + ); + + self.msg_handler + .handle_dirty_area(x as u32, y as u32, w as u32, h as u32); + Ok(()) + } + + fn dpy_cursor_update(&self, cursor: &DisplayMouse) -> Result<()> { + if self.cursorbuffer == 0 { + error!("Hwcursor not set."); + // No need to return Err for this situation is not fatal + return Ok(()); + } + + let len = cursor.width * cursor.height * size_of::() as u32; + if len > CURSOR_SIZE as u32 { + error!("Too large cursor length {}.", len); + // No need to return Err for this situation is not fatal + return Ok(()); + } + + // SAFETY: len is checked before copying,it's safe to do this. + unsafe { + ptr::copy_nonoverlapping( + cursor.data.as_ptr(), + self.cursorbuffer as *mut u8, + len as usize, + ); + } + + self.msg_handler.handle_cursor_define( + cursor.width, + cursor.height, + cursor.hot_x, + cursor.hot_y, + size_of::() as u32, + ); + Ok(()) + } +} + +pub fn ohui_init(ohui_srv: Arc, cfg: &DisplayConfig) -> Result<()> { + // set iothread + ohui_srv.set_iothread(cfg.ohui_config.iothread.clone()); + // Register ohui interface + let dcl = Arc::new(Mutex::new(DisplayChangeListener::new( + None, + ohui_srv.clone(), + ))); + dcl.lock().unwrap().update_interval = DISPLAY_UPDATE_INTERVAL_DEFAULT; + register_display(&dcl)?; + // start listener + ohui_start_listener(ohui_srv) +} + +struct OhUiTrans { + server: Arc, +} + +impl OhUiTrans { + pub fn new(server: Arc) -> Self { + OhUiTrans { server } + } + + fn handle_disconnect(&self) { + self.server.set_connect(false); + if let Err(e) = ohui_start_listener(self.server.clone()) { + error!("Failed to restart listener: {:?}.", e) + } + } + + fn handle_recv(&self) -> Result<()> { + self.server.handle_recv() + } + + fn get_fd(&self) -> RawFd { + self.server.get_channel().get_stream_raw_fd() + } +} + +impl EventNotifierHelper for OhUiTrans { + fn internal_notifiers(trans: Arc>) -> Vec { + let trans_ref = trans.clone(); + let handler: Rc = Rc::new(move |event: EventSet, fd: RawFd| { + if event & EventSet::HANG_UP == EventSet::HANG_UP { + error!("OhUiTrans: disconnected."); + trans_ref.lock().unwrap().handle_disconnect(); + // Delete stream notifiers + return Some(gen_delete_notifiers(&[fd])); + } else if event & EventSet::IN == EventSet::IN { + let locked_trans = trans_ref.lock().unwrap(); + // Handle incoming data + if let Err(e) = locked_trans.handle_recv() { + error!("{}.", e); + locked_trans.handle_disconnect(); + return Some(gen_delete_notifiers(&[fd])); + } + } + None + }); + + vec![EventNotifier::new( + NotifierOperation::AddShared, + trans.lock().unwrap().get_fd(), + None, + EventSet::IN | EventSet::HANG_UP, + vec![handler], + )] + } +} + +struct OhUiListener { + server: Arc, +} + +impl OhUiListener { + fn new(server: Arc) -> Self { + OhUiListener { server } + } + + fn handle_connection(&self) -> Result<()> { + // Set stream sock with nonblocking + self.server.get_channel().set_nonblocking(true)?; + // Register OhUiTrans read notifier + ohui_register_event(OhUiTrans::new(self.server.clone()), self.server.clone())?; + self.server.set_connect(true); + // Send window info to the client + self.server.send_window_info(); + Ok(()) + } + + fn accept(&self) -> Result<()> { + self.server.get_channel().accept() + } + + fn get_fd(&self) -> RawFd { + self.server.get_channel().get_listener_raw_fd() + } +} + +impl EventNotifierHelper for OhUiListener { + fn internal_notifiers(listener: Arc>) -> Vec { + let listener_ref = listener.clone(); + let handler: Rc = Rc::new(move |_event: EventSet, fd: RawFd| { + let locked_listener = listener_ref.lock().unwrap(); + match locked_listener.accept() { + Ok(()) => match locked_listener.handle_connection() { + Ok(()) => info!("New connection accepted."), + Err(e) => { + error!("Failed to start connection and going to restart listening {e}."); + return None; + } + }, + Err(e) => { + error!("Accept failed: {:?}.", e); + return None; + } + } + // Only support one connection so remove listener + Some(gen_delete_notifiers(&[fd])) + }); + + vec![EventNotifier::new( + NotifierOperation::AddShared, + listener.lock().unwrap().get_fd(), + None, + EventSet::IN, + vec![handler], + )] + } +} + +fn ohui_register_event(e: T, srv: Arc) -> Result<()> { + let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(e))); + let mut evts: Vec = Vec::new(); + register_event_helper( + notifiers, + srv.iothread.get_or_init(|| None).as_ref(), + &mut evts, + )? +} + +fn ohui_start_listener(server: Arc) -> Result<()> { + // Bind and set listener nonblocking + let channel = server.get_channel(); + channel.bind()?; + channel.set_listener_nonblocking(true)?; + ohui_register_event(OhUiListener::new(server.clone()), server.clone())?; + info!("Successfully start listener."); + Ok(()) +} -- Gitee From 7cab0f98de1a698ebe1d1dcdbd23260a0cb52542 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 1 Feb 2024 15:10:43 +0800 Subject: [PATCH 1622/2187] OHUI: enable OHUI server enable OHUI server on OHOS Signed-off-by: zhanghan64 --- Cargo.lock | 1 + machine/Cargo.toml | 2 +- machine/src/aarch64/standard.rs | 43 ++- machine/src/lib.rs | 21 ++ machine_manager/Cargo.toml | 1 + machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/display.rs | 63 ++++- machine_manager/src/config/gpu.rs | 3 + machine_manager/src/config/mod.rs | 6 +- ui/Cargo.toml | 2 + ui/src/input.rs | 24 +- ui/src/ohui_srv/mod.rs | 2 +- ui/src/ohui_srv/msg_handle.rs | 365 ++++++++++++++++++++++++++ virtio/Cargo.toml | 1 + virtio/src/device/gpu.rs | 16 +- virtio/src/transport/virtio_pci.rs | 19 +- 16 files changed, 546 insertions(+), 25 deletions(-) create mode 100755 ui/src/ohui_srv/msg_handle.rs diff --git a/Cargo.lock b/Cargo.lock index 324b69ae2..c258dfea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1744,6 +1744,7 @@ dependencies = [ name = "ui" version = "2.3.0" dependencies = [ + "address_space", "anyhow", "bitintr", "cairo-rs", diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 38c374f61..f3f0966f1 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -46,6 +46,6 @@ windows_emu_pid = ["ui/console", "machine_manager/windows_emu_pid"] gtk = ["windows_emu_pid", "ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] vnc_auth = ["vnc"] -ohui_srv = ["ui/ohui_srv"] +ohui_srv = ["ui/ohui_srv", "machine_manager/ohui_srv", "virtio/ohui_srv"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index af5a7304d..0608a944b 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -34,6 +34,8 @@ use acpi::{ ARCH_GIC_MAINT_IRQ, ID_MAPPING_ENTRY_SIZE, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, ROOT_COMPLEX_ENTRY_SIZE, }; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use address_space::FileBackend; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::{CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE}; @@ -69,6 +71,8 @@ use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_ use migration::{MigrationManager, MigrationStatus}; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use ui::ohui_srv::{ohui_init, OhUiServer}; #[cfg(feature = "vnc")] use ui::vnc::vnc_init; use util::byte_code::ByteCode; @@ -149,6 +153,9 @@ pub struct StdMachine { dtb_vec: Vec, /// List contains the boot order of boot devices. boot_order_list: Arc>>, + /// OHUI server + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + ohui_server: Option>, } impl StdMachine { @@ -196,6 +203,8 @@ impl StdMachine { ), dtb_vec: Vec::new(), boot_order_list: Arc::new(Mutex::new(Vec::new())), + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + ohui_server: None, }) } @@ -327,6 +336,17 @@ impl StdMachine { } None } + + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn add_ohui_server(&mut self, vm_config: &VmConfig) -> Result<()> { + if let Some(dpy) = vm_config.display.as_ref() { + if !dpy.ohui_config.ohui { + return Ok(()); + } + self.ohui_server = Some(Arc::new(OhUiServer::new(dpy.get_ui_path())?)); + } + Ok(()) + } } impl StdMachineOps for StdMachine { @@ -524,6 +544,17 @@ impl MachineOps for StdMachine { syscall_whitelist() } + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn update_ohui_srv(&mut self, passthru: bool) { + self.ohui_server.as_ref().unwrap().set_passthru(passthru); + } + + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn get_ohui_fb(&self) -> Option { + let server = self.ohui_server.as_ref()?; + server.get_ohui_fb() + } + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let mut locked_vm = vm.lock().unwrap(); @@ -587,6 +618,9 @@ impl MachineOps for StdMachine { locked_vm.cpu_post_init(&cpu_config)?; + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + locked_vm.add_ohui_server(vm_config)?; + locked_vm .add_devices(vm_config) .with_context(|| "Failed to add devices")?; @@ -678,8 +712,9 @@ impl MachineOps for StdMachine { #[allow(unused_variables)] fn display_init(&mut self, vm_config: &mut VmConfig) -> Result<()> { // GTK display init. - #[cfg(feature = "gtk")] + #[cfg(any(feature = "gtk", all(target_env = "ohos", feature = "ohui_srv")))] match vm_config.display { + #[cfg(feature = "gtk")] Some(ref ds_cfg) if ds_cfg.gtk => { let ui_context = UiContext { vm_name: vm_config.guest_name.clone(), @@ -691,6 +726,12 @@ impl MachineOps for StdMachine { gtk_display_init(ds_cfg, ui_context) .with_context(|| "Failed to init GTK display!")?; } + // OHUI server init. + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + Some(ref ds_cfg) if ds_cfg.ohui_config.ohui => { + ohui_init(self.ohui_server.as_ref().unwrap().clone(), ds_cfg) + .with_context(|| "Failed to init OH UI server!")?; + } _ => {} }; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c49a43d0f..5f712e714 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -39,6 +39,8 @@ use log::warn; #[cfg(feature = "windows_emu_pid")] use vmm_sys_util::eventfd::EventFd; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use address_space::FileBackend; use address_space::{create_backend_mem, create_default_mem, AddressSpace, GuestAddress, Region}; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; @@ -99,6 +101,8 @@ use util::{ use vfio::{VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(feature = "virtio_gpu")] use virtio::Gpu; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block, BlockState, Rng, RngState, @@ -1355,12 +1359,29 @@ pub trait MachineOps { Ok(()) } + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn update_ohui_srv(&mut self, _passthru: bool) {} + + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn get_ohui_fb(&self) -> Option { + None + } + #[cfg(feature = "virtio_gpu")] fn add_virtio_pci_gpu(&mut self, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_gpu(cfg_args)?; let device = Arc::new(Mutex::new(Gpu::new(device_cfg.clone()))); + + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + if device.lock().unwrap().device_quirk() == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0) + && self.get_ohui_fb().is_some() + { + self.update_ohui_srv(true); + device.lock().unwrap().set_bar0_fb(self.get_ohui_fb()); + } + self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false)?; Ok(()) } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 8988da2dd..c47dccfac 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -35,6 +35,7 @@ usb_host = [] usb_camera = [] usb_camera_v4l2 = ["usb_camera"] windows_emu_pid = [] +ohui_srv = [] gtk = [] vnc = [] vnc_auth = [] diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index fbfd7b033..e43958b2d 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -549,7 +549,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); #[cfg(feature = "vnc")] add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); - #[cfg(feature = "gtk")] + #[cfg(any(feature = "gtk", feature = "ohui_srv"))] add_args_to_config!((args.value_of("display")), vm_cfg, add_display); #[cfg(feature = "windows_emu_pid")] add_args_to_config!( diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index bb09501a5..8f1f2e088 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -10,15 +10,23 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "gtk")] use std::sync::Arc; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use anyhow::Context; use anyhow::{bail, Result}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "gtk")] use vmm_sys_util::eventfd::EventFd; use crate::config::{CmdParser, ExBool, VmConfig}; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +static DEFAULT_UI_PATH: &str = "/tmp/"; + /// Event fd related to power button in gtk. +#[cfg(feature = "gtk")] pub struct UiContext { /// Name of virtual machine. pub vm_name: String, @@ -32,7 +40,18 @@ pub struct UiContext { pub resume_req: Option>, } -/// GTK related configuration. +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct OhuiConfig { + /// Use OHUI. + pub ohui: bool, + /// Create the OHUI thread. + pub iothread: Option, + /// Confirm related files' path. + pub path: String, +} + +/// GTK and OHUI related configuration. #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct DisplayConfig { /// Create the GTK thread. @@ -41,17 +60,31 @@ pub struct DisplayConfig { pub app_name: Option, /// Keep the window fill the desktop. pub full_screen: bool, + /// Used for OHUI + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + pub ohui_config: OhuiConfig, +} + +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +impl DisplayConfig { + pub fn get_ui_path(&self) -> String { + self.ohui_config.path.clone() + } } impl VmConfig { pub fn add_display(&mut self, vm_config: &str) -> Result<()> { let mut cmd_parser = CmdParser::new("display"); cmd_parser.push("").push("full-screen").push("app-name"); + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + cmd_parser.push("iothread").push("socks-path"); cmd_parser.parse(vm_config)?; let mut display_config = DisplayConfig::default(); if let Some(str) = cmd_parser.get_value::("")? { match str.as_str() { "gtk" => display_config.gtk = true, + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + "ohui" => display_config.ohui_config.ohui = true, _ => bail!("Unsupported device: {}", str), } } @@ -62,6 +95,34 @@ impl VmConfig { display_config.full_screen = default.into(); } + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + if display_config.ohui_config.ohui { + if let Some(iothread) = cmd_parser.get_value::("iothread")? { + display_config.ohui_config.iothread = Some(iothread); + } + + if let Some(path) = cmd_parser.get_value::("socks-path")? { + let path = std::fs::canonicalize(path.clone()).with_context(|| { + format!("Failed to get real directory path: {:?}", path.clone()) + })?; + if !path.exists() { + bail!( + "The defined directory {:?} path doesn't exist", + path.as_os_str() + ); + } + if !path.is_dir() { + bail!( + "The defined socks-path {:?} is not directory", + path.as_os_str() + ); + } + display_config.ohui_config.path = path.to_str().unwrap().to_string(); + } else { + display_config.ohui_config.path = DEFAULT_UI_PATH.to_string(); + } + } + self.display = Some(display_config); Ok(()) } diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs index 196bd0ebb..56ab2842e 100644 --- a/machine_manager/src/config/gpu.rs +++ b/machine_manager/src/config/gpu.rs @@ -21,6 +21,9 @@ pub const VIRTIO_GPU_MAX_OUTPUTS: usize = 16; pub const VIRTIO_GPU_MAX_HOSTMEM: u64 = 256 * M; +/// The bar0 size of enable_bar0 features +pub const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; + #[derive(Clone, Debug)] pub struct GpuDevConfig { pub id: String, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 66eb53436..f59dc4ce2 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -12,7 +12,7 @@ #[cfg(feature = "usb_camera")] pub mod camera; -#[cfg(feature = "gtk")] +#[cfg(any(feature = "gtk", feature = "ohui_srv"))] pub mod display; pub mod error; #[cfg(feature = "vnc")] @@ -54,7 +54,7 @@ pub use chardev::*; #[cfg(feature = "demo_device")] pub use demo_dev::*; pub use devices::*; -#[cfg(feature = "gtk")] +#[cfg(any(feature = "gtk", feature = "ohui_srv"))] pub use display::*; pub use drive::*; pub use error::ConfigError; @@ -147,7 +147,7 @@ pub struct VmConfig { pub incoming: Option, #[cfg(feature = "vnc")] pub vnc: Option, - #[cfg(feature = "gtk")] + #[cfg(any(feature = "gtk", all(target_env = "ohos", feature = "ohui_srv")))] pub display: Option, #[cfg(feature = "usb_camera")] pub camera_backend: HashMap, diff --git a/ui/Cargo.toml b/ui/Cargo.toml index bc6e8a94c..717248166 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -25,6 +25,7 @@ sasl2-sys = { version = "0.1.20", optional = true } machine_manager = { path = "../machine_manager" } util = { path = "../util" } trace = { path = "../trace" } +address_space = { path = "../address_space" } [features] keycode = [] @@ -33,3 +34,4 @@ console = ["pixman"] gtk = ["console", "keycode", "dep:cairo-rs", "dep:gtk", "dep:gettext-rs", "machine_manager/gtk"] vnc = ["console", "keycode", "machine_manager/vnc"] vnc_auth = ["vnc", "dep:rustls", "dep:rustls-pemfile", "dep:sasl2-sys", "machine_manager/vnc_auth"] +ohui_srv = ["console", "keycode", "machine_manager/ohui_srv"] diff --git a/ui/src/input.rs b/ui/src/input.rs index cfad190d8..ffc454d43 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -49,8 +49,9 @@ pub const KEYCODE_RET: u16 = 38; const KEYCODE_SHIFT: u16 = 42; const KEYCODE_SHIFT_R: u16 = 54; const KEYCODE_ALT: u16 = 56; -const KEYCODE_CAPS_LOCK: u16 = 58; -const KEYCODE_NUM_LOCK: u16 = 69; +pub const KEYCODE_CAPS_LOCK: u16 = 58; +pub const KEYCODE_NUM_LOCK: u16 = 69; +pub const KEYCODE_SCR_LOCK: u16 = 70; const KEYCODE_CTRL_R: u16 = 157; const KEYCODE_ALT_R: u16 = 184; const KEYPAD_1: u16 = 0xffb0; @@ -60,8 +61,8 @@ const KEYPAD_DECIMAL: u16 = 0xffae; const KEYCODE_KP_7: u16 = 0x47; const KEYCODE_KP_DECIMAL: u16 = 0x53; // Led (HID) -const NUM_LOCK_LED: u8 = 0x1; -const CAPS_LOCK_LED: u8 = 0x2; +pub const NUM_LOCK_LED: u8 = 0x1; +pub const CAPS_LOCK_LED: u8 = 0x2; pub const SCROLL_LOCK_LED: u8 = 0x4; static INPUTS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Inputs::default()))); @@ -383,6 +384,11 @@ pub fn key_event(keycode: u16, down: bool) -> Result<()> { Ok(()) } +pub fn trigger_key(keycode: u16) -> Result<()> { + key_event(keycode, true)?; + key_event(keycode, false) +} + /// A complete mouse click event. pub fn press_mouse(button: u32) -> Result<()> { input_button(button, true)?; @@ -403,14 +409,14 @@ pub fn update_key_state(down: bool, keysym: i32, keycode: u16) -> Result<()> { let shift = locked_input .keyboard_state .keyboard_modifier_get(KeyboardModifier::KeyModShift); - let in_upper = get_kbd_led_state(CAPS_LOCK_LED); + let in_upper = check_kbd_led_state(CAPS_LOCK_LED); if (shift && upper == in_upper) || (!shift && upper != in_upper) { debug!("Correct caps lock {} inside {}", upper, in_upper); locked_input.press_key(KEYCODE_CAPS_LOCK)?; } } else if down && in_keypad { let numlock = keysym_is_num_lock(keysym); - let in_numlock = get_kbd_led_state(NUM_LOCK_LED); + let in_numlock = check_kbd_led_state(NUM_LOCK_LED); if in_numlock != numlock { debug!("Correct num lock {} inside {}", numlock, in_numlock); locked_input.press_key(KEYCODE_NUM_LOCK)?; @@ -447,10 +453,14 @@ pub fn release_all_key() -> Result<()> { Ok(()) } -pub fn get_kbd_led_state(state: u8) -> bool { +pub fn check_kbd_led_state(state: u8) -> bool { LED_STATE.lock().unwrap().kbd_led & state == state } +pub fn get_kbd_led_state() -> u8 { + LED_STATE.lock().unwrap().kbd_led +} + pub fn set_kbd_led_state(state: u8) { LED_STATE.lock().unwrap().kbd_led = state; } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index ec053772a..3e288b65a 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -486,7 +486,7 @@ fn ohui_register_event(e: T, srv: Arc) -> Re notifiers, srv.iothread.get_or_init(|| None).as_ref(), &mut evts, - )? + ) } fn ohui_start_listener(server: Arc) -> Result<()> { diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs new file mode 100755 index 000000000..afdf8d2ee --- /dev/null +++ b/ui/src/ohui_srv/msg_handle.rs @@ -0,0 +1,365 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// Stratovirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +use anyhow::{anyhow, bail, Result}; +use log::{error, warn}; +use util::byte_code::ByteCode; + +use super::{channel::OhUiChannel, msg::*}; +use crate::{ + console::{get_active_console, graphic_hardware_ui_info}, + input::{ + self, get_kbd_led_state, input_button, input_move_abs, input_point_sync, keyboard_update, + release_all_key, trigger_key, Axis, ABS_MAX, CAPS_LOCK_LED, INPUT_BUTTON_WHEEL_DOWN, + INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, + KEYCODE_CAPS_LOCK, KEYCODE_NUM_LOCK, KEYCODE_SCR_LOCK, NUM_LOCK_LED, SCROLL_LOCK_LED, + }, + keycode::{DpyMod, KeyCode}, +}; + +fn trans_mouse_pos(x: f64, y: f64, w: f64, h: f64) -> (u32, u32) { + if x < 0.0 || y < 0.0 || x > w || y > h { + error!("incorrect mouse pos info, ({}, {}) of {} * {}", x, y, w, h); + return (0, 0); + } + // TODO: we don't take the situation that image is scaled into consideration + // + // The horizontal and vertical directions of the USB tablet are mapped as follows: + // Horizontal: [0, ABS_MAX] in the tablet is mapped to screan pixels [0, PIXELMAX_W] linearly; + // Vertical: [0, ABS_MAX] in the tablet is mapped linearly to [0, PIXELMAX_H] on the screen; + // For example, if the mouse position is (x, y) and the screen size is wh, + // the data converted to the USB tablet is as follows: ((x/w) ABS_MAX, (y/h) * ABS_MAX). + ( + (x * (ABS_MAX as f64) / w) as u32, + (y * (ABS_MAX as f64) / h) as u32, + ) +} + +#[derive(Default)] +struct WindowState { + width: u32, + height: u32, + led_state: Option, +} + +impl WindowState { + fn update_window_info(&mut self, w: u32, h: u32) { + self.width = w; + self.height = h; + } + + fn press_btn(&mut self, btn: u32) -> Result<()> { + input_button(btn, true)?; + input_point_sync() + } + + fn release_btn(&mut self, btn: u32) -> Result<()> { + input_button(btn, false)?; + input_point_sync() + } + + fn do_key_action(&self, keycode: u16, action: u16) -> Result<()> { + let press = action != 0; + keyboard_update(press, keycode)?; + input::key_event(keycode, press).map_err(|e| { + anyhow!( + "do key event failed: code: {}, action: {}, {:?}", + keycode, + press, + e + ) + }) + } + + fn move_pointer(&mut self, x: f64, y: f64) -> Result<()> { + let (pos_x, pos_y) = trans_mouse_pos(x, y, self.width as f64, self.height as f64); + input_move_abs(Axis::X, pos_x)?; + input_move_abs(Axis::Y, pos_y)?; + input_point_sync() + } + + fn update_host_ledstate(&mut self, led: u8) { + self.led_state = Some(led); + } + + fn sync_kbd_led_state(&mut self) -> Result<()> { + if self.led_state.is_none() { + return Ok(()); + } + + let host_stat = self.led_state.unwrap(); + let guest_stat = get_kbd_led_state(); + if host_stat != guest_stat { + let sync_bits = host_stat ^ guest_stat; + if (sync_bits & CAPS_LOCK_LED) != 0 { + trigger_key(KEYCODE_CAPS_LOCK)?; + } + if (sync_bits & NUM_LOCK_LED) != 0 { + trigger_key(KEYCODE_NUM_LOCK)?; + } + if (sync_bits & SCROLL_LOCK_LED) != 0 { + trigger_key(KEYCODE_SCR_LOCK)?; + } + } + self.led_state = None; + Ok(()) + } +} + +pub struct OhUiMsgHandler { + channel: Arc, + state: Mutex, + hmcode2svcode: HashMap, + reader: Mutex, +} + +impl OhUiMsgHandler { + pub fn new(channel: Arc) -> Self { + OhUiMsgHandler { + channel, + state: Mutex::new(WindowState::default()), + hmcode2svcode: KeyCode::keysym_to_qkeycode(DpyMod::Ohui), + reader: Mutex::new(MsgReader::default()), + } + } + + pub fn handle_msg(&self) -> Result<()> { + let mut reader = self.reader.lock().unwrap(); + if !reader.recv(&self.channel)? { + return Ok(()); + } + + let hdr = &reader.header; + let body_size = hdr.size as usize; + let event_type = hdr.event_type; + if body_size != event_msg_data_len(hdr.event_type) { + warn!( + "{:?} data len is wrong, we want {}, but receive {}", + event_type, + event_msg_data_len(hdr.event_type), + body_size + ); + reader.clear(); + return Ok(()); + } + + let body_bytes = reader.body.as_ref().unwrap(); + if let Err(e) = match event_type { + EventType::MouseButton => { + let body = MouseButtonEvent::from_bytes(&body_bytes[..]).unwrap(); + self.handle_mouse_button(body) + } + EventType::MouseMotion => { + let body = MouseMotionEvent::from_bytes(&body_bytes[..]).unwrap(); + self.handle_mouse_motion(body) + } + EventType::Keyboard => { + let body = KeyboardEvent::from_bytes(&body_bytes[..]).unwrap(); + self.handle_keyboard(body) + } + EventType::WindowInfo => { + let body = WindowInfoEvent::from_bytes(&body_bytes[..]).unwrap(); + self.handle_windowinfo(body); + Ok(()) + } + EventType::Scroll => { + let body = ScrollEvent::from_bytes(&body_bytes[..]).unwrap(); + self.handle_scroll(body) + } + EventType::Focus => { + let body = FocusEvent::from_bytes(&body_bytes[..]).unwrap(); + if body.state == CLIENT_FOCUSOUT_EVENT { + reader.clear(); + release_all_key()?; + } + Ok(()) + } + EventType::Ledstate => { + let body = LedstateEvent::from_bytes(&body_bytes[..]).unwrap(); + self.handle_ledstate(body); + Ok(()) + } + _ => { + error!( + "unsupported type {:?} and body size {}", + event_type, body_size + ); + Ok(()) + } + } { + error!("handle_msg: error: {e}"); + } + reader.clear(); + Ok(()) + } + + fn handle_mouse_button(&self, mb: &MouseButtonEvent) -> Result<()> { + let (btn, action) = (mb.button, mb.btn_action); + match action { + CLIENT_PRESS_BTN => self.state.lock().unwrap().press_btn(btn), + CLIENT_RELEASE_BTN => self.state.lock().unwrap().release_btn(btn), + _ => bail!("Invalid mouse event number {}", action), + } + } + + pub fn handle_cursor_define( + &self, + w: u32, + h: u32, + hot_x: u32, + hot_y: u32, + size_per_pixel: u32, + ) { + let body = HWCursorEvent::new(w, h, hot_x, hot_y, size_per_pixel); + if let Err(e) = self.channel.send_message(EventType::CursorDefine, &body) { + error!("handle_cursor_define: failed to send message with error {e}"); + } + } + + // NOTE: we only support absolute position info now, that means usb-mouse does not work. + fn handle_mouse_motion(&self, mm: &MouseMotionEvent) -> Result<()> { + self.state.lock().unwrap().move_pointer(mm.x, mm.y) + } + + fn handle_keyboard(&self, ke: &KeyboardEvent) -> Result<()> { + if self.state.lock().unwrap().led_state.is_some() { + self.state.lock().unwrap().sync_kbd_led_state()?; + } + let hmkey = ke.keycode; + let keycode = match self.hmcode2svcode.get(&hmkey) { + Some(k) => *k, + None => { + bail!("not supported keycode {}", hmkey); + } + }; + self.state + .lock() + .unwrap() + .do_key_action(keycode, ke.key_action) + } + + fn handle_scroll(&self, se: &ScrollEvent) -> Result<()> { + let org_dir = se.direction; + let dir = match org_dir { + CLIENT_WHEEL_UP => INPUT_BUTTON_WHEEL_UP, + CLIENT_WHEEL_DOWN => INPUT_BUTTON_WHEEL_DOWN, + CLIENT_WHEEL_LEFT => INPUT_BUTTON_WHEEL_LEFT, + CLIENT_WHEEL_RIGHT => INPUT_BUTTON_WHEEL_RIGHT, + _ => bail!("Invalid mouse scroll number {}", org_dir), + }; + self.state.lock().unwrap().press_btn(dir)?; + self.state.lock().unwrap().release_btn(dir)?; + Ok(()) + } + + fn handle_windowinfo(&self, wi: &WindowInfoEvent) { + let cons = get_active_console(); + + for con in cons { + let c = match con.upgrade() { + Some(c) => c, + None => continue, + }; + if let Err(e) = graphic_hardware_ui_info(c, wi.width, wi.height) { + error!("handle_windowinfo failed with error {e}"); + } + } + } + + fn handle_ledstate(&self, led: &LedstateEvent) { + self.state + .lock() + .unwrap() + .update_host_ledstate(led.state as u8); + } + + pub fn send_windowinfo(&self, w: u32, h: u32) { + self.state.lock().unwrap().update_window_info(w, h); + let body = WindowInfoEvent::new(w, h); + self.channel + .send_message(EventType::WindowInfo, &body) + .unwrap(); + } + + pub fn handle_dirty_area(&self, x: u32, y: u32, w: u32, h: u32) { + let body = FrameBufferDirtyEvent::new(x, y, w, h); + if let Err(e) = self + .channel + .send_message(EventType::FrameBufferDirty, &body) + { + error!("handle_dirty_area: failed to send message with error {e}"); + } + } +} + +#[derive(Default)] +struct MsgReader { + /// cache for header + pub header: EventMsgHdr, + /// received byte size of header + pub header_ready: usize, + /// cache of body + pub body: Option>, + /// received byte size of body + pub body_ready: usize, +} + +impl MsgReader { + pub fn recv(&mut self, channel: &Arc) -> Result { + if self.recv_header(channel)? { + return self.recv_body(channel); + } + Ok(false) + } + + pub fn clear(&mut self) { + self.header_ready = 0; + self.body_ready = 0; + self.body = None; + } + + fn recv_header(&mut self, channel: &Arc) -> Result { + if self.header_ready == EVENT_MSG_HDR_SIZE as usize { + return Ok(true); + } + + let buf = self.header.as_mut_bytes(); + self.header_ready += channel.recv_slice(&mut buf[self.header_ready..])?; + Ok(self.header_ready == EVENT_MSG_HDR_SIZE as usize) + } + + fn recv_body(&mut self, channel: &Arc) -> Result { + let body_size = self.header.size as usize; + if body_size == self.body_ready { + return Ok(true); + } + + // The caller make sure that self.clear() is + // called after a complete message receiving. + if self.body.is_none() { + self.body = Some(Vec::with_capacity(body_size)); + } + let buf = self.body.as_mut().unwrap(); + // SAFETY: 1. we guarantee new message has new body, so + // buf's capcity is equal to body_size. 2. buf has 'u8' + // type elements, it will be initialized by zero. + unsafe { + buf.set_len(body_size); + } + self.body_ready += channel.recv_slice(&mut buf[self.body_ready..])?; + + Ok(self.body_ready == body_size) + } +} diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 0cf839343..f089d8534 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -31,3 +31,4 @@ clap = { version = "=4.1.4", default-features = false, features = ["std", "deriv [features] default = [] virtio_gpu = ["ui", "machine_manager/virtio_gpu", "util/pixman"] +ohui_srv = [] diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index b4e0f9224..db7f98d21 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -35,7 +35,7 @@ use crate::{ VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, VIRTIO_GPU_RESP_ERR_UNSPEC, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressSpace, FileBackend, GuestAddress}; use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration_derive::ByteCode; @@ -1646,6 +1646,8 @@ pub struct Gpu { output_states: Arc>, /// Each console corresponds to a display. consoles: Vec>>>, + /// bar0 file backend which is set by ohui server + bar0_fb: Option, } /// SAFETY: The raw pointer in rust doesn't impl Send, all write operations @@ -1661,6 +1663,18 @@ impl Gpu { } } + pub fn set_bar0_fb(&mut self, fb: Option) { + if !self.cfg.enable_bar0 { + self.bar0_fb = None; + return; + } + self.bar0_fb = fb; + } + + pub fn get_bar0_fb(&self) -> Option { + self.bar0_fb.as_ref().cloned() + } + fn build_device_config_space(&mut self) { let mut config_space = self.config_space.lock().unwrap(); config_space.num_scanouts = self.cfg.max_outputs; diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index d3dcf2ba4..d8cd3ecf8 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -18,12 +18,12 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, warn}; -use machine_manager::config::M; +use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use vmm_sys_util::eventfd::EventFd; use crate::{ - virtio_has_feature, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, VirtioDeviceQuirk, - VirtioInterrupt, VirtioInterruptType, + virtio_has_feature, Gpu, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, + VirtioDeviceQuirk, VirtioInterrupt, VirtioInterruptType, }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, @@ -128,17 +128,18 @@ const COMMON_Q_USEDHI_REG: u64 = 0x34; /// 1: select feature bits 32 to 63. const MAX_FEATURES_SELECT_NUM: u32 = 2; -/// The bar0 size of enable_bar0 features -const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; +fn init_gpu_bar0(dev: &Arc>, config: &mut PciConfig) -> Result<()> { + let locked_dev = dev.lock().unwrap(); + let gpu = locked_dev.as_any().downcast_ref::().unwrap(); + let fb = gpu.get_bar0_fb(); -fn init_gpu_bar0(config: &mut PciConfig) -> Result<()> { let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(0), None, VIRTIO_GPU_ENABLE_BAR0_SIZE, - None, - false, + fb, false, + true, false, )?); @@ -1110,7 +1111,7 @@ impl PciDevOps for VirtioPciDevice { self.assign_interrupt_cb(); if device_quirk == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0) { - init_gpu_bar0(&mut self.base.config)?; + init_gpu_bar0(&self.device, &mut self.base.config)?; } self.device -- Gitee From 9d1b98767a6bd56c5ab44d4281640c36a89bf340 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 13:46:52 +0800 Subject: [PATCH 1623/2187] trace: add trace for vhost Add trace for vhost. Signed-off-by: liuxiangdong --- trace/trace_event/virtio.toml | 78 ++++++++++++++++++++++++++++++++ virtio/src/vhost/kernel/mod.rs | 14 +++++- virtio/src/vhost/user/client.rs | 15 +++++- virtio/src/vhost/user/message.rs | 1 + 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index 8b67ccb39..1e7def41b 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -225,3 +225,81 @@ name = "virtio_tpt_write_config" args = "id: &str, offset: u64, data: &[u8]" message = "write config for {}, offset is {:#X}, data is {:X?}" enabled = true + +[[events]] +name = "vhost_set_owner" +args = "" +message = "" +enabled = true + +[[events]] +name = "vhost_reset_owner" +args = "" +message = "" +enabled = true + +[[events]] +name = "vhost_get_features" +args = "features: u64" +message = "features: {:#x}." +enabled = true + +[[events]] +name = "vhost_set_features" +args = "features: u64" +message = "features: {:#x}." +enabled = true + +[[events]] +name = "vhost_set_mem_table" +args = "mem: &dyn fmt::Debug" +message = "mem table: {:?}." +enabled = true + +[[events]] +name = "vhost_set_vring_num" +args = "queue_idx: usize, num: u16" +message = "set vring {} descriptors num {}." +enabled = true + +[[events]] +name = "vhost_set_vring_addr" +args = "vring_addr: &dyn fmt::Debug" +message = "vring addr: {:?}." +enabled = true + +[[events]] +name = "vhost_set_vring_base" +args = "queue_idx: usize, num: u16" +message = "queue_idx {} num {}." +enabled = true + +[[events]] +name = "vhost_get_vring_base" +args = "queue_idx: usize, num: u16" +message = "queue_idx {} num {}." +enabled = true + +[[events]] +name = "vhost_set_vring_call" +args = "queue_idx: usize, event_fd: &dyn fmt::Debug" +message = "queue_idx {}, event_fd {:?}." +enabled = true + +[[events]] +name = "vhost_set_vring_kick" +args = "queue_idx: usize, event_fd: &dyn fmt::Debug" +message = "queue_idx {}, event_fd {:?}." +enabled = true + +[[events]] +name = "vhost_set_vring_enable" +args = "queue_idx: usize, status: bool" +message = "set vring {} status {}." +enabled = true + +[[events]] +name = "vhost_delete_mem_range_failed" +args = "" +message = "Vhost: deleting mem region failed: not matched." +enabled = true diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index f4ce333dc..02d55cadb 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -22,7 +22,6 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; -use log::debug; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr, ioctl_iowr_nr}; @@ -183,7 +182,7 @@ impl VhostMemInfo { return; } } - debug!("Vhost: deleting mem region failed: not matched"); + trace::vhost_delete_mem_range_failed(); } } @@ -265,6 +264,7 @@ impl AsRawFd for VhostBackend { impl VhostOps for VhostBackend { fn set_owner(&self) -> Result<()> { + trace::vhost_set_owner(); // SAFETY: self.fd was created in function new() and the // return value will be checked later. let ret = unsafe { ioctl(self, VHOST_SET_OWNER()) }; @@ -277,6 +277,7 @@ impl VhostOps for VhostBackend { } fn reset_owner(&self) -> Result<()> { + trace::vhost_reset_owner(); // SAFETY: self.fd was created in function new() and the // return value will be checked later. let ret = unsafe { ioctl(self, VHOST_RESET_OWNER()) }; @@ -298,10 +299,12 @@ impl VhostOps for VhostBackend { "VHOST_GET_FEATURES".to_string() ))); } + trace::vhost_get_features(avail_features); Ok(avail_features) } fn set_features(&self, features: u64) -> Result<()> { + trace::vhost_set_features(features); // SAFETY: self.fd was created in function new() and the // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_FEATURES(), &features) }; @@ -334,6 +337,7 @@ impl VhostOps for VhostBackend { .copy_from_slice(region.as_bytes()); } + trace::vhost_set_mem_table(&bytes); // SAFETY: self.fd was created in function new() and the // return value will be checked later. let ret = unsafe { ioctl_with_ptr(self, VHOST_SET_MEM_TABLE(), bytes.as_ptr()) }; @@ -346,6 +350,7 @@ impl VhostOps for VhostBackend { } fn set_vring_num(&self, queue_idx: usize, num: u16) -> Result<()> { + trace::vhost_set_vring_num(queue_idx, num); let vring_state = VhostVringState { index: queue_idx as u32, num: u32::from(num), @@ -397,6 +402,7 @@ impl VhostOps for VhostBackend { log_guest_addr: 0_u64, }; + trace::vhost_set_vring_addr(&vring_addr); // SAFETY: self.fd was created in function new() and the // return value will be checked later. let ret = unsafe { ioctl_with_ref(self, VHOST_SET_VRING_ADDR(), &vring_addr) }; @@ -409,6 +415,7 @@ impl VhostOps for VhostBackend { } fn set_vring_base(&self, queue_idx: usize, num: u16) -> Result<()> { + trace::vhost_set_vring_base(queue_idx, num); let vring_state = VhostVringState { index: queue_idx as u32, num: u32::from(num), @@ -438,10 +445,12 @@ impl VhostOps for VhostBackend { "VHOST_GET_VRING_BASE".to_string() ))); } + trace::vhost_get_vring_base(queue_idx, vring_state.num as u16); Ok(vring_state.num as u16) } fn set_vring_call(&self, queue_idx: usize, fd: Arc) -> Result<()> { + trace::vhost_set_vring_call(queue_idx, &fd); let vring_file = VhostVringFile { index: queue_idx as u32, fd: fd.as_raw_fd(), @@ -458,6 +467,7 @@ impl VhostOps for VhostBackend { } fn set_vring_kick(&self, queue_idx: usize, fd: Arc) -> Result<()> { + trace::vhost_set_vring_kick(queue_idx, &fd); let vring_file = VhostVringFile { index: queue_idx as u32, fd: fd.as_raw_fd(), diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 0b312c2ce..e74b79c57 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -794,6 +794,7 @@ impl VhostUserClient { impl VhostOps for VhostUserClient { fn set_owner(&self) -> Result<()> { + trace::vhost_set_owner(); let hdr = VhostUserMsgHdr::new(VhostUserMsgReq::SetOwner as u32, 0, 0); let body_opt: Option<&u32> = None; let payload_opt: Option<&[u8]> = None; @@ -821,10 +822,12 @@ impl VhostOps for VhostUserClient { .wait_ack_msg::(request) .with_context(|| "Failed to wait ack msg for getting features")?; + trace::vhost_get_features(features); Ok(features) } fn set_features(&self, features: u64) -> Result<()> { + trace::vhost_set_features(features); self.set_value(VhostUserMsgReq::SetFeatures, features) } @@ -858,10 +861,12 @@ impl VhostOps for VhostUserClient { ) .with_context(|| "Failed to send msg for setting mem table")?; + trace::vhost_set_mem_table(&memcontext.regions); Ok(()) } fn set_vring_num(&self, queue_idx: usize, num: u16) -> Result<()> { + trace::vhost_set_vring_num(queue_idx, num); let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( @@ -920,7 +925,7 @@ impl VhostOps for VhostUserClient { queue.avail_ring.0 ) })?; - let _vring_addr = VhostUserVringAddr { + let vring_addr = VhostUserVringAddr { index: index as u32, flags, desc_user_addr, @@ -928,17 +933,19 @@ impl VhostOps for VhostUserClient { avail_user_addr, log_guest_addr: 0_u64, }; + trace::vhost_set_vring_addr(&vring_addr); self.client .lock() .unwrap() .sock - .send_msg(Some(&hdr), Some(&_vring_addr), payload_opt, &[]) + .send_msg(Some(&hdr), Some(&vring_addr), payload_opt, &[]) .with_context(|| "Failed to send msg for setting vring addr")?; Ok(()) } fn set_vring_base(&self, queue_idx: usize, last_avail_idx: u16) -> Result<()> { + trace::vhost_set_vring_base(queue_idx, last_avail_idx); let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( @@ -964,6 +971,7 @@ impl VhostOps for VhostUserClient { } fn set_vring_call(&self, queue_idx: usize, fd: Arc) -> Result<()> { + trace::vhost_set_vring_call(queue_idx, &fd); let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( @@ -988,6 +996,7 @@ impl VhostOps for VhostUserClient { } fn set_vring_kick(&self, queue_idx: usize, fd: Arc) -> Result<()> { + trace::vhost_set_vring_kick(queue_idx, &fd); let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( @@ -1012,6 +1021,7 @@ impl VhostOps for VhostUserClient { } fn set_vring_enable(&self, queue_idx: usize, status: bool) -> Result<()> { + trace::vhost_set_vring_enable(queue_idx, status); let client = self.client.lock().unwrap(); if queue_idx as u64 > client.max_queue_num { bail!( @@ -1059,6 +1069,7 @@ impl VhostOps for VhostUserClient { .wait_ack_msg::(request) .with_context(|| "Failed to wait ack msg for getting vring base")?; + trace::vhost_get_vring_base(queue_idx, res.value as u16); Ok(res.value as u16) } } diff --git a/virtio/src/vhost/user/message.rs b/virtio/src/vhost/user/message.rs index 29803bf76..44e847732 100644 --- a/virtio/src/vhost/user/message.rs +++ b/virtio/src/vhost/user/message.rs @@ -271,6 +271,7 @@ impl VhostUserVringState { /// The configuration for the address of virtual ring. #[repr(C)] +#[derive(Debug)] pub struct VhostUserVringAddr { /// Index for virtual ring. pub index: u32, -- Gitee From 98fe906e6870b9a370bc26097ff7a7be7dbe92d1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 16:18:58 +0800 Subject: [PATCH 1624/2187] usb-tablet: use clap to parse the parameters of the usb-tablet config Use clap to parse the parameters of the usb-tablet config. Signed-off-by: liuxiangdong --- devices/src/usb/tablet.rs | 18 ++++++++++++++++-- machine/src/lib.rs | 13 +++++-------- machine_manager/src/config/usb.rs | 28 ---------------------------- 3 files changed, 21 insertions(+), 38 deletions(-) diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index ab7485fc4..19123c7c6 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -14,6 +14,7 @@ use std::cmp::min; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; +use clap::Parser; use log::{debug, info, warn}; use once_cell::sync::Lazy; @@ -27,6 +28,7 @@ use super::{ config::*, notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; +use machine_manager::config::valid_id; use ui::input::{ register_pointer, unregister_pointer, Axis, InputEvent, InputType, PointerOpts, INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, @@ -111,6 +113,18 @@ const STR_SERIAL_TABLET_INDEX: u8 = 4; /// String descriptor const DESC_STRINGS: [&str; 5] = ["", "StratoVirt", "StratoVirt USB Tablet", "HID Tablet", "2"]; + +#[derive(Parser, Clone, Debug, Default)] +#[command(name = "usb_tablet")] +pub struct UsbTabletConfig { + #[arg(long, value_parser = valid_id)] + id: String, + #[arg(long)] + bus: Option, + #[arg(long)] + port: Option, +} + /// USB tablet device. pub struct UsbTablet { base: UsbDeviceBase, @@ -120,9 +134,9 @@ pub struct UsbTablet { } impl UsbTablet { - pub fn new(id: String) -> Self { + pub fn new(config: UsbTabletConfig) -> Self { Self { - base: UsbDeviceBase::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), + base: UsbDeviceBase::new(config.id, USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Tablet), cntlr: None, } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 08ab2afed..4f7157f3c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -58,11 +58,11 @@ use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; +use devices::usb::tablet::{UsbTablet, UsbTabletConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::{UsbHost, UsbHostConfig}; use devices::usb::{ - keyboard::UsbKeyboard, storage::UsbStorage, tablet::UsbTablet, xhci::xhci_pci::XhciPciDevice, - UsbDevice, + keyboard::UsbKeyboard, storage::UsbStorage, xhci::xhci_pci::XhciPciDevice, UsbDevice, }; #[cfg(target_arch = "aarch64")] use devices::InterruptController; @@ -85,9 +85,7 @@ use machine_manager::config::{ NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; -use machine_manager::config::{ - parse_usb_keyboard, parse_usb_storage, parse_usb_tablet, parse_xhci, -}; +use machine_manager::config::{parse_usb_keyboard, parse_usb_storage, parse_xhci}; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{MachineInterface, VmState}; use migration::{MigrateOps, MigrationManager}; @@ -1729,9 +1727,8 @@ pub trait MachineOps { .with_context(|| "Failed to realize usb keyboard device")? } "usb-tablet" => { - let device_cfg = parse_usb_tablet(cfg_args)?; - // SAFETY: id is already checked not none in parse_usb_tablet(). - let tablet = UsbTablet::new(device_cfg.id.unwrap()); + let config = UsbTabletConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let tablet = UsbTablet::new(config); tablet .realize() .with_context(|| "Failed to realize usb tablet device")? diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index c04ab22b1..f3a505428 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -121,34 +121,6 @@ pub fn parse_usb_keyboard(conf: &str) -> Result { Ok(dev) } -#[derive(Debug)] -pub struct UsbTabletConfig { - pub id: Option, -} - -impl UsbTabletConfig { - fn new() -> Self { - UsbTabletConfig { id: None } - } -} - -impl ConfigCheck for UsbTabletConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-tablet") - } -} - -pub fn parse_usb_tablet(conf: &str) -> Result { - let mut cmd_parser = CmdParser::new("usb-tablet"); - cmd_parser.push("").push("id").push("bus").push("port"); - cmd_parser.parse(conf)?; - let mut dev = UsbTabletConfig::new(); - dev.id = cmd_parser.get_value::("id")?; - - dev.check()?; - Ok(dev) -} - pub fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; check_arg_too_long(&id.unwrap(), "id")?; -- Gitee From 6888cc7575fc67c50d8f8cbae419c3b8220d2026 Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Wed, 14 Feb 2024 21:42:57 +0300 Subject: [PATCH 1625/2187] Implement RX Flow Control for serial ports Before the patch, when Serial-port is not ready to accept data, we paused all RX activity on port for 50ms. It's turned out, that this noticeably slows down serial port. For example, on pl011 FIFO is 16 bytes. with this approach pl011 is able to accept only 320 bytes/second and WinDBG doesn't work Implemented some flow-control for RX. When chardev receives external data and serial::remain_size() returns zero, chardev notifies serial that RX is paused. And removes IN-event from ppoll() of chardev-file-descriptor. When Serial sees that new space appeared and it can accept new data again, serial calls chardev::unpause_rx() and IN-event is again added to ppoll(). For uart and pl011 we just see that data was read from fifo. For virtio-serial, we subscribe for input-queue notifications and see when new input-buf appears. Test shows (contact me for virtio-serial performance measurement utility), that Host->Guest transmission performance is Release build: - After the patch: about 250 (sometimes 320) MByte/Second - before patch: It works very slow. about 25 (twenty five) MByte/Second If test-program is slowed down by generating random-numbers for each buffer, then situation is a bit better: - After the patch: about 135 (up to 160) MByte/Second - before patch: about 120 (130 is maximum I saw) MByte/Second Signed-off-by: Dmitry Skorodumov --- chardev_backend/src/chardev.rs | 134 +++++++++++++++++---------- devices/src/legacy/pl011.rs | 20 ++++ devices/src/legacy/serial.rs | 24 +++++ trace/trace_event/device_legacy.toml | 24 +++++ trace/trace_event/virtio.toml | 18 ++++ virtio/src/device/serial.rs | 74 ++++++++++++++- 6 files changed, 245 insertions(+), 49 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 1860656f2..7a07a78ac 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -39,15 +39,16 @@ use util::set_termi_raw_mode; use util::socket::{SocketListener, SocketStream}; use util::unix::limit_permission; -/// When the receiver is not setup or remain_size is 0, we will delay the listen. -const HANDLE_INPUT_DELAY: Duration = Duration::from_millis(50); - /// Provide the trait that helps handle the input data. pub trait InputReceiver: Send { /// Handle the input data and trigger interrupt if necessary. fn receive(&mut self, buffer: &[u8]); /// Return the remain space size of receiver buffer. + /// 0 if receiver is not ready or no space in FIFO fn remain_size(&mut self) -> usize; + /// Tell receiver that RX is paused and receiver + /// must unpause it when it becomes ready + fn set_paused(&mut self); } /// Provide the trait that notifies device the socket is opened or closed. @@ -78,9 +79,12 @@ pub struct Chardev { receiver: Option>>, /// Used to notify device the socket is opened or closed. dev: Option>>, - /// Timer used to relisten on input stream. When the receiver - /// is not setup or remain_size is 0, we will delay the listen. - relisten_timer: Option, + /// Whether event-handling of device is initialized + /// and we wait for port to become available + wait_port: bool, + /// Scheduled DPC to unpause input stream. + /// Unpause must be done inside event-loop + unpause_timer: Option, } impl Chardev { @@ -94,7 +98,8 @@ impl Chardev { stream_fd: None, receiver: None, dev: None, - relisten_timer: None, + wait_port: false, + unpause_timer: None, } } @@ -187,22 +192,49 @@ impl Chardev { pub fn set_receiver(&mut self, dev: &Arc>) { self.receiver = Some(dev.clone()); + if self.wait_port { + warn!("Serial port for chardev \'{}\' appeared.", &self.id); + self.wait_port = false; + self.unpause_rx(); + } + } + + fn wait_for_port(&mut self, input_fd: RawFd) -> EventNotifier { + // set_receiver() will unpause rx + warn!( + "Serial port for chardev \'{}\' is not ready yet, waiting for port.", + &self.id + ); + + self.wait_port = true; + + EventNotifier::new( + NotifierOperation::Modify, + input_fd, + None, + EventSet::HANG_UP, + vec![], + ) } pub fn set_device(&mut self, dev: Arc>) { self.dev = Some(dev.clone()); } - fn cancel_relisten_timer(&mut self) { - let main_loop = EventLoop::get_ctx(None).unwrap(); - if let Some(timer_id) = self.relisten_timer { - main_loop.timer_del(timer_id); - self.relisten_timer = None; + pub fn unpause_rx(&mut self) { + // Receiver calls this if it returned 0 from remain_size() + // and now it's ready to accept rx-data again + if self.input.is_none() { + error!("unpause called for non-initialized device \'{}\'", &self.id); + return; } - } + if self.unpause_timer.is_some() { + return; // already set + } + + let input_fd = self.input.clone().unwrap().lock().unwrap().as_raw_fd(); - fn set_relisten_timer(&mut self, input_fd: RawFd) -> EventNotifier { - let relisten_fn = Box::new(move || { + let unpause_fn = Box::new(move || { let res = EventLoop::update_event( vec![EventNotifier::new( NotifierOperation::Modify, @@ -214,22 +246,20 @@ impl Chardev { None, ); if let Err(e) = res { - error!("Failed to relisten on fd {input_fd}: {e:?}"); + error!("Failed to unpause on fd {input_fd}: {e:?}"); } }); - let main_loop = EventLoop::get_ctx(None).unwrap(); - let timer_id = main_loop.timer_add(relisten_fn, HANDLE_INPUT_DELAY); - self.cancel_relisten_timer(); - self.relisten_timer = Some(timer_id); + let timer_id = main_loop.timer_add(unpause_fn, Duration::ZERO); + self.unpause_timer = Some(timer_id); + } - EventNotifier::new( - NotifierOperation::Modify, - input_fd, - None, - EventSet::HANG_UP, - vec![], - ) + fn cancel_unpause_timer(&mut self) { + if let Some(timer_id) = self.unpause_timer { + let main_loop = EventLoop::get_ctx(None).unwrap(); + main_loop.timer_del(timer_id); + self.unpause_timer = None; + } } } @@ -284,13 +314,12 @@ fn get_terminal_notifier(chardev: Arc>) -> Option let event_handler: Rc = Rc::new(move |_, _| { let mut locked_chardev = cloned_chardev.lock().unwrap(); if locked_chardev.receiver.is_none() { - warn!( - "Failed to read input data from chardev \'{}\', receiver is none", - &locked_chardev.id - ); - let cancel_listen = locked_chardev.set_relisten_timer(input_fd); - return Some(vec![cancel_listen]); + let wait_port = locked_chardev.wait_for_port(input_fd); + return Some(vec![wait_port]); } + + locked_chardev.cancel_unpause_timer(); // it will be rescheduled if needed + let receiver = locked_chardev.receiver.clone().unwrap(); let input = locked_chardev.input.clone().unwrap(); drop(locked_chardev); @@ -298,10 +327,15 @@ fn get_terminal_notifier(chardev: Arc>) -> Option let mut locked_receiver = receiver.lock().unwrap(); let buff_size = locked_receiver.remain_size(); if buff_size == 0 { - drop(locked_receiver); - let mut locked_chardev = cloned_chardev.lock().unwrap(); - let cancel_listen = locked_chardev.set_relisten_timer(input_fd); - return Some(vec![cancel_listen]); + locked_receiver.set_paused(); + + return Some(vec![EventNotifier::new( + NotifierOperation::Modify, + input_fd, + None, + EventSet::HANG_UP, + vec![], + )]); } let mut buffer = vec![0_u8; buff_size]; @@ -372,7 +406,7 @@ fn get_socket_notifier(chardev: Arc>) -> Option { locked_chardev.input = None; locked_chardev.output = None; locked_chardev.stream_fd = None; - locked_chardev.cancel_relisten_timer(); + locked_chardev.cancel_unpause_timer(); info!( "Chardev \'{}\' event, connection closed: {}", &locked_chardev.id, connection_info @@ -402,14 +436,13 @@ fn get_socket_notifier(chardev: Arc>) -> Option { let input_ready = event & EventSet::IN == EventSet::IN; if input_ready { + locked_chardev.cancel_unpause_timer(); + if locked_chardev.receiver.is_none() { - warn!( - "Failed to read input data from chardev \'{}\', receiver is none", - &locked_chardev.id - ); - let cancel_listen = locked_chardev.set_relisten_timer(stream_fd); - return Some(vec![cancel_listen]); + let wait_port = locked_chardev.wait_for_port(stream_fd); + return Some(vec![wait_port]); } + let receiver = locked_chardev.receiver.clone().unwrap(); let input = locked_chardev.input.clone().unwrap(); drop(locked_chardev); @@ -417,10 +450,15 @@ fn get_socket_notifier(chardev: Arc>) -> Option { let mut locked_receiver = receiver.lock().unwrap(); let buff_size = locked_receiver.remain_size(); if buff_size == 0 { - drop(locked_receiver); - let mut locked_chardev = handling_chardev.lock().unwrap(); - let cancel_listen = locked_chardev.set_relisten_timer(stream_fd); - return Some(vec![cancel_listen]); + locked_receiver.set_paused(); + + return Some(vec![EventNotifier::new( + NotifierOperation::Modify, + stream_fd, + None, + EventSet::HANG_UP, + vec![], + )]); } let mut buffer = vec![0_u8; buff_size]; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 64923b72d..24a34ef8e 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -121,6 +121,8 @@ impl PL011State { #[allow(clippy::upper_case_acronyms)] pub struct PL011 { base: SysBusDevBase, + /// Whether rx paused + paused: bool, /// Device state. state: PL011State, /// Character device for redirection. @@ -136,6 +138,7 @@ impl PL011 { interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), ..Default::default() }, + paused: false, state: PL011State::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), }) @@ -189,6 +192,14 @@ impl PL011 { .with_context(|| LegacyError::RegNotifierErr)?; Ok(()) } + + fn unpause_rx(&mut self) { + if self.paused { + trace::pl011_unpause_rx(); + self.paused = false; + self.chardev.lock().unwrap().unpause_rx(); + } + } } impl InputReceiver for PL011 { @@ -218,6 +229,11 @@ impl InputReceiver for PL011 { fn remain_size(&mut self) -> usize { PL011_FIFO_SIZE - self.state.read_count as usize } + + fn set_paused(&mut self) { + trace::pl011_pause_rx(); + self.paused = true; + } } impl Device for PL011 { @@ -249,6 +265,8 @@ impl SysBusDevOps for PL011 { match offset >> 2 { 0 => { // Data register. + self.unpause_rx(); + self.state.flags &= !(PL011_FLAG_RXFF as u32); let c = self.state.rfifo[self.state.read_pos as usize]; @@ -370,6 +388,7 @@ impl SysBusDevOps for PL011 { // PL011 works in two modes: character mode or FIFO mode. // Reset FIFO if the mode is changed. if (self.state.lcr ^ value) & 0x10 != 0 { + self.unpause_rx(); // fifo cleared, chardev-rx must be unpaused self.state.read_count = 0; self.state.read_pos = 0; } @@ -421,6 +440,7 @@ impl StateTransfer for PL011 { self.state = *PL011State::from_bytes(state) .with_context(|| MigrationError::FromBytesError("PL011"))?; + self.unpause_rx(); Ok(()) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 825aad12e..7c5b90729 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -113,6 +113,8 @@ impl SerialState { /// Contain registers status and operation methods of serial. pub struct Serial { base: SysBusDevBase, + /// Whether rx paused + paused: bool, /// Receiver buffer register. rbr: VecDeque, /// State of Device Serial. @@ -125,6 +127,7 @@ impl Serial { pub fn new(cfg: SerialConfig) -> Self { Serial { base: SysBusDevBase::new(SysBusDevType::Serial), + paused: false, rbr: VecDeque::new(), state: SerialState::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), @@ -163,6 +166,14 @@ impl Serial { Ok(()) } + fn unpause_rx(&mut self) { + if self.paused { + trace::serial_unpause_rx(); + self.paused = false; + self.chardev.lock().unwrap().unpause_rx(); + } + } + /// Update interrupt identification register, /// this method would be called when the interrupt identification changes. fn update_iir(&mut self) { @@ -200,6 +211,9 @@ impl Serial { if self.state.lcr & UART_LCR_DLAB != 0 { ret = self.state.div as u8; } else { + if self.state.mcr & UART_MCR_LOOP == 0 { + self.unpause_rx(); + } if !self.rbr.is_empty() { ret = self.rbr.pop_front().unwrap_or_default(); } @@ -317,6 +331,10 @@ impl Serial { self.state.lcr = data; } 4 => { + if data & UART_MCR_LOOP == 0 { + // loopback turned off. Unpause rx + self.unpause_rx(); + } self.state.mcr = data; } 7 => { @@ -355,6 +373,11 @@ impl InputReceiver for Serial { 0 } } + + fn set_paused(&mut self) { + trace::serial_pause_rx(); + self.paused = true; + } } impl Device for Serial { @@ -446,6 +469,7 @@ impl StateTransfer for Serial { } self.rbr = rbr; self.state = serial_state; + self.unpause_rx(); Ok(()) } diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_event/device_legacy.toml index dbbb4bb25..14ed94332 100644 --- a/trace/trace_event/device_legacy.toml +++ b/trace/trace_event/device_legacy.toml @@ -64,6 +64,18 @@ args = "ibrd: u32, fbrd: u32" message = "ibrd {}, fbrd {}" enabled = true +[[events]] +name = "pl011_pause_rx" +args = "" +message = "rx paused" +enabled = true + +[[events]] +name = "pl011_unpause_rx" +args = "" +message = "rx unpause" +enabled = true + [[events]] name = "pl011_receive" args = "value: u32, read_count: u32" @@ -100,6 +112,18 @@ args = "len: usize" message = "data length {}" enabled = true +[[events]] +name = "serial_pause_rx" +args = "" +message = "rx paused" +enabled = true + +[[events]] +name = "serial_unpause_rx" +args = "" +message = "rx unpause" +enabled = true + [[events]] name = "pflash_device_id" args = "id: u32" diff --git a/trace/trace_event/virtio.toml b/trace/trace_event/virtio.toml index efe78b987..7789e926c 100644 --- a/trace/trace_event/virtio.toml +++ b/trace/trace_event/virtio.toml @@ -34,6 +34,24 @@ args = "" message = "virtio-serial port is none or disconnected." enabled = true +[[events]] +name = "virtio_serial_pause_rx" +args = "" +message = "pause rx." +enabled = true + +[[events]] +name = "virtio_serial_unpause_chardev_rx" +args = "" +message = "unpause rx on chardev." +enabled = true + +[[events]] +name = "virtio_serial_new_inputqueue_buf" +args = "" +message = "new buf appeared in virtio-serial input queue." +enabled = true + [[events]] name = "virtio_rng_write_req_data" args = "size: u32" diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 5beb3411e..7c6864f61 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -267,6 +267,7 @@ impl VirtioDevice for Serial { let port = find_port_by_nr(&self.ports, nr as u32); let handler = SerialPortHandler { input_queue: queues[queue_id * 2].clone(), + input_queue_evt: queue_evts[queue_id * 2].clone(), output_queue: queues[queue_id * 2 + 1].clone(), output_queue_evt: queue_evts[queue_id * 2 + 1].clone(), mem_space: mem_space.clone(), @@ -335,6 +336,8 @@ impl MigrationHook for Serial {} #[derive(Clone)] pub struct SerialPort { name: Option, + /// Whether rx paused + paused: bool, /// Chardev vector for serialport. pub chardev: Arc>, /// Number id. @@ -357,6 +360,7 @@ impl SerialPort { SerialPort { name: Some(port_cfg.id), + paused: false, chardev: Arc::new(Mutex::new(Chardev::new(port_cfg.chardev))), nr: port_cfg.nr, is_console: port_cfg.is_console, @@ -379,6 +383,14 @@ impl SerialPort { Ok(()) } + fn unpause_chardev_rx(&mut self) { + trace::virtio_serial_unpause_chardev_rx(); + if self.paused { + self.paused = false; + self.chardev.lock().unwrap().unpause_rx(); + } + } + fn activate(&mut self, handler: &Arc>) { self.chardev.lock().unwrap().set_receiver(handler); } @@ -391,6 +403,7 @@ impl SerialPort { /// Handler for queues which are used for port. struct SerialPortHandler { input_queue: Arc>, + input_queue_evt: Arc, output_queue: Arc>, output_queue_evt: Arc, mem_space: Arc, @@ -427,6 +440,15 @@ impl SerialPortHandler { }); } + fn input_avail_handle(&mut self) { + // new buffer appeared in input queue. Unpause RX + trace::virtio_serial_new_inputqueue_buf(); + + self.enable_inputqueue_notify(false); + let mut port_locked = self.port.as_ref().unwrap().lock().unwrap(); + port_locked.unpause_chardev_rx(); + } + fn output_handle_internal(&mut self) -> Result<()> { let mut queue_lock = self.output_queue.lock().unwrap(); @@ -536,6 +558,18 @@ impl SerialPortHandler { } } + fn enable_inputqueue_notify(&mut self, enable: bool) { + if self.device_broken.load(Ordering::SeqCst) { + return; + } + + let mut queue_lock = self.input_queue.lock().unwrap(); + let _ = + queue_lock + .vring + .suppress_queue_notify(&self.mem_space, self.driver_features, !enable); + } + fn input_handle_internal(&mut self, buffer: &[u8]) -> Result<()> { let mut queue_lock = self.input_queue.lock().unwrap(); @@ -628,6 +662,18 @@ impl EventNotifierHelper for SerialPortHandler { h_lock.output_handle(); None }); + + let cloned_inp_cls = serial_handler.clone(); + let input_avail_handler: Rc = Rc::new(move |_, fd: RawFd| { + read_fd(fd); + let mut h_lock = cloned_inp_cls.lock().unwrap(); + if h_lock.device_broken.load(Ordering::SeqCst) { + return None; + } + h_lock.input_avail_handle(); + None + }); + notifiers.push(EventNotifier::new( NotifierOperation::AddShared, serial_handler.lock().unwrap().output_queue_evt.as_raw_fd(), @@ -636,6 +682,14 @@ impl EventNotifierHelper for SerialPortHandler { vec![handler], )); + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + serial_handler.lock().unwrap().input_queue_evt.as_raw_fd(), + None, + EventSet::IN, + vec![input_avail_handler], + )); + notifiers } } @@ -655,6 +709,20 @@ impl InputReceiver for SerialPortHandler { fn remain_size(&mut self) -> usize { self.get_input_avail_bytes(BUF_SIZE) } + + fn set_paused(&mut self) { + trace::virtio_serial_pause_rx(); + if self.port.is_none() { + return; + } + + if self.port.as_ref().unwrap().lock().unwrap().guest_connected { + self.enable_inputqueue_notify(true); + } + + let mut locked_port = self.port.as_ref().unwrap().lock().unwrap(); + locked_port.paused = true; + } } impl SerialControlHandler { @@ -789,7 +857,11 @@ impl SerialControlHandler { } } VIRTIO_CONSOLE_PORT_OPEN => { - port.lock().unwrap().guest_connected = ctrl.value != 0; + let mut locked_port = port.lock().unwrap(); + locked_port.guest_connected = ctrl.value != 0; + if ctrl.value != 0 { + locked_port.unpause_chardev_rx(); + } } _ => (), } -- Gitee From c3e712f5b801ffe3fbcfbf00457309dc4d5f74b1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 16:28:59 +0800 Subject: [PATCH 1626/2187] usb-kbd: use clap to parse the parameters of the usb-kbd config Use clap to parse the parameters of the usb-kbd config. Signed-off-by: liuxiangdong --- devices/src/usb/keyboard.rs | 17 +++++++++++++++-- machine/src/lib.rs | 12 +++++------- machine_manager/src/config/usb.rs | 28 ---------------------------- 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 2712436d2..73dc8ed9e 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; +use clap::Parser; use log::{debug, info, warn}; use once_cell::sync::Lazy; @@ -27,6 +28,7 @@ use super::{ notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, }; +use machine_manager::config::valid_id; use ui::input::{register_keyboard, unregister_keyboard, KeyboardOpts}; /// Keyboard device descriptor @@ -118,6 +120,17 @@ const DESC_STRINGS: [&str; 5] = [ "1", ]; +#[derive(Parser, Clone, Debug, Default)] +#[command(name = "usb_keyboard")] +pub struct UsbKeyboardConfig { + #[arg(long, value_parser = valid_id)] + id: String, + #[arg(long)] + bus: Option, + #[arg(long)] + port: Option, +} + /// USB keyboard device. pub struct UsbKeyboard { base: UsbDeviceBase, @@ -164,9 +177,9 @@ impl KeyboardOpts for UsbKeyboardAdapter { } impl UsbKeyboard { - pub fn new(id: String) -> Self { + pub fn new(config: UsbKeyboardConfig) -> Self { Self { - base: UsbDeviceBase::new(id, USB_DEVICE_BUFFER_DEFAULT_LEN), + base: UsbDeviceBase::new(config.id, USB_DEVICE_BUFFER_DEFAULT_LEN), hid: Hid::new(HidType::Keyboard), cntlr: None, } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4f7157f3c..bfeb9289f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -58,12 +58,11 @@ use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; +use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; use devices::usb::tablet::{UsbTablet, UsbTabletConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::{UsbHost, UsbHostConfig}; -use devices::usb::{ - keyboard::UsbKeyboard, storage::UsbStorage, xhci::xhci_pci::XhciPciDevice, UsbDevice, -}; +use devices::usb::{storage::UsbStorage, xhci::xhci_pci::XhciPciDevice, UsbDevice}; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; @@ -85,7 +84,7 @@ use machine_manager::config::{ NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; -use machine_manager::config::{parse_usb_keyboard, parse_usb_storage, parse_xhci}; +use machine_manager::config::{parse_usb_storage, parse_xhci}; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{MachineInterface, VmState}; use migration::{MigrateOps, MigrationManager}; @@ -1719,9 +1718,8 @@ pub trait MachineOps { fn add_usb_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let usb_device = match parse_device_type(cfg_args)?.as_str() { "usb-kbd" => { - let device_cfg = parse_usb_keyboard(cfg_args)?; - // SAFETY: id is already checked not none in parse_usb_keyboard(). - let keyboard = UsbKeyboard::new(device_cfg.id.unwrap()); + let config = UsbKeyboardConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let keyboard = UsbKeyboard::new(config); keyboard .realize() .with_context(|| "Failed to realize usb keyboard device")? diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index f3a505428..285511110 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -93,34 +93,6 @@ pub fn parse_xhci(conf: &str) -> Result { Ok(dev) } -#[derive(Debug)] -pub struct UsbKeyboardConfig { - pub id: Option, -} - -impl UsbKeyboardConfig { - fn new() -> Self { - UsbKeyboardConfig { id: None } - } -} - -impl ConfigCheck for UsbKeyboardConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-keyboard") - } -} - -pub fn parse_usb_keyboard(conf: &str) -> Result { - let mut cmd_parser = CmdParser::new("usb-kbd"); - cmd_parser.push("").push("id").push("bus").push("port"); - cmd_parser.parse(conf)?; - let mut dev = UsbKeyboardConfig::new(); - dev.id = cmd_parser.get_value::("id")?; - - dev.check()?; - Ok(dev) -} - pub fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; check_arg_too_long(&id.unwrap(), "id")?; -- Gitee From bef3ee89a82380ab3bb11d9bb59ddb7589703e56 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 17:48:34 +0800 Subject: [PATCH 1627/2187] xhci: use clap to parse the parameters of the nec-usb-xhci config Use clap to parse the parameters of the nec-usb-xhci config. And modify incorrect USB2.0/3.0 ports number range. Fix: 36b69102d9359(Fix: The number of ports on the XHCI is configured through boot parameters) Signed-off-by: liuxiangdong --- devices/src/usb/xhci/xhci_controller.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 23 +++++++- machine/src/lib.rs | 9 +-- machine_manager/src/config/usb.rs | 77 +------------------------ 4 files changed, 29 insertions(+), 82 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 75a4ebb7a..1d0a005dd 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -21,6 +21,7 @@ use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, info, warn}; +use super::xhci_pci::XhciConfig; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; use super::xhci_trb::{ @@ -31,7 +32,6 @@ use super::xhci_trb::{ use crate::usb::{config::*, TransferOps}; use crate::usb::{UsbDevice, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus}; use address_space::{AddressSpace, GuestAddress}; -use machine_manager::config::XhciConfig; use machine_manager::event_loop::EventLoop; const INVALID_SLOT_ID: u32 = 0; diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index c7c7896f3..0b3373cd2 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -18,6 +18,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Context, Result}; +use clap::Parser; use log::error; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -35,7 +36,7 @@ use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevO use crate::usb::UsbDevice; use crate::{Device, DeviceBase}; use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; -use machine_manager::config::XhciConfig; +use machine_manager::config::{get_pci_df, valid_id}; use machine_manager::event_loop::register_event_helper; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -64,6 +65,26 @@ const XHCI_PCI_PORT_LENGTH: u32 = 0x10; const XHCI_MSIX_TABLE_OFFSET: u32 = 0x3000; const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; +/// XHCI controller configuration. +#[derive(Parser, Clone, Debug, Default)] +#[command(name = "nec-usb-xhci")] +pub struct XhciConfig { + #[arg(long, value_parser = valid_id)] + id: Option, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + // number of usb2.0 ports. + #[arg(long, value_parser = clap::value_parser!(u8).range(1..u8::MAX as i64))] + pub p2: Option, + // number of usb3.0 ports. + #[arg(long, value_parser = clap::value_parser!(u8).range(1..u8::MAX as i64))] + pub p3: Option, + #[arg(long)] + pub iothread: Option, +} + /// Registers offset. /// 0x0 0x40 0x440 0x1000 0x2000 0x3000 0x4000 /// | cap | oper | port | runtime | doorbell | MSIX | diff --git a/machine/src/lib.rs b/machine/src/lib.rs index bfeb9289f..7c7d7324e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -62,7 +62,8 @@ use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; use devices::usb::tablet::{UsbTablet, UsbTabletConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::{UsbHost, UsbHostConfig}; -use devices::usb::{storage::UsbStorage, xhci::xhci_pci::XhciPciDevice, UsbDevice}; +use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice}; +use devices::usb::{storage::UsbStorage, UsbDevice}; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; @@ -75,6 +76,7 @@ use machine_manager::config::parse_demo_dev; use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] use machine_manager::config::parse_pvpanic; +use machine_manager::config::parse_usb_storage; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, @@ -84,7 +86,6 @@ use machine_manager::config::{ NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; -use machine_manager::config::{parse_usb_storage, parse_xhci}; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{MachineInterface, VmState}; use migration::{MigrateOps, MigrationManager}; @@ -1576,8 +1577,8 @@ pub trait MachineOps { /// /// * `cfg_args` - XHCI Configuration. fn add_usb_xhci(&mut self, cfg_args: &str) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let device_cfg = parse_xhci(cfg_args)?; + let device_cfg = XhciConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let bdf = PciBdf::new(device_cfg.bus.clone(), device_cfg.addr); let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let pcidev = XhciPciDevice::new(&device_cfg, devfn, parent_bus, self.get_sys_mem()); diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 285511110..da363b7e4 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use super::error::ConfigError; use crate::config::{ @@ -18,81 +18,6 @@ use crate::config::{ }; use util::aio::AioEngine; -/// XHCI controller configuration. -#[derive(Debug)] -pub struct XhciConfig { - pub id: Option, - // number of usb2.0 ports - pub p2: Option, - // number of usb3.0 ports - pub p3: Option, - pub iothread: Option, -} - -impl XhciConfig { - fn new() -> Self { - XhciConfig { - id: None, - p2: None, - p3: None, - iothread: None, - } - } - - fn check_ports(&self) -> Result<()> { - if self.p2.is_some() && self.p2.unwrap() == 0 { - return Err(anyhow!(ConfigError::IllegalValue( - "usb port2 number".to_string(), - 0, - true, - u8::MAX as u64, - false, - ))); - } - if self.p3.is_some() && self.p3.unwrap() == 0 { - return Err(anyhow!(ConfigError::IllegalValue( - "usb port3 number".to_string(), - 0, - true, - u8::MAX as u64, - false - ))); - } - Ok(()) - } -} - -impl ConfigCheck for XhciConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "xhci controller")?; - if let Some(iothread) = self.iothread.as_ref() { - check_arg_too_long(iothread, "iothread name")?; - } - self.check_ports() - } -} - -pub fn parse_xhci(conf: &str) -> Result { - let mut cmd_parser = CmdParser::new("nec-usb-xhci"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("p2") - .push("p3") - .push("iothread"); - cmd_parser.parse(conf)?; - let mut dev = XhciConfig::new(); - dev.id = cmd_parser.get_value::("id")?; - dev.p2 = cmd_parser.get_value::("p2")?; - dev.p3 = cmd_parser.get_value::("p3")?; - dev.iothread = cmd_parser.get_value::("iothread")?; - - dev.check()?; - Ok(dev) -} - pub fn check_id(id: Option, device: &str) -> Result<()> { check_arg_nonexist(id.clone(), "id", device)?; check_arg_too_long(&id.unwrap(), "id")?; -- Gitee From b54e8538222246177b6968c9169cef999e4000b0 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 22 Feb 2024 21:52:25 +0800 Subject: [PATCH 1628/2187] CPU: Refactor the destroy interface Execute the destroy of vcpu by hypervisor. Signed-off-by: Jinhao Gao --- cpu/src/lib.rs | 41 ++++++++------------------------------- hypervisor/src/kvm/mod.rs | 33 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index dd689d96f..97fad8f7e 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -65,7 +65,6 @@ use std::cell::RefCell; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; -use std::time::Duration; use anyhow::{anyhow, Context, Result}; use log::{error, info, warn}; @@ -135,9 +134,6 @@ pub trait CPUInterface { where Self: std::marker::Sized; - /// Kick `CPU` to exit hypervisor emulation. - fn kick(&self) -> Result<()>; - /// Make `CPU` lifecycle from `Running` to `Paused`. fn pause(&self) -> Result<()>; @@ -204,6 +200,12 @@ pub trait CPUHypervisorOps: Send + Sync { state: Arc<(Mutex, Condvar)>, pause_signal: Arc, ) -> Result<()>; + + fn destroy( + &self, + task: Arc>>>, + state: Arc<(Mutex, Condvar)>, + ) -> Result<()>; } /// `CPU` is a wrapper around creating and using a hypervisor-based VCPU. @@ -383,11 +385,6 @@ impl CPUInterface for CPU { Ok(()) } - fn kick(&self) -> Result<()> { - self.hypervisor_cpu - .kick_vcpu_thread(self.task.clone(), self.state.clone()) - } - fn pause(&self) -> Result<()> { self.hypervisor_cpu.pause( self.task.clone(), @@ -403,30 +400,8 @@ impl CPUInterface for CPU { } fn destroy(&self) -> Result<()> { - let (cpu_state, cvar) = &*self.state; - let mut cpu_state = cpu_state.lock().unwrap(); - if *cpu_state == CpuLifecycleState::Running { - *cpu_state = CpuLifecycleState::Stopping; - } else if *cpu_state == CpuLifecycleState::Stopped - || *cpu_state == CpuLifecycleState::Paused - { - return Ok(()); - } - - self.kick()?; - cpu_state = cvar - .wait_timeout(cpu_state, Duration::from_millis(32)) - .unwrap() - .0; - - if *cpu_state == CpuLifecycleState::Stopped { - Ok(()) - } else { - Err(anyhow!(CpuError::DestroyVcpu(format!( - "VCPU still in {:?} state", - *cpu_state - )))) - } + self.hypervisor_cpu + .destroy(self.task.clone(), self.state.clone()) } fn guest_shutdown(&self) -> Result<()> { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index ddaef98f7..39db57fdb 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -692,6 +692,39 @@ impl CPUHypervisorOps for KvmCpu { cvar.notify_one(); Ok(()) } + + fn destroy( + &self, + task: Arc>>>, + state: Arc<(Mutex, Condvar)>, + ) -> Result<()> { + let (cpu_state, cvar) = &*state; + let mut locked_cpu_state = cpu_state.lock().unwrap(); + if *locked_cpu_state == CpuLifecycleState::Running { + *locked_cpu_state = CpuLifecycleState::Stopping; + } else if *locked_cpu_state == CpuLifecycleState::Stopped + || *locked_cpu_state == CpuLifecycleState::Paused + { + return Ok(()); + } + drop(locked_cpu_state); + + self.kick_vcpu_thread(task, state.clone())?; + let mut locked_cpu_state = cpu_state.lock().unwrap(); + locked_cpu_state = cvar + .wait_timeout(locked_cpu_state, Duration::from_millis(32)) + .unwrap() + .0; + + if *locked_cpu_state == CpuLifecycleState::Stopped { + Ok(()) + } else { + Err(anyhow!(CpuError::DestroyVcpu(format!( + "VCPU still in {:?} state", + *locked_cpu_state + )))) + } + } } struct KVMInterruptManager { -- Gitee From 2fc3507c0f923d01ae76c1b6b014b87c7dfb6d35 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Thu, 25 Jan 2024 20:29:50 +0800 Subject: [PATCH 1629/2187] acpi: gtdt: fix invalid 64-bit physical addresses Per the ACPI 6.5 specification, on the GTDT Table Structure, the Counter Control Block Address and Counter Read Block Address fields of the GTDT table should be set to 0xFFFFFFFFFFFFFFFF if not provided, rather than 0x0. Signed-off-by: yezengruan --- machine/src/aarch64/standard.rs | 4 ++++ tests/mod_test/tests/aarch64/acpi_test.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index af5a7304d..547d44326 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -768,6 +768,8 @@ impl AcpiBuilder for StdMachine { let mut gtdt = AcpiTable::new(*b"GTDT", 2, *b"STRATO", *b"VIRTGTDT", 1); gtdt.set_table_len(96); + // Counter control block physical address + gtdt.set_field(36, 0xFFFF_FFFF_FFFF_FFFF_u64); // Secure EL1 interrupt gtdt.set_field(48, ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ + INTERRUPT_PPIS_COUNT); // Secure EL1 flags @@ -787,6 +789,8 @@ impl AcpiBuilder for StdMachine { gtdt.set_field(72, ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ + INTERRUPT_PPIS_COUNT); // Non secure EL2 flags gtdt.set_field(76, ACPI_GTDT_INTERRUPT_MODE_LEVEL); + // Counter read block physical address + gtdt.set_field(80, 0xFFFF_FFFF_FFFF_FFFF_u64); let gtdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, >dt) .with_context(|| "Fail to add GTDT table to loader")?; diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 9478f2cad..be78ba68b 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -146,6 +146,7 @@ fn check_gtdt(data: &[u8]) { assert_eq!(String::from_utf8_lossy(&data[..4]), "GTDT"); assert_eq!(LittleEndian::read_u32(&data[4..]), GTDT_TABLE_DATA_LENGTH); // Check length + assert_eq!(LittleEndian::read_u64(&data[36..]), 0xFFFF_FFFF_FFFF_FFFF); // Counter control block physical address assert_eq!(LittleEndian::read_u32(&data[48..]), 29); // Secure EL1 interrupt assert_eq!(LittleEndian::read_u32(&data[52..]), 0); // Secure EL1 flags assert_eq!(LittleEndian::read_u32(&data[56..]), 30); // Non secure EL1 interrupt @@ -154,6 +155,7 @@ fn check_gtdt(data: &[u8]) { assert_eq!(LittleEndian::read_u32(&data[68..]), 0); // Virtual timer flags assert_eq!(LittleEndian::read_u32(&data[72..]), 26); // Non secure EL2 interrupt assert_eq!(LittleEndian::read_u32(&data[76..]), 0); // Non secure EL2 flags + assert_eq!(LittleEndian::read_u64(&data[80..]), 0xFFFF_FFFF_FFFF_FFFF); // Counter base block physical address } fn check_dbg2(data: &[u8]) { -- Gitee From 6f48812bebcbfe63ba57f3da0076d70c584ace7e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 10:30:03 +0800 Subject: [PATCH 1630/2187] acpi: pptt: fix incorrect L2 cache size L2 unified cache size should be 512kb (524288). Signed-off-by: yezengruan --- acpi/src/acpi_table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acpi/src/acpi_table.rs b/acpi/src/acpi_table.rs index e8e22910b..1577fd5b5 100644 --- a/acpi/src/acpi_table.rs +++ b/acpi/src/acpi_table.rs @@ -262,7 +262,7 @@ const CACHE_NODES: [CacheNode; CacheType::L3 as usize + 1] = [ }, // L2 unified cache CacheNode { - size: 524228, + size: 524288, sets: 1024, associativity: 8, attributes: 10, -- Gitee From 3ffd32c6913b8af048e999028546cceb301528ff Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 14:14:00 +0800 Subject: [PATCH 1631/2187] acpi: iort upgrade up to revision E.b Upgrade the IORT table to E.b specification revision (ARM DEN 0049E.b). Signed-off-by: yezengruan --- machine/src/aarch64/standard.rs | 10 +++++++++- tests/mod_test/tests/aarch64/acpi_test.rs | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 547d44326..ef25bccb1 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -885,7 +885,7 @@ impl AcpiBuilder for StdMachine { acpi_data: &Arc>>, loader: &mut TableLoader, ) -> Result { - let mut iort = AcpiTable::new(*b"IORT", 2, *b"STRATO", *b"VIRTIORT", 1); + let mut iort = AcpiTable::new(*b"IORT", 3, *b"STRATO", *b"VIRTIORT", 1); iort.set_table_len(128); // Number of IORT nodes is 2: ITS group node and Root Complex Node. @@ -897,6 +897,8 @@ impl AcpiBuilder for StdMachine { iort.set_field(48, ACPI_IORT_NODE_ITS_GROUP); // ITS node length iort.set_field(49, 24_u16); + // ITS node revision + iort.set_field(51, 1_u8); // ITS count iort.set_field(64, 1_u32); @@ -905,6 +907,10 @@ impl AcpiBuilder for StdMachine { // Length of Root Complex node let len = ROOT_COMPLEX_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE; iort.set_field(73, len); + // Revision of Root Complex node + iort.set_field(75, 3_u8); + // Identifier of Root Complex node + iort.set_field(76, 1_u32); // Mapping counts of Root Complex Node iort.set_field(80, 1_u32); // Mapping offset of Root Complex Node @@ -913,6 +919,8 @@ impl AcpiBuilder for StdMachine { iort.set_field(88, 1_u32); // Memory flags of coherent device iort.set_field(95, 3_u8); + // Memory address size limit + iort.set_field(104, 0x40_u8); // Identity RID mapping iort.set_field(112, 0xffff_u32); // Without SMMU, id mapping is the first node in ITS group node diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index be78ba68b..4774e4a88 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -172,13 +172,17 @@ fn check_iort(data: &[u8]) { assert_eq!(LittleEndian::read_u32(&data[40..]), 48); // Node offset assert_eq!(data[48], 0); // ITS group node assert_eq!(LittleEndian::read_u16(&data[49..]), 24); // ITS node length + assert_eq!(data[51], 1); // ITS node revision assert_eq!(LittleEndian::read_u32(&data[64..]), 1); // ITS count assert_eq!(data[72], 2); // Root Complex Node assert_eq!(LittleEndian::read_u16(&data[73..]), 56); // Length of Root Complex Node + assert_eq!(data[75], 3); // Revision of Root Complex Node + assert_eq!(LittleEndian::read_u32(&data[76..]), 1); // Identifier of Root Complex Node assert_eq!(LittleEndian::read_u32(&data[80..]), 1); // Mapping counts of Root Complex Node assert_eq!(LittleEndian::read_u32(&data[84..]), 36); // Mapping offset of Root Complex Node assert_eq!(LittleEndian::read_u32(&data[88..]), 1); // Cache of coherent device assert_eq!(data[95], 3); // Memory flags of coherent device + assert_eq!(data[104], 0x40); // Memory address size limit assert_eq!(LittleEndian::read_u32(&data[112..]), 0xffff); // Identity RID mapping // Without SMMU, id mapping is the first node in ITS group node -- Gitee From 510fdd9cf1b45bc9d50522d5db59e43e38852f28 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 19 Feb 2024 10:06:34 +0800 Subject: [PATCH 1632/2187] test: fix the acquisition of dsdt table address The DSDT table is pointed to by the FADT table, we should get DSDT table physical address from FADT table. Signed-off-by: yezengruan --- tests/mod_test/tests/aarch64/acpi_test.rs | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 4774e4a88..01f012f1b 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -46,7 +46,6 @@ const ACPI_TABLES_DATA_LENGTH_8: usize = 6069; const ACPI_TABLES_DATA_LENGTH_200: usize = 40510; enum TABLE { - Dsdt, Fadt, Madt, Gtdt, @@ -93,13 +92,19 @@ fn check_dsdt(data: &[u8]) { assert_eq!(LittleEndian::read_u32(&data[4..]), DSDT_TABLE_DATA_LENGTH); // Check length } -fn check_fadt(data: &[u8]) { +fn check_fadt(data: &[u8]) -> u64 { assert_eq!(String::from_utf8_lossy(&data[..4]), "FACP"); assert_eq!(LittleEndian::read_u32(&data[4..]), FADT_TABLE_DATA_LENGTH); // Check length assert_eq!(LittleEndian::read_i32(&data[112..]), 0x10_0500); // Enable HW_REDUCED_ACPI bit assert_eq!(LittleEndian::read_u16(&data[129..]), 0x3); // ARM Boot Architecture Flags assert_eq!(LittleEndian::read_i32(&data[131..]), 3); // FADT minor revision + + // Check 64-bit address of DSDT table. + let dsdt_addr = LittleEndian::read_u64(&data[140..]); + assert_eq!(dsdt_addr, 0); + + dsdt_addr } fn check_madt(data: &[u8], cpu: u8) { @@ -349,18 +354,16 @@ fn test_tables(test_state: &TestState, alloc: &mut GuestAllocator, xsdt_addr: us ); // XSDT entry: An array of 64-bit physical addresses that point to other DESCRIPTION_HEADERs. - // DESCRIPTION_HEADERs: DSDT, FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT - let entry_addr = xsdt_addr + mem::size_of::() - 8; - - // Check DSDT - let mut offset = entry_addr + TABLE::Dsdt as usize * 8; - let dsdt_addr = LittleEndian::read_u64(&read_data[offset..]); - check_dsdt(&read_data[(dsdt_addr as usize)..]); + // DESCRIPTION_HEADERs: FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT + let entry_addr = xsdt_addr + mem::size_of::(); // Check FADT - offset = entry_addr + TABLE::Fadt as usize * 8; + let mut offset = entry_addr + TABLE::Fadt as usize * 8; let fadt_addr = LittleEndian::read_u64(&read_data[offset..]); - check_fadt(&read_data[(fadt_addr as usize)..]); + let dsdt_addr = check_fadt(&read_data[(fadt_addr as usize)..]); + + // Check DSDT (DSDT table is pointed to by the FADT table) + check_dsdt(&read_data[(dsdt_addr as usize)..]); // Check MADT offset = entry_addr + TABLE::Madt as usize * 8; @@ -426,11 +429,11 @@ fn check_madt_of_two_gicr( ); // XSDT entry: An array of 64-bit physical addresses that point to other DESCRIPTION_HEADERs. - // DESCRIPTION_HEADERs: DSDT, FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT - let entry_addr = xsdt_addr + mem::size_of::() - 8; + // DESCRIPTION_HEADERs: FADT, MADT, GTDT, IORT, SPCR, MCFG, SRAT, SLIT, PPTT + let entry_addr = xsdt_addr + mem::size_of::(); // MADT offset base on XSDT - let mut offset = entry_addr + 2 * 8; + let mut offset = entry_addr + TABLE::Madt as usize * 8; let madt_addr = LittleEndian::read_u64(&read_data[offset..]) as usize; // Check second GIC Redistributor -- Gitee From f18c404a5aba8d7845d3330aecc38e865d91923a Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 10:48:38 +0800 Subject: [PATCH 1633/2187] acpi: support facs table for aarch64 Hibernate and wakeup of windows VM require facs table, let us support it on aarch64. Signed-off-by: yezengruan --- machine/src/standard_common/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 4b831657d..2f199ba9d 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -100,12 +100,9 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let mut xsdt_entries = Vec::new(); - #[cfg(target_arch = "x86_64")] - { - let facs_addr = Self::build_facs_table(&acpi_tables, &mut loader) - .with_context(|| "Failed to build ACPI FACS table")?; - xsdt_entries.push(facs_addr); - } + let facs_addr = Self::build_facs_table(&acpi_tables, &mut loader) + .with_context(|| "Failed to build ACPI FACS table")?; + xsdt_entries.push(facs_addr); let dsdt_addr = self .build_dsdt_table(&acpi_tables, &mut loader) @@ -678,7 +675,6 @@ pub(crate) trait AcpiBuilder { /// /// `acpi_data` - Bytes streams that ACPI tables converts to. /// `loader` - ACPI table loader. - #[cfg(target_arch = "x86_64")] fn build_facs_table(acpi_data: &Arc>>, loader: &mut TableLoader) -> Result where Self: Sized, -- Gitee From 20521755c8dbd21d9ed266c4d20b0b4910c80d07 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 11:05:03 +0800 Subject: [PATCH 1634/2187] acpi: set facs address in the fadt's FIRMWARE_CTRL field Physical memory address of the FACS should be set in the FIRMWARE_CTRL field of the FADT, not in the XSDT. (ACPI 6.5 - 5.2.9) Signed-off-by: yezengruan --- machine/src/standard_common/mod.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 2f199ba9d..cc9781927 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -102,12 +102,11 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let facs_addr = Self::build_facs_table(&acpi_tables, &mut loader) .with_context(|| "Failed to build ACPI FACS table")?; - xsdt_entries.push(facs_addr); let dsdt_addr = self .build_dsdt_table(&acpi_tables, &mut loader) .with_context(|| "Failed to build ACPI DSDT table")?; - let fadt_addr = Self::build_fadt_table(&acpi_tables, &mut loader, dsdt_addr) + let fadt_addr = Self::build_fadt_table(&acpi_tables, &mut loader, facs_addr, dsdt_addr) .with_context(|| "Failed to build ACPI FADT table")?; xsdt_entries.push(fadt_addr); @@ -569,10 +568,12 @@ pub(crate) trait AcpiBuilder { /// /// `acpi_data` - Bytes streams that ACPI tables converts to. /// `loader` - ACPI table loader. + /// `facs_addr` - Offset of ACPI FACS table in `acpi_data`. /// `dsdt_addr` - Offset of ACPI DSDT table in `acpi_data`. fn build_fadt_table( acpi_data: &Arc>>, loader: &mut TableLoader, + facs_addr: u64, dsdt_addr: u64, ) -> Result where @@ -647,6 +648,18 @@ pub(crate) trait AcpiBuilder { let fadt_end = locked_acpi_data.len() as u32; drop(locked_acpi_data); + // FACS address field's offset in FADT. + let facs_offset = 36_u32; + // Size of FACS address. + let facs_size = 4_u8; + loader.add_pointer_entry( + ACPI_TABLE_FILE, + fadt_begin + facs_offset, + facs_size, + ACPI_TABLE_FILE, + facs_addr as u32, + )?; + // xDSDT address field's offset in FADT. let xdsdt_offset = 140_u32; // Size of xDSDT address. -- Gitee From 3bcddd735754042663578f5c0c5aed4c9f7fb64b Mon Sep 17 00:00:00 2001 From: yezengruan Date: Sun, 18 Feb 2024 11:15:21 +0800 Subject: [PATCH 1635/2187] acpi: facs: fix incorrect hardware signature The hardware signature is set by the system and should not be set to the value of check sum. If any content or structure of the ACPI tables has changed, then the hardware signature must change. (ACPI Spec 6.5 - 5.2.10) Signed-off-by: yezengruan --- machine/src/standard_common/mod.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index cc9781927..d1fd9eb0c 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -100,7 +100,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let mut xsdt_entries = Vec::new(); - let facs_addr = Self::build_facs_table(&acpi_tables, &mut loader) + let facs_addr = Self::build_facs_table(&acpi_tables) .with_context(|| "Failed to build ACPI FACS table")?; let dsdt_addr = self @@ -687,8 +687,7 @@ pub(crate) trait AcpiBuilder { /// # Arguments /// /// `acpi_data` - Bytes streams that ACPI tables converts to. - /// `loader` - ACPI table loader. - fn build_facs_table(acpi_data: &Arc>>, loader: &mut TableLoader) -> Result + fn build_facs_table(acpi_data: &Arc>>) -> Result where Self: Sized, { @@ -704,16 +703,8 @@ pub(crate) trait AcpiBuilder { let mut locked_acpi_data = acpi_data.lock().unwrap(); let facs_begin = locked_acpi_data.len() as u32; locked_acpi_data.extend(facs_data); - let facs_end = locked_acpi_data.len() as u32; drop(locked_acpi_data); - loader.add_cksum_entry( - ACPI_TABLE_FILE, - facs_begin + TABLE_CHECKSUM_OFFSET, - facs_begin, - facs_end - facs_begin, - )?; - Ok(facs_begin as u64) } -- Gitee From 82b1bbe48e76e5c7d528d92979a79649d439db31 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Mon, 19 Feb 2024 11:23:45 +0800 Subject: [PATCH 1636/2187] test: add acpi facs mod test Signed-off-by: yezengruan --- tests/mod_test/tests/aarch64/acpi_test.rs | 30 +++++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 01f012f1b..56428827f 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -24,6 +24,8 @@ use mod_test::libdriver::machine::TestStdMachine; use mod_test::libdriver::malloc::GuestAllocator; use mod_test::libtest::{test_init, TestState}; +// Now facs table data length is 64. +const FACS_TABLE_DATA_LENGTH: u32 = 64; // Now dsdt table data length is 3482. const DSDT_TABLE_DATA_LENGTH: u32 = 3482; // Now fadt table data length is 276. @@ -40,10 +42,10 @@ const IORT_TABLE_DATA_LENGTH: u32 = 128; const SPCR_TABLE_DATA_LENGTH: u32 = 80; // Now mcfg table data length is 60. const MCFG_TABLE_DATA_LENGTH: u32 = 60; -// Now acpi tables data length is 6069(cpu number is 8). -const ACPI_TABLES_DATA_LENGTH_8: usize = 6069; -// Now acpi tables data length is 40510(cpu number is 200). -const ACPI_TABLES_DATA_LENGTH_200: usize = 40510; +// Now acpi tables data length is 6133(cpu number is 8). +const ACPI_TABLES_DATA_LENGTH_8: usize = 6133; +// Now acpi tables data length is 40574(cpu number is 200). +const ACPI_TABLES_DATA_LENGTH_200: usize = 40574; enum TABLE { Fadt, @@ -92,7 +94,12 @@ fn check_dsdt(data: &[u8]) { assert_eq!(LittleEndian::read_u32(&data[4..]), DSDT_TABLE_DATA_LENGTH); // Check length } -fn check_fadt(data: &[u8]) -> u64 { +fn check_facs(data: &[u8]) { + assert_eq!(String::from_utf8_lossy(&data[..4]), "FACS"); + assert_eq!(LittleEndian::read_u32(&data[4..]), FACS_TABLE_DATA_LENGTH); // Check length +} + +fn check_fadt(data: &[u8]) -> (u32, u64) { assert_eq!(String::from_utf8_lossy(&data[..4]), "FACP"); assert_eq!(LittleEndian::read_u32(&data[4..]), FADT_TABLE_DATA_LENGTH); // Check length @@ -100,11 +107,15 @@ fn check_fadt(data: &[u8]) -> u64 { assert_eq!(LittleEndian::read_u16(&data[129..]), 0x3); // ARM Boot Architecture Flags assert_eq!(LittleEndian::read_i32(&data[131..]), 3); // FADT minor revision + // Check 32-bit address of FACS table. + let facs_addr = LittleEndian::read_u32(&data[36..]); + assert_eq!(facs_addr, 0); + // Check 64-bit address of DSDT table. let dsdt_addr = LittleEndian::read_u64(&data[140..]); - assert_eq!(dsdt_addr, 0); + assert_ne!(dsdt_addr, 0); - dsdt_addr + (facs_addr, dsdt_addr) } fn check_madt(data: &[u8], cpu: u8) { @@ -360,7 +371,10 @@ fn test_tables(test_state: &TestState, alloc: &mut GuestAllocator, xsdt_addr: us // Check FADT let mut offset = entry_addr + TABLE::Fadt as usize * 8; let fadt_addr = LittleEndian::read_u64(&read_data[offset..]); - let dsdt_addr = check_fadt(&read_data[(fadt_addr as usize)..]); + let (facs_addr, dsdt_addr) = check_fadt(&read_data[(fadt_addr as usize)..]); + + // Check FACS (FACS table is pointed to by the FADT table) + check_facs(&read_data[(facs_addr as usize)..]); // Check DSDT (DSDT table is pointed to by the FADT table) check_dsdt(&read_data[(dsdt_addr as usize)..]); -- Gitee From 85d371ce6ee63bc8081f57d298f02c78a8e198fa Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 26 Jan 2024 09:32:34 +0800 Subject: [PATCH 1637/2187] fadt: enable LOW_POWER_S0_IDLE_CAPABLE flag When LOW_POWER_S0_IDLE_CAPABLE is set, the windows power management can set sleep state. Signed-off-by: yezengruan --- machine/src/standard_common/mod.rs | 4 ++-- tests/mod_test/tests/aarch64/acpi_test.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index d1fd9eb0c..9cd2f8241 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -602,8 +602,8 @@ pub(crate) trait AcpiBuilder { fadt.set_field(91, 4); #[cfg(target_arch = "aarch64")] { - // FADT flag: enable HW_REDUCED_ACPI bit on aarch64 plantform. - fadt.set_field(112, 1 << 20 | 1 << 10 | 1 << 8); + // FADT flag: enable HW_REDUCED_ACPI and LOW_POWER_S0_IDLE_CAPABLE bit on aarch64 plantform. + fadt.set_field(112, 1 << 21 | 1 << 20 | 1 << 10 | 1 << 8); // ARM Boot Architecture Flags fadt.set_field(129, 0x3_u16); } diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 56428827f..5e803f88c 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -103,7 +103,8 @@ fn check_fadt(data: &[u8]) -> (u32, u64) { assert_eq!(String::from_utf8_lossy(&data[..4]), "FACP"); assert_eq!(LittleEndian::read_u32(&data[4..]), FADT_TABLE_DATA_LENGTH); // Check length - assert_eq!(LittleEndian::read_i32(&data[112..]), 0x10_0500); // Enable HW_REDUCED_ACPI bit + // Enable HW_REDUCED_ACPI and LOW_POWER_S0_IDLE_CAPABLE bit + assert_eq!(LittleEndian::read_i32(&data[112..]), 0x30_0500); assert_eq!(LittleEndian::read_u16(&data[129..]), 0x3); // ARM Boot Architecture Flags assert_eq!(LittleEndian::read_i32(&data[131..]), 3); // FADT minor revision -- Gitee From 42390aa7d7d8a37eb8c49b9b47fda6f482e9ee9d Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 21 Feb 2024 13:16:14 +0800 Subject: [PATCH 1638/2187] camera: support for NV12 format allow NV12 format of host side transfered to guest. Signed-off-by: zhanghan64 --- devices/src/camera_backend/demo.rs | 1 + devices/src/camera_backend/mod.rs | 44 ++++++++++++++++++----- devices/src/camera_backend/v4l2.rs | 9 +++-- devices/src/usb/camera.rs | 33 +++++++++++------ devices/src/usb/camera_media_type_guid.rs | 9 ++++- 5 files changed, 74 insertions(+), 22 deletions(-) diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 5ac70cc99..5a961bb4d 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -250,6 +250,7 @@ impl ImageFrame { FmtType::Mjpg => build_fake_mjpg(width, height), FmtType::Yuy2 => convert_to_yuy2(data.deref(), width, height), FmtType::Rgb565 => data.deref().to_vec(), + FmtType::Nv12 => bail!("demo device does not support NV12 now"), }; self.frame_idx += 1; if self.frame_idx > FRAME_IDX_LIMIT { diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 28e42f5c6..51f963a87 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -37,7 +37,7 @@ pub struct CamBasicFmt { pub width: u32, pub height: u32, fps: u32, - fmttype: FmtType, + pub fmttype: FmtType, } impl CamBasicFmt { @@ -55,6 +55,7 @@ pub enum FmtType { Yuy2 = 0, Rgb565, Mjpg, + Nv12, } #[derive(Clone, Debug)] @@ -72,16 +73,42 @@ pub struct CameraFormatList { pub frame: Vec, } -pub fn get_video_frame_size(width: u32, height: u32) -> Result { - width +pub fn get_video_frame_size(width: u32, height: u32, fmt: FmtType) -> Result { + let pixel_size = width .checked_mul(height) - .with_context(|| format!("Invalid width {} or height {}", width, height))? - .checked_mul(2) - .with_context(|| format!("Invalid width {} or height {}", width, height)) + .with_context(|| format!("Invalid width {} or height {}", width, height))?; + if pixel_size % 2 != 0 { + bail!("Abnormal width {} or height {}", width, height); + } + match fmt { + // NV12 format: 4 Y values share a pair of UV values, that means every 4 pixels + // need 6 bytes. On average, 1 pixel needs 1.5 bytes. + FmtType::Nv12 => pixel_size + .checked_mul(3) + .with_context(|| { + format!( + "fmt {:?}, Invalid width {} or height {}", + fmt, width, height + ) + })? + .checked_div(2) + .with_context(|| { + format!( + "fmt {:?}, Invalid width {} or height {}", + fmt, width, height + ) + }), + _ => pixel_size.checked_mul(2).with_context(|| { + format!( + "fmt {:?}, Invalid width {} or height {}", + fmt, width, height + ) + }), + } } -pub fn get_bit_rate(width: u32, height: u32, interval: u32) -> Result { - let fm_size = get_video_frame_size(width, height)?; +pub fn get_bit_rate(width: u32, height: u32, interval: u32, fmt: FmtType) -> Result { + let fm_size = get_video_frame_size(width, height, fmt)?; let size_in_bit = fm_size as u64 * INTERVALS_PER_SEC as u64 * 8; let rate = size_in_bit .checked_div(interval as u64) @@ -99,6 +126,7 @@ macro_rules! video_fourcc { pub const PIXFMT_RGB565: u32 = video_fourcc!('R', 'G', 'B', 'P'); pub const PIXFMT_YUYV: u32 = video_fourcc!('Y', 'U', 'Y', 'V'); pub const PIXFMT_MJPG: u32 = video_fourcc!('M', 'J', 'P', 'G'); +pub const PIXFMT_NV12: u32 = video_fourcc!('N', 'V', '1', '2'); /// Callback function which is called when frame data is coming. pub type CameraNotifyCallback = Arc; diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 358725480..7118d987c 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -26,7 +26,7 @@ use v4l2_sys_mit::{ }; use vmm_sys_util::epoll::EventSet; -use super::{PIXFMT_MJPG, PIXFMT_RGB565, PIXFMT_YUYV}; +use super::{PIXFMT_MJPG, PIXFMT_NV12, PIXFMT_RGB565, PIXFMT_YUYV}; use crate::camera_backend::{ CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, INTERVALS_PER_SEC, @@ -232,7 +232,10 @@ impl V4l2CameraBackend { } fn is_pixfmt_supported(&self, pixelformat: u32) -> bool { - pixelformat == PIXFMT_MJPG || pixelformat == PIXFMT_RGB565 || pixelformat == PIXFMT_YUYV + pixelformat == PIXFMT_MJPG + || pixelformat == PIXFMT_RGB565 + || pixelformat == PIXFMT_YUYV + || pixelformat == PIXFMT_NV12 } } @@ -440,6 +443,7 @@ fn cam_fmt_to_v4l2(t: &FmtType) -> u32 { FmtType::Yuy2 => PIXFMT_YUYV, FmtType::Rgb565 => PIXFMT_RGB565, FmtType::Mjpg => PIXFMT_MJPG, + FmtType::Nv12 => PIXFMT_NV12, } } @@ -448,6 +452,7 @@ fn cam_fmt_from_v4l2(t: u32) -> Result { PIXFMT_YUYV => FmtType::Yuy2, PIXFMT_RGB565 => FmtType::Rgb565, PIXFMT_MJPG => FmtType::Mjpg, + PIXFMT_NV12 => FmtType::Nv12, _ => bail!("Invalid v4l2 type {}", t), }; Ok(fmt) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 50d007d5f..bd7ae9fb6 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -493,8 +493,8 @@ impl VideoStreamingControl { error!("Invalid interval {:?}", e); 0 }); - self.dwMaxVideoFrameSize = - get_video_frame_size(fmt.width, fmt.height).unwrap_or_else(|e| { + self.dwMaxVideoFrameSize = get_video_frame_size(fmt.width, fmt.height, fmt.fmttype) + .unwrap_or_else(|e| { error!("Invalid frame size {:?}", e); 0 }); @@ -730,7 +730,8 @@ impl UsbCamera { ) -> Result<()> { self.vs_control.bFormatIndex = vs_control.bFormatIndex; self.vs_control.bFrameIndex = vs_control.bFrameIndex; - self.vs_control.dwMaxVideoFrameSize = get_video_frame_size(fmt.width, fmt.height)?; + self.vs_control.dwMaxVideoFrameSize = + get_video_frame_size(fmt.width, fmt.height, fmt.fmttype)?; self.vs_control.dwFrameInterval = vs_control.dwFrameInterval; Ok(()) } @@ -1105,8 +1106,13 @@ fn gen_intface_header_desc(fmt_num: u8) -> VsDescInputHeader { } fn gen_fmt_header(fmt: &CameraFormatList) -> Result> { + let bits_per_pixel = match fmt.format { + FmtType::Yuy2 | FmtType::Rgb565 => 0x10, + FmtType::Nv12 => 0xc, + _ => 0, + }; let header = match fmt.format { - FmtType::Yuy2 | FmtType::Rgb565 => VsDescUncompressedFmt { + FmtType::Yuy2 | FmtType::Rgb565 | FmtType::Nv12 => VsDescUncompressedFmt { bLength: 0x1B, bDescriptorType: CS_INTERFACE, bDescriptorSubtype: VS_FORMAT_UNCOMPRESSED, @@ -1115,7 +1121,7 @@ fn gen_fmt_header(fmt: &CameraFormatList) -> Result> { guidFormat: *MEDIA_TYPE_GUID_HASHMAP .get(&fmt.format) .with_context(|| "unsupported video format.")?, - bBitsPerPixel: 0x10, + bBitsPerPixel: bits_per_pixel, bDefaultFrameIndex: 1, bAspectRatioX: 0, bAspectRatioY: 0, @@ -1144,22 +1150,27 @@ fn gen_fmt_header(fmt: &CameraFormatList) -> Result> { Ok(header) } +#[inline(always)] +fn get_subtype(pixfmt: FmtType) -> u8 { + match pixfmt { + FmtType::Yuy2 | FmtType::Rgb565 | FmtType::Nv12 => VS_FRAME_UNCOMPRESSED, + FmtType::Mjpg => VS_FRAME_MJPEG, + } +} + fn gen_frm_desc(pixfmt: FmtType, frm: &CameraFrame) -> Result> { - let bitrate = get_bit_rate(frm.width, frm.height, frm.interval)?; + let bitrate = get_bit_rate(frm.width, frm.height, frm.interval, pixfmt)?; let desc = VsDescFrm { bLength: 0x1e, // TODO: vary with interval number. bDescriptorType: CS_INTERFACE, - bDescriptorSubtype: match pixfmt { - FmtType::Rgb565 | FmtType::Yuy2 => VS_FRAME_UNCOMPRESSED, - FmtType::Mjpg => VS_FRAME_MJPEG, - }, + bDescriptorSubtype: get_subtype(pixfmt), bFrameIndex: frm.index, bmCapabilities: 0x1, wWidth: frm.width as u16, wHeight: frm.height as u16, dwMinBitRate: bitrate, dwMaxBitRate: bitrate, - dwMaxVideoFrameBufSize: get_video_frame_size(frm.width, frm.height)?, + dwMaxVideoFrameBufSize: get_video_frame_size(frm.width, frm.height, pixfmt)?, dwDefaultFrameInterval: frm.interval, bFrameIntervalType: 1, dwIntervalVals: frm.interval, diff --git a/devices/src/usb/camera_media_type_guid.rs b/devices/src/usb/camera_media_type_guid.rs index 1b520eba1..409e20c2f 100644 --- a/devices/src/usb/camera_media_type_guid.rs +++ b/devices/src/usb/camera_media_type_guid.rs @@ -18,7 +18,7 @@ use once_cell::sync::Lazy; use crate::camera_backend::FmtType; -pub const MEDIA_TYPE_GUID: [(FmtType, [u8; 16]); 2] = [ +pub const MEDIA_TYPE_GUID: [(FmtType, [u8; 16]); 3] = [ ( FmtType::Yuy2, [ @@ -33,6 +33,13 @@ pub const MEDIA_TYPE_GUID: [(FmtType, [u8; 16]); 2] = [ 0x9b, 0x71, ], ), + ( + FmtType::Nv12, + [ + b'N', b'V', b'1', b'2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, + 0x9b, 0x71, + ], + ), ]; pub static MEDIA_TYPE_GUID_HASHMAP: Lazy> = -- Gitee From 723437c60921796d485eeafc12c82e07345c82a8 Mon Sep 17 00:00:00 2001 From: yezengruan Date: Fri, 23 Feb 2024 11:20:28 +0800 Subject: [PATCH 1639/2187] acpi: add pci segment number in dsdt table Signed-off-by: yezengruan --- devices/src/pci/host.rs | 2 ++ tests/mod_test/tests/aarch64/acpi_test.rs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 1ec5dd6fe..510ecfb94 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -447,6 +447,8 @@ impl AmlBuilder for PciHost { // CCA: Cache Coherency Attribute, which determines whether // guest supports DMA features in pci host on aarch64 platform. pci_host_bridge.append_child(AmlNameDecl::new("_CCA", AmlOne)); + // SEG: The PCI segment number. + pci_host_bridge.append_child(AmlNameDecl::new("_SEG", AmlZero)); } build_osc_for_aml(&mut pci_host_bridge); diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 5e803f88c..d28c8221d 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -26,8 +26,8 @@ use mod_test::libtest::{test_init, TestState}; // Now facs table data length is 64. const FACS_TABLE_DATA_LENGTH: u32 = 64; -// Now dsdt table data length is 3482. -const DSDT_TABLE_DATA_LENGTH: u32 = 3482; +// Now dsdt table data length is 3488. +const DSDT_TABLE_DATA_LENGTH: u32 = 3488; // Now fadt table data length is 276. const FADT_TABLE_DATA_LENGTH: u32 = 276; // Now madt table data length is 744. @@ -43,9 +43,9 @@ const SPCR_TABLE_DATA_LENGTH: u32 = 80; // Now mcfg table data length is 60. const MCFG_TABLE_DATA_LENGTH: u32 = 60; // Now acpi tables data length is 6133(cpu number is 8). -const ACPI_TABLES_DATA_LENGTH_8: usize = 6133; +const ACPI_TABLES_DATA_LENGTH_8: usize = 6139; // Now acpi tables data length is 40574(cpu number is 200). -const ACPI_TABLES_DATA_LENGTH_200: usize = 40574; +const ACPI_TABLES_DATA_LENGTH_200: usize = 40580; enum TABLE { Fadt, -- Gitee From 3d2981f7d2f3757ce45d6ff61e1d6a24e6b578d1 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 21 Feb 2024 13:39:51 +0800 Subject: [PATCH 1640/2187] camera: Add basic OH camera framework support Add basic OH camera framework support. Signed-off-by: zhanghan64 --- Cargo.toml | 1 + devices/Cargo.toml | 5 +- devices/src/camera_backend/demo.rs | 1 + devices/src/camera_backend/mod.rs | 4 + devices/src/camera_backend/ohos/mod.rs | 14 ++ .../src/camera_backend/ohos/ohcam_bindings.rs | 53 ++++++ devices/src/camera_backend/ohos/ohcam_rapi.rs | 161 ++++++++++++++++++ machine/Cargo.toml | 1 + machine_manager/Cargo.toml | 1 + machine_manager/src/config/camera.rs | 6 + machine_manager/src/config/mod.rs | 2 + 11 files changed, 247 insertions(+), 2 deletions(-) create mode 100755 devices/src/camera_backend/ohos/mod.rs create mode 100755 devices/src/camera_backend/ohos/ohcam_bindings.rs create mode 100755 devices/src/camera_backend/ohos/ohcam_rapi.rs diff --git a/Cargo.toml b/Cargo.toml index c3593a065..32af637d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ pvpanic = ["machine/pvpanic"] demo_device = ["machine/demo_device"] usb_host = ["machine/usb_host"] usb_camera_v4l2 = ["machine/usb_camera_v4l2"] +usb_camera_oh = ["machine/usb_camera_oh"] gtk = ["machine/gtk"] vnc = ["machine/vnc"] vnc_auth = ["machine/vnc_auth"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 3f921078d..25529536a 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -47,6 +47,7 @@ scream_ohaudio = ["scream", "machine_manager/scream_ohaudio"] pvpanic = ["machine_manager/pvpanic"] demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] -usb_camera = ["dep:cairo-rs", "machine_manager/usb_camera"] -usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] +usb_camera = ["machine_manager/usb_camera"] +usb_camera_v4l2 = ["usb_camera", "dep:cairo-rs", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] +usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh"] ramfb = ["ui/console", "util/pixman"] diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 5a961bb4d..0147f3f51 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -18,6 +18,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; +#[cfg(not(target_env = "ohos"))] use cairo::{Format, ImageSurface}; use log::{debug, error, info}; use rand::{thread_rng, Rng}; diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 51f963a87..003859978 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -14,6 +14,7 @@ //! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait //! CameraBackend. +#[cfg(not(target_env = "ohos")] pub mod demo; #[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; @@ -22,6 +23,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; +#[cfg(not(target_env = "ohos")] use self::demo::DemoCameraBackend; #[cfg(feature = "usb_camera_v4l2")] use self::v4l2::V4l2CameraBackend; @@ -183,10 +185,12 @@ pub fn create_cam_backend( cameradev.path, config.iothread, )?)), + #[cfg(not(target_env = "ohos")] CamBackendType::Demo => Arc::new(Mutex::new(DemoCameraBackend::new( config.id, cameradev.path, )?)), + _ => bail!("Not supportted CamBackendType."), }; Ok(cam) diff --git a/devices/src/camera_backend/ohos/mod.rs b/devices/src/camera_backend/ohos/mod.rs new file mode 100755 index 000000000..eae150cfb --- /dev/null +++ b/devices/src/camera_backend/ohos/mod.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod ohcam_bindings; +mod ohcam_rapi; diff --git a/devices/src/camera_backend/ohos/ohcam_bindings.rs b/devices/src/camera_backend/ohos/ohcam_bindings.rs new file mode 100755 index 000000000..1e36dd7fc --- /dev/null +++ b/devices/src/camera_backend/ohos/ohcam_bindings.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::{c_int, c_void}; + +// SAFETY: The safety of this function is guaranteed by caller. +pub type BufferProcess = unsafe extern "C" fn(src_buffer: u64, length: i32); +// SAFETY: The safety of this function is guaranteed by caller. +pub type BrokenProcess = unsafe extern "C" fn(); + +#[derive(Default)] +pub struct ProfileRecorder { + pub fmt: i32, + pub width: i32, + pub height: i32, + pub fps: i32, +} + +#[link(name = "camera_wrapper.z")] +extern "C" { + pub fn OhcamCreateCtx() -> *mut c_void; + pub fn OhcamCreateSession(ctx: *mut c_void) -> c_int; + pub fn OhcamReleaseSession(ctx: *mut c_void); + pub fn OhcamInitCameras(ctx: *mut c_void) -> c_int; + pub fn OhcamInitProfiles(ctx: *mut c_void) -> c_int; + pub fn OhcamGetProfileSize(ctx: *mut c_void, idx: c_int) -> c_int; + pub fn OhcamGetProfile( + ctx: *mut c_void, + cam_idx: c_int, + profile_idx: c_int, + profile: *mut c_void, + ) -> c_int; + pub fn OhcamSetProfile(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> c_int; + pub fn OhcamPreStart( + ctx: *mut c_void, + buffer_proc: BufferProcess, + broken_process: BrokenProcess, + ) -> c_int; + pub fn OhcamStart(ctx: *mut c_void) -> c_int; + pub fn OhcamStopOutput(ctx: *mut c_void); + pub fn OhcamRelease(ctx: *mut c_void); + pub fn OhcamDestroyCtx(ctx: *mut *mut c_void); + pub fn OhcamAllowNextFrame(ctx: *mut c_void); +} diff --git a/devices/src/camera_backend/ohos/ohcam_rapi.rs b/devices/src/camera_backend/ohos/ohcam_rapi.rs new file mode 100755 index 000000000..37a5a5069 --- /dev/null +++ b/devices/src/camera_backend/ohos/ohcam_rapi.rs @@ -0,0 +1,161 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::{c_int, c_void}; +use std::ptr; + +use anyhow::{bail, Result}; + +use super::ohcam_bindings as capi; + +// OH camera framework's related definitions +#[allow(unused)] +pub const CAMERA_FORMAT_YCBCR420: i32 = 2; +#[allow(unused)] +pub const CAMERA_FORMAT_RGB18888: i32 = 3; +pub const CAMERA_FORMAT_YUV420SP: i32 = 1003; +pub const CAMERA_FORMAT_MJPEG: i32 = 2000; + +// camera path is actually the specified camera ID. +pub fn check_cam_idx(idx: u8) -> Result<()> { + // SAFETY: We call related API sequentially for specified ctx. + let mut ctx = unsafe { capi::OhcamCreateCtx() }; + // SAFETY: We call related API sequentially for specified ctx. + let n = unsafe { capi::OhcamInitCameras(ctx) }; + if n < 0 { + ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); + bail!("OHCAM WRAPPER: failed to init cameras"); + } else if idx + 1 > n as u8 { + ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); + bail!("Invalid idx: {}, valid num is less than {}", idx, n); + } + ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); + Ok(()) +} + +pub fn ohcam_init() -> Result<*mut c_void> { + // SAFETY: We call related API sequentially for specified ctx. + let mut ctx = unsafe { capi::OhcamCreateCtx() }; + if ctx.is_null() { + bail!("OHCAM WRAPPER: create camera ctx failed"); + } + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + if capi::OhcamInitCameras(ctx) < 0 { + ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); + bail!("OHCAM WRAPPER: failed to init cameras"); + } + if capi::OhcamInitProfiles(ctx) < 0 { + ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); + bail!("OHCAM WRAPPER: failed to init profiles"); + } + } + Ok(ctx) +} + +pub fn ohcam_get_fmt_nums(ctx: *mut c_void, idx: c_int) -> Result { + // SAFETY: We call related API sequentially for specified ctx. + let ret = unsafe { capi::OhcamGetProfileSize(ctx, idx) }; + if ret < 0 { + bail!("OHCAM WRAPPER: invalid camera idx {}", idx); + } + Ok(ret) +} + +pub fn ohcam_release_camera(ctx: *mut c_void) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { capi::OhcamRelease(ctx) }; +} + +pub fn ohcam_drop_ctx(p_ctx: *mut *mut c_void) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { capi::OhcamDestroyCtx(p_ctx) }; +} + +pub fn ohcam_set_fmt(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> Result<()> { + // SAFETY: We call related API sequentially for specified ctx. + let ret = unsafe { capi::OhcamSetProfile(ctx, cam_idx, profile_idx) }; + if ret < 0 { + bail!("OHCAM WRAPPER: Failed to ohcam_set_profile"); + } + Ok(()) +} + +pub fn ohcam_start_stream( + ctx: *mut c_void, + buffer_proc: capi::BufferProcess, + broken_process: capi::BrokenProcess, +) -> Result<()> { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + if capi::OhcamPreStart(ctx, buffer_proc, broken_process) != 0 { + bail!("OHCAM WRAPPER: Pre start failed"); + } + if capi::OhcamStart(ctx) != 0 { + bail!("OHCAM WRAPPER: Start failed"); + } + } + Ok(()) +} + +pub fn ohcam_reset_cam(ctx: *mut c_void) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + capi::OhcamCreateSession(ctx); + capi::OhcamInitCameras(ctx); + capi::OhcamInitProfiles(ctx); + } +} + +pub fn ohcam_stop_stream(ctx: *mut c_void) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + capi::OhcamStopOutput(ctx); + capi::OhcamReleaseSession(ctx); + } +} + +pub fn ohcam_get_profile( + ctx: *mut c_void, + cam_idx: c_int, + profile_idx: c_int, + format: *mut c_int, + width: *mut c_int, + height: *mut c_int, + fps: *mut c_int, +) -> Result<()> { + let profile_recorder = capi::ProfileRecorder::default(); + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + let ret = capi::OhcamGetProfile( + ctx, + cam_idx, + profile_idx, + ptr::addr_of!(profile_recorder) as *mut c_void, + ); + if ret < 0 { + bail!("Failed to OhcamGetProfile"); + } + *format = profile_recorder.fmt; + *width = profile_recorder.width; + *height = profile_recorder.height; + *fps = profile_recorder.fps; + } + Ok(()) +} + +pub fn ohcam_next_frame(ctx: *mut c_void) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + capi::OhcamAllowNextFrame(ctx); + } +} diff --git a/machine/Cargo.toml b/machine/Cargo.toml index f3f0966f1..d77eb78be 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -42,6 +42,7 @@ demo_device = ["devices/demo_device", "machine_manager/demo_device"] usb_host = ["devices/usb_host", "machine_manager/usb_host"] usb_camera = ["devices/usb_camera", "machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "devices/usb_camera_v4l2", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] +usb_camera_oh = ["usb_camera", "devices/usb_camera_oh", "machine_manager/usb_camera_oh"] windows_emu_pid = ["ui/console", "machine_manager/windows_emu_pid"] gtk = ["windows_emu_pid", "ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index c47dccfac..b1869f755 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -34,6 +34,7 @@ demo_device = [] usb_host = [] usb_camera = [] usb_camera_v4l2 = ["usb_camera"] +usb_camera_oh = ["usb_camera"] windows_emu_pid = [] ohui_srv = [] gtk = [] diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index 63353f8b3..999616ee2 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -36,6 +36,9 @@ pub struct CameraDevConfig { pub enum CamBackendType { #[cfg(feature = "usb_camera_v4l2")] V4l2, + #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] + OhCamera, + #[cfg(not(target_env = "ohos"))] Demo, } @@ -46,6 +49,9 @@ impl FromStr for CamBackendType { match s { #[cfg(feature = "usb_camera_v4l2")] "v4l2" => Ok(CamBackendType::V4l2), + #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] + "ohcamera" => Ok(CamBackendType::OhCamera), + #[cfg(not(target_env = "ohos"))] "demo" => Ok(CamBackendType::Demo), _ => Err(anyhow!("Unknown camera backend type")), } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index f59dc4ce2..87a375cca 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -86,6 +86,7 @@ pub use vnc::*; use std::collections::HashMap; use std::fs::File; use std::io::Read; +#[cfg(not(all(target_env = "ohos", feature = "usb_camera_oh")))] use std::path::Path; use std::str::FromStr; @@ -758,6 +759,7 @@ pub fn valid_id(id: &str) -> Result { pub fn existed_path(path: &str) -> Result { let filepath = path.to_string(); + #[cfg(not(target_env = "ohos"))] if !Path::new(path).exists() { bail!(ConfigError::FileNotExist(filepath)); } -- Gitee From 37444eaf524dc1ee40d1f4d2563d402dff33c227 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 23 Feb 2024 11:28:49 +0800 Subject: [PATCH 1641/2187] camera:enable virtual camera on OHOS enable virtual camera on OHOS Signed-off-by: zhanghan64 --- devices/src/camera_backend/demo.rs | 5 +- devices/src/camera_backend/mod.rs | 29 ++- devices/src/camera_backend/ohos/mod.rs | 2 + devices/src/camera_backend/ohos/ohcam.rs | 308 +++++++++++++++++++++++ devices/src/camera_backend/v4l2.rs | 5 +- machine_manager/src/config/camera.rs | 4 +- machine_manager/src/config/mod.rs | 12 - 7 files changed, 342 insertions(+), 23 deletions(-) create mode 100755 devices/src/camera_backend/ohos/ohcam.rs diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 0147f3f51..b3ff2e922 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize}; use super::INTERVALS_PER_SEC; use crate::camera_backend::{ - CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, + check_path, CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, }; use util::aio::{mem_from_buf, Iovec}; @@ -140,9 +140,10 @@ pub struct DemoCameraBackend { impl DemoCameraBackend { pub fn new(id: String, config_path: String) -> Result { + let checked_path = check_path(config_path.as_str())?; Ok(DemoCameraBackend { id, - config_path, + config_path: checked_path, frame_image: Arc::new(Mutex::new(FrameImage::default())), notify_cb: None, broken_cb: None, diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 003859978..a47ce2662 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -14,21 +14,26 @@ //! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait //! CameraBackend. -#[cfg(not(target_env = "ohos")] +#[cfg(not(target_env = "ohos"))] pub mod demo; +#[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] +pub mod ohos; #[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; +use std::path::Path; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -#[cfg(not(target_env = "ohos")] +#[cfg(not(target_env = "ohos"))] use self::demo::DemoCameraBackend; +#[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] +use self::ohos::ohcam::OhCameraBackend; #[cfg(feature = "usb_camera_v4l2")] use self::v4l2::V4l2CameraBackend; use crate::usb::camera::UsbCameraConfig; -use machine_manager::config::{CamBackendType, CameraDevConfig}; +use machine_manager::config::{CamBackendType, CameraDevConfig, ConfigError}; use util::aio::Iovec; /// Frame interval in 100ns units. @@ -75,6 +80,15 @@ pub struct CameraFormatList { pub frame: Vec, } +pub fn check_path(path: &str) -> Result { + let filepath = path.to_string(); + if !Path::new(path).exists() { + bail!(ConfigError::FileNotExist(filepath)); + } + + Ok(filepath) +} + pub fn get_video_frame_size(width: u32, height: u32, fmt: FmtType) -> Result { let pixel_size = width .checked_mul(height) @@ -174,6 +188,7 @@ pub trait CameraBackend: Send + Sync { fn register_broken_cb(&mut self, cb: CameraBrokenCallback); } +#[allow(unused_variables)] pub fn create_cam_backend( config: UsbCameraConfig, cameradev: CameraDevConfig, @@ -185,12 +200,16 @@ pub fn create_cam_backend( cameradev.path, config.iothread, )?)), - #[cfg(not(target_env = "ohos")] + #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] + CamBackendType::OhCamera => Arc::new(Mutex::new(OhCameraBackend::new( + cameradev.id, + cameradev.path, + )?)), + #[cfg(not(target_env = "ohos"))] CamBackendType::Demo => Arc::new(Mutex::new(DemoCameraBackend::new( config.id, cameradev.path, )?)), - _ => bail!("Not supportted CamBackendType."), }; Ok(cam) diff --git a/devices/src/camera_backend/ohos/mod.rs b/devices/src/camera_backend/ohos/mod.rs index eae150cfb..e21270a73 100755 --- a/devices/src/camera_backend/ohos/mod.rs +++ b/devices/src/camera_backend/ohos/mod.rs @@ -10,5 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod ohcam; + mod ohcam_bindings; mod ohcam_rapi; diff --git a/devices/src/camera_backend/ohos/ohcam.rs b/devices/src/camera_backend/ohos/ohcam.rs new file mode 100755 index 000000000..05b80e65d --- /dev/null +++ b/devices/src/camera_backend/ohos/ohcam.rs @@ -0,0 +1,308 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::{c_int, c_void}; +use std::sync::RwLock; + +use anyhow::{bail, Context, Result}; +use once_cell::sync::Lazy; + +use super::ohcam_rapi::*; +use crate::camera_backend::{ + CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, + CameraNotifyCallback, FmtType, +}; +use util::aio::Iovec; + +type OhCamCB = RwLock; +static OHCAM_CALLBACK: Lazy = Lazy::new(|| RwLock::new(OhCamCallBack::default())); + +// In UVC, interval's unit is 100ns. +// So, fps * interval / 10_000_000 == 1. +const FPS_INTERVAL_TRANS: u32 = 10_000_000; + +#[derive(Default)] +struct OhCamCallBack { + /// Callback to used to notify when data is coming. + notify_cb: Option, + /// Callback to used to notify the broken. + broken_cb: Option, + ptr: Option, + buffer_size: u64, +} + +impl OhCamCallBack { + fn set_buffer(&mut self, addr: u64, s: i32) { + self.buffer_size = s as u64; + self.ptr = Some(addr); + } + + fn get_buffer(&self) -> (Option, u64) { + (self.ptr, self.buffer_size) + } + + fn clear_buffer(&mut self) { + self.buffer_size = 0; + self.ptr = None; + } + + fn set_notify_cb(&mut self, cb: CameraNotifyCallback) { + self.notify_cb = Some(cb); + } + + fn set_broken_cb(&mut self, cb: CameraNotifyCallback) { + self.broken_cb = Some(cb); + } + + fn notify(&self) { + if let Some(notify_cb) = &self.notify_cb { + notify_cb(); + } + } + + fn broken(&self) { + if let Some(broken_cb) = &self.broken_cb { + broken_cb(); + } + } +} + +#[derive(Clone)] +pub struct OhCameraBackend { + id: String, + camidx: u8, + profile_cnt: u8, + ctx: *mut c_void, + fmt_list: Vec, + selected_profile: u8, +} + +// SAFETY: Send and Sync is not auto-implemented for raw pointer type. +// implementing them is safe because ctx field is access. +unsafe impl Send for OhCameraBackend {} +// SAFETY: Same reason as above. +unsafe impl Sync for OhCameraBackend {} + +fn cam_fmt_from_oh(t: i32) -> Result { + let fmt = match t { + CAMERA_FORMAT_YUV420SP => FmtType::Nv12, + CAMERA_FORMAT_MJPEG => FmtType::Mjpg, + _ => bail!("OHCAM: No supported type {}", t), + }; + + Ok(fmt) +} + +impl OhCameraBackend { + pub fn new(id: String, camid: String) -> Result { + let p_ctx = ohcam_init()?; + let idx = camid.parse::().with_context(|| "Invalid PATH format")?; + check_cam_idx(idx)?; + + let profile_cnt = ohcam_get_fmt_nums(p_ctx, idx as i32)? as u8; + + Ok(OhCameraBackend { + id, + camidx: idx, + profile_cnt, + ctx: p_ctx, + fmt_list: vec![], + selected_profile: 0, + }) + } +} + +impl Drop for OhCameraBackend { + fn drop(&mut self) { + ohcam_release_camera(self.ctx); + ohcam_drop_ctx(std::ptr::addr_of_mut!(self.ctx)); + } +} + +impl CameraBackend for OhCameraBackend { + fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { + for fmt in &self.fmt_list { + if fmt.format != cam_fmt.fmttype { + continue; + } + for frm in &fmt.frame { + if frm.width != cam_fmt.width || frm.height != cam_fmt.height { + continue; + } + + let fps = FPS_INTERVAL_TRANS + .checked_div(frm.interval) + .with_context(|| format!("OHCAM: Invalid interval {}", frm.interval))?; + if fps != cam_fmt.fps { + continue; + } + + self.selected_profile = fmt.fmt_index - 1; + ohcam_set_fmt(self.ctx, self.camidx as i32, self.selected_profile as i32)?; + return Ok(()); + } + } + Ok(()) + } + + fn set_ctl(&self) -> Result<()> { + Ok(()) + } + + fn video_stream_on(&mut self) -> Result<()> { + ohcam_start_stream(self.ctx, on_buffer_available, on_broken) + } + + fn video_stream_off(&mut self) -> Result<()> { + ohcam_stop_stream(self.ctx); + OHCAM_CALLBACK.write().unwrap().clear_buffer(); + Ok(()) + } + + fn list_format(&mut self) -> Result> { + let mut fmt_list: Vec = Vec::new(); + let mut fmt: c_int = 0; + let mut w: c_int = 0; + let mut h: c_int = 0; + let mut fps: c_int = 0; + for idx in 0..self.profile_cnt { + if ohcam_get_profile( + self.ctx, + self.camidx as i32, + idx as i32, + &mut fmt, + &mut w, + &mut h, + &mut fps, + ) + .is_err() + { + continue; + } + // NOTE: windows camera APP doesn't support fps lower than 30, and some OH PC only support 15 fps. + if fps < 30 { + fps = 30; + } + if (fmt != CAMERA_FORMAT_YUV420SP) && (fmt != CAMERA_FORMAT_MJPEG) { + continue; + } + let frame = CameraFrame { + width: w as u32, + height: h as u32, + index: 1, + interval: FPS_INTERVAL_TRANS / fps as u32, + }; + fmt_list.push(CameraFormatList { + format: cam_fmt_from_oh(fmt)?, + frame: vec![frame], + fmt_index: (idx) + 1, + }); + } + self.fmt_list = fmt_list.clone(); + Ok(fmt_list) + } + + fn reset(&mut self) { + OHCAM_CALLBACK.write().unwrap().clear_buffer(); + ohcam_reset_cam(self.ctx); + } + + fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result { + let mut out = CamBasicFmt::default(); + for fmt in &self.fmt_list { + if fmt.fmt_index != format_index { + continue; + } + out.fmttype = fmt.format; + for frm in &fmt.frame { + if frm.index != frame_index { + continue; + } + out.width = frm.width; + out.height = frm.height; + out.fps = FPS_INTERVAL_TRANS + .checked_div(frm.interval) + .with_context(|| { + format!( + "{}: Invalid interval {} for format/frame {}:{}", + self.id, frm.interval, format_index, frame_index + ) + })?; + return Ok(out); + } + } + bail!( + "{}: format/frame with idx {}/{} is not found", + self.id, + format_index, + frame_index + ); + } + + fn get_frame_size(&self) -> usize { + OHCAM_CALLBACK.read().unwrap().get_buffer().1 as usize + } + + fn next_frame(&mut self) -> Result<()> { + ohcam_next_frame(self.ctx); + OHCAM_CALLBACK.write().unwrap().clear_buffer(); + Ok(()) + } + + fn get_frame(&self, iovecs: &[Iovec], frame_offset: usize, len: usize) -> Result { + let (src, src_len) = OHCAM_CALLBACK.read().unwrap().get_buffer(); + if src_len == 0 { + bail!("Invalid frame src_len {}", src_len); + } + + if frame_offset + len > src_len as usize { + bail!("Invalid frame offset {} or len {}", frame_offset, len); + } + + let mut copied = 0; + for iov in iovecs { + if len == copied { + break; + } + let cnt = std::cmp::min(iov.iov_len as usize, len - copied); + let src_ptr = src.unwrap() + frame_offset as u64 + copied as u64; + // SAFETY: the address is not out of range. + unsafe { + std::ptr::copy_nonoverlapping(src_ptr as *const u8, iov.iov_base as *mut u8, cnt); + } + copied += cnt; + } + Ok(copied) + } + + fn register_notify_cb(&mut self, cb: CameraNotifyCallback) { + OHCAM_CALLBACK.write().unwrap().set_notify_cb(cb); + } + + fn register_broken_cb(&mut self, cb: CameraBrokenCallback) { + OHCAM_CALLBACK.write().unwrap().set_broken_cb(cb); + } +} + +// SAFETY: use RW lock to ensure the security of resources. +unsafe extern "C" fn on_buffer_available(src_buffer: u64, length: i32) { + OHCAM_CALLBACK + .write() + .unwrap() + .set_buffer(src_buffer, length); + OHCAM_CALLBACK.read().unwrap().notify(); +} + +// SAFETY: use RW lock to ensure the security of resources. +unsafe extern "C" fn on_broken() { + OHCAM_CALLBACK.read().unwrap().broken(); +} diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 7118d987c..0885d1da7 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -28,7 +28,7 @@ use vmm_sys_util::epoll::EventSet; use super::{PIXFMT_MJPG, PIXFMT_NV12, PIXFMT_RGB565, PIXFMT_YUYV}; use crate::camera_backend::{ - CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, + check_path, CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, INTERVALS_PER_SEC, }; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -80,9 +80,10 @@ pub struct V4l2CameraBackend { impl V4l2CameraBackend { pub fn new(id: String, path: String, iothread: Option) -> Result { let backend = V4l2Backend::new(path.clone(), BUFFER_CNT)?; + let checked_path = check_path(path.as_str())?; let cam = V4l2CameraBackend { id, - dev_path: path, + dev_path: checked_path, sample: Arc::new(Mutex::new(Sample::default())), backend: Some(Arc::new(backend)), running: false, diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index 999616ee2..90872b46a 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -17,7 +17,7 @@ use clap::Parser; use serde::{Deserialize, Serialize}; use crate::{ - config::{existed_path, str_slip_to_clap, valid_id, VmConfig}, + config::{str_slip_to_clap, valid_id, VmConfig}, qmp::qmp_schema, }; @@ -26,7 +26,7 @@ use crate::{ pub struct CameraDevConfig { #[arg(long, value_parser = valid_id)] pub id: String, - #[arg(long, value_parser = existed_path)] + #[arg(long)] pub path: String, #[arg(long)] pub backend: CamBackendType, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 87a375cca..e7b0bfeb3 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -86,8 +86,6 @@ pub use vnc::*; use std::collections::HashMap; use std::fs::File; use std::io::Read; -#[cfg(not(all(target_env = "ohos", feature = "usb_camera_oh")))] -use std::path::Path; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; @@ -757,16 +755,6 @@ pub fn valid_id(id: &str) -> Result { Ok(id.to_string()) } -pub fn existed_path(path: &str) -> Result { - let filepath = path.to_string(); - #[cfg(not(target_env = "ohos"))] - if !Path::new(path).exists() { - bail!(ConfigError::FileNotExist(filepath)); - } - - Ok(filepath) -} - #[cfg(test)] mod tests { use super::*; -- Gitee From 3098e684311e2915fc153e67b77cafb68ce2d577 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 23 Feb 2024 16:57:57 +0800 Subject: [PATCH 1642/2187] OHUI:UT:fix keyboard UT for OHOS keycodes on OHOS is unique, fix it in UT case. --- ui/src/input.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/src/input.rs b/ui/src/input.rs index ffc454d43..c41ba1fc6 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -629,9 +629,15 @@ mod tests { test_input.register_input(); let test_kdb = test_input.kbd.clone(); + #[cfg(not(all(target_env = "ohos", feature = "ohui_srv")))] let keysym2qkeycode = KeyCode::keysym_to_qkeycode(DpyMod::Gtk); + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + let keysym2qkeycode = KeyCode::keysym_to_qkeycode(DpyMod::Ohui); // ["0", "a", "space"] + #[cfg(not(all(target_env = "ohos", feature = "ohui_srv")))] let keysym_lists: Vec = vec![0x0030, 0x0061, 0x0020]; + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + let keysym_lists: Vec = vec![0x07D0, 0x07E1, 0x0802]; let keycode_lists: Vec = keysym_lists .iter() .map(|x| *keysym2qkeycode.get(&x).unwrap()) -- Gitee From e8994e861b965d93b759e34d1b219a5e5df5fbbd Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 26 Feb 2024 09:15:17 +0800 Subject: [PATCH 1643/2187] Interrupt Controller: Refactor the interface of triggering interrupt. For Edge-sensitive interrupt, introduce a general interface to trigger interrupt. Signed-off-by: Jinhao Gao --- devices/src/interrupt_controller/mod.rs | 11 +++++++---- devices/src/pci/intx.rs | 2 +- hypervisor/src/kvm/mod.rs | 14 +++++++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 34916629e..4a0453c8f 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -67,7 +67,11 @@ pub trait LineIrqManager: Send + Sync { Ok(()) } - fn set_irq_line(&self, _irq: u32, _level: bool) -> Result<()> { + fn set_level_irq(&self, _irq: u32, _level: bool) -> Result<()> { + Ok(()) + } + + fn set_edge_irq(&self, _irq: u32) -> Result<()> { Ok(()) } @@ -155,10 +159,9 @@ impl IrqState { return irq_handler.write_irqfd(irq_fd.clone()); } if self.trigger_mode == TriggerMode::Edge { - irq_handler.set_irq_line(self.irq, true)?; - irq_handler.set_irq_line(self.irq, false) + irq_handler.set_edge_irq(self.irq) } else { - irq_handler.set_irq_line(self.irq, true) + irq_handler.set_level_irq(self.irq, true) } } } diff --git a/devices/src/pci/intx.rs b/devices/src/pci/intx.rs index c450ff633..bf53fa520 100644 --- a/devices/src/pci/intx.rs +++ b/devices/src/pci/intx.rs @@ -102,7 +102,7 @@ impl Intx { } let irq_handler = &locked_intx_state.irq_handler; - if let Err(e) = irq_handler.set_irq_line(irq, level) { + if let Err(e) = irq_handler.set_level_irq(irq, level) { error!( "Failed to set irq {} level {} of device {}: {}.", irq, level, self.device_name, e diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 39db57fdb..3a4fffd5b 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -794,13 +794,21 @@ impl LineIrqManager for KVMInterruptManager { Ok(()) } - fn set_irq_line(&self, gsi: u32, level: bool) -> Result<()> { + fn set_level_irq(&self, gsi: u32, level: bool) -> Result<()> { let kvm_irq = self.arch_map_irq(gsi); self.vm_fd .set_irq_line(kvm_irq, level) - .with_context(|| format!("Failed to set irq {} level {:?}.", kvm_irq, level))?; + .with_context(|| format!("Failed to set irq {} level {:?}.", kvm_irq, level)) + } - Ok(()) + fn set_edge_irq(&self, gsi: u32) -> Result<()> { + let kvm_irq = self.arch_map_irq(gsi); + self.vm_fd + .set_irq_line(kvm_irq, true) + .with_context(|| format!("Failed to set irq {} level {:?}.", kvm_irq, true))?; + self.vm_fd + .set_irq_line(kvm_irq, false) + .with_context(|| format!("Failed to set irq {} level {:?}.", kvm_irq, false)) } fn write_irqfd(&self, irq_fd: Arc) -> Result<()> { -- Gitee From 4cee4b21bb6a2d415785c1be32af9b2259dc56b1 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 27 Feb 2024 16:31:07 +0800 Subject: [PATCH 1644/2187] bugfix: fix "cargo build" check missing gpu feature control makes "cargo build" failed, fix it. --- virtio/src/transport/virtio_pci.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index d8cd3ecf8..aa27db4f6 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -18,12 +18,13 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, warn}; -use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use vmm_sys_util::eventfd::EventFd; +#[cfg(feature = "virtio_gpu")] +use crate::Gpu; use crate::{ - virtio_has_feature, Gpu, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, - VirtioDeviceQuirk, VirtioInterrupt, VirtioInterruptType, + virtio_has_feature, NotifyEventFds, Queue, VirtioBaseState, VirtioDevice, VirtioDeviceQuirk, + VirtioInterrupt, VirtioInterruptType, }; use crate::{ CONFIG_STATUS_ACKNOWLEDGE, CONFIG_STATUS_DRIVER, CONFIG_STATUS_DRIVER_OK, CONFIG_STATUS_FAILED, @@ -32,9 +33,9 @@ use crate::{ VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_CONSOLE, VIRTIO_TYPE_FS, VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, }; -use address_space::{ - AddressRange, AddressSpace, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, -}; +#[cfg(feature = "virtio_gpu")] +use address_space::HostMemMapping; +use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; use devices::pci::config::{ RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, @@ -46,6 +47,8 @@ use devices::pci::{ PciBus, PciDevBase, PciDevOps, PciError, }; use devices::{Device, DeviceBase}; +#[cfg(feature = "virtio_gpu")] +use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -128,6 +131,7 @@ const COMMON_Q_USEDHI_REG: u64 = 0x34; /// 1: select feature bits 32 to 63. const MAX_FEATURES_SELECT_NUM: u32 = 2; +#[cfg(feature = "virtio_gpu")] fn init_gpu_bar0(dev: &Arc>, config: &mut PciConfig) -> Result<()> { let locked_dev = dev.lock().unwrap(); let gpu = locked_dev.as_any().downcast_ref::().unwrap(); @@ -1110,6 +1114,7 @@ impl PciDevOps for VirtioPciDevice { self.assign_interrupt_cb(); + #[cfg(feature = "virtio_gpu")] if device_quirk == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0) { init_gpu_bar0(&self.device, &mut self.base.config)?; } -- Gitee From 7a846701da5fd1c016e8d832176ea92fee45eecf Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 27 Feb 2024 19:20:26 +0800 Subject: [PATCH 1645/2187] Machine: Refactor the destroy interface of standardvm When shutdown vm, whether inside or outside the vm, the destruction is performed by the main thread. Signed-off-by: Jinhao Gao --- machine/src/aarch64/standard.rs | 35 ++++++++++++++++++------------ machine/src/standard_common/mod.rs | 2 +- machine/src/x86_64/standard.rs | 33 ++++++++++++++++++---------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index d50237e09..7cef00855 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -62,7 +62,6 @@ use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, MigrateMode, NumaNode, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; -use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, VmState, @@ -254,6 +253,25 @@ impl StdMachine { Ok(()) } + pub fn handle_destroy_request(vm: &Arc>) -> Result<()> { + let locked_vm = vm.lock().unwrap(); + let vmstate = { + let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); + *state + }; + + if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { + warn!("Failed to destroy guest, destroy continue."); + if locked_vm.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request.") + } + } + + info!("vm destroy"); + + Ok(()) + } + fn build_pptt_cores(&self, pptt: &mut AcpiTable, cluster_offset: u32, uid: &mut u32) { for core in 0..self.base.cpu_topo.cores { let mut priv_resources = vec![0; 3]; @@ -1174,22 +1192,11 @@ impl MachineLifecycle for StdMachine { } fn destroy(&self) -> bool { - let vmstate = { - let state = self.base.vm_state.deref().0.lock().unwrap(); - *state - }; - - if !self.notify_lifecycle(vmstate, VmState::Shutdown) { - warn!("Failed to destroy guest, destroy continue."); - if self.shutdown_req.write(1).is_err() { - error!("Failed to send shutdown request.") - } + if self.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request."); return false; } - info!("vm destroy"); - EventLoop::get_ctx(None).unwrap().kick(); - true } diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 4b831657d..33458c9b5 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -343,7 +343,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { let _ret = shutdown_req.read(); - if clone_vm.lock().unwrap().destroy() { + if StdMachine::handle_destroy_request(&clone_vm).is_ok() { Some(gen_delete_notifiers(&[shutdown_req_fd])) } else { None diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index e8882d131..c8abfd871 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -16,7 +16,7 @@ use std::ops::Deref; use std::sync::{Arc, Barrier, Mutex}; use anyhow::{bail, Context, Result}; -use log::{error, info}; +use log::{error, info, warn}; use vmm_sys_util::eventfd::EventFd; use super::ich9_lpc; @@ -48,7 +48,6 @@ use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, MigrateMode, NumaNode, PFlashConfig, SerialConfig, VmConfig, }; use machine_manager::event; -use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, VmState, @@ -207,6 +206,25 @@ impl StdMachine { Ok(()) } + pub fn handle_destroy_request(vm: &Arc>) -> Result<()> { + let locked_vm = vm.lock().unwrap(); + let vmstate = { + let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); + *state + }; + + if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { + warn!("Failed to destroy guest, destroy continue."); + if locked_vm.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request.") + } + } + + info!("vm destroy"); + + Ok(()) + } + fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); @@ -977,18 +995,11 @@ impl MachineLifecycle for StdMachine { } fn destroy(&self) -> bool { - let vmstate = { - let state = self.base.vm_state.deref().0.lock().unwrap(); - *state - }; - - if !self.notify_lifecycle(vmstate, VmState::Shutdown) { + if self.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request."); return false; } - info!("vm destroy"); - EventLoop::get_ctx(None).unwrap().kick(); - true } -- Gitee From 8173a7f144d8cf51b4423311f671c3b8cf1893ad Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Wed, 7 Feb 2024 09:38:43 +0800 Subject: [PATCH 1646/2187] PvPanic: Add MST for PvPanic add MST for PvPanic device Signed-off-by: Fan Xuan Zhe --- tests/mod_test/Cargo.toml | 3 +- tests/mod_test/tests/pvpanic_test.rs | 181 +++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 tests/mod_test/tests/pvpanic_test.rs diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 406988b6f..32507e99a 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -14,8 +14,9 @@ serde_json = "1.0" libc = "0.2" byteorder = "1.4.3" serde = { version = "1.0", features = ["derive"] } -devices = { path = "../../devices", features = ["scream"]} +devices = { path = "../../devices", features = ["scream", "pvpanic"]} util = { path = "../../util" } acpi = { path = "../../acpi" } machine = { path = "../../machine" } +machine_manager = { path = "../../machine_manager"} virtio = { path = "../../virtio", features = ["virtio_gpu"] } diff --git a/tests/mod_test/tests/pvpanic_test.rs b/tests/mod_test/tests/pvpanic_test.rs new file mode 100644 index 000000000..044515966 --- /dev/null +++ b/tests/mod_test/tests/pvpanic_test.rs @@ -0,0 +1,181 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cell::RefCell; +use std::fs; +use std::path::Path; +use std::rc::Rc; + +use devices::pci::config::{ + PCI_CLASS_SYSTEM_OTHER, PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_SUBDEVICE_ID_QEMU, + PCI_VENDOR_ID_REDHAT, PCI_VENDOR_ID_REDHAT_QUMRANET, +}; +use machine_manager::config::{PVPANIC_CRASHLOADED, PVPANIC_PANICKED}; +use mod_test::{ + libdriver::{machine::TestStdMachine, pci::*}, + libtest::{test_init, TestState, MACHINE_TYPE_ARG}, +}; + +const TMP_LOG_PATH: &str = "/tmp/pvpanic-mst.log"; +const BUS_NUM: u8 = 0; +const ADDR: u8 = 7; +const DEFAULT_SUPPORTED_FEATURE: u8 = (PVPANIC_PANICKED | PVPANIC_CRASHLOADED) as u8; + +#[derive(Clone, Copy)] +struct PvPanicDevCfg { + bus_num: u8, + addr: u8, + supported_features: u8, +} + +impl Default for PvPanicDevCfg { + fn default() -> Self { + Self { + bus_num: BUS_NUM, + addr: ADDR, + supported_features: DEFAULT_SUPPORTED_FEATURE, + } + } +} + +impl PvPanicDevCfg { + fn init(&self, enable_log: bool) -> (Rc>, Rc>) { + let mut test_machine_args: Vec<&str> = Vec::new(); + + let mut args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); + test_machine_args.append(&mut args); + + if enable_log { + let mut args: Vec<&str> = vec!["-D", TMP_LOG_PATH]; + test_machine_args.append(&mut args); + } + + let pvpanic_str = fmt_pvpanic_deves(self.clone()); + args = pvpanic_str[..].split(' ').collect(); + test_machine_args.append(&mut args); + + let test_state = Rc::new(RefCell::new(test_init(test_machine_args))); + let machine = Rc::new(RefCell::new(TestStdMachine::new(test_state.clone()))); + + let mut pvpanic_pci_dev = TestPciDev::new(machine.clone().borrow().pci_bus.clone()); + let devfn = self.addr << 3; + pvpanic_pci_dev.devfn = devfn; + + pvpanic_pci_dev.set_bus_num(self.bus_num); + pvpanic_pci_dev.enable(); + + (Rc::new(RefCell::new(pvpanic_pci_dev)), test_state) + } +} + +fn fmt_pvpanic_deves(cfg: PvPanicDevCfg) -> String { + format!( + "-device pvpanic,id=pvpanic_pci,bus=pcie.{},addr=0x{},supported-features={}", + cfg.bus_num, cfg.addr, cfg.supported_features, + ) +} + +/// PvPanic device read config space. +/// TestStep: +/// 1. Init device. +/// 2. Read PvPanic device config space. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn test_pvpanic_read_config() { + let cfg = PvPanicDevCfg::default(); + + let (pvpanic_pci_dev, test_state) = cfg.init(false); + + let info = pvpanic_pci_dev.borrow().config_readw(PCI_VENDOR_ID); + assert_eq!(info, PCI_VENDOR_ID_REDHAT); + + let info = pvpanic_pci_dev.borrow().config_readw(PCI_DEVICE_ID); + assert_eq!(info, PCI_DEVICE_ID_REDHAT_PVPANIC); + + let info = pvpanic_pci_dev.borrow().config_readw(PCI_SUB_CLASS_DEVICE); + assert_eq!(info, PCI_CLASS_SYSTEM_OTHER); + + let info = pvpanic_pci_dev + .borrow() + .config_readw(PCI_SUBSYSTEM_VENDOR_ID); + assert_eq!(info, PCI_VENDOR_ID_REDHAT_QUMRANET); + + let info = pvpanic_pci_dev.borrow().config_readw(PCI_SUBSYSTEM_ID); + assert_eq!(info, PCI_SUBDEVICE_ID_QEMU); + + test_state.borrow_mut().stop(); +} + +/// PvPanic device read supported features. +/// TestStep: +/// 1. Init device. +/// 2. Read supported features of PvPanic to emulate front-end driver. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn test_pvpanic_read_supported_features() { + let cfg = PvPanicDevCfg::default(); + + let (pvpanic_pci_dev, test_state) = cfg.init(false); + + let bar_addr = pvpanic_pci_dev.borrow().io_map(0); + + let start = bar_addr; + + let info = test_state.borrow().readb(start); + assert_eq!(info, DEFAULT_SUPPORTED_FEATURE); + + test_state.borrow_mut().stop(); +} + +/// PvPanic device write events. +/// TestStep: +/// 1. Init device. +/// 2. Write 3 types of events to PvPanic bar0 to emulate front-end driver and check device behaviors via log. +/// 3. Destroy device. +/// Expect: +/// 1/2/3: success. +#[test] +fn test_pvpanic_write_events() { + let cfg = PvPanicDevCfg::default(); + + if Path::new(TMP_LOG_PATH).exists() { + fs::remove_file(TMP_LOG_PATH).unwrap(); + } + + let (pvpanic_pci_dev, test_state) = cfg.init(true); + + let bar_addr = pvpanic_pci_dev.borrow().io_map(0); + let start = bar_addr; + let tmp_log_path = String::from(TMP_LOG_PATH); + let write_test_params: [(u8, &str); 3] = [ + (PVPANIC_PANICKED as u8, "pvpanic: panicked event"), + (PVPANIC_CRASHLOADED as u8, "pvpanic: crashloaded event"), + (!DEFAULT_SUPPORTED_FEATURE, "pvpanic: unknown event"), + ]; + + for &(data, expected_log_content) in write_test_params.iter() { + test_state.borrow().writeb(start, data); + let tmp_log_content = std::fs::read_to_string(&tmp_log_path).unwrap(); + + assert!(tmp_log_content.contains(expected_log_content)); + } + + test_state.borrow_mut().stop(); + match fs::remove_file(TMP_LOG_PATH) { + Ok(_) => {} + Err(e) => assert!(false, "{}", e), + } +} -- Gitee From 03c5bd50c7c29e5b31a04923a0962a509e05ef67 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 29 Feb 2024 11:32:26 +0800 Subject: [PATCH 1647/2187] CPU: Refactor some interface of CPUHypervisorOps Delete kick_vcpu_thread method and rename kick method. Signed-off-by: Jinhao Gao --- Cargo.lock | 1 + cpu/src/lib.rs | 8 +------- hypervisor/src/kvm/mod.rs | 36 ++++++++++++++++-------------------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c258dfea2..e7d4347a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1056,6 +1056,7 @@ dependencies = [ "hex", "libc", "machine", + "machine_manager", "rand", "serde", "serde_json", diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 97fad8f7e..a25a08aa5 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -180,13 +180,7 @@ pub trait CPUHypervisorOps: Send + Sync { thread_barrier: Arc, ) -> Result<()>; - fn kick(&self) -> Result<()>; - - fn kick_vcpu_thread( - &self, - task: Arc>>>, - state: Arc<(Mutex, Condvar)>, - ) -> Result<()>; + fn set_hypervisor_exit(&self) -> Result<()>; fn pause( &self, diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 3a4fffd5b..8eff335bb 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -393,7 +393,7 @@ impl KvmCpu { extern "C" fn handle_signal(signum: c_int, _: *mut siginfo_t, _: *mut c_void) { if signum == VCPU_TASK_SIGNAL { let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - vcpu.hypervisor_cpu().kick().unwrap(); + vcpu.hypervisor_cpu().set_hypervisor_exit().unwrap(); // Setting pause_signal to be `true` if kvm changes vCPU to pause state. vcpu.pause_signal().store(true, Ordering::SeqCst); fence(Ordering::Release) @@ -508,6 +508,19 @@ impl KvmCpu { } Ok(true) } + + fn kick_vcpu_thread(&self, task: Arc>>>) -> Result<()> { + let task = task.lock().unwrap(); + match task.as_ref() { + Some(thread) => thread + .kill(VCPU_TASK_SIGNAL) + .with_context(|| CpuError::KickVcpu("Fail to kick vcpu".to_string())), + None => { + warn!("VCPU thread not started, no need to kick"); + Ok(()) + } + } + } } impl CPUHypervisorOps for KvmCpu { @@ -616,28 +629,11 @@ impl CPUHypervisorOps for KvmCpu { Ok(()) } - fn kick(&self) -> Result<()> { + fn set_hypervisor_exit(&self) -> Result<()> { self.fd.set_kvm_immediate_exit(1); Ok(()) } - fn kick_vcpu_thread( - &self, - task: Arc>>>, - _state: Arc<(Mutex, Condvar)>, - ) -> Result<()> { - let task = task.lock().unwrap(); - match task.as_ref() { - Some(thread) => thread - .kill(VCPU_TASK_SIGNAL) - .with_context(|| CpuError::KickVcpu("Fail to kick vcpu".to_string())), - None => { - warn!("VCPU thread not started, no need to kick"); - Ok(()) - } - } - } - fn pause( &self, task: Arc>>>, @@ -709,7 +705,7 @@ impl CPUHypervisorOps for KvmCpu { } drop(locked_cpu_state); - self.kick_vcpu_thread(task, state.clone())?; + self.kick_vcpu_thread(task)?; let mut locked_cpu_state = cpu_state.lock().unwrap(); locked_cpu_state = cvar .wait_timeout(locked_cpu_state, Duration::from_millis(32)) -- Gitee From 47265fae6b15b1cfecb325f5928c365356d6a50d Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Thu, 29 Feb 2024 15:36:22 +0800 Subject: [PATCH 1648/2187] PvPanic: update Cargo.lock Updating Cargo.lock due to adding MST for PvPanic device Signed-off-by: Fan Xuan Zhe --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 43c592ba2..cb95d080b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1018,6 +1018,7 @@ dependencies = [ "hex", "libc", "machine", + "machine_manager", "rand", "serde", "serde_json", -- Gitee From 436285ce992d6a1f5efdfa6c57d78f7e58bb39ca Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 29 Feb 2024 15:14:29 +0800 Subject: [PATCH 1649/2187] cpu: bugfix vcpu lifecycle of guest reset and pause 1. When the vcpu receives the guest reset, the cpu state is set to Paused to prevent the vcpu enter guest again. 2. Setting vcpu pause_signal to true when vcpu already Paused. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 7 ++++++- hypervisor/src/kvm/mod.rs | 9 ++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index a25a08aa5..7190e2b76 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -62,7 +62,7 @@ pub use x86_64::X86CPUTopology as CPUTopology; pub use x86_64::X86RegsIndex as RegsIndex; use std::cell::RefCell; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{fence, AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; @@ -428,6 +428,8 @@ impl CPUInterface for CPU { fn guest_reset(&self) -> Result<()> { if let Some(vm) = self.vm.upgrade() { + let (cpu_state, _) = &*self.state; + *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; vm.lock().unwrap().reset(); } else { return Err(anyhow!(CpuError::NoMachineInterface)); @@ -487,6 +489,9 @@ impl CPUThreadWorker { info!("Vcpu{} paused", self.thread_cpu.id); flag = 1; } + // Setting pause_signal to be `true` if kvm changes vCPU to pause state. + self.thread_cpu.pause_signal().store(true, Ordering::SeqCst); + fence(Ordering::Release); cpu_state = cvar.wait(cpu_state).unwrap(); } CpuLifecycleState::Running => { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 8eff335bb..5c2d9394b 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -27,7 +27,7 @@ pub use aarch64::gicv2::KvmGICv2; pub use aarch64::gicv3::{KvmGICv3, KvmGICv3Its}; use std::collections::HashMap; -use std::sync::atomic::{fence, AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Condvar, Mutex}; use std::thread; use std::time::Duration; @@ -393,10 +393,7 @@ impl KvmCpu { extern "C" fn handle_signal(signum: c_int, _: *mut siginfo_t, _: *mut c_void) { if signum == VCPU_TASK_SIGNAL { let _ = CPUThreadWorker::run_on_local_thread_vcpu(|vcpu| { - vcpu.hypervisor_cpu().set_hypervisor_exit().unwrap(); - // Setting pause_signal to be `true` if kvm changes vCPU to pause state. - vcpu.pause_signal().store(true, Ordering::SeqCst); - fence(Ordering::Release) + vcpu.hypervisor_cpu().set_hypervisor_exit().unwrap() }); } } @@ -646,6 +643,8 @@ impl CPUHypervisorOps for KvmCpu { if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; cvar.notify_one() + } else if *cpu_state.lock().unwrap() == CpuLifecycleState::Paused { + return Ok(()); } match task.as_ref() { -- Gitee From 6ec5f70da9f51fa64fe45825595e6c7dae5f5109 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Thu, 29 Feb 2024 17:54:21 +0800 Subject: [PATCH 1650/2187] fmt: fix some cargo clippy errors error: deref which would be done by auto-deref Signed-off-by: AlisaWang --- machine_manager/src/config/pci.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 522b4a8d8..e1ad4985a 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -77,15 +77,14 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { } let slot = addr_vec.first().unwrap(); - let slot = str_to_num::(*slot).with_context(|| format!("Invalid slot num: {}", slot))?; + let slot = str_to_num::(slot).with_context(|| format!("Invalid slot num: {}", slot))?; if slot > 31 { bail!("Invalid slot num: {}", slot); } let func = if addr_vec.get(1).is_some() { let function = addr_vec.get(1).unwrap(); - str_to_num::(*function) - .with_context(|| format!("Invalid function num: {}", function))? + str_to_num::(function).with_context(|| format!("Invalid function num: {}", function))? } else { 0 }; -- Gitee From bc72c5a1fb7e95be082c7ec649e15bd46262f852 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 29 Feb 2024 19:26:04 +0800 Subject: [PATCH 1651/2187] CPU: Modify the set_tid method Add the parameter for set_tid in case that some hypervisor need to modify tid of cpu thread. Signed-off-by: Jinhao Gao --- cpu/src/lib.rs | 8 ++++++-- hypervisor/src/kvm/mod.rs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 7190e2b76..7a1162951 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -281,8 +281,12 @@ impl CPU { } /// Set thread id for `CPU`. - pub fn set_tid(&self) { - *self.tid.lock().unwrap() = Some(gettid().as_raw() as u64); + pub fn set_tid(&self, tid: Option) { + if tid.is_none() { + *self.tid.lock().unwrap() = Some(gettid().as_raw() as u64); + } else { + *self.tid.lock().unwrap() = tid; + } } /// Get the hypervisor of this `CPU`. diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 5c2d9394b..2abfb2833 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -582,7 +582,7 @@ impl CPUHypervisorOps for KvmCpu { ); } - cpu_thread_worker.thread_cpu.set_tid(); + cpu_thread_worker.thread_cpu.set_tid(None); #[cfg(not(test))] self.put_register(cpu_thread_worker.thread_cpu.clone())?; -- Gitee From 851b0875e99919fec8ca87ef835fb06bf996034c Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 2 Mar 2024 06:12:53 +0800 Subject: [PATCH 1652/2187] Trace: add trace for loop context utils/loop_context trace point: update_event timer_add timer_del timer_run Signed-off-by: Xiao Ye --- trace/trace_event/event_loop.toml | 24 ++++++++++++++++++++++++ util/src/loop_context.rs | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 trace/trace_event/event_loop.toml diff --git a/trace/trace_event/event_loop.toml b/trace/trace_event/event_loop.toml new file mode 100644 index 000000000..0ff3af23b --- /dev/null +++ b/trace/trace_event/event_loop.toml @@ -0,0 +1,24 @@ + +[[events]] +name = "update_event" +args = "raw_fd: &dyn fmt::Debug, operation: &dyn fmt::Debug" +message = "raw_fd={:?} operation={:?}" +enabled = true + +[[events]] +name = "timer_add" +args = "id: &dyn fmt::Debug, expire_time: &dyn fmt::Debug" +message = "id={:?} expire_time={:?}" +enabled = true + +[[events]] +name = "timer_del" +args = "id: &dyn fmt::Debug, expire_time: &dyn fmt::Debug" +message = "id={:?} expire_time={:?}" +enabled = true + +[[events]] +name = "timer_run" +args = "id: &dyn fmt::Debug" +message = "id={:?}" +enabled = true diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 8317f34ab..10e4cf706 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -473,6 +473,7 @@ impl EventLoopContext { /// * `notifiers` - event notifiers wanted to add to or remove from `EventLoop`. pub fn update_events(&mut self, notifiers: Vec) -> Result<()> { for en in notifiers { + trace::update_event(&en.raw_fd, &en.op); match en.op { NotifierOperation::AddExclusion | NotifierOperation::AddShared => { self.add_event(en)?; @@ -554,6 +555,7 @@ impl EventLoopContext { break; } } + trace::timer_add(&timer.id, &timer.expire_time); timers.insert(index, timer); drop(timers); self.kick(); @@ -565,6 +567,7 @@ impl EventLoopContext { let mut timers = self.timers.lock().unwrap(); for (i, t) in timers.iter().enumerate() { if timer_id == t.id { + trace::timer_del(&t.id, &t.expire_time); timers.remove(i); break; } @@ -603,6 +606,7 @@ impl EventLoopContext { let expired_timers: Vec> = timers.drain(0..expired_nr).collect(); drop(timers); for timer in expired_timers { + trace::timer_run(&timer.id); (timer.func)(); } } -- Gitee From 533a56829fa4801defdf951b367eb8cd50814981 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 3 Mar 2024 06:42:26 +0800 Subject: [PATCH 1653/2187] Trace: add trace for thread pool utils/thread_pool trace point: thread_pool_submit_task thread_pool_spawn_thread Signed-off-by: Xiao Ye --- trace/trace_event/event_loop.toml | 18 ++++++++++++++++++ util/src/thread_pool.rs | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/trace/trace_event/event_loop.toml b/trace/trace_event/event_loop.toml index 0ff3af23b..f8f6b27d6 100644 --- a/trace/trace_event/event_loop.toml +++ b/trace/trace_event/event_loop.toml @@ -22,3 +22,21 @@ name = "timer_run" args = "id: &dyn fmt::Debug" message = "id={:?}" enabled = true + +[[events]] +name = "thread_pool_submit_task" +args = "" +message = "" +enabled = true + +[[events]] +name = "thread_pool_spawn_thread" +args = "total_threads: &u64, blocked_threads: &u64, new_threads: &u64, pending_threads: &u64" +message = "total_threads={:?} blocked_threads={:?} new_threads={:?} pending_threads={:?}" +enabled = true + +[[events]] +name = "thread_pool_exit_thread" +args = "total_threads: &u64, lists_len: &dyn fmt::Debug" +message = "total_threads={:?} lists_len={:?}" +enabled = true diff --git a/util/src/thread_pool.rs b/util/src/thread_pool.rs index ecfac7ddb..db059a808 100644 --- a/util/src/thread_pool.rs +++ b/util/src/thread_pool.rs @@ -90,6 +90,12 @@ impl PoolState { self.new_threads -= 1; self.pending_threads += 1; + trace::thread_pool_spawn_thread( + &self.total_threads, + &self.blocked_threads, + &self.new_threads, + &self.pending_threads, + ); thread::Builder::new() .name("thread-pool".to_string()) .spawn(move || worker_thread(pool)) @@ -120,6 +126,7 @@ impl Default for ThreadPool { impl ThreadPool { /// Submit task to thread pool. pub fn submit_task(pool: Arc, task: Box) -> Result<()> { + trace::thread_pool_submit_task(); let mut locked_state = pool.pool_state.lock().unwrap(); if locked_state.spawn_thread_needed() { locked_state.spawn_thread(pool.clone())? @@ -198,6 +205,7 @@ fn worker_thread(pool: Arc) { locked_state = pool.pool_state.lock().unwrap(); } locked_state.total_threads -= 1; + trace::thread_pool_exit_thread(&locked_state.total_threads, &locked_state.req_lists.len); pool.stop_cond.notify_one(); pool.request_cond.notify_one(); -- Gitee From c06e5324abf21f61ecdab8b8beded5ad89e4e3df Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 4 Mar 2024 15:50:38 +0800 Subject: [PATCH 1654/2187] AddressSpace: Introduce the ioeventfd_enabled flag Implement io eventfd notification in AddressSpace when the hypervisor don't support io eventfd features. So add a flag to determine whether the io eventfd feature is enabled. Signed-off-by: Jinhao Gao --- address_space/src/address_space.rs | 10 ++++++++-- hypervisor/src/kvm/mod.rs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index f646c615f..cee4fbfcb 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -25,7 +25,6 @@ use crate::{ use migration::{migration::Migratable, MigrationManager}; use util::aio::Iovec; use util::byte_code::ByteCode; -use util::test_helper::is_test_enabled; /// Contains an array of `FlatRange`. #[derive(Default, Clone, Debug)] @@ -134,6 +133,8 @@ pub struct AddressSpace { ioeventfds: Arc>>, /// The backend memory region tree, used for migrate. machine_ram: Option>, + /// Whether the hypervisor enables the ioeventfd. + hyp_ioevtfd_enabled: Arc>, } impl fmt::Debug for AddressSpace { @@ -165,6 +166,7 @@ impl AddressSpace { listeners: Arc::new(Mutex::new(Vec::new())), ioeventfds: Arc::new(Mutex::new(Vec::new())), machine_ram, + hyp_ioevtfd_enabled: Arc::new(Mutex::new(false)), }); root.set_belonged_address_space(&space); @@ -622,7 +624,7 @@ impl AddressSpace { pub fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { let view = self.flat_view.load(); - if is_test_enabled() { + if !*self.hyp_ioevtfd_enabled.lock().unwrap() { for evtfd in self.ioeventfds.lock().unwrap().iter() { if addr != evtfd.addr_range.base || count != evtfd.addr_range.size { continue; @@ -745,6 +747,10 @@ impl AddressSpace { .with_context(|| "Failed to generate and update ioeventfds")?; Ok(()) } + + pub fn set_ioevtfd_enabled(&self, ioevtfd_enabled: bool) { + *self.hyp_ioevtfd_enabled.lock().unwrap() = ioevtfd_enabled; + } } #[cfg(test)] diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 2abfb2833..1bd06fa37 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -150,7 +150,7 @@ impl HypervisorOps for KvmHypervisor { sys_mem: &Arc, ) -> Result<()> { self.arch_init()?; - + sys_mem.set_ioevtfd_enabled(true); sys_mem .register_listener(self.create_memory_listener()) .with_context(|| "Failed to register hypervisor listener for memory space.")?; -- Gitee From ac3dd7696cf99710f0177e3b57ddd3e2c89efb36 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 3 Mar 2024 22:36:18 +0800 Subject: [PATCH 1655/2187] Fix Bad system call on x86_64 system libc::SYS_rseq is not added to the seccomp filtering whitelist on x86_64 systems; Signed-off-by: Yan Wen --- machine/src/x86_64/standard.rs | 2 ++ machine_manager/src/signal_handler.rs | 22 +++++++++++++++------- src/main.rs | 16 ++++++++-------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index c8abfd871..a5e12fa72 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -779,6 +779,8 @@ pub(crate) fn arch_syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_sched_setattr), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_fadvise64), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rseq), ] } diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 6b922aac5..2f1ba86e9 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -27,6 +27,7 @@ use util::set_termi_canon_mode; pub const VM_EXIT_GENE_ERR: i32 = 1; const SYSTEMCALL_OFFSET: isize = 6; +const SYS_SECCOMP: i32 = 1; static mut RECEIVED_SIGNAL: AtomicI32 = AtomicI32::new(0); @@ -87,13 +88,20 @@ extern "C" fn receive_signal_kill(num: c_int, _: *mut siginfo_t, _: *mut c_void) extern "C" fn receive_signal_sys(num: c_int, info: *mut siginfo_t, _: *mut c_void) { set_signal(num); // SAFETY: The safety of this function is guaranteed by caller. - let badcall = unsafe { *(info as *const i32).offset(SYSTEMCALL_OFFSET) as usize }; - write!( - &mut std::io::stderr(), - "Received a bad system call, number: {} \r\n", - badcall - ) - .expect("Failed to write to stderr"); + if let Some(sig_info) = unsafe { info.as_ref() } { + if SYS_SECCOMP == sig_info.si_code { + eprintln!("seccomp violation, Try running with `strace -ff` to identify the cause."); + } + + // SAFETY: the pointer is not null. + let badcall = unsafe { *(info.cast::().offset(SYSTEMCALL_OFFSET)) }; + write!( + &mut std::io::stderr(), + "Received a bad system call, number: {} \r\n", + badcall + ) + .expect("Failed to write to stderr"); + } } /// Register kill signal handler. Signals supported now are SIGTERM and SIGSYS. diff --git a/src/main.rs b/src/main.rs index 4310ab13f..15eaefc31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -203,6 +203,14 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } }; + let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); + if !cmd_args.is_present("disable-seccomp") { + vm.lock() + .unwrap() + .register_seccomp(balloon_switch_on) + .with_context(|| "Failed to register seccomp rules.")?; + } + for socket in sockets { EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(socket))), @@ -213,14 +221,6 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res machine::vm_run(&vm, cmd_args).with_context(|| "Failed to start VM.")?; - let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); - if !cmd_args.is_present("disable-seccomp") { - vm.lock() - .unwrap() - .register_seccomp(balloon_switch_on) - .with_context(|| "Failed to register seccomp rules.")?; - } - EventLoop::loop_run().with_context(|| "MainLoop exits unexpectedly: error occurs")?; EventLoop::loop_clean(); Ok(()) -- Gitee From 2a32979b5fd6a8afa6797210bbd0ac8a72be26a0 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 6 Mar 2024 11:12:34 +0800 Subject: [PATCH 1656/2187] Build: Delete redundant dynamic library refereces. Delete redundant dynamic library refereces. Signed-off-by: Jinhao Gao --- build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/build.rs b/build.rs index e060c79a5..96f77ffd4 100644 --- a/build.rs +++ b/build.rs @@ -16,7 +16,6 @@ fn ohos_env_configure() { println!("cargo:rustc-link-arg=--verbose"); println!("cargo:rustc-link-arg=--sysroot={}/sysroot", ohos_sdk_path); println!("cargo:rustc-link-arg=-lpixman_static"); - println!("cargo:rustc-link-lib=hmlibs"); println!( "cargo:rustc-link-search={}/sysroot/usr/lib/aarch64-linux-ohos", ohos_sdk_path -- Gitee From 1986aacce8dda20eb9139750868418e851ad3c13 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 4 Mar 2024 22:38:02 +0800 Subject: [PATCH 1657/2187] AddressSpace: Refactor hyp_ioevtfd_enabled Replace Mutex lock with OnceCell to reduce performance overhead. Signed-off-by: Jinhao Gao --- Cargo.lock | 1 + address_space/Cargo.toml | 1 + address_space/src/address_space.rs | 12 ++++++++---- hypervisor/src/kvm/mod.rs | 7 +++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7d4347a4..478857db9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "migration", "migration_derive", "nix 0.26.2", + "once_cell", "thiserror", "util", "vmm-sys-util", diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 332da9364..96b4327ef 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -14,6 +14,7 @@ vmm-sys-util = "0.11.1" arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" +once_cell = "1.18.0" machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index cee4fbfcb..f2fcf414a 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -17,6 +17,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; use arc_swap::ArcSwap; +use log::error; +use once_cell::sync::OnceCell; use crate::{ AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, @@ -134,7 +136,7 @@ pub struct AddressSpace { /// The backend memory region tree, used for migrate. machine_ram: Option>, /// Whether the hypervisor enables the ioeventfd. - hyp_ioevtfd_enabled: Arc>, + hyp_ioevtfd_enabled: OnceCell, } impl fmt::Debug for AddressSpace { @@ -166,7 +168,7 @@ impl AddressSpace { listeners: Arc::new(Mutex::new(Vec::new())), ioeventfds: Arc::new(Mutex::new(Vec::new())), machine_ram, - hyp_ioevtfd_enabled: Arc::new(Mutex::new(false)), + hyp_ioevtfd_enabled: OnceCell::new(), }); root.set_belonged_address_space(&space); @@ -624,7 +626,7 @@ impl AddressSpace { pub fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { let view = self.flat_view.load(); - if !*self.hyp_ioevtfd_enabled.lock().unwrap() { + if !*self.hyp_ioevtfd_enabled.get_or_init(|| false) { for evtfd in self.ioeventfds.lock().unwrap().iter() { if addr != evtfd.addr_range.base || count != evtfd.addr_range.size { continue; @@ -749,7 +751,9 @@ impl AddressSpace { } pub fn set_ioevtfd_enabled(&self, ioevtfd_enabled: bool) { - *self.hyp_ioevtfd_enabled.lock().unwrap() = ioevtfd_enabled; + self.hyp_ioevtfd_enabled + .set(ioevtfd_enabled) + .unwrap_or_else(|_| error!("Failed to set hyp_ioevtfd_enabled")); } } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 1bd06fa37..b1ca6afa2 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -73,7 +73,6 @@ use machine_manager::machine::HypervisorType; #[cfg(target_arch = "aarch64")] use migration::snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}; use migration::{MigrateMemSlot, MigrateOps, MigrationManager}; -#[cfg(not(test))] use util::test_helper::is_test_enabled; #[cfg(target_arch = "x86_64")] use x86_64::cpu_caps::X86CPUCaps as CPUCaps; @@ -150,7 +149,11 @@ impl HypervisorOps for KvmHypervisor { sys_mem: &Arc, ) -> Result<()> { self.arch_init()?; - sys_mem.set_ioevtfd_enabled(true); + + if !is_test_enabled() { + sys_mem.set_ioevtfd_enabled(true); + } + sys_mem .register_listener(self.create_memory_listener()) .with_context(|| "Failed to register hypervisor listener for memory space.")?; -- Gitee From 3aa066dcb8a5f57262ed8d54f8ac3dd5cc64b75e Mon Sep 17 00:00:00 2001 From: yezengruan Date: Tue, 5 Mar 2024 21:30:25 +0800 Subject: [PATCH 1658/2187] qmp: fix unexpected type returned by query-qmp-schema Fix unexpected type returned by QEMU command 'query-qmp-schema'. Now we use `need_strum` to determine whether to add `#[strum(serialize = $name)]`. Signed-off-by: yezengruan --- machine_manager/src/qmp/qmp_schema.rs | 126 +++++++++++++------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 19e993109..6627ff7c0 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -54,7 +54,7 @@ impl QmpErrorClass { } macro_rules! define_qmp_command_enum { - ($($command:ident($name:expr, $args_type:ty $(, $serde_default:ident)?)),*) => { + ($($command:ident($name:expr, $args_type:ty, $need_strum:ident $(, $serde_default:ident)?)),*) => { /// A enum to store all command struct #[derive(Debug, Clone, Serialize, Deserialize, EnumIter, EnumVariantNames, EnumString)] #[serde(tag = "execute")] @@ -62,7 +62,7 @@ macro_rules! define_qmp_command_enum { pub enum QmpCommand { $( #[serde(rename = $name)] - #[strum(serialize = $name)] + #[cfg_attr($need_strum, strum(serialize = $name))] $command { $(#[serde($serde_default)])? arguments: $args_type, @@ -76,67 +76,67 @@ macro_rules! define_qmp_command_enum { // QMP command enum definition example: command("name", arguments, ..) define_qmp_command_enum!( - qmp_capabilities("qmp_capabilities", qmp_capabilities, default), - quit("quit", quit, default), - stop("stop", stop, default), - cont("cont", cont, default), - system_powerdown("system_powerdown", system_powerdown, default), - system_reset("system_reset", system_reset, default), - device_add("device_add", Box), - device_del("device_del", device_del), - chardev_add("chardev-add", chardev_add), - chardev_remove("chardev-remove", chardev_remove), - netdev_add("netdev_add", Box), - netdev_del("netdev_del", netdev_del), - cameradev_add("cameradev_add", cameradev_add), - cameradev_del("cameradev_del", cameradev_del), - query_hotpluggable_cpus("query-hotpluggable-cpus", query_hotpluggable_cpus, default), - query_cpus("query-cpus", query_cpus, default), - query_status("query-status", query_status, default), - getfd("getfd", getfd), - blockdev_add("blockdev-add", Box), - blockdev_del("blockdev-del", blockdev_del), - balloon("balloon", balloon, default), - query_mem("query-mem", query_mem, default), - query_mem_gpa("query-mem-gpa", query_mem_gpa, default), - query_balloon("query-balloon", query_balloon, default), - query_vnc("query-vnc", query_vnc, default), - query_display_image("query-display-image", query_display_image, default), - migrate("migrate", migrate), - query_migrate("query-migrate", query_migrate, default), - cancel_migrate("migrate_cancel", cancel_migrate, default), - query_version("query-version", query_version, default), - query_commands("query-commands", query_commands, default), - query_target("query-target", query_target, default), - query_kvm("query-kvm", query_kvm, default), - query_machines("query-machines", query_machines, default), - query_events("query-events", query_events, default), - list_type("qom-list-types", list_type, default), - device_list_properties("device-list-properties", device_list_properties, default), - block_commit("block-commit", block_commit, default), - query_tpm_models("query-tpm-models", query_tpm_models, default), - query_tpm_types("query-tpm-types", query_tpm_types, default), - query_command_line_options("query-command-line-options", query_command_line_options, default), - query_migrate_capabilities("query-migrate-capabilities", query_migrate_capabilities, default), - query_qmp_schema("query-qmp-schema", query_qmp_schema, default), - query_sev_capabilities("query-sev-capabilities", query_sev_capabilities, default), - query_chardev("query-chardev", query_chardev, default), - qom_list("qom-list", qom_list, default), - qom_get("qom-get", qom_get, default), - query_block("query-block", query_block, default), - query_named_block_nodes("query-named-block-nodes", query_named_block_nodes, default), - query_blockstats("query-blockstats", query_blockstats, default), - query_block_jobs("query-block-jobs", query_block_jobs, default), - query_gic_capabilities("query-gic-capabilities", query_gic_capabilities, default), - query_iothreads("query-iothreads", query_iothreads, default), - update_region("update_region", update_region, default), - input_event("input_event", input_event, default), - human_monitor_command("human-monitor-command", human_monitor_command), - blockdev_snapshot_internal_sync("blockdev-snapshot-internal-sync", blockdev_snapshot_internal), - blockdev_snapshot_delete_internal_sync("blockdev-snapshot-delete-internal-sync", blockdev_snapshot_internal), - query_vcpu_reg("query-vcpu-reg", query_vcpu_reg), - trace_event_get_state("trace-event-get-state", trace_event_get_state), - trace_event_set_state("trace-event-set-state", trace_event_set_state) + qmp_capabilities("qmp_capabilities", qmp_capabilities, FALSE, default), + quit("quit", quit, FALSE, default), + stop("stop", stop, FALSE, default), + cont("cont", cont, FALSE, default), + system_powerdown("system_powerdown", system_powerdown, FALSE, default), + system_reset("system_reset", system_reset, FALSE, default), + device_add("device_add", Box, FALSE), + device_del("device_del", device_del, FALSE), + chardev_add("chardev-add", chardev_add, FALSE), + chardev_remove("chardev-remove", chardev_remove, FALSE), + netdev_add("netdev_add", Box, FALSE), + netdev_del("netdev_del", netdev_del, FALSE), + cameradev_add("cameradev_add", cameradev_add, FALSE), + cameradev_del("cameradev_del", cameradev_del, FALSE), + query_hotpluggable_cpus("query-hotpluggable-cpus", query_hotpluggable_cpus, TRUE, default), + query_cpus("query-cpus", query_cpus, TRUE, default), + query_status("query-status", query_status, FALSE, default), + getfd("getfd", getfd, FALSE), + blockdev_add("blockdev-add", Box, FALSE), + blockdev_del("blockdev-del", blockdev_del, FALSE), + balloon("balloon", balloon, FALSE, default), + query_mem("query-mem", query_mem, FALSE, default), + query_mem_gpa("query-mem-gpa", query_mem_gpa, FALSE, default), + query_balloon("query-balloon", query_balloon, FALSE, default), + query_vnc("query-vnc", query_vnc, TRUE, default), + query_display_image("query-display-image", query_display_image, FALSE, default), + migrate("migrate", migrate, FALSE), + query_migrate("query-migrate", query_migrate, FALSE, default), + cancel_migrate("migrate_cancel", cancel_migrate, FALSE, default), + query_version("query-version", query_version, FALSE, default), + query_commands("query-commands", query_commands, FALSE, default), + query_target("query-target", query_target, FALSE, default), + query_kvm("query-kvm", query_kvm, FALSE, default), + query_machines("query-machines", query_machines, FALSE, default), + query_events("query-events", query_events, TRUE, default), + list_type("qom-list-types", list_type, FALSE, default), + device_list_properties("device-list-properties", device_list_properties, FALSE, default), + block_commit("block-commit", block_commit, TRUE, default), + query_tpm_models("query-tpm-models", query_tpm_models, FALSE, default), + query_tpm_types("query-tpm-types", query_tpm_types, FALSE, default), + query_command_line_options("query-command-line-options", query_command_line_options, FALSE, default), + query_migrate_capabilities("query-migrate-capabilities", query_migrate_capabilities, FALSE, default), + query_qmp_schema("query-qmp-schema", query_qmp_schema, FALSE, default), + query_sev_capabilities("query-sev-capabilities", query_sev_capabilities, FALSE, default), + query_chardev("query-chardev", query_chardev, TRUE, default), + qom_list("qom-list", qom_list, TRUE, default), + qom_get("qom-get", qom_get, TRUE, default), + query_block("query-block", query_block, TRUE, default), + query_named_block_nodes("query-named-block-nodes", query_named_block_nodes, TRUE, default), + query_blockstats("query-blockstats", query_blockstats, TRUE, default), + query_block_jobs("query-block-jobs", query_block_jobs, TRUE, default), + query_gic_capabilities("query-gic-capabilities", query_gic_capabilities, TRUE, default), + query_iothreads("query-iothreads", query_iothreads, TRUE, default), + update_region("update_region", update_region, TRUE, default), + input_event("input_event", input_event, FALSE, default), + human_monitor_command("human-monitor-command", human_monitor_command, FALSE), + blockdev_snapshot_internal_sync("blockdev-snapshot-internal-sync", blockdev_snapshot_internal, FALSE), + blockdev_snapshot_delete_internal_sync("blockdev-snapshot-delete-internal-sync", blockdev_snapshot_internal, FALSE), + query_vcpu_reg("query-vcpu-reg", query_vcpu_reg, FALSE), + trace_event_get_state("trace-event-get-state", trace_event_get_state, FALSE), + trace_event_set_state("trace-event-set-state", trace_event_set_state, FALSE) ); /// Command trait for Deserialize and find back Response. -- Gitee From 07f2d0388357466d75490a7e70101aba2662358c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Sat, 9 Mar 2024 17:45:15 +0800 Subject: [PATCH 1659/2187] AddressSpace: Optimized the ioeventfd mechanism Use binary search instead of iteration to reduce performance loss. Signed-off-by: Jinhao Gao --- address_space/src/address_space.rs | 39 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index f2fcf414a..261365e59 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -627,26 +627,33 @@ impl AddressSpace { let view = self.flat_view.load(); if !*self.hyp_ioevtfd_enabled.get_or_init(|| false) { - for evtfd in self.ioeventfds.lock().unwrap().iter() { - if addr != evtfd.addr_range.base || count != evtfd.addr_range.size { - continue; - } - if !evtfd.data_match { - evtfd.fd.write(1).unwrap(); - return Ok(()); - } + let ioeventfds = self.ioeventfds.lock().unwrap(); + if let Ok(index) = ioeventfds + .as_slice() + .binary_search_by(|ioevtfd| ioevtfd.addr_range.base.cmp(&addr)) + { + let evtfd = &ioeventfds[index]; + if count == evtfd.addr_range.size || evtfd.addr_range.size == 0 { + if !evtfd.data_match { + if let Err(e) = evtfd.fd.write(1) { + error!("Failed to write ioeventfd {:?}: {}", evtfd, e); + } + return Ok(()); + } - let mut buf = Vec::new(); - src.read_to_end(&mut buf).unwrap(); + let mut buf = Vec::new(); + src.read_to_end(&mut buf).unwrap(); - if buf.len() <= 8 { - let data = u64::from_bytes(buf.as_slice()).unwrap(); - if *data == evtfd.data { - evtfd.fd.write(1).unwrap(); - return Ok(()); + if buf.len() <= 8 { + let data = u64::from_bytes(buf.as_slice()).unwrap(); + if *data == evtfd.data { + if let Err(e) = evtfd.fd.write(1) { + error!("Failed to write ioeventfd {:?}: {}", evtfd, e); + } + return Ok(()); + } } } - view.write(&mut buf.as_slice(), addr, count)?; } } -- Gitee From 84d5af96ae604ddc6c3900c3c3000d4992024419 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 12 Mar 2024 11:30:54 +0800 Subject: [PATCH 1660/2187] ohui: fixup crash issue without ohui server Fixup crash issue if ohui server is not configured. Signed-off-by: Zhao Yi Min --- machine/src/aarch64/standard.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index e36a22223..7b67aa171 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -566,8 +566,10 @@ impl MachineOps for StdMachine { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] fn get_ohui_fb(&self) -> Option { - let server = self.ohui_server.as_ref()?; - server.get_ohui_fb() + match &self.ohui_server { + Some(server) => server.get_ohui_fb(), + None => None, + } } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { -- Gitee From 4b9f722487cb3b6a5c9a401908b49bbe1ed5e73e Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Tue, 12 Mar 2024 19:43:14 +0800 Subject: [PATCH 1661/2187] vhost-user: fix restart error when client reconnecting If vhost-user server process has been killed, the socket event was deleted from EventLoop and started a timer to try to reconnect vhost-user server. Before reconnect successfully, VM restart would reset vhost-user device and then device reset would delete event again so that stratovirt would crash. This patch sets reconnecting to false in delete_event() if the reconnecting is ongoing. The device reset is going to connect the server again and vhost_user_reconnect() will directly return. Signed-off-by: Huxiaohang --- virtio/src/vhost/user/client.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index e74b79c57..0bab67572 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -96,6 +96,9 @@ impl ClientInternal { } fn vhost_user_reconnect(client: &Arc>) { + if !client.lock().unwrap().reconnecting { + return; + } let cloned_client = client.clone(); let func = Box::new(move || { vhost_user_reconnect(&cloned_client); @@ -635,6 +638,11 @@ impl VhostUserClient { /// Delete the socket event in ClientInternal. pub fn delete_event(&mut self) -> Result<()> { + if self.reconnecting { + self.reconnecting = false; + // The socket event has been deleted before try to reconnect so let's just return. + return Ok(()); + } unregister_event_helper(None, &mut self.delete_evts) } -- Gitee From ed4a5481188699e8511c8668d9a88c4c76b40ac1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 24 Feb 2024 00:12:41 +0800 Subject: [PATCH 1662/2187] machine: add two kvm ioctl syscalls to whitelist for aarch64 Add two kvm ioctl syscalls to whitelist for aarch64: KVM_SET_MP_STATE KVM_SET_VCPU_EVENTS Fix: 474cae283(CPU: Move the interface of operating CPU register to CPUhypervisorOp) Signed-off-by: liuxiangdong --- hypervisor/src/kvm/mod.rs | 2 ++ hypervisor/src/kvm/x86_64/mod.rs | 2 -- machine/src/standard_common/syscall.rs | 10 ++++++---- machine/src/x86_64/standard.rs | 2 -- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index b1ca6afa2..462525fb2 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -97,6 +97,8 @@ ioctl_iow_nr!(KVM_SET_GSI_ROUTING, KVMIO, 0x6a, kvm_irq_routing); ioctl_iow_nr!(KVM_IRQFD, KVMIO, 0x76, kvm_irqfd); ioctl_iowr_nr!(KVM_GET_IRQCHIP, KVMIO, 0x62, kvm_irqchip); ioctl_iow_nr!(KVM_IRQ_LINE, KVMIO, 0x61, kvm_irq_level); +ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); +ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); #[allow(clippy::upper_case_acronyms)] #[derive(Default)] diff --git a/hypervisor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs index b574b850a..c9f13a374 100644 --- a/hypervisor/src/kvm/x86_64/mod.rs +++ b/hypervisor/src/kvm/x86_64/mod.rs @@ -28,7 +28,6 @@ use cpu::{ArchCPU, CPUBootConfig, RegsIndex, CPU}; // See: https://elixir.bootlin.com/linux/v4.19.123/source/include/uapi/linux/kvm.h ioctl_iowr_nr!(KVM_GET_SUPPORTED_CPUID, KVMIO, 0x05, kvm_cpuid2); ioctl_iow_nr!(KVM_SET_CPUID2, KVMIO, 0x90, kvm_cpuid2); -ioctl_iow_nr!(KVM_SET_MP_STATE, KVMIO, 0x99, kvm_mp_state); ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave); @@ -36,7 +35,6 @@ ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs); ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); -ioctl_iow_nr!(KVM_SET_VCPU_EVENTS, KVMIO, 0xa0, kvm_vcpu_events); ioctl_ior_nr!(KVM_GET_PIT2, KVMIO, 0x9f, kvm_pit_state2); ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave); ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs); diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index 9385d7595..0cfac2e3f 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -225,25 +225,27 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_SET_IRQS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_GET_STATUS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_CHECK_EXTENSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_SET_CONTAINER() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_SET_IOMMU() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_CREATE_DEVICE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_IOMMU_MAP_DMA() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_IOMMU_UNMAP_DMA() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_GET_DEVICE_FD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_INFO() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_RESET() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_REGION_INFO() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_CREATE_DEVICE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32); + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32); #[cfg(feature = "usb_camera_v4l2")] let bpf_rule = bpf_rule diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index a5e12fa72..e294cd647 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -743,7 +743,6 @@ pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SUPPORTED_CPUID() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_CPUID2() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_SREGS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_REGS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XSAVE() as u32) @@ -751,7 +750,6 @@ pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_DEBUGREGS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) } pub(crate) fn arch_syscall_whitelist() -> Vec { -- Gitee From 6f0a23e094cc7b06e3898b22b378e0e8c2315980 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Wed, 13 Mar 2024 19:23:43 +0800 Subject: [PATCH 1663/2187] PvPanic: add unrealize function for PvPanic device add an implementation of unrealize function so as to avoid the possible error return while the bus attempts to unload all devices attached to it. Signed-off-by: Fan Xuan Zhe --- devices/src/misc/pvpanic.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 005dc154e..e2d29dd12 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -221,6 +221,10 @@ impl PciDevOps for PvPanicPci { Ok(()) } + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } + fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.base.parent_bus.upgrade().unwrap(); let locked_parent_bus = parent_bus.lock().unwrap(); -- Gitee From 9693c326d5e19151a5a410834768f5831cfea834 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Thu, 14 Mar 2024 12:32:05 +0800 Subject: [PATCH 1664/2187] camera:fix dependency libriary name on OHOS we use libhwf_adapter.so instead of libcamera_wrapper.z.so --- devices/src/camera_backend/ohos/ohcam_bindings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/camera_backend/ohos/ohcam_bindings.rs b/devices/src/camera_backend/ohos/ohcam_bindings.rs index 1e36dd7fc..a985d67fd 100755 --- a/devices/src/camera_backend/ohos/ohcam_bindings.rs +++ b/devices/src/camera_backend/ohos/ohcam_bindings.rs @@ -25,7 +25,7 @@ pub struct ProfileRecorder { pub fps: i32, } -#[link(name = "camera_wrapper.z")] +#[link(name = "hwf_adapter")] extern "C" { pub fn OhcamCreateCtx() -> *mut c_void; pub fn OhcamCreateSession(ctx: *mut c_void) -> c_int; -- Gitee From 6546ae518b73efad3a77182f9f9048080f7a3b03 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 12 Mar 2024 20:19:57 +0800 Subject: [PATCH 1665/2187] scream: pass OHUI client HAP's tokenID to scream On OHOS, its audio framework will check if thread uses microphone on background, if so, microphone will not be allowed to use. Due to stratovirt's lack of HAP window on OHOS, stratovirt is always denied. We get HAP tokenID from OHUI client, pass it to scream record thread and bound thread with tokenID. So OHOS will check OHUI client's HAP tokenID, and record thread gets permission for microphone. --- devices/src/misc/scream/mod.rs | 25 ++++++++++++++++++++++-- machine/src/aarch64/standard.rs | 10 ++++++++++ machine/src/lib.rs | 18 +++++++++++++---- ui/src/ohui_srv/mod.rs | 5 ++++- ui/src/ohui_srv/msg.rs | 10 ++++++++++ ui/src/ohui_srv/msg_handle.rs | 9 +++++++-- util/src/lib.rs | 2 ++ util/src/ohos/misc.rs | 34 +++++++++++++++++++++++++++++++++ util/src/ohos/mod.rs | 13 +++++++++++++ 9 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 util/src/ohos/misc.rs create mode 100644 util/src/ohos/mod.rs diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 5428fd5d3..b38774983 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -23,7 +23,7 @@ use std::{ str::FromStr, sync::{ atomic::{fence, Ordering}, - Arc, Mutex, Weak, + Arc, Mutex, RwLock, Weak, }, thread, }; @@ -44,6 +44,8 @@ use machine_manager::config::{get_pci_df, valid_id}; use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +use util::ohos::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; @@ -333,6 +335,16 @@ impl StreamData { } } +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +fn bound_tokenid(token_id: u64) -> Result<()> { + if token_id == 0 { + bail!("UI token ID not passed."); + } else if token_id != get_firstcaller_tokenid() { + set_firstcaller_tokenid(token_id)?; + } + Ok(()) +} + #[derive(Clone, Debug)] enum ScreamInterface { #[cfg(feature = "scream_alsa")] @@ -385,14 +397,16 @@ pub struct Scream { hva: u64, size: u64, config: ScreamConfig, + token_id: Option>>, } impl Scream { - pub fn new(size: u64, config: ScreamConfig) -> Self { + pub fn new(size: u64, config: ScreamConfig, token_id: Option>>) -> Self { Self { hva: 0, size, config, + token_id, } } @@ -442,6 +456,8 @@ impl Scream { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); + #[allow(unused)] + let ti = self.token_id.clone(); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { @@ -456,6 +472,11 @@ impl Scream { hva, ); + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + if let Some(token_id) = &ti { + bound_tokenid(*token_id.read().unwrap()) + .unwrap_or_else(|e| error!("bound token ID failed: {}", e)); + } capt_data.capture_trans(hva, shmem_size, clone_interface.clone()); } }) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 7b67aa171..fe08ce901 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -14,6 +14,8 @@ pub use crate::error::MachineError; use std::mem::size_of; use std::ops::Deref; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use std::sync::RwLock; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -763,6 +765,14 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } + + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn get_token_id(&self) -> Option>> { + match &self.ohui_server { + Some(srv) => Some(srv.token_id.clone()), + None => None, + } + } } pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7c7d7324e..21fce856d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -29,7 +29,7 @@ use std::net::TcpListener; use std::ops::Deref; use std::os::unix::net::UnixListener; use std::path::Path; -use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; +use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; #[cfg(feature = "windows_emu_pid")] use std::time::Duration; @@ -1595,7 +1595,12 @@ pub trait MachineOps { /// /// * `cfg_args` - scream configuration. #[cfg(feature = "scream")] - fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + fn add_ivshmem_scream( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + token_id: Option>>, + ) -> Result<()> { let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args))?; let bdf = PciBdf { bus: config.bus.clone(), @@ -1618,7 +1623,7 @@ pub trait MachineOps { bail!("Object for share config is not on"); } - let scream = Scream::new(mem_cfg.size, config); + let scream = Scream::new(mem_cfg.size, config, token_id); scream .realize(devfn, parent_bus) .with_context(|| "Failed to realize scream device") @@ -1802,6 +1807,7 @@ pub trait MachineOps { let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; + let token_id = self.get_token_id(); create_device_add_matches!( dev.0.as_str(); self; @@ -1830,7 +1836,7 @@ pub trait MachineOps { #[cfg(feature = "demo_device")] ("pcie-demo-dev", add_demo_dev, vm_config, cfg_args), #[cfg(feature = "scream")] - ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args), + ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args, token_id), #[cfg(feature = "pvpanic")] ("pvpanic", add_pvpanic, cfg_args) ); @@ -1839,6 +1845,10 @@ pub trait MachineOps { Ok(()) } + fn get_token_id(&self) -> Option>> { + None + } + fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> { bail!("Pflash device is not supported!"); } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 3e288b65a..190794654 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -105,6 +105,8 @@ pub struct OhUiServer { framebuffer: u64, // framebuffer file backend fb_file: Option, + // tokenID of OHUI client + pub token_id: Arc>, } impl OhUiServer { @@ -172,6 +174,7 @@ impl OhUiServer { cursorbuffer, framebuffer, fb_file, + token_id: Arc::new(RwLock::new(0)), }) } @@ -195,7 +198,7 @@ impl OhUiServer { if !self.connected() { return Err(anyhow!("connection has not establish".to_string())); } - self.msg_handler.handle_msg() + self.msg_handler.handle_msg(self.token_id.clone()) } fn raw_update_dirty_area( diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs index e61270dda..69dc7f001 100755 --- a/ui/src/ohui_srv/msg.rs +++ b/ui/src/ohui_srv/msg.rs @@ -127,6 +127,15 @@ pub struct LedstateEvent { impl ByteCode for LedstateEvent {} +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct GreetEvent { + pad: [u32; 6], + pub token_id: u64, +} + +impl ByteCode for GreetEvent {} + #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct FocusEvent { @@ -183,6 +192,7 @@ pub fn event_msg_data_len(event_type: EventType) -> usize { EventType::FrameBufferDirty => size_of::(), EventType::CursorDefine => size_of::(), EventType::Ledstate => size_of::(), + EventType::Greet => size_of::(), _ => 0, } } diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index afdf8d2ee..5f40ff079 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use anyhow::{anyhow, bail, Result}; use log::{error, warn}; @@ -135,7 +135,7 @@ impl OhUiMsgHandler { } } - pub fn handle_msg(&self) -> Result<()> { + pub fn handle_msg(&self, token_id: Arc>) -> Result<()> { let mut reader = self.reader.lock().unwrap(); if !reader.recv(&self.channel)? { return Ok(()); @@ -191,6 +191,11 @@ impl OhUiMsgHandler { self.handle_ledstate(body); Ok(()) } + EventType::Greet => { + let body = GreetEvent::from_bytes(&body_bytes[..]).unwrap(); + *token_id.write().unwrap() = body.token_id; + Ok(()) + } _ => { error!( "unsupported type {:?} and body size {}", diff --git a/util/src/lib.rs b/util/src/lib.rs index 7ca0d3a70..33edf6456 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -28,6 +28,8 @@ pub mod logger; pub mod loop_context; pub mod num_ops; pub mod offsetof; +#[cfg(target_env = "ohos")] +pub mod ohos; #[cfg(feature = "pixman")] pub mod pixman; pub mod seccomp; diff --git a/util/src/ohos/misc.rs b/util/src/ohos/misc.rs new file mode 100644 index 000000000..4d9678142 --- /dev/null +++ b/util/src/ohos/misc.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use anyhow::{bail, Result}; + +#[link(name = "token_setproc")] +extern "C" { + fn SetFirstCallerTokenID(id: u64) -> i32; + fn GetFirstCallerTokenID() -> u64; +} + +pub fn set_firstcaller_tokenid(id: u64) -> Result<()> { + // SAFETY: This function is only applied on this thread. + unsafe { + if SetFirstCallerTokenID(id) != 0 { + bail!("Set first caller failed"); + } + } + Ok(()) +} + +pub fn get_firstcaller_tokenid() -> u64 { + // SAFETY: This function is only applied on this thread. + unsafe { GetFirstCallerTokenID() } +} diff --git a/util/src/ohos/mod.rs b/util/src/ohos/mod.rs new file mode 100644 index 000000000..ea0e76b39 --- /dev/null +++ b/util/src/ohos/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod misc; -- Gitee From 0ea823eeb8bcc55a10bb720629c9189c408cae2b Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Fri, 15 Mar 2024 09:44:04 +0800 Subject: [PATCH 1666/2187] Optimize "get_token_id"'s code fix code as RUST's recommendation --- machine/src/aarch64/standard.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index fe08ce901..8544a2b79 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -768,10 +768,7 @@ impl MachineOps for StdMachine { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] fn get_token_id(&self) -> Option>> { - match &self.ohui_server { - Some(srv) => Some(srv.token_id.clone()), - None => None, - } + self.ohui_server.as_ref().map(|srv| srv.token_id.clone()) } } -- Gitee From 2462992a2f8e3a21bd9fcbb5a8118f547b0131f8 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Fri, 15 Mar 2024 10:47:27 +0800 Subject: [PATCH 1667/2187] ohos: change the file name ohos to ohos_binding Signed-off-by: Mingwang Li --- util/src/lib.rs | 2 +- util/src/{ohos => ohos_binding}/misc.rs | 0 util/src/{ohos => ohos_binding}/mod.rs | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename util/src/{ohos => ohos_binding}/misc.rs (100%) rename util/src/{ohos => ohos_binding}/mod.rs (100%) diff --git a/util/src/lib.rs b/util/src/lib.rs index 33edf6456..f861d6f6a 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -29,7 +29,7 @@ pub mod loop_context; pub mod num_ops; pub mod offsetof; #[cfg(target_env = "ohos")] -pub mod ohos; +pub mod ohos_binding; #[cfg(feature = "pixman")] pub mod pixman; pub mod seccomp; diff --git a/util/src/ohos/misc.rs b/util/src/ohos_binding/misc.rs similarity index 100% rename from util/src/ohos/misc.rs rename to util/src/ohos_binding/misc.rs diff --git a/util/src/ohos/mod.rs b/util/src/ohos_binding/mod.rs similarity index 100% rename from util/src/ohos/mod.rs rename to util/src/ohos_binding/mod.rs -- Gitee From e6d3b79a888615ab9b0ab38716a9f2e76c4c8c4a Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Fri, 15 Mar 2024 14:11:05 +0800 Subject: [PATCH 1668/2187] Bugfix: fix introduced util module name fix introduced util module name --- devices/src/misc/scream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index b38774983..d782e3d5c 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -45,7 +45,7 @@ use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -use util::ohos::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; +use util::ohos_binding::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; -- Gitee From a5909f9c9fb260c0daed471662318d473d4facb2 Mon Sep 17 00:00:00 2001 From: YeXiao Date: Fri, 15 Mar 2024 14:54:32 +0800 Subject: [PATCH 1669/2187] Image: returen error code if exception occurs If exception occurs, an error code should be return by stratovirt-img. Signed-off-by: Xiao Ye --- image/src/main.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/image/src/main.rs b/image/src/main.rs index 0211c9ccf..a24a2a4ec 100644 --- a/image/src/main.rs +++ b/image/src/main.rs @@ -13,21 +13,36 @@ mod cmdline; mod img; -use std::env; +use std::{ + env, + process::{ExitCode, Termination}, +}; + +use anyhow::{bail, Result}; use crate::img::{image_check, image_create, image_snapshot, print_help}; const BINARY_NAME: &str = "stratovirt-img"; -fn main() { +fn main() -> ExitCode { let args: Vec = env::args().collect(); + + match run(args) { + Ok(ret) => ret.report(), + Err(e) => { + println!("{:?}", e); + ExitCode::FAILURE + } + } +} + +fn run(args: Vec) -> Result<()> { if args.len() < 2 { - println!( + bail!( "{0}: Not enough arguments\n\ Try '{0} --help' for more information", BINARY_NAME ); - return; } let opt = args[1].clone(); @@ -35,17 +50,17 @@ fn main() { match opt.as_str() { "create" => { if let Err(e) = image_create(args[2..].to_vec()) { - println!("{}: {:?}", BINARY_NAME, e); + bail!("{}: {:?}", BINARY_NAME, e); } } "check" => { if let Err(e) = image_check(args[2..].to_vec()) { - println!("{}: {:?}", BINARY_NAME, e); + bail!("{}: {:?}", BINARY_NAME, e); } } "snapshot" => { if let Err(e) = image_snapshot(args[2..].to_vec()) { - println!("{}: {:?}", BINARY_NAME, e); + bail!("{}: {:?}", BINARY_NAME, e); } } "-v" | "--version" => { @@ -60,7 +75,7 @@ fn main() { print_help(); } _ => { - println!( + bail!( "{}: Command not found: {}\n\ Try 'stratovirt-img --help' for more information.", BINARY_NAME, @@ -68,4 +83,6 @@ fn main() { ); } } + + Ok(()) } -- Gitee From 8ff94a40a0d4d4d59e6ae4e1a137e403b357eb92 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 11 Mar 2024 16:13:24 +0800 Subject: [PATCH 1670/2187] Trace: Add trace for PCI module Add trace for PCI module Signed-off-by: Jinhao Gao --- devices/src/pci/config.rs | 3 +++ devices/src/pci/host.rs | 4 ++++ devices/src/pci/msix.rs | 2 ++ trace/trace_event/pci.toml | 29 +++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 trace/trace_event/pci.toml diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 561386561..1e6645610 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -903,6 +903,8 @@ impl PciConfig { } } } + + trace::pci_update_mappings_del(id, self.bars[id].address, self.bars[id].size); self.bars[id].address = BAR_SPACE_UNMAPPED; } @@ -936,6 +938,7 @@ impl PciConfig { } } + trace::pci_update_mappings_add(id, self.bars[id].address, self.bars[id].size); self.bars[id].address = new_addr; } } diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 510ecfb94..b6a44dabd 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -255,6 +255,8 @@ impl SysBusDevOps for PciHost { match self.find_device(bus_num, devfn) { Some(dev) => { let addr: usize = (offset & ECAM_OFFSET_MASK) as usize; + let dev_name = &dev.lock().unwrap().pci_base().base.id.clone(); + trace::pci_read_config(dev_name, addr, data); dev.lock().unwrap().read_config(addr, data); } None => { @@ -272,6 +274,8 @@ impl SysBusDevOps for PciHost { match self.find_device(bus_num, devfn) { Some(dev) => { let addr: usize = (offset & ECAM_OFFSET_MASK) as usize; + let dev_name = &dev.lock().unwrap().pci_base().base.id.clone(); + trace::pci_write_config(dev_name, addr, data); dev.lock().unwrap().write_config(addr, data); true } diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 0469f7f19..57233793a 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -450,6 +450,8 @@ impl Msix { let masked: bool = self.is_func_masked(config); let enabled: bool = self.is_enabled(config); + trace::msix_write_config(self.dev_id.load(Ordering::Relaxed) as u16, masked, enabled); + let mask_state_changed = !((self.func_masked == masked) && (self.enabled == enabled)); self.func_masked = masked; diff --git a/trace/trace_event/pci.toml b/trace/trace_event/pci.toml new file mode 100644 index 000000000..dfd7642fa --- /dev/null +++ b/trace/trace_event/pci.toml @@ -0,0 +1,29 @@ +[[events]] +name = "pci_read_config" +args = "dev_name: &str, addr: usize, data: &[u8]" +message = "dev name: {} addr: 0x{:#X} data: 0x{:X?}" +enabled = true + +[[events]] +name = "pci_write_config" +args = "dev_name: &str, addr: usize, data: &[u8]" +message = "dev name: {} addr: 0x{:#X} data: 0x{:X?}" +enabled = true + +[[events]] +name = "msix_write_config" +args = "dev_id: u16, masked: bool, enabled: bool" +message = "dev id: {} masked: {} enabled: {}" +enabled = true + +[[events]] +name = "pci_update_mappings_add" +args = "bar_id: usize, addr: u64, size: u64" +message = "bar id: {} addr: 0x{:#X} size: {}" +enabled = true + +[[events]] +name = "pci_update_mappings_del" +args = "bar_id: usize, addr: u64, size: u64" +message = "bar id: {} addr: 0x{:#X} size: {}" +enabled = true -- Gitee From 5e04a0da40b027d6d6cca449c110f5626b757cfb Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 11 Mar 2024 16:25:53 +0800 Subject: [PATCH 1671/2187] Trace: Add trace for scream Add trace for scream Signed-off-by: Jinhao Gao --- devices/src/misc/scream/alsa.rs | 4 ++++ devices/src/misc/scream/mod.rs | 5 +++-- trace/trace_event/misc.toml | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 trace/trace_event/misc.toml diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index 8e3337d1a..e3ca324cb 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -86,12 +86,14 @@ impl AlsaStreamData { // Set the latency in microseconds. hwp.set_buffer_time_near(self.latency * 1000, ValueOr::Nearest)?; pcm.hw_params(&hwp)?; + trace::scream_setup_alsa_hwp(&self.app_name, &hwp); // Set software parameters of the stream. let hwp = pcm.hw_params_current()?; let swp = pcm.sw_params_current()?; swp.set_start_threshold(hwp.get_buffer_size().unwrap())?; pcm.sw_params(&swp)?; + trace::scream_setup_alsa_swp(&self.app_name, &swp); } self.pcm = Some(pcm); Ok(()) @@ -200,6 +202,7 @@ impl AudioInterface for AlsaStreamData { }; } Ok(n) => { + trace::scream_alsa_send_frames(frames, offset, end); frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); } } @@ -246,6 +249,7 @@ impl AudioInterface for AlsaStreamData { }; } Ok(n) => { + trace::scream_alsa_receive_frames(frames, offset, end); frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); // During the host headset switchover, io.read is blocked for a long time. diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 5428fd5d3..397e8371e 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -69,7 +69,7 @@ pub enum ScreamDirection { /// Audio stream header information in the shared memory. #[repr(C)] -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] pub struct ShmemStreamHeader { /// Whether audio is started. pub is_started: u32, @@ -126,7 +126,7 @@ pub struct ShmemHeader { /// Audio stream format in the shared memory. #[repr(C)] -#[derive(PartialEq, Eq, Clone, Copy)] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ShmemStreamFmt { /// Indicates whether the audio format is changed. pub fmt_generation: u32, @@ -200,6 +200,7 @@ impl StreamData { ScreamDirection::Playback => &header.play, ScreamDirection::Record => &header.capt, }; + trace::scream_init(&dir, &stream_header); loop { if header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { diff --git a/trace/trace_event/misc.toml b/trace/trace_event/misc.toml new file mode 100644 index 000000000..78ac9d191 --- /dev/null +++ b/trace/trace_event/misc.toml @@ -0,0 +1,29 @@ +[[events]] +name = "scream_init" +args = "dir: &dyn fmt::Debug, header: &dyn fmt::Debug" +message = "dir: {:?} header: {:?}" +enabled = true + +[[events]] +name = "scream_alsa_send_frames" +args = "frame: u32, offset: usize, end: usize" +message = "frames {} offset {} end {}" +enabled = true + +[[events]] +name = "scream_alsa_receive_frames" +args = "frame: u32, offset: usize, end: usize" +message = "frames {} offset {} end {}" +enabled = true + +[[events]] +name = "scream_setup_alsa_swp" +args = "name: &str, swp: &dyn fmt::Debug" +message = "scream {} setup software parameters: {:?}" +enabled = true + +[[events]] +name = "scream_setup_alsa_hwp" +args = "name: &str, hwp: &dyn fmt::Debug" +message = "scream {} setup hardware parameters: {:?}" +enabled = true -- Gitee From b5879e4d55768ab9e6dde2e0558f1b29424c3145 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 11 Mar 2024 22:00:28 +0800 Subject: [PATCH 1672/2187] Trace: Add trace for usbhost Add trace for usbhost Signed-off-by: Jinhao Gao --- devices/src/usb/usbhost/host_usblib.rs | 22 +++- devices/src/usb/usbhost/mod.rs | 169 +++++++++++++++++++++++-- trace/trace_event/usb.toml | 126 ++++++++++++++++++ 3 files changed, 304 insertions(+), 13 deletions(-) diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 3ee20a0b9..2ee7a9dea 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -32,7 +32,7 @@ use rusb::{Context, DeviceHandle, Error, Result, TransferType, UsbContext}; use vmm_sys_util::epoll::EventSet; use super::{IsoTransfer, UsbHost, UsbHostRequest}; -use crate::usb::{UsbPacketStatus, USB_TOKEN_IN}; +use crate::usb::{UsbPacket, UsbPacketStatus, USB_TOKEN_IN}; use util::{ link_list::Node, loop_context::{EventNotifier, NotifierCallback, NotifierOperation}, @@ -217,17 +217,25 @@ extern "system" fn req_complete(host_transfer: *mut libusb_transfer) { return; } - let actual_length = get_length_from_transfer(host_transfer); + let actual_length = get_length_from_transfer(host_transfer) as usize; let transfer_status = get_status_from_transfer(host_transfer); locked_packet.status = map_packet_status(transfer_status); if request.is_control { - request.ctrl_transfer_packet(&mut locked_packet, actual_length as usize); + request.ctrl_transfer_packet(&mut locked_packet, actual_length); } else if locked_packet.pid as u8 == USB_TOKEN_IN && actual_length != 0 { - let data = get_buffer_from_transfer(host_transfer, actual_length as usize); - locked_packet.transfer_packet(data, actual_length as usize); + let data = get_buffer_from_transfer(host_transfer, actual_length); + locked_packet.transfer_packet(data, actual_length); } + trace::usb_host_req_complete( + request.hostbus, + request.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + actual_length, + ); + if let Some(transfer) = locked_packet.xfer_ops.as_ref() { if let Some(ops) = transfer.clone().upgrade() { drop(locked_packet); @@ -253,6 +261,10 @@ extern "system" fn req_complete_iso(host_transfer: *mut libusb_transfer) { drop(locketd_iso_transfer); let mut locked_iso_queue = iso_queue.lock().unwrap(); let iso_transfer = locked_iso_queue.inflight.pop_front().unwrap(); + if locked_iso_queue.inflight.is_empty() { + let queue = &locked_iso_queue; + trace::usb_host_iso_stop(queue.hostbus, queue.hostaddr, queue.ep_number); + } locked_iso_queue.copy.push_back(iso_transfer); } } diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index f91de9427..e34d1176c 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -69,6 +69,8 @@ struct InterfaceStatus { } pub struct UsbHostRequest { + pub hostbus: u8, + pub hostaddr: u8, pub requests: Weak>>, pub packet: Arc>, pub host_transfer: *mut libusb_transfer, @@ -79,12 +81,16 @@ pub struct UsbHostRequest { impl UsbHostRequest { pub fn new( + hostbus: u8, + hostaddr: u8, requests: Weak>>, packet: Arc>, host_transfer: *mut libusb_transfer, is_control: bool, ) -> Self { Self { + hostbus, + hostaddr, requests, packet, host_transfer, @@ -121,6 +127,13 @@ impl UsbHostRequest { if locked_packet.is_async { locked_packet.status = UsbPacketStatus::NoDev; locked_packet.is_async = false; + trace::usb_host_req_complete( + self.hostbus, + self.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + locked_packet.actual_length as usize, + ); cancel_host_transfer(self.host_transfer) .unwrap_or_else(|e| warn!("usb-host cancel host transfer is error: {:?}", e)); @@ -261,6 +274,8 @@ unsafe impl Sync for IsoTransfer {} unsafe impl Send for IsoTransfer {} pub struct IsoQueue { + hostbus: u8, + hostaddr: u8, ep_number: u8, unused: LinkedList>>, inflight: LinkedList>>, @@ -268,8 +283,10 @@ pub struct IsoQueue { } impl IsoQueue { - pub fn new(ep_number: u8) -> Self { + pub fn new(hostbus: u8, hostaddr: u8, ep_number: u8) -> Self { Self { + hostbus, + hostaddr, ep_number, unused: LinkedList::new(), inflight: LinkedList::new(), @@ -513,6 +530,7 @@ impl UsbHost { } { continue; } + trace::usb_host_detach_kernel(self.config.hostbus, self.config.hostaddr, i); self.handle .as_mut() .unwrap() @@ -538,6 +556,7 @@ impl UsbHost { if !self.ifs[i as usize].detached { continue; } + trace::usb_host_attach_kernel(self.config.hostbus, self.config.hostaddr, i); self.handle .as_mut() .unwrap() @@ -554,6 +573,7 @@ impl UsbHost { Err(_) => return, }; + trace::usb_host_parse_config(self.config.hostbus, self.config.hostaddr, conf.number()); for (i, intf) in conf.interfaces().enumerate() { // The usb_deviec.altsetting indexes alternate settings by the interface number. // Get the 0th alternate setting first so that we can grap the interface number, @@ -575,8 +595,13 @@ impl UsbHost { intf_desc = intf.descriptors().nth(alt as usize); } + trace::usb_host_parse_interface( + self.config.hostbus, + self.config.hostaddr, + intf_desc.as_ref().unwrap().interface_number(), + intf_desc.as_ref().unwrap().setting_number(), + ); for ep in intf_desc.as_ref().unwrap().endpoint_descriptors() { - let addr = ep.address(); let pid = match ep.direction() { Direction::In => USB_TOKEN_IN, Direction::Out => USB_TOKEN_OUT, @@ -584,14 +609,30 @@ impl UsbHost { let ep_num = ep.number(); let ep_type = ep.transfer_type() as u8; if ep_num == 0 { - error!("Invalid endpoint address {}", addr); + trace::usb_host_parse_error( + self.config.hostbus, + self.config.hostaddr, + "invalid endpoint address", + ); return; } let in_direction = pid == USB_TOKEN_IN; if self.base.get_endpoint(in_direction, ep_num).ep_type != USB_ENDPOINT_ATTR_INVALID { - error!("duplicate endpoint address") + trace::usb_host_parse_error( + self.config.hostbus, + self.config.hostaddr, + "duplicate endpoint address", + ); } + + trace::usb_host_parse_endpoint( + self.config.hostbus, + self.config.hostaddr, + ep_num, + &ep.direction(), + &ep.transfer_type(), + ); let usb_ep = self.base.get_mut_endpoint(in_direction, ep_num); usb_ep.set_max_packet_size(ep.max_packet_size()); usb_ep.ep_type = ep_type; @@ -603,6 +644,9 @@ impl UsbHost { fn open_and_init(&mut self) -> Result<()> { self.handle = Some(self.libdev.as_ref().unwrap().open()?); + self.config.hostbus = self.libdev.as_ref().unwrap().bus_number(); + self.config.hostaddr = self.libdev.as_ref().unwrap().address(); + trace::usb_host_open_started(self.config.hostbus, self.config.hostaddr); self.detach_kernel()?; @@ -611,6 +655,8 @@ impl UsbHost { self.ep_update(); self.base.speed = self.libdev.as_ref().unwrap().speed() as u32 - 1; + trace::usb_host_open_success(self.config.hostbus, self.config.hostaddr); + Ok(()) } @@ -632,6 +678,7 @@ impl UsbHost { if !self.ifs[i as usize].claimed { continue; } + trace::usb_host_release_interface(self.config.hostbus, self.config.hostaddr, i); self.handle .as_mut() .unwrap() @@ -660,6 +707,7 @@ impl UsbHost { let mut claimed = 0; for i in 0..self.ifs_num { + trace::usb_host_claim_interface(self.config.hostbus, self.config.hostaddr, i); if self.handle.as_mut().unwrap().claim_interface(i).is_ok() { self.ifs[i as usize].claimed = true; claimed += 1; @@ -677,6 +725,7 @@ impl UsbHost { } fn set_config(&mut self, config: u8, packet: &mut UsbPacket) { + trace::usb_host_set_config(self.config.hostbus, self.config.hostaddr, config); self.release_interfaces(); if self.ddesc.is_some() && self.ddesc.as_ref().unwrap().num_configurations() != 1 { @@ -703,6 +752,7 @@ impl UsbHost { } fn set_interface(&mut self, iface: u16, alt: u16, packet: &mut UsbPacket) { + trace::usb_host_set_interface(self.config.hostbus, self.config.hostaddr, iface, alt); self.clear_iso_queues(); if iface > USB_MAX_INTERFACES as u16 { @@ -743,6 +793,8 @@ impl UsbHost { return; } + trace::usb_host_close(self.config.hostbus, self.config.hostaddr); + self.abort_host_transfers() .unwrap_or_else(|e| error!("Failed to abort all libusb transfers: {:?}", e)); self.release_interfaces(); @@ -806,7 +858,11 @@ impl UsbHost { let iso_queue = if self.find_iso_queue(locked_packet.ep_number).is_some() { self.find_iso_queue(locked_packet.ep_number).unwrap() } else { - let iso_queue = Arc::new(Mutex::new(IsoQueue::new(locked_packet.ep_number))); + let iso_queue = Arc::new(Mutex::new(IsoQueue::new( + self.config.hostbus, + self.config.hostaddr, + locked_packet.ep_number, + ))); let cloned_iso_queue = iso_queue.clone(); let ep = self .base @@ -866,6 +922,13 @@ impl UsbHost { let mut locked_iso_queue = iso_queue.lock().unwrap(); match submit_host_transfer(host_transfer) { Ok(()) => { + if locked_iso_queue.inflight.is_empty() { + trace::usb_host_iso_start( + self.config.hostbus, + self.config.hostaddr, + ep.ep_number, + ); + } locked_iso_queue .inflight .push_back(iso_transfer.unwrap().clone()); @@ -894,20 +957,28 @@ impl UsbHost { host_transfer: *mut libusb_transfer, packet: &Arc>, ) { + let mut locked_packet = packet.lock().unwrap(); match submit_host_transfer(host_transfer) { Ok(()) => {} Err(Error::NoDevice) => { - packet.lock().unwrap().status = UsbPacketStatus::NoDev; + locked_packet.status = UsbPacketStatus::NoDev; + trace::usb_host_req_complete( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + locked_packet.actual_length as usize, + ); return; } _ => { - packet.lock().unwrap().status = UsbPacketStatus::Stall; + locked_packet.status = UsbPacketStatus::Stall; self.reset(); return; } }; - packet.lock().unwrap().is_async = true; + locked_packet.is_async = true; } } @@ -955,6 +1026,7 @@ impl UsbDevice for UsbHost { bail!("Invalid USB host config: {:?}", self.config); } + info!("Open and init usbhost device: {:?}", self.config); self.open_and_init()?; let usbhost = Arc::new(Mutex::new(self)); @@ -981,6 +1053,8 @@ impl UsbDevice for UsbHost { self.clear_iso_queues(); + trace::usb_host_reset(self.config.hostbus, self.config.hostaddr); + self.handle .as_mut() .unwrap() @@ -999,30 +1073,66 @@ impl UsbDevice for UsbHost { } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { + trace::usb_host_req_control(self.config.hostbus, self.config.hostaddr, device_req); let mut locked_packet = packet.lock().unwrap(); if self.handle.is_none() { locked_packet.status = UsbPacketStatus::NoDev; + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } match device_req.request_type { USB_DEVICE_OUT_REQUEST => { if device_req.request == USB_REQUEST_SET_ADDRESS { self.base.addr = device_req.value as u8; + trace::usb_host_set_address( + self.config.hostbus, + self.config.hostaddr, + self.base.addr, + ); + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } else if device_req.request == USB_REQUEST_SET_CONFIGURATION { self.set_config(device_req.value as u8, &mut locked_packet); + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } } USB_INTERFACE_OUT_REQUEST => { if device_req.request == USB_REQUEST_SET_INTERFACE { self.set_interface(device_req.index, device_req.value, &mut locked_packet); + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } } USB_ENDPOINT_OUT_REQUEST => { if device_req.request == USB_REQUEST_CLEAR_FEATURE && device_req.value == 0 { self.clear_halt(locked_packet.pid as u8, device_req.index as u8); + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } } @@ -1032,6 +1142,8 @@ impl UsbDevice for UsbHost { let host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); let mut node = Box::new(Node::new(UsbHostRequest::new( + self.config.hostbus, + self.config.hostaddr, Arc::downgrade(&self.requests), packet.clone(), host_transfer, @@ -1059,8 +1171,23 @@ impl UsbDevice for UsbHost { let cloned_packet = packet.clone(); let mut locked_packet = packet.lock().unwrap(); + trace::usb_host_req_data( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + locked_packet.pid, + locked_packet.ep_number, + locked_packet.iovecs.len(), + ); + if self.handle.is_none() { locked_packet.status = UsbPacketStatus::NoDev; + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } let in_direction = locked_packet.pid as u8 == USB_TOKEN_IN; @@ -1070,6 +1197,12 @@ impl UsbDevice for UsbHost { .halted { locked_packet.status = UsbPacketStatus::Stall; + trace::usb_host_req_emulated( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + ); return; } @@ -1081,6 +1214,8 @@ impl UsbDevice for UsbHost { USB_ENDPOINT_ATTR_BULK => { host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); let mut node = Box::new(Node::new(UsbHostRequest::new( + self.config.hostbus, + self.config.hostaddr, Arc::downgrade(&self.requests), cloned_packet, host_transfer, @@ -1103,6 +1238,8 @@ impl UsbDevice for UsbHost { USB_ENDPOINT_ATTR_INT => { host_transfer = alloc_host_transfer(NON_ISO_PACKETS_NUMS); let mut node = Box::new(Node::new(UsbHostRequest::new( + self.config.hostbus, + self.config.hostaddr, Arc::downgrade(&self.requests), cloned_packet, host_transfer, @@ -1128,10 +1265,26 @@ impl UsbDevice for UsbHost { } else { self.handle_iso_data_out(packet.clone()); } + let locked_packet = packet.lock().unwrap(); + trace::usb_host_req_complete( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + locked_packet.actual_length as usize, + ); return; } _ => { packet.lock().unwrap().status = UsbPacketStatus::Stall; + let locked_packet = packet.lock().unwrap(); + trace::usb_host_req_complete( + self.config.hostbus, + self.config.hostaddr, + &*locked_packet as *const UsbPacket as u64, + &locked_packet.status, + locked_packet.actual_length as usize, + ); return; } }; diff --git a/trace/trace_event/usb.toml b/trace/trace_event/usb.toml index 49a23cd81..fc5699a34 100644 --- a/trace/trace_event/usb.toml +++ b/trace/trace_event/usb.toml @@ -339,3 +339,129 @@ name = "usb_keyboard_set_report" args = "led_state: &dyn fmt::Debug" message = "led_state={:?}" enabled = true + +[[events]] +name = "usb_host_open_started" +args = "bus_num: u8, addr: u8" +message = "dev bus 0x{:X} addr 0x{:X}" +enabled = true + +[[events]] +name = "usb_host_close" +args = "bus_num: u8, addr: u8" +message = "dev bus 0x{:X} addr 0x{:X}" +enabled = true + +[[events]] +name = "usb_host_open_success" +args = "bus_num: u8, addr: u8" +message = "dev bus 0x{:X} addr 0x{:X}" +enabled = true + +[[events]] +name = "usb_host_reset" +args = "bus_num: u8, addr: u8" +message = "dev bus 0x{:X} addr 0x{:X} reset" +enabled = true + +[[events]] +name = "usb_host_attach_kernel" +args = "bus_num: u8, addr: u8, interface: u8" +message = "dev bus 0x{:X} addr 0x{:X}, interface {}" +enabled = true + +[[events]] +name = "usb_host_detach_kernel" +args = "bus_num: u8, addr: u8, interface: u8" +message = "dev bus 0x{:X} addr 0x{:X}, interface {}" +enabled = true + +[[events]] +name = "usb_host_set_interface" +args = "bus_num: u8, addr: u8, iface: u16, alt: u16" +message = "dev bus 0x{:X} addr 0x{:X}, set interface {}, alt {}" +enabled = true + +[[events]] +name = "usb_host_set_config" +args = "bus_num: u8, addr: u8, config: u8" +message = "dev bus 0x{:X} addr 0x{:X}, set config {}" +enabled = true + +[[events]] +name = "usb_host_set_address" +args = "bus_num: u8, addr: u8, address: u8" +message = "dev bus 0x{:X} addr 0x{:X}, set address {}" +enabled = true + +[[events]] +name = "usb_host_claim_interface" +args = "bus_num: u8, addr: u8, interface: u8" +message = "dev bus 0x{:X} addr 0x{:X}, claim interface {}" +enabled = true + +[[events]] +name = "usb_host_release_interface" +args = "bus_num: u8, addr: u8, interface: u8" +message = "dev bus 0x{:X} addr 0x{:X}, release interface {}" +enabled = true + +[[events]] +name = "usb_host_parse_config" +args = "bus_num: u8, addr: u8, value: u8" +message = "dev bus 0x{:X} addr 0x{:X}, parse config value {}" +enabled = true + +[[events]] +name = "usb_host_parse_interface" +args = "bus_num: u8, addr: u8, num: u8, alt: u8" +message = "dev bus 0x{:X} addr 0x{:X}, parse interface num {} alt {}" +enabled = true + +[[events]] +name = "usb_host_parse_error" +args = "bus_num: u8, addr: u8, msg: &str" +message = "dev bus 0x{:X} addr 0x{:X}, msg {}" +enabled = true + +[[events]] +name = "usb_host_parse_endpoint" +args = "bus_num: u8, addr: u8, ep: u8, dir: &dyn fmt::Debug, ep_type: &dyn fmt::Debug" +message = "dev bus 0x{:X} addr 0x{:X}, parse endpoint {} dir {:?} ep_type {:?}" +enabled = true + +[[events]] +name = "usb_host_req_control" +args = "bus_num: u8, addr: u8, request: &dyn fmt::Debug" +message = "dev bus 0x{:X} addr 0x{:X}, request {:?}" +enabled = true + +[[events]] +name = "usb_host_req_data" +args = "bus_num: u8, addr: u8, packet: u64, pid: u32, ep_num: u8, iov_len: usize" +message = "dev bus 0x{:X} addr 0x{:X}, packet 0x{:#X}, pid {} ep_number {} iov len {}" +enabled = true + +[[events]] +name = "usb_host_iso_start" +args = "bus_num: u8, addr: u8, ep_number: u8" +message = "dev bus 0x{:X} addr 0x{:X}, endpoint {}" +enabled = true + +[[events]] +name = "usb_host_iso_stop" +args = "bus_num: u8, addr: u8, ep_number: u8" +message = "dev bus 0x{:X} addr 0x{:X}, endpoint {}" +enabled = true + +[[events]] +name = "usb_host_req_emulated" +args = "bus_num: u8, addr: u8, packet: u64, status: &dyn fmt::Debug" +message = "dev bus 0x{:X} addr 0x{:X}, packet 0x{:#X}, status {:?}" +enabled = true + +[[events]] +name = "usb_host_req_complete" +args = "bus_num: u8, addr: u8, packet: u64, status: &dyn fmt::Debug, actual_length: usize" +message = "dev bus 0x{:X} addr 0x{:X}, packet 0x{:#X}, status {:?} actual length {}" +enabled = true -- Gitee From 78826d5a9bc85f123eee71450c5ce58c3755f33c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 17 Mar 2024 23:21:07 +0800 Subject: [PATCH 1673/2187] machine: fix clippy warnings Fix clippy warnings: error: unused variable: `token_id` --> machine/src/lib.rs:1810:17 | 1810 | let token_id = self.get_token_id(); | ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_token_id` | = note: `-D unused-variables` implied by `-D warnings` Signed-off-by: liuxiangdong --- machine/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 21fce856d..6c13c03c1 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1807,6 +1807,7 @@ pub trait MachineOps { let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; + #[cfg(feature = "scream")] let token_id = self.get_token_id(); create_device_add_matches!( -- Gitee From f028399a9667c8ba7eab5c1daace7abc4943320c Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:18:20 +0800 Subject: [PATCH 1674/2187] Revert "Bugfix: fix introduced util module name" This reverts commit e6d3b79a888615ab9b0ab38716a9f2e76c4c8c4a. Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 24dc5dcbe..606f76de9 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -45,7 +45,7 @@ use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -use util::ohos_binding::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; +use util::ohos::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; -- Gitee From 4e9d2b1d619d596f4527a48cf96c2bb69ae895a5 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:18:57 +0800 Subject: [PATCH 1675/2187] Revert "ohos: change the file name ohos to ohos_binding" This reverts commit 2462992a2f8e3a21bd9fcbb5a8118f547b0131f8. Signed-off-by: zhanghan64 --- util/src/lib.rs | 2 +- util/src/{ohos_binding => ohos}/misc.rs | 0 util/src/{ohos_binding => ohos}/mod.rs | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename util/src/{ohos_binding => ohos}/misc.rs (100%) rename util/src/{ohos_binding => ohos}/mod.rs (100%) diff --git a/util/src/lib.rs b/util/src/lib.rs index f861d6f6a..33edf6456 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -29,7 +29,7 @@ pub mod loop_context; pub mod num_ops; pub mod offsetof; #[cfg(target_env = "ohos")] -pub mod ohos_binding; +pub mod ohos; #[cfg(feature = "pixman")] pub mod pixman; pub mod seccomp; diff --git a/util/src/ohos_binding/misc.rs b/util/src/ohos/misc.rs similarity index 100% rename from util/src/ohos_binding/misc.rs rename to util/src/ohos/misc.rs diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos/mod.rs similarity index 100% rename from util/src/ohos_binding/mod.rs rename to util/src/ohos/mod.rs -- Gitee From 50cc669f50f75fa3e4d7a29fd77d532ade0d8b06 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:19:22 +0800 Subject: [PATCH 1676/2187] Revert "Optimize "get_token_id"'s code" This reverts commit 0ea823eeb8bcc55a10bb720629c9189c408cae2b. Signed-off-by: zhanghan64 --- machine/src/aarch64/standard.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 8544a2b79..fe08ce901 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -768,7 +768,10 @@ impl MachineOps for StdMachine { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] fn get_token_id(&self) -> Option>> { - self.ohui_server.as_ref().map(|srv| srv.token_id.clone()) + match &self.ohui_server { + Some(srv) => Some(srv.token_id.clone()), + None => None, + } } } -- Gitee From c0337f10d6bd11f83e6f2f70264730a80c4a35c2 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:21:33 +0800 Subject: [PATCH 1677/2187] Revert "machine: fix clippy warnings" This reverts commit 78826d5a9bc85f123eee71450c5ce58c3755f33c. Signed-off-by: zhanghan64 --- machine/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6c13c03c1..21fce856d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1807,7 +1807,6 @@ pub trait MachineOps { let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; - #[cfg(feature = "scream")] let token_id = self.get_token_id(); create_device_add_matches!( -- Gitee From fb14f172e738c5a255ec108ac8acac1a36ab8683 Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Tue, 19 Mar 2024 20:21:38 +0800 Subject: [PATCH 1678/2187] Revert "scream: pass OHUI client HAP's tokenID to scream" This reverts commit 6546ae518b73efad3a77182f9f9048080f7a3b03. Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 25 ++---------------------- machine/src/aarch64/standard.rs | 10 ---------- machine/src/lib.rs | 18 ++++------------- ui/src/ohui_srv/mod.rs | 5 +---- ui/src/ohui_srv/msg.rs | 10 ---------- ui/src/ohui_srv/msg_handle.rs | 9 ++------- util/src/lib.rs | 2 -- util/src/ohos/misc.rs | 34 --------------------------------- util/src/ohos/mod.rs | 13 ------------- 9 files changed, 9 insertions(+), 117 deletions(-) delete mode 100644 util/src/ohos/misc.rs delete mode 100644 util/src/ohos/mod.rs diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 606f76de9..397e8371e 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -23,7 +23,7 @@ use std::{ str::FromStr, sync::{ atomic::{fence, Ordering}, - Arc, Mutex, RwLock, Weak, + Arc, Mutex, Weak, }, thread, }; @@ -44,8 +44,6 @@ use machine_manager::config::{get_pci_df, valid_id}; use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; -#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -use util::ohos::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; @@ -336,16 +334,6 @@ impl StreamData { } } -#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -fn bound_tokenid(token_id: u64) -> Result<()> { - if token_id == 0 { - bail!("UI token ID not passed."); - } else if token_id != get_firstcaller_tokenid() { - set_firstcaller_tokenid(token_id)?; - } - Ok(()) -} - #[derive(Clone, Debug)] enum ScreamInterface { #[cfg(feature = "scream_alsa")] @@ -398,16 +386,14 @@ pub struct Scream { hva: u64, size: u64, config: ScreamConfig, - token_id: Option>>, } impl Scream { - pub fn new(size: u64, config: ScreamConfig, token_id: Option>>) -> Self { + pub fn new(size: u64, config: ScreamConfig) -> Self { Self { hva: 0, size, config, - token_id, } } @@ -457,8 +443,6 @@ impl Scream { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); - #[allow(unused)] - let ti = self.token_id.clone(); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { @@ -473,11 +457,6 @@ impl Scream { hva, ); - #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] - if let Some(token_id) = &ti { - bound_tokenid(*token_id.read().unwrap()) - .unwrap_or_else(|e| error!("bound token ID failed: {}", e)); - } capt_data.capture_trans(hva, shmem_size, clone_interface.clone()); } }) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index fe08ce901..7b67aa171 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -14,8 +14,6 @@ pub use crate::error::MachineError; use std::mem::size_of; use std::ops::Deref; -#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] -use std::sync::RwLock; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -765,14 +763,6 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } - - #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - fn get_token_id(&self) -> Option>> { - match &self.ohui_server { - Some(srv) => Some(srv.token_id.clone()), - None => None, - } - } } pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 21fce856d..7c7d7324e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -29,7 +29,7 @@ use std::net::TcpListener; use std::ops::Deref; use std::os::unix::net::UnixListener; use std::path::Path; -use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; +use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; #[cfg(feature = "windows_emu_pid")] use std::time::Duration; @@ -1595,12 +1595,7 @@ pub trait MachineOps { /// /// * `cfg_args` - scream configuration. #[cfg(feature = "scream")] - fn add_ivshmem_scream( - &mut self, - vm_config: &mut VmConfig, - cfg_args: &str, - token_id: Option>>, - ) -> Result<()> { + fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args))?; let bdf = PciBdf { bus: config.bus.clone(), @@ -1623,7 +1618,7 @@ pub trait MachineOps { bail!("Object for share config is not on"); } - let scream = Scream::new(mem_cfg.size, config, token_id); + let scream = Scream::new(mem_cfg.size, config); scream .realize(devfn, parent_bus) .with_context(|| "Failed to realize scream device") @@ -1807,7 +1802,6 @@ pub trait MachineOps { let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; - let token_id = self.get_token_id(); create_device_add_matches!( dev.0.as_str(); self; @@ -1836,7 +1830,7 @@ pub trait MachineOps { #[cfg(feature = "demo_device")] ("pcie-demo-dev", add_demo_dev, vm_config, cfg_args), #[cfg(feature = "scream")] - ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args, token_id), + ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args), #[cfg(feature = "pvpanic")] ("pvpanic", add_pvpanic, cfg_args) ); @@ -1845,10 +1839,6 @@ pub trait MachineOps { Ok(()) } - fn get_token_id(&self) -> Option>> { - None - } - fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> { bail!("Pflash device is not supported!"); } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 190794654..3e288b65a 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -105,8 +105,6 @@ pub struct OhUiServer { framebuffer: u64, // framebuffer file backend fb_file: Option, - // tokenID of OHUI client - pub token_id: Arc>, } impl OhUiServer { @@ -174,7 +172,6 @@ impl OhUiServer { cursorbuffer, framebuffer, fb_file, - token_id: Arc::new(RwLock::new(0)), }) } @@ -198,7 +195,7 @@ impl OhUiServer { if !self.connected() { return Err(anyhow!("connection has not establish".to_string())); } - self.msg_handler.handle_msg(self.token_id.clone()) + self.msg_handler.handle_msg() } fn raw_update_dirty_area( diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs index 69dc7f001..e61270dda 100755 --- a/ui/src/ohui_srv/msg.rs +++ b/ui/src/ohui_srv/msg.rs @@ -127,15 +127,6 @@ pub struct LedstateEvent { impl ByteCode for LedstateEvent {} -#[repr(C, packed)] -#[derive(Debug, Default, Copy, Clone)] -pub struct GreetEvent { - pad: [u32; 6], - pub token_id: u64, -} - -impl ByteCode for GreetEvent {} - #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct FocusEvent { @@ -192,7 +183,6 @@ pub fn event_msg_data_len(event_type: EventType) -> usize { EventType::FrameBufferDirty => size_of::(), EventType::CursorDefine => size_of::(), EventType::Ledstate => size_of::(), - EventType::Greet => size_of::(), _ => 0, } } diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 5f40ff079..afdf8d2ee 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Result}; use log::{error, warn}; @@ -135,7 +135,7 @@ impl OhUiMsgHandler { } } - pub fn handle_msg(&self, token_id: Arc>) -> Result<()> { + pub fn handle_msg(&self) -> Result<()> { let mut reader = self.reader.lock().unwrap(); if !reader.recv(&self.channel)? { return Ok(()); @@ -191,11 +191,6 @@ impl OhUiMsgHandler { self.handle_ledstate(body); Ok(()) } - EventType::Greet => { - let body = GreetEvent::from_bytes(&body_bytes[..]).unwrap(); - *token_id.write().unwrap() = body.token_id; - Ok(()) - } _ => { error!( "unsupported type {:?} and body size {}", diff --git a/util/src/lib.rs b/util/src/lib.rs index 33edf6456..7ca0d3a70 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -28,8 +28,6 @@ pub mod logger; pub mod loop_context; pub mod num_ops; pub mod offsetof; -#[cfg(target_env = "ohos")] -pub mod ohos; #[cfg(feature = "pixman")] pub mod pixman; pub mod seccomp; diff --git a/util/src/ohos/misc.rs b/util/src/ohos/misc.rs deleted file mode 100644 index 4d9678142..000000000 --- a/util/src/ohos/misc.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{bail, Result}; - -#[link(name = "token_setproc")] -extern "C" { - fn SetFirstCallerTokenID(id: u64) -> i32; - fn GetFirstCallerTokenID() -> u64; -} - -pub fn set_firstcaller_tokenid(id: u64) -> Result<()> { - // SAFETY: This function is only applied on this thread. - unsafe { - if SetFirstCallerTokenID(id) != 0 { - bail!("Set first caller failed"); - } - } - Ok(()) -} - -pub fn get_firstcaller_tokenid() -> u64 { - // SAFETY: This function is only applied on this thread. - unsafe { GetFirstCallerTokenID() } -} diff --git a/util/src/ohos/mod.rs b/util/src/ohos/mod.rs deleted file mode 100644 index ea0e76b39..000000000 --- a/util/src/ohos/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod misc; -- Gitee From 24e3511d37c18efd6cba6e578d853146c76f5346 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 20 Mar 2024 12:45:33 +0800 Subject: [PATCH 1679/2187] Trace: Fix a clippy error Fix a clippy error Signed-off-by: Jinhao Gao --- devices/src/pci/msix.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 57233793a..1d1e30be5 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -450,7 +450,7 @@ impl Msix { let masked: bool = self.is_func_masked(config); let enabled: bool = self.is_enabled(config); - trace::msix_write_config(self.dev_id.load(Ordering::Relaxed) as u16, masked, enabled); + trace::msix_write_config(self.dev_id.load(Ordering::Relaxed), masked, enabled); let mask_state_changed = !((self.func_masked == masked) && (self.enabled == enabled)); -- Gitee From bc0a059ad1a048cc5e4183b8144bb02d79e3985d Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 12 Mar 2024 20:19:57 +0800 Subject: [PATCH 1680/2187] scream: pass OHUI client HAP's tokenID to scream On OHOS, its audio framework will check if thread uses microphone on background, if so, microphone will not be allowed to use. Due to stratovirt's lack of HAP window on OHOS, stratovirt is always denied. We get HAP tokenID from OHUI client, pass it to scream record thread and bound thread with tokenID. So OHOS will check OHUI client's HAP tokenID, and record thread gets permission for microphone. Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 24 +++++++++- machine/src/aarch64/standard.rs | 7 +++ machine/src/lib.rs | 19 ++++++-- ui/src/ohui_srv/mod.rs | 5 ++- ui/src/ohui_srv/msg.rs | 10 +++++ ui/src/ohui_srv/msg_handle.rs | 9 +++- util/src/lib.rs | 2 + util/src/ohos_binding/misc.rs | 80 +++++++++++++++++++++++++++++++++ util/src/ohos_binding/mod.rs | 13 ++++++ 9 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 util/src/ohos_binding/misc.rs create mode 100644 util/src/ohos_binding/mod.rs diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 397e8371e..2cb54366c 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -23,7 +23,7 @@ use std::{ str::FromStr, sync::{ atomic::{fence, Ordering}, - Arc, Mutex, Weak, + Arc, Mutex, RwLock, Weak, }, thread, }; @@ -44,6 +44,8 @@ use machine_manager::config::{get_pci_df, valid_id}; use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +use util::ohos_binding::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; @@ -334,6 +336,16 @@ impl StreamData { } } +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +fn bound_tokenid(token_id: u64) -> Result<()> { + if token_id == 0 { + bail!("UI token ID not passed."); + } else if token_id != get_firstcaller_tokenid()? { + set_firstcaller_tokenid(token_id)?; + } + Ok(()) +} + #[derive(Clone, Debug)] enum ScreamInterface { #[cfg(feature = "scream_alsa")] @@ -386,14 +398,16 @@ pub struct Scream { hva: u64, size: u64, config: ScreamConfig, + token_id: Option>>, } impl Scream { - pub fn new(size: u64, config: ScreamConfig) -> Self { + pub fn new(size: u64, config: ScreamConfig, token_id: Option>>) -> Self { Self { hva: 0, size, config, + token_id, } } @@ -443,6 +457,7 @@ impl Scream { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); + let _ti = self.token_id.clone(); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { @@ -457,6 +472,11 @@ impl Scream { hva, ); + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + if let Some(token_id) = &_ti { + bound_tokenid(*token_id.read().unwrap()) + .unwrap_or_else(|e| error!("bound token ID failed: {}", e)); + } capt_data.capture_trans(hva, shmem_size, clone_interface.clone()); } }) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 7b67aa171..8544a2b79 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -14,6 +14,8 @@ pub use crate::error::MachineError; use std::mem::size_of; use std::ops::Deref; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use std::sync::RwLock; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -763,6 +765,11 @@ impl MachineOps for StdMachine { fn get_boot_order_list(&self) -> Option>>> { Some(self.boot_order_list.clone()) } + + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + fn get_token_id(&self) -> Option>> { + self.ohui_server.as_ref().map(|srv| srv.token_id.clone()) + } } pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7c7d7324e..6c13c03c1 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -29,7 +29,7 @@ use std::net::TcpListener; use std::ops::Deref; use std::os::unix::net::UnixListener; use std::path::Path; -use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; +use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; #[cfg(feature = "windows_emu_pid")] use std::time::Duration; @@ -1595,7 +1595,12 @@ pub trait MachineOps { /// /// * `cfg_args` - scream configuration. #[cfg(feature = "scream")] - fn add_ivshmem_scream(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { + fn add_ivshmem_scream( + &mut self, + vm_config: &mut VmConfig, + cfg_args: &str, + token_id: Option>>, + ) -> Result<()> { let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args))?; let bdf = PciBdf { bus: config.bus.clone(), @@ -1618,7 +1623,7 @@ pub trait MachineOps { bail!("Object for share config is not on"); } - let scream = Scream::new(mem_cfg.size, config); + let scream = Scream::new(mem_cfg.size, config, token_id); scream .realize(devfn, parent_bus) .with_context(|| "Failed to realize scream device") @@ -1802,6 +1807,8 @@ pub trait MachineOps { let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; + #[cfg(feature = "scream")] + let token_id = self.get_token_id(); create_device_add_matches!( dev.0.as_str(); self; @@ -1830,7 +1837,7 @@ pub trait MachineOps { #[cfg(feature = "demo_device")] ("pcie-demo-dev", add_demo_dev, vm_config, cfg_args), #[cfg(feature = "scream")] - ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args), + ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args, token_id), #[cfg(feature = "pvpanic")] ("pvpanic", add_pvpanic, cfg_args) ); @@ -1839,6 +1846,10 @@ pub trait MachineOps { Ok(()) } + fn get_token_id(&self) -> Option>> { + None + } + fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> { bail!("Pflash device is not supported!"); } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 3e288b65a..190794654 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -105,6 +105,8 @@ pub struct OhUiServer { framebuffer: u64, // framebuffer file backend fb_file: Option, + // tokenID of OHUI client + pub token_id: Arc>, } impl OhUiServer { @@ -172,6 +174,7 @@ impl OhUiServer { cursorbuffer, framebuffer, fb_file, + token_id: Arc::new(RwLock::new(0)), }) } @@ -195,7 +198,7 @@ impl OhUiServer { if !self.connected() { return Err(anyhow!("connection has not establish".to_string())); } - self.msg_handler.handle_msg() + self.msg_handler.handle_msg(self.token_id.clone()) } fn raw_update_dirty_area( diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs index e61270dda..69dc7f001 100755 --- a/ui/src/ohui_srv/msg.rs +++ b/ui/src/ohui_srv/msg.rs @@ -127,6 +127,15 @@ pub struct LedstateEvent { impl ByteCode for LedstateEvent {} +#[repr(C, packed)] +#[derive(Debug, Default, Copy, Clone)] +pub struct GreetEvent { + pad: [u32; 6], + pub token_id: u64, +} + +impl ByteCode for GreetEvent {} + #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct FocusEvent { @@ -183,6 +192,7 @@ pub fn event_msg_data_len(event_type: EventType) -> usize { EventType::FrameBufferDirty => size_of::(), EventType::CursorDefine => size_of::(), EventType::Ledstate => size_of::(), + EventType::Greet => size_of::(), _ => 0, } } diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index afdf8d2ee..5f40ff079 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use anyhow::{anyhow, bail, Result}; use log::{error, warn}; @@ -135,7 +135,7 @@ impl OhUiMsgHandler { } } - pub fn handle_msg(&self) -> Result<()> { + pub fn handle_msg(&self, token_id: Arc>) -> Result<()> { let mut reader = self.reader.lock().unwrap(); if !reader.recv(&self.channel)? { return Ok(()); @@ -191,6 +191,11 @@ impl OhUiMsgHandler { self.handle_ledstate(body); Ok(()) } + EventType::Greet => { + let body = GreetEvent::from_bytes(&body_bytes[..]).unwrap(); + *token_id.write().unwrap() = body.token_id; + Ok(()) + } _ => { error!( "unsupported type {:?} and body size {}", diff --git a/util/src/lib.rs b/util/src/lib.rs index 7ca0d3a70..f861d6f6a 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -28,6 +28,8 @@ pub mod logger; pub mod loop_context; pub mod num_ops; pub mod offsetof; +#[cfg(target_env = "ohos")] +pub mod ohos_binding; #[cfg(feature = "pixman")] pub mod pixman; pub mod seccomp; diff --git a/util/src/ohos_binding/misc.rs b/util/src/ohos_binding/misc.rs new file mode 100644 index 000000000..1d9e31a57 --- /dev/null +++ b/util/src/ohos_binding/misc.rs @@ -0,0 +1,80 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::fs::OpenOptions; + +use anyhow::{bail, Context, Result}; +use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; +use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; + +const ACCESS_TOKEN_ID_IOCTL_BASE: u32 = b'A' as u32; +const GET_FTOKEN_ID: u32 = 3; +const SET_FTOKEN_ID: u32 = 4; +const ACCESS_TOKEN_ID_DEV: &str = "/dev/access_token_id"; + +ioctl_iow_nr!( + ACCESS_TOKENID_SET_FTOKENID, + ACCESS_TOKEN_ID_IOCTL_BASE, + SET_FTOKEN_ID, + ::std::os::raw::c_ulonglong +); +ioctl_ior_nr!( + ACCESS_TOKENID_GET_FTOKENID, + ACCESS_TOKEN_ID_IOCTL_BASE, + GET_FTOKEN_ID, + ::std::os::raw::c_ulonglong +); + +pub fn set_firstcaller_tokenid(id: u64) -> Result<()> { + let fd = OpenOptions::new() + .read(true) + .write(true) + .open(ACCESS_TOKEN_ID_DEV) + .with_context(|| { + format!( + "Failed to open {} for set_firstcaller_tokenid.", + ACCESS_TOKEN_ID_DEV + ) + })?; + // SAFETY: ioctl is safe. called file is '/dev/access_token_id' fd and we check the return. + let ret = unsafe { ioctl_with_ref(&fd, ACCESS_TOKENID_SET_FTOKENID(), &id) }; + if ret != 0 { + bail!( + "Failed to set first caller tokenid: {ret}, error info: {}", + std::io::Error::last_os_error() + ); + } + Ok(()) +} + +pub fn get_firstcaller_tokenid() -> Result { + let fd = OpenOptions::new() + .read(true) + .write(true) + .open(ACCESS_TOKEN_ID_DEV) + .with_context(|| { + format!( + "Failed to open {} for get_firstcaller_tokenid.", + ACCESS_TOKEN_ID_DEV + ) + })?; + let mut id: u64 = 0; + // SAFETY: ioctl is safe. called file is '/dev/access_token_id' fd and we check the return. + let ret = unsafe { ioctl_with_mut_ref(&fd, ACCESS_TOKENID_GET_FTOKENID(), &mut id) }; + if ret != 0 { + bail!( + "Failed to get first caller tokenid: {ret}, error info: {}", + std::io::Error::last_os_error() + ); + } + Ok(id) +} diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs new file mode 100644 index 000000000..ea0e76b39 --- /dev/null +++ b/util/src/ohos_binding/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod misc; -- Gitee From 50917ee602bed4098f10fb75424d197adf4ce957 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Wed, 20 Mar 2024 17:27:00 +0800 Subject: [PATCH 1681/2187] AddressSpace: Fix a bug The src of mmio write will be none once read. The write of view will fail. Signed-off-by: Jinhao Gao --- address_space/src/address_space.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 261365e59..b8e9ccb2e 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -653,6 +653,8 @@ impl AddressSpace { return Ok(()); } } + view.write(&mut buf.as_slice(), addr, count)?; + return Ok(()); } } } -- Gitee From 0866ae504dd608d8db8d413fb05e21dc1b84aa38 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 24 Mar 2024 10:49:30 +0800 Subject: [PATCH 1682/2187] vhost-user-blk: `VIRTIO_BLK_F_RO` is set in SPDK Vhost-user-blk doesn't support `readonly` parameter. The read-only feature is set in SPDK and is negotiated between SPDK and stratovirt by vhost user message `GetFeatures`. Set `VIRTIO_BLK_F_RO` in stratovirt as default feature, and then it will be intersected with the features of SPDK. Signed-off-by: liuxiangdong --- virtio/src/vhost/user/block.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 499a7c853..1fe8ac8e2 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -167,10 +167,8 @@ impl VirtioDevice for Block { | 1_u64 << VIRTIO_BLK_F_FLUSH | 1_u64 << VIRTIO_BLK_F_DISCARD | 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES - | 1_u64 << VIRTIO_BLK_F_SEG_MAX; - if self.blk_cfg.read_only { - self.base.device_features |= 1_u64 << VIRTIO_BLK_F_RO; - }; + | 1_u64 << VIRTIO_BLK_F_SEG_MAX + | 1_u64 << VIRTIO_BLK_F_RO; if self.blk_cfg.queues > 1 { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; } -- Gitee From 36b955773c8233bed017211b462bbe0b22101407 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Sun, 24 Mar 2024 16:30:12 +0800 Subject: [PATCH 1683/2187] Trace: Introduce trace scope Implement tracing of a piece of code, which will print message at the beginning and end, mainly used to obtain performance data. Signed-off-by: Gan Qixin --- Cargo.toml | 1 + trace/Cargo.toml | 1 + trace/build.rs | 2 +- trace/src/ftrace.rs | 11 ++ trace/src/hitrace.rs | 53 ++++++ trace/src/lib.rs | 79 +++++--- trace/src/trace_scope.rs | 113 ++++++++++++ trace/trace_generator/src/lib.rs | 170 ++++++++++++++---- trace/{trace_event => trace_info}/aio.toml | 0 .../block_backend.toml | 0 trace/{trace_event => trace_info}/camera.toml | 0 trace/{trace_event => trace_info}/cpu.toml | 0 .../device_legacy.toml | 0 .../event_loop.toml | 0 trace/trace_info/gpu.toml | 5 + .../{trace_event => trace_info}/machine.toml | 0 trace/{trace_event => trace_info}/misc.toml | 0 trace/{trace_event => trace_info}/pci.toml | 0 trace/{trace_event => trace_info}/scsi.toml | 0 trace/{trace_event => trace_info}/ui.toml | 0 trace/{trace_event => trace_info}/usb.toml | 0 trace/{trace_event => trace_info}/virtio.toml | 0 virtio/src/device/gpu.rs | 1 + 23 files changed, 376 insertions(+), 60 deletions(-) create mode 100644 trace/src/hitrace.rs create mode 100644 trace/src/trace_scope.rs rename trace/{trace_event => trace_info}/aio.toml (100%) rename trace/{trace_event => trace_info}/block_backend.toml (100%) rename trace/{trace_event => trace_info}/camera.toml (100%) rename trace/{trace_event => trace_info}/cpu.toml (100%) rename trace/{trace_event => trace_info}/device_legacy.toml (100%) rename trace/{trace_event => trace_info}/event_loop.toml (100%) create mode 100644 trace/trace_info/gpu.toml rename trace/{trace_event => trace_info}/machine.toml (100%) rename trace/{trace_event => trace_info}/misc.toml (100%) rename trace/{trace_event => trace_info}/pci.toml (100%) rename trace/{trace_event => trace_info}/scsi.toml (100%) rename trace/{trace_event => trace_info}/ui.toml (100%) rename trace/{trace_event => trace_info}/usb.toml (100%) rename trace/{trace_event => trace_info}/virtio.toml (100%) diff --git a/Cargo.toml b/Cargo.toml index 32af637d9..1912aa6d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ ramfb = ["machine/ramfb"] virtio_gpu = ["machine/virtio_gpu"] trace_to_logger = ["trace/trace_to_logger"] trace_to_ftrace = ["trace/trace_to_ftrace"] +trace_to_hitrace = ["trace/trace_to_hitrace"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/trace/Cargo.toml b/trace/Cargo.toml index 55e511c5c..2355cdb0d 100644 --- a/trace/Cargo.toml +++ b/trace/Cargo.toml @@ -16,3 +16,4 @@ trace_generator = { path = "trace_generator" } [features] trace_to_logger = [] trace_to_ftrace = [] +trace_to_hitrace = [] diff --git a/trace/build.rs b/trace/build.rs index cac00d636..d8a01f1d4 100644 --- a/trace/build.rs +++ b/trace/build.rs @@ -12,7 +12,7 @@ fn main() { println!( - "cargo:rerun-if-changed={}/trace_event", + "cargo:rerun-if-changed={}/trace_info", std::env::var("CARGO_MANIFEST_DIR").unwrap() ); } diff --git a/trace/src/ftrace.rs b/trace/src/ftrace.rs index 607ce0486..a6bf373dd 100644 --- a/trace/src/ftrace.rs +++ b/trace/src/ftrace.rs @@ -13,8 +13,15 @@ use std::{ fs::{File, OpenOptions}, io::{prelude::Write, BufRead, BufReader}, + sync::Mutex, }; +use lazy_static::lazy_static; + +lazy_static! { + static ref TRACE_MARKER_FD: Mutex = Mutex::new(open_trace_marker()); +} + pub(crate) fn open_trace_marker() -> File { let mounts_path: &str = "/proc/mounts"; let mounts_fd = File::open(mounts_path) @@ -57,3 +64,7 @@ pub(crate) fn open_trace_marker() -> File { .open(&trace_marker_path) .unwrap_or_else(|e| panic!("Failed to open {}: {:?}", trace_marker_path, e)) } + +pub fn write_trace_marker(buf: &str) { + let _result = TRACE_MARKER_FD.lock().unwrap().write_all(buf.as_bytes()); +} diff --git a/trace/src/hitrace.rs b/trace/src/hitrace.rs new file mode 100644 index 000000000..1cff1cd7c --- /dev/null +++ b/trace/src/hitrace.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +const HITRACE_TAG_VIRSE: u64 = 1u64 << 11; + +#[link(name = "hitrace_meter")] +extern "C" { + fn StartTraceWrapper(label: u64, value: *const u8); + fn FinishTrace(label: u64); + fn StartAsyncTraceWrapper(label: u64, value: *const u8, taskId: i32); + fn FinishAsyncTraceWrapper(label: u64, value: *const u8, taskId: i32); +} + +pub fn start_trace(value: &str) { + if let Ok(value_ptr) = std::ffi::CString::new(value) { + // SAFETY: All parameters have been checked. + unsafe { StartTraceWrapper(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8) } + } +} + +pub fn finish_trace() { + // SAFETY: All parameters have been checked. + unsafe { + FinishTrace(HITRACE_TAG_VIRSE); + } +} + +pub fn start_trace_async(value: &str, task_id: i32) { + if let Ok(value_ptr) = std::ffi::CString::new(value) { + // SAFETY: All parameters have been checked. + unsafe { + StartAsyncTraceWrapper(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8, task_id) + } + } +} + +pub fn finish_trace_async(value: &str, task_id: i32) { + if let Ok(value_ptr) = std::ffi::CString::new(value) { + // SAFETY: All parameters have been checked. + unsafe { + FinishAsyncTraceWrapper(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8, task_id) + } + } +} diff --git a/trace/src/lib.rs b/trace/src/lib.rs index 7aceb1d5f..d6228f293 100644 --- a/trace/src/lib.rs +++ b/trace/src/lib.rs @@ -12,30 +12,38 @@ #[cfg(feature = "trace_to_ftrace")] pub(crate) mod ftrace; +#[cfg(all(target_env = "ohos", feature = "trace_to_hitrace"))] +pub(crate) mod hitrace; +#[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") +))] +pub(crate) mod trace_scope; use std::{ fmt, os::unix::io::RawFd, sync::atomic::{AtomicBool, Ordering}, }; -#[cfg(feature = "trace_to_ftrace")] -use std::{fs::File, io::Write, sync::Mutex}; use anyhow::{Ok, Result}; use lazy_static::lazy_static; use regex::Regex; -use trace_generator::{add_trace_event_to, gen_trace_func, gen_trace_state}; +use trace_generator::{ + add_trace_state_to, gen_trace_event_func, gen_trace_scope_func, gen_trace_state, +}; -struct TraceEvent { +struct TraceState { name: String, get_state: fn() -> bool, set_state: fn(bool), } -impl TraceEvent { +impl TraceState { fn new(name: String, get_state: fn() -> bool, set_state: fn(bool)) -> Self { - TraceEvent { + TraceState { name, get_state, set_state, @@ -44,20 +52,20 @@ impl TraceEvent { } #[derive(Default)] -struct TraceEventSet { - event_list: Vec, +struct TraceStateSet { + state_list: Vec, } -impl TraceEventSet { - fn add_trace_event(&mut self, event: TraceEvent) { - self.event_list.push(event); +impl TraceStateSet { + fn add_trace_state(&mut self, state: TraceState) { + self.state_list.push(state); } - fn set_state_by_pattern(&self, pattern: String, state: bool) -> Result<()> { + fn set_state_by_pattern(&self, pattern: String, target_state: bool) -> Result<()> { let re = Regex::new(&pattern)?; - for event in &self.event_list { - if re.is_match(&event.name) { - (event.set_state)(state); + for state in &self.state_list { + if re.is_match(&state.name) { + (state.set_state)(target_state); } } Ok(()) @@ -66,9 +74,9 @@ impl TraceEventSet { fn get_state_by_pattern(&self, pattern: String) -> Result> { let re = Regex::new(&pattern)?; let mut ret: Vec<(String, bool)> = Vec::new(); - for event in &self.event_list { - if re.is_match(&event.name) { - ret.push((event.name.to_string(), (event.get_state)())); + for state in &self.state_list { + if re.is_match(&state.name) { + ret.push((state.name.to_string(), (state.get_state)())); } } Ok(ret) @@ -78,24 +86,41 @@ impl TraceEventSet { gen_trace_state! {} lazy_static! { - static ref TRACE_EVENT_SET: TraceEventSet = { - let mut set = TraceEventSet::default(); - add_trace_event_to!(set); + static ref TRACE_STATE_SET: TraceStateSet = { + let mut set = TraceStateSet::default(); + add_trace_state_to!(set); set }; } -gen_trace_func! {} +gen_trace_event_func! {} -#[cfg(feature = "trace_to_ftrace")] -lazy_static! { - static ref TRACE_MARKER_FD: Mutex = Mutex::new(ftrace::open_trace_marker()); +gen_trace_scope_func! {} + +#[macro_export] +macro_rules! trace_scope_start { + ($func: ident) => { + let _scope = trace::$func(false); + }; + ($func: ident, args=($($args: expr),+)) => { + let _scope = trace::$func(false, $($args),+); + }; +} + +#[macro_export] +macro_rules! trace_scope_asyn_start { + ($func: ident) => { + let _scope = trace::$func(true); + }; + ($func: ident, args=($($args: expr),+)) => { + let _scope = trace::$func(true, $($args),+); + }; } pub fn get_state_by_pattern(pattern: String) -> Result> { - TRACE_EVENT_SET.get_state_by_pattern(pattern) + TRACE_STATE_SET.get_state_by_pattern(pattern) } pub fn set_state_by_pattern(pattern: String, state: bool) -> Result<()> { - TRACE_EVENT_SET.set_state_by_pattern(pattern, state) + TRACE_STATE_SET.set_state_by_pattern(pattern, state) } diff --git a/trace/src/trace_scope.rs b/trace/src/trace_scope.rs new file mode 100644 index 000000000..4a0210170 --- /dev/null +++ b/trace/src/trace_scope.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::atomic::{AtomicI32, Ordering}; + +#[cfg(all(target_env = "ohos", feature = "trace_to_hitrace"))] +use crate::hitrace::{finish_trace, finish_trace_async, start_trace, start_trace_async}; + +#[cfg(feature = "trace_to_ftrace")] +use crate::ftrace::write_trace_marker; + +static mut TRACE_SCOPE_COUNTER: AtomicI32 = AtomicI32::new(i32::MIN); + +pub enum Scope { + Common(TraceScope), + Asyn(TraceScopeAsyn), + None, +} + +pub struct TraceScope {} + +impl TraceScope { + pub fn new(value: String) -> Self { + #[cfg(feature = "trace_to_logger")] + { + log::trace!("[SCOPE_START]{}", value); + } + #[cfg(feature = "trace_to_ftrace")] + { + write_trace_marker(&format!("[SCOPE_START]{}", value)); + } + #[cfg(all(target_env = "ohos", feature = "trace_to_hitrace"))] + { + start_trace(&value); + } + TraceScope {} + } +} + +impl Drop for TraceScope { + fn drop(&mut self) { + #[cfg(feature = "trace_to_logger")] + { + log::trace!("[SCOPE_END]"); + } + #[cfg(feature = "trace_to_ftrace")] + { + write_trace_marker("[SCOPE_END]"); + } + #[cfg(all(target_env = "ohos", feature = "trace_to_hitrace"))] + { + finish_trace() + } + } +} + +pub struct TraceScopeAsyn { + value: String, + id: i32, +} + +impl TraceScopeAsyn { + #[allow(unused_variables)] + pub fn new(value: String) -> Self { + // SAFETY: AtomicI32 can be safely shared between threads. + let id = unsafe { + TRACE_SCOPE_COUNTER + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + Some(x.wrapping_add(1)) + }) + .unwrap() + }; + #[cfg(feature = "trace_to_logger")] + { + log::trace!("[SCOPE_START(id={})]{}", id, value); + } + #[cfg(feature = "trace_to_ftrace")] + { + write_trace_marker(&format!("[SCOPE_START(id={})]{}", id, value)); + } + #[cfg(all(target_env = "ohos", feature = "trace_to_hitrace"))] + { + start_trace_async(&value, id); + } + TraceScopeAsyn { value, id } + } +} + +impl Drop for TraceScopeAsyn { + fn drop(&mut self) { + #[cfg(feature = "trace_to_logger")] + { + log::trace!("[SCOPE_END(id={})]{}", self.id, self.value); + } + #[cfg(feature = "trace_to_ftrace")] + { + write_trace_marker(&format!("[SCOPE_END(id={})]{}", self.id, self.value)); + } + #[cfg(all(target_env = "ohos", feature = "trace_to_hitrace"))] + { + finish_trace_async(&self.value, self.id); + } + } +} diff --git a/trace/trace_generator/src/lib.rs b/trace/trace_generator/src/lib.rs index 8115ad6d7..008263cc0 100644 --- a/trace/trace_generator/src/lib.rs +++ b/trace/trace_generator/src/lib.rs @@ -17,10 +17,10 @@ use quote::quote; use serde::Deserialize; use syn::{parse_macro_input, parse_str, Ident, Type}; -const TRACE_DIR_NAME: &str = "trace_event"; +const TRACE_DIR_NAME: &str = "trace_info"; #[derive(Debug, Deserialize)] -struct TraceEventDesc { +struct TraceDesc { name: String, args: String, message: String, @@ -29,10 +29,11 @@ struct TraceEventDesc { #[derive(Debug, Deserialize)] struct TraceConf { - events: Vec, + events: Option>, + scopes: Option>, } -fn get_trace_event_desc() -> TraceConf { +fn get_trace_desc() -> TraceConf { let trace_dir_path = format!( "{}/{}", std::env::var("CARGO_MANIFEST_DIR").unwrap(), @@ -53,32 +54,51 @@ fn get_trace_event_desc() -> TraceConf { } #[proc_macro] -pub fn add_trace_event_to(input: TokenStream) -> TokenStream { - let set = parse_macro_input!(input as Ident); - let events = get_trace_event_desc().events; - let init_code = events.iter().map(|desc| match &desc.enabled { - true => { - let event_name = desc.name.trim(); - let get_func = parse_str::(format!("get_{}_state", event_name).as_str()).unwrap(); - let set_func = parse_str::(format!("set_{}_state", event_name).as_str()).unwrap(); - quote!( - #set.add_trace_event(TraceEvent::new(#event_name.to_string(), #get_func, #set_func)); - ) +pub fn add_trace_state_to(input: TokenStream) -> TokenStream { + let trace_conf = get_trace_desc(); + let mut state_name = Vec::new(); + for desc in trace_conf.events.unwrap_or_default() { + if desc.enabled { + state_name.push(desc.name.trim().to_string()); + } + } + for desc in trace_conf.scopes.unwrap_or_default() { + if desc.enabled { + state_name.push(desc.name.trim().to_string()); } - false => quote!(), + } + + let set = parse_macro_input!(input as Ident); + let init_code = state_name.iter().map(|name| { + let get_func = parse_str::(format!("get_{}_state", name).as_str()).unwrap(); + let set_func = parse_str::(format!("set_{}_state", name).as_str()).unwrap(); + quote!( + #set.add_trace_state(TraceState::new(#name.to_string(), #get_func, #set_func)); + ) }); TokenStream::from(quote! { #( #init_code )* }) } #[proc_macro] pub fn gen_trace_state(_input: TokenStream) -> TokenStream { - let events = get_trace_event_desc().events; - let trace_state = events.iter().map(|desc| { - let event_name = parse_str::(desc.name.trim()).unwrap(); + let trace_conf = get_trace_desc(); + let mut state_name = Vec::new(); + for desc in trace_conf.events.unwrap_or_default() { + if desc.enabled { + state_name.push(desc.name.trim().to_string()); + } + } + for desc in trace_conf.scopes.unwrap_or_default() { + if desc.enabled { + state_name.push(desc.name.trim().to_string()); + } + } + + let trace_state = state_name.iter().map(|name| { let state_name = - parse_str::(format!("{}_state", event_name).to_uppercase().as_str()).unwrap(); - let get_func = parse_str::(format!("get_{}_state", event_name).as_str()).unwrap(); - let set_func = parse_str::(format!("set_{}_state", event_name).as_str()).unwrap(); + parse_str::(format!("{}_state", name).to_uppercase().as_str()).unwrap(); + let get_func = parse_str::(format!("get_{}_state", name).as_str()).unwrap(); + let set_func = parse_str::(format!("set_{}_state", name).as_str()).unwrap(); quote!( static mut #state_name: AtomicBool = AtomicBool::new(false); fn #get_func() -> bool { @@ -96,10 +116,14 @@ pub fn gen_trace_state(_input: TokenStream) -> TokenStream { } #[proc_macro] -pub fn gen_trace_func(_input: TokenStream) -> TokenStream { - let events = get_trace_event_desc().events; +pub fn gen_trace_event_func(_input: TokenStream) -> TokenStream { + let events = match get_trace_desc().events { + Some(events) => events, + None => return TokenStream::from(quote!()), + }; let trace_func = events.iter().map(|desc| { - let func_name = parse_str::(desc.name.trim()).unwrap(); + let event_name = desc.name.trim(); + let func_name = parse_str::(event_name).unwrap(); let func_args = match desc.args.is_empty() { true => quote!(), @@ -134,12 +158,9 @@ pub fn gen_trace_func(_input: TokenStream) -> TokenStream { let func_body = match desc.enabled { true => { - let mut _body = quote!(); let message = format!("[{{}}] {}", desc.message.trim()); - let event_name = desc.name.trim(); let state_name = parse_str::(format!("{}_state", event_name).to_uppercase().as_str()).unwrap(); - _body = quote!( - #_body + quote!( #[cfg(any(feature = "trace_to_logger", feature = "trace_to_ftrace"))] // SAFETY: AtomicBool can be safely shared between threads. if unsafe { #state_name.load(Ordering::SeqCst) } { @@ -150,11 +171,10 @@ pub fn gen_trace_func(_input: TokenStream) -> TokenStream { #[cfg(feature = "trace_to_ftrace")] { let trace_info = format!(#message, #event_name.to_string() #message_args); - let _result = TRACE_MARKER_FD.lock().unwrap().write_all(trace_info.as_bytes()); + let _result = ftrace::write_trace_marker(&trace_info); } } - ); - _body + ) } false => quote!(), }; @@ -169,3 +189,89 @@ pub fn gen_trace_func(_input: TokenStream) -> TokenStream { TokenStream::from(quote! { #( #trace_func )* }) } + +#[proc_macro] +pub fn gen_trace_scope_func(_input: TokenStream) -> TokenStream { + let scopes = match get_trace_desc().scopes { + Some(scopes) => scopes, + None => return TokenStream::from(quote!()), + }; + let trace_func =scopes.iter().map(|desc| { + let scope_name = desc.name.trim(); + let func_name = parse_str::(scope_name).unwrap(); + + let func_args = match desc.args.is_empty() { + true => quote!(), + false => { + let split_args: Vec<&str> = desc.args.split(',').collect(); + let _args = split_args.iter().map(|arg| { + let (v, t) = arg.split_once(':').unwrap(); + let arg_name = parse_str::(v.trim()).unwrap(); + let arg_type = parse_str::(t.trim()).unwrap(); + quote!( + #arg_name: #arg_type, + ) + }); + quote! { #( #_args )* } + } + }; + + let message_args = match desc.args.is_empty() { + true => quote!(), + false => { + let split_args: Vec<&str> = desc.args.split(',').collect(); + let _args = split_args.iter().map(|arg| { + let (v, _) = arg.split_once(':').unwrap(); + let arg_name = parse_str::(v.trim()).unwrap(); + quote!( + , #arg_name + ) + }); + quote! { #( #_args )* } + } + }; + + let func_body = match desc.enabled { + true => { + let message = format!("[{{}}] {}", desc.message.trim()); + let state_name = parse_str::(format!("{}_state", scope_name).to_uppercase().as_str()).unwrap(); + quote!( + #[cfg(any(feature = "trace_to_logger", feature = "trace_to_ftrace", all(target_env = "ohos", feature = "trace_to_hitrace")))] + // SAFETY: AtomicBool can be safely shared between threads. + if unsafe { #state_name.load(Ordering::SeqCst) } { + let trace_info = format!(#message, #scope_name.to_string() #message_args); + if asyn { + return trace_scope::Scope::Asyn(trace_scope::TraceScopeAsyn::new(trace_info)) + } + return trace_scope::Scope::Common(trace_scope::TraceScope::new(trace_info)) + } + return trace_scope::Scope::None + ) + } + false => quote!(), + }; + + quote!( + #[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + ))] + #[inline(always)] + pub fn #func_name(asyn: bool, #func_args) -> trace_scope::Scope { + #func_body + } + + #[cfg(not(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + )))] + #[inline(always)] + pub fn #func_name(asyn: bool, #func_args) { + } + ) + }); + + TokenStream::from(quote! { #( #trace_func )* }) +} diff --git a/trace/trace_event/aio.toml b/trace/trace_info/aio.toml similarity index 100% rename from trace/trace_event/aio.toml rename to trace/trace_info/aio.toml diff --git a/trace/trace_event/block_backend.toml b/trace/trace_info/block_backend.toml similarity index 100% rename from trace/trace_event/block_backend.toml rename to trace/trace_info/block_backend.toml diff --git a/trace/trace_event/camera.toml b/trace/trace_info/camera.toml similarity index 100% rename from trace/trace_event/camera.toml rename to trace/trace_info/camera.toml diff --git a/trace/trace_event/cpu.toml b/trace/trace_info/cpu.toml similarity index 100% rename from trace/trace_event/cpu.toml rename to trace/trace_info/cpu.toml diff --git a/trace/trace_event/device_legacy.toml b/trace/trace_info/device_legacy.toml similarity index 100% rename from trace/trace_event/device_legacy.toml rename to trace/trace_info/device_legacy.toml diff --git a/trace/trace_event/event_loop.toml b/trace/trace_info/event_loop.toml similarity index 100% rename from trace/trace_event/event_loop.toml rename to trace/trace_info/event_loop.toml diff --git a/trace/trace_info/gpu.toml b/trace/trace_info/gpu.toml new file mode 100644 index 000000000..d794cfabf --- /dev/null +++ b/trace/trace_info/gpu.toml @@ -0,0 +1,5 @@ +[[scopes]] +name = "update_cursor" +args = "" +message = "" +enabled = true diff --git a/trace/trace_event/machine.toml b/trace/trace_info/machine.toml similarity index 100% rename from trace/trace_event/machine.toml rename to trace/trace_info/machine.toml diff --git a/trace/trace_event/misc.toml b/trace/trace_info/misc.toml similarity index 100% rename from trace/trace_event/misc.toml rename to trace/trace_info/misc.toml diff --git a/trace/trace_event/pci.toml b/trace/trace_info/pci.toml similarity index 100% rename from trace/trace_event/pci.toml rename to trace/trace_info/pci.toml diff --git a/trace/trace_event/scsi.toml b/trace/trace_info/scsi.toml similarity index 100% rename from trace/trace_event/scsi.toml rename to trace/trace_info/scsi.toml diff --git a/trace/trace_event/ui.toml b/trace/trace_info/ui.toml similarity index 100% rename from trace/trace_event/ui.toml rename to trace/trace_info/ui.toml diff --git a/trace/trace_event/usb.toml b/trace/trace_info/usb.toml similarity index 100% rename from trace/trace_event/usb.toml rename to trace/trace_info/usb.toml diff --git a/trace/trace_event/virtio.toml b/trace/trace_info/virtio.toml similarity index 100% rename from trace/trace_event/virtio.toml rename to trace/trace_info/virtio.toml diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index db7f98d21..97ca67c14 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -843,6 +843,7 @@ impl GpuIoHandler { } fn update_cursor(&mut self, info_cursor: &VirtioGpuUpdateCursor, hdr_type: u32) -> Result<()> { + trace::trace_scope_start!(update_cursor); let scanout = &mut self.scanouts[info_cursor.pos.scanout_id as usize]; match &mut scanout.mouse { None => { -- Gitee From 7e5a2439f358062a846f6fc8cb6c7fb4a7849256 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Sun, 24 Mar 2024 18:07:50 +0800 Subject: [PATCH 1684/2187] Trace: Delete the code containing event in qmp and cmdline These methods can be used for both trace event and trace scope. To avoid ambiguity, modify the relevant code. Signed-off-by: Gan Qixin --- machine_manager/src/cmdline.rs | 4 ++-- machine_manager/src/config/mod.rs | 24 ++++++++++++------------ machine_manager/src/qmp/qmp_schema.rs | 22 +++++++++++----------- machine_manager/src/qmp/qmp_socket.rs | 10 +++++----- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index e11f18393..026c69590 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -446,8 +446,8 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("trace") .multiple(false) .long("trace") - .value_name("events=") - .help("specify the file lists trace events to enable") + .value_name("file=") + .help("specify the file lists trace state to enable") .takes_value(true), ) .arg( diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index e7b0bfeb3..8b2944d8c 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -625,18 +625,18 @@ pub fn parse_bool(s: &str) -> Result { } } -fn enable_trace_events(path: &str) -> Result<()> { +fn enable_trace_state(path: &str) -> Result<()> { let mut file = File::open(path).with_context(|| format!("Failed to open {}", path))?; let mut buf = String::new(); file.read_to_string(&mut buf) .with_context(|| format!("Failed to read {}", path))?; - let events: Vec<&str> = buf.split('\n').filter(|&s| !s.is_empty()).collect(); - for e in events { - set_state_by_pattern(e.trim().to_string(), true).with_context(|| { + let state: Vec<&str> = buf.split('\n').filter(|&s| !s.is_empty()).collect(); + for s in state { + set_state_by_pattern(s.trim().to_string(), true).with_context(|| { format!( "Unable to set the state of {} according to {}", - e.trim(), + s.trim(), path ) })?; @@ -646,13 +646,13 @@ fn enable_trace_events(path: &str) -> Result<()> { pub fn parse_trace_options(opt: &str) -> Result<()> { let mut cmd_parser = CmdParser::new("trace"); - cmd_parser.push("events"); + cmd_parser.push("file"); cmd_parser.get_parameters(opt)?; let path = cmd_parser - .get_value::("events")? - .with_context(|| "trace: events file must be set.")?; - enable_trace_events(&path)?; + .get_value::("file")? + .with_context(|| "trace: trace file must be set.")?; + enable_trace_state(&path)?; Ok(()) } @@ -849,9 +849,9 @@ mod tests { #[test] fn test_parse_trace_options() { - assert!(parse_trace_options("event=test_trace_events").is_err()); - assert!(parse_trace_options("events").is_err()); - assert!(parse_trace_options("events=test_trace_events").is_err()); + assert!(parse_trace_options("fil=test_trace").is_err()); + assert!(parse_trace_options("file").is_err()); + assert!(parse_trace_options("file=test_trace").is_err()); } #[test] diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 37f37bb05..372e417ab 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -136,8 +136,8 @@ define_qmp_command_enum!( blockdev_snapshot_internal_sync("blockdev-snapshot-internal-sync", blockdev_snapshot_internal, FALSE), blockdev_snapshot_delete_internal_sync("blockdev-snapshot-delete-internal-sync", blockdev_snapshot_internal, FALSE), query_vcpu_reg("query-vcpu-reg", query_vcpu_reg, FALSE), - trace_event_get_state("trace-event-get-state", trace_event_get_state, FALSE), - trace_event_set_state("trace-event-set-state", trace_event_set_state, FALSE) + trace_get_state("trace-get-state", trace_get_state, FALSE), + trace_set_state("trace-set-state", trace_set_state, FALSE) ); /// Command trait for Deserialize and find back Response. @@ -1933,7 +1933,7 @@ pub struct DeviceDeleted { pub path: String, } -/// trace-event-get-state +/// trace-get-state /// /// # Arguments /// @@ -1942,25 +1942,25 @@ pub struct DeviceDeleted { /// # Examples /// /// ```text -/// -> { "execute": "trace-event-get-state", +/// -> { "execute": "trace-get-state", /// "arguments": { "name": "event_name" } } /// <- { "return": [ { "name": "event_name", "state": "disabled" } ] } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] -pub struct trace_event_get_state { +pub struct trace_get_state { #[serde(rename = "name")] pub pattern: String, } -pub type TraceEventGetArgument = trace_event_get_state; +pub type TraceGetArgument = trace_get_state; #[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct TraceEventInfo { +pub struct TraceInfo { pub name: String, pub state: bool, } -/// trace-event-set-state +/// trace-set-state /// /// # Arguments /// @@ -1970,20 +1970,20 @@ pub struct TraceEventInfo { /// # Examples /// /// ```text -/// -> { "execute": "trace-event-set-state", +/// -> { "execute": "trace-set-state", /// "arguments": { "name": "event_name", /// "enable": true } } /// <- { "return": {} } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] -pub struct trace_event_set_state { +pub struct trace_set_state { #[serde(rename = "name")] pub pattern: String, #[serde(rename = "enable")] pub enable: bool, } -pub type TraceEventSetArgument = trace_event_set_state; +pub type TraceSetArgument = trace_set_state; #[cfg(test)] mod tests { diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index d7452a3e7..35ad238f4 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -501,12 +501,12 @@ fn qmp_command_exec( qmp_response = controller.lock().unwrap().getfd(arguments.fd_name, if_fd); id } - QmpCommand::trace_event_get_state { arguments, id } => { + QmpCommand::trace_get_state { arguments, id } => { match trace::get_state_by_pattern(arguments.pattern) { Ok(events) => { let mut ret = Vec::new(); for (name, state) in events { - ret.push(qmp_schema::TraceEventInfo { name, state }); + ret.push(qmp_schema::TraceInfo { name, state }); } qmp_response = Response::create_response(serde_json::to_value(ret).unwrap(), None); @@ -514,7 +514,7 @@ fn qmp_command_exec( Err(_) => { qmp_response = Response::create_error_response( qmp_schema::QmpErrorClass::GenericError( - "Failed to get trace event state".to_string(), + "Failed to get trace state".to_string(), ), None, ) @@ -522,11 +522,11 @@ fn qmp_command_exec( } id } - QmpCommand::trace_event_set_state { arguments, id } => { + QmpCommand::trace_set_state { arguments, id } => { if trace::set_state_by_pattern(arguments.pattern, arguments.enable).is_err() { qmp_response = Response::create_error_response( qmp_schema::QmpErrorClass::GenericError( - "Failed to set trace event state".to_string(), + "Failed to set trace state".to_string(), ), None, ) -- Gitee From 8d08cfb1c13deed14ce9e04837a8ab13fe531cbc Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Sun, 24 Mar 2024 18:12:44 +0800 Subject: [PATCH 1685/2187] Trace: Add trace scope and hitrace information to the document Signed-off-by: Gan Qixin --- docs/config_guidebook.md | 2 +- docs/qmp.md | 20 ++++++++--------- docs/trace.md | 47 +++++++++++++++++++++++++++------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index be0ab375d..7a3c87e1b 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1187,7 +1187,7 @@ One property can be set: * events: file lists events to trace. ```shell --trace events= +-trace file= ``` ## 4. Seccomp diff --git a/docs/qmp.md b/docs/qmp.md index c82b722f4..eae920e01 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -533,34 +533,34 @@ Query the display image of virtiogpu. Currently only stdvm and gtk supports. <- { "return": { "fileDir": "/tmp/stratovirt-images", "isSuccess": true } } ``` -### trace-event-get-state +### trace-get-state -Query whether the trace event is enabled. +Query whether the trace state is enabled. #### Arguments -* `name` : Pattern used to match event name. +* `name` : Pattern used to match trace name. #### Example ```json --> { "execute": "trace-event-get-state", "arguments": { "name": "event_name" } } -<- { "return": [ { "name": "event_name", "state": "disabled" } ] } +-> { "execute": "trace-get-state", "arguments": { "name": "trace_name" } } +<- { "return": [ { "name": "trace_name", "state": "disabled" } ] } ``` -### trace-event-set-state +### trace-set-state -Set the state of trace event. +Set the state of trace. #### Arguments -* `name` : Pattern used to match event name. -* `enable` : Whether to enable trace events. +* `name` : Pattern used to match trace name. +* `enable` : Whether to enable trace state. #### Example ```json --> { "execute": "trace-event-set-state", "arguments": { "name": "event_name","enable": true } } +-> { "execute": "trace-set-state", "arguments": { "name": "trace_name","enable": true } } <- { "return": {} } ``` diff --git a/docs/trace.md b/docs/trace.md index eec31e74a..bf1c24aae 100644 --- a/docs/trace.md +++ b/docs/trace.md @@ -3,13 +3,12 @@ This document describes the way for debugging and profiling in StratoVirt and how to use it. -## Add trace event +## Add trace ### Modify configuration file -First, you need to modify or crate toml file in the trace/trace_event directory to -add a new event in order to generate the trace function. Take virtio_receive_request -as an example: +First, you need to modify or crate toml file in the trace/trace_info directory to +add a new event or scope in order to generate the trace function. For example: ```toml [[events]] @@ -17,12 +16,17 @@ name = "virtio_receive_request" args = "device: String, behaviour: String" message = "{}: Request received from guest {}, ready to start processing." enabled = true + +[[scopes]] +name = "update_cursor" +args = "" +message = "" +enabled = true ``` -In the above configuration, "name" is used to represent the only trace event, -and duplication is not allowed; "message" and "args" will be formatted as -information output by trace; "enabled" indicates whether it is enabled during -compilation. +In the above configuration, "name" is used to represent the only trace, and +duplication is not allowed; "message" and "args" will be formatted as information +output by trace; "enabled" indicates whether it is enabled during compilation. ### Call trace function @@ -34,21 +38,27 @@ fn process_queue(&mut self) -> Result<()> { let mut need_interrupt = false; ...... } + +fn update_cursor(&mut self, info_cursor: &VirtioGpuUpdateCursor, hdr_type: u32) -> Result<()> { + // Trace starts from here, and end when it leaves this scope + trace::trace_scope_start!(update_cursor); + ...... +} ``` ## Trace control interface -Trace events in StratoVirt are disabled by default. Users can control whether -the trace event is enabled through the command line or qmp command. +Trace state in StratoVirt are disabled by default. Users can control whether +the trace state is enabled through the command line or qmp command. ### Command line -Before starting, you can prepare the trace event list that needs to be enabled +Before starting, you can prepare the trace list that needs to be enabled and pass it to StratoVirt through [-trace](config_guidebook.md#3-trace). ### QMP -During the running, you can send the [trace-event-set-state](qmp.md#trace-event-set-state) -command through the qmp socket to enable or disable trace event. Similarly, -using the [trace-event-get-state](qmp.md#trace-event-get-state) command can check +During the running, you can send the [trace-set-state](qmp.md#trace-set-state) +command through the qmp socket to enable or disable trace state. Similarly, +using the [trace-get-state](qmp.md#trace-get-state) command can check whether the setting is successful. ## Choose trace backends @@ -62,7 +72,7 @@ of settings. StratoVirt supports outputting trace to the log file at trace level. Turn on the **trace_to_logger** feature to use is. -### ftrace +### Ftrace Ftrace is a tracer provided by Linux kernel, which can help linux developers to debug or analyze issues. As ftrace can avoid performance penalty, it's especially @@ -72,3 +82,10 @@ It can be enabled by turning on the **trace_to_ftrace** feature during compilati StratoVirt use ftrace by writing trace data to ftrace marker, and developers can read trace records from trace file under mounted ftrace director, e.g. /sys/kernel/debug/tracing/trace. + +### HiTraceMeter + +HiTraceMeter(https://gitee.com/openharmony/hiviewdfx_hitrace) is tool used by developers +to trace process and measure performance. Based on the Ftrace, it provides the ability +to measure the execution time of user-mode application code. After turning on the +**trace_to_hitrace** feature, it can be used on HarmonyOS. -- Gitee From 2f152ab3b647d52e28ab4248b2599ba79a751a74 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 25 Mar 2024 15:36:26 +0800 Subject: [PATCH 1686/2187] ohcam: dynamically obtain camera APIs during rumtime stage To avoid the compiling error linking libhwf_adapter.so, let's load the library at runtime stage. The patch introduces hwf_adapter module under util/ohos_bindings. Later it can be also used for QoS feature. Signed-off-by: Zhao Yi Min --- Cargo.lock | 1 + devices/Cargo.toml | 2 +- devices/src/camera_backend/ohos/mod.rs | 1 - .../src/camera_backend/ohos/ohcam_bindings.rs | 53 ----- devices/src/camera_backend/ohos/ohcam_rapi.rs | 38 +-- util/Cargo.toml | 2 + util/src/ohos_binding/hwf_adapter/camera.rs | 84 +++++++ util/src/ohos_binding/hwf_adapter/mod.rs | 224 ++++++++++++++++++ util/src/ohos_binding/mod.rs | 2 + 9 files changed, 333 insertions(+), 74 deletions(-) delete mode 100755 devices/src/camera_backend/ohos/ohcam_bindings.rs create mode 100644 util/src/ohos_binding/hwf_adapter/camera.rs create mode 100644 util/src/ohos_binding/hwf_adapter/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 478857db9..aa9c10067 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1807,6 +1807,7 @@ dependencies = [ "io-uring", "kvm-bindings", "libc", + "libloading", "log", "nix 0.26.2", "once_cell", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 25529536a..ff64dc45b 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -49,5 +49,5 @@ demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] usb_camera = ["machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "dep:cairo-rs", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] -usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh"] +usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh", "util/usb_camera_oh"] ramfb = ["ui/console", "util/pixman"] diff --git a/devices/src/camera_backend/ohos/mod.rs b/devices/src/camera_backend/ohos/mod.rs index e21270a73..d62d8d3eb 100755 --- a/devices/src/camera_backend/ohos/mod.rs +++ b/devices/src/camera_backend/ohos/mod.rs @@ -12,5 +12,4 @@ pub mod ohcam; -mod ohcam_bindings; mod ohcam_rapi; diff --git a/devices/src/camera_backend/ohos/ohcam_bindings.rs b/devices/src/camera_backend/ohos/ohcam_bindings.rs deleted file mode 100755 index a985d67fd..000000000 --- a/devices/src/camera_backend/ohos/ohcam_bindings.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::os::raw::{c_int, c_void}; - -// SAFETY: The safety of this function is guaranteed by caller. -pub type BufferProcess = unsafe extern "C" fn(src_buffer: u64, length: i32); -// SAFETY: The safety of this function is guaranteed by caller. -pub type BrokenProcess = unsafe extern "C" fn(); - -#[derive(Default)] -pub struct ProfileRecorder { - pub fmt: i32, - pub width: i32, - pub height: i32, - pub fps: i32, -} - -#[link(name = "hwf_adapter")] -extern "C" { - pub fn OhcamCreateCtx() -> *mut c_void; - pub fn OhcamCreateSession(ctx: *mut c_void) -> c_int; - pub fn OhcamReleaseSession(ctx: *mut c_void); - pub fn OhcamInitCameras(ctx: *mut c_void) -> c_int; - pub fn OhcamInitProfiles(ctx: *mut c_void) -> c_int; - pub fn OhcamGetProfileSize(ctx: *mut c_void, idx: c_int) -> c_int; - pub fn OhcamGetProfile( - ctx: *mut c_void, - cam_idx: c_int, - profile_idx: c_int, - profile: *mut c_void, - ) -> c_int; - pub fn OhcamSetProfile(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> c_int; - pub fn OhcamPreStart( - ctx: *mut c_void, - buffer_proc: BufferProcess, - broken_process: BrokenProcess, - ) -> c_int; - pub fn OhcamStart(ctx: *mut c_void) -> c_int; - pub fn OhcamStopOutput(ctx: *mut c_void); - pub fn OhcamRelease(ctx: *mut c_void); - pub fn OhcamDestroyCtx(ctx: *mut *mut c_void); - pub fn OhcamAllowNextFrame(ctx: *mut c_void); -} diff --git a/devices/src/camera_backend/ohos/ohcam_rapi.rs b/devices/src/camera_backend/ohos/ohcam_rapi.rs index 37a5a5069..6154bce58 100755 --- a/devices/src/camera_backend/ohos/ohcam_rapi.rs +++ b/devices/src/camera_backend/ohos/ohcam_rapi.rs @@ -15,7 +15,7 @@ use std::ptr; use anyhow::{bail, Result}; -use super::ohcam_bindings as capi; +use util::ohos_binding::hwf_adapter as capi; // OH camera framework's related definitions #[allow(unused)] @@ -28,9 +28,9 @@ pub const CAMERA_FORMAT_MJPEG: i32 = 2000; // camera path is actually the specified camera ID. pub fn check_cam_idx(idx: u8) -> Result<()> { // SAFETY: We call related API sequentially for specified ctx. - let mut ctx = unsafe { capi::OhcamCreateCtx() }; + let mut ctx = unsafe { capi::ohcam_create_ctx() }; // SAFETY: We call related API sequentially for specified ctx. - let n = unsafe { capi::OhcamInitCameras(ctx) }; + let n = unsafe { capi::ohcam_init_cameras(ctx) }; if n < 0 { ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); bail!("OHCAM WRAPPER: failed to init cameras"); @@ -44,17 +44,17 @@ pub fn check_cam_idx(idx: u8) -> Result<()> { pub fn ohcam_init() -> Result<*mut c_void> { // SAFETY: We call related API sequentially for specified ctx. - let mut ctx = unsafe { capi::OhcamCreateCtx() }; + let mut ctx = unsafe { capi::ohcam_create_ctx() }; if ctx.is_null() { bail!("OHCAM WRAPPER: create camera ctx failed"); } // SAFETY: We call related API sequentially for specified ctx. unsafe { - if capi::OhcamInitCameras(ctx) < 0 { + if capi::ohcam_init_cameras(ctx) < 0 { ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); bail!("OHCAM WRAPPER: failed to init cameras"); } - if capi::OhcamInitProfiles(ctx) < 0 { + if capi::ohcam_init_profiles(ctx) < 0 { ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); bail!("OHCAM WRAPPER: failed to init profiles"); } @@ -64,7 +64,7 @@ pub fn ohcam_init() -> Result<*mut c_void> { pub fn ohcam_get_fmt_nums(ctx: *mut c_void, idx: c_int) -> Result { // SAFETY: We call related API sequentially for specified ctx. - let ret = unsafe { capi::OhcamGetProfileSize(ctx, idx) }; + let ret = unsafe { capi::ohcam_get_profile_size(ctx, idx) }; if ret < 0 { bail!("OHCAM WRAPPER: invalid camera idx {}", idx); } @@ -73,17 +73,17 @@ pub fn ohcam_get_fmt_nums(ctx: *mut c_void, idx: c_int) -> Result { pub fn ohcam_release_camera(ctx: *mut c_void) { // SAFETY: We call related API sequentially for specified ctx. - unsafe { capi::OhcamRelease(ctx) }; + unsafe { capi::ohcam_release(ctx) }; } pub fn ohcam_drop_ctx(p_ctx: *mut *mut c_void) { // SAFETY: We call related API sequentially for specified ctx. - unsafe { capi::OhcamDestroyCtx(p_ctx) }; + unsafe { capi::ohcma_destroy_ctx(p_ctx) }; } pub fn ohcam_set_fmt(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> Result<()> { // SAFETY: We call related API sequentially for specified ctx. - let ret = unsafe { capi::OhcamSetProfile(ctx, cam_idx, profile_idx) }; + let ret = unsafe { capi::ohcam_set_profile(ctx, cam_idx, profile_idx) }; if ret < 0 { bail!("OHCAM WRAPPER: Failed to ohcam_set_profile"); } @@ -97,10 +97,10 @@ pub fn ohcam_start_stream( ) -> Result<()> { // SAFETY: We call related API sequentially for specified ctx. unsafe { - if capi::OhcamPreStart(ctx, buffer_proc, broken_process) != 0 { + if capi::ohcam_pre_start(ctx, buffer_proc, broken_process) != 0 { bail!("OHCAM WRAPPER: Pre start failed"); } - if capi::OhcamStart(ctx) != 0 { + if capi::ohcam_start(ctx) != 0 { bail!("OHCAM WRAPPER: Start failed"); } } @@ -110,17 +110,17 @@ pub fn ohcam_start_stream( pub fn ohcam_reset_cam(ctx: *mut c_void) { // SAFETY: We call related API sequentially for specified ctx. unsafe { - capi::OhcamCreateSession(ctx); - capi::OhcamInitCameras(ctx); - capi::OhcamInitProfiles(ctx); + capi::ohcam_create_session(ctx); + capi::ohcam_init_cameras(ctx); + capi::ohcam_init_profiles(ctx); } } pub fn ohcam_stop_stream(ctx: *mut c_void) { // SAFETY: We call related API sequentially for specified ctx. unsafe { - capi::OhcamStopOutput(ctx); - capi::OhcamReleaseSession(ctx); + capi::ohcam_stop_output(ctx); + capi::ohcam_release_session(ctx); } } @@ -136,7 +136,7 @@ pub fn ohcam_get_profile( let profile_recorder = capi::ProfileRecorder::default(); // SAFETY: We call related API sequentially for specified ctx. unsafe { - let ret = capi::OhcamGetProfile( + let ret = capi::ohcam_get_profile( ctx, cam_idx, profile_idx, @@ -156,6 +156,6 @@ pub fn ohcam_get_profile( pub fn ohcam_next_frame(ctx: *mut c_void) { // SAFETY: We call related API sequentially for specified ctx. unsafe { - capi::OhcamAllowNextFrame(ctx); + capi::ohcam_allow_next_frame(ctx); } } diff --git a/util/Cargo.toml b/util/Cargo.toml index d71bf5c30..5813d12d6 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -14,6 +14,7 @@ anyhow = "1.0" kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["poll", "term", "time", "signal", "fs", "feature"] } libc = "0.2" +libloading = "0.7.4" log = { version = "0.4", features = ["std"]} vmm-sys-util = "0.11.1" byteorder = "1.4.3" @@ -26,4 +27,5 @@ trace = {path = "../trace"} [features] default = [] usb_camera_v4l2 = ["dep:v4l2-sys-mit"] +usb_camera_oh = [] pixman = [] diff --git a/util/src/ohos_binding/hwf_adapter/camera.rs b/util/src/ohos_binding/hwf_adapter/camera.rs new file mode 100644 index 000000000..eb9aa359c --- /dev/null +++ b/util/src/ohos_binding/hwf_adapter/camera.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::{c_int, c_void}; + +use anyhow::{Context, Result}; +use libloading::os::unix::Symbol as RawSymbol; +use libloading::Library; + +use crate::get_libfn; + +#[repr(C)] +#[derive(Default)] +pub struct ProfileRecorder { + pub fmt: i32, + pub width: i32, + pub height: i32, + pub fps: i32, +} + +pub type BufferProcessFn = unsafe extern "C" fn(src_buffer: u64, length: i32); +pub type BrokenProcessFn = unsafe extern "C" fn(); + +type OhcamCreateCtxFn = unsafe extern "C" fn() -> *mut c_void; +type OhcamCreateSessionFn = unsafe extern "C" fn(*mut c_void) -> c_int; +type OhcamReleaseSessionFn = unsafe extern "C" fn(*mut c_void); +type OhcamInitCamerasFn = unsafe extern "C" fn(*mut c_void) -> c_int; +type OhcamInitProfilesFn = unsafe extern "C" fn(*mut c_void) -> c_int; +type OhcamGetProfileSizeFn = unsafe extern "C" fn(*mut c_void, c_int) -> c_int; +type OhcamGetProfileFn = unsafe extern "C" fn(*mut c_void, c_int, c_int, *mut c_void) -> c_int; +type OhcamSetProfileFn = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> c_int; +type OhcamPreStartFn = unsafe extern "C" fn(*mut c_void, BufferProcessFn, BrokenProcessFn) -> c_int; +type OhcamStartFn = unsafe extern "C" fn(*mut c_void) -> c_int; +type OhcamStopOutputFn = unsafe extern "C" fn(*mut c_void); +type OhcamReleaseFn = unsafe extern "C" fn(*mut c_void); +type OhcamDestroyCtxFn = unsafe extern "C" fn(*mut *mut c_void); +type OhcamAllowNextFrameFn = unsafe extern "C" fn(*mut c_void); + +pub struct CamFuncTable { + pub create_ctx: RawSymbol, + pub create_session: RawSymbol, + pub release_session: RawSymbol, + pub init_cameras: RawSymbol, + pub init_profiles: RawSymbol, + pub get_profile_size: RawSymbol, + pub get_profile: RawSymbol, + pub set_profile: RawSymbol, + pub pre_start: RawSymbol, + pub start: RawSymbol, + pub stop_output: RawSymbol, + pub release: RawSymbol, + pub destroy_ctx: RawSymbol, + pub allow_next_frame: RawSymbol, +} + +impl CamFuncTable { + pub unsafe fn new(library: &Library) -> Result { + Ok(Self { + create_ctx: get_libfn!(library, OhcamCreateCtxFn, OhcamCreateCtx), + create_session: get_libfn!(library, OhcamCreateSessionFn, OhcamCreateSession), + release_session: get_libfn!(library, OhcamReleaseSessionFn, OhcamReleaseSession), + init_cameras: get_libfn!(library, OhcamInitCamerasFn, OhcamInitCameras), + init_profiles: get_libfn!(library, OhcamInitProfilesFn, OhcamInitProfiles), + get_profile_size: get_libfn!(library, OhcamGetProfileSizeFn, OhcamGetProfileSize), + get_profile: get_libfn!(library, OhcamGetProfileFn, OhcamGetProfile), + set_profile: get_libfn!(library, OhcamSetProfileFn, OhcamSetProfile), + pre_start: get_libfn!(library, OhcamPreStartFn, OhcamPreStart), + start: get_libfn!(library, OhcamStartFn, OhcamStart), + stop_output: get_libfn!(library, OhcamStopOutputFn, OhcamStopOutput), + release: get_libfn!(library, OhcamReleaseFn, OhcamRelease), + destroy_ctx: get_libfn!(library, OhcamDestroyCtxFn, OhcamDestroyCtx), + allow_next_frame: get_libfn!(library, OhcamAllowNextFrameFn, OhcamAllowNextFrame), + }) + } +} diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs new file mode 100644 index 000000000..69b17f885 --- /dev/null +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -0,0 +1,224 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +mod camera; + +use std::ffi::OsStr; +use std::os::raw::{c_int, c_void}; +use std::sync::RwLock; + +use anyhow::{Context, Result}; +use libloading::Library; +use log::error; +use once_cell::sync::Lazy; + +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +use camera::CamFuncTable; + +static LIB_HWF_ADAPTER: Lazy> = Lazy::new(|| + // SAFETY: The dynamic library should be always existing. + unsafe { + RwLock::new( + LibHwfAdapter::new(OsStr::new("/system/lib64/libhwf_adapter.so")) + .map_err(|e| { + error!("failed to init LibHwfAdapter with error: {:?}", e); + e + }) + .unwrap() + ) + }); + +struct LibHwfAdapter { + #[allow(unused)] + library: Library, + #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + camera: CamFuncTable, +} + +impl LibHwfAdapter { + unsafe fn new(library_name: &OsStr) -> Result { + let library = + Library::new(library_name).with_context(|| "failed to load hwf_adapter library")?; + + #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + let camera = + CamFuncTable::new(&library).with_context(|| "failed to init camera function table")?; + + Ok(Self { + library, + #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + camera, + }) + } +} + +#[macro_export] +macro_rules! get_libfn { + ( $lib: ident, $tname: ident, $fname: ident ) => { + $lib.get::<$tname>(stringify!($fname).as_bytes()) + .with_context(|| { + format!( + "failed to get function {} from libhwf_adapter", + stringify!($fname) + ) + })? + .into_raw() + }; +} + +macro_rules! hwf_call { + ( $klass: ident, $fname: ident ( $($x: expr),* ) ) => { + (LIB_HWF_ADAPTER.read().unwrap().$klass.$fname)( $($x),* ) + }; +} + +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +macro_rules! hwf_camera_call { + ( $fname: ident ( $($x: expr),* ) ) => { + hwf_call!(camera, $fname( $($x),* )) + }; +} + +/// Camera APIs in libhwf_adapter.so + +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub type ProfileRecorder = camera::ProfileRecorder; +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub type BufferProcess = camera::BufferProcessFn; +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub type BrokenProcess = camera::BrokenProcessFn; + +/// # Safety +/// +/// The caller must save returned value for later use. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_create_ctx() -> *mut c_void { + hwf_camera_call!(create_ctx()) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_create_session(ctx: *mut c_void) -> c_int { + hwf_camera_call!(create_session(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_release_session(ctx: *mut c_void) { + hwf_camera_call!(release_session(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_init_cameras(ctx: *mut c_void) -> c_int { + hwf_camera_call!(init_cameras(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_init_profiles(ctx: *mut c_void) -> c_int { + hwf_camera_call!(init_profiles(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function +/// and valid profile index. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_get_profile_size(ctx: *mut c_void, idx: c_int) -> c_int { + hwf_camera_call!(get_profile_size(ctx, idx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function, +/// valid profile pointer and valid index of camera and profile. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_get_profile( + ctx: *mut c_void, + cam_idx: c_int, + profile_idx: c_int, + profile: *mut c_void, +) -> c_int { + hwf_camera_call!(get_profile(ctx, cam_idx, profile_idx, profile)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function +/// and valid index of camera and profile. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_set_profile(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> c_int { + hwf_camera_call!(set_profile(ctx, cam_idx, profile_idx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function +/// and take care about the logic of process callbacks. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_pre_start( + ctx: *mut c_void, + buffer_proc: BufferProcess, + broken_proc: BrokenProcess, +) -> c_int { + hwf_camera_call!(pre_start(ctx, buffer_proc, broken_proc)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_start(ctx: *mut c_void) -> c_int { + hwf_camera_call!(start(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_stop_output(ctx: *mut c_void) { + hwf_camera_call!(stop_output(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_release(ctx: *mut c_void) { + hwf_camera_call!(release(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcma_destroy_ctx(ctx: *mut *mut c_void) { + hwf_camera_call!(destroy_ctx(ctx)) +} + +/// # Safety +/// +/// The caller must ensure the ctx is returned from OhcamCreateCtx function. +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub unsafe fn ohcam_allow_next_frame(ctx: *mut c_void) { + hwf_camera_call!(allow_next_frame(ctx)) +} diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs index ea0e76b39..c45f65d0c 100644 --- a/util/src/ohos_binding/mod.rs +++ b/util/src/ohos_binding/mod.rs @@ -10,4 +10,6 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(all(target_env = "ohos", any(feature = "usb_camera_oh")))] +pub mod hwf_adapter; pub mod misc; -- Gitee From c5f818079c75a6d7568f100bac594275bb5ec32b Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Tue, 26 Mar 2024 02:29:28 +0800 Subject: [PATCH 1687/2187] Trace: Fix compilation error when enabled=false Signed-off-by: Gan Qixin --- trace/trace_generator/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/trace/trace_generator/src/lib.rs b/trace/trace_generator/src/lib.rs index 008263cc0..4d1a80a48 100644 --- a/trace/trace_generator/src/lib.rs +++ b/trace/trace_generator/src/lib.rs @@ -216,6 +216,11 @@ pub fn gen_trace_scope_func(_input: TokenStream) -> TokenStream { } }; + let func_decl = match desc.enabled { + true => quote!(pub fn #func_name(asyn: bool, #func_args) -> trace_scope::Scope), + false => quote!(pub fn #func_name(asyn: bool, #func_args)), + }; + let message_args = match desc.args.is_empty() { true => quote!(), false => { @@ -258,7 +263,7 @@ pub fn gen_trace_scope_func(_input: TokenStream) -> TokenStream { all(target_env = "ohos", feature = "trace_to_hitrace") ))] #[inline(always)] - pub fn #func_name(asyn: bool, #func_args) -> trace_scope::Scope { + #func_decl { #func_body } -- Gitee From 23d8df550eac6ffde88586f7368b9522f5f8ef39 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 25 Mar 2024 17:00:17 +0800 Subject: [PATCH 1688/2187] Scream: add microphone control interfaces support qmp-command&start-up-params for users to control if microphone record can be used. Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 30 ++++++++++++++++++++++++--- docs/qmp.md | 15 ++++++++++++++ machine/src/standard_common/mod.rs | 18 ++++++++++++++++ machine_manager/src/machine.rs | 8 +++++++ machine_manager/src/qmp/qmp_schema.rs | 23 ++++++++++++++++++++ machine_manager/src/qmp/qmp_socket.rs | 1 + 6 files changed, 92 insertions(+), 3 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 2cb54366c..93fcb5919 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -29,7 +29,7 @@ use std::{ }; use anyhow::{anyhow, bail, Context, Result}; -use clap::Parser; +use clap::{ArgAction, Parser}; use core::time; use log::{error, warn}; @@ -39,7 +39,7 @@ use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; -use machine_manager::config::{get_pci_df, valid_id}; +use machine_manager::config::{get_pci_df, parse_bool, valid_id}; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohos::ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] @@ -88,6 +88,22 @@ pub struct ShmemStreamHeader { pub fmt: ShmemStreamFmt, } +fn record_authority_rw(auth: bool, write: bool) -> bool { + static AUTH: RwLock = RwLock::new(true); + if write { + *AUTH.write().unwrap() = auth; + } + *AUTH.read().unwrap() +} + +pub fn set_record_authority(auth: bool) { + record_authority_rw(auth, true); +} + +fn get_record_authority() -> bool { + record_authority_rw(false, false) +} + impl ShmemStreamHeader { pub fn check(&self, last_end: u64) -> bool { if (self.offset as u64) < last_end { @@ -323,7 +339,12 @@ impl StreamData { return; } - let recv_chunks_cnt = locked_interface.receive(self); + let recv_chunks_cnt: i32 = if get_record_authority() { + locked_interface.receive(self) + } else { + locked_interface.destroy(); + 0 + }; if recv_chunks_cnt > 0 { self.chunk_idx = (self.chunk_idx + recv_chunks_cnt as u16) % capt.max_chunks; @@ -391,6 +412,8 @@ pub struct ScreamConfig { playback: String, #[arg(long, default_value = "")] record: String, + #[arg(long, default_value = "on", action = ArgAction::Append, value_parser = parse_bool)] + record_auth: bool, } /// Scream sound card device structure. @@ -403,6 +426,7 @@ pub struct Scream { impl Scream { pub fn new(size: u64, config: ScreamConfig, token_id: Option>>) -> Self { + set_record_authority(config.record_auth); Self { hva: 0, size, diff --git a/docs/qmp.md b/docs/qmp.md index 4b0320cae..c82b722f4 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -577,6 +577,21 @@ Receive a file descriptor via SCM rights and assign it a name. <- { "return": {} } ``` +### switch-audio-record + +Control if the scream device can use host's microphone record. + +#### Arguments + +* `authorized` : "on" means scream can use host's microphone record, "off" opposites in meaning. + +#### Example + +```json +-> { "execute": "switch-audio-record", "arguments": { "authorized": "on" } } +<- { "return": {} } + ``` + ## Event Notification When some events happen, connected client will receive QMP events. diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 850fe06ed..147506e23 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -54,6 +54,8 @@ use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; #[cfg(target_arch = "x86_64")] use devices::acpi::cpu_controller::CpuController; use devices::legacy::FwCfgOps; +#[cfg(feature = "scream")] +use devices::misc::scream::set_record_authority; use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; use devices::pci::PciBus; #[cfg(feature = "usb_camera")] @@ -1436,6 +1438,22 @@ impl DeviceInterface for StdMachine { } } + #[cfg(feature = "scream")] + fn switch_audio_record(&self, authorized: String) -> Response { + match authorized.as_str() { + "on" => set_record_authority(true), + "off" => set_record_authority(false), + _ => { + let err_str = format!("Failed to set audio capture authority: {:?}", authorized); + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(err_str), + None, + ); + } + } + Response::create_empty_response() + } + fn getfd(&self, fd_name: String, if_fd: Option) -> Response { if let Some(fd) = if_fd { QmpChannel::set_fd(fd_name, fd); diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 472ca6286..2e79e58d9 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -197,6 +197,14 @@ pub trait DeviceInterface { ) } + /// Control OH audio's control authority. + fn switch_audio_record(&self, _authorized: String) -> Response { + Response::create_response( + serde_json::to_value("switch_audi_record not supported for VM".to_string()).unwrap(), + None, + ) + } + /// Receive a file descriptor via SCM rights and assign it a name. fn getfd(&self, fd_name: String, if_fd: Option) -> Response; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 6627ff7c0..37f37bb05 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -102,6 +102,7 @@ define_qmp_command_enum!( query_balloon("query-balloon", query_balloon, FALSE, default), query_vnc("query-vnc", query_vnc, TRUE, default), query_display_image("query-display-image", query_display_image, FALSE, default), + switch_audio_record("switch-audio-record", switch_audio_record, FALSE), migrate("migrate", migrate, FALSE), query_migrate("query-migrate", query_migrate, FALSE, default), cancel_migrate("migrate_cancel", cancel_migrate, FALSE, default), @@ -994,6 +995,28 @@ pub struct getfd { } generate_command_impl!(getfd, Empty); +/// switch_audio_record +/// +/// Control the authority of audio record +/// +/// # Arguments +/// +/// * `authorized` - on or off. +/// +/// # Examples +/// +/// ```text +/// -> { "execute": "switch_audio_record", "arguments": { "authorized": "on" } } +/// <- { "return": {} } +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct switch_audio_record { + #[serde(rename = "authorized")] + pub authorized: String, +} +generate_command_impl!(switch_audio_record, Empty); + /// query-balloon /// /// Query the actual size of memory of VM. diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 53b9944f5..d7452a3e7 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -469,6 +469,7 @@ fn qmp_command_exec( (input_event, input_event, key, value), (device_list_properties, device_list_properties, typename), (device_del, device_del, id), + (switch_audio_record, switch_audio_record, authorized), (blockdev_del, blockdev_del, node_name), (netdev_del, netdev_del, id), (chardev_remove, chardev_remove, id), -- Gitee From 6ec513547dc65fb1c013db18920a0e35eabefbc0 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 27 Mar 2024 00:55:23 +0800 Subject: [PATCH 1689/2187] Trace: Load libhitrace_meter.so dynamically Signed-off-by: Gan Qixin --- Cargo.lock | 1 + trace/Cargo.toml | 1 + trace/src/hitrace.rs | 85 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 77 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc1a59a9a..90552e41b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,6 +1736,7 @@ version = "2.4.0" dependencies = [ "anyhow", "lazy_static", + "libloading", "log", "regex", "trace_generator", diff --git a/trace/Cargo.toml b/trace/Cargo.toml index ad6cf1bcf..4fd31409d 100644 --- a/trace/Cargo.toml +++ b/trace/Cargo.toml @@ -13,6 +13,7 @@ regex = "1" anyhow = "1.0" trace_generator = { path = "trace_generator" } vmm-sys-util = "0.12.1" +libloading = "0.7.4" [features] trace_to_logger = [] diff --git a/trace/src/hitrace.rs b/trace/src/hitrace.rs index 1cff1cd7c..625ea6209 100644 --- a/trace/src/hitrace.rs +++ b/trace/src/hitrace.rs @@ -10,27 +10,84 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::ffi::OsStr; + +use anyhow::{Context, Result}; +use lazy_static::lazy_static; +use libloading::os::unix::Symbol; +use libloading::Library; +use log::error; + const HITRACE_TAG_VIRSE: u64 = 1u64 << 11; -#[link(name = "hitrace_meter")] -extern "C" { - fn StartTraceWrapper(label: u64, value: *const u8); - fn FinishTrace(label: u64); - fn StartAsyncTraceWrapper(label: u64, value: *const u8, taskId: i32); - fn FinishAsyncTraceWrapper(label: u64, value: *const u8, taskId: i32); +lazy_static! { + static ref HITRACE_FUNC_TABLE: HitraceFuncTable = + // SAFETY: The dynamic library should be always existing. + unsafe { + HitraceFuncTable::new(OsStr::new("libhitrace_meter.so")) + .map_err(|e| { + error!("failed to init HitraceFuncTable with error: {:?}", e); + e + }) + .unwrap() + }; +} + +macro_rules! get_libfn { + ( $lib: ident, $tname: ident, $fname: ident ) => { + $lib.get::<$tname>(stringify!($fname).as_bytes()) + .with_context(|| format!("failed to get function {}", stringify!($fname)))? + .into_raw() + }; +} + +type StartTraceWrapperFn = unsafe extern "C" fn(u64, *const u8); +type FinishTraceFn = unsafe extern "C" fn(u64); +type StartAsyncTraceWrapperFn = unsafe extern "C" fn(u64, *const u8, i32); +type FinishAsyncTraceWrapperFn = unsafe extern "C" fn(u64, *const u8, i32); + +struct HitraceFuncTable { + pub start_trace: Symbol, + pub finish_trace: Symbol, + pub start_trace_async: Symbol, + pub finish_trace_async: Symbol, +} + +impl HitraceFuncTable { + unsafe fn new(library_name: &OsStr) -> Result { + let library = + Library::new(library_name).with_context(|| "failed to load hitrace_meter library")?; + + Ok(Self { + start_trace: get_libfn!(library, StartTraceWrapperFn, StartTraceWrapper), + finish_trace: get_libfn!(library, FinishTraceFn, FinishTrace), + start_trace_async: get_libfn!( + library, + StartAsyncTraceWrapperFn, + StartAsyncTraceWrapper + ), + finish_trace_async: get_libfn!( + library, + FinishAsyncTraceWrapperFn, + FinishAsyncTraceWrapper + ), + }) + } } pub fn start_trace(value: &str) { if let Ok(value_ptr) = std::ffi::CString::new(value) { // SAFETY: All parameters have been checked. - unsafe { StartTraceWrapper(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8) } + unsafe { + (HITRACE_FUNC_TABLE.start_trace)(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8) + } } } pub fn finish_trace() { // SAFETY: All parameters have been checked. unsafe { - FinishTrace(HITRACE_TAG_VIRSE); + (HITRACE_FUNC_TABLE.finish_trace)(HITRACE_TAG_VIRSE); } } @@ -38,7 +95,11 @@ pub fn start_trace_async(value: &str, task_id: i32) { if let Ok(value_ptr) = std::ffi::CString::new(value) { // SAFETY: All parameters have been checked. unsafe { - StartAsyncTraceWrapper(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8, task_id) + (HITRACE_FUNC_TABLE.start_trace_async)( + HITRACE_TAG_VIRSE, + value_ptr.as_ptr() as *const u8, + task_id, + ) } } } @@ -47,7 +108,11 @@ pub fn finish_trace_async(value: &str, task_id: i32) { if let Ok(value_ptr) = std::ffi::CString::new(value) { // SAFETY: All parameters have been checked. unsafe { - FinishAsyncTraceWrapper(HITRACE_TAG_VIRSE, value_ptr.as_ptr() as *const u8, task_id) + (HITRACE_FUNC_TABLE.finish_trace_async)( + HITRACE_TAG_VIRSE, + value_ptr.as_ptr() as *const u8, + task_id, + ) } } } -- Gitee From 9d05ca8725815fa4fd462f136e0a779ca9cda7d2 Mon Sep 17 00:00:00 2001 From: Gan Qixin Date: Wed, 27 Mar 2024 03:27:09 +0800 Subject: [PATCH 1690/2187] Trace: Add new option to enable an certain type of trace Signed-off-by: Gan Qixin --- docs/config_guidebook.md | 7 +++-- machine_manager/src/cmdline.rs | 4 +-- machine_manager/src/config/mod.rs | 46 ++++++++++++++++++++++++++++--- trace/src/lib.rs | 33 +++++++++++++++++++++- trace/trace_generator/src/lib.rs | 10 +++---- 5 files changed, 85 insertions(+), 15 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 16195406b..97665ec78 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1181,14 +1181,15 @@ Please see the [4. Build with features](docs/build_guide.md) if you want to enab ## 3. Trace -Users can specify the configuration file which lists events to trace. +Users can specify a configuration file which lists the traces that needs to be enabled, or specify the trace type that needs to be enabled. Setting both file and type is also allowed, so that traces with the specified type and traces listed in the file will all be enabled. One property can be set: -* events: file lists events to trace. +* file: specify the file containing the traces that needs to be enabled. +* type: specify the traces type that needs to be enabled. ```shell --trace file= +-trace file=|type= ``` ## 4. Seccomp diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 619a7c2b1..fc7304b9c 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -447,8 +447,8 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { Arg::with_name("trace") .multiple(false) .long("trace") - .value_name("file=") - .help("specify the file lists trace state to enable") + .value_name("file=|type=") + .help("specify the trace state to enable") .takes_value(true), ) .arg( diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ca1281a6d..3fbc58184 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -70,7 +70,7 @@ use clap::Parser; use log::error; use serde::{Deserialize, Serialize}; -use trace::set_state_by_pattern; +use trace::{enable_state_by_type, set_state_by_pattern, TraceType}; #[cfg(target_arch = "aarch64")] use util::device_tree::{self, FdtBuilder}; use util::{ @@ -447,7 +447,7 @@ pub fn parse_bool(s: &str) -> Result { } } -fn enable_trace_state(path: &str) -> Result<()> { +fn enable_trace_state_from_file(path: &str) -> Result<()> { let mut file = File::open(path).with_context(|| format!("Failed to open {}", path))?; let mut buf = String::new(); file.read_to_string(&mut buf) @@ -466,16 +466,41 @@ fn enable_trace_state(path: &str) -> Result<()> { Ok(()) } +fn enable_trace_state_from_type(type_str: &str) -> Result<()> { + match type_str { + "events" => enable_state_by_type(TraceType::Event)?, + "scopes" => enable_state_by_type(TraceType::Scope)?, + "all" => { + enable_state_by_type(TraceType::Event)?; + enable_state_by_type(TraceType::Scope)?; + } + _ => bail!("Unknown trace type {}", type_str), + }; + + Ok(()) +} + #[derive(Parser)] #[command(no_binary_name(true))] struct TraceConfig { #[arg(long)] - file: String, + file: Option, + #[arg(long, alias = "type")] + type_str: Option, } pub fn add_trace(opt: &str) -> Result<()> { let trace_cfg = TraceConfig::try_parse_from(str_slip_to_clap(opt, false, false))?; - enable_trace_state(&trace_cfg.file)?; + if trace_cfg.type_str.is_none() && trace_cfg.file.is_none() { + bail!("No type or file after -trace"); + } + + if let Some(type_str) = trace_cfg.type_str { + enable_trace_state_from_type(&type_str)?; + } + if let Some(file) = trace_cfg.file { + enable_trace_state_from_file(&file)?; + } Ok(()) } @@ -747,9 +772,22 @@ mod tests { #[test] fn test_add_trace() { + assert!(std::fs::File::create("/tmp/trace_file").is_ok()); + + assert!(add_trace("file=/tmp/trace_file,type=all").is_ok()); assert!(add_trace("fil=test_trace").is_err()); assert!(add_trace("file").is_err()); assert!(add_trace("file=test_trace").is_err()); + + assert!(add_trace("type=events").is_ok()); + assert!(add_trace("type=scopes").is_ok()); + assert!(add_trace("type=all").is_ok()); + assert!(add_trace("type=xxxxx").is_err()); + + assert!(add_trace("").is_err()); + assert!(add_trace("file=/tmp/trace_file,type=all").is_ok()); + + assert!(std::fs::remove_file("/tmp/trace_file").is_ok()); } #[test] diff --git a/trace/src/lib.rs b/trace/src/lib.rs index 32646d2f3..ba8aa2ab1 100644 --- a/trace/src/lib.rs +++ b/trace/src/lib.rs @@ -30,6 +30,7 @@ use std::{ use anyhow::{Ok, Result}; use lazy_static::lazy_static; +use log::warn; use regex::Regex; use vmm_sys_util::eventfd::EventFd; @@ -37,16 +38,33 @@ use trace_generator::{ add_trace_state_to, gen_trace_event_func, gen_trace_scope_func, gen_trace_state, }; +#[derive(PartialEq, Eq)] +pub enum TraceType { + Event, + Scope, + Unknown, +} + struct TraceState { name: String, + trace_type: TraceType, get_state: fn() -> bool, set_state: fn(bool), } impl TraceState { - fn new(name: String, get_state: fn() -> bool, set_state: fn(bool)) -> Self { + fn new(name: String, type_str: &str, get_state: fn() -> bool, set_state: fn(bool)) -> Self { + let trace_type = match type_str { + "event" => TraceType::Event, + "scope" => TraceType::Scope, + _ => { + warn!("The type of {} is Unknown: {}", name, type_str); + TraceType::Unknown + } + }; TraceState { name, + trace_type, get_state, set_state, } @@ -73,6 +91,15 @@ impl TraceStateSet { Ok(()) } + fn enable_state_by_type(&self, trace_type: TraceType) -> Result<()> { + for state in &self.state_list { + if state.trace_type == trace_type { + (state.set_state)(true); + } + } + Ok(()) + } + fn get_state_by_pattern(&self, pattern: String) -> Result> { let re = Regex::new(&pattern)?; let mut ret: Vec<(String, bool)> = Vec::new(); @@ -126,3 +153,7 @@ pub fn get_state_by_pattern(pattern: String) -> Result> { pub fn set_state_by_pattern(pattern: String, state: bool) -> Result<()> { TRACE_STATE_SET.set_state_by_pattern(pattern, state) } + +pub fn enable_state_by_type(trace_type: TraceType) -> Result<()> { + TRACE_STATE_SET.enable_state_by_type(trace_type) +} diff --git a/trace/trace_generator/src/lib.rs b/trace/trace_generator/src/lib.rs index 4d1a80a48..ce6ce1261 100644 --- a/trace/trace_generator/src/lib.rs +++ b/trace/trace_generator/src/lib.rs @@ -56,24 +56,24 @@ fn get_trace_desc() -> TraceConf { #[proc_macro] pub fn add_trace_state_to(input: TokenStream) -> TokenStream { let trace_conf = get_trace_desc(); - let mut state_name = Vec::new(); + let mut state = Vec::new(); for desc in trace_conf.events.unwrap_or_default() { if desc.enabled { - state_name.push(desc.name.trim().to_string()); + state.push((desc.name.trim().to_string(), "event")); } } for desc in trace_conf.scopes.unwrap_or_default() { if desc.enabled { - state_name.push(desc.name.trim().to_string()); + state.push((desc.name.trim().to_string(), "scope")); } } let set = parse_macro_input!(input as Ident); - let init_code = state_name.iter().map(|name| { + let init_code = state.iter().map(|(name, type_str)| { let get_func = parse_str::(format!("get_{}_state", name).as_str()).unwrap(); let set_func = parse_str::(format!("set_{}_state", name).as_str()).unwrap(); quote!( - #set.add_trace_state(TraceState::new(#name.to_string(), #get_func, #set_func)); + #set.add_trace_state(TraceState::new(#name.to_string(), #type_str, #get_func, #set_func)); ) }); TokenStream::from(quote! { #( #init_code )* }) -- Gitee From e9480ee58602460d4ed9feb40579d101ffe76959 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 26 Mar 2024 21:12:07 +0800 Subject: [PATCH 1691/2187] ohcam: introduce safe APIs for OH camera The commit introduces safe OH camera APIs to ohos_bindings. Signed-off-by: Zhao Yi Min --- devices/src/camera_backend/ohos/mod.rs | 2 - devices/src/camera_backend/ohos/ohcam.rs | 89 ++++----- devices/src/camera_backend/ohos/ohcam_rapi.rs | 161 ----------------- util/src/ohos_binding/camera.rs | 165 +++++++++++++++++ util/src/ohos_binding/hwf_adapter/camera.rs | 35 ++-- util/src/ohos_binding/hwf_adapter/mod.rs | 169 ++---------------- util/src/ohos_binding/mod.rs | 5 +- 7 files changed, 242 insertions(+), 384 deletions(-) delete mode 100755 devices/src/camera_backend/ohos/ohcam_rapi.rs create mode 100644 util/src/ohos_binding/camera.rs diff --git a/devices/src/camera_backend/ohos/mod.rs b/devices/src/camera_backend/ohos/mod.rs index d62d8d3eb..ccb09f3b5 100755 --- a/devices/src/camera_backend/ohos/mod.rs +++ b/devices/src/camera_backend/ohos/mod.rs @@ -11,5 +11,3 @@ // See the Mulan PSL v2 for more details. pub mod ohcam; - -mod ohcam_rapi; diff --git a/devices/src/camera_backend/ohos/ohcam.rs b/devices/src/camera_backend/ohos/ohcam.rs index 05b80e65d..f27abb1b2 100755 --- a/devices/src/camera_backend/ohos/ohcam.rs +++ b/devices/src/camera_backend/ohos/ohcam.rs @@ -10,18 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::raw::{c_int, c_void}; use std::sync::RwLock; use anyhow::{bail, Context, Result}; +use log::error; use once_cell::sync::Lazy; -use super::ohcam_rapi::*; use crate::camera_backend::{ CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, }; use util::aio::Iovec; +use util::ohos_binding::camera::*; type OhCamCB = RwLock; static OHCAM_CALLBACK: Lazy = Lazy::new(|| RwLock::new(OhCamCallBack::default())); @@ -81,7 +81,7 @@ pub struct OhCameraBackend { id: String, camidx: u8, profile_cnt: u8, - ctx: *mut c_void, + ctx: OhCamera, fmt_list: Vec, selected_profile: u8, } @@ -104,30 +104,22 @@ fn cam_fmt_from_oh(t: i32) -> Result { impl OhCameraBackend { pub fn new(id: String, camid: String) -> Result { - let p_ctx = ohcam_init()?; let idx = camid.parse::().with_context(|| "Invalid PATH format")?; - check_cam_idx(idx)?; + let ctx = OhCamera::new(idx as i32)?; - let profile_cnt = ohcam_get_fmt_nums(p_ctx, idx as i32)? as u8; + let profile_cnt = ctx.get_fmt_nums(idx as i32)? as u8; Ok(OhCameraBackend { id, camidx: idx, profile_cnt, - ctx: p_ctx, + ctx, fmt_list: vec![], selected_profile: 0, }) } } -impl Drop for OhCameraBackend { - fn drop(&mut self) { - ohcam_release_camera(self.ctx); - ohcam_drop_ctx(std::ptr::addr_of_mut!(self.ctx)); - } -} - impl CameraBackend for OhCameraBackend { fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { for fmt in &self.fmt_list { @@ -147,7 +139,8 @@ impl CameraBackend for OhCameraBackend { } self.selected_profile = fmt.fmt_index - 1; - ohcam_set_fmt(self.ctx, self.camidx as i32, self.selected_profile as i32)?; + self.ctx + .set_fmt(self.camidx as i32, self.selected_profile as i32)?; return Ok(()); } } @@ -159,53 +152,43 @@ impl CameraBackend for OhCameraBackend { } fn video_stream_on(&mut self) -> Result<()> { - ohcam_start_stream(self.ctx, on_buffer_available, on_broken) + self.ctx.start_stream(on_buffer_available, on_broken) } fn video_stream_off(&mut self) -> Result<()> { - ohcam_stop_stream(self.ctx); + self.ctx.stop_stream(); OHCAM_CALLBACK.write().unwrap().clear_buffer(); Ok(()) } fn list_format(&mut self) -> Result> { let mut fmt_list: Vec = Vec::new(); - let mut fmt: c_int = 0; - let mut w: c_int = 0; - let mut h: c_int = 0; - let mut fps: c_int = 0; + for idx in 0..self.profile_cnt { - if ohcam_get_profile( - self.ctx, - self.camidx as i32, - idx as i32, - &mut fmt, - &mut w, - &mut h, - &mut fps, - ) - .is_err() - { - continue; - } - // NOTE: windows camera APP doesn't support fps lower than 30, and some OH PC only support 15 fps. - if fps < 30 { - fps = 30; - } - if (fmt != CAMERA_FORMAT_YUV420SP) && (fmt != CAMERA_FORMAT_MJPEG) { - continue; + match self.ctx.get_profile(self.camidx as i32, idx as i32) { + Ok((fmt, width, height, mut fps)) => { + if (fmt != CAMERA_FORMAT_YUV420SP) && (fmt != CAMERA_FORMAT_MJPEG) { + continue; + } + // NOTE: windows camera APP doesn't support fps lower than 30, and some OH PC only support 15 fps. + if fps < 30 { + fps = 30; + } + + let frame = CameraFrame { + width: width as u32, + height: height as u32, + index: 1, + interval: FPS_INTERVAL_TRANS / fps as u32, + }; + fmt_list.push(CameraFormatList { + format: cam_fmt_from_oh(fmt)?, + frame: vec![frame], + fmt_index: (idx) + 1, + }); + } + Err(e) => error!("{:?}", e), } - let frame = CameraFrame { - width: w as u32, - height: h as u32, - index: 1, - interval: FPS_INTERVAL_TRANS / fps as u32, - }; - fmt_list.push(CameraFormatList { - format: cam_fmt_from_oh(fmt)?, - frame: vec![frame], - fmt_index: (idx) + 1, - }); } self.fmt_list = fmt_list.clone(); Ok(fmt_list) @@ -213,7 +196,7 @@ impl CameraBackend for OhCameraBackend { fn reset(&mut self) { OHCAM_CALLBACK.write().unwrap().clear_buffer(); - ohcam_reset_cam(self.ctx); + self.ctx.reset_camera(); } fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result { @@ -253,7 +236,7 @@ impl CameraBackend for OhCameraBackend { } fn next_frame(&mut self) -> Result<()> { - ohcam_next_frame(self.ctx); + self.ctx.next_frame(); OHCAM_CALLBACK.write().unwrap().clear_buffer(); Ok(()) } diff --git a/devices/src/camera_backend/ohos/ohcam_rapi.rs b/devices/src/camera_backend/ohos/ohcam_rapi.rs deleted file mode 100755 index 6154bce58..000000000 --- a/devices/src/camera_backend/ohos/ohcam_rapi.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use std::os::raw::{c_int, c_void}; -use std::ptr; - -use anyhow::{bail, Result}; - -use util::ohos_binding::hwf_adapter as capi; - -// OH camera framework's related definitions -#[allow(unused)] -pub const CAMERA_FORMAT_YCBCR420: i32 = 2; -#[allow(unused)] -pub const CAMERA_FORMAT_RGB18888: i32 = 3; -pub const CAMERA_FORMAT_YUV420SP: i32 = 1003; -pub const CAMERA_FORMAT_MJPEG: i32 = 2000; - -// camera path is actually the specified camera ID. -pub fn check_cam_idx(idx: u8) -> Result<()> { - // SAFETY: We call related API sequentially for specified ctx. - let mut ctx = unsafe { capi::ohcam_create_ctx() }; - // SAFETY: We call related API sequentially for specified ctx. - let n = unsafe { capi::ohcam_init_cameras(ctx) }; - if n < 0 { - ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); - bail!("OHCAM WRAPPER: failed to init cameras"); - } else if idx + 1 > n as u8 { - ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); - bail!("Invalid idx: {}, valid num is less than {}", idx, n); - } - ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); - Ok(()) -} - -pub fn ohcam_init() -> Result<*mut c_void> { - // SAFETY: We call related API sequentially for specified ctx. - let mut ctx = unsafe { capi::ohcam_create_ctx() }; - if ctx.is_null() { - bail!("OHCAM WRAPPER: create camera ctx failed"); - } - // SAFETY: We call related API sequentially for specified ctx. - unsafe { - if capi::ohcam_init_cameras(ctx) < 0 { - ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); - bail!("OHCAM WRAPPER: failed to init cameras"); - } - if capi::ohcam_init_profiles(ctx) < 0 { - ohcam_drop_ctx(ptr::addr_of_mut!(ctx)); - bail!("OHCAM WRAPPER: failed to init profiles"); - } - } - Ok(ctx) -} - -pub fn ohcam_get_fmt_nums(ctx: *mut c_void, idx: c_int) -> Result { - // SAFETY: We call related API sequentially for specified ctx. - let ret = unsafe { capi::ohcam_get_profile_size(ctx, idx) }; - if ret < 0 { - bail!("OHCAM WRAPPER: invalid camera idx {}", idx); - } - Ok(ret) -} - -pub fn ohcam_release_camera(ctx: *mut c_void) { - // SAFETY: We call related API sequentially for specified ctx. - unsafe { capi::ohcam_release(ctx) }; -} - -pub fn ohcam_drop_ctx(p_ctx: *mut *mut c_void) { - // SAFETY: We call related API sequentially for specified ctx. - unsafe { capi::ohcma_destroy_ctx(p_ctx) }; -} - -pub fn ohcam_set_fmt(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> Result<()> { - // SAFETY: We call related API sequentially for specified ctx. - let ret = unsafe { capi::ohcam_set_profile(ctx, cam_idx, profile_idx) }; - if ret < 0 { - bail!("OHCAM WRAPPER: Failed to ohcam_set_profile"); - } - Ok(()) -} - -pub fn ohcam_start_stream( - ctx: *mut c_void, - buffer_proc: capi::BufferProcess, - broken_process: capi::BrokenProcess, -) -> Result<()> { - // SAFETY: We call related API sequentially for specified ctx. - unsafe { - if capi::ohcam_pre_start(ctx, buffer_proc, broken_process) != 0 { - bail!("OHCAM WRAPPER: Pre start failed"); - } - if capi::ohcam_start(ctx) != 0 { - bail!("OHCAM WRAPPER: Start failed"); - } - } - Ok(()) -} - -pub fn ohcam_reset_cam(ctx: *mut c_void) { - // SAFETY: We call related API sequentially for specified ctx. - unsafe { - capi::ohcam_create_session(ctx); - capi::ohcam_init_cameras(ctx); - capi::ohcam_init_profiles(ctx); - } -} - -pub fn ohcam_stop_stream(ctx: *mut c_void) { - // SAFETY: We call related API sequentially for specified ctx. - unsafe { - capi::ohcam_stop_output(ctx); - capi::ohcam_release_session(ctx); - } -} - -pub fn ohcam_get_profile( - ctx: *mut c_void, - cam_idx: c_int, - profile_idx: c_int, - format: *mut c_int, - width: *mut c_int, - height: *mut c_int, - fps: *mut c_int, -) -> Result<()> { - let profile_recorder = capi::ProfileRecorder::default(); - // SAFETY: We call related API sequentially for specified ctx. - unsafe { - let ret = capi::ohcam_get_profile( - ctx, - cam_idx, - profile_idx, - ptr::addr_of!(profile_recorder) as *mut c_void, - ); - if ret < 0 { - bail!("Failed to OhcamGetProfile"); - } - *format = profile_recorder.fmt; - *width = profile_recorder.width; - *height = profile_recorder.height; - *fps = profile_recorder.fps; - } - Ok(()) -} - -pub fn ohcam_next_frame(ctx: *mut c_void) { - // SAFETY: We call related API sequentially for specified ctx. - unsafe { - capi::ohcam_allow_next_frame(ctx); - } -} diff --git a/util/src/ohos_binding/camera.rs b/util/src/ohos_binding/camera.rs new file mode 100644 index 000000000..16cb7a3e6 --- /dev/null +++ b/util/src/ohos_binding/camera.rs @@ -0,0 +1,165 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::{c_int, c_void}; +use std::ptr; +use std::sync::Arc; + +use anyhow::{bail, Result}; + +use super::hwf_adapter::camera::{ + BrokenProcessFn, BufferProcessFn, CamFuncTable, OhCameraCtx, ProfileRecorder, +}; +use super::hwf_adapter::hwf_adapter_camera_api; + +// OH camera framework's related definitions +#[allow(unused)] +pub const CAMERA_FORMAT_YCBCR420: i32 = 2; +#[allow(unused)] +pub const CAMERA_FORMAT_RGB18888: i32 = 3; +pub const CAMERA_FORMAT_YUV420SP: i32 = 1003; +pub const CAMERA_FORMAT_MJPEG: i32 = 2000; + +#[derive(Clone)] +pub struct OhCamera { + ctx: *mut OhCameraCtx, + capi: Arc, +} + +impl Drop for OhCamera { + fn drop(&mut self) { + self.release_camera(); + self.destroy_ctx(); + } +} + +impl OhCamera { + pub fn new(idx: i32) -> Result { + let capi = hwf_adapter_camera_api(); + // SAFETY: We call related API sequentially for specified ctx. + let mut ctx = unsafe { (capi.create_ctx)() }; + if ctx.is_null() { + bail!("OH Camera: failed to create camera ctx"); + } + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + let n = (capi.init_cameras)(ctx); + if n < 0 { + (capi.destroy_ctx)(ptr::addr_of_mut!(ctx)); + bail!("OH Camera: failed to init cameras"); + } else if idx >= n { + (capi.destroy_ctx)(ptr::addr_of_mut!(ctx)); + bail!( + "OH Camera: invalid idx {}, valid num is less than {}", + idx, + n + ); + } + if (capi.init_profiles)(ctx) < 0 { + (capi.destroy_ctx)(ptr::addr_of_mut!(ctx)); + bail!("OH Camera: failed to init profiles"); + } + } + Ok(Self { ctx, capi }) + } + + pub fn get_fmt_nums(&self, idx: i32) -> Result { + // SAFETY: We call related API sequentially for specified ctx. + let ret = unsafe { (self.capi.get_profile_size)(self.ctx, idx as c_int) }; + if ret < 0 { + bail!("OH Camera: invalid camera idx {}", idx); + } + Ok(ret) + } + + pub fn release_camera(&self) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.release)(self.ctx) } + } + + pub fn destroy_ctx(&mut self) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.destroy_ctx)(ptr::addr_of_mut!(self.ctx)) } + } + + pub fn set_fmt(&self, cam_idx: i32, profile_idx: i32) -> Result<()> { + let ret = + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.set_profile)(self.ctx, cam_idx as c_int, profile_idx as c_int) }; + if ret < 0 { + bail!("OH Camera: failed to get camera profile"); + } + Ok(()) + } + + pub fn start_stream( + &self, + buffer_proc: BufferProcessFn, + broken_proc: BrokenProcessFn, + ) -> Result<()> { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + if (self.capi.pre_start)(self.ctx, buffer_proc, broken_proc) != 0 { + bail!("OH Camera: failed to prestart camera stream"); + } + if (self.capi.start)(self.ctx) != 0 { + bail!("OH Camera: failed to start camera stream"); + } + } + Ok(()) + } + + pub fn reset_camera(&self) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + (self.capi.create_session)(self.ctx); + (self.capi.init_cameras)(self.ctx); + (self.capi.init_profiles)(self.ctx); + } + } + + pub fn stop_stream(&self) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + (self.capi.stop_output)(self.ctx); + (self.capi.release_session)(self.ctx); + } + } + + pub fn get_profile(&self, cam_idx: i32, profile_idx: i32) -> Result<(i32, i32, i32, i32)> { + let pr = ProfileRecorder::default(); + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + let ret = (self.capi.get_profile)( + self.ctx, + cam_idx as c_int, + profile_idx as c_int, + ptr::addr_of!(pr) as *mut c_void, + ); + if ret < 0 { + bail!( + "OH Camera: failed to get camera {} profile {}", + cam_idx, + profile_idx + ); + } + } + Ok((pr.fmt, pr.width, pr.height, pr.fps)) + } + + pub fn next_frame(&self) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { + (self.capi.allow_next_frame)(self.ctx); + } + } +} diff --git a/util/src/ohos_binding/hwf_adapter/camera.rs b/util/src/ohos_binding/hwf_adapter/camera.rs index eb9aa359c..0fdc9ee74 100644 --- a/util/src/ohos_binding/hwf_adapter/camera.rs +++ b/util/src/ohos_binding/hwf_adapter/camera.rs @@ -18,6 +18,12 @@ use libloading::Library; use crate::get_libfn; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct OhCameraCtx { + _unused: [u8; 0], +} + #[repr(C)] #[derive(Default)] pub struct ProfileRecorder { @@ -30,20 +36,21 @@ pub struct ProfileRecorder { pub type BufferProcessFn = unsafe extern "C" fn(src_buffer: u64, length: i32); pub type BrokenProcessFn = unsafe extern "C" fn(); -type OhcamCreateCtxFn = unsafe extern "C" fn() -> *mut c_void; -type OhcamCreateSessionFn = unsafe extern "C" fn(*mut c_void) -> c_int; -type OhcamReleaseSessionFn = unsafe extern "C" fn(*mut c_void); -type OhcamInitCamerasFn = unsafe extern "C" fn(*mut c_void) -> c_int; -type OhcamInitProfilesFn = unsafe extern "C" fn(*mut c_void) -> c_int; -type OhcamGetProfileSizeFn = unsafe extern "C" fn(*mut c_void, c_int) -> c_int; -type OhcamGetProfileFn = unsafe extern "C" fn(*mut c_void, c_int, c_int, *mut c_void) -> c_int; -type OhcamSetProfileFn = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> c_int; -type OhcamPreStartFn = unsafe extern "C" fn(*mut c_void, BufferProcessFn, BrokenProcessFn) -> c_int; -type OhcamStartFn = unsafe extern "C" fn(*mut c_void) -> c_int; -type OhcamStopOutputFn = unsafe extern "C" fn(*mut c_void); -type OhcamReleaseFn = unsafe extern "C" fn(*mut c_void); -type OhcamDestroyCtxFn = unsafe extern "C" fn(*mut *mut c_void); -type OhcamAllowNextFrameFn = unsafe extern "C" fn(*mut c_void); +type OhcamCreateCtxFn = unsafe extern "C" fn() -> *mut OhCameraCtx; +type OhcamCreateSessionFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; +type OhcamReleaseSessionFn = unsafe extern "C" fn(*mut OhCameraCtx); +type OhcamInitCamerasFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; +type OhcamInitProfilesFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; +type OhcamGetProfileSizeFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int) -> c_int; +type OhcamGetProfileFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int, c_int, *mut c_void) -> c_int; +type OhcamSetProfileFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int, c_int) -> c_int; +type OhcamPreStartFn = + unsafe extern "C" fn(*mut OhCameraCtx, BufferProcessFn, BrokenProcessFn) -> c_int; +type OhcamStartFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; +type OhcamStopOutputFn = unsafe extern "C" fn(*mut OhCameraCtx); +type OhcamReleaseFn = unsafe extern "C" fn(*mut OhCameraCtx); +type OhcamDestroyCtxFn = unsafe extern "C" fn(*mut *mut OhCameraCtx); +type OhcamAllowNextFrameFn = unsafe extern "C" fn(*mut OhCameraCtx); pub struct CamFuncTable { pub create_ctx: RawSymbol, diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs index 69b17f885..e35219466 100644 --- a/util/src/ohos_binding/hwf_adapter/mod.rs +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -11,11 +11,10 @@ // See the Mulan PSL v2 for more details. #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -mod camera; +pub mod camera; use std::ffi::OsStr; -use std::os::raw::{c_int, c_void}; -use std::sync::RwLock; +use std::sync::{Arc, RwLock}; use anyhow::{Context, Result}; use libloading::Library; @@ -42,7 +41,7 @@ struct LibHwfAdapter { #[allow(unused)] library: Library, #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] - camera: CamFuncTable, + camera: Arc, } impl LibHwfAdapter { @@ -51,8 +50,9 @@ impl LibHwfAdapter { Library::new(library_name).with_context(|| "failed to load hwf_adapter library")?; #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] - let camera = - CamFuncTable::new(&library).with_context(|| "failed to init camera function table")?; + let camera = Arc::new( + CamFuncTable::new(&library).with_context(|| "failed to init camera function table")?, + ); Ok(Self { library, @@ -60,6 +60,16 @@ impl LibHwfAdapter { camera, }) } + + #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + fn get_camera_api(&self) -> Arc { + self.camera.clone() + } +} + +#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +pub fn hwf_adapter_camera_api() -> Arc { + LIB_HWF_ADAPTER.read().unwrap().get_camera_api() } #[macro_export] @@ -75,150 +85,3 @@ macro_rules! get_libfn { .into_raw() }; } - -macro_rules! hwf_call { - ( $klass: ident, $fname: ident ( $($x: expr),* ) ) => { - (LIB_HWF_ADAPTER.read().unwrap().$klass.$fname)( $($x),* ) - }; -} - -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -macro_rules! hwf_camera_call { - ( $fname: ident ( $($x: expr),* ) ) => { - hwf_call!(camera, $fname( $($x),* )) - }; -} - -/// Camera APIs in libhwf_adapter.so - -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub type ProfileRecorder = camera::ProfileRecorder; -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub type BufferProcess = camera::BufferProcessFn; -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub type BrokenProcess = camera::BrokenProcessFn; - -/// # Safety -/// -/// The caller must save returned value for later use. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_create_ctx() -> *mut c_void { - hwf_camera_call!(create_ctx()) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_create_session(ctx: *mut c_void) -> c_int { - hwf_camera_call!(create_session(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_release_session(ctx: *mut c_void) { - hwf_camera_call!(release_session(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_init_cameras(ctx: *mut c_void) -> c_int { - hwf_camera_call!(init_cameras(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_init_profiles(ctx: *mut c_void) -> c_int { - hwf_camera_call!(init_profiles(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function -/// and valid profile index. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_get_profile_size(ctx: *mut c_void, idx: c_int) -> c_int { - hwf_camera_call!(get_profile_size(ctx, idx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function, -/// valid profile pointer and valid index of camera and profile. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_get_profile( - ctx: *mut c_void, - cam_idx: c_int, - profile_idx: c_int, - profile: *mut c_void, -) -> c_int { - hwf_camera_call!(get_profile(ctx, cam_idx, profile_idx, profile)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function -/// and valid index of camera and profile. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_set_profile(ctx: *mut c_void, cam_idx: c_int, profile_idx: c_int) -> c_int { - hwf_camera_call!(set_profile(ctx, cam_idx, profile_idx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function -/// and take care about the logic of process callbacks. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_pre_start( - ctx: *mut c_void, - buffer_proc: BufferProcess, - broken_proc: BrokenProcess, -) -> c_int { - hwf_camera_call!(pre_start(ctx, buffer_proc, broken_proc)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_start(ctx: *mut c_void) -> c_int { - hwf_camera_call!(start(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_stop_output(ctx: *mut c_void) { - hwf_camera_call!(stop_output(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_release(ctx: *mut c_void) { - hwf_camera_call!(release(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcma_destroy_ctx(ctx: *mut *mut c_void) { - hwf_camera_call!(destroy_ctx(ctx)) -} - -/// # Safety -/// -/// The caller must ensure the ctx is returned from OhcamCreateCtx function. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] -pub unsafe fn ohcam_allow_next_frame(ctx: *mut c_void) { - hwf_camera_call!(allow_next_frame(ctx)) -} diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs index c45f65d0c..cd5f32fcd 100644 --- a/util/src/ohos_binding/mod.rs +++ b/util/src/ohos_binding/mod.rs @@ -11,5 +11,8 @@ // See the Mulan PSL v2 for more details. #[cfg(all(target_env = "ohos", any(feature = "usb_camera_oh")))] -pub mod hwf_adapter; +pub mod camera; pub mod misc; + +#[cfg(all(target_env = "ohos", any(feature = "usb_camera_oh")))] +mod hwf_adapter; -- Gitee From ed1e402c754d675de926a82554f6c420f07a4883 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 27 Mar 2024 20:21:08 +0800 Subject: [PATCH 1692/2187] util/ohos-bindings: remove duplicated cfg attribute Because ohos-bindings has been conditionally compiled on OHOS platform, remove duplicated cfg attribute 'target_env = "ohos"' for its submodule. Signed-off-by: Zhao Yi Min --- util/src/ohos_binding/hwf_adapter/mod.rs | 14 +++++++------- util/src/ohos_binding/mod.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs index e35219466..47aa0a293 100644 --- a/util/src/ohos_binding/hwf_adapter/mod.rs +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +#[cfg(feature = "usb_camera_oh")] pub mod camera; use std::ffi::OsStr; @@ -21,7 +21,7 @@ use libloading::Library; use log::error; use once_cell::sync::Lazy; -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +#[cfg(feature = "usb_camera_oh")] use camera::CamFuncTable; static LIB_HWF_ADAPTER: Lazy> = Lazy::new(|| @@ -40,7 +40,7 @@ static LIB_HWF_ADAPTER: Lazy> = Lazy::new(|| struct LibHwfAdapter { #[allow(unused)] library: Library, - #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + #[cfg(feature = "usb_camera_oh")] camera: Arc, } @@ -49,25 +49,25 @@ impl LibHwfAdapter { let library = Library::new(library_name).with_context(|| "failed to load hwf_adapter library")?; - #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + #[cfg(feature = "usb_camera_oh")] let camera = Arc::new( CamFuncTable::new(&library).with_context(|| "failed to init camera function table")?, ); Ok(Self { library, - #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + #[cfg(feature = "usb_camera_oh")] camera, }) } - #[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] + #[cfg(feature = "usb_camera_oh")] fn get_camera_api(&self) -> Arc { self.camera.clone() } } -#[cfg(all(feature = "usb_camera_oh", target_env = "ohos"))] +#[cfg(feature = "usb_camera_oh")] pub fn hwf_adapter_camera_api() -> Arc { LIB_HWF_ADAPTER.read().unwrap().get_camera_api() } diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs index cd5f32fcd..24593a0bb 100644 --- a/util/src/ohos_binding/mod.rs +++ b/util/src/ohos_binding/mod.rs @@ -10,9 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -#[cfg(all(target_env = "ohos", any(feature = "usb_camera_oh")))] +#[cfg(feature = "usb_camera_oh")] pub mod camera; pub mod misc; -#[cfg(all(target_env = "ohos", any(feature = "usb_camera_oh")))] +#[cfg(feature = "usb_camera_oh")] mod hwf_adapter; -- Gitee From fb533e4a4f0347e6b311a59585544de5858d8106 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 28 Mar 2024 09:33:19 +0800 Subject: [PATCH 1693/2187] ohaudio: move binding code to util/ohos-bindings Let's move ohaudio FFI code to util/ohos-bindings and move scream ohaudio code to the same folder as v4l2. Signed-off-by: Zhao Yi Min --- devices/Cargo.toml | 2 +- devices/src/misc/scream/mod.rs | 4 ++-- devices/src/misc/scream/{ohos => }/ohaudio.rs | 3 +-- devices/src/misc/scream/ohos/mod.rs | 16 ---------------- util/Cargo.toml | 1 + .../src/ohos_binding/audio/mod.rs | 11 +++++++++-- .../src/ohos_binding/audio/sys.rs | 0 util/src/ohos_binding/mod.rs | 2 ++ 8 files changed, 16 insertions(+), 23 deletions(-) rename devices/src/misc/scream/{ohos => }/ohaudio.rs (98%) delete mode 100755 devices/src/misc/scream/ohos/mod.rs rename devices/src/misc/scream/ohos/ohaudio_rapi.rs => util/src/ohos_binding/audio/mod.rs (97%) rename devices/src/misc/scream/ohos/ohaudio_bindings.rs => util/src/ohos_binding/audio/sys.rs (100%) diff --git a/devices/Cargo.toml b/devices/Cargo.toml index ff64dc45b..95ca790d1 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -43,7 +43,7 @@ default = [] scream = ["machine_manager/scream"] scream_alsa = ["scream", "dep:alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scream_pulseaudio"] -scream_ohaudio = ["scream", "machine_manager/scream_ohaudio"] +scream_ohaudio = ["scream", "machine_manager/scream_ohaudio", "util/scream_ohaudio"] pvpanic = ["machine_manager/pvpanic"] demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 93fcb5919..85b73ee67 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -14,7 +14,7 @@ mod alsa; mod audio_demo; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -mod ohos; +mod ohaudio; #[cfg(feature = "scream_pulseaudio")] mod pulseaudio; @@ -41,7 +41,7 @@ use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -use ohos::ohaudio::OhAudio; +use ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] diff --git a/devices/src/misc/scream/ohos/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs similarity index 98% rename from devices/src/misc/scream/ohos/ohaudio.rs rename to devices/src/misc/scream/ohaudio.rs index 56301a569..77588c887 100755 --- a/devices/src/misc/scream/ohos/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -19,9 +19,8 @@ use std::{cmp, ptr, thread, time}; use log::{error, warn}; -use super::ohaudio_bindings::{OhAudioCapturer, OhAudioRenderer}; -use super::ohaudio_rapi::{AudioContext, AudioProcessCb, AudioStreamType}; use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, StreamData}; +use util::ohos_binding::audio::*; trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; diff --git a/devices/src/misc/scream/ohos/mod.rs b/devices/src/misc/scream/ohos/mod.rs deleted file mode 100755 index 2e5275ac9..000000000 --- a/devices/src/misc/scream/ohos/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod ohaudio; - -mod ohaudio_bindings; -mod ohaudio_rapi; diff --git a/util/Cargo.toml b/util/Cargo.toml index 5813d12d6..18a4eef07 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -28,4 +28,5 @@ trace = {path = "../trace"} default = [] usb_camera_v4l2 = ["dep:v4l2-sys-mit"] usb_camera_oh = [] +scream_ohaudio = [] pixman = [] diff --git a/devices/src/misc/scream/ohos/ohaudio_rapi.rs b/util/src/ohos_binding/audio/mod.rs similarity index 97% rename from devices/src/misc/scream/ohos/ohaudio_rapi.rs rename to util/src/ohos_binding/audio/mod.rs index 5537f2076..bd308d753 100755 --- a/devices/src/misc/scream/ohos/ohaudio_rapi.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -10,13 +10,17 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod sys; + use std::os::raw::c_void; use std::ptr; use log::error; -use super::ohaudio_bindings as capi; -use crate::misc::scream::{AUDIO_SAMPLE_RATE_44KHZ, AUDIO_SAMPLE_RATE_48KHZ}; +use sys as capi; + +const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; +const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; macro_rules! call_capi { ( $f: ident ( $($x: expr),* ) ) => { @@ -167,6 +171,9 @@ impl Into for AudioStreamType { } } +pub type OhAudioCapturer = sys::OhAudioCapturer; +pub type OhAudioRenderer = sys::OhAudioRenderer; + pub enum AudioProcessCb { CapturerCb( Option< diff --git a/devices/src/misc/scream/ohos/ohaudio_bindings.rs b/util/src/ohos_binding/audio/sys.rs similarity index 100% rename from devices/src/misc/scream/ohos/ohaudio_bindings.rs rename to util/src/ohos_binding/audio/sys.rs diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs index 24593a0bb..d329b4a44 100644 --- a/util/src/ohos_binding/mod.rs +++ b/util/src/ohos_binding/mod.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "scream_ohaudio")] +pub mod audio; #[cfg(feature = "usb_camera_oh")] pub mod camera; pub mod misc; -- Gitee From cd6ffc78f9d33c8609e75415449aaff40f53dc73 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 28 Mar 2024 11:05:01 +0800 Subject: [PATCH 1694/2187] hwf_adapter: remove redundant RwLock Let's remove redundant RwLock protecting static variable LIB_HWF_ADAPTER. Signed-off-by: Zhao Yi Min --- util/src/ohos_binding/hwf_adapter/mod.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs index 47aa0a293..4ea093666 100644 --- a/util/src/ohos_binding/hwf_adapter/mod.rs +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -14,7 +14,7 @@ pub mod camera; use std::ffi::OsStr; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use anyhow::{Context, Result}; use libloading::Library; @@ -24,17 +24,15 @@ use once_cell::sync::Lazy; #[cfg(feature = "usb_camera_oh")] use camera::CamFuncTable; -static LIB_HWF_ADAPTER: Lazy> = Lazy::new(|| +static LIB_HWF_ADAPTER: Lazy = Lazy::new(|| // SAFETY: The dynamic library should be always existing. unsafe { - RwLock::new( - LibHwfAdapter::new(OsStr::new("/system/lib64/libhwf_adapter.so")) - .map_err(|e| { - error!("failed to init LibHwfAdapter with error: {:?}", e); - e - }) - .unwrap() - ) + LibHwfAdapter::new(OsStr::new("/system/lib64/libhwf_adapter.so")) + .map_err(|e| { + error!("failed to init LibHwfAdapter with error: {:?}", e); + e + }) + .unwrap() }); struct LibHwfAdapter { @@ -69,7 +67,7 @@ impl LibHwfAdapter { #[cfg(feature = "usb_camera_oh")] pub fn hwf_adapter_camera_api() -> Arc { - LIB_HWF_ADAPTER.read().unwrap().get_camera_api() + LIB_HWF_ADAPTER.get_camera_api() } #[macro_export] -- Gitee From 27fbb016afbf7b650ac00dff68391c98d09c9351 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 29 Mar 2024 14:25:46 +0800 Subject: [PATCH 1695/2187] ohcam: optimize the directory structure of OH camera backend Move the ohcam.rs to parent directory to optimize the directory structure of camera backend. Signed-off-by: Zhao Yi Min --- devices/src/camera_backend/mod.rs | 4 ++-- devices/src/camera_backend/{ohos => }/ohcam.rs | 0 devices/src/camera_backend/ohos/mod.rs | 13 ------------- 3 files changed, 2 insertions(+), 15 deletions(-) rename devices/src/camera_backend/{ohos => }/ohcam.rs (100%) delete mode 100755 devices/src/camera_backend/ohos/mod.rs diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index a47ce2662..a4e919a6a 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -17,7 +17,7 @@ #[cfg(not(target_env = "ohos"))] pub mod demo; #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] -pub mod ohos; +pub mod ohcam; #[cfg(feature = "usb_camera_v4l2")] pub mod v4l2; @@ -29,7 +29,7 @@ use anyhow::{bail, Context, Result}; #[cfg(not(target_env = "ohos"))] use self::demo::DemoCameraBackend; #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] -use self::ohos::ohcam::OhCameraBackend; +use self::ohcam::OhCameraBackend; #[cfg(feature = "usb_camera_v4l2")] use self::v4l2::V4l2CameraBackend; use crate::usb::camera::UsbCameraConfig; diff --git a/devices/src/camera_backend/ohos/ohcam.rs b/devices/src/camera_backend/ohcam.rs similarity index 100% rename from devices/src/camera_backend/ohos/ohcam.rs rename to devices/src/camera_backend/ohcam.rs diff --git a/devices/src/camera_backend/ohos/mod.rs b/devices/src/camera_backend/ohos/mod.rs deleted file mode 100755 index ccb09f3b5..000000000 --- a/devices/src/camera_backend/ohos/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -pub mod ohcam; -- Gitee From f71d473815d2120e357c9b70a3f776dab8cb4f6f Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Mon, 20 Nov 2023 20:51:29 +0300 Subject: [PATCH 1696/2187] usb: Simplify default for UsbPacket Signed-off-by: Goriainov Stanislav --- devices/src/usb/mod.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 08cef7eb8..f263dbcfe 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -60,6 +60,12 @@ pub enum UsbPacketStatus { IoError, } +impl Default for UsbPacketStatus { + fn default() -> Self { + Self::NoDev + } +} + /// USB request used to transfer to USB device. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] @@ -483,6 +489,7 @@ pub trait TransferOps: Send + Sync { } /// Usb packet used for device transfer data. +#[derive(Default)] pub struct UsbPacket { /// USB packet id. pub pid: u32, @@ -583,21 +590,6 @@ impl UsbPacket { } } -impl Default for UsbPacket { - fn default() -> UsbPacket { - UsbPacket { - pid: 0, - is_async: false, - iovecs: Vec::new(), - parameter: 0, - status: UsbPacketStatus::NoDev, - actual_length: 0, - ep_number: 0, - xfer_ops: None, - } - } -} - #[cfg(test)] mod tests { use super::*; -- Gitee From 5cc497e9edad8152d11e6996de8ce3b531de6b37 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Mon, 20 Nov 2023 20:53:52 +0300 Subject: [PATCH 1697/2187] usb: Alter get_iovecs_size signature No need for self to be mut in get_iovecs_size. Signed-off-by: Goriainov Stanislav --- devices/src/usb/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f263dbcfe..f9752beeb 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -580,7 +580,7 @@ impl UsbPacket { self.actual_length = copied as u32; } - pub fn get_iovecs_size(&mut self) -> u64 { + pub fn get_iovecs_size(&self) -> u64 { let mut size = 0; for iov in &self.iovecs { size += iov.iov_len; -- Gitee From 702a9e8a22733b14ad68e0725156ecd186120362 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 8 Feb 2024 16:28:57 +0300 Subject: [PATCH 1698/2187] usb: Fix typo in "packet" Signed-off-by: Stanislav Goriainov --- devices/src/usb/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f9752beeb..7cd979c8d 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -391,10 +391,10 @@ pub trait UsbDevice: Send + Sync { } } - /// Handle control pakcet. + /// Handle control packet. fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest); - /// Handle data pakcet. + /// Handle data packet. fn handle_data(&mut self, packet: &Arc>); /// Unique device id. -- Gitee From 93da74edf0c79c450485e4ba49aad1f6e18d2a04 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Mon, 20 Nov 2023 21:01:27 +0300 Subject: [PATCH 1699/2187] usb: Add packet_id and stream fields to UsbPacket Packet id may find itself useful when tracing down a certain USB packet. Streams in UsbPacket will be needed when stream support is added to the XHCI controller. Signed-off-by: Goriainov Stanislav --- devices/src/usb/mod.rs | 7 +++++++ devices/src/usb/xhci/xhci_controller.rs | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 7cd979c8d..f44d8b507 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -491,6 +491,8 @@ pub trait TransferOps: Send + Sync { /// Usb packet used for device transfer data. #[derive(Default)] pub struct UsbPacket { + /// USB packet unique identifier. + pub packet_id: u32, /// USB packet id. pub pid: u32, pub is_async: bool, @@ -505,6 +507,8 @@ pub struct UsbPacket { pub ep_number: u8, /// Transfer for complete packet. pub xfer_ops: Option>>, + /// Stream id. + pub stream: u32, } impl std::fmt::Display for UsbPacket { @@ -519,12 +523,14 @@ impl std::fmt::Display for UsbPacket { impl UsbPacket { pub fn new( + packet_id: u32, pid: u32, ep_number: u8, iovecs: Vec, xfer_ops: Option>>, ) -> Self { Self { + packet_id, pid, is_async: false, iovecs, @@ -533,6 +539,7 @@ impl UsbPacket { actual_length: 0, ep_number, xfer_ops, + stream: 0, } } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 1d0a005dd..c462a5136 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -690,6 +690,7 @@ pub struct XhciDevice { /// Runtime Register. mfindex_start: Duration, timer_id: Option, + packet_count: u32, } impl XhciDevice { @@ -726,6 +727,7 @@ impl XhciDevice { } let xhci = XhciDevice { + packet_count: 0, oper, usb_ports: Vec::new(), numports_3: p3, @@ -923,6 +925,11 @@ impl XhciDevice { Ok(()) } + fn generate_packet_id(&mut self) -> u32 { + self.packet_count = self.packet_count.wrapping_add(1); + self.packet_count + } + fn get_slot_id(&self, evt: &mut XhciEvent, trb: &XhciTRB) -> u32 { let slot_id = (trb.control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK; if slot_id < 1 || slot_id > self.slots.len() as u32 { @@ -1182,7 +1189,9 @@ impl XhciDevice { index: 0, length: 0, }; + let packet_id = self.generate_packet_id(); let p = Arc::new(Mutex::new(UsbPacket::new( + packet_id, USB_TOKEN_OUT as u32, 0, Vec::new(), @@ -1931,9 +1940,11 @@ impl XhciDevice { vec.append(&mut hvas); } } + + let packet_id = self.generate_packet_id(); let (_, ep_number) = endpoint_id_to_number(locked_xfer.epid as u8); let xfer_ops = Arc::downgrade(xfer) as Weak>; - let packet = UsbPacket::new(dir as u32, ep_number, vec, Some(xfer_ops)); + let packet = UsbPacket::new(packet_id, dir as u32, ep_number, vec, Some(xfer_ops)); Ok(Arc::new(Mutex::new(packet))) } -- Gitee From 551eb5c521f4e7c4ec1596bf045bfe531fbc89e1 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 24 Nov 2023 12:29:59 +0300 Subject: [PATCH 1700/2187] xhci: Rename "flush" to "cancel" The term "cancel" seems to better reflect semantics of the functions. Signed-off-by: Goriainov Stanislav --- devices/src/usb/xhci/xhci_controller.rs | 16 ++++++++-------- trace/trace_event/usb.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index c462a5136..ab9133295 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1082,7 +1082,7 @@ impl XhciDevice { for i in 1..=self.slots[(slot_id - 1) as usize].endpoints.len() as u32 { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(i - 1) as usize]; if epctx.enabled { - self.flush_ep_transfer(slot_id, i, TRBCCode::Invalid)?; + self.cancel_all_ep_transfers(slot_id, i, TRBCCode::Invalid)?; } } self.slots[(slot_id - 1) as usize].usb_port = None; @@ -1441,7 +1441,7 @@ impl XhciDevice { trace::usb_xhci_unimplemented(&"Endpoint already disabled".to_string()); return Ok(TRBCCode::Success); } - self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Invalid)?; + self.cancel_all_ep_transfers(slot_id, ep_id, TRBCCode::Invalid)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if self.oper.dcbaap != 0 { epctx.set_state(EP_DISABLED)?; @@ -1474,7 +1474,7 @@ impl XhciDevice { ); return Ok(TRBCCode::ContextStateError); } - if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Stopped)? > 0 { + if self.cancel_all_ep_transfers(slot_id, ep_id, TRBCCode::Stopped)? > 0 { trace::usb_xhci_unimplemented(&format!( "Endpoint stop when xfers running, slot_id {} epid {}", slot_id, ep_id @@ -1504,7 +1504,7 @@ impl XhciDevice { error!("Endpoint is not halted"); return Ok(TRBCCode::ContextStateError); } - if self.flush_ep_transfer(slot_id, ep_id, TRBCCode::Invalid)? > 0 { + if self.cancel_all_ep_transfers(slot_id, ep_id, TRBCCode::Invalid)? > 0 { warn!("endpoint reset when xfers running!"); } let slot = &mut self.slots[(slot_id - 1) as usize]; @@ -1984,8 +1984,8 @@ impl XhciDevice { } /// Flush transfer in endpoint in some case such as stop endpoint. - fn flush_ep_transfer(&mut self, slotid: u32, epid: u32, report: TRBCCode) -> Result { - trace::usb_xhci_flush_ep_transfer(&slotid, &epid); + fn cancel_all_ep_transfers(&mut self, slotid: u32, epid: u32, report: TRBCCode) -> Result { + trace::usb_xhci_cancel_all_ep_transfers(&slotid, &epid); let mut cnt = 0; let mut report = report; while let Some(xfer) = self.slots[(slotid - 1) as usize].endpoints[(epid - 1) as usize] @@ -1996,7 +1996,7 @@ impl XhciDevice { if locked_xfer.complete { continue; } - cnt += self.do_ep_transfer(slotid, epid, &mut locked_xfer, report)?; + cnt += self.cancel_one_ep_transfer(slotid, epid, &mut locked_xfer, report)?; if cnt != 0 { // Only report once. report = TRBCCode::Invalid; @@ -2008,7 +2008,7 @@ impl XhciDevice { Ok(cnt) } - fn do_ep_transfer( + fn cancel_one_ep_transfer( &mut self, slotid: u32, ep_id: u32, diff --git a/trace/trace_event/usb.toml b/trace/trace_event/usb.toml index fc5699a34..fa5b94542 100644 --- a/trace/trace_event/usb.toml +++ b/trace/trace_event/usb.toml @@ -221,7 +221,7 @@ message = "status={:?}" enabled = true [[events]] -name = "usb_xhci_flush_ep_transfer" +name = "usb_xhci_cancel_all_ep_transfers" args = "slotid: &dyn fmt::Debug, epid: &dyn fmt::Debug" message = "slotid={:?} epid={:?}" enabled = true -- Gitee From 484c0067dc0787d510c2b3bcc05a4fe026831db6 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Mon, 20 Nov 2023 20:37:57 +0300 Subject: [PATCH 1701/2187] usb: Add cancel_packet() function to UsbDevice trait It's better to have the ability to cancel an incoming USB packet, e.g. when resetting the device. Signed-off-by: Goriainov Stanislav --- devices/src/usb/camera.rs | 2 ++ devices/src/usb/keyboard.rs | 2 ++ devices/src/usb/mod.rs | 4 ++++ devices/src/usb/storage.rs | 2 ++ devices/src/usb/tablet.rs | 2 ++ devices/src/usb/usbhost/mod.rs | 2 ++ 6 files changed, 14 insertions(+) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index bd7ae9fb6..12cbe6b22 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -783,6 +783,8 @@ impl UsbDevice for UsbCamera { Ok(()) } + fn cancel_packet(&mut self, _packet: &Arc>) {} + fn reset(&mut self) { info!("Camera {} device reset", self.device_id()); self.base.addr = 0; diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 73dc8ed9e..b6a01a4b5 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -217,6 +217,8 @@ impl UsbDevice for UsbKeyboard { Ok(()) } + fn cancel_packet(&mut self, _packet: &Arc>) {} + fn reset(&mut self) { info!("Keyboard device reset"); self.base.remote_wakeup = 0; diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f44d8b507..3fc1772ff 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -349,6 +349,10 @@ pub trait UsbDevice: Send + Sync { fn unrealize(&mut self) -> Result<()> { Ok(()) } + + /// Cancel specified USB packet. + fn cancel_packet(&mut self, packet: &Arc>); + /// Handle the attach ops when attach device to controller. fn handle_attach(&mut self) -> Result<()> { let usb_dev = self.usb_device_base_mut(); diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index b227ecc08..1c34077e7 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -542,6 +542,8 @@ impl UsbDevice for UsbStorage { Ok(storage) } + fn cancel_packet(&mut self, _packet: &Arc>) {} + fn reset(&mut self) { info!("Storage device reset"); self.base.remote_wakeup = 0; diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 19123c7c6..2e2334930 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -261,6 +261,8 @@ impl UsbDevice for UsbTablet { Ok(()) } + fn cancel_packet(&mut self, _packet: &Arc>) {} + fn reset(&mut self) { info!("Tablet device reset"); self.base.remote_wakeup = 0; diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index e34d1176c..5abdb292c 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -1045,6 +1045,8 @@ impl UsbDevice for UsbHost { Ok(()) } + fn cancel_packet(&mut self, _packet: &Arc>) {} + fn reset(&mut self) { info!("Usb Host device {} reset", self.device_id()); if self.handle.is_none() { -- Gitee From f51263bff8f12cdee9c18a12b1a65fb36d8a1486 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 24 Nov 2023 12:26:58 +0300 Subject: [PATCH 1702/2187] usb: Add target_dev to UsbPacket If one needs to cancel a USB packet, he has to know which device it is targeted at. Signed-off-by: Goriainov Stanislav --- devices/src/usb/mod.rs | 4 ++++ devices/src/usb/xhci/xhci_controller.rs | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 3fc1772ff..e461b7577 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -511,6 +511,8 @@ pub struct UsbPacket { pub ep_number: u8, /// Transfer for complete packet. pub xfer_ops: Option>>, + /// Target USB device for this packet. + pub target_dev: Option>>, /// Stream id. pub stream: u32, } @@ -532,6 +534,7 @@ impl UsbPacket { ep_number: u8, iovecs: Vec, xfer_ops: Option>>, + target_dev: Option>>, ) -> Self { Self { packet_id, @@ -543,6 +546,7 @@ impl UsbPacket { actual_length: 0, ep_number, xfer_ops, + target_dev, stream: 0, } } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index ab9133295..eeb49c8a1 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1189,6 +1189,7 @@ impl XhciDevice { index: 0, length: 0, }; + let target_dev = Arc::downgrade(dev) as Weak>; let packet_id = self.generate_packet_id(); let p = Arc::new(Mutex::new(UsbPacket::new( packet_id, @@ -1196,6 +1197,7 @@ impl XhciDevice { 0, Vec::new(), None, + Some(target_dev), ))); trace::usb_handle_control(&locked_dev.usb_device_base().base.id, &device_req); locked_dev.handle_control(&p, &device_req); @@ -1941,10 +1943,24 @@ impl XhciDevice { } } + let target_dev = + if let Ok(target_dev) = self.get_usb_dev(locked_xfer.slotid, locked_xfer.epid) { + Some(Arc::downgrade(&target_dev) as Weak>) + } else { + None + }; + let packet_id = self.generate_packet_id(); let (_, ep_number) = endpoint_id_to_number(locked_xfer.epid as u8); let xfer_ops = Arc::downgrade(xfer) as Weak>; - let packet = UsbPacket::new(packet_id, dir as u32, ep_number, vec, Some(xfer_ops)); + let packet = UsbPacket::new( + packet_id, + dir as u32, + ep_number, + vec, + Some(xfer_ops), + target_dev, + ); Ok(Arc::new(Mutex::new(packet))) } -- Gitee From db49131b6d21303bb486196acfbd6d2624cf3fd9 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 9 Feb 2024 11:50:00 +0300 Subject: [PATCH 1703/2187] xhci: Add USB Packet cancellation logic Add cancel_packet() calls when cancelling endpoint transfers. Signed-off-by: Stanislav Goriainov --- devices/src/usb/xhci/xhci_controller.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index eeb49c8a1..7bf79b69f 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2037,6 +2037,13 @@ impl XhciDevice { if report != TRBCCode::Invalid { xfer.status = report; xfer.submit_transfer()?; + + if let Some(usb_dev) = xfer.packet.lock().unwrap().target_dev.as_ref() { + if let Some(usb_dev) = usb_dev.clone().upgrade() { + let mut locked_usb_dev = usb_dev.lock().unwrap(); + locked_usb_dev.cancel_packet(&xfer.packet); + } + } } xfer.running_async = false; killed = 1; -- Gitee From 66b452e84f1eb755f41b701656e921fd37cbac2e Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 24 Nov 2023 11:47:26 +0300 Subject: [PATCH 1704/2187] usb: Implement get_device_qualifier_descriptor properly device_qualifier_desc in get_device_qualifer_descriptor() used to always result to None. Signed-off-by: Goriainov Stanislav --- devices/src/usb/config.rs | 1 + devices/src/usb/descriptor.rs | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 3dda38b45..a0ce85200 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -218,6 +218,7 @@ pub const USB_DT_DEVICE_SIZE: u8 = 18; pub const USB_DT_CONFIG_SIZE: u8 = 9; pub const USB_DT_INTERFACE_SIZE: u8 = 9; pub const USB_DT_ENDPOINT_SIZE: u8 = 7; +pub const USB_DT_DEVICE_QUALIFIER_SIZE: u8 = 10; pub const USB_DT_BOS_SIZE: u8 = 5; pub const USB_DT_SS_CAP_SIZE: u8 = 10; pub const USB_DT_SS_EP_COMP_SIZE: u8 = 6; diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index bdb30f771..ca4c51e77 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -349,11 +349,25 @@ impl UsbDescriptor { } fn get_device_qualifier_descriptor(&self) -> Result> { - if let Some(desc) = self.device_qualifier_desc.as_ref() { - Ok(desc.qualifier_desc.as_bytes().to_vec()) - } else { - bail!("Device qualifier descriptor not found"); + if self.device_desc.is_none() { + bail!("device qualifier descriptor not found"); } + + // SAFETY: device_desc has just been checked + let device_desc = &self.device_desc.as_ref().unwrap().device_desc; + let device_qualifier_desc = UsbDeviceQualifierDescriptor { + bLength: USB_DT_DEVICE_QUALIFIER_SIZE, + bDescriptorType: USB_DT_DEVICE_QUALIFIER, + bcdUSB: device_desc.bcdUSB, + bDeviceClass: device_desc.bDeviceClass, + bDeviceSubClass: device_desc.bDeviceSubClass, + bDeviceProtocol: device_desc.bDeviceProtocol, + bMaxPacketSize0: device_desc.bMaxPacketSize0, + bNumConfigurations: device_desc.bNumConfigurations, + bReserved: 0, + }; + + Ok(device_qualifier_desc.as_bytes().to_vec()) } fn get_debug_descriptor(&self) -> Result> { -- Gitee From 2815b2422d15c4b2fd6213ae85173e44fcc8f214 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 9 Feb 2024 12:08:00 +0300 Subject: [PATCH 1705/2187] scsi: Make some fucntions from scsi pub They will be needed in USB UASP device module. Signed-off-by: Stanislav Goriainov --- devices/src/scsi/bus.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 954674350..271c51a56 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -766,7 +766,7 @@ fn scsi_cdb_length(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i32 { } } -fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i32 { +pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i32 { let dev_lock = dev.lock().unwrap(); let block_size = dev_lock.block_size as i32; drop(dev_lock); @@ -824,7 +824,7 @@ fn scsi_cdb_lba(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i64 { } } -fn scsi_cdb_xfer_mode(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> ScsiXferMode { +pub fn scsi_cdb_xfer_mode(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> ScsiXferMode { match cdb[0] { WRITE_6 | WRITE_10 -- Gitee From c260ac42c65c7e8e14554dc39eee6912d49a2ba7 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 29 Mar 2024 18:54:33 +0800 Subject: [PATCH 1706/2187] stratovirt-img: replace `match` branch with macros Replace match branch of image operation with macros image_operation_matches. No functional change. Signed-off-by: Xiao Ye --- image/src/img.rs | 11 ++++++- image/src/main.rs | 77 ++++++++++++++++++++++++----------------------- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/image/src/img.rs b/image/src/img.rs index 41d3924ee..d5e5f13b7 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -19,7 +19,7 @@ use std::{ use anyhow::{bail, Context, Result}; -use crate::cmdline::ArgsParse; +use crate::{cmdline::ArgsParse, BINARY_NAME}; use block_backend::{ qcow2::{header::QcowHeader, InternalSnapshotOps, Qcow2Driver, SyncAioInfo}, raw::RawDriver, @@ -385,6 +385,15 @@ pub(crate) fn create_qcow2_driver_for_check( Ok(qcow2_driver) } +pub(crate) fn print_version() { + println!( + "{} version {}\ + Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved.", + BINARY_NAME, + util::VERSION, + ) +} + pub fn print_help() { print!( r#"Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. diff --git a/image/src/main.rs b/image/src/main.rs index a24a2a4ec..eecafd30b 100644 --- a/image/src/main.rs +++ b/image/src/main.rs @@ -20,10 +20,40 @@ use std::{ use anyhow::{bail, Result}; -use crate::img::{image_check, image_create, image_snapshot, print_help}; +use crate::img::{image_check, image_create, image_snapshot, print_help, print_version}; const BINARY_NAME: &str = "stratovirt-img"; +macro_rules! image_operation_matches { + ( $cmd:expr; + $(($($opt_0:tt)|+, $function_0:tt, $arg:expr)),*; + $(($($opt_1:tt)|+, $function_1:tt)),* + ) => { + match $cmd { + $( + $($opt_0)|+ => { + if let Err(e) = $function_0($arg) { + bail!("{}: {:?}", BINARY_NAME, e); + } + }, + )* + $( + $($opt_1)|+ => { + $function_1() + }, + )* + _ => { + bail!( + "{}: Command not found: {}\n\ + Try 'stratovirt-img --help' for more information.", + BINARY_NAME, + $cmd + ); + } + } + } +} + fn main() -> ExitCode { let args: Vec = env::args().collect(); @@ -46,43 +76,16 @@ fn run(args: Vec) -> Result<()> { } let opt = args[1].clone(); + let cmd_args = args[2..].to_vec(); - match opt.as_str() { - "create" => { - if let Err(e) = image_create(args[2..].to_vec()) { - bail!("{}: {:?}", BINARY_NAME, e); - } - } - "check" => { - if let Err(e) = image_check(args[2..].to_vec()) { - bail!("{}: {:?}", BINARY_NAME, e); - } - } - "snapshot" => { - if let Err(e) = image_snapshot(args[2..].to_vec()) { - bail!("{}: {:?}", BINARY_NAME, e); - } - } - "-v" | "--version" => { - println!( - "{} version {}\ - Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved.", - BINARY_NAME, - util::VERSION, - ) - } - "-h" | "--help" => { - print_help(); - } - _ => { - bail!( - "{}: Command not found: {}\n\ - Try 'stratovirt-img --help' for more information.", - BINARY_NAME, - opt.as_str() - ); - } - } + image_operation_matches!( + opt.as_str(); + ("create", image_create, cmd_args), + ("check", image_check, cmd_args), + ("snapshot", image_snapshot, cmd_args); + ("-v" | "--version", print_version), + ("-h" | "--help", print_help) + ); Ok(()) } -- Gitee From 94ae347cf8269462b11ebf1fe7a7ff5ae8bc79a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=82=A6?= Date: Tue, 2 Apr 2024 19:37:22 +0800 Subject: [PATCH 1707/2187] ohos_bindings: move the macro 'get_libfn' to its super mod for later use Move macro to ohos_binding/mod for later use Signed-off-by:chenyue --- util/src/ohos_binding/hwf_adapter/mod.rs | 14 -------------- util/src/ohos_binding/mod.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs index 4ea093666..ffa11457f 100644 --- a/util/src/ohos_binding/hwf_adapter/mod.rs +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -69,17 +69,3 @@ impl LibHwfAdapter { pub fn hwf_adapter_camera_api() -> Arc { LIB_HWF_ADAPTER.get_camera_api() } - -#[macro_export] -macro_rules! get_libfn { - ( $lib: ident, $tname: ident, $fname: ident ) => { - $lib.get::<$tname>(stringify!($fname).as_bytes()) - .with_context(|| { - format!( - "failed to get function {} from libhwf_adapter", - stringify!($fname) - ) - })? - .into_raw() - }; -} diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs index d329b4a44..5f876ba54 100644 --- a/util/src/ohos_binding/mod.rs +++ b/util/src/ohos_binding/mod.rs @@ -18,3 +18,12 @@ pub mod misc; #[cfg(feature = "usb_camera_oh")] mod hwf_adapter; + +#[macro_export] +macro_rules! get_libfn { + ( $lib: ident, $tname: ident, $fname: ident ) => { + $lib.get::<$tname>(stringify!($fname).as_bytes()) + .with_context(|| format!("failed to get function {}", stringify!($fname)))? + .into_raw() + }; +} -- Gitee From 0a386938af37da2ca49b434393731c266048c639 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Wed, 27 Mar 2024 12:03:24 +0800 Subject: [PATCH 1708/2187] UI: support mouse side button We support side button "forward"&"back" on gtk, VNC and OHUI. And we fix mouse button code transfering. Signed-off-by: zhanghan64 --- devices/src/usb/hid.rs | 6 +++--- devices/src/usb/tablet.rs | 3 +-- ui/src/gtk/draw.rs | 26 +++++++++++++++++--------- ui/src/input.rs | 18 ++++++++++-------- ui/src/ohui_srv/msg.rs | 5 +++++ ui/src/ohui_srv/msg_handle.rs | 13 +++++++++++-- ui/src/vnc/client_io.rs | 35 +++++++++++++++++++++++++++-------- 7 files changed, 74 insertions(+), 32 deletions(-) diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 7f964b881..5706cdda7 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -79,14 +79,14 @@ const TABLET_REPORT_DESCRIPTOR: [u8; 89] = [ 0xa1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (1) - 0x29, 0x03, // Usage Maximum (3) + 0x29, 0x05, // Usage Maximum (5) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) - 0x95, 0x03, // Report Count (3) + 0x95, 0x05, // Report Count (5) 0x75, 0x01, // Report Size (1) 0x81, 0x02, // Input (Data, Variable, Absolute) 0x95, 0x01, // Report Count (1) - 0x75, 0x05, // Report Size (5) + 0x75, 0x03, // Report Size (3) 0x81, 0x01, // Input (Constant) 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x30, // Usage (X) diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 19123c7c6..67a15139a 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -31,11 +31,10 @@ use super::{ use machine_manager::config::valid_id; use ui::input::{ register_pointer, unregister_pointer, Axis, InputEvent, InputType, PointerOpts, - INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, + INPUT_BUTTON_MASK, INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, }; -const INPUT_BUTTON_MASK: u32 = 0x7; const INPUT_COORDINATES_MAX: u32 = 0x7fff; /// Tablet device descriptor diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 72027d944..9d9dde3cd 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -30,11 +30,17 @@ use crate::{ input::{ self, input_button, input_move_abs, input_point_sync, press_mouse, release_all_key, update_key_state, Axis, ABS_MAX, INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, - INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, - INPUT_POINT_RIGHT, + INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, INPUT_POINT_FORWARD, + INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, }, }; +const GTK_INPUT_BUTTON_LEFT: u32 = 1; +const GTK_INPUT_BUTTON_MIDDLE: u32 = 2; +const GTK_INPUT_BUTTON_RIGHT: u32 = 3; +const GTK_INPUT_BUTTON_BACK: u32 = 8; +const GTK_INPUT_BUTTON_FORWARD: u32 = 9; + pub(crate) fn set_callback_for_draw_area( draw_area: &DrawingArea, gs: Rc>, @@ -232,25 +238,27 @@ fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) fn da_pointer_callback(button_event: &gdk::EventButton) -> Result<()> { let button_mask = match button_event.button() { - 1 => INPUT_POINT_LEFT, - 2 => INPUT_POINT_RIGHT, - 3 => INPUT_POINT_MIDDLE, + GTK_INPUT_BUTTON_LEFT => INPUT_POINT_LEFT, + GTK_INPUT_BUTTON_RIGHT => INPUT_POINT_RIGHT, + GTK_INPUT_BUTTON_MIDDLE => INPUT_POINT_MIDDLE, + GTK_INPUT_BUTTON_BACK => INPUT_POINT_BACK, + GTK_INPUT_BUTTON_FORWARD => INPUT_POINT_FORWARD, _ => return Ok(()), }; trace::gtk_pointer_callback(&button_mask); match button_event.event_type() { gdk::EventType::ButtonRelease => { - input_button(button_mask as u32, false)?; + input_button(button_mask, false)?; input_point_sync() } gdk::EventType::ButtonPress => { - input_button(button_mask as u32, true)?; + input_button(button_mask, true)?; input_point_sync() } gdk::EventType::DoubleButtonPress => { - press_mouse(button_mask as u32)?; - press_mouse(button_mask as u32) + press_mouse(button_mask)?; + press_mouse(button_mask) } _ => Ok(()), } diff --git a/ui/src/input.rs b/ui/src/input.rs index c41ba1fc6..59f2b931f 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -24,14 +24,16 @@ use util::bitmap::Bitmap; // Logical window size for mouse. pub const ABS_MAX: u64 = 0x7fff; // Event type of Point. -pub const INPUT_POINT_LEFT: u8 = 0x01; -pub const INPUT_POINT_MIDDLE: u8 = 0x02; -pub const INPUT_POINT_RIGHT: u8 = 0x04; -pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x08; -pub const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x10; -pub const INPUT_BUTTON_WHEEL_LEFT: u32 = 0x20; -pub const INPUT_BUTTON_WHEEL_RIGHT: u32 = 0x40; -pub const INPUT_BUTTON_MAX_NUM: u32 = 7; +pub const INPUT_POINT_LEFT: u32 = 0x01; +pub const INPUT_POINT_RIGHT: u32 = 0x02; +pub const INPUT_POINT_MIDDLE: u32 = 0x04; +pub const INPUT_POINT_BACK: u32 = 0x08; +pub const INPUT_POINT_FORWARD: u32 = 0x10; +pub const INPUT_BUTTON_WHEEL_UP: u32 = 0x20; +pub const INPUT_BUTTON_WHEEL_DOWN: u32 = 0x40; +pub const INPUT_BUTTON_WHEEL_LEFT: u32 = 0x80; +pub const INPUT_BUTTON_WHEEL_RIGHT: u32 = 0x100; +pub const INPUT_BUTTON_MASK: u32 = 0x1f; // ASCII value. pub const ASCII_A: i32 = 65; diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs index 69dc7f001..ed217f539 100755 --- a/ui/src/ohui_srv/msg.rs +++ b/ui/src/ohui_srv/msg.rs @@ -21,6 +21,11 @@ pub const CLIENT_WHEEL_UP: u32 = 0x1; pub const CLIENT_WHEEL_DOWN: u32 = 0x2; pub const CLIENT_WHEEL_LEFT: u32 = 0x3; pub const CLIENT_WHEEL_RIGHT: u32 = 0x4; +pub const CLIENT_MOUSE_BUTTON_LEFT: u32 = 0x0; +pub const CLIENT_MOUSE_BUTTON_RIGHT: u32 = 0x1; +pub const CLIENT_MOUSE_BUTTON_MIDDLE: u32 = 0x2; +pub const CLIENT_MOUSE_BUTTON_BACK: u32 = 0x3; +pub const CLIENT_MOUSE_BUTTON_FORWARD: u32 = 0x4; pub const EVENT_MSG_HDR_SIZE: u32 = size_of::() as u32; #[repr(C)] diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 5f40ff079..e734bc733 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -23,7 +23,8 @@ use crate::{ input::{ self, get_kbd_led_state, input_button, input_move_abs, input_point_sync, keyboard_update, release_all_key, trigger_key, Axis, ABS_MAX, CAPS_LOCK_LED, INPUT_BUTTON_WHEEL_DOWN, - INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, + INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, + INPUT_POINT_FORWARD, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_CAPS_LOCK, KEYCODE_NUM_LOCK, KEYCODE_SCR_LOCK, NUM_LOCK_LED, SCROLL_LOCK_LED, }, keycode::{DpyMod, KeyCode}, @@ -211,7 +212,15 @@ impl OhUiMsgHandler { } fn handle_mouse_button(&self, mb: &MouseButtonEvent) -> Result<()> { - let (btn, action) = (mb.button, mb.btn_action); + let (msg_btn, action) = (mb.button, mb.btn_action); + let btn = match msg_btn { + CLIENT_MOUSE_BUTTON_LEFT => INPUT_POINT_LEFT, + CLIENT_MOUSE_BUTTON_RIGHT => INPUT_POINT_RIGHT, + CLIENT_MOUSE_BUTTON_MIDDLE => INPUT_POINT_MIDDLE, + CLIENT_MOUSE_BUTTON_FORWARD => INPUT_POINT_FORWARD, + CLIENT_MOUSE_BUTTON_BACK => INPUT_POINT_BACK, + _ => bail!("Invalid mouse button number {}", msg_btn), + }; match action { CLIENT_PRESS_BTN => self.state.lock().unwrap().press_btn(btn), CLIENT_RELEASE_BTN => self.state.lock().unwrap().release_btn(btn), diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index a23b945d6..347e7d580 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -32,8 +32,9 @@ use crate::{ input::{ input_button, input_move_abs, input_point_sync, key_event, keyboard_modifier_get, keyboard_state_reset, update_key_state, Axis, KeyboardModifier, ABS_MAX, ASCII_A, ASCII_Z, - INPUT_BUTTON_MAX_NUM, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_1, - KEYCODE_9, UPPERCASE_TO_LOWERCASE, + INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, + INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, + INPUT_POINT_RIGHT, KEYCODE_1, KEYCODE_9, UPPERCASE_TO_LOWERCASE, }, pixman::{bytes_per_pixel, get_image_height, get_image_width, PixelFormat}, utils::BuffPool, @@ -70,6 +71,19 @@ const ENCODING_DESKTOP_RESIZE_EXT: i32 = -308; const ENCODING_ALPHA_CURSOR: i32 = -314; const ENCODING_WMVI: i32 = 1464686185; +const VNC_INPUT_BUTTON_LEFT: u8 = 0x01; +const VNC_INPUT_BUTTON_MIDDLE: u8 = 0x02; +const VNC_INPUT_BUTTON_RIGHT: u8 = 0x04; +const VNC_INPUT_BUTTON_WHEEL_UP: u8 = 0x08; +const VNC_INPUT_BUTTON_WHEEL_DOWN: u8 = 0x10; +const VNC_INPUT_BUTTON_WHEEL_LEFT: u8 = 0x20; +const VNC_INPUT_BUTTON_WHEEL_RIGHT: u8 = 0x40; +// NOTE: VNC only affords 8 bits for mouse button. Button "back" +// occupies bit7, while there is no bit for button "forward". +// Here just support side button "back" only. +const VNC_INPUT_BUTTON_BACK: u8 = 0x80; +const VNC_INPUT_BUTTON_MAX_NUM: u32 = 8; + /// This trait is used to send bytes, /// the return is the total number of bytes sented. pub trait IoOperations { @@ -994,19 +1008,24 @@ impl ClientIoHandler { let new_button = buf[1]; let last_button = self.client.client_dpm.lock().unwrap().last_button; if last_button != new_button { - for bit in 0..INPUT_BUTTON_MAX_NUM { + for bit in 0..VNC_INPUT_BUTTON_MAX_NUM { let button_mask = 1 << bit; if last_button & button_mask == new_button & button_mask { continue; } let button = match button_mask { - INPUT_POINT_LEFT => 0x01, - INPUT_POINT_MIDDLE => 0x04, - INPUT_POINT_RIGHT => 0x02, - _ => button_mask, + VNC_INPUT_BUTTON_LEFT => INPUT_POINT_LEFT, + VNC_INPUT_BUTTON_RIGHT => INPUT_POINT_RIGHT, + VNC_INPUT_BUTTON_MIDDLE => INPUT_POINT_MIDDLE, + VNC_INPUT_BUTTON_WHEEL_UP => INPUT_BUTTON_WHEEL_UP, + VNC_INPUT_BUTTON_WHEEL_DOWN => INPUT_BUTTON_WHEEL_DOWN, + VNC_INPUT_BUTTON_WHEEL_RIGHT => INPUT_BUTTON_WHEEL_RIGHT, + VNC_INPUT_BUTTON_WHEEL_LEFT => INPUT_BUTTON_WHEEL_LEFT, + VNC_INPUT_BUTTON_BACK => INPUT_POINT_BACK, + _ => button_mask as u32, }; - input_button(button as u32, new_button & button_mask != 0)?; + input_button(button, new_button & button_mask != 0)?; } self.client.client_dpm.lock().unwrap().last_button = new_button; } -- Gitee From 09248eef7e749efe5ce46374d9c75f178d26cbe1 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 1 Apr 2024 15:24:16 +0800 Subject: [PATCH 1709/2187] Scream: fix wrong spelling in log Related log was misspelt, fix it. Signed-off-by: zhanghan64 --- machine_manager/src/machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index 2e79e58d9..f0f00aa34 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -200,7 +200,7 @@ pub trait DeviceInterface { /// Control OH audio's control authority. fn switch_audio_record(&self, _authorized: String) -> Response { Response::create_response( - serde_json::to_value("switch_audi_record not supported for VM".to_string()).unwrap(), + serde_json::to_value("switch_audio_record not supported for VM".to_string()).unwrap(), None, ) } -- Gitee From f0394bfe43aaa47940e52fc3121eb74495d775b5 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Sun, 7 Apr 2024 10:10:04 +0800 Subject: [PATCH 1710/2187] UI: fix default surface size If virtio-gpu device goes back to VGA mode, the size of framebuffer would become DEFAULT_SURFACE_WIDTH * DEFAULT_SURFACE_HEIGHT. So let's create the msg surface with the same size as VGA framebuffer's. Signed-off-by: zhanghan64 --- ui/src/console.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/console.rs b/ui/src/console.rs index 380d1b7e9..97dc06e98 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -407,8 +407,8 @@ pub fn display_replace_surface( if surface.is_none() { // Create a place holder message. locked_con.surface = create_msg_surface( - locked_con.width, - locked_con.height, + DEFAULT_SURFACE_WIDTH, + DEFAULT_SURFACE_HEIGHT, "Display is not active.".to_string(), ); } else { -- Gitee From d5601448af32b7c215038a994f998c5a1db682a5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 10 Apr 2024 19:36:12 +0800 Subject: [PATCH 1711/2187] kvm/sve: Skip SVE state when save and restore vcpu state State save and restore is not supported for SVE for now, so we just skip it. Signed-off-by: Keqian Zhu --- hypervisor/src/kvm/aarch64/mod.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 010499a82..caac3999f 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -322,9 +322,12 @@ impl KvmCpu { core_regs.spsr[i] = self.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; } - for i in 0..KVM_NR_FP_REGS as usize { - core_regs.fp_regs.vregs[i] = - self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; + // State save and restore is not supported for SVE for now, so we just skip it. + if self.kvi.lock().unwrap().features[0] & (1 << kvm_bindings::KVM_ARM_VCPU_SVE) == 0 { + for i in 0..KVM_NR_FP_REGS as usize { + core_regs.fp_regs.vregs[i] = + self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; + } } core_regs.fp_regs.fpsr = @@ -364,11 +367,14 @@ impl KvmCpu { self.set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; } - for i in 0..KVM_NR_FP_REGS as usize { - self.set_one_reg( - Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), - core_regs.fp_regs.vregs[i], - )?; + // State save and restore is not supported for SVE for now, so we just skip it. + if self.kvi.lock().unwrap().features[0] & (1 << kvm_bindings::KVM_ARM_VCPU_SVE) == 0 { + for i in 0..KVM_NR_FP_REGS as usize { + self.set_one_reg( + Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), + core_regs.fp_regs.vregs[i], + )?; + } } self.set_one_reg( -- Gitee From c630791b6fd0395b5519663eeb600ce0208dc7a7 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 09:12:53 +0800 Subject: [PATCH 1712/2187] address_space: Optimize performance of get_address_map() The old code will generate a temp vec and append the temp vec to final vec, which will cause unnecessary memory allocation, memcopy and free. Signed-off-by: Keqian Zhu --- address_space/src/address_space.rs | 12 ++++++++---- devices/src/usb/xhci/xhci_controller.rs | 6 ++---- virtio/src/lib.rs | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index b8e9ccb2e..ae18dd86c 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -501,10 +501,14 @@ impl AddressSpace { /// /// * `addr` - Guest address. /// * `count` - Memory needed length - pub fn get_address_map(&self, addr: GuestAddress, count: u64) -> Result> { + pub fn get_address_map( + &self, + addr: GuestAddress, + count: u64, + res: &mut Vec, + ) -> Result<()> { let mut len = count; let mut start = addr; - let mut hva_iovec = Vec::new(); loop { let io_vec = self @@ -516,14 +520,14 @@ impl AddressSpace { .with_context(|| format!("Map iov base {:x?}, iov len {:?} failed", addr, count))?; start = start.unchecked_add(io_vec.iov_len); len -= io_vec.iov_len; - hva_iovec.push(io_vec); + res.push(io_vec); if len == 0 { break; } } - Ok(hva_iovec) + Ok(()) } /// Return the host address according to the given `GuestAddress` from cache. diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 1d0a005dd..178aafc88 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1925,10 +1925,8 @@ impl XhciDevice { trb.parameter }; - let mut hvas = self - .mem_space - .get_address_map(GuestAddress(dma_addr), chunk as u64)?; - vec.append(&mut hvas); + self.mem_space + .get_address_map(GuestAddress(dma_addr), chunk as u64, &mut vec)?; } } let (_, ep_number) = endpoint_id_to_number(locked_xfer.epid as u8); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 3caf4cfbb..9ef9dde8b 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -793,7 +793,8 @@ pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) let mut end: usize = 0; for iov in iovec { - let addr_map = mem_space.get_address_map(iov.addr, iov.len as u64)?; + let mut addr_map = Vec::new(); + mem_space.get_address_map(iov.addr, iov.len as u64, &mut addr_map)?; for addr in addr_map.into_iter() { end = cmp::min(start + addr.iov_len as usize, buf.len()); mem_to_buf(&mut buf[start..end], addr.iov_base)?; @@ -839,11 +840,10 @@ fn gpa_hva_iovec_map( mem_space: &AddressSpace, ) -> Result<(u64, Vec)> { let mut iov_size = 0; - let mut hva_iovec = Vec::new(); + let mut hva_iovec = Vec::with_capacity(gpa_elemiovec.len()); for elem in gpa_elemiovec.iter() { - let mut hva_vec = mem_space.get_address_map(elem.addr, elem.len as u64)?; - hva_iovec.append(&mut hva_vec); + mem_space.get_address_map(elem.addr, elem.len as u64, &mut hva_iovec)?; iov_size += elem.len as u64; } -- Gitee From 06155e85eff41d49d011226276c041842831f106 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 09:24:28 +0800 Subject: [PATCH 1713/2187] virtio/block: Optimize request processing Pre-size req_queue vec to prevent memory reallocation, memcopy and free. Signed-off-by: Keqian Zhu --- virtio/src/device/block.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 3b99ae7c6..6eb7ddcc7 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -557,7 +557,8 @@ impl BlockIoHandler { } fn process_queue_internal(&mut self) -> Result { - let mut req_queue = Vec::new(); + let queue_size = self.queue.lock().unwrap().vring.actual_size() as usize; + let mut req_queue = Vec::with_capacity(queue_size); let mut done = false; loop { @@ -596,7 +597,7 @@ impl BlockIoHandler { continue; } // Avoid bogus guest stuck IO thread. - if req_queue.len() >= queue.vring.actual_size() as usize { + if req_queue.len() >= queue_size { bail!("The front driver may be damaged, avail requests more than queue size"); } req_queue.push(req); -- Gitee From d251692be7e0b971bec5c6f5f747e8489a028084 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 11:06:07 +0800 Subject: [PATCH 1714/2187] virtio/block: Switch time to iteration for stuck prevention The time based approach does heavy effect on performance. Signed-off-by: Keqian Zhu --- virtio/src/device/block.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 6eb7ddcc7..a7719cf35 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -18,7 +18,6 @@ use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex}; -use std::time::Instant; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; @@ -73,8 +72,8 @@ const MAX_NUM_MERGE_REQS: u16 = 32; const MAX_NUM_MERGE_IOVS: usize = 1024; /// Max number bytes of a merged request. const MAX_NUM_MERGE_BYTES: u64 = i32::MAX as u64; -/// Max time for every round of process queue. -const MAX_MILLIS_TIME_PROCESS_QUEUE: u16 = 100; +/// Max iteration for every round of process queue. +const MAX_ITERATION_PROCESS_QUEUE: u16 = 10; /// Max number sectors of per request. const MAX_REQUEST_SECTORS: u32 = u32::MAX >> SECTOR_SHIFT; @@ -654,7 +653,7 @@ impl BlockIoHandler { trace::virtio_blk_process_queue_suppress_notify(len); let mut done = false; - let start_time = Instant::now(); + let mut iteration = 0; while self .queue @@ -665,8 +664,8 @@ impl BlockIoHandler { != 0 { // Do not stuck IO thread. - let now = Instant::now(); - if (now - start_time).as_millis() > MAX_MILLIS_TIME_PROCESS_QUEUE as u128 { + iteration += 1; + if iteration > MAX_ITERATION_PROCESS_QUEUE { // Make sure we can come back. self.queue_evt.write(1)?; break; -- Gitee From cd3e8dd1b4bb9bd97c603187c2b7793d2c1582ba Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 11 Apr 2024 11:16:16 +0800 Subject: [PATCH 1715/2187] virtio/queue: Pre-size the element iovec So as to optimize virtio request parsing. Signed-off-by: Keqian Zhu --- virtio/src/queue/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index 5636c7d79..7f581a044 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -20,6 +20,7 @@ use anyhow::{bail, Result}; use vmm_sys_util::eventfd::EventFd; use address_space::{AddressSpace, GuestAddress, RegionCache}; +use machine_manager::config::DEFAULT_VIRTQUEUE_SIZE; /// Split Virtqueue. pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; @@ -81,8 +82,8 @@ impl Element { Element { index, desc_num: 0, - out_iovec: Vec::new(), - in_iovec: Vec::new(), + out_iovec: Vec::with_capacity(DEFAULT_VIRTQUEUE_SIZE.into()), + in_iovec: Vec::with_capacity(DEFAULT_VIRTQUEUE_SIZE.into()), } } -- Gitee From 883312b80c2cf4bb35bb90b365cf915ca4ce001d Mon Sep 17 00:00:00 2001 From: dskr99 Date: Mon, 4 Mar 2024 12:50:47 +0300 Subject: [PATCH 1716/2187] Correctly fill FDT for pflash We have 2 flash devices: [flash0_base, flash0_size], [flash1_base, flash1_size]. Before the patch, there occured the following: we called generate_flash_device_node() for only flash0. And it filled the FDT-node with data bank0: flash0_base, flash0_size bank1: flash0_base + flash0_size, flash0_size. this formed a memory region that covers range [flash0_base, 2 * flash0_size]. Because of coincidence, this memory region covered both flashes. But it's not a strict way. Just fill 2 FDT nodes flash0: bank0: flash0_base, flash0_size flash1: bank0: flash1_base, flash1_size Signed-off-by: Dmitry Skorodumov --- machine/src/aarch64/fdt.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index eb770eb5e..76815b170 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -138,10 +138,7 @@ fn generate_flash_device_node(fdt: &mut FdtBuilder, res: &SysRes) -> Result<()> let node = format!("flash@{:x}", flash_base); let flash_node_dep = fdt.begin_node(&node)?; fdt.set_property_string("compatible", "cfi-flash")?; - fdt.set_property_array_u64( - "reg", - &[flash_base, flash_size, flash_base + flash_size, flash_size], - )?; + fdt.set_property_array_u64("reg", &[flash_base, flash_size])?; fdt.set_property_u32("bank-width", 4)?; fdt.end_node(flash_node_dep) } @@ -268,7 +265,6 @@ impl CompileFDTHelper for MachineBase { fdt.set_property_string("method", "hvc")?; fdt.end_node(psci_node_dep)?; - let mut pflash_cnt = 0; for dev in self.sysbus.devices.iter() { let locked_dev = dev.lock().unwrap(); match locked_dev.sysbusdev_base().dev_type { @@ -285,12 +281,7 @@ impl CompileFDTHelper for MachineBase { generate_fwcfg_device_node(fdt, &locked_dev.sysbusdev_base().res)?; } SysBusDevType::Flash => { - // Two pflash devices are created, but only one pflash node is required in the fdt table. - // Thereforce, the second pflash device needs to be skipped. - if pflash_cnt == 0 { - generate_flash_device_node(fdt, &locked_dev.sysbusdev_base().res)?; - pflash_cnt += 1; - } + generate_flash_device_node(fdt, &locked_dev.sysbusdev_base().res)?; } _ => (), } -- Gitee From 80fe1f723f0e55cc328ca5d5eb88985c58384fd6 Mon Sep 17 00:00:00 2001 From: dskr99 Date: Mon, 4 Mar 2024 17:10:16 +0300 Subject: [PATCH 1717/2187] Make flash-region size match actual size While creating memory-region of BIOS, we the PFlash::new() receives maximum size for the mem-region. But we allow size of Bios be less, than maximum. It looks more correct to create memory region that matches bios-size. This affect nothing, but attempts to make code better. Signed-off-by: Dmitry Skorodumov --- devices/src/legacy/pflash.rs | 40 +++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 88fdbad0c..e10039228 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -22,7 +22,8 @@ use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; -use util::num_ops::{deposit_u32, extract_u32, read_data_u32, write_data_u32}; +use util::num_ops::{deposit_u32, extract_u32, read_data_u32, round_up, write_data_u32}; +use util::unix::host_page_size; pub struct PFlash { base: SysBusDevBase, @@ -59,6 +60,27 @@ pub struct PFlash { } impl PFlash { + fn flash_region_size( + region_max_size: u64, + backend: &Option, + read_only: bool, + ) -> Result { + // We don't have to occupy the whole memory region. + // If flash is read-only, expose just real data size, + // rounded up to page_size + if let Some(fd) = backend.as_ref() { + let len = fd.metadata().unwrap().len(); + if len > region_max_size || len == 0 || (!read_only && len != region_max_size) { + bail!( + "Invalid flash file: Region size 0x{region_max_size:X}, file size 0x{len:X}; read_only {read_only}" + ); + } + Ok(round_up(len, host_page_size()).unwrap()) + } else { + Ok(region_max_size) + } + } + /// Construct function of PFlash device. /// /// # Arguments @@ -75,9 +97,9 @@ impl PFlash { /// Return Error when /// * block-length is zero. /// * PFlash size is zero. - /// * file size is smaller than PFlash size. + /// * flash is writable and file size is smaller than region_max_size. pub fn new( - size: u64, + region_max_size: u64, backend: &Option, block_len: u32, bank_width: u32, @@ -87,18 +109,11 @@ impl PFlash { if block_len == 0 { bail!("PFlash: block-length is zero which is invalid."); } + let size = Self::flash_region_size(region_max_size, backend, read_only)?; let blocks_per_device: u32 = size as u32 / block_len; if blocks_per_device == 0 { bail!("PFlash: num-blocks is zero which is invalid."); } - if let Some(fd) = backend.as_ref() { - let len = fd.metadata().unwrap().len(); - if len > size || len == 0 || (!read_only && len != size) { - bail!( - "Invalid flash file: Region size 0x{size:X}, file size 0x{len:X}; read_only {read_only}" - ); - } - } let num_devices: u32 = if device_width == 0 { 1 } else { @@ -195,9 +210,10 @@ impl PFlash { mut self, sysbus: &mut SysBus, region_base: u64, - region_size: u64, + region_max_size: u64, backend: Option, ) -> Result<()> { + let region_size = Self::flash_region_size(region_max_size, &backend, self.read_only)?; self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| "Failed to allocate system resource for PFlash.")?; -- Gitee From 6514021598a826beb432248db169f8a15369cccd Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 24 Nov 2023 12:51:48 +0300 Subject: [PATCH 1718/2187] uas: Add support for USB UASP protocol Add USB Attached SCSI (UAS) device. Now it has support for both high and super speeds. High speed support will be gradually removed with introduction of streams support on the XHCI controller. Signed-off-by: Stanislav Goriainov --- devices/src/usb/mod.rs | 1 + devices/src/usb/uas.rs | 1268 +++++++++++++++++++++++++++++ machine/src/lib.rs | 11 +- machine_manager/src/config/usb.rs | 63 ++ trace/trace_event/usb.toml | 102 +++ 5 files changed, 1443 insertions(+), 2 deletions(-) create mode 100644 devices/src/usb/uas.rs diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index e461b7577..44f5d88ec 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -20,6 +20,7 @@ pub mod hid; pub mod keyboard; pub mod storage; pub mod tablet; +pub mod uas; #[cfg(feature = "usb_host")] pub mod usbhost; pub mod xhci; diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs new file mode 100644 index 000000000..552fa15c9 --- /dev/null +++ b/devices/src/usb/uas.rs @@ -0,0 +1,1268 @@ +// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::array; +use std::cmp::min; +use std::collections::{HashMap, VecDeque}; +use std::mem::size_of; +use std::sync::{Arc, Mutex, MutexGuard, Weak}; + +use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, error, info, warn}; +use machine_manager::config::{DriveFile, UsbUasConfig}; +use once_cell::sync::Lazy; +use util::byte_code::ByteCode; + +use super::config::*; +use super::descriptor::{ + UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, + UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, + UsbSuperSpeedEndpointCompDescriptor, +}; +use super::xhci::xhci_controller::XhciDevice; +use super::{ + UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, + USB_DEVICE_BUFFER_DEFAULT_LEN, +}; + +use crate::{ + ScsiBus::{ + scsi_cdb_xfer, scsi_cdb_xfer_mode, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, + ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, + SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, SCSI_SENSE_OVERLAPPED_COMMANDS, + }, + ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, +}; + +// Size of UasIUBody +const UAS_IU_BODY_SIZE: usize = 30; + +// Size of cdb in UAS Command IU +const UAS_COMMAND_CDB_SIZE: usize = 16; + +// CRC16 of "STRATOVIRT" +const USB_UAS_VENDOR_ID: u16 = 0xB74C; + +// UAS Pipe IDs +const UAS_PIPE_ID_COMMAND: u8 = 0x01; +const UAS_PIPE_ID_STATUS: u8 = 0x02; +const UAS_PIPE_ID_DATA_IN: u8 = 0x03; +const UAS_PIPE_ID_DATA_OUT: u8 = 0x04; + +// UAS Streams Attributes +const UAS_MAX_STREAMS_BM_ATTR: u8 = 0; +const UAS_MAX_STREAMS: usize = 1 << UAS_MAX_STREAMS_BM_ATTR; + +// UAS IU IDs +const UAS_IU_ID_COMMAND: u8 = 0x01; +const UAS_IU_ID_SENSE: u8 = 0x03; +const UAS_IU_ID_RESPONSE: u8 = 0x04; +const UAS_IU_ID_TASK_MGMT: u8 = 0x05; +const UAS_IU_ID_READ_READY: u8 = 0x06; +const UAS_IU_ID_WRITE_READY: u8 = 0x07; + +// UAS Response Codes +const _UAS_RC_TMF_COMPLETE: u8 = 0x00; +const _UAS_RC_INVALID_IU: u8 = 0x02; +const UAS_RC_TMF_NOT_SUPPORTED: u8 = 0x04; +const _UAS_RC_TMF_FAILED: u8 = 0x05; +const _UAS_RC_TMF_SUCCEEDED: u8 = 0x08; +const _UAS_RC_INCORRECT_LUN: u8 = 0x09; +const _UAS_RC_OVERLAPPED_TAG: u8 = 0x0A; + +// UAS Task Management Functions +const _UAS_TMF_ABORT_TASK: u8 = 0x01; +const _UAS_TMF_ABORT_TASK_SET: u8 = 0x02; +const _UAS_TMF_CLEAR_TASK_SET: u8 = 0x04; +const _UAS_TMF_LOGICAL_UNIT_RESET: u8 = 0x08; +const _UAS_TMF_I_T_NEXUS_RESET: u8 = 0x10; +const _UAS_TMF_CLEAR_ACA: u8 = 0x40; +const _UAS_TMF_QUERY_TASK: u8 = 0x80; +const _UAS_TMF_QUERY_TASK_SET: u8 = 0x81; +const _UAS_TMF_QUERY_ASYNC_EVENT: u8 = 0x82; + +// USB Pipe Usage Descriptor +const USB_DT_PIPE_USAGE: u8 = 0x24; +const USB_DT_PIPE_USAGE_SIZE: u8 = 4; + +pub struct UsbUas { + base: UsbDeviceBase, + scsi_bus: Arc>, + scsi_device: Arc>, + commands_high: VecDeque, + statuses_high: VecDeque>>, + commands_super: [Option; UAS_MAX_STREAMS + 1], + statuses_super: [Option>>; UAS_MAX_STREAMS + 1], + data: [Option>>; UAS_MAX_STREAMS + 1], + data_ready_sent: bool, +} + +#[derive(Debug)] +enum UsbUasStringId { + #[allow(unused)] + Invalid = 0, + Manufacturer = 1, + Product = 2, + SerialNumber = 3, + ConfigHigh = 4, + ConfigSuper = 5, +} + +const UAS_DESC_STRINGS: [&str; 6] = [ + "", + "StratoVirt", + "StratoVirt USB Uas", + "5", + "High speed config (usb 2.0)", + "Super speed config (usb 3.0)", +]; + +struct UasRequest { + data: Option>>, + status: Arc>, + iu: UasIU, + completed: bool, +} + +impl ScsiRequestOps for UasRequest { + fn scsi_request_complete_cb( + &mut self, + scsi_status: u8, + scsi_sense: Option, + ) -> Result<()> { + let tag = u16::from_be(self.iu.header.tag); + let sense = scsi_sense.unwrap_or(SCSI_SENSE_NO_SENSE); + UsbUas::fill_sense(&mut self.status.lock().unwrap(), tag, scsi_status, &sense); + self.complete(); + Ok(()) + } +} + +#[derive(Debug, PartialEq, Eq)] +enum UasPacketStatus { + Completed = 0, + Pending = 1, +} + +impl From for UasPacketStatus { + fn from(status: bool) -> Self { + match status { + true => Self::Completed, + false => Self::Pending, + } + } +} + +#[allow(non_snake_case)] +#[repr(C, packed)] +#[derive(Copy, Clone, Debug, Default)] +struct UsbPipeUsageDescriptor { + bLength: u8, + bDescriptorType: u8, + bPipeId: u8, + bReserved: u8, +} + +impl ByteCode for UsbPipeUsageDescriptor {} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +struct UasIUHeader { + id: u8, + reserved: u8, + tag: u16, +} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +struct UasIUCommand { + prio_task_attr: u8, // 6:3 priority, 2:0 task attribute + reserved_1: u8, + add_cdb_len: u8, + reserved_2: u8, + lun: u64, + cdb: [u8; UAS_COMMAND_CDB_SIZE], + add_cdb: [u8; 1], // not supported by stratovirt +} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +struct UasIUSense { + status_qualifier: u16, + status: u8, + reserved: [u8; 7], + sense_length: u16, + sense_data: [u8; 18], +} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +struct UasIUResponse { + add_response_info: [u8; 3], + response_code: u8, +} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +struct UasIUTaskManagement { + function: u8, + reserved: u8, + task_tag: u16, + lun: u64, +} + +#[repr(C, packed)] +#[derive(Clone, Copy)] +union UasIUBody { + command: UasIUCommand, + sense: UasIUSense, + response: UasIUResponse, + task_management: UasIUTaskManagement, + raw_data: [u8; UAS_IU_BODY_SIZE], +} + +impl Default for UasIUBody { + fn default() -> Self { + Self { + raw_data: [0; UAS_IU_BODY_SIZE], + } + } +} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy)] +struct UasIU { + header: UasIUHeader, + body: UasIUBody, +} + +impl ByteCode for UasIU {} + +static DESC_DEVICE_UAS_SUPER: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDevice { + device_desc: UsbDeviceDescriptor { + bLength: USB_DT_DEVICE_SIZE, + bDescriptorType: USB_DT_DEVICE, + bcdUSB: 0x0300, + bDeviceClass: 0, + bDeviceSubClass: 0, + bDeviceProtocol: 0, + bMaxPacketSize0: 9, + idVendor: USB_UAS_VENDOR_ID, + idProduct: 0x0001, + bcdDevice: 0x0, + iManufacturer: UsbUasStringId::Manufacturer as u8, + iProduct: UsbUasStringId::Product as u8, + iSerialNumber: UsbUasStringId::SerialNumber as u8, + bNumConfigurations: 1, + }, + configs: vec![Arc::new(UsbDescConfig { + config_desc: UsbConfigDescriptor { + bLength: USB_DT_CONFIG_SIZE, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: 0, + bNumInterfaces: 1, + bConfigurationValue: 1, + iConfiguration: UsbUasStringId::ConfigSuper as u8, + bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, + bMaxPower: 50, + }, + iad_desc: vec![], + interfaces: vec![DESC_IFACE_UAS_SUPER.clone()], + })], + }) +}); + +static DESC_IFACE_UAS_SUPER: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0, + bAlternateSetting: 0, + bNumEndpoints: 4, + bInterfaceClass: USB_CLASS_MASS_STORAGE, + bInterfaceSubClass: 0x06, // SCSI + bInterfaceProtocol: 0x62, // UAS + iInterface: 0, + }, + other_desc: vec![], + endpoints: vec![ + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_COMMAND, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 1024, + bInterval: 0, + }, + extra: [ + UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 15, + bmAttributes: 0, + wBytesPerInterval: 0, + } + .as_bytes(), + UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_COMMAND, + bReserved: 0, + } + .as_bytes(), + ] + .concat() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_STATUS, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 1024, + bInterval: 0, + }, + extra: [ + UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 15, + bmAttributes: UAS_MAX_STREAMS_BM_ATTR, + wBytesPerInterval: 0, + } + .as_bytes(), + UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_STATUS, + bReserved: 0, + } + .as_bytes(), + ] + .concat() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_DATA_IN, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 1024, + bInterval: 0, + }, + extra: [ + UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 15, + bmAttributes: UAS_MAX_STREAMS_BM_ATTR, + wBytesPerInterval: 0, + } + .as_bytes(), + UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_DATA_IN, + bReserved: 0, + } + .as_bytes(), + ] + .concat() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_DATA_OUT, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 1024, + bInterval: 0, + }, + extra: [ + UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 15, + bmAttributes: UAS_MAX_STREAMS_BM_ATTR, + wBytesPerInterval: 0, + } + .as_bytes(), + UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_DATA_OUT, + bReserved: 0, + } + .as_bytes(), + ] + .concat() + .to_vec(), + }), + ], + }) +}); + +static DESC_DEVICE_UAS_HIGH: Lazy> = Lazy::new(|| { + Arc::new(UsbDescDevice { + device_desc: UsbDeviceDescriptor { + bLength: USB_DT_DEVICE_SIZE, + bDescriptorType: USB_DT_DEVICE, + bcdUSB: 0x0200, + bDeviceClass: 0, + bDeviceSubClass: 0, + bDeviceProtocol: 0, + bMaxPacketSize0: 64, + idVendor: USB_UAS_VENDOR_ID, + idProduct: 0x0002, + bcdDevice: 0x0001, + iManufacturer: UsbUasStringId::Manufacturer as u8, + iProduct: UsbUasStringId::Product as u8, + iSerialNumber: UsbUasStringId::SerialNumber as u8, + bNumConfigurations: 1, + }, + configs: vec![Arc::new(UsbDescConfig { + config_desc: UsbConfigDescriptor { + bLength: USB_DT_CONFIG_SIZE, + bDescriptorType: USB_DT_CONFIGURATION, + wTotalLength: 0, + bNumInterfaces: 1, + bConfigurationValue: 1, + iConfiguration: UsbUasStringId::ConfigHigh as u8, + bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, + bMaxPower: 0xFA, + }, + iad_desc: vec![], + interfaces: vec![DESC_IFACE_EMPTY.clone(), DESC_IFACE_UAS_HIGH.clone()], + })], + }) +}); + +static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0, + bAlternateSetting: 1, + bNumEndpoints: 4, + bInterfaceClass: USB_CLASS_MASS_STORAGE, + bInterfaceSubClass: 0x06, // SCSI + bInterfaceProtocol: 0x62, // UAS + iInterface: 0, + }, + other_desc: vec![], + endpoints: vec![ + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_COMMAND, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 512, + bInterval: 0xFF, + }, + extra: UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_COMMAND, + bReserved: 0, + } + .as_bytes() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_STATUS, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 512, + bInterval: 0xFF, + }, + extra: UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_STATUS, + bReserved: 0, + } + .as_bytes() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_DATA_IN, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 512, + bInterval: 0xFF, + }, + extra: UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_DATA_IN, + bReserved: 0, + } + .as_bytes() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_DATA_OUT, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 512, + bInterval: 0xFF, + }, + extra: UsbPipeUsageDescriptor { + bLength: USB_DT_PIPE_USAGE_SIZE, + bDescriptorType: USB_DT_PIPE_USAGE, + bPipeId: UAS_PIPE_ID_DATA_OUT, + bReserved: 0, + } + .as_bytes() + .to_vec(), + }), + ], + }) +}); + +// NOTE: Fake BOT interface descriptor is needed here since Windows UASP driver always expects two +// interfaces: both BOT and UASP. It also anticipates the UASP descriptor to be the second one. +// Therefore, the first one can be a BOT storage stub. +static DESC_IFACE_EMPTY: Lazy> = Lazy::new(|| { + Arc::new(UsbDescIface { + interface_desc: UsbInterfaceDescriptor { + bLength: USB_DT_INTERFACE_SIZE, + bDescriptorType: USB_DT_INTERFACE, + bInterfaceNumber: 0, + bAlternateSetting: 0, + bNumEndpoints: 0, + bInterfaceClass: USB_CLASS_MASS_STORAGE, + bInterfaceSubClass: 0x00, // SCSI + bInterfaceProtocol: 0x00, // BOT + iInterface: 0, + }, + other_desc: vec![], + endpoints: vec![], + }) +}); + +fn complete_async_packet(locked_packet: MutexGuard<'_, UsbPacket>) { + if let Some(xfer_ops) = locked_packet.xfer_ops.as_ref() { + if let Some(xfer_ops) = xfer_ops.clone().upgrade() { + drop(locked_packet); + xfer_ops.lock().unwrap().submit_transfer(); + } + } +} + +impl UsbUas { + pub fn new( + uas_config: UsbUasConfig, + drive_files: Arc>>, + ) -> Self { + let scsi_type = match &uas_config.media as &str { + "disk" => SCSI_TYPE_DISK, + _ => SCSI_TYPE_ROM, + }; + + let mut base = UsbDeviceBase::new( + uas_config.id.clone().unwrap(), + USB_DEVICE_BUFFER_DEFAULT_LEN, + ); + + base.speed = match uas_config.speed.as_deref() { + Some("super") => USB_SPEED_SUPER, + _ => USB_SPEED_HIGH, + }; + + Self { + base, + scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), + scsi_device: Arc::new(Mutex::new(ScsiDevice::new( + uas_config.scsi_cfg, + scsi_type, + drive_files, + ))), + commands_high: VecDeque::new(), + commands_super: array::from_fn(|_| None), + statuses_high: VecDeque::new(), + statuses_super: array::from_fn(|_| None), + data: array::from_fn(|_| None), + data_ready_sent: false, + } + } + + fn streams_enabled(&self) -> bool { + self.base.speed == USB_SPEED_SUPER + } + + fn cancel_io(&mut self) { + self.commands_high = VecDeque::new(); + self.commands_super = array::from_fn(|_| None); + self.statuses_high = VecDeque::new(); + self.statuses_super = array::from_fn(|_| None); + self.data = array::from_fn(|_| None); + self.data_ready_sent = false; + } + + fn peek_next_status(&self, stream: usize) -> Option<&Arc>> { + match self.streams_enabled() { + true => self.statuses_super[stream].as_ref(), + false => self.statuses_high.front(), + } + } + + fn take_next_status(&mut self, stream: usize) -> Arc> { + match self.streams_enabled() { + true => self.statuses_super[stream].take().unwrap(), + false => self.statuses_high.pop_front().unwrap(), + } + } + + fn queue_status(&mut self, status: &Arc>, stream: usize) { + match self.streams_enabled() { + true => self.statuses_super[stream] = Some(Arc::clone(status)), + false => self.statuses_high.push_back(Arc::clone(status)), + }; + } + + fn peek_next_command(&self, stream: usize) -> Option<&UasIU> { + match self.streams_enabled() { + true => self.commands_super[stream].as_ref(), + false => self.commands_high.front(), + } + } + + fn take_next_command(&mut self, stream: usize) -> UasIU { + match self.streams_enabled() { + true => self.commands_super[stream].take().unwrap(), + false => self.commands_high.pop_front().unwrap(), + } + } + + fn queue_command(&mut self, command: UasIU, stream: usize) { + match self.streams_enabled() { + true => self.commands_super[stream] = Some(command), + false => self.commands_high.push_back(command), + } + } + + fn handle_iu_command( + &mut self, + iu: &UasIU, + mut uas_request: UasRequest, + ) -> Result { + // SAFETY: iu is guaranteed to be of type command + let add_cdb_len = unsafe { iu.body.command.add_cdb_len }; + let tag = u16::from_be(iu.header.tag); + + if add_cdb_len > 0 { + Self::fill_fake_sense( + &mut uas_request.status.lock().unwrap(), + tag, + &SCSI_SENSE_INVALID_PARAM_VALUE, + ); + uas_request.complete(); + bail!("additional cdb length is not supported"); + } + + if self.streams_enabled() && tag > UAS_MAX_STREAMS as u16 { + Self::fill_fake_sense( + &mut uas_request.status.lock().unwrap(), + tag, + &SCSI_SENSE_INVALID_TAG, + ); + uas_request.complete(); + bail!("invalid tag {}", tag); + } + + if self.streams_enabled() && self.commands_super[tag as usize].is_some() { + Self::fill_fake_sense( + &mut uas_request.status.lock().unwrap(), + tag, + &SCSI_SENSE_OVERLAPPED_COMMANDS, + ); + uas_request.complete(); + bail!("overlapped tag {}", tag); + } + + let (scsi_iovec, scsi_iovec_size) = match uas_request.data.as_ref() { + Some(data) => { + let mut locked_data = data.lock().unwrap(); + let iov_size = locked_data.get_iovecs_size() as u32; + locked_data.actual_length = iov_size; + (locked_data.iovecs.clone(), iov_size) + } + None => (Vec::new(), 0), + }; + + // SAFETY: iu is guaranteed to of type command + let cdb = unsafe { iu.body.command.cdb }; + // SAFETY: iu is guaranteed to of type command + let lun = unsafe { iu.body.command.lun } as u16; + trace::usb_uas_handle_iu_command(self.device_id(), cdb[0]); + let uas_request = Box::new(uas_request); + let scsi_request = ScsiRequest::new( + cdb, + lun, + scsi_iovec, + scsi_iovec_size, + Arc::clone(&self.scsi_device), + uas_request, + ) + .with_context(|| "Failed to create SCSI request.")?; + + if scsi_request.cmd.xfer > scsi_request.datalen + && scsi_request.cmd.mode != ScsiXferMode::ScsiXferNone + { + bail!( + "insufficient buffer provided (requested length {}, provided length {})", + scsi_request.cmd.xfer, + scsi_request.datalen + ); + } + + let scsi_request = match scsi_request.opstype { + EMULATE_SCSI_OPS => scsi_request.emulate_execute(), + _ => scsi_request.execute(), + } + .with_context(|| "Failed to execute SCSI request.")?; + + let upper_request = &mut scsi_request.lock().unwrap().upper_req; + let uas_request = upper_request + .as_mut() + .as_any_mut() + .downcast_mut::() + .unwrap(); + + Ok(uas_request.completed.into()) + } + + fn handle_iu_task_management( + &mut self, + iu: &UasIU, + mut uas_request: UasRequest, + ) -> Result { + let tag = u16::from_be(iu.header.tag); + + if self.streams_enabled() && tag > UAS_MAX_STREAMS as u16 { + Self::fill_fake_sense( + &mut uas_request.status.lock().unwrap(), + tag, + &SCSI_SENSE_INVALID_TAG, + ); + uas_request.complete(); + bail!("invalid tag {}", tag); + } + + if self.streams_enabled() && self.commands_super[tag as usize].is_some() { + Self::fill_fake_sense( + &mut uas_request.status.lock().unwrap(), + tag, + &SCSI_SENSE_OVERLAPPED_COMMANDS, + ); + uas_request.complete(); + bail!("overlapped tag {}", tag); + } + + // SAFETY: iu is guaranteed to be of type task management + let tmf = unsafe { iu.body.task_management.function }; + + #[allow(clippy::match_single_binding)] + match tmf { + _ => { + warn!("UAS {} device unsupported TMF {}.", self.device_id(), tmf); + Self::fill_response( + &mut uas_request.status.lock().unwrap(), + tag, + UAS_RC_TMF_NOT_SUPPORTED, + ); + } + }; + + uas_request.complete(); + Ok(UasPacketStatus::Completed) + } + + fn fill_response(packet: &mut UsbPacket, tag: u16, code: u8) { + let mut iu = UasIU::new(UAS_IU_ID_RESPONSE, tag); + iu.body.response.response_code = code; + let iu_len = size_of::() + size_of::(); + Self::fill_packet(packet, &mut iu, iu_len); + } + + fn fill_sense(packet: &mut UsbPacket, tag: u16, status: u8, sense: &ScsiSense) { + let mut iu = UasIU::new(UAS_IU_ID_SENSE, tag); + // SAFETY: iu is guaranteed to be of type status + let iu_sense = unsafe { &mut iu.body.sense }; + + iu_sense.status = status; + iu_sense.status_qualifier = 0_u16.to_be(); + iu_sense.sense_length = 0_u16.to_be(); + + if status != GOOD { + iu_sense.sense_length = 18_u16.to_be(); + iu_sense.sense_data[0] = 0x71; // Error code: deferred errors + iu_sense.sense_data[2] = sense.key; + iu_sense.sense_data[7] = 10; // Additional sense length: total length - 8 + iu_sense.sense_data[12] = sense.asc; + iu_sense.sense_data[13] = sense.ascq; + } + + let sense_len = iu_sense.sense_length as usize; + let real_sense_len = size_of::() - iu_sense.sense_data.len() + sense_len; + let iu_len = size_of::() + real_sense_len; + trace::usb_uas_fill_sense(status, iu_len, sense_len); + Self::fill_packet(packet, &mut iu, iu_len); + } + + fn fill_fake_sense(packet: &mut UsbPacket, tag: u16, sense: &ScsiSense) { + let mut iu = UasIU::new(UAS_IU_ID_SENSE, tag); + // SAFETY: iu is guaranteed to be of type status + let iu_sense = unsafe { &mut iu.body.sense }; + + iu_sense.status = CHECK_CONDITION; + iu_sense.status_qualifier = 0_u16.to_be(); + iu_sense.sense_length = 18_u16.to_be(); + iu_sense.sense_data[0] = 0x70; // Error code: current errors + iu_sense.sense_data[2] = sense.key; + iu_sense.sense_data[7] = 10; // Additional sense length: total length - 8 + iu_sense.sense_data[12] = sense.asc; + iu_sense.sense_data[13] = sense.ascq; + + let iu_len = size_of::() + size_of::(); + trace::usb_uas_fill_fake_sense(CHECK_CONDITION, iu_len, 18); + Self::fill_packet(packet, &mut iu, iu_len); + } + + fn fill_read_ready(packet: &mut UsbPacket, tag: u16) { + let mut iu = UasIU::new(UAS_IU_ID_READ_READY, tag); + let iu_len = size_of::(); + Self::fill_packet(packet, &mut iu, iu_len); + } + + fn fill_write_ready(packet: &mut UsbPacket, tag: u16) { + let mut iu = UasIU::new(UAS_IU_ID_WRITE_READY, tag); + let iu_len = size_of::(); + Self::fill_packet(packet, &mut iu, iu_len); + } + + fn fill_packet(packet: &mut UsbPacket, iu: &mut UasIU, iu_len: usize) { + let iov_size = packet.get_iovecs_size() as usize; + let iu_len = min(iov_size, iu_len); + trace::usb_uas_fill_packet(iov_size); + packet.transfer_packet(iu.as_mut_bytes(), iu_len); + } + + fn try_start_next_transfer(&mut self, stream: usize) -> UasPacketStatus { + let command = self.peek_next_command(stream); + + if let Some(command) = command { + // SAFETY: iu is guaranteed to be of type command + let cdb = unsafe { &command.body.command.cdb }; + let xfer_len = scsi_cdb_xfer(cdb, Arc::clone(&self.scsi_device)); + trace::usb_uas_try_start_next_transfer(self.device_id(), xfer_len); + + if xfer_len > 0 { + self.try_start_next_data(stream) + } else { + self.try_start_next_non_data(stream) + } + } else { + debug!( + "UAS {} device no inflight command when trying to start the next transfer.", + self.device_id() + ); + UasPacketStatus::Pending + } + } + + fn try_start_next_data(&mut self, stream: usize) -> UasPacketStatus { + let status = self.peek_next_status(stream); + + if status.is_none() { + debug!( + "UAS {} device no inflight status when trying to start the next data transfer.", + self.device_id() + ); + return UasPacketStatus::Pending; + } + + if !self.data_ready_sent { + return self.fill_data_ready(stream); + } + + if self.data[stream].is_some() { + self.start_next_transfer(stream) + } else { + debug!( + "UAS {} device no inflight data when trying to start the next data transfer.", + self.device_id() + ); + UasPacketStatus::Pending + } + } + + fn fill_data_ready(&mut self, stream: usize) -> UasPacketStatus { + // SAFETY: status must have been checked in try_start_next_data + let status = self.take_next_status(stream); + let mut locked_status = status.lock().unwrap(); + + // SAFETY: command must have been checked in try_start_next_transfer + let iu = self.peek_next_command(stream).unwrap(); + let tag = u16::from_be(iu.header.tag); + + // SAFETY: iu is guaranteed to be of type command + let cdb = unsafe { &iu.body.command.cdb }; + let xfer_mode = scsi_cdb_xfer_mode(cdb); + + match xfer_mode { + ScsiXferMode::ScsiXferFromDev => Self::fill_read_ready(&mut locked_status, tag), + ScsiXferMode::ScsiXferToDev => Self::fill_write_ready(&mut locked_status, tag), + ScsiXferMode::ScsiXferNone => { + warn!( + "UAS {} device cannot fill data ready, operation {} is not a data transfer.", + self.device_id(), + cdb[0] + ); + Self::fill_fake_sense(&mut locked_status, tag, &SCSI_SENSE_INVALID_PARAM_VALUE); + } + } + + if locked_status.is_async { + complete_async_packet(locked_status); + } + + self.data_ready_sent = true; + trace::usb_uas_fill_data_ready(self.device_id(), self.data_ready_sent); + UasPacketStatus::Completed + } + + fn try_start_next_non_data(&mut self, stream: usize) -> UasPacketStatus { + let status = self.peek_next_status(stream); + + if status.is_none() { + debug!( + "UAS {} device no inflight status when trying to start the next non-data transfer.", + self.device_id() + ); + return UasPacketStatus::Pending; + } + + self.start_next_transfer(stream) + } + + fn start_next_transfer(&mut self, stream: usize) -> UasPacketStatus { + trace::usb_uas_start_next_transfer(self.device_id(), stream); + + // SAFETY: status must have been checked in try_start_next_data or try_start_next_non_data + let status = self.take_next_status(stream); + + // SAFETY: command must have been checked in try_start_next_transfer + let command = self.take_next_command(stream); + + let mut uas_request = UasRequest::new(&status, &command); + uas_request.data = self.data[stream].take(); + + let result = match command.header.id { + UAS_IU_ID_COMMAND => self.handle_iu_command(&command, uas_request), + UAS_IU_ID_TASK_MGMT => self.handle_iu_task_management(&command, uas_request), + _ => Err(anyhow!("impossible command IU {}", command.header.id)), + }; + + self.data_ready_sent = false; + + if !self.streams_enabled() { + self.try_start_next_transfer(0); + } + + match result { + Ok(result) => result, + Err(err) => { + error!("UAS {} device error: {:#?}.", self.device_id(), err); + UasPacketStatus::Completed + } + } + } +} + +impl UsbDevice for UsbUas { + fn usb_device_base(&self) -> &UsbDeviceBase { + &self.base + } + + fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { + &mut self.base + } + + fn realize(mut self) -> Result>> { + info!("UAS {} device realize.", self.device_id()); + self.base.reset_usb_endpoint(); + let mut desc_strings: Vec = + UAS_DESC_STRINGS.iter().map(|str| str.to_string()).collect(); + let prefix = &desc_strings[UsbUasStringId::SerialNumber as usize]; + desc_strings[UsbUasStringId::SerialNumber as usize] = + self.base.generate_serial_number(prefix); + + match self.base.speed { + USB_SPEED_HIGH => self + .base + .init_descriptor(DESC_DEVICE_UAS_HIGH.clone(), desc_strings)?, + USB_SPEED_SUPER => self + .base + .init_descriptor(DESC_DEVICE_UAS_SUPER.clone(), desc_strings)?, + _ => bail!("USB UAS unsupported device speed {}.", self.base.speed), + } + + // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not + // supported. + let mut locked_scsi_device = self.scsi_device.lock().unwrap(); + locked_scsi_device.realize(None)?; + locked_scsi_device.parent_bus = Arc::downgrade(&self.scsi_bus); + drop(locked_scsi_device); + self.scsi_bus + .lock() + .unwrap() + .devices + .insert((0, 0), Arc::clone(&self.scsi_device)); + + let uas = Arc::new(Mutex::new(self)); + Ok(uas) + } + + fn cancel_packet(&mut self, _packet: &Arc>) { + self.cancel_io(); + } + + fn reset(&mut self) { + info!("UAS {} device reset.", self.device_id()); + self.base.remote_wakeup = 0; + self.base.addr = 0; + self.cancel_io(); + } + + fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { + let mut locked_packet = packet.lock().unwrap(); + trace::usb_uas_handle_control( + locked_packet.packet_id, + self.device_id(), + device_req.as_bytes(), + ); + + match self + .base + .handle_control_for_descriptor(&mut locked_packet, device_req) + { + Ok(handled) => { + if handled { + debug!( + "UAS {} device control handled by descriptor, return directly.", + self.device_id() + ); + return; + } + + error!( + "UAS {} device unhandled control request {:?}.", + self.device_id(), + device_req + ); + locked_packet.status = UsbPacketStatus::Stall; + } + Err(err) => { + error!( + "UAS {} device descriptor error {:?}.", + self.device_id(), + err + ); + locked_packet.status = UsbPacketStatus::Stall; + } + } + } + + fn handle_data(&mut self, packet: &Arc>) { + let locked_packet = packet.lock().unwrap(); + let stream = locked_packet.stream as usize; + let ep_number = locked_packet.ep_number; + let packet_id = locked_packet.packet_id; + trace::usb_uas_handle_data(self.device_id(), ep_number, stream); + drop(locked_packet); + + if self.streams_enabled() && stream > UAS_MAX_STREAMS { + warn!("UAS {} device invalid stream {}.", self.device_id(), stream); + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + + // NOTE: The architecture of this device is rather simple: it first waits for all of the + // required USB packets to arrive, and only then creates and sends an actual UAS request. + // The number of USB packets differs from 2 to 4 and depends on whether the command involves + // data transfers or not. Since the packets arrive in arbitrary order, some of them may be + // queued asynchronously. Note that the command packet is always completed right away. For + // all the other types of packets, their asynchronous status is determined by the return + // value of try_start_next_transfer(). All the asynchronously queued packets will be + // completed in scsi_request_complete_cb() callback. + match ep_number { + UAS_PIPE_ID_COMMAND => { + if self.streams_enabled() && self.commands_super[stream].is_some() { + warn!( + "UAS {} device multiple command packets on stream {}.", + self.device_id(), + stream + ); + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + + let mut locked_packet = packet.lock().unwrap(); + let mut iu = UasIU::default(); + let iov_size = locked_packet.get_iovecs_size() as usize; + let iu_len = min(iov_size, size_of::()); + locked_packet.transfer_packet(iu.as_mut_bytes(), iu_len); + + trace::usb_uas_command_received(packet_id, self.device_id()); + self.queue_command(iu, stream); + self.try_start_next_transfer(stream); + trace::usb_uas_command_completed(packet_id, self.device_id()); + } + UAS_PIPE_ID_STATUS => { + if self.streams_enabled() && self.statuses_super[stream].is_some() { + warn!( + "UAS {} device multiple status packets on stream {}.", + self.device_id(), + stream + ); + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + + trace::usb_uas_status_received(packet_id, self.device_id()); + self.queue_status(packet, stream); + let result = self.try_start_next_transfer(stream); + + match result { + UasPacketStatus::Completed => { + trace::usb_uas_status_completed(packet_id, self.device_id()) + } + UasPacketStatus::Pending => { + packet.lock().unwrap().is_async = true; + trace::usb_uas_status_queued_async(packet_id, self.device_id()); + } + } + } + UAS_PIPE_ID_DATA_OUT | UAS_PIPE_ID_DATA_IN => { + if self.data[stream].is_some() { + warn!( + "UAS {} device multiple data packets on stream {}.", + self.device_id(), + stream + ); + packet.lock().unwrap().status = UsbPacketStatus::Stall; + return; + } + + trace::usb_uas_data_received(packet_id, self.device_id()); + self.data[stream] = Some(Arc::clone(packet)); + let result = self.try_start_next_transfer(stream); + + match result { + UasPacketStatus::Completed => { + trace::usb_uas_data_completed(packet_id, self.device_id()) + } + UasPacketStatus::Pending => { + packet.lock().unwrap().is_async = true; + trace::usb_uas_data_queued_async(packet_id, self.device_id()); + } + } + } + _ => { + error!( + "UAS {} device bad endpoint number {}.", + self.device_id(), + ep_number + ); + } + } + } + + fn set_controller(&mut self, _controller: std::sync::Weak>) {} + + fn get_controller(&self) -> Option>> { + None + } + + fn get_wakeup_endpoint(&self) -> &UsbEndpoint { + self.base.get_endpoint(true, 1) + } +} + +impl UasRequest { + fn new(status: &Arc>, iu: &UasIU) -> Self { + Self { + data: None, + status: Arc::clone(status), + iu: *iu, + completed: false, + } + } + + fn complete(&mut self) { + let locked_status = self.status.lock().unwrap(); + + // NOTE: Due to the specifics of this device, it waits for all of the required USB packets + // to arrive before starting an actual transfer. Therefore, some packets may arrive earlier + // than others, and they won't be completed right away (except for command packets) but + // rather queued asynchronously. A certain packet may also be async if it was the last to + // arrive and UasRequest didn't complete right away. + if locked_status.is_async { + complete_async_packet(locked_status); + } + + if let Some(data) = &self.data { + let locked_data = data.lock().unwrap(); + + if locked_data.is_async { + complete_async_packet(locked_data); + } + } + + self.completed = true; + } +} + +impl UasIUHeader { + fn new(id: u8, tag: u16) -> Self { + UasIUHeader { + id, + reserved: 0, + tag: tag.to_be(), + } + } +} + +impl UasIU { + fn new(id: u8, tag: u16) -> Self { + Self { + header: UasIUHeader::new(id, tag), + body: UasIUBody::default(), + } + } +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6c13c03c1..c5322a566 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -63,7 +63,7 @@ use devices::usb::tablet::{UsbTablet, UsbTabletConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::{UsbHost, UsbHostConfig}; use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice}; -use devices::usb::{storage::UsbStorage, UsbDevice}; +use devices::usb::{storage::UsbStorage, uas::UsbUas, UsbDevice}; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; @@ -77,6 +77,7 @@ use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] use machine_manager::config::parse_pvpanic; use machine_manager::config::parse_usb_storage; +use machine_manager::config::parse_usb_uas; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, @@ -1760,6 +1761,12 @@ pub trait MachineOps { .realize() .with_context(|| "Failed to realize usb storage device")? } + "usb-uas" => { + let device_cfg = parse_usb_uas(vm_config, cfg_args)?; + let uas = UsbUas::new(device_cfg, self.get_drive_files()); + uas.realize() + .with_context(|| "Failed to realize usb uas device")? + } #[cfg(feature = "usb_host")] "usb-host" => { let config = UsbHostConfig::try_parse_from(str_slip_to_clap(cfg_args))?; @@ -1829,7 +1836,7 @@ pub trait MachineOps { ("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false), ("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args), ("nec-usb-xhci", add_usb_xhci, cfg_args), - ("usb-kbd" | "usb-storage" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); + ("usb-kbd" | "usb-storage" | "usb-uas" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); #[cfg(feature = "virtio_gpu")] ("virtio-gpu-pci", add_virtio_pci_gpu, cfg_args), #[cfg(feature = "ramfb")] diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index da363b7e4..1b0e492cc 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -97,3 +97,66 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result dev.check()?; Ok(dev) } + +#[derive(Clone, Debug, Default)] +pub struct UsbUasConfig { + pub id: Option, + pub speed: Option, + pub scsi_cfg: ScsiDevConfig, + pub media: String, +} + +impl UsbUasConfig { + fn new() -> Self { + Self::default() + } +} + +impl ConfigCheck for UsbUasConfig { + fn check(&self) -> Result<()> { + check_id(self.id.clone(), "usb-uas")?; + + if self.scsi_cfg.aio_type != AioEngine::Off || self.scsi_cfg.direct { + bail!("USB UAS: \"aio=off,direct=false\" must be configured."); + } + + Ok(()) + } +} + +pub fn parse_usb_uas(vm_config: &mut VmConfig, drive_config: &str) -> Result { + let mut cmd_parser = CmdParser::new("usb-uas"); + cmd_parser + .push("") + .push("id") + .push("bus") + .push("port") + .push("drive") + .push("speed"); + + cmd_parser.parse(drive_config)?; + + let mut dev = UsbUasConfig::new(); + dev.id = cmd_parser.get_value::("id")?; + dev.speed = cmd_parser.get_value::("speed")?; + + let uas_drive = cmd_parser.get_value::("drive")?.with_context(|| { + ConfigError::FieldIsMissing("drive".to_string(), "usb uas device".to_string()) + })?; + + let drive_arg = &vm_config + .drives + .remove(&uas_drive) + .with_context(|| "No drive configured matched for usb uas device.")?; + dev.scsi_cfg.path_on_host = drive_arg.path_on_host.clone(); + dev.scsi_cfg.read_only = drive_arg.read_only; + dev.scsi_cfg.aio_type = drive_arg.aio; + dev.scsi_cfg.direct = drive_arg.direct; + dev.scsi_cfg.format = drive_arg.format; + dev.scsi_cfg.l2_cache_size = drive_arg.l2_cache_size; + dev.scsi_cfg.refcount_cache_size = drive_arg.refcount_cache_size; + dev.media = drive_arg.media.clone(); + + dev.check()?; + Ok(dev) +} diff --git a/trace/trace_event/usb.toml b/trace/trace_event/usb.toml index fa5b94542..d94588e61 100644 --- a/trace/trace_event/usb.toml +++ b/trace/trace_event/usb.toml @@ -465,3 +465,105 @@ name = "usb_host_req_complete" args = "bus_num: u8, addr: u8, packet: u64, status: &dyn fmt::Debug, actual_length: usize" message = "dev bus 0x{:X} addr 0x{:X}, packet 0x{:#X}, status {:?} actual length {}" enabled = true + +[[events]] +name = "usb_uas_handle_control" +args = "packet_id: u32, device_id: &str, req: &[u8]" +message = "USB {} packet received on UAS {} device, the request is {:?}." +enabled = true + +[[events]] +name = "usb_uas_handle_iu_command" +args = "device_id: &str, cdb: u8" +message = "UAS {} device handling IU with cdb[0] {}." +enabled = true + +[[events]] +name = "usb_uas_fill_sense" +args = "status: u8, iu_len: usize, sense_len: usize" +message = "UAS device is filling sense with status {:02} URB length {} sense length {}." +enabled = true + +[[events]] +name = "usb_uas_fill_fake_sense" +args = "status: u8, iu_len: usize, sense_len: usize" +message = "UAS device is filling fake sense with status {:02} URB length {} sense length {}." +enabled = true + +[[events]] +name = "usb_uas_fill_packet" +args = "iovec_size: usize" +message = "UAS device is filling USB packet with iovec of size {}." +enabled = true + +[[events]] +name = "usb_uas_try_start_next_transfer" +args = "device_id: &str, xfer_len: i32" +message = "UAS {} device is trying to start next transfer of length {}." +enabled = true + +[[events]] +name = "usb_uas_fill_data_ready" +args = "device_id: &str, data_ready_sent: bool" +message = "UAS {} device set data_ready_sent to {}." +enabled = true + +[[events]] +name = "usb_uas_start_next_transfer" +args = "device_id: &str, stream: usize" +message = "UAS {} device starting a transfer on stream {}." +enabled = true + +[[events]] +name = "usb_uas_handle_data" +args = "device_id: &str, endpoint: u8, stream: usize" +message = "UAS {} device handling data on endpoint {} and stream {}." +enabled = true + +[[events]] +name = "usb_uas_command_received" +args = "packet_id: u32, device_id: &str" +message = "USB {} command packet received on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_command_completed" +args = "packet_id: u32, device_id: &str" +message = "USB {} command packet completed on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_status_received" +args = "packet_id: u32, device_id: &str" +message = "USB {} status packet received on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_status_completed" +args = "packet_id: u32, device_id: &str" +message = "USB {} status packet completed on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_status_queued_async" +args = "packet_id: u32, device_id: &str" +message = "USB {} status packet queued async on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_data_received" +args = "packet_id: u32, device_id: &str" +message = "USB {} data packet received on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_data_completed" +args = "packet_id: u32, device_id: &str" +message = "USB {} data packet completed on UAS {} device." +enabled = true + +[[events]] +name = "usb_uas_data_queued_async" +args = "packet_id: u32, device_id: &str" +message = "USB {} data packet queued async on UAS {} device." +enabled = true -- Gitee From 488e1b48ec2bd9bcfafd5a6a5c6f26105360f63f Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 13 Feb 2024 15:25:47 +0300 Subject: [PATCH 1719/2187] uas: Use strum EnumCount macro for convenience Add strum and strum_macro to the dependecies list. Use strum EnumCount macro instead of the magic const to determine the size of the enum. Signed-off-by: Stanislav Goriainov --- Cargo.lock | 2 ++ devices/Cargo.toml | 2 ++ devices/src/usb/uas.rs | 6 ++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa9c10067..392574656 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -382,6 +382,8 @@ dependencies = [ "rusb", "serde", "serde_json", + "strum", + "strum_macros", "thiserror", "trace", "ui", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 95ca790d1..1727efdea 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -12,6 +12,8 @@ anyhow = "1.0" libc = "0.2" log = "0.4" serde = { version = "1.0", features = ["derive"] } +strum = "0.24.1" +strum_macros = "0.24.3" vmm-sys-util = "0.11.1" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 552fa15c9..ef7e90c48 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -20,6 +20,8 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{debug, error, info, warn}; use machine_manager::config::{DriveFile, UsbUasConfig}; use once_cell::sync::Lazy; +use strum::EnumCount; +use strum_macros::EnumCount; use util::byte_code::ByteCode; use super::config::*; @@ -106,7 +108,7 @@ pub struct UsbUas { data_ready_sent: bool, } -#[derive(Debug)] +#[derive(Debug, EnumCount)] enum UsbUasStringId { #[allow(unused)] Invalid = 0, @@ -117,7 +119,7 @@ enum UsbUasStringId { ConfigSuper = 5, } -const UAS_DESC_STRINGS: [&str; 6] = [ +const UAS_DESC_STRINGS: [&str; UsbUasStringId::COUNT] = [ "", "StratoVirt", "StratoVirt USB Uas", -- Gitee From ee8377d558eba82e1079bf0aa8eb9baa13ee6712 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 13 Feb 2024 17:04:24 +0300 Subject: [PATCH 1720/2187] usb: Move common USB constants to config.rs Move some common USB descriptor-related values to config.rs. Also move vendor and device ids there, so that it would be easier to track ids and ensure that they are unique. Signed-off-by: Stanislav Goriainov --- devices/src/usb/camera.rs | 9 ++------- devices/src/usb/config.rs | 23 +++++++++++++++++++++- devices/src/usb/keyboard.rs | 6 +++--- devices/src/usb/storage.rs | 6 +++--- devices/src/usb/tablet.rs | 2 +- devices/src/usb/uas.rs | 39 ++++++++++++++----------------------- 6 files changed, 46 insertions(+), 39 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 12cbe6b22..cedf8cdc4 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -44,11 +44,6 @@ use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -// CRC16 of "STRATOVIRT" -const UVC_VENDOR_ID: u16 = 0xB74C; -// The first 4 chars of "VIDEO", 5 substitutes V. -const UVC_PRODUCT_ID: u16 = 0x51DE; - const INTERFACE_ID_CONTROL: u8 = 0; const INTERFACE_ID_STREAMING: u8 = 1; @@ -433,8 +428,8 @@ fn gen_desc_device_camera(fmt_list: Vec) -> Result> = Lazy::new(|| { bLength: USB_DT_DEVICE_SIZE, bDescriptorType: USB_DT_DEVICE, idVendor: 0x0627, - idProduct: 0x0001, + idProduct: USB_PRODUCT_ID_KEYBOARD, bcdDevice: 0, iManufacturer: STR_MANUFACTURER_INDEX, iProduct: STR_PRODUCT_KEYBOARD_INDEX, @@ -76,8 +76,8 @@ static DESC_IFACE_KEYBOARD: Lazy> = Lazy::new(|| { bAlternateSetting: 0, bNumEndpoints: 1, bInterfaceClass: USB_CLASS_HID, - bInterfaceSubClass: 1, - bInterfaceProtocol: 1, + bInterfaceSubClass: USB_SUBCLASS_BOOT, + bInterfaceProtocol: USB_IFACE_PROTOCOL_KEYBOARD, iInterface: 0, }, other_desc: vec![Arc::new(UsbDescOther { diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 1c34077e7..4bb44e0ee 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -43,7 +43,7 @@ static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { bLength: USB_DT_DEVICE_SIZE, bDescriptorType: USB_DT_DEVICE, idVendor: USB_STORAGE_VENDOR_ID, - idProduct: 0x0001, + idProduct: USB_PRODUCT_ID_STORAGE, bcdDevice: 0, iManufacturer: STR_MANUFACTURER_INDEX, iProduct: STR_PRODUCT_STORAGE_INDEX, @@ -82,8 +82,8 @@ static DESC_IFACE_STORAGE: Lazy> = Lazy::new(|| { bAlternateSetting: 0, bNumEndpoints: 2, bInterfaceClass: USB_CLASS_MASS_STORAGE, - bInterfaceSubClass: 0x06, // SCSI - bInterfaceProtocol: 0x50, // Bulk-only + bInterfaceSubClass: USB_SUBCLASS_SCSI, + bInterfaceProtocol: USB_IFACE_PROTOCOL_BOT, iInterface: 0, }, other_desc: vec![], diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 2e2334930..900d2a99a 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -45,7 +45,7 @@ static DESC_DEVICE_TABLET: Lazy> = Lazy::new(|| { bLength: USB_DT_DEVICE_SIZE, bDescriptorType: USB_DT_DEVICE, idVendor: 0x0627, - idProduct: 0x0001, + idProduct: USB_PRODUCT_ID_TABLET, bcdDevice: 0, iManufacturer: STR_MANUFACTURER_INDEX, iProduct: STR_PRODUCT_TABLET_INDEX, diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index ef7e90c48..95944ee18 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -51,9 +51,6 @@ const UAS_IU_BODY_SIZE: usize = 30; // Size of cdb in UAS Command IU const UAS_COMMAND_CDB_SIZE: usize = 16; -// CRC16 of "STRATOVIRT" -const USB_UAS_VENDOR_ID: u16 = 0xB74C; - // UAS Pipe IDs const UAS_PIPE_ID_COMMAND: u8 = 0x01; const UAS_PIPE_ID_STATUS: u8 = 0x02; @@ -92,10 +89,6 @@ const _UAS_TMF_QUERY_TASK: u8 = 0x80; const _UAS_TMF_QUERY_TASK_SET: u8 = 0x81; const _UAS_TMF_QUERY_ASYNC_EVENT: u8 = 0x82; -// USB Pipe Usage Descriptor -const USB_DT_PIPE_USAGE: u8 = 0x24; -const USB_DT_PIPE_USAGE_SIZE: u8 = 4; - pub struct UsbUas { base: UsbDeviceBase, scsi_bus: Arc>, @@ -259,9 +252,9 @@ static DESC_DEVICE_UAS_SUPER: Lazy> = Lazy::new(|| { bDeviceSubClass: 0, bDeviceProtocol: 0, bMaxPacketSize0: 9, - idVendor: USB_UAS_VENDOR_ID, - idProduct: 0x0001, - bcdDevice: 0x0, + idVendor: USB_VENDOR_ID_STRATOVIRT, + idProduct: USB_PRODUCT_ID_UAS, + bcdDevice: 0, iManufacturer: UsbUasStringId::Manufacturer as u8, iProduct: UsbUasStringId::Product as u8, iSerialNumber: UsbUasStringId::SerialNumber as u8, @@ -293,8 +286,8 @@ static DESC_IFACE_UAS_SUPER: Lazy> = Lazy::new(|| { bAlternateSetting: 0, bNumEndpoints: 4, bInterfaceClass: USB_CLASS_MASS_STORAGE, - bInterfaceSubClass: 0x06, // SCSI - bInterfaceProtocol: 0x62, // UAS + bInterfaceSubClass: USB_SUBCLASS_SCSI, + bInterfaceProtocol: USB_IFACE_PROTOCOL_UAS, iInterface: 0, }, other_desc: vec![], @@ -429,9 +422,9 @@ static DESC_DEVICE_UAS_HIGH: Lazy> = Lazy::new(|| { bDeviceSubClass: 0, bDeviceProtocol: 0, bMaxPacketSize0: 64, - idVendor: USB_UAS_VENDOR_ID, - idProduct: 0x0002, - bcdDevice: 0x0001, + idVendor: USB_VENDOR_ID_STRATOVIRT, + idProduct: USB_PRODUCT_ID_UAS, + bcdDevice: 0, iManufacturer: UsbUasStringId::Manufacturer as u8, iProduct: UsbUasStringId::Product as u8, iSerialNumber: UsbUasStringId::SerialNumber as u8, @@ -463,8 +456,8 @@ static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { bAlternateSetting: 1, bNumEndpoints: 4, bInterfaceClass: USB_CLASS_MASS_STORAGE, - bInterfaceSubClass: 0x06, // SCSI - bInterfaceProtocol: 0x62, // UAS + bInterfaceSubClass: USB_SUBCLASS_SCSI, + bInterfaceProtocol: USB_IFACE_PROTOCOL_UAS, iInterface: 0, }, other_desc: vec![], @@ -476,7 +469,7 @@ static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_COMMAND, bmAttributes: USB_ENDPOINT_ATTR_BULK, wMaxPacketSize: 512, - bInterval: 0xFF, + bInterval: 0, }, extra: UsbPipeUsageDescriptor { bLength: USB_DT_PIPE_USAGE_SIZE, @@ -494,7 +487,7 @@ static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_STATUS, bmAttributes: USB_ENDPOINT_ATTR_BULK, wMaxPacketSize: 512, - bInterval: 0xFF, + bInterval: 0, }, extra: UsbPipeUsageDescriptor { bLength: USB_DT_PIPE_USAGE_SIZE, @@ -512,7 +505,7 @@ static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_DATA_IN, bmAttributes: USB_ENDPOINT_ATTR_BULK, wMaxPacketSize: 512, - bInterval: 0xFF, + bInterval: 0, }, extra: UsbPipeUsageDescriptor { bLength: USB_DT_PIPE_USAGE_SIZE, @@ -530,7 +523,7 @@ static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_DATA_OUT, bmAttributes: USB_ENDPOINT_ATTR_BULK, wMaxPacketSize: 512, - bInterval: 0xFF, + bInterval: 0, }, extra: UsbPipeUsageDescriptor { bLength: USB_DT_PIPE_USAGE_SIZE, @@ -557,9 +550,7 @@ static DESC_IFACE_EMPTY: Lazy> = Lazy::new(|| { bAlternateSetting: 0, bNumEndpoints: 0, bInterfaceClass: USB_CLASS_MASS_STORAGE, - bInterfaceSubClass: 0x00, // SCSI - bInterfaceProtocol: 0x00, // BOT - iInterface: 0, + ..Default::default() }, other_desc: vec![], endpoints: vec![], -- Gitee From e36f6aa601e9227ba0184741637286664d701216 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 12 Mar 2024 15:22:40 +0300 Subject: [PATCH 1721/2187] uas: Fix some logical code issues Many small fixes including: applying stub BOT Descriptor hack for Super Speed, improving readability etc. Signed-off-by: goriainovstanislav --- devices/src/usb/uas.rs | 43 ++++++++++++++----------- devices/src/usb/xhci/xhci_controller.rs | 4 ++- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 95944ee18..3768b8bbe 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -14,7 +14,7 @@ use std::array; use std::cmp::min; use std::collections::{HashMap, VecDeque}; use std::mem::size_of; -use std::sync::{Arc, Mutex, MutexGuard, Weak}; +use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; use log::{debug, error, info, warn}; @@ -272,7 +272,7 @@ static DESC_DEVICE_UAS_SUPER: Lazy> = Lazy::new(|| { bMaxPower: 50, }, iad_desc: vec![], - interfaces: vec![DESC_IFACE_UAS_SUPER.clone()], + interfaces: vec![DESC_IFACE_EMPTY.clone(), DESC_IFACE_UAS_SUPER.clone()], })], }) }); @@ -283,7 +283,7 @@ static DESC_IFACE_UAS_SUPER: Lazy> = Lazy::new(|| { bLength: USB_DT_INTERFACE_SIZE, bDescriptorType: USB_DT_INTERFACE, bInterfaceNumber: 0, - bAlternateSetting: 0, + bAlternateSetting: 1, bNumEndpoints: 4, bInterfaceClass: USB_CLASS_MASS_STORAGE, bInterfaceSubClass: USB_SUBCLASS_SCSI, @@ -439,7 +439,7 @@ static DESC_DEVICE_UAS_HIGH: Lazy> = Lazy::new(|| { bConfigurationValue: 1, iConfiguration: UsbUasStringId::ConfigHigh as u8, bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, - bMaxPower: 0xFA, + bMaxPower: 50, }, iad_desc: vec![], interfaces: vec![DESC_IFACE_EMPTY.clone(), DESC_IFACE_UAS_HIGH.clone()], @@ -550,14 +550,18 @@ static DESC_IFACE_EMPTY: Lazy> = Lazy::new(|| { bAlternateSetting: 0, bNumEndpoints: 0, bInterfaceClass: USB_CLASS_MASS_STORAGE, - ..Default::default() + bInterfaceSubClass: USB_SUBCLASS_SCSI, + bInterfaceProtocol: USB_IFACE_PROTOCOL_BOT, + iInterface: 0, }, other_desc: vec![], endpoints: vec![], }) }); -fn complete_async_packet(locked_packet: MutexGuard<'_, UsbPacket>) { +fn complete_async_packet(packet: &Arc>) { + let locked_packet = packet.lock().unwrap(); + if let Some(xfer_ops) = locked_packet.xfer_ops.as_ref() { if let Some(xfer_ops) = xfer_ops.clone().upgrade() { drop(locked_packet); @@ -940,8 +944,11 @@ impl UsbUas { } } - if locked_status.is_async { - complete_async_packet(locked_status); + let status_async = locked_status.is_async; + drop(locked_status); + + if status_async { + complete_async_packet(&status); } self.data_ready_sent = true; @@ -982,10 +989,7 @@ impl UsbUas { }; self.data_ready_sent = false; - - if !self.streams_enabled() { - self.try_start_next_transfer(0); - } + self.try_start_next_transfer(stream); match result { Ok(result) => result, @@ -1099,7 +1103,7 @@ impl UsbDevice for UsbUas { trace::usb_uas_handle_data(self.device_id(), ep_number, stream); drop(locked_packet); - if self.streams_enabled() && stream > UAS_MAX_STREAMS { + if self.streams_enabled() && (stream > UAS_MAX_STREAMS || stream == 0) { warn!("UAS {} device invalid stream {}.", self.device_id(), stream); packet.lock().unwrap().status = UsbPacketStatus::Stall; return; @@ -1218,22 +1222,23 @@ impl UasRequest { } fn complete(&mut self) { - let locked_status = self.status.lock().unwrap(); + let status = &self.status; + let status_async = status.lock().unwrap().is_async; // NOTE: Due to the specifics of this device, it waits for all of the required USB packets // to arrive before starting an actual transfer. Therefore, some packets may arrive earlier // than others, and they won't be completed right away (except for command packets) but // rather queued asynchronously. A certain packet may also be async if it was the last to // arrive and UasRequest didn't complete right away. - if locked_status.is_async { - complete_async_packet(locked_status); + if status_async { + complete_async_packet(status); } if let Some(data) = &self.data { - let locked_data = data.lock().unwrap(); + let data_async = data.lock().unwrap().is_async; - if locked_data.is_async { - complete_async_packet(locked_data); + if data_async { + complete_async_packet(data); } } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 7bf79b69f..81fcb617d 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2037,9 +2037,11 @@ impl XhciDevice { if report != TRBCCode::Invalid { xfer.status = report; xfer.submit_transfer()?; + let locked_packet = xfer.packet.lock().unwrap(); - if let Some(usb_dev) = xfer.packet.lock().unwrap().target_dev.as_ref() { + if let Some(usb_dev) = locked_packet.target_dev.as_ref() { if let Some(usb_dev) = usb_dev.clone().upgrade() { + drop(locked_packet); let mut locked_usb_dev = usb_dev.lock().unwrap(); locked_usb_dev.cancel_packet(&xfer.packet); } -- Gitee From 58c875b60c3080b09e5e63b2421b2836dc2c126f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 13 Apr 2024 06:15:20 +0800 Subject: [PATCH 1722/2187] balloon: delete useless `update_config` test Virtio-balloon doesn't support `update_config`, delete this useless test. Signed-off-by: liuxiangdong --- virtio/src/device/balloon.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 96dae3ecd..fd6347a28 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1332,8 +1332,6 @@ mod tests { // Test methods of balloon. let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); - - assert!(bln.update_config(None).is_err()); } #[test] @@ -1690,7 +1688,5 @@ mod tests { // Test methods of balloon. let ram_size = bln.mem_info.lock().unwrap().get_ram_size(); assert_eq!(ram_size, MEMORY_SIZE); - - assert!(bln.update_config(None).is_err()); } } -- Gitee From ab3f17a1743424ef1dc426f81565d5c6ee85727e Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 15 Apr 2024 11:09:56 +0800 Subject: [PATCH 1723/2187] MST: fix UI mst UI module support mouse side button, but related MST not adjusts yet. Here we fix related usb mouse test case. Signed-off-by: zhanghan64 --- tests/mod_test/src/libdriver/usb.rs | 4 ++-- tests/mod_test/tests/usb_test.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 0c403a73f..0a03dd213 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -1868,8 +1868,8 @@ impl TestXhciPciDevice { buf, [ 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, - 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, - 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, + 0x19, 0x01, 0x29, 0x05, 0x15, 0x00, 0x25, 0x01, 0x95, 0x05, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x03, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x00, 0x26, 0xff, 0x7f, 0x35, 0x00, 0x46, 0xff, 0x7f, 0x75, 0x10, 0x95, 0x02, 0x81, 0x02, 0x05, 0x01, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x35, 0x00, 0x45, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 6739cd8ac..8e7070fab 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -2050,9 +2050,9 @@ fn test_xhci_tablet_basic() { // INPUT_BUTTON_WHEEL_LEFT + INPUT_BUTTON_WHEEL_UP. for _ in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x28, true); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0xa0, true); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x28, false); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0xa0, false); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); @@ -2070,9 +2070,9 @@ fn test_xhci_tablet_basic() { // INPUT_BUTTON_WHEEL_RIGHT + INPUT_BUTTON_WHEEL_DOWN. for _ in 0..cnt { - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x50, true); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x140, true); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); - qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x50, false); + qmp_send_pointer_event(test_state.borrow_mut(), 10, 20, 0x140, false); xhci.queue_indirect_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); } xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); @@ -2200,7 +2200,7 @@ fn test_xhci_tablet_invalid_value() { let press_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(press_evt.ccode, TRBCCode::Success as u32); let press_buf = xhci.get_transfer_data_indirect(press_evt.ptr, HID_POINTER_LEN); - assert_eq!(press_buf, [7, 255, 127, 255, 127, 1, 255]); + assert_eq!(press_buf, [31, 255, 127, 255, 127, 1, 255]); let release_evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(release_evt.ccode, TRBCCode::Success as u32); let release_buf = xhci.get_transfer_data_indirect(release_evt.ptr, HID_POINTER_LEN); -- Gitee From 71f2eca51b66a4b6c5c30e024f4b5ac6f1c959c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Mon, 1 Apr 2024 16:52:59 +0800 Subject: [PATCH 1724/2187] unti: add misc unit test Signed-off-by: Jiahong Li --- devices/src/misc/scream/ohaudio.rs | 129 +++++++++++++++++++++++++++++ util/src/ohos_binding/audio/mod.rs | 93 +++++++++++++++++++++ util/src/ohos_binding/audio/sys.rs | 4 +- 3 files changed, 224 insertions(+), 2 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 77588c887..70294107e 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -361,3 +361,132 @@ impl AudioInterface for OhAudio { self.processor.destroy(); } } + +#[cfg(test)] +mod tests { + use crate::misc::scream::ohaudio::{on_read_data_cb, on_write_data_cb, StreamUnit}; + use crate::misc::scream::ohaudio::{OhAudioCapture, OhAudioProcess, OhAudioRender}; + use crate::misc::scream::StreamData; + + use util::ohos_binding::audio::*; + + #[test] + fn test_render_init_and_destroy() { + let mut render = OhAudioRender::default(); + let mut stream_data = StreamData::default(); + + assert!(!render.init(&stream_data)); + + stream_data.fmt.size = 16; + stream_data.fmt.rate = 1; + render.init(&stream_data); + assert!(render.ctx.is_some()); + assert!(render.start); + assert_eq!(render.stream_data.lock().unwrap().len(), 0); + + render.destroy(); + assert!(!render.start); + assert!(render.ctx.is_none()); + assert_eq!(render.stream_data.lock().unwrap().len(), 0); + assert_eq!(render.prepared_data, 0); + } + + #[test] + fn test_render_check_data_ready() { + let mut render = OhAudioRender::default(); + let mut recv_data = StreamData::default(); + recv_data.fmt.size = 16; + recv_data.fmt.rate = 1; + recv_data.fmt.channels = 2; + assert!(!render.check_data_ready(&recv_data)); + + render.prepared_data = 96000; + assert!(render.check_data_ready(&recv_data)); + } + + #[test] + fn test_render_check_fmt_update() { + let mut render = OhAudioRender::default(); + let mut recv_data = StreamData::default(); + recv_data.fmt.size = 16; + recv_data.fmt.rate = 158; + recv_data.fmt.channels = 2; + let stream_data = StreamData::default(); + render.init(&stream_data); + render.check_fmt_update(&recv_data); + assert!(render.ctx.is_none()); + } + + #[test] + fn test_capture_init_and_destroy() { + let mut capture = OhAudioCapture::default(); + let stream_data = StreamData::default(); + assert!(!capture.init(&stream_data)); + } + + #[test] + fn test_on_write_data_cb() { + let mut _renderer = OhAudioRenderer::default(); + let mut render = OhAudioRender::default(); + let user_data = std::ptr::addr_of!(render) as *mut ::std::os::raw::c_void; + + let mut dst: Vec = vec![25, 0, 0, 0, 0, 0, 0, 0, 0]; + + let src1: Vec = vec![10, 11, 12, 13, 14]; + let su1 = StreamUnit { + addr: src1.as_ptr() as u64, + len: src1.len() as u64, + }; + let src2: Vec = vec![21, 22, 23, 24, 25]; + let su2 = StreamUnit { + addr: src2.as_ptr() as u64, + len: src2.len() as u64, + }; + + render.stream_data.lock().unwrap().push(su1); + render.stream_data.lock().unwrap().push(su2); + render.start = true; + + // SAFETY: we checked len. + let dst_ptr = unsafe { dst.as_mut_ptr().offset(1) }; + + on_write_data_cb( + &mut _renderer, + user_data, + dst_ptr as *mut ::std::os::raw::c_void, + 8, + ); + + let target = [25, 10, 11, 12, 13, 14, 21, 22, 23]; + assert_eq!(dst, target); + } + + #[test] + fn test_on_read_data_cb() { + let mut _capturer = OhAudioCapturer::default(); + let mut capture = OhAudioCapture::default(); + + let mut src: Vec = vec![10, 11, 12, 13, 14, 15, 16]; + let mut dst: Vec = vec![99, 0, 0, 0, 0, 0, 0, 0]; + + let user_data = std::ptr::addr_of!(capture) as *mut ::std::os::raw::c_void; + + capture.align = dst.len() as u32; + capture.shm_len = dst.len() as u64; + capture.shm_addr = dst.as_mut_ptr() as u64; + capture.start = true; + // SAFETY: we checked len. + capture.cur_pos = unsafe { dst.as_mut_ptr().offset(3) as u64 }; + + on_read_data_cb( + &mut _capturer, + user_data, + src.as_mut_ptr() as *mut ::std::os::raw::c_void, + src.len() as i32, + ); + + assert_eq!(capture.new_chunks.into_inner(), 0); + let target = [15, 16, 0, 10, 11, 12, 13, 14]; + assert_eq!(dst, target); + } +} diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index bd308d753..7214e3295 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -360,3 +360,96 @@ impl AudioContext { .map_or(false, |_| (self.spec == other)) } } + +#[cfg(test)] +mod tests { + use crate::ohos_binding::audio::sys as capi; + use crate::ohos_binding::audio::{AudioSpec, AudioStreamType, OAErr, SampleRate, SampleSize}; + + #[test] + fn test_err() { + assert_eq!("OK", format!("{}", OAErr::Ok)); + assert_eq!("InvalidParam", format!("{}", OAErr::InvalidParam)); + assert_eq!("IllegalState", format!("{}", OAErr::IllegalState)); + assert_eq!("SysErr", format!("{}", OAErr::SysErr)); + assert_eq!("UnknownErr", format!("{}", OAErr::UnknownErr)); + + assert_eq!( + OAErr::Ok, + OAErr::from(capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_SUCCESS) + ); + assert_eq!( + OAErr::InvalidParam, + OAErr::from(capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_INVALID_PARAM) + ); + assert_eq!( + OAErr::IllegalState, + OAErr::from(capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_ILLEGAL_STATE) + ); + assert_eq!( + OAErr::SysErr, + OAErr::from(capi::OH_AUDIO_STREAM_RESULT_AUDIOSTREAM_ERROR_SYSTEM) + ); + assert_eq!( + OAErr::UnknownErr, + OAErr::from(capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_F32_LE) + ); + } + + #[test] + fn test_sample_size() { + assert_eq!( + Ok(SampleSize( + capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S16_LE + )), + SampleSize::try_from(16) + ); + assert_eq!( + Ok(SampleSize( + capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S24_LE + )), + SampleSize::try_from(24) + ); + assert_eq!( + Ok(SampleSize( + capi::OH_AUDIO_STREAM_SAMPLE_FORMAT_AUDIOSTREAM_SAMPLE_S32_LE + )), + SampleSize::try_from(32) + ); + assert_eq!(Err(OAErr::InvalidParam), SampleSize::try_from(18)); + } + + #[test] + fn test_sample_rate() { + assert_eq!(SampleRate(44100), SampleRate::default()); + assert_eq!(Ok(SampleRate(44100)), SampleRate::try_from(44100)); + assert_eq!(Ok(SampleRate(48000)), SampleRate::try_from(48000)); + assert_eq!(Err(OAErr::InvalidParam), SampleRate::try_from(54321)); + } + + #[test] + fn test_audio_spec() { + let mut spec1 = AudioSpec::default(); + let spec2 = AudioSpec::default(); + assert_eq!(spec1, spec2); + assert_eq!(Err(OAErr::InvalidParam), spec1.set(15, 16, 3)); + assert_eq!(Err(OAErr::InvalidParam), spec1.set(16, 16, 3)); + assert_eq!(Ok(()), spec1.set(32, 48000, 4)); + assert_ne!(spec1, spec2); + } + + #[test] + fn test_audio_stream_type() { + let oh_audio_stream_type_render: capi::OhAudioStreamType = AudioStreamType::Render.into(); + assert_eq!( + capi::OH_AUDIO_STREAM_TYPE_AUDIOSTREAM_TYPE_RERNDERER, + oh_audio_stream_type_render + ); + let oh_audio_stream_type_capturer: capi::OhAudioStreamType = + AudioStreamType::Capturer.into(); + assert_eq!( + capi::OH_AUDIO_STREAM_TYPE_AUDIOSTREAM_TYPE_CAPTURER, + oh_audio_stream_type_capturer + ); + } +} diff --git a/util/src/ohos_binding/audio/sys.rs b/util/src/ohos_binding/audio/sys.rs index 4c11f7848..289becbe3 100755 --- a/util/src/ohos_binding/audio/sys.rs +++ b/util/src/ohos_binding/audio/sys.rs @@ -143,7 +143,7 @@ pub struct OH_AudioStreamBuilderStruct { pub type OhAudioStreamBuilder = OH_AudioStreamBuilderStruct; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub struct OH_AudioRendererStruct { _unused: [u8; 0], } @@ -154,7 +154,7 @@ pub struct OH_AudioRendererStruct { pub type OhAudioRenderer = OH_AudioRendererStruct; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub struct OH_AudioCapturerStruct { _unused: [u8; 0], } -- Gitee From 3fa42a91f05982841f97e46296fbdeaedd30e82e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 15 Apr 2024 14:42:07 +0800 Subject: [PATCH 1725/2187] Machine manager: Modify the way of the output about cleaning files Replace the log with the stdio as the output mode when clean tmp files. Signed-off-by: Jinhao Gao --- machine_manager/src/temp_cleaner.rs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/machine_manager/src/temp_cleaner.rs b/machine_manager/src/temp_cleaner.rs index 2bdec2954..e63d27375 100644 --- a/machine_manager/src/temp_cleaner.rs +++ b/machine_manager/src/temp_cleaner.rs @@ -12,10 +12,11 @@ use std::collections::HashMap; use std::fs; -use std::io::Write; use std::path::Path; use std::sync::Arc; +use log::{error, info}; + static mut GLOBAL_TEMP_CLEANER: Option = None; pub type ExitNotifier = dyn Fn() + Send + Sync; @@ -80,28 +81,15 @@ impl TempCleaner { while let Some(path) = self.paths.pop() { if Path::new(&path).exists() { if let Err(ref e) = fs::remove_file(&path) { - write!( - &mut std::io::stderr(), + error!( "Failed to delete console / socket file:{} :{} \r\n", - &path, - e - ) - .expect("Failed to write to stderr"); + &path, e + ); } else { - write!( - &mut std::io::stdout(), - "Delete file: {} successfully.\r\n", - &path - ) - .expect("Failed to write to stdout"); + info!("Delete file: {} successfully.\r\n", &path); } } else { - write!( - &mut std::io::stdout(), - "file: {} has been removed \r\n", - &path - ) - .expect("Failed to write to stdout"); + info!("file: {} has been removed \r\n", &path); } } } -- Gitee From 293990a4558a25dbc27e0402e9d5e7939f35380e Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 15 Apr 2024 19:31:52 +0800 Subject: [PATCH 1726/2187] usb-storage: support cdrom hotplug Signed-off-by: Zhao Yi Min --- docs/qmp.md | 1 + machine/src/standard_common/mod.rs | 8 +++++++- machine_manager/src/qmp/qmp_schema.rs | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/qmp.md b/docs/qmp.md index c82b722f4..ea9b863a8 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -78,6 +78,7 @@ Add a block backend. * `node-name` : the name of the block driver node, must be unique. * `file` : the backend file information. +* `media` : indicate media type of the backend file. Possible values are `disk` or `cdrom`. If not set, default is `disk`. * `cache` : if use direct io. * `read-only` : if readonly. * `driver` : the block image format. Possible values are `raw` or `qcow2`. If not set, default is `raw`. diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 147506e23..1ab0be07c 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1141,7 +1141,7 @@ impl DeviceInterface for StdMachine { ); } } - "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" => { + "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" | "usb-storage" => { let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); if let Err(e) = self.add_usb_device(&mut vm_config_clone, &cfg_args) { error!("{:?}", e); @@ -1931,6 +1931,12 @@ fn parse_blockdev(args: &BlockDevAddArgument) -> Result { config.direct = false; config.aio = AioEngine::Off; } + if let Some(media) = args.media.as_ref() { + match media.as_str() { + "disk" | "cdrom" => config.media = media.clone(), + _ => bail!("Invalid media argument '{}', expect 'disk | cdrom'", media), + } + } if let Some(discard) = args.discard.as_ref() { config.discard = discard .as_str() diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 37f37bb05..e54bba416 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -423,6 +423,7 @@ pub struct blockdev_add { #[serde(rename = "node-name")] pub node_name: String, pub file: FileOptions, + pub media: Option, pub cache: Option, #[serde(rename = "read-only")] pub read_only: Option, -- Gitee From d2a389c5d9450a697dd3f484f1ee645f03a9eb89 Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Tue, 16 Apr 2024 17:53:02 +0800 Subject: [PATCH 1727/2187] qmp: fix error in parsing unix path When adding a unix socket using the mon option, the Unix type should be created directly. Signed-off-by: Huxiaohang --- machine_manager/src/cmdline.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index e11f18393..8e14d8c2e 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -662,9 +662,7 @@ pub fn check_api_channel( path ); } - sock_paths.push( - QmpSocketPath::new(path).with_context(|| "Failed to parse qmp socket path")?, - ); + sock_paths.push(QmpSocketPath::Unix { path }); } else if let ChardevType::TcpSocket { host, port, -- Gitee From 98cafa2f8085826552996b50791f8d336370589d Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 19 Apr 2024 14:31:35 +0800 Subject: [PATCH 1728/2187] MST: optimize cursor test Cusor test cases mixed up cursor image data and other data, and query cursor only return a word-size value which is not enough when cursor size is increased. We fix it. Signed-off-by: zhanghan64 --- devices/src/pci/demo_device/dpy_device.rs | 4 +++- tests/mod_test/src/libdriver/virtio_gpu.rs | 4 ++-- tests/mod_test/tests/virtio_gpu_test.rs | 20 +++++++++++--------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/devices/src/pci/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs index 9b3f5ce0d..bde922f68 100644 --- a/devices/src/pci/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -209,7 +209,9 @@ impl DeviceTypeOperation for DemoDisplay { } 1 => { buf.push(ds.cursor.len() as u8); - buf.push((ds.cursor.len() as u16 >> 8) as u8); + buf.push((ds.cursor.len() as u32 >> 8) as u8); + buf.push((ds.cursor.len() as u32 >> 16) as u8); + buf.push((ds.cursor.len() as u32 >> 24) as u8); } 2 => { buf = ds.image.clone(); diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index b8e941bf7..b8bd09d7b 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -357,12 +357,12 @@ impl TestDemoDpyDevice { return test_state.borrow_mut().readw(addr); } - pub fn query_cursor(&mut self) -> u16 { + pub fn query_cursor(&mut self) -> u32 { let addr = self.allocator.borrow_mut().alloc(size_of::() as u64); let test_state = self.pci_dev.pci_bus.borrow_mut().test_state.clone(); self.pci_dev .io_writeq(self.bar_addr, DpyEvent::QueryCursor as u64, addr); - return test_state.borrow_mut().readw(addr); + return test_state.borrow_mut().readl(addr); } pub fn get_surface(&mut self, size: u64) -> Vec { diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index 8aded9ca2..26d9c1c5f 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -40,8 +40,10 @@ const D_FMT: u32 = 2; const D_INVALID_FMT: u32 = VIRTIO_GPU_FORMAT_INVALID_UNORM; const D_WIDTH: u32 = 64; const D_HEIGHT: u32 = 64; +const D_CURSOR_WIDTH: u32 = 64; +const D_CURSOR_HEIGHT: u32 = 64; const D_BYTE_PER_PIXEL: u32 = 4; -const D_IMG_SIZE: u32 = D_WIDTH * D_HEIGHT * D_BYTE_PER_PIXEL; +const D_CURSOR_IMG_SIZE: u32 = D_CURSOR_WIDTH * D_CURSOR_HEIGHT * D_BYTE_PER_PIXEL; const D_OFFSET: u64 = 0; const D_X_COORD: u32 = 0; const D_Y_COORD: u32 = 0; @@ -188,11 +190,11 @@ fn image_display_fun() { #[test] fn cursor_display_fun() { - let image_0: Vec = vec![0 as u8; D_IMG_SIZE as usize]; - let image_1: Vec = vec![1 as u8; D_IMG_SIZE as usize]; + let image_0: Vec = vec![0 as u8; D_CURSOR_IMG_SIZE as usize]; + let image_1: Vec = vec![1 as u8; D_CURSOR_IMG_SIZE as usize]; let image_byte_1 = vec![1 as u8; 1]; - let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_CURSOR_WIDTH, D_CURSOR_HEIGHT); let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); @@ -206,7 +208,7 @@ fn cursor_display_fun() { VIRTIO_GPU_RESP_OK_NODATA, resource_create( &gpu, - VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH, D_HEIGHT) + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_CURSOR_WIDTH, D_CURSOR_HEIGHT) ) .hdr_type ); @@ -236,7 +238,7 @@ fn cursor_display_fun() { transfer_to_host( &gpu, VirtioGpuTransferToHost2d::new( - VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_WIDTH, D_HEIGHT), + VirtioGpuRect::new(D_X_COORD, D_Y_COORD, D_CURSOR_WIDTH, D_CURSOR_HEIGHT), D_OFFSET, D_RES_ID, ), @@ -664,7 +666,7 @@ fn scanout_flush_dfx() { #[test] fn cursor_update_dfx() { - let image_size = cal_image_hostmem(D_FMT, D_WIDTH, D_HEIGHT); + let image_size = cal_image_hostmem(D_FMT, D_CURSOR_WIDTH, D_CURSOR_HEIGHT); let image_size = image_size.0.unwrap() as u64; let mut gpu_cfg = GpuDevConfig::default(); @@ -674,7 +676,7 @@ fn cursor_update_dfx() { gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); let image_empty: Vec = vec![]; - let image_0: Vec = vec![0 as u8; D_IMG_SIZE as usize]; + let image_0: Vec = vec![0 as u8; D_CURSOR_IMG_SIZE as usize]; // invalid scanout id assert!(current_curosr_check(&dpy, &image_empty)); @@ -690,7 +692,7 @@ fn cursor_update_dfx() { VIRTIO_GPU_RESP_OK_NODATA, resource_create( &gpu, - VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_WIDTH / 2, D_HEIGHT) + VirtioGpuResourceCreate2d::new(D_RES_ID, D_FMT, D_CURSOR_WIDTH / 2, D_CURSOR_HEIGHT) ) .hdr_type ); -- Gitee From ea855fe17e9c744309b57a13a2ee9dca1f988162 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Sat, 20 Apr 2024 16:24:36 +0800 Subject: [PATCH 1729/2187] OHUI: add windows_emu_pid support We should support windows_emu_pid on OHOS. Signed-off-by: zhanghan64 --- machine/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine/Cargo.toml b/machine/Cargo.toml index d77eb78be..3b12c1225 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -47,6 +47,6 @@ windows_emu_pid = ["ui/console", "machine_manager/windows_emu_pid"] gtk = ["windows_emu_pid", "ui/gtk", "machine_manager/gtk"] vnc = ["ui/vnc", "machine_manager/vnc"] vnc_auth = ["vnc"] -ohui_srv = ["ui/ohui_srv", "machine_manager/ohui_srv", "virtio/ohui_srv"] +ohui_srv = ["windows_emu_pid", "ui/ohui_srv", "machine_manager/ohui_srv", "virtio/ohui_srv"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] -- Gitee From 07396d16da38fef6b5ef895530095c6bfbca2e71 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Sun, 21 Apr 2024 22:04:30 +0800 Subject: [PATCH 1730/2187] Fix Bad system call on x86_64 micro machine libc::SYS_clone3 libc::SYS_rt_sigaction is not added to the seccomp filtering whitelist on x86_64 micro machine; Signed-off-by: Yan Wen --- hypervisor/src/kvm/x86_64/mod.rs | 1 + machine/src/micro_common/syscall.rs | 12 ++++++++++++ machine/src/x86_64/micro.rs | 11 +++++++++++ 3 files changed, 24 insertions(+) diff --git a/hypervisor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs index c9f13a374..f4c2bc198 100644 --- a/hypervisor/src/kvm/x86_64/mod.rs +++ b/hypervisor/src/kvm/x86_64/mod.rs @@ -32,6 +32,7 @@ ioctl_iow_nr!(KVM_SET_SREGS, KVMIO, 0x84, kvm_sregs); ioctl_iow_nr!(KVM_SET_REGS, KVMIO, 0x82, kvm_regs); ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave); ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs); +ioctl_iow_nr!(KVM_SET_FPU, KVMIO, 0x8d, kvm_fpu); ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs); ioctl_iow_nr!(KVM_SET_LAPIC, KVMIO, 0x8f, kvm_lapic_state); ioctl_iow_nr!(KVM_SET_MSRS, KVMIO, 0x89, kvm_msrs); diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index 836cbe6f7..fe0316413 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -109,7 +109,18 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_ppoll), BpfRule::new(libc::SYS_connect), #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_clone3), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rt_sigaction), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_rseq), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_set_robust_list), + #[cfg(target_env = "gnu")] + BpfRule::new(libc::SYS_sched_getaffinity), + #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_clock_gettime), + BpfRule::new(libc::SYS_prctl), madvise_rule(), ]; syscall.append(&mut arch_syscall_whitelist()); @@ -146,6 +157,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32); arch_ioctl_allow_list(bpf_rule) } diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index ed2906517..abc431fd0 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -226,10 +226,21 @@ pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XSAVE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_REGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_SREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XSAVE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_FPU() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_SUPPORTED_CPUID() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEBUGREGS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_DEBUGREGS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_XCRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_XCRS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_LAPIC() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_CPUID2() as u32) } pub(crate) fn arch_syscall_whitelist() -> Vec { -- Gitee From c5fa715f2b60cf9574731eaaec37f01dedcd94fa Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 20 Apr 2024 19:05:12 +0800 Subject: [PATCH 1731/2187] ohui: send message sequentially It's possilbe that send message concurrently. So client might receive the mixed data. So let's ensure send message sequentially. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/channel.rs | 11 ------ ui/src/ohui_srv/msg_handle.rs | 64 +++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 6443b3408..861a16342 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -18,7 +18,6 @@ use anyhow::Result; use libc::iovec; use log::error; -use super::msg::*; use util::byte_code::ByteCode; use util::unix::UnixSock; @@ -92,16 +91,6 @@ impl OhUiChannel { Ok(()) } - pub fn send_message( - &self, - t: EventType, - body: &T, - ) -> Result<()> { - let hdr = EventMsgHdr::new(t); - self.send_by_obj(&hdr)?; - self.send_by_obj(body) - } - pub fn recv_slice(&self, data: &mut [u8]) -> Result { let len = data.len(); if len == 0 { diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index e734bc733..f00853b57 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -120,25 +120,25 @@ impl WindowState { } pub struct OhUiMsgHandler { - channel: Arc, state: Mutex, hmcode2svcode: HashMap, reader: Mutex, + writer: Mutex, } impl OhUiMsgHandler { pub fn new(channel: Arc) -> Self { OhUiMsgHandler { - channel, state: Mutex::new(WindowState::default()), hmcode2svcode: KeyCode::keysym_to_qkeycode(DpyMod::Ohui), - reader: Mutex::new(MsgReader::default()), + reader: Mutex::new(MsgReader::new(channel.clone())), + writer: Mutex::new(MsgWriter::new(channel)), } } pub fn handle_msg(&self, token_id: Arc>) -> Result<()> { let mut reader = self.reader.lock().unwrap(); - if !reader.recv(&self.channel)? { + if !reader.recv()? { return Ok(()); } @@ -237,7 +237,12 @@ impl OhUiMsgHandler { size_per_pixel: u32, ) { let body = HWCursorEvent::new(w, h, hot_x, hot_y, size_per_pixel); - if let Err(e) = self.channel.send_message(EventType::CursorDefine, &body) { + if let Err(e) = self + .writer + .lock() + .unwrap() + .send_message(EventType::CursorDefine, &body) + { error!("handle_cursor_define: failed to send message with error {e}"); } } @@ -302,7 +307,9 @@ impl OhUiMsgHandler { pub fn send_windowinfo(&self, w: u32, h: u32) { self.state.lock().unwrap().update_window_info(w, h); let body = WindowInfoEvent::new(w, h); - self.channel + self.writer + .lock() + .unwrap() .send_message(EventType::WindowInfo, &body) .unwrap(); } @@ -310,7 +317,9 @@ impl OhUiMsgHandler { pub fn handle_dirty_area(&self, x: u32, y: u32, w: u32, h: u32) { let body = FrameBufferDirtyEvent::new(x, y, w, h); if let Err(e) = self - .channel + .writer + .lock() + .unwrap() .send_message(EventType::FrameBufferDirty, &body) { error!("handle_dirty_area: failed to send message with error {e}"); @@ -318,8 +327,9 @@ impl OhUiMsgHandler { } } -#[derive(Default)] struct MsgReader { + /// socket to read + channel: Arc, /// cache for header pub header: EventMsgHdr, /// received byte size of header @@ -331,9 +341,19 @@ struct MsgReader { } impl MsgReader { - pub fn recv(&mut self, channel: &Arc) -> Result { - if self.recv_header(channel)? { - return self.recv_body(channel); + pub fn new(channel: Arc) -> Self { + MsgReader { + channel, + header: EventMsgHdr::default(), + header_ready: 0, + body: None, + body_ready: 0, + } + } + + pub fn recv(&mut self) -> Result { + if self.recv_header()? { + return self.recv_body(); } Ok(false) } @@ -344,17 +364,17 @@ impl MsgReader { self.body = None; } - fn recv_header(&mut self, channel: &Arc) -> Result { + fn recv_header(&mut self) -> Result { if self.header_ready == EVENT_MSG_HDR_SIZE as usize { return Ok(true); } let buf = self.header.as_mut_bytes(); - self.header_ready += channel.recv_slice(&mut buf[self.header_ready..])?; + self.header_ready += self.channel.recv_slice(&mut buf[self.header_ready..])?; Ok(self.header_ready == EVENT_MSG_HDR_SIZE as usize) } - fn recv_body(&mut self, channel: &Arc) -> Result { + fn recv_body(&mut self) -> Result { let body_size = self.header.size as usize; if body_size == self.body_ready { return Ok(true); @@ -372,8 +392,22 @@ impl MsgReader { unsafe { buf.set_len(body_size); } - self.body_ready += channel.recv_slice(&mut buf[self.body_ready..])?; + self.body_ready += self.channel.recv_slice(&mut buf[self.body_ready..])?; Ok(self.body_ready == body_size) } } + +struct MsgWriter(Arc); + +impl MsgWriter { + fn new(channel: Arc) -> Self { + MsgWriter(channel) + } + + fn send_message(&self, t: EventType, body: &T) -> Result<()> { + let hdr = EventMsgHdr::new(t); + self.0.send_by_obj(&hdr)?; + self.0.send_by_obj(body) + } +} -- Gitee From 1089cfb5736fce26178cbcf9648dfd6902143606 Mon Sep 17 00:00:00 2001 From: Yan Wen Date: Mon, 22 Apr 2024 21:16:14 +0800 Subject: [PATCH 1732/2187] machine: Fix the problem that startovirt cannot start due to failure to call create pit2? On Linux 6.8 kernel, starting Stratovirt fails with the following error: Caused by: 0: Failed to create PIT. The reason is that the implementation of the KVM_CREATE_PIT2 API has changed in the 6.8 kernel. Here is the latest implementation description of the API: Creates an in-kernel device model for the i8254 PIT. This call is only valid after enabling in-kernel irqchip support via KVM_CREATE_IRQCHIP. Signed-off-by: Yan Wen --- machine/src/x86_64/micro.rs | 3 +-- machine/src/x86_64/standard.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index ed2906517..f59090011 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -157,6 +157,7 @@ impl MachineOps for LightMachine { )); trace::cpu_topo(&topology); locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; let locked_hypervisor = locked_vm.base.hypervisor.lock().unwrap(); locked_hypervisor.init_machine(&locked_vm.base.sys_io, &locked_vm.base.sys_mem)?; drop(locked_hypervisor); @@ -166,8 +167,6 @@ impl MachineOps for LightMachine { vm_config.machine_config.nr_cpus, )?; - locked_vm.init_interrupt_controller(u64::from(vm_config.machine_config.nr_cpus))?; - // Add mmio devices locked_vm .create_replaceable_devices() diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index e294cd647..790e542a3 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -540,6 +540,7 @@ impl MachineOps for StdMachine { let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; + locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; let locked_hypervisor = locked_vm.base.hypervisor.lock().unwrap(); locked_hypervisor.init_machine(&locked_vm.base.sys_io, &locked_vm.base.sys_mem)?; drop(locked_hypervisor); @@ -549,8 +550,6 @@ impl MachineOps for StdMachine { nr_cpus, )?; - locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; - locked_vm .init_pci_host() .with_context(|| MachineError::InitPCIeHostErr)?; -- Gitee From ae94b119e3a006386dcd8b970f86609e92f72aac Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Apr 2024 14:48:33 +0800 Subject: [PATCH 1733/2187] qcow2: modify the function of extend_len This function is to expand a file to a specified size, so modify the function name. Signed-off-by: Xiao Ye --- block_backend/src/file.rs | 2 +- block_backend/src/qcow2/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 1b1e7f005..56fac1e08 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -202,7 +202,7 @@ impl FileDriver { Ok(disk_size) } - pub fn extend_len(&mut self, len: u64) -> Result<()> { + pub fn extend_to_len(&mut self, len: u64) -> Result<()> { let file_end = self.file.seek(SeekFrom::End(0))?; if len > file_end { self.file.set_len(len)?; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index fe86be3b5..75dce89c1 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -772,7 +772,7 @@ impl Qcow2Driver { } } } - self.driver.extend_len(addr + size)?; + self.driver.extend_to_len(addr + size)?; Ok(addr) } -- Gitee From 891b06d89678df1694f5bdf6eec62fe8d1ee6034 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 30 Mar 2024 00:20:00 +0800 Subject: [PATCH 1734/2187] stratovirt-img: add option of resize The operation of resize can change the virtual size of image. Signed-off-by: Xiao Ye --- block_backend/src/lib.rs | 2 ++ block_backend/src/qcow2/mod.rs | 4 ++++ block_backend/src/raw.rs | 4 ++++ image/src/img.rs | 4 ++++ image/src/main.rs | 5 ++++- 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index 5fe2c9dc6..cbe37dfaa 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -313,6 +313,8 @@ pub trait BlockDriverOps: Send { fn flush_request(&mut self) -> Result<()>; + fn resize(&mut self, new_size: u64) -> Result<()>; + fn drain_request(&self); fn get_inflight(&self) -> Arc; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 75dce89c1..b4342abbc 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1787,6 +1787,10 @@ impl BlockDriverOps for Qcow2Driver { self.driver.flush_request() } + fn resize(&mut self, _new_size: u64) -> Result<()> { + todo!() + } + fn drain_request(&self) { trace::block_drain_request(&self.driver.block_prop.id); self.driver.drain_request(); diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 1231c1b91..e53188a36 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -109,6 +109,10 @@ impl BlockDriverOps for RawDriver { self.driver.flush_request() } + fn resize(&mut self, _new_size: u64) -> Result<()> { + todo!() + } + fn drain_request(&self) { trace::block_drain_request(&self.driver.block_prop.id); self.driver.drain_request(); diff --git a/image/src/img.rs b/image/src/img.rs index d5e5f13b7..590d48269 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -258,6 +258,10 @@ pub(crate) fn image_check(args: Vec) -> Result<()> { } } +pub(crate) fn image_resize(_args: Vec) -> Result<()> { + todo!() +} + pub(crate) fn image_snapshot(args: Vec) -> Result<()> { let mut arg_parser = ArgsParse::create(vec!["l", "h", "help"], vec!["f", "c", "d", "a"], vec![]); diff --git a/image/src/main.rs b/image/src/main.rs index eecafd30b..055ac211f 100644 --- a/image/src/main.rs +++ b/image/src/main.rs @@ -20,7 +20,9 @@ use std::{ use anyhow::{bail, Result}; -use crate::img::{image_check, image_create, image_snapshot, print_help, print_version}; +use crate::img::{ + image_check, image_create, image_resize, image_snapshot, print_help, print_version, +}; const BINARY_NAME: &str = "stratovirt-img"; @@ -82,6 +84,7 @@ fn run(args: Vec) -> Result<()> { opt.as_str(); ("create", image_create, cmd_args), ("check", image_check, cmd_args), + ("resize", image_resize, cmd_args), ("snapshot", image_snapshot, cmd_args); ("-v" | "--version", print_version), ("-h" | "--help", print_help) -- Gitee From 4f427ff83e30989997d82351af7346671d19410b Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 10:27:29 +0800 Subject: [PATCH 1735/2187] stratovirt-img: add arg parse for image_resize Add arg parse for resize operation, the format is as follows: "stratovirt-img resize [-f fmt] filename +size" Signed-off-by: Xiao Ye --- image/src/img.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/image/src/img.rs b/image/src/img.rs index 590d48269..af02df5e4 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -17,7 +17,7 @@ use std::{ sync::Arc, }; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use crate::{cmdline::ArgsParse, BINARY_NAME}; use block_backend::{ @@ -258,8 +258,72 @@ pub(crate) fn image_check(args: Vec) -> Result<()> { } } -pub(crate) fn image_resize(_args: Vec) -> Result<()> { - todo!() +pub(crate) fn image_resize(mut args: Vec) -> Result<()> { + if args.len() < 2 { + bail!("Not enough arguments"); + } + let size_str = args.pop().unwrap(); + let mut arg_parser = ArgsParse::create(vec!["h", "help"], vec!["f"], vec![]); + arg_parser.parse(args)?; + + if arg_parser.opt_present("h") || arg_parser.opt_present("help") { + print_help(); + return Ok(()); + } + + // Parse the image path. + let len = arg_parser.free.len(); + let img_path = match len { + 0 => bail!("Expecting image file name and size"), + 1 => arg_parser.free[0].clone(), + _ => bail!("Unexpected argument: {}", arg_parser.free[1]), + }; + + // If the disk format is specified by user, it will be used firstly, + // or it will be detected. + let mut disk_fmt = None; + if let Some(fmt) = arg_parser.opt_str("f") { + disk_fmt = Some(DiskFormat::from_str(&fmt)?); + }; + + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; + let image_file = ImageFile::create(&img_path, false)?; + let detect_fmt = image_file.detect_img_format()?; + let real_fmt = image_file.check_img_format(disk_fmt, detect_fmt)?; + + let mut conf = BlockProperty::default(); + conf.format = real_fmt; + let mut driver: Box> = match real_fmt { + DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.try_clone()?, aio, conf)), + DiskFormat::Qcow2 => { + let mut qocw2_driver = + Qcow2Driver::new(image_file.file.try_clone()?, aio, conf.clone())?; + qocw2_driver.load_metadata(conf)?; + Box::new(qocw2_driver) + } + }; + + let old_size = driver.disk_size()?; + // Only expansion is supported currently. + let new_size = if size_str.starts_with("+") { + let size = memory_unit_conversion(&size_str, 1)?; + old_size + .checked_add(size) + .ok_or_else(|| anyhow!("Disk size is too large for chosen offset"))? + } else if size_str.starts_with("-") { + bail!("The shrink operation is not supported"); + } else { + let new_size = memory_unit_conversion(&size_str, 1)?; + if new_size < old_size { + bail!("The shrink operation is not supported"); + } + new_size + }; + + driver.resize(new_size)?; + println!("Image resized."); + + Ok(()) } pub(crate) fn image_snapshot(args: Vec) -> Result<()> { @@ -410,6 +474,7 @@ Stratovirt disk image utility Command syntax: create [-f fmt] [-o options] filename [size] check [-r [leaks | all]] [-no_print_error] [-f fmt] filename +resize [-f fmt] filename [+]size snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename Command parameters: -- Gitee From 2b993d6f32af54986b35ff412c1e064530a66cd9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 10:33:15 +0800 Subject: [PATCH 1736/2187] stratovirt-img: add resize operation for raw format. Add resize operation for raw format. Signed-off-by: Xiao Ye --- block_backend/src/raw.rs | 69 +++++++++++++++++++++++++++++++++++++--- util/src/file.rs | 2 +- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index e53188a36..c051b3f10 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -12,6 +12,7 @@ use std::{ fs::File, + os::unix::io::AsRawFd, sync::{ atomic::{AtomicBool, AtomicU64}, Arc, Mutex, @@ -22,9 +23,15 @@ use anyhow::{bail, Result}; use crate::{ file::{CombineRequest, FileDriver}, + qcow2::is_aligned, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, CheckResult, CreateOptions, + SECTOR_SIZE, +}; +use util::{ + aio::{get_iov_size, raw_write, Aio, Iovec}, + file::MAX_FILE_ALIGN, + unix::host_page_size, }; -use util::aio::{get_iov_size, Aio, Iovec}; pub struct RawDriver { driver: FileDriver, @@ -44,12 +51,43 @@ impl RawDriver { status: Arc::new(Mutex::new(BlockStatus::Init)), } } + + // Fill the first block with zero. + // get_file_alignment() detects the alignment length by submitting IO to the first sector. + // If this area is fallocated, misaligned IO will also return success, so we pre fill this area. + pub fn alloc_first_block(&mut self, new_size: u64) -> Result<()> { + let write_size = if new_size < MAX_FILE_ALIGN as u64 { + SECTOR_SIZE + } else { + MAX_FILE_ALIGN as u64 + }; + let max_align = std::cmp::max(MAX_FILE_ALIGN as u64, host_page_size()) as usize; + // SAFETY: allocate aligned memory and free it later. + let align_buf = unsafe { libc::memalign(max_align, write_size as usize) }; + if align_buf.is_null() { + bail!("Failed to alloc memory for write."); + } + + let ret = raw_write( + self.driver.file.as_raw_fd(), + align_buf as u64, + write_size as usize, + 0, + ); + // SAFETY: the memory is allocated in this function. + unsafe { libc::free(align_buf) }; + + if ret < 0 { + bail!("Failed to alloc first block, ret={}", ret); + } + Ok(()) + } } impl BlockDriverOps for RawDriver { fn create_image(&mut self, options: &CreateOptions) -> Result { let raw_options = options.raw()?; - self.driver.file.set_len(raw_options.img_size)?; + self.resize(raw_options.img_size)?; let image_info = format!("fmt=raw size={}", raw_options.img_size); Ok(image_info) } @@ -109,8 +147,31 @@ impl BlockDriverOps for RawDriver { self.driver.flush_request() } - fn resize(&mut self, _new_size: u64) -> Result<()> { - todo!() + fn resize(&mut self, new_size: u64) -> Result<()> { + if !is_aligned(SECTOR_SIZE, new_size) { + bail!( + "The new size {} is not aligned to {}", + new_size, + SECTOR_SIZE + ); + } + + let old_size = self.disk_size()?; + if new_size == old_size { + return Ok(()); + } + + let meta_data = self.driver.file.metadata()?; + if !meta_data.is_file() { + bail!("Cannot resize unregular file"); + } + + self.driver.extend_to_len(new_size)?; + if old_size == 0 { + self.alloc_first_block(new_size)?; + } + + Ok(()) } fn drain_request(&self) { diff --git a/util/src/file.rs b/util/src/file.rs index c4eaeabc4..0fd944508 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -20,7 +20,7 @@ use nix::fcntl::{fcntl, FcntlArg}; use nix::unistd::getpid; const MIN_FILE_ALIGN: u32 = 512; -const MAX_FILE_ALIGN: u32 = 4096; +pub const MAX_FILE_ALIGN: u32 = 4096; /// Permission to read const FILE_LOCK_READ: u64 = 0x01; /// Permission to write -- Gitee From 1a28a226098e291effae8bdf900ded1c72b391a4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Mon, 22 Apr 2024 22:50:01 +0800 Subject: [PATCH 1737/2187] Qcow2: modify the process of snapshot apply. As the addition of resize operation, there may be addition that the current active l1 table is not equal to the snapshot, so the process of snapshot apply should be modified. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 96 +++++++++++++++++++++----------- block_backend/src/qcow2/table.rs | 4 +- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index b4342abbc..7936ab583 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -823,42 +823,62 @@ impl Qcow2Driver { bail!("Snapshot L1 table offset invalid"); } - if snap.disk_size != self.virtual_disk_size() { - bail!("The virtual disk size has been changed"); - } - // Apply the l1 table of snapshot to active l1 table. let mut snap_l1_table = self .sync_aio .borrow_mut() .read_ctrl_cluster(snap.l1_table_offset, snap.l1_size as u64)?; // SAFETY: Upper limit of l1_size is decided by disk virtual size. - snap_l1_table.resize(self.header.l1_size as usize, 0); + snap_l1_table.resize(snap.l1_size as usize, 0); - // Increase the refcount of all clusters searched by L1 table. - self.qcow2_update_snapshot_refcount(snap.l1_table_offset, 1)?; + let cluster_size = self.header.cluster_size(); + let snap_l1_table_bytes = snap.l1_size as u64 * ENTRY_SIZE; + let snap_l1_table_clusters = bytes_to_clusters(snap_l1_table_bytes, cluster_size).unwrap(); + let new_l1_table_offset = self.alloc_cluster(snap_l1_table_clusters, true)?; - let active_l1_bytes = self.header.l1_size as u64 * ENTRY_SIZE; - if self.check_overlap( - METADATA_OVERLAP_CHECK_ACTIVEL1, - self.header.l1_table_offset, - active_l1_bytes, - ) != 0 - { - bail!("check overlap failed"); - } + // Increase the refcount of all clusters searched by L1 table. + self.qcow2_update_snapshot_refcount(snap.l1_table_offset, snap.l1_size as usize, 1)?; + self.sync_aio + .borrow_mut() + .write_ctrl_cluster(new_l1_table_offset, &snap_l1_table)?; - // Synchronous write back to disk. + // Sync active l1 table offset of header to disk. + let mut new_header = self.header.clone(); + new_header.l1_table_offset = new_l1_table_offset; + new_header.l1_size = snap.l1_size; + new_header.size = snap.disk_size; self.sync_aio .borrow_mut() - .write_ctrl_cluster(self.header.l1_table_offset, &snap_l1_table)?; + .write_buffer(0, &new_header.to_vec())?; + + let old_l1_table_offset = self.header.l1_table_offset; + let old_l1_size = self.header.l1_size; + self.header = new_header; + self.table.l1_table_offset = new_l1_table_offset; + self.table.l1_size = snap.l1_size; + self.table.l1_table = snap_l1_table; + + self.qcow2_update_snapshot_refcount(old_l1_table_offset, old_l1_size as usize, -1)?; - self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, -1)?; + // Free the snaphshot L1 table. + let old_l1_table_clusters = + bytes_to_clusters(old_l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); + self.refcount.update_refcount( + old_l1_table_offset, + old_l1_table_clusters, + -1, + false, + &Qcow2DiscardType::Snapshot, + )?; // Update flag of QCOW2_OFFSET_COPIED in current active l1 table, as it has been changed. - self.table.l1_table = snap_l1_table; - self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; + self.qcow2_update_snapshot_refcount( + self.header.l1_table_offset, + self.header.l1_size as usize, + 0, + )?; + self.flush()?; self.table.save_l1_table()?; // Discard unused clusters. @@ -889,7 +909,7 @@ impl Qcow2Driver { self.snapshot.snapshot_table_offset = new_snapshots_offset; // Decrease the refcounts of clusters referenced by the snapshot. - self.qcow2_update_snapshot_refcount(snap.l1_table_offset, -1)?; + self.qcow2_update_snapshot_refcount(snap.l1_table_offset, snap.l1_size as usize, -1)?; // Free the snaphshot L1 table. let l1_table_clusters = @@ -903,7 +923,11 @@ impl Qcow2Driver { )?; // Update the flag of the L1/L2 table entries. - self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 0)?; + self.qcow2_update_snapshot_refcount( + self.header.l1_table_offset, + self.header.l1_size as usize, + 0, + )?; // Free the cluster of the old snapshot table. self.refcount.update_refcount( @@ -957,7 +981,11 @@ impl Qcow2Driver { .write_ctrl_cluster(new_l1_table_offset, &self.table.l1_table)?; // Increase the refcount of all clusters searched by L1 table. - self.qcow2_update_snapshot_refcount(self.header.l1_table_offset, 1)?; + self.qcow2_update_snapshot_refcount( + self.header.l1_table_offset, + self.header.l1_size as usize, + 1, + )?; // Alloc new snapshot table. let (date_sec, date_nsec) = gettime()?; @@ -1092,9 +1120,12 @@ impl Qcow2Driver { } /// Update the refcounts of all clusters searched by l1_table_offset. - fn qcow2_update_snapshot_refcount(&mut self, l1_table_offset: u64, added: i32) -> Result<()> { - let l1_table_size = self.header.l1_size as usize; - let mut l1_table = self.table.l1_table.clone(); + fn qcow2_update_snapshot_refcount( + &mut self, + l1_table_offset: u64, + l1_table_size: usize, + added: i32, + ) -> Result<()> { debug!( "Update snapshot refcount: l1 table offset {:x}, active header l1 table addr {:x}, add {}", l1_table_offset, @@ -1102,13 +1133,14 @@ impl Qcow2Driver { added ); - if l1_table_offset != self.header.l1_table_offset { + let mut l1_table = if l1_table_offset != self.header.l1_table_offset { // Read snapshot l1 table from qcow2 file. - l1_table = self - .sync_aio + self.sync_aio .borrow_mut() - .read_ctrl_cluster(l1_table_offset, l1_table_size as u64)?; - } + .read_ctrl_cluster(l1_table_offset, l1_table_size as u64)? + } else { + self.table.l1_table.clone() + }; let mut old_l2_table_offset: u64; for (i, l1_entry) in l1_table.iter_mut().enumerate().take(l1_table_size) { diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 4f8c93a7a..5886becb4 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -81,8 +81,8 @@ pub struct Qcow2Table { cluster_bits: u64, cluster_size: u64, pub l1_table: Vec, - l1_table_offset: u64, - l1_size: u32, + pub l1_table_offset: u64, + pub l1_size: u32, pub l2_table_cache: Qcow2Cache, sync_aio: Rc>, l2_bits: u64, -- Gitee From 69dd2ce6a4530c44adfce7d47900fb62754af8dd Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 10:37:15 +0800 Subject: [PATCH 1738/2187] stratovirt-img: add resize operation for qcow2 format Add resize operation for qcow2 format. Signed-off-by: Xiao Ye --- block_backend/src/qcow2/mod.rs | 65 +++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 7936ab583..2dd1e4ac5 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -50,6 +50,7 @@ use crate::{ table::{Qcow2ClusterType, Qcow2Table}, }, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, CheckResult, CreateOptions, + SECTOR_SIZE, }; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::SnapshotInfo; @@ -59,6 +60,7 @@ use util::{ Iovec, OpCode, }, num_ops::{div_round_up, ranges_overlap, round_down, round_up}, + offset_of, time::{get_format_time, gettime}, }; @@ -504,15 +506,21 @@ impl Qcow2Driver { .borrow_mut() .write_ctrl_cluster(new_l1_table_offset, &new_l1_table)?; - // Flush new data to disk. - let mut new_header = self.header.clone(); - new_header.l1_size = new_l1_size as u32; - new_header.l1_table_offset = new_l1_table_offset; + // Update the message information, includes: + // entry size of l1 table and active l1 table offset. + // 4 bytes for l1 size and 8 bytes for l1 table offset. + let mut buf = vec![0; 12]; + BigEndian::write_u32(&mut buf[0..4], new_l1_size as u32); + BigEndian::write_u64(&mut buf[4..12], new_l1_table_offset); self.sync_aio .borrow_mut() - .write_buffer(0, &new_header.to_vec())?; - self.header = new_header; + .write_buffer(offset_of!(QcowHeader, l1_size) as u64, &buf)?; + self.header.l1_size = new_l1_size as u32; + self.header.l1_table_offset = new_l1_table_offset; + self.table.l1_size = new_l1_size as u32; + self.table.l1_table_offset = new_l1_table_offset; self.table.l1_table = new_l1_table; + self.free_cluster( old_l1_table_offset, old_l1_table_clusters, @@ -1574,7 +1582,7 @@ impl BlockDriverOps for Qcow2Driver { backing_file_offset: 0, backing_file_size: 0, cluster_bits: qcow2_options.cluster_size.trailing_zeros(), - size: qcow2_options.img_size, + size: 0, crypt_method: 0, l1_size: 0, l1_table_offset: 0, @@ -1620,10 +1628,8 @@ impl BlockDriverOps for Qcow2Driver { // Create qcow2 driver. self.load_refcount_table()?; - let l1_entry_size = cluster_size * (cluster_size / ENTRY_SIZE); - let l1_size = div_round_up(qcow2_options.img_size, l1_entry_size).unwrap(); - self.grow_l1_table(l1_size)?; - self.flush()?; + // Expand image to the new size. + self.resize(qcow2_options.img_size)?; let image_info = format!( "fmt=qcow2 cluster_size={} extended_l2=off compression_type=zlib size={} lazy_refcounts=off refcount_bits={}", @@ -1819,8 +1825,41 @@ impl BlockDriverOps for Qcow2Driver { self.driver.flush_request() } - fn resize(&mut self, _new_size: u64) -> Result<()> { - todo!() + fn resize(&mut self, new_size: u64) -> Result<()> { + if !is_aligned(SECTOR_SIZE, new_size) { + bail!( + "The new size {} is not aligned to {}", + new_size, + SECTOR_SIZE + ); + } + + if !self.snapshot.snapshots.is_empty() && self.header.version < 3 { + bail!("Can't resize a version 2 image with snapshots"); + } + + let old_size = self.virtual_disk_size(); + if new_size == old_size { + return Ok(()); + } + + let cluster_size = self.header.cluster_size(); + // Number of l1 table entries. + let l1_entry_size = cluster_size * (cluster_size / ENTRY_SIZE); + let new_l1_size = div_round_up(new_size, l1_entry_size).unwrap(); + + self.grow_l1_table(new_l1_size)?; + + // Write the size information back to the disk. + // The field of size in header needs 8 bytes. + let mut buf = vec![0; 8]; + BigEndian::write_u64(&mut buf, new_size); + self.sync_aio + .borrow_mut() + .write_buffer(offset_of!(QcowHeader, size) as u64, &buf)?; + self.header.size = new_size; + + self.flush() } fn drain_request(&self) { -- Gitee From f181373c96fc1ecd9f29f6e68b05f949d0e2a719 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 29 Mar 2024 11:57:45 +0800 Subject: [PATCH 1739/2187] stratovirt-img: add test for resize operation Add some unit testcase for image resize operation. Signed-off-by: Xiao Ye --- image/src/img.rs | 361 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) diff --git a/image/src/img.rs b/image/src/img.rs index af02df5e4..7f21220a4 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -628,6 +628,76 @@ mod test { } } + struct TestRawImage { + path: String, + } + + impl TestRawImage { + fn create(path: String, img_size: String) -> Self { + let create_str = format!("-f raw {} {}", path, img_size); + let create_args: Vec = create_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + assert!(image_create(create_args).is_ok()); + + Self { path } + } + + fn create_driver(&mut self) -> RawDriver<()> { + let mut conf = BlockProperty::default(); + conf.format = DiskFormat::Raw; + + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); + + let file = open_file(&self.path, false, false).unwrap(); + let raw_driver = RawDriver::new(file, aio, conf); + raw_driver + } + } + + impl Drop for TestRawImage { + fn drop(&mut self) { + assert!(remove_file(self.path.clone()).is_ok()); + } + } + + fn vec_is_fill_with(vec: &Vec, num: u8) -> bool { + for elem in vec { + if elem != &num { + return false; + } + } + true + } + + fn image_write( + driver: &mut dyn BlockDriverOps<()>, + offset: usize, + buf: &Vec, + ) -> Result<()> { + driver.write_vectored( + vec![Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + offset, + (), + ) + } + + fn image_read(driver: &mut dyn BlockDriverOps<()>, offset: usize, buf: &Vec) -> Result<()> { + driver.read_vectored( + vec![Iovec { + iov_base: buf.as_ptr() as u64, + iov_len: buf.len() as u64, + }], + offset, + (), + ) + } + /// Test the function of creating image. /// TestStep: /// 1. Create image with different args. @@ -1534,4 +1604,295 @@ mod test { assert_eq!(elem, 2); } } + + /// Test the function of resize image. + /// + /// TestStep: + /// 1. Resize the image with different args. + /// Expect: + /// 1. If the format of args is invalid, the operation return error. + #[test] + fn test_arg_parse_of_image_resize() { + let path = "/tmp/test_arg_parse_of_image_resize.img"; + let test_case = vec![ + ("qcow2", "-f qcow2 img_path +1M", true), + ("qcow2", "-f raw img_path +1M", true), + ("qcow2", "img_path +0M", true), + ("qcow2", "img_path +1M", true), + ("qcow2", "img_path 10M", true), + ("qcow2", "img_path 11M", true), + ("qcow2", "img_path 9M", false), + ("qcow2", "img_path 1M", false), + ("qcow2", "img_path ++1M", false), + ("qcow2", "img_path +511", false), + ("qcow2", "img_path -1M", false), + ("qcow2", "img_path +1M extra_args", false), + ("qcow2", "+1M", false), + ("qcow2", "", false), + ("raw", "img_path +1M", true), + ("raw", "img_path 18446744073709551615", false), + ("raw", "img_path +511", false), + ("raw", "-f raw img_path +1M", true), + ("raw", "-f qcow2 img_path +1M", false), + ]; + + for (img_type, cmd, res) in test_case { + assert!(image_create(vec![ + "-f".to_string(), + img_type.to_string(), + path.to_string(), + "+10M".to_string() + ]) + .is_ok()); + + // Apply resize operation. + let cmd = cmd.replace("img_path", path); + let args: Vec = cmd + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + assert_eq!(image_resize(args).is_ok(), res); + + assert!(remove_file(path.to_string()).is_ok()); + } + } + + /// Test the function of resize for raw image. + /// + /// TestStep: + /// 1. Create a raw image with size of 10M. + /// 2. Write data 1 in the range of [0, 10M], with expect 1. + /// 3. Resize the image with +10M. + /// 4. Write data 2 in the range of [10M, 20M], with expect 1. + /// 5. Read the data from the image, with expect 2. + /// + /// Expect: + /// 1. Data successfully written to disk. + /// 2. The data read from disk meets expectation. + #[test] + fn test_resize_raw() { + // Step 1: Create a raw image with size of 10M. + let path = "/tmp/test_resize_raw.raw"; + let mut test_image = TestRawImage::create(path.to_string(), "10M".to_string()); + + // Step 2: Write data 1 in the range of [0, 10M]. + let mut offset: usize = 0; + let buf = vec![1; 10240]; + let mut driver = test_image.create_driver(); + while offset < 10 * M as usize { + assert!(image_write(&mut driver, offset as usize, &buf).is_ok()); + offset += 10240; + } + drop(driver); + + // Step 3: Resize the image with 10M. + assert!(image_resize(vec![ + "-f".to_string(), + "raw".to_string(), + path.to_string(), + "+10M".to_string(), + ]) + .is_ok()); + + // Step 4: Write data 2 in the range of [10M, 20M] + let buf = vec![2; 10240]; + let mut driver = test_image.create_driver(); + while offset < (10 + 10) * M as usize { + assert!(image_write(&mut driver, offset as usize, &buf).is_ok()); + offset += 10240; + } + + // Step 5: Read the data from the image, the data read from disk meets expectation. + let mut offset = 0; + let buf = vec![0; 10240]; + while offset < 10 * M as usize { + assert!(image_read(&mut driver, offset, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 1)); + offset += 10240; + } + + while offset < (10 + 10) * M as usize { + assert!(image_read(&mut driver, offset, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 2)); + offset += 10240; + } + } + + /// Test the function of resize for qcow2 image. + /// + /// TestStep: + /// 1. Create a qcow2 image with size of 10M. + /// 2. Write data 1 in the range of [0, 10M], with expect 1. + /// 3. Resize the image with +10M. + /// 4. Write data 2 in the range of [10M, 20M], with expect 1. + /// 5. Read the data from the image, with expect 2. + /// + /// Expect: + /// 1. Data successfully written to disk. + /// 2. The data read from disk meets expectation. + #[test] + fn test_resize_qcow2() { + let path = "/tmp/test_resize_qcow2.qcow2"; + // Step 1: Create a qcow2 image with size of 10M. + let test_image = TestQcow2Image::create(16, 16, path, "+10M"); + + // Step 2: Write data of 1 to the disk in the range of [0, 10M] + let mut offset = 0; + let buf = vec![1; 10240]; + let mut qcow2_driver = test_image.create_driver(); + while offset < 10 * M { + assert!(image_write(&mut qcow2_driver, offset as usize, &buf).is_ok()); + offset += 10240; + } + // If the offset exceed the virtual size, it is expect to be failed. + assert!(image_write(&mut qcow2_driver, offset as usize, &buf).is_err()); + drop(qcow2_driver); + + // Step 3: Resize the image with 10M. + assert!(image_resize(vec![ + "-f".to_string(), + "qcow2".to_string(), + path.to_string(), + "+10M".to_string(), + ]) + .is_ok()); + + // Step4: Write data 2 in the range of [10M, 20M] + let buf = vec![2; 10240]; + let mut qcow2_driver = test_image.create_driver(); + while offset < (10 + 10) * M { + assert!(image_write(&mut qcow2_driver, offset as usize, &buf).is_ok()); + offset += 10240; + } + assert!(image_write(&mut qcow2_driver, offset as usize, &buf).is_err()); + + // Step 5: Read the data from the image. + let mut offset = 0; + let buf = vec![0; 10240]; + while offset < 10 * M { + assert!(image_read(&mut qcow2_driver, offset as usize, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 1)); + offset += 10240; + } + + while offset < (10 + 10) * M { + assert!(image_read(&mut qcow2_driver, offset as usize, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 2)); + offset += 10240; + } + assert!(image_read(&mut qcow2_driver, offset as usize, &buf).is_err()); + } + + /// Test resize image with snapshot operation. + /// + /// TestStep: + /// 1. Create a qcow2 image with size of 1G. + /// 2. Perform the operations of snapshot and image resize. + #[test] + fn test_image_resize_with_snapshot() { + let path = "/tmp/test_image_resize_with_snapshot.qcow2"; + let test_image = TestQcow2Image::create(16, 16, path, "1G"); + let mut driver = test_image.create_driver(); + let buf = vec![1; 1024 * 1024]; + assert!(image_write(&mut driver, 0, &buf).is_ok()); + assert_eq!(driver.header.size, 1 * G); + drop(driver); + let quite = false; + let fix = FIX_ERRORS | FIX_LEAKS; + + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot_1G".to_string(), + path.to_string() + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let mut driver = test_image.create_driver(); + let buf = vec![2; 1024 * 1024]; + assert!(image_write(&mut driver, 0, &buf).is_ok()); + assert_eq!(driver.header.size, 1 * G); + drop(driver); + assert_eq!(test_image.check_image(quite, fix), true); + + assert!(image_resize(vec![ + "-f".to_string(), + "qcow2".to_string(), + path.to_string(), + "+20G".to_string(), + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let mut driver = test_image.create_driver(); + let buf = vec![3; 1024 * 1024]; + assert!(image_write(&mut driver, 20 * G as usize, &buf).is_ok()); + + assert!(image_read(&mut driver, 0, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 2)); + assert_eq!(driver.header.size, 21 * G); + drop(driver); + assert_eq!(test_image.check_image(quite, fix), true); + + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot_21G".to_string(), + path.to_string() + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let mut driver = test_image.create_driver(); + let buf = vec![4; 1024 * 1024]; + assert!(image_write(&mut driver, 20 * G as usize, &buf).is_ok()); + assert_eq!(driver.header.size, 21 * G); + drop(driver); + assert_eq!(test_image.check_image(quite, fix), true); + + assert!(image_resize(vec![ + "-f".to_string(), + "qcow2".to_string(), + path.to_string(), + "+10G".to_string(), + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let mut driver = test_image.create_driver(); + let buf = vec![5; 1024 * 1024]; + assert!(image_write(&mut driver, 30 * G as usize, &buf).is_ok()); + + assert!(image_read(&mut driver, 20 * G as usize, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 4)); + assert_eq!(driver.header.size, 31 * G); + drop(driver); + assert_eq!(test_image.check_image(quite, fix), true); + + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot_1G".to_string(), + path.to_string() + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let mut driver = test_image.create_driver(); + let buf = vec![0; 1024 * 1024]; + assert!(image_read(&mut driver, 0, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 1)); + assert_eq!(driver.header.size, 1 * G); + drop(driver); + assert_eq!(test_image.check_image(quite, fix), true); + + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot_21G".to_string(), + path.to_string() + ]) + .is_ok()); + assert_eq!(test_image.check_image(quite, fix), true); + let mut driver = test_image.create_driver(); + let buf = vec![0; 1024 * 1024]; + assert!(image_read(&mut driver, 20 * G as usize, &buf).is_ok()); + assert!(vec_is_fill_with(&buf, 3)); + assert_eq!(driver.header.size, 21 * G); + drop(driver); + assert_eq!(test_image.check_image(quite, fix), true); + } } -- Gitee From dd68f6b9298d6251261bf12e8bb05d1971df17f2 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sun, 31 Mar 2024 13:06:13 +0800 Subject: [PATCH 1740/2187] stratovirt-img: add docs for image_resize Add docs for image resize. Signed-off-by: Xiao Ye --- docs/stratovirt-img.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/stratovirt-img.md b/docs/stratovirt-img.md index 8aeca46b8..e44be8d67 100644 --- a/docs/stratovirt-img.md +++ b/docs/stratovirt-img.md @@ -55,6 +55,26 @@ stratovirt-img check img_path Note: The command of check is not supported by raw format. +## Resize + +Change the virtual size of the disk. +- `+size`means increase from old size, while `size` means resize to new size. + +Command syntax: + +```shell +resize [-f fmt] img_path [+]size +``` + +Sample Configuration: + +```shell +stratovirt-img resize -f qcow2 img_path +size +stratovirt-img resize -f raw img_path +size +``` + +Note: Shrink operation is not supported now. + ## Snapshot Operating internal snapshot for disk, it is only supported by qcow2. -- Gitee From f5ff0519b63653635cd31b3abb52e1cb8b5089e4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 23 Apr 2024 16:56:49 +0800 Subject: [PATCH 1741/2187] file: optimize some error log Signed-off-by: Xiao Ye --- util/src/aio/raw.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 69cb0bae5..4654a7c07 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -37,7 +37,8 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { } if ret < 0 { error!( - "Failed to pread: buf{}, size{}, offset{}, errno{}.", + "Failed to pread: fd {} buf {:#x}, size {}, offset{:#x}, errno {}.", + fd, buf, size, offset, @@ -66,7 +67,8 @@ pub fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { } if ret < 0 { error!( - "Failed to preadv: offset{}, errno{}.", + "Failed to preadv: fd {} offset {:#x}, errno {}.", + fd, offset, nix::errno::errno(), ); @@ -93,7 +95,8 @@ pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { } if ret < 0 { error!( - "Failed to pwrite: buf{}, size{}, offset{}, errno{}.", + "Failed to pwrite: fd {} buf {:#x}, size{}, offset {:#x}, errno {}.", + fd, buf, size, offset, @@ -122,7 +125,8 @@ pub fn raw_writev(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { } if ret < 0 { error!( - "Failed to pwritev: offset{}, errno{}.", + "Failed to pwritev: fd {} offset {:#x}, errno {}.", + fd, offset, nix::errno::errno(), ); @@ -134,7 +138,7 @@ pub fn raw_datasync(fd: RawFd) -> i64 { // SAFETY: fd is valid. let ret = unsafe { i64::from(fdatasync(fd)) }; if ret < 0 { - error!("Failed to fdatasync: errno{}.", nix::errno::errno()); + error!("Failed to fdatasync: errno {}.", nix::errno::errno()); } ret } @@ -143,7 +147,7 @@ pub fn raw_discard(fd: RawFd, offset: usize, size: u64) -> i32 { let ret = do_fallocate(fd, FallocateMode::PunchHole, true, offset as u64, size); if ret < 0 && ret != -libc::ENOTSUP { - error!("Failed to fallocate for {}, errno {}.", fd, ret); + error!("Failed to fallocate for fd {}, errno {}.", fd, ret); } ret } -- Gitee From f7ee8889c4c9f1a145c2e2afd3553e9094118352 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 24 Apr 2024 06:25:40 +0800 Subject: [PATCH 1742/2187] docs: fix error `sasl` cmdline for VNC `sasl` doesn't need "=on", fix it. Fix: 99e005cba8a(VNC: Add docs for VNC) Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 7a3c87e1b..83a55a8aa 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1036,7 +1036,7 @@ Sample Configuration: ```shell -object authz-simple,id=authz0,identity=username -object tls-creds-x509,id=vnc-tls-creds0,dir=/etc/pki/vnc --vnc 0.0.0.0:0,tls-creds=vnc-tls-creds0,sasl=on,sasl-authz=authz0 +-vnc 0.0.0.0:0,tls-creds=vnc-tls-creds0,sasl,sasl-authz=authz0 ``` Note: 1. Only one client can be connected at the same time. Follow-up clients connections will result in failure. 2. TLS encrypted transmission can be configured separately, but authentication must be used together with encryption. -- Gitee From 0eff5f9e07d1dd3a42a9fd7d3e330cf40002a1ae Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 24 Apr 2024 02:59:41 +0800 Subject: [PATCH 1743/2187] virtio: modify error for VirtioMmioDevice and VirtioPciDevice Add message of device names in error logs for VirtioMmioDevice and VirtioPciDevice. Signed-off-by: Xiao Ye --- virtio/src/transport/virtio_mmio.rs | 39 ++++++++----------- virtio/src/transport/virtio_pci.rs | 60 +++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 260c0dee1..93c181c10 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -409,10 +409,8 @@ impl SysBusDevOps for VirtioMmioDevice { Ok(v) => v, Err(ref e) => { error!( - "Failed to read mmio register {}, type: {}, {:?}", - offset, - self.device.lock().unwrap().device_type(), - e, + "Failed to read mmio register {:#x}, device: {}, {:?}", + offset, self.base.base.id, e, ); return false; } @@ -427,9 +425,9 @@ impl SysBusDevOps for VirtioMmioDevice { .read_config(offset - 0x100, data) { error!( - "Failed to read virtio-dev config space {} type: {} {:?}", + "Failed to read virtio-dev config space {:#x} device: {}, {:?}", offset - 0x100, - self.device.lock().unwrap().device_type(), + self.base.base.id, e, ); return false; @@ -437,9 +435,8 @@ impl SysBusDevOps for VirtioMmioDevice { } _ => { warn!( - "Failed to read mmio register: overflows, offset is 0x{:x}, type: {}", - offset, - self.device.lock().unwrap().device_type(), + "Failed to read mmio register: overflows, offset is {:#x}, device: {}", + offset, self.base.base.id ); } }; @@ -454,10 +451,8 @@ impl SysBusDevOps for VirtioMmioDevice { let value = LittleEndian::read_u32(data); if let Err(ref e) = self.write_common_config(offset, value) { error!( - "Failed to write mmio register {}, type: {}, {:?}", - offset, - self.device.lock().unwrap().device_type(), - e, + "Failed to write mmio register {:#x}, device: {}, {:?}", + offset, self.base.base.id, e, ); return false; } @@ -474,9 +469,8 @@ impl SysBusDevOps for VirtioMmioDevice { drop(locked_dev); if let Err(ref e) = self.activate() { error!( - "Failed to activate dev, type: {}, {:?}", - self.device.lock().unwrap().device_type(), - e, + "Failed to activate dev, device: {}, {:?}", + self.base.base.id, e, ); return false; } @@ -488,26 +482,25 @@ impl SysBusDevOps for VirtioMmioDevice { if locked_device.check_device_status(CONFIG_STATUS_DRIVER, CONFIG_STATUS_FAILED) { if let Err(ref e) = locked_device.write_config(offset - 0x100, data) { error!( - "Failed to write virtio-dev config space {}, type: {}, {:?}", + "Failed to write virtio-dev config space {:#x}, device: {}, {:?}", offset - 0x100, - locked_device.device_type(), + self.base.base.id, e, ); return false; } } else { - error!("Failed to write virtio-dev config space: driver is not ready 0x{:X}, type: {}", + error!("Failed to write virtio-dev config space: driver is not ready {:#x}, device: {}", locked_device.device_status(), - locked_device.device_type(), + self.base.base.id, ); return false; } } _ => { warn!( - "Failed to write mmio register: overflows, offset is 0x{:x} type: {}", - offset, - self.device.lock().unwrap().device_type(), + "Failed to write mmio register: overflows, offset is {:#x} device: {}", + offset, self.base.base.id, ); return false; } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index aa27db4f6..0303fccb5 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -457,7 +457,10 @@ impl VirtioPciDevice { } let queue = Queue::new(*q_config, queue_type).unwrap(); if q_config.ready && !queue.is_valid(&self.sys_mem) { - error!("Failed to activate device: Invalid queue"); + error!( + "Failed to activate device {}: Invalid queue", + self.base.base.id + ); return false; } let arc_queue = Arc::new(Mutex::new(queue)); @@ -479,12 +482,18 @@ impl VirtioPciDevice { } let call_evts = NotifyEventFds::new(queue_num); if let Err(e) = locked_dev.set_guest_notifiers(&call_evts.events) { - error!("Failed to set guest notifiers, error is {:?}", e); + error!( + "Failed to set guest notifiers, device {} error is {:?}", + self.base.base.id, e + ); return false; } drop(locked_dev); if !self.queues_register_irqfd(&call_evts.events) { - error!("Failed to register queues irqfd."); + error!( + "Failed to register queues irqfd for device {}", + self.base.base.id + ); return false; } locked_dev = self.device.lock().unwrap(); @@ -496,7 +505,10 @@ impl VirtioPciDevice { self.interrupt_cb.clone().unwrap(), queue_evts, ) { - error!("Failed to activate device, error is {:?}", e); + error!( + "Failed to activate device {}, error is {:?}", + self.base.base.id, e + ); return false; } @@ -516,7 +528,10 @@ impl VirtioPciDevice { let mut locked_dev = self.device.lock().unwrap(); if locked_dev.device_activated() { if let Err(e) = locked_dev.deactivate() { - error!("Failed to deactivate virtio device, error is {:?}", e); + error!( + "Failed to deactivate virtio device {}, error is {:?}", + self.base.base.id, e + ); return false; } locked_dev.virtio_base_mut().reset(); @@ -621,7 +636,7 @@ impl VirtioPciDevice { } COMMON_GF_REG => { if locked_device.device_status() & CONFIG_STATUS_FEATURES_OK != 0 { - error!("it's not allowed to set features after having been negoiated"); + error!("it's not allowed to set features after having been negoiated for device {}", self.base.base.id); return Ok(()); } let gfeatures_sel = locked_device.gfeatures_sel(); @@ -652,13 +667,16 @@ impl VirtioPciDevice { let features = (locked_device.driver_features(1) as u64) << 32; if !virtio_has_feature(features, VIRTIO_F_VERSION_1) { error!( - "Device is modern only, but the driver not support VIRTIO_F_VERSION_1" + "Device {} is modern only, but the driver not support VIRTIO_F_VERSION_1", self.base.base.id ); return Ok(()); } } if value != 0 && (locked_device.device_status() & !value) != 0 { - error!("Driver must not clear a device status bit"); + error!( + "Driver must not clear a device status bit, device {}", + self.base.base.id + ); return Ok(()); } @@ -688,7 +706,10 @@ impl VirtioPciDevice { .map(|config| config.size = value as u16)?, COMMON_Q_ENABLE_REG => { if value != 1 { - error!("Driver set illegal value for queue_enable {}", value); + error!( + "Driver set illegal value for queue_enable {}, device {}", + value, self.base.base.id + ); return Err(anyhow!(PciError::QueueEnable(value))); } locked_device @@ -924,7 +945,7 @@ impl VirtioPciDevice { }; if let Err(e) = result { error!( - "Failed to access virtio configuration through VirtioPciCfgAccessCap. {:?}", + "Failed to access virtio configuration through VirtioPciCfgAccessCap. device is {}, error is {:?}", self.base.base.id, e ); } @@ -940,7 +961,10 @@ impl VirtioPciDevice { fn queues_register_irqfd(&self, call_fds: &[Arc]) -> bool { if self.base.config.msix.is_none() { - error!("Failed to get msix in virtio pci device configure"); + error!( + "Failed to get msix in virtio pci device configure, device is {}", + self.base.base.id + ); return false; } @@ -1191,8 +1215,8 @@ impl PciDevOps for VirtioPciDevice { let end = offset + data_size; if end > PCIE_CONFIG_SPACE_SIZE || data_size > REG_SIZE { error!( - "Failed to write pcie config space at offset 0x{:x} with data size {}", - offset, data_size + "Failed to write pcie config space at offset {:#x} with data size {}, device is {}", + offset, data_size, self.base.base.id ); return; } @@ -1333,10 +1357,16 @@ impl MigrationHook for VirtioPciDevice { .unwrap() .activate(self.sys_mem.clone(), cb, queue_evts) { - error!("Failed to resume device, error is {:?}", e); + error!( + "Failed to resume device {}, error is {:?}", + self.base.base.id, e + ); } } else { - error!("Failed to resume device: No interrupt callback"); + error!( + "Failed to resume device {}: No interrupt callback", + self.base.base.id + ); } Ok(()) -- Gitee From f90fbb865bec1ed5cd14bef0ae0ba6fc9d8a1f9f Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 24 Apr 2024 14:42:26 +0800 Subject: [PATCH 1744/2187] virtio: convert some log level Modify some log for virtio. Signed-off-by: Xiao Ye --- trace/trace_info/virtio.toml | 6 ------ virtio/src/transport/virtio_mmio.rs | 4 ++-- virtio/src/transport/virtio_pci.rs | 11 ++++++----- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/trace/trace_info/virtio.toml b/trace/trace_info/virtio.toml index 1e7def41b..7bff17c5e 100644 --- a/trace/trace_info/virtio.toml +++ b/trace/trace_info/virtio.toml @@ -196,12 +196,6 @@ args = "vring: u64, event_idx: u16" message = "virtqueue {:#X} set avail event idx {}" enabled = true -[[events]] -name = "virtio_tpt_common" -args = "func: &str, id: &str" -message = "{} for device {}" -enabled = true - [[events]] name = "virtio_tpt_read_common_config" args = "id: &str, offset: u64" diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 93c181c10..55381c5ea 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, warn}; +use log::{debug, error, info, warn}; use vmm_sys_util::eventfd::EventFd; use crate::error::VirtioError; @@ -188,7 +188,7 @@ impl VirtioMmioDevice { /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate(&mut self) -> Result<()> { - trace::virtio_tpt_common("activate", &self.base.base.id); + info!("func: activate, id: {:?}", &self.base.base.id); let mut locked_dev = self.device.lock().unwrap(); let queue_num = locked_dev.queue_num(); let queue_type = locked_dev.queue_type(); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 40d493e60..5fc891686 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -17,7 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use log::{debug, error, warn}; +use log::{debug, error, info, warn}; use vmm_sys_util::eventfd::EventFd; #[cfg(feature = "virtio_gpu")] @@ -429,7 +429,7 @@ impl VirtioPciDevice { } fn activate_device(&self) -> bool { - trace::virtio_tpt_common("activate_device", &self.base.base.id); + info!("func: activate_device, id: {:?}", &self.base.base.id); let mut locked_dev = self.device.lock().unwrap(); if locked_dev.device_activated() { return true; @@ -514,7 +514,7 @@ impl VirtioPciDevice { } fn deactivate_device(&self) -> bool { - trace::virtio_tpt_common("deactivate_device", &self.base.base.id); + info!("func: deactivate_device, id: {:?}", &self.base.base.id); if self.need_irqfd && self.base.config.msix.is_some() { let msix = self.base.config.msix.as_ref().unwrap(); if msix.lock().unwrap().unregister_irqfd().is_err() { @@ -1018,6 +1018,7 @@ impl PciDevOps for VirtioPciDevice { } fn realize(mut self) -> Result<()> { + info!("func: realize, id: {:?}", &self.base.base.id); self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -1185,7 +1186,7 @@ impl PciDevOps for VirtioPciDevice { } fn unrealize(&mut self) -> Result<()> { - trace::virtio_tpt_common("unrealize", &self.base.base.id); + info!("func: unrealize, id: {:?}", &self.base.base.id); self.device .lock() .unwrap() @@ -1233,7 +1234,7 @@ impl PciDevOps for VirtioPciDevice { } fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - trace::virtio_tpt_common("reset", &self.base.base.id); + info!("func: reset, id: {:?}", &self.base.base.id); self.deactivate_device(); self.device .lock() -- Gitee From 590e548095711d674f407d0dbb3f1e914d10631c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 24 Apr 2024 15:41:34 +0800 Subject: [PATCH 1745/2187] machine: using function `add_virtio_pci_device` to create virtio-pci device Using function `add_virtio_pci_device` to create virtio-pci device to reduce duplicate code. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 73 +++++------------------------- virtio/src/transport/virtio_pci.rs | 14 ++++-- 2 files changed, 21 insertions(+), 66 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6c13c03c1..5fe00f96c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -575,18 +575,7 @@ pub trait MachineOps { _ => { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let mut virtio_pci_device = VirtioPciDevice::new( - device_cfg.id.clone(), - devfn, - sys_mem, - vsock.clone(), - parent_bus, - multi_func, - ); - virtio_pci_device.enable_need_irqfd(); - virtio_pci_device - .realize() + self.add_virtio_pci_device(&device_cfg.id, &bdf, vsock.clone(), multi_func, true) .with_context(|| "Failed to add virtio pci vsock device")?; } } @@ -681,18 +670,9 @@ pub trait MachineOps { if config.addr.is_none() || config.bus.is_none() { bail!("virtio balloon pci config is error!"); } - let bdf = PciBdf { - bus: config.bus.unwrap(), - addr: config.addr.unwrap(), - }; + let bdf = PciBdf::new(config.bus.unwrap(), config.addr.unwrap()); let multi_func = config.multifunction.unwrap_or_default(); - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let sys_mem = self.get_sys_mem().clone(); - let virtio_pci_device = VirtioPciDevice::new( - config.id, devfn, sys_mem, balloon, parent_bus, multi_func, - ); - virtio_pci_device - .realize() + self.add_virtio_pci_device(&config.id, &bdf, balloon, multi_func, false) .with_context(|| "Failed to add virtio pci balloon device")?; } } @@ -724,17 +704,7 @@ pub trait MachineOps { _ => { let bdf = serial_cfg.pci_bdf.unwrap(); let multi_func = serial_cfg.multifunction; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let virtio_pci_device = VirtioPciDevice::new( - serial_cfg.id.clone(), - devfn, - sys_mem, - serial.clone(), - parent_bus, - multi_func, - ); - virtio_pci_device - .realize() + self.add_virtio_pci_device(&serial_cfg.id, &bdf, serial.clone(), multi_func, false) .with_context(|| "Failed to add virtio pci serial device")?; } } @@ -833,9 +803,9 @@ pub trait MachineOps { /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration arguments. fn add_virtio_rng(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_rng_dev(vm_config, cfg_args)?; + let rng_cfg = parse_rng_dev(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); - let rng_dev = Arc::new(Mutex::new(Rng::new(device_cfg.clone()))); + let rng_dev = Arc::new(Mutex::new(Rng::new(rng_cfg.clone()))); match parse_device_type(cfg_args)?.as_str() { "virtio-rng-device" => { @@ -846,23 +816,12 @@ pub trait MachineOps { _ => { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let sys_mem = self.get_sys_mem().clone(); - let vitio_pci_device = VirtioPciDevice::new( - device_cfg.id.clone(), - devfn, - sys_mem, - rng_dev.clone(), - parent_bus, - multi_func, - ); - vitio_pci_device - .realize() + self.add_virtio_pci_device(&rng_cfg.id, &bdf, rng_dev.clone(), multi_func, false) .with_context(|| "Failed to add pci rng device")?; } } - MigrationManager::register_device_instance(RngState::descriptor(), rng_dev, &device_cfg.id); + MigrationManager::register_device_instance(RngState::descriptor(), rng_dev, &rng_cfg.id); Ok(()) } @@ -893,16 +852,10 @@ pub trait MachineOps { .with_context(|| "Failed to add vhost user fs device")?; } _ => { - let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); + let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem))); let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - - let mut vitio_pci_device = - VirtioPciDevice::new(id_clone, devfn, sys_mem, device, parent_bus, multi_func); - vitio_pci_device.enable_need_irqfd(); - vitio_pci_device - .realize() + self.add_virtio_pci_device(&id_clone, &bdf, device, multi_func, true) .with_context(|| "Failed to add pci fs device")?; } } @@ -1425,17 +1378,15 @@ pub trait MachineOps { ) -> Result>> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; let sys_mem = self.get_sys_mem(); - let mut pcidev = VirtioPciDevice::new( + let pcidev = VirtioPciDevice::new( id.to_string(), devfn, sys_mem.clone(), device, parent_bus, multi_func, + need_irqfd, ); - if need_irqfd { - pcidev.enable_need_irqfd(); - } let clone_pcidev = Arc::new(Mutex::new(pcidev.clone())); pcidev .realize() diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index aa27db4f6..8c45a674f 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -326,6 +326,7 @@ impl VirtioPciDevice { device: Arc>, parent_bus: Weak>, multi_func: bool, + need_irqfd: bool, ) -> Self { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { @@ -342,14 +343,10 @@ impl VirtioPciDevice { notify_eventfds: Arc::new(NotifyEventFds::new(queue_num)), interrupt_cb: None, multi_func, - need_irqfd: false, + need_irqfd, } } - pub fn enable_need_irqfd(&mut self) { - self.need_irqfd = true; - } - fn assign_interrupt_cb(&mut self) { let locked_dev = self.device.lock().unwrap(); let virtio_base = locked_dev.virtio_base(); @@ -1456,6 +1453,7 @@ mod tests { virtio_dev.clone(), Arc::downgrade(&parent_bus), false, + false, ); // Read virtio device features @@ -1513,6 +1511,7 @@ mod tests { virtio_dev.clone(), Arc::downgrade(&parent_bus), false, + false, ); // Read Queue's Descriptor Table address @@ -1564,6 +1563,7 @@ mod tests { cloned_virtio_dev, Arc::downgrade(&parent_bus), false, + false, ); assert!(init_msix( @@ -1638,6 +1638,7 @@ mod tests { virtio_dev, Arc::downgrade(&parent_bus), false, + false, ); virtio_pci.init_write_mask(false).unwrap(); virtio_pci.init_write_clear_mask(false).unwrap(); @@ -1678,6 +1679,7 @@ mod tests { virtio_dev, Arc::downgrade(&parent_bus), false, + false, ); assert!(virtio_pci.realize().is_ok()); } @@ -1717,6 +1719,7 @@ mod tests { virtio_dev.clone(), Arc::downgrade(&parent_bus), false, + false, ); #[cfg(target_arch = "aarch64")] virtio_pci.base.config.set_interrupt_pin(); @@ -1808,6 +1811,7 @@ mod tests { virtio_dev, Arc::downgrade(&parent_bus), true, + false, ); assert!(init_multifunction( -- Gitee From 0162d9d50d6c52b5fb4cd265678c77c2cdcab707 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 25 Apr 2024 00:07:59 +0800 Subject: [PATCH 1746/2187] qcow2: fix some error in qcow2 If there is some io error in loading metadata for qcow2 disk, it's better to report io error rather than virtio error. Signed-off-by: Xiao Ye --- block_backend/src/file.rs | 16 +++++----------- block_backend/src/qcow2/mod.rs | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 56fac1e08..83237651e 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -102,7 +102,7 @@ impl FileDriver { completecb: T, ) -> Result<()> { if req_list.is_empty() { - return self.complete_request(opcode, &Vec::new(), 0, 0, completecb); + return self.complete_request(opcode, 0, completecb); } let single_req = req_list.len() == 1; let cnt = Arc::new(AtomicU32::new(req_list.len() as u32)); @@ -127,16 +127,10 @@ impl FileDriver { self.process_request(OpCode::Preadv, req_list, completecb) } - fn complete_request( - &mut self, - opcode: OpCode, - iovec: &[Iovec], - offset: usize, - nbytes: u64, - completecb: T, - ) -> Result<()> { - let aiocb = self.package_aiocb(opcode, iovec.to_vec(), offset, nbytes, completecb); - (self.aio.borrow_mut().complete_func)(&aiocb, nbytes as i64) + pub fn complete_request(&mut self, opcode: OpCode, res: i64, completecb: T) -> Result<()> { + let iovec: Vec = Vec::new(); + let aiocb = self.package_aiocb(opcode, iovec.to_vec(), 0, 0, completecb); + (self.aio.borrow_mut().complete_func)(&aiocb, res) } pub fn write_vectored(&mut self, req_list: Vec, completecb: T) -> Result<()> { diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 2dd1e4ac5..b3529fbb0 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1672,8 +1672,8 @@ impl BlockDriverOps for Qcow2Driver { let mut copied = 0; while copied < nbytes { let pos = offset as u64 + copied; - match self.host_offset_for_read(pos, nbytes - copied)? { - HostRange::DataAddress(host_offset, cnt) => { + match self.host_offset_for_read(pos, nbytes - copied) { + Ok(HostRange::DataAddress(host_offset, cnt)) => { let (begin, end) = iovecs_split(left, cnt); left = end; req_list.push(CombineRequest { @@ -1683,12 +1683,16 @@ impl BlockDriverOps for Qcow2Driver { }); copied += cnt; } - HostRange::DataNotInit(cnt) => { + Ok(HostRange::DataNotInit(cnt)) => { let (begin, end) = iovecs_split(left, cnt); left = end; iovec_write_zero(&begin); copied += cnt; } + Err(e) => { + error!("Failed to read vectored: {:?}", e); + return self.driver.complete_request(OpCode::Preadv, -1, completecb); + } } } @@ -1706,7 +1710,15 @@ impl BlockDriverOps for Qcow2Driver { while copied < nbytes { let pos = offset as u64 + copied; let count = self.cluster_aligned_bytes(pos, nbytes - copied); - let host_offset = self.host_offset_for_write(pos, count)?; + let host_offset = match self.host_offset_for_write(pos, count) { + Ok(host_offset) => host_offset, + Err(e) => { + error!("Failed to write vectored: {:?}", e); + return self + .driver + .complete_request(OpCode::Pwritev, -1, completecb); + } + }; if let Some(end) = req_list.last_mut() { if end.offset + end.nbytes == host_offset { end.nbytes += count; -- Gitee From 1472c5cf8f191e18fad8308a7b85133b95c59798 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 25 Apr 2024 06:23:38 +0800 Subject: [PATCH 1747/2187] docs: add description of parameter `enable_bar0` for virtio-gpu Add description of parameter `enable_bar0` for virtio-gpu. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 83a55a8aa..bf205fc18 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -1107,14 +1107,15 @@ Usually used in conjunction with VNC, the final images is rendered to the VNC cl Sample Configuration: ```shell --device virtio-gpu-pci,id=,bus=pcie.0,addr=0x2.0x0[,max_outputs=][,edid=true|false][,xres=][,yres= ][,max_hostmem=] +-device virtio-gpu-pci,id=,bus=pcie.0,addr=0x2.0x0[,max_outputs=][,edid=true|false][,xres=][,yres= ][,max_hostmem=][,enable_bar0=true|false] ``` -In addition to the required slot information, five optional properties are supported for virtio-gpu. +In addition to the required slot information, six optional properties are supported for virtio-gpu. * max_outputs: Number of screens supported by the current graphics card. The maximum value is 16. (can switch by using ctrl + alt + , for details, see vnc Client switchover) * edid: Edid feature, the virtual machine's kernel may checks this feature for HiDPi. You are advised to set to true. * xres/yres: The size of the login windows. * max_hostmem: The maximum memory that a graphics card can occupy on the host is expressed in byte. You are advised to set not less than 256MiB, otherwise the final supported resolutions is affected. +* enable_bar0: Enable a 64M bar0 in virtio-gpu. Note: 1. Only virtio-gpu 2D supported. -- Gitee From 3c88d3568a482e49a0e4ecdf505e337ac0697010 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 16 Apr 2024 19:25:19 +0800 Subject: [PATCH 1748/2187] CPU: add vcpu trace add kvm hypervisor vcpu trace Signed-off-by: Mingwang Li --- Cargo.lock | 2 + cpu/src/aarch64/caps.rs | 2 +- hypervisor/Cargo.toml | 1 + hypervisor/src/kvm/aarch64/mod.rs | 13 +- hypervisor/src/kvm/interrupt.rs | 1 + hypervisor/src/kvm/listener.rs | 28 ++++ hypervisor/src/kvm/mod.rs | 135 ++++++++++--------- hypervisor/src/kvm/x86_64/mod.rs | 47 +++++++ trace/Cargo.toml | 1 + trace/src/lib.rs | 2 + trace/trace_info/kvm.toml | 209 ++++++++++++++++++++++++++++++ 11 files changed, 376 insertions(+), 65 deletions(-) create mode 100644 trace/trace_info/kvm.toml diff --git a/Cargo.lock b/Cargo.lock index aa9c10067..fc8729308 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -765,6 +765,7 @@ dependencies = [ "migration", "migration_derive", "thiserror", + "trace", "util", "vmm-sys-util", ] @@ -1729,6 +1730,7 @@ dependencies = [ "log", "regex", "trace_generator", + "vmm-sys-util", ] [[package]] diff --git a/cpu/src/aarch64/caps.rs b/cpu/src/aarch64/caps.rs index bd1bea4a2..5b2a30553 100644 --- a/cpu/src/aarch64/caps.rs +++ b/cpu/src/aarch64/caps.rs @@ -34,7 +34,7 @@ impl From<&CpuConfig> for ArmCPUFeatures { } /// Entry to cpreg list. -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] pub struct CpregListEntry { pub reg_id: u64, pub value: u128, diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 1b5aa5d25..d87911990 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -20,3 +20,4 @@ machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } +trace = { path = "../trace" } diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index caac3999f..072123600 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -190,10 +190,13 @@ impl KvmCpu { fn get_one_reg(&self, reg_id: u64) -> Result { let mut val = [0_u8; 16]; self.fd.get_one_reg(reg_id, &mut val)?; - Ok(u128::from_le_bytes(val)) + let data = u128::from_le_bytes(val); + trace::kvm_get_one_reg(self.id, reg_id, data); + Ok(data) } fn set_one_reg(&self, reg_id: u64, val: u128) -> Result<()> { + trace::kvm_set_one_reg(self.id, reg_id, val); self.fd.set_one_reg(reg_id, &val.to_le_bytes())?; Ok(()) } @@ -219,12 +222,14 @@ impl KvmCpu { if mp_state.mp_state != KVM_MP_STATE_STOPPED { mp_state.mp_state = KVM_MP_STATE_RUNNABLE; } + trace::kvm_get_mp_state(self.id, &mp_state); locked_arch_cpu.mp_state = mp_state; } } RegsIndex::VcpuEvents => { if self.caps.vcpu_events { locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; + trace::kvm_get_vcpu_events(self.id, &locked_arch_cpu.cpu_events); } } RegsIndex::CpregList => { @@ -244,6 +249,10 @@ impl KvmCpu { locked_arch_cpu.cpreg_list[index] = cpreg_entry; locked_arch_cpu.cpreg_len += 1; } + trace::kvm_get_reg_list( + self.id, + &&locked_arch_cpu.cpreg_list[0..locked_arch_cpu.cpreg_len], + ); } RegsIndex::VtimerCount => { locked_arch_cpu.vtimer_cnt = self @@ -270,6 +279,7 @@ impl KvmCpu { } RegsIndex::MpState => { if self.caps.mp_state { + trace::kvm_set_mp_state(self.id, &locked_arch_cpu.mp_state); self.fd .set_mp_state(locked_arch_cpu.mp_state) .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; @@ -277,6 +287,7 @@ impl KvmCpu { } RegsIndex::VcpuEvents => { if self.caps.vcpu_events { + trace::kvm_set_vcpu_events(self.id, &locked_arch_cpu.cpu_events); self.fd .set_vcpu_events(&locked_arch_cpu.cpu_events) .with_context(|| format!("Failed to set vcpu event for CPU {}", apic_id))?; diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 6b9d9ff65..ea9e7790c 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -205,6 +205,7 @@ impl IrqRouteTable { std::cmp::max(align_of::(), align_of::()), )?; + trace::kvm_commit_irq_routing(); // SAFETY: data in `routes` is reliable. unsafe { let irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index dd137e875..713b72384 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -306,6 +306,13 @@ impl KvmMemoryListener { fn add_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { let vm_fd = self.vm_fd.as_ref().unwrap(); let io_addr = IoEventAddress::Mmio(ioevtfd.addr_range.base.raw_value()); + trace::kvm_add_ioeventfd( + &ioevtfd.fd, + &io_addr, + ioevtfd.data_match, + ioevtfd.addr_range.size, + ioevtfd.data, + ); let ioctl_ret = if ioevtfd.data_match { let length = ioevtfd.addr_range.size; match length { @@ -340,6 +347,13 @@ impl KvmMemoryListener { fn delete_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { let vm_fd = self.vm_fd.as_ref().unwrap(); let io_addr = IoEventAddress::Mmio(ioevtfd.addr_range.base.raw_value()); + trace::kvm_delete_ioeventfd( + &ioevtfd.fd, + &io_addr, + ioevtfd.data_match, + ioevtfd.addr_range.size, + ioevtfd.data, + ); let ioctl_ret = if ioevtfd.data_match { let length = ioevtfd.addr_range.size; match length { @@ -452,6 +466,13 @@ impl KvmIoListener { fn add_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { let vm_fd = self.vm_fd.as_ref().unwrap(); let io_addr = IoEventAddress::Pio(ioevtfd.addr_range.base.raw_value()); + trace::kvm_add_ioeventfd( + &ioevtfd.fd, + &io_addr, + ioevtfd.data_match, + ioevtfd.addr_range.size, + ioevtfd.data, + ); let ioctl_ret = if ioevtfd.data_match { let length = ioevtfd.addr_range.size; match length { @@ -486,6 +507,13 @@ impl KvmIoListener { fn delete_ioeventfd(&self, ioevtfd: &RegionIoEventFd) -> Result<()> { let vm_fd = self.vm_fd.as_ref().unwrap(); let io_addr = IoEventAddress::Pio(ioevtfd.addr_range.base.raw_value()); + trace::kvm_delete_ioeventfd( + &ioevtfd.fd, + &io_addr, + ioevtfd.data_match, + ioevtfd.addr_range.size, + ioevtfd.data, + ); let ioctl_ret = if ioevtfd.data_match { let length = ioevtfd.addr_range.size; match length { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 462525fb2..671eb548a 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -417,82 +417,85 @@ impl KvmCpu { .with_context(|| CpuError::NoMachineInterface)?; match self.fd.run() { - Ok(run) => match run { - #[cfg(target_arch = "x86_64")] - VcpuExit::IoIn(addr, data) => { - vm.lock().unwrap().pio_in(u64::from(addr), data); - } - #[cfg(target_arch = "x86_64")] - VcpuExit::IoOut(addr, data) => { - #[cfg(feature = "boot_time")] - capture_boot_signal(addr as u64, data); + Ok(run) => { + trace::kvm_vcpu_run_exit(cpu.id, &run); + match run { + #[cfg(target_arch = "x86_64")] + VcpuExit::IoIn(addr, data) => { + vm.lock().unwrap().pio_in(u64::from(addr), data); + } + #[cfg(target_arch = "x86_64")] + VcpuExit::IoOut(addr, data) => { + #[cfg(feature = "boot_time")] + capture_boot_signal(addr as u64, data); - vm.lock().unwrap().pio_out(u64::from(addr), data); - } - VcpuExit::MmioRead(addr, data) => { - vm.lock().unwrap().mmio_read(addr, data); - } - VcpuExit::MmioWrite(addr, data) => { - #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] - capture_boot_signal(addr, data); + vm.lock().unwrap().pio_out(u64::from(addr), data); + } + VcpuExit::MmioRead(addr, data) => { + vm.lock().unwrap().mmio_read(addr, data); + } + VcpuExit::MmioWrite(addr, data) => { + #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] + capture_boot_signal(addr, data); - vm.lock().unwrap().mmio_write(addr, data); - } - #[cfg(target_arch = "x86_64")] - VcpuExit::Hlt => { - info!("Vcpu{} received KVM_EXIT_HLT signal", cpu.id); - return Err(anyhow!(CpuError::VcpuHltEvent(cpu.id))); - } - #[cfg(target_arch = "x86_64")] - VcpuExit::Shutdown => { - info!("Vcpu{} received an KVM_EXIT_SHUTDOWN signal", cpu.id); - cpu.guest_shutdown()?; + vm.lock().unwrap().mmio_write(addr, data); + } + #[cfg(target_arch = "x86_64")] + VcpuExit::Hlt => { + info!("Vcpu{} received KVM_EXIT_HLT signal", cpu.id); + return Err(anyhow!(CpuError::VcpuHltEvent(cpu.id))); + } + #[cfg(target_arch = "x86_64")] + VcpuExit::Shutdown => { + info!("Vcpu{} received an KVM_EXIT_SHUTDOWN signal", cpu.id); + cpu.guest_shutdown()?; - return Ok(false); - } - #[cfg(target_arch = "aarch64")] - VcpuExit::SystemEvent(event, flags) => { - if event == kvm_bindings::KVM_SYSTEM_EVENT_SHUTDOWN { - info!( - "Vcpu{} received an KVM_SYSTEM_EVENT_SHUTDOWN signal", - cpu.id() - ); - cpu.guest_shutdown() - .with_context(|| "Some error occurred in guest shutdown")?; - return Ok(true); - } else if event == kvm_bindings::KVM_SYSTEM_EVENT_RESET { - info!("Vcpu{} received an KVM_SYSTEM_EVENT_RESET signal", cpu.id()); - cpu.guest_reset() - .with_context(|| "Some error occurred in guest reset")?; - return Ok(true); - } else { - error!( + return Ok(false); + } + #[cfg(target_arch = "aarch64")] + VcpuExit::SystemEvent(event, flags) => { + if event == kvm_bindings::KVM_SYSTEM_EVENT_SHUTDOWN { + info!( + "Vcpu{} received an KVM_SYSTEM_EVENT_SHUTDOWN signal", + cpu.id() + ); + cpu.guest_shutdown() + .with_context(|| "Some error occurred in guest shutdown")?; + return Ok(true); + } else if event == kvm_bindings::KVM_SYSTEM_EVENT_RESET { + info!("Vcpu{} received an KVM_SYSTEM_EVENT_RESET signal", cpu.id()); + cpu.guest_reset() + .with_context(|| "Some error occurred in guest reset")?; + return Ok(true); + } else { + error!( "Vcpu{} received unexpected system event with type 0x{:x}, flags 0x{:x}", cpu.id(), event, flags ); + } + return Ok(false); } - return Ok(false); - } - VcpuExit::FailEntry(reason, cpuid) => { - info!( + VcpuExit::FailEntry(reason, cpuid) => { + info!( "Vcpu{} received KVM_EXIT_FAIL_ENTRY signal. the vcpu could not be run due to unknown reasons({})", cpuid, reason ); - return Ok(false); - } - VcpuExit::InternalError => { - info!("Vcpu{} received KVM_EXIT_INTERNAL_ERROR signal", cpu.id()); - return Ok(false); - } - r => { - return Err(anyhow!(CpuError::VcpuExitReason( - cpu.id(), - format!("{:?}", r) - ))); + return Ok(false); + } + VcpuExit::InternalError => { + info!("Vcpu{} received KVM_EXIT_INTERNAL_ERROR signal", cpu.id()); + return Ok(false); + } + r => { + return Err(anyhow!(CpuError::VcpuExitReason( + cpu.id(), + format!("{:?}", r) + ))); + } } - }, + } Err(ref e) => { match e.errno() { libc::EAGAIN => {} @@ -569,6 +572,7 @@ impl CPUHypervisorOps for KvmCpu { } fn reset_vcpu(&self, cpu: Arc) -> Result<()> { + trace::kvm_reset_vcpu(self.id); self.arch_reset_vcpu(cpu)?; Ok(()) @@ -846,6 +850,7 @@ impl MsiIrqManager for KVMInterruptManager { fn release_irq(&self, irq: u32) -> Result<()> { let mut locked_irq_route_table = self.irq_route_table.lock().unwrap(); + trace::kvm_release_irq(irq); locked_irq_route_table.release_gsi(irq).map_err(|e| { error!("Failed to release gsi, error is {:?}", e); e @@ -853,6 +858,7 @@ impl MsiIrqManager for KVMInterruptManager { } fn register_irqfd(&self, irq_fd: Arc, irq: u32) -> Result<()> { + trace::kvm_register_irqfd(&irq_fd, irq); self.vm_fd.register_irqfd(&irq_fd, irq).map_err(|e| { error!("Failed to register irq, error is {:?}", e); e @@ -862,6 +868,7 @@ impl MsiIrqManager for KVMInterruptManager { } fn unregister_irqfd(&self, irq_fd: Arc, irq: u32) -> Result<()> { + trace::kvm_unregister_irqfd(&irq_fd, irq); self.vm_fd.unregister_irqfd(&irq_fd, irq).map_err(|e| { error!("Failed to unregister irq, error is {:?}", e); e @@ -872,6 +879,7 @@ impl MsiIrqManager for KVMInterruptManager { fn trigger(&self, irq_fd: Option>, vector: MsiVector, dev_id: u32) -> Result<()> { if irq_fd.is_some() { + trace::kvm_trigger_irqfd(irq_fd.as_ref().unwrap()); irq_fd.unwrap().write(1)?; } else { #[cfg(target_arch = "aarch64")] @@ -888,6 +896,7 @@ impl MsiIrqManager for KVMInterruptManager { pad: [0; 12], }; + trace::kvm_signal_msi(&kvm_msi); self.vm_fd.signal_msi(kvm_msi)?; } diff --git a/hypervisor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs index f4c2bc198..7d7e7b581 100644 --- a/hypervisor/src/kvm/x86_64/mod.rs +++ b/hypervisor/src/kvm/x86_64/mod.rs @@ -119,42 +119,55 @@ impl KvmCpu { match regs_index { RegsIndex::Regs => { locked_arch_cpu.regs = self.fd.get_regs()?; + trace::kvm_get_regs(self.id, &locked_arch_cpu.regs); } RegsIndex::Sregs => { locked_arch_cpu.sregs = self.fd.get_sregs()?; + trace::kvm_get_sregs(self.id, &locked_arch_cpu.sregs); } RegsIndex::Fpu => { if !self.caps.has_xsave { locked_arch_cpu.fpu = self.fd.get_fpu()?; + trace::kvm_get_fpu(self.id, &locked_arch_cpu.fpu); } } RegsIndex::MpState => { locked_arch_cpu.mp_state = self.fd.get_mp_state()?; + trace::kvm_get_mp_state(self.id, &locked_arch_cpu.mp_state); } RegsIndex::LapicState => { locked_arch_cpu.lapic = self.fd.get_lapic()?; + trace::kvm_get_lapic(self.id, &locked_arch_cpu.lapic); } RegsIndex::MsrEntry => { locked_arch_cpu.msr_len = self.fd.get_msrs(&mut msr_entries)?; for (i, entry) in msr_entries.as_slice().iter().enumerate() { locked_arch_cpu.msr_list[i] = *entry; } + trace::kvm_get_msrs( + self.id, + &&locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], + ); } RegsIndex::VcpuEvents => { locked_arch_cpu.cpu_events = self.fd.get_vcpu_events()?; + trace::kvm_get_vcpu_events(self.id, &locked_arch_cpu.cpu_events); } RegsIndex::Xsave => { if self.caps.has_xsave { locked_arch_cpu.xsave = self.fd.get_xsave()?; + trace::kvm_get_xsave(self.id, &locked_arch_cpu.xsave); } } RegsIndex::Xcrs => { if self.caps.has_xcrs { locked_arch_cpu.xcrs = self.fd.get_xcrs()?; + trace::kvm_get_xcrs(self.id, &locked_arch_cpu.xcrs); } } RegsIndex::DebugRegs => { locked_arch_cpu.debugregs = self.fd.get_debug_regs()?; + trace::kvm_get_debug_regs(self.id, &locked_arch_cpu.debugregs); } } @@ -170,31 +183,40 @@ impl KvmCpu { let apic_id = locked_arch_cpu.apic_id; match regs_index { RegsIndex::Regs => { + trace::kvm_set_regs(self.id, &locked_arch_cpu.regs); self.fd .set_regs(&locked_arch_cpu.regs) .with_context(|| format!("Failed to set regs for CPU {}", apic_id))?; } RegsIndex::Sregs => { + trace::kvm_set_sregs(self.id, &locked_arch_cpu.sregs); self.fd .set_sregs(&locked_arch_cpu.sregs) .with_context(|| format!("Failed to set sregs for CPU {}", apic_id))?; } RegsIndex::Fpu => { + trace::kvm_set_fpu(self.id, &locked_arch_cpu.fpu); self.fd .set_fpu(&locked_arch_cpu.fpu) .with_context(|| format!("Failed to set fpu for CPU {}", apic_id))?; } RegsIndex::MpState => { + trace::kvm_set_mp_state(self.id, &locked_arch_cpu.mp_state); self.fd .set_mp_state(locked_arch_cpu.mp_state) .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; } RegsIndex::LapicState => { + trace::kvm_set_lapic(self.id, &locked_arch_cpu.lapic); self.fd .set_lapic(&locked_arch_cpu.lapic) .with_context(|| format!("Failed to set lapic for CPU {}", apic_id))?; } RegsIndex::MsrEntry => { + trace::kvm_set_msrs( + self.id, + &&locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], + ); self.fd .set_msrs(&Msrs::from_entries( &locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], @@ -202,21 +224,25 @@ impl KvmCpu { .with_context(|| format!("Failed to set msrs for CPU {}", apic_id))?; } RegsIndex::VcpuEvents => { + trace::kvm_set_vcpu_events(self.id, &locked_arch_cpu.cpu_events); self.fd .set_vcpu_events(&locked_arch_cpu.cpu_events) .with_context(|| format!("Failed to set vcpu events for CPU {}", apic_id))?; } RegsIndex::Xsave => { + trace::kvm_set_xsave(self.id, &locked_arch_cpu.xsave); self.fd .set_xsave(&locked_arch_cpu.xsave) .with_context(|| format!("Failed to set xsave for CPU {}", apic_id))?; } RegsIndex::Xcrs => { + trace::kvm_set_xcrs(self.id, &locked_arch_cpu.xcrs); self.fd .set_xcrs(&locked_arch_cpu.xcrs) .with_context(|| format!("Failed to set xcrs for CPU {}", apic_id))?; } RegsIndex::DebugRegs => { + trace::kvm_set_debug_regs(self.id, &locked_arch_cpu.debugregs); self.fd .set_debug_regs(&locked_arch_cpu.debugregs) .with_context(|| format!("Failed to set debug register for CPU {}", apic_id))?; @@ -241,48 +267,69 @@ impl KvmCpu { locked_arch_cpu .setup_cpuid(&mut cpuid) .with_context(|| format!("Failed to set cpuid for CPU {}", apic_id))?; + trace::kvm_setup_cpuid(self.id, &cpuid); self.fd .set_cpuid2(&cpuid) .with_context(|| format!("Failed to set cpuid for CPU {}/KVM", apic_id))?; + trace::kvm_set_cpuid2(self.id, &cpuid); self.fd .set_mp_state(locked_arch_cpu.mp_state) .with_context(|| format!("Failed to set mpstate for CPU {}", apic_id))?; + trace::kvm_set_mp_state(self.id, &locked_arch_cpu.mp_state); + self.fd .set_sregs(&locked_arch_cpu.sregs) .with_context(|| format!("Failed to set sregs for CPU {}", apic_id))?; + trace::kvm_set_sregs(self.id, &locked_arch_cpu.sregs); + self.fd .set_regs(&locked_arch_cpu.regs) .with_context(|| format!("Failed to set regs for CPU {}", apic_id))?; + trace::kvm_set_regs(self.id, &locked_arch_cpu.regs); + if self.caps.has_xsave { self.fd .set_xsave(&locked_arch_cpu.xsave) .with_context(|| format!("Failed to set xsave for CPU {}", apic_id))?; + trace::kvm_set_xsave(self.id, &locked_arch_cpu.xsave); } else { self.fd .set_fpu(&locked_arch_cpu.fpu) .with_context(|| format!("Failed to set fpu for CPU {}", apic_id))?; + trace::kvm_set_fpu(self.id, &locked_arch_cpu.fpu); } if self.caps.has_xcrs { self.fd .set_xcrs(&locked_arch_cpu.xcrs) .with_context(|| format!("Failed to set xcrs for CPU {}", apic_id))?; + trace::kvm_set_xcrs(self.id, &locked_arch_cpu.xcrs); } self.fd .set_debug_regs(&locked_arch_cpu.debugregs) .with_context(|| format!("Failed to set debug register for CPU {}", apic_id))?; + trace::kvm_set_debug_regs(self.id, &locked_arch_cpu.debugregs); + self.fd .set_lapic(&locked_arch_cpu.lapic) .with_context(|| format!("Failed to set lapic for CPU {}", apic_id))?; + trace::kvm_set_lapic(self.id, &locked_arch_cpu.lapic); + self.fd .set_msrs(&Msrs::from_entries( &locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], )?) .with_context(|| format!("Failed to set msrs for CPU {}", apic_id))?; + trace::kvm_set_msrs( + self.id, + &&locked_arch_cpu.msr_list[0..locked_arch_cpu.msr_len], + ); + self.fd .set_vcpu_events(&locked_arch_cpu.cpu_events) .with_context(|| format!("Failed to set vcpu events for CPU {}", apic_id))?; + trace::kvm_set_vcpu_events(self.id, &locked_arch_cpu.cpu_events); Ok(()) } diff --git a/trace/Cargo.toml b/trace/Cargo.toml index 2355cdb0d..7aa0d9f6e 100644 --- a/trace/Cargo.toml +++ b/trace/Cargo.toml @@ -12,6 +12,7 @@ lazy_static = "1.4.0" regex = "1" anyhow = "1.0" trace_generator = { path = "trace_generator" } +vmm-sys-util = "0.11.1" [features] trace_to_logger = [] diff --git a/trace/src/lib.rs b/trace/src/lib.rs index d6228f293..1501a5028 100644 --- a/trace/src/lib.rs +++ b/trace/src/lib.rs @@ -25,11 +25,13 @@ use std::{ fmt, os::unix::io::RawFd, sync::atomic::{AtomicBool, Ordering}, + sync::Arc, }; use anyhow::{Ok, Result}; use lazy_static::lazy_static; use regex::Regex; +use vmm_sys_util::eventfd::EventFd; use trace_generator::{ add_trace_state_to, gen_trace_event_func, gen_trace_scope_func, gen_trace_state, diff --git a/trace/trace_info/kvm.toml b/trace/trace_info/kvm.toml new file mode 100644 index 000000000..ce6060bc7 --- /dev/null +++ b/trace/trace_info/kvm.toml @@ -0,0 +1,209 @@ +[[events]] +name = "kvm_vcpu_run_exit" +args = "index: u8, reason: &dyn fmt::Debug" +message = "vcpu index {} exit reason {:?}" +enabled = true + +[[events]] +name = "kvm_add_ioeventfd" +args = "fd: &Arc, addr: &dyn fmt::Debug, data_match: bool, size: u64, data: u64" +message = "fd {:?} addr {:?} data_match {} size {} data {}" +enabled = true + +[[events]] +name = "kvm_delete_ioeventfd" +args = "fd: &Arc, addr: &dyn fmt::Debug, data_match: bool, size: u64, data: u64" +message = "fd {:?} addr {:?} data_match {} size {} data {}" +enabled = true + +[[events]] +name = "kvm_commit_irq_routing" +args = "" +message = "" +enabled = true + +[[events]] +name = "kvm_release_irq" +args = "irq: u32" +message = "irq {}" +enabled = true + +[[events]] +name = "kvm_register_irqfd" +args = "fd: &EventFd, gsi: u32" +message = "fd {:?} gsi {}" +enabled = true + +[[events]] +name = "kvm_unregister_irqfd" +args = "fd: &EventFd, gsi: u32" +message = "fd {:?} gsi {}" +enabled = true + +[[events]] +name = "kvm_trigger_irqfd" +args = "irq_fd: &EventFd" +message = "irq_fd {:?}" +enabled = true + +[[events]] +name = "kvm_signal_msi" +args = "msi: &dyn fmt::Debug" +message = "kvm_msi {:?}" +enabled = true + +[[events]] +name = "kvm_reset_vcpu" +args = "id: u8" +message = "vcpu id {}" +enabled = true + +[[events]] +name = "kvm_get_one_reg" +args = "id: u8, reg_id: u64, val: u128" +message = "vcpu id {} reg_id: {} val {}" +enabled = true + +[[events]] +name = "kvm_set_one_reg" +args = "id: u8, reg_id: u64, val: u128" +message = "vcpu id {} reg_id: {} value {}" +enabled = true + +[[events]] +name = "kvm_get_mp_state" +args = "id: u8, mp_state: &dyn fmt::Debug" +message = "vcpu id {} mp_state {:?}" +enabled = true + +[[events]] +name = "kvm_get_vcpu_events" +args = "id: u8, cpu_events: &dyn fmt::Debug" +message = "vcpu id {} cpu_events {:?}" +enabled = true + +[[events]] +name = "kvm_get_reg_list" +args = "id: u8, cpreg_list: &dyn fmt::Debug" +message = "vcpu id {} cpreg_list {:?}" +enabled = true + +[[events]] +name = "kvm_set_mp_state" +args = "id: u8, mp_state: &dyn fmt::Debug" +message = "vcpu id {} mp_state {:?}" +enabled = true + +[[events]] +name = "kvm_set_vcpu_events" +args = "id: u8, event: &dyn fmt::Debug" +message = "vcpu id {} event {:?}" +enabled = true + +[[events]] +name = "kvm_get_regs" +args = "id: u8, regs: &dyn fmt::Debug" +message = "vcpu id {} kvm_regs {:?}" +enabled = true + +[[events]] +name = "kvm_get_sregs" +args = "id: u8, sregs: &dyn fmt::Debug" +message = "vcpu id {} sregs {:?}" +enabled = true + +[[events]] +name = "kvm_get_fpu" +args = "id: u8, fpu: &dyn fmt::Debug" +message = "vcpu id {} fpu {:?}" +enabled = true + +[[events]] +name = "kvm_get_lapic" +args = "id: u8, lapic: &dyn fmt::Debug" +message = "vcpu id {} lapic {:?}" +enabled = true + +[[events]] +name = "kvm_get_msrs" +args = "id: u8, msrs: &dyn fmt::Debug" +message = "vcpu id {} msrs {:?}" +enabled = true + +[[events]] +name = "kvm_get_xsave" +args = "id: u8, xsave: &dyn fmt::Debug" +message = "vcpu id {} xsave {:?}" +enabled = true + +[[events]] +name = "kvm_get_xcrs" +args = "id: u8, xcrs: &dyn fmt::Debug" +message = "vcpu id {} xcrs {:?}" +enabled = true + +[[events]] +name = "kvm_get_debug_regs" +args = "id: u8, debugregs: &dyn fmt::Debug" +message = "vcpu id {} debugregs: {:?}" +enabled = true + +[[events]] +name = "kvm_set_regs" +args = "id: u8, reg: &dyn fmt::Debug" +message = "vcpu id {} regs {:?}" +enabled = true + +[[events]] +name = "kvm_set_sregs" +args = "id: u8, sregs: &dyn fmt::Debug" +message = "vcpu id {} sregs {:?}" +enabled = true + +[[events]] +name = "kvm_set_fpu" +args = "id: u8, fpu: &dyn fmt::Debug" +message = "vcpu id {} fpu {:?}" +enabled = true + +[[events]] +name = "kvm_set_lapic" +args = "id: u8, lapic: &dyn fmt::Debug" +message = "vcpu id {} lapic {:?}" +enabled = true + +[[events]] +name = "kvm_set_msrs" +args = "id: u8, msrs: &dyn fmt::Debug" +message = "vcpu id {} msrs: {:?}" +enabled = true + +[[events]] +name = "kvm_set_xsave" +args = "id: u8, xsave: &dyn fmt::Debug" +message = "vcpu id {} xsave {:?}" +enabled = true + +[[events]] +name = "kvm_set_xcrs" +args = "id: u8, xcrs: &dyn fmt::Debug" +message = "vcpu id {} xcrs {:?}" +enabled = true + +[[events]] +name = "kvm_set_debug_regs" +args = "id: u8, debugregs: &dyn fmt::Debug" +message = "vcpu id {} debugregs {:?}" +enabled = true + +[[events]] +name = "kvm_setup_cpuid" +args = "id: u8, cpuid: &dyn fmt::Debug" +message = "vcpu id {} cpuid {:?}" +enabled = true + +[[events]] +name = "kvm_set_cpuid2" +args = "id: u8, cpuid2: &dyn fmt::Debug" +message = "vcpu id {} cpuid2 {:?}" +enabled = true -- Gitee From 67c9893a2e30101f5157045409488a6cba1533f4 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Apr 2024 04:58:41 +0800 Subject: [PATCH 1749/2187] Virtio: initialize device name in VirtioMmioDevice Signed-off-by: Xiao Ye --- machine/src/lib.rs | 14 ++++--- machine/src/micro_common/mod.rs | 10 +++-- virtio/src/transport/virtio_mmio.rs | 58 ++++++++++++++++++++++++----- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 63fd72057..ab0957c9d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -577,7 +577,7 @@ pub trait MachineOps { ("addr", device_cfg.addr), ("multifunction", device_cfg.multifunction) ); - let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); + let device = VirtioMmioDevice::new(&sys_mem, device_cfg.id.clone(), vsock.clone()); MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) @@ -679,7 +679,7 @@ pub trait MachineOps { ("addr", config.addr), ("multifunction", config.multifunction) ); - let device = VirtioMmioDevice::new(sys_mem, balloon); + let device = VirtioMmioDevice::new(sys_mem, config.id.clone(), balloon); self.realize_virtio_mmio_device(device)?; } _ => { @@ -717,7 +717,7 @@ pub trait MachineOps { ("addr", serial_cfg.addr), ("multifunction", serial_cfg.multifunction) ); - let device = VirtioMmioDevice::new(&sys_mem, serial.clone()); + let device = VirtioMmioDevice::new(&sys_mem, serial_cfg.id.clone(), serial.clone()); MigrationManager::register_device_instance( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) @@ -853,7 +853,7 @@ pub trait MachineOps { ("addr", rng_cfg.addr), ("multifunction", rng_cfg.multifunction) ); - let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); + let device = VirtioMmioDevice::new(sys_mem, rng_cfg.id.clone(), rng_dev.clone()); self.realize_virtio_mmio_device(device) .with_context(|| "Failed to add virtio mmio rng device")?; } @@ -905,7 +905,8 @@ pub trait MachineOps { ("addr", dev_cfg.addr), ("multifunction", dev_cfg.multifunction) ); - let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); + let virtio_mmio_device = + VirtioMmioDevice::new(&sys_mem, dev_cfg.id.clone(), device); self.realize_virtio_mmio_device(virtio_mmio_device) .with_context(|| "Failed to add vhost user fs device")?; } @@ -1371,7 +1372,8 @@ pub trait MachineOps { chardev_cfg, self.get_sys_mem(), ))); - let virtio_mmio_device = VirtioMmioDevice::new(self.get_sys_mem(), device); + let virtio_mmio_device = + VirtioMmioDevice::new(self.get_sys_mem(), device_cfg.id.clone(), device); self.realize_virtio_mmio_device(virtio_mmio_device) .with_context(|| "Failed to add vhost user block device")?; Ok(()) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index b4051b46e..c8b96d597 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -164,7 +164,8 @@ impl LightMachine { DriveConfig::default(), self.get_drive_files(), ))); - let virtio_mmio = VirtioMmioDevice::new(&self.base.sys_mem, block.clone()); + let virtio_mmio = + VirtioMmioDevice::new(&self.base.sys_mem, id.to_string(), block.clone()); rpl_devs.push(virtio_mmio); MigrationManager::register_device_instance( @@ -178,7 +179,8 @@ impl LightMachine { NetworkInterfaceConfig::default(), NetDevcfg::default(), ))); - let virtio_mmio = VirtioMmioDevice::new(&self.base.sys_mem, net.clone()); + let virtio_mmio = + VirtioMmioDevice::new(&self.base.sys_mem, id.to_string(), net.clone()); rpl_devs.push(virtio_mmio); MigrationManager::register_device_instance( @@ -415,7 +417,7 @@ impl LightMachine { netdev_cfg, &self.base.sys_mem, ))); - VirtioMmioDevice::new(&self.base.sys_mem, net) + VirtioMmioDevice::new(&self.base.sys_mem, net_cfg.id.clone(), net) } else { let chardev = netdev_cfg.chardev.clone().with_context(|| { format!("Chardev not configured for netdev {:?}", netdev_cfg.id) @@ -431,7 +433,7 @@ impl LightMachine { sock_path, &self.base.sys_mem, ))); - VirtioMmioDevice::new(&self.base.sys_mem, net) + VirtioMmioDevice::new(&self.base.sys_mem, net_cfg.id.clone(), net) }; self.realize_virtio_mmio_device(device)?; } else { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 55381c5ea..1340fd9cf 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -134,12 +134,20 @@ pub struct VirtioMmioDevice { } impl VirtioMmioDevice { - pub fn new(mem_space: &Arc, device: Arc>) -> Self { + pub fn new( + mem_space: &Arc, + name: String, + device: Arc>, + ) -> Self { let device_clone = device.clone(); let queue_num = device_clone.lock().unwrap().queue_num(); VirtioMmioDevice { base: SysBusDevBase { + base: DeviceBase { + id: name, + hotpluggable: false, + }, dev_type: SysBusDevType::VirtioMmio, interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), ..Default::default() @@ -696,7 +704,11 @@ mod tests { fn test_virtio_mmio_device_new() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let locked_device = virtio_device.lock().unwrap(); assert_eq!(locked_device.device_activated(), false); @@ -716,7 +728,11 @@ mod tests { fn test_virtio_mmio_device_read_01() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); // read the register of magic value @@ -775,7 +791,11 @@ mod tests { fn test_virtio_mmio_device_read_02() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); // read the register representing max size of the queue @@ -867,7 +887,11 @@ mod tests { fn test_virtio_mmio_device_read_03() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); // read the configuration atomic value @@ -915,7 +939,11 @@ mod tests { fn test_virtio_mmio_device_write_01() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); // write the selector for device features @@ -1029,7 +1057,11 @@ mod tests { fn test_virtio_mmio_device_write_02() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); // write the ready status of queue @@ -1096,7 +1128,11 @@ mod tests { fn test_virtio_mmio_device_write_03() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); // write the low 32bit of queue's descriptor table address @@ -1221,7 +1257,11 @@ mod tests { fn test_virtio_mmio_device_write_04() { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new(&sys_space, virtio_device.clone()); + let mut virtio_mmio_device = VirtioMmioDevice::new( + &sys_space, + "test_virtio_mmio_device".to_string(), + virtio_device.clone(), + ); let addr = GuestAddress(0); virtio_mmio_device.assign_interrupt_cb(); -- Gitee From 5a671aef7190a28db4f3187108ca41f3d0e2dfbb Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 15 Apr 2024 20:49:33 +0800 Subject: [PATCH 1750/2187] Hypervisor: Support test hypervisor type Previously, MST tests were limited to running on specific operating systems due to dependencies on kvm module. However, with the introduction of the test hypervisor type, MST can now overcome OS-specific limitations. Signed-off-by: Jinhao Gao --- .../src/interrupt_controller/aarch64/gicv3.rs | 77 +++- hypervisor/src/lib.rs | 1 + hypervisor/src/test/aarch64/mod.rs | 23 + hypervisor/src/test/listener.rs | 40 ++ hypervisor/src/test/mod.rs | 413 ++++++++++++++++++ util/src/test_helper.rs | 20 +- 6 files changed, 541 insertions(+), 33 deletions(-) create mode 100644 hypervisor/src/test/aarch64/mod.rs create mode 100644 hypervisor/src/test/listener.rs create mode 100644 hypervisor/src/test/mod.rs diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index b9cb520c4..60babf03d 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -41,35 +41,58 @@ pub struct GICv3Config { pub trait GICv3Access: Send + Sync { fn init_gic( &self, - nr_irqs: u32, - redist_regions: Vec, - dist_base: u64, - ) -> Result<()>; + _nr_irqs: u32, + _redist_regions: Vec, + _dist_base: u64, + ) -> Result<()> { + Ok(()) + } - /// Returns `gicr_attr` of `vCPU`. - fn vcpu_gicr_attr(&self, cpu: usize) -> u64; + fn vcpu_gicr_attr(&self, _cpu: usize) -> u64 { + 0 + } - fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()>; + fn access_gic_distributor( + &self, + _offset: u64, + _gicd_value: &mut u32, + _write: bool, + ) -> Result<()> { + Ok(()) + } fn access_gic_redistributor( &self, - offset: u64, - cpu: usize, - gicr_value: &mut u32, - write: bool, - ) -> Result<()>; + _offset: u64, + _cpu: usize, + _gicr_value: &mut u32, + _write: bool, + ) -> Result<()> { + Ok(()) + } fn access_gic_cpu( &self, - offset: u64, - cpu: usize, - gicc_value: &mut u64, - write: bool, - ) -> Result<()>; + _offset: u64, + _cpu: usize, + _gicc_value: &mut u64, + _write: bool, + ) -> Result<()> { + Ok(()) + } - fn access_gic_line_level(&self, offset: u64, gicll_value: &mut u32, write: bool) -> Result<()>; + fn access_gic_line_level( + &self, + _offset: u64, + _gicll_value: &mut u32, + _write: bool, + ) -> Result<()> { + Ok(()) + } - fn pause(&self) -> Result<()>; + fn pause(&self) -> Result<()> { + Ok(()) + } } #[derive(Clone, Copy)] @@ -328,13 +351,21 @@ impl GICDevice for GICv3 { } pub trait GICv3ItsAccess: Send + Sync { - fn init_gic_its(&self, msi_base: u64) -> Result<()>; + fn init_gic_its(&self, _msi_base: u64) -> Result<()> { + Ok(()) + } - fn access_gic_its(&self, attr: u32, its_value: &mut u64, write: bool) -> Result<()>; + fn access_gic_its(&self, _attr: u32, _its_value: &mut u64, _write: bool) -> Result<()> { + Ok(()) + } - fn access_gic_its_tables(&self, save: bool) -> Result<()>; + fn access_gic_its_tables(&self, _save: bool) -> Result<()> { + Ok(()) + } - fn reset(&self) -> Result<()>; + fn reset(&self) -> Result<()> { + Ok(()) + } } pub struct GICv3Its { diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index f53f23267..25fc90ef6 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -14,6 +14,7 @@ pub mod error; pub mod kvm; +pub mod test; pub use error::HypervisorError; diff --git a/hypervisor/src/test/aarch64/mod.rs b/hypervisor/src/test/aarch64/mod.rs new file mode 100644 index 000000000..0b5317afb --- /dev/null +++ b/hypervisor/src/test/aarch64/mod.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use devices::{GICv3Access, GICv3ItsAccess}; + +#[derive(Default)] +pub struct TestGicv3 {} + +impl GICv3Access for TestGicv3 {} + +#[derive(Default)] +pub struct TestGicv3Its {} + +impl GICv3ItsAccess for TestGicv3Its {} diff --git a/hypervisor/src/test/listener.rs b/hypervisor/src/test/listener.rs new file mode 100644 index 000000000..15a65dd03 --- /dev/null +++ b/hypervisor/src/test/listener.rs @@ -0,0 +1,40 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use address_space::Listener; + +#[derive(Default, Clone)] +pub struct TestMemoryListener { + enabled: bool, +} + +impl Listener for TestMemoryListener { + /// Get default priority. + fn priority(&self) -> i32 { + 10_i32 + } + + /// Is this listener enabled to call. + fn enabled(&self) -> bool { + self.enabled + } + + /// Enable listener for address space. + fn enable(&mut self) { + self.enabled = true; + } + + /// Disable listener for address space. + fn disable(&mut self) { + self.enabled = false; + } +} diff --git a/hypervisor/src/test/mod.rs b/hypervisor/src/test/mod.rs new file mode 100644 index 000000000..8fce37ee1 --- /dev/null +++ b/hypervisor/src/test/mod.rs @@ -0,0 +1,413 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(target_arch = "aarch64")] +mod aarch64; +mod listener; + +use std::collections::HashMap; +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Barrier, Condvar, Mutex}; +use std::thread; +use std::time::Duration; + +use anyhow::{anyhow, Context, Result}; +use kvm_ioctls::DeviceFd; +use log::info; +use vmm_sys_util::eventfd::EventFd; + +#[cfg(target_arch = "aarch64")] +use self::aarch64::{TestGicv3, TestGicv3Its}; +use self::listener::TestMemoryListener; +use super::HypervisorOps; +use address_space::{AddressSpace, Listener}; +#[cfg(target_arch = "aarch64")] +use cpu::CPUFeatures; +use cpu::{ + ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUThreadWorker, CpuError, CpuLifecycleState, + RegsIndex, CPU, +}; +use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, TriggerMode}; +#[cfg(target_arch = "aarch64")] +use devices::{GICVersion, GICv3, ICGICConfig, InterruptController, GIC_IRQ_INTERNAL}; +use machine_manager::machine::HypervisorType; +use migration::{MigrateMemSlot, MigrateOps}; +use util::test_helper::{IntxInfo, MsixMsg, TEST_INTX_LIST, TEST_MSIX_LIST}; + +pub struct TestHypervisor {} + +impl TestHypervisor { + pub fn new() -> Result { + Ok(TestHypervisor {}) + } + + fn create_memory_listener(&self) -> Arc> { + Arc::new(Mutex::new(TestMemoryListener::default())) + } +} + +impl HypervisorOps for TestHypervisor { + fn get_hypervisor_type(&self) -> HypervisorType { + HypervisorType::Test + } + + fn init_machine( + &self, + #[cfg(target_arch = "x86_64")] _sys_io: &Arc, + sys_mem: &Arc, + ) -> Result<()> { + sys_mem + .register_listener(self.create_memory_listener()) + .with_context(|| "Failed to register hypervisor listener for memory space.") + } + + #[cfg(target_arch = "aarch64")] + fn create_interrupt_controller( + &mut self, + gic_conf: &ICGICConfig, + ) -> Result> { + gic_conf.check_sanity()?; + + let create_gicv3 = || { + let gicv3 = Arc::new(GICv3::new( + Arc::new(TestGicv3::default()), + Arc::new(TestGicv3Its::default()), + gic_conf, + )?); + + Ok(Arc::new(InterruptController::new(gicv3))) + }; + + match &gic_conf.version { + Some(GICVersion::GICv3) => create_gicv3(), + Some(GICVersion::GICv2) => Err(anyhow!("MST doesn't support Gicv2.")), + // Try v3 by default if no version specified. + None => create_gicv3(), + } + } + + #[cfg(target_arch = "x86_64")] + fn create_interrupt_controller(&mut self) -> Result<()> { + Ok(()) + } + + fn create_hypervisor_cpu( + &self, + vcpu_id: u8, + ) -> Result> { + Ok(Arc::new(TestCpu::new(vcpu_id))) + } + + fn create_irq_manager(&mut self) -> Result { + let test_irq_manager = Arc::new(TestInterruptManager {}); + Ok(IrqManager { + line_irq_manager: Some(test_irq_manager.clone()), + msi_irq_manager: Some(test_irq_manager), + }) + } + + fn create_vfio_device(&self) -> Option { + None + } +} + +pub struct TestCpu { + #[allow(unused)] + id: u8, +} + +impl TestCpu { + pub fn new(vcpu_id: u8) -> Self { + Self { id: vcpu_id } + } +} + +impl CPUHypervisorOps for TestCpu { + fn get_hypervisor_type(&self) -> HypervisorType { + HypervisorType::Test + } + + fn init_pmu(&self) -> Result<()> { + Ok(()) + } + + fn vcpu_init(&self) -> Result<()> { + Ok(()) + } + + #[allow(unused)] + fn set_boot_config( + &self, + arch_cpu: Arc>, + boot_config: &CPUBootConfig, + #[cfg(target_arch = "aarch64")] _vcpu_config: &CPUFeatures, + ) -> Result<()> { + #[cfg(target_arch = "aarch64")] + { + arch_cpu.lock().unwrap().mpidr = self.id as u64; + arch_cpu.lock().unwrap().set_core_reg(boot_config); + } + Ok(()) + } + + fn get_one_reg(&self, _reg_id: u64) -> Result { + Err(anyhow!("MST does not support getting one reg.")) + } + + fn get_regs(&self, _arch_cpu: Arc>, _regs_index: RegsIndex) -> Result<()> { + Ok(()) + } + + fn set_regs(&self, _arch_cpu: Arc>, _regs_index: RegsIndex) -> Result<()> { + Ok(()) + } + + fn put_register(&self, _cpu: Arc) -> Result<()> { + Err(anyhow!("Test does not support putting register.")) + } + + fn reset_vcpu(&self, cpu: Arc) -> Result<()> { + cpu.arch_cpu.lock().unwrap().set(&cpu.boot_state()); + Ok(()) + } + + fn vcpu_exec( + &self, + cpu_thread_worker: CPUThreadWorker, + thread_barrier: Arc, + ) -> Result<()> { + cpu_thread_worker.init_local_thread_vcpu(); + cpu_thread_worker.thread_cpu.set_tid(None); + + // Wait for all vcpu to complete the running + // environment initialization. + thread_barrier.wait(); + + info!("Test vcpu{} start running", cpu_thread_worker.thread_cpu.id); + while let Ok(true) = cpu_thread_worker.ready_for_running() { + thread::sleep(Duration::from_millis(5)); + continue; + } + + // The vcpu thread is about to exit, marking the state + // of the CPU state as Stopped. + let (cpu_state, cvar) = &*cpu_thread_worker.thread_cpu.state; + *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; + cvar.notify_one(); + + Ok(()) + } + + fn set_hypervisor_exit(&self) -> Result<()> { + Ok(()) + } + + fn pause( + &self, + _task: Arc>>>, + _state: Arc<(Mutex, Condvar)>, + _pause_signal: Arc, + ) -> Result<()> { + Ok(()) + } + + fn resume( + &self, + _state: Arc<(Mutex, Condvar)>, + _pause_signal: Arc, + ) -> Result<()> { + Ok(()) + } + + fn destroy( + &self, + _task: Arc>>>, + state: Arc<(Mutex, Condvar)>, + ) -> Result<()> { + let (cpu_state, cvar) = &*state; + let mut locked_cpu_state = cpu_state.lock().unwrap(); + if *locked_cpu_state == CpuLifecycleState::Running { + *locked_cpu_state = CpuLifecycleState::Stopping; + } else if *locked_cpu_state == CpuLifecycleState::Stopped + || *locked_cpu_state == CpuLifecycleState::Paused + { + return Ok(()); + } + drop(locked_cpu_state); + + let mut locked_cpu_state = cpu_state.lock().unwrap(); + locked_cpu_state = cvar + .wait_timeout(locked_cpu_state, Duration::from_millis(10)) + .unwrap() + .0; + + if *locked_cpu_state == CpuLifecycleState::Stopped { + Ok(()) + } else { + Err(anyhow!(CpuError::DestroyVcpu(format!( + "VCPU still in {:?} state", + *locked_cpu_state + )))) + } + } +} + +impl MigrateOps for TestHypervisor { + fn get_mem_slots(&self) -> Arc>> { + Arc::new(Mutex::new(HashMap::new())) + } + + fn get_dirty_log(&self, _slot: u32, _mem_size: u64) -> Result> { + Err(anyhow!( + "Failed to get dirty log, mst doesn't support migration feature." + )) + } + + fn start_dirty_log(&self) -> Result<()> { + Err(anyhow!( + "Failed to start dirty log, mst doesn't support migration feature." + )) + } + + fn stop_dirty_log(&self) -> Result<()> { + Err(anyhow!( + "Failed to stop dirty log, mst doesn't support migration feature." + )) + } + + fn register_instance(&self) -> Result<()> { + Ok(()) + } +} + +struct TestInterruptManager {} + +impl TestInterruptManager { + #[cfg(target_arch = "x86_64")] + pub fn arch_map_irq(&self, gsi: u32) -> u32 { + gsi + } + + #[cfg(target_arch = "aarch64")] + pub fn arch_map_irq(&self, gsi: u32) -> u32 { + gsi + GIC_IRQ_INTERNAL + } + + pub fn add_msix_msg(addr: u64, data: u32) { + let new_msg = MsixMsg::new(addr, data); + let mut msix_list_lock = TEST_MSIX_LIST.lock().unwrap(); + + for msg in msix_list_lock.iter() { + if new_msg.addr == msg.addr && new_msg.data == msg.data { + return; + } + } + + msix_list_lock.push(new_msg); + } +} + +impl LineIrqManager for TestInterruptManager { + fn irqfd_enable(&self) -> bool { + false + } + + fn register_irqfd( + &self, + _irq_fd: Arc, + _irq: u32, + _trigger_mode: TriggerMode, + ) -> Result<()> { + Err(anyhow!( + "Failed to register irqfd, mst doesn't support irqfd feature." + )) + } + + fn unregister_irqfd(&self, _irq_fd: Arc, _irq: u32) -> Result<()> { + Err(anyhow!( + "Failed to unregister irqfd, mst doesn't support irqfd feature." + )) + } + + fn set_level_irq(&self, gsi: u32, level: bool) -> Result<()> { + let physical_irq = self.arch_map_irq(gsi); + let level: i8 = if level { 1 } else { 0 }; + + let mut intx_list_lock = TEST_INTX_LIST.lock().unwrap(); + + for intx in intx_list_lock.iter_mut() { + if intx.irq == physical_irq { + intx.level = level; + return Ok(()); + } + } + + let new_intx = IntxInfo::new(physical_irq, level); + intx_list_lock.push(new_intx); + Ok(()) + } + + fn set_edge_irq(&self, _gsi: u32) -> Result<()> { + Ok(()) + } + + fn write_irqfd(&self, _irq_fd: Arc) -> Result<()> { + Err(anyhow!( + "Failed to write irqfd, mst doesn't support irqfd feature." + )) + } +} + +impl MsiIrqManager for TestInterruptManager { + fn allocate_irq(&self, _vector: MsiVector) -> Result { + Err(anyhow!( + "Failed to allocate irq, mst doesn't support irq routing feature." + )) + } + + fn release_irq(&self, _irq: u32) -> Result<()> { + Err(anyhow!( + "Failed to release irq, mst doesn't support irq routing feature." + )) + } + + fn register_irqfd(&self, _irq_fd: Arc, _irq: u32) -> Result<()> { + Err(anyhow!( + "Failed to register msi irqfd, mst doesn't support irqfd feature." + )) + } + + fn unregister_irqfd(&self, _irq_fd: Arc, _irq: u32) -> Result<()> { + Err(anyhow!( + "Failed to unregister msi irqfd, mst doesn't support irqfd feature." + )) + } + + fn trigger( + &self, + _irq_fd: Option>, + vector: MsiVector, + _dev_id: u32, + ) -> Result<()> { + let data = vector.msg_data; + let mut addr: u64 = vector.msg_addr_hi as u64; + addr = (addr << 32) + vector.msg_addr_lo as u64; + TestInterruptManager::add_msix_msg(addr, data); + Ok(()) + } + + fn update_route_table(&self, _gsi: u32, _vector: MsiVector) -> Result<()> { + Err(anyhow!( + "Failed to update route table, mst doesn't support irq routing feature." + )) + } +} diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs index 7f69b427a..b78f4e2b6 100644 --- a/util/src/test_helper.rs +++ b/util/src/test_helper.rs @@ -16,25 +16,25 @@ use std::time::{Duration, Instant}; use once_cell::sync::{Lazy, OnceCell}; #[derive(Default, Clone, Copy)] -struct MsixMsg { - addr: u64, - data: u32, +pub struct MsixMsg { + pub addr: u64, + pub data: u32, } impl MsixMsg { - fn new(addr: u64, data: u32) -> Self { + pub fn new(addr: u64, data: u32) -> Self { MsixMsg { addr, data } } } #[derive(Default, Clone, Copy, Debug)] -struct IntxInfo { - irq: u32, - level: i8, +pub struct IntxInfo { + pub irq: u32, + pub level: i8, } impl IntxInfo { - fn new(irq: u32, level: i8) -> Self { + pub fn new(irq: u32, level: i8) -> Self { IntxInfo { irq, level } } } @@ -42,8 +42,8 @@ impl IntxInfo { static TEST_ENABLED: OnceCell = OnceCell::new(); static TEST_BASE_TIME: OnceCell = OnceCell::new(); static mut TEST_CLOCK: Option>> = None; -static TEST_MSIX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); -static TEST_INTX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); +pub static TEST_MSIX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); +pub static TEST_INTX_LIST: Lazy>> = Lazy::new(|| Mutex::new(Vec::new())); pub fn set_test_enabled() { if let Err(_e) = TEST_ENABLED.set(true) { -- Gitee From 4f0623f96f1f80d3121a138bed896a1a3f55d57e Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 15 Apr 2024 20:51:04 +0800 Subject: [PATCH 1751/2187] Config: Support test as a hypervisor type Support test as a hypervisor type in the cmdline. Signed-off-by: Jinhao Gao --- devices/src/pci/intx.rs | 8 +------ devices/src/pci/msix.rs | 9 -------- machine/src/lib.rs | 23 +++++++++++++++----- machine_manager/src/config/machine_config.rs | 22 ++++++++++++++----- machine_manager/src/machine.rs | 17 ++++++++++++++- src/main.rs | 5 +++++ tests/mod_test/src/libtest.rs | 1 + util/src/test_helper.rs | 14 ------------ 8 files changed, 57 insertions(+), 42 deletions(-) diff --git a/devices/src/pci/intx.rs b/devices/src/pci/intx.rs index bf53fa520..b0624fe02 100644 --- a/devices/src/pci/intx.rs +++ b/devices/src/pci/intx.rs @@ -16,8 +16,7 @@ use anyhow::Result; use log::error; use crate::interrupt_controller::LineIrqManager; -use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_INTR_BASE, PCI_PIN_NUM}; -use util::test_helper::{is_test_enabled, trigger_intx}; +use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_PIN_NUM}; pub type InterruptHandler = Box Result<()> + Send + Sync>; @@ -96,11 +95,6 @@ impl Intx { let irq = locked_intx_state.gsi_base + self.irq_pin; let level = locked_intx_state.irq_count[self.irq_pin as usize] != 0; - if is_test_enabled() { - trigger_intx(irq + PCI_INTR_BASE as u32, change); - return; - } - let irq_handler = &locked_intx_state.irq_handler; if let Err(e) = irq_handler.set_level_irq(irq, level) { error!( diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 1d1e30be5..451a66384 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -32,7 +32,6 @@ use migration_derive::{ByteCode, Desc}; use util::{ byte_code::ByteCode, num_ops::{ranges_overlap, round_up}, - test_helper::{add_msix_msg, is_test_enabled}, }; pub const MSIX_TABLE_ENTRY_SIZE: u16 = 16; @@ -401,14 +400,6 @@ impl Msix { pub fn send_msix(&self, vector: u16, dev_id: u16) { let msg = self.get_message(vector); - if is_test_enabled() { - let data = msg.data; - let mut addr: u64 = msg.address_hi as u64; - addr = (addr << 32) + msg.address_lo as u64; - add_msix_msg(addr, data); - return; - } - let msix_vector = MsiVector { msg_addr_lo: msg.address_lo, msg_addr_hi: msg.address_hi, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6c13c03c1..73b58ce8f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -67,7 +67,7 @@ use devices::usb::{storage::UsbStorage, UsbDevice}; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; -use hypervisor::{kvm::KvmHypervisor, HypervisorOps}; +use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; #[cfg(feature = "demo_device")] @@ -87,7 +87,7 @@ use machine_manager::config::{ MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{MachineInterface, VmState}; +use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; use migration::{MigrateOps, MigrationManager}; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; @@ -186,7 +186,20 @@ impl MachineBase { mmio_region, ); - let hypervisor = Arc::new(Mutex::new(KvmHypervisor::new()?)); + let hypervisor: Arc>; + let migration_hypervisor: Arc>; + match vm_config.machine_config.hypervisor { + HypervisorType::Kvm => { + let kvm_hypervisor = Arc::new(Mutex::new(KvmHypervisor::new()?)); + hypervisor = kvm_hypervisor.clone(); + migration_hypervisor = kvm_hypervisor; + } + HypervisorType::Test => { + let test_hypervisor = Arc::new(Mutex::new(TestHypervisor::new()?)); + hypervisor = test_hypervisor.clone(); + migration_hypervisor = test_hypervisor; + } + }; Ok(MachineBase { cpu_topo, @@ -204,8 +217,8 @@ impl MachineBase { drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)), fwcfg_dev: None, machine_ram, - hypervisor: hypervisor.clone(), - migration_hypervisor: hypervisor, + hypervisor, + migration_hypervisor, }) } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index c1a6fb76f..490ee9070 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -20,6 +20,7 @@ use crate::config::{ check_arg_too_long, check_path_too_long, CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES, }; +use crate::machine::HypervisorType; const DEFAULT_CPUS: u8 = 1; const DEFAULT_THREADS: u8 = 1; @@ -167,6 +168,7 @@ pub enum ShutdownAction { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct MachineConfig { pub mach_type: MachineType, + pub hypervisor: HypervisorType, pub nr_cpus: u8, pub nr_threads: u8, pub nr_cores: u8, @@ -185,6 +187,7 @@ impl Default for MachineConfig { fn default() -> Self { MachineConfig { mach_type: MachineType::MicroVm, + hypervisor: HypervisorType::Kvm, nr_cpus: DEFAULT_CPUS, nr_threads: DEFAULT_THREADS, nr_cores: DEFAULT_CORES, @@ -239,9 +242,14 @@ impl VmConfig { if let Some(accel) = cmd_parser.get_value::("accel")? { // Libvirt checks the parameter types of 'kvm', 'kvm:tcg' and 'tcg'. - if accel.ne("kvm:tcg") && accel.ne("tcg") && accel.ne("kvm") { - bail!("Only \'kvm\', \'kvm:tcg\' and \'tcg\' are supported for \'accel\' of \'machine\'"); + if accel.ne("kvm:tcg") && accel.ne("tcg") && accel.ne("kvm") && accel.ne("test") { + bail!("Only \'kvm\', \'kvm:tcg\', \'test\' and \'tcg\' are supported for \'accel\' of \'machine\'"); } + + match accel.as_str() { + "test" => self.machine_config.hypervisor = HypervisorType::Test, + _ => self.machine_config.hypervisor = HypervisorType::Kvm, + }; } if let Some(usb) = cmd_parser.get_value::("usb")? { if usb.into() { @@ -276,10 +284,11 @@ impl VmConfig { cmd_parser.push(""); cmd_parser.parse(accel_config)?; - if let Some(accel) = cmd_parser.get_value::("")? { - if accel.ne("kvm") { - bail!("Only \'kvm\' is supported for \'accel\'"); - } + if let Some(accel) = cmd_parser + .get_value::("") + .with_context(|| "Only \'kvm\' and \'test\' is supported for \'accel\'")? + { + self.machine_config.hypervisor = accel; } Ok(()) @@ -747,6 +756,7 @@ mod tests { }; let mut machine_config = MachineConfig { mach_type: MachineType::MicroVm, + hypervisor: HypervisorType::Kvm, nr_cpus: 1, nr_cores: 1, nr_threads: 1, diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index f0f00aa34..ff4d85863 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -11,9 +11,11 @@ // See the Mulan PSL v2 for more details. use std::os::unix::io::RawFd; +use std::str::FromStr; use std::sync::Mutex; use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; use strum::VariantNames; use crate::config::ShutdownAction; @@ -44,10 +46,23 @@ pub enum VmState { } /// Type for Hypervisor. -#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum HypervisorType { #[default] Kvm, + Test, +} + +impl FromStr for HypervisorType { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + match s { + "kvm" => Ok(HypervisorType::Kvm), + "test" => Ok(HypervisorType::Test), + _ => Err(()), + } + } } /// Trait to handle virtual machine lifecycle. diff --git a/src/main.rs b/src/main.rs index 15eaefc31..89c4e23e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,6 +74,11 @@ fn run() -> Result<()> { let cmd_args = create_args_parser().get_matches()?; if cmd_args.is_present("mod-test") { + let machine = cmd_args.value_of("machine").unwrap_or_default(); + let accel = cmd_args.value_of("accel").unwrap_or_default(); + if !machine.contains("accel=test") && accel.ne("test") { + bail!("MST can only use test accel!"); + } set_test_enabled(); } diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 9b1f6f685..6de33d664 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -356,6 +356,7 @@ pub fn test_init(extra_arg: Vec<&str>) -> TestState { let listener = init_socket(&test_socket); let child = Command::new(binary_path) + .args(["-accel", "test"]) .args(["-qmp", &format!("unix:{},server,nowait", qmp_socket)]) .args(["-mod-test", &test_socket]) .args(extra_arg) diff --git a/util/src/test_helper.rs b/util/src/test_helper.rs index b78f4e2b6..47cb11b62 100644 --- a/util/src/test_helper.rs +++ b/util/src/test_helper.rs @@ -139,20 +139,6 @@ pub fn has_msix_msg(addr: u64, data: u32) -> bool { } } -pub fn trigger_intx(irq: u32, change: i8) { - let new_intx = IntxInfo::new(irq, change); - let mut intx_list_lock = TEST_INTX_LIST.lock().unwrap(); - - for intx in intx_list_lock.iter_mut() { - if intx.irq == new_intx.irq { - intx.level += new_intx.level; - return; - } - } - - intx_list_lock.push(new_intx); -} - pub fn query_intx(irq: u32) -> bool { let mut intx_list_lock = TEST_INTX_LIST.lock().unwrap(); for intx in intx_list_lock.iter_mut() { -- Gitee From 3034d0c33d2c976e9e46b247d2b1078f329cc25f Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 16 Apr 2024 12:35:36 +0800 Subject: [PATCH 1752/2187] Kvm: Delete some code related to MST The code related to MST is unnecessary when test hypervisor is supported. Signed-off-by: Jinhao Gao --- hypervisor/src/kvm/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 462525fb2..6820eccfd 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -73,7 +73,6 @@ use machine_manager::machine::HypervisorType; #[cfg(target_arch = "aarch64")] use migration::snapshot::{GICV3_ITS_SNAPSHOT_ID, GICV3_SNAPSHOT_ID}; use migration::{MigrateMemSlot, MigrateOps, MigrationManager}; -use util::test_helper::is_test_enabled; #[cfg(target_arch = "x86_64")] use x86_64::cpu_caps::X86CPUCaps as CPUCaps; @@ -152,9 +151,7 @@ impl HypervisorOps for KvmHypervisor { ) -> Result<()> { self.arch_init()?; - if !is_test_enabled() { - sys_mem.set_ioevtfd_enabled(true); - } + sys_mem.set_ioevtfd_enabled(true); sys_mem .register_listener(self.create_memory_listener()) @@ -600,10 +597,6 @@ impl CPUHypervisorOps for KvmCpu { while let Ok(true) = cpu_thread_worker.ready_for_running() { #[cfg(not(test))] { - if is_test_enabled() { - thread::sleep(Duration::from_millis(5)); - continue; - } if !self .kvm_vcpu_exec(cpu_thread_worker.thread_cpu.clone()) .with_context(|| { -- Gitee From d953d0f1f32c630a08efd031be5b373918e12d1a Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 16 Apr 2024 11:01:55 +0800 Subject: [PATCH 1753/2187] Machine manager: Fix format Fix some problem of format. Signed-off-by: Jinhao Gao --- machine_manager/src/temp_cleaner.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/machine_manager/src/temp_cleaner.rs b/machine_manager/src/temp_cleaner.rs index e63d27375..cd5b2f42d 100644 --- a/machine_manager/src/temp_cleaner.rs +++ b/machine_manager/src/temp_cleaner.rs @@ -81,15 +81,12 @@ impl TempCleaner { while let Some(path) = self.paths.pop() { if Path::new(&path).exists() { if let Err(ref e) = fs::remove_file(&path) { - error!( - "Failed to delete console / socket file:{} :{} \r\n", - &path, e - ); + error!("Failed to delete console / socket file:{} :{}", &path, e); } else { - info!("Delete file: {} successfully.\r\n", &path); + info!("Delete file: {} successfully", &path); } } else { - info!("file: {} has been removed \r\n", &path); + info!("file: {} has been removed", &path); } } } -- Gitee From 9ff9894b7128071b0e4c7c1e1d165aab19117a5a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 26 Apr 2024 14:13:22 +0800 Subject: [PATCH 1754/2187] virtio-serial: print `nr` in virtio-serial's log print `nr` in log. Signed-off-by: liuxiangdong --- virtio/src/device/serial.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 7c6864f61..5bf1c101f 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -263,8 +263,8 @@ impl VirtioDevice for Serial { 0 => 0, 1 => continue, _ => queue_id - 1, - }; - let port = find_port_by_nr(&self.ports, nr as u32); + } as u32; + let port = find_port_by_nr(&self.ports, nr); let handler = SerialPortHandler { input_queue: queues[queue_id * 2].clone(), input_queue_evt: queue_evts[queue_id * 2].clone(), @@ -275,6 +275,7 @@ impl VirtioDevice for Serial { driver_features: self.base.driver_features, device_broken: self.base.broken.clone(), port: port.clone(), + nr, }; let handler_h = Arc::new(Mutex::new(handler)); let notifiers = EventNotifierHelper::internal_notifiers(handler_h.clone()); @@ -412,6 +413,7 @@ struct SerialPortHandler { /// Virtio serial device is broken or not. device_broken: Arc, port: Option>>, + nr: u32, } /// Handler for queues which are used for control. @@ -431,7 +433,7 @@ impl SerialPortHandler { fn output_handle(&mut self) { trace::virtio_receive_request("Serial".to_string(), "to IO".to_string()); self.output_handle_internal().unwrap_or_else(|e| { - error!("Port handle output error: {:?}", e); + error!("Port {} handle output error: {:?}", self.nr, e); report_virtio_error( self.interrupt_cb.clone(), self.driver_features, @@ -523,13 +525,13 @@ impl SerialPortHandler { // This may occur when chardev is abnormal. Consider optimizing this logic in the // future. if let Err(e) = locked_output.write_all(&buffer[..write_len]) { - error!("Failed to write msg to chardev: {:?}", e); + error!("Port {} failed to write msg to chardev: {:?}", self.nr, e); } if let Err(e) = locked_output.flush() { - error!("Failed to flush msg to chardev: {:?}", e); + error!("Port {} failed to flush msg to chardev: {:?}", self.nr, e); } } else { - error!("Failed to get output fd"); + error!("Port {} failed to get output fd", self.nr); }; } @@ -552,7 +554,10 @@ impl SerialPortHandler { { Ok(n) => n, Err(_) => { - warn!("error occurred while getting available bytes of vring"); + warn!( + "error occurred while port {} getting available bytes of vring", + self.nr + ); 0 } } @@ -697,7 +702,7 @@ impl EventNotifierHelper for SerialPortHandler { impl InputReceiver for SerialPortHandler { fn receive(&mut self, buffer: &[u8]) { self.input_handle_internal(buffer).unwrap_or_else(|e| { - error!("Port handle input error: {:?}", e); + error!("Port {} handle input error: {:?}", self.nr, e); report_virtio_error( self.interrupt_cb.clone(), self.driver_features, -- Gitee From 5715e1fa92bdc5df95c2d10803dcdf661e423cea Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Apr 2024 18:17:40 +0800 Subject: [PATCH 1755/2187] stratovirt-img: add query operation Add query operation for stratovirt-img, which can be used for query image info. Signed-off-by: Xiao Ye --- block_backend/src/lib.rs | 12 ++++++++++++ block_backend/src/qcow2/mod.rs | 6 +++++- block_backend/src/raw.rs | 6 +++++- image/src/img.rs | 4 ++++ image/src/main.rs | 3 ++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index cbe37dfaa..c4877097f 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -151,6 +151,16 @@ impl CreateOptions { } } +#[derive(Default)] +pub struct ImageInfo { + pub path: String, + pub format: String, + pub actual_size: u64, + pub virtual_size: u64, + pub cluster_size: Option, + pub snap_lists: Option, +} + #[derive(Default, Clone, Copy)] pub struct DiskFragments { pub allocated_clusters: u64, @@ -291,6 +301,8 @@ impl Default for BlockProperty { pub trait BlockDriverOps: Send { fn create_image(&mut self, options: &CreateOptions) -> Result; + fn query_image(&mut self, image_info: &mut ImageInfo) -> Result<()>; + fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()>; fn disk_size(&mut self) -> Result; diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 922233f6c..9f30c22b6 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -50,7 +50,7 @@ use crate::{ table::{Qcow2ClusterType, Qcow2Table}, }, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, CheckResult, CreateOptions, - SECTOR_SIZE, + ImageInfo, SECTOR_SIZE, }; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_schema::SnapshotInfo; @@ -1643,6 +1643,10 @@ impl BlockDriverOps for Qcow2Driver { Ok(image_info) } + fn query_image(&mut self, _info: &mut ImageInfo) -> Result<()> { + todo!(); + } + fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()> { let cluster_size = self.header.cluster_size(); let refcount_order = self.header.refcount_order; diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index c051b3f10..ccbc31a67 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -25,7 +25,7 @@ use crate::{ file::{CombineRequest, FileDriver}, qcow2::is_aligned, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, CheckResult, CreateOptions, - SECTOR_SIZE, + ImageInfo, SECTOR_SIZE, }; use util::{ aio::{get_iov_size, raw_write, Aio, Iovec}, @@ -92,6 +92,10 @@ impl BlockDriverOps for RawDriver { Ok(image_info) } + fn query_image(&mut self, _info: &mut ImageInfo) -> Result<()> { + todo!(); + } + fn check_image(&mut self, _res: &mut CheckResult, _quite: bool, _fix: u64) -> Result<()> { bail!("This image format does not support checks"); } diff --git a/image/src/img.rs b/image/src/img.rs index 0e54d0b90..fd3c5a438 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -190,6 +190,10 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { Ok(()) } +pub(crate) fn image_info(_args: Vec) -> Result<()> { + todo!() +} + pub(crate) fn image_check(args: Vec) -> Result<()> { let mut arg_parser = ArgsParse::create(vec!["no_print_error", "h", "help"], vec!["f", "r"], vec![]); diff --git a/image/src/main.rs b/image/src/main.rs index 055ac211f..b9c7aef5e 100644 --- a/image/src/main.rs +++ b/image/src/main.rs @@ -21,7 +21,7 @@ use std::{ use anyhow::{bail, Result}; use crate::img::{ - image_check, image_create, image_resize, image_snapshot, print_help, print_version, + image_check, image_create, image_info, image_resize, image_snapshot, print_help, print_version, }; const BINARY_NAME: &str = "stratovirt-img"; @@ -83,6 +83,7 @@ fn run(args: Vec) -> Result<()> { image_operation_matches!( opt.as_str(); ("create", image_create, cmd_args), + ("info", image_info, cmd_args), ("check", image_check, cmd_args), ("resize", image_resize, cmd_args), ("snapshot", image_snapshot, cmd_args); -- Gitee From 9f9742df32ad78e6ec57fd939d41eaa0f4978959 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Apr 2024 18:33:14 +0800 Subject: [PATCH 1756/2187] stratovirt-img: add arg parse for query operation Cmd line of query operation is as follows: stratovirt-img info filename Signed-off-by: Xiao Ye --- block_backend/src/lib.rs | 51 ++++++++++++++++++++++++++++++++++++++++ image/src/img.rs | 51 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index c4877097f..b6f42513c 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -15,6 +15,7 @@ pub mod qcow2; pub mod raw; use std::{ + fmt, fs::File, sync::{ atomic::{AtomicBool, AtomicU64, Ordering}, @@ -151,6 +152,30 @@ impl CreateOptions { } } +// Transform size into string with storage units. +fn size_to_string(size: f64) -> Result { + let units = vec!["", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; + + // Switch to higher power if the integer part is >= 1000, + // For example: 1000 * 2^30 bytes + // It's better to output 0.978 TiB, rather than 1000 GiB. + let n = (size / 1000.0 * 1024.0).log2() as u64; + let idx = n / 10; + if idx >= units.len() as u64 { + bail!("Input value {} is too large", size); + } + let div = 1_u64 << (idx * 10); + + // Keep three significant digits and do not output any extra zeros, + // For example: 512 * 2^20 bytes + // It's better to output 512 MiB, rather than 512.000 MiB. + let num_str = format!("{:.3}", size / div as f64); + let num_str = num_str.trim_end_matches('0').trim_end_matches('.'); + + let res = format!("{} {}", num_str, units[idx as usize]); + Ok(res) +} + #[derive(Default)] pub struct ImageInfo { pub path: String, @@ -161,6 +186,32 @@ pub struct ImageInfo { pub snap_lists: Option, } +impl fmt::Display for ImageInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "image: {}\n\ + file format: {}\n\ + virtual size: {} ({} bytes)\n\ + disk size: {}", + self.path, + self.format, + size_to_string(self.virtual_size as f64).unwrap_or_else(|e| format!("{:?}", e)), + self.virtual_size, + size_to_string(self.actual_size as f64).unwrap_or_else(|e| format!("{:?}", e)) + )?; + + if let Some(cluster_size) = self.cluster_size { + writeln!(f, "cluster_size: {}", cluster_size)?; + } + + if let Some(snap_lists) = &self.snap_lists { + write!(f, "Snapshot list:\n{}", snap_lists)?; + } + Ok(()) + } +} + #[derive(Default, Clone, Copy)] pub struct DiskFragments { pub allocated_clusters: u64, diff --git a/image/src/img.rs b/image/src/img.rs index fd3c5a438..caf1ebf3d 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -23,8 +23,8 @@ use crate::{cmdline::ArgsParse, BINARY_NAME}; use block_backend::{ qcow2::{header::QcowHeader, InternalSnapshotOps, Qcow2Driver, SyncAioInfo}, raw::RawDriver, - BlockDriverOps, BlockProperty, CheckResult, CreateOptions, FIX_ERRORS, FIX_LEAKS, NO_FIX, - SECTOR_SIZE, + BlockDriverOps, BlockProperty, CheckResult, CreateOptions, ImageInfo, FIX_ERRORS, FIX_LEAKS, + NO_FIX, SECTOR_SIZE, }; use machine_manager::config::{memory_unit_conversion, DiskFormat}; use util::{ @@ -190,8 +190,50 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { Ok(()) } -pub(crate) fn image_info(_args: Vec) -> Result<()> { - todo!() +pub(crate) fn image_info(args: Vec) -> Result<()> { + if args.len() < 1 { + bail!("Not enough arguments"); + } + let mut arg_parser = ArgsParse::create(vec!["h", "help"], vec![], vec![]); + arg_parser.parse(args)?; + + if arg_parser.opt_present("h") || arg_parser.opt_present("help") { + print_help(); + return Ok(()); + } + + // Parse the image path. + let len = arg_parser.free.len(); + let img_path = match len { + 0 => bail!("Image path is needed"), + 1 => arg_parser.free[0].clone(), + _ => { + let param = arg_parser.free[1].clone(); + bail!("Unexpected argument: {}", param); + } + }; + + let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; + let image_file = ImageFile::create(&img_path, false)?; + let detect_fmt = image_file.detect_img_format()?; + + let mut conf = BlockProperty::default(); + conf.format = detect_fmt; + let mut driver: Box> = match detect_fmt { + DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.try_clone()?, aio, conf)), + DiskFormat::Qcow2 => { + let mut qocw2_driver = + Qcow2Driver::new(image_file.file.try_clone()?, aio, conf.clone())?; + qocw2_driver.load_metadata(conf)?; + Box::new(qocw2_driver) + } + }; + + let mut image_info = ImageInfo::default(); + image_info.path = img_path; + driver.query_image(&mut image_info)?; + print!("{}", image_info); + Ok(()) } pub(crate) fn image_check(args: Vec) -> Result<()> { @@ -478,6 +520,7 @@ Stratovirt disk image utility Command syntax: create [-f fmt] [-o options] filename [size] +info filename check [-r [leaks | all]] [-no_print_error] [-f fmt] filename resize [-f fmt] filename [+]size snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename -- Gitee From e862b3b644d405cb14ff3a130941089956c7ab10 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Apr 2024 18:47:27 +0800 Subject: [PATCH 1757/2187] stratovirt-img: implement query operation. Implement query operations for raw and qcow2. Signed-off-by: Xiao Ye --- block_backend/src/file.rs | 12 ++++++++++-- block_backend/src/qcow2/mod.rs | 14 +++++++++++--- block_backend/src/raw.rs | 7 +++++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 83237651e..45ea15493 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -14,7 +14,10 @@ use std::{ cell::RefCell, fs::File, io::{Seek, SeekFrom}, - os::unix::prelude::{AsRawFd, RawFd}, + os::{ + linux::fs::MetadataExt, + unix::prelude::{AsRawFd, RawFd}, + }, rc::Rc, sync::{ atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering}, @@ -26,7 +29,7 @@ use anyhow::{Context, Result}; use log::error; use vmm_sys_util::epoll::EventSet; -use crate::{BlockIoErrorCallback, BlockProperty}; +use crate::{qcow2::DEFAULT_SECTOR_SIZE, BlockIoErrorCallback, BlockProperty}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::{ aio::{Aio, AioCb, AioEngine, Iovec, OpCode}, @@ -188,6 +191,11 @@ impl FileDriver { unregister_event_helper(self.block_prop.iothread.as_ref(), &mut self.delete_evts) } + pub fn actual_size(&mut self) -> Result { + let meta_data = self.file.metadata()?; + Ok(meta_data.st_blocks() * DEFAULT_SECTOR_SIZE) + } + pub fn disk_size(&mut self) -> Result { let disk_size = self .file diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 9f30c22b6..1b5a3dfbc 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -77,7 +77,7 @@ pub const QCOW2_OFLAG_ZERO: u64 = 1 << 0; const QCOW2_OFFSET_COMPRESSED: u64 = 1 << 62; pub const QCOW2_OFFSET_COPIED: u64 = 1 << 63; const MAX_L1_SIZE: u64 = 32 * (1 << 20); -const DEFAULT_SECTOR_SIZE: u64 = 512; +pub(crate) const DEFAULT_SECTOR_SIZE: u64 = 512; pub(crate) const QCOW2_MAX_L1_SIZE: u64 = 1 << 25; // The default flush interval is 30s. @@ -1643,8 +1643,16 @@ impl BlockDriverOps for Qcow2Driver { Ok(image_info) } - fn query_image(&mut self, _info: &mut ImageInfo) -> Result<()> { - todo!(); + fn query_image(&mut self, info: &mut ImageInfo) -> Result<()> { + info.format = "qcow2".to_string(); + info.virtual_size = self.disk_size()?; + info.actual_size = self.driver.actual_size()?; + info.cluster_size = Some(self.header.cluster_size()); + + if !self.snapshot.snapshots.is_empty() { + info.snap_lists = Some(self.qcow2_list_snapshots()); + } + Ok(()) } fn check_image(&mut self, res: &mut CheckResult, quite: bool, fix: u64) -> Result<()> { diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index ccbc31a67..d8622d5aa 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -92,8 +92,11 @@ impl BlockDriverOps for RawDriver { Ok(image_info) } - fn query_image(&mut self, _info: &mut ImageInfo) -> Result<()> { - todo!(); + fn query_image(&mut self, info: &mut ImageInfo) -> Result<()> { + info.format = "raw".to_string(); + info.virtual_size = self.disk_size()?; + info.actual_size = self.driver.actual_size()?; + Ok(()) } fn check_image(&mut self, _res: &mut CheckResult, _quite: bool, _fix: u64) -> Result<()> { -- Gitee From a5eede98da267d31520f2e654f93f5b8b3ca7409 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Apr 2024 19:15:16 +0800 Subject: [PATCH 1758/2187] stratovirt-img: add unit test Add unit test for the function of image_info. Signed-off-by: Xiao Ye --- image/src/img.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/image/src/img.rs b/image/src/img.rs index caf1ebf3d..e106b70eb 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -807,6 +807,55 @@ mod test { assert!(remove_file(path).is_ok()); } + /// Test the function of query image. + /// TestStep: + /// 2. Query image info with different type. + /// Expect: + /// 1. Ihe invalid args will result in failure. + #[test] + fn test_args_parse_of_image_info() { + let path = "/tmp/test_args_parse_of_image_info.qcow2"; + let test_case = vec![ + ("img_path", true), + ("-f qcow2", false), + ("invalid_args", false), + ("img_path +1G", false), + ("-h", true), + ("--help", true), + ]; + + for case in test_case { + let cmd_str = case.0.replace("img_path", path); + let args: Vec = cmd_str + .split(' ') + .into_iter() + .map(|str| str.to_string()) + .collect(); + + // Query image info with type of qcow2. + assert!(image_create(vec![ + "-f".to_string(), + "qcow2".to_string(), + path.to_string(), + "+10M".to_string() + ]) + .is_ok()); + assert_eq!(image_info(args.clone()).is_ok(), case.1); + + // Query image info with type of raw. + assert!(image_create(vec![ + "-f".to_string(), + "raw".to_string(), + path.to_string(), + "+10M".to_string() + ]) + .is_ok()); + assert_eq!(image_info(args).is_ok(), case.1); + } + + assert!(remove_file(path).is_ok()); + } + /// Test the function of creating image. /// TestStep: /// 1. Create image with different cluster bits, image size and refcount bits. -- Gitee From a39513824699b6ff038a5cdaa36ad711a906befc Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 26 Apr 2024 19:21:44 +0800 Subject: [PATCH 1759/2187] stratovirt-img: add docs Add docs for query operation of stratovirt-img. Signed-off-by: Xiao Ye --- docs/stratovirt-img.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/stratovirt-img.md b/docs/stratovirt-img.md index e44be8d67..7021e5655 100644 --- a/docs/stratovirt-img.md +++ b/docs/stratovirt-img.md @@ -35,6 +35,16 @@ stratovirt-img create -f qcow2 -o cluster-size=65536 img_path img_size Note: 1. The cluster size can be only be set for `qcow2` or default to 65536. 2. Disk format is default to raw. +## Info + +Query the information of virtual disk. + +Sample Configuration: + +```shell +stratovirt-img info img_path +``` + ## Check Check if there are some mistakes on the image and choose to fix. -- Gitee From 584c104e2a826d823ddf67c6cebbeb0cde6d0ed8 Mon Sep 17 00:00:00 2001 From: yexiao Date: Sat, 27 Apr 2024 20:29:28 +0800 Subject: [PATCH 1760/2187] log: modify some log Modify some log information. Signed-off-by: Xiao Ye --- machine/src/standard_common/mod.rs | 4 ++-- machine_manager/src/cmdline.rs | 2 +- machine_manager/src/config/mod.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 67ab3e228..bac724a89 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -882,11 +882,11 @@ impl StdMachine { power_button: Arc, shutdown_req: Arc, ) { - let emu_pid = vm_config.windows_emu_pid.as_ref(); + let emu_pid = vm_config.emulator_pid.as_ref(); if emu_pid.is_none() { return; } - log::info!("Watching on windows emu lifetime"); + log::info!("Watching on emulator lifetime"); crate::check_windows_emu_pid( "/proc/".to_owned() + emu_pid.unwrap(), power_button, diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index c9f54f6e2..de8e54c4e 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -509,7 +509,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .multiple(false) .long("windows_emu_pid") .value_name("pid") - .help("watch on the external windows emu pid") + .help("watch on the external emulator pid") .takes_value(true), ); diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ab6c0edfe..36ae7a407 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -141,7 +141,7 @@ pub struct VmConfig { #[cfg(feature = "usb_camera")] pub camera_backend: HashMap, #[cfg(feature = "windows_emu_pid")] - pub windows_emu_pid: Option, + pub emulator_pid: Option, pub smbios: SmbiosConfig, } @@ -268,9 +268,9 @@ impl VmConfig { #[cfg(feature = "windows_emu_pid")] pub fn add_windows_emu_pid(&mut self, windows_emu_pid: &str) -> Result<()> { if windows_emu_pid.is_empty() { - bail!("The arg of windows_emu_pid is empty!"); + bail!("The arg of emulator_pid is empty!"); } - self.windows_emu_pid = Some(windows_emu_pid.to_string()); + self.emulator_pid = Some(windows_emu_pid.to_string()); Ok(()) } -- Gitee From b79f9adb6ff0bb271781a65d3bcbfb5d81ee751b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 28 Apr 2024 02:49:25 +0800 Subject: [PATCH 1761/2187] pci: fix error devfn note Fix error devfn not. `devfn` is `Slot number << 3 | Function number`. Signed-off-by: liuxiangdong --- devices/src/pci/bus.rs | 2 +- devices/src/pci/config.rs | 2 +- devices/src/pci/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index b0f5dc801..d493e1812 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -94,7 +94,7 @@ impl PciBus { /// # Arguments /// /// * `bus_num` - The bus number. - /// * `devfn` - Slot number << 8 | device number. + /// * `devfn` - Slot number << 3 | Function number. pub fn get_device(&self, bus_num: u8, devfn: u8) -> Option>> { if let Some(dev) = self.devices.get(&devfn) { return Some((*dev).clone()); diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 1e6645610..16c9b6a53 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -1020,7 +1020,7 @@ impl PciConfig { /// /// # Arguments /// - /// * `devfn` - Slot number << 3 | function number. + /// * `devfn` - Slot number << 3 | Function number. /// * `port_num` - Port number. /// * `dev_type` - Device type. pub fn add_pcie_cap(&mut self, devfn: u8, port_num: u8, dev_type: u8) -> Result { diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 4a8d942d8..0c8c0af3e 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -200,7 +200,7 @@ pub trait PciDevOps: Device + Send + AsAny { /// # Arguments /// /// * `bus_num` - Bus number. - /// * `devfn` - Slot number << 8 | Function number. + /// * `devfn` - Slot number << 3 | Function number. /// /// # Returns /// -- Gitee From 6079887d8c4beeb941d97d8a22f0f588f0fd6e74 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 28 Apr 2024 09:18:09 +0800 Subject: [PATCH 1762/2187] virtio-fs: don't trigger irq of the virtio-fs device in test hypervisor Commit 4f0623f96f(Config: Support test as a hypervisor type) introduce test hypervisor and it doesn't support irqfd. As a result, virtio-fs device cannot be created successfully in MST. The virtio-fs device needs to determine whether to use irqfd based on the kernel support. Fix: 4f0623f96f(Config: Support test as a hypervisor type) part cherry-pick from 074e684648a34f9b88a7785300acc1abcec97bfa ([Huawei]vhost-user-fs: Fix the bug of triggering irq of the vhost-user-fs device) Signed-off-by: Jinhao Gao Signed-off-by: liuxiangdong --- devices/src/interrupt_controller/mod.rs | 2 ++ hypervisor/src/kvm/mod.rs | 4 ++++ hypervisor/src/test/mod.rs | 4 ++++ machine/src/lib.rs | 5 ++++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index 4a0453c8f..a1fb65d2e 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -81,6 +81,8 @@ pub trait LineIrqManager: Send + Sync { } pub trait MsiIrqManager: Send + Sync { + fn irqfd_enable(&self) -> bool; + fn allocate_irq(&self, _vector: MsiVector) -> Result { Ok(0) } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 8648b9c21..3e03a2a95 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -816,6 +816,10 @@ impl LineIrqManager for KVMInterruptManager { } impl MsiIrqManager for KVMInterruptManager { + fn irqfd_enable(&self) -> bool { + self.irqfd_cap + } + fn allocate_irq(&self, vector: MsiVector) -> Result { let mut locked_irq_route_table = self.irq_route_table.lock().unwrap(); let gsi = locked_irq_route_table.allocate_gsi().map_err(|e| { diff --git a/hypervisor/src/test/mod.rs b/hypervisor/src/test/mod.rs index 8fce37ee1..8e4697ae1 100644 --- a/hypervisor/src/test/mod.rs +++ b/hypervisor/src/test/mod.rs @@ -368,6 +368,10 @@ impl LineIrqManager for TestInterruptManager { } impl MsiIrqManager for TestInterruptManager { + fn irqfd_enable(&self) -> bool { + false + } + fn allocate_irq(&self, _vector: MsiVector) -> Result { Err(anyhow!( "Failed to allocate irq, mst doesn't support irq routing feature." diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 895551eef..fd21da8b9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -869,7 +869,10 @@ pub trait MachineOps { let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem))); let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; - self.add_virtio_pci_device(&id_clone, &bdf, device, multi_func, true) + let root_bus = self.get_pci_host()?.lock().unwrap().root_bus.clone(); + let msi_irq_manager = &root_bus.lock().unwrap().msi_irq_manager; + let need_irqfd = msi_irq_manager.as_ref().unwrap().irqfd_enable(); + self.add_virtio_pci_device(&id_clone, &bdf, device, multi_func, need_irqfd) .with_context(|| "Failed to add pci fs device")?; } } -- Gitee From 7d71547fdd0298a8689f40d6518ee80f3d1addd6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 28 Apr 2024 12:18:51 +0800 Subject: [PATCH 1763/2187] virtio-fs: use clone instead of reference to avoid deadlock `root_bus` will be locked for using its reference and then deadlock will occur in `add_virtio_pci_device`->`get_devfn_and_parent_bus`. (gdb) bt #0 syscall () at ../sysdeps/unix/sysv/linux/aarch64/syscall.S:38 #1 0x0000aaaabb7588f4 in std::sys::unix::futex::futex_wait () at library/std/src/sys/unix/futex.rs:62 #2 0x0000aaaababc3d44 in std::sys::unix::locks::futex_mutex::Mutex::lock_contended () at library/std/src/sys/unix/locks/futex_mutex.rs:61 #3 0x0000aaaabb048420 in std::sys::unix::locks::futex_mutex::Mutex::lock (self=0xaaaac1825080) at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys/unix/locks/futex_mutex.rs:33 #4 0x0000aaaabaf9b910 in std::sys_common::mutex::MovableMutex::raw_lock (self=0xaaaac1825080) at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/mutex.rs:75 #5 0x0000aaaabaf1c9a8 in std::sync::mutex::Mutexdevices::pci::bus::PciBus::lockdevices::pci::bus::PciBus (self=0xaaaac1825080) at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sync/mutex.rs:266 #6 0x0000aaaabaed0c90 in devices::pci::bus::PciBus::find_bus_by_name (bus=0xffffe9972688, bus_name=...) at devices/src/pci/bus.rs:147 #7 0x0000aaaabac13dd8 in machine::MachineOps::get_devfn_and_parent_busmachine::aarch64::standard::StdMachine (self=0xaaaac1828fe8, bdf=0xffffe9973670) at machine/src/lib.rs:1357 #8 0x0000aaaabac14494 in machine::MachineOps::add_virtio_pci_devicemachine::aarch64::standard::StdMachine (self=0xaaaac1828fe8, id=..., bdf=0xffffe9973670, device=..., multi_func=false, need_irqfd=false) at machine/src/lib.rs:1396 #9 0x0000aaaabac0fa58 in machine::MachineOps::add_virtio_fsmachine::aarch64::standard::StdMachine (self=0xaaaac1828fe8, vm_config=0xffffe9977658, cfg_args=...) at machine/src/lib.rs:875 #10 0x0000aaaabac184a4 in machine::MachineOps::add_devicesmachine::aarch64::standard::StdMachine (self=0xaaaac1828fe8, vm_config=0xffffe9977658) at machine/src/lib.rs:1787 #11 0x0000aaaababf4618 in machine::aarch64::standard::{impl#2}::realize (vm=0xffffe9975bf0, vm_config=0xffffe9977658) at machine/src/aarch64/standard.rs:631 #12 0x0000aaaababdff80 in stratovirt::real_main (cmd_args=0xffffe9977430, vm_config=0xffffe9977658) at src/main.rs:174 #13 0x0000aaaababdf4a4 in stratovirt::run () at src/main.rs:107 #14 0x0000aaaababdeefc in stratovirt::main () at src/main.rs:62 Fix: 6079887d8c4b(virtio-fs: don't trigger irq of the virtio-fs device in test hypervisor) Signed-off-by: liuxiangdong --- machine/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fd21da8b9..7b4b7a01d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -870,7 +870,7 @@ pub trait MachineOps { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let root_bus = self.get_pci_host()?.lock().unwrap().root_bus.clone(); - let msi_irq_manager = &root_bus.lock().unwrap().msi_irq_manager; + let msi_irq_manager = root_bus.lock().unwrap().msi_irq_manager.clone(); let need_irqfd = msi_irq_manager.as_ref().unwrap().irqfd_enable(); self.add_virtio_pci_device(&id_clone, &bdf, device, multi_func, need_irqfd) .with_context(|| "Failed to add pci fs device")?; -- Gitee From ce70a605bffb32f163e082715624e8fc1c60914f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 23:37:42 +0800 Subject: [PATCH 1764/2187] machine: report device type when adding device failed Report device type when adding device failed. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7b4b7a01d..587dbc063 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -283,13 +283,13 @@ macro_rules! create_device_add_matches { match $command { $( $($driver_name)|+ => { - $controller.$function_name($($arg),*)?; + $controller.$function_name($($arg),*).with_context(|| format!("add {} fail.", $command))?; }, )* $( #[cfg($($features)*)] $driver_name1 => { - $controller.$function_name1($($arg1),*)?; + $controller.$function_name1($($arg1),*).with_context(|| format!("add {} fail.", $command))?; }, )* _ => bail!("Unsupported device: {:?}", $command), -- Gitee From 1f75217917a0dd4982dafc838edc98c1d6e716ee Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 23:44:57 +0800 Subject: [PATCH 1765/2187] machine_manager: refactor str_slip_to_clap to support different types of first parameters The first parameter will be parsed as the `binary name` unless Command::no_binary_name is used when using `clap`. Stratovirt command line may use the first parameter as class type. Eg: 1. drive config: "-drive file=,if=pflash,unit=0" This cmdline has no class type. 2. device config: "-device virtio-balloon-pci,id=,bus=,addr=<0x4>" This cmdline sets device type `virtio-balloon-pci` as the first parameter. Use first_pos_is_type to indicate whether the first parameter is a type class which needs a separate analysis. Eg: 1. drive config: "-drive file=,if=pflash,unit=0" Set first_pos_is_type false for this cmdline has no class type. 2. device config: "-device virtio-balloon-pci,id=,bus=,addr=<0x4>" Set first_pos_is_type true for this cmdline has device type "virtio-balloon-pci" as the first parameter. Use first_pos_is_subcommand to indicate whether the first parameter is a subclass. Eg: Chardev has stdio/unix-socket/tcp-socket/pty/file classes. These classes have different configurations but will be stored in the same `ChardevConfig` structure by using `enum`. So, we will use class type as a subcommand to indicate which subtype will be used to store the configuration in enumeration type. Subcommand in `clap` doesn't need `--` in parameter. 1. -serial file,path= `file` is subcommand for socket. Set first_pos_is_subcommand true for first parameter `file` is the subclass type for chardev. Allow single parameter. Eg: -serial socket,path=,server,nowait `server` and `nowait` are single parameters. Signed-off-by: liuxiangdong --- devices/src/misc/scream/mod.rs | 4 +- devices/src/usb/camera.rs | 4 +- devices/src/usb/keyboard.rs | 4 +- devices/src/usb/tablet.rs | 4 +- devices/src/usb/usbhost/mod.rs | 4 +- devices/src/usb/xhci/xhci_pci.rs | 4 +- machine/src/lib.rs | 20 ++++++---- machine_manager/src/config/camera.rs | 7 +++- machine_manager/src/config/mod.rs | 58 ++++++++++++++++++++++++---- virtio/src/device/balloon.rs | 4 +- 10 files changed, 88 insertions(+), 25 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 13659f4b1..ed0890111 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -402,8 +402,10 @@ impl FromStr for ScreamInterface { } #[derive(Parser, Debug, Clone)] -#[command(name = "ivshmem_scream")] +#[command(no_binary_name(true))] pub struct ScreamConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] id: String, #[arg(long)] diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index cedf8cdc4..f687d455c 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -90,8 +90,10 @@ const FRAME_SIZE_1280_720: u32 = 1280 * 720 * 2; const USB_CAMERA_BUFFER_LEN: usize = 12 * 1024; #[derive(Parser, Debug, Clone)] -#[command(name = "usb_camera")] +#[command(no_binary_name(true))] pub struct UsbCameraConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] pub id: String, #[arg(long)] diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 9ad3865d8..716856038 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -121,8 +121,10 @@ const DESC_STRINGS: [&str; 5] = [ ]; #[derive(Parser, Clone, Debug, Default)] -#[command(name = "usb_keyboard")] +#[command(no_binary_name(true))] pub struct UsbKeyboardConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] id: String, #[arg(long)] diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index af3ab2973..fffb4df41 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -114,8 +114,10 @@ const STR_SERIAL_TABLET_INDEX: u8 = 4; const DESC_STRINGS: [&str; 5] = ["", "StratoVirt", "StratoVirt USB Tablet", "HID Tablet", "2"]; #[derive(Parser, Clone, Debug, Default)] -#[command(name = "usb_tablet")] +#[command(no_binary_name(true))] pub struct UsbTabletConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] id: String, #[arg(long)] diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 5abdb292c..40d7c8af9 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -357,8 +357,10 @@ impl IsoQueue { } #[derive(Parser, Clone, Debug, Default)] -#[command(name = "usb_host")] +#[command(no_binary_name(true))] pub struct UsbHostConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] id: String, #[arg(long, default_value = "0")] diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 0b3373cd2..09c24f46f 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -67,8 +67,10 @@ const XHCI_MSIX_PBA_OFFSET: u32 = 0x3800; /// XHCI controller configuration. #[derive(Parser, Clone, Debug, Default)] -#[command(name = "nec-usb-xhci")] +#[command(no_binary_name(true))] pub struct XhciConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] id: Option, #[arg(long)] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 587dbc063..249a3f5f9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -666,13 +666,13 @@ pub trait MachineOps { if vm_config.dev_name.get("balloon").is_some() { bail!("Only one balloon device is supported for each vm."); } - let config = BalloonConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let config = BalloonConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; vm_config.dev_name.insert("balloon".to_string(), 1); let sys_mem = self.get_sys_mem(); let balloon = Arc::new(Mutex::new(Balloon::new(config.clone(), sys_mem.clone()))); Balloon::object_init(balloon.clone()); - match parse_device_type(cfg_args)?.as_str() { + match config.classtype.as_str() { "virtio-balloon-device" => { if config.addr.is_some() || config.bus.is_some() || config.multifunction.is_some() { bail!("virtio balloon device config is error!"); @@ -1545,7 +1545,7 @@ pub trait MachineOps { /// /// * `cfg_args` - XHCI Configuration. fn add_usb_xhci(&mut self, cfg_args: &str) -> Result<()> { - let device_cfg = XhciConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let device_cfg = XhciConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let bdf = PciBdf::new(device_cfg.bus.clone(), device_cfg.addr); let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; @@ -1569,7 +1569,7 @@ pub trait MachineOps { cfg_args: &str, token_id: Option>>, ) -> Result<()> { - let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let bdf = PciBdf { bus: config.bus.clone(), addr: config.addr, @@ -1692,14 +1692,16 @@ pub trait MachineOps { fn add_usb_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let usb_device = match parse_device_type(cfg_args)?.as_str() { "usb-kbd" => { - let config = UsbKeyboardConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let config = + UsbKeyboardConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let keyboard = UsbKeyboard::new(config); keyboard .realize() .with_context(|| "Failed to realize usb keyboard device")? } "usb-tablet" => { - let config = UsbTabletConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let config = + UsbTabletConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let tablet = UsbTablet::new(config); tablet .realize() @@ -1707,7 +1709,8 @@ pub trait MachineOps { } #[cfg(feature = "usb_camera")] "usb-camera" => { - let config = UsbCameraConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let config = + UsbCameraConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let cameradev = get_cameradev_by_id(vm_config, config.cameradev.clone()) .with_context(|| { format!( @@ -1736,7 +1739,8 @@ pub trait MachineOps { } #[cfg(feature = "usb_host")] "usb-host" => { - let config = UsbHostConfig::try_parse_from(str_slip_to_clap(cfg_args))?; + let config = + UsbHostConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let usbhost = UsbHost::new(config)?; usbhost .realize() diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index a5ed0702a..43af312c3 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -22,8 +22,10 @@ use crate::{ }; #[derive(Parser, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[command(name = "camera device")] +#[command(no_binary_name(true))] pub struct CameraDevConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] pub id: String, #[arg(long)] @@ -59,7 +61,7 @@ impl FromStr for CamBackendType { impl VmConfig { pub fn add_camera_backend(&mut self, camera_config: &str) -> Result<()> { let cfg = format!("cameradev,backend={}", camera_config); - let config = CameraDevConfig::try_parse_from(str_slip_to_clap(&cfg))?; + let config = CameraDevConfig::try_parse_from(str_slip_to_clap(&cfg, true, false))?; self.add_cameradev_with_config(config) } @@ -103,6 +105,7 @@ impl VmConfig { pub fn get_cameradev_config(args: qmp_schema::CameraDevAddArgument) -> Result { let path = args.path.with_context(|| "cameradev config path is null")?; let config = CameraDevConfig { + classtype: "cameradev".to_string(), id: args.id, path, backend: CamBackendType::from_str(&args.driver)?, diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 8b2944d8c..985c1b78f 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -737,14 +737,56 @@ pub fn check_arg_nonexist(arg: Option, name: &str, device: &str) -> Resu } /// Configure StratoVirt parameters in clap format. -pub fn str_slip_to_clap(args: &str) -> Vec { - let args_vecs = args.split([',', '=']).collect::>(); - let mut itr: Vec = Vec::with_capacity(args_vecs.len()); - for (cnt, param) in args_vecs.iter().enumerate() { - if cnt % 2 == 1 { - itr.push(format!("--{}", param)); - } else { - itr.push(param.to_string()); +/// +/// The first parameter will be parsed as the `binary name` unless Command::no_binary_name is used when using `clap`. +/// Stratovirt command line may use the first parameter as class type. +/// Eg: +/// 1. drive config: "-drive file=,if=pflash,unit=0" +/// This cmdline has no class type. +/// 2. device config: "-device virtio-balloon-pci,id=,bus=,addr=<0x4>" +/// This cmdline sets device type `virtio-balloon-pci` as the first parameter. +/// +/// Use first_pos_is_type to indicate whether the first parameter is a type class which needs a separate analysis. +/// Eg: +/// 1. drive config: "-drive file=,if=pflash,unit=0" +/// Set first_pos_is_type false for this cmdline has no class type. +/// 2. device config: "-device virtio-balloon-pci,id=,bus=,addr=<0x4>" +/// Set first_pos_is_type true for this cmdline has device type "virtio-balloon-pci" as the first parameter. +/// +/// Use first_pos_is_subcommand to indicate whether the first parameter is a subclass. +/// Eg: +/// Chardev has stdio/unix-socket/tcp-socket/pty/file classes. These classes have different configurations but will be stored +/// in the same `ChardevConfig` structure by using `enum`. So, we will use class type as a subcommand to indicate which subtype +/// will be used to store the configuration in enumeration type. Subcommand in `clap` doesn't need `--` in parameter. +/// 1. -serial file,path= +/// Set first_pos_is_subcommand true for first parameter `file` is the subclass type for chardev. +pub fn str_slip_to_clap( + args: &str, + first_pos_is_type: bool, + mut first_pos_is_subcommand: bool, +) -> Vec { + let args_str = if first_pos_is_type && !first_pos_is_subcommand { + format!("classtype={}", args) + } else { + args.to_string() + }; + let args_vecs = args_str.split([',']).collect::>(); + let mut itr: Vec = Vec::with_capacity(args_vecs.len() * 2); + for params in args_vecs { + let key_value = params.split(['=']).collect::>(); + // Command line like "key=value" will be converted to "--key value". + // Command line like "key" will be converted to "--key". + for (cnt, param) in key_value.iter().enumerate() { + if cnt % 2 == 0 { + if first_pos_is_subcommand { + itr.push(param.to_string()); + first_pos_is_subcommand = false; + } else { + itr.push(format!("--{}", param)); + } + } else { + itr.push(param.to_string()); + } } } itr diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index fd6347a28..6e6095f9c 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -882,8 +882,10 @@ impl EventNotifierHelper for BalloonIoHandler { } #[derive(Parser, Debug, Clone, Default)] -#[command(name = "balloon")] +#[command(no_binary_name(true))] pub struct BalloonConfig { + #[arg(long)] + pub classtype: String, #[arg(long, value_parser = valid_id)] pub id: String, #[arg(long)] -- Gitee From 51537a157453961e61fa76ac144feadb0313b289 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 22 Feb 2024 23:50:26 +0800 Subject: [PATCH 1766/2187] drive: use clap to parse the parameters of the drive config Use clap to parse the parameters of the drive config, including pflash drive and block drive. Signed-off-by: liuxiangdong --- machine/src/aarch64/standard.rs | 8 +- machine/src/lib.rs | 6 +- machine/src/standard_common/mod.rs | 15 +- machine/src/x86_64/standard.rs | 10 +- machine_manager/src/config/drive.rs | 533 +++++++++++++--------------- machine_manager/src/config/mod.rs | 10 +- machine_manager/src/config/scsi.rs | 2 +- machine_manager/src/config/usb.rs | 2 +- util/src/aio/mod.rs | 7 +- 9 files changed, 272 insertions(+), 321 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 8d38c4440..56a3a3a21 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -61,7 +61,7 @@ use machine_manager::config::ShutdownAction; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, MigrateMode, NumaNode, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, DriveConfig, MigrateMode, NumaNode, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -685,16 +685,16 @@ impl MachineOps for StdMachine { Ok(()) } - fn add_pflash_device(&mut self, configs: &[PFlashConfig]) -> Result<()> { + fn add_pflash_device(&mut self, configs: &[DriveConfig]) -> Result<()> { let mut configs_vec = configs.to_vec(); - configs_vec.sort_by_key(|c| c.unit); + configs_vec.sort_by_key(|c| c.unit.unwrap()); let sector_len: u32 = 1024 * 256; let mut flash_base: u64 = MEM_LAYOUT[LayoutEntryType::Flash as usize].0; let flash_size: u64 = MEM_LAYOUT[LayoutEntryType::Flash as usize].1 / 2; for i in 0..=1 { let (fd, read_only) = if i < configs_vec.len() { let path = &configs_vec[i].path_on_host; - let read_only = configs_vec[i].read_only; + let read_only = configs_vec[i].readonly; let fd = self.fetch_drive_file(path)?; (Some(fd), read_only) } else { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 249a3f5f9..fb0256d26 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -83,8 +83,8 @@ use machine_manager::config::{ parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, - BootSource, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, - NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, + BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, + NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; @@ -1828,7 +1828,7 @@ pub trait MachineOps { None } - fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> { + fn add_pflash_device(&mut self, _configs: &[DriveConfig]) -> Result<()> { bail!("Pflash device is not supported!"); } diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 1ab0be07c..67ab3e228 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1272,7 +1272,7 @@ impl DeviceInterface for StdMachine { if let Err(e) = self.register_drive_file( &config.id, &args.file.filename, - config.read_only, + config.readonly, config.direct, ) { error!("{:?}", e); @@ -1672,12 +1672,7 @@ impl DeviceInterface for StdMachine { None, ); } - let drive_cfg = match self - .get_vm_config() - .lock() - .unwrap() - .add_block_drive(cmd_args[2]) - { + let drive_cfg = match self.get_vm_config().lock().unwrap().add_drive(cmd_args[2]) { Ok(cfg) => cfg, Err(ref e) => { return Response::create_error_response( @@ -1689,7 +1684,7 @@ impl DeviceInterface for StdMachine { if let Err(e) = self.register_drive_file( &drive_cfg.id, &drive_cfg.path_on_host, - drive_cfg.read_only, + drive_cfg.readonly, drive_cfg.direct, ) { error!("{:?}", e); @@ -1915,8 +1910,10 @@ impl DeviceInterface for StdMachine { fn parse_blockdev(args: &BlockDevAddArgument) -> Result { let mut config = DriveConfig { id: args.node_name.clone(), + drive_type: "none".to_string(), + unit: None, path_on_host: args.file.filename.clone(), - read_only: args.read_only.unwrap_or(false), + readonly: args.read_only.unwrap_or(false), direct: true, iops: args.iops, aio: args.file.aio, diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 6499151bb..fdb88d1ef 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -45,7 +45,7 @@ use hypervisor::kvm::*; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, MigrateMode, NumaNode, PFlashConfig, SerialConfig, VmConfig, + parse_incoming_uri, BootIndexInfo, DriveConfig, MigrateMode, NumaNode, SerialConfig, VmConfig, }; use machine_manager::event; use machine_manager::machine::{ @@ -628,9 +628,9 @@ impl MachineOps for StdMachine { Ok(()) } - fn add_pflash_device(&mut self, configs: &[PFlashConfig]) -> Result<()> { + fn add_pflash_device(&mut self, configs: &[DriveConfig]) -> Result<()> { let mut configs_vec = configs.to_vec(); - configs_vec.sort_by_key(|c| c.unit); + configs_vec.sort_by_key(|c| c.unit.unwrap()); // The two PFlash devices locates below 4GB, this variable represents the end address // of current PFlash device. let mut flash_end: u64 = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; @@ -638,7 +638,7 @@ impl MachineOps for StdMachine { let mut fd = self.fetch_drive_file(&config.path_on_host)?; let pfl_size = fd.metadata().unwrap().len(); - if config.unit == 0 { + if config.unit.unwrap() == 0 { // According to the Linux/x86 boot protocol, the memory region of // 0x000000 - 0x100000 (1 MiB) is for BIOS usage. And the top 128 // KiB is for BIOS code which is stored in the first PFlash. @@ -674,7 +674,7 @@ impl MachineOps for StdMachine { sector_len, 4_u32, 1_u32, - config.read_only, + config.readonly, ) .with_context(|| MachineError::InitPflashErr)?; PFlash::realize( diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index e88826a53..3e787c0ca 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -16,13 +16,16 @@ use std::path::Path; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; +use clap::{ArgAction, Parser}; use log::error; use serde::{Deserialize, Serialize}; +use super::valid_id; use super::{error::ConfigError, pci_args_check, M}; use crate::config::{ - check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, CmdParser, ConfigCheck, - ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, parse_bool, + str_slip_to_clap, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, + MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, }; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; @@ -115,8 +118,9 @@ impl Default for BlkDevConfig { } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum DiskFormat { + #[default] Raw, Qcow2, } @@ -142,44 +146,88 @@ impl ToString for DiskFormat { } } -/// Config struct for `drive`. -/// Contains block device's attr. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] +fn parse_size(s: &str) -> Result { + let size = memory_unit_conversion(s, M).with_context(|| format!("Invalid size: {}", s))?; + Ok(size) +} + +fn valid_l2_cache_size(s: &str) -> Result { + let size = parse_size(s)?; + if size > MAX_L2_CACHE_SIZE { + return Err(anyhow!(ConfigError::IllegalValue( + "l2-cache-size".to_string(), + 0, + true, + MAX_L2_CACHE_SIZE, + true + ))); + } + Ok(size) +} + +fn valid_refcount_cache_size(s: &str) -> Result { + let size = parse_size(s)?; + if size > MAX_REFTABLE_CACHE_SIZE { + return Err(anyhow!(ConfigError::IllegalValue( + "refcount-cache-size".to_string(), + 0, + true, + MAX_REFTABLE_CACHE_SIZE, + true + ))); + } + Ok(size) +} + +fn valid_path(path: &str) -> Result { + if path.len() > MAX_PATH_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "Drive device path".to_string(), + MAX_PATH_LENGTH, + ))); + } + Ok(path.to_string()) +} + +/// Config struct for `drive`, including `block drive` and `pflash drive`. +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct DriveConfig { + #[arg(long, default_value = "")] pub id: String, + #[arg(long, alias = "if", default_value = "none", value_parser = ["none", "pflash"])] + pub drive_type: String, + #[arg(long, value_parser = clap::value_parser!(u8).range(..MAX_UNIT_ID as i64))] + pub unit: Option, + #[arg(long, alias = "file", value_parser = valid_path)] pub path_on_host: String, - pub read_only: bool, + #[arg(long, default_value = "off", value_parser = parse_bool, action = ArgAction::Append)] + pub readonly: bool, + #[arg(long, default_value = "true", value_parser = parse_bool, action = ArgAction::Append)] pub direct: bool, + #[arg(long, alias = "throttling.iops-total", value_parser = clap::value_parser!(u64).range(..=MAX_IOPS as u64))] pub iops: Option, + #[arg( + long, + default_value = "native", + default_value_if("direct", "false", "off"), + default_value_if("direct", "off", "off") + )] pub aio: AioEngine, + #[arg(long, default_value = "disk", value_parser = ["disk", "cdrom"])] pub media: String, + #[arg(long, default_value = "ignore", value_parser = parse_bool, action = ArgAction::Append)] pub discard: bool, + #[arg(long, alias = "detect-zeroes", default_value = "off")] pub write_zeroes: WriteZeroesState, + #[arg(long, default_value = "raw")] pub format: DiskFormat, + #[arg(long, value_parser = valid_l2_cache_size)] pub l2_cache_size: Option, + #[arg(long, value_parser = valid_refcount_cache_size)] pub refcount_cache_size: Option, } -impl Default for DriveConfig { - fn default() -> Self { - DriveConfig { - id: "".to_string(), - path_on_host: "".to_string(), - read_only: false, - direct: true, - iops: None, - aio: AioEngine::Native, - media: "disk".to_string(), - discard: false, - write_zeroes: WriteZeroesState::Off, - format: DiskFormat::Raw, - l2_cache_size: None, - refcount_cache_size: None, - } - } -} - impl DriveConfig { /// Check whether the drive file path on the host is valid. pub fn check_path(&self) -> Result<()> { @@ -222,63 +270,68 @@ impl DriveConfig { impl ConfigCheck for DriveConfig { fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "Drive id")?; + if self.drive_type == "pflash" { + self.unit.with_context(|| { + ConfigError::FieldIsMissing("unit".to_string(), "pflash".to_string()) + })?; + if self.format.to_string() != "raw" { + bail!("Only \'raw\' type of pflash is supported"); + } + } else { + if self.id.is_empty() { + return Err(anyhow!(ConfigError::FieldIsMissing( + "id".to_string(), + "blk".to_string() + ))); + } + valid_id(&self.id)?; + valid_path(&self.path_on_host)?; + if self.iops > Some(MAX_IOPS) { + return Err(anyhow!(ConfigError::IllegalValue( + "iops of block device".to_string(), + 0, + true, + MAX_IOPS, + true, + ))); + } + if self.l2_cache_size > Some(MAX_L2_CACHE_SIZE) { + return Err(anyhow!(ConfigError::IllegalValue( + "l2-cache-size".to_string(), + 0, + true, + MAX_L2_CACHE_SIZE, + true + ))); + } + if self.refcount_cache_size > Some(MAX_REFTABLE_CACHE_SIZE) { + return Err(anyhow!(ConfigError::IllegalValue( + "refcount-cache-size".to_string(), + 0, + true, + MAX_REFTABLE_CACHE_SIZE, + true + ))); + } - if self.path_on_host.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "Drive device path".to_string(), - MAX_PATH_LENGTH, - ))); - } - if self.iops > Some(MAX_IOPS) { - return Err(anyhow!(ConfigError::IllegalValue( - "iops of block device".to_string(), - 0, - true, - MAX_IOPS, - true, - ))); - } - if self.aio != AioEngine::Off { - if self.aio == AioEngine::Native && !self.direct { + if self.aio != AioEngine::Off { + if self.aio == AioEngine::Native && !self.direct { + return Err(anyhow!(ConfigError::InvalidParam( + "aio".to_string(), + "native aio type should be used with \"direct\" on".to_string(), + ))); + } + aio_probe(self.aio)?; + } else if self.direct { return Err(anyhow!(ConfigError::InvalidParam( "aio".to_string(), - "native aio type should be used with \"direct\" on".to_string(), + "low performance expected when use sync io with \"direct\" on".to_string(), ))); } - aio_probe(self.aio)?; - } else if self.direct { - return Err(anyhow!(ConfigError::InvalidParam( - "aio".to_string(), - "low performance expected when use sync io with \"direct\" on".to_string(), - ))); - } - - if !["disk", "cdrom"].contains(&self.media.as_str()) { - return Err(anyhow!(ConfigError::InvalidParam( - "media".to_string(), - "media should be \"disk\" or \"cdrom\"".to_string(), - ))); } - if self.l2_cache_size > Some(MAX_L2_CACHE_SIZE) { - return Err(anyhow!(ConfigError::IllegalValue( - "l2-cache-size".to_string(), - 0, - true, - MAX_L2_CACHE_SIZE, - true - ))); - } - if self.refcount_cache_size > Some(MAX_REFTABLE_CACHE_SIZE) { - return Err(anyhow!(ConfigError::IllegalValue( - "refcount-cache-size".to_string(), - 0, - true, - MAX_REFTABLE_CACHE_SIZE, - true - ))); - } + #[cfg(not(test))] + self.check_path()?; Ok(()) } @@ -326,10 +379,12 @@ impl ConfigCheck for BlkDevConfig { } let fake_drive = DriveConfig { + id: self.id.clone(), path_on_host: self.path_on_host.clone(), direct: self.direct, iops: self.iops, aio: self.aio, + media: "disk".to_string(), ..Default::default() }; fake_drive.check()?; @@ -342,60 +397,6 @@ impl ConfigCheck for BlkDevConfig { } } -fn parse_drive(cmd_parser: CmdParser) -> Result { - let mut drive = DriveConfig::default(); - if let Some(fmt) = cmd_parser.get_value::("format")? { - drive.format = fmt; - } - - drive.id = cmd_parser - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "blk".to_string()))?; - drive.path_on_host = cmd_parser - .get_value::("file")? - .with_context(|| ConfigError::FieldIsMissing("file".to_string(), "blk".to_string()))?; - - if let Some(read_only) = cmd_parser.get_value::("readonly")? { - drive.read_only = read_only.into(); - } - if let Some(direct) = cmd_parser.get_value::("direct")? { - drive.direct = direct.into(); - } - drive.iops = cmd_parser.get_value::("throttling.iops-total")?; - drive.aio = cmd_parser.get_value::("aio")?.unwrap_or({ - if drive.direct { - AioEngine::Native - } else { - AioEngine::Off - } - }); - drive.media = cmd_parser - .get_value::("media")? - .unwrap_or_else(|| "disk".to_string()); - if let Some(discard) = cmd_parser.get_value::("discard")? { - drive.discard = discard.into(); - } - drive.write_zeroes = cmd_parser - .get_value::("detect-zeroes")? - .unwrap_or(WriteZeroesState::Off); - - if let Some(l2_cache) = cmd_parser.get_value::("l2-cache-size")? { - let sz = memory_unit_conversion(&l2_cache, M) - .with_context(|| format!("Invalid l2 cache size: {}", l2_cache))?; - drive.l2_cache_size = Some(sz); - } - if let Some(rc_cache) = cmd_parser.get_value::("refcount-cache-size")? { - let sz = memory_unit_conversion(&rc_cache, M) - .with_context(|| format!("Invalid refcount cache size: {}", rc_cache))?; - drive.refcount_cache_size = Some(sz); - } - - drive.check()?; - #[cfg(not(test))] - drive.check_path()?; - Ok(drive) -} - pub fn parse_blk( vm_config: &mut VmConfig, drive_config: &str, @@ -455,7 +456,7 @@ pub fn parse_blk( .remove(&blkdrive) .with_context(|| "No drive configured matched for blk device")?; blkdevcfg.path_on_host = drive_arg.path_on_host.clone(); - blkdevcfg.read_only = drive_arg.read_only; + blkdevcfg.read_only = drive_arg.readonly; blkdevcfg.direct = drive_arg.direct; blkdevcfg.iops = drive_arg.iops; blkdevcfg.aio = drive_arg.aio; @@ -522,83 +523,23 @@ pub fn parse_vhost_user_blk( Ok(blkdevcfg) } -/// Config struct for `pflash`. -/// Contains pflash device's attr. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct PFlashConfig { - pub path_on_host: String, - pub read_only: bool, - pub unit: usize, -} - -impl ConfigCheck for PFlashConfig { - fn check(&self) -> Result<()> { - if self.path_on_host.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "drive device path".to_string(), - MAX_PATH_LENGTH, - ))); - } - - if self.unit >= MAX_UNIT_ID { - return Err(anyhow!(ConfigError::UnitIdError( - "PFlash unit id".to_string(), - self.unit, - MAX_UNIT_ID - 1 - ))); - } - Ok(()) - } -} - impl VmConfig { - /// Add '-drive ...' drive config to `VmConfig`. - pub fn add_drive(&mut self, drive_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("drive"); - cmd_parser.push("if"); - - cmd_parser.get_parameters(drive_config)?; - let drive_type = cmd_parser - .get_value::("if")? - .unwrap_or_else(|| "none".to_string()); - match drive_type.as_str() { + /// Add '-drive ...' drive config to `VmConfig`, including `block drive` and `pflash drive`. + pub fn add_drive(&mut self, drive_config: &str) -> Result { + let drive_cfg = DriveConfig::try_parse_from(str_slip_to_clap(drive_config, false, false))?; + drive_cfg.check()?; + match drive_cfg.drive_type.as_str() { "none" => { - self.add_block_drive(drive_config)?; + self.add_drive_with_config(drive_cfg.clone())?; } "pflash" => { - self.add_pflash(drive_config)?; + self.add_flashdev(drive_cfg.clone())?; } _ => { - bail!("Unknow 'if' argument: {:?}", drive_type.as_str()); + bail!("Unknow 'if' argument: {:?}", &drive_cfg.drive_type); } } - Ok(()) - } - - /// Add block drive config to vm and return the added drive config. - pub fn add_block_drive(&mut self, block_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("drive"); - cmd_parser - .push("file") - .push("id") - .push("readonly") - .push("direct") - .push("format") - .push("if") - .push("throttling.iops-total") - .push("aio") - .push("media") - .push("discard") - .push("detect-zeroes") - .push("format") - .push("l2-cache-size") - .push("refcount-cache-size"); - - cmd_parser.parse(block_config)?; - let drive_cfg = parse_drive(cmd_parser)?; - self.add_drive_with_config(drive_cfg.clone())?; Ok(drive_cfg) } @@ -609,11 +550,10 @@ impl VmConfig { /// * `drive_conf` - The drive config to be added to the vm. pub fn add_drive_with_config(&mut self, drive_conf: DriveConfig) -> Result<()> { let drive_id = drive_conf.id.clone(); - if self.drives.get(&drive_id).is_none() { - self.drives.insert(drive_id, drive_conf); - } else { + if self.drives.get(&drive_id).is_some() { bail!("Drive {} has been added", drive_id); } + self.drives.insert(drive_id, drive_conf); Ok(()) } @@ -631,13 +571,13 @@ impl VmConfig { } /// Add new flash device to `VmConfig`. - fn add_flashdev(&mut self, pflash: PFlashConfig) -> Result<()> { + fn add_flashdev(&mut self, pflash: DriveConfig) -> Result<()> { if self.pflashs.is_some() { for pf in self.pflashs.as_ref().unwrap() { - if pf.unit == pflash.unit { + if pf.unit.unwrap() == pflash.unit.unwrap() { return Err(anyhow!(ConfigError::IdRepeat( "pflash".to_string(), - pf.unit.to_string() + pf.unit.unwrap().to_string() ))); } } @@ -647,41 +587,6 @@ impl VmConfig { } Ok(()) } - - /// Add '-pflash ...' pflash config to `VmConfig`. - pub fn add_pflash(&mut self, pflash_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("pflash"); - cmd_parser - .push("if") - .push("file") - .push("format") - .push("readonly") - .push("unit"); - - cmd_parser.parse(pflash_config)?; - - let mut pflash = PFlashConfig::default(); - - if let Some(format) = cmd_parser.get_value::("format")? { - if format.ne("raw") { - bail!("Only \'raw\' type of pflash is supported"); - } - } - pflash.path_on_host = cmd_parser.get_value::("file")?.with_context(|| { - ConfigError::FieldIsMissing("file".to_string(), "pflash".to_string()) - })?; - - if let Some(read_only) = cmd_parser.get_value::("readonly")? { - pflash.read_only = read_only.into(); - } - - pflash.unit = cmd_parser.get_value::("unit")?.with_context(|| { - ConfigError::FieldIsMissing("unit".to_string(), "pflash".to_string()) - })? as usize; - - pflash.check()?; - self.add_flashdev(pflash) - } } #[cfg(test)] @@ -760,34 +665,31 @@ mod tests { } #[test] - fn test_pflash_config_cmdline_parser() { + fn test_pflash_drive_config_cmdline_parser() { + // Test1: Right. let mut vm_config = VmConfig::default(); assert!(vm_config - .add_drive("if=pflash,readonly=on,file=flash0.fd,unit=0") + .add_drive("if=pflash,readonly=on,file=flash0.fd,unit=0,format=raw") .is_ok()); assert!(vm_config.pflashs.is_some()); let pflash = vm_config.pflashs.unwrap(); assert!(pflash.len() == 1); let pflash_cfg = &pflash[0]; - assert_eq!(pflash_cfg.unit, 0); + assert_eq!(pflash_cfg.unit.unwrap(), 0); assert_eq!(pflash_cfg.path_on_host, "flash0.fd".to_string()); - assert_eq!(pflash_cfg.read_only, true); + assert_eq!(pflash_cfg.readonly, true); + // Test2: Change parameters sequence. let mut vm_config = VmConfig::default(); assert!(vm_config .add_drive("readonly=on,file=flash0.fd,unit=0,if=pflash") .is_ok()); - let mut vm_config = VmConfig::default(); assert!(vm_config .add_drive("readonly=on,if=pflash,file=flash0.fd,unit=0") .is_ok()); - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_drive("if=pflash,readonly=on,file=flash0.fd,unit=2") - .is_err()); - + // Test3: Add duplicate pflash. let mut vm_config = VmConfig::default(); assert!(vm_config .add_drive("if=pflash,readonly=on,file=flash0.fd,unit=0") @@ -795,52 +697,103 @@ mod tests { assert!(vm_config .add_drive("if=pflash,file=flash1.fd,unit=1") .is_ok()); + assert!(vm_config + .add_drive("if=pflash,file=flash1.fd,unit=1") + .is_err()); assert!(vm_config.pflashs.is_some()); let pflash = vm_config.pflashs.unwrap(); assert!(pflash.len() == 2); let pflash_cfg = &pflash[0]; - assert_eq!(pflash_cfg.unit, 0); + assert_eq!(pflash_cfg.unit.unwrap(), 0); assert_eq!(pflash_cfg.path_on_host, "flash0.fd".to_string()); - assert_eq!(pflash_cfg.read_only, true); + assert_eq!(pflash_cfg.readonly, true); let pflash_cfg = &pflash[1]; - assert_eq!(pflash_cfg.unit, 1); + assert_eq!(pflash_cfg.unit.unwrap(), 1); assert_eq!(pflash_cfg.path_on_host, "flash1.fd".to_string()); - assert_eq!(pflash_cfg.read_only, false); - } + assert_eq!(pflash_cfg.readonly, false); - #[test] - fn test_drive_config_check() { - let mut drive_conf = DriveConfig::default(); - for _ in 0..MAX_STRING_LENGTH { - drive_conf.id += "A"; - } - assert!(drive_conf.check().is_ok()); + // Test4: Illegal parameters unit/format. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_drive("if=pflash,readonly=on,file=flash0.fd,unit=2") + .is_err()); + assert!(vm_config + .add_drive("if=pflash,readonly=on,file=flash0.fd,unit=0,format=qcow2") + .is_err()); - // Overflow - drive_conf.id += "A"; - assert!(drive_conf.check().is_err()); + // Test5: Missing parameters file/unit. + let mut vm_config = VmConfig::default(); + assert!(vm_config.add_drive("if=pflash,readonly=on,unit=2").is_err()); + assert!(vm_config + .add_drive("if=pflash,readonly=on,file=flash0.fd") + .is_err()); + } - let mut drive_conf = DriveConfig::default(); - for _ in 0..MAX_PATH_LENGTH { - drive_conf.path_on_host += "A"; - } - assert!(drive_conf.check().is_ok()); + #[test] + fn test_block_drive_config_cmdline_parser() { + // Test1: Right. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=qcow2,readonly=off,direct=on,throttling.iops-total=200,discard=unmap,detect-zeroes=unmap") + .is_ok()); + assert!(vm_config.drives.len() == 1); + let drive_cfg = &vm_config.drives.remove("rootfs").unwrap(); + + assert_eq!(drive_cfg.id, "rootfs"); + assert_eq!(drive_cfg.path_on_host, "/path/to/rootfs"); + assert_eq!(drive_cfg.format.to_string(), "qcow2"); + assert_eq!(drive_cfg.readonly, false); + assert_eq!(drive_cfg.direct, true); + assert_eq!(drive_cfg.iops.unwrap(), 200); + assert_eq!(drive_cfg.discard, true); + assert_eq!( + drive_cfg.write_zeroes, + WriteZeroesState::from_str("unmap").unwrap() + ); - // Overflow - drive_conf.path_on_host += "A"; - assert!(drive_conf.check().is_err()); + // Test2: Change parameters sequence. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_drive("throttling.iops-total=200,file=/path/to/rootfs,format=qcow2,id=rootfs,readonly=off,direct=on,discard=unmap,detect-zeroes=unmap") + .is_ok()); - let mut drive_conf = DriveConfig::default(); - drive_conf.iops = Some(MAX_IOPS); - assert!(drive_conf.check().is_ok()); + // Test3: Add duplicate block drive config. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=qcow2,readonly=off,direct=on") + .is_ok()); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=qcow2,readonly=off,direct=on") + .is_err()); + let drive_cfg = &vm_config.drives.remove("rootfs"); + assert!(drive_cfg.is_some()); - let mut drive_conf = DriveConfig::default(); - drive_conf.iops = None; - assert!(drive_conf.check().is_ok()); + // Test4: Illegal parameters. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=vhdx") + .is_err()); + assert!(vm_config + .add_drive("id=rootfs,if=illegal,file=/path/to/rootfs,format=vhdx") + .is_err()); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=raw,throttling.iops-total=1000001") + .is_err()); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=raw,media=illegal") + .is_err()); + assert!(vm_config + .add_drive("id=rootfs,file=/path/to/rootfs,format=raw,detect-zeroes=illegal") + .is_err()); - // Overflow - drive_conf.iops = Some(MAX_IOPS + 1); - assert!(drive_conf.check().is_err()); + // Test5: Missing parameters id/file. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_drive("file=/path/to/rootfs,format=qcow2,readonly=off,direct=on,throttling.iops-total=200") + .is_err()); + assert!(vm_config + .add_drive("id=rootfs,format=qcow2,readonly=off,direct=on,throttling.iops-total=200") + .is_err()); } #[test] @@ -888,19 +841,19 @@ mod tests { fn test_drive_config_discard() { let mut vm_config = VmConfig::default(); let drive_conf = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,discard=ignore") + .add_drive("id=rootfs,file=/path/to/rootfs,discard=ignore") .unwrap(); assert_eq!(drive_conf.discard, false); let mut vm_config = VmConfig::default(); let drive_conf = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,discard=unmap") + .add_drive("id=rootfs,file=/path/to/rootfs,discard=unmap") .unwrap(); assert_eq!(drive_conf.discard, true); let mut vm_config = VmConfig::default(); let ret = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,discard=invalid") + .add_drive("id=rootfs,file=/path/to/rootfs,discard=invalid") .is_err(); assert_eq!(ret, true); } @@ -909,25 +862,25 @@ mod tests { fn test_drive_config_write_zeroes() { let mut vm_config = VmConfig::default(); let drive_conf = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=off") + .add_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=off") .unwrap(); assert_eq!(drive_conf.write_zeroes, WriteZeroesState::Off); let mut vm_config = VmConfig::default(); let drive_conf = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=on") + .add_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=on") .unwrap(); assert_eq!(drive_conf.write_zeroes, WriteZeroesState::On); let mut vm_config = VmConfig::default(); let drive_conf = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=unmap") + .add_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=unmap") .unwrap(); assert_eq!(drive_conf.write_zeroes, WriteZeroesState::Unmap); let mut vm_config = VmConfig::default(); let ret = vm_config - .add_block_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=invalid") + .add_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=invalid") .is_err(); assert_eq!(ret, true); } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 985c1b78f..8f4d72e75 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -139,7 +139,7 @@ pub struct VmConfig { pub serial: Option, pub iothreads: Option>, pub object: ObjectConfig, - pub pflashs: Option>, + pub pflashs: Option>, pub dev_name: HashMap, pub global_config: HashMap, pub numa_nodes: Vec<(String, String)>, @@ -395,7 +395,7 @@ impl VmConfig { &mut drive_files, &drive.id, &drive.path_on_host, - drive.read_only, + drive.readonly, drive.direct, )?; } @@ -405,7 +405,7 @@ impl VmConfig { &mut drive_files, "", &pflash.path_on_host, - pflash.read_only, + pflash.readonly, false, )?; } @@ -619,8 +619,8 @@ impl From for bool { pub fn parse_bool(s: &str) -> Result { match s { - "on" => Ok(true), - "off" => Ok(false), + "true" | "on" | "yes" | "unmap" => Ok(true), + "false" | "off" | "no" | "ignore" => Ok(false), _ => Err(anyhow!("Unknow bool value {s}")), } } diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index b73833bcd..d2fb48f71 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -268,7 +268,7 @@ pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result .remove(&scsi_drive) .with_context(|| "No drive configured matched for scsi device")?; scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); - scsi_dev_cfg.read_only = drive_arg.read_only; + scsi_dev_cfg.read_only = drive_arg.readonly; scsi_dev_cfg.direct = drive_arg.direct; scsi_dev_cfg.aio_type = drive_arg.aio; scsi_dev_cfg.format = drive_arg.format; diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs index 1b0e492cc..67917924e 100644 --- a/machine_manager/src/config/usb.rs +++ b/machine_manager/src/config/usb.rs @@ -86,7 +86,7 @@ pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result .remove(&storage_drive) .with_context(|| "No drive configured matched for usb storage device.")?; dev.scsi_cfg.path_on_host = drive_arg.path_on_host.clone(); - dev.scsi_cfg.read_only = drive_arg.read_only; + dev.scsi_cfg.read_only = drive_arg.readonly; dev.scsi_cfg.aio_type = drive_arg.aio; dev.scsi_cfg.direct = drive_arg.direct; dev.scsi_cfg.format = drive_arg.format; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index d8d733da3..0b117b853 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -66,7 +66,7 @@ pub enum AioEngine { } impl FromStr for AioEngine { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { match s { @@ -74,7 +74,7 @@ impl FromStr for AioEngine { AIO_NATIVE => Ok(AioEngine::Native), AIO_IOURING => Ok(AioEngine::IoUring), AIO_THREADS => Ok(AioEngine::Threads), - _ => Err(()), + _ => Err(anyhow!("Unknown aio type")), } } } @@ -90,8 +90,9 @@ impl ToString for AioEngine { } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum WriteZeroesState { + #[default] Off, On, Unmap, -- Gitee From 088e6d0b5516c8f1da1f4d1942ac94237d499cad Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 23 Feb 2024 10:50:25 +0800 Subject: [PATCH 1767/2187] scsi: use clap to parse the parameters of the scsi-device/usb-storage config Usb-storage has a scsi device as its backend. Use clap to parse the parameters of the scsi-device/usb-storage config simultaneously. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 16 +-- devices/src/scsi/disk.rs | 141 ++++++++++++++++++++++--- devices/src/usb/storage.rs | 58 ++++++++--- devices/src/usb/uas.rs | 41 ++++++-- machine/src/lib.rs | 63 ++++++----- machine_manager/src/config/mod.rs | 2 - machine_manager/src/config/scsi.rs | 154 +-------------------------- machine_manager/src/config/usb.rs | 162 ----------------------------- virtio/src/device/scsi_cntlr.rs | 14 ++- 9 files changed, 255 insertions(+), 396 deletions(-) delete mode 100644 machine_manager/src/config/usb.rs diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 271c51a56..0b018c656 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -666,7 +666,7 @@ impl ScsiRequest { let mut not_supported_flag = false; let mut sense = None; let mut status = GOOD; - let found_lun = self.dev.lock().unwrap().config.lun; + let found_lun = self.dev.lock().unwrap().dev_cfg.lun; // Requested lun id is not equal to found device id means it may be a target request. // REPORT LUNS is also a target request command. @@ -1174,7 +1174,7 @@ fn scsi_command_emulate_mode_sense( if dev_lock.state.features & (1 << SCSI_DISK_F_DPOFUA) != 0 { dev_specific_parameter = 0x10; } - if dev_lock.config.read_only { + if dev_lock.drive_cfg.readonly { // Readonly. dev_specific_parameter |= 0x80; } @@ -1362,7 +1362,7 @@ fn scsi_command_emulate_report_luns( let dev_lock = dev.lock().unwrap(); // Byte 0-3: Lun List Length. Byte 4-7: Reserved. let mut outbuf: Vec = vec![0; 8]; - let target = dev_lock.config.target; + let target = dev_lock.dev_cfg.target; if cmd.xfer < 16 { bail!("scsi REPORT LUNS xfer {} too short!", cmd.xfer); @@ -1383,17 +1383,17 @@ fn scsi_command_emulate_report_luns( for (_pos, device) in scsi_bus_clone.devices.iter() { let device_lock = device.lock().unwrap(); - if device_lock.config.target != target { + if device_lock.dev_cfg.target != target { drop(device_lock); continue; } let len = outbuf.len(); - if device_lock.config.lun < 256 { + if device_lock.dev_cfg.lun < 256 { outbuf.push(0); - outbuf.push(device_lock.config.lun as u8); + outbuf.push(device_lock.dev_cfg.lun as u8); } else { - outbuf.push(0x40 | ((device_lock.config.lun >> 8) & 0xff) as u8); - outbuf.push((device_lock.config.lun & 0xff) as u8); + outbuf.push(0x40 | ((device_lock.dev_cfg.lun >> 8) & 0xff) as u8); + outbuf.push((device_lock.dev_cfg.lun & 0xff) as u8); } outbuf.resize(len + 8, 0); drop(device_lock); diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 6c4652ed8..7edccdeb3 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -14,11 +14,12 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; +use clap::Parser; use crate::ScsiBus::{aio_complete_cb, ScsiBus, ScsiCompleteCb}; use crate::{Device, DeviceBase}; use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; -use machine_manager::config::{DriveFile, ScsiDevConfig, VmConfig}; +use machine_manager::config::{valid_id, DriveConfig, DriveFile, VmConfig}; use machine_manager::event_loop::EventLoop; use util::aio::{Aio, AioEngine, WriteZeroesState}; @@ -57,6 +58,49 @@ pub const SCSI_DISK_DEFAULT_BLOCK_SIZE: u32 = 1 << SCSI_DISK_DEFAULT_BLOCK_SIZE_ pub const SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT: u32 = 11; pub const SCSI_CDROM_DEFAULT_BLOCK_SIZE: u32 = 1 << SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT; +// Stratovirt uses scsi mod in only virtio-scsi and usb-storage. Scsi's channel/target/lun +// of usb-storage are both 0. Scsi's channel/target/lun of virtio-scsi is no more than 0/255/16383. +// Set valid range of channel/target according to the range of virtio-scsi as 0/255. +// +// For stratovirt doesn't support `Flat space addressing format`(14 bits for lun) and only supports +// `peripheral device addressing format`(8 bits for lun) now, lun should be less than 255(2^8 - 1) temporarily. +const SCSI_MAX_CHANNEL: i64 = 0; +const SCSI_MAX_TARGET: i64 = 255; +const SUPPORT_SCSI_MAX_LUN: i64 = 255; + +#[derive(Parser, Clone, Debug, Default)] +#[command(no_binary_name(true))] +pub struct ScsiDevConfig { + #[arg(long, value_parser = ["scsi-cd", "scsi-hd"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long, value_parser = valid_scsi_bus)] + pub bus: String, + /// Scsi four level hierarchical address(host, channel, target, lun). + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u8).range(..=SCSI_MAX_CHANNEL))] + pub channel: u8, + #[arg(long, alias = "scsi-id", value_parser = clap::value_parser!(u8).range(..=SCSI_MAX_TARGET))] + pub target: u8, + #[arg(long, value_parser = clap::value_parser!(u16).range(..=SUPPORT_SCSI_MAX_LUN))] + pub lun: u16, + #[arg(long)] + pub drive: String, + #[arg(long)] + pub serial: Option, + #[arg(long)] + pub bootindex: Option, +} + +// Scsi device should has bus named as "$parent_cntlr_name.0". +fn valid_scsi_bus(bus: &str) -> Result { + let strs = bus.split('.').collect::>(); + if strs.len() != 2 || strs[1] != "0" { + bail!("Invalid scsi bus {}", bus); + } + Ok(bus.to_string()) +} + #[derive(Clone, Default)] pub struct ScsiDevState { /// Features which the scsi device supports. @@ -99,7 +143,9 @@ impl Device for ScsiDevice { pub struct ScsiDevice { pub base: DeviceBase, /// Configuration of the scsi device. - pub config: ScsiDevConfig, + pub dev_cfg: ScsiDevConfig, + /// Configuration of the scsi device's drive. + pub drive_cfg: DriveConfig, /// State of the scsi device. pub state: ScsiDevState, /// Block backend opened by scsi device. @@ -129,13 +175,19 @@ unsafe impl Sync for ScsiDevice {} impl ScsiDevice { pub fn new( - config: ScsiDevConfig, - scsi_type: u32, + dev_cfg: ScsiDevConfig, + drive_cfg: DriveConfig, drive_files: Arc>>, ) -> ScsiDevice { + let scsi_type = match dev_cfg.classtype.as_str() { + "scsi-hd" => SCSI_TYPE_DISK, + _ => SCSI_TYPE_ROM, + }; + ScsiDevice { - base: DeviceBase::new(config.id.clone(), false), - config, + base: DeviceBase::new(dev_cfg.id.clone(), false), + dev_cfg, + drive_cfg, state: ScsiDevState::new(), block_backend: None, req_align: 1, @@ -164,35 +216,35 @@ impl ScsiDevice { } } - if let Some(serial) = &self.config.serial { + if let Some(serial) = &self.dev_cfg.serial { self.state.serial = serial.clone(); } let drive_files = self.drive_files.lock().unwrap(); // File path can not be empty string. And it has also been checked in CmdParser::parse. - let file = VmConfig::fetch_drive_file(&drive_files, &self.config.path_on_host)?; + let file = VmConfig::fetch_drive_file(&drive_files, &self.drive_cfg.path_on_host)?; - let alignments = VmConfig::fetch_drive_align(&drive_files, &self.config.path_on_host)?; + let alignments = VmConfig::fetch_drive_align(&drive_files, &self.drive_cfg.path_on_host)?; self.req_align = alignments.0; self.buf_align = alignments.1; - let drive_id = VmConfig::get_drive_id(&drive_files, &self.config.path_on_host)?; + let drive_id = VmConfig::get_drive_id(&drive_files, &self.drive_cfg.path_on_host)?; let mut thread_pool = None; - if self.config.aio_type != AioEngine::Off { + if self.drive_cfg.aio != AioEngine::Off { thread_pool = Some(EventLoop::get_ctx(None).unwrap().thread_pool.clone()); } - let aio = Aio::new(Arc::new(aio_complete_cb), self.config.aio_type, thread_pool)?; + let aio = Aio::new(Arc::new(aio_complete_cb), self.drive_cfg.aio, thread_pool)?; let conf = BlockProperty { id: drive_id, - format: self.config.format, + format: self.drive_cfg.format, iothread, - direct: self.config.direct, + direct: self.drive_cfg.direct, req_align: self.req_align, buf_align: self.buf_align, discard: false, write_zeroes: WriteZeroesState::Off, - l2_cache_size: self.config.l2_cache_size, - refcount_cache_size: self.config.refcount_cache_size, + l2_cache_size: self.drive_cfg.l2_cache_size, + refcount_cache_size: self.drive_cfg.refcount_cache_size, }; let backend = create_block_backend(file, aio, conf)?; let disk_size = backend.lock().unwrap().disk_size()?; @@ -202,3 +254,60 @@ impl ScsiDevice { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + + #[test] + fn test_scsi_device_cmdline_parser() { + // Test1: Right. + let cmdline1 = "scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-0-0-0-0,id=scsi0-0-0-0,serial=123456,bootindex=1"; + let config = + ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline1, true, false)).unwrap(); + assert_eq!(config.id, "scsi0-0-0-0"); + assert_eq!(config.bus, "scsi0.0"); + assert_eq!(config.target, 0); + assert_eq!(config.lun, 0); + assert_eq!(config.drive, "drive-0-0-0-0"); + assert_eq!(config.serial.unwrap(), "123456"); + assert_eq!(config.bootindex.unwrap(), 1); + + // Test2: Default value. + let cmdline2 = "scsi-cd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let config = + ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline2, true, false)).unwrap(); + assert_eq!(config.channel, 0); + assert_eq!(config.serial, None); + assert_eq!(config.bootindex, None); + + // Test3: Illegal value. + let cmdline3 = "scsi-hd,bus=scsi0.0,scsi-id=256,lun=0,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline3, true, false)); + assert!(result.is_err()); + let cmdline3 = "scsi-hd,bus=scsi0.0,scsi-id=0,lun=256,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline3, true, false)); + assert!(result.is_err()); + let cmdline3 = "illegal,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline3, true, false)); + assert!(result.is_err()); + + // Test4: Missing necessary parameters. + let cmdline4 = "scsi-hd,scsi-id=0,lun=0,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + let cmdline4 = "scsi-hd,bus=scsi0.0,lun=0,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + let cmdline4 = "scsi-hd,bus=scsi0.0,scsi-id=0,drive=drive-0-0-0-0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + let cmdline4 = "scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,id=scsi0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + let cmdline4 = "scsi-hd,bus=scsi0.0,scsi-id=0,lun=0,drive=drive-0-0-0-0"; + let result = ScsiDevConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + } +} diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 4bb44e0ee..2f9d4c969 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -17,6 +17,7 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; +use clap::Parser; use log::{error, info, warn}; use once_cell::sync::Lazy; @@ -32,9 +33,10 @@ use crate::{ ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD, SCSI_CMD_BUF_SIZE, }, - ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, + ScsiDisk::{ScsiDevConfig, ScsiDevice}, }; -use machine_manager::config::{DriveFile, UsbStorageConfig}; +use machine_manager::config::{DriveConfig, DriveFile}; +use util::aio::AioEngine; // Storage device descriptor static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { @@ -221,6 +223,21 @@ impl UsbStorageState { } } +#[derive(Parser, Clone, Debug)] +#[command(no_binary_name(true))] +pub struct UsbStorageConfig { + #[arg(long, value_parser = ["usb-storage"])] + pub classtype: String, + #[arg(long)] + pub id: String, + #[arg(long)] + pub drive: String, + #[arg(long)] + bus: Option, + #[arg(long)] + port: Option, +} + /// USB storage device. pub struct UsbStorage { base: UsbDeviceBase, @@ -228,7 +245,9 @@ pub struct UsbStorage { /// USB controller used to notify controller to transfer data. cntlr: Option>>, /// Configuration of the USB storage device. - pub config: UsbStorageConfig, + pub dev_cfg: UsbStorageConfig, + /// Configuration of the USB storage device's drive. + pub drive_cfg: DriveConfig, /// Scsi bus attached to this usb-storage device. scsi_bus: Arc>, /// Effective scsi backend. @@ -305,26 +324,37 @@ impl UsbMsdCsw { impl UsbStorage { pub fn new( - config: UsbStorageConfig, + dev_cfg: UsbStorageConfig, + drive_cfg: DriveConfig, drive_files: Arc>>, - ) -> Self { - let scsi_type = match &config.media as &str { - "disk" => SCSI_TYPE_DISK, - _ => SCSI_TYPE_ROM, + ) -> Result { + if drive_cfg.aio != AioEngine::Off || drive_cfg.direct { + bail!("USB-storage: \"aio=off,direct=false\" must be configured."); + } + + let scsidev_classtype = match &drive_cfg.media as &str { + "disk" => "scsi-hd".to_string(), + _ => "scsi-cd".to_string(), + }; + let scsi_dev_cfg = ScsiDevConfig { + classtype: scsidev_classtype, + drive: dev_cfg.drive.clone(), + ..Default::default() }; - Self { - base: UsbDeviceBase::new(config.id.clone().unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN), + Ok(Self { + base: UsbDeviceBase::new(dev_cfg.id.clone(), USB_DEVICE_BUFFER_DEFAULT_LEN), state: UsbStorageState::new(), cntlr: None, - config: config.clone(), + dev_cfg, + drive_cfg: drive_cfg.clone(), scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), scsi_dev: Arc::new(Mutex::new(ScsiDevice::new( - config.scsi_cfg, - scsi_type, + scsi_dev_cfg, + drive_cfg, drive_files, ))), - } + }) } fn handle_control_packet(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 3768b8bbe..9ba134cd4 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -17,12 +17,11 @@ use std::mem::size_of; use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use log::{debug, error, info, warn}; -use machine_manager::config::{DriveFile, UsbUasConfig}; use once_cell::sync::Lazy; use strum::EnumCount; use strum_macros::EnumCount; -use util::byte_code::ByteCode; use super::config::*; use super::descriptor::{ @@ -35,15 +34,16 @@ use super::{ UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; - use crate::{ ScsiBus::{ scsi_cdb_xfer, scsi_cdb_xfer_mode, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, SCSI_SENSE_OVERLAPPED_COMMANDS, }, - ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}, + ScsiDisk::{ScsiDevConfig, ScsiDevice}, }; +use machine_manager::config::{DriveConfig, DriveFile}; +use util::byte_code::ByteCode; // Size of UasIUBody const UAS_IU_BODY_SIZE: usize = 30; @@ -89,6 +89,23 @@ const _UAS_TMF_QUERY_TASK: u8 = 0x80; const _UAS_TMF_QUERY_TASK_SET: u8 = 0x81; const _UAS_TMF_QUERY_ASYNC_EVENT: u8 = 0x82; +#[derive(Parser, Clone, Debug)] +#[command(no_binary_name(true))] +pub struct UsbUasConfig { + #[arg(long, value_parser = ["usb-uas"])] + pub classtype: String, + #[arg(long)] + pub drive: String, + #[arg(long)] + pub id: Option, + #[arg(long)] + pub speed: Option, + #[arg(long)] + bus: Option, + #[arg(long)] + port: Option, +} + pub struct UsbUas { base: UsbDeviceBase, scsi_bus: Arc>, @@ -573,11 +590,17 @@ fn complete_async_packet(packet: &Arc>) { impl UsbUas { pub fn new( uas_config: UsbUasConfig, + drive_cfg: DriveConfig, drive_files: Arc>>, ) -> Self { - let scsi_type = match &uas_config.media as &str { - "disk" => SCSI_TYPE_DISK, - _ => SCSI_TYPE_ROM, + let scsidev_classtype = match &drive_cfg.media as &str { + "disk" => "scsi-hd".to_string(), + _ => "scsi-cd".to_string(), + }; + let scsi_dev_cfg = ScsiDevConfig { + classtype: scsidev_classtype, + drive: uas_config.drive.clone(), + ..Default::default() }; let mut base = UsbDeviceBase::new( @@ -594,8 +617,8 @@ impl UsbUas { base, scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), scsi_device: Arc::new(Mutex::new(ScsiDevice::new( - uas_config.scsi_cfg, - scsi_type, + scsi_dev_cfg, + drive_cfg, drive_files, ))), commands_high: VecDeque::new(), diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fb0256d26..031f16975 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -59,14 +59,16 @@ use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; +use devices::usb::storage::{UsbStorage, UsbStorageConfig}; use devices::usb::tablet::{UsbTablet, UsbTabletConfig}; +use devices::usb::uas::{UsbUas, UsbUasConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::{UsbHost, UsbHostConfig}; use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice}; -use devices::usb::{storage::UsbStorage, uas::UsbUas, UsbDevice}; +use devices::usb::UsbDevice; #[cfg(target_arch = "aarch64")] use devices::InterruptController; -use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM}; +use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; @@ -76,16 +78,13 @@ use machine_manager::config::parse_demo_dev; use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] use machine_manager::config::parse_pvpanic; -use machine_manager::config::parse_usb_storage; -use machine_manager::config::parse_usb_uas; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, - parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk, - parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, - BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, - NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, - MAX_VIRTIO_QUEUE, + parse_root_port, parse_scsi_controller, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, + parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, + DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, + NumaNodes, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -1119,29 +1118,27 @@ pub trait MachineOps { } fn add_scsi_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let device_cfg = parse_scsi_device(vm_config, cfg_args)?; - let scsi_type = match parse_device_type(cfg_args)?.as_str() { - "scsi-hd" => SCSI_TYPE_DISK, - _ => SCSI_TYPE_ROM, - }; - if let Some(bootindex) = device_cfg.boot_index { + let device_cfg = ScsiDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let drive_arg = vm_config + .drives + .remove(&device_cfg.drive) + .with_context(|| "No drive configured matched for scsi device")?; + + if let Some(bootindex) = device_cfg.bootindex { self.check_bootindex(bootindex) .with_context(|| "Failed to add scsi device for invalid bootindex")?; } let device = Arc::new(Mutex::new(ScsiDevice::new( device_cfg.clone(), - scsi_type, + drive_arg, self.get_drive_files(), ))); + // Bus name `$parent_cntlr_name.0` is checked when parsing by clap. + let cntlr = device_cfg.bus.split('.').collect::>()[0].to_string(); let pci_dev = self - .get_pci_dev_by_id_and_type(vm_config, Some(&device_cfg.cntlr), "virtio-scsi-pci") - .with_context(|| { - format!( - "Can not find scsi controller from pci bus {}", - device_cfg.cntlr - ) - })?; + .get_pci_dev_by_id_and_type(vm_config, Some(&cntlr), "virtio-scsi-pci") + .with_context(|| format!("Can not find scsi controller from pci bus {}", cntlr))?; let locked_pcidev = pci_dev.lock().unwrap(); let virtio_pcidev = locked_pcidev .as_any() @@ -1167,7 +1164,7 @@ pub trait MachineOps { .insert((device_cfg.target, device_cfg.lun), device.clone()); device.lock().unwrap().parent_bus = Arc::downgrade(bus); - if let Some(bootindex) = device_cfg.boot_index { + if let Some(bootindex) = device_cfg.bootindex { // Eg: OpenFirmware device path(virtio-scsi disk): // /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3 // | | | | | | @@ -1725,15 +1722,25 @@ pub trait MachineOps { .with_context(|| "Failed to realize usb camera device")? } "usb-storage" => { - let device_cfg = parse_usb_storage(vm_config, cfg_args)?; - let storage = UsbStorage::new(device_cfg, self.get_drive_files()); + let device_cfg = + UsbStorageConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let drive_cfg = vm_config + .drives + .remove(&device_cfg.drive) + .with_context(|| "No drive configured matched for usb storage device.")?; + let storage = UsbStorage::new(device_cfg, drive_cfg, self.get_drive_files())?; storage .realize() .with_context(|| "Failed to realize usb storage device")? } "usb-uas" => { - let device_cfg = parse_usb_uas(vm_config, cfg_args)?; - let uas = UsbUas::new(device_cfg, self.get_drive_files()); + let device_cfg = + UsbUasConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let drive_cfg = vm_config + .drives + .remove(&device_cfg.drive) + .with_context(|| "No drive configured matched for usb uas device.")?; + let uas = UsbUas::new(device_cfg, drive_cfg, self.get_drive_files()); uas.realize() .with_context(|| "Failed to realize usb uas device")? } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 8f4d72e75..fd7614145 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -44,7 +44,6 @@ mod scsi; mod smbios; #[cfg(feature = "vnc_auth")] mod tls_creds; -mod usb; mod vfio; pub use boot_source::*; @@ -78,7 +77,6 @@ pub use scsi::*; pub use smbios::*; #[cfg(feature = "vnc_auth")] pub use tls_creds::*; -pub use usb::*; pub use vfio::*; #[cfg(feature = "vnc")] pub use vnc::*; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs index d2fb48f71..da3d1dbb7 100644 --- a/machine_manager/src/config/scsi.rs +++ b/machine_manager/src/config/scsi.rs @@ -12,22 +12,10 @@ use anyhow::{anyhow, bail, Context, Result}; -use super::{error::ConfigError, pci_args_check, DiskFormat}; +use super::{error::ConfigError, pci_args_check}; use crate::config::{ - check_arg_too_long, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, + check_arg_too_long, CmdParser, ConfigCheck, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, }; -use util::aio::AioEngine; - -/// According to Virtio Spec. -/// Max_channel should be 0. -/// Max_target should be less than or equal to 255. -pub const VIRTIO_SCSI_MAX_TARGET: u16 = 255; -/// Max_lun should be less than or equal to 16383 (2^14 - 1). -pub const VIRTIO_SCSI_MAX_LUN: u16 = 16383; - -/// Only support peripheral device addressing format(8 bits for lun) in stratovirt now. -/// So, max lun id supported is 255 (2^8 - 1). -const SUPPORT_SCSI_MAX_LUN: u16 = 255; // Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi should be larger than 2. const MIN_QUEUE_SIZE_SCSI: u16 = 2; @@ -139,141 +127,3 @@ pub fn parse_scsi_controller( cntlr_cfg.check()?; Ok(cntlr_cfg) } - -#[derive(Clone, Debug)] -pub struct ScsiDevConfig { - /// Scsi Device id. - pub id: String, - /// The image file path. - pub path_on_host: String, - /// Serial number of the scsi device. - pub serial: Option, - /// Scsi controller which the scsi device attaches to. - pub cntlr: String, - /// Scsi device can not do write operation. - pub read_only: bool, - /// If true, use direct access io. - pub direct: bool, - /// Async IO type. - pub aio_type: AioEngine, - /// Boot order. - pub boot_index: Option, - /// Scsi four level hierarchical address(host, channel, target, lun). - pub channel: u8, - pub target: u8, - pub lun: u16, - pub format: DiskFormat, - pub l2_cache_size: Option, - pub refcount_cache_size: Option, -} - -impl Default for ScsiDevConfig { - fn default() -> Self { - ScsiDevConfig { - id: "".to_string(), - path_on_host: "".to_string(), - serial: None, - cntlr: "".to_string(), - read_only: false, - direct: true, - aio_type: AioEngine::Native, - boot_index: None, - channel: 0, - target: 0, - lun: 0, - format: DiskFormat::Raw, - l2_cache_size: None, - refcount_cache_size: None, - } - } -} - -pub fn parse_scsi_device(vm_config: &mut VmConfig, drive_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("scsi-device"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("scsi-id") - .push("lun") - .push("serial") - .push("bootindex") - .push("drive"); - - cmd_parser.parse(drive_config)?; - - let mut scsi_dev_cfg = ScsiDevConfig::default(); - - let scsi_drive = cmd_parser.get_value::("drive")?.with_context(|| { - ConfigError::FieldIsMissing("drive".to_string(), "scsi device".to_string()) - })?; - - if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { - scsi_dev_cfg.boot_index = Some(boot_index); - } - - if let Some(serial) = cmd_parser.get_value::("serial")? { - scsi_dev_cfg.serial = Some(serial); - } - - scsi_dev_cfg.id = cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "scsi device".to_string()) - })?; - - if let Some(bus) = cmd_parser.get_value::("bus")? { - // Format "$parent_cntlr_name.0" is required by scsi bus. - let strs = bus.split('.').collect::>(); - if strs.len() != 2 || strs[1] != "0" { - bail!("Invalid scsi bus {}", bus); - } - scsi_dev_cfg.cntlr = strs[0].to_string(); - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "bus".to_string(), - "scsi device".to_string() - ))); - } - - if let Some(target) = cmd_parser.get_value::("scsi-id")? { - if target > VIRTIO_SCSI_MAX_TARGET as u8 { - return Err(anyhow!(ConfigError::IllegalValue( - "scsi-id of scsi device".to_string(), - 0, - true, - VIRTIO_SCSI_MAX_TARGET as u64, - true, - ))); - } - scsi_dev_cfg.target = target; - } - - if let Some(lun) = cmd_parser.get_value::("lun")? { - // Do not support Flat space addressing format(14 bits for lun) in stratovirt now. - // We now support peripheral device addressing format(8 bits for lun). - // So, MAX_LUN should be less than 255(2^8 - 1) temporarily. - if lun > SUPPORT_SCSI_MAX_LUN { - return Err(anyhow!(ConfigError::IllegalValue( - "lun of scsi device".to_string(), - 0, - true, - SUPPORT_SCSI_MAX_LUN as u64, - true, - ))); - } - scsi_dev_cfg.lun = lun; - } - - let drive_arg = &vm_config - .drives - .remove(&scsi_drive) - .with_context(|| "No drive configured matched for scsi device")?; - scsi_dev_cfg.path_on_host = drive_arg.path_on_host.clone(); - scsi_dev_cfg.read_only = drive_arg.readonly; - scsi_dev_cfg.direct = drive_arg.direct; - scsi_dev_cfg.aio_type = drive_arg.aio; - scsi_dev_cfg.format = drive_arg.format; - scsi_dev_cfg.l2_cache_size = drive_arg.l2_cache_size; - scsi_dev_cfg.refcount_cache_size = drive_arg.refcount_cache_size; - - Ok(scsi_dev_cfg) -} diff --git a/machine_manager/src/config/usb.rs b/machine_manager/src/config/usb.rs deleted file mode 100644 index 67917924e..000000000 --- a/machine_manager/src/config/usb.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{bail, Context, Result}; - -use super::error::ConfigError; -use crate::config::{ - check_arg_nonexist, check_arg_too_long, CmdParser, ConfigCheck, ScsiDevConfig, VmConfig, -}; -use util::aio::AioEngine; - -pub fn check_id(id: Option, device: &str) -> Result<()> { - check_arg_nonexist(id.clone(), "id", device)?; - check_arg_too_long(&id.unwrap(), "id")?; - - Ok(()) -} - -#[derive(Clone, Debug)] -pub struct UsbStorageConfig { - /// USB Storage device id. - pub id: Option, - /// The scsi backend config. - pub scsi_cfg: ScsiDevConfig, - /// The backend scsi device type(Disk or CD-ROM). - pub media: String, -} - -impl UsbStorageConfig { - fn new() -> Self { - Self { - id: None, - scsi_cfg: ScsiDevConfig::default(), - media: "".to_string(), - } - } -} - -impl Default for UsbStorageConfig { - fn default() -> Self { - Self::new() - } -} - -impl ConfigCheck for UsbStorageConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-storage")?; - - if self.scsi_cfg.aio_type != AioEngine::Off || self.scsi_cfg.direct { - bail!("USB-storage: \"aio=off,direct=false\" must be configured."); - } - - Ok(()) - } -} - -pub fn parse_usb_storage(vm_config: &mut VmConfig, drive_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("usb-storage"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("port") - .push("drive"); - - cmd_parser.parse(drive_config)?; - - let mut dev = UsbStorageConfig::new(); - dev.id = cmd_parser.get_value::("id")?; - - let storage_drive = cmd_parser.get_value::("drive")?.with_context(|| { - ConfigError::FieldIsMissing("drive".to_string(), "usb storage device".to_string()) - })?; - - let drive_arg = &vm_config - .drives - .remove(&storage_drive) - .with_context(|| "No drive configured matched for usb storage device.")?; - dev.scsi_cfg.path_on_host = drive_arg.path_on_host.clone(); - dev.scsi_cfg.read_only = drive_arg.readonly; - dev.scsi_cfg.aio_type = drive_arg.aio; - dev.scsi_cfg.direct = drive_arg.direct; - dev.scsi_cfg.format = drive_arg.format; - dev.scsi_cfg.l2_cache_size = drive_arg.l2_cache_size; - dev.scsi_cfg.refcount_cache_size = drive_arg.refcount_cache_size; - dev.media = drive_arg.media.clone(); - - dev.check()?; - Ok(dev) -} - -#[derive(Clone, Debug, Default)] -pub struct UsbUasConfig { - pub id: Option, - pub speed: Option, - pub scsi_cfg: ScsiDevConfig, - pub media: String, -} - -impl UsbUasConfig { - fn new() -> Self { - Self::default() - } -} - -impl ConfigCheck for UsbUasConfig { - fn check(&self) -> Result<()> { - check_id(self.id.clone(), "usb-uas")?; - - if self.scsi_cfg.aio_type != AioEngine::Off || self.scsi_cfg.direct { - bail!("USB UAS: \"aio=off,direct=false\" must be configured."); - } - - Ok(()) - } -} - -pub fn parse_usb_uas(vm_config: &mut VmConfig, drive_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("usb-uas"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("port") - .push("drive") - .push("speed"); - - cmd_parser.parse(drive_config)?; - - let mut dev = UsbUasConfig::new(); - dev.id = cmd_parser.get_value::("id")?; - dev.speed = cmd_parser.get_value::("speed")?; - - let uas_drive = cmd_parser.get_value::("drive")?.with_context(|| { - ConfigError::FieldIsMissing("drive".to_string(), "usb uas device".to_string()) - })?; - - let drive_arg = &vm_config - .drives - .remove(&uas_drive) - .with_context(|| "No drive configured matched for usb uas device.")?; - dev.scsi_cfg.path_on_host = drive_arg.path_on_host.clone(); - dev.scsi_cfg.read_only = drive_arg.read_only; - dev.scsi_cfg.aio_type = drive_arg.aio; - dev.scsi_cfg.direct = drive_arg.direct; - dev.scsi_cfg.format = drive_arg.format; - dev.scsi_cfg.l2_cache_size = drive_arg.l2_cache_size; - dev.scsi_cfg.refcount_cache_size = drive_arg.refcount_cache_size; - dev.media = drive_arg.media.clone(); - - dev.check()?; - Ok(dev) -} diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index d3d6cf427..2cc15bcc2 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -33,16 +33,20 @@ use devices::ScsiBus::{ EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, }; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use machine_manager::{ - config::{ScsiCntlrConfig, VIRTIO_SCSI_MAX_LUN, VIRTIO_SCSI_MAX_TARGET}, - event_loop::EventLoop, -}; +use machine_manager::{config::ScsiCntlrConfig, event_loop::EventLoop}; use util::aio::Iovec; use util::byte_code::ByteCode; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; +/// According to Virtio Spec. +/// Max_channel should be 0. +/// Max_target should be less than or equal to 255. +const VIRTIO_SCSI_MAX_TARGET: u16 = 255; +/// Max_lun should be less than or equal to 16383 (2^14 - 1). +const VIRTIO_SCSI_MAX_LUN: u32 = 16383; + /// Virtio Scsi Controller has 1 ctrl queue, 1 event queue and at least 1 cmd queue. const SCSI_CTRL_QUEUE_NUM: usize = 1; const SCSI_EVENT_QUEUE_NUM: usize = 1; @@ -171,7 +175,7 @@ impl VirtioDevice for ScsiCntlr { // seg_max: queue size - 2, 32 bit. self.config_space.seg_max = self.queue_size_max() as u32 - 2; self.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; - self.config_space.max_lun = VIRTIO_SCSI_MAX_LUN as u32; + self.config_space.max_lun = VIRTIO_SCSI_MAX_LUN; // num_queues: request queues number. self.config_space.num_queues = self.config.queues; -- Gitee From 8ac73b5df1ad85926d654615e24ad2203b9b953c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 13 Mar 2024 17:33:38 +0800 Subject: [PATCH 1768/2187] scsicntlr: use clap to parse the parameters of the virtio-scsi-pci config Use clap to parse the parameters of the virtio-scsi-pci config. Signed-off-by: liuxiangdong --- docs/config_guidebook.md | 2 +- machine/src/lib.rs | 36 ++++---- machine_manager/src/config/mod.rs | 25 +++++- machine_manager/src/config/scsi.rs | 129 ----------------------------- virtio/src/device/scsi_cntlr.rs | 94 +++++++++++++++++++-- 5 files changed, 132 insertions(+), 154 deletions(-) delete mode 100644 machine_manager/src/config/scsi.rs diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index bf205fc18..16195406b 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -938,7 +938,7 @@ Six properties can be set for Virtio-Scsi controller. * bus: bus number of the device. * addr: including slot number and function number. * iothread: indicate which iothread will be used, if not specified the main thread will be used. (optional) -* num-queues: the optional num-queues attribute controls the number of request queues to be used for the scsi controller. If not set, the default block queue number is 1. The max queues number supported is no more than 32. (optional) +* num-queues: the optional num-queues attribute controls the number of request queues to be used for the scsi controller. If not set, the default queue number is the smaller one of vCPU count and the max queues number (e.g, min(vcpu_count, 32)). The max queues number supported is no more than 32. (optional) * queue-size: the optional virtqueue size for all the queues. Configuration range is (2, 1024] and queue size must be power of 2. Default queue size is 256. ```shell -device virtio-scsi-pci,id=,bus=,addr=<0x3>[,multifunction={on|off}][,iothread=][,num-queues=][,queue-size=] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 031f16975..6b0be7dfd 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -81,10 +81,10 @@ use machine_manager::config::parse_pvpanic; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, - parse_root_port, parse_scsi_controller, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, - parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, - DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, - NumaNodes, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + parse_root_port, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, + parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, + MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, + SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -104,7 +104,7 @@ use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block, BlockState, Rng, RngState, - ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr}, + ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}, Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; @@ -1094,26 +1094,28 @@ pub trait MachineOps { cfg_args: &str, hotplug: bool, ) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( - 0, - vm_config.machine_config.nr_cpus, - MAX_VIRTIO_QUEUE, - )); - let device_cfg = parse_scsi_controller(cfg_args, queues_auto)?; + let mut device_cfg = + ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let bdf = PciBdf::new(device_cfg.bus.clone(), device_cfg.addr); + let multi_func = device_cfg.multifunction.unwrap_or_default(); + if device_cfg.num_queues.is_none() { + let queues_auto = VirtioPciDevice::virtio_pci_auto_queues_num( + 0, + vm_config.machine_config.nr_cpus, + MAX_VIRTIO_QUEUE, + ); + device_cfg.num_queues = Some(queues_auto as u32); + } let device = Arc::new(Mutex::new(ScsiCntlr::new(device_cfg.clone()))); let bus_name = format!("{}.0", device_cfg.id); scsi_cntlr_create_scsi_bus(&bus_name, &device)?; - let pci_dev = self - .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) + self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false) .with_context(|| "Failed to add virtio scsi controller")?; if !hotplug { self.reset_bus(&device_cfg.id)?; } - device.lock().unwrap().config.boot_prefix = pci_dev.lock().unwrap().get_dev_path(); Ok(()) } @@ -1140,6 +1142,7 @@ pub trait MachineOps { .get_pci_dev_by_id_and_type(vm_config, Some(&cntlr), "virtio-scsi-pci") .with_context(|| format!("Can not find scsi controller from pci bus {}", cntlr))?; let locked_pcidev = pci_dev.lock().unwrap(); + let prefix = locked_pcidev.get_dev_path().unwrap(); let virtio_pcidev = locked_pcidev .as_any() .downcast_ref::() @@ -1172,7 +1175,6 @@ pub trait MachineOps { // | | | channel(unused, fixed 0). // | PCI slot,[function] holding SCSI controller. // PCI root as system bus port. - let prefix = cntlr.config.boot_prefix.as_ref().unwrap(); let dev_path = format! {"{}/channel@0/disk@{:x},{:x}", prefix, device_cfg.target, device_cfg.lun}; self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index fd7614145..b34540cb3 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -40,7 +40,6 @@ mod ramfb; mod rng; #[cfg(feature = "vnc_auth")] mod sasl_auth; -mod scsi; mod smbios; #[cfg(feature = "vnc_auth")] mod tls_creds; @@ -73,7 +72,6 @@ pub use ramfb::*; pub use rng::*; #[cfg(feature = "vnc_auth")] pub use sasl_auth::*; -pub use scsi::*; pub use smbios::*; #[cfg(feature = "vnc_auth")] pub use tls_creds::*; @@ -112,6 +110,10 @@ pub const MAX_TAG_LENGTH: usize = 36; pub const MAX_NODES: u32 = 128; /// Default virtqueue size for virtio devices excepts virtio-fs. pub const DEFAULT_VIRTQUEUE_SIZE: u16 = 256; +// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi should be larger than 2. +pub const MIN_QUEUE_SIZE_SCSI: u64 = 2; +// Max size of each virtqueue for virtio-scsi. +pub const MAX_QUEUE_SIZE_SCSI: u64 = 1024; #[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct ObjectConfig { @@ -795,6 +797,25 @@ pub fn valid_id(id: &str) -> Result { Ok(id.to_string()) } +// Virtio queue size must be power of 2 and in range [min_size, max_size]. +pub fn valid_virtqueue_size(size: u64, min_size: u64, max_size: u64) -> Result<()> { + if size < min_size || size > max_size { + return Err(anyhow!(ConfigError::IllegalValue( + "virtqueue size".to_string(), + min_size, + true, + max_size, + true + ))); + } + + if size & (size - 1) != 0 { + bail!("Virtqueue size should be power of 2!"); + } + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/scsi.rs b/machine_manager/src/config/scsi.rs deleted file mode 100644 index da3d1dbb7..000000000 --- a/machine_manager/src/config/scsi.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{anyhow, bail, Context, Result}; - -use super::{error::ConfigError, pci_args_check}; -use crate::config::{ - check_arg_too_long, CmdParser, ConfigCheck, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE, -}; - -// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi should be larger than 2. -const MIN_QUEUE_SIZE_SCSI: u16 = 2; -// Max size of each virtqueue for virtio-scsi. -const MAX_QUEUE_SIZE_SCSI: u16 = 1024; - -#[derive(Debug, Clone)] -pub struct ScsiCntlrConfig { - /// Virtio-scsi-pci device id. - pub id: String, - /// Thread name of io handler. - pub iothread: Option, - /// Number of scsi cmd queues. - pub queues: u32, - /// Boot path of this scsi controller. It's prefix of scsi device's boot path. - pub boot_prefix: Option, - /// Virtqueue size for all queues. - pub queue_size: u16, -} - -impl Default for ScsiCntlrConfig { - fn default() -> Self { - ScsiCntlrConfig { - id: "".to_string(), - iothread: None, - // At least 1 cmd queue. - queues: 1, - boot_prefix: None, - queue_size: DEFAULT_VIRTQUEUE_SIZE, - } - } -} - -impl ConfigCheck for ScsiCntlrConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "virtio-scsi-pci device id")?; - - if self.iothread.is_some() { - check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; - } - - if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u32 { - return Err(anyhow!(ConfigError::IllegalValue( - "queues number of scsi controller".to_string(), - 1, - true, - MAX_VIRTIO_QUEUE as u64, - true, - ))); - } - - if self.queue_size <= MIN_QUEUE_SIZE_SCSI || self.queue_size > MAX_QUEUE_SIZE_SCSI { - return Err(anyhow!(ConfigError::IllegalValue( - "virtqueue size of scsi controller".to_string(), - MIN_QUEUE_SIZE_SCSI as u64, - false, - MAX_QUEUE_SIZE_SCSI as u64, - true - ))); - } - - if self.queue_size & (self.queue_size - 1) != 0 { - bail!("Virtqueue size should be power of 2!"); - } - - Ok(()) - } -} - -pub fn parse_scsi_controller( - drive_config: &str, - queues_auto: Option, -) -> Result { - let mut cmd_parser = CmdParser::new("virtio-scsi-pci"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("multifunction") - .push("iothread") - .push("num-queues") - .push("queue-size"); - - cmd_parser.parse(drive_config)?; - - pci_args_check(&cmd_parser)?; - - let mut cntlr_cfg = ScsiCntlrConfig::default(); - - if let Some(iothread) = cmd_parser.get_value::("iothread")? { - cntlr_cfg.iothread = Some(iothread); - } - - cntlr_cfg.id = cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "virtio scsi pci".to_string()) - })?; - - if let Some(queues) = cmd_parser.get_value::("num-queues")? { - cntlr_cfg.queues = queues; - } else if let Some(queues) = queues_auto { - cntlr_cfg.queues = queues as u32; - } - - if let Some(size) = cmd_parser.get_value::("queue-size")? { - cntlr_cfg.queue_size = size; - } - - cntlr_cfg.check()?; - Ok(cntlr_cfg) -} diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 2cc15bcc2..dc2d8f514 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; +use clap::Parser; use log::{error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -32,8 +33,11 @@ use devices::ScsiBus::{ ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, }; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use machine_manager::{config::ScsiCntlrConfig, event_loop::EventLoop}; +use machine_manager::config::{ + get_pci_df, parse_bool, valid_id, valid_virtqueue_size, MAX_QUEUE_SIZE_SCSI, MAX_VIRTIO_QUEUE, + MIN_QUEUE_SIZE_SCSI, +}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use util::aio::Iovec; use util::byte_code::ByteCode; use util::loop_context::{ @@ -92,6 +96,34 @@ const VIRTIO_SCSI_S_BAD_TARGET: u8 = 3; /// with a response equal to VIRTIO_SCSI_S_FAILURE. const VIRTIO_SCSI_S_FAILURE: u8 = 9; +#[derive(Parser, Debug, Clone, Default)] +#[command(no_binary_name(true))] +pub struct ScsiCntlrConfig { + #[arg(long, value_parser = ["virtio-scsi-pci"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + #[arg(long, value_parser = parse_bool)] + pub multifunction: Option, + #[arg(long, alias = "num-queues", value_parser = clap::value_parser!(u32).range(1..=MAX_VIRTIO_QUEUE as i64))] + pub num_queues: Option, + #[arg(long)] + pub iothread: Option, + #[arg(long, alias = "queue-size", default_value = "256", value_parser = valid_scsi_cntlr_queue_size)] + pub queue_size: u16, +} + +fn valid_scsi_cntlr_queue_size(s: &str) -> Result { + let size: u64 = s.parse()?; + valid_virtqueue_size(size, MIN_QUEUE_SIZE_SCSI + 1, MAX_QUEUE_SIZE_SCSI)?; + + Ok(size as u16) +} + #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] struct VirtioScsiConfig { @@ -125,7 +157,8 @@ pub struct ScsiCntlr { impl ScsiCntlr { pub fn new(config: ScsiCntlrConfig) -> ScsiCntlr { // Note: config.queues <= MAX_VIRTIO_QUEUE(32). - let queue_num = config.queues as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM; + let queue_num = + config.num_queues.unwrap() as usize + SCSI_CTRL_QUEUE_NUM + SCSI_EVENT_QUEUE_NUM; let queue_size = config.queue_size; Self { @@ -168,7 +201,6 @@ impl VirtioDevice for ScsiCntlr { } fn init_config_features(&mut self) -> Result<()> { - self.config_space.num_queues = self.config.queues; self.config_space.max_sectors = 0xFFFF_u32; // cmd_per_lun: maximum number of linked commands can be sent to one LUN. 32bit. self.config_space.cmd_per_lun = 128; @@ -177,7 +209,7 @@ impl VirtioDevice for ScsiCntlr { self.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; self.config_space.max_lun = VIRTIO_SCSI_MAX_LUN; // num_queues: request queues number. - self.config_space.num_queues = self.config.queues; + self.config_space.num_queues = self.config.num_queues.unwrap(); self.base.device_features |= (1_u64 << VIRTIO_F_VERSION_1) | (1_u64 << VIRTIO_F_RING_EVENT_IDX) @@ -969,3 +1001,55 @@ pub fn scsi_cntlr_create_scsi_bus( locked_scsi_cntlr.bus = Some(Arc::new(Mutex::new(bus))); Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + + #[test] + fn test_scsi_cntlr_config_cmdline_parser() { + // Test1: Right. + let cmdline1 = "virtio-scsi-pci,id=scsi0,bus=pcie.0,addr=0x3,multifunction=on,iothread=iothread1,num-queues=3,queue-size=128"; + let device_cfg = + ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline1, true, false)).unwrap(); + assert_eq!(device_cfg.id, "scsi0"); + assert_eq!(device_cfg.bus, "pcie.0"); + assert_eq!(device_cfg.addr, (3, 0)); + assert_eq!(device_cfg.multifunction, Some(true)); + assert_eq!(device_cfg.iothread.unwrap(), "iothread1"); + assert_eq!(device_cfg.num_queues.unwrap(), 3); + assert_eq!(device_cfg.queue_size, 128); + + // Test2: Default value. + let cmdline2 = "virtio-scsi-pci,id=scsi0,bus=pcie.0,addr=0x3.0x1"; + let device_cfg = + ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline2, true, false)).unwrap(); + assert_eq!(device_cfg.addr, (3, 1)); + assert_eq!(device_cfg.multifunction, None); + assert_eq!(device_cfg.num_queues, None); + assert_eq!(device_cfg.queue_size, 256); + + // Test3: Illegal value. + let cmdline3 = "virtio-scsi-pci,id=scsi0,bus=pcie.0,addr=0x3.0x1,num-queues=33"; + let result = ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline3, true, false)); + assert!(result.is_err()); + let cmdline3 = "virtio-scsi-pci,id=scsi0,bus=pcie.0,addr=0x3.0x1,queue-size=1025"; + let result = ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline3, true, false)); + assert!(result.is_err()); + let cmdline3 = "virtio-scsi-pci,id=scsi0,bus=pcie.0,addr=0x3.0x1,queue-size=65"; + let result = ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline3, true, false)); + assert!(result.is_err()); + + // Test4: Missing necessary parameters. + let cmdline4 = "virtio-scsi-pci,id=scsi0"; + let result = ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + let cmdline4 = "virtio-scsi-pci,bus=pcie.0,addr=0x3.0x1"; + let result = ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + let cmdline4 = "virtio-scsi-pci,id=scsi0,addr=0x3.0x1"; + let result = ScsiCntlrConfig::try_parse_from(str_slip_to_clap(cmdline4, true, false)); + assert!(result.is_err()); + } +} -- Gitee From 01ea396aef0417d8fa5ec44ab5e8b101f40c9af3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 17 Mar 2024 23:18:50 +0800 Subject: [PATCH 1769/2187] chardev: use clap to parse the parameters of the chardev config Use clap to parse the parameters of the chardev config. Signed-off-by: liuxiangdong --- chardev_backend/src/chardev.rs | 92 +++--- devices/src/legacy/pl011.rs | 5 +- devices/src/legacy/serial.rs | 10 +- machine_manager/src/cmdline.rs | 64 ++-- machine_manager/src/config/chardev.rs | 430 ++++++++++---------------- machine_manager/src/config/drive.rs | 16 +- machine_manager/src/config/fs.rs | 40 ++- machine_manager/src/config/mod.rs | 31 +- virtio/src/device/serial.rs | 5 +- 9 files changed, 295 insertions(+), 398 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 7a07a78ac..3f34fc610 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -28,7 +28,7 @@ use vmm_sys_util::epoll::EventSet; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{PathInfo, PTY_PATH}; use machine_manager::{ - config::{ChardevConfig, ChardevType}, + config::{ChardevConfig, ChardevType, SocketType}, temp_cleaner::TempCleaner, }; use util::file::clear_file; @@ -90,8 +90,8 @@ pub struct Chardev { impl Chardev { pub fn new(chardev_cfg: ChardevConfig) -> Self { Chardev { - id: chardev_cfg.id, - backend: chardev_cfg.backend, + id: chardev_cfg.id(), + backend: chardev_cfg.classtype, listener: None, input: None, output: None, @@ -105,12 +105,12 @@ impl Chardev { pub fn realize(&mut self) -> Result<()> { match &self.backend { - ChardevType::Stdio => { + ChardevType::Stdio { .. } => { set_termi_raw_mode().with_context(|| "Failed to set terminal to raw mode")?; self.input = Some(Arc::new(Mutex::new(std::io::stdin()))); self.output = Some(Arc::new(Mutex::new(std::io::stdout()))); } - ChardevType::Pty => { + ChardevType::Pty { .. } => { let (master, path) = set_pty_raw_mode().with_context(|| "Failed to set pty to raw mode")?; info!("Pty path is: {:?}", path); @@ -125,58 +125,43 @@ impl Chardev { self.input = Some(master_arc.clone()); self.output = Some(master_arc); } - ChardevType::UnixSocket { - path, - server, - nowait, - } => { + ChardevType::Socket { server, nowait, .. } => { if !*server || !*nowait { bail!( "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", &self.id ); } - - clear_file(path.clone())?; - let listener = SocketListener::bind_by_uds(path).with_context(|| { - format!( - "Failed to bind socket for chardev \'{}\', path: {}", - &self.id, path - ) - })?; - self.listener = Some(listener); - - // add file to temporary pool, so it could be cleaned when vm exit. - TempCleaner::add_path(path.clone()); - limit_permission(path).with_context(|| { - format!( - "Failed to change file permission for chardev \'{}\', path: {}", - &self.id, path - ) - })?; - } - ChardevType::TcpSocket { - host, - port, - server, - nowait, - } => { - if !*server || !*nowait { - bail!( - "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", - &self.id - ); + let socket_type = self.backend.socket_type()?; + if let SocketType::Tcp { host, port } = socket_type { + let listener = SocketListener::bind_by_tcp(&host, port).with_context(|| { + format!( + "Failed to bind socket for chardev \'{}\', address: {}:{}", + &self.id, host, port + ) + })?; + self.listener = Some(listener); + } else if let SocketType::Unix { path } = socket_type { + clear_file(path.clone())?; + let listener = SocketListener::bind_by_uds(&path).with_context(|| { + format!( + "Failed to bind socket for chardev \'{}\', path: {}", + &self.id, path + ) + })?; + self.listener = Some(listener); + + // add file to temporary pool, so it could be cleaned when vm exit. + TempCleaner::add_path(path.clone()); + limit_permission(&path).with_context(|| { + format!( + "Failed to change file permission for chardev \'{}\', path: {}", + &self.id, path + ) + })?; } - - let listener = SocketListener::bind_by_tcp(host, *port).with_context(|| { - format!( - "Failed to bind socket for chardev \'{}\', address: {}:{}", - &self.id, host, port - ) - })?; - self.listener = Some(listener); } - ChardevType::File(path) => { + ChardevType::File { path, .. } => { let file = Arc::new(Mutex::new( OpenOptions::new() .read(true) @@ -510,11 +495,10 @@ impl EventNotifierHelper for Chardev { let notifier = { let backend = chardev.lock().unwrap().backend.clone(); match backend { - ChardevType::Stdio => get_terminal_notifier(chardev), - ChardevType::Pty => get_terminal_notifier(chardev), - ChardevType::UnixSocket { .. } => get_socket_notifier(chardev), - ChardevType::TcpSocket { .. } => get_socket_notifier(chardev), - ChardevType::File(_) => None, + ChardevType::Stdio { .. } => get_terminal_notifier(chardev), + ChardevType::Pty { .. } => get_terminal_notifier(chardev), + ChardevType::Socket { .. } => get_socket_notifier(chardev), + ChardevType::File { .. } => None, } }; notifier.map_or(Vec::new(), |value| vec![value]) diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 24a34ef8e..afe1e540e 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -486,8 +486,9 @@ mod test { #[test] fn test_receive() { let chardev_cfg = ChardevConfig { - id: "chardev".to_string(), - backend: ChardevType::Stdio, + classtype: ChardevType::Stdio { + id: "chardev".to_string(), + }, }; let mut pl011_dev = PL011::new(SerialConfig { chardev: chardev_cfg, diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 7c5b90729..8b096f099 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -490,8 +490,9 @@ mod test { fn test_methods_of_serial() { // test new method let chardev_cfg = ChardevConfig { - id: "chardev".to_string(), - backend: ChardevType::Stdio, + classtype: ChardevType::Stdio { + id: "chardev".to_string(), + }, }; let mut usart = Serial::new(SerialConfig { chardev: chardev_cfg.clone(), @@ -545,8 +546,9 @@ mod test { #[test] fn test_serial_migration_interface() { let chardev_cfg = ChardevConfig { - id: "chardev".to_string(), - backend: ChardevType::Stdio, + classtype: ChardevType::Stdio { + id: "chardev".to_string(), + }, }; let mut usart = Serial::new(SerialConfig { chardev: chardev_cfg, diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 5b0d2751e..659070188 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -13,7 +13,7 @@ use anyhow::{bail, Context, Result}; use crate::{ - config::{parse_trace_options, ChardevType, CmdParser, MachineType, VmConfig}, + config::{parse_trace_options, ChardevType, CmdParser, MachineType, SocketType, VmConfig}, qmp::qmp_socket::QmpSocketPath, temp_cleaner::TempCleaner, }; @@ -641,47 +641,33 @@ pub fn check_api_channel( .get_value::("chardev")? .with_context(|| "Argument \'chardev\' is missing for \'mon\'")?; - if let Some(mode) = cmd_parser.get_value::("mode")? { - if mode != *"control" { - bail!("Invalid \'mode\' parameter: {:?} for monitor", &mode); - } - } else { - bail!("Argument \'mode\' of \'mon\' should be set to \'control\'."); + let mode = cmd_parser + .get_value::("mode")? + .with_context(|| "Argument \'mode\' of \'mon\' should be set to \'control\'.")?; + if mode != "control" { + bail!("Invalid \'mode\' parameter: {:?} for monitor", &mode); } - if let Some(cfg) = vm_config.chardev.remove(&chardev) { - if let ChardevType::UnixSocket { - path, - server, - nowait, - } = cfg.backend - { - if !server || !nowait { - bail!( - "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", - path - ); - } - sock_paths.push(QmpSocketPath::Unix { path }); - } else if let ChardevType::TcpSocket { - host, - port, - server, - nowait, - } = cfg.backend - { - if !server || !nowait { - bail!( - "Argument \'server\' and \'nowait\' are both required for chardev \'{}:{}\'", - host, port - ); - } - sock_paths.push(QmpSocketPath::Tcp { host, port }); - } else { - bail!("Only chardev of unix-socket type can be used for monitor"); + let cfg = vm_config + .chardev + .remove(&chardev) + .with_context(|| format!("No chardev found: {}", &chardev))?; + let socket = cfg + .classtype + .socket_type() + .with_context(|| "Only chardev of unix-socket type can be used for monitor")?; + if let ChardevType::Socket { server, nowait, .. } = cfg.classtype { + if !server || !nowait { + bail!( + "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", + cfg.id() + ); } - } else { - bail!("No chardev found: {}", &chardev); + } + if let SocketType::Tcp { host, port } = socket { + sock_paths.push(QmpSocketPath::Tcp { host, port }); + } else if let SocketType::Unix { path } = socket { + sock_paths.push(QmpSocketPath::Unix { path }); } } diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 943de721b..f5ea60677 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -14,12 +14,14 @@ use std::net::IpAddr; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; +use clap::{ArgAction, Parser, Subcommand}; use log::error; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, get_pci_bdf, pci_args_check, PciBdf}; +use super::{error::ConfigError, get_pci_bdf, pci_args_check, str_slip_to_clap, PciBdf}; use crate::config::{ - check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, + check_arg_too_long, valid_id, valid_path, valid_socket_path, CmdParser, ConfigCheck, ExBool, + VmConfig, }; use crate::qmp::qmp_schema; @@ -29,25 +31,6 @@ const MIN_GUEST_CID: u64 = 3; /// Default value of max ports for virtio-serial. const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31; -/// Character device options. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum ChardevType { - Stdio, - Pty, - UnixSocket { - path: String, - server: bool, - nowait: bool, - }, - TcpSocket { - host: String, - port: u16, - server: bool, - nowait: bool, - }, - File(String), -} - /// Config structure for virtio-serial-port. #[derive(Debug, Clone)] pub struct VirtioSerialPort { @@ -64,198 +47,107 @@ impl ConfigCheck for VirtioSerialPort { } /// Config structure for character device. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct ChardevConfig { - pub id: String, - pub backend: ChardevType, + #[command(subcommand)] + pub classtype: ChardevType, } -impl ConfigCheck for ChardevConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "chardev id")?; - match &self.backend { - ChardevType::UnixSocket { path, .. } => { - if path.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "unix-socket path".to_string(), - MAX_PATH_LENGTH - ))); - } - Ok(()) - } - ChardevType::TcpSocket { host, port, .. } => { - if *port == 0u16 { - return Err(anyhow!(ConfigError::InvalidParam( - "port".to_string(), - "tcp-socket".to_string() - ))); - } - let ip_address = IpAddr::from_str(host); - if ip_address.is_err() { - return Err(anyhow!(ConfigError::InvalidParam( - "host".to_string(), - "tcp-socket".to_string() - ))); - } - Ok(()) - } - ChardevType::File(path) => { - if path.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "file path".to_string(), - MAX_PATH_LENGTH - ))); - } - Ok(()) - } - _ => Ok(()), +impl ChardevConfig { + pub fn id(&self) -> String { + match &self.classtype { + ChardevType::Stdio { id } => id, + ChardevType::Pty { id } => id, + ChardevType::Socket { id, .. } => id, + ChardevType::File { id, .. } => id, } + .clone() } } -fn check_chardev_fields( - dev_type: &str, - cmd_parser: &CmdParser, - supported_fields: &[&str], -) -> Result<()> { - for (field, value) in &cmd_parser.params { - let supported_field = supported_fields.contains(&field.as_str()); - if !supported_field && value.is_some() { - bail!( - "Chardev of type {} does not support \'{}\' argument", - dev_type, - field - ); +impl ConfigCheck for ChardevConfig { + fn check(&self) -> Result<()> { + if let ChardevType::Socket { .. } = self.classtype { + self.classtype.socket_type()?; } - } - Ok(()) -} - -fn parse_stdio_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { - let supported_fields = ["", "id"]; - check_chardev_fields("stdio", &cmd_parser, &supported_fields)?; - Ok(ChardevConfig { - id: chardev_id, - backend: ChardevType::Stdio, - }) -} -fn parse_pty_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { - let supported_fields = ["", "id"]; - check_chardev_fields("pty", &cmd_parser, &supported_fields)?; - Ok(ChardevConfig { - id: chardev_id, - backend: ChardevType::Pty, - }) + Ok(()) + } } -fn parse_file_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { - let supported_fields = ["", "id", "path"]; - check_chardev_fields("file", &cmd_parser, &supported_fields)?; - - let path = cmd_parser - .get_value::("path")? - .with_context(|| ConfigError::FieldIsMissing("path".to_string(), "chardev".to_string()))?; - - let default_value = path.clone(); - let file_path = std::fs::canonicalize(path).map_or(default_value, |canonical_path| { - String::from(canonical_path.to_str().unwrap()) - }); - - Ok(ChardevConfig { - id: chardev_id, - backend: ChardevType::File(file_path), - }) +/// Character device options. +#[derive(Subcommand, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum ChardevType { + Stdio { + #[arg(long, value_parser = valid_id)] + id: String, + }, + Pty { + #[arg(long, value_parser = valid_id)] + id: String, + }, + // Unix Socket: use `path`. + // Tcp Socket: use `host` and `port`. + #[clap(group = clap::ArgGroup::new("unix-socket").args(&["host", "port"]).requires("port").multiple(true).conflicts_with("tcp-socket"))] + #[clap(group = clap::ArgGroup::new("tcp-socket").arg("path").conflicts_with("unix-socket"))] + Socket { + #[arg(long, value_parser = valid_id)] + id: String, + #[arg(long, value_parser = valid_socket_path)] + path: Option, + #[arg(long, value_parser = valid_host, default_value = "0.0.0.0")] + host: String, + #[arg(long, value_parser = clap::value_parser!(u16).range(1..))] + port: Option, + #[arg(long, action = ArgAction::SetTrue)] + server: bool, + #[arg(long, action = ArgAction::SetTrue)] + nowait: bool, + }, + File { + #[arg(long, value_parser = valid_id)] + id: String, + #[arg(long, value_parser = valid_path)] + path: String, + }, } -fn parse_socket_chardev(chardev_id: String, cmd_parser: CmdParser) -> Result { - let mut server_enabled = false; - let server = cmd_parser.get_value::("server")?; - if let Some(server) = server { - if server.ne("") { - bail!("No parameter needed for server"); - } - server_enabled = true; - } - - let mut nowait_enabled = false; - let nowait = cmd_parser.get_value::("nowait")?; - if let Some(nowait) = nowait { - if nowait.ne("") { - bail!("No parameter needed for nowait"); +impl ChardevType { + pub fn socket_type(&self) -> Result { + if let ChardevType::Socket { + path, host, port, .. + } = self + { + if path.is_some() && port.is_none() { + return Ok(SocketType::Unix { + path: path.clone().unwrap(), + }); + } else if port.is_some() && path.is_none() { + return Ok(SocketType::Tcp { + host: host.clone(), + port: (*port).unwrap(), + }); + } } - nowait_enabled = true; - } - - let path = cmd_parser.get_value::("path")?; - if let Some(path) = path { - let supported_fields = ["", "id", "path", "server", "nowait"]; - check_chardev_fields("unix-socket", &cmd_parser, &supported_fields)?; - - let default_value = path.clone(); - let socket_path = std::fs::canonicalize(path).map_or(default_value, |canonical_path| { - String::from(canonical_path.to_str().unwrap()) - }); - - return Ok(ChardevConfig { - id: chardev_id, - backend: ChardevType::UnixSocket { - path: socket_path, - server: server_enabled, - nowait: nowait_enabled, - }, - }); - } - - let port = cmd_parser.get_value::("port")?; - if let Some(port) = port { - let supported_fields = ["", "id", "host", "port", "server", "nowait"]; - check_chardev_fields("tcp-socket", &cmd_parser, &supported_fields)?; - - let host = cmd_parser.get_value::("host")?; - return Ok(ChardevConfig { - id: chardev_id, - backend: ChardevType::TcpSocket { - host: host.unwrap_or_else(|| String::from("0.0.0.0")), - port, - server: server_enabled, - nowait: nowait_enabled, - }, - }); + bail!("Not socket type or invalid socket type"); } - - Err(anyhow!(ConfigError::InvalidParam( - "backend".to_string(), - "chardev".to_string() - ))) } -pub fn parse_chardev(chardev_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("chardev"); - for field in ["", "id", "path", "host", "port", "server", "nowait"] { - cmd_parser.push(field); - } - - cmd_parser.parse(chardev_config)?; +pub enum SocketType { + Unix { path: String }, + Tcp { host: String, port: u16 }, +} - let chardev_id = cmd_parser - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "chardev".to_string()))?; - - let backend = cmd_parser - .get_value::("")? - .with_context(|| ConfigError::InvalidParam("backend".to_string(), "chardev".to_string()))?; - - match backend.as_str() { - "stdio" => parse_stdio_chardev(chardev_id, cmd_parser), - "pty" => parse_pty_chardev(chardev_id, cmd_parser), - "file" => parse_file_chardev(chardev_id, cmd_parser), - "socket" => parse_socket_chardev(chardev_id, cmd_parser), - _ => Err(anyhow!(ConfigError::InvalidParam( - backend, - "chardev".to_string() - ))), +fn valid_host(host: &str) -> Result { + let ip_address = IpAddr::from_str(host); + if ip_address.is_err() { + return Err(anyhow!(ConfigError::InvalidParam( + "host".to_string(), + "tcp-socket".to_string() + ))); } + Ok(host.to_string()) } /// Get chardev config from qmp arguments. @@ -291,9 +183,11 @@ pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result Result Result { - if let Some(char_dev) = vm_config.chardev.remove(chardev) { - match char_dev.backend.clone() { - ChardevType::UnixSocket { - path, - server, - nowait, - } => { - if server || nowait { - bail!( - "Argument \'server\' or \'nowait\' is not need for chardev \'{}\'", - path - ); - } - Ok(path) - } - _ => { - bail!( - "Chardev {:?} backend should be unix-socket type.", - &char_dev.id - ); - } + let char_dev = vm_config + .chardev + .remove(chardev) + .with_context(|| format!("Chardev: {:?} not found for character device", chardev))?; + if let ChardevType::Socket { + path, + server, + nowait, + .. + } = char_dev.classtype + { + path.clone().with_context(|| { + format!("Chardev {:?} backend should be unix-socket type.", chardev) + })?; + if server || nowait { + bail!( + "Argument \'server\' or \'nowait\' is not need for chardev \'{}\'", + path.unwrap() + ); } - } else { - bail!("Chardev: {:?} not found for character device", &chardev); + return Ok(path.unwrap()); } + bail!("Chardev {:?} backend should be unix-socket type.", chardev); } pub fn parse_virtserialport( @@ -378,14 +270,9 @@ pub fn parse_virtserialport( impl VmConfig { /// Add chardev config to `VmConfig`. pub fn add_chardev(&mut self, chardev_config: &str) -> Result<()> { - let chardev = parse_chardev(chardev_config)?; + let chardev = ChardevConfig::try_parse_from(str_slip_to_clap(chardev_config, true, true))?; chardev.check()?; - let chardev_id = chardev.id.clone(); - if self.chardev.get(&chardev_id).is_none() { - self.chardev.insert(chardev_id, chardev); - } else { - bail!("Chardev {:?} has been added", &chardev_id); - } + self.add_chardev_with_config(chardev)?; Ok(()) } @@ -395,16 +282,11 @@ impl VmConfig { /// /// * `conf` - The chardev config to be added to the vm. pub fn add_chardev_with_config(&mut self, conf: ChardevConfig) -> Result<()> { - if let Err(e) = conf.check() { - bail!("Chardev config checking failed, {}", e.to_string()); - } - - let chardev_id = conf.id.clone(); - if self.chardev.get(&chardev_id).is_none() { - self.chardev.insert(chardev_id, conf); - } else { + let chardev_id = conf.id(); + if self.chardev.get(&chardev_id).is_some() { bail!("Chardev {:?} has been added", chardev_id); } + self.chardev.insert(chardev_id, conf); Ok(()) } @@ -414,11 +296,9 @@ impl VmConfig { /// /// * `id` - The chardev id which is used to delete chardev config. pub fn del_chardev_by_id(&mut self, id: &str) -> Result<()> { - if self.chardev.get(id).is_some() { - self.chardev.remove(id); - } else { - bail!("Chardev {} not found", id); - } + self.chardev + .remove(id) + .with_context(|| format!("Chardev {} not found", id))?; Ok(()) } } @@ -609,7 +489,7 @@ mod tests { let console_cfg = virt_console.unwrap(); assert_eq!(console_cfg.id, "console1"); - assert_eq!(console_cfg.chardev.backend, expected_chardev); + assert_eq!(console_cfg.chardev.classtype, expected_chardev); let mut vm_config = VmConfig::default(); assert!( @@ -639,8 +519,11 @@ mod tests { #[test] fn test_mmio_console_config_cmdline_parser_1() { let chardev_cfg = "socket,id=test_console,path=/path/to/socket,server,nowait"; - let expected_chardev = ChardevType::UnixSocket { - path: "/path/to/socket".to_string(), + let expected_chardev = ChardevType::Socket { + id: "test_console".to_string(), + path: Some("/path/to/socket".to_string()), + host: "0.0.0.0".to_string(), + port: None, server: true, nowait: true, }; @@ -650,9 +533,11 @@ mod tests { #[test] fn test_mmio_console_config_cmdline_parser_2() { let chardev_cfg = "socket,id=test_console,host=127.0.0.1,port=9090,server,nowait"; - let expected_chardev = ChardevType::TcpSocket { + let expected_chardev = ChardevType::Socket { + id: "test_console".to_string(), + path: None, host: "127.0.0.1".to_string(), - port: 9090, + port: Some(9090), server: true, nowait: true, }; @@ -681,7 +566,7 @@ mod tests { let bdf = serial_info.pci_bdf.unwrap(); assert_eq!(bdf.bus, "pcie.0"); assert_eq!(bdf.addr, (1, 2)); - assert_eq!(console_cfg.chardev.backend, expected_chardev); + assert_eq!(console_cfg.chardev.classtype, expected_chardev); let mut vm_config = VmConfig::default(); assert!(parse_virtio_serial( @@ -694,8 +579,11 @@ mod tests { #[test] fn test_pci_console_config_cmdline_parser_1() { let chardev_cfg = "socket,id=test_console,path=/path/to/socket,server,nowait"; - let expected_chardev = ChardevType::UnixSocket { - path: "/path/to/socket".to_string(), + let expected_chardev = ChardevType::Socket { + id: "test_console".to_string(), + path: Some("/path/to/socket".to_string()), + host: "0.0.0.0".to_string(), + port: None, server: true, nowait: true, }; @@ -705,9 +593,11 @@ mod tests { #[test] fn test_pci_console_config_cmdline_parser_2() { let chardev_cfg = "socket,id=test_console,host=127.0.0.1,port=9090,server,nowait"; - let expected_chardev = ChardevType::TcpSocket { + let expected_chardev = ChardevType::Socket { + id: "test_console".to_string(), + path: None, host: "127.0.0.1".to_string(), - port: 9090, + port: Some(9090), server: true, nowait: true, }; @@ -744,17 +634,30 @@ mod tests { let device_id = "test_id"; if let Some(char_dev) = vm_config.chardev.remove(device_id) { - assert_eq!(char_dev.backend, expect); + assert_eq!(char_dev.classtype, expect); } else { assert!(false); } }; - check_argument("stdio,id=test_id".to_string(), ChardevType::Stdio); - check_argument("pty,id=test_id".to_string(), ChardevType::Pty); + check_argument( + "stdio,id=test_id".to_string(), + ChardevType::Stdio { + id: "test_id".to_string(), + }, + ); + check_argument( + "pty,id=test_id".to_string(), + ChardevType::Pty { + id: "test_id".to_string(), + }, + ); check_argument( "file,id=test_id,path=/some/file".to_string(), - ChardevType::File("/some/file".to_string()), + ChardevType::File { + id: "test_id".to_string(), + path: "/some/file".to_string(), + }, ); let extra_params = [ @@ -767,17 +670,22 @@ mod tests { for (param, server_state, nowait_state) in extra_params { check_argument( format!("{}{}", "socket,id=test_id,path=/path/to/socket", param), - ChardevType::UnixSocket { - path: "/path/to/socket".to_string(), + ChardevType::Socket { + id: "test_id".to_string(), + path: Some("/path/to/socket".to_string()), + host: "0.0.0.0".to_string(), + port: None, server: server_state, nowait: nowait_state, }, ); check_argument( format!("{}{}", "socket,id=test_id,port=9090", param), - ChardevType::TcpSocket { + ChardevType::Socket { + id: "test_id".to_string(), + path: None, host: "0.0.0.0".to_string(), - port: 9090, + port: Some(9090), server: server_state, nowait: nowait_state, }, @@ -787,9 +695,11 @@ mod tests { "{}{}", "socket,id=test_id,host=172.56.16.12,port=7070", param ), - ChardevType::TcpSocket { + ChardevType::Socket { + id: "test_id".to_string(), + path: None, host: "172.56.16.12".to_string(), - port: 7070, + port: Some(7070), server: server_state, nowait: nowait_state, }, diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 3e787c0ca..a58d00129 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -20,12 +20,12 @@ use clap::{ArgAction, Parser}; use log::error; use serde::{Deserialize, Serialize}; -use super::valid_id; use super::{error::ConfigError, pci_args_check, M}; +use super::{valid_id, valid_path}; use crate::config::{ check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, parse_bool, - str_slip_to_clap, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_PATH_LENGTH, - MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE, + str_slip_to_clap, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_STRING_LENGTH, + MAX_VIRTIO_QUEUE, }; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; @@ -179,16 +179,6 @@ fn valid_refcount_cache_size(s: &str) -> Result { Ok(size) } -fn valid_path(path: &str) -> Result { - if path.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "Drive device path".to_string(), - MAX_PATH_LENGTH, - ))); - } - Ok(path.to_string()) -} - /// Config struct for `drive`, including `block drive` and `pflash drive`. #[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] #[command(no_binary_name(true))] diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index e1a16ab35..6900888fc 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -12,10 +12,10 @@ use anyhow::{anyhow, bail, Context, Result}; -use super::error::ConfigError; +use super::{error::ConfigError, SocketType}; use crate::config::{ - pci_args_check, ChardevType, CmdParser, ConfigCheck, VmConfig, MAX_SOCK_PATH_LENGTH, - MAX_STRING_LENGTH, MAX_TAG_LENGTH, + pci_args_check, CmdParser, ConfigCheck, VmConfig, MAX_SOCK_PATH_LENGTH, MAX_STRING_LENGTH, + MAX_TAG_LENGTH, }; /// Config struct for `fs`. @@ -90,26 +90,22 @@ pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { ..Default::default() }; - if let Some(name) = cmd_parser.get_value::("chardev")? { - if let Some(char_dev) = vm_config.chardev.remove(&name) { - match &char_dev.backend { - ChardevType::UnixSocket { path, .. } => { - fs_cfg.sock = path.clone(); - } - _ => { - bail!("Chardev {:?} backend should be unix-socket type.", &name); - } - } - } else { - bail!("Chardev {:?} not found or is in use", &name); + let name = cmd_parser + .get_value::("chardev")? + .with_context(|| { + ConfigError::FieldIsMissing("chardev".to_string(), "virtio-fs".to_string()) + })?; + let char_dev = vm_config + .chardev + .remove(&name) + .with_context(|| format!("Chardev {:?} not found or is in use", &name))?; + match char_dev.classtype.socket_type()? { + SocketType::Unix { path } => { + fs_cfg.sock = path; } - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "chardev".to_string(), - "virtio-fs".to_string() - ))); - } - fs_cfg.check()?; + _ => bail!("Chardev {:?} backend should be unix-socket type.", &name), + }; + fs_cfg.check()?; Ok(fs_cfg) } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index b34540cb3..411dc3ff2 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -80,7 +80,7 @@ pub use vfio::*; pub use vnc::*; use std::collections::HashMap; -use std::fs::File; +use std::fs::{canonicalize, File}; use std::io::Read; use std::str::FromStr; @@ -179,12 +179,12 @@ impl VmConfig { let mut stdio_count = 0; if let Some(serial) = self.serial.as_ref() { - if serial.chardev.backend == ChardevType::Stdio { + if let ChardevType::Stdio { .. } = serial.chardev.classtype { stdio_count += 1; } } for (_, char_dev) in self.chardev.clone() { - if char_dev.backend == ChardevType::Stdio { + if let ChardevType::Stdio { .. } = char_dev.classtype { stdio_count += 1; } } @@ -816,6 +816,31 @@ pub fn valid_virtqueue_size(size: u64, min_size: u64, max_size: u64) -> Result<( Ok(()) } +pub fn valid_path(path: &str) -> Result { + if path.len() > MAX_PATH_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "path".to_string(), + MAX_PATH_LENGTH, + ))); + } + + let canonical_path = canonicalize(path).map_or(path.to_string(), |pathbuf| { + String::from(pathbuf.to_str().unwrap()) + }); + + Ok(canonical_path) +} + +pub fn valid_socket_path(sock_path: &str) -> Result { + if sock_path.len() > MAX_SOCK_PATH_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "socket path".to_string(), + MAX_SOCK_PATH_LENGTH, + ))); + } + valid_path(sock_path) +} + #[cfg(test)] mod tests { use super::*; diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 5bf1c101f..57955619a 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -357,7 +357,10 @@ impl SerialPort { pub fn new(port_cfg: VirtioSerialPort) -> Self { // Console is default host connected. And pty chardev has opened by default in realize() // function. - let host_connected = port_cfg.is_console || port_cfg.chardev.backend == ChardevType::Pty; + let mut host_connected = port_cfg.is_console; + if let ChardevType::Pty { .. } = port_cfg.chardev.classtype { + host_connected = true; + } SerialPort { name: Some(port_cfg.id), -- Gitee From cd4bd1d115defb70469a27c04ff49f6e8573c72d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 12:45:15 +0800 Subject: [PATCH 1770/2187] vfio: use clap to parse the parameters of the vfio config Use clap to parse the parameters of the vfio config. Signed-off-by: liuxiangdong --- Cargo.lock | 1 + machine/src/lib.rs | 53 ++++-------- machine_manager/src/config/mod.rs | 2 - machine_manager/src/config/vfio.rs | 134 ----------------------------- vfio/Cargo.toml | 1 + vfio/src/lib.rs | 2 +- vfio/src/vfio_pci.rs | 57 ++++++++++++ 7 files changed, 78 insertions(+), 172 deletions(-) delete mode 100644 machine_manager/src/config/vfio.rs diff --git a/Cargo.lock b/Cargo.lock index b53bd7ae2..dfcfbe290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1855,6 +1855,7 @@ dependencies = [ "address_space", "anyhow", "byteorder", + "clap", "devices", "hypervisor", "kvm-bindings", diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6b0be7dfd..86ae94e40 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -81,10 +81,10 @@ use machine_manager::config::parse_pvpanic; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, - parse_root_port, parse_vfio, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, - parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, + parse_root_port, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, + str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, - SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + SerialConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -96,7 +96,7 @@ use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; -use vfio::{VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; +use vfio::{VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(feature = "virtio_gpu")] use virtio::Gpu; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] @@ -1274,49 +1274,32 @@ pub trait MachineOps { Ok(()) } - fn create_vfio_pci_device( - &mut self, - id: &str, - bdf: &PciBdf, - host: &str, - sysfsdev: &str, - multifunc: bool, - ) -> Result<()> { - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?; - let path = if !host.is_empty() { - format!("/sys/bus/pci/devices/{}", host) + fn add_vfio_device(&mut self, cfg_args: &str, hotplug: bool) -> Result<()> { + let hypervisor = self.get_hypervisor(); + let locked_hypervisor = hypervisor.lock().unwrap(); + *KVM_DEVICE_FD.lock().unwrap() = locked_hypervisor.create_vfio_device(); + + let device_cfg = VfioConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let bdf = PciBdf::new(device_cfg.bus.clone(), device_cfg.addr); + let multi_func = device_cfg.multifunction.unwrap_or_default(); + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let path = if device_cfg.host.is_some() { + format!("/sys/bus/pci/devices/{}", device_cfg.host.unwrap()) } else { - sysfsdev.to_string() + device_cfg.sysfsdev.unwrap() }; let device = VfioDevice::new(Path::new(&path), self.get_sys_mem()) .with_context(|| "Failed to create vfio device.")?; let vfio_pci = VfioPciDevice::new( device, devfn, - id.to_string(), + device_cfg.id.to_string(), parent_bus, - multifunc, + multi_func, self.get_sys_mem().clone(), ); VfioPciDevice::realize(vfio_pci).with_context(|| "Failed to realize vfio-pci device.")?; - Ok(()) - } - - fn add_vfio_device(&mut self, cfg_args: &str, hotplug: bool) -> Result<()> { - let hypervisor = self.get_hypervisor(); - let locked_hypervisor = hypervisor.lock().unwrap(); - *KVM_DEVICE_FD.lock().unwrap() = locked_hypervisor.create_vfio_device(); - let device_cfg: VfioConfig = parse_vfio(cfg_args)?; - let bdf = get_pci_bdf(cfg_args)?; - let multifunc = get_multi_function(cfg_args)?; - self.create_vfio_pci_device( - &device_cfg.id, - &bdf, - &device_cfg.host, - &device_cfg.sysfsdev, - multifunc, - )?; if !hotplug { self.reset_bus(&device_cfg.id)?; } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 411dc3ff2..d399391d5 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -43,7 +43,6 @@ mod sasl_auth; mod smbios; #[cfg(feature = "vnc_auth")] mod tls_creds; -mod vfio; pub use boot_source::*; #[cfg(feature = "usb_camera")] @@ -75,7 +74,6 @@ pub use sasl_auth::*; pub use smbios::*; #[cfg(feature = "vnc_auth")] pub use tls_creds::*; -pub use vfio::*; #[cfg(feature = "vnc")] pub use vnc::*; diff --git a/machine_manager/src/config/vfio.rs b/machine_manager/src/config/vfio.rs deleted file mode 100644 index dddebde74..000000000 --- a/machine_manager/src/config/vfio.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{anyhow, Result}; - -use super::error::ConfigError; -use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; - -#[derive(Default, Debug)] -pub struct VfioConfig { - pub sysfsdev: String, - pub host: String, - pub id: String, -} - -impl ConfigCheck for VfioConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.host, "host")?; - check_arg_too_long(&self.id, "id")?; - - Ok(()) - } -} - -pub fn parse_vfio(vfio_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("vfio-pci"); - cmd_parser - .push("") - .push("host") - .push("sysfsdev") - .push("id") - .push("bus") - .push("addr") - .push("multifunction"); - cmd_parser.parse(vfio_config)?; - - let mut vfio: VfioConfig = VfioConfig::default(); - if let Some(host) = cmd_parser.get_value::("host")? { - vfio.host = host; - } - - if let Some(sysfsdev) = cmd_parser.get_value::("sysfsdev")? { - vfio.sysfsdev = sysfsdev; - } - - if vfio.host.is_empty() && vfio.sysfsdev.is_empty() { - return Err(anyhow!(ConfigError::FieldIsMissing( - "host nor sysfsdev".to_string(), - "vfio".to_string() - ))); - } - - if !vfio.host.is_empty() && !vfio.sysfsdev.is_empty() { - return Err(anyhow!(ConfigError::InvalidParam( - "host and sysfsdev".to_string(), - "vfio".to_string() - ))); - } - - if let Some(id) = cmd_parser.get_value::("id")? { - vfio.id = id; - } - vfio.check()?; - - Ok(vfio) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::get_pci_bdf; - - #[test] - fn test_check_vfio_config() { - let mut vfio_config = - parse_vfio("vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x1.0x2").unwrap(); - assert!(vfio_config.check().is_ok()); - - vfio_config.host = "IYqUdAMXggoUMU28eBJCxQGUirYYSyW1cfGJI3ZpZAzMFCKnVPA5e7gnurLtXjCm\ - YoG5pfqRDbN7M2dpSd8fzSbufAJaor8UY9xbH7BybZ7WDEFmkxgCQp6PWgaBSmLOCe1tEMs4RQ938ZLnh8ej\ - Q81VovbrU7ecafacCn9AJQoidN3Seab3QOEd4SJbtd4hAPeYvsXLVa6xOZxtVjqjRxk9b36feF0C5JrucVcs\ - QsusZZtVfUFUZxOoV8JltVsBmdasnic" - .to_string(); - assert!(vfio_config.check().is_err()); - - vfio_config.id = "LPwM1h4QUTCjL4fX2gFdCdPrF9S0kGHf0onpU6E4fyI6Jmzg0DCM9sffvEVjaVu1ilp\ - 2OrgCWzvNBflYvUUihPj3ePPYs3erSHmSOmQZbnGEFsiBSTJHfPAsRtWJoipeIh9cgIR1tnU3OjwPPli4gmb6\ - E6GgSyMd0oQtUGFyNf5pRHlYqlx3s7PMPVUtRJP0bBnNd5eDwWAotInu33h6UI0zfKgckAxeVdEROKAExx5xWK\ - V3AgPhvvPzFx3chYymy" - .to_string(); - assert!(vfio_config.check().is_err()); - } - - #[test] - fn test_vfio_config_cmdline_parser() { - let vfio_cfg = parse_vfio("vfio-pci,host=0000:1a:00.3,id=net"); - assert!(vfio_cfg.is_ok()); - let vfio_config = vfio_cfg.unwrap(); - assert_eq!(vfio_config.host, "0000:1a:00.3"); - assert_eq!(vfio_config.id, "net"); - } - - #[test] - fn test_pci_vfio_config_cmdline_parser() { - let vfio_cfg1 = "vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x1.0x2"; - let config1 = parse_vfio(vfio_cfg1); - assert!(config1.is_ok()); - let vfio_cfg2 = "vfio-pci,host=0000:1a:00.3,bus=pcie.0,addr=0x1.0x2"; - let config2 = parse_vfio(vfio_cfg2); - assert!(config2.is_ok()); - let vfio_cfg3 = "vfio-pci,id=net,bus=pcie.0,addr=0x1.0x2"; - let config3 = parse_vfio(vfio_cfg3); - assert!(config3.is_err()); - - let pci_bdf = get_pci_bdf(vfio_cfg1); - assert!(pci_bdf.is_ok()); - let pci = pci_bdf.unwrap(); - assert_eq!(pci.bus, "pcie.0".to_string()); - assert_eq!(pci.addr, (1, 2)); - - let vfio_cfg1 = - "vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x1.0x2,multifunction=on"; - assert!(parse_vfio(vfio_cfg1).is_ok()); - } -} diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index ca4a130f7..94d7b5e4b 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -22,3 +22,4 @@ hypervisor = { path = "../hypervisor"} machine_manager = { path = "../machine_manager" } util = { path = "../util" } devices = { path = "../devices" } +clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index 49c77af17..ab47b6cef 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -22,7 +22,7 @@ pub use vfio_dev::{ VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, VFIO_GROUP_SET_CONTAINER, VFIO_IOMMU_MAP_DMA, VFIO_IOMMU_UNMAP_DMA, VFIO_SET_IOMMU, }; -pub use vfio_pci::VfioPciDevice; +pub use vfio_pci::{VfioConfig, VfioPciDevice}; use std::collections::HashMap; use std::os::unix::io::RawFd; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index d354098ee..baca7bfeb 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -17,6 +17,7 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; +use clap::{ArgAction, Parser}; use log::error; use vfio_bindings::bindings::vfio; use vmm_sys_util::eventfd::EventFd; @@ -43,12 +44,33 @@ use devices::pci::{ pci_ext_cap_next, pci_ext_cap_ver, PciBus, PciDevBase, PciDevOps, }; use devices::{pci::MsiVector, Device, DeviceBase}; +use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use util::num_ops::ranges_overlap; use util::unix::host_page_size; const PCI_NUM_BARS: u8 = 6; const PCI_ROM_SLOT: u8 = 6; +#[derive(Parser, Default, Debug)] +#[command(no_binary_name(true))] +#[clap(group = clap::ArgGroup::new("path").args(&["host", "sysfsdev"]).multiple(false).required(true))] +pub struct VfioConfig { + #[arg(long, value_parser = ["vfio-pci"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long, value_parser = valid_id)] + pub host: Option, + #[arg(long)] + pub bus: String, + #[arg(long)] + pub sysfsdev: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + #[arg(long, value_parser = parse_bool, action = ArgAction::Append)] + pub multifunction: Option, +} + struct MsixTable { table_bar: u8, table_offset: u64, @@ -1009,3 +1031,38 @@ fn get_irq_rawfds(gsi_msi_routes: &[GsiMsiRoute], start: u32, count: u32) -> Vec } rawfds } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + + #[test] + fn test_vfio_config_cmdline_parser() { + // Test1: right. + let vfio_cmd1 = "vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x5,multifunction=on"; + let result = VfioConfig::try_parse_from(str_slip_to_clap(vfio_cmd1, true, false)); + assert!(result.is_ok()); + let vfio_config = result.unwrap(); + assert_eq!(vfio_config.host, Some("0000:1a:00.3".to_string())); + assert_eq!(vfio_config.id, "net"); + assert_eq!(vfio_config.bus, "pcie.0"); + assert_eq!(vfio_config.addr, (5, 0)); + assert_eq!(vfio_config.multifunction, Some(true)); + + // Test2: Missing bus/addr. + let vfio_cmd2 = "vfio-pci,host=0000:1a:00.3,id=net"; + let result = VfioConfig::try_parse_from(str_slip_to_clap(vfio_cmd2, true, false)); + assert!(result.is_err()); + + // Test3: `host` conflicts with `sysfsdev`. + let vfio_cmd3 = "vfio-pci,host=0000:1a:00.3,sysfsdev=/sys/bus/pci/devices/0000:00:02.0,id=net,bus=pcie.0,addr=0x5"; + let result = VfioConfig::try_parse_from(str_slip_to_clap(vfio_cmd3, true, false)); + assert!(result.is_err()); + + // Test4: Missing host/sysfsdev. + let vfio_cmd4 = "vfio-pci,id=net,bus=pcie.0,addr=0x1.0x2"; + let result = VfioConfig::try_parse_from(str_slip_to_clap(vfio_cmd4, true, false)); + assert!(result.is_err()); + } +} -- Gitee From 8a2f71a3aeea673b6b835e37b7deb8fd89e92921 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 13:57:58 +0800 Subject: [PATCH 1771/2187] demo-pci: use clap to parse the parameters of the demo-pci config Use clap to parse the parameters of the demo-pci config. Signed-off-by: liuxiangdong --- devices/src/pci/demo_device/mod.rs | 63 +++++++++++++++-- machine/src/lib.rs | 18 ++--- machine_manager/src/config/demo_dev.rs | 97 -------------------------- machine_manager/src/config/mod.rs | 4 -- 4 files changed, 64 insertions(+), 118 deletions(-) delete mode 100644 machine_manager/src/config/demo_dev.rs diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 778953667..ff35f475a 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -42,6 +42,7 @@ use std::{ }; use anyhow::{bail, Result}; +use clap::Parser; use log::error; use crate::pci::demo_device::{ @@ -57,7 +58,30 @@ use crate::pci::{ use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; use crate::{Device, DeviceBase}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; -use machine_manager::config::DemoDevConfig; +use machine_manager::config::{get_pci_df, valid_id}; + +/// Config struct for `demo_dev`. +/// Contains demo_dev device's attr. +#[derive(Parser, Debug, Clone)] +#[command(no_binary_name(true))] +pub struct DemoDevConfig { + #[arg(long, value_parser = ["pcie-demo-dev"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + // Different device implementations can be configured based on this parameter + #[arg(long, alias = "device_type")] + pub device_type: Option, + #[arg(long, alias = "bar_num", default_value = "0")] + pub bar_num: u8, + // Every bar has the same size just for simplification. + #[arg(long, alias = "bar_size", default_value = "0")] + pub bar_size: u64, +} pub struct DemoDev { base: PciDevBase, @@ -71,14 +95,15 @@ impl DemoDev { pub fn new( cfg: DemoDevConfig, devfn: u8, - _sys_mem: Arc, + sys_mem: Arc, parent_bus: Weak>, ) -> Self { // You can choose different device function based on the parameter of device_type. - let device: Arc> = match cfg.device_type.as_str() { - "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(_sys_mem, cfg.id.clone()))), - "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(_sys_mem))), - "demo-display" => Arc::new(Mutex::new(DemoDisplay::new(_sys_mem))), + let device_type = cfg.device_type.clone().unwrap_or_default(); + let device: Arc> = match device_type.as_str() { + "demo-gpu" => Arc::new(Mutex::new(DemoGpu::new(sys_mem, cfg.id.clone()))), + "demo-input" => Arc::new(Mutex::new(DemoKbdMouse::new(sys_mem))), + "demo-display" => Arc::new(Mutex::new(DemoDisplay::new(sys_mem))), _ => Arc::new(Mutex::new(BaseDevice::new())), }; DemoDev { @@ -234,3 +259,29 @@ pub trait DeviceTypeOperation: Send { fn realize(&mut self) -> Result<()>; fn unrealize(&mut self) -> Result<()>; } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + #[test] + fn test_parse_demo_dev() { + // Test1: Right. + let demo_cmd1 = "pcie-demo-dev,bus=pcie.0,addr=0x4,id=test_0,device_type=demo-gpu,bar_num=3,bar_size=4096"; + let result = DemoDevConfig::try_parse_from(str_slip_to_clap(demo_cmd1, true, false)); + assert!(result.is_ok()); + let demo_cfg = result.unwrap(); + assert_eq!(demo_cfg.id, "test_0".to_string()); + assert_eq!(demo_cfg.device_type, Some("demo-gpu".to_string())); + assert_eq!(demo_cfg.bar_num, 3); + assert_eq!(demo_cfg.bar_size, 4096); + + // Test2: Default bar_num/bar_size. + let demo_cmd2 = "pcie-demo-dev,bus=pcie.0,addr=4.0,id=test_0,device_type=demo-gpu"; + let result = DemoDevConfig::try_parse_from(str_slip_to_clap(demo_cmd2, true, false)); + assert!(result.is_ok()); + let demo_cfg = result.unwrap(); + assert_eq!(demo_cfg.bar_num, 0); + assert_eq!(demo_cfg.bar_size, 0); + } +} diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 86ae94e40..fbd20a6d4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -51,7 +51,7 @@ use devices::misc::pvpanic::PvPanicPci; #[cfg(feature = "scream")] use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] -use devices::pci::demo_device::DemoDev; +use devices::pci::demo_device::{DemoDev, DemoDevConfig}; use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; @@ -72,8 +72,6 @@ use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; -#[cfg(feature = "demo_device")] -use machine_manager::config::parse_demo_dev; #[cfg(feature = "virtio_gpu")] use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] @@ -1805,7 +1803,7 @@ pub trait MachineOps { #[cfg(feature = "ramfb")] ("ramfb", add_ramfb, cfg_args), #[cfg(feature = "demo_device")] - ("pcie-demo-dev", add_demo_dev, vm_config, cfg_args), + ("pcie-demo-dev", add_demo_dev, cfg_args), #[cfg(feature = "scream")] ("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args, token_id), #[cfg(feature = "pvpanic")] @@ -1833,15 +1831,13 @@ pub trait MachineOps { } #[cfg(feature = "demo_device")] - fn add_demo_dev(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - - let demo_cfg = parse_demo_dev(vm_config, cfg_args.to_string()) + fn add_demo_dev(&mut self, cfg_args: &str) -> Result<()> { + let config = DemoDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false)) .with_context(|| "failed to parse cmdline for demo dev.")?; - + let bdf = PciBdf::new(config.bus.clone(), config.addr); + let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); - let demo_dev = DemoDev::new(demo_cfg, devfn, sys_mem, parent_bus); + let demo_dev = DemoDev::new(config, devfn, sys_mem, parent_bus); demo_dev.realize() } diff --git a/machine_manager/src/config/demo_dev.rs b/machine_manager/src/config/demo_dev.rs deleted file mode 100644 index 10d21994e..000000000 --- a/machine_manager/src/config/demo_dev.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{bail, Result}; - -use super::{pci_args_check, CmdParser, VmConfig}; - -/// Config struct for `demo_dev`. -/// Contains demo_dev device's attr. -#[derive(Debug, Clone)] -pub struct DemoDevConfig { - pub id: String, - // Different device implementations can be configured based on this parameter - pub device_type: String, - pub bar_num: u8, - // Every bar has the same size just for simplification. - pub bar_size: u64, -} - -impl DemoDevConfig { - pub fn new() -> Self { - Self { - id: "".to_string(), - device_type: "".to_string(), - bar_num: 0, - bar_size: 0, - } - } -} - -impl Default for DemoDevConfig { - fn default() -> Self { - Self::new() - } -} - -pub fn parse_demo_dev(_vm_config: &mut VmConfig, args_str: String) -> Result { - let mut cmd_parser = CmdParser::new("demo-dev"); - cmd_parser - .push("") - .push("id") - .push("addr") - .push("device_type") - .push("bus") - .push("bar_num") - .push("bar_size"); - cmd_parser.parse(&args_str)?; - - pci_args_check(&cmd_parser)?; - - let mut demo_dev_cfg = DemoDevConfig::new(); - - if let Some(id) = cmd_parser.get_value::("id")? { - demo_dev_cfg.id = id; - } else { - bail!("No id configured for demo device"); - } - - if let Some(device_type) = cmd_parser.get_value::("device_type")? { - demo_dev_cfg.device_type = device_type; - } - - if let Some(bar_num) = cmd_parser.get_value::("bar_num")? { - demo_dev_cfg.bar_num = bar_num; - } - - // todo: support parsing hex num "0x**". It just supports decimal number now. - if let Some(bar_size) = cmd_parser.get_value::("bar_size")? { - demo_dev_cfg.bar_size = bar_size; - } - - Ok(demo_dev_cfg) -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_parse_demo_dev() { - let mut vm_config = VmConfig::default(); - let config_line = "-device pcie-demo-dev,bus=pcie.0,addr=4.0,id=test_0,device_type=demo-gpu,bar_num=3,bar_size=4096"; - let demo_cfg = parse_demo_dev(&mut vm_config, config_line.to_string()).unwrap(); - assert_eq!(demo_cfg.id, "test_0".to_string()); - assert_eq!(demo_cfg.device_type, "demo-gpu".to_string()); - assert_eq!(demo_cfg.bar_num, 3); - assert_eq!(demo_cfg.bar_size, 4096); - } -} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index d399391d5..444cb59ee 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -20,8 +20,6 @@ pub mod vnc; mod boot_source; mod chardev; -#[cfg(feature = "demo_device")] -mod demo_dev; mod devices; mod drive; mod fs; @@ -48,8 +46,6 @@ pub use boot_source::*; #[cfg(feature = "usb_camera")] pub use camera::*; pub use chardev::*; -#[cfg(feature = "demo_device")] -pub use demo_dev::*; pub use devices::*; #[cfg(any(feature = "gtk", feature = "ohui_srv"))] pub use display::*; -- Gitee From bc6fa2808ccbc5b393849931e9c0ae3ed2c07063 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 15:12:04 +0800 Subject: [PATCH 1772/2187] ramfb: use clap to parse the parameters of the ramfb config Use clap to parse the parameters of the ramfb config. Signed-off-by: liuxiangdong --- devices/src/legacy/mod.rs | 2 +- devices/src/legacy/ramfb.rs | 35 +++++++++++++++++++++++++++++ machine/src/aarch64/standard.rs | 12 +++++----- machine_manager/src/config/mod.rs | 4 ---- machine_manager/src/config/ramfb.rs | 24 -------------------- 5 files changed, 43 insertions(+), 34 deletions(-) delete mode 100644 machine_manager/src/config/ramfb.rs diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 00632feb4..74cc2e4a7 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -53,5 +53,5 @@ pub use pl011::PL011; #[cfg(target_arch = "aarch64")] pub use pl031::{PL031, RTC_CR, RTC_DR, RTC_IMSC, RTC_LR}; #[cfg(all(feature = "ramfb", target_arch = "aarch64"))] -pub use ramfb::Ramfb; +pub use ramfb::{Ramfb, RamfbConfig}; pub use serial::{Serial, SERIAL_ADDR}; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 470fcd796..6dab9da23 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -16,6 +16,7 @@ use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; use anyhow::{Context, Result}; +use clap::{ArgAction, Parser}; use drm_fourcc::DrmFourcc; use log::error; @@ -24,6 +25,7 @@ use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; +use machine_manager::config::valid_id; use machine_manager::event_loop::EventLoop; use ui::console::{ console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, @@ -39,6 +41,17 @@ const INSTALL_CHECK_INTERVEL_MS: u64 = 500; const INSTALL_RELEASE_INTERVEL_MS: u64 = 200; const INSTALL_PRESS_INTERVEL_MS: u64 = 100; +#[derive(Parser, Debug, Clone)] +#[command(no_binary_name(true))] +pub struct RamfbConfig { + #[arg(long, value_parser = ["ramfb"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long, default_value = "false", action = ArgAction::Append)] + pub install: bool, +} + #[repr(packed)] struct RamfbCfg { _addr: u64, @@ -317,3 +330,25 @@ fn set_press_event(install: Arc, data: *const u8) { install.store(false, Ordering::Release); } } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + + #[test] + fn test_ramfb_config_cmdline_parser() { + // Test1: install. + let ramfb_cmd1 = "ramfb,id=ramfb0,install=true"; + let ramfb_config = + RamfbConfig::try_parse_from(str_slip_to_clap(ramfb_cmd1, true, false)).unwrap(); + assert_eq!(ramfb_config.id, "ramfb0"); + assert_eq!(ramfb_config.install, true); + + // Test2: Default. + let ramfb_cmd2 = "ramfb,id=ramfb0"; + let ramfb_config = + RamfbConfig::try_parse_from(str_slip_to_clap(ramfb_cmd2, true, false)).unwrap(); + assert_eq!(ramfb_config.install, false); + } +} diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 56a3a3a21..3dd906e82 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -19,6 +19,8 @@ use std::sync::RwLock; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; +#[cfg(feature = "ramfb")] +use clap::Parser; use log::{error, info, warn}; use vmm_sys_util::eventfd::EventFd; @@ -45,18 +47,18 @@ use super::pci_host_root::PciHostRoot; use crate::standard_common::syscall::syscall_whitelist; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged, GedEvent}; use devices::acpi::power::PowerDev; -#[cfg(feature = "ramfb")] -use devices::legacy::Ramfb; use devices::legacy::{ FwCfgEntryType, FwCfgMem, FwCfgOps, LegacyError as DevErrorKind, PFlash, PL011, PL031, }; +#[cfg(feature = "ramfb")] +use devices::legacy::{Ramfb, RamfbConfig}; use devices::pci::{PciDevOps, PciHost, PciIntxState}; use devices::sysbus::SysBusDevType; use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_MAX}; use hypervisor::kvm::aarch64::*; use hypervisor::kvm::*; #[cfg(feature = "ramfb")] -use machine_manager::config::parse_ramfb; +use machine_manager::config::str_slip_to_clap; use machine_manager::config::ShutdownAction; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; @@ -747,12 +749,12 @@ impl MachineOps for StdMachine { #[cfg(feature = "ramfb")] fn add_ramfb(&mut self, cfg_args: &str) -> Result<()> { - let install = parse_ramfb(cfg_args)?; + let config = RamfbConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let fwcfg_dev = self .get_fwcfg_dev() .with_context(|| "Ramfb device must be used UEFI to boot, please add pflash devices")?; let sys_mem = self.get_sys_mem(); - let mut ramfb = Ramfb::new(sys_mem.clone(), install); + let mut ramfb = Ramfb::new(sys_mem.clone(), config.install); ramfb.ramfb_state.setup(&fwcfg_dev)?; ramfb.realize(&mut self.base.sysbus) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 444cb59ee..16587d0f3 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -33,8 +33,6 @@ mod numa; mod pci; #[cfg(feature = "pvpanic")] mod pvpanic_pci; -#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] -mod ramfb; mod rng; #[cfg(feature = "vnc_auth")] mod sasl_auth; @@ -62,8 +60,6 @@ pub use numa::*; pub use pci::*; #[cfg(feature = "pvpanic")] pub use pvpanic_pci::*; -#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] -pub use ramfb::*; pub use rng::*; #[cfg(feature = "vnc_auth")] pub use sasl_auth::*; diff --git a/machine_manager/src/config/ramfb.rs b/machine_manager/src/config/ramfb.rs deleted file mode 100644 index 8473c1df9..000000000 --- a/machine_manager/src/config/ramfb.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::Result; - -use crate::config::CmdParser; - -pub fn parse_ramfb(cfg_args: &str) -> Result { - let mut cmd_parser = CmdParser::new("ramfb"); - cmd_parser.push("").push("install").push("id"); - cmd_parser.parse(cfg_args)?; - - let install = cmd_parser.get_value::("install")?.unwrap_or(false); - Ok(install) -} -- Gitee From fb379880b787b2946fb01895c01ef5f73eb10ffc Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 17:45:09 +0800 Subject: [PATCH 1773/2187] virtiofs: use clap to parse the parameters of the virtiofs config Use clap to parse the parameters of the virtiofs config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 25 ++++--- machine_manager/src/config/fs.rs | 111 ------------------------------ machine_manager/src/config/mod.rs | 3 - virtio/src/vhost/user/fs.rs | 74 ++++++++++++++++++-- 4 files changed, 86 insertions(+), 127 deletions(-) delete mode 100644 machine_manager/src/config/fs.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fbd20a6d4..68abf2e85 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -78,7 +78,7 @@ use machine_manager::config::parse_gpu; use machine_manager::config::parse_pvpanic; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, - parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, + parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, parse_root_port, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, @@ -847,29 +847,36 @@ pub trait MachineOps { /// * 'vm_config' - VM configuration. /// * 'cfg_args' - Device configuration arguments. fn add_virtio_fs(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let dev_cfg = parse_fs(vm_config, cfg_args)?; - let id_clone = dev_cfg.id.clone(); + let dev_cfg = + vhost::user::FsConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let char_dev = vm_config + .chardev + .remove(&dev_cfg.chardev) + .with_context(|| format!("Chardev {:?} not found or is in use", &dev_cfg.chardev))?; let sys_mem = self.get_sys_mem().clone(); if !vm_config.machine_config.mem_config.mem_share { bail!("When configuring the vhost-user-fs-device or vhost-user-fs-pci device, the memory must be shared."); } - match parse_device_type(cfg_args)?.as_str() { + let device = Arc::new(Mutex::new(vhost::user::Fs::new( + dev_cfg.clone(), + char_dev, + sys_mem.clone(), + ))); + match dev_cfg.classtype.as_str() { "vhost-user-fs-device" => { - let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone()))); let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); self.realize_virtio_mmio_device(virtio_mmio_device) .with_context(|| "Failed to add vhost user fs device")?; } _ => { - let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem))); - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; + let bdf = PciBdf::new(dev_cfg.bus.clone().unwrap(), dev_cfg.addr.unwrap()); + let multi_func = dev_cfg.multifunction.unwrap_or_default(); let root_bus = self.get_pci_host()?.lock().unwrap().root_bus.clone(); let msi_irq_manager = root_bus.lock().unwrap().msi_irq_manager.clone(); let need_irqfd = msi_irq_manager.as_ref().unwrap().irqfd_enable(); - self.add_virtio_pci_device(&id_clone, &bdf, device, multi_func, need_irqfd) + self.add_virtio_pci_device(&dev_cfg.id, &bdf, device, multi_func, need_irqfd) .with_context(|| "Failed to add pci fs device")?; } } diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs deleted file mode 100644 index 6900888fc..000000000 --- a/machine_manager/src/config/fs.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{anyhow, bail, Context, Result}; - -use super::{error::ConfigError, SocketType}; -use crate::config::{ - pci_args_check, CmdParser, ConfigCheck, VmConfig, MAX_SOCK_PATH_LENGTH, MAX_STRING_LENGTH, - MAX_TAG_LENGTH, -}; - -/// Config struct for `fs`. -/// Contains fs device's attr. -#[derive(Debug, Clone)] -pub struct FsConfig { - /// Device tag. - pub tag: String, - /// Device id. - pub id: String, - /// Char device sock path. - pub sock: String, -} - -impl Default for FsConfig { - fn default() -> Self { - FsConfig { - tag: "".to_string(), - id: "".to_string(), - sock: "".to_string(), - } - } -} - -impl ConfigCheck for FsConfig { - fn check(&self) -> Result<()> { - if self.tag.len() >= MAX_TAG_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "fs device tag".to_string(), - MAX_TAG_LENGTH - 1, - ))); - } - - if self.id.len() >= MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "fs device id".to_string(), - MAX_STRING_LENGTH - 1, - ))); - } - - if self.sock.len() > MAX_SOCK_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "fs sock path".to_string(), - MAX_SOCK_PATH_LENGTH, - ))); - } - - Ok(()) - } -} - -pub fn parse_fs(vm_config: &mut VmConfig, fs_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("fs"); - cmd_parser - .push("") - .push("tag") - .push("id") - .push("chardev") - .push("bus") - .push("addr") - .push("multifunction"); - cmd_parser.parse(fs_config)?; - pci_args_check(&cmd_parser)?; - - let mut fs_cfg = FsConfig { - tag: cmd_parser.get_value::("tag")?.with_context(|| { - ConfigError::FieldIsMissing("tag".to_string(), "virtio-fs".to_string()) - })?, - id: cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "virtio-fs".to_string()) - })?, - ..Default::default() - }; - - let name = cmd_parser - .get_value::("chardev")? - .with_context(|| { - ConfigError::FieldIsMissing("chardev".to_string(), "virtio-fs".to_string()) - })?; - let char_dev = vm_config - .chardev - .remove(&name) - .with_context(|| format!("Chardev {:?} not found or is in use", &name))?; - match char_dev.classtype.socket_type()? { - SocketType::Unix { path } => { - fs_cfg.sock = path; - } - _ => bail!("Chardev {:?} backend should be unix-socket type.", &name), - }; - - fs_cfg.check()?; - Ok(fs_cfg) -} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 16587d0f3..57c4b5b78 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -22,7 +22,6 @@ mod boot_source; mod chardev; mod devices; mod drive; -mod fs; #[cfg(feature = "virtio_gpu")] mod gpu; mod incoming; @@ -49,7 +48,6 @@ pub use devices::*; pub use display::*; pub use drive::*; pub use error::ConfigError; -pub use fs::*; #[cfg(feature = "virtio_gpu")] pub use gpu::*; pub use incoming::*; @@ -96,7 +94,6 @@ pub const MAX_SOCK_PATH_LENGTH: usize = 108; pub const MAX_VIRTIO_QUEUE: usize = 32; pub const FAST_UNPLUG_ON: &str = "1"; pub const FAST_UNPLUG_OFF: &str = "0"; -pub const MAX_TAG_LENGTH: usize = 36; pub const MAX_NODES: u32 = 128; /// Default virtqueue size for virtio devices excepts virtio-fs. pub const DEFAULT_VIRTQUEUE_SIZE: u16 = 256; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index a1c5ed8d3..75c3698a0 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -19,7 +19,8 @@ const VIRTIO_FS_QUEUE_SIZE: u16 = 128; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use vmm_sys_util::eventfd::EventFd; use super::super::super::{VirtioDevice, VIRTIO_TYPE_FS}; @@ -27,10 +28,45 @@ use super::super::VhostOps; use super::{listen_guest_notifier, VhostBackendType, VhostUserClient}; use crate::{read_config_default, VirtioBase, VirtioInterrupt}; use address_space::AddressSpace; -use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; +use machine_manager::config::{ + get_pci_df, parse_bool, valid_id, ChardevConfig, ConfigError, SocketType, +}; use machine_manager::event_loop::unregister_event_helper; use util::byte_code::ByteCode; +const MAX_TAG_LENGTH: usize = 36; + +/// Config struct for `fs`. +/// Contains fs device's attr. +#[derive(Parser, Debug, Clone)] +#[command(no_binary_name(true))] +pub struct FsConfig { + #[arg(long, value_parser = ["vhost-user-fs-pci", "vhost-user-fs-device"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub chardev: String, + #[arg(long, value_parser = valid_tag)] + pub tag: String, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, value_parser = parse_bool)] + pub multifunction: Option, +} + +fn valid_tag(tag: &str) -> Result { + if tag.len() >= MAX_TAG_LENGTH { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "fs device tag".to_string(), + MAX_TAG_LENGTH - 1, + ))); + } + Ok(tag.to_string()) +} + #[derive(Copy, Clone)] #[repr(C, packed)] struct VirtioFsConfig { @@ -52,6 +88,7 @@ impl ByteCode for VirtioFsConfig {} pub struct Fs { base: VirtioBase, fs_cfg: FsConfig, + chardev_cfg: ChardevConfig, config_space: VirtioFsConfig, client: Option>>, mem_space: Arc, @@ -64,14 +101,16 @@ impl Fs { /// # Arguments /// /// `fs_cfg` - The config of this Fs device. + /// `chardev_cfg` - The config of this Fs device's chardev. /// `mem_space` - The address space of this Fs device. - pub fn new(fs_cfg: FsConfig, mem_space: Arc) -> Self { + pub fn new(fs_cfg: FsConfig, chardev_cfg: ChardevConfig, mem_space: Arc) -> Self { let queue_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; let queue_size = VIRTIO_FS_QUEUE_SIZE; Fs { base: VirtioBase::new(VIRTIO_TYPE_FS, queue_num, queue_size), fs_cfg, + chardev_cfg, config_space: VirtioFsConfig::default(), client: None, mem_space, @@ -91,9 +130,15 @@ impl VirtioDevice for Fs { fn realize(&mut self) -> Result<()> { let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; + + let socket_path = match self.chardev_cfg.classtype.socket_type()? { + SocketType::Unix { path } => path, + _ => bail!("Vhost-user-fs Chardev backend should be unix-socket type."), + }; + let client = VhostUserClient::new( &self.mem_space, - &self.fs_cfg.sock, + &socket_path, queues_num as u64, VhostBackendType::TypeFs, ) @@ -194,3 +239,24 @@ impl VirtioDevice for Fs { self.realize() } } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + + #[test] + fn test_vhostuserfs_cmdline_parser() { + // Test1: Right. + let fs_cmd = "vhost-user-fs-device,id=fs0,chardev=chardev0,tag=tag0"; + let fs_config = FsConfig::try_parse_from(str_slip_to_clap(fs_cmd, true, false)).unwrap(); + assert_eq!(fs_config.id, "fs0"); + assert_eq!(fs_config.chardev, "chardev0"); + assert_eq!(fs_config.tag, "tag0"); + + // Test2: Illegal value. + let fs_cmd = "vhost-user-fs-device,id=fs0,chardev=chardev0,tag=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + let result = FsConfig::try_parse_from(str_slip_to_clap(fs_cmd, true, false)); + assert!(result.is_err()); + } +} -- Gitee From a9f71661c7a871b0d2cfe2c79f0c10ba7a9b8e61 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 20:48:17 +0800 Subject: [PATCH 1774/2187] virtio-gpu: use clap to parse the parameters of the virtio-gpu config Use clap to parse the parameters of the virtio-gpu config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 16 ++- machine_manager/src/config/gpu.rs | 176 ------------------------------ machine_manager/src/config/mod.rs | 6 +- virtio/src/device/gpu.rs | 93 +++++++++++++++- 4 files changed, 101 insertions(+), 190 deletions(-) delete mode 100644 machine_manager/src/config/gpu.rs diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 68abf2e85..25f5a8bee 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -72,8 +72,6 @@ use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; -#[cfg(feature = "virtio_gpu")] -use machine_manager::config::parse_gpu; #[cfg(feature = "pvpanic")] use machine_manager::config::parse_pvpanic; use machine_manager::config::{ @@ -95,8 +93,6 @@ use util::{ seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; use vfio::{VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; -#[cfg(feature = "virtio_gpu")] -use virtio::Gpu; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; use virtio::{ @@ -106,6 +102,8 @@ use virtio::{ Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; +#[cfg(feature = "virtio_gpu")] +use virtio::{Gpu, GpuDevConfig}; /// Machine structure include base members. pub struct MachineBase { @@ -1321,10 +1319,10 @@ pub trait MachineOps { #[cfg(feature = "virtio_gpu")] fn add_virtio_pci_gpu(&mut self, cfg_args: &str) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let device_cfg = parse_gpu(cfg_args)?; - let device = Arc::new(Mutex::new(Gpu::new(device_cfg.clone()))); + let config = GpuDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + config.check(); + let bdf = PciBdf::new(config.bus.clone(), config.addr); + let device = Arc::new(Mutex::new(Gpu::new(config.clone()))); #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] if device.lock().unwrap().device_quirk() == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0) @@ -1334,7 +1332,7 @@ pub trait MachineOps { device.lock().unwrap().set_bar0_fb(self.get_ohui_fb()); } - self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false)?; + self.add_virtio_pci_device(&config.id, &bdf, device, false, false)?; Ok(()) } diff --git a/machine_manager/src/config/gpu.rs b/machine_manager/src/config/gpu.rs deleted file mode 100644 index 56ab2842e..000000000 --- a/machine_manager/src/config/gpu.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{anyhow, Result}; -use log::warn; - -use super::{error::ConfigError, M}; -use crate::config::{check_arg_too_long, CmdParser, ConfigCheck}; - -/// The maximum number of outputs. -pub const VIRTIO_GPU_MAX_OUTPUTS: usize = 16; - -pub const VIRTIO_GPU_MAX_HOSTMEM: u64 = 256 * M; - -/// The bar0 size of enable_bar0 features -pub const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; - -#[derive(Clone, Debug)] -pub struct GpuDevConfig { - pub id: String, - pub max_outputs: u32, - pub edid: bool, - pub xres: u32, - pub yres: u32, - pub max_hostmem: u64, - pub enable_bar0: bool, -} - -impl Default for GpuDevConfig { - fn default() -> Self { - GpuDevConfig { - id: "".to_string(), - max_outputs: 1, - edid: true, - xres: 1024, - yres: 768, - max_hostmem: VIRTIO_GPU_MAX_HOSTMEM, - enable_bar0: false, - } - } -} - -impl ConfigCheck for GpuDevConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "id")?; - if self.max_outputs > VIRTIO_GPU_MAX_OUTPUTS as u32 || self.max_outputs == 0 { - return Err(anyhow!(ConfigError::IllegalValue( - "max_outputs".to_string(), - 0, - false, - VIRTIO_GPU_MAX_OUTPUTS as u64, - true - ))); - } - - if self.max_hostmem == 0 { - return Err(anyhow!(ConfigError::IllegalValueUnilateral( - "max_hostmem".to_string(), - true, - false, - 0 - ))); - } - - if self.max_hostmem < VIRTIO_GPU_MAX_HOSTMEM { - warn!( - "max_hostmem should >= {}, allocating less than it may cause \ - the GPU to fail to start or refresh.", - VIRTIO_GPU_MAX_HOSTMEM - ); - } - - Ok(()) - } -} - -pub fn parse_gpu(gpu_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("virtio-gpu-pci"); - cmd_parser - .push("") - .push("id") - .push("max_outputs") - .push("edid") - .push("xres") - .push("yres") - .push("max_hostmem") - .push("bus") - .push("addr") - .push("enable_bar0"); - cmd_parser.parse(gpu_config)?; - - let mut gpu_cfg: GpuDevConfig = GpuDevConfig::default(); - if let Some(id) = cmd_parser.get_value::("id")? { - gpu_cfg.id = id; - } - if let Some(max_outputs) = cmd_parser.get_value::("max_outputs")? { - gpu_cfg.max_outputs = max_outputs; - } - if let Some(edid) = cmd_parser.get_value::("edid")? { - gpu_cfg.edid = edid; - } - if let Some(xres) = cmd_parser.get_value::("xres")? { - gpu_cfg.xres = xres; - } - if let Some(yres) = cmd_parser.get_value::("yres")? { - gpu_cfg.yres = yres; - } - if let Some(max_hostmem) = cmd_parser.get_value::("max_hostmem")? { - gpu_cfg.max_hostmem = max_hostmem; - } - if let Some(enable_bar0) = cmd_parser.get_value::("enable_bar0")? { - gpu_cfg.enable_bar0 = enable_bar0; - } - gpu_cfg.check()?; - - Ok(gpu_cfg) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_pci_gpu_config_cmdline_parser() { - let max_hostmem = VIRTIO_GPU_MAX_HOSTMEM + 1; - let gpu_cfg_cmdline = format!( - "{}{}", - "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ - max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=", - max_hostmem.to_string() - ); - let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); - assert!(gpu_cfg_.is_ok()); - let gpu_cfg = gpu_cfg_.unwrap(); - assert_eq!(gpu_cfg.id, "gpu_1"); - assert_eq!(gpu_cfg.max_outputs, 1); - assert_eq!(gpu_cfg.edid, true); - assert_eq!(gpu_cfg.xres, 1024); - assert_eq!(gpu_cfg.yres, 768); - assert_eq!(gpu_cfg.max_hostmem, max_hostmem); - - // max_outputs is illegal - let gpu_cfg_cmdline = format!( - "{}{}", - "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ - max_outputs=17,edid=true,xres=1024,yres=768,max_hostmem=", - max_hostmem.to_string() - ); - let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); - assert!(gpu_cfg_.is_err()); - - let gpu_cfg_cmdline = format!( - "{}{}", - "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ - max_outputs=0,edid=true,xres=1024,yres=768,max_hostmem=", - max_hostmem.to_string() - ); - let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); - assert!(gpu_cfg_.is_err()); - - // max_hostmem is illegal - let gpu_cfg_cmdline = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,\ - max_outputs=1,edid=true,xres=1024,yres=768,max_hostmem=0"; - let gpu_cfg_ = parse_gpu(&gpu_cfg_cmdline); - assert!(gpu_cfg_.is_err()); - } -} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 57c4b5b78..519c5b5ef 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -22,8 +22,6 @@ mod boot_source; mod chardev; mod devices; mod drive; -#[cfg(feature = "virtio_gpu")] -mod gpu; mod incoming; mod iothread; mod machine_config; @@ -48,8 +46,6 @@ pub use devices::*; pub use display::*; pub use drive::*; pub use error::ConfigError; -#[cfg(feature = "virtio_gpu")] -pub use gpu::*; pub use incoming::*; pub use iothread::*; pub use machine_config::*; @@ -101,6 +97,8 @@ pub const DEFAULT_VIRTQUEUE_SIZE: u16 = 256; pub const MIN_QUEUE_SIZE_SCSI: u64 = 2; // Max size of each virtqueue for virtio-scsi. pub const MAX_QUEUE_SIZE_SCSI: u64 = 1024; +/// The bar0 size of enable_bar0 features +pub const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; #[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct ObjectConfig { diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 97ca67c14..98f9d2c31 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -18,6 +18,7 @@ use std::sync::{Arc, Mutex, Weak}; use std::{ptr, vec}; use anyhow::{anyhow, bail, Context, Result}; +use clap::{ArgAction, Parser}; use log::{error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -36,7 +37,7 @@ use crate::{ VIRTIO_GPU_RESP_OK_EDID, VIRTIO_GPU_RESP_OK_NODATA, VIRTIO_TYPE_GPU, }; use address_space::{AddressSpace, FileBackend, GuestAddress}; -use machine_manager::config::{GpuDevConfig, DEFAULT_VIRTQUEUE_SIZE, VIRTIO_GPU_MAX_OUTPUTS}; +use machine_manager::config::{get_pci_df, valid_id, DEFAULT_VIRTQUEUE_SIZE}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration_derive::ByteCode; use ui::console::{ @@ -72,6 +73,49 @@ const VIRTIO_GPU_RES_WIN_FRAMEBUF: u32 = 0x80000000; const VIRTIO_GPU_RES_EFI_FRAMEBUF: u32 = 0x40000000; const VIRTIO_GPU_RES_FRAMEBUF: u32 = VIRTIO_GPU_RES_WIN_FRAMEBUF | VIRTIO_GPU_RES_EFI_FRAMEBUF; +/// The maximum number of outputs. +const VIRTIO_GPU_MAX_OUTPUTS: usize = 16; +/// The default maximum memory 256M. +const VIRTIO_GPU_DEFAULT_MAX_HOSTMEM: u64 = 0x10000000; + +#[derive(Parser, Clone, Debug, Default)] +#[command(no_binary_name(true))] +pub struct GpuDevConfig { + #[arg(long, value_parser = ["virtio-gpu-pci"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + #[arg(long, alias = "max_outputs", default_value="1", value_parser = clap::value_parser!(u32).range(1..=VIRTIO_GPU_MAX_OUTPUTS as i64))] + pub max_outputs: u32, + #[arg(long, default_value="true", action = ArgAction::Append)] + pub edid: bool, + #[arg(long, default_value = "1024")] + pub xres: u32, + #[arg(long, default_value = "768")] + pub yres: u32, + // The default max_hostmem is 256M. + #[arg(long, alias = "max_hostmem", default_value="268435456", value_parser = clap::value_parser!(u64).range(1..))] + pub max_hostmem: u64, + #[arg(long, alias = "enable_bar0", default_value="false", action = ArgAction::Append)] + pub enable_bar0: bool, +} + +impl GpuDevConfig { + pub fn check(&self) { + if self.max_hostmem < VIRTIO_GPU_DEFAULT_MAX_HOSTMEM { + warn!( + "max_hostmem should >= {}, allocating less than it may cause \ + the GPU to fail to start or refresh.", + VIRTIO_GPU_DEFAULT_MAX_HOSTMEM + ); + } + } +} + #[derive(Debug)] struct GpuResource { resource_id: u32, @@ -1843,3 +1887,50 @@ impl VirtioDevice for Gpu { result } } + +#[cfg(test)] +mod tests { + use super::*; + use machine_manager::config::str_slip_to_clap; + + #[test] + fn test_parse_virtio_gpu_pci_cmdline() { + // Test1: Right. + let gpu_cmd = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,max_outputs=5,edid=false,\ + xres=2048,yres=800,enable_bar0=true,max_hostmem=268435457"; + let gpu_cfg = GpuDevConfig::try_parse_from(str_slip_to_clap(gpu_cmd, true, false)).unwrap(); + assert_eq!(gpu_cfg.id, "gpu_1"); + assert_eq!(gpu_cfg.bus, "pcie.0"); + assert_eq!(gpu_cfg.addr, (4, 0)); + assert_eq!(gpu_cfg.max_outputs, 5); + assert_eq!(gpu_cfg.xres, 2048); + assert_eq!(gpu_cfg.yres, 800); + assert_eq!(gpu_cfg.edid, false); + assert_eq!(gpu_cfg.max_hostmem, 268435457); + assert_eq!(gpu_cfg.enable_bar0, true); + + // Test2: Default. + let gpu_cmd2 = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0"; + let gpu_cfg = + GpuDevConfig::try_parse_from(str_slip_to_clap(gpu_cmd2, true, false)).unwrap(); + assert_eq!(gpu_cfg.max_outputs, 1); + assert_eq!(gpu_cfg.xres, 1024); + assert_eq!(gpu_cfg.yres, 768); + assert_eq!(gpu_cfg.edid, true); + assert_eq!(gpu_cfg.max_hostmem, VIRTIO_GPU_DEFAULT_MAX_HOSTMEM); + assert_eq!(gpu_cfg.enable_bar0, false); + + // Test3/4: max_outputs is illegal. + let gpu_cmd3 = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,max_outputs=17"; + let result = GpuDevConfig::try_parse_from(str_slip_to_clap(gpu_cmd3, true, false)); + assert!(result.is_err()); + let gpu_cmd4 = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,max_outputs=0"; + let result = GpuDevConfig::try_parse_from(str_slip_to_clap(gpu_cmd4, true, false)); + assert!(result.is_err()); + + // Test5: max_hostmem is illegal. + let gpu_cmd5 = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,max_hostmem=0"; + let result = GpuDevConfig::try_parse_from(str_slip_to_clap(gpu_cmd5, true, false)); + assert!(result.is_err()); + } +} -- Gitee From aa4de8c0fddc5bf6607c56beae1477d024d7d285 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 22:12:18 +0800 Subject: [PATCH 1775/2187] pvpanic: use clap to parse the parameters of the pvpanic config Use clap to parse the parameters of the pvpanic config. Signed-off-by: liuxiangdong --- devices/src/misc/pvpanic.rs | 53 +++++++++++++++++- machine/src/lib.rs | 11 ++-- machine_manager/src/config/mod.rs | 4 -- machine_manager/src/config/pvpanic_pci.rs | 66 ----------------------- tests/mod_test/tests/pvpanic_test.rs | 2 +- 5 files changed, 57 insertions(+), 79 deletions(-) delete mode 100644 machine_manager/src/config/pvpanic_pci.rs diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index e2d29dd12..a65979549 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -16,7 +16,9 @@ use std::sync::{ }; use anyhow::{bail, Context, Result}; +use clap::Parser; use log::{debug, error, info}; +use serde::{Deserialize, Serialize}; use crate::pci::{ config::{ @@ -29,7 +31,7 @@ use crate::pci::{ }; use crate::{Device, DeviceBase}; use address_space::{GuestAddress, Region, RegionOps}; -use machine_manager::config::{PvpanicDevConfig, PVPANIC_CRASHLOADED, PVPANIC_PANICKED}; +use machine_manager::config::{get_pci_df, valid_id}; const PVPANIC_PCI_REVISION_ID: u8 = 1; const PVPANIC_PCI_VENDOR_ID: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; @@ -40,6 +42,33 @@ const PVPANIC_REG_BAR_SIZE: u64 = 0x4; #[cfg(target_arch = "x86_64")] const PVPANIC_REG_BAR_SIZE: u64 = 0x1; +pub const PVPANIC_PANICKED: u32 = 1 << 0; +pub const PVPANIC_CRASHLOADED: u32 = 1 << 1; + +#[derive(Parser, Debug, Clone, Serialize, Deserialize)] +#[command(no_binary_name(true))] +pub struct PvpanicDevConfig { + #[arg(long, value_parser = ["pvpanic"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + #[arg(long, alias = "supported-features", default_value = "3", value_parser = valid_supported_features)] + pub supported_features: u32, +} + +fn valid_supported_features(f: &str) -> Result { + let features = f.parse::()?; + let supported_features = match features & !(PVPANIC_PANICKED | PVPANIC_CRASHLOADED) { + 0 => features, + _ => bail!("Unsupported pvpanic device features {}", features), + }; + Ok(supported_features) +} + #[derive(Copy, Clone)] pub struct PvPanicState { supported_features: u32, @@ -244,6 +273,7 @@ impl PciDevOps for PvPanicPci { mod tests { use super::*; use crate::pci::{host::tests::create_pci_host, le_read_u16, PciHost}; + use machine_manager::config::str_slip_to_clap; fn init_pvpanic_dev(devfn: u8, supported_features: u32, dev_id: &str) -> Arc> { let pci_host = create_pci_host(); @@ -253,6 +283,9 @@ mod tests { let config = PvpanicDevConfig { id: dev_id.to_string(), supported_features, + classtype: "".to_string(), + bus: "pcie.0".to_string(), + addr: (3, 0), }; let pvpanic_dev = PvPanicPci::new(&config, devfn, root_bus.clone()); assert_eq!(pvpanic_dev.base.base.id, "pvpanic_test".to_string()); @@ -264,6 +297,24 @@ mod tests { pci_host } + #[test] + fn test_pvpanic_cmdline_parser() { + // Test1: Right. + let cmdline = "pvpanic,id=pvpanic0,bus=pcie.0,addr=0x7,supported-features=0"; + let result = PvpanicDevConfig::try_parse_from(str_slip_to_clap(cmdline, true, false)); + assert_eq!(result.unwrap().supported_features, 0); + + // Test2: Default value. + let cmdline = "pvpanic,id=pvpanic0,bus=pcie.0,addr=0x7"; + let result = PvpanicDevConfig::try_parse_from(str_slip_to_clap(cmdline, true, false)); + assert_eq!(result.unwrap().supported_features, 3); + + // Test3: Illegal value. + let cmdline = "pvpanic,id=pvpanic0,bus=pcie.0,addr=0x7,supported-features=4"; + let result = PvpanicDevConfig::try_parse_from(str_slip_to_clap(cmdline, true, false)); + assert!(result.is_err()); + } + #[test] fn test_pvpanic_attached() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 25f5a8bee..7fc1b3965 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -47,7 +47,7 @@ use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUTopology, CpuTopology, CPU}; use devices::legacy::FwCfgOps; #[cfg(feature = "pvpanic")] -use devices::misc::pvpanic::PvPanicPci; +use devices::misc::pvpanic::{PvPanicPci, PvpanicDevConfig}; #[cfg(feature = "scream")] use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] @@ -72,8 +72,6 @@ use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; -#[cfg(feature = "pvpanic")] -use machine_manager::config::parse_pvpanic; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, @@ -1031,11 +1029,10 @@ pub trait MachineOps { #[cfg(feature = "pvpanic")] fn add_pvpanic(&mut self, cfg_args: &str) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let device_cfg = parse_pvpanic(cfg_args)?; - + let config = PvpanicDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let bdf = PciBdf::new(config.bus.clone(), config.addr); let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let pcidev = PvPanicPci::new(&device_cfg, devfn, parent_bus); + let pcidev = PvPanicPci::new(&config, devfn, parent_bus); pcidev .realize() .with_context(|| "Failed to realize pvpanic device")?; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 519c5b5ef..05e91a6fc 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -28,8 +28,6 @@ mod machine_config; mod network; mod numa; mod pci; -#[cfg(feature = "pvpanic")] -mod pvpanic_pci; mod rng; #[cfg(feature = "vnc_auth")] mod sasl_auth; @@ -52,8 +50,6 @@ pub use machine_config::*; pub use network::*; pub use numa::*; pub use pci::*; -#[cfg(feature = "pvpanic")] -pub use pvpanic_pci::*; pub use rng::*; #[cfg(feature = "vnc_auth")] pub use sasl_auth::*; diff --git a/machine_manager/src/config/pvpanic_pci.rs b/machine_manager/src/config/pvpanic_pci.rs deleted file mode 100644 index d0c3b8723..000000000 --- a/machine_manager/src/config/pvpanic_pci.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use crate::config::{CmdParser, ConfigCheck}; -use anyhow::{bail, Context, Result}; -use serde::{Deserialize, Serialize}; - -pub const PVPANIC_PANICKED: u32 = 1 << 0; -pub const PVPANIC_CRASHLOADED: u32 = 1 << 1; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PvpanicDevConfig { - pub id: String, - pub supported_features: u32, -} - -impl Default for PvpanicDevConfig { - fn default() -> Self { - PvpanicDevConfig { - id: "".to_string(), - supported_features: PVPANIC_PANICKED | PVPANIC_CRASHLOADED, - } - } -} - -impl ConfigCheck for PvpanicDevConfig { - fn check(&self) -> Result<()> { - Ok(()) - } -} - -pub fn parse_pvpanic(args_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("pvpanic"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("supported-features"); - cmd_parser.parse(args_config)?; - - let mut pvpanicdevcfg = PvpanicDevConfig::default(); - - if let Some(features) = cmd_parser.get_value::("supported-features")? { - pvpanicdevcfg.supported_features = - match features & !(PVPANIC_PANICKED | PVPANIC_CRASHLOADED) { - 0 => features, - _ => bail!("Unsupported pvpanic device features {}", features), - } - } - - pvpanicdevcfg.id = cmd_parser - .get_value::("id")? - .with_context(|| "No id configured for pvpanic device")?; - - Ok(pvpanicdevcfg) -} diff --git a/tests/mod_test/tests/pvpanic_test.rs b/tests/mod_test/tests/pvpanic_test.rs index 044515966..03dfc6787 100644 --- a/tests/mod_test/tests/pvpanic_test.rs +++ b/tests/mod_test/tests/pvpanic_test.rs @@ -15,11 +15,11 @@ use std::fs; use std::path::Path; use std::rc::Rc; +use devices::misc::pvpanic::{PVPANIC_CRASHLOADED, PVPANIC_PANICKED}; use devices::pci::config::{ PCI_CLASS_SYSTEM_OTHER, PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT, PCI_VENDOR_ID_REDHAT_QUMRANET, }; -use machine_manager::config::{PVPANIC_CRASHLOADED, PVPANIC_PANICKED}; use mod_test::{ libdriver::{machine::TestStdMachine, pci::*}, libtest::{test_init, TestState, MACHINE_TYPE_ARG}, -- Gitee From 965a6e8ecc691946c95220b03c0507339e5e1777 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 21 Mar 2024 22:40:30 +0800 Subject: [PATCH 1776/2187] iothread: use clap to parse the parameters of the iothread config Use clap to parse the parameters of the iothread config. Signed-off-by: liuxiangdong --- machine_manager/src/config/iothread.rs | 28 +++++++++----------------- virtio/src/device/block.rs | 1 + 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/machine_manager/src/config/iothread.rs b/machine_manager/src/config/iothread.rs index ac3a0a9e5..029d6583c 100644 --- a/machine_manager/src/config/iothread.rs +++ b/machine_manager/src/config/iothread.rs @@ -11,37 +11,29 @@ // See the Mulan PSL v2 for more details. use anyhow::{anyhow, Result}; +use clap::Parser; use serde::{Deserialize, Serialize}; -use super::error::ConfigError; -use crate::config::{check_arg_too_long, CmdParser, ConfigCheck, VmConfig}; +use super::{error::ConfigError, str_slip_to_clap, valid_id}; +use crate::config::VmConfig; const MAX_IOTHREAD_NUM: usize = 8; /// Config structure for iothread. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct IothreadConfig { + #[arg(long, value_parser = ["iothread"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] pub id: String, } -impl ConfigCheck for IothreadConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "iothread id") - } -} - impl VmConfig { /// Add new iothread device to `VmConfig`. pub fn add_iothread(&mut self, iothread_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("iothread"); - cmd_parser.push("").push("id"); - cmd_parser.parse(iothread_config)?; - - let mut iothread = IothreadConfig::default(); - if let Some(id) = cmd_parser.get_value::("id")? { - iothread.id = id; - } - iothread.check()?; + let iothread = + IothreadConfig::try_parse_from(str_slip_to_clap(iothread_config, true, false))?; if self.iothreads.is_some() { if self.iothreads.as_ref().unwrap().len() >= MAX_IOTHREAD_NUM { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index a7719cf35..0aeddca7d 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1541,6 +1541,7 @@ mod tests { // spawn io thread let io_conf = IothreadConfig { + classtype: "iothread".to_string(), id: thread_name.clone(), }; EventLoop::object_init(&Some(vec![io_conf])).unwrap(); -- Gitee From 9ceb8f80434aecc356c11d25c54bdbb37c6cf02a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 22 Mar 2024 10:26:54 +0800 Subject: [PATCH 1777/2187] authz-simple: use clap to parse the parameters of the authz-simple config Use clap to parse the parameters of the authz-simple config. Signed-off-by: liuxiangdong --- machine_manager/src/config/sasl_auth.rs | 37 +++++++++---------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs index 506763adc..37b47bc7c 100644 --- a/machine_manager/src/config/sasl_auth.rs +++ b/machine_manager/src/config/sasl_auth.rs @@ -10,44 +10,33 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; +use clap::Parser; use serde::{Deserialize, Serialize}; -use crate::config::{ - ConfigError, {CmdParser, VmConfig}, -}; +use crate::config::{str_slip_to_clap, valid_id, ConfigError, VmConfig}; -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SaslAuthObjConfig { - /// Object Id. + #[arg(long, value_parser = ["authz-simple"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] pub id: String, /// Authentication User Name. + #[arg(long, default_value = "")] pub identity: String, } impl VmConfig { pub fn add_saslauth(&mut self, saslauth_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("authz-simple"); - cmd_parser.push("").push("id").push("identity"); - cmd_parser.parse(saslauth_config)?; - - let mut saslauth = SaslAuthObjConfig { - id: cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "vnc sasl_auth".to_string()) - })?, - ..Default::default() - }; - - if let Some(identity) = cmd_parser.get_value::("identity")? { - saslauth.identity = identity; - } - + let saslauth = + SaslAuthObjConfig::try_parse_from(str_slip_to_clap(saslauth_config, true, false))?; let id = saslauth.id.clone(); - if self.object.sasl_object.get(&id).is_none() { - self.object.sasl_object.insert(id, saslauth); - } else { + if self.object.sasl_object.get(&id).is_some() { return Err(anyhow!(ConfigError::IdRepeat("saslauth".to_string(), id))); } + self.object.sasl_object.insert(id, saslauth); Ok(()) } -- Gitee From 5878ba7e7fdec356719c068105df1896176141be Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 22 Mar 2024 11:15:45 +0800 Subject: [PATCH 1778/2187] tls_creds: use clap to parse the parameters of the tls_creds config Use clap to parse the parameters of the tls_creds config. Signed-off-by: liuxiangdong --- machine_manager/src/config/mod.rs | 9 ++++ machine_manager/src/config/tls_creds.rs | 58 +++++++------------------ ui/src/vnc/server_io.rs | 2 +- 3 files changed, 25 insertions(+), 44 deletions(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 05e91a6fc..b8742e513 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -62,6 +62,7 @@ pub use vnc::*; use std::collections::HashMap; use std::fs::{canonicalize, File}; use std::io::Read; +use std::path::Path; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; @@ -822,6 +823,14 @@ pub fn valid_socket_path(sock_path: &str) -> Result { valid_path(sock_path) } +pub fn valid_dir(d: &str) -> Result { + let dir = String::from(d); + if !Path::new(&dir).is_dir() { + return Err(anyhow!(ConfigError::DirNotExist(dir))); + } + Ok(dir) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index 8803ea428..92903161c 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -10,64 +10,36 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::path::Path; - -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; +use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; -use crate::config::{ - ConfigError, {CmdParser, VmConfig}, -}; +use crate::config::{str_slip_to_clap, valid_dir, valid_id, ConfigError, VmConfig}; -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct TlsCredObjConfig { + #[arg(long)] + pub classtype: String, + #[arg(long, value_parser = valid_id)] pub id: String, + #[arg(long, value_parser = valid_dir)] pub dir: String, - pub cred_type: String, + #[arg(long)] pub endpoint: Option, + #[arg(long, alias = "verify-peer", default_value= "false", action = ArgAction::Append)] pub verifypeer: bool, } impl VmConfig { pub fn add_tlscred(&mut self, tlscred_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("tls-creds-x509"); - cmd_parser - .push("") - .push("id") - .push("dir") - .push("endpoint") - .push("verify-peer"); - cmd_parser.parse(tlscred_config)?; - - let mut tlscred = TlsCredObjConfig { - id: cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "vnc tls_creds".to_string()) - })?, - ..Default::default() - }; - - if let Some(dir) = cmd_parser.get_value::("dir")? { - if Path::new(&dir).is_dir() { - tlscred.dir = dir; - } else { - return Err(anyhow!(ConfigError::DirNotExist(dir))); - } - } - if let Some(endpoint) = cmd_parser.get_value::("endpoint")? { - tlscred.endpoint = Some(endpoint); - } - if let Some(verifypeer) = cmd_parser.get_value::("verify-peer")? { - tlscred.verifypeer = verifypeer == *"true"; - } - tlscred.cred_type = "x509".to_string(); - + let tlscred = + TlsCredObjConfig::try_parse_from(str_slip_to_clap(tlscred_config, true, false))?; let id = tlscred.id.clone(); - if self.object.tls_object.get(&id).is_none() { - self.object.tls_object.insert(id, tlscred); - } else { + if self.object.tls_object.get(&id).is_some() { return Err(anyhow!(ConfigError::IdRepeat("tlscred".to_string(), id))); } - + self.object.tls_object.insert(id, tlscred); Ok(()) } } diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index 76af9e8d4..ac577df1d 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -221,7 +221,7 @@ impl SecurityType { // Tls configuration. if let Some(tls_cred) = object.tls_object.get(&vnc_cfg.tls_creds) { let tlscred = TlsCreds { - cred_type: tls_cred.cred_type.clone(), + cred_type: "x509".to_string(), dir: tls_cred.dir.clone(), endpoint: tls_cred.endpoint.clone(), verifypeer: tls_cred.verifypeer, -- Gitee From 39ea60910c2723e74cfb57ecdb2956d50a35e217 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 22 Mar 2024 19:56:10 +0800 Subject: [PATCH 1779/2187] rng: use clap to parse the parameters of the rng config Use clap to parse the parameters of the rng config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 28 ++-- machine_manager/src/config/mod.rs | 9 +- machine_manager/src/config/rng.rs | 241 +----------------------------- virtio/src/device/rng.rs | 150 +++++++++++++++---- virtio/src/lib.rs | 2 +- 5 files changed, 151 insertions(+), 279 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7fc1b3965..ab1429b23 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -74,11 +74,11 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, - parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev, - parse_root_port, parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, - str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, - MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, - SerialConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_root_port, + parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, + BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, + NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VmConfig, FAST_UNPLUG_ON, + MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -95,7 +95,7 @@ use vfio::{VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block, - BlockState, Rng, RngState, + BlockState, Rng, RngConfig, RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}, Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, @@ -810,19 +810,25 @@ pub trait MachineOps { /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration arguments. fn add_virtio_rng(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let rng_cfg = parse_rng_dev(vm_config, cfg_args)?; + let rng_cfg = RngConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + rng_cfg.bytes_per_sec()?; + let rngobj_cfg = vm_config + .object + .rng_object + .remove(&rng_cfg.rng) + .with_context(|| "Object for rng-random device not found")?; let sys_mem = self.get_sys_mem(); - let rng_dev = Arc::new(Mutex::new(Rng::new(rng_cfg.clone()))); + let rng_dev = Arc::new(Mutex::new(Rng::new(rng_cfg.clone(), rngobj_cfg))); - match parse_device_type(cfg_args)?.as_str() { + match rng_cfg.classtype.as_str() { "virtio-rng-device" => { let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); self.realize_virtio_mmio_device(device) .with_context(|| "Failed to add virtio mmio rng device")?; } _ => { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; + let bdf = PciBdf::new(rng_cfg.bus.clone().unwrap(), rng_cfg.addr.unwrap()); + let multi_func = rng_cfg.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&rng_cfg.id, &bdf, rng_dev.clone(), multi_func, false) .with_context(|| "Failed to add pci rng device")?; } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index b8742e513..bb69d1e08 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -66,6 +66,7 @@ use std::path::Path; use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use log::error; use serde::{Deserialize, Serialize}; @@ -209,13 +210,13 @@ impl VmConfig { .with_context(|| "Failed to add iothread")?; } "rng-random" => { - let rng_cfg = parse_rng_obj(object_args)?; + let rng_cfg = + RngObjConfig::try_parse_from(str_slip_to_clap(object_args, true, false))?; let id = rng_cfg.id.clone(); - if self.object.rng_object.get(&id).is_none() { - self.object.rng_object.insert(id, rng_cfg); - } else { + if self.object.rng_object.get(&id).is_some() { bail!("Object: {} has been added", id); } + self.object.rng_object.insert(id, rng_cfg); } "memory-backend-ram" | "memory-backend-file" | "memory-backend-memfd" => { self.add_mem_zone(object_args, device_type)?; diff --git a/machine_manager/src/config/rng.rs b/machine_manager/src/config/rng.rs index b153dcf2e..78c3ef79c 100644 --- a/machine_manager/src/config/rng.rs +++ b/machine_manager/src/config/rng.rs @@ -10,243 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use serde::{Deserialize, Serialize}; -use super::error::ConfigError; -use super::pci_args_check; -use crate::config::{CmdParser, ConfigCheck, VmConfig, MAX_PATH_LENGTH}; +use crate::config::{valid_id, valid_path}; -const MIN_BYTES_PER_SEC: u64 = 64; -const MAX_BYTES_PER_SEC: u64 = 1_000_000_000; - -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct RngObjConfig { + #[arg(long, value_parser = ["rng-random"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] pub id: String, + #[arg(long, value_parser = valid_path)] pub filename: String, } - -/// Config structure for virtio-rng. -#[derive(Debug, Clone, Default)] -pub struct RngConfig { - pub id: String, - pub random_file: String, - pub bytes_per_sec: Option, -} - -impl ConfigCheck for RngConfig { - fn check(&self) -> Result<()> { - if self.id.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "rng id".to_string(), - MAX_PATH_LENGTH - ))); - } - - if self.random_file.len() > MAX_PATH_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "rng random file".to_string(), - MAX_PATH_LENGTH, - ))); - } - - if let Some(bytes_per_sec) = self.bytes_per_sec { - if !(MIN_BYTES_PER_SEC..=MAX_BYTES_PER_SEC).contains(&bytes_per_sec) { - return Err(anyhow!(ConfigError::IllegalValue( - "The bytes per second of rng device".to_string(), - MIN_BYTES_PER_SEC, - true, - MAX_BYTES_PER_SEC, - true, - ))); - } - } - - Ok(()) - } -} - -pub fn parse_rng_dev(vm_config: &mut VmConfig, rng_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("rng"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("multifunction") - .push("max-bytes") - .push("period") - .push("rng"); - - cmd_parser.parse(rng_config)?; - pci_args_check(&cmd_parser)?; - let mut rng_cfg = RngConfig::default(); - let rng = cmd_parser - .get_value::("rng")? - .with_context(|| ConfigError::FieldIsMissing("rng".to_string(), "rng".to_string()))?; - - rng_cfg.id = cmd_parser.get_value::("id")?.unwrap_or_default(); - - if let Some(max) = cmd_parser.get_value::("max-bytes")? { - if let Some(peri) = cmd_parser.get_value::("period")? { - let mul = max - .checked_mul(1000) - .with_context(|| format!("Illegal max-bytes arguments: {:?}", max))?; - let div = mul - .checked_div(peri) - .with_context(|| format!("Illegal period arguments: {:?}", peri))?; - rng_cfg.bytes_per_sec = Some(div); - } else { - bail!("Argument 'period' is missing"); - } - } else if cmd_parser.get_value::("period")?.is_some() { - bail!("Argument 'max-bytes' is missing"); - } - - rng_cfg.random_file = vm_config - .object - .rng_object - .remove(&rng) - .map(|rng_object| rng_object.filename) - .with_context(|| "Object for rng-random device not found")?; - - rng_cfg.check()?; - Ok(rng_cfg) -} - -pub fn parse_rng_obj(object_args: &str) -> Result { - let mut cmd_params = CmdParser::new("rng-object"); - cmd_params.push("").push("id").push("filename"); - - cmd_params.parse(object_args)?; - let id = cmd_params - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "rng-object".to_string()))?; - let filename = cmd_params - .get_value::("filename")? - .with_context(|| { - ConfigError::FieldIsMissing("filename".to_string(), "rng-object".to_string()) - })?; - let rng_obj_cfg = RngObjConfig { id, filename }; - - Ok(rng_obj_cfg) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::get_pci_bdf; - - #[test] - fn test_rng_config_cmdline_parser_01() { - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_config = parse_rng_dev(&mut vm_config, "virtio-rng-device,rng=objrng0"); - assert!(rng_config.is_ok()); - let config = rng_config.unwrap(); - assert_eq!(config.random_file, "/path/to/random_file"); - assert_eq!(config.bytes_per_sec, None); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_config = parse_rng_dev( - &mut vm_config, - "virtio-rng-device,rng=objrng0,max-bytes=1234,period=1000", - ); - assert!(rng_config.is_ok()); - let config = rng_config.unwrap(); - assert_eq!(config.random_file, "/path/to/random_file"); - assert_eq!(config.bytes_per_sec, Some(1234)); - } - - #[test] - fn test_rng_config_cmdline_parser_02() { - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_config = parse_rng_dev( - &mut vm_config, - "virtio-rng-device,rng=objrng0,max-bytes=63,period=1000", - ); - assert!(rng_config.is_err()); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_config = parse_rng_dev( - &mut vm_config, - "virtio-rng-device,rng=objrng0,max-bytes=64,period=1000", - ); - assert!(rng_config.is_ok()); - let config = rng_config.unwrap(); - assert_eq!(config.random_file, "/path/to/random_file"); - assert_eq!(config.bytes_per_sec, Some(64)); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_config = parse_rng_dev( - &mut vm_config, - "virtio-rng-device,rng=objrng0,max-bytes=1000000000,period=1000", - ); - assert!(rng_config.is_ok()); - let config = rng_config.unwrap(); - assert_eq!(config.random_file, "/path/to/random_file"); - assert_eq!(config.bytes_per_sec, Some(1000000000)); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_config = parse_rng_dev( - &mut vm_config, - "virtio-rng-device,rng=objrng0,max-bytes=1000000001,period=1000", - ); - assert!(rng_config.is_err()); - } - - #[test] - fn test_pci_rng_config_cmdline_parser() { - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_cfg = "virtio-rng-pci,rng=objrng0,bus=pcie.0,addr=0x1.0x3"; - let rng_config = parse_rng_dev(&mut vm_config, rng_cfg); - assert!(rng_config.is_ok()); - let config = rng_config.unwrap(); - assert_eq!(config.random_file, "/path/to/random_file"); - assert_eq!(config.bytes_per_sec, None); - let pci_bdf = get_pci_bdf(rng_cfg); - assert!(pci_bdf.is_ok()); - let pci = pci_bdf.unwrap(); - assert_eq!(pci.bus, "pcie.0".to_string()); - assert_eq!(pci.addr, (1, 3)); - - // object "objrng0" has been removed. - let rng_config = parse_rng_dev(&mut vm_config, rng_cfg); - assert!(rng_config.is_err()); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_cfg = "virtio-rng-device,rng=objrng0,bus=pcie.0,addr=0x1.0x3"; - let rng_config = parse_rng_dev(&mut vm_config, rng_cfg); - assert!(rng_config.is_err()); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_object("rng-random,id=objrng0,filename=/path/to/random_file") - .is_ok()); - let rng_cfg = "virtio-rng-pci,rng=objrng0,bus=pcie.0,addr=0x1.0x3,multifunction=on"; - assert!(parse_rng_dev(&mut vm_config, rng_cfg).is_ok()); - } -} diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index c44105241..9f1007ed2 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -18,7 +18,8 @@ use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use log::error; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -30,7 +31,7 @@ use crate::{ }; use address_space::AddressSpace; use machine_manager::{ - config::{RngConfig, DEFAULT_VIRTQUEUE_SIZE}, + config::{get_pci_df, valid_id, ConfigError, RngObjConfig, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, event_loop::{register_event_helper, unregister_event_helper}, }; @@ -46,6 +47,62 @@ use util::loop_context::{ const QUEUE_NUM_RNG: usize = 1; const RNG_SIZE_MAX: u32 = 1 << 20; +const MIN_BYTES_PER_SEC: u64 = 64; +const MAX_BYTES_PER_SEC: u64 = 1_000_000_000; + +/// Config structure for virtio-rng. +#[derive(Parser, Debug, Clone, Default)] +#[command(no_binary_name(true))] +pub struct RngConfig { + #[arg(long, value_parser = ["virtio-rng-device", "virtio-rng-pci"])] + pub classtype: String, + #[arg(long, default_value = "", value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub rng: String, + #[arg(long, alias = "max-bytes")] + pub max_bytes: Option, + #[arg(long)] + pub period: Option, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long)] + pub multifunction: Option, +} + +impl RngConfig { + pub fn bytes_per_sec(&self) -> Result> { + if self.max_bytes.is_some() != self.period.is_some() { + bail!("\"max_bytes\" and \"period\" should be configured or not configured Simultaneously."); + } + + if let Some(max) = self.max_bytes { + let peri = self.period.unwrap(); + let mul = max + .checked_mul(1000) + .with_context(|| format!("Illegal max-bytes arguments: {:?}", max))?; + let bytes_per_sec = mul + .checked_div(peri) + .with_context(|| format!("Illegal period arguments: {:?}", peri))?; + + if !(MIN_BYTES_PER_SEC..=MAX_BYTES_PER_SEC).contains(&bytes_per_sec) { + return Err(anyhow!(ConfigError::IllegalValue( + "The bytes per second of rng device".to_string(), + MIN_BYTES_PER_SEC, + true, + MAX_BYTES_PER_SEC, + true, + ))); + } + + return Ok(Some(bytes_per_sec)); + } + Ok(None) + } +} + fn get_req_data_size(in_iov: &[ElemIovec]) -> Result { let mut size = 0_u32; for iov in in_iov { @@ -216,34 +273,37 @@ pub struct RngState { pub struct Rng { /// Virtio device base property. base: VirtioBase, - /// Configuration of virtio rng device + /// Configuration of virtio rng device. rng_cfg: RngConfig, + /// Configuration of rng-random. + rngobj_cfg: RngObjConfig, /// The file descriptor of random number generator random_file: Option, } impl Rng { - pub fn new(rng_cfg: RngConfig) -> Self { + pub fn new(rng_cfg: RngConfig, rngobj_cfg: RngObjConfig) -> Self { Rng { base: VirtioBase::new(VIRTIO_TYPE_RNG, QUEUE_NUM_RNG, DEFAULT_VIRTQUEUE_SIZE), rng_cfg, + rngobj_cfg, ..Default::default() } } fn check_random_file(&self) -> Result<()> { - let path = Path::new(&self.rng_cfg.random_file); + let path = Path::new(&self.rngobj_cfg.filename); if !path.exists() { bail!( "The path of random file {} is not existed", - self.rng_cfg.random_file + self.rngobj_cfg.filename ); } if !path.metadata().unwrap().file_type().is_char_device() { bail!( "The type of random file {} is not a character special file", - self.rng_cfg.random_file + self.rngobj_cfg.filename ); } @@ -263,7 +323,7 @@ impl VirtioDevice for Rng { fn realize(&mut self) -> Result<()> { self.check_random_file() .with_context(|| "Failed to check random file")?; - let file = File::open(&self.rng_cfg.random_file) + let file = File::open(&self.rngobj_cfg.filename) .with_context(|| "Failed to open file of random number generator")?; self.random_file = Some(file); self.init_config_features()?; @@ -308,7 +368,7 @@ impl VirtioDevice for Rng { .unwrap() .try_clone() .with_context(|| "Failed to clone random file for virtio rng")?, - leak_bucket: match self.rng_cfg.bytes_per_sec { + leak_bucket: match self.rng_cfg.bytes_per_sec()? { Some(bps) => Some(LeakBucket::new(bps)?), None => None, }, @@ -361,7 +421,7 @@ mod tests { use super::*; use crate::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use machine_manager::config::{RngConfig, DEFAULT_VIRTQUEUE_SIZE}; + use machine_manager::config::{str_slip_to_clap, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; const VIRTQ_DESC_F_NEXT: u16 = 0x01; const VIRTQ_DESC_F_WRITE: u16 = 0x02; @@ -393,21 +453,60 @@ mod tests { sys_space } + #[test] + fn test_rng_config_cmdline_parse() { + // Test1: Right rng-random. + let mut vm_config = VmConfig::default(); + assert!(vm_config + .add_object("rng-random,id=objrng0,filename=/path/to/random_file") + .is_ok()); + let rngobj_cfg = vm_config.object.rng_object.remove("objrng0").unwrap(); + assert_eq!(rngobj_cfg.filename, "/path/to/random_file"); + + // Test2: virtio-rng-device + let rng_cmd = "virtio-rng-device,rng=objrng0"; + let rng_config = RngConfig::try_parse_from(str_slip_to_clap(rng_cmd, true, false)).unwrap(); + assert_eq!(rng_config.bytes_per_sec().unwrap(), None); + assert_eq!(rng_config.multifunction, None); + + // Test3: virtio-rng-pci. + let rng_cmd = "virtio-rng-pci,bus=pcie.0,addr=0x1,rng=objrng0,max-bytes=1234,period=1000"; + let rng_config = RngConfig::try_parse_from(str_slip_to_clap(rng_cmd, true, false)).unwrap(); + assert_eq!(rng_config.bytes_per_sec().unwrap(), Some(1234)); + assert_eq!(rng_config.bus.unwrap(), "pcie.0"); + assert_eq!(rng_config.addr.unwrap(), (1, 0)); + + // Test4: Illegal max-bytes/period. + let rng_cmd = "virtio-rng-device,rng=objrng0,max-bytes=63,period=1000"; + let rng_config = RngConfig::try_parse_from(str_slip_to_clap(rng_cmd, true, false)).unwrap(); + assert!(rng_config.bytes_per_sec().is_err()); + + let rng_cmd = "virtio-rng-device,rng=objrng0,max-bytes=1000000001,period=1000"; + let rng_config = RngConfig::try_parse_from(str_slip_to_clap(rng_cmd, true, false)).unwrap(); + assert!(rng_config.bytes_per_sec().is_err()); + } + #[test] fn test_rng_init() { - let file = TempFile::new().unwrap(); - let random_file = file.as_path().to_str().unwrap().to_string(); + let rngobj_config = RngObjConfig { + classtype: "rng-random".to_string(), + id: "rng0".to_string(), + filename: "".to_string(), + }; let rng_config = RngConfig { - id: "".to_string(), - random_file: random_file.clone(), - bytes_per_sec: Some(64), + classtype: "virtio-rng-pci".to_string(), + rng: "rng0".to_string(), + max_bytes: Some(64), + period: Some(1000), + bus: Some("pcie.0".to_string()), + addr: Some((3, 0)), + ..Default::default() }; - let rng = Rng::new(rng_config); + let rng = Rng::new(rng_config, rngobj_config); assert!(rng.random_file.is_none()); assert_eq!(rng.base.driver_features, 0_u64); assert_eq!(rng.base.device_features, 0_u64); - assert_eq!(rng.rng_cfg.random_file, random_file); - assert_eq!(rng.rng_cfg.bytes_per_sec, Some(64)); + assert_eq!(rng.rng_cfg.bytes_per_sec().unwrap().unwrap(), 64); assert_eq!(rng.queue_num(), QUEUE_NUM_RNG); assert_eq!(rng.queue_size_max(), DEFAULT_VIRTQUEUE_SIZE); @@ -416,18 +515,9 @@ mod tests { #[test] fn test_rng_features() { - let random_file = TempFile::new() - .unwrap() - .as_path() - .to_str() - .unwrap() - .to_string(); - let rng_config = RngConfig { - id: "".to_string(), - random_file, - bytes_per_sec: Some(64), - }; - let mut rng = Rng::new(rng_config); + let rng_config = RngConfig::default(); + let rngobj_cfg = RngObjConfig::default(); + let mut rng = Rng::new(rng_config, rngobj_cfg); // If the device feature is 0, all driver features are not supported. rng.base.device_features = 0; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9ef9dde8b..750d6e1b8 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -37,7 +37,7 @@ pub use device::block::{Block, BlockState, VirtioBlkConfig}; #[cfg(feature = "virtio_gpu")] pub use device::gpu::*; pub use device::net::*; -pub use device::rng::{Rng, RngState}; +pub use device::rng::{Rng, RngConfig, RngState}; pub use device::scsi_cntlr as ScsiCntlr; pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; pub use error::VirtioError; -- Gitee From 20be4af0111057540fd7265c6b0d87a6a7b575e8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 23 Mar 2024 01:04:33 +0800 Subject: [PATCH 1780/2187] rootport: use clap to parse the parameters of the rootport config Use clap to parse the parameters of the rootport config. Signed-off-by: liuxiangdong --- devices/src/pci/bus.rs | 17 +++++++-- devices/src/pci/host.rs | 23 ++++++++++-- devices/src/pci/mod.rs | 2 +- devices/src/pci/root_port.rs | 62 ++++++++++++++++++++++--------- machine/src/lib.rs | 28 ++++++-------- machine_manager/src/config/pci.rs | 62 +------------------------------ 6 files changed, 91 insertions(+), 103 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index d493e1812..797466636 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -275,7 +275,7 @@ mod tests { use crate::pci::bus::PciBus; use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; use crate::pci::root_port::RootPort; - use crate::pci::{PciDevBase, PciHost}; + use crate::pci::{PciDevBase, PciHost, RootPortConfig}; use crate::{Device, DeviceBase}; use address_space::{AddressSpace, Region}; @@ -372,8 +372,12 @@ mod tests { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone(), false); + let root_port_config = RootPortConfig { + addr: (1, 0), + id: "pcie.1".to_string(), + ..Default::default() + }; + let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); // Test device is attached to the root bus. @@ -421,7 +425,12 @@ mod tests { let locked_pci_host = pci_host.lock().unwrap(); let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone(), false); + let root_port_config = RootPortConfig { + id: "pcie.1".to_string(), + addr: (1, 0), + ..Default::default() + }; + let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index b6a44dabd..f39571560 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -547,7 +547,7 @@ pub mod tests { use super::*; use crate::pci::bus::PciBus; use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; - use crate::pci::root_port::RootPort; + use crate::pci::root_port::{RootPort, RootPortConfig}; use crate::pci::{PciDevBase, Result}; use crate::{Device, DeviceBase}; use address_space::Region; @@ -658,7 +658,12 @@ pub mod tests { let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); let pio_addr_ops = PciHost::build_pio_addr_ops(pci_host.clone()); let pio_data_ops = PciHost::build_pio_data_ops(pci_host.clone()); - let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false); + let root_port_config = RootPortConfig { + addr: (1, 0), + id: "pcie.1".to_string(), + ..Default::default() + }; + let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); let mut data = [0_u8; 4]; @@ -735,10 +740,20 @@ pub mod tests { let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(pci_host.clone()); - let mut root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone(), false); + let root_port_config = RootPortConfig { + addr: (1, 0), + id: "pcie.1".to_string(), + ..Default::default() + }; + let mut root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.write_config(SECONDARY_BUS_NUM as usize, &[1]); root_port.realize().unwrap(); - let mut root_port = RootPort::new("pcie.2".to_string(), 16, 0, root_bus, false); + let root_port_config = RootPortConfig { + addr: (2, 0), + id: "pcie.2".to_string(), + ..Default::default() + }; + let mut root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.write_config(SECONDARY_BUS_NUM as usize, &[2]); root_port.realize().unwrap(); diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 0c8c0af3e..c29d7a0d0 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -28,7 +28,7 @@ pub use error::PciError; pub use host::PciHost; pub use intx::{init_intx, InterruptHandler, PciIntxState}; pub use msix::{init_msix, MsiVector}; -pub use root_port::RootPort; +pub use root_port::{RootPort, RootPortConfig}; use std::{ mem::size_of, diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 401302773..6396c0ce8 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -14,6 +14,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; use anyhow::{anyhow, bail, Context, Result}; +use clap::{ArgAction, Parser}; use log::{error, info}; use once_cell::sync::OnceCell; @@ -39,17 +40,41 @@ use crate::pci::{ }; use crate::{Device, DeviceBase, MsiIrqManager}; use address_space::Region; +use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use machine_manager::qmp::qmp_channel::send_device_deleted_msg; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::{byte_code::ByteCode, num_ops::ranges_overlap}; +use util::{ + byte_code::ByteCode, + num_ops::{ranges_overlap, str_to_num}, +}; const DEVICE_ID_RP: u16 = 0x000c; static FAST_UNPLUG_FEATURE: OnceCell = OnceCell::new(); +/// Basic information of RootPort like port number. +#[derive(Parser, Debug, Clone, Default)] +#[command(no_binary_name(true))] +pub struct RootPortConfig { + #[arg(long, value_parser = ["pcie-root-port"])] + pub classtype: String, + #[arg(long, value_parser = str_to_num::)] + pub port: u8, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: String, + #[arg(long, value_parser = get_pci_df)] + pub addr: (u8, u8), + #[arg(long, default_value = "off", value_parser = parse_bool, action = ArgAction::Append)] + pub multifunction: bool, + #[arg(long, default_value = "0")] + pub chassis: u8, +} + /// Device state root port. #[repr(C)] #[derive(Copy, Clone, Desc, ByteCode)] @@ -81,22 +106,15 @@ impl RootPort { /// /// # Arguments /// - /// * `name` - Root port name. - /// * `devfn` - Device number << 3 | Function number. - /// * `port_num` - Root port number. + /// * `cfg` - Root port config. /// * `parent_bus` - Weak reference to the parent bus. - pub fn new( - name: String, - devfn: u8, - port_num: u8, - parent_bus: Weak>, - multifunction: bool, - ) -> Self { + pub fn new(cfg: RootPortConfig, parent_bus: Weak>) -> Self { + let devfn = cfg.addr.0 << 3 | cfg.addr.1; #[cfg(target_arch = "x86_64")] let io_region = Region::init_container_region(1 << 16, "RootPortIo"); let mem_region = Region::init_container_region(u64::max_value(), "RootPortMem"); let sec_bus = Arc::new(Mutex::new(PciBus::new( - name.clone(), + cfg.id.clone(), #[cfg(target_arch = "x86_64")] io_region.clone(), mem_region.clone(), @@ -104,18 +122,18 @@ impl RootPort { Self { base: PciDevBase { - base: DeviceBase::new(name, true), + base: DeviceBase::new(cfg.id, true), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, parent_bus, }, - port_num, + port_num: cfg.port, sec_bus, #[cfg(target_arch = "x86_64")] io_region, mem_region, dev_id: Arc::new(AtomicU16::new(0)), - multifunction, + multifunction: cfg.multifunction, hpev_notified: false, } } @@ -694,7 +712,12 @@ mod tests { fn test_read_config() { let pci_host = create_pci_host(); let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); - let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false); + let root_port_config = RootPortConfig { + addr: (1, 0), + id: "pcie.1".to_string(), + ..Default::default() + }; + let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap(); @@ -710,7 +733,12 @@ mod tests { fn test_write_config() { let pci_host = create_pci_host(); let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); - let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false); + let root_port_config = RootPortConfig { + addr: (1, 0), + id: "pcie.1".to_string(), + ..Default::default() + }; + let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap(); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index ab1429b23..f308f579f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -52,7 +52,7 @@ use devices::misc::pvpanic::{PvPanicPci, PvpanicDevConfig}; use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] use devices::pci::demo_device::{DemoDev, DemoDevConfig}; -use devices::pci::{PciBus, PciDevOps, PciHost, RootPort}; +use devices::pci::{PciBus, PciDevOps, PciHost, RootPort, RootPortConfig}; use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; @@ -74,10 +74,10 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, - parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_root_port, - parse_vhost_user_blk, parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, - BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, - NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VmConfig, FAST_UNPLUG_ON, + parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_vhost_user_blk, + parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, + BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, + NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; @@ -1352,21 +1352,15 @@ pub trait MachineOps { } fn add_pci_root_port(&mut self, cfg_args: &str) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; - let device_cfg = parse_root_port(cfg_args)?; + let dev_cfg = RootPortConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let bdf = PciBdf::new(dev_cfg.bus.clone(), dev_cfg.addr); + let (_, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let pci_host = self.get_pci_host()?; let bus = pci_host.lock().unwrap().root_bus.clone(); - if PciBus::find_bus_by_name(&bus, &device_cfg.id).is_some() { - bail!("ID {} already exists.", &device_cfg.id); + if PciBus::find_bus_by_name(&bus, &dev_cfg.id).is_some() { + bail!("ID {} already exists.", &dev_cfg.id); } - let rootport = RootPort::new( - device_cfg.id, - devfn, - device_cfg.port, - parent_bus, - device_cfg.multifunction, - ); + let rootport = RootPort::new(dev_cfg, parent_bus); rootport .realize() .with_context(|| "Failed to add pci root port")?; diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index e1ad4985a..ce53656e0 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -13,9 +13,8 @@ use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; -use super::error::ConfigError; -use super::{CmdParser, ConfigCheck, UnsignedInteger}; -use crate::config::{check_arg_too_long, ExBool}; +use super::CmdParser; +use crate::config::ExBool; use util::num_ops::str_to_num; /// Basic information of pci devices such as bus number, @@ -43,30 +42,6 @@ impl Default for PciBdf { } } -/// Basic information of RootPort like port number. -#[derive(Debug, Clone)] -pub struct RootPortConfig { - pub port: u8, - pub id: String, - pub multifunction: bool, -} - -impl ConfigCheck for RootPortConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "root_port id") - } -} - -impl Default for RootPortConfig { - fn default() -> Self { - RootPortConfig { - port: 0, - id: "".to_string(), - multifunction: false, - } - } -} - pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { let addr_vec: Vec<&str> = addr.split('.').collect(); if addr_vec.len() > 2 { @@ -129,39 +104,6 @@ pub fn get_multi_function(pci_cfg: &str) -> Result { Ok(false) } -pub fn parse_root_port(rootport_cfg: &str) -> Result { - let mut cmd_parser = CmdParser::new("pcie-root-port"); - cmd_parser - .push("") - .push("bus") - .push("addr") - .push("port") - .push("chassis") - .push("multifunction") - .push("id"); - cmd_parser.parse(rootport_cfg)?; - - let root_port = RootPortConfig { - port: cmd_parser - .get_value::("port")? - .with_context(|| { - ConfigError::FieldIsMissing("port".to_string(), "rootport".to_string()) - })? - .0 as u8, - id: cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "rootport".to_string()) - })?, - multifunction: cmd_parser - .get_value::("multifunction")? - .map_or(false, bool::from), - }; - - let _ = cmd_parser.get_value::("chassis")?; - - root_port.check()?; - Ok(root_port) -} - pub fn pci_args_check(cmd_parser: &CmdParser) -> Result<()> { let device_type = cmd_parser.get_value::("")?; let dev_type = device_type.unwrap(); -- Gitee From d76e9a212584642c523b0c071d9f812587cdd96b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 24 Mar 2024 10:43:05 +0800 Subject: [PATCH 1781/2187] virtio-blk/vhost-user-blk: use clap to parse the parameters of the virtio-blk/vhost-user-blk config Use clap to parse the parameters of the virtio-blk and vhost-user-blk config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 95 +++++--- machine/src/micro_common/mod.rs | 106 +++++---- machine_manager/src/config/chardev.rs | 17 +- machine_manager/src/config/drive.rs | 317 +------------------------- machine_manager/src/config/mod.rs | 19 +- machine_manager/src/config/network.rs | 6 +- virtio/src/device/block.rs | 218 +++++++++++++----- virtio/src/device/net.rs | 6 +- virtio/src/device/scsi_cntlr.rs | 12 +- virtio/src/lib.rs | 7 +- virtio/src/vhost/user/block.rs | 69 ++++-- virtio/src/vhost/user/mod.rs | 2 +- 12 files changed, 377 insertions(+), 497 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f308f579f..d21fed9f6 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -73,12 +73,11 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ - complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id, - parse_device_type, parse_net, parse_numa_distance, parse_numa_mem, parse_vhost_user_blk, - parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, - BootSource, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, - NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VmConfig, FAST_UNPLUG_ON, - MAX_VIRTIO_QUEUE, + complete_numa_node, get_multi_function, get_pci_bdf, parse_device_id, parse_device_type, + parse_net, parse_numa_distance, parse_numa_mem, parse_virtio_serial, parse_virtserialport, + parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, + MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, + SerialConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -97,8 +96,8 @@ use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block, BlockState, Rng, RngConfig, RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}, - Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, - VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, + Serial, SerialPort, VhostKern, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, + VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; #[cfg(feature = "virtio_gpu")] use virtio::{Gpu, GpuDevConfig}; @@ -1052,26 +1051,37 @@ pub trait MachineOps { cfg_args: &str, hotplug: bool, ) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( - 0, - vm_config.machine_config.nr_cpus, - MAX_VIRTIO_QUEUE, - )); - let device_cfg = parse_blk(vm_config, cfg_args, queues_auto)?; - if let Some(bootindex) = device_cfg.boot_index { + let mut device_cfg = + VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let bdf = PciBdf::new(device_cfg.bus.clone().unwrap(), device_cfg.addr.unwrap()); + let multi_func = device_cfg.multifunction.unwrap_or_default(); + if device_cfg.num_queues.is_none() { + let queues_auto = VirtioPciDevice::virtio_pci_auto_queues_num( + 0, + vm_config.machine_config.nr_cpus, + MAX_VIRTIO_QUEUE, + ); + device_cfg.num_queues = Some(queues_auto); + } + if let Some(bootindex) = device_cfg.bootindex { self.check_bootindex(bootindex) .with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?; } + + let drive_cfg = vm_config + .drives + .remove(&device_cfg.drive) + .with_context(|| "No drive configured matched for blk device")?; + let device = Arc::new(Mutex::new(Block::new( device_cfg.clone(), + drive_cfg, self.get_drive_files(), ))); let pci_dev = self .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false) .with_context(|| "Failed to add virtio pci device")?; - if let Some(bootindex) = device_cfg.boot_index { + if let Some(bootindex) = device_cfg.bootindex { // Eg: OpenFirmware device path(virtio-blk disk): // /pci@i0cf8/scsi@6[,3]/disk@0,0 // | | | | | @@ -1233,27 +1243,42 @@ pub trait MachineOps { cfg_args: &str, hotplug: bool, ) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num( - 0, - vm_config.machine_config.nr_cpus, - MAX_VIRTIO_QUEUE, - )); - let device_cfg = parse_vhost_user_blk(vm_config, cfg_args, queues_auto)?; + let mut device_cfg = VhostUser::VhostUserBlkDevConfig::try_parse_from(str_slip_to_clap( + cfg_args, true, false, + ))?; + let bdf = PciBdf::new(device_cfg.bus.clone().unwrap(), device_cfg.addr.unwrap()); + if device_cfg.num_queues.is_none() { + let queues_auto = VirtioPciDevice::virtio_pci_auto_queues_num( + 0, + vm_config.machine_config.nr_cpus, + MAX_VIRTIO_QUEUE, + ); + device_cfg.num_queues = Some(queues_auto); + } + let chardev_cfg = vm_config + .chardev + .remove(&device_cfg.chardev) + .with_context(|| { + format!( + "Chardev: {:?} not found for vhost user blk", + &device_cfg.chardev + ) + })?; + let device: Arc> = Arc::new(Mutex::new(VhostUser::Block::new( &device_cfg, + chardev_cfg, self.get_sys_mem(), ))); let pci_dev = self - .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, true) + .add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), false, true) .with_context(|| { format!( "Failed to add virtio pci device, device id: {}", &device_cfg.id ) })?; - if let Some(bootindex) = device_cfg.boot_index { + if let Some(bootindex) = device_cfg.bootindex { if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() { self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id); } @@ -1269,9 +1294,21 @@ pub trait MachineOps { vm_config: &mut VmConfig, cfg_args: &str, ) -> Result<()> { - let device_cfg = parse_vhost_user_blk(vm_config, cfg_args, None)?; + let device_cfg = VhostUser::VhostUserBlkDevConfig::try_parse_from(str_slip_to_clap( + cfg_args, true, false, + ))?; + let chardev_cfg = vm_config + .chardev + .remove(&device_cfg.chardev) + .with_context(|| { + format!( + "Chardev: {:?} not found for vhost user blk", + &device_cfg.chardev + ) + })?; let device: Arc> = Arc::new(Mutex::new(VhostUser::Block::new( &device_cfg, + chardev_cfg, self.get_sys_mem(), ))); let virtio_mmio_device = VirtioMmioDevice::new(self.get_sys_mem(), device); diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index b97f046ca..1ac459665 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -38,6 +38,7 @@ use std::sync::{Arc, Mutex}; use std::vec::Vec; use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use log::{error, info}; #[cfg(target_arch = "aarch64")] @@ -48,7 +49,7 @@ use crate::{MachineBase, MachineError, MachineOps}; use cpu::CpuLifecycleState; use devices::sysbus::{IRQ_BASE, IRQ_MAX}; use machine_manager::config::{ - parse_blk, parse_incoming_uri, parse_net, BlkDevConfig, ConfigCheck, DiskFormat, MigrateMode, + parse_incoming_uri, parse_net, str_slip_to_clap, ConfigCheck, DriveConfig, MigrateMode, NetworkInterfaceConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }; use machine_manager::event; @@ -61,8 +62,8 @@ use machine_manager::qmp::{ qmp_channel::QmpChannel, qmp_response::Response, qmp_schema, qmp_schema::UpdateRegionArgument, }; use migration::MigrationManager; -use util::aio::WriteZeroesState; use util::{loop_context::EventLoopManager, num_ops::str_to_num, set_termi_canon_mode}; +use virtio::device::block::VirtioBlkDevConfig; use virtio::{ create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, @@ -78,8 +79,8 @@ const MMIO_REPLACEABLE_NET_NR: usize = 2; struct MmioReplaceableConfig { // Device id. id: String, - // The dev_config of the related backend device. - dev_config: Arc, + // The config of the related backend device. Eg: Drive config of virtio mmio device. + back_config: Arc, } // The device information of replaceable device. @@ -158,7 +159,8 @@ impl LightMachine { let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { let block = Arc::new(Mutex::new(Block::new( - BlkDevConfig::default(), + VirtioBlkDevConfig::default(), + DriveConfig::default(), self.get_drive_files(), ))); let virtio_mmio = VirtioMmioDevice::new(&self.base.sys_mem, block.clone()); @@ -217,7 +219,7 @@ impl LightMachine { pub(crate) fn fill_replaceable_device( &mut self, id: &str, - dev_config: Arc, + dev_config: Vec>, index: usize, ) -> Result<()> { let mut replaceable_devices = self.replaceable_info.devices.lock().unwrap(); @@ -232,14 +234,14 @@ impl LightMachine { .device .lock() .unwrap() - .update_config(Some(dev_config.clone())) + .update_config(dev_config.clone()) .with_context(|| MachineError::UpdCfgErr(id.to_string()))?; } - self.add_replaceable_config(id, dev_config) + self.add_replaceable_config(id, dev_config[0].clone()) } - fn add_replaceable_config(&self, id: &str, dev_config: Arc) -> Result<()> { + fn add_replaceable_config(&self, id: &str, back_config: Arc) -> Result<()> { let mut configs_lock = self.replaceable_info.configs.lock().unwrap(); let limit = MMIO_REPLACEABLE_BLK_NR + MMIO_REPLACEABLE_NET_NR; if configs_lock.len() >= limit { @@ -254,7 +256,7 @@ impl LightMachine { let config = MmioReplaceableConfig { id: id.to_string(), - dev_config, + back_config, }; trace::mmio_replaceable_config(&config); @@ -262,21 +264,28 @@ impl LightMachine { Ok(()) } - fn add_replaceable_device(&self, id: &str, driver: &str, slot: usize) -> Result<()> { + fn add_replaceable_device( + &self, + args: Box, + slot: usize, + ) -> Result<()> { + let id = args.id; + let driver = args.driver; + // Find the configuration by id. let configs_lock = self.replaceable_info.configs.lock().unwrap(); - let mut dev_config = None; + let mut configs = Vec::new(); for config in configs_lock.iter() { if config.id == id { - dev_config = Some(config.dev_config.clone()); + configs.push(config.back_config.clone()); } } - if dev_config.is_none() { + if configs.is_empty() { bail!("Failed to find device configuration."); } // Sanity check for config, driver and slot. - let cfg_any = dev_config.as_ref().unwrap().as_any(); + let cfg_any = configs[0].as_any(); let index = if driver.contains("net") { if slot >= MMIO_REPLACEABLE_NET_NR { return Err(anyhow!(MachineError::RplDevLmtErr( @@ -295,9 +304,19 @@ impl LightMachine { MMIO_REPLACEABLE_BLK_NR ))); } - if cfg_any.downcast_ref::().is_none() { + if cfg_any.downcast_ref::().is_none() { return Err(anyhow!(MachineError::DevTypeErr("blk".to_string()))); } + let dev_config = VirtioBlkDevConfig { + classtype: driver, + id: id.clone(), + drive: args.drive.with_context(|| "No drive set")?, + bootindex: args.boot_index, + iothread: args.iothread, + serial: args.serial_num, + ..Default::default() + }; + configs.push(Arc::new(dev_config)); slot } else { bail!("Unsupported replaceable device type."); @@ -316,7 +335,7 @@ impl LightMachine { .device .lock() .unwrap() - .update_config(dev_config) + .update_config(configs) .with_context(|| MachineError::UpdCfgErr(id.to_string()))?; } Ok(()) @@ -328,8 +347,10 @@ impl LightMachine { let mut configs_lock = self.replaceable_info.configs.lock().unwrap(); for (index, config) in configs_lock.iter().enumerate() { if config.id == id { - if let Some(blkconf) = config.dev_config.as_any().downcast_ref::() { - self.unregister_drive_file(&blkconf.path_on_host)?; + if let Some(drive_config) = + config.back_config.as_any().downcast_ref::() + { + self.unregister_drive_file(&drive_config.path_on_host)?; } configs_lock.remove(index); is_exist = true; @@ -347,7 +368,7 @@ impl LightMachine { .device .lock() .unwrap() - .update_config(None) + .update_config(Vec::new()) .with_context(|| MachineError::UpdCfgErr(id.to_string()))?; } } @@ -387,7 +408,11 @@ impl LightMachine { MMIO_REPLACEABLE_NET_NR ); } - self.fill_replaceable_device(&device_cfg.id, Arc::new(device_cfg.clone()), index)?; + self.fill_replaceable_device( + &device_cfg.id, + vec![Arc::new(device_cfg.clone())], + index, + )?; self.replaceable_info.net_count += 1; } Ok(()) @@ -398,7 +423,12 @@ impl LightMachine { vm_config: &mut VmConfig, cfg_args: &str, ) -> Result<()> { - let device_cfg = parse_blk(vm_config, cfg_args, None)?; + let device_cfg = + VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let drive_cfg = vm_config + .drives + .remove(&device_cfg.drive) + .with_context(|| "No drive configured matched for blk device")?; if self.replaceable_info.block_count >= MMIO_REPLACEABLE_BLK_NR { bail!( "A maximum of {} block replaceable devices are supported.", @@ -406,7 +436,9 @@ impl LightMachine { ); } let index = self.replaceable_info.block_count; - self.fill_replaceable_device(&device_cfg.id, Arc::new(device_cfg.clone()), index)?; + let configs: Vec> = + vec![Arc::new(drive_cfg), Arc::new(device_cfg.clone())]; + self.fill_replaceable_device(&device_cfg.id, configs, index)?; self.replaceable_info.block_count += 1; Ok(()) } @@ -668,7 +700,7 @@ impl DeviceInterface for LightMachine { fn device_add(&mut self, args: Box) -> Response { // get slot of bus by addr or lun let mut slot = 0; - if let Some(addr) = args.addr { + if let Some(addr) = args.addr.clone() { if let Ok(num) = str_to_num::(&addr) { slot = num; } else { @@ -684,7 +716,7 @@ impl DeviceInterface for LightMachine { slot = lun + 1; } - match self.add_replaceable_device(&args.id, &args.driver, slot) { + match self.add_replaceable_device(args.clone(), slot) { Ok(()) => Response::create_empty_response(), Err(ref e) => { error!("{:?}", e); @@ -719,32 +751,22 @@ impl DeviceInterface for LightMachine { } fn blockdev_add(&self, args: Box) -> Response { - let read_only = args.read_only.unwrap_or(false); + let readonly = args.read_only.unwrap_or(false); let mut direct = true; if args.cache.is_some() && !args.cache.unwrap().direct.unwrap_or(true) { direct = false; } - let config = BlkDevConfig { + let config = DriveConfig { id: args.node_name.clone(), + drive_type: "none".to_string(), path_on_host: args.file.filename.clone(), - read_only, + readonly, direct, - serial_num: None, - iothread: None, - iops: None, - queues: 1, - boot_index: None, - chardev: None, - socket_path: None, aio: args.file.aio, - queue_size: DEFAULT_VIRTQUEUE_SIZE, - discard: false, - write_zeroes: WriteZeroesState::Off, - format: DiskFormat::Raw, - l2_cache_size: None, - refcount_cache_size: None, + ..Default::default() }; + if let Err(e) = config.check() { error!("{:?}", e); return Response::create_error_response( @@ -753,7 +775,7 @@ impl DeviceInterface for LightMachine { ); } // Register drive backend file for hotplugged drive. - if let Err(e) = self.register_drive_file(&config.id, &args.file.filename, read_only, direct) + if let Err(e) = self.register_drive_file(&config.id, &args.file.filename, readonly, direct) { error!("{:?}", e); return Response::create_error_response( diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index f5ea60677..68c23c6ea 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -199,22 +199,17 @@ pub fn get_chardev_config(args: qmp_schema::CharDevAddArgument) -> Result Result { - let char_dev = vm_config - .chardev - .remove(chardev) - .with_context(|| format!("Chardev: {:?} not found for character device", chardev))?; +pub fn get_chardev_socket_path(chardev: ChardevConfig) -> Result { + let id = chardev.id(); if let ChardevType::Socket { path, server, nowait, .. - } = char_dev.classtype + } = chardev.classtype { - path.clone().with_context(|| { - format!("Chardev {:?} backend should be unix-socket type.", chardev) - })?; + path.clone() + .with_context(|| format!("Chardev {:?} backend should be unix-socket type.", id))?; if server || nowait { bail!( "Argument \'server\' or \'nowait\' is not need for chardev \'{}\'", @@ -223,7 +218,7 @@ pub fn get_chardev_socket_path(chardev: &str, vm_config: &mut VmConfig) -> Resul } return Ok(path.unwrap()); } - bail!("Chardev {:?} backend should be unix-socket type.", chardev); + bail!("Chardev {:?} backend should be unix-socket type.", id); } pub fn parse_virtserialport( diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index a58d00129..3107ce757 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -20,24 +20,16 @@ use clap::{ArgAction, Parser}; use log::error; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, pci_args_check, M}; +use super::{error::ConfigError, M}; use super::{valid_id, valid_path}; use crate::config::{ - check_arg_too_long, get_chardev_socket_path, memory_unit_conversion, parse_bool, - str_slip_to_clap, CmdParser, ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_STRING_LENGTH, - MAX_VIRTIO_QUEUE, + memory_unit_conversion, parse_bool, str_slip_to_clap, ConfigCheck, VmConfig, MAX_STRING_LENGTH, }; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; -const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; const MAX_UNIT_ID: usize = 2; -// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-blk should be larger than 2. -const MIN_QUEUE_SIZE_BLK: u16 = 2; -// Max size of each virtqueue for virtio-blk. -const MAX_QUEUE_SIZE_BLK: u16 = 1024; - // L2 Cache max size is 32M. pub const MAX_L2_CACHE_SIZE: u64 = 32 * (1 << 20); // Refcount table cache max size is 32M. @@ -63,29 +55,6 @@ pub struct DriveFile { pub buf_align: u32, } -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct BlkDevConfig { - pub id: String, - pub path_on_host: String, - pub read_only: bool, - pub direct: bool, - pub serial_num: Option, - pub iothread: Option, - pub iops: Option, - pub queues: u16, - pub boot_index: Option, - pub chardev: Option, - pub socket_path: Option, - pub aio: AioEngine, - pub queue_size: u16, - pub discard: bool, - pub write_zeroes: WriteZeroesState, - pub format: DiskFormat, - pub l2_cache_size: Option, - pub refcount_cache_size: Option, -} - #[derive(Debug, Clone)] pub struct BootIndexInfo { pub boot_index: u8, @@ -93,31 +62,6 @@ pub struct BootIndexInfo { pub dev_path: String, } -impl Default for BlkDevConfig { - fn default() -> Self { - BlkDevConfig { - id: "".to_string(), - path_on_host: "".to_string(), - read_only: false, - direct: true, - serial_num: None, - iothread: None, - iops: None, - queues: 1, - boot_index: None, - chardev: None, - socket_path: None, - aio: AioEngine::Native, - queue_size: DEFAULT_VIRTQUEUE_SIZE, - discard: false, - write_zeroes: WriteZeroesState::Off, - format: DiskFormat::Raw, - l2_cache_size: None, - refcount_cache_size: None, - } - } -} - #[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum DiskFormat { #[default] @@ -327,192 +271,6 @@ impl ConfigCheck for DriveConfig { } } -impl ConfigCheck for BlkDevConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "drive device id")?; - if self.serial_num.is_some() && self.serial_num.as_ref().unwrap().len() > MAX_SERIAL_NUM { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "drive serial number".to_string(), - MAX_SERIAL_NUM, - ))); - } - - if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "iothread name".to_string(), - MAX_STRING_LENGTH, - ))); - } - - if self.queues < 1 || self.queues > MAX_VIRTIO_QUEUE as u16 { - return Err(anyhow!(ConfigError::IllegalValue( - "number queues of block device".to_string(), - 1, - true, - MAX_VIRTIO_QUEUE as u64, - true, - ))); - } - - if self.queue_size <= MIN_QUEUE_SIZE_BLK || self.queue_size > MAX_QUEUE_SIZE_BLK { - return Err(anyhow!(ConfigError::IllegalValue( - "queue size of block device".to_string(), - MIN_QUEUE_SIZE_BLK as u64, - false, - MAX_QUEUE_SIZE_BLK as u64, - true - ))); - } - - if self.queue_size & (self.queue_size - 1) != 0 { - bail!("Queue size should be power of 2!"); - } - - let fake_drive = DriveConfig { - id: self.id.clone(), - path_on_host: self.path_on_host.clone(), - direct: self.direct, - iops: self.iops, - aio: self.aio, - media: "disk".to_string(), - ..Default::default() - }; - fake_drive.check()?; - #[cfg(not(test))] - if self.chardev.is_none() { - fake_drive.check_path()?; - } - - Ok(()) - } -} - -pub fn parse_blk( - vm_config: &mut VmConfig, - drive_config: &str, - queues_auto: Option, -) -> Result { - let mut cmd_parser = CmdParser::new("virtio-blk"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("multifunction") - .push("drive") - .push("bootindex") - .push("serial") - .push("iothread") - .push("num-queues") - .push("queue-size"); - - cmd_parser.parse(drive_config)?; - - pci_args_check(&cmd_parser)?; - - let mut blkdevcfg = BlkDevConfig::default(); - if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { - blkdevcfg.boot_index = Some(boot_index); - } - - let blkdrive = cmd_parser - .get_value::("drive")? - .with_context(|| ConfigError::FieldIsMissing("drive".to_string(), "blk".to_string()))?; - - if let Some(iothread) = cmd_parser.get_value::("iothread")? { - blkdevcfg.iothread = Some(iothread); - } - - if let Some(serial) = cmd_parser.get_value::("serial")? { - blkdevcfg.serial_num = Some(serial); - } - - blkdevcfg.id = cmd_parser - .get_value::("id")? - .with_context(|| "No id configured for blk device")?; - - if let Some(queues) = cmd_parser.get_value::("num-queues")? { - blkdevcfg.queues = queues; - } else if let Some(queues) = queues_auto { - blkdevcfg.queues = queues; - } - - if let Some(queue_size) = cmd_parser.get_value::("queue-size")? { - blkdevcfg.queue_size = queue_size; - } - - let drive_arg = &vm_config - .drives - .remove(&blkdrive) - .with_context(|| "No drive configured matched for blk device")?; - blkdevcfg.path_on_host = drive_arg.path_on_host.clone(); - blkdevcfg.read_only = drive_arg.readonly; - blkdevcfg.direct = drive_arg.direct; - blkdevcfg.iops = drive_arg.iops; - blkdevcfg.aio = drive_arg.aio; - blkdevcfg.discard = drive_arg.discard; - blkdevcfg.write_zeroes = drive_arg.write_zeroes; - blkdevcfg.format = drive_arg.format; - blkdevcfg.l2_cache_size = drive_arg.l2_cache_size; - blkdevcfg.refcount_cache_size = drive_arg.refcount_cache_size; - blkdevcfg.check()?; - Ok(blkdevcfg) -} - -pub fn parse_vhost_user_blk( - vm_config: &mut VmConfig, - drive_config: &str, - queues_auto: Option, -) -> Result { - let mut cmd_parser = CmdParser::new("vhost-user-blk-pci"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("num-queues") - .push("chardev") - .push("queue-size") - .push("bootindex"); - - cmd_parser.parse(drive_config)?; - - pci_args_check(&cmd_parser)?; - - let mut blkdevcfg = BlkDevConfig::default(); - - if let Some(boot_index) = cmd_parser.get_value::("bootindex")? { - blkdevcfg.boot_index = Some(boot_index); - } - - blkdevcfg.chardev = cmd_parser - .get_value::("chardev")? - .map(Some) - .with_context(|| { - ConfigError::FieldIsMissing("chardev".to_string(), "vhost-user-blk-pci".to_string()) - })?; - - blkdevcfg.id = cmd_parser - .get_value::("id")? - .with_context(|| "No id configured for blk device")?; - - if let Some(queues) = cmd_parser.get_value::("num-queues")? { - blkdevcfg.queues = queues; - } else if let Some(queues) = queues_auto { - blkdevcfg.queues = queues; - } - - if let Some(size) = cmd_parser.get_value::("queue-size")? { - blkdevcfg.queue_size = size; - } - - if let Some(chardev) = &blkdevcfg.chardev { - blkdevcfg.socket_path = Some(get_chardev_socket_path(chardev, vm_config)?); - } - blkdevcfg.check()?; - Ok(blkdevcfg) -} - impl VmConfig { /// Add '-drive ...' drive config to `VmConfig`, including `block drive` and `pflash drive`. pub fn add_drive(&mut self, drive_config: &str) -> Result { @@ -582,77 +340,6 @@ impl VmConfig { #[cfg(test)] mod tests { use super::*; - use crate::config::get_pci_bdf; - - #[test] - fn test_drive_config_cmdline_parser() { - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_drive( - "id=rootfs,file=/path/to/rootfs,readonly=off,direct=on,throttling.iops-total=200" - ) - .is_ok()); - let blk_cfg_res = parse_blk( - &mut vm_config, - "virtio-blk-device,drive=rootfs,id=rootfs,iothread=iothread1,serial=111111,num-queues=4", - None, - ); - assert!(blk_cfg_res.is_ok()); - let blk_device_config = blk_cfg_res.unwrap(); - assert_eq!(blk_device_config.id, "rootfs"); - assert_eq!(blk_device_config.path_on_host, "/path/to/rootfs"); - assert_eq!(blk_device_config.direct, true); - assert_eq!(blk_device_config.read_only, false); - assert_eq!(blk_device_config.serial_num, Some(String::from("111111"))); - assert_eq!(blk_device_config.queues, 4); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_drive("id=rootfs,file=/path/to/rootfs,readonly=off,direct=on") - .is_ok()); - let blk_cfg_res = parse_blk( - &mut vm_config, - "virtio-blk-device,drive=rootfs1,id=rootfs1,iothread=iothread1,iops=200,serial=111111", - None, - ); - assert!(blk_cfg_res.is_err()); // Can not find drive named "rootfs1". - } - - #[test] - fn test_pci_block_config_cmdline_parser() { - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_drive("id=rootfs,file=/path/to/rootfs,readonly=off,direct=on") - .is_ok()); - let blk_cfg = "virtio-blk-pci,id=rootfs,bus=pcie.0,addr=0x1.0x2,drive=rootfs,serial=111111,num-queues=4"; - let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg, None); - assert!(blk_cfg_res.is_ok()); - let drive_configs = blk_cfg_res.unwrap(); - assert_eq!(drive_configs.id, "rootfs"); - assert_eq!(drive_configs.path_on_host, "/path/to/rootfs"); - assert_eq!(drive_configs.direct, true); - assert_eq!(drive_configs.read_only, false); - assert_eq!(drive_configs.serial_num, Some(String::from("111111"))); - assert_eq!(drive_configs.queues, 4); - - let pci_bdf = get_pci_bdf(blk_cfg); - assert!(pci_bdf.is_ok()); - let pci = pci_bdf.unwrap(); - assert_eq!(pci.bus, "pcie.0".to_string()); - assert_eq!(pci.addr, (1, 2)); - - // drive "rootfs" has been removed. - let blk_cfg_res = parse_blk(&mut vm_config, blk_cfg, None); - assert!(blk_cfg_res.is_err()); - - let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_drive("id=rootfs,file=/path/to/rootfs,readonly=off,direct=on") - .is_ok()); - let blk_cfg = - "virtio-blk-pci,id=blk1,bus=pcie.0,addr=0x1.0x2,drive=rootfs,multifunction=on"; - assert!(parse_blk(&mut vm_config, blk_cfg, None).is_ok()); - } #[test] fn test_pflash_drive_config_cmdline_parser() { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index bb69d1e08..4d1b2ab94 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -91,10 +91,10 @@ pub const FAST_UNPLUG_OFF: &str = "0"; pub const MAX_NODES: u32 = 128; /// Default virtqueue size for virtio devices excepts virtio-fs. pub const DEFAULT_VIRTQUEUE_SIZE: u16 = 256; -// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi should be larger than 2. -pub const MIN_QUEUE_SIZE_SCSI: u64 = 2; -// Max size of each virtqueue for virtio-scsi. -pub const MAX_QUEUE_SIZE_SCSI: u64 = 1024; +// Seg_max = queue_size - 2. So, size of each virtqueue for virtio-scsi/virtio-blk should be larger than 2. +pub const MIN_QUEUE_SIZE_BLOCK_DEVICE: u64 = 2; +// Max size of each virtqueue for virtio-scsi/virtio-blk. +pub const MAX_QUEUE_SIZE_BLOCK_DEVICE: u64 = 1024; /// The bar0 size of enable_bar0 features pub const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; @@ -832,6 +832,17 @@ pub fn valid_dir(d: &str) -> Result { Ok(dir) } +pub fn valid_block_device_virtqueue_size(s: &str) -> Result { + let size: u64 = s.parse()?; + valid_virtqueue_size( + size, + MIN_QUEUE_SIZE_BLOCK_DEVICE + 1, + MAX_QUEUE_SIZE_BLOCK_DEVICE, + )?; + + Ok(size as u16) +} + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index f7d79a13b..01c7570e9 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -296,7 +296,11 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result> SECTOR_SHIFT; +/// Max length of serial number. +const MAX_SERIAL_NUM_LEN: usize = 20; type SenderConfig = ( Option>>>, @@ -86,6 +92,70 @@ type SenderConfig = ( bool, ); +fn valid_serial(s: &str) -> Result { + if s.len() > MAX_SERIAL_NUM_LEN { + return Err(anyhow!(ConfigError::StringLengthTooLong( + "device serial number".to_string(), + MAX_SERIAL_NUM_LEN, + ))); + } + Ok(s.to_string()) +} + +#[derive(Parser, Debug, Clone)] +#[command(no_binary_name(true))] +pub struct VirtioBlkDevConfig { + #[arg(long, value_parser = ["virtio-blk-pci", "virtio-blk-device"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, value_parser = parse_bool)] + pub multifunction: Option, + #[arg(long)] + pub drive: String, + #[arg(long)] + pub bootindex: Option, + #[arg(long, alias = "num-queues", value_parser = clap::value_parser!(u16).range(1..=MAX_VIRTIO_QUEUE as i64))] + pub num_queues: Option, + #[arg(long)] + pub iothread: Option, + #[arg(long, alias = "queue-size", default_value = "256", value_parser = valid_block_device_virtqueue_size)] + pub queue_size: u16, + #[arg(long, value_parser = valid_serial)] + pub serial: Option, +} + +impl Default for VirtioBlkDevConfig { + fn default() -> Self { + Self { + classtype: "".to_string(), + id: "".to_string(), + bus: None, + addr: None, + multifunction: None, + drive: "".to_string(), + num_queues: Some(1), + bootindex: None, + iothread: None, + queue_size: DEFAULT_VIRTQUEUE_SIZE, + serial: None, + } + } +} + +impl ConfigCheck for VirtioBlkDevConfig { + fn check(&self) -> Result<()> { + if self.serial.is_some() { + valid_serial(&self.serial.clone().unwrap())?; + } + Ok(()) + } +} + fn get_serial_num_config(serial_num: &str) -> Vec { let mut id_bytes = vec![0; VIRTIO_BLK_ID_BYTES as usize]; let bytes_to_copy = cmp::min(serial_num.len(), VIRTIO_BLK_ID_BYTES as usize); @@ -955,7 +1025,9 @@ pub struct Block { /// Virtio device base property. base: VirtioBase, /// Configuration of the block device. - blk_cfg: BlkDevConfig, + blk_cfg: VirtioBlkDevConfig, + /// Configuration of the block device's drive. + drive_cfg: DriveConfig, /// Config space of the block device. config_space: VirtioBlkConfig, /// BLock backend opened by the block device. @@ -978,14 +1050,16 @@ pub struct Block { impl Block { pub fn new( - blk_cfg: BlkDevConfig, + blk_cfg: VirtioBlkDevConfig, + drive_cfg: DriveConfig, drive_files: Arc>>, ) -> Block { - let queue_num = blk_cfg.queues as usize; + let queue_num = blk_cfg.num_queues.unwrap_or(1) as usize; let queue_size = blk_cfg.queue_size; Self { base: VirtioBase::new(VIRTIO_TYPE_BLOCK, queue_num, queue_size), blk_cfg, + drive_cfg, req_align: 1, buf_align: 1, drive_files, @@ -999,11 +1073,11 @@ impl Block { // seg_max = queue_size - 2: 32bits self.config_space.seg_max = self.queue_size_max() as u32 - 2; - if self.blk_cfg.queues > 1 { - self.config_space.num_queues = self.blk_cfg.queues; + if self.blk_cfg.num_queues.unwrap_or(1) > 1 { + self.config_space.num_queues = self.blk_cfg.num_queues.unwrap_or(1); } - if self.blk_cfg.discard { + if self.drive_cfg.discard { // Just support one segment per request. self.config_space.max_discard_seg = 1; // The default discard alignment is 1 sector. @@ -1011,7 +1085,7 @@ impl Block { self.config_space.max_discard_sectors = MAX_REQUEST_SECTORS; } - if self.blk_cfg.write_zeroes != WriteZeroesState::Off { + if self.drive_cfg.write_zeroes != WriteZeroesState::Off { // Just support one segment per request. self.config_space.max_write_zeroes_seg = 1; self.config_space.max_write_zeroes_sectors = MAX_REQUEST_SECTORS; @@ -1058,35 +1132,36 @@ impl VirtioDevice for Block { ); } - if !self.blk_cfg.path_on_host.is_empty() { + if !self.drive_cfg.path_on_host.is_empty() { let drive_files = self.drive_files.lock().unwrap(); - let file = VmConfig::fetch_drive_file(&drive_files, &self.blk_cfg.path_on_host)?; - let alignments = VmConfig::fetch_drive_align(&drive_files, &self.blk_cfg.path_on_host)?; + let file = VmConfig::fetch_drive_file(&drive_files, &self.drive_cfg.path_on_host)?; + let alignments = + VmConfig::fetch_drive_align(&drive_files, &self.drive_cfg.path_on_host)?; self.req_align = alignments.0; self.buf_align = alignments.1; - let drive_id = VmConfig::get_drive_id(&drive_files, &self.blk_cfg.path_on_host)?; + let drive_id = VmConfig::get_drive_id(&drive_files, &self.drive_cfg.path_on_host)?; let mut thread_pool = None; - if self.blk_cfg.aio != AioEngine::Off { + if self.drive_cfg.aio != AioEngine::Off { thread_pool = Some(EventLoop::get_ctx(None).unwrap().thread_pool.clone()); } let aio = Aio::new( Arc::new(BlockIoHandler::complete_func), - self.blk_cfg.aio, + self.drive_cfg.aio, thread_pool, )?; let conf = BlockProperty { id: drive_id, - format: self.blk_cfg.format, + format: self.drive_cfg.format, iothread: self.blk_cfg.iothread.clone(), - direct: self.blk_cfg.direct, + direct: self.drive_cfg.direct, req_align: self.req_align, buf_align: self.buf_align, - discard: self.blk_cfg.discard, - write_zeroes: self.blk_cfg.write_zeroes, - l2_cache_size: self.blk_cfg.l2_cache_size, - refcount_cache_size: self.blk_cfg.refcount_cache_size, + discard: self.drive_cfg.discard, + write_zeroes: self.drive_cfg.write_zeroes, + l2_cache_size: self.drive_cfg.l2_cache_size, + refcount_cache_size: self.drive_cfg.refcount_cache_size, }; let backend = create_block_backend(file, aio, conf)?; let disk_size = backend.lock().unwrap().disk_size()?; @@ -1110,16 +1185,16 @@ impl VirtioDevice for Block { | 1_u64 << VIRTIO_F_RING_EVENT_IDX | 1_u64 << VIRTIO_BLK_F_FLUSH | 1_u64 << VIRTIO_BLK_F_SEG_MAX; - if self.blk_cfg.read_only { + if self.drive_cfg.readonly { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_RO; }; - if self.blk_cfg.queues > 1 { + if self.blk_cfg.num_queues.unwrap_or(1) > 1 { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; } - if self.blk_cfg.discard { + if self.drive_cfg.discard { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_DISCARD; } - if self.blk_cfg.write_zeroes != WriteZeroesState::Off { + if self.drive_cfg.write_zeroes != WriteZeroesState::Off { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES; } self.build_device_config_space(); @@ -1130,7 +1205,7 @@ impl VirtioDevice for Block { fn unrealize(&mut self) -> Result<()> { MigrationManager::unregister_device_instance(BlockState::descriptor(), &self.blk_cfg.id); let drive_files = self.drive_files.lock().unwrap(); - let drive_id = VmConfig::get_drive_id(&drive_files, &self.blk_cfg.path_on_host)?; + let drive_id = VmConfig::get_drive_id(&drive_files, &self.drive_cfg.path_on_host)?; remove_block_backend(&drive_id); Ok(()) } @@ -1176,20 +1251,20 @@ impl VirtioDevice for Block { req_align: self.req_align, buf_align: self.buf_align, disk_sectors: self.disk_sectors, - direct: self.blk_cfg.direct, - serial_num: self.blk_cfg.serial_num.clone(), + direct: self.drive_cfg.direct, + serial_num: self.blk_cfg.serial.clone(), driver_features, receiver, update_evt: update_evt.clone(), device_broken: self.base.broken.clone(), interrupt_cb: interrupt_cb.clone(), iothread: self.blk_cfg.iothread.clone(), - leak_bucket: match self.blk_cfg.iops { + leak_bucket: match self.drive_cfg.iops { Some(iops) => Some(LeakBucket::new(iops)?), None => None, }, - discard: self.blk_cfg.discard, - write_zeroes: self.blk_cfg.write_zeroes, + discard: self.drive_cfg.discard, + write_zeroes: self.drive_cfg.write_zeroes, }; let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); @@ -1236,18 +1311,28 @@ impl VirtioDevice for Block { Ok(()) } - fn update_config(&mut self, dev_config: Option>) -> Result<()> { - let is_plug = dev_config.is_some(); - if let Some(conf) = dev_config { - self.blk_cfg = conf + // configs[0]: DriveConfig. configs[1]: VirtioBlkDevConfig. + fn update_config(&mut self, configs: Vec>) -> Result<()> { + let mut is_plug = false; + if configs.len() == 2 { + self.drive_cfg = configs[0] + .as_any() + .downcast_ref::() + .unwrap() + .clone(); + self.blk_cfg = configs[1] .as_any() - .downcast_ref::() + .downcast_ref::() .unwrap() .clone(); // microvm type block device don't support multiple queue. - self.blk_cfg.queues = QUEUE_NUM_BLK as u16; - } else { + self.blk_cfg.num_queues = Some(QUEUE_NUM_BLK as u16); + is_plug = true; + } else if configs.is_empty() { self.blk_cfg = Default::default(); + self.drive_cfg = Default::default(); + } else { + bail!("Invalid update configs."); } if !is_plug { @@ -1296,8 +1381,8 @@ impl VirtioDevice for Block { self.req_align, self.buf_align, self.disk_sectors, - self.blk_cfg.serial_num.clone(), - self.blk_cfg.direct, + self.blk_cfg.serial.clone(), + self.drive_cfg.direct, )) .with_context(|| VirtioError::ChannelSend("image fd".to_string()))?; } @@ -1354,7 +1439,9 @@ mod tests { use super::*; use crate::*; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - use machine_manager::config::{IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; + use machine_manager::config::{ + str_slip_to_clap, IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, + }; const QUEUE_NUM_BLK: usize = 1; const CONFIG_SPACE_SIZE: usize = 60; @@ -1390,11 +1477,36 @@ mod tests { fn init_default_block() -> Block { Block::new( - BlkDevConfig::default(), + VirtioBlkDevConfig::default(), + DriveConfig::default(), Arc::new(Mutex::new(HashMap::new())), ) } + #[test] + fn test_virtio_block_config_cmdline_parser() { + // Test1: Right. + let blk_cmd1 = "virtio-blk-pci,id=rootfs,bus=pcie.0,addr=0x1.0x2,drive=rootfs,serial=111111,num-queues=4"; + let blk_config = + VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(blk_cmd1, true, false)).unwrap(); + assert_eq!(blk_config.id, "rootfs"); + assert_eq!(blk_config.bus.unwrap(), "pcie.0"); + assert_eq!(blk_config.addr.unwrap(), (1, 2)); + assert_eq!(blk_config.serial.unwrap(), "111111"); + assert_eq!(blk_config.num_queues.unwrap(), 4); + + // Test2: Default values. + assert_eq!(blk_config.queue_size, DEFAULT_VIRTQUEUE_SIZE); + + // Test3: Illegal values. + let blk_cmd3 = "virtio-blk-pci,id=rootfs,bus=pcie.0,addr=0x1.0x2,drive=rootfs,serial=111111,num-queues=33"; + let result = VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(blk_cmd3, true, false)); + assert!(result.is_err()); + let blk_cmd3 = "virtio-blk-pci,id=rootfs,drive=rootfs,serial=111111111111111111111111111111111111111111111111111111111111111111111"; + let result = VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(blk_cmd3, true, false)); + assert!(result.is_err()); + } + // Use different input parameters to verify block `new()` and `realize()` functionality. #[test] fn test_block_init() { @@ -1410,16 +1522,16 @@ mod tests { assert!(block.senders.is_empty()); // Realize block device: create TempFile as backing file. - block.blk_cfg.read_only = true; - block.blk_cfg.direct = false; + block.drive_cfg.readonly = true; + block.drive_cfg.direct = false; let f = TempFile::new().unwrap(); - block.blk_cfg.path_on_host = f.as_path().to_str().unwrap().to_string(); + block.drive_cfg.path_on_host = f.as_path().to_str().unwrap().to_string(); VmConfig::add_drive_file( &mut block.drive_files.lock().unwrap(), "", - &block.blk_cfg.path_on_host, - block.blk_cfg.read_only, - block.blk_cfg.direct, + &block.drive_cfg.path_on_host, + block.drive_cfg.readonly, + block.drive_cfg.direct, ) .unwrap(); assert!(block.realize().is_ok()); @@ -1548,19 +1660,19 @@ mod tests { let mut block = init_default_block(); let file = TempFile::new().unwrap(); - block.blk_cfg.path_on_host = file.as_path().to_str().unwrap().to_string(); - block.blk_cfg.direct = false; + block.drive_cfg.path_on_host = file.as_path().to_str().unwrap().to_string(); + block.drive_cfg.direct = false; // config iothread and iops block.blk_cfg.iothread = Some(thread_name); - block.blk_cfg.iops = Some(100); + block.drive_cfg.iops = Some(100); VmConfig::add_drive_file( &mut block.drive_files.lock().unwrap(), "", - &block.blk_cfg.path_on_host, - block.blk_cfg.read_only, - block.blk_cfg.direct, + &block.drive_cfg.path_on_host, + block.drive_cfg.readonly, + block.drive_cfg.direct, ) .unwrap(); diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 4e605faa8..48b19cc4a 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1596,9 +1596,9 @@ impl VirtioDevice for Net { Ok(()) } - fn update_config(&mut self, dev_config: Option>) -> Result<()> { - if let Some(conf) = dev_config { - self.net_cfg = conf + fn update_config(&mut self, dev_config: Vec>) -> Result<()> { + if !dev_config.is_empty() { + self.net_cfg = dev_config[0] .as_any() .downcast_ref::() .unwrap() diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index dc2d8f514..8f301df15 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -34,8 +34,7 @@ use devices::ScsiBus::{ EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, }; use machine_manager::config::{ - get_pci_df, parse_bool, valid_id, valid_virtqueue_size, MAX_QUEUE_SIZE_SCSI, MAX_VIRTIO_QUEUE, - MIN_QUEUE_SIZE_SCSI, + get_pci_df, parse_bool, valid_block_device_virtqueue_size, valid_id, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use util::aio::Iovec; @@ -113,17 +112,10 @@ pub struct ScsiCntlrConfig { pub num_queues: Option, #[arg(long)] pub iothread: Option, - #[arg(long, alias = "queue-size", default_value = "256", value_parser = valid_scsi_cntlr_queue_size)] + #[arg(long, alias = "queue-size", default_value = "256", value_parser = valid_block_device_virtqueue_size)] pub queue_size: u16, } -fn valid_scsi_cntlr_queue_size(s: &str) -> Result { - let size: u64 = s.parse()?; - valid_virtqueue_size(size, MIN_QUEUE_SIZE_SCSI + 1, MAX_QUEUE_SIZE_SCSI)?; - - Ok(size as u16) -} - #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] struct VirtioScsiConfig { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 750d6e1b8..7f0fac219 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -33,7 +33,7 @@ mod queue; mod transport; pub use device::balloon::*; -pub use device::block::{Block, BlockState, VirtioBlkConfig}; +pub use device::block::{Block, BlockState, VirtioBlkConfig, VirtioBlkDevConfig}; #[cfg(feature = "virtio_gpu")] pub use device::gpu::*; pub use device::net::*; @@ -729,8 +729,9 @@ pub trait VirtioDevice: Send + AsAny { /// /// # Arguments /// - /// * `_file_path` - The related backend file path. - fn update_config(&mut self, _dev_config: Option>) -> Result<()> { + /// * `_configs` - The related configs for device. + /// eg: DriveConfig and VirtioBlkDevConfig for virtio blk device. + fn update_config(&mut self, _configs: Vec>) -> Result<()> { bail!("Unsupported to update configuration") } diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 25d030b77..8c55cac12 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; +use clap::Parser; use vmm_sys_util::eventfd::EventFd; use super::client::VhostUserClient; @@ -30,14 +31,41 @@ use crate::{ VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; use address_space::AddressSpace; -use machine_manager::{config::BlkDevConfig, event_loop::unregister_event_helper}; +use machine_manager::config::{ + get_chardev_socket_path, get_pci_df, valid_block_device_virtqueue_size, valid_id, + ChardevConfig, MAX_VIRTIO_QUEUE, +}; +use machine_manager::event_loop::unregister_event_helper; use util::byte_code::ByteCode; +#[derive(Parser, Debug, Clone, Default)] +#[command(no_binary_name(true))] +pub struct VhostUserBlkDevConfig { + #[arg(long, value_parser = ["vhost-user-blk-device", "vhost-user-blk-pci"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, alias = "num-queues", value_parser = clap::value_parser!(u16).range(1..=MAX_VIRTIO_QUEUE as i64))] + pub num_queues: Option, + #[arg(long)] + pub chardev: String, + #[arg(long, alias = "queue-size", default_value = "256", value_parser = valid_block_device_virtqueue_size)] + pub queue_size: u16, + #[arg(long)] + pub bootindex: Option, +} + pub struct Block { /// Virtio device base property. base: VirtioBase, /// Configuration of the block device. - blk_cfg: BlkDevConfig, + blk_cfg: VhostUserBlkDevConfig, + /// Configuration of the vhost user blk's socket chardev. + chardev_cfg: ChardevConfig, /// Config space of the block device. config_space: VirtioBlkConfig, /// System address space. @@ -51,13 +79,18 @@ pub struct Block { } impl Block { - pub fn new(cfg: &BlkDevConfig, mem_space: &Arc) -> Self { - let queue_num = cfg.queues as usize; + pub fn new( + cfg: &VhostUserBlkDevConfig, + chardev_cfg: ChardevConfig, + mem_space: &Arc, + ) -> Self { + let queue_num = cfg.num_queues.unwrap_or(1) as usize; let queue_size = cfg.queue_size; Block { base: VirtioBase::new(VIRTIO_TYPE_BLOCK, queue_num, queue_size), blk_cfg: cfg.clone(), + chardev_cfg, config_space: Default::default(), mem_space: mem_space.clone(), client: None, @@ -68,12 +101,7 @@ impl Block { /// Connect with spdk and register update event. fn init_client(&mut self) -> Result<()> { - let socket_path = self - .blk_cfg - .socket_path - .as_ref() - .map(|path| path.to_string()) - .with_context(|| "vhost-user: socket path is not found")?; + let socket_path = get_chardev_socket_path(self.chardev_cfg.clone())?; let client = VhostUserClient::new( &self.mem_space, &socket_path, @@ -146,10 +174,10 @@ impl VirtioDevice for Block { ); } - if self.blk_cfg.queues > 1 { - self.config_space.num_queues = self.blk_cfg.queues; + if self.blk_cfg.num_queues.unwrap_or(1) > 1 { + self.config_space.num_queues = self.blk_cfg.num_queues.unwrap_or(1); } - } else if self.blk_cfg.queues > 1 { + } else if self.blk_cfg.num_queues.unwrap_or(1) > 1 { bail!( "spdk doesn't support multi queue, spdk protocol features: {:#b}", protocol_features @@ -169,7 +197,7 @@ impl VirtioDevice for Block { | 1_u64 << VIRTIO_BLK_F_WRITE_ZEROES | 1_u64 << VIRTIO_BLK_F_SEG_MAX | 1_u64 << VIRTIO_BLK_F_RO; - if self.blk_cfg.queues > 1 { + if self.blk_cfg.num_queues.unwrap_or(1) > 1 { self.base.device_features |= 1_u64 << VIRTIO_BLK_F_MQ; } self.base.device_features &= features; @@ -217,13 +245,7 @@ impl VirtioDevice for Block { if !self.enable_irqfd { let queue_num = self.base.queues.len(); - listen_guest_notifier( - &mut self.base, - &mut client, - self.blk_cfg.iothread.as_ref(), - queue_num, - interrupt_cb, - )?; + listen_guest_notifier(&mut self.base, &mut client, None, queue_num, interrupt_cb)?; } client.activate_vhost_user()?; @@ -235,10 +257,7 @@ impl VirtioDevice for Block { if let Some(client) = &self.client { client.lock().unwrap().reset_vhost_user(false); } - unregister_event_helper( - self.blk_cfg.iothread.as_ref(), - &mut self.base.deactivate_evts, - )?; + unregister_event_helper(None, &mut self.base.deactivate_evts)?; Ok(()) } diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 2f0dc96f0..8a6e8d42d 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -18,7 +18,7 @@ mod message; mod net; mod sock; -pub use self::block::Block; +pub use self::block::{Block, VhostUserBlkDevConfig}; pub use self::client::*; pub use self::fs::*; pub use self::message::*; -- Gitee From 0e8b388f733f76e9b249478cd316d1fbfa2b3aa4 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Sun, 28 Apr 2024 15:04:36 +0800 Subject: [PATCH 1782/2187] Camera: Add filter for camera format We only transfer some essential formats to guest. Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index f27abb1b2..a520d639d 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -29,6 +29,9 @@ static OHCAM_CALLBACK: Lazy = Lazy::new(|| RwLock::new(OhCamCallBack::d // In UVC, interval's unit is 100ns. // So, fps * interval / 10_000_000 == 1. const FPS_INTERVAL_TRANS: u32 = 10_000_000; +const RESOLUTION_WHITELIST: [(i32, i32); 2] = [(640, 480), (1280, 720)]; +const FRAME_FORMAT_WHITELIST: [i32; 1] = [CAMERA_FORMAT_YUV420SP]; +const FPS_WHITELIST: [i32; 1] = [30]; #[derive(Default)] struct OhCamCallBack { @@ -166,14 +169,13 @@ impl CameraBackend for OhCameraBackend { for idx in 0..self.profile_cnt { match self.ctx.get_profile(self.camidx as i32, idx as i32) { - Ok((fmt, width, height, mut fps)) => { - if (fmt != CAMERA_FORMAT_YUV420SP) && (fmt != CAMERA_FORMAT_MJPEG) { + Ok((fmt, width, height, fps)) => { + if !FRAME_FORMAT_WHITELIST.iter().any(|&x| x == fmt) + || !RESOLUTION_WHITELIST.iter().any(|&x| x == (width, height)) + || !FPS_WHITELIST.iter().any(|&x| x == fps) + { continue; } - // NOTE: windows camera APP doesn't support fps lower than 30, and some OH PC only support 15 fps. - if fps < 30 { - fps = 30; - } let frame = CameraFrame { width: width as u32, -- Gitee From 256349e515b9e9d707a68d93013e36da0f593405 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 13 Apr 2024 21:30:37 +0800 Subject: [PATCH 1783/2187] netdev: use clap to parse the parameters of the netdev config Use clap to parse the parameters of the netdev config. Signed-off-by: liuxiangdong --- machine_manager/src/config/network.rs | 312 +++++++++----------------- 1 file changed, 106 insertions(+), 206 deletions(-) diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 01c7570e9..d1421309b 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -13,9 +13,11 @@ use std::os::unix::io::RawFd; use anyhow::{anyhow, bail, Context, Result}; +use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; use super::{error::ConfigError, pci_args_check}; +use super::{parse_bool, str_slip_to_clap, valid_id}; use crate::config::get_chardev_socket_path; use crate::config::{ check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, @@ -30,23 +32,80 @@ pub const MAX_QUEUE_SIZE_NET: u16 = 4096; /// Max num of virtqueues. const MAX_QUEUE_PAIRS: usize = MAX_VIRTIO_QUEUE / 2; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct NetDevcfg { + #[arg(long, alias="classtype", value_parser = ["tap", "vhost-user"])] + pub netdev_type: String, + #[arg(long, value_parser = valid_id)] pub id: String, + #[arg(long, aliases = ["fds", "fd"], use_value_delimiter = true, value_delimiter = ':')] pub tap_fds: Option>, - pub vhost_type: Option, + #[arg(long, alias = "vhost", default_value = "off", value_parser = parse_bool, action = ArgAction::Append)] + pub vhost_kernel: bool, + #[arg(long, aliases = ["vhostfds", "vhostfd"], use_value_delimiter = true, value_delimiter = ':')] pub vhost_fds: Option>, + #[arg(long, default_value = "", value_parser = valid_id)] pub ifname: String, + #[arg(long, default_value = "1", value_parser = parse_queues)] pub queues: u16, + #[arg(long)] pub chardev: Option, } +impl NetDevcfg { + pub fn vhost_type(&self) -> Option { + if self.vhost_kernel { + return Some("vhost-kernel".to_string()); + } + if self.netdev_type == "vhost-user" { + return Some("vhost-user".to_string()); + } + // Default: virtio net. + None + } + + fn auto_queues(&mut self) -> Result<()> { + if let Some(fds) = &self.tap_fds { + let fds_num = fds + .len() + .checked_mul(2) + .with_context(|| format!("Invalid fds number {}", fds.len()))? + as u16; + if fds_num > self.queues { + self.queues = fds_num; + } + } + if let Some(fds) = &self.vhost_fds { + let fds_num = fds + .len() + .checked_mul(2) + .with_context(|| format!("Invalid vhostfds number {}", fds.len()))? + as u16; + if fds_num > self.queues { + self.queues = fds_num; + } + } + Ok(()) + } +} + +fn parse_queues(q: &str) -> Result { + let queues = q + .parse::()? + .checked_mul(2) + .with_context(|| "Invalid 'queues' value")?; + is_netdev_queues_valid(queues)?; + Ok(queues) +} + impl Default for NetDevcfg { fn default() -> Self { NetDevcfg { + netdev_type: "".to_string(), id: "".to_string(), tap_fds: None, - vhost_type: None, + vhost_kernel: false, vhost_fds: None, ifname: "".to_string(), queues: 2, @@ -57,24 +116,18 @@ impl Default for NetDevcfg { impl ConfigCheck for NetDevcfg { fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "id")?; - check_arg_too_long(&self.ifname, "ifname")?; - - if let Some(vhost_type) = self.vhost_type.as_ref() { - if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" { - return Err(anyhow!(ConfigError::UnknownVhostType)); - } + if self.vhost_kernel && self.netdev_type == "vhost-user" { + bail!("vhost-user netdev does not support 'vhost' option"); } - if !is_netdev_queues_valid(self.queues) { - return Err(anyhow!(ConfigError::IllegalValue( - "number queues of net device".to_string(), - 1, - true, - MAX_VIRTIO_QUEUE as u64 / 2, - true, - ))); + if self.vhost_fds.is_some() && self.vhost_type().is_none() { + bail!("Argument 'vhostfd' or 'vhostfds' are not needed for virtio-net device"); } + if self.tap_fds.is_none() && self.ifname.eq("") && self.netdev_type.ne("vhost-user") { + bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device"); + } + + is_netdev_queues_valid(self.queues)?; Ok(()) } @@ -156,102 +209,6 @@ impl ConfigCheck for NetworkInterfaceConfig { } } -fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result>> { - if let Some(fds) = cmd_parser.get_value::(name)? { - let mut raw_fds = Vec::new(); - for fd in fds.split(':').collect::>().iter() { - raw_fds.push( - (*fd) - .parse::() - .with_context(|| "Failed to parse fds")?, - ); - } - Ok(Some(raw_fds)) - } else { - Ok(None) - } -} - -fn parse_netdev(cmd_parser: CmdParser) -> Result { - let mut net = NetDevcfg::default(); - let netdev_type = cmd_parser.get_value::("")?.unwrap_or_default(); - if netdev_type.ne("tap") && netdev_type.ne("vhost-user") { - bail!("Unsupported netdev type: {:?}", &netdev_type); - } - net.id = cmd_parser - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "netdev".to_string()))?; - if let Some(ifname) = cmd_parser.get_value::("ifname")? { - net.ifname = ifname; - } - if let Some(queue_pairs) = cmd_parser.get_value::("queues")? { - let queues = queue_pairs.checked_mul(2); - if queues.is_none() || !is_netdev_queues_valid(queues.unwrap()) { - return Err(anyhow!(ConfigError::IllegalValue( - "number queues of net device".to_string(), - 1, - true, - MAX_VIRTIO_QUEUE as u64 / 2, - true, - ))); - } - - net.queues = queues.unwrap(); - } - - if let Some(tap_fd) = parse_fds(&cmd_parser, "fd")? { - net.tap_fds = Some(tap_fd); - } else if let Some(tap_fds) = parse_fds(&cmd_parser, "fds")? { - net.tap_fds = Some(tap_fds); - } - if let Some(fds) = &net.tap_fds { - let fds_num = - fds.len() - .checked_mul(2) - .with_context(|| format!("Invalid fds number {}", fds.len()))? as u16; - if fds_num > net.queues { - net.queues = fds_num; - } - } - - if let Some(vhost) = cmd_parser.get_value::("vhost")? { - if vhost.into() { - net.vhost_type = Some(String::from("vhost-kernel")); - } - } else if netdev_type.eq("vhost-user") { - net.vhost_type = Some(String::from("vhost-user")); - } - if let Some(chardev) = cmd_parser.get_value::("chardev")? { - net.chardev = Some(chardev); - } - if let Some(vhost_fd) = parse_fds(&cmd_parser, "vhostfd")? { - net.vhost_fds = Some(vhost_fd); - } else if let Some(vhost_fds) = parse_fds(&cmd_parser, "vhostfds")? { - net.vhost_fds = Some(vhost_fds); - } - if let Some(fds) = &net.vhost_fds { - let fds_num = fds - .len() - .checked_mul(2) - .with_context(|| format!("Invalid vhostfds number {}", fds.len()))? - as u16; - if fds_num > net.queues { - net.queues = fds_num; - } - } - - if net.vhost_fds.is_some() && net.vhost_type.is_none() { - bail!("Argument \'vhostfd\' is not needed for virtio-net device"); - } - if net.tap_fds.is_none() && net.ifname.eq("") && netdev_type.ne("vhost-user") { - bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device"); - } - - net.check()?; - - Ok(net) -} - pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result { let mut cmd_parser = CmdParser::new("virtio-net"); cmd_parser @@ -293,7 +250,7 @@ pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result) -> Result) -> Result Result<()> { - let mut cmd_parser = CmdParser::new("netdev"); - cmd_parser - .push("") - .push("id") - .push("fd") - .push("fds") - .push("vhost") - .push("ifname") - .push("vhostfd") - .push("vhostfds") - .push("queues") - .push("chardev"); - - cmd_parser.parse(netdev_config)?; - let drive_cfg = parse_netdev(cmd_parser)?; - self.add_netdev_with_config(drive_cfg) + let mut netdev_cfg = + NetDevcfg::try_parse_from(str_slip_to_clap(netdev_config, true, false))?; + netdev_cfg.auto_queues()?; + netdev_cfg.check()?; + self.add_netdev_with_config(netdev_cfg) } pub fn add_netdev_with_config(&mut self, conf: NetDevcfg) -> Result<()> { let netdev_id = conf.id.clone(); - if self.netdevs.get(&netdev_id).is_none() { - self.netdevs.insert(netdev_id, conf); - } else { + if self.netdevs.get(&netdev_id).is_some() { bail!("Netdev {:?} has been added", netdev_id); } + self.netdevs.insert(netdev_id, conf); Ok(()) } pub fn del_netdev_by_id(&mut self, id: &str) -> Result<()> { - if self.netdevs.get(id).is_some() { - self.netdevs.remove(id); - } else { - bail!("Netdev {} not found", id); - } + self.netdevs + .remove(id) + .with_context(|| format!("Netdev {} not found", id))?; + Ok(()) } } @@ -489,14 +410,24 @@ fn check_mac_address(mac: &str) -> bool { true } -fn is_netdev_queues_valid(queues: u16) -> bool { - queues >= 1 && queues <= MAX_VIRTIO_QUEUE as u16 +fn is_netdev_queues_valid(queues: u16) -> Result<()> { + if !(queues >= 2 && queues <= MAX_VIRTIO_QUEUE as u16) { + return Err(anyhow!(ConfigError::IllegalValue( + "number queues of net device".to_string(), + 1, + true, + MAX_QUEUE_PAIRS as u64, + true, + ))); + } + + Ok(()) } #[cfg(test)] mod tests { use super::*; - use crate::config::{get_pci_bdf, MAX_STRING_LENGTH}; + use crate::config::get_pci_bdf; #[test] fn test_network_config_cmdline_parser() { @@ -661,37 +592,6 @@ mod tests { assert!(net_cfg_res.is_err()); } - #[test] - fn test_netdev_config_check() { - let mut netdev_conf = NetDevcfg::default(); - for _ in 0..MAX_STRING_LENGTH { - netdev_conf.id += "A"; - } - assert!(netdev_conf.check().is_ok()); - - // Overflow - netdev_conf.id += "A"; - assert!(netdev_conf.check().is_err()); - - let mut netdev_conf = NetDevcfg::default(); - for _ in 0..MAX_STRING_LENGTH { - netdev_conf.ifname += "A"; - } - assert!(netdev_conf.check().is_ok()); - - // Overflow - netdev_conf.ifname += "A"; - assert!(netdev_conf.check().is_err()); - - let mut netdev_conf = NetDevcfg::default(); - netdev_conf.vhost_type = None; - assert!(netdev_conf.check().is_ok()); - netdev_conf.vhost_type = Some(String::from("vhost-kernel")); - assert!(netdev_conf.check().is_ok()); - netdev_conf.vhost_type = Some(String::from("vhost-")); - assert!(netdev_conf.check().is_err()); - } - #[test] fn test_add_netdev_with_different_queues() { let mut vm_config = VmConfig::default(); @@ -815,9 +715,9 @@ mod tests { ..qmp_schema::NetDevAddArgument::default() }); let net_cfg = get_netdev_config(netdev).unwrap(); + assert_eq!(net_cfg.vhost_type().unwrap(), "vhost-kernel"); assert_eq!(net_cfg.tap_fds.unwrap()[0], 11); assert_eq!(net_cfg.vhost_fds.unwrap()[0], 21); - assert_eq!(net_cfg.vhost_type.unwrap(), "vhost-kernel"); } // Normal test with 'vhostfds'. @@ -835,9 +735,9 @@ mod tests { ..qmp_schema::NetDevAddArgument::default() }); let net_cfg = get_netdev_config(netdev).unwrap(); - assert_eq!(net_cfg.tap_fds.unwrap(), [11, 12, 13, 14]); - assert_eq!(net_cfg.vhost_fds.unwrap(), [21, 22, 23, 24]); - assert_eq!(net_cfg.vhost_type.unwrap(), "vhost-kernel"); + assert_eq!(net_cfg.vhost_type().unwrap(), "vhost-kernel"); + assert_eq!(net_cfg.tap_fds.unwrap(), vec![11, 12, 13, 14]); + assert_eq!(net_cfg.vhost_fds.unwrap(), vec![21, 22, 23, 24]); } let err_msgs = [ @@ -863,8 +763,7 @@ mod tests { ..qmp_schema::NetDevAddArgument::default() }); let err_msg = format!( - "The 'queues' {} is bigger than max queue num {}", - MAX_QUEUE_PAIRS + 1, + "number queues of net device must >= 1 and <= {}.", MAX_QUEUE_PAIRS ); check_err_msg(netdev, &err_msg); @@ -933,6 +832,7 @@ mod tests { fds: Some(fds.to_string()), ..qmp_schema::NetDevAddArgument::default() }); + // number queues of net device let err_msg = format!( "The num of fd {} is bigger than max queue num {}", MAX_QUEUE_PAIRS + 1, -- Gitee From b22d4283c9189996b8384e9f90e947dab7c58053 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 14 Apr 2024 10:08:00 +0800 Subject: [PATCH 1784/2187] network: use clap to parse the parameters of the virtio-net/vhost-net/vhost-user-net config Use clap to parse the parameters of the virtio-net/vhost-net/vhost-user-net config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 50 ++-- machine/src/micro_common/mod.rs | 78 +++--- machine_manager/src/config/network.rs | 363 +++++++++----------------- virtio/src/device/net.rs | 37 ++- virtio/src/vhost/kernel/net.rs | 86 +++--- virtio/src/vhost/user/net.rs | 32 ++- 6 files changed, 296 insertions(+), 350 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d21fed9f6..eaafa909e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -73,11 +73,12 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ - complete_numa_node, get_multi_function, get_pci_bdf, parse_device_id, parse_device_type, - parse_net, parse_numa_distance, parse_numa_mem, parse_virtio_serial, parse_virtserialport, - parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, DriveFile, Incoming, - MachineMemConfig, MigrateMode, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, - SerialConfig, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + complete_numa_node, get_chardev_socket_path, get_multi_function, get_pci_bdf, parse_device_id, + parse_device_type, parse_numa_distance, parse_numa_mem, parse_virtio_serial, + parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, + DriveFile, Incoming, MachineMemConfig, MigrateMode, NetworkInterfaceConfig, NumaConfig, + NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VmConfig, FAST_UNPLUG_ON, + MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -1204,35 +1205,52 @@ pub trait MachineOps { cfg_args: &str, hotplug: bool, ) -> Result<()> { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; - let device_cfg = parse_net(vm_config, cfg_args)?; + let net_cfg = + NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let netdev_cfg = vm_config + .netdevs + .remove(&net_cfg.netdev) + .with_context(|| format!("Netdev: {:?} not found for net device", &net_cfg.netdev))?; + let bdf = PciBdf::new(net_cfg.bus.clone().unwrap(), net_cfg.addr.unwrap()); + let multi_func = net_cfg.multifunction.unwrap_or_default(); + let mut need_irqfd = false; - let device: Arc> = if device_cfg.vhost_type.is_some() { + let device: Arc> = if netdev_cfg.vhost_type().is_some() { need_irqfd = true; - if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { + if netdev_cfg.vhost_type().unwrap() == "vhost-kernel" { Arc::new(Mutex::new(VhostKern::Net::new( - &device_cfg, + &net_cfg, + netdev_cfg, self.get_sys_mem(), ))) } else { + let chardev = netdev_cfg.chardev.clone().with_context(|| { + format!("Chardev not configured for netdev {:?}", netdev_cfg.id) + })?; + let chardev_cfg = vm_config + .chardev + .remove(&chardev) + .with_context(|| format!("Chardev: {:?} not found for netdev", chardev))?; + let sock_path = get_chardev_socket_path(chardev_cfg)?; Arc::new(Mutex::new(VhostUser::Net::new( - &device_cfg, + &net_cfg, + netdev_cfg, + sock_path, self.get_sys_mem(), ))) } } else { - let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone()))); + let device = Arc::new(Mutex::new(virtio::Net::new(net_cfg.clone(), netdev_cfg))); MigrationManager::register_device_instance( VirtioNetState::descriptor(), device.clone(), - &device_cfg.id, + &net_cfg.id, ); device }; - self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, need_irqfd)?; + self.add_virtio_pci_device(&net_cfg.id, &bdf, device, multi_func, need_irqfd)?; if !hotplug { - self.reset_bus(&device_cfg.id)?; + self.reset_bus(&net_cfg.id)?; } Ok(()) } diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 1ac459665..ba58236b6 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -49,8 +49,8 @@ use crate::{MachineBase, MachineError, MachineOps}; use cpu::CpuLifecycleState; use devices::sysbus::{IRQ_BASE, IRQ_MAX}; use machine_manager::config::{ - parse_incoming_uri, parse_net, str_slip_to_clap, ConfigCheck, DriveConfig, MigrateMode, - NetworkInterfaceConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, + get_chardev_socket_path, parse_incoming_uri, str_slip_to_clap, ConfigCheck, DriveConfig, + MigrateMode, NetDevcfg, NetworkInterfaceConfig, VmConfig, }; use machine_manager::event; use machine_manager::event_loop::EventLoop; @@ -79,7 +79,8 @@ const MMIO_REPLACEABLE_NET_NR: usize = 2; struct MmioReplaceableConfig { // Device id. id: String, - // The config of the related backend device. Eg: Drive config of virtio mmio device. + // The config of the related backend device. + // Eg: Drive config of virtio mmio block. Netdev config of virtio mmio net. back_config: Arc, } @@ -173,7 +174,10 @@ impl LightMachine { ); } for id in 0..MMIO_REPLACEABLE_NET_NR { - let net = Arc::new(Mutex::new(Net::new(NetworkInterfaceConfig::default()))); + let net = Arc::new(Mutex::new(Net::new( + NetworkInterfaceConfig::default(), + NetDevcfg::default(), + ))); let virtio_mmio = VirtioMmioDevice::new(&self.base.sys_mem, net.clone()); rpl_devs.push(virtio_mmio); @@ -293,9 +297,18 @@ impl LightMachine { MMIO_REPLACEABLE_NET_NR ))); } - if cfg_any.downcast_ref::().is_none() { + if cfg_any.downcast_ref::().is_none() { return Err(anyhow!(MachineError::DevTypeErr("net".to_string()))); } + let net_config = NetworkInterfaceConfig { + classtype: driver, + id: id.clone(), + netdev: args.chardev.with_context(|| "No chardev set")?, + mac: args.mac, + iothread: args.iothread, + ..Default::default() + }; + configs.push(Arc::new(net_config)); slot + MMIO_REPLACEABLE_BLK_NR } else if driver.contains("blk") { if slot >= MMIO_REPLACEABLE_BLK_NR { @@ -384,17 +397,33 @@ impl LightMachine { vm_config: &mut VmConfig, cfg_args: &str, ) -> Result<()> { - let device_cfg = parse_net(vm_config, cfg_args)?; - if device_cfg.vhost_type.is_some() { - let device = if device_cfg.vhost_type == Some(String::from("vhost-kernel")) { + let net_cfg = + NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + let netdev_cfg = vm_config + .netdevs + .remove(&net_cfg.netdev) + .with_context(|| format!("Netdev: {:?} not found for net device", &net_cfg.netdev))?; + if netdev_cfg.vhost_type().is_some() { + let device = if netdev_cfg.vhost_type().unwrap() == "vhost-kernel" { let net = Arc::new(Mutex::new(VhostKern::Net::new( - &device_cfg, + &net_cfg, + netdev_cfg, &self.base.sys_mem, ))); VirtioMmioDevice::new(&self.base.sys_mem, net) } else { + let chardev = netdev_cfg.chardev.clone().with_context(|| { + format!("Chardev not configured for netdev {:?}", netdev_cfg.id) + })?; + let chardev_cfg = vm_config + .chardev + .remove(&chardev) + .with_context(|| format!("Chardev: {:?} not found for netdev", chardev))?; + let sock_path = get_chardev_socket_path(chardev_cfg)?; let net = Arc::new(Mutex::new(VhostUser::Net::new( - &device_cfg, + &net_cfg, + netdev_cfg, + sock_path, &self.base.sys_mem, ))); VirtioMmioDevice::new(&self.base.sys_mem, net) @@ -408,11 +437,9 @@ impl LightMachine { MMIO_REPLACEABLE_NET_NR ); } - self.fill_replaceable_device( - &device_cfg.id, - vec![Arc::new(device_cfg.clone())], - index, - )?; + let configs: Vec> = + vec![Arc::new(netdev_cfg), Arc::new(net_cfg.clone())]; + self.fill_replaceable_device(&net_cfg.id, configs, index)?; self.replaceable_info.net_count += 1; } Ok(()) @@ -805,18 +832,9 @@ impl DeviceInterface for LightMachine { } fn netdev_add(&mut self, args: Box) -> Response { - let mut config = NetworkInterfaceConfig { + let mut netdev_cfg = NetDevcfg { id: args.id.clone(), - host_dev_name: "".to_string(), - mac: None, - tap_fds: None, - vhost_type: None, - vhost_fds: None, - iothread: None, - queues: 2, - mq: false, - socket_path: None, - queue_size: DEFAULT_VIRTQUEUE_SIZE, + ..Default::default() }; if let Some(fds) = args.fds { @@ -828,7 +846,7 @@ impl DeviceInterface for LightMachine { }; if let Some(fd_num) = QmpChannel::get_fd(&netdev_fd) { - config.tap_fds = Some(vec![fd_num]); + netdev_cfg.tap_fds = Some(vec![fd_num]); } else { // try to convert string to RawFd let fd_num = match netdev_fd.parse::() { @@ -846,10 +864,10 @@ impl DeviceInterface for LightMachine { ); } }; - config.tap_fds = Some(vec![fd_num]); + netdev_cfg.tap_fds = Some(vec![fd_num]); } } else if let Some(if_name) = args.if_name { - config.host_dev_name = if_name.clone(); + netdev_cfg.ifname = if_name.clone(); if create_tap(None, Some(&if_name), 1).is_err() { return Response::create_error_response( qmp_schema::QmpErrorClass::GenericError( @@ -860,7 +878,7 @@ impl DeviceInterface for LightMachine { } } - match self.add_replaceable_config(&args.id, Arc::new(config)) { + match self.add_replaceable_config(&args.id, Arc::new(netdev_cfg)) { Ok(()) => Response::create_empty_response(), Err(ref e) => { error!("{:?}", e); diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index d1421309b..c9e58976a 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -16,19 +16,15 @@ use anyhow::{anyhow, bail, Context, Result}; use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, pci_args_check}; -use super::{parse_bool, str_slip_to_clap, valid_id}; -use crate::config::get_chardev_socket_path; -use crate::config::{ - check_arg_too_long, CmdParser, ConfigCheck, ExBool, VmConfig, DEFAULT_VIRTQUEUE_SIZE, - MAX_PATH_LENGTH, MAX_VIRTIO_QUEUE, -}; +use super::error::ConfigError; +use super::{get_pci_df, parse_bool, str_slip_to_clap, valid_id, valid_virtqueue_size}; +use crate::config::{ConfigCheck, VmConfig, DEFAULT_VIRTQUEUE_SIZE, MAX_VIRTIO_QUEUE}; use crate::qmp::{qmp_channel::QmpChannel, qmp_schema}; const MAC_ADDRESS_LENGTH: usize = 17; /// Max virtqueue size of each virtqueue. -pub const MAX_QUEUE_SIZE_NET: u16 = 4096; +const MAX_QUEUE_SIZE_NET: u64 = 4096; /// Max num of virtqueues. const MAX_QUEUE_PAIRS: usize = MAX_VIRTIO_QUEUE / 2; @@ -135,135 +131,73 @@ impl ConfigCheck for NetDevcfg { /// Config struct for network /// Contains network device config, such as `host_dev_name`, `mac`... -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Parser)] #[serde(deny_unknown_fields)] +#[command(no_binary_name(true))] pub struct NetworkInterfaceConfig { + #[arg(long, value_parser = ["virtio-net-pci", "virtio-net-device"])] + pub classtype: String, + #[arg(long, default_value = "", value_parser = valid_id)] pub id: String, - pub host_dev_name: String, + #[arg(long)] + pub netdev: String, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, value_parser = parse_bool, action = ArgAction::Append)] + pub multifunction: Option, + #[arg(long, value_parser = valid_mac)] pub mac: Option, - pub tap_fds: Option>, - pub vhost_type: Option, - pub vhost_fds: Option>, + #[arg(long)] pub iothread: Option, - pub queues: u16, + #[arg(long, default_value="off", value_parser = parse_bool, action = ArgAction::Append)] pub mq: bool, - pub socket_path: Option, - /// All queues of a net device have the same queue size now. + // All queues of a net device have the same queue size now. + #[arg(long, default_value = "256", alias = "queue-size", value_parser = valid_network_queue_size)] pub queue_size: u16, + // MSI-X vectors the this network device has. This member isn't used now in stratovirt. + #[arg(long, default_value = "0")] + pub vectors: u16, } impl Default for NetworkInterfaceConfig { fn default() -> Self { NetworkInterfaceConfig { + classtype: "".to_string(), id: "".to_string(), - host_dev_name: "".to_string(), + netdev: "".to_string(), + bus: None, + addr: None, + multifunction: None, mac: None, - tap_fds: None, - vhost_type: None, - vhost_fds: None, iothread: None, - queues: 2, mq: false, - socket_path: None, queue_size: DEFAULT_VIRTQUEUE_SIZE, + vectors: 0, } } } +fn valid_network_queue_size(s: &str) -> Result { + let size: u64 = s.parse()?; + valid_virtqueue_size(size, DEFAULT_VIRTQUEUE_SIZE as u64, MAX_QUEUE_SIZE_NET)?; + + Ok(size as u16) +} + impl ConfigCheck for NetworkInterfaceConfig { fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "id")?; - check_arg_too_long(&self.host_dev_name, "host dev name")?; - if self.mac.is_some() && !check_mac_address(self.mac.as_ref().unwrap()) { return Err(anyhow!(ConfigError::MacFormatError)); } - if self.iothread.is_some() { - check_arg_too_long(self.iothread.as_ref().unwrap(), "iothread name")?; - } - - if self.socket_path.is_some() && self.socket_path.as_ref().unwrap().len() > MAX_PATH_LENGTH - { - return Err(anyhow!(ConfigError::StringLengthTooLong( - "socket path".to_string(), - MAX_PATH_LENGTH - ))); - } - - if self.queue_size < DEFAULT_VIRTQUEUE_SIZE || self.queue_size > MAX_QUEUE_SIZE_NET { - return Err(anyhow!(ConfigError::IllegalValue( - "queue size of net device".to_string(), - DEFAULT_VIRTQUEUE_SIZE as u64, - true, - MAX_QUEUE_SIZE_NET as u64, - true - ))); - } - - if self.queue_size & (self.queue_size - 1) != 0 { - bail!("queue size of net device should be power of 2!"); - } + valid_network_queue_size(&self.queue_size.to_string())?; Ok(()) } } -pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("virtio-net"); - cmd_parser - .push("") - .push("id") - .push("netdev") - .push("mq") - .push("vectors") - .push("bus") - .push("addr") - .push("multifunction") - .push("mac") - .push("iothread") - .push("queue-size"); - - cmd_parser.parse(net_config)?; - pci_args_check(&cmd_parser)?; - let mut netdevinterfacecfg = NetworkInterfaceConfig::default(); - - let netdev = cmd_parser - .get_value::("netdev")? - .with_context(|| ConfigError::FieldIsMissing("netdev".to_string(), "net".to_string()))?; - let netid = cmd_parser.get_value::("id")?.unwrap_or_default(); - - if let Some(mq) = cmd_parser.get_value::("mq")? { - netdevinterfacecfg.mq = mq.inner; - } - netdevinterfacecfg.iothread = cmd_parser.get_value::("iothread")?; - netdevinterfacecfg.mac = cmd_parser.get_value::("mac")?; - if let Some(queue_size) = cmd_parser.get_value::("queue-size")? { - netdevinterfacecfg.queue_size = queue_size; - } - - let netcfg = &vm_config - .netdevs - .remove(&netdev) - .with_context(|| format!("Netdev: {:?} not found for net device", &netdev))?; - netdevinterfacecfg.id = netid; - netdevinterfacecfg.host_dev_name = netcfg.ifname.clone(); - netdevinterfacecfg.tap_fds = netcfg.tap_fds.clone(); - netdevinterfacecfg.vhost_fds = netcfg.vhost_fds.clone(); - netdevinterfacecfg.vhost_type = netcfg.vhost_type(); - netdevinterfacecfg.queues = netcfg.queues; - if let Some(chardev) = &netcfg.chardev { - let char_dev = vm_config - .chardev - .remove(chardev) - .with_context(|| format!("Chardev: {:?} not found for character device", chardev))?; - netdevinterfacecfg.socket_path = Some(get_chardev_socket_path(char_dev)?); - } - - netdevinterfacecfg.check()?; - Ok(netdevinterfacecfg) -} - fn get_netdev_fd(fd_name: &str) -> Result { if let Some(fd) = QmpChannel::get_fd(fd_name) { Ok(fd) @@ -381,6 +315,13 @@ impl VmConfig { } } +fn valid_mac(mac: &str) -> Result { + if !check_mac_address(mac) { + return Err(anyhow!(ConfigError::MacFormatError)); + } + Ok(mac.to_string()) +} + fn check_mac_address(mac: &str) -> bool { if mac.len() != MAC_ADDRESS_LENGTH { return false; @@ -427,169 +368,119 @@ fn is_netdev_queues_valid(queues: u16) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use crate::config::get_pci_bdf; #[test] - fn test_network_config_cmdline_parser() { + fn test_netdev_config_cmdline_parser() { let mut vm_config = VmConfig::default(); + + // Test1: Right. assert!(vm_config.add_netdev("tap,id=eth0,ifname=tap0").is_ok()); - let net_cfg_res = parse_net( - &mut vm_config, - "virtio-net-device,id=net0,netdev=eth0,iothread=iothread0", - ); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.id, "net0"); - assert_eq!(network_configs.host_dev_name, "tap0"); - assert_eq!(network_configs.iothread, Some("iothread0".to_string())); - assert!(network_configs.mac.is_none()); - assert!(network_configs.tap_fds.is_none()); - assert!(network_configs.vhost_type.is_none()); - assert!(network_configs.vhost_fds.is_none()); + assert!(vm_config.add_netdev("tap,id=eth0,ifname=tap0").is_err()); + let netdev_cfg = vm_config.netdevs.get("eth0").unwrap(); + assert_eq!(netdev_cfg.id, "eth0"); + assert_eq!(netdev_cfg.ifname, "tap0"); + assert!(netdev_cfg.tap_fds.is_none()); + assert_eq!(netdev_cfg.vhost_kernel, false); + assert!(netdev_cfg.vhost_fds.is_none()); + assert_eq!(netdev_cfg.queues, 2); + assert!(netdev_cfg.vhost_type().is_none()); - let mut vm_config = VmConfig::default(); assert!(vm_config .add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4") .is_ok()); - let net_cfg_res = parse_net( - &mut vm_config, - "virtio-net-device,id=net1,netdev=eth1,mac=12:34:56:78:9A:BC", - ); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.id, "net1"); - assert_eq!(network_configs.host_dev_name, "tap1"); - assert_eq!(network_configs.mac, Some(String::from("12:34:56:78:9A:BC"))); - assert!(network_configs.tap_fds.is_none()); - assert_eq!( - network_configs.vhost_type, - Some(String::from("vhost-kernel")) - ); - assert_eq!(network_configs.vhost_fds, Some(vec![4])); + let netdev_cfg = vm_config.netdevs.get("eth1").unwrap(); + assert_eq!(netdev_cfg.ifname, "tap1"); + assert_eq!(netdev_cfg.vhost_type().unwrap(), "vhost-kernel"); + assert_eq!(netdev_cfg.vhost_fds, Some(vec![4])); - let mut vm_config = VmConfig::default(); - assert!(vm_config.add_netdev("tap,id=eth1,fd=35").is_ok()); - let net_cfg_res = parse_net(&mut vm_config, "virtio-net-device,id=net1,netdev=eth1"); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.id, "net1"); - assert_eq!(network_configs.host_dev_name, ""); - assert_eq!(network_configs.tap_fds, Some(vec![35])); + assert!(vm_config.add_netdev("tap,id=eth2,fd=35").is_ok()); + let netdev_cfg = vm_config.netdevs.get("eth2").unwrap(); + assert_eq!(netdev_cfg.tap_fds, Some(vec![35])); - let mut vm_config = VmConfig::default(); assert!(vm_config - .add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4") + .add_netdev("tap,id=eth3,ifname=tap0,queues=4") .is_ok()); - let net_cfg_res = parse_net( - &mut vm_config, - "virtio-net-device,id=net1,netdev=eth2,mac=12:34:56:78:9A:BC", - ); - assert!(net_cfg_res.is_err()); + let netdev_cfg = vm_config.netdevs.get("eth3").unwrap(); + assert_eq!(netdev_cfg.queues, 8); - let mut vm_config = VmConfig::default(); - assert!(vm_config.add_netdev("tap,id=eth1,fd=35").is_ok()); - let net_cfg_res = parse_net(&mut vm_config, "virtio-net-device,id=net1,netdev=eth3"); - assert!(net_cfg_res.is_err()); - - // multi queue testcases - let mut vm_config = VmConfig::default(); assert!(vm_config - .add_netdev("tap,id=eth0,ifname=tap0,queues=4") + .add_netdev("tap,id=eth4,fds=34:35:36:37:38") .is_ok()); - let net_cfg_res = parse_net( - &mut vm_config, - "virtio-net-device,id=net0,netdev=eth0,iothread=iothread0,mq=on,vectors=6", - ); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.queues, 8); - assert_eq!(network_configs.mq, true); + let netdev_cfg = vm_config.netdevs.get("eth4").unwrap(); + assert_eq!(netdev_cfg.queues, 10); + assert_eq!(netdev_cfg.tap_fds, Some(vec![34, 35, 36, 37, 38])); - let mut vm_config = VmConfig::default(); assert!(vm_config - .add_netdev("tap,id=eth0,fds=34:35:36:37:38") + .add_netdev("tap,id=eth5,fds=34:35:36:37:38,vhost=on,vhostfds=39:40:41:42:43") .is_ok()); - let net_cfg_res = parse_net( - &mut vm_config, - "virtio-net-device,id=net0,netdev=eth0,iothread=iothread0,mq=off,vectors=12", - ); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.queues, 10); - assert_eq!(network_configs.tap_fds, Some(vec![34, 35, 36, 37, 38])); - assert_eq!(network_configs.mq, false); + let netdev_cfg = vm_config.netdevs.get("eth5").unwrap(); + assert_eq!(netdev_cfg.queues, 10); + assert_eq!(netdev_cfg.vhost_fds, Some(vec![39, 40, 41, 42, 43])); - let mut vm_config = VmConfig::default(); + // Test2: Missing values assert!(vm_config - .add_netdev("tap,id=eth0,fds=34:35:36:37:38,vhost=on,vhostfds=39:40:41:42:43") - .is_ok()); - let net_cfg_res = parse_net( - &mut vm_config, - "virtio-net-device,id=net0,netdev=eth0,iothread=iothread0,mq=off,vectors=12", - ); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.queues, 10); - assert_eq!(network_configs.vhost_fds, Some(vec![39, 40, 41, 42, 43])); - assert_eq!(network_configs.mq, false); + .add_netdev("tap,fds=34:35:36:37:38,vhost=on") + .is_err()); + + // Test3: Illegal values. + assert!(vm_config + .add_netdev("tap,id=eth10,fds=34:35:36:37:38,vhost=on,vhostfds=39,40,41,42,43") + .is_err()); + assert!(vm_config.add_netdev("tap,id=eth10,queues=0").is_err()); + assert!(vm_config.add_netdev("tap,id=eth10,queues=17").is_err()); } #[test] - fn test_pci_network_config_cmdline_parser() { + fn test_networkinterface_config_cmdline_parser() { + // Test1: Right. let mut vm_config = VmConfig::default(); - assert!(vm_config .add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4") .is_ok()); + let net_cmd = + "virtio-net-pci,id=net1,netdev=eth1,bus=pcie.0,addr=0x1.0x2,mac=12:34:56:78:9A:BC,mq=on,vectors=6,queue-size=2048,multifunction=on"; let net_cfg = - "virtio-net-pci,id=net1,netdev=eth1,bus=pcie.0,addr=0x1.0x2,mac=12:34:56:78:9A:BC"; - let net_cfg_res = parse_net(&mut vm_config, net_cfg); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.id, "net1"); - assert_eq!(network_configs.host_dev_name, "tap1"); - assert_eq!(network_configs.mac, Some(String::from("12:34:56:78:9A:BC"))); - assert!(network_configs.tap_fds.is_none()); - assert_eq!( - network_configs.vhost_type, - Some(String::from("vhost-kernel")) - ); - assert_eq!(network_configs.vhost_fds.unwrap()[0], 4); - let pci_bdf = get_pci_bdf(net_cfg); - assert!(pci_bdf.is_ok()); - let pci = pci_bdf.unwrap(); - assert_eq!(pci.bus, "pcie.0".to_string()); - assert_eq!(pci.addr, (1, 2)); - - let net_cfg_res = parse_net(&mut vm_config, net_cfg); - assert!(net_cfg_res.is_err()); - + NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)).unwrap(); + assert_eq!(net_cfg.id, "net1"); + assert_eq!(net_cfg.netdev, "eth1"); + assert_eq!(net_cfg.bus.unwrap(), "pcie.0"); + assert_eq!(net_cfg.addr.unwrap(), (1, 2)); + assert_eq!(net_cfg.mac.unwrap(), "12:34:56:78:9A:BC"); + assert_eq!(net_cfg.vectors, 6); + assert_eq!(net_cfg.mq, true); + assert_eq!(net_cfg.queue_size, 2048); + assert_eq!(net_cfg.multifunction, Some(true)); + let netdev_cfg = vm_config.netdevs.get(&net_cfg.netdev).unwrap(); + assert_eq!(netdev_cfg.vhost_type().unwrap(), "vhost-kernel"); + + // Test2: Default values. let mut vm_config = VmConfig::default(); - assert!(vm_config - .add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4") - .is_ok()); - let net_cfg = - "virtio-net-pci,id=net1,netdev=eth1,bus=pcie.0,addr=0x1.0x2,mac=12:34:56:78:9A:BC,multifunction=on"; - assert!(parse_net(&mut vm_config, net_cfg).is_ok()); - - // For vhost-user net assert!(vm_config.add_netdev("vhost-user,id=netdevid").is_ok()); - let net_cfg = + let net_cmd = "virtio-net-pci,id=netid,netdev=netdevid,bus=pcie.0,addr=0x2.0x0,mac=12:34:56:78:9A:BC"; - let net_cfg_res = parse_net(&mut vm_config, net_cfg); - assert!(net_cfg_res.is_ok()); - let network_configs = net_cfg_res.unwrap(); - assert_eq!(network_configs.id, "netid"); - assert_eq!(network_configs.vhost_type, Some("vhost-user".to_string())); - assert_eq!(network_configs.mac, Some("12:34:56:78:9A:BC".to_string())); - - assert!(vm_config - .add_netdev("vhost-user,id=netdevid2,chardev=chardevid2") - .is_ok()); let net_cfg = - "virtio-net-pci,id=netid2,netdev=netdevid2,bus=pcie.0,addr=0x2.0x0,mac=12:34:56:78:9A:BC"; - let net_cfg_res = parse_net(&mut vm_config, net_cfg); - assert!(net_cfg_res.is_err()); + NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)).unwrap(); + assert_eq!(net_cfg.queue_size, 256); + assert_eq!(net_cfg.mq, false); + assert_eq!(net_cfg.vectors, 0); + let netdev_cfg = vm_config.netdevs.get(&net_cfg.netdev).unwrap(); + assert_eq!(netdev_cfg.vhost_type().unwrap(), "vhost-user"); + + // Test3: Missing Parameters. + let net_cmd = "virtio-net-pci,id=netid"; + let result = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)); + assert!(result.is_err()); + + // Test4: Illegal Parameters. + let net_cmd = "virtio-net-pci,id=netid,netdev=netdevid,mac=1:1:1"; + let result = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)); + assert!(result.is_err()); + let net_cmd = "virtio-net-pci,id=netid,netdev=netdevid,queue-size=128"; + let result = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)); + assert!(result.is_err()); + let net_cmd = "virtio-net-pci,id=netid,netdev=netdevid,queue-size=10240"; + let result = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)); + assert!(result.is_err()); } #[test] diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 48b19cc4a..1691dc06a 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -46,7 +46,7 @@ use crate::{ use address_space::{AddressSpace, RegionCache}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use machine_manager::{ - config::{ConfigCheck, NetworkInterfaceConfig}, + config::{ConfigCheck, NetDevcfg, NetworkInterfaceConfig}, event_loop::EventLoop, }; use migration::{ @@ -1190,6 +1190,8 @@ pub struct Net { base: VirtioBase, /// Configuration of the network device. net_cfg: NetworkInterfaceConfig, + /// Configuration of the network device. + netdev_cfg: NetDevcfg, /// Virtio net configurations. config_space: Arc>, /// Tap device opened. @@ -1203,9 +1205,9 @@ pub struct Net { } impl Net { - pub fn new(net_cfg: NetworkInterfaceConfig) -> Self { + pub fn new(net_cfg: NetworkInterfaceConfig, netdev_cfg: NetDevcfg) -> Self { let queue_num = if net_cfg.mq { - (net_cfg.queues + 1) as usize + (netdev_cfg.queues + 1) as usize } else { QUEUE_NUM_NET }; @@ -1214,6 +1216,7 @@ impl Net { Self { base: VirtioBase::new(VIRTIO_TYPE_NET, queue_num, queue_size), net_cfg, + netdev_cfg, ..Default::default() } } @@ -1397,11 +1400,11 @@ impl VirtioDevice for Net { ); } - let queue_pairs = self.net_cfg.queues / 2; - if !self.net_cfg.host_dev_name.is_empty() { - self.taps = create_tap(None, Some(&self.net_cfg.host_dev_name), queue_pairs) + let queue_pairs = self.netdev_cfg.queues / 2; + if !self.netdev_cfg.ifname.is_empty() { + self.taps = create_tap(None, Some(&self.netdev_cfg.ifname), queue_pairs) .with_context(|| "Failed to open tap with file path")?; - } else if let Some(fds) = self.net_cfg.tap_fds.as_mut() { + } else if let Some(fds) = self.netdev_cfg.tap_fds.as_mut() { let mut created_fds = 0; if let Some(taps) = &self.taps { for (index, tap) in taps.iter().enumerate() { @@ -1444,7 +1447,7 @@ impl VirtioDevice for Net { let mut locked_config = self.config_space.lock().unwrap(); - let queue_pairs = self.net_cfg.queues / 2; + let queue_pairs = self.netdev_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) @@ -1596,9 +1599,15 @@ impl VirtioDevice for Net { Ok(()) } + // configs[0]: NetDevcfg. configs[1]: NetworkInterfaceConfig. fn update_config(&mut self, dev_config: Vec>) -> Result<()> { if !dev_config.is_empty() { - self.net_cfg = dev_config[0] + self.netdev_cfg = dev_config[0] + .as_any() + .downcast_ref::() + .unwrap() + .clone(); + self.net_cfg = dev_config[1] .as_any() .downcast_ref::() .unwrap() @@ -1703,16 +1712,16 @@ mod tests { #[test] fn test_net_init() { // test net new method - let mut net = Net::new(NetworkInterfaceConfig::default()); + let mut net = Net::new(NetworkInterfaceConfig::default(), NetDevcfg::default()); assert_eq!(net.base.device_features, 0); assert_eq!(net.base.driver_features, 0); assert_eq!(net.taps.is_none(), true); assert_eq!(net.senders.is_none(), true); assert_eq!(net.net_cfg.mac.is_none(), true); - assert_eq!(net.net_cfg.tap_fds.is_none(), true); - assert_eq!(net.net_cfg.vhost_type.is_none(), true); - assert_eq!(net.net_cfg.vhost_fds.is_none(), true); + assert_eq!(net.netdev_cfg.tap_fds.is_none(), true); + assert!(net.netdev_cfg.vhost_type().is_none()); + assert_eq!(net.netdev_cfg.vhost_fds.is_none(), true); // test net realize method net.realize().unwrap(); @@ -1864,7 +1873,7 @@ mod tests { #[test] fn test_iothread() { - let mut net = Net::new(NetworkInterfaceConfig::default()); + let mut net = Net::new(NetworkInterfaceConfig::default(), NetDevcfg::default()); net.net_cfg.iothread = Some("iothread".to_string()); if let Err(err) = net.realize() { let err_msg = format!( diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 55a497242..8a4f6c020 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -31,7 +31,7 @@ use crate::{ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MQ, VIRTIO_TYPE_NET, }; use address_space::AddressSpace; -use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::config::{NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; @@ -79,6 +79,8 @@ pub struct Net { base: VirtioBase, /// Configuration of the network device. net_cfg: NetworkInterfaceConfig, + /// Configuration of the backend netdev. + netdev_cfg: NetDevcfg, /// Virtio net configurations. config_space: Arc>, /// Tap device opened. @@ -94,17 +96,22 @@ pub struct Net { } impl Net { - pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { - let queue_num = if cfg.mq { - (cfg.queues + 1) as usize + pub fn new( + net_cfg: &NetworkInterfaceConfig, + netdev_cfg: NetDevcfg, + mem_space: &Arc, + ) -> Self { + let queue_num = if net_cfg.mq { + (netdev_cfg.queues + 1) as usize } else { QUEUE_NUM_NET }; - let queue_size = cfg.queue_size; + let queue_size = net_cfg.queue_size; Net { base: VirtioBase::new(VIRTIO_TYPE_NET, queue_num, queue_size), - net_cfg: cfg.clone(), + net_cfg: net_cfg.clone(), + netdev_cfg, config_space: Default::default(), taps: None, backends: None, @@ -125,10 +132,10 @@ impl VirtioDevice for Net { } fn realize(&mut self) -> Result<()> { - let queue_pairs = self.net_cfg.queues / 2; + let queue_pairs = self.netdev_cfg.queues / 2; let mut backends = Vec::with_capacity(queue_pairs as usize); for index in 0..queue_pairs { - let fd = if let Some(fds) = self.net_cfg.vhost_fds.as_mut() { + let fd = if let Some(fds) = self.netdev_cfg.vhost_fds.as_mut() { fds.get(index as usize).copied() } else { None @@ -142,12 +149,12 @@ impl VirtioDevice for Net { backends.push(backend); } - let host_dev_name = match self.net_cfg.host_dev_name.as_str() { + let host_dev_name = match self.netdev_cfg.ifname.as_str() { "" => None, - _ => Some(self.net_cfg.host_dev_name.as_str()), + _ => Some(self.netdev_cfg.ifname.as_str()), }; - self.taps = create_tap(self.net_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) + self.taps = create_tap(self.netdev_cfg.tap_fds.as_ref(), host_dev_name, queue_pairs) .with_context(|| "Failed to create tap for vhost net")?; self.backends = Some(backends); @@ -174,7 +181,7 @@ impl VirtioDevice for Net { let mut locked_config = self.config_space.lock().unwrap(); - let queue_pairs = self.net_cfg.queues / 2; + let queue_pairs = self.netdev_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) @@ -385,7 +392,7 @@ impl VirtioDevice for Net { } fn reset(&mut self) -> Result<()> { - let queue_pairs = self.net_cfg.queues / 2; + let queue_pairs = self.netdev_cfg.queues / 2; for index in 0..queue_pairs as usize { let backend = match &self.backends { None => return Err(anyhow!("Failed to get backend for vhost net")), @@ -441,46 +448,43 @@ mod tests { #[test] fn test_vhost_net_realize() { - let net1 = NetworkInterfaceConfig { - id: "eth1".to_string(), - host_dev_name: "tap1".to_string(), - mac: Some("1F:2C:3E:4A:5B:6D".to_string()), - vhost_type: Some("vhost-kernel".to_string()), + let netdev_cfg1 = NetDevcfg { + netdev_type: "tap".to_string(), + id: "net1".to_string(), tap_fds: Some(vec![4]), + vhost_kernel: true, vhost_fds: Some(vec![5]), - iothread: None, + ifname: "tap1".to_string(), queues: 2, + ..Default::default() + }; + let vhost_net_conf = NetworkInterfaceConfig { + id: "eth1".to_string(), + mac: Some("1F:2C:3E:4A:5B:6D".to_string()), + iothread: None, mq: false, - socket_path: None, queue_size: DEFAULT_VIRTQUEUE_SIZE, + ..Default::default() }; - let conf = vec![net1]; - let confs = Some(conf); - let vhost_net_confs = confs.unwrap(); - let vhost_net_conf = vhost_net_confs[0].clone(); let vhost_net_space = vhost_address_space_init(); - let mut vhost_net = Net::new(&vhost_net_conf, &vhost_net_space); + let mut vhost_net = Net::new(&vhost_net_conf, netdev_cfg1, &vhost_net_space); // the tap_fd and vhost_fd attribute of vhost-net can't be assigned. - assert_eq!(vhost_net.realize().is_ok(), false); + assert!(vhost_net.realize().is_err()); - let net1 = NetworkInterfaceConfig { - id: "eth0".to_string(), - host_dev_name: "".to_string(), - mac: Some("1A:2B:3C:4D:5E:6F".to_string()), - vhost_type: Some("vhost-kernel".to_string()), - tap_fds: None, - vhost_fds: None, - iothread: None, + let netdev_cfg2 = NetDevcfg { + netdev_type: "tap".to_string(), + id: "net2".to_string(), + vhost_kernel: true, queues: 2, - mq: false, - socket_path: None, + ..Default::default() + }; + let net_cfg2 = NetworkInterfaceConfig { + id: "eth2".to_string(), + mac: Some("1A:2B:3C:4D:5E:6F".to_string()), queue_size: DEFAULT_VIRTQUEUE_SIZE, + ..Default::default() }; - let conf = vec![net1]; - let confs = Some(conf); - let vhost_net_confs = confs.unwrap(); - let vhost_net_conf = vhost_net_confs[0].clone(); - let mut vhost_net = Net::new(&vhost_net_conf, &vhost_net_space); + let mut vhost_net = Net::new(&net_cfg2, netdev_cfg2, &vhost_net_space); // if fail to open vhost-net device, no need to continue. if let Err(_e) = File::open("/dev/vhost-net") { diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index e7f0964bf..4dc763458 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -28,7 +28,7 @@ use crate::{ VIRTIO_NET_F_MRG_RXBUF, VIRTIO_TYPE_NET, }; use address_space::AddressSpace; -use machine_manager::config::NetworkInterfaceConfig; +use machine_manager::config::{NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; @@ -42,6 +42,10 @@ pub struct Net { base: VirtioBase, /// Configuration of the vhost user network device. net_cfg: NetworkInterfaceConfig, + /// Configuration of the backend netdev. + netdev_cfg: NetDevcfg, + /// path of the socket chardev. + sock_path: String, /// Virtio net configurations. config_space: Arc>, /// System address space. @@ -53,18 +57,25 @@ pub struct Net { } impl Net { - pub fn new(cfg: &NetworkInterfaceConfig, mem_space: &Arc) -> Self { - let queue_num = if cfg.mq { + pub fn new( + net_cfg: &NetworkInterfaceConfig, + netdev_cfg: NetDevcfg, + sock_path: String, + mem_space: &Arc, + ) -> Self { + let queue_num = if net_cfg.mq { // If support multi-queue, it should add 1 control queue. - (cfg.queues + 1) as usize + (netdev_cfg.queues + 1) as usize } else { QUEUE_NUM_NET }; - let queue_size = cfg.queue_size; + let queue_size = net_cfg.queue_size; Net { base: VirtioBase::new(VIRTIO_TYPE_NET, queue_num, queue_size), - net_cfg: cfg.clone(), + net_cfg: net_cfg.clone(), + netdev_cfg, + sock_path, config_space: Default::default(), mem_space: mem_space.clone(), client: None, @@ -115,14 +126,9 @@ impl VirtioDevice for Net { } fn realize(&mut self) -> Result<()> { - let socket_path = self - .net_cfg - .socket_path - .as_ref() - .with_context(|| "vhost-user: socket path is not found")?; let client = VhostUserClient::new( &self.mem_space, - socket_path, + &self.sock_path, self.queue_num() as u64, VhostBackendType::TypeNet, ) @@ -158,7 +164,7 @@ impl VirtioDevice for Net { let mut locked_config = self.config_space.lock().unwrap(); - let queue_pairs = self.net_cfg.queues / 2; + let queue_pairs = self.netdev_cfg.queues / 2; if self.net_cfg.mq && (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN..=VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) .contains(&queue_pairs) -- Gitee From a876b01981042f8adccf2821110777a8b0fce675 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 22 Apr 2024 03:20:07 +0800 Subject: [PATCH 1785/2187] virtio-serial: use clap to parse the parameters of the virtio-serial/virtserialport/virtconsole config Use clap to parse the parameters of the virtio-serial/virtserialport/virtconsole config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 54 +++-- machine_manager/src/config/chardev.rs | 283 ++++++++++---------------- virtio/src/device/serial.rs | 36 ++-- 3 files changed, 164 insertions(+), 209 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index eaafa909e..5888f684b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -74,10 +74,10 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ complete_numa_node, get_chardev_socket_path, get_multi_function, get_pci_bdf, parse_device_id, - parse_device_type, parse_numa_distance, parse_numa_mem, parse_virtio_serial, - parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo, BootSource, DriveConfig, - DriveFile, Incoming, MachineMemConfig, MigrateMode, NetworkInterfaceConfig, NumaConfig, - NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VmConfig, FAST_UNPLUG_ON, + parse_device_type, parse_numa_distance, parse_numa_mem, parse_vsock, str_slip_to_clap, + BootIndexInfo, BootSource, ConfigCheck, DriveConfig, DriveFile, Incoming, MachineMemConfig, + MigrateMode, NetworkInterfaceConfig, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, + SerialConfig, VirtioSerialInfo, VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; @@ -694,11 +694,17 @@ pub trait MachineOps { /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. fn add_virtio_serial(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let serial_cfg = parse_virtio_serial(vm_config, cfg_args)?; + if vm_config.virtio_serial.is_some() { + bail!("Only one virtio serial device is supported"); + } + let mut serial_cfg = + VirtioSerialInfo::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + serial_cfg.auto_max_ports(); + serial_cfg.check()?; let sys_mem = self.get_sys_mem().clone(); let serial = Arc::new(Mutex::new(Serial::new(serial_cfg.clone()))); - match parse_device_type(cfg_args)?.as_str() { + match serial_cfg.classtype.as_str() { "virtio-serial-device" => { let device = VirtioMmioDevice::new(&sys_mem, serial.clone()); MigrationManager::register_device_instance( @@ -709,8 +715,8 @@ pub trait MachineOps { ); } _ => { - let bdf = serial_cfg.pci_bdf.unwrap(); - let multi_func = serial_cfg.multifunction; + let bdf = PciBdf::new(serial_cfg.bus.clone().unwrap(), serial_cfg.addr.unwrap()); + let multi_func = serial_cfg.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&serial_cfg.id, &bdf, serial.clone(), multi_func, false) .with_context(|| "Failed to add virtio pci serial device")?; } @@ -722,6 +728,7 @@ pub trait MachineOps { &serial_cfg.id, ); + vm_config.virtio_serial = Some(serial_cfg); Ok(()) } @@ -738,7 +745,7 @@ pub trait MachineOps { .with_context(|| "No virtio serial device specified")?; let mut virtio_device = None; - if serial_cfg.pci_bdf.is_none() { + if serial_cfg.bus.is_none() { // Micro_vm. for dev in self.get_sys_bus().devices.iter() { let locked_busdev = dev.lock().unwrap(); @@ -775,24 +782,31 @@ pub trait MachineOps { let mut virtio_dev_h = virtio_dev.lock().unwrap(); let serial = virtio_dev_h.as_any_mut().downcast_mut::().unwrap(); - let is_console = matches!(parse_device_type(cfg_args)?.as_str(), "virtconsole"); + let mut serialport_cfg = + VirtioSerialPortCfg::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let free_port0 = find_port_by_nr(&serial.ports, 0).is_none(); // Note: port 0 is reserved for a virtconsole. let free_nr = get_max_nr(&serial.ports) + 1; - let serialport_cfg = - parse_virtserialport(vm_config, cfg_args, is_console, free_nr, free_port0)?; - if serialport_cfg.nr >= serial.max_nr_ports { + serialport_cfg.auto_nr(free_port0, free_nr, serial.max_nr_ports)?; + serialport_cfg.check()?; + if find_port_by_nr(&serial.ports, serialport_cfg.nr.unwrap()).is_some() { bail!( - "virtio serial port nr {} should be less than virtio serial's max_nr_ports {}", - serialport_cfg.nr, - serial.max_nr_ports + "Repetitive virtio serial port nr {}.", + serialport_cfg.nr.unwrap() ); } - if find_port_by_nr(&serial.ports, serialport_cfg.nr).is_some() { - bail!("Repetitive virtio serial port nr {}.", serialport_cfg.nr,); - } + let is_console = matches!(serialport_cfg.classtype.as_str(), "virtconsole"); + let chardev_cfg = vm_config + .chardev + .remove(&serialport_cfg.chardev) + .with_context(|| { + format!( + "Chardev {:?} not found or is in use", + &serialport_cfg.chardev + ) + })?; - let mut serial_port = SerialPort::new(serialport_cfg); + let mut serial_port = SerialPort::new(serialport_cfg, chardev_cfg); let port = Arc::new(Mutex::new(serial_port.clone())); serial_port.realize()?; if !is_console { diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 68c23c6ea..124a6288d 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -18,10 +18,10 @@ use clap::{ArgAction, Parser, Subcommand}; use log::error; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, get_pci_bdf, pci_args_check, str_slip_to_clap, PciBdf}; +use super::{error::ConfigError, pci_args_check, str_slip_to_clap}; +use super::{get_pci_df, parse_bool}; use crate::config::{ - check_arg_too_long, valid_id, valid_path, valid_socket_path, CmdParser, ConfigCheck, ExBool, - VmConfig, + check_arg_too_long, valid_id, valid_path, valid_socket_path, CmdParser, ConfigCheck, VmConfig, }; use crate::qmp::qmp_schema; @@ -32,17 +32,50 @@ const MIN_GUEST_CID: u64 = 3; const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31; /// Config structure for virtio-serial-port. -#[derive(Debug, Clone)] -pub struct VirtioSerialPort { +#[derive(Parser, Debug, Clone)] +#[command(no_binary_name(true))] +pub struct VirtioSerialPortCfg { + #[arg(long, value_parser = ["virtconsole", "virtserialport"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] pub id: String, - pub chardev: ChardevConfig, - pub nr: u32, - pub is_console: bool, + #[arg(long)] + pub chardev: String, + #[arg(long)] + pub nr: Option, } -impl ConfigCheck for VirtioSerialPort { +impl ConfigCheck for VirtioSerialPortCfg { fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "chardev id") + if self.classtype != "virtconsole" && self.nr.unwrap() == 0 { + bail!("Port number 0 on virtio-serial devices reserved for virtconsole device."); + } + + Ok(()) + } +} + +impl VirtioSerialPortCfg { + /// If nr is not set in command line. Configure incremental maximum value for virtconsole. + /// Configure incremental maximum value(except 0) for virtserialport. + pub fn auto_nr(&mut self, free_port0: bool, free_nr: u32, max_nr_ports: u32) -> Result<()> { + let free_console_nr = if free_port0 { 0 } else { free_nr }; + let auto_nr = match self.classtype.as_str() { + "virtconsole" => free_console_nr, + "virtserialport" => free_nr, + _ => bail!("Invalid classtype."), + }; + let nr = self.nr.unwrap_or(auto_nr); + if nr >= max_nr_ports { + bail!( + "virtio serial port nr {} should be less than virtio serial's max_nr_ports {}", + nr, + max_nr_ports + ); + } + + self.nr = Some(nr); + Ok(()) } } @@ -221,47 +254,6 @@ pub fn get_chardev_socket_path(chardev: ChardevConfig) -> Result { bail!("Chardev {:?} backend should be unix-socket type.", id); } -pub fn parse_virtserialport( - vm_config: &mut VmConfig, - config_args: &str, - is_console: bool, - free_nr: u32, - free_port0: bool, -) -> Result { - let mut cmd_parser = CmdParser::new("virtserialport"); - cmd_parser.push("").push("id").push("chardev").push("nr"); - cmd_parser.parse(config_args)?; - - let chardev_name = cmd_parser - .get_value::("chardev")? - .with_context(|| { - ConfigError::FieldIsMissing("chardev".to_string(), "virtserialport".to_string()) - })?; - let id = cmd_parser.get_value::("id")?.with_context(|| { - ConfigError::FieldIsMissing("id".to_string(), "virtserialport".to_string()) - })?; - - let nr = cmd_parser - .get_value::("nr")? - .unwrap_or(if is_console && free_port0 { 0 } else { free_nr }); - - if nr == 0 && !is_console { - bail!("Port number 0 on virtio-serial devices reserved for virtconsole device."); - } - - if let Some(chardev) = vm_config.chardev.remove(&chardev_name) { - let port_cfg = VirtioSerialPort { - id, - chardev, - nr, - is_console, - }; - port_cfg.check()?; - return Ok(port_cfg); - } - bail!("Chardev {:?} not found or is in use", &chardev_name); -} - impl VmConfig { /// Add chardev config to `VmConfig`. pub fn add_chardev(&mut self, chardev_config: &str) -> Result<()> { @@ -388,127 +380,87 @@ pub fn parse_vsock(vsock_config: &str) -> Result { Ok(vsock) } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct VirtioSerialInfo { + #[arg(long, value_parser = ["virtio-serial-pci", "virtio-serial-device"])] + pub classtype: String, + #[arg(long, default_value = "", value_parser = valid_id)] pub id: String, - pub pci_bdf: Option, - pub multifunction: bool, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, value_parser = parse_bool, action = ArgAction::Append)] + pub multifunction: Option, + #[arg(long, default_value = "31", value_parser = clap::value_parser!(u32).range(1..=DEFAULT_SERIAL_PORTS_NUMBER as i64))] pub max_ports: u32, } -impl ConfigCheck for VirtioSerialInfo { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "virtio-serial id")?; - - if self.max_ports < 1 || self.max_ports > DEFAULT_SERIAL_PORTS_NUMBER { - return Err(anyhow!(ConfigError::IllegalValue( - "Virtio-serial max_ports".to_string(), - 1, - true, - DEFAULT_SERIAL_PORTS_NUMBER as u64, - true - ))); +impl VirtioSerialInfo { + pub fn auto_max_ports(&mut self) { + if self.classtype == "virtio-serial-device" { + // Micro_vm does not support multi-ports in virtio-serial-device. + self.max_ports = 1; } - - Ok(()) } } -pub fn parse_virtio_serial( - vm_config: &mut VmConfig, - serial_config: &str, -) -> Result { - let mut cmd_parser = CmdParser::new("virtio-serial"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("multifunction") - .push("max_ports"); - cmd_parser.parse(serial_config)?; - pci_args_check(&cmd_parser)?; - - if vm_config.virtio_serial.is_some() { - bail!("Only one virtio serial device is supported"); - } - - let id = cmd_parser.get_value::("id")?.unwrap_or_default(); - let multifunction = cmd_parser - .get_value::("multifunction")? - .map_or(false, |switch| switch.into()); - let max_ports = cmd_parser - .get_value::("max_ports")? - .unwrap_or(DEFAULT_SERIAL_PORTS_NUMBER); - let virtio_serial = if serial_config.contains("-pci") { - let pci_bdf = get_pci_bdf(serial_config)?; - VirtioSerialInfo { - id, - pci_bdf: Some(pci_bdf), - multifunction, - max_ports, - } - } else { - VirtioSerialInfo { - id, - pci_bdf: None, - multifunction, - // Micro_vm does not support multi-ports in virtio-serial-device. - max_ports: 1, +impl ConfigCheck for VirtioSerialInfo { + fn check(&self) -> Result<()> { + match self.classtype.as_str() { + "virtio-serial-pci" => {} + "virtio-serial-device" => { + if self.bus.is_some() || self.addr.is_some() || self.multifunction.is_some() { + bail!("virtio mmio device should not set bus/addr/multifunction"); + } + } + _ => { + bail!("Invalid classtype."); + } } - }; - virtio_serial.check()?; - vm_config.virtio_serial = Some(virtio_serial.clone()); - Ok(virtio_serial) + Ok(()) + } } #[cfg(test)] mod tests { use super::*; - use crate::config::parse_virtio_serial; fn test_mmio_console_config_cmdline_parser(chardev_cfg: &str, expected_chardev: ChardevType) { let mut vm_config = VmConfig::default(); - assert!(parse_virtio_serial(&mut vm_config, "virtio-serial-device").is_ok()); + let serial_cmd = "virtio-serial-device"; + let mut serial_cfg = + VirtioSerialInfo::try_parse_from(str_slip_to_clap(serial_cmd, true, false)).unwrap(); + serial_cfg.auto_max_ports(); + assert!(serial_cfg.check().is_ok()); + vm_config.virtio_serial = Some(serial_cfg.clone()); assert!(vm_config.add_chardev(chardev_cfg).is_ok()); - let virt_console = parse_virtserialport( - &mut vm_config, - "virtconsole,chardev=test_console,id=console1,nr=1", - true, - 0, - true, - ); - assert!(virt_console.is_ok()); - - let console_cfg = virt_console.unwrap(); - assert_eq!(console_cfg.id, "console1"); - assert_eq!(console_cfg.chardev.classtype, expected_chardev); + let port_cmd = "virtconsole,chardev=test_console,id=console1,nr=0"; + let mut port_cfg = + VirtioSerialPortCfg::try_parse_from(str_slip_to_clap(port_cmd, true, false)).unwrap(); + assert!(port_cfg.auto_nr(true, 0, serial_cfg.max_ports).is_ok()); + let chardev = vm_config.chardev.remove(&port_cfg.chardev).unwrap(); + assert_eq!(port_cfg.id, "console1"); + assert_eq!(port_cfg.nr.unwrap(), 0); + assert_eq!(chardev.classtype, expected_chardev); + + // Error: VirtioSerialPortCfg.nr >= VirtioSerialInfo.max_nr_ports. + let port_cmd = "virtconsole,chardev=test_console,id=console1,nr=1"; + let mut port_cfg = + VirtioSerialPortCfg::try_parse_from(str_slip_to_clap(port_cmd, true, false)).unwrap(); + assert!(port_cfg.auto_nr(true, 0, serial_cfg.max_ports).is_err()); let mut vm_config = VmConfig::default(); - assert!( - parse_virtio_serial(&mut vm_config, "virtio-serial-device,bus=pcie.0,addr=0x1") - .is_err() - ); + let serial_cmd = "virtio-serial-device,bus=pcie.0,addr=0x1"; + let serial_cfg = + VirtioSerialInfo::try_parse_from(str_slip_to_clap(serial_cmd, true, false)).unwrap(); + assert!(serial_cfg.check().is_err()); assert!(vm_config .add_chardev("sock,id=test_console,path=/path/to/socket") .is_err()); - - let mut vm_config = VmConfig::default(); - assert!(parse_virtio_serial(&mut vm_config, "virtio-serial-device").is_ok()); - assert!(vm_config - .add_chardev("socket,id=test_console,path=/path/to/socket,server,nowait") - .is_ok()); - let virt_console = parse_virtserialport( - &mut vm_config, - "virtconsole,chardev=test_console1,id=console1,nr=1", - true, - 0, - true, - ); - // test_console1 does not exist. - assert!(virt_console.is_err()); } #[test] @@ -541,34 +493,25 @@ mod tests { fn test_pci_console_config_cmdline_parser(chardev_cfg: &str, expected_chardev: ChardevType) { let mut vm_config = VmConfig::default(); - let virtio_arg = "virtio-serial-pci,bus=pcie.0,addr=0x1.0x2"; - assert!(parse_virtio_serial(&mut vm_config, virtio_arg).is_ok()); + let serial_cmd = "virtio-serial-pci,bus=pcie.0,addr=0x1.0x2,multifunction=on"; + let mut serial_cfg = + VirtioSerialInfo::try_parse_from(str_slip_to_clap(serial_cmd, true, false)).unwrap(); + serial_cfg.auto_max_ports(); + assert!(serial_cfg.check().is_ok()); + vm_config.virtio_serial = Some(serial_cfg.clone()); assert!(vm_config.add_chardev(chardev_cfg).is_ok()); - let virt_console = parse_virtserialport( - &mut vm_config, - "virtconsole,chardev=test_console,id=console1,nr=1", - true, - 0, - true, - ); - assert!(virt_console.is_ok()); - let console_cfg = virt_console.unwrap(); - + let console_cmd = "virtconsole,chardev=test_console,id=console1,nr=1"; + let mut console_cfg = + VirtioSerialPortCfg::try_parse_from(str_slip_to_clap(console_cmd, true, false)) + .unwrap(); + assert!(console_cfg.auto_nr(true, 0, serial_cfg.max_ports).is_ok()); + let chardev = vm_config.chardev.remove(&console_cfg.chardev).unwrap(); assert_eq!(console_cfg.id, "console1"); let serial_info = vm_config.virtio_serial.clone().unwrap(); - assert!(serial_info.pci_bdf.is_some()); - let bdf = serial_info.pci_bdf.unwrap(); - assert_eq!(bdf.bus, "pcie.0"); - assert_eq!(bdf.addr, (1, 2)); - assert_eq!(console_cfg.chardev.classtype, expected_chardev); - - let mut vm_config = VmConfig::default(); - assert!(parse_virtio_serial( - &mut vm_config, - "virtio-serial-pci,bus=pcie.0,addr=0x1.0x2,multifunction=on" - ) - .is_ok()); + assert_eq!(serial_info.bus.unwrap(), "pcie.0"); + assert_eq!(serial_info.addr.unwrap(), (1, 2)); + assert_eq!(chardev.classtype, expected_chardev); } #[test] diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 57955619a..3968821ce 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -20,6 +20,7 @@ use std::{cmp, usize}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, info, warn}; +use machine_manager::config::ChardevConfig; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -31,7 +32,7 @@ use crate::{ use address_space::AddressSpace; use chardev_backend::chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; use machine_manager::{ - config::{ChardevType, VirtioSerialInfo, VirtioSerialPort, DEFAULT_VIRTQUEUE_SIZE}, + config::{ChardevType, VirtioSerialInfo, VirtioSerialPortCfg, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, event_loop::{register_event_helper, unregister_event_helper}, }; @@ -354,20 +355,21 @@ pub struct SerialPort { } impl SerialPort { - pub fn new(port_cfg: VirtioSerialPort) -> Self { + pub fn new(port_cfg: VirtioSerialPortCfg, chardev_cfg: ChardevConfig) -> Self { // Console is default host connected. And pty chardev has opened by default in realize() // function. - let mut host_connected = port_cfg.is_console; - if let ChardevType::Pty { .. } = port_cfg.chardev.classtype { + let is_console = matches!(port_cfg.classtype.as_str(), "virtconsole"); + let mut host_connected = is_console; + if let ChardevType::Pty { .. } = chardev_cfg.classtype { host_connected = true; } SerialPort { name: Some(port_cfg.id), paused: false, - chardev: Arc::new(Mutex::new(Chardev::new(port_cfg.chardev))), - nr: port_cfg.nr, - is_console: port_cfg.is_console, + chardev: Arc::new(Mutex::new(Chardev::new(chardev_cfg))), + nr: port_cfg.nr.unwrap(), + is_console, guest_connected: false, host_connected, ctrl_handler: None, @@ -1023,18 +1025,15 @@ impl ChardevNotifyDevice for SerialPort { mod tests { pub use super::*; - use machine_manager::config::PciBdf; - #[test] fn test_set_driver_features() { let mut serial = Serial::new(VirtioSerialInfo { + classtype: "virtio-serial-pci".to_string(), id: "serial".to_string(), - pci_bdf: Some(PciBdf { - bus: "pcie.0".to_string(), - addr: (0, 0), - }), - multifunction: false, + multifunction: Some(false), max_ports: 31, + bus: Some("pcie.0".to_string()), + addr: Some((0, 0)), }); // If the device feature is 0, all driver features are not supported. @@ -1097,13 +1096,12 @@ mod tests { fn test_read_config() { let max_ports: u8 = 31; let serial = Serial::new(VirtioSerialInfo { + classtype: "virtio-serial-pci".to_string(), id: "serial".to_string(), - pci_bdf: Some(PciBdf { - bus: "pcie.0".to_string(), - addr: (0, 0), - }), - multifunction: false, + multifunction: Some(false), max_ports: max_ports as u32, + bus: Some("pcie.0".to_string()), + addr: Some((0, 0)), }); // The offset of configuration that needs to be read exceeds the maximum. -- Gitee From 3833656b72588c1ff172fa61f3e54e2da6069b46 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 22 Apr 2024 03:56:29 +0800 Subject: [PATCH 1786/2187] vhost-vsock: use clap to parse the parameters of the vhost-vsock config Use clap to parse the parameters of the vhost-vsock config. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 18 +++--- machine_manager/src/config/chardev.rs | 85 +-------------------------- virtio/src/vhost/kernel/mod.rs | 2 +- virtio/src/vhost/kernel/vsock.rs | 50 ++++++++++++++-- 4 files changed, 58 insertions(+), 97 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5888f684b..a068fdbe2 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -73,12 +73,11 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ - complete_numa_node, get_chardev_socket_path, get_multi_function, get_pci_bdf, parse_device_id, - parse_device_type, parse_numa_distance, parse_numa_mem, parse_vsock, str_slip_to_clap, - BootIndexInfo, BootSource, ConfigCheck, DriveConfig, DriveFile, Incoming, MachineMemConfig, - MigrateMode, NetworkInterfaceConfig, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, - SerialConfig, VirtioSerialInfo, VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, - MAX_VIRTIO_QUEUE, + complete_numa_node, get_chardev_socket_path, get_pci_bdf, parse_device_id, parse_device_type, + parse_numa_distance, parse_numa_mem, str_slip_to_clap, BootIndexInfo, BootSource, ConfigCheck, + DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NetworkInterfaceConfig, + NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VirtioSerialInfo, + VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -566,7 +565,8 @@ pub trait MachineOps { /// /// * `cfg_args` - Device configuration. fn add_virtio_vsock(&mut self, cfg_args: &str) -> Result<()> { - let device_cfg = parse_vsock(cfg_args)?; + let device_cfg = + VhostKern::VsockConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let sys_mem = self.get_sys_mem().clone(); let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); match parse_device_type(cfg_args)?.as_str() { @@ -580,8 +580,8 @@ pub trait MachineOps { ); } _ => { - let bdf = get_pci_bdf(cfg_args)?; - let multi_func = get_multi_function(cfg_args)?; + let bdf = PciBdf::new(device_cfg.bus.clone().unwrap(), device_cfg.addr.unwrap()); + let multi_func = device_cfg.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&device_cfg.id, &bdf, vsock.clone(), multi_func, true) .with_context(|| "Failed to add virtio pci vsock device")?; } diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 124a6288d..73c3033a7 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -18,16 +18,11 @@ use clap::{ArgAction, Parser, Subcommand}; use log::error; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, pci_args_check, str_slip_to_clap}; +use super::{error::ConfigError, str_slip_to_clap}; use super::{get_pci_df, parse_bool}; -use crate::config::{ - check_arg_too_long, valid_id, valid_path, valid_socket_path, CmdParser, ConfigCheck, VmConfig, -}; +use crate::config::{valid_id, valid_path, valid_socket_path, ConfigCheck, VmConfig}; use crate::qmp::qmp_schema; -const MAX_GUEST_CID: u64 = 4_294_967_295; -const MIN_GUEST_CID: u64 = 3; - /// Default value of max ports for virtio-serial. const DEFAULT_SERIAL_PORTS_NUMBER: u32 = 31; @@ -325,61 +320,6 @@ impl VmConfig { } } -/// Config structure for virtio-vsock. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct VsockConfig { - pub id: String, - pub guest_cid: u64, - pub vhost_fd: Option, -} - -impl ConfigCheck for VsockConfig { - fn check(&self) -> Result<()> { - check_arg_too_long(&self.id, "vsock id")?; - - if self.guest_cid < MIN_GUEST_CID || self.guest_cid >= MAX_GUEST_CID { - return Err(anyhow!(ConfigError::IllegalValue( - "Vsock guest-cid".to_string(), - MIN_GUEST_CID, - true, - MAX_GUEST_CID, - false, - ))); - } - - Ok(()) - } -} - -pub fn parse_vsock(vsock_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("vhost-vsock"); - cmd_parser - .push("") - .push("id") - .push("bus") - .push("addr") - .push("multifunction") - .push("guest-cid") - .push("vhostfd"); - cmd_parser.parse(vsock_config)?; - pci_args_check(&cmd_parser)?; - let id = cmd_parser - .get_value::("id")? - .with_context(|| ConfigError::FieldIsMissing("id".to_string(), "vsock".to_string()))?; - - let guest_cid = cmd_parser.get_value::("guest-cid")?.with_context(|| { - ConfigError::FieldIsMissing("guest-cid".to_string(), "vsock".to_string()) - })?; - - let vhost_fd = cmd_parser.get_value::("vhostfd")?; - let vsock = VsockConfig { - id, - guest_cid, - vhost_fd, - }; - Ok(vsock) -} - #[derive(Parser, Clone, Debug, Serialize, Deserialize)] #[command(no_binary_name(true))] pub struct VirtioSerialInfo { @@ -542,27 +482,6 @@ mod tests { test_pci_console_config_cmdline_parser(chardev_cfg, expected_chardev) } - #[test] - fn test_vsock_config_cmdline_parser() { - let vsock_cfg_op = parse_vsock("vhost-vsock-device,id=test_vsock,guest-cid=3"); - assert!(vsock_cfg_op.is_ok()); - - let vsock_config = vsock_cfg_op.unwrap(); - assert_eq!(vsock_config.id, "test_vsock"); - assert_eq!(vsock_config.guest_cid, 3); - assert_eq!(vsock_config.vhost_fd, None); - assert!(vsock_config.check().is_ok()); - - let vsock_cfg_op = parse_vsock("vhost-vsock-device,id=test_vsock,guest-cid=3,vhostfd=4"); - assert!(vsock_cfg_op.is_ok()); - - let vsock_config = vsock_cfg_op.unwrap(); - assert_eq!(vsock_config.id, "test_vsock"); - assert_eq!(vsock_config.guest_cid, 3); - assert_eq!(vsock_config.vhost_fd, Some(4)); - assert!(vsock_config.check().is_ok()); - } - #[test] fn test_chardev_config_cmdline_parser() { let check_argument = |arg: String, expect: ChardevType| { diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 02d55cadb..2111e2bbb 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -14,7 +14,7 @@ mod net; mod vsock; pub use net::Net; -pub use vsock::{Vsock, VsockState}; +pub use vsock::{Vsock, VsockConfig, VsockState}; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 8d782c623..89bcd112f 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -16,6 +16,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; +use clap::{ArgAction, Parser}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::ioctl::ioctl_with_ref; @@ -26,7 +27,7 @@ use crate::{ VirtioInterruptType, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_TYPE_VSOCK, }; use address_space::AddressSpace; -use machine_manager::config::{VsockConfig, DEFAULT_VIRTQUEUE_SIZE}; +use machine_manager::config::{get_pci_df, parse_bool, valid_id, DEFAULT_VIRTQUEUE_SIZE}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; @@ -40,6 +41,29 @@ const VHOST_PATH: &str = "/dev/vhost-vsock"; /// Event transport reset const VIRTIO_VSOCK_EVENT_TRANSPORT_RESET: u32 = 0; +const MAX_GUEST_CID: u64 = 4_294_967_295; +const MIN_GUEST_CID: u64 = 3; + +/// Config structure for virtio-vsock. +#[derive(Parser, Debug, Clone, Default)] +#[command(no_binary_name(true))] +pub struct VsockConfig { + #[arg(long, value_parser = ["vhost-vsock-pci", "vhost-vsock-device"])] + pub classtype: String, + #[arg(long, value_parser = valid_id)] + pub id: String, + #[arg(long)] + pub bus: Option, + #[arg(long, value_parser = get_pci_df)] + pub addr: Option<(u8, u8)>, + #[arg(long, value_parser = parse_bool, action = ArgAction::Append)] + pub multifunction: Option, + #[arg(long, alias = "guest-cid", value_parser = clap::value_parser!(u64).range(MIN_GUEST_CID..=MAX_GUEST_CID))] + pub guest_cid: u64, + #[arg(long, alias = "vhostfd")] + pub vhost_fd: Option, +} + trait VhostVsockBackend { /// Each guest should have an unique CID which is used to route data to the guest. fn set_guest_cid(&self, cid: u64) -> Result<()>; @@ -390,9 +414,9 @@ impl MigrationHook for Vsock { #[cfg(test)] mod tests { - pub use super::super::*; - pub use super::*; - pub use address_space::*; + use super::*; + use address_space::*; + use machine_manager::config::str_slip_to_clap; fn vsock_address_space_init() -> Arc { let root = Region::init_container_region(u64::max_value(), "sysmem"); @@ -405,12 +429,30 @@ mod tests { id: "test_vsock_1".to_string(), guest_cid: 3, vhost_fd: None, + ..Default::default() }; let sys_mem = vsock_address_space_init(); let vsock = Vsock::new(&vsock_conf, &sys_mem); vsock } + #[test] + fn test_vsock_config_cmdline_parser() { + let vsock_cmd = "vhost-vsock-device,id=test_vsock,guest-cid=3"; + let vsock_config = + VsockConfig::try_parse_from(str_slip_to_clap(vsock_cmd, true, false)).unwrap(); + assert_eq!(vsock_config.id, "test_vsock"); + assert_eq!(vsock_config.guest_cid, 3); + assert_eq!(vsock_config.vhost_fd, None); + + let vsock_cmd = "vhost-vsock-device,id=test_vsock,guest-cid=3,vhostfd=4"; + let vsock_config = + VsockConfig::try_parse_from(str_slip_to_clap(vsock_cmd, true, false)).unwrap(); + assert_eq!(vsock_config.id, "test_vsock"); + assert_eq!(vsock_config.guest_cid, 3); + assert_eq!(vsock_config.vhost_fd, Some(4)); + } + #[test] fn test_vsock_init() { // test vsock new method -- Gitee From 0436a1f7170041e08d50287d205323eb284084ab Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 24 Apr 2024 06:23:28 +0800 Subject: [PATCH 1787/2187] vnc: use clap to parse the parameters of the vnc config Use clap to parse the parameters of the vnc config. Signed-off-by: liuxiangdong --- machine_manager/src/config/vnc.rs | 62 ++++++++----------------------- ui/src/vnc/mod.rs | 2 +- 2 files changed, 17 insertions(+), 47 deletions(-) diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index b243d9454..ca1fd104e 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -13,22 +13,26 @@ use std::net::Ipv4Addr; use anyhow::{anyhow, Context, Result}; +use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; -use crate::config::{CmdParser, ConfigError, VmConfig}; +use crate::config::{str_slip_to_clap, ConfigError, VmConfig}; /// Configuration of vnc. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct VncConfig { - /// Listening ip. - pub ip: String, - /// Listening port. - pub port: String, + /// Vnc listening addr (ip, port). + #[arg(long, alias = "classtype", value_parser = parse_ip_port)] + pub addr: (String, u16), /// Configuration of encryption. + #[arg(long, alias = "tls-creds", default_value = "")] pub tls_creds: String, /// Authentication switch. + #[arg(long, default_value = "false", action = ArgAction::SetTrue)] pub sasl: bool, /// Configuration of authentication. + #[arg(long, alias = "sasl-authz", default_value = "")] pub sasl_authz: String, } @@ -38,45 +42,13 @@ const VNC_PORT_OFFSET: i32 = 5900; impl VmConfig { /// Make configuration for vnc: "chardev" -> "vnc". pub fn add_vnc(&mut self, vnc_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("vnc"); - cmd_parser - .push("") - .push("tls-creds") - .push("sasl") - .push("sasl-authz"); - cmd_parser.parse(vnc_config)?; - - let mut vnc_config = VncConfig::default(); - // Parse Ip:Port. - if let Some(addr) = cmd_parser.get_value::("")? { - parse_port(&mut vnc_config, addr)?; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "ip".to_string(), - "port".to_string() - ))); - } - - // VNC Security Type. - if let Some(tls_creds) = cmd_parser.get_value::("tls-creds")? { - vnc_config.tls_creds = tls_creds - } - if let Some(_sasl) = cmd_parser.get_value::("sasl")? { - vnc_config.sasl = true - } else { - vnc_config.sasl = false - } - if let Some(sasl_authz) = cmd_parser.get_value::("sasl-authz")? { - vnc_config.sasl_authz = sasl_authz; - } - + let vnc_config = VncConfig::try_parse_from(str_slip_to_clap(vnc_config, true, false))?; self.vnc = Some(vnc_config); Ok(()) } } -/// Parse Ip:port. -fn parse_port(vnc_config: &mut VncConfig, addr: String) -> Result<()> { +fn parse_ip_port(addr: &str) -> Result<(String, u16)> { let v: Vec<&str> = addr.split(':').collect(); if v.len() != 2 { return Err(anyhow!(ConfigError::FieldIsMissing( @@ -97,10 +69,8 @@ fn parse_port(vnc_config: &mut VncConfig, addr: String) -> Result<()> { "port".to_string() ))); } - vnc_config.ip = ip.to_string(); - vnc_config.port = ((base_port + VNC_PORT_OFFSET) as u16).to_string(); - Ok(()) + Ok((ip.to_string(), (base_port + VNC_PORT_OFFSET) as u16)) } #[cfg(test)] @@ -113,8 +83,8 @@ mod tests { let config_line = "0.0.0.0:1,tls-creds=vnc-tls-creds0,sasl,sasl-authz=authz0"; assert!(vm_config.add_vnc(config_line).is_ok()); let vnc_config = vm_config.vnc.unwrap(); - assert_eq!(vnc_config.ip, String::from("0.0.0.0")); - assert_eq!(vnc_config.port, String::from("5901")); + assert_eq!(vnc_config.addr.0, String::from("0.0.0.0")); + assert_eq!(vnc_config.addr.1, 5901); assert_eq!(vnc_config.tls_creds, String::from("vnc-tls-creds0")); assert_eq!(vnc_config.sasl, true); assert_eq!(vnc_config.sasl_authz, String::from("authz0")); @@ -124,7 +94,7 @@ mod tests { assert!(vm_config.add_vnc(config_line).is_ok()); let vnc_config = vm_config.vnc.unwrap(); assert_eq!(vnc_config.sasl, false); - assert_eq!(vnc_config.port, String::from("11800")); + assert_eq!(vnc_config.addr.1, 11800); let mut vm_config = VmConfig::default(); let config_line = "0.0.0.0:1,sasl,sasl-authz=authz0"; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index 9f0a7e93d..ff2e48c91 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -290,7 +290,7 @@ pub fn vnc_init(vnc: &Option, object: &ObjectConfig) -> Result<()> { None => return Ok(()), }; - let addr = format!("{}:{}", vnc_cfg.ip, vnc_cfg.port); + let addr = format!("{}:{}", vnc_cfg.addr.0, vnc_cfg.addr.1); let listener: TcpListener = match TcpListener::bind(addr.as_str()) { Ok(l) => l, Err(e) => { -- Gitee From 2418358927270c0be1dd621bfc2d16a860397197 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 25 Apr 2024 13:06:19 +0800 Subject: [PATCH 1788/2187] machine_manager: add check_arg_exist and check_arg_nonexist macro Add check_arg_exist/check_arg_nonexist macro to make sure args are existed/not existed. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 42 ++++++++++++++++++++++----- machine/src/micro_common/mod.rs | 12 +++++++- machine_manager/src/config/chardev.rs | 24 --------------- machine_manager/src/config/mod.rs | 28 ++++++++++++++++-- 4 files changed, 71 insertions(+), 35 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a068fdbe2..63fd72057 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -81,6 +81,7 @@ use machine_manager::config::{ }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; +use machine_manager::{check_arg_exist, check_arg_nonexist}; use migration::{MigrateOps, MigrationManager}; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; @@ -571,6 +572,11 @@ pub trait MachineOps { let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); match parse_device_type(cfg_args)?.as_str() { "vhost-vsock-device" => { + check_arg_nonexist!( + ("bus", device_cfg.bus), + ("addr", device_cfg.addr), + ("multifunction", device_cfg.multifunction) + ); let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); MigrationManager::register_device_instance( VirtioMmioState::descriptor(), @@ -580,6 +586,7 @@ pub trait MachineOps { ); } _ => { + check_arg_exist!(("bus", device_cfg.bus), ("addr", device_cfg.addr)); let bdf = PciBdf::new(device_cfg.bus.clone().unwrap(), device_cfg.addr.unwrap()); let multi_func = device_cfg.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&device_cfg.id, &bdf, vsock.clone(), multi_func, true) @@ -667,16 +674,16 @@ pub trait MachineOps { Balloon::object_init(balloon.clone()); match config.classtype.as_str() { "virtio-balloon-device" => { - if config.addr.is_some() || config.bus.is_some() || config.multifunction.is_some() { - bail!("virtio balloon device config is error!"); - } + check_arg_nonexist!( + ("bus", config.bus), + ("addr", config.addr), + ("multifunction", config.multifunction) + ); let device = VirtioMmioDevice::new(sys_mem, balloon); self.realize_virtio_mmio_device(device)?; } _ => { - if config.addr.is_none() || config.bus.is_none() { - bail!("virtio balloon pci config is error!"); - } + check_arg_exist!(("bus", config.bus), ("addr", config.addr)); let bdf = PciBdf::new(config.bus.unwrap(), config.addr.unwrap()); let multi_func = config.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&config.id, &bdf, balloon, multi_func, false) @@ -700,12 +707,16 @@ pub trait MachineOps { let mut serial_cfg = VirtioSerialInfo::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; serial_cfg.auto_max_ports(); - serial_cfg.check()?; let sys_mem = self.get_sys_mem().clone(); let serial = Arc::new(Mutex::new(Serial::new(serial_cfg.clone()))); match serial_cfg.classtype.as_str() { "virtio-serial-device" => { + check_arg_nonexist!( + ("bus", serial_cfg.bus), + ("addr", serial_cfg.addr), + ("multifunction", serial_cfg.multifunction) + ); let device = VirtioMmioDevice::new(&sys_mem, serial.clone()); MigrationManager::register_device_instance( VirtioMmioState::descriptor(), @@ -715,6 +726,7 @@ pub trait MachineOps { ); } _ => { + check_arg_exist!(("bus", serial_cfg.bus), ("addr", serial_cfg.addr)); let bdf = PciBdf::new(serial_cfg.bus.clone().unwrap(), serial_cfg.addr.unwrap()); let multi_func = serial_cfg.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&serial_cfg.id, &bdf, serial.clone(), multi_func, false) @@ -836,11 +848,17 @@ pub trait MachineOps { match rng_cfg.classtype.as_str() { "virtio-rng-device" => { + check_arg_nonexist!( + ("bus", rng_cfg.bus), + ("addr", rng_cfg.addr), + ("multifunction", rng_cfg.multifunction) + ); let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); self.realize_virtio_mmio_device(device) .with_context(|| "Failed to add virtio mmio rng device")?; } _ => { + check_arg_exist!(("bus", rng_cfg.bus), ("addr", rng_cfg.addr)); let bdf = PciBdf::new(rng_cfg.bus.clone().unwrap(), rng_cfg.addr.unwrap()); let multi_func = rng_cfg.multifunction.unwrap_or_default(); self.add_virtio_pci_device(&rng_cfg.id, &bdf, rng_dev.clone(), multi_func, false) @@ -882,11 +900,17 @@ pub trait MachineOps { ))); match dev_cfg.classtype.as_str() { "vhost-user-fs-device" => { + check_arg_nonexist!( + ("bus", dev_cfg.bus), + ("addr", dev_cfg.addr), + ("multifunction", dev_cfg.multifunction) + ); let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device); self.realize_virtio_mmio_device(virtio_mmio_device) .with_context(|| "Failed to add vhost user fs device")?; } _ => { + check_arg_exist!(("bus", dev_cfg.bus), ("addr", dev_cfg.addr)); let bdf = PciBdf::new(dev_cfg.bus.clone().unwrap(), dev_cfg.addr.unwrap()); let multi_func = dev_cfg.multifunction.unwrap_or_default(); let root_bus = self.get_pci_host()?.lock().unwrap().root_bus.clone(); @@ -1068,6 +1092,7 @@ pub trait MachineOps { ) -> Result<()> { let mut device_cfg = VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + check_arg_exist!(("bus", device_cfg.bus), ("addr", device_cfg.addr)); let bdf = PciBdf::new(device_cfg.bus.clone().unwrap(), device_cfg.addr.unwrap()); let multi_func = device_cfg.multifunction.unwrap_or_default(); if device_cfg.num_queues.is_none() { @@ -1225,6 +1250,7 @@ pub trait MachineOps { .netdevs .remove(&net_cfg.netdev) .with_context(|| format!("Netdev: {:?} not found for net device", &net_cfg.netdev))?; + check_arg_exist!(("bus", net_cfg.bus), ("addr", net_cfg.addr)); let bdf = PciBdf::new(net_cfg.bus.clone().unwrap(), net_cfg.addr.unwrap()); let multi_func = net_cfg.multifunction.unwrap_or_default(); @@ -1278,6 +1304,7 @@ pub trait MachineOps { let mut device_cfg = VhostUser::VhostUserBlkDevConfig::try_parse_from(str_slip_to_clap( cfg_args, true, false, ))?; + check_arg_exist!(("bus", device_cfg.bus), ("addr", device_cfg.addr)); let bdf = PciBdf::new(device_cfg.bus.clone().unwrap(), device_cfg.addr.unwrap()); if device_cfg.num_queues.is_none() { let queues_auto = VirtioPciDevice::virtio_pci_auto_queues_num( @@ -1329,6 +1356,7 @@ pub trait MachineOps { let device_cfg = VhostUser::VhostUserBlkDevConfig::try_parse_from(str_slip_to_clap( cfg_args, true, false, ))?; + check_arg_nonexist!(("bus", device_cfg.bus), ("addr", device_cfg.addr)); let chardev_cfg = vm_config .chardev .remove(&device_cfg.chardev) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index ba58236b6..b4051b46e 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -52,7 +52,6 @@ use machine_manager::config::{ get_chardev_socket_path, parse_incoming_uri, str_slip_to_clap, ConfigCheck, DriveConfig, MigrateMode, NetDevcfg, NetworkInterfaceConfig, VmConfig, }; -use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ DeviceInterface, MachineAddressInterface, MachineExternalInterface, MachineInterface, @@ -61,6 +60,7 @@ use machine_manager::machine::{ use machine_manager::qmp::{ qmp_channel::QmpChannel, qmp_response::Response, qmp_schema, qmp_schema::UpdateRegionArgument, }; +use machine_manager::{check_arg_nonexist, event}; use migration::MigrationManager; use util::{loop_context::EventLoopManager, num_ops::str_to_num, set_termi_canon_mode}; use virtio::device::block::VirtioBlkDevConfig; @@ -399,6 +399,11 @@ impl LightMachine { ) -> Result<()> { let net_cfg = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + check_arg_nonexist!( + ("bus", net_cfg.bus), + ("addr", net_cfg.addr), + ("multifunction", net_cfg.multifunction) + ); let netdev_cfg = vm_config .netdevs .remove(&net_cfg.netdev) @@ -452,6 +457,11 @@ impl LightMachine { ) -> Result<()> { let device_cfg = VirtioBlkDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + check_arg_nonexist!( + ("bus", device_cfg.bus), + ("addr", device_cfg.addr), + ("multifunction", device_cfg.multifunction) + ); let drive_cfg = vm_config .drives .remove(&device_cfg.drive) diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 73c3033a7..0ea4d49b8 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -346,24 +346,6 @@ impl VirtioSerialInfo { } } -impl ConfigCheck for VirtioSerialInfo { - fn check(&self) -> Result<()> { - match self.classtype.as_str() { - "virtio-serial-pci" => {} - "virtio-serial-device" => { - if self.bus.is_some() || self.addr.is_some() || self.multifunction.is_some() { - bail!("virtio mmio device should not set bus/addr/multifunction"); - } - } - _ => { - bail!("Invalid classtype."); - } - } - - Ok(()) - } -} - #[cfg(test)] mod tests { use super::*; @@ -374,7 +356,6 @@ mod tests { let mut serial_cfg = VirtioSerialInfo::try_parse_from(str_slip_to_clap(serial_cmd, true, false)).unwrap(); serial_cfg.auto_max_ports(); - assert!(serial_cfg.check().is_ok()); vm_config.virtio_serial = Some(serial_cfg.clone()); assert!(vm_config.add_chardev(chardev_cfg).is_ok()); @@ -394,10 +375,6 @@ mod tests { assert!(port_cfg.auto_nr(true, 0, serial_cfg.max_ports).is_err()); let mut vm_config = VmConfig::default(); - let serial_cmd = "virtio-serial-device,bus=pcie.0,addr=0x1"; - let serial_cfg = - VirtioSerialInfo::try_parse_from(str_slip_to_clap(serial_cmd, true, false)).unwrap(); - assert!(serial_cfg.check().is_err()); assert!(vm_config .add_chardev("sock,id=test_console,path=/path/to/socket") .is_err()); @@ -437,7 +414,6 @@ mod tests { let mut serial_cfg = VirtioSerialInfo::try_parse_from(str_slip_to_clap(serial_cmd, true, false)).unwrap(); serial_cfg.auto_max_ports(); - assert!(serial_cfg.check().is_ok()); vm_config.virtio_serial = Some(serial_cfg.clone()); assert!(vm_config.add_chardev(chardev_cfg).is_ok()); diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 4d1b2ab94..6863bac4a 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -713,10 +713,32 @@ pub fn check_path_too_long(arg: &str, name: &str) -> Result<()> { Ok(()) } -pub fn check_arg_nonexist(arg: Option, name: &str, device: &str) -> Result<()> { - arg.with_context(|| ConfigError::FieldIsMissing(name.to_string(), device.to_string()))?; +/// Make sure args are existed. +/// +/// arg_name: Name of arg. +/// arg_value: Value of arg. Should be Option<> class. +/// Eg: +/// check_arg_exist!(("id", id)); +/// check_arg_exist!(("bus", bus), ("addr", addr)); +#[macro_export] +macro_rules! check_arg_exist{ + ($(($arg_name:tt, $arg_value:expr)),*) => { + $($arg_value.clone().with_context(|| format!("Should set {}.", $arg_name))?;)* + } +} - Ok(()) +/// Make sure args are existed. +/// +/// arg_name: Name of arg. +/// arg_value: Value of arg. Should be Option<> class. +/// Eg: +/// check_arg_nonexist!(("id", id)); +/// check_arg_nonexist!(("bus", bus), ("addr", addr)); +#[macro_export] +macro_rules! check_arg_nonexist{ + ($(($arg_name:tt, $arg_value:expr)),*) => { + $($arg_value.clone().map_or(Some(0), |_| None).with_context(|| format!("Should not set {}", $arg_name))?;)* + } } /// Configure StratoVirt parameters in clap format. -- Gitee From f571aa22cd441613dd00e1f88f8034f7440b9cf8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Apr 2024 03:45:08 +0800 Subject: [PATCH 1789/2187] machine_manager: using local variable instead of `mut` input parameter Using local variable is more semantically consistent than using `mut` input parameter. Signed-off-by: liuxiangdong --- machine_manager/src/config/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 6863bac4a..ab8e687a7 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -768,9 +768,10 @@ macro_rules! check_arg_nonexist{ pub fn str_slip_to_clap( args: &str, first_pos_is_type: bool, - mut first_pos_is_subcommand: bool, + first_pos_is_subcommand: bool, ) -> Vec { - let args_str = if first_pos_is_type && !first_pos_is_subcommand { + let mut subcommand = first_pos_is_subcommand; + let args_str = if first_pos_is_type && !subcommand { format!("classtype={}", args) } else { args.to_string() @@ -783,9 +784,9 @@ pub fn str_slip_to_clap( // Command line like "key" will be converted to "--key". for (cnt, param) in key_value.iter().enumerate() { if cnt % 2 == 0 { - if first_pos_is_subcommand { + if subcommand { itr.push(param.to_string()); - first_pos_is_subcommand = false; + subcommand = false; } else { itr.push(format!("--{}", param)); } -- Gitee From 2afa65c7fde1d8b62ab7fbb5c36bbdcc1c32607c Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Thu, 11 Apr 2024 15:23:30 +0800 Subject: [PATCH 1790/2187] UI: support that sync ledstate to host on OHOS Guest OS may changes its ledstate from inner side, in which situation we should sync ledstate to host's keyboard. Signed-off-by: zhanghan64 --- ui/src/input.rs | 28 ++++++++++++++++++++++++++++ ui/src/ohui_srv/mod.rs | 10 ++++++++-- ui/src/ohui_srv/msg.rs | 6 ++++++ ui/src/ohui_srv/msg_handle.rs | 23 +++++++++++++++++++---- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/ui/src/input.rs b/ui/src/input.rs index 59f2b931f..d540c998e 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -252,6 +252,23 @@ impl KeyBoardState { #[derive(Default)] struct LedState { kbd_led: u8, + sync: Option>, +} + +pub trait SyncLedstate: Send + Sync { + fn sync_to_host(&self, state: u8) { + debug!("ledstate in guest is {}", state); + } +} + +impl LedState { + fn register_led_sync(&mut self, sync: Arc) { + self.sync = Some(sync); + } + + fn unregister_led_sync(&mut self) { + self.sync = None; + } } #[derive(Default)] @@ -328,6 +345,14 @@ impl Inputs { } } +pub fn register_led_sync(sync: Arc) { + LED_STATE.lock().unwrap().register_led_sync(sync); +} + +pub fn unregister_led_sync() { + LED_STATE.lock().unwrap().unregister_led_sync(); +} + pub fn register_keyboard(device: &str, kbd: Arc>) { INPUTS.lock().unwrap().register_kbd(device, kbd); } @@ -465,6 +490,9 @@ pub fn get_kbd_led_state() -> u8 { pub fn set_kbd_led_state(state: u8) { LED_STATE.lock().unwrap().kbd_led = state; + if let Some(sync_cb) = LED_STATE.lock().unwrap().sync.as_ref() { + sync_cb.sync_to_host(state); + } } pub fn keyboard_modifier_get(key_mod: KeyboardModifier) -> bool { diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 190794654..b5689b575 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -35,6 +35,7 @@ use crate::{ DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, DISPLAY_UPDATE_INTERVAL_DEFAULT, }, + input::{register_led_sync, unregister_led_sync}, pixman::{bytes_per_pixel, get_image_data, ref_pixman_image, unref_pixman_image}, }; use address_space::FileBackend; @@ -94,7 +95,7 @@ pub struct OhUiServer { // transfer channel via unix sock channel: Arc, // message handler - msg_handler: OhUiMsgHandler, + msg_handler: Arc, // connected or not connected: AtomicBool, // iothread processing unix socket @@ -168,7 +169,7 @@ impl OhUiServer { passthru: OnceCell::new(), surface: RwLock::new(GuestSurface::new()), channel: channel.clone(), - msg_handler: OhUiMsgHandler::new(channel), + msg_handler: Arc::new(OhUiMsgHandler::new(channel)), connected: AtomicBool::new(false), iothread: OnceCell::new(), cursorbuffer, @@ -252,6 +253,11 @@ impl OhUiServer { #[inline(always)] fn set_connect(&self, conn: bool) { self.connected.store(conn, Ordering::Relaxed); + if conn { + register_led_sync(self.msg_handler.clone()); + } else { + unregister_led_sync(); + } } fn set_iothread(&self, iothread: Option) { diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs index ed217f539..c359d71f5 100755 --- a/ui/src/ohui_srv/msg.rs +++ b/ui/src/ohui_srv/msg.rs @@ -132,6 +132,12 @@ pub struct LedstateEvent { impl ByteCode for LedstateEvent {} +impl LedstateEvent { + pub fn new(state: u32) -> Self { + LedstateEvent { state } + } +} + #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct GreetEvent { diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index f00853b57..313dbdd85 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -22,10 +22,11 @@ use crate::{ console::{get_active_console, graphic_hardware_ui_info}, input::{ self, get_kbd_led_state, input_button, input_move_abs, input_point_sync, keyboard_update, - release_all_key, trigger_key, Axis, ABS_MAX, CAPS_LOCK_LED, INPUT_BUTTON_WHEEL_DOWN, - INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, - INPUT_POINT_FORWARD, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, - KEYCODE_CAPS_LOCK, KEYCODE_NUM_LOCK, KEYCODE_SCR_LOCK, NUM_LOCK_LED, SCROLL_LOCK_LED, + release_all_key, trigger_key, Axis, SyncLedstate, ABS_MAX, CAPS_LOCK_LED, + INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, + INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, INPUT_POINT_FORWARD, INPUT_POINT_LEFT, + INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_CAPS_LOCK, KEYCODE_NUM_LOCK, + KEYCODE_SCR_LOCK, NUM_LOCK_LED, SCROLL_LOCK_LED, }, keycode::{DpyMod, KeyCode}, }; @@ -126,6 +127,20 @@ pub struct OhUiMsgHandler { writer: Mutex, } +impl SyncLedstate for OhUiMsgHandler { + fn sync_to_host(&self, state: u8) { + let body = LedstateEvent::new(state as u32); + if let Err(e) = self + .writer + .lock() + .unwrap() + .send_message(EventType::Ledstate, &body) + { + error!("sync_to_host: failed to send message with error {e}"); + } + } +} + impl OhUiMsgHandler { pub fn new(channel: Arc) -> Self { OhUiMsgHandler { -- Gitee From 3a09a0c675a36b7af6b3c773f5db0199da73e1e0 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 29 Apr 2024 10:14:44 +0800 Subject: [PATCH 1791/2187] qmp: optimize send_event() 1. Fixup the typo: unlocked -> locked. 2. Print event info before error occurs. Signed-off-by: Zhao Yi Min --- machine_manager/src/qmp/qmp_channel.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/machine_manager/src/qmp/qmp_channel.rs b/machine_manager/src/qmp/qmp_channel.rs index 42b0f441e..a0839501b 100644 --- a/machine_manager/src/qmp/qmp_channel.rs +++ b/machine_manager/src/qmp/qmp_channel.rs @@ -153,9 +153,10 @@ impl QmpChannel { pub fn send_event(event: &schema::QmpEvent) { if Self::is_connected() { let mut event_str = serde_json::to_string(&event).unwrap(); - let mut writer_unlocked = Self::inner().event_writer.write().unwrap(); - let writer = writer_unlocked.as_mut().unwrap(); + let mut writer_locked = Self::inner().event_writer.write().unwrap(); + let writer = writer_locked.as_mut().unwrap(); + info!("EVENT: --> {:?}", event); if let Err(e) = writer.flush() { error!("flush err, {:?}", e); return; @@ -163,9 +164,7 @@ impl QmpChannel { event_str.push_str("\r\n"); if let Err(e) = writer.write(event_str.as_bytes()) { error!("write err, {:?}", e); - return; } - info!("EVENT: --> {:?}", event); } } -- Gitee From d623b8fb2da3a7db4179c6e6a36a9e0c0cd23835 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Apr 2024 04:07:44 +0800 Subject: [PATCH 1792/2187] global: use clap to parse the parameters of the global config Use clap to parse the parameters of the global config. Signed-off-by: liuxiangdong --- machine_manager/src/config/mod.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ab8e687a7..51b8bf4c4 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -98,6 +98,13 @@ pub const MAX_QUEUE_SIZE_BLOCK_DEVICE: u64 = 1024; /// The bar0 size of enable_bar0 features pub const VIRTIO_GPU_ENABLE_BAR0_SIZE: u64 = 64 * M; +#[derive(Parser)] +#[command(no_binary_name(true))] +struct GlobalConfig { + #[arg(long, alias = "pcie-root-port.fast-unplug", value_parser = ["0", "1"])] + fast_unplug: Option, +} + #[derive(Clone, Default, Debug, Serialize, Deserialize)] pub struct ObjectConfig { pub rng_object: HashMap, @@ -243,24 +250,18 @@ impl VmConfig { /// /// * `global_config` - The args of global config. pub fn add_global_config(&mut self, global_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("global"); - cmd_parser.push("pcie-root-port.fast-unplug"); - cmd_parser.parse(global_config)?; + let global_config = + GlobalConfig::try_parse_from(str_slip_to_clap(global_config, false, false))?; - if let Some(fast_unplug_value) = - cmd_parser.get_value::("pcie-root-port.fast-unplug")? - { - if fast_unplug_value != FAST_UNPLUG_ON && fast_unplug_value != FAST_UNPLUG_OFF { - bail!("The value of fast-unplug is invalid: {}", fast_unplug_value); - } + if let Some(fast_unplug_value) = global_config.fast_unplug { let fast_unplug_key = String::from("pcie-root-port.fast-unplug"); - if self.global_config.get(&fast_unplug_key).is_none() { - self.global_config - .insert(fast_unplug_key, fast_unplug_value); - } else { + if self.global_config.get(&fast_unplug_key).is_some() { bail!("Global config {} has been added", fast_unplug_key); } + self.global_config + .insert(fast_unplug_key, fast_unplug_value); } + Ok(()) } -- Gitee From 3bba8fe5b24775322ed0407d0a057a0cf5e21406 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Apr 2024 16:26:09 +0800 Subject: [PATCH 1793/2187] mem-backend: use clap to parse the parameters of the mem-backend config Use clap to parse the parameters of the mem-backend config. Signed-off-by: liuxiangdong --- address_space/src/host_mmap.rs | 2 +- machine_manager/src/config/drive.rs | 12 +- machine_manager/src/config/machine_config.rs | 242 ++++++------------- machine_manager/src/config/mod.rs | 20 +- 4 files changed, 85 insertions(+), 191 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index ed2ce2e98..da850b036 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -294,7 +294,7 @@ pub fn create_default_mem(mem_config: &MachineMemConfig, thread_num: u8) -> Resu pub fn create_backend_mem(mem_config: &MemZoneConfig, thread_num: u8) -> Result { let mut f_back: Option = None; - if mem_config.memfd { + if mem_config.memfd() { let anon_fd = memfd_create( &CString::new("stratovirt_anon_mem")?, MemFdCreateFlag::empty(), diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index 3107ce757..e8e1a60ca 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -20,11 +20,8 @@ use clap::{ArgAction, Parser}; use log::error; use serde::{Deserialize, Serialize}; -use super::{error::ConfigError, M}; -use super::{valid_id, valid_path}; -use crate::config::{ - memory_unit_conversion, parse_bool, str_slip_to_clap, ConfigCheck, VmConfig, MAX_STRING_LENGTH, -}; +use super::{error::ConfigError, parse_size, valid_id, valid_path}; +use crate::config::{parse_bool, str_slip_to_clap, ConfigCheck, VmConfig, MAX_STRING_LENGTH}; use util::aio::{aio_probe, AioEngine, WriteZeroesState}; const MAX_IOPS: u64 = 1_000_000; @@ -90,11 +87,6 @@ impl ToString for DiskFormat { } } -fn parse_size(s: &str) -> Result { - let size = memory_unit_conversion(s, M).with_context(|| format!("Invalid size: {}", s))?; - Ok(size) -} - fn valid_l2_cache_size(s: &str) -> Result { let size = parse_size(s)?; if size > MAX_L2_CACHE_SIZE { diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 490ee9070..89a40471f 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -13,13 +13,12 @@ use std::str::FromStr; use anyhow::{anyhow, bail, Context, Result}; +use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; -use crate::config::{ - check_arg_too_long, check_path_too_long, CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, - MAX_NODES, -}; +use super::{parse_bool, parse_size, str_slip_to_clap, valid_id, valid_path}; +use crate::config::{CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES}; use crate::machine::HypervisorType; const DEFAULT_CPUS: u8 = 1; @@ -83,32 +82,37 @@ impl From for HostMemPolicy { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct MemZoneConfig { + #[arg(long, alias = "classtype", value_parser = ["memory-backend-ram", "memory-backend-file", "memory-backend-memfd"])] + pub mem_type: String, + #[arg(long, value_parser = valid_id)] pub id: String, + #[arg(long, value_parser = parse_size)] pub size: u64, - pub host_numa_nodes: Option>, + // Note: + // `Clap` will incorrectly assume that we're trying to get multiple arguments since we got + // a `Vec` from parser function `get_host_nodes`. Generally, we should use `Box` or a `new struct type` + // to encapsulate this `Vec`. And fortunately, there's a trick (using full qualified path of Vec) + // to avoid the new type wrapper. See: github.com/clap-rs/clap/issues/4626. + #[arg(long, alias = "host-nodes", value_parser = get_host_nodes)] + pub host_numa_nodes: Option<::std::vec::Vec>, + #[arg(long, default_value = "default", value_parser=["default", "preferred", "bind", "interleave"])] pub policy: String, + #[arg(long, value_parser = valid_path)] pub mem_path: Option, + #[arg(long, default_value = "true", value_parser = parse_bool, action = ArgAction::Append)] pub dump_guest_core: bool, + #[arg(long, default_value = "off", value_parser = parse_bool, action = ArgAction::Append)] pub share: bool, + #[arg(long, alias = "mem-prealloc", default_value = "false", value_parser = parse_bool, action = ArgAction::Append)] pub prealloc: bool, - pub memfd: bool, } -impl Default for MemZoneConfig { - fn default() -> Self { - MemZoneConfig { - id: String::new(), - size: 0, - host_numa_nodes: None, - policy: String::from("bind"), - mem_path: None, - dump_guest_core: true, - share: false, - prealloc: false, - memfd: false, - } +impl MemZoneConfig { + pub fn memfd(&self) -> bool { + self.mem_type.eq("memory-backend-memfd") } } @@ -461,149 +465,26 @@ impl VmConfig { } impl VmConfig { - fn get_mem_zone_id(&self, cmd_parser: &CmdParser) -> Result { - if let Some(id) = cmd_parser.get_value::("id")? { - check_arg_too_long(&id, "id")?; - Ok(id) - } else { - Err(anyhow!(ConfigError::FieldIsMissing( - "id".to_string(), - "memory-backend-ram".to_string() - ))) - } - } - - fn get_mem_path(&self, cmd_parser: &CmdParser) -> Result> { - if let Some(path) = cmd_parser.get_value::("mem-path")? { - check_path_too_long(&path, "mem-path")?; - return Ok(Some(path)); - } - Ok(None) - } - - fn get_mem_zone_size(&self, cmd_parser: &CmdParser) -> Result { - if let Some(mem) = cmd_parser.get_value::("size")? { - let size = memory_unit_conversion(&mem, M)?; - Ok(size) - } else { - Err(anyhow!(ConfigError::FieldIsMissing( - "size".to_string(), - "memory-backend-ram".to_string() - ))) - } - } - - fn get_mem_zone_host_nodes(&self, cmd_parser: &CmdParser) -> Result>> { - if let Some(mut host_nodes) = cmd_parser - .get_value::("host-nodes") - .with_context(|| { - ConfigError::ConvertValueFailed(String::from("u32"), "host-nodes".to_string()) - })? - .map(|v| v.0.iter().map(|e| *e as u32).collect::>()) - { - host_nodes.sort_unstable(); - if host_nodes[host_nodes.len() - 1] >= MAX_NODES { - return Err(anyhow!(ConfigError::IllegalValue( - "host_nodes".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ))); - } - Ok(Some(host_nodes)) - } else { - Ok(None) - } - } - - fn get_mem_zone_policy(&self, cmd_parser: &CmdParser) -> Result { - let policy = cmd_parser - .get_value::("policy")? - .unwrap_or_else(|| "default".to_string()); - if HostMemPolicy::from(policy.clone()) == HostMemPolicy::NotSupported { - return Err(anyhow!(ConfigError::InvalidParam( - "policy".to_string(), - policy - ))); - } - Ok(policy) - } - - fn get_mem_share(&self, cmd_parser: &CmdParser) -> Result { - let share = cmd_parser - .get_value::("share")? - .unwrap_or_else(|| "off".to_string()); - - if share.eq("on") || share.eq("off") { - Ok(share.eq("on")) - } else { - Err(anyhow!(ConfigError::InvalidParam( - "share".to_string(), - share - ))) - } - } - - fn get_mem_dump(&self, cmd_parser: &CmdParser) -> Result { - if let Some(dump_guest) = cmd_parser.get_value::("dump-guest-core")? { - return Ok(dump_guest.into()); - } - Ok(true) - } - - fn get_mem_prealloc(&self, cmd_parser: &CmdParser) -> Result { - if let Some(mem_prealloc) = cmd_parser.get_value::("mem-prealloc")? { - return Ok(mem_prealloc.into()); - } - Ok(false) - } - /// Convert memory zone cmdline to VM config /// /// # Arguments /// /// * `mem_zone` - The memory zone cmdline string. - /// * `mem_type` - The memory zone type - pub fn add_mem_zone(&mut self, mem_zone: &str, mem_type: String) -> Result { - let mut cmd_parser = CmdParser::new("mem_zone"); - cmd_parser - .push("") - .push("id") - .push("size") - .push("host-nodes") - .push("policy") - .push("share") - .push("mem-path") - .push("dump-guest-core") - .push("mem-prealloc"); - cmd_parser.parse(mem_zone)?; - - let zone_config = MemZoneConfig { - id: self.get_mem_zone_id(&cmd_parser)?, - size: self.get_mem_zone_size(&cmd_parser)?, - host_numa_nodes: self.get_mem_zone_host_nodes(&cmd_parser)?, - policy: self.get_mem_zone_policy(&cmd_parser)?, - dump_guest_core: self.get_mem_dump(&cmd_parser)?, - share: self.get_mem_share(&cmd_parser)?, - mem_path: self.get_mem_path(&cmd_parser)?, - prealloc: self.get_mem_prealloc(&cmd_parser)?, - memfd: mem_type.eq("memory-backend-memfd"), - }; + pub fn add_mem_zone(&mut self, mem_zone: &str) -> Result { + let zone_config = MemZoneConfig::try_parse_from(str_slip_to_clap(mem_zone, true, false))?; - if (zone_config.mem_path.is_none() && mem_type.eq("memory-backend-file")) - || (zone_config.mem_path.is_some() && mem_type.ne("memory-backend-file")) + if (zone_config.mem_path.is_none() && zone_config.mem_type.eq("memory-backend-file")) + || (zone_config.mem_path.is_some() && zone_config.mem_type.ne("memory-backend-file")) { - bail!("Object type: {} config path err", mem_type); + bail!("Object type: {} config path err", zone_config.mem_type); } - if self.object.mem_object.get(&zone_config.id).is_none() { - self.object - .mem_object - .insert(zone_config.id.clone(), zone_config.clone()); - } else { + if self.object.mem_object.get(&zone_config.id).is_some() { bail!("Object: {} has been added", zone_config.id); } + self.object + .mem_object + .insert(zone_config.id.clone(), zone_config.clone()); if zone_config.host_numa_nodes.is_none() { return Ok(zone_config); @@ -740,6 +621,34 @@ fn get_inner(outer: Option) -> Result { outer.with_context(|| ConfigError::IntegerOverflow("-m".to_string())) } +fn get_host_nodes(nodes: &str) -> Result> { + let mut host_nodes = IntegerList::from_str(nodes) + .with_context(|| { + ConfigError::ConvertValueFailed(String::from("u32"), "host-nodes".to_string()) + })? + .0 + .iter() + .map(|e| *e as u32) + .collect::>(); + + if host_nodes.is_empty() { + bail!("Got empty host nodes list!"); + } + + host_nodes.sort_unstable(); + if host_nodes[host_nodes.len() - 1] >= MAX_NODES { + return Err(anyhow!(ConfigError::IllegalValue( + "host_nodes".to_string(), + 0, + true, + MAX_NODES as u64, + false, + ))); + } + + Ok(host_nodes) +} + #[cfg(test)] mod tests { use super::*; @@ -1089,10 +998,7 @@ mod tests { fn test_add_mem_zone() { let mut vm_config = VmConfig::default(); let zone_config_1 = vm_config - .add_mem_zone( - "-object memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind", - String::from("memory-backend-ram"), - ) + .add_mem_zone("memory-backend-ram,size=2G,id=mem1,host-nodes=1,policy=bind") .unwrap(); assert_eq!(zone_config_1.id, "mem1"); assert_eq!(zone_config_1.size, 2147483648); @@ -1100,38 +1006,26 @@ mod tests { assert_eq!(zone_config_1.policy, "bind"); let zone_config_2 = vm_config - .add_mem_zone( - "-object memory-backend-ram,size=2G,id=mem2,host-nodes=1-2,policy=default", - String::from("memory-backend-ram"), - ) + .add_mem_zone("memory-backend-ram,size=2G,id=mem2,host-nodes=1-2,policy=default") .unwrap(); assert_eq!(zone_config_2.host_numa_nodes, Some(vec![1, 2])); let zone_config_3 = vm_config - .add_mem_zone( - "-object memory-backend-ram,size=2M,id=mem3,share=on", - String::from("memory-backend-ram"), - ) + .add_mem_zone("memory-backend-ram,size=2M,id=mem3,share=on") .unwrap(); assert_eq!(zone_config_3.size, 2 * 1024 * 1024); assert_eq!(zone_config_3.share, true); let zone_config_4 = vm_config - .add_mem_zone( - "-object memory-backend-ram,size=2M,id=mem4", - String::from("memory-backend-ram"), - ) + .add_mem_zone("memory-backend-ram,size=2M,id=mem4") .unwrap(); assert_eq!(zone_config_4.share, false); - assert_eq!(zone_config_4.memfd, false); + assert_eq!(zone_config_4.memfd(), false); let zone_config_5 = vm_config - .add_mem_zone( - "-object memory-backend-memfd,size=2M,id=mem5", - String::from("memory-backend-memfd"), - ) + .add_mem_zone("memory-backend-memfd,size=2M,id=mem5") .unwrap(); - assert_eq!(zone_config_5.memfd, true); + assert_eq!(zone_config_5.memfd(), true); } #[test] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 51b8bf4c4..da327e1f2 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -226,7 +226,7 @@ impl VmConfig { self.object.rng_object.insert(id, rng_cfg); } "memory-backend-ram" | "memory-backend-file" | "memory-backend-memfd" => { - self.add_mem_zone(object_args, device_type)?; + self.add_mem_zone(object_args)?; } #[cfg(feature = "vnc_auth")] "tls-creds-x509" => { @@ -657,7 +657,7 @@ impl FromStr for UnsignedInteger { pub struct IntegerList(pub Vec); impl FromStr for IntegerList { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { let mut integer_list = Vec::new(); @@ -669,19 +669,22 @@ impl FromStr for IntegerList { for list in lists.iter() { let items: Vec<&str> = list.split('-').collect(); if items.len() > 2 { - return Err(()); + return Err(anyhow!( + "{} parameters connected by -, should be no more than 2.", + items.len() + )); } let start = items[0] .parse::() - .map_err(|e| error!("Invalid value {}, error is {:?}", items[0], e))?; + .map_err(|e| anyhow!("Invalid value {}, error is {:?}", items[0], e))?; integer_list.push(start); if items.len() == 2 { let end = items[1] .parse::() - .map_err(|e| error!("Invalid value {}, error is {:?}", items[1], e))?; + .map_err(|e| anyhow!("Invalid value {}, error is {:?}", items[1], e))?; if start >= end { - return Err(()); + return Err(anyhow!("start {} is bigger than end {}.", start, end)); } for i in start..end { @@ -867,6 +870,11 @@ pub fn valid_block_device_virtqueue_size(s: &str) -> Result { Ok(size as u16) } +pub fn parse_size(s: &str) -> Result { + let size = memory_unit_conversion(s, M).with_context(|| format!("Invalid size: {}", s))?; + Ok(size) +} + #[cfg(test)] mod tests { use super::*; -- Gitee From f13d6478b32a42124ba97fa28297f50e391317d5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Apr 2024 16:35:16 +0800 Subject: [PATCH 1794/2187] machine_manager: delete useless function get_multi_function Delete useless function get_multi_function. Signed-off-by: liuxiangdong --- machine_manager/src/config/pci.rs | 37 ------------------------------- 1 file changed, 37 deletions(-) diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index ce53656e0..cad97decb 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -89,21 +89,6 @@ pub fn get_pci_bdf(pci_cfg: &str) -> Result { Ok(pci_bdf) } -pub fn get_multi_function(pci_cfg: &str) -> Result { - let mut cmd_parser = CmdParser::new("multifunction"); - cmd_parser.push("").push("multifunction"); - cmd_parser.get_parameters(pci_cfg)?; - - if let Some(multi_func) = cmd_parser - .get_value::("multifunction") - .with_context(|| "Failed to get multifunction parameter, please set on or off (default).")? - { - return Ok(multi_func.inner); - } - - Ok(false) -} - pub fn pci_args_check(cmd_parser: &CmdParser) -> Result<()> { let device_type = cmd_parser.get_value::("")?; let dev_type = device_type.unwrap(); @@ -183,26 +168,4 @@ mod tests { let pci_bdf = get_pci_bdf("virtio-balloon-device,addr=0x1.0x2"); assert!(pci_bdf.is_err()); } - - #[test] - fn test_get_multi_function() { - assert_eq!( - get_multi_function("virtio-balloon-device,bus=pcie.0,addr=0x1.0x2").unwrap(), - false - ); - assert_eq!( - get_multi_function("virtio-balloon-device,bus=pcie.0,addr=0x1.0x2,multifunction=on") - .unwrap(), - true - ); - assert_eq!( - get_multi_function("virtio-balloon-device,bus=pcie.0,addr=0x1.0x2,multifunction=off") - .unwrap(), - false - ); - assert!(get_multi_function( - "virtio-balloon-device,bus=pcie.0,addr=0x1.0x2,multifunction=close" - ) - .is_err()); - } } -- Gitee From a462341580fd9c5e044f0ab61bac6ec6b33b3a34 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Apr 2024 18:57:02 +0800 Subject: [PATCH 1795/2187] trace: use clap to parse the parameters of the trace config Use clap to parse the parameters of the trace config. Signed-off-by: liuxiangdong --- machine_manager/src/cmdline.rs | 4 ++-- machine_manager/src/config/mod.rs | 27 ++++++++++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index 659070188..c9f54f6e2 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -13,7 +13,7 @@ use anyhow::{bail, Context, Result}; use crate::{ - config::{parse_trace_options, ChardevType, CmdParser, MachineType, SocketType, VmConfig}, + config::{add_trace, ChardevType, CmdParser, MachineType, SocketType, VmConfig}, qmp::qmp_socket::QmpSocketPath, temp_cleaner::TempCleaner, }; @@ -587,7 +587,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_args_to_config_multi!((args.values_of("cameradev")), vm_cfg, add_camera_backend); add_args_to_config_multi!((args.values_of("smbios")), vm_cfg, add_smbios); if let Some(opt) = args.value_of("trace") { - parse_trace_options(&opt)?; + add_trace(&opt)?; } // Check the mini-set for Vm to start is ok diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index da327e1f2..0355a6b9e 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -628,15 +628,16 @@ fn enable_trace_state(path: &str) -> Result<()> { Ok(()) } -pub fn parse_trace_options(opt: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("trace"); - cmd_parser.push("file"); - cmd_parser.get_parameters(opt)?; - - let path = cmd_parser - .get_value::("file")? - .with_context(|| "trace: trace file must be set.")?; - enable_trace_state(&path)?; +#[derive(Parser)] +#[command(no_binary_name(true))] +struct TraceConfig { + #[arg(long)] + file: String, +} + +pub fn add_trace(opt: &str) -> Result<()> { + let trace_cfg = TraceConfig::try_parse_from(str_slip_to_clap(opt, false, false))?; + enable_trace_state(&trace_cfg.file)?; Ok(()) } @@ -968,10 +969,10 @@ mod tests { } #[test] - fn test_parse_trace_options() { - assert!(parse_trace_options("fil=test_trace").is_err()); - assert!(parse_trace_options("file").is_err()); - assert!(parse_trace_options("file=test_trace").is_err()); + fn test_add_trace() { + assert!(add_trace("fil=test_trace").is_err()); + assert!(add_trace("file").is_err()); + assert!(add_trace("file=test_trace").is_err()); } #[test] -- Gitee From ca838abd5227bad1d5dc54b4c7f2acf0c2c7f12c Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 30 Apr 2024 12:17:42 +0800 Subject: [PATCH 1796/2187] Camera: add new format New formats(NV12, YUY2) are supportted on OHOS, let's add them. Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 8 +++++++- util/src/ohos_binding/camera.rs | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index a520d639d..2bf3253f5 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -30,7 +30,11 @@ static OHCAM_CALLBACK: Lazy = Lazy::new(|| RwLock::new(OhCamCallBack::d // So, fps * interval / 10_000_000 == 1. const FPS_INTERVAL_TRANS: u32 = 10_000_000; const RESOLUTION_WHITELIST: [(i32, i32); 2] = [(640, 480), (1280, 720)]; -const FRAME_FORMAT_WHITELIST: [i32; 1] = [CAMERA_FORMAT_YUV420SP]; +const FRAME_FORMAT_WHITELIST: [i32; 3] = [ + CAMERA_FORMAT_YUV420SP, + CAMERA_FORMAT_YUYV422, + CAMERA_FORMAT_NV12, +]; const FPS_WHITELIST: [i32; 1] = [30]; #[derive(Default)] @@ -98,6 +102,8 @@ unsafe impl Sync for OhCameraBackend {} fn cam_fmt_from_oh(t: i32) -> Result { let fmt = match t { CAMERA_FORMAT_YUV420SP => FmtType::Nv12, + CAMERA_FORMAT_NV12 => FmtType::Nv12, + CAMERA_FORMAT_YUYV422 => FmtType::Yuy2, CAMERA_FORMAT_MJPEG => FmtType::Mjpg, _ => bail!("OHCAM: No supported type {}", t), }; diff --git a/util/src/ohos_binding/camera.rs b/util/src/ohos_binding/camera.rs index 16cb7a3e6..c481403eb 100644 --- a/util/src/ohos_binding/camera.rs +++ b/util/src/ohos_binding/camera.rs @@ -27,6 +27,8 @@ pub const CAMERA_FORMAT_YCBCR420: i32 = 2; #[allow(unused)] pub const CAMERA_FORMAT_RGB18888: i32 = 3; pub const CAMERA_FORMAT_YUV420SP: i32 = 1003; +pub const CAMERA_FORMAT_NV12: i32 = 1004; +pub const CAMERA_FORMAT_YUYV422: i32 = 1005; pub const CAMERA_FORMAT_MJPEG: i32 = 2000; #[derive(Clone)] -- Gitee From d5ef85a614584cc27b780a8816f2d369b4acb188 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 8 May 2024 14:21:54 +0800 Subject: [PATCH 1797/2187] SysBus: Optimized SysBus Debug code Reduce duplicate code in the architecture. Signed-off-by: Mingwang Li --- devices/src/sysbus/mod.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index ca039aba8..36003cd1b 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -52,26 +52,18 @@ pub struct SysBus { impl fmt::Debug for SysBus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug = f.debug_struct("SysBus"); + #[cfg(target_arch = "x86_64")] - let debug = f - .debug_struct("SysBus") - .field("sys_io", &self.sys_io) - .field("sys_mem", &self.sys_mem) - .field("free_irqs", &self.free_irqs) - .field("min_free_irq", &self.min_free_irq) - .field("mmio_region", &self.mmio_region) - .field("min_free_base", &self.min_free_base) - .finish(); - #[cfg(target_arch = "aarch64")] - let debug = f - .debug_struct("SysBus") + let debug = debug.field("sys_io", &self.sys_io); + + debug .field("sys_mem", &self.sys_mem) .field("free_irqs", &self.free_irqs) .field("min_free_irq", &self.min_free_irq) .field("mmio_region", &self.mmio_region) .field("min_free_base", &self.min_free_base) - .finish(); - debug + .finish() } } -- Gitee From edc56cc24df80cf727af14c0a92ee5fe75c23149 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 29 Apr 2024 17:24:56 +0800 Subject: [PATCH 1798/2187] Scream: Audio data flushing on OHOS Audio data is accquired by audio service on OHOS. If data is not enough for how much audio service want, we flush it to avoid data lost. Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 10 ++- devices/src/misc/scream/ohaudio.rs | 102 ++++++++++++++++++----------- util/src/ohos_binding/audio/mod.rs | 4 ++ 3 files changed, 75 insertions(+), 41 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 85b73ee67..13659f4b1 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -295,8 +295,14 @@ impl StreamData { let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let play = &header.play; - while play.fmt.fmt_generation == self.fmt.fmt_generation && self.chunk_idx != play.chunk_idx - { + loop { + if play.fmt.fmt_generation != self.fmt.fmt_generation { + break; + } + if self.chunk_idx == play.chunk_idx { + thread::sleep(time::Duration::from_micros(POLL_DELAY_US)); + continue; + } // If the difference between the currently processed chunk_idx and the chunk_idx in // the shared memory is greater than 4, the processing of the backend device is too // slow and the backward data is skipped. diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 70294107e..4ada1eb58 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -12,12 +12,12 @@ use std::os::raw::c_void; use std::sync::{ - atomic::{fence, AtomicI32, Ordering}, + atomic::{fence, AtomicBool, AtomicI32, Ordering}, Arc, Mutex, }; use std::{cmp, ptr, thread, time}; -use log::{error, warn}; +use log::error; use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, StreamData}; use util::ohos_binding::audio::*; @@ -36,12 +36,16 @@ struct StreamUnit { } const STREAM_DATA_VEC_CAPACITY: usize = 30; +const FLUSH_DELAY_THRESHOLD_MS: u64 = 100; +const FLUSH_DELAY_MS: u64 = 5; +const FLUSH_DELAY_CNT: u64 = 200; struct OhAudioRender { ctx: Option, stream_data: Arc>>, - prepared_data: u32, + data_size: AtomicI32, start: bool, + flushing: AtomicBool, } impl Default for OhAudioRender { @@ -49,23 +53,14 @@ impl Default for OhAudioRender { OhAudioRender { ctx: None, stream_data: Arc::new(Mutex::new(Vec::with_capacity(STREAM_DATA_VEC_CAPACITY))), - prepared_data: 0, + data_size: AtomicI32::new(0), start: false, + flushing: AtomicBool::new(false), } } } impl OhAudioRender { - #[inline(always)] - fn check_data_ready(&self, recv_data: &StreamData) -> bool { - let size = recv_data.fmt.size as u32 / 8; - let channels = recv_data.fmt.channels as u32; - let rate = recv_data.fmt.get_rate(); - // Wait for data of 500 ms ready. - // FIXME: the value of rate is wrong. - self.prepared_data >= (size * channels * rate / 2) - } - fn check_fmt_update(&mut self, recv_data: &StreamData) { if self.ctx.is_some() && !self.ctx.as_ref().unwrap().check_fmt( @@ -77,46 +72,62 @@ impl OhAudioRender { self.destroy(); } } + + fn flush(&mut self) { + self.flushing.store(true, Ordering::Release); + let mut cnt = 0; + while (cnt < FLUSH_DELAY_CNT) && (self.flushing.load(Ordering::Acquire)) { + thread::sleep(time::Duration::from_millis(FLUSH_DELAY_MS)); + cnt = cnt + 1; + } + // We need to wait for 100ms to ensure the audio data has + // been flushed before stop renderer. + thread::sleep(time::Duration::from_millis(FLUSH_DELAY_THRESHOLD_MS)); + let _ = self.ctx.as_ref().unwrap().flush_renderer(); + } } impl OhAudioProcess for OhAudioRender { fn init(&mut self, stream: &StreamData) -> bool { - let mut context = AudioContext::new(AudioStreamType::Render); - match context.init( - stream.fmt.size, - stream.fmt.get_rate(), - stream.fmt.channels, - AudioProcessCb::RendererCb(Some(on_write_data_cb)), - ptr::addr_of!(*self) as *mut c_void, - ) { - Ok(()) => self.ctx = Some(context), - Err(e) => { - error!("failed to create oh audio render context: {}", e); - return false; + if self.ctx.is_none() { + let mut context = AudioContext::new(AudioStreamType::Render); + match context.init( + stream.fmt.size, + stream.fmt.get_rate(), + stream.fmt.channels, + AudioProcessCb::RendererCb(Some(on_write_data_cb)), + ptr::addr_of!(*self) as *mut c_void, + ) { + Ok(()) => self.ctx = Some(context), + Err(e) => { + error!("failed to create oh audio render context: {}", e); + return false; + } } } match self.ctx.as_ref().unwrap().start() { Ok(()) => { self.start = true; - true } Err(e) => { error!("failed to start oh audio renderer: {}", e); - false } } + self.start } fn destroy(&mut self) { if self.ctx.is_some() { if self.start { + self.flush(); self.ctx.as_mut().unwrap().stop(); self.start = false; } self.ctx = None; } - self.stream_data.lock().unwrap().clear(); - self.prepared_data = 0; + let mut locked_data = self.stream_data.lock().unwrap(); + locked_data.clear(); + self.data_size.store(0, Ordering::Relaxed); } fn process(&mut self, recv_data: &StreamData) -> i32 { @@ -128,14 +139,15 @@ impl OhAudioProcess for OhAudioRender { addr: recv_data.audio_base, len: recv_data.audio_size as u64, }; - self.stream_data.lock().unwrap().push(su); + let mut locked_data = self.stream_data.lock().unwrap(); + locked_data.push(su); + self.data_size + .fetch_add(recv_data.audio_size as i32, Ordering::Relaxed); + drop(locked_data); - if !self.start { - self.prepared_data += recv_data.audio_size; - if self.check_data_ready(recv_data) && !self.init(recv_data) { - error!("failed to init oh audio"); - self.destroy(); - } + if !self.start && !self.init(recv_data) { + error!("failed to init oh audio"); + self.destroy(); } 0 } @@ -239,7 +251,11 @@ extern "C" fn on_write_data_cb( .as_mut() .unwrap_unchecked() }; - if !render.start { + + let data_size = render.data_size.load(Ordering::Relaxed); + if !render.flushing.load(Ordering::Acquire) && data_size < length { + // SAFETY: we checked len. + unsafe { ptr::write_bytes(buffer as *mut u8, 0, length as usize) }; return 0; } @@ -265,8 +281,16 @@ extern "C" fn on_write_data_cb( su.addr += len; } } + render + .data_size + .fetch_sub(length - left as i32, Ordering::Relaxed); + if left > 0 { - warn!("data in stream unit list is not enough"); + // SAFETY: we checked len. + unsafe { ptr::write_bytes(dst_addr as *mut u8, 0, left as usize) }; + } + if render.flushing.load(Ordering::Acquire) && su_list.is_empty() { + render.flushing.store(false, Ordering::Release); } 0 } diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index 7214e3295..9590dad7e 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -300,6 +300,10 @@ impl AudioContext { call_capi!(OH_AudioRenderer_Start(self.renderer)) } + pub fn flush_renderer(&self) -> Result<(), OAErr> { + call_capi!(OH_AudioRenderer_Flush(self.renderer)) + } + pub fn new(stream_type: AudioStreamType) -> Self { Self { stream_type, -- Gitee From 3df3435999802beb2ea0c4e1cfa43636c5d71dc4 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 9 May 2024 17:32:48 +0800 Subject: [PATCH 1799/2187] stratovirt-img: set permission to 0660 while creating new image While creating new image file, let's set the minimum permissions. Signed-off-by: Zhao Yi Min --- image/src/img.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/image/src/img.rs b/image/src/img.rs index 7f21220a4..0e54d0b90 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -169,6 +169,7 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { .read(true) .write(true) .custom_flags(libc::O_CREAT | libc::O_TRUNC) + .mode(0o660) .open(path.clone())?; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; -- Gitee From 45d1b6dc30c537c933feb888bdabc7be8da43e91 Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 15 Apr 2024 14:46:28 +0300 Subject: [PATCH 1800/2187] Fixes for reset of vhost-user device 1) Call the backend::deactivate_device() unconditionally from transport::deactivate_device(). Before the patch, it was called only if device activated. But device can be in half-activated state and we need to reset it again 2) Implement function reset_vhost_user(). For virtio-fs, this is a message that executes reset at virtiofsd side. 3) Pass parameter to client::reset_vhost_user() about how device should be reset: using reset_vhost_user, or by sending message to vhost-user daemon to deconfigure queues 4) Call client::reset_vhost_user() from virtio-fs to reset the device 5) the client::reset_vhost_user() must not return error immediately if communication with client failed. We must clear remembered events always Signed-off-by: Dmitry Skorodumov --- virtio/src/transport/virtio_pci.rs | 44 ++++++++++++++++-------------- virtio/src/vhost/user/block.rs | 17 ++++-------- virtio/src/vhost/user/client.rs | 34 +++++++++++++++++++---- virtio/src/vhost/user/fs.rs | 3 ++ 4 files changed, 61 insertions(+), 37 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 5fc891686..041356f9a 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -316,6 +316,8 @@ pub struct VirtioPciDevice { multi_func: bool, /// If the device need to register irqfd. need_irqfd: bool, + /// Device activation error + activate_err: bool, } impl VirtioPciDevice { @@ -344,6 +346,7 @@ impl VirtioPciDevice { interrupt_cb: None, multi_func, need_irqfd, + activate_err: false, } } @@ -428,7 +431,7 @@ impl VirtioPciDevice { Ok(write_start) } - fn activate_device(&self) -> bool { + fn activate_device(&mut self) -> bool { info!("func: activate_device, id: {:?}", &self.base.base.id); let mut locked_dev = self.device.lock().unwrap(); if locked_dev.device_activated() { @@ -502,46 +505,47 @@ impl VirtioPciDevice { self.interrupt_cb.clone().unwrap(), queue_evts, ) { - error!( - "Failed to activate device {}, error is {:?}", - self.base.base.id, e - ); + // log only first activation error + if !self.activate_err { + self.activate_err = true; + error!( + "Failed to activate device {}, error is {:?}", + self.base.base.id, e + ); + } return false; } + self.activate_err = false; locked_dev.set_device_activated(true); true } - fn deactivate_device(&self) -> bool { + fn deactivate_device(&self) { info!("func: deactivate_device, id: {:?}", &self.base.base.id); if self.need_irqfd && self.base.config.msix.is_some() { let msix = self.base.config.msix.as_ref().unwrap(); if msix.lock().unwrap().unregister_irqfd().is_err() { - return false; + warn!("unregister_irqfd failed"); } } - + // call deactivate unconditionally, since device can be + // in half initialized state let mut locked_dev = self.device.lock().unwrap(); - if locked_dev.device_activated() { - if let Err(e) = locked_dev.deactivate() { - error!( - "Failed to deactivate virtio device {}, error is {:?}", - self.base.base.id, e - ); - return false; - } - locked_dev.virtio_base_mut().reset(); + if let Err(e) = locked_dev.deactivate() { + error!( + "Failed to deactivate virtio device {}, error is {:?}", + self.base.base.id, e + ); } + locked_dev.virtio_base_mut().reset(); if let Some(intx) = &self.base.config.intx { intx.lock().unwrap().reset(); } if let Some(msix) = &self.base.config.msix { msix.lock().unwrap().clear_pending_vectors(); - } - - true + }; } /// Read data from the common config of virtio device. diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 1fe8ac8e2..25d030b77 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -232,18 +232,13 @@ impl VirtioDevice for Block { } fn deactivate(&mut self) -> Result<()> { - self.client - .as_ref() - .with_context(|| "Failed to get client when deactivating device")? - .lock() - .unwrap() - .reset_vhost_user()?; - if !self.base.deactivate_evts.is_empty() { - unregister_event_helper( - self.blk_cfg.iothread.as_ref(), - &mut self.base.deactivate_evts, - )?; + if let Some(client) = &self.client { + client.lock().unwrap().reset_vhost_user(false); } + unregister_event_helper( + self.blk_cfg.iothread.as_ref(), + &mut self.base.deactivate_evts, + )?; Ok(()) } diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 0bab67572..b32bf72cc 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -142,9 +142,9 @@ fn vhost_user_reconnect(client: &Arc>) { } if let Err(e) = locked_client.activate_vhost_user() { - error!("Failed to reactivate vhost-user net, {:?}", e); + error!("Failed to reactivate vhost-user {}, {:?}", dev_type, e); } else { - info!("Reconnecting vhost-user net succeed."); + info!("Reconnecting vhost-user {} succeed.", dev_type); } } @@ -612,7 +612,7 @@ impl VhostUserClient { Ok(()) } - pub fn reset_vhost_user(&mut self) -> Result<()> { + fn reset_queues(&mut self) -> Result<()> { for (queue_index, queue_mutex) in self.queues.iter().enumerate() { if !queue_mutex.lock().unwrap().vring.is_enabled() { continue; @@ -622,12 +622,25 @@ impl VhostUserClient { self.get_vring_base(queue_index) .with_context(|| format!("Failed to get vring base, index: {}", queue_index))?; } + Ok(()) + } + + pub fn reset_vhost_user(&mut self, reset_owner: bool) { + let dev_type = self.backend_type.to_string(); + if reset_owner { + if let Err(e) = self.reset_owner() { + warn!("Failed to reset owner for vhost-user {}: {:?}", dev_type, e); + } + } else if let Err(e) = self.reset_queues() { + warn!( + "Failed to reset queues for vhost-user {}: {:?}", + dev_type, e + ); + } self.queue_evts.clear(); self.call_events.clear(); self.queues.clear(); - - Ok(()) } pub fn add_event(client: &Arc>) -> Result<()> { @@ -1055,7 +1068,16 @@ impl VhostOps for VhostUserClient { } fn reset_owner(&self) -> Result<()> { - bail!("Does not support for resetting owner") + trace::vhost_reset_owner(); + let hdr = VhostUserMsgHdr::new(VhostUserMsgReq::ResetOwner as u32, 0, 0); + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = None; + let client = self.client.lock().unwrap(); + client + .sock + .send_msg(Some(&hdr), body_opt, payload_opt, &[]) + .with_context(|| "Failed to send msg for reset_owner")?; + Ok(()) } fn get_vring_base(&self, queue_idx: usize) -> Result { diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 3f60f68da..a1c5ed8d3 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -167,6 +167,9 @@ impl VirtioDevice for Fs { } fn deactivate(&mut self) -> Result<()> { + if let Some(client) = &self.client { + client.lock().unwrap().reset_vhost_user(true); + } unregister_event_helper(None, &mut self.base.deactivate_evts)?; Ok(()) } -- Gitee From c9473fb4985c362adfeeecf6b0ae4b9d0a625345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Sun, 7 Apr 2024 16:22:47 +0800 Subject: [PATCH 1801/2187] Introduce NV12 format for Camera MST Signed-off-by: Jiahong Li --- Cargo.lock | 1 - devices/Cargo.toml | 3 +- devices/src/camera_backend/demo.rs | 94 +++++++++++++++++++++---- devices/src/camera_backend/mod.rs | 3 - machine_manager/src/config/camera.rs | 2 - tests/mod_test/tests/usb_camera_test.rs | 17 ++++- 6 files changed, 96 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa9c10067..88025d2b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -364,7 +364,6 @@ dependencies = [ "anyhow", "block_backend", "byteorder", - "cairo-rs", "chardev_backend", "clap", "cpu", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 95ca790d1..d77504360 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -34,7 +34,6 @@ psimple = { version = "2.27", package = "libpulse-simple-binding", optional = tr alsa = { version = "0.7.0", optional = true } rusb = { version = "0.9", optional = true } libusb1-sys = { version = "0.6.4", optional = true } -cairo-rs = { version = "0.17.10", optional = true } trace = { path = "../trace" } clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } @@ -48,6 +47,6 @@ pvpanic = ["machine_manager/pvpanic"] demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] usb_camera = ["machine_manager/usb_camera"] -usb_camera_v4l2 = ["usb_camera", "dep:cairo-rs", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] +usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh", "util/usb_camera_oh"] ramfb = ["ui/console", "util/pixman"] diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index b3ff2e922..0640c7812 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -13,13 +13,10 @@ //! Demo backend for vCamera device, that helps for testing. use std::fs::read_to_string; -use std::ops::Deref; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -#[cfg(not(target_env = "ohos"))] -use cairo::{Format, ImageSurface}; use log::{debug, error, info}; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; @@ -239,20 +236,13 @@ impl ImageFrame { ImageMode::Random => RgbColor::from(self.frame_idx as u8 % 8), }; debug!("Demo Image color {:?}", color); - let mut surface = ImageSurface::create(Format::Rgb24, width as i32, height as i32)?; - let cr = cairo::Context::new(&surface)?; let (r, g, b) = get_rgb_color(&color); - cr.set_source_rgb(r as f64, g as f64, b as f64); - cr.rectangle(0.0, 0.0, width as f64, height as f64); - cr.fill()?; - cr.paint()?; - drop(cr); - let data = surface.data()?; + let data = init_img(width, height, (r, g, b)); let image = match format { FmtType::Mjpg => build_fake_mjpg(width, height), - FmtType::Yuy2 => convert_to_yuy2(data.deref(), width, height), - FmtType::Rgb565 => data.deref().to_vec(), - FmtType::Nv12 => bail!("demo device does not support NV12 now"), + FmtType::Yuy2 => convert_to_yuy2(&data, width, height), + FmtType::Rgb565 => data, + FmtType::Nv12 => convert_to_nv12(&data, width, height), }; self.frame_idx += 1; if self.frame_idx > FRAME_IDX_LIMIT { @@ -269,7 +259,12 @@ fn read_config(path: &str) -> Result { } fn build_format_list() -> Vec { - vec![build_yuy2_list(), build_mjpg_list(), build_rgb565_list()] + vec![ + build_yuy2_list(), + build_mjpg_list(), + build_rgb565_list(), + build_nv12_list(), + ] } fn build_yuy2_list() -> CameraFormatList { @@ -377,6 +372,33 @@ fn build_rgb565_list() -> CameraFormatList { } } +fn build_nv12_list() -> CameraFormatList { + CameraFormatList { + format: FmtType::Nv12, + fmt_index: 4, + frame: vec![ + CameraFrame { + width: 1280, + height: 720, + interval: INTERVALS_PER_SEC / 10, + index: 1, + }, + CameraFrame { + width: 640, + height: 480, + interval: INTERVALS_PER_SEC / 30, + index: 2, + }, + CameraFrame { + width: 480, + height: 240, + interval: INTERVALS_PER_SEC / 30, + index: 3, + }, + ], + } +} + impl CameraBackend for DemoCameraBackend { fn set_fmt(&mut self, cam_fmt: &CamBasicFmt) -> Result<()> { *self.cur_format.lock().unwrap() = *cam_fmt; @@ -502,6 +524,48 @@ fn clip(x: i32) -> u8 { } } +fn init_img(width: u32, height: u32, color: (u8, u8, u8)) -> Vec { + let len = height * width; + let (r, g, b) = color; + let mut img: Vec = Vec::with_capacity((len * 4) as usize); + for _ in 0..len { + img.push(b); + img.push(g); + img.push(r); + img.push(255); + } + img +} + +fn convert_to_nv12(source: &[u8], width: u32, height: u32) -> Vec { + let pixel = 4; + let len = height * width; + let mut img_nv12: Vec = Vec::with_capacity(len as usize); + for i in 0..len { + let idx = (i * pixel) as usize; + let (b, g, r) = ( + source[idx] as f32, + source[idx + 1] as f32, + source[idx + 2] as f32, + ); + let y = (0.299 * r + 0.587 * g + 0.114 * b) as u8; + img_nv12.push(y as u8); + } + for i in 0..(width * height / 2) { + let idx = (i * 2 * pixel) as usize; + let (b, g, r) = ( + source[idx] as f32, + source[idx + 1] as f32, + source[idx + 2] as f32, + ); + let u = (-0.147 * r - 0.289 * g + 0.436 * b + 128_f32) as u8; + let v = (0.615 * r - 0.515 * g - 0.100 * b + 128_f32) as u8; + img_nv12.push(u); + img_nv12.push(v); + } + img_nv12 +} + fn convert_to_yuy2(source: &[u8], width: u32, height: u32) -> Vec { let pixbytes = 4; let sz = width * height * 2; diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index a4e919a6a..00723e5e2 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -14,7 +14,6 @@ //! Backend devices, such as v4l2, usb, or demo device, etc., shall implement trait //! CameraBackend. -#[cfg(not(target_env = "ohos"))] pub mod demo; #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] pub mod ohcam; @@ -26,7 +25,6 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -#[cfg(not(target_env = "ohos"))] use self::demo::DemoCameraBackend; #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] use self::ohcam::OhCameraBackend; @@ -205,7 +203,6 @@ pub fn create_cam_backend( cameradev.id, cameradev.path, )?)), - #[cfg(not(target_env = "ohos"))] CamBackendType::Demo => Arc::new(Mutex::new(DemoCameraBackend::new( config.id, cameradev.path, diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index 90872b46a..a5ed0702a 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -38,7 +38,6 @@ pub enum CamBackendType { V4l2, #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] OhCamera, - #[cfg(not(target_env = "ohos"))] Demo, } @@ -51,7 +50,6 @@ impl FromStr for CamBackendType { "v4l2" => Ok(CamBackendType::V4l2), #[cfg(all(target_env = "ohos", feature = "usb_camera_oh"))] "ohcamera" => Ok(CamBackendType::OhCamera), - #[cfg(not(target_env = "ohos"))] "demo" => Ok(CamBackendType::Demo), _ => Err(anyhow!("Unknown camera backend type")), } diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 66d75983d..b519e1ca5 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -33,6 +33,7 @@ enum FmtType { Yuy2 = 0, Rgb565, Mjpg, + Nv12, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -115,8 +116,10 @@ fn format_index_to_fmt(idx: u8) -> FmtType { FmtType::Yuy2 } else if idx == 2 { FmtType::Mjpg - } else { + } else if idx == 3 { FmtType::Rgb565 + } else { + FmtType::Nv12 } } @@ -193,6 +196,16 @@ fn check_frame_data(fmt: &FmtType, data: &[u8]) { let pos = data.len() - 2; assert_eq!(data[pos..], [0xff, 0xf9]); } + FmtType::Nv12 => { + let len = data.len(); + for i in 0..(len / 2) { + assert_eq!(data[i], 76); + } + for i in 0..(len / 4) { + let idx = len / 2 + i * 2; + assert_eq!(data[idx..idx + 2], [90, 255]); + } + } } } @@ -263,6 +276,8 @@ fn test_xhci_camera_basic() { check_frame(&mut xhci, slot_id, 2, 2, 3); // Rgb check_frame(&mut xhci, slot_id, 3, 3, 3); + // Nv12 + check_frame(&mut xhci, slot_id, 4, 1, 3); test_state.borrow_mut().stop(); } -- Gitee From 8c7a3fe0a51bbe9b77e18fd869d74f128f0ea9eb Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 10 May 2024 11:40:18 +0800 Subject: [PATCH 1802/2187] Bugfix-OHUI:fix incorrect Result process It is not suitable to unwrap Result, we fix it by judging Err type. Signed-off-by: zhanghan64 --- ui/src/ohui_srv/msg_handle.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 313dbdd85..9f063f1fe 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -322,11 +322,14 @@ impl OhUiMsgHandler { pub fn send_windowinfo(&self, w: u32, h: u32) { self.state.lock().unwrap().update_window_info(w, h); let body = WindowInfoEvent::new(w, h); - self.writer + if let Err(e) = self + .writer .lock() .unwrap() .send_message(EventType::WindowInfo, &body) - .unwrap(); + { + error!("send_windowinfo: failed to send message with error {e}"); + } } pub fn handle_dirty_area(&self, x: u32, y: u32, w: u32, h: u32) { -- Gitee From 6539c40166291998348c2ece67a00b41353f330c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 10 May 2024 15:21:31 +0800 Subject: [PATCH 1803/2187] version: Update to v2.4.0 Signed-off-by: Keqian Zhu --- Cargo.lock | 44 ++++++++++++------------- Cargo.toml | 2 +- acpi/Cargo.toml | 2 +- address_space/Cargo.toml | 2 +- block_backend/Cargo.toml | 2 +- boot_loader/Cargo.toml | 2 +- chardev_backend/Cargo.toml | 2 +- cpu/Cargo.toml | 2 +- devices/Cargo.toml | 2 +- docs/cpu_hotplug.ch.md | 4 +-- docs/cpu_hotplug.md | 4 +-- hypervisor/Cargo.toml | 2 +- image/Cargo.toml | 2 +- machine/Cargo.toml | 2 +- machine_manager/Cargo.toml | 2 +- machine_manager/src/qmp/qmp_response.rs | 2 +- machine_manager/src/qmp/qmp_schema.rs | 2 +- migration/Cargo.toml | 2 +- migration/migration_derive/Cargo.toml | 2 +- ozone/Cargo.toml | 2 +- tests/mod_test/Cargo.toml | 2 +- trace/Cargo.toml | 2 +- trace/trace_generator/Cargo.toml | 2 +- ui/Cargo.toml | 2 +- util/Cargo.toml | 2 +- vfio/Cargo.toml | 2 +- virtio/Cargo.toml | 2 +- 27 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72bf20b72..f4c523c63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acpi" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -16,7 +16,7 @@ dependencies = [ [[package]] name = "address_space" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "arc-swap", @@ -154,7 +154,7 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block_backend" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "byteorder", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "boot_loader" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -251,7 +251,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chardev_backend" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "libc", @@ -338,7 +338,7 @@ dependencies = [ [[package]] name = "cpu" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "kvm-bindings", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "devices" -version = "2.3.0" +version = "2.4.0" dependencies = [ "acpi", "address_space", @@ -750,7 +750,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hypervisor" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -933,7 +933,7 @@ checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "machine" -version = "2.3.0" +version = "2.4.0" dependencies = [ "acpi", "address_space", @@ -961,7 +961,7 @@ dependencies = [ [[package]] name = "machine_manager" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "clap", @@ -1015,7 +1015,7 @@ dependencies = [ [[package]] name = "migration" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "kvm-ioctls", @@ -1031,7 +1031,7 @@ dependencies = [ [[package]] name = "migration_derive" -version = "2.3.0" +version = "2.4.0" dependencies = [ "migration", "proc-macro2", @@ -1048,7 +1048,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mod_test" -version = "2.3.0" +version = "2.4.0" dependencies = [ "acpi", "anyhow", @@ -1174,7 +1174,7 @@ checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "ozone" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "libc", @@ -1571,7 +1571,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stratovirt" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "log", @@ -1584,7 +1584,7 @@ dependencies = [ [[package]] name = "stratovirt-img" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "block_backend", @@ -1722,7 +1722,7 @@ dependencies = [ [[package]] name = "trace" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "lazy_static", @@ -1734,7 +1734,7 @@ dependencies = [ [[package]] name = "trace_generator" -version = "2.3.0" +version = "2.4.0" dependencies = [ "proc-macro2", "quote", @@ -1745,7 +1745,7 @@ dependencies = [ [[package]] name = "ui" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -1800,7 +1800,7 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "util" -version = "2.3.0" +version = "2.4.0" dependencies = [ "anyhow", "arc-swap", @@ -1848,7 +1848,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vfio" -version = "2.3.0" +version = "2.4.0" dependencies = [ "address_space", "anyhow", @@ -1875,7 +1875,7 @@ checksum = "43449b404c488f70507dca193debd4bea361fe8089869b947adc19720e464bce" [[package]] name = "virtio" -version = "2.3.0" +version = "2.4.0" dependencies = [ "acpi", "address_space", diff --git a/Cargo.toml b/Cargo.toml index 1912aa6d1..a82f9b791 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stratovirt" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" description = "a lightweight hypervisor with low memory overhead and fast booting speed" diff --git a/acpi/Cargo.toml b/acpi/Cargo.toml index b8d52d1d5..8c778ef92 100644 --- a/acpi/Cargo.toml +++ b/acpi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "acpi" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 96b4327ef..7ff40882a 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "address_space" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index 2eeeeae81..6f7c45b3d 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "block_backend" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index f73eef25b..d04c4ceaf 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "boot_loader" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/chardev_backend/Cargo.toml b/chardev_backend/Cargo.toml index 92fbdab46..83b47b337 100644 --- a/chardev_backend/Cargo.toml +++ b/chardev_backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chardev_backend" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index 5b93cb411..a665edecb 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cpu" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/devices/Cargo.toml b/devices/Cargo.toml index d77504360..e19dea9b0 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "devices" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/docs/cpu_hotplug.ch.md b/docs/cpu_hotplug.ch.md index 4de478a13..7fc1b3864 100644 --- a/docs/cpu_hotplug.ch.md +++ b/docs/cpu_hotplug.ch.md @@ -30,7 +30,7 @@ $ ./stratovirt \ ```shell $ ncat -U /path/to/api/socket -{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.4.0"},"capabilities":[]}} -> {"execute": "device_add","arguments": { "id": "device-id", "driver": "generic-x86-cpu", "cpu-id": cpuid }} <- {"return":{}} <- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} @@ -45,7 +45,7 @@ $ ncat -U /path/to/api/socket ```shell $ ncat -U /path/to/api/socket -{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.4.0"},"capabilities":[]}} -> {"execute": "device_del", "arguments": { "id": "device-id"}} <- {"return":{}} <- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} diff --git a/docs/cpu_hotplug.md b/docs/cpu_hotplug.md index 4cfdc526c..1a5f2046d 100644 --- a/docs/cpu_hotplug.md +++ b/docs/cpu_hotplug.md @@ -30,7 +30,7 @@ After the VM boot up, hotplug CPU with QMP: ```shell $ ncat -U path/to/api/socket -{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.4.0"},"capabilities":[]}} -> {"execute": "device_add","arguments": { "id": "device-id", "driver": "generic-x86-cpu", "cpu-id": cpuid }} <- {"return":{}} <- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} @@ -45,7 +45,7 @@ hotunplug CPU with QMP: ```shell $ ncat -U path/to/api/socket -{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.3.0"},"capabilities":[]}} +{"QMP":{"version":{"qemu":{"micro":1,"minor":0,"major":5},"package":"StratoVirt-2.4.0"},"capabilities":[]}} -> {"execute": "device_del", "arguments": { "id": "device-id"}} <- {"return":{}} <- {"event":"CPU_RESIZE","data":{},"timestamp":{"seconds":seconds, "microseconds":microseconds}} diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index d87911990..df70a5b23 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hypervisor" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/image/Cargo.toml b/image/Cargo.toml index d0ecafb9d..fc40a2ae8 100644 --- a/image/Cargo.toml +++ b/image/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stratovirt-img" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 3b12c1225..2f677f8c1 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "machine" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index b1869f755..29e151bf7 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "machine_manager" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/machine_manager/src/qmp/qmp_response.rs b/machine_manager/src/qmp/qmp_response.rs index 831be4f3b..54fe538a3 100644 --- a/machine_manager/src/qmp/qmp_response.rs +++ b/machine_manager/src/qmp/qmp_response.rs @@ -196,7 +196,7 @@ mod tests { "minor": 0, "major": 5 }, - "package": "StratoVirt-2.3.0" + "package": "StratoVirt-2.4.0" }, "capabilities": [] } diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 4f4f5f95c..4281624fc 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -1152,7 +1152,7 @@ generate_command_impl!(balloon, Empty); /// -> { "execute": "query-version" } /// <- { "return": { /// "version": { "qemu": { "minor": 1, "micro": 0, "major": 5 }, -/// "package": "StratoVirt-2.3.0" }, +/// "package": "StratoVirt-2.4.0" }, /// "capabilities": [] } } /// ``` #[derive(Default, Debug, Clone, Serialize, Deserialize)] diff --git a/migration/Cargo.toml b/migration/Cargo.toml index 1474dd519..e1469d9ff 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "migration" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" diff --git a/migration/migration_derive/Cargo.toml b/migration/migration_derive/Cargo.toml index d0e71a38c..beecc7b23 100644 --- a/migration/migration_derive/Cargo.toml +++ b/migration/migration_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "migration_derive" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/ozone/Cargo.toml b/ozone/Cargo.toml index 615e931d7..dafafca70 100644 --- a/ozone/Cargo.toml +++ b/ozone/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ozone" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" description = "Provides protection for stratovirt" diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 32507e99a..9ccebc058 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_test" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/trace/Cargo.toml b/trace/Cargo.toml index 7aa0d9f6e..267a35661 100644 --- a/trace/Cargo.toml +++ b/trace/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trace" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/trace/trace_generator/Cargo.toml b/trace/trace_generator/Cargo.toml index 15feb969c..646404477 100644 --- a/trace/trace_generator/Cargo.toml +++ b/trace/trace_generator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trace_generator" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 717248166..56f2b6f67 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ui" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/util/Cargo.toml b/util/Cargo.toml index 18a4eef07..95aefcdac 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "util" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index 61f20bd45..ca4a130f7 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vfio" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index f089d8534..d2890ed09 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "virtio" -version = "2.3.0" +version = "2.4.0" authors = ["Huawei StratoVirt Team"] edition = "2021" license = "Mulan PSL v2" -- Gitee From 9766516463918b85f1e0de8f5079ff79052dabf2 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 10 May 2024 16:21:01 +0800 Subject: [PATCH 1804/2187] Scream: fix format use "+=" to self increase Signed-off-by: zhanghan64 --- devices/src/misc/scream/ohaudio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 4ada1eb58..bd7d89c29 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -78,7 +78,7 @@ impl OhAudioRender { let mut cnt = 0; while (cnt < FLUSH_DELAY_CNT) && (self.flushing.load(Ordering::Acquire)) { thread::sleep(time::Duration::from_millis(FLUSH_DELAY_MS)); - cnt = cnt + 1; + cnt += 1; } // We need to wait for 100ms to ensure the audio data has // been flushed before stop renderer. -- Gitee From 0b9ff4cf7ee956f6d676a5a4fcddee55c4b372e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pengxiao=20Zhou=28=E5=91=A8=E6=9C=8B=E8=82=96=29?= Date: Fri, 10 May 2024 11:49:08 +0800 Subject: [PATCH 1805/2187] Fix bad system call, number: 56 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pengxiao Zhou(周朋肖) --- machine/src/aarch64/standard.rs | 1 - machine/src/micro_common/syscall.rs | 1 + machine/src/standard_common/syscall.rs | 1 + machine/src/x86_64/standard.rs | 2 -- 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 8544a2b79..8d38c4440 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -787,7 +787,6 @@ pub(crate) fn arch_syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_epoll_pwait), BpfRule::new(libc::SYS_mkdirat), BpfRule::new(libc::SYS_unlinkat), - BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_rt_sigaction), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_rseq), diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index fe0316413..f3acec191 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -86,6 +86,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_accept4), BpfRule::new(libc::SYS_lseek), futex_rule(), + BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_exit), BpfRule::new(libc::SYS_exit_group), BpfRule::new(libc::SYS_rt_sigreturn), diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index 0cfac2e3f..d665f314e 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -104,6 +104,7 @@ pub fn syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_accept4), BpfRule::new(libc::SYS_lseek), futex_rule(), + BpfRule::new(libc::SYS_clone), BpfRule::new(libc::SYS_exit), BpfRule::new(libc::SYS_exit_group), BpfRule::new(libc::SYS_rt_sigreturn), diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 790e542a3..6499151bb 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -762,8 +762,6 @@ pub(crate) fn arch_syscall_whitelist() -> Vec { BpfRule::new(libc::SYS_mkdir), BpfRule::new(libc::SYS_unlink), BpfRule::new(libc::SYS_readlink), - #[cfg(target_env = "musl")] - BpfRule::new(libc::SYS_clone), #[cfg(target_env = "gnu")] BpfRule::new(libc::SYS_clone3), #[cfg(target_env = "gnu")] -- Gitee From 403a9774312f1a0ff9be210ce879f12cb4621133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Tue, 14 May 2024 15:14:36 +0800 Subject: [PATCH 1806/2187] add oh ui trace Signed-off-by: Jiahong Li --- trace/trace_info/ui.toml | 54 +++++++++++++++++++++++++++++++++++ ui/src/ohui_srv/msg_handle.rs | 9 ++++++ 2 files changed, 63 insertions(+) diff --git a/trace/trace_info/ui.toml b/trace/trace_info/ui.toml index 091c05648..3c63d3289 100644 --- a/trace/trace_info/ui.toml +++ b/trace/trace_info/ui.toml @@ -219,3 +219,57 @@ name = "console_select" args = "con_id: &dyn fmt::Debug" message = "console id={:?}" enabled = true + +[[events]] +name = "oh_event_mouse_button" +args = "msg_btn: u32, action: u32" +message = "msg_btn={} action={}" +enabled = true + +[[events]] +name = "oh_event_mouse_motion" +args = "x: f64, y: f64" +message = "x={} y={}" +enabled = true + +[[events]] +name = "oh_event_keyboard" +args = "keycode: u16, key_action: u16" +message = "keycode={} key_action={}" +enabled = true + +[[events]] +name = "oh_event_windowinfo" +args = "width: u32, height: u32" +message = "width={} height={}" +enabled = true + +[[events]] +name = "oh_event_scroll" +args = "direction: u32" +message = "direction={}" +enabled = true + +[[events]] +name = "oh_event_ledstate" +args = "state: u32" +message = "state={}" +enabled = true + +[[events]] +name = "oh_event_focus" +args = "state: u32" +message = "state={}" +enabled = true + +[[events]] +name = "oh_event_greet" +args = "id: u64" +message = "token_id={}" +enabled = true + +[[events]] +name = "oh_event_unsupported_type" +args = "ty: &dyn fmt::Debug, size: u32" +message = "type={:?} body_size={}" +enabled = true diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 9f063f1fe..eeb44c15c 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -196,6 +196,7 @@ impl OhUiMsgHandler { } EventType::Focus => { let body = FocusEvent::from_bytes(&body_bytes[..]).unwrap(); + trace::oh_event_focus(body.state); if body.state == CLIENT_FOCUSOUT_EVENT { reader.clear(); release_all_key()?; @@ -209,6 +210,7 @@ impl OhUiMsgHandler { } EventType::Greet => { let body = GreetEvent::from_bytes(&body_bytes[..]).unwrap(); + trace::oh_event_greet(body.token_id); *token_id.write().unwrap() = body.token_id; Ok(()) } @@ -217,6 +219,7 @@ impl OhUiMsgHandler { "unsupported type {:?} and body size {}", event_type, body_size ); + trace::oh_event_unsupported_type(&event_type, body_size.try_into().unwrap()); Ok(()) } } { @@ -228,6 +231,7 @@ impl OhUiMsgHandler { fn handle_mouse_button(&self, mb: &MouseButtonEvent) -> Result<()> { let (msg_btn, action) = (mb.button, mb.btn_action); + trace::oh_event_mouse_button(msg_btn, action); let btn = match msg_btn { CLIENT_MOUSE_BUTTON_LEFT => INPUT_POINT_LEFT, CLIENT_MOUSE_BUTTON_RIGHT => INPUT_POINT_RIGHT, @@ -264,6 +268,7 @@ impl OhUiMsgHandler { // NOTE: we only support absolute position info now, that means usb-mouse does not work. fn handle_mouse_motion(&self, mm: &MouseMotionEvent) -> Result<()> { + trace::oh_event_mouse_motion(mm.x, mm.y); self.state.lock().unwrap().move_pointer(mm.x, mm.y) } @@ -278,6 +283,7 @@ impl OhUiMsgHandler { bail!("not supported keycode {}", hmkey); } }; + trace::oh_event_keyboard(keycode, ke.key_action); self.state .lock() .unwrap() @@ -295,6 +301,7 @@ impl OhUiMsgHandler { }; self.state.lock().unwrap().press_btn(dir)?; self.state.lock().unwrap().release_btn(dir)?; + trace::oh_event_scroll(dir); Ok(()) } @@ -310,6 +317,7 @@ impl OhUiMsgHandler { error!("handle_windowinfo failed with error {e}"); } } + trace::oh_event_windowinfo(wi.width, wi.height); } fn handle_ledstate(&self, led: &LedstateEvent) { @@ -317,6 +325,7 @@ impl OhUiMsgHandler { .lock() .unwrap() .update_host_ledstate(led.state as u8); + trace::oh_event_ledstate(led.state); } pub fn send_windowinfo(&self, w: u32, h: u32) { -- Gitee From 9cb2b5a8dc9f74d1345a43de53205a2b8762c14f Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 May 2024 19:45:45 +0800 Subject: [PATCH 1807/2187] block_backend: do not call contains_keys for set/get_refcount Do not call contains_keys() in set/get_refcount() for performance. Signed-off-by: Yan Wang --- block_backend/src/qcow2/refcount.rs | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 1a3dfdddc..bb80381d7 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -381,6 +381,22 @@ impl RefCount { self.refcount_blk_cache.flush(self.sync_aio.clone()) } + fn get_refcount_block_cache(&mut self, rt_idx: u64) -> Result>> { + let entry = self.refcount_blk_cache.get(rt_idx); + let cache_entry = if let Some(entry) = entry { + entry.clone() + } else { + self.load_refcount_block(rt_idx).with_context(|| { + format!("Failed to get refcount block cache, index is {}", rt_idx) + })?; + self.refcount_blk_cache + .get(rt_idx) + .with_context(|| format!("Not found refcount block cache, index is {}", rt_idx))? + .clone() + }; + Ok(cache_entry) + } + fn set_refcount( &mut self, rt_idx: u64, @@ -391,17 +407,9 @@ impl RefCount { ) -> Result<()> { let is_add = added > 0; let added_value = added.unsigned_abs() as u16; - if !self.refcount_blk_cache.contains_keys(rt_idx) { - self.load_refcount_block(rt_idx).with_context(|| { - format!("Failed to get refcount block cache, index is {}", rt_idx) - })?; - } let cache_entry = self - .refcount_blk_cache - .get(rt_idx) - .with_context(|| format!("Not found refcount block cache, index is {}", rt_idx))? - .clone(); - + .get_refcount_block_cache(rt_idx) + .with_context(|| "Get refcount block cache failed")?; let mut rb_vec = Vec::new(); let mut borrowed_entry = cache_entry.borrow_mut(); let is_dirty = borrowed_entry.dirty_info.is_dirty; @@ -471,17 +479,9 @@ impl RefCount { ); } - if !self.refcount_blk_cache.contains_keys(rt_idx) { - self.load_refcount_block(rt_idx).with_context(|| { - format!("Failed to get refcount block cache, index is {}", rt_idx) - })?; - } let cache_entry = self - .refcount_blk_cache - .get(rt_idx) - .with_context(|| format!("Not found refcount block cache, index is {}", rt_idx))? - .clone(); - + .get_refcount_block_cache(rt_idx) + .with_context(|| "Get refcount block cache failed")?; let rb_idx = self.cluster_in_rc_block(cluster) as usize; let rc_value = cache_entry.borrow_mut().get_entry_map(rb_idx).unwrap(); -- Gitee From 62ea5211f8952263ebb3f6c365c4d92860476caf Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 May 2024 21:08:52 +0800 Subject: [PATCH 1808/2187] block_backend: do not write zero for new data cluster There is no need to write zero for new allocated data cluster. Signed-off-by: Yan Wang --- block_backend/src/qcow2/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 2dd1e4ac5..3b4999f46 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -435,7 +435,7 @@ impl Qcow2Driver { l2_entry &= !QCOW2_OFLAG_ZERO; let mut cluster_addr = l2_entry & L2_TABLE_OFFSET_MASK; if cluster_addr == 0 { - let new_addr = self.alloc_cluster(1, true)?; + let new_addr = self.alloc_cluster(1, false)?; l2_entry = new_addr | QCOW2_OFFSET_COPIED; cluster_addr = new_addr & L2_TABLE_OFFSET_MASK; } else if l2_entry & QCOW2_OFFSET_COPIED == 0 { -- Gitee From 7226d1766590a45e2ab01e7ce7042ec60a022e14 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Thu, 9 May 2024 21:18:36 +0800 Subject: [PATCH 1809/2187] block_backend: init capacity for vec element Pre-allocate capacity for vec element Signed-off-by: Yan Wang --- block_backend/src/qcow2/refcount.rs | 4 ++-- util/src/aio/mod.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index bb80381d7..7b6804028 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -316,7 +316,7 @@ impl RefCount { bail!("Failed to update refcount, offset is not aligned to cluster"); } let first_cluster = bytes_to_clusters(offset, self.cluster_size).unwrap(); - let mut rc_vec = Vec::new(); + let mut rc_vec: Vec<(u64, u64, usize)> = Vec::with_capacity(clusters as usize); let mut i = 0; while i < clusters { let rt_idx = (first_cluster + i) >> self.refcount_blk_bits; @@ -410,7 +410,7 @@ impl RefCount { let cache_entry = self .get_refcount_block_cache(rt_idx) .with_context(|| "Get refcount block cache failed")?; - let mut rb_vec = Vec::new(); + let mut rb_vec: Vec = Vec::with_capacity(clusters); let mut borrowed_entry = cache_entry.borrow_mut(); let is_dirty = borrowed_entry.dirty_info.is_dirty; for i in 0..clusters { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index d8d733da3..3a197aae1 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -812,8 +812,9 @@ fn iovec_is_zero(iovecs: &[Iovec]) -> bool { } pub fn iovecs_split(iovecs: Vec, mut size: u64) -> (Vec, Vec) { - let mut begin = Vec::new(); - let mut end = Vec::new(); + let len = iovecs.len(); + let mut begin: Vec = Vec::with_capacity(len); + let mut end: Vec = Vec::with_capacity(len); for iov in iovecs { if size == 0 { end.push(iov); -- Gitee From 19804df7f1222a0ac3e8574a8fd1d4b52934ad90 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Mon, 13 May 2024 16:21:24 +0800 Subject: [PATCH 1810/2187] block_backend: optimize check_overlap funtion Using hashmap to optimize check_overlap function. Signed-off-by: Yan Wang --- block_backend/src/qcow2/mod.rs | 45 +++++++++++++++-------------- block_backend/src/qcow2/refcount.rs | 14 ++++++++- block_backend/src/qcow2/table.rs | 15 +++++++++- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 3b4999f46..b89d6c1b4 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -341,6 +341,13 @@ impl Qcow2Driver { .sync_aio .borrow_mut() .read_ctrl_cluster(self.header.refcount_table_offset, sz)?; + for block_offset in &self.refcount.refcount_table { + if *block_offset == 0 { + continue; + } + let rfb_offset = block_offset & REFCOUNT_TABLE_OFFSET_MASK; + self.refcount.refcount_table_map.insert(rfb_offset, 1); + } Ok(()) } @@ -865,6 +872,11 @@ impl Qcow2Driver { self.table.l1_table_offset = new_l1_table_offset; self.table.l1_size = snap.l1_size; self.table.l1_table = snap_l1_table; + self.table.l1_table_map.clear(); + for l1_entry in self.table.l1_table.iter() { + let addr = l1_entry & L1_TABLE_OFFSET_MASK; + self.table.l1_table_map.insert(addr, 1); + } self.qcow2_update_snapshot_refcount(old_l1_table_offset, old_l1_size as usize, -1)?; @@ -1291,13 +1303,14 @@ impl Qcow2Driver { return 0; } - if check & METADATA_OVERLAP_CHECK_MAINHEADER != 0 && offset < self.header.cluster_size() { + let cluster_size = self.header.cluster_size(); + if check & METADATA_OVERLAP_CHECK_MAINHEADER != 0 && offset < cluster_size { return METADATA_OVERLAP_CHECK_MAINHEADER as i64; } let size = round_up( self.refcount.offset_into_cluster(offset) + size, - self.header.cluster_size(), + cluster_size, ) .unwrap() as usize; let offset = self.refcount.start_of_cluster(offset) as usize; @@ -1321,15 +1334,10 @@ impl Qcow2Driver { } if check & METADATA_OVERLAP_CHECK_ACTIVEL2 != 0 { - for l1_entry in &self.table.l1_table { - if ranges_overlap( - offset, - size, - (l1_entry & L1_TABLE_OFFSET_MASK) as usize, - self.header.cluster_size() as usize, - ) - .unwrap() - { + let num = size as u64 / cluster_size; + for i in 0..num { + let addr = offset as u64 + i * cluster_size; + if self.table.l1_table_map.contains_key(&addr) { return METADATA_OVERLAP_CHECK_ACTIVEL2 as i64; } } @@ -1340,7 +1348,7 @@ impl Qcow2Driver { offset, size, self.header.refcount_table_offset as usize, - self.header.refcount_table_clusters as usize * self.header.cluster_size() as usize, + self.header.refcount_table_clusters as usize * cluster_size as usize, ) .unwrap() { @@ -1348,15 +1356,10 @@ impl Qcow2Driver { } if check & METADATA_OVERLAP_CHECK_REFCOUNTBLOCK != 0 { - for block_offset in &self.refcount.refcount_table { - if ranges_overlap( - offset, - size, - (block_offset & REFCOUNT_TABLE_OFFSET_MASK) as usize, - self.header.cluster_size() as usize, - ) - .unwrap() - { + let num = size as u64 / cluster_size; + for i in 0..num { + let addr = offset as u64 + i * cluster_size; + if self.refcount.refcount_table_map.contains_key(&addr) { return METADATA_OVERLAP_CHECK_REFCOUNTBLOCK as i64; } } diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 7b6804028..b58e3718b 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use anyhow::{bail, Context, Result}; use log::{error, info}; @@ -32,6 +32,9 @@ use util::{ // The max refcount table size default is 4 clusters; const MAX_REFTABLE_NUM: u64 = 4; +// Default refcount table map length, which can describe 512GiB data for 64Kib cluster. +const REFCOUNT_TABLE_MAP_LEN: usize = 256; + #[derive(Eq, PartialEq, Clone)] pub enum Qcow2DiscardType { Never, @@ -64,6 +67,7 @@ impl DiscardTask { #[derive(Clone)] pub struct RefCount { pub refcount_table: Vec, + pub refcount_table_map: HashMap, sync_aio: Rc>, pub(crate) refcount_blk_cache: Qcow2Cache, pub discard_list: Vec, @@ -87,6 +91,7 @@ impl RefCount { pub fn new(sync_aio: Rc>) -> Self { RefCount { refcount_table: Vec::new(), + refcount_table_map: HashMap::with_capacity(REFCOUNT_TABLE_MAP_LEN), sync_aio, refcount_blk_cache: Qcow2Cache::default(), discard_list: Vec::new(), @@ -217,9 +222,11 @@ impl RefCount { new_table.resize(new_table_size as usize, 0); let start_offset = start_idx * self.cluster_size; let mut table_offset = start_offset; + let mut added_rb = Vec::new(); for i in 0..new_block_clusters { if new_table[i as usize] == 0 { new_table[i as usize] = table_offset; + added_rb.push(table_offset & REFCOUNT_TABLE_OFFSET_MASK); table_offset += self.cluster_size; } } @@ -247,6 +254,9 @@ impl RefCount { let old_table_offset = self.refcount_table_offset; let old_table_clusters = self.refcount_table_clusters; self.refcount_table = new_table; + for rb_offset in added_rb.iter() { + self.refcount_table_map.insert(*rb_offset, 1); + } self.refcount_table_offset = header.refcount_table_offset; self.refcount_table_clusters = header.refcount_table_clusters; self.refcount_table_size = new_table_size; @@ -542,6 +552,8 @@ impl RefCount { // Update refcount table. self.refcount_table[rt_idx as usize] = alloc_offset; + let rb_offset = alloc_offset & REFCOUNT_TABLE_OFFSET_MASK; + self.refcount_table_map.insert(rb_offset, 1); let rc_block = vec![0_u8; self.cluster_size as usize]; let cache_entry = Rc::new(RefCell::new(CacheTable::new( alloc_offset, diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 5886becb4..6554f3fb4 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use anyhow::{Context, Result}; use log::info; @@ -28,6 +28,9 @@ use crate::{ use machine_manager::config::MAX_L2_CACHE_SIZE; use util::num_ops::div_round_up; +// Default l1 table map length, which can describe 512GiB data for 64KiB cluster. +const L1_TABLE_MAP_LEN: usize = 1024; + #[derive(PartialEq, Eq, Debug)] pub enum Qcow2ClusterType { /// Cluster is unallocated. @@ -81,6 +84,7 @@ pub struct Qcow2Table { cluster_bits: u64, cluster_size: u64, pub l1_table: Vec, + pub l1_table_map: HashMap, pub l1_table_offset: u64, pub l1_size: u32, pub l2_table_cache: Qcow2Cache, @@ -96,6 +100,7 @@ impl Qcow2Table { cluster_bits: 0, cluster_size: 0, l1_table: Vec::new(), + l1_table_map: HashMap::with_capacity(L1_TABLE_MAP_LEN), l1_table_offset: 0, l1_size: 0, l2_table_cache: Qcow2Cache::default(), @@ -143,6 +148,10 @@ impl Qcow2Table { .sync_aio .borrow_mut() .read_ctrl_cluster(self.l1_table_offset, self.l1_size as u64)?; + for l1_entry in &self.l1_table { + let l1_entry_addr = l1_entry & L1_TABLE_OFFSET_MASK; + self.l1_table_map.insert(l1_entry_addr, 1); + } Ok(()) } @@ -185,7 +194,11 @@ impl Qcow2Table { } pub fn update_l1_table(&mut self, l1_index: usize, l2_address: u64) { + let old_addr = self.l1_table[l1_index] & L1_TABLE_OFFSET_MASK; + let new_addr = l2_address & L1_TABLE_OFFSET_MASK; self.l1_table[l1_index] = l2_address; + self.l1_table_map.remove(&old_addr); + self.l1_table_map.insert(new_addr, 1); } pub fn update_l2_table( -- Gitee From 152fcb169f0f0738975e97a876f8f8a832c7d8fc Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Tue, 14 May 2024 14:49:01 +0800 Subject: [PATCH 1811/2187] block: using cache to get address map Using cache to get address map to improve the gpa->hva conversion performance. Signed-off-by: Yan Wang --- address_space/src/address_space.rs | 3 ++- devices/src/usb/xhci/xhci_controller.rs | 8 +++++-- virtio/src/device/block.rs | 32 +++++++++++++++---------- virtio/src/lib.rs | 23 +++++++++++++++--- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index ae18dd86c..f935d01b6 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -503,6 +503,7 @@ impl AddressSpace { /// * `count` - Memory needed length pub fn get_address_map( &self, + cache: &Option, addr: GuestAddress, count: u64, res: &mut Vec, @@ -512,7 +513,7 @@ impl AddressSpace { loop { let io_vec = self - .addr_cache_init(start) + .get_host_address_from_cache(start, cache) .map(|(hva, fr_len)| Iovec { iov_base: hva, iov_len: std::cmp::min(len, fr_len), diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index ba2e13852..a58877196 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1936,8 +1936,12 @@ impl XhciDevice { trb.parameter }; - self.mem_space - .get_address_map(GuestAddress(dma_addr), chunk as u64, &mut vec)?; + self.mem_space.get_address_map( + &None, + GuestAddress(dma_addr), + chunk as u64, + &mut vec, + )?; } } diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index a7719cf35..c21141213 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -25,17 +25,17 @@ use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf, - read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, - VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_BLK_F_DISCARD, - VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, - VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, - VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, - VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, + check_config_space_rw, gpa_hva_iovec_map_by_cache, iov_discard_back, iov_discard_front, + iov_to_buf_by_cache, read_config_default, report_virtio_error, virtio_has_feature, Element, + Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, + VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, + VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressSpace, GuestAddress, RegionCache}; use block_backend::{ create_block_backend, remove_block_backend, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, @@ -200,7 +200,12 @@ struct Request { } impl Request { - fn new(handler: &BlockIoHandler, elem: &mut Element, status: &mut u8) -> Result { + fn new( + handler: &BlockIoHandler, + cache: &Option, + elem: &mut Element, + status: &mut u8, + ) -> Result { if elem.out_iovec.is_empty() || elem.in_iovec.is_empty() { bail!( "Missed header for block request: out {} in {} desc num {}", @@ -211,8 +216,9 @@ impl Request { } let mut out_header = RequestOutHeader::default(); - iov_to_buf( + iov_to_buf_by_cache( &handler.mem_space, + cache, &elem.out_iovec, out_header.as_mut_bytes(), ) @@ -268,7 +274,8 @@ impl Request { } .with_context(|| "Empty data for block request")?; - let (data_len, iovec) = gpa_hva_iovec_map(data_iovec, &handler.mem_space)?; + let (data_len, iovec) = + gpa_hva_iovec_map_by_cache(data_iovec, &handler.mem_space, cache)?; request.data_len = data_len; request.iovec = iovec; } @@ -581,7 +588,8 @@ impl BlockIoHandler { // Init and put valid request into request queue. let mut status = VIRTIO_BLK_S_OK; - let req = Request::new(self, &mut elem, &mut status)?; + let cache = queue.vring.get_cache(); + let req = Request::new(self, cache, &mut elem, &mut status)?; if status != VIRTIO_BLK_S_OK { let aiocompletecb = AioCompleteCb::new( self.queue.clone(), diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9ef9dde8b..ab5481b93 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -57,7 +57,7 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; -use address_space::AddressSpace; +use address_space::{AddressSpace, RegionCache}; use machine_manager::config::ConfigCheck; use migration_derive::ByteCode; use util::aio::{mem_to_buf, Iovec}; @@ -789,12 +789,21 @@ pub fn report_virtio_error( /// Read iovec to buf and return the read number of bytes. pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) -> Result { + iov_to_buf_by_cache(mem_space, &None, iovec, buf) +} + +pub fn iov_to_buf_by_cache( + mem_space: &AddressSpace, + cache: &Option, + iovec: &[ElemIovec], + buf: &mut [u8], +) -> Result { let mut start: usize = 0; let mut end: usize = 0; for iov in iovec { let mut addr_map = Vec::new(); - mem_space.get_address_map(iov.addr, iov.len as u64, &mut addr_map)?; + mem_space.get_address_map(cache, iov.addr, iov.len as u64, &mut addr_map)?; for addr in addr_map.into_iter() { end = cmp::min(start + addr.iov_len as usize, buf.len()); mem_to_buf(&mut buf[start..end], addr.iov_base)?; @@ -838,12 +847,20 @@ pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ fn gpa_hva_iovec_map( gpa_elemiovec: &[ElemIovec], mem_space: &AddressSpace, +) -> Result<(u64, Vec)> { + gpa_hva_iovec_map_by_cache(gpa_elemiovec, mem_space, &None) +} + +fn gpa_hva_iovec_map_by_cache( + gpa_elemiovec: &[ElemIovec], + mem_space: &AddressSpace, + cache: &Option, ) -> Result<(u64, Vec)> { let mut iov_size = 0; let mut hva_iovec = Vec::with_capacity(gpa_elemiovec.len()); for elem in gpa_elemiovec.iter() { - mem_space.get_address_map(elem.addr, elem.len as u64, &mut hva_iovec)?; + mem_space.get_address_map(cache, elem.addr, elem.len as u64, &mut hva_iovec)?; iov_size += elem.len as u64; } -- Gitee From d0d447238ca41b6675c1bdde494a0c10829d5528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Wed, 15 May 2024 11:59:06 +0800 Subject: [PATCH 1812/2187] add oh scream trace Signed-off-by: Jiahong Li --- devices/src/misc/scream/ohaudio.rs | 6 +++++ trace/trace_info/misc.toml | 36 ++++++++++++++++++++++++++++++ util/src/ohos_binding/audio/mod.rs | 1 + 3 files changed, 43 insertions(+) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index bd7d89c29..f1ea10615 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -108,6 +108,7 @@ impl OhAudioProcess for OhAudioRender { match self.ctx.as_ref().unwrap().start() { Ok(()) => { self.start = true; + trace::oh_scream_render_init(&self.ctx); } Err(e) => { error!("failed to start oh audio renderer: {}", e); @@ -128,6 +129,7 @@ impl OhAudioProcess for OhAudioRender { let mut locked_data = self.stream_data.lock().unwrap(); locked_data.clear(); self.data_size.store(0, Ordering::Relaxed); + trace::oh_scream_render_destroy(); } fn process(&mut self, recv_data: &StreamData) -> i32 { @@ -197,6 +199,7 @@ impl OhAudioProcess for OhAudioCapture { match self.ctx.as_ref().unwrap().start() { Ok(()) => { self.start = true; + trace::oh_scream_capture_init(&self.ctx); true } Err(e) => { @@ -214,6 +217,7 @@ impl OhAudioProcess for OhAudioCapture { } self.ctx = None; } + trace::oh_scream_capture_destroy(); } fn preprocess(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) { @@ -271,6 +275,7 @@ extern "C" fn on_write_data_cb( unsafe { ptr::copy_nonoverlapping(su.addr as *const u8, dst_addr as *mut u8, len as usize) }; + trace::oh_scream_on_write_data_cb(len as usize); dst_addr += len; left -= len; @@ -330,6 +335,7 @@ extern "C" fn on_read_data_cb( len as usize, ) }; + trace::oh_scream_on_read_data_cb(len as usize); left -= len; src_addr += len; capture.cur_pos += len; diff --git a/trace/trace_info/misc.toml b/trace/trace_info/misc.toml index 78ac9d191..c05741938 100644 --- a/trace/trace_info/misc.toml +++ b/trace/trace_info/misc.toml @@ -27,3 +27,39 @@ name = "scream_setup_alsa_hwp" args = "name: &str, hwp: &dyn fmt::Debug" message = "scream {} setup hardware parameters: {:?}" enabled = true + +[[events]] +name = "oh_scream_render_init" +args = "context: &dyn fmt::Debug" +message = "context: {:?}" +enabled = true + +[[events]] +name = "oh_scream_render_destroy" +args = "" +message = "" +enabled = true + +[[events]] +name = "oh_scream_capture_init" +args = "context: &dyn fmt::Debug" +message = "context: {:?}" +enabled = true + +[[events]] +name = "oh_scream_capture_destroy" +args = "" +message = "" +enabled = true + +[[events]] +name = "oh_scream_on_write_data_cb" +args = "len: usize" +message = "len: {}" +enabled = true + +[[events]] +name = "oh_scream_on_read_data_cb" +args = "len: usize" +message = "len: {}" +enabled = true diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index 9590dad7e..4dd7687f2 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -197,6 +197,7 @@ pub enum AudioProcessCb { ), } +#[derive(Debug)] pub struct AudioContext { stream_type: AudioStreamType, spec: AudioSpec, -- Gitee From bbcb7cc75075d71535643b93fb3016b76890e2e5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 29 Apr 2024 20:56:42 +0800 Subject: [PATCH 1813/2187] cpu-features: use clap to parse the parameters of the cpu features config Use clap to parse the parameters of the cpu features config. Signed-off-by: liuxiangdong --- machine_manager/src/config/machine_config.rs | 81 +++++++++++++------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 89a40471f..fa277b03a 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -140,9 +140,14 @@ impl Default for MachineMemConfig { } } -#[derive(Clone, Debug, Serialize, Deserialize, Default)] +#[derive(Parser, Clone, Debug, Serialize, Deserialize, Default)] +#[command(no_binary_name(true))] pub struct CpuConfig { + #[arg(long, alias = "classtype", value_parser = ["host"])] + pub family: String, + #[arg(long, default_value = "off")] pub pmu: PmuConfig, + #[arg(long, default_value = "off")] pub sve: SveConfig, } @@ -153,6 +158,20 @@ pub enum PmuConfig { Off, } +impl FromStr for PmuConfig { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + "on" => Ok(PmuConfig::On), + "off" => Ok(PmuConfig::Off), + _ => Err(anyhow!( + "Invalid PMU option,must be one of \'on\" or \"off\"." + )), + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] pub enum SveConfig { On, @@ -160,6 +179,20 @@ pub enum SveConfig { Off, } +impl FromStr for SveConfig { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + "on" => Ok(SveConfig::On), + "off" => Ok(SveConfig::Off), + _ => Err(anyhow!( + "Invalid SVE option, must be one of \"on\" or \"off\"." + )), + } + } +} + #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ShutdownAction { #[default] @@ -418,28 +451,8 @@ impl VmConfig { } pub fn add_cpu_feature(&mut self, features: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("cpu"); - cmd_parser.push(""); - cmd_parser.push("pmu"); - cmd_parser.push("sve"); - cmd_parser.parse(features)?; - - // Check PMU when actually enabling PMU. - if let Some(k) = cmd_parser.get_value::("pmu")? { - self.machine_config.cpu_config.pmu = match k.as_ref() { - "on" => PmuConfig::On, - "off" => PmuConfig::Off, - _ => bail!("Invalid PMU option,must be one of \'on\" or \"off\"."), - } - } - - if let Some(k) = cmd_parser.get_value::("sve")? { - self.machine_config.cpu_config.sve = match k.as_ref() { - "on" => SveConfig::On, - "off" => SveConfig::Off, - _ => bail!("Invalid SVE option, must be one of \"on\" or \"off\"."), - } - } + let cpu_config = CpuConfig::try_parse_from(str_slip_to_clap(features, true, false))?; + self.machine_config.cpu_config = cpu_config; Ok(()) } @@ -1049,15 +1062,25 @@ mod tests { assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); vm_config.add_cpu_feature("host,pmu=off").unwrap(); assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); - vm_config.add_cpu_feature("pmu=off").unwrap(); - assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::Off); vm_config.add_cpu_feature("host,pmu=on").unwrap(); assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::On); - vm_config.add_cpu_feature("pmu=on").unwrap(); - assert!(vm_config.machine_config.cpu_config.pmu == PmuConfig::On); - vm_config.add_cpu_feature("sve=on").unwrap(); + vm_config.add_cpu_feature("host,sve=on").unwrap(); assert!(vm_config.machine_config.cpu_config.sve == SveConfig::On); - vm_config.add_cpu_feature("sve=off").unwrap(); + vm_config.add_cpu_feature("host,sve=off").unwrap(); assert!(vm_config.machine_config.cpu_config.sve == SveConfig::Off); + + // Illegal cpu command lines: should set cpu family. + let result = vm_config.add_cpu_feature("pmu=off"); + assert!(result.is_err()); + let result = vm_config.add_cpu_feature("sve=on"); + assert!(result.is_err()); + + // Illegal parameters. + let result = vm_config.add_cpu_feature("host,sve1=on"); + assert!(result.is_err()); + + // Illegal values. + let result = vm_config.add_cpu_feature("host,sve=false"); + assert!(result.is_err()); } } -- Gitee From 9ba9b56d02a72e8783d687c385b1e1f9e52983cb Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 16 Feb 2024 11:46:40 +0300 Subject: [PATCH 1814/2187] usb: Start adding support for USB streams Add stream_id to kick_endpoint() and wakeup_endpoint() signatures. Default it to always be zero for now. Also add streamid to XhciTransfer structure. Signed-off-by: Stanislav Goriainov --- devices/src/usb/mod.rs | 9 +++++---- devices/src/usb/xhci/xhci_controller.rs | 18 ++++++++++++++---- devices/src/usb/xhci/xhci_regs.rs | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 44f5d88ec..d8e2e3d8f 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -482,7 +482,7 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { locked_xhci.port_notify(&usb_port, PORTSC_PLC)?; } } - if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep) { + if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep, 0) { error!("Failed to wakeup endpoint {:?}", e); } Ok(()) @@ -510,12 +510,12 @@ pub struct UsbPacket { pub actual_length: u32, /// Endpoint number. pub ep_number: u8, + /// Stream id. + pub stream: u32, /// Transfer for complete packet. pub xfer_ops: Option>>, /// Target USB device for this packet. pub target_dev: Option>>, - /// Stream id. - pub stream: u32, } impl std::fmt::Display for UsbPacket { @@ -533,6 +533,7 @@ impl UsbPacket { packet_id: u32, pid: u32, ep_number: u8, + stream: u32, iovecs: Vec, xfer_ops: Option>>, target_dev: Option>>, @@ -546,9 +547,9 @@ impl UsbPacket { status: UsbPacketStatus::Success, actual_length: 0, ep_number, + stream, xfer_ops, target_dev, - stream: 0, } } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a58877196..e914ce021 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -120,6 +120,7 @@ pub struct XhciTransfer { td: Vec, complete: bool, slotid: u32, + streamid: u32, epid: u32, in_xfer: bool, iso_xfer: bool, @@ -149,6 +150,7 @@ impl XhciTransfer { complete: false, slotid: ep_info.0, epid: ep_info.1, + streamid: 0, in_xfer, iso_xfer: false, timed_xfer: false, @@ -1195,6 +1197,7 @@ impl XhciDevice { packet_id, USB_TOKEN_OUT as u32, 0, + 0, Vec::new(), None, Some(target_dev), @@ -1558,7 +1561,7 @@ impl XhciDevice { } /// Data plane - pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32) -> Result<()> { + pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32, _stream_id: u32) -> Result<()> { let epctx = match self.get_endpoint_ctx(slot_id, ep_id) { Ok(epctx) => epctx, Err(e) => { @@ -1839,7 +1842,7 @@ impl XhciDevice { if ep_state == EP_STOPPED && ep_state == EP_ERROR { return; } - if let Err(e) = locked_xhci.kick_endpoint(slotid, epid) { + if let Err(e) = locked_xhci.kick_endpoint(slotid, epid, 0) { error!("Failed to kick endpoint: {:?}", e); } }); @@ -1954,11 +1957,13 @@ impl XhciDevice { let packet_id = self.generate_packet_id(); let (_, ep_number) = endpoint_id_to_number(locked_xfer.epid as u8); + let stream = locked_xfer.streamid; let xfer_ops = Arc::downgrade(xfer) as Weak>; let packet = UsbPacket::new( packet_id, dir as u32, ep_number, + stream, vec, Some(xfer_ops), target_dev, @@ -2068,7 +2073,12 @@ impl XhciDevice { } /// Used for device to wakeup endpoint - pub fn wakeup_endpoint(&mut self, slot_id: u32, ep: &UsbEndpoint) -> Result<()> { + pub fn wakeup_endpoint( + &mut self, + slot_id: u32, + ep: &UsbEndpoint, + _stream_id: u32, + ) -> Result<()> { let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); if let Err(e) = self.get_endpoint_ctx(slot_id, ep_id as u32) { trace::usb_xhci_unimplemented(&format!( @@ -2077,7 +2087,7 @@ impl XhciDevice { )); return Ok(()); } - self.kick_endpoint(slot_id, ep_id as u32)?; + self.kick_endpoint(slot_id, ep_id as u32, 0)?; Ok(()) } diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 102b87f1d..98796e2ae 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -703,7 +703,7 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { return false; } else { let ep_id = value & DB_TARGET_MASK; - if let Err(e) = xhci.kick_endpoint(slot_id, ep_id) { + if let Err(e) = xhci.kick_endpoint(slot_id, ep_id, 0) { error!("Failed to kick endpoint: {:?}", e); xhci.host_controller_error(); return false; -- Gitee From b86da650c87c791e7323ba46e07000b76625ebe9 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 16 May 2024 16:31:44 +0800 Subject: [PATCH 1815/2187] Build: Add debug option for building release version Add debug option of profile in cargo.toml for building release version with more debug information like symbal table. Signed-off-by: Jinhao Gao --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index a82f9b791..b00a2da08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,3 +55,4 @@ panic = "abort" [profile.release] panic = "abort" lto = true +debug = true -- Gitee From 3be1920a71b5496b97210ee4c04b4b2f15881af6 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 2 May 2024 15:47:38 +0300 Subject: [PATCH 1816/2187] net: Refactor rx, improve tx Suppress tx virtio queue notify and listen for tap fd OUT event when the tap is full instead of going into a busy loop. Remove redundant recv_evt for rx. Signed-off-by: goriainovstanislav --- virtio/src/device/net.rs | 172 ++++++++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 57 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 1691dc06a..0e5a9a847 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -672,13 +672,18 @@ impl EventNotifierHelper for NetCtrlHandler { } struct TxVirtio { + tap_full: bool, queue: Arc>, queue_evt: Arc, } impl TxVirtio { fn new(queue: Arc>, queue_evt: Arc) -> Self { - TxVirtio { queue, queue_evt } + TxVirtio { + tap_full: false, + queue, + queue_evt, + } } } @@ -686,16 +691,14 @@ struct RxVirtio { queue_full: bool, queue: Arc>, queue_evt: Arc, - recv_evt: Arc, } impl RxVirtio { - fn new(queue: Arc>, queue_evt: Arc, recv_evt: Arc) -> Self { + fn new(queue: Arc>, queue_evt: Arc) -> Self { RxVirtio { queue_full: false, queue, queue_evt, - recv_evt, } } } @@ -863,7 +866,7 @@ impl NetIoHandler { rx_packets += 1; if rx_packets >= self.queue_size { self.rx - .recv_evt + .queue_evt .write(1) .with_context(|| "Failed to trigger tap queue event".to_string())?; break; @@ -925,10 +928,12 @@ impl NetIoHandler { }; if tap_fd != -1 && self.send_packets(tap_fd, &iovecs) == -1 { queue.vring.push_back(); - self.tx.queue_evt.write(1).with_context(|| { - "Failed to trigger tx queue event when writev blocked".to_string() - })?; - return Ok(()); + queue + .vring + .suppress_queue_notify(&self.mem_space, self.driver_features, true) + .with_context(|| "Failed to suppress tx queue notify")?; + self.tx.tap_full = true; + break; } queue @@ -959,6 +964,50 @@ impl NetIoHandler { Ok(()) } + fn tap_fd_handler(net_io: &mut Self) -> Vec { + let mut notifiers = Vec::new(); + + if !net_io.is_listening && (!net_io.rx.queue_full || net_io.tx.tap_full) { + notifiers.push(EventNotifier::new( + NotifierOperation::Resume, + net_io.tap_fd, + None, + EventSet::empty(), + Vec::new(), + )); + net_io.is_listening = true; + } + + if !net_io.is_listening { + return notifiers; + } + + // NOTE: We want to poll for OUT event when the tap is full, and for IN event when the + // virtio queue is NOT full. + let tap_events = match (net_io.rx.queue_full, net_io.tx.tap_full) { + (false, true) => EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, + (true, true) => EventSet::OUT | EventSet::EDGE_TRIGGERED, + (false, false) => EventSet::IN | EventSet::EDGE_TRIGGERED, + (true, false) => EventSet::empty(), + }; + + let tap_operation = if tap_events.is_empty() { + net_io.is_listening = false; + NotifierOperation::Park + } else { + NotifierOperation::Modify + }; + + notifiers.push(EventNotifier::new( + tap_operation, + net_io.tap_fd, + None, + tap_events, + Vec::new(), + )); + notifiers + } + fn update_evt_handler(net_io: &Arc>) -> Vec { let mut locked_net_io = net_io.lock().unwrap(); locked_net_io.tap = match locked_net_io.receiver.recv() { @@ -977,7 +1026,6 @@ impl NetIoHandler { let mut notifiers_fds = vec![ locked_net_io.update_evt.as_raw_fd(), locked_net_io.rx.queue_evt.as_raw_fd(), - locked_net_io.rx.recv_evt.as_raw_fd(), locked_net_io.tx.queue_evt.as_raw_fd(), ]; if old_tap_fd != -1 { @@ -1055,30 +1103,23 @@ impl EventNotifierHelper for NetIoHandler { return None; } - if let Err(ref e) = locked_net_io.rx.recv_evt.write(1) { - error!("Failed to trigger tap receive event, {:?}", e); + locked_net_io.rx.queue_full = false; + + if let Err(ref err) = locked_net_io.handle_rx() { + error!("Failed to handle receive queue event: {:?}", err); report_virtio_error( locked_net_io.interrupt_cb.clone(), locked_net_io.driver_features, &locked_net_io.device_broken, ); + return None; } - if let Some(tap) = locked_net_io.tap.as_ref() { - if !locked_net_io.is_listening { - let notifier = vec![EventNotifier::new( - NotifierOperation::Resume, - tap.as_raw_fd(), - None, - EventSet::IN | EventSet::EDGE_TRIGGERED, - Vec::new(), - )]; - locked_net_io.is_listening = true; - locked_net_io.rx.queue_full = false; - return Some(notifier); - } + if locked_net_io.tap.is_some() { + Some(NetIoHandler::tap_fd_handler(&mut locked_net_io)) + } else { + None } - None }); let rx_fd = locked_net_io.rx.queue_evt.as_raw_fd(); notifiers.push(build_event_notifier( @@ -1096,6 +1137,7 @@ impl EventNotifierHelper for NetIoHandler { if locked_net_io.device_broken.load(Ordering::SeqCst) { return None; } + if let Err(ref e) = locked_net_io.handle_tx() { error!("Failed to handle tx(tx event) for net, {:?}", e); report_virtio_error( @@ -1104,7 +1146,12 @@ impl EventNotifierHelper for NetIoHandler { &locked_net_io.device_broken, ); } - None + + if locked_net_io.tap.is_some() { + Some(NetIoHandler::tap_fd_handler(&mut locked_net_io)) + } else { + None + } }); let tx_fd = locked_net_io.tx.queue_evt.as_raw_fd(); notifiers.push(build_event_notifier( @@ -1117,50 +1164,62 @@ impl EventNotifierHelper for NetIoHandler { // Register event notifier for tap. let cloned_net_io = net_io.clone(); if let Some(tap) = locked_net_io.tap.as_ref() { - let handler: Rc = Rc::new(move |_, _| { + let handler: Rc = Rc::new(move |events: EventSet, _| { let mut locked_net_io = cloned_net_io.lock().unwrap(); if locked_net_io.device_broken.load(Ordering::SeqCst) { return None; } - if let Err(ref e) = locked_net_io.handle_rx() { - error!("Failed to handle rx(tap event), {:?}", e); - report_virtio_error( - locked_net_io.interrupt_cb.clone(), + if events.contains(EventSet::OUT) { + locked_net_io.tx.tap_full = false; + let mut locked_queue = locked_net_io.tx.queue.lock().unwrap(); + + if let Err(ref err) = locked_queue.vring.suppress_queue_notify( + &locked_net_io.mem_space, locked_net_io.driver_features, - &locked_net_io.device_broken, - ); - return None; + false, + ) { + error!("Failed to enable tx queue notify: {:?}", err); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + &locked_net_io.device_broken, + ); + return None; + }; + + drop(locked_queue); + + if let Err(ref e) = locked_net_io.handle_tx() { + error!("Failed to handle tx(tx event) for net, {:?}", e); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + &locked_net_io.device_broken, + ); + } } - if let Some(tap) = locked_net_io.tap.as_ref() { - if locked_net_io.rx.queue_full && locked_net_io.is_listening { - let notifier = vec![EventNotifier::new( - NotifierOperation::Park, - tap.as_raw_fd(), - None, - EventSet::IN | EventSet::EDGE_TRIGGERED, - Vec::new(), - )]; - locked_net_io.is_listening = false; - return Some(notifier); + if events.contains(EventSet::IN) { + if let Err(ref err) = locked_net_io.handle_rx() { + error!("Failed to handle receive queue event: {:?}", err); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + &locked_net_io.device_broken, + ); + return None; } } - None + + Some(NetIoHandler::tap_fd_handler(&mut locked_net_io)) }); let tap_fd = tap.as_raw_fd(); notifiers.push(build_event_notifier( tap_fd, Some(handler.clone()), NotifierOperation::AddShared, - EventSet::IN | EventSet::EDGE_TRIGGERED, - )); - let recv_evt_fd = locked_net_io.rx.recv_evt.as_raw_fd(); - notifiers.push(build_event_notifier( - recv_evt_fd, - Some(handler), - NotifierOperation::AddShared, - EventSet::IN | EventSet::EDGE_TRIGGERED, + EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, )); } @@ -1565,9 +1624,8 @@ impl VirtioDevice for Net { } let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); - let recv_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); let mut handler = NetIoHandler { - rx: RxVirtio::new(rx_queue, rx_queue_evt, recv_evt), + rx: RxVirtio::new(rx_queue, rx_queue_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), tap: self.taps.as_ref().map(|t| t[index].clone()), tap_fd: -1, -- Gitee From 185877bc6eb00ef49e07770aae93ec03c5667e47 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 16 May 2024 17:34:51 +0800 Subject: [PATCH 1817/2187] trace: add trace for fwcfg Signed-off-by: Mingwang Li --- devices/src/legacy/fwcfg.rs | 21 +++++++++++++++------ trace/trace_info/device_legacy.toml | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 2abf4368e..e675fb7d7 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -361,11 +361,14 @@ impl FwCfgCommon { /// Select the entry by the key specified fn select_entry(&mut self, key: u16) { + let ret; self.cur_offset = 0; if (key & FW_CFG_ENTRY_MASK) >= self.max_entry() { self.cur_entry = FW_CFG_INVALID; + ret = 0; } else { self.cur_entry = key; + ret = 1; // unwrap() is safe because we have checked the range of `key`. let selected_entry = self.get_entry_mut().unwrap(); @@ -373,6 +376,8 @@ impl FwCfgCommon { cb.select_callback(); } } + + trace::fwcfg_select_entry(key, get_key_name(key as usize), ret); } fn add_entry( @@ -404,11 +409,12 @@ impl FwCfgCommon { warn!("Entry not empty, will override"); } - entry.data = data; + entry.data = data.clone(); entry.select_cb = select_cb; entry.allow_write = allow_write; entry.write_cb = write_cb; + trace::fwcfg_add_entry(key, get_key_name(key as usize), data); Ok(()) } @@ -467,11 +473,8 @@ impl FwCfgCommon { } } - let file = FwCfgFile::new( - data.len() as u32, - FW_CFG_FILE_FIRST + index as u16, - filename, - ); + let data_len = data.len(); + let file = FwCfgFile::new(data_len as u32, FW_CFG_FILE_FIRST + index as u16, filename); self.files.insert(index, file); self.files.iter_mut().skip(index + 1).for_each(|f| { f.select += 1; @@ -489,6 +492,8 @@ impl FwCfgCommon { FW_CFG_FILE_FIRST as usize + index, FwCfgEntry::new(data, select_cb, write_cb, allow_write), ); + + trace::fwcfg_add_file(index, filename, data_len); Ok(()) } @@ -650,6 +655,8 @@ impl FwCfgCommon { self.cur_offset = offset; write_dma_result(&self.mem_space, dma_addr, dma.control)?; + + trace::fwcfg_read_data(0); Ok(()) } @@ -743,6 +750,8 @@ impl FwCfgCommon { value <<= 8 * size as u64; } self.cur_offset = cur_offset; + + trace::fwcfg_read_data(value); Ok(value) } diff --git a/trace/trace_info/device_legacy.toml b/trace/trace_info/device_legacy.toml index 14ed94332..42bbae08d 100644 --- a/trace/trace_info/device_legacy.toml +++ b/trace/trace_info/device_legacy.toml @@ -201,3 +201,27 @@ name = "pflash_write_data" args = "offset: u64, size: usize, value: &[u8], counter: u32" message = "data offset: 0x{:04x}, size: {}, value: 0x{:x?}, counter: 0x{:04x}" enabled = true + +[[events]] +name = "fwcfg_select_entry" +args = "key: u16, key_name: &'static str, ret: i32" +message = "key_value {} key_name {:?} ret {}" +enabled = true + +[[events]] +name = "fwcfg_add_entry" +args = "key: u16, key_name: &'static str, data: Vec" +message = "key_value {} key_name {:?} data {:?}" +enabled = true + +[[events]] +name = "fwcfg_read_data" +args = "value: u64" +message = "value {}" +enabled = true + +[[events]] +name = "fwcfg_add_file" +args = "index: usize, filename: &str, data_len: usize" +message = "index {} filename {:?} data_len {}" +enabled = true -- Gitee From 3503978e45aeb2e47641a6110bf676e302a82708 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 16 Feb 2024 12:37:02 +0300 Subject: [PATCH 1818/2187] xhci: Add streams to doorbell ops Deduce stream id from doorbell value and pass it to kick_endpoint(). Signed-off-by: Stanislav Goriainov --- devices/src/usb/xhci/xhci_controller.rs | 4 ++-- devices/src/usb/xhci/xhci_regs.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index e914ce021..87e13929d 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2077,7 +2077,7 @@ impl XhciDevice { &mut self, slot_id: u32, ep: &UsbEndpoint, - _stream_id: u32, + stream_id: u32, ) -> Result<()> { let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); if let Err(e) = self.get_endpoint_ctx(slot_id, ep_id as u32) { @@ -2087,7 +2087,7 @@ impl XhciDevice { )); return Ok(()); } - self.kick_endpoint(slot_id, ep_id as u32, 0)?; + self.kick_endpoint(slot_id, ep_id as u32, stream_id)?; Ok(()) } diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 98796e2ae..23c0b9a94 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -103,6 +103,9 @@ const XHCI_INTR_REG_SHIFT: u64 = 5; /// Doorbell Register Bit Field. /// DB Target. const DB_TARGET_MASK: u32 = 0xff; +/// DB Stream. +const DB_STREAM_ID_SHIFT: u32 = 16; +const DB_STREAM_ID_MASK: u32 = 0xffff; /// Port Registers. const XHCI_PORTSC: u64 = 0x0; const XHCI_PORTPMSC: u64 = 0x4; @@ -703,7 +706,8 @@ pub fn build_doorbell_ops(xhci_dev: &Arc>) -> RegionOps { return false; } else { let ep_id = value & DB_TARGET_MASK; - if let Err(e) = xhci.kick_endpoint(slot_id, ep_id, 0) { + let stream_id = (value >> DB_STREAM_ID_SHIFT) & DB_STREAM_ID_MASK; + if let Err(e) = xhci.kick_endpoint(slot_id, ep_id, stream_id) { error!("Failed to kick endpoint: {:?}", e); xhci.host_controller_error(); return false; -- Gitee From 6837cd22b5de523e89d88b6898cdbbb2933fd54d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 16 May 2024 21:49:53 +0800 Subject: [PATCH 1819/2187] trace: add trace for ged and power Signed-off-by: Mingwang Li --- devices/src/acpi/ged.rs | 4 ++++ devices/src/acpi/power.rs | 3 +++ trace/trace_info/acpi.toml | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 trace/trace_info/acpi.toml diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index f50e1b118..55e5ba0f8 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -122,6 +122,7 @@ impl Ged { .notification_type .store(AcpiEvent::PowerDown as u32, Ordering::SeqCst); ged_clone.inject_interrupt(); + trace::ged_inject_acpi_event(AcpiEvent::PowerDown as u32); if QmpChannel::is_connected() { event!(Powerdown); } @@ -151,6 +152,7 @@ impl Ged { .notification_type .store(AcpiEvent::CpuResize as u32, Ordering::SeqCst); clone_ged.inject_interrupt(); + trace::ged_inject_acpi_event(AcpiEvent::CpuResize as u32); if QmpChannel::is_connected() { event!(CpuResize); } @@ -174,6 +176,7 @@ impl Ged { self.notification_type .fetch_or(evt as u32, Ordering::SeqCst); self.inject_interrupt(); + trace::ged_inject_acpi_event(evt as u32); } } @@ -203,6 +206,7 @@ impl SysBusDevOps for Ged { let value = self .notification_type .swap(AcpiEvent::Nothing as u32, Ordering::SeqCst); + trace::ged_read(value); write_data_u32(data, value) } diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index a51071d8b..5d5685d3f 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -154,6 +154,8 @@ impl PowerDev { // unit: mW self.regs[REG_IDX_BAT_PRATE] = (self.regs[REG_IDX_BAT_PRATE] * self.regs[REG_IDX_BAT_PVOLT]) / 1000; + + trace::power_status_read(&self.regs); Ok(()) } @@ -253,6 +255,7 @@ impl SysBusDevOps for PowerDev { return false; } let value = self.regs[reg_idx as usize]; + trace::power_read(reg_idx, value); write_data_u32(data, value) } diff --git a/trace/trace_info/acpi.toml b/trace/trace_info/acpi.toml new file mode 100644 index 000000000..9b4352aaa --- /dev/null +++ b/trace/trace_info/acpi.toml @@ -0,0 +1,23 @@ +[[events]] +name = "ged_inject_acpi_event" +args = "event: u32" +message = "acpi_sevent {}" +enabled = true + +[[events]] +name = "ged_read" +args = "event: u32" +message = "acpi_sevent {}" +enabled = true + +[[events]] +name = "power_read" +args = "reg_idx: u64, value: u32" +message = "reg_idx {} value {}" +enabled = true + +[[events]] +name = "power_status_read" +args = "regs: &dyn fmt::Debug" +message = "regs {:?}" +enabled = true -- Gitee From ba306fac4123b37532717fcc977b24b7ded5c0d2 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 17 May 2024 01:47:22 +0800 Subject: [PATCH 1820/2187] address_space: fix panic when writing address space `ByteCode::from_bytes` will check date's length and will return `None` when it's not equal to the expected size. Panic will happend when writing address space if buf's length is less than 8. Signed-off-by: liuxiangdong --- address_space/src/address_space.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index f935d01b6..bad13f3b9 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -650,6 +650,7 @@ impl AddressSpace { src.read_to_end(&mut buf).unwrap(); if buf.len() <= 8 { + buf.resize(8, 0); let data = u64::from_bytes(buf.as_slice()).unwrap(); if *data == evtfd.data { if let Err(e) = evtfd.fd.write(1) { -- Gitee From 61165a99967904dd919771d89f3fbbde02d1e60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Fri, 17 May 2024 11:05:02 +0800 Subject: [PATCH 1821/2187] oh camera fmt Signed-off-by: Jiahong Li --- devices/src/camera_backend/demo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 0640c7812..19b1d4da7 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -549,7 +549,7 @@ fn convert_to_nv12(source: &[u8], width: u32, height: u32) -> Vec { source[idx + 2] as f32, ); let y = (0.299 * r + 0.587 * g + 0.114 * b) as u8; - img_nv12.push(y as u8); + img_nv12.push(y); } for i in 0..(width * height / 2) { let idx = (i * 2 * pixel) as usize; -- Gitee From 65073766eef32bc3a14d0db01cff1c7657e293db Mon Sep 17 00:00:00 2001 From: li-huachao Date: Mon, 13 May 2024 20:03:09 +0800 Subject: [PATCH 1822/2187] Balloon: fix trace::virtio_receive_request Whe req_type is flase, the balloon request type is deflate. --- virtio/src/device/balloon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 6e6095f9c..bc86b048b 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -616,7 +616,7 @@ impl BalloonIoHandler { trace::virtio_receive_request("Balloon".to_string(), "to inflate".to_string()); &self.inf_queue } else { - trace::virtio_receive_request("Balloon".to_string(), "to inflate".to_string()); + trace::virtio_receive_request("Balloon".to_string(), "to deflate".to_string()); &self.def_queue }; let mut locked_queue = queue.lock().unwrap(); -- Gitee From 27dc1d49de21e3fe22bc318a7e6403b5a43827ac Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 17 May 2024 15:00:17 +0800 Subject: [PATCH 1823/2187] event-loop: introduce new operations to update event This patch introduces two operations to add/delete events from current event table for a file descriptor. Signed-off-by: Zhao Yi Min --- util/src/loop_context.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 10e4cf706..ecad86e29 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -54,6 +54,10 @@ pub enum NotifierOperation { Park = 16, /// Resume a file descriptor from the event table Resume = 32, + /// Add events to current event table for a file descriptor + AddEvents = 64, + /// Delete events from current event table for a file descriptor + DeleteEvents = 128, } #[derive(Debug, PartialEq)] @@ -466,6 +470,35 @@ impl EventLoopContext { Ok(()) } + fn update_events_for_fd(&mut self, event: &EventNotifier, add: bool) -> Result<()> { + let mut events_map = self.events.write().unwrap(); + match events_map.get_mut(&event.raw_fd) { + Some(notifier) => { + let new_events = if add { + event.event | notifier.event + } else { + !event.event & notifier.event + }; + if new_events != notifier.event { + self.epoll + .ctl( + ControlOperation::Modify, + notifier.raw_fd, + EpollEvent::new(new_events, &**notifier as *const _ as u64), + ) + .with_context(|| { + format!("Failed to add events, event fd: {}", notifier.raw_fd) + })?; + notifier.event = new_events; + } + } + _ => { + return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd))); + } + } + Ok(()) + } + /// update fds registered to `EventLoop` according to the operation type. /// /// # Arguments @@ -490,6 +523,12 @@ impl EventLoopContext { NotifierOperation::Resume => { self.resume_event(&en)?; } + NotifierOperation::AddEvents => { + self.update_events_for_fd(&en, true)?; + } + NotifierOperation::DeleteEvents => { + self.update_events_for_fd(&en, false)?; + } } } self.kick(); -- Gitee From 7e797b93cb2cc4448256059cb79a08587bfa9991 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 16 May 2024 11:40:34 +0800 Subject: [PATCH 1824/2187] virtio-serial: fixup data discard issue when socket is blocked If the peer of chardev receives data too slowly, output fd of chardev might be blocked. This makes the data discarded. This patch introduces an output buffer to save left data. When blocked error occurs, add output stream fd to event poll. After the stream fd can be written, the handler notifies output virtqueue of virtio-serial device to continue sending data. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 73 +++++++++++++++++-- virtio/src/device/serial.rs | 127 +++++++++++++++++++++++---------- 2 files changed, 160 insertions(+), 40 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 3f34fc610..cf4bb7126 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -15,6 +15,7 @@ use std::io::{Stdin, Stdout}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::path::PathBuf; use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -24,6 +25,7 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::pty::openpty; use nix::sys::termios::{cfmakeraw, tcgetattr, tcsetattr, SetArg, Termios}; use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::EventFd; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{PathInfo, PTY_PATH}; @@ -85,6 +87,10 @@ pub struct Chardev { /// Scheduled DPC to unpause input stream. /// Unpause must be done inside event-loop unpause_timer: Option, + /// output stream fd is blocked + output_blocked: Option>, + /// output listener to notify when output stream fd can be written + output_listener_fd: Option>, } impl Chardev { @@ -100,6 +106,8 @@ impl Chardev { dev: None, wait_port: false, unpause_timer: None, + output_blocked: None, + output_listener_fd: None, } } @@ -222,7 +230,7 @@ impl Chardev { let unpause_fn = Box::new(move || { let res = EventLoop::update_event( vec![EventNotifier::new( - NotifierOperation::Modify, + NotifierOperation::AddEvents, input_fd, None, EventSet::IN | EventSet::HANG_UP, @@ -246,6 +254,33 @@ impl Chardev { self.unpause_timer = None; } } + + pub fn add_listen_for_tx( + &mut self, + listener_fd: Arc, + blocked: Arc, + ) -> Result<()> { + let event_notifier = EventNotifier::new( + NotifierOperation::AddEvents, + self.stream_fd.unwrap(), + None, + EventSet::OUT, + Vec::new(), + ); + + match EventLoop::update_event(vec![event_notifier], None) { + Ok(()) => { + self.output_blocked = Some(blocked.clone()); + self.output_listener_fd = Some(listener_fd); + blocked.store(true, Ordering::Release); + Ok(()) + } + Err(e) => { + blocked.store(false, Ordering::Release); + Err(e) + } + } + } } fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { @@ -438,10 +473,10 @@ fn get_socket_notifier(chardev: Arc>) -> Option { locked_receiver.set_paused(); return Some(vec![EventNotifier::new( - NotifierOperation::Modify, + NotifierOperation::DeleteEvents, stream_fd, None, - EventSet::HANG_UP, + EventSet::IN, vec![], )]); } @@ -471,12 +506,42 @@ fn get_socket_notifier(chardev: Arc>) -> Option { None }); + let handling_chardev = cloned_chardev.clone(); + let output_handler = Rc::new(move |event, fd| { + if event & EventSet::OUT != EventSet::OUT { + return None; + } + + let mut locked_cdev = handling_chardev.lock().unwrap(); + if locked_cdev.output_blocked.is_some() && locked_cdev.output_listener_fd.is_some() { + let fd = locked_cdev.output_listener_fd.as_ref().unwrap(); + if let Err(e) = fd.write(1) { + error!("Failed to write eventfd with error {:?}", e); + return None; + } + locked_cdev + .output_blocked + .as_ref() + .unwrap() + .store(false, Ordering::Release); + locked_cdev.output_blocked = None; + locked_cdev.output_listener_fd = None; + } + Some(vec![EventNotifier::new( + NotifierOperation::DeleteEvents, + fd, + None, + EventSet::OUT, + Vec::new(), + )]) + }); + Some(vec![EventNotifier::new( NotifierOperation::AddShared, stream_fd, Some(listener_fd), EventSet::IN | EventSet::HANG_UP, - vec![input_handler], + vec![input_handler, output_handler], )]) }); diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 3968821ce..7d4b11ea5 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -25,8 +25,8 @@ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use crate::{ - gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, report_virtio_error, - Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + gpa_hva_iovec_map, iov_to_buf, read_config_default, report_virtio_error, Element, Queue, + VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; @@ -277,6 +277,10 @@ impl VirtioDevice for Serial { device_broken: self.base.broken.clone(), port: port.clone(), nr, + outbuf: Vec::with_capacity(BUF_SIZE), + outbuf_consumed: 0, + outbuf_len: 0, + output_blocked: Arc::new(AtomicBool::new(false)), }; let handler_h = Arc::new(Mutex::new(handler)); let notifiers = EventNotifierHelper::internal_notifiers(handler_h.clone()); @@ -419,6 +423,10 @@ struct SerialPortHandler { device_broken: Arc, port: Option>>, nr: u32, + outbuf: Vec, + outbuf_consumed: usize, + outbuf_len: usize, + output_blocked: Arc, } /// Handler for queues which are used for control. @@ -457,9 +465,24 @@ impl SerialPortHandler { } fn output_handle_internal(&mut self) -> Result<()> { - let mut queue_lock = self.output_queue.lock().unwrap(); + // If fd for tx is blocked, copy data to output buffer and wait for POLL_OUT event. + if self.output_blocked.load(Ordering::Acquire) { + return Ok(()); + } - loop { + match self.consume_outbuf() { + Ok(blocked) => { + if blocked { + return Ok(()); + } + } + Err(e) => bail!("Failed to consume out buffer with error {:?}", e), + } + + let queue = self.output_queue.clone(); + let mut queue_lock = queue.lock().unwrap(); + let mut blocked = false; + while !blocked { let elem = queue_lock .vring .pop_avail(&self.mem_space, self.driver_features)?; @@ -467,25 +490,28 @@ impl SerialPortHandler { break; } + assert_eq!(self.outbuf_len, 0); + // Discard requests when there is no port using this queue. Popping elements without // processing means discarding the request. if self.port.is_some() { - let mut iovec = elem.out_iovec; - let mut iovec_size = Element::iovec_size(&iovec); - while iovec_size > 0 { - let mut buffer = [0_u8; BUF_SIZE]; - let size = iov_to_buf(&self.mem_space, &iovec, &mut buffer)? as u64; - - self.write_chardev_msg(&buffer, size as usize); - - iovec = iov_discard_front(&mut iovec, size) - .unwrap_or_default() - .to_vec(); - // Safety: iovec follows the iov_discard_front operation and - // iovec_size always equals Element::iovec_size(&iovec). - iovec_size -= size; - trace::virtio_serial_output_data(iovec_size, size); + let iovec = elem.out_iovec; + let iovec_size = Element::iovec_size(&iovec); + if iovec_size as usize > self.outbuf.len() { + self.outbuf.resize(iovec_size as usize, 0); + } + + let buffer = &mut self.outbuf[..]; + let size = iov_to_buf(&self.mem_space, &iovec, buffer)? as u64; + + assert_eq!(size, iovec_size); + self.outbuf_len = size as usize; + + match self.consume_outbuf() { + Ok(b) => blocked = b, + Err(e) => bail!("Failed to consume out buffer with error {:?}", e), } + trace::virtio_serial_output_data(iovec_size, size); } queue_lock @@ -516,28 +542,57 @@ impl SerialPortHandler { Ok(()) } - fn write_chardev_msg(&self, buffer: &[u8], write_len: usize) { - let port_locked = self.port.as_ref().unwrap().lock().unwrap(); + fn clear_outbuf(&mut self) { + self.outbuf_len = 0; + self.outbuf_consumed = 0; + } + + fn consume_outbuf(&mut self) -> Result { + if self.outbuf_len == 0 { + return Ok(false); + } + + let port_cloned = self.port.clone(); + let port_locked = port_cloned.as_ref().unwrap().lock().unwrap(); // Discard output buffer if this port's chardev is not connected. if !port_locked.host_connected { - return; + self.clear_outbuf(); + return Ok(false); } - if let Some(output) = &mut port_locked.chardev.lock().unwrap().output { - let mut locked_output = output.lock().unwrap(); - // To do: - // If the buffer is not fully written to chardev, the incomplete part will be discarded. - // This may occur when chardev is abnormal. Consider optimizing this logic in the - // future. - if let Err(e) = locked_output.write_all(&buffer[..write_len]) { - error!("Port {} failed to write msg to chardev: {:?}", self.nr, e); - } - if let Err(e) = locked_output.flush() { - error!("Port {} failed to flush msg to chardev: {:?}", self.nr, e); + let mut locked_chardev = port_locked.chardev.lock().unwrap(); + if locked_chardev.output.is_none() { + error!("Port {} failed to get output interface", self.nr); + self.clear_outbuf(); + return Ok(false); + } + let output = locked_chardev.output.clone(); + let mut locked_output = output.as_ref().unwrap().lock().unwrap(); + + while self.outbuf_consumed < self.outbuf_len { + match locked_output.write(&self.outbuf[self.outbuf_consumed..self.outbuf_len]) { + Ok(size) => self.outbuf_consumed += size, + Err(e) => { + let err_type = e.kind(); + if err_type != std::io::ErrorKind::WouldBlock + && err_type != std::io::ErrorKind::Interrupted + { + self.clear_outbuf(); + bail!("chardev failed to write message with error {:?}", e); + } + if let Err(e) = locked_chardev.add_listen_for_tx( + self.output_queue_evt.clone(), + self.output_blocked.clone(), + ) { + error!("failed to wait for tx fd with error {:?}", e); + continue; + } + return Ok(true); + } } - } else { - error!("Port {} failed to get output fd", self.nr); - }; + } + self.clear_outbuf(); + Ok(false) } fn get_input_avail_bytes(&mut self, max_size: usize) -> usize { -- Gitee From 8805060f0a67dd192ec7733a623cc06fb5e302d8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 16 May 2024 14:49:11 +0800 Subject: [PATCH 1825/2187] machine_manager: add func to get value of a parameter from string Add func `get_value_of_parameter` to get value of parameter from string. Signed-off-by: liuxiangdong --- machine_manager/src/config/mod.rs | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 0355a6b9e..f6a5d9a25 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -803,6 +803,24 @@ pub fn str_slip_to_clap( itr } +/// Retrieve the value of the specified parameter from a string in the format "key=value". +pub fn get_value_of_parameter(parameter: &str, args_str: &str) -> Result { + let args_vecs = args_str.split([',']).collect::>(); + + for args in args_vecs { + let key_value = args.split(['=']).collect::>(); + if key_value.len() != 2 || key_value[0] != parameter { + continue; + } + if key_value[1].is_empty() { + bail!("Find empty arg {} in string {}.", key_value[0], args_str); + } + return Ok(key_value[1].to_string()); + } + + bail!("Cannot find {}'s value from string {}", parameter, args_str); +} + pub fn valid_id(id: &str) -> Result { check_arg_too_long(id, "id")?; Ok(id.to_string()) @@ -1007,4 +1025,20 @@ mod tests { let res = vm_config.add_global_config("pcie-root-port.fast-unplug=1"); assert!(res.is_err()); } + + #[test] + fn test_get_value_of_parameter() { + let cmd = "scsi-hd,id=disk1,drive=scsi-drive-0"; + let id = get_value_of_parameter("id", cmd).unwrap(); + assert_eq!(id, "disk1"); + + let cmd = "id="; + assert!(get_value_of_parameter("id", cmd).is_err()); + + let cmd = "id"; + assert!(get_value_of_parameter("id", cmd).is_err()); + + let cmd = "scsi-hd,idxxx=disk1"; + assert!(get_value_of_parameter("id", cmd).is_err()); + } } -- Gitee From 0dc4603966596bcde35637fbf417ac6d6e03c588 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 16 May 2024 19:22:11 +0800 Subject: [PATCH 1826/2187] machine_manager: use `get_value_of_parameter` to get a single parameter's value Use `get_value_of_parameter` func to get a single parameter's value. Signed-off-by: liuxiangdong --- machine/src/lib.rs | 16 +++++----- machine_manager/src/config/devices.rs | 45 ++------------------------- machine_manager/src/config/mod.rs | 32 ++++++++++--------- machine_manager/src/config/numa.rs | 32 ++++++++----------- machine_manager/src/config/pci.rs | 23 +++++--------- 5 files changed, 49 insertions(+), 99 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index ab0957c9d..990f9cc2d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -73,11 +73,11 @@ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; use machine_manager::config::{ - complete_numa_node, get_chardev_socket_path, get_pci_bdf, parse_device_id, parse_device_type, - parse_numa_distance, parse_numa_mem, str_slip_to_clap, BootIndexInfo, BootSource, ConfigCheck, - DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, NetworkInterfaceConfig, - NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, VirtioSerialInfo, - VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + complete_numa_node, get_chardev_socket_path, get_class_type, get_pci_bdf, + get_value_of_parameter, parse_numa_distance, parse_numa_mem, str_slip_to_clap, BootIndexInfo, + BootSource, ConfigCheck, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, + NetworkInterfaceConfig, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, + VirtioSerialInfo, VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -570,7 +570,7 @@ pub trait MachineOps { VhostKern::VsockConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let sys_mem = self.get_sys_mem().clone(); let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); - match parse_device_type(cfg_args)?.as_str() { + match device_cfg.classtype.as_str() { "vhost-vsock-device" => { check_arg_nonexist!( ("bus", device_cfg.bus), @@ -1771,7 +1771,7 @@ pub trait MachineOps { /// * `driver` - USB device class. /// * `cfg_args` - USB device Configuration. fn add_usb_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - let usb_device = match parse_device_type(cfg_args)?.as_str() { + let usb_device = match get_class_type(cfg_args)?.as_str() { "usb-kbd" => { let config = UsbKeyboardConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; @@ -1873,7 +1873,7 @@ pub trait MachineOps { for dev in &cloned_vm_config.devices { let cfg_args = dev.1.as_str(); // Check whether the device id exists to ensure device uniqueness. - let id = parse_device_id(cfg_args)?; + let id = get_value_of_parameter("id", cfg_args)?; self.check_device_id_existed(&id) .with_context(|| format!("Failed to check device id: config {}", cfg_args))?; #[cfg(feature = "scream")] diff --git a/machine_manager/src/config/devices.rs b/machine_manager/src/config/devices.rs index cf42739b4..e355b88f2 100644 --- a/machine_manager/src/config/devices.rs +++ b/machine_manager/src/config/devices.rs @@ -13,7 +13,7 @@ use anyhow::{Context, Result}; use regex::Regex; -use super::{CmdParser, VmConfig}; +use super::{get_class_type, VmConfig}; use crate::qmp::qmp_schema; impl VmConfig { @@ -117,7 +117,7 @@ impl VmConfig { } pub fn add_device(&mut self, device_config: &str) -> Result<()> { - let device_type = parse_device_type(device_config)?; + let device_type = get_class_type(device_config).with_context(|| "Missing driver field.")?; self.devices.push((device_type, device_config.to_string())); Ok(()) @@ -135,44 +135,3 @@ impl VmConfig { } } } - -pub fn parse_device_type(device_config: &str) -> Result { - let mut cmd_params = CmdParser::new("device"); - cmd_params.push(""); - cmd_params.get_parameters(device_config)?; - cmd_params - .get_value::("")? - .with_context(|| "Missing driver field.") -} - -pub fn parse_device_id(device_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("device"); - cmd_parser.push("id"); - - cmd_parser.get_parameters(device_config)?; - if let Some(id) = cmd_parser.get_value::("id")? { - Ok(id) - } else { - Ok(String::new()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_device_id() { - let test_conf = "virtio-blk-device,drive=rootfs,id=blkid"; - let ret = parse_device_id(test_conf); - assert!(ret.is_ok()); - let id = ret.unwrap(); - assert_eq!("blkid", id); - - let test_conf = "virtio-blk-device,drive=rootfs"; - let ret = parse_device_id(test_conf); - assert!(ret.is_ok()); - let id = ret.unwrap(); - assert_eq!("", id); - } -} diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index f6a5d9a25..ab6c0edfe 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -204,14 +204,9 @@ impl VmConfig { /// /// * `object_args` - The args of object. pub fn add_object(&mut self, object_args: &str) -> Result<()> { - let mut cmd_params = CmdParser::new("object"); - cmd_params.push(""); - - cmd_params.get_parameters(object_args)?; - let device_type = cmd_params - .get_value::("")? - .with_context(|| "Object type not specified")?; - match device_type.as_str() { + let object_type = + get_class_type(object_args).with_context(|| "Object type not specified")?; + match object_type.as_str() { "iothread" => { self.add_iothread(object_args) .with_context(|| "Failed to add iothread")?; @@ -237,7 +232,7 @@ impl VmConfig { self.add_saslauth(object_args)?; } _ => { - bail!("Unknow object type: {:?}", &device_type); + bail!("Unknow object type: {:?}", &object_type); } } @@ -746,6 +741,14 @@ macro_rules! check_arg_nonexist{ } } +fn concat_classtype(args: &str, concat: bool) -> String { + if concat { + format!("classtype={}", args) + } else { + args.to_string() + } +} + /// Configure StratoVirt parameters in clap format. /// /// The first parameter will be parsed as the `binary name` unless Command::no_binary_name is used when using `clap`. @@ -776,11 +779,7 @@ pub fn str_slip_to_clap( first_pos_is_subcommand: bool, ) -> Vec { let mut subcommand = first_pos_is_subcommand; - let args_str = if first_pos_is_type && !subcommand { - format!("classtype={}", args) - } else { - args.to_string() - }; + let args_str = concat_classtype(args, first_pos_is_type && !subcommand); let args_vecs = args_str.split([',']).collect::>(); let mut itr: Vec = Vec::with_capacity(args_vecs.len() * 2); for params in args_vecs { @@ -821,6 +820,11 @@ pub fn get_value_of_parameter(parameter: &str, args_str: &str) -> Result bail!("Cannot find {}'s value from string {}", parameter, args_str); } +pub fn get_class_type(args: &str) -> Result { + let args_str = concat_classtype(args, true); + get_value_of_parameter("classtype", &args_str) +} + pub fn valid_id(id: &str) -> Result { check_arg_too_long(id, "id")?; Ok(id.to_string()) diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index a9a0bfa3c..27da8260f 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -16,6 +16,7 @@ use std::collections::{BTreeMap, HashSet}; use anyhow::{anyhow, bail, Context, Result}; use super::error::ConfigError; +use super::get_class_type; use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; const MIN_NUMA_DISTANCE: u8 = 10; @@ -238,13 +239,8 @@ impl VmConfig { /// /// * `numa_config` - The NUMA node configuration. pub fn add_numa(&mut self, numa_config: &str) -> Result<()> { - let mut cmd_params = CmdParser::new("numa"); - cmd_params.push(""); - - cmd_params.get_parameters(numa_config)?; - if let Some(numa_type) = cmd_params.get_value::("")? { - self.numa_nodes.push((numa_type, numa_config.to_string())); - } + let numa_type = get_class_type(numa_config).with_context(|| "Numa type not specified")?; + self.numa_nodes.push((numa_type, numa_config.to_string())); Ok(()) } @@ -258,17 +254,15 @@ mod tests { fn test_parse_numa_mem() { let mut vm_config = VmConfig::default(); assert!(vm_config - .add_numa("-numa node,nodeid=0,cpus=0-1,memdev=mem0") - .is_ok()); - assert!(vm_config - .add_numa("-numa node,nodeid=1,cpus=2-1,memdev=mem1") + .add_numa("node,nodeid=0,cpus=0-1,memdev=mem0") .is_ok()); assert!(vm_config - .add_numa("-numa node,nodeid=2,memdev=mem2") + .add_numa("node,nodeid=1,cpus=2-1,memdev=mem1") .is_ok()); - assert!(vm_config.add_numa("-numa node,nodeid=3,cpus=3-4").is_ok()); + assert!(vm_config.add_numa("node,nodeid=2,memdev=mem2").is_ok()); + assert!(vm_config.add_numa("node,nodeid=3,cpus=3-4").is_ok()); assert!(vm_config - .add_numa("-numa node,nodeid=0,cpus=[0-1:3-5],memdev=mem0") + .add_numa("node,nodeid=0,cpus=[0-1:3-5],memdev=mem0") .is_ok()); let numa = vm_config.numa_nodes.get(0).unwrap(); @@ -291,11 +285,11 @@ mod tests { #[test] fn test_parse_numa_distance() { let mut vm_config = VmConfig::default(); - assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=15").is_ok()); - assert!(vm_config.add_numa("-numa dist,dst=1,val=10").is_ok()); - assert!(vm_config.add_numa("-numa dist,src=0,val=10").is_ok()); - assert!(vm_config.add_numa("-numa dist,src=0,dst=1").is_ok()); - assert!(vm_config.add_numa("-numa dist,src=0,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("dist,src=0,dst=1,val=15").is_ok()); + assert!(vm_config.add_numa("dist,dst=1,val=10").is_ok()); + assert!(vm_config.add_numa("dist,src=0,val=10").is_ok()); + assert!(vm_config.add_numa("dist,src=0,dst=1").is_ok()); + assert!(vm_config.add_numa("dist,src=0,dst=1,val=10").is_ok()); let numa = vm_config.numa_nodes.get(0).unwrap(); let dist = parse_numa_distance(numa.1.as_str()).unwrap(); diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index cad97decb..4f054e900 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -13,7 +13,7 @@ use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; -use super::CmdParser; +use super::{get_value_of_parameter, CmdParser}; use crate::config::ExBool; use util::num_ops::str_to_num; @@ -71,21 +71,14 @@ pub fn get_pci_df(addr: &str) -> Result<(u8, u8)> { } pub fn get_pci_bdf(pci_cfg: &str) -> Result { - let mut cmd_parser = CmdParser::new("bdf"); - cmd_parser.push("").push("bus").push("addr"); - cmd_parser.get_parameters(pci_cfg)?; - - let mut pci_bdf = PciBdf { - bus: cmd_parser - .get_value::("bus")? - .with_context(|| "Bus not specified for pci device")?, - ..Default::default() - }; - if let Some(addr) = cmd_parser.get_value::("addr")? { - pci_bdf.addr = get_pci_df(&addr).with_context(|| "Failed to get addr")?; - } else { - bail!("No addr found for pci device"); + let bus = get_value_of_parameter("bus", pci_cfg)?; + let addr_str = get_value_of_parameter("addr", pci_cfg)?; + if addr_str.is_empty() { + bail!("Invalid addr."); } + let addr = get_pci_df(&addr_str).with_context(|| "Failed to get addr")?; + let pci_bdf = PciBdf::new(bus, addr); + Ok(pci_bdf) } -- Gitee From 9d007c70b87c341300aa4a447cfa1a0c4babe0f8 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 19 May 2024 20:32:29 +0800 Subject: [PATCH 1827/2187] Devices: add trait object conversion function Stratovirt will implement DEVICE/BUS framework as a simple simplified `QOM`. Add Trait object conversion function to make preparation for the next commits. Signed-off-by: liuxiangdong --- devices/src/lib.rs | 6 ++- devices/src/misc/mod.rs | 2 +- devices/src/pci/mod.rs | 79 ++++++++++++++++++++++++++++++------- devices/src/sysbus/mod.rs | 80 +++++++++++++++++++++++++++++++++++++- machine/src/aarch64/mod.rs | 2 +- machine/src/lib.rs | 42 +++++++++++++++++--- machine/src/x86_64/mod.rs | 3 +- src/main.rs | 4 +- vfio/src/lib.rs | 6 +++ virtio/src/lib.rs | 10 +++++ 10 files changed, 208 insertions(+), 26 deletions(-) diff --git a/devices/src/lib.rs b/devices/src/lib.rs index a155ff643..0486bf1e8 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -39,6 +39,10 @@ pub use legacy::error::LegacyError as LegacyErrs; pub use scsi::bus as ScsiBus; pub use scsi::disk as ScsiDisk; +use std::any::Any; + +use util::AsAny; + #[derive(Clone, Default)] pub struct DeviceBase { /// Name of this device @@ -53,7 +57,7 @@ impl DeviceBase { } } -pub trait Device { +pub trait Device: Any + AsAny { fn device_base(&self) -> &DeviceBase; fn device_base_mut(&mut self) -> &mut DeviceBase; diff --git a/devices/src/misc/mod.rs b/devices/src/misc/mod.rs index 36c2d9c5b..0e0c015a4 100644 --- a/devices/src/misc/mod.rs +++ b/devices/src/misc/mod.rs @@ -14,7 +14,7 @@ pub mod scream; #[cfg(feature = "scream")] -mod ivshmem; +pub mod ivshmem; #[cfg(feature = "pvpanic")] pub mod pvpanic; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index c29d7a0d0..7a90d6eac 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -30,20 +30,23 @@ pub use intx::{init_intx, InterruptHandler, PciIntxState}; pub use msix::{init_msix, MsiVector}; pub use root_port::{RootPort, RootPortConfig}; -use std::{ - mem::size_of, - sync::{Arc, Mutex, Weak}, -}; +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::mem::size_of; +use std::sync::{Arc, Mutex, Weak}; use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; -use crate::{ - pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}, - MsiIrqManager, -}; -use crate::{Device, DeviceBase}; -use util::AsAny; +#[cfg(feature = "scream")] +use crate::misc::ivshmem::Ivshmem; +#[cfg(feature = "pvpanic")] +use crate::misc::pvpanic::PvPanicPci; +use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; +use crate::usb::xhci::xhci_pci::XhciPciDevice; +use crate::{Device, DeviceBase, MsiIrqManager}; +#[cfg(feature = "demo_device")] +use demo_device::DemoDev; const BDF_FUNC_SHIFT: u8 = 3; pub const PCI_SLOT_MAX: u8 = 32; @@ -142,7 +145,7 @@ pub struct PciDevBase { pub parent_bus: Weak>, } -pub trait PciDevOps: Device + Send + AsAny { +pub trait PciDevOps: Device + Send { /// Get base property of pci device. fn pci_base(&self) -> &PciDevBase; @@ -270,6 +273,57 @@ pub trait PciDevOps: Device + Send + AsAny { } } +pub type ToPciDevOpsFunc = fn(&dyn Any) -> &dyn PciDevOps; + +static mut PCIDEVOPS_HASHMAP: Option> = None; + +pub fn convert_to_pcidevops(item: &dyn Any) -> &dyn PciDevOps { + // SAFETY: The typeid of `T` is the typeid recorded in the hashmap. The target structure type of + // the conversion is its own structure type, so the conversion result will definitely not be `None`. + let t = item.downcast_ref::().unwrap(); + t as &dyn PciDevOps +} + +pub fn register_pcidevops_type() -> Result<()> { + let type_id = TypeId::of::(); + // SAFETY: PCIDEVOPS_HASHMAP will be built in `type_init` function sequentially in the main thread. + // And will not be changed after `type_init`. + unsafe { + if PCIDEVOPS_HASHMAP.is_none() { + PCIDEVOPS_HASHMAP = Some(HashMap::new()); + } + let types = PCIDEVOPS_HASHMAP.as_mut().unwrap(); + if types.get(&type_id).is_some() { + bail!("Type Id {:?} has been registered.", type_id); + } + types.insert(type_id, convert_to_pcidevops::); + } + + Ok(()) +} + +pub fn devices_register_pcidevops_type() -> Result<()> { + #[cfg(feature = "scream")] + register_pcidevops_type::()?; + #[cfg(feature = "pvpanic")] + register_pcidevops_type::()?; + register_pcidevops_type::()?; + #[cfg(feature = "demo_device")] + register_pcidevops_type::()?; + register_pcidevops_type::() +} + +pub fn to_pcidevops(dev: &dyn Device) -> Option<&dyn PciDevOps> { + let type_id = dev.type_id(); + // SAFETY: PCIDEVOPS_HASHMAP has been built. And this function is called without changing hashmap. + unsafe { + let types = PCIDEVOPS_HASHMAP.as_mut().unwrap(); + let func = types.get(&type_id)?; + let pcidev = func(dev.as_any()); + Some(pcidev) + } +} + /// Init multifunction for pci devices. /// /// # Arguments @@ -349,11 +403,10 @@ pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { #[cfg(test)] mod tests { + use super::*; use crate::DeviceBase; use address_space::{AddressSpace, Region}; - use super::*; - #[test] fn test_le_write_u16_01() { let mut buf: [u8; 2] = [0; 2]; diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 36003cd1b..6083bb3f0 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -14,16 +14,30 @@ pub mod error; pub use error::SysBusError; +use std::any::{Any, TypeId}; +use std::collections::HashMap; use std::fmt; use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use vmm_sys_util::eventfd::EventFd; +#[cfg(target_arch = "x86_64")] +use crate::acpi::cpu_controller::CpuController; +use crate::acpi::ged::Ged; +#[cfg(target_arch = "aarch64")] +use crate::acpi::power::PowerDev; +#[cfg(all(feature = "ramfb", target_arch = "aarch64"))] +use crate::legacy::Ramfb; +#[cfg(target_arch = "x86_64")] +use crate::legacy::{FwCfgIO, RTC}; +#[cfg(target_arch = "aarch64")] +use crate::legacy::{FwCfgMem, PL011, PL031}; +use crate::legacy::{PFlash, Serial}; +use crate::pci::PciHost; use crate::{Device, DeviceBase, IrqState, LineIrqManager, TriggerMode}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; -use util::AsAny; // Now that the serial device use a hardcoded IRQ number (4), and the starting // free IRQ number can be 5. @@ -251,7 +265,7 @@ impl SysBusDevBase { } /// Operations for sysbus devices. -pub trait SysBusDevOps: Device + Send + AmlBuilder + AsAny { +pub trait SysBusDevOps: Device + Send + AmlBuilder { fn sysbusdev_base(&self) -> &SysBusDevBase; fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase; @@ -342,3 +356,65 @@ impl AmlBuilder for SysBus { scope.aml_bytes() } } + +pub type ToSysBusDevOpsFunc = fn(&dyn Any) -> &dyn SysBusDevOps; + +static mut SYSBUSDEVTYPE_HASHMAP: Option> = None; + +pub fn convert_to_sysbusdevops(item: &dyn Any) -> &dyn SysBusDevOps { + // SAFETY: The typeid of `T` is the typeid recorded in the hashmap. The target structure type of + // the conversion is its own structure type, so the conversion result will definitely not be `None`. + let t = item.downcast_ref::().unwrap(); + t as &dyn SysBusDevOps +} + +pub fn register_sysbusdevops_type() -> Result<()> { + let type_id = TypeId::of::(); + // SAFETY: SYSBUSDEVTYPE_HASHMAP will be built in `type_init` function sequentially in the main thread. + // And will not be changed after `type_init`. + unsafe { + if SYSBUSDEVTYPE_HASHMAP.is_none() { + SYSBUSDEVTYPE_HASHMAP = Some(HashMap::new()); + } + let types = SYSBUSDEVTYPE_HASHMAP.as_mut().unwrap(); + if types.get(&type_id).is_some() { + bail!("Type Id {:?} has been registered.", type_id); + } + types.insert(type_id, convert_to_sysbusdevops::); + } + + Ok(()) +} + +pub fn devices_register_sysbusdevops_type() -> Result<()> { + #[cfg(target_arch = "x86_64")] + { + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + } + #[cfg(target_arch = "aarch64")] + { + register_sysbusdevops_type::()?; + #[cfg(all(feature = "ramfb"))] + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + } + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + register_sysbusdevops_type::()?; + register_sysbusdevops_type::() +} + +pub fn to_sysbusdevops(dev: &dyn Device) -> Option<&dyn SysBusDevOps> { + let type_id = dev.type_id(); + // SAFETY: SYSBUSDEVTYPE_HASHMAP has been built. And this function is called without changing hashmap. + unsafe { + let types = SYSBUSDEVTYPE_HASHMAP.as_mut().unwrap(); + let func = types.get(&type_id)?; + let sysbusdev = func(dev.as_any()); + Some(sysbusdev) + } +} diff --git a/machine/src/aarch64/mod.rs b/machine/src/aarch64/mod.rs index ee107ad49..3ffe949fe 100644 --- a/machine/src/aarch64/mod.rs +++ b/machine/src/aarch64/mod.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod micro; +pub mod pci_host_root; pub mod standard; mod fdt; -mod pci_host_root; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 9bd6b29c3..94d07f5b3 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -52,10 +52,13 @@ use devices::misc::pvpanic::{PvPanicPci, PvpanicDevConfig}; use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] use devices::pci::demo_device::{DemoDev, DemoDevConfig}; -use devices::pci::{PciBus, PciDevOps, PciHost, RootPort, RootPortConfig}; +use devices::pci::{ + devices_register_pcidevops_type, register_pcidevops_type, PciBus, PciDevOps, PciHost, RootPort, + RootPortConfig, +}; use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; -use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType}; +use devices::sysbus::{devices_register_sysbusdevops_type, SysBus, SysBusDevOps, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; @@ -90,12 +93,13 @@ use util::{ arg_parser, seccomp::{BpfRule, SeccompOpt, SyscallFilter}, }; -use vfio::{VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; +use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; use virtio::{ - balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block, - BlockState, Rng, RngConfig, RngState, + balloon_allow_list, find_port_by_nr, get_max_nr, vhost, virtio_register_pcidevops_type, + virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, Rng, RngConfig, + RngState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}, Serial, SerialPort, VhostKern, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, @@ -2347,3 +2351,31 @@ fn check_windows_emu_pid( .unwrap() .timer_add(check_emu_alive, check_delay); } + +fn machine_register_pcidevops_type() -> Result<()> { + #[cfg(target_arch = "x86_64")] + { + register_pcidevops_type::()?; + register_pcidevops_type::()?; + } + #[cfg(target_arch = "aarch64")] + { + register_pcidevops_type::()?; + } + + Ok(()) +} + +pub fn type_init() -> Result<()> { + // Register all sysbus devices type. + virtio_register_sysbusdevops_type()?; + devices_register_sysbusdevops_type()?; + + // Register all pci devices type. + machine_register_pcidevops_type()?; + vfio_register_pcidevops_type()?; + virtio_register_pcidevops_type()?; + devices_register_pcidevops_type()?; + + Ok(()) +} diff --git a/machine/src/x86_64/mod.rs b/machine/src/x86_64/mod.rs index 47b4ecbe1..b3227f997 100644 --- a/machine/src/x86_64/mod.rs +++ b/machine/src/x86_64/mod.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. pub mod ich9_lpc; +pub mod mch; pub mod micro; pub mod standard; - -mod mch; diff --git a/src/main.rs b/src/main.rs index 5f0381032..4a7775646 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use thiserror::Error; -use machine::{LightMachine, MachineOps, StdMachine}; +use machine::{type_init, LightMachine, MachineOps, StdMachine}; use machine_manager::{ cmdline::{check_api_channel, create_args_parser, create_vmconfig}, config::MachineType, @@ -71,6 +71,8 @@ fn main() -> ExitCode { } fn run() -> Result<()> { + type_init()?; + let cmd_args = create_args_parser().get_matches()?; if cmd_args.is_present("mod-test") { diff --git a/vfio/src/lib.rs b/vfio/src/lib.rs index ab47b6cef..3d2705a39 100644 --- a/vfio/src/lib.rs +++ b/vfio/src/lib.rs @@ -28,9 +28,11 @@ use std::collections::HashMap; use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; +use anyhow::Result; use kvm_ioctls::DeviceFd; use once_cell::sync::Lazy; +use devices::pci::register_pcidevops_type; use vfio_dev::VfioGroup; pub static KVM_DEVICE_FD: Lazy>> = Lazy::new(|| Mutex::new(None)); @@ -38,3 +40,7 @@ pub static CONTAINERS: Lazy>>>> = Lazy::new(|| Mutex::new(HashMap::new())); pub static GROUPS: Lazy>>> = Lazy::new(|| Mutex::new(HashMap::new())); + +pub fn vfio_register_pcidevops_type() -> Result<()> { + register_pcidevops_type::() +} diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 58fbe4f07..1fed7f323 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -58,6 +58,8 @@ use log::{error, warn}; use vmm_sys_util::eventfd::EventFd; use address_space::{AddressSpace, RegionCache}; +use devices::pci::register_pcidevops_type; +use devices::sysbus::register_sysbusdevops_type; use machine_manager::config::ConfigCheck; use migration_derive::ByteCode; use util::aio::{mem_to_buf, Iovec}; @@ -867,3 +869,11 @@ fn gpa_hva_iovec_map_by_cache( Ok((iov_size, hva_iovec)) } + +pub fn virtio_register_sysbusdevops_type() -> Result<()> { + register_sysbusdevops_type::() +} + +pub fn virtio_register_pcidevops_type() -> Result<()> { + register_pcidevops_type::() +} -- Gitee From dc58c6e83c56ddac26ddb335640ff86184a5a846 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 21 May 2024 19:37:13 +0800 Subject: [PATCH 1828/2187] Camera: fix "create session"'s position We just call create_session before start stream, not when camera reset. Signed-off-by: zhanghan64 --- util/src/ohos_binding/camera.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/src/ohos_binding/camera.rs b/util/src/ohos_binding/camera.rs index c481403eb..e481e887d 100644 --- a/util/src/ohos_binding/camera.rs +++ b/util/src/ohos_binding/camera.rs @@ -110,6 +110,9 @@ impl OhCamera { ) -> Result<()> { // SAFETY: We call related API sequentially for specified ctx. unsafe { + if (self.capi.create_session)(self.ctx) != 0 { + bail!("OH Camera: failed to create session"); + } if (self.capi.pre_start)(self.ctx, buffer_proc, broken_proc) != 0 { bail!("OH Camera: failed to prestart camera stream"); } @@ -123,7 +126,6 @@ impl OhCamera { pub fn reset_camera(&self) { // SAFETY: We call related API sequentially for specified ctx. unsafe { - (self.capi.create_session)(self.ctx); (self.capi.init_cameras)(self.ctx); (self.capi.init_profiles)(self.ctx); } -- Gitee From 867f2425b9022e53822f1d1c869a5098f0249647 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 May 2024 15:35:46 +0800 Subject: [PATCH 1829/2187] accel: use clap to parse the parameters of the accel config Use clap to parse the parameters of the accel config. Signed-off-by: liuxiangdong --- machine_manager/src/config/machine_config.rs | 39 ++++++++++++++------ machine_manager/src/machine.rs | 8 ++-- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index fa277b03a..20b52fe10 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -251,6 +251,13 @@ impl ConfigCheck for MachineConfig { } } +#[derive(Parser)] +#[command(no_binary_name(true))] +struct AccelConfig { + #[arg(long, alias = "classtype")] + hypervisor: HypervisorType, +} + impl VmConfig { /// Add argument `name` to `VmConfig`. /// @@ -317,17 +324,8 @@ impl VmConfig { /// Add '-accel' accelerator config to `VmConfig`. pub fn add_accel(&mut self, accel_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("accel"); - cmd_parser.push(""); - cmd_parser.parse(accel_config)?; - - if let Some(accel) = cmd_parser - .get_value::("") - .with_context(|| "Only \'kvm\' and \'test\' is supported for \'accel\'")? - { - self.machine_config.hypervisor = accel; - } - + let accel_cfg = AccelConfig::try_parse_from(str_slip_to_clap(accel_config, true, false))?; + self.machine_config.hypervisor = accel_cfg.hypervisor; Ok(()) } @@ -1083,4 +1081,23 @@ mod tests { let result = vm_config.add_cpu_feature("host,sve=false"); assert!(result.is_err()); } + + #[test] + fn test_add_accel() { + let mut vm_config = VmConfig::default(); + let accel_cfg = "kvm"; + assert!(vm_config.add_accel(accel_cfg).is_ok()); + let machine_cfg = vm_config.machine_config; + assert_eq!(machine_cfg.hypervisor, HypervisorType::Kvm); + + let mut vm_config = VmConfig::default(); + let accel_cfg = "kvm:tcg"; + assert!(vm_config.add_accel(accel_cfg).is_ok()); + let machine_cfg = vm_config.machine_config; + assert_eq!(machine_cfg.hypervisor, HypervisorType::Kvm); + + let mut vm_config = VmConfig::default(); + let accel_cfg = "kvm1"; + assert!(vm_config.add_accel(accel_cfg).is_err()); + } } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index ff4d85863..a040d6efa 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -14,6 +14,7 @@ use std::os::unix::io::RawFd; use std::str::FromStr; use std::sync::Mutex; +use anyhow::anyhow; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use strum::VariantNames; @@ -54,13 +55,14 @@ pub enum HypervisorType { } impl FromStr for HypervisorType { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { match s { - "kvm" => Ok(HypervisorType::Kvm), + // Note: "kvm:tcg" is a configuration compatible with libvirt. + "kvm" | "kvm:tcg" => Ok(HypervisorType::Kvm), "test" => Ok(HypervisorType::Test), - _ => Err(()), + _ => Err(anyhow!("Not supported or invalid hypervisor type {}.", s)), } } } -- Gitee From 4e432f495751c0a8728d709b2d4d5b732d9d806f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 18 May 2024 16:20:54 +0800 Subject: [PATCH 1830/2187] qmp/mon: use clap to parse the parameters of the qmp/mon config Use clap to parse the parameters of the qmp/mon config. Signed-off-by: liuxiangdong --- machine_manager/src/cmdline.rs | 70 ++++++++++++++++------------------ 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index de8e54c4e..619a7c2b1 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -11,9 +11,10 @@ // See the Mulan PSL v2 for more details. use anyhow::{bail, Context, Result}; +use clap::{ArgAction, Parser}; use crate::{ - config::{add_trace, ChardevType, CmdParser, MachineType, SocketType, VmConfig}, + config::{add_trace, str_slip_to_clap, ChardevType, MachineType, SocketType, VmConfig}, qmp::qmp_socket::QmpSocketPath, temp_cleaner::TempCleaner, }; @@ -599,6 +600,28 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { Ok(vm_cfg) } +#[derive(Parser)] +#[command(no_binary_name(true))] +struct QmpConfig { + #[arg(long, alias = "classtype")] + uri: String, + #[arg(long, action = ArgAction::SetTrue, required = true)] + server: bool, + #[arg(long, action = ArgAction::SetTrue, required = true)] + nowait: bool, +} + +#[derive(Parser)] +#[command(no_binary_name(true))] +struct MonConfig { + #[arg(long, default_value = "")] + id: String, + #[arg(long, value_parser = ["control"])] + mode: String, + #[arg(long)] + chardev: String, +} + /// This function is to parse qmp socket path and type. /// /// # Arguments @@ -613,45 +636,18 @@ pub fn check_api_channel( vm_config: &mut VmConfig, ) -> Result> { let mut sock_paths = Vec::new(); - if let Some(qmp_config) = args.value_of("qmp") { - let mut cmd_parser = CmdParser::new("qmp"); - cmd_parser.push("").push("server").push("nowait"); - - cmd_parser.parse(&qmp_config)?; - if let Some(uri) = cmd_parser.get_value::("")? { - let sock_path = - QmpSocketPath::new(uri).with_context(|| "Failed to parse qmp socket path")?; - sock_paths.push(sock_path); - } else { - bail!("No uri found for qmp"); - } - if cmd_parser.get_value::("server")?.is_none() { - bail!("Argument \'server\' is needed for qmp"); - } - if cmd_parser.get_value::("nowait")?.is_none() { - bail!("Argument \'nowait\' is needed for qmp"); - } + if let Some(qmp_args) = args.value_of("qmp") { + let qmp_cfg = QmpConfig::try_parse_from(str_slip_to_clap(&qmp_args, true, false))?; + let sock_path = + QmpSocketPath::new(qmp_cfg.uri).with_context(|| "Failed to parse qmp socket path")?; + sock_paths.push(sock_path); } - if let Some(mon_config) = args.value_of("mon") { - let mut cmd_parser = CmdParser::new("monitor"); - cmd_parser.push("id").push("mode").push("chardev"); - cmd_parser.parse(&mon_config)?; - - let chardev = cmd_parser - .get_value::("chardev")? - .with_context(|| "Argument \'chardev\' is missing for \'mon\'")?; - - let mode = cmd_parser - .get_value::("mode")? - .with_context(|| "Argument \'mode\' of \'mon\' should be set to \'control\'.")?; - if mode != "control" { - bail!("Invalid \'mode\' parameter: {:?} for monitor", &mode); - } - + if let Some(mon_args) = args.value_of("mon") { + let mon_cfg = MonConfig::try_parse_from(str_slip_to_clap(&mon_args, false, false))?; let cfg = vm_config .chardev - .remove(&chardev) - .with_context(|| format!("No chardev found: {}", &chardev))?; + .remove(&mon_cfg.chardev) + .with_context(|| format!("No chardev found: {}", &mon_cfg.chardev))?; let socket = cfg .classtype .socket_type() -- Gitee From c68feac2d1e4cc57de016daff0c421bb3770b190 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 05:10:53 +0800 Subject: [PATCH 1831/2187] numa: use clap to parse the parameters of the numa config Use clap to parse the parameters of the numa config Signed-off-by: liuxiangdong --- machine/src/lib.rs | 39 +++--- machine_manager/src/config/numa.rs | 198 +++++++++++------------------ 2 files changed, 91 insertions(+), 146 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 94d07f5b3..1ae93787c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -79,8 +79,8 @@ use machine_manager::config::{ complete_numa_node, get_chardev_socket_path, get_class_type, get_pci_bdf, get_value_of_parameter, parse_numa_distance, parse_numa_mem, str_slip_to_clap, BootIndexInfo, BootSource, ConfigCheck, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, - NetworkInterfaceConfig, NumaConfig, NumaDistance, NumaNode, NumaNodes, PciBdf, SerialConfig, - VirtioSerialInfo, VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, SerialConfig, VirtioSerialInfo, + VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; @@ -1571,46 +1571,47 @@ pub trait MachineOps { for numa in vm_config.numa_nodes.iter() { match numa.0.as_str() { "node" => { - let numa_config: NumaConfig = parse_numa_mem(numa.1.as_str())?; - if numa_nodes.contains_key(&numa_config.numa_id) { - bail!("Numa node id is repeated {}", numa_config.numa_id); + let node_config = parse_numa_mem(numa.1.as_str())?; + if numa_nodes.contains_key(&node_config.numa_id) { + bail!("Numa node id is repeated {}", node_config.numa_id); } let mut numa_node = NumaNode { - cpus: numa_config.cpus, - mem_dev: numa_config.mem_dev.clone(), + cpus: node_config.cpus, + mem_dev: node_config.mem_dev.clone(), ..Default::default() }; numa_node.size = vm_config .object .mem_object - .remove(&numa_config.mem_dev) + .remove(&node_config.mem_dev) .map(|mem_conf| mem_conf.size) .with_context(|| { format!( "Object for memory-backend {} config not found", - numa_config.mem_dev + node_config.mem_dev ) })?; - numa_nodes.insert(numa_config.numa_id, numa_node); + numa_nodes.insert(node_config.numa_id, numa_node); } "dist" => { - let dist: (u32, NumaDistance) = parse_numa_distance(numa.1.as_str())?; - if !numa_nodes.contains_key(&dist.0) { - bail!("Numa node id is not found {}", dist.0); + let dist_config = parse_numa_distance(numa.1.as_str())?; + if !numa_nodes.contains_key(&dist_config.numa_id) { + bail!("Numa node id is not found {}", dist_config.numa_id); } - if !numa_nodes.contains_key(&dist.1.destination) { - bail!("Numa node id is not found {}", dist.1.destination); + if !numa_nodes.contains_key(&dist_config.destination) { + bail!("Numa node id is not found {}", dist_config.destination); } - if let Some(n) = numa_nodes.get_mut(&dist.0) { - if n.distances.contains_key(&dist.1.destination) { + if let Some(n) = numa_nodes.get_mut(&dist_config.numa_id) { + if n.distances.contains_key(&dist_config.destination) { bail!( "Numa destination info {} repeat settings", - dist.1.destination + dist_config.destination ); } - n.distances.insert(dist.1.destination, dist.1.distance); + n.distances + .insert(dist_config.destination, dist_config.distance); } } _ => { diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 27da8260f..664b18038 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -12,30 +12,17 @@ use std::cmp::max; use std::collections::{BTreeMap, HashSet}; +use std::str::FromStr; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; +use clap::Parser; use super::error::ConfigError; -use super::get_class_type; -use crate::config::{CmdParser, IntegerList, VmConfig, MAX_NODES}; +use super::{get_class_type, str_slip_to_clap}; +use crate::config::{IntegerList, VmConfig, MAX_NODES}; const MIN_NUMA_DISTANCE: u8 = 10; -#[derive(Default, Debug)] -pub struct NumaDistance { - pub destination: u32, - pub distance: u8, -} - -#[derive(Default, Debug)] -pub struct NumaConfig { - pub numa_id: u32, - pub cpus: Vec, - pub distances: Option>, - pub size: u64, - pub mem_dev: String, -} - #[derive(Default)] pub struct NumaNode { pub cpus: Vec, @@ -110,126 +97,83 @@ pub fn complete_numa_node(numa_nodes: &mut NumaNodes, nr_cpus: u8, mem_size: u64 Ok(()) } -/// Parse the NUMA node memory parameters. -/// -/// # Arguments -/// -/// * `numa_config` - The NUMA node configuration. -pub fn parse_numa_mem(numa_config: &str) -> Result { - let mut cmd_parser = CmdParser::new("numa"); - cmd_parser - .push("") - .push("nodeid") - .push("cpus") - .push("memdev"); - cmd_parser.parse(numa_config)?; - - let mut config: NumaConfig = NumaConfig::default(); - if let Some(node_id) = cmd_parser.get_value::("nodeid")? { - if node_id >= MAX_NODES { - return Err(anyhow!(ConfigError::IllegalValue( - "nodeid".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ))); - } - config.numa_id = node_id; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "nodeid".to_string(), - "numa".to_string() - ))); - } - if let Some(mut cpus) = cmd_parser - .get_value::("cpus") +#[derive(Parser)] +#[command(no_binary_name(true))] +pub struct NumaNodeConfig { + #[arg(long, value_parser = ["node"])] + pub classtype: String, + #[arg(long, alias = "nodeid", value_parser = clap::value_parser!(u32).range(..MAX_NODES as i64))] + pub numa_id: u32, + #[arg(long, value_parser = get_cpus)] + pub cpus: ::std::vec::Vec, + #[arg(long, alias = "memdev")] + pub mem_dev: String, +} + +fn get_cpus(cpus_str: &str) -> Result> { + let mut cpus = IntegerList::from_str(cpus_str) .with_context(|| ConfigError::ConvertValueFailed(String::from("u8"), "cpus".to_string()))? - .map(|v| v.0.iter().map(|e| *e as u8).collect::>()) - { - cpus.sort_unstable(); - config.cpus = cpus; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "cpus".to_string(), - "numa".to_string() - ))); + .0 + .iter() + .map(|e| *e as u8) + .collect::>(); + + if cpus.is_empty() { + bail!("Got empty cpus list!"); } - config.mem_dev = cmd_parser - .get_value::("memdev")? - .with_context(|| ConfigError::FieldIsMissing("memdev".to_string(), "numa".to_string()))?; - Ok(config) + cpus.sort_unstable(); + + Ok(cpus) } -/// Parse the NUMA node distance parameters. +/// Parse the NUMA node memory parameters. /// /// # Arguments /// -/// * `numa_dist` - The NUMA node distance configuration. -pub fn parse_numa_distance(numa_dist: &str) -> Result<(u32, NumaDistance)> { - let mut cmd_parser = CmdParser::new("numa"); - cmd_parser.push("").push("src").push("dst").push("val"); - cmd_parser.parse(numa_dist)?; - - let mut dist: NumaDistance = NumaDistance::default(); - let numa_id = if let Some(src) = cmd_parser.get_value::("src")? { - if src >= MAX_NODES { - return Err(anyhow!(ConfigError::IllegalValue( - "src".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ))); - } - src - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "src".to_string(), - "numa".to_string() - ))); - }; - if let Some(dst) = cmd_parser.get_value::("dst")? { - if dst >= MAX_NODES { - return Err(anyhow!(ConfigError::IllegalValue( - "dst".to_string(), - 0, - true, - MAX_NODES as u64, - false, - ))); - } - dist.destination = dst; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "dst".to_string(), - "numa".to_string() - ))); - } - if let Some(val) = cmd_parser.get_value::("val")? { - if val < MIN_NUMA_DISTANCE { - bail!("NUMA distance shouldn't be less than 10"); - } - if numa_id == dist.destination && val != MIN_NUMA_DISTANCE { - bail!("Local distance of node {} should be 10.", numa_id); +/// * `numa_config` - The NUMA node configuration. +pub fn parse_numa_mem(numa_config: &str) -> Result { + let config = NumaNodeConfig::try_parse_from(str_slip_to_clap(numa_config, true, false))?; + Ok(config) +} + +#[derive(Parser)] +#[command(no_binary_name(true))] +pub struct NumaDistConfig { + #[arg(long, value_parser = ["dist"])] + pub classtype: String, + #[arg(long, alias = "src", value_parser = clap::value_parser!(u32).range(..MAX_NODES as i64))] + pub numa_id: u32, + #[arg(long, alias = "dst", value_parser = clap::value_parser!(u32).range(..MAX_NODES as i64))] + pub destination: u32, + #[arg(long, alias = "val", value_parser = clap::value_parser!(u8).range(MIN_NUMA_DISTANCE as i64..))] + pub distance: u8, +} + +impl NumaDistConfig { + fn check(&self) -> Result<()> { + if self.numa_id == self.destination && self.distance != MIN_NUMA_DISTANCE { + bail!("Local distance of node {} should be 10.", self.numa_id); } - if numa_id != dist.destination && val == MIN_NUMA_DISTANCE { + if self.numa_id != self.destination && self.distance == MIN_NUMA_DISTANCE { bail!( "Remote distance of node {} should be more than 10.", - numa_id + self.numa_id ); } - - dist.distance = val; - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "val".to_string(), - "numa".to_string() - ))); + Ok(()) } +} - Ok((numa_id, dist)) +/// Parse the NUMA node distance parameters. +/// +/// # Arguments +/// +/// * `numa_dist` - The NUMA node distance configuration. +pub fn parse_numa_distance(numa_dist: &str) -> Result { + let dist_cfg = NumaDistConfig::try_parse_from(str_slip_to_clap(numa_dist, true, false))?; + dist_cfg.check()?; + Ok(dist_cfg) } impl VmConfig { @@ -292,10 +236,10 @@ mod tests { assert!(vm_config.add_numa("dist,src=0,dst=1,val=10").is_ok()); let numa = vm_config.numa_nodes.get(0).unwrap(); - let dist = parse_numa_distance(numa.1.as_str()).unwrap(); - assert_eq!(dist.0, 0); - assert_eq!(dist.1.destination, 1); - assert_eq!(dist.1.distance, 15); + let dist_cfg = parse_numa_distance(numa.1.as_str()).unwrap(); + assert_eq!(dist_cfg.numa_id, 0); + assert_eq!(dist_cfg.destination, 1); + assert_eq!(dist_cfg.distance, 15); let numa = vm_config.numa_nodes.get(1).unwrap(); assert!(parse_numa_distance(numa.1.as_str()).is_err()); -- Gitee From d066231e67c919d6eb80716d96d7bca65038774a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 05:58:49 +0800 Subject: [PATCH 1832/2187] memory size: use clap to parse the parameters of the memory size config Use clap to parse the parameters of the memory size config Signed-off-by: liuxiangdong --- machine_manager/src/config/machine_config.rs | 36 ++++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 20b52fe10..ec60bc7c1 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -17,7 +17,9 @@ use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; use super::error::ConfigError; -use super::{parse_bool, parse_size, str_slip_to_clap, valid_id, valid_path}; +use super::{ + get_value_of_parameter, parse_bool, parse_size, str_slip_to_clap, valid_id, valid_path, +}; use crate::config::{CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES}; use crate::machine::HypervisorType; @@ -258,6 +260,13 @@ struct AccelConfig { hypervisor: HypervisorType, } +#[derive(Parser)] +#[command(no_binary_name(true))] +struct MemSizeConfig { + #[arg(long, alias = "classtype", value_parser = parse_size)] + size: u64, +} + impl VmConfig { /// Add argument `name` to `VmConfig`. /// @@ -331,23 +340,14 @@ impl VmConfig { /// Add '-m' memory config to `VmConfig`. pub fn add_memory(&mut self, mem_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("m"); - cmd_parser.push("").push("size"); - - cmd_parser.parse(mem_config)?; - - let mem = if let Some(mem_size) = cmd_parser.get_value::("")? { - memory_unit_conversion(&mem_size, M)? - } else if let Some(mem_size) = cmd_parser.get_value::("size")? { - memory_unit_conversion(&mem_size, M)? - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "size".to_string(), - "memory".to_string() - ))); - }; - - self.machine_config.mem_config.mem_size = mem; + // Is there a "size=" prefix tag in the command line. + let mut has_size_label = false; + if get_value_of_parameter("size", mem_config).is_ok() { + has_size_label = true; + } + let mem_cfg = + MemSizeConfig::try_parse_from(str_slip_to_clap(mem_config, !has_size_label, false))?; + self.machine_config.mem_config.mem_size = mem_cfg.size; Ok(()) } -- Gitee From fc769b50b3696782cd6197ffe919fea60a53502b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 07:20:34 +0800 Subject: [PATCH 1833/2187] smbios: use clap to parse the parameters of the smbios config Use clap to parse the parameters of the smbios config. As of now, all `get_parameters` have been replaced by `get_value_of_parameter`, delete useless `get_parameters`. Signed-off-by: liuxiangdong --- devices/src/smbios/smbios_table.rs | 4 +- machine_manager/src/config/mod.rs | 42 ----- machine_manager/src/config/smbios.rs | 251 ++++++++++++--------------- 3 files changed, 116 insertions(+), 181 deletions(-) diff --git a/devices/src/smbios/smbios_table.rs b/devices/src/smbios/smbios_table.rs index 9211bd881..f476025b0 100644 --- a/devices/src/smbios/smbios_table.rs +++ b/devices/src/smbios/smbios_table.rs @@ -656,9 +656,9 @@ impl SmbiosTable { fn build_type0(&mut self, type0: SmbiosType0Config) { let mut table0: SmbiosType0Table = SmbiosType0Table::new(); - if let Some(vender) = type0.vender { + if let Some(vendor) = type0.vendor { table0.header.vendor_idx = table0.str_index + 1; - table0.set_str(vender); + table0.set_str(vendor); } if let Some(version) = type0.version { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 36ae7a407..d88d49378 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -500,48 +500,6 @@ impl CmdParser { Ok(()) } - /// Parse all cmdline parameters string into `params`. - /// - /// # Arguments - /// - /// * `cmd_param`: The whole cmdline parameter string. - fn get_parameters(&mut self, cmd_param: &str) -> Result<()> { - if cmd_param.starts_with(',') || cmd_param.ends_with(',') { - return Err(anyhow!(ConfigError::InvalidParam( - cmd_param.to_string(), - self.name.clone() - ))); - } - let param_items = cmd_param.split(',').collect::>(); - for param_item in param_items { - let param = param_item.splitn(2, '=').collect::>(); - let (param_key, param_value) = match param.len() { - 1 => ("", param[0]), - 2 => (param[0], param[1]), - _ => { - return Err(anyhow!(ConfigError::InvalidParam( - param_item.to_string(), - self.name.clone() - ))); - } - }; - - if self.params.contains_key(param_key) { - let field_value = self.params.get_mut(param_key).unwrap(); - if field_value.is_none() { - *field_value = Some(String::from(param_value)); - } else { - return Err(anyhow!(ConfigError::FieldRepeat( - self.name.clone(), - param_key.to_string() - ))); - } - } - } - - Ok(()) - } - /// Get cmdline parameters value from param field name. /// /// # Arguments diff --git a/machine_manager/src/config/smbios.rs b/machine_manager/src/config/smbios.rs index 2c8f0d95d..75220f456 100644 --- a/machine_manager/src/config/smbios.rs +++ b/machine_manager/src/config/smbios.rs @@ -12,74 +12,138 @@ use std::str::FromStr; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Result}; +use clap::Parser; use serde::{Deserialize, Serialize}; -use crate::config::{CmdParser, VmConfig}; +use super::{get_value_of_parameter, str_slip_to_clap}; +use crate::config::VmConfig; -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Default, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SmbiosType0Config { - pub vender: Option, + #[arg(long, alias = "type", value_parser = ["0"])] + pub smbios_type: String, + #[arg(long)] + pub vendor: Option, + #[arg(long)] pub version: Option, + #[arg(long)] pub date: Option, + // Note: we don't set `ArgAction::Append` for `added`, so it cannot be specified + // from the command line, as command line will parse errors. + #[arg(long, default_value = "true")] pub added: bool, } -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Default, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SmbiosType1Config { + #[arg(long, alias = "type", value_parser = ["1"])] + pub smbios_type: String, + #[arg(long)] pub manufacturer: Option, + #[arg(long)] pub product: Option, + #[arg(long)] pub version: Option, + #[arg(long)] pub serial: Option, + #[arg(long)] pub sku: Option, + #[arg(long)] pub family: Option, + #[arg(long, value_parser = get_uuid)] pub uuid: Option, + #[arg(long, default_value = "true")] pub added: bool, } -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Default, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SmbiosType2Config { + #[arg(long, alias = "type", value_parser = ["2"])] + pub smbios_type: String, + #[arg(long)] pub manufacturer: Option, + #[arg(long)] pub product: Option, + #[arg(long)] pub version: Option, + #[arg(long)] pub serial: Option, + #[arg(long)] pub asset: Option, + #[arg(long)] pub location: Option, + #[arg(long, default_value = "true")] pub added: bool, } -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Default, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SmbiosType3Config { + #[arg(long, alias = "type", value_parser = ["3"])] + pub smbios_type: String, + #[arg(long)] pub manufacturer: Option, + #[arg(long)] pub version: Option, + #[arg(long)] pub serial: Option, + #[arg(long)] pub sku: Option, + #[arg(long)] pub asset: Option, + #[arg(long, default_value = "true")] pub added: bool, } -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Default, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SmbiosType4Config { + #[arg(long, alias = "type", value_parser = ["4"])] + pub smbios_type: String, + #[arg(long)] pub manufacturer: Option, + #[arg(long)] pub version: Option, + #[arg(long)] pub serial: Option, + #[arg(long)] pub asset: Option, + #[arg(long, alias = "sock_pfx")] pub sock_pfx: Option, + #[arg(long)] pub part: Option, + #[arg(long)] pub max_speed: Option, + #[arg(long)] pub current_speed: Option, + #[arg(long, default_value = "true")] pub added: bool, } -#[derive(Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Parser, Clone, Default, Debug, Serialize, Deserialize)] +#[command(no_binary_name(true))] pub struct SmbiosType17Config { + #[arg(long, alias = "type", value_parser = ["17"])] + pub smbios_type: String, + #[arg(long)] pub manufacturer: Option, + #[arg(long)] pub serial: Option, + #[arg(long)] pub asset: Option, + #[arg(long, alias = "loc_pfx")] pub loc_pfx: Option, + #[arg(long)] pub part: Option, + #[arg(long, default_value = "0")] pub speed: u16, + #[arg(long)] pub bank: Option, + #[arg(long, default_value = "true")] pub added: bool, } @@ -124,13 +188,13 @@ pub struct Uuid { } impl FromStr for Uuid { - type Err = (); + type Err = anyhow::Error; fn from_str(str: &str) -> std::result::Result { let name = str.to_string(); if !check_valid_uuid(&name) { - return Err(()); + return Err(anyhow!("Invalid uuid {}", name)); } let mut uuid_bytes = Vec::new(); @@ -149,6 +213,11 @@ impl FromStr for Uuid { } } +fn get_uuid(s: &str) -> Result { + let uuid = Uuid::from_str(s)?; + Ok(uuid) +} + impl VmConfig { /// # Arguments /// @@ -158,19 +227,8 @@ impl VmConfig { bail!("smbios type0 has been added"); } - let mut cmd_parser = CmdParser::new("smbios"); - cmd_parser - .push("") - .push("type") - .push("vendor") - .push("version") - .push("date"); - cmd_parser.parse(type0)?; - - self.smbios.type0.vender = cmd_parser.get_value::("vendor")?; - self.smbios.type0.version = cmd_parser.get_value::("version")?; - self.smbios.type0.date = cmd_parser.get_value::("date")?; - self.smbios.type0.added = true; + let type0_cfg = SmbiosType0Config::try_parse_from(str_slip_to_clap(type0, false, false))?; + self.smbios.type0 = type0_cfg; Ok(()) } @@ -183,27 +241,8 @@ impl VmConfig { bail!("smbios type1 has been added"); } - let mut cmd_parser = CmdParser::new("smbios"); - cmd_parser - .push("") - .push("type") - .push("manufacturer") - .push("product") - .push("version") - .push("serial") - .push("sku") - .push("uuid") - .push("family"); - cmd_parser.parse(type1)?; - - self.smbios.type1.manufacturer = cmd_parser.get_value::("manufacturer")?; - self.smbios.type1.product = cmd_parser.get_value::("product")?; - self.smbios.type1.version = cmd_parser.get_value::("version")?; - self.smbios.type1.serial = cmd_parser.get_value::("serial")?; - self.smbios.type1.sku = cmd_parser.get_value::("sku")?; - self.smbios.type1.family = cmd_parser.get_value::("family")?; - self.smbios.type1.uuid = cmd_parser.get_value::("uuid")?; - self.smbios.type1.added = true; + let type1_cfg = SmbiosType1Config::try_parse_from(str_slip_to_clap(type1, false, false))?; + self.smbios.type1 = type1_cfg; Ok(()) } @@ -215,26 +254,8 @@ impl VmConfig { if self.smbios.type2.added { bail!("smbios type2 has been added"); } - - let mut cmd_parser = CmdParser::new("smbios"); - cmd_parser - .push("") - .push("type") - .push("manufacturer") - .push("product") - .push("version") - .push("serial") - .push("asset") - .push("location"); - cmd_parser.parse(type2)?; - - self.smbios.type2.manufacturer = cmd_parser.get_value::("manufacturer")?; - self.smbios.type2.product = cmd_parser.get_value::("product")?; - self.smbios.type2.version = cmd_parser.get_value::("version")?; - self.smbios.type2.serial = cmd_parser.get_value::("serial")?; - self.smbios.type2.asset = cmd_parser.get_value::("asset")?; - self.smbios.type2.location = cmd_parser.get_value::("location")?; - self.smbios.type2.added = true; + let type2_cfg = SmbiosType2Config::try_parse_from(str_slip_to_clap(type2, false, false))?; + self.smbios.type2 = type2_cfg; Ok(()) } @@ -247,23 +268,8 @@ impl VmConfig { bail!("smbios type3 has been added"); } - let mut cmd_parser = CmdParser::new("smbios"); - cmd_parser - .push("") - .push("type") - .push("manufacturer") - .push("version") - .push("serial") - .push("sku") - .push("asset"); - cmd_parser.parse(type3)?; - - self.smbios.type3.manufacturer = cmd_parser.get_value::("manufacturer")?; - self.smbios.type3.version = cmd_parser.get_value::("version")?; - self.smbios.type3.serial = cmd_parser.get_value::("serial")?; - self.smbios.type3.sku = cmd_parser.get_value::("sku")?; - self.smbios.type3.asset = cmd_parser.get_value::("asset")?; - self.smbios.type3.added = true; + let type3_cfg = SmbiosType3Config::try_parse_from(str_slip_to_clap(type3, false, false))?; + self.smbios.type3 = type3_cfg; Ok(()) } @@ -276,29 +282,8 @@ impl VmConfig { bail!("smbios type4 has been added"); } - let mut cmd_parser = CmdParser::new("smbios"); - cmd_parser - .push("") - .push("type") - .push("manufacturer") - .push("version") - .push("serial") - .push("sock_pfx") - .push("max-speed") - .push("current-speed") - .push("part") - .push("asset"); - cmd_parser.parse(type4)?; - - self.smbios.type4.manufacturer = cmd_parser.get_value::("manufacturer")?; - self.smbios.type4.version = cmd_parser.get_value::("version")?; - self.smbios.type4.serial = cmd_parser.get_value::("serial")?; - self.smbios.type4.asset = cmd_parser.get_value::("asset")?; - self.smbios.type4.part = cmd_parser.get_value::("part")?; - self.smbios.type4.sock_pfx = cmd_parser.get_value::("sock_pfx")?; - self.smbios.type4.max_speed = cmd_parser.get_value::("max-speed")?; - self.smbios.type4.current_speed = cmd_parser.get_value::("current-speed")?; - self.smbios.type4.added = true; + let type4_cfg = SmbiosType4Config::try_parse_from(str_slip_to_clap(type4, false, false))?; + self.smbios.type4 = type4_cfg; Ok(()) } @@ -311,31 +296,9 @@ impl VmConfig { bail!("smbios type17 has been added"); } - let mut cmd_parser = CmdParser::new("smbios"); - cmd_parser - .push("") - .push("type") - .push("loc_pfx") - .push("bank") - .push("manufacturer") - .push("serial") - .push("speed") - .push("part") - .push("asset"); - cmd_parser.parse(type17)?; - - self.smbios.type17.manufacturer = cmd_parser.get_value::("manufacturer")?; - self.smbios.type17.loc_pfx = cmd_parser.get_value::("loc_pfx")?; - self.smbios.type17.serial = cmd_parser.get_value::("serial")?; - self.smbios.type17.asset = cmd_parser.get_value::("asset")?; - self.smbios.type17.part = cmd_parser.get_value::("part")?; - self.smbios.type17.speed = if let Some(speed) = cmd_parser.get_value::("speed")? { - speed - } else { - 0 - }; - self.smbios.type17.bank = cmd_parser.get_value::("bank")?; - self.smbios.type17.added = true; + let type17_cfg = + SmbiosType17Config::try_parse_from(str_slip_to_clap(type17, false, false))?; + self.smbios.type17 = type17_cfg; Ok(()) } @@ -346,13 +309,7 @@ impl VmConfig { /// /// * `smbios_args` - The args of object. pub fn add_smbios(&mut self, smbios_args: &str) -> Result<()> { - let mut cmd_params = CmdParser::new("smbios"); - cmd_params.push("").push("type"); - - cmd_params.get_parameters(smbios_args)?; - let smbios_type = cmd_params - .get_value::("type")? - .with_context(|| "smbios type not specified")?; + let smbios_type = get_value_of_parameter("type", smbios_args)?; match smbios_type.as_str() { "0" => { self.add_smbios_type0(smbios_args)?; @@ -397,4 +354,24 @@ mod test { ] ); } + + #[test] + fn test_add_smbios() { + let mut vm_config = VmConfig::default(); + + let smbios0 = "type=0,vendor=fake,version=fake,date=fake"; + let smbios1 = "type=1,manufacturer=fake,version=fake,product=fake,serial=fake,uuid=33DB4D5E-1FF7-401C-9657-7441C03DD766,sku=fake,family=fake"; + let smbios2 = "type=2,manufacturer=fake,product=fake,version=fake,serial=fake,asset=fake,location=fake"; + let smbios3 = "type=3,manufacturer=fake,version=fake,serial=fake,asset=fake,sku=fake"; + let smbios4 = "type=4,sock_pfx=fake,manufacturer=fake,version=fake,serial=fake,asset=fake,part=fake,max-speed=1,current-speed=1"; + let smbios17 = "type=17,loc_pfx=fake,bank=fake,manufacturer=fake,serial=fake,asset=fake,part=fake,speed=1"; + + assert!(vm_config.add_smbios(smbios0).is_ok()); + assert!(vm_config.add_smbios(smbios1).is_ok()); + assert!(vm_config.add_smbios(smbios2).is_ok()); + assert!(vm_config.add_smbios(smbios3).is_ok()); + assert!(vm_config.add_smbios(smbios4).is_ok()); + assert!(vm_config.add_smbios(smbios17).is_ok()); + assert!(vm_config.add_smbios(smbios0).is_err()); + } } -- Gitee From 0af0f8978dc47e98ed470bbb95efe18ca9c3235f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 11:25:11 +0800 Subject: [PATCH 1834/2187] machine: use clap to parse the parameters of the machine config Use clap to parse the parameters of the machine config Signed-off-by: liuxiangdong --- machine_manager/src/config/machine_config.rs | 96 ++++++++------------ 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index ec60bc7c1..2d6754271 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -20,7 +20,7 @@ use super::error::ConfigError; use super::{ get_value_of_parameter, parse_bool, parse_size, str_slip_to_clap, valid_id, valid_path, }; -use crate::config::{CmdParser, ConfigCheck, ExBool, IntegerList, VmConfig, MAX_NODES}; +use crate::config::{CmdParser, ConfigCheck, IntegerList, VmConfig, MAX_NODES}; use crate::machine::HypervisorType; const DEFAULT_CPUS: u8 = 1; @@ -47,7 +47,7 @@ pub enum MachineType { } impl FromStr for MachineType { - type Err = (); + type Err = anyhow::Error; fn from_str(s: &str) -> std::result::Result { match s.to_lowercase().as_str() { @@ -57,7 +57,7 @@ impl FromStr for MachineType { "q35" => Ok(MachineType::StandardVm), #[cfg(target_arch = "aarch64")] "virt" => Ok(MachineType::StandardVm), - _ => Err(()), + _ => Err(anyhow!("Invalid machine type.")), } } } @@ -267,6 +267,27 @@ struct MemSizeConfig { size: u64, } +#[derive(Parser)] +#[command(no_binary_name(true))] +struct MachineCmdConfig { + #[arg(long, aliases = ["classtype", "type"])] + mach_type: MachineType, + #[arg(long, default_value = "on", action = ArgAction::Append, value_parser = parse_bool)] + dump_guest_core: bool, + #[arg(long, default_value = "off", action = ArgAction::Append, value_parser = parse_bool)] + mem_share: bool, + #[arg(long, default_value = "kvm")] + accel: HypervisorType, + // The "usb" member is added for compatibility with libvirt and is currently not in use. + // It only supports configuration as "off". Currently, a `String` type is used to verify incoming values. + // When it will be used, it needs to be changed to a `bool` type. + #[arg(long, default_value = "off", value_parser = ["off"])] + usb: String, + #[cfg(target_arch = "aarch64")] + #[arg(long, default_value = "3", value_parser = clap::value_parser!(u8).range(3..=3))] + gic_version: u8, +} + impl VmConfig { /// Add argument `name` to `VmConfig`. /// @@ -274,59 +295,21 @@ impl VmConfig { /// /// * `name` - The name `String` added to `VmConfig`. pub fn add_machine(&mut self, mach_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("machine"); - cmd_parser - .push("") - .push("type") - .push("accel") - .push("usb") - .push("dump-guest-core") - .push("mem-share"); - #[cfg(target_arch = "aarch64")] - cmd_parser.push("gic-version"); - cmd_parser.parse(mach_config)?; - - #[cfg(target_arch = "aarch64")] - if let Some(gic_version) = cmd_parser.get_value::("gic-version")? { - if gic_version != 3 { - bail!("Unsupported gic version, only gicv3 is supported"); - } - } - - if let Some(accel) = cmd_parser.get_value::("accel")? { - // Libvirt checks the parameter types of 'kvm', 'kvm:tcg' and 'tcg'. - if accel.ne("kvm:tcg") && accel.ne("tcg") && accel.ne("kvm") && accel.ne("test") { - bail!("Only \'kvm\', \'kvm:tcg\', \'test\' and \'tcg\' are supported for \'accel\' of \'machine\'"); - } - - match accel.as_str() { - "test" => self.machine_config.hypervisor = HypervisorType::Test, - _ => self.machine_config.hypervisor = HypervisorType::Kvm, - }; - } - if let Some(usb) = cmd_parser.get_value::("usb")? { - if usb.into() { - bail!("Argument \'usb\' should be set to \'off\'"); - } - } - if let Some(mach_type) = cmd_parser - .get_value::("") - .with_context(|| "Unrecognized machine type")? - { - self.machine_config.mach_type = mach_type; - } - if let Some(mach_type) = cmd_parser - .get_value::("type") - .with_context(|| "Unrecognized machine type")? - { - self.machine_config.mach_type = mach_type; - } - if let Some(dump_guest) = cmd_parser.get_value::("dump-guest-core")? { - self.machine_config.mem_config.dump_guest_core = dump_guest.into(); - } - if let Some(mem_share) = cmd_parser.get_value::("mem-share")? { - self.machine_config.mem_config.mem_share = mem_share.into(); + let mut has_type_label = false; + if get_value_of_parameter("type", mach_config).is_ok() { + has_type_label = true; } + let mach_cfg = MachineCmdConfig::try_parse_from(str_slip_to_clap( + mach_config, + !has_type_label, + false, + ))?; + // TODO: The current "accel" configuration in "-machine" command line and "-accel" command line are not foolproof. + // Later parsing will overwrite first parsing. We will optimize this in the future. + self.machine_config.hypervisor = mach_cfg.accel; + self.machine_config.mach_type = mach_cfg.mach_type; + self.machine_config.mem_config.dump_guest_core = mach_cfg.dump_guest_core; + self.machine_config.mem_config.mem_share = mach_cfg.mem_share; Ok(()) } @@ -900,11 +883,12 @@ mod tests { assert_eq!(machine_cfg.mem_config.mem_share, true); let mut vm_config = VmConfig::default(); - let memory_cfg_str = "type=none,dump-guest-core=off,mem-share=off,accel=kvm,usb=off"; + let memory_cfg_str = "none,dump-guest-core=off,mem-share=off,accel=kvm,usb=off"; let machine_cfg_ret = vm_config.add_machine(memory_cfg_str); assert!(machine_cfg_ret.is_ok()); let machine_cfg = vm_config.machine_config; assert_eq!(machine_cfg.mach_type, MachineType::None); + assert_eq!(machine_cfg.hypervisor, HypervisorType::Kvm); assert_eq!(machine_cfg.mem_config.dump_guest_core, false); assert_eq!(machine_cfg.mem_config.mem_share, false); -- Gitee From 2472e3feb6faa32d114f09e9465bce0be0c8128f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 12:19:17 +0800 Subject: [PATCH 1835/2187] display: use clap to parse the parameters of the display config Use clap to parse the parameters of the display config Signed-off-by: liuxiangdong --- machine/src/aarch64/standard.rs | 6 +- machine/src/x86_64/standard.rs | 2 +- machine_manager/src/config/display.rs | 115 ++++++++++---------------- ui/src/ohui_srv/mod.rs | 2 +- 4 files changed, 47 insertions(+), 78 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index b803e7419..12fe4086f 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -361,7 +361,7 @@ impl StdMachine { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] fn add_ohui_server(&mut self, vm_config: &VmConfig) -> Result<()> { if let Some(dpy) = vm_config.display.as_ref() { - if !dpy.ohui_config.ohui { + if dpy.display_type != "ohui" { return Ok(()); } self.ohui_server = Some(Arc::new(OhUiServer::new(dpy.get_ui_path())?)); @@ -722,7 +722,7 @@ impl MachineOps for StdMachine { #[cfg(any(feature = "gtk", all(target_env = "ohos", feature = "ohui_srv")))] match vm_config.display { #[cfg(feature = "gtk")] - Some(ref ds_cfg) if ds_cfg.gtk => { + Some(ref ds_cfg) if ds_cfg.display_type == "gtk" => { let ui_context = UiContext { vm_name: vm_config.guest_name.clone(), power_button: Some(self.power_button.clone()), @@ -735,7 +735,7 @@ impl MachineOps for StdMachine { } // OHUI server init. #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - Some(ref ds_cfg) if ds_cfg.ohui_config.ohui => { + Some(ref ds_cfg) if ds_cfg.display_type == "ohui" => { ohui_init(self.ohui_server.as_ref().unwrap().clone(), ds_cfg) .with_context(|| "Failed to init OH UI server!")?; } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index c6804809a..0dc5da820 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -702,7 +702,7 @@ impl MachineOps for StdMachine { // GTK display init. #[cfg(feature = "gtk")] match vm_config.display { - Some(ref ds_cfg) if ds_cfg.gtk => { + Some(ref ds_cfg) if ds_cfg.display_type == "gtk" => { let ui_context = UiContext { vm_name: vm_config.guest_name.clone(), power_button: None, diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 8f1f2e088..a8eca8c0d 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -13,17 +13,15 @@ #[cfg(feature = "gtk")] use std::sync::Arc; +use anyhow::Result; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] -use anyhow::Context; -use anyhow::{bail, Result}; +use anyhow::{bail, Context}; +use clap::{ArgAction, Parser}; use serde::{Deserialize, Serialize}; #[cfg(feature = "gtk")] use vmm_sys_util::eventfd::EventFd; -use crate::config::{CmdParser, ExBool, VmConfig}; - -#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] -static DEFAULT_UI_PATH: &str = "/tmp/"; +use crate::config::{parse_bool, str_slip_to_clap, VmConfig}; /// Event fd related to power button in gtk. #[cfg(feature = "gtk")] @@ -41,88 +39,59 @@ pub struct UiContext { } #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct OhuiConfig { - /// Use OHUI. - pub ohui: bool, - /// Create the OHUI thread. - pub iothread: Option, - /// Confirm related files' path. - pub path: String, +fn get_sock_path(p: &str) -> Result { + let path = std::fs::canonicalize(p.clone()) + .with_context(|| format!("Failed to get real directory path: {:?}", p.clone()))?; + if !path.exists() { + bail!( + "The defined directory {:?} path doesn't exist", + path.as_os_str() + ); + } + if !path.is_dir() { + bail!( + "The defined socks-path {:?} is not directory", + path.as_os_str() + ); + } + + Ok(path.to_str().unwrap().to_string()) } /// GTK and OHUI related configuration. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Parser, Debug, Clone, Default, Serialize, Deserialize)] +#[command(no_binary_name(true))] + pub struct DisplayConfig { - /// Create the GTK thread. - pub gtk: bool, + #[arg(long, alias = "classtype", value_parser = ["gtk", "ohui"])] + pub display_type: String, /// App name if configured. + #[arg(long)] pub app_name: Option, /// Keep the window fill the desktop. + #[arg(long, default_value = "off", action = ArgAction::Append, value_parser = parse_bool)] pub full_screen: bool, - /// Used for OHUI + /// Create the OHUI thread. #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - pub ohui_config: OhuiConfig, + #[arg(long)] + pub iothread: Option, + /// Confirm related files' path. Default ui path is "/tmp". + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + #[arg(long, alias = "socks-path", default_value = "/tmp/", value_parser = get_sock_path)] + pub path: String, } #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] impl DisplayConfig { pub fn get_ui_path(&self) -> String { - self.ohui_config.path.clone() + self.path.clone() } } impl VmConfig { pub fn add_display(&mut self, vm_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("display"); - cmd_parser.push("").push("full-screen").push("app-name"); - #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - cmd_parser.push("iothread").push("socks-path"); - cmd_parser.parse(vm_config)?; - let mut display_config = DisplayConfig::default(); - if let Some(str) = cmd_parser.get_value::("")? { - match str.as_str() { - "gtk" => display_config.gtk = true, - #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - "ohui" => display_config.ohui_config.ohui = true, - _ => bail!("Unsupported device: {}", str), - } - } - if let Some(name) = cmd_parser.get_value::("app-name")? { - display_config.app_name = Some(name); - } - if let Some(default) = cmd_parser.get_value::("full-screen")? { - display_config.full_screen = default.into(); - } - - #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - if display_config.ohui_config.ohui { - if let Some(iothread) = cmd_parser.get_value::("iothread")? { - display_config.ohui_config.iothread = Some(iothread); - } - - if let Some(path) = cmd_parser.get_value::("socks-path")? { - let path = std::fs::canonicalize(path.clone()).with_context(|| { - format!("Failed to get real directory path: {:?}", path.clone()) - })?; - if !path.exists() { - bail!( - "The defined directory {:?} path doesn't exist", - path.as_os_str() - ); - } - if !path.is_dir() { - bail!( - "The defined socks-path {:?} is not directory", - path.as_os_str() - ); - } - display_config.ohui_config.path = path.to_str().unwrap().to_string(); - } else { - display_config.ohui_config.path = DEFAULT_UI_PATH.to_string(); - } - } - + let display_config = + DisplayConfig::try_parse_from(str_slip_to_clap(vm_config, true, false))?; self.display = Some(display_config); Ok(()) } @@ -141,28 +110,28 @@ mod tests { let config_line = "gtk"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); - assert_eq!(display_config.gtk, true); + assert_eq!(display_config.display_type, "gtk"); assert_eq!(display_config.full_screen, false); let mut vm_config = VmConfig::default(); let config_line = "gtk,full-screen=on"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); - assert_eq!(display_config.gtk, true); + assert_eq!(display_config.display_type, "gtk"); assert_eq!(display_config.full_screen, true); let mut vm_config = VmConfig::default(); let config_line = "gtk,full-screen=off"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); - assert_eq!(display_config.gtk, true); + assert_eq!(display_config.display_type, "gtk"); assert_eq!(display_config.full_screen, false); let mut vm_config = VmConfig::default(); let config_line = "gtk,app-name=desktopappengine"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); - assert_eq!(display_config.gtk, true); + assert_eq!(display_config.display_type, "gtk"); assert_eq!(display_config.full_screen, false); assert_eq!( display_config.app_name, diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 8f72b132c..1d25483a5 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -373,7 +373,7 @@ impl DisplayChangeListenerOperations for OhUiServer { pub fn ohui_init(ohui_srv: Arc, cfg: &DisplayConfig) -> Result<()> { // set iothread - ohui_srv.set_iothread(cfg.ohui_config.iothread.clone()); + ohui_srv.set_iothread(cfg.iothread.clone()); // Register ohui interface let dcl = Arc::new(Mutex::new(DisplayChangeListener::new( None, -- Gitee From bffe34a83f4a6d7107f4caf11294688ce1779782 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 14:05:33 +0800 Subject: [PATCH 1836/2187] smp: use clap to parse the parameters of the smp config Use clap to parse the parameters of the smp config Signed-off-by: liuxiangdong --- machine_manager/src/config/machine_config.rs | 239 ++++++++----------- 1 file changed, 93 insertions(+), 146 deletions(-) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 2d6754271..2f9cf70e3 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -20,7 +20,7 @@ use super::error::ConfigError; use super::{ get_value_of_parameter, parse_bool, parse_size, str_slip_to_clap, valid_id, valid_path, }; -use crate::config::{CmdParser, ConfigCheck, IntegerList, VmConfig, MAX_NODES}; +use crate::config::{ConfigCheck, IntegerList, VmConfig, MAX_NODES}; use crate::machine::HypervisorType; const DEFAULT_CPUS: u8 = 1; @@ -288,6 +288,84 @@ struct MachineCmdConfig { gic_version: u8, } +#[derive(Parser)] +#[command(no_binary_name(true))] +struct SmpConfig { + #[arg(long, alias = "classtype", value_parser = clap::value_parser!(u64).range(MIN_NR_CPUS..=MAX_NR_CPUS))] + cpus: u64, + #[arg(long, default_value = "0")] + maxcpus: u64, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u64).range(..u8::MAX as u64))] + sockets: u64, + #[arg(long, default_value = "1", value_parser = clap::value_parser!(u64).range(1..u8::MAX as u64))] + dies: u64, + #[arg(long, default_value = "1", value_parser = clap::value_parser!(u64).range(1..u8::MAX as u64))] + clusters: u64, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u64).range(..u8::MAX as u64))] + cores: u64, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u64).range(..u8::MAX as u64))] + threads: u64, +} + +impl SmpConfig { + fn auto_adjust_topology(&mut self) -> Result<()> { + let mut max_cpus = self.maxcpus; + let mut sockets = self.sockets; + let mut cores = self.cores; + let mut threads = self.threads; + + if max_cpus == 0 { + if sockets * self.dies * self.clusters * cores * threads > 0 { + max_cpus = sockets * self.dies * self.clusters * cores * threads; + } else { + max_cpus = self.cpus; + } + } + + if cores == 0 { + if sockets == 0 { + sockets = 1; + } + if threads == 0 { + threads = 1; + } + cores = max_cpus / (sockets * self.dies * self.clusters * threads); + } else if sockets == 0 { + if threads == 0 { + threads = 1; + } + sockets = max_cpus / (self.dies * self.clusters * cores * threads); + } + + if threads == 0 { + threads = max_cpus / (sockets * self.dies * self.clusters * cores); + } + + let min_max_cpus = std::cmp::max(self.cpus, MIN_NR_CPUS); + + if !(min_max_cpus..=MAX_NR_CPUS).contains(&max_cpus) { + return Err(anyhow!(ConfigError::IllegalValue( + "MAX CPU number".to_string(), + min_max_cpus, + true, + MAX_NR_CPUS, + true, + ))); + } + + if sockets * self.dies * self.clusters * cores * threads != max_cpus { + bail!("sockets * dies * clusters * cores * threads must be equal to max_cpus"); + } + + self.maxcpus = max_cpus; + self.sockets = sockets; + self.cores = cores; + self.threads = threads; + + Ok(()) + } +} + impl VmConfig { /// Add argument `name` to `VmConfig`. /// @@ -337,96 +415,21 @@ impl VmConfig { /// Add '-smp' cpu config to `VmConfig`. pub fn add_cpu(&mut self, cpu_config: &str) -> Result<()> { - let mut cmd_parser = CmdParser::new("smp"); - cmd_parser - .push("") - .push("maxcpus") - .push("sockets") - .push("dies") - .push("clusters") - .push("cores") - .push("threads") - .push("cpus"); - - cmd_parser.parse(cpu_config)?; - - let cpu = if let Some(cpu) = cmd_parser.get_value::("")? { - cpu - } else if let Some(cpu) = cmd_parser.get_value::("cpus")? { - if cpu == 0 { - return Err(anyhow!(ConfigError::IllegalValue( - "cpu".to_string(), - 1, - true, - MAX_NR_CPUS, - true - ))); - } - cpu - } else { - return Err(anyhow!(ConfigError::FieldIsMissing( - "cpus".to_string(), - "smp".to_string() - ))); - }; - - let sockets = smp_read_and_check(&cmd_parser, "sockets", 0)?; - - let dies = smp_read_and_check(&cmd_parser, "dies", 1)?; - - let clusters = smp_read_and_check(&cmd_parser, "clusters", 1)?; - - let cores = smp_read_and_check(&cmd_parser, "cores", 0)?; - - let threads = smp_read_and_check(&cmd_parser, "threads", 0)?; - - let max_cpus = cmd_parser.get_value::("maxcpus")?.unwrap_or_default(); - - let (max_cpus, sockets, cores, threads) = - adjust_topology(cpu, max_cpus, sockets, dies, clusters, cores, threads); - - // limit cpu count - if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&cpu) { - return Err(anyhow!(ConfigError::IllegalValue( - "CPU number".to_string(), - MIN_NR_CPUS, - true, - MAX_NR_CPUS, - true, - ))); - } - - if !(MIN_NR_CPUS..=MAX_NR_CPUS).contains(&max_cpus) { - return Err(anyhow!(ConfigError::IllegalValue( - "MAX CPU number".to_string(), - MIN_NR_CPUS, - true, - MAX_NR_CPUS, - true, - ))); - } - - if max_cpus < cpu { - return Err(anyhow!(ConfigError::IllegalValue( - "maxcpus".to_string(), - cpu, - true, - MAX_NR_CPUS, - true, - ))); + let mut has_cpus_label = false; + if get_value_of_parameter("cpus", cpu_config).is_ok() { + has_cpus_label = true; } - - if sockets * dies * clusters * cores * threads != max_cpus { - bail!("sockets * dies * clusters * cores * threads must be equal to max_cpus"); - } - - self.machine_config.nr_cpus = cpu as u8; - self.machine_config.nr_threads = threads as u8; - self.machine_config.nr_cores = cores as u8; - self.machine_config.nr_dies = dies as u8; - self.machine_config.nr_clusters = clusters as u8; - self.machine_config.nr_sockets = sockets as u8; - self.machine_config.max_cpus = max_cpus as u8; + let mut smp_cfg = + SmpConfig::try_parse_from(str_slip_to_clap(cpu_config, !has_cpus_label, false))?; + smp_cfg.auto_adjust_topology()?; + + self.machine_config.nr_cpus = smp_cfg.cpus as u8; + self.machine_config.nr_threads = smp_cfg.threads as u8; + self.machine_config.nr_cores = smp_cfg.cores as u8; + self.machine_config.nr_dies = smp_cfg.dies as u8; + self.machine_config.nr_clusters = smp_cfg.clusters as u8; + self.machine_config.nr_sockets = smp_cfg.sockets as u8; + self.machine_config.max_cpus = smp_cfg.maxcpus as u8; Ok(()) } @@ -499,62 +502,6 @@ impl VmConfig { } } -fn smp_read_and_check(cmd_parser: &CmdParser, name: &str, default_val: u64) -> Result { - if let Some(values) = cmd_parser.get_value::(name)? { - if values == 0 { - return Err(anyhow!(ConfigError::IllegalValue( - name.to_string(), - 1, - true, - u8::MAX as u64, - false - ))); - } - Ok(values) - } else { - Ok(default_val) - } -} - -fn adjust_topology( - cpu: u64, - mut max_cpus: u64, - mut sockets: u64, - dies: u64, - clusters: u64, - mut cores: u64, - mut threads: u64, -) -> (u64, u64, u64, u64) { - if max_cpus == 0 { - if sockets * dies * clusters * cores * threads > 0 { - max_cpus = sockets * dies * clusters * cores * threads; - } else { - max_cpus = cpu; - } - } - - if cores == 0 { - if sockets == 0 { - sockets = 1; - } - if threads == 0 { - threads = 1; - } - cores = max_cpus / (sockets * dies * clusters * threads); - } else if sockets == 0 { - if threads == 0 { - threads = 1; - } - sockets = max_cpus / (dies * clusters * cores * threads); - } - - if threads == 0 { - threads = max_cpus / (sockets * dies * clusters * cores); - } - - (max_cpus, sockets, cores, threads) -} - /// Convert memory units from GiB, Mib to Byte. /// /// # Arguments -- Gitee From f77d33853d1c187ac6aa068d6d97a9a964de61d3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 20 May 2024 14:08:09 +0800 Subject: [PATCH 1837/2187] machine_manager: delete CmdParser So far, we have replaced all `Cmdparser` with `clap` and `get_value_of_parameter` Delete useless `CmdPaser`. Signed-off-by: liuxiangdong --- devices/src/scsi/disk.rs | 2 +- machine_manager/src/config/mod.rs | 203 ------------------------------ machine_manager/src/config/pci.rs | 22 +--- 3 files changed, 2 insertions(+), 225 deletions(-) diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 7edccdeb3..37aca3da4 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -221,7 +221,7 @@ impl ScsiDevice { } let drive_files = self.drive_files.lock().unwrap(); - // File path can not be empty string. And it has also been checked in CmdParser::parse. + // File path can not be empty string. And it has also been checked in command parsing by using `Clap`. let file = VmConfig::fetch_drive_file(&drive_files, &self.drive_cfg.path_on_host)?; let alignments = VmConfig::fetch_drive_align(&drive_files, &self.drive_cfg.path_on_host)?; diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index d88d49378..ca1281a6d 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -415,121 +415,6 @@ pub trait ConfigCheck: AsAny + Send + Sync + std::fmt::Debug { fn check(&self) -> Result<()>; } -/// Struct `CmdParser` used to parse and check cmdline parameters to vm config. -pub struct CmdParser { - name: String, - params: HashMap>, -} - -impl CmdParser { - /// Allocates an empty `CmdParser`. - pub fn new(name: &str) -> Self { - CmdParser { - name: name.to_string(), - params: HashMap::>::new(), - } - } - - /// Push a new param field into `params`. - /// - /// # Arguments - /// - /// * `param_field`: The cmdline parameter field name. - pub fn push(&mut self, param_field: &str) -> &mut Self { - self.params.insert(param_field.to_string(), None); - - self - } - - /// Parse cmdline parameters string into `params`. - /// - /// # Arguments - /// - /// * `cmd_param`: The whole cmdline parameter string. - pub fn parse(&mut self, cmd_param: &str) -> Result<()> { - if cmd_param.starts_with(',') || cmd_param.ends_with(',') { - return Err(anyhow!(ConfigError::InvalidParam( - cmd_param.to_string(), - self.name.clone() - ))); - } - let param_items = cmd_param.split(',').collect::>(); - for (i, param_item) in param_items.iter().enumerate() { - if param_item.starts_with('=') || param_item.ends_with('=') { - return Err(anyhow!(ConfigError::InvalidParam( - param_item.to_string(), - self.name.clone() - ))); - } - let param = param_item.splitn(2, '=').collect::>(); - let (param_key, param_value) = match param.len() { - 1 => { - if i == 0 { - ("", param[0]) - } else { - (param[0], "") - } - } - 2 => (param[0], param[1]), - _ => { - return Err(anyhow!(ConfigError::InvalidParam( - param_item.to_string(), - self.name.clone() - ))); - } - }; - - if self.params.contains_key(param_key) { - let field_value = self.params.get_mut(param_key).unwrap(); - if field_value.is_none() { - *field_value = Some(String::from(param_value)); - } else { - return Err(anyhow!(ConfigError::FieldRepeat( - self.name.clone(), - param_key.to_string() - ))); - } - } else { - return Err(anyhow!(ConfigError::InvalidParam( - param[0].to_string(), - self.name.clone() - ))); - } - } - - Ok(()) - } - - /// Get cmdline parameters value from param field name. - /// - /// # Arguments - /// - /// * `param_field`: The cmdline parameter field name. - pub fn get_value(&self, param_field: &str) -> Result> { - match self.params.get(param_field) { - Some(value) => { - let field_msg = if param_field.is_empty() { - &self.name - } else { - param_field - }; - - if let Some(raw_value) = value { - Ok(Some(raw_value.parse().map_err(|_| { - anyhow!(ConfigError::ConvertValueFailed( - field_msg.to_string(), - raw_value.clone() - )) - })?)) - } else { - Ok(None) - } - } - None => Ok(None), - } - } -} - /// This struct is a wrapper for `bool`. /// More switch string can be transferred to this structure. pub struct ExBool { @@ -860,94 +745,6 @@ pub fn parse_size(s: &str) -> Result { mod tests { use super::*; - #[test] - fn test_cmd_parser() { - let mut cmd_parser = CmdParser::new("test"); - cmd_parser - .push("") - .push("id") - .push("path") - .push("num") - .push("test1") - .push("test2") - .push("test3") - .push("test4") - .push("test5") - .push("test6") - .push("test7"); - assert!(cmd_parser - .parse("socket,id=charconsole0,path=/tmp/console.sock,num=1,test1=true,test2=on,test3=yes,test4=false,test5=off,test6=no,test7=random") - .is_ok()); - assert_eq!( - cmd_parser.get_value::("").unwrap().unwrap(), - "socket".to_string() - ); - assert_eq!( - cmd_parser.get_value::("id").unwrap().unwrap(), - "charconsole0".to_string() - ); - assert_eq!( - cmd_parser.get_value::("path").unwrap().unwrap(), - "/tmp/console.sock".to_string() - ); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_u64); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_u32); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_u16); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_u8); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_i64); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_i32); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_i16); - assert_eq!(cmd_parser.get_value::("num").unwrap().unwrap(), 1_i8); - assert!(cmd_parser.get_value::("test1").unwrap().unwrap()); - assert!( - cmd_parser - .get_value::("test1") - .unwrap() - .unwrap() - .inner - ); - assert!( - cmd_parser - .get_value::("test2") - .unwrap() - .unwrap() - .inner - ); - assert!( - cmd_parser - .get_value::("test3") - .unwrap() - .unwrap() - .inner - ); - assert!(!cmd_parser.get_value::("test4").unwrap().unwrap()); - assert!( - !cmd_parser - .get_value::("test4") - .unwrap() - .unwrap() - .inner - ); - assert!( - !cmd_parser - .get_value::("test5") - .unwrap() - .unwrap() - .inner - ); - assert!( - !cmd_parser - .get_value::("test6") - .unwrap() - .unwrap() - .inner - ); - assert!(cmd_parser.get_value::("test7").is_err()); - assert!(cmd_parser.get_value::("test7").is_err()); - assert!(cmd_parser.get_value::("random").unwrap().is_none()); - assert!(cmd_parser.parse("random=false").is_err()); - } - #[test] fn test_add_trace() { assert!(add_trace("fil=test_trace").is_err()); diff --git a/machine_manager/src/config/pci.rs b/machine_manager/src/config/pci.rs index 4f054e900..6642f4f62 100644 --- a/machine_manager/src/config/pci.rs +++ b/machine_manager/src/config/pci.rs @@ -13,8 +13,7 @@ use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; -use super::{get_value_of_parameter, CmdParser}; -use crate::config::ExBool; +use super::get_value_of_parameter; use util::num_ops::str_to_num; /// Basic information of pci devices such as bus number, @@ -82,25 +81,6 @@ pub fn get_pci_bdf(pci_cfg: &str) -> Result { Ok(pci_bdf) } -pub fn pci_args_check(cmd_parser: &CmdParser) -> Result<()> { - let device_type = cmd_parser.get_value::("")?; - let dev_type = device_type.unwrap(); - // Safe, because this function only be called when certain - // devices type are added. - if dev_type.ends_with("-device") { - if cmd_parser.get_value::("bus")?.is_some() { - bail!("virtio mmio device does not support bus arguments"); - } - if cmd_parser.get_value::("addr")?.is_some() { - bail!("virtio mmio device does not support addr arguments"); - } - if cmd_parser.get_value::("multifunction")?.is_some() { - bail!("virtio mmio device does not support multifunction arguments"); - } - } - Ok(()) -} - #[cfg(test)] mod tests { use super::*; -- Gitee From 7a3504857df431f7ae2addc2ef3398d41610f869 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 22 May 2024 00:11:10 +0800 Subject: [PATCH 1838/2187] usb: adjust log content "Error" words can easily cause misunderstandings among the caller. Adjust the print information content for `handle_control_for_descriptor`. Signed-off-by: liuxiangdong --- devices/src/usb/camera.rs | 2 +- devices/src/usb/keyboard.rs | 5 ++++- devices/src/usb/storage.rs | 2 +- devices/src/usb/tablet.rs | 2 +- devices/src/usb/uas.rs | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 710a71105..79cc1ba5d 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -810,7 +810,7 @@ impl UsbDevice for UsbCamera { } } Err(e) => { - warn!("Camera descriptor error {:?}", e); + warn!("Received incorrect USB Camera descriptor message: {:?}", e); locked_packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 716856038..532914277 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -241,7 +241,10 @@ impl UsbDevice for UsbKeyboard { } } Err(e) => { - warn!("Keyboard descriptor error {:?}", e); + warn!( + "Received incorrect USB Keyboard descriptor message: {:?}", + e + ); locked_packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 2f9d4c969..2f924d8a7 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -595,7 +595,7 @@ impl UsbDevice for UsbStorage { self.handle_control_packet(&mut locked_packet, device_req) } Err(e) => { - warn!("Storage descriptor error {:?}", e); + warn!("Received incorrect USB Storage descriptor message: {:?}", e); locked_packet.status = UsbPacketStatus::Stall; } } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index fffb4df41..a4bfebf40 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -284,7 +284,7 @@ impl UsbDevice for UsbTablet { } } Err(e) => { - warn!("Tablet descriptor error {:?}", e); + warn!("Received incorrect USB Tablet descriptor message: {:?}", e); locked_packet.status = UsbPacketStatus::Stall; return; } diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 9ba134cd4..4bdfc398f 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -1108,8 +1108,8 @@ impl UsbDevice for UsbUas { locked_packet.status = UsbPacketStatus::Stall; } Err(err) => { - error!( - "UAS {} device descriptor error {:?}.", + warn!( + "{} received incorrect UAS descriptor message: {:?}", self.device_id(), err ); -- Gitee From 85b11276695c6638ba7c2d775600bcf153250ae3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 22 May 2024 12:23:24 +0800 Subject: [PATCH 1839/2187] virtio-mmio: use `realize_virtio_mmio_device` to create virtiommio device Replace the `VirtioMmioDevice::realize` with the existing function `realize_virtio_mmio_device`. Signed-off-by: liuxiangdong --- machine/src/micro_common/mod.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 49cd15348..d4db45b0c 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -190,8 +190,6 @@ impl LightMachine { ); } - let mut region_base = self.base.sysbus.min_free_base; - let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; for (id, dev) in rpl_devs.into_iter().enumerate() { self.replaceable_info .devices @@ -205,20 +203,11 @@ impl LightMachine { MigrationManager::register_transport_instance( VirtioMmioState::descriptor(), - VirtioMmioDevice::realize( - dev, - &mut self.base.sysbus, - region_base, - MEM_LAYOUT[LayoutEntryType::Mmio as usize].1, - #[cfg(target_arch = "x86_64")] - &self.base.boot_source, - ) - .with_context(|| MachineError::RlzVirtioMmioErr)?, + self.realize_virtio_mmio_device(dev)?, &id.to_string(), ); - region_base += region_size; } - self.base.sysbus.min_free_base = region_base; + Ok(()) } -- Gitee From 3f3aeec9cb2d7c60fc596ebf601018148ce837db Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 22 May 2024 20:53:10 +0800 Subject: [PATCH 1840/2187] util: Avoid potential unsafe use of unwrap Signed-off-by: Keqian Zhu --- util/src/unix.rs | 86 +++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/util/src/unix.rs b/util/src/unix.rs index d71e3c826..6a723ed25 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -187,10 +187,11 @@ impl UnixSock { /// The listener accepts incoming client connections. pub fn accept(&mut self) -> Result<()> { - let (sock, _addr) = self + let listener = self .listener .as_ref() - .unwrap() + .with_context(|| "UnixSock is not bound")?; + let (sock, _addr) = listener .accept() .with_context(|| format!("Failed to accept the socket {}", self.path))?; self.sock = Some(sock); @@ -203,8 +204,12 @@ impl UnixSock { } pub fn server_connection_refuse(&mut self) -> Result<()> { + let listener = self + .listener + .as_ref() + .with_context(|| "UnixSock is not bound")?; // Refuse connection by finishing life cycle of stream fd from listener fd. - self.listener.as_ref().unwrap().accept().with_context(|| { + listener.accept().with_context(|| { format!( "Failed to accept the socket for refused connection {}", self.path @@ -224,18 +229,21 @@ impl UnixSock { } pub fn listen_set_nonblocking(&self, nonblocking: bool) -> Result<()> { - self.listener + let listener = self + .listener .as_ref() - .unwrap() + .with_context(|| "UnixSock is not bound")?; + listener .set_nonblocking(nonblocking) .with_context(|| "couldn't set nonblocking for unix sock listener") } pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { - self.sock + let sock = self + .sock .as_ref() - .unwrap() - .set_nonblocking(nonblocking) + .with_context(|| "UnixSock is not connected")?; + sock.set_nonblocking(nonblocking) .with_context(|| "couldn't set nonblocking") } @@ -287,7 +295,7 @@ impl UnixSock { /// # Errors /// /// The socket file descriptor is broken. - pub fn send_msg(&self, iovecs: &mut [iovec], out_fds: &[RawFd]) -> std::io::Result { + pub fn send_msg(&self, iovecs: &mut [iovec], out_fds: &[RawFd]) -> Result { // SAFETY: We checked the iovecs lens before. let iovecs_len = iovecs.len(); // SAFETY: We checked the out_fds lens before. @@ -331,17 +339,17 @@ impl UnixSock { msg.msg_controllen = cmsg_capacity as _; } - let write_count = - // SAFETY: msg parameters are valid. - unsafe { sendmsg(self.sock.as_ref().unwrap().as_raw_fd(), &msg, MSG_NOSIGNAL) }; + let sock = self + .sock + .as_ref() + .with_context(|| "UnixSock is not connected")?; + // SAFETY: msg parameters are valid. + let write_count = unsafe { sendmsg(sock.as_raw_fd(), &msg, MSG_NOSIGNAL) }; if write_count == -1 { - Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!( - "Failed to send msg, err: {}", - std::io::Error::last_os_error() - ), + Err(anyhow!( + "Failed to send msg, err: {}", + std::io::Error::last_os_error() )) } else { Ok(write_count as usize) @@ -358,11 +366,7 @@ impl UnixSock { /// # Errors /// /// The socket file descriptor is broken. - pub fn recv_msg( - &self, - iovecs: &mut [iovec], - in_fds: &mut [RawFd], - ) -> std::io::Result<(usize, usize)> { + pub fn recv_msg(&self, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<(usize, usize)> { // SAFETY: We check the iovecs lens before. let iovecs_len = iovecs.len(); // SAFETY: We check the in_fds lens before. @@ -386,33 +390,25 @@ impl UnixSock { msg.msg_controllen = cmsg_capacity as _; } + let sock = self + .sock + .as_ref() + .with_context(|| "UnixSock is not connected")?; // SAFETY: msg parameters are valid. - let total_read = unsafe { - recvmsg( - self.sock.as_ref().unwrap().as_raw_fd(), - &mut msg, - MSG_WAITALL, - ) - }; + let total_read = unsafe { recvmsg(sock.as_raw_fd(), &mut msg, MSG_WAITALL) }; if total_read == -1 { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!( - "Failed to recv msg, err: {}", - std::io::Error::last_os_error() - ), - )); + bail!( + "Failed to recv msg, err: {}", + std::io::Error::last_os_error() + ); } if total_read == 0 && (msg.msg_controllen as u64) < size_of::() as u64 { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!( - "The length of control message is invalid, {} {}", - msg.msg_controllen, - size_of::() - ), - )); + bail!( + "The length of control message is invalid, {} {}", + msg.msg_controllen, + size_of::() + ); } let mut cmsg_ptr = msg.msg_control as *mut cmsghdr; -- Gitee From 94bb128b501530be72d937066771220b5ecc2709 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 22 May 2024 11:08:08 +0800 Subject: [PATCH 1841/2187] virtio-mmio: add function `add_virtio_mmio_device` Add function `add_virtio_mmio_device` to add a new virtio mmio device. This function includes two steps: `new` and `realize`. This is the pre-patch for the next Realize unified patch. No functional changes. Signed-off-by: liuxiangdong --- machine/src/aarch64/micro.rs | 9 +++-- machine/src/lib.rs | 37 ++++++++---------- machine/src/micro_common/mod.rs | 67 +++++++++++++++++---------------- machine/src/x86_64/micro.rs | 9 +++-- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index d7e1b1d4c..40c8baff1 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -26,7 +26,7 @@ use util::{ device_tree::{self, CompileFDT, FdtBuilder}, seccomp::{BpfRule, SeccompCmpOpt}, }; -use virtio::VirtioMmioDevice; +use virtio::{VirtioDevice, VirtioMmioDevice}; #[repr(usize)] pub enum LayoutEntryType { @@ -218,11 +218,12 @@ impl MachineOps for LightMachine { self.add_virtio_mmio_block(vm_config, cfg_args) } - fn realize_virtio_mmio_device( + fn add_virtio_mmio_device( &mut self, - dev: VirtioMmioDevice, + name: String, + device: Arc>, ) -> Result>> { - self.realize_virtio_mmio_device(dev) + self.add_virtio_mmio_device(name, device) } fn syscall_whitelist(&self) -> Vec { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1ae93787c..c1f103142 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -586,11 +586,12 @@ pub trait MachineOps { ("addr", device_cfg.addr), ("multifunction", device_cfg.multifunction) ); - let device = VirtioMmioDevice::new(&sys_mem, device_cfg.id.clone(), vsock.clone()); + let device = self + .add_virtio_mmio_device(device_cfg.id.clone(), vsock.clone()) + .with_context(|| MachineError::RlzVirtioMmioErr)?; MigrationManager::register_device_instance( VirtioMmioState::descriptor(), - self.realize_virtio_mmio_device(device) - .with_context(|| MachineError::RlzVirtioMmioErr)?, + device, &device_cfg.id, ); } @@ -612,9 +613,10 @@ pub trait MachineOps { Ok(()) } - fn realize_virtio_mmio_device( + fn add_virtio_mmio_device( &mut self, - _dev: VirtioMmioDevice, + _name: String, + _device: Arc>, ) -> Result>> { bail!("Virtio mmio devices not supported"); } @@ -688,8 +690,7 @@ pub trait MachineOps { ("addr", config.addr), ("multifunction", config.multifunction) ); - let device = VirtioMmioDevice::new(sys_mem, config.id.clone(), balloon); - self.realize_virtio_mmio_device(device)?; + self.add_virtio_mmio_device(config.id.clone(), balloon)?; } _ => { check_arg_exist!(("bus", config.bus), ("addr", config.addr)); @@ -716,7 +717,6 @@ pub trait MachineOps { let mut serial_cfg = VirtioSerialInfo::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; serial_cfg.auto_max_ports(); - let sys_mem = self.get_sys_mem().clone(); let serial = Arc::new(Mutex::new(Serial::new(serial_cfg.clone()))); match serial_cfg.classtype.as_str() { @@ -726,11 +726,12 @@ pub trait MachineOps { ("addr", serial_cfg.addr), ("multifunction", serial_cfg.multifunction) ); - let device = VirtioMmioDevice::new(&sys_mem, serial_cfg.id.clone(), serial.clone()); + let device = self + .add_virtio_mmio_device(serial_cfg.id.clone(), serial.clone()) + .with_context(|| MachineError::RlzVirtioMmioErr)?; MigrationManager::register_device_instance( VirtioMmioState::descriptor(), - self.realize_virtio_mmio_device(device) - .with_context(|| MachineError::RlzVirtioMmioErr)?, + device, &serial_cfg.id, ); } @@ -852,7 +853,6 @@ pub trait MachineOps { .rng_object .remove(&rng_cfg.rng) .with_context(|| "Object for rng-random device not found")?; - let sys_mem = self.get_sys_mem(); let rng_dev = Arc::new(Mutex::new(Rng::new(rng_cfg.clone(), rngobj_cfg))); match rng_cfg.classtype.as_str() { @@ -862,8 +862,7 @@ pub trait MachineOps { ("addr", rng_cfg.addr), ("multifunction", rng_cfg.multifunction) ); - let device = VirtioMmioDevice::new(sys_mem, rng_cfg.id.clone(), rng_dev.clone()); - self.realize_virtio_mmio_device(device) + self.add_virtio_mmio_device(rng_cfg.id.clone(), rng_dev.clone()) .with_context(|| "Failed to add virtio mmio rng device")?; } _ => { @@ -905,7 +904,7 @@ pub trait MachineOps { let device = Arc::new(Mutex::new(vhost::user::Fs::new( dev_cfg.clone(), char_dev, - sys_mem.clone(), + sys_mem, ))); match dev_cfg.classtype.as_str() { "vhost-user-fs-device" => { @@ -914,9 +913,7 @@ pub trait MachineOps { ("addr", dev_cfg.addr), ("multifunction", dev_cfg.multifunction) ); - let virtio_mmio_device = - VirtioMmioDevice::new(&sys_mem, dev_cfg.id.clone(), device); - self.realize_virtio_mmio_device(virtio_mmio_device) + self.add_virtio_mmio_device(dev_cfg.id.clone(), device) .with_context(|| "Failed to add vhost user fs device")?; } _ => { @@ -1381,9 +1378,7 @@ pub trait MachineOps { chardev_cfg, self.get_sys_mem(), ))); - let virtio_mmio_device = - VirtioMmioDevice::new(self.get_sys_mem(), device_cfg.id.clone(), device); - self.realize_virtio_mmio_device(virtio_mmio_device) + self.add_virtio_mmio_device(device_cfg.id.clone(), device) .with_context(|| "Failed to add vhost user block device")?; Ok(()) } diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index d4db45b0c..8aee4003b 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -157,54 +157,54 @@ impl LightMachine { } pub(crate) fn create_replaceable_devices(&mut self) -> Result<()> { - let mut rpl_devs: Vec = Vec::new(); for id in 0..MMIO_REPLACEABLE_BLK_NR { let block = Arc::new(Mutex::new(Block::new( VirtioBlkDevConfig::default(), DriveConfig::default(), self.get_drive_files(), ))); - let virtio_mmio = - VirtioMmioDevice::new(&self.base.sys_mem, id.to_string(), block.clone()); - rpl_devs.push(virtio_mmio); - MigrationManager::register_device_instance( BlockState::descriptor(), - block, + block.clone(), + &id.to_string(), + ); + + let blk_mmio = self.add_virtio_mmio_device(id.to_string(), block.clone())?; + let info = MmioReplaceableDevInfo { + device: block, + id: id.to_string(), + used: false, + }; + self.replaceable_info.devices.lock().unwrap().push(info); + MigrationManager::register_transport_instance( + VirtioMmioState::descriptor(), + blk_mmio, &id.to_string(), ); } for id in 0..MMIO_REPLACEABLE_NET_NR { + let total_id = id + MMIO_REPLACEABLE_BLK_NR; let net = Arc::new(Mutex::new(Net::new( NetworkInterfaceConfig::default(), NetDevcfg::default(), ))); - let virtio_mmio = - VirtioMmioDevice::new(&self.base.sys_mem, id.to_string(), net.clone()); - rpl_devs.push(virtio_mmio); - MigrationManager::register_device_instance( VirtioNetState::descriptor(), - net, - &id.to_string(), + net.clone(), + &total_id.to_string(), ); - } - - for (id, dev) in rpl_devs.into_iter().enumerate() { - self.replaceable_info - .devices - .lock() - .unwrap() - .push(MmioReplaceableDevInfo { - device: dev.device.clone(), - id: id.to_string(), - used: false, - }); + let net_mmio = self.add_virtio_mmio_device(total_id.to_string(), net.clone())?; + let info = MmioReplaceableDevInfo { + device: net, + id: total_id.to_string(), + used: false, + }; + self.replaceable_info.devices.lock().unwrap().push(info); MigrationManager::register_transport_instance( VirtioMmioState::descriptor(), - self.realize_virtio_mmio_device(dev)?, - &id.to_string(), + net_mmio, + &total_id.to_string(), ); } @@ -400,13 +400,13 @@ impl LightMachine { .remove(&net_cfg.netdev) .with_context(|| format!("Netdev: {:?} not found for net device", &net_cfg.netdev))?; if netdev_cfg.vhost_type().is_some() { - let device = if netdev_cfg.vhost_type().unwrap() == "vhost-kernel" { + if netdev_cfg.vhost_type().unwrap() == "vhost-kernel" { let net = Arc::new(Mutex::new(VhostKern::Net::new( &net_cfg, netdev_cfg, &self.base.sys_mem, ))); - VirtioMmioDevice::new(&self.base.sys_mem, net_cfg.id.clone(), net) + self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; } else { let chardev = netdev_cfg.chardev.clone().with_context(|| { format!("Chardev not configured for netdev {:?}", netdev_cfg.id) @@ -422,9 +422,8 @@ impl LightMachine { sock_path, &self.base.sys_mem, ))); - VirtioMmioDevice::new(&self.base.sys_mem, net_cfg.id.clone(), net) + self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; }; - self.realize_virtio_mmio_device(device)?; } else { let index = MMIO_REPLACEABLE_BLK_NR + self.replaceable_info.net_count; if index >= MMIO_REPLACEABLE_BLK_NR + MMIO_REPLACEABLE_NET_NR { @@ -471,10 +470,14 @@ impl LightMachine { Ok(()) } - pub(crate) fn realize_virtio_mmio_device( + pub(crate) fn add_virtio_mmio_device( &mut self, - dev: VirtioMmioDevice, + name: String, + device: Arc>, ) -> Result>> { + let sys_mem = self.get_sys_mem().clone(); + let dev = VirtioMmioDevice::new(&sys_mem, name, device); + let region_base = self.base.sysbus.min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; let realized_virtio_mmio_device = VirtioMmioDevice::realize( diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index d8fb92e61..e5d17ce52 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -25,7 +25,7 @@ use hypervisor::kvm::*; use machine_manager::config::{SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use virtio::VirtioMmioDevice; +use virtio::{VirtioDevice, VirtioMmioDevice}; #[repr(usize)] pub enum LayoutEntryType { @@ -204,11 +204,12 @@ impl MachineOps for LightMachine { self.add_virtio_mmio_block(vm_config, cfg_args) } - fn realize_virtio_mmio_device( + fn add_virtio_mmio_device( &mut self, - dev: VirtioMmioDevice, + name: String, + device: Arc>, ) -> Result>> { - self.realize_virtio_mmio_device(dev) + self.add_virtio_mmio_device(name, device) } fn syscall_whitelist(&self) -> Vec { -- Gitee From 80f71540bd0185cd6c5b0e5923c724e2aeee5416 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 22 May 2024 09:01:06 +0800 Subject: [PATCH 1842/2187] sysbus: don't pass in mem region information when `attach_device` Sysbusdev has got its mem region information when `set_sys_resource`, so we needn't to pass it again. Remove these useless function parameters in function `attach_device`. And, we can assume that this device does not have its own memory layout when region_base/region_size are both 0 such as `Ramfb`. Thus, we can remove useless `get_sys_resource_mut`. For these dynamic devices, use `attach_device` to replace `attach_dynamic_device`. Signed-off-by: liuxiangdong --- devices/src/acpi/cpu_controller.rs | 12 +-- devices/src/acpi/ged.rs | 10 +-- devices/src/acpi/power.rs | 10 +-- devices/src/legacy/fwcfg.rs | 43 +++------ devices/src/legacy/pflash.rs | 15 ++-- devices/src/legacy/pl011.rs | 10 +-- devices/src/legacy/pl031.rs | 10 +-- devices/src/legacy/ramfb.rs | 2 +- devices/src/legacy/rtc.rs | 10 +-- devices/src/legacy/serial.rs | 10 +-- devices/src/sysbus/mod.rs | 131 ++++++++++++++-------------- virtio/src/transport/virtio_mmio.rs | 10 +-- 12 files changed, 109 insertions(+), 164 deletions(-) diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index 1259e8d2d..4fe256c62 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -19,7 +19,7 @@ use anyhow::{bail, Context, Result}; use log::{error, info}; use vmm_sys_util::eventfd::EventFd; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps}; use crate::{Device, DeviceBase}; use acpi::{ AcpiError, AcpiLocalApic, AmlAcquire, AmlAddressSpaceType, AmlArg, AmlBuffer, AmlBuilder, @@ -99,11 +99,11 @@ impl CpuController { self.max_cpus = max_cpus; self.cpu_config = Some(cpu_config); self.hotplug_cpu_req = Some(hotplug_cpu_req); - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "CPUController") .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; let dev = Arc::new(Mutex::new(self)); let ret_dev = dev.clone(); - sysbus.attach_device(&dev, region_base, region_size, "CPUController")?; + sysbus.attach_device(&dev)?; Ok(ret_dev) } @@ -329,15 +329,11 @@ impl SysBusDevOps for CpuController { } true } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl AmlBuilder for CpuController { fn aml_bytes(&self) -> Vec { - let res = self.base.res; + let res = self.base.res.clone(); let mut cpu_hotplug_controller = AmlDevice::new("PRES"); cpu_hotplug_controller.append_child(AmlNameDecl::new("_HID", AmlEisaId::new("PNP0A06"))); cpu_hotplug_controller.append_child(AmlNameDecl::new( diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 300868eaf..ea6c52daf 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -19,7 +19,7 @@ use anyhow::{Context, Result}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps}; use crate::{Device, DeviceBase}; use acpi::{ AcpiError, AmlActiveLevel, AmlAddressSpaceType, AmlAnd, AmlBuilder, AmlDevice, AmlEdgeLevel, @@ -97,12 +97,12 @@ impl Ged { region_size: u64, ) -> Result>> { self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "Ged") .with_context(|| AcpiError::Alignment(region_size as u32))?; self.battery_present = battery_present; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size, "Ged")?; + sysbus.attach_device(&dev)?; let ged = dev.lock().unwrap(); ged.register_acpi_powerdown_event(ged_event.power_button) @@ -213,10 +213,6 @@ impl SysBusDevOps for Ged { fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { true } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl AmlBuilder for Ged { diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 5d5685d3f..d64486fc6 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -18,7 +18,7 @@ use anyhow::{Context, Result}; use log::info; use crate::acpi::ged::{AcpiEvent, Ged}; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps}; use crate::{Device, DeviceBase}; use acpi::{ AcpiError, AmlAddressSpaceType, AmlBuilder, AmlDevice, AmlField, AmlFieldAccessType, @@ -183,11 +183,11 @@ impl PowerDev { region_base: u64, region_size: u64, ) -> Result<()> { - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "PowerDev") .with_context(|| AcpiError::Alignment(region_size as u32))?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size, "PowerDev")?; + sysbus.attach_device(&dev)?; let pdev_available: bool; { @@ -262,10 +262,6 @@ impl SysBusDevOps for PowerDev { fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { true } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl AmlBuilder for PowerDev { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index e675fb7d7..45ac6d4bc 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -19,7 +19,7 @@ use byteorder::{BigEndian, ByteOrder}; use log::{error, warn}; use crate::legacy::error::LegacyError; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::{ AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, AmlString, @@ -856,12 +856,12 @@ impl FwCfgMem { region_size: u64, ) -> Result>> { self.fwcfg.common_realize()?; - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "FwCfgMem") .with_context(|| "Failed to allocate system resource for FwCfg.")?; let dev = Arc::new(Mutex::new(self)); sysbus - .attach_device(&dev, region_base, region_size, "FwCfgMem") + .attach_device(&dev) .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } @@ -989,19 +989,15 @@ impl SysBusDevOps for FwCfgMem { true } - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } - fn set_sys_resource( &mut self, _sysbus: &mut SysBus, region_base: u64, region_size: u64, + region_name: &str, ) -> Result<()> { - let res = self.get_sys_resource_mut().unwrap(); - res.region_base = region_base; - res.region_size = region_size; + self.sysbusdev_base_mut() + .set_sys(-1, region_base, region_size, region_name); Ok(()) } @@ -1022,30 +1018,19 @@ pub struct FwCfgIO { impl FwCfgIO { pub fn new(sys_mem: Arc) -> Self { FwCfgIO { - base: SysBusDevBase { - base: DeviceBase::default(), - dev_type: SysBusDevType::FwCfg, - res: SysRes { - region_base: FW_CFG_IO_BASE, - region_size: FW_CFG_IO_SIZE, - irq: -1, - }, - ..Default::default() - }, + base: SysBusDevBase::new(SysBusDevType::FwCfg), fwcfg: FwCfgCommon::new(sys_mem), } } pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { self.fwcfg.common_realize()?; - let region_base = self.base.res.region_base; - let region_size = self.base.res.region_size; - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, FW_CFG_IO_BASE, FW_CFG_IO_SIZE, "FwCfgIO") .with_context(|| "Failed to allocate system resource for FwCfg.")?; let dev = Arc::new(Mutex::new(self)); sysbus - .attach_device(&dev, region_base, region_size, "FwCfgIO") + .attach_device(&dev) .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } @@ -1174,19 +1159,15 @@ impl SysBusDevOps for FwCfgIO { true } - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } - fn set_sys_resource( &mut self, _sysbus: &mut SysBus, region_base: u64, region_size: u64, + region_name: &str, ) -> Result<()> { - let res = self.get_sys_resource_mut().unwrap(); - res.region_base = region_base; - res.region_size = region_size; + self.sysbusdev_base_mut() + .set_sys(-1, region_base, region_size, region_name); Ok(()) } diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index e10039228..6c225f67b 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -18,7 +18,7 @@ use anyhow::{anyhow, bail, Context, Result}; use log::{error, warn}; use super::error::LegacyError; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; @@ -214,7 +214,7 @@ impl PFlash { backend: Option, ) -> Result<()> { let region_size = Self::flash_region_size(region_max_size, &backend, self.read_only)?; - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "PflashRom") .with_context(|| "Failed to allocate system resource for PFlash.")?; let host_mmap = Arc::new(HostMemMapping::new( @@ -893,20 +893,15 @@ impl SysBusDevOps for PFlash { } } - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } - fn set_sys_resource( &mut self, _sysbus: &mut SysBus, region_base: u64, region_size: u64, + region_name: &str, ) -> Result<()> { - let res = self.get_sys_resource_mut().unwrap(); - res.region_base = region_base; - res.region_size = region_size; - res.irq = 0; + self.sysbusdev_base_mut() + .set_sys(0, region_base, region_size, region_name); Ok(()) } diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index fe76da2de..31069837b 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -16,7 +16,7 @@ use anyhow::{Context, Result}; use log::{debug, error}; use super::error::LegacyError; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlExtendedInterrupt, AmlIntShare, @@ -165,12 +165,12 @@ impl PL011 { .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "PL011") .with_context(|| "Failed to set system resource for PL011.")?; let dev = Arc::new(Mutex::new(self)); sysbus - .attach_device(&dev, region_base, region_size, "PL011") + .attach_device(&dev) .with_context(|| "Failed to attach PL011 to system bus.")?; bs.lock().unwrap().kernel_cmdline.push(Param { @@ -424,10 +424,6 @@ impl SysBusDevOps for PL011 { true } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl StateTransfer for PL011 { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index dc5b94e09..9ad650b07 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -17,7 +17,7 @@ use anyhow::{Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use super::error::LegacyError; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::GuestAddress; @@ -101,11 +101,11 @@ impl PL031 { region_size: u64, ) -> Result<()> { self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "PL031") .with_context(|| LegacyError::SetSysResErr)?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size, "PL031")?; + sysbus.attach_device(&dev)?; MigrationManager::register_device_instance( PL031State::descriptor(), @@ -198,10 +198,6 @@ impl SysBusDevOps for PL031 { true } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl AmlBuilder for PL031 { diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 6dab9da23..3945de43e 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -252,7 +252,7 @@ impl Ramfb { pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { let dev = Arc::new(Mutex::new(self)); - sysbus.attach_dynamic_device(&dev)?; + sysbus.attach_device(&dev)?; Ok(()) } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index ccb907cbf..1e498084b 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -124,7 +124,7 @@ impl RTC { res: SysRes { region_base: RTC_PORT_INDEX, region_size: 8, - irq: -1, + ..Default::default() }, interrupt_evt: Some(Arc::new(create_new_eventfd()?)), ..Default::default() @@ -269,10 +269,10 @@ impl RTC { pub fn realize(mut self, sysbus: &mut SysBus) -> Result<()> { let region_base = self.base.res.region_base; let region_size = self.base.res.region_size; - self.set_sys_resource(sysbus, region_base, region_size)?; + self.set_sys_resource(sysbus, region_base, region_size, "RTC")?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size, "RTC")?; + sysbus.attach_device(&dev)?; Ok(()) } @@ -391,10 +391,6 @@ impl SysBusDevOps for RTC { } } - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } - fn reset(&mut self) -> Result<()> { self.cmos_data.fill(0); self.init_rtc_reg(); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 80415e075..dc729ffff 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -17,7 +17,7 @@ use anyhow::{bail, Context, Result}; use log::{debug, error}; use super::error::LegacyError; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlExtendedInterrupt, @@ -144,11 +144,11 @@ impl Serial { .realize() .with_context(|| "Failed to realize chardev")?; self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); - self.set_sys_resource(sysbus, region_base, region_size) + self.set_sys_resource(sysbus, region_base, region_size, "Serial") .with_context(|| LegacyError::SetSysResErr)?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size, "Serial")?; + sysbus.attach_device(&dev)?; MigrationManager::register_device_instance( SerialState::descriptor(), @@ -415,10 +415,6 @@ impl SysBusDevOps for Serial { fn get_irq(&self, _sysbus: &mut SysBus) -> Result { Ok(UART_IRQ) } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl AmlBuilder for Serial { diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 6083bb3f0..95c71ec8b 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -118,84 +118,82 @@ impl SysBus { } } - pub fn attach_device( - &mut self, - dev: &Arc>, - region_base: u64, - region_size: u64, - region_name: &str, - ) -> Result<()> { - let region_ops = self.build_region_ops(dev); - let region = Region::init_io_region(region_size, region_ops, region_name); - let locked_dev = dev.lock().unwrap(); - - region.set_ioeventfds(&locked_dev.ioeventfds()); - match locked_dev.sysbusdev_base().dev_type { - SysBusDevType::Serial if cfg!(target_arch = "x86_64") => { - #[cfg(target_arch = "x86_64")] - self.sys_io - .root() - .add_subregion(region, region_base) - .with_context(|| { - format!( - "Failed to register region in I/O space: offset={},size={}", - region_base, region_size - ) - })?; - } - SysBusDevType::FwCfg if cfg!(target_arch = "x86_64") => { - #[cfg(target_arch = "x86_64")] - self.sys_io + pub fn attach_device(&mut self, dev: &Arc>) -> Result<()> { + let res = dev.lock().unwrap().get_sys_resource().clone(); + let region_base = res.region_base; + let region_size = res.region_size; + let region_name = res.region_name; + + // region_base/region_size are both 0 means this device doesn't have its own memory layout. + // The normally allocated device region_base is above the `MEM_LAYOUT[LayoutEntryType::Mmio as usize].0`. + if region_base != 0 && region_size != 0 { + let region_ops = self.build_region_ops(dev); + let region = Region::init_io_region(region_size, region_ops, ®ion_name); + let locked_dev = dev.lock().unwrap(); + + region.set_ioeventfds(&locked_dev.ioeventfds()); + match locked_dev.sysbusdev_base().dev_type { + SysBusDevType::Serial if cfg!(target_arch = "x86_64") => { + #[cfg(target_arch = "x86_64")] + self.sys_io + .root() + .add_subregion(region, region_base) + .with_context(|| { + format!( + "Failed to register region in I/O space: offset={},size={}", + region_base, region_size + ) + })?; + } + SysBusDevType::FwCfg if cfg!(target_arch = "x86_64") => { + #[cfg(target_arch = "x86_64")] + self.sys_io + .root() + .add_subregion(region, region_base) + .with_context(|| { + format!( + "Failed to register region in I/O space: offset 0x{:x}, size {}", + region_base, region_size + ) + })?; + } + SysBusDevType::Rtc if cfg!(target_arch = "x86_64") => { + #[cfg(target_arch = "x86_64")] + self.sys_io + .root() + .add_subregion(region, region_base) + .with_context(|| { + format!( + "Failed to register region in I/O space: offset 0x{:x}, size {}", + region_base, region_size + ) + })?; + } + _ => self + .sys_mem .root() .add_subregion(region, region_base) .with_context(|| { format!( - "Failed to register region in I/O space: offset 0x{:x}, size {}", + "Failed to register region in memory space: offset={},size={}", region_base, region_size ) - })?; + })?, } - SysBusDevType::Rtc if cfg!(target_arch = "x86_64") => { - #[cfg(target_arch = "x86_64")] - self.sys_io - .root() - .add_subregion(region, region_base) - .with_context(|| { - format!( - "Failed to register region in I/O space: offset 0x{:x}, size {}", - region_base, region_size - ) - })?; - } - _ => self - .sys_mem - .root() - .add_subregion(region, region_base) - .with_context(|| { - format!( - "Failed to register region in memory space: offset={},size={}", - region_base, region_size - ) - })?, } self.devices.push(dev.clone()); Ok(()) } - - pub fn attach_dynamic_device( - &mut self, - dev: &Arc>, - ) -> Result<()> { - self.devices.push(dev.clone()); - Ok(()) - } } -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct SysRes { + // Note: region_base/region_size are both 0 means that this device doesn't have its own memory layout. + // The normally allocated device memory region is above the `MEM_LAYOUT[LayoutEntryType::Mmio as usize].0`. pub region_base: u64, pub region_size: u64, + pub region_name: String, pub irq: i32, } @@ -204,6 +202,7 @@ impl Default for SysRes { Self { region_base: 0, region_size: 0, + region_name: "".to_string(), irq: -1, } } @@ -257,10 +256,11 @@ impl SysBusDevBase { } } - pub fn set_sys(&mut self, irq: i32, region_base: u64, region_size: u64) { + pub fn set_sys(&mut self, irq: i32, region_base: u64, region_size: u64, region_name: &str) { self.res.irq = irq; self.res.region_base = region_base; self.res.region_size = region_size; + self.res.region_name = region_name.to_string(); } } @@ -306,8 +306,8 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder { Ok(irq) } - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - None + fn get_sys_resource(&mut self) -> &mut SysRes { + &mut self.sysbusdev_base_mut().res } fn set_sys_resource( @@ -315,6 +315,7 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder { sysbus: &mut SysBus, region_base: u64, region_size: u64, + region_name: &str, ) -> Result<()> { let irq = self.get_irq(sysbus)?; let interrupt_evt = self.sysbusdev_base().interrupt_evt.clone(); @@ -326,7 +327,7 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder { irq_state.register_irq()?; self.sysbusdev_base_mut() - .set_sys(irq, region_base, region_size); + .set_sys(irq, region_base, region_size, region_name); Ok(()) } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index eb09093fe..2d0598af3 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -26,7 +26,7 @@ use crate::{ QUEUE_TYPE_PACKED_VRING, VIRTIO_F_RING_PACKED, VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, }; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; -use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use devices::{Device, DeviceBase}; #[cfg(target_arch = "x86_64")] use machine_manager::config::{BootSource, Param}; @@ -170,7 +170,7 @@ impl VirtioMmioDevice { if region_base >= sysbus.mmio_region.1 { bail!("Mmio region space exhausted."); } - self.set_sys_resource(sysbus, region_base, region_size)?; + self.set_sys_resource(sysbus, region_base, region_size, "VirtioMmio")?; self.assign_interrupt_cb(); self.device .lock() @@ -179,7 +179,7 @@ impl VirtioMmioDevice { .with_context(|| "Failed to realize virtio.")?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev, region_base, region_size, "VirtioMmio")?; + sysbus.attach_device(&dev)?; #[cfg(target_arch = "x86_64")] bs.lock().unwrap().kernel_cmdline.push(Param { @@ -530,10 +530,6 @@ impl SysBusDevOps for VirtioMmioDevice { } ret } - - fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { - Some(&mut self.base.res) - } } impl acpi::AmlBuilder for VirtioMmioDevice { -- Gitee From 93994cfeda19a3c5918e45c580e585a1e285cbf7 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 7 May 2024 15:25:57 +0300 Subject: [PATCH 1843/2187] net: Tweak rx queue notify Suppress rx virtio queue notifications when the queue isn't empty. Signed-off-by: goriainovstanislav --- virtio/src/device/net.rs | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 0e5a9a847..198f4b7f3 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -688,7 +688,7 @@ impl TxVirtio { } struct RxVirtio { - queue_full: bool, + queue_avail: bool, queue: Arc>, queue_evt: Arc, } @@ -696,7 +696,7 @@ struct RxVirtio { impl RxVirtio { fn new(queue: Arc>, queue_evt: Arc) -> Self { RxVirtio { - queue_full: false, + queue_avail: false, queue, queue_evt, } @@ -795,7 +795,11 @@ impl NetIoHandler { .pop_avail(&self.mem_space, self.driver_features) .with_context(|| "Failed to pop avail ring for net rx")?; if elem.desc_num == 0 { - self.rx.queue_full = true; + queue + .vring + .suppress_queue_notify(&self.mem_space, self.driver_features, false) + .with_context(|| "Failed to enable rx queue notify")?; + self.rx.queue_avail = false; break; } else if elem.in_iovec.is_empty() { bail!("The length of in iovec is 0"); @@ -868,7 +872,7 @@ impl NetIoHandler { self.rx .queue_evt .write(1) - .with_context(|| "Failed to trigger tap queue event".to_string())?; + .with_context(|| "Failed to trigger rx queue event".to_string())?; break; } } @@ -967,7 +971,7 @@ impl NetIoHandler { fn tap_fd_handler(net_io: &mut Self) -> Vec { let mut notifiers = Vec::new(); - if !net_io.is_listening && (!net_io.rx.queue_full || net_io.tx.tap_full) { + if !net_io.is_listening && (net_io.rx.queue_avail || net_io.tx.tap_full) { notifiers.push(EventNotifier::new( NotifierOperation::Resume, net_io.tap_fd, @@ -983,12 +987,12 @@ impl NetIoHandler { } // NOTE: We want to poll for OUT event when the tap is full, and for IN event when the - // virtio queue is NOT full. - let tap_events = match (net_io.rx.queue_full, net_io.tx.tap_full) { - (false, true) => EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, - (true, true) => EventSet::OUT | EventSet::EDGE_TRIGGERED, - (false, false) => EventSet::IN | EventSet::EDGE_TRIGGERED, - (true, false) => EventSet::empty(), + // virtio queue is available. + let tap_events = match (net_io.rx.queue_avail, net_io.tx.tap_full) { + (true, true) => EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, + (false, true) => EventSet::OUT | EventSet::EDGE_TRIGGERED, + (true, false) => EventSet::IN | EventSet::EDGE_TRIGGERED, + (false, false) => EventSet::empty(), }; let tap_operation = if tap_events.is_empty() { @@ -1103,7 +1107,24 @@ impl EventNotifierHelper for NetIoHandler { return None; } - locked_net_io.rx.queue_full = false; + locked_net_io.rx.queue_avail = true; + let mut locked_queue = locked_net_io.rx.queue.lock().unwrap(); + + if let Err(ref err) = locked_queue.vring.suppress_queue_notify( + &locked_net_io.mem_space, + locked_net_io.driver_features, + true, + ) { + error!("Failed to suppress rx queue notify: {:?}", err); + report_virtio_error( + locked_net_io.interrupt_cb.clone(), + locked_net_io.driver_features, + &locked_net_io.device_broken, + ); + return None; + }; + + drop(locked_queue); if let Err(ref err) = locked_net_io.handle_rx() { error!("Failed to handle receive queue event: {:?}", err); -- Gitee From b8870973d365dd0ad43ec1744bddd9deb61bda34 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Thu, 23 May 2024 16:30:04 +0800 Subject: [PATCH 1844/2187] ohcam: remove useless format Now OH camera subsystem supports NV12&YUY2 We don't need NV21, let's remove it. Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 2bf3253f5..3debe893c 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -30,11 +30,7 @@ static OHCAM_CALLBACK: Lazy = Lazy::new(|| RwLock::new(OhCamCallBack::d // So, fps * interval / 10_000_000 == 1. const FPS_INTERVAL_TRANS: u32 = 10_000_000; const RESOLUTION_WHITELIST: [(i32, i32); 2] = [(640, 480), (1280, 720)]; -const FRAME_FORMAT_WHITELIST: [i32; 3] = [ - CAMERA_FORMAT_YUV420SP, - CAMERA_FORMAT_YUYV422, - CAMERA_FORMAT_NV12, -]; +const FRAME_FORMAT_WHITELIST: [i32; 2] = [CAMERA_FORMAT_YUYV422, CAMERA_FORMAT_NV12]; const FPS_WHITELIST: [i32; 1] = [30]; #[derive(Default)] -- Gitee From 23cd52ef2bd6cd85231b28d0f67d33484f75575d Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 24 May 2024 01:51:01 +0800 Subject: [PATCH 1845/2187] clippy: fix warnings Fix these warnings: warning: constant `WINDOWS_EMU_PID_DEFAULT_INTERVAL` is never used --> machine/src/lib.rs:110:7 | 110 | const WINDOWS_EMU_PID_DEFAULT_INTERVAL: u64 = 4000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default warning: constant `WINDOWS_EMU_PID_SHUTDOWN_INTERVAL` is never used --> machine/src/lib.rs:111:7 | 111 | const WINDOWS_EMU_PID_SHUTDOWN_INTERVAL: u64 = 1000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: constant `WINDOWS_EMU_PID_POWERDOWN_INTERVAL` is never used --> machine/src/lib.rs:112:7 | 112 | const WINDOWS_EMU_PID_POWERDOWN_INTERVAL: u64 = 30000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Signed-off-by: liuxiangdong --- machine/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c1f103142..bde75da4c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -107,8 +107,11 @@ use virtio::{ #[cfg(feature = "virtio_gpu")] use virtio::{Gpu, GpuDevConfig}; +#[cfg(feature = "windows_emu_pid")] const WINDOWS_EMU_PID_DEFAULT_INTERVAL: u64 = 4000; +#[cfg(feature = "windows_emu_pid")] const WINDOWS_EMU_PID_SHUTDOWN_INTERVAL: u64 = 1000; +#[cfg(feature = "windows_emu_pid")] const WINDOWS_EMU_PID_POWERDOWN_INTERVAL: u64 = 30000; /// Machine structure include base members. -- Gitee From cd2ec1a213e57233c82eb6ac4db92fce2c309e32 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 24 May 2024 12:47:01 +0800 Subject: [PATCH 1846/2187] test: use unified function to create virtio device Use unified function to create virtio pci/mmio device, and then remove these duplicate codes. Signed-off-by: liuxiangdong --- virtio/src/transport/virtio_mmio.rs | 70 +++-------- virtio/src/transport/virtio_pci.rs | 187 +++++----------------------- 2 files changed, 45 insertions(+), 212 deletions(-) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 2d0598af3..77d0ca0a0 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -594,7 +594,7 @@ impl MigrationHook for VirtioMmioDevice { } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use crate::{ check_config_space_rw, read_config_default, VirtioBase, QUEUE_TYPE_SPLIT_VRING, @@ -602,7 +602,7 @@ mod tests { }; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - fn address_space_init() -> Arc { + pub fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "sysmem"); let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); let host_mmap = Arc::new( @@ -697,9 +697,9 @@ mod tests { } } - #[test] - fn test_virtio_mmio_device_new() { + fn virtio_mmio_test_init() -> (Arc>, VirtioMmioDevice) { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); + let sys_space = address_space_init(); let virtio_mmio_device = VirtioMmioDevice::new( &sys_space, @@ -707,6 +707,12 @@ mod tests { virtio_device.clone(), ); + (virtio_device, virtio_mmio_device) + } + + #[test] + fn test_virtio_mmio_device_new() { + let (virtio_device, virtio_mmio_device) = virtio_mmio_test_init(); let locked_device = virtio_device.lock().unwrap(); assert_eq!(locked_device.device_activated(), false); assert_eq!( @@ -723,13 +729,7 @@ mod tests { #[test] fn test_virtio_mmio_device_read_01() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); // read the register of magic value @@ -786,13 +786,7 @@ mod tests { #[test] fn test_virtio_mmio_device_read_02() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); // read the register representing max size of the queue @@ -882,13 +876,7 @@ mod tests { #[test] fn test_virtio_mmio_device_read_03() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); // read the configuration atomic value @@ -934,13 +922,7 @@ mod tests { #[test] fn test_virtio_mmio_device_write_01() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); // write the selector for device features @@ -1052,13 +1034,7 @@ mod tests { #[test] fn test_virtio_mmio_device_write_02() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); // write the ready status of queue @@ -1123,13 +1099,7 @@ mod tests { #[test] fn test_virtio_mmio_device_write_03() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); // write the low 32bit of queue's descriptor table address @@ -1252,13 +1222,7 @@ mod tests { #[test] fn test_virtio_mmio_device_write_04() { - let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_space = address_space_init(); - let mut virtio_mmio_device = VirtioMmioDevice::new( - &sys_space, - "test_virtio_mmio_device".to_string(), - virtio_device.clone(), - ); + let (virtio_device, mut virtio_mmio_device) = virtio_mmio_test_init(); let addr = GuestAddress(0); virtio_mmio_device.assign_interrupt_cb(); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 8ca540156..111ae3de0 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1385,8 +1385,9 @@ mod tests { use vmm_sys_util::eventfd::EventFd; use super::*; + use crate::transport::virtio_mmio::tests::address_space_init; use crate::VirtioBase; - use address_space::{AddressSpace, GuestAddress, HostMemMapping}; + use address_space::{AddressSpace, GuestAddress}; use devices::pci::{ config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC}, le_read_u16, @@ -1468,31 +1469,40 @@ mod tests { }; } - #[test] - fn test_common_config_dev_feature() { + fn virtio_pci_test_init( + multi_func: bool, + ) -> ( + Arc>, + Arc>, + VirtioPciDevice, + ) { let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); + let sys_mem = address_space_init(); let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), ))); - let mut virtio_pci = VirtioPciDevice::new( + let virtio_pci = VirtioPciDevice::new( String::from("test device"), 0, sys_mem, virtio_dev.clone(), Arc::downgrade(&parent_bus), - false, + multi_func, false, ); + // Note: if parent_bus is used in the code execution during the testing process, a variable needs to + // be used to maintain the count and avoid rust from automatically releasing this `Arc`. + (virtio_dev, parent_bus, virtio_pci) + } + + #[test] + fn test_common_config_dev_feature() { + let (virtio_dev, _, mut virtio_pci) = virtio_pci_test_init(false); + // Read virtio device features virtio_dev.lock().unwrap().set_hfeatures_sel(0_u32); com_cfg_read_test!(virtio_pci, COMMON_DF_REG, 0xFFFF_FFF0_u32); @@ -1528,28 +1538,7 @@ mod tests { #[test] fn test_common_config_queue() { - let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - let parent_bus = Arc::new(Mutex::new(PciBus::new( - String::from("test bus"), - #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16, "parent_bus"), - sys_mem.root().clone(), - ))); - let virtio_pci = VirtioPciDevice::new( - String::from("test device"), - 0, - sys_mem, - virtio_dev.clone(), - Arc::downgrade(&parent_bus), - false, - false, - ); + let (virtio_dev, _, virtio_pci) = virtio_pci_test_init(false); // Read Queue's Descriptor Table address virtio_dev @@ -1579,29 +1568,7 @@ mod tests { #[test] fn test_common_config_queue_error() { - let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - let parent_bus = Arc::new(Mutex::new(PciBus::new( - String::from("test bus"), - #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16, "parent_bus"), - sys_mem.root().clone(), - ))); - let cloned_virtio_dev = virtio_dev.clone(); - let mut virtio_pci = VirtioPciDevice::new( - String::from("test device"), - 0, - sys_mem, - cloned_virtio_dev, - Arc::downgrade(&parent_bus), - false, - false, - ); + let (virtio_dev, _, mut virtio_pci) = virtio_pci_test_init(false); assert!(init_msix( &mut virtio_pci.base, @@ -1654,29 +1621,8 @@ mod tests { #[test] fn test_virtio_pci_config_access() { - let virtio_dev: Arc> = - Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - let parent_bus = Arc::new(Mutex::new(PciBus::new( - String::from("test bus"), - #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16, "parent_bus"), - sys_mem.root().clone(), - ))); - let mut virtio_pci = VirtioPciDevice::new( - String::from("test device"), - 0, - sys_mem, - virtio_dev, - Arc::downgrade(&parent_bus), - false, - false, - ); + let (_, _parent_bus, mut virtio_pci) = virtio_pci_test_init(false); + virtio_pci.init_write_mask(false).unwrap(); virtio_pci.init_write_clear_mask(false).unwrap(); @@ -1695,69 +1641,14 @@ mod tests { #[test] fn test_virtio_pci_realize() { - let virtio_dev: Arc> = - Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - let parent_bus = Arc::new(Mutex::new(PciBus::new( - String::from("test bus"), - #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16, "parent_bus"), - sys_mem.root().clone(), - ))); - let virtio_pci = VirtioPciDevice::new( - String::from("test device"), - 0, - sys_mem, - virtio_dev, - Arc::downgrade(&parent_bus), - false, - false, - ); + let (_, _parent_bus, virtio_pci) = virtio_pci_test_init(false); assert!(virtio_pci.realize().is_ok()); } #[test] fn test_device_activate() { - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - let mem_size: u64 = 1024 * 1024; - let host_mmap = Arc::new( - HostMemMapping::new(GuestAddress(0), None, mem_size, None, false, false, false) - .unwrap(), - ); - sys_mem - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "sysmem"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); + let (virtio_dev, _parent_bus, mut virtio_pci) = virtio_pci_test_init(false); - let virtio_dev = Arc::new(Mutex::new(VirtioDeviceTest::new())); - let parent_bus = Arc::new(Mutex::new(PciBus::new( - String::from("test bus"), - #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16, "parent_bus"), - sys_mem.root().clone(), - ))); - let mut virtio_pci = VirtioPciDevice::new( - String::from("test device"), - 0, - sys_mem, - virtio_dev.clone(), - Arc::downgrade(&parent_bus), - false, - false, - ); #[cfg(target_arch = "aarch64")] virtio_pci.base.config.set_interrupt_pin(); @@ -1827,29 +1718,7 @@ mod tests { #[test] fn test_multifunction() { - let virtio_dev: Arc> = - Arc::new(Mutex::new(VirtioDeviceTest::new())); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - let parent_bus = Arc::new(Mutex::new(PciBus::new( - String::from("test bus"), - #[cfg(target_arch = "x86_64")] - Region::init_container_region(1 << 16, "parent_bus"), - sys_mem.root().clone(), - ))); - let mut virtio_pci = VirtioPciDevice::new( - String::from("test device"), - 24, - sys_mem, - virtio_dev, - Arc::downgrade(&parent_bus), - true, - false, - ); + let (_, _parent_bus, mut virtio_pci) = virtio_pci_test_init(true); assert!(init_multifunction( virtio_pci.multi_func, -- Gitee From e88f6f0f4b1a9685210226b6f3949574b2f27238 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Thu, 23 May 2024 21:35:07 +0800 Subject: [PATCH 1847/2187] iothread: stratovirt exit wait for iothread exit When the process exits, resources are reclaimed, but the iothread is still running. As a result, some unexpected results are caused. Signed-off-by: Mingwang Li --- machine/src/aarch64/standard.rs | 2 ++ machine/src/micro_common/mod.rs | 2 +- machine/src/x86_64/standard.rs | 2 ++ machine_manager/src/event_loop.rs | 47 ++++++++++++++++++++++----- machine_manager/src/signal_handler.rs | 2 +- src/main.rs | 6 ++-- util/src/loop_context.rs | 26 +++++++-------- 7 files changed, 59 insertions(+), 28 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 3dd906e82..bb46ce382 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -66,6 +66,7 @@ use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, DriveConfig, MigrateMode, NumaNode, SerialConfig, VmConfig, }; use machine_manager::event; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, VmState, @@ -271,6 +272,7 @@ impl StdMachine { } } + EventLoop::kick_all(); info!("vm destroy"); Ok(()) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index c8b96d597..49cd15348 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -532,7 +532,7 @@ impl MachineLifecycle for LightMachine { } info!("vm destroy"); - EventLoop::get_ctx(None).unwrap().kick(); + EventLoop::kick_all(); true } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index fdb88d1ef..289737e19 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -48,6 +48,7 @@ use machine_manager::config::{ parse_incoming_uri, BootIndexInfo, DriveConfig, MigrateMode, NumaNode, SerialConfig, VmConfig, }; use machine_manager::event; +use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, MigrateInterface, VmState, @@ -220,6 +221,7 @@ impl StdMachine { } } + EventLoop::kick_all(); info!("vm destroy"); Ok(()) diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 7acaca84a..79d7cf6f9 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::os::unix::prelude::RawFd; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Barrier, Mutex}; use std::{process, thread}; use anyhow::{bail, Result}; @@ -49,9 +49,18 @@ impl EventLoop { /// * `iothreads` - refer to `-iothread` params pub fn object_init(iothreads: &Option>) -> Result<()> { let mut io_threads = HashMap::new(); + let cnt = match iothreads { + Some(thrs) => thrs.len(), + None => 0, + }; + let thread_exit_barrier = Arc::new(Barrier::new(cnt + 1)); + if let Some(thrs) = iothreads { for thr in thrs { - io_threads.insert(thr.id.clone(), EventLoopContext::new()); + io_threads.insert( + thr.id.clone(), + EventLoopContext::new(thread_exit_barrier.clone()), + ); } } @@ -60,7 +69,7 @@ impl EventLoop { unsafe { if GLOBAL_EVENT_LOOP.is_none() { GLOBAL_EVENT_LOOP = Some(EventLoop { - main_loop: EventLoopContext::new(), + main_loop: EventLoopContext::new(thread_exit_barrier), io_threads, }); @@ -76,10 +85,11 @@ impl EventLoop { }; IOTHREADS.lock().unwrap().push(iothread_info); while let Ok(ret) = ctx.iothread_run() { - if !ret { + if !ret || get_signal() != 0 { break; } } + ctx.thread_exit_barrier.wait(); })?; } } else { @@ -115,11 +125,16 @@ impl EventLoop { /// /// # Arguments /// - /// * `manager` - The main part to manager the event loop specified by name. - /// * `name` - specify which event loop to manage - pub fn set_manager(manager: Arc>, name: Option<&String>) { - if let Some(ctx) = Self::get_ctx(name) { - ctx.set_manager(manager) + /// * `manager` - The main part to manager the event loop. + pub fn set_manager(manager: Arc>) { + // SAFETY: All concurrently accessed data of EventLoopContext is protected. + unsafe { + if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { + event_loop.main_loop.set_manager(manager.clone()); + for (_name, io_thread) in event_loop.io_threads.iter_mut() { + io_thread.set_manager(manager.clone()); + } + } } } @@ -152,10 +167,12 @@ impl EventLoop { let sig_num = get_signal(); if sig_num != 0 { info!("MainLoop exits due to receive signal {}", sig_num); + event_loop.main_loop.thread_exit_barrier.wait(); return Ok(()); } if !event_loop.main_loop.run()? { info!("MainLoop exits due to guest internal operation."); + event_loop.main_loop.thread_exit_barrier.wait(); return Ok(()); } } @@ -172,6 +189,18 @@ impl EventLoop { GLOBAL_EVENT_LOOP = None; } } + + pub fn kick_all() { + // SAFETY: All concurrently accessed data of EventLoopContext is protected. + unsafe { + if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { + for (_name, io_thread) in event_loop.io_threads.iter_mut() { + io_thread.kick(); + } + event_loop.main_loop.kick(); + } + } + } } pub fn register_event_helper( diff --git a/machine_manager/src/signal_handler.rs b/machine_manager/src/signal_handler.rs index 2f1ba86e9..6d679192d 100644 --- a/machine_manager/src/signal_handler.rs +++ b/machine_manager/src/signal_handler.rs @@ -50,7 +50,7 @@ pub fn set_signal(num: c_int) { unsafe { RECEIVED_SIGNAL.store(num, Ordering::SeqCst); } - EventLoop::get_ctx(None).unwrap().kick(); + EventLoop::kick_all(); } } diff --git a/src/main.rs b/src/main.rs index 89c4e23e4..5f0381032 100644 --- a/src/main.rs +++ b/src/main.rs @@ -160,7 +160,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res LightMachine::new(vm_config).with_context(|| "Failed to init MicroVM")?, )); MachineOps::realize(&vm, vm_config).with_context(|| "Failed to realize micro VM.")?; - EventLoop::set_manager(vm.clone(), None); + EventLoop::set_manager(vm.clone()); for listener in listeners { sockets.push(Socket::from_listener(listener, Some(vm.clone()))); @@ -173,7 +173,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res )); MachineOps::realize(&vm, vm_config) .with_context(|| "Failed to realize standard VM.")?; - EventLoop::set_manager(vm.clone(), None); + EventLoop::set_manager(vm.clone()); if is_test_enabled() { let sock_path = cmd_args.value_of("mod-test"); @@ -199,7 +199,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let vm = Arc::new(Mutex::new( StdMachine::new(vm_config).with_context(|| "Failed to init NoneVM")?, )); - EventLoop::set_manager(vm.clone(), None); + EventLoop::set_manager(vm.clone()); for listener in listeners { sockets.push(Socket::from_listener(listener, Some(vm.clone()))); diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ecad86e29..ded2f1fec 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Barrier, Mutex, RwLock}; use std::time::{Duration, Instant}; use anyhow::{anyhow, Context, Result}; @@ -219,6 +219,8 @@ pub struct EventLoopContext { pub thread_pool: Arc, /// Record VM clock state. pub clock_state: Arc>, + /// The io thread barrier. + pub thread_exit_barrier: Arc, } impl Drop for EventLoopContext { @@ -235,7 +237,7 @@ unsafe impl Send for EventLoopContext {} impl EventLoopContext { /// Constructs a new `EventLoopContext`. - pub fn new() -> Self { + pub fn new(thread_exit_barrier: Arc) -> Self { let mut ctx = EventLoopContext { epoll: Epoll::new().unwrap(), manager: None, @@ -249,6 +251,7 @@ impl EventLoopContext { timer_next_id: AtomicU64::new(0), thread_pool: Arc::new(ThreadPool::default()), clock_state: Arc::new(Mutex::new(ClockState::default())), + thread_exit_barrier, }; ctx.init_kick(); ctx @@ -716,12 +719,6 @@ impl EventLoopContext { } } -impl Default for EventLoopContext { - fn default() -> Self { - Self::new() - } -} - pub fn read_fd(fd: RawFd) -> u64 { let mut value: u64 = 0; @@ -746,6 +743,7 @@ pub fn read_fd(fd: RawFd) -> u64 { #[cfg(test)] mod test { use std::os::unix::io::{AsRawFd, RawFd}; + use std::sync::Barrier; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; @@ -794,7 +792,7 @@ mod test { #[test] fn basic_test() { - let mut mainloop = EventLoopContext::new(); + let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1))); let mut notifiers = Vec::new(); let fd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let fd1_related = EventFd::new(EFD_NONBLOCK).unwrap(); @@ -822,7 +820,7 @@ mod test { #[test] fn parked_event_test() { - let mut mainloop = EventLoopContext::new(); + let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1))); let mut notifiers = Vec::new(); let fd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let fd2 = EventFd::new(EFD_NONBLOCK).unwrap(); @@ -869,7 +867,7 @@ mod test { #[test] fn event_handler_test() { - let mut mainloop = EventLoopContext::new(); + let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1))); let mut notifiers = Vec::new(); let fd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let fd1_related = EventFd::new(EFD_NONBLOCK).unwrap(); @@ -908,7 +906,7 @@ mod test { #[test] fn error_operation_test() { - let mut mainloop = EventLoopContext::new(); + let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1))); let fd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let leisure_fd = EventFd::new(EFD_NONBLOCK).unwrap(); @@ -945,7 +943,7 @@ mod test { #[test] fn error_parked_operation_test() { - let mut mainloop = EventLoopContext::new(); + let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1))); let fd1 = EventFd::new(EFD_NONBLOCK).unwrap(); let fd2 = EventFd::new(EFD_NONBLOCK).unwrap(); @@ -980,7 +978,7 @@ mod test { #[test] fn fd_released_test() { - let mut mainloop = EventLoopContext::new(); + let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1))); let fd = mainloop.create_event(); // In this case, fd is already closed. But program was wrote to ignore the error. -- Gitee From 8516efc5ede2b9fe04b0427db208e87a5acd50e9 Mon Sep 17 00:00:00 2001 From: Yan Wang Date: Sat, 25 May 2024 15:31:57 +0800 Subject: [PATCH 1848/2187] ohui: add hitrace for ohui message Signed-off-by: Yan Wang --- trace/trace_info/ui.toml | 6 ++++++ ui/src/ohui_srv/msg_handle.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/trace/trace_info/ui.toml b/trace/trace_info/ui.toml index 3c63d3289..1df64556a 100644 --- a/trace/trace_info/ui.toml +++ b/trace/trace_info/ui.toml @@ -273,3 +273,9 @@ name = "oh_event_unsupported_type" args = "ty: &dyn fmt::Debug, size: u32" message = "type={:?} body_size={}" enabled = true + +[[scopes]] +name = "handle_msg" +args = "opcode: &dyn fmt::Debug" +message = "handle ohui {:?} message" +enabled = true diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index eeb44c15c..6b71cf9fe 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -170,6 +170,7 @@ impl OhUiMsgHandler { reader.clear(); return Ok(()); } + trace::trace_scope_start!(handle_msg, args = (&event_type)); let body_bytes = reader.body.as_ref().unwrap(); if let Err(e) = match event_type { -- Gitee From fbbbbd52581ae0d443e745df05355806dae54fd4 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 27 May 2024 14:40:06 +0800 Subject: [PATCH 1849/2187] ohaudio: fixup hang issue while destorying capturer Currently we stop capturer firstly then set start to false. The capture callback might loop forever because start is true. Then the stop function we called would hang because capture callback cannot reuturn. So let's set start to false firstly and then call stop function. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index f1ea10615..10f9cb856 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -212,8 +212,8 @@ impl OhAudioProcess for OhAudioCapture { fn destroy(&mut self) { if self.ctx.is_some() { if self.start { - self.ctx.as_mut().unwrap().stop(); self.start = false; + self.ctx.as_mut().unwrap().stop(); } self.ctx = None; } -- Gitee From b62052fab463b659ecb50ab06df17723118bbcad Mon Sep 17 00:00:00 2001 From: li-huachao Date: Mon, 27 May 2024 19:29:26 +0800 Subject: [PATCH 1850/2187] Balloon: introduce scopes traces. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add scopes traces for balloon when handle normal balloon、fpr and auto-balloon. --- trace/trace_info/virtio.toml | 18 ++++++++++++++++++ virtio/src/device/balloon.rs | 3 +++ 2 files changed, 21 insertions(+) diff --git a/trace/trace_info/virtio.toml b/trace/trace_info/virtio.toml index 7bff17c5e..1f8ea2415 100644 --- a/trace/trace_info/virtio.toml +++ b/trace/trace_info/virtio.toml @@ -297,3 +297,21 @@ name = "vhost_delete_mem_range_failed" args = "" message = "Vhost: deleting mem region failed: not matched." enabled = true + +[[scopes]] +name = "auto_msg_evt_handler" +args = "" +message = "Balloon: handle auto balloon message" +enabled = true + +[[scopes]] +name = "reporting_evt_handler" +args = "" +message = "Balloon: handle fpr message" +enabled = true + +[[scopes]] +name = "process_balloon_queue" +args = "" +message = "Balloon: handle normal balloon message" +enabled = true diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index bc86b048b..33f89d3e8 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -612,6 +612,7 @@ impl BalloonIoHandler { /// if `req_type` is `BALLOON_INFLATE_EVENT`, then inflate the balloon, otherwise, deflate the /// balloon. fn process_balloon_queue(&mut self, req_type: bool) -> Result<()> { + trace::trace_scope_start!(process_balloon_queue); let queue = if req_type { trace::virtio_receive_request("Balloon".to_string(), "to inflate".to_string()); &self.inf_queue @@ -648,6 +649,7 @@ impl BalloonIoHandler { } fn reporting_evt_handler(&mut self) -> Result<()> { + trace::trace_scope_start!(reporting_evt_handler); let queue = self .report_queue .as_ref() @@ -682,6 +684,7 @@ impl BalloonIoHandler { } fn auto_msg_evt_handler(&mut self) -> Result<()> { + trace::trace_scope_start!(auto_msg_evt_handler); let queue = self .msg_queue .as_ref() -- Gitee From 6bdb590d1a24e4f6bac5dcc8d8d6342444b0c625 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 28 May 2024 11:04:31 +0800 Subject: [PATCH 1851/2187] OHUI: fix related files' permisson Permission of ohui's cursor/fb/sock is default(644) now, We limit it to 600, removing redundant permission. --- ui/src/ohui_srv/channel.rs | 15 +++++++++++++-- ui/src/ohui_srv/mod.rs | 14 +++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 861a16342..7818367b6 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -19,7 +19,7 @@ use libc::iovec; use log::error; use util::byte_code::ByteCode; -use util::unix::UnixSock; +use util::unix::{limit_permission, UnixSock}; pub struct OhUiChannel { pub sock: RwLock, @@ -35,7 +35,18 @@ impl OhUiChannel { } pub fn bind(&self) -> Result<()> { - self.sock.write().unwrap().bind(true) + match self.sock.write().unwrap().bind(true) { + Ok(_) => { + limit_permission(self.path.as_str()).unwrap_or_else(|e| { + error!( + "Failed to limit permission for ohui-sock {}, err: {:?}", + self.path, e + ); + }); + Ok(()) + } + Err(e) => Err(e), + } } pub fn get_listener_raw_fd(&self) -> RawFd { diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index b5689b575..53cbbfbd2 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -52,7 +52,7 @@ use util::{ NotifierOperation, }, pixman::{pixman_format_code_t, pixman_image_t}, - unix::do_mmap, + unix::{do_mmap, limit_permission}, }; #[derive(Debug, Clone)] @@ -127,6 +127,12 @@ impl OhUiServer { .ok_or_else(|| anyhow!("init_fb_file: Failed to get str from {}", path))?; let fb_backend = FileBackend::new_mem(fb_file, VIRTIO_GPU_ENABLE_BAR0_SIZE)?; TempCleaner::add_path(fb_file.to_string()); + limit_permission(fb_file).unwrap_or_else(|e| { + error!( + "Failed to limit permission for ohui-fb {}, err: {:?}", + fb_file, e + ); + }); let host_addr = do_mmap( &Some(fb_backend.file.as_ref()), @@ -147,6 +153,12 @@ impl OhUiServer { .ok_or_else(|| anyhow!("init_cursor_file: Failed to get str from {}", path))?; let cursor_backend = FileBackend::new_mem(cursor_file, CURSOR_SIZE)?; TempCleaner::add_path(cursor_file.to_string()); + limit_permission(cursor_file).unwrap_or_else(|e| { + error!( + "Failed to limit permission for ohui-cursor {}, err: {:?}", + cursor_file, e + ); + }); let cursorbuffer = do_mmap( &Some(cursor_backend.file.as_ref()), -- Gitee From bb099c091115e46d0ed7d4c2042aa59ce8a3856d Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Tue, 28 May 2024 19:34:02 +0800 Subject: [PATCH 1852/2187] ged: replace store with fetch_or Using store may overwrite other events, so fetch_or is used instead. Signed-off-by: Mingwang Li --- devices/src/acpi/ged.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 55e5ba0f8..7a87180b1 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -120,7 +120,7 @@ impl Ged { read_fd(power_down_fd); ged_clone .notification_type - .store(AcpiEvent::PowerDown as u32, Ordering::SeqCst); + .fetch_or(AcpiEvent::PowerDown as u32, Ordering::SeqCst); ged_clone.inject_interrupt(); trace::ged_inject_acpi_event(AcpiEvent::PowerDown as u32); if QmpChannel::is_connected() { @@ -150,7 +150,7 @@ impl Ged { read_fd(cpu_resize_fd); clone_ged .notification_type - .store(AcpiEvent::CpuResize as u32, Ordering::SeqCst); + .fetch_or(AcpiEvent::CpuResize as u32, Ordering::SeqCst); clone_ged.inject_interrupt(); trace::ged_inject_acpi_event(AcpiEvent::CpuResize as u32); if QmpChannel::is_connected() { -- Gitee From fd468397266a37b8c0b347665262f385d315d840 Mon Sep 17 00:00:00 2001 From: li-huachao Date: Mon, 27 May 2024 20:33:17 +0800 Subject: [PATCH 1853/2187] Memory: introduce scopes traces. --- address_space/Cargo.toml | 1 + address_space/src/address_space.rs | 11 ++++++++ address_space/src/host_mmap.rs | 1 + machine/src/lib.rs | 1 + trace/trace_info/memory.toml | 41 ++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+) create mode 100644 trace/trace_info/memory.toml diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 7ff40882a..3c1a6e144 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -19,3 +19,4 @@ machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } +trace = { path = "../trace" } diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index bad13f3b9..fd419d77d 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -611,6 +611,7 @@ impl AddressSpace { /// /// Return Error if the `addr` is not mapped. pub fn read(&self, dst: &mut dyn std::io::Write, addr: GuestAddress, count: u64) -> Result<()> { + trace::trace_scope_start!(address_space_read, args = (&addr, count)); let view = self.flat_view.load(); view.read(dst, addr, count)?; @@ -629,6 +630,7 @@ impl AddressSpace { /// /// Return Error if the `addr` is not mapped. pub fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { + trace::trace_scope_start!(address_space_write, args = (&addr, count)); let view = self.flat_view.load(); if !*self.hyp_ioevtfd_enabled.get_or_init(|| false) { @@ -693,6 +695,10 @@ impl AddressSpace { /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { + trace::trace_scope_start!( + address_space_write_direct, + args = (host_addr, std::mem::size_of::()) + ); // Mark vmm dirty page manually if live migration is active. MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); @@ -732,6 +738,10 @@ impl AddressSpace { /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn read_object_direct(&self, host_addr: u64) -> Result { + trace::trace_scope_start!( + address_space_read_direct, + args = (host_addr, std::mem::size_of::()) + ); let mut obj = T::default(); let mut dst = obj.as_mut_bytes(); // SAFETY: host_addr is managed by address_space, it has been verified for legality. @@ -746,6 +756,7 @@ impl AddressSpace { /// Update the topology of memory. pub fn update_topology(&self) -> Result<()> { + trace::trace_scope_start!(address_update_topology); let old_fv = self.flat_view.load(); let addr_range = AddressRange::new(GuestAddress(0), self.root.size()); diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index da850b036..ca309770a 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -205,6 +205,7 @@ fn touch_pages(start: u64, page_size: u64, nr_pages: u64) { /// * `size` - Size of memory. /// * `nr_vcpus` - Number of vcpus. fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { + trace::trace_scope_start!(pre_alloc, args = (size)); let page_size = host_page_size(); let threads = max_nr_threads(nr_vcpus); let nr_pages = (size + page_size - 1) / page_size; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 990f9cc2d..f58634cf0 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -403,6 +403,7 @@ pub trait MachineOps { sys_mem: &Arc, nr_cpus: u8, ) -> Result<()> { + trace::trace_scope_start!(init_memory); let migrate_info = self.get_migrate_info(); if migrate_info.0 != MigrateMode::File { self.create_machine_ram(mem_config, nr_cpus)?; diff --git a/trace/trace_info/memory.toml b/trace/trace_info/memory.toml new file mode 100644 index 000000000..4ff485a5d --- /dev/null +++ b/trace/trace_info/memory.toml @@ -0,0 +1,41 @@ +[[scopes]] +name = "address_space_read" +args = "addr: &dyn fmt::Debug, count: u64" +message = "Memory: flatview_read addr {:?}, count {}" +enabled = true + +[[scopes]] +name = "address_space_write" +args = "addr: &dyn fmt::Debug, count: u64" +message = "Memory: flatview_write addr {:?}, count {}" +enabled = true + +[[scopes]] +name = "address_space_read_direct" +args = "host_addr: u64, count: usize" +message = "Memory: address_space_read_direct host_addr {}, count {}" +enabled = true + +[[scopes]] +name = "address_space_write_direct" +args = "host_addr: u64, count: usize" +message = "Memory: address_space_write_direct host_addr {}, count {}" +enabled = true + +[[scopes]] +name = "address_update_topology" +args = "" +message = "Memory: update opology" +enabled = true + +[[scopes]] +name = "pre_alloc" +args = "size: u64" +message = "Memory: pre_alloc ram size is {}" +enabled = true + +[[scopes]] +name = "init_memory" +args = "" +message = "Memory: init memory" +enabled = true -- Gitee From 117371621cdd843f235aa80b7e328558cb1c9e63 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 29 May 2024 14:43:32 +0800 Subject: [PATCH 1854/2187] ohui: optimize socket interface Because send/recv have better performance than sendmsg/recvmsg. Let's use util::socket instead of util::unix::UnixSock. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/channel.rs | 107 +++++++++++++--------------------- ui/src/ohui_srv/mod.rs | 31 +++++----- ui/src/ohui_srv/msg_handle.rs | 59 ++++++++----------- 3 files changed, 83 insertions(+), 114 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 7818367b6..561dd765e 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -10,83 +10,65 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::raw::c_void; +use std::io::{Read, Write}; +use std::os::fd::AsRawFd; use std::os::unix::io::RawFd; -use std::sync::RwLock; -use anyhow::Result; -use libc::iovec; +use anyhow::{bail, Result}; use log::error; use util::byte_code::ByteCode; -use util::unix::{limit_permission, UnixSock}; +use util::socket::{SocketListener, SocketStream}; +use util::unix::limit_permission; pub struct OhUiChannel { - pub sock: RwLock, pub path: String, + pub listener: SocketListener, + pub stream: Option, } impl OhUiChannel { - pub fn new(path: &str) -> Self { - OhUiChannel { - sock: RwLock::new(UnixSock::new(path)), - path: String::from(path), - } - } + pub fn new(path: &str) -> Result { + let listener = match SocketListener::bind_by_uds(path) { + Ok(l) => l, + Err(e) => bail!("Failed to create listener with path {}, {:?}", path, e), + }; + limit_permission(path.as_str()).unwrap_or_else(|e| { + error!( + "Failed to limit permission for ohui-sock {}, err: {:?}", + path, e + ); + }); - pub fn bind(&self) -> Result<()> { - match self.sock.write().unwrap().bind(true) { - Ok(_) => { - limit_permission(self.path.as_str()).unwrap_or_else(|e| { - error!( - "Failed to limit permission for ohui-sock {}, err: {:?}", - self.path, e - ); - }); - Ok(()) - } - Err(e) => Err(e), - } + Ok(OhUiChannel { + path: String::from(path), + listener, + stream: None, + }) } pub fn get_listener_raw_fd(&self) -> RawFd { - self.sock.read().unwrap().get_listener_raw_fd() - } - - pub fn get_stream_raw_fd(&self) -> RawFd { - self.sock.read().unwrap().get_stream_raw_fd() + self.listener.as_raw_fd() } - pub fn set_nonblocking(&self, nb: bool) -> Result<()> { - self.sock.read().unwrap().set_nonblocking(nb) + pub fn get_stream_raw_fd(&self) -> Option { + self.stream.as_ref().and_then(|s| Some(s.as_raw_fd())) } - pub fn set_listener_nonblocking(&self, nb: bool) -> Result<()> { - self.sock.read().unwrap().listen_set_nonblocking(nb) - } - - pub fn accept(&self) -> Result<()> { - self.sock.write().unwrap().accept() - } - - pub fn send(&self, data: *const u8, len: usize) -> Result { - let mut iovs = Vec::with_capacity(1); - iovs.push(iovec { - iov_base: data as *mut c_void, - iov_len: len, - }); - let ret = self.sock.read().unwrap().send_msg(&mut iovs, &[])?; - Ok(ret) + pub fn accept(&mut self) -> Result<()> { + self.stream = Some(self.listener.accept()?); + Ok(()) } - pub fn send_by_obj(&self, obj: &T) -> Result<()> { + pub fn send_by_obj(&mut self, obj: &T) -> Result<()> { + let stream = self.get_stream()?; let slice = obj.as_bytes(); let mut left = slice.len(); let mut count = 0_usize; while left > 0 { let buf = &slice[count..]; - match self.send(buf.as_ptr(), left) { + match stream.write(buf) { Ok(n) => { left -= n; count += n; @@ -95,19 +77,20 @@ impl OhUiChannel { if std::io::Error::last_os_error().raw_os_error().unwrap() == libc::EAGAIN { continue; } - return Err(e); + bail!(e); } } } Ok(()) } - pub fn recv_slice(&self, data: &mut [u8]) -> Result { + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result { + let stream = self.get_stream()?; let len = data.len(); if len == 0 { return Ok(0); } - let ret = self.recv(data.as_mut_ptr(), len); + let ret = stream.read(data); match ret { Ok(n) => Ok(n), Err(e) => { @@ -116,24 +99,18 @@ impl OhUiChannel { .unwrap_or(libc::EIO) != libc::EAGAIN { - error!("recv_slice(): error occurred: {}", e); + bail!("recv_slice(): error occurred: {:?}", e); } Ok(0) } } } - pub fn recv(&self, data: *mut u8, len: usize) -> Result { - let mut iovs = Vec::with_capacity(1); - iovs.push(iovec { - iov_base: data as *mut c_void, - iov_len: len, - }); - - let ret = self.sock.read().unwrap().recv_msg(&mut iovs, &mut []); - match ret { - Ok((n, _)) => Ok(n), - Err(e) => Err(e.into()), + fn get_stream(&mut self) -> Result<&mut SocketStream> { + if self.stream.is_some() { + Ok(self.stream.as_mut().unwrap()) + } else { + bail!("No connection established") } } } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 53cbbfbd2..e53a2fcf7 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -93,7 +93,7 @@ pub struct OhUiServer { // guest surface for framebuffer surface: RwLock, // transfer channel via unix sock - channel: Arc, + channel: Arc>, // message handler msg_handler: Arc, // connected or not @@ -111,13 +111,13 @@ pub struct OhUiServer { } impl OhUiServer { - fn init_channel(path: &String) -> Result> { + fn init_channel(path: &String) -> Result>> { let file_path = Path::new(path.as_str()).join("ohui.sock"); let sock_file = file_path .to_str() .ok_or_else(|| anyhow!("init_channel: Failed to get str from {}", path))?; TempCleaner::add_path(sock_file.to_string()); - Ok(Arc::new(OhUiChannel::new(sock_file))) + Ok(Arc::new(Mutex::new(OhUiChannel::new(sock_file)?))) } fn init_fb_file(path: &String) -> Result<(Option, u64)> { @@ -198,8 +198,8 @@ impl OhUiServer { } #[inline(always)] - fn get_channel(&self) -> &OhUiChannel { - self.channel.as_ref() + fn get_channel(&self) -> Arc> { + self.channel.clone() } #[inline(always)] @@ -404,7 +404,12 @@ impl OhUiTrans { } fn get_fd(&self) -> RawFd { - self.server.get_channel().get_stream_raw_fd() + self.server + .get_channel() + .lock() + .unwrap() + .get_stream_raw_fd() + .unwrap() } } @@ -449,8 +454,6 @@ impl OhUiListener { } fn handle_connection(&self) -> Result<()> { - // Set stream sock with nonblocking - self.server.get_channel().set_nonblocking(true)?; // Register OhUiTrans read notifier ohui_register_event(OhUiTrans::new(self.server.clone()), self.server.clone())?; self.server.set_connect(true); @@ -460,11 +463,15 @@ impl OhUiListener { } fn accept(&self) -> Result<()> { - self.server.get_channel().accept() + self.server.get_channel().lock().unwrap().accept() } fn get_fd(&self) -> RawFd { - self.server.get_channel().get_listener_raw_fd() + self.server + .get_channel() + .lock() + .unwrap() + .get_listener_raw_fd() } } @@ -511,10 +518,6 @@ fn ohui_register_event(e: T, srv: Arc) -> Re } fn ohui_start_listener(server: Arc) -> Result<()> { - // Bind and set listener nonblocking - let channel = server.get_channel(); - channel.bind()?; - channel.set_listener_nonblocking(true)?; ohui_register_event(OhUiListener::new(server.clone()), server.clone())?; info!("Successfully start listener."); Ok(()) diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 6b71cf9fe..7a8f894b0 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -124,30 +124,25 @@ pub struct OhUiMsgHandler { state: Mutex, hmcode2svcode: HashMap, reader: Mutex, - writer: Mutex, + writer: MsgWriter, } impl SyncLedstate for OhUiMsgHandler { fn sync_to_host(&self, state: u8) { let body = LedstateEvent::new(state as u32); - if let Err(e) = self - .writer - .lock() - .unwrap() - .send_message(EventType::Ledstate, &body) - { + if let Err(e) = self.writer.send_message(EventType::Ledstate, &body) { error!("sync_to_host: failed to send message with error {e}"); } } } impl OhUiMsgHandler { - pub fn new(channel: Arc) -> Self { + pub fn new(channel: Arc>) -> Self { OhUiMsgHandler { state: Mutex::new(WindowState::default()), hmcode2svcode: KeyCode::keysym_to_qkeycode(DpyMod::Ohui), reader: Mutex::new(MsgReader::new(channel.clone())), - writer: Mutex::new(MsgWriter::new(channel)), + writer: MsgWriter::new(channel), } } @@ -257,12 +252,7 @@ impl OhUiMsgHandler { size_per_pixel: u32, ) { let body = HWCursorEvent::new(w, h, hot_x, hot_y, size_per_pixel); - if let Err(e) = self - .writer - .lock() - .unwrap() - .send_message(EventType::CursorDefine, &body) - { + if let Err(e) = self.writer.send_message(EventType::CursorDefine, &body) { error!("handle_cursor_define: failed to send message with error {e}"); } } @@ -332,24 +322,14 @@ impl OhUiMsgHandler { pub fn send_windowinfo(&self, w: u32, h: u32) { self.state.lock().unwrap().update_window_info(w, h); let body = WindowInfoEvent::new(w, h); - if let Err(e) = self - .writer - .lock() - .unwrap() - .send_message(EventType::WindowInfo, &body) - { + if let Err(e) = self.writer.send_message(EventType::WindowInfo, &body) { error!("send_windowinfo: failed to send message with error {e}"); } } pub fn handle_dirty_area(&self, x: u32, y: u32, w: u32, h: u32) { let body = FrameBufferDirtyEvent::new(x, y, w, h); - if let Err(e) = self - .writer - .lock() - .unwrap() - .send_message(EventType::FrameBufferDirty, &body) - { + if let Err(e) = self.writer.send_message(EventType::FrameBufferDirty, &body) { error!("handle_dirty_area: failed to send message with error {e}"); } } @@ -357,7 +337,7 @@ impl OhUiMsgHandler { struct MsgReader { /// socket to read - channel: Arc, + channel: Arc>, /// cache for header pub header: EventMsgHdr, /// received byte size of header @@ -369,7 +349,7 @@ struct MsgReader { } impl MsgReader { - pub fn new(channel: Arc) -> Self { + pub fn new(channel: Arc>) -> Self { MsgReader { channel, header: EventMsgHdr::default(), @@ -398,7 +378,11 @@ impl MsgReader { } let buf = self.header.as_mut_bytes(); - self.header_ready += self.channel.recv_slice(&mut buf[self.header_ready..])?; + self.header_ready += self + .channel + .lock() + .unwrap() + .recv_slice(&mut buf[self.header_ready..])?; Ok(self.header_ready == EVENT_MSG_HDR_SIZE as usize) } @@ -420,22 +404,27 @@ impl MsgReader { unsafe { buf.set_len(body_size); } - self.body_ready += self.channel.recv_slice(&mut buf[self.body_ready..])?; + self.body_ready += self + .channel + .lock() + .unwrap() + .recv_slice(&mut buf[self.body_ready..])?; Ok(self.body_ready == body_size) } } -struct MsgWriter(Arc); +struct MsgWriter(Arc>); impl MsgWriter { - fn new(channel: Arc) -> Self { + fn new(channel: Arc>) -> Self { MsgWriter(channel) } fn send_message(&self, t: EventType, body: &T) -> Result<()> { + let mut channel = self.0.lock().unwrap(); let hdr = EventMsgHdr::new(t); - self.0.send_by_obj(&hdr)?; - self.0.send_by_obj(body) + channel.send_by_obj(&hdr)?; + channel.send_by_obj(body) } } -- Gitee From b1653b78d7d43e7e95b37d0113683591ad9b8ebc Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 29 May 2024 19:57:11 +0800 Subject: [PATCH 1855/2187] ohui: disconnect if received data is wrong If received data is wrong, we should notify the client but for now we have no this kind of mechanism. So let's disconnect directly. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/channel.rs | 6 ++++++ ui/src/ohui_srv/mod.rs | 2 ++ ui/src/ohui_srv/msg_handle.rs | 9 ++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 561dd765e..008a7e26b 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -106,6 +106,12 @@ impl OhUiChannel { } } + pub fn disconnect(&mut self) { + if self.stream.is_some() { + self.stream = None; + } + } + fn get_stream(&mut self) -> Result<&mut SocketStream> { if self.stream.is_some() { Ok(self.stream.as_mut().unwrap()) diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index e53a2fcf7..603abb609 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -269,6 +269,8 @@ impl OhUiServer { register_led_sync(self.msg_handler.clone()); } else { unregister_led_sync(); + self.channel.lock().unwrap().disconnect(); + self.msg_handler.reset(); } } diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 7a8f894b0..5fb84f5ef 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -156,14 +156,13 @@ impl OhUiMsgHandler { let body_size = hdr.size as usize; let event_type = hdr.event_type; if body_size != event_msg_data_len(hdr.event_type) { - warn!( + reader.clear(); + bail!( "{:?} data len is wrong, we want {}, but receive {}", event_type, event_msg_data_len(hdr.event_type), body_size ); - reader.clear(); - return Ok(()); } trace::trace_scope_start!(handle_msg, args = (&event_type)); @@ -333,6 +332,10 @@ impl OhUiMsgHandler { error!("handle_dirty_area: failed to send message with error {e}"); } } + + pub fn reset(&self) { + self.reader.lock().unwrap().clear(); + } } struct MsgReader { -- Gitee From 94d0d4a18f5c7159452d865ebad8966d6e66fe4f Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 30 May 2024 11:44:13 +0800 Subject: [PATCH 1856/2187] ohaudio: add hitrace Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 2 +- devices/src/misc/scream/ohaudio.rs | 11 ++++++++++- trace/trace_info/misc.toml | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index ed0890111..496c96c24 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -186,7 +186,7 @@ impl ShmemStreamFmt { } /// Audio stream data structure. -#[derive(Default)] +#[derive(Debug, Default)] pub struct StreamData { pub fmt: ShmemStreamFmt, chunk_idx: u16, diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 10f9cb856..fc6fccf0e 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -137,6 +137,8 @@ impl OhAudioProcess for OhAudioRender { fence(Ordering::Acquire); + trace::trace_scope_start!(ohaudio_render_process, args = (recv_data)); + let su = StreamUnit { addr: recv_data.audio_base, len: recv_data.audio_size as u64, @@ -230,6 +232,9 @@ impl OhAudioProcess for OhAudioCapture { fn process(&mut self, recv_data: &StreamData) -> i32 { self.check_fmt_update(recv_data); + + trace::trace_scope_start!(ohaudio_capturer_process, args = (recv_data)); + if !self.start && !self.init(recv_data) { self.destroy(); return 0; @@ -255,8 +260,10 @@ extern "C" fn on_write_data_cb( .as_mut() .unwrap_unchecked() }; - let data_size = render.data_size.load(Ordering::Relaxed); + + trace::trace_scope_start!(ohaudio_write_cb, args = (length, data_size)); + if !render.flushing.load(Ordering::Acquire) && data_size < length { // SAFETY: we checked len. unsafe { ptr::write_bytes(buffer as *mut u8, 0, length as usize) }; @@ -313,6 +320,8 @@ extern "C" fn on_read_data_cb( .unwrap_unchecked() }; + trace::trace_scope_start!(ohaudio_read_cb, args = (length)); + loop { if !capture.start { return 0; diff --git a/trace/trace_info/misc.toml b/trace/trace_info/misc.toml index c05741938..897a6bc03 100644 --- a/trace/trace_info/misc.toml +++ b/trace/trace_info/misc.toml @@ -63,3 +63,27 @@ name = "oh_scream_on_read_data_cb" args = "len: usize" message = "len: {}" enabled = true + +[[scopes]] +name = "ohaudio_render_process" +args = "data: &dyn fmt::Debug" +message = "audio data {:?} to render" +enabled = true + +[[scopes]] +name = "ohaudio_capturer_process" +args = "data: &dyn fmt::Debug" +message = "audio data {:?} to capture" +enabled = true + +[[scopes]] +name = "ohaudio_write_cb" +args = "to_copy: i32, len: i32" +message = "OH audio expect audio data {} bytes, we have {} bytes" +enabled = true + +[[scopes]] +name = "ohaudio_read_cb" +args = "len: i32" +message = "OH audio captured {} bytes" +enabled = true -- Gitee From 1648cec951d30efe8f161120e800e9a955229384 Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Tue, 28 May 2024 17:29:31 +0800 Subject: [PATCH 1857/2187] eventfd: add cloexec flag when eventfd create Signed-off-by: Huxiaohang --- devices/src/acpi/ged.rs | 4 ++-- devices/src/legacy/pl011.rs | 5 ++--- devices/src/legacy/pl031.rs | 4 ++-- devices/src/legacy/rtc.rs | 4 ++-- devices/src/legacy/serial.rs | 5 ++--- devices/src/usb/camera.rs | 5 +++-- devices/src/usb/xhci/xhci_pci.rs | 5 +++-- machine/src/aarch64/standard.rs | 12 ++++++------ machine/src/standard_common/mod.rs | 6 ++++-- machine/src/x86_64/standard.rs | 15 +++++++++------ ui/src/vnc/client_io.rs | 8 ++++---- util/src/aio/mod.rs | 3 ++- util/src/leak_bucket.rs | 4 ++-- util/src/loop_context.rs | 9 +++++++-- vfio/src/vfio_pci.rs | 5 +++-- virtio/src/device/block.rs | 5 +++-- virtio/src/device/net.rs | 5 +++-- virtio/src/queue/mod.rs | 3 ++- virtio/src/transport/virtio_mmio.rs | 5 +++-- virtio/src/vhost/kernel/net.rs | 5 ++--- virtio/src/vhost/kernel/vsock.rs | 5 ++--- 21 files changed, 68 insertions(+), 54 deletions(-) diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 7a87180b1..300868eaf 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -35,7 +35,7 @@ use address_space::GuestAddress; use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_channel::QmpChannel; -use util::loop_context::{read_fd, EventNotifier, NotifierOperation}; +use util::loop_context::{create_new_eventfd, read_fd, EventNotifier, NotifierOperation}; use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; #[derive(Clone, Copy)] @@ -96,7 +96,7 @@ impl Ged { region_base: u64, region_size: u64, ) -> Result>> { - self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); + self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| AcpiError::Alignment(region_size as u32))?; self.battery_present = battery_present; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index afe1e540e..fe76da2de 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -14,7 +14,6 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use log::{debug, error}; -use vmm_sys_util::eventfd::EventFd; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; @@ -36,7 +35,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; +use util::loop_context::{create_new_eventfd, EventNotifierHelper}; use util::num_ops::read_data_u32; const PL011_FLAG_TXFE: u8 = 0x80; @@ -135,7 +134,7 @@ impl PL011 { Ok(PL011 { base: SysBusDevBase { dev_type: SysBusDevType::PL011, - interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), + interrupt_evt: Some(Arc::new(create_new_eventfd()?)), ..Default::default() }, paused: false, diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index a5790dd6d..dc5b94e09 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -15,7 +15,6 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use anyhow::{Context, Result}; use byteorder::{ByteOrder, LittleEndian}; -use vmm_sys_util::eventfd::EventFd; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; @@ -28,6 +27,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::loop_context::create_new_eventfd; use util::num_ops::write_data_u32; /// Registers for pl031 from ARM PrimeCell Real Time Clock Technical Reference Manual. @@ -100,7 +100,7 @@ impl PL031 { region_base: u64, region_size: u64, ) -> Result<()> { - self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); + self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| LegacyError::SetSysResErr)?; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 8334c2d19..ccb907cbf 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -15,7 +15,6 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use anyhow::Result; use log::{debug, error, warn}; -use vmm_sys_util::eventfd::EventFd; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; @@ -24,6 +23,7 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; +use util::loop_context::create_new_eventfd; use util::time::{mktime64, NANOSECONDS_PER_SECOND}; /// IO port of RTC device to select Register to read/write. @@ -126,7 +126,7 @@ impl RTC { region_size: 8, irq: -1, }, - interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), + interrupt_evt: Some(Arc::new(create_new_eventfd()?)), ..Default::default() }, cmos_data: [0_u8; 128], diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 8b096f099..80415e075 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -15,7 +15,6 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use log::{debug, error}; -use vmm_sys_util::eventfd::EventFd; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; @@ -34,7 +33,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; +use util::loop_context::{create_new_eventfd, EventNotifierHelper}; pub const SERIAL_ADDR: u64 = 0x3f8; @@ -144,7 +143,7 @@ impl Serial { .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; - self.base.interrupt_evt = Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)); + self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| LegacyError::SetSysResErr)?; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index f687d455c..c4e5e7796 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -41,7 +41,8 @@ use machine_manager::event_loop::{register_event_helper, unregister_event_helper use util::aio::{iov_discard_front_direct, Iovec}; use util::byte_code::ByteCode; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; const INTERFACE_ID_CONTROL: u8 = 0; @@ -505,7 +506,7 @@ impl UsbCamera { Ok(Self { base: UsbDeviceBase::new(config.id, USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), - camera_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + camera_fd: Arc::new(create_new_eventfd()?), camera_backend: camera, packet_list: Arc::new(Mutex::new(LinkedList::new())), payload: Arc::new(Mutex::new(UvcPayload::new())), diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 09c24f46f..0e8ffd232 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -39,7 +39,8 @@ use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::{get_pci_df, valid_id}; use machine_manager::event_loop::register_event_helper; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; /// 5.2 PCI Configuration Registers(USB) @@ -122,7 +123,7 @@ impl XhciPciDevice { XHCI_PCI_CONFIG_LENGTH as u64, "XhciPciContainer", ), - doorbell_fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + doorbell_fd: Arc::new(create_new_eventfd().unwrap()), delete_evts: Vec::new(), iothread: config.iothread.clone(), } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index bb46ce382..b803e7419 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -81,7 +81,7 @@ use ui::ohui_srv::{ohui_init, OhUiServer}; use ui::vnc::vnc_init; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; -use util::loop_context::EventLoopManager; +use util::loop_context::{create_new_eventfd, EventLoopManager}; use util::seccomp::{BpfRule, SeccompCmpOpt}; use util::set_termi_canon_mode; @@ -186,23 +186,23 @@ impl StdMachine { IRQ_MAP[IrqEntryType::Pcie as usize].0, ))), power_button: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("power_button".to_string()))?, ), shutdown_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("shutdown_req".to_string()))?, ), reset_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("reset_req".to_string()))?, ), pause_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("pause_req".to_string()))?, ), resume_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("resume_req".to_string()))?, ), dtb_vec: Vec::new(), diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index bac724a89..02780b8ee 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -79,7 +79,9 @@ use ui::input::{input_button, input_move_abs, input_point_sync, key_event, Axis} use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; use util::byte_code::ByteCode; -use util::loop_context::{read_fd, EventNotifier, NotifierCallback, NotifierOperation}; +use util::loop_context::{ + create_new_eventfd, read_fd, EventNotifier, NotifierCallback, NotifierOperation, +}; use virtio::{qmp_balloon, qmp_query_balloon}; const MAX_REGION_SIZE: u64 = 65536; @@ -1549,7 +1551,7 @@ impl DeviceInterface for StdMachine { region = Region::init_io_region(args.size, dummy_dev_ops, "UpdateRegionTest"); if args.ioeventfd.is_some() && args.ioeventfd.unwrap() { let ioeventfds = vec![RegionIoEventFd { - fd: Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()), + fd: Arc::new(create_new_eventfd().unwrap()), addr_range: AddressRange::from(( 0, args.ioeventfd_size.unwrap_or_default(), diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 289737e19..c6804809a 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -61,7 +61,10 @@ use ui::gtk::gtk_display_init; use ui::vnc::vnc_init; use util::seccomp::SeccompCmpOpt; use util::{ - byte_code::ByteCode, loop_context::EventLoopManager, seccomp::BpfRule, set_termi_canon_mode, + byte_code::ByteCode, + loop_context::{create_new_eventfd, EventLoopManager}, + seccomp::BpfRule, + set_termi_canon_mode, }; pub(crate) const VENDOR_ID_INTEL: u16 = 0x8086; @@ -156,20 +159,20 @@ impl StdMachine { IRQ_MAP[IrqEntryType::Pcie as usize].0, ))), reset_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("reset request".to_string()))?, ), shutdown_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK).with_context(|| { + create_new_eventfd().with_context(|| { MachineError::InitEventFdErr("shutdown request".to_string()) })?, ), power_button: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("power button".to_string()))?, ), cpu_resize_req: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("cpu resize".to_string()))?, ), boot_order_list: Arc::new(Mutex::new(Vec::new())), @@ -264,7 +267,7 @@ impl StdMachine { let region_size: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].1; let cpu_config = CpuConfig::new(boot_config, cpu_topology); let hotplug_cpu_req = Arc::new( - EventFd::new(libc::EFD_NONBLOCK) + create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("hotplug cpu".to_string()))?, ); diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index 347e7d580..b4fa90556 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -47,8 +47,8 @@ use crate::{ use util::{ bitmap::Bitmap, loop_context::{ - gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, - NotifierOperation, + create_new_eventfd, gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, + NotifierCallback, NotifierOperation, }, }; @@ -392,8 +392,8 @@ impl ClientState { pub fn new(addr: String) -> Self { ClientState { addr, - disconn_evt: Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), - write_fd: Arc::new(Mutex::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), + disconn_evt: Arc::new(Mutex::new(create_new_eventfd().unwrap())), + write_fd: Arc::new(Mutex::new(create_new_eventfd().unwrap())), in_buffer: Arc::new(Mutex::new(BuffPool::new())), out_buffer: Arc::new(Mutex::new(BuffPool::new())), client_dpm: Arc::new(Mutex::new(DisplayMode::default())), diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 03183b800..a9427f67e 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -32,6 +32,7 @@ use uring::IoUringContext; use vmm_sys_util::eventfd::EventFd; use super::link_list::{List, Node}; +use crate::loop_context::create_new_eventfd; use crate::num_ops::{round_down, round_up}; use crate::thread_pool::ThreadPool; use crate::unix::host_page_size; @@ -504,7 +505,7 @@ impl Aio { thread_pool: Option>, ) -> Result { let max_events: usize = 128; - let fd = EventFd::new(libc::EFD_NONBLOCK)?; + let fd = create_new_eventfd()?; let ctx: Option>> = if let Some(pool) = thread_pool { let threads_aio_ctx = ThreadsAioContext::new(max_events as u32, &fd, pool); match engine { diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 5dd65f2f0..cde308c4b 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -20,7 +20,7 @@ use log::error; use vmm_sys_util::eventfd::EventFd; use crate::clock::get_current_time; -use crate::loop_context::EventLoopContext; +use crate::loop_context::{create_new_eventfd, EventLoopContext}; use crate::time::NANOSECONDS_PER_SECOND; /// Used to improve the accuracy of bucket level. @@ -53,7 +53,7 @@ impl LeakBucket { level: 0, prev_time: get_current_time(), timer_started: false, - timer_wakeup: Arc::new(EventFd::new(libc::EFD_NONBLOCK)?), + timer_wakeup: Arc::new(create_new_eventfd()?), }) } diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ded2f1fec..957fdb4cb 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -13,6 +13,7 @@ use std::collections::BTreeMap; use std::fmt; use std::fmt::Debug; +use std::io::Error; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -20,7 +21,7 @@ use std::sync::{Arc, Barrier, Mutex, RwLock}; use std::time::{Duration, Instant}; use anyhow::{anyhow, Context, Result}; -use libc::{c_void, read, EFD_NONBLOCK}; +use libc::{c_void, read, EFD_CLOEXEC, EFD_NONBLOCK}; use log::{error, warn}; use nix::errno::Errno; use nix::{ @@ -158,6 +159,10 @@ pub fn gen_delete_notifiers(fds: &[RawFd]) -> Vec { notifiers } +pub fn create_new_eventfd() -> Result { + EventFd::new(EFD_NONBLOCK | EFD_CLOEXEC) +} + /// EventLoop manager, advise continue running or stop running pub trait EventLoopManager: Send + Sync { fn loop_should_exit(&self) -> bool; @@ -241,7 +246,7 @@ impl EventLoopContext { let mut ctx = EventLoopContext { epoll: Epoll::new().unwrap(), manager: None, - kick_event: EventFd::new(EFD_NONBLOCK).unwrap(), + kick_event: create_new_eventfd().unwrap(), kick_me: AtomicBool::new(false), kicked: AtomicBool::new(false), events: Arc::new(RwLock::new(BTreeMap::new())), diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index baca7bfeb..ebf028e59 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -45,6 +45,7 @@ use devices::pci::{ }; use devices::{pci::MsiVector, Device, DeviceBase}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; +use util::loop_context::create_new_eventfd; use util::num_ops::ranges_overlap; use util::unix::host_page_size; @@ -534,7 +535,7 @@ impl VfioPciDevice { let mut locked_gsi_routes = cloned_gsi_routes.lock().unwrap(); let gsi_route = locked_gsi_routes.get_mut(vector as usize).unwrap(); if gsi_route.irq_fd.is_none() { - let irq_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let irq_fd = create_new_eventfd().unwrap(); gsi_route.irq_fd = Some(Arc::new(irq_fd)); } let irq_fd = gsi_route.irq_fd.clone(); @@ -722,7 +723,7 @@ impl VfioPciDevice { fn vfio_enable_msix(&mut self) -> Result<()> { let mut gsi_routes = self.gsi_msi_routes.lock().unwrap(); if gsi_routes.len() == 0 { - let irq_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let irq_fd = create_new_eventfd().unwrap(); let gsi_route = GsiMsiRoute { irq_fd: Some(Arc::new(irq_fd)), gsi: -1, diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index f8cd6d657..9b14a8800 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -58,7 +58,8 @@ use util::aio::{ use util::byte_code::ByteCode; use util::leak_bucket::LeakBucket; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; use util::offset_of; @@ -1249,7 +1250,7 @@ impl VirtioDevice for Block { continue; } let (sender, receiver) = channel(); - let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); + let update_evt = Arc::new(create_new_eventfd()?); let driver_features = self.base.driver_features; let handler = BlockIoHandler { queue: queue.clone(), diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 198f4b7f3..391a6cd0f 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -57,7 +57,8 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::gen_delete_notifiers; use util::loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, + create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; use util::num_ops::str_to_num; use util::tap::{ @@ -1644,7 +1645,7 @@ impl VirtioDevice for Net { .with_context(|| "Failed to set tap offload")?; } - let update_evt = Arc::new(EventFd::new(libc::EFD_NONBLOCK)?); + let update_evt = Arc::new(create_new_eventfd()?); let mut handler = NetIoHandler { rx: RxVirtio::new(rx_queue, rx_queue_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index 7f581a044..1e612b298 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -21,6 +21,7 @@ use vmm_sys_util::eventfd::EventFd; use address_space::{AddressSpace, GuestAddress, RegionCache}; use machine_manager::config::DEFAULT_VIRTQUEUE_SIZE; +use util::loop_context::create_new_eventfd; /// Split Virtqueue. pub const QUEUE_TYPE_SPLIT_VRING: u16 = 1; @@ -226,7 +227,7 @@ impl NotifyEventFds { pub fn new(queue_num: usize) -> Self { let mut events = Vec::new(); for _i in 0..queue_num { - events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); + events.push(Arc::new(create_new_eventfd().unwrap())); } NotifyEventFds { events } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 1340fd9cf..eb09093fe 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -33,6 +33,7 @@ use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::loop_context::create_new_eventfd; /// Registers of virtio-mmio device refer to Virtio Spec. /// Magic value - Read Only. @@ -105,7 +106,7 @@ impl HostNotifyInfo { fn new(queue_num: usize) -> Self { let mut events = Vec::new(); for _i in 0..queue_num { - events.push(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())); + events.push(Arc::new(create_new_eventfd().unwrap())); } HostNotifyInfo { events } @@ -149,7 +150,7 @@ impl VirtioMmioDevice { hotpluggable: false, }, dev_type: SysBusDevType::VirtioMmio, - interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap())), + interrupt_evt: Some(Arc::new(create_new_eventfd().unwrap())), ..Default::default() }, device, diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 8a4f6c020..89a299707 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -34,7 +34,7 @@ use address_space::AddressSpace; use machine_manager::config::{NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; +use util::loop_context::{create_new_eventfd, EventNotifierHelper}; use util::tap::Tap; /// Number of virtqueues. @@ -327,8 +327,7 @@ impl VirtioDevice for Net { let event = if self.call_events.is_empty() { let host_notify = VhostNotify { notify_evt: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| VirtioError::EventFdCreate)?, + create_new_eventfd().with_context(|| VirtioError::EventFdCreate)?, ), queue: queue_mutex.clone(), }; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 89bcd112f..ade3ec2ff 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -32,7 +32,7 @@ use machine_manager::event_loop::{register_event_helper, unregister_event_helper use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::loop_context::EventNotifierHelper; +use util::loop_context::{create_new_eventfd, EventNotifierHelper}; /// Number of virtqueues. const QUEUE_NUM_VSOCK: usize = 3; @@ -311,8 +311,7 @@ impl VirtioDevice for Vsock { let event = if self.call_events.is_empty() { let host_notify = VhostNotify { notify_evt: Arc::new( - EventFd::new(libc::EFD_NONBLOCK) - .with_context(|| VirtioError::EventFdCreate)?, + create_new_eventfd().with_context(|| VirtioError::EventFdCreate)?, ), queue: queue_mutex.clone(), }; -- Gitee From fe52a2dbfd5887016344f4043e198bf56d67191f Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Thu, 30 May 2024 14:28:53 +0800 Subject: [PATCH 1858/2187] VIOGPU: another way to set OS stage OS stage was set when we report EDID info. But sometimes EDID is disabled, for which OS stage will not be set. Let's fix this logic. Signed-off-by: zhanghan64 --- virtio/src/device/gpu.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 98f9d2c31..2d138ed24 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1226,6 +1226,12 @@ impl GpuIoHandler { scanout.width = info_set_scanout.rect.width; scanout.height = info_set_scanout.rect.height; + if (self.driver_features & (1 << VIRTIO_GPU_F_EDID)) == 0 + && (info_set_scanout.resource_id & VIRTIO_GPU_RES_WIN_FRAMEBUF) != 0 + { + self.change_run_stage()?; + } + self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) } -- Gitee From 7143c03e6fd20dd45c109d4935a74edc6c0be3be Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 30 May 2024 20:17:54 +0800 Subject: [PATCH 1859/2187] ohui: fixup compile error for OHOS target Fixup compiple error if target env is OHOS. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/channel.rs | 2 +- ui/src/ohui_srv/msg_handle.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 008a7e26b..93f8625ec 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -33,7 +33,7 @@ impl OhUiChannel { Ok(l) => l, Err(e) => bail!("Failed to create listener with path {}, {:?}", path, e), }; - limit_permission(path.as_str()).unwrap_or_else(|e| { + limit_permission(path).unwrap_or_else(|e| { error!( "Failed to limit permission for ohui-sock {}, err: {:?}", path, e diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 5fb84f5ef..cc0d679e7 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -14,7 +14,7 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex, RwLock}; use anyhow::{anyhow, bail, Result}; -use log::{error, warn}; +use log::error; use util::byte_code::ByteCode; use super::{channel::OhUiChannel, msg::*}; @@ -155,12 +155,13 @@ impl OhUiMsgHandler { let hdr = &reader.header; let body_size = hdr.size as usize; let event_type = hdr.event_type; - if body_size != event_msg_data_len(hdr.event_type) { + let expect_body_size = event_msg_data_len(hdr.event_type); + if body_size != expect_body_size { reader.clear(); bail!( "{:?} data len is wrong, we want {}, but receive {}", event_type, - event_msg_data_len(hdr.event_type), + expect_body_size, body_size ); } -- Gitee From 9ef8832d47970c6146f4ae6e303d0a91c6809f4b Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 30 May 2024 20:41:24 +0800 Subject: [PATCH 1860/2187] ohui: fixup clippy error Fixup clippy error. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/channel.rs | 2 +- ui/src/ohui_srv/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 93f8625ec..cfa86d47a 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -52,7 +52,7 @@ impl OhUiChannel { } pub fn get_stream_raw_fd(&self) -> Option { - self.stream.as_ref().and_then(|s| Some(s.as_raw_fd())) + self.stream.as_ref().map(|s| s.as_raw_fd()) } pub fn accept(&mut self) -> Result<()> { diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 603abb609..8f72b132c 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -520,7 +520,7 @@ fn ohui_register_event(e: T, srv: Arc) -> Re } fn ohui_start_listener(server: Arc) -> Result<()> { - ohui_register_event(OhUiListener::new(server.clone()), server.clone())?; + ohui_register_event(OhUiListener::new(server.clone()), server)?; info!("Successfully start listener."); Ok(()) } -- Gitee From 9dfaed1528d8a7aa0a1d1715accc67f208489162 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 31 May 2024 09:29:07 +0800 Subject: [PATCH 1861/2187] Cargo: update Cargo.lock file Signed-off-by: Xiao Ye --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index dfcfbe290..fb50c772f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,7 @@ dependencies = [ "nix 0.26.2", "once_cell", "thiserror", + "trace", "util", "vmm-sys-util", ] -- Gitee From c7db9849338c1ff3d6305ef6d16ee4687abc6bf9 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 31 May 2024 09:38:04 +0800 Subject: [PATCH 1862/2187] log: modify some log Signed-off-by: Xiao Ye --- machine/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f58634cf0..bc663ff87 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -2316,7 +2316,7 @@ fn check_windows_emu_pid( ) { let mut check_delay = Duration::from_millis(4000); if !Path::new(&pid_path).exists() { - log::info!("Detect windows emu exited, let VM exits now"); + log::info!("Detect emulator exited, let VM exits now"); if get_run_stage() == VmRunningStage::Os { if let Err(e) = powerdown_req.write(1) { log::error!("Failed to send powerdown request after emu exits: {:?}", e); -- Gitee From 2b8045dc9e390edd719854e72ec7ef1e6b5fc2e1 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 31 May 2024 09:57:09 +0800 Subject: [PATCH 1863/2187] virtio_pci: Do not send msix when vector is INVALID Signed-off-by: Keqian Zhu --- virtio/src/transport/virtio_pci.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 041356f9a..8ca540156 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -390,7 +390,9 @@ impl VirtioPciDevice { let mut locked_msix = cloned_msix.lock().unwrap(); if locked_msix.enabled { - locked_msix.notify(vector, dev_id.load(Ordering::Acquire)); + if vector != INVALID_VECTOR_NUM { + locked_msix.notify(vector, dev_id.load(Ordering::Acquire)); + } } else { cloned_intx.lock().unwrap().notify(1); } -- Gitee From c458256ddface7a63ef950639d575b600f2d9d06 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 31 May 2024 11:38:48 +0800 Subject: [PATCH 1864/2187] usbCamera: clean resource when camera is deleted When frontend is using camera which means register_camera_fd() has been called, if camera is to be deleted, we need to unregistered it. Without unregistering, camera_backend of UsbCamera wont be dropped. --- devices/src/usb/camera.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index c4e5e7796..710a71105 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -777,6 +777,7 @@ impl UsbDevice for UsbCamera { fn unrealize(&mut self) -> Result<()> { info!("Camera {} unrealize", self.device_id()); + self.unregister_camera_fd()?; self.camera_backend.lock().unwrap().reset(); Ok(()) } -- Gitee From 5a14fff8a57ea8c9491f462549e6504919536db4 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Thu, 30 May 2024 20:27:32 +0800 Subject: [PATCH 1865/2187] windows_emu_pid: fix checking logic 1s is too short for windows' power-down duration. We enlarge it to 30s. Signed-off-by: zhanghan64 --- machine/src/lib.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f58634cf0..09c619e8e 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -103,6 +103,10 @@ use virtio::{ #[cfg(feature = "virtio_gpu")] use virtio::{Gpu, GpuDevConfig}; +const WINDOWS_EMU_PID_DEFAULT_INTERVAL: u64 = 4000; +const WINDOWS_EMU_PID_SHUTDOWN_INTERVAL: u64 = 1000; +const WINDOWS_EMU_PID_POWERDOWN_INTERVAL: u64 = 30000; + /// Machine structure include base members. pub struct MachineBase { /// `vCPU` topology, support sockets, cores, threads. @@ -2314,18 +2318,22 @@ fn check_windows_emu_pid( powerdown_req: Arc, shutdown_req: Arc, ) { - let mut check_delay = Duration::from_millis(4000); + let mut check_delay = Duration::from_millis(WINDOWS_EMU_PID_DEFAULT_INTERVAL); if !Path::new(&pid_path).exists() { log::info!("Detect windows emu exited, let VM exits now"); if get_run_stage() == VmRunningStage::Os { + // Wait 30s for windows normal exit. + check_delay = Duration::from_millis(WINDOWS_EMU_PID_POWERDOWN_INTERVAL); if let Err(e) = powerdown_req.write(1) { log::error!("Failed to send powerdown request after emu exits: {:?}", e); } - } else if let Err(e) = shutdown_req.write(1) { - log::error!("Failed to send shutdown request after emu exits: {:?}", e); + } else { + // Wait 1s for windows shutdown. + check_delay = Duration::from_millis(WINDOWS_EMU_PID_SHUTDOWN_INTERVAL); + if let Err(e) = shutdown_req.write(1) { + log::error!("Failed to send shutdown request after emu exits: {:?}", e); + } } - // Continue checking to prevent exit failed. - check_delay = Duration::from_millis(1000); } let check_emu_alive = Box::new(move || { -- Gitee From 233b04c135b3ca3de96d3731acaa42fdfabddbd1 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 3 Jun 2024 15:47:08 +0800 Subject: [PATCH 1866/2187] trace: export trace scope and add Clone trait Let's export trace scope and add Clone trait for later use. Signed-off-by: Zhao Yi Min --- trace/src/lib.rs | 2 +- trace/src/trace_scope.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/trace/src/lib.rs b/trace/src/lib.rs index 1501a5028..32646d2f3 100644 --- a/trace/src/lib.rs +++ b/trace/src/lib.rs @@ -19,7 +19,7 @@ pub(crate) mod hitrace; feature = "trace_to_ftrace", all(target_env = "ohos", feature = "trace_to_hitrace") ))] -pub(crate) mod trace_scope; +pub mod trace_scope; use std::{ fmt, diff --git a/trace/src/trace_scope.rs b/trace/src/trace_scope.rs index 4a0210170..a860ef8d7 100644 --- a/trace/src/trace_scope.rs +++ b/trace/src/trace_scope.rs @@ -20,12 +20,14 @@ use crate::ftrace::write_trace_marker; static mut TRACE_SCOPE_COUNTER: AtomicI32 = AtomicI32::new(i32::MIN); +#[derive(Clone)] pub enum Scope { Common(TraceScope), Asyn(TraceScopeAsyn), None, } +#[derive(Clone)] pub struct TraceScope {} impl TraceScope { @@ -63,6 +65,7 @@ impl Drop for TraceScope { } } +#[derive(Clone)] pub struct TraceScopeAsyn { value: String, id: i32, -- Gitee From 6d9896e9f077f6ed740fc9d3e555c0703d6261ef Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 31 May 2024 18:01:24 +0800 Subject: [PATCH 1867/2187] usb-host: add dlopen support Add dlopen support for OH USB subsystem Signed-off-by: Fan Xuan Zhe --- util/Cargo.toml | 1 + util/src/ohos_binding/hwf_adapter/mod.rs | 23 +++++++++++ util/src/ohos_binding/hwf_adapter/usb.rs | 45 +++++++++++++++++++++ util/src/ohos_binding/mod.rs | 2 + util/src/ohos_binding/usb.rs | 50 ++++++++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 util/src/ohos_binding/hwf_adapter/usb.rs create mode 100644 util/src/ohos_binding/usb.rs diff --git a/util/Cargo.toml b/util/Cargo.toml index 95aefcdac..8d8a38ec9 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -28,5 +28,6 @@ trace = {path = "../trace"} default = [] usb_camera_v4l2 = ["dep:v4l2-sys-mit"] usb_camera_oh = [] +usb_host = [] scream_ohaudio = [] pixman = [] diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs index ffa11457f..2709c81f3 100644 --- a/util/src/ohos_binding/hwf_adapter/mod.rs +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -12,6 +12,8 @@ #[cfg(feature = "usb_camera_oh")] pub mod camera; +#[cfg(feature = "usb_host")] +pub mod usb; use std::ffi::OsStr; use std::sync::Arc; @@ -23,6 +25,8 @@ use once_cell::sync::Lazy; #[cfg(feature = "usb_camera_oh")] use camera::CamFuncTable; +#[cfg(feature = "usb_host")] +use usb::UsbFuncTable; static LIB_HWF_ADAPTER: Lazy = Lazy::new(|| // SAFETY: The dynamic library should be always existing. @@ -40,6 +44,8 @@ struct LibHwfAdapter { library: Library, #[cfg(feature = "usb_camera_oh")] camera: Arc, + #[cfg(feature = "usb_host")] + usb: Arc, } impl LibHwfAdapter { @@ -52,10 +58,17 @@ impl LibHwfAdapter { CamFuncTable::new(&library).with_context(|| "failed to init camera function table")?, ); + #[cfg(feature = "usb_host")] + let usb = Arc::new( + UsbFuncTable::new(&library).with_context(|| "failed to init usb function table")?, + ); + Ok(Self { library, #[cfg(feature = "usb_camera_oh")] camera, + #[cfg(feature = "usb_host")] + usb, }) } @@ -63,9 +76,19 @@ impl LibHwfAdapter { fn get_camera_api(&self) -> Arc { self.camera.clone() } + + #[cfg(feature = "usb_host")] + fn get_usb_api(&self) -> Arc { + self.usb.clone() + } } #[cfg(feature = "usb_camera_oh")] pub fn hwf_adapter_camera_api() -> Arc { LIB_HWF_ADAPTER.get_camera_api() } + +#[cfg(feature = "usb_host")] +pub fn hwf_adapter_usb_api() -> Arc { + LIB_HWF_ADAPTER.get_usb_api() +} diff --git a/util/src/ohos_binding/hwf_adapter/usb.rs b/util/src/ohos_binding/hwf_adapter/usb.rs new file mode 100644 index 000000000..abb3cc747 --- /dev/null +++ b/util/src/ohos_binding/hwf_adapter/usb.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::c_int; + +use anyhow::{Context, Result}; +use libloading::os::unix::Symbol as RawSymbol; +use libloading::Library; + +use crate::get_libfn; + +#[allow(non_snake_case)] +#[repr(C)] +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub struct OhusbDevice { + pub busNum: u8, + pub devAddr: u8, + pub fd: c_int, +} + +type OhusbOpenDeviceFn = unsafe extern "C" fn(*mut OhusbDevice) -> c_int; +type OhusbCloseDeviceFn = unsafe extern "C" fn(*mut OhusbDevice) -> c_int; + +pub struct UsbFuncTable { + pub open_device: RawSymbol, + pub close_device: RawSymbol, +} + +impl UsbFuncTable { + pub unsafe fn new(library: &Library) -> Result { + Ok(Self { + open_device: get_libfn!(library, OhusbOpenDeviceFn, OhusbOpenDevice), + close_device: get_libfn!(library, OhusbCloseDeviceFn, OhusbCloseDevice), + }) + } +} diff --git a/util/src/ohos_binding/mod.rs b/util/src/ohos_binding/mod.rs index 5f876ba54..2e6a3cfcc 100644 --- a/util/src/ohos_binding/mod.rs +++ b/util/src/ohos_binding/mod.rs @@ -15,6 +15,8 @@ pub mod audio; #[cfg(feature = "usb_camera_oh")] pub mod camera; pub mod misc; +#[cfg(feature = "usb_host")] +pub mod usb; #[cfg(feature = "usb_camera_oh")] mod hwf_adapter; diff --git a/util/src/ohos_binding/usb.rs b/util/src/ohos_binding/usb.rs new file mode 100644 index 000000000..a8d227bd3 --- /dev/null +++ b/util/src/ohos_binding/usb.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub use super::hwf_adapter::usb::OhusbDevice; + +use std::sync::Arc; + +use anyhow::{bail, Result}; + +use super::hwf_adapter::hwf_adapter_usb_api; +use super::hwf_adapter::usb::UsbFuncTable; + +#[derive(Clone)] +pub struct OhUsb { + capi: Arc, +} + +impl OhUsb { + pub fn new() -> Result { + let capi = hwf_adapter_usb_api(); + Ok(Self { capi }) + } + + pub fn open_device(&self, dev_handle: *mut OhusbDevice) -> Result { + // SAFETY: We call related API sequentially for specified ctx. + let ret = unsafe { (self.capi.open_device)(dev_handle) }; + if ret < 0 { + bail!("OH USB: open device failed."); + } + Ok(ret) + } + + pub fn close_device(&self, dev_handle: *mut OhusbDevice) -> Result { + // SAFETY: We call related API sequentially for specified ctx. + let ret = unsafe { (self.capi.close_device)(dev_handle) }; + if ret < 0 { + bail!("OH USB: close device failed."); + } + Ok(ret) + } +} -- Gitee From f631031020039aefc7f55e42f560ff18d6e10063 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 3 Jun 2024 15:54:50 +0800 Subject: [PATCH 1868/2187] ohcam: introduce scope traces This patch introduces two scope traces for ohcam: 1) function scope trace for get_frame(); 2) async scope trace for next_frame(); Signed-off-by: Zhao Yi Min --- devices/src/camera_backend/ohcam.rs | 65 +++++++++++++++++++++++++++++ trace/trace_info/camera.toml | 12 ++++++ 2 files changed, 77 insertions(+) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 3debe893c..55b6a80ae 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -20,6 +20,12 @@ use crate::camera_backend::{ CamBasicFmt, CameraBackend, CameraBrokenCallback, CameraFormatList, CameraFrame, CameraNotifyCallback, FmtType, }; +#[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") +))] +use trace::trace_scope::Scope; use util::aio::Iovec; use util::ohos_binding::camera::*; @@ -79,6 +85,33 @@ impl OhCamCallBack { } } +#[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") +))] +#[derive(Clone, Default)] +struct OhCameraAsyncScope { + next_frame_id: u64, + async_scope: Option, +} + +#[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") +))] +impl OhCameraAsyncScope { + fn start(&mut self) { + self.async_scope = Some(trace::ohcam_next_frame(true, self.next_frame_id)); + self.next_frame_id += 1; + } + + fn stop(&mut self) { + self.async_scope = None; + } +} + #[derive(Clone)] pub struct OhCameraBackend { id: String, @@ -87,6 +120,12 @@ pub struct OhCameraBackend { ctx: OhCamera, fmt_list: Vec, selected_profile: u8, + #[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + ))] + async_scope: Box, } // SAFETY: Send and Sync is not auto-implemented for raw pointer type. @@ -121,6 +160,12 @@ impl OhCameraBackend { ctx, fmt_list: vec![], selected_profile: 0, + #[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + ))] + async_scope: Box::new(OhCameraAsyncScope::default()), }) } } @@ -163,6 +208,12 @@ impl CameraBackend for OhCameraBackend { fn video_stream_off(&mut self) -> Result<()> { self.ctx.stop_stream(); OHCAM_CALLBACK.write().unwrap().clear_buffer(); + #[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + ))] + self.async_scope.stop(); Ok(()) } @@ -201,6 +252,12 @@ impl CameraBackend for OhCameraBackend { fn reset(&mut self) { OHCAM_CALLBACK.write().unwrap().clear_buffer(); self.ctx.reset_camera(); + #[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + ))] + self.async_scope.stop(); } fn get_format_by_index(&self, format_index: u8, frame_index: u8) -> Result { @@ -240,6 +297,12 @@ impl CameraBackend for OhCameraBackend { } fn next_frame(&mut self) -> Result<()> { + #[cfg(any( + feature = "trace_to_logger", + feature = "trace_to_ftrace", + all(target_env = "ohos", feature = "trace_to_hitrace") + ))] + self.async_scope.start(); self.ctx.next_frame(); OHCAM_CALLBACK.write().unwrap().clear_buffer(); Ok(()) @@ -255,6 +318,8 @@ impl CameraBackend for OhCameraBackend { bail!("Invalid frame offset {} or len {}", frame_offset, len); } + trace::trace_scope_start!(ohcam_get_frame, args = (frame_offset, len)); + let mut copied = 0; for iov in iovecs { if len == copied { diff --git a/trace/trace_info/camera.toml b/trace/trace_info/camera.toml index 4e0ee65de..e1f044ffb 100644 --- a/trace/trace_info/camera.toml +++ b/trace/trace_info/camera.toml @@ -21,3 +21,15 @@ name = "camera_get_format_by_index" args = "format_index: u8, frame_index: u8, out: &dyn fmt::Debug" message = "V4l2 fmt {}, frm {}, info {:?}." enabled = true + +[[scopes]] +name = "ohcam_get_frame" +args = "offset: usize, len: usize" +message = "ohcam get frame offset {} len {}" +enabled = true + +[[scopes]] +name = "ohcam_next_frame" +args = "frame_id: u64" +message = "ohcam next frame {}" +enabled = true -- Gitee From 8e62cf7929b74616ef41db56ec882fd838644756 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 31 May 2024 18:03:47 +0800 Subject: [PATCH 1869/2187] usb-host: usb subsystem support for permission issues Add USB subsystem calls in the usb-host module to solve permission issues in the current libusb-based USB device pass-through solution. Signed-off-by: Fan Xuan Zhe --- build.rs | 1 + devices/Cargo.toml | 2 +- devices/src/usb/usbhost/host_usblib.rs | 19 ++++++ devices/src/usb/usbhost/mod.rs | 50 ++++++++++++---- devices/src/usb/usbhost/ohusb.rs | 83 ++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 devices/src/usb/usbhost/ohusb.rs diff --git a/build.rs b/build.rs index 96f77ffd4..53d0c9b25 100644 --- a/build.rs +++ b/build.rs @@ -16,6 +16,7 @@ fn ohos_env_configure() { println!("cargo:rustc-link-arg=--verbose"); println!("cargo:rustc-link-arg=--sysroot={}/sysroot", ohos_sdk_path); println!("cargo:rustc-link-arg=-lpixman_static"); + println!("cargo:rustc-link-arg=-lusb-1.0"); println!( "cargo:rustc-link-search={}/sysroot/usr/lib/aarch64-linux-ohos", ohos_sdk_path diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 5d54eabf6..00181b49c 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -47,7 +47,7 @@ scream_pulseaudio = ["scream", "dep:pulse", "dep:psimple", "machine_manager/scre scream_ohaudio = ["scream", "machine_manager/scream_ohaudio", "util/scream_ohaudio"] pvpanic = ["machine_manager/pvpanic"] demo_device = ["machine_manager/demo_device", "ui/console", "util/pixman"] -usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host"] +usb_host = ["dep:libusb1-sys", "dep:rusb", "machine_manager/usb_host", "util/usb_host"] usb_camera = ["machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh", "util/usb_camera_oh"] diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 2ee7a9dea..e558f8144 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -16,6 +16,8 @@ use std::{ }; use libc::{c_int, c_uint, c_void, EPOLLIN, EPOLLOUT}; +#[cfg(all(target_arch = "aarch64", target_env = "ohos"))] +use libusb1_sys::{constants::LIBUSB_SUCCESS, libusb_context, libusb_set_option}; use libusb1_sys::{ constants::{ LIBUSB_ERROR_ACCESS, LIBUSB_ERROR_BUSY, LIBUSB_ERROR_INTERRUPTED, @@ -380,3 +382,20 @@ pub fn free_host_transfer(transfer: *mut libusb_transfer) { // SAFETY: have checked the validity of transfer before call libusb_free_transfer. unsafe { libusb1_sys::libusb_free_transfer(transfer) }; } + +#[cfg(all(target_arch = "aarch64", target_env = "ohos"))] +pub fn set_option(opt: u32) -> Result<()> { + // SAFETY: This function will only configure a specific option within libusb, null for ctx is valid. + let err = unsafe { + libusb_set_option( + std::ptr::null_mut() as *mut libusb_context, + opt, + std::ptr::null_mut() as *mut c_void, + ) + }; + if err != LIBUSB_SUCCESS { + return Err(from_libusb(err)); + } + + Ok(()) +} diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 40d7c8af9..bd87be431 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -11,6 +11,8 @@ // See the Mulan PSL v2 for more details. mod host_usblib; +#[cfg(all(target_arch = "aarch64", target_env = "ohos"))] +mod ohusb; use std::{ collections::LinkedList, @@ -20,7 +22,7 @@ use std::{ time::Duration, }; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, Context as anyhowContext, Result}; use clap::Parser; use libc::c_int; use libusb1_sys::{ @@ -50,6 +52,8 @@ use machine_manager::{ event_loop::{register_event_helper, unregister_event_helper}, temp_cleaner::{ExitNotifier, TempCleaner}, }; +#[cfg(all(target_arch = "aarch64", target_env = "ohos"))] +use ohusb::OhUsbDev; use util::{ byte_code::ByteCode, link_list::{List, Node}, @@ -404,6 +408,8 @@ pub struct UsbHost { iso_queues: Arc>>>>, iso_urb_frames: u32, iso_urb_count: u32, + #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] + oh_dev: OhUsbDev, } // SAFETY: Send and Sync is not auto-implemented for util::link_list::List. @@ -414,6 +420,9 @@ unsafe impl Send for UsbHost {} impl UsbHost { pub fn new(config: UsbHostConfig) -> Result { + #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] + let oh_dev = OhUsbDev::new()?; + let mut context = Context::new()?; context.set_log_level(rusb::LogLevel::None); let iso_urb_frames = config.iso_urb_frames; @@ -434,9 +443,12 @@ impl UsbHost { iso_queues: Arc::new(Mutex::new(LinkedList::new())), iso_urb_frames, iso_urb_count, + #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] + oh_dev, }) } + #[cfg(not(all(target_arch = "aarch64", target_env = "ohos")))] fn find_libdev(&self) -> Option> { if self.config.vendorid != 0 && self.config.productid != 0 { self.find_dev_by_vendor_product() @@ -449,6 +461,7 @@ impl UsbHost { } } + #[cfg(not(all(target_arch = "aarch64", target_env = "ohos")))] fn find_dev_by_bus_addr(&self) -> Option> { self.context .devices() @@ -465,6 +478,7 @@ impl UsbHost { .unwrap_or_else(|| None) } + #[cfg(not(all(target_arch = "aarch64", target_env = "ohos")))] fn find_dev_by_vendor_product(&self) -> Option> { self.context .devices() @@ -482,6 +496,7 @@ impl UsbHost { .unwrap_or_else(|| None) } + #[cfg(not(all(target_arch = "aarch64", target_env = "ohos")))] fn find_dev_by_bus_port(&self) -> Option> { let hostport: Vec<&str> = self.config.hostport.as_ref().unwrap().split('.').collect(); let mut port: Vec = Vec::new(); @@ -644,8 +659,7 @@ impl UsbHost { } } - fn open_and_init(&mut self) -> Result<()> { - self.handle = Some(self.libdev.as_ref().unwrap().open()?); + fn init_usbdev(&mut self) -> Result<()> { self.config.hostbus = self.libdev.as_ref().unwrap().bus_number(); self.config.hostaddr = self.libdev.as_ref().unwrap().address(); trace::usb_host_open_started(self.config.hostbus, self.config.hostaddr); @@ -982,6 +996,26 @@ impl UsbHost { locked_packet.is_async = true; } + + #[cfg(not(all(target_arch = "aarch64", target_env = "ohos")))] + fn open_usbdev(&mut self) -> Result<()> { + self.libdev = Some( + self.find_libdev() + .with_context(|| format!("Invalid USB host config: {:?}", self.config))?, + ); + self.handle = Some(self.libdev.as_ref().unwrap().open()?); + Ok(()) + } + + #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] + fn open_usbdev(&mut self) -> Result<()> { + self.handle = Some( + self.oh_dev + .open(self.config.clone(), self.context.clone())?, + ); + self.libdev = Some(self.handle.as_ref().unwrap().device()); + Ok(()) + } } impl Drop for UsbHost { @@ -1023,13 +1057,9 @@ impl UsbDevice for UsbHost { } fn realize(mut self) -> Result>> { - self.libdev = self.find_libdev(); - if self.libdev.is_none() { - bail!("Invalid USB host config: {:?}", self.config); - } - info!("Open and init usbhost device: {:?}", self.config); - self.open_and_init()?; + self.open_usbdev()?; + self.init_usbdev()?; let usbhost = Arc::new(Mutex::new(self)); let notifiers = EventNotifierHelper::internal_notifiers(usbhost.clone()); @@ -1296,7 +1326,7 @@ impl UsbDevice for UsbHost { } } -fn check_device_valid(device: &Device) -> bool { +pub fn check_device_valid(device: &Device) -> bool { let ddesc = match device.device_descriptor() { Ok(ddesc) => ddesc, Err(_) => return false, diff --git a/devices/src/usb/usbhost/ohusb.rs b/devices/src/usb/usbhost/ohusb.rs new file mode 100644 index 000000000..22027c9f5 --- /dev/null +++ b/devices/src/usb/usbhost/ohusb.rs @@ -0,0 +1,83 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::fd::AsRawFd; +use std::ptr; + +use anyhow::{bail, Context as anyhowContext, Result}; +use libc::c_int; +use libusb1_sys::constants::LIBUSB_OPTION_NO_DEVICE_DISCOVERY; +use log::{error, info}; +use rusb::{Context, DeviceHandle, UsbContext}; + +use super::host_usblib::set_option; +use super::{check_device_valid, UsbHostConfig}; +use util::ohos_binding::usb::*; + +pub struct OhUsbDev { + dev: OhusbDevice, + lib: OhUsb, +} + +impl Drop for OhUsbDev { + fn drop(&mut self) { + if let Err(e) = self.lib.close_device(ptr::addr_of_mut!(self.dev)) { + error!("Failed to close usb device with error {:?}", e) + } + } +} + +impl OhUsbDev { + pub fn new() -> Result { + // In combination with libusb_wrap_sys_device(), in order to access a device directly without prior device scanning on ohos. + set_option(LIBUSB_OPTION_NO_DEVICE_DISCOVERY)?; + + Ok(Self { + dev: OhusbDevice { + busNum: u8::MAX, + devAddr: u8::MAX, + fd: -1, + }, + lib: OhUsb::new()?, + }) + } + + pub fn open(&mut self, cfg: UsbHostConfig, ctx: Context) -> Result> { + self.dev.busNum = cfg.hostbus; + self.dev.devAddr = cfg.hostaddr; + + match self.lib.open_device(ptr::addr_of_mut!(self.dev))? { + 0 => { + if self.dev.fd < 0 { + bail!( + "Failed to open usb device due to invalid fd {}", + self.dev.fd + ); + } + } + _ => bail!("Failed to open usb device"), + } + info!("OH USB: open_device: returned fd is {}", self.dev.fd); + + // SAFETY: fd is valid. + let handle = unsafe { + ctx.open_device_with_fd(self.dev.fd.as_raw_fd()) + .with_context(|| format!("os last error: {:?}", std::io::Error::last_os_error()))? + }; + + if !check_device_valid(&handle.device()) { + bail!("Invalid USB host config: {:?}", cfg); + } + + Ok(handle) + } +} -- Gitee From cfd4ccdc7be8fe47a9a5a2e08a7d9f5c57a68178 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Tue, 4 Jun 2024 10:41:49 +0800 Subject: [PATCH 1870/2187] usb-host: remove unused use statement, add conditional compile remove unused use statement and add conditional compile to eliminate warnings while compiling. Signed-off-by: Fan Xuan Zhe --- devices/src/usb/usbhost/mod.rs | 4 +++- devices/src/usb/usbhost/ohusb.rs | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index bd87be431..3da8cf05d 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -22,7 +22,9 @@ use std::{ time::Duration, }; -use anyhow::{anyhow, Context as anyhowContext, Result}; +#[cfg(not(all(target_arch = "aarch64", target_env = "ohos")))] +use anyhow::Context as anyhowContext; +use anyhow::{anyhow, Result}; use clap::Parser; use libc::c_int; use libusb1_sys::{ diff --git a/devices/src/usb/usbhost/ohusb.rs b/devices/src/usb/usbhost/ohusb.rs index 22027c9f5..469a17a07 100644 --- a/devices/src/usb/usbhost/ohusb.rs +++ b/devices/src/usb/usbhost/ohusb.rs @@ -14,7 +14,6 @@ use std::os::fd::AsRawFd; use std::ptr; use anyhow::{bail, Context as anyhowContext, Result}; -use libc::c_int; use libusb1_sys::constants::LIBUSB_OPTION_NO_DEVICE_DISCOVERY; use log::{error, info}; use rusb::{Context, DeviceHandle, UsbContext}; -- Gitee From 19f262caae35b86d458e1fe2fc59217dbf24d68f Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Wed, 5 Jun 2024 20:13:41 +0800 Subject: [PATCH 1871/2187] ohui: solve lock race between send and recv data While sending or receiving data, both of logic route should hold channel lock. This has side effect on the performance. This patch solves this issue of lock race. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/channel.rs | 91 +++++++++++--------------- ui/src/ohui_srv/mod.rs | 5 +- ui/src/ohui_srv/msg_handle.rs | 119 +++++++++++++++++++++------------- 3 files changed, 114 insertions(+), 101 deletions(-) diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index cfa86d47a..64d6e0ac0 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::io::{Read, Write}; +use std::io::{ErrorKind, Read, Write}; use std::os::fd::AsRawFd; use std::os::unix::io::RawFd; @@ -22,9 +22,8 @@ use util::socket::{SocketListener, SocketStream}; use util::unix::limit_permission; pub struct OhUiChannel { - pub path: String, - pub listener: SocketListener, - pub stream: Option, + listener: SocketListener, + stream: Option, } impl OhUiChannel { @@ -41,7 +40,6 @@ impl OhUiChannel { }); Ok(OhUiChannel { - path: String::from(path), listener, stream: None, }) @@ -60,63 +58,50 @@ impl OhUiChannel { Ok(()) } - pub fn send_by_obj(&mut self, obj: &T) -> Result<()> { - let stream = self.get_stream()?; - let slice = obj.as_bytes(); - let mut left = slice.len(); - let mut count = 0_usize; - - while left > 0 { - let buf = &slice[count..]; - match stream.write(buf) { - Ok(n) => { - left -= n; - count += n; - } - Err(e) => { - if std::io::Error::last_os_error().raw_os_error().unwrap() == libc::EAGAIN { - continue; - } - bail!(e); - } - } - } - Ok(()) + pub fn disconnect(&mut self) { + self.stream = None; } +} - pub fn recv_slice(&mut self, data: &mut [u8]) -> Result { - let stream = self.get_stream()?; - let len = data.len(); - if len == 0 { - return Ok(0); - } - let ret = stream.read(data); - match ret { - Ok(n) => Ok(n), +pub fn recv_slice(stream: &mut dyn Read, data: &mut [u8]) -> Result { + let len = data.len(); + let mut ret = 0; + + while ret < len { + match stream.read(&mut data[ret..len]) { + Ok(0) => break, + Ok(n) => ret += n, Err(e) => { - if std::io::Error::last_os_error() - .raw_os_error() - .unwrap_or(libc::EIO) - != libc::EAGAIN - { - bail!("recv_slice(): error occurred: {:?}", e); + let ek = e.kind(); + if ek != ErrorKind::WouldBlock && ek != ErrorKind::Interrupted { + bail!("recv_slice: error occurred: {:?}", e); } - Ok(0) + break; } } } + Ok(ret) +} - pub fn disconnect(&mut self) { - if self.stream.is_some() { - self.stream = None; - } - } +pub fn send_obj(stream: &mut dyn Write, obj: &T) -> Result<()> { + let slice = obj.as_bytes(); + let mut left = slice.len(); + let mut count = 0_usize; - fn get_stream(&mut self) -> Result<&mut SocketStream> { - if self.stream.is_some() { - Ok(self.stream.as_mut().unwrap()) - } else { - bail!("No connection established") + while left > 0 { + match stream.write(&slice[count..]) { + Ok(n) => { + left -= n; + count += n; + } + Err(e) => { + let ek = e.kind(); + if ek == ErrorKind::WouldBlock || ek == ErrorKind::Interrupted { + continue; + } + bail!(e); + } } } + Ok(()) } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 1d25483a5..899d26342 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -180,8 +180,8 @@ impl OhUiServer { Ok(OhUiServer { passthru: OnceCell::new(), surface: RwLock::new(GuestSurface::new()), - channel: channel.clone(), - msg_handler: Arc::new(OhUiMsgHandler::new(channel)), + channel, + msg_handler: Arc::new(OhUiMsgHandler::new()), connected: AtomicBool::new(false), iothread: OnceCell::new(), cursorbuffer, @@ -266,6 +266,7 @@ impl OhUiServer { fn set_connect(&self, conn: bool) { self.connected.store(conn, Ordering::Relaxed); if conn { + self.msg_handler.update_sock(self.channel.clone()); register_led_sync(self.msg_handler.clone()); } else { unregister_led_sync(); diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index cc0d679e7..dffd0914f 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -11,13 +11,18 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; +use std::os::fd::{FromRawFd, RawFd}; +use std::os::unix::net::UnixStream; use std::sync::{Arc, Mutex, RwLock}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use log::error; use util::byte_code::ByteCode; -use super::{channel::OhUiChannel, msg::*}; +use super::{ + channel::{recv_slice, send_obj, OhUiChannel}, + msg::*, +}; use crate::{ console::{get_active_console, graphic_hardware_ui_info}, input::{ @@ -120,34 +125,46 @@ impl WindowState { } } +#[derive(Default)] pub struct OhUiMsgHandler { state: Mutex, hmcode2svcode: HashMap, - reader: Mutex, - writer: MsgWriter, + reader: Mutex>, + writer: Mutex>, } impl SyncLedstate for OhUiMsgHandler { fn sync_to_host(&self, state: u8) { - let body = LedstateEvent::new(state as u32); - if let Err(e) = self.writer.send_message(EventType::Ledstate, &body) { - error!("sync_to_host: failed to send message with error {e}"); + if let Some(writer) = self.writer.lock().unwrap().as_mut() { + let body = LedstateEvent::new(state as u32); + if let Err(e) = writer.send_message(EventType::Ledstate, &body) { + error!("sync_to_host: failed to send message with error {e}"); + } } } } impl OhUiMsgHandler { - pub fn new(channel: Arc>) -> Self { + pub fn new() -> Self { OhUiMsgHandler { state: Mutex::new(WindowState::default()), hmcode2svcode: KeyCode::keysym_to_qkeycode(DpyMod::Ohui), - reader: Mutex::new(MsgReader::new(channel.clone())), - writer: MsgWriter::new(channel), + reader: Mutex::new(None), + writer: Mutex::new(None), } } + pub fn update_sock(&self, channel: Arc>) { + let fd = channel.lock().unwrap().get_stream_raw_fd().unwrap(); + *self.reader.lock().unwrap() = Some(MsgReader::new(fd)); + *self.writer.lock().unwrap() = Some(MsgWriter::new(fd)); + } + pub fn handle_msg(&self, token_id: Arc>) -> Result<()> { - let mut reader = self.reader.lock().unwrap(); + let mut locked_reader = self.reader.lock().unwrap(); + let reader = locked_reader + .as_mut() + .with_context(|| "handle_msg: no connection established")?; if !reader.recv()? { return Ok(()); } @@ -251,9 +268,11 @@ impl OhUiMsgHandler { hot_y: u32, size_per_pixel: u32, ) { - let body = HWCursorEvent::new(w, h, hot_x, hot_y, size_per_pixel); - if let Err(e) = self.writer.send_message(EventType::CursorDefine, &body) { - error!("handle_cursor_define: failed to send message with error {e}"); + if let Some(writer) = self.writer.lock().unwrap().as_mut() { + let body = HWCursorEvent::new(w, h, hot_x, hot_y, size_per_pixel); + if let Err(e) = writer.send_message(EventType::CursorDefine, &body) { + error!("handle_cursor_define: failed to send message with error {e}"); + } } } @@ -321,45 +340,52 @@ impl OhUiMsgHandler { pub fn send_windowinfo(&self, w: u32, h: u32) { self.state.lock().unwrap().update_window_info(w, h); - let body = WindowInfoEvent::new(w, h); - if let Err(e) = self.writer.send_message(EventType::WindowInfo, &body) { - error!("send_windowinfo: failed to send message with error {e}"); + if let Some(writer) = self.writer.lock().unwrap().as_mut() { + let body = WindowInfoEvent::new(w, h); + if let Err(e) = writer.send_message(EventType::WindowInfo, &body) { + error!("send_windowinfo: failed to send message with error {e}"); + } } } pub fn handle_dirty_area(&self, x: u32, y: u32, w: u32, h: u32) { - let body = FrameBufferDirtyEvent::new(x, y, w, h); - if let Err(e) = self.writer.send_message(EventType::FrameBufferDirty, &body) { - error!("handle_dirty_area: failed to send message with error {e}"); + if let Some(writer) = self.writer.lock().unwrap().as_mut() { + let body = FrameBufferDirtyEvent::new(x, y, w, h); + if let Err(e) = writer.send_message(EventType::FrameBufferDirty, &body) { + error!("handle_dirty_area: failed to send message with error {e}"); + } } } pub fn reset(&self) { - self.reader.lock().unwrap().clear(); + *self.reader.lock().unwrap() = None; + *self.writer.lock().unwrap() = None; } } struct MsgReader { - /// socket to read - channel: Arc>, /// cache for header - pub header: EventMsgHdr, + header: EventMsgHdr, /// received byte size of header - pub header_ready: usize, + header_ready: usize, /// cache of body - pub body: Option>, + body: Option>, /// received byte size of body - pub body_ready: usize, + body_ready: usize, + /// UnixStream to read + sock: UnixStream, } impl MsgReader { - pub fn new(channel: Arc>) -> Self { + pub fn new(fd: RawFd) -> Self { MsgReader { - channel, header: EventMsgHdr::default(), header_ready: 0, body: None, body_ready: 0, + // SAFETY: The fd is valid only when the new connection has been established + // and MsgReader instance would be destroyed when disconnected. + sock: unsafe { UnixStream::from_raw_fd(fd) }, } } @@ -382,11 +408,7 @@ impl MsgReader { } let buf = self.header.as_mut_bytes(); - self.header_ready += self - .channel - .lock() - .unwrap() - .recv_slice(&mut buf[self.header_ready..])?; + self.header_ready += recv_slice(&mut self.sock, &mut buf[self.header_ready..])?; Ok(self.header_ready == EVENT_MSG_HDR_SIZE as usize) } @@ -408,27 +430,32 @@ impl MsgReader { unsafe { buf.set_len(body_size); } - self.body_ready += self - .channel - .lock() - .unwrap() - .recv_slice(&mut buf[self.body_ready..])?; + self.body_ready += recv_slice(&mut self.sock, &mut buf[self.body_ready..])?; Ok(self.body_ready == body_size) } } -struct MsgWriter(Arc>); +struct MsgWriter { + sock: UnixStream, +} impl MsgWriter { - fn new(channel: Arc>) -> Self { - MsgWriter(channel) + fn new(fd: RawFd) -> Self { + Self { + // SAFETY: The fd is valid only when the new connection has been established + // and MsgWriter instance would be destroyed when disconnected. + sock: unsafe { UnixStream::from_raw_fd(fd) }, + } } - fn send_message(&self, t: EventType, body: &T) -> Result<()> { - let mut channel = self.0.lock().unwrap(); + fn send_message( + &mut self, + t: EventType, + body: &T, + ) -> Result<()> { let hdr = EventMsgHdr::new(t); - channel.send_by_obj(&hdr)?; - channel.send_by_obj(body) + send_obj(&mut self.sock, &hdr)?; + send_obj(&mut self.sock, body) } } -- Gitee From e5adb22b05db625f3c966809d74467fab3818bd0 Mon Sep 17 00:00:00 2001 From: "limingwang (A)" Date: Thu, 6 Jun 2024 11:52:02 +0800 Subject: [PATCH 1872/2187] bugfix: enable shutdown vm again After the shutdown fails, perform the shutdown again. Signed-off-by: Mingwang Li --- machine/src/aarch64/standard.rs | 5 +++-- machine/src/standard_common/mod.rs | 2 +- machine/src/x86_64/standard.rs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 12fe4086f..4b6eebdf0 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -258,7 +258,7 @@ impl StdMachine { Ok(()) } - pub fn handle_destroy_request(vm: &Arc>) -> Result<()> { + pub fn handle_destroy_request(vm: &Arc>) -> bool { let locked_vm = vm.lock().unwrap(); let vmstate = { let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); @@ -270,12 +270,13 @@ impl StdMachine { if locked_vm.shutdown_req.write(1).is_err() { error!("Failed to send shutdown request.") } + return false; } EventLoop::kick_all(); info!("vm destroy"); - Ok(()) + true } fn build_pptt_cores(&self, pptt: &mut AcpiTable, cluster_offset: u32, uid: &mut u32) { diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 02780b8ee..f18350ae4 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -343,7 +343,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { let _ret = shutdown_req.read(); - if StdMachine::handle_destroy_request(&clone_vm).is_ok() { + if StdMachine::handle_destroy_request(&clone_vm) { Some(gen_delete_notifiers(&[shutdown_req_fd])) } else { None diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 0dc5da820..8dca2db77 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -210,7 +210,7 @@ impl StdMachine { Ok(()) } - pub fn handle_destroy_request(vm: &Arc>) -> Result<()> { + pub fn handle_destroy_request(vm: &Arc>) -> bool { let locked_vm = vm.lock().unwrap(); let vmstate = { let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); @@ -222,12 +222,13 @@ impl StdMachine { if locked_vm.shutdown_req.write(1).is_err() { error!("Failed to send shutdown request.") } + return false; } EventLoop::kick_all(); info!("vm destroy"); - Ok(()) + true } fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { -- Gitee From af856fd9c70bc363681cc142863f2a0b3121d5ea Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 3 Jun 2024 21:18:30 +0800 Subject: [PATCH 1873/2187] util/socket: add set_nonblocking interface to fixup main-loop hang issue Accepted stream is blocking mode although listening stream has been set nonblocked. This might block char device write. Then main-loop would be also blocked. So let's introduce an interface to set nonblocking and set accepted fd with nonblocking mode by default. Signed-off-by: Zhao Yi Min --- util/src/socket.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/util/src/socket.rs b/util/src/socket.rs index 3b1290bbe..573b7f443 100644 --- a/util/src/socket.rs +++ b/util/src/socket.rs @@ -41,6 +41,13 @@ impl SocketStream { } => link_description.clone(), } } + + pub fn set_nonblocking(&mut self, nonblocking: bool) -> IoResult<()> { + match self { + SocketStream::Tcp { stream, .. } => stream.set_nonblocking(nonblocking), + SocketStream::Unix { stream, .. } => stream.set_nonblocking(nonblocking), + } + } } impl AsRawFd for SocketStream { @@ -132,6 +139,7 @@ impl SocketListener { match self { SocketListener::Tcp { listener, address } => { let (stream, sock_addr) = listener.accept()?; + stream.set_nonblocking(true)?; let peer_address = sock_addr.to_string(); let link_description = format!( "{{ protocol: tcp, address: {}, peer: {} }}", @@ -144,6 +152,7 @@ impl SocketListener { } SocketListener::Unix { listener, address } => { let (stream, _) = listener.accept()?; + stream.set_nonblocking(true)?; let link_description = format!("{{ protocol: unix, address: {} }}", address); Ok(SocketStream::Unix { link_description, -- Gitee From 1bf12724299717962661f947656aa8b5865583f4 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 4 Jun 2024 20:30:58 +0800 Subject: [PATCH 1874/2187] chardev: introduce out buffer queue This patch introduces an out buffer queue to temporarily save the data to be written out. The out stream fd would be registerred to main loop if the buffer queue is not empty. When all the data has been consumed, remove the handler for OUT event from main loop. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 135 +++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 8 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index cf4bb7126..7bf53e209 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -10,8 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::VecDeque; use std::fs::{read_link, File, OpenOptions}; -use std::io::{Stdin, Stdout}; +use std::io::{ErrorKind, Stdin, Stdout}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::path::PathBuf; use std::rc::Rc; @@ -41,6 +42,8 @@ use util::set_termi_raw_mode; use util::socket::{SocketListener, SocketStream}; use util::unix::limit_permission; +const BUF_QUEUE_SIZE: usize = 128; + /// Provide the trait that helps handle the input data. pub trait InputReceiver: Send { /// Handle the input data and trigger interrupt if necessary. @@ -91,6 +94,8 @@ pub struct Chardev { output_blocked: Option>, /// output listener to notify when output stream fd can be written output_listener_fd: Option>, + /// output buffer queue + outbuf: VecDeque>, } impl Chardev { @@ -108,6 +113,7 @@ impl Chardev { unpause_timer: None, output_blocked: None, output_listener_fd: None, + outbuf: VecDeque::with_capacity(BUF_QUEUE_SIZE), } } @@ -255,6 +261,58 @@ impl Chardev { } } + fn clear_outbuf(&mut self) { + self.outbuf.clear(); + } + + pub fn outbuf_is_full(&self) -> bool { + self.outbuf.len() == self.outbuf.capacity() + } + + pub fn fill_outbuf(&mut self, buf: Vec, listener_fd: Option>) -> Result<()> { + match self.backend { + ChardevType::File { .. } | ChardevType::Pty { .. } | ChardevType::Stdio { .. } => { + if self.output.is_none() { + bail!("chardev has no output"); + } + return file_write_direct(self.output.as_ref().unwrap().clone(), buf); + } + ChardevType::Socket { .. } => (), + } + + if self.output.is_none() { + return Ok(()); + } + + if self.outbuf_is_full() { + bail!("Failed to append buffer because output buffer queue is full"); + } + self.outbuf.push_back(buf); + self.output_listener_fd = listener_fd; + + let event_notifier = EventNotifier::new( + NotifierOperation::AddEvents, + self.stream_fd.unwrap(), + None, + EventSet::OUT, + Vec::new(), + ); + EventLoop::update_event(vec![event_notifier], None)?; + Ok(()) + } + + fn consume_outbuf(&mut self) -> Result<()> { + let cloned_output = self.output.as_ref().unwrap().clone(); + while !self.outbuf.is_empty() { + if write_buffer_out(cloned_output.clone(), self.outbuf.front_mut().unwrap())? { + break; + } + self.outbuf.pop_front(); + } + cloned_output.lock().unwrap().flush()?; + Ok(()) + } + pub fn add_listen_for_tx( &mut self, listener_fd: Arc, @@ -283,6 +341,50 @@ impl Chardev { } } +fn file_write_direct(writer: Arc>, buf: Vec) -> Result<()> { + let len = buf.len(); + let mut written = 0; + let mut locked_writer = writer.lock().unwrap(); + + while written < len { + match locked_writer.write(&buf[written..len]) { + Ok(n) => written += n, + Err(e) => bail!("chardev failed to write file with error {:?}", e), + } + } + Ok(()) +} + +// If write is blocked, return true. Otherwise return false. +fn write_buffer_out( + writer: Arc>, + buf: &mut Vec, +) -> Result { + let len = buf.len(); + let mut locked_writer = writer.lock().unwrap(); + let mut written = 0; + + while written < len { + match locked_writer.write(&buf[written..len]) { + Ok(0) => break, + Ok(n) => written += n, + Err(e) => { + let err_type = e.kind(); + if err_type != ErrorKind::WouldBlock && err_type != ErrorKind::Interrupted { + bail!("chardev failed to write data with error {:?}", e); + } + break; + } + } + } + + if written == len { + return Ok(false); + } + buf.drain(0..written); + Ok(true) +} + fn set_pty_raw_mode() -> Result<(i32, PathBuf)> { let (master, slave) = match openpty(None, None) { Ok(res) => (res.master, res.slave), @@ -513,6 +615,18 @@ fn get_socket_notifier(chardev: Arc>) -> Option { } let mut locked_cdev = handling_chardev.lock().unwrap(); + if let Err(e) = locked_cdev.consume_outbuf() { + error!("Failed to consume outbuf with error {:?}", e); + locked_cdev.clear_outbuf(); + return Some(vec![EventNotifier::new( + NotifierOperation::DeleteEvents, + fd, + None, + EventSet::OUT, + Vec::new(), + )]); + } + if locked_cdev.output_blocked.is_some() && locked_cdev.output_listener_fd.is_some() { let fd = locked_cdev.output_listener_fd.as_ref().unwrap(); if let Err(e) = fd.write(1) { @@ -527,13 +641,18 @@ fn get_socket_notifier(chardev: Arc>) -> Option { locked_cdev.output_blocked = None; locked_cdev.output_listener_fd = None; } - Some(vec![EventNotifier::new( - NotifierOperation::DeleteEvents, - fd, - None, - EventSet::OUT, - Vec::new(), - )]) + + if locked_cdev.outbuf.is_empty() { + Some(vec![EventNotifier::new( + NotifierOperation::DeleteEvents, + fd, + None, + EventSet::OUT, + Vec::new(), + )]) + } else { + None + } }); Some(vec![EventNotifier::new( -- Gitee From 011cec237838e2bc3171037c33a0d0e71aefb64a Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 4 Jun 2024 20:53:13 +0800 Subject: [PATCH 1875/2187] virtio-serial: fit output handler to chardev outbuf Let's fit the output handler of virtio-serial to the new mechanism of chardev output buffer and clean up the code. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 39 +----------- virtio/src/device/serial.rs | 110 ++++++--------------------------- 2 files changed, 21 insertions(+), 128 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 7bf53e209..a1c158d58 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -16,7 +16,6 @@ use std::io::{ErrorKind, Stdin, Stdout}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::path::PathBuf; use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -90,8 +89,6 @@ pub struct Chardev { /// Scheduled DPC to unpause input stream. /// Unpause must be done inside event-loop unpause_timer: Option, - /// output stream fd is blocked - output_blocked: Option>, /// output listener to notify when output stream fd can be written output_listener_fd: Option>, /// output buffer queue @@ -111,7 +108,6 @@ impl Chardev { dev: None, wait_port: false, unpause_timer: None, - output_blocked: None, output_listener_fd: None, outbuf: VecDeque::with_capacity(BUF_QUEUE_SIZE), } @@ -312,33 +308,6 @@ impl Chardev { cloned_output.lock().unwrap().flush()?; Ok(()) } - - pub fn add_listen_for_tx( - &mut self, - listener_fd: Arc, - blocked: Arc, - ) -> Result<()> { - let event_notifier = EventNotifier::new( - NotifierOperation::AddEvents, - self.stream_fd.unwrap(), - None, - EventSet::OUT, - Vec::new(), - ); - - match EventLoop::update_event(vec![event_notifier], None) { - Ok(()) => { - self.output_blocked = Some(blocked.clone()); - self.output_listener_fd = Some(listener_fd); - blocked.store(true, Ordering::Release); - Ok(()) - } - Err(e) => { - blocked.store(false, Ordering::Release); - Err(e) - } - } - } } fn file_write_direct(writer: Arc>, buf: Vec) -> Result<()> { @@ -627,18 +596,12 @@ fn get_socket_notifier(chardev: Arc>) -> Option { )]); } - if locked_cdev.output_blocked.is_some() && locked_cdev.output_listener_fd.is_some() { + if locked_cdev.output_listener_fd.is_some() { let fd = locked_cdev.output_listener_fd.as_ref().unwrap(); if let Err(e) = fd.write(1) { error!("Failed to write eventfd with error {:?}", e); return None; } - locked_cdev - .output_blocked - .as_ref() - .unwrap() - .store(false, Ordering::Release); - locked_cdev.output_blocked = None; locked_cdev.output_listener_fd = None; } diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 7d4b11ea5..102a3ed6f 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -277,10 +277,6 @@ impl VirtioDevice for Serial { device_broken: self.base.broken.clone(), port: port.clone(), nr, - outbuf: Vec::with_capacity(BUF_SIZE), - outbuf_consumed: 0, - outbuf_len: 0, - output_blocked: Arc::new(AtomicBool::new(false)), }; let handler_h = Arc::new(Mutex::new(handler)); let notifiers = EventNotifierHelper::internal_notifiers(handler_h.clone()); @@ -423,10 +419,6 @@ struct SerialPortHandler { device_broken: Arc, port: Option>>, nr: u32, - outbuf: Vec, - outbuf_consumed: usize, - outbuf_len: usize, - output_blocked: Arc, } /// Handler for queues which are used for control. @@ -465,24 +457,17 @@ impl SerialPortHandler { } fn output_handle_internal(&mut self) -> Result<()> { - // If fd for tx is blocked, copy data to output buffer and wait for POLL_OUT event. - if self.output_blocked.load(Ordering::Acquire) { - return Ok(()); - } + let mut queue_lock = self.output_queue.lock().unwrap(); - match self.consume_outbuf() { - Ok(blocked) => { - if blocked { - return Ok(()); + loop { + if let Some(port) = self.port.as_ref() { + let locked_port = port.lock().unwrap(); + let locked_cdev = locked_port.chardev.lock().unwrap(); + if locked_cdev.outbuf_is_full() { + break; } } - Err(e) => bail!("Failed to consume out buffer with error {:?}", e), - } - let queue = self.output_queue.clone(); - let mut queue_lock = queue.lock().unwrap(); - let mut blocked = false; - while !blocked { let elem = queue_lock .vring .pop_avail(&self.mem_space, self.driver_features)?; @@ -490,26 +475,24 @@ impl SerialPortHandler { break; } - assert_eq!(self.outbuf_len, 0); - // Discard requests when there is no port using this queue. Popping elements without // processing means discarding the request. - if self.port.is_some() { + if let Some(port) = self.port.as_ref() { let iovec = elem.out_iovec; let iovec_size = Element::iovec_size(&iovec); - if iovec_size as usize > self.outbuf.len() { - self.outbuf.resize(iovec_size as usize, 0); - } + let mut buf = vec![0u8; iovec_size as usize]; + let size = iov_to_buf(&self.mem_space, &iovec, &mut buf[..])? as u64; - let buffer = &mut self.outbuf[..]; - let size = iov_to_buf(&self.mem_space, &iovec, buffer)? as u64; - - assert_eq!(size, iovec_size); - self.outbuf_len = size as usize; - - match self.consume_outbuf() { - Ok(b) => blocked = b, - Err(e) => bail!("Failed to consume out buffer with error {:?}", e), + let locked_port = port.lock().unwrap(); + if locked_port.host_connected { + if let Err(e) = locked_port + .chardev + .lock() + .unwrap() + .fill_outbuf(buf, Some(self.output_queue_evt.clone())) + { + error!("Failed to append elem buffer to chardev with error {:?}", e); + } } trace::virtio_serial_output_data(iovec_size, size); } @@ -542,59 +525,6 @@ impl SerialPortHandler { Ok(()) } - fn clear_outbuf(&mut self) { - self.outbuf_len = 0; - self.outbuf_consumed = 0; - } - - fn consume_outbuf(&mut self) -> Result { - if self.outbuf_len == 0 { - return Ok(false); - } - - let port_cloned = self.port.clone(); - let port_locked = port_cloned.as_ref().unwrap().lock().unwrap(); - // Discard output buffer if this port's chardev is not connected. - if !port_locked.host_connected { - self.clear_outbuf(); - return Ok(false); - } - - let mut locked_chardev = port_locked.chardev.lock().unwrap(); - if locked_chardev.output.is_none() { - error!("Port {} failed to get output interface", self.nr); - self.clear_outbuf(); - return Ok(false); - } - let output = locked_chardev.output.clone(); - let mut locked_output = output.as_ref().unwrap().lock().unwrap(); - - while self.outbuf_consumed < self.outbuf_len { - match locked_output.write(&self.outbuf[self.outbuf_consumed..self.outbuf_len]) { - Ok(size) => self.outbuf_consumed += size, - Err(e) => { - let err_type = e.kind(); - if err_type != std::io::ErrorKind::WouldBlock - && err_type != std::io::ErrorKind::Interrupted - { - self.clear_outbuf(); - bail!("chardev failed to write message with error {:?}", e); - } - if let Err(e) = locked_chardev.add_listen_for_tx( - self.output_queue_evt.clone(), - self.output_blocked.clone(), - ) { - error!("failed to wait for tx fd with error {:?}", e); - continue; - } - return Ok(true); - } - } - } - self.clear_outbuf(); - Ok(false) - } - fn get_input_avail_bytes(&mut self, max_size: usize) -> usize { let port = self.port.as_ref(); if port.is_none() || !port.unwrap().lock().unwrap().guest_connected { -- Gitee From 312ee21fcbf06bf9b64b2f0cdba072d0ab0c82b4 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 4 Jun 2024 21:01:05 +0800 Subject: [PATCH 1876/2187] legacy-serial: use new chardev output mechanism Let's fit legacy-serial to the new chardev output mechanism. Signed-off-by: Zhao Yi Min --- devices/src/legacy/serial.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 80415e075..192de908e 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -296,19 +296,10 @@ impl Serial { self.rbr.push_back(data); self.state.lsr |= UART_LSR_DR; - } else { - let output = self.chardev.lock().unwrap().output.clone(); - if output.is_none() { - self.update_iir(); - bail!("serial: failed to get output fd."); - } - let mut locked_output = output.as_ref().unwrap().lock().unwrap(); - locked_output - .write_all(&[data]) - .with_context(|| "serial: failed to write.")?; - locked_output - .flush() - .with_context(|| "serial: failed to flush.")?; + } else if let Err(e) = + self.chardev.lock().unwrap().fill_outbuf(vec![data], None) + { + bail!("Failed to append data to output buffer of chardev, {:?}", e); } self.update_iir(); -- Gitee From 5c8eab444f1fd7d19f4a2291219bb741d2e3cac6 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 4 Jun 2024 21:08:52 +0800 Subject: [PATCH 1877/2187] pl011: use new chardev output mechanism Let's fit pl011 to the new output mechanism of chardev. Signed-off-by: Zhao Yi Min --- devices/src/legacy/pl011.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index fe76da2de..b2ff1ca14 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; -use log::{debug, error}; +use log::error; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; @@ -352,20 +352,10 @@ impl SysBusDevOps for PL011 { match offset >> 2 { 0 => { let ch = value as u8; - - if let Some(output) = &mut self.chardev.lock().unwrap().output { - let mut locked_output = output.lock().unwrap(); - if let Err(e) = locked_output.write_all(&[ch]) { - debug!("Failed to write to pl011 output fd, error is {:?}", e); - } - if let Err(e) = locked_output.flush() { - debug!("Failed to flush pl011, error is {:?}", e); - } - } else { - debug!("Failed to get output fd"); + if let Err(e) = self.chardev.lock().unwrap().fill_outbuf(vec![ch], None) { + error!("Failed to append pl011 data to outbuf of chardev, {:?}", e); return false; } - self.state.int_level |= INT_TX; self.interrupt(); } -- Gitee From 5bf9b6c4478f98a3e79d3e19f68507a6c72649fa Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 7 Jun 2024 15:25:44 +0800 Subject: [PATCH 1878/2187] chardev: flush data after write out Current code only writes data without flushing. So this will make peer cannot get data immediately. So flush data after write. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index a1c158d58..1b24d0905 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -305,7 +305,6 @@ impl Chardev { } self.outbuf.pop_front(); } - cloned_output.lock().unwrap().flush()?; Ok(()) } } @@ -321,6 +320,9 @@ fn file_write_direct(writer: Arc>, buf: Vec bail!("chardev failed to write file with error {:?}", e), } } + locked_writer + .flush() + .with_context(|| "chardev failed to flush")?; Ok(()) } @@ -346,6 +348,9 @@ fn write_buffer_out( } } } + locked_writer + .flush() + .with_context(|| "chardev failed to flush")?; if written == len { return Ok(false); -- Gitee From a63082ef4a940b11da5fadeea7bb08c10cb7c4c9 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 7 Jun 2024 15:50:41 +0800 Subject: [PATCH 1879/2187] chardev: optimize the code of writing out Use sync/async as suffix of the function name to be better readable. And also remove extra clone operation. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 1b24d0905..bd2b37dbd 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -271,7 +271,7 @@ impl Chardev { if self.output.is_none() { bail!("chardev has no output"); } - return file_write_direct(self.output.as_ref().unwrap().clone(), buf); + return write_buffer_sync(self.output.as_ref().unwrap().clone(), buf); } ChardevType::Socket { .. } => (), } @@ -298,9 +298,9 @@ impl Chardev { } fn consume_outbuf(&mut self) -> Result<()> { - let cloned_output = self.output.as_ref().unwrap().clone(); + let output = self.output.as_ref().unwrap(); while !self.outbuf.is_empty() { - if write_buffer_out(cloned_output.clone(), self.outbuf.front_mut().unwrap())? { + if write_buffer_async(output.clone(), self.outbuf.front_mut().unwrap())? { break; } self.outbuf.pop_front(); @@ -309,7 +309,7 @@ impl Chardev { } } -fn file_write_direct(writer: Arc>, buf: Vec) -> Result<()> { +fn write_buffer_sync(writer: Arc>, buf: Vec) -> Result<()> { let len = buf.len(); let mut written = 0; let mut locked_writer = writer.lock().unwrap(); @@ -327,7 +327,7 @@ fn file_write_direct(writer: Arc>, buf: Vec>, buf: &mut Vec, ) -> Result { -- Gitee From f8888ebb59d169a41913aa67a84375f10035853e Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Tue, 11 Jun 2024 09:53:38 +0800 Subject: [PATCH 1880/2187] usb-host: conditionally link the 'libusb-1.0' library based on whether 'usb-host' feature is enabled adds feature check to see whether "usb-host" is enabled, and if so, adds a libusb linker argument during the build process. Signed-off-by: Fan Xuan Zhe --- build.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 53d0c9b25..9c03e5358 100644 --- a/build.rs +++ b/build.rs @@ -16,7 +16,9 @@ fn ohos_env_configure() { println!("cargo:rustc-link-arg=--verbose"); println!("cargo:rustc-link-arg=--sysroot={}/sysroot", ohos_sdk_path); println!("cargo:rustc-link-arg=-lpixman_static"); - println!("cargo:rustc-link-arg=-lusb-1.0"); + if cfg!(feature = "usb-host") { + println!("cargo:rustc-link-arg=-lusb-1.0"); + } println!( "cargo:rustc-link-search={}/sysroot/usr/lib/aarch64-linux-ohos", ohos_sdk_path -- Gitee From 2f54e0be9d48f1604f5ee84f782d631400b0aeae Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Tue, 11 Jun 2024 20:26:16 +0800 Subject: [PATCH 1881/2187] usb-host: fix typo in conditionally link the specific feature name in code is "usb_host" rather than "usb-host". Signed-off-by: Fan Xuan Zhe --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 9c03e5358..13b5a8985 100644 --- a/build.rs +++ b/build.rs @@ -16,7 +16,7 @@ fn ohos_env_configure() { println!("cargo:rustc-link-arg=--verbose"); println!("cargo:rustc-link-arg=--sysroot={}/sysroot", ohos_sdk_path); println!("cargo:rustc-link-arg=-lpixman_static"); - if cfg!(feature = "usb-host") { + if cfg!(feature = "usb_host") { println!("cargo:rustc-link-arg=-lusb-1.0"); } println!( -- Gitee From 6220a2364d6ac115f4f549a62a5c27d6b48abf96 Mon Sep 17 00:00:00 2001 From: yexiao Date: Fri, 31 May 2024 10:46:47 +0800 Subject: [PATCH 1882/2187] gtk: activate the current page duration display init Signed-off-by: Xiao Ye --- ui/src/gtk/menu.rs | 9 +++++++-- ui/src/gtk/mod.rs | 9 ++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ui/src/gtk/menu.rs b/ui/src/gtk/menu.rs index e89b7a3ea..c1e4b6b64 100644 --- a/ui/src/gtk/menu.rs +++ b/ui/src/gtk/menu.rs @@ -42,7 +42,7 @@ pub(crate) struct GtkMenu { pub(crate) window: ApplicationWindow, container: gtk::Box, pub(crate) note_book: gtk::Notebook, - pub(crate) radio_group: Vec, + pub(crate) radio_group: Rc>>, accel_group: AccelGroup, menu_bar: MenuBar, machine_menu: Menu, @@ -64,7 +64,7 @@ impl GtkMenu { window, container: gtk::Box::new(Orientation::Vertical, 0), note_book: gtk::Notebook::default(), - radio_group: vec![], + radio_group: Rc::new(RefCell::new(vec![])), accel_group: AccelGroup::default(), menu_bar: MenuBar::new(), machine_menu: Menu::new(), @@ -258,6 +258,11 @@ impl GtkMenu { self.zoom_fit.activate(); } + if let Some(page_num) = self.note_book.current_page() { + let radio_item = &self.radio_group.borrow()[page_num as usize]; + radio_item.activate(); + } + self.menu_bar.hide(); } } diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index ced3792bd..84b59e5d6 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -304,14 +304,17 @@ impl GtkDisplay { })); self.gtk_menu.view_menu.append(&gs_show_menu); - if !self.gtk_menu.radio_group.is_empty() { - let first_radio = &self.gtk_menu.radio_group[0]; + if !self.gtk_menu.radio_group.borrow().is_empty() { + let first_radio = &self.gtk_menu.radio_group.borrow()[0]; gs_show_menu.join_group(Some(first_radio)); } else { note_book.set_current_page(Some(page_num)); } - self.gtk_menu.radio_group.push(gs_show_menu.clone()); + self.gtk_menu + .radio_group + .borrow_mut() + .push(gs_show_menu.clone()); gs.borrow_mut().show_menu = gs_show_menu; gs.borrow_mut().draw_area = draw_area; -- Gitee From f96b456ae486bf08b523a9a48557a70a202bd8fd Mon Sep 17 00:00:00 2001 From: AlisaWang Date: Wed, 12 Jun 2024 10:45:11 +0800 Subject: [PATCH 1883/2187] MST:add "-disable-seccomp" on ohos Signed-off-by: AlisaWang --- tests/mod_test/src/libtest.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 6de33d664..ada68e0a6 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -355,7 +355,12 @@ pub fn test_init(extra_arg: Vec<&str>) -> TestState { let listener = init_socket(&test_socket); - let child = Command::new(binary_path) + let mut cmd = Command::new(binary_path); + + #[cfg(target_env = "ohos")] + cmd.args(["-disable-seccomp"]); + + let child = cmd .args(["-accel", "test"]) .args(["-qmp", &format!("unix:{},server,nowait", qmp_socket)]) .args(["-mod-test", &test_socket]) -- Gitee From 8011a6bfe153138024d6fd164c1d9d8f9ae111ac Mon Sep 17 00:00:00 2001 From: zhanghan Date: Wed, 12 Jun 2024 11:09:15 +0800 Subject: [PATCH 1884/2187] OHAudio: avoid audio-playing's delay We drop old data before push new one. So data in buffer includes newest chunks. Signed-off-by: zhanghan --- devices/src/misc/scream/ohaudio.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index fc6fccf0e..2349d93a2 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -35,7 +35,7 @@ struct StreamUnit { pub len: u64, } -const STREAM_DATA_VEC_CAPACITY: usize = 30; +const STREAM_DATA_VEC_CAPACITY: usize = 15; const FLUSH_DELAY_THRESHOLD_MS: u64 = 100; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; @@ -144,6 +144,15 @@ impl OhAudioProcess for OhAudioRender { len: recv_data.audio_size as u64, }; let mut locked_data = self.stream_data.lock().unwrap(); + // When audio data is not consumed in time, we remove old chunk + // and push new one. So audio-playing won't delay. One chunk means + // 20 ms data. + if locked_data.len() >= STREAM_DATA_VEC_CAPACITY { + let remove_size = locked_data[0].len; + locked_data.remove(0); + self.data_size + .fetch_sub(remove_size as i32, Ordering::Relaxed); + } locked_data.push(su); self.data_size .fetch_add(recv_data.audio_size as i32, Ordering::Relaxed); -- Gitee From 171efe8a6153ee287154683c7e82f3bb3315057a Mon Sep 17 00:00:00 2001 From: yexiao Date: Wed, 12 Jun 2024 21:22:58 +0800 Subject: [PATCH 1885/2187] ThreadPool: modify the upper limit of threads in pool. Set the upper limit of threads to 10 in thread pool. Signed-off-by: Xiao Ye --- util/src/thread_pool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/thread_pool.rs b/util/src/thread_pool.rs index db059a808..570d42561 100644 --- a/util/src/thread_pool.rs +++ b/util/src/thread_pool.rs @@ -20,7 +20,7 @@ use log::error; use crate::link_list::{List, Node}; const MIN_THREADS: u64 = 1; -const MAX_THREADS: u64 = 64; +const MAX_THREADS: u64 = 10; type PoolTask = Box; pub trait TaskOperation: Sync + Send { -- Gitee From 1c80f8f07d59b7a51bfe47b2f6aa4560513ed4d3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 13 Jun 2024 14:21:13 +0800 Subject: [PATCH 1886/2187] virtio-net: fix an error of virtio-net If the virtio queue is not ready, then report virtio error. this error was introduced in 93994cfeda19a3c5918e45c580e585a1e285cbf7. Signed-off-by: Xiao Ye --- tests/mod_test/tests/net_test.rs | 5 ++++- virtio/src/queue/split.rs | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 3ec79a1eb..4062ce83c 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -2083,7 +2083,10 @@ fn virtio_net_abnormal_rx_tx_test_3() { vqs.push(vq); } fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); - net.borrow().set_driver_ok(); + + // Set driver ok without check. + let status = net.borrow().get_status() | VIRTIO_CONFIG_S_DRIVER_OK; + net.borrow().set_status(status); let request = get_arp_request(id); let length = request.as_bytes().len() as u64; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 302c258da..643693b5d 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -892,6 +892,10 @@ impl VringOps for SplitVring { features: u64, suppress: bool, ) -> Result<()> { + if !self.is_enabled() { + bail!("queue is not ready"); + } + if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { self.set_avail_event(sys_mem, self.get_avail_idx(sys_mem)?)?; } else { -- Gitee From 259e9728fbc44a94f5f662b325c8f405deeb5bc6 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 13 Jun 2024 14:30:30 +0800 Subject: [PATCH 1887/2187] virtio-net: fix an error of virtio-net The default value of queue_avail should be true, this error was introduced in 93994cfeda19a3c5918e45c580e585a1e285cbf7. Signed-off-by: Xiao Ye --- virtio/src/device/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index c4053f064..25abc3455 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -694,7 +694,7 @@ struct RxVirtio { impl RxVirtio { fn new(queue: Arc>, queue_evt: Arc) -> Self { RxVirtio { - queue_avail: false, + queue_avail: true, queue, queue_evt, } -- Gitee From fb7b8cfd801cc05b43b7d1e4503e4f1c449e7b3e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 13 Jun 2024 19:57:19 +0800 Subject: [PATCH 1888/2187] display: fix clippy warnings in `get_sock_path` Fix clippy warnings in `get_sock_path`. Fix: 2472e3feb(display: use clap to parse the parameters of the display config) Signed-off-by: liuxiangdong --- machine_manager/src/config/display.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index a8eca8c0d..d07828581 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -40,8 +40,8 @@ pub struct UiContext { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] fn get_sock_path(p: &str) -> Result { - let path = std::fs::canonicalize(p.clone()) - .with_context(|| format!("Failed to get real directory path: {:?}", p.clone()))?; + let path = std::fs::canonicalize(p) + .with_context(|| format!("Failed to get real directory path: {:?}", p))?; if !path.exists() { bail!( "The defined directory {:?} path doesn't exist", -- Gitee From 92240bc1e985cede6cf63be7350ef315fc0fbd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Wed, 12 Jun 2024 15:15:24 +0800 Subject: [PATCH 1889/2187] check oh msg header size Signed-off-by: Jiahong Li --- ui/src/ohui_srv/msg_handle.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index dffd0914f..1e085e520 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -170,18 +170,8 @@ impl OhUiMsgHandler { } let hdr = &reader.header; - let body_size = hdr.size as usize; let event_type = hdr.event_type; - let expect_body_size = event_msg_data_len(hdr.event_type); - if body_size != expect_body_size { - reader.clear(); - bail!( - "{:?} data len is wrong, we want {}, but receive {}", - event_type, - expect_body_size, - body_size - ); - } + let body_size = hdr.size as size; trace::trace_scope_start!(handle_msg, args = (&event_type)); let body_bytes = reader.body.as_ref().unwrap(); @@ -391,17 +381,32 @@ impl MsgReader { pub fn recv(&mut self) -> Result { if self.recv_header()? { + self.check_header()?; return self.recv_body(); } Ok(false) } - pub fn clear(&mut self) { + fn clear(&mut self) { self.header_ready = 0; self.body_ready = 0; self.body = None; } + fn check_header(&mut self) -> Result<()> { + let expected_size = event_msg_data_len(self.header.event_type); + if expected_size != self.header.size as usize { + self.clear(); + bail!( + "{:?} data len is wrong, we want {}, but receive {}", + self.header.event_type as EventType, + expected_size, + self.header.size as usize, + ); + } + Ok(()) + } + fn recv_header(&mut self) -> Result { if self.header_ready == EVENT_MSG_HDR_SIZE as usize { return Ok(true); -- Gitee From db94857704bfae545206e881e43b36062b22cbe1 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 14 Jun 2024 05:45:01 +0800 Subject: [PATCH 1890/2187] address_space: fix searching ioeventfd error The address that ioeventfd listens to is not unique. For example, virtio-mmio device will listen to `QueueNotify` register(0x050). This register will receive a value to notify this virtio-mmio device that there are new buffers to process in a queue. So, all virtqueues of this device will listen to this register. In the case of multiple ioevevtfds with the same address range, it is necessary to determine which ioeventfd to use through `datamatch` and `data`. However, according to the description of the rust `slice` library, the action of function `binary_search_by` is: `If there are multiple matches, then any one of the matches could be returned.` Thus, the virtio-mmio device may encounter various exceptions because of this random unexpected ioeventfd calling. Delete this `binary_search_by`. Fix: 07f2d03883574(AddressSpace: Optimized the ioeventfd mechanism) Signed-off-by: liuxiangdong --- address_space/src/address_space.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index fd419d77d..d34dd59e8 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -633,13 +633,15 @@ impl AddressSpace { trace::trace_scope_start!(address_space_write, args = (&addr, count)); let view = self.flat_view.load(); + let mut buf = Vec::new(); + src.read_to_end(&mut buf).unwrap(); + if !*self.hyp_ioevtfd_enabled.get_or_init(|| false) { let ioeventfds = self.ioeventfds.lock().unwrap(); - if let Ok(index) = ioeventfds - .as_slice() - .binary_search_by(|ioevtfd| ioevtfd.addr_range.base.cmp(&addr)) - { - let evtfd = &ioeventfds[index]; + for evtfd in ioeventfds.as_slice() { + if evtfd.addr_range.base != addr { + continue; + } if count == evtfd.addr_range.size || evtfd.addr_range.size == 0 { if !evtfd.data_match { if let Err(e) = evtfd.fd.write(1) { @@ -648,26 +650,27 @@ impl AddressSpace { return Ok(()); } - let mut buf = Vec::new(); - src.read_to_end(&mut buf).unwrap(); + let mut buf_temp = buf.clone(); - if buf.len() <= 8 { - buf.resize(8, 0); - let data = u64::from_bytes(buf.as_slice()).unwrap(); + if buf_temp.len() <= 8 { + buf_temp.resize(8, 0); + let data = u64::from_bytes(buf_temp.as_slice()).unwrap(); if *data == evtfd.data { if let Err(e) = evtfd.fd.write(1) { error!("Failed to write ioeventfd {:?}: {}", evtfd, e); } return Ok(()); + } else { + continue; } } - view.write(&mut buf.as_slice(), addr, count)?; + view.write(&mut buf_temp.as_slice(), addr, count)?; return Ok(()); } } } - view.write(src, addr, count)?; + view.write(&mut buf.as_slice(), addr, count)?; Ok(()) } -- Gitee From d53acc57a55172f012858f0e120384a673023245 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 23 May 2024 17:25:21 +0800 Subject: [PATCH 1891/2187] test: add `sysbus_init` function in sysbus mod Many tests require the creation of the sysbus. Use the unified `sysbus_init` function to complete this task. Signed-off-by: liuxiangdong --- devices/src/legacy/fwcfg.rs | 27 +-------------------------- devices/src/legacy/pflash.rs | 28 +--------------------------- devices/src/sysbus/mod.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 53 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 45ac6d4bc..5af3c9398 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1292,34 +1292,9 @@ impl AmlBuilder for FwCfgIO { #[cfg(test)] mod test { use super::*; - use crate::sysbus::{IRQ_BASE, IRQ_MAX}; + use crate::sysbus::sysbus_init; use address_space::{AddressSpace, HostMemMapping, Region}; - fn sysbus_init() -> SysBus { - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sys_mem"), - "sys_mem", - None, - ) - .unwrap(); - #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new( - Region::init_container_region(1 << 16, "sys_io"), - "sys_io", - None, - ) - .unwrap(); - let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); - let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); - SysBus::new( - #[cfg(target_arch = "x86_64")] - &sys_io, - &sys_mem, - free_irqs, - mmio_region, - ) - } - fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "root"); let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 6c225f67b..747d22b98 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -930,33 +930,7 @@ mod test { use std::fs::File; use super::*; - use crate::sysbus::{IRQ_BASE, IRQ_MAX}; - use address_space::AddressSpace; - - fn sysbus_init() -> SysBus { - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sys_mem"), - "sys_mem", - None, - ) - .unwrap(); - #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new( - Region::init_container_region(1 << 16, "sys_io"), - "sys_io", - None, - ) - .unwrap(); - let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); - let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); - SysBus::new( - #[cfg(target_arch = "x86_64")] - &sys_io, - &sys_mem, - free_irqs, - mmio_region, - ) - } + use crate::sysbus::sysbus_init; fn pflash_dev_init(file_name: &str) -> Arc> { let sector_len: u32 = 0x40_000; diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 95c71ec8b..f9bbeb5ab 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -419,3 +419,29 @@ pub fn to_sysbusdevops(dev: &dyn Device) -> Option<&dyn SysBusDevOps> { Some(sysbusdev) } } + +#[cfg(test)] +pub fn sysbus_init() -> SysBus { + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sys_mem"), + "sys_mem", + None, + ) + .unwrap(); + #[cfg(target_arch = "x86_64")] + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sys_io"), + "sys_io", + None, + ) + .unwrap(); + let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); + let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); + SysBus::new( + #[cfg(target_arch = "x86_64")] + &sys_io, + &sys_mem, + free_irqs, + mmio_region, + ) +} -- Gitee From 03c44e5050b5a011843cec5113cde6af5b330ffe Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 24 May 2024 01:46:52 +0800 Subject: [PATCH 1892/2187] test: add `address_space_init` function in devices/virtio crate Many tests require the creation of the address space. Use the unified `address_space_init` function to complete this task. In rust, a type under `#[cfg(test)]` exported in crate A can not be used for another test under `#[cfg(test)]` in crate B. So we can not add this function by `#[cfg(test)]` in address_space crate. Because currently only virtio and devices crates will use it, create function `address_space_init` for each crate. Signed-off-by: liuxiangdong --- devices/src/legacy/fwcfg.rs | 27 +------------------ devices/src/lib.rs | 32 ++++++++++++++++++++++ virtio/src/device/balloon.rs | 32 +++------------------- virtio/src/device/block.rs | 30 ++------------------- virtio/src/device/net.rs | 2 +- virtio/src/device/rng.rs | 30 ++------------------- virtio/src/device/serial.rs | 2 +- virtio/src/lib.rs | 34 ++++++++++++++++++++++++ virtio/src/queue/split.rs | 28 ++------------------ virtio/src/transport/virtio_mmio.rs | 41 ++++++----------------------- virtio/src/transport/virtio_pci.rs | 8 +++--- virtio/src/vhost/kernel/net.rs | 31 ++-------------------- virtio/src/vhost/kernel/vsock.rs | 10 ++----- 13 files changed, 94 insertions(+), 213 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 5af3c9398..ad3e3f3ef 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1293,32 +1293,7 @@ impl AmlBuilder for FwCfgIO { mod test { use super::*; use crate::sysbus::sysbus_init; - use address_space::{AddressSpace, HostMemMapping, Region}; - - fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "root"); - let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - 0x1000_0000, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "region_1"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } + use crate::test::address_space_init; #[test] fn test_entry_functions() { diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 0486bf1e8..491ca1d57 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -72,3 +72,35 @@ pub trait Device: Any + AsAny { self.device_base().hotpluggable } } + +#[cfg(test)] +pub mod test { + use std::sync::Arc; + + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + + pub fn address_space_init() -> Arc { + let root = Region::init_container_region(1 << 36, "root"); + let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); + let host_mmap = Arc::new( + HostMemMapping::new( + GuestAddress(0), + None, + 0x1000_0000, + None, + false, + false, + false, + ) + .unwrap(), + ); + sys_space + .root() + .add_subregion( + Region::init_ram_region(host_mmap.clone(), "region_1"), + host_mmap.start_address().raw_value(), + ) + .unwrap(); + sys_space + } +} diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 33f89d3e8..fe62211c5 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1245,39 +1245,13 @@ pub fn balloon_allow_list(syscall_allow_list: &mut Vec) { #[cfg(test)] mod tests { - pub use super::*; - pub use crate::*; - + use super::*; + use crate::tests::{address_space_init, MEMORY_SIZE}; + use crate::*; use address_space::{AddressRange, HostMemMapping, Region}; - const MEMORY_SIZE: u64 = 1024 * 1024; const QUEUE_SIZE: u16 = 256; - fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "space"); - let sys_space = AddressSpace::new(root, "space", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - MEMORY_SIZE, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "space"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } - fn create_flat_range(addr: u64, size: u64, offset_in_region: u64) -> FlatRange { let mem_mapping = Arc::new( HostMemMapping::new(GuestAddress(addr), None, size, None, false, false, false).unwrap(), diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 9b14a8800..837cfe6dc 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1446,8 +1446,9 @@ mod tests { use vmm_sys_util::tempfile::TempFile; use super::*; + use crate::tests::address_space_init; use crate::*; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + use address_space::GuestAddress; use machine_manager::config::{ str_slip_to_clap, IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }; @@ -1456,33 +1457,6 @@ mod tests { const CONFIG_SPACE_SIZE: usize = 60; const VIRTQ_DESC_F_NEXT: u16 = 0x01; const VIRTQ_DESC_F_WRITE: u16 = 0x02; - const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; - - // build dummy address space of vm - fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - SYSTEM_SPACE_SIZE, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "sysmem"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } fn init_default_block() -> Block { Block::new( diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 391a6cd0f..7b400206a 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1787,7 +1787,7 @@ impl MigrationHook for Net {} #[cfg(test)] mod tests { - pub use super::*; + use super::*; #[test] fn test_net_init() { diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 9f1007ed2..3d26ea9e2 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -419,39 +419,13 @@ mod tests { use vmm_sys_util::tempfile::TempFile; use super::*; + use crate::tests::address_space_init; use crate::*; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + use address_space::GuestAddress; use machine_manager::config::{str_slip_to_clap, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; const VIRTQ_DESC_F_NEXT: u16 = 0x01; const VIRTQ_DESC_F_WRITE: u16 = 0x02; - const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; - - // build dummy address space of vm - fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - SYSTEM_SPACE_SIZE, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "sysmem"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } #[test] fn test_rng_config_cmdline_parse() { diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 102a3ed6f..0cf424215 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -1008,7 +1008,7 @@ impl ChardevNotifyDevice for SerialPort { #[cfg(test)] mod tests { - pub use super::*; + use super::*; #[test] fn test_set_driver_features() { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 1fed7f323..9fc5cd596 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -877,3 +877,37 @@ pub fn virtio_register_sysbusdevops_type() -> Result<()> { pub fn virtio_register_pcidevops_type() -> Result<()> { register_pcidevops_type::() } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + + pub const MEMORY_SIZE: u64 = 1024 * 1024; + + pub fn address_space_init() -> Arc { + let root = Region::init_container_region(1 << 36, "root"); + let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); + let host_mmap = Arc::new( + HostMemMapping::new( + GuestAddress(0), + None, + MEMORY_SIZE, + None, + false, + false, + false, + ) + .unwrap(), + ); + sys_space + .root() + .add_subregion( + Region::init_ram_region(host_mmap.clone(), "region_1"), + host_mmap.start_address().raw_value(), + ) + .unwrap(); + sys_space + } +} diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 31b2fe306..302c258da 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -975,33 +975,9 @@ impl VringOps for SplitVring { #[cfg(test)] mod tests { use super::*; + use crate::tests::address_space_init; use crate::{Queue, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING}; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - - fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - SYSTEM_SPACE_SIZE, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "sysmem"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } + use address_space::{AddressSpace, GuestAddress}; trait VringOpsTest { fn set_desc( diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 77d0ca0a0..814fbeeed 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -594,53 +594,28 @@ impl MigrationHook for VirtioMmioDevice { } #[cfg(test)] -pub mod tests { +mod tests { use super::*; + use crate::tests::address_space_init; use crate::{ check_config_space_rw, read_config_default, VirtioBase, QUEUE_TYPE_SPLIT_VRING, VIRTIO_TYPE_BLOCK, }; - use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; - - pub fn address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - SYSTEM_SPACE_SIZE, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "sysmem"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } + use address_space::{AddressSpace, GuestAddress}; - const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; const CONFIG_SPACE_SIZE: usize = 16; const QUEUE_NUM: usize = 2; const QUEUE_SIZE: u16 = 256; - pub struct VirtioDeviceTest { + struct VirtioDeviceTest { base: VirtioBase, - pub config_space: Vec, - pub b_active: bool, - pub b_realized: bool, + config_space: Vec, + b_active: bool, + b_realized: bool, } impl VirtioDeviceTest { - pub fn new() -> Self { + fn new() -> Self { let mut config_space = Vec::new(); for i in 0..CONFIG_SPACE_SIZE { config_space.push(i as u8); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 111ae3de0..a9ba784bd 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1385,7 +1385,7 @@ mod tests { use vmm_sys_util::eventfd::EventFd; use super::*; - use crate::transport::virtio_mmio::tests::address_space_init; + use crate::tests::address_space_init; use crate::VirtioBase; use address_space::{AddressSpace, GuestAddress}; use devices::pci::{ @@ -1397,13 +1397,13 @@ mod tests { const VIRTIO_DEVICE_QUEUE_NUM: usize = 2; const VIRTIO_DEVICE_QUEUE_SIZE: u16 = 256; - pub struct VirtioDeviceTest { + struct VirtioDeviceTest { base: VirtioBase, - pub is_activated: bool, + is_activated: bool, } impl VirtioDeviceTest { - pub fn new() -> Self { + fn new() -> Self { let mut base = VirtioBase::new( VIRTIO_DEVICE_TEST_TYPE, VIRTIO_DEVICE_QUEUE_NUM, diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 89a299707..0933cef49 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -415,36 +415,9 @@ mod tests { use std::fs::File; use super::*; - use address_space::*; + use crate::tests::address_space_init; use machine_manager::config::DEFAULT_VIRTQUEUE_SIZE; - const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; - - fn vhost_address_space_init() -> Arc { - let root = Region::init_container_region(1 << 36, "sysmem"); - let sys_space = AddressSpace::new(root, "sysmem", None).unwrap(); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(0), - None, - SYSTEM_SPACE_SIZE, - None, - false, - false, - false, - ) - .unwrap(), - ); - sys_space - .root() - .add_subregion( - Region::init_ram_region(host_mmap.clone(), "sysmem"), - host_mmap.start_address().raw_value(), - ) - .unwrap(); - sys_space - } - #[test] fn test_vhost_net_realize() { let netdev_cfg1 = NetDevcfg { @@ -465,7 +438,7 @@ mod tests { queue_size: DEFAULT_VIRTQUEUE_SIZE, ..Default::default() }; - let vhost_net_space = vhost_address_space_init(); + let vhost_net_space = address_space_init(); let mut vhost_net = Net::new(&vhost_net_conf, netdev_cfg1, &vhost_net_space); // the tap_fd and vhost_fd attribute of vhost-net can't be assigned. assert!(vhost_net.realize().is_err()); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index ade3ec2ff..8c06c63dd 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -414,15 +414,9 @@ impl MigrationHook for Vsock { #[cfg(test)] mod tests { use super::*; - use address_space::*; + use crate::tests::address_space_init; use machine_manager::config::str_slip_to_clap; - fn vsock_address_space_init() -> Arc { - let root = Region::init_container_region(u64::max_value(), "sysmem"); - let sys_mem = AddressSpace::new(root, "sysmem", None).unwrap(); - sys_mem - } - fn vsock_create_instance() -> Vsock { let vsock_conf = VsockConfig { id: "test_vsock_1".to_string(), @@ -430,7 +424,7 @@ mod tests { vhost_fd: None, ..Default::default() }; - let sys_mem = vsock_address_space_init(); + let sys_mem = address_space_init(); let vsock = Vsock::new(&vsock_conf, &sys_mem); vsock } -- Gitee From 871a763a86eadc7bf5596cc2f751c8859eb5ed15 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 14 Jun 2024 15:02:52 +0800 Subject: [PATCH 1893/2187] test: use `create_pci_host` function in bus mod There exists `create_pci_host` function, delete redundant one. Signed-off-by: liuxiangdong --- devices/src/pci/bus.rs | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 797466636..3acc5250e 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -274,10 +274,10 @@ mod tests { use super::*; use crate::pci::bus::PciBus; use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; + use crate::pci::host::tests::create_pci_host; use crate::pci::root_port::RootPort; - use crate::pci::{PciDevBase, PciHost, RootPortConfig}; + use crate::pci::{PciDevBase, RootPortConfig}; use crate::{Device, DeviceBase}; - use address_space::{AddressSpace, Region}; #[derive(Clone)] struct PciDevice { @@ -339,34 +339,6 @@ mod tests { } } - pub fn create_pci_host() -> Arc> { - #[cfg(target_arch = "x86_64")] - let sys_io = AddressSpace::new( - Region::init_container_region(1 << 16, "sysio"), - "sysio", - None, - ) - .unwrap(); - let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "sysmem"), - "sysmem", - None, - ) - .unwrap(); - Arc::new(Mutex::new(PciHost::new( - #[cfg(target_arch = "x86_64")] - &sys_io, - &sys_mem, - (0xB000_0000, 0x1000_0000), - (0xC000_0000, 0x3000_0000), - #[cfg(target_arch = "aarch64")] - (0xF000_0000, 0x1000_0000), - #[cfg(target_arch = "aarch64")] - (512 << 30, 512 << 30), - 16, - ))) - } - #[test] fn test_find_attached_bus() { let pci_host = create_pci_host(); -- Gitee From 75ed42cc79e376c4757f907cef1bab2e1e7e43c4 Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Wed, 12 Jun 2024 20:54:39 +0800 Subject: [PATCH 1894/2187] update vmm-sys-util to 0.12.1 Adding bitflags v2.5.0 Updating kvm-bindings v0.6.0 -> v0.7.0 Updating kvm-ioctls v0.15.0 -> v0.16.0 Updating vmm-sys-util v0.11.1 -> v0.12.1 --- Cargo.lock | 51 +++++++++++-------- address_space/Cargo.toml | 2 +- block_backend/Cargo.toml | 2 +- boot_loader/Cargo.toml | 2 +- chardev_backend/Cargo.toml | 2 +- cpu/Cargo.toml | 4 +- cpu/src/x86_64/mod.rs | 37 ++++++++++++-- devices/Cargo.toml | 2 +- hypervisor/Cargo.toml | 6 +-- hypervisor/src/kvm/mod.rs | 2 +- ...Third_Party_Open_Source_Software_Notice.md | 8 +-- machine/Cargo.toml | 2 +- machine_manager/Cargo.toml | 2 +- migration/Cargo.toml | 2 +- tests/mod_test/Cargo.toml | 2 +- trace/Cargo.toml | 2 +- ui/Cargo.toml | 2 +- util/Cargo.toml | 4 +- util/src/byte_code.rs | 2 +- vfio/Cargo.toml | 6 +-- virtio/Cargo.toml | 2 +- 21 files changed, 90 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb50c772f..bc1a59a9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44" dependencies = [ "alsa-sys", - "bitflags", + "bitflags 1.3.2", "libc", "nix 0.24.3", ] @@ -83,7 +83,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba16453d10c712284061a05f6510f75abeb92b56ba88dfeb48c74775020cc22" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "glib", "libc", ] @@ -118,7 +118,7 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -141,6 +141,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "bitintr" version = "0.3.0" @@ -200,7 +206,7 @@ version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab3603c4028a5e368d09b51c8b624b9a46edcd7c3778284077a6125af73c9f0a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -280,7 +286,7 @@ version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "once_cell", @@ -495,7 +501,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1df5ea52cccd7e3a0897338b5564968274b52f5fd12601e0afa44f454c74d3" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -511,7 +517,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b023fbe0c6b407bd3d9805d107d9800da3829dc5a676653210f1d5f16d7f59bf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -586,7 +592,7 @@ version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d14522e56c6bcb6f7a3aebc25cbcfb06776af4c0c25232b601b4383252d7cb92" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -619,7 +625,7 @@ version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f1de7cbde31ea4f0a919453a2dcece5d54d5b70e08f8ad254dc4840f5f09b6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -685,7 +691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c4222ab92b08d4d0bab90ddb6185b4e575ceeea8b8cdf00b938d7b6661d966" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -788,7 +794,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7b36074613a723279637061b40db993208908a94f10ccb14436ce735bc0f57" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] @@ -809,19 +815,20 @@ dependencies = [ [[package]] name = "kvm-bindings" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" +checksum = "081fbd8164229a990fbf24a1f35d287740db110c2b5d42addf460165f1b0e032" dependencies = [ "vmm-sys-util", ] [[package]] name = "kvm-ioctls" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bdde2b46ee7b6587ef79f751019c4726c4f2d3e4628df5d69f3f9c5cb6c6bd4" +checksum = "9002dff009755414f22b962ec6ae6980b07d6d8b06e5297b1062019d72bd6a8c" dependencies = [ + "bitflags 2.5.0", "kvm-bindings", "libc", "vmm-sys-util", @@ -861,7 +868,7 @@ version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libpulse-sys", "num-derive", @@ -1075,7 +1082,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", ] @@ -1086,7 +1093,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.7.1", @@ -1192,7 +1199,7 @@ version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52c280b82a881e4208afb3359a8e7fde27a1b272280981f1f34610bed5770d37" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", "glib", "libc", @@ -1905,11 +1912,11 @@ dependencies = [ [[package]] name = "vmm-sys-util" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" +checksum = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] diff --git a/address_space/Cargo.toml b/address_space/Cargo.toml index 3c1a6e144..c77820d3f 100644 --- a/address_space/Cargo.toml +++ b/address_space/Cargo.toml @@ -10,7 +10,7 @@ description = "provide memory management for VM" libc = "0.2" log = "0.4" nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" diff --git a/block_backend/Cargo.toml b/block_backend/Cargo.toml index 6f7c45b3d..d052bd0d5 100644 --- a/block_backend/Cargo.toml +++ b/block_backend/Cargo.toml @@ -7,7 +7,7 @@ license = "Mulan PSL v2" [dependencies] thiserror = "1.0" -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.12.1" anyhow = "1.0" log = "0.4" byteorder = "1.4.3" diff --git a/boot_loader/Cargo.toml b/boot_loader/Cargo.toml index d04c4ceaf..e2c9c45f6 100644 --- a/boot_loader/Cargo.toml +++ b/boot_loader/Cargo.toml @@ -8,7 +8,7 @@ license = "Mulan PSL v2" [dependencies] thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } log = "0.4" address_space = { path = "../address_space" } devices = { path = "../devices" } diff --git a/chardev_backend/Cargo.toml b/chardev_backend/Cargo.toml index 83b47b337..8a1b15358 100644 --- a/chardev_backend/Cargo.toml +++ b/chardev_backend/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Mulan PSL v2" [dependencies] -vmm-sys-util = "0.11.0" +vmm-sys-util = "0.12.1" anyhow = "1.0" log = "0.4" libc = "0.2" diff --git a/cpu/Cargo.toml b/cpu/Cargo.toml index a665edecb..57e4d7e26 100644 --- a/cpu/Cargo.toml +++ b/cpu/Cargo.toml @@ -9,11 +9,11 @@ description = "CPU emulation" [dependencies] thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["fs", "feature"] } log = "0.4" libc = "0.2" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" machine_manager = { path = "../machine_manager" } migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 0a8ad1690..088d0eb0d 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -120,7 +120,7 @@ impl X86CPUTopology { /// The state of vCPU's register. #[allow(clippy::upper_case_acronyms)] #[repr(C)] -#[derive(Copy, Clone, Desc, ByteCode)] +#[derive(Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] pub struct X86CPUState { max_vcpus: u32, @@ -142,6 +142,34 @@ pub struct X86CPUState { pub debugregs: DebugRegs, } +impl Clone for X86CPUState { + fn clone(&self) -> Self { + let mut xsave: Xsave = Default::default(); + // we just clone xsave.region, because xsave.extra does not save + // valid values and it is not allowed to be cloned. + xsave.region = self.xsave.region; + Self { + max_vcpus: self.max_vcpus, + nr_threads: self.nr_threads, + nr_cores: self.nr_cores, + nr_dies: self.nr_dies, + nr_sockets: self.nr_sockets, + apic_id: self.apic_id, + regs: self.regs, + sregs: self.sregs, + fpu: self.fpu, + mp_state: self.mp_state, + lapic: self.lapic, + msr_len: self.msr_len, + msr_list: self.msr_list, + cpu_events: self.cpu_events, + xsave, + xcrs: self.xcrs, + debugregs: self.debugregs, + } + } +} + impl X86CPUState { /// Allocates a new `X86CPUState`. /// @@ -181,7 +209,8 @@ impl X86CPUState { self.msr_len = locked_cpu_state.msr_len; self.msr_list = locked_cpu_state.msr_list; self.cpu_events = locked_cpu_state.cpu_events; - self.xsave = locked_cpu_state.xsave; + self.xsave = Default::default(); + self.xsave.region = locked_cpu_state.xsave.region; self.xcrs = locked_cpu_state.xcrs; self.debugregs = locked_cpu_state.debugregs; } @@ -512,11 +541,11 @@ impl StateTransfer for CPU { } fn set_state(&self, state: &[u8]) -> Result<()> { - let cpu_state = *X86CPUState::from_bytes(state) + let cpu_state = X86CPUState::from_bytes(state) .with_context(|| MigrationError::FromBytesError("CPU"))?; let mut cpu_state_locked = self.arch_cpu.lock().unwrap(); - *cpu_state_locked = cpu_state; + *cpu_state_locked = cpu_state.clone(); Ok(()) } diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 00181b49c..fec9d5c13 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4" serde = { version = "1.0", features = ["derive"] } strum = "0.24.1" strum_macros = "0.24.3" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" byteorder = "1.4.3" drm-fourcc = ">=2.2.0" once_cell = "1.18.0" diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index df70a5b23..ccdf1305e 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -8,11 +8,11 @@ license = "Mulan PSL v2" [dependencies] anyhow = "1.0" thiserror = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.15.0" +kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.16.0" libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" address_space = { path = "../address_space" } cpu = { path = "../cpu" } devices = { path = "../devices" } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 3e03a2a95..6689e516f 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -466,7 +466,7 @@ impl KvmCpu { return Ok(true); } else { error!( - "Vcpu{} received unexpected system event with type 0x{:x}, flags 0x{:x}", + "Vcpu{} received unexpected system event with type 0x{:x}, flags {:#x?}", cpu.id(), event, flags diff --git a/license/Third_Party_Open_Source_Software_Notice.md b/license/Third_Party_Open_Source_Software_Notice.md index 71a4f7778..721534eaf 100644 --- a/license/Third_Party_Open_Source_Software_Notice.md +++ b/license/Third_Party_Open_Source_Software_Notice.md @@ -308,7 +308,7 @@ Copyright (c) David Tolnay License: MIT or Apache License Version 2.0 Please see above. -Software: vmm-sys-util 0.11.1 +Software: vmm-sys-util 0.12.1 Copyright notice: Copyright 2019 Intel Corporation. All Rights Reserved. Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -318,7 +318,7 @@ Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. Copyright 2018 The Chromium OS Authors. All rights reserved. Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -License: Apache License Version 2.0 or BSD 3-Clause +License: BSD 3-Clause Please see above. Software: libusb1-sys 0.6.4 @@ -356,7 +356,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Software: kvm-ioctls 0.13.0 +Software: kvm-ioctls 0.16.0 Copyright notice: Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. Portions Copyright 2017 The Chromium OS Authors. All rights reserved. @@ -365,7 +365,7 @@ Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: MIT or Apache License Version 2.0 Please see above. -Software: kvm-bindings 0.6.0 +Software: kvm-bindings 0.7.0 Copyright notice: Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. License: The APACHE 2.0 License diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 2f677f8c1..e5d9253ab 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -10,7 +10,7 @@ description = "Emulation machines" log = "0.4" libc = "0.2" serde_json = "1.0" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" thiserror = "1.0" anyhow = "1.0" acpi = { path = "../acpi" } diff --git a/machine_manager/Cargo.toml b/machine_manager/Cargo.toml index 29e151bf7..ec787bc51 100644 --- a/machine_manager/Cargo.toml +++ b/machine_manager/Cargo.toml @@ -12,7 +12,7 @@ regex = "1" log = "0.4" libc = "0.2" serde_json = "1.0" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" hex = "0.4.3" serde = { version = "1.0", features = ["derive"] } strum = "0.24.1" diff --git a/migration/Cargo.toml b/migration/Cargo.toml index e1469d9ff..f0d10681b 100644 --- a/migration/Cargo.toml +++ b/migration/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Huawei StratoVirt Team"] edition = "2021" [dependencies] -kvm-ioctls = "0.15.0" +kvm-ioctls = "0.16.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" once_cell = "1.18.0" diff --git a/tests/mod_test/Cargo.toml b/tests/mod_test/Cargo.toml index 9ccebc058..9b144af1d 100644 --- a/tests/mod_test/Cargo.toml +++ b/tests/mod_test/Cargo.toml @@ -8,7 +8,7 @@ license = "Mulan PSL v2" [dependencies] rand = "0.8.5" hex = "0.4.3" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" anyhow = "1.0" serde_json = "1.0" libc = "0.2" diff --git a/trace/Cargo.toml b/trace/Cargo.toml index 267a35661..ad6cf1bcf 100644 --- a/trace/Cargo.toml +++ b/trace/Cargo.toml @@ -12,7 +12,7 @@ lazy_static = "1.4.0" regex = "1" anyhow = "1.0" trace_generator = { path = "trace_generator" } -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" [features] trace_to_logger = [] diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 56f2b6f67..b7faa4240 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -12,7 +12,7 @@ anyhow = "1.0" libc = "0.2" log = "0.4" serde_json = "1.0" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" once_cell = "1.18.0" sscanf = "0.4.1" bitintr = "0.3.0" diff --git a/util/Cargo.toml b/util/Cargo.toml index 8d8a38ec9..806fe935e 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -11,12 +11,12 @@ license = "Mulan PSL v2" arc-swap = "1.6.0" thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } +kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } nix = { version = "0.26.2", default-features = false, features = ["poll", "term", "time", "signal", "fs", "feature"] } libc = "0.2" libloading = "0.7.4" log = { version = "0.4", features = ["std"]} -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" byteorder = "1.4.3" once_cell = "1.18.0" io-uring = "0.6.0" diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 64fe6c9ac..5c45ade09 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -15,7 +15,7 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// A trait bound defined for types which are safe to convert to a byte slice and /// to create from a byte slice. -pub trait ByteCode: Default + Copy + Send + Sync { +pub trait ByteCode: Default + Send + Sync { /// Return the contents of an object (impl trait `ByteCode`) as a slice of bytes. /// the inverse of this function is "from_bytes" fn as_bytes(&self) -> &[u8] { diff --git a/vfio/Cargo.toml b/vfio/Cargo.toml index 94d7b5e4b..6b3e135ad 100644 --- a/vfio/Cargo.toml +++ b/vfio/Cargo.toml @@ -10,11 +10,11 @@ description = "Virtual function I/O" byteorder = "1.4.3" thiserror = "1.0" anyhow = "1.0" -kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } -kvm-ioctls = "0.15.0" +kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] } +kvm-ioctls = "0.16.0" libc = "0.2" log = "0.4" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" vfio-bindings = "0.3" once_cell = "1.18.0" address_space = { path = "../address_space" } diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index d2890ed09..91c89cd05 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -13,7 +13,7 @@ anyhow = "1.0" libc = "0.2" log = "0.4" serde_json = "1.0" -vmm-sys-util = "0.11.1" +vmm-sys-util = "0.12.1" once_cell = "1.18.0" address_space = { path = "../address_space" } machine_manager = { path = "../machine_manager" } -- Gitee From b84bfda8d37b6cb72e4b611a80d429a72cffe6e3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 13 Jun 2024 06:28:21 +0800 Subject: [PATCH 1895/2187] virtio-net: add configuration options for virtio-net By this configuration, the sending and receiving tasks will be execute on rx_iothread and tx_iothread separately, thereby improving the performance of virtio-net. the example is as follows: -object iothread,id=rx-iothread \ -object iothread,id=tx-iothread \ -device virtio-net-pci,netdev=net0,XXX,rx-iothread=rx-iothread,tx-iothread=tx-iothread \ -netdev tap,id=net0,ifname=VMEngineTap\ Signed-off-by: Xiao Ye --- machine/src/lib.rs | 3 ++- machine/src/micro_common/mod.rs | 6 ++++-- machine_manager/src/config/network.rs | 18 ++++++++++++++++++ virtio/src/device/net.rs | 12 ++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6b9a80a6c..19d4ba6c0 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1257,8 +1257,9 @@ pub trait MachineOps: MachineLifecycle { cfg_args: &str, hotplug: bool, ) -> Result<()> { - let net_cfg = + let mut net_cfg = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + net_cfg.auto_iothread(); let netdev_cfg = vm_config .netdevs .remove(&net_cfg.netdev) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index e7d08ae6e..076af9c55 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -302,7 +302,7 @@ impl LightMachine { if cfg_any.downcast_ref::().is_none() { return Err(anyhow!(MachineError::DevTypeErr("net".to_string()))); } - let net_config = NetworkInterfaceConfig { + let mut net_config = NetworkInterfaceConfig { classtype: driver, id: id.clone(), netdev: args.chardev.with_context(|| "No chardev set")?, @@ -310,6 +310,7 @@ impl LightMachine { iothread: args.iothread, ..Default::default() }; + net_config.auto_iothread(); configs.push(Arc::new(net_config)); slot + MMIO_REPLACEABLE_BLK_NR } else if driver.contains("blk") { @@ -399,8 +400,9 @@ impl LightMachine { vm_config: &mut VmConfig, cfg_args: &str, ) -> Result<()> { - let net_cfg = + let mut net_cfg = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; + net_cfg.auto_iothread(); check_arg_nonexist!( ("bus", net_cfg.bus), ("addr", net_cfg.addr), diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index c9e58976a..9f2030107 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -151,6 +151,10 @@ pub struct NetworkInterfaceConfig { pub mac: Option, #[arg(long)] pub iothread: Option, + #[arg(long)] + pub rx_iothread: Option, + #[arg(long)] + pub tx_iothread: Option, #[arg(long, default_value="off", value_parser = parse_bool, action = ArgAction::Append)] pub mq: bool, // All queues of a net device have the same queue size now. @@ -172,6 +176,8 @@ impl Default for NetworkInterfaceConfig { multifunction: None, mac: None, iothread: None, + rx_iothread: None, + tx_iothread: None, mq: false, queue_size: DEFAULT_VIRTQUEUE_SIZE, vectors: 0, @@ -179,6 +185,18 @@ impl Default for NetworkInterfaceConfig { } } +impl NetworkInterfaceConfig { + pub fn auto_iothread(&mut self) { + // If rx_iothread or tx_iothread is not configured, the default iothread will be used. + if self.rx_iothread.is_none() { + self.rx_iothread = self.iothread.clone(); + } + if self.tx_iothread.is_none() { + self.tx_iothread = self.iothread.clone(); + } + } +} + fn valid_network_queue_size(s: &str) -> Result { let size: u64 = s.parse()?; valid_virtqueue_size(size, DEFAULT_VIRTQUEUE_SIZE as u64, MAX_QUEUE_SIZE_NET)?; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index ccf5e6c49..65adb9524 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1246,6 +1246,10 @@ pub struct Net { update_evts: Vec>, /// The information about control command. ctrl_info: Option>>, + /// The deactivate events for receiving. + rx_deactivate_evts: Vec, + /// The deactivate events for transporting. + tx_deactivate_evts: Vec, } impl Net { @@ -1699,6 +1703,14 @@ impl VirtioDevice for Net { self.net_cfg.iothread.as_ref(), &mut self.base.deactivate_evts, )?; + unregister_event_helper( + self.net_cfg.rx_iothread.as_ref(), + &mut self.rx_deactivate_evts, + )?; + unregister_event_helper( + self.net_cfg.tx_iothread.as_ref(), + &mut self.tx_deactivate_evts, + )?; self.update_evts.clear(); self.ctrl_info = None; Ok(()) -- Gitee From 00cac2dd75da0fd310cc225bdd33547bcb8f4dc3 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 13 Jun 2024 21:17:09 +0800 Subject: [PATCH 1896/2187] virtio-net: remove the tap_fd 1. Remove the tap_fd from NetIoHandler, as it is equal with Tap. 2. Move the function of send_packets adn read_from_tap to tap.rs, and rename the function of read_from_tap with receive_packets. Signed-off-by: Xiao Ye --- util/src/tap.rs | 64 ++++++++++++++++++++++++++-- virtio/src/device/net.rs | 91 +++++++--------------------------------- 2 files changed, 75 insertions(+), 80 deletions(-) diff --git a/util/src/tap.rs b/util/src/tap.rs index d59e20a22..1c8039cfc 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::fs::{File, OpenOptions}; -use std::io::{Read, Result as IoResult, Write}; +use std::io::{ErrorKind, Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; @@ -22,6 +22,8 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag}; use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val}; use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr}; +use crate::aio::Iovec; + const IFF_ATTACH_QUEUE: u16 = 0x0200; const IFF_DETACH_QUEUE: u16 = 0x0400; @@ -185,11 +187,67 @@ impl Tap { ret } - pub fn read(&mut self, buf: &mut [u8]) -> IoResult { + pub fn receive_packets(&self, iovecs: &[Iovec]) -> i32 { + // SAFETY: the arguments of readv has been checked and is correct. + let size = unsafe { + libc::readv( + self.as_raw_fd() as libc::c_int, + iovecs.as_ptr() as *const libc::iovec, + iovecs.len() as libc::c_int, + ) + } as i32; + if size < 0 { + let e = std::io::Error::last_os_error(); + if e.kind() == std::io::ErrorKind::WouldBlock { + return size; + } + + // If the backend tap device is removed, readv returns less than 0. + // At this time, the content in the tap needs to be cleaned up. + // Here, read is called to process, otherwise handle_rx may be triggered all the time. + let mut buf = [0; 1024]; + match self.read(&mut buf) { + Ok(cnt) => error!("Failed to call readv but tap read is ok: cnt {}", cnt), + Err(e) => { + // When the backend tap device is abnormally removed, read return EBADFD. + error!("Failed to read tap: {:?}", e); + } + } + error!("Failed to call readv for net handle_rx: {:?}", e); + } + + size + } + + pub fn send_packets(&self, iovecs: &[Iovec]) -> i8 { + loop { + // SAFETY: the arguments of writev has been checked and is correct. + let size = unsafe { + libc::writev( + self.as_raw_fd(), + iovecs.as_ptr() as *const libc::iovec, + iovecs.len() as libc::c_int, + ) + }; + if size < 0 { + let e = std::io::Error::last_os_error(); + match e.kind() { + ErrorKind::Interrupted => continue, + ErrorKind::WouldBlock => return -1_i8, + // Ignore other errors which can not be handled. + _ => error!("Failed to call writev for net handle_tx: {:?}", e), + } + } + break; + } + 0_i8 + } + + pub fn read(&self, buf: &mut [u8]) -> IoResult { self.file.as_ref().read(buf) } - pub fn write(&mut self, buf: &[u8]) -> IoResult { + pub fn write(&self, buf: &[u8]) -> IoResult { self.file.as_ref().write(buf) } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 65adb9524..74a498f2b 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::io::ErrorKind; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::rc::Rc; @@ -706,7 +705,6 @@ struct NetIoHandler { rx: RxVirtio, tx: TxVirtio, tap: Option, - tap_fd: RawFd, mem_space: Arc, interrupt_cb: Arc, driver_features: u64, @@ -719,38 +717,6 @@ struct NetIoHandler { } impl NetIoHandler { - fn read_from_tap(iovecs: &[Iovec], tap: &mut Tap) -> i32 { - // SAFETY: the arguments of readv has been checked and is correct. - let size = unsafe { - libc::readv( - tap.as_raw_fd() as libc::c_int, - iovecs.as_ptr() as *const libc::iovec, - iovecs.len() as libc::c_int, - ) - } as i32; - if size < 0 { - let e = std::io::Error::last_os_error(); - if e.kind() == std::io::ErrorKind::WouldBlock { - return size; - } - - // If the backend tap device is removed, readv returns less than 0. - // At this time, the content in the tap needs to be cleaned up. - // Here, read is called to process, otherwise handle_rx may be triggered all the time. - let mut buf = [0; 1024]; - match tap.read(&mut buf) { - Ok(cnt) => error!("Failed to call readv but tap read is ok: cnt {}", cnt), - Err(e) => { - // When the backend tap device is abnormally removed, read return EBADFD. - error!("Failed to read tap: {:?}", e); - } - } - error!("Failed to call readv for net handle_rx: {:?}", e); - } - - size - } - fn handle_rx(&mut self) -> Result<()> { trace::virtio_receive_request("Net".to_string(), "to rx".to_string()); if self.tap.is_none() { @@ -786,7 +752,7 @@ impl NetIoHandler { } // Read the data from the tap device. - let size = NetIoHandler::read_from_tap(&iovecs, self.tap.as_mut().unwrap()); + let size = self.tap.as_ref().unwrap().receive_packets(&iovecs); if size < (NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH) as i32 { queue.vring.push_back(); break; @@ -847,30 +813,6 @@ impl NetIoHandler { Ok(()) } - fn send_packets(&self, tap_fd: libc::c_int, iovecs: &[Iovec]) -> i8 { - loop { - // SAFETY: the arguments of writev has been checked and is correct. - let size = unsafe { - libc::writev( - tap_fd, - iovecs.as_ptr() as *const libc::iovec, - iovecs.len() as libc::c_int, - ) - }; - if size < 0 { - let e = std::io::Error::last_os_error(); - match e.kind() { - ErrorKind::Interrupted => continue, - ErrorKind::WouldBlock => return -1_i8, - // Ignore other errors which can not be handled. - _ => error!("Failed to call writev for net handle_tx: {:?}", e), - } - } - break; - } - 0_i8 - } - fn handle_tx(&mut self) -> Result<()> { trace::virtio_receive_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); @@ -889,12 +831,7 @@ impl NetIoHandler { let (_, iovecs) = gpa_hva_iovec_map(&elem.out_iovec, &self.mem_space, queue.vring.get_cache())?; - let tap_fd = if let Some(tap) = self.tap.as_mut() { - tap.as_raw_fd() as libc::c_int - } else { - -1_i32 - }; - if tap_fd != -1 && self.send_packets(tap_fd, &iovecs) == -1 { + if self.tap.is_none() || self.tap.as_ref().unwrap().send_packets(&iovecs) == -1 { queue.vring.push_back(); queue .vring @@ -934,11 +871,15 @@ impl NetIoHandler { fn tap_fd_handler(net_io: &mut Self) -> Vec { let mut notifiers = Vec::new(); + if net_io.tap.is_none() { + return notifiers; + } + let tap_fd = net_io.tap.as_ref().unwrap().as_raw_fd(); if !net_io.is_listening && (net_io.rx.queue_avail || net_io.tx.tap_full) { notifiers.push(EventNotifier::new( NotifierOperation::Resume, - net_io.tap_fd, + tap_fd, None, EventSet::empty(), Vec::new(), @@ -968,7 +909,7 @@ impl NetIoHandler { notifiers.push(EventNotifier::new( tap_operation, - net_io.tap_fd, + tap_fd, None, tap_events, Vec::new(), @@ -978,6 +919,11 @@ impl NetIoHandler { fn update_evt_handler(net_io: &Arc>) -> Vec { let mut locked_net_io = net_io.lock().unwrap(); + let old_tap_fd = if locked_net_io.tap.is_some() { + locked_net_io.tap.as_ref().unwrap().as_raw_fd() + } else { + -1 + }; locked_net_io.tap = match locked_net_io.receiver.recv() { Ok(tap) => tap, Err(e) => { @@ -985,11 +931,6 @@ impl NetIoHandler { None } }; - let old_tap_fd = locked_net_io.tap_fd; - locked_net_io.tap_fd = -1; - if let Some(tap) = locked_net_io.tap.as_ref() { - locked_net_io.tap_fd = tap.as_raw_fd(); - } let mut notifiers_fds = vec![ locked_net_io.update_evt.as_raw_fd(), @@ -1607,11 +1548,10 @@ impl VirtioDevice for Net { } let update_evt = Arc::new(create_new_eventfd()?); - let mut handler = NetIoHandler { + let handler = NetIoHandler { rx: RxVirtio::new(rx_queue, rx_queue_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), tap: self.taps.as_ref().map(|t| t[index].clone()), - tap_fd: -1, mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features, @@ -1622,9 +1562,6 @@ impl VirtioDevice for Net { ctrl_info: ctrl_info.clone(), queue_size: self.queue_size_max(), }; - if let Some(tap) = &handler.tap { - handler.tap_fd = tap.as_raw_fd(); - } let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); register_event_helper( -- Gitee From 518bd7cfa5d03f3a3c47663be7c37bcaf2919203 Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 13 Jun 2024 07:40:34 +0800 Subject: [PATCH 1897/2187] virtio-net: set virtio-net's handlers listened on different threads Set virtio-net's handlers listened on different threads: 1. iothread: update_evt_fd and CtrlVirtio queue. 2. rx_iothread: tap_fd and RxVirtio queue. 3. tx_iothread: TxVirtio queue. Signed-off-by: Xiao Ye --- virtio/src/device/net.rs | 501 +++++++++++++++++++++++---------------- 1 file changed, 302 insertions(+), 199 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 74a498f2b..ef952304b 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -16,7 +16,7 @@ use std::path::Path; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::{cmp, fs, mem}; use anyhow::{bail, Context, Result}; @@ -54,8 +54,8 @@ use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::gen_base_func; use util::loop_context::{ - create_new_eventfd, gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, - NotifierCallback, NotifierOperation, + create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, + NotifierOperation, }; use util::num_ops::str_to_num; use util::tap::{ @@ -669,57 +669,35 @@ impl EventNotifierHelper for NetCtrlHandler { } } -struct TxVirtio { - tap_full: bool, +struct RTxVirtio { queue: Arc>, queue_evt: Arc, } -impl TxVirtio { +impl RTxVirtio { fn new(queue: Arc>, queue_evt: Arc) -> Self { - TxVirtio { - tap_full: false, - queue, - queue_evt, - } + TxVirtio { queue, queue_evt } } } -struct RxVirtio { - queue_avail: bool, - queue: Arc>, - queue_evt: Arc, -} - -impl RxVirtio { - fn new(queue: Arc>, queue_evt: Arc) -> Self { - RxVirtio { - queue_avail: true, - queue, - queue_evt, - } - } -} +type RxVirtio = RTxVirtio; +type TxVirtio = RTxVirtio; -struct NetIoHandler { +struct NetIoQueue { rx: RxVirtio, tx: TxVirtio, - tap: Option, + ctrl_info: Arc>, mem_space: Arc, interrupt_cb: Arc, + listen_state: Arc>, driver_features: u64, - receiver: Receiver, - update_evt: Arc, - device_broken: Arc, - is_listening: bool, - ctrl_info: Arc>, queue_size: u16, } -impl NetIoHandler { - fn handle_rx(&mut self) -> Result<()> { +impl NetIoQueue { + fn handle_rx(&self, tap: &Arc>>) -> Result<()> { trace::virtio_receive_request("Net".to_string(), "to rx".to_string()); - if self.tap.is_none() { + if tap.read().unwrap().is_none() { return Ok(()); } @@ -735,7 +713,7 @@ impl NetIoHandler { .vring .suppress_queue_notify(&self.mem_space, self.driver_features, false) .with_context(|| "Failed to enable rx queue notify")?; - self.rx.queue_avail = false; + self.listen_state.lock().unwrap().set_queue_avail(false); break; } else if elem.in_iovec.is_empty() { bail!("The length of in iovec is 0"); @@ -752,7 +730,13 @@ impl NetIoHandler { } // Read the data from the tap device. - let size = self.tap.as_ref().unwrap().receive_packets(&iovecs); + let locked_tap = tap.read().unwrap(); + let size = if locked_tap.is_some() { + locked_tap.as_ref().unwrap().receive_packets(&iovecs) + } else { + -1 + }; + drop(locked_tap); if size < (NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH) as i32 { queue.vring.push_back(); break; @@ -813,7 +797,7 @@ impl NetIoHandler { Ok(()) } - fn handle_tx(&mut self) -> Result<()> { + fn handle_tx(&self, tap: &Arc>>) -> Result<()> { trace::virtio_receive_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); @@ -831,15 +815,17 @@ impl NetIoHandler { let (_, iovecs) = gpa_hva_iovec_map(&elem.out_iovec, &self.mem_space, queue.vring.get_cache())?; - if self.tap.is_none() || self.tap.as_ref().unwrap().send_packets(&iovecs) == -1 { + let locked_tap = tap.read().unwrap(); + if locked_tap.is_none() || locked_tap.as_ref().unwrap().send_packets(&iovecs) == -1 { queue.vring.push_back(); queue .vring .suppress_queue_notify(&self.mem_space, self.driver_features, true) .with_context(|| "Failed to suppress tx queue notify")?; - self.tx.tap_full = true; + self.listen_state.lock().unwrap().set_tap_full(true); break; } + drop(locked_tap); queue .vring @@ -868,32 +854,62 @@ impl NetIoHandler { Ok(()) } +} - fn tap_fd_handler(net_io: &mut Self) -> Vec { - let mut notifiers = Vec::new(); - if net_io.tap.is_none() { - return notifiers; +struct ListenState { + queue_avail: bool, + tap_full: bool, + is_listening: bool, + has_changed: bool, +} + +impl ListenState { + fn new() -> Self { + Self { + queue_avail: true, + tap_full: false, + is_listening: true, + has_changed: false, + } + } + + fn set_tap_full(&mut self, value: bool) { + if self.tap_full == value { + return; } - let tap_fd = net_io.tap.as_ref().unwrap().as_raw_fd(); + self.tap_full = value; + self.has_changed = true; + } + + fn set_queue_avail(&mut self, value: bool) { + if self.queue_avail == value { + return; + } + self.queue_avail = value; + self.has_changed = true; + } + + fn tap_fd_handler(&mut self, tap: &Tap) -> Vec { + let mut notifiers = Vec::new(); - if !net_io.is_listening && (net_io.rx.queue_avail || net_io.tx.tap_full) { + if !self.is_listening && (self.queue_avail || self.tap_full) { notifiers.push(EventNotifier::new( NotifierOperation::Resume, - tap_fd, + tap.as_raw_fd(), None, EventSet::empty(), Vec::new(), )); - net_io.is_listening = true; + self.is_listening = true; } - if !net_io.is_listening { + if !self.is_listening { return notifiers; } // NOTE: We want to poll for OUT event when the tap is full, and for IN event when the // virtio queue is available. - let tap_events = match (net_io.rx.queue_avail, net_io.tx.tap_full) { + let tap_events = match (self.queue_avail, self.tap_full) { (true, true) => EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, (false, true) => EventSet::OUT | EventSet::EDGE_TRIGGERED, (true, false) => EventSet::IN | EventSet::EDGE_TRIGGERED, @@ -901,7 +917,7 @@ impl NetIoHandler { }; let tap_operation = if tap_events.is_empty() { - net_io.is_listening = false; + self.is_listening = false; NotifierOperation::Park } else { NotifierOperation::Modify @@ -909,43 +925,13 @@ impl NetIoHandler { notifiers.push(EventNotifier::new( tap_operation, - tap_fd, + tap.as_raw_fd(), None, tap_events, Vec::new(), )); notifiers } - - fn update_evt_handler(net_io: &Arc>) -> Vec { - let mut locked_net_io = net_io.lock().unwrap(); - let old_tap_fd = if locked_net_io.tap.is_some() { - locked_net_io.tap.as_ref().unwrap().as_raw_fd() - } else { - -1 - }; - locked_net_io.tap = match locked_net_io.receiver.recv() { - Ok(tap) => tap, - Err(e) => { - error!("Failed to receive the tap {:?}", e); - None - } - }; - - let mut notifiers_fds = vec![ - locked_net_io.update_evt.as_raw_fd(), - locked_net_io.rx.queue_evt.as_raw_fd(), - locked_net_io.tx.queue_evt.as_raw_fd(), - ]; - if old_tap_fd != -1 { - notifiers_fds.push(old_tap_fd); - } - let mut notifiers = gen_delete_notifiers(¬ifiers_fds); - drop(locked_net_io); - - notifiers.append(&mut EventNotifierHelper::internal_notifiers(net_io.clone())); - notifiers - } } fn get_net_header(iovec: &[Iovec], buf: &mut [u8]) -> Result { @@ -979,175 +965,267 @@ fn build_event_notifier( EventNotifier::new(op, fd, None, event, handlers) } -impl EventNotifierHelper for NetIoHandler { - fn internal_notifiers(net_io: Arc>) -> Vec { - // Register event notifier for update_evt. - let locked_net_io = net_io.lock().unwrap(); - let cloned_net_io = net_io.clone(); +struct NetIoHandler { + /// The context name of iothread for tap and rx virtio queue. + /// Since we placed the handlers of RxVirtio, TxVirtio and tap_fd in different threads, + /// thread name is needed to change the monitoring status of tap_fd. + rx_iothread: Option, + /// Virtio queue used for net io. + net_queue: Arc, + /// The context of tap device. + tap: Arc>>, + /// Device is broken or not. + device_broken: Arc, + /// The receiver half of Rust's channel to recv tap information. + receiver: Receiver, + /// Eventfd for config space update. + update_evt: Arc, +} + +impl NetIoHandler { + fn update_evt_handler(&mut self) -> Result<()> { + let mut locked_tap = self.tap.write().unwrap(); + let old_tap_fd = if locked_tap.is_some() { + locked_tap.as_ref().unwrap().as_raw_fd() + } else { + -1 + }; + + *locked_tap = match self.receiver.recv() { + Ok(tap) => tap, + Err(e) => { + error!("Failed to receive the tap {:?}", e); + None + } + }; + drop(locked_tap); + + if old_tap_fd != -1 { + unregister_event_helper(self.rx_iothread.as_ref(), &mut vec![old_tap_fd])?; + } + if self.tap.read().unwrap().is_some() { + EventLoop::update_event(self.tap_notifier(), self.rx_iothread.as_ref())?; + } + Ok(()) + } + + /// Register event notifier for update_evt. + fn update_evt_notifier(&self, net_io: Arc>) -> Vec { + let device_broken = self.device_broken.clone(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - if cloned_net_io - .lock() - .unwrap() - .device_broken - .load(Ordering::SeqCst) - { + + if device_broken.load(Ordering::SeqCst) { return None; } - Some(NetIoHandler::update_evt_handler(&cloned_net_io)) + + if let Err(e) = net_io.lock().unwrap().update_evt_handler() { + error!("Update net events failed: {:?}", e); + } + + None }); - let mut notifiers = vec![build_event_notifier( - locked_net_io.update_evt.as_raw_fd(), + let notifiers = vec![build_event_notifier( + self.update_evt.as_raw_fd(), Some(handler), NotifierOperation::AddShared, EventSet::IN, )]; + notifiers + } - // Register event notifier for rx. - let cloned_net_io = net_io.clone(); + /// Register event notifier for rx. + fn rx_virtio_notifier(&self) -> Vec { + let net_queue = self.net_queue.clone(); + let device_broken = self.device_broken.clone(); + let tap = self.tap.clone(); + let rx_iothread = self.rx_iothread.as_ref().cloned(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - let mut locked_net_io = cloned_net_io.lock().unwrap(); - if locked_net_io.device_broken.load(Ordering::SeqCst) { + + if device_broken.load(Ordering::SeqCst) { return None; } - locked_net_io.rx.queue_avail = true; - let mut locked_queue = locked_net_io.rx.queue.lock().unwrap(); + net_queue.listen_state.lock().unwrap().set_queue_avail(true); + let mut locked_queue = net_queue.rx.queue.lock().unwrap(); if let Err(ref err) = locked_queue.vring.suppress_queue_notify( - &locked_net_io.mem_space, - locked_net_io.driver_features, + &net_queue.mem_space, + net_queue.driver_features, true, ) { error!("Failed to suppress rx queue notify: {:?}", err); report_virtio_error( - locked_net_io.interrupt_cb.clone(), - locked_net_io.driver_features, - &locked_net_io.device_broken, + net_queue.interrupt_cb.clone(), + net_queue.driver_features, + &device_broken, ); return None; }; drop(locked_queue); - if let Err(ref err) = locked_net_io.handle_rx() { + if let Err(ref err) = net_queue.handle_rx(&tap) { error!("Failed to handle receive queue event: {:?}", err); report_virtio_error( - locked_net_io.interrupt_cb.clone(), - locked_net_io.driver_features, - &locked_net_io.device_broken, + net_queue.interrupt_cb.clone(), + net_queue.driver_features, + &device_broken, ); return None; } - if locked_net_io.tap.is_some() { - Some(NetIoHandler::tap_fd_handler(&mut locked_net_io)) - } else { - None + let mut locked_listen = net_queue.listen_state.lock().unwrap(); + let locked_tap = tap.read().unwrap(); + if locked_tap.is_none() || !locked_listen.has_changed { + return None; } + + let notifiers = locked_listen.tap_fd_handler(locked_tap.as_ref().unwrap()); + locked_listen.has_changed = false; + drop(locked_tap); + drop(locked_listen); + + if let Err(e) = EventLoop::update_event(notifiers, rx_iothread.as_ref()) { + error!("Update tap notifiers failed in handle rx: {:?}", e); + } + None }); - let rx_fd = locked_net_io.rx.queue_evt.as_raw_fd(); - notifiers.push(build_event_notifier( + let rx_fd = self.net_queue.rx.queue_evt.as_raw_fd(); + let notifiers = vec![build_event_notifier( rx_fd, Some(handler), NotifierOperation::AddShared, EventSet::IN, - )); + )]; + notifiers + } - // Register event notifier for tx. - let cloned_net_io = net_io.clone(); + /// Register event notifier for tx. + fn tx_virtio_notifier(&self) -> Vec { + let net_queue = self.net_queue.clone(); + let device_broken = self.device_broken.clone(); + let tap = self.tap.clone(); + let rx_iothread = self.rx_iothread.as_ref().cloned(); let handler: Rc = Rc::new(move |_, fd: RawFd| { read_fd(fd); - let mut locked_net_io = cloned_net_io.lock().unwrap(); - if locked_net_io.device_broken.load(Ordering::SeqCst) { + + if device_broken.load(Ordering::SeqCst) { return None; } - if let Err(ref e) = locked_net_io.handle_tx() { + if let Err(ref e) = net_queue.handle_tx(&tap) { error!("Failed to handle tx(tx event) for net, {:?}", e); report_virtio_error( - locked_net_io.interrupt_cb.clone(), - locked_net_io.driver_features, - &locked_net_io.device_broken, + net_queue.interrupt_cb.clone(), + net_queue.driver_features, + &device_broken, ); } - if locked_net_io.tap.is_some() { - Some(NetIoHandler::tap_fd_handler(&mut locked_net_io)) - } else { - None + let mut locked_listen = net_queue.listen_state.lock().unwrap(); + let locked_tap = tap.read().unwrap(); + if locked_tap.is_none() || !locked_listen.has_changed { + return None; } + + let notifiers = locked_listen.tap_fd_handler(locked_tap.as_ref().unwrap()); + locked_listen.has_changed = false; + drop(locked_tap); + drop(locked_listen); + + if let Err(e) = EventLoop::update_event(notifiers, rx_iothread.as_ref()) { + error!("Update tap notifiers failed in handle tx: {:?}", e); + } + + None }); - let tx_fd = locked_net_io.tx.queue_evt.as_raw_fd(); - notifiers.push(build_event_notifier( + let tx_fd = self.net_queue.tx.queue_evt.as_raw_fd(); + let notifiers = vec![build_event_notifier( tx_fd, Some(handler), NotifierOperation::AddShared, EventSet::IN, - )); + )]; + notifiers + } - // Register event notifier for tap. - let cloned_net_io = net_io.clone(); - if let Some(tap) = locked_net_io.tap.as_ref() { - let handler: Rc = Rc::new(move |events: EventSet, _| { - let mut locked_net_io = cloned_net_io.lock().unwrap(); - if locked_net_io.device_broken.load(Ordering::SeqCst) { + /// Register event notifier for tap. + fn tap_notifier(&self) -> Vec { + let tap = self.tap.clone(); + let net_queue = self.net_queue.clone(); + let device_broken = self.device_broken.clone(); + let locked_tap = self.tap.read().unwrap(); + if locked_tap.is_none() { + return vec![]; + } + let handler: Rc = Rc::new(move |events: EventSet, _| { + if device_broken.load(Ordering::SeqCst) { + return None; + } + + if events.contains(EventSet::OUT) { + net_queue.listen_state.lock().unwrap().set_tap_full(false); + let mut locked_queue = net_queue.tx.queue.lock().unwrap(); + + if let Err(ref err) = locked_queue.vring.suppress_queue_notify( + &net_queue.mem_space, + net_queue.driver_features, + false, + ) { + error!("Failed to enable tx queue notify: {:?}", err); + report_virtio_error( + net_queue.interrupt_cb.clone(), + net_queue.driver_features, + &device_broken, + ); return None; - } + }; - if events.contains(EventSet::OUT) { - locked_net_io.tx.tap_full = false; - let mut locked_queue = locked_net_io.tx.queue.lock().unwrap(); - - if let Err(ref err) = locked_queue.vring.suppress_queue_notify( - &locked_net_io.mem_space, - locked_net_io.driver_features, - false, - ) { - error!("Failed to enable tx queue notify: {:?}", err); - report_virtio_error( - locked_net_io.interrupt_cb.clone(), - locked_net_io.driver_features, - &locked_net_io.device_broken, - ); - return None; - }; - - drop(locked_queue); - - if let Err(ref e) = locked_net_io.handle_tx() { - error!("Failed to handle tx(tx event) for net, {:?}", e); - report_virtio_error( - locked_net_io.interrupt_cb.clone(), - locked_net_io.driver_features, - &locked_net_io.device_broken, - ); - } + drop(locked_queue); + + if let Err(ref e) = net_queue.handle_tx(&tap) { + error!("Failed to handle tx(tx event) for net, {:?}", e); + report_virtio_error( + net_queue.interrupt_cb.clone(), + net_queue.driver_features, + &device_broken, + ); } + } - if events.contains(EventSet::IN) { - if let Err(ref err) = locked_net_io.handle_rx() { - error!("Failed to handle receive queue event: {:?}", err); - report_virtio_error( - locked_net_io.interrupt_cb.clone(), - locked_net_io.driver_features, - &locked_net_io.device_broken, - ); - return None; - } + if events.contains(EventSet::IN) { + if let Err(ref err) = net_queue.handle_rx(&tap) { + error!("Failed to handle receive queue event: {:?}", err); + report_virtio_error( + net_queue.interrupt_cb.clone(), + net_queue.driver_features, + &device_broken, + ); + return None; } + } - Some(NetIoHandler::tap_fd_handler(&mut locked_net_io)) - }); - let tap_fd = tap.as_raw_fd(); - notifiers.push(build_event_notifier( - tap_fd, - Some(handler.clone()), - NotifierOperation::AddShared, - EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, - )); - } + let mut locked_listen = net_queue.listen_state.lock().unwrap(); + let locked_tap = tap.read().unwrap(); + if !locked_listen.has_changed || locked_tap.is_none() { + return None; + } + let tap_notifiers = locked_listen.tap_fd_handler(locked_tap.as_ref().unwrap()); + locked_listen.has_changed = false; + drop(locked_tap); + drop(locked_listen); + + Some(tap_notifiers) + }); + let tap_fd = locked_tap.as_ref().unwrap().as_raw_fd(); + let notifiers = vec![build_event_notifier( + tap_fd, + Some(handler), + NotifierOperation::AddShared, + EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, + )]; notifiers } @@ -1548,27 +1626,52 @@ impl VirtioDevice for Net { } let update_evt = Arc::new(create_new_eventfd()?); - let handler = NetIoHandler { + let net_queue = Arc::new(NetIoQueue { rx: RxVirtio::new(rx_queue, rx_queue_evt), tx: TxVirtio::new(tx_queue, tx_queue_evt), - tap: self.taps.as_ref().map(|t| t[index].clone()), + ctrl_info: ctrl_info.clone(), mem_space: mem_space.clone(), interrupt_cb: interrupt_cb.clone(), driver_features, + listen_state: Arc::new(Mutex::new(ListenState::new())), + queue_size: self.queue_size_max(), + }); + let tap = Arc::new(RwLock::new(self.taps.as_ref().map(|t| t[index].clone()))); + let net_io = Arc::new(Mutex::new(NetIoHandler { + rx_iothread: self.net_cfg.rx_iothread.as_ref().cloned(), + net_queue, + tap, + device_broken: self.base.broken.clone(), receiver, update_evt: update_evt.clone(), - device_broken: self.base.broken.clone(), - is_listening: true, - ctrl_info: ctrl_info.clone(), - queue_size: self.queue_size_max(), - }; - - let notifiers = EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))); + })); + let cloned_net_io = net_io.clone(); + let locked_net_io = net_io.lock().unwrap(); + let update_evt_notifiers = locked_net_io.update_evt_notifier(cloned_net_io); + let rx_notifiers = locked_net_io.rx_virtio_notifier(); + let tx_notifiers = locked_net_io.tx_virtio_notifier(); + let tap_notifiers = locked_net_io.tap_notifier(); + drop(locked_net_io); register_event_helper( - notifiers, + update_evt_notifiers, self.net_cfg.iothread.as_ref(), &mut self.base.deactivate_evts, )?; + register_event_helper( + rx_notifiers, + self.net_cfg.rx_iothread.as_ref(), + &mut self.rx_deactivate_evts, + )?; + register_event_helper( + tap_notifiers, + self.net_cfg.rx_iothread.as_ref(), + &mut self.rx_deactivate_evts, + )?; + register_event_helper( + tx_notifiers, + self.net_cfg.tx_iothread.as_ref(), + &mut self.tx_deactivate_evts, + )?; self.update_evts.push(update_evt); } self.senders = Some(senders); -- Gitee From 9025d1cee5dc74e787baac1238eaa52e2dbd044f Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 13 Jun 2024 09:43:08 +0800 Subject: [PATCH 1898/2187] virtio-net: modif the config_guidebook for virtio-net Add the configuration of rx-iothread and tx-iothread for virtio-net in config_guidebook. Signed-off-by: Xiao Ye --- docs/config_guidebook.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 97665ec78..a836f7041 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -444,6 +444,8 @@ Eight properties are supported for virtio-net-device or virtio-net-pci. * id: unique net device id. * iothread: indicate which iothread will be used, if not specified the main thread will be used. It has no effect when vhost is set. +* rx-iothread: set the receiving task in this iothread, if not specified the former parameter iothread will be used. +* tx-iothread: set the sending task in this iothread, if not specified the former parameter iothread will be used. * netdev: netdev of net device. * vhost: whether to run as a vhost-net device. * vhostfd: the file descriptor of opened tap device. @@ -462,10 +464,10 @@ is a single function device, the function number should be set to zero. ```shell # virtio mmio net device -netdev tap,id=,ifname= --device virtio-net-device,id=,netdev=[,iothread=][,mac=] +-device virtio-net-device,id=,netdev=[,iothread=][,rx-iothread=][,tx-iothread=][,mac=] # virtio pci net device -netdev tap,id=,ifname=[,queues=] --device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,mac=][,mq={on|off}][,queue-size=] +-device virtio-net-pci,id=,netdev=,bus=,addr=<0x2>[,multifunction={on|off}][,iothread=][,rx-iothread=][,tx-iothread=][,mac=][,mq={on|off}][,queue-size=] ``` StratoVirt also supports vhost-net to get a higher performance in network. It can be set by -- Gitee From ee8c90320a37e0fcaa638fce0f57ace1fb5ecc33 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 15 Jun 2024 10:15:03 +0800 Subject: [PATCH 1899/2187] util: add `gen_base_func` macro to replace duplicate code Now, there exist many duplicate codes to get `base` such as `virtio_base`/`device_base`/`machine_base`/`sysbusdev_base`. Use unified macro `gen_base_func` to generate these code. Signed-off-by: liuxiangdong --- devices/src/acpi/cpu_controller.rs | 17 ++------- devices/src/acpi/ged.rs | 23 ++++-------- devices/src/acpi/power.rs | 17 ++------- devices/src/legacy/fwcfg.rs | 34 +++--------------- devices/src/legacy/pflash.rs | 17 ++------- devices/src/legacy/pl011.rs | 17 ++------- devices/src/legacy/pl031.rs | 17 ++------- devices/src/legacy/ramfb.rs | 17 ++------- devices/src/legacy/rtc.rs | 17 ++------- devices/src/legacy/serial.rs | 17 ++------- devices/src/misc/ivshmem.rs | 17 ++------- devices/src/misc/pvpanic.rs | 17 ++------- devices/src/pci/bus.rs | 17 ++------- devices/src/pci/demo_device/mod.rs | 17 ++------- devices/src/pci/host.rs | 33 +++-------------- devices/src/pci/mod.rs | 17 ++------- devices/src/pci/root_port.rs | 23 +++--------- devices/src/scsi/disk.rs | 9 ++--- devices/src/usb/camera.rs | 9 ++--- devices/src/usb/keyboard.rs | 9 ++--- devices/src/usb/storage.rs | 9 ++--- devices/src/usb/tablet.rs | 9 ++--- devices/src/usb/uas.rs | 9 ++--- devices/src/usb/usbhost/mod.rs | 19 ++++------ devices/src/usb/xhci/xhci_pci.rs | 17 ++------- machine/src/aarch64/micro.rs | 15 +++----- machine/src/aarch64/pci_host_root.rs | 17 ++------- machine/src/aarch64/standard.rs | 10 ++---- machine/src/x86_64/ich9_lpc.rs | 17 ++------- machine/src/x86_64/mch.rs | 17 ++------- machine/src/x86_64/micro.rs | 9 ++--- machine/src/x86_64/standard.rs | 18 +++------- util/src/lib.rs | 54 ++++++++++++++++++++++++++++ vfio/src/vfio_pci.rs | 17 ++------- virtio/src/device/balloon.rs | 26 +++++--------- virtio/src/device/block.rs | 10 ++---- virtio/src/device/gpu.rs | 9 ++--- virtio/src/device/net.rs | 14 +++----- virtio/src/device/rng.rs | 9 ++--- virtio/src/device/scsi_cntlr.rs | 9 ++--- virtio/src/device/serial.rs | 9 ++--- virtio/src/transport/virtio_mmio.rs | 25 +++---------- virtio/src/transport/virtio_pci.rs | 29 +++------------ virtio/src/vhost/kernel/net.rs | 9 ++--- virtio/src/vhost/kernel/vsock.rs | 9 ++--- virtio/src/vhost/user/block.rs | 9 ++--- virtio/src/vhost/user/fs.rs | 9 ++--- virtio/src/vhost/user/net.rs | 9 ++--- 48 files changed, 203 insertions(+), 580 deletions(-) diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index 4fe256c62..c3052f61b 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -32,6 +32,7 @@ use acpi::{ use address_space::GuestAddress; use cpu::{CPUBootConfig, CPUInterface, CPUTopology, CpuLifecycleState, CPU}; use migration::MigrationManager; +use util::gen_base_func; const CPU_ENABLE_FLAG: u8 = 1; const CPU_INSERTING_FLAG: u8 = 2; @@ -242,23 +243,11 @@ impl CpuController { } impl Device for CpuController { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for CpuController { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { data[0] = 0; diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index ea6c52daf..fe1538423 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -35,8 +35,11 @@ use address_space::GuestAddress; use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::qmp::qmp_channel::QmpChannel; -use util::loop_context::{create_new_eventfd, read_fd, EventNotifier, NotifierOperation}; -use util::{loop_context::NotifierCallback, num_ops::write_data_u32}; +use util::gen_base_func; +use util::loop_context::{ + create_new_eventfd, read_fd, EventNotifier, NotifierCallback, NotifierOperation, +}; +use util::num_ops::write_data_u32; #[derive(Clone, Copy)] pub enum AcpiEvent { @@ -181,23 +184,11 @@ impl Ged { } impl Device for Ged { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for Ged { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if offset != 0 { diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index d64486fc6..8669e8b39 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -30,6 +30,7 @@ use machine_manager::event_loop::EventLoop; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::num_ops::write_data_u32; const AML_ACAD_REG: &str = "ADPM"; @@ -231,23 +232,11 @@ impl MigrationHook for PowerDev { } impl Device for PowerDev { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for PowerDev { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { let reg_idx: u64 = offset / 4; diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index ad3e3f3ef..5e587f6ec 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -31,7 +31,7 @@ use acpi::{AmlMemory32Fixed, AmlReadAndWrite}; use address_space::{AddressSpace, GuestAddress}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; -use util::offset_of; +use util::{gen_base_func, offset_of}; #[cfg(target_arch = "x86_64")] const FW_CFG_IO_BASE: u64 = 0x510; @@ -931,24 +931,12 @@ impl FwCfgOps for FwCfgMem { #[cfg(target_arch = "aarch64")] impl Device for FwCfgMem { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } #[cfg(target_arch = "aarch64")] impl SysBusDevOps for FwCfgMem { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { common_read(self, data, base, offset) @@ -1098,24 +1086,12 @@ impl FwCfgOps for FwCfgIO { #[cfg(target_arch = "x86_64")] impl Device for FwCfgIO { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } #[cfg(target_arch = "x86_64")] impl SysBusDevOps for FwCfgIO { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { common_read(self, data, base, offset) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 747d22b98..2db2ce228 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -22,6 +22,7 @@ use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; +use util::gen_base_func; use util::num_ops::{deposit_u32, extract_u32, read_data_u32, round_up, write_data_u32}; use util::unix::host_page_size; @@ -691,23 +692,11 @@ impl PFlash { } impl Device for PFlash { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for PFlash { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { let mut index: u64; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index f5fdafae3..2a6cab1c1 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -35,6 +35,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{create_new_eventfd, EventNotifierHelper}; use util::num_ops::read_data_u32; @@ -236,23 +237,11 @@ impl InputReceiver for PL011 { } impl Device for PL011 { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for PL011 { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if data.len() > 4 { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 9ad650b07..98a351963 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -27,6 +27,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::create_new_eventfd; use util::num_ops::write_data_u32; @@ -123,23 +124,11 @@ impl PL031 { } impl Device for PL031 { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for PL031 { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); /// Read data from registers by guest. fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 3945de43e..278fd8e77 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -32,6 +32,7 @@ use ui::console::{ DisplaySurface, HardWareOperations, }; use ui::input::{key_event, KEYCODE_RET}; +use util::gen_base_func; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; const BYTES_PER_PIXELS: u32 = 8; @@ -258,23 +259,11 @@ impl Ramfb { } impl Device for Ramfb { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for Ramfb { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, _data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool { error!("Ramfb can not be read!"); diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 1e498084b..259ac9810 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -23,6 +23,7 @@ use acpi::{ AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; +use util::gen_base_func; use util::loop_context::create_new_eventfd; use util::time::{mktime64, NANOSECONDS_PER_SECOND}; @@ -351,23 +352,11 @@ impl RTC { } impl Device for RTC { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for RTC { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { if offset == 0 { diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 520e929cd..3386f7ff8 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -33,6 +33,7 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{create_new_eventfd, EventNotifierHelper}; pub const SERIAL_ADDR: u64 = 0x3f8; @@ -371,23 +372,11 @@ impl InputReceiver for Serial { } impl Device for Serial { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for Serial { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { data[0] = self.read_internal(offset); diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index e5c7cd74f..cc9e383f8 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -26,6 +26,7 @@ use crate::pci::{ }; use crate::{Device, DeviceBase}; use address_space::{GuestAddress, Region, RegionOps}; +use util::gen_base_func; const PCI_VENDOR_ID_IVSHMEM: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; const PCI_DEVICE_ID_IVSHMEM: u16 = 0x1110; @@ -92,23 +93,11 @@ impl Ivshmem { } impl Device for Ivshmem { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for Ivshmem { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index a65979549..cdebea6c8 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -32,6 +32,7 @@ use crate::pci::{ use crate::{Device, DeviceBase}; use address_space::{GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; +use util::gen_base_func; const PVPANIC_PCI_REVISION_ID: u8 = 1; const PVPANIC_PCI_VENDOR_ID: u16 = PCI_VENDOR_ID_REDHAT_QUMRANET; @@ -166,23 +167,11 @@ impl PvPanicPci { } impl Device for PvPanicPci { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for PvPanicPci { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 3acc5250e..1141afe9a 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -278,6 +278,7 @@ mod tests { use crate::pci::root_port::RootPort; use crate::pci::{PciDevBase, RootPortConfig}; use crate::{Device, DeviceBase}; + use util::gen_base_func; #[derive(Clone)] struct PciDevice { @@ -285,23 +286,11 @@ mod tests { } impl Device for PciDevice { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for PciDevice { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { #[allow(unused_variables)] diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index ff35f475a..987120c38 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -59,6 +59,7 @@ use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; use crate::{Device, DeviceBase}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; +use util::gen_base_func; /// Config struct for `demo_dev`. /// Contains demo_dev device's attr. @@ -195,23 +196,11 @@ const DEVICE_ID_DEMO: u16 = 0xBEEF; const CLASS_CODE_DEMO: u16 = 0xEE; impl Device for DemoDev { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for DemoDev { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); /// Realize PCI/PCIe device. fn realize(mut self) -> Result<()> { diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index f39571560..8bcaee721 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -34,6 +34,7 @@ use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlOne, AmlQWordDesc}; use address_space::{AddressSpace, GuestAddress, RegionOps}; +use util::gen_base_func; #[cfg(target_arch = "x86_64")] const CONFIG_ADDRESS_ENABLE_MASK: u32 = 0x8000_0000; @@ -231,23 +232,11 @@ impl PciHost { } impl Device for PciHost { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for PciHost { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { let bus_num = ((offset as u32 >> ECAM_BUS_SHIFT) & CONFIG_BUS_MASK) as u8; @@ -557,23 +546,11 @@ pub mod tests { } impl Device for PciDevice { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for PciDevice { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn init_write_mask(&mut self, _is_bridge: bool) -> Result<()> { let mut offset = 0_usize; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 7a90d6eac..13da03462 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -406,6 +406,7 @@ mod tests { use super::*; use crate::DeviceBase; use address_space::{AddressSpace, Region}; + use util::gen_base_func; #[test] fn test_le_write_u16_01() { @@ -453,23 +454,11 @@ mod tests { } impl Device for PciDev { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for PciDev { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, _offset: usize, _data: &[u8]) {} diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 6396c0ce8..9d3bcf254 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -46,10 +46,9 @@ use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; -use util::{ - byte_code::ByteCode, - num_ops::{ranges_overlap, str_to_num}, -}; +use util::byte_code::ByteCode; +use util::gen_base_func; +use util::num_ops::{ranges_overlap, str_to_num}; const DEVICE_ID_RP: u16 = 0x000c; @@ -345,23 +344,11 @@ impl RootPort { } impl Device for RootPort { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for RootPort { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(true)?; diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 37aca3da4..178250a4c 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -22,6 +22,7 @@ use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; use machine_manager::config::{valid_id, DriveConfig, DriveFile, VmConfig}; use machine_manager::event_loop::EventLoop; use util::aio::{Aio, AioEngine, WriteZeroesState}; +use util::gen_base_func; /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; @@ -131,13 +132,7 @@ impl ScsiDevState { } impl Device for ScsiDevice { - fn device_base(&self) -> &DeviceBase { - &self.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base); } pub struct ScsiDevice { diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 79cc1ba5d..fccd0a170 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -40,6 +40,7 @@ use machine_manager::config::valid_id; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::aio::{iov_discard_front_direct, Iovec}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{ create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -750,13 +751,7 @@ impl UsbCamera { } impl UsbDevice for UsbCamera { - fn usb_device_base(&self) -> &UsbDeviceBase { - &self.base - } - - fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { - &mut self.base - } + gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { let fmt_list = self.camera_backend.lock().unwrap().list_format()?; diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 532914277..214ade866 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -30,6 +30,7 @@ use super::{ }; use machine_manager::config::valid_id; use ui::input::{register_keyboard, unregister_keyboard, KeyboardOpts}; +use util::gen_base_func; /// Keyboard device descriptor static DESC_DEVICE_KEYBOARD: Lazy> = Lazy::new(|| { @@ -189,13 +190,7 @@ impl UsbKeyboard { } impl UsbDevice for UsbKeyboard { - fn usb_device_base(&self) -> &UsbDeviceBase { - &self.base - } - - fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { - &mut self.base - } + gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { self.base.reset_usb_endpoint(); diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 2f924d8a7..67c03488a 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -37,6 +37,7 @@ use crate::{ }; use machine_manager::config::{DriveConfig, DriveFile}; use util::aio::AioEngine; +use util::gen_base_func; // Storage device descriptor static DESC_DEVICE_STORAGE: Lazy> = Lazy::new(|| { @@ -541,13 +542,7 @@ impl UsbStorage { } impl UsbDevice for UsbStorage { - fn usb_device_base(&self) -> &UsbDeviceBase { - &self.base - } - - fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { - &mut self.base - } + gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { self.base.reset_usb_endpoint(); diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index a4bfebf40..78551cb8c 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -34,6 +34,7 @@ use ui::input::{ INPUT_BUTTON_MASK, INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, }; +use util::gen_base_func; const INPUT_COORDINATES_MAX: u32 = 0x7fff; @@ -233,13 +234,7 @@ impl PointerOpts for UsbTabletAdapter { } impl UsbDevice for UsbTablet { - fn usb_device_base(&self) -> &UsbDeviceBase { - &self.base - } - - fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { - &mut self.base - } + gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { self.base.reset_usb_endpoint(); diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 4bdfc398f..e913ca5cc 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -44,6 +44,7 @@ use crate::{ }; use machine_manager::config::{DriveConfig, DriveFile}; use util::byte_code::ByteCode; +use util::gen_base_func; // Size of UasIUBody const UAS_IU_BODY_SIZE: usize = 30; @@ -1025,13 +1026,7 @@ impl UsbUas { } impl UsbDevice for UsbUas { - fn usb_device_base(&self) -> &UsbDeviceBase { - &self.base - } - - fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { - &mut self.base - } + gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { info!("UAS {} device realize.", self.device_id()); diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 3da8cf05d..65e05112a 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -56,12 +56,11 @@ use machine_manager::{ }; #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] use ohusb::OhUsbDev; -use util::{ - byte_code::ByteCode, - link_list::{List, Node}, - loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}, - num_ops::str_to_num, -}; +use util::byte_code::ByteCode; +use util::gen_base_func; +use util::link_list::{List, Node}; +use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierCallback}; +use util::num_ops::str_to_num; const NON_ISO_PACKETS_NUMS: c_int = 0; const HANDLE_TIMEOUT_MS: u64 = 2; @@ -1050,13 +1049,7 @@ impl EventNotifierHelper for UsbHost { } impl UsbDevice for UsbHost { - fn usb_device_base(&self) -> &UsbDeviceBase { - &self.base - } - - fn usb_device_base_mut(&mut self) -> &mut UsbDeviceBase { - &mut self.base - } + gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { info!("Open and init usbhost device: {:?}", self.config); diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 0e8ffd232..8a7208e6f 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -38,6 +38,7 @@ use crate::{Device, DeviceBase}; use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::{get_pci_df, valid_id}; use machine_manager::event_loop::register_event_helper; +use util::gen_base_func; use util::loop_context::{ create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -237,23 +238,11 @@ impl XhciPciDevice { } impl Device for XhciPciDevice { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for XhciPciDevice { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 40c8baff1..39fed7e4d 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -22,10 +22,9 @@ use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_ use hypervisor::kvm::aarch64::*; use machine_manager::config::{SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; -use util::{ - device_tree::{self, CompileFDT, FdtBuilder}, - seccomp::{BpfRule, SeccompCmpOpt}, -}; +use util::device_tree::{self, CompileFDT, FdtBuilder}; +use util::gen_base_func; +use util::seccomp::{BpfRule, SeccompCmpOpt}; use virtio::{VirtioDevice, VirtioMmioDevice}; #[repr(usize)] @@ -54,13 +53,7 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ ]; impl MachineOps for LightMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - - fn machine_base_mut(&mut self) -> &mut MachineBase { - &mut self.base - } + gen_base_func!(machine_base, machine_base_mut, MachineBase, base); fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index aec540233..512cf8f3c 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -22,6 +22,7 @@ use devices::pci::{ le_write_u16, PciBus, PciDevBase, PciDevOps, }; use devices::{Device, DeviceBase}; +use util::gen_base_func; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; @@ -44,23 +45,11 @@ impl PciHostRoot { } impl Device for PciHostRoot { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for PciHostRoot { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 4b6eebdf0..cec804b6f 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -83,7 +83,7 @@ use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::loop_context::{create_new_eventfd, EventLoopManager}; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::set_termi_canon_mode; +use util::{gen_base_func, set_termi_canon_mode}; /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { @@ -446,13 +446,7 @@ impl StdMachineOps for StdMachine { } impl MachineOps for StdMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - - fn machine_base_mut(&mut self) -> &mut MachineBase { - &mut self.base - } + gen_base_func!(machine_base, machine_base_mut, MachineBase, base); fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index 0a93312ef..4038e5037 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -29,6 +29,7 @@ use devices::pci::config::{ use devices::pci::{le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps}; use devices::{Device, DeviceBase}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::num_ops::ranges_overlap; const DEVICE_ID_INTEL_ICH9: u16 = 0x2918; @@ -229,23 +230,11 @@ impl LPCBridge { } impl Device for LPCBridge { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for LPCBridge { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index 5c6f593ec..ddf7bd674 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -25,6 +25,7 @@ use devices::pci::{ le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, }; use devices::{Device, DeviceBase}; +use util::gen_base_func; use util::num_ops::ranges_overlap; const DEVICE_ID_INTEL_Q35_MCH: u16 = 0x29c0; @@ -121,23 +122,11 @@ impl Mch { } impl Device for Mch { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for Mch { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index e5d17ce52..219df0bd0 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -24,6 +24,7 @@ use hypervisor::kvm::x86_64::*; use hypervisor::kvm::*; use machine_manager::config::{SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; +use util::gen_base_func; use util::seccomp::{BpfRule, SeccompCmpOpt}; use virtio::{VirtioDevice, VirtioMmioDevice}; @@ -47,13 +48,7 @@ pub const MEM_LAYOUT: &[(u64, u64)] = &[ ]; impl MachineOps for LightMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - - fn machine_base_mut(&mut self) -> &mut MachineBase { - &mut self.base - } + gen_base_func!(machine_base, machine_base_mut, MachineBase, base); fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let vm_ram = self.get_vm_ram(); diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 8dca2db77..e31d6929e 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -59,13 +59,11 @@ use migration::{MigrationManager, MigrationStatus}; use ui::gtk::gtk_display_init; #[cfg(feature = "vnc")] use ui::vnc::vnc_init; +use util::byte_code::ByteCode; +use util::loop_context::{create_new_eventfd, EventLoopManager}; +use util::seccomp::BpfRule; use util::seccomp::SeccompCmpOpt; -use util::{ - byte_code::ByteCode, - loop_context::{create_new_eventfd, EventLoopManager}, - seccomp::BpfRule, - set_termi_canon_mode, -}; +use util::{gen_base_func, set_termi_canon_mode}; pub(crate) const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -419,13 +417,7 @@ impl StdMachineOps for StdMachine { } impl MachineOps for StdMachine { - fn machine_base(&self) -> &MachineBase { - &self.base - } - - fn machine_base_mut(&mut self) -> &mut MachineBase { - &mut self.base - } + gen_base_func!(machine_base, machine_base_mut, MachineBase, base); fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { let ram = self.get_vm_ram(); diff --git a/util/src/lib.rs b/util/src/lib.rs index f861d6f6a..a33cab3c8 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -95,6 +95,60 @@ pub fn set_termi_canon_mode() -> std::io::Result<()> { Ok(()) } +/// Macro: Generate base getting function. +/// +/// # Arguments +/// +/// * `get_func` - Name of getting `&base` function. +/// * `get_mut_func` - Name of getting `&mut base` function. +/// * `base_type` - Type of `base`. +/// * `base` - `base` in self. +/// +/// # Examples +/// +/// ```rust +/// use util::gen_base_func; +/// struct TestBase(u8); +/// struct Test { +/// base: TestBase, +/// } +/// +/// impl Test { +/// gen_base_func!(test_base, test_base_mut, TestBase, base); +/// } +/// ``` +/// +/// This is equivalent to: +/// +/// ```rust +/// struct TestBase(u8); +/// struct Test { +/// base: TestBase, +/// } +/// +/// impl Test { +/// fn test_base(&self) -> &TestBase { +/// &self.base +/// } +/// +/// fn test_base_mut(&mut self) -> &mut TestBase { +/// &mut self.base +/// } +/// } +/// ``` +#[macro_export] +macro_rules! gen_base_func { + ($get_func: ident, $get_mut_func: ident, $base_type: ty, $($base: tt).*) => { + fn $get_func(&self) -> &$base_type { + &self.$($base).* + } + + fn $get_mut_func(&mut self) -> &mut $base_type { + &mut self.$($base).* + } + }; +} + /// This trait is to cast trait object to struct. pub trait AsAny { fn as_any(&self) -> &dyn Any; diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index ebf028e59..7a74badc4 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -45,6 +45,7 @@ use devices::pci::{ }; use devices::{pci::MsiVector, Device, DeviceBase}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; +use util::gen_base_func; use util::loop_context::create_new_eventfd; use util::num_ops::ranges_overlap; use util::unix::host_page_size; @@ -813,23 +814,11 @@ impl VfioPciDevice { } impl Device for VfioPciDevice { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for VfioPciDevice { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { self.init_write_mask(false)?; diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index fe62211c5..97724ece9 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -41,17 +41,15 @@ use machine_manager::{ qmp::qmp_channel::QmpChannel, qmp::qmp_schema::BalloonInfo, }; -use util::{ - bitmap::Bitmap, - byte_code::ByteCode, - loop_context::{ - read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, - }, - num_ops::round_down, - offset_of, - seccomp::BpfRule, - unix::host_page_size, +use util::bitmap::Bitmap; +use util::byte_code::ByteCode; +use util::loop_context::{ + read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; +use util::num_ops::round_down; +use util::seccomp::BpfRule; +use util::unix::host_page_size; +use util::{gen_base_func, offset_of}; const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u32 = 2; const VIRTIO_BALLOON_F_REPORTING: u32 = 5; @@ -1053,13 +1051,7 @@ impl Balloon { } impl VirtioDevice for Balloon { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { self.bln_cfg.check()?; diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 837cfe6dc..26fa748fe 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -61,7 +61,7 @@ use util::loop_context::{ create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; -use util::offset_of; +use util::{gen_base_func, offset_of}; /// Number of virtqueues. const QUEUE_NUM_BLK: usize = 1; @@ -1122,13 +1122,7 @@ impl Block { } impl VirtioDevice for Block { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { // if iothread not found, return err diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 2d138ed24..acd74d0ab 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -52,6 +52,7 @@ use ui::pixman::{ use util::aio::{iov_from_buf_direct, iov_to_buf_direct, Iovec}; use util::byte_code::ByteCode; use util::edid::EdidInfo; +use util::gen_base_func; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -1733,13 +1734,7 @@ impl Gpu { } impl VirtioDevice for Gpu { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn device_quirk(&self) -> Option { if self.cfg.enable_bar0 { diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 7b400206a..4101ec9cc 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -55,10 +55,10 @@ use migration::{ }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::loop_context::gen_delete_notifiers; +use util::gen_base_func; use util::loop_context::{ - create_new_eventfd, read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, - NotifierOperation, + create_new_eventfd, gen_delete_notifiers, read_fd, EventNotifier, EventNotifierHelper, + NotifierCallback, NotifierOperation, }; use util::num_ops::str_to_num; use util::tap::{ @@ -1462,13 +1462,7 @@ fn get_tap_offload_flags(features: u64) -> u32 { } impl VirtioDevice for Net { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { // if iothread not found, return err diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 3d26ea9e2..557e759cd 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -39,6 +39,7 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use util::aio::raw_read; use util::byte_code::ByteCode; +use util::gen_base_func; use util::leak_bucket::LeakBucket; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, @@ -312,13 +313,7 @@ impl Rng { } impl VirtioDevice for Rng { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { self.check_random_file() diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 8f301df15..26db5f132 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -39,6 +39,7 @@ use machine_manager::config::{ use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use util::aio::Iovec; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -170,13 +171,7 @@ impl ScsiCntlr { } impl VirtioDevice for ScsiCntlr { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { // If iothread not found, return err. diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 0cf424215..73aa4ac64 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -40,6 +40,7 @@ use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, Sta use migration_derive::{ByteCode, Desc}; use util::aio::iov_from_buf_direct; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{ read_fd, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, }; @@ -215,13 +216,7 @@ pub fn find_port_by_nr( } impl VirtioDevice for Serial { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { self.init_config_features()?; diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 814fbeeed..c538f958a 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -33,6 +33,7 @@ use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::create_new_eventfd; /// Registers of virtio-mmio device refer to Virtio Spec. @@ -391,23 +392,11 @@ impl VirtioMmioDevice { } impl Device for VirtioMmioDevice { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for VirtioMmioDevice { - fn sysbusdev_base(&self) -> &SysBusDevBase { - &self.base - } - - fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { - &mut self.base - } + gen_base_func!(sysbusdev_base, sysbusdev_base_mut, SysBusDevBase, base); /// Read data by virtio driver from VM. fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { @@ -631,13 +620,7 @@ mod tests { } impl VirtioDevice for VirtioDeviceTest { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { self.b_realized = true; diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index a9ba784bd..d80ded3ca 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -52,9 +52,8 @@ use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; -use util::num_ops::ranges_overlap; -use util::num_ops::{read_data_u32, write_data_u32}; -use util::offset_of; +use util::num_ops::{ranges_overlap, read_data_u32, write_data_u32}; +use util::{gen_base_func, offset_of}; const VIRTIO_QUEUE_MAX: u32 = 1024; @@ -1005,23 +1004,11 @@ impl VirtioPciDevice { } impl Device for VirtioPciDevice { - fn device_base(&self) -> &DeviceBase { - &self.base.base - } - - fn device_base_mut(&mut self) -> &mut DeviceBase { - &mut self.base.base - } + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl PciDevOps for VirtioPciDevice { - fn pci_base(&self) -> &PciDevBase { - &self.base - } - - fn pci_base_mut(&mut self) -> &mut PciDevBase { - &mut self.base - } + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { info!("func: realize, id: {:?}", &self.base.base.id); @@ -1418,13 +1405,7 @@ mod tests { } impl VirtioDevice for VirtioDeviceTest { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { self.init_config_features()?; diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 0933cef49..d55376cf4 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -34,6 +34,7 @@ use address_space::AddressSpace; use machine_manager::config::{NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{create_new_eventfd, EventNotifierHelper}; use util::tap::Tap; @@ -123,13 +124,7 @@ impl Net { } impl VirtioDevice for Net { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { let queue_pairs = self.netdev_cfg.queues / 2; diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 8c06c63dd..4f2d1a445 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -32,6 +32,7 @@ use machine_manager::event_loop::{register_event_helper, unregister_event_helper use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::{create_new_eventfd, EventNotifierHelper}; /// Number of virtqueues. @@ -193,13 +194,7 @@ impl Vsock { } impl VirtioDevice for Vsock { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { let vhost_fd: Option = self.vsock_cfg.vhost_fd; diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 8c55cac12..8ccce18e4 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -37,6 +37,7 @@ use machine_manager::config::{ }; use machine_manager::event_loop::unregister_event_helper; use util::byte_code::ByteCode; +use util::gen_base_func; #[derive(Parser, Debug, Clone, Default)] #[command(no_binary_name(true))] @@ -119,13 +120,7 @@ impl Block { } impl VirtioDevice for Block { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { self.init_client()?; diff --git a/virtio/src/vhost/user/fs.rs b/virtio/src/vhost/user/fs.rs index 75c3698a0..f08ddd399 100644 --- a/virtio/src/vhost/user/fs.rs +++ b/virtio/src/vhost/user/fs.rs @@ -33,6 +33,7 @@ use machine_manager::config::{ }; use machine_manager::event_loop::unregister_event_helper; use util::byte_code::ByteCode; +use util::gen_base_func; const MAX_TAG_LENGTH: usize = 36; @@ -120,13 +121,7 @@ impl Fs { } impl VirtioDevice for Fs { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; diff --git a/virtio/src/vhost/user/net.rs b/virtio/src/vhost/user/net.rs index 4dc763458..fc9834463 100644 --- a/virtio/src/vhost/user/net.rs +++ b/virtio/src/vhost/user/net.rs @@ -31,6 +31,7 @@ use address_space::AddressSpace; use machine_manager::config::{NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use util::byte_code::ByteCode; +use util::gen_base_func; use util::loop_context::EventNotifierHelper; /// Number of virtqueues. @@ -117,13 +118,7 @@ impl Net { } impl VirtioDevice for Net { - fn virtio_base(&self) -> &VirtioBase { - &self.base - } - - fn virtio_base_mut(&mut self) -> &mut VirtioBase { - &mut self.base - } + gen_base_func!(virtio_base, virtio_base_mut, VirtioBase, base); fn realize(&mut self) -> Result<()> { let client = VhostUserClient::new( -- Gitee From f30677c8bf6174c6cfbcfc2a91b8b0c390438067 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jun 2024 00:39:13 +0800 Subject: [PATCH 1900/2187] machine: unified `StdMachine` Unified `StdMachine` to reduce redundant code. Signed-off-by: liuxiangdong --- machine/src/aarch64/standard.rs | 178 ++------------------------- machine/src/standard_common/mod.rs | 190 +++++++++++++++++++++++++++-- machine/src/x86_64/standard.rs | 148 +--------------------- 3 files changed, 192 insertions(+), 324 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 1dcf86cbb..a3cb4dbd1 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -13,7 +13,6 @@ pub use crate::error::MachineError; use std::mem::size_of; -use std::ops::Deref; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use std::sync::RwLock; use std::sync::{Arc, Mutex}; @@ -21,11 +20,11 @@ use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; #[cfg(feature = "ramfb")] use clap::Parser; -use log::{error, info, warn}; -use vmm_sys_util::eventfd::EventFd; +use super::pci_host_root::PciHostRoot; +use crate::standard_common::syscall::syscall_whitelist; use crate::standard_common::{AcpiBuilder, StdMachineOps}; -use crate::{MachineBase, MachineOps}; +use crate::{MachineBase, MachineOps, StdMachine}; use acpi::{ processor_append_priv_res, AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, @@ -42,9 +41,6 @@ use acpi::{ use address_space::FileBackend; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::{CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE}; - -use super::pci_host_root::PciHostRoot; -use crate::standard_common::syscall::syscall_whitelist; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged, GedEvent}; use devices::acpi::power::PowerDev; use devices::legacy::{ @@ -59,19 +55,12 @@ use hypervisor::kvm::aarch64::*; use hypervisor::kvm::*; #[cfg(feature = "ramfb")] use machine_manager::config::str_slip_to_clap; -use machine_manager::config::ShutdownAction; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; -use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, DriveConfig, MigrateMode, NumaNode, SerialConfig, VmConfig, -}; +use machine_manager::config::{BootIndexInfo, DriveConfig, NumaNode, SerialConfig, VmConfig}; use machine_manager::event; -use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{ - MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, - MigrateInterface, PauseNotify, VmState, -}; -use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; +use machine_manager::machine::{MachineLifecycle, PauseNotify}; +use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; @@ -81,9 +70,9 @@ use ui::ohui_srv::{ohui_init, OhUiServer}; use ui::vnc::vnc_init; use util::byte_code::ByteCode; use util::device_tree::{self, CompileFDT, FdtBuilder}; -use util::loop_context::{create_new_eventfd, EventLoopManager}; +use util::gen_base_func; +use util::loop_context::create_new_eventfd; use util::seccomp::{BpfRule, SeccompCmpOpt}; -use util::{gen_base_func, set_termi_canon_mode}; /// The type of memory layout entry on aarch64 pub enum LayoutEntryType { @@ -137,31 +126,6 @@ const IRQ_MAP: &[(i32, i32)] = &[ (16, 19), // Pcie ]; -/// Standard machine structure. -pub struct StdMachine { - /// Machine base members. - base: MachineBase, - /// PCI/PCIe host bridge. - pci_host: Arc>, - /// VM power button, handle VM `Shutdown` event. - pub power_button: Arc, - /// Shutdown request, handle VM `shutdown` event. - shutdown_req: Arc, - /// Reset request, handle VM `Reset` event. - reset_req: Arc, - /// Pause request, handle VM `Pause` event. - pause_req: Arc, - /// Resume request, handle VM `Resume` event. - resume_req: Arc, - /// Device Tree Blob. - dtb_vec: Vec, - /// List contains the boot order of boot devices. - boot_order_list: Arc>>, - /// OHUI server - #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - ohui_server: Option>, -} - impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { let free_irqs = ( @@ -258,27 +222,6 @@ impl StdMachine { Ok(()) } - pub fn handle_destroy_request(vm: &Arc>) -> bool { - let locked_vm = vm.lock().unwrap(); - let vmstate = { - let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); - *state - }; - - if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { - warn!("Failed to destroy guest, destroy continue."); - if locked_vm.shutdown_req.write(1).is_err() { - error!("Failed to send shutdown request.") - } - return false; - } - - EventLoop::kick_all(); - info!("vm destroy"); - - true - } - fn build_pptt_cores(&self, pptt: &mut AcpiTable, cluster_offset: u32, uid: &mut u32) { for core in 0..self.base.cpu_topo.cores { let mut priv_resources = vec![0; 3]; @@ -1196,111 +1139,6 @@ impl AcpiBuilder for StdMachine { } } -impl MachineLifecycle for StdMachine { - fn pause(&self) -> bool { - if self.notify_lifecycle(VmState::Running, VmState::Paused) { - event!(Stop); - true - } else { - false - } - } - - fn resume(&self) -> bool { - if !self.notify_lifecycle(VmState::Paused, VmState::Running) { - return false; - } - event!(Resume); - true - } - - fn destroy(&self) -> bool { - if self.shutdown_req.write(1).is_err() { - error!("Failed to send shutdown request."); - return false; - } - - true - } - - fn powerdown(&self) -> bool { - if self.power_button.write(1).is_err() { - error!("ARM standard vm write power button failed"); - return false; - } - true - } - - fn get_shutdown_action(&self) -> ShutdownAction { - self.base - .vm_config - .lock() - .unwrap() - .machine_config - .shutdown_action - } - - fn reset(&mut self) -> bool { - if self.reset_req.write(1).is_err() { - error!("ARM standard vm write reset req failed"); - return false; - } - true - } - - fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { - if let Err(e) = self.vm_state_transfer( - &self.base.cpus, - &self.base.irq_chip, - &mut self.base.vm_state.0.lock().unwrap(), - old, - new, - ) { - error!("VM state transfer failed: {:?}", e); - return false; - } - true - } -} - -impl MigrateInterface for StdMachine { - fn migrate(&self, uri: String) -> Response { - match parse_incoming_uri(&uri) { - Ok((MigrateMode::File, path)) => migration::snapshot(path), - Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), - Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), - _ => Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), - None, - ), - } - } - - fn query_migrate(&self) -> Response { - migration::query_migrate() - } - - fn cancel_migrate(&self) -> Response { - migration::cancel_migrate() - } -} - -impl MachineInterface for StdMachine {} -impl MachineExternalInterface for StdMachine {} -impl MachineTestInterface for StdMachine {} - -impl EventLoopManager for StdMachine { - fn loop_should_exit(&self) -> bool { - let vmstate = self.base.vm_state.deref().0.lock().unwrap(); - *vmstate == VmState::Shutdown - } - - fn loop_cleanup(&self) -> Result<()> { - set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; - Ok(()) - } -} - /// Function that helps to generate flash node in device-tree. /// diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index f18350ae4..c8617ef95 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -12,11 +12,7 @@ pub mod syscall; -#[cfg(target_arch = "aarch64")] -pub use crate::aarch64::standard::StdMachine; pub use crate::error::MachineError; -#[cfg(target_arch = "x86_64")] -pub use crate::x86_64::standard::StdMachine; use std::mem::size_of; use std::ops::Deref; @@ -28,7 +24,8 @@ use std::sync::{Arc, Mutex}; use std::u64; use anyhow::{bail, Context, Result}; -use log::error; +use log::{error, info, warn}; +use util::set_termi_canon_mode; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -40,7 +37,7 @@ use crate::x86_64::ich9_lpc::{ }; #[cfg(target_arch = "x86_64")] use crate::x86_64::standard::{LayoutEntryType, MEM_LAYOUT}; -use crate::MachineOps; +use crate::{MachineBase, MachineOps}; #[cfg(target_arch = "x86_64")] use acpi::AcpiGenericAddress; use acpi::{ @@ -57,18 +54,23 @@ use devices::legacy::FwCfgOps; #[cfg(feature = "scream")] use devices::misc::scream::set_record_authority; use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; -use devices::pci::PciBus; +use devices::pci::{PciBus, PciHost}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_config; +#[cfg(target_arch = "aarch64")] +use machine_manager::config::ShutdownAction; #[cfg(feature = "windows_emu_pid")] use machine_manager::config::VmConfig; use machine_manager::config::{ - get_chardev_config, get_netdev_config, memory_unit_conversion, ConfigCheck, DiskFormat, - DriveConfig, ExBool, NumaNode, NumaNodes, M, + get_chardev_config, get_netdev_config, memory_unit_conversion, parse_incoming_uri, + BootIndexInfo, ConfigCheck, DiskFormat, DriveConfig, ExBool, MigrateMode, NumaNode, NumaNodes, + M, }; +use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ - DeviceInterface, MachineAddressInterface, MachineLifecycle, VmState, + DeviceInterface, MachineAddressInterface, MachineExternalInterface, MachineInterface, + MachineLifecycle, MachineTestInterface, MigrateInterface, VmState, }; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; @@ -80,12 +82,70 @@ use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; use util::byte_code::ByteCode; use util::loop_context::{ - create_new_eventfd, read_fd, EventNotifier, NotifierCallback, NotifierOperation, + create_new_eventfd, read_fd, EventLoopManager, EventNotifier, NotifierCallback, + NotifierOperation, }; use virtio::{qmp_balloon, qmp_query_balloon}; const MAX_REGION_SIZE: u64 = 65536; +/// Standard machine structure. +pub struct StdMachine { + /// Machine base members. + pub(crate) base: MachineBase, + /// PCI/PCIe host bridge. + pub(crate) pci_host: Arc>, + /// Reset request, handle VM `Reset` event. + pub(crate) reset_req: Arc, + /// Shutdown request, handle VM `shutdown` event. + pub(crate) shutdown_req: Arc, + /// VM power button, handle VM `Shutdown` event. + pub(crate) power_button: Arc, + /// List contains the boot order of boot devices. + pub(crate) boot_order_list: Arc>>, + /// CPU Resize request, handle vm cpu hot(un)plug event. + #[cfg(target_arch = "x86_64")] + pub(crate) cpu_resize_req: Arc, + /// Cpu Controller. + #[cfg(target_arch = "x86_64")] + pub(crate) cpu_controller: Option>>, + /// Pause request, handle VM `Pause` event. + #[cfg(target_arch = "aarch64")] + pub(crate) pause_req: Arc, + /// Resume request, handle VM `Resume` event. + #[cfg(target_arch = "aarch64")] + pub(crate) resume_req: Arc, + /// Device Tree Blob. + #[cfg(target_arch = "aarch64")] + pub(crate) dtb_vec: Vec, + /// OHUI server + #[cfg(all(target_arch = "aarch64", target_env = "ohos", feature = "ohui_srv"))] + pub(crate) ohui_server: Option>, +} + +impl StdMachine { + pub fn handle_destroy_request(vm: &Arc>) -> bool { + let locked_vm = vm.lock().unwrap(); + let vmstate = { + let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); + *state + }; + + if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { + warn!("Failed to destroy guest, destroy continue."); + if locked_vm.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request.") + } + return false; + } + + EventLoop::kick_all(); + info!("vm destroy"); + + true + } +} + pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn init_pci_host(&self) -> Result<()>; @@ -930,6 +990,114 @@ impl StdMachine { } } +impl MachineLifecycle for StdMachine { + fn pause(&self) -> bool { + if self.notify_lifecycle(VmState::Running, VmState::Paused) { + event!(Stop); + true + } else { + false + } + } + + fn resume(&self) -> bool { + if !self.notify_lifecycle(VmState::Paused, VmState::Running) { + return false; + } + event!(Resume); + true + } + + fn destroy(&self) -> bool { + if self.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request."); + return false; + } + + true + } + + #[cfg(target_arch = "aarch64")] + fn powerdown(&self) -> bool { + if self.power_button.write(1).is_err() { + error!("Standard vm write power button failed"); + return false; + } + true + } + + #[cfg(target_arch = "aarch64")] + fn get_shutdown_action(&self) -> ShutdownAction { + self.base + .vm_config + .lock() + .unwrap() + .machine_config + .shutdown_action + } + + fn reset(&mut self) -> bool { + if self.reset_req.write(1).is_err() { + error!("Standard vm write reset request failed"); + return false; + } + true + } + + fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { + if let Err(e) = self.vm_state_transfer( + &self.base.cpus, + #[cfg(target_arch = "aarch64")] + &self.base.irq_chip, + &mut self.base.vm_state.0.lock().unwrap(), + old, + new, + ) { + error!("VM state transfer failed: {:?}", e); + return false; + } + true + } +} + +impl MigrateInterface for StdMachine { + fn migrate(&self, uri: String) -> Response { + match parse_incoming_uri(&uri) { + Ok((MigrateMode::File, path)) => migration::snapshot(path), + Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), + Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), + _ => Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), + None, + ), + } + } + + fn query_migrate(&self) -> Response { + migration::query_migrate() + } + + fn cancel_migrate(&self) -> Response { + migration::cancel_migrate() + } +} + +impl MachineInterface for StdMachine {} +impl MachineExternalInterface for StdMachine {} +impl MachineTestInterface for StdMachine {} + +impl EventLoopManager for StdMachine { + fn loop_should_exit(&self) -> bool { + let vmstate = self.base.vm_state.deref().0.lock().unwrap(); + *vmstate == VmState::Shutdown + } + + fn loop_cleanup(&self) -> Result<()> { + set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; + Ok(()) + } +} + impl MachineAddressInterface for StdMachine { #[cfg(target_arch = "x86_64")] fn pio_in(&self, addr: u64, data: &mut [u8]) -> bool { diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index e31d6929e..2deb47388 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -12,19 +12,16 @@ use std::io::{Seek, SeekFrom}; use std::mem::size_of; -use std::ops::Deref; use std::sync::{Arc, Barrier, Mutex}; use anyhow::{bail, Context, Result}; -use log::{error, info, warn}; -use vmm_sys_util::eventfd::EventFd; use super::ich9_lpc; use super::mch::Mch; use crate::error::MachineError; use crate::standard_common::syscall::syscall_whitelist; use crate::standard_common::{AcpiBuilder, StdMachineOps}; -use crate::{MachineBase, MachineOps}; +use crate::{MachineBase, MachineOps, StdMachine}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, AmlBuilder, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, TableLoader, @@ -44,26 +41,19 @@ use hypervisor::kvm::x86_64::*; use hypervisor::kvm::*; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; -use machine_manager::config::{ - parse_incoming_uri, BootIndexInfo, DriveConfig, MigrateMode, NumaNode, SerialConfig, VmConfig, -}; +use machine_manager::config::{BootIndexInfo, DriveConfig, NumaNode, SerialConfig, VmConfig}; use machine_manager::event; -use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{ - MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, - MigrateInterface, VmState, -}; -use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; +use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; #[cfg(feature = "gtk")] use ui::gtk::gtk_display_init; #[cfg(feature = "vnc")] use ui::vnc::vnc_init; use util::byte_code::ByteCode; -use util::loop_context::{create_new_eventfd, EventLoopManager}; +use util::gen_base_func; +use util::loop_context::create_new_eventfd; use util::seccomp::BpfRule; use util::seccomp::SeccompCmpOpt; -use util::{gen_base_func, set_termi_canon_mode}; pub(crate) const VENDOR_ID_INTEL: u16 = 0x8086; const HOLE_640K_START: u64 = 0x000A_0000; @@ -113,26 +103,6 @@ const IRQ_MAP: &[(i32, i32)] = &[ (16, 19), // Pcie ]; -/// Standard machine structure. -pub struct StdMachine { - // Machine base members. - base: MachineBase, - /// PCI/PCIe host bridge. - pci_host: Arc>, - /// Reset request, handle VM `Reset` event. - reset_req: Arc, - /// Shutdown_req, handle VM 'ShutDown' event. - shutdown_req: Arc, - /// VM power button, handle VM `Powerdown` event. - power_button: Arc, - /// CPU Resize request, handle vm cpu hot(un)plug event. - cpu_resize_req: Arc, - /// List contains the boot order of boot devices. - boot_order_list: Arc>>, - /// Cpu Controller. - cpu_controller: Option>>, -} - impl StdMachine { pub fn new(vm_config: &VmConfig) -> Result { let free_irqs = ( @@ -208,27 +178,6 @@ impl StdMachine { Ok(()) } - pub fn handle_destroy_request(vm: &Arc>) -> bool { - let locked_vm = vm.lock().unwrap(); - let vmstate = { - let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); - *state - }; - - if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { - warn!("Failed to destroy guest, destroy continue."); - if locked_vm.shutdown_req.write(1).is_err() { - error!("Failed to send shutdown request.") - } - return false; - } - - EventLoop::kick_all(); - info!("vm destroy"); - - true - } - fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); @@ -970,90 +919,3 @@ impl AcpiBuilder for StdMachine { Ok(srat_begin) } } - -impl MachineLifecycle for StdMachine { - fn pause(&self) -> bool { - if self.notify_lifecycle(VmState::Running, VmState::Paused) { - event!(Stop); - true - } else { - false - } - } - - fn resume(&self) -> bool { - if !self.notify_lifecycle(VmState::Paused, VmState::Running) { - return false; - } - event!(Resume); - true - } - - fn destroy(&self) -> bool { - if self.shutdown_req.write(1).is_err() { - error!("Failed to send shutdown request."); - return false; - } - - true - } - - fn reset(&mut self) -> bool { - if self.reset_req.write(1).is_err() { - error!("X86 standard vm write reset request failed"); - return false; - } - true - } - - fn notify_lifecycle(&self, old: VmState, new: VmState) -> bool { - if let Err(e) = self.vm_state_transfer( - &self.base.cpus, - &mut self.base.vm_state.0.lock().unwrap(), - old, - new, - ) { - error!("VM state transfer failed: {:?}", e); - return false; - } - true - } -} - -impl MigrateInterface for StdMachine { - fn migrate(&self, uri: String) -> Response { - match parse_incoming_uri(&uri) { - Ok((MigrateMode::File, path)) => migration::snapshot(path), - Ok((MigrateMode::Unix, path)) => migration::migration_unix_mode(path), - Ok((MigrateMode::Tcp, path)) => migration::migration_tcp_mode(path), - _ => Response::create_error_response( - qmp_schema::QmpErrorClass::GenericError(format!("Invalid uri: {}", uri)), - None, - ), - } - } - - fn query_migrate(&self) -> Response { - migration::query_migrate() - } - - fn cancel_migrate(&self) -> Response { - migration::cancel_migrate() - } -} - -impl MachineInterface for StdMachine {} -impl MachineExternalInterface for StdMachine {} -impl MachineTestInterface for StdMachine {} - -impl EventLoopManager for StdMachine { - fn loop_should_exit(&self) -> bool { - let vmstate = self.base.vm_state.deref().0.lock().unwrap(); - *vmstate == VmState::Shutdown - } - - fn loop_cleanup(&self) -> Result<()> { - set_termi_canon_mode().with_context(|| "Failed to set terminal to canonical mode")?; - Ok(()) - } -} -- Gitee From af9b9d569cf1bcaee48c37568552d15d1b93ac7e Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jun 2024 08:03:37 +0800 Subject: [PATCH 1901/2187] micro_vm: execute the shutdown action in the main thread In Hongmeng, the VCPU status is queried when the virtual machine shuts down. In micro VM, the VCPU is already trapped for executing a shutdown action and will not change the state of the VCPU, while shutting down will keep querying for state changes, resulting in a loop timeout. This leads to the micro vm getting stuck when shutting down on hongmeng. Move the shutdown action of the micro VM to the main thread, so that the VCPU thread will not get stuck by itself and the shutdown can proceed normally. Signed-off-by: liuxiangdong --- machine/src/aarch64/micro.rs | 4 +- machine/src/aarch64/standard.rs | 7 ++-- machine/src/lib.rs | 60 ++++++++++++++++++++++++++---- machine/src/micro_common/mod.rs | 24 ++++++------ machine/src/standard_common/mod.rs | 52 +------------------------- machine/src/x86_64/micro.rs | 7 ++-- machine/src/x86_64/standard.rs | 10 ++--- 7 files changed, 80 insertions(+), 84 deletions(-) diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 39fed7e4d..e43cf5eb6 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use crate::{micro_common::syscall::syscall_whitelist, MachineBase, MachineError}; -use crate::{LightMachine, MachineOps}; +use crate::{register_shutdown_event, LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; @@ -193,6 +193,8 @@ impl MachineOps for LightMachine { fdt_vec.len() as u64, ) .with_context(|| MachineError::WrtFdtErr(boot_config.fdt_addr, fdt_vec.len()))?; + register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) + .with_context(|| "Failed to register shutdown event")?; MigrationManager::register_vm_instance(vm.clone()); MigrationManager::register_migration_instance(locked_vm.base.migration_hypervisor.clone()); diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index a3cb4dbd1..b731c0d3c 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -24,7 +24,7 @@ use clap::Parser; use super::pci_host_root::PciHostRoot; use crate::standard_common::syscall::syscall_whitelist; use crate::standard_common::{AcpiBuilder, StdMachineOps}; -use crate::{MachineBase, MachineOps, StdMachine}; +use crate::{register_shutdown_event, MachineBase, MachineOps, StdMachine}; use acpi::{ processor_append_priv_res, AcpiGicCpu, AcpiGicDistributor, AcpiGicIts, AcpiGicRedistributor, AcpiSratGiccAffinity, AcpiSratMemoryAffinity, AcpiTable, AmlBuilder, AmlDevice, AmlInteger, @@ -524,9 +524,8 @@ impl MachineOps for StdMachine { let nr_cpus = vm_config.machine_config.nr_cpus; let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; - locked_vm - .register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) - .with_context(|| "Fail to register shutdown event")?; + register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) + .with_context(|| "Failed to register shutdown event")?; locked_vm .register_reset_event(locked_vm.reset_req.clone(), vm.clone()) .with_context(|| "Fail to register reset event")?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index a956e1f93..7dccd8f23 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -27,16 +27,18 @@ use std::collections::{BTreeMap, HashMap}; use std::fs::{remove_file, File}; use std::net::TcpListener; use std::ops::Deref; +use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixListener; use std::path::Path; +use std::rc::Rc; use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; #[cfg(feature = "windows_emu_pid")] use std::time::Duration; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; -use log::warn; -#[cfg(feature = "windows_emu_pid")] +use log::{info, warn}; +use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] @@ -83,16 +85,19 @@ use machine_manager::config::{ VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{HypervisorType, MachineInterface, PauseNotify, VmState}; +use machine_manager::machine::{ + HypervisorType, MachineInterface, MachineLifecycle, PauseNotify, VmState, +}; use machine_manager::{check_arg_exist, check_arg_nonexist}; use migration::{MigrateOps, MigrationManager}; #[cfg(feature = "windows_emu_pid")] use ui::console::{get_run_stage, VmRunningStage}; +use util::arg_parser; use util::file::{clear_file, lock_file, unlock_file}; -use util::{ - arg_parser, - seccomp::{BpfRule, SeccompOpt, SyscallFilter}, +use util::loop_context::{ + gen_delete_notifiers, EventNotifier, NotifierCallback, NotifierOperation, }; +use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; @@ -304,7 +309,7 @@ macro_rules! create_device_add_matches { }; } -pub trait MachineOps { +pub trait MachineOps: MachineLifecycle { fn machine_base(&self) -> &MachineBase; fn machine_base_mut(&mut self) -> &mut MachineBase; @@ -2251,6 +2256,47 @@ pub trait MachineOps { } } +fn register_shutdown_event( + shutdown_req: Arc, + vm: Arc>, +) -> Result<()> { + let shutdown_req_fd = shutdown_req.as_raw_fd(); + let shutdown_req_handler: Rc = Rc::new(move |_, _| { + let _ret = shutdown_req.read(); + if handle_destroy_request(&vm) { + Some(gen_delete_notifiers(&[shutdown_req_fd])) + } else { + None + } + }); + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + shutdown_req_fd, + None, + EventSet::IN, + vec![shutdown_req_handler], + ); + EventLoop::update_event(vec![notifier], None) + .with_context(|| "Failed to register event notifier.") +} + +fn handle_destroy_request(vm: &Arc>) -> bool { + let locked_vm = vm.lock().unwrap(); + let vmstate: VmState = { + let state = locked_vm.machine_base().vm_state.deref().0.lock().unwrap(); + *state + }; + + if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { + return false; + } + + info!("vm destroy"); + EventLoop::kick_all(); + + true +} + /// Normal run or resume virtual machine from migration/snapshot. /// /// # Arguments diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 8aee4003b..03dd03564 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -39,7 +39,8 @@ use std::vec::Vec; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; -use log::{error, info}; +use log::error; +use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "aarch64")] use crate::aarch64::micro::{LayoutEntryType, MEM_LAYOUT}; @@ -52,7 +53,6 @@ use machine_manager::config::{ get_chardev_socket_path, parse_incoming_uri, str_slip_to_clap, ConfigCheck, DriveConfig, MigrateMode, NetDevcfg, NetworkInterfaceConfig, VmConfig, }; -use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ DeviceInterface, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, MigrateInterface, VmState, @@ -62,7 +62,8 @@ use machine_manager::qmp::{ }; use machine_manager::{check_arg_nonexist, event}; use migration::MigrationManager; -use util::{loop_context::EventLoopManager, num_ops::str_to_num, set_termi_canon_mode}; +use util::loop_context::{create_new_eventfd, EventLoopManager}; +use util::{num_ops::str_to_num, set_termi_canon_mode}; use virtio::device::block::VirtioBlkDevConfig; use virtio::{ create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VhostUser, @@ -134,6 +135,8 @@ pub struct LightMachine { pub(crate) base: MachineBase, // All replaceable device information. pub(crate) replaceable_info: MmioReplaceableInfo, + /// Shutdown request, handle VM `shutdown` event. + pub(crate) shutdown_req: Arc, } impl LightMachine { @@ -153,6 +156,10 @@ impl LightMachine { Ok(LightMachine { base, replaceable_info: MmioReplaceableInfo::new(), + shutdown_req: Arc::new( + create_new_eventfd() + .with_context(|| MachineError::InitEventFdErr("shutdown_req".to_string()))?, + ), }) } @@ -514,18 +521,11 @@ impl MachineLifecycle for LightMachine { } fn destroy(&self) -> bool { - let vmstate = { - let state = self.base.vm_state.deref().0.lock().unwrap(); - *state - }; - - if !self.notify_lifecycle(vmstate, VmState::Shutdown) { + if self.shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request."); return false; } - info!("vm destroy"); - EventLoop::kick_all(); - true } diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index c8617ef95..d34df77a3 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -24,7 +24,7 @@ use std::sync::{Arc, Mutex}; use std::u64; use anyhow::{bail, Context, Result}; -use log::{error, info, warn}; +use log::error; use util::set_termi_canon_mode; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -123,29 +123,6 @@ pub struct StdMachine { pub(crate) ohui_server: Option>, } -impl StdMachine { - pub fn handle_destroy_request(vm: &Arc>) -> bool { - let locked_vm = vm.lock().unwrap(); - let vmstate = { - let state = locked_vm.base.vm_state.deref().0.lock().unwrap(); - *state - }; - - if !locked_vm.notify_lifecycle(vmstate, VmState::Shutdown) { - warn!("Failed to destroy guest, destroy continue."); - if locked_vm.shutdown_req.write(1).is_err() { - error!("Failed to send shutdown request.") - } - return false; - } - - EventLoop::kick_all(); - info!("vm destroy"); - - true - } -} - pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn init_pci_host(&self) -> Result<()>; @@ -392,33 +369,6 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { EventLoop::update_event(vec![notifier], None) .with_context(|| "Failed to register event notifier.") } - - fn register_shutdown_event( - &self, - shutdown_req: Arc, - clone_vm: Arc>, - ) -> Result<()> { - use util::loop_context::gen_delete_notifiers; - - let shutdown_req_fd = shutdown_req.as_raw_fd(); - let shutdown_req_handler: Rc = Rc::new(move |_, _| { - let _ret = shutdown_req.read(); - if StdMachine::handle_destroy_request(&clone_vm) { - Some(gen_delete_notifiers(&[shutdown_req_fd])) - } else { - None - } - }); - let notifier = EventNotifier::new( - NotifierOperation::AddShared, - shutdown_req_fd, - None, - EventSet::IN, - vec![shutdown_req_handler], - ); - EventLoop::update_event(vec![notifier], None) - .with_context(|| "Failed to register event notifier.") - } } /// Trait that helps to build ACPI tables. diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 219df0bd0..f5b19b7f0 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -14,9 +14,8 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use crate::{ - micro_common::syscall::syscall_whitelist, LightMachine, MachineBase, MachineError, MachineOps, -}; +use crate::micro_common::syscall::syscall_whitelist; +use crate::{register_shutdown_event, LightMachine, MachineBase, MachineError, MachineOps}; use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; use devices::legacy::FwCfgOps; @@ -179,6 +178,8 @@ impl MachineOps for LightMachine { &topology, &boot_config, )?); + register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) + .with_context(|| "Failed to register shutdown event")?; MigrationManager::register_vm_instance(vm.clone()); let migration_hyp = locked_vm.base.migration_hypervisor.clone(); diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 2deb47388..089cca508 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -21,7 +21,7 @@ use super::mch::Mch; use crate::error::MachineError; use crate::standard_common::syscall::syscall_whitelist; use crate::standard_common::{AcpiBuilder, StdMachineOps}; -use crate::{MachineBase, MachineOps, StdMachine}; +use crate::{register_shutdown_event, MachineBase, MachineOps, StdMachine}; use acpi::{ AcpiIoApic, AcpiLocalApic, AcpiSratMemoryAffinity, AcpiSratProcessorAffinity, AcpiTable, AmlBuilder, AmlInteger, AmlNameDecl, AmlPackage, AmlScope, AmlScopeBuilder, TableLoader, @@ -179,7 +179,6 @@ impl StdMachine { } fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { - let clone_vm = vm.clone(); let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let ich = ich9_lpc::LPCBridge::new( root_bus, @@ -187,9 +186,9 @@ impl StdMachine { self.reset_req.clone(), self.shutdown_req.clone(), )?; - self.register_reset_event(self.reset_req.clone(), vm) + self.register_reset_event(self.reset_req.clone(), vm.clone()) .with_context(|| "Fail to register reset event in LPC")?; - self.register_shutdown_event(ich.shutdown_req.clone(), clone_vm) + register_shutdown_event(ich.shutdown_req.clone(), vm) .with_context(|| "Fail to register shutdown event in LPC")?; ich.realize() } @@ -483,7 +482,6 @@ impl MachineOps for StdMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let max_cpus = vm_config.machine_config.max_cpus; - let clone_vm = vm.clone(); let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; @@ -501,7 +499,7 @@ impl MachineOps for StdMachine { .init_pci_host() .with_context(|| MachineError::InitPCIeHostErr)?; locked_vm - .init_ich9_lpc(clone_vm) + .init_ich9_lpc(vm.clone()) .with_context(|| "Fail to init LPC bridge")?; locked_vm.add_devices(vm_config)?; -- Gitee From 3fad00f58f4855f1b542e01668288c12928b6f9b Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 23 May 2024 15:14:49 +0800 Subject: [PATCH 1902/2187] devices: delete parameters in funtion `realize` for all devices Delete parameters(except `bus`) in funtion `realize` for all devices, and move them to function `new`. This is the first step to unify the use of the `realize` function which will be moved to `device` trait. Note: the `bus` parameter in function `realize` was not deleted as it will be removed in subsequent patches. Signed-off-by: liuxiangdong --- devices/src/acpi/cpu_controller.rs | 26 ++++--- devices/src/acpi/ged.rs | 37 +++++----- devices/src/acpi/power.rs | 26 ++++--- devices/src/interrupt_controller/mod.rs | 4 ++ devices/src/legacy/fwcfg.rs | 46 ++++++------ devices/src/legacy/pflash.rs | 91 ++++++++++++------------ devices/src/legacy/pl011.rs | 46 ++++++------ devices/src/legacy/pl031.rs | 34 +++++---- devices/src/legacy/rtc.rs | 27 ++++--- devices/src/legacy/serial.rs | 41 ++++++----- devices/src/misc/scream/mod.rs | 31 ++++---- devices/src/scsi/disk.rs | 7 +- devices/src/usb/storage.rs | 3 +- devices/src/usb/uas.rs | 3 +- machine/src/aarch64/micro.rs | 40 ++++++----- machine/src/aarch64/standard.rs | 94 +++++++++++++++---------- machine/src/lib.rs | 27 ++++--- machine/src/micro_common/mod.rs | 31 +++++--- machine/src/x86_64/micro.rs | 9 ++- machine/src/x86_64/standard.rs | 70 +++++++++--------- virtio/src/lib.rs | 26 +++++++ virtio/src/transport/virtio_mmio.rs | 59 ++++++---------- 22 files changed, 425 insertions(+), 353 deletions(-) diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index c3052f61b..9e2383d5c 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -88,20 +88,30 @@ pub struct CpuController { } impl CpuController { - pub fn realize( - mut self, - sysbus: &mut SysBus, + pub fn new( max_cpus: u8, + sysbus: &mut SysBus, region_base: u64, region_size: u64, cpu_config: CpuConfig, hotplug_cpu_req: Arc, - ) -> Result>> { - self.max_cpus = max_cpus; - self.cpu_config = Some(cpu_config); - self.hotplug_cpu_req = Some(hotplug_cpu_req); - self.set_sys_resource(sysbus, region_base, region_size, "CPUController") + boot_vcpus: Vec>, + ) -> Result { + let mut cpu_controller = CpuController { + max_cpus, + cpu_config: Some(cpu_config), + hotplug_cpu_req: Some(hotplug_cpu_req), + ..Default::default() + }; + cpu_controller + .set_sys_resource(sysbus, region_base, region_size, "CPUController") .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; + cpu_controller.set_boot_vcpu(boot_vcpus)?; + + Ok(cpu_controller) + } + + pub fn realize(self, sysbus: &mut SysBus) -> Result>> { let dev = Arc::new(Mutex::new(self)); let ret_dev = dev.clone(); sysbus.attach_device(&dev)?; diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index fe1538423..7c3c92a42 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -54,6 +54,7 @@ pub enum AcpiEvent { const AML_GED_EVT_REG: &str = "EREG"; const AML_GED_EVT_SEL: &str = "ESEL"; +#[derive(Clone)] pub struct GedEvent { power_button: Arc, #[cfg(target_arch = "x86_64")] @@ -78,32 +79,32 @@ pub struct Ged { base: SysBusDevBase, notification_type: Arc, battery_present: bool, -} - -impl Default for Ged { - fn default() -> Self { - Self { - base: SysBusDevBase::default(), - notification_type: Arc::new(AtomicU32::new(AcpiEvent::Nothing as u32)), - battery_present: false, - } - } + ged_event: GedEvent, } impl Ged { - pub fn realize( - mut self, - sysbus: &mut SysBus, - ged_event: GedEvent, + pub fn new( battery_present: bool, + sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> Result>> { - self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); - self.set_sys_resource(sysbus, region_base, region_size, "Ged") + ged_event: GedEvent, + ) -> Result { + let mut ged = Self { + base: SysBusDevBase::default(), + notification_type: Arc::new(AtomicU32::new(AcpiEvent::Nothing as u32)), + battery_present, + ged_event, + }; + ged.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); + ged.set_sys_resource(sysbus, region_base, region_size, "Ged") .with_context(|| AcpiError::Alignment(region_size as u32))?; - self.battery_present = battery_present; + Ok(ged) + } + + pub fn realize(self, sysbus: &mut SysBus) -> Result>> { + let ged_event = self.ged_event.clone(); let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev)?; diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 8669e8b39..1da560629 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -81,8 +81,13 @@ pub struct PowerDev { } impl PowerDev { - pub fn new(ged_dev: Arc>) -> Self { - Self { + pub fn new( + ged_dev: Arc>, + sysbus: &mut SysBus, + region_base: u64, + region_size: u64, + ) -> Result { + let mut pdev = Self { base: SysBusDevBase::default(), regs: vec![0; POWERDEV_REGS_SIZE], state: PowerDevState { @@ -91,7 +96,10 @@ impl PowerDev { last_bat_lvl: 0xffffffff, }, ged: ged_dev, - } + }; + pdev.set_sys_resource(sysbus, region_base, region_size, "PowerDev") + .with_context(|| AcpiError::Alignment(region_size as u32))?; + Ok(pdev) } fn read_sysfs_power_props( @@ -175,18 +183,8 @@ impl PowerDev { fn send_power_event(&self, evt: AcpiEvent) { self.ged.lock().unwrap().inject_acpi_event(evt); } -} - -impl PowerDev { - pub fn realize( - mut self, - sysbus: &mut SysBus, - region_base: u64, - region_size: u64, - ) -> Result<()> { - self.set_sys_resource(sysbus, region_base, region_size, "PowerDev") - .with_context(|| AcpiError::Alignment(region_size as u32))?; + pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev)?; diff --git a/devices/src/interrupt_controller/mod.rs b/devices/src/interrupt_controller/mod.rs index a1fb65d2e..d1b04a168 100644 --- a/devices/src/interrupt_controller/mod.rs +++ b/devices/src/interrupt_controller/mod.rs @@ -142,6 +142,10 @@ impl IrqState { } pub fn register_irq(&mut self) -> Result<()> { + if self.irq_handler.is_none() { + return Ok(()); + } + let irq_handler = self.irq_handler.as_ref().unwrap(); if !irq_handler.irqfd_enable() { self.irq_fd = None; diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 5e587f6ec..3f18833df 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -842,23 +842,25 @@ pub struct FwCfgMem { #[cfg(target_arch = "aarch64")] impl FwCfgMem { - pub fn new(sys_mem: Arc) -> Self { - FwCfgMem { - base: SysBusDevBase::new(SysBusDevType::FwCfg), - fwcfg: FwCfgCommon::new(sys_mem), - } - } - - pub fn realize( - mut self, + pub fn new( + sys_mem: Arc, sysbus: &mut SysBus, region_base: u64, region_size: u64, - ) -> Result>> { - self.fwcfg.common_realize()?; - self.set_sys_resource(sysbus, region_base, region_size, "FwCfgMem") + ) -> Result { + let mut fwcfgmem = FwCfgMem { + base: SysBusDevBase::new(SysBusDevType::FwCfg), + fwcfg: FwCfgCommon::new(sys_mem), + }; + fwcfgmem + .set_sys_resource(sysbus, region_base, region_size, "FwCfgMem") .with_context(|| "Failed to allocate system resource for FwCfg.")?; + Ok(fwcfgmem) + } + + pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { + self.fwcfg.common_realize()?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev) @@ -1004,18 +1006,20 @@ pub struct FwCfgIO { #[cfg(target_arch = "x86_64")] impl FwCfgIO { - pub fn new(sys_mem: Arc) -> Self { - FwCfgIO { + pub fn new(sys_mem: Arc, sysbus: &mut SysBus) -> Result { + let mut fwcfg = FwCfgIO { base: SysBusDevBase::new(SysBusDevType::FwCfg), fwcfg: FwCfgCommon::new(sys_mem), - } + }; + fwcfg + .set_sys_resource(sysbus, FW_CFG_IO_BASE, FW_CFG_IO_SIZE, "FwCfgIO") + .with_context(|| "Failed to allocate system resource for FwCfg.")?; + + Ok(fwcfg) } pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { self.fwcfg.common_realize()?; - self.set_sys_resource(sysbus, FW_CFG_IO_BASE, FW_CFG_IO_SIZE, "FwCfgIO") - .with_context(|| "Failed to allocate system resource for FwCfg.")?; - let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev) @@ -1478,9 +1482,9 @@ mod test { fn test_read_write_aarch64() { let mut sys_bus = sysbus_init(); let sys_mem = address_space_init(); - let fwcfg = FwCfgMem::new(sys_mem); + let fwcfg = FwCfgMem::new(sys_mem, &mut sys_bus, 0x0902_0000, 0x0000_0018).unwrap(); - let fwcfg_dev = FwCfgMem::realize(fwcfg, &mut sys_bus, 0x0902_0000, 0x0000_0018).unwrap(); + let fwcfg_dev = fwcfg.realize(&mut sys_bus).unwrap(); // Read FW_CFG_DMA_SIGNATURE entry. let base = GuestAddress(0x0000); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; @@ -1518,7 +1522,7 @@ mod test { fn test_read_write_x86_64() { let mut sys_bus = sysbus_init(); let sys_mem = address_space_init(); - let fwcfg = FwCfgIO::new(sys_mem); + let fwcfg = FwCfgIO::new(sys_mem, &mut sys_bus).unwrap(); let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut sys_bus).unwrap(); // Read FW_CFG_DMA_SIGNATURE entry. diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 2db2ce228..7eec7cca3 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -58,6 +58,8 @@ pub struct PFlash { write_blk_size: u32, /// ROM region of PFlash. rom: Option, + /// backend: Option, + host_mmap: Arc, } impl PFlash { @@ -99,18 +101,21 @@ impl PFlash { /// * block-length is zero. /// * PFlash size is zero. /// * flash is writable and file size is smaller than region_max_size. + #[allow(clippy::too_many_arguments)] pub fn new( region_max_size: u64, - backend: &Option, + backend: Option, block_len: u32, bank_width: u32, device_width: u32, read_only: bool, + sysbus: &mut SysBus, + region_base: u64, ) -> Result { if block_len == 0 { bail!("PFlash: block-length is zero which is invalid."); } - let size = Self::flash_region_size(region_max_size, backend, read_only)?; + let size = Self::flash_region_size(region_max_size, &backend, read_only)?; let blocks_per_device: u32 = size as u32 / block_len; if blocks_per_device == 0 { bail!("PFlash: num-blocks is zero which is invalid."); @@ -187,9 +192,21 @@ impl PFlash { // Number of protection fields. cfi_table[0x3f] = 0x01; - Ok(PFlash { + let has_backend = backend.is_some(); + let region_size = Self::flash_region_size(region_max_size, &backend, read_only)?; + let host_mmap = Arc::new(HostMemMapping::new( + GuestAddress(region_base), + None, + region_size, + backend.map(FileBackend::new_common), + false, + true, + read_only, + )?); + + let mut pflash = PFlash { base: SysBusDevBase::new(SysBusDevType::Flash), - has_backend: backend.is_some(), + has_backend, block_len, bank_width, // device id for Intel PFlash. @@ -204,33 +221,20 @@ impl PFlash { counter: 0, write_blk_size, rom: None, - }) - } + host_mmap, + }; - pub fn realize( - mut self, - sysbus: &mut SysBus, - region_base: u64, - region_max_size: u64, - backend: Option, - ) -> Result<()> { - let region_size = Self::flash_region_size(region_max_size, &backend, self.read_only)?; - self.set_sys_resource(sysbus, region_base, region_size, "PflashRom") + pflash + .set_sys_resource(sysbus, region_base, region_size, "PflashRom") .with_context(|| "Failed to allocate system resource for PFlash.")?; + Ok(pflash) + } - let host_mmap = Arc::new(HostMemMapping::new( - GuestAddress(region_base), - None, - region_size, - backend.map(FileBackend::new_common), - false, - true, - self.read_only, - )?); - + pub fn realize(self, sysbus: &mut SysBus) -> Result>> { + let region_base = self.base.res.region_base; + let host_mmap = self.host_mmap.clone(); let dev = Arc::new(Mutex::new(self)); let region_ops = sysbus.build_region_ops(&dev); - let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "PflashRom"); dev.lock().unwrap().rom = Some(rom_region.clone()); sysbus @@ -238,9 +242,9 @@ impl PFlash { .root() .add_subregion(rom_region, region_base) .with_context(|| "Failed to attach PFlash to system bus")?; - sysbus.devices.push(dev); + sysbus.devices.push(dev.clone()); - Ok(()) + Ok(dev) } fn set_read_array_mode(&mut self, is_illegal_cmd: bool) -> Result<()> { @@ -938,25 +942,20 @@ mod test { .open(file_name) .unwrap(), ); - let pflash = PFlash::new(flash_size, &fd, sector_len, 4, 2, read_only).unwrap(); - let sysbus = sysbus_init(); - let dev = Arc::new(Mutex::new(pflash)); - let region_ops = sysbus.build_region_ops(&dev); - let host_mmap = Arc::new( - HostMemMapping::new( - GuestAddress(flash_base), - None, - flash_size, - fd.map(FileBackend::new_common), - false, - true, - false, - ) - .unwrap(), - ); + let mut sysbus = sysbus_init(); + let pflash = PFlash::new( + flash_size, + fd, + sector_len, + 4, + 2, + read_only, + &mut sysbus, + flash_base, + ) + .unwrap(); + let dev = pflash.realize(&mut sysbus).unwrap(); - let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "pflash-dev"); - dev.lock().unwrap().rom = Some(rom_region); dev } diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 2a6cab1c1..293ec750a 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -25,10 +25,8 @@ use acpi::{ }; use address_space::GuestAddress; use chardev_backend::chardev::{Chardev, InputReceiver}; -use machine_manager::{ - config::{BootSource, Param, SerialConfig}, - event_loop::EventLoop, -}; +use machine_manager::config::SerialConfig; +use machine_manager::event_loop::EventLoop; use migration::{ snapshot::PL011_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, @@ -131,8 +129,13 @@ pub struct PL011 { impl PL011 { /// Create a new `PL011` instance with default parameters. - pub fn new(cfg: SerialConfig) -> Result { - Ok(PL011 { + pub fn new( + cfg: SerialConfig, + sysbus: &mut SysBus, + region_base: u64, + region_size: u64, + ) -> Result { + let mut pl011 = PL011 { base: SysBusDevBase { dev_type: SysBusDevType::PL011, interrupt_evt: Some(Arc::new(create_new_eventfd()?)), @@ -141,7 +144,12 @@ impl PL011 { paused: false, state: PL011State::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), - }) + }; + pl011 + .set_sys_resource(sysbus, region_base, region_size, "PL011") + .with_context(|| "Failed to set system resource for PL011.")?; + + Ok(pl011) } fn interrupt(&mut self) { @@ -154,30 +162,16 @@ impl PL011 { } } - pub fn realize( - mut self, - sysbus: &mut SysBus, - region_base: u64, - region_size: u64, - bs: &Arc>, - ) -> Result<()> { + pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { self.chardev .lock() .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; - self.set_sys_resource(sysbus, region_base, region_size, "PL011") - .with_context(|| "Failed to set system resource for PL011.")?; - let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev) .with_context(|| "Failed to attach PL011 to system bus.")?; - - bs.lock().unwrap().kernel_cmdline.push(Param { - param_type: "earlycon".to_string(), - value: format!("pl011,mmio,0x{:08x}", region_base), - }); MigrationManager::register_device_instance( PL011State::descriptor(), dev.clone(), @@ -455,6 +449,7 @@ impl AmlBuilder for PL011 { #[cfg(test)] mod test { use super::*; + use crate::sysbus::sysbus_init; use machine_manager::config::{ChardevConfig, ChardevType}; #[test] @@ -464,10 +459,11 @@ mod test { id: "chardev".to_string(), }, }; - let mut pl011_dev = PL011::new(SerialConfig { + let config = SerialConfig { chardev: chardev_cfg, - }) - .unwrap(); + }; + let mut sysbus = sysbus_init(); + let mut pl011_dev = PL011::new(config, &mut sysbus, 0x0900_0000, 0x0000_1000).unwrap(); assert_eq!(pl011_dev.state.rfifo, [0; PL011_FIFO_SIZE]); assert_eq!(pl011_dev.state.flags, 0x90); assert_eq!(pl011_dev.state.lcr, 0); diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index 98a351963..f1d330c78 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -79,9 +79,9 @@ pub struct PL031 { base_time: Instant, } -impl Default for PL031 { - fn default() -> Self { - Self { +impl PL031 { + pub fn new(sysbus: &mut SysBus, region_base: u64, region_size: u64) -> Result { + let mut pl031 = Self { base: SysBusDevBase::new(SysBusDevType::Rtc), state: PL031State::default(), // since 1970-01-01 00:00:00,it never cause overflow. @@ -90,21 +90,16 @@ impl Default for PL031 { .expect("time wrong") .as_secs() as u32, base_time: Instant::now(), - } - } -} - -impl PL031 { - pub fn realize( - mut self, - sysbus: &mut SysBus, - region_base: u64, - region_size: u64, - ) -> Result<()> { - self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); - self.set_sys_resource(sysbus, region_base, region_size, "PL031") + }; + pl031.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); + pl031 + .set_sys_resource(sysbus, region_base, region_size, "PL031") .with_context(|| LegacyError::SetSysResErr)?; + Ok(pl031) + } + + pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev)?; @@ -219,13 +214,15 @@ impl MigrationHook for PL031 {} #[cfg(test)] mod test { use super::*; + use crate::sysbus::sysbus_init; use util::time::mktime64; const WIGGLE: u32 = 2; #[test] fn test_set_year_20xx() { - let mut rtc = PL031::default(); + let mut sysbus = sysbus_init(); + let mut rtc = PL031::new(&mut sysbus, 0x0901_0000, 0x0000_1000).unwrap(); // Set rtc time: 2013-11-13 02:04:56. let mut wtick = mktime64(2013, 11, 13, 2, 4, 56) as u32; let mut data = [0; 4]; @@ -251,7 +248,8 @@ mod test { #[test] fn test_set_year_1970() { - let mut rtc = PL031::default(); + let mut sysbus = sysbus_init(); + let mut rtc = PL031::new(&mut sysbus, 0x0901_0000, 0x0000_1000).unwrap(); // Set rtc time (min): 1970-01-01 00:00:00. let wtick = mktime64(1970, 1, 1, 0, 0, 0) as u32; let mut data = [0; 4]; diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 259ac9810..d60b870f1 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -16,7 +16,7 @@ use std::time::{Instant, SystemTime, UNIX_EPOCH}; use anyhow::Result; use log::{debug, error, warn}; -use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; +use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::{ AmlBuilder, AmlDevice, AmlEisaId, AmlIoDecode, AmlIoResource, AmlIrqNoFlags, AmlNameDecl, @@ -118,15 +118,10 @@ pub struct RTC { impl RTC { /// Construct function of RTC device. - pub fn new() -> Result { + pub fn new(sysbus: &mut SysBus) -> Result { let mut rtc = RTC { base: SysBusDevBase { dev_type: SysBusDevType::Rtc, - res: SysRes { - region_base: RTC_PORT_INDEX, - region_size: 8, - ..Default::default() - }, interrupt_evt: Some(Arc::new(create_new_eventfd()?)), ..Default::default() }, @@ -147,6 +142,8 @@ impl RTC { rtc.init_rtc_reg(); + rtc.set_sys_resource(sysbus, RTC_PORT_INDEX, 8, "RTC")?; + Ok(rtc) } @@ -267,11 +264,7 @@ impl RTC { true } - pub fn realize(mut self, sysbus: &mut SysBus) -> Result<()> { - let region_base = self.base.res.region_base; - let region_size = self.base.res.region_size; - self.set_sys_resource(sysbus, region_base, region_size, "RTC")?; - + pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev)?; Ok(()) @@ -413,6 +406,7 @@ mod test { use anyhow::Context; use super::*; + use crate::sysbus::sysbus_init; use address_space::GuestAddress; const WIGGLE: u8 = 2; @@ -433,7 +427,8 @@ mod test { #[test] fn test_set_year_20xx() -> Result<()> { - let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + let mut sysbus = sysbus_init(); + let mut rtc = RTC::new(&mut sysbus).with_context(|| "Failed to create RTC device")?; // Set rtc time: 2013-11-13 02:04:56 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x20); cmos_write(&mut rtc, RTC_YEAR, 0x13); @@ -467,7 +462,8 @@ mod test { #[test] fn test_set_year_1970() -> Result<()> { - let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + let mut sysbus = sysbus_init(); + let mut rtc = RTC::new(&mut sysbus).with_context(|| "Failed to create RTC device")?; // Set rtc time (min): 1970-01-01 00:00:00 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); cmos_write(&mut rtc, RTC_YEAR, 0x70); @@ -490,7 +486,8 @@ mod test { #[test] fn test_invalid_rtc_time() -> Result<()> { - let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + let mut sysbus = sysbus_init(); + let mut rtc = RTC::new(&mut sysbus).with_context(|| "Failed to create RTC device")?; // Set rtc year: 1969 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); cmos_write(&mut rtc, RTC_YEAR, 0x69); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 3386f7ff8..05d6929c5 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -124,30 +124,32 @@ pub struct Serial { } impl Serial { - pub fn new(cfg: SerialConfig) -> Self { - Serial { + pub fn new( + cfg: SerialConfig, + sysbus: &mut SysBus, + region_base: u64, + region_size: u64, + ) -> Result { + let mut serial = Serial { base: SysBusDevBase::new(SysBusDevType::Serial), paused: false, rbr: VecDeque::new(), state: SerialState::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), - } + }; + serial.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); + serial + .set_sys_resource(sysbus, region_base, region_size, "Serial") + .with_context(|| LegacyError::SetSysResErr)?; + Ok(serial) } - pub fn realize( - mut self, - sysbus: &mut SysBus, - region_base: u64, - region_size: u64, - ) -> Result<()> { + + pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { self.chardev .lock() .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; - self.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); - self.set_sys_resource(sysbus, region_base, region_size, "Serial") - .with_context(|| LegacyError::SetSysResErr)?; - let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev)?; @@ -459,6 +461,7 @@ impl MigrationHook for Serial {} #[cfg(test)] mod test { use super::*; + use crate::sysbus::sysbus_init; use machine_manager::config::{ChardevConfig, ChardevType}; #[test] @@ -469,9 +472,11 @@ mod test { id: "chardev".to_string(), }, }; - let mut usart = Serial::new(SerialConfig { + let mut sysbus = sysbus_init(); + let config = SerialConfig { chardev: chardev_cfg.clone(), - }); + }; + let mut usart = Serial::new(config, &mut sysbus, SERIAL_ADDR, 8).unwrap(); assert_eq!(usart.state.ier, 0); assert_eq!(usart.state.iir, 1); assert_eq!(usart.state.lcr, 3); @@ -525,9 +530,11 @@ mod test { id: "chardev".to_string(), }, }; - let mut usart = Serial::new(SerialConfig { + let config = SerialConfig { chardev: chardev_cfg, - }); + }; + let mut sysbus = sysbus_init(); + let mut usart = Serial::new(config, &mut sysbus, SERIAL_ADDR, 8).unwrap(); // Get state vector for usart let serial_state_result = usart.get_state_vec(); assert!(serial_state_result.is_ok()); diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index d9c8c5cb5..a583b6c33 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -446,15 +446,27 @@ impl PauseNotify for Scream { } impl Scream { - pub fn new(size: u64, config: ScreamConfig, token_id: Option>>) -> Self { + pub fn new( + size: u64, + config: ScreamConfig, + token_id: Option>>, + ) -> Result { set_record_authority(config.record_auth); - Self { + let header_size = mem::size_of::() as u64; + if size < header_size { + bail!( + "The size {} of the shared memory is smaller than audio header {}", + size, + header_size + ); + } + Ok(Self { hva: 0, size, config, token_id, interface_resource: RwLock::new(Vec::new()), - } + }) } #[allow(unused_variables)] @@ -538,16 +550,7 @@ impl Scream { Ok(()) } - pub fn realize(&mut self, devfn: u8, parent_bus: Weak>) -> Result<()> { - let header_size = mem::size_of::() as u64; - if self.size < header_size { - bail!( - "The size {} of the shared memory is smaller then audio header {}", - self.size, - header_size - ); - } - + pub fn realize(&mut self, parent_bus: Weak>) -> Result<()> { let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(0), None, @@ -559,8 +562,8 @@ impl Scream { )?); self.hva = host_mmap.host_address(); + let devfn = (self.config.addr.0 << 3) + self.config.addr.1; let mem_region = Region::init_ram_region(host_mmap, "ivshmem_ram"); - let ivshmem = Ivshmem::new("ivshmem".to_string(), devfn, parent_bus, mem_region); ivshmem.realize()?; diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 178250a4c..1f88cf0ad 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -161,6 +161,7 @@ pub struct ScsiDevice { drive_files: Arc>>, /// Aio context. pub aio: Option>>>, + pub iothread: Option, } // SAFETY: the devices attached in one scsi controller will process IO in the same thread. @@ -173,6 +174,7 @@ impl ScsiDevice { dev_cfg: ScsiDevConfig, drive_cfg: DriveConfig, drive_files: Arc>>, + iothread: Option, ) -> ScsiDevice { let scsi_type = match dev_cfg.classtype.as_str() { "scsi-hd" => SCSI_TYPE_DISK, @@ -193,10 +195,11 @@ impl ScsiDevice { parent_bus: Weak::new(), drive_files, aio: None, + iothread, } } - pub fn realize(&mut self, iothread: Option) -> Result<()> { + pub fn realize(&mut self) -> Result<()> { match self.scsi_type { SCSI_TYPE_DISK => { self.block_size = SCSI_DISK_DEFAULT_BLOCK_SIZE; @@ -232,7 +235,7 @@ impl ScsiDevice { let conf = BlockProperty { id: drive_id, format: self.drive_cfg.format, - iothread, + iothread: self.iothread.clone(), direct: self.drive_cfg.direct, req_align: self.req_align, buf_align: self.buf_align, diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 67c03488a..429c7b9b1 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -354,6 +354,7 @@ impl UsbStorage { scsi_dev_cfg, drive_cfg, drive_files, + None, ))), }) } @@ -555,7 +556,7 @@ impl UsbDevice for UsbStorage { // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); - locked_scsi_dev.realize(None)?; + locked_scsi_dev.realize()?; drop(locked_scsi_dev); self.scsi_bus .lock() diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index e913ca5cc..e0104272c 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -621,6 +621,7 @@ impl UsbUas { scsi_dev_cfg, drive_cfg, drive_files, + None, ))), commands_high: VecDeque::new(), commands_super: array::from_fn(|_| None), @@ -1050,7 +1051,7 @@ impl UsbDevice for UsbUas { // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. let mut locked_scsi_device = self.scsi_device.lock().unwrap(); - locked_scsi_device.realize(None)?; + locked_scsi_device.realize()?; locked_scsi_device.parent_bus = Arc::downgrade(&self.scsi_bus); drop(locked_scsi_device); self.scsi_bus diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index e43cf5eb6..6b019d94a 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -18,9 +18,10 @@ use crate::{micro_common::syscall::syscall_whitelist, MachineBase, MachineError} use crate::{register_shutdown_event, LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; -use devices::{legacy::PL031, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; +use devices::legacy::{PL011, PL031}; +use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; use hypervisor::kvm::aarch64::*; -use machine_manager::config::{SerialConfig, VmConfig}; +use machine_manager::config::{Param, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; use util::device_tree::{self, CompileFDT, FdtBuilder}; use util::gen_base_func; @@ -105,30 +106,35 @@ impl MachineOps for LightMachine { } fn add_rtc_device(&mut self) -> Result<()> { - PL031::realize( - PL031::default(), + let pl031 = PL031::new( &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, - ) - .with_context(|| "Failed to realize pl031.") + )?; + pl031 + .realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize pl031.") } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { - use devices::legacy::PL011; - let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; - - let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; + let pl011 = PL011::new( + config.clone(), + &mut self.base.sysbus, + region_base, + region_size, + ) + .with_context(|| "Failed to create PL011")?; pl011 - .realize( - &mut self.base.sysbus, - region_base, - region_size, - &self.base.boot_source, - ) - .with_context(|| "Failed to realize PL011") + .realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize PL011")?; + let mut bs = self.base.boot_source.lock().unwrap(); + bs.kernel_cmdline.push(Param { + param_type: "earlycon".to_string(), + value: format!("pl011,mmio,0x{:08x}", region_base), + }); + Ok(()) } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index b731c0d3c..fe8e6a02d 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -57,7 +57,9 @@ use hypervisor::kvm::*; use machine_manager::config::str_slip_to_clap; #[cfg(feature = "gtk")] use machine_manager::config::UiContext; -use machine_manager::config::{BootIndexInfo, DriveConfig, NumaNode, SerialConfig, VmConfig}; +use machine_manager::config::{ + BootIndexInfo, DriveConfig, NumaNode, Param, SerialConfig, VmConfig, +}; use machine_manager::event; use machine_manager::machine::{MachineLifecycle, PauseNotify}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; @@ -343,7 +345,12 @@ impl StdMachineOps for StdMachine { return Ok(None); } - let mut fwcfg = FwCfgMem::new(self.base.sys_mem.clone()); + let mut fwcfg = FwCfgMem::new( + self.base.sys_mem.clone(), + &mut self.base.sysbus, + MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0, + MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, + )?; fwcfg .add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec()) .with_context(|| DevErrorKind::AddEntryErr("NbCpus".to_string()))?; @@ -375,13 +382,9 @@ impl StdMachineOps for StdMachine { .add_file_entry("bios-geometry", bios_geometry) .with_context(|| DevErrorKind::AddEntryErr("bios-geometry".to_string()))?; - let fwcfg_dev = FwCfgMem::realize( - fwcfg, - &mut self.base.sysbus, - MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0, - MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, - ) - .with_context(|| "Failed to realize fwcfg device")?; + let fwcfg_dev = fwcfg + .realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize fwcfg device")?; self.base.fwcfg_dev = Some(fwcfg_dev.clone()); Ok(Some(fwcfg_dev)) @@ -454,36 +457,36 @@ impl MachineOps for StdMachine { } fn add_rtc_device(&mut self) -> Result<()> { - let rtc = PL031::default(); - PL031::realize( - rtc, + let rtc = PL031::new( &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, - ) - .with_context(|| "Failed to realize PL031") + )?; + rtc.realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize PL031") } fn add_ged_device(&mut self) -> Result<()> { let battery_present = self.base.vm_config.lock().unwrap().machine_config.battery; - let ged = Ged::default(); + let ged = Ged::new( + battery_present, + &mut self.base.sysbus, + MEM_LAYOUT[LayoutEntryType::Ged as usize].0, + MEM_LAYOUT[LayoutEntryType::Ged as usize].1, + GedEvent::new(self.power_button.clone()), + )?; let ged_dev = ged - .realize( - &mut self.base.sysbus, - GedEvent::new(self.power_button.clone()), - battery_present, - MEM_LAYOUT[LayoutEntryType::Ged as usize].0, - MEM_LAYOUT[LayoutEntryType::Ged as usize].1, - ) + .realize(&mut self.base.sysbus) .with_context(|| "Failed to realize Ged")?; if battery_present { - let pdev = PowerDev::new(ged_dev); - pdev.realize( + let pdev = PowerDev::new( + ged_dev, &mut self.base.sysbus, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].0, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].1, - ) - .with_context(|| "Failed to realize PowerDev")?; + )?; + pdev.realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize PowerDev")?; } Ok(()) } @@ -491,16 +494,22 @@ impl MachineOps for StdMachine { fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; - - let pl011 = PL011::new(config.clone()).with_context(|| "Failed to create PL011")?; + let pl011 = PL011::new( + config.clone(), + &mut self.base.sysbus, + region_base, + region_size, + ) + .with_context(|| "Failed to create PL011")?; pl011 - .realize( - &mut self.base.sysbus, - region_base, - region_size, - &self.base.boot_source, - ) - .with_context(|| "Failed to realize PL011") + .realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize PL011")?; + let mut bs = self.base.boot_source.lock().unwrap(); + bs.kernel_cmdline.push(Param { + param_type: "earlycon".to_string(), + value: format!("pl011,mmio,0x{:08x}", region_base), + }); + Ok(()) } fn syscall_whitelist(&self) -> Vec { @@ -646,9 +655,18 @@ impl MachineOps for StdMachine { (None, false) }; - let pflash = PFlash::new(flash_size, &fd, sector_len, 4, 2, read_only) - .with_context(|| MachineError::InitPflashErr)?; - PFlash::realize(pflash, &mut self.base.sysbus, flash_base, flash_size, fd) + let pflash = PFlash::new( + flash_size, + fd, + sector_len, + 4, + 2, + read_only, + &mut self.base.sysbus, + flash_base, + ) + .with_context(|| MachineError::InitPflashErr)?; + PFlash::realize(pflash, &mut self.base.sysbus) .with_context(|| MachineError::RlzPflashErr)?; flash_base += flash_size; } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 7dccd8f23..c02f54aba 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1199,16 +1199,10 @@ pub trait MachineOps: MachineLifecycle { .drives .remove(&device_cfg.drive) .with_context(|| "No drive configured matched for scsi device")?; - if let Some(bootindex) = device_cfg.bootindex { self.check_bootindex(bootindex) .with_context(|| "Failed to add scsi device for invalid bootindex")?; } - let device = Arc::new(Mutex::new(ScsiDevice::new( - device_cfg.clone(), - drive_arg, - self.get_drive_files(), - ))); // Bus name `$parent_cntlr_name.0` is checked when parsing by clap. let cntlr = device_cfg.bus.split('.').collect::>()[0].to_string(); @@ -1223,7 +1217,6 @@ pub trait MachineOps: MachineLifecycle { .unwrap(); let virtio_device = virtio_pcidev.get_virtio_device().lock().unwrap(); let cntlr = virtio_device.as_any().downcast_ref::().unwrap(); - let bus = cntlr.bus.as_ref().unwrap(); if bus .lock() @@ -1234,7 +1227,14 @@ pub trait MachineOps: MachineLifecycle { bail!("Wrong! Two scsi devices have the same scsi-id and lun"); } let iothread = cntlr.config.iothread.clone(); - device.lock().unwrap().realize(iothread)?; + + let device = Arc::new(Mutex::new(ScsiDevice::new( + device_cfg.clone(), + drive_arg, + self.get_drive_files(), + iothread, + ))); + device.lock().unwrap().realize()?; bus.lock() .unwrap() .devices @@ -1670,11 +1670,8 @@ pub trait MachineOps: MachineLifecycle { token_id: Option>>, ) -> Result<()> { let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; - let bdf = PciBdf { - bus: config.bus.clone(), - addr: config.addr, - }; - let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; + let bdf = PciBdf::new(config.bus.clone(), config.addr); + let (_, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let mem_cfg = vm_config .object @@ -1691,9 +1688,9 @@ pub trait MachineOps: MachineLifecycle { bail!("Object for share config is not on"); } - let mut scream = Scream::new(mem_cfg.size, config, token_id); + let mut scream = Scream::new(mem_cfg.size, config, token_id)?; scream - .realize(devfn, parent_bus) + .realize(parent_bus) .with_context(|| "Failed to realize scream device")?; self.register_vm_pause_notifier(Arc::new(scream)); Ok(()) diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 03dd03564..349efe21f 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -48,7 +48,11 @@ use crate::aarch64::micro::{LayoutEntryType, MEM_LAYOUT}; use crate::x86_64::micro::{LayoutEntryType, MEM_LAYOUT}; use crate::{MachineBase, MachineError, MachineOps}; use cpu::CpuLifecycleState; +#[cfg(target_arch = "x86_64")] +use devices::sysbus::SysBusDevOps; use devices::sysbus::{IRQ_BASE, IRQ_MAX}; +#[cfg(target_arch = "x86_64")] +use machine_manager::config::Param; use machine_manager::config::{ get_chardev_socket_path, parse_incoming_uri, str_slip_to_clap, ConfigCheck, DriveConfig, MigrateMode, NetDevcfg, NetworkInterfaceConfig, VmConfig, @@ -483,21 +487,30 @@ impl LightMachine { device: Arc>, ) -> Result>> { let sys_mem = self.get_sys_mem().clone(); - let dev = VirtioMmioDevice::new(&sys_mem, name, device); - let region_base = self.base.sysbus.min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; - let realized_virtio_mmio_device = VirtioMmioDevice::realize( - dev, + let dev = VirtioMmioDevice::new( + &sys_mem, + name, + device, &mut self.base.sysbus, region_base, region_size, - #[cfg(target_arch = "x86_64")] - &self.base.boot_source, - ) - .with_context(|| MachineError::RlzVirtioMmioErr)?; + )?; + let mmio_device = dev + .realize(&mut self.base.sysbus) + .with_context(|| MachineError::RlzVirtioMmioErr)?; + #[cfg(target_arch = "x86_64")] + { + let res = mmio_device.lock().unwrap().get_sys_resource().clone(); + let mut bs = self.base.boot_source.lock().unwrap(); + bs.kernel_cmdline.push(Param { + param_type: "virtio_mmio.device".to_string(), + value: format!("{}@0x{:08x}:{}", res.region_size, res.region_base, res.irq), + }); + } self.base.sysbus.min_free_base += region_size; - Ok(realized_virtio_mmio_device) + Ok(mmio_device) } } diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index f5b19b7f0..0d88aaec5 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -132,9 +132,14 @@ impl MachineOps for LightMachine { let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; - let serial = Serial::new(config.clone()); + let serial = Serial::new( + config.clone(), + &mut self.base.sysbus, + region_base, + region_size, + )?; serial - .realize(&mut self.base.sysbus, region_base, region_size) + .realize(&mut self.base.sysbus) .with_context(|| "Failed to realize serial device.") } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 089cca508..dc970a335 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -208,8 +208,6 @@ impl StdMachine { cpu_topology: CPUTopology, vm: Arc>, ) -> Result<()> { - let cpu_controller: CpuController = Default::default(); - let region_base: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].1; let cpu_config = CpuConfig::new(boot_config, cpu_topology); @@ -217,22 +215,18 @@ impl StdMachine { create_new_eventfd() .with_context(|| MachineError::InitEventFdErr("hotplug cpu".to_string()))?, ); - + let cpu_controller = CpuController::new( + self.base.cpu_topo.max_cpus, + &mut self.base.sysbus, + region_base, + region_size, + cpu_config, + hotplug_cpu_req.clone(), + self.base.cpus.clone(), + )?; let realize_controller = cpu_controller - .realize( - &mut self.base.sysbus, - self.base.cpu_topo.max_cpus, - region_base, - region_size, - cpu_config, - hotplug_cpu_req.clone(), - ) + .realize(&mut self.base.sysbus) .with_context(|| "Failed to realize Cpu Controller")?; - - let mut lock_controller = realize_controller.lock().unwrap(); - lock_controller.set_boot_vcpu(self.base.cpus.clone())?; - drop(lock_controller); - self.register_hotplug_vcpu_event(hotplug_cpu_req, vm)?; self.cpu_controller = Some(realize_controller); Ok(()) @@ -281,7 +275,7 @@ impl StdMachineOps for StdMachine { nr_cpus: u8, max_cpus: u8, ) -> Result>>> { - let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone()); + let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone(), &mut self.base.sysbus)?; fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, max_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; @@ -439,38 +433,45 @@ impl MachineOps for StdMachine { } fn add_rtc_device(&mut self, mem_size: u64) -> Result<()> { - let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; + let mut rtc = + RTC::new(&mut self.base.sysbus).with_context(|| "Failed to create RTC device")?; rtc.set_memory( mem_size, MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); - RTC::realize(rtc, &mut self.base.sysbus).with_context(|| "Failed to realize RTC device") + rtc.realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize RTC device") } fn add_ged_device(&mut self) -> Result<()> { - let ged = Ged::default(); let region_base: u64 = MEM_LAYOUT[LayoutEntryType::GedMmio as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::GedMmio as usize].1; - let ged_event = GedEvent::new(self.power_button.clone(), self.cpu_resize_req.clone()); - ged.realize( - &mut self.base.sysbus, - ged_event, + let ged = Ged::new( false, + &mut self.base.sysbus, region_base, region_size, - ) - .with_context(|| "Failed to realize Ged")?; + ged_event, + )?; + + ged.realize(&mut self.base.sysbus) + .with_context(|| "Failed to realize Ged")?; Ok(()) } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; - let serial = Serial::new(config.clone()); + let serial = Serial::new( + config.clone(), + &mut self.base.sysbus, + region_base, + region_size, + )?; serial - .realize(&mut self.base.sysbus, region_base, region_size) + .realize(&mut self.base.sysbus) .with_context(|| "Failed to realize serial device.")?; Ok(()) } @@ -615,21 +616,18 @@ impl MachineOps for StdMachine { let backend = Some(fd); let pflash = PFlash::new( pfl_size, - &backend, + backend, sector_len, 4_u32, 1_u32, config.readonly, - ) - .with_context(|| MachineError::InitPflashErr)?; - PFlash::realize( - pflash, &mut self.base.sysbus, flash_end - pfl_size, - pfl_size, - backend, ) - .with_context(|| MachineError::RlzPflashErr)?; + .with_context(|| MachineError::InitPflashErr)?; + pflash + .realize(&mut self.base.sysbus) + .with_context(|| MachineError::RlzPflashErr)?; flash_end -= pfl_size; } diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 9fc5cd596..6e89dd95c 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -883,9 +883,35 @@ mod tests { use std::sync::Arc; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; pub const MEMORY_SIZE: u64 = 1024 * 1024; + pub fn sysbus_init() -> SysBus { + let sys_mem = AddressSpace::new( + Region::init_container_region(u64::max_value(), "sys_mem"), + "sys_mem", + None, + ) + .unwrap(); + #[cfg(target_arch = "x86_64")] + let sys_io = AddressSpace::new( + Region::init_container_region(1 << 16, "sys_io"), + "sys_io", + None, + ) + .unwrap(); + let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); + let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); + SysBus::new( + #[cfg(target_arch = "x86_64")] + &sys_io, + &sys_mem, + free_irqs, + mmio_region, + ) + } + pub fn address_space_init() -> Arc { let root = Region::init_container_region(1 << 36, "root"); let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index c538f958a..ebde7c0c0 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -28,8 +28,6 @@ use crate::{ use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use devices::{Device, DeviceBase}; -#[cfg(target_arch = "x86_64")] -use machine_manager::config::{BootSource, Param}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -140,38 +138,32 @@ impl VirtioMmioDevice { mem_space: &Arc, name: String, device: Arc>, - ) -> Self { - let device_clone = device.clone(); - let queue_num = device_clone.lock().unwrap().queue_num(); - - VirtioMmioDevice { + sysbus: &mut SysBus, + region_base: u64, + region_size: u64, + ) -> Result { + if region_base >= sysbus.mmio_region.1 { + bail!("Mmio region space exhausted."); + } + let queue_num = device.lock().unwrap().queue_num(); + let mut mmio_device = VirtioMmioDevice { base: SysBusDevBase { - base: DeviceBase { - id: name, - hotpluggable: false, - }, + base: DeviceBase::new(name, false), dev_type: SysBusDevType::VirtioMmio, - interrupt_evt: Some(Arc::new(create_new_eventfd().unwrap())), + interrupt_evt: Some(Arc::new(create_new_eventfd()?)), ..Default::default() }, device, host_notify_info: HostNotifyInfo::new(queue_num), mem_space: mem_space.clone(), interrupt_cb: None, - } + }; + mmio_device.set_sys_resource(sysbus, region_base, region_size, "VirtioMmio")?; + + Ok(mmio_device) } - pub fn realize( - mut self, - sysbus: &mut SysBus, - region_base: u64, - region_size: u64, - #[cfg(target_arch = "x86_64")] bs: &Arc>, - ) -> Result>> { - if region_base >= sysbus.mmio_region.1 { - bail!("Mmio region space exhausted."); - } - self.set_sys_resource(sysbus, region_base, region_size, "VirtioMmio")?; + pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { self.assign_interrupt_cb(); self.device .lock() @@ -182,16 +174,6 @@ impl VirtioMmioDevice { let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev)?; - #[cfg(target_arch = "x86_64")] - bs.lock().unwrap().kernel_cmdline.push(Param { - param_type: "virtio_mmio.device".to_string(), - value: format!( - "{}@0x{:08x}:{}", - region_size, - region_base, - dev.lock().unwrap().base.res.irq - ), - }); Ok(dev) } @@ -585,7 +567,7 @@ impl MigrationHook for VirtioMmioDevice { #[cfg(test)] mod tests { use super::*; - use crate::tests::address_space_init; + use crate::tests::{address_space_init, sysbus_init}; use crate::{ check_config_space_rw, read_config_default, VirtioBase, QUEUE_TYPE_SPLIT_VRING, VIRTIO_TYPE_BLOCK, @@ -659,11 +641,16 @@ mod tests { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); + let mut sysbus = sysbus_init(); let virtio_mmio_device = VirtioMmioDevice::new( &sys_space, "test_virtio_mmio_device".to_string(), virtio_device.clone(), - ); + &mut sysbus, + 0x0A00_0000, + 0x0000_0200, + ) + .unwrap(); (virtio_device, virtio_mmio_device) } -- Gitee From d1f2ed02e4403dce18425caf6225a104ba85033a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 17 Jun 2024 08:33:02 +0800 Subject: [PATCH 1903/2187] virtio: move `get_libc_iovecs` to virtio lib Move `get_libc_iovecs` to virtio lib for it will be used by virto-net and the next submitted virtio-vsock. No functional change. Signed-off-by: liuxiangdong --- virtio/src/device/net.rs | 58 +++++++--------------------------------- virtio/src/lib.rs | 29 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 4101ec9cc..c4053f064 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -27,10 +27,10 @@ use once_cell::sync::Lazy; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, iov_discard_front, iov_to_buf, mem_to_buf, read_config_default, - report_virtio_error, virtio_has_feature, ElemIovec, Element, Queue, VirtioBase, VirtioDevice, - VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VIRTIO_F_RING_EVENT_IDX, - VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, + check_config_space_rw, get_libc_iovecs, iov_discard_front, iov_to_buf, mem_to_buf, + read_config_default, report_virtio_error, virtio_has_feature, ElemIovec, Element, Queue, + VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET, VIRTIO_NET_CTRL_MAC_TABLE_SET, VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, @@ -43,12 +43,9 @@ use crate::{ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; -use address_space::{AddressSpace, RegionCache}; -use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; -use machine_manager::{ - config::{ConfigCheck, NetDevcfg, NetworkInterfaceConfig}, - event_loop::EventLoop, -}; +use address_space::AddressSpace; +use machine_manager::config::{ConfigCheck, NetDevcfg, NetworkInterfaceConfig}; +use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use migration::{ migration::Migratable, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer, @@ -753,35 +750,6 @@ impl NetIoHandler { size } - fn get_libc_iovecs( - mem_space: &Arc, - cache: &Option, - elem_iovecs: &[ElemIovec], - ) -> Vec { - let mut iovecs = Vec::new(); - for elem_iov in elem_iovecs.iter() { - // elem_iov.addr has been checked in pop_avail(). - let mut len = elem_iov.len; - let mut start = elem_iov.addr; - loop { - let io_vec = mem_space - .get_host_address_from_cache(start, cache) - .map(|(hva, fr_len)| libc::iovec { - iov_base: hva as *mut libc::c_void, - iov_len: std::cmp::min(elem_iov.len, fr_len as u32) as libc::size_t, - }) - .unwrap(); - start = start.unchecked_add(io_vec.iov_len as u64); - len -= io_vec.iov_len as u32; - iovecs.push(io_vec); - if len == 0 { - break; - } - } - } - iovecs - } - fn handle_rx(&mut self) -> Result<()> { trace::virtio_receive_request("Net".to_string(), "to rx".to_string()); if self.tap.is_none() { @@ -805,11 +773,7 @@ impl NetIoHandler { } else if elem.in_iovec.is_empty() { bail!("The length of in iovec is 0"); } - let iovecs = NetIoHandler::get_libc_iovecs( - &self.mem_space, - queue.vring.get_cache(), - &elem.in_iovec, - ); + let iovecs = get_libc_iovecs(&self.mem_space, queue.vring.get_cache(), &elem.in_iovec); if MigrationManager::is_active() { // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. @@ -921,11 +885,7 @@ impl NetIoHandler { bail!("The length of out iovec is 0"); } - let iovecs = NetIoHandler::get_libc_iovecs( - &self.mem_space, - queue.vring.get_cache(), - &elem.out_iovec, - ); + let iovecs = get_libc_iovecs(&self.mem_space, queue.vring.get_cache(), &elem.out_iovec); let tap_fd = if let Some(tap) = self.tap.as_mut() { tap.as_raw_fd() as libc::c_int } else { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 6e89dd95c..61ee3eadc 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -870,6 +870,35 @@ fn gpa_hva_iovec_map_by_cache( Ok((iov_size, hva_iovec)) } +fn get_libc_iovecs( + mem_space: &Arc, + cache: &Option, + elem_iovecs: &[ElemIovec], +) -> Vec { + let mut iovecs = Vec::new(); + for elem_iov in elem_iovecs.iter() { + // elem_iov.addr has been checked in pop_avail(). + let mut len = elem_iov.len; + let mut start = elem_iov.addr; + loop { + let io_vec = mem_space + .get_host_address_from_cache(start, cache) + .map(|(hva, fr_len)| libc::iovec { + iov_base: hva as *mut libc::c_void, + iov_len: std::cmp::min(elem_iov.len, fr_len as u32) as libc::size_t, + }) + .unwrap(); + start = start.unchecked_add(io_vec.iov_len as u64); + len -= io_vec.iov_len as u32; + iovecs.push(io_vec); + if len == 0 { + break; + } + } + } + iovecs +} + pub fn virtio_register_sysbusdevops_type() -> Result<()> { register_sysbusdevops_type::() } -- Gitee From 63ab65fb1f9fc9c869d5012f982186597bba9fc0 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jun 2024 18:31:39 +0800 Subject: [PATCH 1904/2187] sysbus: add sysbus error type `AddRegionErr` Add sysbus error type `AddRegionErr` to optimize duplicate code in `attach_device`. Signed-off-by: liuxiangdong --- devices/src/sysbus/error.rs | 2 ++ devices/src/sysbus/mod.rs | 38 ++++--------------------------------- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/devices/src/sysbus/error.rs b/devices/src/sysbus/error.rs index da1c89098..1d233b6be 100644 --- a/devices/src/sysbus/error.rs +++ b/devices/src/sysbus/error.rs @@ -19,4 +19,6 @@ pub enum SysBusError { #[from] source: address_space::error::AddressSpaceError, }, + #[error("Failed to register region in {0} space: offset={1:#x},size={2:#x}")] + AddRegionErr(&'static str, u64, u64), } diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index f9bbeb5ab..4ca00be1f 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -133,40 +133,13 @@ impl SysBus { region.set_ioeventfds(&locked_dev.ioeventfds()); match locked_dev.sysbusdev_base().dev_type { - SysBusDevType::Serial if cfg!(target_arch = "x86_64") => { - #[cfg(target_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] + SysBusDevType::Serial | SysBusDevType::FwCfg | SysBusDevType::Rtc => { self.sys_io .root() .add_subregion(region, region_base) .with_context(|| { - format!( - "Failed to register region in I/O space: offset={},size={}", - region_base, region_size - ) - })?; - } - SysBusDevType::FwCfg if cfg!(target_arch = "x86_64") => { - #[cfg(target_arch = "x86_64")] - self.sys_io - .root() - .add_subregion(region, region_base) - .with_context(|| { - format!( - "Failed to register region in I/O space: offset 0x{:x}, size {}", - region_base, region_size - ) - })?; - } - SysBusDevType::Rtc if cfg!(target_arch = "x86_64") => { - #[cfg(target_arch = "x86_64")] - self.sys_io - .root() - .add_subregion(region, region_base) - .with_context(|| { - format!( - "Failed to register region in I/O space: offset 0x{:x}, size {}", - region_base, region_size - ) + SysBusError::AddRegionErr("I/O", region_base, region_size) })?; } _ => self @@ -174,10 +147,7 @@ impl SysBus { .root() .add_subregion(region, region_base) .with_context(|| { - format!( - "Failed to register region in memory space: offset={},size={}", - region_base, region_size - ) + SysBusError::AddRegionErr("memory", region_base, region_size) })?, } } -- Gitee From 548e39fdcc4d2eeda7c1b8ea5b4567dd760943fd Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 17 Jun 2024 10:54:18 +0800 Subject: [PATCH 1905/2187] power: fix clippy warnings Fix clippy warnings: error: redundant clone --> devices/src/acpi/power.rs:200:37 | 200 | power_status_update(&dev.clone()); | ^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` Signed-off-by: liuxiangdong --- devices/src/acpi/power.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 1da560629..ae193987c 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -197,7 +197,7 @@ impl PowerDev { } } if pdev_available { - power_status_update(&dev.clone()); + power_status_update(&dev); } else { let mut pdev = dev.lock().unwrap(); pdev.power_load_static_status(); -- Gitee From 9e32970d7d30f44e391a91aa24ca46efe7227678 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 16 Jun 2024 11:27:38 +0800 Subject: [PATCH 1906/2187] sysbus: change `sysbus` in machine to Arc We will use `Week>` to represent the parent bus of a device. So, sysbus needs to be modified to be of the same type as other buses. Change `sysbus` in machine to Arc. Signed-off-by: liuxiangdong --- devices/src/acpi/cpu_controller.rs | 6 ++--- devices/src/acpi/ged.rs | 6 ++--- devices/src/acpi/power.rs | 8 +++--- devices/src/legacy/fwcfg.rs | 16 +++++++----- devices/src/legacy/pflash.rs | 25 ++++++++---------- devices/src/legacy/pl011.rs | 10 +++++--- devices/src/legacy/pl031.rs | 14 +++++----- devices/src/legacy/ramfb.rs | 4 +-- devices/src/legacy/rtc.rs | 18 ++++++------- devices/src/legacy/serial.rs | 14 +++++----- devices/src/sysbus/mod.rs | 14 +++++----- machine/src/aarch64/fdt.rs | 3 ++- machine/src/aarch64/micro.rs | 19 +++++--------- machine/src/aarch64/standard.rs | 40 +++++++++++++---------------- machine/src/lib.rs | 13 +++++----- machine/src/micro_common/mod.rs | 8 +++--- machine/src/x86_64/micro.rs | 11 +++----- machine/src/x86_64/standard.rs | 34 ++++++++++-------------- virtio/src/lib.rs | 8 +++--- virtio/src/transport/virtio_mmio.rs | 12 ++++----- 20 files changed, 133 insertions(+), 150 deletions(-) diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index 9e2383d5c..70d0866ad 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -90,7 +90,7 @@ pub struct CpuController { impl CpuController { pub fn new( max_cpus: u8, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, cpu_config: CpuConfig, @@ -111,10 +111,10 @@ impl CpuController { Ok(cpu_controller) } - pub fn realize(self, sysbus: &mut SysBus) -> Result>> { + pub fn realize(self, sysbus: &Arc>) -> Result>> { let dev = Arc::new(Mutex::new(self)); let ret_dev = dev.clone(); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; Ok(ret_dev) } diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 7c3c92a42..cfe072e26 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -85,7 +85,7 @@ pub struct Ged { impl Ged { pub fn new( battery_present: bool, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, ged_event: GedEvent, @@ -103,10 +103,10 @@ impl Ged { Ok(ged) } - pub fn realize(self, sysbus: &mut SysBus) -> Result>> { + pub fn realize(self, sysbus: &Arc>) -> Result>> { let ged_event = self.ged_event.clone(); let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; let ged = dev.lock().unwrap(); ged.register_acpi_powerdown_event(ged_event.power_button) diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 1da560629..ca4011274 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -83,7 +83,7 @@ pub struct PowerDev { impl PowerDev { pub fn new( ged_dev: Arc>, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, ) -> Result { @@ -184,9 +184,9 @@ impl PowerDev { self.ged.lock().unwrap().inject_acpi_event(evt); } - pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + pub fn realize(self, sysbus: &Arc>) -> Result<()> { let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; let pdev_available: bool; { @@ -197,7 +197,7 @@ impl PowerDev { } } if pdev_available { - power_status_update(&dev.clone()); + power_status_update(&dev); } else { let mut pdev = dev.lock().unwrap(); pdev.power_load_static_status(); diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 3f18833df..dfd20dfb1 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -844,7 +844,7 @@ pub struct FwCfgMem { impl FwCfgMem { pub fn new( sys_mem: Arc, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, ) -> Result { @@ -859,10 +859,12 @@ impl FwCfgMem { Ok(fwcfgmem) } - pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { + pub fn realize(mut self, sysbus: &Arc>) -> Result>> { self.fwcfg.common_realize()?; let dev = Arc::new(Mutex::new(self)); sysbus + .lock() + .unwrap() .attach_device(&dev) .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) @@ -981,7 +983,7 @@ impl SysBusDevOps for FwCfgMem { fn set_sys_resource( &mut self, - _sysbus: &mut SysBus, + _sysbus: &Arc>, region_base: u64, region_size: u64, region_name: &str, @@ -1006,7 +1008,7 @@ pub struct FwCfgIO { #[cfg(target_arch = "x86_64")] impl FwCfgIO { - pub fn new(sys_mem: Arc, sysbus: &mut SysBus) -> Result { + pub fn new(sys_mem: Arc, sysbus: &Arc>) -> Result { let mut fwcfg = FwCfgIO { base: SysBusDevBase::new(SysBusDevType::FwCfg), fwcfg: FwCfgCommon::new(sys_mem), @@ -1018,10 +1020,12 @@ impl FwCfgIO { Ok(fwcfg) } - pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { + pub fn realize(mut self, sysbus: &Arc>) -> Result>> { self.fwcfg.common_realize()?; let dev = Arc::new(Mutex::new(self)); sysbus + .lock() + .unwrap() .attach_device(&dev) .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) @@ -1141,7 +1145,7 @@ impl SysBusDevOps for FwCfgIO { fn set_sys_resource( &mut self, - _sysbus: &mut SysBus, + _sysbus: &Arc>, region_base: u64, region_size: u64, region_name: &str, diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 7eec7cca3..b2f8d616c 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -109,7 +109,7 @@ impl PFlash { bank_width: u32, device_width: u32, read_only: bool, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, ) -> Result { if block_len == 0 { @@ -230,19 +230,21 @@ impl PFlash { Ok(pflash) } - pub fn realize(self, sysbus: &mut SysBus) -> Result>> { + pub fn realize(self, sysbus: &Arc>) -> Result>> { let region_base = self.base.res.region_base; let host_mmap = self.host_mmap.clone(); let dev = Arc::new(Mutex::new(self)); - let region_ops = sysbus.build_region_ops(&dev); + let region_ops = sysbus.lock().unwrap().build_region_ops(&dev); let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "PflashRom"); dev.lock().unwrap().rom = Some(rom_region.clone()); sysbus + .lock() + .unwrap() .sys_mem .root() .add_subregion(rom_region, region_base) .with_context(|| "Failed to attach PFlash to system bus")?; - sysbus.devices.push(dev.clone()); + sysbus.lock().unwrap().devices.push(dev.clone()); Ok(dev) } @@ -888,7 +890,7 @@ impl SysBusDevOps for PFlash { fn set_sys_resource( &mut self, - _sysbus: &mut SysBus, + _sysbus: &Arc>, region_base: u64, region_size: u64, region_name: &str, @@ -942,19 +944,12 @@ mod test { .open(file_name) .unwrap(), ); - let mut sysbus = sysbus_init(); + let sysbus = sysbus_init(); let pflash = PFlash::new( - flash_size, - fd, - sector_len, - 4, - 2, - read_only, - &mut sysbus, - flash_base, + flash_size, fd, sector_len, 4, 2, read_only, &sysbus, flash_base, ) .unwrap(); - let dev = pflash.realize(&mut sysbus).unwrap(); + let dev = pflash.realize(&sysbus).unwrap(); dev } diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 293ec750a..164f4b43a 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -131,7 +131,7 @@ impl PL011 { /// Create a new `PL011` instance with default parameters. pub fn new( cfg: SerialConfig, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, ) -> Result { @@ -162,7 +162,7 @@ impl PL011 { } } - pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + pub fn realize(self, sysbus: &Arc>) -> Result<()> { self.chardev .lock() .unwrap() @@ -170,6 +170,8 @@ impl PL011 { .with_context(|| "Failed to realize chardev")?; let dev = Arc::new(Mutex::new(self)); sysbus + .lock() + .unwrap() .attach_device(&dev) .with_context(|| "Failed to attach PL011 to system bus.")?; MigrationManager::register_device_instance( @@ -462,8 +464,8 @@ mod test { let config = SerialConfig { chardev: chardev_cfg, }; - let mut sysbus = sysbus_init(); - let mut pl011_dev = PL011::new(config, &mut sysbus, 0x0900_0000, 0x0000_1000).unwrap(); + let sysbus = sysbus_init(); + let mut pl011_dev = PL011::new(config, &sysbus, 0x0900_0000, 0x0000_1000).unwrap(); assert_eq!(pl011_dev.state.rfifo, [0; PL011_FIFO_SIZE]); assert_eq!(pl011_dev.state.flags, 0x90); assert_eq!(pl011_dev.state.lcr, 0); diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index f1d330c78..ab41fb2a3 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -80,7 +80,7 @@ pub struct PL031 { } impl PL031 { - pub fn new(sysbus: &mut SysBus, region_base: u64, region_size: u64) -> Result { + pub fn new(sysbus: &Arc>, region_base: u64, region_size: u64) -> Result { let mut pl031 = Self { base: SysBusDevBase::new(SysBusDevType::Rtc), state: PL031State::default(), @@ -99,9 +99,9 @@ impl PL031 { Ok(pl031) } - pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + pub fn realize(self, sysbus: &Arc>) -> Result<()> { let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; MigrationManager::register_device_instance( PL031State::descriptor(), @@ -221,8 +221,8 @@ mod test { #[test] fn test_set_year_20xx() { - let mut sysbus = sysbus_init(); - let mut rtc = PL031::new(&mut sysbus, 0x0901_0000, 0x0000_1000).unwrap(); + let sysbus = sysbus_init(); + let mut rtc = PL031::new(&sysbus, 0x0901_0000, 0x0000_1000).unwrap(); // Set rtc time: 2013-11-13 02:04:56. let mut wtick = mktime64(2013, 11, 13, 2, 4, 56) as u32; let mut data = [0; 4]; @@ -248,8 +248,8 @@ mod test { #[test] fn test_set_year_1970() { - let mut sysbus = sysbus_init(); - let mut rtc = PL031::new(&mut sysbus, 0x0901_0000, 0x0000_1000).unwrap(); + let sysbus = sysbus_init(); + let mut rtc = PL031::new(&sysbus, 0x0901_0000, 0x0000_1000).unwrap(); // Set rtc time (min): 1970-01-01 00:00:00. let wtick = mktime64(1970, 1, 1, 0, 0, 0) as u32; let mut data = [0; 4]; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 278fd8e77..0394c7b2c 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -251,9 +251,9 @@ impl Ramfb { } } - pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + pub fn realize(self, sysbus: &Arc>) -> Result<()> { let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; Ok(()) } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index d60b870f1..c24e27525 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -118,7 +118,7 @@ pub struct RTC { impl RTC { /// Construct function of RTC device. - pub fn new(sysbus: &mut SysBus) -> Result { + pub fn new(sysbus: &Arc>) -> Result { let mut rtc = RTC { base: SysBusDevBase { dev_type: SysBusDevType::Rtc, @@ -264,9 +264,9 @@ impl RTC { true } - pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + pub fn realize(self, sysbus: &Arc>) -> Result<()> { let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; Ok(()) } @@ -427,8 +427,8 @@ mod test { #[test] fn test_set_year_20xx() -> Result<()> { - let mut sysbus = sysbus_init(); - let mut rtc = RTC::new(&mut sysbus).with_context(|| "Failed to create RTC device")?; + let sysbus = sysbus_init(); + let mut rtc = RTC::new(&sysbus).with_context(|| "Failed to create RTC device")?; // Set rtc time: 2013-11-13 02:04:56 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x20); cmos_write(&mut rtc, RTC_YEAR, 0x13); @@ -462,8 +462,8 @@ mod test { #[test] fn test_set_year_1970() -> Result<()> { - let mut sysbus = sysbus_init(); - let mut rtc = RTC::new(&mut sysbus).with_context(|| "Failed to create RTC device")?; + let sysbus = sysbus_init(); + let mut rtc = RTC::new(&sysbus).with_context(|| "Failed to create RTC device")?; // Set rtc time (min): 1970-01-01 00:00:00 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); cmos_write(&mut rtc, RTC_YEAR, 0x70); @@ -486,8 +486,8 @@ mod test { #[test] fn test_invalid_rtc_time() -> Result<()> { - let mut sysbus = sysbus_init(); - let mut rtc = RTC::new(&mut sysbus).with_context(|| "Failed to create RTC device")?; + let sysbus = sysbus_init(); + let mut rtc = RTC::new(&sysbus).with_context(|| "Failed to create RTC device")?; // Set rtc year: 1969 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); cmos_write(&mut rtc, RTC_YEAR, 0x69); diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 05d6929c5..dc7e25fb7 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -126,7 +126,7 @@ pub struct Serial { impl Serial { pub fn new( cfg: SerialConfig, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, ) -> Result { @@ -144,14 +144,14 @@ impl Serial { Ok(serial) } - pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { + pub fn realize(self, sysbus: &Arc>) -> Result<()> { self.chardev .lock() .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; MigrationManager::register_device_instance( SerialState::descriptor(), @@ -472,11 +472,11 @@ mod test { id: "chardev".to_string(), }, }; - let mut sysbus = sysbus_init(); + let sysbus = sysbus_init(); let config = SerialConfig { chardev: chardev_cfg.clone(), }; - let mut usart = Serial::new(config, &mut sysbus, SERIAL_ADDR, 8).unwrap(); + let mut usart = Serial::new(config, &sysbus, SERIAL_ADDR, 8).unwrap(); assert_eq!(usart.state.ier, 0); assert_eq!(usart.state.iir, 1); assert_eq!(usart.state.lcr, 3); @@ -533,8 +533,8 @@ mod test { let config = SerialConfig { chardev: chardev_cfg, }; - let mut sysbus = sysbus_init(); - let mut usart = Serial::new(config, &mut sysbus, SERIAL_ADDR, 8).unwrap(); + let sysbus = sysbus_init(); + let mut usart = Serial::new(config, &sysbus, SERIAL_ADDR, 8).unwrap(); // Get state vector for usart let serial_state_result = usart.get_state_vec(); assert!(serial_state_result.is_ok()); diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 4ca00be1f..1e56f3636 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -282,14 +282,16 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder { fn set_sys_resource( &mut self, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, region_name: &str, ) -> Result<()> { - let irq = self.get_irq(sysbus)?; + let mut locked_sysbus = sysbus.lock().unwrap(); + let irq = self.get_irq(&mut locked_sysbus)?; let interrupt_evt = self.sysbusdev_base().interrupt_evt.clone(); - let irq_manager = sysbus.irq_manager.clone(); + let irq_manager = locked_sysbus.irq_manager.clone(); + drop(locked_sysbus); self.sysbusdev_base_mut().irq_state = IrqState::new(irq as u32, interrupt_evt, irq_manager, TriggerMode::Edge); @@ -391,7 +393,7 @@ pub fn to_sysbusdevops(dev: &dyn Device) -> Option<&dyn SysBusDevOps> { } #[cfg(test)] -pub fn sysbus_init() -> SysBus { +pub fn sysbus_init() -> Arc> { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sys_mem"), "sys_mem", @@ -407,11 +409,11 @@ pub fn sysbus_init() -> SysBus { .unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); - SysBus::new( + Arc::new(Mutex::new(SysBus::new( #[cfg(target_arch = "x86_64")] &sys_io, &sys_mem, free_irqs, mmio_region, - ) + ))) } diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index 76815b170..c08822c35 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -265,7 +265,8 @@ impl CompileFDTHelper for MachineBase { fdt.set_property_string("method", "hvc")?; fdt.end_node(psci_node_dep)?; - for dev in self.sysbus.devices.iter() { + let devices = self.sysbus.lock().unwrap().devices.clone(); + for dev in devices.iter() { let locked_dev = dev.lock().unwrap(); match locked_dev.sysbusdev_base().dev_type { SysBusDevType::PL011 => { diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 6b019d94a..51cc2de42 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -101,33 +101,28 @@ impl MachineOps for LightMachine { self.base.irq_chip.as_ref().unwrap().realize()?; let irq_manager = locked_hypervisor.create_irq_manager()?; - self.base.sysbus.irq_manager = irq_manager.line_irq_manager; + self.base.sysbus.lock().unwrap().irq_manager = irq_manager.line_irq_manager; Ok(()) } fn add_rtc_device(&mut self) -> Result<()> { let pl031 = PL031::new( - &mut self.base.sysbus, + &self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, )?; pl031 - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize pl031.") } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; - let pl011 = PL011::new( - config.clone(), - &mut self.base.sysbus, - region_base, - region_size, - ) - .with_context(|| "Failed to create PL011")?; + let pl011 = PL011::new(config.clone(), &self.base.sysbus, region_base, region_size) + .with_context(|| "Failed to create PL011")?; pl011 - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize PL011")?; let mut bs = self.base.boot_source.lock().unwrap(); bs.kernel_cmdline.push(Param { @@ -140,7 +135,7 @@ impl MachineOps for LightMachine { fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let mut locked_vm = vm.lock().unwrap(); - trace::sysbus(&locked_vm.base.sysbus); + trace::sysbus(&locked_vm.base.sysbus.lock().unwrap()); trace::vm_state(&locked_vm.base.vm_state); let topology = CPUTopology::new().set_topology(( diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 289083cfa..a5a99aef6 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -347,7 +347,7 @@ impl StdMachineOps for StdMachine { let mut fwcfg = FwCfgMem::new( self.base.sys_mem.clone(), - &mut self.base.sysbus, + &self.base.sysbus, MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0, MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, )?; @@ -383,7 +383,7 @@ impl StdMachineOps for StdMachine { .with_context(|| DevErrorKind::AddEntryErr("bios-geometry".to_string()))?; let fwcfg_dev = fwcfg - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize fwcfg device")?; self.base.fwcfg_dev = Some(fwcfg_dev.clone()); @@ -447,18 +447,18 @@ impl MachineOps for StdMachine { "Failed to create intx state: legacy irq manager is none." )); } - self.base.sysbus.irq_manager = line_irq_manager; + self.base.sysbus.lock().unwrap().irq_manager = line_irq_manager; Ok(()) } fn add_rtc_device(&mut self) -> Result<()> { let rtc = PL031::new( - &mut self.base.sysbus, + &self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, )?; - rtc.realize(&mut self.base.sysbus) + rtc.realize(&self.base.sysbus) .with_context(|| "Failed to realize PL031") } @@ -466,22 +466,22 @@ impl MachineOps for StdMachine { let battery_present = self.base.vm_config.lock().unwrap().machine_config.battery; let ged = Ged::new( battery_present, - &mut self.base.sysbus, + &self.base.sysbus, MEM_LAYOUT[LayoutEntryType::Ged as usize].0, MEM_LAYOUT[LayoutEntryType::Ged as usize].1, GedEvent::new(self.power_button.clone()), )?; let ged_dev = ged - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize Ged")?; if battery_present { let pdev = PowerDev::new( ged_dev, - &mut self.base.sysbus, + &self.base.sysbus, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].0, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].1, )?; - pdev.realize(&mut self.base.sysbus) + pdev.realize(&self.base.sysbus) .with_context(|| "Failed to realize PowerDev")?; } Ok(()) @@ -490,15 +490,10 @@ impl MachineOps for StdMachine { fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; - let pl011 = PL011::new( - config.clone(), - &mut self.base.sysbus, - region_base, - region_size, - ) - .with_context(|| "Failed to create PL011")?; + let pl011 = PL011::new(config.clone(), &self.base.sysbus, region_base, region_size) + .with_context(|| "Failed to create PL011")?; pl011 - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize PL011")?; let mut bs = self.base.boot_source.lock().unwrap(); bs.kernel_cmdline.push(Param { @@ -658,11 +653,11 @@ impl MachineOps for StdMachine { 4, 2, read_only, - &mut self.base.sysbus, + &self.base.sysbus, flash_base, ) .with_context(|| MachineError::InitPflashErr)?; - PFlash::realize(pflash, &mut self.base.sysbus) + PFlash::realize(pflash, &self.base.sysbus) .with_context(|| MachineError::RlzPflashErr)?; flash_base += flash_size; } @@ -714,7 +709,7 @@ impl MachineOps for StdMachine { let mut ramfb = Ramfb::new(sys_mem.clone(), config.install); ramfb.ramfb_state.setup(&fwcfg_dev)?; - ramfb.realize(&mut self.base.sysbus) + ramfb.realize(&self.base.sysbus) } fn get_pci_host(&mut self) -> Result<&Arc>> { @@ -958,7 +953,8 @@ impl AcpiBuilder for StdMachine { spcr.set_field(52, 1_u8 << 3); // Irq number used by the UART let mut uart_irq: u32 = 0; - for dev in self.base.sysbus.devices.iter() { + let devices = self.base.sysbus.lock().unwrap().devices.clone(); + for dev in devices.iter() { let locked_dev = dev.lock().unwrap(); if locked_dev.sysbusdev_base().dev_type == SysBusDevType::PL011 { uart_irq = locked_dev.sysbusdev_base().irq_state.irq as _; @@ -1007,7 +1003,7 @@ impl AcpiBuilder for StdMachine { dsdt.append_child(sb_scope.aml_bytes().as_slice()); // 3. Info of devices attached to system bus. - dsdt.append_child(self.base.sysbus.aml_bytes().as_slice()); + dsdt.append_child(self.base.sysbus.lock().unwrap().aml_bytes().as_slice()); let dsdt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &dsdt) .with_context(|| "Fail to add DSDT table to loader")?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f331c60ea..ecb96e2c2 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -133,7 +133,7 @@ pub struct MachineBase { #[cfg(target_arch = "x86_64")] sys_io: Arc, /// System bus. - sysbus: SysBus, + sysbus: Arc>, /// VM running state. vm_state: Arc<(Mutex, Condvar)>, /// Vm boot_source config. @@ -218,7 +218,7 @@ impl MachineBase { sys_mem, #[cfg(target_arch = "x86_64")] sys_io, - sysbus, + sysbus: Arc::new(Mutex::new(sysbus)), vm_state: Arc::new((Mutex::new(VmState::Created), Condvar::new())), boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)), vm_config: Arc::new(Mutex::new(vm_config.clone())), @@ -776,7 +776,7 @@ pub trait MachineOps: MachineLifecycle { let mut virtio_device = None; if serial_cfg.bus.is_none() { // Micro_vm. - for dev in self.get_sys_bus().devices.iter() { + for dev in self.get_sysbus_devices().iter() { let locked_busdev = dev.lock().unwrap(); if locked_busdev.sysbusdev_base().dev_type == SysBusDevType::VirtioMmio { let virtio_mmio_dev = locked_busdev @@ -938,8 +938,8 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } - fn get_sys_bus(&mut self) -> &SysBus { - &self.machine_base().sysbus + fn get_sysbus_devices(&mut self) -> Vec>> { + self.machine_base().sysbus.lock().unwrap().devices.clone() } fn get_fwcfg_dev(&mut self) -> Option>> { @@ -951,8 +951,7 @@ pub trait MachineOps: MachineLifecycle { } fn reset_all_devices(&mut self) -> Result<()> { - let sysbus = self.get_sys_bus(); - for dev in sysbus.devices.iter() { + for dev in self.get_sysbus_devices().iter() { dev.lock() .unwrap() .reset() diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 349efe21f..e7d08ae6e 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -487,18 +487,18 @@ impl LightMachine { device: Arc>, ) -> Result>> { let sys_mem = self.get_sys_mem().clone(); - let region_base = self.base.sysbus.min_free_base; + let region_base = self.base.sysbus.lock().unwrap().min_free_base; let region_size = MEM_LAYOUT[LayoutEntryType::Mmio as usize].1; let dev = VirtioMmioDevice::new( &sys_mem, name, device, - &mut self.base.sysbus, + &self.base.sysbus, region_base, region_size, )?; let mmio_device = dev - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| MachineError::RlzVirtioMmioErr)?; #[cfg(target_arch = "x86_64")] { @@ -509,7 +509,7 @@ impl LightMachine { value: format!("{}@0x{:08x}:{}", res.region_size, res.region_base, res.irq), }); } - self.base.sysbus.min_free_base += region_size; + self.base.sysbus.lock().unwrap().min_free_base += region_size; Ok(mmio_device) } } diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 0d88aaec5..dad7e7b76 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -83,7 +83,7 @@ impl MachineOps for LightMachine { locked_hypervisor.create_interrupt_controller()?; let irq_manager = locked_hypervisor.create_irq_manager()?; - self.base.sysbus.irq_manager = irq_manager.line_irq_manager; + self.base.sysbus.lock().unwrap().irq_manager = irq_manager.line_irq_manager; Ok(()) } @@ -132,14 +132,9 @@ impl MachineOps for LightMachine { let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; - let serial = Serial::new( - config.clone(), - &mut self.base.sysbus, - region_base, - region_size, - )?; + let serial = Serial::new(config.clone(), &self.base.sysbus, region_base, region_size)?; serial - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize serial device.") } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index dc970a335..e61d55897 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -217,7 +217,7 @@ impl StdMachine { ); let cpu_controller = CpuController::new( self.base.cpu_topo.max_cpus, - &mut self.base.sysbus, + &self.base.sysbus, region_base, region_size, cpu_config, @@ -225,7 +225,7 @@ impl StdMachine { self.base.cpus.clone(), )?; let realize_controller = cpu_controller - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize Cpu Controller")?; self.register_hotplug_vcpu_event(hotplug_cpu_req, vm)?; self.cpu_controller = Some(realize_controller); @@ -275,7 +275,7 @@ impl StdMachineOps for StdMachine { nr_cpus: u8, max_cpus: u8, ) -> Result>>> { - let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone(), &mut self.base.sysbus)?; + let mut fwcfg = FwCfgIO::new(self.base.sys_mem.clone(), &self.base.sysbus)?; fwcfg.add_data_entry(FwCfgEntryType::NbCpus, nr_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::MaxCpus, max_cpus.as_bytes().to_vec())?; fwcfg.add_data_entry(FwCfgEntryType::Irq0Override, 1_u32.as_bytes().to_vec())?; @@ -285,7 +285,7 @@ impl StdMachineOps for StdMachine { .add_file_entry("bootorder", boot_order) .with_context(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; - let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut self.base.sysbus) + let fwcfg_dev = FwCfgIO::realize(fwcfg, &self.base.sysbus) .with_context(|| "Failed to realize fwcfg device")?; self.base.fwcfg_dev = Some(fwcfg_dev.clone()); @@ -397,7 +397,7 @@ impl MachineOps for StdMachine { let root_bus = &self.pci_host.lock().unwrap().root_bus; let irq_manager = locked_hypervisor.create_irq_manager()?; root_bus.lock().unwrap().msi_irq_manager = irq_manager.msi_irq_manager; - self.base.sysbus.irq_manager = irq_manager.line_irq_manager; + self.base.sysbus.lock().unwrap().irq_manager = irq_manager.line_irq_manager; Ok(()) } @@ -433,14 +433,13 @@ impl MachineOps for StdMachine { } fn add_rtc_device(&mut self, mem_size: u64) -> Result<()> { - let mut rtc = - RTC::new(&mut self.base.sysbus).with_context(|| "Failed to create RTC device")?; + let mut rtc = RTC::new(&self.base.sysbus).with_context(|| "Failed to create RTC device")?; rtc.set_memory( mem_size, MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); - rtc.realize(&mut self.base.sysbus) + rtc.realize(&self.base.sysbus) .with_context(|| "Failed to realize RTC device") } @@ -450,13 +449,13 @@ impl MachineOps for StdMachine { let ged_event = GedEvent::new(self.power_button.clone(), self.cpu_resize_req.clone()); let ged = Ged::new( false, - &mut self.base.sysbus, + &self.base.sysbus, region_base, region_size, ged_event, )?; - ged.realize(&mut self.base.sysbus) + ged.realize(&self.base.sysbus) .with_context(|| "Failed to realize Ged")?; Ok(()) } @@ -464,14 +463,9 @@ impl MachineOps for StdMachine { fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; - let serial = Serial::new( - config.clone(), - &mut self.base.sysbus, - region_base, - region_size, - )?; + let serial = Serial::new(config.clone(), &self.base.sysbus, region_base, region_size)?; serial - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| "Failed to realize serial device.")?; Ok(()) } @@ -621,12 +615,12 @@ impl MachineOps for StdMachine { 4_u32, 1_u32, config.readonly, - &mut self.base.sysbus, + &self.base.sysbus, flash_end - pfl_size, ) .with_context(|| MachineError::InitPflashErr)?; pflash - .realize(&mut self.base.sysbus) + .realize(&self.base.sysbus) .with_context(|| MachineError::RlzPflashErr)?; flash_end -= pfl_size; } @@ -736,7 +730,7 @@ impl AcpiBuilder for StdMachine { dsdt.append_child(sb_scope.aml_bytes().as_slice()); // 2. Info of devices attached to system bus. - dsdt.append_child(self.base.sysbus.aml_bytes().as_slice()); + dsdt.append_child(self.base.sysbus.lock().unwrap().aml_bytes().as_slice()); // 3. Add _S5 sleep state. let mut package = AmlPackage::new(4); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 61ee3eadc..54bc52560 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -909,14 +909,14 @@ pub fn virtio_register_pcidevops_type() -> Result<()> { #[cfg(test)] mod tests { - use std::sync::Arc; + use std::sync::{Arc, Mutex}; use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; use devices::sysbus::{SysBus, IRQ_BASE, IRQ_MAX}; pub const MEMORY_SIZE: u64 = 1024 * 1024; - pub fn sysbus_init() -> SysBus { + pub fn sysbus_init() -> Arc> { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sys_mem"), "sys_mem", @@ -932,13 +932,13 @@ mod tests { .unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); - SysBus::new( + Arc::new(Mutex::new(SysBus::new( #[cfg(target_arch = "x86_64")] &sys_io, &sys_mem, free_irqs, mmio_region, - ) + ))) } pub fn address_space_init() -> Arc { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index ebde7c0c0..5d07ce776 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -138,11 +138,11 @@ impl VirtioMmioDevice { mem_space: &Arc, name: String, device: Arc>, - sysbus: &mut SysBus, + sysbus: &Arc>, region_base: u64, region_size: u64, ) -> Result { - if region_base >= sysbus.mmio_region.1 { + if region_base >= sysbus.lock().unwrap().mmio_region.1 { bail!("Mmio region space exhausted."); } let queue_num = device.lock().unwrap().queue_num(); @@ -163,7 +163,7 @@ impl VirtioMmioDevice { Ok(mmio_device) } - pub fn realize(mut self, sysbus: &mut SysBus) -> Result>> { + pub fn realize(mut self, sysbus: &Arc>) -> Result>> { self.assign_interrupt_cb(); self.device .lock() @@ -172,7 +172,7 @@ impl VirtioMmioDevice { .with_context(|| "Failed to realize virtio.")?; let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; + sysbus.lock().unwrap().attach_device(&dev)?; Ok(dev) } @@ -641,12 +641,12 @@ mod tests { let virtio_device = Arc::new(Mutex::new(VirtioDeviceTest::new())); let sys_space = address_space_init(); - let mut sysbus = sysbus_init(); + let sysbus = sysbus_init(); let virtio_mmio_device = VirtioMmioDevice::new( &sys_space, "test_virtio_mmio_device".to_string(), virtio_device.clone(), - &mut sysbus, + &sysbus, 0x0A00_0000, 0x0000_0200, ) -- Gitee From fce48e81e1d21a2a6dddc7b3532cacdcdf6c9d92 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 18 Jun 2024 00:33:47 +0800 Subject: [PATCH 1907/2187] xhci: fix some error with xhci controller Replace the function of report_transfer_error with submit_transfer, as the latter will send transfer event without length. Signed-off-by: Xiao Ye --- devices/src/usb/xhci/xhci_controller.rs | 34 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b515130d4..02ec9b173 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -212,6 +212,7 @@ impl XhciTransfer { pub fn submit_transfer(&mut self) -> Result<()> { // Event Data Transfer Length Accumulator. let mut edtla: u32 = 0; + let mut shortpkt = false; let mut left = self.packet.lock().unwrap().actual_length; for i in 0..self.td.len() { let trb = &self.td[i]; @@ -222,7 +223,9 @@ impl XhciTransfer { TRBType::TrData | TRBType::TrNormal | TRBType::TrIsoch => { if chunk > left { chunk = left; - self.status = TRBCCode::ShortPacket; + if self.status == TRBCCode::Success { + shortpkt = true; + } } left -= chunk; edtla = edtla.checked_add(chunk).with_context(|| @@ -238,16 +241,25 @@ impl XhciTransfer { } } if (trb.control & TRB_TR_IOC == TRB_TR_IOC) - || (self.status == TRBCCode::ShortPacket - && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) + || (shortpkt && (trb.control & TRB_TR_ISP == TRB_TR_ISP)) + || (self.status != TRBCCode::Success && left == 0) { - self.send_transfer_event(trb, chunk, &mut edtla)?; + self.send_transfer_event(trb, chunk, &mut edtla, shortpkt)?; + if self.status != TRBCCode::Success { + return Ok(()); + } } } Ok(()) } - fn send_transfer_event(&self, trb: &XhciTRB, transferred: u32, edtla: &mut u32) -> Result<()> { + fn send_transfer_event( + &self, + trb: &XhciTRB, + transferred: u32, + edtla: &mut u32, + shortpkt: bool, + ) -> Result<()> { let trb_type = trb.get_type(); let mut evt = XhciEvent::new(TRBType::ErTransfer, TRBCCode::Success); evt.slot_id = self.slotid as u8; @@ -255,7 +267,15 @@ impl XhciTransfer { evt.length = (trb.status & TRB_TR_LEN_MASK) - transferred; evt.flags = 0; evt.ptr = trb.addr; - evt.ccode = self.status; + evt.ccode = if self.status == TRBCCode::Success { + if shortpkt { + TRBCCode::ShortPacket + } else { + TRBCCode::Success + } + } else { + self.status + }; if trb_type == TRBType::TrEvdata { evt.ptr = trb.parameter; evt.flags |= TRB_EV_ED; @@ -2312,7 +2332,7 @@ impl XhciDevice { if xfer.running_retry { if report != TRBCCode::Invalid { xfer.status = report; - xfer.report_transfer_error()?; + xfer.submit_transfer()?; } let epctx = &mut self.slots[(slotid - 1) as usize].endpoints[(ep_id - 1) as usize]; epctx.retry = None; -- Gitee From 5b236bfd584fbe438b28df5f1ec431b4afe44bb0 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 18 Jun 2024 09:12:39 +0800 Subject: [PATCH 1908/2187] virtio-net: remove the out event of tap fd. Remove the OUT event of tap fd to prevent triggering a large number of OUT events after initialization. Signed-off-by: Xiao Ye --- virtio/src/device/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index ef952304b..785372692 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1224,7 +1224,7 @@ impl NetIoHandler { tap_fd, Some(handler), NotifierOperation::AddShared, - EventSet::OUT | EventSet::IN | EventSet::EDGE_TRIGGERED, + EventSet::IN | EventSet::EDGE_TRIGGERED, )]; notifiers -- Gitee From f8d34aacf3c5ea9e336992ccfef58635f70769e5 Mon Sep 17 00:00:00 2001 From: yexiao Date: Tue, 18 Jun 2024 09:27:16 +0800 Subject: [PATCH 1909/2187] virtio-net: move the event of handle_tx from rx thread to tx thread. Set all handle_tx on the same thread to prevent lock contention between tx_iothread and rx_iothread threads Signed-off-by: Xiao Ye --- virtio/src/device/net.rs | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 785372692..05ac2857c 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1167,32 +1167,11 @@ impl NetIoHandler { if events.contains(EventSet::OUT) { net_queue.listen_state.lock().unwrap().set_tap_full(false); - let mut locked_queue = net_queue.tx.queue.lock().unwrap(); - - if let Err(ref err) = locked_queue.vring.suppress_queue_notify( - &net_queue.mem_space, - net_queue.driver_features, - false, - ) { - error!("Failed to enable tx queue notify: {:?}", err); - report_virtio_error( - net_queue.interrupt_cb.clone(), - net_queue.driver_features, - &device_broken, - ); - return None; - }; - - drop(locked_queue); - - if let Err(ref e) = net_queue.handle_tx(&tap) { - error!("Failed to handle tx(tx event) for net, {:?}", e); - report_virtio_error( - net_queue.interrupt_cb.clone(), - net_queue.driver_features, - &device_broken, - ); - } + net_queue + .tx + .queue_evt + .write(1) + .unwrap_or_else(|e| error!("Failed to notify tx thread: {:?}", e)); } if events.contains(EventSet::IN) { -- Gitee From 6f5bd559ba1e6e33d04e1e4407788c9ef2be46ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Tue, 11 Jun 2024 15:35:51 +0800 Subject: [PATCH 1910/2187] ohaudio: introduce vm pause notfier to restart renderer and capturer This patch introduces vm pause notifier to machine. Ohaudio registers notifier which set restart flag. After the machine resumed, the render or capturer of ohaudio would destory current context and re- create an new context. Signed-off-by: Jiahong Li --- devices/src/misc/scream/mod.rs | 45 ++++++++++++++++++++++-------- devices/src/misc/scream/ohaudio.rs | 37 ++++++++++++++++++++++-- machine/src/aarch64/standard.rs | 6 +++- machine/src/lib.rs | 27 ++++++++++++++++-- machine_manager/src/machine.rs | 4 +++ 5 files changed, 102 insertions(+), 17 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 496c96c24..d9c8c5cb5 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -40,6 +40,7 @@ use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; +use machine_manager::machine::PauseNotify; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] @@ -337,27 +338,29 @@ impl StreamData { let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let capt = &mut header.capt; let addr = hva + capt.offset as u64; - let mut locked_interface = interface.lock().unwrap(); - locked_interface.pre_receive(addr, capt); + interface.lock().unwrap().pre_receive(addr, capt); while capt.is_started != 0 { if !self.update_buffer_by_chunk_idx(hva, shmem_size, capt) { return; } let recv_chunks_cnt: i32 = if get_record_authority() { - locked_interface.receive(self) + interface.lock().unwrap().receive(self) } else { - locked_interface.destroy(); + interface.lock().unwrap().destroy(); 0 }; - if recv_chunks_cnt > 0 { - self.chunk_idx = (self.chunk_idx + recv_chunks_cnt as u16) % capt.max_chunks; - - // Make sure chunk_idx write does not bypass audio chunk write. - fence(Ordering::SeqCst); - capt.chunk_idx = self.chunk_idx; + match recv_chunks_cnt.cmp(&0) { + std::cmp::Ordering::Less => thread::sleep(time::Duration::from_millis(100)), + std::cmp::Ordering::Greater => { + self.chunk_idx = (self.chunk_idx + recv_chunks_cnt as u16) % capt.max_chunks; + // Make sure chunk_idx write does not bypass audio chunk write. + fence(Ordering::SeqCst); + capt.chunk_idx = self.chunk_idx; + } + std::cmp::Ordering::Equal => continue, } } } @@ -430,6 +433,16 @@ pub struct Scream { size: u64, config: ScreamConfig, token_id: Option>>, + interface_resource: RwLock>>>, +} + +impl PauseNotify for Scream { + fn notify(&self, paused: bool) { + let interfaces = self.interface_resource.read().unwrap(); + for interface in interfaces.iter() { + interface.lock().unwrap().pause(paused); + } + } } impl Scream { @@ -440,6 +453,7 @@ impl Scream { size, config, token_id, + interface_resource: RwLock::new(Vec::new()), } } @@ -464,6 +478,10 @@ impl Scream { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamPlay", ScreamDirection::Playback); + self.interface_resource + .write() + .unwrap() + .push(interface.clone()); thread::Builder::new() .name("scream audio play worker".to_string()) .spawn(move || { @@ -490,6 +508,10 @@ impl Scream { let shmem_size = self.size; let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); let _ti = self.token_id.clone(); + self.interface_resource + .write() + .unwrap() + .push(interface.clone()); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { @@ -516,7 +538,7 @@ impl Scream { Ok(()) } - pub fn realize(mut self, devfn: u8, parent_bus: Weak>) -> Result<()> { + pub fn realize(&mut self, devfn: u8, parent_bus: Weak>) -> Result<()> { let header_size = mem::size_of::() as u64; if self.size < header_size { bail!( @@ -554,4 +576,5 @@ pub trait AudioInterface: Send { fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) {} fn receive(&mut self, recv_data: &StreamData) -> i32; fn destroy(&mut self); + fn pause(&mut self, _paused: bool) {} } diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 2349d93a2..990648ef2 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -27,6 +27,7 @@ trait OhAudioProcess { fn destroy(&mut self); fn preprocess(&mut self, _start_addr: u64, _sh_header: &ShmemStreamHeader) {} fn process(&mut self, recv_data: &StreamData) -> i32; + fn pause(&mut self, paused: bool); } #[derive(Debug, Clone, Copy)] @@ -45,6 +46,7 @@ struct OhAudioRender { stream_data: Arc>>, data_size: AtomicI32, start: bool, + pause: bool, flushing: AtomicBool, } @@ -55,6 +57,7 @@ impl Default for OhAudioRender { stream_data: Arc::new(Mutex::new(Vec::with_capacity(STREAM_DATA_VEC_CAPACITY))), data_size: AtomicI32::new(0), start: false, + pause: false, flushing: AtomicBool::new(false), } } @@ -117,15 +120,27 @@ impl OhAudioProcess for OhAudioRender { self.start } + fn pause(&mut self, paused: bool) { + self.pause = paused; + if paused { + self.destroy(); + } + } + fn destroy(&mut self) { if self.ctx.is_some() { if self.start { - self.flush(); + if !self.pause { + self.flush(); + } self.ctx.as_mut().unwrap().stop(); self.start = false; } self.ctx = None; } + if self.pause { + return; + } let mut locked_data = self.stream_data.lock().unwrap(); locked_data.clear(); self.data_size.store(0, Ordering::Relaxed); @@ -133,6 +148,9 @@ impl OhAudioProcess for OhAudioRender { } fn process(&mut self, recv_data: &StreamData) -> i32 { + if self.pause { + return 0; + } self.check_fmt_update(recv_data); fence(Ordering::Acquire); @@ -175,6 +193,7 @@ struct OhAudioCapture { shm_len: u64, cur_pos: u64, start: bool, + pause: bool, } impl OhAudioCapture { @@ -220,6 +239,13 @@ impl OhAudioProcess for OhAudioCapture { } } + fn pause(&mut self, paused: bool) { + self.pause = paused; + if paused { + self.destroy(); + } + } + fn destroy(&mut self) { if self.ctx.is_some() { if self.start { @@ -240,13 +266,16 @@ impl OhAudioProcess for OhAudioCapture { } fn process(&mut self, recv_data: &StreamData) -> i32 { + if self.pause { + return -1; + } self.check_fmt_update(recv_data); trace::trace_scope_start!(ohaudio_capturer_process, args = (recv_data)); if !self.start && !self.init(recv_data) { self.destroy(); - return 0; + return -1; } self.new_chunks.store(0, Ordering::Release); while self.new_chunks.load(Ordering::Acquire) == 0 { @@ -408,6 +437,10 @@ impl AudioInterface for OhAudio { fn destroy(&mut self) { self.processor.destroy(); } + + fn pause(&mut self, paused: bool) { + self.processor.pause(paused); + } } #[cfg(test)] diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 4b6eebdf0..8ea9f0a1d 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -69,7 +69,7 @@ use machine_manager::event; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{ MachineExternalInterface, MachineInterface, MachineLifecycle, MachineTestInterface, - MigrateInterface, VmState, + MigrateInterface, PauseNotify, VmState, }; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; @@ -469,6 +469,10 @@ impl MachineOps for StdMachine { .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } + fn register_vm_pause_notifier(&mut self, notifier: Arc) { + self.base.pause_notifiers.push(notifier); + } + fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { let v3 = ICGICv3Config { msi: true, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index bde75da4c..a956e1f93 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -83,7 +83,7 @@ use machine_manager::config::{ VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; +use machine_manager::machine::{HypervisorType, MachineInterface, PauseNotify, VmState}; use machine_manager::{check_arg_exist, check_arg_nonexist}; use migration::{MigrateOps, MigrationManager}; #[cfg(feature = "windows_emu_pid")] @@ -148,6 +148,8 @@ pub struct MachineBase { hypervisor: Arc>, /// migrate hypervisor. migration_hypervisor: Arc>, + /// vm pause notifiers. + pause_notifiers: Vec>, } impl MachineBase { @@ -224,6 +226,7 @@ impl MachineBase { machine_ram, hypervisor, migration_hypervisor, + pause_notifiers: Vec::new(), }) } @@ -1646,6 +1649,9 @@ pub trait MachineOps { Ok(()) } + /// Register vm pause listener. + fn register_vm_pause_notifier(&mut self, _listener: Arc) {} + /// Add scream sound based on ivshmem. /// /// # Arguments @@ -1680,10 +1686,12 @@ pub trait MachineOps { bail!("Object for share config is not on"); } - let scream = Scream::new(mem_cfg.size, config, token_id); + let mut scream = Scream::new(mem_cfg.size, config, token_id); scream .realize(devfn, parent_bus) - .with_context(|| "Failed to realize scream device") + .with_context(|| "Failed to realize scream device")?; + self.register_vm_pause_notifier(Arc::new(scream)); + Ok(()) } /// Get the corresponding device from the PCI bus based on the device id and device type name. @@ -1982,6 +1990,13 @@ pub trait MachineOps { self.machine_base().drive_files.clone() } + //// Trigger vm pause notifiers. + fn notify_vm_pause_notifiers(&self, paused: bool) { + for notifier in self.machine_base().pause_notifiers.iter() { + notifier.notify(paused); + } + } + /// Fetch a cloned file from drive backend files. fn fetch_drive_file(&self, path: &str) -> Result { let files = self.get_drive_files(); @@ -2130,6 +2145,9 @@ pub trait MachineOps { *vm_state = VmState::Paused; + // Notify VM paused. + self.notify_vm_pause_notifiers(true); + Ok(()) } @@ -2153,6 +2171,9 @@ pub trait MachineOps { *vm_state = VmState::Running; + // Notify VM resumed. + self.notify_vm_pause_notifiers(false); + Ok(()) } diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index a040d6efa..cd37eefd4 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -67,6 +67,10 @@ impl FromStr for HypervisorType { } } +pub trait PauseNotify: Send + Sync { + fn notify(&self, paused: bool); +} + /// Trait to handle virtual machine lifecycle. /// /// # Notes -- Gitee From c47cfb58e5e83f8a1d977c45f33d109fd69769bb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 18 Jun 2024 21:42:07 +0800 Subject: [PATCH 1911/2187] stratovirt-img: Fix all clippy warnings Fix all clippy warnings. Signed-off-by: liuxiangdong --- image/src/cmdline.rs | 16 +++++------ image/src/img.rs | 63 ++++++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/image/src/cmdline.rs b/image/src/cmdline.rs index a2f6079bf..8cf7c392b 100644 --- a/image/src/cmdline.rs +++ b/image/src/cmdline.rs @@ -71,15 +71,15 @@ impl ArgsParse { for idx in 0..len { let str = args[idx as usize].clone(); - if str.starts_with("-") && str.len() > 1 { - if pre_opt.1.len() != 0 { + if str.starts_with('-') && str.len() > 1 { + if !pre_opt.1.is_empty() { bail!("missing argument for option '{}'", pre_opt.1); } let name = if str.starts_with("--") && str.len() > 2 { - (&str[2..]).to_string() - } else if str.starts_with("-") && str.len() > 1 { - (&str[1..]).to_string() + str[2..].to_string() + } else if str.starts_with('-') && str.len() > 1 { + str[1..].to_string() } else { bail!("unrecognized option '{}'", str); }; @@ -100,7 +100,7 @@ impl ArgsParse { continue; } - if pre_opt.0 + 1 == idx && pre_opt.1.len() != 0 { + if pre_opt.0 + 1 == idx && !pre_opt.1.is_empty() { let name = pre_opt.1.to_string(); let value = str.to_string(); if let Some(arg) = self.args.get_mut(&name) { @@ -117,14 +117,14 @@ impl ArgsParse { } } pre_opt = (0, "".to_string()); - } else if pre_opt.1.len() == 0 { + } else if pre_opt.1.is_empty() { self.free.push(str.to_string()); } else { bail!("unrecognized option '{}'", pre_opt.1); } } - if pre_opt.0 == 0 && pre_opt.1.len() != 0 { + if pre_opt.0 == 0 && !pre_opt.1.is_empty() { bail!("unrecognized option '{}'", pre_opt.1); } diff --git a/image/src/img.rs b/image/src/img.rs index e106b70eb..d37b5f39a 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -115,7 +115,7 @@ impl Drop for ImageFile { pub(crate) fn image_create(args: Vec) -> Result<()> { let mut create_options = CreateOptions::default(); let mut arg_parser = ArgsParse::create(vec!["h", "help"], vec!["f"], vec!["o"]); - arg_parser.parse(args.clone())?; + arg_parser.parse(args)?; if arg_parser.opt_present("h") || arg_parser.opt_present("help") { print_help(); @@ -170,7 +170,7 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { .write(true) .custom_flags(libc::O_CREAT | libc::O_TRUNC) .mode(0o660) - .open(path.clone())?; + .open(path)?; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; let image_info = match disk_fmt { @@ -191,7 +191,7 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { } pub(crate) fn image_info(args: Vec) -> Result<()> { - if args.len() < 1 { + if args.is_empty() { bail!("Not enough arguments"); } let mut arg_parser = ArgsParse::create(vec!["h", "help"], vec![], vec![]); @@ -216,9 +216,10 @@ pub(crate) fn image_info(args: Vec) -> Result<()> { let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; let image_file = ImageFile::create(&img_path, false)?; let detect_fmt = image_file.detect_img_format()?; - - let mut conf = BlockProperty::default(); - conf.format = detect_fmt; + let conf = BlockProperty { + format: detect_fmt, + ..Default::default() + }; let mut driver: Box> = match detect_fmt { DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.try_clone()?, aio, conf)), DiskFormat::Qcow2 => { @@ -229,8 +230,10 @@ pub(crate) fn image_info(args: Vec) -> Result<()> { } }; - let mut image_info = ImageInfo::default(); - image_info.path = img_path; + let mut image_info = ImageInfo { + path: img_path, + ..Default::default() + }; driver.query_image(&mut image_info)?; print!("{}", image_info); Ok(()) @@ -258,9 +261,9 @@ pub(crate) fn image_check(args: Vec) -> Result<()> { } if let Some(kind) = arg_parser.opt_str("r") { - if kind == "leaks".to_string() { + if kind == *"leaks" { fix |= FIX_LEAKS; - } else if kind == "all".to_string() { + } else if kind == *"all" { fix |= FIX_LEAKS; fix |= FIX_ERRORS; } else { @@ -294,8 +297,10 @@ pub(crate) fn image_check(args: Vec) -> Result<()> { bail!("stratovirt-img: This image format does not support checks"); } DiskFormat::Qcow2 => { - let mut conf = BlockProperty::default(); - conf.format = DiskFormat::Qcow2; + let conf = BlockProperty { + format: DiskFormat::Qcow2, + ..Default::default() + }; let mut qcow2_driver = create_qcow2_driver_for_check(file, conf)?; let ret = qcow2_driver.check_image(&mut check_res, quite, fix); let check_message = check_res.collect_check_message(); @@ -337,9 +342,10 @@ pub(crate) fn image_resize(mut args: Vec) -> Result<()> { let image_file = ImageFile::create(&img_path, false)?; let detect_fmt = image_file.detect_img_format()?; let real_fmt = image_file.check_img_format(disk_fmt, detect_fmt)?; - - let mut conf = BlockProperty::default(); - conf.format = real_fmt; + let conf = BlockProperty { + format: real_fmt, + ..Default::default() + }; let mut driver: Box> = match real_fmt { DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.try_clone()?, aio, conf)), DiskFormat::Qcow2 => { @@ -352,12 +358,12 @@ pub(crate) fn image_resize(mut args: Vec) -> Result<()> { let old_size = driver.disk_size()?; // Only expansion is supported currently. - let new_size = if size_str.starts_with("+") { + let new_size = if size_str.starts_with('+') { let size = memory_unit_conversion(&size_str, 1)?; old_size .checked_add(size) .ok_or_else(|| anyhow!("Disk size is too large for chosen offset"))? - } else if size_str.starts_with("-") { + } else if size_str.starts_with('-') { bail!("The shrink operation is not supported"); } else { let new_size = memory_unit_conversion(&size_str, 1)?; @@ -443,10 +449,12 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { } // Create qcow2 driver. - let mut qcow2_conf = BlockProperty::default(); - qcow2_conf.format = DiskFormat::Qcow2; - qcow2_conf.discard = true; - qcow2_conf.write_zeroes = WriteZeroesState::Unmap; + let qcow2_conf = BlockProperty { + format: DiskFormat::Qcow2, + discard: true, + write_zeroes: WriteZeroesState::Unmap, + ..Default::default() + }; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let mut qcow2_driver = Qcow2Driver::new(image_file.file.try_clone()?, aio, qcow2_conf.clone())?; @@ -603,8 +611,10 @@ mod test { } fn create_driver(&self) -> Qcow2Driver<()> { - let mut conf = BlockProperty::default(); - conf.format = DiskFormat::Qcow2; + let conf = BlockProperty { + format: DiskFormat::Qcow2, + ..Default::default() + }; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let mut qcow2_driver = Qcow2Driver::new(self.file.try_clone().unwrap(), aio, conf.clone()).unwrap(); @@ -614,8 +624,10 @@ mod test { fn create_driver_for_check(&self) -> Qcow2Driver<()> { let file = self.file.try_clone().unwrap(); - let mut conf = BlockProperty::default(); - conf.format = DiskFormat::Qcow2; + let conf = BlockProperty { + format: DiskFormat::Qcow2, + ..Default::default() + }; let qcow2_driver = create_qcow2_driver_for_check(file, conf).unwrap(); qcow2_driver } @@ -695,7 +707,6 @@ mod test { fn create_driver(&mut self) -> RawDriver<()> { let mut conf = BlockProperty::default(); - conf.format = DiskFormat::Raw; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); -- Gitee From 4d410d35ef22236fb9890a15811d1d86fda146b5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 18 Jun 2024 18:00:46 +0800 Subject: [PATCH 1912/2187] drive: use `Arc` to avoid generating actual copy actions We don't need to copy multiple File instances, Using Arc to avoid generating actual copy actions. Signed-off-by: liuxiangdong --- address_space/src/host_mmap.rs | 4 +-- block_backend/src/file.rs | 7 +++-- block_backend/src/lib.rs | 2 +- block_backend/src/qcow2/mod.rs | 48 +++++++++++++++++----------- block_backend/src/qcow2/refcount.rs | 17 ++++++---- block_backend/src/raw.rs | 2 +- devices/src/legacy/pflash.rs | 10 +++--- image/src/img.rs | 49 ++++++++++++++--------------- machine/src/lib.rs | 2 +- machine/src/standard_common/mod.rs | 4 +-- machine/src/x86_64/standard.rs | 12 +++---- machine_manager/src/config/drive.rs | 3 +- machine_manager/src/config/mod.rs | 13 ++++---- 13 files changed, 95 insertions(+), 78 deletions(-) diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index ca309770a..d2012e6f5 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -59,9 +59,9 @@ impl FileBackend { /// # Arguments /// /// * `fd` - Opened backend file. - pub fn new_common(fd: File) -> Self { + pub fn new_common(fd: Arc) -> Self { Self { - file: Arc::new(fd), + file: fd, offset: 0, page_size: 0, } diff --git a/block_backend/src/file.rs b/block_backend/src/file.rs index 45ea15493..3acef0381 100644 --- a/block_backend/src/file.rs +++ b/block_backend/src/file.rs @@ -55,7 +55,7 @@ impl CombineRequest { } pub struct FileDriver { - pub file: File, + pub file: Arc, aio: Rc>>, pub incomplete: Arc, delete_evts: Vec, @@ -63,7 +63,7 @@ pub struct FileDriver { } impl FileDriver { - pub fn new(file: File, aio: Aio, block_prop: BlockProperty) -> Self { + pub fn new(file: Arc, aio: Aio, block_prop: BlockProperty) -> Self { Self { file, incomplete: aio.incomplete_cnt.clone(), @@ -199,13 +199,14 @@ impl FileDriver { pub fn disk_size(&mut self) -> Result { let disk_size = self .file + .as_ref() .seek(SeekFrom::End(0)) .with_context(|| "Failed to seek the end for file")?; Ok(disk_size) } pub fn extend_to_len(&mut self, len: u64) -> Result<()> { - let file_end = self.file.seek(SeekFrom::End(0))?; + let file_end = self.file.as_ref().seek(SeekFrom::End(0))?; if len > file_end { self.file.set_len(len)?; } diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index b6f42513c..b4de1fc95 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -394,7 +394,7 @@ pub trait BlockDriverOps: Send { } pub fn create_block_backend( - file: File, + file: Arc, aio: Aio, prop: BlockProperty, ) -> Result>>> { diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 1b5a3dfbc..c8296d211 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -269,7 +269,7 @@ pub fn qcow2_flush_metadata( } impl Qcow2Driver { - pub fn new(file: File, aio: Aio, conf: BlockProperty) -> Result { + pub fn new(file: Arc, aio: Aio, conf: BlockProperty) -> Result { let fd = file.as_raw_fd(); let sync_aio = Rc::new(RefCell::new(SyncAioInfo::new(fd, conf.clone())?)); Ok(Self { @@ -1614,19 +1614,25 @@ impl BlockDriverOps for Qcow2Driver { // Write zero. for i in 0..3 { let offset = i * cluster_size; - self.driver.file.seek(SeekFrom::Start(offset))?; - self.driver.file.write_all(&zero_buf.to_vec())? + self.driver.file.as_ref().seek(SeekFrom::Start(offset))?; + self.driver.file.as_ref().write_all(&zero_buf.to_vec())? } - self.driver.file.rewind()?; - self.driver.file.write_all(&self.header.to_vec())?; + self.driver.file.as_ref().rewind()?; + self.driver.file.as_ref().write_all(&self.header.to_vec())?; // Refcount table. - self.driver.file.seek(SeekFrom::Start(cluster_size))?; - self.driver.file.write_all(&rc_table)?; + self.driver + .file + .as_ref() + .seek(SeekFrom::Start(cluster_size))?; + self.driver.file.as_ref().write_all(&rc_table)?; // Refcount block table. - self.driver.file.seek(SeekFrom::Start(cluster_size * 2))?; - self.driver.file.write_all(&rc_block)?; + self.driver + .file + .as_ref() + .seek(SeekFrom::Start(cluster_size * 2))?; + self.driver.file.as_ref().write_all(&rc_block)?; // Create qcow2 driver. self.load_refcount_table()?; @@ -2015,11 +2021,13 @@ mod test { } fn create_qcow2_driver(&self, conf: BlockProperty) -> Qcow2Driver<()> { - let file = std::fs::OpenOptions::new() - .read(true) - .write(true) - .open(&self.path) - .unwrap(); + let file = Arc::new( + std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&self.path) + .unwrap(), + ); let aio = Aio::new( Arc::new(SyncAioInfo::complete_func), util::aio::AioEngine::Off, @@ -2090,11 +2098,13 @@ mod test { pub fn create_qcow2(path: &str) -> (TestImage, Qcow2Driver<()>) { let mut image = TestImage::new(path, 30, 16); - let file = std::fs::OpenOptions::new() - .read(true) - .write(true) - .open(path) - .unwrap(); + let file = Arc::new( + std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(path) + .unwrap(), + ); let aio = Aio::new( Arc::new(SyncAioInfo::complete_func), util::aio::AioEngine::Off, diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index b58e3718b..0318d7e9e 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -791,8 +791,8 @@ mod test { path: &str, img_bits: u32, cluster_bits: u32, - ) -> (Qcow2Driver<()>, File) { - let file = image_create(path, img_bits, cluster_bits); + ) -> (Qcow2Driver<()>, Arc) { + let file = Arc::new(image_create(path, img_bits, cluster_bits)); let aio = Aio::new( Arc::new(SyncAioInfo::complete_func), util::aio::AioEngine::Off, @@ -811,10 +811,9 @@ mod test { l2_cache_size: None, refcount_cache_size: None, }; - let cloned_file = file.try_clone().unwrap(); - let mut qcow2_driver = Qcow2Driver::new(file, aio, conf.clone()).unwrap(); + let mut qcow2_driver = Qcow2Driver::new(file.clone(), aio, conf.clone()).unwrap(); qcow2_driver.load_metadata(conf).unwrap(); - (qcow2_driver, cloned_file) + (qcow2_driver, file) } #[test] @@ -835,6 +834,7 @@ mod test { // Check if the refcount of the cluster is updated to the disk. let mut rc_value = [0_u8; 2]; cloned_file + .as_ref() .read_at( &mut rc_value, cluster_sz * 2 + 2 * free_cluster_index as u64, @@ -873,6 +873,7 @@ mod test { let table_size = div_round_up(image_size, block_size * cluster_size).unwrap(); let mut refcount_table = vec![0_u8; table_size as usize * ENTRY_SIZE as usize]; assert!(cloned_file + .as_ref() .read_at(&mut refcount_table, table_offset) .is_ok()); for i in 0..table_size { @@ -928,9 +929,13 @@ mod test { let old_rct_size = cluster_sz as usize * rct_clusters as usize; let new_rct_size = cluster_sz as usize * new_rct_clusters as usize; let mut old_rc_table = vec![0_u8; old_rct_size]; - cloned_file.read_at(&mut old_rc_table, rct_offset).unwrap(); + cloned_file + .as_ref() + .read_at(&mut old_rc_table, rct_offset) + .unwrap(); let mut new_rc_table = vec![0_u8; new_rct_size]; cloned_file + .as_ref() .read_at(&mut new_rc_table, new_rct_offset as u64) .unwrap(); for i in 0..old_rct_size { diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index d8622d5aa..056d06437 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -45,7 +45,7 @@ unsafe impl Send for RawDriver {} unsafe impl Sync for RawDriver {} impl RawDriver { - pub fn new(file: File, aio: Aio, prop: BlockProperty) -> Self { + pub fn new(file: Arc, aio: Aio, prop: BlockProperty) -> Self { Self { driver: FileDriver::new(file, aio, prop), status: Arc::new(Mutex::new(BlockStatus::Init)), diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index b2f8d616c..db9929a3f 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -65,14 +65,14 @@ pub struct PFlash { impl PFlash { fn flash_region_size( region_max_size: u64, - backend: &Option, + backend: &Option>, read_only: bool, ) -> Result { // We don't have to occupy the whole memory region. // If flash is read-only, expose just real data size, // rounded up to page_size if let Some(fd) = backend.as_ref() { - let len = fd.metadata().unwrap().len(); + let len = fd.as_ref().metadata().unwrap().len(); if len > region_max_size || len == 0 || (!read_only && len != region_max_size) { bail!( "Invalid flash file: Region size 0x{region_max_size:X}, file size 0x{len:X}; read_only {read_only}" @@ -104,7 +104,7 @@ impl PFlash { #[allow(clippy::too_many_arguments)] pub fn new( region_max_size: u64, - backend: Option, + backend: Option>, block_len: u32, bank_width: u32, device_width: u32, @@ -937,13 +937,13 @@ mod test { fd.set_len(flash_size).unwrap(); drop(fd); - let fd = Some( + let fd = Some(Arc::new( std::fs::OpenOptions::new() .read(true) .write(true) .open(file_name) .unwrap(), - ); + )); let sysbus = sysbus_init(); let pflash = PFlash::new( flash_size, fd, sector_len, 4, 2, read_only, &sysbus, flash_base, diff --git a/image/src/img.rs b/image/src/img.rs index d37b5f39a..3d4738a44 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -40,7 +40,7 @@ enum SnapshotOperation { } pub struct ImageFile { - file: File, + file: Arc, path: String, } @@ -58,7 +58,7 @@ impl ImageFile { })?; Ok(Self { - file, + file: Arc::new(file), path: path.to_string(), }) } @@ -165,12 +165,14 @@ pub(crate) fn image_create(args: Vec) -> Result<()> { } let path = create_options.path.clone(); - let file = std::fs::OpenOptions::new() - .read(true) - .write(true) - .custom_flags(libc::O_CREAT | libc::O_TRUNC) - .mode(0o660) - .open(path)?; + let file = Arc::new( + std::fs::OpenOptions::new() + .read(true) + .write(true) + .custom_flags(libc::O_CREAT | libc::O_TRUNC) + .mode(0o660) + .open(path)?, + ); let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None)?; let image_info = match disk_fmt { @@ -221,10 +223,9 @@ pub(crate) fn image_info(args: Vec) -> Result<()> { ..Default::default() }; let mut driver: Box> = match detect_fmt { - DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.try_clone()?, aio, conf)), + DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.clone(), aio, conf)), DiskFormat::Qcow2 => { - let mut qocw2_driver = - Qcow2Driver::new(image_file.file.try_clone()?, aio, conf.clone())?; + let mut qocw2_driver = Qcow2Driver::new(image_file.file.clone(), aio, conf.clone())?; qocw2_driver.load_metadata(conf)?; Box::new(qocw2_driver) } @@ -291,7 +292,7 @@ pub(crate) fn image_check(args: Vec) -> Result<()> { let real_fmt = image_file.check_img_format(disk_fmt, detect_fmt)?; let mut check_res = CheckResult::default(); - let file = image_file.file.try_clone()?; + let file = image_file.file.clone(); match real_fmt { DiskFormat::Raw => { bail!("stratovirt-img: This image format does not support checks"); @@ -347,10 +348,9 @@ pub(crate) fn image_resize(mut args: Vec) -> Result<()> { ..Default::default() }; let mut driver: Box> = match real_fmt { - DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.try_clone()?, aio, conf)), + DiskFormat::Raw => Box::new(RawDriver::new(image_file.file.clone(), aio, conf)), DiskFormat::Qcow2 => { - let mut qocw2_driver = - Qcow2Driver::new(image_file.file.try_clone()?, aio, conf.clone())?; + let mut qocw2_driver = Qcow2Driver::new(image_file.file.clone(), aio, conf.clone())?; qocw2_driver.load_metadata(conf)?; Box::new(qocw2_driver) } @@ -457,7 +457,7 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { }; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); - let mut qcow2_driver = Qcow2Driver::new(image_file.file.try_clone()?, aio, qcow2_conf.clone())?; + let mut qcow2_driver = Qcow2Driver::new(image_file.file.clone(), aio, qcow2_conf.clone())?; qcow2_driver.load_metadata(qcow2_conf)?; match snapshot_operation { @@ -482,7 +482,7 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { } pub(crate) fn create_qcow2_driver_for_check( - file: File, + file: Arc, conf: BlockProperty, ) -> Result> { let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); @@ -577,7 +577,7 @@ mod test { pub header: QcowHeader, pub cluster_bits: u64, pub path: String, - pub file: File, + pub file: Arc, } impl TestQcow2Image { @@ -606,7 +606,7 @@ mod test { header, cluster_bits, path: path.to_string(), - file, + file: Arc::new(file), } } @@ -616,14 +616,13 @@ mod test { ..Default::default() }; let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); - let mut qcow2_driver = - Qcow2Driver::new(self.file.try_clone().unwrap(), aio, conf.clone()).unwrap(); + let mut qcow2_driver = Qcow2Driver::new(self.file.clone(), aio, conf.clone()).unwrap(); qcow2_driver.load_metadata(conf).unwrap(); qcow2_driver } fn create_driver_for_check(&self) -> Qcow2Driver<()> { - let file = self.file.try_clone().unwrap(); + let file = self.file.clone(); let conf = BlockProperty { format: DiskFormat::Qcow2, ..Default::default() @@ -669,7 +668,7 @@ mod test { } fn file_len(&mut self) -> u64 { - let file_len = self.file.seek(SeekFrom::End(0)).unwrap(); + let file_len = self.file.as_ref().seek(SeekFrom::End(0)).unwrap(); file_len } @@ -706,12 +705,12 @@ mod test { } fn create_driver(&mut self) -> RawDriver<()> { - let mut conf = BlockProperty::default(); + let conf = BlockProperty::default(); let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let file = open_file(&self.path, false, false).unwrap(); - let raw_driver = RawDriver::new(file, aio, conf); + let raw_driver = RawDriver::new(Arc::new(file), aio, conf); raw_driver } } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index ecb96e2c2..6b9a80a6c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1983,7 +1983,7 @@ pub trait MachineOps: MachineLifecycle { } /// Fetch a cloned file from drive backend files. - fn fetch_drive_file(&self, path: &str) -> Result { + fn fetch_drive_file(&self, path: &str) -> Result> { let files = self.get_drive_files(); let drive_files = files.lock().unwrap(); VmConfig::fetch_drive_file(&drive_files, path) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index e8ddab4f3..60944fb99 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1653,13 +1653,13 @@ impl DeviceInterface for StdMachine { let mut fd = None; if args.region_type.eq("rom_device_region") || args.region_type.eq("ram_device_region") { if let Some(file_name) = args.device_fd_path { - fd = Some( + fd = Some(Arc::new( std::fs::OpenOptions::new() .read(true) .write(true) .open(file_name) .unwrap(), - ); + )); } } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index e61d55897..3a1169114 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -575,8 +575,8 @@ impl MachineOps for StdMachine { // of current PFlash device. let mut flash_end: u64 = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; for config in configs_vec { - let mut fd = self.fetch_drive_file(&config.path_on_host)?; - let pfl_size = fd.metadata().unwrap().len(); + let file = self.fetch_drive_file(&config.path_on_host)?; + let pfl_size = file.as_ref().metadata()?.len(); if config.unit.unwrap() == 0 { // According to the Linux/x86 boot protocol, the memory region of @@ -584,7 +584,7 @@ impl MachineOps for StdMachine { // KiB is for BIOS code which is stored in the first PFlash. let rom_base = 0xe0000; let rom_size = 0x20000; - fd.seek(SeekFrom::Start(pfl_size - rom_size))?; + file.as_ref().seek(SeekFrom::Start(pfl_size - rom_size))?; let ram1 = Arc::new(HostMemMapping::new( GuestAddress(rom_base), @@ -596,18 +596,18 @@ impl MachineOps for StdMachine { false, )?); let rom_region = Region::init_ram_region(ram1, "PflashRam"); - rom_region.write(&mut fd, GuestAddress(rom_base), 0, rom_size)?; + rom_region.write(&mut file.as_ref(), GuestAddress(rom_base), 0, rom_size)?; rom_region.set_priority(10); self.base .sys_mem .root() .add_subregion(rom_region, rom_base)?; - fd.rewind()? + file.as_ref().rewind()? } let sector_len: u32 = 1024 * 4; - let backend = Some(fd); + let backend = Some(file); let pflash = PFlash::new( pfl_size, backend, diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index e8e1a60ca..a6f9fd750 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -14,6 +14,7 @@ use std::fs::{metadata, File}; use std::os::linux::fs::MetadataExt; use std::path::Path; use std::str::FromStr; +use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use clap::{ArgAction, Parser}; @@ -37,7 +38,7 @@ pub struct DriveFile { /// Drive id. pub id: String, /// The opened file. - pub file: File, + pub file: Arc, /// The num of drives share same file. pub count: u32, /// File path. diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 3fbc58184..e172f7d58 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -64,6 +64,7 @@ use std::fs::{canonicalize, File}; use std::io::Read; use std::path::Path; use std::str::FromStr; +use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; @@ -305,7 +306,7 @@ impl VmConfig { } let drive_file = DriveFile { id: id.to_string(), - file, + file: Arc::new(file), count: 1, read_only, path: path.to_string(), @@ -337,12 +338,12 @@ impl VmConfig { } /// Get a file from drive file store. - pub fn fetch_drive_file(drive_files: &HashMap, path: &str) -> Result { + pub fn fetch_drive_file( + drive_files: &HashMap, + path: &str, + ) -> Result> { match drive_files.get(path) { - Some(drive_file) => drive_file - .file - .try_clone() - .with_context(|| format!("Failed to clone drive backend file {}", path)), + Some(drive_file) => Ok(drive_file.file.clone()), None => Err(anyhow!("The file {} is not in drive backend", path)), } } -- Gitee From e48ccca5c16ee1edffa56a64fc80e217cb38e18f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 18 Jun 2024 05:26:40 +0800 Subject: [PATCH 1913/2187] address_space: optimize the process from gpa iovecs to hva iovecs Optimize the process from gpa iovecs to hva iovecs: 1) libc::iovec is pub struct iovec { pub iov_base: *mut ::c_void, pub iov_len: ::size_t, } and it equals to util::aio::Iovec pub struct Iovec { pub iov_base: u64, pub iov_len: u64, } On 64 bit platforms. At present, we are already mixing libc::iovec and util::aio::Iovec, So, just convert from GPA to HVA with unified util::aio::Iovec type. Use `gpa_hva_iovec_map` to do this. 2) add cache in parameters to improve performance and remove meaningless encapsulation functions. Signed-off-by: liuxiangdong --- virtio/src/device/block.rs | 5 ++--- virtio/src/device/gpu.rs | 6 +++--- virtio/src/device/net.rs | 17 +++++++++------- virtio/src/device/scsi_cntlr.rs | 9 ++++++--- virtio/src/device/serial.rs | 3 ++- virtio/src/lib.rs | 36 --------------------------------- 6 files changed, 23 insertions(+), 53 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 26fa748fe..a8515cd74 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -26,7 +26,7 @@ use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, gpa_hva_iovec_map_by_cache, iov_discard_back, iov_discard_front, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_to_buf_by_cache, read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, @@ -345,8 +345,7 @@ impl Request { } .with_context(|| "Empty data for block request")?; - let (data_len, iovec) = - gpa_hva_iovec_map_by_cache(data_iovec, &handler.mem_space, cache)?; + let (data_len, iovec) = gpa_hva_iovec_map(data_iovec, &handler.mem_space, cache)?; request.data_len = data_len; request.iovec = iovec; } diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index acd74d0ab..3b38d2adc 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -432,8 +432,8 @@ impl VirtioGpuRequest { iov_discard_front(&mut elem.out_iovec, size_of::() as u64) .unwrap_or_default(); - let (out_len, out_iovec) = gpa_hva_iovec_map(data_iovec, mem_space)?; - let (in_len, in_iovec) = gpa_hva_iovec_map(&elem.in_iovec, mem_space)?; + let (out_len, out_iovec) = gpa_hva_iovec_map(data_iovec, mem_space, &None)?; + let (in_len, in_iovec) = gpa_hva_iovec_map(&elem.in_iovec, mem_space, &None)?; // Note: in_iov and out_iov total len is no more than 1<<32, and // out_iov is more than 1, so in_len and out_len will not overflow. @@ -1491,7 +1491,7 @@ impl GpuIoHandler { len: ent.length, }); } - match gpa_hva_iovec_map(&elemiovec, &self.mem_space) { + match gpa_hva_iovec_map(&elemiovec, &self.mem_space, &None) { Ok((_, iov)) => { res.iov = iov; self.response_nodata(VIRTIO_GPU_RESP_OK_NODATA, req) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 25abc3455..79d5e0c23 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -24,10 +24,11 @@ use anyhow::{bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, warn}; use once_cell::sync::Lazy; +use util::aio::Iovec; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, get_libc_iovecs, iov_discard_front, iov_to_buf, mem_to_buf, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, mem_to_buf, read_config_default, report_virtio_error, virtio_has_feature, ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VirtioNetHdr, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_NET_CTRL_MAC, @@ -718,7 +719,7 @@ struct NetIoHandler { } impl NetIoHandler { - fn read_from_tap(iovecs: &[libc::iovec], tap: &mut Tap) -> i32 { + fn read_from_tap(iovecs: &[Iovec], tap: &mut Tap) -> i32 { // SAFETY: the arguments of readv has been checked and is correct. let size = unsafe { libc::readv( @@ -773,7 +774,8 @@ impl NetIoHandler { } else if elem.in_iovec.is_empty() { bail!("The length of in iovec is 0"); } - let iovecs = get_libc_iovecs(&self.mem_space, queue.vring.get_cache(), &elem.in_iovec); + let (_, iovecs) = + gpa_hva_iovec_map(&elem.in_iovec, &self.mem_space, queue.vring.get_cache())?; if MigrationManager::is_active() { // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. @@ -845,7 +847,7 @@ impl NetIoHandler { Ok(()) } - fn send_packets(&self, tap_fd: libc::c_int, iovecs: &[libc::iovec]) -> i8 { + fn send_packets(&self, tap_fd: libc::c_int, iovecs: &[Iovec]) -> i8 { loop { // SAFETY: the arguments of writev has been checked and is correct. let size = unsafe { @@ -885,7 +887,8 @@ impl NetIoHandler { bail!("The length of out iovec is 0"); } - let iovecs = get_libc_iovecs(&self.mem_space, queue.vring.get_cache(), &elem.out_iovec); + let (_, iovecs) = + gpa_hva_iovec_map(&elem.out_iovec, &self.mem_space, queue.vring.get_cache())?; let tap_fd = if let Some(tap) = self.tap.as_mut() { tap.as_raw_fd() as libc::c_int } else { @@ -1004,13 +1007,13 @@ impl NetIoHandler { } } -fn get_net_header(iovec: &[libc::iovec], buf: &mut [u8]) -> Result { +fn get_net_header(iovec: &[Iovec], buf: &mut [u8]) -> Result { let mut start: usize = 0; let mut end: usize = 0; for elem in iovec { end = start - .checked_add(elem.iov_len) + .checked_add(elem.iov_len as usize) .with_context(|| "Overflow when getting the net header")?; end = cmp::min(end, buf.len()); mem_to_buf(&mut buf[start..end], elem.iov_base as u64)?; diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 26db5f132..3c77e41cb 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -508,7 +508,7 @@ impl VirtioScsiReq let mut request = VirtioScsiRequest { mem_space: mem_space.clone(), - queue, + queue: queue.clone(), desc_index: elem.index, iovec: Vec::with_capacity(elem.desc_num as usize), data_len: 0, @@ -521,15 +521,18 @@ impl VirtioScsiReq resp, }; + let locked_queue = queue.lock().unwrap(); + let cache = locked_queue.vring.get_cache(); + // Get possible dataout buffer from virtqueue Element. let mut iovec = elem.out_iovec.clone(); let elemiov = iov_discard_front(&mut iovec, size_of::() as u64).unwrap_or_default(); - let (out_len, out_iovec) = gpa_hva_iovec_map(elemiov, mem_space)?; + let (out_len, out_iovec) = gpa_hva_iovec_map(elemiov, mem_space, cache)?; // Get possible dataout buffer from virtqueue Element. let mut iovec = elem.in_iovec.clone(); let elemiov = iov_discard_front(&mut iovec, size_of::() as u64).unwrap_or_default(); - let (in_len, in_iovec) = gpa_hva_iovec_map(elemiov, mem_space)?; + let (in_len, in_iovec) = gpa_hva_iovec_map(elemiov, mem_space, cache)?; if out_len > 0 && in_len > 0 { warn!("Wrong scsi request! Don't support both datain and dataout buffer"); diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 73aa4ac64..9ad7b3a21 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -889,7 +889,8 @@ impl SerialControlHandler { return Ok(()); } - let (in_size, ctrl_vec) = gpa_hva_iovec_map(&elem.in_iovec, &self.mem_space)?; + let cache = queue_lock.vring.get_cache(); + let (in_size, ctrl_vec) = gpa_hva_iovec_map(&elem.in_iovec, &self.mem_space, cache)?; let len = size_of::() + extra.len(); if in_size < len as u64 { bail!( diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 54bc52560..a3aa50237 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -850,13 +850,6 @@ pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ fn gpa_hva_iovec_map( gpa_elemiovec: &[ElemIovec], mem_space: &AddressSpace, -) -> Result<(u64, Vec)> { - gpa_hva_iovec_map_by_cache(gpa_elemiovec, mem_space, &None) -} - -fn gpa_hva_iovec_map_by_cache( - gpa_elemiovec: &[ElemIovec], - mem_space: &AddressSpace, cache: &Option, ) -> Result<(u64, Vec)> { let mut iov_size = 0; @@ -870,35 +863,6 @@ fn gpa_hva_iovec_map_by_cache( Ok((iov_size, hva_iovec)) } -fn get_libc_iovecs( - mem_space: &Arc, - cache: &Option, - elem_iovecs: &[ElemIovec], -) -> Vec { - let mut iovecs = Vec::new(); - for elem_iov in elem_iovecs.iter() { - // elem_iov.addr has been checked in pop_avail(). - let mut len = elem_iov.len; - let mut start = elem_iov.addr; - loop { - let io_vec = mem_space - .get_host_address_from_cache(start, cache) - .map(|(hva, fr_len)| libc::iovec { - iov_base: hva as *mut libc::c_void, - iov_len: std::cmp::min(elem_iov.len, fr_len as u32) as libc::size_t, - }) - .unwrap(); - start = start.unchecked_add(io_vec.iov_len as u64); - len -= io_vec.iov_len as u32; - iovecs.push(io_vec); - if len == 0 { - break; - } - } - } - iovecs -} - pub fn virtio_register_sysbusdevops_type() -> Result<()> { register_sysbusdevops_type::() } -- Gitee From 7cc89c2996b99390455506e410c8dda0298f4598 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 18 Jun 2024 16:07:46 +0800 Subject: [PATCH 1914/2187] virtio: add `iov_read_object` function to obtain a specific type from iov We need to read some specific type objects from iov in virtio message. Add function `iov_read_object` to do this. Signed-off-by: liuxiangdong --- virtio/src/device/block.rs | 29 ++++++++---------------- virtio/src/device/gpu.rs | 18 +++++---------- virtio/src/device/net.rs | 2 +- virtio/src/device/scsi_cntlr.rs | 40 ++++++++------------------------- virtio/src/device/serial.rs | 23 ++++++++----------- virtio/src/lib.rs | 21 +++++++++++++---- 6 files changed, 51 insertions(+), 82 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index a8515cd74..601b41d78 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -26,13 +26,13 @@ use log::{error, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, gpa_hva_iovec_map, iov_discard_back, iov_discard_front, - iov_to_buf_by_cache, read_config_default, report_virtio_error, virtio_has_feature, Element, - Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, - VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, - VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, - VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, - VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_back, iov_discard_front, iov_read_object, + read_config_default, report_virtio_error, virtio_has_feature, Element, Queue, VirtioBase, + VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_BLK_F_DISCARD, + VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_SEG_MAX, + VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, + VIRTIO_BLK_S_UNSUPP, VIRTIO_BLK_T_DISCARD, VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, + VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT, VIRTIO_BLK_T_WRITE_ZEROES, VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; @@ -286,19 +286,8 @@ impl Request { ); } - let mut out_header = RequestOutHeader::default(); - iov_to_buf_by_cache( - &handler.mem_space, - cache, - &elem.out_iovec, - out_header.as_mut_bytes(), - ) - .and_then(|size| { - if size < size_of::() { - bail!("Invalid out header for block request: length {}", size); - } - Ok(()) - })?; + let mut out_header = + iov_read_object::(&handler.mem_space, &elem.out_iovec, cache)?; out_header.request_type = LittleEndian::read_u32(out_header.request_type.as_bytes()); out_header.sector = LittleEndian::read_u64(out_header.sector.as_bytes()); diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 3b38d2adc..d2c1078b2 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -23,11 +23,11 @@ use log::{error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, - ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioDeviceQuirk, VirtioError, - VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, - VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID, - VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_read_object, + read_config_default, ElemIovec, Element, Queue, VirtioBase, VirtioDevice, VirtioDeviceQuirk, + VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, + VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_FLAG_FENCE, @@ -418,13 +418,7 @@ impl VirtioGpuRequest { ); } - let mut header = VirtioGpuCtrlHdr::default(); - iov_to_buf(mem_space, &elem.out_iovec, header.as_mut_bytes()).and_then(|size| { - if size < size_of::() { - bail!("Invalid header for gpu request: len {}.", size) - } - Ok(()) - })?; + let header = iov_read_object::(mem_space, &elem.out_iovec, &None)?; // Size of out_iovec is no less than size of VirtioGpuCtrlHdr, so // it is possible to get none back. diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 79d5e0c23..fde5613ed 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -466,7 +466,7 @@ fn get_buf_and_discard( iovec: &mut [ElemIovec], buf: &mut [u8], ) -> Result> { - iov_to_buf(mem_space, iovec, buf).and_then(|size| { + iov_to_buf(mem_space, &None, iovec, buf).and_then(|size| { if size < buf.len() { error!("Invalid length {}, expected length {}", size, buf.len()); bail!("Invalid length {}, expected length {}", size, buf.len()); diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 3c77e41cb..fa31c9b9a 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -22,10 +22,10 @@ use log::{error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use crate::{ - check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_to_buf, read_config_default, - report_virtio_error, Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, - VIRTIO_TYPE_SCSI, + check_config_space_rw, gpa_hva_iovec_map, iov_discard_front, iov_read_object, + read_config_default, report_virtio_error, Element, Queue, VirtioBase, VirtioDevice, + VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, + VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; use address_space::{AddressSpace, GuestAddress}; use block_backend::BlockIoErrorCallback; @@ -479,32 +479,13 @@ impl VirtioScsiReq elem.desc_num ); } + let locked_queue = queue.lock().unwrap(); + let cache = locked_queue.vring.get_cache(); // Get request from virtqueue Element. - let mut req = T::default(); - iov_to_buf(mem_space, &elem.out_iovec, req.as_mut_bytes()).and_then(|size| { - if size < size_of::() { - bail!( - "Invalid length for request: get {}, expected {}", - size, - size_of::(), - ); - } - Ok(()) - })?; - + let req = iov_read_object::(mem_space, &elem.out_iovec, cache)?; // Get response from virtqueue Element. - let mut resp = U::default(); - iov_to_buf(mem_space, &elem.in_iovec, resp.as_mut_bytes()).and_then(|size| { - if size < size_of::() { - bail!( - "Invalid length for response: get {}, expected {}", - size, - size_of::(), - ); - } - Ok(()) - })?; + let resp = iov_read_object::(mem_space, &elem.in_iovec, cache)?; let mut request = VirtioScsiRequest { mem_space: mem_space.clone(), @@ -515,15 +496,12 @@ impl VirtioScsiReq mode: ScsiXferMode::ScsiXferNone, interrupt_cb, driver_features, - // Safety: in_iovec will not be empty since it has been checked after "iov_to_buf". + // Safety: in_iovec will not be empty since it has been checked after "iov_read_object". resp_addr: elem.in_iovec[0].addr, req, resp, }; - let locked_queue = queue.lock().unwrap(); - let cache = locked_queue.vring.get_cache(); - // Get possible dataout buffer from virtqueue Element. let mut iovec = elem.out_iovec.clone(); let elemiov = iov_discard_front(&mut iovec, size_of::() as u64).unwrap_or_default(); diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 9ad7b3a21..f8a1e35fe 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -25,8 +25,8 @@ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use crate::{ - gpa_hva_iovec_map, iov_to_buf, read_config_default, report_virtio_error, Element, Queue, - VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + gpa_hva_iovec_map, iov_read_object, iov_to_buf, read_config_default, report_virtio_error, + Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; use address_space::AddressSpace; @@ -476,7 +476,8 @@ impl SerialPortHandler { let iovec = elem.out_iovec; let iovec_size = Element::iovec_size(&iovec); let mut buf = vec![0u8; iovec_size as usize]; - let size = iov_to_buf(&self.mem_space, &iovec, &mut buf[..])? as u64; + let cache = queue_lock.vring.get_cache(); + let size = iov_to_buf(&self.mem_space, cache, &iovec, &mut buf[..])? as u64; let locked_port = port.lock().unwrap(); if locked_port.host_connected { @@ -739,17 +740,11 @@ impl SerialControlHandler { break; } - let mut req = VirtioConsoleControl::default(); - iov_to_buf(&self.mem_space, &elem.out_iovec, req.as_mut_bytes()).and_then(|size| { - if size < size_of::() { - bail!( - "Invalid length for request: get {}, expected {}", - size, - size_of::(), - ); - } - Ok(()) - })?; + let mut req = iov_read_object::( + &self.mem_space, + &elem.out_iovec, + queue_lock.vring.get_cache(), + )?; req.id = LittleEndian::read_u32(req.id.as_bytes()); req.event = LittleEndian::read_u16(req.event.as_bytes()); req.value = LittleEndian::read_u16(req.value.as_bytes()); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index a3aa50237..4c3e8e87c 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -49,6 +49,7 @@ pub use vhost::user as VhostUser; use std::cmp; use std::io::Write; +use std::mem::size_of; use std::os::unix::prelude::RawFd; use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicU8, Ordering}; use std::sync::{Arc, Mutex}; @@ -63,6 +64,7 @@ use devices::sysbus::register_sysbusdevops_type; use machine_manager::config::ConfigCheck; use migration_derive::ByteCode; use util::aio::{mem_to_buf, Iovec}; +use util::byte_code::ByteCode; use util::num_ops::{read_u32, write_u32}; use util::AsAny; @@ -790,12 +792,23 @@ pub fn report_virtio_error( broken.store(true, Ordering::SeqCst); } -/// Read iovec to buf and return the read number of bytes. -pub fn iov_to_buf(mem_space: &AddressSpace, iovec: &[ElemIovec], buf: &mut [u8]) -> Result { - iov_to_buf_by_cache(mem_space, &None, iovec, buf) +/// Read object typed `T` from iovec. +pub fn iov_read_object( + mem_space: &Arc, + iovec: &[ElemIovec], + cache: &Option, +) -> Result { + let mut obj = T::default(); + let count = iov_to_buf(mem_space, cache, iovec, obj.as_mut_bytes())?; + let size = size_of::(); + if count < size { + bail!("Read length error: expected {}, read {}.", size, count); + } + Ok(obj) } -pub fn iov_to_buf_by_cache( +/// Read iovec to buf and return the read number of bytes. +pub fn iov_to_buf( mem_space: &AddressSpace, cache: &Option, iovec: &[ElemIovec], -- Gitee From c00162b7d57de4cc7fbc9becb46ac70f39457daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Wed, 19 Jun 2024 09:34:51 +0800 Subject: [PATCH 1915/2187] Main: Add vm config fail error log The error information is added to prevent users from failing to obtain the error infomation when they fail to read the standard error. Signed-off-by: Yihua Jin --- src/main.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 4a7775646..f97a395be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -103,7 +103,13 @@ fn run() -> Result<()> { exit_with_code(VM_EXIT_GENE_ERR); })); - let mut vm_config: VmConfig = create_vmconfig(&cmd_args)?; + let mut vm_config: VmConfig = match create_vmconfig(&cmd_args) { + Ok(vm_cfg) => vm_cfg, + Err(e) => { + error!("Failed to create vmconfig {:?}", e); + return Err(e); + } + }; info!("VmConfig is {:?}", vm_config); match real_main(&cmd_args, &mut vm_config) { -- Gitee From 6a23e7bcc8136aff744acae9480cdd9ab15bf02f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 19 Jun 2024 12:18:56 +0800 Subject: [PATCH 1916/2187] net: fix some clippy warnings fix some clippy warnings. Signed-off-by: liuxiangdong --- virtio/src/device/net.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index fde5613ed..ccf5e6c49 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -781,7 +781,7 @@ impl NetIoHandler { // FIXME: mark dirty page needs to be managed by `AddressSpace` crate. for iov in iovecs.iter() { // Mark vmm dirty page manually if live migration is active. - MigrationManager::mark_dirty_log(iov.iov_base as u64, iov.iov_len as u64); + MigrationManager::mark_dirty_log(iov.iov_base, iov.iov_len); } } @@ -1016,7 +1016,7 @@ fn get_net_header(iovec: &[Iovec], buf: &mut [u8]) -> Result { .checked_add(elem.iov_len as usize) .with_context(|| "Overflow when getting the net header")?; end = cmp::min(end, buf.len()); - mem_to_buf(&mut buf[start..end], elem.iov_base as u64)?; + mem_to_buf(&mut buf[start..end], elem.iov_base)?; if end >= buf.len() { break; } -- Gitee From 2d5215af53e7577d6ae4382f89a4d7d1e4f2d5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Wed, 19 Jun 2024 15:54:52 +0800 Subject: [PATCH 1917/2187] Main: Remove redundant branch When display log is not set, logs are output to stderr by default. So just let it handle by default. Signed-off-by: Yihua Jin --- src/main.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index f97a395be..cff73cbe2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,12 +121,7 @@ fn run() -> Result<()> { } Err(ref e) => { set_termi_canon_mode().expect("Failed to set terminal to canonical mode."); - if cmd_args.is_present("display log") { - error!("{}", format!("{:?}\r\n", e)); - } else { - write!(&mut std::io::stderr(), "{}", format_args!("{:?}\r\n", e)) - .expect("Failed to write to stderr"); - } + error!("{}", format!("{:?}\r\n", e)); // clean temporary file TempCleaner::clean(); exit_with_code(VM_EXIT_GENE_ERR); -- Gitee From f15d988e02e4ab2e2bca75f58b4782706e551904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Thu, 20 Jun 2024 13:03:58 +0800 Subject: [PATCH 1918/2187] Acpi: Disable s0 idle sleep Remove the s0 idle sleep capability. Signed-off-by: Yihua Jin --- machine/src/standard_common/mod.rs | 4 ++-- tests/mod_test/tests/aarch64/acpi_test.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index f18350ae4..8fb208cd6 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -606,8 +606,8 @@ pub(crate) trait AcpiBuilder { fadt.set_field(91, 4); #[cfg(target_arch = "aarch64")] { - // FADT flag: enable HW_REDUCED_ACPI and LOW_POWER_S0_IDLE_CAPABLE bit on aarch64 plantform. - fadt.set_field(112, 1 << 21 | 1 << 20 | 1 << 10 | 1 << 8); + // FADT flag: enable HW_REDUCED_ACPI bit on aarch64 plantform. + fadt.set_field(112, 1 << 20 | 1 << 10 | 1 << 8); // ARM Boot Architecture Flags fadt.set_field(129, 0x3_u16); } diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index d28c8221d..6ef32b646 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -103,8 +103,8 @@ fn check_fadt(data: &[u8]) -> (u32, u64) { assert_eq!(String::from_utf8_lossy(&data[..4]), "FACP"); assert_eq!(LittleEndian::read_u32(&data[4..]), FADT_TABLE_DATA_LENGTH); // Check length - // Enable HW_REDUCED_ACPI and LOW_POWER_S0_IDLE_CAPABLE bit - assert_eq!(LittleEndian::read_i32(&data[112..]), 0x30_0500); + // Enable HW_REDUCED_ACPI bit + assert_eq!(LittleEndian::read_i32(&data[112..]), 0x10_0500); assert_eq!(LittleEndian::read_u16(&data[129..]), 0x3); // ARM Boot Architecture Flags assert_eq!(LittleEndian::read_i32(&data[131..]), 3); // FADT minor revision -- Gitee From 5aae8db00f4eea7cfde01f12e8058fb101f1d408 Mon Sep 17 00:00:00 2001 From: Huxiaohang Date: Mon, 24 Jun 2024 12:09:10 +0800 Subject: [PATCH 1919/2187] fix mst compile error This error was introduced by commit 75ed42cc79e376c4757f907cef1bab2e1e7e43c4, it deleted the Copy trait of ByteCode, which will cause errors in mst, so change Copy to Clone should be better modification. --- util/src/byte_code.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/byte_code.rs b/util/src/byte_code.rs index 5c45ade09..29d82c68a 100644 --- a/util/src/byte_code.rs +++ b/util/src/byte_code.rs @@ -15,7 +15,7 @@ use std::slice::{from_raw_parts, from_raw_parts_mut}; /// A trait bound defined for types which are safe to convert to a byte slice and /// to create from a byte slice. -pub trait ByteCode: Default + Send + Sync { +pub trait ByteCode: Clone + Default + Send + Sync { /// Return the contents of an object (impl trait `ByteCode`) as a slice of bytes. /// the inverse of this function is "from_bytes" fn as_bytes(&self) -> &[u8] { -- Gitee From f1ce80cca5391982533e60f0a86704a9535f6ee0 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 21 Jun 2024 12:22:21 +0800 Subject: [PATCH 1920/2187] machine_manager: introduce NotifierManager to notify vm pause/resume This commit introduces NotifierManager to manage all notifiers and notify them when VM is paused or resumed. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 43 +++++++++---------- machine/src/aarch64/standard.rs | 6 +-- machine/src/lib.rs | 30 ++++---------- machine_manager/src/lib.rs | 1 + machine_manager/src/machine.rs | 4 -- machine_manager/src/notifier.rs | 73 +++++++++++++++++++++++++++++++++ 6 files changed, 102 insertions(+), 55 deletions(-) create mode 100644 machine_manager/src/notifier.rs diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index a583b6c33..cf96e3ac9 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -40,7 +40,7 @@ use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; -use machine_manager::machine::PauseNotify; +use machine_manager::notifier::register_vm_pause_notifier; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] @@ -433,16 +433,8 @@ pub struct Scream { size: u64, config: ScreamConfig, token_id: Option>>, - interface_resource: RwLock>>>, -} - -impl PauseNotify for Scream { - fn notify(&self, paused: bool) { - let interfaces = self.interface_resource.read().unwrap(); - for interface in interfaces.iter() { - interface.lock().unwrap().pause(paused); - } - } + interface_resource: Vec>>, + notify_id: u64, } impl Scream { @@ -465,7 +457,8 @@ impl Scream { size, config, token_id, - interface_resource: RwLock::new(Vec::new()), + interface_resource: Vec::new(), + notify_id: 0, }) } @@ -486,14 +479,11 @@ impl Scream { } } - fn start_play_thread_fn(&self) -> Result<()> { + fn start_play_thread_fn(&mut self) -> Result<()> { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamPlay", ScreamDirection::Playback); - self.interface_resource - .write() - .unwrap() - .push(interface.clone()); + self.interface_resource.push(interface.clone()); thread::Builder::new() .name("scream audio play worker".to_string()) .spawn(move || { @@ -515,15 +505,12 @@ impl Scream { Ok(()) } - fn start_record_thread_fn(&self) -> Result<()> { + fn start_record_thread_fn(&mut self) -> Result<()> { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); let _ti = self.token_id.clone(); - self.interface_resource - .write() - .unwrap() - .push(interface.clone()); + self.interface_resource.push(interface.clone()); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { @@ -568,7 +555,17 @@ impl Scream { ivshmem.realize()?; self.start_play_thread_fn()?; - self.start_record_thread_fn() + self.start_record_thread_fn()?; + + let cloned_interfaces = self.interface_resource.clone(); + let pause_notify = Arc::new(move |paused: bool| { + for interface in cloned_interfaces.iter() { + interface.lock().unwrap().pause(paused); + } + }); + self.notify_id = register_vm_pause_notifier(pause_notify); + + Ok(()) } } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index fe8e6a02d..289083cfa 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -61,7 +61,7 @@ use machine_manager::config::{ BootIndexInfo, DriveConfig, NumaNode, Param, SerialConfig, VmConfig, }; use machine_manager::event; -use machine_manager::machine::{MachineLifecycle, PauseNotify}; +use machine_manager::machine::MachineLifecycle; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; #[cfg(feature = "gtk")] @@ -409,10 +409,6 @@ impl MachineOps for StdMachine { .add_subregion(ram, MEM_LAYOUT[LayoutEntryType::Mem as usize].0) } - fn register_vm_pause_notifier(&mut self, notifier: Arc) { - self.base.pause_notifiers.push(notifier); - } - fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()> { let v3 = ICGICv3Config { msi: true, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c02f54aba..f331c60ea 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -85,9 +85,8 @@ use machine_manager::config::{ VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; -use machine_manager::machine::{ - HypervisorType, MachineInterface, MachineLifecycle, PauseNotify, VmState, -}; +use machine_manager::machine::{HypervisorType, MachineInterface, MachineLifecycle, VmState}; +use machine_manager::notifier::pause_notify; use machine_manager::{check_arg_exist, check_arg_nonexist}; use migration::{MigrateOps, MigrationManager}; #[cfg(feature = "windows_emu_pid")] @@ -153,8 +152,6 @@ pub struct MachineBase { hypervisor: Arc>, /// migrate hypervisor. migration_hypervisor: Arc>, - /// vm pause notifiers. - pause_notifiers: Vec>, } impl MachineBase { @@ -231,7 +228,6 @@ impl MachineBase { machine_ram, hypervisor, migration_hypervisor, - pause_notifiers: Vec::new(), }) } @@ -1654,9 +1650,6 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } - /// Register vm pause listener. - fn register_vm_pause_notifier(&mut self, _listener: Arc) {} - /// Add scream sound based on ivshmem. /// /// # Arguments @@ -1691,9 +1684,7 @@ pub trait MachineOps: MachineLifecycle { let mut scream = Scream::new(mem_cfg.size, config, token_id)?; scream .realize(parent_bus) - .with_context(|| "Failed to realize scream device")?; - self.register_vm_pause_notifier(Arc::new(scream)); - Ok(()) + .with_context(|| "Failed to realize scream device") } /// Get the corresponding device from the PCI bus based on the device id and device type name. @@ -1992,13 +1983,6 @@ pub trait MachineOps: MachineLifecycle { self.machine_base().drive_files.clone() } - //// Trigger vm pause notifiers. - fn notify_vm_pause_notifiers(&self, paused: bool) { - for notifier in self.machine_base().pause_notifiers.iter() { - notifier.notify(paused); - } - } - /// Fetch a cloned file from drive backend files. fn fetch_drive_file(&self, path: &str) -> Result { let files = self.get_drive_files(); @@ -2148,7 +2132,7 @@ pub trait MachineOps: MachineLifecycle { *vm_state = VmState::Paused; // Notify VM paused. - self.notify_vm_pause_notifiers(true); + pause_notify(true); Ok(()) } @@ -2164,6 +2148,9 @@ pub trait MachineOps: MachineLifecycle { self.active_drive_files()?; + // Notify VM resumed. + pause_notify(false); + for (cpu_index, cpu) in cpus.iter().enumerate() { if let Err(e) = cpu.resume() { self.deactive_drive_files()?; @@ -2173,9 +2160,6 @@ pub trait MachineOps: MachineLifecycle { *vm_state = VmState::Running; - // Notify VM resumed. - self.notify_vm_pause_notifiers(false); - Ok(()) } diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index 78c6a77e9..b1475fce2 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -26,6 +26,7 @@ pub mod config; pub mod error; pub mod event_loop; pub mod machine; +pub mod notifier; pub mod qmp; pub mod signal_handler; pub mod socket; diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index cd37eefd4..a040d6efa 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -67,10 +67,6 @@ impl FromStr for HypervisorType { } } -pub trait PauseNotify: Send + Sync { - fn notify(&self, paused: bool); -} - /// Trait to handle virtual machine lifecycle. /// /// # Notes diff --git a/machine_manager/src/notifier.rs b/machine_manager/src/notifier.rs new file mode 100644 index 000000000..36285f36c --- /dev/null +++ b/machine_manager/src/notifier.rs @@ -0,0 +1,73 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +use log::error; +use once_cell::sync::Lazy; + +static NOTIFIER_MANAGER: Lazy> = + Lazy::new(|| RwLock::new(NotifierManager::new())); + +pub type PauseNOtifyCallback = dyn Fn(bool) + Send + Sync; + +struct NotifierManager { + pause_notifiers: HashMap>, + next_id: u64, +} + +impl NotifierManager { + fn new() -> Self { + Self { + pause_notifiers: HashMap::new(), + next_id: 1, + } + } + + fn register_pause_notifier(&mut self, notifier: Arc) -> u64 { + let id = self.next_id; + self.pause_notifiers.insert(id, notifier); + self.next_id += 1; + id + } + + fn unregister_pause_notifier(&mut self, id: u64) { + if self.pause_notifiers.remove(&id).is_none() { + error!("There is no pause notifier with id {}", id); + } + } + + fn pause_notify(&self, paused: bool) { + for (_, notify) in self.pause_notifiers.iter() { + notify(paused); + } + } +} + +pub fn register_vm_pause_notifier(notifier: Arc) -> u64 { + NOTIFIER_MANAGER + .write() + .unwrap() + .register_pause_notifier(notifier) +} + +pub fn unregister_vm_pause_notifier(id: u64) { + NOTIFIER_MANAGER + .write() + .unwrap() + .unregister_pause_notifier(id) +} + +pub fn pause_notify(paused: bool) { + NOTIFIER_MANAGER.read().unwrap().pause_notify(paused); +} -- Gitee From 9c443cf1fb3032b3222021efcf0ff47e94797bca Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 22 Jun 2024 11:28:41 +0800 Subject: [PATCH 1921/2187] ohcam: stop/start camera stream when VM is paused/resumed This patch adds an new interface to CameraBackend trait that is used to monitor VM paused/resumed. This can solve the issue that camera keeps being used after VM paused. Signed-off-by: Zhao Yi Min --- devices/src/camera_backend/mod.rs | 3 +++ devices/src/camera_backend/ohcam.rs | 33 ++++++++++++++++++++++++++++- devices/src/usb/camera.rs | 12 +++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 00723e5e2..6e67d7624 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -184,6 +184,9 @@ pub trait CameraBackend: Send + Sync { /// Register broken callback which is called when backend is broken. fn register_broken_cb(&mut self, cb: CameraBrokenCallback); + + /// Pause/resume stream. + fn pause(&mut self, _paused: bool) {} } #[allow(unused_variables)] diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 55b6a80ae..096f24b1d 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -120,6 +120,8 @@ pub struct OhCameraBackend { ctx: OhCamera, fmt_list: Vec, selected_profile: u8, + stream_on: bool, + paused: bool, #[cfg(any( feature = "trace_to_logger", feature = "trace_to_ftrace", @@ -160,6 +162,8 @@ impl OhCameraBackend { ctx, fmt_list: vec![], selected_profile: 0, + stream_on: false, + paused: false, #[cfg(any( feature = "trace_to_logger", feature = "trace_to_ftrace", @@ -202,12 +206,15 @@ impl CameraBackend for OhCameraBackend { } fn video_stream_on(&mut self) -> Result<()> { - self.ctx.start_stream(on_buffer_available, on_broken) + self.ctx.start_stream(on_buffer_available, on_broken)?; + self.stream_on = true; + Ok(()) } fn video_stream_off(&mut self) -> Result<()> { self.ctx.stop_stream(); OHCAM_CALLBACK.write().unwrap().clear_buffer(); + self.stream_on = false; #[cfg(any( feature = "trace_to_logger", feature = "trace_to_ftrace", @@ -343,6 +350,30 @@ impl CameraBackend for OhCameraBackend { fn register_broken_cb(&mut self, cb: CameraBrokenCallback) { OHCAM_CALLBACK.write().unwrap().set_broken_cb(cb); } + + fn pause(&mut self, paused: bool) { + if self.paused == paused { + return; + } + + if paused { + // If stream is off, we don't need to set self.paused. + // Because it's not required to re-open stream while + // vm is resuming. + if !self.stream_on { + return; + } + self.paused = true; + self.video_stream_off().unwrap_or_else(|e| { + error!("ohcam pause: failed to pause stream {:?}", e); + }); + } else { + self.paused = false; + self.video_stream_on().unwrap_or_else(|e| { + error!("ohcam resume: failed to resume stream {:?}", e); + }) + } + } } // SAFETY: use RW lock to ensure the security of resources. diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index fccd0a170..a8807318f 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -38,6 +38,7 @@ use crate::usb::{ use machine_manager::config::camera::CameraDevConfig; use machine_manager::config::valid_id; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; +use machine_manager::notifier::{register_vm_pause_notifier, unregister_vm_pause_notifier}; use util::aio::{iov_discard_front_direct, Iovec}; use util::byte_code::ByteCode; use util::gen_base_func; @@ -115,6 +116,7 @@ pub struct UsbCamera { broken: Arc, // if the device broken or not iothread: Option, delete_evts: Vec, + notifier_id: u64, } #[derive(Debug)] @@ -515,6 +517,7 @@ impl UsbCamera { broken: Arc::new(AtomicBool::new(false)), iothread: config.iothread, delete_evts: Vec::new(), + notifier_id: 0, }) } @@ -767,6 +770,13 @@ impl UsbDevice for UsbCamera { self.register_cb(); let camera = Arc::new(Mutex::new(self)); + let cloned_camera = camera.clone(); + let pause_notify = Arc::new(move |paused: bool| { + let locked_cam = cloned_camera.lock().unwrap(); + locked_cam.camera_backend.lock().unwrap().pause(paused); + }); + camera.lock().unwrap().notifier_id = register_vm_pause_notifier(pause_notify); + Ok(camera) } @@ -774,6 +784,8 @@ impl UsbDevice for UsbCamera { info!("Camera {} unrealize", self.device_id()); self.unregister_camera_fd()?; self.camera_backend.lock().unwrap().reset(); + unregister_vm_pause_notifier(self.notifier_id); + self.notifier_id = 0; Ok(()) } -- Gitee From f44a2d8958f5d3a72eaf1e2849aa8796c74acc70 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 2 Jul 2024 11:03:25 +0800 Subject: [PATCH 1922/2187] ohaudio: fixup errors triggerred by audio server died This patch introduces a new enum to indicate ohaudio processor's current status. When the vm is paused/resumed or audio server was died, we should recreate audio context later. When audio server is died, on_error callback should be called. But currently audio server has bug so our on_error callback can't be called. So we have to check the duration since last on_write/read called. If the duration is larger than 1000ms, we can think there's error occurred in audio server. This patch also fixes HAP crash issue made by stratovirt. The reason is that stratovirt takes long time while Windows guest is capturing audio. So let's move pause notifier from scream to its backend ohaudio. This will avoid main loop waiting for long time to get scream interface lock. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 16 +- devices/src/misc/scream/ohaudio.rs | 232 +++++++++++++++++++++-------- 2 files changed, 170 insertions(+), 78 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index cf96e3ac9..0c5be725b 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -40,7 +40,6 @@ use super::ivshmem::Ivshmem; use crate::pci::{PciBus, PciDevOps}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; -use machine_manager::notifier::register_vm_pause_notifier; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohaudio::OhAudio; #[cfg(feature = "scream_pulseaudio")] @@ -434,7 +433,6 @@ pub struct Scream { config: ScreamConfig, token_id: Option>>, interface_resource: Vec>>, - notify_id: u64, } impl Scream { @@ -458,7 +456,6 @@ impl Scream { config, token_id, interface_resource: Vec::new(), - notify_id: 0, }) } @@ -555,17 +552,7 @@ impl Scream { ivshmem.realize()?; self.start_play_thread_fn()?; - self.start_record_thread_fn()?; - - let cloned_interfaces = self.interface_resource.clone(); - let pause_notify = Arc::new(move |paused: bool| { - for interface in cloned_interfaces.iter() { - interface.lock().unwrap().pause(paused); - } - }); - self.notify_id = register_vm_pause_notifier(pause_notify); - - Ok(()) + self.start_record_thread_fn() } } @@ -576,5 +563,4 @@ pub trait AudioInterface: Send { fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) {} fn receive(&mut self, recv_data: &StreamData) -> i32; fn destroy(&mut self); - fn pause(&mut self, _paused: bool) {} } diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 990648ef2..9c960875f 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -13,13 +13,17 @@ use std::os::raw::c_void; use std::sync::{ atomic::{fence, AtomicBool, AtomicI32, Ordering}, - Arc, Mutex, + Arc, Mutex, RwLock, +}; +use std::{ + cmp, ptr, thread, + time::{Duration, Instant}, }; -use std::{cmp, ptr, thread, time}; -use log::error; +use log::{error, warn}; use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, StreamData}; +use machine_manager::notifier::register_vm_pause_notifier; use util::ohos_binding::audio::*; trait OhAudioProcess { @@ -27,7 +31,6 @@ trait OhAudioProcess { fn destroy(&mut self); fn preprocess(&mut self, _start_addr: u64, _sh_header: &ShmemStreamHeader) {} fn process(&mut self, recv_data: &StreamData) -> i32; - fn pause(&mut self, paused: bool); } #[derive(Debug, Clone, Copy)] @@ -41,13 +44,27 @@ const FLUSH_DELAY_THRESHOLD_MS: u64 = 100; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; +#[derive(Copy, Clone, Default, PartialEq, PartialOrd)] +enum OhAudioStatus { + // Processor is ready and waiting for play/capture. + #[default] + READY, + // Processor is started and doing job. + STARTED, + // Processor is paused. + PAUSED, + // OH audio framework error occurred. + ERROR, +} + struct OhAudioRender { ctx: Option, stream_data: Arc>>, data_size: AtomicI32, - start: bool, - pause: bool, flushing: AtomicBool, + status: Arc>, + last_called_time: Option, + pause_notifier_id: u64, } impl Default for OhAudioRender { @@ -56,9 +73,10 @@ impl Default for OhAudioRender { ctx: None, stream_data: Arc::new(Mutex::new(Vec::with_capacity(STREAM_DATA_VEC_CAPACITY))), data_size: AtomicI32::new(0), - start: false, - pause: false, flushing: AtomicBool::new(false), + status: Arc::new(RwLock::new(OhAudioStatus::default())), + last_called_time: None, + pause_notifier_id: 0, } } } @@ -80,14 +98,40 @@ impl OhAudioRender { self.flushing.store(true, Ordering::Release); let mut cnt = 0; while (cnt < FLUSH_DELAY_CNT) && (self.flushing.load(Ordering::Acquire)) { - thread::sleep(time::Duration::from_millis(FLUSH_DELAY_MS)); + thread::sleep(Duration::from_millis(FLUSH_DELAY_MS)); cnt += 1; } // We need to wait for 100ms to ensure the audio data has // been flushed before stop renderer. - thread::sleep(time::Duration::from_millis(FLUSH_DELAY_THRESHOLD_MS)); + thread::sleep(Duration::from_millis(FLUSH_DELAY_THRESHOLD_MS)); let _ = self.ctx.as_ref().unwrap().flush_renderer(); } + + #[inline(always)] + fn get_status(&self) -> OhAudioStatus { + *self.status.read().unwrap() + } + + #[inline(always)] + fn set_status(&self, status: OhAudioStatus) { + *self.status.write().unwrap() = status; + } + + fn register_pause_notifier(&mut self) { + let status = self.status.clone(); + let pause_notify = Arc::new(move |paused: bool| { + let s = *status.read().unwrap(); + if paused { + if s == OhAudioStatus::PAUSED { + return; + } + *status.write().unwrap() = OhAudioStatus::PAUSED; + } else { + *status.write().unwrap() = OhAudioStatus::ERROR; + } + }); + self.pause_notifier_id = register_vm_pause_notifier(pause_notify); + } } impl OhAudioProcess for OhAudioRender { @@ -110,47 +154,43 @@ impl OhAudioProcess for OhAudioRender { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { - self.start = true; + self.set_status(OhAudioStatus::STARTED); trace::oh_scream_render_init(&self.ctx); } Err(e) => { error!("failed to start oh audio renderer: {}", e); } } - self.start - } - - fn pause(&mut self, paused: bool) { - self.pause = paused; - if paused { - self.destroy(); - } + self.last_called_time = None; + self.get_status() == OhAudioStatus::STARTED } fn destroy(&mut self) { - if self.ctx.is_some() { - if self.start { - if !self.pause { - self.flush(); - } - self.ctx.as_mut().unwrap().stop(); - self.start = false; + let status = self.get_status(); + + match status { + OhAudioStatus::PAUSED => return, + OhAudioStatus::ERROR => { + self.ctx = None; + self.set_status(OhAudioStatus::READY); + return; } - self.ctx = None; - } - if self.pause { - return; + OhAudioStatus::STARTED => self.flush(), + _ => {} } - let mut locked_data = self.stream_data.lock().unwrap(); - locked_data.clear(); + self.ctx = None; + self.stream_data.lock().unwrap().clear(); self.data_size.store(0, Ordering::Relaxed); + self.set_status(OhAudioStatus::READY); trace::oh_scream_render_destroy(); } fn process(&mut self, recv_data: &StreamData) -> i32 { - if self.pause { + let mut status = self.get_status(); + if status == OhAudioStatus::PAUSED { return 0; } + self.check_fmt_update(recv_data); fence(Ordering::Acquire); @@ -176,7 +216,13 @@ impl OhAudioProcess for OhAudioRender { .fetch_add(recv_data.audio_size as i32, Ordering::Relaxed); drop(locked_data); - if !self.start && !self.init(recv_data) { + if status == OhAudioStatus::ERROR { + error!("Audio server error occurred. Destroy and reconnect it."); + self.destroy(); + status = self.get_status(); + } + + if status == OhAudioStatus::READY && !self.init(recv_data) { error!("failed to init oh audio"); self.destroy(); } @@ -192,8 +238,9 @@ struct OhAudioCapture { shm_addr: u64, shm_len: u64, cur_pos: u64, - start: bool, - pause: bool, + status: Arc>, + last_called_time: Option, + pause_notifier_id: u64, } impl OhAudioCapture { @@ -208,6 +255,33 @@ impl OhAudioCapture { self.destroy(); } } + + #[inline(always)] + fn get_status(&self) -> OhAudioStatus { + *self.status.write().unwrap() + } + + #[inline(always)] + fn set_status(&self, status: OhAudioStatus) { + *self.status.write().unwrap() = status; + } + + fn register_pause_notifier(&mut self) { + let status = self.status.clone(); + let pause_notify = Arc::new(move |paused: bool| { + let s = *status.read().unwrap(); + if paused { + if s == OhAudioStatus::PAUSED { + return; + } + *status.write().unwrap() = OhAudioStatus::PAUSED; + } else { + // Set error status to recreate capture context. + *status.write().unwrap() = OhAudioStatus::ERROR; + } + }); + self.pause_notifier_id = register_vm_pause_notifier(pause_notify); + } } impl OhAudioProcess for OhAudioCapture { @@ -228,7 +302,8 @@ impl OhAudioProcess for OhAudioCapture { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { - self.start = true; + self.last_called_time = None; + self.set_status(OhAudioStatus::STARTED); trace::oh_scream_capture_init(&self.ctx); true } @@ -239,20 +314,14 @@ impl OhAudioProcess for OhAudioCapture { } } - fn pause(&mut self, paused: bool) { - self.pause = paused; - if paused { - self.destroy(); - } - } - fn destroy(&mut self) { - if self.ctx.is_some() { - if self.start { - self.start = false; - self.ctx.as_mut().unwrap().stop(); + let status = self.get_status(); + match status { + OhAudioStatus::PAUSED => return, + _ => { + self.ctx = None; + self.set_status(OhAudioStatus::READY); } - self.ctx = None; } trace::oh_scream_capture_destroy(); } @@ -266,20 +335,30 @@ impl OhAudioProcess for OhAudioCapture { } fn process(&mut self, recv_data: &StreamData) -> i32 { - if self.pause { + let mut status = self.get_status(); + if status == OhAudioStatus::PAUSED { return -1; } self.check_fmt_update(recv_data); trace::trace_scope_start!(ohaudio_capturer_process, args = (recv_data)); - if !self.start && !self.init(recv_data) { + if status == OhAudioStatus::ERROR { + self.destroy(); + status = self.get_status(); + } + + if status == OhAudioStatus::READY && !self.init(recv_data) { self.destroy(); return -1; } self.new_chunks.store(0, Ordering::Release); while self.new_chunks.load(Ordering::Acquire) == 0 { - thread::sleep(time::Duration::from_millis(10)); + status = self.get_status(); + if status == OhAudioStatus::PAUSED || status == OhAudioStatus::ERROR { + return -1; + } + thread::sleep(Duration::from_millis(10)); } self.new_chunks.load(Ordering::Acquire) @@ -298,6 +377,20 @@ extern "C" fn on_write_data_cb( .as_mut() .unwrap_unchecked() }; + + match &render.last_called_time { + None => render.last_called_time = Some(Instant::now()), + Some(last) => { + let elapsed = last.elapsed().as_millis(); + if elapsed >= 1000 { + warn!("{elapsed}ms elapsed after last on_write called. Will restart render."); + render.set_status(OhAudioStatus::ERROR); + return 0; + } + render.last_called_time = Some(Instant::now()); + } + } + let data_size = render.data_size.load(Ordering::Relaxed); trace::trace_scope_start!(ohaudio_write_cb, args = (length, data_size)); @@ -358,10 +451,23 @@ extern "C" fn on_read_data_cb( .unwrap_unchecked() }; + match &capture.last_called_time { + None => capture.last_called_time = Some(Instant::now()), + Some(last) => { + let elapsed = last.elapsed().as_millis(); + if elapsed >= 1000 { + warn!("{elapsed}ms elapsed after last on_read called. Will restart capture."); + capture.set_status(OhAudioStatus::ERROR); + return 0; + } + capture.last_called_time = Some(Instant::now()); + } + } + trace::trace_scope_start!(ohaudio_read_cb, args = (length)); loop { - if !capture.start { + if capture.get_status() != OhAudioStatus::STARTED { return 0; } if capture.new_chunks.load(Ordering::Acquire) == 0 { @@ -411,12 +517,16 @@ unsafe impl Send for OhAudio {} impl OhAudio { pub fn init(dir: ScreamDirection) -> Self { match dir { - ScreamDirection::Playback => Self { - processor: Box::::default(), - }, - ScreamDirection::Record => Self { - processor: Box::::default(), - }, + ScreamDirection::Playback => { + let mut processor = Box::::default(); + processor.register_pause_notifier(); + Self { processor } + } + ScreamDirection::Record => { + let mut processor = Box::::default(); + processor.register_pause_notifier(); + Self { processor } + } } } } @@ -437,10 +547,6 @@ impl AudioInterface for OhAudio { fn destroy(&mut self) { self.processor.destroy(); } - - fn pause(&mut self, paused: bool) { - self.processor.pause(paused); - } } #[cfg(test)] -- Gitee From c32ec67fc2450ccbf72682bb9193d80124f1943d Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 2 Jul 2024 17:59:52 +0800 Subject: [PATCH 1923/2187] ohaudio: fixup clippy error Use the acronym lowercase to fixup clippy error. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 60 +++++++++++++++--------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 9c960875f..fdc5f481d 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -48,13 +48,13 @@ const FLUSH_DELAY_CNT: u64 = 200; enum OhAudioStatus { // Processor is ready and waiting for play/capture. #[default] - READY, + Ready, // Processor is started and doing job. - STARTED, + Started, // Processor is paused. - PAUSED, + Paused, // OH audio framework error occurred. - ERROR, + Error, } struct OhAudioRender { @@ -122,12 +122,12 @@ impl OhAudioRender { let pause_notify = Arc::new(move |paused: bool| { let s = *status.read().unwrap(); if paused { - if s == OhAudioStatus::PAUSED { + if s == OhAudioStatus::Paused { return; } - *status.write().unwrap() = OhAudioStatus::PAUSED; + *status.write().unwrap() = OhAudioStatus::Paused; } else { - *status.write().unwrap() = OhAudioStatus::ERROR; + *status.write().unwrap() = OhAudioStatus::Error; } }); self.pause_notifier_id = register_vm_pause_notifier(pause_notify); @@ -154,7 +154,7 @@ impl OhAudioProcess for OhAudioRender { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { - self.set_status(OhAudioStatus::STARTED); + self.set_status(OhAudioStatus::Started); trace::oh_scream_render_init(&self.ctx); } Err(e) => { @@ -162,32 +162,32 @@ impl OhAudioProcess for OhAudioRender { } } self.last_called_time = None; - self.get_status() == OhAudioStatus::STARTED + self.get_status() == OhAudioStatus::Started } fn destroy(&mut self) { let status = self.get_status(); match status { - OhAudioStatus::PAUSED => return, - OhAudioStatus::ERROR => { + OhAudioStatus::Paused => return, + OhAudioStatus::Error => { self.ctx = None; - self.set_status(OhAudioStatus::READY); + self.set_status(OhAudioStatus::Ready); return; } - OhAudioStatus::STARTED => self.flush(), + OhAudioStatus::Started => self.flush(), _ => {} } self.ctx = None; self.stream_data.lock().unwrap().clear(); self.data_size.store(0, Ordering::Relaxed); - self.set_status(OhAudioStatus::READY); + self.set_status(OhAudioStatus::Ready); trace::oh_scream_render_destroy(); } fn process(&mut self, recv_data: &StreamData) -> i32 { let mut status = self.get_status(); - if status == OhAudioStatus::PAUSED { + if status == OhAudioStatus::Paused { return 0; } @@ -216,13 +216,13 @@ impl OhAudioProcess for OhAudioRender { .fetch_add(recv_data.audio_size as i32, Ordering::Relaxed); drop(locked_data); - if status == OhAudioStatus::ERROR { + if status == OhAudioStatus::Error { error!("Audio server error occurred. Destroy and reconnect it."); self.destroy(); status = self.get_status(); } - if status == OhAudioStatus::READY && !self.init(recv_data) { + if status == OhAudioStatus::Ready && !self.init(recv_data) { error!("failed to init oh audio"); self.destroy(); } @@ -271,13 +271,13 @@ impl OhAudioCapture { let pause_notify = Arc::new(move |paused: bool| { let s = *status.read().unwrap(); if paused { - if s == OhAudioStatus::PAUSED { + if s == OhAudioStatus::Paused { return; } - *status.write().unwrap() = OhAudioStatus::PAUSED; + *status.write().unwrap() = OhAudioStatus::Paused; } else { // Set error status to recreate capture context. - *status.write().unwrap() = OhAudioStatus::ERROR; + *status.write().unwrap() = OhAudioStatus::Error; } }); self.pause_notifier_id = register_vm_pause_notifier(pause_notify); @@ -303,7 +303,7 @@ impl OhAudioProcess for OhAudioCapture { match self.ctx.as_ref().unwrap().start() { Ok(()) => { self.last_called_time = None; - self.set_status(OhAudioStatus::STARTED); + self.set_status(OhAudioStatus::Started); trace::oh_scream_capture_init(&self.ctx); true } @@ -317,10 +317,10 @@ impl OhAudioProcess for OhAudioCapture { fn destroy(&mut self) { let status = self.get_status(); match status { - OhAudioStatus::PAUSED => return, + OhAudioStatus::Paused => return, _ => { self.ctx = None; - self.set_status(OhAudioStatus::READY); + self.set_status(OhAudioStatus::Ready); } } trace::oh_scream_capture_destroy(); @@ -336,26 +336,26 @@ impl OhAudioProcess for OhAudioCapture { fn process(&mut self, recv_data: &StreamData) -> i32 { let mut status = self.get_status(); - if status == OhAudioStatus::PAUSED { + if status == OhAudioStatus::Paused { return -1; } self.check_fmt_update(recv_data); trace::trace_scope_start!(ohaudio_capturer_process, args = (recv_data)); - if status == OhAudioStatus::ERROR { + if status == OhAudioStatus::Error { self.destroy(); status = self.get_status(); } - if status == OhAudioStatus::READY && !self.init(recv_data) { + if status == OhAudioStatus::Ready && !self.init(recv_data) { self.destroy(); return -1; } self.new_chunks.store(0, Ordering::Release); while self.new_chunks.load(Ordering::Acquire) == 0 { status = self.get_status(); - if status == OhAudioStatus::PAUSED || status == OhAudioStatus::ERROR { + if status == OhAudioStatus::Paused || status == OhAudioStatus::Error { return -1; } thread::sleep(Duration::from_millis(10)); @@ -384,7 +384,7 @@ extern "C" fn on_write_data_cb( let elapsed = last.elapsed().as_millis(); if elapsed >= 1000 { warn!("{elapsed}ms elapsed after last on_write called. Will restart render."); - render.set_status(OhAudioStatus::ERROR); + render.set_status(OhAudioStatus::Error); return 0; } render.last_called_time = Some(Instant::now()); @@ -457,7 +457,7 @@ extern "C" fn on_read_data_cb( let elapsed = last.elapsed().as_millis(); if elapsed >= 1000 { warn!("{elapsed}ms elapsed after last on_read called. Will restart capture."); - capture.set_status(OhAudioStatus::ERROR); + capture.set_status(OhAudioStatus::Error); return 0; } capture.last_called_time = Some(Instant::now()); @@ -467,7 +467,7 @@ extern "C" fn on_read_data_cb( trace::trace_scope_start!(ohaudio_read_cb, args = (length)); loop { - if capture.get_status() != OhAudioStatus::STARTED { + if capture.get_status() != OhAudioStatus::Started { return 0; } if capture.new_chunks.load(Ordering::Acquire) == 0 { -- Gitee From 9f6df27fa30e68ca4e19e1519392b0d364c50707 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 5 Jul 2024 12:00:31 +0800 Subject: [PATCH 1924/2187] ohaudio: optimize render logic Reimplement the render logic to make the code more clean and readable. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 232 +++++++++++++++++++---------- trace/trace_info/misc.toml | 4 +- 2 files changed, 158 insertions(+), 78 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index fdc5f481d..d15303ae1 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -10,13 +10,16 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::VecDeque; use std::os::raw::c_void; use std::sync::{ atomic::{fence, AtomicBool, AtomicI32, Ordering}, Arc, Mutex, RwLock, }; use std::{ - cmp, ptr, thread, + cmp, + io::Read, + ptr, thread, time::{Duration, Instant}, }; @@ -26,6 +29,10 @@ use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, St use machine_manager::notifier::register_vm_pause_notifier; use util::ohos_binding::audio::*; +const STREAM_DATA_VEC_CAPACITY: usize = 15; +const FLUSH_DELAY_MS: u64 = 5; +const FLUSH_DELAY_CNT: u64 = 200; + trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; fn destroy(&mut self); @@ -35,14 +42,121 @@ trait OhAudioProcess { #[derive(Debug, Clone, Copy)] struct StreamUnit { - pub addr: u64, - pub len: u64, + addr: usize, + len: usize, } -const STREAM_DATA_VEC_CAPACITY: usize = 15; -const FLUSH_DELAY_THRESHOLD_MS: u64 = 100; -const FLUSH_DELAY_MS: u64 = 5; -const FLUSH_DELAY_CNT: u64 = 200; +impl Read for StreamUnit { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let len = cmp::min(self.len, buf.len()); + // SAFETY: all the source data are in scream BAR. + unsafe { ptr::copy_nonoverlapping(self.addr as *const u8, buf.as_mut_ptr(), len) }; + self.len -= len; + self.addr += len; + Ok(len) + } +} + +impl StreamUnit { + #[inline] + fn is_empty(&self) -> bool { + self.len == 0 + } + + fn new(addr: usize, len: usize) -> Self { + Self { addr, len } + } + + #[inline] + fn len(&self) -> usize { + self.len + } +} + +struct StreamQueue { + queue: VecDeque, + data_size: usize, +} + +impl Read for StreamQueue { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let len = buf.len(); + let mut ret = 0; + while ret < len { + if self.queue.len() == 0 { + break; + } + let unit = match self.queue.front_mut() { + Some(u) => u, + None => break, + }; + ret += unit.read(&mut buf[ret..len]).unwrap(); + self.data_size -= ret; + if unit.is_empty() { + self.pop_front(); + } + } + Ok(ret) + } + + // If there's no enough data, let's fill the whole buffer with 0. + fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { + let len = buf.len(); + match self.read(buf) { + Ok(ret) => { + if ret < len { + self.read_zero(&mut buf[ret..len]); + } + Ok(()) + } + Err(e) => Err(e), + } + } +} + +impl StreamQueue { + fn new(capacity: usize) -> Self { + Self { + queue: VecDeque::with_capacity(capacity), + data_size: 0, + } + } + + fn clear(&mut self) { + self.queue.clear(); + } + + #[inline] + fn data_size(&self) -> usize { + self.data_size + } + + fn pop_front(&mut self) { + if let Some(elem) = self.queue.pop_front() { + self.data_size -= elem.len(); + } + } + + fn push_back(&mut self, unit: StreamUnit) { + // When audio data is not consumed in time, this buffer + // might be full. So let's keep the max size by dropping + // the old data. This can guarantee sound playing can't + // be delayed too much and the buffer won't become too + // large. + if self.queue.len() == self.queue.capacity() { + self.pop_front(); + } + self.data_size += unit.len; + self.queue.push_back(unit); + } + + fn read_zero(&mut self, buf: &mut [u8]) { + // SAFETY: the buffer is guaranteed by the caller. + unsafe { + ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()); + } + } +} #[derive(Copy, Clone, Default, PartialEq, PartialOrd)] enum OhAudioStatus { @@ -59,8 +173,7 @@ enum OhAudioStatus { struct OhAudioRender { ctx: Option, - stream_data: Arc>>, - data_size: AtomicI32, + stream_data: Arc>, flushing: AtomicBool, status: Arc>, last_called_time: Option, @@ -71,8 +184,7 @@ impl Default for OhAudioRender { fn default() -> OhAudioRender { OhAudioRender { ctx: None, - stream_data: Arc::new(Mutex::new(Vec::with_capacity(STREAM_DATA_VEC_CAPACITY))), - data_size: AtomicI32::new(0), + stream_data: Arc::new(Mutex::new(StreamQueue::new(STREAM_DATA_VEC_CAPACITY))), flushing: AtomicBool::new(false), status: Arc::new(RwLock::new(OhAudioStatus::default())), last_called_time: None, @@ -95,18 +207,31 @@ impl OhAudioRender { } fn flush(&mut self) { - self.flushing.store(true, Ordering::Release); + self.set_flushing(true); let mut cnt = 0; - while (cnt < FLUSH_DELAY_CNT) && (self.flushing.load(Ordering::Acquire)) { + while cnt < FLUSH_DELAY_CNT { thread::sleep(Duration::from_millis(FLUSH_DELAY_MS)); cnt += 1; + if self.stream_data.lock().unwrap().data_size() == 0 { + break; + } } - // We need to wait for 100ms to ensure the audio data has - // been flushed before stop renderer. - thread::sleep(Duration::from_millis(FLUSH_DELAY_THRESHOLD_MS)); + } + + fn flush_renderer(&self) { let _ = self.ctx.as_ref().unwrap().flush_renderer(); } + #[inline(always)] + fn is_flushing(&self) -> bool { + self.flushing.load(Ordering::Acquire) + } + + #[inline(always)] + fn set_flushing(&mut self, flush: bool) { + self.flushing.store(flush, Ordering::Release); + } + #[inline(always)] fn get_status(&self) -> OhAudioStatus { *self.status.read().unwrap() @@ -180,7 +305,7 @@ impl OhAudioProcess for OhAudioRender { } self.ctx = None; self.stream_data.lock().unwrap().clear(); - self.data_size.store(0, Ordering::Relaxed); + self.set_flushing(false); self.set_status(OhAudioStatus::Ready); trace::oh_scream_render_destroy(); } @@ -197,24 +322,10 @@ impl OhAudioProcess for OhAudioRender { trace::trace_scope_start!(ohaudio_render_process, args = (recv_data)); - let su = StreamUnit { - addr: recv_data.audio_base, - len: recv_data.audio_size as u64, - }; - let mut locked_data = self.stream_data.lock().unwrap(); - // When audio data is not consumed in time, we remove old chunk - // and push new one. So audio-playing won't delay. One chunk means - // 20 ms data. - if locked_data.len() >= STREAM_DATA_VEC_CAPACITY { - let remove_size = locked_data[0].len; - locked_data.remove(0); - self.data_size - .fetch_sub(remove_size as i32, Ordering::Relaxed); - } - locked_data.push(su); - self.data_size - .fetch_add(recv_data.audio_size as i32, Ordering::Relaxed); - drop(locked_data); + self.stream_data.lock().unwrap().push_back(StreamUnit::new( + recv_data.audio_base as usize, + recv_data.audio_size as usize, + )); if status == OhAudioStatus::Error { error!("Audio server error occurred. Destroy and reconnect it."); @@ -391,49 +502,18 @@ extern "C" fn on_write_data_cb( } } - let data_size = render.data_size.load(Ordering::Relaxed); - - trace::trace_scope_start!(ohaudio_write_cb, args = (length, data_size)); - - if !render.flushing.load(Ordering::Acquire) && data_size < length { - // SAFETY: we checked len. - unsafe { ptr::write_bytes(buffer as *mut u8, 0, length as usize) }; - return 0; - } - - // Copy stream data from shared memory to buffer. - let mut dst_addr = buffer as u64; - let mut left = length as u64; - let mut su_list = render.stream_data.lock().unwrap(); - while left > 0 && su_list.len() > 0 { - let su = &mut su_list[0]; - let len = cmp::min(left, su.len); + let len = length as usize; + // SAFETY: the buffer is guaranteed by OH audio framework. + let wbuf = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, len) }; - // SAFETY: we checked len. - unsafe { - ptr::copy_nonoverlapping(su.addr as *const u8, dst_addr as *mut u8, len as usize) - }; - trace::oh_scream_on_write_data_cb(len as usize); - - dst_addr += len; - left -= len; - su.len -= len; - if su.len == 0 { - su_list.remove(0); - } else { - su.addr += len; + trace::trace_scope_start!(ohaudio_write_cb, args = (len)); + match render.stream_data.lock().unwrap().read_exact(wbuf) { + Ok(()) => { + if render.is_flushing() { + render.flush_renderer(); + } } - } - render - .data_size - .fetch_sub(length - left as i32, Ordering::Relaxed); - - if left > 0 { - // SAFETY: we checked len. - unsafe { ptr::write_bytes(dst_addr as *mut u8, 0, left as usize) }; - } - if render.flushing.load(Ordering::Acquire) && su_list.is_empty() { - render.flushing.store(false, Ordering::Release); + Err(e) => error!("Failed to read stream data {:?}", e), } 0 } diff --git a/trace/trace_info/misc.toml b/trace/trace_info/misc.toml index 897a6bc03..0a2a2ac66 100644 --- a/trace/trace_info/misc.toml +++ b/trace/trace_info/misc.toml @@ -78,8 +78,8 @@ enabled = true [[scopes]] name = "ohaudio_write_cb" -args = "to_copy: i32, len: i32" -message = "OH audio expect audio data {} bytes, we have {} bytes" +args = "to_copy: i32" +message = "OH audio expect audio data {} bytes" enabled = true [[scopes]] -- Gitee From 10fe2a8d16dc4d794c94f1d9b0d99646d0ed7d46 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 5 Jul 2024 18:50:04 +0800 Subject: [PATCH 1925/2187] ohaudio: fixup the caculation error Now data size is caculated wrong. Let's fixup it. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index d15303ae1..8858dfdae 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -90,8 +90,9 @@ impl Read for StreamQueue { Some(u) => u, None => break, }; - ret += unit.read(&mut buf[ret..len]).unwrap(); - self.data_size -= ret; + let rlen = unit.read(&mut buf[ret..len]).unwrap(); + ret += rlen; + self.data_size -= rlen; if unit.is_empty() { self.pop_front(); } -- Gitee From 307c8c20c5073916dcf583c01c0ff0bf0d50c2b5 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 8 Jul 2024 12:44:47 +0800 Subject: [PATCH 1926/2187] ohaudio: add OH trace back for audio write callback Add OH traces of event and scope back for audio write callback. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 1 + trace/trace_info/misc.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 8858dfdae..6e0539657 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -507,6 +507,7 @@ extern "C" fn on_write_data_cb( // SAFETY: the buffer is guaranteed by OH audio framework. let wbuf = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, len) }; + trace::oh_scream_on_write_data_cb(len); trace::trace_scope_start!(ohaudio_write_cb, args = (len)); match render.stream_data.lock().unwrap().read_exact(wbuf) { Ok(()) => { diff --git a/trace/trace_info/misc.toml b/trace/trace_info/misc.toml index 0a2a2ac66..28cf51326 100644 --- a/trace/trace_info/misc.toml +++ b/trace/trace_info/misc.toml @@ -78,7 +78,7 @@ enabled = true [[scopes]] name = "ohaudio_write_cb" -args = "to_copy: i32" +args = "to_copy: usize" message = "OH audio expect audio data {} bytes" enabled = true -- Gitee From 43eec8289b66c8240d249331a374c97138966e3a Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 8 Jul 2024 12:57:24 +0800 Subject: [PATCH 1927/2187] ohaudio: use is_empty() to make code clearer and more explicit Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 6e0539657..6fcee525c 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -83,7 +83,7 @@ impl Read for StreamQueue { let len = buf.len(); let mut ret = 0; while ret < len { - if self.queue.len() == 0 { + if self.queue.is_empty() { break; } let unit = match self.queue.front_mut() { -- Gitee From 13d052c6f4b1b0ad0253e77fee15c204e22572e1 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 22 Feb 2024 18:19:47 +0300 Subject: [PATCH 1928/2187] xhci: Add structures and methods related to XHCI Streams Continue working on streams support. Add StreamContext related data structures, implement some useful methods. This patch doesn't influence the work of stratovirt since streams are still disabled in HCCPARAMS1. Signed-off-by: Stanislav Goriainov --- devices/src/usb/xhci/xhci_controller.rs | 155 +++++++++++++++++++++++- 1 file changed, 151 insertions(+), 4 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 87e13929d..4bdf8404d 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -64,6 +64,10 @@ const TRB_CR_DC: u32 = 1 << 9; const TRB_CR_SLOTID_SHIFT: u32 = 24; const TRB_CR_SLOTID_MASK: u32 = 0xff; const COMMAND_LIMIT: u32 = 256; +const EP_CTX_MAX_PSTREAMS_SHIFT: u32 = 10; +const EP_CTX_MAX_PSTREAMS_MASK: u32 = 0xf; +const EP_CTX_LSA_SHIFT: u32 = 15; +const EP_CTX_LSA_MASK: u32 = 0x01; const EP_CTX_INTERVAL_SHIFT: u32 = 16; const EP_CTX_INTERVAL_MASK: u32 = 0xff; const EVENT_TRB_CCODE_SHIFT: u32 = 24; @@ -110,6 +114,17 @@ const EP_CONTEXT_EP_TYPE_MASK: u32 = 0x7; const EP_CONTEXT_EP_TYPE_SHIFT: u32 = 3; const ISO_BASE_TIME_INTERVAL: u64 = 125000; const MFINDEX_WRAP_NUM: u64 = 0x4000; +/// Stream Context. +const STREAM_CTX_SCT_SHIFT: u32 = 1; +const STREAM_CTX_SCT_MASK: u32 = 0x7; +const _STREAM_CTX_SCT_SECONDARY_TR: u32 = 0; +const STREAM_CTX_SCT_PRIMARY_TR: u32 = 1; +const _STREAM_CTX_SCT_PRIMARY_SSA_8: u32 = 2; +const _STREAM_CTX_SCT_PRIMARY_SSA_16: u32 = 3; +const _STREAM_CTX_SCT_PRIMARY_SSA_32: u32 = 4; +const _STREAM_CTX_SCT_PRIMARY_SSA_64: u32 = 5; +const _STREAM_CTX_SCT_PRIMARY_SSA_128: u32 = 6; +const _STREAM_CTX_SCT_PRIMARY_SSA_256: u32 = 7; type DmaAddr = u64; @@ -286,6 +301,10 @@ pub struct XhciEpContext { mfindex_last: u64, transfers: LinkedList>>, retry: Option>>, + mem: Arc, + max_pstreams: u32, + lsa: bool, + stream_array: Option>, } impl XhciEpContext { @@ -302,17 +321,33 @@ impl XhciEpContext { mfindex_last: 0, transfers: LinkedList::new(), retry: None, + mem: Arc::clone(mem), + max_pstreams: 0, + lsa: false, + stream_array: None, } } /// Init the endpoint context used the context read from memory. - fn init_ctx(&mut self, output_ctx: DmaAddr, ctx: &XhciEpCtx) { + fn init_ctx(&mut self, output_ctx: DmaAddr, ctx: &XhciEpCtx) -> Result<()> { let dequeue: DmaAddr = addr64_from_u32(ctx.deq_lo & !0xf, ctx.deq_hi); self.ep_type = ((ctx.ep_info2 >> EP_TYPE_SHIFT) & EP_TYPE_MASK).into(); self.output_ctx_addr.store(output_ctx, Ordering::SeqCst); - self.ring.init(dequeue); - self.ring.set_cycle_bit((ctx.deq_lo & 1) == 1); + self.max_pstreams = (ctx.ep_info >> EP_CTX_MAX_PSTREAMS_SHIFT) & EP_CTX_MAX_PSTREAMS_MASK; + self.lsa = ((ctx.ep_info >> EP_CTX_LSA_SHIFT) & EP_CTX_LSA_MASK) != 0; self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); + + if self.max_pstreams == 0 { + self.ring.init(dequeue); + self.ring.set_cycle_bit((ctx.deq_lo & 1) == 1); + } else { + let mut stream_array = + XhciStreamArray::new(&self.mem, &self.output_ctx_addr, self.max_pstreams); + stream_array.init(dequeue)?; + self.stream_array = Some(Arc::new(stream_array)); + } + + Ok(()) } fn get_ep_state(&self) -> u32 { @@ -354,6 +389,41 @@ impl XhciEpContext { } self.transfers = undo; } + + fn _get_stream_context(&self, stream_id: u32) -> Result<&XhciStreamContext> { + if self.stream_array.is_none() { + bail!("Endpoint {} does not support streams.", self.epid); + } + + if !self.lsa { + bail!("Only Linear Streams Array (LSA) is supported.") + } + + // SAFETY: Stream Array was checked to be not None. + let pstreams = &self.stream_array.as_ref().unwrap().0; + let pstreams_num = pstreams.len() as u32; + + if stream_id >= pstreams_num || stream_id == 0 { + bail!( + "Stream ID {} is either invalid or reserved, max number of streams is {}.", + stream_id, + pstreams_num + ); + } + + let stream_context = &pstreams[stream_id as usize]; + + if self.lsa && (stream_context.sct != STREAM_CTX_SCT_PRIMARY_TR) { + bail!( + "Invalid SCT {} on stream {}, LSA is {}", + stream_context.sct, + stream_id, + self.lsa + ); + } + + Ok(stream_context) + } } fn set_ep_state_helper( @@ -679,6 +749,83 @@ pub trait DwordOrder: Default + Copy + Send + Sync { } } +#[repr(transparent)] +pub struct XhciStreamArray(Vec); + +impl XhciStreamArray { + fn new(mem: &Arc, addr: &Arc, max_pstreams: u32) -> Self { + let pstreams_num = 1 << (max_pstreams + 1); + let pstreams = (0..pstreams_num) + .map(|_| XhciStreamContext::new(mem, addr)) + .collect(); + XhciStreamArray(pstreams) + } + + fn init(&mut self, mut dequeue: u64) -> Result<()> { + for stream_context in self.0.iter_mut() { + stream_context.init(dequeue)?; + dequeue += std::mem::size_of::() as u64; + } + + Ok(()) + } +} + +pub struct XhciStreamContext { + /// Memory address space. + mem: Arc, + /// Address of this Stream Context. + dequeue: AtomicU64, + /// Stream Context Type (SCT). + sct: u32, + /// Secondary Stream Array. + _secondary_streams: Option>, + /// Transfer Ring. + ring: Option>, +} + +impl XhciStreamContext { + fn new(mem: &Arc, addr: &Arc) -> Self { + // NOTE: No Secondary Stream Array support yet. + Self { + mem: Arc::clone(mem), + dequeue: AtomicU64::new(0), + sct: STREAM_CTX_SCT_PRIMARY_TR, + _secondary_streams: None, + ring: Some(Arc::new(XhciTransferRing::new(mem, addr))), + } + } + + fn init(&mut self, addr: u64) -> Result<()> { + let mut stream_ctx = XhciStreamCtx::default(); + dma_read_u32(&self.mem, GuestAddress(addr), stream_ctx.as_mut_dwords())?; + let dequeue = addr64_from_u32(stream_ctx.deq_lo & !0xf, stream_ctx.deq_hi); + self.set_dequeue_ptr(addr); + self.sct = (stream_ctx.deq_lo >> STREAM_CTX_SCT_SHIFT) & STREAM_CTX_SCT_MASK; + self.ring.as_ref().unwrap().init(dequeue); + Ok(()) + } + + pub fn get_dequeue_ptr(&self) -> u64 { + self.dequeue.load(Ordering::Acquire) + } + + pub fn set_dequeue_ptr(&self, addr: u64) { + self.dequeue.store(addr, Ordering::Release); + } +} + +#[repr(C, packed)] +#[derive(Debug, Default, Clone, Copy)] +pub struct XhciStreamCtx { + pub deq_lo: u32, + pub deq_hi: u32, + pub stopped_edtla: u32, + pub reserved: u32, +} + +impl DwordOrder for XhciStreamCtx {} + /// Xhci controller device. pub struct XhciDevice { pub numports_2: u8, @@ -1422,7 +1569,7 @@ impl XhciDevice { epctx.epid = ep_id; epctx.enabled = true; // It is safe to use plus here because we previously verify the address on the outer layer. - epctx.init_ctx(output_ctx + EP_CTX_OFFSET + entry_offset, &ep_ctx); + epctx.init_ctx(output_ctx + EP_CTX_OFFSET + entry_offset, &ep_ctx)?; epctx.set_ep_state(EP_RUNNING); ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= EP_RUNNING; -- Gitee From 7dde2e50f00f80ab66e24cfa7bca84422d9cfeda Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 27 Feb 2024 17:32:30 +0300 Subject: [PATCH 1929/2187] xhci: Add stream support for update_dequeue This patch doesn't break Stratovirt runtime as streams are disabled on XHCI Controller level. Alter update_dequeue function to be able to work with streams as well. Signed-off-by: Goriainov Stanislav --- devices/src/usb/xhci/xhci_controller.rs | 124 +++++++++++++++++------- devices/src/usb/xhci/xhci_ring.rs | 8 +- 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 4bdf8404d..3774ed416 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -60,6 +60,8 @@ pub const SLOT_CONFIGURED: u32 = 3; const TRB_CR_BSR: u32 = 1 << 9; const TRB_CR_EPID_SHIFT: u32 = 16; const TRB_CR_EPID_MASK: u32 = 0x1f; +const TRB_CR_STREAMID_SHIFT: u32 = 16; +const TRB_CR_STREAMID_MASK: u32 = 0xffff; const TRB_CR_DC: u32 = 1 << 9; const TRB_CR_SLOTID_SHIFT: u32 = 24; const TRB_CR_SLOTID_MASK: u32 = 0xff; @@ -293,7 +295,7 @@ impl TransferOps for XhciTransfer { pub struct XhciEpContext { epid: u32, enabled: bool, - ring: Arc, + ring: Option>, ep_type: EpType, output_ctx_addr: Arc, state: Arc, @@ -309,13 +311,12 @@ pub struct XhciEpContext { impl XhciEpContext { pub fn new(mem: &Arc) -> Self { - let addr = Arc::new(AtomicU64::new(0)); Self { epid: 0, enabled: false, - ring: Arc::new(XhciTransferRing::new(mem, &addr)), + ring: None, ep_type: EpType::Invalid, - output_ctx_addr: addr, + output_ctx_addr: Arc::new(AtomicU64::new(0)), state: Arc::new(AtomicU32::new(0)), interval: 0, mfindex_last: 0, @@ -338,12 +339,16 @@ impl XhciEpContext { self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); if self.max_pstreams == 0 { - self.ring.init(dequeue); - self.ring.set_cycle_bit((ctx.deq_lo & 1) == 1); + let ring = XhciTransferRing::new(&self.mem, &self.output_ctx_addr); + ring.init(dequeue); + ring.set_cycle_bit((ctx.deq_lo & 1) == 1); + self.ring = Some(Arc::new(ring)); } else { let mut stream_array = XhciStreamArray::new(&self.mem, &self.output_ctx_addr, self.max_pstreams); - stream_array.init(dequeue)?; + stream_array + .init(dequeue) + .with_context(|| "Failed to initialize Stream Array.")?; self.stream_array = Some(Arc::new(stream_array)); } @@ -360,22 +365,46 @@ impl XhciEpContext { /// Update the endpoint state and write the state to memory. fn set_state(&mut self, state: u32) -> Result<()> { - set_ep_state_helper(&self.ring, &self.state, state) + set_ep_state_helper(self.ring.as_ref().unwrap(), &self.state, state) } - /// Update the dequeue pointer in endpoint context. + /// Update the dequeue pointer in endpoint or stream context. /// If dequeue is None, only flush the dequeue pointer to memory. - fn update_dequeue(&mut self, mem: &Arc, dequeue: Option) -> Result<()> { - let mut ep_ctx = XhciEpCtx::default(); - let output_addr = self.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + fn update_dequeue( + &mut self, + mem: &Arc, + dequeue: Option, + stream_id: u32, + ) -> Result<()> { + let ring = self.get_ring(stream_id).with_context(|| { + format!( + "Failed to find Transfer Ring for Endpoint {}, Stream ID {}", + self.epid, stream_id + ) + })?; + if let Some(dequeue) = dequeue { - self.ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); - self.ring - .set_cycle_bit((dequeue & EP_CTX_DCS) == EP_CTX_DCS); + ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); + ring.set_cycle_bit((dequeue & EP_CTX_DCS) == EP_CTX_DCS); } - self.ring.update_dequeue_to_ctx(&mut ep_ctx); - dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + + if self.max_pstreams == 0 { + let mut ep_ctx = XhciEpCtx::default(); + let output_addr = self.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); + dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + } else { + let mut stream_ctx = XhciStreamCtx::default(); + let stream_context = self.get_stream(stream_id).with_context(|| { + format!("Failed to find Stream Context with Stream ID {}", stream_id) + })?; + let output_addr = stream_context.dequeue.load(Ordering::Acquire); + dma_read_u32(mem, GuestAddress(output_addr), stream_ctx.as_mut_dwords())?; + ring.update_dequeue_to_ctx(stream_ctx.as_mut_dwords()); + dma_write_u32(mem, GuestAddress(output_addr), stream_ctx.as_dwords())?; + } + Ok(()) } @@ -390,7 +419,7 @@ impl XhciEpContext { self.transfers = undo; } - fn _get_stream_context(&self, stream_id: u32) -> Result<&XhciStreamContext> { + fn get_stream(&self, stream_id: u32) -> Result<&XhciStreamContext> { if self.stream_array.is_none() { bail!("Endpoint {} does not support streams.", self.epid); } @@ -413,7 +442,7 @@ impl XhciEpContext { let stream_context = &pstreams[stream_id as usize]; - if self.lsa && (stream_context.sct != STREAM_CTX_SCT_PRIMARY_TR) { + if stream_context.sct != STREAM_CTX_SCT_PRIMARY_TR { bail!( "Invalid SCT {} on stream {}, LSA is {}", stream_context.sct, @@ -424,6 +453,18 @@ impl XhciEpContext { Ok(stream_context) } + + fn get_ring(&self, stream_id: u32) -> Result<&Arc> { + if self.max_pstreams == 0 { + Ok(self.ring.as_ref().unwrap()) + } else { + let stream_context = self.get_stream(stream_id).with_context(|| { + format!("Failed to find Stream Context with Stream ID {}", stream_id) + })?; + // SAFETY: Secondary Stream Arrays are not supported. + Ok(stream_context.ring.as_ref().unwrap()) + } + } } fn set_ep_state_helper( @@ -437,7 +478,7 @@ fn set_ep_state_helper( dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= state; - ring.update_dequeue_to_ctx(&mut ep_ctx); + ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; ep_state.store(state, Ordering::SeqCst); Ok(()) @@ -800,9 +841,11 @@ impl XhciStreamContext { let mut stream_ctx = XhciStreamCtx::default(); dma_read_u32(&self.mem, GuestAddress(addr), stream_ctx.as_mut_dwords())?; let dequeue = addr64_from_u32(stream_ctx.deq_lo & !0xf, stream_ctx.deq_hi); + let ring = self.ring.as_ref().unwrap(); self.set_dequeue_ptr(addr); self.sct = (stream_ctx.deq_lo >> STREAM_CTX_SCT_SHIFT) & STREAM_CTX_SCT_MASK; - self.ring.as_ref().unwrap().init(dequeue); + ring.init(dequeue); + ring.set_cycle_bit((dequeue & 1) == 1); Ok(()) } @@ -1177,7 +1220,10 @@ impl XhciDevice { slot_id = self.get_slot_id(&mut event, &trb); if slot_id != 0 { let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; - event.ccode = self.set_tr_dequeue_pointer(slot_id, ep_id, &trb)?; + let stream_id = + trb.control >> TRB_CR_STREAMID_SHIFT & TRB_CR_STREAMID_MASK; + event.ccode = + self.set_tr_dequeue_pointer(slot_id, ep_id, stream_id, &trb)?; } } TRBType::CrResetDevice => { @@ -1679,6 +1725,7 @@ impl XhciDevice { &mut self, slotid: u32, epid: u32, + streamid: u32, trb: &XhciTRB, ) -> Result { trace::usb_xhci_set_tr_dequeue(&slotid, &epid, &trb.parameter); @@ -1703,12 +1750,12 @@ impl XhciDevice { ); return Ok(TRBCCode::ContextStateError); } - epctx.update_dequeue(&self.mem_space, Some(trb.parameter))?; + epctx.update_dequeue(&self.mem_space, Some(trb.parameter), streamid)?; Ok(TRBCCode::Success) } /// Data plane - pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32, _stream_id: u32) -> Result<()> { + pub(crate) fn kick_endpoint(&mut self, slot_id: u32, ep_id: u32, stream_id: u32) -> Result<()> { let epctx = match self.get_endpoint_ctx(slot_id, ep_id) { Ok(epctx) => epctx, Err(e) => { @@ -1725,11 +1772,15 @@ impl XhciDevice { return Ok(()); } - trace::usb_xhci_ep_kick(&slot_id, &ep_id, &epctx.ring.get_dequeue_ptr()); + trace::usb_xhci_ep_kick( + &slot_id, + &ep_id, + &epctx.ring.as_ref().unwrap().get_dequeue_ptr(), + ); if self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] .retry .is_some() - && !self.endpoint_retry_transfer(slot_id, ep_id)? + && !self.endpoint_retry_transfer(slot_id, ep_id, stream_id)? { // Return directly to retry again at the next kick. return Ok(()); @@ -1744,15 +1795,15 @@ impl XhciDevice { let ep_state = epctx.state.clone(); const KICK_LIMIT: u32 = 256; let mut count = 0; - let ring = epctx.ring.clone(); + let ring = epctx.ring.as_ref().unwrap().clone(); loop { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - let td = match epctx.ring.fetch_td()? { + let td = match epctx.ring.as_ref().unwrap().fetch_td()? { Some(td) => { trace::usb_xhci_unimplemented(&format!( "fetch transfer trb {:?} ring dequeue {:?}", td, - epctx.ring.get_dequeue_ptr(), + epctx.ring.as_ref().unwrap().get_dequeue_ptr(), )); td } @@ -1765,7 +1816,7 @@ impl XhciDevice { let mut evt = XhciEvent::new(TRBType::ErTransfer, ccode); evt.slot_id = slot_id as u8; evt.ep_id = ep_id as u8; - evt.ptr = epctx.ring.dequeue.load(Ordering::Acquire); + evt.ptr = epctx.ring.as_ref().unwrap().dequeue.load(Ordering::Acquire); if let Err(e) = self.intrs[0].lock().unwrap().send_event(&evt) { error!("Failed to send event: {:?}", e); } @@ -1798,7 +1849,7 @@ impl XhciDevice { self.endpoint_do_transfer(&mut locked_xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if locked_xfer.complete { - epctx.update_dequeue(&self.mem_space, None)?; + epctx.update_dequeue(&self.mem_space, None, stream_id)?; } else { epctx.transfers.push_back(xfer.clone()); } @@ -1847,7 +1898,12 @@ impl XhciDevice { /// Return Ok(true) if retry is done. /// Return Ok(false) if packet is need to retry again. /// Return Err() if retry failed. - fn endpoint_retry_transfer(&mut self, slot_id: u32, ep_id: u32) -> Result { + fn endpoint_retry_transfer( + &mut self, + slot_id: u32, + ep_id: u32, + stream_id: u32, + ) -> Result { let slot = &mut self.slots[(slot_id - 1) as usize]; // Safe because the retry is checked in the outer function call. let xfer = slot.endpoints[(ep_id - 1) as usize] @@ -1879,7 +1935,7 @@ impl XhciDevice { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if locked_xfer.complete { drop(locked_xfer); - epctx.update_dequeue(&self.mem_space, None)?; + epctx.update_dequeue(&self.mem_space, None, stream_id)?; epctx.flush_transfer(); } epctx.retry = None; diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index f37135721..cde8c9264 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -206,15 +206,15 @@ impl XhciTransferRing { let mut ep_ctx = XhciEpCtx::default(); let output_addr = self.output_ctx_addr.load(Ordering::Acquire); dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; - self.update_dequeue_to_ctx(&mut ep_ctx); + self.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; Ok(()) } - pub fn update_dequeue_to_ctx(&self, ep_ctx: &mut XhciEpCtx) { + pub fn update_dequeue_to_ctx(&self, ctx: &mut [u32]) { let dequeue = self.get_dequeue_ptr(); - ep_ctx.deq_lo = dequeue as u32 | self.get_cycle_bit() as u32; - ep_ctx.deq_hi = (dequeue >> 32) as u32; + ctx[0] = dequeue as u32 | self.get_cycle_bit() as u32; + ctx[1] = (dequeue >> 32) as u32; } } -- Gitee From 84fe594fc95cf3aab3d96b438908791cd9dc1ac0 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 29 Feb 2024 17:52:57 +0300 Subject: [PATCH 1930/2187] xhci: Add reset functionality for streams Implement streams reset for endpoint context. Also make stream context sharable between threads. Signed-off-by: goriainovstanislav --- devices/src/usb/xhci/xhci_controller.rs | 115 +++++++++++++++--------- 1 file changed, 74 insertions(+), 41 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 3774ed416..c1973ca7f 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -17,7 +17,7 @@ use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::{error, info, warn}; @@ -306,7 +306,7 @@ pub struct XhciEpContext { mem: Arc, max_pstreams: u32, lsa: bool, - stream_array: Option>, + stream_array: Option, } impl XhciEpContext { @@ -344,12 +344,12 @@ impl XhciEpContext { ring.set_cycle_bit((ctx.deq_lo & 1) == 1); self.ring = Some(Arc::new(ring)); } else { - let mut stream_array = + let stream_array = XhciStreamArray::new(&self.mem, &self.output_ctx_addr, self.max_pstreams); stream_array .init(dequeue) .with_context(|| "Failed to initialize Stream Array.")?; - self.stream_array = Some(Arc::new(stream_array)); + self.stream_array = Some(stream_array); } Ok(()) @@ -399,7 +399,7 @@ impl XhciEpContext { let stream_context = self.get_stream(stream_id).with_context(|| { format!("Failed to find Stream Context with Stream ID {}", stream_id) })?; - let output_addr = stream_context.dequeue.load(Ordering::Acquire); + let output_addr = stream_context.lock().unwrap().dequeue; dma_read_u32(mem, GuestAddress(output_addr), stream_ctx.as_mut_dwords())?; ring.update_dequeue_to_ctx(stream_ctx.as_mut_dwords()); dma_write_u32(mem, GuestAddress(output_addr), stream_ctx.as_dwords())?; @@ -419,7 +419,7 @@ impl XhciEpContext { self.transfers = undo; } - fn get_stream(&self, stream_id: u32) -> Result<&XhciStreamContext> { + fn get_stream(&self, stream_id: u32) -> Result>> { if self.stream_array.is_none() { bail!("Endpoint {} does not support streams.", self.epid); } @@ -441,30 +441,46 @@ impl XhciEpContext { } let stream_context = &pstreams[stream_id as usize]; + let mut locked_context = stream_context.lock().unwrap(); - if stream_context.sct != STREAM_CTX_SCT_PRIMARY_TR { + if locked_context.needs_update { + locked_context.update()?; + } + + if self.lsa && locked_context.sct != STREAM_CTX_SCT_PRIMARY_TR { bail!( "Invalid SCT {} on stream {}, LSA is {}", - stream_context.sct, + locked_context.sct, stream_id, self.lsa ); } - Ok(stream_context) + Ok(Arc::clone(stream_context)) } - fn get_ring(&self, stream_id: u32) -> Result<&Arc> { + fn get_ring(&self, stream_id: u32) -> Result> { if self.max_pstreams == 0 { - Ok(self.ring.as_ref().unwrap()) + Ok(Arc::clone(self.ring.as_ref().unwrap())) } else { - let stream_context = self.get_stream(stream_id).with_context(|| { + let stream = self.get_stream(stream_id).with_context(|| { format!("Failed to find Stream Context with Stream ID {}", stream_id) })?; - // SAFETY: Secondary Stream Arrays are not supported. - Ok(stream_context.ring.as_ref().unwrap()) + let locked_stream = stream.lock().unwrap(); + Ok(Arc::clone(&locked_stream.ring)) } } + + fn reset_streams(&self) -> Result<()> { + let stream_arr = self.stream_array.as_ref().ok_or_else(|| { + anyhow!( + "Endpoint {} does not support streams, reset aborted.", + self.epid + ) + })?; + stream_arr.reset(); + Ok(()) + } } fn set_ep_state_helper( @@ -791,70 +807,80 @@ pub trait DwordOrder: Default + Copy + Send + Sync { } #[repr(transparent)] -pub struct XhciStreamArray(Vec); +pub struct XhciStreamArray(Vec>>); impl XhciStreamArray { fn new(mem: &Arc, addr: &Arc, max_pstreams: u32) -> Self { let pstreams_num = 1 << (max_pstreams + 1); let pstreams = (0..pstreams_num) - .map(|_| XhciStreamContext::new(mem, addr)) + .map(|_| Arc::new(Mutex::new(XhciStreamContext::new(mem, addr)))) .collect(); XhciStreamArray(pstreams) } - fn init(&mut self, mut dequeue: u64) -> Result<()> { - for stream_context in self.0.iter_mut() { - stream_context.init(dequeue)?; + fn init(&self, mut dequeue: u64) -> Result<()> { + for stream_context in self.0.iter() { + stream_context.lock().unwrap().init(dequeue)?; dequeue += std::mem::size_of::() as u64; } Ok(()) } + + fn reset(&self) { + for stream_context in self.0.iter() { + stream_context.lock().unwrap().reset(); + } + } } pub struct XhciStreamContext { /// Memory address space. mem: Arc, - /// Address of this Stream Context. - dequeue: AtomicU64, + /// Output context address. + dequeue: u64, /// Stream Context Type (SCT). sct: u32, - /// Secondary Stream Array. - _secondary_streams: Option>, /// Transfer Ring. - ring: Option>, + ring: Arc, + /// Whether this Stream Context is initialized. + needs_update: bool, } impl XhciStreamContext { fn new(mem: &Arc, addr: &Arc) -> Self { - // NOTE: No Secondary Stream Array support yet. Self { mem: Arc::clone(mem), - dequeue: AtomicU64::new(0), - sct: STREAM_CTX_SCT_PRIMARY_TR, - _secondary_streams: None, - ring: Some(Arc::new(XhciTransferRing::new(mem, addr))), + dequeue: 0, + sct: 0, + ring: Arc::new(XhciTransferRing::new(mem, addr)), + needs_update: true, } } fn init(&mut self, addr: u64) -> Result<()> { + self.dequeue = addr; + self.update()?; + Ok(()) + } + + fn update(&mut self) -> Result<()> { let mut stream_ctx = XhciStreamCtx::default(); - dma_read_u32(&self.mem, GuestAddress(addr), stream_ctx.as_mut_dwords())?; + dma_read_u32( + &self.mem, + GuestAddress(self.dequeue), + stream_ctx.as_mut_dwords(), + )?; let dequeue = addr64_from_u32(stream_ctx.deq_lo & !0xf, stream_ctx.deq_hi); - let ring = self.ring.as_ref().unwrap(); - self.set_dequeue_ptr(addr); self.sct = (stream_ctx.deq_lo >> STREAM_CTX_SCT_SHIFT) & STREAM_CTX_SCT_MASK; - ring.init(dequeue); - ring.set_cycle_bit((dequeue & 1) == 1); + self.ring.init(dequeue); + self.ring.set_cycle_bit((dequeue & 1) == 1); + self.needs_update = false; Ok(()) } - pub fn get_dequeue_ptr(&self) -> u64 { - self.dequeue.load(Ordering::Acquire) - } - - pub fn set_dequeue_ptr(&self, addr: u64) { - self.dequeue.store(addr, Ordering::Release); + fn reset(&mut self) { + self.needs_update = true; } } @@ -1678,7 +1704,11 @@ impl XhciDevice { slot_id, ep_id )); } - self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize].set_state(EP_STOPPED)?; + let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; + epctx.set_state(EP_STOPPED)?; + if epctx.max_pstreams != 0 { + epctx.reset_streams()?; + } Ok(TRBCCode::Success) } @@ -1718,6 +1748,9 @@ impl XhciDevice { error!("Failed to found port"); return Ok(TRBCCode::UsbTransactionError); } + if epctx.max_pstreams != 0 { + epctx.reset_streams()?; + } Ok(TRBCCode::Success) } -- Gitee From 4abfbd24053677cd09ea0a491d7ebf9ab6dd85f9 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 1 Mar 2024 10:48:54 +0300 Subject: [PATCH 1931/2187] xhci: Refactor existing code Remove output context address from XchiTransferRing. Store the entire endpoint context structure in XhciTransfer. Remove ep_set_state_helper. Signed-off-by: goriainovstanislav --- devices/src/usb/xhci/xhci_controller.rs | 110 +++++++++++------------- devices/src/usb/xhci/xhci_ring.rs | 15 ++-- 2 files changed, 57 insertions(+), 68 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index c1973ca7f..29a45dfee 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -137,28 +137,25 @@ pub struct XhciTransfer { td: Vec, complete: bool, slotid: u32, - streamid: u32, epid: u32, + streamid: u32, + ep_context: XhciEpContext, in_xfer: bool, iso_xfer: bool, timed_xfer: bool, running_retry: bool, running_async: bool, interrupter: Arc>, - ep_ring: Arc, - ep_type: EpType, - ep_state: Arc, mfindex_kick: u64, } impl XhciTransfer { fn new( - ep_info: (u32, u32, EpType), + ep_info: (u32, u32, u32), + ep_context: &XhciEpContext, in_xfer: bool, td: Vec, intr: &Arc>, - ring: &Arc, - ep_state: &Arc, ) -> Self { XhciTransfer { packet: Arc::new(Mutex::new(UsbPacket::default())), @@ -167,16 +164,14 @@ impl XhciTransfer { complete: false, slotid: ep_info.0, epid: ep_info.1, - streamid: 0, + streamid: ep_info.2, + ep_context: ep_context.clone(), in_xfer, iso_xfer: false, timed_xfer: false, running_retry: false, running_async: false, interrupter: intr.clone(), - ep_ring: ring.clone(), - ep_type: ep_info.2, - ep_state: ep_state.clone(), mfindex_kick: 0, } } @@ -190,18 +185,20 @@ impl XhciTransfer { if self.status == TRBCCode::Success { trace::usb_xhci_xfer_success(&self.packet.lock().unwrap().actual_length); self.submit_transfer()?; - self.ep_ring.refresh_dequeue_ptr()?; + let ring = self.ep_context.ring.as_ref().unwrap(); + ring.refresh_dequeue_ptr(self.ep_context.output_ctx_addr.load(Ordering::Acquire))?; return Ok(()); } trace::usb_xhci_xfer_error(&self.packet.lock().unwrap().status); self.report_transfer_error()?; + let ep_type = self.ep_context.ep_type; - if self.ep_type == EpType::IsoIn || self.ep_type == EpType::IsoOut { + if ep_type == EpType::IsoIn || ep_type == EpType::IsoOut { return Ok(()); } // Set the endpoint state to halted if an error occurs in the packet. - set_ep_state_helper(&self.ep_ring, &self.ep_state, EP_HALTED)?; + self.ep_context.set_state(EP_HALTED)?; Ok(()) } @@ -292,6 +289,7 @@ impl TransferOps for XhciTransfer { } /// Endpoint context which use the ring to transfer data. +#[derive(Clone)] pub struct XhciEpContext { epid: u32, enabled: bool, @@ -339,13 +337,12 @@ impl XhciEpContext { self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); if self.max_pstreams == 0 { - let ring = XhciTransferRing::new(&self.mem, &self.output_ctx_addr); + let ring = XhciTransferRing::new(&self.mem); ring.init(dequeue); ring.set_cycle_bit((ctx.deq_lo & 1) == 1); self.ring = Some(Arc::new(ring)); } else { - let stream_array = - XhciStreamArray::new(&self.mem, &self.output_ctx_addr, self.max_pstreams); + let stream_array = XhciStreamArray::new(&self.mem, self.max_pstreams); stream_array .init(dequeue) .with_context(|| "Failed to initialize Stream Array.")?; @@ -360,12 +357,21 @@ impl XhciEpContext { } fn set_ep_state(&self, state: u32) { - self.state.store(state, Ordering::SeqCst); + self.state.store(state, Ordering::Release); } /// Update the endpoint state and write the state to memory. - fn set_state(&mut self, state: u32) -> Result<()> { - set_ep_state_helper(self.ring.as_ref().unwrap(), &self.state, state) + fn set_state(&self, state: u32) -> Result<()> { + let mut ep_ctx = XhciEpCtx::default(); + let output_addr = self.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + ep_ctx.ep_info &= !EP_STATE_MASK; + ep_ctx.ep_info |= state; + let ring = self.ring.as_ref().unwrap(); + ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); + dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + self.set_ep_state(state); + Ok(()) } /// Update the dequeue pointer in endpoint or stream context. @@ -420,16 +426,16 @@ impl XhciEpContext { } fn get_stream(&self, stream_id: u32) -> Result>> { - if self.stream_array.is_none() { - bail!("Endpoint {} does not support streams.", self.epid); - } + let stream_arr = self + .stream_array + .as_ref() + .ok_or_else(|| anyhow!("Endpoint {} does not support streams.", self.epid))?; if !self.lsa { - bail!("Only Linear Streams Array (LSA) is supported.") + bail!("Only Linear Streams Array (LSA) is supported."); } - // SAFETY: Stream Array was checked to be not None. - let pstreams = &self.stream_array.as_ref().unwrap().0; + let pstreams = &stream_arr.0; let pstreams_num = pstreams.len() as u32; if stream_id >= pstreams_num || stream_id == 0 { @@ -443,8 +449,8 @@ impl XhciEpContext { let stream_context = &pstreams[stream_id as usize]; let mut locked_context = stream_context.lock().unwrap(); - if locked_context.needs_update { - locked_context.update()?; + if locked_context.needs_refresh { + locked_context.refresh()?; } if self.lsa && locked_context.sct != STREAM_CTX_SCT_PRIMARY_TR { @@ -483,23 +489,6 @@ impl XhciEpContext { } } -fn set_ep_state_helper( - ring: &Arc, - ep_state: &Arc, - state: u32, -) -> Result<()> { - let mem = &ring.mem; - let mut ep_ctx = XhciEpCtx::default(); - let output_addr = ring.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; - ep_ctx.ep_info &= !EP_STATE_MASK; - ep_ctx.ep_info |= state; - ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); - dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; - ep_state.store(state, Ordering::SeqCst); - Ok(()) -} - /// Endpoint type, including control, bulk, interrupt and isochronous. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum EpType { @@ -807,13 +796,14 @@ pub trait DwordOrder: Default + Copy + Send + Sync { } #[repr(transparent)] +#[derive(Clone)] pub struct XhciStreamArray(Vec>>); impl XhciStreamArray { - fn new(mem: &Arc, addr: &Arc, max_pstreams: u32) -> Self { + fn new(mem: &Arc, max_pstreams: u32) -> Self { let pstreams_num = 1 << (max_pstreams + 1); let pstreams = (0..pstreams_num) - .map(|_| Arc::new(Mutex::new(XhciStreamContext::new(mem, addr)))) + .map(|_| Arc::new(Mutex::new(XhciStreamContext::new(mem)))) .collect(); XhciStreamArray(pstreams) } @@ -834,6 +824,7 @@ impl XhciStreamArray { } } +#[derive(Clone)] pub struct XhciStreamContext { /// Memory address space. mem: Arc, @@ -843,28 +834,28 @@ pub struct XhciStreamContext { sct: u32, /// Transfer Ring. ring: Arc, - /// Whether this Stream Context is initialized. - needs_update: bool, + /// Whether the context is up to date. + needs_refresh: bool, } impl XhciStreamContext { - fn new(mem: &Arc, addr: &Arc) -> Self { + fn new(mem: &Arc) -> Self { Self { mem: Arc::clone(mem), dequeue: 0, sct: 0, - ring: Arc::new(XhciTransferRing::new(mem, addr)), - needs_update: true, + ring: Arc::new(XhciTransferRing::new(mem)), + needs_refresh: true, } } fn init(&mut self, addr: u64) -> Result<()> { self.dequeue = addr; - self.update()?; + self.refresh()?; Ok(()) } - fn update(&mut self) -> Result<()> { + fn refresh(&mut self) -> Result<()> { let mut stream_ctx = XhciStreamCtx::default(); dma_read_u32( &self.mem, @@ -875,12 +866,12 @@ impl XhciStreamContext { self.sct = (stream_ctx.deq_lo >> STREAM_CTX_SCT_SHIFT) & STREAM_CTX_SCT_MASK; self.ring.init(dequeue); self.ring.set_cycle_bit((dequeue & 1) == 1); - self.needs_update = false; + self.needs_refresh = false; Ok(()) } fn reset(&mut self) { - self.needs_update = true; + self.needs_refresh = true; } } @@ -1825,10 +1816,8 @@ impl XhciDevice { return Ok(()); } epctx.set_state(EP_RUNNING)?; - let ep_state = epctx.state.clone(); const KICK_LIMIT: u32 = 256; let mut count = 0; - let ring = epctx.ring.as_ref().unwrap().clone(); loop { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; let td = match epctx.ring.as_ref().unwrap().fetch_td()? { @@ -1861,12 +1850,11 @@ impl XhciDevice { let in_xfer = transfer_in_direction(ep_id as u8, &td, epctx.ep_type); // NOTE: Only support primary interrupter now. let xfer = Arc::new(Mutex::new(XhciTransfer::new( - (slot_id, ep_id, epctx.ep_type), + (slot_id, ep_id, stream_id), + epctx, in_xfer, td, &self.intrs[0], - &ring, - &ep_state, ))); let packet = match self.setup_usb_packet(&xfer) { Ok(pkt) => pkt, diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index cde8c9264..6fd9d85c8 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -116,16 +116,14 @@ pub struct XhciTransferRing { pub dequeue: AtomicU64, /// Consumer Cycle State pub ccs: AtomicBool, - pub output_ctx_addr: Arc, } impl XhciTransferRing { - pub fn new(mem: &Arc, addr: &Arc) -> Self { + pub fn new(mem: &Arc) -> Self { Self { mem: mem.clone(), dequeue: AtomicU64::new(0), ccs: AtomicBool::new(true), - output_ctx_addr: addr.clone(), } } @@ -202,12 +200,15 @@ impl XhciTransferRing { } /// Refresh dequeue pointer to output context. - pub fn refresh_dequeue_ptr(&self) -> Result<()> { + pub fn refresh_dequeue_ptr(&self, output_ctx_addr: u64) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - let output_addr = self.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + dma_read_u32( + &self.mem, + GuestAddress(output_ctx_addr), + ep_ctx.as_mut_dwords(), + )?; self.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); - dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + dma_write_u32(&self.mem, GuestAddress(output_ctx_addr), ep_ctx.as_dwords())?; Ok(()) } -- Gitee From 9db6ea18d114c23aeac8f8e9cff90c96786d15ac Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 1 Mar 2024 13:43:32 +0300 Subject: [PATCH 1932/2187] xhci: Add support for XHCI Streams First version of USB Streams support. No secondary streams for now, so remove SCT checks (only ensure that LSA is enabled). Signed-off-by: goriainovstanislav --- devices/src/usb/uas.rs | 2 +- devices/src/usb/xhci/xhci_controller.rs | 172 +++++++++++++----------- devices/src/usb/xhci/xhci_regs.rs | 4 +- 3 files changed, 101 insertions(+), 77 deletions(-) diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 9ba134cd4..3694d15b8 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -58,7 +58,7 @@ const UAS_PIPE_ID_DATA_IN: u8 = 0x03; const UAS_PIPE_ID_DATA_OUT: u8 = 0x04; // UAS Streams Attributes -const UAS_MAX_STREAMS_BM_ATTR: u8 = 0; +const UAS_MAX_STREAMS_BM_ATTR: u8 = 4; const UAS_MAX_STREAMS: usize = 1 << UAS_MAX_STREAMS_BM_ATTR; // UAS IU IDs diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 29a45dfee..06c714af1 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -117,10 +117,10 @@ const EP_CONTEXT_EP_TYPE_SHIFT: u32 = 3; const ISO_BASE_TIME_INTERVAL: u64 = 125000; const MFINDEX_WRAP_NUM: u64 = 0x4000; /// Stream Context. -const STREAM_CTX_SCT_SHIFT: u32 = 1; -const STREAM_CTX_SCT_MASK: u32 = 0x7; +const _STREAM_CTX_SCT_SHIFT: u32 = 1; +const _STREAM_CTX_SCT_MASK: u32 = 0x7; const _STREAM_CTX_SCT_SECONDARY_TR: u32 = 0; -const STREAM_CTX_SCT_PRIMARY_TR: u32 = 1; +const _STREAM_CTX_SCT_PRIMARY_TR: u32 = 1; const _STREAM_CTX_SCT_PRIMARY_SSA_8: u32 = 2; const _STREAM_CTX_SCT_PRIMARY_SSA_16: u32 = 3; const _STREAM_CTX_SCT_PRIMARY_SSA_32: u32 = 4; @@ -152,7 +152,7 @@ pub struct XhciTransfer { impl XhciTransfer { fn new( ep_info: (u32, u32, u32), - ep_context: &XhciEpContext, + ep_context: XhciEpContext, in_xfer: bool, td: Vec, intr: &Arc>, @@ -165,7 +165,7 @@ impl XhciTransfer { slotid: ep_info.0, epid: ep_info.1, streamid: ep_info.2, - ep_context: ep_context.clone(), + ep_context, in_xfer, iso_xfer: false, timed_xfer: false, @@ -185,7 +185,12 @@ impl XhciTransfer { if self.status == TRBCCode::Success { trace::usb_xhci_xfer_success(&self.packet.lock().unwrap().actual_length); self.submit_transfer()?; - let ring = self.ep_context.ring.as_ref().unwrap(); + let ring = self.ep_context.get_ring(self.streamid).with_context(|| { + format!( + "Failed to find Transfer Ring with Endpoint ID {}, Slot ID {}, Stream ID {}.", + self.epid, self.slotid, self.streamid + ) + })?; ring.refresh_dequeue_ptr(self.ep_context.output_ctx_addr.load(Ordering::Acquire))?; return Ok(()); } @@ -198,7 +203,7 @@ impl XhciTransfer { return Ok(()); } // Set the endpoint state to halted if an error occurs in the packet. - self.ep_context.set_state(EP_HALTED)?; + self.ep_context.set_state(EP_HALTED, Some(self.streamid))?; Ok(()) } @@ -361,56 +366,63 @@ impl XhciEpContext { } /// Update the endpoint state and write the state to memory. - fn set_state(&self, state: u32) -> Result<()> { + fn set_state(&self, state: u32, stream_id: Option) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); let output_addr = self.output_ctx_addr.load(Ordering::Acquire); dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= state; - let ring = self.ring.as_ref().unwrap(); - ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + self.flush_dequeue_to_memory(stream_id)?; self.set_ep_state(state); Ok(()) } /// Update the dequeue pointer in endpoint or stream context. /// If dequeue is None, only flush the dequeue pointer to memory. - fn update_dequeue( - &mut self, - mem: &Arc, - dequeue: Option, - stream_id: u32, - ) -> Result<()> { - let ring = self.get_ring(stream_id).with_context(|| { - format!( - "Failed to find Transfer Ring for Endpoint {}, Stream ID {}", - self.epid, stream_id - ) - })?; - + fn update_dequeue(&self, dequeue: Option, stream_id: u32) -> Result<()> { if let Some(dequeue) = dequeue { + let ring = self.get_ring(stream_id).with_context(|| { + format!( + "Failed to find Transfer Ring for Endpoint {}, Stream ID {}.", + self.epid, stream_id + ) + })?; ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); ring.set_cycle_bit((dequeue & EP_CTX_DCS) == EP_CTX_DCS); } + self.flush_dequeue_to_memory(Some(stream_id))?; + Ok(()) + } + + /// Flush the dequeue pointer to the memory. + /// Stream Endpoints flush ring dequeue to both Endpoint and Stream context. + fn flush_dequeue_to_memory(&self, stream_id: Option) -> Result<()> { + let mut ep_ctx = XhciEpCtx::default(); + let output_addr = self.output_ctx_addr.load(Ordering::Acquire); + dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + if self.max_pstreams == 0 { - let mut ep_ctx = XhciEpCtx::default(); - let output_addr = self.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + let ring = self.get_ring(0)?; ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); - dma_write_u32(mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; - } else { + } else if let Some(stream_id) = stream_id { let mut stream_ctx = XhciStreamCtx::default(); - let stream_context = self.get_stream(stream_id).with_context(|| { - format!("Failed to find Stream Context with Stream ID {}", stream_id) - })?; - let output_addr = stream_context.lock().unwrap().dequeue; - dma_read_u32(mem, GuestAddress(output_addr), stream_ctx.as_mut_dwords())?; + let stream = self.get_stream(stream_id)?; + let locked_stream = stream.lock().unwrap(); + let output_addr = locked_stream.dequeue; + let ring = locked_stream.ring.as_ref(); + dma_read_u32( + &self.mem, + GuestAddress(output_addr), + stream_ctx.as_mut_dwords(), + )?; ring.update_dequeue_to_ctx(stream_ctx.as_mut_dwords()); - dma_write_u32(mem, GuestAddress(output_addr), stream_ctx.as_dwords())?; + ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); + dma_write_u32(&self.mem, GuestAddress(output_addr), stream_ctx.as_dwords())?; } + dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; Ok(()) } @@ -425,6 +437,8 @@ impl XhciEpContext { self.transfers = undo; } + /// Find and return a stream corresponding to the specified Stream ID. + /// Returns error if there is no stream support or LSA is not enabled. fn get_stream(&self, stream_id: u32) -> Result>> { let stream_arr = self .stream_array @@ -435,7 +449,7 @@ impl XhciEpContext { bail!("Only Linear Streams Array (LSA) is supported."); } - let pstreams = &stream_arr.0; + let XhciStreamArray(pstreams) = &stream_arr; let pstreams_num = pstreams.len() as u32; if stream_id >= pstreams_num || stream_id == 0 { @@ -448,35 +462,33 @@ impl XhciEpContext { let stream_context = &pstreams[stream_id as usize]; let mut locked_context = stream_context.lock().unwrap(); - - if locked_context.needs_refresh { - locked_context.refresh()?; - } - - if self.lsa && locked_context.sct != STREAM_CTX_SCT_PRIMARY_TR { - bail!( - "Invalid SCT {} on stream {}, LSA is {}", - locked_context.sct, - stream_id, - self.lsa - ); - } - + locked_context.try_refresh()?; Ok(Arc::clone(stream_context)) } + /// Get a ring corresponding to the specified Stream ID if stream support is enabled, + /// return the standard Transfer Ring otherwise. fn get_ring(&self, stream_id: u32) -> Result> { if self.max_pstreams == 0 { - Ok(Arc::clone(self.ring.as_ref().unwrap())) + Ok(Arc::clone(self.ring.as_ref().ok_or_else(|| { + anyhow!( + "Failed to get the Transfer Ring for Endpoint {} without streams.", + self.epid + ) + })?)) } else { let stream = self.get_stream(stream_id).with_context(|| { - format!("Failed to find Stream Context with Stream ID {}", stream_id) + format!( + "Failed to find Stream Context with Stream ID {}.", + stream_id + ) })?; let locked_stream = stream.lock().unwrap(); Ok(Arc::clone(&locked_stream.ring)) } } + /// Reset all streams on this Endpoint. fn reset_streams(&self) -> Result<()> { let stream_arr = self.stream_array.as_ref().ok_or_else(|| { anyhow!( @@ -828,13 +840,11 @@ impl XhciStreamArray { pub struct XhciStreamContext { /// Memory address space. mem: Arc, - /// Output context address. + /// Dequeue pointer. dequeue: u64, - /// Stream Context Type (SCT). - sct: u32, - /// Transfer Ring. + /// Transfer Ring (no Secondary Streams for now). ring: Arc, - /// Whether the context is up to date. + /// Whether the context is up to date after reset. needs_refresh: bool, } @@ -843,7 +853,6 @@ impl XhciStreamContext { Self { mem: Arc::clone(mem), dequeue: 0, - sct: 0, ring: Arc::new(XhciTransferRing::new(mem)), needs_refresh: true, } @@ -855,6 +864,14 @@ impl XhciStreamContext { Ok(()) } + fn try_refresh(&mut self) -> Result<()> { + if self.needs_refresh { + self.refresh()?; + } + + Ok(()) + } + fn refresh(&mut self) -> Result<()> { let mut stream_ctx = XhciStreamCtx::default(); dma_read_u32( @@ -863,9 +880,7 @@ impl XhciStreamContext { stream_ctx.as_mut_dwords(), )?; let dequeue = addr64_from_u32(stream_ctx.deq_lo & !0xf, stream_ctx.deq_hi); - self.sct = (stream_ctx.deq_lo >> STREAM_CTX_SCT_SHIFT) & STREAM_CTX_SCT_MASK; self.ring.init(dequeue); - self.ring.set_cycle_bit((dequeue & 1) == 1); self.needs_refresh = false; Ok(()) } @@ -1238,7 +1253,7 @@ impl XhciDevice { if slot_id != 0 { let ep_id = trb.control >> TRB_CR_EPID_SHIFT & TRB_CR_EPID_MASK; let stream_id = - trb.control >> TRB_CR_STREAMID_SHIFT & TRB_CR_STREAMID_MASK; + trb.status >> TRB_CR_STREAMID_SHIFT & TRB_CR_STREAMID_MASK; event.ccode = self.set_tr_dequeue_pointer(slot_id, ep_id, stream_id, &trb)?; } @@ -1659,7 +1674,7 @@ impl XhciDevice { self.cancel_all_ep_transfers(slot_id, ep_id, TRBCCode::Invalid)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if self.oper.dcbaap != 0 { - epctx.set_state(EP_DISABLED)?; + epctx.set_state(EP_DISABLED, None)?; } epctx.enabled = false; Ok(TRBCCode::Success) @@ -1696,7 +1711,7 @@ impl XhciDevice { )); } let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - epctx.set_state(EP_STOPPED)?; + epctx.set_state(EP_STOPPED, None)?; if epctx.max_pstreams != 0 { epctx.reset_streams()?; } @@ -1730,7 +1745,7 @@ impl XhciDevice { let epctx = &mut slot.endpoints[(ep_id - 1) as usize]; if let Some(port) = &slot.usb_port { if port.lock().unwrap().dev.is_some() { - epctx.set_state(EP_STOPPED)?; + epctx.set_state(EP_STOPPED, None)?; } else { error!("Failed to found usb device"); return Ok(TRBCCode::UsbTransactionError); @@ -1774,7 +1789,7 @@ impl XhciDevice { ); return Ok(TRBCCode::ContextStateError); } - epctx.update_dequeue(&self.mem_space, Some(trb.parameter), streamid)?; + epctx.update_dequeue(Some(trb.parameter), streamid)?; Ok(TRBCCode::Success) } @@ -1789,6 +1804,13 @@ impl XhciDevice { } }; + let ring = epctx.get_ring(stream_id).with_context(|| { + format!( + "Failed to kick Endpoint {}, no Transfer ring found on Stream ID {}", + ep_id, stream_id + ) + })?; + // If the device has been detached, but the guest has not been notified. // In this case, the Transaction Error is reported when the TRB processed. // Therefore, don't continue here. @@ -1796,11 +1818,7 @@ impl XhciDevice { return Ok(()); } - trace::usb_xhci_ep_kick( - &slot_id, - &ep_id, - &epctx.ring.as_ref().unwrap().get_dequeue_ptr(), - ); + trace::usb_xhci_ep_kick(&slot_id, &ep_id, &ring.get_dequeue_ptr()); if self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize] .retry .is_some() @@ -1815,17 +1833,17 @@ impl XhciDevice { info!("xhci: endpoint halted"); return Ok(()); } - epctx.set_state(EP_RUNNING)?; + epctx.set_state(EP_RUNNING, Some(stream_id))?; const KICK_LIMIT: u32 = 256; let mut count = 0; loop { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - let td = match epctx.ring.as_ref().unwrap().fetch_td()? { + let td = match ring.fetch_td()? { Some(td) => { trace::usb_xhci_unimplemented(&format!( "fetch transfer trb {:?} ring dequeue {:?}", td, - epctx.ring.as_ref().unwrap().get_dequeue_ptr(), + ring.get_dequeue_ptr(), )); td } @@ -1838,7 +1856,7 @@ impl XhciDevice { let mut evt = XhciEvent::new(TRBType::ErTransfer, ccode); evt.slot_id = slot_id as u8; evt.ep_id = ep_id as u8; - evt.ptr = epctx.ring.as_ref().unwrap().dequeue.load(Ordering::Acquire); + evt.ptr = ring.get_dequeue_ptr(); if let Err(e) = self.intrs[0].lock().unwrap().send_event(&evt) { error!("Failed to send event: {:?}", e); } @@ -1848,6 +1866,10 @@ impl XhciDevice { } }; let in_xfer = transfer_in_direction(ep_id as u8, &td, epctx.ep_type); + let mut epctx = epctx.clone(); + // NOTE: It is necessary to clear the transfer list here because otherwise it would + // result in an infinite cycle of destructor calls, leading to a stack overflow. + epctx.transfers.clear(); // NOTE: Only support primary interrupter now. let xfer = Arc::new(Mutex::new(XhciTransfer::new( (slot_id, ep_id, stream_id), @@ -1870,7 +1892,7 @@ impl XhciDevice { self.endpoint_do_transfer(&mut locked_xfer)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if locked_xfer.complete { - epctx.update_dequeue(&self.mem_space, None, stream_id)?; + epctx.update_dequeue(None, stream_id)?; } else { epctx.transfers.push_back(xfer.clone()); } @@ -1956,7 +1978,7 @@ impl XhciDevice { let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; if locked_xfer.complete { drop(locked_xfer); - epctx.update_dequeue(&self.mem_space, None, stream_id)?; + epctx.update_dequeue(None, stream_id)?; epctx.flush_transfer(); } epctx.retry = None; diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 23c0b9a94..d817a6319 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -381,7 +381,9 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { } XHCI_CAP_REG_HCSPARAMS3 => 0x0, XHCI_CAP_REG_HCCPARAMS1 => { - 0x8 << CAP_HCCP_EXCP_SHIFT | (0 << CAP_HCCP_MPSAS_SHIFT) | CAP_HCCP_AC64 + // The offset of the first extended capability is (base) + (0x8 << 2) + // The primary stream array size is 1 << (0x7 + 1) + 0x8 << CAP_HCCP_EXCP_SHIFT | (0x7 << CAP_HCCP_MPSAS_SHIFT) | CAP_HCCP_AC64 } XHCI_CAP_REG_DBOFF => XHCI_OFF_DOORBELL, XHCI_CAP_REG_RTSOFF => XHCI_OFF_RUNTIME, -- Gitee From 9ea42175e2ae2dae2a99ec699197a33c46667458 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 15 Mar 2024 15:34:53 +0300 Subject: [PATCH 1933/2187] xhci: Add trace to XHCI Streams Add trace event for update_dequeue, set_state, get_ring, get_stream and reset_streams. Signed-off-by: goriainovstanislav --- devices/src/usb/xhci/xhci_controller.rs | 5 +++++ trace/trace_info/usb.toml | 30 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 06c714af1..b515130d4 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -375,6 +375,7 @@ impl XhciEpContext { dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; self.flush_dequeue_to_memory(stream_id)?; self.set_ep_state(state); + trace::usb_xhci_set_state(self.epid, state); Ok(()) } @@ -390,6 +391,7 @@ impl XhciEpContext { })?; ring.init(dequeue & EP_CTX_TR_DEQUEUE_POINTER_MASK); ring.set_cycle_bit((dequeue & EP_CTX_DCS) == EP_CTX_DCS); + trace::usb_xhci_update_dequeue(self.epid, dequeue, stream_id); } self.flush_dequeue_to_memory(Some(stream_id))?; @@ -463,6 +465,7 @@ impl XhciEpContext { let stream_context = &pstreams[stream_id as usize]; let mut locked_context = stream_context.lock().unwrap(); locked_context.try_refresh()?; + trace::usb_xhci_get_stream(stream_id, self.epid); Ok(Arc::clone(stream_context)) } @@ -484,6 +487,7 @@ impl XhciEpContext { ) })?; let locked_stream = stream.lock().unwrap(); + trace::usb_xhci_get_ring(self.epid, stream_id); Ok(Arc::clone(&locked_stream.ring)) } } @@ -497,6 +501,7 @@ impl XhciEpContext { ) })?; stream_arr.reset(); + trace::usb_xhci_reset_streams(self.epid); Ok(()) } } diff --git a/trace/trace_info/usb.toml b/trace/trace_info/usb.toml index d94588e61..82991ed67 100644 --- a/trace/trace_info/usb.toml +++ b/trace/trace_info/usb.toml @@ -232,6 +232,36 @@ args = "str: &dyn fmt::Debug" message = "{:?}" enabled = true +[[events]] +name = "usb_xhci_set_state" +args = "ep_id: u32, new_state: u32" +message = "Endpoint {} set new state {}." +enabled = true + +[[events]] +name = "usb_xhci_update_dequeue" +args = "ep_id: u32, dequeue: u64, stream_id: u32" +message = "Endpoint {} update dequeue {} on Stream ID {}." +enabled = true + +[[events]] +name = "usb_xhci_reset_streams" +args = "ep_id: u32" +message = "Resetting streams on Endpoint {}." +enabled = true + +[[events]] +name = "usb_xhci_get_ring" +args = "ep_id: u32, stream_id: u32" +message = "Found Transfer ring on Endpoint {} Stream ID {}." +enabled = true + +[[events]] +name = "usb_xhci_get_stream" +args = "stream_id: u32, ep_id: u32" +message = "Found Stream Context {} for Endpoint {}." +enabled = true + [[events]] name = "usb_handle_control" args = "device: &str, req: &dyn fmt::Debug" -- Gitee From c36857f02436e0752046a777cede4bc43a0e6d1b Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 29 Mar 2024 13:19:52 +0300 Subject: [PATCH 1934/2187] usb: Imporve overall readability Fix typo in "endpoint". Simplify Default for UsbPacketStatus. Make comments more meaningful. Signed-off-by: goriainovstanislav --- devices/src/usb/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index d8e2e3d8f..ac2e3addf 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -51,9 +51,10 @@ const USB_MAX_ADDRESS: u8 = 127; pub const USB_DEVICE_BUFFER_DEFAULT_LEN: usize = 4096; /// USB packet return status. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub enum UsbPacketStatus { Success, + #[default] NoDev, Nak, Stall, @@ -61,12 +62,6 @@ pub enum UsbPacketStatus { IoError, } -impl Default for UsbPacketStatus { - fn default() -> Self { - Self::NoDev - } -} - /// USB request used to transfer to USB device. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] @@ -386,7 +381,7 @@ pub trait UsbDevice: Send + Sync { locked_packet.status = UsbPacketStatus::Success; let ep_nr = locked_packet.ep_number; drop(locked_packet); - debug!("handle packet endpointer number {}", ep_nr); + debug!("handle packet endpoint number {}", ep_nr); if ep_nr == 0 { if let Err(e) = self.do_parameter(packet) { error!("Failed to handle control packet {:?}", e); @@ -496,9 +491,9 @@ pub trait TransferOps: Send + Sync { /// Usb packet used for device transfer data. #[derive(Default)] pub struct UsbPacket { - /// USB packet unique identifier. + /// Unique number for packet tracking. pub packet_id: u32, - /// USB packet id. + /// USB packet id (direction of the transfer). pub pid: u32, pub is_async: bool, pub iovecs: Vec, -- Gitee From a4ac1246acb7f66c92d0040adf5d0678bcb1a42c Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 29 Mar 2024 13:21:44 +0300 Subject: [PATCH 1935/2187] uas: Add support for Super Speed, remove support for High Speed Since there are now streams on Xhci, it makes much more sense to only leave support for Super Speed. Device architecture stays the same. Code becomes even simpler after removing obsolete High Speed support. Signed-off-by: goriainovstanislav --- devices/src/scsi/bus.rs | 2 +- devices/src/usb/uas.rs | 523 +++++++++----------------------------- trace/trace_info/usb.toml | 18 +- 3 files changed, 130 insertions(+), 413 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 0b018c656..8a2226531 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -824,7 +824,7 @@ fn scsi_cdb_lba(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i64 { } } -pub fn scsi_cdb_xfer_mode(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> ScsiXferMode { +fn scsi_cdb_xfer_mode(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> ScsiXferMode { match cdb[0] { WRITE_6 | WRITE_10 diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 3694d15b8..d6d58a2dc 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -12,7 +12,7 @@ use std::array; use std::cmp::min; -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use std::mem::size_of; use std::sync::{Arc, Mutex, Weak}; @@ -36,9 +36,9 @@ use super::{ }; use crate::{ ScsiBus::{ - scsi_cdb_xfer, scsi_cdb_xfer_mode, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, - ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, - SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, SCSI_SENSE_OVERLAPPED_COMMANDS, + scsi_cdb_xfer, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, + SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, }, ScsiDisk::{ScsiDevConfig, ScsiDevice}, }; @@ -66,11 +66,9 @@ const UAS_IU_ID_COMMAND: u8 = 0x01; const UAS_IU_ID_SENSE: u8 = 0x03; const UAS_IU_ID_RESPONSE: u8 = 0x04; const UAS_IU_ID_TASK_MGMT: u8 = 0x05; -const UAS_IU_ID_READ_READY: u8 = 0x06; -const UAS_IU_ID_WRITE_READY: u8 = 0x07; // UAS Response Codes -const _UAS_RC_TMF_COMPLETE: u8 = 0x00; +const UAS_RC_TMF_COMPLETE: u8 = 0x00; const _UAS_RC_INVALID_IU: u8 = 0x02; const UAS_RC_TMF_NOT_SUPPORTED: u8 = 0x04; const _UAS_RC_TMF_FAILED: u8 = 0x05; @@ -79,7 +77,7 @@ const _UAS_RC_INCORRECT_LUN: u8 = 0x09; const _UAS_RC_OVERLAPPED_TAG: u8 = 0x0A; // UAS Task Management Functions -const _UAS_TMF_ABORT_TASK: u8 = 0x01; +const UAS_TMF_ABORT_TASK: u8 = 0x01; const _UAS_TMF_ABORT_TASK_SET: u8 = 0x02; const _UAS_TMF_CLEAR_TASK_SET: u8 = 0x04; const _UAS_TMF_LOGICAL_UNIT_RESET: u8 = 0x08; @@ -110,23 +108,19 @@ pub struct UsbUas { base: UsbDeviceBase, scsi_bus: Arc>, scsi_device: Arc>, - commands_high: VecDeque, - statuses_high: VecDeque>>, - commands_super: [Option; UAS_MAX_STREAMS + 1], - statuses_super: [Option>>; UAS_MAX_STREAMS + 1], + commands: [Option; UAS_MAX_STREAMS + 1], + statuses: [Option>>; UAS_MAX_STREAMS + 1], data: [Option>>; UAS_MAX_STREAMS + 1], - data_ready_sent: bool, } -#[derive(Debug, EnumCount)] +#[derive(Debug, Default, EnumCount)] enum UsbUasStringId { - #[allow(unused)] + #[default] Invalid = 0, Manufacturer = 1, Product = 2, SerialNumber = 3, - ConfigHigh = 4, - ConfigSuper = 5, + Configuration = 4, } const UAS_DESC_STRINGS: [&str; UsbUasStringId::COUNT] = [ @@ -134,7 +128,6 @@ const UAS_DESC_STRINGS: [&str; UsbUasStringId::COUNT] = [ "StratoVirt", "StratoVirt USB Uas", "5", - "High speed config (usb 2.0)", "Super speed config (usb 3.0)", ]; @@ -153,7 +146,7 @@ impl ScsiRequestOps for UasRequest { ) -> Result<()> { let tag = u16::from_be(self.iu.header.tag); let sense = scsi_sense.unwrap_or(SCSI_SENSE_NO_SENSE); - UsbUas::fill_sense(&mut self.status.lock().unwrap(), tag, scsi_status, &sense); + UsbUas::fill_sense(&mut self.status.lock().unwrap(), tag, sense, scsi_status); self.complete(); Ok(()) } @@ -259,7 +252,7 @@ struct UasIU { impl ByteCode for UasIU {} -static DESC_DEVICE_UAS_SUPER: Lazy> = Lazy::new(|| { +static DESC_DEVICE_UAS: Lazy> = Lazy::new(|| { Arc::new(UsbDescDevice { device_desc: UsbDeviceDescriptor { bLength: USB_DT_DEVICE_SIZE, @@ -284,17 +277,17 @@ static DESC_DEVICE_UAS_SUPER: Lazy> = Lazy::new(|| { wTotalLength: 0, bNumInterfaces: 1, bConfigurationValue: 1, - iConfiguration: UsbUasStringId::ConfigSuper as u8, + iConfiguration: UsbUasStringId::Configuration as u8, bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, bMaxPower: 50, }, iad_desc: vec![], - interfaces: vec![DESC_IFACE_EMPTY.clone(), DESC_IFACE_UAS_SUPER.clone()], + interfaces: vec![DESC_IFACE_BOT.clone(), DESC_IFACE_UAS.clone()], })], }) }); -static DESC_IFACE_UAS_SUPER: Lazy> = Lazy::new(|| { +static DESC_IFACE_UAS: Lazy> = Lazy::new(|| { Arc::new(UsbDescIface { interface_desc: UsbInterfaceDescriptor { bLength: USB_DT_INTERFACE_SIZE, @@ -429,136 +422,10 @@ static DESC_IFACE_UAS_SUPER: Lazy> = Lazy::new(|| { }) }); -static DESC_DEVICE_UAS_HIGH: Lazy> = Lazy::new(|| { - Arc::new(UsbDescDevice { - device_desc: UsbDeviceDescriptor { - bLength: USB_DT_DEVICE_SIZE, - bDescriptorType: USB_DT_DEVICE, - bcdUSB: 0x0200, - bDeviceClass: 0, - bDeviceSubClass: 0, - bDeviceProtocol: 0, - bMaxPacketSize0: 64, - idVendor: USB_VENDOR_ID_STRATOVIRT, - idProduct: USB_PRODUCT_ID_UAS, - bcdDevice: 0, - iManufacturer: UsbUasStringId::Manufacturer as u8, - iProduct: UsbUasStringId::Product as u8, - iSerialNumber: UsbUasStringId::SerialNumber as u8, - bNumConfigurations: 1, - }, - configs: vec![Arc::new(UsbDescConfig { - config_desc: UsbConfigDescriptor { - bLength: USB_DT_CONFIG_SIZE, - bDescriptorType: USB_DT_CONFIGURATION, - wTotalLength: 0, - bNumInterfaces: 1, - bConfigurationValue: 1, - iConfiguration: UsbUasStringId::ConfigHigh as u8, - bmAttributes: USB_CONFIGURATION_ATTR_ONE | USB_CONFIGURATION_ATTR_SELF_POWER, - bMaxPower: 50, - }, - iad_desc: vec![], - interfaces: vec![DESC_IFACE_EMPTY.clone(), DESC_IFACE_UAS_HIGH.clone()], - })], - }) -}); - -static DESC_IFACE_UAS_HIGH: Lazy> = Lazy::new(|| { - Arc::new(UsbDescIface { - interface_desc: UsbInterfaceDescriptor { - bLength: USB_DT_INTERFACE_SIZE, - bDescriptorType: USB_DT_INTERFACE, - bInterfaceNumber: 0, - bAlternateSetting: 1, - bNumEndpoints: 4, - bInterfaceClass: USB_CLASS_MASS_STORAGE, - bInterfaceSubClass: USB_SUBCLASS_SCSI, - bInterfaceProtocol: USB_IFACE_PROTOCOL_UAS, - iInterface: 0, - }, - other_desc: vec![], - endpoints: vec![ - Arc::new(UsbDescEndpoint { - endpoint_desc: UsbEndpointDescriptor { - bLength: USB_DT_ENDPOINT_SIZE, - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_COMMAND, - bmAttributes: USB_ENDPOINT_ATTR_BULK, - wMaxPacketSize: 512, - bInterval: 0, - }, - extra: UsbPipeUsageDescriptor { - bLength: USB_DT_PIPE_USAGE_SIZE, - bDescriptorType: USB_DT_PIPE_USAGE, - bPipeId: UAS_PIPE_ID_COMMAND, - bReserved: 0, - } - .as_bytes() - .to_vec(), - }), - Arc::new(UsbDescEndpoint { - endpoint_desc: UsbEndpointDescriptor { - bLength: USB_DT_ENDPOINT_SIZE, - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_STATUS, - bmAttributes: USB_ENDPOINT_ATTR_BULK, - wMaxPacketSize: 512, - bInterval: 0, - }, - extra: UsbPipeUsageDescriptor { - bLength: USB_DT_PIPE_USAGE_SIZE, - bDescriptorType: USB_DT_PIPE_USAGE, - bPipeId: UAS_PIPE_ID_STATUS, - bReserved: 0, - } - .as_bytes() - .to_vec(), - }), - Arc::new(UsbDescEndpoint { - endpoint_desc: UsbEndpointDescriptor { - bLength: USB_DT_ENDPOINT_SIZE, - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | UAS_PIPE_ID_DATA_IN, - bmAttributes: USB_ENDPOINT_ATTR_BULK, - wMaxPacketSize: 512, - bInterval: 0, - }, - extra: UsbPipeUsageDescriptor { - bLength: USB_DT_PIPE_USAGE_SIZE, - bDescriptorType: USB_DT_PIPE_USAGE, - bPipeId: UAS_PIPE_ID_DATA_IN, - bReserved: 0, - } - .as_bytes() - .to_vec(), - }), - Arc::new(UsbDescEndpoint { - endpoint_desc: UsbEndpointDescriptor { - bLength: USB_DT_ENDPOINT_SIZE, - bDescriptorType: USB_DT_ENDPOINT, - bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | UAS_PIPE_ID_DATA_OUT, - bmAttributes: USB_ENDPOINT_ATTR_BULK, - wMaxPacketSize: 512, - bInterval: 0, - }, - extra: UsbPipeUsageDescriptor { - bLength: USB_DT_PIPE_USAGE_SIZE, - bDescriptorType: USB_DT_PIPE_USAGE, - bPipeId: UAS_PIPE_ID_DATA_OUT, - bReserved: 0, - } - .as_bytes() - .to_vec(), - }), - ], - }) -}); - // NOTE: Fake BOT interface descriptor is needed here since Windows UASP driver always expects two // interfaces: both BOT and UASP. It also anticipates the UASP descriptor to be the second one. // Therefore, the first one can be a BOT storage stub. -static DESC_IFACE_EMPTY: Lazy> = Lazy::new(|| { +static DESC_IFACE_BOT: Lazy> = Lazy::new(|| { Arc::new(UsbDescIface { interface_desc: UsbInterfaceDescriptor { bLength: USB_DT_INTERFACE_SIZE, @@ -603,86 +470,24 @@ impl UsbUas { ..Default::default() }; - let mut base = UsbDeviceBase::new( - uas_config.id.clone().unwrap(), - USB_DEVICE_BUFFER_DEFAULT_LEN, - ); - - base.speed = match uas_config.speed.as_deref() { - Some("super") => USB_SPEED_SUPER, - _ => USB_SPEED_HIGH, - }; - Self { - base, + base: UsbDeviceBase::new(uas_config.id.unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN), scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), scsi_device: Arc::new(Mutex::new(ScsiDevice::new( scsi_dev_cfg, drive_cfg, drive_files, ))), - commands_high: VecDeque::new(), - commands_super: array::from_fn(|_| None), - statuses_high: VecDeque::new(), - statuses_super: array::from_fn(|_| None), + commands: array::from_fn(|_| None), + statuses: array::from_fn(|_| None), data: array::from_fn(|_| None), - data_ready_sent: false, } } - fn streams_enabled(&self) -> bool { - self.base.speed == USB_SPEED_SUPER - } - fn cancel_io(&mut self) { - self.commands_high = VecDeque::new(); - self.commands_super = array::from_fn(|_| None); - self.statuses_high = VecDeque::new(); - self.statuses_super = array::from_fn(|_| None); + self.commands = array::from_fn(|_| None); + self.statuses = array::from_fn(|_| None); self.data = array::from_fn(|_| None); - self.data_ready_sent = false; - } - - fn peek_next_status(&self, stream: usize) -> Option<&Arc>> { - match self.streams_enabled() { - true => self.statuses_super[stream].as_ref(), - false => self.statuses_high.front(), - } - } - - fn take_next_status(&mut self, stream: usize) -> Arc> { - match self.streams_enabled() { - true => self.statuses_super[stream].take().unwrap(), - false => self.statuses_high.pop_front().unwrap(), - } - } - - fn queue_status(&mut self, status: &Arc>, stream: usize) { - match self.streams_enabled() { - true => self.statuses_super[stream] = Some(Arc::clone(status)), - false => self.statuses_high.push_back(Arc::clone(status)), - }; - } - - fn peek_next_command(&self, stream: usize) -> Option<&UasIU> { - match self.streams_enabled() { - true => self.commands_super[stream].as_ref(), - false => self.commands_high.front(), - } - } - - fn take_next_command(&mut self, stream: usize) -> UasIU { - match self.streams_enabled() { - true => self.commands_super[stream].take().unwrap(), - false => self.commands_high.pop_front().unwrap(), - } - } - - fn queue_command(&mut self, command: UasIU, stream: usize) { - match self.streams_enabled() { - true => self.commands_super[stream] = Some(command), - false => self.commands_high.push_back(command), - } } fn handle_iu_command( @@ -690,7 +495,7 @@ impl UsbUas { iu: &UasIU, mut uas_request: UasRequest, ) -> Result { - // SAFETY: iu is guaranteed to be of type command + // SAFETY: IU is guaranteed to be of type command. let add_cdb_len = unsafe { iu.body.command.add_cdb_len }; let tag = u16::from_be(iu.header.tag); @@ -698,33 +503,23 @@ impl UsbUas { Self::fill_fake_sense( &mut uas_request.status.lock().unwrap(), tag, - &SCSI_SENSE_INVALID_PARAM_VALUE, + SCSI_SENSE_INVALID_PARAM_VALUE, ); uas_request.complete(); bail!("additional cdb length is not supported"); } - if self.streams_enabled() && tag > UAS_MAX_STREAMS as u16 { + if tag > UAS_MAX_STREAMS as u16 { Self::fill_fake_sense( &mut uas_request.status.lock().unwrap(), tag, - &SCSI_SENSE_INVALID_TAG, + SCSI_SENSE_INVALID_TAG, ); uas_request.complete(); bail!("invalid tag {}", tag); } - if self.streams_enabled() && self.commands_super[tag as usize].is_some() { - Self::fill_fake_sense( - &mut uas_request.status.lock().unwrap(), - tag, - &SCSI_SENSE_OVERLAPPED_COMMANDS, - ); - uas_request.complete(); - bail!("overlapped tag {}", tag); - } - - let (scsi_iovec, scsi_iovec_size) = match uas_request.data.as_ref() { + let (scsi_iovec, scsi_iovec_size) = match &uas_request.data { Some(data) => { let mut locked_data = data.lock().unwrap(); let iov_size = locked_data.get_iovecs_size() as u32; @@ -734,9 +529,9 @@ impl UsbUas { None => (Vec::new(), 0), }; - // SAFETY: iu is guaranteed to of type command + // SAFETY: IU is guaranteed to of type command. let cdb = unsafe { iu.body.command.cdb }; - // SAFETY: iu is guaranteed to of type command + // SAFETY: IU is guaranteed to of type command. let lun = unsafe { iu.body.command.lun } as u16; trace::usb_uas_handle_iu_command(self.device_id(), cdb[0]); let uas_request = Box::new(uas_request); @@ -748,7 +543,7 @@ impl UsbUas { Arc::clone(&self.scsi_device), uas_request, ) - .with_context(|| "Failed to create SCSI request.")?; + .with_context(|| "failed to create SCSI request")?; if scsi_request.cmd.xfer > scsi_request.datalen && scsi_request.cmd.mode != ScsiXferMode::ScsiXferNone @@ -764,7 +559,7 @@ impl UsbUas { EMULATE_SCSI_OPS => scsi_request.emulate_execute(), _ => scsi_request.execute(), } - .with_context(|| "Failed to execute SCSI request.")?; + .with_context(|| "failed to execute SCSI request")?; let upper_request = &mut scsi_request.lock().unwrap().upper_req; let uas_request = upper_request @@ -783,31 +578,34 @@ impl UsbUas { ) -> Result { let tag = u16::from_be(iu.header.tag); - if self.streams_enabled() && tag > UAS_MAX_STREAMS as u16 { + if tag > UAS_MAX_STREAMS as u16 { Self::fill_fake_sense( &mut uas_request.status.lock().unwrap(), tag, - &SCSI_SENSE_INVALID_TAG, + SCSI_SENSE_INVALID_TAG, ); uas_request.complete(); bail!("invalid tag {}", tag); } - if self.streams_enabled() && self.commands_super[tag as usize].is_some() { - Self::fill_fake_sense( - &mut uas_request.status.lock().unwrap(), - tag, - &SCSI_SENSE_OVERLAPPED_COMMANDS, - ); - uas_request.complete(); - bail!("overlapped tag {}", tag); - } - - // SAFETY: iu is guaranteed to be of type task management + // SAFETY: IU is guaranteed to be of type task management. let tmf = unsafe { iu.body.task_management.function }; + trace::usb_uas_handle_iu_task_management(self.device_id(), tmf, tag); - #[allow(clippy::match_single_binding)] match tmf { + UAS_TMF_ABORT_TASK => { + // SAFETY: IU is guaranteed to be of type task management. + let task_tag = unsafe { iu.body.task_management.task_tag } as usize; + self.commands[task_tag] = None; + self.statuses[task_tag] = None; + self.data[task_tag] = None; + trace::usb_uas_tmf_abort_task(self.device_id(), task_tag); + Self::fill_response( + &mut uas_request.status.lock().unwrap(), + tag, + UAS_RC_TMF_COMPLETE, + ); + } _ => { warn!("UAS {} device unsupported TMF {}.", self.device_id(), tmf); Self::fill_response( @@ -829,34 +627,9 @@ impl UsbUas { Self::fill_packet(packet, &mut iu, iu_len); } - fn fill_sense(packet: &mut UsbPacket, tag: u16, status: u8, sense: &ScsiSense) { + fn fill_fake_sense(packet: &mut UsbPacket, tag: u16, sense: ScsiSense) { let mut iu = UasIU::new(UAS_IU_ID_SENSE, tag); - // SAFETY: iu is guaranteed to be of type status - let iu_sense = unsafe { &mut iu.body.sense }; - - iu_sense.status = status; - iu_sense.status_qualifier = 0_u16.to_be(); - iu_sense.sense_length = 0_u16.to_be(); - - if status != GOOD { - iu_sense.sense_length = 18_u16.to_be(); - iu_sense.sense_data[0] = 0x71; // Error code: deferred errors - iu_sense.sense_data[2] = sense.key; - iu_sense.sense_data[7] = 10; // Additional sense length: total length - 8 - iu_sense.sense_data[12] = sense.asc; - iu_sense.sense_data[13] = sense.ascq; - } - - let sense_len = iu_sense.sense_length as usize; - let real_sense_len = size_of::() - iu_sense.sense_data.len() + sense_len; - let iu_len = size_of::() + real_sense_len; - trace::usb_uas_fill_sense(status, iu_len, sense_len); - Self::fill_packet(packet, &mut iu, iu_len); - } - - fn fill_fake_sense(packet: &mut UsbPacket, tag: u16, sense: &ScsiSense) { - let mut iu = UasIU::new(UAS_IU_ID_SENSE, tag); - // SAFETY: iu is guaranteed to be of type status + // SAFETY: IU is guaranteed to be of type status. let iu_sense = unsafe { &mut iu.body.sense }; iu_sense.status = CHECK_CONDITION; @@ -869,19 +642,32 @@ impl UsbUas { iu_sense.sense_data[13] = sense.ascq; let iu_len = size_of::() + size_of::(); - trace::usb_uas_fill_fake_sense(CHECK_CONDITION, iu_len, 18); + trace::usb_uas_fill_fake_sense(CHECK_CONDITION, iu_len, iu_sense.sense_length as usize); Self::fill_packet(packet, &mut iu, iu_len); } - fn fill_read_ready(packet: &mut UsbPacket, tag: u16) { - let mut iu = UasIU::new(UAS_IU_ID_READ_READY, tag); - let iu_len = size_of::(); - Self::fill_packet(packet, &mut iu, iu_len); - } + fn fill_sense(packet: &mut UsbPacket, tag: u16, sense: ScsiSense, status: u8) { + let mut iu = UasIU::new(UAS_IU_ID_SENSE, tag); + // SAFETY: IU is guaranteed to be of type status. + let iu_sense = unsafe { &mut iu.body.sense }; - fn fill_write_ready(packet: &mut UsbPacket, tag: u16) { - let mut iu = UasIU::new(UAS_IU_ID_WRITE_READY, tag); - let iu_len = size_of::(); + iu_sense.status = status; + iu_sense.status_qualifier = 0_u16.to_be(); + iu_sense.sense_length = 0_u16.to_be(); + + if status != GOOD { + iu_sense.sense_length = 18_u16.to_be(); + iu_sense.sense_data[0] = 0x71; // Error code: deferred errors + iu_sense.sense_data[2] = sense.key; + iu_sense.sense_data[7] = 10; // Additional sense length: total length - 8 + iu_sense.sense_data[12] = sense.asc; + iu_sense.sense_data[13] = sense.ascq; + } + + let sense_len = + size_of::() - iu_sense.sense_data.len() + iu_sense.sense_length as usize; + let iu_len = size_of::() + sense_len; + trace::usb_uas_fill_sense(status, iu_len, iu_sense.sense_length as usize); Self::fill_packet(packet, &mut iu, iu_len); } @@ -893,115 +679,52 @@ impl UsbUas { } fn try_start_next_transfer(&mut self, stream: usize) -> UasPacketStatus { - let command = self.peek_next_command(stream); - - if let Some(command) = command { - // SAFETY: iu is guaranteed to be of type command - let cdb = unsafe { &command.body.command.cdb }; - let xfer_len = scsi_cdb_xfer(cdb, Arc::clone(&self.scsi_device)); - trace::usb_uas_try_start_next_transfer(self.device_id(), xfer_len); - - if xfer_len > 0 { - self.try_start_next_data(stream) - } else { - self.try_start_next_non_data(stream) - } - } else { + if self.commands[stream].is_none() { debug!( - "UAS {} device no inflight command when trying to start the next transfer.", - self.device_id() + "UAS {} device no inflight command on stream {}.", + self.device_id(), + stream ); - UasPacketStatus::Pending + return UasPacketStatus::Pending; } - } - - fn try_start_next_data(&mut self, stream: usize) -> UasPacketStatus { - let status = self.peek_next_status(stream); - if status.is_none() { + if self.statuses[stream].is_none() { debug!( - "UAS {} device no inflight status when trying to start the next data transfer.", - self.device_id() + "UAS {} device no inflight status on stream {}.", + self.device_id(), + stream ); return UasPacketStatus::Pending; } - if !self.data_ready_sent { - return self.fill_data_ready(stream); + // SAFETY: Command was checked to be Some. + let command = self.commands[stream].as_ref().unwrap(); + // SAFETY: IU is guaranteed to be of type command. + let cdb = unsafe { &command.body.command.cdb }; + let xfer_len = scsi_cdb_xfer(cdb, Arc::clone(&self.scsi_device)); + trace::usb_uas_try_start_next_transfer(self.device_id(), xfer_len); + + if xfer_len == 0 { + return self.start_next_transfer(stream); } if self.data[stream].is_some() { self.start_next_transfer(stream) } else { debug!( - "UAS {} device no inflight data when trying to start the next data transfer.", - self.device_id() + "UAS {} device no inflight data on stream {}.", + self.device_id(), + stream ); UasPacketStatus::Pending } } - fn fill_data_ready(&mut self, stream: usize) -> UasPacketStatus { - // SAFETY: status must have been checked in try_start_next_data - let status = self.take_next_status(stream); - let mut locked_status = status.lock().unwrap(); - - // SAFETY: command must have been checked in try_start_next_transfer - let iu = self.peek_next_command(stream).unwrap(); - let tag = u16::from_be(iu.header.tag); - - // SAFETY: iu is guaranteed to be of type command - let cdb = unsafe { &iu.body.command.cdb }; - let xfer_mode = scsi_cdb_xfer_mode(cdb); - - match xfer_mode { - ScsiXferMode::ScsiXferFromDev => Self::fill_read_ready(&mut locked_status, tag), - ScsiXferMode::ScsiXferToDev => Self::fill_write_ready(&mut locked_status, tag), - ScsiXferMode::ScsiXferNone => { - warn!( - "UAS {} device cannot fill data ready, operation {} is not a data transfer.", - self.device_id(), - cdb[0] - ); - Self::fill_fake_sense(&mut locked_status, tag, &SCSI_SENSE_INVALID_PARAM_VALUE); - } - } - - let status_async = locked_status.is_async; - drop(locked_status); - - if status_async { - complete_async_packet(&status); - } - - self.data_ready_sent = true; - trace::usb_uas_fill_data_ready(self.device_id(), self.data_ready_sent); - UasPacketStatus::Completed - } - - fn try_start_next_non_data(&mut self, stream: usize) -> UasPacketStatus { - let status = self.peek_next_status(stream); - - if status.is_none() { - debug!( - "UAS {} device no inflight status when trying to start the next non-data transfer.", - self.device_id() - ); - return UasPacketStatus::Pending; - } - - self.start_next_transfer(stream) - } - fn start_next_transfer(&mut self, stream: usize) -> UasPacketStatus { trace::usb_uas_start_next_transfer(self.device_id(), stream); - - // SAFETY: status must have been checked in try_start_next_data or try_start_next_non_data - let status = self.take_next_status(stream); - - // SAFETY: command must have been checked in try_start_next_transfer - let command = self.take_next_command(stream); - + // SAFETY: Status and command must have been checked in try_start_next_transfer. + let status = self.statuses[stream].take().unwrap(); + let command = self.commands[stream].take().unwrap(); let mut uas_request = UasRequest::new(&status, &command); uas_request.data = self.data[stream].take(); @@ -1011,9 +734,6 @@ impl UsbUas { _ => Err(anyhow!("impossible command IU {}", command.header.id)), }; - self.data_ready_sent = false; - self.try_start_next_transfer(stream); - match result { Ok(result) => result, Err(err) => { @@ -1036,21 +756,11 @@ impl UsbDevice for UsbUas { fn realize(mut self) -> Result>> { info!("UAS {} device realize.", self.device_id()); self.base.reset_usb_endpoint(); - let mut desc_strings: Vec = - UAS_DESC_STRINGS.iter().map(|str| str.to_string()).collect(); - let prefix = &desc_strings[UsbUasStringId::SerialNumber as usize]; - desc_strings[UsbUasStringId::SerialNumber as usize] = - self.base.generate_serial_number(prefix); - - match self.base.speed { - USB_SPEED_HIGH => self - .base - .init_descriptor(DESC_DEVICE_UAS_HIGH.clone(), desc_strings)?, - USB_SPEED_SUPER => self - .base - .init_descriptor(DESC_DEVICE_UAS_SUPER.clone(), desc_strings)?, - _ => bail!("USB UAS unsupported device speed {}.", self.base.speed), - } + self.base.speed = USB_SPEED_SUPER; + let mut s: Vec = UAS_DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let prefix = &s[UsbUasStringId::SerialNumber as usize]; + s[UsbUasStringId::SerialNumber as usize] = self.base.generate_serial_number(prefix); + self.base.init_descriptor(DESC_DEVICE_UAS.clone(), s)?; // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. @@ -1126,7 +836,7 @@ impl UsbDevice for UsbUas { trace::usb_uas_handle_data(self.device_id(), ep_number, stream); drop(locked_packet); - if self.streams_enabled() && (stream > UAS_MAX_STREAMS || stream == 0) { + if stream > UAS_MAX_STREAMS || ep_number != UAS_PIPE_ID_COMMAND && stream == 0 { warn!("UAS {} device invalid stream {}.", self.device_id(), stream); packet.lock().unwrap().status = UsbPacketStatus::Stall; return; @@ -1134,7 +844,7 @@ impl UsbDevice for UsbUas { // NOTE: The architecture of this device is rather simple: it first waits for all of the // required USB packets to arrive, and only then creates and sends an actual UAS request. - // The number of USB packets differs from 2 to 4 and depends on whether the command involves + // The number of USB packets differs from 2 to 3 and depends on whether the command involves // data transfers or not. Since the packets arrive in arbitrary order, some of them may be // queued asynchronously. Note that the command packet is always completed right away. For // all the other types of packets, their asynchronous status is determined by the return @@ -1142,7 +852,14 @@ impl UsbDevice for UsbUas { // completed in scsi_request_complete_cb() callback. match ep_number { UAS_PIPE_ID_COMMAND => { - if self.streams_enabled() && self.commands_super[stream].is_some() { + let mut locked_packet = packet.lock().unwrap(); + let mut iu = UasIU::default(); + let iov_size = locked_packet.get_iovecs_size() as usize; + let iu_len = min(iov_size, size_of::()); + locked_packet.transfer_packet(iu.as_mut_bytes(), iu_len); + let stream = u16::from_be(iu.header.tag) as usize; + + if self.commands[stream].is_some() { warn!( "UAS {} device multiple command packets on stream {}.", self.device_id(), @@ -1152,19 +869,13 @@ impl UsbDevice for UsbUas { return; } - let mut locked_packet = packet.lock().unwrap(); - let mut iu = UasIU::default(); - let iov_size = locked_packet.get_iovecs_size() as usize; - let iu_len = min(iov_size, size_of::()); - locked_packet.transfer_packet(iu.as_mut_bytes(), iu_len); - trace::usb_uas_command_received(packet_id, self.device_id()); - self.queue_command(iu, stream); + self.commands[stream] = Some(iu); self.try_start_next_transfer(stream); trace::usb_uas_command_completed(packet_id, self.device_id()); } UAS_PIPE_ID_STATUS => { - if self.streams_enabled() && self.statuses_super[stream].is_some() { + if self.statuses[stream].is_some() { warn!( "UAS {} device multiple status packets on stream {}.", self.device_id(), @@ -1175,7 +886,7 @@ impl UsbDevice for UsbUas { } trace::usb_uas_status_received(packet_id, self.device_id()); - self.queue_status(packet, stream); + self.statuses[stream] = Some(Arc::clone(packet)); let result = self.try_start_next_transfer(stream); match result { @@ -1223,7 +934,7 @@ impl UsbDevice for UsbUas { } } - fn set_controller(&mut self, _controller: std::sync::Weak>) {} + fn set_controller(&mut self, _cntlr: std::sync::Weak>) {} fn get_controller(&self) -> Option>> { None @@ -1250,9 +961,9 @@ impl UasRequest { // NOTE: Due to the specifics of this device, it waits for all of the required USB packets // to arrive before starting an actual transfer. Therefore, some packets may arrive earlier - // than others, and they won't be completed right away (except for command packets) but + // than others, and they won't be completed right away (except for the command packets), but // rather queued asynchronously. A certain packet may also be async if it was the last to - // arrive and UasRequest didn't complete right away. + // arrive, but UasRequest didn't complete right away. if status_async { complete_async_packet(status); } diff --git a/trace/trace_info/usb.toml b/trace/trace_info/usb.toml index 82991ed67..cabcf6ca4 100644 --- a/trace/trace_info/usb.toml +++ b/trace/trace_info/usb.toml @@ -532,12 +532,6 @@ args = "device_id: &str, xfer_len: i32" message = "UAS {} device is trying to start next transfer of length {}." enabled = true -[[events]] -name = "usb_uas_fill_data_ready" -args = "device_id: &str, data_ready_sent: bool" -message = "UAS {} device set data_ready_sent to {}." -enabled = true - [[events]] name = "usb_uas_start_next_transfer" args = "device_id: &str, stream: usize" @@ -597,3 +591,15 @@ name = "usb_uas_data_queued_async" args = "packet_id: u32, device_id: &str" message = "USB {} data packet queued async on UAS {} device." enabled = true + +[[events]] +name = "usb_uas_handle_iu_task_management" +args = "device_id: &str, tmf: u8, tag: u16" +message = "UAS {} device handling TMF {} with tag {}." +enabled = true + +[[events]] +name = "usb_uas_tmf_abort_task" +args = "device_id: &str, task_tag: usize" +message = "UAS {} device aborting task with tag {}." +enabled = true -- Gitee From 6773e7312390a587fed865a31e2209d8debca815 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 4 Apr 2024 15:08:47 +0300 Subject: [PATCH 1936/2187] usb: Remove obsolete code responsible for qualifier descriptors Qualifier decriptor is constructed implicitly at the moment of get_device_qualifier_descriptor call. Therefore, there is no need to store it explicitly anymore. Signed-off-by: goriainovstanislav --- devices/src/usb/descriptor.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index ca4c51e77..3864464dc 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -165,11 +165,6 @@ pub struct UsbDescDevice { pub configs: Vec>, } -/// USB device qualifier descriptor. -pub struct UsbDescDeviceQualifier { - pub qualifier_desc: UsbDeviceQualifierDescriptor, -} - /// USB config descriptor. pub struct UsbDescConfig { pub config_desc: UsbConfigDescriptor, @@ -221,7 +216,6 @@ pub struct UsbDescEndpoint { /// USB Descriptor. pub struct UsbDescriptor { pub device_desc: Option>, - pub device_qualifier_desc: Option>, pub configuration_selected: Option>, pub interfaces: Vec>>, pub altsetting: Vec, @@ -233,7 +227,6 @@ impl UsbDescriptor { pub fn new() -> Self { Self { device_desc: None, - device_qualifier_desc: None, configuration_selected: None, interfaces: vec![None; USB_MAX_INTERFACES as usize], altsetting: vec![0; USB_MAX_INTERFACES as usize], -- Gitee From c4d367a84572348a5c5d83e8661b885f8fd3d354 Mon Sep 17 00:00:00 2001 From: Dmitry Skorodumov Date: Mon, 8 Jul 2024 21:27:01 +0300 Subject: [PATCH 1937/2187] This is a temporary fix for PL011 send workflow The commit 1bf12724299717962661f947656aa8b5865583f4 introduces mechanic that works fine with virtio-serial. Unfortunately, it doesn't work with pl011 and breaks windbg. To use this mechanic with pl011, much more actions are required: we need properly return Send-FIFO_FULL/FIFO_EMPTY. We need properly signal INT_TX interrupt etc. The work for proper fix is in progress. Implement simple workaround so far: just send all the data synchronously if listener_fd is None. This will allow work with non-corrected PL011, which always reports that send-FIFO is empty and alway can accept data from guest to send out. Signed-off-by: Dmitry Skorodumov --- chardev_backend/src/chardev.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index bd2b37dbd..8b065f992 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -273,11 +273,14 @@ impl Chardev { } return write_buffer_sync(self.output.as_ref().unwrap().clone(), buf); } - ChardevType::Socket { .. } => (), - } - - if self.output.is_none() { - return Ok(()); + ChardevType::Socket { .. } => { + if self.output.is_none() { + return Ok(()); + } + if listener_fd.is_none() { + return write_buffer_sync(self.output.as_ref().unwrap().clone(), buf); + } + } } if self.outbuf_is_full() { -- Gitee From bd9ede4c0b27435c232c70f7a6da9b36bff230b6 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Mon, 8 Jul 2024 19:50:19 +0800 Subject: [PATCH 1938/2187] Machine: the Mutex lock of vm is changed to RwLock When lifecycle operations are performed on a VM, a deadlock may occur on the VM. Therefore, the VM lock is changed to the read/write lock. Signed-off-by: Mingwang Li --- cpu/src/lib.rs | 16 +++++----- .../src/interrupt_controller/aarch64/gicv3.rs | 2 +- .../src/interrupt_controller/aarch64/mod.rs | 6 ++-- hypervisor/src/kvm/mod.rs | 14 ++++----- machine/src/aarch64/micro.rs | 6 ++-- machine/src/aarch64/standard.rs | 10 +++---- machine/src/lib.rs | 30 +++++++++---------- machine/src/micro_common/mod.rs | 2 +- machine/src/standard_common/mod.rs | 18 +++++------ machine/src/x86_64/micro.rs | 6 ++-- machine/src/x86_64/standard.rs | 22 +++++++------- machine_manager/src/event_loop.rs | 4 +-- machine_manager/src/machine.rs | 2 +- machine_manager/src/qmp/qmp_socket.rs | 21 ++++++------- machine_manager/src/test_server.rs | 18 +++++------ migration/src/general.rs | 2 +- migration/src/manager.rs | 4 +-- migration/src/migration.rs | 4 +-- src/main.rs | 12 ++++---- util/src/loop_context.rs | 12 ++++---- 20 files changed, 107 insertions(+), 104 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 7a1162951..3c3f3f0d0 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -63,7 +63,7 @@ pub use x86_64::X86RegsIndex as RegsIndex; use std::cell::RefCell; use std::sync::atomic::{fence, AtomicBool, Ordering}; -use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; +use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; use std::thread; use anyhow::{anyhow, Context, Result}; @@ -216,7 +216,7 @@ pub struct CPU { /// The thread tid of this VCPU. tid: Arc>>, /// The VM combined by this VCPU. - vm: Weak>, + vm: Weak>, /// The state backup of architecture CPU right before boot. boot_state: Arc>, /// Sync the pause state of vCPU in hypervisor and userspace. @@ -238,7 +238,7 @@ impl CPU { hypervisor_cpu: Arc, id: u8, arch_cpu: Arc>, - vm: Arc>, + vm: Arc>, ) -> Self { CPU { id, @@ -294,7 +294,7 @@ impl CPU { &self.hypervisor_cpu } - pub fn vm(&self) -> Weak> { + pub fn vm(&self) -> Weak> { self.vm.clone() } @@ -404,15 +404,15 @@ impl CPUInterface for CPU { fn guest_shutdown(&self) -> Result<()> { if let Some(vm) = self.vm.upgrade() { - let shutdown_act = vm.lock().unwrap().get_shutdown_action(); + let shutdown_act = vm.read().unwrap().get_shutdown_action(); match shutdown_act { ShutdownActionPoweroff => { let (cpu_state, _) = &*self.state; *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; - vm.lock().unwrap().destroy(); + vm.read().unwrap().destroy(); } ShutdownActionPause => { - vm.lock().unwrap().pause(); + vm.read().unwrap().pause(); } } } else { @@ -434,7 +434,7 @@ impl CPUInterface for CPU { if let Some(vm) = self.vm.upgrade() { let (cpu_state, _) = &*self.state; *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; - vm.lock().unwrap().reset(); + vm.read().unwrap().reset(); } else { return Err(anyhow!(CpuError::NoMachineInterface)); } diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 60babf03d..26f41e224 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -338,7 +338,7 @@ impl GICDevice for GICv3 { Ok(()) } - fn reset(&self) -> Result<()> { + fn reset_state(&self) -> Result<()> { info!("Reset gicv3its"); self.reset_its_state()?; info!("Reset gicv3"); diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index ab548604c..27371aba9 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -73,7 +73,7 @@ pub trait GICDevice: MachineLifecycle { fn realize(&self) -> Result<()>; /// Reset 'GIC' - fn reset(&self) -> Result<()> { + fn reset_state(&self) -> Result<()> { Ok(()) } @@ -117,7 +117,9 @@ impl InterruptController { /// Reset the InterruptController pub fn reset(&self) -> Result<()> { - self.gic.reset().with_context(|| "Failed to reset GIC") + self.gic + .reset_state() + .with_context(|| "Failed to reset GIC") } /// Change `InterruptController` lifecycle state to `Stopped`. diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 6689e516f..98dcfca46 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -419,23 +419,23 @@ impl KvmCpu { match run { #[cfg(target_arch = "x86_64")] VcpuExit::IoIn(addr, data) => { - vm.lock().unwrap().pio_in(u64::from(addr), data); + vm.read().unwrap().pio_in(u64::from(addr), data); } #[cfg(target_arch = "x86_64")] VcpuExit::IoOut(addr, data) => { #[cfg(feature = "boot_time")] capture_boot_signal(addr as u64, data); - vm.lock().unwrap().pio_out(u64::from(addr), data); + vm.read().unwrap().pio_out(u64::from(addr), data); } VcpuExit::MmioRead(addr, data) => { - vm.lock().unwrap().mmio_read(addr, data); + vm.read().unwrap().mmio_read(addr, data); } VcpuExit::MmioWrite(addr, data) => { #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] capture_boot_signal(addr, data); - vm.lock().unwrap().mmio_write(addr, data); + vm.read().unwrap().mmio_write(addr, data); } #[cfg(target_arch = "x86_64")] VcpuExit::Hlt => { @@ -919,7 +919,7 @@ impl MsiIrqManager for KVMInterruptManager { #[cfg(test)] mod test { - use std::sync::{Arc, Mutex}; + use std::sync::{Arc, Mutex, RwLock}; use std::time::Duration; #[cfg(target_arch = "x86_64")] @@ -1002,7 +1002,7 @@ mod test { return; } - let vm = Arc::new(Mutex::new(TestVm::new())); + let vm = Arc::new(RwLock::new(TestVm::new())); let code_seg = kvm_segment { base: 0, @@ -1118,7 +1118,7 @@ mod test { vcpu_fd, )); - let vm = Arc::new(Mutex::new(TestVm::new())); + let vm = Arc::new(RwLock::new(TestVm::new())); let cpu = CPU::new( hypervisor_cpu.clone(), 0, diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 51cc2de42..a3b761bfa 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use anyhow::{bail, Context, Result}; @@ -132,8 +132,8 @@ impl MachineOps for LightMachine { Ok(()) } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { - let mut locked_vm = vm.lock().unwrap(); + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + let mut locked_vm = vm.write().unwrap(); trace::sysbus(&locked_vm.base.sysbus.lock().unwrap()); trace::vm_state(&locked_vm.base.vm_state); diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index a5a99aef6..5eefe28d1 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -15,7 +15,7 @@ pub use crate::error::MachineError; use std::mem::size_of; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use std::sync::RwLock; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use anyhow::{anyhow, bail, Context, Result}; #[cfg(feature = "ramfb")] @@ -178,8 +178,8 @@ impl StdMachine { }) } - pub fn handle_reset_request(vm: &Arc>) -> Result<()> { - let mut locked_vm = vm.lock().unwrap(); + pub fn handle_reset_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.write().unwrap(); let mut fdt_addr: u64 = 0; for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { @@ -520,9 +520,9 @@ impl MachineOps for StdMachine { } } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; - let mut locked_vm = vm.lock().unwrap(); + let mut locked_vm = vm.write().unwrap(); locked_vm.init_global_config(vm_config)?; register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) .with_context(|| "Failed to register shutdown event")?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 19d4ba6c0..8e6c668e6 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -449,7 +449,7 @@ pub trait MachineOps: MachineLifecycle { /// * `max_cpus` - max cpu number of virtual machine. fn create_vcpu( vcpu_id: u8, - vm: Arc>, + vm: Arc>, hypervisor: Arc>, #[cfg(target_arch = "x86_64")] max_cpus: u8, ) -> Result> @@ -483,7 +483,7 @@ pub trait MachineOps: MachineLifecycle { /// * `max_cpus` - The max number of vcpus. /// * `boot_cfg` - Boot message generated by reading boot source to guest memory. fn init_vcpu( - vm: Arc>, + vm: Arc>, hypervisor: Arc>, nr_cpus: u8, #[cfg(target_arch = "x86_64")] max_cpus: u8, @@ -2052,7 +2052,7 @@ pub trait MachineOps: MachineLifecycle { /// /// * `vm` - The machine structure. /// * `vm_config` - VM configuration. - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> where Self: Sized; @@ -2239,7 +2239,7 @@ pub trait MachineOps: MachineLifecycle { fn register_shutdown_event( shutdown_req: Arc, - vm: Arc>, + vm: Arc>, ) -> Result<()> { let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { @@ -2261,8 +2261,8 @@ fn register_shutdown_event( .with_context(|| "Failed to register event notifier.") } -fn handle_destroy_request(vm: &Arc>) -> bool { - let locked_vm = vm.lock().unwrap(); +fn handle_destroy_request(vm: &Arc>) -> bool { + let locked_vm = vm.read().unwrap(); let vmstate: VmState = { let state = locked_vm.machine_base().vm_state.deref().0.lock().unwrap(); *state @@ -2285,12 +2285,12 @@ fn handle_destroy_request(vm: &Arc>) -> bool { /// * `vm` - virtual machine that implement `MachineOps`. /// * `cmd_args` - Command arguments from user. pub fn vm_run( - vm: &Arc>, + vm: &Arc>, cmd_args: &arg_parser::ArgMatches, ) -> Result<()> { - let migrate = vm.lock().unwrap().get_migrate_info(); + let migrate = vm.read().unwrap().get_migrate_info(); if migrate.0 == MigrateMode::Unknown { - vm.lock() + vm.read() .unwrap() .run(cmd_args.is_present("freeze_cpu")) .with_context(|| "Failed to start VM.")?; @@ -2302,13 +2302,13 @@ pub fn vm_run( } /// Start incoming migration from destination. -fn start_incoming_migration(vm: &Arc>) -> Result<()> { - let (mode, path) = vm.lock().unwrap().get_migrate_info(); +fn start_incoming_migration(vm: &Arc>) -> Result<()> { + let (mode, path) = vm.read().unwrap().get_migrate_info(); match mode { MigrateMode::File => { MigrationManager::restore_snapshot(&path) .with_context(|| "Failed to restore snapshot")?; - vm.lock() + vm.read() .unwrap() .run(false) .with_context(|| "Failed to start VM.")?; @@ -2321,7 +2321,7 @@ fn start_incoming_migration(vm: &Arc>) -> Re MigrationManager::recv_migration(&mut sock) .with_context(|| "Failed to receive migration with unix mode")?; - vm.lock() + vm.read() .unwrap() .run(false) .with_context(|| "Failed to start VM.")?; @@ -2334,7 +2334,7 @@ fn start_incoming_migration(vm: &Arc>) -> Re MigrationManager::recv_migration(&mut sock) .with_context(|| "Failed to receive migration with tcp mode")?; - vm.lock() + vm.read() .unwrap() .run(false) .with_context(|| "Failed to start VM.")?; @@ -2347,7 +2347,7 @@ fn start_incoming_migration(vm: &Arc>) -> Re } // End the migration and reset the mode. - let locked_vm = vm.lock().unwrap(); + let locked_vm = vm.read().unwrap(); let vm_config = locked_vm.get_vm_config(); if let Some((mode, _)) = vm_config.lock().unwrap().incoming.as_mut() { *mode = MigrateMode::Unknown; diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 076af9c55..9e5551bee 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -544,7 +544,7 @@ impl MachineLifecycle for LightMachine { true } - fn reset(&mut self) -> bool { + fn reset(&self) -> bool { // For micro vm, the reboot command is equivalent to the shutdown command. for cpu in self.base.cpus.iter() { let (cpu_state, _) = cpu.state(); diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 60944fb99..5563b34ec 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -20,7 +20,7 @@ use std::os::unix::io::RawFd; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::string::String; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use std::u64; use anyhow::{bail, Context, Result}; @@ -240,7 +240,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { /// /// * `clone_vm` - Reference of the StdMachine. #[cfg(target_arch = "x86_64")] - fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()>; + fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()>; /// Register event notifier for hotplug vcpu event. /// @@ -252,7 +252,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_hotplug_vcpu_event( &self, hotplug_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let hotplug_req_fd = hotplug_req.as_raw_fd(); let hotplug_req_handler: Rc = Rc::new(move |_, _| { @@ -298,7 +298,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_reset_event( &self, reset_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let reset_req_fd = reset_req.as_raw_fd(); let reset_req_handler: Rc = Rc::new(move |_, _| { @@ -323,12 +323,12 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_pause_event( &self, pause_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let pause_req_fd = pause_req.as_raw_fd(); let pause_req_handler: Rc = Rc::new(move |_, _| { let _ret = pause_req.read(); - if !clone_vm.lock().unwrap().pause() { + if !clone_vm.read().unwrap().pause() { error!("VM pause failed"); } None @@ -348,12 +348,12 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_resume_event( &self, resume_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let resume_req_fd = resume_req.as_raw_fd(); let resume_req_handler: Rc = Rc::new(move |_, _| { let _ret = resume_req.read(); - if !clone_vm.lock().unwrap().resume() { + if !clone_vm.read().unwrap().resume() { error!("VM resume failed!"); } None @@ -986,7 +986,7 @@ impl MachineLifecycle for StdMachine { .shutdown_action } - fn reset(&mut self) -> bool { + fn reset(&self) -> bool { if self.reset_req.write(1).is_err() { error!("Standard vm write reset request failed"); return false; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index dad7e7b76..0c0afc762 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use anyhow::{bail, Context, Result}; @@ -138,8 +138,8 @@ impl MachineOps for LightMachine { .with_context(|| "Failed to realize serial device.") } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { - let mut locked_vm = vm.lock().unwrap(); + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + let mut locked_vm = vm.write().unwrap(); trace::sysbus(&locked_vm.base.sysbus); trace::vm_state(&locked_vm.base.vm_state); diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 3a1169114..c9c80f79c 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -12,7 +12,7 @@ use std::io::{Seek, SeekFrom}; use std::mem::size_of; -use std::sync::{Arc, Barrier, Mutex}; +use std::sync::{Arc, Barrier, Mutex, RwLock}; use anyhow::{bail, Context, Result}; @@ -148,8 +148,8 @@ impl StdMachine { }) } - pub fn handle_reset_request(vm: &Arc>) -> Result<()> { - let mut locked_vm = vm.lock().unwrap(); + pub fn handle_reset_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.write().unwrap(); for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { cpu.pause() @@ -178,7 +178,7 @@ impl StdMachine { Ok(()) } - fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { + fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); let ich = ich9_lpc::LPCBridge::new( root_bus, @@ -197,8 +197,8 @@ impl StdMachine { None } - pub fn handle_hotplug_vcpu_request(vm: &Arc>) -> Result<()> { - let mut locked_vm = vm.lock().unwrap(); + pub fn handle_hotplug_vcpu_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.write().unwrap(); locked_vm.add_vcpu_device(vm.clone()) } @@ -206,7 +206,7 @@ impl StdMachine { &mut self, boot_config: CPUBootConfig, cpu_topology: CPUTopology, - vm: Arc>, + vm: Arc>, ) -> Result<()> { let region_base: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].1; @@ -296,7 +296,7 @@ impl StdMachineOps for StdMachine { self.cpu_controller.as_ref().unwrap() } - fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()> { + fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()> { let mut locked_controller = self.cpu_controller.as_ref().unwrap().lock().unwrap(); let device_id; let vcpu_id; @@ -310,7 +310,7 @@ impl StdMachineOps for StdMachine { let boot_cfg = locked_controller.get_boot_config(); let topology = locked_controller.get_topology_config(); - let hypervisor = clone_vm.lock().unwrap().base.hypervisor.clone(); + let hypervisor = clone_vm.read().unwrap().base.hypervisor.clone(); let vcpu = ::create_vcpu( vcpu_id, clone_vm, @@ -474,10 +474,10 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let max_cpus = vm_config.machine_config.max_cpus; - let mut locked_vm = vm.lock().unwrap(); + let mut locked_vm = vm.write().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index 79d7cf6f9..e197a39ee 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::os::unix::prelude::RawFd; -use std::sync::{Arc, Barrier, Mutex}; +use std::sync::{Arc, Barrier, RwLock}; use std::{process, thread}; use anyhow::{bail, Result}; @@ -126,7 +126,7 @@ impl EventLoop { /// # Arguments /// /// * `manager` - The main part to manager the event loop. - pub fn set_manager(manager: Arc>) { + pub fn set_manager(manager: Arc>) { // SAFETY: All concurrently accessed data of EventLoopContext is protected. unsafe { if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index a040d6efa..e36638448 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -116,7 +116,7 @@ pub trait MachineLifecycle { } /// Reset VM, stop running and restart a new VM. - fn reset(&mut self) -> bool { + fn reset(&self) -> bool { self.notify_lifecycle(VmState::Running, VmState::Shutdown) } diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 35ad238f4..62a9d62ea 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -100,7 +100,7 @@ pub struct Socket { /// Socket stream with RwLock stream: RwLock>, /// Perform socket command - performer: Option>>, + performer: Option>>, } impl Socket { @@ -112,7 +112,7 @@ impl Socket { /// * `performer` - The `VM` to perform socket command. pub fn from_listener( listener: SocketListener, - performer: Option>>, + performer: Option>>, ) -> Self { Socket { listener, @@ -275,12 +275,13 @@ impl EventNotifierHelper for Socket { } /// Macro: to execute handle func with every arguments. +/// Attentions: Lifecycle commands cannot hold a write lock on the executor to avoid deadlock. macro_rules! qmp_command_match { ( $func:tt, $executor:expr, $ret:expr ) => { - $ret = $executor.$func().into(); + $ret = $executor.read().unwrap().$func().into(); }; ( $func:tt, $executor:expr, $cmd:expr, $ret:expr, $($arg:tt),* ) => { - $ret = $executor.$func( + $ret = $executor.write().unwrap().$func( $($cmd.$arg),* ).into(); }; @@ -289,7 +290,7 @@ macro_rules! qmp_command_match { /// Macro: to execute handle func with all arguments. macro_rules! qmp_command_match_with_argument { ( $func:tt, $executor:expr, $cmd:expr, $ret:expr ) => { - $ret = $executor.$func($cmd).into(); + $ret = $executor.write().unwrap().$func($cmd).into(); }; } @@ -365,7 +366,7 @@ fn parse_tcp_uri(uri: &str) -> Result<(String, u16)> { /// This function will fail when json parser failed or socket file description broke. fn handle_qmp( stream_fd: RawFd, - controller: &Arc>, + controller: &Arc>, leak_bucket: &mut LeakBucket, ) -> Result<()> { let mut qmp_service = crate::socket::SocketHandler::new(stream_fd); @@ -422,7 +423,7 @@ fn handle_qmp( /// function, and exec this qmp command. fn qmp_command_exec( qmp_command: QmpCommand, - controller: &Arc>, + controller: &Arc>, if_fd: Option, ) -> (String, bool) { let mut qmp_response = Response::create_empty_response(); @@ -430,7 +431,7 @@ fn qmp_command_exec( // Use macro create match to cover most Qmp command let mut id = create_command_matches!( - qmp_command.clone(); controller.lock().unwrap(); qmp_response; + qmp_command.clone(); controller; qmp_response; (stop, pause), (cont, resume), (system_powerdown, powerdown), @@ -493,12 +494,12 @@ fn qmp_command_exec( if id.is_none() { id = match qmp_command { QmpCommand::quit { id, .. } => { - controller.lock().unwrap().destroy(); + controller.read().unwrap().destroy(); shutdown_flag = true; id } QmpCommand::getfd { arguments, id } => { - qmp_response = controller.lock().unwrap().getfd(arguments.fd_name, if_fd); + qmp_response = controller.read().unwrap().getfd(arguments.fd_name, if_fd); id } QmpCommand::trace_get_state { arguments, id } => { diff --git a/machine_manager/src/test_server.rs b/machine_manager/src/test_server.rs index f126ede07..f8e2078c0 100644 --- a/machine_manager/src/test_server.rs +++ b/machine_manager/src/test_server.rs @@ -14,7 +14,7 @@ use std::os::unix::io::RawFd; use std::os::unix::net::UnixStream; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use hex::FromHexError; use vmm_sys_util::epoll::EventSet; @@ -27,11 +27,11 @@ use util::test_helper::{eoi_intx, get_test_clock, has_msix_msg, query_intx, set_ pub struct TestSock { stream: UnixStream, - controller: Arc>, + controller: Arc>, } impl TestSock { - pub fn new(path: &str, controller: Arc>) -> Self { + pub fn new(path: &str, controller: Arc>) -> Self { let stream = match UnixStream::connect(path) { Ok(s) => s, Err(e) => { @@ -115,7 +115,7 @@ fn update_clock(target: u64) { } } -fn handle_test_cmd(stream_fd: RawFd, controller: &Arc>) { +fn handle_test_cmd(stream_fd: RawFd, controller: &Arc>) { let mut handler = SocketHandler::new(stream_fd); let msg = handler.get_line().unwrap().unwrap(); @@ -129,7 +129,7 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc { @@ -193,7 +193,7 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc { @@ -207,7 +207,7 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc { diff --git a/migration/src/general.rs b/migration/src/general.rs index 0777e0de1..0f9f56dc2 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -260,7 +260,7 @@ pub trait Lifecycle { /// Pause VM during migration. fn pause() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.lock().unwrap().pause(); + locked_vm.read().unwrap().pause(); } Ok(()) diff --git a/migration/src/manager.rs b/migration/src/manager.rs index d381ae312..302350ff5 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -163,7 +163,7 @@ pub struct Vmm { /// Vm config pub config: Arc>, /// Trait to represent a Vm. - pub vm: Option>>, + pub vm: Option>>, /// Trait to represent CPU devices. pub cpus: HashMap>, /// Trait to represent memory devices. @@ -245,7 +245,7 @@ impl MigrationManager { /// # Arguments /// /// * `vm` - vm instance with MachineLifecycle trait. - pub fn register_vm_instance(vm: Arc>) + pub fn register_vm_instance(vm: Arc>) where T: MachineLifecycle + Sync + Send + 'static, { diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 2782d9cd8..2edfa3562 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -578,7 +578,7 @@ impl MigrationManager { /// Clear live migration environment and shut down VM. fn clear_migration() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.lock().unwrap().destroy(); + locked_vm.read().unwrap().destroy(); } Ok(()) @@ -587,7 +587,7 @@ impl MigrationManager { /// Recover the virtual machine if migration is failed. pub fn recover_from_migration() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.lock().unwrap().resume(); + locked_vm.read().unwrap().resume(); } Ok(()) diff --git a/src/main.rs b/src/main.rs index cff73cbe2..2d85d8f1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use std::io::Write; use std::process::{ExitCode, Termination}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use anyhow::{bail, Context, Result}; use log::{error, info}; @@ -154,12 +154,12 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let listeners = check_api_channel(cmd_args, vm_config)?; let mut sockets = Vec::new(); - let vm: Arc> = match vm_config.machine_config.mach_type { + let vm: Arc> = match vm_config.machine_config.mach_type { MachineType::MicroVm => { if is_test_enabled() { panic!("module test framework does not support microvm.") } - let vm = Arc::new(Mutex::new( + let vm = Arc::new(RwLock::new( LightMachine::new(vm_config).with_context(|| "Failed to init MicroVM")?, )); MachineOps::realize(&vm, vm_config).with_context(|| "Failed to realize micro VM.")?; @@ -171,7 +171,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res vm } MachineType::StandardVm => { - let vm = Arc::new(Mutex::new( + let vm = Arc::new(RwLock::new( StdMachine::new(vm_config).with_context(|| "Failed to init StandardVM")?, )); MachineOps::realize(&vm, vm_config) @@ -199,7 +199,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res if is_test_enabled() { panic!("please specify machine type.") } - let vm = Arc::new(Mutex::new( + let vm = Arc::new(RwLock::new( StdMachine::new(vm_config).with_context(|| "Failed to init NoneVM")?, )); EventLoop::set_manager(vm.clone()); @@ -213,7 +213,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); if !cmd_args.is_present("disable-seccomp") { - vm.lock() + vm.read() .unwrap() .register_seccomp(balloon_switch_on) .with_context(|| "Failed to register seccomp rules.")?; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 957fdb4cb..a511b1fcd 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -202,7 +202,7 @@ pub struct EventLoopContext { /// Epoll file descriptor. epoll: Epoll, /// Control epoll loop running. - manager: Option>>, + manager: Option>>, /// Used to wakeup epoll to re-evaluate events or timers. kick_event: EventFd, /// Used to avoid unnecessary kick operation when the @@ -290,7 +290,7 @@ impl EventLoopContext { } } - pub fn set_manager(&mut self, manager: Arc>) { + pub fn set_manager(&mut self, manager: Arc>) { self.manager = Some(manager); } @@ -547,8 +547,8 @@ impl EventLoopContext { /// Executes `epoll.wait()` to wait for events, and call the responding callbacks. pub fn run(&mut self) -> Result { if let Some(manager) = &self.manager { - if manager.lock().unwrap().loop_should_exit() { - manager.lock().unwrap().loop_cleanup()?; + if manager.read().unwrap().loop_should_exit() { + manager.read().unwrap().loop_cleanup()?; return Ok(false); } } @@ -558,8 +558,8 @@ impl EventLoopContext { pub fn iothread_run(&mut self) -> Result { if let Some(manager) = &self.manager { - if manager.lock().unwrap().loop_should_exit() { - manager.lock().unwrap().loop_cleanup()?; + if manager.read().unwrap().loop_should_exit() { + manager.read().unwrap().loop_cleanup()?; return Ok(false); } } -- Gitee From f0de508cdf9f1dcd223b25199a684ce52ece7ea7 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 16 May 2024 16:00:58 +0300 Subject: [PATCH 1939/2187] docs: Add USB Uas device entry to config_guidebook.md Signed-off-by: goriainovstanislav --- docs/config_guidebook.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a836f7041..a018e970a 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -931,6 +931,22 @@ Note: Please see the [4. Build with features](docs/build_guide.md) if you want to enable usb-host. +#### 2.13.7 USB Uas +USB Mass Storage Device that is based on the USB Attached Scsi (UAS) protocol. It should be attached to USB controller. + +Three properties can be set for USB Uas. + +* id: unique device id. +* file: the path of backend image file. +* media: the media type of storage. Possible values are `disk` or `cdrom`. If not set, default is `disk`. + +```shell +-device usb-uas,drive=,id= +-drive id=,file=[,media={disk|cdrom}],aio=off,direct=false +``` + +Note: "aio=off,direct=false" must be configured and other aio/direct values are not supported. + ### 2.14 Virtio Scsi Controller Virtio Scsi controller is a pci device which can be attached scsi device. -- Gitee From a87aa9e48ec6c2a9ecbaf016341e5139f004f87f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 11 Jul 2024 11:13:15 +0800 Subject: [PATCH 1940/2187] machine: add missing `OhUiServer` use Add missing `OhUiServer` use. Fix: f30677c8bf6174(machine: unified `StdMachine`) Signed-off-by: liuxiangdong --- machine/src/standard_common/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 5563b34ec..6818dce32 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -77,6 +77,8 @@ use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_ #[cfg(feature = "gtk")] use ui::gtk::qmp_query_display_image; use ui::input::{input_button, input_move_abs, input_point_sync, key_event, Axis}; +#[cfg(all(target_env = "ohos", feature = "ohui_srv"))] +use ui::ohui_srv::OhUiServer; #[cfg(feature = "vnc")] use ui::vnc::qmp_query_vnc; use util::aio::{AioEngine, WriteZeroesState}; -- Gitee From a2a9f99f9a27991f56f227b3e556e895d8647ba4 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Fri, 17 May 2024 10:40:34 +0300 Subject: [PATCH 1941/2187] usb: Remove get_wakeup_endpoint from UsbDevice trait Wakeup endpoint is only required for USB keyboard and tablet devices and can be passed to notify_controller directly, so no get_wakeup_endpoint is needed. Signed-off-by: goriainovstanislav --- devices/src/usb/camera.rs | 8 +------- devices/src/usb/keyboard.rs | 13 +++++-------- devices/src/usb/mod.rs | 10 +++------- devices/src/usb/storage.rs | 6 +----- devices/src/usb/tablet.rs | 14 ++++++-------- devices/src/usb/uas.rs | 6 +----- devices/src/usb/usbhost/mod.rs | 4 ---- devices/src/usb/xhci/xhci_controller.rs | 16 +++++----------- 8 files changed, 22 insertions(+), 55 deletions(-) diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index a8807318f..b4ef08613 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -32,9 +32,7 @@ use crate::camera_backend::{ }; use crate::usb::config::*; use crate::usb::descriptor::*; -use crate::usb::{ - UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, -}; +use crate::usb::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; use machine_manager::config::camera::CameraDevConfig; use machine_manager::config::valid_id; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; @@ -859,10 +857,6 @@ impl UsbDevice for UsbCamera { fn get_controller(&self) -> Option>> { None } - - fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.base.get_endpoint(true, 1) - } } /// UVC payload diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 214ade866..4c6847a33 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -22,11 +22,10 @@ use super::descriptor::{ UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; -use super::xhci::xhci_controller::XhciDevice; +use super::xhci::xhci_controller::{endpoint_number_to_id, XhciDevice}; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{ - notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, - UsbPacketStatus, + notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus, }; use machine_manager::config::valid_id; use ui::input::{register_keyboard, unregister_keyboard, KeyboardOpts}; @@ -175,7 +174,9 @@ impl KeyboardOpts for UsbKeyboardAdapter { } drop(locked_kbd); let clone_kbd = self.usb_kbd.clone(); - notify_controller(&(clone_kbd as Arc>)) + // Wakeup endpoint. + let ep_id = endpoint_number_to_id(true, 1); + notify_controller(&(clone_kbd as Arc>), ep_id) } } @@ -260,8 +261,4 @@ impl UsbDevice for UsbKeyboard { fn get_controller(&self) -> Option>> { self.cntlr.clone() } - - fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.base.get_endpoint(true, 1) - } } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index ac2e3addf..5fc8c5171 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -76,7 +76,7 @@ pub struct UsbDeviceRequest { impl ByteCode for UsbDeviceRequest {} /// The data transmission channel. -#[derive(Default, Clone)] +#[derive(Default, Clone, Copy)] pub struct UsbEndpoint { pub ep_number: u8, pub in_direction: bool, @@ -366,9 +366,6 @@ pub trait UsbDevice: Send + Sync { /// Get the controller which the USB device attached. fn get_controller(&self) -> Option>>; - /// Get the endpoint to wakeup. - fn get_wakeup_endpoint(&self) -> &UsbEndpoint; - /// Set the attached USB port. fn set_usb_port(&mut self, port: Option>>) { let usb_dev = self.usb_device_base_mut(); @@ -445,7 +442,7 @@ pub trait UsbDevice: Send + Sync { } /// Notify controller to process data request. -pub fn notify_controller(dev: &Arc>) -> Result<()> { +pub fn notify_controller(dev: &Arc>, ep_id: u8) -> Result<()> { let locked_dev = dev.lock().unwrap(); let xhci = if let Some(cntlr) = &locked_dev.get_controller() { cntlr.upgrade().unwrap() @@ -460,7 +457,6 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { }; let slot_id = usb_dev.addr; let wakeup = usb_dev.remote_wakeup & USB_DEVICE_REMOTE_WAKEUP == USB_DEVICE_REMOTE_WAKEUP; - let ep = locked_dev.get_wakeup_endpoint().clone(); // Drop the small lock. drop(locked_dev); let mut locked_xhci = xhci.lock().unwrap(); @@ -477,7 +473,7 @@ pub fn notify_controller(dev: &Arc>) -> Result<()> { locked_xhci.port_notify(&usb_port, PORTSC_PLC)?; } } - if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, &ep, 0) { + if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, ep_id as u32, 0) { error!("Failed to wakeup endpoint {:?}", e); } Ok(()) diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 429c7b9b1..282169e47 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -27,7 +27,7 @@ use super::descriptor::{ }; use super::xhci::xhci_controller::XhciDevice; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; -use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus}; +use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; use crate::{ ScsiBus::{ ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD, @@ -628,8 +628,4 @@ impl UsbDevice for UsbStorage { fn get_controller(&self) -> Option>> { self.cntlr.clone() } - - fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.base.get_endpoint(true, 1) - } } diff --git a/devices/src/usb/tablet.rs b/devices/src/usb/tablet.rs index 78551cb8c..b9c54fc97 100644 --- a/devices/src/usb/tablet.rs +++ b/devices/src/usb/tablet.rs @@ -23,10 +23,10 @@ use super::descriptor::{ UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, }; use super::hid::{Hid, HidType, QUEUE_LENGTH, QUEUE_MASK}; -use super::xhci::xhci_controller::XhciDevice; +use super::xhci::xhci_controller::{endpoint_number_to_id, XhciDevice}; use super::{ - config::*, notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, - UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, + config::*, notify_controller, UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, + UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; use machine_manager::config::valid_id; use ui::input::{ @@ -229,7 +229,9 @@ impl PointerOpts for UsbTabletAdapter { locked_tablet.hid.num += 1; drop(locked_tablet); let clone_tablet = self.tablet.clone(); - notify_controller(&(clone_tablet as Arc>)) + // Wakeup endpoint. + let ep_id = endpoint_number_to_id(true, 1); + notify_controller(&(clone_tablet as Arc>), ep_id) } } @@ -300,8 +302,4 @@ impl UsbDevice for UsbTablet { fn get_controller(&self) -> Option>> { self.cntlr.clone() } - - fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.base.get_endpoint(true, 1) - } } diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 3baca6a4b..688c1cca7 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -31,7 +31,7 @@ use super::descriptor::{ }; use super::xhci::xhci_controller::XhciDevice; use super::{ - UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbEndpoint, UsbPacket, UsbPacketStatus, + UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; use crate::{ @@ -935,10 +935,6 @@ impl UsbDevice for UsbUas { fn get_controller(&self) -> Option>> { None } - - fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.base.get_endpoint(true, 1) - } } impl UasRequest { diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 65e05112a..5859e19f6 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -1097,10 +1097,6 @@ impl UsbDevice for UsbHost { None } - fn get_wakeup_endpoint(&self) -> &UsbEndpoint { - self.base.get_endpoint(true, 1) - } - fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { trace::usb_host_req_control(self.config.hostbus, self.config.hostaddr, device_req); let mut locked_packet = packet.lock().unwrap(); diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index b515130d4..8fb49165f 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -30,7 +30,7 @@ use super::xhci_trb::{ TRB_TYPE_SHIFT, }; use crate::usb::{config::*, TransferOps}; -use crate::usb::{UsbDevice, UsbDeviceRequest, UsbEndpoint, UsbError, UsbPacket, UsbPacketStatus}; +use crate::usb::{UsbDevice, UsbDeviceRequest, UsbError, UsbPacket, UsbPacketStatus}; use address_space::{AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; @@ -2324,21 +2324,15 @@ impl XhciDevice { } /// Used for device to wakeup endpoint - pub fn wakeup_endpoint( - &mut self, - slot_id: u32, - ep: &UsbEndpoint, - stream_id: u32, - ) -> Result<()> { - let ep_id = endpoint_number_to_id(ep.in_direction, ep.ep_number); - if let Err(e) = self.get_endpoint_ctx(slot_id, ep_id as u32) { + pub fn wakeup_endpoint(&mut self, slot_id: u32, ep_id: u32, stream_id: u32) -> Result<()> { + if let Err(e) = self.get_endpoint_ctx(slot_id, ep_id) { trace::usb_xhci_unimplemented(&format!( "Invalid slot id or ep id, maybe device not activated, {:?}", e )); return Ok(()); } - self.kick_endpoint(slot_id, ep_id as u32, stream_id)?; + self.kick_endpoint(slot_id, ep_id, stream_id)?; Ok(()) } @@ -2496,7 +2490,7 @@ fn endpoint_id_to_number(ep_id: u8) -> (bool, u8) { (ep_id & 1 == 1, ep_id >> 1) } -fn endpoint_number_to_id(in_direction: bool, ep_number: u8) -> u8 { +pub fn endpoint_number_to_id(in_direction: bool, ep_number: u8) -> u8 { if ep_number == 0 { // Control endpoint. 1 -- Gitee From a99b65f2ba6eec927c51114c4adb064ffd856fbd Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 Jul 2024 07:40:41 +0800 Subject: [PATCH 1942/2187] pci: use unified "TestPciDevice" Use unified "TestPciDevice" for UT. Signed-off-by: liuxiangdong --- devices/src/pci/bus.rs | 87 +++------------------------------- devices/src/pci/host.rs | 73 ++--------------------------- devices/src/pci/mod.rs | 101 +++++++++++++++++++++++++++++----------- 3 files changed, 84 insertions(+), 177 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 1141afe9a..0524caa02 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -269,64 +269,11 @@ impl PciBus { #[cfg(test)] mod tests { - use anyhow::Result; - use super::*; use crate::pci::bus::PciBus; - use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; use crate::pci::host::tests::create_pci_host; - use crate::pci::root_port::RootPort; - use crate::pci::{PciDevBase, RootPortConfig}; - use crate::{Device, DeviceBase}; - use util::gen_base_func; - - #[derive(Clone)] - struct PciDevice { - base: PciDevBase, - } - - impl Device for PciDevice { - gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); - } - - impl PciDevOps for PciDevice { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn write_config(&mut self, offset: usize, data: &[u8]) { - #[allow(unused_variables)] - self.base.config.write( - offset, - data, - 0, - #[cfg(target_arch = "x86_64")] - None, - None, - ); - } - - fn realize(mut self) -> Result<()> { - let devfn = self.base.devfn; - self.init_write_mask(false)?; - self.init_write_clear_mask(false)?; - - let dev = Arc::new(Mutex::new(self)); - dev.lock() - .unwrap() - .base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .devices - .insert(devfn, dev.clone()); - Ok(()) - } - - fn unrealize(&mut self) -> Result<()> { - Ok(()) - } - } + use crate::pci::root_port::{RootPort, RootPortConfig}; + use crate::pci::tests::TestPciDevice; #[test] fn test_find_attached_bus() { @@ -342,26 +289,12 @@ mod tests { root_port.realize().unwrap(); // Test device is attached to the root bus. - let pci_dev = PciDevice { - base: PciDevBase { - base: DeviceBase::new("test1".to_string(), false), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - devfn: 10, - parent_bus: root_bus.clone(), - }, - }; + let pci_dev = TestPciDevice::new("test1", 10, root_bus); pci_dev.realize().unwrap(); // Test device is attached to the root port. let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); - let pci_dev = PciDevice { - base: PciDevBase { - base: DeviceBase::new("test2".to_string(), false), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - devfn: 12, - parent_bus: Arc::downgrade(&bus), - }, - }; + let pci_dev = TestPciDevice::new("test2", 12, Arc::downgrade(&bus)); pci_dev.realize().unwrap(); let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test0"); @@ -395,16 +328,8 @@ mod tests { root_port.realize().unwrap(); let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); - let pci_dev = PciDevice { - base: PciDevBase { - base: DeviceBase::new("test1".to_string(), false), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - devfn: 0, - parent_bus: Arc::downgrade(&bus), - }, - }; - let dev = Arc::new(Mutex::new(pci_dev.clone())); - let dev_ops: Arc> = dev; + let pci_dev = TestPciDevice::new("test1", 0, Arc::downgrade(&bus)); + let dev_ops: Arc> = Arc::new(Mutex::new(pci_dev.clone())); pci_dev.realize().unwrap(); let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 8bcaee721..1af696828 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -535,71 +535,11 @@ pub mod tests { use super::*; use crate::pci::bus::PciBus; - use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE, SECONDARY_BUS_NUM}; + use crate::pci::config::SECONDARY_BUS_NUM; use crate::pci::root_port::{RootPort, RootPortConfig}; - use crate::pci::{PciDevBase, Result}; - use crate::{Device, DeviceBase}; + use crate::pci::tests::TestPciDevice; use address_space::Region; - struct PciDevice { - base: PciDevBase, - } - - impl Device for PciDevice { - gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); - } - - impl PciDevOps for PciDevice { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn init_write_mask(&mut self, _is_bridge: bool) -> Result<()> { - let mut offset = 0_usize; - while offset < self.base.config.config.len() { - LittleEndian::write_u32( - &mut self.base.config.write_mask[offset..offset + 4], - 0xffff_ffff, - ); - offset += 4; - } - Ok(()) - } - - fn init_write_clear_mask(&mut self, _is_bridge: bool) -> Result<()> { - Ok(()) - } - - fn write_config(&mut self, offset: usize, data: &[u8]) { - #[allow(unused_variables)] - self.base.config.write( - offset, - data, - 0, - #[cfg(target_arch = "x86_64")] - None, - None, - ); - } - - fn realize(mut self) -> Result<()> { - let devfn = self.base.devfn; - self.init_write_mask(false)?; - self.init_write_clear_mask(false)?; - - let dev = Arc::new(Mutex::new(self)); - dev.lock() - .unwrap() - .base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .devices - .insert(devfn, dev.clone()); - Ok(()) - } - } - pub fn create_pci_host() -> Arc> { #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new( @@ -735,14 +675,7 @@ pub mod tests { root_port.realize().unwrap(); let bus = PciBus::find_bus_by_name(&pci_host.lock().unwrap().root_bus, "pcie.2").unwrap(); - let pci_dev = PciDevice { - base: PciDevBase { - base: DeviceBase::new("PCI device".to_string(), false), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - devfn: 8, - parent_bus: Arc::downgrade(&bus), - }, - }; + let pci_dev = TestPciDevice::new("PCI device", 8, Arc::downgrade(&bus)); pci_dev.realize().unwrap(); let addr: u64 = 8_u64 << ECAM_DEVFN_SHIFT | SECONDARY_BUS_NUM as u64; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 13da03462..9142c9fd5 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -404,10 +404,84 @@ pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { #[cfg(test)] mod tests { use super::*; + use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; use crate::DeviceBase; use address_space::{AddressSpace, Region}; use util::gen_base_func; + #[derive(Clone)] + pub struct TestPciDevice { + base: PciDevBase, + } + + impl TestPciDevice { + pub fn new(name: &str, devfn: u8, parent_bus: Weak>) -> Self { + Self { + base: PciDevBase { + base: DeviceBase::new(name.to_string(), false), + config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + devfn, + parent_bus, + }, + } + } + } + + impl Device for TestPciDevice { + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + } + + impl PciDevOps for TestPciDevice { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); + + fn write_config(&mut self, offset: usize, data: &[u8]) { + self.base.config.write( + offset, + data, + 0, + #[cfg(target_arch = "x86_64")] + None, + None, + ); + } + + fn realize(mut self) -> Result<()> { + let devfn = self.base.devfn; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; + + let dev = Arc::new(Mutex::new(self)); + let parent_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); + parent_bus + .lock() + .unwrap() + .devices + .insert(devfn, dev.clone()); + + Ok(()) + } + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } + + fn init_write_mask(&mut self, _is_bridge: bool) -> Result<()> { + let mut offset = 0_usize; + while offset < self.base.config.config.len() { + LittleEndian::write_u32( + &mut self.base.config.write_mask[offset..offset + 4], + 0xffff_ffff, + ); + offset += 4; + } + Ok(()) + } + + fn init_write_clear_mask(&mut self, _is_bridge: bool) -> Result<()> { + Ok(()) + } + } + #[test] fn test_le_write_u16_01() { let mut buf: [u8; 2] = [0; 2]; @@ -449,24 +523,6 @@ mod tests { #[test] fn set_dev_id() { - struct PciDev { - base: PciDevBase, - } - - impl Device for PciDev { - gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); - } - - impl PciDevOps for PciDev { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn write_config(&mut self, _offset: usize, _data: &[u8]) {} - - fn realize(self) -> Result<()> { - Ok(()) - } - } - let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sysmem"), "sysmem", @@ -480,14 +536,7 @@ mod tests { sys_mem.root().clone(), ))); - let dev = PciDev { - base: PciDevBase { - base: DeviceBase::new("PCI device".to_string(), false), - config: PciConfig::new(1, 1), - devfn: 0, - parent_bus: Arc::downgrade(&parent_bus), - }, - }; + let dev = TestPciDevice::new("PCI device", 0, Arc::downgrade(&parent_bus)); assert_eq!(dev.set_dev_id(1, 2), 258); } } -- Gitee From db8669db7d600fc84c6c3d8455fdbc8b53a74b8a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 28 Nov 2023 02:28:08 +0800 Subject: [PATCH 1943/2187] devices: Implement devicebase/busbase struct and Device/Bus trait We will implement a topology structure of ` Bus 1- Device 1- Bus 2` just like hardware. Implement devicebase/busbase struct and Device/Bus trait as the first step. The new topology follows the following rules: 1. Devices should implement Device trait. 2. Buses should implement Bus trait. 3. Every device must attached to its parent bus and may have extended child bus. 4. Every bus must attached to its parent device and may have devices which attached to it. Signed-off-by: liuxiangdong --- devices/src/lib.rs | 105 ++++++++++++++++++++++++++- devices/src/misc/ivshmem.rs | 2 +- devices/src/misc/pvpanic.rs | 5 +- devices/src/pci/bus.rs | 18 +++-- devices/src/pci/demo_device/mod.rs | 2 +- devices/src/pci/hotplug.rs | 5 +- devices/src/pci/mod.rs | 8 +- devices/src/pci/msix.rs | 4 +- devices/src/pci/root_port.rs | 2 +- devices/src/scsi/bus.rs | 12 ++- devices/src/scsi/disk.rs | 2 +- devices/src/sysbus/mod.rs | 9 ++- devices/src/usb/mod.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 2 +- machine/src/aarch64/pci_host_root.rs | 2 +- machine/src/lib.rs | 3 +- machine/src/x86_64/ich9_lpc.rs | 6 +- machine/src/x86_64/mch.rs | 6 +- vfio/src/vfio_pci.rs | 2 +- virtio/src/transport/virtio_mmio.rs | 2 +- virtio/src/transport/virtio_pci.rs | 2 +- 21 files changed, 163 insertions(+), 38 deletions(-) diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 491ca1d57..404a8c206 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -40,7 +40,10 @@ pub use scsi::bus as ScsiBus; pub use scsi::disk as ScsiDisk; use std::any::Any; +use std::collections::BTreeMap; +use std::sync::{Arc, Mutex, Weak}; +use anyhow::{bail, Context, Result}; use util::AsAny; #[derive(Clone, Default)] @@ -49,15 +52,24 @@ pub struct DeviceBase { pub id: String, /// Whether it supports hot-plug/hot-unplug. pub hotpluggable: bool, + /// parent bus. + pub parent: Option>>, + /// Child bus. + pub child: Option>>, } impl DeviceBase { - pub fn new(id: String, hotpluggable: bool) -> Self { - DeviceBase { id, hotpluggable } + pub fn new(id: String, hotpluggable: bool, parent: Option>>) -> Self { + DeviceBase { + id, + hotpluggable, + parent, + child: None, + } } } -pub trait Device: Any + AsAny { +pub trait Device: Any + AsAny + Send + Sync { fn device_base(&self) -> &DeviceBase; fn device_base_mut(&mut self) -> &mut DeviceBase; @@ -71,6 +83,93 @@ pub trait Device: Any + AsAny { fn hotpluggable(&self) -> bool { self.device_base().hotpluggable } + + /// Get the bus which this device is mounted on. + fn parent_bus(&self) -> Option>> { + self.device_base().parent.clone() + } + + /// Get the bus which this device has. + fn child_bus(&self) -> Option>> { + self.device_base().child.clone() + } +} + +#[derive(Default)] +pub struct BusBase { + /// Name of this bus. + pub name: String, + /// Parent device. + pub parent: Option>>, + /// Children devices. + /// + /// Note: + /// 1. The construction of FDT table needs to strictly follow the order of sysbus, + /// so `BTreemap` needs to be used. + /// 2. every device has a unique address on the bus. Using `u64` is sufficient for we can + /// convert it to u8(devfn) for PCI bus and convert it to (u8, u16)(target, lun) for SCSI bus. + /// SysBus doesn't need this unique `u64` address, so we will incrementally fill in a useless number. + pub children: BTreeMap>>, +} + +impl BusBase { + fn new(name: String) -> BusBase { + Self { + name, + ..Default::default() + } + } +} + +pub trait Bus: Send + Sync { + fn bus_base(&self) -> &BusBase; + + fn bus_base_mut(&mut self) -> &mut BusBase; + + /// Get the name of this bus. + fn name(&self) -> String { + self.bus_base().name.clone() + } + + /// Get the device that owns this bus. + fn parent_device(&self) -> Option>> { + self.bus_base().parent.clone() + } + + /// Get the devices mounted on this bus. + fn child_devices(&self) -> BTreeMap>> { + self.bus_base().children.clone() + } + + /// Get the specific device mounted on this bus. + fn child_dev(&self, key: u64) -> Option<&Arc>> { + self.bus_base().children.get(&key) + } + + /// Attach device to this bus. + fn attach_child(&mut self, key: u64, dev: Arc>) -> Result<()> { + let children = &mut self.bus_base_mut().children; + if children.get(&key).is_some() { + bail!( + "Location of the device {} is same as one of the bus {}", + dev.lock().unwrap().name(), + self.name() + ); + } + children.insert(key, dev); + + Ok(()) + } + + /// Detach device from this bus. + fn detach_child(&mut self, key: u64) -> Result<()> { + self.bus_base_mut() + .children + .remove(&key) + .with_context(|| format!("No such device using key {} in bus {}.", key, self.name()))?; + + Ok(()) + } } #[cfg(test)] diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index cc9e383f8..acb9cc1ae 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -52,7 +52,7 @@ impl Ivshmem { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(name, false), + base: DeviceBase::new(name, false, Some(parent_bus.clone())), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), devfn, parent_bus, diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index cdebea6c8..2e2444ea0 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -112,7 +112,7 @@ impl PvPanicPci { pub fn new(config: &PvpanicDevConfig, devfn: u8, parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(config.id.clone(), false), + base: DeviceBase::new(config.id.clone(), false, Some(parent_bus.clone())), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, parent_bus, @@ -262,6 +262,7 @@ impl PciDevOps for PvPanicPci { mod tests { use super::*; use crate::pci::{host::tests::create_pci_host, le_read_u16, PciHost}; + use crate::Bus; use machine_manager::config::str_slip_to_clap; fn init_pvpanic_dev(devfn: u8, supported_features: u32, dev_id: &str) -> Arc> { @@ -320,7 +321,7 @@ mod tests { let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "pvpanic_test"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); - assert_eq!(bus.lock().unwrap().name, "pcie.0"); + assert_eq!(bus.lock().unwrap().name(), "pcie.0"); assert_eq!(dev.lock().unwrap().name(), "pvpanic_test"); } diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 0524caa02..d87d5d852 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -22,15 +22,15 @@ use super::{ hotplug::HotplugOps, PciDevOps, PciIntxState, }; -use crate::MsiIrqManager; +use crate::{Bus, BusBase, MsiIrqManager}; use address_space::Region; +use util::gen_base_func; type DeviceBusInfo = (Arc>, Arc>); /// PCI bus structure. pub struct PciBus { - /// Bus name - pub name: String, + pub base: BusBase, /// Devices attached to the bus. pub devices: HashMap>>, /// Child buses of the bus. @@ -49,6 +49,10 @@ pub struct PciBus { pub msi_irq_manager: Option>, } +impl Bus for PciBus { + gen_base_func!(bus_base, bus_base_mut, BusBase, base); +} + impl PciBus { /// Create new bus entity. /// @@ -63,7 +67,7 @@ impl PciBus { mem_region: Region, ) -> Self { Self { - name, + base: BusBase::new(name), devices: HashMap::new(), child_buses: Vec::new(), parent_bridge: None, @@ -145,7 +149,7 @@ impl PciBus { /// * `name` - Bus name. pub fn find_bus_by_name(bus: &Arc>, bus_name: &str) -> Option>> { let locked_bus = bus.lock().unwrap(); - if locked_bus.name.as_str() == bus_name { + if locked_bus.name().as_str() == bus_name { return Some((*bus).clone()); } for sub_bus in &locked_bus.child_buses { @@ -303,13 +307,13 @@ mod tests { let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); - assert_eq!(bus.lock().unwrap().name, "pcie.0"); + assert_eq!(bus.lock().unwrap().name(), "pcie.0"); assert_eq!(dev.lock().unwrap().name(), "test1"); let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test2"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); - assert_eq!(bus.lock().unwrap().name, "pcie.1"); + assert_eq!(bus.lock().unwrap().name(), "pcie.1"); assert_eq!(dev.lock().unwrap().name(), "test2"); } diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 987120c38..52e1381d5 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -109,7 +109,7 @@ impl DemoDev { }; DemoDev { base: PciDevBase { - base: DeviceBase::new(cfg.id.clone(), false), + base: DeviceBase::new(cfg.id.clone(), false, Some(parent_bus.clone())), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), devfn, parent_bus, diff --git a/devices/src/pci/hotplug.rs b/devices/src/pci/hotplug.rs index df5bfca39..24533a24e 100644 --- a/devices/src/pci/hotplug.rs +++ b/devices/src/pci/hotplug.rs @@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use crate::pci::{PciBus, PciDevOps}; +use crate::Bus; pub trait HotplugOps: Send { /// Plug device, usually called when hot plug device in device_add. @@ -47,7 +48,7 @@ pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> } else { bail!( "No hot plug controller found for bus {} when plug", - locked_bus.name + locked_bus.name() ); } } @@ -76,7 +77,7 @@ pub fn handle_unplug_pci_request( .with_context(|| { format!( "No hot plug controller found for bus {} when unplug request", - locked_bus.name + locked_bus.name() ) })?; // No need to hold the lock. diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 9142c9fd5..b3168e7a2 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -44,7 +44,7 @@ use crate::misc::ivshmem::Ivshmem; use crate::misc::pvpanic::PvPanicPci; use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; use crate::usb::xhci::xhci_pci::XhciPciDevice; -use crate::{Device, DeviceBase, MsiIrqManager}; +use crate::{Bus, Device, DeviceBase, MsiIrqManager}; #[cfg(feature = "demo_device")] use demo_device::DemoDev; @@ -221,7 +221,7 @@ pub trait PciDevOps: Device + Send { /// Get the path of the PCI bus where the device resides. fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { let locked_parent_bus = parent_bus.lock().unwrap(); - let parent_dev_path = if locked_parent_bus.name.eq("pcie.0") { + let parent_dev_path = if locked_parent_bus.name().eq("pcie.0") { String::from("/pci@ffffffffffffffff") } else { // This else branch will not be executed currently, @@ -368,7 +368,7 @@ pub fn init_multifunction( // Function 0 should set multifunction bit. bail!( "PCI: single function device can't be populated in bus {} function {}.{}", - &locked_bus.name, + &locked_bus.name(), slot, devfn & 0x07 ); @@ -418,7 +418,7 @@ mod tests { pub fn new(name: &str, devfn: u8, parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(name.to_string(), false), + base: DeviceBase::new(name.to_string(), false, Some(parent_bus.clone())), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn, parent_bus, diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 451a66384..3d4034025 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -644,7 +644,7 @@ mod tests { #[test] fn test_init_msix() { let mut base = PciDevBase { - base: DeviceBase::new("msix".to_string(), false), + base: DeviceBase::new("msix".to_string(), false, None), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), devfn: 1, parent_bus: Weak::new(), @@ -747,7 +747,7 @@ mod tests { #[test] fn test_write_config() { let mut base = PciDevBase { - base: DeviceBase::new("msix".to_string(), false), + base: DeviceBase::new("msix".to_string(), false, None), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), devfn: 1, parent_bus: Weak::new(), diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 9d3bcf254..b63490580 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -121,7 +121,7 @@ impl RootPort { Self { base: PciDevBase { - base: DeviceBase::new(cfg.id, true), + base: DeviceBase::new(cfg.id, true, Some(parent_bus.clone())), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, parent_bus, diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 8a2226531..b816bdd7f 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -24,8 +24,9 @@ use crate::ScsiDisk::{ SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, SECTOR_SHIFT, }; +use crate::{Bus, BusBase}; use util::aio::{AioCb, AioReqResult, Iovec}; -use util::AsAny; +use util::{gen_base_func, AsAny}; /// Scsi Operation code. pub const TEST_UNIT_READY: u8 = 0x00; @@ -367,16 +368,19 @@ pub enum ScsiXferMode { } pub struct ScsiBus { - /// Bus name. - pub name: String, + pub base: BusBase, /// Scsi Devices attached to the bus. pub devices: HashMap<(u8, u16), Arc>>, } +impl Bus for ScsiBus { + gen_base_func!(bus_base, bus_base_mut, BusBase, base); +} + impl ScsiBus { pub fn new(bus_name: String) -> ScsiBus { ScsiBus { - name: bus_name, + base: BusBase::new(bus_name), devices: HashMap::new(), } } diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 1f88cf0ad..8aab9a229 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -182,7 +182,7 @@ impl ScsiDevice { }; ScsiDevice { - base: DeviceBase::new(dev_cfg.id.clone(), false), + base: DeviceBase::new(dev_cfg.id.clone(), false, None), dev_cfg, drive_cfg, state: ScsiDevState::new(), diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 1e56f3636..05479a3ce 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -35,9 +35,10 @@ use crate::legacy::{FwCfgIO, RTC}; use crate::legacy::{FwCfgMem, PL011, PL031}; use crate::legacy::{PFlash, Serial}; use crate::pci::PciHost; -use crate::{Device, DeviceBase, IrqState, LineIrqManager, TriggerMode}; +use crate::{Bus, BusBase, Device, DeviceBase, IrqState, LineIrqManager, TriggerMode}; use acpi::{AmlBuilder, AmlScope}; use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use util::gen_base_func; // Now that the serial device use a hardcoded IRQ number (4), and the starting // free IRQ number can be 5. @@ -53,6 +54,7 @@ pub const IRQ_BASE: i32 = 32; pub const IRQ_MAX: i32 = 191; pub struct SysBus { + pub base: BusBase, #[cfg(target_arch = "x86_64")] pub sys_io: Arc, pub sys_mem: Arc, @@ -89,6 +91,7 @@ impl SysBus { mmio_region: (u64, u64), ) -> Self { Self { + base: BusBase::new("sysbus".to_string()), #[cfg(target_arch = "x86_64")] sys_io: sys_io.clone(), sys_mem: sys_mem.clone(), @@ -157,6 +160,10 @@ impl SysBus { } } +impl Bus for SysBus { + gen_base_func!(bus_base, bus_base_mut, BusBase, base); +} + #[derive(Clone)] pub struct SysRes { // Note: region_base/region_size are both 0 means that this device doesn't have its own memory layout. diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 5fc8c5171..f8dc2f90c 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -130,7 +130,7 @@ pub struct UsbDeviceBase { impl UsbDeviceBase { pub fn new(id: String, data_buf_len: usize) -> Self { let mut dev = UsbDeviceBase { - base: DeviceBase::new(id, false), + base: DeviceBase::new(id, false, None), port: None, speed: 0, addr: 0, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 8a7208e6f..cdcd50cb7 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -113,7 +113,7 @@ impl XhciPciDevice { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(config.id.clone().unwrap(), true), + base: DeviceBase::new(config.id.clone().unwrap(), true, Some(parent_bus.clone())), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, parent_bus, diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index 512cf8f3c..7192fbbe0 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -35,7 +35,7 @@ impl PciHostRoot { pub fn new(parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new("PCI Host Root".to_string(), false), + base: DeviceBase::new("PCI Host Root".to_string(), false, Some(parent_bus.clone())), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), parent_bus, devfn: 0, diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8e6c668e6..1b71e85be 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -71,6 +71,7 @@ use devices::usb::uas::{UsbUas, UsbUasConfig}; use devices::usb::usbhost::{UsbHost, UsbHostConfig}; use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice}; use devices::usb::UsbDevice; +use devices::Bus; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; @@ -1511,7 +1512,7 @@ pub trait MachineOps: MachineLifecycle { .with_context(|| format!("Bus not found, dev id {}", dev_id))? .0; let locked_bus = bus.lock().unwrap(); - if locked_bus.name == "pcie.0" { + if locked_bus.name() == "pcie.0" { // No need to reset root bus return Ok(()); } diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index 4038e5037..83e1e4a54 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -64,7 +64,11 @@ impl LPCBridge { ) -> Result { Ok(Self { base: PciDevBase { - base: DeviceBase::new("ICH9 LPC bridge".to_string(), false), + base: DeviceBase::new( + "ICH9 LPC bridge".to_string(), + false, + Some(parent_bus.clone()), + ), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0x1F << 3, parent_bus, diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index ddf7bd674..a025281e5 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -57,7 +57,11 @@ impl Mch { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new("Memory Controller Hub".to_string(), false), + base: DeviceBase::new( + "Memory Controller Hub".to_string(), + false, + Some(parent_bus.clone()), + ), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, parent_bus, diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 7a74badc4..391fba16c 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -132,7 +132,7 @@ impl VfioPciDevice { Self { // Unknown PCI or PCIe type here, allocate enough space to match the two types. base: PciDevBase { - base: DeviceBase::new(name, true), + base: DeviceBase::new(name, true, Some(parent_bus.clone())), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), devfn, parent_bus, diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 5d07ce776..8e872a380 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -148,7 +148,7 @@ impl VirtioMmioDevice { let queue_num = device.lock().unwrap().queue_num(); let mut mmio_device = VirtioMmioDevice { base: SysBusDevBase { - base: DeviceBase::new(name, false), + base: DeviceBase::new(name, false, None), dev_type: SysBusDevType::VirtioMmio, interrupt_evt: Some(Arc::new(create_new_eventfd()?)), ..Default::default() diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index d80ded3ca..a4eabe2bc 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -332,7 +332,7 @@ impl VirtioPciDevice { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { base: PciDevBase { - base: DeviceBase::new(name, true), + base: DeviceBase::new(name, true, Some(parent_bus.clone())), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), devfn, parent_bus, -- Gitee From c247f5557a6c810df9edb5d0d424f71ffb9cce70 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 20 Jun 2024 19:38:45 +0800 Subject: [PATCH 1944/2187] devices: add method for converting from trait object to struct object Step 1. Add function device_as_any and bus_as_any: Currently, AsAny trait uses Any trait as a generic parameter. Any trait requires a `'static` lifecycle. This framework involves a large number of local variable transformations which don't have `'static` lifecycle. If AsAny is used directly on these local variables, an error "borrowed value does not live long enough"/ "argument requires that `XXXXX` is borrowed for `'static`" will be reported. So, add device/bus_as_any functions to make preparation for the runtime conversion. Step 2. Add convert_device/bus_ref/mut macro: We can use `downcast_mut/ref` after step 1, and then we can complete the runtime conversion from trait object to struct object by using device_as_any/device_as_any. Signed-off-by: liuxiangdong --- devices/src/lib.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 404a8c206..33f9af560 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -74,6 +74,13 @@ pub trait Device: Any + AsAny + Send + Sync { fn device_base_mut(&mut self) -> &mut DeviceBase; + /// `Any` trait requires a `'static` lifecycle. Error "argument requires that `device` is borrowed for `'static`" + /// will be reported when using `as_any` directly for local variables which don't have `'static` lifecycle. + /// Encapsulation of `as_any` can solve this problem. + fn device_as_any(&mut self) -> &mut dyn Any { + self.as_any_mut() + } + /// Get device name. fn name(&self) -> String { self.device_base().id.clone() @@ -95,6 +102,44 @@ pub trait Device: Any + AsAny + Send + Sync { } } +/// Macro `convert_device_ref!`: Convert from Arc> to &$device_type. +/// +/// # Arguments +/// +/// * `$trait_device` - Variable defined as Arc>. +/// * `$lock_device` - Variable used to get MutexGuard<'_, dyn Device>. +/// * `$struct_device` - Variable used to get &$device_type. +/// * `$device_type` - Struct corresponding to device type. +#[macro_export] +macro_rules! convert_device_ref { + ($trait_device:expr, $lock_device: ident, $struct_device: ident, $device_type: ident) => { + let mut $lock_device = $trait_device.lock().unwrap(); + let $struct_device = $lock_device + .device_as_any() + .downcast_ref::<$device_type>() + .unwrap(); + }; +} + +/// Macro `convert_device_mut!`: Convert from Arc> to &mut $device_type. +/// +/// # Arguments +/// +/// * `$trait_device` - Variable defined as Arc>. +/// * `$lock_device` - Variable used to get MutexGuard<'_, dyn Device>. +/// * `$struct_device` - Variable used to get &mut $device_type. +/// * `$device_type` - Struct corresponding to device type. +#[macro_export] +macro_rules! convert_device_mut { + ($trait_device:expr, $lock_device: ident, $struct_device: ident, $device_type: ident) => { + let mut $lock_device = $trait_device.lock().unwrap(); + let $struct_device = $lock_device + .device_as_any() + .downcast_mut::<$device_type>() + .unwrap(); + }; +} + #[derive(Default)] pub struct BusBase { /// Name of this bus. @@ -121,11 +166,18 @@ impl BusBase { } } -pub trait Bus: Send + Sync { +pub trait Bus: Any + AsAny + Send + Sync { fn bus_base(&self) -> &BusBase; fn bus_base_mut(&mut self) -> &mut BusBase; + /// `Any` trait requires a `'static` lifecycle. Error "argument requires that `bus` is borrowed for `'static`" + /// will be reported when using `as_any` directly for local variables which don't have `'static` lifecycle. + /// Encapsulation of `as_any` can solve this problem. + fn bus_as_any(&mut self) -> &mut dyn Any { + self.as_any_mut() + } + /// Get the name of this bus. fn name(&self) -> String { self.bus_base().name.clone() @@ -172,6 +224,38 @@ pub trait Bus: Send + Sync { } } +/// Macro `convert_bus_ref!`: Convert from Arc> to &$bus_type. +/// +/// # Arguments +/// +/// * `$trait_bus` - Variable defined as Arc>. +/// * `$lock_bus` - Variable used to get MutexGuard<'_, dyn Bus>. +/// * `$struct_bus` - Variable used to get &$bus_type. +/// * `$bus_type` - Struct corresponding to bus type. +#[macro_export] +macro_rules! convert_bus_ref { + ($trait_bus:expr, $lock_bus: ident, $struct_bus: ident, $bus_type: ident) => { + let mut $lock_bus = $trait_bus.lock().unwrap(); + let $struct_bus = $lock_bus.bus_as_any().downcast_ref::<$bus_type>().unwrap(); + }; +} + +/// Macro `convert_bus_mut!`: Convert from Arc> to &mut $bus_type. +/// +/// # Arguments +/// +/// * `$trait_bus` - Variable defined as Arc>. +/// * `$lock_bus` - Variable used to get MutexGuard<'_, dyn Bus>. +/// * `$struct_bus` - Variable used to get &mut $bus_type. +/// * `$bus_type` - Struct corresponding to bus type. +#[macro_export] +macro_rules! convert_bus_mut { + ($trait_bus:expr, $lock_bus: ident, $struct_bus: ident, $bus_type: ident) => { + let mut $lock_bus = $trait_bus.lock().unwrap(); + let $struct_bus = $lock_bus.bus_as_any().downcast_mut::<$bus_type>().unwrap(); + }; +} + #[cfg(test)] pub mod test { use std::sync::Arc; -- Gitee From 98e5f31bfd3e3c079e07e46f75b114cbc517f897 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 28 Nov 2023 14:11:49 +0800 Subject: [PATCH 1945/2187] devices/scsi: adjust the scsi bus/disk module to the new framework Adjust the scsi bus/disk module to the new framework: 1.Scsi Bus have scsi disks as its child devices. 2.Scsi Disk have an scsi bus as its parent bus. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 214 +++++++++++++++++--------------- devices/src/scsi/disk.rs | 15 ++- devices/src/usb/storage.rs | 8 +- devices/src/usb/uas.rs | 14 +-- machine/src/lib.rs | 18 +-- virtio/src/device/scsi_cntlr.rs | 14 ++- 6 files changed, 151 insertions(+), 132 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index b816bdd7f..cc7c72692 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::cmp; -use std::collections::HashMap; use std::io::Write; use std::sync::{Arc, Mutex}; @@ -24,7 +23,7 @@ use crate::ScsiDisk::{ SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, SCSI_DISK_F_DPOFUA, SCSI_DISK_F_REMOVABLE, SCSI_TYPE_DISK, SCSI_TYPE_ROM, SECTOR_SHIFT, }; -use crate::{Bus, BusBase}; +use crate::{convert_bus_ref, convert_device_ref, Bus, BusBase, Device, SCSI_DEVICE}; use util::aio::{AioCb, AioReqResult, Iovec}; use util::{gen_base_func, AsAny}; @@ -357,6 +356,11 @@ const GC_FC_CORE: u16 = 0x0001; /// The medium may be removed from the device. const GC_FC_REMOVABLE_MEDIUM: u16 = 0x0003; +// BusBase.Children uses `u64` for device's unique address. We use bits [32-39] in `u64` +// to represent the target number and bits[0-15] in `u64` to the lun number. +const TARGET_ID_SHIFT: u64 = 32; +const LUN_ID_MASK: u64 = 0xFFFF; + #[derive(Clone, PartialEq, Eq)] pub enum ScsiXferMode { /// TEST_UNIT_READY, ... @@ -367,21 +371,36 @@ pub enum ScsiXferMode { ScsiXferToDev, } +// Convert from (target, lun) to unique address in BusBase. +pub fn get_scsi_key(target: u8, lun: u16) -> u64 { + (target as u64) << TARGET_ID_SHIFT | lun as u64 +} + +// Convert from unique address in BusBase to (target, lun). +fn parse_scsi_key(key: u64) -> (u8, u16) { + ((key >> TARGET_ID_SHIFT) as u8, (key & LUN_ID_MASK) as u16) +} + pub struct ScsiBus { pub base: BusBase, - /// Scsi Devices attached to the bus. - pub devices: HashMap<(u8, u16), Arc>>, } impl Bus for ScsiBus { gen_base_func!(bus_base, bus_base_mut, BusBase, base); } +/// Convert from Arc> to &ScsiBus. +#[macro_export] +macro_rules! SCSI_BUS { + ($trait_bus:expr, $lock_bus: ident, $struct_bus: ident) => { + convert_bus_ref!($trait_bus, $lock_bus, $struct_bus, ScsiBus); + }; +} + impl ScsiBus { pub fn new(bus_name: String) -> ScsiBus { ScsiBus { base: BusBase::new(bus_name), - devices: HashMap::new(), } } @@ -389,9 +408,9 @@ impl ScsiBus { /// If the device requested by the target number and the lun number is non-existen, /// return the first device in ScsiBus's devices list. It's OK because we will not /// use this "random" device, we will just use it to prove that the target is existen. - pub fn get_device(&self, target: u8, lun: u16) -> Option>> { - if let Some(dev) = self.devices.get(&(target, lun)) { - return Some((*dev).clone()); + pub fn get_device(&self, target: u8, lun: u16) -> Option>> { + if let Some(device) = self.child_dev(get_scsi_key(target, lun)) { + return Some(device.clone()); } // If lun device requested in CDB's LUNS bytes is not found, it may be a target request. @@ -400,11 +419,11 @@ impl ScsiBus { // is non-existent. So, we should find if there exists a lun which has the same id with // target id in CBD's LUNS bytes. And, if there exist two or more luns which have the same // target id, just return the first one is OK enough. - for (id, device) in self.devices.iter() { - let (target_id, lun_id) = id; - if *target_id == target { - trace::scsi_bus_get_device(*target_id, lun, *lun_id); - return Some((*device).clone()); + for (key, device) in self.child_devices() { + let (target_id, lun_id) = parse_scsi_key(key); + if target_id == target { + trace::scsi_bus_get_device(target_id, lun, lun_id); + return Some(device.clone()); } } @@ -417,7 +436,7 @@ impl ScsiBus { fn scsi_bus_parse_req_cdb( cdb: [u8; SCSI_CMD_BUF_SIZE], - dev: Arc>, + dev: Arc>, ) -> Option { let op = cdb[0]; let len = scsi_cdb_length(&cdb); @@ -496,7 +515,7 @@ pub struct ScsiRequest { pub iovec: Vec, // Provided buffer's length. pub datalen: u32, - pub dev: Arc>, + pub dev: Arc>, // Upper level request which contains this ScsiRequest. pub upper_req: Box, } @@ -507,18 +526,17 @@ impl ScsiRequest { req_lun: u16, iovec: Vec, datalen: u32, - scsidevice: Arc>, + device: Arc>, upper_req: Box, ) -> Result { - let cmd = scsi_bus_parse_req_cdb(cdb, scsidevice.clone()).with_context(|| "Error cdb!")?; + let cmd = scsi_bus_parse_req_cdb(cdb, device.clone()).with_context(|| "Error cdb!")?; let op = cmd.op; let opstype = scsi_operation_type(op); if op == WRITE_10 || op == READ_10 { - let dev_lock = scsidevice.lock().unwrap(); - let disk_size = dev_lock.disk_sectors << SECTOR_SHIFT; - let disk_type = dev_lock.scsi_type; - drop(dev_lock); + SCSI_DEVICE!(device, locked_dev, scsi_dev); + let disk_size = scsi_dev.disk_sectors << SECTOR_SHIFT; + let disk_type = scsi_dev.scsi_type; let offset_shift = match disk_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, @@ -545,7 +563,7 @@ impl ScsiRequest { opstype, iovec, datalen, - dev: scsidevice, + dev: device, upper_req, }) } @@ -554,14 +572,14 @@ impl ScsiRequest { let mode = self.cmd.mode.clone(); let op = self.cmd.op; let dev = self.dev.clone(); - let locked_dev = dev.lock().unwrap(); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); // SAFETY: the block_backend is assigned after device realized. - let block_backend = locked_dev.block_backend.as_ref().unwrap(); + let block_backend = scsi_dev.block_backend.as_ref().unwrap(); let mut locked_backend = block_backend.lock().unwrap(); let s_req = Arc::new(Mutex::new(self)); let scsicompletecb = ScsiCompleteCb { req: s_req.clone() }; - let offset_bits = match locked_dev.scsi_type { + let offset_bits = match scsi_dev.scsi_type { SCSI_TYPE_DISK => SCSI_DISK_DEFAULT_BLOCK_SIZE_SHIFT, _ => SCSI_CDROM_DEFAULT_BLOCK_SIZE_SHIFT, }; @@ -634,8 +652,8 @@ impl ScsiRequest { Ok(Vec::new()) } TEST_UNIT_READY => { - let dev_lock = self.dev.lock().unwrap(); - if dev_lock.block_backend.is_none() { + SCSI_DEVICE!(self.dev, locked_dev, scsi_dev); + if scsi_dev.block_backend.is_none() { Err(anyhow!("No scsi backend!")) } else { Ok(Vec::new()) @@ -670,7 +688,9 @@ impl ScsiRequest { let mut not_supported_flag = false; let mut sense = None; let mut status = GOOD; - let found_lun = self.dev.lock().unwrap().dev_cfg.lun; + SCSI_DEVICE!(self.dev, locked_dev, scsi_dev); + let found_lun = scsi_dev.dev_cfg.lun; + drop(locked_dev); // Requested lun id is not equal to found device id means it may be a target request. // REPORT LUNS is also a target request command. @@ -770,10 +790,10 @@ fn scsi_cdb_length(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i32 { } } -pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i32 { - let dev_lock = dev.lock().unwrap(); - let block_size = dev_lock.block_size as i32; - drop(dev_lock); +pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i32 { + SCSI_DEVICE!(dev, locked_dev, scsi_dev); + let block_size = scsi_dev.block_size as i32; + drop(locked_dev); let mut xfer = match cdb[0] >> 5 { // Group Code | Transfer length. | @@ -883,28 +903,27 @@ fn scsi_cdb_xfer_mode(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> ScsiXferMode { /// VPD: Vital Product Data. fn scsi_command_emulate_vpd_page( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { let buflen: usize; let mut outbuf: Vec = vec![0; 4]; - - let dev_lock = dev.lock().unwrap(); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); let page_code = cmd.buf[2]; - outbuf[0] = dev_lock.scsi_type as u8 & 0x1f; + outbuf[0] = scsi_dev.scsi_type as u8 & 0x1f; outbuf[1] = page_code; match page_code { 0x00 => { // Supported VPD Pages. outbuf.push(0_u8); - if !dev_lock.state.serial.is_empty() { + if !scsi_dev.state.serial.is_empty() { // 0x80: Unit Serial Number. outbuf.push(0x80); } // 0x83: Device Identification. outbuf.push(0x83); - if dev_lock.scsi_type == SCSI_TYPE_DISK { + if scsi_dev.scsi_type == SCSI_TYPE_DISK { // 0xb0: Block Limits. outbuf.push(0xb0); // 0xb1: Block Device Characteristics. @@ -916,20 +935,20 @@ fn scsi_command_emulate_vpd_page( } 0x80 => { // Unit Serial Number. - let len = dev_lock.state.serial.len(); + let len = scsi_dev.state.serial.len(); if len == 0 { bail!("Missed serial number!"); } let l = cmp::min(SCSI_INQUIRY_VPD_SERIAL_NUMBER_MAX_LEN, len); - let mut serial_vec = dev_lock.state.serial.as_bytes().to_vec(); + let mut serial_vec = scsi_dev.state.serial.as_bytes().to_vec(); serial_vec.truncate(l); outbuf.append(&mut serial_vec); buflen = outbuf.len(); } 0x83 => { // Device Identification. - let mut len: u8 = dev_lock.state.device_id.len() as u8; + let mut len: u8 = scsi_dev.state.device_id.len() as u8; if len > (255 - 8) { len = 255 - 8; } @@ -941,7 +960,7 @@ fn scsi_command_emulate_vpd_page( // len: identifier length. outbuf.append(&mut [0x2_u8, 0_u8, 0_u8, len].to_vec()); - let mut device_id_vec = dev_lock.state.device_id.as_bytes().to_vec(); + let mut device_id_vec = scsi_dev.state.device_id.as_bytes().to_vec(); device_id_vec.truncate(len as usize); outbuf.append(&mut device_id_vec); } @@ -949,7 +968,7 @@ fn scsi_command_emulate_vpd_page( } 0xb0 => { // Block Limits. - if dev_lock.scsi_type == SCSI_TYPE_ROM { + if scsi_dev.scsi_type == SCSI_TYPE_ROM { bail!("Invalid scsi type: SCSI_TYPE_ROM !"); } outbuf.resize(64, 0); @@ -1069,7 +1088,7 @@ fn scsi_command_emulate_target_inquiry(lun: u16, cmd: &ScsiCommand) -> Result>, + dev: &Arc>, ) -> Result> { // Byte1 bit0: EVPD(enable vital product data). if cmd.buf[1] == 0x1 { @@ -1083,26 +1102,26 @@ fn scsi_command_emulate_inquiry( let buflen = cmp::min(cmd.xfer, SCSI_MAX_INQUIRY_LEN); let mut outbuf: Vec = vec![0; SCSI_MAX_INQUIRY_LEN as usize]; - let dev_lock = dev.lock().unwrap(); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); - outbuf[0] = (dev_lock.scsi_type & 0x1f) as u8; - outbuf[1] = match dev_lock.state.features & SCSI_DISK_F_REMOVABLE { + outbuf[0] = (scsi_dev.scsi_type & 0x1f) as u8; + outbuf[1] = match scsi_dev.state.features & SCSI_DISK_F_REMOVABLE { 1 => 0x80, _ => 0, }; - let product_bytes = dev_lock.state.product.as_bytes(); + let product_bytes = scsi_dev.state.product.as_bytes(); let product_len = cmp::min(product_bytes.len(), SCSI_INQUIRY_PRODUCT_MAX_LEN); - let vendor_bytes = dev_lock.state.vendor.as_bytes(); + let vendor_bytes = scsi_dev.state.vendor.as_bytes(); let vendor_len = cmp::min(vendor_bytes.len(), SCSI_INQUIRY_VENDOR_MAX_LEN); - let version_bytes = dev_lock.state.version.as_bytes(); + let version_bytes = scsi_dev.state.version.as_bytes(); let vension_len = cmp::min(version_bytes.len(), SCSI_INQUIRY_VERSION_MAX_LEN); outbuf[16..16 + product_len].copy_from_slice(product_bytes); outbuf[8..8 + vendor_len].copy_from_slice(vendor_bytes); outbuf[32..32 + vension_len].copy_from_slice(version_bytes); - drop(dev_lock); + drop(locked_dev); // outbuf: // Byte2: Version. @@ -1125,17 +1144,17 @@ fn scsi_command_emulate_inquiry( fn scsi_command_emulate_read_capacity_10( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { if cmd.buf[8] & 1 == 0 && cmd.lba != 0 { // PMI(Partial Medium Indicator) bail!("Invalid scsi cmd READ_CAPACITY_10!"); } - let dev_lock = dev.lock().unwrap(); - let block_size = dev_lock.block_size; + SCSI_DEVICE!(dev, locked_dev, scsi_dev); + let block_size = scsi_dev.block_size; let mut outbuf: Vec = vec![0; 8]; - let mut nb_sectors = cmp::min(dev_lock.disk_sectors as u32, u32::MAX); + let mut nb_sectors = cmp::min(scsi_dev.disk_sectors as u32, u32::MAX); nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; nb_sectors -= 1; @@ -1150,18 +1169,18 @@ fn scsi_command_emulate_read_capacity_10( fn scsi_command_emulate_mode_sense( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { // disable block descriptors(DBD) bit. let mut dbd: bool = cmd.buf[1] & 0x8 != 0; let page_code = cmd.buf[2] & 0x3f; let page_control = (cmd.buf[2] & 0xc0) >> 6; let mut outbuf: Vec = vec![0]; - let dev_lock = dev.lock().unwrap(); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); let mut dev_specific_parameter: u8 = 0; - let mut nb_sectors = dev_lock.disk_sectors as u32; - let scsi_type = dev_lock.scsi_type; - let block_size = dev_lock.block_size; + let mut nb_sectors = scsi_dev.disk_sectors as u32; + let scsi_type = scsi_dev.scsi_type; + let block_size = scsi_dev.block_size; nb_sectors /= block_size / DEFAULT_SECTOR_SIZE; trace::scsi_emulate_mode_sense( @@ -1175,17 +1194,17 @@ fn scsi_command_emulate_mode_sense( // Device specific paramteter field for direct access block devices: // Bit 7: WP(Write Protect); bit 4: DPOFUA; if scsi_type == SCSI_TYPE_DISK { - if dev_lock.state.features & (1 << SCSI_DISK_F_DPOFUA) != 0 { + if scsi_dev.state.features & (1 << SCSI_DISK_F_DPOFUA) != 0 { dev_specific_parameter = 0x10; } - if dev_lock.drive_cfg.readonly { + if scsi_dev.drive_cfg.readonly { // Readonly. dev_specific_parameter |= 0x80; } } else { dbd = true; } - drop(dev_lock); + drop(locked_dev); if cmd.op == MODE_SENSE { outbuf.resize(4, 0); @@ -1361,12 +1380,12 @@ fn scsi_command_emulate_mode_sense_page( fn scsi_command_emulate_report_luns( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { - let dev_lock = dev.lock().unwrap(); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); // Byte 0-3: Lun List Length. Byte 4-7: Reserved. let mut outbuf: Vec = vec![0; 8]; - let target = dev_lock.dev_cfg.target; + let target = scsi_dev.dev_cfg.target; if cmd.xfer < 16 { bail!("scsi REPORT LUNS xfer {} too short!", cmd.xfer); @@ -1380,27 +1399,24 @@ fn scsi_command_emulate_report_luns( ); } - let scsi_bus = dev_lock.parent_bus.upgrade().unwrap(); - let scsi_bus_clone = scsi_bus.lock().unwrap(); + let bus = scsi_dev.parent_bus().unwrap().upgrade().unwrap(); + SCSI_BUS!(bus, locked_bus, scsi_bus); + drop(locked_dev); - drop(dev_lock); - - for (_pos, device) in scsi_bus_clone.devices.iter() { - let device_lock = device.lock().unwrap(); - if device_lock.dev_cfg.target != target { - drop(device_lock); + for device in scsi_bus.child_devices().values() { + SCSI_DEVICE!(device, locked_dev, scsi_dev); + if scsi_dev.dev_cfg.target != target { continue; } let len = outbuf.len(); - if device_lock.dev_cfg.lun < 256 { + if scsi_dev.dev_cfg.lun < 256 { outbuf.push(0); - outbuf.push(device_lock.dev_cfg.lun as u8); + outbuf.push(scsi_dev.dev_cfg.lun as u8); } else { - outbuf.push(0x40 | ((device_lock.dev_cfg.lun >> 8) & 0xff) as u8); - outbuf.push((device_lock.dev_cfg.lun & 0xff) as u8); + outbuf.push(0x40 | ((scsi_dev.dev_cfg.lun >> 8) & 0xff) as u8); + outbuf.push((scsi_dev.dev_cfg.lun & 0xff) as u8); } outbuf.resize(len + 8, 0); - drop(device_lock); } let len: u32 = outbuf.len() as u32 - 8; @@ -1410,20 +1426,19 @@ fn scsi_command_emulate_report_luns( fn scsi_command_emulate_service_action_in_16( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { // Read Capacity(16) Command. // Byte 0: Operation Code(0x9e) // Byte 1: bit0 - bit4: Service Action(0x10), bit 5 - bit 7: Reserved. if cmd.buf[1] & 0x1f == SUBCODE_READ_CAPACITY_16 { - let dev_lock = dev.lock().unwrap(); - let block_size = dev_lock.block_size; + SCSI_DEVICE!(dev, locked_dev, scsi_dev); + let block_size = scsi_dev.block_size; let mut outbuf: Vec = vec![0; 32]; - let mut nb_sectors = dev_lock.disk_sectors; + let mut nb_sectors = scsi_dev.disk_sectors; nb_sectors /= (block_size / DEFAULT_SECTOR_SIZE) as u64; nb_sectors -= 1; - - drop(dev_lock); + drop(locked_dev); // Byte[0-7]: Returned Logical BLock Address(the logical block address of the last logical // block). @@ -1443,7 +1458,7 @@ fn scsi_command_emulate_service_action_in_16( fn scsi_command_emulate_read_disc_information( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { // Byte1: Bits[0-2]: Data type. // Data Type | Returned Data. | @@ -1457,9 +1472,11 @@ fn scsi_command_emulate_read_disc_information( if data_type != 0 { bail!("Unsupported read disc information data type {}!", data_type); } - if dev.lock().unwrap().scsi_type != SCSI_TYPE_ROM { + SCSI_DEVICE!(dev, locked_dev, scsi_dev); + if scsi_dev.scsi_type != SCSI_TYPE_ROM { bail!("Read disc information command is only for scsi multi-media device!"); } + drop(locked_dev); // Outbuf: // Bytes[0-1]: Disc Information Length(32). @@ -1507,7 +1524,7 @@ const RT_RAW_TOC: u8 = 0x0010; fn scsi_command_emulate_read_toc( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { // Byte1: Bit1: MSF.(MSF: Minute, Second, Frame) // MSF = 1: the address fields in some returned data formats shall be in MSF form. @@ -1521,7 +1538,8 @@ fn scsi_command_emulate_read_toc( match format { RT_FORMATTED_TOC => { - let nb_sectors = dev.lock().unwrap().disk_sectors as u32; + SCSI_DEVICE!(dev, locked_dev, scsi_dev); + let nb_sectors = scsi_dev.disk_sectors as u32; let mut buf = cdrom_read_formatted_toc(nb_sectors, msf, track_number)?; outbuf.append(&mut buf); } @@ -1542,11 +1560,11 @@ fn scsi_command_emulate_read_toc( fn scsi_command_emulate_get_configuration( _cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { - let dev_lock = dev.lock().unwrap(); - if dev_lock.scsi_type != SCSI_TYPE_ROM { - bail!("Invalid scsi type {}", dev_lock.scsi_type); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); + if scsi_dev.scsi_type != SCSI_TYPE_ROM { + bail!("Invalid scsi type {}", scsi_dev.scsi_type); } // 8 bytes(Feature Header) + 12 bytes(Profile List Feature) + @@ -1559,7 +1577,7 @@ fn scsi_command_emulate_get_configuration( // Bytes[4-5]: Reserved. // Bytes[6-7]: Current Profile. BigEndian::write_u32(&mut outbuf[0..4], 36); - let current = if dev_lock.disk_sectors > CD_MAX_SECTORS as u64 { + let current = if scsi_dev.disk_sectors > CD_MAX_SECTORS as u64 { GC_PROFILE_DVD_ROM } else { GC_PROFILE_CD_ROM @@ -1620,14 +1638,14 @@ fn scsi_command_emulate_get_configuration( fn scsi_command_emulate_get_event_status_notification( cmd: &ScsiCommand, - dev: &Arc>, + dev: &Arc>, ) -> Result> { // Byte4: Notification Class Request. let notification_class_request = cmd.buf[4]; - let dev_lock = dev.lock().unwrap(); + SCSI_DEVICE!(dev, locked_dev, scsi_dev); - if dev_lock.scsi_type != SCSI_TYPE_ROM { - bail!("Invalid scsi type {}", dev_lock.scsi_type); + if scsi_dev.scsi_type != SCSI_TYPE_ROM { + bail!("Invalid scsi type {}", scsi_dev.scsi_type); } // Byte1: Bit0: Polled. diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 8aab9a229..3b9e6a6cd 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -11,12 +11,12 @@ // See the Mulan PSL v2 for more details. use std::collections::HashMap; -use std::sync::{Arc, Mutex, Weak}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Result}; use clap::Parser; -use crate::ScsiBus::{aio_complete_cb, ScsiBus, ScsiCompleteCb}; +use crate::ScsiBus::{aio_complete_cb, ScsiCompleteCb}; use crate::{Device, DeviceBase}; use block_backend::{create_block_backend, BlockDriverOps, BlockProperty}; use machine_manager::config::{valid_id, DriveConfig, DriveFile, VmConfig}; @@ -135,6 +135,14 @@ impl Device for ScsiDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base); } +/// Convert from Arc> to &ScsiDevice. +#[macro_export] +macro_rules! SCSI_DEVICE { + ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { + convert_device_ref!($trait_device, $lock_device, $struct_device, ScsiDevice); + }; +} + pub struct ScsiDevice { pub base: DeviceBase, /// Configuration of the scsi device. @@ -155,8 +163,6 @@ pub struct ScsiDevice { pub block_size: u32, /// Scsi device type. pub scsi_type: u32, - /// Scsi Bus attached to. - pub parent_bus: Weak>, /// Drive backend files. drive_files: Arc>>, /// Aio context. @@ -192,7 +198,6 @@ impl ScsiDevice { disk_sectors: 0, block_size: 0, scsi_type, - parent_bus: Weak::new(), drive_files, aio: None, iothread, diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 282169e47..74bb82ab5 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -29,9 +29,10 @@ use super::xhci::xhci_controller::XhciDevice; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; use crate::{ + Bus, ScsiBus::{ - ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD, - SCSI_CMD_BUF_SIZE, + get_scsi_key, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + EMULATE_SCSI_OPS, GOOD, SCSI_CMD_BUF_SIZE, }, ScsiDisk::{ScsiDevConfig, ScsiDevice}, }; @@ -561,8 +562,7 @@ impl UsbDevice for UsbStorage { self.scsi_bus .lock() .unwrap() - .devices - .insert((0, 0), self.scsi_dev.clone()); + .attach_child(get_scsi_key(0, 0), self.scsi_dev.clone())?; let storage: Arc> = Arc::new(Mutex::new(self)); Ok(storage) diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 688c1cca7..c58f7ecf2 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -34,9 +34,10 @@ use super::{ UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; +use crate::Bus; use crate::{ ScsiBus::{ - scsi_cdb_xfer, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + get_scsi_key, scsi_cdb_xfer, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, }, @@ -542,7 +543,7 @@ impl UsbUas { lun, scsi_iovec, scsi_iovec_size, - Arc::clone(&self.scsi_device), + self.scsi_device.clone(), uas_request, ) .with_context(|| "failed to create SCSI request")?; @@ -703,7 +704,7 @@ impl UsbUas { let command = self.commands[stream].as_ref().unwrap(); // SAFETY: IU is guaranteed to be of type command. let cdb = unsafe { &command.body.command.cdb }; - let xfer_len = scsi_cdb_xfer(cdb, Arc::clone(&self.scsi_device)); + let xfer_len = scsi_cdb_xfer(cdb, self.scsi_device.clone()); trace::usb_uas_try_start_next_transfer(self.device_id(), xfer_len); if xfer_len == 0 { @@ -762,14 +763,13 @@ impl UsbDevice for UsbUas { // supported. let mut locked_scsi_device = self.scsi_device.lock().unwrap(); locked_scsi_device.realize()?; - locked_scsi_device.parent_bus = Arc::downgrade(&self.scsi_bus); + locked_scsi_device.base.parent = + Some(Arc::downgrade(&self.scsi_bus) as Weak>); drop(locked_scsi_device); self.scsi_bus .lock() .unwrap() - .devices - .insert((0, 0), Arc::clone(&self.scsi_device)); - + .attach_child(get_scsi_key(0, 0), self.scsi_device.clone())?; let uas = Arc::new(Mutex::new(self)); Ok(uas) } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 1b71e85be..c5ea36481 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -74,6 +74,7 @@ use devices::usb::UsbDevice; use devices::Bus; #[cfg(target_arch = "aarch64")] use devices::InterruptController; +use devices::ScsiBus::get_scsi_key; use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] @@ -1214,13 +1215,9 @@ pub trait MachineOps: MachineLifecycle { let virtio_device = virtio_pcidev.get_virtio_device().lock().unwrap(); let cntlr = virtio_device.as_any().downcast_ref::().unwrap(); let bus = cntlr.bus.as_ref().unwrap(); - if bus - .lock() - .unwrap() - .devices - .contains_key(&(device_cfg.target, device_cfg.lun)) - { - bail!("Wrong! Two scsi devices have the same scsi-id and lun"); + let key = get_scsi_key(device_cfg.target, device_cfg.lun); + if bus.lock().unwrap().child_dev(key).is_some() { + bail!("Wrong! Two scsi devices have the same scsi-id and lun!"); } let iothread = cntlr.config.iothread.clone(); @@ -1231,11 +1228,8 @@ pub trait MachineOps: MachineLifecycle { iothread, ))); device.lock().unwrap().realize()?; - bus.lock() - .unwrap() - .devices - .insert((device_cfg.target, device_cfg.lun), device.clone()); - device.lock().unwrap().parent_bus = Arc::downgrade(bus); + bus.lock().unwrap().attach_child(key, device.clone())?; + device.lock().unwrap().base.parent = Some(Arc::downgrade(bus) as Weak>); if let Some(bootindex) = device_cfg.bootindex { // Eg: OpenFirmware device path(virtio-scsi disk): diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index fa31c9b9a..3970bf64a 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -33,6 +33,8 @@ use devices::ScsiBus::{ ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, SCSI_CMD_BUF_SIZE, SCSI_SENSE_INVALID_OPCODE, }; +use devices::ScsiDisk::ScsiDevice; +use devices::{convert_device_ref, Bus, SCSI_DEVICE}; use machine_manager::config::{ get_pci_df, parse_bool, valid_block_device_virtqueue_size, valid_id, MAX_VIRTIO_QUEUE, }; @@ -296,11 +298,11 @@ impl VirtioDevice for ScsiCntlr { // Register event notifier for device aio. let bus = self.bus.as_ref().unwrap(); let locked_bus = bus.lock().unwrap(); - for device in locked_bus.devices.values() { - let locked_device = device.lock().unwrap(); + for device in locked_bus.child_devices().values() { + SCSI_DEVICE!(device, locked_dev, scsi_dev); let err_cb = self.gen_error_cb(interrupt_cb.clone()); // SAFETY: the disk_image is assigned after device realized. - let disk_image = locked_device.block_backend.as_ref().unwrap(); + let disk_image = scsi_dev.block_backend.as_ref().unwrap(); let mut locked_backend = disk_image.lock().unwrap(); locked_backend.register_io_event(self.base.broken.clone(), err_cb)?; } @@ -314,10 +316,10 @@ impl VirtioDevice for ScsiCntlr { )?; let bus = self.bus.as_ref().unwrap(); let locked_bus = bus.lock().unwrap(); - for device in locked_bus.devices.values() { - let locked_dev = device.lock().unwrap(); + for device in locked_bus.child_devices().values() { + SCSI_DEVICE!(device, locked_dev, scsi_dev); // SAFETY: the disk_image is assigned after device realized. - let disk_image = locked_dev.block_backend.as_ref().unwrap(); + let disk_image = scsi_dev.block_backend.as_ref().unwrap(); let mut locked_backend = disk_image.lock().unwrap(); locked_backend.unregister_io_event()?; } -- Gitee From e89e0bea6633c6c57e476fad3a80309ee0c3c303 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 21 Jun 2024 08:38:02 +0800 Subject: [PATCH 1946/2187] devices/sysbus: sysbus delete devices field `devices` field of struct `SysBus` has the same effect as the `children` field of struct `BusBase`. Since the devices mounted on sysbus do not need to describe their addresses, we will incrementally fill in a useless number in the hashmap. Delete useless `devices` field. Signed-off-by: liuxiangdong --- devices/src/legacy/pflash.rs | 2 +- devices/src/lib.rs | 5 ++++ devices/src/sysbus/mod.rs | 49 +++++++++++++++++++++++---------- machine/src/aarch64/fdt.rs | 21 +++++++------- machine/src/aarch64/standard.rs | 14 +++++----- machine/src/lib.rs | 22 ++++++++------- 6 files changed, 71 insertions(+), 42 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index db9929a3f..c12fb251d 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -244,7 +244,7 @@ impl PFlash { .root() .add_subregion(rom_region, region_base) .with_context(|| "Failed to attach PFlash to system bus")?; - sysbus.lock().unwrap().devices.push(dev.clone()); + sysbus.lock().unwrap().sysbus_attach_child(dev.clone())?; Ok(dev) } diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 33f9af560..1cb361b8a 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -40,6 +40,7 @@ pub use scsi::bus as ScsiBus; pub use scsi::disk as ScsiDisk; use std::any::Any; +use std::any::TypeId; use std::collections::BTreeMap; use std::sync::{Arc, Mutex, Weak}; @@ -81,6 +82,10 @@ pub trait Device: Any + AsAny + Send + Sync { self.as_any_mut() } + fn device_type_id(&self) -> TypeId { + self.type_id() + } + /// Get device name. fn name(&self) -> String { self.device_base().id.clone() diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 05479a3ce..76a190e07 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -55,10 +55,11 @@ pub const IRQ_MAX: i32 = 191; pub struct SysBus { pub base: BusBase, + // Record the largest key used in the BTreemap of the busbase(children field). + max_key: u64, #[cfg(target_arch = "x86_64")] pub sys_io: Arc, pub sys_mem: Arc, - pub devices: Vec>>, pub free_irqs: (i32, i32), pub min_free_irq: i32, pub mmio_region: (u64, u64), @@ -92,10 +93,10 @@ impl SysBus { ) -> Self { Self { base: BusBase::new("sysbus".to_string()), + max_key: 0, #[cfg(target_arch = "x86_64")] sys_io: sys_io.clone(), sys_mem: sys_mem.clone(), - devices: Vec::new(), free_irqs, min_free_irq: free_irqs.0, mmio_region, @@ -155,7 +156,17 @@ impl SysBus { } } - self.devices.push(dev.clone()); + self.sysbus_attach_child(dev.clone())?; + Ok(()) + } + + pub fn sysbus_attach_child(&mut self, dev: Arc>) -> Result<()> { + self.attach_child(self.max_key, dev.clone())?; + // Note: Incrementally generate a number that has no substantive effect, and is only used for the + // key of Btreemap in the busbase(children field). + // The number of system-bus devices is limited, and it is also difficult to reach the `u64` range for + // hot-plug times. So, `u64` is currently sufficient for using and don't consider overflow issues for now. + self.max_key += 1; Ok(()) } } @@ -326,26 +337,37 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder { } } +/// Convert from Arc> to &mut dyn SysBusDevOps. +#[macro_export] +macro_rules! SYS_BUS_DEVICE { + ($trait_device:expr, $lock_device: ident, $trait_sysbusdevops: ident) => { + let mut $lock_device = $trait_device.lock().unwrap(); + let $trait_sysbusdevops = to_sysbusdevops(&mut *$lock_device).unwrap(); + }; +} + impl AmlBuilder for SysBus { fn aml_bytes(&self) -> Vec { let mut scope = AmlScope::new("_SB"); - self.devices.iter().for_each(|dev| { - scope.append(&dev.lock().unwrap().aml_bytes()); - }); + let child_devices = self.base.children.clone(); + for dev in child_devices.values() { + SYS_BUS_DEVICE!(dev, locked_dev, sysbusdev); + scope.append(&sysbusdev.aml_bytes()); + } scope.aml_bytes() } } -pub type ToSysBusDevOpsFunc = fn(&dyn Any) -> &dyn SysBusDevOps; +pub type ToSysBusDevOpsFunc = fn(&mut dyn Any) -> &mut dyn SysBusDevOps; static mut SYSBUSDEVTYPE_HASHMAP: Option> = None; -pub fn convert_to_sysbusdevops(item: &dyn Any) -> &dyn SysBusDevOps { +pub fn convert_to_sysbusdevops(item: &mut dyn Any) -> &mut dyn SysBusDevOps { // SAFETY: The typeid of `T` is the typeid recorded in the hashmap. The target structure type of // the conversion is its own structure type, so the conversion result will definitely not be `None`. - let t = item.downcast_ref::().unwrap(); - t as &dyn SysBusDevOps + let t = item.downcast_mut::().unwrap(); + t as &mut dyn SysBusDevOps } pub fn register_sysbusdevops_type() -> Result<()> { @@ -388,13 +410,12 @@ pub fn devices_register_sysbusdevops_type() -> Result<()> { register_sysbusdevops_type::() } -pub fn to_sysbusdevops(dev: &dyn Device) -> Option<&dyn SysBusDevOps> { - let type_id = dev.type_id(); +pub fn to_sysbusdevops(dev: &mut dyn Device) -> Option<&mut dyn SysBusDevOps> { // SAFETY: SYSBUSDEVTYPE_HASHMAP has been built. And this function is called without changing hashmap. unsafe { let types = SYSBUSDEVTYPE_HASHMAP.as_mut().unwrap(); - let func = types.get(&type_id)?; - let sysbusdev = func(dev.as_any()); + let func = types.get(&dev.device_type_id())?; + let sysbusdev = func(dev.as_any_mut()); Some(sysbusdev) } } diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index c08822c35..7bf0c2c92 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -14,7 +14,8 @@ use anyhow::Result; use crate::MachineBase; use cpu::PMU_INTR; -use devices::sysbus::{SysBusDevType, SysRes}; +use devices::sysbus::{to_sysbusdevops, SysBusDevType, SysRes}; +use devices::{Bus, SYS_BUS_DEVICE}; use util::device_tree::{self, FdtBuilder}; /// Function that helps to generate arm pmu in device-tree. @@ -265,24 +266,24 @@ impl CompileFDTHelper for MachineBase { fdt.set_property_string("method", "hvc")?; fdt.end_node(psci_node_dep)?; - let devices = self.sysbus.lock().unwrap().devices.clone(); - for dev in devices.iter() { - let locked_dev = dev.lock().unwrap(); - match locked_dev.sysbusdev_base().dev_type { + let devices = self.sysbus.lock().unwrap().child_devices(); + for dev in devices.values() { + SYS_BUS_DEVICE!(dev, locked_dev, sysbusdev); + match sysbusdev.sysbusdev_base().dev_type { SysBusDevType::PL011 => { - generate_serial_device_node(fdt, &locked_dev.sysbusdev_base().res)? + generate_serial_device_node(fdt, &sysbusdev.sysbusdev_base().res)? } SysBusDevType::Rtc => { - generate_rtc_device_node(fdt, &locked_dev.sysbusdev_base().res)? + generate_rtc_device_node(fdt, &sysbusdev.sysbusdev_base().res)? } SysBusDevType::VirtioMmio => { - generate_virtio_devices_node(fdt, &locked_dev.sysbusdev_base().res)? + generate_virtio_devices_node(fdt, &sysbusdev.sysbusdev_base().res)? } SysBusDevType::FwCfg => { - generate_fwcfg_device_node(fdt, &locked_dev.sysbusdev_base().res)?; + generate_fwcfg_device_node(fdt, &sysbusdev.sysbusdev_base().res)?; } SysBusDevType::Flash => { - generate_flash_device_node(fdt, &locked_dev.sysbusdev_base().res)?; + generate_flash_device_node(fdt, &sysbusdev.sysbusdev_base().res)?; } _ => (), } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 5eefe28d1..76b3b1405 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -49,8 +49,8 @@ use devices::legacy::{ #[cfg(feature = "ramfb")] use devices::legacy::{Ramfb, RamfbConfig}; use devices::pci::{PciDevOps, PciHost, PciIntxState}; -use devices::sysbus::SysBusDevType; -use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_MAX}; +use devices::sysbus::{to_sysbusdevops, SysBusDevType}; +use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_MAX, SYS_BUS_DEVICE}; use hypervisor::kvm::aarch64::*; use hypervisor::kvm::*; #[cfg(feature = "ramfb")] @@ -953,11 +953,11 @@ impl AcpiBuilder for StdMachine { spcr.set_field(52, 1_u8 << 3); // Irq number used by the UART let mut uart_irq: u32 = 0; - let devices = self.base.sysbus.lock().unwrap().devices.clone(); - for dev in devices.iter() { - let locked_dev = dev.lock().unwrap(); - if locked_dev.sysbusdev_base().dev_type == SysBusDevType::PL011 { - uart_irq = locked_dev.sysbusdev_base().irq_state.irq as _; + let devices = self.get_sysbus_devices(); + for dev in devices.values() { + SYS_BUS_DEVICE!(dev, locked_dev, sysbusdev); + if sysbusdev.sysbusdev_base().dev_type == SysBusDevType::PL011 { + uart_irq = sysbusdev.sysbusdev_base().irq_state.irq as _; break; } } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c5ea36481..8ab05262f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -60,7 +60,9 @@ use devices::pci::{ }; use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; -use devices::sysbus::{devices_register_sysbusdevops_type, SysBus, SysBusDevOps, SysBusDevType}; +use devices::sysbus::{ + devices_register_sysbusdevops_type, to_sysbusdevops, SysBus, SysBusDevOps, SysBusDevType, +}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; @@ -71,11 +73,11 @@ use devices::usb::uas::{UsbUas, UsbUasConfig}; use devices::usb::usbhost::{UsbHost, UsbHostConfig}; use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice}; use devices::usb::UsbDevice; -use devices::Bus; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use devices::ScsiBus::get_scsi_key; use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; +use devices::{Bus, Device, SYS_BUS_DEVICE}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; @@ -778,9 +780,9 @@ pub trait MachineOps: MachineLifecycle { let mut virtio_device = None; if serial_cfg.bus.is_none() { // Micro_vm. - for dev in self.get_sysbus_devices().iter() { - let locked_busdev = dev.lock().unwrap(); - if locked_busdev.sysbusdev_base().dev_type == SysBusDevType::VirtioMmio { + for dev in self.get_sysbus_devices().values() { + SYS_BUS_DEVICE!(dev, locked_busdev, sysbusdev); + if sysbusdev.sysbusdev_base().dev_type == SysBusDevType::VirtioMmio { let virtio_mmio_dev = locked_busdev .as_any() .downcast_ref::() @@ -940,8 +942,8 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } - fn get_sysbus_devices(&mut self) -> Vec>> { - self.machine_base().sysbus.lock().unwrap().devices.clone() + fn get_sysbus_devices(&self) -> BTreeMap>> { + self.machine_base().sysbus.lock().unwrap().child_devices() } fn get_fwcfg_dev(&mut self) -> Option>> { @@ -953,9 +955,9 @@ pub trait MachineOps: MachineLifecycle { } fn reset_all_devices(&mut self) -> Result<()> { - for dev in self.get_sysbus_devices().iter() { - dev.lock() - .unwrap() + for dev in self.get_sysbus_devices().values() { + SYS_BUS_DEVICE!(dev, locked_dev, sysbusdev); + sysbusdev .reset() .with_context(|| "Fail to reset sysbus device")?; } -- Gitee From d520adce956badc97c7b0ade860da075a05589f5 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 9 Dec 2023 16:19:46 +0800 Subject: [PATCH 1947/2187] devices/pci: pcibus delete parent_bridge field `parent_bridge` field of struct `PciBus` is only used for `RootPort` class, and `RootPort` is parent device of PciBus. We can get parent device from new BUS-DEVICE framework. Delete this field and use new framework to implement these functions. Signed-off-by: liuxiangdong --- devices/src/pci/bus.rs | 31 ++++++++++++------------------- devices/src/pci/intx.rs | 10 ++++++---- devices/src/pci/mod.rs | 15 ++++++--------- devices/src/pci/root_port.rs | 20 ++++++++++++++++++-- machine/src/lib.rs | 3 +-- 5 files changed, 43 insertions(+), 36 deletions(-) diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index d87d5d852..3490fe6d1 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -22,7 +22,10 @@ use super::{ hotplug::HotplugOps, PciDevOps, PciIntxState, }; -use crate::{Bus, BusBase, MsiIrqManager}; +use crate::pci::RootPort; +use crate::{ + convert_device_mut, convert_device_ref, Bus, BusBase, MsiIrqManager, MUT_ROOT_PORT, ROOT_PORT, +}; use address_space::Region; use util::gen_base_func; @@ -35,8 +38,6 @@ pub struct PciBus { pub devices: HashMap>>, /// Child buses of the bus. pub child_buses: Vec>>, - /// Pci bridge which the bus originates from. - pub parent_bridge: Option>>, /// IO region which the parent bridge manages. #[cfg(target_arch = "x86_64")] pub io_region: Region, @@ -70,7 +71,6 @@ impl PciBus { base: BusBase::new(name), devices: HashMap::new(), child_buses: Vec::new(), - parent_bridge: None, #[cfg(target_arch = "x86_64")] io_region, mem_region, @@ -236,18 +236,11 @@ impl PciBus { } fn get_bridge_control_reg(&self, offset: usize, data: &mut [u8]) { - if self.parent_bridge.is_none() { - return; + if let Some(parent_bridge) = self.parent_device() { + let bridge = parent_bridge.upgrade().unwrap(); + MUT_ROOT_PORT!(bridge, locked_bridge, rootport); + rootport.read_config(offset, data); } - - self.parent_bridge - .as_ref() - .unwrap() - .upgrade() - .unwrap() - .lock() - .unwrap() - .read_config(offset, data); } pub fn generate_dev_id(&self, devfn: u8) -> u16 { @@ -260,11 +253,11 @@ impl PciBus { } pub fn get_msi_irq_manager(&self) -> Option> { - match &self.parent_bridge { + match self.parent_device().as_ref() { Some(parent_bridge) => { - let parent_bridge = parent_bridge.upgrade().unwrap(); - let locked_parent_bridge = parent_bridge.lock().unwrap(); - locked_parent_bridge.get_msi_irq_manager() + let bridge = parent_bridge.upgrade().unwrap(); + ROOT_PORT!(bridge, locked_bridge, rootport); + rootport.get_msi_irq_manager() } None => self.msi_irq_manager.clone(), } diff --git a/devices/src/pci/intx.rs b/devices/src/pci/intx.rs index b0624fe02..ca1418507 100644 --- a/devices/src/pci/intx.rs +++ b/devices/src/pci/intx.rs @@ -15,8 +15,10 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use log::error; +use super::{PciDevOps, RootPort}; use crate::interrupt_controller::LineIrqManager; use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_PIN_NUM}; +use crate::{convert_device_ref, Bus, ROOT_PORT}; pub type InterruptHandler = Box Result<()> + Send + Sync>; @@ -133,13 +135,13 @@ pub fn init_intx( let locked_pci_bus = pci_bus.lock().unwrap(); let pin = config.config[INTERRUPT_PIN as usize] - 1; - let (irq, intx_state) = match &locked_pci_bus.parent_bridge { + let (irq, intx_state) = match &locked_pci_bus.parent_device() { Some(parent_bridge) => { let parent_bridge = parent_bridge.upgrade().unwrap(); - let locked_parent_bridge = parent_bridge.lock().unwrap(); + ROOT_PORT!(parent_bridge, locked_bridge, bridge); ( - swizzle_map_irq(locked_parent_bridge.pci_base().devfn, pin), - locked_parent_bridge.get_intx_state(), + swizzle_map_irq(bridge.pci_base().devfn, pin), + bridge.get_intx_state(), ) } None => { diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index b3168e7a2..97b9126c7 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -44,7 +44,7 @@ use crate::misc::ivshmem::Ivshmem; use crate::misc::pvpanic::PvPanicPci; use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; use crate::usb::xhci::xhci_pci::XhciPciDevice; -use crate::{Bus, Device, DeviceBase, MsiIrqManager}; +use crate::{convert_device_ref, Bus, Device, DeviceBase, MsiIrqManager, ROOT_PORT}; #[cfg(feature = "demo_device")] use demo_device::DemoDev; @@ -227,16 +227,13 @@ pub trait PciDevOps: Device + Send { // This else branch will not be executed currently, // which is mainly to be compatible with new PCI bridge devices. // unwrap is safe because pci bus under root port will not return null. - locked_parent_bus - .parent_bridge - .as_ref() + let parent_bridge = locked_parent_bus + .parent_device() .unwrap() .upgrade() - .unwrap() - .lock() - .unwrap() - .get_dev_path() - .unwrap() + .unwrap(); + ROOT_PORT!(parent_bridge, locked_bridge, rootport); + rootport.get_dev_path().unwrap() }; parent_dev_path } diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index b63490580..21f252ef0 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -347,6 +347,22 @@ impl Device for RootPort { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } +/// Convert from Arc> to &mut RootPort. +#[macro_export] +macro_rules! MUT_ROOT_PORT { + ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { + convert_device_mut!($trait_device, $lock_device, $struct_device, RootPort); + }; +} + +/// Convert from Arc> to &RootPort. +#[macro_export] +macro_rules! ROOT_PORT { + ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { + convert_device_ref!($trait_device, $lock_device, $struct_device, RootPort); + }; +} + impl PciDevOps for RootPort { gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); @@ -404,8 +420,8 @@ impl PciDevOps for RootPort { let root_port = Arc::new(Mutex::new(self)); #[allow(unused_mut)] let mut locked_root_port = root_port.lock().unwrap(); - locked_root_port.sec_bus.lock().unwrap().parent_bridge = - Some(Arc::downgrade(&root_port) as Weak>); + locked_root_port.sec_bus.lock().unwrap().base.parent = + Some(Arc::downgrade(&root_port) as Weak>); locked_root_port.sec_bus.lock().unwrap().hotplug_controller = Some(Arc::downgrade(&root_port) as Weak>); let pci_device = locked_parent_bus.devices.get(&locked_root_port.base.devfn); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8ab05262f..c58b03f48 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1513,8 +1513,7 @@ pub trait MachineOps: MachineLifecycle { return Ok(()); } let parent_bridge = locked_bus - .parent_bridge - .as_ref() + .parent_device() .with_context(|| format!("Parent bridge does not exist, dev id {}", dev_id))?; let dev = parent_bridge.upgrade().unwrap(); let locked_dev = dev.lock().unwrap(); -- Gitee From 02a6a9a149119c51780086f965e0cc9666d7ab01 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 9 Dec 2023 18:32:24 +0800 Subject: [PATCH 1948/2187] devices/pci: pcidevbase deletes parent_bus field `parent_bus` field of struct `PciDevBase` has the same effect as the `parent_bus` field of struct `DeviceBase`. Delete it. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 31 ++++++++-------- devices/src/misc/pvpanic.rs | 37 +++++++++---------- devices/src/pci/bus.rs | 16 +++++++++ devices/src/pci/config.rs | 11 +++--- devices/src/pci/demo_device/mod.rs | 19 +++++----- devices/src/pci/intx.rs | 14 ++++---- devices/src/pci/mod.rs | 40 +++++++++------------ devices/src/pci/msix.rs | 34 +++++++++--------- devices/src/pci/root_port.rs | 49 +++++++++++-------------- devices/src/usb/xhci/xhci_pci.rs | 26 +++++++------- machine/src/aarch64/pci_host_root.rs | 25 ++++++------- machine/src/x86_64/ich9_lpc.rs | 18 +++------- machine/src/x86_64/mch.rs | 42 ++++++---------------- vfio/src/vfio_pci.rs | 53 ++++++++++++++-------------- virtio/src/transport/virtio_pci.rs | 52 +++++++++++++-------------- 15 files changed, 214 insertions(+), 253 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index acb9cc1ae..08212c441 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -17,14 +17,12 @@ use std::sync::{ use anyhow::{bail, Result}; -use crate::pci::{ - config::{ - PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, - PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, - }, - le_write_u16, PciBus, PciDevBase, PciDevOps, +use crate::pci::config::{ + PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, + PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use crate::{Device, DeviceBase}; +use crate::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; +use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; use util::gen_base_func; @@ -52,10 +50,9 @@ impl Ivshmem { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(name, false, Some(parent_bus.clone())), + base: DeviceBase::new(name, false, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), devfn, - parent_bus, }, dev_id: Arc::new(AtomicU16::new(0)), ram_mem_region, @@ -123,16 +120,16 @@ impl PciDevOps for Ivshmem { self.register_bars()?; // Attach to the PCI bus. - let pci_bus = self.base.parent_bus.upgrade().unwrap(); - let mut locked_pci_bus = pci_bus.lock().unwrap(); - let pci_device = locked_pci_bus.devices.get(&self.base.devfn); + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(bus, locked_bus, pci_bus); + let pci_device = pci_bus.devices.get(&self.base.devfn); match pci_device { Some(device) => bail!( "Devfn {:?} has been used by {:?}", &self.base.devfn, device.lock().unwrap().name() ), - None => locked_pci_bus + None => pci_bus .devices .insert(self.base.devfn, Arc::new(Mutex::new(self))), }; @@ -140,16 +137,16 @@ impl PciDevOps for Ivshmem { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(parent_bus, locked_bus, pci_bus); self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), + Some(&pci_bus.io_region), + Some(&pci_bus.mem_region), ); } } diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 2e2444ea0..b1d483b3f 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -20,16 +20,14 @@ use clap::Parser; use log::{debug, error, info}; use serde::{Deserialize, Serialize}; -use crate::pci::{ - config::{ - PciConfig, RegionType, CLASS_PI, DEVICE_ID, HEADER_TYPE, PCI_CLASS_SYSTEM_OTHER, - PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_SUBDEVICE_ID_QEMU, - PCI_VENDOR_ID_REDHAT, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUBSYSTEM_ID, - SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, - }, - le_write_u16, PciBus, PciDevBase, PciDevOps, +use crate::pci::config::{ + PciConfig, RegionType, CLASS_PI, DEVICE_ID, HEADER_TYPE, PCI_CLASS_SYSTEM_OTHER, + PCI_CONFIG_SPACE_SIZE, PCI_DEVICE_ID_REDHAT_PVPANIC, PCI_SUBDEVICE_ID_QEMU, + PCI_VENDOR_ID_REDHAT, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUBSYSTEM_ID, + SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use crate::{Device, DeviceBase}; +use crate::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; +use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; use util::gen_base_func; @@ -112,10 +110,9 @@ impl PvPanicPci { pub fn new(config: &PvpanicDevConfig, devfn: u8, parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(config.id.clone(), false, Some(parent_bus.clone())), + base: DeviceBase::new(config.id.clone(), false, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, - parent_bus, }, dev_id: AtomicU16::new(0), pvpanic: Arc::new(PvPanicState::new(config.supported_features)), @@ -218,16 +215,16 @@ impl PciDevOps for PvPanicPci { // Attach to the PCI bus. let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); - let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); - let mut locked_pci_bus = pci_bus.lock().unwrap(); - let device_id = locked_pci_bus.generate_dev_id(devfn); + let bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(bus, locked_bus, pci_bus); + let device_id = pci_bus.generate_dev_id(devfn); dev.lock() .unwrap() .dev_id .store(device_id, Ordering::Release); - let pci_device = locked_pci_bus.devices.get(&devfn); + let pci_device = pci_bus.devices.get(&devfn); if pci_device.is_none() { - locked_pci_bus.devices.insert(devfn, dev); + pci_bus.devices.insert(devfn, dev); } else { bail!( "pvpanic: Devfn {:?} has been used by {:?}", @@ -244,16 +241,16 @@ impl PciDevOps for PvPanicPci { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(parent_bus, locked_bus, pci_bus); self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), + Some(&pci_bus.io_region), + Some(&pci_bus.mem_region), ); } } diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 3490fe6d1..631f1be54 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -54,6 +54,22 @@ impl Bus for PciBus { gen_base_func!(bus_base, bus_base_mut, BusBase, base); } +/// Convert from Arc> to &mut PciBus. +#[macro_export] +macro_rules! MUT_PCI_BUS { + ($trait_bus:expr, $lock_bus: ident, $struct_bus: ident) => { + convert_bus_mut!($trait_bus, $lock_bus, $struct_bus, PciBus); + }; +} + +/// Convert from Arc> to &PciBus. +#[macro_export] +macro_rules! PCI_BUS { + ($trait_bus:expr, $lock_bus: ident, $struct_bus: ident) => { + convert_bus_ref!($trait_bus, $lock_bus, $struct_bus, PciBus); + }; +} + impl PciBus { /// Create new bus entity. /// diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 16c9b6a53..69976cacb 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -22,6 +22,7 @@ use crate::pci::{ le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, pci_ext_cap_next, PciBus, PciError, BDF_FUNC_SHIFT, }; +use crate::{convert_bus_ref, Bus, PCI_BUS}; use address_space::Region; use util::num_ops::ranges_overlap; @@ -804,8 +805,8 @@ impl PciConfig { /// # Arguments /// /// * `bus` - The bus which region registered. - pub fn unregister_bars(&mut self, bus: &Arc>) -> Result<()> { - let locked_bus = bus.lock().unwrap(); + pub fn unregister_bars(&mut self, bus: &Arc>) -> Result<()> { + PCI_BUS!(bus, locked_bus, pci_bus); for bar in self.bars.iter_mut() { if bar.address == BAR_SPACE_UNMAPPED || bar.size == 0 { continue; @@ -815,7 +816,7 @@ impl PciConfig { { #[cfg(target_arch = "x86_64")] if let Some(region) = bar.region.as_ref() { - locked_bus + pci_bus .io_region .delete_subregion(region) .with_context(|| "Failed to unregister io bar")?; @@ -823,7 +824,7 @@ impl PciConfig { } _ => { if let Some(region) = bar.region.as_ref() { - locked_bus + pci_bus .mem_region .delete_subregion(region) .with_context(|| "Failed to unregister mem bar")?; @@ -1510,7 +1511,7 @@ mod tests { #[cfg(target_arch = "x86_64")] io_region.clone(), mem_region.clone(), - ))); + ))) as Arc>; assert!(pci_config.unregister_bars(&bus).is_ok()); diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 52e1381d5..37dc6b73f 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -56,7 +56,7 @@ use crate::pci::{ init_msix, le_write_u16, PciBus, PciDevOps, }; use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; use util::gen_base_func; @@ -109,10 +109,9 @@ impl DemoDev { }; DemoDev { base: PciDevBase { - base: DeviceBase::new(cfg.id.clone(), false, Some(parent_bus.clone())), + base: DeviceBase::new(cfg.id.clone(), false, Some(parent_bus)), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), devfn, - parent_bus, }, cmd_cfg: cfg, mem_region: Region::init_container_region(u32::MAX as u64, "DemoDev"), @@ -135,14 +134,14 @@ impl DemoDev { } fn attach_to_parent_bus(self) -> Result<()> { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let mut locked_parent_bus = parent_bus.lock().unwrap(); - if locked_parent_bus.devices.get(&self.base.devfn).is_some() { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); + if pci_bus.devices.get(&self.base.devfn).is_some() { bail!("device already existed"); } let devfn = self.base.devfn; let demo_pci_dev = Arc::new(Mutex::new(self)); - locked_parent_bus.devices.insert(devfn, demo_pci_dev); + pci_bus.devices.insert(devfn, demo_pci_dev); Ok(()) } @@ -223,8 +222,8 @@ impl PciDevOps for DemoDev { /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let parent_bus_locked = parent_bus.lock().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(parent_bus, locked_bus, pci_bus); self.base.config.write( offset, @@ -232,7 +231,7 @@ impl PciDevOps for DemoDev { self.dev_id.load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] None, - Some(&parent_bus_locked.mem_region), + Some(&pci_bus.mem_region), ); } diff --git a/devices/src/pci/intx.rs b/devices/src/pci/intx.rs index ca1418507..9b62e0bda 100644 --- a/devices/src/pci/intx.rs +++ b/devices/src/pci/intx.rs @@ -18,7 +18,7 @@ use log::error; use super::{PciDevOps, RootPort}; use crate::interrupt_controller::LineIrqManager; use crate::pci::{swizzle_map_irq, PciBus, PciConfig, INTERRUPT_PIN, PCI_PIN_NUM}; -use crate::{convert_device_ref, Bus, ROOT_PORT}; +use crate::{convert_bus_ref, convert_device_ref, Bus, PCI_BUS, ROOT_PORT}; pub type InterruptHandler = Box Result<()> + Send + Sync>; @@ -121,7 +121,7 @@ impl Intx { pub fn init_intx( name: String, config: &mut PciConfig, - parent_bus: Weak>, + parent_bus: Weak>, devfn: u8, ) -> Result<()> { if config.config[INTERRUPT_PIN as usize] == 0 { @@ -131,11 +131,11 @@ pub fn init_intx( return Ok(()); } - let (irq, intx_state) = if let Some(pci_bus) = parent_bus.upgrade() { - let locked_pci_bus = pci_bus.lock().unwrap(); + let (irq, intx_state) = if let Some(bus) = parent_bus.upgrade() { + PCI_BUS!(bus, locked_bus, pci_bus); let pin = config.config[INTERRUPT_PIN as usize] - 1; - let (irq, intx_state) = match &locked_pci_bus.parent_device() { + let (irq, intx_state) = match &pci_bus.parent_device() { Some(parent_bridge) => { let parent_bridge = parent_bridge.upgrade().unwrap(); ROOT_PORT!(parent_bridge, locked_bridge, bridge); @@ -145,10 +145,10 @@ pub fn init_intx( ) } None => { - if locked_pci_bus.intx_state.is_some() { + if pci_bus.intx_state.is_some() { ( swizzle_map_irq(devfn, pin), - Some(locked_pci_bus.intx_state.as_ref().unwrap().clone()), + Some(pci_bus.intx_state.as_ref().unwrap().clone()), ) } else { (std::u32::MAX, None) diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 97b9126c7..e3da00e35 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -44,7 +44,9 @@ use crate::misc::ivshmem::Ivshmem; use crate::misc::pvpanic::PvPanicPci; use crate::pci::config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC, MAX_FUNC}; use crate::usb::xhci::xhci_pci::XhciPciDevice; -use crate::{convert_device_ref, Bus, Device, DeviceBase, MsiIrqManager, ROOT_PORT}; +use crate::{ + convert_bus_ref, convert_device_ref, Bus, Device, DeviceBase, MsiIrqManager, PCI_BUS, ROOT_PORT, +}; #[cfg(feature = "demo_device")] use demo_device::DemoDev; @@ -141,8 +143,6 @@ pub struct PciDevBase { pub config: PciConfig, /// Devfn. pub devfn: u8, - /// Primary Bus. - pub parent_bus: Weak>, } pub trait PciDevOps: Device + Send { @@ -219,19 +219,15 @@ pub trait PciDevOps: Device + Send { } /// Get the path of the PCI bus where the device resides. - fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { - let locked_parent_bus = parent_bus.lock().unwrap(); - let parent_dev_path = if locked_parent_bus.name().eq("pcie.0") { + fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { + PCI_BUS!(parent_bus, locked_bus, pci_bus); + let parent_dev_path = if pci_bus.name().eq("pcie.0") { String::from("/pci@ffffffffffffffff") } else { // This else branch will not be executed currently, // which is mainly to be compatible with new PCI bridge devices. // unwrap is safe because pci bus under root port will not return null. - let parent_bridge = locked_parent_bus - .parent_device() - .unwrap() - .upgrade() - .unwrap(); + let parent_bridge = pci_bus.parent_device().unwrap().upgrade().unwrap(); ROOT_PORT!(parent_bridge, locked_bridge, rootport); rootport.get_dev_path().unwrap() }; @@ -333,7 +329,7 @@ pub fn init_multifunction( multifunction: bool, config: &mut [u8], devfn: u8, - parent_bus: Weak>, + parent_bus: Weak>, ) -> Result<()> { let mut header_type = le_read_u16(config, HEADER_TYPE as usize)? & (!HEADER_TYPE_MULTIFUNC as u16); @@ -348,9 +344,9 @@ pub fn init_multifunction( // leave the bit to 0. let slot = pci_slot(devfn); let bus = parent_bus.upgrade().unwrap(); - let locked_bus = bus.lock().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); if pci_func(devfn) != 0 { - let pci_dev = locked_bus.devices.get(&pci_devfn(slot, 0)); + let pci_dev = pci_bus.devices.get(&pci_devfn(slot, 0)); if pci_dev.is_none() { return Ok(()); } @@ -379,7 +375,7 @@ pub fn init_multifunction( // If function 0 is set to single function, the rest function should be None. for func in 1..MAX_FUNC { - if locked_bus.devices.get(&pci_devfn(slot, func)).is_some() { + if pci_bus.devices.get(&pci_devfn(slot, func)).is_some() { bail!( "PCI: {}.0 indicates single function, but {}.{} is already populated", slot, @@ -402,7 +398,7 @@ pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { mod tests { use super::*; use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; - use crate::DeviceBase; + use crate::{convert_bus_mut, DeviceBase, MUT_PCI_BUS}; use address_space::{AddressSpace, Region}; use util::gen_base_func; @@ -415,10 +411,9 @@ mod tests { pub fn new(name: &str, devfn: u8, parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(name.to_string(), false, Some(parent_bus.clone())), + base: DeviceBase::new(name.to_string(), false, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn, - parent_bus, }, } } @@ -448,12 +443,9 @@ mod tests { self.init_write_clear_mask(false)?; let dev = Arc::new(Mutex::new(self)); - let parent_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); - parent_bus - .lock() - .unwrap() - .devices - .insert(devfn, dev.clone()); + let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); + pci_bus.devices.insert(devfn, dev.clone()); Ok(()) } diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 3d4034025..e82b7c171 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -21,9 +21,10 @@ use vmm_sys_util::eventfd::EventFd; use crate::pci::config::{CapId, RegionType, MINIMUM_BAR_SIZE_FOR_MMIO}; use crate::pci::{ - le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, PciDevBase, + le_read_u16, le_read_u32, le_read_u64, le_write_u16, le_write_u32, le_write_u64, PciBus, + PciDevBase, }; -use crate::MsiIrqManager; +use crate::{convert_bus_ref, MsiIrqManager, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; use migration::{ DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, @@ -552,7 +553,7 @@ pub fn init_msix( offset_opt: Option<(u32, u32)>, ) -> Result<()> { let config = &mut pcidev_base.config; - let parent_bus = &pcidev_base.parent_bus; + let parent_bus = pcidev_base.base.parent.as_ref().unwrap(); if vector_nr == 0 || vector_nr > MSIX_TABLE_SIZE_MAX as u32 + 1 { bail!( "invalid msix vectors, which should be in [1, {}]", @@ -586,9 +587,9 @@ pub fn init_msix( offset = msix_cap_offset + MSIX_CAP_PBA as usize; le_write_u32(&mut config.config, offset, pba_offset | bar_id as u32)?; - let msi_irq_manager = if let Some(pci_bus) = parent_bus.upgrade() { - let locked_pci_bus = pci_bus.lock().unwrap(); - locked_pci_bus.get_msi_irq_manager() + let msi_irq_manager = if let Some(bus) = parent_bus.upgrade() { + PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.get_msi_irq_manager() } else { error!("Msi irq controller is none"); None @@ -633,21 +634,20 @@ pub fn init_msix( #[cfg(test)] mod tests { - use std::sync::Weak; - use super::*; - use crate::{ - pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}, - DeviceBase, - }; + use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; + use crate::pci::host::tests::create_pci_host; + use crate::DeviceBase; #[test] fn test_init_msix() { + let pci_host = create_pci_host(); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); let mut base = PciDevBase { - base: DeviceBase::new("msix".to_string(), false, None), + base: DeviceBase::new("msix".to_string(), false, Some(root_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), devfn: 1, - parent_bus: Weak::new(), }; // Too many vectors. assert!(init_msix( @@ -746,11 +746,13 @@ mod tests { #[test] fn test_write_config() { + let pci_host = create_pci_host(); + let locked_pci_host = pci_host.lock().unwrap(); + let root_bus = Arc::downgrade(&locked_pci_host.root_bus); let mut base = PciDevBase { - base: DeviceBase::new("msix".to_string(), false, None), + base: DeviceBase::new("msix".to_string(), false, Some(root_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), devfn: 1, - parent_bus: Weak::new(), }; init_msix(&mut base, 0, 2, Arc::new(AtomicU16::new(0)), None, None).unwrap(); let msix = base.config.msix.as_ref().unwrap(); diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 21f252ef0..cf05c4b6e 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -38,7 +38,9 @@ use crate::pci::{init_multifunction, PciDevBase, PciError, PciIntxState, INTERRU use crate::pci::{ le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, PciDevOps, }; -use crate::{Device, DeviceBase, MsiIrqManager}; +use crate::{ + convert_bus_mut, convert_bus_ref, Device, DeviceBase, MsiIrqManager, MUT_PCI_BUS, PCI_BUS, +}; use address_space::Region; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use machine_manager::qmp::qmp_channel::send_device_deleted_msg; @@ -121,10 +123,9 @@ impl RootPort { Self { base: PciDevBase { - base: DeviceBase::new(cfg.id, true, Some(parent_bus.clone())), + base: DeviceBase::new(cfg.id, true, Some(parent_bus)), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, - parent_bus, }, port_num: cfg.port, sec_bus, @@ -236,16 +237,13 @@ impl RootPort { } fn register_region(&mut self) { + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + let command: u16 = le_read_u16(&self.base.config.config, COMMAND as usize).unwrap(); if command & COMMAND_IO_SPACE != 0 { #[cfg(target_arch = "x86_64")] - if let Err(e) = self - .base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() + if let Err(e) = pci_bus .io_region .add_subregion(self.io_region.clone(), 0) .with_context(|| "Failed to add IO container region.") @@ -254,13 +252,7 @@ impl RootPort { } } if command & COMMAND_MEMORY_SPACE != 0 { - if let Err(e) = self - .base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() + if let Err(e) = pci_bus .mem_region .add_subregion(self.mem_region.clone(), 0) .with_context(|| "Failed to add memory container region.") @@ -367,6 +359,7 @@ impl PciDevOps for RootPort { gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { + let parent_bus = self.parent_bus().unwrap(); self.init_write_mask(true)?; self.init_write_clear_mask(true)?; @@ -382,7 +375,7 @@ impl PciDevOps for RootPort { self.multifunction, config_space, self.base.devfn, - self.base.parent_bus.clone(), + parent_bus.clone(), )?; #[cfg(target_arch = "aarch64")] @@ -400,18 +393,18 @@ impl PciDevOps for RootPort { init_intx( self.name(), &mut self.base.config, - self.base.parent_bus.clone(), + parent_bus.clone(), self.base.devfn, )?; - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let mut locked_parent_bus = parent_bus.lock().unwrap(); + let bus = parent_bus.upgrade().unwrap(); + MUT_PCI_BUS!(bus, locked_bus, pci_bus); #[cfg(target_arch = "x86_64")] - locked_parent_bus + pci_bus .io_region .add_subregion(self.sec_bus.lock().unwrap().io_region.clone(), 0) .with_context(|| "Failed to register subregion in I/O space.")?; - locked_parent_bus + pci_bus .mem_region .add_subregion(self.sec_bus.lock().unwrap().mem_region.clone(), 0) .with_context(|| "Failed to register subregion in memory space.")?; @@ -424,12 +417,10 @@ impl PciDevOps for RootPort { Some(Arc::downgrade(&root_port) as Weak>); locked_root_port.sec_bus.lock().unwrap().hotplug_controller = Some(Arc::downgrade(&root_port) as Weak>); - let pci_device = locked_parent_bus.devices.get(&locked_root_port.base.devfn); + let pci_device = pci_bus.devices.get(&locked_root_port.base.devfn); if pci_device.is_none() { - locked_parent_bus - .child_buses - .push(locked_root_port.sec_bus.clone()); - locked_parent_bus + pci_bus.child_buses.push(locked_root_port.sec_bus.clone()); + pci_bus .devices .insert(locked_root_port.base.devfn, root_port.clone()); } else { @@ -554,7 +545,7 @@ impl PciDevOps for RootPort { } fn get_dev_path(&self) -> Option { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); let parent_dev_path = self.get_parent_dev_path(parent_bus); let dev_path = self.populate_dev_path(parent_dev_path, self.base.devfn, "/pci-bridge@"); Some(dev_path) diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index cdcd50cb7..7b966bd7f 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -34,7 +34,7 @@ use crate::pci::config::{ }; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; use crate::usb::UsbDevice; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::{get_pci_df, valid_id}; use machine_manager::event_loop::register_event_helper; @@ -113,10 +113,9 @@ impl XhciPciDevice { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new(config.id.clone().unwrap(), true, Some(parent_bus.clone())), + base: DeviceBase::new(config.id.clone().unwrap(), true, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), devfn, - parent_bus, }, xhci: XhciDevice::new(mem_space, config), dev_id: Arc::new(AtomicU16::new(0)), @@ -297,10 +296,11 @@ impl PciDevOps for XhciPciDevice { Some((XHCI_MSIX_TABLE_OFFSET, XHCI_MSIX_PBA_OFFSET)), )?; + let parent_bus = self.parent_bus().unwrap(); init_intx( self.name(), &mut self.base.config, - self.base.parent_bus.clone(), + parent_bus, self.base.devfn, )?; @@ -337,11 +337,11 @@ impl PciDevOps for XhciPciDevice { })); let dev = Arc::new(Mutex::new(self)); // Attach to the PCI bus. - let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); - let mut locked_pci_bus = pci_bus.lock().unwrap(); - let pci_device = locked_pci_bus.devices.get(&devfn); + let bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(bus, locked_bus, pci_bus); + let pci_device = pci_bus.devices.get(&devfn); if pci_device.is_none() { - locked_pci_bus.devices.insert(devfn, dev); + pci_bus.devices.insert(devfn, dev); } else { bail!( "Devfn {:?} has been used by {:?}", @@ -358,17 +358,17 @@ impl PciDevOps for XhciPciDevice { } fn write_config(&mut self, offset: usize, data: &[u8]) { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); - locked_parent_bus.update_dev_id(self.base.devfn, &self.dev_id); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(parent_bus, locked_bus, pci_bus); + pci_bus.update_dev_id(self.base.devfn, &self.dev_id); self.base.config.write( offset, data, self.dev_id.clone().load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), + Some(&pci_bus.io_region), + Some(&pci_bus.mem_region), ); } diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index 7192fbbe0..a984ec700 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -14,14 +14,12 @@ use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; -use devices::pci::{ - config::{ - PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, - REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, - }, - le_write_u16, PciBus, PciDevBase, PciDevOps, +use devices::pci::config::{ + PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, + REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use devices::{Device, DeviceBase}; +use devices::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; +use devices::{convert_bus_mut, Device, DeviceBase, MUT_PCI_BUS}; use util::gen_base_func; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; @@ -35,9 +33,8 @@ impl PciHostRoot { pub fn new(parent_bus: Weak>) -> Self { Self { base: PciDevBase { - base: DeviceBase::new("PCI Host Root".to_string(), false, Some(parent_bus.clone())), + base: DeviceBase::new("PCI Host Root".to_string(), false, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), - parent_bus, devfn: 0, }, } @@ -72,12 +69,10 @@ impl PciDevOps for PciHostRoot { )?; le_write_u16(&mut self.base.config.config, REVISION_ID, 0)?; - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - parent_bus - .lock() - .unwrap() - .devices - .insert(0, Arc::new(Mutex::new(self))); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); + pci_bus.devices.insert(0, Arc::new(Mutex::new(self))); + Ok(()) } diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index 83e1e4a54..461f97b9a 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -27,7 +27,7 @@ use devices::pci::config::{ HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use devices::pci::{le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps}; -use devices::{Device, DeviceBase}; +use devices::{convert_bus_mut, Device, DeviceBase, MUT_PCI_BUS}; use util::byte_code::ByteCode; use util::gen_base_func; use util::num_ops::ranges_overlap; @@ -64,14 +64,9 @@ impl LPCBridge { ) -> Result { Ok(Self { base: PciDevBase { - base: DeviceBase::new( - "ICH9 LPC bridge".to_string(), - false, - Some(parent_bus.clone()), - ), + base: DeviceBase::new("ICH9 LPC bridge".to_string(), false, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0x1F << 3, - parent_bus, }, sys_io, pm_timer: Arc::new(Mutex::new(AcpiPMTimer::new())), @@ -281,12 +276,9 @@ impl PciDevOps for LPCBridge { self.init_pm_ctrl_reg() .with_context(|| "Fail to init IO region for PM control register")?; - let parent_bus = self.base.parent_bus.clone(); - parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); + pci_bus .devices .insert(0x1F << 3, Arc::new(Mutex::new(self))); Ok(()) diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index a025281e5..f9d418c1a 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -24,7 +24,7 @@ use devices::pci::{ }, le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, }; -use devices::{Device, DeviceBase}; +use devices::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; use util::gen_base_func; use util::num_ops::ranges_overlap; @@ -57,14 +57,9 @@ impl Mch { ) -> Self { Self { base: PciDevBase { - base: DeviceBase::new( - "Memory Controller Hub".to_string(), - false, - Some(parent_bus.clone()), - ), + base: DeviceBase::new("Memory Controller Hub".to_string(), false, Some(parent_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, - parent_bus, }, mmconfig_region: Some(mmconfig_region), mmconfig_ops, @@ -90,27 +85,17 @@ impl Mch { } if let Some(region) = self.mmconfig_region.as_ref() { - self.base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .mem_region - .delete_subregion(region)?; + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.mem_region.delete_subregion(region)?; self.mmconfig_region = None; } if enable == 0x1 { let region = Region::init_io_region(length, self.mmconfig_ops.clone(), "PcieXBar"); let base_addr: u64 = pciexbar & addr_mask; - self.base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .mem_region - .add_subregion(region, base_addr)?; + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.mem_region.add_subregion(region, base_addr)?; } Ok(()) } @@ -152,14 +137,9 @@ impl PciDevOps for Mch { CLASS_CODE_HOST_BRIDGE, )?; - let parent_bus = self.base.parent_bus.clone(); - parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .devices - .insert(0, Arc::new(Mutex::new(self))); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); + pci_bus.devices.insert(0, Arc::new(Mutex::new(self))); Ok(()) } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 391fba16c..b4c1f273f 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -43,7 +43,9 @@ use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, pci_ext_cap_next, pci_ext_cap_ver, PciBus, PciDevBase, PciDevOps, }; -use devices::{pci::MsiVector, Device, DeviceBase}; +use devices::{ + convert_bus_mut, convert_bus_ref, pci::MsiVector, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS, +}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use util::gen_base_func; use util::loop_context::create_new_eventfd; @@ -132,10 +134,9 @@ impl VfioPciDevice { Self { // Unknown PCI or PCIe type here, allocate enough space to match the two types. base: PciDevBase { - base: DeviceBase::new(name, true, Some(parent_bus.clone())), + base: DeviceBase::new(name, true, Some(parent_bus)), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), devfn, - parent_bus, }, config_size: 0, config_offset: 0, @@ -453,7 +454,7 @@ impl VfioPciDevice { } fn unregister_bars(&mut self) -> Result<()> { - let bus = self.base.parent_bus.upgrade().unwrap(); + let bus = self.parent_bus().unwrap().upgrade().unwrap(); self.base.config.unregister_bars(&bus)?; Ok(()) } @@ -474,9 +475,9 @@ impl VfioPciDevice { MSIX_CAP_FUNC_MASK | MSIX_CAP_ENABLE, )?; - let msi_irq_manager = if let Some(pci_bus) = self.base.parent_bus.upgrade() { - let locked_pci_bus = pci_bus.lock().unwrap(); - locked_pci_bus.get_msi_irq_manager() + let msi_irq_manager = if let Some(bus) = self.parent_bus().unwrap().upgrade() { + PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.get_msi_irq_manager() } else { None }; @@ -508,7 +509,7 @@ impl VfioPciDevice { let cloned_dev = self.vfio_device.clone(); let cloned_gsi_routes = self.gsi_msi_routes.clone(); - let parent_bus = self.base.parent_bus.clone(); + let parent_bus = self.parent_bus().unwrap().clone(); let dev_id = self.dev_id.clone(); let devfn = self.base.devfn; let cloned_msix = msix.clone(); @@ -522,8 +523,9 @@ impl VfioPciDevice { } let entry = locked_msix.get_message(vector as u16); - let parent_bus = parent_bus.upgrade().unwrap(); - parent_bus.lock().unwrap().update_dev_id(devfn, &dev_id); + let bus = parent_bus.upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.update_dev_id(devfn, &dev_id); let msix_vector = MsiVector { msg_addr_lo: entry.address_lo, msg_addr_hi: entry.address_hi, @@ -821,6 +823,7 @@ impl PciDevOps for VfioPciDevice { gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn realize(mut self) -> Result<()> { + let parent_bus = self.parent_bus().unwrap(); self.init_write_mask(false)?; self.init_write_clear_mask(false)?; Result::with_context(self.vfio_device.lock().unwrap().reset(), || { @@ -838,21 +841,17 @@ impl PciDevOps for VfioPciDevice { self.multi_func, &mut self.base.config.config, self.base.devfn, - self.base.parent_bus.clone(), + parent_bus.clone(), ), || "Failed to init vfio device multifunction.", )?; #[cfg(target_arch = "aarch64")] { - let bus_num = self - .base - .parent_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .number(SECONDARY_BUS_NUM as usize); + let bus = parent_bus.upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + let bus_num = pci_bus.number(SECONDARY_BUS_NUM as usize); + drop(locked_bus); self.dev_id = Arc::new(AtomicU16::new(self.set_dev_id(bus_num, self.base.devfn))); } @@ -867,11 +866,11 @@ impl PciDevOps for VfioPciDevice { let devfn = self.base.devfn; let dev = Arc::new(Mutex::new(self)); - let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); - let mut locked_pci_bus = pci_bus.lock().unwrap(); - let pci_device = locked_pci_bus.devices.get(&devfn); + let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); + MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); + let pci_device = pci_bus.devices.get(&devfn); if pci_device.is_none() { - locked_pci_bus.devices.insert(devfn, dev); + pci_bus.devices.insert(devfn, dev); } else { bail!( "Devfn {:?} has been used by {:?}", @@ -965,15 +964,15 @@ impl PciDevOps for VfioPciDevice { let was_enable = self.base.config.msix.as_ref().map_or(false, |m| { m.lock().unwrap().is_enabled(&self.base.config.config) }); - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(parent_bus, locked_bus, pci_bus); self.base.config.write( offset, data, self.dev_id.load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), + Some(&pci_bus.io_region), + Some(&pci_bus.mem_region), ); if ranges_overlap(offset, size, COMMAND as usize, REG_SIZE).unwrap() { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index a4eabe2bc..19bd02121 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -46,7 +46,7 @@ use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps, PciError, }; -use devices::{Device, DeviceBase}; +use devices::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; #[cfg(feature = "virtio_gpu")] use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -332,10 +332,9 @@ impl VirtioPciDevice { let queue_num = device.lock().unwrap().queue_num(); VirtioPciDevice { base: PciDevBase { - base: DeviceBase::new(name, true, Some(parent_bus.clone())), + base: DeviceBase::new(name, true, Some(parent_bus)), config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), devfn, - parent_bus, }, device, dev_id: Arc::new(AtomicU16::new(0)), @@ -469,11 +468,9 @@ impl VirtioPciDevice { } locked_dev.virtio_base_mut().queues = queues; - let parent = self.base.parent_bus.upgrade().unwrap(); - parent - .lock() - .unwrap() - .update_dev_id(self.base.devfn, &self.dev_id); + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.update_dev_id(self.base.devfn, &self.dev_id); if self.need_irqfd { let mut queue_num = locked_dev.queue_num(); // No need to create call event for control queue. @@ -1012,6 +1009,7 @@ impl PciDevOps for VirtioPciDevice { fn realize(mut self) -> Result<()> { info!("func: realize, id: {:?}", &self.base.base.id); + let parent_bus = self.parent_bus().unwrap(); self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -1051,7 +1049,7 @@ impl PciDevOps for VirtioPciDevice { self.multi_func, &mut self.base.config.config, self.base.devfn, - self.base.parent_bus.clone(), + parent_bus.clone(), )?; #[cfg(target_arch = "aarch64")] self.base.config.set_interrupt_pin(); @@ -1123,7 +1121,7 @@ impl PciDevOps for VirtioPciDevice { init_intx( self.name(), &mut self.base.config, - self.base.parent_bus.clone(), + parent_bus.clone(), self.base.devfn, )?; @@ -1160,11 +1158,11 @@ impl PciDevOps for VirtioPciDevice { )?; // Register device to pci bus. - let pci_bus = dev.lock().unwrap().base.parent_bus.upgrade().unwrap(); - let mut locked_pci_bus = pci_bus.lock().unwrap(); - let pci_device = locked_pci_bus.devices.get(&devfn); + let bus = parent_bus.upgrade().unwrap(); + MUT_PCI_BUS!(bus, locked_bus, pci_bus); + let pci_device = pci_bus.devices.get(&devfn); if pci_device.is_none() { - locked_pci_bus.devices.insert(devfn, dev.clone()); + pci_bus.devices.insert(devfn, dev.clone()); } else { bail!( "Devfn {:?} has been used by {:?}", @@ -1186,7 +1184,7 @@ impl PciDevOps for VirtioPciDevice { .unrealize() .with_context(|| "Failed to unrealize the virtio device")?; - let bus = self.base.parent_bus.upgrade().unwrap(); + let bus = self.parent_bus().unwrap().upgrade().unwrap(); self.base.config.unregister_bars(&bus)?; MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name()); @@ -1213,15 +1211,15 @@ impl PciDevOps for VirtioPciDevice { } trace::virtio_tpt_write_config(&self.base.base.id, offset as u64, data); - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); self.base.config.write( offset, data, self.dev_id.clone().load(Ordering::Acquire), #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), + Some(&pci_bus.io_region), + Some(&pci_bus.mem_region), ); self.do_cfg_access(offset, end, true); } @@ -1240,7 +1238,7 @@ impl PciDevOps for VirtioPciDevice { } fn get_dev_path(&self) -> Option { - let parent_bus = self.base.parent_bus.upgrade().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); match self.device.lock().unwrap().device_type() { VIRTIO_TYPE_BLOCK => { // The virtio blk device is identified as a single-channel SCSI device, @@ -1330,12 +1328,12 @@ impl MigrationHook for VirtioPciDevice { } // Reregister ioevents for notifies. - let parent_bus = self.base.parent_bus.upgrade().unwrap(); - let locked_parent_bus = parent_bus.lock().unwrap(); + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + PCI_BUS!(parent_bus, locked_bus, pci_bus); if let Err(e) = self.base.config.update_bar_mapping( #[cfg(target_arch = "x86_64")] - Some(&locked_parent_bus.io_region), - Some(&locked_parent_bus.mem_region), + Some(&pci_bus.io_region), + Some(&pci_bus.mem_region), ) { bail!("Failed to update bar, error is {:?}", e); } @@ -1643,10 +1641,11 @@ mod tests { ) .unwrap(); + let parent_bus = virtio_pci.parent_bus().unwrap(); init_intx( virtio_pci.name(), &mut virtio_pci.base.config, - virtio_pci.base.parent_bus.clone(), + parent_bus.clone(), virtio_pci.base.devfn, ) .unwrap(); @@ -1701,11 +1700,12 @@ mod tests { fn test_multifunction() { let (_, _parent_bus, mut virtio_pci) = virtio_pci_test_init(true); + let parent_bus = virtio_pci.parent_bus().unwrap(); assert!(init_multifunction( virtio_pci.multi_func, &mut virtio_pci.base.config.config, virtio_pci.base.devfn, - virtio_pci.base.parent_bus.clone() + parent_bus, ) .is_ok()); let header_type = -- Gitee From 193721e9eed7638b2cd1d9993f6501c22a15da9f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Mon, 11 Dec 2023 20:48:39 +0800 Subject: [PATCH 1949/2187] devices/pcibus: pcibus deletes devices field `devices` field of struct `PciBus` has the same effect as the `children` field of struct `BusBase`. Delete it. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 18 ++------ devices/src/misc/pvpanic.rs | 67 ++++++++++++---------------- devices/src/pci/bus.rs | 53 +++++++++++----------- devices/src/pci/demo_device/mod.rs | 29 +++++------- devices/src/pci/host.rs | 47 ++++++++++++------- devices/src/pci/hotplug.rs | 14 +++--- devices/src/pci/mod.rs | 53 +++++++++++++--------- devices/src/pci/root_port.rs | 66 +++++++++++++-------------- devices/src/usb/xhci/xhci_pci.rs | 16 ++----- machine/src/aarch64/pci_host_root.rs | 6 +-- machine/src/lib.rs | 23 +++++----- machine/src/x86_64/ich9_lpc.rs | 8 ++-- machine/src/x86_64/mch.rs | 6 +-- vfio/src/vfio_pci.rs | 21 +++------ virtio/src/transport/virtio_pci.rs | 16 ++----- 15 files changed, 202 insertions(+), 241 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 08212c441..ae91fcbb6 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -15,14 +15,14 @@ use std::sync::{ Arc, Mutex, Weak, }; -use anyhow::{bail, Result}; +use anyhow::Result; use crate::pci::config::{ PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use crate::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; -use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; +use crate::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; use util::gen_base_func; @@ -121,18 +121,8 @@ impl PciDevOps for Ivshmem { // Attach to the PCI bus. let bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(bus, locked_bus, pci_bus); - let pci_device = pci_bus.devices.get(&self.base.devfn); - match pci_device { - Some(device) => bail!( - "Devfn {:?} has been used by {:?}", - &self.base.devfn, - device.lock().unwrap().name() - ), - None => pci_bus - .devices - .insert(self.base.devfn, Arc::new(Mutex::new(self))), - }; + let mut locked_bus = bus.lock().unwrap(); + locked_bus.attach_child(self.base.devfn as u64, Arc::new(Mutex::new(self)))?; Ok(()) } diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index b1d483b3f..eae09f889 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -222,16 +222,7 @@ impl PciDevOps for PvPanicPci { .unwrap() .dev_id .store(device_id, Ordering::Release); - let pci_device = pci_bus.devices.get(&devfn); - if pci_device.is_none() { - pci_bus.devices.insert(devfn, dev); - } else { - bail!( - "pvpanic: Devfn {:?} has been used by {:?}", - &devfn, - pci_device.unwrap().lock().unwrap().name() - ); - } + locked_bus.attach_child(devfn as u64, dev)?; Ok(()) } @@ -259,9 +250,17 @@ impl PciDevOps for PvPanicPci { mod tests { use super::*; use crate::pci::{host::tests::create_pci_host, le_read_u16, PciHost}; - use crate::Bus; + use crate::{convert_device_mut, Bus}; use machine_manager::config::str_slip_to_clap; + /// Convert from Arc> to &mut PvPanicPci. + #[macro_export] + macro_rules! MUT_PVPANIC_PCI { + ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { + convert_device_mut!($trait_device, $lock_device, $struct_device, PvPanicPci); + }; + } + fn init_pvpanic_dev(devfn: u8, supported_features: u32, dev_id: &str) -> Arc> { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); @@ -311,7 +310,7 @@ mod tests { let pvpanic_dev = root_bus.upgrade().unwrap().lock().unwrap().get_device(0, 7); assert!(pvpanic_dev.is_some()); assert_eq!( - pvpanic_dev.unwrap().lock().unwrap().pci_base().base.id, + pvpanic_dev.unwrap().lock().unwrap().name(), "pvpanic_test".to_string() ); @@ -335,40 +334,28 @@ mod tests { .unwrap() .get_device(0, 7) .unwrap(); - - let info = le_read_u16( - &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, - VENDOR_ID as usize, - ) - .unwrap_or_else(|_| 0); + MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); + let info = le_read_u16(&pvpanic.pci_base_mut().config.config, VENDOR_ID as usize) + .unwrap_or_else(|_| 0); assert_eq!(info, PCI_VENDOR_ID_REDHAT); - let info = le_read_u16( - &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, - DEVICE_ID as usize, - ) - .unwrap_or_else(|_| 0); + let info = le_read_u16(&pvpanic.pci_base_mut().config.config, DEVICE_ID as usize) + .unwrap_or_else(|_| 0); assert_eq!(info, PCI_DEVICE_ID_REDHAT_PVPANIC); let info = le_read_u16( - &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, + &pvpanic.pci_base_mut().config.config, SUB_CLASS_CODE as usize, ) .unwrap_or_else(|_| 0); assert_eq!(info, PCI_CLASS_SYSTEM_OTHER); - let info = le_read_u16( - &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, - SUBSYSTEM_VENDOR_ID, - ) - .unwrap_or_else(|_| 0); + let info = le_read_u16(&pvpanic.pci_base_mut().config.config, SUBSYSTEM_VENDOR_ID) + .unwrap_or_else(|_| 0); assert_eq!(info, PVPANIC_PCI_VENDOR_ID); - let info = le_read_u16( - &pvpanic_dev.lock().unwrap().pci_base_mut().config.config, - SUBSYSTEM_ID, - ) - .unwrap_or_else(|_| 0); + let info = + le_read_u16(&pvpanic.pci_base_mut().config.config, SUBSYSTEM_ID).unwrap_or_else(|_| 0); assert_eq!(info, PCI_SUBDEVICE_ID_QEMU); } @@ -385,10 +372,11 @@ mod tests { .unwrap() .get_device(0, 7) .unwrap(); + MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test read supported_features let mut data_read = [0xffu8; 1]; - let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + let result = &pvpanic.pci_base_mut().config.bars[0] .region .as_ref() .unwrap() @@ -413,11 +401,12 @@ mod tests { .unwrap() .get_device(0, 7) .unwrap(); + MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test write panicked event let data_write = [PVPANIC_PANICKED as u8; 1]; let count = data_write.len() as u64; - let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + let result = &pvpanic.pci_base_mut().config.bars[0] .region .as_ref() .unwrap() @@ -438,11 +427,12 @@ mod tests { .unwrap() .get_device(0, 7) .unwrap(); + MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test write crashload event let data_write = [PVPANIC_CRASHLOADED as u8; 1]; let count = data_write.len() as u64; - let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + let result = &pvpanic.pci_base_mut().config.bars[0] .region .as_ref() .unwrap() @@ -463,11 +453,12 @@ mod tests { .unwrap() .get_device(0, 7) .unwrap(); + MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test write unknown event let data_write = [100u8; 1]; let count = data_write.len() as u64; - let result = &pvpanic_dev.lock().unwrap().pci_base_mut().config.bars[0] + let result = &pvpanic.pci_base_mut().config.bars[0] .region .as_ref() .unwrap() diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 631f1be54..6e03f1a1c 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -10,11 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::collections::HashMap; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use log::debug; use super::{ @@ -22,20 +21,19 @@ use super::{ hotplug::HotplugOps, PciDevOps, PciIntxState, }; -use crate::pci::RootPort; +use crate::pci::{to_pcidevops, RootPort}; use crate::{ - convert_device_mut, convert_device_ref, Bus, BusBase, MsiIrqManager, MUT_ROOT_PORT, ROOT_PORT, + convert_device_mut, convert_device_ref, Bus, BusBase, Device, MsiIrqManager, MUT_ROOT_PORT, + PCI_BUS_DEVICE, ROOT_PORT, }; use address_space::Region; use util::gen_base_func; -type DeviceBusInfo = (Arc>, Arc>); +type DeviceBusInfo = (Arc>, Arc>); /// PCI bus structure. pub struct PciBus { pub base: BusBase, - /// Devices attached to the bus. - pub devices: HashMap>>, /// Child buses of the bus. pub child_buses: Vec>>, /// IO region which the parent bridge manages. @@ -85,7 +83,6 @@ impl PciBus { ) -> Self { Self { base: BusBase::new(name), - devices: HashMap::new(), child_buses: Vec::new(), #[cfg(target_arch = "x86_64")] io_region, @@ -115,9 +112,9 @@ impl PciBus { /// /// * `bus_num` - The bus number. /// * `devfn` - Slot number << 3 | Function number. - pub fn get_device(&self, bus_num: u8, devfn: u8) -> Option>> { - if let Some(dev) = self.devices.get(&devfn) { - return Some((*dev).clone()); + pub fn get_device(&self, bus_num: u8, devfn: u8) -> Option>> { + if let Some(dev) = self.child_dev(devfn as u64) { + return Some(dev.clone()); } debug!("Can't find device {}:{}", bus_num, devfn); None @@ -185,7 +182,7 @@ impl PciBus { pub fn find_attached_bus(pci_bus: &Arc>, name: &str) -> Option { // Device is attached in pci_bus. let locked_bus = pci_bus.lock().unwrap(); - for dev in locked_bus.devices.values() { + for dev in locked_bus.child_devices().values() { if dev.lock().unwrap().name() == name { return Some((pci_bus.clone(), dev.clone())); } @@ -205,30 +202,27 @@ impl PciBus { /// /// * `bus` - Bus to detach from. /// * `dev` - Device attached to the bus. - pub fn detach_device(bus: &Arc>, dev: &Arc>) -> Result<()> { - let mut dev_locked = dev.lock().unwrap(); - dev_locked + pub fn detach_device(bus: &Arc>, dev: &Arc>) -> Result<()> { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + pci_dev .unrealize() - .with_context(|| format!("Failed to unrealize device {}", dev_locked.name()))?; + .with_context(|| format!("Failed to unrealize device {}", pci_dev.name()))?; - let devfn = dev_locked.pci_base().devfn; + let devfn = pci_dev.pci_base().devfn as u64; let mut locked_bus = bus.lock().unwrap(); - if locked_bus.devices.get(&devfn).is_some() { - locked_bus.devices.remove(&devfn); - } else { - bail!("Device {} not found in the bus", dev_locked.name()); - } + locked_bus + .detach_child(devfn) + .with_context(|| format!("Device {} not found in the bus", pci_dev.name()))?; Ok(()) } pub fn reset(&mut self) -> Result<()> { - for (_id, pci_dev) in self.devices.iter() { + for dev in self.child_devices().values() { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); pci_dev - .lock() - .unwrap() .reset(false) - .with_context(|| "Fail to reset pci dev")?; + .with_context(|| format!("Fail to reset pci dev {}", pci_dev.name()))?; } for child_bus in self.child_buses.iter_mut() { @@ -287,6 +281,7 @@ mod tests { use crate::pci::host::tests::create_pci_host; use crate::pci::root_port::{RootPort, RootPortConfig}; use crate::pci::tests::TestPciDevice; + use crate::pci::{clean_pcidevops_type, register_pcidevops_type}; #[test] fn test_find_attached_bus() { @@ -328,6 +323,8 @@ mod tests { #[test] fn test_detach_device() { + register_pcidevops_type::().unwrap(); + let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); let root_bus = Arc::downgrade(&locked_pci_host.root_bus); @@ -342,7 +339,7 @@ mod tests { let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); let pci_dev = TestPciDevice::new("test1", 0, Arc::downgrade(&bus)); - let dev_ops: Arc> = Arc::new(Mutex::new(pci_dev.clone())); + let dev_ops: Arc> = Arc::new(Mutex::new(pci_dev.clone())); pci_dev.realize().unwrap(); let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); @@ -353,5 +350,7 @@ mod tests { let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); assert!(info.is_none()); + + clean_pcidevops_type(); } } diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 37dc6b73f..483bfbab9 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -41,22 +41,20 @@ use std::{ }, }; -use anyhow::{bail, Result}; +use anyhow::Result; use clap::Parser; use log::error; -use crate::pci::demo_device::{ - dpy_device::DemoDisplay, gpu_device::DemoGpu, kbd_pointer_device::DemoKbdMouse, +use crate::pci::config::{ + PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, PCIE_CONFIG_SPACE_SIZE, + SUB_CLASS_CODE, VENDOR_ID, }; -use crate::pci::{ - config::{ - PciConfig, RegionType, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_ENDPOINT, - PCIE_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, - }, - init_msix, le_write_u16, PciBus, PciDevOps, +use crate::pci::demo_device::{ + base_device::BaseDevice, dpy_device::DemoDisplay, gpu_device::DemoGpu, + kbd_pointer_device::DemoKbdMouse, }; -use crate::pci::{demo_device::base_device::BaseDevice, PciDevBase}; -use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; +use crate::pci::{init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; +use crate::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; use util::gen_base_func; @@ -134,14 +132,11 @@ impl DemoDev { } fn attach_to_parent_bus(self) -> Result<()> { + let devfn = self.base.devfn as u64; let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); - if pci_bus.devices.get(&self.base.devfn).is_some() { - bail!("device already existed"); - } - let devfn = self.base.devfn; + let mut locked_bus = parent_bus.lock().unwrap(); let demo_pci_dev = Arc::new(Mutex::new(self)); - pci_bus.devices.insert(devfn, demo_pci_dev); + locked_bus.attach_child(devfn, demo_pci_dev)?; Ok(()) } diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 1af696828..4d45a4f4f 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -16,11 +16,11 @@ use anyhow::{Context, Result}; #[cfg(target_arch = "aarch64")] use crate::pci::PCI_INTR_BASE; -use crate::pci::{bus::PciBus, PciDevOps, PCI_PIN_NUM, PCI_SLOT_MAX}; +use crate::pci::{bus::PciBus, to_pcidevops, PCI_PIN_NUM, PCI_SLOT_MAX}; #[cfg(target_arch = "x86_64")] use crate::pci::{le_read_u32, le_write_u32}; use crate::sysbus::{SysBusDevBase, SysBusDevOps}; -use crate::{Device, DeviceBase}; +use crate::{Bus, Device, DeviceBase, PCI_BUS_DEVICE}; use acpi::{ AmlActiveLevel, AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlElse, @@ -111,14 +111,16 @@ impl PciHost { } } - pub fn find_device(&self, bus_num: u8, devfn: u8) -> Option>> { + pub fn find_device(&self, bus_num: u8, devfn: u8) -> Option>> { let locked_root_bus = self.root_bus.lock().unwrap(); if bus_num == 0 { - return locked_root_bus.get_device(0, devfn); + let dev = locked_root_bus.child_dev(devfn as u64)?; + return Some(dev.clone()); } for bus in &locked_root_bus.child_buses { if let Some(b) = PciBus::find_bus_by_num(bus, bus_num) { - return b.lock().unwrap().get_device(bus_num, devfn); + let dev = b.lock().unwrap().child_dev(devfn as u64)?.clone(); + return Some(dev); } } None @@ -197,7 +199,8 @@ impl PciHost { match locked_hb.find_device(bus_num, devfn) { Some(dev) => { offset &= PIO_OFFSET_MASK; - dev.lock().unwrap().read_config(offset as usize, data); + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + pci_dev.read_config(offset as usize, data); } None => { for d in data.iter_mut() { @@ -219,7 +222,8 @@ impl PciHost { let devfn = ((offset >> PIO_DEVFN_SHIFT) & CONFIG_DEVFN_MASK) as u8; if let Some(dev) = locked_hb.find_device(bus_num, devfn) { offset &= PIO_OFFSET_MASK; - dev.lock().unwrap().write_config(offset as usize, data); + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + pci_dev.write_config(offset as usize, data); } true }; @@ -244,9 +248,9 @@ impl SysBusDevOps for PciHost { match self.find_device(bus_num, devfn) { Some(dev) => { let addr: usize = (offset & ECAM_OFFSET_MASK) as usize; - let dev_name = &dev.lock().unwrap().pci_base().base.id.clone(); - trace::pci_read_config(dev_name, addr, data); - dev.lock().unwrap().read_config(addr, data); + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + trace::pci_read_config(&pci_dev.name(), addr, data); + pci_dev.read_config(addr, data); } None => { for d in data.iter_mut() { @@ -263,9 +267,9 @@ impl SysBusDevOps for PciHost { match self.find_device(bus_num, devfn) { Some(dev) => { let addr: usize = (offset & ECAM_OFFSET_MASK) as usize; - let dev_name = &dev.lock().unwrap().pci_base().base.id.clone(); - trace::pci_write_config(dev_name, addr, data); - dev.lock().unwrap().write_config(addr, data); + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + trace::pci_write_config(&pci_dev.name(), addr, data); + pci_dev.write_config(addr, data); true } None => true, @@ -273,10 +277,9 @@ impl SysBusDevOps for PciHost { } fn reset(&mut self) -> Result<()> { - for (_id, pci_dev) in self.root_bus.lock().unwrap().devices.iter_mut() { + for dev in self.root_bus.lock().unwrap().child_devices().values() { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); pci_dev - .lock() - .unwrap() .reset(true) .with_context(|| "Fail to reset pci device under pci host")?; } @@ -531,6 +534,7 @@ impl AmlBuilder for PciHost { #[cfg(test)] pub mod tests { + #[cfg(target_arch = "x86_64")] use byteorder::{ByteOrder, LittleEndian}; use super::*; @@ -538,6 +542,7 @@ pub mod tests { use crate::pci::config::SECONDARY_BUS_NUM; use crate::pci::root_port::{RootPort, RootPortConfig}; use crate::pci::tests::TestPciDevice; + use crate::pci::{clean_pcidevops_type, register_pcidevops_type, PciDevOps}; use address_space::Region; pub fn create_pci_host() -> Arc> { @@ -571,6 +576,8 @@ pub mod tests { #[test] #[cfg(target_arch = "x86_64")] fn test_pio_ops() { + register_pcidevops_type::().unwrap(); + let pci_host = create_pci_host(); let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); let pio_addr_ops = PciHost::build_pio_addr_ops(pci_host.clone()); @@ -597,7 +604,6 @@ pub mod tests { assert_eq!(buf, data); // Non-DWORD access on CONFIG_ADDR - let mut config = [0_u8; 4]; (pio_addr_ops.read)(&mut config, GuestAddress(0), 0); let data = [0x12, 0x34]; @@ -649,10 +655,15 @@ pub mod tests { let mut buf = [0_u8; 4]; (pio_data_ops.read)(&mut buf, GuestAddress(0), 0); assert_eq!(buf, [0xff_u8; 4]); + + clean_pcidevops_type(); } #[test] fn test_mmio_ops() { + register_pcidevops_type::().unwrap(); + register_pcidevops_type::().unwrap(); + let pci_host = create_pci_host(); let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(pci_host.clone()); @@ -703,5 +714,7 @@ pub mod tests { let mut buf = [0_u8; 2]; (mmconfig_region_ops.read)(&mut buf, GuestAddress(0), addr); assert_eq!(buf, data); + + clean_pcidevops_type(); } } diff --git a/devices/src/pci/hotplug.rs b/devices/src/pci/hotplug.rs index 24533a24e..862956be0 100644 --- a/devices/src/pci/hotplug.rs +++ b/devices/src/pci/hotplug.rs @@ -14,19 +14,19 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; -use crate::pci::{PciBus, PciDevOps}; -use crate::Bus; +use crate::pci::PciBus; +use crate::{Bus, Device}; pub trait HotplugOps: Send { /// Plug device, usually called when hot plug device in device_add. - fn plug(&mut self, dev: &Arc>) -> Result<()>; + fn plug(&mut self, dev: &Arc>) -> Result<()>; /// Unplug device request, usually called when hot unplug device in device_del. /// Only send unplug request to the guest OS, without actually removing the device. - fn unplug_request(&mut self, dev: &Arc>) -> Result<()>; + fn unplug_request(&mut self, dev: &Arc>) -> Result<()>; /// Remove the device. - fn unplug(&mut self, dev: &Arc>) -> Result<()>; + fn unplug(&mut self, dev: &Arc>) -> Result<()>; } /// Plug the device into the bus. @@ -41,7 +41,7 @@ pub trait HotplugOps: Send { /// Return Error if /// * No hot plug controller found. /// * Device plug failed. -pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> Result<()> { +pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> Result<()> { let locked_bus = bus.lock().unwrap(); if let Some(hpc) = locked_bus.hotplug_controller.as_ref() { hpc.upgrade().unwrap().lock().unwrap().plug(dev) @@ -67,7 +67,7 @@ pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> /// * Device unplug request failed. pub fn handle_unplug_pci_request( bus: &Arc>, - dev: &Arc>, + dev: &Arc>, ) -> Result<()> { let locked_bus = bus.lock().unwrap(); let hpc = locked_bus diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index e3da00e35..b7faa7923 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -266,15 +266,15 @@ pub trait PciDevOps: Device + Send { } } -pub type ToPciDevOpsFunc = fn(&dyn Any) -> &dyn PciDevOps; +pub type ToPciDevOpsFunc = fn(&mut dyn Any) -> &mut dyn PciDevOps; static mut PCIDEVOPS_HASHMAP: Option> = None; -pub fn convert_to_pcidevops(item: &dyn Any) -> &dyn PciDevOps { +pub fn convert_to_pcidevops(item: &mut dyn Any) -> &mut dyn PciDevOps { // SAFETY: The typeid of `T` is the typeid recorded in the hashmap. The target structure type of // the conversion is its own structure type, so the conversion result will definitely not be `None`. - let t = item.downcast_ref::().unwrap(); - t as &dyn PciDevOps + let t = item.downcast_mut::().unwrap(); + t as &mut dyn PciDevOps } pub fn register_pcidevops_type() -> Result<()> { @@ -306,17 +306,32 @@ pub fn devices_register_pcidevops_type() -> Result<()> { register_pcidevops_type::() } -pub fn to_pcidevops(dev: &dyn Device) -> Option<&dyn PciDevOps> { - let type_id = dev.type_id(); +#[cfg(test)] +pub fn clean_pcidevops_type() { + unsafe { + PCIDEVOPS_HASHMAP = None; + } +} + +pub fn to_pcidevops(dev: &mut dyn Device) -> Option<&mut dyn PciDevOps> { // SAFETY: PCIDEVOPS_HASHMAP has been built. And this function is called without changing hashmap. unsafe { let types = PCIDEVOPS_HASHMAP.as_mut().unwrap(); - let func = types.get(&type_id)?; - let pcidev = func(dev.as_any()); + let func = types.get(&dev.device_type_id())?; + let pcidev = func(dev.as_any_mut()); Some(pcidev) } } +/// Convert from Arc> to &mut dyn PciDevOps. +#[macro_export] +macro_rules! PCI_BUS_DEVICE { + ($trait_device:expr, $lock_device: ident, $trait_pcidevops: ident) => { + let mut $lock_device = $trait_device.lock().unwrap(); + let $trait_pcidevops = to_pcidevops(&mut *$lock_device).unwrap(); + }; +} + /// Init multifunction for pci devices. /// /// # Arguments @@ -346,22 +361,19 @@ pub fn init_multifunction( let bus = parent_bus.upgrade().unwrap(); PCI_BUS!(bus, locked_bus, pci_bus); if pci_func(devfn) != 0 { - let pci_dev = pci_bus.devices.get(&pci_devfn(slot, 0)); - if pci_dev.is_none() { + let dev = pci_bus.child_dev(pci_devfn(slot, 0) as u64); + if dev.is_none() { return Ok(()); } let mut data = vec![0_u8; 2]; - pci_dev - .unwrap() - .lock() - .unwrap() - .read_config(HEADER_TYPE as usize, data.as_mut_slice()); + PCI_BUS_DEVICE!(dev.unwrap(), locked_dev, pci_dev); + pci_dev.read_config(HEADER_TYPE as usize, data.as_mut_slice()); if LittleEndian::read_u16(&data) & HEADER_TYPE_MULTIFUNC as u16 == 0 { // Function 0 should set multifunction bit. bail!( "PCI: single function device can't be populated in bus {} function {}.{}", - &locked_bus.name(), + &pci_bus.name(), slot, devfn & 0x07 ); @@ -375,7 +387,7 @@ pub fn init_multifunction( // If function 0 is set to single function, the rest function should be None. for func in 1..MAX_FUNC { - if pci_bus.devices.get(&pci_devfn(slot, func)).is_some() { + if pci_bus.child_dev(pci_devfn(slot, func) as u64).is_some() { bail!( "PCI: {}.0 indicates single function, but {}.{} is already populated", slot, @@ -398,7 +410,7 @@ pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { mod tests { use super::*; use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; - use crate::{convert_bus_mut, DeviceBase, MUT_PCI_BUS}; + use crate::DeviceBase; use address_space::{AddressSpace, Region}; use util::gen_base_func; @@ -438,14 +450,13 @@ mod tests { } fn realize(mut self) -> Result<()> { - let devfn = self.base.devfn; + let devfn = self.base.devfn as u64; self.init_write_mask(false)?; self.init_write_clear_mask(false)?; let dev = Arc::new(Mutex::new(self)); let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); - pci_bus.devices.insert(devfn, dev.clone()); + parent_bus.lock().unwrap().attach_child(devfn, dev)?; Ok(()) } diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index cf05c4b6e..ea6b43b15 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -34,12 +34,13 @@ use crate::pci::config::{BRIDGE_CONTROL, BRIDGE_CTL_SEC_BUS_RESET}; use crate::pci::hotplug::HotplugOps; use crate::pci::intx::init_intx; use crate::pci::msix::init_msix; -use crate::pci::{init_multifunction, PciDevBase, PciError, PciIntxState, INTERRUPT_PIN}; use crate::pci::{ - le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, le_write_u16, PciDevOps, + init_multifunction, le_read_u16, le_write_clear_value_u16, le_write_set_value_u16, + le_write_u16, to_pcidevops, PciDevBase, PciDevOps, PciError, PciIntxState, INTERRUPT_PIN, }; use crate::{ - convert_bus_mut, convert_bus_ref, Device, DeviceBase, MsiIrqManager, MUT_PCI_BUS, PCI_BUS, + convert_bus_mut, convert_bus_ref, Bus, Device, DeviceBase, MsiIrqManager, MUT_PCI_BUS, PCI_BUS, + PCI_BUS_DEVICE, }; use address_space::Region; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; @@ -221,10 +222,10 @@ impl RootPort { // Store device in a temp vector and unlock the bus. // If the device unrealize called when the bus is locked, a deadlock occurs. // This is because the device unrealize also requires the bus lock. - let devices = self.sec_bus.lock().unwrap().devices.clone(); + let devices = self.sec_bus.lock().unwrap().child_devices().clone(); for dev in devices.values() { - let mut locked_dev = dev.lock().unwrap(); - if let Err(e) = locked_dev.unrealize() { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + if let Err(e) = pci_dev.unrealize() { error!("{}", format!("{:?}", e)); error!("Failed to unrealize device {}.", locked_dev.name()); } @@ -233,7 +234,7 @@ impl RootPort { // Send QMP event for successful hot unplugging. send_device_deleted_msg(&locked_dev.name()); } - self.sec_bus.lock().unwrap().devices.clear(); + self.sec_bus.lock().unwrap().bus_base_mut().children.clear(); } fn register_region(&mut self) { @@ -417,12 +418,10 @@ impl PciDevOps for RootPort { Some(Arc::downgrade(&root_port) as Weak>); locked_root_port.sec_bus.lock().unwrap().hotplug_controller = Some(Arc::downgrade(&root_port) as Weak>); - let pci_device = pci_bus.devices.get(&locked_root_port.base.devfn); + let pci_device = pci_bus.child_dev(locked_root_port.base.devfn as u64); if pci_device.is_none() { pci_bus.child_buses.push(locked_root_port.sec_bus.clone()); - pci_bus - .devices - .insert(locked_root_port.base.devfn, root_port.clone()); + pci_bus.attach_child(locked_root_port.base.devfn as u64, root_port.clone())?; } else { bail!( "Devfn {:?} has been used by {:?}", @@ -568,11 +567,13 @@ impl PciDevOps for RootPort { } impl HotplugOps for RootPort { - fn plug(&mut self, dev: &Arc>) -> Result<()> { + fn plug(&mut self, dev: &Arc>) -> Result<()> { if !dev.lock().unwrap().hotpluggable() { bail!("Don't support hot-plug!"); } - let devfn = dev.lock().unwrap().pci_base().devfn; + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + let devfn = pci_dev.pci_base().devfn; + drop(locked_dev); // Only if devfn is equal to 0, hot plugging is supported. if devfn != 0 { return Err(anyhow!(PciError::HotplugUnsupported(devfn))); @@ -594,7 +595,7 @@ impl HotplugOps for RootPort { Ok(()) } - fn unplug_request(&mut self, dev: &Arc>) -> Result<()> { + fn unplug_request(&mut self, dev: &Arc>) -> Result<()> { let pcie_cap_offset = self.base.config.pci_express_cap_offset; let sltctl = le_read_u16( &self.base.config.config, @@ -609,7 +610,9 @@ impl HotplugOps for RootPort { if !dev.lock().unwrap().hotpluggable() { bail!("Don't support hot-unplug request!"); } - let devfn = dev.lock().unwrap().pci_base().devfn; + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + let devfn = pci_dev.pci_base().devfn; + drop(locked_dev); if devfn != 0 { return self.unplug(dev); } @@ -647,14 +650,14 @@ impl HotplugOps for RootPort { Ok(()) } - fn unplug(&mut self, dev: &Arc>) -> Result<()> { + fn unplug(&mut self, dev: &Arc>) -> Result<()> { if !dev.lock().unwrap().hotpluggable() { bail!("Don't support hot-unplug!"); } - let devfn = dev.lock().unwrap().pci_base().devfn; - let mut locked_dev = dev.lock().unwrap(); - locked_dev.unrealize()?; - self.sec_bus.lock().unwrap().devices.remove(&devfn); + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + let devfn = pci_dev.pci_base().devfn as u64; + pci_dev.unrealize()?; + self.sec_bus.lock().unwrap().detach_child(devfn)?; Ok(()) } } @@ -700,7 +703,7 @@ impl MigrationHook for RootPort {} #[cfg(test)] mod tests { use super::*; - use crate::pci::host::tests::create_pci_host; + use crate::{convert_device_mut, pci::host::tests::create_pci_host, MUT_ROOT_PORT}; #[test] fn test_read_config() { @@ -714,12 +717,10 @@ mod tests { let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); - let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap(); + let dev = pci_host.lock().unwrap().find_device(0, 8).unwrap(); let mut buf = [1_u8; 4]; - root_port - .lock() - .unwrap() - .read_config(PCIE_CONFIG_SPACE_SIZE - 1, &mut buf); + MUT_ROOT_PORT!(dev, locked_dev, root_port); + root_port.read_config(PCIE_CONFIG_SPACE_SIZE - 1, &mut buf); assert_eq!(buf, [1_u8; 4]); } @@ -734,19 +735,14 @@ mod tests { }; let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); - let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap(); + let dev = pci_host.lock().unwrap().find_device(0, 8).unwrap(); + MUT_ROOT_PORT!(dev, locked_dev, root_port); // Invalid write. let data = [1_u8; 4]; - root_port - .lock() - .unwrap() - .write_config(PCIE_CONFIG_SPACE_SIZE - 1, &data); + root_port.write_config(PCIE_CONFIG_SPACE_SIZE - 1, &data); let mut buf = [0_u8]; - root_port - .lock() - .unwrap() - .read_config(PCIE_CONFIG_SPACE_SIZE - 1, &mut buf); + root_port.read_config(PCIE_CONFIG_SPACE_SIZE - 1, &mut buf); assert_eq!(buf, [0_u8]); } } diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 7b966bd7f..e8b2e6464 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -34,7 +34,7 @@ use crate::pci::config::{ }; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; use crate::usb::UsbDevice; -use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; +use crate::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::{get_pci_df, valid_id}; use machine_manager::event_loop::register_event_helper; @@ -314,7 +314,7 @@ impl PciDevOps for XhciPciDevice { mem_region_size, )?; - let devfn = self.base.devfn; + let devfn = self.base.devfn as u64; // It is safe to unwrap, because it is initialized in init_msix. let cloned_msix = self.base.config.msix.as_ref().unwrap().clone(); let cloned_intx = self.base.config.intx.as_ref().unwrap().clone(); @@ -338,17 +338,7 @@ impl PciDevOps for XhciPciDevice { let dev = Arc::new(Mutex::new(self)); // Attach to the PCI bus. let bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(bus, locked_bus, pci_bus); - let pci_device = pci_bus.devices.get(&devfn); - if pci_device.is_none() { - pci_bus.devices.insert(devfn, dev); - } else { - bail!( - "Devfn {:?} has been used by {:?}", - &devfn, - pci_device.unwrap().lock().unwrap().name() - ); - } + bus.lock().unwrap().attach_child(devfn, dev)?; Ok(()) } diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index a984ec700..ec50c954c 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -19,7 +19,7 @@ use devices::pci::config::{ REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use devices::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; -use devices::{convert_bus_mut, Device, DeviceBase, MUT_PCI_BUS}; +use devices::{Device, DeviceBase}; use util::gen_base_func; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; @@ -70,8 +70,8 @@ impl PciDevOps for PciHostRoot { le_write_u16(&mut self.base.config.config, REVISION_ID, 0)?; let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); - pci_bus.devices.insert(0, Arc::new(Mutex::new(self))); + let mut locked_bus = parent_bus.lock().unwrap(); + locked_bus.attach_child(0, Arc::new(Mutex::new(self)))?; Ok(()) } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index c58b03f48..34a78be1d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -55,8 +55,8 @@ use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] use devices::pci::demo_device::{DemoDev, DemoDevConfig}; use devices::pci::{ - devices_register_pcidevops_type, register_pcidevops_type, PciBus, PciDevOps, PciHost, RootPort, - RootPortConfig, + devices_register_pcidevops_type, register_pcidevops_type, to_pcidevops, PciBus, PciDevOps, + PciHost, RootPort, RootPortConfig, }; use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; @@ -77,7 +77,7 @@ use devices::usb::UsbDevice; use devices::InterruptController; use devices::ScsiBus::get_scsi_key; use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; -use devices::{Bus, Device, SYS_BUS_DEVICE}; +use devices::{Bus, Device, PCI_BUS_DEVICE, SYS_BUS_DEVICE}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; @@ -1209,11 +1209,11 @@ pub trait MachineOps: MachineLifecycle { .get_pci_dev_by_id_and_type(vm_config, Some(&cntlr), "virtio-scsi-pci") .with_context(|| format!("Can not find scsi controller from pci bus {}", cntlr))?; let locked_pcidev = pci_dev.lock().unwrap(); - let prefix = locked_pcidev.get_dev_path().unwrap(); let virtio_pcidev = locked_pcidev .as_any() .downcast_ref::() .unwrap(); + let prefix = virtio_pcidev.get_dev_path().unwrap(); let virtio_device = virtio_pcidev.get_virtio_device().lock().unwrap(); let cntlr = virtio_device.as_any().downcast_ref::().unwrap(); let bus = cntlr.bus.as_ref().unwrap(); @@ -1521,7 +1521,7 @@ pub trait MachineOps: MachineLifecycle { drop(locked_dev); let mut devfn = None; let locked_bus = locked_pci_host.root_bus.lock().unwrap(); - for (id, dev) in &locked_bus.devices { + for (id, dev) in &locked_bus.child_devices() { if dev.lock().unwrap().name() == name { devfn = Some(*id); break; @@ -1529,12 +1529,11 @@ pub trait MachineOps: MachineLifecycle { } drop(locked_bus); // It's safe to call devfn.unwrap(), because the bus exists. - match locked_pci_host.find_device(0, devfn.unwrap()) { - Some(dev) => dev - .lock() - .unwrap() - .reset(false) - .with_context(|| "Failed to reset bus"), + match locked_pci_host.find_device(0, devfn.unwrap() as u8) { + Some(dev) => { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + pci_dev.reset(false).with_context(|| "Failed to reset bus") + } None => bail!("Failed to found device"), } } @@ -1695,7 +1694,7 @@ pub trait MachineOps: MachineLifecycle { vm_config: &VmConfig, id: Option<&str>, dev_type: &str, - ) -> Option>> { + ) -> Option>> { let (id_check, id_str) = if id.is_some() { (true, format! {"id={}", id.unwrap()}) } else { diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index 461f97b9a..1507fe8bf 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -27,7 +27,7 @@ use devices::pci::config::{ HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; use devices::pci::{le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps}; -use devices::{convert_bus_mut, Device, DeviceBase, MUT_PCI_BUS}; +use devices::{Device, DeviceBase}; use util::byte_code::ByteCode; use util::gen_base_func; use util::num_ops::ranges_overlap; @@ -277,10 +277,8 @@ impl PciDevOps for LPCBridge { .with_context(|| "Fail to init IO region for PM control register")?; let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); - pci_bus - .devices - .insert(0x1F << 3, Arc::new(Mutex::new(self))); + let mut locked_bus = parent_bus.lock().unwrap(); + locked_bus.attach_child(0x1F << 3, Arc::new(Mutex::new(self)))?; Ok(()) } diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index f9d418c1a..b8a1679c7 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -24,7 +24,7 @@ use devices::pci::{ }, le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, }; -use devices::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; +use devices::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; use util::gen_base_func; use util::num_ops::ranges_overlap; @@ -138,8 +138,8 @@ impl PciDevOps for Mch { )?; let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); - pci_bus.devices.insert(0, Arc::new(Mutex::new(self))); + let mut locked_bus = parent_bus.lock().unwrap(); + locked_bus.attach_child(0, Arc::new(Mutex::new(self)))?; Ok(()) } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index b4c1f273f..dbe0d2a4d 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -41,11 +41,9 @@ use devices::pci::msix::{ }; use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, - pci_ext_cap_next, pci_ext_cap_ver, PciBus, PciDevBase, PciDevOps, -}; -use devices::{ - convert_bus_mut, convert_bus_ref, pci::MsiVector, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS, + pci_ext_cap_next, pci_ext_cap_ver, MsiVector, PciBus, PciDevBase, PciDevOps, }; +use devices::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use util::gen_base_func; use util::loop_context::create_new_eventfd; @@ -864,20 +862,11 @@ impl PciDevOps for VfioPciDevice { )?)); Result::with_context(self.register_bars(), || "Failed to register bars")?; - let devfn = self.base.devfn; + let devfn = self.base.devfn as u64; let dev = Arc::new(Mutex::new(self)); let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); - MUT_PCI_BUS!(parent_bus, locked_bus, pci_bus); - let pci_device = pci_bus.devices.get(&devfn); - if pci_device.is_none() { - pci_bus.devices.insert(devfn, dev); - } else { - bail!( - "Devfn {:?} has been used by {:?}", - &devfn, - pci_device.unwrap().lock().unwrap().name() - ); - } + let mut locked_bus = parent_bus.lock().unwrap(); + locked_bus.attach_child(devfn, dev)?; Ok(()) } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 19bd02121..5e8114ec5 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -46,7 +46,7 @@ use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps, PciError, }; -use devices::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; +use devices::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; #[cfg(feature = "virtio_gpu")] use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -1139,7 +1139,7 @@ impl PciDevOps for VirtioPciDevice { .with_context(|| "Failed to realize virtio device")?; let name = self.name(); - let devfn = self.base.devfn; + let devfn = self.base.devfn as u64; let dev = Arc::new(Mutex::new(self)); let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) as u64) @@ -1159,17 +1159,7 @@ impl PciDevOps for VirtioPciDevice { // Register device to pci bus. let bus = parent_bus.upgrade().unwrap(); - MUT_PCI_BUS!(bus, locked_bus, pci_bus); - let pci_device = pci_bus.devices.get(&devfn); - if pci_device.is_none() { - pci_bus.devices.insert(devfn, dev.clone()); - } else { - bail!( - "Devfn {:?} has been used by {:?}", - &devfn, - pci_device.unwrap().lock().unwrap().name() - ); - } + bus.lock().unwrap().attach_child(devfn, dev.clone())?; MigrationManager::register_transport_instance(VirtioPciState::descriptor(), dev, &name); -- Gitee From 3453edaf9549cec2cff7f2789602c88c4186cc3a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 1 Feb 2024 23:09:14 +0800 Subject: [PATCH 1950/2187] devices/pci: remove child_buses/sec_bus/root_bus in pci architecture 1. `child_buses` field of struct `PciBus` refers to the secondary PCI bus extended from bridge devices mounted on the `PciBus`. We can get it from PciBus.child_dev().child_bus(). 2. `sec_bus` field of struct `RootPort` refers to the secondary PCI bus extended from `RootPort`. And it has the same effect as the `child_bus` field of struct `DeviceBase`. 3. `root_bus` field of struct `PciHost` refers to the PCI bus extended from `PciHost`. And it has the same effect as the `child_bus` field of struct `DeviceBase`. Delete them. Signed-off-by: liuxiangdong --- devices/src/misc/ivshmem.rs | 4 +- devices/src/misc/pvpanic.rs | 85 +++++++----------------- devices/src/misc/scream/mod.rs | 4 +- devices/src/pci/bus.rs | 99 +++++++++++++++------------- devices/src/pci/demo_device/mod.rs | 13 ++-- devices/src/pci/host.rs | 37 ++++++----- devices/src/pci/hotplug.rs | 18 ++--- devices/src/pci/mod.rs | 6 +- devices/src/pci/msix.rs | 6 +- devices/src/pci/root_port.rs | 67 +++++++++---------- devices/src/usb/xhci/xhci_pci.rs | 4 +- machine/src/aarch64/pci_host_root.rs | 6 +- machine/src/aarch64/standard.rs | 15 +++-- machine/src/lib.rs | 30 +++++---- machine/src/standard_common/mod.rs | 9 ++- machine/src/x86_64/ich9_lpc.rs | 6 +- machine/src/x86_64/mch.rs | 4 +- machine/src/x86_64/standard.rs | 12 ++-- vfio/src/vfio_pci.rs | 4 +- virtio/src/transport/virtio_pci.rs | 6 +- 20 files changed, 210 insertions(+), 225 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index ae91fcbb6..685fbb9e1 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -22,7 +22,7 @@ use crate::pci::config::{ PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; use crate::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; -use crate::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; +use crate::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; use util::gen_base_func; @@ -45,7 +45,7 @@ impl Ivshmem { pub fn new( name: String, devfn: u8, - parent_bus: Weak>, + parent_bus: Weak>, ram_mem_region: Region, ) -> Self { Self { diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index eae09f889..76d232192 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -27,7 +27,7 @@ use crate::pci::config::{ SUBSYSTEM_VENDOR_ID, SUB_CLASS_CODE, VENDOR_ID, }; use crate::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; -use crate::{convert_bus_mut, convert_bus_ref, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; +use crate::{convert_bus_mut, convert_bus_ref, Bus, Device, DeviceBase, MUT_PCI_BUS, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; use util::gen_base_func; @@ -107,7 +107,7 @@ pub struct PvPanicPci { } impl PvPanicPci { - pub fn new(config: &PvpanicDevConfig, devfn: u8, parent_bus: Weak>) -> Self { + pub fn new(config: &PvpanicDevConfig, devfn: u8, parent_bus: Weak>) -> Self { Self { base: PciDevBase { base: DeviceBase::new(config.id.clone(), false, Some(parent_bus)), @@ -250,7 +250,7 @@ impl PciDevOps for PvPanicPci { mod tests { use super::*; use crate::pci::{host::tests::create_pci_host, le_read_u16, PciHost}; - use crate::{convert_device_mut, Bus}; + use crate::{convert_bus_ref, convert_device_mut, PCI_BUS}; use machine_manager::config::str_slip_to_clap; /// Convert from Arc> to &mut PvPanicPci. @@ -264,7 +264,7 @@ mod tests { fn init_pvpanic_dev(devfn: u8, supported_features: u32, dev_id: &str) -> Arc> { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let config = PvpanicDevConfig { id: dev_id.to_string(), @@ -273,11 +273,10 @@ mod tests { bus: "pcie.0".to_string(), addr: (3, 0), }; - let pvpanic_dev = PvPanicPci::new(&config, devfn, root_bus.clone()); + let pvpanic_dev = PvPanicPci::new(&config, devfn, root_bus); assert_eq!(pvpanic_dev.base.base.id, "pvpanic_test".to_string()); pvpanic_dev.realize().unwrap(); - drop(root_bus); drop(locked_pci_host); pci_host @@ -304,17 +303,17 @@ mod tests { #[test] fn test_pvpanic_attached() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); - let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let pvpanic_dev = root_bus.upgrade().unwrap().lock().unwrap().get_device(0, 7); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, pci_bus); + let pvpanic_dev = pci_bus.get_device(0, 7); + drop(locked_bus); assert!(pvpanic_dev.is_some()); assert_eq!( pvpanic_dev.unwrap().lock().unwrap().name(), "pvpanic_test".to_string() ); - let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "pvpanic_test"); + let info = PciBus::find_attached_bus(&root_bus, "pvpanic_test"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name(), "pcie.0"); @@ -324,16 +323,9 @@ mod tests { #[test] fn test_pvpanic_config() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); - let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let pvpanic_dev = root_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .get_device(0, 7) - .unwrap(); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, pci_bus); + let pvpanic_dev = pci_bus.get_device(0, 7).unwrap(); MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); let info = le_read_u16(&pvpanic.pci_base_mut().config.config, VENDOR_ID as usize) .unwrap_or_else(|_| 0); @@ -362,16 +354,9 @@ mod tests { #[test] fn test_pvpanic_read_features() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); - let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let pvpanic_dev = root_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .get_device(0, 7) - .unwrap(); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, pci_bus); + let pvpanic_dev = pci_bus.get_device(0, 7).unwrap(); MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test read supported_features @@ -391,16 +376,9 @@ mod tests { #[test] fn test_pvpanic_write_panicked() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); - let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let pvpanic_dev = root_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .get_device(0, 7) - .unwrap(); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, pci_bus); + let pvpanic_dev = pci_bus.get_device(0, 7).unwrap(); MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test write panicked event @@ -417,16 +395,9 @@ mod tests { #[test] fn test_pvpanic_write_crashload() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); - let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let pvpanic_dev = root_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .get_device(0, 7) - .unwrap(); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, pci_bus); + let pvpanic_dev = pci_bus.get_device(0, 7).unwrap(); MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test write crashload event @@ -444,15 +415,9 @@ mod tests { fn test_pvpanic_write_unknown() { let pci_host = init_pvpanic_dev(7, PVPANIC_PANICKED | PVPANIC_CRASHLOADED, "pvpanic_test"); let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); - - let pvpanic_dev = root_bus - .upgrade() - .unwrap() - .lock() - .unwrap() - .get_device(0, 7) - .unwrap(); + let root_bus = locked_pci_host.child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, pci_bus); + let pvpanic_dev = pci_bus.get_device(0, 7).unwrap(); MUT_PVPANIC_PCI!(pvpanic_dev, locked_dev, pvpanic); // test write unknown event diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 0c5be725b..254671df2 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -37,7 +37,7 @@ use log::{error, warn}; use self::alsa::AlsaStreamData; use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; -use crate::pci::{PciBus, PciDevOps}; +use crate::{pci::PciDevOps, Bus}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] @@ -534,7 +534,7 @@ impl Scream { Ok(()) } - pub fn realize(&mut self, parent_bus: Weak>) -> Result<()> { + pub fn realize(&mut self, parent_bus: Weak>) -> Result<()> { let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(0), None, diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 6e03f1a1c..7bf02021e 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -23,19 +23,17 @@ use super::{ }; use crate::pci::{to_pcidevops, RootPort}; use crate::{ - convert_device_mut, convert_device_ref, Bus, BusBase, Device, MsiIrqManager, MUT_ROOT_PORT, - PCI_BUS_DEVICE, ROOT_PORT, + convert_bus_mut, convert_bus_ref, convert_device_mut, convert_device_ref, Bus, BusBase, Device, + MsiIrqManager, MUT_ROOT_PORT, PCI_BUS_DEVICE, ROOT_PORT, }; use address_space::Region; use util::gen_base_func; -type DeviceBusInfo = (Arc>, Arc>); +type DeviceBusInfo = (Arc>, Arc>); /// PCI bus structure. pub struct PciBus { pub base: BusBase, - /// Child buses of the bus. - pub child_buses: Vec>>, /// IO region which the parent bridge manages. #[cfg(target_arch = "x86_64")] pub io_region: Region, @@ -83,7 +81,6 @@ impl PciBus { ) -> Self { Self { base: BusBase::new(name), - child_buses: Vec::new(), #[cfg(target_arch = "x86_64")] io_region, mem_region, @@ -139,15 +136,18 @@ impl PciBus { /// /// * `bus` - Bus to find from. /// * `bus_number` - The bus number. - pub fn find_bus_by_num(bus: &Arc>, bus_num: u8) -> Option>> { - let locked_bus = bus.lock().unwrap(); - if locked_bus.number(SECONDARY_BUS_NUM as usize) == bus_num { - return Some((*bus).clone()); + pub fn find_bus_by_num(bus: &Arc>, bus_num: u8) -> Option>> { + PCI_BUS!(bus, locked_bus, pci_bus); + if pci_bus.number(SECONDARY_BUS_NUM as usize) == bus_num { + return Some(bus.clone()); } - if locked_bus.in_range(bus_num) { - for sub_bus in &locked_bus.child_buses { - if let Some(b) = PciBus::find_bus_by_num(sub_bus, bus_num) { - return Some(b); + if pci_bus.in_range(bus_num) { + for dev in pci_bus.child_devices().values() { + let child_bus = dev.lock().unwrap().child_bus(); + if let Some(sub_bus) = child_bus { + if let Some(b) = PciBus::find_bus_by_num(&sub_bus, bus_num) { + return Some(b); + } } } } @@ -160,14 +160,20 @@ impl PciBus { /// /// * `bus` - Bus to find from. /// * `name` - Bus name. - pub fn find_bus_by_name(bus: &Arc>, bus_name: &str) -> Option>> { + pub fn find_bus_by_name( + bus: &Arc>, + bus_name: &str, + ) -> Option>> { let locked_bus = bus.lock().unwrap(); if locked_bus.name().as_str() == bus_name { - return Some((*bus).clone()); + return Some(bus.clone()); } - for sub_bus in &locked_bus.child_buses { - if let Some(b) = PciBus::find_bus_by_name(sub_bus, bus_name) { - return Some(b); + for dev in locked_bus.child_devices().values() { + let child_bus = dev.lock().unwrap().child_bus(); + if let Some(sub_bus) = child_bus { + if let Some(b) = PciBus::find_bus_by_name(&sub_bus, bus_name) { + return Some(b); + } } } None @@ -177,20 +183,22 @@ impl PciBus { /// /// # Arguments /// - /// * `pci_bus` - On which bus to find. + /// * `bus` - On which bus to find. /// * `name` - Device name. - pub fn find_attached_bus(pci_bus: &Arc>, name: &str) -> Option { - // Device is attached in pci_bus. - let locked_bus = pci_bus.lock().unwrap(); + pub fn find_attached_bus(bus: &Arc>, name: &str) -> Option { + // Device is attached in bus. + let locked_bus = bus.lock().unwrap(); for dev in locked_bus.child_devices().values() { if dev.lock().unwrap().name() == name { - return Some((pci_bus.clone(), dev.clone())); + return Some((bus.clone(), dev.clone())); } - } - // Find in child bus. - for bus in &locked_bus.child_buses { - if let Some(found) = PciBus::find_attached_bus(bus, name) { - return Some(found); + + // Find in child bus. + let child_bus = dev.lock().unwrap().child_bus(); + if let Some(sub_bus) = child_bus { + if let Some(found) = PciBus::find_attached_bus(&sub_bus, name) { + return Some(found); + } } } None @@ -202,7 +210,7 @@ impl PciBus { /// /// * `bus` - Bus to detach from. /// * `dev` - Device attached to the bus. - pub fn detach_device(bus: &Arc>, dev: &Arc>) -> Result<()> { + pub fn detach_device(bus: &Arc>, dev: &Arc>) -> Result<()> { PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); pci_dev .unrealize() @@ -223,14 +231,11 @@ impl PciBus { pci_dev .reset(false) .with_context(|| format!("Fail to reset pci dev {}", pci_dev.name()))?; - } - for child_bus in self.child_buses.iter_mut() { - child_bus - .lock() - .unwrap() - .reset() - .with_context(|| "Fail to reset child bus")?; + if let Some(bus) = pci_dev.child_bus() { + MUT_PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.reset().with_context(|| "Fail to reset child bus")?; + } } Ok(()) @@ -287,7 +292,7 @@ mod tests { fn test_find_attached_bus() { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let root_port_config = RootPortConfig { addr: (1, 0), id: "pcie.1".to_string(), @@ -301,20 +306,21 @@ mod tests { pci_dev.realize().unwrap(); // Test device is attached to the root port. - let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); + let bus = + PciBus::find_bus_by_name(&locked_pci_host.child_bus().unwrap(), "pcie.1").unwrap(); let pci_dev = TestPciDevice::new("test2", 12, Arc::downgrade(&bus)); pci_dev.realize().unwrap(); - let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test0"); + let info = PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), "test0"); assert!(info.is_none()); - let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); + let info = PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), "test1"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name(), "pcie.0"); assert_eq!(dev.lock().unwrap().name(), "test1"); - let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test2"); + let info = PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), "test2"); assert!(info.is_some()); let (bus, dev) = info.unwrap(); assert_eq!(bus.lock().unwrap().name(), "pcie.1"); @@ -327,7 +333,7 @@ mod tests { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let root_port_config = RootPortConfig { id: "pcie.1".to_string(), @@ -337,18 +343,19 @@ mod tests { let root_port = RootPort::new(root_port_config, root_bus.clone()); root_port.realize().unwrap(); - let bus = PciBus::find_bus_by_name(&locked_pci_host.root_bus, "pcie.1").unwrap(); + let bus = + PciBus::find_bus_by_name(&locked_pci_host.child_bus().unwrap(), "pcie.1").unwrap(); let pci_dev = TestPciDevice::new("test1", 0, Arc::downgrade(&bus)); let dev_ops: Arc> = Arc::new(Mutex::new(pci_dev.clone())); pci_dev.realize().unwrap(); - let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); + let info = PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), "test1"); assert!(info.is_some()); let res = PciBus::detach_device(&bus, &dev_ops); assert!(res.is_ok()); - let info = PciBus::find_attached_bus(&locked_pci_host.root_bus, "test1"); + let info = PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), "test1"); assert!(info.is_none()); clean_pcidevops_type(); diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 483bfbab9..77fcfd30c 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -33,13 +33,8 @@ pub mod dpy_device; pub mod gpu_device; pub mod kbd_pointer_device; -use std::{ - sync::Mutex, - sync::{ - atomic::{AtomicU16, Ordering}, - Arc, Weak, - }, -}; +use std::sync::atomic::{AtomicU16, Ordering}; +use std::sync::{Arc, Mutex, Weak}; use anyhow::Result; use clap::Parser; @@ -54,7 +49,7 @@ use crate::pci::demo_device::{ kbd_pointer_device::DemoKbdMouse, }; use crate::pci::{init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; -use crate::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; +use crate::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; use address_space::{AddressSpace, GuestAddress, Region, RegionOps}; use machine_manager::config::{get_pci_df, valid_id}; use util::gen_base_func; @@ -95,7 +90,7 @@ impl DemoDev { cfg: DemoDevConfig, devfn: u8, sys_mem: Arc, - parent_bus: Weak>, + parent_bus: Weak>, ) -> Self { // You can choose different device function based on the parameter of device_type. let device_type = cfg.device_type.clone().unwrap_or_default(); diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 4d45a4f4f..6f1a25599 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -20,7 +20,7 @@ use crate::pci::{bus::PciBus, to_pcidevops, PCI_PIN_NUM, PCI_SLOT_MAX}; #[cfg(target_arch = "x86_64")] use crate::pci::{le_read_u32, le_write_u32}; use crate::sysbus::{SysBusDevBase, SysBusDevOps}; -use crate::{Bus, Device, DeviceBase, PCI_BUS_DEVICE}; +use crate::{Device, DeviceBase, PCI_BUS_DEVICE}; use acpi::{ AmlActiveLevel, AmlAddressSpaceDecode, AmlAnd, AmlArg, AmlBuilder, AmlCacheable, AmlCreateDWordField, AmlDWord, AmlDWordDesc, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlElse, @@ -54,7 +54,6 @@ const ECAM_OFFSET_MASK: u64 = 0xfff; #[derive(Clone)] pub struct PciHost { base: SysBusDevBase, - pub root_bus: Arc>, #[cfg(target_arch = "x86_64")] config_addr: u32, pcie_ecam_range: (u64, u64), @@ -96,9 +95,10 @@ impl PciHost { io_region, mem_region, ); + let mut base = SysBusDevBase::default(); + base.base.child = Some(Arc::new(Mutex::new(root_bus))); PciHost { - base: SysBusDevBase::default(), - root_bus: Arc::new(Mutex::new(root_bus)), + base, #[cfg(target_arch = "x86_64")] config_addr: 0, pcie_ecam_range, @@ -112,15 +112,20 @@ impl PciHost { } pub fn find_device(&self, bus_num: u8, devfn: u8) -> Option>> { - let locked_root_bus = self.root_bus.lock().unwrap(); + let root_bus = self.child_bus().unwrap(); + let locked_root_bus = root_bus.lock().unwrap(); if bus_num == 0 { let dev = locked_root_bus.child_dev(devfn as u64)?; return Some(dev.clone()); } - for bus in &locked_root_bus.child_buses { - if let Some(b) = PciBus::find_bus_by_num(bus, bus_num) { - let dev = b.lock().unwrap().child_dev(devfn as u64)?.clone(); - return Some(dev); + + for dev in locked_root_bus.child_devices().values() { + let child_bus = dev.lock().unwrap().child_bus(); + if let Some(bus) = child_bus { + if let Some(b) = PciBus::find_bus_by_num(&bus, bus_num) { + let dev = b.lock().unwrap().child_dev(devfn as u64)?.clone(); + return Some(dev); + } } } None @@ -277,7 +282,8 @@ impl SysBusDevOps for PciHost { } fn reset(&mut self) -> Result<()> { - for dev in self.root_bus.lock().unwrap().child_devices().values() { + let root_bus = self.child_bus().unwrap(); + for dev in root_bus.lock().unwrap().child_devices().values() { PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); pci_dev .reset(true) @@ -579,7 +585,7 @@ pub mod tests { register_pcidevops_type::().unwrap(); let pci_host = create_pci_host(); - let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); + let root_bus = Arc::downgrade(&pci_host.lock().unwrap().child_bus().unwrap()); let pio_addr_ops = PciHost::build_pio_addr_ops(pci_host.clone()); let pio_data_ops = PciHost::build_pio_data_ops(pci_host.clone()); let root_port_config = RootPortConfig { @@ -665,7 +671,8 @@ pub mod tests { register_pcidevops_type::().unwrap(); let pci_host = create_pci_host(); - let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap(); + let weak_root_bus = Arc::downgrade(&root_bus); let mmconfig_region_ops = PciHost::build_mmconfig_ops(pci_host.clone()); let root_port_config = RootPortConfig { @@ -673,7 +680,7 @@ pub mod tests { id: "pcie.1".to_string(), ..Default::default() }; - let mut root_port = RootPort::new(root_port_config, root_bus.clone()); + let mut root_port = RootPort::new(root_port_config, weak_root_bus.clone()); root_port.write_config(SECONDARY_BUS_NUM as usize, &[1]); root_port.realize().unwrap(); let root_port_config = RootPortConfig { @@ -681,11 +688,11 @@ pub mod tests { id: "pcie.2".to_string(), ..Default::default() }; - let mut root_port = RootPort::new(root_port_config, root_bus.clone()); + let mut root_port = RootPort::new(root_port_config, weak_root_bus); root_port.write_config(SECONDARY_BUS_NUM as usize, &[2]); root_port.realize().unwrap(); - let bus = PciBus::find_bus_by_name(&pci_host.lock().unwrap().root_bus, "pcie.2").unwrap(); + let bus = PciBus::find_bus_by_name(&root_bus, "pcie.2").unwrap(); let pci_dev = TestPciDevice::new("PCI device", 8, Arc::downgrade(&bus)); pci_dev.realize().unwrap(); diff --git a/devices/src/pci/hotplug.rs b/devices/src/pci/hotplug.rs index 862956be0..7f13de932 100644 --- a/devices/src/pci/hotplug.rs +++ b/devices/src/pci/hotplug.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use crate::pci::PciBus; -use crate::{Bus, Device}; +use crate::{convert_bus_ref, Bus, Device, PCI_BUS}; pub trait HotplugOps: Send { /// Plug device, usually called when hot plug device in device_add. @@ -41,14 +41,14 @@ pub trait HotplugOps: Send { /// Return Error if /// * No hot plug controller found. /// * Device plug failed. -pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> Result<()> { - let locked_bus = bus.lock().unwrap(); - if let Some(hpc) = locked_bus.hotplug_controller.as_ref() { +pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> Result<()> { + PCI_BUS!(bus, locked_bus, pci_bus); + if let Some(hpc) = pci_bus.hotplug_controller.as_ref() { hpc.upgrade().unwrap().lock().unwrap().plug(dev) } else { bail!( "No hot plug controller found for bus {} when plug", - locked_bus.name() + pci_bus.name() ); } } @@ -66,18 +66,18 @@ pub fn handle_plug(bus: &Arc>, dev: &Arc>) -> Re /// * No hot plug controller found. /// * Device unplug request failed. pub fn handle_unplug_pci_request( - bus: &Arc>, + bus: &Arc>, dev: &Arc>, ) -> Result<()> { - let locked_bus = bus.lock().unwrap(); - let hpc = locked_bus + PCI_BUS!(bus, locked_bus, pci_bus); + let hpc = pci_bus .hotplug_controller .as_ref() .cloned() .with_context(|| { format!( "No hot plug controller found for bus {} when unplug request", - locked_bus.name() + pci_bus.name() ) })?; // No need to hold the lock. diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index b7faa7923..cbb95263c 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -420,7 +420,7 @@ mod tests { } impl TestPciDevice { - pub fn new(name: &str, devfn: u8, parent_bus: Weak>) -> Self { + pub fn new(name: &str, devfn: u8, parent_bus: Weak>) -> Self { Self { base: PciDevBase { base: DeviceBase::new(name.to_string(), false, Some(parent_bus)), @@ -529,12 +529,12 @@ mod tests { None, ) .unwrap(); - let parent_bus: Arc> = Arc::new(Mutex::new(PciBus::new( + let parent_bus = Arc::new(Mutex::new(PciBus::new( String::from("test bus"), #[cfg(target_arch = "x86_64")] Region::init_container_region(1 << 16, "parent_bus"), sys_mem.root().clone(), - ))); + ))) as Arc>; let dev = TestPciDevice::new("PCI device", 0, Arc::downgrade(&parent_bus)); assert_eq!(dev.set_dev_id(1, 2), 258); diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index e82b7c171..70eb603d7 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -637,13 +637,13 @@ mod tests { use super::*; use crate::pci::config::{PciConfig, PCI_CONFIG_SPACE_SIZE}; use crate::pci::host::tests::create_pci_host; - use crate::DeviceBase; + use crate::{Device, DeviceBase}; #[test] fn test_init_msix() { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let mut base = PciDevBase { base: DeviceBase::new("msix".to_string(), false, Some(root_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), @@ -748,7 +748,7 @@ mod tests { fn test_write_config() { let pci_host = create_pci_host(); let locked_pci_host = pci_host.lock().unwrap(); - let root_bus = Arc::downgrade(&locked_pci_host.root_bus); + let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let mut base = PciDevBase { base: DeviceBase::new("msix".to_string(), false, Some(root_bus)), config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index ea6b43b15..aceea6fab 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -94,7 +94,6 @@ struct RootPortState { pub struct RootPort { base: PciDevBase, port_num: u8, - sec_bus: Arc>, #[cfg(target_arch = "x86_64")] io_region: Region, mem_region: Region, @@ -110,26 +109,27 @@ impl RootPort { /// /// * `cfg` - Root port config. /// * `parent_bus` - Weak reference to the parent bus. - pub fn new(cfg: RootPortConfig, parent_bus: Weak>) -> Self { + pub fn new(cfg: RootPortConfig, parent_bus: Weak>) -> Self { let devfn = cfg.addr.0 << 3 | cfg.addr.1; #[cfg(target_arch = "x86_64")] let io_region = Region::init_container_region(1 << 16, "RootPortIo"); let mem_region = Region::init_container_region(u64::max_value(), "RootPortMem"); - let sec_bus = Arc::new(Mutex::new(PciBus::new( + let child_bus = Arc::new(Mutex::new(PciBus::new( cfg.id.clone(), #[cfg(target_arch = "x86_64")] io_region.clone(), mem_region.clone(), ))); + let mut dev_base = DeviceBase::new(cfg.id, true, Some(parent_bus)); + dev_base.child = Some(child_bus); Self { base: PciDevBase { - base: DeviceBase::new(cfg.id, true, Some(parent_bus)), + base: dev_base, config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), devfn, }, port_num: cfg.port, - sec_bus, #[cfg(target_arch = "x86_64")] io_region, mem_region, @@ -222,7 +222,8 @@ impl RootPort { // Store device in a temp vector and unlock the bus. // If the device unrealize called when the bus is locked, a deadlock occurs. // This is because the device unrealize also requires the bus lock. - let devices = self.sec_bus.lock().unwrap().child_devices().clone(); + let bus = self.child_bus().unwrap(); + let devices = bus.lock().unwrap().child_devices(); for dev in devices.values() { PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); if let Err(e) = pci_dev.unrealize() { @@ -234,7 +235,7 @@ impl RootPort { // Send QMP event for successful hot unplugging. send_device_deleted_msg(&locked_dev.name()); } - self.sec_bus.lock().unwrap().bus_base_mut().children.clear(); + bus.lock().unwrap().bus_base_mut().children.clear(); } fn register_region(&mut self) { @@ -398,37 +399,30 @@ impl PciDevOps for RootPort { self.base.devfn, )?; - let bus = parent_bus.upgrade().unwrap(); - MUT_PCI_BUS!(bus, locked_bus, pci_bus); + let arc_parent_bus = parent_bus.upgrade().unwrap(); + MUT_PCI_BUS!(arc_parent_bus, locked_parent_bus, parent_pci_bus); + let child_bus = self.child_bus().unwrap(); + MUT_PCI_BUS!(child_bus, locked_child_bus, child_pci_bus); #[cfg(target_arch = "x86_64")] - pci_bus + parent_pci_bus .io_region - .add_subregion(self.sec_bus.lock().unwrap().io_region.clone(), 0) + .add_subregion(child_pci_bus.io_region.clone(), 0) .with_context(|| "Failed to register subregion in I/O space.")?; - pci_bus + parent_pci_bus .mem_region - .add_subregion(self.sec_bus.lock().unwrap().mem_region.clone(), 0) + .add_subregion(child_pci_bus.mem_region.clone(), 0) .with_context(|| "Failed to register subregion in memory space.")?; + drop(locked_child_bus); let name = self.name(); let root_port = Arc::new(Mutex::new(self)); - #[allow(unused_mut)] - let mut locked_root_port = root_port.lock().unwrap(); - locked_root_port.sec_bus.lock().unwrap().base.parent = - Some(Arc::downgrade(&root_port) as Weak>); - locked_root_port.sec_bus.lock().unwrap().hotplug_controller = + let locked_root_port = root_port.lock().unwrap(); + let child_bus = locked_root_port.child_bus().unwrap(); + MUT_PCI_BUS!(child_bus, locked_child_bus, child_pci_bus); + child_pci_bus.base.parent = Some(Arc::downgrade(&root_port) as Weak>); + child_pci_bus.hotplug_controller = Some(Arc::downgrade(&root_port) as Weak>); - let pci_device = pci_bus.child_dev(locked_root_port.base.devfn as u64); - if pci_device.is_none() { - pci_bus.child_buses.push(locked_root_port.sec_bus.clone()); - pci_bus.attach_child(locked_root_port.base.devfn as u64, root_port.clone())?; - } else { - bail!( - "Devfn {:?} has been used by {:?}", - locked_root_port.base.devfn, - pci_device.unwrap().lock().unwrap().name() - ); - } + parent_pci_bus.attach_child(locked_root_port.base.devfn as u64, root_port.clone())?; // Need to drop locked_root_port in order to register root_port instance. drop(locked_root_port); MigrationManager::register_device_instance(RootPortState::descriptor(), root_port, &name); @@ -515,11 +509,11 @@ impl PciDevOps for RootPort { /// Only set slot status to on, and no other device reset actions are implemented. fn reset(&mut self, reset_child_device: bool) -> Result<()> { if reset_child_device { - self.sec_bus - .lock() - .unwrap() + let child_bus = self.child_bus().unwrap(); + MUT_PCI_BUS!(child_bus, locked_child_bus, child_pci_bus); + child_pci_bus .reset() - .with_context(|| "Fail to reset sec_bus in root port")?; + .with_context(|| "Fail to reset child_bus in root port")?; } else { let cap_offset = self.base.config.pci_express_cap_offset; le_write_u16( @@ -657,7 +651,8 @@ impl HotplugOps for RootPort { PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); let devfn = pci_dev.pci_base().devfn as u64; pci_dev.unrealize()?; - self.sec_bus.lock().unwrap().detach_child(devfn)?; + let child_bus = self.child_bus().unwrap(); + child_bus.lock().unwrap().detach_child(devfn)?; Ok(()) } } @@ -708,7 +703,7 @@ mod tests { #[test] fn test_read_config() { let pci_host = create_pci_host(); - let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); + let root_bus = Arc::downgrade(&pci_host.lock().unwrap().child_bus().unwrap()); let root_port_config = RootPortConfig { addr: (1, 0), id: "pcie.1".to_string(), @@ -727,7 +722,7 @@ mod tests { #[test] fn test_write_config() { let pci_host = create_pci_host(); - let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus); + let root_bus = Arc::downgrade(&pci_host.lock().unwrap().child_bus().unwrap()); let root_port_config = RootPortConfig { addr: (1, 0), id: "pcie.1".to_string(), diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index e8b2e6464..82d990ea3 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -34,7 +34,7 @@ use crate::pci::config::{ }; use crate::pci::{init_intx, init_msix, le_write_u16, PciBus, PciDevBase, PciDevOps}; use crate::usb::UsbDevice; -use crate::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; +use crate::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; use address_space::{AddressRange, AddressSpace, Region, RegionIoEventFd}; use machine_manager::config::{get_pci_df, valid_id}; use machine_manager::event_loop::register_event_helper; @@ -108,7 +108,7 @@ impl XhciPciDevice { pub fn new( config: &XhciConfig, devfn: u8, - parent_bus: Weak>, + parent_bus: Weak>, mem_space: &Arc, ) -> Self { Self { diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index ec50c954c..d3566f3c2 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -18,8 +18,8 @@ use devices::pci::config::{ PciConfig, CLASS_CODE_HOST_BRIDGE, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; -use devices::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; -use devices::{Device, DeviceBase}; +use devices::pci::{le_write_u16, PciDevBase, PciDevOps}; +use devices::{Bus, Device, DeviceBase}; use util::gen_base_func; const DEVICE_ID_PCIE_HOST: u16 = 0x0008; @@ -30,7 +30,7 @@ pub struct PciHostRoot { } impl PciHostRoot { - pub fn new(parent_bus: Weak>) -> Self { + pub fn new(parent_bus: Weak>) -> Self { Self { base: PciDevBase { base: DeviceBase::new("PCI Host Root".to_string(), false, Some(parent_bus)), diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 76b3b1405..37d91f30e 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -48,9 +48,11 @@ use devices::legacy::{ }; #[cfg(feature = "ramfb")] use devices::legacy::{Ramfb, RamfbConfig}; -use devices::pci::{PciDevOps, PciHost, PciIntxState}; +use devices::pci::{PciBus, PciDevOps, PciHost, PciIntxState}; use devices::sysbus::{to_sysbusdevops, SysBusDevType}; -use devices::{ICGICConfig, ICGICv3Config, GIC_IRQ_MAX, SYS_BUS_DEVICE}; +use devices::{ + convert_bus_mut, Device, ICGICConfig, ICGICv3Config, GIC_IRQ_MAX, MUT_PCI_BUS, SYS_BUS_DEVICE, +}; use hypervisor::kvm::aarch64::*; use hypervisor::kvm::*; #[cfg(feature = "ramfb")] @@ -318,7 +320,7 @@ impl StdMachine { impl StdMachineOps for StdMachine { fn init_pci_host(&self) -> Result<()> { - let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); + let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().child_bus().unwrap()); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); let mmconfig_region = Region::init_io_region( MEM_LAYOUT[LayoutEntryType::HighPcieEcam as usize].1, @@ -432,16 +434,17 @@ impl MachineOps for StdMachine { self.base.irq_chip = Some(locked_hypervisor.create_interrupt_controller(&intc_conf)?); self.base.irq_chip.as_ref().unwrap().realize()?; - let root_bus = &self.pci_host.lock().unwrap().root_bus; + let root_bus = &self.pci_host.lock().unwrap().child_bus().unwrap(); + MUT_PCI_BUS!(root_bus, locked_bus, root_pci_bus); let irq_manager = locked_hypervisor.create_irq_manager()?; - root_bus.lock().unwrap().msi_irq_manager = irq_manager.msi_irq_manager; + root_pci_bus.msi_irq_manager = irq_manager.msi_irq_manager; let line_irq_manager = irq_manager.line_irq_manager; if let Some(line_irq_manager) = line_irq_manager.clone() { let irq_state = Some(Arc::new(Mutex::new(PciIntxState::new( IRQ_MAP[IrqEntryType::Pcie as usize].0 as u32, line_irq_manager.clone(), )))); - root_bus.lock().unwrap().intx_state = irq_state; + root_pci_bus.intx_state = irq_state; } else { return Err(anyhow!( "Failed to create intx state: legacy irq manager is none." diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 34a78be1d..1cf7d3711 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -77,7 +77,7 @@ use devices::usb::UsbDevice; use devices::InterruptController; use devices::ScsiBus::get_scsi_key; use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; -use devices::{Bus, Device, PCI_BUS_DEVICE, SYS_BUS_DEVICE}; +use devices::{convert_bus_ref, Bus, Device, PCI_BUS, PCI_BUS_DEVICE, SYS_BUS_DEVICE}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; @@ -931,8 +931,10 @@ pub trait MachineOps: MachineLifecycle { check_arg_exist!(("bus", dev_cfg.bus), ("addr", dev_cfg.addr)); let bdf = PciBdf::new(dev_cfg.bus.clone().unwrap(), dev_cfg.addr.unwrap()); let multi_func = dev_cfg.multifunction.unwrap_or_default(); - let root_bus = self.get_pci_host()?.lock().unwrap().root_bus.clone(); - let msi_irq_manager = root_bus.lock().unwrap().msi_irq_manager.clone(); + let root_bus = self.get_pci_host()?.lock().unwrap().child_bus().unwrap(); + PCI_BUS!(root_bus, locked_bus, root_pci_bus); + let msi_irq_manager = root_pci_bus.msi_irq_manager.clone(); + drop(locked_bus); let need_irqfd = msi_irq_manager.as_ref().unwrap().irqfd_enable(); self.add_virtio_pci_device(&dev_cfg.id, &bdf, device, multi_func, need_irqfd) .with_context(|| "Failed to add pci fs device")?; @@ -997,7 +999,9 @@ pub trait MachineOps: MachineLifecycle { if name.is_empty() { bail!("Device id is empty"); } - if PciBus::find_attached_bus(&pci_host.lock().unwrap().root_bus, name).is_some() { + if PciBus::find_attached_bus(&pci_host.lock().unwrap().child_bus().unwrap(), name) + .is_some() + { bail!("Device id {} existed", name); } if self.check_id_existed_in_xhci(name).unwrap_or_default() { @@ -1446,9 +1450,9 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } - fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> Result<(u8, Weak>)> { + fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> Result<(u8, Weak>)> { let pci_host = self.get_pci_host()?; - let bus = pci_host.lock().unwrap().root_bus.clone(); + let bus = pci_host.lock().unwrap().child_bus().unwrap().clone(); let pci_bus = PciBus::find_bus_by_name(&bus, &bdf.bus); if pci_bus.is_none() { bail!("Parent bus :{} not found", &bdf.bus); @@ -1463,7 +1467,7 @@ pub trait MachineOps: MachineLifecycle { let bdf = PciBdf::new(dev_cfg.bus.clone(), dev_cfg.addr); let (_, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let pci_host = self.get_pci_host()?; - let bus = pci_host.lock().unwrap().root_bus.clone(); + let bus = pci_host.lock().unwrap().child_bus().unwrap().clone(); if PciBus::find_bus_by_name(&bus, &dev_cfg.id).is_some() { bail!("ID {} already exists.", &dev_cfg.id); } @@ -1504,7 +1508,7 @@ pub trait MachineOps: MachineLifecycle { fn reset_bus(&mut self, dev_id: &str) -> Result<()> { let pci_host = self.get_pci_host()?; let locked_pci_host = pci_host.lock().unwrap(); - let bus = PciBus::find_attached_bus(&locked_pci_host.root_bus, dev_id) + let bus = PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), dev_id) .with_context(|| format!("Bus not found, dev id {}", dev_id))? .0; let locked_bus = bus.lock().unwrap(); @@ -1520,7 +1524,8 @@ pub trait MachineOps: MachineLifecycle { let name = locked_dev.name(); drop(locked_dev); let mut devfn = None; - let locked_bus = locked_pci_host.root_bus.lock().unwrap(); + let bus = locked_pci_host.child_bus().unwrap(); + let locked_bus = bus.lock().unwrap(); for (id, dev) in &locked_bus.child_devices() { if dev.lock().unwrap().name() == name { devfn = Some(*id); @@ -1710,9 +1715,10 @@ pub trait MachineOps: MachineLifecycle { let bdf = get_pci_bdf(cfg_args).ok()?; let devfn = (bdf.addr.0 << 3) + bdf.addr.1; let pci_host = self.get_pci_host().ok()?; - let root_bus = pci_host.lock().unwrap().root_bus.clone(); - if let Some(pci_bus) = PciBus::find_bus_by_name(&root_bus, &bdf.bus) { - return pci_bus.lock().unwrap().get_device(0, devfn); + let root_bus = pci_host.lock().unwrap().child_bus().unwrap().clone(); + if let Some(bus) = PciBus::find_bus_by_name(&root_bus, &bdf.bus) { + PCI_BUS!(bus, locked_bus, pci_bus); + return pci_bus.get_device(0, devfn); } else { return None; } diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 6818dce32..de61e004f 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -55,6 +55,7 @@ use devices::legacy::FwCfgOps; use devices::misc::scream::set_record_authority; use devices::pci::hotplug::{handle_plug, handle_unplug_pci_request}; use devices::pci::{PciBus, PciHost}; +use devices::Device; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_config; #[cfg(target_arch = "aarch64")] @@ -1298,7 +1299,9 @@ impl DeviceInterface for StdMachine { // It's safe to call get_pci_host().unwrap() because it has been checked before. let locked_pci_host = self.get_pci_host().unwrap().lock().unwrap(); - if let Some((bus, dev)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &args.id) { + if let Some((bus, dev)) = + PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), &args.id) + { match handle_plug(&bus, &dev) { Ok(()) => Response::create_empty_response(), Err(e) => { @@ -1335,7 +1338,9 @@ impl DeviceInterface for StdMachine { }; let locked_pci_host = pci_host.lock().unwrap(); - if let Some((bus, dev)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &device_id) { + if let Some((bus, dev)) = + PciBus::find_attached_bus(&locked_pci_host.child_bus().unwrap(), &device_id) + { return match handle_unplug_pci_request(&bus, &dev) { Ok(()) => { let locked_dev = dev.lock().unwrap(); diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index 1507fe8bf..e93f85e41 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -26,8 +26,8 @@ use devices::pci::config::{ PciConfig, CLASS_CODE_ISA_BRIDGE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID, }; -use devices::pci::{le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps}; -use devices::{Device, DeviceBase}; +use devices::pci::{le_write_u16, le_write_u32, PciDevBase, PciDevOps}; +use devices::{Bus, Device, DeviceBase}; use util::byte_code::ByteCode; use util::gen_base_func; use util::num_ops::ranges_overlap; @@ -57,7 +57,7 @@ pub struct LPCBridge { impl LPCBridge { pub fn new( - parent_bus: Weak>, + parent_bus: Weak>, sys_io: Arc, reset_req: Arc, shutdown_req: Arc, diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index b8a1679c7..9ec0d110d 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -24,7 +24,7 @@ use devices::pci::{ }, le_read_u64, le_write_u16, PciBus, PciDevBase, PciDevOps, }; -use devices::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; +use devices::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; use util::gen_base_func; use util::num_ops::ranges_overlap; @@ -51,7 +51,7 @@ pub struct Mch { impl Mch { pub fn new( - parent_bus: Weak>, + parent_bus: Weak>, mmconfig_region: Region, mmconfig_ops: RegionOps, ) -> Self { diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index c9c80f79c..98ab024ac 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -36,7 +36,8 @@ use devices::legacy::{ error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, }; -use devices::pci::{PciDevOps, PciHost}; +use devices::pci::{PciBus, PciDevOps, PciHost}; +use devices::{convert_bus_mut, Device, MUT_PCI_BUS}; use hypervisor::kvm::x86_64::*; use hypervisor::kvm::*; #[cfg(feature = "gtk")] @@ -179,7 +180,7 @@ impl StdMachine { } fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { - let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); + let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().child_bus().unwrap()); let ich = ich9_lpc::LPCBridge::new( root_bus, self.base.sys_io.clone(), @@ -235,7 +236,7 @@ impl StdMachine { impl StdMachineOps for StdMachine { fn init_pci_host(&self) -> Result<()> { - let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().root_bus); + let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().child_bus().unwrap()); let mmconfig_region_ops = PciHost::build_mmconfig_ops(self.pci_host.clone()); let mmconfig_region = Region::init_io_region( MEM_LAYOUT[LayoutEntryType::PcieEcam as usize].1, @@ -394,9 +395,10 @@ impl MachineOps for StdMachine { let mut locked_hypervisor = hypervisor.lock().unwrap(); locked_hypervisor.create_interrupt_controller()?; - let root_bus = &self.pci_host.lock().unwrap().root_bus; + let child_bus = self.pci_host.lock().unwrap().child_bus().unwrap(); + MUT_PCI_BUS!(child_bus, locked_bus, pci_bus); let irq_manager = locked_hypervisor.create_irq_manager()?; - root_bus.lock().unwrap().msi_irq_manager = irq_manager.msi_irq_manager; + pci_bus.msi_irq_manager = irq_manager.msi_irq_manager; self.base.sysbus.lock().unwrap().irq_manager = irq_manager.line_irq_manager; Ok(()) diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index dbe0d2a4d..3ae3cae8f 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -43,7 +43,7 @@ use devices::pci::{ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, pci_ext_cap_id, pci_ext_cap_next, pci_ext_cap_ver, MsiVector, PciBus, PciDevBase, PciDevOps, }; -use devices::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; +use devices::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use util::gen_base_func; use util::loop_context::create_new_eventfd; @@ -125,7 +125,7 @@ impl VfioPciDevice { vfio_device: Arc>, devfn: u8, name: String, - parent_bus: Weak>, + parent_bus: Weak>, multi_func: bool, mem_as: Arc, ) -> Self { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 5e8114ec5..ec3209048 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -46,7 +46,7 @@ use devices::pci::{ config::PciConfig, init_intx, init_msix, init_multifunction, le_write_u16, le_write_u32, PciBus, PciDevBase, PciDevOps, PciError, }; -use devices::{convert_bus_ref, Device, DeviceBase, PCI_BUS}; +use devices::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; #[cfg(feature = "virtio_gpu")] use machine_manager::config::VIRTIO_GPU_ENABLE_BAR0_SIZE; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -325,7 +325,7 @@ impl VirtioPciDevice { devfn: u8, sys_mem: Arc, device: Arc>, - parent_bus: Weak>, + parent_bus: Weak>, multi_func: bool, need_irqfd: bool, ) -> Self { @@ -1458,7 +1458,7 @@ mod tests { 0, sys_mem, virtio_dev.clone(), - Arc::downgrade(&parent_bus), + Arc::downgrade(&(parent_bus.clone() as Arc>)), multi_func, false, ); -- Gitee From ead8f492489bb7b30b640e830ebb906005ac570c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 12 Jul 2024 22:49:32 +0800 Subject: [PATCH 1951/2187] devices: unified bus/device reset function Move reset function to Bus/Device trait to unify reset function: 1. Bus reset means all devices attached to this bus should reset. 2. Device reset is the device's own logic. Signed-off-by: liuxiangdong --- devices/src/legacy/fwcfg.rs | 20 +++++----- devices/src/legacy/pflash.rs | 24 ++++++------ devices/src/legacy/ramfb.rs | 10 ++--- devices/src/legacy/rtc.rs | 14 +++---- devices/src/lib.rs | 16 ++++++++ devices/src/pci/bus.rs | 40 +++++++++---------- devices/src/pci/demo_device/mod.rs | 9 ++--- devices/src/pci/host.rs | 24 ++++++------ devices/src/pci/mod.rs | 5 --- devices/src/pci/root_port.rs | 62 +++++++++++++++--------------- devices/src/sysbus/mod.rs | 4 -- devices/src/usb/xhci/xhci_pci.rs | 16 ++++---- machine/src/lib.rs | 30 +++++++-------- vfio/src/vfio_pci.rs | 12 +++--- virtio/src/transport/virtio_pci.rs | 26 ++++++------- 15 files changed, 157 insertions(+), 155 deletions(-) diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index dfd20dfb1..c60164974 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -936,6 +936,11 @@ impl FwCfgOps for FwCfgMem { #[cfg(target_arch = "aarch64")] impl Device for FwCfgMem { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); + Ok(()) + } } #[cfg(target_arch = "aarch64")] @@ -992,11 +997,6 @@ impl SysBusDevOps for FwCfgMem { .set_sys(-1, region_base, region_size, region_name); Ok(()) } - - fn reset(&mut self) -> Result<()> { - self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); - Ok(()) - } } #[allow(clippy::upper_case_acronyms)] @@ -1095,6 +1095,11 @@ impl FwCfgOps for FwCfgIO { #[cfg(target_arch = "x86_64")] impl Device for FwCfgIO { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); + Ok(()) + } } #[cfg(target_arch = "x86_64")] @@ -1154,11 +1159,6 @@ impl SysBusDevOps for FwCfgIO { .set_sys(-1, region_base, region_size, region_name); Ok(()) } - - fn reset(&mut self) -> Result<()> { - self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); - Ok(()) - } } pub trait FwCfgOps: Send + Sync { diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index c12fb251d..c4b1b1deb 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -699,6 +699,18 @@ impl PFlash { impl Device for PFlash { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.rom + .as_ref() + .unwrap() + .set_rom_device_romd(true) + .with_context(|| "Fail to set PFlash rom region read only")?; + self.cmd = 0x00; + self.write_cycle = 0; + self.status = 0x80; + Ok(()) + } } impl SysBusDevOps for PFlash { @@ -899,18 +911,6 @@ impl SysBusDevOps for PFlash { .set_sys(0, region_base, region_size, region_name); Ok(()) } - - fn reset(&mut self) -> Result<()> { - self.rom - .as_ref() - .unwrap() - .set_rom_device_romd(true) - .with_context(|| "Fail to set PFlash rom region read only")?; - self.cmd = 0x00; - self.write_cycle = 0; - self.status = 0x80; - Ok(()) - } } impl AmlBuilder for PFlash { diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 0394c7b2c..7bb501b5e 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -260,6 +260,11 @@ impl Ramfb { impl Device for Ramfb { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.ramfb_state.reset_ramfb_state(); + Ok(()) + } } impl SysBusDevOps for Ramfb { @@ -274,11 +279,6 @@ impl SysBusDevOps for Ramfb { error!("Ramfb can not be written!"); false } - - fn reset(&mut self) -> Result<()> { - self.ramfb_state.reset_ramfb_state(); - Ok(()) - } } impl AmlBuilder for Ramfb { diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index c24e27525..6860a1e28 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -346,6 +346,13 @@ impl RTC { impl Device for RTC { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.cmos_data.fill(0); + self.init_rtc_reg(); + self.set_memory(self.mem_size, self.gap_start); + Ok(()) + } } impl SysBusDevOps for RTC { @@ -372,13 +379,6 @@ impl SysBusDevOps for RTC { self.write_data(data) } } - - fn reset(&mut self) -> Result<()> { - self.cmos_data.fill(0); - self.init_rtc_reg(); - self.set_memory(self.mem_size, self.gap_start); - Ok(()) - } } impl AmlBuilder for RTC { diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 1cb361b8a..98aea03fb 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -105,6 +105,10 @@ pub trait Device: Any + AsAny + Send + Sync { fn child_bus(&self) -> Option>> { self.device_base().child.clone() } + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + Ok(()) + } } /// Macro `convert_device_ref!`: Convert from Arc> to &$device_type. @@ -227,6 +231,18 @@ pub trait Bus: Any + AsAny + Send + Sync { Ok(()) } + + /// Bus reset means that all devices attached to this bus should reset. + fn reset(&self) -> Result<()> { + for dev in self.child_devices().values() { + let mut locked_dev = dev.lock().unwrap(); + locked_dev + .reset(true) + .with_context(|| format!("Failed to reset device {}", locked_dev.name()))?; + } + + Ok(()) + } } /// Macro `convert_bus_ref!`: Convert from Arc> to &$bus_type. diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 7bf02021e..0ee811165 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -46,10 +46,6 @@ pub struct PciBus { pub msi_irq_manager: Option>, } -impl Bus for PciBus { - gen_base_func!(bus_base, bus_base_mut, BusBase, base); -} - /// Convert from Arc> to &mut PciBus. #[macro_export] macro_rules! MUT_PCI_BUS { @@ -66,6 +62,26 @@ macro_rules! PCI_BUS { }; } +impl Bus for PciBus { + gen_base_func!(bus_base, bus_base_mut, BusBase, base); + + fn reset(&self) -> Result<()> { + for dev in self.child_devices().values() { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + pci_dev + .reset(false) + .with_context(|| format!("Fail to reset pci dev {}", pci_dev.name()))?; + + if let Some(bus) = pci_dev.child_bus() { + MUT_PCI_BUS!(bus, locked_bus, pci_bus); + pci_bus.reset().with_context(|| "Fail to reset child bus")?; + } + } + + Ok(()) + } +} + impl PciBus { /// Create new bus entity. /// @@ -225,22 +241,6 @@ impl PciBus { Ok(()) } - pub fn reset(&mut self) -> Result<()> { - for dev in self.child_devices().values() { - PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); - pci_dev - .reset(false) - .with_context(|| format!("Fail to reset pci dev {}", pci_dev.name()))?; - - if let Some(bus) = pci_dev.child_bus() { - MUT_PCI_BUS!(bus, locked_bus, pci_bus); - pci_bus.reset().with_context(|| "Fail to reset child bus")?; - } - } - - Ok(()) - } - fn is_during_reset(&self) -> bool { let mut data = vec![0_u8; 2]; self.get_bridge_control_reg(BRIDGE_CONTROL as usize + 1, &mut data); diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 77fcfd30c..b78ff6175 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -186,6 +186,10 @@ const CLASS_CODE_DEMO: u16 = 0xEE; impl Device for DemoDev { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.base.config.reset_common_regs() + } } impl PciDevOps for DemoDev { @@ -224,11 +228,6 @@ impl PciDevOps for DemoDev { Some(&pci_bus.mem_region), ); } - - /// Reset device - fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - self.base.config.reset_common_regs() - } } pub trait DeviceTypeOperation: Send { diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 6f1a25599..284811570 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -242,6 +242,18 @@ impl PciHost { impl Device for PciHost { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + let root_bus = self.child_bus().unwrap(); + for dev in root_bus.lock().unwrap().child_devices().values() { + PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); + pci_dev + .reset(true) + .with_context(|| "Fail to reset pci device under pci host")?; + } + + Ok(()) + } } impl SysBusDevOps for PciHost { @@ -280,18 +292,6 @@ impl SysBusDevOps for PciHost { None => true, } } - - fn reset(&mut self) -> Result<()> { - let root_bus = self.child_bus().unwrap(); - for dev in root_bus.lock().unwrap().child_devices().values() { - PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); - pci_dev - .reset(true) - .with_context(|| "Fail to reset pci device under pci host")?; - } - - Ok(()) - } } #[cfg(target_arch = "x86_64")] diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index cbb95263c..59f295f5a 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -213,11 +213,6 @@ pub trait PciDevOps: Device + Send { ((bus_num as u16) << bus_shift) | (devfn as u16) } - /// Reset device - fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - Ok(()) - } - /// Get the path of the PCI bus where the device resides. fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { PCI_BUS!(parent_bus, locked_bus, pci_bus); diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index aceea6fab..5c9786462 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -339,6 +339,37 @@ impl RootPort { impl Device for RootPort { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + /// Only set slot status to on, and no other device reset actions are implemented. + fn reset(&mut self, reset_child_device: bool) -> Result<()> { + if reset_child_device { + let child_bus = self.child_bus().unwrap(); + MUT_PCI_BUS!(child_bus, locked_child_bus, child_pci_bus); + child_pci_bus + .reset() + .with_context(|| "Fail to reset child_bus in root port")?; + } else { + let cap_offset = self.base.config.pci_express_cap_offset; + le_write_u16( + &mut self.base.config.config, + (cap_offset + PCI_EXP_SLTSTA) as usize, + PCI_EXP_SLTSTA_PDS, + )?; + le_write_u16( + &mut self.base.config.config, + (cap_offset + PCI_EXP_SLTCTL) as usize, + !PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PWR_IND_ON, + )?; + le_write_u16( + &mut self.base.config.config, + (cap_offset + PCI_EXP_LNKSTA) as usize, + PCI_EXP_LNKSTA_DLLLA, + )?; + } + + self.base.config.reset_bridge_regs()?; + self.base.config.reset() + } } /// Convert from Arc> to &mut RootPort. @@ -506,37 +537,6 @@ impl PciDevOps for RootPort { self.do_unplug(offset, data, old_ctl, old_status); } - /// Only set slot status to on, and no other device reset actions are implemented. - fn reset(&mut self, reset_child_device: bool) -> Result<()> { - if reset_child_device { - let child_bus = self.child_bus().unwrap(); - MUT_PCI_BUS!(child_bus, locked_child_bus, child_pci_bus); - child_pci_bus - .reset() - .with_context(|| "Fail to reset child_bus in root port")?; - } else { - let cap_offset = self.base.config.pci_express_cap_offset; - le_write_u16( - &mut self.base.config.config, - (cap_offset + PCI_EXP_SLTSTA) as usize, - PCI_EXP_SLTSTA_PDS, - )?; - le_write_u16( - &mut self.base.config.config, - (cap_offset + PCI_EXP_SLTCTL) as usize, - !PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PWR_IND_ON, - )?; - le_write_u16( - &mut self.base.config.config, - (cap_offset + PCI_EXP_LNKSTA) as usize, - PCI_EXP_LNKSTA_DLLLA, - )?; - } - - self.base.config.reset_bridge_regs()?; - self.base.config.reset() - } - fn get_dev_path(&self) -> Option { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); let parent_dev_path = self.get_parent_dev_path(parent_bus); diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index 76a190e07..a436f7795 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -331,10 +331,6 @@ pub trait SysBusDevOps: Device + Send + AmlBuilder { ) }); } - - fn reset(&mut self) -> Result<()> { - Ok(()) - } } /// Convert from Arc> to &mut dyn SysBusDevOps. diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 82d990ea3..e0ccbf6d0 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -238,6 +238,14 @@ impl XhciPciDevice { impl Device for XhciPciDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + self.xhci.lock().unwrap().reset(); + + self.base.config.reset()?; + + Ok(()) + } } impl PciDevOps for XhciPciDevice { @@ -361,14 +369,6 @@ impl PciDevOps for XhciPciDevice { Some(&pci_bus.mem_region), ); } - - fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - self.xhci.lock().unwrap().reset(); - - self.base.config.reset()?; - - Ok(()) - } } struct DoorbellHandler { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 39e82b6f6..62f01f69f 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -55,14 +55,12 @@ use devices::misc::scream::{Scream, ScreamConfig}; #[cfg(feature = "demo_device")] use devices::pci::demo_device::{DemoDev, DemoDevConfig}; use devices::pci::{ - devices_register_pcidevops_type, register_pcidevops_type, to_pcidevops, PciBus, PciDevOps, - PciHost, RootPort, RootPortConfig, + devices_register_pcidevops_type, register_pcidevops_type, PciBus, PciDevOps, PciHost, RootPort, + RootPortConfig, }; use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable}; use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE}; -use devices::sysbus::{ - devices_register_sysbusdevops_type, to_sysbusdevops, SysBus, SysBusDevOps, SysBusDevType, -}; +use devices::sysbus::{devices_register_sysbusdevops_type, to_sysbusdevops, SysBus, SysBusDevType}; #[cfg(feature = "usb_camera")] use devices::usb::camera::{UsbCamera, UsbCameraConfig}; use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; @@ -77,7 +75,7 @@ use devices::usb::UsbDevice; use devices::InterruptController; use devices::ScsiBus::get_scsi_key; use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; -use devices::{convert_bus_ref, Bus, Device, PCI_BUS, PCI_BUS_DEVICE, SYS_BUS_DEVICE}; +use devices::{convert_bus_ref, Bus, Device, PCI_BUS, SYS_BUS_DEVICE}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; @@ -957,18 +955,15 @@ pub trait MachineOps: MachineLifecycle { } fn reset_all_devices(&mut self) -> Result<()> { - for dev in self.get_sysbus_devices().values() { - SYS_BUS_DEVICE!(dev, locked_dev, sysbusdev); - sysbusdev - .reset() - .with_context(|| "Fail to reset sysbus device")?; - } + let sysbus = self.machine_base().sysbus.clone(); + sysbus.lock().unwrap().reset()?; + // Todo: this logic will be deleted after deleting pci_host in machine struct. if let Ok(pci_host) = self.get_pci_host() { pci_host .lock() .unwrap() - .reset() + .reset(true) .with_context(|| "Fail to reset pci host")?; } @@ -1535,10 +1530,11 @@ pub trait MachineOps: MachineLifecycle { drop(locked_bus); // It's safe to call devfn.unwrap(), because the bus exists. match locked_pci_host.find_device(0, devfn.unwrap() as u8) { - Some(dev) => { - PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); - pci_dev.reset(false).with_context(|| "Failed to reset bus") - } + Some(dev) => dev + .lock() + .unwrap() + .reset(false) + .with_context(|| "Failed to reset bus"), None => bail!("Failed to found device"), } } diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 3ae3cae8f..3e28c2e38 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -815,6 +815,12 @@ impl VfioPciDevice { impl Device for VfioPciDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + Result::with_context(self.vfio_device.lock().unwrap().reset(), || { + "Fail to reset vfio dev" + }) + } } impl PciDevOps for VfioPciDevice { @@ -988,12 +994,6 @@ impl PciDevOps for VfioPciDevice { } } } - - fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - Result::with_context(self.vfio_device.lock().unwrap().reset(), || { - "Fail to reset vfio dev" - }) - } } fn get_irq_rawfds(gsi_msi_routes: &[GsiMsiRoute], start: u32, count: u32) -> Vec { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index ec3209048..b812decd1 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1002,6 +1002,19 @@ impl VirtioPciDevice { impl Device for VirtioPciDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + info!("func: reset, id: {:?}", &self.base.base.id); + self.deactivate_device(); + self.device + .lock() + .unwrap() + .reset() + .with_context(|| "Failed to reset virtio device")?; + self.base.config.reset()?; + + Ok(()) + } } impl PciDevOps for VirtioPciDevice { @@ -1214,19 +1227,6 @@ impl PciDevOps for VirtioPciDevice { self.do_cfg_access(offset, end, true); } - fn reset(&mut self, _reset_child_device: bool) -> Result<()> { - info!("func: reset, id: {:?}", &self.base.base.id); - self.deactivate_device(); - self.device - .lock() - .unwrap() - .reset() - .with_context(|| "Failed to reset virtio device")?; - self.base.config.reset()?; - - Ok(()) - } - fn get_dev_path(&self) -> Option { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); match self.device.lock().unwrap().device_type() { -- Gitee From 00f1a7e8dda3b04dcbdaf8f9cf82c6e71aa3b23f Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 13 Jul 2024 18:49:50 +0800 Subject: [PATCH 1952/2187] devices: unified device unrealize function Move unrealize function to Device trait to unify unrealize function: 1. Default unrealize is not supported. 2. Device unrealize is the device's own logic. Signed-off-by: liuxiangdong --- devices/src/lib.rs | 5 +++++ devices/src/misc/pvpanic.rs | 8 +++---- devices/src/pci/demo_device/mod.rs | 9 ++++---- devices/src/pci/mod.rs | 13 ++++-------- devices/src/usb/xhci/xhci_pci.rs | 10 ++++----- vfio/src/vfio_pci.rs | 16 +++++++------- virtio/src/transport/virtio_pci.rs | 34 +++++++++++++++--------------- 7 files changed, 47 insertions(+), 48 deletions(-) diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 98aea03fb..b75fc0e46 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -109,6 +109,11 @@ pub trait Device: Any + AsAny + Send + Sync { fn reset(&mut self, _reset_child_device: bool) -> Result<()> { Ok(()) } + + /// Unrealize device. + fn unrealize(&mut self) -> Result<()> { + bail!("Unrealize of the device {} is not implemented", self.name()); + } } /// Macro `convert_device_ref!`: Convert from Arc> to &$device_type. diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 76d232192..f3bc74093 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -165,6 +165,10 @@ impl PvPanicPci { impl Device for PvPanicPci { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } } impl PciDevOps for PvPanicPci { @@ -227,10 +231,6 @@ impl PciDevOps for PvPanicPci { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - Ok(()) - } - fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); PCI_BUS!(parent_bus, locked_bus, pci_bus); diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index b78ff6175..9e5c20a8f 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -190,6 +190,10 @@ impl Device for DemoDev { fn reset(&mut self, _reset_child_device: bool) -> Result<()> { self.base.config.reset_common_regs() } + + fn unrealize(&mut self) -> Result<()> { + self.device.lock().unwrap().unrealize() + } } impl PciDevOps for DemoDev { @@ -209,11 +213,6 @@ impl PciDevOps for DemoDev { Ok(()) } - /// Unrealize PCI/PCIe device. - fn unrealize(&mut self) -> Result<()> { - self.device.lock().unwrap().unrealize() - } - /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 59f295f5a..8d2ce8470 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -175,11 +175,6 @@ pub trait PciDevOps: Device + Send { /// Realize PCI/PCIe device. fn realize(self) -> Result<()>; - /// Unrealize PCI/PCIe device. - fn unrealize(&mut self) -> Result<()> { - bail!("Unrealize of the pci device is not implemented"); - } - /// Configuration space read. /// /// # Arguments @@ -428,6 +423,10 @@ mod tests { impl Device for TestPciDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn unrealize(&mut self) -> Result<()> { + Ok(()) + } } impl PciDevOps for TestPciDevice { @@ -456,10 +455,6 @@ mod tests { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - Ok(()) - } - fn init_write_mask(&mut self, _is_bridge: bool) -> Result<()> { let mut offset = 0_usize; while offset < self.base.config.config.len() { diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index e0ccbf6d0..67afc3caf 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -246,6 +246,11 @@ impl Device for XhciPciDevice { Ok(()) } + + fn unrealize(&mut self) -> Result<()> { + trace::usb_xhci_exit(); + Ok(()) + } } impl PciDevOps for XhciPciDevice { @@ -350,11 +355,6 @@ impl PciDevOps for XhciPciDevice { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - trace::usb_xhci_exit(); - Ok(()) - } - fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); PCI_BUS!(parent_bus, locked_bus, pci_bus); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 3e28c2e38..22ae5945c 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -821,6 +821,14 @@ impl Device for VfioPciDevice { "Fail to reset vfio dev" }) } + + fn unrealize(&mut self) -> Result<()> { + if let Err(e) = VfioPciDevice::unrealize(self) { + error!("{:?}", e); + bail!("Failed to unrealize vfio-pci."); + } + Ok(()) + } } impl PciDevOps for VfioPciDevice { @@ -877,14 +885,6 @@ impl PciDevOps for VfioPciDevice { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - if let Err(e) = VfioPciDevice::unrealize(self) { - error!("{:?}", e); - bail!("Failed to unrealize vfio-pci."); - } - Ok(()) - } - /// Read pci data from pci config if it emulate, otherwise read from vfio device. fn read_config(&mut self, offset: usize, data: &mut [u8]) { let size = data.len(); diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index b812decd1..d33c47137 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1015,6 +1015,23 @@ impl Device for VirtioPciDevice { Ok(()) } + + fn unrealize(&mut self) -> Result<()> { + info!("func: unrealize, id: {:?}", &self.base.base.id); + self.device + .lock() + .unwrap() + .unrealize() + .with_context(|| "Failed to unrealize the virtio device")?; + + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + self.base.config.unregister_bars(&bus)?; + + MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name()); + MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name()); + + Ok(()) + } } impl PciDevOps for VirtioPciDevice { @@ -1179,23 +1196,6 @@ impl PciDevOps for VirtioPciDevice { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - info!("func: unrealize, id: {:?}", &self.base.base.id); - self.device - .lock() - .unwrap() - .unrealize() - .with_context(|| "Failed to unrealize the virtio device")?; - - let bus = self.parent_bus().unwrap().upgrade().unwrap(); - self.base.config.unregister_bars(&bus)?; - - MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name()); - MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name()); - - Ok(()) - } - fn read_config(&mut self, offset: usize, data: &mut [u8]) { trace::virtio_tpt_read_config(&self.base.base.id, offset as u64, data.len()); self.do_cfg_access(offset, offset + data.len(), false); -- Gitee From b4140808ad958d0508340a6e64cd4038e6368b93 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sat, 13 Jul 2024 21:56:13 +0800 Subject: [PATCH 1953/2187] devices: delete the parameters of `realize` function Unify the parameter of `realize` function to `mut self` or `self`. Make preparation for unifing `realize` function in next commit. Signed-off-by: liuxiangdong --- devices/src/acpi/cpu_controller.rs | 12 +++-- devices/src/acpi/ged.rs | 9 ++-- devices/src/acpi/power.rs | 9 ++-- devices/src/legacy/fwcfg.rs | 20 ++++---- devices/src/legacy/pflash.rs | 15 +++--- devices/src/legacy/pl011.rs | 10 ++-- devices/src/legacy/pl031.rs | 9 ++-- devices/src/legacy/ramfb.rs | 16 ++++--- devices/src/legacy/rtc.rs | 9 ++-- devices/src/legacy/serial.rs | 9 ++-- devices/src/lib.rs | 4 ++ devices/src/scsi/disk.rs | 21 +++++---- devices/src/sysbus/mod.rs | 8 ++++ devices/src/usb/storage.rs | 62 +++++++++++++------------ devices/src/usb/uas.rs | 71 +++++++++++++++-------------- machine/src/aarch64/micro.rs | 8 +--- machine/src/aarch64/standard.rs | 22 ++++----- machine/src/lib.rs | 11 ++--- machine/src/micro_common/mod.rs | 2 +- machine/src/x86_64/micro.rs | 2 +- machine/src/x86_64/standard.rs | 14 +++--- virtio/src/transport/virtio_mmio.rs | 9 ++-- 22 files changed, 198 insertions(+), 154 deletions(-) diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index 70d0866ad..a15fdc39c 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -20,7 +20,7 @@ use log::{error, info}; use vmm_sys_util::eventfd::EventFd; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AcpiError, AcpiLocalApic, AmlAcquire, AmlAddressSpaceType, AmlArg, AmlBuffer, AmlBuilder, AmlCallWithArgs1, AmlCallWithArgs2, AmlCallWithArgs4, AmlDevice, AmlEisaId, AmlEqual, AmlField, @@ -107,15 +107,17 @@ impl CpuController { .set_sys_resource(sysbus, region_base, region_size, "CPUController") .with_context(|| AcpiError::Alignment(region_size.try_into().unwrap()))?; cpu_controller.set_boot_vcpu(boot_vcpus)?; + cpu_controller.set_parent_bus(sysbus.clone()); Ok(cpu_controller) } - pub fn realize(self, sysbus: &Arc>) -> Result>> { + pub fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - let ret_dev = dev.clone(); - sysbus.lock().unwrap().attach_device(&dev)?; - Ok(ret_dev) + sysbus.attach_device(&dev)?; + Ok(dev) } fn eject_cpu(&mut self, vcpu_id: u8) -> Result<()> { diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index cfe072e26..915b2bce2 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -20,7 +20,7 @@ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AcpiError, AmlActiveLevel, AmlAddressSpaceType, AmlAnd, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlEqual, AmlExtendedInterrupt, AmlField, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUnit, @@ -99,14 +99,17 @@ impl Ged { ged.base.interrupt_evt = Some(Arc::new(create_new_eventfd()?)); ged.set_sys_resource(sysbus, region_base, region_size, "Ged") .with_context(|| AcpiError::Alignment(region_size as u32))?; + ged.set_parent_bus(sysbus.clone()); Ok(ged) } - pub fn realize(self, sysbus: &Arc>) -> Result>> { + pub fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let ged_event = self.ged_event.clone(); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; let ged = dev.lock().unwrap(); ged.register_acpi_powerdown_event(ged_event.power_button) diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index ca4011274..faea9c85e 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -19,7 +19,7 @@ use log::info; use crate::acpi::ged::{AcpiEvent, Ged}; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AcpiError, AmlAddressSpaceType, AmlBuilder, AmlDevice, AmlField, AmlFieldAccessType, AmlFieldLockRule, AmlFieldUnit, AmlFieldUpdateRule, AmlIndex, AmlInteger, AmlMethod, AmlName, @@ -99,6 +99,7 @@ impl PowerDev { }; pdev.set_sys_resource(sysbus, region_base, region_size, "PowerDev") .with_context(|| AcpiError::Alignment(region_size as u32))?; + pdev.set_parent_bus(sysbus.clone()); Ok(pdev) } @@ -184,9 +185,11 @@ impl PowerDev { self.ged.lock().unwrap().inject_acpi_event(evt); } - pub fn realize(self, sysbus: &Arc>) -> Result<()> { + pub fn realize(self) -> Result<()> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; let pdev_available: bool; { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index c60164974..25635fc85 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -20,7 +20,7 @@ use log::{error, warn}; use crate::legacy::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, AmlString, }; @@ -855,16 +855,17 @@ impl FwCfgMem { fwcfgmem .set_sys_resource(sysbus, region_base, region_size, "FwCfgMem") .with_context(|| "Failed to allocate system resource for FwCfg.")?; + fwcfgmem.set_parent_bus(sysbus.clone()); Ok(fwcfgmem) } - pub fn realize(mut self, sysbus: &Arc>) -> Result>> { + pub fn realize(mut self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); self.fwcfg.common_realize()?; let dev = Arc::new(Mutex::new(self)); sysbus - .lock() - .unwrap() .attach_device(&dev) .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) @@ -1016,16 +1017,17 @@ impl FwCfgIO { fwcfg .set_sys_resource(sysbus, FW_CFG_IO_BASE, FW_CFG_IO_SIZE, "FwCfgIO") .with_context(|| "Failed to allocate system resource for FwCfg.")?; + fwcfg.set_parent_bus(sysbus.clone()); Ok(fwcfg) } - pub fn realize(mut self, sysbus: &Arc>) -> Result>> { + pub fn realize(mut self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); self.fwcfg.common_realize()?; let dev = Arc::new(Mutex::new(self)); sysbus - .lock() - .unwrap() .attach_device(&dev) .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) @@ -1488,7 +1490,7 @@ mod test { let sys_mem = address_space_init(); let fwcfg = FwCfgMem::new(sys_mem, &mut sys_bus, 0x0902_0000, 0x0000_0018).unwrap(); - let fwcfg_dev = fwcfg.realize(&mut sys_bus).unwrap(); + let fwcfg_dev = fwcfg.realize().unwrap(); // Read FW_CFG_DMA_SIGNATURE entry. let base = GuestAddress(0x0000); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; @@ -1528,7 +1530,7 @@ mod test { let sys_mem = address_space_init(); let fwcfg = FwCfgIO::new(sys_mem, &mut sys_bus).unwrap(); - let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut sys_bus).unwrap(); + let fwcfg_dev = fwcfg.realize().unwrap(); // Read FW_CFG_DMA_SIGNATURE entry. let base = GuestAddress(0x0000); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index c4b1b1deb..e0a1b3af3 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -19,7 +19,7 @@ use log::{error, warn}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::AmlBuilder; use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; use util::gen_base_func; @@ -227,24 +227,25 @@ impl PFlash { pflash .set_sys_resource(sysbus, region_base, region_size, "PflashRom") .with_context(|| "Failed to allocate system resource for PFlash.")?; + pflash.set_parent_bus(sysbus.clone()); Ok(pflash) } - pub fn realize(self, sysbus: &Arc>) -> Result>> { + pub fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let region_base = self.base.res.region_base; let host_mmap = self.host_mmap.clone(); let dev = Arc::new(Mutex::new(self)); - let region_ops = sysbus.lock().unwrap().build_region_ops(&dev); + let region_ops = sysbus.build_region_ops(&dev); let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "PflashRom"); dev.lock().unwrap().rom = Some(rom_region.clone()); sysbus - .lock() - .unwrap() .sys_mem .root() .add_subregion(rom_region, region_base) .with_context(|| "Failed to attach PFlash to system bus")?; - sysbus.lock().unwrap().sysbus_attach_child(dev.clone())?; + sysbus.sysbus_attach_child(dev.clone())?; Ok(dev) } @@ -949,7 +950,7 @@ mod test { flash_size, fd, sector_len, 4, 2, read_only, &sysbus, flash_base, ) .unwrap(); - let dev = pflash.realize(&sysbus).unwrap(); + let dev = pflash.realize().unwrap(); dev } diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 164f4b43a..35c04fe71 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -17,7 +17,7 @@ use log::error; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlMemory32Fixed, AmlNameDecl, AmlReadAndWrite, AmlResTemplate, AmlResourceUsage, @@ -148,6 +148,7 @@ impl PL011 { pl011 .set_sys_resource(sysbus, region_base, region_size, "PL011") .with_context(|| "Failed to set system resource for PL011.")?; + pl011.set_parent_bus(sysbus.clone()); Ok(pl011) } @@ -162,18 +163,19 @@ impl PL011 { } } - pub fn realize(self, sysbus: &Arc>) -> Result<()> { + pub fn realize(self) -> Result<()> { self.chardev .lock() .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); sysbus - .lock() - .unwrap() .attach_device(&dev) .with_context(|| "Failed to attach PL011 to system bus.")?; + drop(locked_bus); MigrationManager::register_device_instance( PL011State::descriptor(), dev.clone(), diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index ab41fb2a3..b88454c47 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -18,7 +18,7 @@ use byteorder::{ByteOrder, LittleEndian}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::AmlBuilder; use address_space::GuestAddress; use migration::{ @@ -95,13 +95,16 @@ impl PL031 { pl031 .set_sys_resource(sysbus, region_base, region_size, "PL031") .with_context(|| LegacyError::SetSysResErr)?; + pl031.set_parent_bus(sysbus.clone()); Ok(pl031) } - pub fn realize(self, sysbus: &Arc>) -> Result<()> { + pub fn realize(self) -> Result<()> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; MigrationManager::register_device_instance( PL031State::descriptor(), diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 7bb501b5e..b87a62a25 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -22,7 +22,7 @@ use log::error; use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; use machine_manager::config::valid_id; @@ -244,16 +244,20 @@ pub struct Ramfb { } impl Ramfb { - pub fn new(sys_mem: Arc, install: bool) -> Self { - Ramfb { + pub fn new(sys_mem: Arc, sysbus: &Arc>, install: bool) -> Self { + let mut ramfb = Ramfb { base: SysBusDevBase::new(SysBusDevType::Ramfb), ramfb_state: RamfbState::new(sys_mem, install), - } + }; + ramfb.set_parent_bus(sysbus.clone()); + ramfb } - pub fn realize(self, sysbus: &Arc>) -> Result<()> { + pub fn realize(self) -> Result<()> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; Ok(()) } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 6860a1e28..add41a885 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -17,7 +17,7 @@ use anyhow::Result; use log::{debug, error, warn}; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AmlBuilder, AmlDevice, AmlEisaId, AmlIoDecode, AmlIoResource, AmlIrqNoFlags, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, @@ -143,6 +143,7 @@ impl RTC { rtc.init_rtc_reg(); rtc.set_sys_resource(sysbus, RTC_PORT_INDEX, 8, "RTC")?; + rtc.set_parent_bus(sysbus.clone()); Ok(rtc) } @@ -264,9 +265,11 @@ impl RTC { true } - pub fn realize(self, sysbus: &Arc>) -> Result<()> { + pub fn realize(self) -> Result<()> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; Ok(()) } diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index dc7e25fb7..898a933b9 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -18,7 +18,7 @@ use log::{debug, error}; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use crate::{Device, DeviceBase}; +use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlEisaId, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlIoDecode, AmlIoResource, AmlNameDecl, AmlResTemplate, @@ -141,17 +141,20 @@ impl Serial { serial .set_sys_resource(sysbus, region_base, region_size, "Serial") .with_context(|| LegacyError::SetSysResErr)?; + serial.set_parent_bus(sysbus.clone()); Ok(serial) } - pub fn realize(self, sysbus: &Arc>) -> Result<()> { + pub fn realize(self) -> Result<()> { self.chardev .lock() .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; MigrationManager::register_device_instance( SerialState::descriptor(), diff --git a/devices/src/lib.rs b/devices/src/lib.rs index b75fc0e46..ecc9c140b 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -101,6 +101,10 @@ pub trait Device: Any + AsAny + Send + Sync { self.device_base().parent.clone() } + fn set_parent_bus(&mut self, bus: Arc>) { + self.device_base_mut().parent = Some(Arc::downgrade(&bus)); + } + /// Get the bus which this device has. fn child_bus(&self) -> Option>> { self.device_base().child.clone() diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 3b9e6a6cd..9e1c98954 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -24,6 +24,8 @@ use machine_manager::event_loop::EventLoop; use util::aio::{Aio, AioEngine, WriteZeroesState}; use util::gen_base_func; +use super::bus::ScsiBus; + /// SCSI DEVICE TYPES. pub const SCSI_TYPE_DISK: u32 = 0x00; pub const SCSI_TYPE_TAPE: u32 = 0x01; @@ -143,6 +145,7 @@ macro_rules! SCSI_DEVICE { }; } +#[derive(Default)] pub struct ScsiDevice { pub base: DeviceBase, /// Configuration of the scsi device. @@ -181,30 +184,30 @@ impl ScsiDevice { drive_cfg: DriveConfig, drive_files: Arc>>, iothread: Option, + scsi_bus: Arc>, ) -> ScsiDevice { let scsi_type = match dev_cfg.classtype.as_str() { "scsi-hd" => SCSI_TYPE_DISK, _ => SCSI_TYPE_ROM, }; - ScsiDevice { + let mut scsi_dev = ScsiDevice { base: DeviceBase::new(dev_cfg.id.clone(), false, None), dev_cfg, drive_cfg, state: ScsiDevState::new(), - block_backend: None, req_align: 1, buf_align: 1, - disk_sectors: 0, - block_size: 0, scsi_type, drive_files, - aio: None, iothread, - } + ..Default::default() + }; + scsi_dev.set_parent_bus(scsi_bus); + scsi_dev } - pub fn realize(&mut self) -> Result<()> { + pub fn realize(mut self) -> Result>> { match self.scsi_type { SCSI_TYPE_DISK => { self.block_size = SCSI_DISK_DEFAULT_BLOCK_SIZE; @@ -231,6 +234,7 @@ impl ScsiDevice { self.req_align = alignments.0; self.buf_align = alignments.1; let drive_id = VmConfig::get_drive_id(&drive_files, &self.drive_cfg.path_on_host)?; + drop(drive_files); let mut thread_pool = None; if self.drive_cfg.aio != AioEngine::Off { @@ -254,7 +258,8 @@ impl ScsiDevice { self.block_backend = Some(backend); self.disk_sectors = disk_size >> SECTOR_SHIFT; - Ok(()) + let dev = Arc::new(Mutex::new(self)); + Ok(dev) } } diff --git a/devices/src/sysbus/mod.rs b/devices/src/sysbus/mod.rs index a436f7795..01de827dd 100644 --- a/devices/src/sysbus/mod.rs +++ b/devices/src/sysbus/mod.rs @@ -175,6 +175,14 @@ impl Bus for SysBus { gen_base_func!(bus_base, bus_base_mut, BusBase, base); } +/// Convert from Arc> to &mut SysBus. +#[macro_export] +macro_rules! MUT_SYS_BUS { + ($trait_bus:expr, $lock_bus: ident, $struct_bus: ident) => { + convert_bus_mut!($trait_bus, $lock_bus, $struct_bus, SysBus); + }; +} + #[derive(Clone)] pub struct SysRes { // Note: region_base/region_size are both 0 means that this device doesn't have its own memory layout. diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 74bb82ab5..2c03f09a3 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -28,14 +28,12 @@ use super::descriptor::{ use super::xhci::xhci_controller::XhciDevice; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; -use crate::{ - Bus, - ScsiBus::{ - get_scsi_key, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, - EMULATE_SCSI_OPS, GOOD, SCSI_CMD_BUF_SIZE, - }, - ScsiDisk::{ScsiDevConfig, ScsiDevice}, +use crate::Bus; +use crate::ScsiBus::{ + get_scsi_key, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, + GOOD, SCSI_CMD_BUF_SIZE, }; +use crate::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use machine_manager::config::{DriveConfig, DriveFile}; use util::aio::AioEngine; use util::gen_base_func; @@ -258,7 +256,9 @@ pub struct UsbStorage { // (usb-storage/scsi bus/scsi device) correspond one-to-one, add scsi device member here // for the execution efficiency (No need to find a unique device from the hash table of the // unique bus). - scsi_dev: Arc>, + scsi_dev: Option>>, + /// Drive backend files. + drive_files: Arc>>, } #[derive(Debug)] @@ -334,29 +334,15 @@ impl UsbStorage { bail!("USB-storage: \"aio=off,direct=false\" must be configured."); } - let scsidev_classtype = match &drive_cfg.media as &str { - "disk" => "scsi-hd".to_string(), - _ => "scsi-cd".to_string(), - }; - let scsi_dev_cfg = ScsiDevConfig { - classtype: scsidev_classtype, - drive: dev_cfg.drive.clone(), - ..Default::default() - }; - Ok(Self { base: UsbDeviceBase::new(dev_cfg.id.clone(), USB_DEVICE_BUFFER_DEFAULT_LEN), state: UsbStorageState::new(), cntlr: None, dev_cfg, - drive_cfg: drive_cfg.clone(), + drive_cfg, scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), - scsi_dev: Arc::new(Mutex::new(ScsiDevice::new( - scsi_dev_cfg, - drive_cfg, - drive_files, - None, - ))), + scsi_dev: None, + drive_files, }) } @@ -513,7 +499,7 @@ impl UsbStorage { 0, packet.iovecs.clone(), self.state.iovec_len, - self.scsi_dev.clone(), + self.scsi_dev.as_ref().unwrap().clone(), csw, ) .with_context(|| "Error in creating scsirequest.")?; @@ -556,13 +542,29 @@ impl UsbDevice for UsbStorage { // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. - let mut locked_scsi_dev = self.scsi_dev.lock().unwrap(); - locked_scsi_dev.realize()?; - drop(locked_scsi_dev); + let scsidev_classtype = match self.drive_cfg.media.as_str() { + "disk" => "scsi-hd".to_string(), + _ => "scsi-cd".to_string(), + }; + let scsi_dev_cfg = ScsiDevConfig { + classtype: scsidev_classtype, + drive: self.dev_cfg.drive.clone(), + ..Default::default() + }; + let scsi_device = ScsiDevice::new( + scsi_dev_cfg, + self.drive_cfg.clone(), + self.drive_files.clone(), + None, + self.scsi_bus.clone(), + ); + let realized_scsi = scsi_device.realize()?; + self.scsi_dev = Some(realized_scsi.clone()); + self.scsi_bus .lock() .unwrap() - .attach_child(get_scsi_key(0, 0), self.scsi_dev.clone())?; + .attach_child(get_scsi_key(0, 0), realized_scsi)?; let storage: Arc> = Arc::new(Mutex::new(self)); Ok(storage) diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index c58f7ecf2..d4c9dfbcc 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -35,14 +35,12 @@ use super::{ USB_DEVICE_BUFFER_DEFAULT_LEN, }; use crate::Bus; -use crate::{ - ScsiBus::{ - get_scsi_key, scsi_cdb_xfer, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, - CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, - SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, - }, - ScsiDisk::{ScsiDevConfig, ScsiDevice}, +use crate::ScsiBus::{ + get_scsi_key, scsi_cdb_xfer, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, + CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, + SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, }; +use crate::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use machine_manager::config::{DriveConfig, DriveFile}; use util::byte_code::ByteCode; use util::gen_base_func; @@ -108,8 +106,11 @@ pub struct UsbUasConfig { pub struct UsbUas { base: UsbDeviceBase, + uas_config: UsbUasConfig, scsi_bus: Arc>, - scsi_device: Arc>, + scsi_device: Option>>, + drive_cfg: DriveConfig, + drive_files: Arc>>, commands: [Option; UAS_MAX_STREAMS + 1], statuses: [Option>>; UAS_MAX_STREAMS + 1], data: [Option>>; UAS_MAX_STREAMS + 1], @@ -462,25 +463,16 @@ impl UsbUas { drive_cfg: DriveConfig, drive_files: Arc>>, ) -> Self { - let scsidev_classtype = match &drive_cfg.media as &str { - "disk" => "scsi-hd".to_string(), - _ => "scsi-cd".to_string(), - }; - let scsi_dev_cfg = ScsiDevConfig { - classtype: scsidev_classtype, - drive: uas_config.drive.clone(), - ..Default::default() - }; - Self { - base: UsbDeviceBase::new(uas_config.id.unwrap(), USB_DEVICE_BUFFER_DEFAULT_LEN), + base: UsbDeviceBase::new( + uas_config.id.as_ref().unwrap().clone(), + USB_DEVICE_BUFFER_DEFAULT_LEN, + ), + uas_config, scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), - scsi_device: Arc::new(Mutex::new(ScsiDevice::new( - scsi_dev_cfg, - drive_cfg, - drive_files, - None, - ))), + scsi_device: None, + drive_cfg, + drive_files, commands: array::from_fn(|_| None), statuses: array::from_fn(|_| None), data: array::from_fn(|_| None), @@ -543,7 +535,7 @@ impl UsbUas { lun, scsi_iovec, scsi_iovec_size, - self.scsi_device.clone(), + self.scsi_device.as_ref().unwrap().clone(), uas_request, ) .with_context(|| "failed to create SCSI request")?; @@ -704,7 +696,7 @@ impl UsbUas { let command = self.commands[stream].as_ref().unwrap(); // SAFETY: IU is guaranteed to be of type command. let cdb = unsafe { &command.body.command.cdb }; - let xfer_len = scsi_cdb_xfer(cdb, self.scsi_device.clone()); + let xfer_len = scsi_cdb_xfer(cdb, self.scsi_device.as_ref().unwrap().clone()); trace::usb_uas_try_start_next_transfer(self.device_id(), xfer_len); if xfer_len == 0 { @@ -761,15 +753,28 @@ impl UsbDevice for UsbUas { // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. - let mut locked_scsi_device = self.scsi_device.lock().unwrap(); - locked_scsi_device.realize()?; - locked_scsi_device.base.parent = - Some(Arc::downgrade(&self.scsi_bus) as Weak>); - drop(locked_scsi_device); + let scsidev_classtype = match self.drive_cfg.media.as_str() { + "disk" => "scsi-hd".to_string(), + _ => "scsi-cd".to_string(), + }; + let scsi_dev_cfg = ScsiDevConfig { + classtype: scsidev_classtype, + drive: self.uas_config.drive.clone(), + ..Default::default() + }; + let scsi_device = ScsiDevice::new( + scsi_dev_cfg, + self.drive_cfg.clone(), + self.drive_files.clone(), + None, + self.scsi_bus.clone(), + ); + let realized_scsi = scsi_device.realize()?; + self.scsi_device = Some(realized_scsi.clone()); self.scsi_bus .lock() .unwrap() - .attach_child(get_scsi_key(0, 0), self.scsi_device.clone())?; + .attach_child(get_scsi_key(0, 0), realized_scsi)?; let uas = Arc::new(Mutex::new(self)); Ok(uas) } diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index a3b761bfa..9c04f55fa 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -111,9 +111,7 @@ impl MachineOps for LightMachine { MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, )?; - pl031 - .realize(&self.base.sysbus) - .with_context(|| "Failed to realize pl031.") + pl031.realize().with_context(|| "Failed to realize pl031.") } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { @@ -121,9 +119,7 @@ impl MachineOps for LightMachine { let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; let pl011 = PL011::new(config.clone(), &self.base.sysbus, region_base, region_size) .with_context(|| "Failed to create PL011")?; - pl011 - .realize(&self.base.sysbus) - .with_context(|| "Failed to realize PL011")?; + pl011.realize().with_context(|| "Failed to realize PL011")?; let mut bs = self.base.boot_source.lock().unwrap(); bs.kernel_cmdline.push(Param { param_type: "earlycon".to_string(), diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index f2fe637e0..a848e1201 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -385,7 +385,7 @@ impl StdMachineOps for StdMachine { .with_context(|| DevErrorKind::AddEntryErr("bios-geometry".to_string()))?; let fwcfg_dev = fwcfg - .realize(&self.base.sysbus) + .realize() .with_context(|| "Failed to realize fwcfg device")?; self.base.fwcfg_dev = Some(fwcfg_dev.clone()); @@ -461,8 +461,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, )?; - rtc.realize(&self.base.sysbus) - .with_context(|| "Failed to realize PL031") + rtc.realize().with_context(|| "Failed to realize PL031") } fn add_ged_device(&mut self) -> Result<()> { @@ -474,9 +473,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::Ged as usize].1, GedEvent::new(self.power_button.clone()), )?; - let ged_dev = ged - .realize(&self.base.sysbus) - .with_context(|| "Failed to realize Ged")?; + let ged_dev = ged.realize().with_context(|| "Failed to realize Ged")?; if battery_present { let pdev = PowerDev::new( ged_dev, @@ -484,7 +481,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::PowerDev as usize].0, MEM_LAYOUT[LayoutEntryType::PowerDev as usize].1, )?; - pdev.realize(&self.base.sysbus) + pdev.realize() .with_context(|| "Failed to realize PowerDev")?; } Ok(()) @@ -495,9 +492,7 @@ impl MachineOps for StdMachine { let region_size: u64 = MEM_LAYOUT[LayoutEntryType::Uart as usize].1; let pl011 = PL011::new(config.clone(), &self.base.sysbus, region_base, region_size) .with_context(|| "Failed to create PL011")?; - pl011 - .realize(&self.base.sysbus) - .with_context(|| "Failed to realize PL011")?; + pl011.realize().with_context(|| "Failed to realize PL011")?; let mut bs = self.base.boot_source.lock().unwrap(); bs.kernel_cmdline.push(Param { param_type: "earlycon".to_string(), @@ -661,7 +656,8 @@ impl MachineOps for StdMachine { flash_base, ) .with_context(|| MachineError::InitPflashErr)?; - PFlash::realize(pflash, &self.base.sysbus) + pflash + .realize() .with_context(|| MachineError::RlzPflashErr)?; flash_base += flash_size; } @@ -710,10 +706,10 @@ impl MachineOps for StdMachine { .get_fwcfg_dev() .with_context(|| "Ramfb device must be used UEFI to boot, please add pflash devices")?; let sys_mem = self.get_sys_mem(); - let mut ramfb = Ramfb::new(sys_mem.clone(), config.install); + let mut ramfb = Ramfb::new(sys_mem.clone(), &self.base.sysbus, config.install); ramfb.ramfb_state.setup(&fwcfg_dev)?; - ramfb.realize(&self.base.sysbus) + ramfb.realize() } fn get_pci_host(&mut self) -> Result<&Arc>> { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 62f01f69f..d6e7813df 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1221,16 +1221,15 @@ pub trait MachineOps: MachineLifecycle { bail!("Wrong! Two scsi devices have the same scsi-id and lun!"); } let iothread = cntlr.config.iothread.clone(); - - let device = Arc::new(Mutex::new(ScsiDevice::new( + let scsi_device = ScsiDevice::new( device_cfg.clone(), drive_arg, self.get_drive_files(), iothread, - ))); - device.lock().unwrap().realize()?; - bus.lock().unwrap().attach_child(key, device.clone())?; - device.lock().unwrap().base.parent = Some(Arc::downgrade(bus) as Weak>); + bus.clone(), + ); + let device = scsi_device.realize()?; + bus.lock().unwrap().attach_child(key, device)?; if let Some(bootindex) = device_cfg.bootindex { // Eg: OpenFirmware device path(virtio-scsi disk): diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 9e5551bee..55fb20375 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -500,7 +500,7 @@ impl LightMachine { region_size, )?; let mmio_device = dev - .realize(&self.base.sysbus) + .realize() .with_context(|| MachineError::RlzVirtioMmioErr)?; #[cfg(target_arch = "x86_64")] { diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 0c0afc762..f33189bca 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -134,7 +134,7 @@ impl MachineOps for LightMachine { let region_size: u64 = 8; let serial = Serial::new(config.clone(), &self.base.sysbus, region_base, region_size)?; serial - .realize(&self.base.sysbus) + .realize() .with_context(|| "Failed to realize serial device.") } diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index be3067c6f..b1cfe2857 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -226,7 +226,7 @@ impl StdMachine { self.base.cpus.clone(), )?; let realize_controller = cpu_controller - .realize(&self.base.sysbus) + .realize() .with_context(|| "Failed to realize Cpu Controller")?; self.register_hotplug_vcpu_event(hotplug_cpu_req, vm)?; self.cpu_controller = Some(realize_controller); @@ -286,7 +286,8 @@ impl StdMachineOps for StdMachine { .add_file_entry("bootorder", boot_order) .with_context(|| DevErrorKind::AddEntryErr("bootorder".to_string()))?; - let fwcfg_dev = FwCfgIO::realize(fwcfg, &self.base.sysbus) + let fwcfg_dev = fwcfg + .realize() .with_context(|| "Failed to realize fwcfg device")?; self.base.fwcfg_dev = Some(fwcfg_dev.clone()); @@ -441,7 +442,7 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); - rtc.realize(&self.base.sysbus) + rtc.realize() .with_context(|| "Failed to realize RTC device") } @@ -457,8 +458,7 @@ impl MachineOps for StdMachine { ged_event, )?; - ged.realize(&self.base.sysbus) - .with_context(|| "Failed to realize Ged")?; + ged.realize().with_context(|| "Failed to realize Ged")?; Ok(()) } @@ -467,7 +467,7 @@ impl MachineOps for StdMachine { let region_size: u64 = 8; let serial = Serial::new(config.clone(), &self.base.sysbus, region_base, region_size)?; serial - .realize(&self.base.sysbus) + .realize() .with_context(|| "Failed to realize serial device.")?; Ok(()) } @@ -623,7 +623,7 @@ impl MachineOps for StdMachine { ) .with_context(|| MachineError::InitPflashErr)?; pflash - .realize(&self.base.sysbus) + .realize() .with_context(|| MachineError::RlzPflashErr)?; flash_end -= pfl_size; } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 8e872a380..cab3973d9 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -27,7 +27,7 @@ use crate::{ }; use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd}; use devices::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; -use devices::{Device, DeviceBase}; +use devices::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; @@ -159,11 +159,12 @@ impl VirtioMmioDevice { interrupt_cb: None, }; mmio_device.set_sys_resource(sysbus, region_base, region_size, "VirtioMmio")?; + mmio_device.set_parent_bus(sysbus.clone()); Ok(mmio_device) } - pub fn realize(mut self, sysbus: &Arc>) -> Result>> { + pub fn realize(mut self) -> Result>> { self.assign_interrupt_cb(); self.device .lock() @@ -171,8 +172,10 @@ impl VirtioMmioDevice { .realize() .with_context(|| "Failed to realize virtio.")?; + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); - sysbus.lock().unwrap().attach_device(&dev)?; + sysbus.attach_device(&dev)?; Ok(dev) } -- Gitee From bebea6f20f4e6e82b3300af711bb2aff950215c3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 14 Jul 2024 10:13:46 +0800 Subject: [PATCH 1954/2187] devices: unified device `realize` function Move `realize` function to `Device` trait to unify `realize` function: 1. Each device has its own `realize` logic. Signed-off-by: liuxiangdong --- devices/src/acpi/cpu_controller.rs | 16 ++-- devices/src/acpi/ged.rs | 32 ++++---- devices/src/acpi/power.rs | 47 ++++++------ devices/src/legacy/fwcfg.rs | 44 +++++------ devices/src/legacy/pflash.rs | 38 ++++----- devices/src/legacy/pl011.rs | 57 +++++++------- devices/src/legacy/pl031.rs | 24 +++--- devices/src/legacy/ramfb.rs | 16 ++-- devices/src/legacy/rtc.rs | 16 ++-- devices/src/legacy/serial.rs | 53 ++++++------- devices/src/lib.rs | 10 +++ devices/src/misc/ivshmem.rs | 15 ++-- devices/src/misc/pvpanic.rs | 20 ++--- devices/src/misc/scream/mod.rs | 15 ++-- devices/src/pci/demo_device/mod.rs | 38 ++++----- devices/src/pci/mod.rs | 30 ++++---- devices/src/pci/root_port.rs | 50 ++++++------ devices/src/scsi/disk.rs | 110 +++++++++++++-------------- devices/src/usb/storage.rs | 2 +- devices/src/usb/uas.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 22 +++--- machine/src/aarch64/micro.rs | 7 +- machine/src/aarch64/pci_host_root.rs | 15 ++-- machine/src/aarch64/standard.rs | 11 ++- machine/src/lib.rs | 3 +- machine/src/micro_common/mod.rs | 1 + machine/src/x86_64/ich9_lpc.rs | 15 ++-- machine/src/x86_64/mch.rs | 15 ++-- machine/src/x86_64/micro.rs | 8 +- machine/src/x86_64/standard.rs | 11 ++- vfio/src/vfio_pci.rs | 28 +++---- virtio/src/transport/virtio_mmio.rs | 32 ++++---- virtio/src/transport/virtio_pci.rs | 50 ++++++------ 33 files changed, 439 insertions(+), 414 deletions(-) diff --git a/devices/src/acpi/cpu_controller.rs b/devices/src/acpi/cpu_controller.rs index a15fdc39c..797f44d46 100644 --- a/devices/src/acpi/cpu_controller.rs +++ b/devices/src/acpi/cpu_controller.rs @@ -112,14 +112,6 @@ impl CpuController { Ok(cpu_controller) } - pub fn realize(self) -> Result>> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - Ok(dev) - } - fn eject_cpu(&mut self, vcpu_id: u8) -> Result<()> { let vcpu = self.vcpu_map.get(&vcpu_id).unwrap(); vcpu.destroy()?; @@ -256,6 +248,14 @@ impl CpuController { impl Device for CpuController { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + Ok(dev) + } } impl SysBusDevOps for CpuController { diff --git a/devices/src/acpi/ged.rs b/devices/src/acpi/ged.rs index 915b2bce2..76a15032c 100644 --- a/devices/src/acpi/ged.rs +++ b/devices/src/acpi/ged.rs @@ -104,22 +104,6 @@ impl Ged { Ok(ged) } - pub fn realize(self) -> Result>> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let ged_event = self.ged_event.clone(); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - - let ged = dev.lock().unwrap(); - ged.register_acpi_powerdown_event(ged_event.power_button) - .with_context(|| "Failed to register ACPI powerdown event.")?; - #[cfg(target_arch = "x86_64")] - ged.register_acpi_cpu_resize_event(ged_event.cpu_resize) - .with_context(|| "Failed to register ACPI cpu resize event.")?; - Ok(dev.clone()) - } - fn register_acpi_powerdown_event(&self, power_button: Arc) -> Result<()> { let power_down_fd = power_button.as_raw_fd(); let ged_clone = self.clone(); @@ -189,6 +173,22 @@ impl Ged { impl Device for Ged { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let ged_event = self.ged_event.clone(); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + + let ged = dev.lock().unwrap(); + ged.register_acpi_powerdown_event(ged_event.power_button) + .with_context(|| "Failed to register ACPI powerdown event.")?; + #[cfg(target_arch = "x86_64")] + ged.register_acpi_cpu_resize_event(ged_event.cpu_resize) + .with_context(|| "Failed to register ACPI cpu resize event.")?; + Ok(dev.clone()) + } } impl SysBusDevOps for Ged { diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index faea9c85e..3a2a614cf 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -184,29 +184,6 @@ impl PowerDev { fn send_power_event(&self, evt: AcpiEvent) { self.ged.lock().unwrap().inject_acpi_event(evt); } - - pub fn realize(self) -> Result<()> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - - let pdev_available: bool; - { - let mut pdev = dev.lock().unwrap(); - pdev_available = pdev.power_battery_init_info().is_ok(); - if pdev_available { - pdev.send_power_event(AcpiEvent::BatteryInf); - } - } - if pdev_available { - power_status_update(&dev); - } else { - let mut pdev = dev.lock().unwrap(); - pdev.power_load_static_status(); - } - Ok(()) - } } impl StateTransfer for PowerDev { @@ -234,6 +211,30 @@ impl MigrationHook for PowerDev { impl Device for PowerDev { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + + let pdev_available: bool; + { + let mut pdev = dev.lock().unwrap(); + pdev_available = pdev.power_battery_init_info().is_ok(); + if pdev_available { + pdev.send_power_event(AcpiEvent::BatteryInf); + } + } + if pdev_available { + power_status_update(&dev); + } else { + let mut pdev = dev.lock().unwrap(); + pdev.power_load_static_status(); + } + + Ok(dev) + } } impl SysBusDevOps for PowerDev { diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 25635fc85..71258d935 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -859,17 +859,6 @@ impl FwCfgMem { Ok(fwcfgmem) } - - pub fn realize(mut self) -> Result>> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - self.fwcfg.common_realize()?; - let dev = Arc::new(Mutex::new(self)); - sysbus - .attach_device(&dev) - .with_context(|| "Failed to attach FwCfg device to system bus.")?; - Ok(dev) - } } #[cfg(target_arch = "aarch64")] @@ -942,6 +931,17 @@ impl Device for FwCfgMem { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } + + fn realize(mut self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + self.fwcfg.common_realize()?; + let dev = Arc::new(Mutex::new(self)); + sysbus + .attach_device(&dev) + .with_context(|| "Failed to attach FwCfg device to system bus.")?; + Ok(dev) + } } #[cfg(target_arch = "aarch64")] @@ -1021,17 +1021,6 @@ impl FwCfgIO { Ok(fwcfg) } - - pub fn realize(mut self) -> Result>> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - self.fwcfg.common_realize()?; - let dev = Arc::new(Mutex::new(self)); - sysbus - .attach_device(&dev) - .with_context(|| "Failed to attach FwCfg device to system bus.")?; - Ok(dev) - } } #[cfg(target_arch = "x86_64")] @@ -1102,6 +1091,17 @@ impl Device for FwCfgIO { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } + + fn realize(mut self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + self.fwcfg.common_realize()?; + let dev = Arc::new(Mutex::new(self)); + sysbus + .attach_device(&dev) + .with_context(|| "Failed to attach FwCfg device to system bus.")?; + Ok(dev) + } } #[cfg(target_arch = "x86_64")] diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index e0a1b3af3..d3a6c0a42 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -231,25 +231,6 @@ impl PFlash { Ok(pflash) } - pub fn realize(self) -> Result>> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let region_base = self.base.res.region_base; - let host_mmap = self.host_mmap.clone(); - let dev = Arc::new(Mutex::new(self)); - let region_ops = sysbus.build_region_ops(&dev); - let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "PflashRom"); - dev.lock().unwrap().rom = Some(rom_region.clone()); - sysbus - .sys_mem - .root() - .add_subregion(rom_region, region_base) - .with_context(|| "Failed to attach PFlash to system bus")?; - sysbus.sysbus_attach_child(dev.clone())?; - - Ok(dev) - } - fn set_read_array_mode(&mut self, is_illegal_cmd: bool) -> Result<()> { if is_illegal_cmd { warn!( @@ -712,6 +693,25 @@ impl Device for PFlash { self.status = 0x80; Ok(()) } + + fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let region_base = self.base.res.region_base; + let host_mmap = self.host_mmap.clone(); + let dev = Arc::new(Mutex::new(self)); + let region_ops = sysbus.build_region_ops(&dev); + let rom_region = Region::init_rom_device_region(host_mmap, region_ops, "PflashRom"); + dev.lock().unwrap().rom = Some(rom_region.clone()); + sysbus + .sys_mem + .root() + .add_subregion(rom_region, region_base) + .with_context(|| "Failed to attach PFlash to system bus")?; + sysbus.sysbus_attach_child(dev.clone())?; + + Ok(dev) + } } impl SysBusDevOps for PFlash { diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index 35c04fe71..b656b875d 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -163,34 +163,6 @@ impl PL011 { } } - pub fn realize(self) -> Result<()> { - self.chardev - .lock() - .unwrap() - .realize() - .with_context(|| "Failed to realize chardev")?; - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus - .attach_device(&dev) - .with_context(|| "Failed to attach PL011 to system bus.")?; - drop(locked_bus); - MigrationManager::register_device_instance( - PL011State::descriptor(), - dev.clone(), - PL011_SNAPSHOT_ID, - ); - let locked_dev = dev.lock().unwrap(); - locked_dev.chardev.lock().unwrap().set_receiver(&dev); - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), - None, - ) - .with_context(|| LegacyError::RegNotifierErr)?; - Ok(()) - } - fn unpause_rx(&mut self) { if self.paused { trace::pl011_unpause_rx(); @@ -236,6 +208,35 @@ impl InputReceiver for PL011 { impl Device for PL011 { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(self) -> Result>> { + self.chardev + .lock() + .unwrap() + .realize() + .with_context(|| "Failed to realize chardev")?; + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus + .attach_device(&dev) + .with_context(|| "Failed to attach PL011 to system bus.")?; + drop(locked_bus); + MigrationManager::register_device_instance( + PL011State::descriptor(), + dev.clone(), + PL011_SNAPSHOT_ID, + ); + let locked_dev = dev.lock().unwrap(); + locked_dev.chardev.lock().unwrap().set_receiver(&dev); + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), + None, + ) + .with_context(|| LegacyError::RegNotifierErr)?; + drop(locked_dev); + Ok(dev) + } } impl SysBusDevOps for PL011 { diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index b88454c47..a206a6fee 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -100,7 +100,16 @@ impl PL031 { Ok(pl031) } - pub fn realize(self) -> Result<()> { + /// Get current clock value. + fn get_current_value(&self) -> u32 { + (self.base_time.elapsed().as_secs() as u128 + self.tick_offset as u128) as u32 + } +} + +impl Device for PL031 { + gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(self) -> Result>> { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); let dev = Arc::new(Mutex::new(self)); @@ -108,21 +117,12 @@ impl PL031 { MigrationManager::register_device_instance( PL031State::descriptor(), - dev, + dev.clone(), PL031_SNAPSHOT_ID, ); - Ok(()) + Ok(dev) } - - /// Get current clock value. - fn get_current_value(&self) -> u32 { - (self.base_time.elapsed().as_secs() as u128 + self.tick_offset as u128) as u32 - } -} - -impl Device for PL031 { - gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); } impl SysBusDevOps for PL031 { diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index b87a62a25..40eda77e0 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -252,14 +252,6 @@ impl Ramfb { ramfb.set_parent_bus(sysbus.clone()); ramfb } - - pub fn realize(self) -> Result<()> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - Ok(()) - } } impl Device for Ramfb { @@ -269,6 +261,14 @@ impl Device for Ramfb { self.ramfb_state.reset_ramfb_state(); Ok(()) } + + fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + Ok(dev) + } } impl SysBusDevOps for Ramfb { diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index add41a885..369344181 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -265,14 +265,6 @@ impl RTC { true } - pub fn realize(self) -> Result<()> { - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - Ok(()) - } - /// Get current clock value. fn get_current_value(&self) -> i64 { (self.base_time.elapsed().as_secs() as i128 + self.tick_offset as i128) as i64 @@ -356,6 +348,14 @@ impl Device for RTC { self.set_memory(self.mem_size, self.gap_start); Ok(()) } + + fn realize(self) -> Result>> { + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + Ok(dev) + } } impl SysBusDevOps for RTC { diff --git a/devices/src/legacy/serial.rs b/devices/src/legacy/serial.rs index 898a933b9..e21112b3c 100644 --- a/devices/src/legacy/serial.rs +++ b/devices/src/legacy/serial.rs @@ -145,32 +145,6 @@ impl Serial { Ok(serial) } - pub fn realize(self) -> Result<()> { - self.chardev - .lock() - .unwrap() - .realize() - .with_context(|| "Failed to realize chardev")?; - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - - MigrationManager::register_device_instance( - SerialState::descriptor(), - dev.clone(), - SERIAL_SNAPSHOT_ID, - ); - let locked_dev = dev.lock().unwrap(); - locked_dev.chardev.lock().unwrap().set_receiver(&dev); - EventLoop::update_event( - EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), - None, - ) - .with_context(|| LegacyError::RegNotifierErr)?; - Ok(()) - } - fn unpause_rx(&mut self) { if self.paused { trace::serial_unpause_rx(); @@ -378,6 +352,33 @@ impl InputReceiver for Serial { impl Device for Serial { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(self) -> Result>> { + self.chardev + .lock() + .unwrap() + .realize() + .with_context(|| "Failed to realize chardev")?; + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + + MigrationManager::register_device_instance( + SerialState::descriptor(), + dev.clone(), + SERIAL_SNAPSHOT_ID, + ); + let locked_dev = dev.lock().unwrap(); + locked_dev.chardev.lock().unwrap().set_receiver(&dev); + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), + None, + ) + .with_context(|| LegacyError::RegNotifierErr)?; + drop(locked_dev); + Ok(dev) + } } impl SysBusDevOps for Serial { diff --git a/devices/src/lib.rs b/devices/src/lib.rs index ecc9c140b..d335d1a7c 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -114,6 +114,16 @@ pub trait Device: Any + AsAny + Send + Sync { Ok(()) } + /// Realize device. + fn realize(self) -> Result>> + where + Self: Sized, + { + // Note: Only PciHost does not have its own realization logic, + // but it will not be called. + bail!("Realize of the device {} is not implemented", self.name()); + } + /// Unrealize device. fn unrealize(&mut self) -> Result<()> { bail!("Unrealize of the device {} is not implemented", self.name()); diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 685fbb9e1..6145bb86e 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -91,12 +91,8 @@ impl Ivshmem { impl Device for Ivshmem { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); -} - -impl PciDevOps for Ivshmem { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; le_write_u16( @@ -122,9 +118,14 @@ impl PciDevOps for Ivshmem { // Attach to the PCI bus. let bus = self.parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = bus.lock().unwrap(); - locked_bus.attach_child(self.base.devfn as u64, Arc::new(Mutex::new(self)))?; - Ok(()) + let dev = Arc::new(Mutex::new(self)); + locked_bus.attach_child(dev.lock().unwrap().base.devfn as u64, dev.clone())?; + Ok(dev) } +} + +impl PciDevOps for Ivshmem { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index f3bc74093..8c8dcd064 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -166,15 +166,7 @@ impl PvPanicPci { impl Device for PvPanicPci { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); - fn unrealize(&mut self) -> Result<()> { - Ok(()) - } -} - -impl PciDevOps for PvPanicPci { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; le_write_u16( @@ -226,10 +218,18 @@ impl PciDevOps for PvPanicPci { .unwrap() .dev_id .store(device_id, Ordering::Release); - locked_bus.attach_child(devfn as u64, dev)?; + locked_bus.attach_child(devfn as u64, dev.clone())?; + Ok(dev) + } + + fn unrealize(&mut self) -> Result<()> { Ok(()) } +} + +impl PciDevOps for PvPanicPci { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 254671df2..7d4a50a10 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -18,15 +18,10 @@ mod ohaudio; #[cfg(feature = "scream_pulseaudio")] mod pulseaudio; -use std::{ - mem, - str::FromStr, - sync::{ - atomic::{fence, Ordering}, - Arc, Mutex, RwLock, Weak, - }, - thread, -}; +use std::str::FromStr; +use std::sync::atomic::{fence, Ordering}; +use std::sync::{Arc, Mutex, RwLock, Weak}; +use std::{mem, thread}; use anyhow::{anyhow, bail, Context, Result}; use clap::{ArgAction, Parser}; @@ -37,7 +32,7 @@ use log::{error, warn}; use self::alsa::AlsaStreamData; use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; -use crate::{pci::PciDevOps, Bus}; +use crate::{Bus, Device}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 9e5c20a8f..58a88c7df 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -126,16 +126,6 @@ impl DemoDev { Ok(()) } - fn attach_to_parent_bus(self) -> Result<()> { - let devfn = self.base.devfn as u64; - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - let mut locked_bus = parent_bus.lock().unwrap(); - let demo_pci_dev = Arc::new(Mutex::new(self)); - locked_bus.attach_child(devfn, demo_pci_dev)?; - - Ok(()) - } - fn register_data_handling_bar(&mut self) -> Result<()> { let device = self.device.clone(); let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { @@ -191,16 +181,7 @@ impl Device for DemoDev { self.base.config.reset_common_regs() } - fn unrealize(&mut self) -> Result<()> { - self.device.lock().unwrap().unrealize() - } -} - -impl PciDevOps for DemoDev { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - /// Realize PCI/PCIe device. - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_pci_config()?; if self.cmd_cfg.bar_num > 0 { init_msix(&mut self.base, 0, 1, self.dev_id.clone(), None, None)?; @@ -209,10 +190,23 @@ impl PciDevOps for DemoDev { self.register_data_handling_bar()?; self.device.lock().unwrap().realize()?; - self.attach_to_parent_bus()?; - Ok(()) + let devfn = self.base.devfn as u64; + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + let mut locked_bus = parent_bus.lock().unwrap(); + let demo_pci_dev = Arc::new(Mutex::new(self)); + locked_bus.attach_child(devfn, demo_pci_dev.clone())?; + + Ok(demo_pci_dev) } + fn unrealize(&mut self) -> Result<()> { + self.device.lock().unwrap().unrealize() + } +} + +impl PciDevOps for DemoDev { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); + /// write the pci configuration space fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 8d2ce8470..ccac50e50 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -172,9 +172,6 @@ pub trait PciDevOps: Device + Send { Ok(()) } - /// Realize PCI/PCIe device. - fn realize(self) -> Result<()>; - /// Configuration space read. /// /// # Arguments @@ -424,6 +421,21 @@ mod tests { impl Device for TestPciDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + fn realize(mut self) -> Result>> { + let devfn = self.base.devfn as u64; + self.init_write_mask(false)?; + self.init_write_clear_mask(false)?; + + let dev = Arc::new(Mutex::new(self)); + let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); + parent_bus + .lock() + .unwrap() + .attach_child(devfn, dev.clone())?; + + Ok(dev) + } + fn unrealize(&mut self) -> Result<()> { Ok(()) } @@ -443,18 +455,6 @@ mod tests { ); } - fn realize(mut self) -> Result<()> { - let devfn = self.base.devfn as u64; - self.init_write_mask(false)?; - self.init_write_clear_mask(false)?; - - let dev = Arc::new(Mutex::new(self)); - let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); - parent_bus.lock().unwrap().attach_child(devfn, dev)?; - - Ok(()) - } - fn init_write_mask(&mut self, _is_bridge: bool) -> Result<()> { let mut offset = 0_usize; while offset < self.base.config.config.len() { diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 5c9786462..0d854c080 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -370,28 +370,8 @@ impl Device for RootPort { self.base.config.reset_bridge_regs()?; self.base.config.reset() } -} -/// Convert from Arc> to &mut RootPort. -#[macro_export] -macro_rules! MUT_ROOT_PORT { - ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { - convert_device_mut!($trait_device, $lock_device, $struct_device, RootPort); - }; -} - -/// Convert from Arc> to &RootPort. -#[macro_export] -macro_rules! ROOT_PORT { - ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { - convert_device_ref!($trait_device, $lock_device, $struct_device, RootPort); - }; -} - -impl PciDevOps for RootPort { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { let parent_bus = self.parent_bus().unwrap(); self.init_write_mask(true)?; self.init_write_clear_mask(true)?; @@ -456,10 +436,34 @@ impl PciDevOps for RootPort { parent_pci_bus.attach_child(locked_root_port.base.devfn as u64, root_port.clone())?; // Need to drop locked_root_port in order to register root_port instance. drop(locked_root_port); - MigrationManager::register_device_instance(RootPortState::descriptor(), root_port, &name); + MigrationManager::register_device_instance( + RootPortState::descriptor(), + root_port.clone(), + &name, + ); - Ok(()) + Ok(root_port) } +} + +/// Convert from Arc> to &mut RootPort. +#[macro_export] +macro_rules! MUT_ROOT_PORT { + ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { + convert_device_mut!($trait_device, $lock_device, $struct_device, RootPort); + }; +} + +/// Convert from Arc> to &RootPort. +#[macro_export] +macro_rules! ROOT_PORT { + ($trait_device:expr, $lock_device: ident, $struct_device: ident) => { + convert_device_ref!($trait_device, $lock_device, $struct_device, RootPort); + }; +} + +impl PciDevOps for RootPort { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { let size = data.len(); diff --git a/devices/src/scsi/disk.rs b/devices/src/scsi/disk.rs index 9e1c98954..a3106890b 100644 --- a/devices/src/scsi/disk.rs +++ b/devices/src/scsi/disk.rs @@ -135,6 +135,61 @@ impl ScsiDevState { impl Device for ScsiDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base); + + fn realize(mut self) -> Result>> { + match self.scsi_type { + SCSI_TYPE_DISK => { + self.block_size = SCSI_DISK_DEFAULT_BLOCK_SIZE; + self.state.product = "STRA HARDDISK".to_string(); + } + SCSI_TYPE_ROM => { + self.block_size = SCSI_CDROM_DEFAULT_BLOCK_SIZE; + self.state.product = "STRA CDROM".to_string(); + } + _ => { + bail!("Scsi type {} does not support now", self.scsi_type); + } + } + + if let Some(serial) = &self.dev_cfg.serial { + self.state.serial = serial.clone(); + } + + let drive_files = self.drive_files.lock().unwrap(); + // File path can not be empty string. And it has also been checked in command parsing by using `Clap`. + let file = VmConfig::fetch_drive_file(&drive_files, &self.drive_cfg.path_on_host)?; + + let alignments = VmConfig::fetch_drive_align(&drive_files, &self.drive_cfg.path_on_host)?; + self.req_align = alignments.0; + self.buf_align = alignments.1; + let drive_id = VmConfig::get_drive_id(&drive_files, &self.drive_cfg.path_on_host)?; + drop(drive_files); + + let mut thread_pool = None; + if self.drive_cfg.aio != AioEngine::Off { + thread_pool = Some(EventLoop::get_ctx(None).unwrap().thread_pool.clone()); + } + let aio = Aio::new(Arc::new(aio_complete_cb), self.drive_cfg.aio, thread_pool)?; + let conf = BlockProperty { + id: drive_id, + format: self.drive_cfg.format, + iothread: self.iothread.clone(), + direct: self.drive_cfg.direct, + req_align: self.req_align, + buf_align: self.buf_align, + discard: false, + write_zeroes: WriteZeroesState::Off, + l2_cache_size: self.drive_cfg.l2_cache_size, + refcount_cache_size: self.drive_cfg.refcount_cache_size, + }; + let backend = create_block_backend(file, aio, conf)?; + let disk_size = backend.lock().unwrap().disk_size()?; + self.block_backend = Some(backend); + self.disk_sectors = disk_size >> SECTOR_SHIFT; + + let dev = Arc::new(Mutex::new(self)); + Ok(dev) + } } /// Convert from Arc> to &ScsiDevice. @@ -206,61 +261,6 @@ impl ScsiDevice { scsi_dev.set_parent_bus(scsi_bus); scsi_dev } - - pub fn realize(mut self) -> Result>> { - match self.scsi_type { - SCSI_TYPE_DISK => { - self.block_size = SCSI_DISK_DEFAULT_BLOCK_SIZE; - self.state.product = "STRA HARDDISK".to_string(); - } - SCSI_TYPE_ROM => { - self.block_size = SCSI_CDROM_DEFAULT_BLOCK_SIZE; - self.state.product = "STRA CDROM".to_string(); - } - _ => { - bail!("Scsi type {} does not support now", self.scsi_type); - } - } - - if let Some(serial) = &self.dev_cfg.serial { - self.state.serial = serial.clone(); - } - - let drive_files = self.drive_files.lock().unwrap(); - // File path can not be empty string. And it has also been checked in command parsing by using `Clap`. - let file = VmConfig::fetch_drive_file(&drive_files, &self.drive_cfg.path_on_host)?; - - let alignments = VmConfig::fetch_drive_align(&drive_files, &self.drive_cfg.path_on_host)?; - self.req_align = alignments.0; - self.buf_align = alignments.1; - let drive_id = VmConfig::get_drive_id(&drive_files, &self.drive_cfg.path_on_host)?; - drop(drive_files); - - let mut thread_pool = None; - if self.drive_cfg.aio != AioEngine::Off { - thread_pool = Some(EventLoop::get_ctx(None).unwrap().thread_pool.clone()); - } - let aio = Aio::new(Arc::new(aio_complete_cb), self.drive_cfg.aio, thread_pool)?; - let conf = BlockProperty { - id: drive_id, - format: self.drive_cfg.format, - iothread: self.iothread.clone(), - direct: self.drive_cfg.direct, - req_align: self.req_align, - buf_align: self.buf_align, - discard: false, - write_zeroes: WriteZeroesState::Off, - l2_cache_size: self.drive_cfg.l2_cache_size, - refcount_cache_size: self.drive_cfg.refcount_cache_size, - }; - let backend = create_block_backend(file, aio, conf)?; - let disk_size = backend.lock().unwrap().disk_size()?; - self.block_backend = Some(backend); - self.disk_sectors = disk_size >> SECTOR_SHIFT; - - let dev = Arc::new(Mutex::new(self)); - Ok(dev) - } } #[cfg(test)] diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 2c03f09a3..e8765ea9b 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -28,12 +28,12 @@ use super::descriptor::{ use super::xhci::xhci_controller::XhciDevice; use super::{config::*, USB_DEVICE_BUFFER_DEFAULT_LEN}; use super::{UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus}; -use crate::Bus; use crate::ScsiBus::{ get_scsi_key, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, EMULATE_SCSI_OPS, GOOD, SCSI_CMD_BUF_SIZE, }; use crate::ScsiDisk::{ScsiDevConfig, ScsiDevice}; +use crate::{Bus, Device}; use machine_manager::config::{DriveConfig, DriveFile}; use util::aio::AioEngine; use util::gen_base_func; diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index d4c9dfbcc..797800331 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -34,13 +34,13 @@ use super::{ UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus, USB_DEVICE_BUFFER_DEFAULT_LEN, }; -use crate::Bus; use crate::ScsiBus::{ get_scsi_key, scsi_cdb_xfer, ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, EMULATE_SCSI_OPS, GOOD, SCSI_SENSE_INVALID_PARAM_VALUE, SCSI_SENSE_INVALID_TAG, SCSI_SENSE_NO_SENSE, }; use crate::ScsiDisk::{ScsiDevConfig, ScsiDevice}; +use crate::{Bus, Device}; use machine_manager::config::{DriveConfig, DriveFile}; use util::byte_code::ByteCode; use util::gen_base_func; diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 67afc3caf..07b2bb2b3 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -247,16 +247,7 @@ impl Device for XhciPciDevice { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - trace::usb_xhci_exit(); - Ok(()) - } -} - -impl PciDevOps for XhciPciDevice { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; le_write_u16( @@ -351,9 +342,18 @@ impl PciDevOps for XhciPciDevice { let dev = Arc::new(Mutex::new(self)); // Attach to the PCI bus. let bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); - bus.lock().unwrap().attach_child(devfn, dev)?; + bus.lock().unwrap().attach_child(devfn, dev.clone())?; + Ok(dev) + } + + fn unrealize(&mut self) -> Result<()> { + trace::usb_xhci_exit(); Ok(()) } +} + +impl PciDevOps for XhciPciDevice { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 9c04f55fa..578029127 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -19,7 +19,7 @@ use crate::{register_shutdown_event, LightMachine, MachineOps}; use address_space::{AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; use devices::legacy::{PL011, PL031}; -use devices::{ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; +use devices::{Device, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; use hypervisor::kvm::aarch64::*; use machine_manager::config::{Param, SerialConfig, VmConfig}; use migration::{MigrationManager, MigrationStatus}; @@ -111,7 +111,10 @@ impl MachineOps for LightMachine { MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, )?; - pl031.realize().with_context(|| "Failed to realize pl031.") + pl031 + .realize() + .with_context(|| "Failed to realize pl031.")?; + Ok(()) } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index d3566f3c2..b3950f24a 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -43,12 +43,8 @@ impl PciHostRoot { impl Device for PciHostRoot { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); -} - -impl PciDevOps for PciHostRoot { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -71,10 +67,15 @@ impl PciDevOps for PciHostRoot { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = parent_bus.lock().unwrap(); - locked_bus.attach_child(0, Arc::new(Mutex::new(self)))?; + let dev = Arc::new(Mutex::new(self)); + locked_bus.attach_child(0, dev.clone())?; - Ok(()) + Ok(dev) } +} + +impl PciDevOps for PciHostRoot { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { self.base.config.write(offset, data, 0, None); diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index a848e1201..9be74e0c9 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -48,7 +48,7 @@ use devices::legacy::{ }; #[cfg(feature = "ramfb")] use devices::legacy::{Ramfb, RamfbConfig}; -use devices::pci::{PciBus, PciDevOps, PciHost, PciIntxState}; +use devices::pci::{PciBus, PciHost, PciIntxState}; use devices::sysbus::{to_sysbusdevops, SysBusDevType}; use devices::{ convert_bus_mut, Device, ICGICConfig, ICGICv3Config, GIC_IRQ_MAX, MUT_PCI_BUS, SYS_BUS_DEVICE, @@ -339,7 +339,8 @@ impl StdMachineOps for StdMachine { let pcihost_root = PciHostRoot::new(root_bus); pcihost_root .realize() - .with_context(|| "Failed to realize pcihost root device.") + .with_context(|| "Failed to realize pcihost root device.")?; + Ok(()) } fn add_fwcfg_device(&mut self, nr_cpus: u8) -> Result>>> { @@ -461,7 +462,8 @@ impl MachineOps for StdMachine { MEM_LAYOUT[LayoutEntryType::Rtc as usize].0, MEM_LAYOUT[LayoutEntryType::Rtc as usize].1, )?; - rtc.realize().with_context(|| "Failed to realize PL031") + rtc.realize().with_context(|| "Failed to realize PL031")?; + Ok(()) } fn add_ged_device(&mut self) -> Result<()> { @@ -709,7 +711,8 @@ impl MachineOps for StdMachine { let mut ramfb = Ramfb::new(sys_mem.clone(), &self.base.sysbus, config.install); ramfb.ramfb_state.setup(&fwcfg_dev)?; - ramfb.realize() + ramfb.realize()?; + Ok(()) } fn get_pci_host(&mut self) -> Result<&Arc>> { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index d6e7813df..8a47faacd 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1945,7 +1945,8 @@ pub trait MachineOps: MachineLifecycle { let sys_mem = self.get_sys_mem().clone(); let demo_dev = DemoDev::new(config, devfn, sys_mem, parent_bus); - demo_dev.realize() + demo_dev.realize()?; + Ok(()) } /// Return the syscall whitelist for seccomp. diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 55fb20375..53f126265 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -51,6 +51,7 @@ use cpu::CpuLifecycleState; #[cfg(target_arch = "x86_64")] use devices::sysbus::SysBusDevOps; use devices::sysbus::{IRQ_BASE, IRQ_MAX}; +use devices::Device; #[cfg(target_arch = "x86_64")] use machine_manager::config::Param; use machine_manager::config::{ diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index e93f85e41..bb9f0e8ff 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -230,12 +230,8 @@ impl LPCBridge { impl Device for LPCBridge { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); -} -impl PciDevOps for LPCBridge { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -278,9 +274,14 @@ impl PciDevOps for LPCBridge { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = parent_bus.lock().unwrap(); - locked_bus.attach_child(0x1F << 3, Arc::new(Mutex::new(self)))?; - Ok(()) + let dev = Arc::new(Mutex::new(self)); + locked_bus.attach_child(0x1F << 3, dev.clone())?; + Ok(dev) } +} + +impl PciDevOps for LPCBridge { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { self.base.config.write(offset, data, 0, None, None); diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index 9ec0d110d..0aec6ec89 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -112,12 +112,8 @@ impl Mch { impl Device for Mch { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); -} -impl PciDevOps for Mch { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -139,9 +135,14 @@ impl PciDevOps for Mch { let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = parent_bus.lock().unwrap(); - locked_bus.attach_child(0, Arc::new(Mutex::new(self)))?; - Ok(()) + let dev = Arc::new(Mutex::new(self)); + locked_bus.attach_child(0, dev.clone())?; + Ok(dev) } +} + +impl PciDevOps for Mch { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn write_config(&mut self, offset: usize, data: &[u8]) { let old_pciexbar: u64 = le_read_u64(&self.base.config.config, PCIEXBAR as usize).unwrap(); diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index f33189bca..8f0494b5c 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -18,7 +18,8 @@ use crate::micro_common::syscall::syscall_whitelist; use crate::{register_shutdown_event, LightMachine, MachineBase, MachineError, MachineOps}; use address_space::{AddressSpace, Region}; use cpu::{CPUBootConfig, CPUTopology}; -use devices::legacy::FwCfgOps; +use devices::legacy::{FwCfgOps, Serial, SERIAL_ADDR}; +use devices::Device; use hypervisor::kvm::x86_64::*; use hypervisor::kvm::*; use machine_manager::config::{SerialConfig, VmConfig}; @@ -128,14 +129,13 @@ impl MachineOps for LightMachine { } fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()> { - use devices::legacy::{Serial, SERIAL_ADDR}; - let region_base: u64 = SERIAL_ADDR; let region_size: u64 = 8; let serial = Serial::new(config.clone(), &self.base.sysbus, region_base, region_size)?; serial .realize() - .with_context(|| "Failed to realize serial device.") + .with_context(|| "Failed to realize serial device.")?; + Ok(()) } fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index b1cfe2857..901f054c4 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -36,7 +36,7 @@ use devices::legacy::{ error::LegacyError as DevErrorKind, FwCfgEntryType, FwCfgIO, FwCfgOps, PFlash, Serial, RTC, SERIAL_ADDR, }; -use devices::pci::{PciBus, PciDevOps, PciHost}; +use devices::pci::{PciBus, PciHost}; use devices::{convert_bus_mut, Device, MUT_PCI_BUS}; use hypervisor::kvm::x86_64::*; use hypervisor::kvm::*; @@ -191,7 +191,8 @@ impl StdMachine { .with_context(|| "Fail to register reset event in LPC")?; register_shutdown_event(ich.shutdown_req.clone(), vm) .with_context(|| "Fail to register shutdown event in LPC")?; - ich.realize() + ich.realize()?; + Ok(()) } pub fn get_vcpu_reg_val(&self, _addr: u64, _vcpu: usize) -> Option { @@ -268,7 +269,8 @@ impl StdMachineOps for StdMachine { .with_context(|| "Failed to register CONFIG_DATA port in I/O space.")?; let mch = Mch::new(root_bus, mmconfig_region, mmconfig_region_ops); - mch.realize() + mch.realize()?; + Ok(()) } fn add_fwcfg_device( @@ -443,7 +445,8 @@ impl MachineOps for StdMachine { + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); rtc.realize() - .with_context(|| "Failed to realize RTC device") + .with_context(|| "Failed to realize RTC device")?; + Ok(()) } fn add_ged_device(&mut self) -> Result<()> { diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 22ae5945c..d52ea581c 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -822,19 +822,7 @@ impl Device for VfioPciDevice { }) } - fn unrealize(&mut self) -> Result<()> { - if let Err(e) = VfioPciDevice::unrealize(self) { - error!("{:?}", e); - bail!("Failed to unrealize vfio-pci."); - } - Ok(()) - } -} - -impl PciDevOps for VfioPciDevice { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { let parent_bus = self.parent_bus().unwrap(); self.init_write_mask(false)?; self.init_write_clear_mask(false)?; @@ -880,10 +868,22 @@ impl PciDevOps for VfioPciDevice { let dev = Arc::new(Mutex::new(self)); let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = parent_bus.lock().unwrap(); - locked_bus.attach_child(devfn, dev)?; + locked_bus.attach_child(devfn, dev.clone())?; + Ok(dev) + } + + fn unrealize(&mut self) -> Result<()> { + if let Err(e) = VfioPciDevice::unrealize(self) { + error!("{:?}", e); + bail!("Failed to unrealize vfio-pci."); + } Ok(()) } +} + +impl PciDevOps for VfioPciDevice { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); /// Read pci data from pci config if it emulate, otherwise read from vfio device. fn read_config(&mut self, offset: usize, data: &mut [u8]) { diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index cab3973d9..f01f87c05 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -164,22 +164,6 @@ impl VirtioMmioDevice { Ok(mmio_device) } - pub fn realize(mut self) -> Result>> { - self.assign_interrupt_cb(); - self.device - .lock() - .unwrap() - .realize() - .with_context(|| "Failed to realize virtio.")?; - - let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); - MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); - let dev = Arc::new(Mutex::new(self)); - sysbus.attach_device(&dev)?; - - Ok(dev) - } - /// Activate the virtio device, this function is called by vcpu thread when frontend /// virtio driver is ready and write `DRIVER_OK` to backend. fn activate(&mut self) -> Result<()> { @@ -378,6 +362,22 @@ impl VirtioMmioDevice { impl Device for VirtioMmioDevice { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); + + fn realize(mut self) -> Result>> { + self.assign_interrupt_cb(); + self.device + .lock() + .unwrap() + .realize() + .with_context(|| "Failed to realize virtio.")?; + + let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); + MUT_SYS_BUS!(parent_bus, locked_bus, sysbus); + let dev = Arc::new(Mutex::new(self)); + sysbus.attach_device(&dev)?; + + Ok(dev) + } } impl SysBusDevOps for VirtioMmioDevice { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index d33c47137..8a058d8ea 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1016,28 +1016,7 @@ impl Device for VirtioPciDevice { Ok(()) } - fn unrealize(&mut self) -> Result<()> { - info!("func: unrealize, id: {:?}", &self.base.base.id); - self.device - .lock() - .unwrap() - .unrealize() - .with_context(|| "Failed to unrealize the virtio device")?; - - let bus = self.parent_bus().unwrap().upgrade().unwrap(); - self.base.config.unregister_bars(&bus)?; - - MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name()); - MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name()); - - Ok(()) - } -} - -impl PciDevOps for VirtioPciDevice { - gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); - - fn realize(mut self) -> Result<()> { + fn realize(mut self) -> Result>> { info!("func: realize, id: {:?}", &self.base.base.id); let parent_bus = self.parent_bus().unwrap(); self.init_write_mask(false)?; @@ -1191,10 +1170,35 @@ impl PciDevOps for VirtioPciDevice { let bus = parent_bus.upgrade().unwrap(); bus.lock().unwrap().attach_child(devfn, dev.clone())?; - MigrationManager::register_transport_instance(VirtioPciState::descriptor(), dev, &name); + MigrationManager::register_transport_instance( + VirtioPciState::descriptor(), + dev.clone(), + &name, + ); + + Ok(dev) + } + + fn unrealize(&mut self) -> Result<()> { + info!("func: unrealize, id: {:?}", &self.base.base.id); + self.device + .lock() + .unwrap() + .unrealize() + .with_context(|| "Failed to unrealize the virtio device")?; + + let bus = self.parent_bus().unwrap().upgrade().unwrap(); + self.base.config.unregister_bars(&bus)?; + + MigrationManager::unregister_device_instance(MsixState::descriptor(), &self.name()); + MigrationManager::unregister_transport_instance(VirtioPciState::descriptor(), &self.name()); Ok(()) } +} + +impl PciDevOps for VirtioPciDevice { + gen_base_func!(pci_base, pci_base_mut, PciDevBase, base); fn read_config(&mut self, offset: usize, data: &mut [u8]) { trace::virtio_tpt_read_config(&self.base.base.id, offset as u64, data.len()); -- Gitee From 26b00e76a3dbe00440d14489a29c4872c8274954 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 14 Jul 2024 12:21:58 +0800 Subject: [PATCH 1955/2187] clippy: fix clippy warnings. Fix clippy warning: error: casting to the same type is unnecessary (usize -> usize) --> image/src/cmdline.rs:73:28 | 73 | let str = args[idx as usize].clone(); | ^^^^^^^^^^^^ help: try: idx | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast = note: -D clippy::unnecessary-cast implied by -D warnings Signed-off-by: liuxiangdong --- image/src/cmdline.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image/src/cmdline.rs b/image/src/cmdline.rs index 8cf7c392b..8235ff5d3 100644 --- a/image/src/cmdline.rs +++ b/image/src/cmdline.rs @@ -70,7 +70,7 @@ impl ArgsParse { let mut pre_opt = (0, "".to_string()); for idx in 0..len { - let str = args[idx as usize].clone(); + let str = args[idx].clone(); if str.starts_with('-') && str.len() > 1 { if !pre_opt.1.is_empty() { bail!("missing argument for option '{}'", pre_opt.1); -- Gitee From b10610c8702f951af82ccb9379fc219da9087f8a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 14 Jul 2024 15:22:06 +0800 Subject: [PATCH 1956/2187] vhost-vsock: vsock should not support write_config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to Virtio Spec: Socket device configuration uses the following layout structure: struct virtio_vsock_config { le64 guest_cid; }; The guest_cid field contains the guest’s context ID, which uniquely identifies the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed. So, config space of vsock should not be changed in the lifetime, which means `write_config` should not be supported. Signed-off-by: liuxiangdong --- virtio/src/vhost/kernel/vsock.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 4f2d1a445..e6580ffaa 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -23,8 +23,8 @@ use vmm_sys_util::ioctl::ioctl_with_ref; use super::super::{VhostIoHandler, VhostNotify, VhostOps}; use super::{VhostBackend, VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; use crate::{ - check_config_space_rw, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, - VirtioInterruptType, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_TYPE_VSOCK, + Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, + VIRTIO_F_ACCESS_PLATFORM, VIRTIO_TYPE_VSOCK, }; use address_space::AddressSpace; use machine_manager::config::{get_pci_df, parse_bool, valid_id, DEFAULT_VIRTQUEUE_SIZE}; @@ -234,10 +234,7 @@ impl VirtioDevice for Vsock { Ok(()) } - fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { - check_config_space_rw(&self.config_space, offset, data)?; - let data_len = data.len(); - self.config_space[(offset as usize)..(offset as usize + data_len)].copy_from_slice(data); + fn write_config(&mut self, _offset: u64, _data: &[u8]) -> Result<()> { Ok(()) } -- Gitee From 17777c3e2f4539f6c5cb237e783f7195770dc846 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 16 Jul 2024 09:59:32 +0800 Subject: [PATCH 1957/2187] chardev: check output validation When the socket is closed, output would be set to None. So we should check output validation otherwise it would panic low potentially. Signed-off-by: Zhao Yi Min --- chardev_backend/src/chardev.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index 8b065f992..d13d306e2 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -301,6 +301,9 @@ impl Chardev { } fn consume_outbuf(&mut self) -> Result<()> { + if self.output.is_none() { + bail!("no output interface"); + } let output = self.output.as_ref().unwrap(); while !self.outbuf.is_empty() { if write_buffer_async(output.clone(), self.outbuf.front_mut().unwrap())? { -- Gitee From 93829022f06d574788b7c1b4183b1796c06b4809 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 17 Jul 2024 09:20:17 +0800 Subject: [PATCH 1958/2187] virtio-mmio: fix error Configuration space register address range According to Virtio Spec: 0x100+ is Configuration space register. Device-specific configuration space starts at the offset 0x100 and is accessed with byte alignment. Its meaning and size depend on the device and the driver. And each virtio-mmio device has a mmio range sized 0x200, (`MEM_LAYOUT[LayoutEntryType::Mmio as usize].1). Thus, the right `Configuration space register` range is `[0x100, 0x1ff]`. Fix it. Signed-off-by: liuxiangdong --- virtio/src/transport/virtio_mmio.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index f01f87c05..42892b0d8 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -400,7 +400,7 @@ impl SysBusDevOps for VirtioMmioDevice { }; LittleEndian::write_u32(data, value); } - 0x100..=0xfff => { + 0x100..=0x1ff => { if let Err(ref e) = self .device .lock() @@ -460,7 +460,7 @@ impl SysBusDevOps for VirtioMmioDevice { self.device.lock().unwrap().set_device_activated(true); } } - 0x100..=0xfff => { + 0x100..=0x1ff => { let mut locked_device = self.device.lock().unwrap(); if locked_device.check_device_status(CONFIG_STATUS_DRIVER, CONFIG_STATUS_FAILED) { if let Err(ref e) = locked_device.write_config(offset - 0x100, data) { @@ -845,7 +845,7 @@ mod tests { // read the unknown register let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; assert_eq!(virtio_mmio_device.read(&mut buf[..], addr, 0xf1), false); - assert_eq!(virtio_mmio_device.read(&mut buf[..], addr, 0xfff + 1), true); + assert_eq!(virtio_mmio_device.read(&mut buf[..], addr, 0x1ff + 1), true); assert_eq!(buf, [0xff, 0xff, 0xff, 0xff]); // read the configuration space of virtio device -- Gitee From 8e181fe3f6bfb414b1434e24cb61d4f85e51bb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A5=9A=E5=BD=B1?= Date: Mon, 15 Jul 2024 17:20:05 +0800 Subject: [PATCH 1959/2187] ohui: adapt to the new update ledstate interface When typing keyboards keys, obatin and refresh the state of ledstate, ensure that the keyboard status in the vm is synchronized with the pc. Signed-off-by: Jiahong Li --- ui/src/input.rs | 28 ---------------------------- ui/src/ohui_srv/mod.rs | 7 ++----- ui/src/ohui_srv/msg.rs | 8 ++------ ui/src/ohui_srv/msg_handle.rs | 34 ++++++++++------------------------ 4 files changed, 14 insertions(+), 63 deletions(-) diff --git a/ui/src/input.rs b/ui/src/input.rs index d540c998e..59f2b931f 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -252,23 +252,6 @@ impl KeyBoardState { #[derive(Default)] struct LedState { kbd_led: u8, - sync: Option>, -} - -pub trait SyncLedstate: Send + Sync { - fn sync_to_host(&self, state: u8) { - debug!("ledstate in guest is {}", state); - } -} - -impl LedState { - fn register_led_sync(&mut self, sync: Arc) { - self.sync = Some(sync); - } - - fn unregister_led_sync(&mut self) { - self.sync = None; - } } #[derive(Default)] @@ -345,14 +328,6 @@ impl Inputs { } } -pub fn register_led_sync(sync: Arc) { - LED_STATE.lock().unwrap().register_led_sync(sync); -} - -pub fn unregister_led_sync() { - LED_STATE.lock().unwrap().unregister_led_sync(); -} - pub fn register_keyboard(device: &str, kbd: Arc>) { INPUTS.lock().unwrap().register_kbd(device, kbd); } @@ -490,9 +465,6 @@ pub fn get_kbd_led_state() -> u8 { pub fn set_kbd_led_state(state: u8) { LED_STATE.lock().unwrap().kbd_led = state; - if let Some(sync_cb) = LED_STATE.lock().unwrap().sync.as_ref() { - sync_cb.sync_to_host(state); - } } pub fn keyboard_modifier_get(key_mod: KeyboardModifier) -> bool { diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 899d26342..b650946d1 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -35,7 +35,6 @@ use crate::{ DisplayChangeListenerOperations, DisplayMouse, DisplaySurface, DISPLAY_UPDATE_INTERVAL_DEFAULT, }, - input::{register_led_sync, unregister_led_sync}, pixman::{bytes_per_pixel, get_image_data, ref_pixman_image, unref_pixman_image}, }; use address_space::FileBackend; @@ -95,7 +94,7 @@ pub struct OhUiServer { // transfer channel via unix sock channel: Arc>, // message handler - msg_handler: Arc, + msg_handler: OhUiMsgHandler, // connected or not connected: AtomicBool, // iothread processing unix socket @@ -181,7 +180,7 @@ impl OhUiServer { passthru: OnceCell::new(), surface: RwLock::new(GuestSurface::new()), channel, - msg_handler: Arc::new(OhUiMsgHandler::new()), + msg_handler: OhUiMsgHandler::new(), connected: AtomicBool::new(false), iothread: OnceCell::new(), cursorbuffer, @@ -267,9 +266,7 @@ impl OhUiServer { self.connected.store(conn, Ordering::Relaxed); if conn { self.msg_handler.update_sock(self.channel.clone()); - register_led_sync(self.msg_handler.clone()); } else { - unregister_led_sync(); self.channel.lock().unwrap().disconnect(); self.msg_handler.reset(); } diff --git a/ui/src/ohui_srv/msg.rs b/ui/src/ohui_srv/msg.rs index c359d71f5..1ce2d4465 100755 --- a/ui/src/ohui_srv/msg.rs +++ b/ui/src/ohui_srv/msg.rs @@ -110,6 +110,8 @@ impl ByteCode for MouseMotionEvent {} pub struct KeyboardEvent { pub key_action: u16, pub keycode: u16, + pub led_state: u8, + pad: [u8; 3], } impl ByteCode for KeyboardEvent {} @@ -132,12 +134,6 @@ pub struct LedstateEvent { impl ByteCode for LedstateEvent {} -impl LedstateEvent { - pub fn new(state: u32) -> Self { - LedstateEvent { state } - } -} - #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct GreetEvent { diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 1e085e520..2ce24ca65 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -27,11 +27,10 @@ use crate::{ console::{get_active_console, graphic_hardware_ui_info}, input::{ self, get_kbd_led_state, input_button, input_move_abs, input_point_sync, keyboard_update, - release_all_key, trigger_key, Axis, SyncLedstate, ABS_MAX, CAPS_LOCK_LED, - INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, - INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, INPUT_POINT_FORWARD, INPUT_POINT_LEFT, - INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, KEYCODE_CAPS_LOCK, KEYCODE_NUM_LOCK, - KEYCODE_SCR_LOCK, NUM_LOCK_LED, SCROLL_LOCK_LED, + release_all_key, trigger_key, Axis, ABS_MAX, CAPS_LOCK_LED, INPUT_BUTTON_WHEEL_DOWN, + INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, + INPUT_POINT_FORWARD, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, + KEYCODE_CAPS_LOCK, KEYCODE_NUM_LOCK, KEYCODE_SCR_LOCK, NUM_LOCK_LED, SCROLL_LOCK_LED, }, keycode::{DpyMod, KeyCode}, }; @@ -133,17 +132,6 @@ pub struct OhUiMsgHandler { writer: Mutex>, } -impl SyncLedstate for OhUiMsgHandler { - fn sync_to_host(&self, state: u8) { - if let Some(writer) = self.writer.lock().unwrap().as_mut() { - let body = LedstateEvent::new(state as u32); - if let Err(e) = writer.send_message(EventType::Ledstate, &body) { - error!("sync_to_host: failed to send message with error {e}"); - } - } - } -} - impl OhUiMsgHandler { pub fn new() -> Self { OhUiMsgHandler { @@ -206,11 +194,7 @@ impl OhUiMsgHandler { } Ok(()) } - EventType::Ledstate => { - let body = LedstateEvent::from_bytes(&body_bytes[..]).unwrap(); - self.handle_ledstate(body); - Ok(()) - } + EventType::Ledstate => Ok(()), EventType::Greet => { let body = GreetEvent::from_bytes(&body_bytes[..]).unwrap(); trace::oh_event_greet(body.token_id); @@ -273,9 +257,11 @@ impl OhUiMsgHandler { } fn handle_keyboard(&self, ke: &KeyboardEvent) -> Result<()> { - if self.state.lock().unwrap().led_state.is_some() { - self.state.lock().unwrap().sync_kbd_led_state()?; - } + self.state + .lock() + .unwrap() + .update_host_ledstate(ke.led_state); + self.state.lock().unwrap().sync_kbd_led_state()?; let hmkey = ke.keycode; let keycode = match self.hmcode2svcode.get(&hmkey) { Some(k) => *k, -- Gitee From 5cea1f51bf1d656c177d3df144f9e0b05453bc79 Mon Sep 17 00:00:00 2001 From: Mingwang Li Date: Wed, 17 Jul 2024 14:27:26 +0800 Subject: [PATCH 1960/2187] bugfix: resume VM before let vm exit After the VM is paused, if the management process exits, the VM can be shutdown only after it is resumed. Signed-off-by: Mingwang Li --- machine/src/aarch64/standard.rs | 3 ++- machine/src/lib.rs | 42 +++++++++++++++++++++++++++++- machine/src/standard_common/mod.rs | 22 ---------------- machine/src/x86_64/standard.rs | 3 ++- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 76b3b1405..c525e385d 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -615,10 +615,11 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to init display")?; #[cfg(feature = "windows_emu_pid")] - locked_vm.watch_windows_emu_pid( + crate::watch_windows_emu_pid( vm_config, locked_vm.power_button.clone(), locked_vm.shutdown_req.clone(), + vm.clone(), ); MigrationManager::register_vm_config(locked_vm.get_vm_config()); diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8ab05262f..067a4e1fc 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -2365,10 +2365,21 @@ fn check_windows_emu_pid( pid_path: String, powerdown_req: Arc, shutdown_req: Arc, + vm: Arc>, ) { let mut check_delay = Duration::from_millis(WINDOWS_EMU_PID_DEFAULT_INTERVAL); if !Path::new(&pid_path).exists() { - log::info!("Detect emulator exited, let VM exits now"); + info!("Detect emulator exited, let VM exits now"); + let locked_vm = vm.read().unwrap(); + let mut vm_state = locked_vm.get_vm_state().deref().0.lock().unwrap(); + if *vm_state == VmState::Paused { + info!("VM state is paused, resume VM before exit"); + if let Err(e) = locked_vm.vm_resume(&locked_vm.machine_base().cpus, &mut vm_state) { + log::error!("Failed to resume VM when check windows emu pid: {:?}", e); + } + } + drop(vm_state); + drop(locked_vm); if get_run_stage() == VmRunningStage::Os { // Wait 30s for windows normal exit. check_delay = Duration::from_millis(WINDOWS_EMU_PID_POWERDOWN_INTERVAL); @@ -2389,6 +2400,35 @@ fn check_windows_emu_pid( pid_path.clone(), powerdown_req.clone(), shutdown_req.clone(), + vm.clone(), + ); + }); + EventLoop::get_ctx(None) + .unwrap() + .timer_add(check_emu_alive, check_delay); +} + +/// When windows emu exits, stratovirt should exits too. +#[cfg(feature = "windows_emu_pid")] +pub(crate) fn watch_windows_emu_pid( + vm_config: &VmConfig, + power_button: Arc, + shutdown_req: Arc, + vm: Arc>, +) { + let emu_pid = vm_config.emulator_pid.as_ref(); + if emu_pid.is_none() { + return; + } + info!("Watching on emulator lifetime"); + let pid_path = "/proc/".to_owned() + emu_pid.unwrap(); + let check_delay = Duration::from_millis(WINDOWS_EMU_PID_DEFAULT_INTERVAL); + let check_emu_alive = Box::new(move || { + check_windows_emu_pid( + pid_path.clone(), + power_button.clone(), + shutdown_req.clone(), + vm.clone(), ); }); EventLoop::get_ctx(None) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 6818dce32..0a116b3b3 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -59,8 +59,6 @@ use devices::pci::{PciBus, PciHost}; use machine_manager::config::get_cameradev_config; #[cfg(target_arch = "aarch64")] use machine_manager::config::ShutdownAction; -#[cfg(feature = "windows_emu_pid")] -use machine_manager::config::VmConfig; use machine_manager::config::{ get_chardev_config, get_netdev_config, memory_unit_conversion, parse_incoming_uri, BootIndexInfo, ConfigCheck, DiskFormat, DriveConfig, ExBool, MigrateMode, NumaNode, NumaNodes, @@ -888,26 +886,6 @@ impl StdMachine { self.detach_usb_from_xhci_controller(&mut locked_vmconfig, id) } - /// When windows emu exits, stratovirt should exits too. - #[cfg(feature = "windows_emu_pid")] - pub(crate) fn watch_windows_emu_pid( - &self, - vm_config: &VmConfig, - power_button: Arc, - shutdown_req: Arc, - ) { - let emu_pid = vm_config.emulator_pid.as_ref(); - if emu_pid.is_none() { - return; - } - log::info!("Watching on emulator lifetime"); - crate::check_windows_emu_pid( - "/proc/".to_owned() + emu_pid.unwrap(), - power_button, - shutdown_req, - ); - } - #[cfg(target_arch = "x86_64")] fn plug_cpu_device(&mut self, args: &qmp_schema::DeviceAddArgument) -> Result<()> { if self.get_numa_nodes().is_some() { diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index c9c80f79c..68cbf8a5c 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -550,10 +550,11 @@ impl MachineOps for StdMachine { .with_context(|| "Fail to init display")?; #[cfg(feature = "windows_emu_pid")] - locked_vm.watch_windows_emu_pid( + crate::watch_windows_emu_pid( vm_config, locked_vm.shutdown_req.clone(), locked_vm.shutdown_req.clone(), + vm.clone(), ); MigrationManager::register_vm_config(locked_vm.get_vm_config()); -- Gitee From 5628e02c1f2e55b93b0b58a9cc24afc8a776d699 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 18 Jul 2024 04:52:50 +0800 Subject: [PATCH 1961/2187] virtio: virtio ut should init Eventloop if test `activate` The virtio `activate` function involves the `Eventloop`. If do `activate` in UT tests, `Eventloop` needs to be initialized first. Otherwise executing a single UT will result in an error. Signed-off-by: liuxiangdong --- virtio/src/device/balloon.rs | 31 +++++++++++++++++++------------ virtio/src/device/block.rs | 1 + virtio/src/device/net.rs | 4 ++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 7240a3314..4c981c185 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1240,6 +1240,7 @@ mod tests { use crate::tests::{address_space_init, MEMORY_SIZE}; use crate::*; use address_space::{AddressRange, HostMemMapping, Region}; + use machine_manager::event_loop::EventLoop; const QUEUE_SIZE: u16 = 256; @@ -1532,6 +1533,8 @@ mod tests { #[test] fn test_balloon_activate() { + EventLoop::object_init(&None).unwrap(); + let mem_space = address_space_init(); let interrupt_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); let interrupt_status = Arc::new(AtomicU32::new(0)); @@ -1548,18 +1551,20 @@ mod tests { }, ) as VirtioInterrupt); - let mut queue_config_inf = QueueConfig::new(QUEUE_SIZE); - queue_config_inf.desc_table = GuestAddress(0); - queue_config_inf.avail_ring = GuestAddress(4096); - queue_config_inf.used_ring = GuestAddress(8192); - queue_config_inf.ready = true; - queue_config_inf.size = QUEUE_SIZE; - let mut queues: Vec>> = Vec::new(); - let queue1 = Arc::new(Mutex::new(Queue::new(queue_config_inf, 1).unwrap())); - queues.push(queue1); - let event_inf = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); - let queue_evts: Vec> = vec![event_inf.clone()]; + let mut queue_evts: Vec> = Vec::new(); + for i in 0..QUEUE_NUM_BALLOON as u64 { + let mut queue_config_inf = QueueConfig::new(QUEUE_SIZE); + queue_config_inf.desc_table = GuestAddress(12288 * i + 0); + queue_config_inf.avail_ring = GuestAddress(12288 * i + 4096); + queue_config_inf.used_ring = GuestAddress(12288 * i + 8192); + queue_config_inf.ready = true; + queue_config_inf.size = QUEUE_SIZE; + let queue = Arc::new(Mutex::new(Queue::new(queue_config_inf, 1).unwrap())); + queues.push(queue); + let event_inf = Arc::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()); + queue_evts.push(event_inf); + } let bln_cfg = BalloonConfig { id: "bln".to_string(), @@ -1568,7 +1573,9 @@ mod tests { }; let mut bln = Balloon::new(bln_cfg, mem_space.clone()); bln.base.queues = queues; - assert!(bln.activate(mem_space, interrupt_cb, queue_evts).is_err()); + assert!(bln.activate(mem_space, interrupt_cb, queue_evts).is_ok()); + + EventLoop::loop_clean(); } #[test] diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 601b41d78..cb0a51c0c 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1746,5 +1746,6 @@ mod tests { break; } } + EventLoop::loop_clean(); } } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 05ac2857c..407bf67f9 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1941,6 +1941,8 @@ mod tests { #[test] fn test_iothread() { + EventLoop::object_init(&None).unwrap(); + let mut net = Net::new(NetworkInterfaceConfig::default(), NetDevcfg::default()); net.net_cfg.iothread = Some("iothread".to_string()); if let Err(err) = net.realize() { @@ -1952,5 +1954,7 @@ mod tests { } else { assert!(false); } + + EventLoop::loop_clean(); } } -- Gitee From 7d981102553c36cc4b01f22726e3f6c345715ed8 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Thu, 18 Jul 2024 18:54:57 +0800 Subject: [PATCH 1962/2187] Memory: change memory read/write scopes trace to events --- address_space/src/address_space.rs | 14 ++++---------- trace/trace_info/memory.toml | 8 ++++---- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index d34dd59e8..d19e6c3de 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -611,7 +611,7 @@ impl AddressSpace { /// /// Return Error if the `addr` is not mapped. pub fn read(&self, dst: &mut dyn std::io::Write, addr: GuestAddress, count: u64) -> Result<()> { - trace::trace_scope_start!(address_space_read, args = (&addr, count)); + trace::address_space_read(&addr, count); let view = self.flat_view.load(); view.read(dst, addr, count)?; @@ -630,7 +630,7 @@ impl AddressSpace { /// /// Return Error if the `addr` is not mapped. pub fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { - trace::trace_scope_start!(address_space_write, args = (&addr, count)); + trace::address_space_write(&addr, count); let view = self.flat_view.load(); let mut buf = Vec::new(); @@ -698,10 +698,7 @@ impl AddressSpace { /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { - trace::trace_scope_start!( - address_space_write_direct, - args = (host_addr, std::mem::size_of::()) - ); + trace::address_space_write_direct(host_addr, std::mem::size_of::()); // Mark vmm dirty page manually if live migration is active. MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); @@ -741,10 +738,7 @@ impl AddressSpace { /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn read_object_direct(&self, host_addr: u64) -> Result { - trace::trace_scope_start!( - address_space_read_direct, - args = (host_addr, std::mem::size_of::()) - ); + trace::address_space_read_direct(host_addr, std::mem::size_of::()); let mut obj = T::default(); let mut dst = obj.as_mut_bytes(); // SAFETY: host_addr is managed by address_space, it has been verified for legality. diff --git a/trace/trace_info/memory.toml b/trace/trace_info/memory.toml index 4ff485a5d..1a832e58f 100644 --- a/trace/trace_info/memory.toml +++ b/trace/trace_info/memory.toml @@ -1,22 +1,22 @@ -[[scopes]] +[[events]] name = "address_space_read" args = "addr: &dyn fmt::Debug, count: u64" message = "Memory: flatview_read addr {:?}, count {}" enabled = true -[[scopes]] +[[events]] name = "address_space_write" args = "addr: &dyn fmt::Debug, count: u64" message = "Memory: flatview_write addr {:?}, count {}" enabled = true -[[scopes]] +[[events]] name = "address_space_read_direct" args = "host_addr: u64, count: usize" message = "Memory: address_space_read_direct host_addr {}, count {}" enabled = true -[[scopes]] +[[events]] name = "address_space_write_direct" args = "host_addr: u64, count: usize" message = "Memory: address_space_write_direct host_addr {}, count {}" -- Gitee From d83e820be9304277ca38a6889209e43c9f17ee2c Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Thu, 18 Jul 2024 19:00:39 +0800 Subject: [PATCH 1963/2187] Balloon: change balloon scopes trace to events --- trace/trace_info/virtio.toml | 10 ++-------- virtio/src/device/balloon.rs | 5 ++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/trace/trace_info/virtio.toml b/trace/trace_info/virtio.toml index 1f8ea2415..d30ba1f77 100644 --- a/trace/trace_info/virtio.toml +++ b/trace/trace_info/virtio.toml @@ -298,20 +298,14 @@ args = "" message = "Vhost: deleting mem region failed: not matched." enabled = true -[[scopes]] +[[events]] name = "auto_msg_evt_handler" args = "" message = "Balloon: handle auto balloon message" enabled = true -[[scopes]] +[[events]] name = "reporting_evt_handler" args = "" message = "Balloon: handle fpr message" enabled = true - -[[scopes]] -name = "process_balloon_queue" -args = "" -message = "Balloon: handle normal balloon message" -enabled = true diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 97724ece9..7240a3314 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -610,7 +610,6 @@ impl BalloonIoHandler { /// if `req_type` is `BALLOON_INFLATE_EVENT`, then inflate the balloon, otherwise, deflate the /// balloon. fn process_balloon_queue(&mut self, req_type: bool) -> Result<()> { - trace::trace_scope_start!(process_balloon_queue); let queue = if req_type { trace::virtio_receive_request("Balloon".to_string(), "to inflate".to_string()); &self.inf_queue @@ -647,7 +646,7 @@ impl BalloonIoHandler { } fn reporting_evt_handler(&mut self) -> Result<()> { - trace::trace_scope_start!(reporting_evt_handler); + trace::reporting_evt_handler(); let queue = self .report_queue .as_ref() @@ -682,7 +681,7 @@ impl BalloonIoHandler { } fn auto_msg_evt_handler(&mut self) -> Result<()> { - trace::trace_scope_start!(auto_msg_evt_handler); + trace::auto_msg_evt_handler(); let queue = self .msg_queue .as_ref() -- Gitee From 9a4fac59db679b977d0f3776eabccf405a8134f6 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Thu, 18 Jul 2024 21:08:30 +0800 Subject: [PATCH 1964/2187] OHAudio:fix audio task's detroy logic we need to change status of audio tasks before we drop audio tasks. So audio callback function will get right task status. Signed-off-by: zhanghan --- devices/src/misc/scream/ohaudio.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 6fcee525c..1f5853056 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -297,8 +297,8 @@ impl OhAudioProcess for OhAudioRender { match status { OhAudioStatus::Paused => return, OhAudioStatus::Error => { - self.ctx = None; self.set_status(OhAudioStatus::Ready); + self.ctx = None; return; } OhAudioStatus::Started => self.flush(), @@ -431,8 +431,8 @@ impl OhAudioProcess for OhAudioCapture { match status { OhAudioStatus::Paused => return, _ => { - self.ctx = None; self.set_status(OhAudioStatus::Ready); + self.ctx = None; } } trace::oh_scream_capture_destroy(); -- Gitee From d9762d3764ad455fed05dee3d6e171850b2fcc83 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 12 Jul 2024 11:49:04 +0800 Subject: [PATCH 1965/2187] acpi: Add configuration interface for Hardware Signature ... which is used for VM S4 state. Signed-off-by: Keqian Zhu --- docs/config_guidebook.md | 8 ++++++++ machine/src/aarch64/standard.rs | 5 +++++ machine/src/standard_common/mod.rs | 17 +++++++++++++++-- machine/src/x86_64/standard.rs | 5 +++++ machine_manager/src/cmdline.rs | 14 ++++++++++++++ machine_manager/src/config/machine_config.rs | 5 +++++ machine_manager/src/config/mod.rs | 1 + 7 files changed, 53 insertions(+), 2 deletions(-) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a836f7041..c37d0437e 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -285,6 +285,14 @@ The SMBIOS specification defines the data structures and information that will e ``` +### 1.12 Hardware Signature +This option is used for configuring ACPI Hardware Signature, which is used for VM S4 state. It's an 32 bit integer. For more information, please refer to https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#firmware-acpi-control-structure-facs-table. + +```shell +# cmdline +-hardware-signature 1 +``` + ## 2. Device Configuration For machine type "microvm", only virtio-mmio and legacy devices are supported. diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index f2fe637e0..4cae6227d 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -1150,6 +1150,11 @@ impl AcpiBuilder for StdMachine { .with_context(|| "Fail to add PPTT table to loader")?; Ok(pptt_begin) } + + fn get_hardware_signature(&self) -> Option { + let vm_config = self.machine_base().vm_config.lock().unwrap(); + vm_config.hardware_signature + } } /// Function that helps to generate flash node in device-tree. diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index ee6d2cd1e..86ee0e263 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -142,7 +142,8 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let mut xsdt_entries = Vec::new(); - let facs_addr = Self::build_facs_table(&acpi_tables) + let facs_addr = self + .build_facs_table(&acpi_tables) .with_context(|| "Failed to build ACPI FACS table")?; let dsdt_addr = self @@ -697,12 +698,15 @@ pub(crate) trait AcpiBuilder { Ok(fadt_begin as u64) } + /// Get the Hardware Signature used to build FACS table. + fn get_hardware_signature(&self) -> Option; + /// Build ACPI FACS table, returns the offset of ACPI FACS table in `acpi_data`. /// /// # Arguments /// /// `acpi_data` - Bytes streams that ACPI tables converts to. - fn build_facs_table(acpi_data: &Arc>>) -> Result + fn build_facs_table(&self, acpi_data: &Arc>>) -> Result where Self: Sized, { @@ -715,6 +719,15 @@ pub(crate) trait AcpiBuilder { // FACS table length. facs_data[4] = 0x40; + // FACS table Hardware Signature. + if let Some(signature) = self.get_hardware_signature() { + let signature = signature.as_bytes(); + facs_data[8] = signature[0]; + facs_data[9] = signature[1]; + facs_data[10] = signature[2]; + facs_data[11] = signature[3]; + } + let mut locked_acpi_data = acpi_data.lock().unwrap(); let facs_begin = locked_acpi_data.len() as u32; locked_acpi_data.extend(facs_data); diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index be3067c6f..483863c74 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -911,4 +911,9 @@ impl AcpiBuilder for StdMachine { .with_context(|| "Fail to add SRAT table to loader")?; Ok(srat_begin) } + + fn get_hardware_signature(&self) -> Option { + let vm_config = self.machine_base().vm_config.lock().unwrap(); + vm_config.hardware_signature + } } diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index fc7304b9c..f29804e60 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -460,6 +460,15 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_values(true) .required(false), ) + .arg( + Arg::with_name("hardware-signature") + .multiple(false) + .long("hardware-signature") + .value_name("<32bit integer>") + .help("set ACPI Hardware Signature") + .takes_value(true) + .required(false), + ) .arg( Arg::with_name("smbios") .multiple(true) @@ -577,6 +586,11 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { add_kernel_cmdline, vec ); + add_args_to_config!( + (args.value_of("hardware-signature")), + vm_cfg, + add_hw_signature + ); add_args_to_config_multi!((args.values_of("drive")), vm_cfg, add_drive); add_args_to_config_multi!((args.values_of("object")), vm_cfg, add_object); add_args_to_config_multi!((args.values_of("netdev")), vm_cfg, add_netdev); diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 2f9cf70e3..25d3b7c12 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -459,6 +459,11 @@ impl VmConfig { self.machine_config.battery = true; true } + + pub fn add_hw_signature(&mut self, config: &str) -> Result<()> { + self.hardware_signature = Some(u32::from_str(config)?); + Ok(()) + } } impl VmConfig { diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index e172f7d58..2aff98011 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -135,6 +135,7 @@ pub struct VmConfig { pub global_config: HashMap, pub numa_nodes: Vec<(String, String)>, pub incoming: Option, + pub hardware_signature: Option, #[cfg(feature = "vnc")] pub vnc: Option, #[cfg(any(feature = "gtk", all(target_env = "ohos", feature = "ohui_srv")))] -- Gitee From 9439f94edec43a67a480898e41ac65fd4a2dcdbe Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jul 2024 15:13:30 +0800 Subject: [PATCH 1966/2187] vhost-vsock: add class-id for vhost-vsock Add class-id for virtio socket type. Signed-off-by: liuxiangdong --- virtio/src/transport/virtio_pci.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 8a058d8ea..ca392dde0 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -31,7 +31,7 @@ use crate::{ CONFIG_STATUS_FEATURES_OK, CONFIG_STATUS_NEEDS_RESET, INVALID_VECTOR_NUM, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING, VIRTIO_F_RING_PACKED, VIRTIO_F_VERSION_1, VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_BLOCK, VIRTIO_TYPE_CONSOLE, - VIRTIO_TYPE_FS, VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, + VIRTIO_TYPE_FS, VIRTIO_TYPE_GPU, VIRTIO_TYPE_NET, VIRTIO_TYPE_SCSI, VIRTIO_TYPE_VSOCK, }; #[cfg(feature = "virtio_gpu")] use address_space::HostMemMapping; @@ -170,7 +170,7 @@ fn get_virtio_class_id(device_type: u32, _device_quirk: Option VIRTIO_PCI_CLASS_ID_BLOCK, VIRTIO_TYPE_FS => VIRTIO_PCI_CLASS_ID_STORAGE_OTHER, VIRTIO_TYPE_NET => VIRTIO_PCI_CLASS_ID_NET, - VIRTIO_TYPE_CONSOLE => VIRTIO_PCI_CLASS_ID_COMMUNICATION_OTHER, + VIRTIO_TYPE_CONSOLE | VIRTIO_TYPE_VSOCK => VIRTIO_PCI_CLASS_ID_COMMUNICATION_OTHER, #[cfg(target_arch = "x86_64")] VIRTIO_TYPE_GPU => VIRTIO_PCI_CLASS_ID_DISPLAY_VGA, #[cfg(target_arch = "aarch64")] -- Gitee From 596f7648c335e9fdd154040b258b49a9bc423b80 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jul 2024 16:31:11 +0800 Subject: [PATCH 1967/2187] mst: shield aio related tests on HarmonyOS HarmonyOS does not support libaio/io_uring now. Shield these tests. Signed-off-by: liuxiangdong --- tests/mod_test/tests/block_test.rs | 12 ++++++++++-- tests/mod_test/tests/scsi_test.rs | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index e7a633aa4..dc2395be8 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -708,6 +708,11 @@ fn blk_all_features() { let device_args = Rc::new(String::from( ",multifunction=on,serial=111111,num-queues=4,bootindex=1,iothread=iothread1", )); + #[cfg(target_env = "ohos")] + let drive_args = Rc::new(String::from( + ",direct=false,readonly=off,throttling.iops-total=1024", + )); + #[cfg(not(target_env = "ohos"))] let drive_args = if aio_probe(AioEngine::IoUring).is_ok() { Rc::new(String::from( ",direct=on,aio=io_uring,readonly=off,throttling.iops-total=1024", @@ -1038,16 +1043,18 @@ fn blk_iops() { /// 1/2/3: success. #[test] fn blk_with_different_aio() { - const BLOCK_DRIVER_CFG: [(ImageType, &str, AioEngine); 6] = [ + let block_driver_cfg: Vec<(ImageType, &str, AioEngine)> = vec![ (ImageType::Raw, "off", AioEngine::Off), (ImageType::Qcow2, "off", AioEngine::Off), (ImageType::Raw, "off", AioEngine::Threads), (ImageType::Qcow2, "off", AioEngine::Threads), + #[cfg(not(target_env = "ohos"))] (ImageType::Raw, "on", AioEngine::Native), + #[cfg(not(target_env = "ohos"))] (ImageType::Raw, "on", AioEngine::IoUring), ]; - for (image_type, direct, aio_engine) in BLOCK_DRIVER_CFG { + for (image_type, direct, aio_engine) in block_driver_cfg { println!("Image type: {:?}", image_type); let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1, &image_type)); let device_args = Rc::new(String::from("")); @@ -1106,6 +1113,7 @@ fn blk_with_different_aio() { /// 3. Destroy device. /// Expect: /// 1/2/3: success. +#[cfg(not(target_env = "ohos"))] #[test] fn blk_aio_io_uring() { for image_type in ImageType::IMAGE_TYPE { diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index aecb4b808..61fb68054 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -27,6 +27,7 @@ use mod_test::libdriver::virtio::{ use mod_test::libdriver::virtio_pci_modern::TestVirtioPciDev; use mod_test::libtest::{test_init, TestState, MACHINE_TYPE_ARG}; use mod_test::utils::{cleanup_img, create_img, ImageType, TEST_IMAGE_SIZE}; +#[cfg(not(target_env = "ohos"))] use util::aio::{aio_probe, AioEngine}; use util::byte_code::ByteCode; use util::offset_of; @@ -505,6 +506,7 @@ impl std::fmt::Display for ScsiDeviceType { } } +#[allow(dead_code)] #[derive(Clone, Debug, Copy)] enum TestAioType { AioOff = 0, @@ -1865,6 +1867,7 @@ fn aio_model_test() { let mut lun = 0x2; let mut device_vec: Vec = Vec::new(); + #[cfg(not(target_env = "ohos"))] if aio_probe(AioEngine::IoUring).is_ok() { // Scsi Disk 1. AIO io_uring. Direct false. let image_path = Rc::new(create_img(TEST_IMAGE_SIZE, 0, &ImageType::Raw)); @@ -1916,6 +1919,7 @@ fn aio_model_test() { // Scsi Disk 5. AIO native. Direct false. This is not allowed. // Stratovirt will report "native aio type should be used with direct on" + #[cfg(not(target_env = "ohos"))] if aio_probe(AioEngine::Native).is_ok() { // Scsi Disk 6. AIO native. Direct true. lun += 1; -- Gitee From fbb05a0105d72b6e23c07d5ce8d2bc18d6e5c679 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jul 2024 17:06:25 +0800 Subject: [PATCH 1968/2187] mst: fix some warnings Fix some warnings in mst. Signed-off-by: liuxiangdong --- tests/mod_test/src/libtest.rs | 2 +- tests/mod_test/tests/balloon_test.rs | 2 +- tests/mod_test/tests/memory_test.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index aa19403ba..3aa957231 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -57,7 +57,7 @@ impl StreamHandler { fn clear_stream(&self) { let mut stream = self.stream.try_clone().unwrap(); stream.set_nonblocking(true).unwrap(); - stream.read(&mut [0_u8; 1024]); + let _ = stream.read(&mut [0_u8; 1024]); } fn read_line(&self, timeout: Duration) -> String { diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 9fc23cc4c..b67453e30 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -13,7 +13,7 @@ use std::cell::RefCell; use std::fs::{remove_file, File}; use std::io::{self, BufRead, BufReader}; -use std::process::{exit, Command}; +use std::process::Command; use std::rc::Rc; use std::{thread, time}; diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 143dbc74e..ed7dca586 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -12,7 +12,7 @@ use std::cell::RefCell; use std::fs::{remove_file, File}; -use std::process::{exit, Command}; +use std::process::Command; use std::rc::Rc; use std::string::String; -- Gitee From 1df4f9c2fa242274b64c8b7cf98d32b38b71adb6 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 19 Jul 2024 19:14:12 +0800 Subject: [PATCH 1969/2187] mst: delete redundant `add_msix_msg` function Delete redundant `add_msix_msg` function. Signed-off-by: liuxiangdong --- hypervisor/src/test/mod.rs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/hypervisor/src/test/mod.rs b/hypervisor/src/test/mod.rs index 8e4697ae1..7120bb9e6 100644 --- a/hypervisor/src/test/mod.rs +++ b/hypervisor/src/test/mod.rs @@ -41,7 +41,7 @@ use devices::{pci::MsiVector, IrqManager, LineIrqManager, MsiIrqManager, Trigger use devices::{GICVersion, GICv3, ICGICConfig, InterruptController, GIC_IRQ_INTERNAL}; use machine_manager::machine::HypervisorType; use migration::{MigrateMemSlot, MigrateOps}; -use util::test_helper::{IntxInfo, MsixMsg, TEST_INTX_LIST, TEST_MSIX_LIST}; +use util::test_helper::{add_msix_msg, IntxInfo, TEST_INTX_LIST}; pub struct TestHypervisor {} @@ -301,19 +301,6 @@ impl TestInterruptManager { pub fn arch_map_irq(&self, gsi: u32) -> u32 { gsi + GIC_IRQ_INTERNAL } - - pub fn add_msix_msg(addr: u64, data: u32) { - let new_msg = MsixMsg::new(addr, data); - let mut msix_list_lock = TEST_MSIX_LIST.lock().unwrap(); - - for msg in msix_list_lock.iter() { - if new_msg.addr == msg.addr && new_msg.data == msg.data { - return; - } - } - - msix_list_lock.push(new_msg); - } } impl LineIrqManager for TestInterruptManager { @@ -405,7 +392,7 @@ impl MsiIrqManager for TestInterruptManager { let data = vector.msg_data; let mut addr: u64 = vector.msg_addr_hi as u64; addr = (addr << 32) + vector.msg_addr_lo as u64; - TestInterruptManager::add_msix_msg(addr, data); + add_msix_msg(addr, data); Ok(()) } -- Gitee From 4948d2c3651959d27445eae35fdc74a1f92a2056 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 22 Jul 2024 09:33:03 +0800 Subject: [PATCH 1970/2187] ohui: remove unused code Remove unused code. Signed-off-by: Zhao Yi Min --- ui/src/ohui_srv/msg_handle.rs | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 2ce24ca65..84b788570 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -57,7 +57,6 @@ fn trans_mouse_pos(x: f64, y: f64, w: f64, h: f64) -> (u32, u32) { struct WindowState { width: u32, height: u32, - led_state: Option, } impl WindowState { @@ -96,19 +95,10 @@ impl WindowState { input_point_sync() } - fn update_host_ledstate(&mut self, led: u8) { - self.led_state = Some(led); - } - - fn sync_kbd_led_state(&mut self) -> Result<()> { - if self.led_state.is_none() { - return Ok(()); - } - - let host_stat = self.led_state.unwrap(); + fn sync_kbd_led_state(&mut self, led: u8) -> Result<()> { let guest_stat = get_kbd_led_state(); - if host_stat != guest_stat { - let sync_bits = host_stat ^ guest_stat; + if led != guest_stat { + let sync_bits = led ^ guest_stat; if (sync_bits & CAPS_LOCK_LED) != 0 { trigger_key(KEYCODE_CAPS_LOCK)?; } @@ -119,7 +109,6 @@ impl WindowState { trigger_key(KEYCODE_SCR_LOCK)?; } } - self.led_state = None; Ok(()) } } @@ -260,8 +249,7 @@ impl OhUiMsgHandler { self.state .lock() .unwrap() - .update_host_ledstate(ke.led_state); - self.state.lock().unwrap().sync_kbd_led_state()?; + .sync_kbd_led_state(ke.led_state)?; let hmkey = ke.keycode; let keycode = match self.hmcode2svcode.get(&hmkey) { Some(k) => *k, @@ -306,14 +294,6 @@ impl OhUiMsgHandler { trace::oh_event_windowinfo(wi.width, wi.height); } - fn handle_ledstate(&self, led: &LedstateEvent) { - self.state - .lock() - .unwrap() - .update_host_ledstate(led.state as u8); - trace::oh_event_ledstate(led.state); - } - pub fn send_windowinfo(&self, w: u32, h: u32) { self.state.lock().unwrap().update_window_info(w, h); if let Some(writer) = self.writer.lock().unwrap().as_mut() { -- Gitee From 0c5afe2976411f7896d91c6324160b99853a6ce9 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 18 Jul 2024 15:59:16 +0800 Subject: [PATCH 1971/2187] hisysevent: Add hisysevent module Add hisysevent module to to interconnect with the hisysevent Service of openHarmonyOS. Signed-off-by: Jinhao Gao Please enter the commit message for your changes. Lines starting --- Cargo.lock | 11 +++ Cargo.toml | 2 + hisysevent/Cargo.toml | 16 +++ hisysevent/build.rs | 18 ++++ hisysevent/src/interface.rs | 189 ++++++++++++++++++++++++++++++++++++ hisysevent/src/lib.rs | 27 ++++++ 6 files changed, 263 insertions(+) create mode 100644 hisysevent/Cargo.toml create mode 100644 hisysevent/build.rs create mode 100644 hisysevent/src/interface.rs create mode 100644 hisysevent/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 90552e41b..dd39a60c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -757,6 +757,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hisysevent" +version = "2.4.0" +dependencies = [ + "anyhow", + "lazy_static", + "libloading", + "log", +] + [[package]] name = "hypervisor" version = "2.4.0" @@ -1584,6 +1594,7 @@ name = "stratovirt" version = "2.4.0" dependencies = [ "anyhow", + "hisysevent", "log", "machine", "machine_manager", diff --git a/Cargo.toml b/Cargo.toml index b00a2da08..3277768c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ machine = { path = "machine" } machine_manager = { path = "machine_manager" } util = { path = "util" } trace = { path = "trace" } +hisysevent = { path = "hisysevent" } [workspace] members = [ @@ -42,6 +43,7 @@ virtio_gpu = ["machine/virtio_gpu"] trace_to_logger = ["trace/trace_to_logger"] trace_to_ftrace = ["trace/trace_to_ftrace"] trace_to_hitrace = ["trace/trace_to_hitrace"] +hisysevent = ["hisysevent/hisysevent"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/hisysevent/Cargo.toml b/hisysevent/Cargo.toml new file mode 100644 index 000000000..b7febaa74 --- /dev/null +++ b/hisysevent/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "hisysevent" +version = "2.4.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "Provide hisysevent infrastructure of hmos for StratoVirt" + +[dependencies] +log = "0.4" +lazy_static = "1.4.0" +anyhow = "1.0" +libloading = "0.7.4" + +[features] +hisysevent = [] diff --git a/hisysevent/build.rs b/hisysevent/build.rs new file mode 100644 index 000000000..89563d852 --- /dev/null +++ b/hisysevent/build.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +fn main() { + println!( + "cargo:rerun-if-changed={}/hisysevent", + std::env::var("CARGO_MANIFEST_DIR").unwrap() + ); +} diff --git a/hisysevent/src/interface.rs b/hisysevent/src/interface.rs new file mode 100644 index 000000000..5f6ecf64b --- /dev/null +++ b/hisysevent/src/interface.rs @@ -0,0 +1,189 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::ffi::{c_char, c_int, c_uint, c_ulonglong, CString, OsStr}; + +use anyhow::{Context, Result}; +use lazy_static::lazy_static; +use libloading::os::unix::Symbol; +use libloading::Library; +use log::error; + +const MAX_PARAM_NAME_LENGTH: usize = 49; + +#[derive(Copy, Clone, Debug)] +pub enum HiSysEventType { + _Fault = 1, + _Statistic, + _Security, + _Behavior, +} + +#[derive(Copy, Clone, Debug)] +pub enum EventParamType { + // Invalid type. + _Invalid = 0, + _TypeBool, + _TypeI8, + _TypeU8, + _TypeI16, + _TypeU16, + _TypeI32, + _TypeU32, + _TypeI64, + _TypeU64, + _TypeF32, + _TypeF64, + _TypeString, + _ArrayBool, + _ArrayI8, + _ArrayU8, + _ArrayI16, + _ArrayU16, + _ArrayI32, + _ArrayU32, + _ArrayI64, + _ArrayU64, + _ArrayF32, + _ArrayF64, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union EventParamValue { + pub bool_value: bool, + pub i8_value: i8, + pub u8_value: u8, + pub i16_value: i16, + pub u16_value: u16, + pub i32_value: i32, + pub u32_value: u32, + pub i64_value: i64, + pub u64_value: u64, + pub f32_value: f32, + pub f64_value: f64, + // String. + pub char_ptr_value: *const c_char, + // Array. + pub void_ptr_value: *const (), +} + +pub struct EventParam<'a> { + pub param_name: &'a str, + pub param_type: EventParamType, + pub param_value: EventParamValue, + pub array_size: usize, +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct EventParamWrapper { + pub param_name: [u8; MAX_PARAM_NAME_LENGTH], + pub param_type: c_int, + pub param_value: EventParamValue, + pub array_size: c_uint, +} + +lazy_static! { + static ref HISYSEVENT_FUNC_TABLE: HiSysEventFuncTable = + // SAFETY: The dynamic library should be always existing. + unsafe { + HiSysEventFuncTable::new(OsStr::new("libhisysevent.z.so")) + .map_err(|e| { + error!("failed to init HiSysEventFuncTable with error: {:?}", e); + e + }) + .unwrap() + }; +} + +macro_rules! get_libfn { + ( $lib: ident, $tname: ident, $fname: ident ) => { + $lib.get::<$tname>(stringify!($fname).as_bytes()) + .with_context(|| format!("failed to get function {}", stringify!($fname)))? + .into_raw() + }; +} + +type HiSysEventWriteWrapperFn = unsafe extern "C" fn( + func: *const c_char, + line: c_ulonglong, + domain: *const c_char, + name: *const c_char, + event_type: c_int, + params: *const EventParamWrapper, + size: c_uint, +) -> c_int; + +struct HiSysEventFuncTable { + pub hisysevent_write: Symbol, +} + +impl HiSysEventFuncTable { + unsafe fn new(library_name: &OsStr) -> Result { + let library = + Library::new(library_name).with_context(|| "failed to load hisysevent library")?; + + Ok(Self { + hisysevent_write: get_libfn!(library, HiSysEventWriteWrapperFn, HiSysEvent_Write), + }) + } +} + +fn format_param_array(event_params: &[EventParam]) -> Vec { + let mut params_wrapper: Vec = vec![]; + + for param in event_params { + let mut param_name = [0_u8; MAX_PARAM_NAME_LENGTH]; + let name = param.param_name.as_bytes(); + let end = std::cmp::min(name.len(), param_name.len()); + param_name[..end].copy_from_slice(&name[..end]); + params_wrapper.push(EventParamWrapper { + param_name, + param_type: param.param_type as i32, + param_value: param.param_value, + array_size: param.array_size as u32, + }); + } + + params_wrapper +} + +// Write system event. +pub(crate) fn write_to_hisysevent( + func_name: &str, + event_name: &str, + event_type: c_int, + event_params: &[EventParam], +) { + let func = CString::new(func_name).unwrap(); + let domain = CString::new("VM_ENGINE").unwrap(); + let event = CString::new(event_name).unwrap(); + + let params_wrapper = format_param_array(event_params); + + // SAFETY: Call hisysevent function, all parameters are just read. + let ret = unsafe { + (HISYSEVENT_FUNC_TABLE.hisysevent_write)( + func.as_ptr() as *const c_char, + line!() as c_ulonglong, + domain.as_ptr() as *const c_char, + event.as_ptr() as *const c_char, + event_type, + params_wrapper.as_ptr() as *const EventParamWrapper, + params_wrapper.len() as u32, + ) + }; + if ret != 0 { + error!("Failed to write event {} to hisysevent.", event_name); + } +} diff --git a/hisysevent/src/lib.rs b/hisysevent/src/lib.rs new file mode 100644 index 000000000..3166fcafb --- /dev/null +++ b/hisysevent/src/lib.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(all(target_env = "ohos", feature = "hisysevent"))] +mod interface; + +#[macro_export] +macro_rules! function { + () => {{ + fn hook() {} + fn type_name_of(_: T) -> &'static str { + std::any::type_name::() + } + let name = type_name_of(hook); + let off_set: usize = 6; // ::hook + &name[..name.len() - off_set] + }}; +} -- Gitee From cc83891b8302fd1447ea9f76f69517502abad6ef Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 18 Jul 2024 16:35:09 +0800 Subject: [PATCH 1972/2187] hisysevent: Add event code generator macro Add code generator macro to parse event configuration file for generator hisysevent writing function. Signed-off-by: Jinhao Gao --- Cargo.lock | 13 ++ hisysevent/Cargo.toml | 1 + hisysevent/code_generator/Cargo.toml | 18 +++ hisysevent/code_generator/src/lib.rs | 202 +++++++++++++++++++++++++++ hisysevent/event_info/example.toml | 5 + hisysevent/src/lib.rs | 7 + 6 files changed, 246 insertions(+) create mode 100644 hisysevent/code_generator/Cargo.toml create mode 100644 hisysevent/code_generator/src/lib.rs create mode 100644 hisysevent/event_info/example.toml diff --git a/Cargo.lock b/Cargo.lock index dd39a60c8..20ed7089a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,6 +314,18 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "code_generator" +version = "2.4.0" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "serde", + "syn 2.0.18", + "toml", +] + [[package]] name = "const_format" version = "0.2.31" @@ -762,6 +774,7 @@ name = "hisysevent" version = "2.4.0" dependencies = [ "anyhow", + "code_generator", "lazy_static", "libloading", "log", diff --git a/hisysevent/Cargo.toml b/hisysevent/Cargo.toml index b7febaa74..11b3b69a0 100644 --- a/hisysevent/Cargo.toml +++ b/hisysevent/Cargo.toml @@ -11,6 +11,7 @@ log = "0.4" lazy_static = "1.4.0" anyhow = "1.0" libloading = "0.7.4" +code_generator = { path = "code_generator" } [features] hisysevent = [] diff --git a/hisysevent/code_generator/Cargo.toml b/hisysevent/code_generator/Cargo.toml new file mode 100644 index 000000000..9c1fc3c17 --- /dev/null +++ b/hisysevent/code_generator/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "code_generator" +version = "2.4.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" + +[lib] +name = "code_generator" +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +regex = "1" +serde = { version = "1.0", features = ["derive"] } +syn = "2.0.18" +toml = "0.7" diff --git a/hisysevent/code_generator/src/lib.rs b/hisysevent/code_generator/src/lib.rs new file mode 100644 index 000000000..8d7c8d068 --- /dev/null +++ b/hisysevent/code_generator/src/lib.rs @@ -0,0 +1,202 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{fs, io::Read}; + +use proc_macro::TokenStream; +use quote::quote; +use regex::Regex; +use serde::Deserialize; +use syn::{parse_str, Expr, Ident, Type}; + +const EVENT_DIR_NAME: &str = "event_info"; + +#[derive(Debug, Deserialize)] +struct EventDesc { + name: String, + event_type: String, + args: String, + enabled: bool, +} + +#[derive(Debug, Deserialize)] +struct HiSysEventConf { + events: Option>, +} + +fn get_event_desc() -> HiSysEventConf { + let event_dir_path = format!( + "{}/{}", + std::env::var("CARGO_MANIFEST_DIR").unwrap(), + EVENT_DIR_NAME + ); + let paths = fs::read_dir(event_dir_path).unwrap(); + let mut desc = String::new(); + + for path in paths { + let file_path = path.unwrap().path(); + let file_name = file_path.to_str().unwrap(); + if file_name.ends_with(".toml") { + let mut file = fs::File::open(file_path).unwrap(); + file.read_to_string(&mut desc).unwrap(); + } + } + match toml::from_str::(&desc) { + Ok(ret) => ret, + Err(e) => panic!("Failed to parse event info : {}", e), + } +} + +fn is_slice(arg_type: &str) -> bool { + let regex = Regex::new(r"\[([^\[\]]*)\]").unwrap(); + let match_texts = regex + .captures_iter(arg_type) + .map(|mat| mat.get(1).map_or("", |m| m.as_str())); + match match_texts.count() { + 0 => false, + 1 => true, + _ => panic!("The format of parameter type: {} is wrong!", arg_type), + } +} + +fn capitalize(s: &str) -> String { + if s.is_empty() { + return String::new(); + } + + let mut chars = s.chars().collect::>(); + if chars[0].is_alphabetic() { + chars[0] = chars[0] + .to_uppercase() + .collect::() + .chars() + .next() + .unwrap(); + } + chars.iter().collect() +} + +fn parse_param_type(arg_type: &str) -> String { + if is_slice(arg_type) { + let regex = Regex::new(r"\[([^\[\]]*)\]").unwrap(); + let match_texts: Vec<&str> = regex + .captures_iter(arg_type) + .map(|mat| mat.get(1).map_or("", |m| m.as_str())) + .collect(); + format!("Array{}", capitalize(match_texts[0])) + } else { + format!("Type{}", capitalize(arg_type)) + } +} + +fn generate_param_value(arg_type: &str, arg_value: &str) -> (Ident, Expr) { + let param_type: Ident; + let param_value: Expr; + if is_slice(arg_type) { + let trans_token = ".as_ptr() as *const std::ffi::c_int as *const ()"; + param_type = parse_str::("void_ptr_value").unwrap(); + param_value = parse_str::(format!("{}{}", arg_value, trans_token).as_str()).unwrap(); + } else if arg_type.contains("String") { + let cstr_arg = format!("std::ffi::CString::new({}).unwrap()", arg_value); + let trans_token = ".into_raw() as *const std::ffi::c_char"; + param_type = parse_str::("char_ptr_value").unwrap(); + param_value = parse_str::(format!("{}{}", cstr_arg, trans_token).as_str()).unwrap(); + } else { + param_type = parse_str::(format!("{}_value", arg_type).as_str()).unwrap(); + param_value = parse_str::(format!("{} as {}", arg_value, arg_type).as_str()).unwrap(); + } + (param_type, param_value) +} + +#[proc_macro] +pub fn gen_hisysevent_func(_input: TokenStream) -> TokenStream { + let events = match get_event_desc().events { + Some(events) => events, + None => return TokenStream::from(quote!()), + }; + let hisysevent_func = events.iter().map(|desc| { + if desc.name.trim().is_empty() { + panic!("Empty event name is unsupported!"); + } + let desc_name = desc.name.trim(); + let func_name = parse_str::(desc_name).unwrap(); + let event_name = desc_name; + let event_type = + parse_str::(format!("HiSysEventType::_{}", desc.event_type.trim()).as_str()) + .unwrap(); + + let func_args = match desc.args.is_empty() { + true => quote!(), + false => { + let split_args: Vec<&str> = desc.args.split(',').collect(); + let _args = split_args.iter().map(|arg| { + let (v, t) = arg.split_once(':').unwrap(); + let arg_name = parse_str::(v.trim()).unwrap(); + let arg_type = parse_str::(t.trim()).unwrap(); + quote!( + #arg_name: #arg_type, + ) + }); + quote! { #( #_args )* } + } + }; + + let param_body = { + let split_args: Vec<&str> = desc.args.split(',').collect(); + let _args = split_args.iter().map(|arg| { + let (v, t) = arg.split_once(':').unwrap(); + let param_name = v.trim(); + let param_type_str: String = parse_param_type(t.trim()); + let param_type_token = format!("EventParamType::_{}", param_type_str); + let param_type = parse_str::(param_type_token.as_str()).unwrap(); + let (elem_type, elem_value) = generate_param_value(t.trim(), v.trim()); + let param_size = if param_type_str.contains("Array") { + parse_str::(format!("{}.len()", v.trim()).as_str()).unwrap() + } else { + parse_str::("0").unwrap() + }; + + quote!( + EventParam { + param_name: #param_name, + param_type: #param_type, + param_value: EventParamValue{#elem_type: #elem_value}, + array_size: #param_size}, + ) + }); + quote! { #( #_args )* } + }; + + let func_body = match desc.enabled { + true => { + quote!( + #[cfg(all(target_env = "ohos", feature = "hisysevent"))] + { + let func = function!(); + let params: &[EventParam] = &[#param_body]; + write_to_hisysevent(func, #event_name, #event_type as std::ffi::c_int, params); + } + ) + } + false => quote!(), + }; + + quote!( + #[inline(always)] + pub fn #func_name(#func_args) { + #func_body + } + ) + }); + + TokenStream::from(quote! { #( #hisysevent_func )* }) +} diff --git a/hisysevent/event_info/example.toml b/hisysevent/event_info/example.toml new file mode 100644 index 000000000..6a5e97ad6 --- /dev/null +++ b/hisysevent/event_info/example.toml @@ -0,0 +1,5 @@ +[[events]] +name = "example" +event_type = "Behavior" +args = "example_bool: bool, example_str: String, example_integer: u32, example_array: &[u8]" +enabled = true diff --git a/hisysevent/src/lib.rs b/hisysevent/src/lib.rs index 3166fcafb..6ab5515a2 100644 --- a/hisysevent/src/lib.rs +++ b/hisysevent/src/lib.rs @@ -13,6 +13,11 @@ #[cfg(all(target_env = "ohos", feature = "hisysevent"))] mod interface; +use code_generator::gen_hisysevent_func; + +#[cfg(all(target_env = "ohos", feature = "hisysevent"))] +use crate::interface::*; + #[macro_export] macro_rules! function { () => {{ @@ -25,3 +30,5 @@ macro_rules! function { &name[..name.len() - off_set] }}; } + +gen_hisysevent_func! {} -- Gitee From eb55b6e7f35e506c9f2255194530638a2c5f7914 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Fri, 19 Jul 2024 09:48:50 +0800 Subject: [PATCH 1973/2187] Docs: Add hisysevent markdown file Add hisysevent markdown file to introduce the way to use hisysevent. Signed-off-by: Jinhao Gao --- docs/hisysevent.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/hisysevent.md diff --git a/docs/hisysevent.md b/docs/hisysevent.md new file mode 100644 index 000000000..b92ce4fe4 --- /dev/null +++ b/docs/hisysevent.md @@ -0,0 +1,40 @@ +# HiSysEvent + +HiSysEvent(https://gitee.com/openharmony/hiviewdfx_hisysevent) is a tool in open- +harmonyOS to recode important information of key processes during system running, +helping locate faults and do some data analytics. + +This document describes the way to how to use hisysevent in StratoVirt. + +## Add Event + +### Modify configuration file + +First, you need to modify or creat toml file in the event/event_info directory +to add a new event in order to generate the event function. For example: + +```toml +[[events]] +name = "example" +event_type = "Behavior" +args = "example_bool: bool, example_str: String, example_integer: u64, example_array: &[u8]" +enable = true +``` + +In the above configuration, "name" is used to represent the only event, and +duplication is not allowed; "event_type" is one of four event type defined +by openharmonyOS: Fault, Statistic, Security and Behavior; "args" will be +formatted as arguments passed to hisysevent service in open-harmonyOS; +"enabled" indicates whether it is enabled during compilation. + +### Call event function + +Just call the event function where needed. +```rust +fn init_machine_ram(&self, sys_mem: &Arc, mem_size: u64) -> Result<()> { + hisysevent::example("true", "init_ram".to_string(), mem_size, &[0,1]); + let vm_ram = self.get_vm_ram(); + let layout_size = MEM_LAYOUT[LayoutEntryType::Mem as usize].1; + ...... +} +``` -- Gitee From 875545622d7eca8e52ee9a9d3720b1b94f8bdfbd Mon Sep 17 00:00:00 2001 From: yexiao Date: Thu, 25 Jul 2024 18:27:32 +0800 Subject: [PATCH 1974/2187] mod_test: modify module test Clear the buffer of the stream before sending qmp command. Signed-off-by: Xiao Ye --- tests/mod_test/src/libtest.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index ada68e0a6..aa19403ba 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -54,6 +54,12 @@ impl StreamHandler { .unwrap(); } + fn clear_stream(&self) { + let mut stream = self.stream.try_clone().unwrap(); + stream.set_nonblocking(true).unwrap(); + stream.read(&mut [0_u8; 1024]); + } + fn read_line(&self, timeout: Duration) -> String { let start = Instant::now(); let mut resp = self.read_buffer.borrow_mut(); @@ -137,6 +143,7 @@ impl TestState { pub fn qmp(&self, cmd: &str) -> Value { let timeout = Duration::from_secs(10); + self.qmp_sock.clear_stream(); self.qmp_sock.write_line(cmd); serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap() } -- Gitee From 0cfe9f6dddb7f5b02602e7639073d3fc43cc8e7a Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 2 Sep 2024 17:40:54 +0800 Subject: [PATCH 1975/2187] ohui: fix integer calculation issues Use suitable calculation methods. Signed-off-by: zhanghan64 --- ui/src/ohui_srv/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 45a51a8e5..92e22ea82 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -14,7 +14,6 @@ pub mod channel; pub mod msg; pub mod msg_handle; -use std::mem::size_of; use std::os::unix::io::RawFd; use std::path::Path; use std::ptr; @@ -24,7 +23,7 @@ use std::sync::{ Arc, Mutex, RwLock, }; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, info}; use once_cell::sync::OnceCell; use vmm_sys_util::epoll::EventSet; @@ -384,7 +383,12 @@ impl DisplayChangeListenerOperations for OhUiServer { return Ok(()); } - let len = cursor.width * cursor.height * size_of::() as u32; + let len = cursor + .width + .checked_mul(cursor.height) + .with_context(|| "Invalid cursor width * height")? + .checked_mul(bytes_per_pixel() as u32) + .with_context(|| "Invalid cursor size")?; if len > CURSOR_SIZE as u32 || len > cursor.data.len().try_into()? { error!("Too large cursor length {}.", len); // No need to return Err for this situation is not fatal @@ -405,7 +409,7 @@ impl DisplayChangeListenerOperations for OhUiServer { cursor.height, cursor.hot_x, cursor.hot_y, - size_of::() as u32, + bytes_per_pixel() as u32, ); Ok(()) } -- Gitee From 6c8a2091e33df36a264c68cdfcf4b930eb5860a1 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 2 Sep 2024 17:44:27 +0800 Subject: [PATCH 1976/2187] ohaudio: fix integer calculation issues Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 45 +++++++++++++++++------------- devices/src/misc/scream/ohaudio.rs | 4 +++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 806f00bf0..5272553ae 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -155,11 +155,12 @@ impl ShmemStreamHeader { if self.chunk_idx > self.max_chunks { error!( - "The chunk index of stream {} exceeds the maximum number of chunks {}", - self.chunk_idx, self.max_chunks + "Invalid max_chunks: {} or chunk_idx: {}", + self.max_chunks, self.chunk_idx ); return false; } + if self.fmt.channels == 0 || self.fmt.channel_map == 0 { error!( "The fmt channels {} or channel_map {} is invalid", @@ -274,7 +275,12 @@ impl ScreamCond { #[derive(Debug, Default)] pub struct StreamData { pub fmt: ShmemStreamFmt, + max_chunks: u16, chunk_idx: u16, + /// Start address of header implies. + start_addr: u64, + /// Length of total data which header implies. + data_shm_len: u64, /// Size of the data to be played or recorded. pub audio_size: u32, /// Location of the played or recorded audio data in the shared memory. @@ -284,10 +290,14 @@ pub struct StreamData { } impl StreamData { - fn init(&mut self, header: &ShmemStreamHeader) { + fn init(&mut self, header: &ShmemStreamHeader, hva: u64) { fence(Ordering::Acquire); self.fmt = header.fmt; self.chunk_idx = header.chunk_idx; + self.max_chunks = header.max_chunks; + self.data_shm_len = u64::from(header.chunk_size) * u64::from(self.max_chunks); + self.start_addr = hva + u64::from(header.offset); + self.audio_size = header.chunk_size; } fn register_pause_notifier(&mut self, cond: Arc) { @@ -328,7 +338,7 @@ impl StreamData { header = // SAFETY: hva is allocated by libc:::mmap, it can be guaranteed to be legal. &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; - self.init(stream_header); + self.init(stream_header, hva); let mut last_end = 0_u64; // The recording buffer is behind the playback buffer. Thereforce, the end position of @@ -355,18 +365,14 @@ impl StreamData { shmem_size: u64, stream_header: &ShmemStreamHeader, ) -> bool { - self.audio_size = stream_header.chunk_size; - self.audio_base = hva - + u64::from(stream_header.offset) - + u64::from(stream_header.chunk_size) * u64::from(self.chunk_idx); - - if (self.audio_base + u64::from(self.audio_size)) > (hva + shmem_size) { + self.audio_base = self + .start_addr + .saturating_add(u64::from(self.audio_size) * u64::from(self.chunk_idx)); + let buf_end = hva + shmem_size; + if self.audio_base.saturating_add(u64::from(self.audio_size)) > buf_end { error!( "Scream: wrong header: offset {} chunk_idx {} chunk_size {} max_chunks {}", - stream_header.offset, - stream_header.chunk_idx, - stream_header.chunk_size, - stream_header.max_chunks, + stream_header.offset, stream_header.chunk_idx, self.audio_size, self.max_chunks, ); return false; } @@ -400,15 +406,15 @@ impl StreamData { // slow and the backward data is skipped. if play .chunk_idx - .wrapping_add(play.max_chunks) + .wrapping_add(self.max_chunks) .wrapping_sub(self.chunk_idx) - % play.max_chunks + % self.max_chunks > 4 { self.chunk_idx = - play.chunk_idx.wrapping_add(play.max_chunks).wrapping_sub(1) % play.max_chunks; + play.chunk_idx.wrapping_add(self.max_chunks).wrapping_sub(1) % self.max_chunks; } else { - self.chunk_idx = (self.chunk_idx + 1) % play.max_chunks; + self.chunk_idx = (self.chunk_idx + 1) % self.max_chunks; } if !self.update_buffer_by_chunk_idx(hva, shmem_size, play) { @@ -429,9 +435,8 @@ impl StreamData { // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let capt = &mut header.capt; - let addr = hva + u64::from(capt.offset); - interface.lock().unwrap().pre_receive(addr, capt); + interface.lock().unwrap().pre_receive(self.start_addr, capt); while capt.is_started != 0 { cond.wait_if_paused(interface.clone()); diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 752e4d292..8040ea887 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -454,6 +454,10 @@ extern "C" fn on_read_data_cb( break; } } + if capture.align == 0 { + error!("on_read_data_cb, capture.align is 0"); + return 0; + } let old_pos = capture.cur_pos - ((capture.cur_pos - capture.shm_addr) % u64::from(capture.align)); let buf_end = capture.shm_addr + capture.shm_len; -- Gitee From 4e4f270cdb41c849f02d212118fdc5efe84e6ba0 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 2 Sep 2024 17:45:05 +0800 Subject: [PATCH 1977/2187] ohcamera: fix integer calculation issues Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 9 ++++----- devices/src/usb/camera.rs | 26 ++++++++++++++++++++++---- util/src/ohos_binding/camera.rs | 3 +++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 3527048cf..01fc0157b 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -260,7 +260,10 @@ impl CameraBackend for OhCameraBackend { fmt_list.push(CameraFormatList { format: cam_fmt_from_oh(fmt)?, frame: vec![frame], - fmt_index: (idx) + 1, + fmt_index: idx.checked_add(1).unwrap_or_else(|| { + error!("list_format: too much profile ID"); + u8::MAX + }), }); } Err(e) => error!("{:?}", e), @@ -354,10 +357,6 @@ impl CameraBackend for OhCameraBackend { bail!("Invalid frame src_len {}", src_len); } - if frame_offset + len > src_len as usize { - bail!("Invalid frame offset {} or len {}", frame_offset, len); - } - trace::trace_scope_start!(ohcam_get_frame, args = (frame_offset, len)); let mut copied = 0_usize; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 159a33ca1..e05f89b91 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -886,7 +886,12 @@ impl UvcPayload { let mut frame_data_size = iov_size; let header_len = self.header.len(); // Within the scope of the frame. - if self.frame_offset + frame_data_size as usize >= current_frame_size { + if self + .frame_offset + .checked_add(frame_data_size as usize) + .with_context(|| "get_frame_data_size: invalid frame data")? + >= current_frame_size + { if self.frame_offset > current_frame_size { bail!( "Invalid frame offset {} {}", @@ -897,7 +902,12 @@ impl UvcPayload { frame_data_size = (current_frame_size - self.frame_offset) as u64; } // Within the scope of the payload. - if self.payload_offset + frame_data_size as usize >= MAX_PAYLOAD as usize { + if self + .payload_offset + .checked_add(frame_data_size as usize) + .with_context(|| "get_frame_data_size: invalid payload data")? + >= MAX_PAYLOAD as usize + { if self.payload_offset > MAX_PAYLOAD as usize { bail!( "Invalid payload offset {} {}", @@ -908,7 +918,12 @@ impl UvcPayload { frame_data_size = u64::from(MAX_PAYLOAD) - self.payload_offset as u64; } // payload start, reserve the header. - if self.payload_offset == 0 && frame_data_size + header_len as u64 > iov_size { + if self.payload_offset == 0 + && frame_data_size + .checked_add(header_len as u64) + .with_context(|| "get_frame_data_size: invalid header_len")? + > iov_size + { if iov_size <= header_len as u64 { bail!("Invalid iov size {}", iov_size); } @@ -1092,7 +1107,10 @@ fn gen_fmt_desc(fmt_list: Vec) -> Result fn gen_intface_header_desc(fmt_num: u8) -> VsDescInputHeader { VsDescInputHeader { - bLength: 0xd + fmt_num, + bLength: 0xd_u8.checked_add(fmt_num).unwrap_or_else(|| { + error!("gen_intface_header_desc: too large fmt num"); + u8::MAX + }), bDescriptorType: CS_INTERFACE, bDescriptorSubtype: VS_INPUT_HEADER, bNumFormats: fmt_num, diff --git a/util/src/ohos_binding/camera.rs b/util/src/ohos_binding/camera.rs index eadeb0d1e..4121fcf32 100644 --- a/util/src/ohos_binding/camera.rs +++ b/util/src/ohos_binding/camera.rs @@ -69,6 +69,9 @@ impl OhCamera { bail!("OH Camera: failed to init profiles"); } } + if fmt_cnt > i32::from(u8::MAX) { + bail!("Invalid format counts: {fmt_cnt}"); + } Ok((Self { ctx, capi }, fmt_cnt)) } -- Gitee From a42889d04fe7b549fabcbfc4c49886c20fe7e7e0 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 29 Jul 2024 15:46:25 +0800 Subject: [PATCH 1978/2187] ohui: add parameter 'ui-path' for cmdline This patch introduces a new parameter 'ui-path' for cmdline to define the directory path of OHUI framebuffer and cursor files. Signed-off-by: Zhao Yi Min --- machine/src/aarch64/standard.rs | 5 ++++- machine_manager/src/config/display.rs | 18 +++++++++++++----- ui/src/ohui_srv/mod.rs | 8 ++++---- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index c82efefc9..c117223b1 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -312,7 +312,10 @@ impl StdMachine { if dpy.display_type != "ohui" { return Ok(()); } - self.ohui_server = Some(Arc::new(OhUiServer::new(dpy.get_ui_path())?)); + self.ohui_server = Some(Arc::new(OhUiServer::new( + dpy.get_ui_path(), + dpy.get_sock_path(), + )?)); } Ok(()) } diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index d07828581..74154f9db 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -39,7 +39,7 @@ pub struct UiContext { } #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] -fn get_sock_path(p: &str) -> Result { +fn get_dir_path(p: &str) -> Result { let path = std::fs::canonicalize(p) .with_context(|| format!("Failed to get real directory path: {:?}", p))?; if !path.exists() { @@ -75,16 +75,24 @@ pub struct DisplayConfig { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] #[arg(long)] pub iothread: Option, - /// Confirm related files' path. Default ui path is "/tmp". + /// Confirm socket path. Default socket path is "/tmp". #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - #[arg(long, alias = "socks-path", default_value = "/tmp/", value_parser = get_sock_path)] - pub path: String, + #[arg(long, alias = "socks-path", default_value = "/tmp/", value_parser = get_dir_path)] + pub sock_path: String, + /// Define the directory path for OHUI framebuffer and cursor. + #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] + #[arg(long, alias = "ui-path", default_value = "/dev/shm/hwf/", value_parser = get_dir_path)] + pub ui_path: String, } #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] impl DisplayConfig { + pub fn get_sock_path(&self) -> String { + self.sock_path.clone() + } + pub fn get_ui_path(&self) -> String { - self.path.clone() + self.ui_path.clone() } } diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index b650946d1..384bf02f1 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -171,10 +171,10 @@ impl OhUiServer { Ok(cursorbuffer) } - pub fn new(path: String) -> Result { - let channel = Self::init_channel(&path)?; - let (fb_file, framebuffer) = Self::init_fb_file(&path)?; - let cursorbuffer = Self::init_cursor_file(&path)?; + pub fn new(ui_path: String, sock_path: String) -> Result { + let channel = Self::init_channel(&sock_path)?; + let (fb_file, framebuffer) = Self::init_fb_file(&ui_path)?; + let cursorbuffer = Self::init_cursor_file(&ui_path)?; Ok(OhUiServer { passthru: OnceCell::new(), -- Gitee From 90dffbccb7aada39efb2f29d9f406ec808465f8c Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 29 Jul 2024 17:15:09 +0800 Subject: [PATCH 1979/2187] pflash: Allow less than 64M size writable pflash There is no 64M size requirement for writable pflash. Signed-off-by: Keqian Zhu --- devices/src/legacy/pflash.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index d3a6c0a42..5db5b9bc2 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -69,11 +69,10 @@ impl PFlash { read_only: bool, ) -> Result { // We don't have to occupy the whole memory region. - // If flash is read-only, expose just real data size, - // rounded up to page_size + // Expose just real data size, rounded up to page_size. if let Some(fd) = backend.as_ref() { let len = fd.as_ref().metadata().unwrap().len(); - if len > region_max_size || len == 0 || (!read_only && len != region_max_size) { + if len > region_max_size || len == 0 || (!read_only && len % host_page_size() != 0) { bail!( "Invalid flash file: Region size 0x{region_max_size:X}, file size 0x{len:X}; read_only {read_only}" ); -- Gitee From 4331d9c6b23e988e5188740f8923da0bb2bacd53 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 29 Jul 2024 19:28:12 +0800 Subject: [PATCH 1980/2187] ohui/ui-path: set different default value for different display The original default value made gtk unit test fail. So let's use a available directory for gtk type although it is not required. Signed-off-by: Zhao Yi Min --- machine_manager/src/config/display.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 74154f9db..743a476cc 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -40,6 +40,10 @@ pub struct UiContext { #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] fn get_dir_path(p: &str) -> Result { + if cfg!(debug_assertions) { + return Ok(p.to_string()); + } + let path = std::fs::canonicalize(p) .with_context(|| format!("Failed to get real directory path: {:?}", p))?; if !path.exists() { @@ -81,7 +85,7 @@ pub struct DisplayConfig { pub sock_path: String, /// Define the directory path for OHUI framebuffer and cursor. #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] - #[arg(long, alias = "ui-path", default_value = "/dev/shm/hwf/", value_parser = get_dir_path)] + #[arg(long, alias = "ui-path", default_value_if("display_type", "ohui", "/dev/shm/hwf/"), default_value = "/tmp/", value_parser = get_dir_path)] pub ui_path: String, } -- Gitee From c6e922583eb51bdbec6d9ef9bbbc331ab4971b57 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Mon, 29 Jul 2024 15:45:11 +0800 Subject: [PATCH 1981/2187] ohaudio: add logs We need to know when to start/stop for audio tasks for issues analysis, so we add two logs. Signed-off-by: zhanghan64 --- devices/src/misc/scream/ohaudio.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index d011c04df..f7ad7117b 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -18,7 +18,7 @@ use std::sync::{ }; use std::{cmp, io::Read, ptr, thread, time::Duration}; -use log::{error, warn}; +use log::{error, info, warn}; use crate::misc::ivshmem::Ivshmem; use crate::misc::scream::{ @@ -237,6 +237,7 @@ impl OhAudioProcess for OhAudioRender { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { + info!("Renderer start"); self.status = AudioStatus::Started; trace::oh_scream_render_init(&self.ctx); } @@ -248,6 +249,7 @@ impl OhAudioProcess for OhAudioRender { } fn destroy(&mut self) { + info!("Renderer destroy"); match self.status { AudioStatus::Error => { self.ctx = None; @@ -384,6 +386,7 @@ impl OhAudioProcess for OhAudioCapture { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { + info!("Capturer start"); self.status = AudioStatus::Started; trace::oh_scream_capture_init(&self.ctx); true @@ -396,6 +399,7 @@ impl OhAudioProcess for OhAudioCapture { } fn destroy(&mut self) { + info!("Capturer destroy"); self.status = AudioStatus::Ready; self.ctx = None; self.stream.reset(); -- Gitee From 260e105ddc43eacfa32d223f0067cbbc6382ab93 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 30 Jul 2024 11:30:00 +0800 Subject: [PATCH 1982/2187] Revert "Machine: the Mutex lock of vm is changed to RwLock" This reverts commit bd9ede4c0b27435c232c70f7a6da9b36bff230b6. Signed-off-by: Keqian Zhu --- cpu/src/lib.rs | 16 ++++----- .../src/interrupt_controller/aarch64/gicv3.rs | 2 +- .../src/interrupt_controller/aarch64/mod.rs | 6 ++-- hypervisor/src/kvm/mod.rs | 14 ++++---- machine/src/aarch64/micro.rs | 6 ++-- machine/src/aarch64/standard.rs | 10 +++--- machine/src/lib.rs | 36 +++++++++---------- machine/src/micro_common/mod.rs | 2 +- machine/src/standard_common/mod.rs | 18 +++++----- machine/src/x86_64/micro.rs | 6 ++-- machine/src/x86_64/standard.rs | 22 ++++++------ machine_manager/src/event_loop.rs | 4 +-- machine_manager/src/machine.rs | 2 +- machine_manager/src/qmp/qmp_socket.rs | 21 ++++++----- machine_manager/src/test_server.rs | 18 +++++----- migration/src/general.rs | 2 +- migration/src/manager.rs | 4 +-- migration/src/migration.rs | 4 +-- src/main.rs | 12 +++---- util/src/loop_context.rs | 12 +++---- 20 files changed, 107 insertions(+), 110 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 3c3f3f0d0..7a1162951 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -63,7 +63,7 @@ pub use x86_64::X86RegsIndex as RegsIndex; use std::cell::RefCell; use std::sync::atomic::{fence, AtomicBool, Ordering}; -use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; +use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; use anyhow::{anyhow, Context, Result}; @@ -216,7 +216,7 @@ pub struct CPU { /// The thread tid of this VCPU. tid: Arc>>, /// The VM combined by this VCPU. - vm: Weak>, + vm: Weak>, /// The state backup of architecture CPU right before boot. boot_state: Arc>, /// Sync the pause state of vCPU in hypervisor and userspace. @@ -238,7 +238,7 @@ impl CPU { hypervisor_cpu: Arc, id: u8, arch_cpu: Arc>, - vm: Arc>, + vm: Arc>, ) -> Self { CPU { id, @@ -294,7 +294,7 @@ impl CPU { &self.hypervisor_cpu } - pub fn vm(&self) -> Weak> { + pub fn vm(&self) -> Weak> { self.vm.clone() } @@ -404,15 +404,15 @@ impl CPUInterface for CPU { fn guest_shutdown(&self) -> Result<()> { if let Some(vm) = self.vm.upgrade() { - let shutdown_act = vm.read().unwrap().get_shutdown_action(); + let shutdown_act = vm.lock().unwrap().get_shutdown_action(); match shutdown_act { ShutdownActionPoweroff => { let (cpu_state, _) = &*self.state; *cpu_state.lock().unwrap() = CpuLifecycleState::Stopped; - vm.read().unwrap().destroy(); + vm.lock().unwrap().destroy(); } ShutdownActionPause => { - vm.read().unwrap().pause(); + vm.lock().unwrap().pause(); } } } else { @@ -434,7 +434,7 @@ impl CPUInterface for CPU { if let Some(vm) = self.vm.upgrade() { let (cpu_state, _) = &*self.state; *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; - vm.read().unwrap().reset(); + vm.lock().unwrap().reset(); } else { return Err(anyhow!(CpuError::NoMachineInterface)); } diff --git a/devices/src/interrupt_controller/aarch64/gicv3.rs b/devices/src/interrupt_controller/aarch64/gicv3.rs index 26f41e224..60babf03d 100644 --- a/devices/src/interrupt_controller/aarch64/gicv3.rs +++ b/devices/src/interrupt_controller/aarch64/gicv3.rs @@ -338,7 +338,7 @@ impl GICDevice for GICv3 { Ok(()) } - fn reset_state(&self) -> Result<()> { + fn reset(&self) -> Result<()> { info!("Reset gicv3its"); self.reset_its_state()?; info!("Reset gicv3"); diff --git a/devices/src/interrupt_controller/aarch64/mod.rs b/devices/src/interrupt_controller/aarch64/mod.rs index 27371aba9..ab548604c 100644 --- a/devices/src/interrupt_controller/aarch64/mod.rs +++ b/devices/src/interrupt_controller/aarch64/mod.rs @@ -73,7 +73,7 @@ pub trait GICDevice: MachineLifecycle { fn realize(&self) -> Result<()>; /// Reset 'GIC' - fn reset_state(&self) -> Result<()> { + fn reset(&self) -> Result<()> { Ok(()) } @@ -117,9 +117,7 @@ impl InterruptController { /// Reset the InterruptController pub fn reset(&self) -> Result<()> { - self.gic - .reset_state() - .with_context(|| "Failed to reset GIC") + self.gic.reset().with_context(|| "Failed to reset GIC") } /// Change `InterruptController` lifecycle state to `Stopped`. diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 98dcfca46..6689e516f 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -419,23 +419,23 @@ impl KvmCpu { match run { #[cfg(target_arch = "x86_64")] VcpuExit::IoIn(addr, data) => { - vm.read().unwrap().pio_in(u64::from(addr), data); + vm.lock().unwrap().pio_in(u64::from(addr), data); } #[cfg(target_arch = "x86_64")] VcpuExit::IoOut(addr, data) => { #[cfg(feature = "boot_time")] capture_boot_signal(addr as u64, data); - vm.read().unwrap().pio_out(u64::from(addr), data); + vm.lock().unwrap().pio_out(u64::from(addr), data); } VcpuExit::MmioRead(addr, data) => { - vm.read().unwrap().mmio_read(addr, data); + vm.lock().unwrap().mmio_read(addr, data); } VcpuExit::MmioWrite(addr, data) => { #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] capture_boot_signal(addr, data); - vm.read().unwrap().mmio_write(addr, data); + vm.lock().unwrap().mmio_write(addr, data); } #[cfg(target_arch = "x86_64")] VcpuExit::Hlt => { @@ -919,7 +919,7 @@ impl MsiIrqManager for KVMInterruptManager { #[cfg(test)] mod test { - use std::sync::{Arc, Mutex, RwLock}; + use std::sync::{Arc, Mutex}; use std::time::Duration; #[cfg(target_arch = "x86_64")] @@ -1002,7 +1002,7 @@ mod test { return; } - let vm = Arc::new(RwLock::new(TestVm::new())); + let vm = Arc::new(Mutex::new(TestVm::new())); let code_seg = kvm_segment { base: 0, @@ -1118,7 +1118,7 @@ mod test { vcpu_fd, )); - let vm = Arc::new(RwLock::new(TestVm::new())); + let vm = Arc::new(Mutex::new(TestVm::new())); let cpu = CPU::new( hypervisor_cpu.clone(), 0, diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 578029127..e528da660 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -131,8 +131,8 @@ impl MachineOps for LightMachine { Ok(()) } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { - let mut locked_vm = vm.write().unwrap(); + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); trace::sysbus(&locked_vm.base.sysbus.lock().unwrap()); trace::vm_state(&locked_vm.base.vm_state); diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index c117223b1..4da86b197 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -15,7 +15,7 @@ pub use crate::error::MachineError; use std::mem::size_of; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use std::sync::RwLock; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; #[cfg(feature = "ramfb")] @@ -180,8 +180,8 @@ impl StdMachine { }) } - pub fn handle_reset_request(vm: &Arc>) -> Result<()> { - let mut locked_vm = vm.write().unwrap(); + pub fn handle_reset_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); let mut fdt_addr: u64 = 0; for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { @@ -523,9 +523,9 @@ impl MachineOps for StdMachine { } } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; - let mut locked_vm = vm.write().unwrap(); + let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) .with_context(|| "Failed to register shutdown event")?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8a47faacd..8a79e42b9 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -451,7 +451,7 @@ pub trait MachineOps: MachineLifecycle { /// * `max_cpus` - max cpu number of virtual machine. fn create_vcpu( vcpu_id: u8, - vm: Arc>, + vm: Arc>, hypervisor: Arc>, #[cfg(target_arch = "x86_64")] max_cpus: u8, ) -> Result> @@ -485,7 +485,7 @@ pub trait MachineOps: MachineLifecycle { /// * `max_cpus` - The max number of vcpus. /// * `boot_cfg` - Boot message generated by reading boot source to guest memory. fn init_vcpu( - vm: Arc>, + vm: Arc>, hypervisor: Arc>, nr_cpus: u8, #[cfg(target_arch = "x86_64")] max_cpus: u8, @@ -2049,7 +2049,7 @@ pub trait MachineOps: MachineLifecycle { /// /// * `vm` - The machine structure. /// * `vm_config` - VM configuration. - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> where Self: Sized; @@ -2236,7 +2236,7 @@ pub trait MachineOps: MachineLifecycle { fn register_shutdown_event( shutdown_req: Arc, - vm: Arc>, + vm: Arc>, ) -> Result<()> { let shutdown_req_fd = shutdown_req.as_raw_fd(); let shutdown_req_handler: Rc = Rc::new(move |_, _| { @@ -2258,8 +2258,8 @@ fn register_shutdown_event( .with_context(|| "Failed to register event notifier.") } -fn handle_destroy_request(vm: &Arc>) -> bool { - let locked_vm = vm.read().unwrap(); +fn handle_destroy_request(vm: &Arc>) -> bool { + let locked_vm = vm.lock().unwrap(); let vmstate: VmState = { let state = locked_vm.machine_base().vm_state.deref().0.lock().unwrap(); *state @@ -2282,12 +2282,12 @@ fn handle_destroy_request(vm: &Arc>) -> bool { /// * `vm` - virtual machine that implement `MachineOps`. /// * `cmd_args` - Command arguments from user. pub fn vm_run( - vm: &Arc>, + vm: &Arc>, cmd_args: &arg_parser::ArgMatches, ) -> Result<()> { - let migrate = vm.read().unwrap().get_migrate_info(); + let migrate = vm.lock().unwrap().get_migrate_info(); if migrate.0 == MigrateMode::Unknown { - vm.read() + vm.lock() .unwrap() .run(cmd_args.is_present("freeze_cpu")) .with_context(|| "Failed to start VM.")?; @@ -2299,13 +2299,13 @@ pub fn vm_run( } /// Start incoming migration from destination. -fn start_incoming_migration(vm: &Arc>) -> Result<()> { - let (mode, path) = vm.read().unwrap().get_migrate_info(); +fn start_incoming_migration(vm: &Arc>) -> Result<()> { + let (mode, path) = vm.lock().unwrap().get_migrate_info(); match mode { MigrateMode::File => { MigrationManager::restore_snapshot(&path) .with_context(|| "Failed to restore snapshot")?; - vm.read() + vm.lock() .unwrap() .run(false) .with_context(|| "Failed to start VM.")?; @@ -2318,7 +2318,7 @@ fn start_incoming_migration(vm: &Arc>) -> R MigrationManager::recv_migration(&mut sock) .with_context(|| "Failed to receive migration with unix mode")?; - vm.read() + vm.lock() .unwrap() .run(false) .with_context(|| "Failed to start VM.")?; @@ -2331,7 +2331,7 @@ fn start_incoming_migration(vm: &Arc>) -> R MigrationManager::recv_migration(&mut sock) .with_context(|| "Failed to receive migration with tcp mode")?; - vm.read() + vm.lock() .unwrap() .run(false) .with_context(|| "Failed to start VM.")?; @@ -2344,7 +2344,7 @@ fn start_incoming_migration(vm: &Arc>) -> R } // End the migration and reset the mode. - let locked_vm = vm.read().unwrap(); + let locked_vm = vm.lock().unwrap(); let vm_config = locked_vm.get_vm_config(); if let Some((mode, _)) = vm_config.lock().unwrap().incoming.as_mut() { *mode = MigrateMode::Unknown; @@ -2365,12 +2365,12 @@ fn check_windows_emu_pid( pid_path: String, powerdown_req: Arc, shutdown_req: Arc, - vm: Arc>, + vm: Arc>, ) { let mut check_delay = Duration::from_millis(WINDOWS_EMU_PID_DEFAULT_INTERVAL); if !Path::new(&pid_path).exists() { info!("Detect emulator exited, let VM exits now"); - let locked_vm = vm.read().unwrap(); + let locked_vm = vm.lock().unwrap(); let mut vm_state = locked_vm.get_vm_state().deref().0.lock().unwrap(); if *vm_state == VmState::Paused { info!("VM state is paused, resume VM before exit"); @@ -2414,7 +2414,7 @@ pub(crate) fn watch_windows_emu_pid( vm_config: &VmConfig, power_button: Arc, shutdown_req: Arc, - vm: Arc>, + vm: Arc>, ) { let emu_pid = vm_config.emulator_pid.as_ref(); if emu_pid.is_none() { diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 53f126265..8410e96b4 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -545,7 +545,7 @@ impl MachineLifecycle for LightMachine { true } - fn reset(&self) -> bool { + fn reset(&mut self) -> bool { // For micro vm, the reboot command is equivalent to the shutdown command. for cpu in self.base.cpus.iter() { let (cpu_state, _) = cpu.state(); diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 86ee0e263..a7fb49ce8 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -20,7 +20,7 @@ use std::os::unix::io::RawFd; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; use std::string::String; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use std::u64; use anyhow::{bail, Context, Result}; @@ -242,7 +242,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { /// /// * `clone_vm` - Reference of the StdMachine. #[cfg(target_arch = "x86_64")] - fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()>; + fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()>; /// Register event notifier for hotplug vcpu event. /// @@ -254,7 +254,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_hotplug_vcpu_event( &self, hotplug_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let hotplug_req_fd = hotplug_req.as_raw_fd(); let hotplug_req_handler: Rc = Rc::new(move |_, _| { @@ -300,7 +300,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_reset_event( &self, reset_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let reset_req_fd = reset_req.as_raw_fd(); let reset_req_handler: Rc = Rc::new(move |_, _| { @@ -325,12 +325,12 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_pause_event( &self, pause_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let pause_req_fd = pause_req.as_raw_fd(); let pause_req_handler: Rc = Rc::new(move |_, _| { let _ret = pause_req.read(); - if !clone_vm.read().unwrap().pause() { + if !clone_vm.lock().unwrap().pause() { error!("VM pause failed"); } None @@ -350,12 +350,12 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { fn register_resume_event( &self, resume_req: Arc, - clone_vm: Arc>, + clone_vm: Arc>, ) -> Result<()> { let resume_req_fd = resume_req.as_raw_fd(); let resume_req_handler: Rc = Rc::new(move |_, _| { let _ret = resume_req.read(); - if !clone_vm.read().unwrap().resume() { + if !clone_vm.lock().unwrap().resume() { error!("VM resume failed!"); } None @@ -980,7 +980,7 @@ impl MachineLifecycle for StdMachine { .shutdown_action } - fn reset(&self) -> bool { + fn reset(&mut self) -> bool { if self.reset_req.write(1).is_err() { error!("Standard vm write reset request failed"); return false; diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 8f0494b5c..c530633f4 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; @@ -138,8 +138,8 @@ impl MachineOps for LightMachine { Ok(()) } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { - let mut locked_vm = vm.write().unwrap(); + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); trace::sysbus(&locked_vm.base.sysbus); trace::vm_state(&locked_vm.base.vm_state); diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index 865eb4567..da509da71 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -12,7 +12,7 @@ use std::io::{Seek, SeekFrom}; use std::mem::size_of; -use std::sync::{Arc, Barrier, Mutex, RwLock}; +use std::sync::{Arc, Barrier, Mutex}; use anyhow::{bail, Context, Result}; @@ -149,8 +149,8 @@ impl StdMachine { }) } - pub fn handle_reset_request(vm: &Arc>) -> Result<()> { - let mut locked_vm = vm.write().unwrap(); + pub fn handle_reset_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); for (cpu_index, cpu) in locked_vm.base.cpus.iter().enumerate() { cpu.pause() @@ -179,7 +179,7 @@ impl StdMachine { Ok(()) } - fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { + fn init_ich9_lpc(&self, vm: Arc>) -> Result<()> { let root_bus = Arc::downgrade(&self.pci_host.lock().unwrap().child_bus().unwrap()); let ich = ich9_lpc::LPCBridge::new( root_bus, @@ -199,8 +199,8 @@ impl StdMachine { None } - pub fn handle_hotplug_vcpu_request(vm: &Arc>) -> Result<()> { - let mut locked_vm = vm.write().unwrap(); + pub fn handle_hotplug_vcpu_request(vm: &Arc>) -> Result<()> { + let mut locked_vm = vm.lock().unwrap(); locked_vm.add_vcpu_device(vm.clone()) } @@ -208,7 +208,7 @@ impl StdMachine { &mut self, boot_config: CPUBootConfig, cpu_topology: CPUTopology, - vm: Arc>, + vm: Arc>, ) -> Result<()> { let region_base: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].0; let region_size: u64 = MEM_LAYOUT[LayoutEntryType::CpuController as usize].1; @@ -300,7 +300,7 @@ impl StdMachineOps for StdMachine { self.cpu_controller.as_ref().unwrap() } - fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()> { + fn add_vcpu_device(&mut self, clone_vm: Arc>) -> Result<()> { let mut locked_controller = self.cpu_controller.as_ref().unwrap().lock().unwrap(); let device_id; let vcpu_id; @@ -314,7 +314,7 @@ impl StdMachineOps for StdMachine { let boot_cfg = locked_controller.get_boot_config(); let topology = locked_controller.get_topology_config(); - let hypervisor = clone_vm.read().unwrap().base.hypervisor.clone(); + let hypervisor = clone_vm.lock().unwrap().base.hypervisor.clone(); let vcpu = ::create_vcpu( vcpu_id, clone_vm, @@ -479,10 +479,10 @@ impl MachineOps for StdMachine { syscall_whitelist() } - fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { + fn realize(vm: &Arc>, vm_config: &mut VmConfig) -> Result<()> { let nr_cpus = vm_config.machine_config.nr_cpus; let max_cpus = vm_config.machine_config.max_cpus; - let mut locked_vm = vm.write().unwrap(); + let mut locked_vm = vm.lock().unwrap(); locked_vm.init_global_config(vm_config)?; locked_vm.base.numa_nodes = locked_vm.add_numa_nodes(vm_config)?; locked_vm.init_interrupt_controller(u64::from(nr_cpus))?; diff --git a/machine_manager/src/event_loop.rs b/machine_manager/src/event_loop.rs index e197a39ee..79d7cf6f9 100644 --- a/machine_manager/src/event_loop.rs +++ b/machine_manager/src/event_loop.rs @@ -12,7 +12,7 @@ use std::collections::HashMap; use std::os::unix::prelude::RawFd; -use std::sync::{Arc, Barrier, RwLock}; +use std::sync::{Arc, Barrier, Mutex}; use std::{process, thread}; use anyhow::{bail, Result}; @@ -126,7 +126,7 @@ impl EventLoop { /// # Arguments /// /// * `manager` - The main part to manager the event loop. - pub fn set_manager(manager: Arc>) { + pub fn set_manager(manager: Arc>) { // SAFETY: All concurrently accessed data of EventLoopContext is protected. unsafe { if let Some(event_loop) = GLOBAL_EVENT_LOOP.as_mut() { diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index e36638448..a040d6efa 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -116,7 +116,7 @@ pub trait MachineLifecycle { } /// Reset VM, stop running and restart a new VM. - fn reset(&self) -> bool { + fn reset(&mut self) -> bool { self.notify_lifecycle(VmState::Running, VmState::Shutdown) } diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 62a9d62ea..35ad238f4 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -100,7 +100,7 @@ pub struct Socket { /// Socket stream with RwLock stream: RwLock>, /// Perform socket command - performer: Option>>, + performer: Option>>, } impl Socket { @@ -112,7 +112,7 @@ impl Socket { /// * `performer` - The `VM` to perform socket command. pub fn from_listener( listener: SocketListener, - performer: Option>>, + performer: Option>>, ) -> Self { Socket { listener, @@ -275,13 +275,12 @@ impl EventNotifierHelper for Socket { } /// Macro: to execute handle func with every arguments. -/// Attentions: Lifecycle commands cannot hold a write lock on the executor to avoid deadlock. macro_rules! qmp_command_match { ( $func:tt, $executor:expr, $ret:expr ) => { - $ret = $executor.read().unwrap().$func().into(); + $ret = $executor.$func().into(); }; ( $func:tt, $executor:expr, $cmd:expr, $ret:expr, $($arg:tt),* ) => { - $ret = $executor.write().unwrap().$func( + $ret = $executor.$func( $($cmd.$arg),* ).into(); }; @@ -290,7 +289,7 @@ macro_rules! qmp_command_match { /// Macro: to execute handle func with all arguments. macro_rules! qmp_command_match_with_argument { ( $func:tt, $executor:expr, $cmd:expr, $ret:expr ) => { - $ret = $executor.write().unwrap().$func($cmd).into(); + $ret = $executor.$func($cmd).into(); }; } @@ -366,7 +365,7 @@ fn parse_tcp_uri(uri: &str) -> Result<(String, u16)> { /// This function will fail when json parser failed or socket file description broke. fn handle_qmp( stream_fd: RawFd, - controller: &Arc>, + controller: &Arc>, leak_bucket: &mut LeakBucket, ) -> Result<()> { let mut qmp_service = crate::socket::SocketHandler::new(stream_fd); @@ -423,7 +422,7 @@ fn handle_qmp( /// function, and exec this qmp command. fn qmp_command_exec( qmp_command: QmpCommand, - controller: &Arc>, + controller: &Arc>, if_fd: Option, ) -> (String, bool) { let mut qmp_response = Response::create_empty_response(); @@ -431,7 +430,7 @@ fn qmp_command_exec( // Use macro create match to cover most Qmp command let mut id = create_command_matches!( - qmp_command.clone(); controller; qmp_response; + qmp_command.clone(); controller.lock().unwrap(); qmp_response; (stop, pause), (cont, resume), (system_powerdown, powerdown), @@ -494,12 +493,12 @@ fn qmp_command_exec( if id.is_none() { id = match qmp_command { QmpCommand::quit { id, .. } => { - controller.read().unwrap().destroy(); + controller.lock().unwrap().destroy(); shutdown_flag = true; id } QmpCommand::getfd { arguments, id } => { - qmp_response = controller.read().unwrap().getfd(arguments.fd_name, if_fd); + qmp_response = controller.lock().unwrap().getfd(arguments.fd_name, if_fd); id } QmpCommand::trace_get_state { arguments, id } => { diff --git a/machine_manager/src/test_server.rs b/machine_manager/src/test_server.rs index f8e2078c0..f126ede07 100644 --- a/machine_manager/src/test_server.rs +++ b/machine_manager/src/test_server.rs @@ -14,7 +14,7 @@ use std::os::unix::io::RawFd; use std::os::unix::net::UnixStream; use std::os::unix::prelude::AsRawFd; use std::rc::Rc; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use hex::FromHexError; use vmm_sys_util::epoll::EventSet; @@ -27,11 +27,11 @@ use util::test_helper::{eoi_intx, get_test_clock, has_msix_msg, query_intx, set_ pub struct TestSock { stream: UnixStream, - controller: Arc>, + controller: Arc>, } impl TestSock { - pub fn new(path: &str, controller: Arc>) -> Self { + pub fn new(path: &str, controller: Arc>) -> Self { let stream = match UnixStream::connect(path) { Ok(s) => s, Err(e) => { @@ -115,7 +115,7 @@ fn update_clock(target: u64) { } } -fn handle_test_cmd(stream_fd: RawFd, controller: &Arc>) { +fn handle_test_cmd(stream_fd: RawFd, controller: &Arc>) { let mut handler = SocketHandler::new(stream_fd); let msg = handler.get_line().unwrap().unwrap(); @@ -129,7 +129,7 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc { @@ -193,7 +193,7 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc { @@ -207,7 +207,7 @@ fn handle_test_cmd(stream_fd: RawFd, controller: &Arc { diff --git a/migration/src/general.rs b/migration/src/general.rs index 0f9f56dc2..0777e0de1 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -260,7 +260,7 @@ pub trait Lifecycle { /// Pause VM during migration. fn pause() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.read().unwrap().pause(); + locked_vm.lock().unwrap().pause(); } Ok(()) diff --git a/migration/src/manager.rs b/migration/src/manager.rs index 302350ff5..d381ae312 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -163,7 +163,7 @@ pub struct Vmm { /// Vm config pub config: Arc>, /// Trait to represent a Vm. - pub vm: Option>>, + pub vm: Option>>, /// Trait to represent CPU devices. pub cpus: HashMap>, /// Trait to represent memory devices. @@ -245,7 +245,7 @@ impl MigrationManager { /// # Arguments /// /// * `vm` - vm instance with MachineLifecycle trait. - pub fn register_vm_instance(vm: Arc>) + pub fn register_vm_instance(vm: Arc>) where T: MachineLifecycle + Sync + Send + 'static, { diff --git a/migration/src/migration.rs b/migration/src/migration.rs index 2edfa3562..2782d9cd8 100644 --- a/migration/src/migration.rs +++ b/migration/src/migration.rs @@ -578,7 +578,7 @@ impl MigrationManager { /// Clear live migration environment and shut down VM. fn clear_migration() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.read().unwrap().destroy(); + locked_vm.lock().unwrap().destroy(); } Ok(()) @@ -587,7 +587,7 @@ impl MigrationManager { /// Recover the virtual machine if migration is failed. pub fn recover_from_migration() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.read().unwrap().resume(); + locked_vm.lock().unwrap().resume(); } Ok(()) diff --git a/src/main.rs b/src/main.rs index 2d85d8f1f..cff73cbe2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use std::io::Write; use std::process::{ExitCode, Termination}; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use anyhow::{bail, Context, Result}; use log::{error, info}; @@ -154,12 +154,12 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let listeners = check_api_channel(cmd_args, vm_config)?; let mut sockets = Vec::new(); - let vm: Arc> = match vm_config.machine_config.mach_type { + let vm: Arc> = match vm_config.machine_config.mach_type { MachineType::MicroVm => { if is_test_enabled() { panic!("module test framework does not support microvm.") } - let vm = Arc::new(RwLock::new( + let vm = Arc::new(Mutex::new( LightMachine::new(vm_config).with_context(|| "Failed to init MicroVM")?, )); MachineOps::realize(&vm, vm_config).with_context(|| "Failed to realize micro VM.")?; @@ -171,7 +171,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res vm } MachineType::StandardVm => { - let vm = Arc::new(RwLock::new( + let vm = Arc::new(Mutex::new( StdMachine::new(vm_config).with_context(|| "Failed to init StandardVM")?, )); MachineOps::realize(&vm, vm_config) @@ -199,7 +199,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res if is_test_enabled() { panic!("please specify machine type.") } - let vm = Arc::new(RwLock::new( + let vm = Arc::new(Mutex::new( StdMachine::new(vm_config).with_context(|| "Failed to init NoneVM")?, )); EventLoop::set_manager(vm.clone()); @@ -213,7 +213,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); if !cmd_args.is_present("disable-seccomp") { - vm.read() + vm.lock() .unwrap() .register_seccomp(balloon_switch_on) .with_context(|| "Failed to register seccomp rules.")?; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index a511b1fcd..957fdb4cb 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -202,7 +202,7 @@ pub struct EventLoopContext { /// Epoll file descriptor. epoll: Epoll, /// Control epoll loop running. - manager: Option>>, + manager: Option>>, /// Used to wakeup epoll to re-evaluate events or timers. kick_event: EventFd, /// Used to avoid unnecessary kick operation when the @@ -290,7 +290,7 @@ impl EventLoopContext { } } - pub fn set_manager(&mut self, manager: Arc>) { + pub fn set_manager(&mut self, manager: Arc>) { self.manager = Some(manager); } @@ -547,8 +547,8 @@ impl EventLoopContext { /// Executes `epoll.wait()` to wait for events, and call the responding callbacks. pub fn run(&mut self) -> Result { if let Some(manager) = &self.manager { - if manager.read().unwrap().loop_should_exit() { - manager.read().unwrap().loop_cleanup()?; + if manager.lock().unwrap().loop_should_exit() { + manager.lock().unwrap().loop_cleanup()?; return Ok(false); } } @@ -558,8 +558,8 @@ impl EventLoopContext { pub fn iothread_run(&mut self) -> Result { if let Some(manager) = &self.manager { - if manager.read().unwrap().loop_should_exit() { - manager.read().unwrap().loop_cleanup()?; + if manager.lock().unwrap().loop_should_exit() { + manager.lock().unwrap().loop_cleanup()?; return Ok(false); } } -- Gitee From 4437d45671c8de8b659e99f4f8b238291ae02154 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Tue, 30 Jul 2024 11:44:50 +0800 Subject: [PATCH 1983/2187] ohaudio: stream interruption event process When host's audio render interrupts our render task, we destoy current render task. When host's audio capture interrupts our capture task, we just print logs. Because now no host audio capture will interrupt us, in case of potential problems, we do some log. Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 4 +- devices/src/misc/scream/ohaudio.rs | 60 +++++++++++++++++++++++++++--- util/src/ohos_binding/audio/mod.rs | 30 ++++++++++++--- util/src/ohos_binding/audio/sys.rs | 43 ++++++++++++++++++++- 4 files changed, 122 insertions(+), 15 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 328bef208..ead35388e 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -72,7 +72,7 @@ const POLL_DELAY_US: u64 = (TARGET_LATENCY_MS as u64) * 1000 / 8; pub const SCREAM_MAGIC: u64 = 0x02032023; -#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd)] pub enum AudioStatus { // Processor is ready and waiting for play/capture. #[default] @@ -81,6 +81,8 @@ pub enum AudioStatus { Started, // OH audio framework error occurred. Error, + // OH audio stream is interrupted. + Intr, } type AuthorityNotify = dyn Fn() + Send + Sync; diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index f7ad7117b..eaea009ce 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -225,7 +225,7 @@ impl OhAudioProcess for OhAudioRender { stream.fmt.size, stream.fmt.get_rate(), stream.fmt.channels, - AudioProcessCb::RendererCb(Some(on_write_data_cb)), + AudioProcessCb::RendererCb(Some(on_write_data_cb), Some(render_on_interrupt_cb)), ptr::addr_of!(*self) as *mut c_void, ) { Ok(()) => self.ctx = Some(context), @@ -251,7 +251,7 @@ impl OhAudioProcess for OhAudioRender { fn destroy(&mut self) { info!("Renderer destroy"); match self.status { - AudioStatus::Error => { + AudioStatus::Error | AudioStatus::Intr => { self.ctx = None; self.status = AudioStatus::Ready; return; @@ -278,8 +278,11 @@ impl OhAudioProcess for OhAudioRender { recv_data.audio_size as usize, )); - if self.status == AudioStatus::Error { - error!("Audio server error occurred. Destroy and reconnect it."); + if self.status == AudioStatus::Error || self.status == AudioStatus::Intr { + error!( + "Audio server {:?} occurred. Destroy and reconnect it.", + self.status + ); self.destroy(); } @@ -375,7 +378,7 @@ impl OhAudioProcess for OhAudioCapture { stream.fmt.size, stream.fmt.get_rate(), stream.fmt.channels, - AudioProcessCb::CapturerCb(Some(on_read_data_cb)), + AudioProcessCb::CapturerCb(Some(on_read_data_cb), Some(capture_on_interrupt_cb)), ptr::addr_of!(*self) as *mut c_void, ) { Ok(()) => self.ctx = Some(context), @@ -411,7 +414,7 @@ impl OhAudioProcess for OhAudioCapture { trace::trace_scope_start!(ohaudio_capturer_process, args = (recv_data)); - if self.status == AudioStatus::Error { + if self.status == AudioStatus::Error || self.status == AudioStatus::Intr { self.destroy(); } @@ -439,6 +442,51 @@ impl OhAudioProcess for OhAudioCapture { } } +extern "C" fn render_on_interrupt_cb( + _renderer: *mut OhAudioRenderer, + user_data: *mut ::std::os::raw::c_void, + source_type: capi::OHAudioInterruptSourceType, + hint: capi::OHAudioInterruptHint, +) -> i32 { + info!( + "Render interrupts, type is {}, hint is {}", + source_type, hint + ); + // SAFETY: we make sure that it is OhAudioRender when register callback. + let render = unsafe { + (user_data as *mut OhAudioRender) + .as_mut() + .unwrap_unchecked() + }; + if hint == capi::AUDIOSTREAM_INTERRUPT_HINT_PAUSE { + render.status = AudioStatus::Intr; + } + 0 +} + +extern "C" fn capture_on_interrupt_cb( + _capturer: *mut OhAudioCapturer, + user_data: *mut ::std::os::raw::c_void, + source_type: capi::OHAudioInterruptSourceType, + hint: capi::OHAudioInterruptHint, +) -> i32 { + info!( + "Capture interrupts, type is {}, hint is {}", + source_type, hint + ); + + // SAFETY: we make sure that it is OhAudioCapture when register callback. + let capture = unsafe { + (user_data as *mut OhAudioCapture) + .as_mut() + .unwrap_unchecked() + }; + if hint == capi::AUDIOSTREAM_INTERRUPT_HINT_PAUSE { + capture.status = AudioStatus::Intr; + } + 0 +} + extern "C" fn on_write_data_cb( _renderer: *mut OhAudioRenderer, user_data: *mut ::std::os::raw::c_void, diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index 613647198..07b2cb09a 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod sys; +pub mod sys; use std::os::raw::c_void; use std::ptr; @@ -20,7 +20,7 @@ use log::error; use once_cell::sync::Lazy; use super::hwf_adapter::{hwf_adapter_volume_api, volume::VolumeFuncTable}; -use sys as capi; +pub use sys as capi; const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; @@ -187,6 +187,14 @@ pub enum AudioProcessCb { length: i32, ) -> i32, >, + Option< + extern "C" fn( + capturer: *mut capi::OhAudioCapturer, + userData: *mut c_void, + source_type: capi::OHAudioInterruptSourceType, + hint: capi::OHAudioInterruptHint, + ) -> i32, + >, ), RendererCb( Option< @@ -197,6 +205,14 @@ pub enum AudioProcessCb { length: i32, ) -> i32, >, + Option< + extern "C" fn( + capturer: *mut capi::OhAudioRenderer, + userData: *mut c_void, + source_type: capi::OHAudioInterruptSourceType, + hint: capi::OHAudioInterruptHint, + ) -> i32, + >, ), } @@ -258,8 +274,9 @@ impl AudioContext { fn create_renderer(&mut self, cb: AudioProcessCb) -> Result<(), OAErr> { let mut cbs = capi::OhAudioRendererCallbacks::default(); - if let AudioProcessCb::RendererCb(f) = cb { - cbs.oh_audio_renderer_on_write_data = f; + if let AudioProcessCb::RendererCb(data_cb, interrupt_cb) = cb { + cbs.oh_audio_renderer_on_write_data = data_cb; + cbs.oh_audio_renderer_on_interrupt_event = interrupt_cb; } call_capi!(OH_AudioStreamBuilder_SetRendererCallback( self.builder, @@ -274,8 +291,9 @@ impl AudioContext { fn create_capturer(&mut self, cb: AudioProcessCb) -> Result<(), OAErr> { let mut cbs = capi::OhAudioCapturerCallbacks::default(); - if let AudioProcessCb::CapturerCb(v) = cb { - cbs.oh_audio_capturer_on_read_data = v; + if let AudioProcessCb::CapturerCb(data_cb, interrupt_cb) = cb { + cbs.oh_audio_capturer_on_read_data = data_cb; + cbs.oh_audio_capturer_on_interrupt_event = interrupt_cb; } call_capi!(OH_AudioStreamBuilder_SetCapturerCallback( self.builder, diff --git a/util/src/ohos_binding/audio/sys.rs b/util/src/ohos_binding/audio/sys.rs index 289becbe3..56dcee2e4 100755 --- a/util/src/ohos_binding/audio/sys.rs +++ b/util/src/ohos_binding/audio/sys.rs @@ -131,6 +131,31 @@ pub const OH_AUDIO_STREAM_SOURCE_TYPE_AUDIOSTREAM_SOURCE_TYPE_VOICE_COMMUNICATIO /// @since 10 pub type OHAudioStreamSourceType = ::std::os::raw::c_int; +#[allow(unused)] +pub const AUDIOSTREAM_INTERRUPT_FORCE: OHAudioInterruptSourceType = 0; +#[allow(unused)] +pub const AUDIOSTREAM_INTERRUPT_SHARE: OHAudioInterruptSourceType = 1; + +/// Defines the audio interrupt source type. +/// +/// @since 10 +pub type OHAudioInterruptSourceType = ::std::os::raw::c_int; + +#[allow(unused)] +pub const AUDIOSTREAM_INTERRUPT_HINT_RESUME: OHAudioInterruptHint = 1; +pub const AUDIOSTREAM_INTERRUPT_HINT_PAUSE: OHAudioInterruptHint = 2; +#[allow(unused)] +pub const AUDIOSTREAM_INTERRUPT_HINT_STOP: OHAudioInterruptHint = 3; +#[allow(unused)] +pub const AUDIOSTREAM_INTERRUPT_HINT_DUCK: OHAudioInterruptHint = 4; +#[allow(unused)] +pub const AUDIOSTREAM_INTERRUPT_HINT_UNDUCK: OHAudioInterruptHint = 5; + +/// Defines the audio interrupt hint type. +/// +/// @since 10 +pub type OHAudioInterruptHint = ::std::os::raw::c_int; + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct OH_AudioStreamBuilderStruct { @@ -183,7 +208,14 @@ pub struct OhAudioRendererCallbacks { ) -> i32, >, pub oh_audio_renderer_on_stream_event: PlaceHolderFn, - pub oh_audio_renderer_on_interrpt_event: PlaceHolderFn, + pub oh_audio_renderer_on_interrupt_event: ::std::option::Option< + extern "C" fn( + renderer: *mut OhAudioRenderer, + userData: *mut ::std::os::raw::c_void, + source_type: OHAudioInterruptSourceType, + hint: OHAudioInterruptHint, + ) -> i32, + >, pub oh_audio_renderer_on_error: PlaceHolderFn, } @@ -204,7 +236,14 @@ pub struct OhAudioCapturerCallbacks { ) -> i32, >, pub oh_audio_capturer_on_stream_event: PlaceHolderFn, - pub oh_audio_capturer_on_interrpt_event: PlaceHolderFn, + pub oh_audio_capturer_on_interrupt_event: ::std::option::Option< + extern "C" fn( + capturer: *mut OhAudioCapturer, + userData: *mut ::std::os::raw::c_void, + source_type: OHAudioInterruptSourceType, + hint: OHAudioInterruptHint, + ) -> i32, + >, pub oh_audio_capturer_on_error: PlaceHolderFn, } -- Gitee From cc45a48827201b5f1261b28d525f4d03ded127f5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 30 Jul 2024 14:55:12 +0800 Subject: [PATCH 1984/2187] machine: Avoid deadlock by retry when pause destroy or reset VM Enable timeout when waiting for vCPU exits. When the timeout occurs, which means the vCPU may also try to acquire VM lock which is already hold by us. So we should give up and try again. Signed-off-by: Keqian Zhu --- cpu/src/aarch64/mod.rs | 2 ++ cpu/src/lib.rs | 18 +++++++++++++++--- hypervisor/src/kvm/aarch64/mod.rs | 10 +++++++--- hypervisor/src/kvm/mod.rs | 13 +++++++++---- machine/src/aarch64/standard.rs | 7 ++++--- machine/src/lib.rs | 6 +++++- machine/src/standard_common/mod.rs | 12 +++++++++--- machine_manager/src/qmp/qmp_socket.rs | 26 ++++++++++++++++++++++++-- migration/src/general.rs | 16 +++++++++++++++- 9 files changed, 90 insertions(+), 20 deletions(-) diff --git a/cpu/src/aarch64/mod.rs b/cpu/src/aarch64/mod.rs index 3aebcbc0a..9d89b4897 100644 --- a/cpu/src/aarch64/mod.rs +++ b/cpu/src/aarch64/mod.rs @@ -108,6 +108,8 @@ pub struct ArmCPUState { pub features: ArmCPUFeatures, /// Virtual timer count. pub vtimer_cnt: u64, + /// Virtual timer count valid. + pub vtimer_cnt_valid: bool, } impl ArmCPUState { diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 7a1162951..2690c1c2c 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -65,14 +65,16 @@ use std::cell::RefCell; use std::sync::atomic::{fence, AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Condvar, Mutex, Weak}; use std::thread; +use std::time::Duration; +use std::time::Instant; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use log::{error, info, warn}; use nix::unistd::gettid; use machine_manager::config::ShutdownAction::{ShutdownActionPause, ShutdownActionPoweroff}; use machine_manager::event; -use machine_manager::machine::{HypervisorType, MachineInterface}; +use machine_manager::machine::{HypervisorType, MachineInterface, VmState}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; // SIGRTMIN = 34 (GNU, in MUSL is 35) and SIGRTMAX = 64 in linux, VCPU signal @@ -412,7 +414,17 @@ impl CPUInterface for CPU { vm.lock().unwrap().destroy(); } ShutdownActionPause => { - vm.lock().unwrap().pause(); + let now = Instant::now(); + while !vm.lock().unwrap().pause() { + thread::sleep(Duration::from_millis(5)); + if now.elapsed() > Duration::from_secs(2) { + // Not use resume() to avoid unnecessary qmp event. + vm.lock() + .unwrap() + .notify_lifecycle(VmState::Paused, VmState::Running); + bail!("Failed to pause VM"); + } + } } } } else { diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 072123600..3befdd7c7 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -259,6 +259,7 @@ impl KvmCpu { .get_one_reg(KVM_REG_ARM_TIMER_CNT) .with_context(|| "Failed to get virtual timer count")? as u64; + locked_arch_cpu.vtimer_cnt_valid = true; } } @@ -270,7 +271,7 @@ impl KvmCpu { arch_cpu: Arc>, regs_index: RegsIndex, ) -> Result<()> { - let locked_arch_cpu = arch_cpu.lock().unwrap(); + let mut locked_arch_cpu = arch_cpu.lock().unwrap(); let apic_id = locked_arch_cpu.apic_id; match regs_index { RegsIndex::CoreRegs => { @@ -300,8 +301,11 @@ impl KvmCpu { } } RegsIndex::VtimerCount => { - self.set_one_reg(KVM_REG_ARM_TIMER_CNT, locked_arch_cpu.vtimer_cnt as u128) - .with_context(|| "Failed to set virtual timer count")?; + if locked_arch_cpu.vtimer_cnt_valid { + self.set_one_reg(KVM_REG_ARM_TIMER_CNT, locked_arch_cpu.vtimer_cnt as u128) + .with_context(|| "Failed to set virtual timer count")?; + locked_arch_cpu.vtimer_cnt_valid = false; + } } } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 6689e516f..d8a8da8f6 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -645,7 +645,9 @@ impl CPUHypervisorOps for KvmCpu { if *cpu_state.lock().unwrap() == CpuLifecycleState::Running { *cpu_state.lock().unwrap() = CpuLifecycleState::Paused; cvar.notify_one() - } else if *cpu_state.lock().unwrap() == CpuLifecycleState::Paused { + } else if *cpu_state.lock().unwrap() == CpuLifecycleState::Paused + && pause_signal.load(Ordering::SeqCst) + { return Ok(()); } @@ -662,10 +664,13 @@ impl CPUHypervisorOps for KvmCpu { } // It shall wait for the vCPU pause state from hypervisor exits. - loop { - if pause_signal.load(Ordering::SeqCst) { - break; + let mut sleep_times = 0; + while !pause_signal.load(Ordering::SeqCst) { + if sleep_times >= 5 { + bail!(CpuError::StopVcpu("timeout".to_string())); } + thread::sleep(Duration::from_millis(5)); + sleep_times += 1; } Ok(()) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 4da86b197..74d24c52f 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -63,7 +63,7 @@ use machine_manager::config::{ BootIndexInfo, DriveConfig, NumaNode, Param, SerialConfig, VmConfig, }; use machine_manager::event; -use machine_manager::machine::MachineLifecycle; +use machine_manager::machine::{MachineLifecycle, VmState}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_schema}; use migration::{MigrationManager, MigrationStatus}; #[cfg(feature = "gtk")] @@ -289,8 +289,9 @@ impl StdMachine { if let Some(vcpu) = self.get_cpus().get(vcpu_index) { let (cpu_state, _) = vcpu.state(); let cpu_state = *cpu_state.lock().unwrap(); - if cpu_state != CpuLifecycleState::Paused { - self.pause(); + if cpu_state != CpuLifecycleState::Paused && !self.pause() { + self.notify_lifecycle(VmState::Paused, VmState::Running); + return None; } let value = match vcpu.hypervisor_cpu.get_one_reg(addr) { diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 8a79e42b9..fbb6f14eb 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -37,7 +37,7 @@ use std::time::Duration; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; -use log::{info, warn}; +use log::{error, info, warn}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -2244,6 +2244,10 @@ fn register_shutdown_event( if handle_destroy_request(&vm) { Some(gen_delete_notifiers(&[shutdown_req_fd])) } else { + warn!("Fail to shutdown VM, try again"); + if shutdown_req.write(1).is_err() { + error!("Failed to send shutdown request"); + } None } }); diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index a7fb49ce8..ebf86b189 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -24,7 +24,7 @@ use std::sync::{Arc, Mutex}; use std::u64; use anyhow::{bail, Context, Result}; -use log::error; +use log::{error, warn}; use util::set_termi_canon_mode; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -306,7 +306,10 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let reset_req_handler: Rc = Rc::new(move |_, _| { read_fd(reset_req_fd); if let Err(e) = StdMachine::handle_reset_request(&clone_vm) { - error!("Fail to reboot standard VM, {:?}", e); + warn!("Fail to reboot standard VM, {:?}, try again", e); + if reset_req.write(1).is_err() { + error!("Failed to send VM reset request"); + } } None @@ -331,7 +334,10 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { let pause_req_handler: Rc = Rc::new(move |_, _| { let _ret = pause_req.read(); if !clone_vm.lock().unwrap().pause() { - error!("VM pause failed"); + warn!("VM pause failed, try again"); + if pause_req.write(1).is_err() { + error!("Failed to send VM pause request"); + } } None }); diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 35ad238f4..8326e7cd9 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -15,6 +15,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::str::FromStr; use std::sync::{Arc, Mutex, RwLock}; +use std::thread; +use std::time::{Duration, Instant}; use anyhow::{bail, Context, Result}; use log::{error, info, warn}; @@ -25,7 +27,7 @@ use super::qmp_schema::QmpCommand; use super::{qmp_channel::QmpChannel, qmp_response::QmpGreeting, qmp_response::Response}; use crate::event; use crate::event_loop::EventLoop; -use crate::machine::MachineExternalInterface; +use crate::machine::{MachineExternalInterface, VmState}; use crate::socket::SocketHandler; use crate::socket::SocketRWHandler; use crate::temp_cleaner::TempCleaner; @@ -431,7 +433,6 @@ fn qmp_command_exec( // Use macro create match to cover most Qmp command let mut id = create_command_matches!( qmp_command.clone(); controller.lock().unwrap(); qmp_response; - (stop, pause), (cont, resume), (system_powerdown, powerdown), (system_reset, reset), @@ -492,6 +493,27 @@ fn qmp_command_exec( // Handle the Qmp command which macro can't cover if id.is_none() { id = match qmp_command { + QmpCommand::stop { arguments: _, id } => { + let now = Instant::now(); + while !controller.lock().unwrap().pause() { + thread::sleep(Duration::from_millis(5)); + if now.elapsed() > Duration::from_secs(2) { + // Not use resume() to avoid unnecessary qmp event. + controller + .lock() + .unwrap() + .notify_lifecycle(VmState::Paused, VmState::Running); + qmp_response = Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError( + "Failed to pause VM".to_string(), + ), + None, + ); + break; + } + } + id + } QmpCommand::quit { id, .. } => { controller.lock().unwrap().destroy(); shutdown_flag = true; diff --git a/migration/src/general.rs b/migration/src/general.rs index 0777e0de1..d0c7c65dc 100644 --- a/migration/src/general.rs +++ b/migration/src/general.rs @@ -14,6 +14,8 @@ use std::collections::{hash_map::DefaultHasher, HashMap}; use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::mem::size_of; +use std::thread; +use std::time::{Duration, Instant}; use anyhow::{anyhow, bail, Context, Result}; @@ -22,6 +24,7 @@ use crate::protocol::{ DeviceStateDesc, FileFormat, MigrationHeader, MigrationStatus, VersionCheck, HEADER_LENGTH, }; use crate::{MigrationError, MigrationManager}; +use machine_manager::machine::VmState; use util::unix::host_page_size; impl MigrationManager { @@ -260,7 +263,18 @@ pub trait Lifecycle { /// Pause VM during migration. fn pause() -> Result<()> { if let Some(locked_vm) = &MIGRATION_MANAGER.vmm.read().unwrap().vm { - locked_vm.lock().unwrap().pause(); + let now = Instant::now(); + while !locked_vm.lock().unwrap().pause() { + thread::sleep(Duration::from_millis(5)); + if now.elapsed() > Duration::from_secs(2) { + // Not use resume() to avoid unnecessary qmp event. + locked_vm + .lock() + .unwrap() + .notify_lifecycle(VmState::Paused, VmState::Running); + bail!("Failed to pause VM"); + } + } } Ok(()) -- Gitee From a97344533c213598e6b13b6758d93fe772c81a75 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Thu, 1 Aug 2024 14:27:11 +0800 Subject: [PATCH 1985/2187] OHUI:force to update ohui-fb When dpy_switch, new surface is created but not copy to ohui-fb, because viogpu bar0 passthru to ohui-fb. Force to copy data from surface to ohui-fb when dpy_switch. Signed-off-by: zhanghan --- ui/src/ohui_srv/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 384bf02f1..6f13d6b01 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -219,11 +219,12 @@ impl OhUiServer { stride: i32, pos: (i32, i32), size: (i32, i32), + force_copy: bool, ) { let (x, y) = pos; let (w, h) = size; - if self.framebuffer == 0 || *self.passthru.get_or_init(|| false) { + if self.framebuffer == 0 || (!force_copy && *self.passthru.get_or_init(|| false)) { return; } @@ -297,6 +298,7 @@ impl DisplayChangeListenerOperations for OhUiServer { locked_surface.stride, (0, 0), (locked_surface.width, locked_surface.height), + true, ); if !self.connected() { @@ -328,6 +330,7 @@ impl DisplayChangeListenerOperations for OhUiServer { locked_surface.stride, (x, y), (w, h), + false, ); self.msg_handler -- Gitee From caba92246e44f9db75a093bf9615461ee394ae4f Mon Sep 17 00:00:00 2001 From: zhanghan Date: Fri, 2 Aug 2024 11:46:08 +0800 Subject: [PATCH 1986/2187] MST:fix usb camera mst case adjust different log for ohcamera Signed-off-by: zhanghan --- tests/mod_test/tests/usb_camera_test.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index b519e1ca5..72539614b 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -553,11 +553,17 @@ fn test_xhci_camera_hotplug_invalid() { .with_config("auto_run", true) .build(); + #[cfg(not(target_env = "ohos"))] qmp_cameradev_add(&test_state, "camdev0", "v4l2", "/tmp/not-existed"); + #[cfg(target_env = "ohos")] + qmp_cameradev_add(&test_state, "camdev0", "ohcamera", "InvalidNum"); // Invalid cameradev. let value = qmp_plug_camera(&test_state, "usbcam0", "camdev0"); let desc = value["error"]["desc"].as_str().unwrap().to_string(); + #[cfg(not(target_env = "ohos"))] assert_eq!(desc, "Failed to open v4l2 backend /tmp/not-existed."); + #[cfg(target_env = "ohos")] + assert_eq!(desc, "Invalid PATH format"); // Invalid device id. let value = qmp_unplug_camera(&test_state.clone(), "usbcam0"); let desc = value["error"]["desc"].as_str().unwrap().to_string(); -- Gitee From 140c0a06986d1adebfc4c3e620743882afcbb3ea Mon Sep 17 00:00:00 2001 From: jinhaogao Date: Sat, 3 Aug 2024 15:40:28 +0800 Subject: [PATCH 1987/2187] Test: Modify the parameter of hotplugging block in pci test. Change the parameter of hotplugging block from asynchronous to synchronous in case that the asynchronous backend is not supported. Signed-off-by: jinhaogao --- tests/mod_test/tests/pci_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index e23d67b12..bdd4da48a 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -250,8 +250,8 @@ fn build_hotplug_blk_cmd( let add_blk_command = format!( "{{\"execute\": \"blockdev-add\", \ \"arguments\": {{\"node-name\": \"drive-{}\", \"file\": {{\"driver\": \ - \"file\", \"filename\": \"{}\", \"aio\": \"native\"}}, \ - \"cache\": {{\"direct\": true}}, \"read-only\": false}}}}", + \"file\", \"filename\": \"{}\", \"aio\": \"off\"}}, \ + \"cache\": {{\"direct\": false}}, \"read-only\": false}}}}", hotplug_blk_id, hotplug_image_path ); -- Gitee From 090add698de7e3f2a23ad4f26a41628553d3c1ae Mon Sep 17 00:00:00 2001 From: Jinyu Tang Date: Mon, 5 Aug 2024 15:37:57 +0800 Subject: [PATCH 1988/2187] Micro: fix the ioctl allow for aarch64 In micro mode, KVM_SET_VCPU_EVENTS and KVM_SET_ONE_REG are not allowed by seccomp, but they are essential for vcpu init. The guest can not run when use micro mode in aarch64 for this reason. So fix it. --- machine/src/aarch64/micro.rs | 1 + machine/src/micro_common/syscall.rs | 1 + machine/src/x86_64/micro.rs | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index e528da660..6afc15e21 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -231,6 +231,7 @@ pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_ONE_REG() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DEVICE_ATTR() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_REG_LIST() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_ONE_REG() as u32) } pub(crate) fn arch_syscall_whitelist() -> Vec { diff --git a/machine/src/micro_common/syscall.rs b/machine/src/micro_common/syscall.rs index f3acec191..6ae9a56a2 100644 --- a/machine/src/micro_common/syscall.rs +++ b/machine/src/micro_common/syscall.rs @@ -159,6 +159,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32); arch_ioctl_allow_list(bpf_rule) } diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index c530633f4..5747076ae 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -235,7 +235,6 @@ pub(crate) fn arch_ioctl_allow_list(bpf_rule: BpfRule) -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_LAPIC() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MSRS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MSRS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_CPUID2() as u32) } -- Gitee From 6e947a8af35cddce317cac359eeefa8a581206d2 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Tue, 6 Aug 2024 09:35:48 +0800 Subject: [PATCH 1989/2187] Memory/Baloon: MST enhancements Skip NUMA related test cases if the system does not support NUMA. --- tests/mod_test/src/utils.rs | 28 ++++++++++++++++++++++++++++ tests/mod_test/tests/balloon_test.rs | 7 ++++++- tests/mod_test/tests/memory_test.rs | 11 ++++++++++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index b84205d4b..228247e21 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -127,3 +127,31 @@ pub fn cleanup_img(image_path: String) { fs::remove_file(img_path).expect("lack permissions to remove the file"); } + +pub fn support_numa() -> bool { + let numa_nodes_path = "/sys/devices/system/node/"; + + if Path::new(numa_nodes_path).exists() { + match fs::read_dir(numa_nodes_path) { + Ok(entries) => { + let mut has_nodes = false; + for entry in entries { + if let Ok(entry) = entry { + if entry.file_name().to_str().unwrap_or("").starts_with("node") { + has_nodes = true; + break; + } + } + } + if has_nodes { + return true; + } else { + return false; + } + } + Err(_) => return false, + } + } else { + return false; + } +} diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index dbcd67fde..9fc23cc4c 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -13,10 +13,11 @@ use std::cell::RefCell; use std::fs::{remove_file, File}; use std::io::{self, BufRead, BufReader}; -use std::process::Command; +use std::process::{exit, Command}; use std::rc::Rc; use std::{thread, time}; +use mod_test::utils::support_numa; use serde_json::json; use mod_test::libdriver::machine::TestStdMachine; @@ -1054,6 +1055,10 @@ fn auto_balloon_test_001() { /// Expect: /// 1/2.Success fn balloon_numa1() { + if !support_numa() { + return; + } + let page_num = 255_u32; let mut idx = 0_u32; let balloon = VirtioBalloonTest::numa_node_new(); diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 155ce51a9..aeeda0cf6 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -12,10 +12,11 @@ use std::cell::RefCell; use std::fs::{remove_file, File}; -use std::process::Command; +use std::process::{exit, Command}; use std::rc::Rc; use std::string::String; +use mod_test::utils::support_numa; use serde_json::{json, Value::String as JsonString}; use mod_test::{ @@ -609,6 +610,10 @@ fn ram_readwrite_exception() { /// 1/2/3/4: success. #[test] fn ram_readwrite_numa() { + if !support_numa() { + return; + } + let mut args: Vec<&str> = Vec::new(); let mut extra_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); args.append(&mut extra_args); @@ -665,6 +670,10 @@ fn ram_readwrite_numa() { /// 1/2/3/4: success. #[test] fn ram_readwrite_numa1() { + if !support_numa() { + return; + } + let mut args: Vec<&str> = Vec::new(); let mut extra_args: Vec<&str> = MACHINE_TYPE_ARG.split(' ').collect(); args.append(&mut extra_args); -- Gitee From e12feb87a69bc2caca656843915b003c0f72238d Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Tue, 6 Aug 2024 10:08:30 +0800 Subject: [PATCH 1990/2187] Memory: do not test hugepage on ohos. On the OH system, the virtual machine does not have hugepage usage scenarios, maybe insufficient hugepages are reserved. So we ignore this test case. --- tests/mod_test/tests/memory_test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index aeeda0cf6..143dbc74e 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -475,6 +475,7 @@ fn prealloc_ram_read_write() { /// 4. Destroy device. /// Expect: /// 1/2/3/4: success. +#[cfg(not(target_env = "ohos"))] #[test] fn hugepage_ram_read_write() { // crate hugetlbfs directory -- Gitee From 1884a71bd982eb081724856102b526c93a7a7c12 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Mon, 5 Aug 2024 19:21:48 +0800 Subject: [PATCH 1991/2187] scream: support volume sync on OHOS This patch extends ivshmem bar0 and adds msix interrupt support to synchronize volume on OHOS. Scream registers bar0 extension operations to handle bar0 read/write. When the guest changes volume, scream driver writes the changed volume to the specific field of bar0 then scream handler sets the OHOS volume. When OHOS volume changes, OH audio framework calls scream callback and then scream notify the guest via msix vector 0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 楚影 --- devices/src/misc/ivshmem.rs | 94 +++++++++++++++++++-- devices/src/misc/scream/mod.rs | 17 +++- devices/src/misc/scream/ohaudio.rs | 75 +++++++++++++++- util/src/ohos_binding/audio/mod.rs | 73 ++++++++++++++++ util/src/ohos_binding/hwf_adapter/mod.rs | 25 ++++++ util/src/ohos_binding/hwf_adapter/volume.rs | 45 ++++++++++ 6 files changed, 318 insertions(+), 11 deletions(-) create mode 100644 util/src/ohos_binding/hwf_adapter/volume.rs diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 6145bb86e..090779fa2 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -12,15 +12,17 @@ use std::sync::{ atomic::{AtomicU16, Ordering}, - Arc, Mutex, Weak, + Arc, Mutex, RwLock, Weak, }; use anyhow::Result; +use log::error; use crate::pci::config::{ PciConfig, RegionType, DEVICE_ID, PCI_CLASS_MEMORY_RAM, PCI_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT_QUMRANET, REVISION_ID, SUB_CLASS_CODE, VENDOR_ID, }; +use crate::pci::msix::init_msix; use crate::pci::{le_write_u16, PciBus, PciDevBase, PciDevOps}; use crate::{convert_bus_ref, Bus, Device, DeviceBase, PCI_BUS}; use address_space::{GuestAddress, Region, RegionOps}; @@ -34,11 +36,27 @@ const PCI_BAR_MAX_IVSHMEM: u8 = 3; const IVSHMEM_REG_BAR_SIZE: u64 = 0x100; +const IVSHMEM_BAR0_IRQ_MASK: u64 = 0; +const IVSHMEM_BAR0_IRQ_STATUS: u64 = 4; +const IVSHMEM_BAR0_IVPOSITION: u64 = 8; +const IVSHMEM_BAR0_DOORBELL: u64 = 12; + +type Bar0Write = dyn Fn(&[u8], u64) -> bool + Send + Sync; +type Bar0Read = dyn Fn(&mut [u8], u64) -> bool + Send + Sync; + +#[derive(Default)] +struct Bar0Ops { + write: Option>, + read: Option>, +} + /// Intel-VM shared memory device structure. pub struct Ivshmem { base: PciDevBase, dev_id: Arc, ram_mem_region: Region, + vector_nr: u32, + bar0_ops: Arc>, } impl Ivshmem { @@ -47,6 +65,7 @@ impl Ivshmem { devfn: u8, parent_bus: Weak>, ram_mem_region: Region, + vector_nr: u32, ) -> Self { Self { base: PciDevBase { @@ -56,14 +75,45 @@ impl Ivshmem { }, dev_id: Arc::new(AtomicU16::new(0)), ram_mem_region, + vector_nr, + bar0_ops: Arc::new(RwLock::new(Bar0Ops::default())), } } fn register_bars(&mut self) -> Result<()> { - // Currently, ivshmem uses only the shared memory and does not use interrupt. - // Therefore, bar0 read and write callback is not implemented. - let reg_read = move |_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }; - let reg_write = move |_: &[u8], _: GuestAddress, _: u64| -> bool { true }; + // Currently, ivshmem does not support intx interrupt, ivposition and doorbell. + let bar0_ops = self.bar0_ops.clone(); + let reg_read = move |data: &mut [u8], _: GuestAddress, offset: u64| -> bool { + if offset >= IVSHMEM_REG_BAR_SIZE { + error!("ivshmem: read offset {} exceeds bar0 size", offset); + return true; + } + match offset { + IVSHMEM_BAR0_IRQ_MASK | IVSHMEM_BAR0_IRQ_STATUS | IVSHMEM_BAR0_IVPOSITION => {} + _ => { + if let Some(rcb) = bar0_ops.read().unwrap().read.as_ref() { + return rcb(data, offset); + } + } + } + true + }; + let bar0_ops = self.bar0_ops.clone(); + let reg_write = move |data: &[u8], _: GuestAddress, offset: u64| -> bool { + if offset >= IVSHMEM_REG_BAR_SIZE { + error!("ivshmem: write offset {} exceeds bar0 size", offset); + return true; + } + match offset { + IVSHMEM_BAR0_IRQ_MASK | IVSHMEM_BAR0_IRQ_STATUS | IVSHMEM_BAR0_DOORBELL => {} + _ => { + if let Some(wcb) = bar0_ops.read().unwrap().write.as_ref() { + return wcb(data, offset); + } + } + } + true + }; let reg_region_ops = RegionOps { read: Arc::new(reg_read), write: Arc::new(reg_write), @@ -73,11 +123,23 @@ impl Ivshmem { self.base.config.register_bar( 0, Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops, "IvshmemIo"), - RegionType::Mem64Bit, + RegionType::Mem32Bit, false, IVSHMEM_REG_BAR_SIZE, )?; + // bar1: msix + if self.vector_nr > 0 { + init_msix( + &mut self.base, + 1, + self.vector_nr, + self.dev_id.clone(), + None, + None, + )?; + } + // bar2: ram self.base.config.register_bar( 2, @@ -87,6 +149,22 @@ impl Ivshmem { self.ram_mem_region.size(), ) } + + pub fn trigger_msix(&self, vector: u16) { + if self.vector_nr == 0 { + return; + } + if let Some(msix) = self.base.config.msix.as_ref() { + msix.lock() + .unwrap() + .notify(vector, self.dev_id.load(Ordering::Acquire)); + } + } + + pub fn set_bar0_ops(&mut self, bar0_ops: (Arc, Arc)) { + self.bar0_ops.write().unwrap().write = Some(bar0_ops.0); + self.bar0_ops.write().unwrap().read = Some(bar0_ops.1); + } } impl Device for Ivshmem { @@ -117,7 +195,9 @@ impl Device for Ivshmem { // Attach to the PCI bus. let bus = self.parent_bus().unwrap().upgrade().unwrap(); - let mut locked_bus = bus.lock().unwrap(); + PCI_BUS!(bus, locked_bus, pci_bus); + self.dev_id + .store(pci_bus.generate_dev_id(self.base.devfn), Ordering::Release); let dev = Arc::new(Mutex::new(self)); locked_bus.attach_child(dev.lock().unwrap().base.devfn as u64, dev.clone())?; Ok(dev) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 7d4a50a10..c5e11e568 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -36,7 +36,7 @@ use crate::{Bus, Device}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -use ohaudio::OhAudio; +use ohaudio::{OhAudio, OhAudioVolume}; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] @@ -49,6 +49,8 @@ pub const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; pub const TARGET_LATENCY_MS: u32 = 50; +const IVSHMEM_VECTORS_NR: u32 = 1; + // A frame of back-end audio data is 50ms, and the next frame of audio data needs // to be trained in polling within 50ms. Theoretically, the shorter the polling time, // the better. However, if the value is too small, the overhead is high. So take a @@ -543,8 +545,17 @@ impl Scream { let devfn = (self.config.addr.0 << 3) + self.config.addr.1; let mem_region = Region::init_ram_region(host_mmap, "ivshmem_ram"); - let ivshmem = Ivshmem::new("ivshmem".to_string(), devfn, parent_bus, mem_region); - ivshmem.realize()?; + let ivshmem = Ivshmem::new( + "ivshmem".to_string(), + devfn, + parent_bus, + mem_region, + IVSHMEM_VECTORS_NR, + ); + let _ivshmem = ivshmem.realize()?; + + #[cfg(target_env = "ohos")] + OhAudioVolume::init_volume_sync(_ivshmem); self.start_play_thread_fn()?; self.start_record_thread_fn() diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 1f5853056..c44d7067f 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -23,15 +23,18 @@ use std::{ time::{Duration, Instant}, }; -use log::{error, warn}; +use log::{error, info, warn}; +use crate::misc::ivshmem::Ivshmem; use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, StreamData}; +use crate::pci::{le_read_u32, le_write_u32}; use machine_manager::notifier::register_vm_pause_notifier; use util::ohos_binding::audio::*; const STREAM_DATA_VEC_CAPACITY: usize = 15; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; +const IVSHMEM_BAR0_VOLUME: u64 = 240; trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; @@ -631,6 +634,76 @@ impl AudioInterface for OhAudio { } } +pub struct OhAudioVolume { + shm_dev: Arc>, + ohos_vol: RwLock, +} + +impl GuestVolumeNotifier for OhAudioVolume { + fn notify(&self, vol: u32) { + *self.ohos_vol.write().unwrap() = vol; + self.shm_dev.lock().unwrap().trigger_msix(0); + } +} + +impl OhAudioVolume { + pub fn new(shm_dev: Arc>) -> Arc { + let vol = Arc::new(Self { + shm_dev, + ohos_vol: RwLock::new(0), + }); + register_guest_volume_notifier(vol.clone()); + vol + } + + fn set_volume(&self, vol: u32) { + set_ohos_volume(vol); + } + + fn get_volume(&self) -> u32 { + *self.ohos_vol.read().unwrap() + } + + fn init_volume(&self) { + *self.ohos_vol.write().unwrap() = get_ohos_volume(); + } + + pub fn init_volume_sync(ivshmem: Arc>) { + let ohvol = OhAudioVolume::new(ivshmem.clone()); + let ohvol2 = ohvol.clone(); + ohvol.init_volume(); + let bar0_write = Arc::new(move |data: &[u8], offset: u64| { + match offset { + IVSHMEM_BAR0_VOLUME => match le_read_u32(data, 0) { + Ok(v) => ohvol.set_volume(v), + Err(e) => error!("ohaudio le_read_u32 failed: {e}"), + }, + _ => { + info!("ivshmem-scream on ohaudio: unsupported write:{offset}"); + } + } + true + }); + let bar0_read = Arc::new(move |data: &mut [u8], offset: u64| { + match offset { + IVSHMEM_BAR0_VOLUME => { + if let Err(e) = le_write_u32(data, 0, ohvol2.get_volume()) { + error!("ohaudio le_write_u32 failed: {e}"); + } + } + _ => { + info!("ivshmem-scream on ohaudio: unsupported read:{offset}"); + } + } + true + }); + ivshmem + .lock() + .unwrap() + .set_bar0_ops((bar0_write, bar0_read)); + } +} + #[cfg(test)] mod tests { use crate::misc::scream::ohaudio::{on_read_data_cb, on_write_data_cb, StreamUnit}; diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index 4dd7687f2..e67bf4e41 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -14,9 +14,12 @@ mod sys; use std::os::raw::c_void; use std::ptr; +use std::sync::{Arc, RwLock}; use log::error; +use once_cell::sync::Lazy; +use super::hwf_adapter::{hwf_adapter_volume_api, volume::VolumeFuncTable}; use sys as capi; const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; @@ -366,6 +369,76 @@ impl AudioContext { } } +// From here, the code is related to ohaudio volume. +static OH_VOLUME_ADAPTER: Lazy> = Lazy::new(|| RwLock::new(OhVolume::new())); + +pub trait GuestVolumeNotifier: Send + Sync { + fn notify(&self, vol: u32); +} + +struct OhVolume { + capi: Arc, + notifiers: Vec>, +} + +impl OhVolume { + fn new() -> Self { + let capi = hwf_adapter_volume_api(); + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (*capi.register_volume_change)(on_ohos_volume_changed) }; + Self { + capi, + notifiers: Vec::new(), + } + } + + fn get_ohos_volume(&self) -> u32 { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.get_volume)() as u32 } + } + + fn set_ohos_volume(&self, volume: i32) { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.set_volume)(volume) }; + } + + fn notify_volume_change(&self, volume: i32) { + for notifier in self.notifiers.iter() { + notifier.notify(volume as u32); + } + } + + fn register_guest_notifier(&mut self, notifier: Arc) { + self.notifiers.push(notifier); + } +} + +// SAFETY: use RW lock to ensure the security of resources. +unsafe extern "C" fn on_ohos_volume_changed(volume: i32) { + OH_VOLUME_ADAPTER + .read() + .unwrap() + .notify_volume_change(volume); +} + +pub fn register_guest_volume_notifier(notifier: Arc) { + OH_VOLUME_ADAPTER + .write() + .unwrap() + .register_guest_notifier(notifier); +} + +pub fn get_ohos_volume() -> u32 { + OH_VOLUME_ADAPTER.read().unwrap().get_ohos_volume() +} + +pub fn set_ohos_volume(vol: u32) { + OH_VOLUME_ADAPTER + .read() + .unwrap() + .set_ohos_volume(vol as i32); +} + #[cfg(test)] mod tests { use crate::ohos_binding::audio::sys as capi; diff --git a/util/src/ohos_binding/hwf_adapter/mod.rs b/util/src/ohos_binding/hwf_adapter/mod.rs index 2709c81f3..14e4e0c24 100644 --- a/util/src/ohos_binding/hwf_adapter/mod.rs +++ b/util/src/ohos_binding/hwf_adapter/mod.rs @@ -15,6 +15,9 @@ pub mod camera; #[cfg(feature = "usb_host")] pub mod usb; +#[cfg(feature = "scream_ohaudio")] +pub mod volume; + use std::ffi::OsStr; use std::sync::Arc; @@ -27,6 +30,8 @@ use once_cell::sync::Lazy; use camera::CamFuncTable; #[cfg(feature = "usb_host")] use usb::UsbFuncTable; +#[cfg(feature = "scream_ohaudio")] +use volume::VolumeFuncTable; static LIB_HWF_ADAPTER: Lazy = Lazy::new(|| // SAFETY: The dynamic library should be always existing. @@ -46,6 +51,8 @@ struct LibHwfAdapter { camera: Arc, #[cfg(feature = "usb_host")] usb: Arc, + #[cfg(feature = "scream_ohaudio")] + volume: Arc, } impl LibHwfAdapter { @@ -63,12 +70,20 @@ impl LibHwfAdapter { UsbFuncTable::new(&library).with_context(|| "failed to init usb function table")?, ); + #[cfg(feature = "scream_ohaudio")] + let volume = Arc::new( + VolumeFuncTable::new(&library) + .with_context(|| "failed to init volume function table")?, + ); + Ok(Self { library, #[cfg(feature = "usb_camera_oh")] camera, #[cfg(feature = "usb_host")] usb, + #[cfg(feature = "scream_ohaudio")] + volume, }) } @@ -81,6 +96,11 @@ impl LibHwfAdapter { fn get_usb_api(&self) -> Arc { self.usb.clone() } + + #[cfg(feature = "scream_ohaudio")] + fn get_volume_api(&self) -> Arc { + self.volume.clone() + } } #[cfg(feature = "usb_camera_oh")] @@ -92,3 +112,8 @@ pub fn hwf_adapter_camera_api() -> Arc { pub fn hwf_adapter_usb_api() -> Arc { LIB_HWF_ADAPTER.get_usb_api() } + +#[cfg(feature = "scream_ohaudio")] +pub fn hwf_adapter_volume_api() -> Arc { + LIB_HWF_ADAPTER.get_volume_api() +} diff --git a/util/src/ohos_binding/hwf_adapter/volume.rs b/util/src/ohos_binding/hwf_adapter/volume.rs new file mode 100644 index 000000000..3de5e34bd --- /dev/null +++ b/util/src/ohos_binding/hwf_adapter/volume.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::raw::c_int; + +use anyhow::{Context, Result}; +use libloading::os::unix::Symbol as RawSymbol; +use libloading::Library; + +use crate::get_libfn; + +pub type VolumeChangedCallBack = unsafe extern "C" fn(c_int); + +type OhSysAudioGetVolumeFn = unsafe extern "C" fn() -> c_int; +type OhSysAudioSetVolumeFn = unsafe extern "C" fn(c_int); +type OhSysAudioRegisterVolumeChangeFn = unsafe extern "C" fn(VolumeChangedCallBack) -> c_int; + +pub struct VolumeFuncTable { + pub get_volume: RawSymbol, + pub set_volume: RawSymbol, + pub register_volume_change: RawSymbol, +} + +impl VolumeFuncTable { + pub unsafe fn new(library: &Library) -> Result { + Ok(Self { + get_volume: get_libfn!(library, OhSysAudioGetVolumeFn, OhSysAudioGetVolume), + set_volume: get_libfn!(library, OhSysAudioSetVolumeFn, OhSysAudioSetVolume), + register_volume_change: get_libfn!( + library, + OhSysAudioRegisterVolumeChangeFn, + OhSysAudioRegisterVolumeChange + ), + }) + } +} -- Gitee From f2d3452fbb534be89db2de91ed37df5b620e37e7 Mon Sep 17 00:00:00 2001 From: lixiang_yewu Date: Wed, 7 Aug 2024 07:25:12 +0000 Subject: [PATCH 1992/2187] update docs/trace.md. Signed-off-by: lixiang_yewu --- docs/trace.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/trace.md b/docs/trace.md index bf1c24aae..d4623fa6a 100644 --- a/docs/trace.md +++ b/docs/trace.md @@ -70,7 +70,7 @@ of settings. ### log StratoVirt supports outputting trace to the log file at trace level. Turn on -the **trace_to_logger** feature to use is. +the **trace_to_logger** feature to use it. ### Ftrace @@ -80,7 +80,7 @@ suited for performance issues. It can be enabled by turning on the **trace_to_ftrace** feature during compilation. StratoVirt use ftrace by writing trace data to ftrace marker, and developers can -read trace records from trace file under mounted ftrace director, +read trace records from trace file under mounted ftrace directory, e.g. /sys/kernel/debug/tracing/trace. ### HiTraceMeter -- Gitee From be4ec576908fe1e5b66c4d6703c0e4e7c599c77f Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Wed, 7 Aug 2024 21:10:48 +0800 Subject: [PATCH 1993/2187] io_uring: delete ASYNC for performance Without ASYNC flag, the operation for io_uring is to try and issue an sqe as non-blocking first, and if that fails, execute it in an async manner. With ASYNC flag it ask for an sqe to be issued aysnc form the start. Note that this flag immediately causes the SQE to be offloaded to an async helper thread with no initial non-blocking attempt. This may be less efficient and should not be used liberally or without understanding the performance and efficiency tradeoffs. Signed-off-by: Yan Wang --- util/src/aio/uring.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 8f95832e9..367a85eb2 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -13,7 +13,7 @@ use std::os::unix::io::AsRawFd; use anyhow::{bail, Context}; -use io_uring::{opcode, squeue, types, IoUring}; +use io_uring::{opcode, types, IoUring}; use libc; use vmm_sys_util::eventfd::EventFd; @@ -70,17 +70,12 @@ impl AioContext for IoUringContext { OpCode::Preadv => opcode::Readv::new(fd, iovs as *const libc::iovec, len as u32) .offset(offset) .build() - .flags(squeue::Flags::ASYNC) .user_data(data), OpCode::Pwritev => opcode::Writev::new(fd, iovs as *const libc::iovec, len as u32) .offset(offset) .build() - .flags(squeue::Flags::ASYNC) - .user_data(data), - OpCode::Fdsync => opcode::Fsync::new(fd) - .build() - .flags(squeue::Flags::ASYNC) .user_data(data), + OpCode::Fdsync => opcode::Fsync::new(fd).build().user_data(data), _ => { bail!("Invalid entry code"); } -- Gitee From 74d6ec8440e43b71fba0d36ff800d41459182ac7 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 8 Aug 2024 14:53:58 +0800 Subject: [PATCH 1994/2187] PCI: Add devfn for pciconfig Log the information of pci bar mapping. Signed-off-by: Jinhao Gao --- devices/src/misc/ivshmem.rs | 2 +- devices/src/misc/pvpanic.rs | 2 +- devices/src/pci/config.rs | 31 +++++++++++++++++++--------- devices/src/pci/demo_device/mod.rs | 2 +- devices/src/pci/mod.rs | 2 +- devices/src/pci/msix.rs | 4 ++-- devices/src/pci/root_port.rs | 2 +- devices/src/usb/xhci/xhci_pci.rs | 2 +- machine/src/aarch64/pci_host_root.rs | 2 +- machine/src/x86_64/ich9_lpc.rs | 2 +- machine/src/x86_64/mch.rs | 2 +- vfio/src/vfio_pci.rs | 4 ++-- virtio/src/transport/virtio_pci.rs | 2 +- 13 files changed, 35 insertions(+), 24 deletions(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 090779fa2..d4d50f36e 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -70,7 +70,7 @@ impl Ivshmem { Self { base: PciDevBase { base: DeviceBase::new(name, false, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), + config: PciConfig::new(devfn, PCI_CONFIG_SPACE_SIZE, PCI_BAR_MAX_IVSHMEM), devfn, }, dev_id: Arc::new(AtomicU16::new(0)), diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 8c8dcd064..1647a2d58 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -111,7 +111,7 @@ impl PvPanicPci { Self { base: PciDevBase { base: DeviceBase::new(config.id.clone(), false, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), + config: PciConfig::new(devfn, PCI_CONFIG_SPACE_SIZE, 1), devfn, }, dev_id: AtomicU16::new(0), diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 69976cacb..02858fe14 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, Context, Result}; -use log::{error, warn}; +use log::{error, info, warn}; use crate::pci::intx::Intx; use crate::pci::msix::{Msix, MSIX_TABLE_ENTRY_SIZE}; @@ -383,6 +383,8 @@ pub enum PcieDevType { /// Configuration space of PCI/PCIe device. #[derive(Clone)] pub struct PciConfig { + /// Device number and function number. + pub devfn: u8, /// Configuration space data. pub config: Vec, /// Mask of writable bits. @@ -412,7 +414,7 @@ impl PciConfig { /// /// * `config_size` - Configuration size in bytes. /// * `nr_bar` - Number of BARs. - pub fn new(config_size: usize, nr_bar: u8) -> Self { + pub fn new(devfn: u8, config_size: usize, nr_bar: u8) -> Self { let mut bars = Vec::new(); for _ in 0..nr_bar as usize { bars.push(Bar { @@ -426,6 +428,7 @@ impl PciConfig { } PciConfig { + devfn, config: vec![0; config_size], write_mask: vec![0; config_size], write_clear_mask: vec![0; config_size], @@ -905,6 +908,10 @@ impl PciConfig { } } + info!( + "pci dev {} delete bar {} mapping: addr 0x{:X} size {}", + self.devfn, id, self.bars[id].address, self.bars[id].size + ); trace::pci_update_mappings_del(id, self.bars[id].address, self.bars[id].size); self.bars[id].address = BAR_SPACE_UNMAPPED; } @@ -939,6 +946,10 @@ impl PciConfig { } } + info!( + "pci dev {} update bar {} mapping: addr 0x{:X} size {}", + self.devfn, id, new_addr, self.bars[id].size + ); trace::pci_update_mappings_add(id, self.bars[id].address, self.bars[id].size); self.bars[id].address = new_addr; } @@ -1209,7 +1220,7 @@ mod tests { #[test] fn test_find_pci_cap() { - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); + let mut pci_config = PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 3); let offset = pci_config.find_pci_cap(MSIX_CAP_ID); assert_eq!(offset, 0xff); @@ -1239,7 +1250,7 @@ mod tests { write: Arc::new(write_ops), }; let region = Region::init_io_region(8192, region_ops.clone(), "io"); - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); + let mut pci_config = PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 3); #[cfg(target_arch = "x86_64")] assert!(pci_config @@ -1316,7 +1327,7 @@ mod tests { write: Arc::new(write_ops), }; let region = Region::init_io_region(8192, region_ops, "io"); - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 6); + let mut pci_config = PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 6); #[cfg(target_arch = "x86_64")] assert!(pci_config @@ -1413,7 +1424,7 @@ mod tests { #[test] fn test_add_pci_cap() { - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2); + let mut pci_config = PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 2); // Overflow. assert!(pci_config @@ -1430,7 +1441,7 @@ mod tests { #[test] fn test_add_pcie_ext_cap() { - let mut pci_config = PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2); + let mut pci_config = PciConfig::new(0, PCIE_CONFIG_SPACE_SIZE, 2); // Overflow. assert!(pci_config @@ -1451,7 +1462,7 @@ mod tests { #[test] fn test_get_ext_cap_size() { - let mut pcie_config = PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 3); + let mut pcie_config = PciConfig::new(0, PCIE_CONFIG_SPACE_SIZE, 3); let offset1 = pcie_config.add_pcie_ext_cap(1, 0x10, 1).unwrap(); let offset2 = pcie_config.add_pcie_ext_cap(1, 0x40, 1).unwrap(); pcie_config.add_pcie_ext_cap(1, 0x20, 1).unwrap(); @@ -1464,7 +1475,7 @@ mod tests { #[test] fn test_reset_common_regs() { - let mut pcie_config = PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 3); + let mut pcie_config = PciConfig::new(0, PCIE_CONFIG_SPACE_SIZE, 3); pcie_config.init_common_write_mask().unwrap(); pcie_config.init_common_write_clear_mask().unwrap(); @@ -1489,7 +1500,7 @@ mod tests { write: Arc::new(write_ops), }; let region = Region::init_io_region(4096, region_ops, "io"); - let mut pci_config = PciConfig::new(PCI_CONFIG_SPACE_SIZE, 3); + let mut pci_config = PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 3); // bar is unmapped #[cfg(target_arch = "x86_64")] diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index 58a88c7df..b4163f7aa 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -103,7 +103,7 @@ impl DemoDev { DemoDev { base: PciDevBase { base: DeviceBase::new(cfg.id.clone(), false, Some(parent_bus)), - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), + config: PciConfig::new(devfn, PCIE_CONFIG_SPACE_SIZE, cfg.bar_num), devfn, }, cmd_cfg: cfg, diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index ccac50e50..ccf1263a2 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -411,7 +411,7 @@ mod tests { Self { base: PciDevBase { base: DeviceBase::new(name.to_string(), false, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + config: PciConfig::new(devfn, PCI_CONFIG_SPACE_SIZE, 0), devfn, }, } diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 70eb603d7..7f960f44d 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -646,7 +646,7 @@ mod tests { let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let mut base = PciDevBase { base: DeviceBase::new("msix".to_string(), false, Some(root_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), + config: PciConfig::new(1, PCI_CONFIG_SPACE_SIZE, 2), devfn: 1, }; // Too many vectors. @@ -751,7 +751,7 @@ mod tests { let root_bus = Arc::downgrade(&locked_pci_host.child_bus().unwrap()); let mut base = PciDevBase { base: DeviceBase::new("msix".to_string(), false, Some(root_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 2), + config: PciConfig::new(1, PCI_CONFIG_SPACE_SIZE, 2), devfn: 1, }; init_msix(&mut base, 0, 2, Arc::new(AtomicU16::new(0)), None, None).unwrap(); diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 0d854c080..00bffd491 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -126,7 +126,7 @@ impl RootPort { Self { base: PciDevBase { base: dev_base, - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, 2), + config: PciConfig::new(devfn, PCIE_CONFIG_SPACE_SIZE, 2), devfn, }, port_num: cfg.port, diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 07b2bb2b3..602cf136b 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -114,7 +114,7 @@ impl XhciPciDevice { Self { base: PciDevBase { base: DeviceBase::new(config.id.clone().unwrap(), true, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 1), + config: PciConfig::new(devfn, PCI_CONFIG_SPACE_SIZE, 1), devfn, }, xhci: XhciDevice::new(mem_space, config), diff --git a/machine/src/aarch64/pci_host_root.rs b/machine/src/aarch64/pci_host_root.rs index b3950f24a..65ec778f8 100644 --- a/machine/src/aarch64/pci_host_root.rs +++ b/machine/src/aarch64/pci_host_root.rs @@ -34,7 +34,7 @@ impl PciHostRoot { Self { base: PciDevBase { base: DeviceBase::new("PCI Host Root".to_string(), false, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + config: PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, }, } diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index bb9f0e8ff..b27b8276b 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -65,7 +65,7 @@ impl LPCBridge { Ok(Self { base: PciDevBase { base: DeviceBase::new("ICH9 LPC bridge".to_string(), false, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + config: PciConfig::new(0x1F << 3, PCI_CONFIG_SPACE_SIZE, 0), devfn: 0x1F << 3, }, sys_io, diff --git a/machine/src/x86_64/mch.rs b/machine/src/x86_64/mch.rs index 0aec6ec89..13a47cdf2 100644 --- a/machine/src/x86_64/mch.rs +++ b/machine/src/x86_64/mch.rs @@ -58,7 +58,7 @@ impl Mch { Self { base: PciDevBase { base: DeviceBase::new("Memory Controller Hub".to_string(), false, Some(parent_bus)), - config: PciConfig::new(PCI_CONFIG_SPACE_SIZE, 0), + config: PciConfig::new(0, PCI_CONFIG_SPACE_SIZE, 0), devfn: 0, }, mmconfig_region: Some(mmconfig_region), diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index d52ea581c..5dad81631 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -133,7 +133,7 @@ impl VfioPciDevice { // Unknown PCI or PCIe type here, allocate enough space to match the two types. base: PciDevBase { base: DeviceBase::new(name, true, Some(parent_bus)), - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), + config: PciConfig::new(devfn, PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS), devfn, }, config_size: 0, @@ -185,7 +185,7 @@ impl VfioPciDevice { // Cache the pci config space to avoid overwriting the original config space. Because we // will parse the chain of extended caps in cache config and insert them into original // config space. - let mut config = PciConfig::new(PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS); + let mut config = PciConfig::new(self.base.devfn, PCIE_CONFIG_SPACE_SIZE, PCI_NUM_BARS); config.config = config_data; let mut next = PCI_CONFIG_SPACE_SIZE; while (PCI_CONFIG_SPACE_SIZE..PCIE_CONFIG_SPACE_SIZE).contains(&next) { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index ca392dde0..149cb329f 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -333,7 +333,7 @@ impl VirtioPciDevice { VirtioPciDevice { base: PciDevBase { base: DeviceBase::new(name, true, Some(parent_bus)), - config: PciConfig::new(PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), + config: PciConfig::new(devfn, PCIE_CONFIG_SPACE_SIZE, VIRTIO_PCI_BAR_MAX), devfn, }, device, -- Gitee From f87e741dafb6599d553fd906898cd3ddf187282c Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 8 Aug 2024 15:08:59 +0800 Subject: [PATCH 1995/2187] Trace: Delete the pci trace of updating bar mapping The information of updating bar mapping is logged. So don't need to trace. Signed-off-by: Jinhao Gao --- devices/src/pci/config.rs | 4 ++-- trace/trace_info/pci.toml | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 02858fe14..5228e3376 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -912,7 +912,7 @@ impl PciConfig { "pci dev {} delete bar {} mapping: addr 0x{:X} size {}", self.devfn, id, self.bars[id].address, self.bars[id].size ); - trace::pci_update_mappings_del(id, self.bars[id].address, self.bars[id].size); + self.bars[id].address = BAR_SPACE_UNMAPPED; } @@ -950,7 +950,7 @@ impl PciConfig { "pci dev {} update bar {} mapping: addr 0x{:X} size {}", self.devfn, id, new_addr, self.bars[id].size ); - trace::pci_update_mappings_add(id, self.bars[id].address, self.bars[id].size); + self.bars[id].address = new_addr; } } diff --git a/trace/trace_info/pci.toml b/trace/trace_info/pci.toml index dfd7642fa..be76599f3 100644 --- a/trace/trace_info/pci.toml +++ b/trace/trace_info/pci.toml @@ -15,15 +15,3 @@ name = "msix_write_config" args = "dev_id: u16, masked: bool, enabled: bool" message = "dev id: {} masked: {} enabled: {}" enabled = true - -[[events]] -name = "pci_update_mappings_add" -args = "bar_id: usize, addr: u64, size: u64" -message = "bar id: {} addr: 0x{:#X} size: {}" -enabled = true - -[[events]] -name = "pci_update_mappings_del" -args = "bar_id: usize, addr: u64, size: u64" -message = "bar id: {} addr: 0x{:#X} size: {}" -enabled = true -- Gitee From a07708403198228f1d9ecba94ee94635d7529c26 Mon Sep 17 00:00:00 2001 From: zephyr Date: Thu, 8 Aug 2024 22:01:45 +0800 Subject: [PATCH 1996/2187] Ohaudio: pause play and record threads if vm paused Pause play and record threads if vm paused. --- devices/src/misc/scream/mod.rs | 46 ++++++++- devices/src/misc/scream/ohaudio.rs | 154 ++++++----------------------- 2 files changed, 76 insertions(+), 124 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index c5e11e568..b0821164c 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -20,7 +20,7 @@ mod pulseaudio; use std::str::FromStr; use std::sync::atomic::{fence, Ordering}; -use std::sync::{Arc, Mutex, RwLock, Weak}; +use std::sync::{Arc, Condvar, Mutex, RwLock, Weak}; use std::{mem, thread}; use anyhow::{anyhow, bail, Context, Result}; @@ -35,6 +35,7 @@ use super::ivshmem::Ivshmem; use crate::{Bus, Device}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; +use machine_manager::notifier::register_vm_pause_notifier; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohaudio::{OhAudio, OhAudioVolume}; #[cfg(feature = "scream_pulseaudio")] @@ -59,6 +60,17 @@ const POLL_DELAY_US: u64 = (TARGET_LATENCY_MS as u64) * 1000 / 8; pub const SCREAM_MAGIC: u64 = 0x02032023; +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd)] +pub enum AudioStatus { + // Processor is ready and waiting for play/capture. + #[default] + Ready, + // Processor is started and doing job. + Started, + // OH audio framework error occurred. + Error, +} + /// The scream device defines the audio directions. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ScreamDirection { @@ -191,6 +203,10 @@ pub struct StreamData { pub audio_size: u32, /// Location of the played or recorded audio data in the shared memory. pub audio_base: u64, + /// used for VM pause + cond: Arc, + paused: Arc>, + pause_notifier_id: u64, } impl StreamData { @@ -200,6 +216,27 @@ impl StreamData { self.chunk_idx = header.chunk_idx; } + #[inline(always)] + fn wait_if_paused(&mut self, interface: Arc>) { + let mut locked_pause = self.paused.lock().unwrap(); + while *locked_pause { + interface.lock().unwrap().destroy(); + locked_pause = self.cond.wait(locked_pause).unwrap(); + } + } + + fn register_pause_notifier(&mut self) { + let cond = self.cond.clone(); + let cond_pause = self.paused.clone(); + let pause_notify = Arc::new(move |paused: bool| { + *cond_pause.lock().unwrap() = paused; + if !paused { + cond.notify_all(); + } + }); + self.pause_notifier_id = register_vm_pause_notifier(pause_notify); + } + fn wait_for_ready( &mut self, interface: Arc>, @@ -221,6 +258,7 @@ impl StreamData { if header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { interface.lock().unwrap().destroy(); while header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { + self.wait_if_paused(interface.clone()); thread::sleep(time::Duration::from_millis(10)); header = // SAFETY: hva is allocated by libc:::mmap, it can be guaranteed to be legal. @@ -293,6 +331,8 @@ impl StreamData { let play = &header.play; loop { + self.wait_if_paused(interface.clone()); + if play.fmt.fmt_generation != self.fmt.fmt_generation { break; } @@ -337,6 +377,8 @@ impl StreamData { interface.lock().unwrap().pre_receive(addr, capt); while capt.is_started != 0 { + self.wait_if_paused(interface.clone()); + if !self.update_buffer_by_chunk_idx(hva, shmem_size, capt) { return; } @@ -483,6 +525,7 @@ impl Scream { .spawn(move || { let clone_interface = interface.clone(); let mut play_data = StreamData::default(); + play_data.register_pause_notifier(); loop { play_data.wait_for_ready( @@ -510,6 +553,7 @@ impl Scream { .spawn(move || { let clone_interface = interface.clone(); let mut capt_data = StreamData::default(); + capt_data.register_pause_notifier(); loop { capt_data.wait_for_ready( diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index c44d7067f..cea3b21fd 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -14,7 +14,7 @@ use std::collections::VecDeque; use std::os::raw::c_void; use std::sync::{ atomic::{fence, AtomicBool, AtomicI32, Ordering}, - Arc, Mutex, RwLock, + Arc, Mutex, }; use std::{ cmp, @@ -26,7 +26,9 @@ use std::{ use log::{error, info, warn}; use crate::misc::ivshmem::Ivshmem; -use crate::misc::scream::{AudioInterface, ScreamDirection, ShmemStreamHeader, StreamData}; +use crate::misc::scream::{ + AudioInterface, AudioStatus, ScreamDirection, ShmemStreamHeader, StreamData, +}; use crate::pci::{le_read_u32, le_write_u32}; use machine_manager::notifier::register_vm_pause_notifier; use util::ohos_binding::audio::*; @@ -162,26 +164,12 @@ impl StreamQueue { } } -#[derive(Copy, Clone, Default, PartialEq, PartialOrd)] -enum OhAudioStatus { - // Processor is ready and waiting for play/capture. - #[default] - Ready, - // Processor is started and doing job. - Started, - // Processor is paused. - Paused, - // OH audio framework error occurred. - Error, -} - struct OhAudioRender { ctx: Option, stream_data: Arc>, flushing: AtomicBool, - status: Arc>, + status: AudioStatus, last_called_time: Option, - pause_notifier_id: u64, } impl Default for OhAudioRender { @@ -190,9 +178,8 @@ impl Default for OhAudioRender { ctx: None, stream_data: Arc::new(Mutex::new(StreamQueue::new(STREAM_DATA_VEC_CAPACITY))), flushing: AtomicBool::new(false), - status: Arc::new(RwLock::new(OhAudioStatus::default())), + status: AudioStatus::default(), last_called_time: None, - pause_notifier_id: 0, } } } @@ -235,32 +222,6 @@ impl OhAudioRender { fn set_flushing(&mut self, flush: bool) { self.flushing.store(flush, Ordering::Release); } - - #[inline(always)] - fn get_status(&self) -> OhAudioStatus { - *self.status.read().unwrap() - } - - #[inline(always)] - fn set_status(&self, status: OhAudioStatus) { - *self.status.write().unwrap() = status; - } - - fn register_pause_notifier(&mut self) { - let status = self.status.clone(); - let pause_notify = Arc::new(move |paused: bool| { - let s = *status.read().unwrap(); - if paused { - if s == OhAudioStatus::Paused { - return; - } - *status.write().unwrap() = OhAudioStatus::Paused; - } else { - *status.write().unwrap() = OhAudioStatus::Error; - } - }); - self.pause_notifier_id = register_vm_pause_notifier(pause_notify); - } } impl OhAudioProcess for OhAudioRender { @@ -283,7 +244,7 @@ impl OhAudioProcess for OhAudioRender { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { - self.set_status(OhAudioStatus::Started); + self.status = AudioStatus::Started; trace::oh_scream_render_init(&self.ctx); } Err(e) => { @@ -291,35 +252,27 @@ impl OhAudioProcess for OhAudioRender { } } self.last_called_time = None; - self.get_status() == OhAudioStatus::Started + self.status == AudioStatus::Started } fn destroy(&mut self) { - let status = self.get_status(); - - match status { - OhAudioStatus::Paused => return, - OhAudioStatus::Error => { - self.set_status(OhAudioStatus::Ready); + match self.status { + AudioStatus::Error => { self.ctx = None; + self.status = AudioStatus::Ready; return; } - OhAudioStatus::Started => self.flush(), + AudioStatus::Started => self.flush(), _ => {} } self.ctx = None; self.stream_data.lock().unwrap().clear(); self.set_flushing(false); - self.set_status(OhAudioStatus::Ready); + self.status = AudioStatus::Ready; trace::oh_scream_render_destroy(); } fn process(&mut self, recv_data: &StreamData) -> i32 { - let mut status = self.get_status(); - if status == OhAudioStatus::Paused { - return 0; - } - self.check_fmt_update(recv_data); fence(Ordering::Acquire); @@ -331,13 +284,12 @@ impl OhAudioProcess for OhAudioRender { recv_data.audio_size as usize, )); - if status == OhAudioStatus::Error { + if self.status == AudioStatus::Error { error!("Audio server error occurred. Destroy and reconnect it."); self.destroy(); - status = self.get_status(); } - if status == OhAudioStatus::Ready && !self.init(recv_data) { + if self.status == AudioStatus::Ready && !self.init(recv_data) { error!("failed to init oh audio"); self.destroy(); } @@ -353,9 +305,8 @@ struct OhAudioCapture { shm_addr: u64, shm_len: u64, cur_pos: u64, - status: Arc>, + status: AudioStatus, last_called_time: Option, - pause_notifier_id: u64, } impl OhAudioCapture { @@ -370,33 +321,6 @@ impl OhAudioCapture { self.destroy(); } } - - #[inline(always)] - fn get_status(&self) -> OhAudioStatus { - *self.status.write().unwrap() - } - - #[inline(always)] - fn set_status(&self, status: OhAudioStatus) { - *self.status.write().unwrap() = status; - } - - fn register_pause_notifier(&mut self) { - let status = self.status.clone(); - let pause_notify = Arc::new(move |paused: bool| { - let s = *status.read().unwrap(); - if paused { - if s == OhAudioStatus::Paused { - return; - } - *status.write().unwrap() = OhAudioStatus::Paused; - } else { - // Set error status to recreate capture context. - *status.write().unwrap() = OhAudioStatus::Error; - } - }); - self.pause_notifier_id = register_vm_pause_notifier(pause_notify); - } } impl OhAudioProcess for OhAudioCapture { @@ -418,7 +342,7 @@ impl OhAudioProcess for OhAudioCapture { match self.ctx.as_ref().unwrap().start() { Ok(()) => { self.last_called_time = None; - self.set_status(OhAudioStatus::Started); + self.status = AudioStatus::Started; trace::oh_scream_capture_init(&self.ctx); true } @@ -430,14 +354,8 @@ impl OhAudioProcess for OhAudioCapture { } fn destroy(&mut self) { - let status = self.get_status(); - match status { - OhAudioStatus::Paused => return, - _ => { - self.set_status(OhAudioStatus::Ready); - self.ctx = None; - } - } + self.status = AudioStatus::Ready; + self.ctx = None; trace::oh_scream_capture_destroy(); } @@ -450,27 +368,21 @@ impl OhAudioProcess for OhAudioCapture { } fn process(&mut self, recv_data: &StreamData) -> i32 { - let mut status = self.get_status(); - if status == OhAudioStatus::Paused { - return -1; - } self.check_fmt_update(recv_data); trace::trace_scope_start!(ohaudio_capturer_process, args = (recv_data)); - if status == OhAudioStatus::Error { + if self.status == AudioStatus::Error { self.destroy(); - status = self.get_status(); } - if status == OhAudioStatus::Ready && !self.init(recv_data) { + if self.status == AudioStatus::Ready && !self.init(recv_data) { self.destroy(); return -1; } self.new_chunks.store(0, Ordering::Release); while self.new_chunks.load(Ordering::Acquire) == 0 { - status = self.get_status(); - if status == OhAudioStatus::Paused || status == OhAudioStatus::Error { + if self.status == AudioStatus::Error { return -1; } thread::sleep(Duration::from_millis(10)); @@ -499,7 +411,7 @@ extern "C" fn on_write_data_cb( let elapsed = last.elapsed().as_millis(); if elapsed >= 1000 { warn!("{elapsed}ms elapsed after last on_write called. Will restart render."); - render.set_status(OhAudioStatus::Error); + render.status = AudioStatus::Error; return 0; } render.last_called_time = Some(Instant::now()); @@ -542,7 +454,7 @@ extern "C" fn on_read_data_cb( let elapsed = last.elapsed().as_millis(); if elapsed >= 1000 { warn!("{elapsed}ms elapsed after last on_read called. Will restart capture."); - capture.set_status(OhAudioStatus::Error); + capture.status = AudioStatus::Error; return 0; } capture.last_called_time = Some(Instant::now()); @@ -552,7 +464,7 @@ extern "C" fn on_read_data_cb( trace::trace_scope_start!(ohaudio_read_cb, args = (length)); loop { - if capture.get_status() != OhAudioStatus::Started { + if capture.status != AudioStatus::Started { return 0; } if capture.new_chunks.load(Ordering::Acquire) == 0 { @@ -602,16 +514,12 @@ unsafe impl Send for OhAudio {} impl OhAudio { pub fn init(dir: ScreamDirection) -> Self { match dir { - ScreamDirection::Playback => { - let mut processor = Box::::default(); - processor.register_pause_notifier(); - Self { processor } - } - ScreamDirection::Record => { - let mut processor = Box::::default(); - processor.register_pause_notifier(); - Self { processor } - } + ScreamDirection::Playback => Self { + processor: Box::::default(), + }, + ScreamDirection::Record => Self { + processor: Box::::default(), + }, } } } -- Gitee From 8dda5f874edace05e95977c442c7a35c685c7cb8 Mon Sep 17 00:00:00 2001 From: zephyr Date: Thu, 11 Jul 2024 14:09:57 +0800 Subject: [PATCH 1997/2187] Query_state: add state_query module to enable state query from a qmp command A new module state_query is introduced to allow other modules to register state query callback functions in this module, and then use the newly introduced qmp command to perform status queries of different modules. --- machine/src/standard_common/mod.rs | 17 +++++++ machine_manager/src/lib.rs | 1 + machine_manager/src/machine.rs | 8 ++++ machine_manager/src/qmp/qmp_schema.rs | 18 ++++++- machine_manager/src/qmp/qmp_socket.rs | 3 +- machine_manager/src/state_query.rs | 69 +++++++++++++++++++++++++++ 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 machine_manager/src/state_query.rs diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index ebf86b189..4af265d9a 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -25,6 +25,7 @@ use std::u64; use anyhow::{bail, Context, Result}; use log::{error, warn}; +use serde_json::json; use util::set_termi_canon_mode; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; @@ -73,6 +74,7 @@ use machine_manager::machine::{ }; use machine_manager::qmp::qmp_schema::{BlockDevAddArgument, UpdateRegionArgument}; use machine_manager::qmp::{qmp_channel::QmpChannel, qmp_response::Response, qmp_schema}; +use machine_manager::state_query::query_workloads; #[cfg(feature = "gtk")] use ui::gtk::qmp_query_display_image; use ui::input::{input_button, input_move_abs, input_point_sync, key_event, Axis}; @@ -2029,6 +2031,21 @@ impl DeviceInterface for StdMachine { ), } } + + fn query_workloads(&self) -> Response { + let workloads = query_workloads(); + + if !workloads.is_empty() { + let status = workloads + .iter() + .map(|(module, state)| json!({ "module": module, "state": state })) + .collect(); + + Response::create_response(serde_json::Value::Array(status), None) + } else { + Response::create_empty_response() + } + } } fn parse_blockdev(args: &BlockDevAddArgument) -> Result { diff --git a/machine_manager/src/lib.rs b/machine_manager/src/lib.rs index b1475fce2..cab6b8806 100644 --- a/machine_manager/src/lib.rs +++ b/machine_manager/src/lib.rs @@ -30,6 +30,7 @@ pub mod notifier; pub mod qmp; pub mod signal_handler; pub mod socket; +pub mod state_query; pub mod temp_cleaner; pub mod test_server; diff --git a/machine_manager/src/machine.rs b/machine_manager/src/machine.rs index a040d6efa..bc4a38ec9 100644 --- a/machine_manager/src/machine.rs +++ b/machine_manager/src/machine.rs @@ -237,6 +237,14 @@ pub trait DeviceInterface { /// Query display of stratovirt. fn query_display_image(&self) -> Response; + /// Query state. + fn query_workloads(&self) -> Response { + Response::create_error_response( + QmpErrorClass::GenericError("query_workloads not supported for VM".to_string()), + None, + ) + } + /// Set balloon's size. fn balloon(&self, size: u64) -> Response; diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index 4281624fc..d520bd6ab 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -137,7 +137,8 @@ define_qmp_command_enum!( blockdev_snapshot_delete_internal_sync("blockdev-snapshot-delete-internal-sync", blockdev_snapshot_internal, FALSE), query_vcpu_reg("query-vcpu-reg", query_vcpu_reg, FALSE), trace_get_state("trace-get-state", trace_get_state, FALSE), - trace_set_state("trace-set-state", trace_set_state, FALSE) + trace_set_state("trace-set-state", trace_set_state, FALSE), + query_workloads("query-workloads", query_workloads, FALSE) ); /// Command trait for Deserialize and find back Response. @@ -1986,6 +1987,21 @@ pub struct trace_set_state { } pub type TraceSetArgument = trace_set_state; +/// query_workloads +/// +/// Query the current workloads of the running VM. +/// +/// # Examples +/// +/// ```text +/// -> {"execute": "query-workloads", "arguments": {}} +/// <- {"return":[{"module":"scream-play","state":"Off"},{"module":"tap-0","state":"upload: 0 download: 0"}]} +/// ``` +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct query_workloads {} +generate_command_impl!(query_workloads, Empty); + #[cfg(test)] mod tests { use super::*; diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 8326e7cd9..24993aa22 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -466,7 +466,8 @@ fn qmp_command_exec( (query_vnc, query_vnc), (query_display_image, query_display_image), (list_type, list_type), - (query_hotpluggable_cpus, query_hotpluggable_cpus); + (query_hotpluggable_cpus, query_hotpluggable_cpus), + (query_workloads, query_workloads); (input_event, input_event, key, value), (device_list_properties, device_list_properties, typename), (device_del, device_del, id), diff --git a/machine_manager/src/state_query.rs b/machine_manager/src/state_query.rs new file mode 100644 index 000000000..e5c0dd9f4 --- /dev/null +++ b/machine_manager/src/state_query.rs @@ -0,0 +1,69 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +use log::error; +use once_cell::sync::Lazy; + +static STATE_QUERY_MANAGER: Lazy> = + Lazy::new(|| RwLock::new(StateQueryManager::new())); + +pub type StateQueryCallback = dyn Fn() -> String + Send + Sync; + +struct StateQueryManager { + query_callbacks: HashMap>, +} + +impl StateQueryManager { + fn new() -> Self { + Self { + query_callbacks: HashMap::new(), + } + } + + fn register_query_callback(&mut self, key: String, callback: Arc) { + self.query_callbacks.insert(key, callback); + } + + fn unregister_query_callback(&mut self, key: &str) { + if self.query_callbacks.remove(key).is_none() { + error!("There is no query callback with key {}", key); + } + } + + fn query_workloads(&self) -> Vec<(String, String)> { + self.query_callbacks + .iter() + .map(|(module, query)| (module.clone(), query())) + .collect() + } +} + +pub fn register_state_query_callback(key: String, callback: Arc) { + STATE_QUERY_MANAGER + .write() + .unwrap() + .register_query_callback(key, callback); +} + +pub fn unregister_state_query_callback(key: &str) { + STATE_QUERY_MANAGER + .write() + .unwrap() + .unregister_query_callback(key); +} + +pub fn query_workloads() -> Vec<(String, String)> { + STATE_QUERY_MANAGER.read().unwrap().query_workloads() +} -- Gitee From 089cc63c32b893b9231f5ee3ab50f7fab3d43c96 Mon Sep 17 00:00:00 2001 From: zephyr Date: Fri, 9 Aug 2024 09:38:41 +0800 Subject: [PATCH 1998/2187] Scream: add a get_status function for trait AudioInterface Add a get_status function for trait AudioInterface so as to facilitate queries in other modules. --- devices/src/misc/scream/alsa.rs | 12 ++++++++++-- devices/src/misc/scream/audio_demo.rs | 6 +++++- devices/src/misc/scream/mod.rs | 1 + devices/src/misc/scream/ohaudio.rs | 13 +++++++++++++ devices/src/misc/scream/pulseaudio.rs | 16 ++++++++++++---- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index e3ca324cb..7daaee0fc 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -24,8 +24,8 @@ use anyhow::Result; use log::{debug, error, warn}; use super::{ - AudioInterface, ScreamDirection, ShmemStreamFmt, StreamData, AUDIO_SAMPLE_RATE_44KHZ, - TARGET_LATENCY_MS, + AudioInterface, AudioStatus, ScreamDirection, ShmemStreamFmt, StreamData, + AUDIO_SAMPLE_RATE_44KHZ, TARGET_LATENCY_MS, }; const MAX_CHANNELS: u8 = 8; @@ -283,4 +283,12 @@ impl AudioInterface for AlsaStreamData { self.init = false; } + + fn get_status(&self) -> AudioStatus { + if self.init { + AudioStatus::Started + } else { + AudioStatus::Ready + } + } } diff --git a/devices/src/misc/scream/audio_demo.rs b/devices/src/misc/scream/audio_demo.rs index d22b7bc32..3576c43f3 100644 --- a/devices/src/misc/scream/audio_demo.rs +++ b/devices/src/misc/scream/audio_demo.rs @@ -19,7 +19,7 @@ use std::{ use core::time; use log::error; -use super::{AudioInterface, ScreamDirection, StreamData}; +use super::{AudioInterface, AudioStatus, ScreamDirection, StreamData}; pub struct AudioDemo { file: File, @@ -86,4 +86,8 @@ impl AudioInterface for AudioDemo { } fn destroy(&mut self) {} + + fn get_status(&self) -> AudioStatus { + AudioStatus::Started + } } diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index b0821164c..cf8ca2ecd 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -613,4 +613,5 @@ pub trait AudioInterface: Send { fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) {} fn receive(&mut self, recv_data: &StreamData) -> i32; fn destroy(&mut self); + fn get_status(&self) -> AudioStatus; } diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index cea3b21fd..3dbf2822e 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -43,6 +43,7 @@ trait OhAudioProcess { fn destroy(&mut self); fn preprocess(&mut self, _start_addr: u64, _sh_header: &ShmemStreamHeader) {} fn process(&mut self, recv_data: &StreamData) -> i32; + fn get_status(&self) -> AudioStatus; } #[derive(Debug, Clone, Copy)] @@ -295,6 +296,10 @@ impl OhAudioProcess for OhAudioRender { } 0 } + + fn get_status(&self) -> AudioStatus { + self.status + } } #[derive(Default)] @@ -390,6 +395,10 @@ impl OhAudioProcess for OhAudioCapture { self.new_chunks.load(Ordering::Acquire) } + + fn get_status(&self) -> AudioStatus { + self.status + } } extern "C" fn on_write_data_cb( @@ -540,6 +549,10 @@ impl AudioInterface for OhAudio { fn destroy(&mut self) { self.processor.destroy(); } + + fn get_status(&self) -> AudioStatus { + self.processor.get_status() + } } pub struct OhAudioVolume { diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index c42fabacd..2e90bfbbd 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -22,7 +22,7 @@ use pulse::{ time::MicroSeconds, }; -use super::{AudioInterface, AUDIO_SAMPLE_RATE_44KHZ}; +use super::{AudioInterface, AudioStatus, AUDIO_SAMPLE_RATE_44KHZ}; use crate::misc::scream::{ScreamDirection, ShmemStreamFmt, StreamData, TARGET_LATENCY_MS}; const MAX_LATENCY_MS: u32 = 100; @@ -215,9 +215,9 @@ impl PulseStreamData { .map_or_else( |_| { warn!( - "Unable to open PulseAudio with sample rate {}, sample size {} and channels {}", - self.ss.rate, recv_data.fmt.size, recv_data.fmt.channels - ); + "Unable to open PulseAudio with sample rate {}, sample size {} and channels {}", + self.ss.rate, recv_data.fmt.size, recv_data.fmt.channels + ); None }, Some, @@ -289,6 +289,14 @@ impl AudioInterface for PulseStreamData { } self.simple = None; } + + fn get_status(&self) -> AudioStatus { + if self.simple.is_some() { + AudioStatus::Started + } else { + AudioStatus::Ready + } + } } #[cfg(test)] -- Gitee From b027abd8458b1ed7f66e6cb0e5c254f636cd4437 Mon Sep 17 00:00:00 2001 From: zephyr Date: Thu, 8 Aug 2024 14:18:11 +0800 Subject: [PATCH 1999/2187] Scream: register a state query callback function A callback function for query scream status is registered in STATE_QUERY_MANAGER. --- devices/src/misc/scream/mod.rs | 15 +++++++++++++++ devices/src/misc/scream/ohaudio.rs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index cf8ca2ecd..bc8b7b8b9 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -36,6 +36,7 @@ use crate::{Bus, Device}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; use machine_manager::notifier::register_vm_pause_notifier; +use machine_manager::state_query::register_state_query_callback; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] use ohaudio::{OhAudio, OhAudioVolume}; #[cfg(feature = "scream_pulseaudio")] @@ -520,6 +521,8 @@ impl Scream { let shmem_size = self.size; let interface = self.interface_init("ScreamPlay", ScreamDirection::Playback); self.interface_resource.push(interface.clone()); + let cloned_interface = interface.clone(); + self.register_state_query("scream-play".to_string(), cloned_interface); thread::Builder::new() .name("scream audio play worker".to_string()) .spawn(move || { @@ -548,6 +551,8 @@ impl Scream { let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); let _ti = self.token_id.clone(); self.interface_resource.push(interface.clone()); + let cloned_interface = interface.clone(); + self.register_state_query("scream-record".to_string(), cloned_interface); thread::Builder::new() .name("scream audio capt worker".to_string()) .spawn(move || { @@ -575,6 +580,16 @@ impl Scream { Ok(()) } + fn register_state_query(&self, module: String, interface: Arc>) { + register_state_query_callback( + module, + Arc::new(move || match interface.lock().unwrap().get_status() { + AudioStatus::Started => "On".to_string(), + _ => "Off".to_string(), + }), + ); + } + pub fn realize(&mut self, parent_bus: Weak>) -> Result<()> { let host_mmap = Arc::new(HostMemMapping::new( GuestAddress(0), diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 3dbf2822e..56b564476 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -14,7 +14,7 @@ use std::collections::VecDeque; use std::os::raw::c_void; use std::sync::{ atomic::{fence, AtomicBool, AtomicI32, Ordering}, - Arc, Mutex, + Arc, Mutex, RwLock, }; use std::{ cmp, -- Gitee From 2b114faeb736d802f691896ca144d6b2bcb36931 Mon Sep 17 00:00:00 2001 From: zephyr Date: Tue, 30 Jul 2024 09:17:38 +0800 Subject: [PATCH 2000/2187] Net: add upload and download count, and register a state query callback function A upload count and a download count are added to Tap to record the net use status, and the callback function for query status is registered in virtio-net module through STATE_QUERY_MANAGER. --- docs/qmp.md | 11 +++++++++++ util/src/tap.rs | 14 +++++++++++++- virtio/src/device/net.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/qmp.md b/docs/qmp.md index e4837f587..5e5e25e9f 100644 --- a/docs/qmp.md +++ b/docs/qmp.md @@ -534,6 +534,17 @@ Query the display image of virtiogpu. Currently only stdvm and gtk supports. <- { "return": { "fileDir": "/tmp/stratovirt-images", "isSuccess": true } } ``` +### query-workloads + +Query the workloads of the vm. + +#### Example + +```json +-> {"execute": "query-workloads", "arguments": {}} +<- {"return":[{"module":"scream-play","state":"Off"},{"module":"tap-0","state":"upload: 0 download: 0"}]} +``` + ### trace-get-state Query whether the trace state is enabled. diff --git a/util/src/tap.rs b/util/src/tap.rs index 1c8039cfc..462de2890 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -14,7 +14,10 @@ use std::fs::{File, OpenOptions}; use std::io::{ErrorKind, Read, Result as IoResult, Write}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::sync::Arc; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, +}; use anyhow::{anyhow, bail, Context, Result}; use log::error; @@ -57,6 +60,8 @@ pub struct IfReq { pub struct Tap { pub file: Arc, pub enabled: bool, + pub upload_stats: Arc, + pub download_stats: Arc, } impl Tap { @@ -127,6 +132,8 @@ impl Tap { Ok(Tap { file: Arc::new(file), enabled: true, + upload_stats: Arc::new(AtomicU64::new(0)), + download_stats: Arc::new(AtomicU64::new(0)), }) } @@ -214,6 +221,8 @@ impl Tap { } } error!("Failed to call readv for net handle_rx: {:?}", e); + } else { + self.download_stats.fetch_add(size as u64, Ordering::SeqCst); } size @@ -237,7 +246,10 @@ impl Tap { // Ignore other errors which can not be handled. _ => error!("Failed to call writev for net handle_tx: {:?}", e), } + } else { + self.upload_stats.fetch_add(size as u64, Ordering::SeqCst); } + break; } 0_i8 diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 407bf67f9..689e9c592 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -46,6 +46,9 @@ use crate::{ use address_space::AddressSpace; use machine_manager::config::{ConfigCheck, NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; +use machine_manager::state_query::{ + register_state_query_callback, unregister_state_query_callback, +}; use migration::{ migration::Migratable, DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer, @@ -1462,6 +1465,21 @@ impl VirtioDevice for Net { self.taps = None; } + if let Some(ref taps) = self.taps { + for (idx, tap) in taps.iter().enumerate() { + let upload_stats = tap.upload_stats.clone(); + let download_stats = tap.download_stats.clone(); + register_state_query_callback( + format!("tap-{}", idx), + Arc::new(move || { + let upload = upload_stats.load(Ordering::SeqCst); + let download = download_stats.load(Ordering::SeqCst); + format!("upload: {} download: {}", upload, download) + }), + ) + } + } + self.init_config_features()?; Ok(()) @@ -1521,6 +1539,11 @@ impl VirtioDevice for Net { } fn unrealize(&mut self) -> Result<()> { + if let Some(ref taps) = self.taps { + for (idx, _) in taps.iter().enumerate() { + unregister_state_query_callback(&format!("tap-{}", idx)); + } + } mark_mac_table(&self.config_space.lock().unwrap().mac, false); MigrationManager::unregister_device_instance( VirtioNetState::descriptor(), @@ -1734,6 +1757,16 @@ impl VirtioDevice for Net { self.ctrl_info = None; Ok(()) } + + fn reset(&mut self) -> Result<()> { + if let Some(ref mut taps) = self.taps { + for tap in taps.iter_mut() { + tap.download_stats.store(0, Ordering::SeqCst); + tap.upload_stats.store(0, Ordering::SeqCst); + } + } + Ok(()) + } } // SAFETY: Send and Sync is not auto-implemented for `Sender` type. -- Gitee From cfc5b168b98d0d1e0b5d204e3002e57c5a0383e1 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 9 Aug 2024 12:07:53 +0800 Subject: [PATCH 2001/2187] scream: introduce AudioExtension to support volume sync This patch introduces a new interface 'AudioExtension' to support volume sync. Scream only processes ivshmem bar0 read or write, and audio backend processes volume synchronization. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 64 +++++++++++++++++++++++-- devices/src/misc/scream/ohaudio.rs | 77 ++++++++++-------------------- 2 files changed, 84 insertions(+), 57 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index b0821164c..88ce9e59a 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -26,12 +26,13 @@ use std::{mem, thread}; use anyhow::{anyhow, bail, Context, Result}; use clap::{ArgAction, Parser}; use core::time; -use log::{error, warn}; +use log::{error, info, warn}; #[cfg(feature = "scream_alsa")] use self::alsa::AlsaStreamData; use self::audio_demo::AudioDemo; use super::ivshmem::Ivshmem; +use crate::pci::{le_read_u32, le_write_u32}; use crate::{Bus, Device}; use address_space::{GuestAddress, HostMemMapping, Region}; use machine_manager::config::{get_pci_df, parse_bool, valid_id}; @@ -50,7 +51,10 @@ pub const WINDOWS_SAMPLE_BASE_RATE: u8 = 128; pub const TARGET_LATENCY_MS: u32 = 50; +#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] +const IVSHMEM_VOLUME_SYNC_VECTOR: u16 = 0; const IVSHMEM_VECTORS_NR: u32 = 1; +const IVSHMEM_BAR0_VOLUME: u64 = 240; // A frame of back-end audio data is 50ms, and the next frame of audio data needs // to be trained in polling within 50ms. Theoretically, the shorter the polling time, @@ -596,14 +600,52 @@ impl Scream { mem_region, IVSHMEM_VECTORS_NR, ); - let _ivshmem = ivshmem.realize()?; + let ivshmem = ivshmem.realize()?; - #[cfg(target_env = "ohos")] - OhAudioVolume::init_volume_sync(_ivshmem); + self.set_ivshmem_ops(ivshmem); self.start_play_thread_fn()?; self.start_record_thread_fn() } + + fn set_ivshmem_ops(&mut self, ivshmem: Arc>) { + let interface = self.create_audio_extension(ivshmem.clone()); + let interface2 = interface.clone(); + let bar0_write = Arc::new(move |data: &[u8], offset: u64| { + match offset { + IVSHMEM_BAR0_VOLUME => { + interface.set_host_volume(le_read_u32(data, 0).unwrap()); + } + _ => { + info!("ivshmem-scream: unsupported write: {offset}"); + } + } + true + }); + let bar0_read = Arc::new(move |data: &mut [u8], offset: u64| { + match offset { + IVSHMEM_BAR0_VOLUME => { + let _ = le_write_u32(data, 0, interface2.get_host_volume()); + } + _ => { + info!("ivshmem-scream: unsupported read: {offset}"); + } + } + true + }); + ivshmem + .lock() + .unwrap() + .set_bar0_ops((bar0_write, bar0_read)); + } + + fn create_audio_extension(&self, _ivshmem: Arc>) -> Arc { + match self.config.interface { + #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] + ScreamInterface::OhAudio => OhAudioVolume::new(_ivshmem), + _ => Arc::new(AudioExtensionDummy {}), + } + } } pub trait AudioInterface: Send { @@ -614,3 +656,17 @@ pub trait AudioInterface: Send { fn receive(&mut self, recv_data: &StreamData) -> i32; fn destroy(&mut self); } + +pub trait AudioExtension: Send + Sync { + fn set_host_volume(&self, _vol: u32) {} + fn get_host_volume(&self) -> u32 { + 0 + } +} + +struct AudioExtensionDummy; +impl AudioExtension for AudioExtensionDummy {} +// SAFETY: it is a dummy +unsafe impl Send for AudioExtensionDummy {} +// SAFETY: it is a dummy +unsafe impl Sync for AudioExtensionDummy {} diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index cea3b21fd..c275ea02e 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -14,7 +14,7 @@ use std::collections::VecDeque; use std::os::raw::c_void; use std::sync::{ atomic::{fence, AtomicBool, AtomicI32, Ordering}, - Arc, Mutex, + Arc, Mutex, RwLock, }; use std::{ cmp, @@ -23,11 +23,12 @@ use std::{ time::{Duration, Instant}, }; -use log::{error, info, warn}; +use log::{error, warn}; use crate::misc::ivshmem::Ivshmem; use crate::misc::scream::{ - AudioInterface, AudioStatus, ScreamDirection, ShmemStreamHeader, StreamData, + AudioExtension, AudioInterface, AudioStatus, ScreamDirection, ShmemStreamHeader, StreamData, + IVSHMEM_VOLUME_SYNC_VECTOR, }; use crate::pci::{le_read_u32, le_write_u32}; use machine_manager::notifier::register_vm_pause_notifier; @@ -36,7 +37,6 @@ use util::ohos_binding::audio::*; const STREAM_DATA_VEC_CAPACITY: usize = 15; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; -const IVSHMEM_BAR0_VOLUME: u64 = 240; trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; @@ -547,10 +547,28 @@ pub struct OhAudioVolume { ohos_vol: RwLock, } +// SAFETY: all fields are protected by lock +unsafe impl Send for OhAudioVolume {} +// SAFETY: all fields are protected by lock +unsafe impl Sync for OhAudioVolume {} + impl GuestVolumeNotifier for OhAudioVolume { fn notify(&self, vol: u32) { *self.ohos_vol.write().unwrap() = vol; - self.shm_dev.lock().unwrap().trigger_msix(0); + self.shm_dev + .lock() + .unwrap() + .trigger_msix(IVSHMEM_VOLUME_SYNC_VECTOR); + } +} + +impl AudioExtension for OhAudioVolume { + fn get_host_volume(&self) -> u32 { + *self.ohos_vol.read().unwrap() + } + + fn set_host_volume(&self, vol: u32) { + set_ohos_volume(vol); } } @@ -558,58 +576,11 @@ impl OhAudioVolume { pub fn new(shm_dev: Arc>) -> Arc { let vol = Arc::new(Self { shm_dev, - ohos_vol: RwLock::new(0), + ohos_vol: RwLock::new(get_ohos_volume()), }); register_guest_volume_notifier(vol.clone()); vol } - - fn set_volume(&self, vol: u32) { - set_ohos_volume(vol); - } - - fn get_volume(&self) -> u32 { - *self.ohos_vol.read().unwrap() - } - - fn init_volume(&self) { - *self.ohos_vol.write().unwrap() = get_ohos_volume(); - } - - pub fn init_volume_sync(ivshmem: Arc>) { - let ohvol = OhAudioVolume::new(ivshmem.clone()); - let ohvol2 = ohvol.clone(); - ohvol.init_volume(); - let bar0_write = Arc::new(move |data: &[u8], offset: u64| { - match offset { - IVSHMEM_BAR0_VOLUME => match le_read_u32(data, 0) { - Ok(v) => ohvol.set_volume(v), - Err(e) => error!("ohaudio le_read_u32 failed: {e}"), - }, - _ => { - info!("ivshmem-scream on ohaudio: unsupported write:{offset}"); - } - } - true - }); - let bar0_read = Arc::new(move |data: &mut [u8], offset: u64| { - match offset { - IVSHMEM_BAR0_VOLUME => { - if let Err(e) = le_write_u32(data, 0, ohvol2.get_volume()) { - error!("ohaudio le_write_u32 failed: {e}"); - } - } - _ => { - info!("ivshmem-scream on ohaudio: unsupported read:{offset}"); - } - } - true - }); - ivshmem - .lock() - .unwrap() - .set_bar0_ops((bar0_write, bar0_read)); - } } #[cfg(test)] -- Gitee From 78132486e3cb8b116fdaf82877ef4bd59a2fa367 Mon Sep 17 00:00:00 2001 From: zephyr Date: Fri, 9 Aug 2024 18:14:23 +0800 Subject: [PATCH 2002/2187] Ohaudio: remove unused imports Remove unused 'le_read_u32', 'le_write_u32' and 'register_vm_pause_notifier' imports. --- devices/src/misc/scream/ohaudio.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 56b564476..c7b3bf60d 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -29,8 +29,6 @@ use crate::misc::ivshmem::Ivshmem; use crate::misc::scream::{ AudioInterface, AudioStatus, ScreamDirection, ShmemStreamHeader, StreamData, }; -use crate::pci::{le_read_u32, le_write_u32}; -use machine_manager::notifier::register_vm_pause_notifier; use util::ohos_binding::audio::*; const STREAM_DATA_VEC_CAPACITY: usize = 15; -- Gitee From 6bf11a5165f9a73d91d67eecbe88b25e3fe4de82 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Sat, 10 Aug 2024 18:20:49 +0800 Subject: [PATCH 2003/2187] scream: enable low latency mode Let's enable low latency mode to decrease sound delay. Signed-off-by: Zhao Yi Min --- util/src/ohos_binding/audio/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index e67bf4e41..1a1b600a0 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -249,7 +249,6 @@ impl AudioContext { )) } - #[allow(unused)] fn set_latency_mode(&self) -> Result<(), OAErr> { call_capi!(OH_AudioStreamBuilder_SetLatencyMode( self.builder, @@ -332,6 +331,7 @@ impl AudioContext { self.set_fmt(size, rate, channels)?; self.set_sample_rate()?; self.set_sample_format()?; + self.set_latency_mode()?; self.create_processor(cb) } -- Gitee From f681c02ee48f83ad8e5b7e14d485a7aa2575f536 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 12 Aug 2024 14:50:26 +0800 Subject: [PATCH 2004/2187] ohaudio: remove unuseful code OH audio framework has fixed up the issue so we don't need to check last call time. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/ohaudio.rs | 40 ++---------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index a378d39e6..5b03c1888 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -16,14 +16,9 @@ use std::sync::{ atomic::{fence, AtomicBool, AtomicI32, Ordering}, Arc, Mutex, RwLock, }; -use std::{ - cmp, - io::Read, - ptr, thread, - time::{Duration, Instant}, -}; +use std::{cmp, io::Read, ptr, thread, time::Duration}; -use log::{error, warn}; +use log::error; use crate::misc::ivshmem::Ivshmem; use crate::misc::scream::{ @@ -168,7 +163,6 @@ struct OhAudioRender { stream_data: Arc>, flushing: AtomicBool, status: AudioStatus, - last_called_time: Option, } impl Default for OhAudioRender { @@ -178,7 +172,6 @@ impl Default for OhAudioRender { stream_data: Arc::new(Mutex::new(StreamQueue::new(STREAM_DATA_VEC_CAPACITY))), flushing: AtomicBool::new(false), status: AudioStatus::default(), - last_called_time: None, } } } @@ -250,7 +243,6 @@ impl OhAudioProcess for OhAudioRender { error!("failed to start oh audio renderer: {}", e); } } - self.last_called_time = None; self.status == AudioStatus::Started } @@ -309,7 +301,6 @@ struct OhAudioCapture { shm_len: u64, cur_pos: u64, status: AudioStatus, - last_called_time: Option, } impl OhAudioCapture { @@ -344,7 +335,6 @@ impl OhAudioProcess for OhAudioCapture { } match self.ctx.as_ref().unwrap().start() { Ok(()) => { - self.last_called_time = None; self.status = AudioStatus::Started; trace::oh_scream_capture_init(&self.ctx); true @@ -412,19 +402,6 @@ extern "C" fn on_write_data_cb( .unwrap_unchecked() }; - match &render.last_called_time { - None => render.last_called_time = Some(Instant::now()), - Some(last) => { - let elapsed = last.elapsed().as_millis(); - if elapsed >= 1000 { - warn!("{elapsed}ms elapsed after last on_write called. Will restart render."); - render.status = AudioStatus::Error; - return 0; - } - render.last_called_time = Some(Instant::now()); - } - } - let len = length as usize; // SAFETY: the buffer is guaranteed by OH audio framework. let wbuf = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, len) }; @@ -455,19 +432,6 @@ extern "C" fn on_read_data_cb( .unwrap_unchecked() }; - match &capture.last_called_time { - None => capture.last_called_time = Some(Instant::now()), - Some(last) => { - let elapsed = last.elapsed().as_millis(); - if elapsed >= 1000 { - warn!("{elapsed}ms elapsed after last on_read called. Will restart capture."); - capture.status = AudioStatus::Error; - return 0; - } - capture.last_called_time = Some(Instant::now()); - } - } - trace::trace_scope_start!(ohaudio_read_cb, args = (length)); loop { -- Gitee From 85ed50119272707b1dfa4394e16fca5e219c1944 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 12 Aug 2024 15:59:00 +0800 Subject: [PATCH 2005/2187] scream: notify starting work via mmio trap This patch introduces a new field in bar0 to let the guest notify the backend to start processing audio stream. The patch also adds a new struct to process conditional value with two bits, 0x1 indicating play or capture, 0x2 bit indicating start or stop. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 153 ++++++++++++++++++++++----------- 1 file changed, 101 insertions(+), 52 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index a273e6e77..fe55fda8f 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -56,6 +56,10 @@ pub const TARGET_LATENCY_MS: u32 = 50; const IVSHMEM_VOLUME_SYNC_VECTOR: u16 = 0; const IVSHMEM_VECTORS_NR: u32 = 1; const IVSHMEM_BAR0_VOLUME: u64 = 240; +const IVSHMEM_BAR0_STATUS: u64 = 244; + +const STATUS_PLAY_BIT: u32 = 0x1; +const STATUS_START_BIT: u32 = 0x2; // A frame of back-end audio data is 50ms, and the next frame of audio data needs // to be trained in polling within 50ms. Theoretically, the shorter the polling time, @@ -199,6 +203,51 @@ impl ShmemStreamFmt { } } +struct ScreamCond { + cond: Condvar, + paused: Mutex, +} + +impl ScreamCond { + const STREAM_PAUSE_BIT: u8 = 0x1; + const VM_PAUSE_BIT: u8 = 0x2; + + fn new() -> Arc { + Arc::new(Self { + cond: Condvar::default(), + paused: Mutex::new(Self::STREAM_PAUSE_BIT), + }) + } + + fn wait_if_paused(&self, interface: Arc>) { + let mut locked_pause = self.paused.lock().unwrap(); + while *locked_pause != 0 { + interface.lock().unwrap().destroy(); + locked_pause = self.cond.wait(locked_pause).unwrap(); + } + } + + fn set_value(&self, bv: u8, set: bool) { + let mut locked_pause = self.paused.lock().unwrap(); + let old_val = *locked_pause; + match set { + true => *locked_pause = old_val | bv, + false => *locked_pause = old_val & !bv, + } + if *locked_pause == 0 { + self.cond.notify_all(); + } + } + + fn set_vm_pause(&self, paused: bool) { + self.set_value(Self::VM_PAUSE_BIT, paused); + } + + fn set_stream_pause(&self, paused: bool) { + self.set_value(Self::STREAM_PAUSE_BIT, paused); + } +} + /// Audio stream data structure. #[derive(Debug, Default)] pub struct StreamData { @@ -208,9 +257,7 @@ pub struct StreamData { pub audio_size: u32, /// Location of the played or recorded audio data in the shared memory. pub audio_base: u64, - /// used for VM pause - cond: Arc, - paused: Arc>, + /// VM pause notifier id. pause_notifier_id: u64, } @@ -221,23 +268,9 @@ impl StreamData { self.chunk_idx = header.chunk_idx; } - #[inline(always)] - fn wait_if_paused(&mut self, interface: Arc>) { - let mut locked_pause = self.paused.lock().unwrap(); - while *locked_pause { - interface.lock().unwrap().destroy(); - locked_pause = self.cond.wait(locked_pause).unwrap(); - } - } - - fn register_pause_notifier(&mut self) { - let cond = self.cond.clone(); - let cond_pause = self.paused.clone(); + fn register_pause_notifier(&mut self, cond: Arc) { let pause_notify = Arc::new(move |paused: bool| { - *cond_pause.lock().unwrap() = paused; - if !paused { - cond.notify_all(); - } + cond.set_vm_pause(paused); }); self.pause_notifier_id = register_vm_pause_notifier(pause_notify); } @@ -246,8 +279,8 @@ impl StreamData { &mut self, interface: Arc>, dir: ScreamDirection, - poll_delay_us: u64, hva: u64, + cond: Arc, ) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the scream realize. @@ -257,27 +290,24 @@ impl StreamData { ScreamDirection::Playback => &header.play, ScreamDirection::Record => &header.capt, }; - trace::scream_init(&dir, &stream_header); loop { - if header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { + let mut locked_paused = cond.paused.lock().unwrap(); + while *locked_paused != 0 { interface.lock().unwrap().destroy(); - while header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { - self.wait_if_paused(interface.clone()); - thread::sleep(time::Duration::from_millis(10)); - header = - // SAFETY: hva is allocated by libc:::mmap, it can be guaranteed to be legal. - &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; - } - self.init(stream_header); + locked_paused = cond.cond.wait(locked_paused).unwrap(); } - // Audio playback requires waiting for the guest to play audio data. - if dir == ScreamDirection::Playback && self.chunk_idx == stream_header.chunk_idx { - thread::sleep(time::Duration::from_micros(poll_delay_us)); + if header.magic != SCREAM_MAGIC || stream_header.is_started == 0 { + *locked_paused |= ScreamCond::STREAM_PAUSE_BIT; continue; } + header = + // SAFETY: hva is allocated by libc:::mmap, it can be guaranteed to be legal. + &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; + self.init(stream_header); + let mut last_end = 0; // The recording buffer is behind the playback buffer. Thereforce, the end position of // the playback buffer must be calculted to determine whether the two buffers overlap. @@ -287,14 +317,11 @@ impl StreamData { } if !stream_header.check(last_end) { + *locked_paused |= ScreamCond::STREAM_PAUSE_BIT; continue; } - // Guest reformats the audio, and the scream device also needs to be init. - if self.fmt != stream_header.fmt { - self.init(stream_header); - continue; - } + trace::scream_init(&dir, &stream_header); return; } @@ -329,6 +356,7 @@ impl StreamData { hva: u64, shmem_size: u64, interface: Arc>, + cond: Arc, ) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the header check. @@ -336,7 +364,7 @@ impl StreamData { let play = &header.play; loop { - self.wait_if_paused(interface.clone()); + cond.wait_if_paused(interface.clone()); if play.fmt.fmt_generation != self.fmt.fmt_generation { break; @@ -373,6 +401,7 @@ impl StreamData { hva: u64, shmem_size: u64, interface: Arc>, + cond: Arc, ) { // SAFETY: hva is the shared memory base address. It already verifies the validity // of the address range during the header check. @@ -382,7 +411,7 @@ impl StreamData { interface.lock().unwrap().pre_receive(addr, capt); while capt.is_started != 0 { - self.wait_if_paused(interface.clone()); + cond.wait_if_paused(interface.clone()); if !self.update_buffer_by_chunk_idx(hva, shmem_size, capt) { return; @@ -520,7 +549,7 @@ impl Scream { } } - fn start_play_thread_fn(&mut self) -> Result<()> { + fn start_play_thread_fn(&mut self, cond: Arc) -> Result<()> { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamPlay", ScreamDirection::Playback); @@ -532,24 +561,29 @@ impl Scream { .spawn(move || { let clone_interface = interface.clone(); let mut play_data = StreamData::default(); - play_data.register_pause_notifier(); + play_data.register_pause_notifier(cond.clone()); loop { play_data.wait_for_ready( clone_interface.clone(), ScreamDirection::Playback, - POLL_DELAY_US, hva, + cond.clone(), ); - play_data.playback_trans(hva, shmem_size, clone_interface.clone()); + play_data.playback_trans( + hva, + shmem_size, + clone_interface.clone(), + cond.clone(), + ); } }) .with_context(|| "Failed to create thread scream")?; Ok(()) } - fn start_record_thread_fn(&mut self) -> Result<()> { + fn start_record_thread_fn(&mut self, cond: Arc) -> Result<()> { let hva = self.hva; let shmem_size = self.size; let interface = self.interface_init("ScreamCapt", ScreamDirection::Record); @@ -562,14 +596,14 @@ impl Scream { .spawn(move || { let clone_interface = interface.clone(); let mut capt_data = StreamData::default(); - capt_data.register_pause_notifier(); + capt_data.register_pause_notifier(cond.clone()); loop { capt_data.wait_for_ready( clone_interface.clone(), ScreamDirection::Record, - POLL_DELAY_US, hva, + cond.clone(), ); #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] @@ -577,7 +611,7 @@ impl Scream { bound_tokenid(*token_id.read().unwrap()) .unwrap_or_else(|e| error!("bound token ID failed: {}", e)); } - capt_data.capture_trans(hva, shmem_size, clone_interface.clone()); + capt_data.capture_trans(hva, shmem_size, clone_interface.clone(), cond.clone()); } }) .with_context(|| "Failed to create thread scream")?; @@ -617,13 +651,20 @@ impl Scream { ); let ivshmem = ivshmem.realize()?; - self.set_ivshmem_ops(ivshmem); + let play_cond = ScreamCond::new(); + let capt_cond = ScreamCond::new(); + self.set_ivshmem_ops(ivshmem, play_cond.clone(), capt_cond.clone()); - self.start_play_thread_fn()?; - self.start_record_thread_fn() + self.start_play_thread_fn(play_cond)?; + self.start_record_thread_fn(capt_cond) } - fn set_ivshmem_ops(&mut self, ivshmem: Arc>) { + fn set_ivshmem_ops( + &mut self, + ivshmem: Arc>, + play_cond: Arc, + capt_cond: Arc, + ) { let interface = self.create_audio_extension(ivshmem.clone()); let interface2 = interface.clone(); let bar0_write = Arc::new(move |data: &[u8], offset: u64| { @@ -631,6 +672,14 @@ impl Scream { IVSHMEM_BAR0_VOLUME => { interface.set_host_volume(le_read_u32(data, 0).unwrap()); } + IVSHMEM_BAR0_STATUS => { + let val = le_read_u32(data, 0).unwrap(); + if val & STATUS_PLAY_BIT == STATUS_PLAY_BIT { + play_cond.set_stream_pause(val & STATUS_START_BIT != STATUS_START_BIT); + } else { + capt_cond.set_stream_pause(val & STATUS_START_BIT != STATUS_START_BIT); + } + } _ => { info!("ivshmem-scream: unsupported write: {offset}"); } -- Gitee From 7ac04f54febbbce6e0b5ce9128e6887d048b4506 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Tue, 13 Aug 2024 22:05:24 +0800 Subject: [PATCH 2006/2187] ivshmem: set max access size for bar0 region Set max access size to 4 bytes for bar0 region. Signed-off-by: Zhao Yi Min --- devices/src/misc/ivshmem.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index d4d50f36e..2d1325826 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -120,9 +120,12 @@ impl Ivshmem { }; // bar0: mmio register + let mut bar0_region = + Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops, "IvshmemIo"); + bar0_region.set_access_size(4); self.base.config.register_bar( 0, - Region::init_io_region(IVSHMEM_REG_BAR_SIZE, reg_region_ops, "IvshmemIo"), + bar0_region, RegionType::Mem32Bit, false, IVSHMEM_REG_BAR_SIZE, -- Gitee From c98ab992acd5c85d217c35c0cc2a29f67804d8ff Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Wed, 14 Aug 2024 19:21:37 +0800 Subject: [PATCH 2007/2187] xhci: Check whether event ring address is valid The event ring must locate in guest ram. Signed-off-by: Keqian Zhu --- devices/src/usb/xhci/xhci_controller.rs | 15 +++++++-- devices/src/usb/xhci/xhci_regs.rs | 44 ++++++++++++++----------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 3fe6631e2..2114f607a 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -25,7 +25,7 @@ use super::xhci_pci::XhciConfig; use super::xhci_regs::{XhciInterrupter, XhciOperReg}; use super::xhci_ring::{XhciCommandRing, XhciEventRingSeg, XhciTRB, XhciTransferRing}; use super::xhci_trb::{ - TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_TR_DIR, TRB_TR_FRAMEID_MASK, + TRBCCode, TRBType, SETUP_TRB_TR_LEN, TRB_EV_ED, TRB_SIZE, TRB_TR_DIR, TRB_TR_FRAMEID_MASK, TRB_TR_FRAMEID_SHIFT, TRB_TR_IDT, TRB_TR_IOC, TRB_TR_ISP, TRB_TR_LEN_MASK, TRB_TR_SIA, TRB_TYPE_SHIFT, }; @@ -2359,7 +2359,7 @@ impl XhciDevice { pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { let mut locked_intr = self.intrs[idx as usize].lock().unwrap(); if locked_intr.erstsz == 0 || locked_intr.erstba == 0 { - locked_intr.er_start = 0; + locked_intr.er_start = GuestAddress(0); locked_intr.er_size = 0; return Ok(()); } @@ -2368,7 +2368,16 @@ impl XhciDevice { if seg.size < 16 || seg.size > 4096 { bail!("Invalid segment size {}", seg.size); } - locked_intr.er_start = addr64_from_u32(seg.addr_lo, seg.addr_hi); + + // GPAChecked: the event ring must locate in guest ram. + let base_addr = GuestAddress(addr64_from_u32(seg.addr_lo, seg.addr_hi)); + // SAFETY: seg size is a 16 bit register, will not overflow. + let er_len = seg.size * TRB_SIZE; + if !self.mem_space.address_in_memory(base_addr, er_len as u64) { + bail!("The event ring does not locate in guest ram"); + } + + locked_intr.er_start = base_addr; locked_intr.er_size = seg.size; locked_intr.er_ep_idx = 0; locked_intr.er_pcs = true; diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index d817a6319..28186170d 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -188,7 +188,7 @@ pub struct XhciInterrupter { pub erdp: u64, /// Event Ring Producer Cycle State pub er_pcs: bool, - pub er_start: u64, + pub er_start: GuestAddress, pub er_size: u32, pub er_ep_idx: u32, } @@ -212,7 +212,7 @@ impl XhciInterrupter { erstba: 0, erdp: 0, er_pcs: true, - er_start: 0, + er_start: GuestAddress(0), er_size: 0, er_ep_idx: 0, } @@ -238,7 +238,7 @@ impl XhciInterrupter { self.erstba = 0; self.erdp = 0; self.er_pcs = true; - self.er_start = 0; + self.er_start = GuestAddress(0); self.er_size = 0; self.er_ep_idx = 0; } @@ -248,19 +248,21 @@ impl XhciInterrupter { let er_end = self .er_start .checked_add((TRB_SIZE * self.er_size) as u64) - .ok_or(UsbError::MemoryAccessOverflow( - self.er_start, - (TRB_SIZE * self.er_size) as u64, - ))?; - if self.erdp < self.er_start || self.erdp >= er_end { + .ok_or_else(|| { + UsbError::MemoryAccessOverflow( + self.er_start.raw_value(), + (TRB_SIZE * self.er_size) as u64, + ) + })?; + if self.erdp < self.er_start.raw_value() || self.erdp >= er_end.raw_value() { bail!( "DMA out of range, erdp {} er_start {:x} er_size {}", self.erdp, - self.er_start, + self.er_start.raw_value(), self.er_size ); } - let dp_idx = (self.erdp - self.er_start) / TRB_SIZE as u64; + let dp_idx = (self.erdp - self.er_start.raw_value()) / TRB_SIZE as u64; if ((self.er_ep_idx + 2) % self.er_size) as u64 == dp_idx { debug!("Event ring full error, idx {}", dp_idx); let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); @@ -335,10 +337,12 @@ impl XhciInterrupter { let addr = self .er_start .checked_add((TRB_SIZE * self.er_ep_idx) as u64) - .ok_or(UsbError::MemoryAccessOverflow( - self.er_start, - (TRB_SIZE * self.er_ep_idx) as u64, - ))?; + .ok_or_else(|| { + UsbError::MemoryAccessOverflow( + self.er_start.raw_value(), + (TRB_SIZE * self.er_ep_idx) as u64, + ) + })?; let cycle = trb.control as u8; // Toggle the cycle bit to avoid driver read it. let control = if trb.control & TRB_C == TRB_C { @@ -350,10 +354,10 @@ impl XhciInterrupter { LittleEndian::write_u64(&mut buf, trb.parameter); LittleEndian::write_u32(&mut buf[8..], trb.status); LittleEndian::write_u32(&mut buf[12..], control); - dma_write_bytes(&self.mem, GuestAddress(addr), &buf)?; + dma_write_bytes(&self.mem, addr, &buf)?; // Write the cycle bit at last. fence(Ordering::SeqCst); - dma_write_bytes(&self.mem, GuestAddress(addr + 12), &[cycle])?; + dma_write_bytes(&self.mem, addr.unchecked_add(12), &[cycle])?; Ok(()) } } @@ -650,14 +654,14 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } else { error!( "Memory access overflow, addr {:x} offset {:x}", - locked_intr.er_start, + locked_intr.er_start.raw_value(), (TRB_SIZE * locked_intr.er_size) as u64 ); return false; }; - if erdp >= locked_intr.er_start - && erdp < er_end - && (erdp - locked_intr.er_start) / TRB_SIZE as u64 + if erdp >= locked_intr.er_start.raw_value() + && erdp < er_end.raw_value() + && (erdp - locked_intr.er_start.raw_value()) / TRB_SIZE as u64 != locked_intr.er_ep_idx as u64 { drop(locked_intr); -- Gitee From 071033e418087f016edfd28b7bf7b58d4710af95 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 13 Aug 2024 16:54:37 +0800 Subject: [PATCH 2008/2187] scream:support microphone authority sync This patch defines an status register on ivshmem BAR0, in which BIT2 is used for passing microphone's authority infomation to frontend. We use msix 1 to tell front end status-changed-event, and use BAR0 register to pass related info.` Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 60 ++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index fe55fda8f..237d271a5 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -27,6 +27,7 @@ use anyhow::{anyhow, bail, Context, Result}; use clap::{ArgAction, Parser}; use core::time; use log::{error, info, warn}; +use once_cell::sync::Lazy; #[cfg(feature = "scream_alsa")] use self::alsa::AlsaStreamData; @@ -54,12 +55,14 @@ pub const TARGET_LATENCY_MS: u32 = 50; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] const IVSHMEM_VOLUME_SYNC_VECTOR: u16 = 0; -const IVSHMEM_VECTORS_NR: u32 = 1; +const IVSHMEM_STATUS_CHANGE_VECTOR: u16 = 1; +const IVSHMEM_VECTORS_NR: u32 = 2; const IVSHMEM_BAR0_VOLUME: u64 = 240; const IVSHMEM_BAR0_STATUS: u64 = 244; const STATUS_PLAY_BIT: u32 = 0x1; const STATUS_START_BIT: u32 = 0x2; +const STATUS_MIC_AVAIL_BIT: u32 = 0x4; // A frame of back-end audio data is 50ms, and the next frame of audio data needs // to be trained in polling within 50ms. Theoretically, the shorter the polling time, @@ -80,6 +83,26 @@ pub enum AudioStatus { Error, } +type AuthorityNotify = dyn Fn() + Send + Sync; + +#[derive(Clone)] +pub struct AuthorityInformation { + state: bool, + notify: Option>, +} + +impl AuthorityInformation { + const fn default() -> AuthorityInformation { + AuthorityInformation { + state: true, + notify: None, + } + } +} + +type AuthInfo = RwLock; +static AUTH_INFO: Lazy = Lazy::new(|| RwLock::new(AuthorityInformation::default())); + /// The scream device defines the audio directions. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ScreamDirection { @@ -106,20 +129,19 @@ pub struct ShmemStreamHeader { pub fmt: ShmemStreamFmt, } -fn record_authority_rw(auth: bool, write: bool) -> bool { - static AUTH: RwLock = RwLock::new(true); - if write { - *AUTH.write().unwrap() = auth; +pub fn set_record_authority(auth: bool) { + AUTH_INFO.write().unwrap().state = auth; + if let Some(auth_notify) = &AUTH_INFO.read().unwrap().notify { + auth_notify(); } - *AUTH.read().unwrap() } -pub fn set_record_authority(auth: bool) { - record_authority_rw(auth, true); +pub fn set_authority_notify(notify: Option>) { + AUTH_INFO.write().unwrap().notify = notify; } -fn get_record_authority() -> bool { - record_authority_rw(false, false) +pub fn get_record_authority() -> bool { + AUTH_INFO.read().unwrap().state } impl ShmemStreamHeader { @@ -650,11 +672,20 @@ impl Scream { IVSHMEM_VECTORS_NR, ); let ivshmem = ivshmem.realize()?; + let ivshmem_cloned = ivshmem.clone(); let play_cond = ScreamCond::new(); let capt_cond = ScreamCond::new(); self.set_ivshmem_ops(ivshmem, play_cond.clone(), capt_cond.clone()); + let author_notify = Arc::new(move || { + ivshmem_cloned + .lock() + .unwrap() + .trigger_msix(IVSHMEM_STATUS_CHANGE_VECTOR); + }); + set_authority_notify(Some(author_notify)); + self.start_play_thread_fn(play_cond)?; self.start_record_thread_fn(capt_cond) } @@ -691,6 +722,9 @@ impl Scream { IVSHMEM_BAR0_VOLUME => { let _ = le_write_u32(data, 0, interface2.get_host_volume()); } + IVSHMEM_BAR0_STATUS => { + let _ = le_write_u32(data, 0, interface2.get_status_register()); + } _ => { info!("ivshmem-scream: unsupported read: {offset}"); } @@ -727,6 +761,12 @@ pub trait AudioExtension: Send + Sync { fn get_host_volume(&self) -> u32 { 0 } + fn get_status_register(&self) -> u32 { + match get_record_authority() { + true => STATUS_MIC_AVAIL_BIT, + false => 0, + } + } } struct AudioExtensionDummy; -- Gitee From 136b588da3f3a1a987a226491e72843f104c3670 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Thu, 15 Aug 2024 18:42:27 +0800 Subject: [PATCH 2009/2187] PvPanic: add hisysevent trace when panic occurred Add hisysevent trace when panicked/crashloaded event happened. --- Cargo.lock | 1 + devices/Cargo.toml | 1 + devices/src/misc/pvpanic.rs | 2 ++ hisysevent/event_info/misc.toml | 5 +++++ 4 files changed, 9 insertions(+) create mode 100644 hisysevent/event_info/misc.toml diff --git a/Cargo.lock b/Cargo.lock index 20ed7089a..9453a417a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -387,6 +387,7 @@ dependencies = [ "clap", "cpu", "drm-fourcc", + "hisysevent", "libc", "libpulse-binding", "libpulse-simple-binding", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index fec9d5c13..69dc546d0 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -38,6 +38,7 @@ rusb = { version = "0.9", optional = true } libusb1-sys = { version = "0.6.4", optional = true } trace = { path = "../trace" } clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } +hisysevent = { path = "../hisysevent" } [features] default = [] diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 1647a2d58..366d0f45d 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -87,12 +87,14 @@ impl PvPanicState { if (event & PVPANIC_PANICKED) == PVPANIC_PANICKED && (self.supported_features & PVPANIC_PANICKED) == PVPANIC_PANICKED { + hisysevent::STRATOVIRT_PVPANIC("PANICKED".to_string()); info!("pvpanic: panicked event"); } if (event & PVPANIC_CRASHLOADED) == PVPANIC_CRASHLOADED && (self.supported_features & PVPANIC_CRASHLOADED) == PVPANIC_CRASHLOADED { + hisysevent::STRATOVIRT_PVPANIC("CRASHLOADED".to_string()); info!("pvpanic: crashloaded event"); } diff --git a/hisysevent/event_info/misc.toml b/hisysevent/event_info/misc.toml new file mode 100644 index 000000000..eeb4be8d0 --- /dev/null +++ b/hisysevent/event_info/misc.toml @@ -0,0 +1,5 @@ +[[events]] +name = "STRATOVIRT_PVPANIC" +event_type = "Fault" +args = "event: String" +enabled = true -- Gitee From ae6251696776c989ec49b7749e2825050f095298 Mon Sep 17 00:00:00 2001 From: lixiang_yewu Date: Thu, 15 Aug 2024 11:17:03 +0000 Subject: [PATCH 2010/2187] update docs/snapshot.md. Signed-off-by: lixiang_yewu --- docs/snapshot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/snapshot.md b/docs/snapshot.md index 6a9c97f73..5e9c118b2 100644 --- a/docs/snapshot.md +++ b/docs/snapshot.md @@ -25,7 +25,7 @@ $ ncat -U path/to/socket {"return":{}} ``` -When VM is in paused state, is's safe to take a snapshot of the VM into the specified directory with QMP. +When VM is in paused state, it's safe to take a snapshot of the VM into the specified directory with QMP. ```shell $ ncat -U path/to/socket {"QMP":{"version":{"StratoVirt":{"micro":1,"minor":0,"major":0},"package":""},"capabilities":[]}} -- Gitee From f9f037644ac8b92d9ccd9863afd64f70543e29fd Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Thu, 15 Aug 2024 19:46:11 +0800 Subject: [PATCH 2011/2187] virtio: add GPAChecked lable while using gpa addr Add GPAChecked lable which means using gpa addr is valid. Signed-off-by: Yan Wang --- virtio/src/device/balloon.rs | 1 + virtio/src/device/serial.rs | 1 + virtio/src/queue/split.rs | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 4c981c185..e7a70f29c 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -160,6 +160,7 @@ fn iov_to_buf( return None; } + // GPAChecked: the iov has been checked in pop_avail(). match address_space.read_object::(GuestAddress(iov.iov_base.raw_value() + offset)) { Ok(dat) => Some(dat), Err(ref e) => { diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index f8a1e35fe..c0ebccae3 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -589,6 +589,7 @@ impl SerialPortHandler { let write_end = written_count + len; let mut source_slice = &buffer[written_count..write_end]; + // GPAChecked: the elem_iov has been checked in pop_avail(). self.mem_space .write(&mut source_slice, elem_iov.addr, len as u64) .with_context(|| { diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 643693b5d..40eb41aaf 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -298,6 +298,7 @@ impl SplitVringDesc { return false; } }; + // GPAChecked: the vring desc [addr, addr+len] must locate in guest ram. if base > reg_cache.start && end < reg_cache.end { miss_cached = false; } @@ -311,6 +312,7 @@ impl SplitVringDesc { } if miss_cached { + // GPAChecked: the vring desc addr must locate in guest ram. if let Err(ref e) = checked_offset_mem(sys_mem, self.addr, u64::from(self.len)) { error!("The memory of descriptor is invalid, {:?} ", e); return false; @@ -642,6 +644,7 @@ impl SplitVring { } fn is_invalid_memory(&self, sys_mem: &Arc, actual_size: u64) -> bool { + // GPAChecked: the desc ring table must locate in guest ram. let desc_table_end = match checked_offset_mem(sys_mem, self.desc_table, DESCRIPTOR_LEN * actual_size) { Ok(addr) => addr, @@ -656,6 +659,7 @@ impl SplitVring { } }; + // GPAChecked: the avail ring table must locate in guest ram. let desc_avail_end = match checked_offset_mem( sys_mem, self.avail_ring, @@ -673,6 +677,7 @@ impl SplitVring { } }; + // GPAChecked: the used ring table must locate in guest ram. let desc_used_end = match checked_offset_mem( sys_mem, self.used_ring, -- Gitee From 498a9d714af4b30cd57b929468bd2838ea70502c Mon Sep 17 00:00:00 2001 From: zhanghan Date: Fri, 16 Aug 2024 09:30:14 +0800 Subject: [PATCH 2012/2187] ohaudio: extend waiting time for capture data We now have 200 ms for waiting capture data, if no data was accuqired during 200 ms, we destroy this capturer and restart again. But now the fact shows that 200 ms is too short for ohaudio to give us data, let's change it to 500 ms. Signed-off-by: zhanghan64 --- devices/src/misc/scream/ohaudio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index eaea009ce..a4d892543 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -31,7 +31,7 @@ const STREAM_DATA_VEC_CAPACITY: usize = 15; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; const SCREAM_MAX_VOLUME: u32 = 110; -const CAPTURE_WAIT_TIMEOUT: u64 = 200; +const CAPTURE_WAIT_TIMEOUT: u64 = 500; trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; -- Gitee From 051da72035161039cf34bcee64fdd3a276cc6e17 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 16 Aug 2024 12:52:42 +0800 Subject: [PATCH 2013/2187] pci: Fix `unused_parens` clippy warnings warning: unnecessary parentheses around function argument --> devices/src/pci/config.rs:1316:23 | 1316 | u64::from((MEM_BASE_ADDR_MASK as u32)) | ^ ^ | = note: `#[warn(unused_parens)]` on by default help: remove these parentheses | 1316 - u64::from((MEM_BASE_ADDR_MASK as u32)) 1316 + u64::from(MEM_BASE_ADDR_MASK as u32) Signed-off-by: liuxiangdong --- devices/src/pci/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 5228e3376..2f041c4fd 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -1313,7 +1313,7 @@ mod tests { assert_eq!(pci_config.get_bar_address(0), BAR_SPACE_UNMAPPED); assert_eq!( pci_config.get_bar_address(1), - (MEM_BASE_ADDR_MASK as u32) as u64 + u64::from(MEM_BASE_ADDR_MASK as u32) ); assert_eq!(pci_config.get_bar_address(2), MEM_BASE_ADDR_MASK); } -- Gitee From f3f0a3c43201ff19eae60e6eea87ee819129bbed Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 16 Aug 2024 12:55:00 +0800 Subject: [PATCH 2014/2187] clippy: fix all cast_lossless warnings Use command line: cargo clippy --fix --workspace -- -Aclippy::all -Wclippy::cast_lossless Signed-off-by: liuxiangdong --- acpi/src/acpi_device.rs | 2 +- acpi/src/aml_compiler.rs | 2 +- acpi/src/table_loader.rs | 2 +- address_space/src/address.rs | 10 +- address_space/src/host_mmap.rs | 4 +- block_backend/src/lib.rs | 4 +- block_backend/src/qcow2/cache.rs | 2 +- block_backend/src/qcow2/check.rs | 34 +-- block_backend/src/qcow2/header.rs | 12 +- block_backend/src/qcow2/mod.rs | 30 +-- block_backend/src/qcow2/refcount.rs | 16 +- block_backend/src/qcow2/snapshot.rs | 2 +- block_backend/src/qcow2/table.rs | 6 +- block_backend/src/raw.rs | 6 +- boot_loader/src/x86_64/direct_boot/gdt.rs | 4 +- boot_loader/src/x86_64/direct_boot/mod.rs | 8 +- boot_loader/src/x86_64/standard_boot/elf.rs | 12 +- boot_loader/src/x86_64/standard_boot/mod.rs | 2 +- cpu/src/x86_64/mod.rs | 18 +- devices/src/acpi/power.rs | 2 +- .../src/interrupt_controller/aarch64/state.rs | 18 +- devices/src/legacy/fwcfg.rs | 28 +-- devices/src/legacy/pflash.rs | 20 +- devices/src/legacy/pl011.rs | 16 +- devices/src/legacy/pl031.rs | 2 +- devices/src/legacy/rtc.rs | 4 +- devices/src/misc/ivshmem.rs | 2 +- devices/src/misc/pvpanic.rs | 4 +- devices/src/misc/scream/mod.rs | 16 +- devices/src/pci/bus.rs | 6 +- devices/src/pci/config.rs | 40 ++-- devices/src/pci/host.rs | 16 +- devices/src/pci/mod.rs | 19 +- devices/src/pci/msix.rs | 42 ++-- devices/src/pci/root_port.rs | 11 +- devices/src/scsi/bus.rs | 22 +- devices/src/smbios/smbios_table.rs | 10 +- devices/src/usb/descriptor.rs | 6 +- devices/src/usb/hid.rs | 2 +- devices/src/usb/keyboard.rs | 4 +- devices/src/usb/mod.rs | 26 +-- devices/src/usb/storage.rs | 4 +- devices/src/usb/xhci/xhci_controller.rs | 38 ++-- devices/src/usb/xhci/xhci_pci.rs | 29 +-- devices/src/usb/xhci/xhci_regs.rs | 38 ++-- devices/src/usb/xhci/xhci_ring.rs | 10 +- hypervisor/src/kvm/aarch64/gicv2.rs | 8 +- hypervisor/src/kvm/aarch64/gicv3.rs | 10 +- hypervisor/src/kvm/aarch64/mod.rs | 41 ++-- hypervisor/src/kvm/mod.rs | 2 +- hypervisor/src/test/mod.rs | 6 +- image/src/img.rs | 8 +- machine/src/aarch64/fdt.rs | 2 +- machine/src/aarch64/standard.rs | 10 +- machine/src/lib.rs | 4 +- machine/src/standard_common/mod.rs | 20 +- machine/src/x86_64/ich9_lpc.rs | 17 +- machine_manager/src/config/machine_config.rs | 2 +- machine_manager/src/config/network.rs | 2 +- machine_manager/src/qmp/qmp_channel.rs | 2 +- machine_manager/src/socket.rs | 6 +- migration/migration_derive/src/attr_parser.rs | 2 +- migration/src/manager.rs | 2 +- migration/src/protocol.rs | 10 +- tests/mod_test/src/libdriver/pci.rs | 14 +- tests/mod_test/src/libdriver/pci_bus.rs | 9 +- tests/mod_test/src/libdriver/qcow2.rs | 2 +- tests/mod_test/src/libdriver/usb.rs | 196 ++++++++++-------- tests/mod_test/src/libdriver/virtio.rs | 30 +-- tests/mod_test/src/libdriver/virtio_block.rs | 18 +- tests/mod_test/src/libdriver/virtio_gpu.rs | 4 +- .../src/libdriver/virtio_pci_modern.rs | 108 +++++----- tests/mod_test/src/utils.rs | 8 +- tests/mod_test/tests/aarch64/acpi_test.rs | 8 +- tests/mod_test/tests/balloon_test.rs | 28 +-- tests/mod_test/tests/block_test.rs | 63 +++--- tests/mod_test/tests/net_test.rs | 34 +-- tests/mod_test/tests/pci_test.rs | 18 +- tests/mod_test/tests/rng_test.rs | 4 +- tests/mod_test/tests/scream_test.rs | 16 +- tests/mod_test/tests/scsi_test.rs | 78 +++---- tests/mod_test/tests/serial_test.rs | 14 +- tests/mod_test/tests/usb_camera_test.rs | 4 +- tests/mod_test/tests/usb_storage_test.rs | 10 +- tests/mod_test/tests/usb_test.rs | 80 ++++--- tests/mod_test/tests/virtio_test.rs | 67 +++--- tests/mod_test/tests/virtiofs_test.rs | 4 +- tests/mod_test/tests/vnc_test.rs | 4 +- ui/src/console.rs | 4 +- ui/src/pixman.rs | 14 +- util/src/aio/mod.rs | 24 +-- util/src/aio/threads.rs | 2 +- util/src/aio/uring.rs | 2 +- util/src/edid.rs | 2 +- util/src/file.rs | 2 +- util/src/leak_bucket.rs | 2 +- util/src/logger.rs | 4 +- util/src/num_ops.rs | 8 +- util/src/tap.rs | 4 +- util/src/unix.rs | 6 +- vfio/src/vfio_pci.rs | 25 +-- virtio/src/device/balloon.rs | 20 +- virtio/src/device/block.rs | 18 +- virtio/src/device/gpu.rs | 20 +- virtio/src/device/net.rs | 8 +- virtio/src/device/rng.rs | 36 ++-- virtio/src/device/scsi_cntlr.rs | 4 +- virtio/src/device/serial.rs | 8 +- virtio/src/lib.rs | 18 +- virtio/src/queue/mod.rs | 2 +- virtio/src/queue/split.rs | 136 ++++++------ virtio/src/transport/virtio_mmio.rs | 16 +- virtio/src/transport/virtio_pci.rs | 44 ++-- virtio/src/vhost/kernel/net.rs | 4 +- virtio/src/vhost/kernel/vsock.rs | 6 +- virtio/src/vhost/user/block.rs | 4 +- virtio/src/vhost/user/client.rs | 8 +- 117 files changed, 1064 insertions(+), 973 deletions(-) diff --git a/acpi/src/acpi_device.rs b/acpi/src/acpi_device.rs index aff03b84d..d315fc40b 100644 --- a/acpi/src/acpi_device.rs +++ b/acpi/src/acpi_device.rs @@ -52,7 +52,7 @@ impl AcpiPMTimer { } let now = Instant::now(); let time_nanos = now.duration_since(self.start).as_nanos(); - let counter: u128 = (time_nanos * PM_TIMER_FREQUENCY) / (NANOSECONDS_PER_SECOND as u128); + let counter: u128 = (time_nanos * PM_TIMER_FREQUENCY) / u128::from(NANOSECONDS_PER_SECOND); data.copy_from_slice(&((counter & 0xFFFF_FFFF) as u32).to_le_bytes()); true diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index 2ad8d51ae..b7aa0a9a6 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -1616,7 +1616,7 @@ impl AmlIrqNoFlags { impl AmlBuilder for AmlIrqNoFlags { fn aml_bytes(&self) -> Vec { - let irq_mask = 1 << (self.irq as u16); + let irq_mask = 1 << u16::from(self.irq); vec![0x22, (irq_mask & 0xFF) as u8, (irq_mask >> 8) as u8] } } diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 0a58c6de6..74a9d173e 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -356,7 +356,7 @@ impl TableLoader { dst_file_entry.file_blob.lock().unwrap() [offset as usize..(offset as usize + size as usize)] - .copy_from_slice(&(src_offset as u64).as_bytes()[0..size as usize]); + .copy_from_slice(&u64::from(src_offset).as_bytes()[0..size as usize]); self.cmds.push(TableLoaderEntry::new_add_pointer_entry( dst_file, src_file, offset, size, diff --git a/address_space/src/address.rs b/address_space/src/address.rs index a5ace6f03..6fd1a8dcb 100644 --- a/address_space/src/address.rs +++ b/address_space/src/address.rs @@ -166,16 +166,16 @@ impl AddressRange { /// /// * `other` - Other AddressRange. pub fn find_intersection(&self, other: AddressRange) -> Option { - let begin = self.base.raw_value() as u128; - let end = self.size as u128 + begin; - let other_begin = other.base.raw_value() as u128; - let other_end = other.size as u128 + other_begin; + let begin = u128::from(self.base.raw_value()); + let end = u128::from(self.size) + begin; + let other_begin = u128::from(other.base.raw_value()); + let other_end = u128::from(other.size) + other_begin; if end <= other_begin || other_end <= begin { return None; } let start = std::cmp::max(self.base, other.base); - let size_inter = (std::cmp::min(end, other_end) - start.0 as u128) as u64; + let size_inter = (std::cmp::min(end, other_end) - u128::from(start.0)) as u64; Some(AddressRange { base: start, diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index d2012e6f5..c4b8ce178 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -209,8 +209,8 @@ fn mem_prealloc(host_addr: u64, size: u64, nr_vcpus: u8) { let page_size = host_page_size(); let threads = max_nr_threads(nr_vcpus); let nr_pages = (size + page_size - 1) / page_size; - let pages_per_thread = nr_pages / (threads as u64); - let left = nr_pages % (threads as u64); + let pages_per_thread = nr_pages / u64::from(threads); + let left = nr_pages % u64::from(threads); let mut addr = host_addr; let mut threads_join = Vec::new(); for i in 0..threads { diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index b4de1fc95..c571f292e 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -402,7 +402,7 @@ pub fn create_block_backend( DiskFormat::Raw => { let mut raw_file = RawDriver::new(file, aio, prop.clone()); let file_size = raw_file.disk_size()?; - if file_size & (prop.req_align as u64 - 1) != 0 { + if file_size & (u64::from(prop.req_align) - 1) != 0 { bail!("The size of raw file is not aligned to {}.", prop.req_align); } Ok(Arc::new(Mutex::new(raw_file))) @@ -415,7 +415,7 @@ pub fn create_block_backend( .with_context(|| "Failed to load metadata")?; let file_size = qcow2.disk_size()?; - if file_size & (prop.req_align as u64 - 1) != 0 { + if file_size & (u64::from(prop.req_align) - 1) != 0 { bail!( "The size of qcow2 file is not aligned to {}.", prop.req_align diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 445ddc31e..c46bc5d93 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -85,7 +85,7 @@ impl CacheTable { bail!("Invalid idx {}", idx); } let v = match self.entry_size { - ENTRY_SIZE_U16 => BigEndian::read_u16(&self.table_data[start..end]) as u64, + ENTRY_SIZE_U16 => u64::from(BigEndian::read_u16(&self.table_data[start..end])), ENTRY_SIZE_U64 => BigEndian::read_u64(&self.table_data[start..end]), _ => bail!("Unsupported entry size {}", self.entry_size), }; diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index 2f6bb9f70..c148178ce 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -105,7 +105,7 @@ impl RefcountBlock { let start_bytes = idx * self.entry_bytes; let end_bytes = start_bytes + self.entry_bytes; let value = match self.entry_bytes { - ENTRY_SIZE_U16 => BigEndian::read_u16(&self.data[start_bytes..end_bytes]) as u64, + ENTRY_SIZE_U16 => u64::from(BigEndian::read_u16(&self.data[start_bytes..end_bytes])), ENTRY_SIZE_U64 => BigEndian::read_u64(&self.data[start_bytes..end_bytes]), _ => bail!("Entry size is unsupported"), }; @@ -221,7 +221,7 @@ impl Qcow2Driver { let snapshot_table_length = size_of::() as u64; let snapshot_table_offset = self.header.snapshots_offset; // Validate snapshot table. - if (u64::MAX - nb_snapshots as u64 * snapshot_table_length) < snapshot_table_offset + if (u64::MAX - u64::from(nb_snapshots) * snapshot_table_length) < snapshot_table_offset || !is_aligned(self.header.cluster_size(), snapshot_table_offset) { res.err_num += 1; @@ -300,7 +300,7 @@ impl Qcow2Driver { /// Rebuild a new refcount table according to metadata, including active l1 table, active l2 table, /// snapshot table, refcount table and refcount block. pub(crate) fn check_refcounts(&mut self, check: &mut Qcow2Check) -> Result<()> { - let cluster_bits = self.header.cluster_bits as u64; + let cluster_bits = u64::from(self.header.cluster_bits); let cluster_size = 1 << cluster_bits; let virtual_size = self.header.size; check.res.disk_frag.total_clusters = div_round_up(virtual_size, cluster_size).unwrap(); @@ -380,14 +380,14 @@ impl Qcow2Driver { 0, self.header.cluster_size(), file_len, - self.header.cluster_bits as u64, + u64::from(self.header.cluster_bits), check, )?; // Increase the refcount of active l1 table. let active_l1_offset = self.header.l1_table_offset; let active_l1_size = self.header.l1_size; - self.check_refcounts_l1(active_l1_offset, active_l1_size as u64, true, check)?; + self.check_refcounts_l1(active_l1_offset, u64::from(active_l1_size), true, check)?; // Increase the refcount of snapshot table. for idx in 0..self.header.nb_snapshots { @@ -404,7 +404,7 @@ impl Qcow2Driver { continue; } - if snap_l1_size as u64 > QCOW2_MAX_L1_SIZE / ENTRY_SIZE { + if u64::from(snap_l1_size) > QCOW2_MAX_L1_SIZE / ENTRY_SIZE { output_msg!( check.quite, "ERROR snapshot {:?}({:?}) l1_size={:?} l1 table is too large; snapshot table entry courropted", @@ -414,7 +414,7 @@ impl Qcow2Driver { continue; } - self.check_refcounts_l1(snap_l1_offset, snap_l1_size as u64, false, check)?; + self.check_refcounts_l1(snap_l1_offset, u64::from(snap_l1_size), false, check)?; } let snap_table_offset = self.header.snapshots_offset; @@ -424,19 +424,19 @@ impl Qcow2Driver { snap_table_offset, snap_table_size, file_len, - self.header.cluster_bits as u64, + u64::from(self.header.cluster_bits), check, )?; } let reftable_offset = self.header.refcount_table_offset; let reftable_bytes = - self.header.refcount_table_clusters as u64 * self.header.cluster_size(); + u64::from(self.header.refcount_table_clusters) * self.header.cluster_size(); self.increase_refcounts( reftable_offset, reftable_bytes, file_len, - self.header.cluster_bits as u64, + u64::from(self.header.cluster_bits), check, )?; @@ -463,7 +463,7 @@ impl Qcow2Driver { l1_offset, l1_size_bytes, file_len, - self.header.cluster_bits as u64, + u64::from(self.header.cluster_bits), check, )?; let l1_table = self @@ -497,7 +497,7 @@ impl Qcow2Driver { l2_offset, self.header.cluster_size(), file_len, - self.header.cluster_bits as u64, + u64::from(self.header.cluster_bits), check, )?; @@ -529,7 +529,7 @@ impl Qcow2Driver { file_len: u64, check: &mut Qcow2Check, ) -> Result<()> { - let cluster_bits = self.header.cluster_bits as u64; + let cluster_bits = u64::from(self.header.cluster_bits); let cluster_size = 1 << cluster_bits; let l2_size = cluster_size >> ENTRY_BITS; @@ -661,7 +661,7 @@ impl Qcow2Driver { } fn check_refcount_block(&mut self, check: &mut Qcow2Check) -> Result<()> { - let cluster_bits = self.header.cluster_bits as u64; + let cluster_bits = u64::from(self.header.cluster_bits); let cluster_size = 1 << cluster_bits; let file_len = self.driver.disk_size()?; let nb_clusters = bytes_to_clusters(file_len, cluster_size)?; @@ -794,7 +794,7 @@ impl Qcow2Driver { ); if need_fixed { - let added = rc_value_2 as i32 - rc_value_1 as i32; + let added = i32::from(rc_value_2) - i32::from(rc_value_1); let cluster_offset = cluster_idx << cluster_bits; self.refcount.update_refcount( cluster_offset, @@ -964,8 +964,8 @@ impl Qcow2Driver { let mut reftable_offset: u64 = 0; let mut new_reftable: Vec = Vec::new(); let mut reftable_clusters: u64 = 0; - let cluster_bits = self.header.cluster_bits as u64; - let refblock_bits: u64 = cluster_bits + 3 - self.header.refcount_order as u64; + let cluster_bits = u64::from(self.header.cluster_bits); + let refblock_bits: u64 = cluster_bits + 3 - u64::from(self.header.refcount_order); let refblock_size: u64 = 1 << refblock_bits; // self.refblock.nb_clusters means the maximum number of clusters that can be represented by diff --git a/block_backend/src/qcow2/header.rs b/block_backend/src/qcow2/header.rs index d2b9f8f78..99404b9f9 100644 --- a/block_backend/src/qcow2/header.rs +++ b/block_backend/src/qcow2/header.rs @@ -138,7 +138,7 @@ impl QcowHeader { if !(MIN_CLUSTER_BIT..=MAX_CLUSTER_BIT).contains(&self.cluster_bits) { bail!("Invalid cluster bits {}", self.cluster_bits); } - if self.header_length as u64 > self.cluster_size() { + if u64::from(self.header_length) > self.cluster_size() { bail!( "Header length {} over cluster size {}", self.header_length, @@ -168,7 +168,7 @@ impl QcowHeader { if self.refcount_table_clusters == 0 { bail!("Refcount table clusters is zero"); } - if self.refcount_table_clusters as u64 > MAX_REFTABLE_SIZE / self.cluster_size() { + if u64::from(self.refcount_table_clusters) > MAX_REFTABLE_SIZE / self.cluster_size() { bail!( "Refcount table size over limit {}", self.refcount_table_clusters @@ -181,7 +181,7 @@ impl QcowHeader { ); } self.refcount_table_offset - .checked_add(self.refcount_table_clusters as u64 * self.cluster_size()) + .checked_add(u64::from(self.refcount_table_clusters) * self.cluster_size()) .with_context(|| { format!( "Invalid offset {} or refcount table clusters {}", @@ -192,7 +192,7 @@ impl QcowHeader { } fn check_l1_table(&self) -> Result<()> { - if self.l1_size as u64 > MAX_L1TABLE_SIZE / ENTRY_SIZE { + if u64::from(self.l1_size) > MAX_L1TABLE_SIZE / ENTRY_SIZE { bail!("L1 table size over limit {}", self.l1_size); } if !self.cluster_aligned(self.l1_table_offset) { @@ -201,7 +201,7 @@ impl QcowHeader { let size_per_l1_entry = self.cluster_size() * self.cluster_size() / ENTRY_SIZE; let l1_need_sz = div_round_up(self.size, size_per_l1_entry).with_context(|| "Failed to get l1 size")?; - if (self.l1_size as u64) < l1_need_sz { + if u64::from(self.l1_size) < l1_need_sz { bail!( "L1 table is too small, l1 size {} expect {}", self.l1_size, @@ -209,7 +209,7 @@ impl QcowHeader { ); } self.l1_table_offset - .checked_add(self.l1_size as u64 * ENTRY_SIZE) + .checked_add(u64::from(self.l1_size) * ENTRY_SIZE) .with_context(|| { format!( "Invalid offset {} or entry size {}", diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index c8296d211..3edc388e3 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -335,8 +335,8 @@ impl Qcow2Driver { } pub fn load_refcount_table(&mut self) -> Result<()> { - let sz = - self.header.refcount_table_clusters as u64 * (self.header.cluster_size() / ENTRY_SIZE); + let sz = u64::from(self.header.refcount_table_clusters) + * (self.header.cluster_size() / ENTRY_SIZE); self.refcount.refcount_table = self .sync_aio .borrow_mut() @@ -479,7 +479,7 @@ impl Qcow2Driver { /// Extend the l1 table. pub fn grow_l1_table(&mut self, new_l1_size: u64) -> Result<()> { - let old_l1_size = self.header.l1_size as u64; + let old_l1_size = u64::from(self.header.l1_size); if new_l1_size <= old_l1_size { return Ok(()); } @@ -542,7 +542,7 @@ impl Qcow2Driver { /// Output: target entry. pub fn get_table_cluster(&mut self, guest_offset: u64) -> Result>> { let l1_index = self.table.get_l1_table_index(guest_offset); - if l1_index >= self.header.l1_size as u64 { + if l1_index >= u64::from(self.header.l1_size) { bail!("Need to grow l1 table size."); } @@ -828,11 +828,11 @@ impl Qcow2Driver { let snap = self.snapshot.snapshots[snap_id as usize].clone(); // Validate snapshot table - if snap.l1_size as u64 > MAX_L1_SIZE / ENTRY_SIZE { + if u64::from(snap.l1_size) > MAX_L1_SIZE / ENTRY_SIZE { bail!("Snapshot L1 table too large"); } - if i64::MAX as u64 - snap.l1_size as u64 * ENTRY_SIZE < snap.l1_table_offset + if i64::MAX as u64 - u64::from(snap.l1_size) * ENTRY_SIZE < snap.l1_table_offset || !is_aligned(self.header.cluster_size(), snap.l1_table_offset) { bail!("Snapshot L1 table offset invalid"); @@ -842,12 +842,12 @@ impl Qcow2Driver { let mut snap_l1_table = self .sync_aio .borrow_mut() - .read_ctrl_cluster(snap.l1_table_offset, snap.l1_size as u64)?; + .read_ctrl_cluster(snap.l1_table_offset, u64::from(snap.l1_size))?; // SAFETY: Upper limit of l1_size is decided by disk virtual size. snap_l1_table.resize(snap.l1_size as usize, 0); let cluster_size = self.header.cluster_size(); - let snap_l1_table_bytes = snap.l1_size as u64 * ENTRY_SIZE; + let snap_l1_table_bytes = u64::from(snap.l1_size) * ENTRY_SIZE; let snap_l1_table_clusters = bytes_to_clusters(snap_l1_table_bytes, cluster_size).unwrap(); let new_l1_table_offset = self.alloc_cluster(snap_l1_table_clusters, true)?; @@ -882,7 +882,7 @@ impl Qcow2Driver { // Free the snaphshot L1 table. let old_l1_table_clusters = - bytes_to_clusters(old_l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); + bytes_to_clusters(u64::from(old_l1_size) * ENTRY_SIZE, cluster_size).unwrap(); self.refcount.update_refcount( old_l1_table_offset, old_l1_table_clusters, @@ -933,7 +933,7 @@ impl Qcow2Driver { // Free the snaphshot L1 table. let l1_table_clusters = - bytes_to_clusters(snap.l1_size as u64 * ENTRY_SIZE, cluster_size).unwrap(); + bytes_to_clusters(u64::from(snap.l1_size) * ENTRY_SIZE, cluster_size).unwrap(); self.refcount.update_refcount( snap.l1_table_offset, l1_table_clusters, @@ -972,7 +972,7 @@ impl Qcow2Driver { Ok(SnapshotInfo { id: snap.id.to_string(), name: snap.name.clone(), - vm_state_size: snap.vm_state_size as u64, + vm_state_size: u64::from(snap.vm_state_size), date_sec: snap.date_sec, date_nsec: snap.date_nsec, vm_clock_nsec: snap.vm_clock_nsec, @@ -993,7 +993,7 @@ impl Qcow2Driver { // Alloc cluster and copy L1 table for snapshot. let cluster_size = self.header.cluster_size(); - let l1_table_len = self.header.l1_size as u64 * ENTRY_SIZE; + let l1_table_len = u64::from(self.header.l1_size) * ENTRY_SIZE; let l1_table_clusters = bytes_to_clusters(l1_table_len, cluster_size).unwrap(); let new_l1_table_offset = self.alloc_cluster(l1_table_clusters, true)?; self.sync_aio @@ -1272,7 +1272,7 @@ impl Qcow2Driver { _ => snap.icount.to_string(), }; - let date = get_format_time(snap.date_sec as i64); + let date = get_format_time(i64::from(snap.date_sec)); let date_str = format!( "{:04}-{:02}-{:02} {:02}:{:02}:{:02}", date[0], date[1], date[2], date[3], date[4], date[5] @@ -1985,10 +1985,10 @@ mod test { .custom_flags(libc::O_CREAT | libc::O_TRUNC) .open(path) .unwrap(); - file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) + file.set_len(cluster_sz * 3 + u64::from(header.l1_size) * ENTRY_SIZE) .unwrap(); let zero_buf = - vec![0_u8; (cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) as usize]; + vec![0_u8; (cluster_sz * 3 + u64::from(header.l1_size) * ENTRY_SIZE) as usize]; file.write_all(&zero_buf).unwrap(); file.seek(SeekFrom::Start(0)).unwrap(); file.write_all(&header.to_vec()).unwrap(); diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 0318d7e9e..67c4c1e25 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -119,7 +119,7 @@ impl RefCount { self.refcount_table_offset = header.refcount_table_offset; self.refcount_table_clusters = header.refcount_table_clusters; self.refcount_table_size = - header.refcount_table_clusters as u64 * header.cluster_size() / ENTRY_SIZE; + u64::from(header.refcount_table_clusters) * header.cluster_size() / ENTRY_SIZE; self.refcount_blk_bits = header.cluster_bits + 3 - header.refcount_order; self.refcount_blk_size = 1 << self.refcount_blk_bits; self.cluster_bits = header.cluster_bits; @@ -141,7 +141,7 @@ impl RefCount { } fn cluster_in_rc_block(&self, cluster_index: u64) -> u64 { - cluster_index & (self.refcount_blk_size - 1) as u64 + cluster_index & u64::from(self.refcount_blk_size - 1) } /// Allocate a continuous space that is not referenced by existing refcount table @@ -182,7 +182,7 @@ impl RefCount { let (table, blocks) = refcount_metadata_size( clusters, self.cluster_size, - header.refcount_order as u64, + u64::from(header.refcount_order), true, )?; self.extend_refcount_table(header, start_idx, table, blocks)?; @@ -274,7 +274,7 @@ impl RefCount { // Free the old cluster of refcount table. self.update_refcount( old_table_offset, - old_table_clusters as u64, + u64::from(old_table_clusters), -1, true, &Qcow2DiscardType::Other, @@ -443,7 +443,7 @@ impl RefCount { ) })? }; - let cluster_idx = rt_idx * self.refcount_blk_size as u64 + rb_idx + i as u64; + let cluster_idx = rt_idx * u64::from(self.refcount_blk_size) + rb_idx + i as u64; if rc_value == 0 { if self.discard_passthrough.contains(discard_type) { // update refcount discard. @@ -460,7 +460,7 @@ impl RefCount { } for (idx, rc_value) in rb_vec.iter().enumerate() { - borrowed_entry.set_entry_map(rb_idx as usize + idx, *rc_value as u64)?; + borrowed_entry.set_entry_map(rb_idx as usize + idx, u64::from(*rc_value))?; } if !is_dirty { self.refcount_blk_cache.add_dirty_table(cache_entry.clone()); @@ -829,7 +829,7 @@ mod test { let free_cluster_index = 3 + ((header.l1_size * ENTRY_SIZE as u32 + cluster_sz as u32 - 1) >> cluster_bits); let addr = qcow2.alloc_cluster(1, true).unwrap(); - assert_eq!(addr, cluster_sz * free_cluster_index as u64); + assert_eq!(addr, cluster_sz * u64::from(free_cluster_index)); qcow2.flush().unwrap(); // Check if the refcount of the cluster is updated to the disk. let mut rc_value = [0_u8; 2]; @@ -837,7 +837,7 @@ mod test { .as_ref() .read_at( &mut rc_value, - cluster_sz * 2 + 2 * free_cluster_index as u64, + cluster_sz * 2 + 2 * u64::from(free_cluster_index), ) .unwrap(); assert_eq!(1, BigEndian::read_u16(&rc_value)); diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index b5ee4ba37..7a858e597 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -281,7 +281,7 @@ impl QcowSnapshot { // Snapshot Extra data. // vm_state_size_large is used for vm snapshot. // It's equal to vm_state_size which is also 0 in disk snapshot. - BigEndian::write_u64(&mut buf[40..48], self.vm_state_size as u64); + BigEndian::write_u64(&mut buf[40..48], u64::from(self.vm_state_size)); BigEndian::write_u64(&mut buf[48..56], self.disk_size); if self.extra_data_size == SNAPSHOT_EXTRA_DATA_LEN_24 as u32 { BigEndian::write_u64(&mut buf[56..64], self.icount); diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index 6554f3fb4..c791cfa05 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -133,9 +133,9 @@ impl Qcow2Table { }; info!("Driver {} l2 cache size {}", conf.id, cache_size); let l2_table_cache: Qcow2Cache = Qcow2Cache::new(cache_size as usize); - self.cluster_bits = header.cluster_bits as u64; + self.cluster_bits = u64::from(header.cluster_bits); self.cluster_size = header.cluster_size(); - self.l2_bits = header.cluster_bits as u64 - ENTRY_BITS; + self.l2_bits = u64::from(header.cluster_bits) - ENTRY_BITS; self.l2_size = header.cluster_size() / ENTRY_SIZE; self.l2_table_cache = l2_table_cache; self.l1_table_offset = header.l1_table_offset; @@ -147,7 +147,7 @@ impl Qcow2Table { self.l1_table = self .sync_aio .borrow_mut() - .read_ctrl_cluster(self.l1_table_offset, self.l1_size as u64)?; + .read_ctrl_cluster(self.l1_table_offset, u64::from(self.l1_size))?; for l1_entry in &self.l1_table { let l1_entry_addr = l1_entry & L1_TABLE_OFFSET_MASK; self.l1_table_map.insert(l1_entry_addr, 1); diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 056d06437..0edbc75d0 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -56,12 +56,12 @@ impl RawDriver { // get_file_alignment() detects the alignment length by submitting IO to the first sector. // If this area is fallocated, misaligned IO will also return success, so we pre fill this area. pub fn alloc_first_block(&mut self, new_size: u64) -> Result<()> { - let write_size = if new_size < MAX_FILE_ALIGN as u64 { + let write_size = if new_size < u64::from(MAX_FILE_ALIGN) { SECTOR_SIZE } else { - MAX_FILE_ALIGN as u64 + u64::from(MAX_FILE_ALIGN) }; - let max_align = std::cmp::max(MAX_FILE_ALIGN as u64, host_page_size()) as usize; + let max_align = std::cmp::max(u64::from(MAX_FILE_ALIGN), host_page_size()) as usize; // SAFETY: allocate aligned memory and free it later. let align_buf = unsafe { libc::memalign(max_align, write_size as usize) }; if align_buf.is_null() { diff --git a/boot_loader/src/x86_64/direct_boot/gdt.rs b/boot_loader/src/x86_64/direct_boot/gdt.rs index 62c70be68..f5d95af32 100644 --- a/boot_loader/src/x86_64/direct_boot/gdt.rs +++ b/boot_loader/src/x86_64/direct_boot/gdt.rs @@ -119,9 +119,9 @@ pub fn setup_gdt(guest_mem: &Arc) -> Result { ]; let mut code_seg: kvm_segment = GdtEntry(gdt_table[GDT_ENTRY_BOOT_CS as usize]).into(); - code_seg.selector = GDT_ENTRY_BOOT_CS as u16 * 8; + code_seg.selector = u16::from(GDT_ENTRY_BOOT_CS) * 8; let mut data_seg: kvm_segment = GdtEntry(gdt_table[GDT_ENTRY_BOOT_DS as usize]).into(); - data_seg.selector = GDT_ENTRY_BOOT_DS as u16 * 8; + data_seg.selector = u16::from(GDT_ENTRY_BOOT_DS) * 8; write_gdt_table(&gdt_table[..], guest_mem)?; write_idt_value(0, guest_mem)?; diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index ddeede393..8aed3118f 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -66,7 +66,7 @@ fn load_bzimage(kernel_image: &mut File) -> Result { return Err(e); } - let mut setup_size = boot_hdr.setup_sects as u64; + let mut setup_size = u64::from(boot_hdr.setup_sects); if setup_size == 0 { setup_size = 4; } @@ -107,8 +107,8 @@ fn load_kernel_image( let (boot_hdr, kernel_start, vmlinux_start) = if let Ok(hdr) = load_bzimage(&mut kernel_image) { ( hdr, - hdr.code32_start as u64 + BZIMAGE_BOOT_OFFSET, - hdr.code32_start as u64, + u64::from(hdr.code32_start) + BZIMAGE_BOOT_OFFSET, + u64::from(hdr.code32_start), ) } else { ( @@ -209,7 +209,7 @@ fn setup_kernel_cmdline( sys_mem.write( &mut config.kernel_cmdline.as_bytes(), GuestAddress(CMDLINE_START), - cmdline_len as u64, + u64::from(cmdline_len), )?; Ok(()) diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 2817010ae..3f852578a 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -181,10 +181,11 @@ pub fn load_elf_kernel( let p_align = ph.p_align; let aligned_namesz = - round_up(note_hdr.namesz as u64, p_align).with_context(|| { + round_up(u64::from(note_hdr.namesz), p_align).with_context(|| { format!( "Overflows when align up: num 0x{:x}, alignment 0x{:x}", - note_hdr.namesz as u64, p_align, + u64::from(note_hdr.namesz), + p_align, ) })?; if note_hdr.type_ == XEN_ELFNOTE_PHYS32_ENTRY { @@ -195,11 +196,12 @@ pub fn load_elf_kernel( pvh_start_addr = Some(entry_addr); break; } else { - let aligned_descsz = - round_up(note_hdr.descsz as u64, p_align).with_context(|| { + let aligned_descsz = round_up(u64::from(note_hdr.descsz), p_align) + .with_context(|| { format!( "Overflows when align up, num 0x{:x}, alignment 0x{:x}", - note_hdr.descsz as u64, p_align, + u64::from(note_hdr.descsz), + p_align, ) })?; let tail_size = aligned_namesz + aligned_descsz; diff --git a/boot_loader/src/x86_64/standard_boot/mod.rs b/boot_loader/src/x86_64/standard_boot/mod.rs index 0c17349ce..5ad697bf7 100644 --- a/boot_loader/src/x86_64/standard_boot/mod.rs +++ b/boot_loader/src/x86_64/standard_boot/mod.rs @@ -59,7 +59,7 @@ fn load_kernel_image( header: &RealModeKernelHeader, fwcfg: &mut dyn FwCfgOps, ) -> Result> { - let mut setup_size = header.setup_sects as u64; + let mut setup_size = u64::from(header.setup_sects); if setup_size == 0 { setup_size = 4; } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 088d0eb0d..9bf913131 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -221,9 +221,9 @@ impl X86CPUState { /// /// * `topology` - X86 CPU Topology pub fn set_cpu_topology(&mut self, topology: &X86CPUTopology) -> Result<()> { - self.nr_threads = topology.threads as u32; - self.nr_cores = topology.cores as u32; - self.nr_dies = topology.dies as u32; + self.nr_threads = u32::from(topology.threads); + self.nr_cores = u32::from(topology.cores); + self.nr_dies = u32::from(topology.dies); Ok(()) } @@ -271,17 +271,17 @@ impl X86CPUState { pub fn setup_sregs(&mut self, sregs: Sregs, boot_config: &X86CPUBootConfig) -> Result<()> { self.sregs = sregs; - self.sregs.cs.base = (boot_config.boot_selector as u64) << 4; + self.sregs.cs.base = u64::from(boot_config.boot_selector) << 4; self.sregs.cs.selector = boot_config.boot_selector; - self.sregs.ds.base = (boot_config.boot_selector as u64) << 4; + self.sregs.ds.base = u64::from(boot_config.boot_selector) << 4; self.sregs.ds.selector = boot_config.boot_selector; - self.sregs.es.base = (boot_config.boot_selector as u64) << 4; + self.sregs.es.base = u64::from(boot_config.boot_selector) << 4; self.sregs.es.selector = boot_config.boot_selector; - self.sregs.fs.base = (boot_config.boot_selector as u64) << 4; + self.sregs.fs.base = u64::from(boot_config.boot_selector) << 4; self.sregs.fs.selector = boot_config.boot_selector; - self.sregs.gs.base = (boot_config.boot_selector as u64) << 4; + self.sregs.gs.base = u64::from(boot_config.boot_selector) << 4; self.sregs.gs.selector = boot_config.boot_selector; - self.sregs.ss.base = (boot_config.boot_selector as u64) << 4; + self.sregs.ss.base = u64::from(boot_config.boot_selector) << 4; self.sregs.ss.selector = boot_config.boot_selector; if boot_config.prot64_mode { diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 3a2a614cf..8f066a808 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -356,7 +356,7 @@ impl AmlBuilder for PowerDev { acpi_bat_dev.append_child(method); let mut bst_pkg = AmlPackage::new(4); - bst_pkg.append_child(AmlInteger(ACPI_BATTERY_STATE_CHARGING as u64)); + bst_pkg.append_child(AmlInteger(u64::from(ACPI_BATTERY_STATE_CHARGING))); bst_pkg.append_child(AmlInteger(0xFFFFFFFF)); bst_pkg.append_child(AmlInteger(0xFFFFFFFF)); bst_pkg.append_child(AmlInteger(0xFFFFFFFF)); diff --git a/devices/src/interrupt_controller/aarch64/state.rs b/devices/src/interrupt_controller/aarch64/state.rs index 926096103..a099790bd 100644 --- a/devices/src/interrupt_controller/aarch64/state.rs +++ b/devices/src/interrupt_controller/aarch64/state.rs @@ -280,7 +280,7 @@ impl GICv3 { ..Default::default() }; - let offset = dist.irq_base / (GIC_IRQ_INTERNAL as u64 / REGISTER_SIZE); + let offset = dist.irq_base / (u64::from(GIC_IRQ_INTERNAL) / REGISTER_SIZE); self.access_gic_distributor(GICD_IGROUPR + offset, &mut dist.gicd_igroupr, false)?; self.access_gic_distributor(GICD_ISENABLER + offset, &mut dist.gicd_isenabler, false)?; self.access_gic_distributor(dist.irq_base, &mut dist.line_level, false)?; @@ -290,7 +290,7 @@ impl GICv3 { // edge trigger for i in 0..NR_GICD_ICFGR { if ((i * GIC_IRQ_INTERNAL as usize / NR_GICD_ICFGR) as u64 + dist.irq_base) - > self.nr_irqs as u64 + > u64::from(self.nr_irqs) { break; } @@ -299,7 +299,7 @@ impl GICv3 { } for i in 0..NR_GICD_IPRIORITYR { - if (i as u64 * REGISTER_SIZE + dist.irq_base) > self.nr_irqs as u64 { + if (i as u64 * REGISTER_SIZE + dist.irq_base) > u64::from(self.nr_irqs) { break; } let offset = dist.irq_base + REGISTER_SIZE * i as u64; @@ -311,7 +311,7 @@ impl GICv3 { } for i in 0..NR_GICD_IROUTER { - if (i as u64 + dist.irq_base) > self.nr_irqs as u64 { + if (i as u64 + dist.irq_base) > u64::from(self.nr_irqs) { break; } let offset = dist.irq_base + i as u64; @@ -328,12 +328,12 @@ impl GICv3 { } fn set_dist(&self, mut dist: GICv3DistState) -> Result<()> { - let offset = dist.irq_base / (GIC_IRQ_INTERNAL as u64 / REGISTER_SIZE); + let offset = dist.irq_base / (u64::from(GIC_IRQ_INTERNAL) / REGISTER_SIZE); self.access_gic_distributor(GICD_ISENABLER + offset, &mut dist.gicd_isenabler, true)?; self.access_gic_distributor(GICD_IGROUPR + offset, &mut dist.gicd_igroupr, true)?; for i in 0..NR_GICD_IROUTER { - if (i as u64 + dist.irq_base) > self.nr_irqs as u64 { + if (i as u64 + dist.irq_base) > u64::from(self.nr_irqs) { break; } let offset = dist.irq_base + i as u64; @@ -349,7 +349,7 @@ impl GICv3 { // edge trigger for i in 0..NR_GICD_ICFGR { if ((i * GIC_IRQ_INTERNAL as usize / NR_GICD_ICFGR) as u64 + dist.irq_base) - > self.nr_irqs as u64 + > u64::from(self.nr_irqs) { break; } @@ -362,7 +362,7 @@ impl GICv3 { self.access_gic_distributor(GICD_ISACTIVER + offset, &mut dist.gicd_isactiver, true)?; for i in 0..NR_GICD_IPRIORITYR { - if (i as u64 * REGISTER_SIZE + dist.irq_base) > self.nr_irqs as u64 { + if (i as u64 * REGISTER_SIZE + dist.irq_base) > u64::from(self.nr_irqs) { break; } let offset = dist.irq_base + REGISTER_SIZE * i as u64; @@ -634,7 +634,7 @@ impl StateTransfer for GICv3 { .map_err(|e| MigrationError::GetGicRegsError("gicd_statusr", e.to_string()))?; for irq in (GIC_IRQ_INTERNAL..self.nr_irqs).step_by(32) { state.irq_dist[state.dist_len] = self - .get_dist(irq as u64) + .get_dist(u64::from(irq)) .map_err(|e| MigrationError::GetGicRegsError("dist", e.to_string()))?; state.dist_len += 1; } diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index 71258d935..d7a3d7019 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -596,7 +596,7 @@ impl FwCfgCommon { &mem_space, GuestAddress(dma.address), data.as_slice(), - len as u64, + u64::from(len), ) .is_err() { @@ -619,7 +619,7 @@ impl FwCfgCommon { &mem_space, GuestAddress(dma.address), &entry.data[offset as usize..], - len as u64, + u64::from(len), ) .is_err() { @@ -629,7 +629,7 @@ impl FwCfgCommon { if is_write { let mut dma_read_error = false; let data = &mut entry.data[offset as usize..]; - if read_dma_memory(&mem_space, GuestAddress(dma.address), data, len as u64) + if read_dma_memory(&mem_space, GuestAddress(dma.address), data, u64::from(len)) .is_err() { dma_read_error = true; @@ -641,7 +641,7 @@ impl FwCfgCommon { if let Some(cb) = &entry.write_cb { cb.lock().unwrap().write_callback( data.to_vec(), - offset as u64, + u64::from(offset), len as usize, ); } @@ -650,7 +650,7 @@ impl FwCfgCommon { offset += len; } dma.length -= len; - dma.address += len as u64 + dma.address += u64::from(len) } self.cur_offset = offset; @@ -705,7 +705,7 @@ impl FwCfgCommon { fn dma_mem_read(&self, addr: u64, size: u32) -> Result { extract_u64( FW_CFG_DMA_SIGNATURE as u64, - ((8 - addr - size as u64) * 8) as u32, + ((8 - addr - u64::from(size)) * 8) as u32, size * 8, ) .with_context(|| "Failed to extract bits from u64") @@ -739,7 +739,7 @@ impl FwCfgCommon { && cur_offset < entry.data.len() as u32 { loop { - value = (value << 8) | entry.data[cur_offset as usize] as u64; + value = (value << 8) | u64::from(entry.data[cur_offset as usize]); cur_offset += 1; size -= 1; @@ -747,7 +747,7 @@ impl FwCfgCommon { break; } } - value <<= 8 * size as u64; + value <<= 8 * u64::from(size); } self.cur_offset = cur_offset; @@ -955,9 +955,9 @@ impl SysBusDevOps for FwCfgMem { fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { let size = data.len() as u32; let value = match size { - 1 => data[0] as u64, - 2 => BigEndian::read_u16(data) as u64, - 4 => BigEndian::read_u32(data) as u64, + 1 => u64::from(data[0]), + 2 => u64::from(BigEndian::read_u16(data)), + 4 => u64::from(BigEndian::read_u32(data)), 8 => BigEndian::read_u64(data), _ => 0, }; @@ -1127,9 +1127,9 @@ impl SysBusDevOps for FwCfgIO { } 4..=11 => { let value = match size { - 1 => data[0] as u64, - 2 => BigEndian::read_u16(data) as u64, - 4 => BigEndian::read_u32(data) as u64, + 1 => u64::from(data[0]), + 2 => u64::from(BigEndian::read_u16(data)), + 4 => u64::from(BigEndian::read_u32(data)), 8 => BigEndian::read_u64(data), _ => 0, }; diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 5db5b9bc2..67d8aacbb 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -306,7 +306,7 @@ impl PFlash { } // Repeat data for PFlash device which supports x16-mode but works in x8-mode. for i in 1..self.max_device_width { - resp = deposit_u32(resp, 8 * i, 8, self.cfi_table[index as usize] as u32) + resp = deposit_u32(resp, 8 * i, 8, u32::from(self.cfi_table[index as usize])) .with_context(|| "Failed to deposit bits to u32")?; } } @@ -329,11 +329,11 @@ impl PFlash { } // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + size as u64 > mr.size() { + if offset + u64::from(size) > mr.size() { return Err(anyhow!(LegacyError::PFlashWriteOverflow( mr.size(), offset, - size as u64 + u64::from(size) ))); } @@ -422,7 +422,7 @@ impl PFlash { trace::pflash_write("single byte program (0)".to_string(), cmd); } 0x20 => { - let offset_mask = offset & !(self.block_len as u64 - 1); + let offset_mask = offset & !(u64::from(self.block_len) - 1); trace::pflash_write_block_erase(offset, self.block_len); if !self.read_only { let all_one = vec![0xff_u8; self.block_len as usize]; @@ -618,7 +618,7 @@ impl PFlash { } self.status |= 0x80; if self.counter == 0 { - let mask: u64 = !(self.write_blk_size as u64 - 1); + let mask: u64 = !(u64::from(self.write_blk_size) - 1); trace::pflash_write("block write finished".to_string(), self.cmd); self.write_cycle = self.write_cycle.wrapping_add(1); if !self.read_only { @@ -740,15 +740,15 @@ impl SysBusDevOps for PFlash { // 0x70: Status Register. // 0xe8: Write block. // Just read status register, return every device status in bank. - ret = self.status as u32; + ret = u32::from(self.status); if self.device_width != 0 && data_len > self.device_width { let mut shift: u32 = self.device_width * 8; while shift + self.device_width * 8 <= data_len * 8 { - ret |= (self.status as u32) << shift; + ret |= u32::from(self.status) << shift; shift += self.device_width * 8; } } else if self.device_width == 0 && data_len > 2 { - ret |= (self.status as u32) << 16; + ret |= u32::from(self.status) << 16; } trace::pflash_read_status(ret); } @@ -782,7 +782,7 @@ impl SysBusDevOps for PFlash { // combine serval queries into one response. let mut i: u32 = 0; while i < data_len { - match self.query_devid(offset + (i * self.bank_width) as u64) { + match self.query_devid(offset + u64::from(i * self.bank_width)) { Err(e) => { error!("Failed to query devid {:?}", e); break; @@ -822,7 +822,7 @@ impl SysBusDevOps for PFlash { } else { let mut i: u32 = 0; while i < data_len { - match self.query_cfi(offset + (i * self.bank_width) as u64) { + match self.query_cfi(offset + u64::from(i * self.bank_width)) { Err(e) => { error!("Failed to query devid, {:?}", e); break; diff --git a/devices/src/legacy/pl011.rs b/devices/src/legacy/pl011.rs index b656b875d..465173ba8 100644 --- a/devices/src/legacy/pl011.rs +++ b/devices/src/legacy/pl011.rs @@ -97,7 +97,7 @@ impl PL011State { fn new() -> Self { PL011State { rfifo: [0; PL011_FIFO_SIZE], - flags: (PL011_FLAG_TXFE | PL011_FLAG_RXFE) as u32, + flags: u32::from(PL011_FLAG_TXFE | PL011_FLAG_RXFE), lcr: 0, rsr: 0, cr: 0x300, @@ -174,20 +174,20 @@ impl PL011 { impl InputReceiver for PL011 { fn receive(&mut self, data: &[u8]) { - self.state.flags &= !PL011_FLAG_RXFE as u32; + self.state.flags &= u32::from(!PL011_FLAG_RXFE); for val in data { let mut slot = (self.state.read_pos + self.state.read_count) as usize; if slot >= PL011_FIFO_SIZE { slot -= PL011_FIFO_SIZE; } - self.state.rfifo[slot] = *val as u32; + self.state.rfifo[slot] = u32::from(*val); self.state.read_count += 1; trace::pl011_receive(self.state.rfifo[slot], self.state.read_count); } // If in character-mode, or in FIFO-mode and FIFO is full, trigger the interrupt. if ((self.state.lcr & 0x10) == 0) || (self.state.read_count as usize == PL011_FIFO_SIZE) { - self.state.flags |= PL011_FLAG_RXFF as u32; + self.state.flags |= u32::from(PL011_FLAG_RXFF); trace::pl011_receive_full(); } if self.state.read_count >= self.state.read_trigger { @@ -254,7 +254,7 @@ impl SysBusDevOps for PL011 { // Data register. self.unpause_rx(); - self.state.flags &= !(PL011_FLAG_RXFF as u32); + self.state.flags &= !u32::from(PL011_FLAG_RXFF); let c = self.state.rfifo[self.state.read_pos as usize]; if self.state.read_count > 0 { @@ -265,7 +265,7 @@ impl SysBusDevOps for PL011 { } } if self.state.read_count == 0 { - self.state.flags |= PL011_FLAG_RXFE as u32; + self.state.flags |= u32::from(PL011_FLAG_RXFE); } if self.state.read_count == self.state.read_trigger - 1 { self.state.int_level &= !INT_RX; @@ -317,7 +317,7 @@ impl SysBusDevOps for PL011 { 0x3f8..=0x400 => { // Register 0xFE0~0xFFC is UART Peripheral Identification Registers // and PrimeCell Identification Registers. - ret = *self.state.id.get(((offset - 0xfe0) >> 2) as usize).unwrap() as u32; + ret = u32::from(*self.state.id.get(((offset - 0xfe0) >> 2) as usize).unwrap()); } _ => { error!("Failed to read pl011: Invalid offset 0x{:x}", offset); @@ -493,7 +493,7 @@ mod test { pl011_dev.receive(&data); assert_eq!(pl011_dev.state.read_count, data.len() as u32); for i in 0..data.len() { - assert_eq!(pl011_dev.state.rfifo[i], data[i] as u32); + assert_eq!(pl011_dev.state.rfifo[i], u32::from(data[i])); } assert_eq!(pl011_dev.state.flags, 0xC0); assert_eq!(pl011_dev.state.int_level, INT_RX); diff --git a/devices/src/legacy/pl031.rs b/devices/src/legacy/pl031.rs index a206a6fee..d6a979220 100644 --- a/devices/src/legacy/pl031.rs +++ b/devices/src/legacy/pl031.rs @@ -102,7 +102,7 @@ impl PL031 { /// Get current clock value. fn get_current_value(&self) -> u32 { - (self.base_time.elapsed().as_secs() as u128 + self.tick_offset as u128) as u32 + (u128::from(self.base_time.elapsed().as_secs()) + u128::from(self.tick_offset)) as u32 } } diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 369344181..9fd4a0898 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -95,7 +95,7 @@ fn bcd_to_bin(src: u8) -> u64 { return 0_u64; } - (((src >> 4) * 10) + (src & 0x0f)) as u64 + u64::from(((src >> 4) * 10) + (src & 0x0f)) } #[allow(clippy::upper_case_acronyms)] @@ -267,7 +267,7 @@ impl RTC { /// Get current clock value. fn get_current_value(&self) -> i64 { - (self.base_time.elapsed().as_secs() as i128 + self.tick_offset as i128) as i64 + (i128::from(self.base_time.elapsed().as_secs()) + i128::from(self.tick_offset)) as i64 } fn set_rtc_cmos(&mut self, tm: libc::tm) { diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 2d1325826..16ba7b225 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -202,7 +202,7 @@ impl Device for Ivshmem { self.dev_id .store(pci_bus.generate_dev_id(self.base.devfn), Ordering::Release); let dev = Arc::new(Mutex::new(self)); - locked_bus.attach_child(dev.lock().unwrap().base.devfn as u64, dev.clone())?; + locked_bus.attach_child(u64::from(dev.lock().unwrap().base.devfn), dev.clone())?; Ok(dev) } } diff --git a/devices/src/misc/pvpanic.rs b/devices/src/misc/pvpanic.rs index 366d0f45d..23e200d0f 100644 --- a/devices/src/misc/pvpanic.rs +++ b/devices/src/misc/pvpanic.rs @@ -143,7 +143,7 @@ impl PvPanicPci { } }); - matches!(cloned_pvpanic_write.handle_event(val as u32), Ok(())) + matches!(cloned_pvpanic_write.handle_event(u32::from(val)), Ok(())) }); let bar0_region_ops = RegionOps { @@ -220,7 +220,7 @@ impl Device for PvPanicPci { .unwrap() .dev_id .store(device_id, Ordering::Release); - locked_bus.attach_child(devfn as u64, dev.clone())?; + locked_bus.attach_child(u64::from(devfn), dev.clone())?; Ok(dev) } diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 237d271a5..54ac93415 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -146,7 +146,7 @@ pub fn get_record_authority() -> bool { impl ShmemStreamHeader { pub fn check(&self, last_end: u64) -> bool { - if (self.offset as u64) < last_end { + if u64::from(self.offset) < last_end { warn!( "Guest set bad offset {} exceeds last stream buffer end {}", self.offset, last_end @@ -221,7 +221,7 @@ impl ShmemStreamFmt { } else { AUDIO_SAMPLE_RATE_48KHZ }; - sample_rate * (self.rate % WINDOWS_SAMPLE_BASE_RATE) as u32 + sample_rate * u32::from(self.rate % WINDOWS_SAMPLE_BASE_RATE) } } @@ -334,8 +334,8 @@ impl StreamData { // The recording buffer is behind the playback buffer. Thereforce, the end position of // the playback buffer must be calculted to determine whether the two buffers overlap. if dir == ScreamDirection::Record && header.play.is_started != 0 { - last_end = header.play.offset as u64 - + header.play.chunk_size as u64 * header.play.max_chunks as u64; + last_end = u64::from(header.play.offset) + + u64::from(header.play.chunk_size) * u64::from(header.play.max_chunks); } if !stream_header.check(last_end) { @@ -357,10 +357,10 @@ impl StreamData { ) -> bool { self.audio_size = stream_header.chunk_size; self.audio_base = hva - + stream_header.offset as u64 - + (stream_header.chunk_size as u64) * (self.chunk_idx as u64); + + u64::from(stream_header.offset) + + u64::from(stream_header.chunk_size) * u64::from(self.chunk_idx); - if (self.audio_base + self.audio_size as u64) > (hva + shmem_size) { + if (self.audio_base + u64::from(self.audio_size)) > (hva + shmem_size) { error!( "Scream: wrong header: offset {} chunk_idx {} chunk_size {} max_chunks {}", stream_header.offset, @@ -429,7 +429,7 @@ impl StreamData { // of the address range during the header check. let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let capt = &mut header.capt; - let addr = hva + capt.offset as u64; + let addr = hva + u64::from(capt.offset); interface.lock().unwrap().pre_receive(addr, capt); while capt.is_started != 0 { diff --git a/devices/src/pci/bus.rs b/devices/src/pci/bus.rs index 0ee811165..e4f730ab7 100644 --- a/devices/src/pci/bus.rs +++ b/devices/src/pci/bus.rs @@ -126,7 +126,7 @@ impl PciBus { /// * `bus_num` - The bus number. /// * `devfn` - Slot number << 3 | Function number. pub fn get_device(&self, bus_num: u8, devfn: u8) -> Option>> { - if let Some(dev) = self.child_dev(devfn as u64) { + if let Some(dev) = self.child_dev(u64::from(devfn)) { return Some(dev.clone()); } debug!("Can't find device {}:{}", bus_num, devfn); @@ -232,7 +232,7 @@ impl PciBus { .unrealize() .with_context(|| format!("Failed to unrealize device {}", pci_dev.name()))?; - let devfn = pci_dev.pci_base().devfn as u64; + let devfn = u64::from(pci_dev.pci_base().devfn); let mut locked_bus = bus.lock().unwrap(); locked_bus .detach_child(devfn) @@ -260,7 +260,7 @@ impl PciBus { pub fn generate_dev_id(&self, devfn: u8) -> u16 { let bus_num = self.number(SECONDARY_BUS_NUM as usize); - ((bus_num as u16) << 8) | (devfn as u16) + (u16::from(bus_num) << 8) | u16::from(devfn) } pub fn update_dev_id(&self, devfn: u8, dev_id: &Arc) { diff --git a/devices/src/pci/config.rs b/devices/src/pci/config.rs index 2f041c4fd..84bf48d15 100644 --- a/devices/src/pci/config.rs +++ b/devices/src/pci/config.rs @@ -433,11 +433,11 @@ impl PciConfig { write_mask: vec![0; config_size], write_clear_mask: vec![0; config_size], bars, - last_cap_end: PCI_CONFIG_HEAD_END as u16, + last_cap_end: u16::from(PCI_CONFIG_HEAD_END), last_ext_cap_offset: 0, last_ext_cap_end: PCI_CONFIG_SPACE_SIZE as u16, msix: None, - pci_express_cap_offset: PCI_CONFIG_HEAD_END as u16, + pci_express_cap_offset: u16::from(PCI_CONFIG_HEAD_END), intx: None, } } @@ -737,7 +737,7 @@ impl PciConfig { return BAR_SPACE_UNMAPPED; } let bar_val = le_read_u32(&self.config, offset).unwrap(); - return (bar_val & IO_BASE_ADDR_MASK) as u64; + return u64::from(bar_val & IO_BASE_ADDR_MASK); } if command & COMMAND_MEMORY_SPACE == 0 { @@ -747,7 +747,7 @@ impl PciConfig { RegionType::Io => BAR_SPACE_UNMAPPED, RegionType::Mem32Bit => { let bar_val = le_read_u32(&self.config, offset).unwrap(); - (bar_val & MEM_BASE_ADDR_MASK as u32) as u64 + u64::from(bar_val & MEM_BASE_ADDR_MASK as u32) } RegionType::Mem64Bit => { let bar_val = le_read_u64(&self.config, offset).unwrap(); @@ -1013,7 +1013,7 @@ impl PciConfig { le_write_u32( &mut self.config, offset, - id as u32 | (version << PCI_EXT_CAP_VER_SHIFT), + u32::from(id) | (version << PCI_EXT_CAP_VER_SHIFT), )?; if self.last_ext_cap_offset != 0 { let old_value = le_read_u32(&self.config, self.last_ext_cap_offset as usize)?; @@ -1040,7 +1040,7 @@ impl PciConfig { self.add_pci_cap(CapId::Pcie as u8, PCI_EXP_VER2_SIZEOF as usize)?; self.pci_express_cap_offset = cap_offset as u16; let mut offset: usize = cap_offset + PcieCap::CapReg as usize; - let pci_type = (dev_type << PCI_EXP_FLAGS_TYPE_SHIFT) as u16 & PCI_EXP_FLAGS_TYPE; + let pci_type = u16::from(dev_type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE; le_write_u16( &mut self.config, offset, @@ -1065,7 +1065,7 @@ impl PciConfig { | PCI_EXP_LNKCAP_ASPMS_0S | PCI_EXP_LNKCAP_LBNC | PCI_EXP_LNKCAP_DLLLARC - | ((port_num as u32) << PCI_EXP_LNKCAP_PN_SHIFT), + | (u32::from(port_num) << PCI_EXP_LNKCAP_PN_SHIFT), )?; offset = cap_offset + PcieCap::LinkStat as usize; le_write_u16( @@ -1085,7 +1085,7 @@ impl PciConfig { | PCI_EXP_SLTCAP_PIP | PCI_EXP_SLTCAP_HPS | PCI_EXP_SLTCAP_HPC - | ((slot as u32) << PCI_EXP_SLTCAP_PSN_SHIFT), + | (u32::from(slot) << PCI_EXP_SLTCAP_PSN_SHIFT), )?; offset = cap_offset + PcieCap::SlotCtl as usize; le_write_u16( @@ -1184,8 +1184,8 @@ impl PciConfig { fn validate_bar_size(&self, bar_type: RegionType, size: u64) -> Result<()> { if !size.is_power_of_two() || (bar_type == RegionType::Io && size < MINIMUM_BAR_SIZE_FOR_PIO as u64) - || (bar_type == RegionType::Mem32Bit && size > u32::MAX as u64) - || (bar_type == RegionType::Io && size > u16::MAX as u64) + || (bar_type == RegionType::Mem32Bit && size > u64::from(u32::MAX)) + || (bar_type == RegionType::Io && size > u64::from(u16::MAX)) { return Err(anyhow!(PciError::InvalidConf( "Bar size of type ".to_string() + &bar_type.to_string(), @@ -1276,7 +1276,7 @@ mod tests { le_write_u32( &mut pci_config.config, BAR_0 as usize, - IO_BASE_ADDR_MASK | BAR_IO_SPACE as u32, + IO_BASE_ADDR_MASK | u32::from(BAR_IO_SPACE), ) .unwrap(); le_write_u32( @@ -1288,7 +1288,7 @@ mod tests { le_write_u64( &mut pci_config.config, BAR_0 as usize + 2 * REG_SIZE, - MEM_BASE_ADDR_MASK | (BAR_MEM_64BIT | BAR_PREFETCH) as u64, + MEM_BASE_ADDR_MASK | u64::from(BAR_MEM_64BIT | BAR_PREFETCH), ) .unwrap(); @@ -1298,7 +1298,7 @@ mod tests { { // I/O space access is enabled. le_write_u16(&mut pci_config.config, COMMAND as usize, COMMAND_IO_SPACE).unwrap(); - assert_eq!(pci_config.get_bar_address(0), IO_BASE_ADDR_MASK as u64); + assert_eq!(pci_config.get_bar_address(0), u64::from(IO_BASE_ADDR_MASK)); } assert_eq!(pci_config.get_bar_address(1), BAR_SPACE_UNMAPPED); assert_eq!(pci_config.get_bar_address(2), BAR_SPACE_UNMAPPED); @@ -1344,14 +1344,14 @@ mod tests { le_write_u32( &mut pci_config.config, BAR_0 as usize, - 2048_u32 | BAR_IO_SPACE as u32, + 2048_u32 | u32::from(BAR_IO_SPACE), ) .unwrap(); le_write_u32(&mut pci_config.config, BAR_0 as usize + REG_SIZE, 2048).unwrap(); le_write_u32( &mut pci_config.config, BAR_0 as usize + 2 * REG_SIZE, - 2048_u32 | BAR_MEM_64BIT as u32 | BAR_PREFETCH as u32, + 2048_u32 | u32::from(BAR_MEM_64BIT) | u32::from(BAR_PREFETCH), ) .unwrap(); le_write_u16( @@ -1401,14 +1401,14 @@ mod tests { le_write_u32( &mut pci_config.config, BAR_0 as usize, - 4096_u32 | BAR_IO_SPACE as u32, + 4096_u32 | u32::from(BAR_IO_SPACE), ) .unwrap(); le_write_u32(&mut pci_config.config, BAR_0 as usize + REG_SIZE, 4096).unwrap(); le_write_u32( &mut pci_config.config, BAR_0 as usize + 2 * REG_SIZE, - 4096_u32 | BAR_MEM_64BIT as u32 | BAR_PREFETCH as u32, + 4096_u32 | u32::from(BAR_MEM_64BIT) | u32::from(BAR_PREFETCH), ) .unwrap(); pci_config @@ -1436,7 +1436,7 @@ mod tests { // Capbility size is not multiple of DWORD. pci_config.add_pci_cap(0x12, 10).unwrap(); - assert_eq!(pci_config.last_cap_end, PCI_CONFIG_HEAD_END as u16 + 12); + assert_eq!(pci_config.last_cap_end, u16::from(PCI_CONFIG_HEAD_END) + 12); } #[test] @@ -1542,14 +1542,14 @@ mod tests { le_write_u32( &mut pci_config.config, BAR_0 as usize, - 2048 | BAR_IO_SPACE as u32, + 2048 | u32::from(BAR_IO_SPACE), ) .unwrap(); le_write_u32(&mut pci_config.config, BAR_0 as usize + REG_SIZE, 2048).unwrap(); le_write_u32( &mut pci_config.config, BAR_0 as usize + 2 * REG_SIZE, - 2048 | BAR_MEM_64BIT as u32 | BAR_PREFETCH as u32, + 2048 | u32::from(BAR_MEM_64BIT) | u32::from(BAR_PREFETCH), ) .unwrap(); le_write_u16( diff --git a/devices/src/pci/host.rs b/devices/src/pci/host.rs index 284811570..7f3b3d78f 100644 --- a/devices/src/pci/host.rs +++ b/devices/src/pci/host.rs @@ -115,7 +115,7 @@ impl PciHost { let root_bus = self.child_bus().unwrap(); let locked_root_bus = root_bus.lock().unwrap(); if bus_num == 0 { - let dev = locked_root_bus.child_dev(devfn as u64)?; + let dev = locked_root_bus.child_dev(u64::from(devfn))?; return Some(dev.clone()); } @@ -123,7 +123,7 @@ impl PciHost { let child_bus = dev.lock().unwrap().child_bus(); if let Some(bus) = child_bus { if let Some(b) = PciBus::find_bus_by_num(&bus, bus_num) { - let dev = b.lock().unwrap().child_dev(devfn as u64)?.clone(); + let dev = b.lock().unwrap().child_dev(u64::from(devfn))?.clone(); return Some(dev); } } @@ -396,8 +396,8 @@ fn build_prt_for_aml(pci_bus: &mut AmlDevice, irq: i32) { (0..PCI_PIN_NUM).for_each(|pin| { let gsi = (pin + slot) % PCI_PIN_NUM; let mut pkg = AmlPackage::new(4); - pkg.append_child(AmlDWord((slot as u32) << 16 | 0xFFFF)); - pkg.append_child(AmlDWord(pin as u32)); + pkg.append_child(AmlDWord(u32::from(slot) << 16 | 0xFFFF)); + pkg.append_child(AmlDWord(u32::from(pin))); pkg.append_child(AmlName(format!("GSI{}", gsi))); pkg.append_child(AmlZero); prt_pkg.append_child(pkg); @@ -419,7 +419,7 @@ fn build_prt_for_aml(pci_bus: &mut AmlDevice, irq: i32) { AmlEdgeLevel::Level, AmlActiveLevel::High, AmlIntShare::Exclusive, - vec![irqs as u32], + vec![u32::from(irqs)], )); gsi.append_child(AmlNameDecl::new("_PRS", crs)); let mut crs = AmlResTemplate::new(); @@ -428,7 +428,7 @@ fn build_prt_for_aml(pci_bus: &mut AmlDevice, irq: i32) { AmlEdgeLevel::Level, AmlActiveLevel::High, AmlIntShare::Exclusive, - vec![irqs as u32], + vec![u32::from(irqs)], )); gsi.append_child(AmlNameDecl::new("_CRS", crs)); let method = AmlMethod::new("_SRS", 1, false); @@ -696,13 +696,13 @@ pub mod tests { let pci_dev = TestPciDevice::new("PCI device", 8, Arc::downgrade(&bus)); pci_dev.realize().unwrap(); - let addr: u64 = 8_u64 << ECAM_DEVFN_SHIFT | SECONDARY_BUS_NUM as u64; + let addr: u64 = 8_u64 << ECAM_DEVFN_SHIFT | u64::from(SECONDARY_BUS_NUM); let data = [1_u8]; (mmconfig_region_ops.write)(&data, GuestAddress(0), addr); let mut buf = [0_u8]; (mmconfig_region_ops.read)(&mut buf, GuestAddress(0), addr); assert_eq!(buf, data); - let addr: u64 = 16_u64 << ECAM_DEVFN_SHIFT | SECONDARY_BUS_NUM as u64; + let addr: u64 = 16_u64 << ECAM_DEVFN_SHIFT | u64::from(SECONDARY_BUS_NUM); let data = [2_u8]; (mmconfig_region_ops.write)(&data, GuestAddress(0), addr); let mut buf = [0_u8]; diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index ccf1263a2..20ba952ce 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -202,7 +202,7 @@ pub trait PciDevOps: Device + Send { /// Device id to send MSI/MSI-X. fn set_dev_id(&self, bus_num: u8, devfn: u8) -> u16 { let bus_shift: u16 = 8; - ((bus_num as u16) << bus_shift) | (devfn as u16) + (u16::from(bus_num) << bus_shift) | u16::from(devfn) } /// Get the path of the PCI bus where the device resides. @@ -334,9 +334,9 @@ pub fn init_multifunction( parent_bus: Weak>, ) -> Result<()> { let mut header_type = - le_read_u16(config, HEADER_TYPE as usize)? & (!HEADER_TYPE_MULTIFUNC as u16); + le_read_u16(config, HEADER_TYPE as usize)? & u16::from(!HEADER_TYPE_MULTIFUNC); if multifunction { - header_type |= HEADER_TYPE_MULTIFUNC as u16; + header_type |= u16::from(HEADER_TYPE_MULTIFUNC); } le_write_u16(config, HEADER_TYPE as usize, header_type)?; @@ -348,7 +348,7 @@ pub fn init_multifunction( let bus = parent_bus.upgrade().unwrap(); PCI_BUS!(bus, locked_bus, pci_bus); if pci_func(devfn) != 0 { - let dev = pci_bus.child_dev(pci_devfn(slot, 0) as u64); + let dev = pci_bus.child_dev(u64::from(pci_devfn(slot, 0))); if dev.is_none() { return Ok(()); } @@ -356,7 +356,7 @@ pub fn init_multifunction( let mut data = vec![0_u8; 2]; PCI_BUS_DEVICE!(dev.unwrap(), locked_dev, pci_dev); pci_dev.read_config(HEADER_TYPE as usize, data.as_mut_slice()); - if LittleEndian::read_u16(&data) & HEADER_TYPE_MULTIFUNC as u16 == 0 { + if LittleEndian::read_u16(&data) & u16::from(HEADER_TYPE_MULTIFUNC) == 0 { // Function 0 should set multifunction bit. bail!( "PCI: single function device can't be populated in bus {} function {}.{}", @@ -374,7 +374,10 @@ pub fn init_multifunction( // If function 0 is set to single function, the rest function should be None. for func in 1..MAX_FUNC { - if pci_bus.child_dev(pci_devfn(slot, func) as u64).is_some() { + if pci_bus + .child_dev(u64::from(pci_devfn(slot, func))) + .is_some() + { bail!( "PCI: {}.0 indicates single function, but {}.{} is already populated", slot, @@ -390,7 +393,7 @@ pub fn init_multifunction( /// PCI-to-PCI bridge specification 9.1: Interrupt routing. pub fn swizzle_map_irq(devfn: u8, pin: u8) -> u32 { let pci_slot = devfn >> 3 & 0x1f; - ((pci_slot + pin) % PCI_PIN_NUM) as u32 + u32::from((pci_slot + pin) % PCI_PIN_NUM) } #[cfg(test)] @@ -422,7 +425,7 @@ mod tests { gen_base_func!(device_base, device_base_mut, DeviceBase, base.base); fn realize(mut self) -> Result>> { - let devfn = self.base.devfn as u64; + let devfn = u64::from(self.base.devfn); self.init_write_mask(false)?; self.init_write_clear_mask(false)?; diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs index 7f960f44d..428c4e831 100644 --- a/devices/src/pci/msix.rs +++ b/devices/src/pci/msix.rs @@ -188,7 +188,7 @@ impl Msix { fn is_vector_pending(&self, vector: u16) -> bool { let offset: usize = vector as usize / 64; - let pending_bit: u64 = 1 << (vector as u64 % 64); + let pending_bit: u64 = 1 << (u64::from(vector) % 64); let value = le_read_u64(&self.pba, offset).unwrap(); if value & pending_bit > 0 { return true; @@ -198,14 +198,14 @@ impl Msix { fn set_pending_vector(&mut self, vector: u16) { let offset: usize = vector as usize / 64; - let pending_bit: u64 = 1 << (vector as u64 % 64); + let pending_bit: u64 = 1 << (u64::from(vector) % 64); let old_val = le_read_u64(&self.pba, offset).unwrap(); le_write_u64(&mut self.pba, offset, old_val | pending_bit).unwrap(); } fn clear_pending_vector(&mut self, vector: u16) { let offset: usize = vector as usize / 64; - let pending_bit: u64 = !(1 << (vector as u64 % 64)); + let pending_bit: u64 = !(1 << (u64::from(vector) % 64)); let old_val = le_read_u64(&self.pba, offset).unwrap(); le_write_u64(&mut self.pba, offset, old_val & pending_bit).unwrap(); } @@ -231,7 +231,7 @@ impl Msix { msg_data: entry.data, masked: false, #[cfg(target_arch = "aarch64")] - dev_id: self.dev_id.load(Ordering::Acquire) as u32, + dev_id: u32::from(self.dev_id.load(Ordering::Acquire)), }; let irq_manager = self.msi_irq_manager.as_ref().unwrap(); @@ -261,7 +261,7 @@ impl Msix { msg_data: entry.data, masked: false, #[cfg(target_arch = "aarch64")] - dev_id: self.dev_id.load(Ordering::Acquire) as u32, + dev_id: u32::from(self.dev_id.load(Ordering::Acquire)), }; let irq_manager = self.msi_irq_manager.as_ref().unwrap(); @@ -407,11 +407,11 @@ impl Msix { msg_data: msg.data, masked: false, #[cfg(target_arch = "aarch64")] - dev_id: dev_id as u32, + dev_id: u32::from(dev_id), }; let irq_manager = self.msi_irq_manager.as_ref().unwrap(); - if let Err(e) = irq_manager.trigger(None, msix_vector, dev_id as u32) { + if let Err(e) = irq_manager.trigger(None, msix_vector, u32::from(dev_id)) { error!("Send msix error: {:?}", e); }; } @@ -517,7 +517,7 @@ impl MigrationHook for Msix { msg_data: msg.data, masked: false, #[cfg(target_arch = "aarch64")] - dev_id: self.dev_id.load(Ordering::Acquire) as u32, + dev_id: u32::from(self.dev_id.load(Ordering::Acquire)), }; let irq_manager = self.msi_irq_manager.as_ref().unwrap(); irq_manager.allocate_irq(msi_vector)?; @@ -554,7 +554,7 @@ pub fn init_msix( ) -> Result<()> { let config = &mut pcidev_base.config; let parent_bus = pcidev_base.base.parent.as_ref().unwrap(); - if vector_nr == 0 || vector_nr > MSIX_TABLE_SIZE_MAX as u32 + 1 { + if vector_nr == 0 || vector_nr > u32::from(MSIX_TABLE_SIZE_MAX) + 1 { bail!( "invalid msix vectors, which should be in [1, {}]", MSIX_TABLE_SIZE_MAX + 1 @@ -570,8 +570,8 @@ pub fn init_msix( MSIX_CAP_FUNC_MASK | MSIX_CAP_ENABLE, )?; offset = msix_cap_offset + MSIX_CAP_TABLE as usize; - let table_size = vector_nr * MSIX_TABLE_ENTRY_SIZE as u32; - let pba_size = ((round_up(vector_nr as u64, 64).unwrap() / 64) * 8) as u32; + let table_size = vector_nr * u32::from(MSIX_TABLE_ENTRY_SIZE); + let pba_size = ((round_up(u64::from(vector_nr), 64).unwrap() / 64) * 8) as u32; let (table_offset, pba_offset) = offset_opt.unwrap_or((0, table_size)); if ranges_overlap( table_offset as usize, @@ -607,19 +607,19 @@ pub fn init_msix( msix.clone(), region, dev_id, - table_offset as u64, - pba_offset as u64, + u64::from(table_offset), + u64::from(pba_offset), )?; } else { - let mut bar_size = ((table_size + pba_size) as u64).next_power_of_two(); + let mut bar_size = u64::from(table_size + pba_size).next_power_of_two(); bar_size = max(bar_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); let region = Region::init_container_region(bar_size, "Msix_region"); Msix::register_memory_region( msix.clone(), ®ion, dev_id, - table_offset as u64, - pba_offset as u64, + u64::from(table_offset), + u64::from(pba_offset), )?; config.register_bar(bar_id, region, RegionType::Mem32Bit, false, bar_size)?; } @@ -653,7 +653,7 @@ mod tests { assert!(init_msix( &mut base, 0, - MSIX_TABLE_SIZE_MAX as u32 + 2, + u32::from(MSIX_TABLE_SIZE_MAX) + 2, Arc::new(AtomicU16::new(0)), None, None, @@ -666,7 +666,7 @@ mod tests { init_msix(&mut base, 1, 2, Arc::new(AtomicU16::new(0)), None, None).unwrap(); let pci_config = base.config; let msix_cap_start = 64_u8; - assert_eq!(pci_config.last_cap_end, 64 + MSIX_CAP_SIZE as u16); + assert_eq!(pci_config.last_cap_end, 64 + u16::from(MSIX_CAP_SIZE)); // Capabilities pointer assert_eq!(pci_config.config[0x34], msix_cap_start); assert_eq!( @@ -690,7 +690,7 @@ mod tests { fn test_mask_vectors() { let nr_vector = 2_u32; let mut msix = Msix::new( - nr_vector * MSIX_TABLE_ENTRY_SIZE as u32, + nr_vector * u32::from(MSIX_TABLE_ENTRY_SIZE), 64, 64, Arc::new(AtomicU16::new(0)), @@ -712,7 +712,7 @@ mod tests { #[test] fn test_pending_vectors() { let mut msix = Msix::new( - MSIX_TABLE_ENTRY_SIZE as u32, + u32::from(MSIX_TABLE_ENTRY_SIZE), 64, 64, Arc::new(AtomicU16::new(0)), @@ -728,7 +728,7 @@ mod tests { #[test] fn test_get_message() { let mut msix = Msix::new( - MSIX_TABLE_ENTRY_SIZE as u32, + u32::from(MSIX_TABLE_ENTRY_SIZE), 64, 64, Arc::new(AtomicU16::new(0)), diff --git a/devices/src/pci/root_port.rs b/devices/src/pci/root_port.rs index 00bffd491..168083447 100644 --- a/devices/src/pci/root_port.rs +++ b/devices/src/pci/root_port.rs @@ -180,7 +180,7 @@ impl RootPort { if locked_msix.enabled { locked_msix.notify(0, self.dev_id.load(Ordering::Acquire)); } else if self.base.config.config[INTERRUPT_PIN as usize] != 0 { - intx.lock().unwrap().notify(self.hpev_notified as u8); + intx.lock().unwrap().notify(u8::from(self.hpev_notified)); } } @@ -277,7 +277,7 @@ impl RootPort { (cap_offset + PCI_EXP_SLTSTA) as usize, ) .unwrap(); - let val: u16 = data[0] as u16 + ((data[1] as u16) << 8); + let val: u16 = u16::from(data[0]) + (u16::from(data[1]) << 8); if (val & !old_status & PCI_EXP_SLOTSTA_EVENTS) != 0 { let tmpstat = (status & !PCI_EXP_SLOTSTA_EVENTS) | (old_status & PCI_EXP_SLOTSTA_EVENTS); @@ -400,7 +400,8 @@ impl Device for RootPort { PcieDevType::RootPort as u8, )?; - self.dev_id.store(self.base.devfn as u16, Ordering::SeqCst); + self.dev_id + .store(u16::from(self.base.devfn), Ordering::SeqCst); init_msix(&mut self.base, 0, 1, self.dev_id.clone(), None, None)?; init_intx( @@ -433,7 +434,7 @@ impl Device for RootPort { child_pci_bus.base.parent = Some(Arc::downgrade(&root_port) as Weak>); child_pci_bus.hotplug_controller = Some(Arc::downgrade(&root_port) as Weak>); - parent_pci_bus.attach_child(locked_root_port.base.devfn as u64, root_port.clone())?; + parent_pci_bus.attach_child(u64::from(locked_root_port.base.devfn), root_port.clone())?; // Need to drop locked_root_port in order to register root_port instance. drop(locked_root_port); MigrationManager::register_device_instance( @@ -653,7 +654,7 @@ impl HotplugOps for RootPort { bail!("Don't support hot-unplug!"); } PCI_BUS_DEVICE!(dev, locked_dev, pci_dev); - let devfn = pci_dev.pci_base().devfn as u64; + let devfn = u64::from(pci_dev.pci_base().devfn); pci_dev.unrealize()?; let child_bus = self.child_bus().unwrap(); child_bus.lock().unwrap().detach_child(devfn)?; diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index cc7c72692..277b09ab9 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -373,7 +373,7 @@ pub enum ScsiXferMode { // Convert from (target, lun) to unique address in BusBase. pub fn get_scsi_key(target: u8, lun: u16) -> u64 { - (target as u64) << TARGET_ID_SHIFT | lun as u64 + u64::from(target) << TARGET_ID_SHIFT | u64::from(lun) } // Convert from unique address in BusBase to (target, lun). @@ -547,7 +547,7 @@ impl ScsiRequest { .with_context(|| "Too large offset IO!")?; offset - .checked_add(datalen as u64) + .checked_add(u64::from(datalen)) .filter(|&off| off <= disk_size) .with_context(|| { format!( @@ -802,8 +802,8 @@ pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) // 010b | Bytes[7-8]. | // 100b | Bytes[10-13]. | // 101b | Bytes[6-9]. | - 0 => cdb[4] as i32, - 1 | 2 => BigEndian::read_u16(&cdb[7..]) as i32, + 0 => i32::from(cdb[4]), + 1 | 2 => i32::from(BigEndian::read_u16(&cdb[7..])), 4 => BigEndian::read_u32(&cdb[10..]) as i32, 5 => BigEndian::read_u32(&cdb[6..]) as i32, _ => -1, @@ -841,8 +841,8 @@ fn scsi_cdb_lba(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i64 { // 010b | Bytes[2-5]. | // 100b | Bytes[2-9]. | // 101b | Bytes[2-5]. | - 0 => (BigEndian::read_u32(&cdb[0..]) & 0x1fffff) as i64, - 1 | 2 | 5 => BigEndian::read_u32(&cdb[2..]) as i64, + 0 => i64::from(BigEndian::read_u32(&cdb[0..]) & 0x1fffff), + 1 | 2 | 5 => i64::from(BigEndian::read_u32(&cdb[2..])), 4 => BigEndian::read_u64(&cdb[2..]) as i64, _ => -1, } @@ -992,7 +992,7 @@ fn scsi_command_emulate_vpd_page( outbuf[4] = 1; let max_xfer_length: u32 = u32::MAX / 512; BigEndian::write_u32(&mut outbuf[8..12], max_xfer_length); - BigEndian::write_u64(&mut outbuf[36..44], max_xfer_length as u64); + BigEndian::write_u64(&mut outbuf[36..44], u64::from(max_xfer_length)); buflen = outbuf.len(); } 0xb1 => { @@ -1436,7 +1436,7 @@ fn scsi_command_emulate_service_action_in_16( let block_size = scsi_dev.block_size; let mut outbuf: Vec = vec![0; 32]; let mut nb_sectors = scsi_dev.disk_sectors; - nb_sectors /= (block_size / DEFAULT_SECTOR_SIZE) as u64; + nb_sectors /= u64::from(block_size / DEFAULT_SECTOR_SIZE); nb_sectors -= 1; drop(locked_dev); @@ -1577,7 +1577,7 @@ fn scsi_command_emulate_get_configuration( // Bytes[4-5]: Reserved. // Bytes[6-7]: Current Profile. BigEndian::write_u32(&mut outbuf[0..4], 36); - let current = if scsi_dev.disk_sectors > CD_MAX_SECTORS as u64 { + let current = if scsi_dev.disk_sectors > u64::from(CD_MAX_SECTORS) { GC_PROFILE_DVD_ROM } else { GC_PROFILE_CD_ROM @@ -1600,9 +1600,9 @@ fn scsi_command_emulate_get_configuration( outbuf[10] = 0x03; outbuf[11] = 8; BigEndian::write_u16(&mut outbuf[12..14], GC_PROFILE_CD_ROM); - outbuf[14] |= (current == GC_PROFILE_CD_ROM) as u8; + outbuf[14] |= u8::from(current == GC_PROFILE_CD_ROM); BigEndian::write_u16(&mut outbuf[16..18], GC_PROFILE_DVD_ROM); - outbuf[18] |= (current == GC_PROFILE_DVD_ROM) as u8; + outbuf[18] |= u8::from(current == GC_PROFILE_DVD_ROM); // Bytes[8-n]: Feature Descriptor(s): // Bytes[20-31]: Feature 1: Core Feature: diff --git a/devices/src/smbios/smbios_table.rs b/devices/src/smbios/smbios_table.rs index f476025b0..2751e6c93 100644 --- a/devices/src/smbios/smbios_table.rs +++ b/devices/src/smbios/smbios_table.rs @@ -866,11 +866,11 @@ impl SmbiosTable { table4.header.core_count = mach_cfg.nr_cores; table4.header.core_enabled = mach_cfg.nr_cores; - table4.header.core_count2 = (mach_cfg.nr_cores as u16).to_le_bytes(); - table4.header.core_enabled2 = (mach_cfg.nr_cores as u16).to_le_bytes(); + table4.header.core_count2 = u16::from(mach_cfg.nr_cores).to_le_bytes(); + table4.header.core_enabled2 = u16::from(mach_cfg.nr_cores).to_le_bytes(); table4.header.thread_count = mach_cfg.nr_threads; - table4.header.thread_count2 = (mach_cfg.nr_threads as u16).to_le_bytes(); + table4.header.thread_count2 = u16::from(mach_cfg.nr_threads).to_le_bytes(); table4.finish(); self.entries.append(&mut table4.header.as_bytes().to_vec()); @@ -946,7 +946,7 @@ impl SmbiosTable { let start_kb = start / 1024; let end_kb = (start + size - 1) / 1024; - if start_kb < u32::MAX as u64 && end_kb < u32::MAX as u64 { + if start_kb < u64::from(u32::MAX) && end_kb < u64::from(u32::MAX) { table19.header.starting_address = (start_kb as u32).to_le_bytes(); table19.header.ending_address = (end_kb as u32).to_le_bytes(); } else { @@ -994,7 +994,7 @@ impl SmbiosTable { let smbios_sockets = mach_cfg.nr_cpus / (mach_cfg.nr_cores * mach_cfg.nr_threads); for i in 0..smbios_sockets { - self.build_type4(smbios.type4.clone(), i as u16, mach_cfg); + self.build_type4(smbios.type4.clone(), u16::from(i), mach_cfg); } let mem_num = ((mach_cfg.mem_config.mem_size + 16 * GB_SIZE - 1) / (16 * GB_SIZE)) as u16; self.build_type16(mach_cfg.mem_config.mem_size, mem_num); diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 3864464dc..0f56753b6 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -257,7 +257,7 @@ impl UsbDescriptor { let mut ifs = self.get_interfaces_descriptor(conf.interfaces.as_ref())?; config_desc.wTotalLength = - config_desc.bLength as u16 + iads.len() as u16 + ifs.len() as u16; + u16::from(config_desc.bLength) + iads.len() as u16 + ifs.len() as u16; let mut buf = config_desc.as_bytes().to_vec(); buf.append(&mut iads); @@ -369,7 +369,7 @@ impl UsbDescriptor { } fn get_bos_descriptor(&self, speed: u32) -> Result> { - let mut total = USB_DT_BOS_SIZE as u16; + let mut total = u16::from(USB_DT_BOS_SIZE); let mut cap = Vec::new(); let mut cap_num = 0; @@ -483,7 +483,7 @@ impl UsbDescriptorOps for UsbDeviceBase { for i in 0..num as usize { if desc.configs[i].config_desc.bConfigurationValue == v { self.descriptor.interface_number = - desc.configs[i].config_desc.bNumInterfaces as u32; + u32::from(desc.configs[i].config_desc.bNumInterfaces); self.descriptor.configuration_selected = Some(desc.configs[i].clone()); found = true; } diff --git a/devices/src/usb/hid.rs b/devices/src/usb/hid.rs index 5706cdda7..513005ef8 100644 --- a/devices/src/usb/hid.rs +++ b/devices/src/usb/hid.rs @@ -264,7 +264,7 @@ impl Hid { self.num -= 1; let keycode = self.keyboard.keycodes[slot as usize]; let key = keycode & 0x7f; - let index = key | ((self.keyboard.modifiers as u32 & (1 << 8)) >> 1); + let index = key | ((u32::from(self.keyboard.modifiers) & (1 << 8)) >> 1); let hid_code = HID_CODE[index as usize]; self.keyboard.modifiers &= !(1 << 8); trace::usb_convert_to_hid_code(&hid_code, &index, &key); diff --git a/devices/src/usb/keyboard.rs b/devices/src/usb/keyboard.rs index 4c6847a33..418d60434 100644 --- a/devices/src/usb/keyboard.rs +++ b/devices/src/usb/keyboard.rs @@ -152,14 +152,14 @@ impl KeyboardOpts for UsbKeyboardAdapter { let mut scan_codes = Vec::new(); let mut keycode = keycode; if keycode & SCANCODE_GREY != 0 { - scan_codes.push(SCANCODE_EMUL0 as u32); + scan_codes.push(u32::from(SCANCODE_EMUL0)); keycode &= !SCANCODE_GREY; } if !down { keycode |= SCANCODE_UP; } - scan_codes.push(keycode as u32); + scan_codes.push(u32::from(keycode)); let mut locked_kbd = self.usb_kbd.lock().unwrap(); if scan_codes.len() as u32 + locked_kbd.hid.num > QUEUE_LENGTH { diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f8dc2f90c..f5c149965 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -104,7 +104,7 @@ impl UsbEndpoint { _ => 1, }; - self.max_packet_size = size as u32 * micro_frames; + self.max_packet_size = u32::from(size) * micro_frames; } } @@ -208,9 +208,9 @@ impl UsbDeviceBase { packet: &mut UsbPacket, device_req: &UsbDeviceRequest, ) -> Result { - let value = device_req.value as u32; - let index = device_req.index as u32; - let length = device_req.length as u32; + let value = u32::from(device_req.value); + let index = u32::from(device_req.index); + let length = u32::from(device_req.length); match device_req.request_type { USB_DEVICE_IN_REQUEST => match device_req.request { USB_REQUEST_GET_DESCRIPTOR => { @@ -473,7 +473,7 @@ pub fn notify_controller(dev: &Arc>, ep_id: u8) -> Result<( locked_xhci.port_notify(&usb_port, PORTSC_PLC)?; } } - if let Err(e) = locked_xhci.wakeup_endpoint(slot_id as u32, ep_id as u32, 0) { + if let Err(e) = locked_xhci.wakeup_endpoint(u32::from(slot_id), u32::from(ep_id), 0) { error!("Failed to wakeup endpoint {:?}", e); } Ok(()) @@ -607,7 +607,7 @@ mod tests { let buf = [0_u8; 10]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_IN as u32; + packet.pid = u32::from(USB_TOKEN_IN); packet.iovecs.push(Iovec::new(hva, 4)); packet.iovecs.push(Iovec::new(hva + 4, 2)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; @@ -621,7 +621,7 @@ mod tests { let buf = [0_u8; 10]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_IN as u32; + packet.pid = u32::from(USB_TOKEN_IN); packet.iovecs.push(Iovec::new(hva, 4)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; @@ -635,7 +635,7 @@ mod tests { let buf = [0_u8; 10]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_IN as u32; + packet.pid = u32::from(USB_TOKEN_IN); packet.iovecs.push(Iovec::new(hva, 4)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; @@ -649,7 +649,7 @@ mod tests { let buf = [0_u8; 10]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_IN as u32; + packet.pid = u32::from(USB_TOKEN_IN); packet.iovecs.push(Iovec::new(hva, 10)); let mut data: Vec = vec![1, 2, 3, 4, 5, 6]; @@ -663,7 +663,7 @@ mod tests { let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_OUT as u32; + packet.pid = u32::from(USB_TOKEN_OUT); packet.iovecs.push(Iovec::new(hva, 4)); packet.iovecs.push(Iovec::new(hva + 4, 2)); @@ -678,7 +678,7 @@ mod tests { let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_OUT as u32; + packet.pid = u32::from(USB_TOKEN_OUT); packet.iovecs.push(Iovec::new(hva, 4)); packet.iovecs.push(Iovec::new(hva + 4, 2)); @@ -693,7 +693,7 @@ mod tests { let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_OUT as u32; + packet.pid = u32::from(USB_TOKEN_OUT); packet.iovecs.push(Iovec::new(hva, 4)); let mut data = [0_u8; 10]; @@ -707,7 +707,7 @@ mod tests { let buf: [u8; 10] = [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]; let hva = buf.as_ptr() as u64; let mut packet = UsbPacket::default(); - packet.pid = USB_TOKEN_OUT as u32; + packet.pid = u32::from(USB_TOKEN_OUT); packet.iovecs.push(Iovec::new(hva, 6)); let mut data = [0_u8; 2]; diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index e8765ea9b..986e67416 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -382,7 +382,7 @@ impl UsbStorage { match self.state.mode { UsbMsdMode::Cbw => { - if packet.get_iovecs_size() < CBW_SIZE as u64 { + if packet.get_iovecs_size() < u64::from(CBW_SIZE) { bail!("Bad CBW size {}", packet.get_iovecs_size()); } self.state.check_cdb_exist(false)?; @@ -436,7 +436,7 @@ impl UsbStorage { bail!("Not supported usb packet(Token_in and data_out)."); } UsbMsdMode::Csw => { - if packet.get_iovecs_size() < CSW_SIZE as u64 { + if packet.get_iovecs_size() < u64::from(CSW_SIZE) { bail!("Bad CSW size {}", packet.get_iovecs_size()); } self.state.check_cdb_exist(true)?; diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 2114f607a..4dc652a99 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -685,8 +685,8 @@ impl XhciEvent { XhciTRB { parameter: self.ptr, status: self.length | (self.ccode as u32) << EVENT_TRB_CCODE_SHIFT, - control: (self.slot_id as u32) << EVENT_TRB_SLOT_ID_SHIFT - | (self.ep_id as u32) << EVENT_TRB_EP_ID_SHIFT + control: u32::from(self.slot_id) << EVENT_TRB_SLOT_ID_SHIFT + | u32::from(self.ep_id) << EVENT_TRB_EP_ID_SHIFT | self.flags | (self.trb_type as u32) << TRB_TYPE_SHIFT, addr: 0, @@ -1056,7 +1056,7 @@ impl XhciDevice { pub fn stop(&mut self) { trace::usb_xhci_stop(); self.oper.set_usb_status_flag(USB_STS_HCH); - self.oper.cmd_ring_ctrl &= !(CMD_RING_CTRL_CRR as u64); + self.oper.cmd_ring_ctrl &= !u64::from(CMD_RING_CTRL_CRR); } pub fn running(&self) -> bool { @@ -1136,7 +1136,7 @@ impl XhciDevice { return Ok(()); } let mut evt = XhciEvent::new(TRBType::ErPortStatusChange, TRBCCode::Success); - evt.ptr = ((locked_port.port_id as u32) << PORT_EVENT_ID_SHIFT) as u64; + evt.ptr = u64::from(u32::from(locked_port.port_id) << PORT_EVENT_ID_SHIFT); self.intrs[0].lock().unwrap().send_event(&evt)?; Ok(()) } @@ -1445,7 +1445,7 @@ impl XhciDevice { let packet_id = self.generate_packet_id(); let p = Arc::new(Mutex::new(UsbPacket::new( packet_id, - USB_TOKEN_OUT as u32, + u32::from(USB_TOKEN_OUT), 0, 0, Vec::new(), @@ -1459,8 +1459,10 @@ impl XhciDevice { fn get_device_context_addr(&self, slot_id: u32) -> Result { self.oper .dcbaap - .checked_add((8 * slot_id) as u64) - .with_context(|| UsbError::MemoryAccessOverflow(self.oper.dcbaap, (8 * slot_id) as u64)) + .checked_add(u64::from(8 * slot_id)) + .with_context(|| { + UsbError::MemoryAccessOverflow(self.oper.dcbaap, u64::from(8 * slot_id)) + }) } fn configure_endpoint(&mut self, slot_id: u32, trb: &XhciTRB) -> Result { @@ -1658,7 +1660,7 @@ impl XhciDevice { output_ctx: DmaAddr, ) -> Result { trace::usb_xhci_enable_endpoint(&slot_id, &ep_id); - let entry_offset = (ep_id - 1) as u64 * EP_INPUT_CTX_ENTRY_SIZE; + let entry_offset = u64::from(ep_id - 1) * EP_INPUT_CTX_ENTRY_SIZE; let mut ep_ctx = XhciEpCtx::default(); dma_read_u32( &self.mem_space, @@ -2076,16 +2078,17 @@ impl XhciDevice { let epctx = &self.slots[(xfer.slotid - 1) as usize].endpoints[(xfer.epid - 1) as usize]; if xfer.td[0].control & TRB_TR_SIA != 0 { - let asap = ((mfindex as u32 + epctx.interval - 1) & !(epctx.interval - 1)) as u64; - if asap >= epctx.mfindex_last && asap <= epctx.mfindex_last + epctx.interval as u64 * 4 + let asap = u64::from((mfindex as u32 + epctx.interval - 1) & !(epctx.interval - 1)); + if asap >= epctx.mfindex_last + && asap <= epctx.mfindex_last + u64::from(epctx.interval) * 4 { - xfer.mfindex_kick = epctx.mfindex_last + epctx.interval as u64; + xfer.mfindex_kick = epctx.mfindex_last + u64::from(epctx.interval); } else { xfer.mfindex_kick = asap; } } else { xfer.mfindex_kick = - (((xfer.td[0].control >> TRB_TR_FRAMEID_SHIFT) & TRB_TR_FRAMEID_MASK) as u64) << 3; + u64::from((xfer.td[0].control >> TRB_TR_FRAMEID_SHIFT) & TRB_TR_FRAMEID_MASK) << 3; xfer.mfindex_kick |= mfindex & !(MFINDEX_WRAP_NUM - 1); if xfer.mfindex_kick + 0x100 < mfindex { xfer.mfindex_kick += MFINDEX_WRAP_NUM; @@ -2213,7 +2216,7 @@ impl XhciDevice { self.mem_space.get_address_map( &None, GuestAddress(dma_addr), - chunk as u64, + u64::from(chunk), &mut vec, )?; } @@ -2232,7 +2235,7 @@ impl XhciDevice { let xfer_ops = Arc::downgrade(xfer) as Weak>; let packet = UsbPacket::new( packet_id, - dir as u32, + u32::from(dir), ep_number, stream, vec, @@ -2373,7 +2376,10 @@ impl XhciDevice { let base_addr = GuestAddress(addr64_from_u32(seg.addr_lo, seg.addr_hi)); // SAFETY: seg size is a 16 bit register, will not overflow. let er_len = seg.size * TRB_SIZE; - if !self.mem_space.address_in_memory(base_addr, er_len as u64) { + if !self + .mem_space + .address_in_memory(base_addr, u64::from(er_len)) + { bail!("The event ring does not locate in guest ram"); } @@ -2508,7 +2514,7 @@ pub fn dma_write_u32( } fn addr64_from_u32(low: u32, high: u32) -> u64 { - (((high << 16) as u64) << 16) | low as u64 + (u64::from(high << 16) << 16) | u64::from(low) } // | ep id | < = > | ep direction | ep number | diff --git a/devices/src/usb/xhci/xhci_pci.rs b/devices/src/usb/xhci/xhci_pci.rs index 602cf136b..f91461e3d 100644 --- a/devices/src/usb/xhci/xhci_pci.rs +++ b/devices/src/usb/xhci/xhci_pci.rs @@ -120,7 +120,7 @@ impl XhciPciDevice { xhci: XhciDevice::new(mem_space, config), dev_id: Arc::new(AtomicU16::new(0)), mem_region: Region::init_container_region( - XHCI_PCI_CONFIG_LENGTH as u64, + u64::from(XHCI_PCI_CONFIG_LENGTH), "XhciPciContainer", ), doorbell_fd: Arc::new(create_new_eventfd().unwrap()), @@ -131,57 +131,57 @@ impl XhciPciDevice { fn mem_region_init(&mut self) -> Result<()> { let cap_region = Region::init_io_region( - XHCI_PCI_CAP_LENGTH as u64, + u64::from(XHCI_PCI_CAP_LENGTH), build_cap_ops(&self.xhci), "XhciPciCapRegion", ); self.mem_region - .add_subregion(cap_region, XHCI_PCI_CAP_OFFSET as u64) + .add_subregion(cap_region, u64::from(XHCI_PCI_CAP_OFFSET)) .with_context(|| "Failed to register cap region.")?; let mut oper_region = Region::init_io_region( - XHCI_PCI_OPER_LENGTH as u64, + u64::from(XHCI_PCI_OPER_LENGTH), build_oper_ops(&self.xhci), "XhciPciOperRegion", ); oper_region.set_access_size(4); self.mem_region - .add_subregion(oper_region, XHCI_PCI_OPER_OFFSET as u64) + .add_subregion(oper_region, u64::from(XHCI_PCI_OPER_OFFSET)) .with_context(|| "Failed to register oper region.")?; let port_num = self.xhci.lock().unwrap().usb_ports.len(); for i in 0..port_num { let port = &self.xhci.lock().unwrap().usb_ports[i]; let port_region = Region::init_io_region( - XHCI_PCI_PORT_LENGTH as u64, + u64::from(XHCI_PCI_PORT_LENGTH), build_port_ops(port), "XhciPciPortRegion", ); - let offset = (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i as u32) as u64; + let offset = u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * i as u32); self.mem_region .add_subregion(port_region, offset) .with_context(|| "Failed to register port region.")?; } let mut runtime_region = Region::init_io_region( - XHCI_PCI_RUNTIME_LENGTH as u64, + u64::from(XHCI_PCI_RUNTIME_LENGTH), build_runtime_ops(&self.xhci), "XhciPciRuntimeRegion", ); runtime_region.set_access_size(4); self.mem_region - .add_subregion(runtime_region, XHCI_PCI_RUNTIME_OFFSET as u64) + .add_subregion(runtime_region, u64::from(XHCI_PCI_RUNTIME_OFFSET)) .with_context(|| "Failed to register runtime region.")?; let doorbell_region = Region::init_io_region( - XHCI_PCI_DOORBELL_LENGTH as u64, + u64::from(XHCI_PCI_DOORBELL_LENGTH), build_doorbell_ops(&self.xhci), "XhciPciDoorbellRegion", ); doorbell_region.set_ioeventfds(&self.ioeventfds()); self.mem_region - .add_subregion(doorbell_region, XHCI_PCI_DOORBELL_OFFSET as u64) + .add_subregion(doorbell_region, u64::from(XHCI_PCI_DOORBELL_OFFSET)) .with_context(|| "Failed to register doorbell region.")?; Ok(()) } @@ -276,7 +276,8 @@ impl Device for XhciPciDevice { PCI_SERIAL_BUS_RELEASE_VERSION_3_0; self.base.config.config[PCI_FRAME_LENGTH_ADJUSTMENT as usize] = PCI_NO_FRAME_LENGTH_TIMING_CAP; - self.dev_id.store(self.base.devfn as u16, Ordering::SeqCst); + self.dev_id + .store(u16::from(self.base.devfn), Ordering::SeqCst); self.mem_region_init()?; let handler = Arc::new(Mutex::new(DoorbellHandler::new( @@ -308,7 +309,7 @@ impl Device for XhciPciDevice { self.base.devfn, )?; - let mut mem_region_size = (XHCI_PCI_CONFIG_LENGTH as u64).next_power_of_two(); + let mut mem_region_size = u64::from(XHCI_PCI_CONFIG_LENGTH).next_power_of_two(); mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); self.base.config.register_bar( 0_usize, @@ -318,7 +319,7 @@ impl Device for XhciPciDevice { mem_region_size, )?; - let devfn = self.base.devfn as u64; + let devfn = u64::from(self.base.devfn); // It is safe to unwrap, because it is initialized in init_msix. let cloned_msix = self.base.config.msix.as_ref().unwrap().clone(); let cloned_intx = self.base.config.intx.as_ref().unwrap().clone(); diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 28186170d..7062d52c7 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -141,7 +141,7 @@ impl XhciOperReg { /// Run the command ring. pub fn start_cmd_ring(&mut self) { - self.cmd_ring_ctrl |= CMD_RING_CTRL_CRR as u64; + self.cmd_ring_ctrl |= u64::from(CMD_RING_CTRL_CRR); } pub fn set_usb_cmd(&mut self, value: u32) { @@ -247,11 +247,11 @@ impl XhciInterrupter { pub fn send_event(&mut self, evt: &XhciEvent) -> Result<()> { let er_end = self .er_start - .checked_add((TRB_SIZE * self.er_size) as u64) + .checked_add(u64::from(TRB_SIZE * self.er_size)) .ok_or_else(|| { UsbError::MemoryAccessOverflow( self.er_start.raw_value(), - (TRB_SIZE * self.er_size) as u64, + u64::from(TRB_SIZE * self.er_size), ) })?; if self.erdp < self.er_start.raw_value() || self.erdp >= er_end.raw_value() { @@ -262,12 +262,12 @@ impl XhciInterrupter { self.er_size ); } - let dp_idx = (self.erdp - self.er_start.raw_value()) / TRB_SIZE as u64; - if ((self.er_ep_idx + 2) % self.er_size) as u64 == dp_idx { + let dp_idx = (self.erdp - self.er_start.raw_value()) / u64::from(TRB_SIZE); + if u64::from((self.er_ep_idx + 2) % self.er_size) == dp_idx { debug!("Event ring full error, idx {}", dp_idx); let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); self.write_event(&event)?; - } else if ((self.er_ep_idx + 1) % self.er_size) as u64 == dp_idx { + } else if u64::from((self.er_ep_idx + 1) % self.er_size) == dp_idx { debug!("Event Ring full, drop Event."); } else { self.write_event(evt)?; @@ -336,11 +336,11 @@ impl XhciInterrupter { fn write_trb(&mut self, trb: &XhciTRB) -> Result<()> { let addr = self .er_start - .checked_add((TRB_SIZE * self.er_ep_idx) as u64) + .checked_add(u64::from(TRB_SIZE * self.er_ep_idx)) .ok_or_else(|| { UsbError::MemoryAccessOverflow( self.er_start.raw_value(), - (TRB_SIZE * self.er_ep_idx) as u64, + u64::from(TRB_SIZE * self.er_ep_idx), ) })?; let cycle = trb.control as u8; @@ -375,7 +375,7 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_VERSION << hci_version_offset | XHCI_CAP_LENGTH } XHCI_CAP_REG_HCSPARAMS1 => { - (max_ports as u32) << CAP_HCSP_NP_SHIFT + u32::from(max_ports) << CAP_HCSP_NP_SHIFT | max_intrs << CAP_HCSP_NI_SHIFT | (locked_dev.slots.len() as u32) << CAP_HCSP_NDS_SHIFT } @@ -396,18 +396,18 @@ pub fn build_cap_ops(xhci_dev: &Arc>) -> RegionOps { 0x20 => { CAP_EXT_USB_REVISION_2_0 << CAP_EXT_REVISION_SHIFT | 0x4 << CAP_EXT_NEXT_CAP_POINTER_SHIFT - | CAP_EXT_CAP_ID_SUPPORT_PROTOCOL as u32 + | u32::from(CAP_EXT_CAP_ID_SUPPORT_PROTOCOL) } 0x24 => CAP_EXT_USB_NAME_STRING, - 0x28 => ((locked_dev.numports_2 as u32) << 8) | 1, + 0x28 => (u32::from(locked_dev.numports_2) << 8) | 1, 0x2c => 0x0, // Extended capabilities (USB 3.0) 0x30 => { CAP_EXT_USB_REVISION_3_0 << CAP_EXT_REVISION_SHIFT - | CAP_EXT_CAP_ID_SUPPORT_PROTOCOL as u32 + | u32::from(CAP_EXT_CAP_ID_SUPPORT_PROTOCOL) } 0x34 => CAP_EXT_USB_NAME_STRING, - 0x38 => ((locked_dev.numports_3 as u32) << 8) | (locked_dev.numports_2 + 1) as u32, + 0x38 => (u32::from(locked_dev.numports_3) << 8) | u32::from(locked_dev.numports_2 + 1), 0x3c => 0x0, _ => { error!("Failed to read xhci cap: not implemented"); @@ -515,7 +515,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { write_u64_low(locked_xhci.oper.cmd_ring_ctrl, crc_lo); } XHCI_OPER_REG_CMD_RING_CTRL_HI => { - let crc_hi = (value as u64) << 32; + let crc_hi = u64::from(value) << 32; let mut crc_lo = read_u32(locked_xhci.oper.cmd_ring_ctrl, 0); if crc_lo & (CMD_RING_CTRL_CA | CMD_RING_CTRL_CS) != 0 && (crc_lo & CMD_RING_CTRL_CRR) == CMD_RING_CTRL_CRR @@ -527,7 +527,7 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { error!("Failed to send event: {:?}", e); } } else { - let addr = (crc_hi | crc_lo as u64) & XHCI_CRCR_CRP_MASK; + let addr = (crc_hi | u64::from(crc_lo)) & XHCI_CRCR_CRP_MASK; locked_xhci.cmd_ring.init(addr); } crc_lo &= !(CMD_RING_CTRL_CA | CMD_RING_CTRL_CS); @@ -648,21 +648,21 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { let erdp = locked_intr.erdp; let er_end = if let Some(addr) = locked_intr .er_start - .checked_add((TRB_SIZE * locked_intr.er_size) as u64) + .checked_add(u64::from(TRB_SIZE * locked_intr.er_size)) { addr } else { error!( "Memory access overflow, addr {:x} offset {:x}", locked_intr.er_start.raw_value(), - (TRB_SIZE * locked_intr.er_size) as u64 + u64::from(TRB_SIZE * locked_intr.er_size) ); return false; }; if erdp >= locked_intr.er_start.raw_value() && erdp < er_end.raw_value() - && (erdp - locked_intr.er_start.raw_value()) / TRB_SIZE as u64 - != locked_intr.er_ep_idx as u64 + && (erdp - locked_intr.er_start.raw_value()) / u64::from(TRB_SIZE) + != u64::from(locked_intr.er_ep_idx) { drop(locked_intr); xhci.intrs[idx as usize].lock().unwrap().send_intr(); diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 6fd9d85c8..86deba806 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -101,8 +101,8 @@ impl XhciCommandRing { self.ccs = !self.ccs; } } else { - self.dequeue = self.dequeue.checked_add(TRB_SIZE as u64).ok_or( - UsbError::MemoryAccessOverflow(self.dequeue, TRB_SIZE as u64), + self.dequeue = self.dequeue.checked_add(u64::from(TRB_SIZE)).ok_or( + UsbError::MemoryAccessOverflow(self.dequeue, u64::from(TRB_SIZE)), )?; return Ok(Some(trb)); } @@ -181,8 +181,8 @@ impl XhciTransferRing { } else { td.push(trb); dequeue = dequeue - .checked_add(TRB_SIZE as u64) - .ok_or(UsbError::MemoryAccessOverflow(dequeue, TRB_SIZE as u64))?; + .checked_add(u64::from(TRB_SIZE)) + .ok_or(UsbError::MemoryAccessOverflow(dequeue, u64::from(TRB_SIZE)))?; if trb_type == TRBType::TrSetup { ctrl_td = true; } else if trb_type == TRBType::TrStatus { @@ -214,7 +214,7 @@ impl XhciTransferRing { pub fn update_dequeue_to_ctx(&self, ctx: &mut [u32]) { let dequeue = self.get_dequeue_ptr(); - ctx[0] = dequeue as u32 | self.get_cycle_bit() as u32; + ctx[0] = dequeue as u32 | u32::from(self.get_cycle_bit()); ctx[1] = (dequeue >> 32) as u32; } } diff --git a/hypervisor/src/kvm/aarch64/gicv2.rs b/hypervisor/src/kvm/aarch64/gicv2.rs index abe70dd4f..58758142d 100644 --- a/hypervisor/src/kvm/aarch64/gicv2.rs +++ b/hypervisor/src/kvm/aarch64/gicv2.rs @@ -84,10 +84,10 @@ impl GICv2Access for KvmGICv2 { } fn vcpu_gicr_attr(&self, offset: u64, cpu: usize) -> u64 { - (((cpu as u64) << kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT as u64) + (((cpu as u64) << u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_SHIFT)) & kvm_bindings::KVM_DEV_ARM_VGIC_CPUID_MASK) - | ((offset << kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT as u64) - & kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK as u64) + | ((offset << u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_SHIFT)) + & u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_OFFSET_MASK)) } fn access_gic_distributor(&self, offset: u64, gicd_value: &mut u32, write: bool) -> Result<()> { @@ -122,7 +122,7 @@ impl GICv2Access for KvmGICv2 { KvmDevice::kvm_device_access( &self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES), 0, true, ) diff --git a/hypervisor/src/kvm/aarch64/gicv3.rs b/hypervisor/src/kvm/aarch64/gicv3.rs index 942c2b6e6..5f8c9ede4 100644 --- a/hypervisor/src/kvm/aarch64/gicv3.rs +++ b/hypervisor/src/kvm/aarch64/gicv3.rs @@ -56,7 +56,7 @@ impl GICv3Access for KvmGICv3 { KvmDevice::kvm_device_check( &self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION as u64, + u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION), ) .with_context(|| { "Multiple redistributors are acquired while KVM does not provide support." @@ -196,7 +196,7 @@ impl GICv3Access for KvmGICv3 { KvmDevice::kvm_device_access( &self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES as u64, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES), 0, true, ) @@ -259,7 +259,7 @@ impl GICv3ItsAccess for KvmGICv3Its { KvmDevice::kvm_device_access( &self.fd, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - attr as u64, + u64::from(attr), its_value as *const u64 as u64, write, ) @@ -267,9 +267,9 @@ impl GICv3ItsAccess for KvmGICv3Its { fn access_gic_its_tables(&self, save: bool) -> Result<()> { let attr = if save { - kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES as u64 + u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES) } else { - kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES as u64 + u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES) }; KvmDevice::kvm_device_access( &self.fd, diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 3befdd7c7..3e6c84619 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -95,7 +95,7 @@ impl KvmCpu { pub fn arch_init_pmu(&self) -> Result<()> { let pmu_attr = kvm_device_attr { group: KVM_ARM_VCPU_PMU_V3_CTRL, - attr: KVM_ARM_VCPU_PMU_V3_INIT as u64, + attr: u64::from(KVM_ARM_VCPU_PMU_V3_INIT), addr: 0, flags: 0, }; @@ -108,7 +108,7 @@ impl KvmCpu { let irq = PMU_INTR + PPI_BASE; let pmu_irq_attr = kvm_device_attr { group: KVM_ARM_VCPU_PMU_V3_CTRL, - attr: KVM_ARM_VCPU_PMU_V3_IRQ as u64, + attr: u64::from(KVM_ARM_VCPU_PMU_V3_IRQ), addr: &irq as *const u32 as u64, flags: 0, }; @@ -302,8 +302,11 @@ impl KvmCpu { } RegsIndex::VtimerCount => { if locked_arch_cpu.vtimer_cnt_valid { - self.set_one_reg(KVM_REG_ARM_TIMER_CNT, locked_arch_cpu.vtimer_cnt as u128) - .with_context(|| "Failed to set virtual timer count")?; + self.set_one_reg( + KVM_REG_ARM_TIMER_CNT, + u128::from(locked_arch_cpu.vtimer_cnt), + ) + .with_context(|| "Failed to set virtual timer count")?; locked_arch_cpu.vtimer_cnt_valid = false; } } @@ -362,24 +365,36 @@ impl KvmCpu { /// * `vcpu_fd` - the VcpuFd in KVM mod. /// * `core_regs` - kvm_regs state to be written. fn set_core_regs(&self, core_regs: kvm_regs) -> Result<()> { - self.set_one_reg(Arm64CoreRegs::UserPTRegSp.into(), core_regs.regs.sp as u128)?; - self.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), core_regs.sp_el1 as u128)?; + self.set_one_reg( + Arm64CoreRegs::UserPTRegSp.into(), + u128::from(core_regs.regs.sp), + )?; + self.set_one_reg(Arm64CoreRegs::KvmSpEl1.into(), u128::from(core_regs.sp_el1))?; self.set_one_reg( Arm64CoreRegs::UserPTRegPState.into(), - core_regs.regs.pstate as u128, + u128::from(core_regs.regs.pstate), + )?; + self.set_one_reg( + Arm64CoreRegs::UserPTRegPc.into(), + u128::from(core_regs.regs.pc), + )?; + self.set_one_reg( + Arm64CoreRegs::KvmElrEl1.into(), + u128::from(core_regs.elr_el1), )?; - self.set_one_reg(Arm64CoreRegs::UserPTRegPc.into(), core_regs.regs.pc as u128)?; - self.set_one_reg(Arm64CoreRegs::KvmElrEl1.into(), core_regs.elr_el1 as u128)?; for i in 0..KVM_NR_REGS as usize { self.set_one_reg( Arm64CoreRegs::UserPTRegRegs(i).into(), - core_regs.regs.regs[i] as u128, + u128::from(core_regs.regs.regs[i]), )?; } for i in 0..KVM_NR_SPSR as usize { - self.set_one_reg(Arm64CoreRegs::KvmSpsr(i).into(), core_regs.spsr[i] as u128)?; + self.set_one_reg( + Arm64CoreRegs::KvmSpsr(i).into(), + u128::from(core_regs.spsr[i]), + )?; } // State save and restore is not supported for SVE for now, so we just skip it. @@ -394,11 +409,11 @@ impl KvmCpu { self.set_one_reg( Arm64CoreRegs::UserFPSIMDStateFpsr.into(), - core_regs.fp_regs.fpsr as u128, + u128::from(core_regs.fp_regs.fpsr), )?; self.set_one_reg( Arm64CoreRegs::UserFPSIMDStateFpcr.into(), - core_regs.fp_regs.fpcr as u128, + u128::from(core_regs.fp_regs.fpcr), )?; Ok(()) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index d8a8da8f6..e7e75d7f6 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -229,7 +229,7 @@ impl HypervisorOps for KvmHypervisor { .vm_fd .as_ref() .unwrap() - .create_vcpu(vcpu_id as u64) + .create_vcpu(u64::from(vcpu_id)) .with_context(|| "Create vcpu failed")?; Ok(Arc::new(KvmCpu::new( vcpu_id, diff --git a/hypervisor/src/test/mod.rs b/hypervisor/src/test/mod.rs index 7120bb9e6..06f3e9314 100644 --- a/hypervisor/src/test/mod.rs +++ b/hypervisor/src/test/mod.rs @@ -153,7 +153,7 @@ impl CPUHypervisorOps for TestCpu { ) -> Result<()> { #[cfg(target_arch = "aarch64")] { - arch_cpu.lock().unwrap().mpidr = self.id as u64; + arch_cpu.lock().unwrap().mpidr = u64::from(self.id); arch_cpu.lock().unwrap().set_core_reg(boot_config); } Ok(()) @@ -390,8 +390,8 @@ impl MsiIrqManager for TestInterruptManager { _dev_id: u32, ) -> Result<()> { let data = vector.msg_data; - let mut addr: u64 = vector.msg_addr_hi as u64; - addr = (addr << 32) + vector.msg_addr_lo as u64; + let mut addr: u64 = u64::from(vector.msg_addr_hi); + addr = (addr << 32) + u64::from(vector.msg_addr_lo); add_msix_msg(addr, data); Ok(()) } diff --git a/image/src/img.rs b/image/src/img.rs index 3d4738a44..fcfebffba 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -600,7 +600,7 @@ mod test { let mut buf = vec![0; QcowHeader::len()]; assert!(file.read_at(&mut buf, 0).is_ok()); let header = QcowHeader::from_vec(&buf).unwrap(); - assert_eq!(header.cluster_bits as u64, cluster_bits); + assert_eq!(u64::from(header.cluster_bits), cluster_bits); Self { header, @@ -897,14 +897,14 @@ mod test { let file_len = test_image.file_len(); let l1_size = test_image.header.l1_size; let reftable_clusters = test_image.header.refcount_table_clusters; - let reftable_size = reftable_clusters as u64 * cluster_size / ENTRY_SIZE; + let reftable_size = u64::from(reftable_clusters) * cluster_size / ENTRY_SIZE; let refblock_size = cluster_size / (refcount_bits / 8); assert_ne!(l1_size, 0); assert_ne!(reftable_clusters, 0); - assert!(l1_size as u64 * cluster_size * cluster_size / ENTRY_SIZE >= image_size); + assert!(u64::from(l1_size) * cluster_size * cluster_size / ENTRY_SIZE >= image_size); assert!(reftable_size * refblock_size * cluster_size >= file_len); - assert_eq!(test_image.header.cluster_bits as u64, cluster_bits); + assert_eq!(u64::from(test_image.header.cluster_bits), cluster_bits); assert_eq!(test_image.header.size, image_size); // Check refcount. diff --git a/machine/src/aarch64/fdt.rs b/machine/src/aarch64/fdt.rs index 7bf0c2c92..f015ad1ff 100644 --- a/machine/src/aarch64/fdt.rs +++ b/machine/src/aarch64/fdt.rs @@ -311,7 +311,7 @@ impl CompileFDTHelper for MachineBase { let dist: u32 = if id as u32 == *i { 10 } else if let Some(distance) = distances.get(i) { - *distance as u32 + u32::from(*distance) } else { 20 }; diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 74d24c52f..693635070 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -242,7 +242,7 @@ impl StdMachine { if self.base.cpu_topo.threads > 1 { let core_offset = pptt.table_len(); let core_hierarchy_node = - ProcessorHierarchyNode::new(0x0, cluster_offset, core as u32, 3); + ProcessorHierarchyNode::new(0x0, cluster_offset, u32::from(core), 3); pptt.append_child(&core_hierarchy_node.aml_bytes()); processor_append_priv_res(pptt, priv_resources); for _thread in 0..self.base.cpu_topo.threads { @@ -264,7 +264,7 @@ impl StdMachine { for cluster in 0..self.base.cpu_topo.clusters { let cluster_offset = pptt.table_len(); let cluster_hierarchy_node = - ProcessorHierarchyNode::new(0x0, socket_offset, cluster as u32, 0); + ProcessorHierarchyNode::new(0x0, socket_offset, u32::from(cluster), 0); pptt.append_child(&cluster_hierarchy_node.aml_bytes()); self.build_pptt_cores(pptt, cluster_offset as u32, uid); } @@ -277,7 +277,7 @@ impl StdMachine { pptt.append_child(&cache_hierarchy_node.aml_bytes()); let socket_offset = pptt.table_len(); - let socket_hierarchy_node = ProcessorHierarchyNode::new(0x1, 0, socket as u32, 1); + let socket_hierarchy_node = ProcessorHierarchyNode::new(0x1, 0, u32::from(socket), 1); pptt.append_child(&socket_hierarchy_node.aml_bytes()); processor_append_priv_res(pptt, priv_resources); @@ -923,7 +923,7 @@ impl AcpiBuilder for StdMachine { // Mapping counts of Root Complex Node iort.set_field(80, 1_u32); // Mapping offset of Root Complex Node - iort.set_field(84, ROOT_COMPLEX_ENTRY_SIZE as u32); + iort.set_field(84, u32::from(ROOT_COMPLEX_ENTRY_SIZE)); // Cache of coherent device iort.set_field(88, 1_u32); // Memory flags of coherent device @@ -1087,7 +1087,7 @@ impl AcpiBuilder for StdMachine { type_id: 3_u8, length: size_of::() as u8, proximity_domain, - process_uid: *cpu as u32, + process_uid: u32::from(*cpu), flags: 1, clock_domain: 0_u32, } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fbb6f14eb..c2774570c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -259,7 +259,7 @@ impl MachineBase { use crate::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET; let count = data.len() as u64; - if addr == SLEEP_CTRL_OFFSET as u64 { + if addr == u64::from(SLEEP_CTRL_OFFSET) { if let Err(e) = self.cpus[0].pause() { log::error!("Fail to pause bsp, {:?}", e); } @@ -1176,7 +1176,7 @@ pub trait MachineOps: MachineLifecycle { vm_config.machine_config.nr_cpus, MAX_VIRTIO_QUEUE, ); - device_cfg.num_queues = Some(queues_auto as u32); + device_cfg.num_queues = Some(u32::from(queues_auto)); } let device = Arc::new(Mutex::new(ScsiCntlr::new(device_cfg.clone()))); diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 4af265d9a..64f113844 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -412,7 +412,7 @@ pub(crate) trait AcpiBuilder { table_end - table_begin, )?; - Ok(table_begin as u64) + Ok(u64::from(table_begin)) } /// Build ACPI DSDT table, returns the offset of ACPI DSDT table in `acpi_data`. @@ -583,7 +583,7 @@ pub(crate) trait AcpiBuilder { mcfg_begin, mcfg_end - mcfg_begin, )?; - Ok(mcfg_begin as u64) + Ok(u64::from(mcfg_begin)) } /// Build ACPI FADT table, returns the offset of ACPI FADT table in `acpi_data`. @@ -646,24 +646,24 @@ pub(crate) trait AcpiBuilder { // Reset Register bit, offset is 116. fadt.set_field(116, 0x01_u8); fadt.set_field(117, 0x08_u8); - fadt.set_field(120, RST_CTRL_OFFSET as u64); + fadt.set_field(120, u64::from(RST_CTRL_OFFSET)); fadt.set_field(128, 0x0F_u8); // PM1a event register bit, offset is 148. fadt.set_field(148, 0x01_u8); fadt.set_field(149, 0x20_u8); - fadt.set_field(152, PM_EVENT_OFFSET as u64); + fadt.set_field(152, u64::from(PM_EVENT_OFFSET)); // PM1a control register bit, offset is 172. fadt.set_field(172, 0x01_u8); fadt.set_field(173, 0x10_u8); - fadt.set_field(176, PM_CTRL_OFFSET as u64); + fadt.set_field(176, u64::from(PM_CTRL_OFFSET)); // Sleep control register, offset is 244. fadt.set_field(244, 0x01_u8); fadt.set_field(245, 0x08_u8); - fadt.set_field(248, SLEEP_CTRL_OFFSET as u64); + fadt.set_field(248, u64::from(SLEEP_CTRL_OFFSET)); // Sleep status tegister, offset is 256. fadt.set_field(256, 0x01_u8); fadt.set_field(257, 0x08_u8); - fadt.set_field(260, SLEEP_CTRL_OFFSET as u64); + fadt.set_field(260, u64::from(SLEEP_CTRL_OFFSET)); } let mut locked_acpi_data = acpi_data.lock().unwrap(); @@ -703,7 +703,7 @@ pub(crate) trait AcpiBuilder { fadt_end - fadt_begin, )?; - Ok(fadt_begin as u64) + Ok(u64::from(fadt_begin)) } /// Get the Hardware Signature used to build FACS table. @@ -741,7 +741,7 @@ pub(crate) trait AcpiBuilder { locked_acpi_data.extend(facs_data); drop(locked_acpi_data); - Ok(facs_begin as u64) + Ok(u64::from(facs_begin)) } /// Build ACPI SRAT CPU table. @@ -861,7 +861,7 @@ pub(crate) trait AcpiBuilder { xsdt_end - xsdt_begin, )?; - Ok(xsdt_begin as u64) + Ok(u64::from(xsdt_begin)) } /// Build ACPI RSDP and add it to FwCfg as file-entry. diff --git a/machine/src/x86_64/ich9_lpc.rs b/machine/src/x86_64/ich9_lpc.rs index b27b8276b..e4c40c583 100644 --- a/machine/src/x86_64/ich9_lpc.rs +++ b/machine/src/x86_64/ich9_lpc.rs @@ -94,9 +94,10 @@ impl LPCBridge { self.base .config .read(PM_BASE_OFFSET as usize, pm_base_addr.as_mut_bytes()); - self.sys_io - .root() - .add_subregion(pmtmr_region, pm_base_addr as u64 + PM_TIMER_OFFSET as u64)?; + self.sys_io.root().add_subregion( + pmtmr_region, + u64::from(pm_base_addr) + u64::from(PM_TIMER_OFFSET), + )?; Ok(()) } @@ -143,7 +144,7 @@ impl LPCBridge { let rst_ctrl_region = Region::init_io_region(0x1, ops, "RstCtrlRegion"); self.sys_io .root() - .add_subregion(rst_ctrl_region, RST_CTRL_OFFSET as u64)?; + .add_subregion(rst_ctrl_region, u64::from(RST_CTRL_OFFSET))?; Ok(()) } @@ -170,7 +171,7 @@ impl LPCBridge { let sleep_reg_region = Region::init_io_region(0x1, ops, "SleepReg"); self.sys_io .root() - .add_subregion(sleep_reg_region, SLEEP_CTRL_OFFSET as u64)?; + .add_subregion(sleep_reg_region, u64::from(SLEEP_CTRL_OFFSET))?; Ok(()) } @@ -192,7 +193,7 @@ impl LPCBridge { let pm_evt_region = Region::init_io_region(0x4, ops, "PmEvtRegion"); self.sys_io .root() - .add_subregion(pm_evt_region, PM_EVENT_OFFSET as u64)?; + .add_subregion(pm_evt_region, u64::from(PM_EVENT_OFFSET))?; Ok(()) } @@ -222,7 +223,7 @@ impl LPCBridge { let pm_ctrl_region = Region::init_io_region(0x4, ops, "PmCtrl"); self.sys_io .root() - .add_subregion(pm_ctrl_region, PM_CTRL_OFFSET as u64)?; + .add_subregion(pm_ctrl_region, u64::from(PM_CTRL_OFFSET))?; Ok(()) } @@ -258,7 +259,7 @@ impl Device for LPCBridge { le_write_u16( &mut self.base.config.config, HEADER_TYPE as usize, - (HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC) as u16, + u16::from(HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC), )?; self.init_sleep_reg() diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 25d3b7c12..05c428e6c 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -587,7 +587,7 @@ fn get_host_nodes(nodes: &str) -> Result> { "host_nodes".to_string(), 0, true, - MAX_NODES as u64, + u64::from(MAX_NODES), false, ))); } diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 9f2030107..7b02a4c94 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -199,7 +199,7 @@ impl NetworkInterfaceConfig { fn valid_network_queue_size(s: &str) -> Result { let size: u64 = s.parse()?; - valid_virtqueue_size(size, DEFAULT_VIRTQUEUE_SIZE as u64, MAX_QUEUE_SIZE_NET)?; + valid_virtqueue_size(size, u64::from(DEFAULT_VIRTQUEUE_SIZE), MAX_QUEUE_SIZE_NET)?; Ok(size as u16) } diff --git a/machine_manager/src/qmp/qmp_channel.rs b/machine_manager/src/qmp/qmp_channel.rs index a0839501b..51063331f 100644 --- a/machine_manager/src/qmp/qmp_channel.rs +++ b/machine_manager/src/qmp/qmp_channel.rs @@ -73,7 +73,7 @@ pub fn create_timestamp() -> TimeStamp { .expect("Time went backwards"); let seconds = u128::from(since_the_epoch.as_secs()); let microseconds = - (since_the_epoch.as_nanos() - seconds * (NANOSECONDS_PER_SECOND as u128)) / (1_000_u128); + (since_the_epoch.as_nanos() - seconds * u128::from(NANOSECONDS_PER_SECOND)) / (1_000_u128); TimeStamp { seconds: seconds as u64, microseconds: microseconds as u64, diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 6bca5b216..4c083eb49 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -96,7 +96,7 @@ impl SocketRWHandler { fn parse_fd(&mut self, mhdr: &msghdr) { // At least it should has one RawFd. // SAFETY: The input parameter is constant. - let min_cmsg_len = unsafe { CMSG_LEN(size_of::() as u32) as u64 }; + let min_cmsg_len = unsafe { u64::from(CMSG_LEN(size_of::() as u32)) }; if (mhdr.msg_controllen as u64) < min_cmsg_len { return; } @@ -111,8 +111,8 @@ impl SocketRWHandler { { // SAFETY: The pointer of scm can be guaranteed not null. let fds = unsafe { - let fd_num = - (scm.cmsg_len as u64 - CMSG_LEN(0) as u64) as usize / size_of::(); + let fd_num = (scm.cmsg_len as u64 - u64::from(CMSG_LEN(0))) as usize + / size_of::(); std::slice::from_raw_parts(CMSG_DATA(scm) as *const RawFd, fd_num) }; self.scm_fd.append(&mut fds.to_vec()); diff --git a/migration/migration_derive/src/attr_parser.rs b/migration/migration_derive/src/attr_parser.rs index ef1269f27..237949842 100644 --- a/migration/migration_derive/src/attr_parser.rs +++ b/migration/migration_derive/src/attr_parser.rs @@ -129,7 +129,7 @@ fn version_to_u32(version_str: &str) -> u32 { panic!("Version str is illegal."); } - (version_vec[2] as u32) + ((version_vec[1] as u32) << 8) + ((version_vec[0] as u32) << 16) + u32::from(version_vec[2]) + (u32::from(version_vec[1]) << 8) + (u32::from(version_vec[0]) << 16) } #[cfg(test)] diff --git a/migration/src/manager.rs b/migration/src/manager.rs index d381ae312..3b081e971 100644 --- a/migration/src/manager.rs +++ b/migration/src/manager.rs @@ -266,7 +266,7 @@ impl MigrationManager { let name = cpu_desc.name.clone() + "/" + &id.to_string(); let mut copied_cpu_desc = cpu_desc.clone(); copied_cpu_desc.name = name.clone(); - copied_cpu_desc.alias = cpu_desc.alias + id as u64; + copied_cpu_desc.alias = cpu_desc.alias + u64::from(id); Self::register_device_desc(copied_cpu_desc); let mut locked_vmm = MIGRATION_MANAGER.vmm.write().unwrap(); diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index 0d57220f0..b00e486fb 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -977,10 +977,10 @@ pub mod tests { device_v3.set_state_mut(¤t_slice).unwrap(); assert!(state_3_desc.current_version > state_2_desc.current_version); - assert_eq!(device_v3.state.ier, device_v2.state.ier as u64); - assert_eq!(device_v3.state.iir, device_v2.state.iir as u64); - assert_eq!(device_v3.state.lcr, device_v2.state.lcr as u64); - assert_eq!(device_v3.state.mcr, device_v2.state.mcr as u64); + assert_eq!(device_v3.state.ier, u64::from(device_v2.state.ier)); + assert_eq!(device_v3.state.iir, u64::from(device_v2.state.iir)); + assert_eq!(device_v3.state.lcr, u64::from(device_v2.state.lcr)); + assert_eq!(device_v3.state.mcr, u64::from(device_v2.state.mcr)); } #[test] @@ -1102,7 +1102,7 @@ pub mod tests { device_v5.set_state_mut(¤t_slice).unwrap(); assert!(state_5_desc.current_version > state_2_desc.current_version); - assert_eq!(device_v5.state.rii, device_v2.state.iir as u64); + assert_eq!(device_v5.state.rii, u64::from(device_v2.state.iir)); } #[test] diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index e78f7d67a..02fc6d92f 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -133,13 +133,13 @@ impl TestPciDev { pub fn enable(&self) { let mut cmd = self.config_readw(PCI_COMMAND); - cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER) as u16; + cmd |= u16::from(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); self.config_writew(PCI_COMMAND, cmd); cmd = self.config_readw(PCI_COMMAND); - assert!(cmd & PCI_COMMAND_IO as u16 == PCI_COMMAND_IO as u16); - assert!(cmd & PCI_COMMAND_MEMORY as u16 == PCI_COMMAND_MEMORY as u16); - assert!(cmd & PCI_COMMAND_MASTER as u16 == PCI_COMMAND_MASTER as u16); + assert!(cmd & u16::from(PCI_COMMAND_IO) == u16::from(PCI_COMMAND_IO)); + assert!(cmd & u16::from(PCI_COMMAND_MEMORY) == u16::from(PCI_COMMAND_MEMORY)); + assert!(cmd & u16::from(PCI_COMMAND_MASTER) == u16::from(PCI_COMMAND_MASTER)); } pub fn find_capability(&self, id: u8, start_addr: u8) -> u8 { @@ -200,7 +200,7 @@ impl TestPciDev { } else { self.io_map(bar_table as u8) }; - self.msix_table_off = (table & !PCI_MSIX_TABLE_BIR) as u64; + self.msix_table_off = u64::from(table & !PCI_MSIX_TABLE_BIR); let table = self.config_readl(addr + PCI_MSIX_PBA); let bar_pba = table & PCI_MSIX_TABLE_BIR; @@ -209,7 +209,7 @@ impl TestPciDev { } else { self.msix_pba_bar = self.msix_table_bar; } - self.msix_pba_off = (table & !PCI_MSIX_TABLE_BIR) as u64; + self.msix_pba_off = u64::from(table & !PCI_MSIX_TABLE_BIR); self.msix_enabled = true; } @@ -413,7 +413,7 @@ impl TestPciDev { impl PciMsixOps for TestPciDev { fn set_msix_vector(&self, msix_entry: u16, msix_addr: u64, msix_data: u32) { assert!(self.msix_enabled); - let offset = self.msix_table_off + (msix_entry * 16) as u64; + let offset = self.msix_table_off + u64::from(msix_entry * 16); let msix_table_bar = self.msix_table_bar; self.io_writel( diff --git a/tests/mod_test/src/libdriver/pci_bus.rs b/tests/mod_test/src/libdriver/pci_bus.rs index 1a146ebf2..db0889db6 100644 --- a/tests/mod_test/src/libdriver/pci_bus.rs +++ b/tests/mod_test/src/libdriver/pci_bus.rs @@ -64,7 +64,8 @@ impl TestPciBus { } fn get_addr(&self, bus_num: u8, devfn: u8, offset: u8) -> u64 { - self.ecam_alloc_ptr + ((bus_num as u32) << 20 | (devfn as u32) << 12 | offset as u32) as u64 + self.ecam_alloc_ptr + + u64::from(u32::from(bus_num) << 20 | u32::from(devfn) << 12 | u32::from(offset)) } pub fn pci_auto_bus_scan(&self, root_port_num: u8) { @@ -106,11 +107,13 @@ impl TestPciBus { impl PciBusOps for TestPciBus { fn memread(&self, addr: u32, len: usize) -> Vec { - self.test_state.borrow().memread(addr as u64, len as u64) + self.test_state + .borrow() + .memread(u64::from(addr), len as u64) } fn memwrite(&self, addr: u32, buf: &[u8]) { - self.test_state.borrow().memwrite(addr as u64, buf); + self.test_state.borrow().memwrite(u64::from(addr), buf); } fn config_readb(&self, bus_num: u8, devfn: u8, offset: u8) -> u8 { diff --git a/tests/mod_test/src/libdriver/qcow2.rs b/tests/mod_test/src/libdriver/qcow2.rs index ca07a6093..86cc9c0c9 100644 --- a/tests/mod_test/src/libdriver/qcow2.rs +++ b/tests/mod_test/src/libdriver/qcow2.rs @@ -236,7 +236,7 @@ pub fn create_qcow2_img(image_path: String, image_size: u64) { .custom_flags(libc::O_CREAT | libc::O_TRUNC) .open(image_path.clone()) .unwrap(); - file.set_len(cluster_sz * 3 + header.l1_size as u64 * ENTRY_SIZE) + file.set_len(cluster_sz * 3 + u64::from(header.l1_size) * ENTRY_SIZE) .unwrap(); file.write_all(&header.to_vec()).unwrap(); diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index 0a03dd213..be1db39b6 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -174,11 +174,11 @@ impl TestNormalTRB { pub fn generate_setup_td(device_req: &UsbDeviceRequest) -> TestNormalTRB { let mut setup_trb = TestNormalTRB::default(); - setup_trb.parameter = (device_req.length as u64) << 48 - | (device_req.index as u64) << 32 - | (device_req.value as u64) << 16 - | (device_req.request as u64) << 8 - | device_req.request_type as u64; + setup_trb.parameter = u64::from(device_req.length) << 48 + | u64::from(device_req.index) << 32 + | u64::from(device_req.value) << 16 + | u64::from(device_req.request) << 8 + | u64::from(device_req.request_type); setup_trb.set_idt_flag(true); setup_trb.set_ch_flag(true); setup_trb.set_trb_type(TRBType::TrSetup as u32); @@ -193,7 +193,7 @@ impl TestNormalTRB { data_trb.set_ch_flag(true); data_trb.set_dir_flag(in_dir); data_trb.set_trb_type(TRBType::TrData as u32); - data_trb.set_trb_transfer_length(len as u32); + data_trb.set_trb_transfer_length(u32::from(len)); data_trb } @@ -503,21 +503,21 @@ impl TestXhciPciDevice { pub fn run(&mut self) { let status = self.pci_dev.io_readl( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, ); assert!(status & USB_STS_HCH == USB_STS_HCH); let cmd = self.pci_dev.io_readl( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBCMD as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBCMD as u64, ); self.pci_dev.io_writel( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBCMD as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBCMD as u64, cmd | USB_CMD_RUN, ); let status = self.pci_dev.io_readl( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, ); assert!(status & USB_STS_HCH != USB_STS_HCH); } @@ -589,7 +589,7 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 1); + let buf = self.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 1); assert_eq!(buf[0], 0); // configure endpoint self.configure_endpoint(slot_id, false); @@ -643,18 +643,21 @@ impl TestXhciPciDevice { pub fn oper_regs_read(&self, offset: u64) -> u32 { self.pci_dev - .io_readl(self.bar_addr, XHCI_PCI_OPER_OFFSET as u64 + offset) + .io_readl(self.bar_addr, u64::from(XHCI_PCI_OPER_OFFSET) + offset) } pub fn oper_regs_write(&mut self, offset: u64, value: u32) { - self.pci_dev - .io_writel(self.bar_addr, XHCI_PCI_OPER_OFFSET as u64 + offset, value); + self.pci_dev.io_writel( + self.bar_addr, + u64::from(XHCI_PCI_OPER_OFFSET) + offset, + value, + ); } pub fn interrupter_regs_read(&self, intr_idx: u64, offset: u64) -> u32 { self.pci_dev.io_readl( self.bar_addr, - XHCI_PCI_RUNTIME_OFFSET as u64 + u64::from(XHCI_PCI_RUNTIME_OFFSET) + XHCI_INTR_REG_SIZE + intr_idx * XHCI_INTR_REG_SIZE + offset, @@ -664,7 +667,7 @@ impl TestXhciPciDevice { pub fn interrupter_regs_write(&mut self, intr_idx: u64, offset: u64, value: u32) { self.pci_dev.io_writel( self.bar_addr, - XHCI_PCI_RUNTIME_OFFSET as u64 + u64::from(XHCI_PCI_RUNTIME_OFFSET) + RUNTIME_REGS_INTERRUPT_OFFSET + intr_idx * XHCI_INTR_REG_SIZE + offset, @@ -675,7 +678,7 @@ impl TestXhciPciDevice { pub fn interrupter_regs_readq(&self, intr_idx: u64, offset: u64) -> u64 { self.pci_dev.io_readq( self.bar_addr, - XHCI_PCI_RUNTIME_OFFSET as u64 + u64::from(XHCI_PCI_RUNTIME_OFFSET) + XHCI_INTR_REG_SIZE + intr_idx * XHCI_INTR_REG_SIZE + offset, @@ -685,7 +688,7 @@ impl TestXhciPciDevice { pub fn interrupter_regs_writeq(&mut self, intr_idx: u64, offset: u64, value: u64) { self.pci_dev.io_writeq( self.bar_addr, - XHCI_PCI_RUNTIME_OFFSET as u64 + u64::from(XHCI_PCI_RUNTIME_OFFSET) + RUNTIME_REGS_INTERRUPT_OFFSET + intr_idx * XHCI_INTR_REG_SIZE + offset, @@ -696,14 +699,14 @@ impl TestXhciPciDevice { pub fn port_regs_read(&self, port_id: u32, offset: u64) -> u32 { self.pci_dev.io_readl( self.bar_addr, - (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) as u64 + offset, + u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) + offset, ) } pub fn port_regs_write(&mut self, port_id: u32, offset: u64, value: u32) { self.pci_dev.io_writel( self.bar_addr, - (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) as u64 + offset, + u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) + offset, value, ); } @@ -711,7 +714,7 @@ impl TestXhciPciDevice { pub fn doorbell_write(&mut self, slot_id: u32, target: u32) { self.pci_dev.io_writel( self.bar_addr, - XHCI_PCI_DOORBELL_OFFSET as u64 + (slot_id << 2) as u64, + u64::from(XHCI_PCI_DOORBELL_OFFSET) + u64::from(slot_id << 2), target, ); } @@ -741,84 +744,84 @@ impl TestXhciPciDevice { // Interface Version Number let cap = self .pci_dev - .io_readl(self.bar_addr, XHCI_PCI_CAP_OFFSET as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET)); assert!(cap & 0x01000000 == 0x01000000); // HCSPARAMS1 let hcsparams1 = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x4) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x4)); assert_eq!(hcsparams1 & 0xffffff, 0x000140); // HCSPARAMS2 let hcsparams2 = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x8) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x8)); assert_eq!(hcsparams2, 0xf); // HCSPARAMS3 let hcsparams3 = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0xc) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0xc)); assert_eq!(hcsparams3, 0); // HCCPARAMS1 let hccparams1 = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x10) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x10)); // AC64 = 1 assert_eq!(hccparams1 & 1, 1); // doorbell offset let db_offset = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x14) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x14)); assert_eq!(db_offset, 0x2000); // runtime offset let runtime_offset = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x18) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x18)); assert_eq!(runtime_offset, 0x1000); // HCCPARAMS2 let hccparams2 = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x1c) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x1c)); assert_eq!(hccparams2, 0); // USB 2.0 let usb2_version = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x20) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x20)); assert!(usb2_version & 0x02000000 == 0x02000000); let usb2_name = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x24) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x24)); assert_eq!(usb2_name, 0x20425355); let usb2_port = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x28) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x28)); let usb2_port_num = (usb2_port >> 8) & 0xff; // extend capability end let end = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x2c) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x2c)); assert_eq!(end, 0); // USB 3.0 let usb3_version = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x30) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x30)); assert!(usb3_version & 0x03000000 == 0x03000000); let usb3_name = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x34) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x34)); assert_eq!(usb3_name, 0x20425355); let usb3_port = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x38) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x38)); let usb3_port_num = (usb3_port >> 8) & 0xff; // extend capability end let end = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x3c) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x3c)); assert_eq!(end, 0); // Max ports let hcsparams1 = self .pci_dev - .io_readl(self.bar_addr, (XHCI_PCI_CAP_OFFSET + 0x4) as u64); + .io_readl(self.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET + 0x4)); assert_eq!(hcsparams1 >> 24, usb2_port_num + usb3_port_num); } @@ -827,36 +830,36 @@ impl TestXhciPciDevice { let enabled_slot = USB_CONFIG_MAX_SLOTS_ENABLED & USB_CONFIG_MAX_SLOTS_EN_MASK; self.pci_dev.io_writel( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, enabled_slot, ); let config = self.pci_dev.io_readl( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, ); assert_eq!(config, enabled_slot); } pub fn init_device_context_base_address_array_pointer(&mut self) { let dcba = DEVICE_CONTEXT_ENTRY_SIZE * (USB_CONFIG_MAX_SLOTS_ENABLED + 1); - let dcbaap = self.allocator.borrow_mut().alloc(dcba as u64); + let dcbaap = self.allocator.borrow_mut().alloc(u64::from(dcba)); self.pci_dev.io_writeq( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DCBAAP as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DCBAAP as u64, dcbaap, ); let value = self.pci_dev.io_readq( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DCBAAP as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DCBAAP as u64, ); assert_eq!(value, dcbaap); self.xhci.dcbaap = value; } pub fn init_command_ring_dequeue_pointer(&mut self) { - let cmd_ring_sz = TRB_SIZE as u64 * COMMAND_RING_LEN; + let cmd_ring_sz = u64::from(TRB_SIZE) * COMMAND_RING_LEN; let cmd_ring = self.allocator.borrow_mut().alloc(cmd_ring_sz); self.pci_dev .pci_bus @@ -867,13 +870,13 @@ impl TestXhciPciDevice { self.xhci.cmd_ring.init(cmd_ring, cmd_ring_sz); self.pci_dev.io_writeq( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CMD_RING_CTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CMD_RING_CTRL as u64, cmd_ring, ); // Read dequeue pointer return 0. let cmd_ring = self.pci_dev.io_readq( self.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CMD_RING_CTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CMD_RING_CTRL as u64, ); assert_eq!(cmd_ring, 0); } @@ -904,7 +907,7 @@ impl TestXhciPciDevice { pub fn reset_port(&mut self, port_id: u32) { assert!(port_id > 0); let port_offset = - (XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) as u64; + u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32); self.pci_dev.io_writel( self.bar_addr, port_offset + XHCI_PORTSC_OFFSET, @@ -954,14 +957,14 @@ impl TestXhciPciDevice { let ep0_tr_ring = self .allocator .borrow_mut() - .alloc(TRB_SIZE as u64 * TRANSFER_RING_LEN); + .alloc(u64::from(TRB_SIZE) * TRANSFER_RING_LEN); ep0_ctx.set_tr_dequeue_pointer(ep0_tr_ring | 1); ep0_ctx.set_ep_state(0); ep0_ctx.set_ep_type(4); self.mem_write_u32(input_ctx_addr + 0x40, ep0_ctx.as_dwords()); self.xhci.device_slot[slot_id as usize].endpoints[(CONTROL_ENDPOINT_ID - 1) as usize] .transfer_ring - .init(ep0_tr_ring, TRB_SIZE as u64 * TRANSFER_RING_LEN); + .init(ep0_tr_ring, u64::from(TRB_SIZE) * TRANSFER_RING_LEN); let mut trb = TestNormalTRB::default(); trb.parameter = input_ctx_addr; @@ -1030,7 +1033,7 @@ impl TestXhciPciDevice { { TD_TRB_LIMIT } else { - TRB_SIZE as u64 * TRANSFER_RING_LEN + u64::from(TRB_SIZE) * TRANSFER_RING_LEN }; for i in 0..endpoint_id.len() { @@ -1137,7 +1140,7 @@ impl TestXhciPciDevice { self.interrupter_regs_writeq( intr_idx as u64, XHCI_INTR_REG_ERDP_LO, - self.xhci.interrupter[intr_idx].er_pointer | ERDP_EHB as u64, + self.xhci.interrupter[intr_idx].er_pointer | u64::from(ERDP_EHB), ); self.event_list.push_back(event); } else { @@ -1156,7 +1159,10 @@ impl TestXhciPciDevice { let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); // Data Stage. - let ptr = self.allocator.borrow_mut().alloc(device_req.length as u64); + let ptr = self + .allocator + .borrow_mut() + .alloc(u64::from(device_req.length)); let in_dir = device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); @@ -1311,7 +1317,7 @@ impl TestXhciPciDevice { let output_ctx_addr = self.get_device_context_address(slot_id); let mut ep_ctx = XhciEpCtx::default(); self.mem_read_u32( - output_ctx_addr + 0x20 * ep_id as u64, + output_ctx_addr + 0x20 * u64::from(ep_id), ep_ctx.as_mut_dwords(), ); ep_ctx @@ -1383,11 +1389,11 @@ impl TestXhciPciDevice { assert_eq!(data, erstsz); // ERSTBA let table_size = EVENT_RING_SEGMENT_TABLE_ENTRY_SIZE * erstsz; - let evt_ring_seg_table = self.allocator.borrow_mut().alloc(table_size as u64); + let evt_ring_seg_table = self.allocator.borrow_mut().alloc(u64::from(table_size)); self.xhci.interrupter[intr_idx].erstba = evt_ring_seg_table; // NOTE: Only support one Segment now. let mut seg = TestEventRingSegment::new(); - let evt_ring_sz = (TRB_SIZE * ersz) as u64; + let evt_ring_sz = u64::from(TRB_SIZE * ersz); let evt_ring = self.allocator.borrow_mut().alloc(evt_ring_sz); seg.init(evt_ring, ersz); self.pci_dev @@ -1480,7 +1486,7 @@ impl TestXhciPciDevice { fn increase_event_ring(&mut self, intr_idx: usize) { self.xhci.interrupter[intr_idx].trb_count -= 1; - self.xhci.interrupter[intr_idx].er_pointer += TRB_SIZE as u64; + self.xhci.interrupter[intr_idx].er_pointer += u64::from(TRB_SIZE); if self.xhci.interrupter[intr_idx].trb_count == 0 { self.xhci.interrupter[intr_idx].segment_index += 1; if self.xhci.interrupter[intr_idx].segment_index @@ -1503,7 +1509,7 @@ impl TestXhciPciDevice { fn read_segment_entry(&self, intr_idx: usize, index: u32) -> TestEventRingSegment { assert!(index <= self.xhci.interrupter[intr_idx].erstsz); - let addr = self.xhci.interrupter[intr_idx].erstba + (TRB_SIZE * index) as u64; + let addr = self.xhci.interrupter[intr_idx].erstba + u64::from(TRB_SIZE * index); let evt_seg_buf = self.mem_read(addr, TRB_SIZE as usize); let mut evt_seg = TestEventRingSegment::new(); evt_seg.addr = LittleEndian::read_u64(&evt_seg_buf); @@ -1513,14 +1519,14 @@ impl TestXhciPciDevice { } fn set_device_context_address(&mut self, slot_id: u32, addr: u64) { - let device_ctx_addr = self.xhci.dcbaap + (slot_id * DEVICE_CONTEXT_ENTRY_SIZE) as u64; + let device_ctx_addr = self.xhci.dcbaap + u64::from(slot_id * DEVICE_CONTEXT_ENTRY_SIZE); let mut buf = [0_u8; 8]; LittleEndian::write_u64(&mut buf, addr); self.mem_write(device_ctx_addr, &buf); } fn get_device_context_address(&self, slot_id: u32) -> u64 { - let device_ctx_addr = self.xhci.dcbaap + (slot_id * DEVICE_CONTEXT_ENTRY_SIZE) as u64; + let device_ctx_addr = self.xhci.dcbaap + u64::from(slot_id * DEVICE_CONTEXT_ENTRY_SIZE); let mut buf = self.mem_read(device_ctx_addr, 8); let addr = LittleEndian::read_u64(&mut buf); addr @@ -1537,21 +1543,25 @@ impl TestXhciPciDevice { fn increase_command_ring(&mut self) { let cmd_ring = self.xhci.cmd_ring; - if cmd_ring.pointer + TRB_SIZE as u64 >= cmd_ring.start + cmd_ring.size * TRB_SIZE as u64 { + if cmd_ring.pointer + u64::from(TRB_SIZE) + >= cmd_ring.start + cmd_ring.size * u64::from(TRB_SIZE) + { self.queue_link_trb(0, 0, cmd_ring.start, true); } - self.xhci.cmd_ring.pointer += TRB_SIZE as u64; + self.xhci.cmd_ring.pointer += u64::from(TRB_SIZE); } fn increase_transfer_ring(&mut self, slot_id: u32, ep_id: u32, len: u64) { let tr_ring = self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize].transfer_ring; - if tr_ring.pointer + TRB_SIZE as u64 >= tr_ring.start + tr_ring.size * TRB_SIZE as u64 { + if tr_ring.pointer + u64::from(TRB_SIZE) + >= tr_ring.start + tr_ring.size * u64::from(TRB_SIZE) + { self.queue_link_trb(slot_id, ep_id, tr_ring.start, true); } self.xhci.device_slot[slot_id as usize].endpoints[(ep_id - 1) as usize] .transfer_ring - .increase_pointer(TRB_SIZE as u64 * len); + .increase_pointer(u64::from(TRB_SIZE) * len); } fn write_trb(&mut self, addr: u64, trb: &TestNormalTRB) { @@ -1607,7 +1617,7 @@ impl TestXhciPciDevice { } // 1. IAD header descriptor - *offset += USB_DT_CONFIG_SIZE as u64; + *offset += u64::from(USB_DT_CONFIG_SIZE); let buf = self.get_transfer_data_indirect_with_offset(addr, 8 as usize, *offset); // descriptor type @@ -1631,7 +1641,7 @@ impl TestXhciPciDevice { assert_eq!(buf[6], SC_VIDEOCONTROL); // get total vc length from its header descriptor - *offset += USB_DT_INTERFACE_SIZE as u64; + *offset += u64::from(USB_DT_INTERFACE_SIZE); let buf = self.get_transfer_data_indirect_with_offset(addr, 0xd as usize, *offset); let total = u16::from_le_bytes(buf[5..7].try_into().unwrap()); @@ -1641,7 +1651,7 @@ impl TestXhciPciDevice { let _buf = self.get_transfer_data_indirect_with_offset(addr, remained as usize, *offset); // 3. VS interface - *offset += remained as u64; + *offset += u64::from(remained); let buf = self.get_transfer_data_indirect_with_offset( addr, USB_DT_INTERFACE_SIZE as usize, @@ -1653,7 +1663,7 @@ impl TestXhciPciDevice { assert_eq!(buf[6], SC_VIDEOSTREAMING); // get total vs length from its header descriptor - *offset += USB_DT_INTERFACE_SIZE as u64; + *offset += u64::from(USB_DT_INTERFACE_SIZE); let buf = self.get_transfer_data_indirect_with_offset(addr, 0xf as usize, *offset); let total = u16::from_le_bytes(buf[4..6].try_into().unwrap()); let remained = total - 0xf; @@ -1668,7 +1678,7 @@ impl TestXhciPciDevice { return; } - *offset += USB_DT_CONFIG_SIZE as u64; + *offset += u64::from(USB_DT_CONFIG_SIZE); let buf = self.get_transfer_data_indirect_with_offset( addr, USB_DT_INTERFACE_SIZE as usize, @@ -1692,7 +1702,7 @@ impl TestXhciPciDevice { match usb_device_type { UsbDeviceType::Tablet => { // hid descriptor - *offset += USB_DT_INTERFACE_SIZE as u64; + *offset += u64::from(USB_DT_INTERFACE_SIZE); let buf = self.get_transfer_data_indirect_with_offset(addr, 9, *offset); assert_eq!( buf, @@ -1711,14 +1721,14 @@ impl TestXhciPciDevice { } UsbDeviceType::Keyboard => { // hid descriptor - *offset += USB_DT_INTERFACE_SIZE as u64; + *offset += u64::from(USB_DT_INTERFACE_SIZE); let buf = self.get_transfer_data_indirect_with_offset(addr, 9, *offset); assert_eq!(buf, [0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3f, 0]); } _ => {} } - *offset += USB_DT_INTERFACE_SIZE as u64; + *offset += u64::from(USB_DT_INTERFACE_SIZE); // endpoint descriptor let buf = self.get_transfer_data_indirect_with_offset( addr, @@ -1738,7 +1748,7 @@ impl TestXhciPciDevice { assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_ENDPOINT); // endpoint address assert_eq!(buf[2], USB_DIRECTION_DEVICE_TO_HOST | 0x01); - *offset += USB_DT_ENDPOINT_SIZE as u64; + *offset += u64::from(USB_DT_ENDPOINT_SIZE); // endpoint descriptor let buf = self.get_transfer_data_indirect_with_offset( addr, @@ -1761,7 +1771,7 @@ impl TestXhciPciDevice { assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); let len = name.len() * 2 + 2; - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + let buf = self.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), len as u64); for i in 0..name.len() { assert_eq!(buf[2 * i + 2], name.as_bytes()[i]); } @@ -1774,8 +1784,10 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = - self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, USB_DT_DEVICE_SIZE as u64); + let buf = self.get_transfer_data_indirect( + evt.ptr - u64::from(TRB_SIZE), + u64::from(USB_DT_DEVICE_SIZE), + ); // descriptor type assert_eq!(buf[1], USB_DESCRIPTOR_TYPE_DEVICE); // bcdUSB @@ -1796,7 +1808,7 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let addr = evt.ptr - TRB_SIZE as u64; + let addr = evt.ptr - u64::from(TRB_SIZE); let mut offset = 0; let buf = self.get_transfer_data_indirect_with_offset(addr, USB_DT_CONFIG_SIZE as usize, offset); @@ -1842,7 +1854,7 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 63); + let buf = self.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 63); assert_eq!( buf, [ @@ -1856,13 +1868,13 @@ impl TestXhciPciDevice { ); } UsbDeviceType::Tablet => { - self.get_hid_report_descriptor(slot_id, HID_POINTER_REPORT_LEN as u16); + self.get_hid_report_descriptor(slot_id, u16::from(HID_POINTER_REPORT_LEN)); self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); let buf = self.get_transfer_data_indirect( - evt.ptr - TRB_SIZE as u64, - HID_POINTER_REPORT_LEN as u64, + evt.ptr - u64::from(TRB_SIZE), + u64::from(HID_POINTER_REPORT_LEN), ); assert_eq!( buf, @@ -1887,7 +1899,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_DEVICE_IN_REQUEST, request: USB_REQUEST_GET_DESCRIPTOR, - value: (USB_DT_DEVICE as u16) << 8, + value: u16::from(USB_DT_DEVICE) << 8, index: 0, length: buf_len, }; @@ -1899,7 +1911,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_DEVICE_IN_REQUEST, request: USB_REQUEST_GET_DESCRIPTOR, - value: (USB_DT_CONFIGURATION as u16) << 8, + value: u16::from(USB_DT_CONFIGURATION) << 8, index: 0, length: buf_len, }; @@ -1911,7 +1923,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_DEVICE_IN_REQUEST, request: USB_REQUEST_GET_DESCRIPTOR, - value: (USB_DT_STRING as u16) << 8 | index, + value: u16::from(USB_DT_STRING) << 8 | index, index: 0, length: buf_len, }; @@ -2189,7 +2201,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_INTERFACE_CLASS_IN_REQUEST, request: GET_INFO, - value: (VS_PROBE_CONTROL as u16) << 8, + value: u16::from(VS_PROBE_CONTROL) << 8, index: VS_INTERFACE_NUM, length: 1, }; @@ -2197,7 +2209,7 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 1); + let buf = self.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 1); buf[0] } @@ -2206,7 +2218,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_INTERFACE_CLASS_IN_REQUEST, request: GET_CUR, - value: (VS_PROBE_CONTROL as u16) << 8, + value: u16::from(VS_PROBE_CONTROL) << 8, index: VS_INTERFACE_NUM, length: len, }; @@ -2214,7 +2226,7 @@ impl TestXhciPciDevice { self.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = self.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, len as u64); + let buf = self.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), u64::from(len)); let mut vs_control = VideoStreamingControl::default(); vs_control.as_mut_bytes().copy_from_slice(&buf); vs_control @@ -2228,7 +2240,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_INTERFACE_CLASS_OUT_REQUEST, request: SET_CUR, - value: (VS_PROBE_CONTROL as u16) << 8, + value: u16::from(VS_PROBE_CONTROL) << 8, index: VS_INTERFACE_NUM, length: len, }; @@ -2243,7 +2255,7 @@ impl TestXhciPciDevice { let device_req = UsbDeviceRequest { request_type: USB_INTERFACE_CLASS_OUT_REQUEST, request: SET_CUR, - value: (VS_COMMIT_CONTROL as u16) << 8, + value: u16::from(VS_COMMIT_CONTROL) << 8, index: VS_INTERFACE_NUM, length: 0, }; @@ -2287,16 +2299,16 @@ impl TestXhciPciDevice { let cnt = (total + TRB_MAX_LEN - 1) / TRB_MAX_LEN; let mut data = Vec::new(); for _ in 0..cnt { - self.queue_indirect_td(slot_id, ep_id, TRB_MAX_LEN as u64); + self.queue_indirect_td(slot_id, ep_id, u64::from(TRB_MAX_LEN)); self.doorbell_write(slot_id, ep_id); // wait for frame done. std::thread::sleep(std::time::Duration::from_millis(FRAME_WAIT_MS)); let evt = self.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); if evt.ccode == TRBCCode::Success as u32 { - let mut buf = self.get_transfer_data_indirect(evt.ptr, TRB_MAX_LEN as u64); + let mut buf = self.get_transfer_data_indirect(evt.ptr, u64::from(TRB_MAX_LEN)); data.append(&mut buf); } else if evt.ccode == TRBCCode::ShortPacket as u32 { - let copied = (TRB_MAX_LEN - evt.length) as u64; + let copied = u64::from(TRB_MAX_LEN - evt.length); let mut buf = self.get_transfer_data_indirect(evt.ptr, copied); data.append(&mut buf); if total == data.len() as u32 { diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 1c9297b7d..991b5a6b8 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -258,7 +258,7 @@ impl TestVringIndirectDesc { let mut flags = test_state.borrow().readw( self.desc - + (size_of::() as u64 * self.index as u64) + + (size_of::() as u64 * u64::from(self.index)) + offset_of!(VringDesc, flags) as u64, ); @@ -359,7 +359,7 @@ impl TestVirtQueue { test_state.borrow().writew( self.used + offset_of!(VringUsed, ring) as u64 - + (size_of::() as u64 * self.size as u64), + + (size_of::() as u64 * u64::from(self.size)), 0, ); } @@ -376,7 +376,7 @@ impl TestVirtQueue { let features = virtio_dev.get_guest_features(); virtio_dev.queue_select(index); - let queue_size = virtio_dev.get_queue_size() as u32; + let queue_size = u32::from(virtio_dev.get_queue_size()); assert!(queue_size != 0); assert!(queue_size & (queue_size - 1) == 0); @@ -390,12 +390,12 @@ impl TestVirtQueue { let addr = alloc .borrow_mut() - .alloc(get_vring_size(self.size, self.align) as u64); + .alloc(u64::from(get_vring_size(self.size, self.align))); self.desc = addr; - self.avail = self.desc + (self.size * size_of::() as u32) as u64; + self.avail = self.desc + u64::from(self.size * size_of::() as u32); self.used = round_up( - self.avail + (size_of::() as u32 * (3 + self.size)) as u64, - self.align as u64, + self.avail + u64::from(size_of::() as u32 * (3 + self.size)), + u64::from(self.align), ) .unwrap(); } @@ -413,7 +413,7 @@ impl TestVirtQueue { let elem_addr = self.used + offset_of!(VringUsed, ring) as u64 - + (self.last_used_idx as u32 % self.size) as u64 + + u64::from(u32::from(self.last_used_idx) % self.size) * size_of::() as u64; let id_addr = elem_addr + offset_of!(VringUsedElem, id) as u64; @@ -434,7 +434,7 @@ impl TestVirtQueue { test_state.borrow().readw( self.used + offset_of!(VringUsed, ring) as u64 - + (size_of::() as u64 * self.size as u64), + + (size_of::() as u64 * u64::from(self.size)), ) } @@ -442,7 +442,7 @@ impl TestVirtQueue { test_state.borrow().writew( self.avail + offset_of!(VringAvail, ring) as u64 - + (size_of::() as u64 * self.size as u64), + + (size_of::() as u64 * u64::from(self.size)), index, ); } @@ -466,7 +466,7 @@ impl TestVirtQueue { test_state.borrow().writew( self.avail + offset_of!(VringAvail, ring) as u64 - + (size_of::() * (idx as u32 % self.size) as usize) as u64, + + (size_of::() * (u32::from(idx) % self.size) as usize) as u64, desc_idx, ); } @@ -551,7 +551,7 @@ impl TestVirtQueue { let free_head = self.free_head; let desc_elem = VringDesc { addr: indirect.desc, - len: size_of::() as u32 * indirect.elem as u32, + len: size_of::() as u32 * u32::from(indirect.elem), flags: VRING_DESC_F_INDIRECT, next: 0, }; @@ -565,7 +565,7 @@ impl TestVirtQueue { // Add a vring desc elem to desc table. fn add_elem_to_desc(&mut self, test_state: Rc>, elem: VringDesc) { self.num_free -= 1; - let desc_elem_addr = self.desc + VRING_DESC_SIZE * self.free_head as u64; + let desc_elem_addr = self.desc + VRING_DESC_SIZE * u64::from(self.free_head); test_state .borrow() .memwrite(desc_elem_addr, elem.as_bytes()); @@ -591,7 +591,7 @@ impl TestVirtioDev { #[inline] pub fn get_vring_size(num: u32, align: u32) -> u32 { let desc_avail = - (size_of::() as u32 * num + size_of::() as u32 * (3 + num)) as u64; - let desc_avail_align = round_up(desc_avail, align as u64).unwrap() as u32; + u64::from(size_of::() as u32 * num + size_of::() as u32 * (3 + num)); + let desc_avail_align = round_up(desc_avail, u64::from(align)).unwrap() as u32; desc_avail_align + size_of::() as u32 * 3 + size_of::() as u32 * num } diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index 67340432f..e284e3ad7 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -188,7 +188,7 @@ pub fn virtio_blk_request( .alloc((size_of::() + data_size + 512) as u64); let data_addr = if align { - round_up(addr + REQ_ADDR_LEN as u64, 512).unwrap() + round_up(addr + u64::from(REQ_ADDR_LEN), 512).unwrap() } else { addr + REQ_DATA_OFFSET }; @@ -237,7 +237,7 @@ pub fn add_blk_request( // Desc elem: [addr, len, flags, next]. let data_addr = if align { - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() } else { req_addr + REQ_DATA_OFFSET }; @@ -254,7 +254,7 @@ pub fn add_blk_request( write: read, }); data_entries.push(TestVringDescEntry { - data: data_addr + REQ_DATA_LEN as u64, + data: data_addr + u64::from(REQ_DATA_LEN), len: REQ_STATUS_LEN, write: true, }); @@ -300,7 +300,7 @@ pub fn virtio_blk_write( ); let status_addr = if align { - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64 + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN) } else { req_addr + REQ_STATUS_OFFSET }; @@ -339,13 +339,13 @@ pub fn virtio_blk_read( ); let data_addr = if align { - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() } else { - req_addr + REQ_ADDR_LEN as u64 + req_addr + u64::from(REQ_ADDR_LEN) }; let status_addr = if align { - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64 + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN) } else { req_addr + REQ_STATUS_OFFSET }; @@ -377,7 +377,7 @@ pub fn virtio_blk_read_write_zeroes( read = false; } let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); - let data_addr = req_addr + REQ_ADDR_LEN as u64; + let data_addr = req_addr + u64::from(REQ_ADDR_LEN); let data_entries: Vec = vec![ TestVringDescEntry { data: req_addr, @@ -407,7 +407,7 @@ pub fn virtio_blk_read_write_zeroes( &mut None, true, ); - let status_addr = req_addr + REQ_ADDR_LEN as u64 + data_len as u64; + let status_addr = req_addr + u64::from(REQ_ADDR_LEN) + data_len as u64; let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_OK); diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index b8bd09d7b..802155d10 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -697,7 +697,7 @@ pub fn current_curosr_check(dpy: &Rc>, local: &Vec>, local: &Vec u8 { self.pci_dev - .io_readb(self.bar, self.device_base as u64 + addr) + .io_readb(self.bar, u64::from(self.device_base) + addr) } fn config_readw(&self, addr: u64) -> u16 { self.pci_dev - .io_readw(self.bar, self.device_base as u64 + addr) + .io_readw(self.bar, u64::from(self.device_base) + addr) } fn config_readl(&self, addr: u64) -> u32 { self.pci_dev - .io_readl(self.bar, self.device_base as u64 + addr) + .io_readl(self.bar, u64::from(self.device_base) + addr) } fn config_readq(&self, addr: u64) -> u64 { self.pci_dev - .io_readq(self.bar, self.device_base as u64 + addr) + .io_readq(self.bar, u64::from(self.device_base) + addr) } #[allow(unused)] fn config_writeb(&self, addr: u64, value: u8) { self.pci_dev - .io_writeb(self.bar, self.device_base as u64 + addr, value) + .io_writeb(self.bar, u64::from(self.device_base) + addr, value) } #[allow(unused)] fn config_writew(&self, addr: u64, value: u16) { self.pci_dev - .io_writew(self.bar, self.device_base as u64 + addr, value) + .io_writew(self.bar, u64::from(self.device_base) + addr, value) } #[allow(unused)] fn config_writel(&self, addr: u64, value: u32) { self.pci_dev - .io_writel(self.bar, self.device_base as u64 + addr, value) + .io_writel(self.bar, u64::from(self.device_base) + addr, value) } #[allow(unused)] fn config_writeq(&self, addr: u64, value: u64) { self.pci_dev - .io_writeq(self.bar, self.device_base as u64 + addr, value) + .io_writeq(self.bar, u64::from(self.device_base) + addr, value) } fn isr_readb(&self) -> u8 { - self.pci_dev.io_readb(self.bar, self.isr_base as u64) + self.pci_dev.io_readb(self.bar, u64::from(self.isr_base)) } fn enable_interrupt(&mut self) { @@ -345,46 +345,50 @@ impl VirtioDeviceOps for TestVirtioPciDev { fn get_device_features(&self) -> u64 { self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature_select) as u64, + u64::from(self.common_base) + + offset_of!(VirtioPciCommonCfg, device_feature_select) as u64, 0, ); - let lo: u64 = self.pci_dev.io_readl( + let lo: u64 = u64::from(self.pci_dev.io_readl( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature) as u64, - ) as u64; + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, device_feature) as u64, + )); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature_select) as u64, + u64::from(self.common_base) + + offset_of!(VirtioPciCommonCfg, device_feature_select) as u64, 1, ); - let hi: u64 = self.pci_dev.io_readl( + let hi: u64 = u64::from(self.pci_dev.io_readl( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_feature) as u64, - ) as u64; + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, device_feature) as u64, + )); (hi << 32) | lo } fn set_guest_features(&self, features: u64) { self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + u64::from(self.common_base) + + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, 0, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, features as u32, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + u64::from(self.common_base) + + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, 1, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, (features >> 32) as u32, ); } @@ -392,36 +396,38 @@ impl VirtioDeviceOps for TestVirtioPciDev { fn get_guest_features(&self) -> u64 { self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + u64::from(self.common_base) + + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, 0, ); - let lo: u64 = self.pci_dev.io_readl( + let lo: u64 = u64::from(self.pci_dev.io_readl( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, - ) as u64; + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + )); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, + u64::from(self.common_base) + + offset_of!(VirtioPciCommonCfg, guest_feature_select) as u64, 1, ); - let hi: u64 = self.pci_dev.io_readl( + let hi: u64 = u64::from(self.pci_dev.io_readl( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, - ) as u64; + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, guest_feature) as u64, + )); (hi << 32) | lo } fn get_status(&self) -> u8 { self.pci_dev.io_readb( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_status) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, device_status) as u64, ) } fn set_status(&self, status: u8) { self.pci_dev.io_writeb( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, device_status) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, device_status) as u64, status, ) } @@ -429,21 +435,21 @@ impl VirtioDeviceOps for TestVirtioPciDev { fn get_generation(&self) -> u8 { self.pci_dev.io_readb( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, config_generation) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, config_generation) as u64, ) } fn get_queue_nums(&self) -> u16 { self.pci_dev.io_readw( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, num_queues) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, num_queues) as u64, ) } fn queue_select(&self, index: u16) { self.pci_dev.io_writew( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_select) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_select) as u64, index, ); } @@ -451,14 +457,14 @@ impl VirtioDeviceOps for TestVirtioPciDev { fn get_queue_select(&self) -> u16 { self.pci_dev.io_readw( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_select) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_select) as u64, ) } fn set_queue_size(&self, size: u16) { self.pci_dev.io_writew( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_size) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_size) as u64, size, ) } @@ -466,39 +472,39 @@ impl VirtioDeviceOps for TestVirtioPciDev { fn get_queue_size(&self) -> u16 { self.pci_dev.io_readw( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_size) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_size) as u64, ) } fn activate_queue(&self, desc: u64, avail: u64, used: u64) { self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_desc_lo) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_desc_lo) as u64, desc as u32, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_desc_hi) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_desc_hi) as u64, (desc >> 32) as u32, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_avail_lo) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_avail_lo) as u64, avail as u32, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_avail_hi) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_avail_hi) as u64, (avail >> 32) as u32, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_used_lo) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_used_lo) as u64, used as u32, ); self.pci_dev.io_writel( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_used_hi) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_used_hi) as u64, (used >> 32) as u32, ); } @@ -555,15 +561,15 @@ impl VirtioDeviceOps for TestVirtioPciDev { let notify_off = self.pci_dev.io_readw( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, ); - virtqueue.borrow_mut().queue_notify_off = - self.notify_base as u64 + notify_off as u64 * self.notify_off_multiplier as u64; + virtqueue.borrow_mut().queue_notify_off = u64::from(self.notify_base) + + u64::from(notify_off) * u64::from(self.notify_off_multiplier); self.pci_dev.io_writew( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, 1, ); @@ -681,12 +687,12 @@ impl VirtioPCIMSIXOps for TestVirtioPciDev { fn set_config_vector(&self, vector: u16) { self.pci_dev.io_writew( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, msix_config) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, msix_config) as u64, vector, ); let vector_get: u16 = self.pci_dev.io_readw( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, msix_config) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, msix_config) as u64, ); assert_eq!( vector, vector_get, @@ -699,12 +705,12 @@ impl VirtioPCIMSIXOps for TestVirtioPciDev { self.queue_select(vq_idx); self.pci_dev.io_writew( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, vector, ); let vector_get: u16 = self.pci_dev.io_readw( self.bar, - self.common_base as u64 + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, + u64::from(self.common_base) + offset_of!(VirtioPciCommonCfg, queue_msix_vector) as u64, ); if vector_get != vector { println!("WARN: set vector {}, get vector {}", vector, vector_get); diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index 228247e21..b393cc378 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -60,14 +60,14 @@ pub fn swap_u16(value: u16) -> u16 { } pub fn swap_u32(value: u32) -> u32 { - let lower_u16 = swap_u16(value as u16) as u32; - let higher_u16 = swap_u16((value >> 16) as u16) as u32; + let lower_u16 = u32::from(swap_u16(value as u16)); + let higher_u16 = u32::from(swap_u16((value >> 16) as u16)); lower_u16 << 16 | higher_u16 } pub fn swap_u64(value: u64) -> u64 { - let lower_u32 = swap_u32(value as u32) as u64; - let higher_u32 = swap_u32((value >> 32) as u32) as u64; + let lower_u32 = u64::from(swap_u32(value as u32)); + let higher_u32 = u64::from(swap_u32((value >> 32) as u32)); lower_u32 << 32 | higher_u32 } diff --git a/tests/mod_test/tests/aarch64/acpi_test.rs b/tests/mod_test/tests/aarch64/acpi_test.rs index 6ef32b646..563d5ebc9 100644 --- a/tests/mod_test/tests/aarch64/acpi_test.rs +++ b/tests/mod_test/tests/aarch64/acpi_test.rs @@ -140,12 +140,12 @@ fn check_madt(data: &[u8], cpu: u8) { offset += mem::size_of::(); for i in 0..cpu { assert_eq!(data[offset + 1], 80); // The length of this structure - assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), i as u32); // CPU interface number - assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), i as u32); // ACPI processor UID + assert_eq!(LittleEndian::read_u32(&data[(offset + 4)..]), u32::from(i)); // CPU interface number + assert_eq!(LittleEndian::read_u32(&data[(offset + 8)..]), u32::from(i)); // ACPI processor UID assert_eq!(LittleEndian::read_u32(&data[(offset + 12)..]), 5); // Flags assert_eq!(LittleEndian::read_u32(&data[(offset + 20)..]), 23); // Performance monitoring interrupts assert_eq!(LittleEndian::read_u64(&data[(offset + 56)..]), 25); // Virtual GIC maintenance interrupt - assert_eq!(LittleEndian::read_u64(&data[(offset + 68)..]), i as u64); // MPIDR + assert_eq!(LittleEndian::read_u64(&data[(offset + 68)..]), u64::from(i)); // MPIDR offset += mem::size_of::(); } @@ -462,7 +462,7 @@ fn check_madt_of_two_gicr( let len = LittleEndian::read_u32(&read_data[(madt_addr + offset + 12)..]); assert_eq!( MEM_LAYOUT[LayoutEntryType::HighGicRedist as usize].1, - len as u64 + u64::from(len) ); } diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index b67453e30..9ac0a6fe7 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -327,18 +327,18 @@ fn balloon_fun(shared: bool, huge: bool) { let free_page = balloon .allocator .borrow_mut() - .alloc(page_num as u64 * PAGE_SIZE_UNIT); + .alloc(u64::from(page_num) * PAGE_SIZE_UNIT); let pfn = (free_page >> 12) as u32; let pfn_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); while idx < page_num { balloon .state .borrow_mut() - .writel(pfn_addr + 4 * idx as u64, pfn + idx); + .writel(pfn_addr + 4 * u64::from(idx), pfn + idx); balloon .state .borrow_mut() - .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + .writeb(free_page + PAGE_SIZE_UNIT * u64::from(idx), 1); idx += 1; } @@ -348,7 +348,7 @@ fn balloon_fun(shared: bool, huge: bool) { while loop_num < page_num { let entry = TestVringDescEntry { - data: pfn_addr + (loop_num as u64 * 4), + data: pfn_addr + (u64::from(loop_num) * 4), len: 4, write: false, }; @@ -377,7 +377,7 @@ fn balloon_fun(shared: bool, huge: bool) { while loop_num < page_num { let entry = TestVringDescEntry { - data: pfn_addr + (loop_num as u64 * 4), + data: pfn_addr + (u64::from(loop_num) * 4), len: 4, write: false, }; @@ -655,18 +655,18 @@ fn balloon_fpr_fun(shared: bool) { let free_page = balloon .allocator .borrow_mut() - .alloc(page_num as u64 * PAGE_SIZE_UNIT); + .alloc(u64::from(page_num) * PAGE_SIZE_UNIT); let pfn = (free_page >> 12) as u32; let pfn_addr = balloon.allocator.borrow_mut().alloc(PAGE_SIZE_UNIT); while idx < page_num { balloon .state .borrow_mut() - .writel(pfn_addr + 4 * idx as u64, pfn + idx); + .writel(pfn_addr + 4 * u64::from(idx), pfn + idx); balloon .state .borrow_mut() - .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + .writeb(free_page + PAGE_SIZE_UNIT * u64::from(idx), 1); idx += 1; } // balloon Illegal addresses @@ -694,7 +694,7 @@ fn balloon_fpr_fun(shared: bool) { while loop_num < page_num { let entry = TestVringDescEntry { - data: pfn_addr + (loop_num as u64 * 4), + data: pfn_addr + (u64::from(loop_num) * 4), len: 4, write: true, }; @@ -940,7 +940,7 @@ fn balloon_deactive_001() { let balloon = VirtioBalloonTest::new(1024, PAGE_SIZE_UNIT, false, false, cfg); let bar = balloon.device.borrow().bar; - let common_base = balloon.device.borrow().common_base as u64; + let common_base = u64::from(balloon.device.borrow().common_base); balloon.device.borrow().pci_dev.io_writel( bar, @@ -1070,11 +1070,11 @@ fn balloon_numa1() { balloon .state .borrow_mut() - .writel(pfn_addr + 4 * idx as u64, pfn + idx); + .writel(pfn_addr + 4 * u64::from(idx), pfn + idx); balloon .state .borrow_mut() - .writeb(free_page + PAGE_SIZE_UNIT * idx as u64, 1); + .writeb(free_page + PAGE_SIZE_UNIT * u64::from(idx), 1); idx += 1; } @@ -1084,7 +1084,7 @@ fn balloon_numa1() { while loop_num < page_num { let entry = TestVringDescEntry { - data: pfn_addr + (loop_num as u64 * 4), + data: pfn_addr + (u64::from(loop_num) * 4), len: 4, write: false, }; @@ -1113,7 +1113,7 @@ fn balloon_numa1() { while loop_num < page_num { let entry = TestVringDescEntry { - data: pfn_addr + (loop_num as u64 * 4), + data: pfn_addr + (u64::from(loop_num) * 4), len: 4, write: false, }; diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index dc2395be8..c716ebf3f 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -169,11 +169,12 @@ fn virtio_blk_get_id( true, ); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status_addr = + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_OK); - let data_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap(); + let data_addr = round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap(); assert_eq!( String::from_utf8( test_state @@ -210,7 +211,8 @@ fn virtio_blk_flush( true, ); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status_addr = + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_OK); } @@ -240,7 +242,8 @@ fn virtio_blk_illegal_req( true, ); - let status_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status_addr = + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_UNSUPP); } @@ -265,7 +268,7 @@ fn blk_basic() { .init_device(test_state.clone(), alloc.clone(), features, 1); let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + assert_eq!(capacity, TEST_IMAGE_SIZE / u64::from(REQ_DATA_LEN)); virtio_blk_write( blk.clone(), @@ -436,7 +439,7 @@ fn blk_feature_ro() { .init_device(test_state.clone(), alloc.clone(), features, 1); let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + assert_eq!(capacity, TEST_IMAGE_SIZE / u64::from(REQ_DATA_LEN)); virtio_blk_write( blk.clone(), @@ -473,7 +476,7 @@ fn blk_feature_ro() { .init_device(test_state.clone(), alloc.clone(), features, 1); let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + assert_eq!(capacity, TEST_IMAGE_SIZE / u64::from(REQ_DATA_LEN)); virtio_blk_read( blk.clone(), @@ -503,7 +506,7 @@ fn blk_feature_ro() { ); let status_addr = - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_IOERR); @@ -623,7 +626,7 @@ fn blk_feature_mq() { true, )); - let data_addr = round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap(); + let data_addr = round_up(req_addr[i] + u64::from(REQ_ADDR_LEN), 512).unwrap(); let mut data_entries: Vec = Vec::with_capacity(3); data_entries.push(TestVringDescEntry { @@ -637,7 +640,7 @@ fn blk_feature_mq() { write: false, }); data_entries.push(TestVringDescEntry { - data: data_addr + REQ_DATA_LEN as u64, + data: data_addr + u64::from(REQ_DATA_LEN), len: REQ_STATUS_LEN, write: true, }); @@ -666,8 +669,8 @@ fn blk_feature_mq() { } for i in 0..num_queues { - let status_addr = - round_up(req_addr[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status_addr = round_up(req_addr[i] + u64::from(REQ_ADDR_LEN), 512).unwrap() + + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_OK); } @@ -798,13 +801,13 @@ fn blk_small_file_511b() { .init_device(test_state.clone(), alloc.clone(), features, 1); let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, size / REQ_DATA_LEN as u64); + assert_eq!(capacity, size / u64::from(REQ_DATA_LEN)); let mut blk_req = TestVirtBlkReq::new(VIRTIO_BLK_T_OUT, 1, 0, REQ_DATA_LEN as usize); blk_req.data.push_str("TEST"); let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, true); - let data_addr = round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap(); + let data_addr = round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap(); let mut data_entries: Vec = Vec::with_capacity(3); data_entries.push(TestVringDescEntry { @@ -818,7 +821,7 @@ fn blk_small_file_511b() { write: false, }); data_entries.push(TestVringDescEntry { - data: data_addr + REQ_DATA_LEN as u64, + data: data_addr + u64::from(REQ_DATA_LEN), len: REQ_STATUS_LEN, write: true, }); @@ -838,7 +841,7 @@ fn blk_small_file_511b() { ); let status_addr = - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_IOERR); @@ -863,7 +866,7 @@ fn blk_small_file_511b() { ); let status_addr = - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_IOERR); @@ -1001,7 +1004,7 @@ fn blk_iops() { } let status_addr = - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_ne!(status, VIRTIO_BLK_S_OK); @@ -1020,7 +1023,7 @@ fn blk_iops() { } let status_addr = - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_OK); @@ -1226,7 +1229,7 @@ fn blk_rw_config() { .init_device(test_state.clone(), alloc.clone(), features, 1); let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + assert_eq!(capacity, TEST_IMAGE_SIZE / u64::from(REQ_DATA_LEN)); blk.borrow().config_writeq(0, 1024); let capacity = blk.borrow().config_readq(0); @@ -1597,8 +1600,8 @@ fn blk_parallel_req() { ); for i in 0..4 { - let status_addr = - round_up(req_addr_vec[i] + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + let status_addr = round_up(req_addr_vec[i] + u64::from(REQ_ADDR_LEN), 512).unwrap() + + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_OK); } @@ -1633,7 +1636,7 @@ fn blk_exceed_capacity() { .init_device(test_state.clone(), alloc.clone(), features, 1); let capacity = blk.borrow().config_readq(0); - assert_eq!(capacity, TEST_IMAGE_SIZE / REQ_DATA_LEN as u64); + assert_eq!(capacity, TEST_IMAGE_SIZE / u64::from(REQ_DATA_LEN)); let (free_head, req_addr) = add_blk_request( test_state.clone(), @@ -1655,7 +1658,7 @@ fn blk_exceed_capacity() { ); let status_addr = - round_up(req_addr + REQ_ADDR_LEN as u64, 512).unwrap() + REQ_DATA_LEN as u64; + round_up(req_addr + u64::from(REQ_ADDR_LEN), 512).unwrap() + u64::from(REQ_DATA_LEN); let status = test_state.borrow().readb(status_addr); assert_eq!(status, VIRTIO_BLK_S_IOERR); @@ -1767,16 +1770,16 @@ fn blk_feature_discard() { ); if image_type == ImageType::Raw && status == VIRTIO_BLK_S_OK { let image_size = get_disk_size(image_path.clone()); - assert_eq!(image_size, full_disk_size - num_sectors as u64 / 2); + assert_eq!(image_size, full_disk_size - u64::from(num_sectors) / 2); } else if image_type == ImageType::Qcow2 && status == VIRTIO_BLK_S_OK - && (num_sectors as u64 * 512 & CLUSTER_SIZE - 1) == 0 + && (u64::from(num_sectors) * 512 & CLUSTER_SIZE - 1) == 0 { // If the disk format is equal to Qcow2. // the length of the num sectors needs to be aligned with the cluster size, // otherwise the calculated file size is not accurate. let image_size = get_disk_size(image_path.clone()); - let delete_num = (num_sectors as u64 * 512) >> 10; + let delete_num = (u64::from(num_sectors) * 512) >> 10; assert_eq!(image_size, full_disk_size - delete_num); } @@ -1937,17 +1940,17 @@ fn blk_feature_write_zeroes() { && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) { let image_size = get_disk_size(image_path.clone()); - assert_eq!(image_size, full_disk_size - num_sectors as u64 / 2); + assert_eq!(image_size, full_disk_size - u64::from(num_sectors) / 2); } else if image_type == ImageType::Qcow2 && status == VIRTIO_BLK_S_OK && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) - && (num_sectors as u64 * 512 & CLUSTER_SIZE - 1) == 0 + && (u64::from(num_sectors) * 512 & CLUSTER_SIZE - 1) == 0 { // If the disk format is equal to Qcow2. // the length of the num sectors needs to be aligned with the cluster size, // otherwise the calculated file size is not accurate. let image_size = get_disk_size(image_path.clone()); - let delete_num = (num_sectors as u64 * 512) >> 10; + let delete_num = (u64::from(num_sectors) * 512) >> 10; assert_eq!(image_size, full_disk_size - delete_num); } diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 4062ce83c..5d400a6a1 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -552,8 +552,8 @@ fn poll_used_ring( let mut idx = test_state .borrow() .readw(vq.borrow().used + offset_of!(VringUsed, idx) as u64); - while start < idx as u64 { - for i in start..idx as u64 { + while start < u64::from(idx) { + for i in start..u64::from(idx) { let len = test_state.borrow().readw( vq.borrow().used + offset_of!(VringUsed, ring) as u64 @@ -567,8 +567,8 @@ fn poll_used_ring( let addr = test_state .borrow() - .readq(vq.borrow().desc + id as u64 * VRING_DESC_SIZE); - let packets = test_state.borrow().memread(addr, len as u64); + .readq(vq.borrow().desc + u64::from(id) * VRING_DESC_SIZE); + let packets = test_state.borrow().memread(addr, u64::from(len)); let src_mac_pos = VIRTIO_NET_HDR_SIZE + ETHERNET_HDR_SIZE + ARP_HDR_SIZE; let dst_mac_pos = src_mac_pos + 10; if arp_request[src_mac_pos..src_mac_pos + MAC_ADDR_LEN] @@ -582,7 +582,7 @@ fn poll_used_ring( } } } - start = idx as u64; + start = u64::from(idx); vq.borrow().set_used_event(test_state.clone(), start as u16); idx = test_state .borrow() @@ -958,7 +958,7 @@ fn net_config_mac_rw( /// Virtio net configure is not allowed to change except mac. fn write_net_config_check(net: Rc>, offset: u64, value: u64, size: u8) { - let origin_value = net.borrow().config_readw(offset) as u64; + let origin_value = u64::from(net.borrow().config_readw(offset)); assert_ne!(origin_value, value); match size { 1 => net.borrow().config_writeb(offset, value as u8), @@ -966,7 +966,7 @@ fn write_net_config_check(net: Rc>, offset: u64, value 4 => net.borrow().config_writel(offset, value as u32), _ => (), }; - let value = net.borrow().config_readw(offset) as u64; + let value = u64::from(net.borrow().config_readw(offset)); assert_eq!(origin_value, value); } @@ -1015,38 +1015,38 @@ fn virtio_net_write_and_check_config() { write_net_config_check( net.clone(), offset_of!(VirtioNetConfig, status) as u64, - u16::MAX as u64, + u64::from(u16::MAX), 2, ); write_net_config_check( net.clone(), offset_of!(VirtioNetConfig, max_virtqueue_pairs) as u64, - u16::MAX as u64, + u64::from(u16::MAX), 2, ); write_net_config_check( net.clone(), offset_of!(VirtioNetConfig, mtu) as u64, - u16::MAX as u64, + u64::from(u16::MAX), 2, ); write_net_config_check( net.clone(), offset_of!(VirtioNetConfig, speed) as u64, - u32::MAX as u64, + u64::from(u32::MAX), 4, ); write_net_config_check( net.clone(), offset_of!(VirtioNetConfig, duplex) as u64, - u8::MAX as u64, + u64::from(u8::MAX), 1, ); write_net_config_check( net.clone(), size_of:: as u64 + 1, - u8::MAX as u64, + u64::from(u8::MAX), 1, ); @@ -1571,7 +1571,7 @@ fn virtio_net_ctrl_rx_test() { alloc.clone(), vqs.clone(), 0, - value as u32, + u32::from(value), VIRTIO_NET_OK, ); arp_request.arp_packet.src_mac[0] += 1; @@ -2071,12 +2071,12 @@ fn virtio_net_abnormal_rx_tx_test_3() { let notify_off = net.borrow().pci_dev.io_readw( net.borrow().bar, - net.borrow().common_base as u64 + u64::from(net.borrow().common_base) + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, ); - vq.borrow_mut().queue_notify_off = net.borrow().notify_base as u64 - + notify_off as u64 * net.borrow().notify_off_multiplier as u64; + vq.borrow_mut().queue_notify_off = u64::from(net.borrow().notify_base) + + u64::from(notify_off) * u64::from(net.borrow().notify_off_multiplier); net.borrow() .setup_virtqueue_intr((i + 1) as u16, alloc.clone(), vq.clone()); diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index bdd4da48a..60239ac8a 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -728,7 +728,7 @@ fn unmask_msix_global(pci_dev: TestPciDev) { } fn mask_msix_vector(pci_dev: TestPciDev, vector: u16) { - let offset: u64 = pci_dev.msix_table_off + (vector * PCI_MSIX_ENTRY_SIZE) as u64; + let offset: u64 = pci_dev.msix_table_off + u64::from(vector * PCI_MSIX_ENTRY_SIZE); let vector_mask = pci_dev.io_readl( pci_dev.msix_table_bar, @@ -743,7 +743,7 @@ fn mask_msix_vector(pci_dev: TestPciDev, vector: u16) { } fn unmask_msix_vector(pci_dev: TestPciDev, vector: u16) { - let offset: u64 = pci_dev.msix_table_off + (vector * PCI_MSIX_ENTRY_SIZE) as u64; + let offset: u64 = pci_dev.msix_table_off + u64::from(vector * PCI_MSIX_ENTRY_SIZE); let vector_control = pci_dev.io_readl( pci_dev.msix_table_bar, @@ -1268,13 +1268,13 @@ fn test_pci_type1_reset() { let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); let command = root_port.rp_dev.config_readw(PCI_COMMAND); - let cmd_memory = command & PCI_COMMAND_MEMORY as u16; + let cmd_memory = command & u16::from(PCI_COMMAND_MEMORY); // Bitwise inversion of memory space enable. let write_cmd = if cmd_memory != 0 { - command & !PCI_COMMAND_MEMORY as u16 + command & u16::from(!PCI_COMMAND_MEMORY) } else { - command | PCI_COMMAND_MEMORY as u16 + command | u16::from(PCI_COMMAND_MEMORY) }; root_port.rp_dev.config_writew(PCI_COMMAND, write_cmd); let old_command = root_port.rp_dev.config_readw(PCI_COMMAND); @@ -1304,7 +1304,7 @@ fn test_out_boundary_config_access() { let devfn = 1 << 3 | 1; let addr = machine.borrow().pci_bus.borrow().ecam_alloc_ptr - + ((0 as u32) << 20 | (devfn as u32) << 12 | 0 as u32) as u64 + + u64::from((0 as u32) << 20 | (devfn as u32) << 12 | 0 as u32) - 1; let write_value = u16::max_value(); @@ -1333,7 +1333,7 @@ fn test_out_size_config_access() { let value = root_port.rp_dev.config_readq(0); assert_ne!( value, - (vendor_device_id as u64) << 32 | command_status as u64 + u64::from(vendor_device_id) << 32 | u64::from(command_status) ); tear_down(None, test_state, alloc, None, Some(image_paths)); @@ -2953,7 +2953,7 @@ fn test_pci_combine_001() { // set memory enabled = 0 let mut val = dev_locked.config_readw(PCI_COMMAND); - val &= !(PCI_COMMAND_MEMORY as u16); + val &= !u16::from(PCI_COMMAND_MEMORY); dev_locked.config_writew(PCI_COMMAND, val); // mmio r/w stops working. @@ -2962,7 +2962,7 @@ fn test_pci_combine_001() { assert_ne!(out, 10); // set memory enabled = 1 - val |= PCI_COMMAND_MEMORY as u16; + val |= u16::from(PCI_COMMAND_MEMORY); dev_locked.config_writew(PCI_COMMAND, val); // mmio r/w gets back to work. diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs index c7a982212..3a120b579 100644 --- a/tests/mod_test/tests/rng_test.rs +++ b/tests/mod_test/tests/rng_test.rs @@ -90,7 +90,7 @@ fn virtio_rng_read_batch( ); assert!(len.unwrap() >= 1); - assert!(len.unwrap() as u64 <= bytes); + assert!(u64::from(len.unwrap()) <= bytes); test_state.borrow().memread(req_addr, RNG_DATA_BYTES) } @@ -131,7 +131,7 @@ fn virtio_rng_read_chained( ); assert!(len.unwrap() >= 1); - assert!(len.unwrap() as u64 <= bytes * DEFAULT_RNG_REQS); + assert!(u64::from(len.unwrap()) <= bytes * DEFAULT_RNG_REQS); test_state.borrow().memread(req_addr, RNG_DATA_BYTES) } diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index 6a7d76c01..ad2c9e68d 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -241,7 +241,7 @@ fn scream_playback_basic_test() { // write one audio chunk for i in 0..AUDIO_CHUNK_SIZE { ivshmem.borrow_mut().writeb( - PLAY_DADA_OFFSET + (AUDIO_CHUNK_SIZE + i) as u64, + PLAY_DADA_OFFSET + u64::from(AUDIO_CHUNK_SIZE + i), AUDIO_DEFAULT_DATA[i as usize], ); } @@ -257,9 +257,10 @@ fn scream_playback_basic_test() { // When four consecutive frames of data are written, only the last two frames of data can be // read. for i in 0..AUDIO_CHUNK_SIZE { - ivshmem - .borrow_mut() - .writeb(PLAY_DADA_OFFSET + i as u64, AUDIO_DEFAULT_DATA[i as usize]); + ivshmem.borrow_mut().writeb( + PLAY_DADA_OFFSET + u64::from(i), + AUDIO_DEFAULT_DATA[i as usize], + ); } // update play header chunk_idx @@ -374,7 +375,10 @@ fn scream_record_basic_test() { let offset = RECORD_BASE + offset_of!(ShmemStreamHeader, chunk_idx) as u64; chunk_idx = ivshmem.borrow_mut().readw(offset); - assert_eq!(chunk_idx as u32, AUDIO_CHUNK_CNT % (AUDIO_CHUNK_CNT - 1)); + assert_eq!( + u32::from(chunk_idx), + AUDIO_CHUNK_CNT % (AUDIO_CHUNK_CNT - 1) + ); let audio_data = ivshmem.borrow_mut().readl(RECORD_DATA_OFFSET); let mut check_data = 0; @@ -498,7 +502,7 @@ fn scream_exception_002() { // write one audio chunk for i in 0..AUDIO_CHUNK_SIZE { ivshmem.borrow_mut().writeb( - PLAY_DADA_OFFSET + (AUDIO_CHUNK_SIZE + i) as u64, + PLAY_DADA_OFFSET + u64::from(AUDIO_CHUNK_SIZE + i), AUDIO_DEFAULT_DATA[i as usize], ); } diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 61fb68054..5ec46385b 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -202,7 +202,7 @@ impl VirtioScsiTest { if let Some(data) = data_out { let out_len = data.len() as u32; let out_bytes = data.as_bytes().to_vec(); - let out_addr = self.alloc.borrow_mut().alloc(out_len as u64); + let out_addr = self.alloc.borrow_mut().alloc(u64::from(out_len)); self.state.borrow().memwrite(out_addr, out_bytes.as_slice()); data_entries.push(TestVringDescEntry { data: out_addr, @@ -216,7 +216,7 @@ impl VirtioScsiTest { let resp_addr = self .alloc .borrow_mut() - .alloc(cmdresp_len + data_in_len as u64); + .alloc(cmdresp_len + u64::from(data_in_len)); let resp_bytes = resp.as_bytes(); self.state.borrow().memwrite(resp_addr, resp_bytes); @@ -264,7 +264,7 @@ impl VirtioScsiTest { data_in.append( self.state .borrow() - .memread(resp_addr + cmdresp_len, data_in_len as u64) + .memread(resp_addr + cmdresp_len, u64::from(data_in_len)) .as_mut(), ); } @@ -667,7 +667,7 @@ fn scsi_hd_basic_test() { for i in 0..32 { // Test 1 Result: Only response 0 for target == 31. Otherwise response // VIRTIO_SCSI_S_BAD_TARGET. - let expect_result = if i == target as u16 { + let expect_result = if i == u16::from(target) { VIRTIO_SCSI_S_OK } else { VIRTIO_SCSI_S_BAD_TARGET @@ -677,7 +677,7 @@ fn scsi_hd_basic_test() { target: i as u8, lun: 0, data_out: None, - data_in_length: INQUIRY_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_DATA_LEN), expect_response: expect_result, expect_status: GOOD, expect_result_data: None, @@ -707,7 +707,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: REPORT_LUNS_DATA_LEN as u32, + data_in_length: u32::from(REPORT_LUNS_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -742,7 +742,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: READ_CAPACITY_10_DATA_LEN as u32, + data_in_length: u32::from(READ_CAPACITY_10_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -755,8 +755,12 @@ fn scsi_hd_basic_test() { // Bytes[4-7]: Logical Block Length In Bytes. // Total size = (last logical block address + 1) * block length. assert_eq!( - (u32::from_be_bytes(data_in.as_ref().unwrap()[0..4].try_into().unwrap()) as u64 + 1) - * (u32::from_be_bytes(data_in.as_ref().unwrap()[4..8].try_into().unwrap()) as u64), + (u64::from(u32::from_be_bytes( + data_in.as_ref().unwrap()[0..4].try_into().unwrap() + )) + 1) + * u64::from(u32::from_be_bytes( + data_in.as_ref().unwrap()[4..8].try_into().unwrap() + )), TEST_IMAGE_SIZE ); @@ -795,7 +799,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN as u32, + data_in_length: u32::from(MODE_SENSE_PAGE_CACHE_LEN_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -840,7 +844,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: MODE_SENSE_PAGE_ALL_DATA_LEN as u32, + data_in_length: u32::from(MODE_SENSE_PAGE_ALL_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -863,7 +867,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -887,7 +891,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_SUPPORTED_VPD_PAGES_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_SUPPORTED_VPD_PAGES_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -908,7 +912,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_UNIT_SERIAL_NUMBER_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_UNIT_SERIAL_NUMBER_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -933,7 +937,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_DEVICE_IDENTIFICATION_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_DEVICE_IDENTIFICATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -955,7 +959,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_BLOCK_LIMITS_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_BLOCK_LIMITS_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -983,7 +987,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_BLOCK_DEVICE_CHARACTERISTICS_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1012,7 +1016,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_LOGICAL_BLOCK_PROVISIONING_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_LOGICAL_BLOCK_PROVISIONING_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1033,7 +1037,7 @@ fn scsi_hd_basic_test() { target, lun, data_out: None, - data_in_length: INQUIRY_REFERRALS_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_REFERRALS_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -1112,7 +1116,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: MODE_SENSE_LEN_DATA_LEN as u32, + data_in_length: u32::from(MODE_SENSE_LEN_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1174,7 +1178,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: READ_TOC_DATA_LEN as u32, + data_in_length: u32::from(READ_TOC_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1207,7 +1211,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: READ_TOC_MSF_DATA_LEN as u32, + data_in_length: u32::from(READ_TOC_MSF_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1231,7 +1235,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: READ_TOC_FORMAT_DATA_LEN as u32, + data_in_length: u32::from(READ_TOC_FORMAT_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1272,7 +1276,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: READ_DISC_INFORMATION_DATA_LEN as u32, + data_in_length: u32::from(READ_DISC_INFORMATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1341,7 +1345,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: GET_CONFIGURATION_DATA_LEN as u32, + data_in_length: u32::from(GET_CONFIGURATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1377,7 +1381,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: GET_EVENT_STATUS_NOTIFICATION_DATA_LEN as u32, + data_in_length: u32::from(GET_EVENT_STATUS_NOTIFICATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1425,7 +1429,7 @@ fn scsi_target_cdb_test() { target, lun: req_lun, data_out: None, - data_in_length: REPORT_LUNS_DATA_LEN as u32, + data_in_length: u32::from(REPORT_LUNS_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1449,7 +1453,7 @@ fn scsi_target_cdb_test() { target, lun: req_lun, data_out: None, - data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_TARGET_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1482,7 +1486,7 @@ fn scsi_target_cdb_test() { target, lun: 0, data_out: None, - data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_TARGET_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: Some(expect_result_vec), @@ -1505,7 +1509,7 @@ fn scsi_target_cdb_test() { target, lun: req_lun, data_out: None, - data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_TARGET_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -1526,7 +1530,7 @@ fn scsi_target_cdb_test() { target, lun: 0, data_out: None, - data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_TARGET_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -1547,7 +1551,7 @@ fn scsi_target_cdb_test() { target, lun: req_lun, data_out: None, - data_in_length: INQUIRY_TARGET_DATA_LEN as u32, + data_in_length: u32::from(INQUIRY_TARGET_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -1621,7 +1625,7 @@ fn scsi_target_cdb_test() { target, lun: req_lun, data_out: None, - data_in_length: READ_CAPACITY_10_DATA_LEN as u32, + data_in_length: u32::from(READ_CAPACITY_10_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -2250,7 +2254,7 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: MODE_SENSE_LEN_DATA_LEN as u32, + data_in_length: u32::from(MODE_SENSE_LEN_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -2269,7 +2273,7 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: READ_DISC_INFORMATION_DATA_LEN as u32, + data_in_length: u32::from(READ_DISC_INFORMATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -2287,7 +2291,7 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: GET_CONFIGURATION_DATA_LEN as u32, + data_in_length: u32::from(GET_CONFIGURATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, @@ -2309,7 +2313,7 @@ fn send_cd_command_to_hd_test() { target, lun, data_out: None, - data_in_length: GET_EVENT_STATUS_NOTIFICATION_DATA_LEN as u32, + data_in_length: u32::from(GET_EVENT_STATUS_NOTIFICATION_DATA_LEN), expect_response: VIRTIO_SCSI_S_OK, expect_status: CHECK_CONDITION, expect_result_data: None, diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index ac46ce516..8336cfd9c 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -214,7 +214,8 @@ impl SerialTest { // Port Ready. for port in self.ports.clone().iter() { - let ready_msg = VirtioConsoleControl::new(*port.0 as u32, VIRTIO_CONSOLE_PORT_READY, 1); + let ready_msg = + VirtioConsoleControl::new(u32::from(*port.0), VIRTIO_CONSOLE_PORT_READY, 1); self.out_control_event(ready_msg); // If it's a console port. @@ -258,7 +259,7 @@ impl SerialTest { // driver -> device: port open. let open_msg: VirtioConsoleControl = - VirtioConsoleControl::new(*port.0 as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); + VirtioConsoleControl::new(u32::from(*port.0), VIRTIO_CONSOLE_PORT_OPEN, 1); self.out_control_event(open_msg); } } @@ -321,7 +322,7 @@ impl SerialTest { // Connect Guest. // driver -> device: port open. let open_msg: VirtioConsoleControl = - VirtioConsoleControl::new(port.nr as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); + VirtioConsoleControl::new(u32::from(port.nr), VIRTIO_CONSOLE_PORT_OPEN, 1); self.out_control_event(open_msg); // IO: Guest -> Host. @@ -461,7 +462,7 @@ fn create_serial(ports_config: Vec, pci_slot: u8, pci_fn: u8) -> Ser fn verify_output_data(test_state: Rc>, addr: u64, len: u32, test_data: &String) { let mut data_buf: Vec = Vec::with_capacity(len as usize); - data_buf.append(test_state.borrow().memread(addr, len as u64).as_mut()); + data_buf.append(test_state.borrow().memread(addr, u64::from(len)).as_mut()); let data = String::from_utf8(data_buf).unwrap(); assert_eq!(data, *test_data); } @@ -662,12 +663,13 @@ fn virtconsole_pty_err_out_control_msg() { // Error out control msg which has invalid event. Just discard this invalid msg. Nothing // happened. - let invalid_event_msg = VirtioConsoleControl::new(nr as u32, VIRTIO_CONSOLE_PORT_NAME, 1); + let invalid_event_msg = VirtioConsoleControl::new(u32::from(nr), VIRTIO_CONSOLE_PORT_NAME, 1); st.out_control_event(invalid_event_msg); // Error out control msg which has non-existed port id. Just discard this invalid msg. Nothing // happened. - let invalid_event_msg = VirtioConsoleControl::new((nr + 5) as u32, VIRTIO_CONSOLE_PORT_OPEN, 1); + let invalid_event_msg = + VirtioConsoleControl::new(u32::from(nr + 5), VIRTIO_CONSOLE_PORT_OPEN, 1); st.out_control_event(invalid_event_msg); // Error out control msg which size is illegal. diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 72539614b..7773200e3 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -155,7 +155,7 @@ fn check_multi_frames( slot_id, VS_ENDPOINT_ID, frame_len, - UVC_HEADER_LEN as u32, + u32::from(UVC_HEADER_LEN), max_payload, ); for buf in &payload_list { @@ -314,7 +314,7 @@ fn test_xhci_camera_invalid_frame_len() { slot_id, VS_ENDPOINT_ID, len as u32, - UVC_HEADER_LEN as u32, + u32::from(UVC_HEADER_LEN), cur.dwMaxPayloadTransferSize, ); for item in payload_list { diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs index 472d91ef2..54e35e2c9 100644 --- a/tests/mod_test/tests/usb_storage_test.rs +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -83,7 +83,7 @@ fn cbw_phase( } let mut iovecs = Vec::new(); - let ptr = guest_allocator.alloc(CBW_SIZE as u64); + let ptr = guest_allocator.alloc(u64::from(CBW_SIZE)); xhci.mem_write(ptr, &cbw_buf); let iovec = TestIovec::new(ptr, len as usize, false); @@ -142,7 +142,7 @@ fn csw_phase( sig_check: bool, ) -> u64 { let mut iovecs = Vec::new(); - let ptr = guest_allocator.alloc(len as u64); + let ptr = guest_allocator.alloc(u64::from(len)); let iovec = TestIovec::new(ptr, len as usize, false); iovecs.push(iovec); @@ -335,7 +335,7 @@ fn usb_storage_functional_get_max_lun() { xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 1); + let buf = xhci.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 1); assert_eq!(buf, [0]); @@ -878,7 +878,7 @@ fn usb_storage_cbw_invalid_endpoint() { LittleEndian::write_u32(&mut cbw_buf[0..4], cbw.sig); let mut iovecs = Vec::new(); - let ptr = guest_allocator.borrow_mut().alloc(CBW_SIZE as u64); + let ptr = guest_allocator.borrow_mut().alloc(u64::from(CBW_SIZE)); xhci.mem_write(ptr, &cbw_buf); let iovec = TestIovec::new(ptr, CBW_SIZE as usize, false); @@ -927,7 +927,7 @@ fn usb_storage_csw_invalid_endpoint() { // Test 2: CSW phase. let mut iovecs = Vec::new(); - let ptr = guest_allocator.borrow_mut().alloc(CSW_SIZE as u64); + let ptr = guest_allocator.borrow_mut().alloc(u64::from(CSW_SIZE)); let iovec = TestIovec::new(ptr, CSW_SIZE as usize, false); iovecs.push(iovec); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index 8e7070fab..a87139261 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -487,7 +487,7 @@ fn test_xhci_keyboard_over_ring_limit() { xhci.queue_link_trb( slot_id, HID_DEVICE_ENDPOINT_ID, - org_ptr + TRB_SIZE as u64 * 64, + org_ptr + u64::from(TRB_SIZE) * 64, false, ); } else if i == 1 { @@ -948,58 +948,59 @@ fn test_xhci_keyboard_controller_init_invalid_register() { xhci.read_capability(); let old_value = xhci .pci_dev - .io_readl(xhci.bar_addr, XHCI_PCI_CAP_OFFSET as u64 + 0x2c); + .io_readl(xhci.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET) + 0x2c); xhci.pci_dev - .io_writel(xhci.bar_addr, XHCI_PCI_CAP_OFFSET as u64 + 0x2c, 0xffff); + .io_writel(xhci.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET) + 0x2c, 0xffff); let value = xhci .pci_dev - .io_readl(xhci.bar_addr, XHCI_PCI_CAP_OFFSET as u64 + 0x2c); + .io_readl(xhci.bar_addr, u64::from(XHCI_PCI_CAP_OFFSET) + 0x2c); assert_eq!(value, old_value); // Case 3: write invalid slot. xhci.pci_dev.io_writel( xhci.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, 0xffff, ); let config = xhci.pci_dev.io_readl( xhci.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, ); assert_ne!(config, 0xffff); // Case 4: invalid oper xhci.pci_dev.io_writel( xhci.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, 0xffff, ); let status = xhci.pci_dev.io_readl( xhci.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, ); assert_ne!(status, 0xffff); // Device Notify Control xhci.pci_dev.io_writel( xhci.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DNCTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DNCTRL as u64, 0x12345, ); let ndctrl = xhci.pci_dev.io_readl( xhci.bar_addr, - XHCI_PCI_OPER_OFFSET as u64 + XHCI_OPER_REG_DNCTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DNCTRL as u64, ); assert_eq!(ndctrl, 0x12345 & XHCI_OPER_NE_MASK); // invalid port offset. let invalid_offset = 0x7; xhci.pci_dev.io_writel( xhci.bar_addr, - XHCI_PCI_PORT_OFFSET as u64 + invalid_offset, + u64::from(XHCI_PCI_PORT_OFFSET) + invalid_offset, 0xff, ); - let invalid_offset = xhci - .pci_dev - .io_readl(xhci.bar_addr, XHCI_PCI_PORT_OFFSET as u64 + invalid_offset); + let invalid_offset = xhci.pci_dev.io_readl( + xhci.bar_addr, + u64::from(XHCI_PCI_PORT_OFFSET) + invalid_offset, + ); assert_eq!(invalid_offset, 0); xhci.init_device_context_base_address_array_pointer(); @@ -1010,25 +1011,28 @@ fn test_xhci_keyboard_controller_init_invalid_register() { xhci.interrupter_regs_writeq(0, XHCI_INTR_REG_ERSTBA_LO, 0); // micro frame index. xhci.pci_dev - .io_writel(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64, 0xf); + .io_writel(xhci.bar_addr, u64::from(XHCI_PCI_RUNTIME_OFFSET), 0xf); let mf_index = xhci .pci_dev - .io_readl(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64); + .io_readl(xhci.bar_addr, u64::from(XHCI_PCI_RUNTIME_OFFSET)); assert!(mf_index <= 0x3fff); // invalid offset - xhci.pci_dev - .io_writel(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64 + 0x1008, 0xf); + xhci.pci_dev.io_writel( + xhci.bar_addr, + u64::from(XHCI_PCI_RUNTIME_OFFSET) + 0x1008, + 0xf, + ); let over_offset = xhci .pci_dev - .io_readl(xhci.bar_addr, XHCI_PCI_RUNTIME_OFFSET as u64 + 0x1008); + .io_readl(xhci.bar_addr, u64::from(XHCI_PCI_RUNTIME_OFFSET) + 0x1008); assert_eq!(over_offset, 0); // Case 6: invalid doorbell xhci.pci_dev - .io_writel(xhci.bar_addr, XHCI_PCI_DOORBELL_OFFSET as u64, 0xf); + .io_writel(xhci.bar_addr, u64::from(XHCI_PCI_DOORBELL_OFFSET), 0xf); let invalid_db = xhci .pci_dev - .io_readl(xhci.bar_addr, XHCI_PCI_DOORBELL_OFFSET as u64); + .io_readl(xhci.bar_addr, u64::from(XHCI_PCI_DOORBELL_OFFSET)); assert_eq!(invalid_db, 0); // Case 7: invalid size @@ -1468,7 +1472,7 @@ fn test_xhci_keyboard_device_init_invalid_request() { let device_req = UsbDeviceRequest { request_type: USB_DEVICE_IN_REQUEST, request: USB_REQUEST_GET_DESCRIPTOR, - value: (USB_DT_CONFIGURATION as u16) << 8 | 6, + value: u16::from(USB_DT_CONFIGURATION) << 8 | 6, index: 10, length: 10, }; @@ -1545,13 +1549,15 @@ fn test_xhci_keyboard_device_init_invalid_control() { let device_req = UsbDeviceRequest { request_type: USB_DEVICE_IN_REQUEST, request: USB_REQUEST_GET_DESCRIPTOR, - value: (USB_DT_CONFIGURATION as u16) << 8, + value: u16::from(USB_DT_CONFIGURATION) << 8, index: 0, length: 64, }; // Case 1: no SetUp Stage. // Data Stage. - let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let ptr = guest_allocator + .borrow_mut() + .alloc(u64::from(device_req.length)); let in_dir = device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); @@ -1569,7 +1575,9 @@ fn test_xhci_keyboard_device_init_invalid_control() { let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); // Data Stage. - let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let ptr = guest_allocator + .borrow_mut() + .alloc(u64::from(device_req.length)); let in_dir = device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); @@ -1587,7 +1595,9 @@ fn test_xhci_keyboard_device_init_invalid_control() { setup_trb.set_idt_flag(false); xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); // Data Stage. - let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let ptr = guest_allocator + .borrow_mut() + .alloc(u64::from(device_req.length)); let in_dir = device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); @@ -1605,7 +1615,9 @@ fn test_xhci_keyboard_device_init_invalid_control() { setup_trb.set_trb_transfer_length(11); xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); // Data Stage. - let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let ptr = guest_allocator + .borrow_mut() + .alloc(u64::from(device_req.length)); let in_dir = device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); @@ -1622,7 +1634,9 @@ fn test_xhci_keyboard_device_init_invalid_control() { let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); xhci.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); // Data Stage. - let ptr = guest_allocator.borrow_mut().alloc(device_req.length as u64); + let ptr = guest_allocator + .borrow_mut() + .alloc(u64::from(device_req.length)); let in_dir = device_req.request_type & USB_DIRECTION_DEVICE_TO_HOST == USB_DIRECTION_DEVICE_TO_HOST; let mut data_trb = TestNormalTRB::generate_data_td(ptr, device_req.length, in_dir); @@ -1871,7 +1885,7 @@ fn test_xhci_keyboard_device_init_device_request_repeat() { xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 2); + let buf = xhci.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 2); assert_eq!(buf, [0, 0]); // set configuration xhci.set_configuration(slot_id, 1); @@ -1883,7 +1897,7 @@ fn test_xhci_keyboard_device_init_device_request_repeat() { xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 2); + let buf = xhci.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 2); assert_eq!(buf[0], 1); // Set remote wakeup. xhci.set_feature(slot_id, USB_DEVICE_REMOTE_WAKEUP as u16); @@ -1895,7 +1909,7 @@ fn test_xhci_keyboard_device_init_device_request_repeat() { xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, 2); + let buf = xhci.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), 2); assert_eq!(buf, [2, 0]); // Clear remote wakeup. xhci.clear_feature(slot_id, USB_DEVICE_REMOTE_WAKEUP as u16); @@ -2162,7 +2176,7 @@ fn test_xhci_tablet_over_ring_limit() { xhci.queue_link_trb( slot_id, HID_DEVICE_ENDPOINT_ID, - org_ptr + TRB_SIZE as u64 * 64, + org_ptr + u64::from(TRB_SIZE) * 64, false, ); } else if i == 1 { @@ -2236,7 +2250,7 @@ fn test_xhci_tablet_device_init_control_command() { xhci.doorbell_write(slot_id, CONTROL_ENDPOINT_ID); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::ShortPacket as u32); - let buf = xhci.get_transfer_data_indirect(evt.ptr - TRB_SIZE as u64, HID_POINTER_LEN); + let buf = xhci.get_transfer_data_indirect(evt.ptr - u64::from(TRB_SIZE), HID_POINTER_LEN); assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0]); xhci.test_pointer_event(slot_id, test_state.clone()); diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 0e7cb2374..2acebf3f8 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -180,7 +180,7 @@ fn check_req_result( fn check_queue(blk: Rc>, desc: u64, avail: u64, used: u64) { let bar = blk.borrow().bar; - let common_base = blk.borrow().common_base as u64; + let common_base = u64::from(blk.borrow().common_base); let reqs = [ (offset_of!(VirtioPciCommonCfg, queue_desc_lo), desc), (offset_of!(VirtioPciCommonCfg, queue_desc_hi), desc >> 32), @@ -452,7 +452,7 @@ fn virtio_feature_indirect() { free_head = vqs[0] .borrow_mut() .add(test_state.clone(), req_addr, 8, false); - let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); @@ -485,7 +485,7 @@ fn virtio_feature_indirect() { free_head = vqs[0] .borrow_mut() .add(test_state.clone(), req_addr, 8, false); - let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); @@ -498,7 +498,7 @@ fn virtio_feature_indirect() { indirect_req.add_desc(test_state.clone(), req_addr + 8, 8, false); indirect_req.add_desc( test_state.clone(), - req_addr + REQ_ADDR_LEN as u64, + req_addr + u64::from(REQ_ADDR_LEN), 513, true, ); @@ -523,7 +523,7 @@ fn virtio_feature_indirect() { String::from_utf8( test_state .borrow() - .memread(req_addr + REQ_ADDR_LEN as u64, 4) + .memread(req_addr + u64::from(REQ_ADDR_LEN), 4) ) .unwrap(), "TEST" @@ -595,7 +595,7 @@ fn virtio_feature_indirect_and_event_idx() { let free_head = vqs[0] .borrow_mut() .add(test_state.clone(), req_addr, REQ_ADDR_LEN, false); - let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); @@ -608,7 +608,7 @@ fn virtio_feature_indirect_and_event_idx() { indirect_req.setup(alloc.clone(), test_state.clone(), 2); indirect_req.add_desc( test_state.clone(), - req_addr + REQ_ADDR_LEN as u64, + req_addr + u64::from(REQ_ADDR_LEN), REQ_DATA_LEN, false, ); @@ -874,12 +874,12 @@ fn virtio_init_device_abnormal_features() { fn virtio_init_device_abnormal_vring_info() { // (err_type, value, ack, device_status) let reqs = [ - (0, u16::MAX as u64, 0, 0), + (0, u64::from(u16::MAX), 0, 0), (0, 2, 0, 0), (1, 0_u64, 0xff, 0), (1, 255, 0xff, 0), (1, 1 << 15, 0xff, 0), - (1, u16::MAX as u64, 0xff, 0), + (1, u64::from(u16::MAX), 0xff, 0), (2, 0, 0xff, 0), (3, 0, 0xff, 0), (4, 0, 0xff, 0), @@ -921,7 +921,7 @@ fn virtio_init_device_abnormal_vring_info() { blk.borrow().queue_select(value as u16); } - let queue_size = blk.borrow().get_queue_size() as u32; + let queue_size = u32::from(blk.borrow().get_queue_size()); // Set invalid queue size. if err_type == 1 { @@ -937,18 +937,19 @@ fn virtio_init_device_abnormal_vring_info() { vq.borrow_mut().indirect = (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) != 0; vq.borrow_mut().event = (features & (1 << VIRTIO_RING_F_EVENT_IDX)) != 0; - let addr = alloc - .borrow_mut() - .alloc(get_vring_size(queue_size, VIRTIO_PCI_VRING_ALIGN) as u64); + let addr = alloc.borrow_mut().alloc(u64::from(get_vring_size( + queue_size, + VIRTIO_PCI_VRING_ALIGN, + ))); vq.borrow_mut().desc = addr; - let avail = addr + (queue_size * size_of::() as u32) as u64 + 16; + let avail = addr + u64::from(queue_size * size_of::() as u32) + 16; vq.borrow_mut().avail = avail; let used = (avail - + (size_of::() as u32 * (3 + queue_size)) as u64 - + VIRTIO_PCI_VRING_ALIGN as u64 + + u64::from(size_of::() as u32 * (3 + queue_size)) + + u64::from(VIRTIO_PCI_VRING_ALIGN) - 1) - & !(VIRTIO_PCI_VRING_ALIGN as u64 - 1) + 16; + & !(u64::from(VIRTIO_PCI_VRING_ALIGN) - 1) + 16; vq.borrow_mut().used = used + 16; match err_type { @@ -1015,32 +1016,32 @@ fn virtio_init_device_abnormal_vring_info() { let notify_off = blk.borrow().pci_dev.io_readw( blk.borrow().bar, - blk.borrow().common_base as u64 + u64::from(blk.borrow().common_base) + offset_of!(VirtioPciCommonCfg, queue_notify_off) as u64, ); - vq.borrow_mut().queue_notify_off = blk.borrow().notify_base as u64 - + notify_off as u64 * blk.borrow().notify_off_multiplier as u64; + vq.borrow_mut().queue_notify_off = u64::from(blk.borrow().notify_base) + + u64::from(notify_off) * u64::from(blk.borrow().notify_off_multiplier); let offset = offset_of!(VirtioPciCommonCfg, queue_enable) as u64; // TEST enable vq with 0 if err_type == 9 { blk.borrow().pci_dev.io_writew( blk.borrow().bar, - blk.borrow().common_base as u64 + offset, + u64::from(blk.borrow().common_base) + offset, 0, ); } else { blk.borrow().pci_dev.io_writew( blk.borrow().bar, - blk.borrow().common_base as u64 + u64::from(blk.borrow().common_base) + offset_of!(VirtioPciCommonCfg, queue_enable) as u64, 1, ); if err_type == 10 { - let status = blk - .borrow() - .pci_dev - .io_readw(blk.borrow().bar, blk.borrow().common_base as u64 + offset); + let status = blk.borrow().pci_dev.io_readw( + blk.borrow().bar, + u64::from(blk.borrow().common_base) + offset, + ); assert_eq!(status, 1); } } @@ -1481,11 +1482,11 @@ fn virtio_io_abnormal_desc_len() { if length == 16 { test_state.borrow().writel( indirect_desc + offset_of!(VringDesc, len) as u64, - u16::MAX as u32 * (VRING_DESC_SIZE as u32 + 1), + u32::from(u16::MAX) * (VRING_DESC_SIZE as u32 + 1), ); test_state.borrow().writel( indirect_desc + offset_of!(VringDesc, flags) as u64, - (VRING_DESC_F_INDIRECT | VRING_DESC_F_NEXT) as u32, + u32::from(VRING_DESC_F_INDIRECT | VRING_DESC_F_NEXT), ); } } @@ -1595,7 +1596,7 @@ fn virtio_io_abnormal_desc_flags_2() { let free_head = vqs[0] .borrow_mut() .add(test_state.clone(), req_addr, REQ_ADDR_LEN, false); - let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); @@ -1607,7 +1608,7 @@ fn virtio_io_abnormal_desc_flags_2() { indirect_req.setup(alloc.clone(), test_state.clone(), 2); indirect_req.add_desc( test_state.clone(), - req_addr + REQ_ADDR_LEN as u64, + req_addr + u64::from(REQ_ADDR_LEN), REQ_DATA_LEN, false, ); @@ -1670,7 +1671,7 @@ fn virtio_io_abnormal_desc_flags_3() { .borrow_mut() .add(test_state.clone(), req_addr, 8, false); - let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); @@ -1687,7 +1688,7 @@ fn virtio_io_abnormal_desc_flags_3() { .add_indirect(test_state.clone(), indirect_req, true); // Add VRING_DESC_F_WRITE or VRING_DESC_F_NEXT to desc[0]->flags; - let addr = vqs[0].borrow().desc + 16_u64 * (free_head + 1) as u64 + 12; + let addr = vqs[0].borrow().desc + 16_u64 * u64::from(free_head + 1) + 12; let flags = test_state.borrow().readw(addr) | flag; test_state.borrow().writew(addr, flags); blk.borrow().virtqueue_notify(vqs[0].clone()); @@ -1871,7 +1872,7 @@ fn virtio_io_abnormal_indirect_desc_elem_num() { let free_head = vqs[0] .borrow_mut() .add(test_state.clone(), req_addr, REQ_ADDR_LEN, false); - let offset = free_head as u64 * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; + let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 544c88083..941626a46 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -1326,7 +1326,7 @@ fn get_xattr(fs: &VirtioFsTest, name: String, nodeid: u64) -> (FuseOutHeader, St let fuse_read_out = fs .state .borrow() - .memread(outbodyaddr, DEFAULT_XATTR_SIZE as u64); + .memread(outbodyaddr, u64::from(DEFAULT_XATTR_SIZE)); let attr = String::from_utf8(fuse_read_out).unwrap(); (out_header, attr) @@ -1804,7 +1804,7 @@ fn regularfile_xattr_test() { let attr_list = fs .state .borrow() - .memread(outbodyaddr, DEFAULT_XATTR_SIZE as u64); + .memread(outbodyaddr, u64::from(DEFAULT_XATTR_SIZE)); // The first attr is "security.selinux" let (_attr1, next1) = read_cstring(attr_list.clone(), 0); // The next attrs are what we set by FUSE_SETXATTR. Check it. diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index e4f4fdf16..f6065c6aa 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -554,12 +554,12 @@ fn test_vnc_kbd_mouse() { assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); // Key event. for &(name, keysym, keycode) in KEYEVENTLIST.iter() { - assert!(vnc_client.test_key_event(0, keysym as u32).is_ok()); + assert!(vnc_client.test_key_event(0, u32::from(keysym)).is_ok()); let msg = input.borrow_mut().read_input_event(); println!("key {:?}: {:?}", name, msg); assert_eq!(msg.keycode, keycode); assert_eq!(msg.down, 0); - assert!(vnc_client.test_key_event(1, keysym as u32).is_ok()); + assert!(vnc_client.test_key_event(1, u32::from(keysym)).is_ok()); let msg = input.borrow_mut().read_input_event(); println!("key {:?}: {:?}", name, msg); diff --git a/ui/src/console.rs b/ui/src/console.rs index 97dc06e98..8977f1a22 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -777,8 +777,8 @@ pub fn console_select(con_id: Option) -> Result<()> { /// * `height` - height of image. /// * `msg` - test messages showed in display. pub fn create_msg_surface(width: i32, height: i32, msg: String) -> Option { - if !(0..MAX_WINDOW_WIDTH as i32).contains(&width) - || !(0..MAX_WINDOW_HEIGHT as i32).contains(&height) + if !(0..i32::from(MAX_WINDOW_WIDTH)).contains(&width) + || !(0..i32::from(MAX_WINDOW_HEIGHT)).contains(&height) { error!("The size of image is invalid!"); return None; diff --git a/ui/src/pixman.rs b/ui/src/pixman.rs index 8c5a20087..216321065 100644 --- a/ui/src/pixman.rs +++ b/ui/src/pixman.rs @@ -36,7 +36,7 @@ pub struct ColorInfo { impl ColorInfo { pub fn set_color_info(&mut self, shift: u8, max: u16) { - self.mask = (max as u32) << (shift as u32); + self.mask = u32::from(max) << u32::from(shift); self.shift = shift; self.max = if max == 0 { 0xFF } else { max as u8 }; self.bits = max.popcnt() as u8; @@ -84,10 +84,14 @@ impl PixelFormat { self.green.max = ((1 << self.green.bits) - 1) as u8; self.blue.max = ((1 << self.blue.bits) - 1) as u8; - self.alpha_chl.mask = self.alpha_chl.max.wrapping_shl(self.alpha_chl.shift as u32) as u32; - self.red.mask = self.red.max.wrapping_shl(self.red.shift as u32) as u32; - self.green.mask = self.green.max.wrapping_shl(self.green.shift as u32) as u32; - self.blue.mask = self.blue.max.wrapping_shl(self.blue.shift as u32) as u32; + self.alpha_chl.mask = u32::from( + self.alpha_chl + .max + .wrapping_shl(u32::from(self.alpha_chl.shift)), + ); + self.red.mask = u32::from(self.red.max.wrapping_shl(u32::from(self.red.shift))); + self.green.mask = u32::from(self.green.max.wrapping_shl(u32::from(self.green.shift))); + self.blue.mask = u32::from(self.blue.max.wrapping_shl(u32::from(self.blue.shift))); } pub fn is_default_pixel_format(&self) -> bool { diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index a9427f67e..ec978d801 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -278,14 +278,14 @@ impl AioCb { pub fn is_misaligned(&self) -> bool { if self.direct && (self.opcode == OpCode::Preadv || self.opcode == OpCode::Pwritev) { - if (self.offset as u64) & (self.req_align as u64 - 1) != 0 { + if (self.offset as u64) & (u64::from(self.req_align) - 1) != 0 { return true; } for iov in self.iovec.iter() { - if iov.iov_base & (self.buf_align as u64 - 1) != 0 { + if iov.iov_base & (u64::from(self.buf_align) - 1) != 0 { return true; } - if iov.iov_len & (self.req_align as u64 - 1) != 0 { + if iov.iov_len & (u64::from(self.req_align) - 1) != 0 { return true; } } @@ -295,8 +295,8 @@ impl AioCb { pub fn handle_misaligned(&mut self) -> Result { let max_len = round_down( - self.nbytes + self.req_align as u64 * 2, - self.req_align as u64, + self.nbytes + u64::from(self.req_align) * 2, + u64::from(self.req_align), ) .with_context(|| "Failed to round down request length.")?; // Set upper limit of buffer length to avoid OOM. @@ -327,10 +327,10 @@ impl AioCb { bounce_buffer: *mut c_void, buffer_len: u64, ) -> Result<()> { - let offset_align = round_down(self.offset as u64, self.req_align as u64) + let offset_align = round_down(self.offset as u64, u64::from(self.req_align)) .with_context(|| "Failed to round down request offset.")?; let high = self.offset as u64 + self.nbytes; - let high_align = round_up(high, self.req_align as u64) + let high_align = round_up(high, u64::from(self.req_align)) .with_context(|| "Failed to round up request high edge.")?; match self.opcode { @@ -403,7 +403,7 @@ impl AioCb { head_loaded = true; } // Is head and tail in the same alignment section? - let same_section = (offset_align + self.req_align as u64) >= high; + let same_section = (offset_align + u64::from(self.req_align)) >= high; let need_tail = !(same_section && head_loaded) && (high_align > high); let mut offset = offset_align; @@ -419,7 +419,7 @@ impl AioCb { if real_high == high && need_tail { let len = raw_read( self.file_fd, - bounce_buffer as u64 + nbytes - self.req_align as u64, + bounce_buffer as u64 + nbytes - u64::from(self.req_align), self.req_align as usize, (offset + nbytes) as usize - self.req_align as usize, ); @@ -574,7 +574,7 @@ impl Aio { -1 } }; - return (self.complete_func)(&cb, ret as i64); + return (self.complete_func)(&cb, i64::from(ret)); } cb.try_convert_to_write_zero(); @@ -586,7 +586,7 @@ impl Aio { OpCode::WriteZeroes | OpCode::WriteZeroesUnmap => cb.write_zeroes_sync(), OpCode::Noop => return Err(anyhow!("Aio opcode is not specified.")), }; - (self.complete_func)(&cb, ret as i64) + (self.complete_func)(&cb, i64::from(ret)) } pub fn flush_request(&mut self) -> Result<()> { @@ -941,7 +941,7 @@ mod tests { let fsize: usize = 2 << 20; // perform sync rw in the same alignment section. - let minor_align = align as u64 - 100; + let minor_align = u64::from(align) - 100; perform_sync_rw(fsize, 0, minor_align, opcode, direct, align); perform_sync_rw(fsize, 50, minor_align, opcode, direct, align); perform_sync_rw(fsize, 100, minor_align, opcode, direct, align); diff --git a/util/src/aio/threads.rs b/util/src/aio/threads.rs index cab4fe08b..1aecf948e 100644 --- a/util/src/aio/threads.rs +++ b/util/src/aio/threads.rs @@ -63,7 +63,7 @@ impl ThreadsTasks { let aio_event = AioEvent { user_data: task.user_data, status: 0, - res: res as i64, + res: i64::from(res), }; self.complete_lists.lock().unwrap().push(aio_event); self.notify_event diff --git a/util/src/aio/uring.rs b/util/src/aio/uring.rs index 367a85eb2..f1d373a65 100644 --- a/util/src/aio/uring.rs +++ b/util/src/aio/uring.rs @@ -105,7 +105,7 @@ impl AioContext for IoUringContext { self.events.push(AioEvent { user_data: cqe.user_data(), status: 0, - res: cqe.result() as i64, + res: i64::from(cqe.result()), }); } &self.events diff --git a/util/src/edid.rs b/util/src/edid.rs index 17080d70b..f124d6d68 100644 --- a/util/src/edid.rs +++ b/util/src/edid.rs @@ -495,7 +495,7 @@ impl EdidInfo { fn fullfill_checksum(&mut self, edid_array: &mut [u8]) { let mut sum: u32 = 0; for elem in edid_array.iter() { - sum += *elem as u32; + sum += u32::from(*elem); } sum &= 0xff; if sum != 0 { diff --git a/util/src/file.rs b/util/src/file.rs index 0fd944508..fd06c78e0 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -92,7 +92,7 @@ pub fn get_file_alignment(file: &File, direct: bool) -> (u32, u32) { while align <= MAX_FILE_ALIGN { if is_io_aligned( file, - aligned_buffer as u64 + align as u64, + aligned_buffer as u64 + u64::from(align), MAX_FILE_ALIGN as usize, ) { buf_align = align; diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index cde308c4b..9390e17bf 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -75,7 +75,7 @@ impl LeakBucket { // update the water level let now = get_current_time(); let nanos = (now - self.prev_time).as_nanos(); - if nanos > (self.level * NANOSECONDS_PER_SECOND / self.capacity) as u128 { + if nanos > u128::from(self.level * NANOSECONDS_PER_SECOND / self.capacity) { self.level = 0; } else { self.level -= nanos as u64 * self.capacity / NANOSECONDS_PER_SECOND; diff --git a/util/src/logger.rs b/util/src/logger.rs index 889e788f9..1652d83cd 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -34,7 +34,7 @@ fn format_now() -> String { println!("{:?}", e); (0, 0) }); - let format_time = get_format_time(sec as i64); + let format_time = get_format_time(i64::from(sec)); format!( "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}", @@ -63,7 +63,7 @@ impl FileRotate { self.current_size += Wrapping(size_inc); let sec = gettime()?.0; - let today = get_format_time(sec as i64)[2]; + let today = get_format_time(i64::from(sec))[2]; if self.current_size < Wrapping(LOG_ROTATE_SIZE_MAX) && self.create_day == today { return Ok(()); } diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 2be535ae5..bf193ce33 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -243,7 +243,7 @@ pub fn extract_u64(value: u64, start: u32, length: u32) -> Option { return None; } - Some((value >> start as u64) & (!(0_u64) >> (64 - length) as u64)) + Some((value >> u64::from(start)) & (!(0_u64) >> u64::from(64 - length))) } /// Deposit @fieldval into the 32 bit @value at the bit field specified @@ -371,8 +371,8 @@ pub fn write_data_u32(data: &mut [u8], value: u32) -> bool { /// ``` pub fn read_data_u32(data: &[u8], value: &mut u32) -> bool { *value = match data.len() { - 1 => data[0] as u32, - 2 => LittleEndian::read_u16(data) as u32, + 1 => u32::from(data[0]), + 2 => u32::from(LittleEndian::read_u16(data)), 4 => LittleEndian::read_u32(data), _ => { error!("Invalid data length: data len {}", data.len()); @@ -401,7 +401,7 @@ pub fn read_data_u32(data: &[u8], value: &mut u32) -> bool { /// ``` pub fn read_data_u16(data: &[u8], value: &mut u16) -> bool { *value = match data.len() { - 1 => data[0] as u16, + 1 => u16::from(data[0]), 2 => LittleEndian::read_u16(data), _ => { error!("Invalid data length: data len {}", data.len()); diff --git a/util/src/tap.rs b/util/src/tap.rs index 462de2890..a523873e1 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -140,7 +140,7 @@ impl Tap { pub fn set_offload(&self, flags: u32) -> Result<()> { let ret = // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. - unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) }; + unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), u64::from(flags)) }; if ret < 0 { return Err(anyhow!("ioctl TUNSETOFFLOAD failed.".to_string())); } @@ -162,7 +162,7 @@ impl Tap { let flags = TUN_F_CSUM | TUN_F_UFO; ( // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. - unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), flags as libc::c_ulong) } + unsafe { ioctl_with_val(self.file.as_ref(), TUNSETOFFLOAD(), u64::from(flags)) } ) >= 0 } diff --git a/util/src/unix.rs b/util/src/unix.rs index 6a723ed25..ae1b1a412 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -278,7 +278,9 @@ impl UnixSock { let nex_cmsg_pos = (next_cmsg as *mut u8).wrapping_sub(msghdr.msg_control as usize) as u64; // SAFETY: Parameter is constant. - if nex_cmsg_pos.wrapping_add(unsafe { CMSG_LEN(0) } as u64) > msghdr.msg_controllen as u64 { + if nex_cmsg_pos.wrapping_add(u64::from(unsafe { CMSG_LEN(0) })) + > msghdr.msg_controllen as u64 + { null_mut() } else { next_cmsg @@ -420,7 +422,7 @@ impl UnixSock { if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS { // SAFETY: Input parameter is constant. - let fd_count = (cmsg.cmsg_len as u64 - unsafe { CMSG_LEN(0) } as u64) as usize + let fd_count = (cmsg.cmsg_len as u64 - u64::from(unsafe { CMSG_LEN(0) })) as usize / size_of::(); // SAFETY: // 1. the pointer of cmsg_ptr was created in this function and can be guaranteed not be null. diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 5dad81631..aea7891c7 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -230,13 +230,13 @@ impl VfioPciDevice { self.vfio_device.lock().unwrap().write_region( data.as_slice(), self.config_offset, - COMMAND as u64, + u64::from(COMMAND), )?; for i in 0..PCI_ROM_SLOT { let offset = BAR_0 as usize + REG_SIZE * i as usize; let v = le_read_u32(&self.base.config.config, offset)?; - if v & BAR_IO_SPACE as u32 != 0 { + if v & u32::from(BAR_IO_SPACE) != 0 { le_write_u32(&mut self.base.config.config, offset, v & !IO_BASE_ADDR_MASK)?; } else { le_write_u32( @@ -274,8 +274,8 @@ impl VfioPciDevice { Ok(VfioMsixInfo { table: MsixTable { table_bar: (table as u16 & MSIX_TABLE_BIR) as u8, - table_offset: (table & MSIX_TABLE_OFFSET) as u64, - table_size: (entries * MSIX_TABLE_ENTRY_SIZE) as u64, + table_offset: u64::from(table & MSIX_TABLE_OFFSET), + table_size: u64::from(entries * MSIX_TABLE_ENTRY_SIZE), }, entries, }) @@ -295,13 +295,13 @@ impl VfioPciDevice { locked_dev.read_region( data.as_mut_slice(), self.config_offset, - (BAR_0 + (REG_SIZE as u8) * i) as u64, + u64::from(BAR_0 + (REG_SIZE as u8) * i), )?; let mut region_type = RegionType::Mem32Bit; let pci_bar = LittleEndian::read_u32(&data); - if pci_bar & BAR_IO_SPACE as u32 != 0 { + if pci_bar & u32::from(BAR_IO_SPACE) != 0 { region_type = RegionType::Io; - } else if pci_bar & BAR_MEM_64BIT as u32 != 0 { + } else if pci_bar & u32::from(BAR_MEM_64BIT) != 0 { region_type = RegionType::Mem64Bit; } let vfio_region = infos.remove(0); @@ -515,7 +515,7 @@ impl VfioPciDevice { let mut locked_msix = msix.lock().unwrap(); locked_msix.table[offset as usize..(offset as usize + data.len())] .copy_from_slice(data); - let vector = offset / MSIX_TABLE_ENTRY_SIZE as u64; + let vector = offset / u64::from(MSIX_TABLE_ENTRY_SIZE); if locked_msix.is_vector_masked(vector as u16) { return true; } @@ -530,7 +530,7 @@ impl VfioPciDevice { msg_data: entry.data, masked: false, #[cfg(target_arch = "aarch64")] - dev_id: dev_id.load(Ordering::Acquire) as u32, + dev_id: u32::from(dev_id.load(Ordering::Acquire)), }; let mut locked_gsi_routes = cloned_gsi_routes.lock().unwrap(); @@ -737,7 +737,7 @@ impl VfioPciDevice { let gsi_route = GsiMsiRoute { irq_fd: None, gsi: -1, - nr: i as u32, + nr: u32::from(i), }; gsi_routes.push(gsi_route); } @@ -864,7 +864,7 @@ impl Device for VfioPciDevice { )?)); Result::with_context(self.register_bars(), || "Failed to register bars")?; - let devfn = self.base.devfn as u64; + let devfn = u64::from(self.base.devfn); let dev = Arc::new(Mutex::new(self)); let parent_bus = dev.lock().unwrap().parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = parent_bus.lock().unwrap(); @@ -971,7 +971,8 @@ impl PciDevOps for VfioPciDevice { ); if ranges_overlap(offset, size, COMMAND as usize, REG_SIZE).unwrap() { - if le_read_u32(&self.base.config.config, offset).unwrap() & COMMAND_MEMORY_SPACE as u32 + if le_read_u32(&self.base.config.config, offset).unwrap() + & u32::from(COMMAND_MEMORY_SPACE) != 0 { if let Err(e) = self.setup_bars_mmap() { diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 4c981c185..4dd36fc02 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -218,7 +218,7 @@ impl Request { for elem_iov in iovec { request.iovec.push(GuestIovec { iov_base: elem_iov.addr, - iov_len: elem_iov.len as u64, + iov_len: u64::from(elem_iov.len), }); request.elem_cnt += elem_iov.len; } @@ -280,7 +280,7 @@ impl Request { while let Some(pfn) = iov_to_buf::(address_space, iov, offset) { offset += std::mem::size_of::() as u64; - let gpa: GuestAddress = GuestAddress((pfn as u64) << VIRTIO_BALLOON_PFN_SHIFT); + let gpa: GuestAddress = GuestAddress(u64::from(pfn) << VIRTIO_BALLOON_PFN_SHIFT); let (hva, shared) = match mem.lock().unwrap().get_host_address(gpa) { Some((addr, mem_share)) => (addr, mem_share), None => { @@ -741,7 +741,7 @@ impl BalloonIoHandler { /// Get the memory size of balloon. fn get_balloon_memory_size(&self) -> u64 { - (self.balloon_actual.load(Ordering::Acquire) as u64) << VIRTIO_BALLOON_PFN_SHIFT + u64::from(self.balloon_actual.load(Ordering::Acquire)) << VIRTIO_BALLOON_PFN_SHIFT } } @@ -916,9 +916,9 @@ impl ConfigCheck for BalloonConfig { { return Err(anyhow!(ConfigError::IllegalValue( "balloon membuf-percent".to_string(), - MEM_BUFFER_PERCENT_MIN as u64, + u64::from(MEM_BUFFER_PERCENT_MIN), false, - MEM_BUFFER_PERCENT_MAX as u64, + u64::from(MEM_BUFFER_PERCENT_MAX), false, ))); } @@ -927,9 +927,9 @@ impl ConfigCheck for BalloonConfig { { return Err(anyhow!(ConfigError::IllegalValue( "balloon monitor-interval".to_string(), - MONITOR_INTERVAL_SECOND_MIN as u64, + u64::from(MONITOR_INTERVAL_SECOND_MIN), false, - MONITOR_INTERVAL_SECOND_MAX as u64, + u64::from(MONITOR_INTERVAL_SECOND_MAX), false, ))); } @@ -1036,7 +1036,7 @@ impl Balloon { /// Get the size of memory that reclaimed by balloon. fn get_balloon_memory_size(&self) -> u64 { - (self.actual.load(Ordering::Acquire) as u64) << VIRTIO_BALLOON_PFN_SHIFT + u64::from(self.actual.load(Ordering::Acquire)) << VIRTIO_BALLOON_PFN_SHIFT } /// Get the actual memory size of guest. @@ -1291,13 +1291,13 @@ mod tests { bln.base.device_features = 1 | 1 << 32; bln.set_driver_features(0, 1); assert_eq!(bln.base.driver_features, 1); - assert_eq!(bln.base.driver_features, bln.driver_features(0) as u64); + assert_eq!(bln.base.driver_features, u64::from(bln.driver_features(0))); bln.base.driver_features = 1 << 32; bln.set_driver_features(1, 1); assert_eq!(bln.base.driver_features, 1 << 32); assert_eq!( bln.base.driver_features, - (bln.driver_features(1) as u64) << 32 + u64::from(bln.driver_features(1)) << 32 ); // Test methods of balloon. diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index cb0a51c0c..d64c56fe1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -299,7 +299,7 @@ impl Request { ); } // Note: addr plus len has been checked not overflow in virtqueue. - let in_header = GuestAddress(in_iov_elem.addr.0 + in_iov_elem.len as u64 - 1); + let in_header = GuestAddress(in_iov_elem.addr.0 + u64::from(in_iov_elem.len) - 1); let mut request = Request { desc_index: elem.index, @@ -464,7 +464,7 @@ impl Request { let sector = LittleEndian::read_u64(segment.sector.as_bytes()); let num_sectors = LittleEndian::read_u32(segment.num_sectors.as_bytes()); if sector - .checked_add(num_sectors as u64) + .checked_add(u64::from(num_sectors)) .filter(|&off| off <= iohandler.disk_sectors) .is_none() || num_sectors > MAX_REQUEST_SECTORS @@ -485,7 +485,7 @@ impl Request { let block_backend = iohandler.block_backend.as_ref().unwrap(); let mut locked_backend = block_backend.lock().unwrap(); let offset = (sector as usize) << SECTOR_SHIFT; - let nbytes = (num_sectors as u64) << SECTOR_SHIFT; + let nbytes = u64::from(num_sectors) << SECTOR_SHIFT; trace::virtio_blk_handle_discard_write_zeroes_req(&opcode, flags, offset, nbytes); if opcode == OpCode::Discard { if flags == VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP { @@ -1068,7 +1068,7 @@ impl Block { // capacity: 64bits self.config_space.capacity = self.disk_sectors; // seg_max = queue_size - 2: 32bits - self.config_space.seg_max = self.queue_size_max() as u32 - 2; + self.config_space.seg_max = u32::from(self.queue_size_max()) - 2; if self.blk_cfg.num_queues.unwrap_or(1) > 1 { self.config_space.num_queues = self.blk_cfg.num_queues.unwrap_or(1); @@ -1547,14 +1547,14 @@ mod tests { let page = 0_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.base.driver_features, 0_u64); - assert_eq!(block.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(block.driver_features(page)), 0_u64); assert_eq!(block.device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; block.set_driver_features(page, driver_feature); assert_eq!(block.base.driver_features, 0_u64); - assert_eq!(block.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(block.driver_features(page)), 0_u64); assert_eq!(block.device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are @@ -1569,7 +1569,7 @@ mod tests { (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) ); assert_eq!( - block.driver_features(page) as u64, + u64::from(block.driver_features(page)), (1_u64 << VIRTIO_F_RING_INDIRECT_DESC) ); assert_eq!( @@ -1663,10 +1663,10 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * DEFAULT_VIRTQUEUE_SIZE as u64); + queue_config.avail_ring = GuestAddress(16 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * DEFAULT_VIRTQUEUE_SIZE as u64); + queue_config.used_ring = GuestAddress(32 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); queue_config.size = DEFAULT_VIRTQUEUE_SIZE; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index d2c1078b2..1145a30f5 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -670,8 +670,8 @@ pub fn cal_image_hostmem(format: u32, width: u32, height: u32) -> (Option } }; let bpp = pixman_format_bpp(pixman_format as u32); - let stride = ((width as u64 * bpp as u64 + 0x1f) >> 5) * (size_of::() as u64); - match stride.checked_mul(height as u64) { + let stride = ((u64::from(width) * u64::from(bpp) + 0x1f) >> 5) * (size_of::() as u64); + match stride.checked_mul(u64::from(height)) { None => { error!( "stride * height is overflow: width {} height {} stride {} bpp {}", @@ -1172,7 +1172,7 @@ impl GpuIoHandler { } let pixman_format = get_image_format(res.pixman_image); - let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + let bpp = (u32::from(pixman_format_bpp(pixman_format as u32)) + 8 - 1) / 8; let pixman_stride = get_image_stride(res.pixman_image); let offset = info_set_scanout.rect.x_coord * bpp + info_set_scanout.rect.y_coord * pixman_stride as u32; @@ -1301,10 +1301,10 @@ impl GpuIoHandler { let extents = pixman_region_extents(final_reg_ptr); display_graphic_update( &scanout.con, - (*extents).x1 as i32, - (*extents).y1 as i32, - ((*extents).x2 - (*extents).x1) as i32, - ((*extents).y2 - (*extents).y1) as i32, + i32::from((*extents).x1), + i32::from((*extents).y1), + i32::from((*extents).x2 - (*extents).x1), + i32::from((*extents).y2 - (*extents).y1), )?; pixman_region_fini(rect_reg_ptr); pixman_region_fini(final_reg_ptr); @@ -1359,7 +1359,7 @@ impl GpuIoHandler { let res = &mut self.resources_list[res_idx]; let pixman_format = get_image_format(res.pixman_image); let width = get_image_width(res.pixman_image) as u32; - let bpp = (pixman_format_bpp(pixman_format as u32) as u32 + 8 - 1) / 8; + let bpp = (u32::from(pixman_format_bpp(pixman_format as u32)) + 8 - 1) / 8; let stride = get_image_stride(res.pixman_image) as u32; let data = get_image_data(res.pixman_image).cast() as *mut u8; @@ -1452,9 +1452,9 @@ impl GpuIoHandler { } let entries = info_attach_backing.nr_entries; - let ents_size = size_of::() as u64 * entries as u64; + let ents_size = size_of::() as u64 * u64::from(entries); let head_size = size_of::() as u64; - if (req.out_len as u64) < (ents_size + head_size) { + if u64::from(req.out_len) < (ents_size + head_size) { error!( "GuestError: The nr_entries {} in resource attach backing request is larger than total len {}.", info_attach_backing.nr_entries, req.out_len, diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 689e9c592..a7794fe88 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -244,7 +244,7 @@ impl CtrlInfo { continue; } - let size = entries as u64 * MAC_ADDR_LEN as u64; + let size = u64::from(entries) * MAC_ADDR_LEN as u64; let res_len = Element::iovec_size(data_iovec); if size > res_len { bail!("Invalid request for setting mac table."); @@ -366,7 +366,7 @@ impl CtrlInfo { data_iovec: &mut Vec, ) -> u8 { let mut ack = VIRTIO_NET_OK; - if cmd as u16 == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { + if u16::from(cmd) == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { let mut queue_pairs: u16 = 0; *data_iovec = get_buf_and_discard(mem_space, data_iovec, queue_pairs.as_mut_bytes()) .unwrap_or_else(|e| { @@ -1609,7 +1609,7 @@ impl VirtioDevice for Net { // The features about offload is included in bits 0 to 31. let features = self.driver_features(0_u32); - let flags = get_tap_offload_flags(features as u64); + let flags = get_tap_offload_flags(u64::from(features)); let mut senders = Vec::new(); let queue_pairs = queue_num / 2; @@ -1699,7 +1699,7 @@ impl VirtioDevice for Net { // Set tap offload. // The features about offload is included in bits 0 to 31. let features = self.driver_features(0_u32); - let flags = get_tap_offload_flags(features as u64); + let flags = get_tap_offload_flags(u64::from(features)); if let Some(taps) = &self.taps { for (_, tap) in taps.iter().enumerate() { tap.set_offload(flags) diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 557e759cd..f0fcc4e19 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -139,7 +139,7 @@ impl RngHandler { .write( &mut buffer[offset..].as_ref(), iov.addr, - min(size - offset as u32, iov.len) as u64, + u64::from(min(size - offset as u32, iov.len)), ) .with_context(|| "Failed to write request data for virtio rng")?; @@ -166,7 +166,7 @@ impl RngHandler { get_req_data_size(&elem.in_iovec).with_context(|| "Failed to get request size")?; if let Some(leak_bucket) = self.leak_bucket.as_mut() { - if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), size as u64) { + if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), u64::from(size)) { queue_lock.vring.push_back(); break; } @@ -326,7 +326,7 @@ impl VirtioDevice for Rng { } fn init_config_features(&mut self) -> Result<()> { - self.base.device_features = 1 << VIRTIO_F_VERSION_1 as u64; + self.base.device_features = 1 << u64::from(VIRTIO_F_VERSION_1); Ok(()) } @@ -494,30 +494,30 @@ mod tests { let page = 0_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.base.driver_features, 0_u64); - assert_eq!(rng.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(rng.driver_features(page)), 0_u64); assert_eq!(rng.device_features(0_u32), 0_u32); let driver_feature: u32 = 0xFF; let page = 1_u32; rng.set_driver_features(page, driver_feature); assert_eq!(rng.base.driver_features, 0_u64); - assert_eq!(rng.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(rng.driver_features(page)), 0_u64); assert_eq!(rng.device_features(1_u32), 0_u32); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. rng.base.device_features = - 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64; + 1_u64 << VIRTIO_F_VERSION_1 | 1_u64 << u64::from(VIRTIO_F_RING_INDIRECT_DESC); let driver_feature: u32 = 1_u32 << VIRTIO_F_RING_INDIRECT_DESC; let page = 0_u32; rng.set_driver_features(page, driver_feature); assert_eq!( rng.base.driver_features, - (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) + (1_u64 << u64::from(VIRTIO_F_RING_INDIRECT_DESC)) ); assert_eq!( - rng.driver_features(page) as u64, - (1_u64 << VIRTIO_F_RING_INDIRECT_DESC as u64) + u64::from(rng.driver_features(page)), + (1_u64 << u64::from(VIRTIO_F_RING_INDIRECT_DESC)) ); assert_eq!( rng.device_features(page), @@ -543,7 +543,7 @@ mod tests { len: u32::max_value(), }, ElemIovec { - addr: GuestAddress(u32::max_value() as u64), + addr: GuestAddress(u64::from(u32::max_value())), len: 1_u32, }, ]; @@ -557,7 +557,7 @@ mod tests { len, }, ElemIovec { - addr: GuestAddress(u32::max_value() as u64), + addr: GuestAddress(u64::from(u32::max_value())), len, }, ]; @@ -591,10 +591,10 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * DEFAULT_VIRTQUEUE_SIZE as u64); + queue_config.avail_ring = GuestAddress(16 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * DEFAULT_VIRTQUEUE_SIZE as u64); + queue_config.used_ring = GuestAddress(32 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); queue_config.size = DEFAULT_VIRTQUEUE_SIZE; @@ -639,7 +639,7 @@ mod tests { .read( &mut read_buffer.as_mut_slice(), GuestAddress(0x40000), - data_len as u64 + u64::from(data_len) ) .is_ok()); assert_eq!(read_buffer, buffer); @@ -674,10 +674,10 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = mem_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress(16 * DEFAULT_VIRTQUEUE_SIZE as u64); + queue_config.avail_ring = GuestAddress(16 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); queue_config.addr_cache.avail_ring_host = mem_space.get_host_address(queue_config.avail_ring).unwrap(); - queue_config.used_ring = GuestAddress(32 * DEFAULT_VIRTQUEUE_SIZE as u64); + queue_config.used_ring = GuestAddress(32 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); queue_config.addr_cache.used_ring_host = mem_space.get_host_address(queue_config.used_ring).unwrap(); queue_config.size = DEFAULT_VIRTQUEUE_SIZE; @@ -742,7 +742,7 @@ mod tests { .read( &mut read_buffer.as_mut_slice(), GuestAddress(0x40000), - data_len as u64 + u64::from(data_len) ) .is_ok()); assert_eq!(read_buffer, buffer1_check); @@ -750,7 +750,7 @@ mod tests { .read( &mut read_buffer.as_mut_slice(), GuestAddress(0x50000), - data_len as u64 + u64::from(data_len) ) .is_ok()); assert_eq!(read_buffer, buffer2_check); diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 3970bf64a..6863b60c4 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -194,7 +194,7 @@ impl VirtioDevice for ScsiCntlr { // cmd_per_lun: maximum number of linked commands can be sent to one LUN. 32bit. self.config_space.cmd_per_lun = 128; // seg_max: queue size - 2, 32 bit. - self.config_space.seg_max = self.queue_size_max() as u32 - 2; + self.config_space.seg_max = u32::from(self.queue_size_max()) - 2; self.config_space.max_target = VIRTIO_SCSI_MAX_TARGET; self.config_space.max_lun = VIRTIO_SCSI_MAX_LUN; // num_queues: request queues number. @@ -753,7 +753,7 @@ impl ScsiRequestOps for CmdQueueRequest { // | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | // | 1 | target | lun | 0 | fn virtio_scsi_get_lun_id(lun: [u8; 8]) -> u16 { - (((lun[2] as u16) << 8) | (lun[3] as u16)) & 0x3FFF + ((u16::from(lun[2]) << 8) | u16::from(lun[3])) & 0x3FFF } struct ScsiCmdQueueHandler { diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index f8a1e35fe..5bf3aae8c 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -1018,13 +1018,13 @@ mod tests { let page = 0_u32; serial.set_driver_features(page, driver_feature); assert_eq!(serial.base.driver_features, 0_u64); - assert_eq!(serial.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(serial.driver_features(page)), 0_u64); let driver_feature: u32 = 0xFF; let page = 1_u32; serial.set_driver_features(page, driver_feature); assert_eq!(serial.base.driver_features, 0_u64); - assert_eq!(serial.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(serial.driver_features(page)), 0_u64); // If both the device feature bit and the front-end driver feature bit are // supported at the same time, this driver feature bit is supported. @@ -1037,7 +1037,7 @@ mod tests { (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); assert_eq!( - serial.driver_features(page) as u64, + u64::from(serial.driver_features(page)), (1_u64 << VIRTIO_CONSOLE_F_SIZE) ); serial.base.driver_features = 0; @@ -1075,7 +1075,7 @@ mod tests { classtype: "virtio-serial-pci".to_string(), id: "serial".to_string(), multifunction: Some(false), - max_ports: max_ports as u32, + max_ports: u32::from(max_ports), bus: Some("pcie.0".to_string()), addr: Some((0, 0)), }); diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 4c3e8e87c..f4cc3c154 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -547,9 +547,9 @@ pub trait VirtioDevice: Send + AsAny { } let features = if page == 0 { - (self.driver_features(1) as u64) << 32 | (v as u64) + u64::from(self.driver_features(1)) << 32 | u64::from(v) } else { - (v as u64) << 32 | (self.driver_features(0) as u64) + u64::from(v) << 32 | u64::from(self.driver_features(0)) }; self.virtio_base_mut().driver_features = features; } @@ -819,7 +819,7 @@ pub fn iov_to_buf( for iov in iovec { let mut addr_map = Vec::new(); - mem_space.get_address_map(cache, iov.addr, iov.len as u64, &mut addr_map)?; + mem_space.get_address_map(cache, iov.addr, u64::from(iov.len), &mut addr_map)?; for addr in addr_map.into_iter() { end = cmp::min(start + addr.iov_len as usize, buf.len()); mem_to_buf(&mut buf[start..end], addr.iov_base)?; @@ -835,12 +835,12 @@ pub fn iov_to_buf( /// Discard "size" bytes of the front of iovec. pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> { for (index, iov) in iovec.iter_mut().enumerate() { - if iov.len as u64 > size { + if u64::from(iov.len) > size { iov.addr.0 += size; iov.len -= size as u32; return Some(&mut iovec[index..]); } - size -= iov.len as u64; + size -= u64::from(iov.len); } None } @@ -849,11 +849,11 @@ pub fn iov_discard_front(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut pub fn iov_discard_back(iovec: &mut [ElemIovec], mut size: u64) -> Option<&mut [ElemIovec]> { let len = iovec.len(); for (index, iov) in iovec.iter_mut().rev().enumerate() { - if iov.len as u64 > size { + if u64::from(iov.len) > size { iov.len -= size as u32; return Some(&mut iovec[..(len - index)]); } - size -= iov.len as u64; + size -= u64::from(iov.len); } None } @@ -869,8 +869,8 @@ fn gpa_hva_iovec_map( let mut hva_iovec = Vec::with_capacity(gpa_elemiovec.len()); for elem in gpa_elemiovec.iter() { - mem_space.get_address_map(cache, elem.addr, elem.len as u64, &mut hva_iovec)?; - iov_size += elem.len as u64; + mem_space.get_address_map(cache, elem.addr, u64::from(elem.len), &mut hva_iovec)?; + iov_size += u64::from(elem.len); } Ok((iov_size, hva_iovec)) diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index 1e612b298..93d950446 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -91,7 +91,7 @@ impl Element { pub fn iovec_size(iovec: &[ElemIovec]) -> u64 { let mut size: u64 = 0; for elem in iovec.iter() { - size += elem.len as u64; + size += u64::from(elem.len); } size } diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 643693b5d..74d81d6b7 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -116,7 +116,7 @@ impl QueueConfig { } fn get_desc_size(&self) -> u64 { - min(self.size, self.max_size) as u64 * DESCRIPTOR_LEN + u64::from(min(self.size, self.max_size)) * DESCRIPTOR_LEN } fn get_used_size(&self, features: u64) -> u64 { @@ -126,7 +126,7 @@ impl QueueConfig { 0_u64 }; - size + VRING_FLAGS_AND_IDX_LEN + (min(self.size, self.max_size) as u64) * USEDELEM_LEN + size + VRING_FLAGS_AND_IDX_LEN + u64::from(min(self.size, self.max_size)) * USEDELEM_LEN } fn get_avail_size(&self, features: u64) -> u64 { @@ -137,7 +137,7 @@ impl QueueConfig { }; size + VRING_FLAGS_AND_IDX_LEN - + (min(self.size, self.max_size) as u64) * (size_of::() as u64) + + u64::from(min(self.size, self.max_size)) * (size_of::() as u64) } pub fn reset(&mut self) { @@ -290,7 +290,7 @@ impl SplitVringDesc { let mut miss_cached = true; if let Some(reg_cache) = cache { let base = self.addr.0; - let offset = self.len as u64; + let offset = u64::from(self.len); let end = match base.checked_add(offset) { Some(addr) => addr, None => { @@ -361,7 +361,7 @@ impl SplitVringDesc { fn is_valid_indirect_desc(&self) -> bool { if self.len == 0 || u64::from(self.len) % DESCRIPTOR_LEN != 0 - || u64::from(self.len) / DESCRIPTOR_LEN > u16::MAX as u64 + || u64::from(self.len) / DESCRIPTOR_LEN > u64::from(u16::MAX) { error!("The indirect descriptor is invalid, len: {}", self.len); return false; @@ -435,7 +435,7 @@ impl SplitVringDesc { elem.out_iovec.push(iovec); } elem.desc_num += 1; - desc_total_len += iovec.len as u64; + desc_total_len += u64::from(iovec.len); if desc.has_next() { desc = Self::next_desc(sys_mem, desc_table_host, queue_size, desc.next, cache)?; @@ -1030,7 +1030,7 @@ mod tests { return Err(anyhow!(VirtioError::QueueIndex(index, self.size))); } - let desc_addr_offset = DESCRIPTOR_LEN * index as u64; + let desc_addr_offset = DESCRIPTOR_LEN * u64::from(index); let desc = SplitVringDesc { addr, len, @@ -1065,7 +1065,7 @@ mod tests { avail_pos: u16, desc_index: u16, ) -> Result<()> { - let avail_idx_offset = VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * (avail_pos as u64); + let avail_idx_offset = VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(avail_pos); sys_mem.write_object::( &desc_index, GuestAddress(self.avail_ring.0 + avail_idx_offset), @@ -1075,14 +1075,14 @@ mod tests { fn get_avail_event(&self, sys_mem: &Arc) -> Result { let avail_event_idx_offset = - VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * (self.actual_size() as u64); + VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(self.actual_size()); let event_idx = sys_mem .read_object::(GuestAddress(self.used_ring.0 + avail_event_idx_offset))?; Ok(event_idx) } fn get_used_elem(&self, sys_mem: &Arc, index: u16) -> Result { - let used_elem_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * (index as u64); + let used_elem_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(index); let used_elem = sys_mem .read_object::(GuestAddress(self.used_ring.0 + used_elem_offset))?; Ok(used_elem) @@ -1103,7 +1103,7 @@ mod tests { fn set_used_event_idx(&self, sys_mem: &Arc, idx: u16) -> Result<()> { let event_idx_offset = - VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * (self.actual_size() as u64); + VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.actual_size()); sys_mem .write_object::(&idx, GuestAddress(self.avail_ring.0 + event_idx_offset))?; Ok(()) @@ -1154,11 +1154,11 @@ mod tests { // it is valid queue_config.desc_table = GuestAddress(0); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.ready = true; @@ -1194,11 +1194,11 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.ready = true; @@ -1208,7 +1208,7 @@ mod tests { // it is invalid when the address of descriptor table is out of bound queue_config.desc_table = - GuestAddress(SYSTEM_SPACE_SIZE - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + 1 as u64); + GuestAddress(SYSTEM_SPACE_SIZE - u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + 1 as u64); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue @@ -1219,29 +1219,29 @@ mod tests { // it is invalid when the address of avail ring is out of bound queue_config.avail_ring = GuestAddress( SYSTEM_SPACE_SIZE - - (VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * (QUEUE_SIZE as u64)) + - (VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE)) + 1 as u64, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), true); // it is invalid when the address of used ring is out of bound queue_config.used_ring = GuestAddress( SYSTEM_SPACE_SIZE - - (VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * (QUEUE_SIZE as u64)) + - (VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * u64::from(QUEUE_SIZE)) + 1 as u64, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); @@ -1254,11 +1254,11 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.ready = true; @@ -1271,29 +1271,29 @@ mod tests { let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), true); // it is invalid when the address of descriptor table is overlapped to the address of avail // ring. - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN - 1); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN - 1); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), true); // it is invalid when the address of avail ring is equal to the address of used ring - queue_config.used_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.used_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); @@ -1301,18 +1301,18 @@ mod tests { // it is invalid when the address of avail ring is overlapped to the address of used ring queue_config.used_ring = GuestAddress( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64) + + AVAILELEM_LEN * u64::from(QUEUE_SIZE) - 1, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); @@ -1325,11 +1325,11 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.ready = true; @@ -1347,28 +1347,28 @@ mod tests { assert_eq!(queue.is_valid(&sys_space), true); // it is invalid when the address of avail ring is not aligned to 2 - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN + 1); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + 1); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), true); // it is invalid when the address of used ring is not aligned to 4 queue_config.used_ring = GuestAddress( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64) + + AVAILELEM_LEN * u64::from(QUEUE_SIZE) + 3, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); assert_eq!(queue.is_valid(&sys_space), false); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); @@ -1383,13 +1383,13 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = sys_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.addr_cache.avail_ring_host = sys_space.get_host_address(queue_config.avail_ring).unwrap(); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.addr_cache.used_ring_host = @@ -1434,7 +1434,7 @@ mod tests { // set 1 to the idx of avail ring vring.set_avail_ring_idx(&sys_space, 1).unwrap(); - let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; + let features = 1 << u64::from(VIRTIO_F_RING_EVENT_IDX); let elem = match vring.pop_avail(&sys_space, features) { Ok(ret) => ret, Err(_) => Element { @@ -1473,13 +1473,13 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = sys_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.addr_cache.avail_ring_host = sys_space.get_host_address(queue_config.avail_ring).unwrap(); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.addr_cache.used_ring_host = @@ -1540,7 +1540,7 @@ mod tests { // set 1 to the idx of avail ring vring.set_avail_ring_idx(&sys_space, 1).unwrap(); - let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; + let features = 1 << u64::from(VIRTIO_F_RING_EVENT_IDX); let elem = match vring.pop_avail(&sys_space, features) { Ok(ret) => ret, Err(_) => Element { @@ -1573,13 +1573,13 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = sys_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.addr_cache.avail_ring_host = sys_space.get_host_address(queue_config.avail_ring).unwrap(); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.addr_cache.used_ring_host = @@ -1592,7 +1592,7 @@ mod tests { // it is error when the idx of avail ring which is equal to next_avail // set 0 to the idx of avail ring which is equal to next_avail vring.set_avail_ring_idx(&sys_space, 0).unwrap(); - let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; + let features = 1 << u64::from(VIRTIO_F_RING_EVENT_IDX); if let Ok(elem) = vring.pop_avail(&sys_space, features) { if elem.desc_num != 0 { assert!(false); @@ -1768,13 +1768,13 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = sys_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.addr_cache.avail_ring_host = sys_space.get_host_address(queue_config.avail_ring).unwrap(); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.addr_cache.used_ring_host = @@ -1835,7 +1835,7 @@ mod tests { // Set 1 to the idx of avail ring. vring.set_avail_ring_idx(&sys_space, 1).unwrap(); - let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; + let features = 1 << u64::from(VIRTIO_F_RING_EVENT_IDX); if let Err(err) = vring.pop_avail(&sys_space, features) { assert_eq!(err.to_string(), "Failed to get vring element"); } else { @@ -1932,13 +1932,13 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = sys_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.addr_cache.avail_ring_host = sys_space.get_host_address(queue_config.avail_ring).unwrap(); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.addr_cache.used_ring_host = @@ -1976,13 +1976,13 @@ mod tests { queue_config.desc_table = GuestAddress(0); queue_config.addr_cache.desc_table_host = sys_space.get_host_address(queue_config.desc_table).unwrap(); - queue_config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * DESCRIPTOR_LEN); + queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); queue_config.addr_cache.avail_ring_host = sys_space.get_host_address(queue_config.avail_ring).unwrap(); queue_config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * DESCRIPTOR_LEN + u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM - + AVAILELEM_LEN * (QUEUE_SIZE as u64), + + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); queue_config.addr_cache.used_ring_host = @@ -2007,7 +2007,7 @@ mod tests { // it's true when the feature of event idx is open and // (new - event_idx - Wrapping(1) < new -old) - let features = 1 << VIRTIO_F_RING_EVENT_IDX as u64; + let features = 1 << u64::from(VIRTIO_F_RING_EVENT_IDX); vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 6).is_ok()); // event_idx diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index 42892b0d8..f8895221b 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -271,10 +271,10 @@ impl VirtioMmioDevice { .map(|config| u32::from(config.max_size))?, QUEUE_READY_REG => locked_device .queue_config() - .map(|config| config.ready as u32)?, + .map(|config| u32::from(config.ready))?, INTERRUPT_STATUS_REG => locked_device.interrupt_status(), STATUS_REG => locked_device.device_status(), - CONFIG_GENERATION_REG => locked_device.config_generation() as u32, + CONFIG_GENERATION_REG => u32::from(locked_device.config_generation()), // SHM_SEL is unimplemented. According to the Virtio v1.2 spec: Reading from a non-existent // region(i.e. where the ID written to SHMSel is unused) results in a length of -1. SHM_LEN_LOW | SHM_LEN_HIGH => u32::MAX, @@ -745,7 +745,7 @@ mod tests { virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), true ); - assert_eq!(LittleEndian::read_u32(&buf[..]), QUEUE_SIZE as u32); + assert_eq!(LittleEndian::read_u32(&buf[..]), u32::from(QUEUE_SIZE)); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_queue_select(1); @@ -753,7 +753,7 @@ mod tests { virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), true ); - assert_eq!(LittleEndian::read_u32(&buf[..]), QUEUE_SIZE as u32); + assert_eq!(LittleEndian::read_u32(&buf[..]), u32::from(QUEUE_SIZE)); // read the register representing the status of queue // for queue_select as 0 @@ -1179,9 +1179,9 @@ mod tests { locked_device.set_device_status(CONFIG_STATUS_FEATURES_OK); if let Ok(config) = locked_device.queue_config_mut(true) { config.desc_table = GuestAddress(0); - config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * 16); + config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * 16); config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * 16 + 8 + 2 * (QUEUE_SIZE as u64), + u64::from(QUEUE_SIZE) * 16 + 8 + 2 * u64::from(QUEUE_SIZE), 4096, )); config.size = QUEUE_SIZE; @@ -1190,9 +1190,9 @@ mod tests { locked_device.set_queue_select(1); if let Ok(config) = locked_device.queue_config_mut(true) { config.desc_table = GuestAddress(0); - config.avail_ring = GuestAddress((QUEUE_SIZE as u64) * 16); + config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * 16); config.used_ring = GuestAddress(align( - (QUEUE_SIZE as u64) * 16 + 8 + 2 * (QUEUE_SIZE as u64), + u64::from(QUEUE_SIZE) * 16 + 8 + 2 * u64::from(QUEUE_SIZE), 4096, )); config.size = QUEUE_SIZE / 2; diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 149cb329f..22f69ca2a 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -574,11 +574,11 @@ impl VirtioPciDevice { 0 } } - COMMON_MSIX_REG => locked_device.config_vector() as u32, + COMMON_MSIX_REG => u32::from(locked_device.config_vector()), COMMON_NUMQ_REG => locked_device.virtio_base().queues_config.len() as u32, COMMON_STATUS_REG => locked_device.device_status(), - COMMON_CFGGENERATION_REG => locked_device.config_generation() as u32, - COMMON_Q_SELECT_REG => locked_device.queue_select() as u32, + COMMON_CFGGENERATION_REG => u32::from(locked_device.config_generation()), + COMMON_Q_SELECT_REG => u32::from(locked_device.queue_select()), COMMON_Q_SIZE_REG => locked_device .queue_config() .map(|config| u32::from(config.size))?, @@ -588,7 +588,7 @@ impl VirtioPciDevice { COMMON_Q_ENABLE_REG => locked_device .queue_config() .map(|config| u32::from(config.ready))?, - COMMON_Q_NOFF_REG => locked_device.queue_select() as u32, + COMMON_Q_NOFF_REG => u32::from(locked_device.queue_select()), COMMON_Q_DESCLO_REG => locked_device .queue_config() .map(|config| config.desc_table.0 as u32)?, @@ -645,7 +645,7 @@ impl VirtioPciDevice { locked_device.set_driver_features(gfeatures_sel, value); if gfeatures_sel == 1 { - let features = (locked_device.driver_features(1) as u64) << 32; + let features = u64::from(locked_device.driver_features(1)) << 32; if virtio_has_feature(features, VIRTIO_F_RING_PACKED) { locked_device.set_queue_type(QUEUE_TYPE_PACKED_VRING); } else { @@ -663,7 +663,7 @@ impl VirtioPciDevice { } COMMON_STATUS_REG => { if value & CONFIG_STATUS_FEATURES_OK != 0 && value & CONFIG_STATUS_DRIVER_OK == 0 { - let features = (locked_device.driver_features(1) as u64) << 32; + let features = u64::from(locked_device.driver_features(1)) << 32; if !virtio_has_feature(features, VIRTIO_F_VERSION_1) { error!( "Device {} is modern only, but the driver not support VIRTIO_F_VERSION_1", self.base.base.id @@ -924,8 +924,8 @@ impl VirtioPciDevice { warn!("The offset {} of VirtioPciCfgAccessCap is not aligned", off); return; } - if (off as u64) - .checked_add(len as u64) + if u64::from(off) + .checked_add(u64::from(len)) .filter(|&end| end <= self.base.config.bars[bar as usize].size) .is_none() { @@ -935,12 +935,18 @@ impl VirtioPciDevice { let result = if is_write { let mut data = self.base.config.config[pci_cfg_data_offset..].as_ref(); - self.sys_mem - .write(&mut data, GuestAddress(bar_base + off as u64), len as u64) + self.sys_mem.write( + &mut data, + GuestAddress(bar_base + u64::from(off)), + u64::from(len), + ) } else { let mut data = self.base.config.config[pci_cfg_data_offset..].as_mut(); - self.sys_mem - .read(&mut data, GuestAddress(bar_base + off as u64), len as u64) + self.sys_mem.read( + &mut data, + GuestAddress(bar_base + u64::from(off)), + u64::from(len), + ) }; if let Err(e) = result { error!( @@ -955,7 +961,7 @@ impl VirtioPciDevice { // its own request completion. i.e, If the vq is not enough, vcpu A will // receive completion of request that submitted by vcpu B, then A needs // to IPI B. - min(queues_max as u16 - queues_fixed, nr_cpus as u16) + min(queues_max as u16 - queues_fixed, u16::from(nr_cpus)) } fn queues_register_irqfd(&self, call_fds: &[Arc]) -> bool { @@ -1148,11 +1154,11 @@ impl Device for VirtioPciDevice { .with_context(|| "Failed to realize virtio device")?; let name = self.name(); - let devfn = self.base.devfn as u64; + let devfn = u64::from(self.base.devfn); let dev = Arc::new(Mutex::new(self)); - let mut mem_region_size = ((VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) - as u64) - .next_power_of_two(); + let mut mem_region_size = + u64::from(VIRTIO_PCI_CAP_NOTIFY_OFFSET + VIRTIO_PCI_CAP_NOTIFY_LENGTH) + .next_power_of_two(); mem_region_size = max(mem_region_size, MINIMUM_BAR_SIZE_FOR_MMIO as u64); let modern_mem_region = Region::init_container_region(mem_region_size, "VirtioPciModernMem"); @@ -1655,7 +1661,7 @@ mod tests { .iter_mut() { queue_cfg.desc_table = GuestAddress(0); - queue_cfg.avail_ring = GuestAddress((VIRTIO_DEVICE_QUEUE_SIZE as u64) * 16); + queue_cfg.avail_ring = GuestAddress(u64::from(VIRTIO_DEVICE_QUEUE_SIZE) * 16); queue_cfg.used_ring = GuestAddress(2 * 4096); queue_cfg.ready = true; queue_cfg.size = VIRTIO_DEVICE_QUEUE_SIZE; @@ -1704,6 +1710,6 @@ mod tests { .is_ok()); let header_type = le_read_u16(&virtio_pci.base.config.config, HEADER_TYPE as usize).unwrap(); - assert_eq!(header_type, HEADER_TYPE_MULTIFUNC as u16); + assert_eq!(header_type, u16::from(HEADER_TYPE_MULTIFUNC)); } } diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index d55376cf4..52dbb85a4 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -466,7 +466,7 @@ mod tests { let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); - assert_eq!(vhost_net.driver_features(page) as u64, 0_u64); + assert_eq!(u64::from(vhost_net.driver_features(page)), 0_u64); let new_page = vhost_net.device_features(page); assert_eq!(new_page, page); @@ -474,7 +474,7 @@ mod tests { let page: u32 = 0x0; let value: u32 = 0xff; vhost_net.set_driver_features(page, value); - assert_eq!(vhost_net.driver_features(page) as u64, 0xff_u64); + assert_eq!(u64::from(vhost_net.driver_features(page)), 0xff_u64); let new_page = vhost_net.device_features(page); assert_ne!(new_page, page); diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index e6580ffaa..18d3f56bc 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -464,11 +464,11 @@ mod tests { vsock.base.device_features = 0x0123_4567_89ab_cdef; // check for unsupported feature vsock.set_driver_features(0, 0x7000_0000); - assert_eq!(vsock.driver_features(0) as u64, 0_u64); + assert_eq!(u64::from(vsock.driver_features(0)), 0_u64); assert_eq!(vsock.base.device_features, 0x0123_4567_89ab_cdef); // check for supported feature vsock.set_driver_features(0, 0x8000_0000); - assert_eq!(vsock.driver_features(0) as u64, 0x8000_0000_u64); + assert_eq!(u64::from(vsock.driver_features(0)), 0x8000_0000_u64); assert_eq!(vsock.base.device_features, 0x0123_4567_89ab_cdef); // test vsock read_config @@ -510,7 +510,7 @@ mod tests { let backend = vsock.backend.unwrap(); assert_eq!(backend.set_guest_cid(3).is_ok(), true); assert_eq!( - backend.set_guest_cid(u32::max_value() as u64).is_ok(), + backend.set_guest_cid(u64::from(u32::max_value())).is_ok(), false ); assert_eq!(backend.set_guest_cid(2).is_ok(), false); diff --git a/virtio/src/vhost/user/block.rs b/virtio/src/vhost/user/block.rs index 8ccce18e4..4e0757f9f 100644 --- a/virtio/src/vhost/user/block.rs +++ b/virtio/src/vhost/user/block.rs @@ -146,7 +146,7 @@ impl VirtioDevice for Block { .set_protocol_features(self.protocol_features) .with_context(|| "Failed to set protocol features for vhost-user blk")?; - if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_CONFIG as u32) { + if virtio_has_feature(protocol_features, u32::from(VHOST_USER_PROTOCOL_F_CONFIG)) { let config = locked_client .get_virtio_blk_config() .with_context(|| "Failed to get config for vhost-user blk")?; @@ -158,7 +158,7 @@ impl VirtioDevice for Block { ); } - if virtio_has_feature(protocol_features, VHOST_USER_PROTOCOL_F_MQ as u32) { + if virtio_has_feature(protocol_features, u32::from(VHOST_USER_PROTOCOL_F_MQ)) { let max_queue_num = locked_client .get_max_queue_num() .with_context(|| "Failed to get queue num for vhost-user blk")?; diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index b32bf72cc..469871f13 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -482,7 +482,7 @@ impl VhostUserClient { .with_context(|| "Failed to get protocol features for vhost-user blk")?; if virtio_has_feature( protocol_feature, - VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD as u32, + u32::from(VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD), ) { if self.inflight.is_none() { // Expect 1 fd. @@ -903,7 +903,7 @@ impl VhostOps for VhostUserClient { size_of::() as u32, ); let payload_opt: Option<&[u8]> = None; - let vring_state = VhostUserVringState::new(queue_idx as u32, num as u32); + let vring_state = VhostUserVringState::new(queue_idx as u32, u32::from(num)); client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) @@ -982,7 +982,7 @@ impl VhostOps for VhostUserClient { size_of::() as u32, ); let payload_opt: Option<&[u8]> = None; - let vring_state = VhostUserVringState::new(queue_idx as u32, last_avail_idx as u32); + let vring_state = VhostUserVringState::new(queue_idx as u32, u32::from(last_avail_idx)); client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) @@ -1058,7 +1058,7 @@ impl VhostOps for VhostUserClient { size_of::() as u32, ); let payload_opt: Option<&[u8]> = None; - let vring_state = VhostUserVringState::new(queue_idx as u32, status as u32); + let vring_state = VhostUserVringState::new(queue_idx as u32, u32::from(status)); client .sock .send_msg(Some(&hdr), Some(&vring_state), payload_opt, &[]) -- Gitee From 6e91e4bb77e29833dd75b4f6270055c870703aa4 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 16 Aug 2024 13:35:22 +0800 Subject: [PATCH 2015/2187] xhci: Fix `or-fun-call` clippy warnings error: use of `ok_or` followed by a function call --> devices/src/usb/xhci/xhci_ring.rs:104:78 | 104 | self.dequeue = self.dequeue.checked_add(u64::from(TRB_SIZE)).ok_or( | ______________________________________________________________________________^ 105 | | UsbError::MemoryAccessOverflow(self.dequeue, u64::from(TRB_SIZE)), 106 | | )?; | |_________________^ help: try this: `ok_or_else(|| UsbError::MemoryAccessOverflow(self.dequeue, u64::from(TRB_SIZE)))` | = note: `-D clippy::or-fun-call` implied by `-D warnings` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call error: use of `ok_or` followed by a function call --> devices/src/usb/xhci/xhci_ring.rs:185:22 | 185 | .ok_or(UsbError::MemoryAccessOverflow(dequeue, u64::from(TRB_SIZE)))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `ok_or_else(|| UsbError::MemoryAccessOverflow(dequeue, u64::from(TRB_SIZE)))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call error: could not compile `devices` due to 2 previous errors --- devices/src/usb/xhci/xhci_ring.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 86deba806..061fd48f3 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -101,9 +101,12 @@ impl XhciCommandRing { self.ccs = !self.ccs; } } else { - self.dequeue = self.dequeue.checked_add(u64::from(TRB_SIZE)).ok_or( - UsbError::MemoryAccessOverflow(self.dequeue, u64::from(TRB_SIZE)), - )?; + self.dequeue = self + .dequeue + .checked_add(u64::from(TRB_SIZE)) + .ok_or_else(|| { + UsbError::MemoryAccessOverflow(self.dequeue, u64::from(TRB_SIZE)) + })?; return Ok(Some(trb)); } } @@ -182,7 +185,7 @@ impl XhciTransferRing { td.push(trb); dequeue = dequeue .checked_add(u64::from(TRB_SIZE)) - .ok_or(UsbError::MemoryAccessOverflow(dequeue, u64::from(TRB_SIZE)))?; + .ok_or_else(|| UsbError::MemoryAccessOverflow(dequeue, u64::from(TRB_SIZE)))?; if trb_type == TRBType::TrSetup { ctrl_td = true; } else if trb_type == TRBType::TrStatus { -- Gitee From b0f24555430c6705b6c89df3a303a8111e063ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Sat, 17 Aug 2024 14:27:27 +0800 Subject: [PATCH 2016/2187] Vfio: Add vfio feature control Add vfio feature to control vfio function. Signed-off-by:Yihua Jin --- Cargo.toml | 2 ++ hypervisor/Cargo.toml | 4 ++++ hypervisor/src/kvm/mod.rs | 5 ++++- hypervisor/src/lib.rs | 2 ++ hypervisor/src/test/mod.rs | 2 ++ machine/Cargo.toml | 3 ++- machine/src/lib.rs | 7 +++++-- machine/src/standard_common/mod.rs | 1 + machine/src/standard_common/syscall.rs | 24 ++++++++++++++---------- 9 files changed, 36 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3277768c7..1af042fe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ machine_manager = { path = "machine_manager" } util = { path = "util" } trace = { path = "trace" } hisysevent = { path = "hisysevent" } +hypervisor = { path = "hypervisor" } [workspace] members = [ @@ -44,6 +45,7 @@ trace_to_logger = ["trace/trace_to_logger"] trace_to_ftrace = ["trace/trace_to_ftrace"] trace_to_hitrace = ["trace/trace_to_hitrace"] hisysevent = ["hisysevent/hisysevent"] +vfio = ["machine/vfio_device", "hypervisor/vfio_device"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index ccdf1305e..95c92558e 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -21,3 +21,7 @@ migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } trace = { path = "../trace" } + +[features] +default = [] +vfio_device = [] diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index d8a8da8f6..8c295e00c 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -36,9 +36,11 @@ use anyhow::anyhow; use anyhow::{bail, Context, Result}; use kvm_bindings::kvm_userspace_memory_region as KvmMemSlot; use kvm_bindings::*; +#[cfg(feature = "vfio_device")] +use kvm_ioctls::DeviceFd; #[cfg(not(test))] use kvm_ioctls::VcpuExit; -use kvm_ioctls::{Cap, DeviceFd, Kvm, VcpuFd, VmFd}; +use kvm_ioctls::{Cap, Kvm, VcpuFd, VmFd}; use libc::{c_int, c_void, siginfo_t}; use log::{error, info, warn}; use vmm_sys_util::{ @@ -259,6 +261,7 @@ impl HypervisorOps for KvmHypervisor { }) } + #[cfg(feature = "vfio_device")] fn create_vfio_device(&self) -> Option { let mut device = kvm_create_device { type_: kvm_device_type_KVM_DEV_TYPE_VFIO, diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 25fc90ef6..a156d9af6 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -22,6 +22,7 @@ use std::any::Any; use std::sync::Arc; use anyhow::Result; +#[cfg(feature = "vfio_device")] use kvm_ioctls::DeviceFd; use address_space::AddressSpace; @@ -56,5 +57,6 @@ pub trait HypervisorOps: Send + Sync + Any { fn create_irq_manager(&mut self) -> Result; + #[cfg(feature = "vfio_device")] fn create_vfio_device(&self) -> Option; } diff --git a/hypervisor/src/test/mod.rs b/hypervisor/src/test/mod.rs index 7120bb9e6..41504d827 100644 --- a/hypervisor/src/test/mod.rs +++ b/hypervisor/src/test/mod.rs @@ -21,6 +21,7 @@ use std::thread; use std::time::Duration; use anyhow::{anyhow, Context, Result}; +#[cfg(feature = "vfio_device")] use kvm_ioctls::DeviceFd; use log::info; use vmm_sys_util::eventfd::EventFd; @@ -115,6 +116,7 @@ impl HypervisorOps for TestHypervisor { }) } + #[cfg(feature = "vfio_device")] fn create_vfio_device(&self) -> Option { None } diff --git a/machine/Cargo.toml b/machine/Cargo.toml index e5d9253ab..ce455c719 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -24,7 +24,7 @@ migration = { path = "../migration" } migration_derive = { path = "../migration/migration_derive" } util = { path = "../util" } virtio = { path = "../virtio" } -vfio = { path = "../vfio" } +vfio = { path = "../vfio" , optional = true } block_backend = { path = "../block_backend" } ui = { path = "../ui" } trace = { path = "../trace" } @@ -50,3 +50,4 @@ vnc_auth = ["vnc"] ohui_srv = ["windows_emu_pid", "ui/ohui_srv", "machine_manager/ohui_srv", "virtio/ohui_srv"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] +vfio_device = ["vfio"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index fbb6f14eb..6b2fe0fae 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -99,6 +99,7 @@ use util::loop_context::{ gen_delete_notifiers, EventNotifier, NotifierCallback, NotifierOperation, }; use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; +#[cfg(feature = "vfio_device")] use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; @@ -1384,7 +1385,7 @@ pub trait MachineOps: MachineLifecycle { .with_context(|| "Failed to add vhost user block device")?; Ok(()) } - + #[cfg(feature = "vfio_device")] fn add_vfio_device(&mut self, cfg_args: &str, hotplug: bool) -> Result<()> { let hypervisor = self.get_hypervisor(); let locked_hypervisor = hypervisor.lock().unwrap(); @@ -1898,12 +1899,13 @@ pub trait MachineOps: MachineLifecycle { ("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args), ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args), ("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args), - ("vfio-pci", add_vfio_device, cfg_args, false), ("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args), ("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false), ("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args), ("nec-usb-xhci", add_usb_xhci, cfg_args), ("usb-kbd" | "usb-storage" | "usb-uas" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); + #[cfg(feature = "vfio_device")] + ("vfio-pci", add_vfio_device, cfg_args, false), #[cfg(feature = "virtio_gpu")] ("virtio-gpu-pci", add_virtio_pci_gpu, cfg_args), #[cfg(feature = "ramfb")] @@ -2461,6 +2463,7 @@ pub fn type_init() -> Result<()> { // Register all pci devices type. machine_register_pcidevops_type()?; + #[cfg(feature = "vfio_device")] vfio_register_pcidevops_type()?; virtio_register_pcidevops_type()?; devices_register_pcidevops_type()?; diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 4af265d9a..9fd03245f 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1252,6 +1252,7 @@ impl DeviceInterface for StdMachine { ); } } + #[cfg(feature = "vfio_device")] "vfio-pci" => { let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); if let Err(e) = self.add_vfio_device(&cfg_args, true) { diff --git a/machine/src/standard_common/syscall.rs b/machine/src/standard_common/syscall.rs index d665f314e..30e1998ea 100644 --- a/machine/src/standard_common/syscall.rs +++ b/machine/src/standard_common/syscall.rs @@ -23,6 +23,7 @@ use util::v4l2::{ VIDIOC_G_FMT, VIDIOC_QBUF, VIDIOC_QUERYBUF, VIDIOC_QUERYCAP, VIDIOC_REQBUFS, VIDIOC_STREAMOFF, VIDIOC_STREAMON, VIDIOC_S_FMT, VIDIOC_S_PARM, }; +#[cfg(feature = "vfio_device")] use vfio::{ VFIO_CHECK_EXTENSION, VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_RESET, VFIO_DEVICE_SET_IRQS, VFIO_GET_API_VERSION, VFIO_GROUP_GET_DEVICE_FD, VFIO_GROUP_GET_STATUS, @@ -226,6 +227,18 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETOFFLOAD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETVNETHDRSZ() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, TUNSETQUEUE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_CREATE_DEVICE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) + .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32); + + #[cfg(feature = "vfio_device")] + let bpf_rule = bpf_rule .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_SET_IRQS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_GET_STATUS() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GET_API_VERSION() as u32) @@ -237,16 +250,7 @@ fn ioctl_allow_list() -> BpfRule { .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_GROUP_GET_DEVICE_FD() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_INFO() as u32) .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_RESET() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_REGION_INFO() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_GSI_ROUTING() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_IRQFD() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_CREATE_DEVICE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_API_VERSION() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_MP_STATE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_VCPU_EVENTS() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_GET_DIRTY_LOG() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_MP_STATE() as u32) - .add_constraint(SeccompCmpOpt::Eq, 1, KVM_SET_VCPU_EVENTS() as u32); + .add_constraint(SeccompCmpOpt::Eq, 1, VFIO_DEVICE_GET_REGION_INFO() as u32); #[cfg(feature = "usb_camera_v4l2")] let bpf_rule = bpf_rule -- Gitee From 9b85ef728c6a75f90fca467eb3563411908e2be5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Aug 2024 11:44:44 +0800 Subject: [PATCH 2017/2187] clippy: Fix clippy warnings from high version clippy Signed-off-by: Keqian Zhu --- acpi/src/aml_compiler.rs | 6 +- acpi/src/table_loader.rs | 4 +- address_space/src/address_space.rs | 16 +- address_space/src/region.rs | 2 +- block_backend/src/lib.rs | 2 +- block_backend/src/qcow2/cache.rs | 10 +- block_backend/src/qcow2/check.rs | 4 +- block_backend/src/qcow2/header.rs | 28 +- block_backend/src/qcow2/mod.rs | 9 +- block_backend/src/qcow2/refcount.rs | 10 +- cpu/src/lib.rs | 2 +- devices/src/acpi/power.rs | 2 +- devices/src/pci/mod.rs | 6 +- devices/src/usb/mod.rs | 2 +- hypervisor/src/kvm/interrupt.rs | 6 +- hypervisor/src/kvm/listener.rs | 14 +- hypervisor/src/kvm/mod.rs | 4 +- image/src/cmdline.rs | 14 +- image/src/img.rs | 129 ++++----- machine_manager/src/config/boot_source.rs | 6 +- machine_manager/src/config/camera.rs | 4 +- machine_manager/src/config/chardev.rs | 2 +- machine_manager/src/config/display.rs | 8 +- machine_manager/src/config/drive.rs | 37 +-- machine_manager/src/config/machine_config.rs | 26 +- machine_manager/src/config/mod.rs | 5 +- machine_manager/src/config/network.rs | 28 +- machine_manager/src/config/numa.rs | 4 +- machine_manager/src/config/sasl_auth.rs | 4 +- machine_manager/src/config/tls_creds.rs | 8 +- machine_manager/src/config/vnc.rs | 4 +- machine_manager/src/qmp/qmp_response.rs | 2 +- machine_manager/src/qmp/qmp_schema.rs | 2 +- machine_manager/src/qmp/qmp_socket.rs | 25 +- machine_manager/src/socket.rs | 2 +- migration/src/protocol.rs | 49 ++-- src/main.rs | 8 +- tests/mod_test/src/libdriver/pci.rs | 13 +- tests/mod_test/src/libdriver/qcow2.rs | 25 +- tests/mod_test/src/libdriver/usb.rs | 98 +++---- tests/mod_test/src/libdriver/virtio_gpu.rs | 22 +- .../src/libdriver/virtio_pci_modern.rs | 4 +- tests/mod_test/src/libdriver/vnc.rs | 51 +--- tests/mod_test/src/libtest.rs | 4 +- tests/mod_test/src/utils.rs | 12 +- tests/mod_test/tests/balloon_test.rs | 39 +-- tests/mod_test/tests/block_test.rs | 23 +- tests/mod_test/tests/fwcfg_test.rs | 44 +-- tests/mod_test/tests/memory_test.rs | 8 +- tests/mod_test/tests/net_test.rs | 92 +++---- tests/mod_test/tests/pci_test.rs | 98 ++++--- tests/mod_test/tests/pvpanic_test.rs | 2 +- tests/mod_test/tests/scsi_test.rs | 16 +- tests/mod_test/tests/serial_test.rs | 12 +- tests/mod_test/tests/usb_camera_test.rs | 2 +- tests/mod_test/tests/usb_storage_test.rs | 4 +- tests/mod_test/tests/usb_test.rs | 43 ++- tests/mod_test/tests/virtio_gpu_test.rs | 18 +- tests/mod_test/tests/virtio_test.rs | 61 ++--- tests/mod_test/tests/virtiofs_test.rs | 258 +++++++++--------- tests/mod_test/tests/vnc_test.rs | 36 ++- .../mod_test/tests/x86_64/cpu_hotplug_test.rs | 7 +- ui/src/console.rs | 8 +- ui/src/input.rs | 6 +- ui/src/utils.rs | 10 +- ui/src/vnc/encoding/enc_hextile.rs | 12 +- util/src/aio/mod.rs | 19 +- util/src/arg_parser.rs | 10 +- util/src/bitmap.rs | 56 ++-- util/src/daemonize.rs | 1 + util/src/logger.rs | 1 - util/src/loop_context.rs | 14 +- util/src/num_ops.rs | 4 +- util/src/unix.rs | 6 +- util/src/v4l2.rs | 2 +- virtio/src/device/balloon.rs | 10 +- virtio/src/device/block.rs | 19 +- virtio/src/device/gpu.rs | 8 +- virtio/src/device/net.rs | 40 +-- virtio/src/device/rng.rs | 12 +- virtio/src/device/scsi_cntlr.rs | 2 +- virtio/src/device/serial.rs | 4 +- virtio/src/queue/split.rs | 112 ++++---- virtio/src/transport/virtio_mmio.rs | 228 ++++------------ virtio/src/transport/virtio_pci.rs | 10 +- virtio/src/vhost/kernel/net.rs | 12 +- virtio/src/vhost/kernel/vsock.rs | 25 +- 87 files changed, 929 insertions(+), 1188 deletions(-) diff --git a/acpi/src/aml_compiler.rs b/acpi/src/aml_compiler.rs index b7aa0a9a6..f97216ae2 100644 --- a/acpi/src/aml_compiler.rs +++ b/acpi/src/aml_compiler.rs @@ -1819,7 +1819,7 @@ mod test { let elem4 = AmlFieldUnit::new(Some("FLD3"), 4); let elem5 = AmlFieldUnit::new(Some("FLD4"), 12); - for e in vec![elem1, elem2, elem3, elem4, elem5] { + for e in [elem1, elem2, elem3, elem4, elem5] { field.append_child(e); } @@ -1991,7 +1991,7 @@ mod test { if_scope1.append_child(AmlReturn::new()); method1.append_child(if_scope1); - let store1 = AmlStore::new(AmlArg(0), AmlLocal(0).clone()); + let store1 = AmlStore::new(AmlArg(0), AmlLocal(0)); method1.append_child(store1); let mut while_scope = AmlWhile::new(AmlLLess::new(AmlLocal(0), AmlArg(1))); @@ -2031,7 +2031,7 @@ mod test { method2.append_child(store2); let mut pkg1 = AmlPackage::new(3); - vec![0x01, 0x03F8, 0x03FF].iter().for_each(|&x| { + [0x01, 0x03F8, 0x03FF].iter().for_each(|&x| { pkg1.append_child(AmlInteger(x as u64)); }); let named_pkg1 = AmlNameDecl::new("PKG1", pkg1); diff --git a/acpi/src/table_loader.rs b/acpi/src/table_loader.rs index 74a9d173e..e968e4d07 100644 --- a/acpi/src/table_loader.rs +++ b/acpi/src/table_loader.rs @@ -382,7 +382,7 @@ mod test { let file_bytes = file_name.as_bytes(); // SATETY: The "alloc" field of union consists of u8 members, so the access is safe. - let alloc = unsafe { &table_loader.cmds.get(0).unwrap().entry.alloc }; + let alloc = unsafe { &table_loader.cmds.first().unwrap().entry.alloc }; assert_eq!( alloc.file[0..file_bytes.len()].to_vec(), file_bytes.to_vec() @@ -450,7 +450,7 @@ mod test { .add_cksum_entry(&file, 0_u32, 0_u32, file_len + 1) .is_err()); assert!(table_loader - .add_cksum_entry(&file, (file_len - 1) as u32, 80, 20) + .add_cksum_entry(&file, file_len - 1, 80, 20) .is_ok()); assert!(table_loader .add_cksum_entry(&file, file_len - 1, 0, 50) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index d19e6c3de..84f1be4e0 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -984,7 +984,7 @@ mod test { space.unregister_listener(listener2).unwrap(); assert_eq!(space.listeners.lock().unwrap().len(), 1); for listener in space.listeners.lock().unwrap().iter() { - assert_eq!(listener.lock().unwrap().enabled(), true); + assert!(listener.lock().unwrap().enabled()); } } @@ -1024,7 +1024,7 @@ mod test { .reqs .lock() .unwrap() - .get(0) + .first() .unwrap() .1, AddressRange::new(region_c.offset(), region_c.size()) @@ -1047,7 +1047,7 @@ mod test { assert_eq!(locked_listener.reqs.lock().unwrap().len(), 4); // delete flat-range 0~6000 first, belonging to region_c assert_eq!( - locked_listener.reqs.lock().unwrap().get(0).unwrap().1, + locked_listener.reqs.lock().unwrap().first().unwrap().1, AddressRange::new(region_c.offset(), region_c.size()) ); // add range 0~2000, belonging to region_c @@ -1197,8 +1197,8 @@ mod test { ram2.start_address().unchecked_add(ram2.size()) ); assert!(space.address_in_memory(GuestAddress(0), 0)); - assert_eq!(space.address_in_memory(GuestAddress(1000), 0), false); - assert_eq!(space.address_in_memory(GuestAddress(1500), 0), false); + assert!(!space.address_in_memory(GuestAddress(1000), 0)); + assert!(!space.address_in_memory(GuestAddress(1500), 0)); assert!(space.address_in_memory(GuestAddress(2900), 0)); assert_eq!( @@ -1227,9 +1227,9 @@ mod test { ram2.start_address().unchecked_add(ram2.size()) ); assert!(space.address_in_memory(GuestAddress(0), 0)); - assert_eq!(space.address_in_memory(GuestAddress(1000), 0), false); - assert_eq!(space.address_in_memory(GuestAddress(1500), 0), false); - assert_eq!(space.address_in_memory(GuestAddress(2400), 0), false); + assert!(!space.address_in_memory(GuestAddress(1000), 0)); + assert!(!space.address_in_memory(GuestAddress(1500), 0)); + assert!(!space.address_in_memory(GuestAddress(2400), 0)); assert!(space.address_in_memory(GuestAddress(2900), 0)); assert_eq!( diff --git a/address_space/src/region.rs b/address_space/src/region.rs index f5e7c77ab..64ad7fbae 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1323,7 +1323,7 @@ mod test { .subregions .read() .unwrap() - .get(0) + .first() .unwrap() .priority(), 10 diff --git a/block_backend/src/lib.rs b/block_backend/src/lib.rs index c571f292e..525c1c662 100644 --- a/block_backend/src/lib.rs +++ b/block_backend/src/lib.rs @@ -154,7 +154,7 @@ impl CreateOptions { // Transform size into string with storage units. fn size_to_string(size: f64) -> Result { - let units = vec!["", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; + let units = ["", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; // Switch to higher power if the integer part is >= 1000, // For example: 1000 * 2^30 bytes diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index c46bc5d93..f3ca26b88 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -262,7 +262,7 @@ mod test { for i in 0..buf.len() { vec.append(&mut buf[i].to_be_bytes().to_vec()); } - let mut entry = CacheTable::new(0x00 as u64, vec, ENTRY_SIZE_U64).unwrap(); + let mut entry = CacheTable::new(0x00_u64, vec, ENTRY_SIZE_U64).unwrap(); assert_eq!(entry.get_entry_map(0).unwrap(), 0x00); assert_eq!(entry.get_entry_map(3).unwrap(), 0x03); assert_eq!(entry.get_entry_map(4).unwrap(), 0x04); @@ -279,19 +279,19 @@ mod test { vec.append(&mut buf[i].to_be_bytes().to_vec()); } let entry_0 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), + CacheTable::new(0x00_u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_0.borrow_mut().lru_count = 0; let entry_1 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), + CacheTable::new(0x00_u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_1.borrow_mut().lru_count = 1; let entry_2 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), + CacheTable::new(0x00_u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_2.borrow_mut().lru_count = 2; let entry_3 = Rc::new(RefCell::new( - CacheTable::new(0x00 as u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), + CacheTable::new(0x00_u64, vec.clone(), ENTRY_SIZE_U64).unwrap(), )); entry_3.borrow_mut().lru_count = 3; diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index c148178ce..35e4f350b 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -1124,8 +1124,8 @@ mod test { assert!(refblock.set_refcount(9, 9).is_ok()); // Get inner dat - let mut vec_1 = (1 as u16).to_be_bytes().to_vec(); - let mut vec_2 = (7 as u16).to_be_bytes().to_vec(); + let mut vec_1 = 1_u16.to_be_bytes().to_vec(); + let mut vec_2 = 7_u16.to_be_bytes().to_vec(); vec_1.append(&mut vec_2); let buf = refblock.get_data(0, 2); assert_eq!(buf, vec_1); diff --git a/block_backend/src/qcow2/header.rs b/block_backend/src/qcow2/header.rs index 99404b9f9..fa3681d9c 100644 --- a/block_backend/src/qcow2/header.rs +++ b/block_backend/src/qcow2/header.rs @@ -320,21 +320,21 @@ mod test { fn invalid_header_list() -> Vec<(Vec, String)> { let mut list = Vec::new(); // Invalid buffer length. - list.push((vec![0_u8; 16], format!("Invalid header len"))); + list.push((vec![0_u8; 16], "Invalid header len".to_string())); // Invalid buffer length for v3. let buf = valid_header_v3(); list.push(( buf[0..90].to_vec(), - format!("Invalid header len for version 3"), + "Invalid header len for version 3".to_string(), )); // Invalid magic. let mut buf = valid_header_v2(); BigEndian::write_u32(&mut buf[0..4], 1234); - list.push((buf, format!("Invalid format"))); + list.push((buf, "Invalid format".to_string())); // Invalid version. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[4..8], 1); - list.push((buf, format!("Invalid version"))); + list.push((buf, "Invalid version".to_string())); // Large header length. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[100..104], 0x10000000_u32); @@ -345,23 +345,23 @@ mod test { // Small cluster bit. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[20..24], 0); - list.push((buf, format!("Invalid cluster bit"))); + list.push((buf, "Invalid cluster bit".to_string())); // Large cluster bit. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[20..24], 65); - list.push((buf, format!("Invalid cluster bit"))); + list.push((buf, "Invalid cluster bit".to_string())); // Invalid backing file offset. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[8..16], 0x2000); - list.push((buf, format!("Don't support backing file offset"))); + list.push((buf, "Don't support backing file offset".to_string())); // Invalid refcount order. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[96..100], 5); - list.push((buf, format!("Invalid refcount order"))); + list.push((buf, "Invalid refcount order".to_string())); // Refcount table offset is not aligned. let mut buf = valid_header_v3(); BigEndian::write_u64(&mut buf[48..56], 0x1234); - list.push((buf, format!("Refcount table offset not aligned"))); + list.push((buf, "Refcount table offset not aligned".to_string())); // Refcount table offset is large. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[36..40], 4 * 1024 * 1024); @@ -377,15 +377,15 @@ mod test { // Invalid refcount table cluster. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[56..60], 256); - list.push((buf, format!("Refcount table size over limit"))); + list.push((buf, "Refcount table size over limit".to_string())); // Refcount table cluster is 0. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[56..60], 0); - list.push((buf, format!("Refcount table clusters is zero"))); + list.push((buf, "Refcount table clusters is zero".to_string())); // L1 table offset is not aligned. let mut buf = valid_header_v3(); BigEndian::write_u64(&mut buf[40..48], 0x123456); - list.push((buf, format!("L1 table offset not aligned"))); + list.push((buf, "L1 table offset not aligned".to_string())); // L1 table offset is large. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[36..40], 4 * 1024 * 1024); @@ -401,12 +401,12 @@ mod test { // Invalid l1 table size. let mut buf = valid_header_v3(); BigEndian::write_u32(&mut buf[36..40], 0xffff_0000_u32); - list.push((buf, format!("L1 table size over limit"))); + list.push((buf, "L1 table size over limit".to_string())); // File size is large than l1 table size. let mut buf = valid_header_v3(); BigEndian::write_u64(&mut buf[24..32], 0xffff_ffff_ffff_0000_u64); BigEndian::write_u32(&mut buf[36..40], 10); - list.push((buf, format!("L1 table is too small"))); + list.push((buf, "L1 table is too small".to_string())); list } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 3edc388e3..7c14cb981 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1994,10 +1994,10 @@ mod test { file.write_all(&header.to_vec()).unwrap(); // Cluster 1 is the refcount table. - assert_eq!(header.refcount_table_offset, cluster_sz * 1); + assert_eq!(header.refcount_table_offset, cluster_sz); let mut refcount_table = [0_u8; ENTRY_SIZE as usize]; BigEndian::write_u64(&mut refcount_table, cluster_sz * 2); - file.seek(SeekFrom::Start(cluster_sz * 1)).unwrap(); + file.seek(SeekFrom::Start(cluster_sz)).unwrap(); file.write_all(&refcount_table).unwrap(); // Clusters which has been allocated. @@ -2357,7 +2357,7 @@ mod test { riovec, wiovec, data: test_data, - offset: 1 * CLUSTER_SIZE as usize, + offset: CLUSTER_SIZE as usize, sz: CLUSTER_SIZE, }); let test_data = vec![TestData::new(5, CLUSTER_SIZE as usize)]; @@ -2743,9 +2743,8 @@ mod test { .borrow_mut() .get_entry_map(l2_index as usize) .unwrap(); - let host_offset = l2_entry & L2_TABLE_OFFSET_MASK; - host_offset + l2_entry & L2_TABLE_OFFSET_MASK } // Change snapshot table offset to unaligned address which will lead to error in refcount update diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 67c4c1e25..7c60ce316 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -890,7 +890,7 @@ mod test { for j in (i + 1)..len { let addr2 = res_data[j].0 as usize; let size2 = res_data[j].1 as usize; - assert_eq!(ranges_overlap(addr1, size1, addr2, size2).unwrap(), false); + assert!(!ranges_overlap(addr1, size1, addr2, size2).unwrap()); } } @@ -936,7 +936,7 @@ mod test { let mut new_rc_table = vec![0_u8; new_rct_size]; cloned_file .as_ref() - .read_at(&mut new_rc_table, new_rct_offset as u64) + .read_at(&mut new_rc_table, new_rct_offset) .unwrap(); for i in 0..old_rct_size { assert_eq!(old_rc_table[i], new_rc_table[i]); @@ -992,7 +992,7 @@ mod test { &Qcow2DiscardType::Never, ); if let Err(err) = ret { - let err_msg = format!("Invalid refcount block address 0x0, index is 2"); + let err_msg = "Invalid refcount block address 0x0, index is 2".to_string(); assert_eq!(err.to_string(), err_msg); } else { assert!(false); @@ -1016,7 +1016,7 @@ mod test { // Test refcount overflow. let ret = refcount.set_refcount(0, 0, 1, 65535, &Qcow2DiscardType::Never); if let Err(err) = ret { - let err_msg = format!("Refcount 2 add 65535 cause overflows, index is 0"); + let err_msg = "Refcount 2 add 65535 cause overflows, index is 0".to_string(); assert_eq!(err.to_string(), err_msg); } else { assert!(false); @@ -1025,7 +1025,7 @@ mod test { // Test refcount underflow. let ret = refcount.set_refcount(0, 0, 1, -65535, &Qcow2DiscardType::Never); if let Err(err) = ret { - let err_msg = format!("Refcount 2 sub 65535 cause overflows, index is 0"); + let err_msg = "Refcount 2 sub 65535 cause overflows, index is 0".to_string(); assert_eq!(err.to_string(), err_msg); } else { assert!(false); diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 2690c1c2c..b141334ea 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -462,7 +462,7 @@ pub struct CPUThreadWorker { } impl CPUThreadWorker { - thread_local!(static LOCAL_THREAD_VCPU: RefCell> = RefCell::new(None)); + thread_local!(static LOCAL_THREAD_VCPU: RefCell> = const { RefCell::new(None) }); /// Allocates a new `CPUThreadWorker`. fn new(thread_cpu: Arc) -> Self { diff --git a/devices/src/acpi/power.rs b/devices/src/acpi/power.rs index 8f066a808..ae3d0bf61 100644 --- a/devices/src/acpi/power.rs +++ b/devices/src/acpi/power.rs @@ -385,7 +385,7 @@ impl AmlBuilder for PowerDev { acpi_acad_dev .aml_bytes() .into_iter() - .chain(acpi_bat_dev.aml_bytes().into_iter()) + .chain(acpi_bat_dev.aml_bytes()) .collect() } } diff --git a/devices/src/pci/mod.rs b/devices/src/pci/mod.rs index 20ba952ce..e2b64ff85 100644 --- a/devices/src/pci/mod.rs +++ b/devices/src/pci/mod.rs @@ -208,7 +208,8 @@ pub trait PciDevOps: Device + Send { /// Get the path of the PCI bus where the device resides. fn get_parent_dev_path(&self, parent_bus: Arc>) -> String { PCI_BUS!(parent_bus, locked_bus, pci_bus); - let parent_dev_path = if pci_bus.name().eq("pcie.0") { + + if pci_bus.name().eq("pcie.0") { String::from("/pci@ffffffffffffffff") } else { // This else branch will not be executed currently, @@ -217,8 +218,7 @@ pub trait PciDevOps: Device + Send { let parent_bridge = pci_bus.parent_device().unwrap().upgrade().unwrap(); ROOT_PORT!(parent_bridge, locked_bridge, rootport); rootport.get_dev_path().unwrap() - }; - parent_dev_path + } } /// Fill the device path according to parent device path and device function. diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f5c149965..5ddaef9e0 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -237,7 +237,7 @@ impl UsbDeviceBase { .as_ref() .with_context(|| "Device descriptor not found")?; desc.configs - .get(0) + .first() .with_context(|| "Config descriptor not found")? .clone() }; diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index ea9e7790c..6a9f8f8f5 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -236,7 +236,7 @@ mod tests { #[test] fn test_get_maximum_gsi_cnt() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -245,11 +245,11 @@ mod tests { #[test] fn test_alloc_and_release_gsi() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } - let irq_route_table = Mutex::new(IrqRouteTable::new(&kvm_hyp.fd.as_ref().unwrap())); + let irq_route_table = Mutex::new(IrqRouteTable::new(kvm_hyp.fd.as_ref().unwrap())); let irq_manager = Arc::new(KVMInterruptManager::new( true, kvm_hyp.vm_fd.clone().unwrap(), diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index 713b72384..aab93039b 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -628,7 +628,7 @@ mod test { #[test] fn test_alloc_slot() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -652,7 +652,7 @@ mod test { #[test] fn test_add_del_ram_region() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -678,7 +678,7 @@ mod test { #[test] fn test_add_region_align() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -700,7 +700,7 @@ mod test { #[test] fn test_add_del_ioeventfd() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -746,7 +746,7 @@ mod test { #[test] fn test_ioeventfd_with_data_match() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -767,7 +767,7 @@ mod test { // Delete ioeventfd with wrong address will cause an error. let mut evtfd_to_del = evtfd.clone(); - evtfd_to_del.addr_range.base.0 = evtfd_to_del.addr_range.base.0 - 2; + evtfd_to_del.addr_range.base.0 -= 2; assert!(kml .handle_request(None, Some(&evtfd_to_del), ListenerReqType::DeleteIoeventfd) .is_err()); @@ -800,7 +800,7 @@ mod test { #[test] #[cfg(target_arch = "x86_64")] fn test_kvm_io_listener() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index e7e75d7f6..d1e921113 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -1002,7 +1002,7 @@ mod test { #[cfg(target_arch = "x86_64")] #[test] fn test_x86_64_kvm_cpu() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } @@ -1110,7 +1110,7 @@ mod test { #[test] #[allow(unused)] fn test_cpu_lifecycle_with_kvm() { - let kvm_hyp = KvmHypervisor::new().unwrap_or(KvmHypervisor::default()); + let kvm_hyp = KvmHypervisor::new().unwrap_or_default(); if kvm_hyp.vm_fd.is_none() { return; } diff --git a/image/src/cmdline.rs b/image/src/cmdline.rs index 8235ff5d3..c991366c6 100644 --- a/image/src/cmdline.rs +++ b/image/src/cmdline.rs @@ -162,20 +162,16 @@ mod test { fn test_arg_parse() { let mut arg_parser = ArgsParse::create(vec!["q", "h", "help"], vec!["f"], vec!["o"]); let cmd_line = "-f qcow2 -q -h --help -o cluster_size=512 -o refcount_bits=16 img_path +1G"; - let cmd_args: Vec = cmd_line - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let cmd_args: Vec = cmd_line.split(' ').map(|str| str.to_string()).collect(); let ret = arg_parser.parse(cmd_args); println!("{:?}", ret); assert!(ret.is_ok()); - assert_eq!(arg_parser.opt_present("f"), true); - assert_eq!(arg_parser.opt_present("q"), true); - assert_eq!(arg_parser.opt_present("h"), true); - assert_eq!(arg_parser.opt_present("help"), true); + assert!(arg_parser.opt_present("f")); + assert!(arg_parser.opt_present("q")); + assert!(arg_parser.opt_present("h")); + assert!(arg_parser.opt_present("help")); let values = arg_parser.opt_strs("o"); assert!(values.contains(&"cluster_size=512".to_string())); diff --git a/image/src/img.rs b/image/src/img.rs index fcfebffba..3e359a3fc 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -86,7 +86,7 @@ impl ImageFile { detect_fmt: DiskFormat, ) -> Result { let real_fmt = match input_fmt { - Some(fmt) if fmt == DiskFormat::Raw => DiskFormat::Raw, + Some(DiskFormat::Raw) => DiskFormat::Raw, Some(fmt) => { if fmt != detect_fmt { bail!( @@ -588,11 +588,8 @@ mod test { "-f qcow2 -o cluster_size={} -o refcount_bits={} {} {}", cluster_size, refcount_bits, path, img_size, ); - let create_args: Vec = create_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let create_args: Vec = + create_str.split(' ').map(|str| str.to_string()).collect(); assert!(image_create(create_args).is_ok()); // Read header. @@ -627,8 +624,8 @@ mod test { format: DiskFormat::Qcow2, ..Default::default() }; - let qcow2_driver = create_qcow2_driver_for_check(file, conf).unwrap(); - qcow2_driver + + create_qcow2_driver_for_check(file, conf).unwrap() } fn read_data(&self, guest_offset: u64, buf: &Vec) -> Result<()> { @@ -694,11 +691,8 @@ mod test { impl TestRawImage { fn create(path: String, img_size: String) -> Self { let create_str = format!("-f raw {} {}", path, img_size); - let create_args: Vec = create_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let create_args: Vec = + create_str.split(' ').map(|str| str.to_string()).collect(); assert!(image_create(create_args).is_ok()); Self { path } @@ -710,8 +704,8 @@ mod test { let aio = Aio::new(Arc::new(SyncAioInfo::complete_func), AioEngine::Off, None).unwrap(); let file = open_file(&self.path, false, false).unwrap(); - let raw_driver = RawDriver::new(Arc::new(file), aio, conf); - raw_driver + + RawDriver::new(Arc::new(file), aio, conf) } } @@ -801,11 +795,8 @@ mod test { for case in test_case { let create_str = case.0.replace("img_path", path); println!("Create options: {}", create_str); - let create_args: Vec = create_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let create_args: Vec = + create_str.split(' ').map(|str| str.to_string()).collect(); if case.1 { assert!(image_create(create_args).is_ok()); @@ -836,11 +827,7 @@ mod test { for case in test_case { let cmd_str = case.0.replace("img_path", path); - let args: Vec = cmd_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let args: Vec = cmd_str.split(' ').map(|str| str.to_string()).collect(); // Query image info with type of qcow2. assert!(image_create(vec![ @@ -908,7 +895,7 @@ mod test { assert_eq!(test_image.header.size, image_size); // Check refcount. - assert_eq!(test_image.check_image(false, 0), true); + assert!(test_image.check_image(false, 0)); } } @@ -928,20 +915,13 @@ mod test { ("-f qcow2 path +1G", DiskFormat::Qcow2), ]; let check_str = format!("-f raw {}", path); - let check_args: Vec = check_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let check_args: Vec = check_str.split(' ').map(|str| str.to_string()).collect(); for case in test_case { let create_str = case.0.replace("path", path); println!("stratovirt-img {}", create_str); - let create_args: Vec = create_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let create_args: Vec = + create_str.split(' ').map(|str| str.to_string()).collect(); assert!(image_create(create_args).is_ok()); let image_file = ImageFile::create(path, false).unwrap(); @@ -981,18 +961,13 @@ mod test { let create_string = create_str.replace("disk_fmt", case.0); let create_args: Vec = create_string .split(' ') - .into_iter() .map(|str| str.to_string()) .collect(); println!("Create args: {}", create_string); assert!(image_create(create_args.clone()).is_ok()); let check_str = case.1.replace("img_path", path); - let check_args: Vec = check_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let check_args: Vec = check_str.split(' ').map(|str| str.to_string()).collect(); println!("Check args: {}", check_str); if case.2 { @@ -1254,7 +1229,7 @@ mod test { let l2_idx = qcow2_driver.table.get_l2_table_index(guest_offset) as usize; let cache_entry = qcow2_driver.get_table_cluster(guest_offset).unwrap(); let mut l2_entry = cache_entry.borrow_mut().get_entry_map(l2_idx).unwrap(); - l2_entry = l2_entry & !QCOW2_OFFSET_COPIED; + l2_entry &= !QCOW2_OFFSET_COPIED; assert!(cache_entry .borrow_mut() .set_entry_map(l2_idx, l2_entry) @@ -1605,18 +1580,14 @@ mod test { let create_string = create_str.replace("disk_fmt", case.0); let create_args: Vec = create_string .split(' ') - .into_iter() .map(|str| str.to_string()) .collect(); println!("Create args: {}", create_string); assert!(image_create(create_args).is_ok()); let snapshot_str = case.1.replace("img_path", path); - let snapshot_args: Vec = snapshot_str - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let snapshot_args: Vec = + snapshot_str.split(' ').map(|str| str.to_string()).collect(); let ret = image_snapshot(snapshot_args); if case.2 { assert!(ret.is_ok()); @@ -1648,10 +1619,10 @@ mod test { let quite = false; let fix = FIX_ERRORS | FIX_LEAKS; - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let buf = vec![1_u8; cluster_size as usize]; assert!(test_image.write_data(0, &buf).is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); // Create a snapshot named test_snapshot0 assert!(image_snapshot(vec![ @@ -1661,10 +1632,10 @@ mod test { ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let buf = vec![2_u8; cluster_size as usize]; assert!(test_image.write_data(0, &buf).is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); // Create as snapshot named test_snapshot1. assert!(image_snapshot(vec![ @@ -1674,10 +1645,10 @@ mod test { ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let buf = vec![3_u8; cluster_size as usize]; assert!(test_image.write_data(0, &buf).is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); // Apply snapshot named test_snapshot0. assert!(image_snapshot(vec![ @@ -1687,7 +1658,7 @@ mod test { ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let buf = vec![0_u8; cluster_size as usize]; assert!(test_image.read_data(0, &buf).is_ok()); for elem in buf { @@ -1695,7 +1666,7 @@ mod test { } let buf = vec![4_u8; cluster_size as usize]; assert!(test_image.write_data(0, &buf).is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); // Apply snapshot named test_snapshot1 assert!(image_snapshot(vec![ @@ -1704,7 +1675,7 @@ mod test { path.to_string() ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let buf = vec![0_u8; cluster_size as usize]; assert!(test_image.read_data(0, &buf).is_ok()); for elem in buf { @@ -1754,14 +1725,10 @@ mod test { // Apply resize operation. let cmd = cmd.replace("img_path", path); - let args: Vec = cmd - .split(' ') - .into_iter() - .map(|str| str.to_string()) - .collect(); + let args: Vec = cmd.split(' ').map(|str| str.to_string()).collect(); assert_eq!(image_resize(args).is_ok(), res); - assert!(remove_file(path.to_string()).is_ok()); + assert!(remove_file(path).is_ok()); } } @@ -1788,7 +1755,7 @@ mod test { let buf = vec![1; 10240]; let mut driver = test_image.create_driver(); while offset < 10 * M as usize { - assert!(image_write(&mut driver, offset as usize, &buf).is_ok()); + assert!(image_write(&mut driver, offset, &buf).is_ok()); offset += 10240; } drop(driver); @@ -1806,7 +1773,7 @@ mod test { let buf = vec![2; 10240]; let mut driver = test_image.create_driver(); while offset < (10 + 10) * M as usize { - assert!(image_write(&mut driver, offset as usize, &buf).is_ok()); + assert!(image_write(&mut driver, offset, &buf).is_ok()); offset += 10240; } @@ -1903,7 +1870,7 @@ mod test { let mut driver = test_image.create_driver(); let buf = vec![1; 1024 * 1024]; assert!(image_write(&mut driver, 0, &buf).is_ok()); - assert_eq!(driver.header.size, 1 * G); + assert_eq!(driver.header.size, G); drop(driver); let quite = false; let fix = FIX_ERRORS | FIX_LEAKS; @@ -1914,13 +1881,13 @@ mod test { path.to_string() ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let mut driver = test_image.create_driver(); let buf = vec![2; 1024 * 1024]; assert!(image_write(&mut driver, 0, &buf).is_ok()); - assert_eq!(driver.header.size, 1 * G); + assert_eq!(driver.header.size, G); drop(driver); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); assert!(image_resize(vec![ "-f".to_string(), @@ -1929,7 +1896,7 @@ mod test { "+20G".to_string(), ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let mut driver = test_image.create_driver(); let buf = vec![3; 1024 * 1024]; assert!(image_write(&mut driver, 20 * G as usize, &buf).is_ok()); @@ -1938,7 +1905,7 @@ mod test { assert!(vec_is_fill_with(&buf, 2)); assert_eq!(driver.header.size, 21 * G); drop(driver); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); assert!(image_snapshot(vec![ "-c".to_string(), @@ -1946,13 +1913,13 @@ mod test { path.to_string() ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let mut driver = test_image.create_driver(); let buf = vec![4; 1024 * 1024]; assert!(image_write(&mut driver, 20 * G as usize, &buf).is_ok()); assert_eq!(driver.header.size, 21 * G); drop(driver); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); assert!(image_resize(vec![ "-f".to_string(), @@ -1961,7 +1928,7 @@ mod test { "+10G".to_string(), ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let mut driver = test_image.create_driver(); let buf = vec![5; 1024 * 1024]; assert!(image_write(&mut driver, 30 * G as usize, &buf).is_ok()); @@ -1970,7 +1937,7 @@ mod test { assert!(vec_is_fill_with(&buf, 4)); assert_eq!(driver.header.size, 31 * G); drop(driver); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); assert!(image_snapshot(vec![ "-a".to_string(), @@ -1978,14 +1945,14 @@ mod test { path.to_string() ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let mut driver = test_image.create_driver(); let buf = vec![0; 1024 * 1024]; assert!(image_read(&mut driver, 0, &buf).is_ok()); assert!(vec_is_fill_with(&buf, 1)); - assert_eq!(driver.header.size, 1 * G); + assert_eq!(driver.header.size, G); drop(driver); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); assert!(image_snapshot(vec![ "-a".to_string(), @@ -1993,13 +1960,13 @@ mod test { path.to_string() ]) .is_ok()); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); let mut driver = test_image.create_driver(); let buf = vec![0; 1024 * 1024]; assert!(image_read(&mut driver, 20 * G as usize, &buf).is_ok()); assert!(vec_is_fill_with(&buf, 3)); assert_eq!(driver.header.size, 21 * G); drop(driver); - assert_eq!(test_image.check_image(quite, fix), true); + assert!(test_image.check_image(quite, fix)); } } diff --git a/machine_manager/src/config/boot_source.rs b/machine_manager/src/config/boot_source.rs index 6f7c227a4..997b84104 100644 --- a/machine_manager/src/config/boot_source.rs +++ b/machine_manager/src/config/boot_source.rs @@ -239,8 +239,8 @@ mod tests { test_kernel_param.push(Param::from_str("maxcpus=8")); assert_eq!(test_kernel_param.params.len(), 6); - assert_eq!(test_kernel_param.contains("maxcpus"), true); - assert_eq!(test_kernel_param.contains("cpus"), false); + assert!(test_kernel_param.contains("maxcpus")); + assert!(!test_kernel_param.contains("cpus")); assert_eq!( test_kernel_param.to_string(), "reboot=k panic=1 pci=off nomodules 8250.nr_uarts=0 maxcpus=8" @@ -257,7 +257,7 @@ mod tests { initrd_file.set_len(100_u64).unwrap(); let mut vm_config = VmConfig::default(); assert!(vm_config.add_kernel(&kernel_path).is_ok()); - vm_config.add_kernel_cmdline(&vec![ + vm_config.add_kernel_cmdline(&[ String::from("console=ttyS0"), String::from("reboot=k"), String::from("panic=1"), diff --git a/machine_manager/src/config/camera.rs b/machine_manager/src/config/camera.rs index 43af312c3..b3964f4f2 100644 --- a/machine_manager/src/config/camera.rs +++ b/machine_manager/src/config/camera.rs @@ -93,10 +93,10 @@ impl VmConfig { } pub fn del_cameradev_by_id(&mut self, id: &str) -> Result<()> { - if self.camera_backend.get(&id.to_string()).is_none() { + if !self.camera_backend.contains_key(id) { bail!("no cameradev with id {}", id); } - self.camera_backend.remove(&id.to_string()); + self.camera_backend.remove(id); Ok(()) } diff --git a/machine_manager/src/config/chardev.rs b/machine_manager/src/config/chardev.rs index 0ea4d49b8..1eb0d489c 100644 --- a/machine_manager/src/config/chardev.rs +++ b/machine_manager/src/config/chardev.rs @@ -265,7 +265,7 @@ impl VmConfig { /// * `conf` - The chardev config to be added to the vm. pub fn add_chardev_with_config(&mut self, conf: ChardevConfig) -> Result<()> { let chardev_id = conf.id(); - if self.chardev.get(&chardev_id).is_some() { + if self.chardev.contains_key(&chardev_id) { bail!("Chardev {:?} has been added", chardev_id); } self.chardev.insert(chardev_id, conf); diff --git a/machine_manager/src/config/display.rs b/machine_manager/src/config/display.rs index 743a476cc..7369e4298 100644 --- a/machine_manager/src/config/display.rs +++ b/machine_manager/src/config/display.rs @@ -123,28 +123,28 @@ mod tests { assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); assert_eq!(display_config.display_type, "gtk"); - assert_eq!(display_config.full_screen, false); + assert!(!display_config.full_screen); let mut vm_config = VmConfig::default(); let config_line = "gtk,full-screen=on"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); assert_eq!(display_config.display_type, "gtk"); - assert_eq!(display_config.full_screen, true); + assert!(display_config.full_screen); let mut vm_config = VmConfig::default(); let config_line = "gtk,full-screen=off"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); assert_eq!(display_config.display_type, "gtk"); - assert_eq!(display_config.full_screen, false); + assert!(!display_config.full_screen); let mut vm_config = VmConfig::default(); let config_line = "gtk,app-name=desktopappengine"; assert!(vm_config.add_display(config_line).is_ok()); let display_config = vm_config.display.unwrap(); assert_eq!(display_config.display_type, "gtk"); - assert_eq!(display_config.full_screen, false); + assert!(!display_config.full_screen); assert_eq!( display_config.app_name, Some("desktopappengine".to_string()) diff --git a/machine_manager/src/config/drive.rs b/machine_manager/src/config/drive.rs index a6f9fd750..a9a132eb2 100644 --- a/machine_manager/src/config/drive.rs +++ b/machine_manager/src/config/drive.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::fmt::Display; use std::fs::{metadata, File}; use std::os::linux::fs::MetadataExt; use std::path::Path; @@ -79,11 +80,11 @@ impl FromStr for DiskFormat { } } -impl ToString for DiskFormat { - fn to_string(&self) -> String { - match *self { - DiskFormat::Raw => "raw".to_string(), - DiskFormat::Qcow2 => "qcow2".to_string(), +impl Display for DiskFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DiskFormat::Raw => write!(f, "raw"), + DiskFormat::Qcow2 => write!(f, "qcow2"), } } } @@ -291,7 +292,7 @@ impl VmConfig { /// * `drive_conf` - The drive config to be added to the vm. pub fn add_drive_with_config(&mut self, drive_conf: DriveConfig) -> Result<()> { let drive_id = drive_conf.id.clone(); - if self.drives.get(&drive_id).is_some() { + if self.drives.contains_key(&drive_id) { bail!("Drive {} has been added", drive_id); } self.drives.insert(drive_id, drive_conf); @@ -304,7 +305,7 @@ impl VmConfig { /// /// * `drive_id` - Drive id. pub fn del_drive_by_id(&mut self, drive_id: &str) -> Result { - if self.drives.get(drive_id).is_some() { + if self.drives.contains_key(drive_id) { Ok(self.drives.remove(drive_id).unwrap().path_on_host) } else { bail!("Drive {} not found", drive_id); @@ -347,7 +348,7 @@ mod tests { let pflash_cfg = &pflash[0]; assert_eq!(pflash_cfg.unit.unwrap(), 0); assert_eq!(pflash_cfg.path_on_host, "flash0.fd".to_string()); - assert_eq!(pflash_cfg.readonly, true); + assert!(pflash_cfg.readonly); // Test2: Change parameters sequence. let mut vm_config = VmConfig::default(); @@ -376,11 +377,11 @@ mod tests { let pflash_cfg = &pflash[0]; assert_eq!(pflash_cfg.unit.unwrap(), 0); assert_eq!(pflash_cfg.path_on_host, "flash0.fd".to_string()); - assert_eq!(pflash_cfg.readonly, true); + assert!(pflash_cfg.readonly); let pflash_cfg = &pflash[1]; assert_eq!(pflash_cfg.unit.unwrap(), 1); assert_eq!(pflash_cfg.path_on_host, "flash1.fd".to_string()); - assert_eq!(pflash_cfg.readonly, false); + assert!(!pflash_cfg.readonly); // Test4: Illegal parameters unit/format. let mut vm_config = VmConfig::default(); @@ -412,10 +413,10 @@ mod tests { assert_eq!(drive_cfg.id, "rootfs"); assert_eq!(drive_cfg.path_on_host, "/path/to/rootfs"); assert_eq!(drive_cfg.format.to_string(), "qcow2"); - assert_eq!(drive_cfg.readonly, false); - assert_eq!(drive_cfg.direct, true); + assert!(!drive_cfg.readonly); + assert!(drive_cfg.direct); assert_eq!(drive_cfg.iops.unwrap(), 200); - assert_eq!(drive_cfg.discard, true); + assert!(drive_cfg.discard); assert_eq!( drive_cfg.write_zeroes, WriteZeroesState::from_str("unmap").unwrap() @@ -502,7 +503,7 @@ mod tests { let mut drive_conf = DriveConfig::default(); drive_conf.id = String::from(*id); assert!(vm_config.drives.get(*id).is_some()); - assert!(vm_config.del_drive_by_id(*id).is_ok()); + assert!(vm_config.del_drive_by_id(id).is_ok()); assert!(vm_config.drives.get(*id).is_none()); } } @@ -513,19 +514,19 @@ mod tests { let drive_conf = vm_config .add_drive("id=rootfs,file=/path/to/rootfs,discard=ignore") .unwrap(); - assert_eq!(drive_conf.discard, false); + assert!(!drive_conf.discard); let mut vm_config = VmConfig::default(); let drive_conf = vm_config .add_drive("id=rootfs,file=/path/to/rootfs,discard=unmap") .unwrap(); - assert_eq!(drive_conf.discard, true); + assert!(drive_conf.discard); let mut vm_config = VmConfig::default(); let ret = vm_config .add_drive("id=rootfs,file=/path/to/rootfs,discard=invalid") .is_err(); - assert_eq!(ret, true); + assert!(ret); } #[test] @@ -552,6 +553,6 @@ mod tests { let ret = vm_config .add_drive("id=rootfs,file=/path/to/rootfs,detect-zeroes=invalid") .is_err(); - assert_eq!(ret, true); + assert!(ret); } } diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 05c428e6c..6163f111d 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -481,7 +481,7 @@ impl VmConfig { bail!("Object type: {} config path err", zone_config.mem_type); } - if self.object.mem_object.get(&zone_config.id).is_some() { + if self.object.mem_object.contains_key(&zone_config.id) { bail!("Object: {} has been added", zone_config.id); } self.object @@ -632,9 +632,9 @@ mod tests { machine_config.nr_cpus = MIN_NR_CPUS as u8; machine_config.mem_config.mem_size = MIN_MEMSIZE - 1; - assert!(!machine_config.check().is_ok()); + assert!(machine_config.check().is_err()); machine_config.mem_config.mem_size = MAX_MEMSIZE + 1; - assert!(!machine_config.check().is_ok()); + assert!(machine_config.check().is_err()); machine_config.mem_config.mem_size = MIN_MEMSIZE; assert!(machine_config.check().is_ok()); @@ -831,8 +831,8 @@ mod tests { assert!(machine_cfg_ret.is_ok()); let machine_cfg = vm_config.machine_config; assert_eq!(machine_cfg.mach_type, MachineType::None); - assert_eq!(machine_cfg.mem_config.dump_guest_core, true); - assert_eq!(machine_cfg.mem_config.mem_share, true); + assert!(machine_cfg.mem_config.dump_guest_core); + assert!(machine_cfg.mem_config.mem_share); let mut vm_config = VmConfig::default(); let memory_cfg_str = "none,dump-guest-core=off,mem-share=off,accel=kvm,usb=off"; @@ -841,8 +841,8 @@ mod tests { let machine_cfg = vm_config.machine_config; assert_eq!(machine_cfg.mach_type, MachineType::None); assert_eq!(machine_cfg.hypervisor, HypervisorType::Kvm); - assert_eq!(machine_cfg.mem_config.dump_guest_core, false); - assert_eq!(machine_cfg.mem_config.mem_share, false); + assert!(!machine_cfg.mem_config.dump_guest_core); + assert!(!machine_cfg.mem_config.mem_share); let mut vm_config = VmConfig::default(); let memory_cfg_str = "type=none,accel=kvm-tcg"; @@ -903,10 +903,10 @@ mod tests { let mut vm_config = VmConfig::default(); let mem_prealloc = vm_config.machine_config.mem_config.mem_prealloc; // default value is false. - assert_eq!(mem_prealloc, false); + assert!(!mem_prealloc); vm_config.enable_mem_prealloc(); let mem_prealloc = vm_config.machine_config.mem_config.mem_prealloc; - assert_eq!(mem_prealloc, true); + assert!(mem_prealloc); } #[test] @@ -961,18 +961,18 @@ mod tests { .add_mem_zone("memory-backend-ram,size=2M,id=mem3,share=on") .unwrap(); assert_eq!(zone_config_3.size, 2 * 1024 * 1024); - assert_eq!(zone_config_3.share, true); + assert!(zone_config_3.share); let zone_config_4 = vm_config .add_mem_zone("memory-backend-ram,size=2M,id=mem4") .unwrap(); - assert_eq!(zone_config_4.share, false); - assert_eq!(zone_config_4.memfd(), false); + assert!(!zone_config_4.share); + assert!(!zone_config_4.memfd()); let zone_config_5 = vm_config .add_mem_zone("memory-backend-memfd,size=2M,id=mem5") .unwrap(); - assert_eq!(zone_config_5.memfd(), true); + assert!(zone_config_5.memfd()); } #[test] diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 2aff98011..ce4c313b2 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -39,7 +39,6 @@ pub use boot_source::*; #[cfg(feature = "usb_camera")] pub use camera::*; pub use chardev::*; -pub use devices::*; #[cfg(any(feature = "gtk", feature = "ohui_srv"))] pub use display::*; pub use drive::*; @@ -217,7 +216,7 @@ impl VmConfig { let rng_cfg = RngObjConfig::try_parse_from(str_slip_to_clap(object_args, true, false))?; let id = rng_cfg.id.clone(); - if self.object.rng_object.get(&id).is_some() { + if self.object.rng_object.contains_key(&id) { bail!("Object: {} has been added", id); } self.object.rng_object.insert(id, rng_cfg); @@ -252,7 +251,7 @@ impl VmConfig { if let Some(fast_unplug_value) = global_config.fast_unplug { let fast_unplug_key = String::from("pcie-root-port.fast-unplug"); - if self.global_config.get(&fast_unplug_key).is_some() { + if self.global_config.contains_key(&fast_unplug_key) { bail!("Global config {} has been added", fast_unplug_key); } self.global_config diff --git a/machine_manager/src/config/network.rs b/machine_manager/src/config/network.rs index 7b02a4c94..30f1ec807 100644 --- a/machine_manager/src/config/network.rs +++ b/machine_manager/src/config/network.rs @@ -189,10 +189,10 @@ impl NetworkInterfaceConfig { pub fn auto_iothread(&mut self) { // If rx_iothread or tx_iothread is not configured, the default iothread will be used. if self.rx_iothread.is_none() { - self.rx_iothread = self.iothread.clone(); + self.rx_iothread.clone_from(&self.iothread); } if self.tx_iothread.is_none() { - self.tx_iothread = self.iothread.clone(); + self.tx_iothread.clone_from(&self.iothread); } } } @@ -317,7 +317,7 @@ impl VmConfig { pub fn add_netdev_with_config(&mut self, conf: NetDevcfg) -> Result<()> { let netdev_id = conf.id.clone(); - if self.netdevs.get(&netdev_id).is_some() { + if self.netdevs.contains_key(&netdev_id) { bail!("Netdev {:?} has been added", netdev_id); } self.netdevs.insert(netdev_id, conf); @@ -398,7 +398,7 @@ mod tests { assert_eq!(netdev_cfg.id, "eth0"); assert_eq!(netdev_cfg.ifname, "tap0"); assert!(netdev_cfg.tap_fds.is_none()); - assert_eq!(netdev_cfg.vhost_kernel, false); + assert!(!netdev_cfg.vhost_kernel); assert!(netdev_cfg.vhost_fds.is_none()); assert_eq!(netdev_cfg.queues, 2); assert!(netdev_cfg.vhost_type().is_none()); @@ -465,7 +465,7 @@ mod tests { assert_eq!(net_cfg.addr.unwrap(), (1, 2)); assert_eq!(net_cfg.mac.unwrap(), "12:34:56:78:9A:BC"); assert_eq!(net_cfg.vectors, 6); - assert_eq!(net_cfg.mq, true); + assert!(net_cfg.mq); assert_eq!(net_cfg.queue_size, 2048); assert_eq!(net_cfg.multifunction, Some(true)); let netdev_cfg = vm_config.netdevs.get(&net_cfg.netdev).unwrap(); @@ -479,7 +479,7 @@ mod tests { let net_cfg = NetworkInterfaceConfig::try_parse_from(str_slip_to_clap(net_cmd, true, false)).unwrap(); assert_eq!(net_cfg.queue_size, 256); - assert_eq!(net_cfg.mq, false); + assert!(!net_cfg.mq); assert_eq!(net_cfg.vectors, 0); let netdev_cfg = vm_config.netdevs.get(&net_cfg.netdev).unwrap(); assert_eq!(netdev_cfg.vhost_type().unwrap(), "vhost-user"); @@ -561,7 +561,7 @@ mod tests { let mut net_conf = NetDevcfg::default(); net_conf.id = String::from(*id); assert!(vm_config.netdevs.get(*id).is_some()); - assert!(vm_config.del_netdev_by_id(*id).is_ok()); + assert!(vm_config.del_netdev_by_id(id).is_ok()); assert!(vm_config.netdevs.get(*id).is_none()); } } @@ -664,7 +664,7 @@ mod tests { queues: Some(u16::MAX), ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[0]); + check_err_msg(netdev, err_msgs[0]); // Abnornal test with invalid 'queues': MAX_QUEUE_PAIRS + 1. let netdev = Box::new(qmp_schema::NetDevAddArgument { @@ -683,7 +683,7 @@ mod tests { vhostfds: Some("21:22:23:24".to_string()), ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[1]); + check_err_msg(netdev, err_msgs[1]); // Abnornal test with 'fds' and 'vhostfd'. let netdev = Box::new(qmp_schema::NetDevAddArgument { @@ -691,7 +691,7 @@ mod tests { vhostfd: Some("21".to_string()), ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[2]); + check_err_msg(netdev, err_msgs[2]); // Abnornal test with different num of 'fds' and 'vhostfds'. let netdev = Box::new(qmp_schema::NetDevAddArgument { @@ -699,7 +699,7 @@ mod tests { vhostfds: Some("21:22:23".to_string()), ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[3]); + check_err_msg(netdev, err_msgs[3]); // Abnornal test with 'net_type=vhost-user'. let netdev = Box::new(qmp_schema::NetDevAddArgument { @@ -709,7 +709,7 @@ mod tests { net_type: Some("vhost-user".to_string()), ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[4]); + check_err_msg(netdev, err_msgs[4]); // Abnornal test with 'fds/vhostfds' and no 'vhost'. let netdev = Box::new(qmp_schema::NetDevAddArgument { @@ -717,13 +717,13 @@ mod tests { vhostfds: Some("21:22:23:24".to_string()), ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[5]); + check_err_msg(netdev, err_msgs[5]); // Abnornal test with all default value. let netdev = Box::new(qmp_schema::NetDevAddArgument { ..qmp_schema::NetDevAddArgument::default() }); - check_err_msg(netdev, &err_msgs[6]); + check_err_msg(netdev, err_msgs[6]); // Abnornal test with invalid fd value. let netdev = Box::new(qmp_schema::NetDevAddArgument { diff --git a/machine_manager/src/config/numa.rs b/machine_manager/src/config/numa.rs index 664b18038..f628fef79 100644 --- a/machine_manager/src/config/numa.rs +++ b/machine_manager/src/config/numa.rs @@ -209,7 +209,7 @@ mod tests { .add_numa("node,nodeid=0,cpus=[0-1:3-5],memdev=mem0") .is_ok()); - let numa = vm_config.numa_nodes.get(0).unwrap(); + let numa = vm_config.numa_nodes.first().unwrap(); let numa_config = parse_numa_mem(numa.1.as_str()).unwrap(); assert_eq!(numa_config.cpus, vec![0, 1]); assert_eq!(numa_config.mem_dev, "mem0"); @@ -235,7 +235,7 @@ mod tests { assert!(vm_config.add_numa("dist,src=0,dst=1").is_ok()); assert!(vm_config.add_numa("dist,src=0,dst=1,val=10").is_ok()); - let numa = vm_config.numa_nodes.get(0).unwrap(); + let numa = vm_config.numa_nodes.first().unwrap(); let dist_cfg = parse_numa_distance(numa.1.as_str()).unwrap(); assert_eq!(dist_cfg.numa_id, 0); assert_eq!(dist_cfg.destination, 1); diff --git a/machine_manager/src/config/sasl_auth.rs b/machine_manager/src/config/sasl_auth.rs index 37b47bc7c..f01699a19 100644 --- a/machine_manager/src/config/sasl_auth.rs +++ b/machine_manager/src/config/sasl_auth.rs @@ -33,7 +33,7 @@ impl VmConfig { let saslauth = SaslAuthObjConfig::try_parse_from(str_slip_to_clap(saslauth_config, true, false))?; let id = saslauth.id.clone(); - if self.object.sasl_object.get(&id).is_some() { + if self.object.sasl_object.contains_key(&id) { return Err(anyhow!(ConfigError::IdRepeat("saslauth".to_string(), id))); } self.object.sasl_object.insert(id, saslauth); @@ -62,7 +62,7 @@ mod tests { assert!(vm_config.add_object("authz-simple,id=authz0").is_ok()); assert!(vm_config.object.sasl_object.get(&id).is_some()); if let Some(obj_cfg) = vm_config.object.sasl_object.get(&id) { - assert!(obj_cfg.identity == "".to_string()); + assert!(obj_cfg.identity == *""); } } } diff --git a/machine_manager/src/config/tls_creds.rs b/machine_manager/src/config/tls_creds.rs index 92903161c..a3b7396c0 100644 --- a/machine_manager/src/config/tls_creds.rs +++ b/machine_manager/src/config/tls_creds.rs @@ -36,7 +36,7 @@ impl VmConfig { let tlscred = TlsCredObjConfig::try_parse_from(str_slip_to_clap(tlscred_config, true, false))?; let id = tlscred.id.clone(); - if self.object.tls_object.get(&id).is_some() { + if self.object.tls_object.contains_key(&id) { return Err(anyhow!(ConfigError::IdRepeat("tlscred".to_string(), id))); } self.object.tls_object.insert(id, tlscred); @@ -58,7 +58,7 @@ mod tests { if !dir.is_dir() { fs::create_dir(dir.clone()).unwrap(); } - assert_eq!(dir.is_dir(), true); + assert!(dir.is_dir()); // Certificate directory is exist. let tls_config: String = format!( @@ -72,12 +72,12 @@ mod tests { if let Some(tls_cred_cfg) = vm_config.object.tls_object.get(&id) { assert_eq!(tls_cred_cfg.dir, dir.to_str().unwrap()); assert_eq!(tls_cred_cfg.endpoint, Some("server".to_string())); - assert_eq!(tls_cred_cfg.verifypeer, false); + assert!(!tls_cred_cfg.verifypeer); } // Delete file. fs::remove_dir(dir.clone()).unwrap(); - assert_eq!(dir.is_dir(), false); + assert!(!dir.is_dir()); // Certificate directory does not exist. let mut vm_config = VmConfig::default(); assert!(vm_config.add_object(tls_config.as_str()).is_err()); diff --git a/machine_manager/src/config/vnc.rs b/machine_manager/src/config/vnc.rs index ca1fd104e..f257ae07c 100644 --- a/machine_manager/src/config/vnc.rs +++ b/machine_manager/src/config/vnc.rs @@ -86,14 +86,14 @@ mod tests { assert_eq!(vnc_config.addr.0, String::from("0.0.0.0")); assert_eq!(vnc_config.addr.1, 5901); assert_eq!(vnc_config.tls_creds, String::from("vnc-tls-creds0")); - assert_eq!(vnc_config.sasl, true); + assert!(vnc_config.sasl); assert_eq!(vnc_config.sasl_authz, String::from("authz0")); let mut vm_config = VmConfig::default(); let config_line = "0.0.0.0:5900,tls-creds=vnc-tls-creds0"; assert!(vm_config.add_vnc(config_line).is_ok()); let vnc_config = vm_config.vnc.unwrap(); - assert_eq!(vnc_config.sasl, false); + assert!(!vnc_config.sasl); assert_eq!(vnc_config.addr.1, 11800); let mut vm_config = VmConfig::default(); diff --git a/machine_manager/src/qmp/qmp_response.rs b/machine_manager/src/qmp/qmp_response.rs index 54fe538a3..8db50f9af 100644 --- a/machine_manager/src/qmp/qmp_response.rs +++ b/machine_manager/src/qmp/qmp_response.rs @@ -226,7 +226,7 @@ mod tests { running: true, status: qmp_schema::RunState::running, }; - let resp = Response::create_response(serde_json::to_value(&resp_value).unwrap(), None); + let resp = Response::create_response(serde_json::to_value(resp_value).unwrap(), None); let json_msg = r#"{"return":{"running":true,"singlestep":false,"status":"running"}}"#; assert_eq!(serde_json::to_string(&resp).unwrap(), json_msg); diff --git a/machine_manager/src/qmp/qmp_schema.rs b/machine_manager/src/qmp/qmp_schema.rs index d520bd6ab..ba43fc011 100644 --- a/machine_manager/src/qmp/qmp_schema.rs +++ b/machine_manager/src/qmp/qmp_schema.rs @@ -2010,7 +2010,7 @@ mod tests { fn test_qmp_event_msg() { let event_json = r#"{"event":"STOP","data":{},"timestamp":{"seconds":1575531524,"microseconds":91519}}"#; - let qmp_event: QmpEvent = serde_json::from_str(&event_json).unwrap(); + let qmp_event: QmpEvent = serde_json::from_str(event_json).unwrap(); match qmp_event { QmpEvent::Stop { data: _, diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index 24993aa22..d2ed49928 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::fmt::Display; use std::net::IpAddr; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; @@ -62,13 +63,13 @@ impl QmpSocketPath { } } -impl ToString for QmpSocketPath { - fn to_string(&self) -> String { +impl Display for QmpSocketPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { QmpSocketPath::Tcp { host, port } => { - format!("{}:{}", &host, &port) + write!(f, "{}:{}", &host, &port) } - QmpSocketPath::Unix { path } => path.clone(), + QmpSocketPath::Unix { path } => write!(f, "{}", path), } } } @@ -591,7 +592,7 @@ mod tests { // Environment Recovery for UnixSocket fn recover_unix_socket_environment(socket_id: &str) { let socket_name: String = format!("test_{}.sock", socket_id); - std::fs::remove_file(&socket_name).unwrap(); + std::fs::remove_file(socket_name).unwrap(); } #[test] @@ -602,20 +603,20 @@ mod tests { // life cycle test // 1.Unconnected - assert_eq!(socket.is_connected(), false); + assert!(!socket.is_connected()); // 2.Connected socket.bind_stream(server); - assert_eq!(socket.is_connected(), true); + assert!(socket.is_connected()); // 3.Unbind SocketStream, reset state socket.drop_stream(); - assert_eq!(socket.is_connected(), false); + assert!(!socket.is_connected()); // 4.Accept and reconnect a new UnixStream let _new_client = UnixStream::connect("test_04.sock"); socket.accept(); - assert_eq!(socket.is_connected(), true); + assert!(socket.is_connected()); // After test. Environment Recover recover_unix_socket_environment("04"); @@ -663,7 +664,7 @@ mod tests { serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); match qmp_event { qmp_schema::QmpEvent::Shutdown { data, timestamp: _ } => { - assert_eq!(data.guest, true); + assert!(data.guest); assert_eq!(data.reason, "guest-shutdown".to_string()); } _ => assert!(false), @@ -692,7 +693,7 @@ mod tests { serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); let qmp_greeting = QmpGreeting::create_greeting(1, 0, 5); assert_eq!(qmp_greeting, qmp_response); - assert_eq!(res.is_err(), false); + assert!(res.is_ok()); // 2.send empty response let res = socket.send_response(false); @@ -701,7 +702,7 @@ mod tests { serde_json::from_str(&(String::from_utf8_lossy(&buffer[..length]))).unwrap(); let qmp_empty_response = Response::create_empty_response(); assert_eq!(qmp_empty_response, qmp_response); - assert_eq!(res.is_err(), false); + assert!(res.is_ok()); // After test. Environment Recover recover_unix_socket_environment("07"); diff --git a/machine_manager/src/socket.rs b/machine_manager/src/socket.rs index 4c083eb49..a2c0f3369 100644 --- a/machine_manager/src/socket.rs +++ b/machine_manager/src/socket.rs @@ -406,7 +406,7 @@ mod tests { // Environment Recovery for UnixSocket fn recover_unix_socket_environment(socket_id: &str) { let socket_name: String = format!("test_{}.sock", socket_id); - std::fs::remove_file(&socket_name).unwrap(); + std::fs::remove_file(socket_name).unwrap(); } fn socket_basic_rw(client_fd: RawFd, server_fd: RawFd) -> bool { diff --git a/migration/src/protocol.rs b/migration/src/protocol.rs index b00e486fb..ca8e23c29 100644 --- a/migration/src/protocol.rs +++ b/migration/src/protocol.rs @@ -920,12 +920,9 @@ pub mod tests { ); let mut current_slice = device_v1.get_state_vec().unwrap(); - assert_eq!( - state_2_desc - .add_padding(&state_1_desc, &mut current_slice) - .is_ok(), - true - ); + assert!(state_2_desc + .add_padding(&state_1_desc, &mut current_slice) + .is_ok()); let mut device_v2 = DeviceV2 { state: DeviceV2State::default(), @@ -964,12 +961,9 @@ pub mod tests { ); let mut current_slice = device_v2.get_state_vec().unwrap(); - assert_eq!( - state_3_desc - .add_padding(&state_2_desc, &mut current_slice) - .is_ok(), - true - ); + assert!(state_3_desc + .add_padding(&state_2_desc, &mut current_slice) + .is_ok()); let mut device_v3 = DeviceV3 { state: DeviceV3State::default(), @@ -1007,12 +1001,9 @@ pub mod tests { ); let mut current_slice = device_v3.get_state_vec().unwrap(); - assert_eq!( - state_4_desc - .add_padding(&state_3_desc, &mut current_slice) - .is_ok(), - true - ); + assert!(state_4_desc + .add_padding(&state_3_desc, &mut current_slice) + .is_ok()); let mut device_v4 = DeviceV4 { state: DeviceV4State::default(), @@ -1050,12 +1041,9 @@ pub mod tests { ); let mut current_slice = device_v4.get_state_vec().unwrap(); - assert_eq!( - state_5_desc - .add_padding(&state_4_desc, &mut current_slice) - .is_ok(), - true - ); + assert!(state_5_desc + .add_padding(&state_4_desc, &mut current_slice) + .is_ok()); let mut device_v5 = DeviceV5 { state: DeviceV5State::default(), @@ -1089,12 +1077,9 @@ pub mod tests { ); let mut current_slice = device_v2.get_state_vec().unwrap(); - assert_eq!( - state_5_desc - .add_padding(&state_2_desc, &mut current_slice) - .is_ok(), - true - ); + assert!(state_5_desc + .add_padding(&state_2_desc, &mut current_slice) + .is_ok()); let mut device_v5 = DeviceV5 { state: DeviceV5State::default(), @@ -1107,11 +1092,11 @@ pub mod tests { #[test] fn test_check_header() { - if !Kvm::new().is_ok() { + if Kvm::new().is_err() { return; } let header = MigrationHeader::default(); - assert_eq!(header.check_header().is_ok(), true); + assert!(header.check_header().is_ok()); } } diff --git a/src/main.rs b/src/main.rs index cff73cbe2..c2e81909e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -180,11 +180,9 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res if is_test_enabled() { let sock_path = cmd_args.value_of("mod-test"); - let test_sock = Some(TestSock::new(sock_path.unwrap().as_str(), vm.clone())); + let test_sock = TestSock::new(sock_path.unwrap().as_str(), vm.clone()); EventLoop::update_event( - EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new( - test_sock.unwrap(), - ))), + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(test_sock))), None, ) .with_context(|| "Failed to add test socket to MainLoop")?; @@ -211,7 +209,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Res } }; - let balloon_switch_on = vm_config.dev_name.get("balloon").is_some(); + let balloon_switch_on = vm_config.dev_name.contains_key("balloon"); if !cmd_args.is_present("disable-seccomp") { vm.lock() .unwrap() diff --git a/tests/mod_test/src/libdriver/pci.rs b/tests/mod_test/src/libdriver/pci.rs index 02fc6d92f..1fc0ad10c 100644 --- a/tests/mod_test/src/libdriver/pci.rs +++ b/tests/mod_test/src/libdriver/pci.rs @@ -334,21 +334,16 @@ impl TestPciDev { } pub fn io_map(&self, barnum: u8) -> u64 { - let addr: u32; - let size: u64; - let location: u64; - let bar_addr: PCIBarAddr; - assert!(barnum <= 5); let bar_offset: u8 = BAR_MAP[barnum as usize]; self.config_writel(bar_offset, 0xFFFFFFFF); - addr = self.config_readl(bar_offset) & !(0x0F_u32); + let addr: u32 = self.config_readl(bar_offset) & !(0x0F_u32); assert!(addr != 0); let mut pci_bus = self.pci_bus.borrow_mut(); - size = 1 << addr.trailing_zeros(); - location = (pci_bus.mmio_alloc_ptr + size - 1) / size * size; + let size: u64 = 1 << addr.trailing_zeros(); + let location: u64 = (pci_bus.mmio_alloc_ptr + size - 1) / size * size; if location < pci_bus.mmio_alloc_ptr || location + size > pci_bus.mmio_limit { return INVALID_BAR_ADDR; } @@ -356,7 +351,7 @@ impl TestPciDev { pci_bus.mmio_alloc_ptr = location + size; drop(pci_bus); self.config_writel(bar_offset, location as u32); - bar_addr = location; + let bar_addr: PCIBarAddr = location; bar_addr } diff --git a/tests/mod_test/src/libdriver/qcow2.rs b/tests/mod_test/src/libdriver/qcow2.rs index 86cc9c0c9..61160a645 100644 --- a/tests/mod_test/src/libdriver/qcow2.rs +++ b/tests/mod_test/src/libdriver/qcow2.rs @@ -66,21 +66,21 @@ impl Qcow2Driver { fn raw_read(&self, offset: u64, buf: &mut [u8]) -> i64 { let ptr = buf.as_mut_ptr() as u64; let cnt = buf.len() as u64; - let iovec = vec![Iovec::new(ptr, cnt)]; - let ret = unsafe { + let iovec = [Iovec::new(ptr, cnt)]; + + unsafe { preadv( self.file.as_raw_fd() as c_int, iovec.as_ptr() as *const iovec, iovec.len() as c_int, offset as off_t, ) as i64 - }; - ret + } } fn raw_write(&mut self, offset: u64, buf: &mut [u8]) { self.file.seek(SeekFrom::Start(offset)).unwrap(); - self.file.write_all(&buf).unwrap(); + self.file.write_all(buf).unwrap(); } } @@ -192,12 +192,7 @@ impl QcowHeader { // From size to bits. fn size_to_bits(size: u64) -> Option { - for i in 0..63 { - if size >> i == 1 { - return Some(i); - } - } - return None; + (0..63).find(|&i| size >> i == 1) } /// Create a qcow2 format image for test. @@ -241,10 +236,10 @@ pub fn create_qcow2_img(image_path: String, image_size: u64) { file.write_all(&header.to_vec()).unwrap(); // Cluster 1 is the refcount table. - assert_eq!(header.refcount_table_offset, cluster_sz * 1); + assert_eq!(header.refcount_table_offset, cluster_sz); let mut refcount_table = [0_u8; ENTRY_SIZE as usize]; BigEndian::write_u64(&mut refcount_table, cluster_sz * 2); - file.seek(SeekFrom::Start(cluster_sz * 1)).unwrap(); + file.seek(SeekFrom::Start(cluster_sz)).unwrap(); file.write_all(&refcount_table).unwrap(); // Clusters which has been allocated. @@ -281,7 +276,7 @@ fn write_full_disk(image_path: String) { // Write l2 table. let mut refcount_block: Vec = Vec::new(); let mut l1_table = [0_u8; ENTRY_SIZE as usize]; - BigEndian::write_u64(&mut l1_table, cluster_size * 4 | QCOW2_OFFSET_COPIED); + BigEndian::write_u64(&mut l1_table, (cluster_size * 4) | QCOW2_OFFSET_COPIED); let mut l2_table: Vec = Vec::new(); for _ in 0..5 { refcount_block.push(0x00); @@ -318,7 +313,7 @@ pub fn delete_snapshot(state: Rc>, device: &str, snap: &str) pub fn query_snapshot(state: Rc>) -> Value { let qmp_str = - format!("{{\"execute\":\"human-monitor-command\",\"arguments\":{{\"command-line\":\"info snapshots\"}}}}"); + "{\"execute\":\"human-monitor-command\",\"arguments\":{\"command-line\":\"info snapshots\"}}".to_string(); let value = state.borrow_mut().qmp(&qmp_str); value diff --git a/tests/mod_test/src/libdriver/usb.rs b/tests/mod_test/src/libdriver/usb.rs index be1db39b6..c3633b48b 100644 --- a/tests/mod_test/src/libdriver/usb.rs +++ b/tests/mod_test/src/libdriver/usb.rs @@ -395,6 +395,12 @@ pub struct TestEventRingSegment { pub reserved: u32, } +impl Default for TestEventRingSegment { + fn default() -> Self { + Self::new() + } +} + impl TestEventRingSegment { pub fn new() -> Self { Self { @@ -503,21 +509,21 @@ impl TestXhciPciDevice { pub fn run(&mut self) { let status = self.pci_dev.io_readl( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS, ); assert!(status & USB_STS_HCH == USB_STS_HCH); let cmd = self.pci_dev.io_readl( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBCMD as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBCMD, ); self.pci_dev.io_writel( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBCMD as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBCMD, cmd | USB_CMD_RUN, ); let status = self.pci_dev.io_readl( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS, ); assert!(status & USB_STS_HCH != USB_STS_HCH); } @@ -627,7 +633,7 @@ impl TestXhciPciDevice { pub fn reset_controller(&mut self, auto_run: bool) { // reset xhci self.oper_regs_write(0, USB_CMD_HCRST); - let status = self.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = self.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE != USB_STS_HCE); if auto_run { self.init_host_controller(XHCI_PCI_SLOT_NUM, XHCI_PCI_FUN_NUM); @@ -699,14 +705,14 @@ impl TestXhciPciDevice { pub fn port_regs_read(&self, port_id: u32, offset: u64) -> u32 { self.pci_dev.io_readl( self.bar_addr, - u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) + offset, + u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1)) + offset, ) } pub fn port_regs_write(&mut self, port_id: u32, offset: u64, value: u32) { self.pci_dev.io_writel( self.bar_addr, - u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32) + offset, + u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1)) + offset, value, ); } @@ -830,13 +836,13 @@ impl TestXhciPciDevice { let enabled_slot = USB_CONFIG_MAX_SLOTS_ENABLED & USB_CONFIG_MAX_SLOTS_EN_MASK; self.pci_dev.io_writel( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG, enabled_slot, ); let config = self.pci_dev.io_readl( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG, ); assert_eq!(config, enabled_slot); } @@ -846,13 +852,13 @@ impl TestXhciPciDevice { let dcbaap = self.allocator.borrow_mut().alloc(u64::from(dcba)); self.pci_dev.io_writeq( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DCBAAP as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DCBAAP, dcbaap, ); let value = self.pci_dev.io_readq( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DCBAAP as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DCBAAP, ); assert_eq!(value, dcbaap); self.xhci.dcbaap = value; @@ -870,13 +876,13 @@ impl TestXhciPciDevice { self.xhci.cmd_ring.init(cmd_ring, cmd_ring_sz); self.pci_dev.io_writeq( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CMD_RING_CTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CMD_RING_CTRL, cmd_ring, ); // Read dequeue pointer return 0. let cmd_ring = self.pci_dev.io_readq( self.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CMD_RING_CTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CMD_RING_CTRL, ); assert_eq!(cmd_ring, 0); } @@ -906,13 +912,9 @@ impl TestXhciPciDevice { pub fn reset_port(&mut self, port_id: u32) { assert!(port_id > 0); - let port_offset = - u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1) as u32); - self.pci_dev.io_writel( - self.bar_addr, - port_offset + XHCI_PORTSC_OFFSET, - PORTSC_PR as u32, - ); + let port_offset = u64::from(XHCI_PCI_PORT_OFFSET + XHCI_PCI_PORT_LENGTH * (port_id - 1)); + self.pci_dev + .io_writel(self.bar_addr, port_offset + XHCI_PORTSC_OFFSET, PORTSC_PR); self.oper_regs_write(XHCI_OPER_REG_USBSTS, USB_STS_PCD); let status = self.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_PCD != USB_STS_PCD); @@ -1156,7 +1158,7 @@ impl TestXhciPciDevice { pub fn queue_device_request(&mut self, slot_id: u32, device_req: &UsbDeviceRequest) -> u64 { // Setup Stage. - let mut setup_trb = TestNormalTRB::generate_setup_td(&device_req); + let mut setup_trb = TestNormalTRB::generate_setup_td(device_req); self.queue_trb(slot_id, CONTROL_ENDPOINT_ID, &mut setup_trb); // Data Stage. let ptr = self @@ -1258,16 +1260,15 @@ impl TestXhciPciDevice { // Read data from parameter directly. pub fn get_transfer_data_direct(&self, addr: u64, len: u64) -> Vec { - let buf = self.mem_read(addr, len as usize); - buf + self.mem_read(addr, len as usize) } // Read data from parameter as address. pub fn get_transfer_data_indirect(&self, addr: u64, len: u64) -> Vec { let buf = self.mem_read(addr, 8); let mem = LittleEndian::read_u64(&buf); - let buf = self.mem_read(mem, len as usize); - buf + + self.mem_read(mem, len as usize) } pub fn get_transfer_data_indirect_with_offset( @@ -1278,8 +1279,8 @@ impl TestXhciPciDevice { ) -> Vec { let buf = self.mem_read(addr, 8); let mem = LittleEndian::read_u64(&buf); - let buf = self.mem_read(mem + offset, len); - buf + + self.mem_read(mem + offset, len) } pub fn get_command_pointer(&self) -> u64 { @@ -1345,7 +1346,7 @@ impl TestXhciPciDevice { trb.set_cycle_bit(self.get_cycle_bit(slot_id, ep_id)); } let en_ptr = self.get_transfer_pointer(slot_id, ep_id); - self.write_trb(en_ptr, &trb); + self.write_trb(en_ptr, trb); self.increase_transfer_ring(slot_id, ep_id, 1); } @@ -1426,7 +1427,7 @@ impl TestXhciPciDevice { assert_eq!(data, self.get_event_pointer(intr_idx)); // enable USB_CMD_INTE - let value = self.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); + let value = self.oper_regs_read(XHCI_OPER_REG_USBCMD); self.oper_regs_write(XHCI_OPER_REG_USBCMD, value | USB_CMD_INTE); // enable INTE let value = self.interrupter_regs_read(intr_idx as u64, XHCI_INTR_REG_IMAN); @@ -1528,8 +1529,8 @@ impl TestXhciPciDevice { fn get_device_context_address(&self, slot_id: u32) -> u64 { let device_ctx_addr = self.xhci.dcbaap + u64::from(slot_id * DEVICE_CONTEXT_ENTRY_SIZE); let mut buf = self.mem_read(device_ctx_addr, 8); - let addr = LittleEndian::read_u64(&mut buf); - addr + + LittleEndian::read_u64(&mut buf) } fn has_msix(&mut self, msix_addr: u64, msix_data: u32) -> bool { @@ -1595,7 +1596,7 @@ impl TestXhciPciDevice { // Descriptor impl TestXhciPciDevice { fn get_usb_device_type(&mut self) -> UsbDeviceType { - let usb_device_type = if *self.device_config.get("tablet").unwrap_or(&false) { + if *self.device_config.get("tablet").unwrap_or(&false) { UsbDeviceType::Tablet } else if *self.device_config.get("keyboard").unwrap_or(&false) { UsbDeviceType::Keyboard @@ -1605,9 +1606,7 @@ impl TestXhciPciDevice { UsbDeviceType::Camera } else { UsbDeviceType::Other - }; - - usb_device_type + } } fn get_iad_desc(&mut self, offset: &mut u64, addr: u64) { @@ -1618,7 +1617,7 @@ impl TestXhciPciDevice { // 1. IAD header descriptor *offset += u64::from(USB_DT_CONFIG_SIZE); - let buf = self.get_transfer_data_indirect_with_offset(addr, 8 as usize, *offset); + let buf = self.get_transfer_data_indirect_with_offset(addr, 8_usize, *offset); // descriptor type assert_eq!(buf[1], USB_DT_INTERFACE_ASSOCIATION); @@ -1642,7 +1641,7 @@ impl TestXhciPciDevice { // get total vc length from its header descriptor *offset += u64::from(USB_DT_INTERFACE_SIZE); - let buf = self.get_transfer_data_indirect_with_offset(addr, 0xd as usize, *offset); + let buf = self.get_transfer_data_indirect_with_offset(addr, 0xd_usize, *offset); let total = u16::from_le_bytes(buf[5..7].try_into().unwrap()); let remained = total - 0xd; @@ -1664,7 +1663,7 @@ impl TestXhciPciDevice { // get total vs length from its header descriptor *offset += u64::from(USB_DT_INTERFACE_SIZE); - let buf = self.get_transfer_data_indirect_with_offset(addr, 0xf as usize, *offset); + let buf = self.get_transfer_data_indirect_with_offset(addr, 0xf_usize, *offset); let total = u16::from_le_bytes(buf[4..6].try_into().unwrap()); let remained = total - 0xf; @@ -2131,7 +2130,7 @@ impl TestXhciPciDevice { // Memory operation impl TestXhciPciDevice { pub fn mem_read_u32(&self, addr: u64, buf: &mut [u32]) { - let vec_len = size_of::() * buf.len(); + let vec_len = std::mem::size_of_val(buf); let tmp = self.mem_read(addr, vec_len); for i in 0..buf.len() { buf[i] = LittleEndian::read_u32(&tmp[(size_of::() * i)..]); @@ -2139,7 +2138,7 @@ impl TestXhciPciDevice { } pub fn mem_write_u32(&self, addr: u64, buf: &[u32]) { - let vec_len = size_of::() * buf.len(); + let vec_len = std::mem::size_of_val(buf); let mut vec = vec![0_u8; vec_len]; let tmp = vec.as_mut_slice(); for i in 0..buf.len() { @@ -2330,6 +2329,12 @@ pub struct TestUsbBuilder { config: HashMap, } +impl Default for TestUsbBuilder { + fn default() -> Self { + Self::new() + } +} + impl TestUsbBuilder { pub fn new() -> Self { let mut args = Vec::new(); @@ -2386,7 +2391,7 @@ impl TestUsbBuilder { } pub fn with_usb_storage(mut self, image_path: &str, media: &str) -> Self { - let args = format!("-device usb-storage,drive=drive0,id=storage0"); + let args = "-device usb-storage,drive=drive0,id=storage0".to_string(); let args: Vec<&str> = args[..].split(' ').collect(); let mut args = args.into_iter().map(|s| s.to_string()).collect(); self.args.append(&mut args); @@ -2492,8 +2497,7 @@ pub fn qmp_plug_keyboard_event(test_state: RefMut, num: u32) -> Value str += &num_str; str += "\",\"bus\":\"usb.0\",\"port\":\"1\"}}"; - let value = test_state.qmp(&str); - value + test_state.qmp(&str) } pub fn qmp_plug_tablet_event(test_state: RefMut, num: u32) -> Value { @@ -2504,8 +2508,7 @@ pub fn qmp_plug_tablet_event(test_state: RefMut, num: u32) -> Value { str += &num_str; str += "\",\"bus\":\"usb.0\",\"port\":\"2\"}}"; - let value = test_state.qmp(&str); - value + test_state.qmp(&str) } pub fn qmp_unplug_usb_event(test_state: RefMut, num: u32) -> Value { @@ -2514,8 +2517,7 @@ pub fn qmp_unplug_usb_event(test_state: RefMut, num: u32) -> Value { str += &num_str; str += "\"}}"; - let value = test_state.qmp(&str); - value + test_state.qmp(&str) } pub fn qmp_event_read(test_state: RefMut) { @@ -2524,6 +2526,6 @@ pub fn qmp_event_read(test_state: RefMut) { pub fn clear_iovec(test_state: RefMut, iovecs: &Vec) { for iov in iovecs.iter() { - test_state.memwrite(iov.io_base, &vec![0; iov.io_len as usize]); + test_state.memwrite(iov.io_base, &vec![0; iov.io_len]); } } diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index 802155d10..e985cc7db 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -671,7 +671,7 @@ pub fn get_display_info(gpu: &Rc>) -> VirtioGpuDisplayInf gpu.borrow_mut() .request_complete(true, hdr.as_bytes(), None, None, Some(&mut resp)); - return resp; + resp } // VIRTIO_GPU_CMD_GET_EDID @@ -689,7 +689,7 @@ pub fn get_edid(gpu: &Rc>, hdr_ctx: VirtioGpuGetEdid) -> None, Some(&mut resp), ); - return resp; + resp } pub fn current_curosr_check(dpy: &Rc>, local: &Vec) -> bool { @@ -740,7 +740,7 @@ pub fn resource_create( None, Some(&mut resp), ); - return resp; + resp } // VIRTIO_GPU_CMD_RESOURCE_UNREF @@ -761,7 +761,7 @@ pub fn resource_unref( None, Some(&mut resp), ); - return resp; + resp } // VIRTIO_GPU_CMD_RESOURCE_FLUSH @@ -782,7 +782,7 @@ pub fn resource_flush( None, Some(&mut resp), ); - return resp; + resp } // VIRTIO_GPU_CMD_UPDATE_CURSOR @@ -831,7 +831,7 @@ pub fn resource_attach_backing( Some(&ctx), Some(&mut resp), ); - return resp; + resp } pub fn resource_attach_backing_with_invalid_ctx_len( @@ -851,7 +851,7 @@ pub fn resource_attach_backing_with_invalid_ctx_len( None, Some(&mut resp), ); - return resp; + resp } // VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING @@ -872,7 +872,7 @@ pub fn resource_detach_backing( None, Some(&mut resp), ); - return resp; + resp } // VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D @@ -892,7 +892,7 @@ pub fn transfer_to_host( None, Some(&mut resp), ); - return resp; + resp } // VIRTIO_GPU_CMD_SET_SCANOUT @@ -913,7 +913,7 @@ pub fn set_scanout( None, Some(&mut resp), ); - return resp; + resp } pub fn invalid_cmd_test(gpu: &Rc>) -> VirtioGpuCtrlHdr { @@ -924,5 +924,5 @@ pub fn invalid_cmd_test(gpu: &Rc>) -> VirtioGpuCtrlHdr { gpu.borrow_mut() .request_complete(true, hdr.as_bytes(), None, None, Some(&mut resp)); - return resp; + resp } diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index bc5b9287b..7e55fee2b 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -263,7 +263,7 @@ impl TestVirtioPciDev { } fn has_msix(&self, msix_addr: u64, msix_data: u32) -> bool { - return self.pci_dev.has_msix(msix_addr, msix_data); + self.pci_dev.has_msix(msix_addr, msix_data) } pub fn setup_virtqueue_intr( @@ -597,7 +597,7 @@ impl VirtioDeviceOps for TestVirtioPciDev { let vq = virtqueue.borrow(); let idx: u16 = test_state.borrow().readw(vq.avail + 2); - if (!vq.event) || (idx >= vq.get_avail_event(test_state.clone()) + 1) { + if (!vq.event) || (idx > vq.get_avail_event(test_state.clone())) { self.virtqueue_notify(virtqueue.clone()); } } diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index 3158c5ee7..83ada8753 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -57,19 +57,14 @@ pub const PIXMAN_YUY2: u32 = 4; pub const REFRESH_TIME_INTERVAL: u64 = 3000 * 1000 * 1000; /// Input event. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum InputEvent { KbdEvent = 0, MouseEvent = 1, + #[default] InvalidEvent = 255, } -impl Default for InputEvent { - fn default() -> Self { - InputEvent::InvalidEvent - } -} - impl From for InputEvent { fn from(v: u8) -> Self { match v { @@ -91,21 +86,16 @@ pub struct InputMessage { } /// GPU device Event. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum GpuEvent { ReplaceSurface = 0, ReplaceCursor = 1, GraphicUpdateArea = 2, GraphicUpdateDirty = 3, + #[default] Deactive = 4, } -impl Default for GpuEvent { - fn default() -> Self { - GpuEvent::Deactive - } -} - #[derive(Debug, Clone, Copy, Default)] pub struct TestGpuCmd { pub event_type: GpuEvent, @@ -691,7 +681,7 @@ impl VncClient { pub fn epoll_ctl(&mut self, event: EpollEvent) -> io::Result<()> { self.epoll - .ctl(ControlOperation::Add, self.stream.as_raw_fd() as i32, event) + .ctl(ControlOperation::Add, self.stream.as_raw_fd(), event) } /// Wait for events on the epoll. @@ -701,13 +691,8 @@ impl VncClient { /// 2. Return if event happen or time out. pub fn epoll_wait(&mut self, event_set: EventSet) -> io::Result { let event = EpollEvent::new(event_set, self.stream.as_raw_fd() as u64); - if let Err(e) = self.epoll.ctl( - ControlOperation::Modify, - self.stream.as_raw_fd() as i32, - event, - ) { - return Err(e); - } + self.epoll + .ctl(ControlOperation::Modify, self.stream.as_raw_fd(), event)?; self.epoll .wait(EPOLL_DEFAULT_TIMEOUT, &mut self.ready_events[..]) } @@ -716,11 +701,8 @@ impl VncClient { pub fn stream_read_to_end(&mut self) -> Result<()> { let mut buf: Vec = Vec::new(); let event = EpollEvent::new(EventSet::IN, self.stream.as_raw_fd() as u64); - self.epoll.ctl( - ControlOperation::Modify, - self.stream.as_raw_fd() as i32, - event, - )?; + self.epoll + .ctl(ControlOperation::Modify, self.stream.as_raw_fd(), event)?; match self .epoll @@ -787,7 +769,7 @@ impl VncClient { if "RFB 003.008\n".as_bytes().to_vec() != buf[..12].to_vec() { bail!("Unsupported RFB version"); } - self.write_msg(&"RFB 003.008\n".as_bytes().to_vec())?; + self.write_msg("RFB 003.008\n".as_bytes())?; buf.drain(..12); // Step 2: Auth num is 1. @@ -800,7 +782,7 @@ impl VncClient { bail!("Unsupported security type!"); } buf.drain(..auth_num as usize); - self.write_msg(&(sec_type as u8).to_be_bytes().to_vec())?; + self.write_msg((sec_type as u8).to_be_bytes().as_ref())?; match sec_type { TestAuthType::VncAuthNone => { @@ -810,7 +792,7 @@ impl VncClient { if buf[..4].to_vec() != [0_u8; 4].to_vec() { bail!("Reject by vnc server"); } - self.write_msg(&0_u8.to_be_bytes().to_vec())?; + self.write_msg(0_u8.to_be_bytes().as_ref())?; buf.drain(..4); // Step 4. display mode information init: width + height + pixelformat + app_name. @@ -856,10 +838,7 @@ impl VncClient { let mut test_event = TestSetupEncoding::new(); if let Some(encoding) = enc { test_event.encs.push(encoding); - test_event.num_encodings = match enc_num { - Some(num) => num, - None => 1_u16, - }; + test_event.num_encodings = enc_num.unwrap_or(1_u16); } else { for encoding in EncodingType::ENCODINGTYPE { test_event.encs.push(encoding); @@ -1020,7 +999,7 @@ impl VncClient { let message_len: usize = frame_buff.w as usize * frame_buff.h as usize * (pf.bit_per_pixel as usize / 8); println!("Total bytes of image data: {:?}", message_len); - self.read_msg(buf, message_len as usize)?; + self.read_msg(buf, message_len)?; buf.drain(..message_len); Ok(()) } @@ -1211,7 +1190,7 @@ impl TestDemoGpuDevice { test_state.borrow_mut().writel(addr + 13, cmd.h); test_state.borrow_mut().writel(addr + 17, cmd.data_len); // Write to specific address. - self.pci_dev.io_writeq(self.bar_addr, 0 as u64, addr); + self.pci_dev.io_writeq(self.bar_addr, 0_u64, addr); test_state.borrow().clock_step_ns(REFRESH_TIME_INTERVAL); println!("cmd : {:?}", cmd); } diff --git a/tests/mod_test/src/libtest.rs b/tests/mod_test/src/libtest.rs index 3aa957231..c98c32244 100644 --- a/tests/mod_test/src/libtest.rs +++ b/tests/mod_test/src/libtest.rs @@ -138,7 +138,7 @@ impl TestState { let resp: Value = serde_json::from_slice(self.qmp_sock.read_line(timeout).as_bytes()).unwrap(); assert!(resp.get("event").is_some()); - return resp; + resp } pub fn qmp(&self, cmd: &str) -> Value { @@ -205,7 +205,7 @@ impl TestState { pub fn readq(&self, addr: u64) -> u64 { let cmd = format!("readq 0x{:x}", addr); - self.send_read_cmd(&cmd) as u64 + self.send_read_cmd(&cmd) } pub fn memread(&self, addr: u64, size: u64) -> Vec { diff --git a/tests/mod_test/src/utils.rs b/tests/mod_test/src/utils.rs index b393cc378..06a2118a2 100644 --- a/tests/mod_test/src/utils.rs +++ b/tests/mod_test/src/utils.rs @@ -56,7 +56,7 @@ pub fn read_le_u64(input: &mut &[u8]) -> u64 { } pub fn swap_u16(value: u16) -> u16 { - return value << 8 | value >> 8; + value << 8 | value >> 8 } pub fn swap_u32(value: u32) -> u32 { @@ -143,15 +143,11 @@ pub fn support_numa() -> bool { } } } - if has_nodes { - return true; - } else { - return false; - } + has_nodes } - Err(_) => return false, + Err(_) => false, } } else { - return false; + false } } diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 9ac0a6fe7..63dafe6c9 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -39,7 +39,7 @@ const ADDRESS_BASE: u64 = 0x4000_0000; fn read_lines(filename: String) -> io::Lines> { let file = File::open(filename).unwrap(); - return io::BufReader::new(file).lines(); + io::BufReader::new(file).lines() } fn get_hugesize() -> u64 { @@ -48,12 +48,12 @@ fn get_hugesize() -> u64 { for line in lines { if let Ok(info) = line { if info.starts_with("HugePages_Free:") { - let free: Vec<&str> = info.split(":").collect(); + let free: Vec<&str> = info.split(':').collect(); free_page = free[1].trim().parse::().unwrap(); } if info.starts_with("Hugepagesize:") { - let huges: Vec<&str> = info.split(":").collect(); - let sizes: Vec<&str> = huges[1].trim().split(" ").collect(); + let huges: Vec<&str> = info.split(':').collect(); + let sizes: Vec<&str> = huges[1].trim().split(' ').collect(); let size = sizes[0].trim().parse::().unwrap(); return free_page * size; } @@ -91,7 +91,7 @@ impl VirtioBalloonTest { let mut extra_args: Vec<&str> = Vec::new(); let mut fpr_switch = String::from("false"); let mut auto_switch = String::from("false"); - let mem_path = format!("-mem-path /tmp/stratovirt/hugepages"); + let mem_path = "-mem-path /tmp/stratovirt/hugepages".to_string(); let mut machine_args = MACHINE_TYPE_ARG.to_string(); if shared { @@ -130,8 +130,7 @@ impl VirtioBalloonTest { dev.borrow_mut().init(pci_slot, 0); let features = dev.borrow_mut().get_device_features(); - let inf_queue; - let def_queue; + let mut fpr_queue = None; let mut auto_queue = None; let mut que_num = 2_usize; @@ -145,8 +144,8 @@ impl VirtioBalloonTest { let ques = dev.borrow_mut() .init_device(test_state.clone(), allocator.clone(), features, que_num); - inf_queue = ques[0].clone(); - def_queue = ques[1].clone(); + let inf_queue = ques[0].clone(); + let def_queue = ques[1].clone(); if cfg.fpr { fpr_queue = Some(ques[idx].clone()); idx += 1; @@ -213,14 +212,12 @@ impl VirtioBalloonTest { dev.borrow_mut().init(4, 0); let features = dev.borrow_mut().get_device_features(); - let inf_queue; - let def_queue; let ques = dev .borrow_mut() .init_device(test_state.clone(), allocator.clone(), features, 2); - inf_queue = ques[0].clone(); - def_queue = ques[1].clone(); + let inf_queue = ques[0].clone(); + let def_queue = ques[1].clone(); VirtioBalloonTest { device: dev, @@ -779,7 +776,7 @@ fn query() { assert_eq!( *ret.get("return").unwrap(), - json!({"actual": 2147483648 as u64}) + json!({"actual": 2147483648_u64}) ); balloon.state.borrow_mut().stop(); @@ -835,10 +832,7 @@ fn balloon_config_001() { let ten_millis = time::Duration::from_millis(10); thread::sleep(ten_millis); let ret = balloon.state.borrow_mut().qmp_read(); - assert_eq!( - *ret.get("data").unwrap(), - json!({"actual": 536870912 as u64}) - ); + assert_eq!(*ret.get("data").unwrap(), json!({"actual": 536870912_u64})); balloon .state @@ -906,10 +900,7 @@ fn balloon_config_002() { let ten_millis = time::Duration::from_millis(10); thread::sleep(ten_millis); let ret = balloon.state.borrow_mut().qmp_read(); - assert_eq!( - *ret.get("data").unwrap(), - json!({"actual": 536870912 as u64}) - ); + assert_eq!(*ret.get("data").unwrap(), json!({"actual": 536870912_u64})); balloon .state @@ -957,7 +948,7 @@ fn balloon_deactive_001() { .qmp("{\"execute\": \"query-balloon\"}"); assert_eq!( *ret.get("return").unwrap(), - json!({"actual": 1073741824 as u64}) + json!({"actual": 1073741824_u64}) ); balloon.state.borrow_mut().stop(); } @@ -1009,7 +1000,7 @@ fn auto_balloon_test_001() { balloon .state .borrow_mut() - .memwrite(msg_addr, &stat.as_bytes()); + .memwrite(msg_addr, stat.as_bytes()); let auto_queue = balloon.auto_queue.unwrap(); diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index c716ebf3f..78d590bf9 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -1014,10 +1014,9 @@ fn blk_iops() { if blk.borrow().queue_was_notified(virtqueues[0].clone()) && virtqueues[0].borrow_mut().get_buf(test_state.clone()) + && virtqueues[0].borrow().desc_len.contains_key(&free_head) { - if virtqueues[0].borrow().desc_len.contains_key(&free_head) { - break; - } + break; } assert!(Instant::now() <= time_out); } @@ -1062,7 +1061,7 @@ fn blk_with_different_aio() { let image_path = Rc::new(create_img(TEST_IMAGE_SIZE_1M, 1, &image_type)); let device_args = Rc::new(String::from("")); let drive_args = if aio_probe(aio_engine).is_ok() { - Rc::new(format!(",direct={},aio={}", direct, aio_engine.to_string())) + Rc::new(format!(",direct={},aio={}", direct, aio_engine)) } else { continue; }; @@ -1773,7 +1772,7 @@ fn blk_feature_discard() { assert_eq!(image_size, full_disk_size - u64::from(num_sectors) / 2); } else if image_type == ImageType::Qcow2 && status == VIRTIO_BLK_S_OK - && (u64::from(num_sectors) * 512 & CLUSTER_SIZE - 1) == 0 + && ((u64::from(num_sectors) * 512) & (CLUSTER_SIZE - 1)) == 0 { // If the disk format is equal to Qcow2. // the length of the num sectors needs to be aligned with the cluster size, @@ -1906,7 +1905,7 @@ fn blk_feature_write_zeroes() { test_state.clone(), alloc.clone(), virtqueues[0].clone(), - &req_data.as_bytes().to_vec(), + req_data.as_bytes(), status, true, false, @@ -1944,7 +1943,7 @@ fn blk_feature_write_zeroes() { } else if image_type == ImageType::Qcow2 && status == VIRTIO_BLK_S_OK && (write_zeroes == "unmap" && discard == "unmap" && flags == 1 || len != wz_len) - && (u64::from(num_sectors) * 512 & CLUSTER_SIZE - 1) == 0 + && ((u64::from(num_sectors) * 512) & (CLUSTER_SIZE - 1)) == 0 { // If the disk format is equal to Qcow2. // the length of the num sectors needs to be aligned with the cluster size, @@ -1983,7 +1982,7 @@ fn blk_snapshot_basic() { .init_device(test_state.clone(), alloc.clone(), features, 1); create_snapshot(test_state.clone(), "drive0", "snap0"); - assert_eq!(check_snapshot(test_state.clone(), "snap0"), true); + assert!(check_snapshot(test_state.clone(), "snap0")); virtio_blk_write( blk.clone(), @@ -2003,7 +2002,7 @@ fn blk_snapshot_basic() { ); delete_snapshot(test_state.clone(), "drive0", "snap0"); - assert_eq!(check_snapshot(test_state.clone(), "snap0"), false); + assert!(!check_snapshot(test_state.clone(), "snap0")); virtio_blk_write( blk.clone(), @@ -2052,7 +2051,7 @@ fn blk_snapshot_basic2() { .borrow_mut() .init_device(test_state.clone(), alloc.clone(), features, 1); create_snapshot(test_state.clone(), "drive0", "snap0"); - assert_eq!(check_snapshot(test_state.clone(), "snap0"), true); + assert!(check_snapshot(test_state.clone(), "snap0")); tear_down( blk.clone(), test_state.clone(), @@ -2095,10 +2094,10 @@ fn blk_snapshot_basic2() { ); create_snapshot(test_state.clone(), "drive0", "snap1"); - assert_eq!(check_snapshot(test_state.clone(), "snap1"), true); + assert!(check_snapshot(test_state.clone(), "snap1")); delete_snapshot(test_state.clone(), "drive0", "snap0"); - assert_eq!(check_snapshot(test_state.clone(), "snap0"), false); + assert!(!check_snapshot(test_state.clone(), "snap0")); virtio_blk_write( blk.clone(), diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index f82083256..da210b043 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -34,7 +34,7 @@ fn test_signature() { let mut test_state = test_init(args); let mut read_data: Vec = Vec::with_capacity(4); - let target_data: [u8; 4] = ['Q' as u8, 'E' as u8, 'M' as u8, 'U' as u8]; + let target_data: [u8; 4] = [b'Q', b'E', b'M', b'U']; // Select Signature entry and read it. test_state.fw_cfg_read_bytes(FwCfgEntryType::Signature as u16, &mut read_data, 4); @@ -251,12 +251,12 @@ fn test_smbios_type0() { &mut allocator.borrow_mut(), anchor_file, &mut read_data, - 24 as u32, + 24_u32, ); - assert_eq!(anchor_size, 24 as u32); + assert_eq!(anchor_size, 24_u32); assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); - assert_eq!(read_data[6], 24 as u8); + assert_eq!(read_data[6], 24_u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); assert_eq!(talble_len, 372); @@ -315,12 +315,12 @@ fn test_smbios_type1() { &mut allocator.borrow_mut(), anchor_file, &mut read_data, - 24 as u32, + 24_u32, ); - assert_eq!(anchor_size, 24 as u32); + assert_eq!(anchor_size, 24_u32); assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); - assert_eq!(read_data[6], 24 as u8); + assert_eq!(read_data[6], 24_u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); assert_eq!(talble_len, 414); @@ -342,7 +342,7 @@ fn test_smbios_type1() { "version0" ); assert_eq!(read_table_date[48], 1); - assert_eq!(read_table_date[49], 27 as u8); + assert_eq!(read_table_date[49], 27_u8); let handle1 = LittleEndian::read_u16(&read_table_date[50..]); assert_eq!(handle1, 0x100); @@ -420,12 +420,12 @@ fn test_smbios_type2() { &mut allocator.borrow_mut(), anchor_file, &mut read_data, - 24 as u32, + 24_u32, ); - assert_eq!(anchor_size, 24 as u32); + assert_eq!(anchor_size, 24_u32); assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); - assert_eq!(read_data[6], 24 as u8); + assert_eq!(read_data[6], 24_u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); @@ -495,12 +495,12 @@ fn test_smbios_type3() { &mut allocator.borrow_mut(), anchor_file, &mut read_data, - 24 as u32, + 24_u32, ); - assert_eq!(anchor_size, 24 as u32); + assert_eq!(anchor_size, 24_u32); assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); - assert_eq!(read_data[6], 24 as u8); + assert_eq!(read_data[6], 24_u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); @@ -547,7 +547,7 @@ fn test_smbios_type4() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); - let cpu_args = format!("-smp 8,maxcpus=8,sockets=2,cores=2,threads=2"); + let cpu_args = "-smp 8,maxcpus=8,sockets=2,cores=2,threads=2".to_string(); let mut extra_args = cpu_args.split(' ').collect(); args.append(&mut extra_args); @@ -570,12 +570,12 @@ fn test_smbios_type4() { &mut allocator.borrow_mut(), anchor_file, &mut read_data, - 24 as u32, + 24_u32, ); - assert_eq!(anchor_size, 24 as u32); + assert_eq!(anchor_size, 24_u32); assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); - assert_eq!(read_data[6], 24 as u8); + assert_eq!(read_data[6], 24_u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); let mut read_table_date: Vec = Vec::with_capacity(talble_len as usize); @@ -631,7 +631,7 @@ fn test_smbios_type17() { let mut args: Vec<&str> = Vec::new(); bios_args(&mut args); - let cpu_args = format!("-smp 8,maxcpus=8,sockets=2,cores=2,threads=2"); + let cpu_args = "-smp 8,maxcpus=8,sockets=2,cores=2,threads=2".to_string(); let mut extra_args = cpu_args.split(' ').collect(); args.append(&mut extra_args); @@ -655,12 +655,12 @@ fn test_smbios_type17() { &mut allocator.borrow_mut(), anchor_file, &mut read_data, - 24 as u32, + 24_u32, ); - assert_eq!(anchor_size, 24 as u32); + assert_eq!(anchor_size, 24_u32); assert_eq!(String::from_utf8_lossy(&read_data[..5]), "_SM3_"); - assert_eq!(read_data[6], 24 as u8); + assert_eq!(read_data[6], 24_u8); let talble_len = LittleEndian::read_u32(&read_data[12..]); assert_eq!(talble_len, 467); diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index ed7dca586..4b7544a1b 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -270,7 +270,7 @@ fn rom_device_region_readwrite() { // Add a dummy rom device by qmp. The function of the device is to multiply the written value by // 2 through the write interface and save it, and read the saved value through the read // interface. - let file = File::create(&ROM_DEV_PATH).unwrap(); + let file = File::create(ROM_DEV_PATH).unwrap(); file.set_len(PAGE_SIZE).unwrap(); let qmp_str = format!( "{{ \"execute\": \"update_region\", @@ -309,7 +309,7 @@ fn rom_device_region_readwrite() { // device. The device can set the write mode to writable according to the device status during // the write operation, or directly return an error indicating that the write is not allowed. // The read operation is the same as that of IO region. - let file = File::create(&ROM_DEV_PATH).unwrap(); + let file = File::create(ROM_DEV_PATH).unwrap(); file.set_len(PAGE_SIZE).unwrap(); let qmp_str = format!( "{{ \"execute\": \"update_region\", @@ -352,7 +352,7 @@ fn ram_device_region_readwrite() { let memory_test = MemoryTest::new(MEM_SIZE, PAGE_SIZE, false, false, None, None); let addr = 0x1_0000_0000; // 4GB - let file = File::create(&RAM_DEV_PATH).unwrap(); + let file = File::create(RAM_DEV_PATH).unwrap(); file.set_len(PAGE_SIZE).unwrap(); let qmp_str = format!( "{{ \"execute\": \"update_region\", @@ -719,7 +719,7 @@ fn ram_readwrite_numa1() { assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); test_state.borrow_mut().qmp("{\"execute\": \"query-mem\"}"); - let file = File::create(&RAM_DEV_PATH).unwrap(); + let file = File::create(RAM_DEV_PATH).unwrap(); file.set_len(PAGE_SIZE).unwrap(); let qmp_str = format!( "{{ \"execute\": \"update_region\", diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 5d400a6a1..ff6a7c99b 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -343,7 +343,7 @@ fn execute_cmd(cmd: String, check: bool) { let output = cmd_exe .output() - .expect(format!("Failed to execute {}", cmd).as_str()); + .unwrap_or_else(|_| panic!("Failed to execute {}", cmd)); println!("{:?}", args); if check { assert!(output.status.success()); @@ -361,25 +361,21 @@ fn execute_cmd_checked(cmd: String) { fn create_tap(id: u8, mq: bool) { let br_name = "mst_net_qbr".to_string() + &id.to_string(); let tap_name = "mst_net_qtap".to_string() + &id.to_string(); - execute_cmd_checked("ip link add name ".to_string() + &br_name + &" type bridge".to_string()); + execute_cmd_checked("ip link add name ".to_string() + &br_name + " type bridge"); if mq { - execute_cmd_checked( - "ip tuntap add ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), - ); + execute_cmd_checked("ip tuntap add ".to_string() + &tap_name + " mode tap multi_queue"); } else { - execute_cmd_checked("ip tuntap add ".to_string() + &tap_name + &" mode tap".to_string()); + execute_cmd_checked("ip tuntap add ".to_string() + &tap_name + " mode tap"); } - execute_cmd_checked( - "ip link set ".to_string() + &tap_name + &" master ".to_string() + &br_name, - ); - execute_cmd_checked("ip link set ".to_string() + &br_name + &" up".to_string()); - execute_cmd_checked("ip link set ".to_string() + &tap_name + &" up".to_string()); + execute_cmd_checked("ip link set ".to_string() + &tap_name + " master " + &br_name); + execute_cmd_checked("ip link set ".to_string() + &br_name + " up"); + execute_cmd_checked("ip link set ".to_string() + &tap_name + " up"); execute_cmd_checked( "ip address add ".to_string() + &id.to_string() - + &".1.1.".to_string() + + ".1.1." + &id.to_string() - + &"/24 dev ".to_string() + + "/24 dev " + &br_name, ); } @@ -387,16 +383,14 @@ fn create_tap(id: u8, mq: bool) { fn clear_tap(id: u8, mq: bool) { let br_name = "mst_net_qbr".to_string() + &id.to_string(); let tap_name = "mst_net_qtap".to_string() + &id.to_string(); - execute_cmd_unchecked("ip link set ".to_string() + &tap_name + &" down".to_string()); - execute_cmd_unchecked("ip link set ".to_string() + &br_name + &" down".to_string()); + execute_cmd_unchecked("ip link set ".to_string() + &tap_name + " down"); + execute_cmd_unchecked("ip link set ".to_string() + &br_name + " down"); if mq { - execute_cmd_unchecked( - "ip tuntap del ".to_string() + &tap_name + &" mode tap multi_queue".to_string(), - ); + execute_cmd_unchecked("ip tuntap del ".to_string() + &tap_name + " mode tap multi_queue"); } else { - execute_cmd_unchecked("ip tuntap del ".to_string() + &tap_name + &" mode tap".to_string()); + execute_cmd_unchecked("ip tuntap del ".to_string() + &tap_name + " mode tap"); } - execute_cmd_unchecked("ip link delete ".to_string() + &br_name + &" type bridge".to_string()); + execute_cmd_unchecked("ip link delete ".to_string() + &br_name + " type bridge"); } #[allow(unused)] @@ -718,13 +712,13 @@ fn send_arp_request( test_state.clone(), alloc.clone(), vqs[1].clone(), - &arp_request, + arp_request, ); check_arp_mac( net.clone(), test_state.clone(), vqs[0].clone(), - &arp_request, + arp_request, need_reply, ); } @@ -750,7 +744,7 @@ fn check_device_status(net: Rc>, status: u8) { /// 1/2/3: success. #[test] fn virtio_net_rx_tx_test() { - let id = 1 * TEST_MAC_ADDR_NUMS; + let id = TEST_MAC_ADDR_NUMS; let (net, test_state, alloc) = set_up(id, false, 0, false); // Three virtqueues: tx/rx/ctrl. @@ -768,7 +762,7 @@ fn virtio_net_rx_tx_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &arp_request.as_bytes(), + arp_request.as_bytes(), true, ); @@ -809,7 +803,7 @@ fn virtio_net_rx_tx_test_iothread() { test_state.clone(), alloc.clone(), vqs.clone(), - &arp_request.as_bytes(), + arp_request.as_bytes(), true, ); @@ -877,7 +871,7 @@ fn virtio_net_ctrl_mq_test() { class: VIRTIO_NET_CTRL_MQ, cmd, }; - test_state.borrow().memwrite(addr, &ctrl_hdr.as_bytes()); + test_state.borrow().memwrite(addr, ctrl_hdr.as_bytes()); test_state .borrow() .writew(addr + size_of::() as u64, vq_pairs); @@ -1072,7 +1066,7 @@ fn send_ctrl_vq_request( ) { let ctrl_vq = &vqs[2]; let addr = alloc.borrow_mut().alloc(ctrl_data.len() as u64); - test_state.borrow().memwrite(addr, &ctrl_data); + test_state.borrow().memwrite(addr, ctrl_data); let data_entries: Vec = vec![ TestVringDescEntry { data: addr, @@ -1187,7 +1181,7 @@ fn ctrl_vq_set_mac_address( test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_mac_addr.as_bytes(), + ctrl_mac_addr.as_bytes(), VIRTIO_NET_OK, ); // Check mac address result. @@ -1231,7 +1225,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_rx_info.as_bytes(), + ctrl_rx_info.as_bytes(), VIRTIO_NET_OK, ); @@ -1250,7 +1244,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_vlan_info.as_bytes(), + ctrl_vlan_info.as_bytes(), ack, ); } @@ -1262,7 +1256,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_vlan_info.as_bytes(), + ctrl_vlan_info.as_bytes(), ack, ); } @@ -1273,7 +1267,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_vlan_info.as_bytes(), + ctrl_vlan_info.as_bytes(), VIRTIO_NET_ERR, ); // Test invalid cmd. @@ -1283,7 +1277,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_vlan_info.as_bytes(), + ctrl_vlan_info.as_bytes(), VIRTIO_NET_ERR, ); // Test invalid vid length. @@ -1303,7 +1297,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &get_arp_request(id).as_bytes(), + get_arp_request(id).as_bytes(), true, ); send_arp_request( @@ -1311,7 +1305,7 @@ fn virtio_net_ctrl_vlan_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &get_arp_request_vlan(id).as_bytes(), + get_arp_request_vlan(id).as_bytes(), false, ); @@ -1406,7 +1400,7 @@ fn virtio_net_ctrl_mac_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_rx_info.as_bytes(), + ctrl_rx_info.as_bytes(), VIRTIO_NET_OK, ); @@ -1470,7 +1464,7 @@ fn virtio_net_ctrl_mac_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &arp_request.as_bytes(), + arp_request.as_bytes(), true, ); @@ -1556,7 +1550,7 @@ fn virtio_net_ctrl_rx_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_rx_info.as_bytes(), + ctrl_rx_info.as_bytes(), VIRTIO_NET_OK, ); let mut ctrl_rx_info = CtrlRxInfo::new(VIRTIO_NET_CTRL_RX, 0, 0); @@ -1602,7 +1596,7 @@ fn virtio_net_ctrl_rx_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &ctrl_rx_info.as_bytes(), + ctrl_rx_info.as_bytes(), ack, ); } @@ -1617,7 +1611,7 @@ fn virtio_net_ctrl_rx_test() { test_state.clone(), alloc.clone(), vqs.clone(), - &arp_request.as_bytes(), + arp_request.as_bytes(), need_reply, ); @@ -1663,7 +1657,7 @@ fn virtio_net_ctrl_abnormal_test() { for i in 0..test_num { let ctrl_vq = &vqs[2]; let addr = alloc.borrow_mut().alloc(ctrl_data.len() as u64); - test_state.borrow().memwrite(addr, &ctrl_data); + test_state.borrow().memwrite(addr, ctrl_data); // ctrl_rx_info.switch: u8 let mut data_len = 1; @@ -1750,7 +1744,7 @@ fn virtio_net_abnormal_rx_tx_test() { assert_eq!(size, QUEUE_SIZE_NET); for _ in 0..size { let addr = alloc.borrow_mut().alloc(length); - test_state.borrow().memwrite(addr, &request.as_bytes()); + test_state.borrow().memwrite(addr, request.as_bytes()); vqs[1] .borrow_mut() .add(test_state.clone(), addr, length as u32, false); @@ -1830,7 +1824,7 @@ fn virtio_net_abnormal_rx_tx_test_2() { let request = get_arp_request(id); let length = request.as_bytes().len() as u64; let addr = alloc.borrow_mut().alloc(length); - test_state.borrow().memwrite(addr, &request.as_bytes()); + test_state.borrow().memwrite(addr, request.as_bytes()); vqs[1] .borrow_mut() .add(test_state.clone(), addr, length as u32, false); @@ -1897,7 +1891,7 @@ fn virtio_net_set_abnormal_feature() { test_state.clone(), alloc.clone(), vqs.clone(), - &arp_request.as_bytes(), + arp_request.as_bytes(), true, ); @@ -1943,7 +1937,7 @@ fn virtio_net_send_abnormal_packet() { test_state.clone(), alloc.clone(), vqs.clone(), - &arp_request.as_bytes(), + arp_request.as_bytes(), false, ); @@ -1961,7 +1955,7 @@ fn virtio_net_send_abnormal_packet() { test_state.clone(), alloc.clone(), vqs[1].clone(), - &data_bytes, + data_bytes, ); } @@ -2022,7 +2016,7 @@ fn virtio_net_rx_tx_mq_test() { test_state.clone(), alloc.clone(), vqs[i as usize * 2 + 1].clone(), - &get_arp_request(id + i as u8 * TEST_MAC_ADDR_NUMS).as_bytes(), + get_arp_request(id + i as u8 * TEST_MAC_ADDR_NUMS).as_bytes(), ); } @@ -2079,7 +2073,7 @@ fn virtio_net_abnormal_rx_tx_test_3() { + u64::from(notify_off) * u64::from(net.borrow().notify_off_multiplier); net.borrow() - .setup_virtqueue_intr((i + 1) as u16, alloc.clone(), vq.clone()); + .setup_virtqueue_intr(i + 1, alloc.clone(), vq.clone()); vqs.push(vq); } fill_rx_vq(test_state.clone(), alloc.clone(), vqs[0].clone()); @@ -2091,7 +2085,7 @@ fn virtio_net_abnormal_rx_tx_test_3() { let request = get_arp_request(id); let length = request.as_bytes().len() as u64; let addr = alloc.borrow_mut().alloc(length); - test_state.borrow().memwrite(addr, &request.as_bytes()); + test_state.borrow().memwrite(addr, request.as_bytes()); vqs[1] .borrow_mut() .add(test_state.clone(), addr, length as u32, false); diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 60239ac8a..831d62886 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -74,7 +74,7 @@ fn init_demo_dev(cfg: DemoDev, dev_num: u8) -> (Rc>, Rc = "-D /tmp/oscar.log".split(' ').collect(); demo_dev_args.append(&mut args); - let demo_str = fmt_demo_deves(cfg.clone(), dev_num); + let demo_str = fmt_demo_deves(cfg, dev_num); args = demo_str[..].split(' ').collect(); demo_dev_args.append(&mut args); @@ -169,7 +169,7 @@ fn build_root_port_args(root_port_nums: u8) -> Vec { if multifunc { addr = bus / 8 + 1; func += 1; - func = func % 8; + func %= 8; } else { addr += 1; func = 0; @@ -287,7 +287,7 @@ fn build_all_device_args( ) -> Vec { let mut device_args: Vec = Vec::new(); let mut root_port_args = build_root_port_args(root_port_nums); - if root_port_args.len() != 0 { + if !root_port_args.is_empty() { device_args.append(&mut root_port_args); } @@ -366,7 +366,7 @@ fn create_machine( .borrow() .pci_bus .borrow() - .pci_auto_bus_scan(root_port_nums as u8); + .pci_auto_bus_scan(root_port_nums); let allocator = machine.borrow().allocator.clone(); (test_state, machine, allocator) @@ -413,7 +413,7 @@ fn tear_down( test_state.borrow_mut().stop(); if let Some(img_paths) = image_paths { - img_paths.iter().enumerate().for_each(|(_i, image_path)| { + img_paths.iter().for_each(|image_path| { cleanup_img(image_path.to_string()); }) } @@ -690,8 +690,8 @@ fn lookup_all_cap_addr(cap_id: u8, pci_dev: TestPciDev) -> Vec { fn get_msix_flag(pci_dev: TestPciDev) -> u16 { let addr = pci_dev.find_capability(PCI_CAP_ID_MSIX, 0); assert_ne!(addr, 0); - let old_value = pci_dev.config_readw(addr + PCI_MSIX_MSG_CTL); - old_value + + pci_dev.config_readw(addr + PCI_MSIX_MSG_CTL) } fn set_msix_enable(pci_dev: TestPciDev) { @@ -730,14 +730,11 @@ fn unmask_msix_global(pci_dev: TestPciDev) { fn mask_msix_vector(pci_dev: TestPciDev, vector: u16) { let offset: u64 = pci_dev.msix_table_off + u64::from(vector * PCI_MSIX_ENTRY_SIZE); - let vector_mask = pci_dev.io_readl( - pci_dev.msix_table_bar, - offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, - ); + let vector_mask = pci_dev.io_readl(pci_dev.msix_table_bar, offset + PCI_MSIX_ENTRY_VECTOR_CTRL); pci_dev.io_writel( pci_dev.msix_table_bar, - offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL, vector_mask | PCI_MSIX_ENTRY_CTRL_MASKBIT, ); } @@ -745,14 +742,12 @@ fn mask_msix_vector(pci_dev: TestPciDev, vector: u16) { fn unmask_msix_vector(pci_dev: TestPciDev, vector: u16) { let offset: u64 = pci_dev.msix_table_off + u64::from(vector * PCI_MSIX_ENTRY_SIZE); - let vector_control = pci_dev.io_readl( - pci_dev.msix_table_bar, - offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, - ); + let vector_control = + pci_dev.io_readl(pci_dev.msix_table_bar, offset + PCI_MSIX_ENTRY_VECTOR_CTRL); pci_dev.io_writel( pci_dev.msix_table_bar, - offset + PCI_MSIX_ENTRY_VECTOR_CTRL as u64, + offset + PCI_MSIX_ENTRY_VECTOR_CTRL, vector_control & !PCI_MSIX_ENTRY_CTRL_MASKBIT, ); } @@ -910,7 +905,7 @@ fn test_pci_device_discovery_001() { validate_config_value_2byte( blk.borrow().pci_dev.pci_bus.clone(), 1, - 1 << 3 | 0, + 1 << 3, PCI_VENDOR_ID, 0xFFFF, 0xFFFF, @@ -952,7 +947,7 @@ fn test_pci_device_discovery_002() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Create a root port whose bdf is 0:2:0. @@ -960,7 +955,7 @@ fn test_pci_device_discovery_002() { machine.clone(), alloc.clone(), 0, - 2 << 3 | 0, + 2 << 3, ))); // Create a block device whose bdf is 1:0:0. @@ -1018,7 +1013,7 @@ fn test_pci_device_discovery_003() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Verify the vendor id for the virtio block device hotplugged. @@ -1069,7 +1064,7 @@ fn test_pci_device_discovery_004() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let blk_id = 0; @@ -1249,7 +1244,7 @@ fn test_pci_type1_config() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); assert_eq!(root_port.rp_dev.config_readb(PCI_PRIMARY_BUS), 0); assert_ne!(root_port.rp_dev.config_readb(PCI_SECONDARY_BUS), 0); @@ -1265,7 +1260,7 @@ fn test_pci_type1_reset() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); let command = root_port.rp_dev.config_readw(PCI_COMMAND); let cmd_memory = command & u16::from(PCI_COMMAND_MEMORY); @@ -1303,9 +1298,8 @@ fn test_out_boundary_config_access() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); let devfn = 1 << 3 | 1; - let addr = machine.borrow().pci_bus.borrow().ecam_alloc_ptr - + u64::from((0 as u32) << 20 | (devfn as u32) << 12 | 0 as u32) - - 1; + let addr = + machine.borrow().pci_bus.borrow().ecam_alloc_ptr + u64::from((devfn as u32) << 12) - 1; let write_value = u16::max_value(); let buf = write_value.to_le_bytes(); @@ -1326,7 +1320,7 @@ fn test_out_size_config_access() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); let vendor_device_id = root_port.rp_dev.config_readl(PCI_VENDOR_ID); let command_status = root_port.rp_dev.config_readl(PCI_COMMAND); @@ -1347,7 +1341,7 @@ fn test_out_boundary_msix_access() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3 | 0); + let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); // Out-of-bounds access to the msix table. let write_value = u32::max_value(); @@ -1608,7 +1602,7 @@ fn test_alloc_abnormal_vector() { let virtqueue = blk .borrow() - .setup_virtqueue(test_state.clone(), alloc.clone(), 0 as u16); + .setup_virtqueue(test_state.clone(), alloc.clone(), 0_u16); blk.borrow() .setup_virtqueue_intr((queue_num + 2) as u16, alloc.clone(), virtqueue.clone()); blk.borrow().set_driver_ok(); @@ -1643,11 +1637,11 @@ fn test_intx_basic() { blk.borrow_mut().set_features_ok(); set_msix_disable(blk.borrow().pci_dev.clone()); - blk.borrow_mut().pci_dev.set_intx_irq_num(1 as u8); + blk.borrow_mut().pci_dev.set_intx_irq_num(1_u8); let virtqueue = blk .borrow() - .setup_virtqueue(test_state.clone(), alloc.clone(), 0 as u16); + .setup_virtqueue(test_state.clone(), alloc.clone(), 0_u16); blk.borrow().set_driver_ok(); let free_head = simple_blk_io_req( @@ -1702,11 +1696,11 @@ fn test_intx_disable() { blk.borrow_mut().set_features_ok(); set_msix_disable(blk.borrow().pci_dev.clone()); - blk.borrow_mut().pci_dev.set_intx_irq_num(1 as u8); + blk.borrow_mut().pci_dev.set_intx_irq_num(1_u8); let virtqueue = blk .borrow() - .setup_virtqueue(test_state.clone(), alloc.clone(), 0 as u16); + .setup_virtqueue(test_state.clone(), alloc.clone(), 0_u16); blk.borrow().set_driver_ok(); // Disable INTx. @@ -1784,7 +1778,7 @@ fn test_pci_hotplug_001() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Hotplug a block device whose id is 1 and bdf is 1:0:0. @@ -1825,7 +1819,7 @@ fn test_pci_hotplug_002() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Create a root port whose bdf is 0:2:0. @@ -1833,7 +1827,7 @@ fn test_pci_hotplug_002() { machine.clone(), alloc.clone(), 0, - 2 << 3 | 0, + 2 << 3, ))); // Hotplug a block device whose id is 1 and bdf is 1:0:0. @@ -1971,11 +1965,11 @@ fn test_pci_hotplug_007() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); set_msix_disable(root_port.borrow().rp_dev.clone()); - root_port.borrow_mut().rp_dev.set_intx_irq_num(1 as u8); + root_port.borrow_mut().rp_dev.set_intx_irq_num(1_u8); // Hotplug a block device whose id is 1 and bdf is 1:0:0. let bus = 1; @@ -2039,7 +2033,7 @@ fn test_pci_hotunplug_001() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Create a block device whose bdf is 1:0:0. @@ -2080,7 +2074,7 @@ fn test_pci_hotunplug_003() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Create a block device whose bdf is 1:0:0. @@ -2163,7 +2157,7 @@ fn test_pci_hotunplug_004() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Create root port whose bdf is 0:2:0. @@ -2171,7 +2165,7 @@ fn test_pci_hotunplug_004() { machine.clone(), alloc.clone(), 0, - 2 << 3 | 0, + 2 << 3, ))); // Create a block device whose bdf is 1:0:0. @@ -2248,7 +2242,7 @@ fn test_pci_hotunplug_005() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let blk = create_blk(machine.clone(), 1, 0, 0); @@ -2291,7 +2285,7 @@ fn test_pci_hotunplug_007() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); // Create a block device whose bdf is 1:0:0. @@ -2338,11 +2332,11 @@ fn test_pci_hotunplug_008() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); set_msix_disable(root_port.borrow().rp_dev.clone()); - root_port.borrow_mut().rp_dev.set_intx_irq_num(1 as u8); + root_port.borrow_mut().rp_dev.set_intx_irq_num(1_u8); // Create a block device whose bdf is 1:0:0. let blk = create_blk(machine.clone(), 1, 0, 0); @@ -2399,7 +2393,7 @@ fn test_pci_hotplug_combine_001() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let hotplug_blk_id = 0; @@ -2539,7 +2533,7 @@ fn test_pci_hotplug_combine_002() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let hotplug_blk_id = 0; @@ -2629,7 +2623,7 @@ fn test_pci_hotplug_combine_003() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let hotunplug_blk_id = 0; @@ -2695,7 +2689,7 @@ fn test_pci_root_port_exp_cap() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let cap_exp_addr = root_port.borrow().rp_dev.find_capability(PCI_CAP_ID_EXP, 0); @@ -2987,7 +2981,7 @@ fn test_pci_combine_002() { machine.clone(), alloc.clone(), 0, - 1 << 3 | 0, + 1 << 3, ))); let blk = Rc::new(RefCell::new(TestVirtioPciDev::new( machine.borrow().pci_bus.clone(), diff --git a/tests/mod_test/tests/pvpanic_test.rs b/tests/mod_test/tests/pvpanic_test.rs index 03dfc6787..01583a830 100644 --- a/tests/mod_test/tests/pvpanic_test.rs +++ b/tests/mod_test/tests/pvpanic_test.rs @@ -59,7 +59,7 @@ impl PvPanicDevCfg { test_machine_args.append(&mut args); } - let pvpanic_str = fmt_pvpanic_deves(self.clone()); + let pvpanic_str = fmt_pvpanic_deves(*self); args = pvpanic_str[..].split(' ').collect(); test_machine_args.append(&mut args); diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 5ec46385b..443ce74f4 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -134,11 +134,7 @@ impl VirtioScsiTest { use_iothread: iothread, }; - let readonly = if scsi_type == ScsiDeviceType::ScsiHd { - false - } else { - true - }; + let readonly = scsi_type != ScsiDeviceType::ScsiHd; let scsi_devices: Vec = vec![ScsiDeviceConfig { cntlr_id: 0, device_type: scsi_type, @@ -258,7 +254,7 @@ impl VirtioScsiTest { size_of::(), ) }; - *resp = slice[0].clone(); + *resp = slice[0]; if data_in_len > 0 { data_in.append( @@ -440,8 +436,8 @@ impl TestVirtioScsiCmdReq { let mut target_lun = [0_u8; 8]; target_lun[0] = 1; target_lun[1] = target; - target_lun[2] = (lun >> 8) as u8 & 0xff; - target_lun[3] = lun as u8 & 0xff; + target_lun[2] = (lun >> 8) as u8; + target_lun[3] = lun as u8; req.lun = target_lun; req.cdb = cdb; @@ -1135,7 +1131,7 @@ fn scsi_cd_basic_test() { target, lun, data_out: None, - data_in_length: TEST_SCSI_SENSE_LEN as u32, + data_in_length: TEST_SCSI_SENSE_LEN, expect_response: VIRTIO_SCSI_S_OK, expect_status: GOOD, expect_result_data: None, @@ -2340,7 +2336,7 @@ fn send_cd_command_to_hd_test() { fn wrong_io_test() { let target = 0xff; let lun = 0xff; - let size = 1 * 1024; // Disk size: 1K. + let size = 1024; // Disk size: 1K. let mut vst = VirtioScsiTest::testcase_start_with_config(ScsiDeviceType::ScsiHd, target, lun, size, true); diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index 8336cfd9c..0efbc5295 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -111,7 +111,7 @@ impl SerialTest { fn get_pty_path(&mut self) -> String { let ret = self.state.borrow().qmp("{\"execute\": \"query-chardev\"}"); - if (*ret.get("return").unwrap()).as_array().unwrap().len() != 0 + if !(*ret.get("return").unwrap()).as_array().unwrap().is_empty() && (*ret.get("return").unwrap())[0].get("filename").is_some() { let filename = (*ret.get("return").unwrap())[0] @@ -120,9 +120,9 @@ impl SerialTest { .to_string() .replace('"', ""); let mut file_path: Vec<&str> = filename.split("pty:").collect(); - return file_path.pop().unwrap().to_string(); + file_path.pop().unwrap().to_string() } else { - return String::from(""); + String::from("") } } @@ -315,7 +315,7 @@ impl SerialTest { server: _, nowait: _, } => { - stream = self.connect_socket_host(&path); + stream = self.connect_socket_host(path); } } @@ -355,9 +355,9 @@ impl SerialTest { let result = match port.chardev_type { ChardevType::Pty => { let output = self.connect_pty_host(true); - output.unwrap().write(&test_data.as_bytes()) + output.unwrap().write(test_data.as_bytes()) } - _ => stream.as_ref().unwrap().write(&test_data.as_bytes()), + _ => stream.as_ref().unwrap().write(test_data.as_bytes()), }; match result { Ok(_num) => { diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 7773200e3..4d6f41da0 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -234,7 +234,7 @@ fn qmp_plug_camera(test_state: &Rc>, id: &str, camdev: &str) let test_state = test_state.borrow_mut(); let cmd = r#"{"execute": "device_add", "arguments": {"id": "ID", "driver": "usb-camera", "cameradev": "CAMDEV"}}"#; let cmd = cmd.replace("ID", id); - let cmd = cmd.replace("CAMDEV", &camdev); + let cmd = cmd.replace("CAMDEV", camdev); test_state.qmp(&cmd) } diff --git a/tests/mod_test/tests/usb_storage_test.rs b/tests/mod_test/tests/usb_storage_test.rs index 54e35e2c9..38a45a7c1 100644 --- a/tests/mod_test/tests/usb_storage_test.rs +++ b/tests/mod_test/tests/usb_storage_test.rs @@ -104,10 +104,10 @@ fn data_phase( ) { let mut iovecs = Vec::new(); let ptr = guest_allocator.alloc(buf.len() as u64); - let iovec = TestIovec::new(ptr, buf.len() as usize, false); + let iovec = TestIovec::new(ptr, buf.len(), false); if !to_host { - xhci.mem_write(ptr, &buf); + xhci.mem_write(ptr, buf); } iovecs.push(iovec); diff --git a/tests/mod_test/tests/usb_test.rs b/tests/mod_test/tests/usb_test.rs index a87139261..8a5f81140 100644 --- a/tests/mod_test/tests/usb_test.rs +++ b/tests/mod_test/tests/usb_test.rs @@ -544,7 +544,7 @@ fn test_xhci_keyboard_reorder() { let buf = xhci.get_transfer_data_indirect(evt.ptr, HID_KEYBOARD_LEN); assert_eq!(buf, [0, 0, 30, 31, 32, 33, 0, 0]); // 1 2 3 4 Up - let key_list = vec![ + let key_list = [ KEYCODE_NUM1, KEYCODE_NUM1 + 1, KEYCODE_NUM1 + 2, @@ -736,7 +736,7 @@ fn test_xhci_keyboard_invalid_value() { xhci.queue_trb(slot_id, HID_DEVICE_ENDPOINT_ID, &mut trb); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); // NOTE: no HCE, only primary interrupter supported now. - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE != USB_STS_HCE); test_state.borrow_mut().stop(); @@ -845,7 +845,7 @@ fn test_xhci_keyboard_over_transfer_ring() { xhci.queue_link_trb(slot_id, HID_DEVICE_ENDPOINT_ID, ptr, false); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); // Host Controller Error - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE == USB_STS_HCE); xhci.reset_controller(true); @@ -856,7 +856,7 @@ fn test_xhci_keyboard_over_transfer_ring() { xhci.queue_td_by_iovec(slot_id, HID_DEVICE_ENDPOINT_ID, &mut iovecs, false); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); // Host Controller Error - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE == USB_STS_HCE); xhci.reset_controller(true); @@ -959,35 +959,35 @@ fn test_xhci_keyboard_controller_init_invalid_register() { // Case 3: write invalid slot. xhci.pci_dev.io_writel( xhci.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG, 0xffff, ); let config = xhci.pci_dev.io_readl( xhci.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_CONFIG, ); assert_ne!(config, 0xffff); // Case 4: invalid oper xhci.pci_dev.io_writel( xhci.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS, 0xffff, ); let status = xhci.pci_dev.io_readl( xhci.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_USBSTS, ); assert_ne!(status, 0xffff); // Device Notify Control xhci.pci_dev.io_writel( xhci.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DNCTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DNCTRL, 0x12345, ); let ndctrl = xhci.pci_dev.io_readl( xhci.bar_addr, - u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DNCTRL as u64, + u64::from(XHCI_PCI_OPER_OFFSET) + XHCI_OPER_REG_DNCTRL, ); assert_eq!(ndctrl, 0x12345 & XHCI_OPER_NE_MASK); // invalid port offset. @@ -1084,7 +1084,7 @@ fn test_xhci_keyboard_controller_init_miss_step() { xhci.enable_slot(); assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); // Host Controller Error - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE == USB_STS_HCE); xhci.reset_controller(false); @@ -1108,7 +1108,7 @@ fn test_xhci_keyboard_controller_init_miss_step() { xhci.address_device(slot_id, false, port_id); assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); // Host Controller Error - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE == USB_STS_HCE); xhci.reset_controller(false); @@ -1330,7 +1330,7 @@ fn test_xhci_keyboard_over_command_ring() { xhci.queue_link_trb(0, 0, ptr, false); xhci.doorbell_write(0, 0); // Host Controller Error - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE == USB_STS_HCE); xhci.reset_controller(true); @@ -1766,7 +1766,7 @@ fn test_xhci_keyboard_device_init_reset_device() { let slot_id = evt.get_slot_id(); // Case 1: reset after enable slot. xhci.reset_device(slot_id); - let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS as u64); + let status = xhci.oper_regs_read(XHCI_OPER_REG_USBSTS); assert!(status & USB_STS_HCE == USB_STS_HCE); xhci.reset_controller(true); @@ -2040,9 +2040,9 @@ fn test_xhci_tablet_basic() { [ i as u8 % 3, (i * 10) as u8, - (i * 10 >> 8) as u8, + ((i * 10) >> 8) as u8, (i * 20) as u8, - (i * 20 >> 8) as u8, + ((i * 20) >> 8) as u8, 0, 0 ] @@ -2053,9 +2053,9 @@ fn test_xhci_tablet_basic() { [ 0, (i * 10) as u8, - (i * 10 >> 8) as u8, + ((i * 10) >> 8) as u8, (i * 20) as u8, - (i * 20 >> 8) as u8, + ((i * 20) >> 8) as u8, 0, 0 ] @@ -2433,11 +2433,11 @@ fn test_xhci_disable_interrupt() { // Case: disable USB_CMD_INTE qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0, true); xhci.queue_direct_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); - let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); + let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD); xhci.oper_regs_write(XHCI_OPER_REG_USBCMD, value & !USB_CMD_INTE); xhci.doorbell_write(slot_id, HID_DEVICE_ENDPOINT_ID); assert!(xhci.fetch_event(PRIMARY_INTERRUPTER_ID).is_none()); - let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD as u64); + let value = xhci.oper_regs_read(XHCI_OPER_REG_USBCMD); xhci.oper_regs_write(XHCI_OPER_REG_USBCMD, value | USB_CMD_INTE); let evt = xhci.fetch_event(PRIMARY_INTERRUPTER_ID).unwrap(); assert_eq!(evt.ccode, TRBCCode::Success as u32); @@ -2447,8 +2447,7 @@ fn test_xhci_disable_interrupt() { // Case: disable IMAN_IE qmp_send_pointer_event(test_state.borrow_mut(), 100, 200, 0, true); xhci.queue_direct_td(slot_id, HID_DEVICE_ENDPOINT_ID, HID_POINTER_LEN); - let value = - xhci.interrupter_regs_read(PRIMARY_INTERRUPTER_ID as u64, XHCI_INTR_REG_IMAN as u64); + let value = xhci.interrupter_regs_read(PRIMARY_INTERRUPTER_ID as u64, XHCI_INTR_REG_IMAN); xhci.interrupter_regs_write( PRIMARY_INTERRUPTER_ID as u64, XHCI_INTR_REG_IMAN, diff --git a/tests/mod_test/tests/virtio_gpu_test.rs b/tests/mod_test/tests/virtio_gpu_test.rs index 26d9c1c5f..c9baef238 100644 --- a/tests/mod_test/tests/virtio_gpu_test.rs +++ b/tests/mod_test/tests/virtio_gpu_test.rs @@ -60,19 +60,19 @@ fn image_display_fun() { let (dpy, gpu) = set_up(&gpu_cfg); let image_addr = gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); - let image_byte_0 = vec![0 as u8; 1]; - let image_byte_1 = vec![1 as u8; 1]; - let image_0 = vec![0 as u8; image_size as usize]; + let image_byte_0 = vec![0_u8; 1]; + let image_byte_1 = vec![1_u8; 1]; + let image_0 = vec![0_u8; image_size as usize]; // image with half data 1 - let mut image_half_1 = vec![0 as u8; image_size as usize]; + let mut image_half_1 = vec![0_u8; image_size as usize]; let mut i = 0; while i < image_size / 2 { image_half_1[i as usize] = 1; i += 1; } // image with quarter data1 - let mut image_quarter_1 = vec![0 as u8; image_size as usize]; + let mut image_quarter_1 = vec![0_u8; image_size as usize]; let mut i = 0; while i < image_size / 4 { image_quarter_1[i as usize] = 1; @@ -190,9 +190,9 @@ fn image_display_fun() { #[test] fn cursor_display_fun() { - let image_0: Vec = vec![0 as u8; D_CURSOR_IMG_SIZE as usize]; - let image_1: Vec = vec![1 as u8; D_CURSOR_IMG_SIZE as usize]; - let image_byte_1 = vec![1 as u8; 1]; + let image_0: Vec = vec![0_u8; D_CURSOR_IMG_SIZE as usize]; + let image_1: Vec = vec![1_u8; D_CURSOR_IMG_SIZE as usize]; + let image_byte_1 = vec![1_u8; 1]; let image_size = cal_image_hostmem(D_FMT, D_CURSOR_WIDTH, D_CURSOR_HEIGHT); let image_size = image_size.0.unwrap() as u64; @@ -676,7 +676,7 @@ fn cursor_update_dfx() { gpu.borrow_mut().allocator.borrow_mut().alloc(image_size); let image_empty: Vec = vec![]; - let image_0: Vec = vec![0 as u8; D_CURSOR_IMG_SIZE as usize]; + let image_0: Vec = vec![0_u8; D_CURSOR_IMG_SIZE as usize]; // invalid scanout id assert!(current_curosr_check(&dpy, &image_empty)); diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 2acebf3f8..0c4a12f47 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -128,7 +128,6 @@ fn init_device_step( vqs = blk .borrow_mut() .init_virtqueue(test_state.clone(), alloc.clone(), 1); - () } 8 => { blk.borrow().set_driver_ok(); @@ -140,7 +139,7 @@ fn init_device_step( // Try to send write and read request to StratoVirt, ignore // the interrupt from device. - if vqs.len() > 0 { + if !vqs.is_empty() { let (_, _) = add_request( test_state.clone(), alloc.clone(), @@ -193,7 +192,7 @@ fn check_queue(blk: Rc>, desc: u64, avail: u64, used: let addr = blk .borrow() .pci_dev - .io_readl(bar, common_base as u64 + offset as u64); + .io_readl(bar, common_base + offset as u64); assert_eq!(addr, value as u32); } } @@ -455,11 +454,10 @@ fn virtio_feature_indirect() { let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() - .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); - test_state.borrow().writew( - vqs[0].borrow().desc + offset as u64 + 2, - free_head as u16 + 1, - ); + .writew(vqs[0].borrow().desc + offset, VRING_DESC_F_NEXT); + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset + 2, free_head as u16 + 1); let mut indirect_req = TestVringIndirectDesc::new(); indirect_req.setup(alloc.clone(), test_state.clone(), 2); indirect_req.add_desc(test_state.clone(), req_addr + 8, 520, false); @@ -488,11 +486,10 @@ fn virtio_feature_indirect() { let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() - .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); - test_state.borrow().writew( - vqs[0].borrow().desc + offset as u64 + 2, - free_head as u16 + 1, - ); + .writew(vqs[0].borrow().desc + offset, VRING_DESC_F_NEXT); + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset + 2, free_head as u16 + 1); let mut indirect_req = TestVringIndirectDesc::new(); indirect_req.setup(alloc.clone(), test_state.clone(), 2); indirect_req.add_desc(test_state.clone(), req_addr + 8, 8, false); @@ -598,11 +595,10 @@ fn virtio_feature_indirect_and_event_idx() { let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() - .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); - test_state.borrow().writew( - vqs[0].borrow().desc + offset as u64 + 2, - free_head as u16 + 1, - ); + .writew(vqs[0].borrow().desc + offset, VRING_DESC_F_NEXT); + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset + 2, free_head as u16 + 1); // 2 desc elems in indirect desc table. let mut indirect_req = TestVringIndirectDesc::new(); indirect_req.setup(alloc.clone(), test_state.clone(), 2); @@ -949,7 +945,7 @@ fn virtio_init_device_abnormal_vring_info() { + u64::from(size_of::() as u32 * (3 + queue_size)) + u64::from(VIRTIO_PCI_VRING_ALIGN) - 1) - & !(u64::from(VIRTIO_PCI_VRING_ALIGN) - 1) + 16; + & (!(u64::from(VIRTIO_PCI_VRING_ALIGN) - 1) + 16); vq.borrow_mut().used = used + 16; match err_type { @@ -1599,11 +1595,10 @@ fn virtio_io_abnormal_desc_flags_2() { let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() - .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); - test_state.borrow().writew( - vqs[0].borrow().desc + offset as u64 + 2, - free_head as u16 + 1, - ); + .writew(vqs[0].borrow().desc + offset, VRING_DESC_F_NEXT); + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset + 2, free_head as u16 + 1); let mut indirect_req = TestVringIndirectDesc::new(); indirect_req.setup(alloc.clone(), test_state.clone(), 2); indirect_req.add_desc( @@ -1674,11 +1669,10 @@ fn virtio_io_abnormal_desc_flags_3() { let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() - .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); - test_state.borrow().writew( - vqs[0].borrow().desc + offset as u64 + 2, - free_head as u16 + 1, - ); + .writew(vqs[0].borrow().desc + offset, VRING_DESC_F_NEXT); + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset + 2, free_head as u16 + 1); let mut indirect_req = TestVringIndirectDesc::new(); indirect_req.setup(alloc.clone(), test_state.clone(), 2); indirect_req.add_desc(test_state.clone(), req_addr + 8, 520, false); @@ -1875,11 +1869,10 @@ fn virtio_io_abnormal_indirect_desc_elem_num() { let offset = u64::from(free_head) * VRING_DESC_SIZE + offset_of!(VringDesc, flags) as u64; test_state .borrow() - .writew(vqs[0].borrow().desc + offset as u64, VRING_DESC_F_NEXT); - test_state.borrow().writew( - vqs[0].borrow().desc + offset as u64 + 2, - free_head as u16 + 1, - ); + .writew(vqs[0].borrow().desc + offset, VRING_DESC_F_NEXT); + test_state + .borrow() + .writew(vqs[0].borrow().desc + offset + 2, free_head as u16 + 1); let mut indirect_req = TestVringIndirectDesc::new(); indirect_req.setup(alloc.clone(), test_state.clone(), queue_size as u16 + 1); for i in 0..queue_size { diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 941626a46..99105aab3 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -156,7 +156,7 @@ impl VirtioFsTest { if let Some(member) = reqmember { let member_size = member.len() as u64; let member_addr = self.allocator.borrow_mut().alloc(member_size); - self.state.borrow().memwrite(member_addr, &member); + self.state.borrow().memwrite(member_addr, member); data_entries.push(TestVringDescEntry { data: member_addr, len: member_size as u32, @@ -299,7 +299,7 @@ impl VirtioFsTest { if let Some(path) = absolute_virtiofs_sock { let path_clone = path.clone(); let sock_path = Path::new(&path_clone); - assert_eq!(sock_path.exists(), true); + assert!(sock_path.exists()); self.state.borrow_mut().stop(); } else { self.state.borrow_mut().stop(); @@ -339,10 +339,10 @@ fn fuse_init(fs: &VirtioFsTest) -> (FuseOutHeader, FuseInitOut) { let fuse_out_head = FuseOutHeader::default(); let fuse_init_out = FuseInitOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_init_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_init_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_init_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_init_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -356,15 +356,13 @@ fn fuse_destroy(fs: &VirtioFsTest) -> FuseOutHeader { let fuse_in_head = FuseInHeader::new(len as u32, FUSE_DESTROY, 0, 0, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let (_, _, outheaderaddr, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), + Some(fuse_in_head.as_bytes()), None, - Some(&fuse_out_head.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); - let out_header = read_obj::(fs.state.clone(), outheaderaddr.unwrap()); - - out_header + read_obj::(fs.state.clone(), outheaderaddr.unwrap()) } fn fuse_lookup(fs: &VirtioFsTest, name: String) -> u64 { @@ -375,10 +373,10 @@ fn fuse_lookup(fs: &VirtioFsTest, name: String) -> u64 { let fuse_out_head = FuseOutHeader::default(); let fuse_lookup_out = FuseEntryOut::default(); let (_outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_lookup_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_lookup_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_lookup_out.as_bytes(), ); let entry_out = read_obj::(fs.state.clone(), outbodyaddr); @@ -396,10 +394,10 @@ fn fuse_open(fs: &VirtioFsTest, nodeid: u64) -> u64 { let fuse_out_head = FuseOutHeader::default(); let fuse_open_out = FuseOpenOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_open_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_open_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_open_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_open_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -419,10 +417,10 @@ fn fuse_open_dir(fs: &VirtioFsTest, nodeid: u64) -> u64 { let fuse_out_head = FuseOutHeader::default(); let fuse_open_out = FuseOpenOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_open_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_open_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_open_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_open_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -452,10 +450,10 @@ fn fuse_lseek( let len = (size_of::() + trim_lseek_in_len) as u32; let fuse_in_head = FuseInHeader::new(len, FUSE_LSEEK, 0, nodeid, 0, 0, 0, 0); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_lseek_in.as_bytes()[0..lseek_in_len - trim], - &fuse_out_head.as_bytes(), - &fuse_lseek_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_lseek_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -475,10 +473,10 @@ fn fuse_getattr(fs: &VirtioFsTest, nodeid: u64, fh: u64) -> (FuseOutHeader, Fuse let fuse_out_head = FuseOutHeader::default(); let fuse_getattr_out = FuseAttrOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_getattr_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_getattr_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_getattr_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_getattr_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -568,10 +566,10 @@ fn mkdir_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_mkdir_out = FuseEntryOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_mkdir_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_mkdir_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_mkdir_out.as_bytes(), ); // Check. @@ -582,7 +580,7 @@ fn mkdir_test() { linkpath.push_str("/shared/dir"); let linkpath_clone = linkpath.clone(); let link_path = Path::new(&linkpath_clone); - assert_eq!(link_path.is_dir(), true); + assert!(link_path.is_dir()); // kill process and clean env. fs.testcase_end(virtiofs_test_dir); @@ -619,9 +617,9 @@ fn sync_fun() { }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), - Some(&fuse_fallocate_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_in_head.as_bytes()), + Some(fuse_fallocate_in.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); @@ -654,9 +652,9 @@ fn syncdir_test() { }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), - Some(&fuse_fallocate_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_in_head.as_bytes()), + Some(fuse_fallocate_in.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); @@ -684,9 +682,9 @@ fn invalid_fuse_test() { let fuse_out_head = FuseOutHeader::default(); let fake_fuse_out_body = [0]; let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fake_fuse_in_body, - &fuse_out_head.as_bytes(), + fuse_out_head.as_bytes(), &fake_fuse_out_body, ); @@ -805,9 +803,9 @@ fn ls_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_read_out = [0; DEFAULT_READ_SIZE]; let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_read_in.as_bytes(), - &fuse_out_head.as_bytes(), + fuse_in_head.as_bytes(), + fuse_read_in.as_bytes(), + fuse_out_head.as_bytes(), &fuse_read_out, ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -820,10 +818,10 @@ fn ls_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_forget_out = FuseForgetOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_read_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_forget_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_read_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_forget_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); assert_eq!(out_header.error, 0); @@ -840,9 +838,9 @@ fn ls_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_read_out = [0_u8; DEFAULT_READ_SIZE]; let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_read_in.as_bytes(), - &fuse_out_head.as_bytes(), + fuse_in_head.as_bytes(), + fuse_read_in.as_bytes(), + fuse_out_head.as_bytes(), &fuse_read_out, ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -858,9 +856,9 @@ fn ls_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_read_out = [0_u8; DEFAULT_READ_SIZE]; let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_read_in.as_bytes(), - &fuse_out_head.as_bytes(), + fuse_in_head.as_bytes(), + fuse_read_in.as_bytes(), + fuse_out_head.as_bytes(), &fuse_read_out, ); @@ -881,10 +879,10 @@ fn fuse_setattr( let fuse_out_head = FuseOutHeader::default(); let fuse_attr_out = FuseAttrOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_setattr_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_attr_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_setattr_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_attr_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -1012,10 +1010,10 @@ fn unlink_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_unlink_out = FuseEntryOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_unlink_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_unlink_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_unlink_out.as_bytes(), ); // Check. @@ -1026,7 +1024,7 @@ fn unlink_test() { linkpath.push_str("/shared/testfile"); let linkpath_clone = linkpath.clone(); let link_path = Path::new(&linkpath_clone); - assert_eq!(link_path.exists(), false); + assert!(!link_path.exists()); // kill process and clean env. fs.testcase_end(virtiofs_test_dir); @@ -1060,10 +1058,10 @@ fn rmdir_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_unlink_out = FuseEntryOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_unlink_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_unlink_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_unlink_out.as_bytes(), ); // Check. @@ -1074,7 +1072,7 @@ fn rmdir_test() { linkpath.push_str("/shared/dir"); let linkpath_clone = linkpath.clone(); let link_path = Path::new(&linkpath_clone); - assert_eq!(link_path.exists(), false); + assert!(!link_path.exists()); // kill process and clean env. fs.testcase_end(virtiofs_test_dir); @@ -1100,10 +1098,10 @@ fn symlink_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_init_out = FuseEntryOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_init_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_init_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_init_out.as_bytes(), ); // Check. @@ -1117,7 +1115,7 @@ fn symlink_test() { linkpath.push_str("/shared/link"); let linkpath_clone = linkpath.clone(); let link_path = Path::new(&linkpath_clone); - assert_eq!(link_path.is_symlink(), true); + assert!(link_path.is_symlink()); // Read link let node_id = fuse_lookup(&fs, linkname.clone()); @@ -1126,9 +1124,9 @@ fn symlink_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_read_link_out = [0_u8; 1024]; let (_, _, outheader, outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), + Some(fuse_in_head.as_bytes()), None, - Some(&fuse_out_head.as_bytes()), + Some(fuse_out_head.as_bytes()), Some(&fuse_read_link_out), ); @@ -1168,9 +1166,9 @@ fn fallocate_test() { }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), - Some(&fuse_fallocate_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_in_head.as_bytes()), + Some(fuse_fallocate_in.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); @@ -1214,10 +1212,10 @@ fn posix_file_lock_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_lk_out = FuseLkOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_lk_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_lk_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_lk_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_lk_out.as_bytes(), ); // Check file is unlock. @@ -1244,10 +1242,10 @@ fn posix_file_lock_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_lk_out = FuseLkOut::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_lk_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_lk_out.as_bytes(), + fuse_in_head.as_bytes(), + fuse_lk_in.as_bytes(), + fuse_out_head.as_bytes(), + fuse_lk_out.as_bytes(), ); // check. @@ -1281,10 +1279,10 @@ fn mknod_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_init_out = FuseEntryOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_mknod_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_init_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_init_out.as_bytes(), ); // Check. @@ -1316,9 +1314,9 @@ fn get_xattr(fs: &VirtioFsTest, name: String, nodeid: u64) -> (FuseOutHeader, St let fuse_out_head = FuseOutHeader::default(); let fuse_out = [0_u8; DEFAULT_XATTR_SIZE as usize]; let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_in.as_bytes(), - &fuse_out_head.as_bytes(), + fuse_out_head.as_bytes(), &fuse_out, ); @@ -1343,9 +1341,9 @@ fn flush_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) { }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), - Some(&fuse_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_in_head.as_bytes()), + Some(fuse_in.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); @@ -1361,10 +1359,10 @@ fn write_file(fs: &VirtioFsTest, nodeid: u64, fh: u64, write_buf: String) { let fuse_out_head = FuseOutHeader::default(); let fuse_write_out = FuseWriteOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_write_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_write_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_write_out.as_bytes(), ); let out_header = read_obj::(fs.state.clone(), outheaderaddr); @@ -1384,9 +1382,9 @@ fn release_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) { }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), - Some(&fuse_read_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_in_head.as_bytes()), + Some(fuse_read_in.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); @@ -1407,10 +1405,10 @@ fn create_file(fs: &VirtioFsTest, name: String) -> (FuseOutHeader, FuseCreateOut let fuse_out_head = FuseOutHeader::default(); let fuse_out = FuseCreateOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_out.as_bytes(), ); // Check. @@ -1470,9 +1468,9 @@ fn read_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) -> String { let fuse_out_head = FuseOutHeader::default(); let fuse_out = [0_u8; DEFAULT_READ_SIZE]; let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_in.as_bytes(), - &fuse_out_head.as_bytes(), + fuse_in_head.as_bytes(), + fuse_in.as_bytes(), + fuse_out_head.as_bytes(), &fuse_out, ); @@ -1480,9 +1478,8 @@ fn read_file(fs: &VirtioFsTest, nodeid: u64, fh: u64) -> String { let out_header = read_obj::(fs.state.clone(), outheaderaddr); assert_eq!(out_header.error, 0); let fuse_read_out = fs.state.borrow().memread(outbodyaddr, 5); - let str = String::from_utf8(fuse_read_out).unwrap(); - str + String::from_utf8(fuse_read_out).unwrap() } #[test] @@ -1537,9 +1534,9 @@ fn rename_test() { let fuse_in_head = FuseInHeader::new(len, FUSE_RENAME, 0, PARENT_NODEID, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), + Some(fuse_in_head.as_bytes()), Some(&fuse_rename_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); @@ -1575,10 +1572,10 @@ fn link_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_entry_out = FuseEntryOut::default(); let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_rename_in.as_bytes(), - &fuse_out_head.as_bytes(), - &fuse_entry_out.as_bytes(), + fuse_out_head.as_bytes(), + fuse_entry_out.as_bytes(), ); // Check. @@ -1607,10 +1604,10 @@ fn statfs_test() { let fuse_out_head = FuseOutHeader::default(); let fuse_statfs_out = FuseKstatfs::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), + Some(fuse_in_head.as_bytes()), None, - Some(&fuse_out_head.as_bytes()), - Some(&fuse_statfs_out.as_bytes()), + Some(fuse_out_head.as_bytes()), + Some(fuse_statfs_out.as_bytes()), ); // Check. @@ -1640,9 +1637,9 @@ fn virtio_fs_fuse_ioctl_test() { let fuse_in_head = FuseInHeader::new(len, FUSE_IOCTL, 0, nodeid, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &[0], - &fuse_out_head.as_bytes(), + fuse_out_head.as_bytes(), &[0], ); @@ -1670,9 +1667,9 @@ fn virtio_fs_fuse_abnormal_test() { let fuse_out_head = FuseOutHeader::default(); let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &[0], - &fuse_out_head.as_bytes(), + fuse_out_head.as_bytes(), &[0], ); @@ -1716,15 +1713,13 @@ fn fuse_setxattr(fs: &VirtioFsTest, name: String, value: String, nodeid: u64) -> }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), + Some(fuse_in_head.as_bytes()), Some(&fuse_setxattr_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); - let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); - - out_header + read_obj::(fs.state.clone(), outheader.unwrap()) } fn fuse_removexattr(fs: &VirtioFsTest, name: String, nodeid: u64) -> FuseOutHeader { @@ -1733,14 +1728,13 @@ fn fuse_removexattr(fs: &VirtioFsTest, name: String, nodeid: u64) -> FuseOutHead let fuse_removexattr_in = FuseRemoveXattrIn { name }; let fuse_out_head = FuseOutHeader::default(); let (_, _, outheader, _outbodyaddr) = fs.do_virtio_request( - Some(&fuse_in_head.as_bytes()), + Some(fuse_in_head.as_bytes()), Some(&fuse_removexattr_in.as_bytes()), - Some(&fuse_out_head.as_bytes()), + Some(fuse_out_head.as_bytes()), None, ); - let out_header = read_obj::(fs.state.clone(), outheader.unwrap()); - out_header + read_obj::(fs.state.clone(), outheader.unwrap()) } fn fuse_listxattr(fs: &VirtioFsTest, nodeid: u64) -> (FuseOutHeader, u64) { @@ -1755,9 +1749,9 @@ fn fuse_listxattr(fs: &VirtioFsTest, nodeid: u64) -> (FuseOutHeader, u64) { let fuse_out_head = FuseOutHeader::default(); let fuse_out = [0_u8; DEFAULT_XATTR_SIZE as usize]; let (outheaderaddr, outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &fuse_in.as_bytes(), - &fuse_out_head.as_bytes(), + fuse_out_head.as_bytes(), &fuse_out, ); @@ -1917,7 +1911,7 @@ fn fuse_batch_forget(fs: &VirtioFsTest, nodeid: u64, trim: usize) { ] .concat(); let (_, _) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), + fuse_in_head.as_bytes(), &data_bytes[0..data_bytes.len() - trim], &[0], &[0], @@ -2009,9 +2003,9 @@ fn virtio_fs_fuse_setlkw_test() { } let (outheaderaddr, _outbodyaddr) = fs.virtiofs_do_virtio_request( - &fuse_in_head.as_bytes(), - &fuse_lk_in_bytes, - &fuse_out_head.as_bytes(), + fuse_in_head.as_bytes(), + fuse_lk_in_bytes, + fuse_out_head.as_bytes(), &[0], ); diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index f6065c6aa..d84230305 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -77,7 +77,7 @@ fn test_set_area_dirty() { let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client - .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) + .test_update_request(UpdateState::Incremental, 0, 0, 640_u16, 480_u16,) .is_ok()); demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); @@ -93,7 +93,7 @@ fn test_set_area_dirty() { demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); assert!(vnc_client - .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) + .test_update_request(UpdateState::Incremental, 0, 0, 640_u16, 480_u16,) .is_ok()); let res = vnc_client.test_recv_server_data(pf); @@ -110,7 +110,7 @@ fn test_set_area_dirty() { let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client - .test_update_request(UpdateState::Incremental, 0, 0, 640 as u16, 480 as u16,) + .test_update_request(UpdateState::Incremental, 0, 0, 640_u16, 480_u16,) .is_ok()); demo_gpu.borrow_mut().update_image_area(0, 0, 64, 64); demo_gpu.borrow_mut().set_area_dirty(0, 0, 64, 64); @@ -171,7 +171,7 @@ fn test_set_multiple_area_dirty() { demo_gpu.borrow_mut().update_image_area(119, 120, 160, 160); demo_gpu.borrow_mut().set_area_dirty(0, 0, 640, 480); assert!(vnc_client - .test_update_request(UpdateState::NotIncremental, 0, 0, 640 as u16, 480 as u16,) + .test_update_request(UpdateState::NotIncremental, 0, 0, 640_u16, 480_u16,) .is_ok()); let res = vnc_client.test_recv_server_data(pf); @@ -193,7 +193,7 @@ fn test_set_multiple_area_dirty() { demo_gpu.borrow_mut().update_image_area(119, 120, 160, 160); demo_gpu.borrow_mut().set_area_dirty(0, 0, 640, 480); assert!(vnc_client - .test_update_request(UpdateState::NotIncremental, 0, 0, 640 as u16, 480 as u16,) + .test_update_request(UpdateState::NotIncremental, 0, 0, 640_u16, 480_u16,) .is_ok()); let res = vnc_client.test_recv_server_data(pf); @@ -418,7 +418,7 @@ fn test_set_pixel_format() { // Raw + bit_per_pixel=32 + true_color_flag=1. let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); @@ -430,7 +430,7 @@ fn test_set_pixel_format() { // Raw + bit_per_pixel=16 + true_color_flag=1. let pf = RfbPixelFormat::new(16, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); @@ -443,12 +443,12 @@ fn test_set_pixel_format() { // Raw + bit_per_pixel=8 + true_color_flag=0. let pf = RfbPixelFormat::new(8, 8, 0_u8, 0_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); - let res = vnc_client.test_recv_server_data(pf.clone()); + let res = vnc_client.test_recv_server_data(pf); assert!(res.is_ok()); let res = res.unwrap(); assert!(res.contains(&(RfbServerMsg::FramebufferUpdate, EncodingType::EncodingRaw))); @@ -463,7 +463,7 @@ fn test_set_pixel_format() { .is_ok()); assert!(vnc_client.stream_read_to_end().is_ok()); let pf = RfbPixelFormat::new(32, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); @@ -481,7 +481,7 @@ fn test_set_pixel_format() { .is_ok()); assert!(vnc_client.stream_read_to_end().is_ok()); let pf = RfbPixelFormat::new(8, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); @@ -498,7 +498,7 @@ fn test_set_pixel_format() { .is_ok()); assert!(vnc_client.stream_read_to_end().is_ok()); let pf = RfbPixelFormat::new(8, 8, 0_u8, 0_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); assert!(vnc_client .test_update_request(UpdateState::NotIncremental, 0, 0, 2560, 2048) .is_ok()); @@ -715,9 +715,7 @@ fn test_rfb_version_abnormal(test_state: Rc>, port: u16) -> R assert!(vnc_client.read_msg(&mut buf, 12).is_ok()); assert_eq!(buf[..12].to_vec(), "RFB 003.008\n".as_bytes().to_vec()); println!("Client Rfb version: RFB 003.010"); - assert!(vnc_client - .write_msg(&"RFB 003.010\n".as_bytes().to_vec()) - .is_ok()); + assert!(vnc_client.write_msg("RFB 003.010\n".as_bytes()).is_ok()); buf.drain(..12); // VNC server closed connection. let res = vnc_client.epoll_wait(EventSet::READ_HANG_UP); @@ -738,9 +736,7 @@ fn test_unsupported_sec_type(test_state: Rc>, port: u16) -> R println!("Connect to server."); assert!(vnc_client.read_msg(&mut buf, 12).is_ok()); assert_eq!(buf[..12].to_vec(), "RFB 003.008\n".as_bytes().to_vec()); - assert!(vnc_client - .write_msg(&"RFB 003.008\n".as_bytes().to_vec()) - .is_ok()); + assert!(vnc_client.write_msg("RFB 003.008\n".as_bytes()).is_ok()); buf.drain(..12); // Step 2: Auth num is 1. @@ -751,7 +747,7 @@ fn test_unsupported_sec_type(test_state: Rc>, port: u16) -> R assert!(vnc_client.read_msg(&mut buf, auth_num as usize).is_ok()); buf.drain(..auth_num as usize); assert!(vnc_client - .write_msg(&(TestAuthType::Invalid as u8).to_be_bytes().to_vec()) + .write_msg((TestAuthType::Invalid as u8).to_be_bytes().as_ref()) .is_ok()); // VNC server close the connection. let res = vnc_client.epoll_wait(EventSet::READ_HANG_UP); @@ -770,7 +766,7 @@ fn test_set_pixel_format_abnormal(test_state: Rc>, port: u16) let mut vnc_client = create_new_client(test_state, port).unwrap(); assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); let pf = RfbPixelFormat::new(17, 8, 0_u8, 1_u8, 255, 255, 255, 16, 8, 0); - assert!(vnc_client.test_set_pixel_format(pf.clone()).is_ok()); + assert!(vnc_client.test_set_pixel_format(pf).is_ok()); // VNC server close the connection. let res = vnc_client.epoll_wait(EventSet::READ_HANG_UP)?; diff --git a/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs b/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs index b3b14123d..6a279843d 100644 --- a/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs +++ b/tests/mod_test/tests/x86_64/cpu_hotplug_test.rs @@ -40,7 +40,7 @@ fn set_up(cpu: u8, max_cpus: Option) -> TestState { args = cpu_args[..].split(' ').collect(); extra_args.append(&mut args); - let mem_args = format!("-m 512"); + let mem_args = "-m 512".to_string(); args = mem_args[..].split(' ').collect(); extra_args.append(&mut args); @@ -48,11 +48,12 @@ fn set_up(cpu: u8, max_cpus: Option) -> TestState { extra_args.push("root=/dev/vda panic=1"); let uefi_drive = - format!("-drive file=/usr/share/edk2/ovmf/OVMF_CODE.fd,if=pflash,unit=0,readonly=true"); + "-drive file=/usr/share/edk2/ovmf/OVMF_CODE.fd,if=pflash,unit=0,readonly=true".to_string(); args = uefi_drive[..].split(' ').collect(); extra_args.append(&mut args); - let root_device = format!("-device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.1"); + let root_device = + "-device pcie-root-port,port=0x0,addr=0x1.0x0,bus=pcie.0,id=pcie.1".to_string(); args = root_device[..].split(' ').collect(); extra_args.append(&mut args); diff --git a/ui/src/console.rs b/ui/src/console.rs index 8977f1a22..40c02c3fb 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -844,22 +844,22 @@ mod tests { #[test] fn test_console_select() { let con_opts = Arc::new(HwOpts {}); - let dev_name0 = format!("test_device0"); + let dev_name0 = "test_device0".to_string(); let con_0 = console_init(dev_name0, ConsoleType::Graphic, con_opts.clone()); let clone_con = con_0.clone(); assert_eq!( clone_con.unwrap().upgrade().unwrap().lock().unwrap().con_id, 0 ); - let dev_name1 = format!("test_device1"); + let dev_name1 = "test_device1".to_string(); let con_1 = console_init(dev_name1, ConsoleType::Graphic, con_opts.clone()); assert_eq!(con_1.unwrap().upgrade().unwrap().lock().unwrap().con_id, 1); - let dev_name2 = format!("test_device2"); + let dev_name2 = "test_device2".to_string(); let con_2 = console_init(dev_name2, ConsoleType::Graphic, con_opts.clone()); assert_eq!(con_2.unwrap().upgrade().unwrap().lock().unwrap().con_id, 2); assert!(console_close(&con_0).is_ok()); assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(1)); - let dev_name3 = format!("test_device3"); + let dev_name3 = "test_device3".to_string(); let con_3 = console_init(dev_name3, ConsoleType::Graphic, con_opts.clone()); assert_eq!(con_3.unwrap().upgrade().unwrap().lock().unwrap().con_id, 3); assert!(console_select(Some(0)).is_ok()); diff --git a/ui/src/input.rs b/ui/src/input.rs index 59f2b931f..a04f65b59 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -598,7 +598,7 @@ mod tests { assert!(key_event(12, true).is_ok()); assert_eq!(test_kdb.lock().unwrap().keycode, 12); - assert_eq!(test_kdb.lock().unwrap().down, true); + assert!(test_kdb.lock().unwrap().down); // Test point event. assert_eq!(test_mouse.lock().unwrap().button, 0); @@ -642,14 +642,14 @@ mod tests { let keysym_lists: Vec = vec![0x07D0, 0x07E1, 0x0802]; let keycode_lists: Vec = keysym_lists .iter() - .map(|x| *keysym2qkeycode.get(&x).unwrap()) + .map(|x| *keysym2qkeycode.get(x).unwrap()) .collect(); for idx in 0..keysym_lists.len() { let keysym = keycode_lists[idx]; let keycode = keycode_lists[idx]; assert!(do_key_event(true, keysym as i32, keycode).is_ok()); assert_eq!(test_kdb.lock().unwrap().keycode, keycode); - assert_eq!(test_kdb.lock().unwrap().down, true); + assert!(test_kdb.lock().unwrap().down); } let locked_input = INPUTS.lock().unwrap(); diff --git a/ui/src/utils.rs b/ui/src/utils.rs index 7bb7844d3..c61ae8552 100644 --- a/ui/src/utils.rs +++ b/ui/src/utils.rs @@ -182,12 +182,12 @@ mod tests { fn test_buffpool_base() { let mut buffpool = BuffPool::new(); buffpool.set_limit(Some(7)); - buffpool.append_limit((0x12345678 as u32).to_be_bytes().to_vec()); - buffpool.append_limit((0x12 as u8).to_be_bytes().to_vec()); - buffpool.append_limit((0x1234 as u16).to_be_bytes().to_vec()); - assert!(buffpool.len() == 7 as usize); + buffpool.append_limit(0x12345678_u32.to_be_bytes().to_vec()); + buffpool.append_limit(0x12_u8.to_be_bytes().to_vec()); + buffpool.append_limit(0x1234_u16.to_be_bytes().to_vec()); + assert!(buffpool.len() == 7_usize); buffpool.remove_front(1); - assert!(buffpool.len() == 6 as usize); + assert!(buffpool.len() == 6_usize); let mut buf: Vec = vec![0_u8; 4]; buffpool.read_front(&mut buf, 4); assert!(buf == vec![52, 86, 120, 18]); diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index 0bada3d58..d6b31f69d 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -396,8 +396,8 @@ mod tests { let image = create_pixman_image( pixman_format_code_t::PIXMAN_x8r8g8b8, - image_width as i32, - image_height as i32, + image_width, + image_height, image_data.as_ptr() as *mut u32, image_stride, ); @@ -427,8 +427,8 @@ mod tests { let image = create_pixman_image( pixman_format_code_t::PIXMAN_x8r8g8b8, - image_width as i32, - image_height as i32, + image_width, + image_height, image_data.as_ptr() as *mut u32, image_stride, ); @@ -458,8 +458,8 @@ mod tests { let image = create_pixman_image( pixman_format_code_t::PIXMAN_x8r8g8b8, - image_width as i32, - image_height as i32, + image_width, + image_height, image_data.as_ptr() as *mut u32, image_stride, ); diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index ec978d801..cc666f23f 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -18,6 +18,7 @@ mod uring; pub use raw::*; use std::clone::Clone; +use std::fmt::Display; use std::io::Write; use std::os::unix::io::RawFd; use std::sync::atomic::{AtomicI64, AtomicU32, AtomicU64, Ordering}; @@ -80,13 +81,13 @@ impl FromStr for AioEngine { } } -impl ToString for AioEngine { - fn to_string(&self) -> String { - match *self { - AioEngine::Off => "off".to_string(), - AioEngine::Native => "native".to_string(), - AioEngine::IoUring => "io_uring".to_string(), - AioEngine::Threads => "threads".to_string(), +impl Display for AioEngine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AioEngine::Off => write!(f, "off"), + AioEngine::Native => write!(f, "native"), + AioEngine::IoUring => write!(f, "io_uring"), + AioEngine::Threads => write!(f, "threads"), } } } @@ -862,7 +863,7 @@ mod tests { assert!(opcode == OpCode::Preadv || opcode == OpCode::Pwritev); // Init a file with special content. let mut content = vec![0u8; fsize]; - for (index, elem) in content.as_mut_slice().into_iter().enumerate() { + for (index, elem) in content.as_mut_slice().iter_mut().enumerate() { *elem = index as u8; } let tmp_file = TempFile::new().unwrap(); @@ -955,7 +956,7 @@ mod tests { fn test_sync_rw_all_align(opcode: OpCode, direct: bool) { let basic_align = 512; - test_sync_rw(opcode, direct, basic_align << 0); + test_sync_rw(opcode, direct, basic_align); test_sync_rw(opcode, direct, basic_align << 1); test_sync_rw(opcode, direct, basic_align << 2); test_sync_rw(opcode, direct, basic_align << 3); diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index ad7d1fcb1..9ab971698 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -800,7 +800,7 @@ mod tests { arg_parser.output_help(&mut buffer.inner); let help_str = buffer.get_msg_vec(); - let help_msg = help_str.split("\n").collect::>(); + let help_msg = help_str.split('\n').collect::>(); assert_eq!(help_msg[0], "StratoVirt 1.0.0"); assert_eq!(help_msg[1], "Huawei Technologies Co., Ltd"); assert_eq!(help_msg[2], "A light kvm-based hypervisor."); @@ -832,10 +832,10 @@ mod tests { arg.possible_values.as_ref().unwrap(), &vec!["vm1", "vm2", "vm3"] ); - assert_eq!(arg.required, false); - assert_eq!(arg.presented, true); - assert_eq!(arg.hiddable, false); - assert_eq!(arg.can_no_value, false); + assert!(!arg.required); + assert!(arg.presented); + assert!(!arg.hiddable); + assert!(!arg.can_no_value); assert_eq!(arg.value.as_ref().unwrap(), "vm1"); let (help_msg, help_type) = arg.help_message(); diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index fed47e0fb..ae6afeb29 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -434,12 +434,12 @@ mod tests { let mut bitmap = Bitmap::::new(1); assert!(bitmap.set(15).is_ok()); assert!(bitmap.set(16).is_err()); - assert_eq!(bitmap.contain(15).unwrap(), true); + assert!(bitmap.contain(15).unwrap()); assert_eq!(bitmap.count_front_bits(16).unwrap(), 1); assert_eq!(bitmap.count_front_bits(15).unwrap(), 0); assert!(bitmap.change(15).is_ok()); assert!(bitmap.change(16).is_err()); - assert_eq!(bitmap.contain(15).unwrap(), false); + assert!(!bitmap.contain(15).unwrap()); } #[test] @@ -451,25 +451,25 @@ mod tests { bitmap.clear_all(); assert!(bitmap.set_range(65, 10).is_ok()); - assert_eq!(bitmap.contain(64).unwrap(), false); - assert_eq!(bitmap.contain(65).unwrap(), true); - assert_eq!(bitmap.contain(70).unwrap(), true); - assert_eq!(bitmap.contain(74).unwrap(), true); - assert_eq!(bitmap.contain(75).unwrap(), false); + assert!(!bitmap.contain(64).unwrap()); + assert!(bitmap.contain(65).unwrap()); + assert!(bitmap.contain(70).unwrap()); + assert!(bitmap.contain(74).unwrap()); + assert!(!bitmap.contain(75).unwrap()); bitmap.clear_all(); assert!(bitmap.set_range(63, 1).is_ok()); - assert_eq!(bitmap.contain(62).unwrap(), false); - assert_eq!(bitmap.contain(63).unwrap(), true); - assert_eq!(bitmap.contain(64).unwrap(), false); + assert!(!bitmap.contain(62).unwrap()); + assert!(bitmap.contain(63).unwrap()); + assert!(!bitmap.contain(64).unwrap()); bitmap.clear_all(); assert!(bitmap.set_range(63, 66).is_ok()); - assert_eq!(bitmap.contain(62).unwrap(), false); - assert_eq!(bitmap.contain(63).unwrap(), true); - assert_eq!(bitmap.contain(67).unwrap(), true); - assert_eq!(bitmap.contain(128).unwrap(), true); - assert_eq!(bitmap.contain(129).unwrap(), false); + assert!(!bitmap.contain(62).unwrap()); + assert!(bitmap.contain(63).unwrap()); + assert!(bitmap.contain(67).unwrap()); + assert!(bitmap.contain(128).unwrap()); + assert!(!bitmap.contain(129).unwrap()); bitmap.clear_all(); } @@ -483,25 +483,25 @@ mod tests { assert!(bitmap.set_range(0, 256).is_ok()); assert!(bitmap.clear_range(65, 10).is_ok()); - assert_eq!(bitmap.contain(64).unwrap(), true); - assert_eq!(bitmap.contain(65).unwrap(), false); - assert_eq!(bitmap.contain(70).unwrap(), false); - assert_eq!(bitmap.contain(74).unwrap(), false); - assert_eq!(bitmap.contain(75).unwrap(), true); + assert!(bitmap.contain(64).unwrap()); + assert!(!bitmap.contain(65).unwrap()); + assert!(!bitmap.contain(70).unwrap()); + assert!(!bitmap.contain(74).unwrap()); + assert!(bitmap.contain(75).unwrap()); assert!(bitmap.set_range(0, 256).is_ok()); assert!(bitmap.clear_range(63, 1).is_ok()); - assert_eq!(bitmap.contain(62).unwrap(), true); - assert_eq!(bitmap.contain(63).unwrap(), false); - assert_eq!(bitmap.contain(64).unwrap(), true); + assert!(bitmap.contain(62).unwrap()); + assert!(!bitmap.contain(63).unwrap()); + assert!(bitmap.contain(64).unwrap()); assert!(bitmap.set_range(0, 256).is_ok()); assert!(bitmap.clear_range(63, 66).is_ok()); - assert_eq!(bitmap.contain(62).unwrap(), true); - assert_eq!(bitmap.contain(63).unwrap(), false); - assert_eq!(bitmap.contain(67).unwrap(), false); - assert_eq!(bitmap.contain(128).unwrap(), false); - assert_eq!(bitmap.contain(129).unwrap(), true); + assert!(bitmap.contain(62).unwrap()); + assert!(!bitmap.contain(63).unwrap()); + assert!(!bitmap.contain(67).unwrap()); + assert!(!bitmap.contain(128).unwrap()); + assert!(bitmap.contain(129).unwrap()); assert!(bitmap.clear_range(0, 256).is_ok()); } diff --git a/util/src/daemonize.rs b/util/src/daemonize.rs index d62440650..f693abb4e 100644 --- a/util/src/daemonize.rs +++ b/util/src/daemonize.rs @@ -50,6 +50,7 @@ fn create_pid_file(path: &str) -> Result<()> { let mut pid_file: File = OpenOptions::new() .write(true) .create(true) + .truncate(true) .mode(0o600) .open(path)?; write!(pid_file, "{}", pid)?; diff --git a/util/src/logger.rs b/util/src/logger.rs index 1652d83cd..c5f6aec7c 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -193,7 +193,6 @@ fn init_logger_with_env(logfile: Box, logfile_path: String) -> fn open_log_file(path: &str) -> Result { std::fs::OpenOptions::new() .read(false) - .write(true) .append(true) .create(true) .mode(0o640) diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 957fdb4cb..ced2af402 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -675,7 +675,7 @@ impl EventLoopContext { match ppoll(&mut pollfds, time_out_spec, None) { Ok(_) => time_out = Some(Duration::ZERO), - Err(e) if e == Errno::EINTR => time_out = Some(Duration::ZERO), + Err(Errno::EINTR) => time_out = Some(Duration::ZERO), Err(e) => return Err(anyhow!(UtilError::EpollWait(e.into()))), }; } @@ -757,12 +757,9 @@ mod test { impl EventLoopContext { fn check_existence(&self, fd: RawFd) -> Option { let events_map = self.events.read().unwrap(); - match events_map.get(&fd) { - None => { - return None; - } - Some(notifier) => Some(*notifier.status.lock().unwrap() == EventStatus::Alive), - } + events_map + .get(&fd) + .map(|notifier| *notifier.status.lock().unwrap() == EventStatus::Alive) } fn create_event(&mut self) -> i32 { @@ -803,8 +800,7 @@ mod test { let fd1_related = EventFd::new(EFD_NONBLOCK).unwrap(); let handler1 = generate_handler(fd1_related.as_raw_fd()); - let mut handlers = Vec::new(); - handlers.push(handler1); + let handlers = vec![handler1]; let event1 = EventNotifier::new( NotifierOperation::AddShared, fd1.as_raw_fd(), diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index bf193ce33..558f57bfd 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -496,13 +496,13 @@ mod test { #[test] fn round_up_test() { - let result = round_up(10001 as u64, 100 as u64); + let result = round_up(10001_u64, 100_u64); assert_eq!(result, Some(10100)); } #[test] fn round_down_test() { - let result = round_down(10001 as u64, 100 as u64); + let result = round_down(10001_u64, 100_u64); assert_eq!(result, Some(10000)); } diff --git a/util/src/unix.rs b/util/src/unix.rs index ae1b1a412..2640d948d 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -113,7 +113,7 @@ pub fn do_mmap( // SAFETY: The return value is checked. let hva = unsafe { libc::mmap( - std::ptr::null_mut() as *mut libc::c_void, + std::ptr::null_mut(), len as libc::size_t, prot, flags, @@ -418,7 +418,7 @@ impl UnixSock { while !cmsg_ptr.is_null() { // SAFETY: The pointer of cmsg_ptr was created in this function and // can be guaranteed not be null. - let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() }; + let cmsg = unsafe { cmsg_ptr.read_unaligned() }; if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS { // SAFETY: Input parameter is constant. @@ -488,7 +488,7 @@ mod tests { assert_ne!(stream.get_stream_raw_fd(), 0); assert!(listener.accept().is_ok()); - assert_eq!(listener.is_accepted(), true); + assert!(listener.is_accepted()); if sock_path.exists() { fs::remove_file("./test_socket1.sock").unwrap(); diff --git a/util/src/v4l2.rs b/util/src/v4l2.rs index 545e264ea..1e2890502 100644 --- a/util/src/v4l2.rs +++ b/util/src/v4l2.rs @@ -136,7 +136,7 @@ impl V4l2Backend { // 2. buf can be guaranteed not be null. let ret = unsafe { libc::mmap( - std::ptr::null_mut() as *mut libc::c_void, + std::ptr::null_mut(), buf.length as libc::size_t, libc::PROT_WRITE | libc::PROT_READ, libc::MAP_SHARED, diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 19fb5dfa8..aa19e88ae 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1497,10 +1497,10 @@ mod tests { .write_object::(&ele, GuestAddress(0x2000)) .unwrap(); mem_space - .write_object::(&0, GuestAddress(queue_config_inf.avail_ring.0 + 4 as u64)) + .write_object::(&0, GuestAddress(queue_config_inf.avail_ring.0 + 4_u64)) .unwrap(); mem_space - .write_object::(&1, GuestAddress(queue_config_inf.avail_ring.0 + 2 as u64)) + .write_object::(&1, GuestAddress(queue_config_inf.avail_ring.0 + 2_u64)) .unwrap(); assert!(handler.process_balloon_queue(BALLOON_INFLATE_EVENT).is_ok()); @@ -1523,10 +1523,10 @@ mod tests { .write_object::(&ele, GuestAddress(0x3000)) .unwrap(); mem_space - .write_object::(&0, GuestAddress(queue_config_def.avail_ring.0 + 4 as u64)) + .write_object::(&0, GuestAddress(queue_config_def.avail_ring.0 + 4_u64)) .unwrap(); mem_space - .write_object::(&1, GuestAddress(queue_config_def.avail_ring.0 + 2 as u64)) + .write_object::(&1, GuestAddress(queue_config_def.avail_ring.0 + 2_u64)) .unwrap(); assert!(handler.process_balloon_queue(BALLOON_DEFLATE_EVENT).is_ok()); @@ -1556,7 +1556,7 @@ mod tests { let mut queue_evts: Vec> = Vec::new(); for i in 0..QUEUE_NUM_BALLOON as u64 { let mut queue_config_inf = QueueConfig::new(QUEUE_SIZE); - queue_config_inf.desc_table = GuestAddress(12288 * i + 0); + queue_config_inf.desc_table = GuestAddress(12288 * i); queue_config_inf.avail_ring = GuestAddress(12288 * i + 4096); queue_config_inf.used_ring = GuestAddress(12288 * i + 8192); queue_config_inf.ready = true; diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index d64c56fe1..d07fc2bc1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1594,18 +1594,18 @@ mod tests { fn test_serial_num_config() { let serial_num = "fldXlNNdCeqMvoIfEFogBxlL"; let serial_num_arr = serial_num.as_bytes(); - let id_bytes = get_serial_num_config(&serial_num); + let id_bytes = get_serial_num_config(serial_num); assert_eq!(id_bytes[..], serial_num_arr[..20]); assert_eq!(id_bytes.len(), 20); let serial_num = "7681194149"; let serial_num_arr = serial_num.as_bytes(); - let id_bytes = get_serial_num_config(&serial_num); + let id_bytes = get_serial_num_config(serial_num); assert_eq!(id_bytes[..10], serial_num_arr[..]); assert_eq!(id_bytes.len(), 20); let serial_num = ""; - let id_bytes_temp = get_serial_num_config(&serial_num); + let id_bytes_temp = get_serial_num_config(serial_num); assert_eq!(id_bytes_temp[..], [0; 20]); assert_eq!(id_bytes_temp.len(), 20); } @@ -1650,7 +1650,7 @@ mod tests { VirtioInterruptType::Config => VIRTIO_MMIO_INT_CONFIG, VirtioInterruptType::Vring => VIRTIO_MMIO_INT_VRING, }; - interrupt_status.fetch_or(status as u32, Ordering::SeqCst); + interrupt_status.fetch_or(status, Ordering::SeqCst); interrupt_evt .write(1) .with_context(|| VirtioError::EventFdWrite)?; @@ -1709,20 +1709,17 @@ mod tests { next: 2, }; mem_space - .write_object::( - &desc, - GuestAddress(queue_config.desc_table.0 + 16 as u64), - ) + .write_object::(&desc, GuestAddress(queue_config.desc_table.0 + 16_u64)) .unwrap(); // write avail_ring idx mem_space - .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4_u64)) .unwrap(); // write avail_ring id mem_space - .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2_u64)) .unwrap(); // imitating guest OS to send notification. @@ -1740,7 +1737,7 @@ mod tests { // get used_ring data let idx = mem_space - .read_object::(GuestAddress(queue_config.used_ring.0 + 2 as u64)) + .read_object::(GuestAddress(queue_config.used_ring.0 + 2_u64)) .unwrap(); if idx == 1 { break; diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 1145a30f5..1f8fa7a3a 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -1900,9 +1900,9 @@ mod tests { assert_eq!(gpu_cfg.max_outputs, 5); assert_eq!(gpu_cfg.xres, 2048); assert_eq!(gpu_cfg.yres, 800); - assert_eq!(gpu_cfg.edid, false); + assert!(!gpu_cfg.edid); assert_eq!(gpu_cfg.max_hostmem, 268435457); - assert_eq!(gpu_cfg.enable_bar0, true); + assert!(gpu_cfg.enable_bar0); // Test2: Default. let gpu_cmd2 = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0"; @@ -1911,9 +1911,9 @@ mod tests { assert_eq!(gpu_cfg.max_outputs, 1); assert_eq!(gpu_cfg.xres, 1024); assert_eq!(gpu_cfg.yres, 768); - assert_eq!(gpu_cfg.edid, true); + assert!(gpu_cfg.edid); assert_eq!(gpu_cfg.max_hostmem, VIRTIO_GPU_DEFAULT_MAX_HOSTMEM); - assert_eq!(gpu_cfg.enable_bar0, false); + assert!(!gpu_cfg.enable_bar0); // Test3/4: max_outputs is illegal. let gpu_cmd3 = "virtio-gpu-pci,id=gpu_1,bus=pcie.0,addr=0x4.0x0,max_outputs=17"; diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index a7794fe88..1ec3dc366 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -614,7 +614,7 @@ impl NetCtrlHandler { // Write result to the device writable iovec. let status = elem .in_iovec - .get(0) + .first() .with_context(|| "Failed to get device writable iovec")?; self.mem_space.write_object::(&ack, status.addr)?; @@ -1817,12 +1817,12 @@ mod tests { assert_eq!(net.base.device_features, 0); assert_eq!(net.base.driver_features, 0); - assert_eq!(net.taps.is_none(), true); - assert_eq!(net.senders.is_none(), true); - assert_eq!(net.net_cfg.mac.is_none(), true); - assert_eq!(net.netdev_cfg.tap_fds.is_none(), true); + assert!(net.taps.is_none()); + assert!(net.senders.is_none()); + assert!(net.net_cfg.mac.is_none()); + assert!(net.netdev_cfg.tap_fds.is_none()); assert!(net.netdev_cfg.vhost_type().is_none()); - assert_eq!(net.netdev_cfg.vhost_fds.is_none(), true); + assert!(net.netdev_cfg.vhost_fds.is_none()); // test net realize method net.realize().unwrap(); @@ -1850,25 +1850,25 @@ mod tests { let mut data: Vec = vec![0; 10]; let offset: u64 = len + 1; - assert_eq!(net.read_config(offset, &mut data).is_ok(), false); + assert!(net.read_config(offset, &mut data).is_err()); let offset: u64 = len; - assert_eq!(net.read_config(offset, &mut data).is_ok(), false); + assert!(net.read_config(offset, &mut data).is_err()); let offset: u64 = 0; - assert_eq!(net.read_config(offset, &mut data).is_ok(), true); + assert!(net.read_config(offset, &mut data).is_ok()); let offset: u64 = len; let mut data: Vec = vec![0; 1]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), false); + assert!(net.write_config(offset, &mut data).is_err()); let offset: u64 = len - 1; let mut data: Vec = vec![0; 1]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), false); + assert!(net.write_config(offset, &mut data).is_err()); let offset: u64 = 0; let mut data: Vec = vec![0; len as usize]; - assert_eq!(net.write_config(offset, &mut data).is_ok(), false); + assert!(net.write_config(offset, &mut data).is_err()); } #[test] @@ -1879,8 +1879,8 @@ mod tests { // Test create tap with net_fds and host_dev_name. let net_fds = vec![32, 33]; let tap_name = "tap0"; - if let Err(err) = create_tap(Some(&net_fds), Some(&tap_name), 1) { - let err_msg = format!("Failed to create tap, index is 0"); + if let Err(err) = create_tap(Some(&net_fds), Some(tap_name), 1) { + let err_msg = "Failed to create tap, index is 0".to_string(); assert_eq!(err.to_string(), err_msg); } else { assert!(false); @@ -1888,7 +1888,7 @@ mod tests { // Test create tap with empty net_fds. if let Err(err) = create_tap(Some(&vec![]), None, 1) { - let err_msg = format!("Failed to get fd from index 0"); + let err_msg = "Failed to get fd from index 0".to_string(); assert_eq!(err.to_string(), err_msg); } else { assert!(false); @@ -1897,7 +1897,7 @@ mod tests { // Test create tap with tap_name which is not exist. if let Err(err) = create_tap(None, Some("the_tap_is_not_exist"), 1) { let err_msg = - format!("Failed to create tap with name the_tap_is_not_exist, index is 0"); + "Failed to create tap with name the_tap_is_not_exist, index is 0".to_string(); assert_eq!(err.to_string(), err_msg); } else { assert!(false); @@ -1913,14 +1913,14 @@ mod tests { 0x00, 0x00, ]; // It has no vla vid, the packet is filtered. - assert_eq!(ctrl_info.filter_packets(&buf), true); + assert!(ctrl_info.filter_packets(&buf)); // It has valid vlan id, the packet is not filtered. let vid: u16 = 1023; buf[ETHERNET_HDR_LENGTH] = u16::to_be_bytes(vid)[0]; buf[ETHERNET_HDR_LENGTH + 1] = u16::to_be_bytes(vid)[1]; ctrl_info.vlan_map.insert(vid >> 5, 1 << (vid & 0x1f)); - assert_eq!(ctrl_info.filter_packets(&buf), false); + assert!(!ctrl_info.filter_packets(&buf)); } #[test] @@ -1928,12 +1928,12 @@ mod tests { let mut net_config = VirtioNetConfig::default(); // Parsing the normal mac address. let mac = "52:54:00:12:34:56"; - let ret = build_device_config_space(&mut net_config, &mac); + let ret = build_device_config_space(&mut net_config, mac); assert_eq!(ret, 1 << VIRTIO_NET_F_MAC); // Parsing the abnormale mac address. let mac = "52:54:00:12:34:"; - let ret = build_device_config_space(&mut net_config, &mac); + let ret = build_device_config_space(&mut net_config, mac); assert_eq!(ret, 0); } diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index f0fcc4e19..aaad7cb9d 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -624,11 +624,11 @@ mod tests { .unwrap(); // write avail_ring idx mem_space - .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4_u64)) .unwrap(); // write avail_ring idx mem_space - .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2_u64)) .unwrap(); let buffer = vec![1_u8; data_len as usize]; @@ -645,7 +645,7 @@ mod tests { assert_eq!(read_buffer, buffer); let idx = mem_space - .read_object::(GuestAddress(queue_config.used_ring.0 + 2 as u64)) + .read_object::(GuestAddress(queue_config.used_ring.0 + 2_u64)) .unwrap(); assert_eq!(idx, 1); assert_eq!(cloned_interrupt_evt.read().unwrap(), 1); @@ -722,11 +722,11 @@ mod tests { // write avail_ring idx mem_space - .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4_u64)) .unwrap(); // write avail_ring idx mem_space - .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2_u64)) .unwrap(); let mut buffer1 = vec![1_u8; data_len as usize]; @@ -756,7 +756,7 @@ mod tests { assert_eq!(read_buffer, buffer2_check); let idx = mem_space - .read_object::(GuestAddress(queue_config.used_ring.0 + 2 as u64)) + .read_object::(GuestAddress(queue_config.used_ring.0 + 2_u64)) .unwrap(); assert_eq!(idx, 1); assert_eq!(cloned_interrupt_evt.read().unwrap(), 1); diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 6863b60c4..9f6fc01df 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -617,7 +617,7 @@ impl ScsiCtrlQueueHandler { let ctrl_desc = elem .out_iovec - .get(0) + .first() .with_context(|| "Error request in ctrl queue. Empty dataout buf!")?; let ctrl_type = self .mem_space diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 504918616..ae76f87a7 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -1084,13 +1084,13 @@ mod tests { // The offset of configuration that needs to be read exceeds the maximum. let offset = size_of::() as u64; let mut read_data: Vec = vec![0; 8]; - assert_eq!(serial.read_config(offset, &mut read_data).is_ok(), false); + assert!(serial.read_config(offset, &mut read_data).is_err()); // Check the configuration that needs to be read. let offset = 0_u64; let mut read_data: Vec = vec![0; 12]; let expect_data: Vec = vec![0, 0, 0, 0, max_ports, 0, 0, 0, 0, 0, 0, 0]; - assert_eq!(serial.read_config(offset, &mut read_data).is_ok(), true); + assert!(serial.read_config(offset, &mut read_data).is_ok()); assert_eq!(read_data, expect_data); } } diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 255e966d2..7c9d512eb 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -1051,14 +1051,14 @@ mod tests { } fn set_avail_ring_idx(&self, sys_mem: &Arc, idx: u16) -> Result<()> { - let avail_idx_offset = 2 as u64; + let avail_idx_offset = 2_u64; sys_mem .write_object::(&idx, GuestAddress(self.avail_ring.0 + avail_idx_offset))?; Ok(()) } fn set_avail_ring_flags(&self, sys_mem: &Arc, flags: u16) -> Result<()> { - let avail_idx_offset = 0 as u64; + let avail_idx_offset = 0_u64; sys_mem .write_object::(&flags, GuestAddress(self.avail_ring.0 + avail_idx_offset))?; Ok(()) @@ -1134,7 +1134,7 @@ mod tests { } const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; - const QUEUE_SIZE: u16 = 256 as u16; + const QUEUE_SIZE: u16 = 256_u16; fn align(size: u64, alignment: u64) -> u64 { let align_adjust = if size % alignment != 0 { @@ -1142,7 +1142,7 @@ mod tests { } else { 0 }; - (size + align_adjust) as u64 + size + align_adjust } #[test] @@ -1169,28 +1169,28 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the status is not ready queue_config.ready = false; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); queue_config.ready = true; // it is invalid when the size of virtual ring is more than the max size queue_config.size = QUEUE_SIZE + 1; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // it is invalid when the size of virtual ring is zero queue_config.size = 0; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // it is invalid when the size of virtual ring isn't power of 2 queue_config.size = 15; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); } #[test] @@ -1209,39 +1209,39 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of descriptor table is out of bound queue_config.desc_table = - GuestAddress(SYSTEM_SPACE_SIZE - u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + 1 as u64); + GuestAddress(SYSTEM_SPACE_SIZE - u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + 1_u64); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.desc_table = GuestAddress(0); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of avail ring is out of bound queue_config.avail_ring = GuestAddress( SYSTEM_SPACE_SIZE - (VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE)) - + 1 as u64, + + 1_u64, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of used ring is out of bound queue_config.used_ring = GuestAddress( SYSTEM_SPACE_SIZE - (VRING_USED_LEN_EXCEPT_USEDELEM + USEDELEM_LEN * u64::from(QUEUE_SIZE)) - + 1 as u64, + + 1_u64, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN @@ -1250,7 +1250,7 @@ mod tests { 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); } #[test] @@ -1269,31 +1269,31 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of descriptor table is equal to the address of avail ring queue_config.avail_ring = GuestAddress(0); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of descriptor table is overlapped to the address of avail // ring. queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN - 1); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of avail ring is equal to the address of used ring queue_config.used_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN @@ -1302,7 +1302,7 @@ mod tests { 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of avail ring is overlapped to the address of used ring queue_config.used_ring = GuestAddress( @@ -1312,7 +1312,7 @@ mod tests { - 1, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN @@ -1321,7 +1321,7 @@ mod tests { 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); } #[test] @@ -1340,25 +1340,25 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of descriptor table is not aligned to 16 - queue_config.desc_table = GuestAddress(15 as u64); + queue_config.desc_table = GuestAddress(15_u64); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.desc_table = GuestAddress(0); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of avail ring is not aligned to 2 queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + 1); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); // it is invalid when the address of used ring is not aligned to 4 queue_config.used_ring = GuestAddress( @@ -1368,7 +1368,7 @@ mod tests { + 3, ); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), false); + assert!(!queue.is_valid(&sys_space)); // recover the address for valid queue queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN @@ -1377,7 +1377,7 @@ mod tests { 4096, )); let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING).unwrap(); - assert_eq!(queue.is_valid(&sys_space), true); + assert!(queue.is_valid(&sys_space)); } #[test] @@ -1402,7 +1402,7 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); - assert_eq!(vring.is_valid(&sys_space), true); + assert!(vring.is_valid(&sys_space)); // it is ok when the descriptor chain is normal // set the information of index 0 for descriptor @@ -1452,11 +1452,11 @@ mod tests { assert_eq!(elem.index, 0); assert_eq!(elem.desc_num, 3); assert_eq!(elem.out_iovec.len(), 1); - let elem_iov = elem.out_iovec.get(0).unwrap(); + let elem_iov = elem.out_iovec.first().unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x111)); assert_eq!(elem_iov.len, 16); assert_eq!(elem.in_iovec.len(), 2); - let elem_iov = elem.in_iovec.get(0).unwrap(); + let elem_iov = elem.in_iovec.first().unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x222)); assert_eq!(elem_iov.len, 32); let elem_iov = elem.in_iovec.get(1).unwrap(); @@ -1492,7 +1492,7 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); - assert_eq!(vring.is_valid(&sys_space), true); + assert!(vring.is_valid(&sys_space)); // it is ok when the descriptor chain is indirect // set the information for indirect descriptor @@ -1558,14 +1558,14 @@ mod tests { assert_eq!(elem.index, 0); assert_eq!(elem.desc_num, 3); assert_eq!(elem.out_iovec.len(), 2); - let elem_iov = elem.out_iovec.get(0).unwrap(); + let elem_iov = elem.out_iovec.first().unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x444)); assert_eq!(elem_iov.len, 100); let elem_iov = elem.out_iovec.get(1).unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x555)); assert_eq!(elem_iov.len, 200); assert_eq!(elem.in_iovec.len(), 1); - let elem_iov = elem.in_iovec.get(0).unwrap(); + let elem_iov = elem.in_iovec.first().unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x666)); assert_eq!(elem_iov.len, 300); } @@ -1592,7 +1592,7 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); - assert_eq!(vring.is_valid(&sys_space), true); + assert!(vring.is_valid(&sys_space)); // it is error when the idx of avail ring which is equal to next_avail // set 0 to the idx of avail ring which is equal to next_avail @@ -1637,7 +1637,7 @@ mod tests { 0, ) .unwrap(); - if let Ok(_) = vring.pop_avail(&sys_space, features) { + if vring.pop_avail(&sys_space, features).is_ok() { assert!(false); } @@ -1787,7 +1787,7 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); - assert_eq!(vring.is_valid(&sys_space), true); + assert!(vring.is_valid(&sys_space)); // Set the information of index 0 for normal descriptor. vring @@ -1906,7 +1906,7 @@ mod tests { // Two elem for reading. assert_eq!(elem.out_iovec.len(), 2); - let elem_iov = elem.out_iovec.get(0).unwrap(); + let elem_iov = elem.out_iovec.first().unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x111)); assert_eq!(elem_iov.len, 16); let elem_iov = elem.out_iovec.get(1).unwrap(); @@ -1915,7 +1915,7 @@ mod tests { // Two elem for writing. assert_eq!(elem.in_iovec.len(), 2); - let elem_iov = elem.in_iovec.get(0).unwrap(); + let elem_iov = elem.in_iovec.first().unwrap(); assert_eq!(elem_iov.addr, GuestAddress(0x444)); assert_eq!(elem_iov.len, 100); let elem_iov = elem.in_iovec.get(1).unwrap(); @@ -1951,7 +1951,7 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); - assert_eq!(vring.is_valid(&sys_space), true); + assert!(vring.is_valid(&sys_space)); // it is false when the index is more than the size of queue if let Err(err) = vring.add_used(&sys_space, QUEUE_SIZE, 100) { @@ -1995,20 +1995,20 @@ mod tests { queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); - assert_eq!(vring.is_valid(&sys_space), true); + assert!(vring.is_valid(&sys_space)); // it's true when the feature of event idx and no interrupt for the avail ring is closed - let features = 0 as u64; + let features = 0_u64; assert!(vring.set_avail_ring_flags(&sys_space, 0).is_ok()); - assert_eq!(vring.should_notify(&sys_space, features), true); + assert!(vring.should_notify(&sys_space, features)); // it's false when the feature of event idx is closed and the feature of no interrupt for // the avail ring is open - let features = 0 as u64; + let features = 0_u64; assert!(vring .set_avail_ring_flags(&sys_space, VRING_AVAIL_F_NO_INTERRUPT) .is_ok()); - assert_eq!(vring.should_notify(&sys_space, features), false); + assert!(!vring.should_notify(&sys_space, features)); // it's true when the feature of event idx is open and // (new - event_idx - Wrapping(1) < new -old) @@ -2016,20 +2016,20 @@ mod tests { vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 6).is_ok()); // event_idx - assert_eq!(vring.should_notify(&sys_space, features), true); + assert!(vring.should_notify(&sys_space, features)); // it's false when the feature of event idx is open and // (new - event_idx - Wrapping(1) > new - old) vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 1).is_ok()); // event_idx - assert_eq!(vring.should_notify(&sys_space, features), false); + assert!(!vring.should_notify(&sys_space, features)); // it's false when the feature of event idx is open and // (new - event_idx - Wrapping(1) = new -old) vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 4).is_ok()); // event_idx - assert_eq!(vring.should_notify(&sys_space, features), false); + assert!(!vring.should_notify(&sys_space, features)); } } diff --git a/virtio/src/transport/virtio_mmio.rs b/virtio/src/transport/virtio_mmio.rs index f8895221b..eb081e133 100644 --- a/virtio/src/transport/virtio_mmio.rs +++ b/virtio/src/transport/virtio_mmio.rs @@ -625,7 +625,7 @@ mod tests { check_config_space_rw(&self.config_space, offset, data)?; let data_len = data.len(); self.config_space[(offset as usize)..(offset as usize + data_len)] - .copy_from_slice(&data[..]); + .copy_from_slice(data); Ok(()) } @@ -662,7 +662,7 @@ mod tests { fn test_virtio_mmio_device_new() { let (virtio_device, virtio_mmio_device) = virtio_mmio_test_init(); let locked_device = virtio_device.lock().unwrap(); - assert_eq!(locked_device.device_activated(), false); + assert!(!locked_device.device_activated()); assert_eq!( virtio_mmio_device.host_notify_info.events.len(), locked_device.queue_num() @@ -682,34 +682,22 @@ mod tests { // read the register of magic value let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, MAGIC_VALUE_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, MAGIC_VALUE_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), MMIO_MAGIC_VALUE); // read the register of version let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, VERSION_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, VERSION_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), MMIO_VERSION); // read the register of device id let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, DEVICE_ID_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, DEVICE_ID_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), VIRTIO_TYPE_BLOCK); // read the register of vendor id let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, VENDOR_ID_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, VENDOR_ID_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), VENDOR_ID); // read the register of the features @@ -717,18 +705,12 @@ mod tests { let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_hfeatures_sel(0); virtio_device.lock().unwrap().base.device_features = 0x0000_00f8_0000_00fe; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0x0000_00fe); // get high 32bit of the features for device which supports VirtIO Version 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_hfeatures_sel(1); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, DEVICE_FEATURES_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0x0000_00f9); } @@ -741,18 +723,12 @@ mod tests { // for queue_select as 0 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_queue_select(0); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), u32::from(QUEUE_SIZE)); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_queue_select(1); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, QUEUE_NUM_MAX_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), u32::from(QUEUE_SIZE)); // read the register representing the status of queue @@ -764,15 +740,9 @@ mod tests { .unwrap() .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 1); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG)); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut data[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.read(&mut data[..], addr, QUEUE_READY_REG)); assert_eq!(LittleEndian::read_u32(&data[..]), 1); // for queue_select as 1 let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; @@ -781,44 +751,29 @@ mod tests { .lock() .unwrap() .set_device_status(CONFIG_STATUS_FEATURES_OK); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, QUEUE_READY_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); // read the register representing the status of interrupt let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, INTERRUPT_STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, INTERRUPT_STATUS_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device .lock() .unwrap() .set_interrupt_status(0b10_1111); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, INTERRUPT_STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, INTERRUPT_STATUS_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0b10_1111); // read the register representing the status of device let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_device_status(0); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_device_status(5); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, STATUS_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 5); } @@ -829,23 +784,17 @@ mod tests { // read the configuration atomic value let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, CONFIG_GENERATION_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, CONFIG_GENERATION_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 0); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; virtio_device.lock().unwrap().set_config_generation(10); - assert_eq!( - virtio_mmio_device.read(&mut buf[..], addr, CONFIG_GENERATION_REG), - true - ); + assert!(virtio_mmio_device.read(&mut buf[..], addr, CONFIG_GENERATION_REG)); assert_eq!(LittleEndian::read_u32(&buf[..]), 10); // read the unknown register let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!(virtio_mmio_device.read(&mut buf[..], addr, 0xf1), false); - assert_eq!(virtio_mmio_device.read(&mut buf[..], addr, 0x1ff + 1), true); + assert!(!virtio_mmio_device.read(&mut buf[..], addr, 0xf1)); + assert!(virtio_mmio_device.read(&mut buf[..], addr, 0x1ff + 1)); assert_eq!(buf, [0xff, 0xff, 0xff, 0xff]); // read the configuration space of virtio device @@ -859,12 +808,12 @@ mod tests { .copy_from_slice(&result); let mut data: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - assert_eq!(virtio_mmio_device.read(&mut data[..], addr, 0x100), true); + assert!(virtio_mmio_device.read(&mut data[..], addr, 0x100)); assert_eq!(data, result); let mut data: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0]; let result: Vec = vec![9, 10, 11, 12, 13, 14, 15, 16]; - assert_eq!(virtio_mmio_device.read(&mut data[..], addr, 0x108), true); + assert!(virtio_mmio_device.read(&mut data[..], addr, 0x108)); assert_eq!(data, result); } @@ -876,10 +825,7 @@ mod tests { // write the selector for device features let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 2); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DEVICE_FEATURES_SEL_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, DEVICE_FEATURES_SEL_REG)); assert_eq!(virtio_device.lock().unwrap().hfeatures_sel(), 2); // write the device features @@ -889,25 +835,16 @@ mod tests { .lock() .unwrap() .set_device_status(CONFIG_STATUS_FEATURES_OK); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), - false - ); + assert!(!virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG)); virtio_device .lock() .unwrap() .set_device_status(CONFIG_STATUS_FAILED); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), - false - ); + assert!(!virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG)); virtio_device.lock().unwrap().set_device_status( CONFIG_STATUS_FEATURES_OK | CONFIG_STATUS_FAILED | CONFIG_STATUS_DRIVER, ); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), - false - ); + assert!(!virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG)); // it is ok to write the low 32bit of device features virtio_device .lock() @@ -917,10 +854,7 @@ mod tests { virtio_device.lock().unwrap().set_gfeatures_sel(0); LittleEndian::write_u32(&mut buf[..], 0x0000_00fe); virtio_device.lock().unwrap().base.device_features = 0x0000_00fe; - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG)); assert_eq!( virtio_device.lock().unwrap().base.driver_features as u32, 0x0000_00fe @@ -930,35 +864,26 @@ mod tests { virtio_device.lock().unwrap().set_gfeatures_sel(1); LittleEndian::write_u32(&mut buf[..], 0x0000_00ff); virtio_device.lock().unwrap().base.device_features = 0x0000_00ff_0000_0000; - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_REG)); assert_eq!( virtio_device.lock().unwrap().queue_type(), QUEUE_TYPE_PACKED_VRING ); assert_eq!( - virtio_device.lock().unwrap().base.driver_features >> 32 as u32, + virtio_device.lock().unwrap().base.driver_features >> 32_u32, 0x0000_00ff ); // write the selector of driver features let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0x00ff_0000); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_SEL_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, DRIVER_FEATURES_SEL_REG)); assert_eq!(virtio_device.lock().unwrap().gfeatures_sel(), 0x00ff_0000); // write the selector of queue let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0x0000_ff00); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_SEL_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_SEL_REG)); assert_eq!(virtio_device.lock().unwrap().queue_select(), 0x0000_ff00); // write the size of queue @@ -969,10 +894,7 @@ mod tests { .unwrap() .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 128); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_NUM_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_NUM_REG)); if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!(config.size, 128); } else { @@ -993,15 +915,9 @@ mod tests { .unwrap() .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 1); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG)); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut data[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.read(&mut data[..], addr, QUEUE_READY_REG)); assert_eq!(LittleEndian::read_u32(&data[..]), 1); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; @@ -1011,15 +927,9 @@ mod tests { .unwrap() .set_device_status(CONFIG_STATUS_FEATURES_OK); LittleEndian::write_u32(&mut buf[..], 2); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_READY_REG)); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut data[..], addr, QUEUE_READY_REG), - true - ); + assert!(virtio_mmio_device.read(&mut data[..], addr, QUEUE_READY_REG)); assert_eq!(LittleEndian::read_u32(&data[..]), 0); // write the interrupt status @@ -1033,15 +943,9 @@ mod tests { .unwrap() .set_interrupt_status(0b10_1111); LittleEndian::write_u32(&mut buf[..], 0b111); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, INTERRUPT_ACK_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, INTERRUPT_ACK_REG)); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut data[..], addr, INTERRUPT_STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut data[..], addr, INTERRUPT_STATUS_REG)); assert_eq!(LittleEndian::read_u32(&data[..]), 0b10_1000); } @@ -1058,10 +962,7 @@ mod tests { .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xffff_fefe); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_LOW_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_LOW_REG)); if let Ok(config) = virtio_mmio_device.device.lock().unwrap().queue_config() { assert_eq!(config.desc_table.0 as u32, 0xffff_fefe) } else { @@ -1076,10 +977,7 @@ mod tests { .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xfcfc_ffff); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_HIGH_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_DESC_HIGH_REG)); if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!((config.desc_table.0 >> 32) as u32, 0xfcfc_ffff) } else { @@ -1094,10 +992,7 @@ mod tests { .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xfcfc_fafa); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_LOW_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_LOW_REG)); if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!(config.avail_ring.0 as u32, 0xfcfc_fafa) } else { @@ -1112,10 +1007,7 @@ mod tests { .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xecec_fafa); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_HIGH_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_AVAIL_HIGH_REG)); if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!((config.avail_ring.0 >> 32) as u32, 0xecec_fafa) } else { @@ -1130,10 +1022,7 @@ mod tests { .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xacac_fafa); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_LOW_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_LOW_REG)); if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!(config.used_ring.0 as u32, 0xacac_fafa) } else { @@ -1148,10 +1037,7 @@ mod tests { .set_device_status(CONFIG_STATUS_FEATURES_OK); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], 0xcccc_fafa); - assert_eq!( - virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_HIGH_REG), - true - ); + assert!(virtio_mmio_device.write(&buf[..], addr, QUEUE_USED_HIGH_REG)); if let Ok(config) = virtio_device.lock().unwrap().queue_config() { assert_eq!((config.used_ring.0 >> 32) as u32, 0xcccc_fafa) } else { @@ -1165,7 +1051,7 @@ mod tests { } else { 0 }; - (size + align_adjust) as u64 + size + align_adjust } #[test] @@ -1203,13 +1089,10 @@ mod tests { // write the device status let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; LittleEndian::write_u32(&mut buf[..], CONFIG_STATUS_ACKNOWLEDGE); - assert_eq!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG), true); - assert_eq!(virtio_device.lock().unwrap().device_activated(), false); + assert!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG)); + assert!(!virtio_device.lock().unwrap().device_activated()); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut data[..], addr, STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut data[..], addr, STATUS_REG)); assert_eq!(LittleEndian::read_u32(&data[..]), CONFIG_STATUS_ACKNOWLEDGE); let mut buf: Vec = vec![0xff, 0xff, 0xff, 0xff]; @@ -1220,15 +1103,12 @@ mod tests { | CONFIG_STATUS_DRIVER_OK | CONFIG_STATUS_FEATURES_OK, ); - assert_eq!(virtio_device.lock().unwrap().b_active, false); - assert_eq!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG), true); - assert_eq!(virtio_device.lock().unwrap().device_activated(), true); - assert_eq!(virtio_device.lock().unwrap().b_active, true); + assert!(!virtio_device.lock().unwrap().b_active); + assert!(virtio_mmio_device.write(&buf[..], addr, STATUS_REG)); + assert!(virtio_device.lock().unwrap().device_activated()); + assert!(virtio_device.lock().unwrap().b_active); let mut data: Vec = vec![0xff, 0xff, 0xff, 0xff]; - assert_eq!( - virtio_mmio_device.read(&mut data[..], addr, STATUS_REG), - true - ); + assert!(virtio_mmio_device.read(&mut data[..], addr, STATUS_REG)); assert_eq!( LittleEndian::read_u32(&data[..]), CONFIG_STATUS_ACKNOWLEDGE diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 22f69ca2a..d656d2835 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1582,7 +1582,7 @@ mod tests { .unwrap() .virtio_base() .queues_config - .get(0) + .first() .unwrap() .ready ); @@ -1673,7 +1673,7 @@ mod tests { let status = (CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER | CONFIG_STATUS_FEATURES_OK) .as_bytes(); (common_cfg_ops.write)(status, GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_dev.lock().unwrap().device_activated(), false); + assert!(!virtio_dev.lock().unwrap().device_activated()); // Device status is not ok, failed to activate virtio device let status = (CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER @@ -1681,7 +1681,7 @@ mod tests { | CONFIG_STATUS_FEATURES_OK) .as_bytes(); (common_cfg_ops.write)(status, GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_dev.lock().unwrap().device_activated(), false); + assert!(!virtio_dev.lock().unwrap().device_activated()); // Status is ok, virtio device is activated. let status = (CONFIG_STATUS_ACKNOWLEDGE | CONFIG_STATUS_DRIVER @@ -1689,11 +1689,11 @@ mod tests { | CONFIG_STATUS_FEATURES_OK) .as_bytes(); (common_cfg_ops.write)(status, GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_dev.lock().unwrap().device_activated(), true); + assert!(virtio_dev.lock().unwrap().device_activated()); // If device status(not zero) is set to zero, reset the device (common_cfg_ops.write)(0_u32.as_bytes(), GuestAddress(0), COMMON_STATUS_REG); - assert_eq!(virtio_dev.lock().unwrap().device_activated(), false); + assert!(!virtio_dev.lock().unwrap().device_activated()); } #[test] diff --git a/virtio/src/vhost/kernel/net.rs b/virtio/src/vhost/kernel/net.rs index 52dbb85a4..1131bac37 100644 --- a/virtio/src/vhost/kernel/net.rs +++ b/virtio/src/vhost/kernel/net.rs @@ -459,7 +459,7 @@ mod tests { } // without assigned value of tap_fd and vhost_fd, // vhost-net device can be realized successfully. - assert_eq!(vhost_net.realize().is_ok(), true); + assert!(vhost_net.realize().is_ok()); // test for get/set_driver_features vhost_net.base.device_features = 0; @@ -482,22 +482,22 @@ mod tests { let len = vhost_net.config_space.lock().unwrap().as_bytes().len() as u64; let offset: u64 = 0; let data: Vec = vec![1; len as usize]; - assert_eq!(vhost_net.write_config(offset, &data).is_ok(), true); + assert!(vhost_net.write_config(offset, &data).is_ok()); let mut read_data: Vec = vec![0; len as usize]; - assert_eq!(vhost_net.read_config(offset, &mut read_data).is_ok(), true); + assert!(vhost_net.read_config(offset, &mut read_data).is_ok()); assert_ne!(read_data, data); let offset: u64 = 1; let data: Vec = vec![1; len as usize]; - assert_eq!(vhost_net.write_config(offset, &data).is_ok(), true); + assert!(vhost_net.write_config(offset, &data).is_ok()); let offset: u64 = len + 1; let mut read_data: Vec = vec![0; len as usize]; - assert_eq!(vhost_net.read_config(offset, &mut read_data).is_ok(), false); + assert!(vhost_net.read_config(offset, &mut read_data).is_err()); let offset: u64 = len - 1; let mut read_data: Vec = vec![0; len as usize]; - assert_eq!(vhost_net.read_config(offset, &mut read_data).is_ok(), false); + assert!(vhost_net.read_config(offset, &mut read_data).is_err()); } } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 18d3f56bc..8774fbebc 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -417,8 +417,8 @@ mod tests { ..Default::default() }; let sys_mem = address_space_init(); - let vsock = Vsock::new(&vsock_conf, &sys_mem); - vsock + + Vsock::new(&vsock_conf, &sys_mem) } #[test] @@ -473,23 +473,23 @@ mod tests { // test vsock read_config let mut buf: [u8; 8] = [0; 8]; - assert_eq!(vsock.read_config(0, &mut buf).is_ok(), true); + assert!(vsock.read_config(0, &mut buf).is_ok()); let value = LittleEndian::read_u64(&buf); assert_eq!(value, vsock.vsock_cfg.guest_cid); let mut buf: [u8; 4] = [0; 4]; - assert_eq!(vsock.read_config(0, &mut buf).is_ok(), true); + assert!(vsock.read_config(0, &mut buf).is_ok()); let value = LittleEndian::read_u32(&buf); assert_eq!(value, vsock.vsock_cfg.guest_cid as u32); let mut buf: [u8; 4] = [0; 4]; - assert_eq!(vsock.read_config(4, &mut buf).is_ok(), true); + assert!(vsock.read_config(4, &mut buf).is_ok()); let value = LittleEndian::read_u32(&buf); assert_eq!(value, (vsock.vsock_cfg.guest_cid >> 32) as u32); let mut buf: [u8; 4] = [0; 4]; - assert_eq!(vsock.read_config(5, &mut buf).is_err(), true); - assert_eq!(vsock.read_config(3, &mut buf).is_err(), true); + assert!(vsock.read_config(5, &mut buf).is_err()); + assert!(vsock.read_config(3, &mut buf).is_err()); } #[test] @@ -508,12 +508,9 @@ mod tests { // test vsock set_guest_cid let backend = vsock.backend.unwrap(); - assert_eq!(backend.set_guest_cid(3).is_ok(), true); - assert_eq!( - backend.set_guest_cid(u64::from(u32::max_value())).is_ok(), - false - ); - assert_eq!(backend.set_guest_cid(2).is_ok(), false); - assert_eq!(backend.set_guest_cid(0).is_ok(), false); + assert!(backend.set_guest_cid(3).is_ok()); + assert!(backend.set_guest_cid(u64::from(u32::max_value())).is_err()); + assert!(backend.set_guest_cid(2).is_err()); + assert!(backend.set_guest_cid(0).is_err()); } } -- Gitee From 36d4a660a01ee60da12c62607d0bf35c3cdac558 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 16 Aug 2024 14:28:48 +0800 Subject: [PATCH 2018/2187] ohcamera:support multi-cameras To support multi-cameras on OHOS, we use "camera ID" accquired by OH camera API instead of index of supported camera list to specify a target camera. And we record related callback info for every camera, avoiding old camera callback info is covered by new one. Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 114 +++++++++++++++----- tests/mod_test/tests/usb_camera_test.rs | 2 +- util/src/ohos_binding/camera.rs | 50 ++++----- util/src/ohos_binding/hwf_adapter/camera.rs | 19 ++-- 4 files changed, 112 insertions(+), 73 deletions(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 096f24b1d..04ba1b4fc 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::HashMap; +use std::ffi::CStr; use std::sync::RwLock; use anyhow::{bail, Context, Result}; @@ -29,8 +31,8 @@ use trace::trace_scope::Scope; use util::aio::Iovec; use util::ohos_binding::camera::*; -type OhCamCB = RwLock; -static OHCAM_CALLBACK: Lazy = Lazy::new(|| RwLock::new(OhCamCallBack::default())); +type OhCamCB = RwLock>; +static OHCAM_CALLBACKS: Lazy = Lazy::new(|| RwLock::new(HashMap::new())); // In UVC, interval's unit is 100ns. // So, fps * interval / 10_000_000 == 1. @@ -114,8 +116,10 @@ impl OhCameraAsyncScope { #[derive(Clone)] pub struct OhCameraBackend { + // ID for this OhCameraBackend. id: String, - camidx: u8, + // ID of OH camera device. + camid: String, profile_cnt: u8, ctx: OhCamera, fmt_list: Vec, @@ -148,17 +152,20 @@ fn cam_fmt_from_oh(t: i32) -> Result { Ok(fmt) } -impl OhCameraBackend { - pub fn new(id: String, camid: String) -> Result { - let idx = camid.parse::().with_context(|| "Invalid PATH format")?; - let ctx = OhCamera::new(idx as i32)?; +impl Drop for OhCameraBackend { + fn drop(&mut self) { + OHCAM_CALLBACKS.write().unwrap().remove_entry(&self.camid); + } +} - let profile_cnt = ctx.get_fmt_nums(idx as i32)? as u8; +impl OhCameraBackend { + pub fn new(id: String, cam_name: String) -> Result { + let (ctx, profile_cnt) = OhCamera::new(cam_name.clone())?; Ok(OhCameraBackend { id, - camidx: idx, - profile_cnt, + camid: cam_name, + profile_cnt: profile_cnt as u8, ctx, fmt_list: vec![], selected_profile: 0, @@ -193,8 +200,7 @@ impl CameraBackend for OhCameraBackend { } self.selected_profile = fmt.fmt_index - 1; - self.ctx - .set_fmt(self.camidx as i32, self.selected_profile as i32)?; + self.ctx.set_fmt(i32::from(self.selected_profile))?; return Ok(()); } } @@ -213,7 +219,9 @@ impl CameraBackend for OhCameraBackend { fn video_stream_off(&mut self) -> Result<()> { self.ctx.stop_stream(); - OHCAM_CALLBACK.write().unwrap().clear_buffer(); + if let Some(cb) = OHCAM_CALLBACKS.write().unwrap().get_mut(&self.camid) { + cb.clear_buffer(); + } self.stream_on = false; #[cfg(any( feature = "trace_to_logger", @@ -228,7 +236,7 @@ impl CameraBackend for OhCameraBackend { let mut fmt_list: Vec = Vec::new(); for idx in 0..self.profile_cnt { - match self.ctx.get_profile(self.camidx as i32, idx as i32) { + match self.ctx.get_profile(idx as i32) { Ok((fmt, width, height, fps)) => { if !FRAME_FORMAT_WHITELIST.iter().any(|&x| x == fmt) || !RESOLUTION_WHITELIST.iter().any(|&x| x == (width, height)) @@ -257,8 +265,12 @@ impl CameraBackend for OhCameraBackend { } fn reset(&mut self) { - OHCAM_CALLBACK.write().unwrap().clear_buffer(); - self.ctx.reset_camera(); + if let Some(cb) = OHCAM_CALLBACKS.write().unwrap().get_mut(&self.camid) { + cb.clear_buffer(); + } + if let Err(e) = self.ctx.reset_camera(self.camid.clone()) { + error!("OHCAM: reset failed, err: {e}"); + } #[cfg(any( feature = "trace_to_logger", feature = "trace_to_ftrace", @@ -300,7 +312,10 @@ impl CameraBackend for OhCameraBackend { } fn get_frame_size(&self) -> usize { - OHCAM_CALLBACK.read().unwrap().get_buffer().1 as usize + if let Some(cb) = OHCAM_CALLBACKS.read().unwrap().get(&self.camid) { + return cb.get_buffer().1 as usize; + } + 0 } fn next_frame(&mut self) -> Result<()> { @@ -311,12 +326,20 @@ impl CameraBackend for OhCameraBackend { ))] self.async_scope.start(); self.ctx.next_frame(); - OHCAM_CALLBACK.write().unwrap().clear_buffer(); + if let Some(cb) = OHCAM_CALLBACKS.write().unwrap().get_mut(&self.camid) { + cb.clear_buffer(); + } Ok(()) } fn get_frame(&self, iovecs: &[Iovec], frame_offset: usize, len: usize) -> Result { - let (src, src_len) = OHCAM_CALLBACK.read().unwrap().get_buffer(); + let (src, src_len) = OHCAM_CALLBACKS + .read() + .unwrap() + .get(&self.camid) + .with_context(|| "Invalid camid in callback table")? + .get_buffer(); + if src_len == 0 { bail!("Invalid frame src_len {}", src_len); } @@ -344,11 +367,21 @@ impl CameraBackend for OhCameraBackend { } fn register_notify_cb(&mut self, cb: CameraNotifyCallback) { - OHCAM_CALLBACK.write().unwrap().set_notify_cb(cb); + OHCAM_CALLBACKS + .write() + .unwrap() + .entry(self.camid.clone()) + .or_insert(OhCamCallBack::default()) + .set_notify_cb(cb); } fn register_broken_cb(&mut self, cb: CameraBrokenCallback) { - OHCAM_CALLBACK.write().unwrap().set_broken_cb(cb); + OHCAM_CALLBACKS + .write() + .unwrap() + .entry(self.camid.clone()) + .or_insert(OhCamCallBack::default()) + .set_broken_cb(cb); } fn pause(&mut self, paused: bool) { @@ -376,16 +409,39 @@ impl CameraBackend for OhCameraBackend { } } +fn cstr_to_string(src: *const u8) -> Result { + if src.is_null() { + bail!("cstr_to_string: src is null"); + } + // SAFETY: we promise that 'src' ends with "null" symbol. + let src_cstr = unsafe { CStr::from_ptr(src) }; + let target_string = src_cstr + .to_str() + .with_context(|| "cstr_to_string: failed to transfer camid")? + .to_owned(); + + Ok(target_string) +} + // SAFETY: use RW lock to ensure the security of resources. -unsafe extern "C" fn on_buffer_available(src_buffer: u64, length: i32) { - OHCAM_CALLBACK - .write() - .unwrap() - .set_buffer(src_buffer, length); - OHCAM_CALLBACK.read().unwrap().notify(); +unsafe extern "C" fn on_buffer_available(src_buffer: u64, length: i32, camid: *const u8) { + let cam = cstr_to_string(camid).unwrap_or_else(|e| { + error!("{e}"); + "".to_string() + }); + if let Some(cb) = OHCAM_CALLBACKS.write().unwrap().get_mut(&cam) { + cb.set_buffer(src_buffer, length); + cb.notify(); + } } // SAFETY: use RW lock to ensure the security of resources. -unsafe extern "C" fn on_broken() { - OHCAM_CALLBACK.read().unwrap().broken(); +unsafe extern "C" fn on_broken(camid: *const u8) { + let cam = cstr_to_string(camid).unwrap_or_else(|e| { + error!("{e}"); + "".to_string() + }); + if let Some(cb) = OHCAM_CALLBACKS.read().unwrap().get(&cam) { + cb.broken(); + } } diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 72539614b..fa32e7ea3 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -563,7 +563,7 @@ fn test_xhci_camera_hotplug_invalid() { #[cfg(not(target_env = "ohos"))] assert_eq!(desc, "Failed to open v4l2 backend /tmp/not-existed."); #[cfg(target_env = "ohos")] - assert_eq!(desc, "Invalid PATH format"); + assert_eq!(desc, "OH Camera: failed to init cameras"); // Invalid device id. let value = qmp_unplug_camera(&test_state.clone(), "usbcam0"); let desc = value["error"]["desc"].as_str().unwrap().to_string(); diff --git a/util/src/ohos_binding/camera.rs b/util/src/ohos_binding/camera.rs index e481e887d..eadeb0d1e 100644 --- a/util/src/ohos_binding/camera.rs +++ b/util/src/ohos_binding/camera.rs @@ -10,11 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::ffi::CString; use std::os::raw::{c_int, c_void}; use std::ptr; use std::sync::Arc; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use super::hwf_adapter::camera::{ BrokenProcessFn, BufferProcessFn, CamFuncTable, OhCameraCtx, ProfileRecorder, @@ -45,42 +46,30 @@ impl Drop for OhCamera { } impl OhCamera { - pub fn new(idx: i32) -> Result { + pub fn new(id: String) -> Result<(OhCamera, i32)> { let capi = hwf_adapter_camera_api(); // SAFETY: We call related API sequentially for specified ctx. let mut ctx = unsafe { (capi.create_ctx)() }; if ctx.is_null() { bail!("OH Camera: failed to create camera ctx"); } + let id_c = CString::new(id).with_context(|| "failed to create CString id")?; + let fmt_cnt; // SAFETY: We call related API sequentially for specified ctx. unsafe { - let n = (capi.init_cameras)(ctx); + let n = (capi.init_camera)(ctx, id_c.as_ptr()); if n < 0 { (capi.destroy_ctx)(ptr::addr_of_mut!(ctx)); bail!("OH Camera: failed to init cameras"); - } else if idx >= n { - (capi.destroy_ctx)(ptr::addr_of_mut!(ctx)); - bail!( - "OH Camera: invalid idx {}, valid num is less than {}", - idx, - n - ); } - if (capi.init_profiles)(ctx) < 0 { + + fmt_cnt = (capi.init_profiles)(ctx); + if fmt_cnt < 0 { (capi.destroy_ctx)(ptr::addr_of_mut!(ctx)); bail!("OH Camera: failed to init profiles"); } } - Ok(Self { ctx, capi }) - } - - pub fn get_fmt_nums(&self, idx: i32) -> Result { - // SAFETY: We call related API sequentially for specified ctx. - let ret = unsafe { (self.capi.get_profile_size)(self.ctx, idx as c_int) }; - if ret < 0 { - bail!("OH Camera: invalid camera idx {}", idx); - } - Ok(ret) + Ok((Self { ctx, capi }, fmt_cnt)) } pub fn release_camera(&self) { @@ -93,10 +82,10 @@ impl OhCamera { unsafe { (self.capi.destroy_ctx)(ptr::addr_of_mut!(self.ctx)) } } - pub fn set_fmt(&self, cam_idx: i32, profile_idx: i32) -> Result<()> { + pub fn set_fmt(&self, profile_idx: i32) -> Result<()> { let ret = // SAFETY: We call related API sequentially for specified ctx. - unsafe { (self.capi.set_profile)(self.ctx, cam_idx as c_int, profile_idx as c_int) }; + unsafe { (self.capi.set_profile)(self.ctx, profile_idx as c_int) }; if ret < 0 { bail!("OH Camera: failed to get camera profile"); } @@ -123,12 +112,14 @@ impl OhCamera { Ok(()) } - pub fn reset_camera(&self) { + pub fn reset_camera(&self, id: String) -> Result<()> { + let id_cstr = CString::new(id).with_context(|| "failed to create CString id")?; // SAFETY: We call related API sequentially for specified ctx. unsafe { - (self.capi.init_cameras)(self.ctx); + (self.capi.init_camera)(self.ctx, id_cstr.as_ptr()); (self.capi.init_profiles)(self.ctx); } + Ok(()) } pub fn stop_stream(&self) { @@ -139,22 +130,17 @@ impl OhCamera { } } - pub fn get_profile(&self, cam_idx: i32, profile_idx: i32) -> Result<(i32, i32, i32, i32)> { + pub fn get_profile(&self, profile_idx: i32) -> Result<(i32, i32, i32, i32)> { let pr = ProfileRecorder::default(); // SAFETY: We call related API sequentially for specified ctx. unsafe { let ret = (self.capi.get_profile)( self.ctx, - cam_idx as c_int, profile_idx as c_int, ptr::addr_of!(pr) as *mut c_void, ); if ret < 0 { - bail!( - "OH Camera: failed to get camera {} profile {}", - cam_idx, - profile_idx - ); + bail!("OH Camera: failed to get profile {}", profile_idx); } } Ok((pr.fmt, pr.width, pr.height, pr.fps)) diff --git a/util/src/ohos_binding/hwf_adapter/camera.rs b/util/src/ohos_binding/hwf_adapter/camera.rs index 0fdc9ee74..bbb207427 100644 --- a/util/src/ohos_binding/hwf_adapter/camera.rs +++ b/util/src/ohos_binding/hwf_adapter/camera.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::raw::{c_int, c_void}; +use std::os::raw::{c_char, c_int, c_void}; use anyhow::{Context, Result}; use libloading::os::unix::Symbol as RawSymbol; @@ -33,17 +33,16 @@ pub struct ProfileRecorder { pub fps: i32, } -pub type BufferProcessFn = unsafe extern "C" fn(src_buffer: u64, length: i32); -pub type BrokenProcessFn = unsafe extern "C" fn(); +pub type BufferProcessFn = unsafe extern "C" fn(src_buffer: u64, length: i32, camid: *const c_char); +pub type BrokenProcessFn = unsafe extern "C" fn(camid: *const c_char); type OhcamCreateCtxFn = unsafe extern "C" fn() -> *mut OhCameraCtx; type OhcamCreateSessionFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; type OhcamReleaseSessionFn = unsafe extern "C" fn(*mut OhCameraCtx); -type OhcamInitCamerasFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; +type OhcamInitCameraFn = unsafe extern "C" fn(*mut OhCameraCtx, *const c_char) -> c_int; type OhcamInitProfilesFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; -type OhcamGetProfileSizeFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int) -> c_int; -type OhcamGetProfileFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int, c_int, *mut c_void) -> c_int; -type OhcamSetProfileFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int, c_int) -> c_int; +type OhcamGetProfileFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int, *mut c_void) -> c_int; +type OhcamSetProfileFn = unsafe extern "C" fn(*mut OhCameraCtx, c_int) -> c_int; type OhcamPreStartFn = unsafe extern "C" fn(*mut OhCameraCtx, BufferProcessFn, BrokenProcessFn) -> c_int; type OhcamStartFn = unsafe extern "C" fn(*mut OhCameraCtx) -> c_int; @@ -56,9 +55,8 @@ pub struct CamFuncTable { pub create_ctx: RawSymbol, pub create_session: RawSymbol, pub release_session: RawSymbol, - pub init_cameras: RawSymbol, + pub init_camera: RawSymbol, pub init_profiles: RawSymbol, - pub get_profile_size: RawSymbol, pub get_profile: RawSymbol, pub set_profile: RawSymbol, pub pre_start: RawSymbol, @@ -75,9 +73,8 @@ impl CamFuncTable { create_ctx: get_libfn!(library, OhcamCreateCtxFn, OhcamCreateCtx), create_session: get_libfn!(library, OhcamCreateSessionFn, OhcamCreateSession), release_session: get_libfn!(library, OhcamReleaseSessionFn, OhcamReleaseSession), - init_cameras: get_libfn!(library, OhcamInitCamerasFn, OhcamInitCameras), + init_camera: get_libfn!(library, OhcamInitCameraFn, OhcamInitCamera), init_profiles: get_libfn!(library, OhcamInitProfilesFn, OhcamInitProfiles), - get_profile_size: get_libfn!(library, OhcamGetProfileSizeFn, OhcamGetProfileSize), get_profile: get_libfn!(library, OhcamGetProfileFn, OhcamGetProfile), set_profile: get_libfn!(library, OhcamSetProfileFn, OhcamSetProfile), pre_start: get_libfn!(library, OhcamPreStartFn, OhcamPreStart), -- Gitee From f513d020df37bba847fdd9756a1f9af3a7d83bdf Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 18 Aug 2024 05:44:09 +0800 Subject: [PATCH 2019/2187] ohui_srv: fix wrong type Fix wrong type in ohui_srv. Signed-off-by: liuxiangdong --- ui/src/ohui_srv/msg_handle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index 84b788570..b87d5916a 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -148,7 +148,7 @@ impl OhUiMsgHandler { let hdr = &reader.header; let event_type = hdr.event_type; - let body_size = hdr.size as size; + let body_size = hdr.size as usize; trace::trace_scope_start!(handle_msg, args = (&event_type)); let body_bytes = reader.body.as_ref().unwrap(); -- Gitee From 970764876da0748b3229d8354d96a46914eecb09 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 18 Aug 2024 05:33:09 +0800 Subject: [PATCH 2020/2187] clippy: fix some cast_lossless warnings Fix some clippy warnings that were not fixed last commit (f3f0a3c432). Use command line: cargo clippy --fix --workspace --all-features --all-targets -- -Aclippy::all -Wclippy::cast_lossless Use command line in HM: cargo clippy --target aarch64-linux-ohos --features "boot_time ramfb virtio_gpu scream_ohaudio ohui_srv vnc usb_camera_oh pvpanic" -- -Aclippy::all -Wclippy::cast_lossless Note: Some header files have different type definitions in HM and Linux, and such warnings will not be fixed this time. See: machine_manager/src/socket.rs util/src/unix.rs Fix: f3f0a3c432(clippy: fix some cast_lossless warnings) Signed-off-by: liuxiangdong --- devices/src/camera_backend/demo.rs | 14 +++--- devices/src/camera_backend/mod.rs | 4 +- devices/src/camera_backend/ohcam.rs | 2 +- devices/src/camera_backend/v4l2.rs | 8 +-- devices/src/legacy/ramfb.rs | 4 +- devices/src/misc/scream/alsa.rs | 25 ++++++---- devices/src/misc/scream/ohaudio.rs | 12 +++-- devices/src/misc/scream/pulseaudio.rs | 11 ++-- devices/src/pci/demo_device/dpy_device.rs | 4 +- .../src/pci/demo_device/kbd_pointer_device.rs | 2 +- devices/src/pci/demo_device/mod.rs | 6 +-- devices/src/usb/camera.rs | 6 +-- devices/src/usb/usbhost/host_usblib.rs | 4 +- devices/src/usb/usbhost/mod.rs | 8 +-- hypervisor/src/kvm/mod.rs | 2 +- ui/src/gtk/draw.rs | 8 +-- ui/src/gtk/mod.rs | 32 ++++++------ ui/src/input.rs | 2 +- ui/src/ohui_srv/msg_handle.rs | 2 +- ui/src/vnc/client_io.rs | 50 +++++++++---------- ui/src/vnc/encoding/enc_hextile.rs | 3 +- ui/src/vnc/mod.rs | 22 ++++---- ui/src/vnc/server_io.rs | 8 +-- util/src/v4l2.rs | 2 +- 24 files changed, 125 insertions(+), 116 deletions(-) diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 19b1d4da7..60732d635 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -204,7 +204,7 @@ impl DemoCameraBackend { notify(); } let interval = if locked_fmt.fps != 0 { - 1000 / locked_fmt.fps as u64 + 1000 / u64::from(locked_fmt.fps) } else { 20 }; @@ -544,9 +544,9 @@ fn convert_to_nv12(source: &[u8], width: u32, height: u32) -> Vec { for i in 0..len { let idx = (i * pixel) as usize; let (b, g, r) = ( - source[idx] as f32, - source[idx + 1] as f32, - source[idx + 2] as f32, + f32::from(source[idx]), + f32::from(source[idx + 1]), + f32::from(source[idx + 2]), ); let y = (0.299 * r + 0.587 * g + 0.114 * b) as u8; img_nv12.push(y); @@ -554,9 +554,9 @@ fn convert_to_nv12(source: &[u8], width: u32, height: u32) -> Vec { for i in 0..(width * height / 2) { let idx = (i * 2 * pixel) as usize; let (b, g, r) = ( - source[idx] as f32, - source[idx + 1] as f32, - source[idx + 2] as f32, + f32::from(source[idx]), + f32::from(source[idx + 1]), + f32::from(source[idx + 2]), ); let u = (-0.147 * r - 0.289 * g + 0.436 * b + 128_f32) as u8; let v = (0.615 * r - 0.515 * g - 0.100 * b + 128_f32) as u8; diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 6e67d7624..86b1c4ec9 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -123,9 +123,9 @@ pub fn get_video_frame_size(width: u32, height: u32, fmt: FmtType) -> Result Result { let fm_size = get_video_frame_size(width, height, fmt)?; - let size_in_bit = fm_size as u64 * INTERVALS_PER_SEC as u64 * 8; + let size_in_bit = u64::from(fm_size) * u64::from(INTERVALS_PER_SEC) * 8; let rate = size_in_bit - .checked_div(interval as u64) + .checked_div(u64::from(interval)) .with_context(|| format!("Invalid size {} or interval {}", size_in_bit, interval))?; Ok(rate as u32) } diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 9d8c28417..a67b7898a 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -236,7 +236,7 @@ impl CameraBackend for OhCameraBackend { let mut fmt_list: Vec = Vec::new(); for idx in 0..self.profile_cnt { - match self.ctx.get_profile(idx as i32) { + match self.ctx.get_profile(i32::from(idx)) { Ok((fmt, width, height, fps)) => { if !FRAME_FORMAT_WHITELIST.iter().any(|&x| x == fmt) || !RESOLUTION_WHITELIST.iter().any(|&x| x == (width, height)) diff --git a/devices/src/camera_backend/v4l2.rs b/devices/src/camera_backend/v4l2.rs index 0885d1da7..f86b13ea8 100644 --- a/devices/src/camera_backend/v4l2.rs +++ b/devices/src/camera_backend/v4l2.rs @@ -225,8 +225,8 @@ impl V4l2CameraBackend { ); continue; } - let interval = - (numerator as u64 * INTERVALS_PER_SEC as u64 / denominator as u64) as u32; + let interval = (u64::from(numerator) * u64::from(INTERVALS_PER_SEC) + / u64::from(denominator)) as u32; list.push(interval); } Ok(list) @@ -498,7 +498,7 @@ impl V4l2IoHandler { let iov = locked_buf .get(buf.index as usize) .with_context(|| "Buffer index overflow")?; - if buf.bytesused as u64 > iov.iov_len { + if u64::from(buf.bytesused) > iov.iov_len { bail!( "Buffer overflow, bytesused {} iov len {}", buf.bytesused, @@ -506,7 +506,7 @@ impl V4l2IoHandler { ); } locked_sample.addr = iov.iov_base; - locked_sample.used_len = buf.bytesused as u64; + locked_sample.used_len = u64::from(buf.bytesused); locked_sample.buf_index = buf.index; drop(locked_sample); // Notify the camera to deal with request. diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 40eda77e0..8363213a4 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -121,13 +121,13 @@ impl RamfbState { } if stride == 0 { - let linesize = width * pixman_format_bpp(format as u32) as u32 / BYTES_PER_PIXELS; + let linesize = width * u32::from(pixman_format_bpp(format as u32)) / BYTES_PER_PIXELS; stride = linesize; } let fb_addr = match self.sys_mem.addr_cache_init(GuestAddress(addr)) { Some((hva, len)) => { - if len < stride as u64 { + if len < u64::from(stride) { error!("Insufficient contiguous memory length"); return; } diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index c97084b76..f6a8689ea 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -81,7 +81,7 @@ impl AlsaStreamData { hwp.set_rate_resample(true)?; hwp.set_access(Access::RWInterleaved)?; hwp.set_format(self.format)?; - hwp.set_channels(channels as u32)?; + hwp.set_channels(u32::from(channels))?; hwp.set_rate(self.rate, ValueOr::Nearest)?; // Set the latency in microseconds. hwp.set_buffer_time_near(self.latency * 1000, ValueOr::Nearest)?; @@ -184,12 +184,14 @@ impl AudioInterface for AlsaStreamData { }; let samples = - recv_data.audio_size / (self.bytes_per_sample * recv_data.fmt.channels as u32); + recv_data.audio_size / (self.bytes_per_sample * u32::from(recv_data.fmt.channels)); while frames < samples { let send_frame_num = min(samples - frames, MAX_FRAME_NUM); - let offset = (frames * self.bytes_per_sample * recv_data.fmt.channels as u32) as usize; + let offset = + (frames * self.bytes_per_sample * u32::from(recv_data.fmt.channels)) as usize; let end = offset - + (send_frame_num * self.bytes_per_sample * recv_data.fmt.channels as u32) as usize; + + (send_frame_num * self.bytes_per_sample * u32::from(recv_data.fmt.channels)) + as usize; match io.write(&data[offset..end]) { Err(e) => { debug!("Failed to write data to ALSA buffer: {:?}", e); @@ -203,7 +205,8 @@ impl AudioInterface for AlsaStreamData { } Ok(n) => { trace::scream_alsa_send_frames(frames, offset, end); - frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); + frames += + n as u32 / (self.bytes_per_sample * u32::from(recv_data.fmt.channels)); } } } @@ -231,11 +234,12 @@ impl AudioInterface for AlsaStreamData { }; let samples = - recv_data.audio_size / (self.bytes_per_sample * recv_data.fmt.channels as u32); + recv_data.audio_size / (self.bytes_per_sample * u32::from(recv_data.fmt.channels)); while frames < samples { - let offset = (frames * self.bytes_per_sample * recv_data.fmt.channels as u32) as usize; + let offset = + (frames * self.bytes_per_sample * u32::from(recv_data.fmt.channels)) as usize; let end = offset - + ((samples - frames) * self.bytes_per_sample * recv_data.fmt.channels as u32) + + ((samples - frames) * self.bytes_per_sample * u32::from(recv_data.fmt.channels)) as usize; match io.read(&mut data[offset..end]) { Err(e) => { @@ -250,7 +254,8 @@ impl AudioInterface for AlsaStreamData { } Ok(n) => { trace::scream_alsa_receive_frames(frames, offset, end); - frames += n as u32 / (self.bytes_per_sample * recv_data.fmt.channels as u32); + frames += + n as u32 / (self.bytes_per_sample * u32::from(recv_data.fmt.channels)); // During the host headset switchover, io.read is blocked for a long time. // As a result, the VM recording delay exceeds 1s. Thereforce, check whether @@ -259,7 +264,7 @@ impl AudioInterface for AlsaStreamData { warn!("Scream alsa can't get frames delay: {e:?}"); 0 }); - if delay > self.rate as i64 >> 1 { + if delay > i64::from(self.rate) >> 1 { warn!("Scream alsa read audio blocked too long, delay {delay} frames, init again!"); self.init = false; } diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index cd504140b..d6badff8f 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -356,8 +356,9 @@ impl OhAudioProcess for OhAudioCapture { self.align = sh_header.chunk_size; self.new_chunks.store(0, Ordering::Release); self.shm_addr = start_addr; - self.shm_len = sh_header.max_chunks as u64 * sh_header.chunk_size as u64; - self.cur_pos = start_addr + sh_header.chunk_idx as u64 * sh_header.chunk_size as u64; + self.shm_len = u64::from(sh_header.max_chunks) * u64::from(sh_header.chunk_size); + self.cur_pos = + start_addr + u64::from(sh_header.chunk_idx) * u64::from(sh_header.chunk_size); } fn process(&mut self, recv_data: &StreamData) -> i32 { @@ -442,7 +443,8 @@ extern "C" fn on_read_data_cb( break; } } - let old_pos = capture.cur_pos - ((capture.cur_pos - capture.shm_addr) % capture.align as u64); + let old_pos = + capture.cur_pos - ((capture.cur_pos - capture.shm_addr) % u64::from(capture.align)); let buf_end = capture.shm_addr + capture.shm_len; let mut src_addr = buffer as u64; let mut left = length as u64; @@ -466,8 +468,8 @@ extern "C" fn on_read_data_cb( } let new_chunks = match capture.cur_pos <= old_pos { - true => (capture.shm_len - (old_pos - capture.cur_pos)) / capture.align as u64, - false => (capture.cur_pos - old_pos) / capture.align as u64, + true => (capture.shm_len - (old_pos - capture.cur_pos)) / u64::from(capture.align), + false => (capture.cur_pos - old_pos) / u64::from(capture.align), }; capture .new_chunks diff --git a/devices/src/misc/scream/pulseaudio.rs b/devices/src/misc/scream/pulseaudio.rs index 2e90bfbbd..221055850 100644 --- a/devices/src/misc/scream/pulseaudio.rs +++ b/devices/src/misc/scream/pulseaudio.rs @@ -84,8 +84,8 @@ impl PulseStreamData { // Set buffer size for requested latency. let buffer_attr = BufferAttr { - maxlength: ss.usec_to_bytes(MicroSeconds(MAX_LATENCY_MS as u64 * 1000)) as u32, - tlength: ss.usec_to_bytes(MicroSeconds(TARGET_LATENCY_MS as u64 * 1000)) as u32, + maxlength: ss.usec_to_bytes(MicroSeconds(u64::from(MAX_LATENCY_MS) * 1000)) as u32, + tlength: ss.usec_to_bytes(MicroSeconds(u64::from(TARGET_LATENCY_MS) * 1000)) as u32, prebuf: std::u32::MAX, minreq: std::u32::MAX, fragsize: std::u32::MAX, @@ -198,9 +198,10 @@ impl PulseStreamData { if self.ss.rate > 0 { // Sample spec has changed, so the playback buffer size for the requested latency must // be recalculated as well. - self.buffer_attr.tlength = - self.ss - .usec_to_bytes(MicroSeconds(self.latency as u64 * 1000)) as u32; + self.buffer_attr.tlength = self + .ss + .usec_to_bytes(MicroSeconds(u64::from(self.latency) * 1000)) + as u32; self.simple = Simple::new( None, diff --git a/devices/src/pci/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs index bde922f68..e0157461a 100644 --- a/devices/src/pci/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -128,8 +128,8 @@ impl DisplayChangeListenerOperations for DpyInterface { } let mut i = 0; - let mut offset = y * stride + x * bpp as i32 / 8; - let count = w * bpp as i32 / 8; + let mut offset = y * stride + x * i32::from(bpp) / 8; + let count = w * i32::from(bpp) / 8; while i < h { error!( "update from {} to {}, before is {}", diff --git a/devices/src/pci/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs index ce5549871..78033f8ed 100644 --- a/devices/src/pci/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -94,7 +94,7 @@ impl KeyboardOpts for TestPciKbd { let msg = PointerMessage { event_type: InputEvent::KbdEvent, keycode, - down: down as u8, + down: u8::from(down), ..Default::default() }; MEM_ADDR.lock().unwrap().send_kbdmouse_message(&msg) diff --git a/devices/src/pci/demo_device/mod.rs b/devices/src/pci/demo_device/mod.rs index b4163f7aa..e8264b0d9 100644 --- a/devices/src/pci/demo_device/mod.rs +++ b/devices/src/pci/demo_device/mod.rs @@ -107,7 +107,7 @@ impl DemoDev { devfn, }, cmd_cfg: cfg, - mem_region: Region::init_container_region(u32::MAX as u64, "DemoDev"), + mem_region: Region::init_container_region(u64::from(u32::MAX), "DemoDev"), dev_id: Arc::new(AtomicU16::new(0)), device, } @@ -160,7 +160,7 @@ impl DemoDev { self.mem_region.clone(), RegionType::Mem64Bit, false, - (self.cmd_cfg.bar_size * self.cmd_cfg.bar_num as u64).next_power_of_two(), + (self.cmd_cfg.bar_size * u64::from(self.cmd_cfg.bar_num)).next_power_of_two(), )?; Ok(()) @@ -190,7 +190,7 @@ impl Device for DemoDev { self.register_data_handling_bar()?; self.device.lock().unwrap().realize()?; - let devfn = self.base.devfn as u64; + let devfn = u64::from(self.base.devfn); let parent_bus = self.parent_bus().unwrap().upgrade().unwrap(); let mut locked_bus = parent_bus.lock().unwrap(); let demo_pci_dev = Arc::new(Mutex::new(self)); diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index b7f0f1302..7dce78341 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -905,7 +905,7 @@ impl UvcPayload { MAX_PAYLOAD ); } - frame_data_size = MAX_PAYLOAD as u64 - self.payload_offset as u64; + frame_data_size = u64::from(MAX_PAYLOAD) - self.payload_offset as u64; } // payload start, reserve the header. if self.payload_offset == 0 && frame_data_size + header_len as u64 > iov_size { @@ -998,7 +998,7 @@ impl CameraIoHandler { // Payload start, add header. pkt.transfer_packet(&mut locked_payload.header, header_len); locked_payload.payload_offset += header_len; - iovecs = iov_discard_front_direct(&mut pkt.iovecs, pkt.actual_length as u64) + iovecs = iov_discard_front_direct(&mut pkt.iovecs, u64::from(pkt.actual_length)) .with_context(|| format!("Invalid iov size {}", pkt_size))?; } let copied = locked_camera.get_frame( @@ -1078,7 +1078,7 @@ fn gen_fmt_desc(fmt_list: Vec) -> Result body.push(Arc::new(UsbDescOther { data })); } - header_struct.wTotalLength = header_struct.bLength as u16 + header_struct.wTotalLength = u16::from(header_struct.bLength) + body.clone().iter().fold(0, |len, x| len + x.data.len()) as u16; let mut vec = header_struct.as_bytes().to_vec(); diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index e558f8144..913760746 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -133,7 +133,7 @@ pub fn set_pollfd_notifiers( if (*poll.offset(i)).is_null() { break; }; - if (*(*poll.offset(i))).events as c_int == EPOLLIN { + if i32::from((*(*poll.offset(i))).events) == EPOLLIN { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, (*(*poll.offset(i))).fd, @@ -141,7 +141,7 @@ pub fn set_pollfd_notifiers( EventSet::IN, vec![handler.clone()], )); - } else if (*(*poll.offset(i))).events as c_int == EPOLLOUT { + } else if i32::from((*(*poll.offset(i))).events) == EPOLLOUT { notifiers.push(EventNotifier::new( NotifierOperation::AddShared, (*(*poll.offset(i))).fd, diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 5859e19f6..62a2382e5 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -243,7 +243,7 @@ impl IsoTransfer { pub fn copy_data(&mut self, packet: Arc>, ep_max_packet_size: u32) -> bool { let mut lockecd_packet = packet.lock().unwrap(); let mut size: usize; - if lockecd_packet.pid == USB_TOKEN_OUT as u32 { + if lockecd_packet.pid == u32::from(USB_TOKEN_OUT) { size = lockecd_packet.get_iovecs_size() as usize; if size > ep_max_packet_size as usize { size = ep_max_packet_size as usize; @@ -783,7 +783,7 @@ impl UsbHost { .set_alternate_setting(iface as u8, alt as u8) { Ok(_) => { - self.base.altsetting[iface as usize] = alt as u32; + self.base.altsetting[iface as usize] = u32::from(alt); self.ep_update(); } Err(e) => { @@ -871,7 +871,7 @@ impl UsbHost { pub fn handle_iso_data_in(&mut self, packet: Arc>) { let cloned_packet = packet.clone(); let locked_packet = packet.lock().unwrap(); - let in_direction = locked_packet.pid == USB_TOKEN_IN as u32; + let in_direction = locked_packet.pid == u32::from(USB_TOKEN_IN); let iso_queue = if self.find_iso_queue(locked_packet.ep_number).is_some() { self.find_iso_queue(locked_packet.ep_number).unwrap() } else { @@ -905,7 +905,7 @@ impl UsbHost { let mut locked_iso_queue = iso_queue.lock().unwrap(); - let in_direction = locked_packet.pid == USB_TOKEN_IN as u32; + let in_direction = locked_packet.pid == u32::from(USB_TOKEN_IN); let ep = self .base .get_endpoint(in_direction, locked_packet.ep_number); diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 6d00cd381..16e7a479a 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -426,7 +426,7 @@ impl KvmCpu { #[cfg(target_arch = "x86_64")] VcpuExit::IoOut(addr, data) => { #[cfg(feature = "boot_time")] - cpu::capture_boot_signal(addr as u64, data); + cpu::capture_boot_signal(u64::from(addr), data); vm.lock().unwrap().pio_out(u64::from(addr), data); } diff --git a/ui/src/gtk/draw.rs b/ui/src/gtk/draw.rs index 9d9dde3cd..b9cbfc6ee 100644 --- a/ui/src/gtk/draw.rs +++ b/ui/src/gtk/draw.rs @@ -218,7 +218,7 @@ fn da_event_callback(gs: &Rc>, event: &gdk::Event) -> fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) -> Result<()> { let mut borrowed_gs = gs.borrow_mut(); let (width, height) = match &borrowed_gs.cairo_image { - Some(image) => (image.width() as f64, image.height() as f64), + Some(image) => (f64::from(image.width()), f64::from(image.height())), None => return Ok(()), }; @@ -231,8 +231,8 @@ fn gd_cursor_move_event(gs: &Rc>, event: &gdk::Event) let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; - input_move_abs(Axis::X, standard_x as u32)?; - input_move_abs(Axis::Y, standard_y as u32)?; + input_move_abs(Axis::X, u32::from(standard_x))?; + input_move_abs(Axis::Y, u32::from(standard_y))?; input_point_sync() } @@ -304,7 +304,7 @@ fn da_draw_callback(gs: &Rc>, cr: &cairo::Context) -> let mut borrowed_gs = gs.borrow_mut(); let scale_mode = borrowed_gs.scale_mode.clone(); let (mut surface_width, mut surface_height) = match &borrowed_gs.cairo_image { - Some(image) => (image.width() as f64, image.height() as f64), + Some(image) => (f64::from(image.width()), f64::from(image.height())), None => return Ok(()), }; diff --git a/ui/src/gtk/mod.rs b/ui/src/gtk/mod.rs index 84b59e5d6..2b055adc9 100644 --- a/ui/src/gtk/mod.rs +++ b/ui/src/gtk/mod.rs @@ -429,8 +429,8 @@ impl GtkDisplayScreen { fn get_window_size(&self) -> Option<(f64, f64)> { if let Some(win) = self.draw_area.window() { - let w_width = win.width() as f64; - let w_height = win.height() as f64; + let w_width = f64::from(win.width()); + let w_height = f64::from(win.height()); if w_width.ne(&0.0) && w_height.ne(&0.0) { return Some((w_width, w_height)); @@ -452,8 +452,8 @@ impl GtkDisplayScreen { None => bail!("No display image."), }; let (scale_width, scale_height) = ( - (surface_width as f64) * self.scale_x, - (surface_height as f64) * self.scale_y, + f64::from(surface_width) * self.scale_x, + f64::from(surface_height) * self.scale_y, ); let (mut window_width, mut window_height) = (0.0, 0.0); @@ -461,7 +461,7 @@ impl GtkDisplayScreen { (window_width, window_height) = (w, h); }; let scale_factor = match self.draw_area.window() { - Some(window) => window.scale_factor() as f64, + Some(window) => f64::from(window.scale_factor()), None => bail!("No display window."), }; @@ -816,13 +816,13 @@ fn do_update_event(gs: &Rc>, event: DisplayChangeEvent drop(locked_con); // Image scalling. - let x1 = ((x as f64) * borrowed_gs.scale_x).floor(); - let y1 = ((y as f64) * borrowed_gs.scale_y).floor(); - let x2 = ((x as f64) * borrowed_gs.scale_x + (w as f64) * borrowed_gs.scale_x).ceil(); - let y2 = ((y as f64) * borrowed_gs.scale_y + (h as f64) * borrowed_gs.scale_y).ceil(); + let x1 = (f64::from(x) * borrowed_gs.scale_x).floor(); + let y1 = (f64::from(y) * borrowed_gs.scale_y).floor(); + let x2 = (f64::from(x) * borrowed_gs.scale_x + f64::from(w) * borrowed_gs.scale_x).ceil(); + let y2 = (f64::from(y) * borrowed_gs.scale_y + f64::from(h) * borrowed_gs.scale_y).ceil(); - let scale_width = (surface_width as f64) * borrowed_gs.scale_x; - let scale_height = (surface_height as f64) * borrowed_gs.scale_y; + let scale_width = f64::from(surface_width) * borrowed_gs.scale_x; + let scale_height = f64::from(surface_height) * borrowed_gs.scale_y; let (window_width, window_height); match borrowed_gs.get_window_size() { Some((w, h)) => (window_width, window_height) = (w, h), @@ -964,8 +964,8 @@ fn do_switch_event(gs: &Rc>) -> Result<()> { None => return Ok(()), }; if scale_mode.borrow().is_full_screen() || scale_mode.borrow().is_free_scale() { - borrowed_gs.scale_x = window_width / surface_width as f64; - borrowed_gs.scale_y = window_height / surface_height as f64; + borrowed_gs.scale_x = window_width / f64::from(surface_width); + borrowed_gs.scale_y = window_height / f64::from(surface_height); } // Vm desktop manage its own cursor, gtk cursor need to be trsp firstly. @@ -1001,7 +1001,7 @@ pub(crate) fn update_window_size(gs: &Rc>) -> Result<( let borrowed_gs = gs.borrow(); let scale_mode = borrowed_gs.scale_mode.borrow().clone(); let (width, height) = match &borrowed_gs.cairo_image { - Some(image) => (image.width() as f64, image.height() as f64), + Some(image) => (f64::from(image.width()), f64::from(image.height())), None => (0.0, 0.0), }; let (mut scale_width, mut scale_height) = if scale_mode.is_free_scale() { @@ -1009,8 +1009,8 @@ pub(crate) fn update_window_size(gs: &Rc>) -> Result<( } else { (width * borrowed_gs.scale_x, height * borrowed_gs.scale_y) }; - scale_width = scale_width.max(DEFAULT_SURFACE_WIDTH as f64); - scale_height = scale_height.max(DEFAULT_SURFACE_HEIGHT as f64); + scale_width = scale_width.max(f64::from(DEFAULT_SURFACE_WIDTH)); + scale_height = scale_height.max(f64::from(DEFAULT_SURFACE_HEIGHT)); let geo: Geometry = Geometry::new( scale_width as i32, diff --git a/ui/src/input.rs b/ui/src/input.rs index a04f65b59..f72119cf5 100644 --- a/ui/src/input.rs +++ b/ui/src/input.rs @@ -647,7 +647,7 @@ mod tests { for idx in 0..keysym_lists.len() { let keysym = keycode_lists[idx]; let keycode = keycode_lists[idx]; - assert!(do_key_event(true, keysym as i32, keycode).is_ok()); + assert!(do_key_event(true, i32::from(keysym), keycode).is_ok()); assert_eq!(test_kdb.lock().unwrap().keycode, keycode); assert!(test_kdb.lock().unwrap().down); } diff --git a/ui/src/ohui_srv/msg_handle.rs b/ui/src/ohui_srv/msg_handle.rs index b87d5916a..21a11637e 100755 --- a/ui/src/ohui_srv/msg_handle.rs +++ b/ui/src/ohui_srv/msg_handle.rs @@ -89,7 +89,7 @@ impl WindowState { } fn move_pointer(&mut self, x: f64, y: f64) -> Result<()> { - let (pos_x, pos_y) = trans_mouse_pos(x, y, self.width as f64, self.height as f64); + let (pos_x, pos_y) = trans_mouse_pos(x, y, f64::from(self.width), f64::from(self.height)); input_move_abs(Axis::X, pos_x)?; input_move_abs(Axis::Y, pos_y)?; input_point_sync() diff --git a/ui/src/vnc/client_io.rs b/ui/src/vnc/client_io.rs index b4fa90556..7ffa89968 100644 --- a/ui/src/vnc/client_io.rs +++ b/ui/src/vnc/client_io.rs @@ -400,7 +400,7 @@ impl ClientState { conn_state: Arc::new(Mutex::new(ConnState::default())), dirty_bitmap: Arc::new(Mutex::new(Bitmap::::new( MAX_WINDOW_HEIGHT as usize - * round_up_div(DIRTY_WIDTH_BITS as u64, u64::BITS as u64) as usize, + * round_up_div(u64::from(DIRTY_WIDTH_BITS), u64::from(u64::BITS)) as usize, ))), } } @@ -728,9 +728,9 @@ impl ClientIoHandler { let pf = self.client.client_dpm.lock().unwrap().pf.clone(); for i in 0..NUM_OF_COLORMAP { - let r = ((i >> pf.red.shift) & pf.red.max as u16) << (16 - pf.red.bits); - let g = ((i >> pf.green.shift) & pf.green.max as u16) << (16 - pf.green.bits); - let b = ((i >> pf.blue.shift) & pf.blue.max as u16) << (16 - pf.blue.bits); + let r = ((i >> pf.red.shift) & u16::from(pf.red.max)) << (16 - pf.red.bits); + let g = ((i >> pf.green.shift) & u16::from(pf.green.max)) << (16 - pf.green.bits); + let b = ((i >> pf.blue.shift) & u16::from(pf.blue.max)) << (16 - pf.blue.bits); buf.append(&mut r.to_be_bytes().to_vec()); buf.append(&mut g.to_be_bytes().to_vec()); buf.append(&mut b.to_be_bytes().to_vec()); @@ -922,10 +922,10 @@ impl ClientIoHandler { } } else { locked_state.update_state = UpdateState::Force; - let x = u16::from_be_bytes([buf[2], buf[3]]) as i32; - let y = u16::from_be_bytes([buf[4], buf[5]]) as i32; - let w = u16::from_be_bytes([buf[6], buf[7]]) as i32; - let h = u16::from_be_bytes([buf[8], buf[9]]) as i32; + let x = i32::from(u16::from_be_bytes([buf[2], buf[3]])); + let y = i32::from(u16::from_be_bytes([buf[4], buf[5]])); + let w = i32::from(u16::from_be_bytes([buf[6], buf[7]])); + let h = i32::from(u16::from_be_bytes([buf[8], buf[9]])); set_area_dirty( &mut client.dirty_bitmap.lock().unwrap(), x, @@ -992,8 +992,8 @@ impl ClientIoHandler { } let buf = self.read_incoming_msg(); - let mut x = ((buf[2] as u16) << 8) + buf[3] as u16; - let mut y = ((buf[4] as u16) << 8) + buf[5] as u16; + let mut x = (u16::from(buf[2]) << 8) + u16::from(buf[3]); + let mut y = (u16::from(buf[4]) << 8) + u16::from(buf[5]); trace::vnc_client_point_event(&buf[1], &x, &y); // Window size alignment. @@ -1001,8 +1001,8 @@ impl ClientIoHandler { let width = get_image_width(locked_surface.server_image); let height = get_image_height(locked_surface.server_image); drop(locked_surface); - x = ((x as u64 * ABS_MAX) / width as u64) as u16; - y = ((y as u64 * ABS_MAX) / height as u64) as u16; + x = ((u64::from(x) * ABS_MAX) / width as u64) as u16; + y = ((u64::from(y) * ABS_MAX) / height as u64) as u16; // ASCII -> HidCode. let new_button = buf[1]; @@ -1023,15 +1023,15 @@ impl ClientIoHandler { VNC_INPUT_BUTTON_WHEEL_RIGHT => INPUT_BUTTON_WHEEL_RIGHT, VNC_INPUT_BUTTON_WHEEL_LEFT => INPUT_BUTTON_WHEEL_LEFT, VNC_INPUT_BUTTON_BACK => INPUT_POINT_BACK, - _ => button_mask as u32, + _ => u32::from(button_mask), }; input_button(button, new_button & button_mask != 0)?; } self.client.client_dpm.lock().unwrap().last_button = new_button; } - input_move_abs(Axis::X, x as u32)?; - input_move_abs(Axis::Y, y as u32)?; + input_move_abs(Axis::X, u32::from(x))?; + input_move_abs(Axis::Y, u32::from(y))?; input_point_sync()?; self.update_event_handler(1, ClientIoHandler::handle_protocol_msg); @@ -1061,7 +1061,7 @@ impl ClientIoHandler { fn auth_failed(&mut self, msg: &str) { let auth_rej: u8 = 1; let mut buf: Vec = vec![1u8]; - buf.append(&mut (auth_rej as u32).to_be_bytes().to_vec()); + buf.append(&mut u32::from(auth_rej).to_be_bytes().to_vec()); // If the RFB protocol version is above 3.8, an error reason will be returned. if self.client.conn_state.lock().unwrap().version.minor >= 8 { let err_msg = msg; @@ -1250,17 +1250,17 @@ pub fn get_rects(client: &Arc, server: &Arc, dirty_num: } h = i - y; - x2 = cmp::min(x2, width / DIRTY_PIXELS_NUM as u64); + x2 = cmp::min(x2, width / u64::from(DIRTY_PIXELS_NUM)); if x2 > x { rects.push(Rectangle::new( - (x * DIRTY_PIXELS_NUM as u64) as i32, + (x * u64::from(DIRTY_PIXELS_NUM)) as i32, y as i32, - ((x2 - x) * DIRTY_PIXELS_NUM as u64) as i32, + ((x2 - x) * u64::from(DIRTY_PIXELS_NUM)) as i32, h as i32, )); } - if x == 0 && x2 == width / DIRTY_PIXELS_NUM as u64 { + if x == 0 && x2 == width / u64::from(DIRTY_PIXELS_NUM) { y += h; if y == height { break; @@ -1289,9 +1289,9 @@ fn pixel_format_message(client: &Arc, buf: &mut Vec) { buf.append(&mut locked_dpm.pf.depth.to_be_bytes().to_vec()); // Depth. buf.append(&mut big_endian.to_be_bytes().to_vec()); // Big-endian flag. buf.append(&mut (1_u8).to_be_bytes().to_vec()); // True-color flag. - buf.append(&mut (locked_dpm.pf.red.max as u16).to_be_bytes().to_vec()); // Red max. - buf.append(&mut (locked_dpm.pf.green.max as u16).to_be_bytes().to_vec()); // Green max. - buf.append(&mut (locked_dpm.pf.blue.max as u16).to_be_bytes().to_vec()); // Blue max. + buf.append(&mut u16::from(locked_dpm.pf.red.max).to_be_bytes().to_vec()); // Red max. + buf.append(&mut u16::from(locked_dpm.pf.green.max).to_be_bytes().to_vec()); // Green max. + buf.append(&mut u16::from(locked_dpm.pf.blue.max).to_be_bytes().to_vec()); // Blue max. buf.append(&mut locked_dpm.pf.red.shift.to_be_bytes().to_vec()); // Red shift. buf.append(&mut locked_dpm.pf.green.shift.to_be_bytes().to_vec()); // Green shift. buf.append(&mut locked_dpm.pf.blue.shift.to_be_bytes().to_vec()); // Blue shift. @@ -1416,7 +1416,7 @@ pub fn display_cursor_define( buf, ); let dpm = client.client_dpm.lock().unwrap().clone(); - let data_size = cursor.width * cursor.height * dpm.pf.pixel_bytes as u32; + let data_size = cursor.width * cursor.height * u32::from(dpm.pf.pixel_bytes); let data_ptr = cursor.data.as_ptr() as *mut u8; write_pixel(data_ptr, data_size as usize, &dpm, buf); buf.append(&mut mask); @@ -1442,7 +1442,7 @@ pub fn vnc_update_output_throttle(client: &Arc) { let width = locked_dpm.client_width; let height = locked_dpm.client_height; let bytes_per_pixel = locked_dpm.pf.pixel_bytes; - let mut offset = width * height * (bytes_per_pixel as i32) * OUTPUT_THROTTLE_SCALE; + let mut offset = width * height * i32::from(bytes_per_pixel) * OUTPUT_THROTTLE_SCALE; drop(locked_dpm); offset = cmp::max(offset, MIN_OUTPUT_LIMIT); diff --git a/ui/src/vnc/encoding/enc_hextile.rs b/ui/src/vnc/encoding/enc_hextile.rs index d6b31f69d..f41a4d8c1 100644 --- a/ui/src/vnc/encoding/enc_hextile.rs +++ b/ui/src/vnc/encoding/enc_hextile.rs @@ -127,7 +127,8 @@ fn compress_each_tile<'a>( &mut tmp_buf, ); // If the length becomes longer after compression, give up compression. - if tmp_buf.len() > (sub_rect.h * sub_rect.w * client_dpm.pf.pixel_bytes as i32) as usize + if tmp_buf.len() + > (sub_rect.h * sub_rect.w * i32::from(client_dpm.pf.pixel_bytes)) as usize { flag = RAW; *last_bg = None; diff --git a/ui/src/vnc/mod.rs b/ui/src/vnc/mod.rs index ff2e48c91..a760ae3b3 100644 --- a/ui/src/vnc/mod.rs +++ b/ui/src/vnc/mod.rs @@ -234,10 +234,10 @@ impl DisplayChangeListenerOperations for VncInterface { return Ok(()); } let server = VNC_SERVERS.lock().unwrap()[0].clone(); - let width = cursor.width as u64; - let height = cursor.height as u64; + let width = u64::from(cursor.width); + let height = u64::from(cursor.height); trace::vnc_dpy_cursor_update(&width, &height); - let bpl = round_up_div(width, BIT_PER_BYTE as u64); + let bpl = round_up_div(width, u64::from(BIT_PER_BYTE)); // Set the bit for mask. let bit_mask: u8 = 0x80; @@ -254,7 +254,7 @@ impl DisplayChangeListenerOperations for VncInterface { let idx = ((i + j * width) as usize) * bytes_per_pixel() + first_bit; if let Some(n) = cursor.data.get(idx) { if *n == 0xff { - mask[(j * bpl + i / BIT_PER_BYTE as u64) as usize] |= bit; + mask[(j * bpl + i / u64::from(BIT_PER_BYTE)) as usize] |= bit; } } bit >>= 1; @@ -426,16 +426,16 @@ pub fn set_area_dirty( let width: i32 = vnc_width(g_w); let height: i32 = vnc_height(g_h); - w += x % DIRTY_PIXELS_NUM as i32; - x -= x % DIRTY_PIXELS_NUM as i32; + w += x % i32::from(DIRTY_PIXELS_NUM); + x -= x % i32::from(DIRTY_PIXELS_NUM); x = cmp::min(x, width); y = cmp::min(y, height); w = cmp::min(x + w, width) - x; h = cmp::min(y + h, height); while y < h { - let pos = (y * VNC_BITMAP_WIDTH as i32 + x / DIRTY_PIXELS_NUM as i32) as usize; - let len = round_up_div(w as u64, DIRTY_PIXELS_NUM as u64) as usize; + let pos = (y * VNC_BITMAP_WIDTH as i32 + x / i32::from(DIRTY_PIXELS_NUM)) as usize; + let len = round_up_div(w as u64, u64::from(DIRTY_PIXELS_NUM)) as usize; dirty.set_range(pos, len)?; y += 1; } @@ -445,14 +445,14 @@ pub fn set_area_dirty( /// Get the width of image. fn vnc_width(width: i32) -> i32 { cmp::min( - MAX_WINDOW_WIDTH as i32, - round_up(width as u64, DIRTY_PIXELS_NUM as u64) as i32, + i32::from(MAX_WINDOW_WIDTH), + round_up(width as u64, u64::from(DIRTY_PIXELS_NUM)) as i32, ) } /// Get the height of image. fn vnc_height(height: i32) -> i32 { - cmp::min(MAX_WINDOW_HEIGHT as i32, height) + cmp::min(i32::from(MAX_WINDOW_HEIGHT), height) } /// Update server image diff --git a/ui/src/vnc/server_io.rs b/ui/src/vnc/server_io.rs index ac577df1d..2b2903559 100644 --- a/ui/src/vnc/server_io.rs +++ b/ui/src/vnc/server_io.rs @@ -173,7 +173,7 @@ struct ImageInfo { impl ImageInfo { fn new(image: *mut pixman_image_t) -> Self { let bpp = pixman_format_bpp(get_image_format(image) as u32); - let length = get_image_width(image) * round_up_div(bpp as u64, 8) as i32; + let length = get_image_width(image) * round_up_div(u64::from(bpp), 8) as i32; ImageInfo { data: get_image_data(image) as *mut u8, stride: get_image_stride(image), @@ -310,8 +310,8 @@ impl VncSurface { guest_dirty_bitmap: Bitmap::::new( MAX_WINDOW_HEIGHT as usize * round_up_div( - (MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM) as u64, - u64::BITS as u64, + u64::from(MAX_WINDOW_WIDTH / DIRTY_PIXELS_NUM), + u64::from(u64::BITS), ) as usize, ), server_image: ptr::null_mut(), @@ -424,7 +424,7 @@ impl VncSurface { let width = self.get_min_width(); let line_bytes = cmp::min(s_info.stride, g_info.length); - while x < round_up_div(width as u64, DIRTY_PIXELS_NUM as u64) as usize { + while x < round_up_div(width as u64, u64::from(DIRTY_PIXELS_NUM)) as usize { if !self .guest_dirty_bitmap .contain(x + y * VNC_BITMAP_WIDTH as usize) diff --git a/util/src/v4l2.rs b/util/src/v4l2.rs index 1e2890502..9abcfd22a 100644 --- a/util/src/v4l2.rs +++ b/util/src/v4l2.rs @@ -152,7 +152,7 @@ impl V4l2Backend { ); } locked_buf[i as usize].iov_base = ret as u64; - locked_buf[i as usize].iov_len = buf.length as u64; + locked_buf[i as usize].iov_len = u64::from(buf.length); // Queue buffer to get data. self.queue_buffer(&buf)?; } -- Gitee From eb8612bf07a6b2a2936d628cbe9c5401fad9712c Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 18 Aug 2024 07:43:46 +0800 Subject: [PATCH 2021/2187] block: print device id in error log Log device ID when block device IO error occurs. This is mainly used for locating unexpected error IO for micro VMs. Signed-off-by: liuxiangdong --- virtio/src/device/block.rs | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index d07fc2bc1..6e17764fb 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -276,10 +276,12 @@ impl Request { cache: &Option, elem: &mut Element, status: &mut u8, + devid: &str, ) -> Result { if elem.out_iovec.is_empty() || elem.in_iovec.is_empty() { bail!( - "Missed header for block request: out {} in {} desc num {}", + "Missed header for block {} request: out {} in {} desc num {}", + devid, elem.out_iovec.len(), elem.in_iovec.len(), elem.desc_num @@ -332,7 +334,7 @@ impl Request { // Otherwise discard the last "status" byte. _ => iov_discard_back(&mut elem.in_iovec, 1), } - .with_context(|| "Empty data for block request")?; + .with_context(|| format!("Empty data for block {} request", devid))?; let (data_len, iovec) = gpa_hva_iovec_map(data_iovec, &handler.mem_space, cache)?; request.data_len = data_len; @@ -345,7 +347,7 @@ impl Request { } } - if !request.io_range_valid(handler.disk_sectors) { + if !request.io_range_valid(handler.disk_sectors, devid) { *status = VIRTIO_BLK_S_IOERR; } @@ -389,24 +391,40 @@ impl Request { VIRTIO_BLK_T_IN => { locked_backend .read_vectored(iovecs, offset, aiocompletecb) - .with_context(|| "Failed to process block request for reading")?; + .with_context(|| { + format!( + "Failed to process block {} request for reading", + iohandler.devid + ) + })?; } VIRTIO_BLK_T_OUT => { locked_backend .write_vectored(iovecs, offset, aiocompletecb) - .with_context(|| "Failed to process block request for writing")?; + .with_context(|| { + format!( + "Failed to process block {} request for writing", + iohandler.devid + ) + })?; } VIRTIO_BLK_T_FLUSH => { - locked_backend - .datasync(aiocompletecb) - .with_context(|| "Failed to process block request for flushing")?; + locked_backend.datasync(aiocompletecb).with_context(|| { + format!( + "Failed to process block {} request for flushing", + iohandler.devid + ) + })?; } VIRTIO_BLK_T_GET_ID => { let serial = serial_num.clone().unwrap_or_else(|| String::from("")); let serial_vec = get_serial_num_config(&serial); let status = iov_from_buf_direct(&self.iovec, &serial_vec).map_or_else( |e| { - error!("Failed to process block request for getting id, {:?}", e); + error!( + "Failed to process block {} request for getting id, {:?}", + iohandler.devid, e + ); VIRTIO_BLK_S_IOERR }, |_| VIRTIO_BLK_S_OK, @@ -504,7 +522,7 @@ impl Request { Ok(()) } - fn io_range_valid(&self, disk_sectors: u64) -> bool { + fn io_range_valid(&self, disk_sectors: u64, devid: &str) -> bool { match self.out_header.request_type { VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT => { if self.data_len % SECTOR_SIZE != 0 { @@ -518,8 +536,8 @@ impl Request { .is_none() { error!( - "offset {} invalid, disk sector {}", - self.out_header.sector, disk_sectors + "devid {} offset {} invalid, disk sector {}", + devid, self.out_header.sector, disk_sectors ); return false; } @@ -536,6 +554,8 @@ impl Request { /// Control block of Block IO. struct BlockIoHandler { + /// Device id of this block device. + devid: String, /// The virtqueue. queue: Arc>, /// Eventfd of the virtqueue for IO event. @@ -648,7 +668,7 @@ impl BlockIoHandler { // Init and put valid request into request queue. let mut status = VIRTIO_BLK_S_OK; let cache = queue.vring.get_cache(); - let req = Request::new(self, cache, &mut elem, &mut status)?; + let req = Request::new(self, cache, &mut elem, &mut status, &self.devid)?; if status != VIRTIO_BLK_S_OK { let aiocompletecb = AioCompleteCb::new( self.queue.clone(), @@ -687,7 +707,10 @@ impl BlockIoHandler { if let Some(block_backend) = self.block_backend.as_ref() { req_rc.execute(self, block_backend.clone(), aiocompletecb)?; } else { - warn!("Failed to execute block request, block_backend not specified"); + warn!( + "Failed to execute block {} request, block_backend not specified", + &self.devid + ); aiocompletecb.complete_request(VIRTIO_BLK_S_IOERR)?; } } @@ -1235,6 +1258,7 @@ impl VirtioDevice for Block { let update_evt = Arc::new(create_new_eventfd()?); let driver_features = self.base.driver_features; let handler = BlockIoHandler { + devid: self.blk_cfg.id.clone(), queue: queue.clone(), queue_evt: queue_evts[index].clone(), mem_space: mem_space.clone(), -- Gitee From 95e08deddc0c7cd8e9b9a579ee42bedbea80dd51 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 18 Aug 2024 09:55:19 +0800 Subject: [PATCH 2022/2187] qcow2: fix refcount updating error when deleting snapshot When deleting snapshot, snapshot information in memory will be deleted firstly. Therefore, the size of the snapshot table in the updated memory (assuming size1) is not the size of the old snapshot table (assuming size0). If this `size1` is used to release the old snapshot table refcount, it may cause problems. For example, if there is only one snapshot and the updated size is 0, using `0` to release the old snapshot table will result in no clusters being released, causing problems with the refcount block count. Fix this problem. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 7c14cb981..f45cfce00 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -913,16 +913,20 @@ impl Qcow2Driver { bail!("Snapshot with name {} does not exist", name); } + // Record the old snapshot table size which will be used to free these old snapshot table clusters. + let cluster_size = self.header.cluster_size(); + let old_snapshot_table_clusters = + bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); + // Delete snapshot information in memory. let snap = self.snapshot.del_snapshot(snapshot_idx as usize); // Alloc new cluster to save snapshots(except the deleted one) to disk. - let cluster_size = self.header.cluster_size(); let mut new_snapshots_offset = 0_u64; - let snapshot_table_clusters = + let new_snapshot_table_clusters = bytes_to_clusters(self.snapshot.snapshot_size, cluster_size).unwrap(); if self.snapshot.snapshots_number() > 0 { - new_snapshots_offset = self.alloc_cluster(snapshot_table_clusters, true)?; + new_snapshots_offset = self.alloc_cluster(new_snapshot_table_clusters, true)?; self.snapshot .save_snapshot_table(new_snapshots_offset, &snap, false)?; } @@ -952,7 +956,7 @@ impl Qcow2Driver { // Free the cluster of the old snapshot table. self.refcount.update_refcount( self.header.snapshots_offset, - snapshot_table_clusters, + old_snapshot_table_clusters, -1, false, &Qcow2DiscardType::Snapshot, -- Gitee From de8eecd7e4727221314808b093f8774fa4a02bfb Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 18 Aug 2024 18:59:45 +0800 Subject: [PATCH 2023/2187] xhci: fix altsetting/interfaces array overflows problem Converting nif/alt to u8 causes truncation, which only compares the lower eight bits, resulting in misjudgment. If nif is returned in this way, assuming that the higher bits of nif is non-zero, it will cause array descriptor.altsetting/descriptor.interfaces overflow in function `set_interface_descriptor`. Fix it. Signed-off-by: liuxiangdong --- devices/src/usb/descriptor.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 0f56753b6..956dc3e94 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -407,8 +407,8 @@ impl UsbDescriptor { for i in 0..conf.iad_desc.len() { let ifaces = &conf.iad_desc[i].as_ref().itfs; for iface in ifaces { - if iface.interface_desc.bInterfaceNumber == nif as u8 - && iface.interface_desc.bAlternateSetting == alt as u8 + if u32::from(iface.interface_desc.bInterfaceNumber) == nif + && u32::from(iface.interface_desc.bAlternateSetting) == alt { return Some(iface.clone()); } @@ -416,8 +416,8 @@ impl UsbDescriptor { } for i in 0..conf.interfaces.len() { let iface = conf.interfaces[i].as_ref(); - if iface.interface_desc.bInterfaceNumber == nif as u8 - && iface.interface_desc.bAlternateSetting == alt as u8 + if u32::from(iface.interface_desc.bInterfaceNumber) == nif + && u32::from(iface.interface_desc.bAlternateSetting) == alt { return Some(conf.interfaces[i].clone()); } -- Gitee From 5bbe2a1705caf14b0eea8136b8affc3b1ca2e93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Sat, 17 Aug 2024 14:34:33 +0800 Subject: [PATCH 2024/2187] Usb_uas: Add usb_uas feature control Add usb_uas faeture to control usb_uas function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + devices/Cargo.toml | 1 + devices/src/usb/mod.rs | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 2 ++ 5 files changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1af042fe2..abe616bb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ trace_to_ftrace = ["trace/trace_to_ftrace"] trace_to_hitrace = ["trace/trace_to_hitrace"] hisysevent = ["hisysevent/hisysevent"] vfio = ["machine/vfio_device", "hypervisor/vfio_device"] +usb_uas = ["machine/usb_uas"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index fec9d5c13..85c062df3 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -52,3 +52,4 @@ usb_camera = ["machine_manager/usb_camera"] usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera_v4l2", "util/usb_camera_v4l2"] usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh", "util/usb_camera_oh"] ramfb = ["ui/console", "util/pixman"] +usb_uas = [] diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index f8dc2f90c..d01696755 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -20,6 +20,7 @@ pub mod hid; pub mod keyboard; pub mod storage; pub mod tablet; +#[cfg(feature = "usb_uas")] pub mod uas; #[cfg(feature = "usb_host")] pub mod usbhost; diff --git a/machine/Cargo.toml b/machine/Cargo.toml index ce455c719..d36c786b8 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -51,3 +51,4 @@ ohui_srv = ["windows_emu_pid", "ui/ohui_srv", "machine_manager/ohui_srv", "virti ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] vfio_device = ["vfio"] +usb_uas = ["devices/usb_uas"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 6b2fe0fae..5f8af29b2 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -66,6 +66,7 @@ use devices::usb::camera::{UsbCamera, UsbCameraConfig}; use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig}; use devices::usb::storage::{UsbStorage, UsbStorageConfig}; use devices::usb::tablet::{UsbTablet, UsbTabletConfig}; +#[cfg(feature = "usb_uas")] use devices::usb::uas::{UsbUas, UsbUasConfig}; #[cfg(feature = "usb_host")] use devices::usb::usbhost::{UsbHost, UsbHostConfig}; @@ -1823,6 +1824,7 @@ pub trait MachineOps: MachineLifecycle { .realize() .with_context(|| "Failed to realize usb storage device")? } + #[cfg(feature = "usb_uas")] "usb-uas" => { let device_cfg = UsbUasConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; -- Gitee From 26c28345b0bc1e592f74677f7415e02fa5eb6351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Mon, 19 Aug 2024 15:38:45 +0800 Subject: [PATCH 2025/2187] Virtio: Add virtio rng feature Add virtio rng feature to control virtio rng function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 13 ++++++++----- virtio/Cargo.toml | 1 + virtio/src/device/mod.rs | 1 + virtio/src/lib.rs | 1 + 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index abe616bb9..dabc17f70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ trace_to_hitrace = ["trace/trace_to_hitrace"] hisysevent = ["hisysevent/hisysevent"] vfio = ["machine/vfio_device", "hypervisor/vfio_device"] usb_uas = ["machine/usb_uas"] +virtio_rng = ["machine/virtio_rng"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index d36c786b8..2a73737e3 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -52,3 +52,4 @@ ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] vfio_device = ["vfio"] usb_uas = ["devices/usb_uas"] +virtio_rng = ["virtio/virtio_rng"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 5f8af29b2..51b30c79b 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -106,14 +106,15 @@ use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, virtio_register_pcidevops_type, - virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, Rng, RngConfig, - RngState, + virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}, Serial, SerialPort, VhostKern, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; #[cfg(feature = "virtio_gpu")] use virtio::{Gpu, GpuDevConfig}; +#[cfg(feature = "virtio_rng")] +use virtio::{Rng, RngConfig, RngState}; #[cfg(feature = "windows_emu_pid")] const WINDOWS_EMU_PID_DEFAULT_INTERVAL: u64 = 4000; @@ -290,7 +291,7 @@ macro_rules! create_device_add_matches { ( $command:expr; $controller: expr; $(($($driver_name:tt)|+, $function_name:tt, $($arg:tt),*)),*; $(#[cfg($($features: tt)*)] - ($driver_name1:tt, $function_name1:tt, $($arg1:tt),*)),* + ($($driver_name1:tt)|+, $function_name1:tt, $($arg1:tt),*)),* ) => { match $command { $( @@ -300,7 +301,7 @@ macro_rules! create_device_add_matches { )* $( #[cfg($($features)*)] - $driver_name1 => { + $($driver_name1)|+ => { $controller.$function_name1($($arg1),*).with_context(|| format!("add {} fail.", $command))?; }, )* @@ -856,6 +857,7 @@ pub trait MachineOps: MachineLifecycle { /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration arguments. + #[cfg(feature = "virtio_rng")] fn add_virtio_rng(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let rng_cfg = RngConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; rng_cfg.bytes_per_sec()?; @@ -1900,12 +1902,13 @@ pub trait MachineOps: MachineLifecycle { ("virtio-balloon-device" | "virtio-balloon-pci", add_virtio_balloon, vm_config, cfg_args), ("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args), ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args), - ("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args), ("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args), ("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false), ("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args), ("nec-usb-xhci", add_usb_xhci, cfg_args), ("usb-kbd" | "usb-storage" | "usb-uas" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); + #[cfg(feature = "virtio_rng")] + ("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args), #[cfg(feature = "vfio_device")] ("vfio-pci", add_vfio_device, cfg_args, false), #[cfg(feature = "virtio_gpu")] diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 91c89cd05..8563bc81d 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -31,4 +31,5 @@ clap = { version = "=4.1.4", default-features = false, features = ["std", "deriv [features] default = [] virtio_gpu = ["ui", "machine_manager/virtio_gpu", "util/pixman"] +virtio_rng = [] ohui_srv = [] diff --git a/virtio/src/device/mod.rs b/virtio/src/device/mod.rs index 11ea8ed93..410b4a757 100644 --- a/virtio/src/device/mod.rs +++ b/virtio/src/device/mod.rs @@ -15,6 +15,7 @@ pub mod block; #[cfg(feature = "virtio_gpu")] pub mod gpu; pub mod net; +#[cfg(feature = "virtio_rng")] pub mod rng; pub mod scsi_cntlr; pub mod serial; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 4c3e8e87c..19c5870e5 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -37,6 +37,7 @@ pub use device::block::{Block, BlockState, VirtioBlkConfig, VirtioBlkDevConfig}; #[cfg(feature = "virtio_gpu")] pub use device::gpu::*; pub use device::net::*; +#[cfg(feature = "virtio_rng")] pub use device::rng::{Rng, RngConfig, RngState}; pub use device::scsi_cntlr as ScsiCntlr; pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; -- Gitee From 1edfbee0cc49aa542df12143abb874d665e3e214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Sat, 17 Aug 2024 16:49:00 +0800 Subject: [PATCH 2026/2187] Virtio: Add virtio scsi feature Add virtio scsi feature to control virtio scsi function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 22 +++++++++++++++------- machine/src/standard_common/mod.rs | 1 + virtio/Cargo.toml | 1 + virtio/src/device/mod.rs | 1 + virtio/src/lib.rs | 1 + 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dabc17f70..dc409580e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ hisysevent = ["hisysevent/hisysevent"] vfio = ["machine/vfio_device", "hypervisor/vfio_device"] usb_uas = ["machine/usb_uas"] virtio_rng = ["machine/virtio_rng"] +virtio_scsi = ["machine/virtio_scsi"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 2a73737e3..6a2476c4c 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -53,3 +53,4 @@ virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] vfio_device = ["vfio"] usb_uas = ["devices/usb_uas"] virtio_rng = ["virtio/virtio_rng"] +virtio_scsi = ["virtio/virtio_scsi"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 51b30c79b..f7aa2d99c 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -74,9 +74,12 @@ use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice}; use devices::usb::UsbDevice; #[cfg(target_arch = "aarch64")] use devices::InterruptController; -use devices::ScsiBus::get_scsi_key; -use devices::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use devices::{convert_bus_ref, Bus, Device, PCI_BUS, SYS_BUS_DEVICE}; +#[cfg(feature = "virtio_scsi")] +use devices::{ + ScsiBus::get_scsi_key, + ScsiDisk::{ScsiDevConfig, ScsiDevice}, +}; use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; @@ -102,13 +105,14 @@ use util::loop_context::{ use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; #[cfg(feature = "vfio_device")] use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; +#[cfg(feature = "virtio_scsi")] +use virtio::ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, virtio_register_pcidevops_type, - virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, - ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}, - Serial, SerialPort, VhostKern, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, + virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, Serial, + SerialPort, VhostKern, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; #[cfg(feature = "virtio_gpu")] @@ -1164,6 +1168,7 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } + #[cfg(feature = "virtio_scsi")] fn add_virtio_pci_scsi( &mut self, vm_config: &mut VmConfig, @@ -1195,6 +1200,7 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } + #[cfg(feature = "virtio_scsi")] fn add_scsi_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = ScsiDevConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let drive_arg = vm_config @@ -1893,8 +1899,6 @@ pub trait MachineOps: MachineLifecycle { dev.0.as_str(); self; ("virtio-blk-device", add_virtio_mmio_block, vm_config, cfg_args), ("virtio-blk-pci", add_virtio_pci_blk, vm_config, cfg_args, false), - ("virtio-scsi-pci", add_virtio_pci_scsi, vm_config, cfg_args, false), - ("scsi-hd" | "scsi-cd", add_scsi_device, vm_config, cfg_args), ("virtio-net-device", add_virtio_mmio_net, vm_config, cfg_args), ("virtio-net-pci", add_virtio_pci_net, vm_config, cfg_args, false), ("pcie-root-port", add_pci_root_port, cfg_args), @@ -1913,6 +1917,10 @@ pub trait MachineOps: MachineLifecycle { ("vfio-pci", add_vfio_device, cfg_args, false), #[cfg(feature = "virtio_gpu")] ("virtio-gpu-pci", add_virtio_pci_gpu, cfg_args), + #[cfg(feature = "virtio_scsi")] + ("virtio-scsi-pci", add_virtio_pci_scsi, vm_config, cfg_args, false), + #[cfg(feature = "virtio_scsi")] + ("scsi-hd" | "scsi-cd", add_scsi_device, vm_config, cfg_args), #[cfg(feature = "ramfb")] ("ramfb", add_ramfb, cfg_args), #[cfg(feature = "demo_device")] diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 9fd03245f..abf28717d 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1216,6 +1216,7 @@ impl DeviceInterface for StdMachine { ); } } + #[cfg(feature = "virtio_scsi")] "virtio-scsi-pci" => { let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); if let Err(e) = self.add_virtio_pci_scsi(&mut vm_config_clone, &cfg_args, true) { diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 8563bc81d..de0b7c613 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -32,4 +32,5 @@ clap = { version = "=4.1.4", default-features = false, features = ["std", "deriv default = [] virtio_gpu = ["ui", "machine_manager/virtio_gpu", "util/pixman"] virtio_rng = [] +virtio_scsi = [] ohui_srv = [] diff --git a/virtio/src/device/mod.rs b/virtio/src/device/mod.rs index 410b4a757..1f9ddba81 100644 --- a/virtio/src/device/mod.rs +++ b/virtio/src/device/mod.rs @@ -17,5 +17,6 @@ pub mod gpu; pub mod net; #[cfg(feature = "virtio_rng")] pub mod rng; +#[cfg(feature = "virtio_scsi")] pub mod scsi_cntlr; pub mod serial; diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 19c5870e5..9c1f46d73 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -39,6 +39,7 @@ pub use device::gpu::*; pub use device::net::*; #[cfg(feature = "virtio_rng")] pub use device::rng::{Rng, RngConfig, RngState}; +#[cfg(feature = "virtio_scsi")] pub use device::scsi_cntlr as ScsiCntlr; pub use device::serial::{find_port_by_nr, get_max_nr, Serial, SerialPort, VirtioSerialState}; pub use error::VirtioError; -- Gitee From eb12fe4e685143ac9f2f4ffd2a4f32a6018eaa6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Mon, 19 Aug 2024 16:00:07 +0800 Subject: [PATCH 2027/2187] Virtio: Add vhost vsock feature Add vhost vsock feature to control vhost vsock function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 10 +++++++--- virtio/Cargo.toml | 1 + virtio/src/vhost/kernel/mod.rs | 2 ++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc409580e..4c8df16c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ vfio = ["machine/vfio_device", "hypervisor/vfio_device"] usb_uas = ["machine/usb_uas"] virtio_rng = ["machine/virtio_rng"] virtio_scsi = ["machine/virtio_scsi"] +vhost_vsock = ["machine/vhost_vsock"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 6a2476c4c..b76c0c2b0 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -54,3 +54,4 @@ vfio_device = ["vfio"] usb_uas = ["devices/usb_uas"] virtio_rng = ["virtio/virtio_rng"] virtio_scsi = ["virtio/virtio_scsi"] +vhost_vsock = ["virtio/vhost_vsock"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f7aa2d99c..16de40fee 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -107,13 +107,15 @@ use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(feature = "virtio_scsi")] use virtio::ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}; +#[cfg(any(feature = "vhost_vsock"))] +use virtio::VhostKern; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, virtio_register_pcidevops_type, virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, Serial, - SerialPort, VhostKern, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, - VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, + SerialPort, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, VirtioMmioState, + VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; #[cfg(feature = "virtio_gpu")] use virtio::{Gpu, GpuDevConfig}; @@ -590,6 +592,7 @@ pub trait MachineOps: MachineLifecycle { /// # Arguments /// /// * `cfg_args` - Device configuration. + #[cfg(feature = "vhost_vsock")] fn add_virtio_vsock(&mut self, cfg_args: &str) -> Result<()> { let device_cfg = VhostKern::VsockConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; @@ -1902,7 +1905,6 @@ pub trait MachineOps: MachineLifecycle { ("virtio-net-device", add_virtio_mmio_net, vm_config, cfg_args), ("virtio-net-pci", add_virtio_pci_net, vm_config, cfg_args, false), ("pcie-root-port", add_pci_root_port, cfg_args), - ("vhost-vsock-pci" | "vhost-vsock-device", add_virtio_vsock, cfg_args), ("virtio-balloon-device" | "virtio-balloon-pci", add_virtio_balloon, vm_config, cfg_args), ("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args), ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args), @@ -1911,6 +1913,8 @@ pub trait MachineOps: MachineLifecycle { ("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args), ("nec-usb-xhci", add_usb_xhci, cfg_args), ("usb-kbd" | "usb-storage" | "usb-uas" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); + #[cfg(feature = "vhost_vsock")] + ("vhost-vsock-pci" | "vhost-vsock-device", add_virtio_vsock, cfg_args), #[cfg(feature = "virtio_rng")] ("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args), #[cfg(feature = "vfio_device")] diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index de0b7c613..5e6327473 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -34,3 +34,4 @@ virtio_gpu = ["ui", "machine_manager/virtio_gpu", "util/pixman"] virtio_rng = [] virtio_scsi = [] ohui_srv = [] +vhost_vsock =[] diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 2111e2bbb..3e2049891 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -11,9 +11,11 @@ // See the Mulan PSL v2 for more details. mod net; +#[cfg(feature = "vhost_vsock")] mod vsock; pub use net::Net; +#[cfg(feature = "vhost_vsock")] pub use vsock::{Vsock, VsockConfig, VsockState}; use std::fs::{File, OpenOptions}; -- Gitee From a7f36606f40fa5036f991768f900e714840cd60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Sat, 17 Aug 2024 17:45:29 +0800 Subject: [PATCH 2028/2187] Virtio: Add vhost user block feature Add vhost user block feature to control vhost vsock function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 8 ++++++-- machine/src/standard_common/mod.rs | 1 + virtio/Cargo.toml | 1 + virtio/src/vhost/user/mod.rs | 2 ++ 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4c8df16c8..ec1ff2a05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ usb_uas = ["machine/usb_uas"] virtio_rng = ["machine/virtio_rng"] virtio_scsi = ["machine/virtio_scsi"] vhost_vsock = ["machine/vhost_vsock"] +vhostuser_block = ["machine/vhostuser_block"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index b76c0c2b0..e8bfa32b8 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -55,3 +55,4 @@ usb_uas = ["devices/usb_uas"] virtio_rng = ["virtio/virtio_rng"] virtio_scsi = ["virtio/virtio_scsi"] vhost_vsock = ["virtio/vhost_vsock"] +vhostuser_block = ["virtio/vhostuser_block"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 16de40fee..729bdde21 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1317,6 +1317,7 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } + #[cfg(feature = "vhostuser_block")] fn add_vhost_user_blk_pci( &mut self, vm_config: &mut VmConfig, @@ -1370,6 +1371,7 @@ pub trait MachineOps: MachineLifecycle { Ok(()) } + #[cfg(feature = "vhostuser_block")] fn add_vhost_user_blk_device( &mut self, vm_config: &mut VmConfig, @@ -1908,11 +1910,13 @@ pub trait MachineOps: MachineLifecycle { ("virtio-balloon-device" | "virtio-balloon-pci", add_virtio_balloon, vm_config, cfg_args), ("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args), ("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args), - ("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args), - ("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false), ("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args), ("nec-usb-xhci", add_usb_xhci, cfg_args), ("usb-kbd" | "usb-storage" | "usb-uas" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args); + #[cfg(feature = "vhostuser_block")] + ("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args), + #[cfg(feature = "vhostuser_block")] + ("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false), #[cfg(feature = "vhost_vsock")] ("vhost-vsock-pci" | "vhost-vsock-device", add_virtio_vsock, cfg_args), #[cfg(feature = "virtio_rng")] diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index abf28717d..88610417b 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1229,6 +1229,7 @@ impl DeviceInterface for StdMachine { ); } } + #[cfg(feature = "vhostuser_block")] "vhost-user-blk-pci" => { let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); if let Err(e) = self.add_vhost_user_blk_pci(&mut vm_config_clone, &cfg_args, true) { diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 5e6327473..3c9d7c6b0 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -35,3 +35,4 @@ virtio_rng = [] virtio_scsi = [] ohui_srv = [] vhost_vsock =[] +vhostuser_block = [] diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 8a6e8d42d..8476b97ff 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -12,12 +12,14 @@ pub mod fs; +#[cfg(feature = "vhostuser_block")] mod block; mod client; mod message; mod net; mod sock; +#[cfg(feature = "vhostuser_block")] pub use self::block::{Block, VhostUserBlkDevConfig}; pub use self::client::*; pub use self::fs::*; -- Gitee From 1716ce317a9eb75fc7e00bfac0d844378012606e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Sat, 17 Aug 2024 18:10:49 +0800 Subject: [PATCH 2029/2187] Virtio: Add vhost user net feature Add vhost user net feature to control vhost vsock function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 46 ++++++++++++++++++------------- machine/src/micro_common/mod.rs | 48 ++++++++++++++++++++------------- virtio/Cargo.toml | 1 + virtio/src/vhost/user/mod.rs | 2 ++ 6 files changed, 61 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ec1ff2a05..fb27ccea6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ virtio_rng = ["machine/virtio_rng"] virtio_scsi = ["machine/virtio_scsi"] vhost_vsock = ["machine/vhost_vsock"] vhostuser_block = ["machine/vhostuser_block"] +vhostuser_net = ["machine/vhostuser_net"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index e8bfa32b8..bbab764d7 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -56,3 +56,4 @@ virtio_rng = ["virtio/virtio_rng"] virtio_scsi = ["virtio/virtio_scsi"] vhost_vsock = ["virtio/vhost_vsock"] vhostuser_block = ["virtio/vhostuser_block"] +vhostuser_net = ["virtio/vhostuser_net"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 729bdde21..2809c4136 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -83,12 +83,14 @@ use devices::{ use hypervisor::{kvm::KvmHypervisor, test::TestHypervisor, HypervisorOps}; #[cfg(feature = "usb_camera")] use machine_manager::config::get_cameradev_by_id; +#[cfg(feature = "vhostuser_net")] +use machine_manager::config::get_chardev_socket_path; use machine_manager::config::{ - complete_numa_node, get_chardev_socket_path, get_class_type, get_pci_bdf, - get_value_of_parameter, parse_numa_distance, parse_numa_mem, str_slip_to_clap, BootIndexInfo, - BootSource, ConfigCheck, DriveConfig, DriveFile, Incoming, MachineMemConfig, MigrateMode, - NetworkInterfaceConfig, NumaNode, NumaNodes, PciBdf, SerialConfig, VirtioSerialInfo, - VirtioSerialPortCfg, VmConfig, FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, + complete_numa_node, get_class_type, get_pci_bdf, get_value_of_parameter, parse_numa_distance, + parse_numa_mem, str_slip_to_clap, BootIndexInfo, BootSource, ConfigCheck, DriveConfig, + DriveFile, Incoming, MachineMemConfig, MigrateMode, NetworkInterfaceConfig, NumaNode, + NumaNodes, PciBdf, SerialConfig, VirtioSerialInfo, VirtioSerialPortCfg, VmConfig, + FAST_UNPLUG_ON, MAX_VIRTIO_QUEUE, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{HypervisorType, MachineInterface, MachineLifecycle, VmState}; @@ -1286,20 +1288,26 @@ pub trait MachineOps: MachineLifecycle { self.get_sys_mem(), ))) } else { - let chardev = netdev_cfg.chardev.clone().with_context(|| { - format!("Chardev not configured for netdev {:?}", netdev_cfg.id) - })?; - let chardev_cfg = vm_config - .chardev - .remove(&chardev) - .with_context(|| format!("Chardev: {:?} not found for netdev", chardev))?; - let sock_path = get_chardev_socket_path(chardev_cfg)?; - Arc::new(Mutex::new(VhostUser::Net::new( - &net_cfg, - netdev_cfg, - sock_path, - self.get_sys_mem(), - ))) + #[cfg(not(feature = "vhostuser_net"))] + bail!("Unsupported Vhostuser_net"); + + #[cfg(feature = "vhostuser_net")] + { + let chardev = netdev_cfg.chardev.clone().with_context(|| { + format!("Chardev not configured for netdev {:?}", netdev_cfg.id) + })?; + let chardev_cfg = vm_config + .chardev + .remove(&chardev) + .with_context(|| format!("Chardev: {:?} not found for netdev", chardev))?; + let sock_path = get_chardev_socket_path(chardev_cfg)?; + Arc::new(Mutex::new(VhostUser::Net::new( + &net_cfg, + netdev_cfg, + sock_path, + self.get_sys_mem(), + ))) + } } } else { let device = Arc::new(Mutex::new(virtio::Net::new(net_cfg.clone(), netdev_cfg))); diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 8410e96b4..18746a1d6 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -52,11 +52,13 @@ use cpu::CpuLifecycleState; use devices::sysbus::SysBusDevOps; use devices::sysbus::{IRQ_BASE, IRQ_MAX}; use devices::Device; +#[cfg(feature = "vhostuser_net")] +use machine_manager::config::get_chardev_socket_path; #[cfg(target_arch = "x86_64")] use machine_manager::config::Param; use machine_manager::config::{ - get_chardev_socket_path, parse_incoming_uri, str_slip_to_clap, ConfigCheck, DriveConfig, - MigrateMode, NetDevcfg, NetworkInterfaceConfig, VmConfig, + parse_incoming_uri, str_slip_to_clap, ConfigCheck, DriveConfig, MigrateMode, NetDevcfg, + NetworkInterfaceConfig, VmConfig, }; use machine_manager::machine::{ DeviceInterface, MachineAddressInterface, MachineExternalInterface, MachineInterface, @@ -70,9 +72,11 @@ use migration::MigrationManager; use util::loop_context::{create_new_eventfd, EventLoopManager}; use util::{num_ops::str_to_num, set_termi_canon_mode}; use virtio::device::block::VirtioBlkDevConfig; +#[cfg(feature = "vhostuser_net")] +use virtio::VhostUser; use virtio::{ - create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VhostUser, - VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, + create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VirtioDevice, + VirtioMmioDevice, VirtioMmioState, VirtioNetState, }; // The replaceable block device maximum count. @@ -422,21 +426,27 @@ impl LightMachine { ))); self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; } else { - let chardev = netdev_cfg.chardev.clone().with_context(|| { - format!("Chardev not configured for netdev {:?}", netdev_cfg.id) - })?; - let chardev_cfg = vm_config - .chardev - .remove(&chardev) - .with_context(|| format!("Chardev: {:?} not found for netdev", chardev))?; - let sock_path = get_chardev_socket_path(chardev_cfg)?; - let net = Arc::new(Mutex::new(VhostUser::Net::new( - &net_cfg, - netdev_cfg, - sock_path, - &self.base.sys_mem, - ))); - self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; + #[cfg(not(feature = "vhostuser_net"))] + bail!("Unsupported Vhostuser_Net"); + + #[cfg(feature = "vhostuser_net")] + { + let chardev = netdev_cfg.chardev.clone().with_context(|| { + format!("Chardev not configured for netdev {:?}", netdev_cfg.id) + })?; + let chardev_cfg = vm_config + .chardev + .remove(&chardev) + .with_context(|| format!("Chardev: {:?} not found for netdev", chardev))?; + let sock_path = get_chardev_socket_path(chardev_cfg)?; + let net = Arc::new(Mutex::new(VhostUser::Net::new( + &net_cfg, + netdev_cfg, + sock_path, + &self.base.sys_mem, + ))); + self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; + } }; } else { let index = MMIO_REPLACEABLE_BLK_NR + self.replaceable_info.net_count; diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 3c9d7c6b0..20d02d9ed 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -36,3 +36,4 @@ virtio_scsi = [] ohui_srv = [] vhost_vsock =[] vhostuser_block = [] +vhostuser_net = [] diff --git a/virtio/src/vhost/user/mod.rs b/virtio/src/vhost/user/mod.rs index 8476b97ff..68a0b166e 100644 --- a/virtio/src/vhost/user/mod.rs +++ b/virtio/src/vhost/user/mod.rs @@ -16,6 +16,7 @@ pub mod fs; mod block; mod client; mod message; +#[cfg(feature = "vhostuser_net")] mod net; mod sock; @@ -24,6 +25,7 @@ pub use self::block::{Block, VhostUserBlkDevConfig}; pub use self::client::*; pub use self::fs::*; pub use self::message::*; +#[cfg(feature = "vhostuser_net")] pub use self::net::Net; pub use self::sock::*; -- Gitee From 034bb568e031426f387a1e34fc07d32ad770faaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Sat, 17 Aug 2024 18:37:13 +0800 Subject: [PATCH 2030/2187] Virtio: Add vhost net feature Add vhost net feature to control vhost vsock function. Signed-off-by: Yihua Jin --- Cargo.toml | 1 + machine/Cargo.toml | 1 + machine/src/lib.rs | 28 ++++++++++++++++++++-------- machine/src/micro_common/mod.rs | 22 +++++++++++++++------- virtio/Cargo.toml | 1 + virtio/src/vhost/kernel/mod.rs | 2 ++ 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fb27ccea6..8bb441751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ virtio_scsi = ["machine/virtio_scsi"] vhost_vsock = ["machine/vhost_vsock"] vhostuser_block = ["machine/vhostuser_block"] vhostuser_net = ["machine/vhostuser_net"] +vhost_net = ["machine/vhost_net"] [package.metadata.rpm.cargo] buildflags = ["--release"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index bbab764d7..dbb25016a 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -57,3 +57,4 @@ virtio_scsi = ["virtio/virtio_scsi"] vhost_vsock = ["virtio/vhost_vsock"] vhostuser_block = ["virtio/vhostuser_block"] vhostuser_net = ["virtio/vhostuser_net"] +vhost_net = ["virtio/vhost_net"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 2809c4136..4cab91377 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -109,14 +109,16 @@ use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; use vfio::{vfio_register_pcidevops_type, VfioConfig, VfioDevice, VfioPciDevice, KVM_DEVICE_FD}; #[cfg(feature = "virtio_scsi")] use virtio::ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr, ScsiCntlrConfig}; -#[cfg(any(feature = "vhost_vsock"))] +#[cfg(any(feature = "vhost_vsock", feature = "vhost_net"))] use virtio::VhostKern; +#[cfg(any(feature = "vhostuser_block", feature = "vhostuser_net"))] +use virtio::VhostUser; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use virtio::VirtioDeviceQuirk; use virtio::{ balloon_allow_list, find_port_by_nr, get_max_nr, vhost, virtio_register_pcidevops_type, virtio_register_sysbusdevops_type, Balloon, BalloonConfig, Block, BlockState, Serial, - SerialPort, VhostUser, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, VirtioMmioState, + SerialPort, VirtioBlkDevConfig, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE, }; #[cfg(feature = "virtio_gpu")] @@ -1278,21 +1280,31 @@ pub trait MachineOps: MachineLifecycle { let bdf = PciBdf::new(net_cfg.bus.clone().unwrap(), net_cfg.addr.unwrap()); let multi_func = net_cfg.multifunction.unwrap_or_default(); + #[cfg(all(not(feature = "vhost_net"), not(feature = "vhostuser_net")))] + let need_irqfd = false; + #[cfg(any(feature = "vhost_net", feature = "vhostuser_net"))] let mut need_irqfd = false; let device: Arc> = if netdev_cfg.vhost_type().is_some() { - need_irqfd = true; if netdev_cfg.vhost_type().unwrap() == "vhost-kernel" { - Arc::new(Mutex::new(VhostKern::Net::new( - &net_cfg, - netdev_cfg, - self.get_sys_mem(), - ))) + #[cfg(not(feature = "vhost_net"))] + bail!("Unsupported Vhost_net"); + + #[cfg(feature = "vhost_net")] + { + need_irqfd = true; + Arc::new(Mutex::new(VhostKern::Net::new( + &net_cfg, + netdev_cfg, + self.get_sys_mem(), + ))) + } } else { #[cfg(not(feature = "vhostuser_net"))] bail!("Unsupported Vhostuser_net"); #[cfg(feature = "vhostuser_net")] { + need_irqfd = true; let chardev = netdev_cfg.chardev.clone().with_context(|| { format!("Chardev not configured for netdev {:?}", netdev_cfg.id) })?; diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 18746a1d6..34c123184 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -72,10 +72,12 @@ use migration::MigrationManager; use util::loop_context::{create_new_eventfd, EventLoopManager}; use util::{num_ops::str_to_num, set_termi_canon_mode}; use virtio::device::block::VirtioBlkDevConfig; +#[cfg(feature = "vhost_net")] +use virtio::VhostKern; #[cfg(feature = "vhostuser_net")] use virtio::VhostUser; use virtio::{ - create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VhostKern, VirtioDevice, + create_tap, qmp_balloon, qmp_query_balloon, Block, BlockState, Net, VirtioDevice, VirtioMmioDevice, VirtioMmioState, VirtioNetState, }; @@ -419,12 +421,18 @@ impl LightMachine { .with_context(|| format!("Netdev: {:?} not found for net device", &net_cfg.netdev))?; if netdev_cfg.vhost_type().is_some() { if netdev_cfg.vhost_type().unwrap() == "vhost-kernel" { - let net = Arc::new(Mutex::new(VhostKern::Net::new( - &net_cfg, - netdev_cfg, - &self.base.sys_mem, - ))); - self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; + #[cfg(not(feature = "vhost_net"))] + bail!("Unsupported Vhost_Net"); + + #[cfg(feature = "vhost_net")] + { + let net = Arc::new(Mutex::new(VhostKern::Net::new( + &net_cfg, + netdev_cfg, + &self.base.sys_mem, + ))); + self.add_virtio_mmio_device(net_cfg.id.clone(), net)?; + } } else { #[cfg(not(feature = "vhostuser_net"))] bail!("Unsupported Vhostuser_Net"); diff --git a/virtio/Cargo.toml b/virtio/Cargo.toml index 20d02d9ed..b8692b394 100644 --- a/virtio/Cargo.toml +++ b/virtio/Cargo.toml @@ -37,3 +37,4 @@ ohui_srv = [] vhost_vsock =[] vhostuser_block = [] vhostuser_net = [] +vhost_net = [] diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index 3e2049891..cca3cedc9 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -10,10 +10,12 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(feature = "vhost_net")] mod net; #[cfg(feature = "vhost_vsock")] mod vsock; +#[cfg(feature = "vhost_net")] pub use net::Net; #[cfg(feature = "vhost_vsock")] pub use vsock::{Vsock, VsockConfig, VsockState}; -- Gitee From a8a25986ed5ee393ef09b004f5096c9a28956de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=BA=A2=E5=8D=8E?= Date: Mon, 19 Aug 2024 16:22:33 +0800 Subject: [PATCH 2031/2187] Vifo: Change the dependency position Change the hypervisor vfio dependency position. Signed-off-by: Yihua Jin --- Cargo.toml | 3 +-- machine/Cargo.toml | 2 +- machine/src/lib.rs | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8bb441751..bf79c900c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ machine_manager = { path = "machine_manager" } util = { path = "util" } trace = { path = "trace" } hisysevent = { path = "hisysevent" } -hypervisor = { path = "hypervisor" } [workspace] members = [ @@ -45,7 +44,7 @@ trace_to_logger = ["trace/trace_to_logger"] trace_to_ftrace = ["trace/trace_to_ftrace"] trace_to_hitrace = ["trace/trace_to_hitrace"] hisysevent = ["hisysevent/hisysevent"] -vfio = ["machine/vfio_device", "hypervisor/vfio_device"] +vfio = ["machine/vfio_device"] usb_uas = ["machine/usb_uas"] virtio_rng = ["machine/virtio_rng"] virtio_scsi = ["machine/virtio_scsi"] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index dbb25016a..c53a050e4 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -50,7 +50,7 @@ vnc_auth = ["vnc"] ohui_srv = ["windows_emu_pid", "ui/ohui_srv", "machine_manager/ohui_srv", "virtio/ohui_srv"] ramfb = ["devices/ramfb", "machine_manager/ramfb"] virtio_gpu = ["virtio/virtio_gpu", "machine_manager/virtio_gpu"] -vfio_device = ["vfio"] +vfio_device = ["vfio", "hypervisor/vfio_device"] usb_uas = ["devices/usb_uas"] virtio_rng = ["virtio/virtio_rng"] virtio_scsi = ["virtio/virtio_scsi"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 4cab91377..6a87070d5 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -29,6 +29,7 @@ use std::net::TcpListener; use std::ops::Deref; use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixListener; +#[cfg(any(feature = "windows_emu_pid", feature = "vfio_device"))] use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; -- Gitee From daaeda02713cc535a351f7560278c200fc37a81a Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Aug 2024 21:36:56 +0800 Subject: [PATCH 2032/2187] clippy: Fix some clippy warnings Signed-off-by: Keqian Zhu --- address_space/src/address_space.rs | 8 +- address_space/src/region.rs | 20 +- block_backend/src/qcow2/cache.rs | 2 +- block_backend/src/qcow2/table.rs | 2 +- hypervisor/Cargo.toml | 1 + hypervisor/src/kvm/interrupt.rs | 2 +- hypervisor/src/kvm/listener.rs | 12 +- hypervisor/src/kvm/mod.rs | 10 +- machine/Cargo.toml | 2 +- machine/src/lib.rs | 12 +- machine/src/standard_common/mod.rs | 2 + machine_manager/src/qmp/qmp_response.rs | 2 +- tests/mod_test/src/libdriver/qcow2.rs | 2 +- tests/mod_test/src/libdriver/virtio.rs | 6 +- tests/mod_test/src/libdriver/virtio_block.rs | 20 +- tests/mod_test/src/libdriver/virtio_gpu.rs | 10 +- .../src/libdriver/virtio_pci_modern.rs | 2 +- tests/mod_test/src/libdriver/virtio_rng.rs | 2 +- tests/mod_test/src/libdriver/vnc.rs | 35 ++-- tests/mod_test/tests/balloon_test.rs | 16 +- tests/mod_test/tests/block_test.rs | 40 ++-- tests/mod_test/tests/fwcfg_test.rs | 16 +- tests/mod_test/tests/memory_test.rs | 8 +- tests/mod_test/tests/net_test.rs | 133 +++---------- tests/mod_test/tests/pci_test.rs | 178 ++++++++---------- tests/mod_test/tests/pvpanic_test.rs | 2 +- tests/mod_test/tests/rng_test.rs | 16 +- tests/mod_test/tests/scsi_test.rs | 12 +- tests/mod_test/tests/serial_test.rs | 10 +- tests/mod_test/tests/usb_camera_test.rs | 2 +- tests/mod_test/tests/virtio_test.rs | 159 +++------------- tests/mod_test/tests/virtiofs_test.rs | 14 +- tests/mod_test/tests/vnc_test.rs | 4 +- ui/src/console.rs | 7 +- virtio/src/device/balloon.rs | 2 +- virtio/src/queue/split.rs | 9 +- virtio/src/transport/virtio_pci.rs | 6 +- 37 files changed, 263 insertions(+), 523 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 84f1be4e0..0689aaea7 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -930,10 +930,10 @@ mod test { let listener3 = Arc::new(Mutex::new(ListenerPrior3::default())); let listener4 = Arc::new(Mutex::new(ListenerPrior4::default())); let listener5 = Arc::new(Mutex::new(ListenerNeg::default())); - space.register_listener(listener1.clone()).unwrap(); + space.register_listener(listener1).unwrap(); space.register_listener(listener3.clone()).unwrap(); - space.register_listener(listener5.clone()).unwrap(); - space.register_listener(listener2.clone()).unwrap(); + space.register_listener(listener5).unwrap(); + space.register_listener(listener2).unwrap(); space.register_listener(listener4.clone()).unwrap(); let mut pre_prior = std::i32::MIN; @@ -978,7 +978,7 @@ mod test { let space = AddressSpace::new(root, "space", None).unwrap(); let listener1 = Arc::new(Mutex::new(ListenerPrior0::default())); let listener2 = Arc::new(Mutex::new(ListenerPrior0::default())); - space.register_listener(listener1.clone()).unwrap(); + space.register_listener(listener1).unwrap(); space.register_listener(listener2.clone()).unwrap(); space.unregister_listener(listener2).unwrap(); diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 64ad7fbae..52e5f43fb 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -1208,7 +1208,7 @@ mod test { let mut device_locked = test_dev_clone.lock().unwrap(); device_locked.read(data, addr, offset) }; - let test_dev_clone = test_dev.clone(); + let test_dev_clone = test_dev; let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool { let mut device_locked = test_dev_clone.lock().unwrap(); device_locked.write(data, addr, offset) @@ -1219,7 +1219,7 @@ mod test { write: Arc::new(write_ops), }; - let io_region = Region::init_io_region(16, test_dev_ops.clone(), "io_region"); + let io_region = Region::init_io_region(16, test_dev_ops, "io_region"); let data = [0x01u8; 8]; let mut data_res = [0x0u8; 8]; let count = data.len() as u64; @@ -1299,7 +1299,7 @@ mod test { }; let io_region = Region::init_io_region(1 << 4, default_ops.clone(), "io1"); - let io_region2 = Region::init_io_region(1 << 4, default_ops.clone(), "io2"); + let io_region2 = Region::init_io_region(1 << 4, default_ops, "io2"); io_region2.set_priority(10); // add duplicate io-region or ram-region will fail @@ -1364,9 +1364,9 @@ mod test { region_b.set_priority(2); region_c.set_priority(1); region_a.add_subregion(region_b.clone(), 2000).unwrap(); - region_a.add_subregion(region_c.clone(), 0).unwrap(); - region_b.add_subregion(region_d.clone(), 0).unwrap(); - region_b.add_subregion(region_e.clone(), 2000).unwrap(); + region_a.add_subregion(region_c, 0).unwrap(); + region_b.add_subregion(region_d, 0).unwrap(); + region_b.add_subregion(region_e, 2000).unwrap(); let addr_range = AddressRange::from((0u64, region_a.size())); let view = region_a @@ -1405,14 +1405,14 @@ mod test { let region_b = Region::init_container_region(5000, "region_b"); let region_c = Region::init_io_region(1000, default_ops.clone(), "regionc"); let region_d = Region::init_io_region(3000, default_ops.clone(), "region_d"); - let region_e = Region::init_io_region(2000, default_ops.clone(), "region_e"); + let region_e = Region::init_io_region(2000, default_ops, "region_e"); region_a.add_subregion(region_b.clone(), 2000).unwrap(); - region_a.add_subregion(region_c.clone(), 0).unwrap(); + region_a.add_subregion(region_c, 0).unwrap(); region_d.set_priority(2); region_e.set_priority(3); - region_b.add_subregion(region_d.clone(), 0).unwrap(); - region_b.add_subregion(region_e.clone(), 2000).unwrap(); + region_b.add_subregion(region_d, 0).unwrap(); + region_b.add_subregion(region_e, 2000).unwrap(); let addr_range = AddressRange::from((0u64, region_a.size())); let view = region_a diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index f3ca26b88..245bbe67b 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -315,7 +315,7 @@ mod test { )); let mut qcow2_cache = Qcow2Cache::new(2); - qcow2_cache.lru_replace(addr, entry.clone()); + qcow2_cache.lru_replace(addr, entry); qcow2_cache.lru_count = u64::MAX - cnt / 2; // Not in cache. assert!(qcow2_cache.get(0).is_none()); diff --git a/block_backend/src/qcow2/table.rs b/block_backend/src/qcow2/table.rs index c791cfa05..3abd1fe49 100644 --- a/block_backend/src/qcow2/table.rs +++ b/block_backend/src/qcow2/table.rs @@ -269,7 +269,7 @@ mod test { let addr = qcow2.alloc_cluster(1, true).unwrap(); let l2_cluster: Vec = vec![0_u8; cluster_size]; let l2_table = Rc::new(RefCell::new( - CacheTable::new(addr, l2_cluster.clone(), ENTRY_SIZE_U64).unwrap(), + CacheTable::new(addr, l2_cluster, ENTRY_SIZE_U64).unwrap(), )); qcow2.table.cache_l2_table(l2_table.clone()).unwrap(); diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 95c92558e..44308ef49 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -25,3 +25,4 @@ trace = { path = "../trace" } [features] default = [] vfio_device = [] +boot_time = [] diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 6a9f8f8f5..4df7feb98 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -252,7 +252,7 @@ mod tests { let irq_route_table = Mutex::new(IrqRouteTable::new(kvm_hyp.fd.as_ref().unwrap())); let irq_manager = Arc::new(KVMInterruptManager::new( true, - kvm_hyp.vm_fd.clone().unwrap(), + kvm_hyp.vm_fd.unwrap(), irq_route_table, )); let mut irq_route_table = irq_manager.irq_route_table.lock().unwrap(); diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index aab93039b..b05175c2c 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -633,7 +633,7 @@ mod test { return; } - let kml = KvmMemoryListener::new(4, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let kml = KvmMemoryListener::new(4, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots); let host_addr = 0u64; assert_eq!(kml.get_free_slot(0, 100, host_addr).unwrap(), 0); @@ -657,7 +657,7 @@ mod test { return; } - let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots); let ram_size = host_page_size(); let ram_fr1 = create_ram_range(0, ram_size, 0); @@ -683,7 +683,7 @@ mod test { return; } - let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots); // flat-range not aligned let page_size = host_page_size(); let ram_fr2 = create_ram_range(page_size, 2 * page_size, 1000); @@ -705,7 +705,7 @@ mod test { return; } - let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots); let evtfd = generate_region_ioeventfd(4, NoDatamatch); assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) @@ -751,7 +751,7 @@ mod test { return; } - let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots.clone()); + let kml = KvmMemoryListener::new(34, kvm_hyp.vm_fd.clone(), kvm_hyp.mem_slots); let evtfd_addr = 0x1000_u64; let mut evtfd = generate_region_ioeventfd(evtfd_addr, 64_u32); evtfd.addr_range.size = 3_u64; @@ -805,7 +805,7 @@ mod test { return; } - let iol = KvmIoListener::new(kvm_hyp.vm_fd.clone()); + let iol = KvmIoListener::new(kvm_hyp.vm_fd); let evtfd = generate_region_ioeventfd(4, NoDatamatch); assert!(iol .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index e57ad943d..209442baa 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -56,8 +56,6 @@ use crate::HypervisorError; #[cfg(target_arch = "aarch64")] use aarch64::cpu_caps::ArmCPUCaps as CPUCaps; use address_space::{AddressSpace, Listener}; -#[cfg(feature = "boot_time")] -use cpu::capture_boot_signal; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; use cpu::{ @@ -427,7 +425,7 @@ impl KvmCpu { #[cfg(target_arch = "x86_64")] VcpuExit::IoOut(addr, data) => { #[cfg(feature = "boot_time")] - capture_boot_signal(addr as u64, data); + cpu::capture_boot_signal(addr as u64, data); vm.lock().unwrap().pio_out(u64::from(addr), data); } @@ -436,7 +434,7 @@ impl KvmCpu { } VcpuExit::MmioWrite(addr, data) => { #[cfg(all(target_arch = "aarch64", feature = "boot_time"))] - capture_boot_signal(addr, data); + cpu::capture_boot_signal(addr, data); vm.lock().unwrap().mmio_write(addr, data); } @@ -1069,7 +1067,7 @@ mod test { vcpu_fd, )); let x86_cpu = Arc::new(Mutex::new(ArchCPU::new(0, 1))); - let cpu = CPU::new(hypervisor_cpu.clone(), 0, x86_cpu, vm.clone()); + let cpu = CPU::new(hypervisor_cpu.clone(), 0, x86_cpu, vm); // test `set_boot_config` function assert!(hypervisor_cpu .set_boot_config(cpu.arch().clone(), &cpu_config) @@ -1131,7 +1129,7 @@ mod test { hypervisor_cpu.clone(), 0, Arc::new(Mutex::new(ArchCPU::default())), - vm.clone(), + vm, ); let (cpu_state, _) = &*cpu.state; assert_eq!(*cpu_state.lock().unwrap(), CpuLifecycleState::Created); diff --git a/machine/Cargo.toml b/machine/Cargo.toml index c53a050e4..2c8a22b67 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -32,7 +32,7 @@ clap = { version = "=4.1.4", default-features = false, features = ["std", "deriv [features] default = [] -boot_time = ["cpu/boot_time"] +boot_time = ["cpu/boot_time", "hypervisor/boot_time"] scream = ["devices/scream", "machine_manager/scream"] scream_alsa = ["scream", "devices/scream_alsa", "machine_manager/scream_alsa"] scream_pulseaudio = ["scream", "devices/scream_pulseaudio","machine_manager/scream_pulseaudio"] diff --git a/machine/src/lib.rs b/machine/src/lib.rs index dd4bbc3ac..73ada4da4 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -35,6 +35,7 @@ use std::rc::Rc; use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak}; #[cfg(feature = "windows_emu_pid")] use std::time::Duration; +use std::u64; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; @@ -185,12 +186,9 @@ impl MachineBase { vm_config.machine_config.nr_threads, vm_config.machine_config.max_cpus, ); - let machine_ram = Arc::new(Region::init_container_region( - u64::max_value(), - "MachineRam", - )); + let machine_ram = Arc::new(Region::init_container_region(u64::MAX, "MachineRam")); let sys_mem = AddressSpace::new( - Region::init_container_region(u64::max_value(), "SysMem"), + Region::init_container_region(u64::MAX, "SysMem"), "sys_mem", Some(machine_ram.clone()), ) @@ -408,7 +406,7 @@ pub trait MachineOps: MachineLifecycle { } let zones = mem_config.mem_zones.as_ref().unwrap(); let mut offset = 0_u64; - for (_, node) in numa_nodes.as_ref().unwrap().iter().enumerate() { + for node in numa_nodes.as_ref().unwrap().iter() { for zone in zones.iter() { if zone.id.eq(&node.1.mem_dev) { let ram = create_backend_mem(zone, thread_num)?; @@ -698,7 +696,7 @@ pub trait MachineOps: MachineLifecycle { } fn add_virtio_balloon(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { - if vm_config.dev_name.get("balloon").is_some() { + if vm_config.dev_name.contains_key("balloon") { bail!("Only one balloon device is supported for each vm."); } let config = BalloonConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 70c36a490..f32bf7a1f 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -327,6 +327,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { .with_context(|| "Failed to register event notifier.") } + #[cfg(target_arch = "aarch64")] fn register_pause_event( &self, pause_req: Arc, @@ -355,6 +356,7 @@ pub(crate) trait StdMachineOps: AcpiBuilder + MachineOps { .with_context(|| "Failed to register event notifier.") } + #[cfg(target_arch = "aarch64")] fn register_resume_event( &self, resume_req: Arc, diff --git a/machine_manager/src/qmp/qmp_response.rs b/machine_manager/src/qmp/qmp_response.rs index 8db50f9af..bbf2d41ed 100644 --- a/machine_manager/src/qmp/qmp_response.rs +++ b/machine_manager/src/qmp/qmp_response.rs @@ -273,7 +273,7 @@ mod tests { let msg = ErrorMessage::new(&err_cls); assert_eq!(msg.desc, strange_msg); assert_eq!(msg.errorkind, "KVMMissingCap".to_string()); - let qmp_err = qmp_schema::QmpErrorClass::KVMMissingCap(strange_msg.clone()); + let qmp_err = qmp_schema::QmpErrorClass::KVMMissingCap(strange_msg); let resp = Response::create_error_response(qmp_err, None); assert_eq!(resp.error, Some(msg)); } diff --git a/tests/mod_test/src/libdriver/qcow2.rs b/tests/mod_test/src/libdriver/qcow2.rs index 61160a645..fb6ba7685 100644 --- a/tests/mod_test/src/libdriver/qcow2.rs +++ b/tests/mod_test/src/libdriver/qcow2.rs @@ -321,7 +321,7 @@ pub fn query_snapshot(state: Rc>) -> Value { // Check if there exists snapshot with the specified name. pub fn check_snapshot(state: Rc>, snap: &str) -> bool { - let value = query_snapshot(state.clone()); + let value = query_snapshot(state); let str = (*value.get("return").unwrap()).as_str().unwrap(); let lines: Vec<&str> = str.split("\r\n").collect(); for line in lines { diff --git a/tests/mod_test/src/libdriver/virtio.rs b/tests/mod_test/src/libdriver/virtio.rs index 991b5a6b8..c5d357e3b 100644 --- a/tests/mod_test/src/libdriver/virtio.rs +++ b/tests/mod_test/src/libdriver/virtio.rs @@ -505,7 +505,7 @@ impl TestVirtQueue { next: 0, }; self.add_elem_to_desc(test_state.clone(), desc_elem); - self.update_avail(test_state.clone(), free_head); + self.update_avail(test_state, free_head); free_head } @@ -536,7 +536,7 @@ impl TestVirtQueue { }; self.add_elem_to_desc(test_state.clone(), desc_elem); } - self.update_avail(test_state.clone(), free_head); + self.update_avail(test_state, free_head); free_head } @@ -557,7 +557,7 @@ impl TestVirtQueue { }; self.add_elem_to_desc(test_state.clone(), desc_elem); if !mixed { - self.update_avail(test_state.clone(), free_head); + self.update_avail(test_state, free_head); } free_head } diff --git a/tests/mod_test/src/libdriver/virtio_block.rs b/tests/mod_test/src/libdriver/virtio_block.rs index e284e3ad7..5cc5121c9 100644 --- a/tests/mod_test/src/libdriver/virtio_block.rs +++ b/tests/mod_test/src/libdriver/virtio_block.rs @@ -154,7 +154,7 @@ pub fn create_blk( let machine = TestStdMachine::new(test_state.clone()); let allocator = machine.allocator.clone(); - let virtio_blk = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let virtio_blk = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); virtio_blk.borrow_mut().init(pci_slot, pci_fn); @@ -233,7 +233,7 @@ pub fn add_blk_request( read = false; } // Get addr and write to Stratovirt. - let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, align); + let req_addr = virtio_blk_request(test_state.clone(), alloc, blk_req, align); // Desc elem: [addr, len, flags, next]. let data_addr = if align { @@ -259,9 +259,7 @@ pub fn add_blk_request( write: true, }); - let free_head = vq - .borrow_mut() - .add_chained(test_state.clone(), data_entries); + let free_head = vq.borrow_mut().add_chained(test_state, data_entries); (free_head, req_addr) } @@ -292,7 +290,7 @@ pub fn virtio_blk_write( .kick_virtqueue(test_state.clone(), virtqueue.clone()); blk.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut None, @@ -320,7 +318,7 @@ pub fn virtio_blk_read( ) { let (free_head, req_addr) = add_blk_request( test_state.clone(), - alloc.clone(), + alloc, virtqueue.clone(), VIRTIO_BLK_T_IN, sector, @@ -331,7 +329,7 @@ pub fn virtio_blk_read( .kick_virtqueue(test_state.clone(), virtqueue.clone()); blk.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut None, @@ -376,7 +374,7 @@ pub fn virtio_blk_read_write_zeroes( } read = false; } - let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + let req_addr = virtio_blk_request(test_state.clone(), alloc, blk_req, false); let data_addr = req_addr + u64::from(REQ_ADDR_LEN); let data_entries: Vec = vec![ TestVringDescEntry { @@ -401,7 +399,7 @@ pub fn virtio_blk_read_write_zeroes( blk.borrow().kick_virtqueue(test_state.clone(), vq.clone()); blk.borrow().poll_used_elem( test_state.clone(), - vq.clone(), + vq, free_head, TIMEOUT_US, &mut None, @@ -459,7 +457,7 @@ pub fn tear_down( vqs: Vec>>, image_path: Rc, ) { - blk.borrow_mut().destroy_device(alloc.clone(), vqs); + blk.borrow_mut().destroy_device(alloc, vqs); test_state.borrow_mut().stop(); if !image_path.is_empty() { cleanup_img(image_path.to_string()); diff --git a/tests/mod_test/src/libdriver/virtio_gpu.rs b/tests/mod_test/src/libdriver/virtio_gpu.rs index e985cc7db..ac5236d89 100644 --- a/tests/mod_test/src/libdriver/virtio_gpu.rs +++ b/tests/mod_test/src/libdriver/virtio_gpu.rs @@ -430,8 +430,8 @@ impl TestVirtioGpu { .borrow_mut() .setup_virtqueue_intr(2, self.allocator.clone(), cursor_q.clone()); - self.ctrl_q = ctrl_q.clone(); - self.cursor_q = cursor_q.clone(); + self.ctrl_q = ctrl_q; + self.cursor_q = cursor_q; self.device.borrow_mut().set_driver_ok(); } @@ -648,9 +648,9 @@ pub fn set_up( demo_dpy.borrow_mut().init(dpy_pci_slot); let virtgpu = Rc::new(RefCell::new(TestVirtioGpu::new( - machine.pci_bus.clone(), - allocator.clone(), - test_state.clone(), + machine.pci_bus, + allocator, + test_state, ))); virtgpu.borrow_mut().init(gpu_pci_slot, gpu_pci_fn); diff --git a/tests/mod_test/src/libdriver/virtio_pci_modern.rs b/tests/mod_test/src/libdriver/virtio_pci_modern.rs index 7e55fee2b..dfc0f2cee 100644 --- a/tests/mod_test/src/libdriver/virtio_pci_modern.rs +++ b/tests/mod_test/src/libdriver/virtio_pci_modern.rs @@ -597,7 +597,7 @@ impl VirtioDeviceOps for TestVirtioPciDev { let vq = virtqueue.borrow(); let idx: u16 = test_state.borrow().readw(vq.avail + 2); - if (!vq.event) || (idx > vq.get_avail_event(test_state.clone())) { + if (!vq.event) || (idx > vq.get_avail_event(test_state)) { self.virtqueue_notify(virtqueue.clone()); } } diff --git a/tests/mod_test/src/libdriver/virtio_rng.rs b/tests/mod_test/src/libdriver/virtio_rng.rs index 9b7520cda..082d714a0 100644 --- a/tests/mod_test/src/libdriver/virtio_rng.rs +++ b/tests/mod_test/src/libdriver/virtio_rng.rs @@ -49,7 +49,7 @@ pub fn create_rng( let machine = TestStdMachine::new(test_state.clone()); let allocator = machine.allocator.clone(); - let rng = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let rng = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); rng.borrow_mut().init(pci_slot, pci_fn); diff --git a/tests/mod_test/src/libdriver/vnc.rs b/tests/mod_test/src/libdriver/vnc.rs index 83ada8753..bf7034702 100644 --- a/tests/mod_test/src/libdriver/vnc.rs +++ b/tests/mod_test/src/libdriver/vnc.rs @@ -108,7 +108,7 @@ pub struct TestGpuCmd { // Encodings Type #[repr(u32)] -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum EncodingType { EncodingRaw = 0x00000000, EncodingCopyrect = 0x00000001, @@ -180,7 +180,7 @@ impl From for EncodingType { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum RfbServerMsg { FramebufferUpdate = 0, SetColourMapEntries = 1, @@ -784,23 +784,20 @@ impl VncClient { buf.drain(..auth_num as usize); self.write_msg((sec_type as u8).to_be_bytes().as_ref())?; - match sec_type { - TestAuthType::VncAuthNone => { - // Step 3. Handle_auth: Authstate::No, Server accept auth and client send share - // mode. - self.read_msg(&mut buf, 4)?; - if buf[..4].to_vec() != [0_u8; 4].to_vec() { - bail!("Reject by vnc server"); - } - self.write_msg(0_u8.to_be_bytes().as_ref())?; - buf.drain(..4); - - // Step 4. display mode information init: width + height + pixelformat + app_name. - self.read_msg(&mut buf, 24)?; - self.display_mod.from_bytes(&mut buf); - self.display_mod.check(); + if let TestAuthType::VncAuthNone = sec_type { + // Step 3. Handle_auth: Authstate::No, Server accept auth and client send share + // mode. + self.read_msg(&mut buf, 4)?; + if buf[..4].to_vec() != [0_u8; 4].to_vec() { + bail!("Reject by vnc server"); } - _ => {} + self.write_msg(0_u8.to_be_bytes().as_ref())?; + buf.drain(..4); + + // Step 4. display mode information init: width + height + pixelformat + app_name. + self.read_msg(&mut buf, 24)?; + self.display_mod.from_bytes(&mut buf); + self.display_mod.check(); } self.stream_read_to_end()?; println!("Connection established!"); @@ -1355,7 +1352,7 @@ pub fn set_up( } let input = Rc::new(RefCell::new(TestDemoInputDevice::new( - machine.pci_bus.clone(), + machine.pci_bus, allocator, ))); input.borrow_mut().init(input_conf.pci_slot); diff --git a/tests/mod_test/tests/balloon_test.rs b/tests/mod_test/tests/balloon_test.rs index 63dafe6c9..8fbc40028 100644 --- a/tests/mod_test/tests/balloon_test.rs +++ b/tests/mod_test/tests/balloon_test.rs @@ -126,7 +126,7 @@ impl VirtioBalloonTest { let machine = TestStdMachine::new_bymem(test_state.clone(), memsize * MBSIZE, page_size); let allocator = machine.allocator.clone(); - let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); dev.borrow_mut().init(pci_slot, 0); let features = dev.borrow_mut().get_device_features(); @@ -208,7 +208,7 @@ impl VirtioBalloonTest { let machine = TestStdMachine::new_bymem(test_state.clone(), 2 * MBSIZE, 4096); let allocator = machine.allocator.clone(); - let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); dev.borrow_mut().init(4, 0); let features = dev.borrow_mut().get_device_features(); @@ -567,12 +567,12 @@ fn balloon_feature_001() { let machine = TestStdMachine::new_bymem(test_state.clone(), 128 * MBSIZE, PAGE_SIZE_UNIT); let allocator = machine.allocator.clone(); - let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); dev.borrow_mut().init(pci_slot, pci_fn); dev.borrow_mut().pci_dev.enable_msix(None); dev.borrow_mut() - .setup_msix_configuration_vector(allocator.clone(), 0); + .setup_msix_configuration_vector(allocator, 0); let features = dev.borrow_mut().get_device_features(); @@ -614,12 +614,12 @@ fn balloon_feature_002() { let machine = TestStdMachine::new_bymem(test_state.clone(), 128 * MBSIZE, PAGE_SIZE_UNIT); let allocator = machine.allocator.clone(); - let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); dev.borrow_mut().init(pci_slot, pci_fn); dev.borrow_mut().pci_dev.enable_msix(None); dev.borrow_mut() - .setup_msix_configuration_vector(allocator.clone(), 0); + .setup_msix_configuration_vector(allocator, 0); let features = dev.borrow_mut().get_device_features(); @@ -705,7 +705,7 @@ fn balloon_fpr_fun(shared: bool) { .kick_virtqueue(balloon.state.clone(), fpr.clone()); balloon.device.borrow_mut().poll_used_elem( balloon.state.clone(), - fpr.clone(), + fpr, free_head, TIMEOUT_US, &mut None, @@ -1016,7 +1016,7 @@ fn auto_balloon_test_001() { .kick_virtqueue(balloon.state.clone(), auto_queue.clone()); balloon.device.borrow_mut().poll_used_elem( balloon.state.clone(), - auto_queue.clone(), + auto_queue, free_head, TIMEOUT_US, &mut None, diff --git a/tests/mod_test/tests/block_test.rs b/tests/mod_test/tests/block_test.rs index 78d590bf9..1f0a513c6 100644 --- a/tests/mod_test/tests/block_test.rs +++ b/tests/mod_test/tests/block_test.rs @@ -63,7 +63,7 @@ fn virtio_blk_discard_and_write_zeroes( TestVirtBlkReq::new(VIRTIO_BLK_T_WRITE_ZEROES, 1, 0, req_len) }; blk_req.data = unsafe { String::from_utf8_unchecked(req_data.to_vec()) }; - let req_addr = virtio_blk_request(test_state.clone(), alloc.clone(), blk_req, false); + let req_addr = virtio_blk_request(test_state.clone(), alloc, blk_req, false); let mut data_entries: Vec = Vec::with_capacity(3); data_entries.push(TestVringDescEntry { @@ -89,7 +89,7 @@ fn virtio_blk_discard_and_write_zeroes( if need_poll_elem { blk.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut None, @@ -153,7 +153,7 @@ fn virtio_blk_get_id( ) { let (free_head, req_addr) = add_blk_request( test_state.clone(), - alloc.clone(), + alloc, virtqueue.clone(), VIRTIO_BLK_T_GET_ID, 0, @@ -162,7 +162,7 @@ fn virtio_blk_get_id( blk.borrow().virtqueue_notify(virtqueue.clone()); blk.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut None, @@ -195,7 +195,7 @@ fn virtio_blk_flush( ) { let (free_head, req_addr) = add_blk_request( test_state.clone(), - alloc.clone(), + alloc, virtqueue.clone(), VIRTIO_BLK_T_FLUSH, sector, @@ -204,7 +204,7 @@ fn virtio_blk_flush( blk.borrow().virtqueue_notify(virtqueue.clone()); blk.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut None, @@ -226,7 +226,7 @@ fn virtio_blk_illegal_req( ) { let (free_head, req_addr) = add_blk_request( test_state.clone(), - alloc.clone(), + alloc, virtqueue.clone(), req_type, 0, @@ -235,7 +235,7 @@ fn virtio_blk_illegal_req( blk.borrow().virtqueue_notify(virtqueue.clone()); blk.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut None, @@ -2021,13 +2021,7 @@ fn blk_snapshot_basic() { true, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, virtqueues, image_path); } /// Block device whose backend file has snapshot sends I/O request. @@ -2052,13 +2046,7 @@ fn blk_snapshot_basic2() { .init_device(test_state.clone(), alloc.clone(), features, 1); create_snapshot(test_state.clone(), "drive0", "snap0"); assert!(check_snapshot(test_state.clone(), "snap0")); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - Rc::new("".to_string()), - ); + tear_down(blk, test_state, alloc, virtqueues, Rc::new("".to_string())); let device_args = Rc::new(String::from("")); let drive_args = Rc::new(String::from(",direct=false")); @@ -2116,11 +2104,5 @@ fn blk_snapshot_basic2() { true, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, virtqueues, image_path); } diff --git a/tests/mod_test/tests/fwcfg_test.rs b/tests/mod_test/tests/fwcfg_test.rs index da210b043..c51535a3c 100644 --- a/tests/mod_test/tests/fwcfg_test.rs +++ b/tests/mod_test/tests/fwcfg_test.rs @@ -163,7 +163,7 @@ fn test_filedir_by_dma() { bios_args(&mut args); let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let file_name = "etc/boot-fail-wait"; let mut read_data: Vec = Vec::with_capacity(mem::size_of::()); @@ -207,7 +207,7 @@ fn test_boot_index() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let file_name = "bootorder"; let mut read_data: Vec = Vec::with_capacity(dev_path.len()); @@ -240,7 +240,7 @@ fn test_smbios_type0() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let anchor_file = "etc/smbios/smbios-anchor"; let tables_file = "etc/smbios/smbios-tables"; @@ -304,7 +304,7 @@ fn test_smbios_type1() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let anchor_file = "etc/smbios/smbios-anchor"; let tables_file = "etc/smbios/smbios-tables"; @@ -409,7 +409,7 @@ fn test_smbios_type2() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let anchor_file = "etc/smbios/smbios-anchor"; let tables_file = "etc/smbios/smbios-tables"; @@ -484,7 +484,7 @@ fn test_smbios_type3() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let anchor_file = "etc/smbios/smbios-anchor"; let tables_file = "etc/smbios/smbios-tables"; @@ -559,7 +559,7 @@ fn test_smbios_type4() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let anchor_file = "etc/smbios/smbios-anchor"; let tables_file = "etc/smbios/smbios-tables"; @@ -644,7 +644,7 @@ fn test_smbios_type17() { let test_state = Rc::new(RefCell::new(test_init(args))); let machine = TestStdMachine::new(test_state.clone()); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; let anchor_file = "etc/smbios/smbios-anchor"; let tables_file = "etc/smbios/smbios-tables"; diff --git a/tests/mod_test/tests/memory_test.rs b/tests/mod_test/tests/memory_test.rs index 4b7544a1b..9c553949f 100644 --- a/tests/mod_test/tests/memory_test.rs +++ b/tests/mod_test/tests/memory_test.rs @@ -77,7 +77,7 @@ impl MemoryTest { let test_state = Rc::new(RefCell::new(test_init(extra_args))); let machine = TestStdMachine::new_bymem(test_state.clone(), memsize * 1024 * 1024, page_size); - let allocator = machine.allocator.clone(); + let allocator = machine.allocator; MemoryTest { state: test_state, @@ -98,7 +98,7 @@ fn ram_read_write(memory_test: &MemoryTest) { .state .borrow_mut() .memread(addr, str.len() as u64); - assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); + assert_eq!(str, String::from_utf8(ret).unwrap()); memory_test.state.borrow_mut().stop(); } @@ -655,7 +655,7 @@ fn ram_readwrite_numa() { let ret = test_state .borrow_mut() .memread(start_base, str.len() as u64); - assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); + assert_eq!(str, String::from_utf8(ret).unwrap()); test_state.borrow_mut().stop(); } @@ -716,7 +716,7 @@ fn ram_readwrite_numa1() { let ret = test_state .borrow_mut() .memread(start_base, str.len() as u64); - assert_eq!(str, String::from_utf8(ret.clone()).unwrap()); + assert_eq!(str, String::from_utf8(ret).unwrap()); test_state.borrow_mut().qmp("{\"execute\": \"query-mem\"}"); let file = File::create(RAM_DEV_PATH).unwrap(); diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index ff6a7c99b..19fba808e 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -445,7 +445,7 @@ pub fn create_net( let test_state = Rc::new(RefCell::new(test_init(extra_args))); let machine = TestStdMachine::new(test_state.clone()); let allocator = machine.allocator.clone(); - let virtio_net = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let virtio_net = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); virtio_net.borrow_mut().init(pci_slot, pci_fn); (virtio_net, test_state, allocator) @@ -490,7 +490,7 @@ fn tear_down( id: u8, mq: bool, ) { - net.borrow_mut().destroy_device(alloc.clone(), vqs); + net.borrow_mut().destroy_device(alloc, vqs); test_state.borrow_mut().stop(); clear_tap(id, mq); } @@ -507,7 +507,7 @@ fn fill_rx_vq( vq.borrow_mut() .add(test_state.clone(), addr, MAX_PACKET_LEN as u32, true); } - vq.borrow().set_used_event(test_state.clone(), 0); + vq.borrow().set_used_event(test_state, 0); } fn init_net_device( @@ -689,14 +689,8 @@ fn send_request( .borrow_mut() .add(test_state.clone(), addr, request.len() as u32, false); net.borrow().virtqueue_notify(vq.clone()); - net.borrow().poll_used_elem( - test_state.clone(), - vq, - free_head, - TIMEOUT_US, - &mut None, - true, - ); + net.borrow() + .poll_used_elem(test_state, vq, free_head, TIMEOUT_US, &mut None, true); } fn send_arp_request( @@ -710,17 +704,11 @@ fn send_arp_request( send_request( net.clone(), test_state.clone(), - alloc.clone(), + alloc, vqs[1].clone(), arp_request, ); - check_arp_mac( - net.clone(), - test_state.clone(), - vqs[0].clone(), - arp_request, - need_reply, - ); + check_arp_mac(net, test_state, vqs[0].clone(), arp_request, need_reply); } fn check_device_status(net: Rc>, status: u8) { @@ -766,14 +754,7 @@ fn virtio_net_rx_tx_test() { true, ); - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Send and receive packet test with iothread. @@ -807,14 +788,7 @@ fn virtio_net_rx_tx_test_iothread() { true, ); - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Test the control mq command. @@ -919,14 +893,7 @@ fn virtio_net_ctrl_mq_test() { assert_eq!(ack, status); } - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - true, - ); + tear_down(net, test_state, alloc, vqs, id, true); } /// Write or Read mac address from device config. @@ -1148,14 +1115,7 @@ fn ctrl_vq_set_mac_table( ctrl_data.len() ); - send_ctrl_vq_request( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs.clone(), - &ctrl_data, - ack, - ); + send_ctrl_vq_request(net, test_state, alloc, vqs, &ctrl_data, ack); } fn ctrl_vq_set_mac_address( @@ -1178,14 +1138,14 @@ fn ctrl_vq_set_mac_address( }; send_ctrl_vq_request( net.clone(), - test_state.clone(), - alloc.clone(), - vqs.clone(), + test_state, + alloc, + vqs, ctrl_mac_addr.as_bytes(), VIRTIO_NET_OK, ); // Check mac address result. - let config_mac = net_config_mac_rw(net.clone(), None); + let config_mac = net_config_mac_rw(net, None); assert_eq!(config_mac, ARP_SOURCE_MAC); } @@ -1309,14 +1269,7 @@ fn virtio_net_ctrl_vlan_test() { false, ); - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Test the control mac command. @@ -1692,14 +1645,7 @@ fn virtio_net_ctrl_abnormal_test() { check_device_status(net.clone(), VIRTIO_CONFIG_S_NEEDS_RESET); } - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Test the abnormal rx/tx request. @@ -1769,14 +1715,7 @@ fn virtio_net_abnormal_rx_tx_test() { assert!(time::Instant::now() - start_time < timeout_us); } - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Test the abnormal rx/tx request 2. @@ -1895,14 +1834,7 @@ fn virtio_net_set_abnormal_feature() { true, ); - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Send abnormal packet. @@ -1978,14 +1910,7 @@ fn virtio_net_send_abnormal_packet() { .qmp("{\"execute\": \"qmp_capabilities\"}"); assert_eq!(*ret.get("return").unwrap(), json!({})); - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } /// Send and receive packet test with mq. @@ -2020,14 +1945,7 @@ fn virtio_net_rx_tx_mq_test() { ); } - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - true, - ); + tear_down(net, test_state, alloc, vqs, id, true); } /// Test the abnormal rx/tx request 3. @@ -2096,12 +2014,5 @@ fn virtio_net_abnormal_rx_tx_test_3() { .readw(vqs[1].borrow().used + offset_of!(VringUsed, idx) as u64); assert_eq!(used_idx, 0); - tear_down( - net.clone(), - test_state.clone(), - alloc.clone(), - vqs, - id, - false, - ); + tear_down(net, test_state, alloc, vqs, id, false); } diff --git a/tests/mod_test/tests/pci_test.rs b/tests/mod_test/tests/pci_test.rs index 831d62886..dcad2ea4a 100644 --- a/tests/mod_test/tests/pci_test.rs +++ b/tests/mod_test/tests/pci_test.rs @@ -82,7 +82,7 @@ fn init_demo_dev(cfg: DemoDev, dev_num: u8) -> (Rc>, Rc Self { - let mut root_port = TestPciDev::new(machine.clone().borrow().pci_bus.clone()); + let mut root_port = TestPciDev::new(machine.borrow().pci_bus.clone()); root_port.set_bus_num(bus_num); root_port.devfn = devfn; assert_eq!(root_port.config_readw(PCI_SUB_CLASS_DEVICE), 0x0604); root_port.enable(); root_port.enable_msix(None); - let root_port_msix = MsixVector::new(0, alloc.clone()); + let root_port_msix = MsixVector::new(0, alloc); root_port.set_msix_vector( root_port_msix.msix_entry, root_port_msix.msix_addr, @@ -319,7 +319,7 @@ fn create_blk( pci_fn: u8, ) -> Rc> { let virtio_blk = Rc::new(RefCell::new(TestVirtioPciDev::new( - machine.clone().borrow().pci_bus.clone(), + machine.borrow().pci_bus.clone(), ))); virtio_blk.borrow_mut().pci_dev.set_bus_num(bus_num); virtio_blk.borrow_mut().init(pci_slot, pci_fn); @@ -405,10 +405,9 @@ fn tear_down( blk.clone().unwrap().borrow_mut().pci_dev.disable_msix(); } if vqs.is_some() { - blk.clone() - .unwrap() + blk.unwrap() .borrow_mut() - .destroy_device(alloc.clone(), vqs.unwrap()); + .destroy_device(alloc, vqs.unwrap()); } test_state.borrow_mut().stop(); @@ -564,12 +563,7 @@ fn validate_blk_io_success( .borrow_mut() .init_device(test_state.clone(), alloc.clone(), features, 1); - validate_std_blk_io( - blk.clone(), - test_state.clone(), - virtqueues.clone(), - alloc.clone(), - ); + validate_std_blk_io(blk.clone(), test_state, virtqueues.clone(), alloc.clone()); blk.borrow_mut().pci_dev.disable_msix(); blk.borrow() @@ -583,14 +577,14 @@ fn simple_blk_io_req( alloc: Rc>, ) -> u32 { let (free_head, _req_addr) = add_blk_request( - test_state.clone(), - alloc.clone(), + test_state, + alloc, virtqueue.clone(), VIRTIO_BLK_T_OUT, 0, false, ); - blk.borrow().virtqueue_notify(virtqueue.clone()); + blk.borrow().virtqueue_notify(virtqueue); free_head } @@ -629,14 +623,7 @@ fn validate_std_blk_io( false, ); - virtio_blk_read( - blk.clone(), - test_state.clone(), - alloc.clone(), - virtqueues[0].clone(), - 0, - false, - ); + virtio_blk_read(blk, test_state, alloc, virtqueues[0].clone(), 0, false); } fn wait_root_port_msix(root_port: Rc>) -> bool { @@ -766,7 +753,7 @@ fn hotplug_blk( // Hotplug a block device whose bdf is 2:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), bus, slot, func); + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, bus, slot, func); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -790,7 +777,7 @@ fn hotplug_blk( validate_hotplug(root_port.clone()); handle_isr(root_port.clone()); - power_on_device(root_port.clone()); + power_on_device(root_port); } fn hotunplug_blk( @@ -823,7 +810,7 @@ fn hotunplug_blk( "Wait for interrupt of root port timeout" ); validate_cmd_complete(root_port.clone()); - handle_isr(root_port.clone()); + handle_isr(root_port); // Verify the vendor id for the virtio block device. validate_config_value_2byte( blk.borrow().pci_dev.pci_bus.clone(), @@ -899,7 +886,7 @@ fn test_pci_device_discovery_001() { let (test_state, machine, alloc, image_paths) = set_up(blk_nums, root_port_nums, true, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // Verify the vendor id for non-existent devices. validate_config_value_2byte( @@ -972,12 +959,12 @@ fn test_pci_device_discovery_002() { ); // Hotplug a block device whose id is 0. - hotunplug_blk(test_state.clone(), blk.clone(), root_port_1.clone(), 0); + hotunplug_blk(test_state.clone(), blk, root_port_1, 0); // Hotplug a block device whose id is 1 and bdf is 2:0:0. hotplug_blk( test_state.clone(), - root_port_2.clone(), + root_port_2, &mut image_paths, 1, 2, @@ -986,7 +973,7 @@ fn test_pci_device_discovery_002() { ); // Create a block device whose bdf is 2:0:0. - let blk = create_blk(machine.clone(), 2, 0, 0); + let blk = create_blk(machine, 2, 0, 0); // Verify the vendor id for the virtio block device hotplugged. validate_config_value_2byte( blk.borrow().pci_dev.pci_bus.clone(), @@ -1010,7 +997,7 @@ fn test_pci_device_discovery_003() { // Create a root port whose bdf is 0:1:0. let root_port = Rc::new(RefCell::new(RootPort::new( - machine.clone(), + machine, alloc.clone(), 0, 1 << 3, @@ -1032,7 +1019,7 @@ fn test_pci_device_discovery_003() { // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(blk_id, hotplug_image_path.clone(), 1, 0, 0); + build_hotplug_blk_cmd(blk_id, hotplug_image_path, 1, 0, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); let ret = test_state.borrow().qmp(&add_device_command); @@ -1069,7 +1056,7 @@ fn test_pci_device_discovery_004() { let blk_id = 0; let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); - image_paths.push(hotplug_image_path.clone()); + image_paths.push(hotplug_image_path); // Hotplug a block device whose id is 0 and bdf is 1:0:0. hotplug_blk( @@ -1083,10 +1070,10 @@ fn test_pci_device_discovery_004() { ); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // Hotunplug the virtio block device whose id is 0. - hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), blk_id); + hotunplug_blk(test_state.clone(), blk, root_port, blk_id); tear_down(None, test_state, alloc, None, Some(image_paths)); } @@ -1099,7 +1086,7 @@ fn test_pci_type0_config() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // Verify that the vendor id of type0 device is read-only. validate_config_perm_2byte( @@ -1244,7 +1231,7 @@ fn test_pci_type1_config() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); + let root_port = RootPort::new(machine, alloc.clone(), 0, 1 << 3); assert_eq!(root_port.rp_dev.config_readb(PCI_PRIMARY_BUS), 0); assert_ne!(root_port.rp_dev.config_readb(PCI_SECONDARY_BUS), 0); @@ -1260,7 +1247,7 @@ fn test_pci_type1_reset() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); + let root_port = RootPort::new(machine, alloc.clone(), 0, 1 << 3); let command = root_port.rp_dev.config_readw(PCI_COMMAND); let cmd_memory = command & u16::from(PCI_COMMAND_MEMORY); @@ -1320,7 +1307,7 @@ fn test_out_size_config_access() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); + let root_port = RootPort::new(machine, alloc.clone(), 0, 1 << 3); let vendor_device_id = root_port.rp_dev.config_readl(PCI_VENDOR_ID); let command_status = root_port.rp_dev.config_readl(PCI_COMMAND); @@ -1341,7 +1328,7 @@ fn test_out_boundary_msix_access() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a root port whose bdf is 0:1:0. - let root_port = RootPort::new(machine.clone(), alloc.clone(), 0, 1 << 3); + let root_port = RootPort::new(machine, alloc.clone(), 0, 1 << 3); // Out-of-bounds access to the msix table. let write_value = u32::max_value(); @@ -1371,7 +1358,7 @@ fn test_repeat_io_map_bar() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), @@ -1406,7 +1393,7 @@ fn test_pci_type0_msix_config() { let root_port_nums = 0; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, false, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 0, 1, 0); + let blk = create_blk(machine, 0, 1, 0); // Verify that there is only one msix capability addr of the type0 pci device. let blk_cap_msix_addrs = lookup_all_cap_addr(PCI_CAP_ID_MSIX, blk.borrow().pci_dev.clone()); @@ -1472,7 +1459,7 @@ fn test_pci_msix_global_ctl() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -1546,7 +1533,7 @@ fn test_pci_msix_local_ctl() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -1586,7 +1573,7 @@ fn test_alloc_abnormal_vector() { let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // 1. Init device. blk.borrow_mut().reset(); @@ -1627,7 +1614,7 @@ fn test_intx_basic() { let root_port_nums = 1; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // 1. Init device. blk.borrow_mut().reset(); @@ -1686,7 +1673,7 @@ fn test_intx_disable() { let root_port_nums = 1; let (test_state, machine, alloc, image_paths) = set_up(root_port_nums, blk_nums, true, false); - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // 1. Init device. blk.borrow_mut().reset(); @@ -1782,18 +1769,10 @@ fn test_pci_hotplug_001() { ))); // Hotplug a block device whose id is 1 and bdf is 1:0:0. - hotplug_blk( - test_state.clone(), - root_port.clone(), - &mut image_paths, - 0, - 1, - 0, - 0, - ); + hotplug_blk(test_state.clone(), root_port, &mut image_paths, 0, 1, 0, 0); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -1833,7 +1812,7 @@ fn test_pci_hotplug_002() { // Hotplug a block device whose id is 1 and bdf is 1:0:0. hotplug_blk( test_state.clone(), - root_port_1.clone(), + root_port_1, &mut image_paths, 1, 1, @@ -1845,17 +1824,17 @@ fn test_pci_hotplug_002() { // Hotplug a block device whose id is 2 and bdf is 2:0:0. hotplug_blk( test_state.clone(), - root_port_2.clone(), + root_port_2, &mut image_paths, 2, 2, 0, 0, ); - let blk_2 = create_blk(machine.clone(), 2, 0, 0); + let blk_2 = create_blk(machine, 2, 0, 0); - validate_blk_io_success(blk_1.clone(), test_state.clone(), alloc.clone()); - validate_blk_io_success(blk_2.clone(), test_state.clone(), alloc.clone()); + validate_blk_io_success(blk_1, test_state.clone(), alloc.clone()); + validate_blk_io_success(blk_2, test_state.clone(), alloc.clone()); tear_down(None, test_state, alloc, None, Some(image_paths)); } @@ -1873,7 +1852,7 @@ fn test_pci_hotplug_003() { // Hotplug a block device whose id is 0, bdf is 1:1:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(0, hotplug_image_path.clone(), 1, 1, 0); + build_hotplug_blk_cmd(0, hotplug_image_path, 1, 1, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); // Verify that hotpluging the device in non-zero slot will fail. @@ -1896,7 +1875,7 @@ fn test_pci_hotplug_004() { let hotplug_blk_id = 1; let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), 0, 1, 0); + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, 0, 1, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); let ret = test_state.borrow().qmp(&add_device_command); @@ -1914,7 +1893,7 @@ fn test_pci_hotplug_005() { set_up(root_port_nums, blk_nums, true, false); let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); - image_paths.push(hotplug_image_path.clone()); + image_paths.push(hotplug_image_path); let hotplug_blk_id = 0; let (add_blk_command, add_device_command) = @@ -1981,7 +1960,7 @@ fn test_pci_hotplug_007() { // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), bus, slot, 0); + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, bus, slot, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); @@ -2005,10 +1984,10 @@ fn test_pci_hotplug_007() { validate_hotplug(root_port.clone()); handle_isr(root_port.clone()); - power_on_device(root_port.clone()); + power_on_device(root_port); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -2037,10 +2016,10 @@ fn test_pci_hotunplug_001() { ))); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // Hotunplug the block device whose bdf is 1:0:0. - hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), 0); + hotunplug_blk(test_state.clone(), blk, root_port, 0); tear_down(None, test_state, alloc, None, Some(image_paths)); } @@ -2078,7 +2057,7 @@ fn test_pci_hotunplug_003() { ))); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let unplug_blk_id = 0; // Hotunplug the block device attaching the root port. @@ -2129,7 +2108,7 @@ fn test_pci_hotunplug_003() { assert!(!(*ret.get("error").unwrap()).is_null()); // The block device will be unplugged when indicator of power and slot is power off. - power_off_device(root_port.clone()); + power_off_device(root_port); test_state.borrow().wait_qmp_event(); // Verify the vendor id for the virtio block device. @@ -2172,7 +2151,7 @@ fn test_pci_hotunplug_004() { let blk_1 = create_blk(machine.clone(), 1, 0, 0); // Create a block device whose bdf is 2:0:0. - let blk_2 = create_blk(machine.clone(), 2, 0, 0); + let blk_2 = create_blk(machine, 2, 0, 0); let unplug_blk_id = 0; let (delete_device_command, delete_blk_command_1) = build_hotunplug_blk_cmd(unplug_blk_id); @@ -2194,10 +2173,10 @@ fn test_pci_hotunplug_004() { "Wait for interrupt of root port timeout" ); - power_off_device(root_port_1.clone()); + power_off_device(root_port_1); test_state.borrow().wait_qmp_event(); - power_off_device(root_port_2.clone()); + power_off_device(root_port_2); test_state.borrow().wait_qmp_event(); // The block device will be unplugged when indicator of power and slot is power off. @@ -2245,10 +2224,10 @@ fn test_pci_hotunplug_005() { 1 << 3, ))); - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // Hotplug the block device whose id is 0 and bdf is 1:0:0. - hotunplug_blk(test_state.clone(), blk.clone(), root_port.clone(), 0); + hotunplug_blk(test_state.clone(), blk, root_port, 0); let (delete_device_command, _delete_blk_command) = build_hotunplug_blk_cmd(0); let ret = test_state.borrow().qmp(&delete_device_command); @@ -2289,7 +2268,7 @@ fn test_pci_hotunplug_007() { ))); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let unplug_blk_id = 0; // Hotunplug the block device attaching the root port. @@ -2303,7 +2282,7 @@ fn test_pci_hotunplug_007() { // The block device will be unplugged when indicator of power and slot is power off. power_off_device(root_port.clone()); // Trigger a 2nd write to PIC/PCC, which will be ignored by the device, and causes no harm. - power_off_device(root_port.clone()); + power_off_device(root_port); test_state.borrow().wait_qmp_event(); @@ -2339,7 +2318,7 @@ fn test_pci_hotunplug_008() { root_port.borrow_mut().rp_dev.set_intx_irq_num(1_u8); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); // Hotunplug the block device whose bdf is 1:0:0. let hotunplug_blk_id = 0; @@ -2366,7 +2345,7 @@ fn test_pci_hotunplug_008() { "Wait for interrupt of root port timeout" ); validate_cmd_complete(root_port.clone()); - handle_isr(root_port.clone()); + handle_isr(root_port); // Verify the vendor id for the virtio block device. validate_config_value_2byte( blk.borrow().pci_dev.pci_bus.clone(), @@ -2402,7 +2381,7 @@ fn test_pci_hotplug_combine_001() { // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, 1, 0, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); let ret = test_state.borrow().qmp(&add_device_command); @@ -2425,7 +2404,7 @@ fn test_pci_hotplug_combine_001() { 1, ); // Verify that the function of the block device is normal. - validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + validate_std_blk_io(blk.clone(), test_state.clone(), vqs, alloc.clone()); let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); @@ -2458,7 +2437,7 @@ fn test_pci_hotplug_combine_001() { // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + build_hotplug_blk_cmd(hotplug_blk_id, hotplug_image_path, 1, 0, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); let ret = test_state.borrow().qmp(&add_device_command); @@ -2482,7 +2461,7 @@ fn test_pci_hotplug_combine_001() { 0xFFFF, ); - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -2490,7 +2469,7 @@ fn test_pci_hotplug_combine_001() { 1, ); // Verify that the function of the block device is normal. - validate_std_blk_io(blk.clone(), test_state.clone(), vqs.clone(), alloc.clone()); + validate_std_blk_io(blk.clone(), test_state.clone(), vqs, alloc.clone()); let (delete_device_command, delete_blk_command) = build_hotunplug_blk_cmd(hotplug_blk_id); let ret = test_state.borrow().qmp(&delete_device_command); @@ -2500,7 +2479,7 @@ fn test_pci_hotplug_combine_001() { ); handle_isr(root_port.clone()); - power_off_device(root_port.clone()); + power_off_device(root_port); assert_eq!(*ret.get("return").unwrap(), json!({})); test_state.borrow().wait_qmp_event(); @@ -2551,7 +2530,7 @@ fn test_pci_hotplug_combine_002() { power_indicator_off(root_port.clone()); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); validate_blk_io_success(blk.clone(), test_state.clone(), alloc.clone()); @@ -2591,7 +2570,7 @@ fn test_pci_hotplug_combine_002() { ); handle_isr(root_port.clone()); - power_off_device(root_port.clone()); + power_off_device(root_port); test_state.borrow().wait_qmp_event(); let ret = test_state.borrow().qmp(&delete_blk_command); @@ -2643,25 +2622,25 @@ fn test_pci_hotplug_combine_003() { // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotunplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + build_hotplug_blk_cmd(hotunplug_blk_id, hotplug_image_path, 1, 0, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert_eq!(*ret.get("return").unwrap(), json!({})); let ret = test_state.borrow().qmp(&add_device_command); assert!(!(*ret.get("error").unwrap()).is_null()); - power_off_device(root_port.clone()); + power_off_device(root_port); test_state.borrow().wait_qmp_event(); let hotplug_image_path = create_img(TEST_IMAGE_SIZE, 1, &ImageType::Raw); image_paths.push(hotplug_image_path.clone()); // Hotplug a block device whose bdf is 1:0:0. let (add_blk_command, add_device_command) = - build_hotplug_blk_cmd(hotunplug_blk_id, hotplug_image_path.clone(), 1, 0, 0); + build_hotplug_blk_cmd(hotunplug_blk_id, hotplug_image_path, 1, 0, 0); let ret = test_state.borrow().qmp(&add_blk_command); assert!(!(*ret.get("error").unwrap()).is_null()); let ret = test_state.borrow().qmp(&add_device_command); assert_eq!(*ret.get("return").unwrap(), json!({})); - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let vqs = blk.borrow_mut().init_device( test_state.clone(), alloc.clone(), @@ -2773,7 +2752,7 @@ fn test_pci_root_port_exp_cap() { 0, ); // Create a block device whose bdf is 1:0:0. - let blk = create_blk(machine.clone(), 1, 0, 0); + let blk = create_blk(machine, 1, 0, 0); let nlw_mask = PCI_EXP_LNKSTA_NLW; let negotiated_link_width = (root_port.borrow().rp_dev.pci_bus.borrow().config_readw( @@ -2843,12 +2822,7 @@ fn test_pci_root_port_exp_cap() { ); // Hotplug the block device whose id is 0 and bdf is 1:0:0. - hotunplug_blk( - test_state.clone(), - blk.clone(), - root_port.clone(), - hotplug_blk_id, - ); + hotunplug_blk(test_state.clone(), blk, root_port.clone(), hotplug_blk_id); let dllla_mask = PCI_EXP_LNKSTA_DLLLA; validate_config_value_2byte( @@ -3001,7 +2975,7 @@ fn test_pci_combine_002() { wait_root_port_msix(root_port.clone()), "Wait for interrupt of root port timeout" ); - power_off_device(root_port.clone()); + power_off_device(root_port); // r/w mmio during hotunplug test_state.borrow().writeb(bar_addr, 5); diff --git a/tests/mod_test/tests/pvpanic_test.rs b/tests/mod_test/tests/pvpanic_test.rs index 01583a830..fd0849599 100644 --- a/tests/mod_test/tests/pvpanic_test.rs +++ b/tests/mod_test/tests/pvpanic_test.rs @@ -66,7 +66,7 @@ impl PvPanicDevCfg { let test_state = Rc::new(RefCell::new(test_init(test_machine_args))); let machine = Rc::new(RefCell::new(TestStdMachine::new(test_state.clone()))); - let mut pvpanic_pci_dev = TestPciDev::new(machine.clone().borrow().pci_bus.clone()); + let mut pvpanic_pci_dev = TestPciDev::new(machine.borrow().pci_bus.clone()); let devfn = self.addr << 3; pvpanic_pci_dev.devfn = devfn; diff --git a/tests/mod_test/tests/rng_test.rs b/tests/mod_test/tests/rng_test.rs index 3a120b579..9296ebb08 100644 --- a/tests/mod_test/tests/rng_test.rs +++ b/tests/mod_test/tests/rng_test.rs @@ -82,7 +82,7 @@ fn virtio_rng_read_batch( .kick_virtqueue(test_state.clone(), virtqueue.clone()); rng.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut len, @@ -123,7 +123,7 @@ fn virtio_rng_read_chained( .kick_virtqueue(test_state.clone(), virtqueue.clone()); rng.borrow().poll_used_elem( test_state.clone(), - virtqueue.clone(), + virtqueue, free_head, TIMEOUT_US, &mut len, @@ -142,7 +142,7 @@ fn tear_down( alloc: Rc>, vqs: Vec>>, ) { - rng.borrow_mut().destroy_device(alloc.clone(), vqs); + rng.borrow_mut().destroy_device(alloc, vqs); test_state.borrow_mut().stop(); } @@ -186,7 +186,7 @@ fn rng_read() { ); assert!(random_num_check(data)); - tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); + tear_down(rng, test_state, alloc, virtqueues); } /// Rng device batch read random numbers function test. @@ -229,7 +229,7 @@ fn rng_read_batch() { ); assert!(random_num_check(data)); - tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); + tear_down(rng, test_state, alloc, virtqueues); } /// Rng device rate limit random numbers reading test. @@ -300,7 +300,7 @@ fn rng_limited_rate() { RNG_DATA_BYTES ))); - tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); + tear_down(rng, test_state, alloc, virtqueues); } /// Rng device read a large number of random numbers test. @@ -344,7 +344,7 @@ fn rng_read_with_max() { ); assert!(random_num_check(data)); - tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); + tear_down(rng, test_state, alloc, virtqueues); } /// Rng device read/write config space. @@ -376,5 +376,5 @@ fn rng_rw_config() { let config = rng.borrow().config_readq(0); assert_ne!(config, 0xff); - tear_down(rng.clone(), test_state.clone(), alloc.clone(), virtqueues); + tear_down(rng, test_state, alloc, virtqueues); } diff --git a/tests/mod_test/tests/scsi_test.rs b/tests/mod_test/tests/scsi_test.rs index 443ce74f4..acbdf17a2 100644 --- a/tests/mod_test/tests/scsi_test.rs +++ b/tests/mod_test/tests/scsi_test.rs @@ -138,7 +138,7 @@ impl VirtioScsiTest { let scsi_devices: Vec = vec![ScsiDeviceConfig { cntlr_id: 0, device_type: scsi_type, - image_path: image_path.clone(), + image_path, target, lun, read_only: readonly, @@ -621,7 +621,7 @@ fn scsi_test_init( let machine = TestStdMachine::new(test_state.clone()); let allocator = machine.allocator.clone(); - let virtio_scsi = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let virtio_scsi = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); virtio_scsi.borrow_mut().init(pci_slot, pci_fn); (virtio_scsi, test_state, allocator) @@ -1874,7 +1874,7 @@ fn aio_model_test() { device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), + image_path, target, lun, read_only: false, @@ -1889,7 +1889,7 @@ fn aio_model_test() { device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), + image_path, target, lun, read_only: false, @@ -1908,7 +1908,7 @@ fn aio_model_test() { device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), + image_path, target, lun, read_only: false, @@ -1927,7 +1927,7 @@ fn aio_model_test() { device_vec.push(ScsiDeviceConfig { cntlr_id: 0, device_type: ScsiDeviceType::ScsiHd, - image_path: image_path.clone(), + image_path, target, lun, read_only: false, diff --git a/tests/mod_test/tests/serial_test.rs b/tests/mod_test/tests/serial_test.rs index 0efbc5295..8009f8f20 100644 --- a/tests/mod_test/tests/serial_test.rs +++ b/tests/mod_test/tests/serial_test.rs @@ -155,7 +155,7 @@ impl SerialTest { self.serial .borrow() - .kick_virtqueue(self.state.clone(), queue.clone()); + .kick_virtqueue(self.state.clone(), queue); (addr, free_head) } @@ -599,7 +599,7 @@ fn virtserialport_socket_basic() { nowait: true, }; let port = PortConfig { - chardev_type: socket.clone(), + chardev_type: socket, nr: 1, is_console: false, }; @@ -657,7 +657,7 @@ fn virtconsole_pty_err_out_control_msg() { }; let pci_slot = 0x04; let pci_fn = 0x0; - let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + let mut st = create_serial(vec![port], pci_slot, pci_fn); st.serial_init(); @@ -707,7 +707,7 @@ fn virtconsole_pty_invalid_in_control_buffer() { }; let pci_slot = 0x04; let pci_fn = 0x0; - let mut st = create_serial(vec![port.clone()], pci_slot, pci_fn); + let mut st = create_serial(vec![port], pci_slot, pci_fn); // Init virtqueues. st.virtqueue_setup(DEFAULT_SERIAL_VIRTQUEUES); @@ -776,7 +776,7 @@ fn virtserialport_socket_not_connect() { nowait: true, }; let port = PortConfig { - chardev_type: socket.clone(), + chardev_type: socket, nr, is_console: false, }; diff --git a/tests/mod_test/tests/usb_camera_test.rs b/tests/mod_test/tests/usb_camera_test.rs index 13ce8c2ba..98ac301a0 100644 --- a/tests/mod_test/tests/usb_camera_test.rs +++ b/tests/mod_test/tests/usb_camera_test.rs @@ -565,7 +565,7 @@ fn test_xhci_camera_hotplug_invalid() { #[cfg(target_env = "ohos")] assert_eq!(desc, "OH Camera: failed to init cameras"); // Invalid device id. - let value = qmp_unplug_camera(&test_state.clone(), "usbcam0"); + let value = qmp_unplug_camera(&test_state, "usbcam0"); let desc = value["error"]["desc"].as_str().unwrap().to_string(); assert_eq!(desc, "Failed to detach device: id usbcam0 not found"); // Invalid cameradev id. diff --git a/tests/mod_test/tests/virtio_test.rs b/tests/mod_test/tests/virtio_test.rs index 0c4a12f47..c306f3592 100644 --- a/tests/mod_test/tests/virtio_test.rs +++ b/tests/mod_test/tests/virtio_test.rs @@ -78,17 +78,12 @@ fn send_one_request( alloc: Rc>, vq: Rc>, ) { - let (free_head, req_addr) = add_request( - test_state.clone(), - alloc.clone(), - vq.clone(), - VIRTIO_BLK_T_OUT, - 0, - ); + let (free_head, req_addr) = + add_request(test_state.clone(), alloc, vq.clone(), VIRTIO_BLK_T_OUT, 0); blk.borrow().virtqueue_notify(vq.clone()); blk.borrow().poll_used_elem( test_state.clone(), - vq.clone(), + vq, free_head, TIMEOUT_US, &mut None, @@ -170,9 +165,7 @@ fn check_req_result( addr: u64, timeout_us: u64, ) { - let status = blk - .borrow() - .req_result(test_state.clone(), addr, timeout_us); + let status = blk.borrow().req_result(test_state, addr, timeout_us); assert!(!blk.borrow().queue_was_notified(vq)); assert_eq!(status, VIRTIO_BLK_S_OK); } @@ -288,13 +281,7 @@ fn do_event_idx_with_flag(flag: u16) { DEFAULT_IO_REQS * 2 - 1, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Feature Test. @@ -332,13 +319,7 @@ fn virtio_feature_none() { check_stratovirt_status(test_state.clone()); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Feature Test. @@ -414,13 +395,7 @@ fn virtio_feature_vertion_1() { DEFAULT_IO_REQS, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Driver just enable VIRTIO_F_VERSION_1|VIRTIO_RING_F_INDIRECT_DESC feature, @@ -526,13 +501,7 @@ fn virtio_feature_indirect() { "TEST" ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Driver just enable VIRTIO_F_VERSION_1|VIRTIO_RING_F_EVENT_IDX feature, @@ -674,13 +643,7 @@ fn virtio_feature_indirect_and_event_idx() { DEFAULT_IO_REQS * 2 - 1, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Setting abnormal status in device initialization. @@ -759,13 +722,7 @@ fn virtio_init_device_abnormal_status() { check_stratovirt_status(test_state.clone()); // 4. Destroy device. - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Setting abnormal feature in device initialization. @@ -1133,13 +1090,7 @@ fn virtio_init_device_out_of_order_1() { 0, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Init device out of order test 2. @@ -1192,13 +1143,7 @@ fn virtio_init_device_out_of_order_2() { 0, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Init device out of order test 3. @@ -1253,13 +1198,7 @@ fn virtio_init_device_out_of_order_3() { 0, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Repeat the initialization operation. @@ -1323,13 +1262,7 @@ fn virtio_init_device_repeat() { 0, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Setting abnormal desc addr in IO request. @@ -1622,13 +1555,7 @@ fn virtio_io_abnormal_desc_flags_2() { assert!(blk.borrow().get_status() & VIRTIO_CONFIG_S_NEEDS_RESET > 0); check_stratovirt_status(test_state.clone()); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Setting abnormal desc flag in IO request, testcase 3. @@ -1829,13 +1756,7 @@ fn virtio_io_abnormal_desc_elem_place() { check_stratovirt_status(test_state.clone()); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Setting (queue_size + 1) indirect desc elems in IO request. @@ -1904,13 +1825,7 @@ fn virtio_io_abnormal_indirect_desc_elem_num() { check_stratovirt_status(test_state.clone()); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Setting invalid flags to avail->flag in IO request. @@ -2180,13 +2095,7 @@ fn virtio_io_abnormal_used_idx() { true, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Virtio test step out of order, testcase 1. @@ -2250,13 +2159,7 @@ fn virtio_test_out_of_order_1() { check_stratovirt_status(test_state.clone()); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Virtio test step out of order, testcase 2. @@ -2278,13 +2181,7 @@ fn virtio_test_out_of_order_2() { 1, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); let (blk, test_state, alloc, image_path) = set_up(&ImageType::Raw); let vqs = blk.borrow_mut().init_device( @@ -2309,13 +2206,7 @@ fn virtio_test_out_of_order_2() { 0, ); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs, - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } /// Virtio test step repeat. @@ -2380,11 +2271,5 @@ fn virtio_test_repeat() { blk.borrow_mut().destroy_device(alloc.clone(), vqs.clone()); blk.borrow_mut().destroy_device(alloc.clone(), vqs.clone()); - tear_down( - blk.clone(), - test_state.clone(), - alloc.clone(), - vqs.clone(), - image_path.clone(), - ); + tear_down(blk, test_state, alloc, vqs, image_path); } diff --git a/tests/mod_test/tests/virtiofs_test.rs b/tests/mod_test/tests/virtiofs_test.rs index 99105aab3..6598a9c7e 100644 --- a/tests/mod_test/tests/virtiofs_test.rs +++ b/tests/mod_test/tests/virtiofs_test.rs @@ -75,7 +75,7 @@ fn env_prepare(temp: bool) -> (String, String, String) { .unwrap(); Command::new("mknod") - .arg(virtiofs_test_character_device.clone()) + .arg(virtiofs_test_character_device) .arg("c") .arg("1") .arg("1") @@ -132,7 +132,7 @@ impl VirtioFsTest { let machine = TestStdMachine::new_bymem(test_state.clone(), memsize * 1024 * 1024, page_size); let allocator = machine.allocator.clone(); - let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus.clone()))); + let dev = Rc::new(RefCell::new(TestVirtioPciDev::new(machine.pci_bus))); dev.borrow_mut().init(pci_slot, pci_fn); let features = virtio_fs_default_feature(dev.clone()); let queues = @@ -288,7 +288,7 @@ impl VirtioFsTest { } fn testcase_end(&self, test_dir: String) { - self.testcase_check_and_end(None, test_dir.clone()); + self.testcase_check_and_end(None, test_dir); } fn testcase_check_and_end(&self, absolute_virtiofs_sock: Option, test_dir: String) { @@ -297,7 +297,7 @@ impl VirtioFsTest { .destroy_device(self.allocator.clone(), self.queues.clone()); if let Some(path) = absolute_virtiofs_sock { - let path_clone = path.clone(); + let path_clone = path; let sock_path = Path::new(&path_clone); assert!(sock_path.exists()); self.state.borrow_mut().stop(); @@ -1118,7 +1118,7 @@ fn symlink_test() { assert!(link_path.is_symlink()); // Read link - let node_id = fuse_lookup(&fs, linkname.clone()); + let node_id = fuse_lookup(&fs, linkname); let len = size_of::() as u32; let fuse_in_head = FuseInHeader::new(len, FUSE_READLINK, 8, node_id, 0, 0, 0, 0); let fuse_out_head = FuseOutHeader::default(); @@ -1492,7 +1492,7 @@ fn openfile_test() { // start vm. let fs = VirtioFsTest::new(TEST_MEM_SIZE, TEST_PAGE_SIZE, virtiofs_sock); fuse_init(&fs); - let nodeid = fuse_lookup(&fs, file.clone()); + let nodeid = fuse_lookup(&fs, file); // open/write/flush/close/open/read/close let fh = fuse_open(&fs, nodeid); @@ -1802,7 +1802,7 @@ fn regularfile_xattr_test() { // The first attr is "security.selinux" let (_attr1, next1) = read_cstring(attr_list.clone(), 0); // The next attrs are what we set by FUSE_SETXATTR. Check it. - let (attr2, _next2) = read_cstring(attr_list.clone(), next1); + let (attr2, _next2) = read_cstring(attr_list, next1); assert_eq!(attr2.unwrap(), testattr_name); // REMOVEXATTR diff --git a/tests/mod_test/tests/vnc_test.rs b/tests/mod_test/tests/vnc_test.rs index d84230305..61868f9e0 100644 --- a/tests/mod_test/tests/vnc_test.rs +++ b/tests/mod_test/tests/vnc_test.rs @@ -785,7 +785,7 @@ fn test_set_encoding_abnormal(test_state: Rc>, port: u16) -> assert!(vnc_client.connect(TestAuthType::VncAuthNone).is_ok()); assert!(vnc_client.test_setup_encodings(Some(100), None).is_ok()); // Send a qmp to query vnc client state. - let value = qmp_query_vnc(test_state.clone()); + let value = qmp_query_vnc(test_state); let client_num = value["return"]["clients"].as_array().unwrap().len(); assert_eq!(client_num, 1); assert!(vnc_client.disconnect().is_ok()); @@ -805,7 +805,7 @@ fn test_client_cut_event(test_state: Rc>, port: u16) -> Resul }; assert!(vnc_client.test_send_client_cut(client_cut).is_ok()); // Send a qmp to query vnc client state. - let value = qmp_query_vnc(test_state.clone()); + let value = qmp_query_vnc(test_state); let client_num = value["return"]["clients"].as_array().unwrap().len(); assert_eq!(client_num, 1); assert!(vnc_client.disconnect().is_ok()); diff --git a/ui/src/console.rs b/ui/src/console.rs index 40c02c3fb..f4461c8c8 100644 --- a/ui/src/console.rs +++ b/ui/src/console.rs @@ -860,7 +860,7 @@ mod tests { assert!(console_close(&con_0).is_ok()); assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(1)); let dev_name3 = "test_device3".to_string(); - let con_3 = console_init(dev_name3, ConsoleType::Graphic, con_opts.clone()); + let con_3 = console_init(dev_name3, ConsoleType::Graphic, con_opts); assert_eq!(con_3.unwrap().upgrade().unwrap().lock().unwrap().con_id, 3); assert!(console_select(Some(0)).is_ok()); assert_eq!(CONSOLES.lock().unwrap().activate_id, Some(0)); @@ -891,10 +891,7 @@ mod tests { None, dcl_opts.clone(), ))); - let dcl_3 = Arc::new(Mutex::new(DisplayChangeListener::new( - None, - dcl_opts.clone(), - ))); + let dcl_3 = Arc::new(Mutex::new(DisplayChangeListener::new(None, dcl_opts))); assert!(register_display(&dcl_0).is_ok()); assert_eq!(dcl_0.lock().unwrap().dcl_id, Some(0)); diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index aa19e88ae..dbfa06b70 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1456,7 +1456,7 @@ mod tests { driver_features: bln.base.driver_features, mem_space: mem_space.clone(), inf_queue: queue1, - inf_evt: event_inf.clone(), + inf_evt: event_inf, def_queue: queue2, def_evt: event_def, report_queue: None, diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 7c9d512eb..58f8250ae 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -1956,12 +1956,9 @@ mod tests { // it is false when the index is more than the size of queue if let Err(err) = vring.add_used(&sys_space, QUEUE_SIZE, 100) { if let Some(e) = err.downcast_ref::() { - match e { - VirtioError::QueueIndex(offset, size) => { - assert_eq!(*offset, 256); - assert_eq!(*size, 256); - } - _ => (), + if let VirtioError::QueueIndex(offset, size) = e { + assert_eq!(*offset, 256); + assert_eq!(*size, 256); } } } diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index d656d2835..1bfaa2c20 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -1526,21 +1526,21 @@ mod tests { .set_queue_select(VIRTIO_DEVICE_QUEUE_NUM as u16 - 1); let queue_select = virtio_dev.lock().unwrap().queue_select(); virtio_dev.lock().unwrap().virtio_base_mut().queues_config[queue_select as usize] - .desc_table = GuestAddress(0xAABBCCDD_FFEEDDAA); + .desc_table = GuestAddress(0xAABB_CCDD_FFEE_DDAA); com_cfg_read_test!(virtio_pci, COMMON_Q_DESCLO_REG, 0xFFEEDDAA_u32); com_cfg_read_test!(virtio_pci, COMMON_Q_DESCHI_REG, 0xAABBCCDD_u32); // Read Queue's Available Ring address virtio_dev.lock().unwrap().set_queue_select(0); virtio_dev.lock().unwrap().virtio_base_mut().queues_config[0].avail_ring = - GuestAddress(0x11223344_55667788); + GuestAddress(0x1122_3344_5566_7788); com_cfg_read_test!(virtio_pci, COMMON_Q_AVAILLO_REG, 0x55667788_u32); com_cfg_read_test!(virtio_pci, COMMON_Q_AVAILHI_REG, 0x11223344_u32); // Read Queue's Used Ring address virtio_dev.lock().unwrap().set_queue_select(0); virtio_dev.lock().unwrap().virtio_base_mut().queues_config[0].used_ring = - GuestAddress(0x55667788_99AABBCC); + GuestAddress(0x5566_7788_99AA_BBCC); com_cfg_read_test!(virtio_pci, COMMON_Q_USEDLO_REG, 0x99AABBCC_u32); com_cfg_read_test!(virtio_pci, COMMON_Q_USEDHI_REG, 0x55667788_u32); } -- Gitee From b04e10c94676d288ed56fb95d451574f25751f36 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 17 Aug 2024 18:44:12 +0800 Subject: [PATCH 2033/2187] machine: Specify type of numeric literal Signed-off-by: Keqian Zhu --- machine/src/aarch64/standard.rs | 4 ++-- machine/src/micro_common/mod.rs | 2 +- machine/src/standard_common/mod.rs | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 693635070..a996a4a46 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -835,7 +835,7 @@ impl AcpiBuilder for StdMachine { dbg2.set_field(40, 1_u32); // Table 2. Debug Device Information structure format - let offset = 44; + let offset = 44_usize; // Revision dbg2.set_field(offset, 0_u8); // Length @@ -1147,7 +1147,7 @@ impl AcpiBuilder for StdMachine { loader: &mut TableLoader, ) -> Result { let mut pptt = AcpiTable::new(*b"PPTT", 2, *b"STRATO", *b"VIRTPPTT", 1); - let mut uid = 0; + let mut uid = 0_u32; self.build_pptt_sockets(&mut pptt, &mut uid); let pptt_begin = StdMachine::add_table_to_loader(acpi_data, loader, &pptt) .with_context(|| "Fail to add PPTT table to loader")?; diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 34c123184..9684259bf 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -764,7 +764,7 @@ impl DeviceInterface for LightMachine { fn device_add(&mut self, args: Box) -> Response { // get slot of bus by addr or lun - let mut slot = 0; + let mut slot = 0_usize; if let Some(addr) = args.addr.clone() { if let Ok(num) = str_to_num::(&addr) { slot = num; diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index f32bf7a1f..357ed1aa3 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -547,7 +547,7 @@ pub(crate) trait AcpiBuilder { { let mut mcfg = AcpiTable::new(*b"MCFG", 1, *b"STRATO", *b"VIRTMCFG", 1); // Bits 20~28 (totally 9 bits) in PCIE ECAM represents bus number. - let bus_number_mask = (1 << 9) - 1; + let bus_number_mask = (1u64 << 9) - 1; let ecam_addr: u64; let max_nr_bus: u64; #[cfg(target_arch = "x86_64")] @@ -610,31 +610,31 @@ pub(crate) trait AcpiBuilder { fadt.set_table_len(208_usize); // PM1A_EVENT bit, offset is 56. #[cfg(target_arch = "x86_64")] - fadt.set_field(56, 0x600); + fadt.set_field(56, 0x600_u32); // PM1A_CONTROL bit, offset is 64. #[cfg(target_arch = "x86_64")] - fadt.set_field(64, 0x604); + fadt.set_field(64, 0x604_u32); // PM_TMR_BLK bit, offset is 76. #[cfg(target_arch = "x86_64")] - fadt.set_field(76, 0x608); + fadt.set_field(76, 0x608_u32); // PM1_EVT_LEN, offset is 88. #[cfg(target_arch = "x86_64")] - fadt.set_field(88, 4); + fadt.set_field(88, 4_u8); // PM1_CNT_LEN, offset is 89. #[cfg(target_arch = "x86_64")] - fadt.set_field(89, 2); + fadt.set_field(89, 2_u8); // PM_TMR_LEN, offset is 91. #[cfg(target_arch = "x86_64")] - fadt.set_field(91, 4); + fadt.set_field(91, 4_u8); #[cfg(target_arch = "aarch64")] { // FADT flag: enable HW_REDUCED_ACPI bit on aarch64 plantform. - fadt.set_field(112, 1 << 20 | 1 << 10 | 1 << 8); + fadt.set_field(112, 1_u32 << 20 | 1_u32 << 10 | 1_u32 << 8); // ARM Boot Architecture Flags fadt.set_field(129, 0x3_u16); } // FADT minor revision - fadt.set_field(131, 3); + fadt.set_field(131, 3_u8); // X_PM_TMR_BLK bit, offset is 208. #[cfg(target_arch = "x86_64")] fadt.append_child(&AcpiGenericAddress::new_io_address(0x608_u32).aml_bytes()); @@ -644,7 +644,7 @@ pub(crate) trait AcpiBuilder { #[cfg(target_arch = "x86_64")] { // FADT flag: disable HW_REDUCED_ACPI bit on x86 plantform. - fadt.set_field(112, 1 << 10 | 1 << 8); + fadt.set_field(112, 1_u32 << 10 | 1_u32 << 8); // Reset Register bit, offset is 116. fadt.set_field(116, 0x01_u8); fadt.set_field(117, 0x08_u8); -- Gitee From 2f62453648163b3be7daa994dcd9299c07621e39 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Aug 2024 13:09:33 +0800 Subject: [PATCH 2034/2187] machine: Verify arithmetic side effects Signed-off-by: Keqian Zhu --- cpu/src/lib.rs | 16 +++--- cpu/src/x86_64/mod.rs | 36 ++++++++------ machine/src/aarch64/micro.rs | 6 ++- machine/src/aarch64/standard.rs | 6 ++- machine/src/lib.rs | 6 ++- machine/src/micro_common/mod.rs | 12 ++--- machine/src/standard_common/mod.rs | 15 +++++- machine/src/x86_64/micro.rs | 2 + machine/src/x86_64/standard.rs | 15 +++++- machine_manager/src/config/machine_config.rs | 51 ++++++++++++-------- 10 files changed, 107 insertions(+), 58 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index b141334ea..5b70ef75f 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -601,16 +601,18 @@ impl CpuTopology { /// # Arguments /// /// * `vcpu_id` - ID of vcpu. - fn get_topo_item(&self, vcpu_id: usize) -> (u8, u8, u8, u8, u8) { - let socketid: u8 = vcpu_id as u8 / (self.dies * self.clusters * self.cores * self.threads); - let dieid: u8 = (vcpu_id as u8 / (self.clusters * self.cores * self.threads)) % self.dies; - let clusterid: u8 = (vcpu_id as u8 / (self.cores * self.threads)) % self.clusters; - let coreid: u8 = (vcpu_id as u8 / self.threads) % self.cores; - let threadid: u8 = vcpu_id as u8 % self.threads; + fn get_topo_item(&self, vcpu_id: u8) -> (u8, u8, u8, u8, u8) { + // nr_cpus is no more than u8::MAX, multiply will not overflow. + // nr_xxx is no less than 1, div and mod operations will not panic. + let socketid: u8 = vcpu_id / (self.dies * self.clusters * self.cores * self.threads); + let dieid: u8 = (vcpu_id / (self.clusters * self.cores * self.threads)) % self.dies; + let clusterid: u8 = (vcpu_id / (self.cores * self.threads)) % self.clusters; + let coreid: u8 = (vcpu_id / self.threads) % self.cores; + let threadid: u8 = vcpu_id % self.threads; (socketid, dieid, clusterid, coreid, threadid) } - pub fn get_topo_instance_for_qmp(&self, cpu_index: usize) -> qmp_schema::CpuInstanceProperties { + pub fn get_topo_instance_for_qmp(&self, cpu_index: u8) -> qmp_schema::CpuInstanceProperties { let (socketid, _dieid, _clusterid, coreid, threadid) = self.get_topo_item(cpu_index); qmp_schema::CpuInstanceProperties { node_id: None, diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 9bf913131..7b0773780 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -123,11 +123,11 @@ impl X86CPUTopology { #[derive(Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] pub struct X86CPUState { - max_vcpus: u32, - nr_threads: u32, - nr_cores: u32, - nr_dies: u32, - nr_sockets: u32, + max_vcpus: u8, + nr_threads: u8, + nr_cores: u8, + nr_dies: u8, + nr_sockets: u8, pub apic_id: u32, pub regs: Regs, pub sregs: Sregs, @@ -177,7 +177,7 @@ impl X86CPUState { /// /// * `vcpu_id` - ID of this `CPU`. /// * `max_vcpus` - Number of vcpus. - pub fn new(vcpu_id: u32, max_vcpus: u32) -> Self { + pub fn new(vcpu_id: u32, max_vcpus: u8) -> Self { let mp_state = MpState { mp_state: if vcpu_id == 0 { MP_STATE_RUNNABLE @@ -221,9 +221,9 @@ impl X86CPUState { /// /// * `topology` - X86 CPU Topology pub fn set_cpu_topology(&mut self, topology: &X86CPUTopology) -> Result<()> { - self.nr_threads = u32::from(topology.threads); - self.nr_cores = u32::from(topology.cores); - self.nr_dies = u32::from(topology.dies); + self.nr_threads = topology.threads; + self.nr_cores = topology.cores; + self.nr_dies = topology.dies; Ok(()) } @@ -359,6 +359,7 @@ impl X86CPUState { data, ..Default::default() }; + // usize is enough for storing msr len. self.msr_len += 1; } } @@ -392,6 +393,7 @@ impl X86CPUState { } pub fn setup_cpuid(&self, cpuid: &mut CpuId) -> Result<()> { + // nr_xx is no less than 1. let core_offset = 32u32 - (self.nr_threads - 1).leading_zeros(); let die_offset = (32u32 - (self.nr_cores - 1).leading_zeros()) + core_offset; let pkg_offset = (32u32 - (self.nr_dies - 1).leading_zeros()) + die_offset; @@ -430,7 +432,8 @@ impl X86CPUState { ); entry.eax &= !0xfc00_0000; if entry.eax & 0x0001_ffff != 0 && self.max_vcpus > 1 { - entry.eax |= (self.max_vcpus - 1) << 26; + // max_vcpus is no less than 1. + entry.eax |= (u32::from(self.max_vcpus) - 1) << 26; } } 6 => { @@ -452,12 +455,13 @@ impl X86CPUState { match entry.index { 0 => { entry.eax = core_offset; - entry.ebx = self.nr_threads; + entry.ebx = u32::from(self.nr_threads); entry.ecx |= ECX_THREAD; } 1 => { entry.eax = pkg_offset; - entry.ebx = self.nr_threads * self.nr_cores; + // nr_cpus is no more than u8::MAX, multiply will not overflow. + entry.ebx = u32::from(self.nr_threads * self.nr_cores); entry.ecx |= ECX_CORE; } _ => { @@ -483,17 +487,19 @@ impl X86CPUState { match entry.index { 0 => { entry.eax = core_offset; - entry.ebx = self.nr_threads; + entry.ebx = u32::from(self.nr_threads); entry.ecx |= ECX_THREAD; } 1 => { entry.eax = die_offset; - entry.ebx = self.nr_cores * self.nr_threads; + // nr_cpus is no more than u8::MAX, multiply will not overflow. + entry.ebx = u32::from(self.nr_cores * self.nr_threads); entry.ecx |= ECX_CORE; } 2 => { entry.eax = pkg_offset; - entry.ebx = self.nr_dies * self.nr_cores * self.nr_threads; + // nr_cpus is no more than u8::MAX, multiply will not overflow. + entry.ebx = u32::from(self.nr_dies * self.nr_cores * self.nr_threads); entry.ecx |= ECX_DIE; } _ => { diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 6afc15e21..6fd87d765 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -279,7 +279,11 @@ impl CompileFDTHelper for LightMachine { match &boot_source.initrd { Some(initrd) => { fdt.set_property_u64("linux,initrd-start", initrd.initrd_addr)?; - fdt.set_property_u64("linux,initrd-end", initrd.initrd_addr + initrd.initrd_size)?; + let initrd_end = initrd + .initrd_addr + .checked_add(initrd.initrd_size) + .with_context(|| "initrd end overflow")?; + fdt.set_property_u64("linux,initrd-end", initrd_end)?; } None => {} } diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index a996a4a46..9feab83ab 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -1290,7 +1290,11 @@ impl CompileFDTHelper for StdMachine { match &boot_source.initrd { Some(initrd) => { fdt.set_property_u64("linux,initrd-start", initrd.initrd_addr)?; - fdt.set_property_u64("linux,initrd-end", initrd.initrd_addr + initrd.initrd_size)?; + let initrd_end = initrd + .initrd_addr + .checked_add(initrd.initrd_size) + .with_context(|| "initrd end overflow")?; + fdt.set_property_u64("linux,initrd-end", initrd_end)?; } None => {} } diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 73ada4da4..39fb7ef2d 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -411,7 +411,9 @@ pub trait MachineOps: MachineLifecycle { if zone.id.eq(&node.1.mem_dev) { let ram = create_backend_mem(zone, thread_num)?; root.add_subregion_not_update(ram, offset)?; - offset += zone.size; + offset = offset + .checked_add(zone.size) + .with_context(|| "total zone size overflow")?; break; } } @@ -477,7 +479,7 @@ pub trait MachineOps: MachineLifecycle { #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); #[cfg(target_arch = "x86_64")] - let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(max_cpus)); + let arch_cpu = ArchCPU::new(u32::from(vcpu_id), max_cpus); let cpu = Arc::new(CPU::new( hypervisor_cpu, diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 9684259bf..3ed04c094 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -642,7 +642,7 @@ impl DeviceInterface for LightMachine { for cpu_index in 0..cpu_topo.max_cpus { if cpu_topo.get_mask(cpu_index as usize) == 1 { let thread_id = cpus[cpu_index as usize].tid(); - let cpu_instance = cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); + let cpu_instance = cpu_topo.get_topo_instance_for_qmp(cpu_index); let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") @@ -683,10 +683,7 @@ impl DeviceInterface for LightMachine { for cpu_index in 0..self.base.cpu_topo.max_cpus { if self.base.cpu_topo.get_mask(cpu_index as usize) == 0 { - let cpu_instance = self - .base - .cpu_topo - .get_topo_instance_for_qmp(cpu_index as usize); + let cpu_instance = self.base.cpu_topo.get_topo_instance_for_qmp(cpu_index); let hotpluggable_cpu = qmp_schema::HotpluggableCPU { type_: cpu_type.clone(), vcpus_count: 1, @@ -695,10 +692,7 @@ impl DeviceInterface for LightMachine { }; hotplug_vec.push(serde_json::to_value(hotpluggable_cpu).unwrap()); } else { - let cpu_instance = self - .base - .cpu_topo - .get_topo_instance_for_qmp(cpu_index as usize); + let cpu_instance = self.base.cpu_topo.get_topo_instance_for_qmp(cpu_index); let hotpluggable_cpu = qmp_schema::HotpluggableCPU { type_: cpu_type.clone(), vcpus_count: 1, diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 357ed1aa3..0bfd64668 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -409,6 +409,7 @@ pub(crate) trait AcpiBuilder { loader.add_cksum_entry( ACPI_TABLE_FILE, + // table_begin is much less than u32::MAX, will not overflow. table_begin + TABLE_CHECKSUM_OFFSET, table_begin, table_end - table_begin, @@ -568,7 +569,7 @@ pub(crate) trait AcpiBuilder { mcfg.append_child(ecam_addr.as_bytes()); // PCI Segment Group Number mcfg.append_child(0_u16.as_bytes()); - // Start Bus Number and End Bus Number + // Start Bus Number and End Bus Number. max_nr_bus is no less than 1. mcfg.append_child(&[0_u8, (max_nr_bus - 1) as u8]); // Reserved mcfg.append_child(&[0_u8; 4]); @@ -581,6 +582,7 @@ pub(crate) trait AcpiBuilder { loader.add_cksum_entry( ACPI_TABLE_FILE, + // mcfg_begin is much less than u32::MAX, will not overflow. mcfg_begin + TABLE_CHECKSUM_OFFSET, mcfg_begin, mcfg_end - mcfg_begin, @@ -680,6 +682,7 @@ pub(crate) trait AcpiBuilder { let facs_size = 4_u8; loader.add_pointer_entry( ACPI_TABLE_FILE, + // fadt_begin is much less than u32::MAX, will not overflow. fadt_begin + facs_offset, facs_size, ACPI_TABLE_FILE, @@ -692,6 +695,7 @@ pub(crate) trait AcpiBuilder { let xdsdt_size = 8_u8; loader.add_pointer_entry( ACPI_TABLE_FILE, + // fadt_begin is much less than u32::MAX, will not overflow. fadt_begin + xdsdt_offset, xdsdt_size, ACPI_TABLE_FILE, @@ -700,6 +704,7 @@ pub(crate) trait AcpiBuilder { loader.add_cksum_entry( ACPI_TABLE_FILE, + // fadt_begin is much less than u32::MAX, will not overflow. fadt_begin + TABLE_CHECKSUM_OFFSET, fadt_begin, fadt_end - fadt_begin, @@ -833,6 +838,7 @@ pub(crate) trait AcpiBuilder { { let mut xsdt = AcpiTable::new(*b"XSDT", 1, *b"STRATO", *b"VIRTXSDT", 1); + // usize is enough for storing table len. xsdt.set_table_len(xsdt.table_len() + size_of::() * xsdt_entries.len()); let mut locked_acpi_data = acpi_data.lock().unwrap(); @@ -848,16 +854,19 @@ pub(crate) trait AcpiBuilder { for entry in xsdt_entries { loader.add_pointer_entry( ACPI_TABLE_FILE, + // xsdt_begin is much less than u32::MAX, will not overflow. xsdt_begin + entry_offset, entry_size, ACPI_TABLE_FILE, entry as u32, )?; + // u32 is enough for storing offset. entry_offset += u32::from(entry_size); } loader.add_cksum_entry( ACPI_TABLE_FILE, + // xsdt_begin is much less than u32::MAX, will not overflow. xsdt_begin + TABLE_CHECKSUM_OFFSET, xsdt_begin, xsdt_end - xsdt_begin, @@ -931,6 +940,7 @@ impl StdMachine { bail!("Cpu-id {} already exist.", cpu_id) } if cpu_id >= max_cpus { + // max_cpus is no less than 1. bail!("Max cpu-id is {}", max_cpus - 1) } @@ -1100,7 +1110,7 @@ impl DeviceInterface for StdMachine { for cpu_index in 0..cpu_topo.max_cpus { if cpu_topo.get_mask(cpu_index as usize) == 1 { let thread_id = cpus[cpu_index as usize].tid(); - let cpu_instance = cpu_topo.get_topo_instance_for_qmp(cpu_index as usize); + let cpu_instance = cpu_topo.get_topo_instance_for_qmp(cpu_index); let cpu_common = qmp_schema::CpuInfoCommon { current: true, qom_path: String::from("/machine/unattached/device[") @@ -1616,6 +1626,7 @@ impl DeviceInterface for StdMachine { } for (i, data) in data.iter_mut().enumerate().take(std::mem::size_of::()) { + // i is less than 8, multiply will not overflow. *data = (self.head >> (8 * i)) as u8; } true diff --git a/machine/src/x86_64/micro.rs b/machine/src/x86_64/micro.rs index 5747076ae..80c3c1b9b 100644 --- a/machine/src/x86_64/micro.rs +++ b/machine/src/x86_64/micro.rs @@ -95,6 +95,7 @@ impl MachineOps for LightMachine { let boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); + // MEM_LAYOUT is defined statically, will not overflow. let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; let gap_end = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; @@ -103,6 +104,7 @@ impl MachineOps for LightMachine { initrd, kernel_cmdline: boot_source.kernel_cmdline.to_string(), cpu_count: self.base.cpu_topo.nrcpus, + // gap_end is bigger than gap_start, as MEM_LAYOUT is defined statically. gap_range: (gap_start, gap_end - gap_start), ioapic_addr: MEM_LAYOUT[LayoutEntryType::IoApic as usize].0 as u32, lapic_addr: MEM_LAYOUT[LayoutEntryType::LocalApic as usize].0 as u32, diff --git a/machine/src/x86_64/standard.rs b/machine/src/x86_64/standard.rs index da509da71..610078551 100644 --- a/machine/src/x86_64/standard.rs +++ b/machine/src/x86_64/standard.rs @@ -411,6 +411,7 @@ impl MachineOps for StdMachine { let boot_source = self.base.boot_source.lock().unwrap(); let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone()); + // MEM_LAYOUT is defined statically, will not overflow. let gap_start = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; let gap_end = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; @@ -419,6 +420,7 @@ impl MachineOps for StdMachine { initrd, kernel_cmdline: boot_source.kernel_cmdline.to_string(), cpu_count: self.base.cpu_topo.nrcpus, + // gap_end is bigger than gap_start, as MEM_LAYOUT is defined statically. gap_range: (gap_start, gap_end - gap_start), ioapic_addr: MEM_LAYOUT[LayoutEntryType::IoApic as usize].0 as u32, lapic_addr: MEM_LAYOUT[LayoutEntryType::LocalApic as usize].0 as u32, @@ -441,6 +443,7 @@ impl MachineOps for StdMachine { let mut rtc = RTC::new(&self.base.sysbus).with_context(|| "Failed to create RTC device")?; rtc.set_memory( mem_size, + // MEM_LAYOUT is defined statically, will not overflow. MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1, ); @@ -590,7 +593,10 @@ impl MachineOps for StdMachine { // KiB is for BIOS code which is stored in the first PFlash. let rom_base = 0xe0000; let rom_size = 0x20000; - file.as_ref().seek(SeekFrom::Start(pfl_size - rom_size))?; + let seek_start = pfl_size + .checked_sub(rom_size) + .with_context(|| "pflash file size less than rom size")?; + file.as_ref().seek(SeekFrom::Start(seek_start))?; let ram1 = Arc::new(HostMemMapping::new( GuestAddress(rom_base), @@ -614,6 +620,9 @@ impl MachineOps for StdMachine { let sector_len: u32 = 1024 * 4; let backend = Some(file); + let region_base = flash_end + .checked_sub(pfl_size) + .with_context(|| "flash end is less than flash size")?; let pflash = PFlash::new( pfl_size, backend, @@ -622,12 +631,13 @@ impl MachineOps for StdMachine { 1_u32, config.readonly, &self.base.sysbus, - flash_end - pfl_size, + region_base, ) .with_context(|| MachineError::InitPflashErr)?; pflash .realize() .with_context(|| MachineError::RlzPflashErr)?; + // sub has been checked above. flash_end -= pfl_size; } @@ -822,6 +832,7 @@ impl AcpiBuilder for StdMachine { node: &NumaNode, srat: &mut AcpiTable, ) -> u64 { + // MEM_LAYOUT is defined statically, will not overflow. let mem_below_4g = MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].0 + MEM_LAYOUT[LayoutEntryType::MemBelow4g as usize].1; let mem_above_4g = MEM_LAYOUT[LayoutEntryType::MemAbove4g as usize].0; diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 6163f111d..96ce5a11a 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -31,8 +31,8 @@ const DEFAULT_CLUSTERS: u8 = 1; const DEFAULT_SOCKETS: u8 = 1; const DEFAULT_MAX_CPUS: u8 = 1; const DEFAULT_MEMSIZE: u64 = 256; -const MAX_NR_CPUS: u64 = 254; -const MIN_NR_CPUS: u64 = 1; +const MAX_NR_CPUS: u8 = 254; +const MIN_NR_CPUS: u8 = 1; const MAX_MEMSIZE: u64 = 549_755_813_888; const MIN_MEMSIZE: u64 = 134_217_728; pub const K: u64 = 1024; @@ -291,20 +291,20 @@ struct MachineCmdConfig { #[derive(Parser)] #[command(no_binary_name(true))] struct SmpConfig { - #[arg(long, alias = "classtype", value_parser = clap::value_parser!(u64).range(MIN_NR_CPUS..=MAX_NR_CPUS))] - cpus: u64, + #[arg(long, alias = "classtype", value_parser = clap::value_parser!(u8).range(i64::from(MIN_NR_CPUS)..=i64::from(MAX_NR_CPUS)))] + cpus: u8, #[arg(long, default_value = "0")] - maxcpus: u64, - #[arg(long, default_value = "0", value_parser = clap::value_parser!(u64).range(..u8::MAX as u64))] - sockets: u64, - #[arg(long, default_value = "1", value_parser = clap::value_parser!(u64).range(1..u8::MAX as u64))] - dies: u64, - #[arg(long, default_value = "1", value_parser = clap::value_parser!(u64).range(1..u8::MAX as u64))] - clusters: u64, - #[arg(long, default_value = "0", value_parser = clap::value_parser!(u64).range(..u8::MAX as u64))] - cores: u64, - #[arg(long, default_value = "0", value_parser = clap::value_parser!(u64).range(..u8::MAX as u64))] - threads: u64, + maxcpus: u8, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u8).range(..i64::from(u8::MAX)))] + sockets: u8, + #[arg(long, default_value = "1", value_parser = clap::value_parser!(u8).range(1..i64::from(u8::MAX)))] + dies: u8, + #[arg(long, default_value = "1", value_parser = clap::value_parser!(u8).range(1..i64::from(u8::MAX)))] + clusters: u8, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u8).range(..i64::from(u8::MAX)))] + cores: u8, + #[arg(long, default_value = "0", value_parser = clap::value_parser!(u8).range(..i64::from(u8::MAX)))] + threads: u8, } impl SmpConfig { @@ -315,8 +315,21 @@ impl SmpConfig { let mut threads = self.threads; if max_cpus == 0 { - if sockets * self.dies * self.clusters * cores * threads > 0 { - max_cpus = sockets * self.dies * self.clusters * cores * threads; + let mut tmp_max = sockets + .checked_mul(self.dies) + .with_context(|| "Illegal smp config")?; + tmp_max = tmp_max + .checked_mul(self.clusters) + .with_context(|| "Illegal smp config")?; + tmp_max = tmp_max + .checked_mul(cores) + .with_context(|| "Illegal smp config")?; + tmp_max = tmp_max + .checked_mul(threads) + .with_context(|| "Illegal smp config")?; + + if tmp_max > 0 { + max_cpus = tmp_max; } else { max_cpus = self.cpus; } @@ -346,9 +359,9 @@ impl SmpConfig { if !(min_max_cpus..=MAX_NR_CPUS).contains(&max_cpus) { return Err(anyhow!(ConfigError::IllegalValue( "MAX CPU number".to_string(), - min_max_cpus, + u64::from(min_max_cpus), true, - MAX_NR_CPUS, + u64::from(MAX_NR_CPUS), true, ))); } -- Gitee From 97cea0be2eb71cf8e71d9df933870155864411f8 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Aug 2024 16:06:15 +0800 Subject: [PATCH 2035/2187] cpu: Fix cast_ptr_alignment warning Cast *u8 to *u32 may causing unaligned memory access. Signed-off-by: Keqian Zhu --- cpu/src/lib.rs | 1 + cpu/src/x86_64/mod.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 5b70ef75f..704ef6044 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -285,6 +285,7 @@ impl CPU { /// Set thread id for `CPU`. pub fn set_tid(&self, tid: Option) { if tid.is_none() { + // Cast is safe as tid is not negative. *self.tid.lock().unwrap() = Some(gettid().as_raw() as u64); } else { *self.tid.lock().unwrap() = tid; diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index 7b0773780..c64ab8e86 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -239,19 +239,19 @@ impl X86CPUState { self.lapic = lapic; // SAFETY: The member regs in struct LapicState is a u8 array with 1024 entries, - // so it's saft to cast u8 pointer to u32 at position APIC_LVT0 and APIC_LVT1. + // so it's safe to cast u8 pointer to u32 at position APIC_LVT0 and APIC_LVT1. // Safe because all value in this unsafe block is certain. unsafe { let apic_lvt_lint0 = &mut self.lapic.regs[APIC_LVT0..] as *mut [i8] as *mut u32; - *apic_lvt_lint0 &= !0x700; - *apic_lvt_lint0 |= APIC_MODE_EXTINT << 8; + let modified = (apic_lvt_lint0.read_unaligned() & !0x700) | (APIC_MODE_EXTINT << 8); + apic_lvt_lint0.write_unaligned(modified); let apic_lvt_lint1 = &mut self.lapic.regs[APIC_LVT1..] as *mut [i8] as *mut u32; - *apic_lvt_lint1 &= !0x700; - *apic_lvt_lint1 |= APIC_MODE_NMI << 8; + let modified = (apic_lvt_lint1.read_unaligned() & !0x700) | (APIC_MODE_NMI << 8); + apic_lvt_lint1.write_unaligned(modified); let apic_id = &mut self.lapic.regs[APIC_ID..] as *mut [i8] as *mut u32; - *apic_id = self.apic_id << 24; + apic_id.write_unaligned(self.apic_id << 24); } Ok(()) -- Gitee From e7c6406c1fd3d91451b2be05c41752aeade2c765 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Mon, 19 Aug 2024 17:15:56 +0800 Subject: [PATCH 2036/2187] machine: Fix cast_possible_truncation warnings Signed-off-by: Keqian Zhu --- machine/src/lib.rs | 2 +- machine/src/standard_common/mod.rs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 39fb7ef2d..e71300dde 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1564,7 +1564,7 @@ pub trait MachineOps: MachineLifecycle { } drop(locked_bus); // It's safe to call devfn.unwrap(), because the bus exists. - match locked_pci_host.find_device(0, devfn.unwrap() as u8) { + match locked_pci_host.find_device(0, u8::try_from(devfn.unwrap())?) { Some(dev) => dev .lock() .unwrap() diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 0bfd64668..4f95bb377 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -570,7 +570,7 @@ pub(crate) trait AcpiBuilder { // PCI Segment Group Number mcfg.append_child(0_u16.as_bytes()); // Start Bus Number and End Bus Number. max_nr_bus is no less than 1. - mcfg.append_child(&[0_u8, (max_nr_bus - 1) as u8]); + mcfg.append_child(&[0_u8, u8::try_from(max_nr_bus - 1)?]); // Reserved mcfg.append_child(&[0_u8; 4]); @@ -686,7 +686,7 @@ pub(crate) trait AcpiBuilder { fadt_begin + facs_offset, facs_size, ACPI_TABLE_FILE, - facs_addr as u32, + u32::try_from(facs_addr)?, )?; // xDSDT address field's offset in FADT. @@ -699,7 +699,7 @@ pub(crate) trait AcpiBuilder { fadt_begin + xdsdt_offset, xdsdt_size, ACPI_TABLE_FILE, - dsdt_addr as u32, + u32::try_from(dsdt_addr)?, )?; loader.add_cksum_entry( @@ -858,7 +858,7 @@ pub(crate) trait AcpiBuilder { xsdt_begin + entry_offset, entry_size, ACPI_TABLE_FILE, - entry as u32, + u32::try_from(entry)?, )?; // u32 is enough for storing offset. entry_offset += u32::from(entry_size); @@ -898,7 +898,7 @@ pub(crate) trait AcpiBuilder { xsdt_offset, xsdt_size, ACPI_TABLE_FILE, - xsdt_addr as u32, + u32::try_from(xsdt_addr)?, )?; let cksum_offset = 8_u32; @@ -1745,6 +1745,12 @@ impl DeviceInterface for StdMachine { } }; + if i32::try_from(args.priority).is_err() { + return Response::create_error_response( + qmp_schema::QmpErrorClass::GenericError("priority illegal".to_string()), + None, + ); + } region.set_priority(args.priority as i32); if let Some(read_only) = args.romd { if region.set_rom_device_romd(read_only).is_err() { -- Gitee From 943e5219cc144027bccea413251e6a0a28c12aae Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Aug 2024 15:09:42 +0800 Subject: [PATCH 2037/2187] Hypervisor: Add numeric literal type Add numeric literal type. Signed-off-by: Jinhao Gao --- hypervisor/src/kvm/aarch64/gicv3.rs | 2 +- hypervisor/src/kvm/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hypervisor/src/kvm/aarch64/gicv3.rs b/hypervisor/src/kvm/aarch64/gicv3.rs index 5f8c9ede4..0ced57c68 100644 --- a/hypervisor/src/kvm/aarch64/gicv3.rs +++ b/hypervisor/src/kvm/aarch64/gicv3.rs @@ -118,7 +118,7 @@ impl GICv3Access for KvmGICv3 { } fn vcpu_gicr_attr(&self, cpu: usize) -> u64 { - let clustersz = 16; + let clustersz = 16usize; let aff1 = (cpu / clustersz) as u64; let aff0 = (cpu % clustersz) as u64; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 209442baa..3cec118e5 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -665,7 +665,7 @@ impl CPUHypervisorOps for KvmCpu { } // It shall wait for the vCPU pause state from hypervisor exits. - let mut sleep_times = 0; + let mut sleep_times = 0u32; while !pause_signal.load(Ordering::SeqCst) { if sleep_times >= 5 { bail!(CpuError::StopVcpu("timeout".to_string())); -- Gitee From d18e9e5a648c9a0728ea10dc6d3648d21add8ed7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 20 Aug 2024 06:50:09 +0800 Subject: [PATCH 2038/2187] scsi: adjust xfer type Adjust the xfer type to avoid overflow. Signed-off-by: liuxiangdong --- devices/src/scsi/bus.rs | 32 +++++++++++++++++--------------- devices/src/usb/storage.rs | 3 ++- devices/src/usb/uas.rs | 2 +- trace/trace_info/usb.toml | 2 +- virtio/src/device/scsi_cntlr.rs | 2 +- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/devices/src/scsi/bus.rs b/devices/src/scsi/bus.rs index 277b09ab9..ceaa1e5bb 100644 --- a/devices/src/scsi/bus.rs +++ b/devices/src/scsi/bus.rs @@ -260,13 +260,13 @@ pub const MODE_PAGE_TO_PROTECT: u8 = 0x1d; pub const MODE_PAGE_CAPABILITIES: u8 = 0x2a; pub const MODE_PAGE_ALLS: u8 = 0x3f; -pub const SCSI_MAX_INQUIRY_LEN: u32 = 256; +pub const SCSI_MAX_INQUIRY_LEN: u64 = 256; pub const SCSI_INQUIRY_PRODUCT_MAX_LEN: usize = 16; pub const SCSI_INQUIRY_VENDOR_MAX_LEN: usize = 8; pub const SCSI_INQUIRY_VERSION_MAX_LEN: usize = 4; pub const SCSI_INQUIRY_VPD_SERIAL_NUMBER_MAX_LEN: usize = 32; -const SCSI_TARGET_INQUIRY_LEN: u32 = 36; +const SCSI_TARGET_INQUIRY_LEN: u64 = 36; /// | bit7 - bit 5 | bit 4 - bit 0 | /// | Peripheral Qualifier | Peripheral Device Type | @@ -446,15 +446,15 @@ fn scsi_bus_parse_req_cdb( // When CDB's Group Code is vendor specific or reserved, len/xfer/lba will be negative. // So, don't need to check again after checking in cdb length. - let xfer = scsi_cdb_xfer(&cdb, dev); - let lba = scsi_cdb_lba(&cdb); + let xfer = scsi_cdb_xfer(&cdb, dev) as u64; + let lba = scsi_cdb_lba(&cdb) as u64; Some(ScsiCommand { buf: cdb, op, len: len as u32, - xfer: xfer as u32, - lba: lba as u64, + xfer, + lba, mode: scsi_cdb_xfer_mode(&cdb), }) } @@ -468,7 +468,7 @@ pub struct ScsiCommand { /// Length of CDB. pub len: u32, /// Transfer length. - pub xfer: u32, + pub xfer: u64, /// Logical Block Address. pub lba: u64, /// Transfer direction. @@ -790,22 +790,22 @@ fn scsi_cdb_length(cdb: &[u8; SCSI_CMD_BUF_SIZE]) -> i32 { } } -pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i32 { +pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) -> i64 { SCSI_DEVICE!(dev, locked_dev, scsi_dev); - let block_size = scsi_dev.block_size as i32; + let block_size = scsi_dev.block_size as i64; drop(locked_dev); - let mut xfer = match cdb[0] >> 5 { + let mut xfer: i64 = match cdb[0] >> 5 { // Group Code | Transfer length. | // 000b | Byte[4]. | // 001b | Bytes[7-8]. | // 010b | Bytes[7-8]. | // 100b | Bytes[10-13]. | // 101b | Bytes[6-9]. | - 0 => i32::from(cdb[4]), - 1 | 2 => i32::from(BigEndian::read_u16(&cdb[7..])), - 4 => BigEndian::read_u32(&cdb[10..]) as i32, - 5 => BigEndian::read_u32(&cdb[6..]) as i32, + 0 => i64::from(cdb[4]), + 1 | 2 => i64::from(BigEndian::read_u16(&cdb[7..])), + 4 => i64::from(BigEndian::read_u32(&cdb[10..])), + 5 => i64::from(BigEndian::read_u32(&cdb[6..])), _ => -1, }; @@ -819,14 +819,16 @@ pub fn scsi_cdb_xfer(cdb: &[u8; SCSI_CMD_BUF_SIZE], dev: Arc>) WRITE_6 | READ_6 => { // length 0 means 256 blocks. if xfer == 0 { + // Safety: block_size is 2048 or 512. xfer = 256 * block_size; } } WRITE_10 | WRITE_12 | WRITE_16 | READ_10 | READ_12 | READ_16 => { + // Safety: xfer is less than u32::max now. xfer *= block_size; } INQUIRY => { - xfer = i32::from(cdb[4]) | i32::from(cdb[3]) << 8; + xfer = i64::from(cdb[4]) | i64::from(cdb[3]) << 8; } _ => {} } diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index 986e67416..bec309773 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -468,6 +468,7 @@ impl UsbStorage { self.state.check_cdb_exist(true)?; self.state.check_iovec_empty(true)?; + // Safety: iovecs are set in `setup_usb_packet` and iovec_len is no more than TRB_TR_LEN_MASK. let iovec_len = packet.get_iovecs_size() as u32; if iovec_len < self.state.cbw.data_len { bail!( @@ -504,7 +505,7 @@ impl UsbStorage { ) .with_context(|| "Error in creating scsirequest.")?; - if sreq.cmd.xfer > sreq.datalen && sreq.cmd.mode != ScsiXferMode::ScsiXferNone { + if sreq.cmd.xfer > u64::from(sreq.datalen) && sreq.cmd.mode != ScsiXferMode::ScsiXferNone { // Wrong USB packet which doesn't provide enough datain/dataout buffer. bail!( "command {:x} requested data's length({}), provided buffer length({})", diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 797800331..04ce87453 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -540,7 +540,7 @@ impl UsbUas { ) .with_context(|| "failed to create SCSI request")?; - if scsi_request.cmd.xfer > scsi_request.datalen + if scsi_request.cmd.xfer > u64::from(scsi_request.datalen) && scsi_request.cmd.mode != ScsiXferMode::ScsiXferNone { bail!( diff --git a/trace/trace_info/usb.toml b/trace/trace_info/usb.toml index cabcf6ca4..9defe6d28 100644 --- a/trace/trace_info/usb.toml +++ b/trace/trace_info/usb.toml @@ -528,7 +528,7 @@ enabled = true [[events]] name = "usb_uas_try_start_next_transfer" -args = "device_id: &str, xfer_len: i32" +args = "device_id: &str, xfer_len: i64" message = "UAS {} device is trying to start next transfer of length {}." enabled = true diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 9f6fc01df..7c6f36f28 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -933,7 +933,7 @@ impl ScsiCmdQueueHandler { } let sreq = scsi_req.unwrap(); - if sreq.cmd.xfer > sreq.datalen && sreq.cmd.mode != ScsiXferMode::ScsiXferNone { + if sreq.cmd.xfer > u64::from(sreq.datalen) && sreq.cmd.mode != ScsiXferMode::ScsiXferNone { // Wrong virtio scsi request which doesn't provide enough datain/dataout buffer. qrequest.resp.response = VIRTIO_SCSI_S_OVERRUN; qrequest.complete()?; -- Gitee From b2439c86d2524d942478c84e3acaa19412efdf38 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Tue, 20 Aug 2024 11:31:59 +0800 Subject: [PATCH 2039/2187] Hypervisor: Fix cast_possible_truncation warnings Signed-off-by: Jinhao Gao --- hypervisor/src/kvm/aarch64/gicv3.rs | 1 + hypervisor/src/kvm/aarch64/mod.rs | 48 +++++++++++++++-------------- hypervisor/src/kvm/interrupt.rs | 7 +++-- hypervisor/src/kvm/listener.rs | 10 +++--- hypervisor/src/kvm/mod.rs | 3 +- 5 files changed, 37 insertions(+), 32 deletions(-) diff --git a/hypervisor/src/kvm/aarch64/gicv3.rs b/hypervisor/src/kvm/aarch64/gicv3.rs index 0ced57c68..23d86c0df 100644 --- a/hypervisor/src/kvm/aarch64/gicv3.rs +++ b/hypervisor/src/kvm/aarch64/gicv3.rs @@ -128,6 +128,7 @@ impl GICv3Access for KvmGICv3 { let last = u64::from((self.vcpu_count - 1) == cpu as u64); + // Allow conversion of variables from i64 to u64. ((cpu_affid << 32) | (1 << 24) | (1 << 8) | (last << 4)) & kvm_bindings::KVM_DEV_ARM_VGIC_V3_MPIDR_MASK as u64 } diff --git a/hypervisor/src/kvm/aarch64/mod.rs b/hypervisor/src/kvm/aarch64/mod.rs index 3e6c84619..7e42f61a6 100644 --- a/hypervisor/src/kvm/aarch64/mod.rs +++ b/hypervisor/src/kvm/aarch64/mod.rs @@ -175,12 +175,13 @@ impl KvmCpu { if vcpu_config.sve { self.fd - .vcpu_finalize(&(kvm_bindings::KVM_ARM_VCPU_SVE as i32))?; + .vcpu_finalize(&(i32::try_from(kvm_bindings::KVM_ARM_VCPU_SVE)?))?; } - arch_cpu.lock().unwrap().mpidr = + arch_cpu.lock().unwrap().mpidr = u64::try_from( self.get_one_reg(KVM_REG_ARM_MPIDR_EL1) - .with_context(|| "Failed to get mpidr")? as u64; + .with_context(|| "Failed to get mpidr")?, + )?; arch_cpu.lock().unwrap().features = *vcpu_config; @@ -255,10 +256,10 @@ impl KvmCpu { ); } RegsIndex::VtimerCount => { - locked_arch_cpu.vtimer_cnt = self - .get_one_reg(KVM_REG_ARM_TIMER_CNT) - .with_context(|| "Failed to get virtual timer count")? - as u64; + locked_arch_cpu.vtimer_cnt = u64::try_from( + self.get_one_reg(KVM_REG_ARM_TIMER_CNT) + .with_context(|| "Failed to get virtual timer count")?, + )?; locked_arch_cpu.vtimer_cnt_valid = true; } } @@ -325,33 +326,34 @@ impl KvmCpu { fn get_core_regs(&self) -> Result { let mut core_regs = kvm_regs::default(); - core_regs.regs.sp = self.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())? as u64; - core_regs.sp_el1 = self.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())? as u64; - core_regs.regs.pstate = self.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())? as u64; - core_regs.regs.pc = self.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())? as u64; - core_regs.elr_el1 = self.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())? as u64; + core_regs.regs.sp = u64::try_from(self.get_one_reg(Arm64CoreRegs::UserPTRegSp.into())?)?; + core_regs.sp_el1 = u64::try_from(self.get_one_reg(Arm64CoreRegs::KvmSpEl1.into())?)?; + core_regs.regs.pstate = + u64::try_from(self.get_one_reg(Arm64CoreRegs::UserPTRegPState.into())?)?; + core_regs.regs.pc = u64::try_from(self.get_one_reg(Arm64CoreRegs::UserPTRegPc.into())?)?; + core_regs.elr_el1 = u64::try_from(self.get_one_reg(Arm64CoreRegs::KvmElrEl1.into())?)?; - for i in 0..KVM_NR_REGS as usize { + for i in 0..usize::try_from(KVM_NR_REGS)? { core_regs.regs.regs[i] = - self.get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())? as u64; + u64::try_from(self.get_one_reg(Arm64CoreRegs::UserPTRegRegs(i).into())?)?; } - for i in 0..KVM_NR_SPSR as usize { - core_regs.spsr[i] = self.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())? as u64; + for i in 0..usize::try_from(KVM_NR_SPSR)? { + core_regs.spsr[i] = u64::try_from(self.get_one_reg(Arm64CoreRegs::KvmSpsr(i).into())?)?; } // State save and restore is not supported for SVE for now, so we just skip it. if self.kvi.lock().unwrap().features[0] & (1 << kvm_bindings::KVM_ARM_VCPU_SVE) == 0 { - for i in 0..KVM_NR_FP_REGS as usize { + for i in 0..usize::try_from(KVM_NR_FP_REGS)? { core_regs.fp_regs.vregs[i] = self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateVregs(i).into())?; } } core_regs.fp_regs.fpsr = - self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())? as u32; + u32::try_from(self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpsr.into())?)?; core_regs.fp_regs.fpcr = - self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())? as u32; + u32::try_from(self.get_one_reg(Arm64CoreRegs::UserFPSIMDStateFpcr.into())?)?; Ok(core_regs) } @@ -383,14 +385,14 @@ impl KvmCpu { u128::from(core_regs.elr_el1), )?; - for i in 0..KVM_NR_REGS as usize { + for i in 0..usize::try_from(KVM_NR_REGS)? { self.set_one_reg( Arm64CoreRegs::UserPTRegRegs(i).into(), u128::from(core_regs.regs.regs[i]), )?; } - for i in 0..KVM_NR_SPSR as usize { + for i in 0..usize::try_from(KVM_NR_SPSR)? { self.set_one_reg( Arm64CoreRegs::KvmSpsr(i).into(), u128::from(core_regs.spsr[i]), @@ -399,7 +401,7 @@ impl KvmCpu { // State save and restore is not supported for SVE for now, so we just skip it. if self.kvi.lock().unwrap().features[0] & (1 << kvm_bindings::KVM_ARM_VCPU_SVE) == 0 { - for i in 0..KVM_NR_FP_REGS as usize { + for i in 0..usize::try_from(KVM_NR_FP_REGS)? { self.set_one_reg( Arm64CoreRegs::UserFPSIMDStateVregs(i).into(), core_regs.fp_regs.vregs[i], @@ -420,7 +422,7 @@ impl KvmCpu { } fn reg_sync_by_cpreg_list(reg_id: u64) -> Result { - let coproc = reg_id as u32 & KVM_REG_ARM_COPROC_MASK; + let coproc = u32::try_from(reg_id)? & KVM_REG_ARM_COPROC_MASK; if coproc == KVM_REG_ARM_CORE { return Ok(false); } diff --git a/hypervisor/src/kvm/interrupt.rs b/hypervisor/src/kvm/interrupt.rs index 4df7feb98..b067b17c3 100644 --- a/hypervisor/src/kvm/interrupt.rs +++ b/hypervisor/src/kvm/interrupt.rs @@ -46,7 +46,7 @@ fn get_maximum_gsi_cnt(kvmfd: &Kvm) -> u32 { gsi_count = 0; } - gsi_count as u32 + u32::try_from(gsi_count).unwrap_or_default() } /// Return `IrqRouteEntry` according to gsi, irqchip kind and pin. @@ -180,7 +180,7 @@ impl IrqRouteTable { .find_next_zero(0) .with_context(|| "Failed to get new free gsi")?; self.gsi_bitmap.set(free_gsi)?; - Ok(free_gsi as u32) + Ok(u32::try_from(free_gsi)?) } /// Release gsi number to free. @@ -208,11 +208,12 @@ impl IrqRouteTable { trace::kvm_commit_irq_routing(); // SAFETY: data in `routes` is reliable. unsafe { + // layout is aligned, so casting of ptr is allowed. let irq_routing = std::alloc::alloc(layout) as *mut IrqRoute; if irq_routing.is_null() { bail!("Failed to alloc irq routing"); } - (*irq_routing).nr = routes.len() as u32; + (*irq_routing).nr = u32::try_from(routes.len())?; (*irq_routing).flags = 0; let entries: &mut [IrqRouteEntry] = (*irq_routing).entries.as_mut_slice(routes.len()); entries.copy_from_slice(&routes); diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index b05175c2c..2e1c603fe 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -93,7 +93,7 @@ impl KvmMemoryListener { for (index, slot) in slots.iter_mut().enumerate() { if slot.size == 0 { - slot.index = index as u32; + slot.index = u32::try_from(index)?; slot.guest_addr = guest_addr; slot.size = size; slot.host_addr = host_addr; @@ -316,8 +316,8 @@ impl KvmMemoryListener { let ioctl_ret = if ioevtfd.data_match { let length = ioevtfd.addr_range.size; match length { - 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), - 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), + 2 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, u16::try_from(ioevtfd.data)?), + 4 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, u32::try_from(ioevtfd.data)?), 8 => vm_fd.register_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), _ => bail!("Unexpected ioeventfd data length {}", length), } @@ -357,8 +357,8 @@ impl KvmMemoryListener { let ioctl_ret = if ioevtfd.data_match { let length = ioevtfd.addr_range.size; match length { - 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u16), - 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data as u32), + 2 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, u16::try_from(ioevtfd.data)?), + 4 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, u32::try_from(ioevtfd.data)?), 8 => vm_fd.unregister_ioevent(&ioevtfd.fd, &io_addr, ioevtfd.data), _ => bail!("Unexpected ioeventfd data length {}", length), } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 3cec118e5..6d00cd381 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -135,6 +135,7 @@ impl KvmHypervisor { } fn create_memory_listener(&self) -> Arc> { + // Memslot will not exceed u32::MAX, so use as translate data type. Arc::new(Mutex::new(KvmMemoryListener::new( self.fd.as_ref().unwrap().get_nr_memslots() as u32, self.vm_fd.clone(), @@ -298,7 +299,7 @@ impl MigrateOps for KvmHypervisor { self.vm_fd .as_ref() .unwrap() - .get_dirty_log(slot, mem_size as usize) + .get_dirty_log(slot, usize::try_from(mem_size)?) .with_context(|| { format!( "Failed to get dirty log, error is {}", -- Gitee From 2e06dd2a2fd00d2dbe3739e829a79276009fb614 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 20 Aug 2024 11:31:42 +0800 Subject: [PATCH 2040/2187] ohui:fix oh keycode keycode of "menu" on oh is 0x813 Signed-off-by: zhanghan64 --- ui/src/keycode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/keycode.rs b/ui/src/keycode.rs index 18bbaa7c5..16db2dbd4 100644 --- a/ui/src/keycode.rs +++ b/ui/src/keycode.rs @@ -458,7 +458,7 @@ const KEY_CODE_OH: [(KeyCode, u16); 105] = [ (KeyCode::Home, 0x0821), (KeyCode::SysReq, 0x081F), (KeyCode::Right, 0x07DF), - (KeyCode::Menu, 0x09A2), + (KeyCode::Menu, 0x0813), (KeyCode::Prior, 0x0814), (KeyCode::Insert, 0x0823), (KeyCode::NumLock, 0x0836), -- Gitee From fa5f3a1775895c3c86686a85288a3ab4e6d700e3 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 20 Aug 2024 11:44:21 +0800 Subject: [PATCH 2041/2187] ohui/ohaudio/ohcam: fix type of literal value Explicitly declare the value of literal Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 4 ++-- devices/src/misc/scream/alsa.rs | 4 ++-- devices/src/misc/scream/mod.rs | 2 +- devices/src/misc/scream/ohaudio.rs | 4 ++-- devices/src/usb/camera.rs | 4 ++-- ui/src/ohui_srv/channel.rs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 04ba1b4fc..9d8c28417 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -340,7 +340,7 @@ impl CameraBackend for OhCameraBackend { .with_context(|| "Invalid camid in callback table")? .get_buffer(); - if src_len == 0 { + if src_len == 0_u64 { bail!("Invalid frame src_len {}", src_len); } @@ -350,7 +350,7 @@ impl CameraBackend for OhCameraBackend { trace::trace_scope_start!(ohcam_get_frame, args = (frame_offset, len)); - let mut copied = 0; + let mut copied = 0_usize; for iov in iovecs { if len == copied { break; diff --git a/devices/src/misc/scream/alsa.rs b/devices/src/misc/scream/alsa.rs index 7daaee0fc..c97084b76 100644 --- a/devices/src/misc/scream/alsa.rs +++ b/devices/src/misc/scream/alsa.rs @@ -168,7 +168,7 @@ impl AudioInterface for AlsaStreamData { return; } - let mut frames = 0; + let mut frames = 0_u32; let mut io = self.pcm.as_ref().unwrap().io_bytes(); // Make sure audio read does not bypass chunk_idx read. @@ -215,7 +215,7 @@ impl AudioInterface for AlsaStreamData { return 0; } - let mut frames = 0; + let mut frames = 0_u32; let mut io = self.pcm.as_ref().unwrap().io_bytes(); // Make sure audio read does not bypass chunk_idx read. diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 54ac93415..5db2c93c5 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -330,7 +330,7 @@ impl StreamData { &unsafe { std::slice::from_raw_parts(hva as *const ShmemHeader, 1) }[0]; self.init(stream_header); - let mut last_end = 0; + let mut last_end = 0_u64; // The recording buffer is behind the playback buffer. Thereforce, the end position of // the playback buffer must be calculted to determine whether the two buffers overlap. if dir == ScreamDirection::Record && header.play.is_started != 0 { diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 5b03c1888..cd504140b 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -80,7 +80,7 @@ struct StreamQueue { impl Read for StreamQueue { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let len = buf.len(); - let mut ret = 0; + let mut ret = 0_usize; while ret < len { if self.queue.is_empty() { break; @@ -191,7 +191,7 @@ impl OhAudioRender { fn flush(&mut self) { self.set_flushing(true); - let mut cnt = 0; + let mut cnt = 0_u64; while cnt < FLUSH_DELAY_CNT { thread::sleep(Duration::from_millis(FLUSH_DELAY_MS)); cnt += 1; diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index b4ef08613..b7f0f1302 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -1109,8 +1109,8 @@ fn gen_intface_header_desc(fmt_num: u8) -> VsDescInputHeader { fn gen_fmt_header(fmt: &CameraFormatList) -> Result> { let bits_per_pixel = match fmt.format { - FmtType::Yuy2 | FmtType::Rgb565 => 0x10, - FmtType::Nv12 => 0xc, + FmtType::Yuy2 | FmtType::Rgb565 => 0x10_u8, + FmtType::Nv12 => 0xc_u8, _ => 0, }; let header = match fmt.format { diff --git a/ui/src/ohui_srv/channel.rs b/ui/src/ohui_srv/channel.rs index 64d6e0ac0..e55279c99 100755 --- a/ui/src/ohui_srv/channel.rs +++ b/ui/src/ohui_srv/channel.rs @@ -65,7 +65,7 @@ impl OhUiChannel { pub fn recv_slice(stream: &mut dyn Read, data: &mut [u8]) -> Result { let len = data.len(); - let mut ret = 0; + let mut ret = 0_usize; while ret < len { match stream.read(&mut data[ret..len]) { -- Gitee From 595e3a68cbb1b4b8e9b82c1dd9bad3f2ff4c2e26 Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Tue, 20 Aug 2024 14:46:38 +0800 Subject: [PATCH 2042/2187] virtio-net: fix possible out of bound of array Checking length of array before using it. Signed-off-by: Yan Wang --- virtio/src/device/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 689e9c592..0f237dbcb 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -1684,7 +1684,7 @@ impl VirtioDevice for Net { // configs[0]: NetDevcfg. configs[1]: NetworkInterfaceConfig. fn update_config(&mut self, dev_config: Vec>) -> Result<()> { - if !dev_config.is_empty() { + if dev_config.len() == 2 { self.netdev_cfg = dev_config[0] .as_any() .downcast_ref::() -- Gitee From 4d4e11b567694db211ca6ab551f645a1c0b44528 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 20 Aug 2024 09:42:51 +0800 Subject: [PATCH 2043/2187] machine: Verify unsafe code blocks Reduce unsafe range of host_cpuid(), and propagate unsafe to function user. Signed-off-by: Keqian Zhu --- cpu/src/x86_64/cpuid.rs | 24 ++++++++--------- cpu/src/x86_64/mod.rs | 57 ++++++++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/cpu/src/x86_64/cpuid.rs b/cpu/src/x86_64/cpuid.rs index 58ecfbd8d..f7b8d7525 100644 --- a/cpu/src/x86_64/cpuid.rs +++ b/cpu/src/x86_64/cpuid.rs @@ -12,21 +12,17 @@ use core::arch::x86_64::__cpuid_count; -pub fn host_cpuid( +pub unsafe fn host_cpuid( leaf: u32, subleaf: u32, - eax: *mut u32, - ebx: *mut u32, - ecx: *mut u32, - edx: *mut u32, + eax: &mut u32, + ebx: &mut u32, + ecx: &mut u32, + edx: &mut u32, ) { - // SAFETY: cpuid is created in get_supported_cpuid(). - unsafe { - let cpuid = __cpuid_count(leaf, subleaf); - - *eax = cpuid.eax; - *ebx = cpuid.ebx; - *ecx = cpuid.ecx; - *edx = cpuid.edx; - } + let cpuid = __cpuid_count(leaf, subleaf); + *eax = cpuid.eax; + *ebx = cpuid.ebx; + *ecx = cpuid.ecx; + *edx = cpuid.edx; } diff --git a/cpu/src/x86_64/mod.rs b/cpu/src/x86_64/mod.rs index c64ab8e86..09fdf5db5 100644 --- a/cpu/src/x86_64/mod.rs +++ b/cpu/src/x86_64/mod.rs @@ -410,26 +410,32 @@ impl X86CPUState { } } 2 => { - host_cpuid( - 2, - 0, - &mut entry.eax, - &mut entry.ebx, - &mut entry.ecx, - &mut entry.edx, - ); + // SAFETY: entry is from KVM_GET_SUPPORTED_CPUID. + unsafe { + host_cpuid( + 2, + 0, + &mut entry.eax, + &mut entry.ebx, + &mut entry.ecx, + &mut entry.edx, + ); + } } 4 => { // cache info: needed for Pentium Pro compatibility // Passthrough host cache info directly to guest - host_cpuid( - 4, - entry.index, - &mut entry.eax, - &mut entry.ebx, - &mut entry.ecx, - &mut entry.edx, - ); + // SAFETY: entry is from KVM_GET_SUPPORTED_CPUID. + unsafe { + host_cpuid( + 4, + entry.index, + &mut entry.eax, + &mut entry.ebx, + &mut entry.ecx, + &mut entry.edx, + ); + } entry.eax &= !0xfc00_0000; if entry.eax & 0x0001_ffff != 0 && self.max_vcpus > 1 { // max_vcpus is no less than 1. @@ -511,14 +517,17 @@ impl X86CPUState { } 0x8000_0002..=0x8000_0004 => { // Passthrough host cpu model name directly to guest - host_cpuid( - entry.function, - entry.index, - &mut entry.eax, - &mut entry.ebx, - &mut entry.ecx, - &mut entry.edx, - ); + // SAFETY: entry is from KVM_GET_SUPPORTED_CPUID. + unsafe { + host_cpuid( + entry.function, + entry.index, + &mut entry.eax, + &mut entry.ebx, + &mut entry.ecx, + &mut entry.edx, + ); + } } _ => (), } -- Gitee From ed3199ad287dd042ad213ecaffc4b9201b3eb210 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Tue, 20 Aug 2024 15:01:48 +0800 Subject: [PATCH 2044/2187] Balloon: pass the memory_advise usafe upwards --- virtio/src/device/balloon.rs | 83 ++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index dbfa06b70..d4e611cdb 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -170,9 +170,8 @@ fn iov_to_buf( } } -fn memory_advise(addr: *mut libc::c_void, len: libc::size_t, advice: libc::c_int) { - // SAFETY: The memory to be freed is allocated by guest. - if unsafe { libc::madvise(addr, len, advice) } != 0 { +unsafe fn memory_advise(addr: *mut libc::c_void, len: libc::size_t, advice: libc::c_int) { + if libc::madvise(addr, len, advice) != 0 { let evt_type = match advice { libc::MADV_DONTNEED => "DONTNEED".to_string(), libc::MADV_REMOVE => "REMOVE".to_string(), @@ -238,11 +237,14 @@ impl Request { } else if hva == last_addr + BALLOON_PAGE_SIZE { free_len += 1; } else { - memory_advise( - start_addr as *const libc::c_void as *mut _, - (free_len * BALLOON_PAGE_SIZE) as usize, - libc::MADV_WILLNEED, - ); + // SAFETY: The memory to be freed is allocated by guest. + unsafe { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + libc::MADV_WILLNEED, + ) + }; free_len = 1; start_addr = hva; } @@ -251,11 +253,14 @@ impl Request { } if free_len != 0 { - memory_advise( - start_addr as *const libc::c_void as *mut _, - (free_len * BALLOON_PAGE_SIZE) as usize, - libc::MADV_WILLNEED, - ); + // SAFETY: The memory to be freed is allocated by guest. + unsafe { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + libc::MADV_WILLNEED, + ) + }; } } /// Mark balloon page with `MADV_DONTNEED` or `MADV_WILLNEED`. @@ -319,11 +324,14 @@ impl Request { } else if hva == last_addr + BALLOON_PAGE_SIZE && last_share == share { free_len += 1; } else { - memory_advise( - start_addr as *const libc::c_void as *mut _, - (free_len * BALLOON_PAGE_SIZE) as usize, - advice, - ); + // SAFETY: The memory to be freed is allocated by guest. + unsafe { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + advice, + ) + }; free_len = 1; start_addr = hva; last_share = share; @@ -337,11 +345,14 @@ impl Request { last_addr = hva; } if free_len != 0 { - memory_advise( - start_addr as *const libc::c_void as *mut _, - (free_len * BALLOON_PAGE_SIZE) as usize, - advice, - ); + // SAFETY: The memory to be freed is allocated by guest. + unsafe { + memory_advise( + start_addr as *const libc::c_void as *mut _, + (free_len * BALLOON_PAGE_SIZE) as usize, + advice, + ) + }; } } else { let mut host_page_bitmap = BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); @@ -375,11 +386,14 @@ impl Request { } else { advice = libc::MADV_DONTNEED; } - memory_advise( - host_page_bitmap.base_address as *const libc::c_void as *mut _, - host_page_size as usize, - advice, - ); + // SAFETY: The memory to be freed is allocated by guest. + unsafe { + memory_advise( + host_page_bitmap.base_address as *const libc::c_void as *mut _, + host_page_size as usize, + advice, + ) + }; host_page_bitmap = BalloonedPageBitmap::new(host_page_size / BALLOON_PAGE_SIZE); } } @@ -401,11 +415,14 @@ impl Request { } else { libc::MADV_DONTNEED }; - memory_advise( - hva as *const libc::c_void as *mut _, - iov.iov_len as usize, - advice, - ); + // SAFETY: The memory to be freed is allocated by guest. + unsafe { + memory_advise( + hva as *const libc::c_void as *mut _, + iov.iov_len as usize, + advice, + ) + }; } } } -- Gitee From 59c84ba4cf7b9cc9286fc883fb60323c1cb539d7 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Tue, 20 Aug 2024 15:24:54 +0800 Subject: [PATCH 2045/2187] Memory: Numeric literals need to ne explicitly typed. --- address_space/src/address_space.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 0689aaea7..4e8e35fcf 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -230,7 +230,7 @@ impl AddressSpace { } locked_listener.enable(); - let mut idx = 0; + let mut idx = 0_usize; let mut mls = self.listeners.lock().unwrap(); for ml in mls.iter() { if ml.lock().unwrap().priority() >= locked_listener.priority() { @@ -381,8 +381,8 @@ impl AddressSpace { /// * `new_evtfds` - New `RegionIoEventFd` array. fn update_ioeventfds_pass(&self, new_evtfds: &[RegionIoEventFd]) -> Result<()> { let old_evtfds = self.ioeventfds.lock().unwrap(); - let mut old_idx = 0; - let mut new_idx = 0; + let mut old_idx = 0_usize; + let mut new_idx = 0_usize; while old_idx < old_evtfds.len() || new_idx < new_evtfds.len() { let old_fd = old_evtfds.get(old_idx); -- Gitee From ded5029edddffade6dc3b093f23bcf3e18920eea Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Tue, 20 Aug 2024 15:38:44 +0800 Subject: [PATCH 2046/2187] Balloon: Numeric literals need to ne explicitly typed. --- virtio/src/device/balloon.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index d4e611cdb..34f241abc 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -282,7 +282,7 @@ impl Request { let mut hvaset = Vec::new(); for iov in self.iovec.iter() { - let mut offset = 0; + let mut offset = 0_u64; while let Some(pfn) = iov_to_buf::(address_space, iov, offset) { offset += std::mem::size_of::() as u64; @@ -305,7 +305,7 @@ impl Request { } let host_page_size = host_page_size(); - let mut advice = 0; + let mut advice = 0_i32; // If host_page_size equals BALLOON_PAGE_SIZE and have the same share properties, // we can directly call the madvise function without any problem. And if the advice is // MADV_WILLNEED, we just hint the whole host page it lives on, since we can't do -- Gitee From f71799bc7ecda447fe3041a974d7bea29b8a8e7e Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Tue, 20 Aug 2024 16:17:29 +0800 Subject: [PATCH 2047/2187] balloon: Fix the unsafe shared reference to BALLOON_DEV The shared reference to mutable static will be hard error in the future Rust version, so Fix it. Signed-off-by: Liu Wenyuan --- virtio/src/device/balloon.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 34f241abc..b0190268b 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -719,7 +719,7 @@ impl BalloonIoHandler { .with_context(|| "Fail to parse available descriptor chain")?; // SAFETY: There is no confliction when writing global variable BALLOON_DEV, in other // words, this function will not be called simultaneously. - if let Some(dev) = unsafe { &BALLOON_DEV } { + if let Some(dev) = unsafe { BALLOON_DEV.as_ref() } { let mut balloon_dev = dev.lock().unwrap(); for iov in req.iovec.iter() { if let Some(stat) = iov_to_buf::(&self.mem_space, iov, 0) { @@ -1218,7 +1218,7 @@ impl VirtioDevice for Balloon { pub fn qmp_balloon(target: u64) -> bool { // SAFETY: there is no confliction when writing global variable BALLOON_DEV, in other // words, this function will not be called simultaneously. - if let Some(dev) = unsafe { &BALLOON_DEV } { + if let Some(dev) = unsafe { BALLOON_DEV.as_ref() } { match dev.lock().unwrap().set_guest_memory_size(target) { Ok(()) => { return true; @@ -1236,7 +1236,7 @@ pub fn qmp_balloon(target: u64) -> bool { pub fn qmp_query_balloon() -> Option { // SAFETY: There is no confliction when writing global variable BALLOON_DEV, in other // words, this function will not be called simultaneously. - if let Some(dev) = unsafe { &BALLOON_DEV } { + if let Some(dev) = unsafe { BALLOON_DEV.as_ref() } { let unlocked_dev = dev.lock().unwrap(); return Some(unlocked_dev.get_guest_memory_size()); } -- Gitee From f8512e0deeaaa75cd6ddf4f65ea65239a783c930 Mon Sep 17 00:00:00 2001 From: Liu Wenyuan Date: Tue, 20 Aug 2024 16:23:13 +0800 Subject: [PATCH 2048/2187] memory: Fix the memory dump madvise unsafe chain The unsafe madvise input params should be checked by the caller. Signed-off-by: Liu Wenyuan --- util/src/unix.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/src/unix.rs b/util/src/unix.rs index 2640d948d..bf103ee34 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -125,15 +125,15 @@ pub fn do_mmap( return Err(std::io::Error::last_os_error()).with_context(|| "Mmap failed."); } if !dump_guest_core { - set_memory_undumpable(hva, len); + // SAFETY: The hva and len are mmap-ed above and are verified. + unsafe { set_memory_undumpable(hva, len) }; } Ok(hva as u64) } -fn set_memory_undumpable(host_addr: *mut libc::c_void, size: u64) { - // SAFETY: host_addr and size are valid and return value is checked. - let ret = unsafe { libc::madvise(host_addr, size as libc::size_t, libc::MADV_DONTDUMP) }; +unsafe fn set_memory_undumpable(host_addr: *mut libc::c_void, size: u64) { + let ret = libc::madvise(host_addr, size as libc::size_t, libc::MADV_DONTDUMP); if ret < 0 { error!( "Syscall madvise(with MADV_DONTDUMP) failed, OS error is {:?}", -- Gitee From fef9e7a57cc84183810e3aee83eb6a2ae98abf1e Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Thu, 22 Aug 2024 11:21:54 +0800 Subject: [PATCH 2049/2187] virtio: add explicit type identification for numeric literals Add explicit type identification for numeric literals in case of overflow. Signed-off-by: Yan Wang --- virtio/src/device/block.rs | 8 ++++---- virtio/src/device/net.rs | 6 +++--- virtio/src/device/serial.rs | 2 +- virtio/src/lib.rs | 2 +- virtio/src/transport/virtio_pci.rs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 6e17764fb..055411645 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -600,9 +600,9 @@ impl BlockIoHandler { let mut merge_req_queue = Vec::::new(); let mut last_req: Option<&mut Request> = None; - let mut merged_reqs = 0; - let mut merged_iovs = 0; - let mut merged_bytes = 0; + let mut merged_reqs: u16 = 0; + let mut merged_iovs: usize = 0; + let mut merged_bytes: u64 = 0; for req in req_queue { let req_iovs = req.iovec.len(); @@ -743,7 +743,7 @@ impl BlockIoHandler { trace::virtio_blk_process_queue_suppress_notify(len); let mut done = false; - let mut iteration = 0; + let mut iteration: u16 = 0; while self .queue diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index b31084151..16e17da15 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -223,7 +223,7 @@ impl CtrlInfo { data_iovec: &mut Vec, ) -> Result { let ack = VIRTIO_NET_OK; - let mut mac_table_len = 0; + let mut mac_table_len: usize = 0; // Default for unicast. let mut overflow = &mut self.mac_info.uni_mac_of; let mut mac_table = &mut self.mac_info.uni_mac_table; @@ -705,7 +705,7 @@ impl NetIoQueue { } let mut queue = self.rx.queue.lock().unwrap(); - let mut rx_packets = 0; + let mut rx_packets: u16 = 0; loop { let elem = queue .vring @@ -804,7 +804,7 @@ impl NetIoQueue { trace::virtio_receive_request("Net".to_string(), "to tx".to_string()); let mut queue = self.tx.queue.lock().unwrap(); - let mut tx_packets = 0; + let mut tx_packets: u16 = 0; loop { let elem = queue .vring diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index ae76f87a7..37256dc72 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -193,7 +193,7 @@ impl Serial { } pub fn get_max_nr(ports: &Arc>>>>) -> u32 { - let mut max = 0; + let mut max: u32 = 0; for port in ports.lock().unwrap().iter() { let nr = port.lock().unwrap().nr; if nr > max { diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 3245477df..401554987 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -867,7 +867,7 @@ fn gpa_hva_iovec_map( mem_space: &AddressSpace, cache: &Option, ) -> Result<(u64, Vec)> { - let mut iov_size = 0; + let mut iov_size: u64 = 0; let mut hva_iovec = Vec::with_capacity(gpa_elemiovec.len()); for elem in gpa_elemiovec.iter() { diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 1bfaa2c20..00db23ee6 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -772,7 +772,7 @@ impl VirtioPciDevice { }; let common_write = move |data: &[u8], _addr: GuestAddress, offset: u64| -> bool { - let mut value = 0; + let mut value: u32 = 0; if !read_data_u32(data, &mut value) { return false; } -- Gitee From eba339a6d82dff6b8394fab76e814c6d2b5822e4 Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Thu, 22 Aug 2024 11:27:10 +0800 Subject: [PATCH 2050/2187] block_backend: add explicit type identification for numeric literals Add explicit type identification for numeric literals in case of overflow. Signed-off-by: Yan Wang --- block_backend/src/qcow2/cache.rs | 2 +- block_backend/src/qcow2/check.rs | 4 ++-- block_backend/src/qcow2/mod.rs | 8 ++++---- block_backend/src/qcow2/refcount.rs | 10 +++++----- block_backend/src/qcow2/snapshot.rs | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/block_backend/src/qcow2/cache.rs b/block_backend/src/qcow2/cache.rs index 245bbe67b..e54739f81 100644 --- a/block_backend/src/qcow2/cache.rs +++ b/block_backend/src/qcow2/cache.rs @@ -190,7 +190,7 @@ impl Qcow2Cache { ) -> Option>> { let mut replaced_entry: Option>> = None; let mut lru_count = u64::MAX; - let mut target_idx = 0; + let mut target_idx: u64 = 0; self.check_refcount(); entry.borrow_mut().lru_count = self.lru_count; self.lru_count += 1; diff --git a/block_backend/src/qcow2/check.rs b/block_backend/src/qcow2/check.rs index 35e4f350b..4313111a7 100644 --- a/block_backend/src/qcow2/check.rs +++ b/block_backend/src/qcow2/check.rs @@ -321,7 +321,7 @@ impl Qcow2Driver { if check.res.need_rebuild && check.fix & FIX_ERRORS != 0 { let old_res = check.res; - let mut fresh_leak = 0; + let mut fresh_leak: i32 = 0; output_msg!(check.quite, "Rebuilding refcount structure"); self.rebuild_refcount_structure(check)?; @@ -874,7 +874,7 @@ impl Qcow2Driver { } } - let mut num_repaired = 0; + let mut num_repaired: i32 = 0; let l2_buf = self.load_cluster(l2_offset)?; let l2_table = Rc::new(RefCell::new(CacheTable::new( l2_offset, diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 7c14cb981..a06739e64 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -390,9 +390,9 @@ impl Qcow2Driver { expect_len ); } - let mut host_start = 0; + let mut host_start: u64 = 0; let mut first_cluster_type = Qcow2ClusterType::Unallocated; - let mut cnt = 0; + let mut cnt: u64 = 0; while cnt < clusters { let offset = cnt * self.header.cluster_size(); let l2_entry = self.get_l2_entry(begin + offset)?; @@ -1690,7 +1690,7 @@ impl BlockDriverOps for Qcow2Driver { let mut left = iovec; let mut req_list: Vec = Vec::new(); - let mut copied = 0; + let mut copied: u64 = 0; while copied < nbytes { let pos = offset as u64 + copied; match self.host_offset_for_read(pos, nbytes - copied) { @@ -1727,7 +1727,7 @@ impl BlockDriverOps for Qcow2Driver { trace::block_write_vectored(&self.driver.block_prop.id, offset, nbytes); let mut req_list: Vec = Vec::new(); - let mut copied = 0; + let mut copied: u64 = 0; while copied < nbytes { let pos = offset as u64 + copied; let count = self.cluster_aligned_bytes(pos, nbytes - copied); diff --git a/block_backend/src/qcow2/refcount.rs b/block_backend/src/qcow2/refcount.rs index 7c60ce316..6e403da5d 100644 --- a/block_backend/src/qcow2/refcount.rs +++ b/block_backend/src/qcow2/refcount.rs @@ -151,7 +151,7 @@ impl RefCount { } let nb_clusters = bytes_to_clusters(size, self.cluster_size).unwrap(); - let mut free_clusters = 0; + let mut free_clusters: u64 = 0; while free_clusters < nb_clusters { let offset = self.free_cluster_index << self.cluster_bits; self.free_cluster_index += 1; @@ -327,7 +327,7 @@ impl RefCount { } let first_cluster = bytes_to_clusters(offset, self.cluster_size).unwrap(); let mut rc_vec: Vec<(u64, u64, usize)> = Vec::with_capacity(clusters as usize); - let mut i = 0; + let mut i: u64 = 0; while i < clusters { let rt_idx = (first_cluster + i) >> self.refcount_blk_bits; if rt_idx >= self.refcount_table_size { @@ -669,11 +669,11 @@ pub fn refcount_metadata_size( ) -> Result<(u64, u64)> { let reftable_entries = cluster_size / ENTRY_SIZE; let refblock_entries = cluster_size * 8 / (1 << refcount_order); - let mut table = 0; - let mut blocks = 0; + let mut table: u64 = 0; + let mut blocks: u64 = 0; let mut clusters = nb_clusters; let mut last_clusters; - let mut total_clusters = 0; + let mut total_clusters: u64 = 0; loop { last_clusters = total_clusters; diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index 7a858e597..9e8345d56 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -85,7 +85,7 @@ impl InternalSnapshot { } pub fn find_new_snapshot_id(&self) -> u64 { - let mut id_max = 0; + let mut id_max: u64 = 0; for snap in &self.snapshots { if id_max < snap.id { id_max = snap.id; @@ -139,7 +139,7 @@ impl InternalSnapshot { for i in 0..nb_snapshots { let offset = addr + self.snapshot_size; - let mut pos = 0; + let mut pos: usize = 0; let header_size = size_of::(); let mut header_buf = vec![0_u8; header_size]; self.sync_aio -- Gitee From 8216b79bd852b9cd5cc1ce65a2236ba6c08b0f0f Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Thu, 22 Aug 2024 11:34:14 +0800 Subject: [PATCH 2051/2187] util: add explicit type identification for numeric literals Add explicit type identification for numeric literals in case of overflow. Signed-off-by: Yan Wang --- util/src/aio/mod.rs | 2 +- util/src/aio/raw.rs | 2 +- util/src/arg_parser.rs | 4 ++-- util/src/bitmap.rs | 2 +- util/src/file.rs | 4 ++-- util/src/loop_context.rs | 4 ++-- util/src/num_ops.rs | 2 +- util/src/tap.rs | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index cc666f23f..56a142c8a 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -133,7 +133,7 @@ impl Iovec { } pub fn get_iov_size(iovecs: &[Iovec]) -> u64 { - let mut sum = 0; + let mut sum: u64 = 0; for iov in iovecs { sum += iov.iov_len; } diff --git a/util/src/aio/raw.rs b/util/src/aio/raw.rs index 4654a7c07..c376e5e03 100644 --- a/util/src/aio/raw.rs +++ b/util/src/aio/raw.rs @@ -171,7 +171,7 @@ fn do_fallocate( offset: u64, size: u64, ) -> i32 { - let mut ret = 0; + let mut ret: i32 = 0; loop { let mode = match &fallocate_mode { FallocateMode::PunchHole => FallocateMode::PunchHole, diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index 9ab971698..e92f052ad 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -626,8 +626,8 @@ fn parse_cmdline( let mut arg_map: BTreeMap> = BTreeMap::new(); let mut multi_vec: Vec = Vec::new(); - let mut i = (0, ""); - let mut j = 1; + let mut i: (usize, &str) = (0, ""); + let mut j: usize = 1; for cmd_arg in &cmd_args[1..] { if !allow_list.contains(cmd_arg) && cmd_arg.starts_with(PREFIX_CHARS_SHORT) diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index ae6afeb29..128a286c0 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -255,7 +255,7 @@ impl Bitmap { self.size() as u64 ))); } - let mut num = 0; + let mut num: usize = 0; for i in 0..self.bit_index(offset) + 1 { if i == self.bit_index(offset) { for j in i * T::len()..offset { diff --git a/util/src/file.rs b/util/src/file.rs index fd06c78e0..bacce3db8 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -67,8 +67,8 @@ pub fn get_file_alignment(file: &File, direct: bool) -> (u32, u32) { return (1, 1); } - let mut req_align = 0; - let mut buf_align = 0; + let mut req_align: u32 = 0; + let mut buf_align: u32 = 0; // SAFETY: we allocate aligned memory and free it later. let aligned_buffer = unsafe { libc::memalign( diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index ced2af402..42c94c5f5 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -296,7 +296,7 @@ impl EventLoopContext { fn clear_gc(&mut self) { let max_cnt = self.gc.write().unwrap().len(); - let mut pop_cnt = 0; + let mut pop_cnt: usize = 0; loop { // Loop to avoid hold lock for long time. @@ -640,7 +640,7 @@ impl EventLoopContext { /// Call function of the timers which have already expired. pub fn run_timers(&mut self) { let now = get_current_time(); - let mut expired_nr = 0; + let mut expired_nr: usize = 0; let mut timers = self.timers.lock().unwrap(); for timer in timers.iter() { diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index 558f57bfd..da9a6e994 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -451,7 +451,7 @@ int_trait_impl!(Num for u8 u16 usize); /// assert!(value == 17); /// ``` pub fn str_to_num(s: &str) -> Result { - let mut base = 10; + let mut base: u32 = 10; if s.starts_with("0x") || s.starts_with("0X") { base = 16; } diff --git a/util/src/tap.rs b/util/src/tap.rs index a523873e1..55c98a93f 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -115,7 +115,7 @@ impl Tap { )); } - let mut features = 0; + let mut features: u16 = 0; // SAFETY: The parameter of file can be guaranteed to be legal, and other parameters are constant. let ret = unsafe { ioctl_with_mut_ref(&file, TUNGETFEATURES(), &mut features) }; if ret < 0 { -- Gitee From 4781b63b52010d9c1f4ee30b930190a4ce4d62cd Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Aug 2024 19:17:23 +0800 Subject: [PATCH 2052/2187] machine_manager: Remove unnecessary cast Signed-off-by: Keqian Zhu --- machine_manager/src/config/machine_config.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/machine_manager/src/config/machine_config.rs b/machine_manager/src/config/machine_config.rs index 96ce5a11a..3d277ba15 100644 --- a/machine_manager/src/config/machine_config.rs +++ b/machine_manager/src/config/machine_config.rs @@ -436,13 +436,13 @@ impl VmConfig { SmpConfig::try_parse_from(str_slip_to_clap(cpu_config, !has_cpus_label, false))?; smp_cfg.auto_adjust_topology()?; - self.machine_config.nr_cpus = smp_cfg.cpus as u8; - self.machine_config.nr_threads = smp_cfg.threads as u8; - self.machine_config.nr_cores = smp_cfg.cores as u8; - self.machine_config.nr_dies = smp_cfg.dies as u8; - self.machine_config.nr_clusters = smp_cfg.clusters as u8; - self.machine_config.nr_sockets = smp_cfg.sockets as u8; - self.machine_config.max_cpus = smp_cfg.maxcpus as u8; + self.machine_config.nr_cpus = smp_cfg.cpus; + self.machine_config.nr_threads = smp_cfg.threads; + self.machine_config.nr_cores = smp_cfg.cores; + self.machine_config.nr_dies = smp_cfg.dies; + self.machine_config.nr_clusters = smp_cfg.clusters; + self.machine_config.nr_sockets = smp_cfg.sockets; + self.machine_config.max_cpus = smp_cfg.maxcpus; Ok(()) } -- Gitee From d0b61f50d73baef216a0430305afbf16606aa499 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Tue, 20 Aug 2024 10:14:43 +0800 Subject: [PATCH 2053/2187] OHUI/OHAUDIO/OHCAM: do safety check before unsafe block Do safety check before unsafe block. Signed-off-by: zhanghan64 --- devices/src/camera_backend/ohcam.rs | 4 +++ devices/src/misc/scream/ohaudio.rs | 10 ++++++ ui/src/ohui_srv/mod.rs | 56 +++++++++++++++++++---------- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index a67b7898a..bf5d8ee9e 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -340,6 +340,10 @@ impl CameraBackend for OhCameraBackend { .with_context(|| "Invalid camid in callback table")? .get_buffer(); + if src.is_none() || src.unwrap() == 0 { + bail!("Invalid frame src") + } + if src_len == 0_u64 { bail!("Invalid frame src_len {}", src_len); } diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index d6badff8f..d746eecfd 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -396,6 +396,11 @@ extern "C" fn on_write_data_cb( buffer: *mut ::std::os::raw::c_void, length: i32, ) -> i32 { + if buffer.is_null() || user_data.is_null() { + error!("on_write_data_cb: Invalid input"); + return 0; + } + // SAFETY: we make sure that it is OhAudioRender when register callback. let render = unsafe { (user_data as *mut OhAudioRender) @@ -426,6 +431,11 @@ extern "C" fn on_read_data_cb( buffer: *mut ::std::os::raw::c_void, length: i32, ) -> i32 { + if buffer.is_null() || user_data.is_null() { + error!("on_read_data_cb: Invalid input"); + return 0; + } + // SAFETY: we make sure that it is OhAudioCapture when register callback. let capture = unsafe { (user_data as *mut OhAudioCapture) diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 6f13d6b01..8370c5b53 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -24,7 +24,7 @@ use std::sync::{ Arc, Mutex, RwLock, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use log::{error, info}; use once_cell::sync::OnceCell; use vmm_sys_util::epoll::EventSet; @@ -213,7 +213,8 @@ impl OhUiServer { self.msg_handler.handle_msg(self.token_id.clone()) } - fn raw_update_dirty_area( + // check dirty area data before call it. + unsafe fn raw_update_dirty_area( &self, surface_data: *mut u32, stride: i32, @@ -224,7 +225,10 @@ impl OhUiServer { let (x, y) = pos; let (w, h) = size; - if self.framebuffer == 0 || (!force_copy && *self.passthru.get_or_init(|| false)) { + if self.framebuffer == 0 + || surface_data.is_null() + || (!force_copy && *self.passthru.get_or_init(|| false)) + { return; } @@ -293,13 +297,16 @@ impl DisplayChangeListenerOperations for OhUiServer { locked_surface.height = surface.height(); drop(locked_surface); let locked_surface = self.surface.read().unwrap(); - self.raw_update_dirty_area( - get_image_data(locked_surface.guest_image), - locked_surface.stride, - (0, 0), - (locked_surface.width, locked_surface.height), - true, - ); + // SAFETY: Dirty area does not exceed surface buffer. + unsafe { + self.raw_update_dirty_area( + get_image_data(locked_surface.guest_image), + locked_surface.stride, + (0, 0), + (locked_surface.width, locked_surface.height), + true, + ) + }; if !self.connected() { return Ok(()); @@ -325,13 +332,24 @@ impl DisplayChangeListenerOperations for OhUiServer { return Ok(()); } - self.raw_update_dirty_area( - get_image_data(locked_surface.guest_image), - locked_surface.stride, - (x, y), - (w, h), - false, - ); + if locked_surface.width < x + || locked_surface.height < y + || locked_surface.width < x.saturating_add(w) + || locked_surface.height < y.saturating_add(h) + { + bail!("dpy_image_update: invalid dirty area"); + } + + // SAFETY: We checked dirty area data before. + unsafe { + self.raw_update_dirty_area( + get_image_data(locked_surface.guest_image), + locked_surface.stride, + (x, y), + (w, h), + false, + ) + }; self.msg_handler .handle_dirty_area(x as u32, y as u32, w as u32, h as u32); @@ -346,13 +364,13 @@ impl DisplayChangeListenerOperations for OhUiServer { } let len = cursor.width * cursor.height * size_of::() as u32; - if len > CURSOR_SIZE as u32 { + if len > CURSOR_SIZE as u32 || len > cursor.data.len().try_into()? { error!("Too large cursor length {}.", len); // No need to return Err for this situation is not fatal return Ok(()); } - // SAFETY: len is checked before copying,it's safe to do this. + // SAFETY: len is checked before copying, it's safe to do this. unsafe { ptr::copy_nonoverlapping( cursor.data.as_ptr(), -- Gitee From e8ceca0fb29bd69bd60f240ca61d44a8a42daa2a Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Fri, 23 Aug 2024 15:04:00 +0800 Subject: [PATCH 2054/2187] virtio: add note for unchecked arithmetic operations Add note for unchecked arithmetic operations to indicate that it is safe. Signed-off-by: Yan Wang --- virtio/src/lib.rs | 4 ++++ virtio/src/queue/mod.rs | 2 ++ virtio/src/queue/split.rs | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 401554987..6d4e8805c 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -819,6 +819,8 @@ pub fn iov_to_buf( let mut start: usize = 0; let mut end: usize = 0; + // Note: iovec is part of elem.in_iovec/out_iovec which has been checked + // in pop_avail(). The sum of iov_len is not greater than u32::MAX. for iov in iovec { let mut addr_map = Vec::new(); mem_space.get_address_map(cache, iov.addr, u64::from(iov.len), &mut addr_map)?; @@ -870,6 +872,8 @@ fn gpa_hva_iovec_map( let mut iov_size: u64 = 0; let mut hva_iovec = Vec::with_capacity(gpa_elemiovec.len()); + // Note: gpa_elemiovec is part of elem.in_iovec/out_iovec which has been checked + // in pop_avail(). The sum of iov_len is not greater than u32::MAX. for elem in gpa_elemiovec.iter() { mem_space.get_address_map(cache, elem.addr, u64::from(elem.len), &mut hva_iovec)?; iov_size += u64::from(elem.len); diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index 93d950446..d226cd703 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -91,6 +91,8 @@ impl Element { pub fn iovec_size(iovec: &[ElemIovec]) -> u64 { let mut size: u64 = 0; for elem in iovec.iter() { + // Note: iovec is part of elem.in_iovec/out_iovec which has been checked + // in pop_avail(). The sum of iov_len is not greater than u32::MAX. size += u64::from(elem.len); } size diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 58f8250ae..092df0e10 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -437,6 +437,10 @@ impl SplitVringDesc { elem.out_iovec.push(iovec); } elem.desc_num += 1; + // Note: iovec.addr + iovec.len is located in RAM, and iovec.len is not greater than the + // VM RAM size. The number of iovec is not greater than 'queue_size * 2 - 1' which with + // a indirect table. Currently, the max value of queue_size is 1024. So, desc_total_len + // must not overflow. desc_total_len += u64::from(iovec.len); if desc.has_next() { -- Gitee From 67cc9b6ff52b7adb63406abfe30ba30dd9d15f83 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Mon, 26 Aug 2024 10:35:08 +0800 Subject: [PATCH 2055/2187] Memory: Fix potential unsafe chain breaks --- address_space/src/address_space.rs | 24 +++--- virtio/src/queue/split.rs | 127 ++++++++++++++++++----------- 2 files changed, 91 insertions(+), 60 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 4e8e35fcf..8caf6ca79 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -695,17 +695,18 @@ impl AddressSpace { /// * `data` - The object that will be written to the memory. /// * `host_addr` - The start host address where the object will be written to. /// + /// # Safety + /// + /// Make true that host_addr and std::mem::size_of::() are in the range of ram. + /// /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { + pub unsafe fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { trace::address_space_write_direct(host_addr, std::mem::size_of::()); // Mark vmm dirty page manually if live migration is active. MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); - - // SAFETY: The host addr is managed by memory space, it has been verified. - let mut dst = unsafe { - std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) - }; + let mut dst = + std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()); dst.write_all(data.as_bytes()) .with_context(|| "Failed to write object via host address") } @@ -735,16 +736,17 @@ impl AddressSpace { /// /// * `hoat_addr` - The start host address where the data will be read from. /// + /// # Safety + /// + /// Make true that host_addr and std::mem::size_of::() are in the range of ram. + /// /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn read_object_direct(&self, host_addr: u64) -> Result { + pub unsafe fn read_object_direct(&self, host_addr: u64) -> Result { trace::address_space_read_direct(host_addr, std::mem::size_of::()); let mut obj = T::default(); let mut dst = obj.as_mut_bytes(); - // SAFETY: host_addr is managed by address_space, it has been verified for legality. - let src = unsafe { - std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()) - }; + let src = std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()); dst.write_all(src) .with_context(|| "Failed to read object via host address")?; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 58f8250ae..4e3af6d3c 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -265,9 +265,12 @@ impl SplitVringDesc { u64::from(index) * DESCRIPTOR_LEN, ) })?; - let desc = sys_mem - .read_object_direct::(desc_addr) - .with_context(|| VirtioError::ReadObjectErr("a descriptor", desc_addr))?; + // SAFETY: dest_addr has been checked in SplitVringDesc::is_valid() and is guaranteed to be within the ram range. + let desc = unsafe { + sys_mem + .read_object_direct::(desc_addr) + .with_context(|| VirtioError::ReadObjectErr("a descriptor", desc_addr)) + }?; if desc.is_valid(sys_mem, queue_size, cache) { Ok(desc) @@ -498,11 +501,14 @@ impl SplitVring { /// Get the flags and idx of the available ring from guest memory. fn get_avail_flags_idx(&self, sys_mem: &Arc) -> Result { - sys_mem - .read_object_direct::(self.addr_cache.avail_ring_host) - .with_context(|| { - VirtioError::ReadObjectErr("avail flags idx", self.avail_ring.raw_value()) - }) + // SAFETY: avail_ring_host is checked when addr_cache inited. + unsafe { + sys_mem + .read_object_direct::(self.addr_cache.avail_ring_host) + .with_context(|| { + VirtioError::ReadObjectErr("avail flags idx", self.avail_ring.raw_value()) + }) + } } /// Get the idx of the available ring from guest memory. @@ -521,11 +527,14 @@ impl SplitVring { fn get_used_flags_idx(&self, sys_mem: &Arc) -> Result { // Make sure the idx read from sys_mem is new. fence(Ordering::SeqCst); - sys_mem - .read_object_direct::(self.addr_cache.used_ring_host) - .with_context(|| { - VirtioError::ReadObjectErr("used flags idx", self.used_ring.raw_value()) - }) + // SAFETY: used_ring_host has been checked in set_addr_cache() and is guaranteed to be within the ram range. + unsafe { + sys_mem + .read_object_direct::(self.addr_cache.used_ring_host) + .with_context(|| { + VirtioError::ReadObjectErr("used flags idx", self.used_ring.raw_value()) + }) + } } /// Get the index of the used ring from guest memory. @@ -543,14 +552,20 @@ impl SplitVring { } else { flags_idx.flags &= !VRING_USED_F_NO_NOTIFY; } - sys_mem - .write_object_direct::(&flags_idx, self.addr_cache.used_ring_host) - .with_context(|| { - format!( - "Failed to set used flags, used_ring: 0x{:X}", - self.used_ring.raw_value() + // SAFETY: used_ring_host has been checked when addr_cache inited. + unsafe { + sys_mem + .write_object_direct::( + &flags_idx, + self.addr_cache.used_ring_host, ) - })?; + .with_context(|| { + format!( + "Failed to set used flags, used_ring: 0x{:X}", + self.used_ring.raw_value() + ) + }) + }?; // Make sure the data has been set. fence(Ordering::SeqCst); Ok(()) @@ -561,19 +576,21 @@ impl SplitVring { trace::virtqueue_set_avail_event(self as *const _ as u64, event_idx); let avail_event_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(self.actual_size()); - - sys_mem - .write_object_direct( - &event_idx, - self.addr_cache.used_ring_host + avail_event_offset, - ) - .with_context(|| { - format!( - "Failed to set avail event idx, used_ring: 0x{:X}, offset: {}", - self.used_ring.raw_value(), - avail_event_offset, + // SAFETY: used_ring_host has been checked in set_addr_cache(). + unsafe { + sys_mem + .write_object_direct( + &event_idx, + self.addr_cache.used_ring_host + avail_event_offset, ) - })?; + .with_context(|| { + format!( + "Failed to set avail event idx, used_ring: 0x{:X}, offset: {}", + self.used_ring.raw_value(), + avail_event_offset, + ) + }) + }?; // Make sure the data has been set. fence(Ordering::SeqCst); Ok(()) @@ -588,9 +605,12 @@ impl SplitVring { // The GPA of avail_ring_host with avail table length has been checked in // is_invalid_memory which must not be overflowed. let used_event_addr = self.addr_cache.avail_ring_host + used_event_offset; - let used_event = sys_mem - .read_object_direct::(used_event_addr) - .with_context(|| VirtioError::ReadObjectErr("used event id", used_event_addr))?; + // SAFETY: used_event_addr is protected by virtio calculations and is guaranteed to be within the ram range. + let used_event = unsafe { + sys_mem + .read_object_direct::(used_event_addr) + .with_context(|| VirtioError::ReadObjectErr("used event id", used_event_addr)) + }?; Ok(used_event) } @@ -750,11 +770,14 @@ impl SplitVring { // The GPA of avail_ring_host with avail table length has been checked in // is_invalid_memory which must not be overflowed. let desc_index_addr = self.addr_cache.avail_ring_host + index_offset; - let desc_index = sys_mem - .read_object_direct::(desc_index_addr) - .with_context(|| { - VirtioError::ReadObjectErr("the index of descriptor", desc_index_addr) - })?; + // SAFETY: dest_index_addr is protected by virtio calculations and is guaranteed to be within the ram range. + let desc_index = unsafe { + sys_mem + .read_object_direct::(desc_index_addr) + .with_context(|| { + VirtioError::ReadObjectErr("the index of descriptor", desc_index_addr) + }) + }?; let desc = SplitVringDesc::new( sys_mem, @@ -860,19 +883,25 @@ impl VringOps for SplitVring { id: u32::from(index), len, }; - sys_mem - .write_object_direct::(&used_elem, used_elem_addr) - .with_context(|| "Failed to write object for used element")?; + // SAFETY: used_elem_addr is guaranteed to be within ram range. + unsafe { + sys_mem + .write_object_direct::(&used_elem, used_elem_addr) + .with_context(|| "Failed to write object for used element") + }?; // Make sure used element is filled before updating used idx. fence(Ordering::Release); self.next_used += Wrapping(1); - sys_mem - .write_object_direct( - &(self.next_used.0), - self.addr_cache.used_ring_host + VRING_IDX_POSITION, - ) - .with_context(|| "Failed to write next used idx")?; + // SAFETY: used_ring_host has been checked when addr_cache inited. + unsafe { + sys_mem + .write_object_direct( + &(self.next_used.0), + self.addr_cache.used_ring_host + VRING_IDX_POSITION, + ) + .with_context(|| "Failed to write next used idx") + }?; // Make sure used index is exposed before notifying guest. fence(Ordering::SeqCst); -- Gitee From 86c3914a76e1d4d1a05ec508666f76bdf2f34440 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 23 Aug 2024 16:02:24 +0800 Subject: [PATCH 2056/2187] usb_host: Free a list of libusb_pollfd structures to avoid memory leak All pollfd lists allocated with libusb_get_pollfds() should be freed by calling libusb_free_pollfds() to avoid memory leak. Signed-off-by: Fan Xuan Zhe fanxuanzhe@huawei.com --- Cargo.lock | 4 ++-- devices/Cargo.toml | 2 +- devices/src/usb/usbhost/host_usblib.rs | 8 +++++++- devices/src/usb/usbhost/mod.rs | 3 +++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9453a417a..36c8f8418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -936,9 +936,9 @@ dependencies = [ [[package]] name = "libusb1-sys" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" +checksum = "17f6bace2f39082e9787c851afce469e7b2fe0f1cc64bbc68ca96653b63d8f17" dependencies = [ "cc", "libc", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index d411cc5c6..932a0cdd1 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -35,7 +35,7 @@ pulse = { version = "2.27", package = "libpulse-binding", optional = true } psimple = { version = "2.27", package = "libpulse-simple-binding", optional = true } alsa = { version = "0.7.0", optional = true } rusb = { version = "0.9", optional = true } -libusb1-sys = { version = "0.6.4", optional = true } +libusb1-sys = { version = "0.6.5", optional = true } trace = { path = "../trace" } clap = { version = "=4.1.4", default-features = false, features = ["std", "derive"] } hisysevent = { path = "../hisysevent" } diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 913760746..8807909b9 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -27,7 +27,8 @@ use libusb1_sys::{ LIBUSB_TRANSFER_COMPLETED, LIBUSB_TRANSFER_ERROR, LIBUSB_TRANSFER_NO_DEVICE, LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_TIMED_OUT, LIBUSB_TRANSFER_TYPE_ISOCHRONOUS, }, - libusb_get_pollfds, libusb_iso_packet_descriptor, libusb_pollfd, libusb_transfer, + libusb_free_pollfds, libusb_get_pollfds, libusb_iso_packet_descriptor, libusb_pollfd, + libusb_transfer, }; use log::error; use rusb::{Context, DeviceHandle, Error, Result, TransferType, UsbContext}; @@ -121,6 +122,11 @@ pub fn get_libusb_pollfds(usbhost: Arc>) -> *const *mut libusb_po unsafe { libusb_get_pollfds(usbhost.lock().unwrap().context.as_raw()) } } +pub unsafe fn free_libusb_pollfds(pollfds: *const *mut libusb_pollfd) { + // Pollfds should be guaranteed to be valid. + libusb_free_pollfds(pollfds) +} + pub fn set_pollfd_notifiers( poll: *const *mut libusb_pollfd, notifiers: &mut Vec, diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 62a2382e5..4bf1325ee 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -1044,6 +1044,9 @@ impl EventNotifierHelper for UsbHost { set_pollfd_notifiers(poll, &mut notifiers, handler); + // SAFETY: pointer of pollfds acquired from libusb_get_pollfds is guaranteed to be valid. + unsafe { free_libusb_pollfds(poll) }; + notifiers } } -- Gitee From 30cd4e39027067bf96775f75ba55e962b76958d9 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Wed, 21 Aug 2024 11:40:12 +0800 Subject: [PATCH 2057/2187] Memory: Numeric literals need to ne explicitly typed part2 --- address_space/src/region.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 52e5f43fb..5d908db0e 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -201,7 +201,7 @@ macro_rules! rw_multi_ops { let offset = $args.offset; let cnt = $args.count; let access_size = $args.access_size; - let mut pos = 0; + let mut pos = 0_u64; for _ in 0..(cnt / access_size) { if !$ops( &mut $slice[pos as usize..(pos + access_size) as usize], -- Gitee From 9204f4af0312c7ccef9cbd63f665b503bab2674d Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Wed, 21 Aug 2024 12:40:00 +0800 Subject: [PATCH 2058/2187] Memory: arithmetic conversion enhance. --- address_space/src/address.rs | 1 + address_space/src/host_mmap.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/address_space/src/address.rs b/address_space/src/address.rs index 6fd1a8dcb..94a79162c 100644 --- a/address_space/src/address.rs +++ b/address_space/src/address.rs @@ -175,6 +175,7 @@ impl AddressRange { return None; } let start = std::cmp::max(self.base, other.base); + // SAFETY: The range of a region will not exceed 64 bits. let size_inter = (std::cmp::min(end, other_end) - u128::from(start.0)) as u64; Some(AddressRange { diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index c4b8ce178..fa807a80e 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -30,7 +30,7 @@ use util::{ unix::{do_mmap, host_page_size}, }; -const MAX_PREALLOC_THREAD: u8 = 16; +const MAX_PREALLOC_THREAD: i64 = 16; /// Verify existing pages in the mapping. const MPOL_MF_STRICT: u32 = 1; /// Move pages owned by this process to conform to mapping. @@ -171,7 +171,8 @@ fn max_nr_threads(nr_vcpus: u8) -> u8 { return 1; } - min(min(nr_host_cpu as u8, MAX_PREALLOC_THREAD), nr_vcpus) + // MAX_PREALLOC_THREAD's value(16) is less than 255. + min(min(nr_host_cpu, MAX_PREALLOC_THREAD) as u8, nr_vcpus) } /// Touch pages to pre-alloc memory for VM. -- Gitee From 3f4f76f0258345ac3121e7cdfdb2c4ff84dd8f0b Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Thu, 22 Aug 2024 09:37:35 +0800 Subject: [PATCH 2059/2187] Use iterators instead of subscript traversals --- virtio/src/device/balloon.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index b0190268b..22462a792 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -458,17 +458,17 @@ impl BlnMemInfo { fn get_host_address(&self, addr: GuestAddress) -> Option<(u64, bool)> { let all_regions = self.regions.lock().unwrap(); - for i in 0..all_regions.len() { - if addr.raw_value() < all_regions[i].guest_phys_addr + all_regions[i].memory_size - && addr.raw_value() >= all_regions[i].guest_phys_addr + for region in all_regions.iter() { + if addr.raw_value() < region.guest_phys_addr + region.memory_size + && addr.raw_value() >= region.guest_phys_addr { return Some(( - all_regions[i].userspace_addr + addr.raw_value() - - all_regions[i].guest_phys_addr, - all_regions[i].mem_share, + region.userspace_addr + addr.raw_value() - region.guest_phys_addr, + region.mem_share, )); } } + None } -- Gitee From c80c449cb4b3ddf67c4dd1f7bf06208e1ea5705a Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Thu, 22 Aug 2024 13:09:08 +0800 Subject: [PATCH 2060/2187] PFlash: Dada type specification rectification --- devices/src/legacy/pflash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 67d8aacbb..a5c16ccd1 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -861,7 +861,7 @@ impl SysBusDevOps for PFlash { } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { - let mut value = 0; + let mut value = 0_u32; if !read_data_u32(data, &mut value) { return false; } -- Gitee From 6efd66301a7ab07dd4384fdd178567b46dc8ac2b Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 23 Aug 2024 17:01:56 +0800 Subject: [PATCH 2061/2187] usb_host: Close the related fd when unrealize usb_host device After unrealizing the USB pass-through device, add processing logic to close the corresponding file descriptor. Signed-off-by: Fan Xuan Zhe fanxuanzhe@huawei.com --- devices/src/usb/usbhost/mod.rs | 2 +- devices/src/usb/usbhost/ohusb.rs | 58 +++++++++++++++----------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 4bf1325ee..b58c8d1b0 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -422,7 +422,7 @@ unsafe impl Send for UsbHost {} impl UsbHost { pub fn new(config: UsbHostConfig) -> Result { #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] - let oh_dev = OhUsbDev::new()?; + let oh_dev = OhUsbDev::new(config.hostbus, config.hostaddr)?; let mut context = Context::new()?; context.set_log_level(rusb::LogLevel::None); diff --git a/devices/src/usb/usbhost/ohusb.rs b/devices/src/usb/usbhost/ohusb.rs index 469a17a07..0e56acef5 100644 --- a/devices/src/usb/usbhost/ohusb.rs +++ b/devices/src/usb/usbhost/ohusb.rs @@ -10,12 +10,13 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::fd::AsRawFd; +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd}; use std::ptr; use anyhow::{bail, Context as anyhowContext, Result}; use libusb1_sys::constants::LIBUSB_OPTION_NO_DEVICE_DISCOVERY; -use log::{error, info}; +use log::info; use rusb::{Context, DeviceHandle, UsbContext}; use super::host_usblib::set_option; @@ -23,53 +24,48 @@ use super::{check_device_valid, UsbHostConfig}; use util::ohos_binding::usb::*; pub struct OhUsbDev { - dev: OhusbDevice, + #[allow(dead_code)] lib: OhUsb, -} - -impl Drop for OhUsbDev { - fn drop(&mut self) { - if let Err(e) = self.lib.close_device(ptr::addr_of_mut!(self.dev)) { - error!("Failed to close usb device with error {:?}", e) - } - } + dev_file: File, } impl OhUsbDev { - pub fn new() -> Result { + pub fn new(bus_num: u8, dev_addr: u8) -> Result { // In combination with libusb_wrap_sys_device(), in order to access a device directly without prior device scanning on ohos. set_option(LIBUSB_OPTION_NO_DEVICE_DISCOVERY)?; - Ok(Self { - dev: OhusbDevice { - busNum: u8::MAX, - devAddr: u8::MAX, - fd: -1, - }, - lib: OhUsb::new()?, - }) - } - - pub fn open(&mut self, cfg: UsbHostConfig, ctx: Context) -> Result> { - self.dev.busNum = cfg.hostbus; - self.dev.devAddr = cfg.hostaddr; + let mut ohusb_dev = OhusbDevice { + busNum: bus_num, + devAddr: dev_addr, + fd: -1, + }; - match self.lib.open_device(ptr::addr_of_mut!(self.dev))? { + let lib = OhUsb::new()?; + match lib.open_device(ptr::addr_of_mut!(ohusb_dev))? { 0 => { - if self.dev.fd < 0 { + if ohusb_dev.fd < 0 { bail!( "Failed to open usb device due to invalid fd {}", - self.dev.fd + ohusb_dev.fd ); } } _ => bail!("Failed to open usb device"), } - info!("OH USB: open_device: returned fd is {}", self.dev.fd); + info!("OH USB: open_device: returned fd is {}", ohusb_dev.fd); - // SAFETY: fd is valid. + Ok(Self { + lib, + // SAFETY: fd is passed from OH USB framework and we have checked the function return value. + // Now let's save it to rust File struct. + dev_file: unsafe { File::from_raw_fd(ohusb_dev.fd) }, + }) + } + + pub fn open(&mut self, cfg: UsbHostConfig, ctx: Context) -> Result> { + // SAFETY: The validation of fd is guaranteed by new function. let handle = unsafe { - ctx.open_device_with_fd(self.dev.fd.as_raw_fd()) + ctx.open_device_with_fd(self.dev_file.as_raw_fd()) .with_context(|| format!("os last error: {:?}", std::io::Error::last_os_error()))? }; -- Gitee From cccaaecfe104ba14bd4779aafd742b71b5ba06e9 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 23 Aug 2024 17:13:09 +0800 Subject: [PATCH 2062/2187] usb_host: Marked the numerical literals in usb_host with specific types Solve the problem that numerical literals in usb_host are not marked with specific types to prevent problems caused by inconsistent inference results on different platforms. Signed-off-by: Fan Xuan Zhe fanxuanzhe@huawei.com --- devices/src/usb/usbhost/host_usblib.rs | 2 +- devices/src/usb/usbhost/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 8807909b9..2f6bc88f9 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -132,7 +132,7 @@ pub fn set_pollfd_notifiers( notifiers: &mut Vec, handler: Rc, ) { - let mut i = 0; + let mut i: isize = 0; // SAFETY: have checked whether the pointer is null before dereference it. unsafe { loop { diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index b58c8d1b0..196baa2c5 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -844,7 +844,7 @@ impl UsbHost { drop(locked_requests); // Max counts of uncompleted request to be handled. - let mut limit = 100; + let mut limit: i32 = 100; loop { if self.requests.lock().unwrap().len == 0 { return Ok(()); -- Gitee From 1708823fb1b01b3a33afdf59f232e5b78e35469e Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 23 Aug 2024 17:20:42 +0800 Subject: [PATCH 2063/2187] usb_host: Handle possible overflow when getting device speed When the return value(i.g. speed) of libusb_get_device_speed is zero which means unknown speed, the following speed -1 operation would cause overflow. Signed-off-by: Fan Xuan Zhe fanxuanzhe@huawei.com --- devices/src/usb/usbhost/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 196baa2c5..267d60dd3 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -671,7 +671,15 @@ impl UsbHost { self.ep_update(); - self.base.speed = self.libdev.as_ref().unwrap().speed() as u32 - 1; + match self.libdev.as_ref().unwrap().speed() as u32 { + 0 => { + return Err(anyhow!( + "Failed to realize usb host device due to unknown device speed." + )) + } + speed => self.base.speed = speed - 1, + }; + trace::usb_host_open_success(self.config.hostbus, self.config.hostaddr); Ok(()) -- Gitee From 7a73b75e7e99c50bbdf2a1f20dfc6cbe6c335056 Mon Sep 17 00:00:00 2001 From: Fan Xuan Zhe Date: Fri, 23 Aug 2024 17:28:39 +0800 Subject: [PATCH 2064/2187] usb_host: Fix unsafe propagation chain breaking The function of get_libusb_pollfds which aims to obtain pollfd breaks the unsafe propagation chain, this patch fixed it. Signed-off-by: Fan Xuan Zhe fanxuanzhe@huawei.com --- devices/src/usb/usbhost/host_usblib.rs | 142 ++++++++++++++++++------- devices/src/usb/usbhost/mod.rs | 10 +- 2 files changed, 109 insertions(+), 43 deletions(-) diff --git a/devices/src/usb/usbhost/host_usblib.rs b/devices/src/usb/usbhost/host_usblib.rs index 2f6bc88f9..8c5542afb 100644 --- a/devices/src/usb/usbhost/host_usblib.rs +++ b/devices/src/usb/usbhost/host_usblib.rs @@ -11,11 +11,14 @@ // See the Mulan PSL v2 for more details. use std::{ + iter::Iterator, + os::unix::io::{AsRawFd, RawFd}, rc::Rc, + slice, sync::{Arc, Mutex}, }; -use libc::{c_int, c_uint, c_void, EPOLLIN, EPOLLOUT}; +use libc::{c_int, c_short, c_uint, c_void, EPOLLIN, EPOLLOUT}; #[cfg(all(target_arch = "aarch64", target_env = "ohos"))] use libusb1_sys::{constants::LIBUSB_SUCCESS, libusb_context, libusb_set_option}; use libusb1_sys::{ @@ -117,46 +120,28 @@ pub fn map_packet_status(status: i32) -> UsbPacketStatus { } } -pub fn get_libusb_pollfds(usbhost: Arc>) -> *const *mut libusb_pollfd { - // SAFETY: call C library of libusb to get pointer of poll fd. - unsafe { libusb_get_pollfds(usbhost.lock().unwrap().context.as_raw()) } -} - -pub unsafe fn free_libusb_pollfds(pollfds: *const *mut libusb_pollfd) { - // Pollfds should be guaranteed to be valid. - libusb_free_pollfds(pollfds) -} - pub fn set_pollfd_notifiers( - poll: *const *mut libusb_pollfd, + pollfds: PollFds, notifiers: &mut Vec, handler: Rc, ) { - let mut i: isize = 0; - // SAFETY: have checked whether the pointer is null before dereference it. - unsafe { - loop { - if (*poll.offset(i)).is_null() { - break; - }; - if i32::from((*(*poll.offset(i))).events) == EPOLLIN { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - (*(*poll.offset(i))).fd, - None, - EventSet::IN, - vec![handler.clone()], - )); - } else if i32::from((*(*poll.offset(i))).events) == EPOLLOUT { - notifiers.push(EventNotifier::new( - NotifierOperation::AddShared, - (*(*poll.offset(i))).fd, - None, - EventSet::OUT, - vec![handler.clone()], - )); - } - i += 1; + for pollfd in pollfds.iter() { + if i32::from(pollfd.events()) == EPOLLIN { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + pollfd.as_raw_fd(), + None, + EventSet::IN, + vec![handler.clone()], + )); + } else if i32::from(pollfd.events()) == EPOLLOUT { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + pollfd.as_raw_fd(), + None, + EventSet::OUT, + vec![handler.clone()], + )); } } } @@ -405,3 +390,86 @@ pub fn set_option(opt: u32) -> Result<()> { Ok(()) } + +#[derive(Debug)] +pub struct PollFd { + fd: c_int, + events: c_short, +} + +impl PollFd { + unsafe fn from_raw(raw: *mut libusb_pollfd) -> Self { + Self { + fd: (*raw).fd, + events: (*raw).events, + } + } + + pub fn events(&self) -> c_short { + self.events + } +} + +impl AsRawFd for PollFd { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +pub struct PollFds { + poll_fds: *const *mut libusb_pollfd, +} + +impl PollFds { + pub unsafe fn new(usbhost: Arc>) -> Result { + let poll_fds = libusb_get_pollfds(usbhost.lock().unwrap().context.as_raw()); + if poll_fds.is_null() { + Err(Error::NotFound) + } else { + Ok(Self { poll_fds }) + } + } + + pub fn iter(&self) -> PollFdIter { + let mut len: usize = 0; + // SAFETY: self.poll_fds is acquired from libusb_get_pollfds which is guaranteed to be valid. + unsafe { + while !(*self.poll_fds.add(len)).is_null() { + len += 1; + } + PollFdIter { + fds: slice::from_raw_parts(self.poll_fds, len), + index: 0, + } + } + } +} + +impl Drop for PollFds { + fn drop(&mut self) { + // SAFETY: self.poll_fds is acquired from libusb_get_pollfds which is guaranteed to be valid. + unsafe { + libusb_free_pollfds(self.poll_fds); + } + } +} + +pub struct PollFdIter<'a> { + fds: &'a [*mut libusb_pollfd], + index: usize, +} + +impl<'a> Iterator for PollFdIter<'a> { + type Item = PollFd; + + fn next(&mut self) -> Option { + if self.index < self.fds.len() { + // SAFETY: self.fds is guaranteed to be valid. + let poll_fd = unsafe { PollFd::from_raw(self.fds[self.index]) }; + self.index += 1; + Some(poll_fd) + } else { + None + } + } +} diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index 267d60dd3..bc79fd38f 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -1038,7 +1038,6 @@ impl EventNotifierHelper for UsbHost { let cloned_usbhost = usbhost.clone(); let mut notifiers = Vec::new(); - let poll = get_libusb_pollfds(usbhost); let timeout = Some(Duration::new(0, 0)); let handler: Rc = Rc::new(move |_, _fd: RawFd| { cloned_usbhost @@ -1049,11 +1048,10 @@ impl EventNotifierHelper for UsbHost { .unwrap_or_else(|e| error!("Failed to handle event: {:?}", e)); None }); - - set_pollfd_notifiers(poll, &mut notifiers, handler); - - // SAFETY: pointer of pollfds acquired from libusb_get_pollfds is guaranteed to be valid. - unsafe { free_libusb_pollfds(poll) }; + // SAFETY: The usbhost is guaranteed to be valid. + if let Ok(pollfds) = unsafe { PollFds::new(usbhost) } { + set_pollfd_notifiers(pollfds, &mut notifiers, handler); + } notifiers } -- Gitee From 6521b4ab89f2681db036aa5752c4b507950538a2 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Fri, 23 Aug 2024 11:31:38 +0800 Subject: [PATCH 2065/2187] UI: clear hardware cursor buffer when dpy switch When switch dpy surface, clear cursor buffer. Signed-off-by: zhanghan64 --- ui/src/ohui_srv/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ui/src/ohui_srv/mod.rs b/ui/src/ohui_srv/mod.rs index 8370c5b53..45a51a8e5 100755 --- a/ui/src/ohui_srv/mod.rs +++ b/ui/src/ohui_srv/mod.rs @@ -85,6 +85,8 @@ impl GuestSurface { } const CURSOR_SIZE: u64 = 16 * 1024; +const DEFAULT_CURSOR_WIDTH: u32 = 128; +const DEFAULT_CURSOR_HEIGHT: u32 = 128; pub struct OhUiServer { // framebuffer passthru to the guest @@ -282,6 +284,17 @@ impl OhUiServer { error!("Failed to initialize iothread of OHUI Server."); } } + + fn clear_cursor_buffer(&self) { + if self.cursorbuffer == 0 { + error!("Cursor buffer is invalid."); + return; + } + //SAFETY: we make sure that buffer info is valid. + unsafe { + ptr::write_bytes(self.cursorbuffer as *mut u8, 0, CURSOR_SIZE as usize); + } + } } impl DisplayChangeListenerOperations for OhUiServer { @@ -307,12 +320,20 @@ impl DisplayChangeListenerOperations for OhUiServer { true, ) }; + self.clear_cursor_buffer(); if !self.connected() { return Ok(()); } self.msg_handler .send_windowinfo(locked_surface.width as u32, locked_surface.height as u32); + self.msg_handler.handle_cursor_define( + DEFAULT_CURSOR_WIDTH, + DEFAULT_CURSOR_HEIGHT, + 0, + 0, + bytes_per_pixel().try_into()?, + ); Ok(()) } -- Gitee From 599dcc769f0207bbbca410ff1cad8dcba7714cb2 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 30 Aug 2024 15:18:21 +0800 Subject: [PATCH 2066/2187] scream: use secure remain function to caculate chunk idx The driver might clear scream capture header, e.g. if BSOD occurred while voice capture is ongoing, the driver will clear the header after VM restarted, then scream backend will div 0 and crash would occur. So let's use secure remain function. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 5db2c93c5..c0b4d0d14 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -449,7 +449,15 @@ impl StreamData { match recv_chunks_cnt.cmp(&0) { std::cmp::Ordering::Less => thread::sleep(time::Duration::from_millis(100)), std::cmp::Ordering::Greater => { - self.chunk_idx = (self.chunk_idx + recv_chunks_cnt as u16) % capt.max_chunks; + self.chunk_idx = match (self.chunk_idx + recv_chunks_cnt as u16) + .checked_rem(capt.max_chunks) + { + Some(idx) => idx, + None => { + warn!("Scream: capture header might be cleared by driver"); + return; + } + }; // Make sure chunk_idx write does not bypass audio chunk write. fence(Ordering::SeqCst); capt.chunk_idx = self.chunk_idx; -- Gitee From 9de4a965565867a207fe265b76cd7f8d8fba4658 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Fri, 30 Aug 2024 15:23:51 +0800 Subject: [PATCH 2067/2187] scream: support streams stop while VM is restarting This patch adds reset function for ivshmem device and introduces a callback for scream to register reset callback. Scream would stop streams while VM is restarting. Signed-off-by: Zhao Yi Min --- devices/src/misc/ivshmem.rs | 13 +++++++++++++ devices/src/misc/scream/mod.rs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/devices/src/misc/ivshmem.rs b/devices/src/misc/ivshmem.rs index 16ba7b225..a98a4a111 100644 --- a/devices/src/misc/ivshmem.rs +++ b/devices/src/misc/ivshmem.rs @@ -57,6 +57,7 @@ pub struct Ivshmem { ram_mem_region: Region, vector_nr: u32, bar0_ops: Arc>, + reset_cb: Option>, } impl Ivshmem { @@ -77,6 +78,7 @@ impl Ivshmem { ram_mem_region, vector_nr, bar0_ops: Arc::new(RwLock::new(Bar0Ops::default())), + reset_cb: None, } } @@ -168,6 +170,10 @@ impl Ivshmem { self.bar0_ops.write().unwrap().write = Some(bar0_ops.0); self.bar0_ops.write().unwrap().read = Some(bar0_ops.1); } + + pub fn register_reset_callback(&mut self, cb: Box) { + self.reset_cb = Some(cb); + } } impl Device for Ivshmem { @@ -205,6 +211,13 @@ impl Device for Ivshmem { locked_bus.attach_child(u64::from(dev.lock().unwrap().base.devfn), dev.clone())?; Ok(dev) } + + fn reset(&mut self, _reset_child_device: bool) -> Result<()> { + if let Some(cb) = &self.reset_cb { + cb(); + } + Ok(()) + } } impl PciDevOps for Ivshmem { diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index c0b4d0d14..38fd38b3a 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -435,6 +435,10 @@ impl StreamData { while capt.is_started != 0 { cond.wait_if_paused(interface.clone()); + if capt.fmt.fmt_generation != self.fmt.fmt_generation { + return; + } + if !self.update_buffer_by_chunk_idx(hva, shmem_size, capt) { return; } @@ -704,6 +708,15 @@ impl Scream { play_cond: Arc, capt_cond: Arc, ) { + let cloned_play_cond = play_cond.clone(); + let cloned_capt_cond = capt_cond.clone(); + let cb = Box::new(move || { + info!("Scream: device is reset."); + cloned_play_cond.set_stream_pause(true); + cloned_capt_cond.set_stream_pause(true); + }); + ivshmem.lock().unwrap().register_reset_callback(cb); + let interface = self.create_audio_extension(ivshmem.clone()); let interface2 = interface.clone(); let bar0_write = Arc::new(move |data: &[u8], offset: u64| { -- Gitee From d46f7e5db43f18819d3bb07045ff89ae216ab55f Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Mon, 26 Aug 2024 07:53:05 +0000 Subject: [PATCH 2068/2187] address_space: add attribute for GuestAddress access To make guset memory access more secure, controlling guest physical address through address attribute. Signed-off-by: Yandong Xu Author: Yandong Xu Date: Mon Aug 26 07:53:05 2024 +0000 --- address_space/src/address.rs | 9 ++ address_space/src/address_space.rs | 107 ++++++++++++++---- address_space/src/lib.rs | 6 +- address_space/src/state.rs | 6 +- boot_loader/src/aarch64/mod.rs | 16 ++- boot_loader/src/x86_64/direct_boot/gdt.rs | 6 +- boot_loader/src/x86_64/direct_boot/mod.rs | 41 +++++-- boot_loader/src/x86_64/direct_boot/mptable.rs | 6 +- boot_loader/src/x86_64/standard_boot/elf.rs | 9 +- devices/src/legacy/fwcfg.rs | 30 ++--- devices/src/pci/demo_device/dpy_device.rs | 3 +- devices/src/pci/demo_device/gpu_device.rs | 10 +- .../src/pci/demo_device/kbd_pointer_device.rs | 38 +++++-- devices/src/usb/xhci/xhci_controller.rs | 30 ++--- hypervisor/src/kvm/listener.rs | 12 +- machine/src/aarch64/micro.rs | 3 +- machine/src/aarch64/standard.rs | 4 +- machine/src/lib.rs | 12 +- machine/src/micro_common/mod.rs | 3 +- machine/src/standard_common/mod.rs | 5 +- virtio/src/device/balloon.rs | 8 +- virtio/src/device/block.rs | 7 +- virtio/src/device/net.rs | 5 +- virtio/src/device/rng.rs | 52 +++++++-- virtio/src/device/scsi_cntlr.rs | 6 +- virtio/src/device/serial.rs | 9 +- virtio/src/transport/virtio_pci.rs | 6 +- virtio/src/vhost/kernel/vsock.rs | 3 +- 28 files changed, 329 insertions(+), 123 deletions(-) diff --git a/address_space/src/address.rs b/address_space/src/address.rs index 94a79162c..5d1d02b67 100644 --- a/address_space/src/address.rs +++ b/address_space/src/address.rs @@ -15,6 +15,15 @@ use std::ops::{BitAnd, BitOr}; use util::num_ops::{round_down, round_up}; +#[derive(PartialEq, Eq)] +pub enum AddressAttr { + Ram, + MMIO, + RamDevice, + RomDevice, + RomDeviceForce, +} + /// Represent the address in given address space. #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct GuestAddress(pub u64); diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 4e8e35fcf..58e304abd 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -15,14 +15,14 @@ use std::fmt::Debug; use std::io::Write; use std::sync::{Arc, Mutex}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use arc_swap::ArcSwap; use log::error; use once_cell::sync::OnceCell; use crate::{ - AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, - RegionIoEventFd, RegionType, + AddressAttr, AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, + ListenerReqType, Region, RegionIoEventFd, RegionType, }; use migration::{migration::Migratable, MigrationManager}; use util::aio::Iovec; @@ -41,10 +41,23 @@ impl FlatView { } } - fn read(&self, dst: &mut dyn std::io::Write, addr: GuestAddress, count: u64) -> Result<()> { + fn read( + &self, + dst: &mut dyn std::io::Write, + addr: GuestAddress, + count: u64, + attr: AddressAttr, + ) -> Result<()> { let mut len = count; let mut l = count; let mut start = addr; + let region_type = match attr { + AddressAttr::Ram => RegionType::Ram, + AddressAttr::MMIO => RegionType::IO, + AddressAttr::RamDevice => RegionType::RamDevice, + AddressAttr::RomDevice => RegionType::RomDevice, + AddressAttr::RomDeviceForce => RegionType::RomDevice, + }; loop { if let Some(fr) = self.find_flatrange(start) { @@ -53,9 +66,20 @@ impl FlatView { let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let fr_remain = fr.addr_range.size - fr_offset; - if fr.owner.region_type() == RegionType::Ram - || fr.owner.region_type() == RegionType::RamDevice - { + if fr.owner.region_type() != region_type { + // Read op RomDevice in I/O access mode as MMIO + if region_type == RegionType::IO + && fr.owner.region_type() == RegionType::RomDevice + { + if fr.owner.get_rom_device_romd().unwrap() { + bail!("mismatch region type") + } + } else { + bail!("mismatch region type") + } + } + + if region_type == RegionType::Ram || region_type == RegionType::RamDevice { l = std::cmp::min(l, fr_remain); } fr.owner.read(dst, region_base, region_offset, l)?; @@ -74,20 +98,42 @@ impl FlatView { } } - fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { + fn write( + &self, + src: &mut dyn std::io::Read, + addr: GuestAddress, + count: u64, + attr: AddressAttr, + ) -> Result<()> { let mut l = count; let mut len = count; let mut start = addr; + let region_type = match attr { + AddressAttr::Ram => RegionType::Ram, + AddressAttr::MMIO => RegionType::IO, + AddressAttr::RamDevice => RegionType::RamDevice, + AddressAttr::RomDeviceForce => RegionType::RomDevice, + _ => { + bail!("Error write attr") + } + }; loop { if let Some(fr) = self.find_flatrange(start) { let fr_offset = start.offset_from(fr.addr_range.base); let region_offset = fr.offset_in_region + fr_offset; let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let fr_remain = fr.addr_range.size - fr_offset; - if fr.owner.region_type() == RegionType::Ram - || fr.owner.region_type() == RegionType::RamDevice + + // Read/Write ops to RomDevice is MMIO. + if fr.owner.region_type() != region_type + && !(region_type == RegionType::IO + && fr.owner.region_type() == RegionType::RomDevice) { + bail!("mismatch region type") + } + + if region_type == RegionType::Ram || region_type == RegionType::RamDevice { l = std::cmp::min(l, fr_remain); } fr.owner.write(src, region_base, region_offset, l)?; @@ -610,11 +656,17 @@ impl AddressSpace { /// # Errors /// /// Return Error if the `addr` is not mapped. - pub fn read(&self, dst: &mut dyn std::io::Write, addr: GuestAddress, count: u64) -> Result<()> { + pub fn read( + &self, + dst: &mut dyn std::io::Write, + addr: GuestAddress, + count: u64, + attr: AddressAttr, + ) -> Result<()> { trace::address_space_read(&addr, count); let view = self.flat_view.load(); - view.read(dst, addr, count)?; + view.read(dst, addr, count, attr)?; Ok(()) } @@ -629,7 +681,13 @@ impl AddressSpace { /// # Errors /// /// Return Error if the `addr` is not mapped. - pub fn write(&self, src: &mut dyn std::io::Read, addr: GuestAddress, count: u64) -> Result<()> { + pub fn write( + &self, + src: &mut dyn std::io::Read, + addr: GuestAddress, + count: u64, + attr: AddressAttr, + ) -> Result<()> { trace::address_space_write(&addr, count); let view = self.flat_view.load(); @@ -664,13 +722,13 @@ impl AddressSpace { continue; } } - view.write(&mut buf_temp.as_slice(), addr, count)?; + view.write(&mut buf_temp.as_slice(), addr, count, attr)?; return Ok(()); } } } - view.write(&mut buf.as_slice(), addr, count)?; + view.write(&mut buf.as_slice(), addr, count, attr)?; Ok(()) } @@ -683,9 +741,19 @@ impl AddressSpace { /// /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn write_object(&self, data: &T, addr: GuestAddress) -> Result<()> { - self.write(&mut data.as_bytes(), addr, std::mem::size_of::() as u64) - .with_context(|| "Failed to write object") + pub fn write_object( + &self, + data: &T, + addr: GuestAddress, + attr: AddressAttr, + ) -> Result<()> { + self.write( + &mut data.as_bytes(), + addr, + std::mem::size_of::() as u64, + attr, + ) + .with_context(|| "Failed to write object") } /// Write an object to memory via host address. @@ -718,12 +786,13 @@ impl AddressSpace { /// /// # Note /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub fn read_object(&self, addr: GuestAddress) -> Result { + pub fn read_object(&self, addr: GuestAddress, attr: AddressAttr) -> Result { let mut obj = T::default(); self.read( &mut obj.as_mut_bytes(), addr, std::mem::size_of::() as u64, + attr, ) .with_context(|| "Failed to read object")?; Ok(obj) diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs index f9feddf38..ae8760a4c 100644 --- a/address_space/src/lib.rs +++ b/address_space/src/lib.rs @@ -19,7 +19,7 @@ //! use std::sync::{Arc, Mutex}; //! extern crate address_space; //! use address_space::{ -//! AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps, +//! AddressAttr, AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region, RegionOps, //! }; //! //! struct DummyDevice; @@ -76,7 +76,7 @@ //! space.root().add_subregion(io_region, 0x2000); //! //! // 5. access address_space -//! space.write_object(&0x11u64, GuestAddress(0)); +//! space.write_object(&0x11u64, GuestAddress(0), AddressAttr::Ram); //! } //! ``` @@ -90,7 +90,7 @@ mod region; mod state; pub use crate::address_space::{AddressSpace, RegionCache}; -pub use address::{AddressRange, GuestAddress}; +pub use address::{AddressAttr, AddressRange, GuestAddress}; pub use error::AddressSpaceError; pub use host_mmap::{create_backend_mem, create_default_mem, FileBackend, HostMemMapping}; pub use listener::{Listener, ListenerReqType, MemSlot}; diff --git a/address_space/src/state.rs b/address_space/src/state.rs index 6a6d81ff7..2f2823258 100644 --- a/address_space/src/state.rs +++ b/address_space/src/state.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use anyhow::{bail, Context, Result}; -use crate::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; +use crate::{AddressAttr, AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; use migration::{ error::MigrationError, DeviceStateDesc, FieldDesc, MemBlock, MigrationHook, StateTransfer, }; @@ -168,14 +168,14 @@ impl MigrationHook for AddressSpace { } fn send_memory(&self, fd: &mut dyn Write, range: MemBlock) -> Result<()> { - self.read(fd, GuestAddress(range.gpa), range.len) + self.read(fd, GuestAddress(range.gpa), range.len, AddressAttr::Ram) .map_err(|e| MigrationError::SendVmMemoryErr(e.to_string()))?; Ok(()) } fn recv_memory(&self, fd: &mut dyn Read, range: MemBlock) -> Result<()> { - self.write(fd, GuestAddress(range.gpa), range.len) + self.write(fd, GuestAddress(range.gpa), range.len, AddressAttr::Ram) .map_err(|e| MigrationError::RecvVmMemoryErr(e.to_string()))?; Ok(()) diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index 20d564c7d..d06a95af6 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -19,7 +19,7 @@ use anyhow::{anyhow, Context, Result}; use log::info; use crate::error::BootLoaderError; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use devices::legacy::{error::LegacyError as FwcfgErrorKind, FwCfgEntryType, FwCfgOps}; use util::byte_code::ByteCode; @@ -85,7 +85,12 @@ fn load_kernel( ))); } sys_mem - .write(&mut kernel_image, GuestAddress(kernel_start), kernel_size) + .write( + &mut kernel_image, + GuestAddress(kernel_start), + kernel_size, + AddressAttr::Ram, + ) .with_context(|| "Fail to write kernel to guest memory")?; } Ok(kernel_end) @@ -129,7 +134,12 @@ fn load_initrd( .with_context(|| FwcfgErrorKind::AddEntryErr("InitrdData".to_string()))?; } else { sys_mem - .write(&mut initrd_image, GuestAddress(initrd_start), initrd_size) + .write( + &mut initrd_image, + GuestAddress(initrd_start), + initrd_size, + AddressAttr::Ram, + ) .with_context(|| "Fail to write initrd to guest memory")?; } diff --git a/boot_loader/src/x86_64/direct_boot/gdt.rs b/boot_loader/src/x86_64/direct_boot/gdt.rs index f5d95af32..07995a061 100644 --- a/boot_loader/src/x86_64/direct_boot/gdt.rs +++ b/boot_loader/src/x86_64/direct_boot/gdt.rs @@ -19,7 +19,7 @@ use super::super::BootGdtSegment; use super::super::{ BOOT_GDT_MAX, BOOT_GDT_OFFSET, BOOT_IDT_OFFSET, GDT_ENTRY_BOOT_CS, GDT_ENTRY_BOOT_DS, }; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; // /* // * Constructor for a conventional segment GDT (or LDT) entry. @@ -94,7 +94,7 @@ fn write_gdt_table(table: &[u64], guest_mem: &Arc) -> Result<()> { let mut boot_gdt_addr = BOOT_GDT_OFFSET; for (_, entry) in table.iter().enumerate() { guest_mem - .write_object(entry, GuestAddress(boot_gdt_addr)) + .write_object(entry, GuestAddress(boot_gdt_addr), AddressAttr::Ram) .with_context(|| format!("Failed to load gdt to 0x{:x}", boot_gdt_addr))?; boot_gdt_addr += 8; } @@ -104,7 +104,7 @@ fn write_gdt_table(table: &[u64], guest_mem: &Arc) -> Result<()> { fn write_idt_value(val: u64, guest_mem: &Arc) -> Result<()> { let boot_idt_addr = BOOT_IDT_OFFSET; guest_mem - .write_object(&val, GuestAddress(boot_idt_addr)) + .write_object(&val, GuestAddress(boot_idt_addr), AddressAttr::Ram) .with_context(|| format!("Failed to load gdt to 0x{:x}", boot_idt_addr))?; Ok(()) diff --git a/boot_loader/src/x86_64/direct_boot/mod.rs b/boot_loader/src/x86_64/direct_boot/mod.rs index 8aed3118f..c910d8225 100644 --- a/boot_loader/src/x86_64/direct_boot/mod.rs +++ b/boot_loader/src/x86_64/direct_boot/mod.rs @@ -29,7 +29,7 @@ use super::{ INITRD_ADDR_MAX, PDE_START, PDPTE_START, PML4_START, VMLINUX_STARTUP, ZERO_PAGE_START, }; use crate::error::BootLoaderError; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use util::byte_code::ByteCode; /// Load bzImage linux kernel to Guest Memory. @@ -91,7 +91,12 @@ fn load_image(image: &mut File, start_addr: u64, sys_mem: &Arc) -> let len = image.seek(SeekFrom::End(0))?; image.seek(SeekFrom::Start(curr_loc))?; - sys_mem.write(image, GuestAddress(start_addr), len - curr_loc)?; + sys_mem.write( + image, + GuestAddress(start_addr), + len - curr_loc, + AddressAttr::Ram, + )?; Ok(()) } @@ -163,13 +168,13 @@ fn setup_page_table(sys_mem: &Arc) -> Result { // Entry covering VA [0..512GB) let pdpte = boot_pdpte_addr | 0x03; sys_mem - .write_object(&pdpte, GuestAddress(boot_pml4_addr)) + .write_object(&pdpte, GuestAddress(boot_pml4_addr), AddressAttr::Ram) .with_context(|| format!("Failed to load PD PTE to 0x{:x}", boot_pml4_addr))?; // Entry covering VA [0..1GB) let pde = boot_pde_addr | 0x03; sys_mem - .write_object(&pde, GuestAddress(boot_pdpte_addr)) + .write_object(&pde, GuestAddress(boot_pdpte_addr), AddressAttr::Ram) .with_context(|| format!("Failed to load PDE to 0x{:x}", boot_pdpte_addr))?; // 512 2MB entries together covering VA [0..1GB). Note we are assuming @@ -177,7 +182,7 @@ fn setup_page_table(sys_mem: &Arc) -> Result { for i in 0..512u64 { let pde = (i << 21) + 0x83u64; sys_mem - .write_object(&pde, GuestAddress(boot_pde_addr + i * 8)) + .write_object(&pde, GuestAddress(boot_pde_addr + i * 8), AddressAttr::Ram) .with_context(|| format!("Failed to load PDE to 0x{:x}", boot_pde_addr + i * 8))?; } @@ -192,7 +197,11 @@ fn setup_boot_params( let mut boot_params = BootParams::new(*boot_hdr); boot_params.setup_e820_entries(config, sys_mem); sys_mem - .write_object(&boot_params, GuestAddress(ZERO_PAGE_START)) + .write_object( + &boot_params, + GuestAddress(ZERO_PAGE_START), + AddressAttr::Ram, + ) .with_context(|| format!("Failed to load zero page to 0x{:x}", ZERO_PAGE_START))?; Ok(()) @@ -210,6 +219,7 @@ fn setup_kernel_cmdline( &mut config.kernel_cmdline.as_bytes(), GuestAddress(CMDLINE_START), u64::from(cmdline_len), + AddressAttr::Ram, )?; Ok(()) @@ -305,18 +315,24 @@ mod test { .unwrap(); assert_eq!(setup_page_table(&space).unwrap(), 0x0000_9000); assert_eq!( - space.read_object::(GuestAddress(0x0000_9000)).unwrap(), + space + .read_object::(GuestAddress(0x0000_9000), AddressAttr::Ram) + .unwrap(), 0x0000_a003 ); assert_eq!( - space.read_object::(GuestAddress(0x0000_a000)).unwrap(), + space + .read_object::(GuestAddress(0x0000_a000), AddressAttr::Ram) + .unwrap(), 0x0000_b003 ); let mut page_addr: u64 = 0x0000_b000; let mut tmp_value: u64 = 0x83; for _ in 0..512u64 { assert_eq!( - space.read_object::(GuestAddress(page_addr)).unwrap(), + space + .read_object::(GuestAddress(page_addr), AddressAttr::Ram) + .unwrap(), tmp_value ); page_addr += 8; @@ -378,7 +394,11 @@ mod test { let mut arr: Vec = Vec::new(); let mut boot_addr: u64 = 0x500; for _ in 0..BOOT_GDT_MAX { - arr.push(space.read_object(GuestAddress(boot_addr)).unwrap()); + arr.push( + space + .read_object(GuestAddress(boot_addr), AddressAttr::Ram) + .unwrap(), + ); boot_addr += 8; } assert_eq!(arr[0], 0); @@ -395,6 +415,7 @@ mod test { &mut read_buffer.as_mut(), GuestAddress(0x0002_0000), cmd_len, + AddressAttr::Ram, ) .unwrap(); let s = String::from_utf8(read_buffer.to_vec()).unwrap(); diff --git a/boot_loader/src/x86_64/direct_boot/mptable.rs b/boot_loader/src/x86_64/direct_boot/mptable.rs index 8eee6d1c1..8ea1ce2d2 100644 --- a/boot_loader/src/x86_64/direct_boot/mptable.rs +++ b/boot_loader/src/x86_64/direct_boot/mptable.rs @@ -15,7 +15,7 @@ use std::sync::Arc; use anyhow::{anyhow, Result}; use crate::error::BootLoaderError; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use util::byte_code::ByteCode; use util::checksum::obj_checksum; @@ -267,7 +267,7 @@ impl LocalInterruptEntry { macro_rules! write_entry { ( $d:expr, $t:ty, $m:expr, $o:expr, $s:expr ) => { let entry = $d; - $m.write_object(&entry, GuestAddress($o))?; + $m.write_object(&entry, GuestAddress($o), AddressAttr::Ram)?; $o += std::mem::size_of::<$t>() as u64; $s = $s.wrapping_add(obj_checksum(&entry)); }; @@ -294,6 +294,7 @@ pub fn setup_isa_mptable( sys_mem.write_object( &FloatingPointer::new(header as u32), GuestAddress(start_addr), + AddressAttr::Ram, )?; let mut offset = header + std::mem::size_of::() as u64; @@ -345,6 +346,7 @@ pub fn setup_isa_mptable( sys_mem.write_object( &ConfigTableHeader::new((offset - header) as u16, sum, lapic_addr), GuestAddress(header), + AddressAttr::Ram, )?; Ok(()) diff --git a/boot_loader/src/x86_64/standard_boot/elf.rs b/boot_loader/src/x86_64/standard_boot/elf.rs index 3f852578a..9158d4c1c 100644 --- a/boot_loader/src/x86_64/standard_boot/elf.rs +++ b/boot_loader/src/x86_64/standard_boot/elf.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use anyhow::{bail, Context, Result}; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use devices::legacy::{FwCfgEntryType, FwCfgOps}; use util::byte_code::ByteCode; use util::num_ops::round_up; @@ -163,7 +163,12 @@ pub fn load_elf_kernel( if ph.p_type == PT_LOAD { kernel_image.seek(SeekFrom::Start(ph.p_offset))?; - sys_mem.write(kernel_image, GuestAddress(ph.p_paddr), ph.p_filesz)?; + sys_mem.write( + kernel_image, + GuestAddress(ph.p_paddr), + ph.p_filesz, + AddressAttr::Ram, + )?; addr_low = std::cmp::min(addr_low, ph.p_paddr); addr_max = std::cmp::max(addr_max, ph.p_paddr); diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index d7a3d7019..ce36d6552 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -28,7 +28,7 @@ use acpi::{ use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlMemory32Fixed, AmlReadAndWrite}; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; use util::{gen_base_func, offset_of}; @@ -243,12 +243,14 @@ fn write_dma_memory( mut buf: &[u8], len: u64, ) -> Result<()> { - addr_space.write(&mut buf, addr, len).with_context(|| { - format!( - "Failed to write dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", - addr.0, len - ) - })?; + addr_space + .write(&mut buf, addr, len, AddressAttr::Ram) + .with_context(|| { + format!( + "Failed to write dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", + addr.0, len + ) + })?; Ok(()) } @@ -260,12 +262,14 @@ fn read_dma_memory( mut buf: &mut [u8], len: u64, ) -> Result<()> { - addr_space.read(&mut buf, addr, len).with_context(|| { - format!( - "Failed to read dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", - addr.0, len - ) - })?; + addr_space + .read(&mut buf, addr, len, AddressAttr::Ram) + .with_context(|| { + format!( + "Failed to read dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", + addr.0, len + ) + })?; Ok(()) } diff --git a/devices/src/pci/demo_device/dpy_device.rs b/devices/src/pci/demo_device/dpy_device.rs index e0157461a..8248b9dd3 100644 --- a/devices/src/pci/demo_device/dpy_device.rs +++ b/devices/src/pci/demo_device/dpy_device.rs @@ -28,7 +28,7 @@ use log::error; use once_cell::sync::Lazy; use super::DeviceTypeOperation; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use ui::{ console::{ register_display, DisplayChangeListener, DisplayChangeListenerOperations, DisplayMouse, @@ -227,6 +227,7 @@ impl DeviceTypeOperation for DemoDisplay { &mut buf.as_slice(), address_space::GuestAddress(mem_addr), buf.len() as u64, + AddressAttr::Ram, ); } diff --git a/devices/src/pci/demo_device/gpu_device.rs b/devices/src/pci/demo_device/gpu_device.rs index 7abef7125..8b7c1775d 100644 --- a/devices/src/pci/demo_device/gpu_device.rs +++ b/devices/src/pci/demo_device/gpu_device.rs @@ -29,7 +29,7 @@ use byteorder::{ByteOrder, LittleEndian}; use log::info; use super::DeviceTypeOperation; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use ui::{ console::{ console_close, console_init, display_cursor_define, display_graphic_update, @@ -196,8 +196,12 @@ impl DeviceTypeOperation for DemoGpu { let mem_addr = LittleEndian::read_u64(data); // Event Type. let mut buf: Vec = vec![]; - self.sys_mem - .read(&mut buf, address_space::GuestAddress(mem_addr), 21)?; + self.sys_mem.read( + &mut buf, + address_space::GuestAddress(mem_addr), + 21, + AddressAttr::Ram, + )?; let event_type = GpuEvent::from(buf[0]); let x = LittleEndian::read_u32(&buf[1..5]); let y = LittleEndian::read_u32(&buf[5..9]); diff --git a/devices/src/pci/demo_device/kbd_pointer_device.rs b/devices/src/pci/demo_device/kbd_pointer_device.rs index 78033f8ed..c4c036bad 100644 --- a/devices/src/pci/demo_device/kbd_pointer_device.rs +++ b/devices/src/pci/demo_device/kbd_pointer_device.rs @@ -22,7 +22,7 @@ use byteorder::{ByteOrder, LittleEndian}; use once_cell::sync::Lazy; use super::DeviceTypeOperation; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use ui::input::{register_keyboard, register_pointer, Axis, InputType, KeyboardOpts, PointerOpts}; static MEM_ADDR: Lazy>> = Lazy::new(|| { @@ -51,12 +51,36 @@ impl MemSpace { bail!("No memory allocated!") } }; - sys_mem.write_object(&(msg.event_type as u8), address_space::GuestAddress(addr))?; - sys_mem.write_object(&msg.keycode, address_space::GuestAddress(addr + 1))?; - sys_mem.write_object(&msg.down, address_space::GuestAddress(addr + 3))?; - sys_mem.write_object(&msg.button, address_space::GuestAddress(addr + 4))?; - sys_mem.write_object(&msg.x, address_space::GuestAddress(addr + 8))?; - sys_mem.write_object(&msg.y, address_space::GuestAddress(addr + 12))?; + sys_mem.write_object( + &(msg.event_type as u8), + address_space::GuestAddress(addr), + AddressAttr::Ram, + )?; + sys_mem.write_object( + &msg.keycode, + address_space::GuestAddress(addr + 1), + AddressAttr::Ram, + )?; + sys_mem.write_object( + &msg.down, + address_space::GuestAddress(addr + 3), + AddressAttr::Ram, + )?; + sys_mem.write_object( + &msg.button, + address_space::GuestAddress(addr + 4), + AddressAttr::Ram, + )?; + sys_mem.write_object( + &msg.x, + address_space::GuestAddress(addr + 8), + AddressAttr::Ram, + )?; + sys_mem.write_object( + &msg.y, + address_space::GuestAddress(addr + 12), + AddressAttr::Ram, + )?; Ok(()) } diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 4dc652a99..fc2a44939 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -31,7 +31,7 @@ use super::xhci_trb::{ }; use crate::usb::{config::*, TransferOps}; use crate::usb::{UsbDevice, UsbDeviceRequest, UsbError, UsbPacket, UsbPacketStatus}; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; const INVALID_SLOT_ID: u32 = 0; @@ -2452,12 +2452,14 @@ pub fn dma_read_bytes( mut buf: &mut [u8], ) -> Result<()> { let len = buf.len() as u64; - addr_space.read(&mut buf, addr, len).with_context(|| { - format!( - "Failed to read dma memory at gpa=0x{:x} len=0x{:x}", - addr.0, len - ) - })?; + addr_space + .read(&mut buf, addr, len, AddressAttr::Ram) + .with_context(|| { + format!( + "Failed to read dma memory at gpa=0x{:x} len=0x{:x}", + addr.0, len + ) + })?; Ok(()) } @@ -2467,12 +2469,14 @@ pub fn dma_write_bytes( mut buf: &[u8], ) -> Result<()> { let len = buf.len() as u64; - addr_space.write(&mut buf, addr, len).with_context(|| { - format!( - "Failed to write dma memory at gpa=0x{:x} len=0x{:x}", - addr.0, len - ) - })?; + addr_space + .write(&mut buf, addr, len, AddressAttr::Ram) + .with_context(|| { + format!( + "Failed to write dma memory at gpa=0x{:x} len=0x{:x}", + addr.0, len + ) + })?; Ok(()) } diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index 2e1c603fe..327df1d85 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -179,12 +179,12 @@ impl KvmMemoryListener { return Ok(()); } - if flat_range.owner.region_type() != RegionType::Ram - && flat_range.owner.region_type() != RegionType::RomDevice - && flat_range.owner.region_type() != RegionType::RamDevice - { - return Ok(()); - } + let attr = match flat_range.owner.region_type() { + address_space::RegionType::Ram => AddressAttr::Ram, + address_space::RegionType::RamDevice => AddressAttr::RamDevice, + address_space::RegionType::RomDevice => AddressAttr::RomDevice, + _ => return Ok(()), + }; let (aligned_addr, aligned_size) = Self::align_mem_slot(flat_range.addr_range, host_page_size()) diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 6fd87d765..8cd32b5db 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -16,7 +16,7 @@ use anyhow::{bail, Context, Result}; use crate::{micro_common::syscall::syscall_whitelist, MachineBase, MachineError}; use crate::{register_shutdown_event, LightMachine, MachineOps}; -use address_space::{AddressSpace, GuestAddress, Region}; +use address_space::{AddressAttr, AddressSpace, GuestAddress, Region}; use cpu::CPUTopology; use devices::legacy::{PL011, PL031}; use devices::{Device, ICGICConfig, ICGICv2Config, ICGICv3Config, GIC_IRQ_MAX}; @@ -191,6 +191,7 @@ impl MachineOps for LightMachine { &mut fdt_vec.as_slice(), GuestAddress(boot_config.fdt_addr), fdt_vec.len() as u64, + AddressAttr::Ram, ) .with_context(|| MachineError::WrtFdtErr(boot_config.fdt_addr, fdt_vec.len()))?; register_shutdown_event(locked_vm.shutdown_req.clone(), vm.clone()) diff --git a/machine/src/aarch64/standard.rs b/machine/src/aarch64/standard.rs index 9feab83ab..fd1aa3495 100644 --- a/machine/src/aarch64/standard.rs +++ b/machine/src/aarch64/standard.rs @@ -39,7 +39,7 @@ use acpi::{ }; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use address_space::FileBackend; -use address_space::{AddressSpace, GuestAddress, Region}; +use address_space::{AddressAttr, AddressSpace, GuestAddress, Region}; use cpu::{CPUInterface, CPUTopology, CpuLifecycleState, PMU_INTR, PPI_BASE}; use devices::acpi::ged::{acpi_dsdt_add_power_button, Ged, GedEvent}; use devices::acpi::power::PowerDev; @@ -201,6 +201,7 @@ impl StdMachine { &mut locked_vm.dtb_vec.as_slice(), GuestAddress(fdt_addr), locked_vm.dtb_vec.len() as u64, + AddressAttr::Ram, ) .with_context(|| "Fail to write dtb into sysmem")?; @@ -594,6 +595,7 @@ impl MachineOps for StdMachine { &mut fdt_vec.as_slice(), GuestAddress(boot_config.fdt_addr), fdt_vec.len() as u64, + AddressAttr::Ram, ) .with_context(|| MachineError::WrtFdtErr(boot_config.fdt_addr, fdt_vec.len()))?; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index e71300dde..f69f40ddc 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -45,7 +45,9 @@ use vmm_sys_util::eventfd::EventFd; #[cfg(all(target_env = "ohos", feature = "ohui_srv"))] use address_space::FileBackend; -use address_space::{create_backend_mem, create_default_mem, AddressSpace, GuestAddress, Region}; +use address_space::{ + create_backend_mem, create_default_mem, AddressAttr, AddressSpace, GuestAddress, Region, +}; #[cfg(target_arch = "aarch64")] use cpu::CPUFeatures; use cpu::{ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUTopology, CpuTopology, CPU}; @@ -262,7 +264,7 @@ impl MachineBase { let length = data.len() as u64; self.sys_io - .read(&mut data, GuestAddress(addr), length) + .read(&mut data, GuestAddress(addr), length, AddressAttr::MMIO) .is_ok() } @@ -277,21 +279,21 @@ impl MachineBase { } } self.sys_io - .write(&mut data, GuestAddress(addr), count) + .write(&mut data, GuestAddress(addr), count, AddressAttr::MMIO) .is_ok() } fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool { let length = data.len() as u64; self.sys_mem - .read(&mut data, GuestAddress(addr), length) + .read(&mut data, GuestAddress(addr), length, AddressAttr::MMIO) .is_ok() } fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool { let count = data.len() as u64; self.sys_mem - .write(&mut data, GuestAddress(addr), count) + .write(&mut data, GuestAddress(addr), count, AddressAttr::MMIO) .is_ok() } } diff --git a/machine/src/micro_common/mod.rs b/machine/src/micro_common/mod.rs index 3ed04c094..200028d56 100644 --- a/machine/src/micro_common/mod.rs +++ b/machine/src/micro_common/mod.rs @@ -597,12 +597,13 @@ impl MachineAddressInterface for LightMachine { #[cfg(target_arch = "x86_64")] fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool { + use address_space::AddressAttr; use address_space::GuestAddress; let count = data.len() as u64; self.base .sys_io - .write(&mut data, GuestAddress(addr), count) + .write(&mut data, GuestAddress(addr), count, AddressAttr::MMIO) .is_ok() } diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index 4f95bb377..b493c6f49 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -46,7 +46,8 @@ use acpi::{ ACPI_TABLE_LOADER_FILE, TABLE_CHECKSUM_OFFSET, }; use address_space::{ - AddressRange, FileBackend, GuestAddress, HostMemMapping, Region, RegionIoEventFd, RegionOps, + AddressAttr, AddressRange, FileBackend, GuestAddress, HostMemMapping, Region, RegionIoEventFd, + RegionOps, }; use block_backend::{qcow2::QCOW2_LIST, BlockStatus}; #[cfg(target_arch = "x86_64")] @@ -2040,7 +2041,7 @@ impl DeviceInterface for StdMachine { match self .machine_base() .sys_mem - .read_object::(GuestAddress(gpa)) + .read_object::(GuestAddress(gpa), AddressAttr::Ram) { Ok(val) => { Response::create_response(serde_json::to_value(format!("{:X}", val)).unwrap(), None) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 22462a792..421cbb8a0 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -31,7 +31,8 @@ use crate::{ VIRTIO_TYPE_BALLOON, }; use address_space::{ - AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, + AddressAttr, AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, + RegionType, }; use machine_manager::{ config::{get_pci_df, parse_bool, DEFAULT_VIRTQUEUE_SIZE}, @@ -161,7 +162,10 @@ fn iov_to_buf( } // GPAChecked: the iov has been checked in pop_avail(). - match address_space.read_object::(GuestAddress(iov.iov_base.raw_value() + offset)) { + match address_space.read_object::( + GuestAddress(iov.iov_base.raw_value() + offset), + AddressAttr::Ram, + ) { Ok(dat) => Some(dat), Err(ref e) => { error!("Read virtioqueue failed: {:?}", e); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 055411645..efb6492e6 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -36,7 +36,7 @@ use crate::{ VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_BLOCK, }; -use address_space::{AddressSpace, GuestAddress, RegionCache}; +use address_space::{AddressAttr, AddressSpace, GuestAddress, RegionCache}; use block_backend::{ create_block_backend, remove_block_backend, BlockDriverOps, BlockIoErrorCallback, BlockProperty, BlockStatus, @@ -228,7 +228,10 @@ impl AioCompleteCb { } fn complete_one_request(&self, req: &Request, status: u8) -> Result<()> { - if let Err(ref e) = self.mem_space.write_object(&status, req.in_header) { + if let Err(ref e) = self + .mem_space + .write_object(&status, req.in_header, AddressAttr::Ram) + { bail!("Failed to write the status (blk io completion) {:?}", e); } diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 16e17da15..09f43aebc 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -43,7 +43,7 @@ use crate::{ VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, VIRTIO_NET_OK, VIRTIO_TYPE_NET, }; -use address_space::AddressSpace; +use address_space::{AddressAttr, AddressSpace}; use machine_manager::config::{ConfigCheck, NetDevcfg, NetworkInterfaceConfig}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use machine_manager::state_query::{ @@ -616,7 +616,8 @@ impl NetCtrlHandler { .in_iovec .first() .with_context(|| "Failed to get device writable iovec")?; - self.mem_space.write_object::(&ack, status.addr)?; + self.mem_space + .write_object::(&ack, status.addr, AddressAttr::Ram)?; locked_queue .vring diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index aaad7cb9d..39a5d57c0 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -29,7 +29,7 @@ use crate::{ ElemIovec, Queue, VirtioBase, VirtioDevice, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_VERSION_1, VIRTIO_TYPE_RNG, }; -use address_space::AddressSpace; +use address_space::{AddressAttr, AddressSpace}; use machine_manager::{ config::{get_pci_df, valid_id, ConfigError, RngObjConfig, DEFAULT_VIRTQUEUE_SIZE}, event_loop::EventLoop, @@ -140,6 +140,7 @@ impl RngHandler { &mut buffer[offset..].as_ref(), iov.addr, u64::from(min(size - offset as u32, iov.len)), + AddressAttr::Ram, ) .with_context(|| "Failed to write request data for virtio rng")?; @@ -416,6 +417,7 @@ mod tests { use super::*; use crate::tests::address_space_init; use crate::*; + use address_space::AddressAttr; use address_space::GuestAddress; use machine_manager::config::{str_slip_to_clap, VmConfig, DEFAULT_VIRTQUEUE_SIZE}; @@ -620,15 +622,23 @@ mod tests { }; // write table descriptor for queue mem_space - .write_object(&desc, queue_config.desc_table) + .write_object(&desc, queue_config.desc_table, AddressAttr::Ram) .unwrap(); // write avail_ring idx mem_space - .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4_u64)) + .write_object::( + &0, + GuestAddress(queue_config.avail_ring.0 + 4_u64), + AddressAttr::Ram, + ) .unwrap(); // write avail_ring idx mem_space - .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2_u64)) + .write_object::( + &1, + GuestAddress(queue_config.avail_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); let buffer = vec![1_u8; data_len as usize]; @@ -639,13 +649,17 @@ mod tests { .read( &mut read_buffer.as_mut_slice(), GuestAddress(0x40000), - u64::from(data_len) + u64::from(data_len), + AddressAttr::Ram ) .is_ok()); assert_eq!(read_buffer, buffer); let idx = mem_space - .read_object::(GuestAddress(queue_config.used_ring.0 + 2_u64)) + .read_object::( + GuestAddress(queue_config.used_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); assert_eq!(idx, 1); assert_eq!(cloned_interrupt_evt.read().unwrap(), 1); @@ -703,7 +717,7 @@ mod tests { }; // write table descriptor for queue mem_space - .write_object(&desc, queue_config.desc_table) + .write_object(&desc, queue_config.desc_table, AddressAttr::Ram) .unwrap(); let desc = SplitVringDesc { @@ -717,16 +731,25 @@ mod tests { .write_object( &desc, GuestAddress(queue_config.desc_table.0 + size_of::() as u64), + AddressAttr::Ram, ) .unwrap(); // write avail_ring idx mem_space - .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4_u64)) + .write_object::( + &0, + GuestAddress(queue_config.avail_ring.0 + 4_u64), + AddressAttr::Ram, + ) .unwrap(); // write avail_ring idx mem_space - .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2_u64)) + .write_object::( + &1, + GuestAddress(queue_config.avail_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); let mut buffer1 = vec![1_u8; data_len as usize]; @@ -742,7 +765,8 @@ mod tests { .read( &mut read_buffer.as_mut_slice(), GuestAddress(0x40000), - u64::from(data_len) + u64::from(data_len), + AddressAttr::Ram ) .is_ok()); assert_eq!(read_buffer, buffer1_check); @@ -750,13 +774,17 @@ mod tests { .read( &mut read_buffer.as_mut_slice(), GuestAddress(0x50000), - u64::from(data_len) + u64::from(data_len), + AddressAttr::Ram ) .is_ok()); assert_eq!(read_buffer, buffer2_check); let idx = mem_space - .read_object::(GuestAddress(queue_config.used_ring.0 + 2_u64)) + .read_object::( + GuestAddress(queue_config.used_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); assert_eq!(idx, 1); assert_eq!(cloned_interrupt_evt.read().unwrap(), 1); diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index 9f6fc01df..6f4143fba 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -27,7 +27,7 @@ use crate::{ VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, VIRTIO_TYPE_SCSI, }; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use block_backend::BlockIoErrorCallback; use devices::ScsiBus::{ ScsiBus, ScsiRequest, ScsiRequestOps, ScsiSense, ScsiXferMode, CHECK_CONDITION, @@ -535,7 +535,7 @@ impl VirtioScsiReq fn complete(&self) -> Result<()> { self.mem_space - .write_object(&self.resp, self.resp_addr) + .write_object(&self.resp, self.resp_addr, AddressAttr::Ram) .with_context(|| "Failed to write the scsi response")?; let mut queue_lock = self.queue.lock().unwrap(); @@ -621,7 +621,7 @@ impl ScsiCtrlQueueHandler { .with_context(|| "Error request in ctrl queue. Empty dataout buf!")?; let ctrl_type = self .mem_space - .read_object::(ctrl_desc.addr) + .read_object::(ctrl_desc.addr, AddressAttr::Ram) .with_context(|| "Failed to get control queue descriptor")?; match ctrl_type { diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 37256dc72..ac093df84 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -29,7 +29,7 @@ use crate::{ Element, Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_SIZE, VIRTIO_F_VERSION_1, VIRTIO_TYPE_CONSOLE, }; -use address_space::AddressSpace; +use address_space::{AddressAttr, AddressSpace}; use chardev_backend::chardev::{Chardev, ChardevNotifyDevice, ChardevStatus, InputReceiver}; use machine_manager::{ config::{ChardevType, VirtioSerialInfo, VirtioSerialPortCfg, DEFAULT_VIRTQUEUE_SIZE}, @@ -591,7 +591,12 @@ impl SerialPortHandler { // GPAChecked: the elem_iov has been checked in pop_avail(). self.mem_space - .write(&mut source_slice, elem_iov.addr, len as u64) + .write( + &mut source_slice, + elem_iov.addr, + len as u64, + AddressAttr::Ram, + ) .with_context(|| { format!( "Failed to write slice for virtio serial port input: addr {:X} len {}", diff --git a/virtio/src/transport/virtio_pci.rs b/virtio/src/transport/virtio_pci.rs index 00db23ee6..0c4bfc88a 100644 --- a/virtio/src/transport/virtio_pci.rs +++ b/virtio/src/transport/virtio_pci.rs @@ -35,7 +35,9 @@ use crate::{ }; #[cfg(feature = "virtio_gpu")] use address_space::HostMemMapping; -use address_space::{AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps}; +use address_space::{ + AddressAttr, AddressRange, AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps, +}; use devices::pci::config::{ RegionType, BAR_SPACE_UNMAPPED, DEVICE_ID, MINIMUM_BAR_SIZE_FOR_MMIO, PCIE_CONFIG_SPACE_SIZE, PCI_SUBDEVICE_ID_QEMU, PCI_VENDOR_ID_REDHAT_QUMRANET, REG_SIZE, REVISION_ID, STATUS, @@ -939,6 +941,7 @@ impl VirtioPciDevice { &mut data, GuestAddress(bar_base + u64::from(off)), u64::from(len), + AddressAttr::MMIO, ) } else { let mut data = self.base.config.config[pci_cfg_data_offset..].as_mut(); @@ -946,6 +949,7 @@ impl VirtioPciDevice { &mut data, GuestAddress(bar_base + u64::from(off)), u64::from(len), + AddressAttr::MMIO, ) }; if let Err(e) = result { diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 8774fbebc..53a387459 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -26,7 +26,7 @@ use crate::{ Queue, VirtioBase, VirtioDevice, VirtioError, VirtioInterrupt, VirtioInterruptType, VIRTIO_F_ACCESS_PLATFORM, VIRTIO_TYPE_VSOCK, }; -use address_space::AddressSpace; +use address_space::{AddressAttr, AddressSpace}; use machine_manager::config::{get_pci_df, parse_bool, valid_id, DEFAULT_VIRTQUEUE_SIZE}; use machine_manager::event_loop::{register_event_helper, unregister_event_helper}; use migration::{DeviceStateDesc, FieldDesc, MigrationHook, MigrationManager, StateTransfer}; @@ -168,6 +168,7 @@ impl Vsock { .write_object( &VIRTIO_VSOCK_EVENT_TRANSPORT_RESET, element.in_iovec[0].addr, + AddressAttr::Ram, ) .with_context(|| "Failed to write buf for virtio vsock event")?; event_queue_locked -- Gitee From d0e4532413ceac9b8ed066167879b61fa0aa14ac Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Mon, 26 Aug 2024 08:24:50 +0000 Subject: [PATCH 2069/2187] tests: add address attr Signed-off-by: Yandong Xu Author: Yandong Xu Date: Mon Aug 26 08:24:50 2024 +0000 --- address_space/src/address_space.rs | 14 +++++--- devices/src/legacy/fwcfg.rs | 44 ++++++++++++++++++++++---- virtio/src/device/balloon.rs | 42 ++++++++++++++++++------ virtio/src/device/block.rs | 33 +++++++++++++++---- virtio/src/queue/split.rs | 51 +++++++++++++++++++++--------- 5 files changed, 143 insertions(+), 41 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 58e304abd..e138fee33 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -854,7 +854,7 @@ mod test { use vmm_sys_util::eventfd::EventFd; use super::*; - use crate::{HostMemMapping, RegionOps}; + use crate::{AddressAttr, HostMemMapping, RegionOps}; #[derive(Default, Clone)] struct TestListener { @@ -1324,9 +1324,15 @@ mod test { .unwrap(); let data: u64 = 10000; - assert!(space.write_object(&data, GuestAddress(992)).is_ok()); - let data1: u64 = space.read_object(GuestAddress(992)).unwrap(); + assert!(space + .write_object(&data, GuestAddress(992), AddressAttr::Ram) + .is_ok()); + let data1: u64 = space + .read_object(GuestAddress(992), AddressAttr::Ram) + .unwrap(); assert_eq!(data1, 10000); - assert!(space.write_object(&data, GuestAddress(993)).is_err()); + assert!(space + .write_object(&data, GuestAddress(993), AddressAttr::Ram) + .is_err()); } } diff --git a/devices/src/legacy/fwcfg.rs b/devices/src/legacy/fwcfg.rs index ce36d6552..b6a57a278 100644 --- a/devices/src/legacy/fwcfg.rs +++ b/devices/src/legacy/fwcfg.rs @@ -1436,7 +1436,12 @@ mod test { let addr = GuestAddress(0x0000); fwcfg_common .mem_space - .write(&mut dma_request.as_ref(), addr, dma_request.len() as u64) + .write( + &mut dma_request.as_ref(), + addr, + dma_request.len() as u64, + AddressAttr::Ram, + ) .unwrap(); // [2]set dma addr. @@ -1446,7 +1451,13 @@ mod test { assert_eq!(fwcfg_common.handle_dma_request().is_ok(), true); // [4]check dma response. - assert_eq!(fwcfg_common.mem_space.read_object::(addr).unwrap(), 0); + assert_eq!( + fwcfg_common + .mem_space + .read_object::(addr, AddressAttr::Ram) + .unwrap(), + 0 + ); // [5]check dma write result. let mut read_dma_buf = Vec::new(); @@ -1454,7 +1465,12 @@ mod test { let len = sig_entry_data.len(); fwcfg_common .mem_space - .read(&mut read_dma_buf, GuestAddress(0xffff), len as u64) + .read( + &mut read_dma_buf, + GuestAddress(0xffff), + len as u64, + AddressAttr::Ram, + ) .unwrap(); assert_eq!(read_dma_buf, sig_entry_data); @@ -1467,7 +1483,12 @@ mod test { let addr = GuestAddress(0x0000); fwcfg_common .mem_space - .write(&mut dma_request.as_ref(), addr, dma_request.len() as u64) + .write( + &mut dma_request.as_ref(), + addr, + dma_request.len() as u64, + AddressAttr::Ram, + ) .unwrap(); fwcfg_common.dma_addr = addr; @@ -1475,14 +1496,25 @@ mod test { assert_eq!(fwcfg_common.handle_dma_request().is_ok(), true); // Result should be all zero. - assert_eq!(fwcfg_common.mem_space.read_object::(addr).unwrap(), 0); + assert_eq!( + fwcfg_common + .mem_space + .read_object::(addr, AddressAttr::Ram) + .unwrap(), + 0 + ); let mut read_dma_buf = Vec::new(); let all_zero = vec![0x0_u8; 4]; let len = all_zero.len(); fwcfg_common .mem_space - .read(&mut read_dma_buf, GuestAddress(0xffff), len as u64) + .read( + &mut read_dma_buf, + GuestAddress(0xffff), + len as u64, + AddressAttr::Ram, + ) .unwrap(); assert_eq!(read_dma_buf, all_zero); } diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 421cbb8a0..094d04f60 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -1261,7 +1261,7 @@ mod tests { use super::*; use crate::tests::{address_space_init, MEMORY_SIZE}; use crate::*; - use address_space::{AddressRange, HostMemMapping, Region}; + use address_space::{AddressAttr, AddressRange, HostMemMapping, Region}; use machine_manager::event_loop::EventLoop; const QUEUE_SIZE: u16 = 256; @@ -1507,7 +1507,11 @@ mod tests { // Set desc table. mem_space - .write_object::(&desc, GuestAddress(queue_config_inf.desc_table.0)) + .write_object::( + &desc, + GuestAddress(queue_config_inf.desc_table.0), + AddressAttr::Ram, + ) .unwrap(); let ele = GuestIovec { @@ -1515,13 +1519,21 @@ mod tests { iov_len: std::mem::size_of::() as u64, }; mem_space - .write_object::(&ele, GuestAddress(0x2000)) + .write_object::(&ele, GuestAddress(0x2000), AddressAttr::Ram) .unwrap(); mem_space - .write_object::(&0, GuestAddress(queue_config_inf.avail_ring.0 + 4_u64)) + .write_object::( + &0, + GuestAddress(queue_config_inf.avail_ring.0 + 4_u64), + AddressAttr::Ram, + ) .unwrap(); mem_space - .write_object::(&1, GuestAddress(queue_config_inf.avail_ring.0 + 2_u64)) + .write_object::( + &1, + GuestAddress(queue_config_inf.avail_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); assert!(handler.process_balloon_queue(BALLOON_INFLATE_EVENT).is_ok()); @@ -1537,17 +1549,29 @@ mod tests { }; mem_space - .write_object::(&desc, GuestAddress(queue_config_def.desc_table.0)) + .write_object::( + &desc, + GuestAddress(queue_config_def.desc_table.0), + AddressAttr::Ram, + ) .unwrap(); mem_space - .write_object::(&ele, GuestAddress(0x3000)) + .write_object::(&ele, GuestAddress(0x3000), AddressAttr::Ram) .unwrap(); mem_space - .write_object::(&0, GuestAddress(queue_config_def.avail_ring.0 + 4_u64)) + .write_object::( + &0, + GuestAddress(queue_config_def.avail_ring.0 + 4_u64), + AddressAttr::Ram, + ) .unwrap(); mem_space - .write_object::(&1, GuestAddress(queue_config_def.avail_ring.0 + 2_u64)) + .write_object::( + &1, + GuestAddress(queue_config_def.avail_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); assert!(handler.process_balloon_queue(BALLOON_DEFLATE_EVENT).is_ok()); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index efb6492e6..1d1749be1 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1457,7 +1457,7 @@ mod tests { use super::*; use crate::tests::address_space_init; use crate::*; - use address_space::GuestAddress; + use address_space::{AddressAttr, GuestAddress}; use machine_manager::config::{ str_slip_to_clap, IothreadConfig, VmConfig, DEFAULT_VIRTQUEUE_SIZE, }; @@ -1715,7 +1715,11 @@ mod tests { next: 1, }; mem_space - .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0), + AddressAttr::Ram, + ) .unwrap(); // write RequestOutHeader to first desc @@ -1725,7 +1729,7 @@ mod tests { sector: 0, }; mem_space - .write_object::(&req_head, GuestAddress(0x100)) + .write_object::(&req_head, GuestAddress(0x100), AddressAttr::Ram) .unwrap(); // making the second descriptor entry to receive data from device @@ -1736,17 +1740,29 @@ mod tests { next: 2, }; mem_space - .write_object::(&desc, GuestAddress(queue_config.desc_table.0 + 16_u64)) + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16_u64), + AddressAttr::Ram, + ) .unwrap(); // write avail_ring idx mem_space - .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4_u64)) + .write_object::( + &0, + GuestAddress(queue_config.avail_ring.0 + 4_u64), + AddressAttr::Ram, + ) .unwrap(); // write avail_ring id mem_space - .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2_u64)) + .write_object::( + &1, + GuestAddress(queue_config.avail_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); // imitating guest OS to send notification. @@ -1764,7 +1780,10 @@ mod tests { // get used_ring data let idx = mem_space - .read_object::(GuestAddress(queue_config.used_ring.0 + 2_u64)) + .read_object::( + GuestAddress(queue_config.used_ring.0 + 2_u64), + AddressAttr::Ram, + ) .unwrap(); if idx == 1 { break; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 092df0e10..6ebbbc4e7 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -990,7 +990,7 @@ mod tests { use super::*; use crate::tests::address_space_init; use crate::{Queue, QUEUE_TYPE_PACKED_VRING, QUEUE_TYPE_SPLIT_VRING}; - use address_space::{AddressSpace, GuestAddress}; + use address_space::{AddressAttr, AddressSpace, GuestAddress}; trait VringOpsTest { fn set_desc( @@ -1049,6 +1049,7 @@ mod tests { sys_mem.write_object::( &desc, GuestAddress(self.desc_table.0 + desc_addr_offset), + AddressAttr::Ram, )?; Ok(()) @@ -1056,15 +1057,21 @@ mod tests { fn set_avail_ring_idx(&self, sys_mem: &Arc, idx: u16) -> Result<()> { let avail_idx_offset = 2_u64; - sys_mem - .write_object::(&idx, GuestAddress(self.avail_ring.0 + avail_idx_offset))?; + sys_mem.write_object::( + &idx, + GuestAddress(self.avail_ring.0 + avail_idx_offset), + AddressAttr::Ram, + )?; Ok(()) } fn set_avail_ring_flags(&self, sys_mem: &Arc, flags: u16) -> Result<()> { let avail_idx_offset = 0_u64; - sys_mem - .write_object::(&flags, GuestAddress(self.avail_ring.0 + avail_idx_offset))?; + sys_mem.write_object::( + &flags, + GuestAddress(self.avail_ring.0 + avail_idx_offset), + AddressAttr::Ram, + )?; Ok(()) } @@ -1078,6 +1085,7 @@ mod tests { sys_mem.write_object::( &desc_index, GuestAddress(self.avail_ring.0 + avail_idx_offset), + AddressAttr::Ram, )?; Ok(()) } @@ -1085,36 +1093,49 @@ mod tests { fn get_avail_event(&self, sys_mem: &Arc) -> Result { let avail_event_idx_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(self.actual_size()); - let event_idx = sys_mem - .read_object::(GuestAddress(self.used_ring.0 + avail_event_idx_offset))?; + let event_idx = sys_mem.read_object::( + GuestAddress(self.used_ring.0 + avail_event_idx_offset), + AddressAttr::Ram, + )?; Ok(event_idx) } fn get_used_elem(&self, sys_mem: &Arc, index: u16) -> Result { let used_elem_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(index); - let used_elem = sys_mem - .read_object::(GuestAddress(self.used_ring.0 + used_elem_offset))?; + let used_elem = sys_mem.read_object::( + GuestAddress(self.used_ring.0 + used_elem_offset), + AddressAttr::Ram, + )?; Ok(used_elem) } fn get_used_ring_idx(&self, sys_mem: &Arc) -> Result { let used_idx_offset = VRING_IDX_POSITION; - let idx = - sys_mem.read_object::(GuestAddress(self.used_ring.0 + used_idx_offset))?; + let idx = sys_mem.read_object::( + GuestAddress(self.used_ring.0 + used_idx_offset), + AddressAttr::Ram, + )?; Ok(idx) } fn set_used_ring_idx(&self, sys_mem: &Arc, idx: u16) -> Result<()> { let used_idx_offset = VRING_IDX_POSITION; - sys_mem.write_object::(&idx, GuestAddress(self.used_ring.0 + used_idx_offset))?; + sys_mem.write_object::( + &idx, + GuestAddress(self.used_ring.0 + used_idx_offset), + AddressAttr::Ram, + )?; Ok(()) } fn set_used_event_idx(&self, sys_mem: &Arc, idx: u16) -> Result<()> { let event_idx_offset = VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.actual_size()); - sys_mem - .write_object::(&idx, GuestAddress(self.avail_ring.0 + event_idx_offset))?; + sys_mem.write_object::( + &idx, + GuestAddress(self.avail_ring.0 + event_idx_offset), + AddressAttr::Ram, + )?; Ok(()) } } @@ -1133,7 +1154,7 @@ mod tests { flags, next, }; - sys_mem.write_object::(&desc, desc_addr)?; + sys_mem.write_object::(&desc, desc_addr, AddressAttr::Ram)?; Ok(()) } -- Gitee From 44795e4c6ac5f14ce8712a4f1c2933d9e63e81a8 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Mon, 26 Aug 2024 13:55:17 +0800 Subject: [PATCH 2070/2187] Memory: Add an unsafe flag to the get host address function. It is dangerous to read and write directly to hva. So we add unsafe to mark this function is not safe! Signed-off-by: Li Huachao Signed-off-by: Yandong Xu --- address_space/src/address_space.rs | 59 ++++++--- address_space/src/region.rs | 28 +++-- devices/src/legacy/pflash.rs | 32 ++++- devices/src/legacy/ramfb.rs | 7 +- hypervisor/src/kvm/listener.rs | 5 +- vfio/src/vfio_dev.rs | 7 +- virtio/src/device/balloon.rs | 54 +++++--- virtio/src/device/block.rs | 21 +++- virtio/src/device/rng.rs | 42 +++++-- virtio/src/queue/split.rs | 193 +++++++++++++++++++---------- virtio/src/vhost/kernel/mod.rs | 11 +- virtio/src/vhost/user/client.rs | 9 +- 12 files changed, 315 insertions(+), 153 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index e138fee33..7e9fa7295 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -496,19 +496,27 @@ impl AddressSpace { Ok(()) } - /// Return the host address according to the given `GuestAddress`. + /// Return the host address according to the given `GuestAddress`. It is dangerous to + /// read and write directly to hva. We strongly recommend that you use the read and + /// write interface provided by AddressSpace unless you know exactly what you need and + /// are sure it is safe. /// /// # Arguments /// /// * `addr` - Guest address. - pub fn get_host_address(&self, addr: GuestAddress) -> Option { + /// + /// # Safety + /// + /// Using this function, the caller needs to make it clear that hva is always in the ram + /// range of the virtual machine. And if you want to operate [hva,hva+size], the range + /// from hva to hva+size needs to be in the ram range. + pub unsafe fn get_host_address(&self, addr: GuestAddress, attr: AddressAttr) -> Option { let view = self.flat_view.load(); - view.find_flatrange(addr).and_then(|range| { let offset = addr.offset_from(range.addr_range.base); range .owner - .get_host_address() + .get_host_address(attr) .map(|host| host + range.offset_in_region + offset) }) } @@ -520,7 +528,7 @@ impl AddressSpace { /// * `addr` - Guest address. /// Return Error if the `addr` is not mapped. /// or return the HVA address and available mem length - pub fn addr_cache_init(&self, addr: GuestAddress) -> Option<(u64, u64)> { + pub fn addr_cache_init(&self, addr: GuestAddress, attr: AddressAttr) -> Option<(u64, u64)> { let view = self.flat_view.load(); if let Some(flat_range) = view.find_flatrange(addr) { @@ -530,12 +538,15 @@ impl AddressSpace { let region_remain = flat_range.owner.size() - region_offset; let fr_remain = flat_range.addr_range.size - fr_offset; - return flat_range.owner.get_host_address().map(|host| { - ( - host + region_offset, - std::cmp::min(fr_remain, region_remain), - ) - }); + // SAFETY: addr and size is in ram region. + return unsafe { + flat_range.owner.get_host_address(attr).map(|host| { + ( + host + region_offset, + std::cmp::min(fr_remain, region_remain), + ) + }) + }; } None @@ -589,7 +600,7 @@ impl AddressSpace { cache: &Option, ) -> Option<(u64, u64)> { if cache.is_none() { - return self.addr_cache_init(addr); + return self.addr_cache_init(addr, AddressAttr::Ram); } let region_cache = cache.unwrap(); if addr.0 >= region_cache.start && addr.0 < region_cache.end { @@ -598,7 +609,7 @@ impl AddressSpace { region_cache.end - addr.0, )) } else { - self.addr_cache_init(addr) + self.addr_cache_init(addr, AddressAttr::Ram) } } @@ -616,13 +627,17 @@ impl AddressSpace { }) } - pub fn get_region_cache(&self, addr: GuestAddress) -> Option { + pub fn get_region_cache(&self, addr: GuestAddress, attr: AddressAttr) -> Option { let view = &self.flat_view.load(); if let Some(range) = view.find_flatrange(addr) { let reg_type = range.owner.region_type(); let start = range.addr_range.base.0; let end = range.addr_range.end_addr().0; - let host_base = self.get_host_address(GuestAddress(start)).unwrap_or(0); + // SAFETY: the size is in region range, and the type will be checked in get_host_address. + let host_base = unsafe { + self.get_host_address(GuestAddress(start), attr) + .unwrap_or(0) + }; let cache = RegionCache { reg_type, host_base, @@ -1271,11 +1286,11 @@ mod test { assert!(space.address_in_memory(GuestAddress(2900), 0)); assert_eq!( - space.get_host_address(GuestAddress(500)), + unsafe { space.get_host_address(GuestAddress(500), AddressAttr::Ram) }, Some(ram1.host_address() + 500) ); assert_eq!( - space.get_host_address(GuestAddress(2500)), + unsafe { space.get_host_address(GuestAddress(2500), AddressAttr::Ram) }, Some(ram2.host_address() + 500) ); @@ -1302,12 +1317,16 @@ mod test { assert!(space.address_in_memory(GuestAddress(2900), 0)); assert_eq!( - space.get_host_address(GuestAddress(500)), + unsafe { space.get_host_address(GuestAddress(500), AddressAttr::Ram) }, Some(ram1.host_address() + 500) ); - assert!(space.get_host_address(GuestAddress(2400)).is_none()); + assert!(unsafe { + space + .get_host_address(GuestAddress(2400), AddressAttr::Ram) + .is_none() + }); assert_eq!( - space.get_host_address(GuestAddress(2500)), + unsafe { space.get_host_address(GuestAddress(2500), AddressAttr::Ram) }, Some(ram2.host_address() + 500) ); } diff --git a/address_space/src/region.rs b/address_space/src/region.rs index 5d908db0e..48c70302a 100644 --- a/address_space/src/region.rs +++ b/address_space/src/region.rs @@ -21,8 +21,8 @@ use log::{debug, warn}; use crate::address_space::FlatView; use crate::{ - AddressRange, AddressSpace, AddressSpaceError, FileBackend, GuestAddress, HostMemMapping, - RegionOps, + AddressAttr, AddressRange, AddressSpace, AddressSpaceError, FileBackend, GuestAddress, + HostMemMapping, RegionOps, }; use migration::{migration::Migratable, MigrationManager}; @@ -467,11 +467,21 @@ impl Region { /// Get the host address if this region is backed by host-memory, /// Return `None` if it is not a Ram-type region. - pub fn get_host_address(&self) -> Option { - if self.region_type != RegionType::Ram - && self.region_type != RegionType::RamDevice - && self.region_type != RegionType::RomDevice - { + /// + /// # Safety + /// + /// Need to make it clear that hva is always in the ram range of the virtual machine. + /// And if you want to operate [hva,hva+size], the range from hva to hva+size needs + /// to be in the ram range. + pub unsafe fn get_host_address(&self, attr: AddressAttr) -> Option { + let region_type = match attr { + AddressAttr::Ram => RegionType::Ram, + AddressAttr::MMIO => return None, + AddressAttr::RamDevice => RegionType::RamDevice, + AddressAttr::RomDevice | AddressAttr::RomDeviceForce => RegionType::RomDevice, + }; + + if self.region_type != region_type { return None; } self.mem_mapping.as_ref().map(|r| r.host_address()) @@ -1155,7 +1165,7 @@ mod test { assert_eq!(&data, &mut res_data); assert_eq!( - ram_region.get_host_address().unwrap(), + unsafe { ram_region.get_host_address(AddressAttr::Ram).unwrap() }, mem_mapping.host_address() ); @@ -1235,7 +1245,7 @@ mod test { .is_ok()); assert_eq!(data.to_vec(), data_res.to_vec()); - assert!(io_region.get_host_address().is_none()); + assert!(unsafe { io_region.get_host_address(AddressAttr::Ram).is_none() }); } #[test] diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index a5c16ccd1..6c3138e01 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -21,7 +21,7 @@ use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::AmlBuilder; -use address_space::{FileBackend, GuestAddress, HostMemMapping, Region}; +use address_space::{AddressAttr, FileBackend, GuestAddress, HostMemMapping, Region}; use util::gen_base_func; use util::num_ops::{deposit_u32, extract_u32, read_data_u32, round_up, write_data_u32}; use util::unix::host_page_size; @@ -337,9 +337,17 @@ impl PFlash { ))); } - let addr: u64 = mr - .get_host_address() - .with_context(|| "Failed to get host address.")?; + let attr = match mr.region_type() { + address_space::RegionType::Ram => AddressAttr::Ram, + address_space::RegionType::RomDevice => AddressAttr::RomDevice, + _ => bail!("Unexpected region type."), + }; + + // SAFETY: size has been checked. + let addr: u64 = unsafe { + mr.get_host_address(attr) + .with_context(|| "Failed to get host address.") + }?; let ret = // SAFETY: addr and size are valid. unsafe { @@ -366,7 +374,13 @@ impl PFlash { data.len() as u64 ))); } - let host_addr = mr.get_host_address().unwrap(); + let attr = match mr.region_type() { + address_space::RegionType::Ram => AddressAttr::Ram, + address_space::RegionType::RomDevice => AddressAttr::RomDevice, + _ => bail!("Unexpected region type."), + }; + // SAFETY: size has been checked. + let host_addr = unsafe { mr.get_host_address(attr).unwrap() }; let src = // SAFETY: host_addr of the region is local allocated and sanity has been checked. unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; @@ -394,7 +408,13 @@ impl PFlash { data.len() as u64 ))); } - let host_addr = mr.get_host_address().unwrap(); + let attr = match mr.region_type() { + address_space::RegionType::Ram => AddressAttr::Ram, + address_space::RegionType::RomDevice => AddressAttr::RomDevice, + _ => bail!("Unexpected region type."), + }; + // SAFETY: size has been checked. + let host_addr = unsafe { mr.get_host_address(attr).unwrap() }; let mut dst = // SAFETY: host_addr of the region is local allocated and sanity has been checked. unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; diff --git a/devices/src/legacy/ramfb.rs b/devices/src/legacy/ramfb.rs index 8363213a4..569a59364 100644 --- a/devices/src/legacy/ramfb.rs +++ b/devices/src/legacy/ramfb.rs @@ -24,7 +24,7 @@ use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{convert_bus_mut, Device, DeviceBase, MUT_SYS_BUS}; use acpi::AmlBuilder; -use address_space::{AddressSpace, GuestAddress}; +use address_space::{AddressAttr, AddressSpace, GuestAddress}; use machine_manager::config::valid_id; use machine_manager::event_loop::EventLoop; use ui::console::{ @@ -125,7 +125,10 @@ impl RamfbState { stride = linesize; } - let fb_addr = match self.sys_mem.addr_cache_init(GuestAddress(addr)) { + let fb_addr = match self + .sys_mem + .addr_cache_init(GuestAddress(addr), AddressAttr::Ram) + { Some((hva, len)) => { if len < u64::from(stride) { error!("Insufficient contiguous memory length"); diff --git a/hypervisor/src/kvm/listener.rs b/hypervisor/src/kvm/listener.rs index 327df1d85..1a6943ffb 100644 --- a/hypervisor/src/kvm/listener.rs +++ b/hypervisor/src/kvm/listener.rs @@ -22,7 +22,7 @@ use log::{debug, warn}; use crate::HypervisorError; use address_space::{ - AddressRange, AddressSpaceError, FlatRange, Listener, ListenerReqType, MemSlot, + AddressAttr, AddressRange, AddressSpaceError, FlatRange, Listener, ListenerReqType, MemSlot, RegionIoEventFd, RegionType, }; use util::{num_ops::round_down, unix::host_page_size}; @@ -193,7 +193,8 @@ impl KvmMemoryListener { let align_adjust = aligned_addr.raw_value() - flat_range.addr_range.base.raw_value(); // `unwrap()` won't fail because Ram-type Region definitely has hva - let aligned_hva = flat_range.owner.get_host_address().unwrap() + // SAFETY: size has been checked. + let aligned_hva = unsafe { flat_range.owner.get_host_address(attr).unwrap() } + flat_range.offset_in_region + align_adjust; diff --git a/vfio/src/vfio_dev.rs b/vfio/src/vfio_dev.rs index cdba51c7f..5a6fa4447 100644 --- a/vfio/src/vfio_dev.rs +++ b/vfio/src/vfio_dev.rs @@ -32,7 +32,9 @@ use vmm_sys_util::{ioctl_io_nr, ioctl_ioc_nr}; use super::{CONTAINERS, GROUPS, KVM_DEVICE_FD}; use crate::VfioError; -use address_space::{AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd}; +use address_space::{ + AddressAttr, AddressSpace, FlatRange, Listener, ListenerReqType, RegionIoEventFd, +}; /// Refer to VFIO in https://github.com/torvalds/linux/blob/master/include/uapi/linux/vfio.h const IOMMU_GROUP: &str = "iommu_group"; @@ -227,7 +229,8 @@ impl VfioContainer { let guest_phys_addr = fr.addr_range.base.raw_value(); let memory_size = fr.addr_range.size; - let hva = match fr.owner.get_host_address() { + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + let hva = match unsafe { fr.owner.get_host_address(AddressAttr::Ram) } { Some(addr) => addr, None => bail!("Failed to get host address"), }; diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 094d04f60..894a84a5d 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -491,7 +491,8 @@ impl BlnMemInfo { fn add_mem_range(&self, fr: &FlatRange) { let guest_phys_addr = fr.addr_range.base.raw_value(); let memory_size = fr.addr_range.size; - if let Some(host_addr) = fr.owner.get_host_address() { + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + if let Some(host_addr) = unsafe { fr.owner.get_host_address(AddressAttr::Ram) } { let userspace_addr = host_addr + fr.offset_in_region; let reg_page_size = fr.owner.get_region_page_size(); self.regions.lock().unwrap().push(BlnMemoryRegion { @@ -509,7 +510,8 @@ impl BlnMemInfo { fn delete_mem_range(&self, fr: &FlatRange) { let mut mem_regions = self.regions.lock().unwrap(); - if let Some(host_addr) = fr.owner.get_host_address() { + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + if let Some(host_addr) = unsafe { fr.owner.get_host_address(AddressAttr::Ram) } { let reg_page_size = fr.owner.get_region_page_size(); let target = BlnMemoryRegion { guest_phys_addr: fr.addr_range.base.raw_value(), @@ -1437,33 +1439,45 @@ mod tests { let mut queue_config_inf = QueueConfig::new(QUEUE_SIZE); queue_config_inf.desc_table = GuestAddress(0x100); - queue_config_inf.addr_cache.desc_table_host = mem_space - .get_host_address(queue_config_inf.desc_table) - .unwrap(); + queue_config_inf.addr_cache.desc_table_host = unsafe { + mem_space + .get_host_address(queue_config_inf.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config_inf.avail_ring = GuestAddress(0x300); - queue_config_inf.addr_cache.avail_ring_host = mem_space - .get_host_address(queue_config_inf.avail_ring) - .unwrap(); + queue_config_inf.addr_cache.avail_ring_host = unsafe { + mem_space + .get_host_address(queue_config_inf.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config_inf.used_ring = GuestAddress(0x600); - queue_config_inf.addr_cache.used_ring_host = mem_space - .get_host_address(queue_config_inf.used_ring) - .unwrap(); + queue_config_inf.addr_cache.used_ring_host = unsafe { + mem_space + .get_host_address(queue_config_inf.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config_inf.ready = true; queue_config_inf.size = QUEUE_SIZE; let mut queue_config_def = QueueConfig::new(QUEUE_SIZE); queue_config_def.desc_table = GuestAddress(0x1100); - queue_config_def.addr_cache.desc_table_host = mem_space - .get_host_address(queue_config_def.desc_table) - .unwrap(); + queue_config_def.addr_cache.desc_table_host = unsafe { + mem_space + .get_host_address(queue_config_def.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config_def.avail_ring = GuestAddress(0x1300); - queue_config_def.addr_cache.avail_ring_host = mem_space - .get_host_address(queue_config_def.avail_ring) - .unwrap(); + queue_config_def.addr_cache.avail_ring_host = unsafe { + mem_space + .get_host_address(queue_config_def.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config_def.used_ring = GuestAddress(0x1600); - queue_config_def.addr_cache.used_ring_host = mem_space - .get_host_address(queue_config_def.used_ring) - .unwrap(); + queue_config_def.addr_cache.used_ring_host = unsafe { + mem_space + .get_host_address(queue_config_def.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config_def.ready = true; queue_config_def.size = QUEUE_SIZE; diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 1d1749be1..3cd02f56a 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -1688,14 +1688,23 @@ mod tests { let mut queue_config = QueueConfig::new(DEFAULT_VIRTQUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - mem_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + mem_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(16 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); - queue_config.addr_cache.avail_ring_host = - mem_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + mem_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(32 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); - queue_config.addr_cache.used_ring_host = - mem_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + mem_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 39a5d57c0..5cc277b1d 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -591,14 +591,23 @@ mod tests { let mut queue_config = QueueConfig::new(DEFAULT_VIRTQUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - mem_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + mem_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(16 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); - queue_config.addr_cache.avail_ring_host = - mem_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + mem_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(32 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); - queue_config.addr_cache.used_ring_host = - mem_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + mem_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; @@ -686,14 +695,23 @@ mod tests { let mut queue_config = QueueConfig::new(DEFAULT_VIRTQUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - mem_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + mem_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(16 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); - queue_config.addr_cache.avail_ring_host = - mem_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + mem_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(32 * u64::from(DEFAULT_VIRTQUEUE_SIZE)); - queue_config.addr_cache.used_ring_host = - mem_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + mem_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.size = DEFAULT_VIRTQUEUE_SIZE; queue_config.ready = true; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index 6ebbbc4e7..2fda39a42 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -27,7 +27,7 @@ use super::{ use crate::{ report_virtio_error, virtio_has_feature, VirtioError, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, }; -use address_space::{AddressSpace, GuestAddress, RegionCache, RegionType}; +use address_space::{AddressAttr, AddressSpace, GuestAddress, RegionCache, RegionType}; use util::byte_code::ByteCode; /// When host consumes a buffer, don't interrupt the guest. @@ -151,41 +151,44 @@ impl QueueConfig { features: u64, broken: &Arc, ) { - self.addr_cache.desc_table_host = - if let Some((addr, size)) = mem_space.addr_cache_init(self.desc_table) { - if size < self.get_desc_size() { - report_virtio_error(interrupt_cb.clone(), features, broken); - 0_u64 - } else { - addr - } - } else { + self.addr_cache.desc_table_host = if let Some((addr, size)) = + mem_space.addr_cache_init(self.desc_table, AddressAttr::Ram) + { + if size < self.get_desc_size() { + report_virtio_error(interrupt_cb.clone(), features, broken); 0_u64 - }; - - self.addr_cache.avail_ring_host = - if let Some((addr, size)) = mem_space.addr_cache_init(self.avail_ring) { - if size < self.get_avail_size(features) { - report_virtio_error(interrupt_cb.clone(), features, broken); - 0_u64 - } else { - addr - } } else { - 0_u64 - }; + addr + } + } else { + 0_u64 + }; - self.addr_cache.used_ring_host = - if let Some((addr, size)) = mem_space.addr_cache_init(self.used_ring) { - if size < self.get_used_size(features) { - report_virtio_error(interrupt_cb.clone(), features, broken); - 0_u64 - } else { - addr - } + self.addr_cache.avail_ring_host = if let Some((addr, size)) = + mem_space.addr_cache_init(self.avail_ring, AddressAttr::Ram) + { + if size < self.get_avail_size(features) { + report_virtio_error(interrupt_cb.clone(), features, broken); + 0_u64 } else { + addr + } + } else { + 0_u64 + }; + + self.addr_cache.used_ring_host = if let Some((addr, size)) = + mem_space.addr_cache_init(self.used_ring, AddressAttr::Ram) + { + if size < self.get_used_size(features) { + report_virtio_error(interrupt_cb.clone(), features, broken); 0_u64 - }; + } else { + addr + } + } else { + 0_u64 + }; } } @@ -303,7 +306,7 @@ impl SplitVringDesc { miss_cached = false; } } else { - let gotten_cache = sys_mem.get_region_cache(self.addr); + let gotten_cache = sys_mem.get_region_cache(self.addr, AddressAttr::Ram); if let Some(obtained_cache) = gotten_cache { if obtained_cache.reg_type == RegionType::Ram { *cache = gotten_cache; @@ -1411,19 +1414,28 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + sys_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); - queue_config.addr_cache.avail_ring_host = - sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + sys_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); - queue_config.addr_cache.used_ring_host = - sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + sys_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); @@ -1501,19 +1513,28 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + sys_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); - queue_config.addr_cache.avail_ring_host = - sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + sys_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); - queue_config.addr_cache.used_ring_host = - sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + sys_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); @@ -1601,19 +1622,28 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + sys_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); - queue_config.addr_cache.avail_ring_host = - sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + sys_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); - queue_config.addr_cache.used_ring_host = - sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + sys_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); @@ -1796,19 +1826,28 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + sys_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); - queue_config.addr_cache.avail_ring_host = - sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + sys_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); - queue_config.addr_cache.used_ring_host = - sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + sys_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); @@ -1960,19 +1999,28 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + sys_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); - queue_config.addr_cache.avail_ring_host = - sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + sys_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); - queue_config.addr_cache.used_ring_host = - sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + sys_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); @@ -2001,19 +2049,28 @@ mod tests { let mut queue_config = QueueConfig::new(QUEUE_SIZE); queue_config.desc_table = GuestAddress(0); - queue_config.addr_cache.desc_table_host = - sys_space.get_host_address(queue_config.desc_table).unwrap(); + queue_config.addr_cache.desc_table_host = unsafe { + sys_space + .get_host_address(queue_config.desc_table, AddressAttr::Ram) + .unwrap() + }; queue_config.avail_ring = GuestAddress(u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN); - queue_config.addr_cache.avail_ring_host = - sys_space.get_host_address(queue_config.avail_ring).unwrap(); + queue_config.addr_cache.avail_ring_host = unsafe { + sys_space + .get_host_address(queue_config.avail_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.used_ring = GuestAddress(align( u64::from(QUEUE_SIZE) * DESCRIPTOR_LEN + VRING_AVAIL_LEN_EXCEPT_AVAILELEM + AVAILELEM_LEN * u64::from(QUEUE_SIZE), 4096, )); - queue_config.addr_cache.used_ring_host = - sys_space.get_host_address(queue_config.used_ring).unwrap(); + queue_config.addr_cache.used_ring_host = unsafe { + sys_space + .get_host_address(queue_config.used_ring, AddressAttr::Ram) + .unwrap() + }; queue_config.ready = true; queue_config.size = QUEUE_SIZE; let mut vring = SplitVring::new(queue_config); diff --git a/virtio/src/vhost/kernel/mod.rs b/virtio/src/vhost/kernel/mod.rs index cca3cedc9..e04b1589a 100644 --- a/virtio/src/vhost/kernel/mod.rs +++ b/virtio/src/vhost/kernel/mod.rs @@ -34,7 +34,8 @@ use super::super::QueueConfig; use super::VhostOps; use crate::VirtioError; use address_space::{ - AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, RegionType, + AddressAttr, AddressSpace, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, + RegionType, }; use util::byte_code::ByteCode; @@ -158,7 +159,9 @@ impl VhostMemInfo { fn add_mem_range(&self, fr: &FlatRange) { let guest_phys_addr = fr.addr_range.base.raw_value(); let memory_size = fr.addr_range.size; - let userspace_addr = fr.owner.get_host_address().unwrap() + fr.offset_in_region; + let userspace_addr = + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + unsafe { fr.owner.get_host_address(AddressAttr::Ram).unwrap() } + fr.offset_in_region; self.regions.lock().unwrap().push(VhostMemoryRegion { guest_phys_addr, @@ -173,7 +176,9 @@ impl VhostMemInfo { let target = VhostMemoryRegion { guest_phys_addr: fr.addr_range.base.raw_value(), memory_size: fr.addr_range.size, - userspace_addr: fr.owner.get_host_address().unwrap() + fr.offset_in_region, + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + userspace_addr: unsafe { fr.owner.get_host_address(AddressAttr::Ram).unwrap() } + + fr.offset_in_region, flags_padding: 0_u64, }; for (index, mr) in mem_regions.iter().enumerate() { diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 469871f13..4a51c9294 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -33,7 +33,8 @@ use crate::device::block::VirtioBlkConfig; use crate::VhostUser::message::VhostUserConfig; use crate::{virtio_has_feature, Queue, QueueConfig}; use address_space::{ - AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, RegionIoEventFd, + AddressAttr, AddressSpace, FileBackend, FlatRange, GuestAddress, Listener, ListenerReqType, + RegionIoEventFd, }; use machine_manager::event_loop::{register_event_helper, unregister_event_helper, EventLoop}; use util::loop_context::{ @@ -234,7 +235,8 @@ impl VhostUserMemInfo { let guest_phys_addr = fr.addr_range.base.raw_value(); let memory_size = fr.addr_range.size; - let host_address = match fr.owner.get_host_address() { + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + let host_address = match unsafe { fr.owner.get_host_address(AddressAttr::Ram) } { Some(addr) => addr, None => bail!("Failed to get host address to add mem range for vhost user device"), }; @@ -287,7 +289,8 @@ impl VhostUserMemInfo { Some(fb) => fb, }; let mut mem_regions = self.regions.lock().unwrap(); - let host_address = match fr.owner.get_host_address() { + // SAFETY: memory_size is range's size, so we make sure [hva, hva+size] is in ram range. + let host_address = match unsafe { fr.owner.get_host_address(AddressAttr::Ram) } { Some(addr) => addr, None => bail!("Failed to get host address to del mem range for vhost user device"), }; -- Gitee From a381c38edb04a4adbaebf44dea2b9ee53523a0b6 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Thu, 29 Aug 2024 12:00:52 +0800 Subject: [PATCH 2071/2187] Balloon: Fix potential risks of data type conversion --- virtio/src/device/balloon.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 22462a792..3500b944e 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -723,10 +723,9 @@ impl BalloonIoHandler { let mut balloon_dev = dev.lock().unwrap(); for iov in req.iovec.iter() { if let Some(stat) = iov_to_buf::(&self.mem_space, iov, 0) { - let ram_size = (balloon_dev.mem_info.lock().unwrap().get_ram_size() - >> VIRTIO_BALLOON_PFN_SHIFT) - as u32; - balloon_dev.set_num_pages(cmp::min(stat._val as u32, ram_size)); + let ram_size = balloon_dev.mem_info.lock().unwrap().get_ram_size() + >> VIRTIO_BALLOON_PFN_SHIFT; + balloon_dev.set_num_pages(cmp::min(stat._val, ram_size) as u32); } } balloon_dev @@ -1037,11 +1036,11 @@ impl Balloon { if host_page_size > BALLOON_PAGE_SIZE && !self.mem_info.lock().unwrap().has_huge_page() { warn!("Balloon used with backing page size > 4kiB, this may not be reliable"); } - let target = (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32; + let target = size >> VIRTIO_BALLOON_PFN_SHIFT; let address_space_ram_size = - (self.mem_info.lock().unwrap().get_ram_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32; + self.mem_info.lock().unwrap().get_ram_size() >> VIRTIO_BALLOON_PFN_SHIFT; let vm_target = cmp::min(target, address_space_ram_size); - self.num_pages = address_space_ram_size - vm_target; + self.num_pages = (address_space_ram_size - vm_target) as u32; self.signal_config_change().with_context(|| { "Failed to notify about configuration change after setting balloon memory" })?; -- Gitee From 81834a913cd0f367b0a262b962b0ded708c9b1ff Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Wed, 4 Sep 2024 17:04:56 +0800 Subject: [PATCH 2072/2187] util: add more useful information to error log Add 'opcode' and 'nbytes' to error log. Signed-off-by: Yan Wang --- util/src/aio/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 56a142c8a..a401879bb 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -613,8 +613,11 @@ impl Aio { evt.res } else { error!( - "Async IO request failed, status {} res {}", - evt.status, evt.res + "Async IO request failed, opcode {:?} status {} res {} expect {}", + (*node).value.opcode, + evt.status, + evt.res, + (*node).value.nbytes ); -1 }; -- Gitee From 297c497060a591210c3b48fe486e0f462f65b3c3 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 2 Sep 2024 11:44:38 +0800 Subject: [PATCH 2073/2187] ohcamera:set first caller tokenID for camera thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On OHOS, there is a mode named "超级隐私模式",on which camera cannot be accessed, and OHOS will pop up a window for telling users that camera is unaccessable. Now no windows appears, when camera is accessed in VM on "超级隐私模式",just because we don't pass a HAP's tokenID to related SA when we open OHOS camera. So we need to set first caller tokenID, a HAP tokenID. Signed-off-by: zhanghan64 --- devices/src/camera_backend/mod.rs | 2 ++ devices/src/camera_backend/ohcam.rs | 8 +++++++- devices/src/misc/scream/mod.rs | 12 +----------- devices/src/usb/camera.rs | 4 ++-- machine/src/lib.rs | 6 +++++- util/src/ohos_binding/misc.rs | 13 +++++++++++-- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/devices/src/camera_backend/mod.rs b/devices/src/camera_backend/mod.rs index 86b1c4ec9..d9fd95f98 100644 --- a/devices/src/camera_backend/mod.rs +++ b/devices/src/camera_backend/mod.rs @@ -193,6 +193,7 @@ pub trait CameraBackend: Send + Sync { pub fn create_cam_backend( config: UsbCameraConfig, cameradev: CameraDevConfig, + _tokenid: u64, ) -> Result>> { let cam: Arc> = match cameradev.backend { #[cfg(feature = "usb_camera_v4l2")] @@ -205,6 +206,7 @@ pub fn create_cam_backend( CamBackendType::OhCamera => Arc::new(Mutex::new(OhCameraBackend::new( cameradev.id, cameradev.path, + _tokenid, )?)), CamBackendType::Demo => Arc::new(Mutex::new(DemoCameraBackend::new( config.id, diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index bf5d8ee9e..3527048cf 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -30,6 +30,7 @@ use crate::camera_backend::{ use trace::trace_scope::Scope; use util::aio::Iovec; use util::ohos_binding::camera::*; +use util::ohos_binding::misc::bound_tokenid; type OhCamCB = RwLock>; static OHCAM_CALLBACKS: Lazy = Lazy::new(|| RwLock::new(HashMap::new())); @@ -132,6 +133,7 @@ pub struct OhCameraBackend { all(target_env = "ohos", feature = "trace_to_hitrace") ))] async_scope: Box, + tokenid: u64, } // SAFETY: Send and Sync is not auto-implemented for raw pointer type. @@ -159,7 +161,7 @@ impl Drop for OhCameraBackend { } impl OhCameraBackend { - pub fn new(id: String, cam_name: String) -> Result { + pub fn new(id: String, cam_name: String, tokenid: u64) -> Result { let (ctx, profile_cnt) = OhCamera::new(cam_name.clone())?; Ok(OhCameraBackend { @@ -177,6 +179,7 @@ impl OhCameraBackend { all(target_env = "ohos", feature = "trace_to_hitrace") ))] async_scope: Box::new(OhCameraAsyncScope::default()), + tokenid, }) } } @@ -212,6 +215,9 @@ impl CameraBackend for OhCameraBackend { } fn video_stream_on(&mut self) -> Result<()> { + if self.tokenid != 0 { + bound_tokenid(self.tokenid)?; + } self.ctx.start_stream(on_buffer_available, on_broken)?; self.stream_on = true; Ok(()) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 38fd38b3a..a6c8ca8bf 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -44,7 +44,7 @@ use ohaudio::{OhAudio, OhAudioVolume}; #[cfg(feature = "scream_pulseaudio")] use pulseaudio::PulseStreamData; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -use util::ohos_binding::misc::{get_firstcaller_tokenid, set_firstcaller_tokenid}; +use util::ohos_binding::misc::bound_tokenid; pub const AUDIO_SAMPLE_RATE_44KHZ: u32 = 44100; pub const AUDIO_SAMPLE_RATE_48KHZ: u32 = 48000; @@ -472,16 +472,6 @@ impl StreamData { } } -#[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] -fn bound_tokenid(token_id: u64) -> Result<()> { - if token_id == 0 { - bail!("UI token ID not passed."); - } else if token_id != get_firstcaller_tokenid()? { - set_firstcaller_tokenid(token_id)?; - } - Ok(()) -} - #[derive(Clone, Debug)] enum ScreamInterface { #[cfg(feature = "scream_alsa")] diff --git a/devices/src/usb/camera.rs b/devices/src/usb/camera.rs index 7dce78341..159a33ca1 100644 --- a/devices/src/usb/camera.rs +++ b/devices/src/usb/camera.rs @@ -502,8 +502,8 @@ impl VideoStreamingControl { } impl UsbCamera { - pub fn new(config: UsbCameraConfig, cameradev: CameraDevConfig) -> Result { - let camera = create_cam_backend(config.clone(), cameradev)?; + pub fn new(config: UsbCameraConfig, cameradev: CameraDevConfig, tokenid: u64) -> Result { + let camera = create_cam_backend(config.clone(), cameradev, tokenid)?; Ok(Self { base: UsbDeviceBase::new(config.id, USB_CAMERA_BUFFER_LEN), vs_control: VideoStreamingControl::default(), diff --git a/machine/src/lib.rs b/machine/src/lib.rs index f69f40ddc..484aaf256 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1833,6 +1833,10 @@ pub trait MachineOps: MachineLifecycle { } #[cfg(feature = "usb_camera")] "usb-camera" => { + let token_id = match self.get_token_id() { + Some(id) => *id.read().unwrap(), + None => 0, + }; let config = UsbCameraConfig::try_parse_from(str_slip_to_clap(cfg_args, true, false))?; let cameradev = get_cameradev_by_id(vm_config, config.cameradev.clone()) @@ -1843,7 +1847,7 @@ pub trait MachineOps: MachineLifecycle { ) })?; - let camera = UsbCamera::new(config, cameradev)?; + let camera = UsbCamera::new(config, cameradev, token_id)?; camera .realize() .with_context(|| "Failed to realize usb camera device")? diff --git a/util/src/ohos_binding/misc.rs b/util/src/ohos_binding/misc.rs index 1d9e31a57..27b564557 100644 --- a/util/src/ohos_binding/misc.rs +++ b/util/src/ohos_binding/misc.rs @@ -34,7 +34,7 @@ ioctl_ior_nr!( ::std::os::raw::c_ulonglong ); -pub fn set_firstcaller_tokenid(id: u64) -> Result<()> { +fn set_firstcaller_tokenid(id: u64) -> Result<()> { let fd = OpenOptions::new() .read(true) .write(true) @@ -56,7 +56,7 @@ pub fn set_firstcaller_tokenid(id: u64) -> Result<()> { Ok(()) } -pub fn get_firstcaller_tokenid() -> Result { +fn get_firstcaller_tokenid() -> Result { let fd = OpenOptions::new() .read(true) .write(true) @@ -78,3 +78,12 @@ pub fn get_firstcaller_tokenid() -> Result { } Ok(id) } + +pub fn bound_tokenid(token_id: u64) -> Result<()> { + if token_id == 0 { + bail!("UI token ID not passed."); + } else if token_id != get_firstcaller_tokenid()? { + set_firstcaller_tokenid(token_id)?; + } + Ok(()) +} -- Gitee From 60e5379a47b20d7092893109119398cecbe07f6a Mon Sep 17 00:00:00 2001 From: zhanghan Date: Thu, 5 Sep 2024 16:58:02 +0800 Subject: [PATCH 2074/2187] ohcam: fix Box type's default declaration RUST thinks Box::::default() is better than Box::new(T::default()). So we use former format for declaration. Signed-off-by: zhanghan --- devices/src/camera_backend/ohcam.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/camera_backend/ohcam.rs b/devices/src/camera_backend/ohcam.rs index 3527048cf..260dcf7d9 100755 --- a/devices/src/camera_backend/ohcam.rs +++ b/devices/src/camera_backend/ohcam.rs @@ -178,7 +178,7 @@ impl OhCameraBackend { feature = "trace_to_ftrace", all(target_env = "ohos", feature = "trace_to_hitrace") ))] - async_scope: Box::new(OhCameraAsyncScope::default()), + async_scope: Box::::default(), tokenid, }) } -- Gitee From e1843a7279226cd9f136051c39c5c8b3b634ff25 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 15 Aug 2024 16:32:21 +0800 Subject: [PATCH 2075/2187] USB: Modify type of dequeue of cmd ring and transfer ring Modify type of dequeue of cmd ring and transfer ring. And check if the dequeue addr locates in guest ram. Signed-off-by: Jinhao Gao --- devices/src/usb/xhci/xhci_controller.rs | 2 +- devices/src/usb/xhci/xhci_ring.rs | 57 +++++++++++++------------ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index fc2a44939..ad92caa86 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1883,7 +1883,7 @@ impl XhciDevice { let mut evt = XhciEvent::new(TRBType::ErTransfer, ccode); evt.slot_id = slot_id as u8; evt.ep_id = ep_id as u8; - evt.ptr = ring.get_dequeue_ptr(); + evt.ptr = ring.get_dequeue_ptr().raw_value(); if let Err(e) = self.intrs[0].lock().unwrap().send_event(&evt) { error!("Failed to send event: {:?}", e); } diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 061fd48f3..0bfcee498 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -10,10 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::sync::atomic::{fence, AtomicBool, AtomicU64, Ordering}; -use std::sync::Arc; +use std::sync::atomic::{fence, AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use byteorder::{ByteOrder, LittleEndian}; use log::debug; @@ -54,7 +54,7 @@ impl XhciTRB { #[derive(Clone)] pub struct XhciCommandRing { mem: Arc, - pub dequeue: u64, + pub dequeue: GuestAddress, /// Consumer Cycle State pub ccs: bool, } @@ -63,13 +63,13 @@ impl XhciCommandRing { pub fn new(mem: &Arc) -> Self { Self { mem: mem.clone(), - dequeue: 0, + dequeue: GuestAddress(0), ccs: true, } } pub fn init(&mut self, addr: u64) { - self.dequeue = addr; + self.dequeue = GuestAddress(addr); self.ccs = true; } @@ -87,7 +87,7 @@ impl XhciCommandRing { } fence(Ordering::Acquire); let mut trb = read_trb(&self.mem, self.dequeue)?; - trb.addr = self.dequeue; + trb.addr = self.dequeue.raw_value(); trb.ccs = self.ccs; let trb_type = trb.get_type(); debug!("Fetch TRB: type {:?} trb {:?}", trb_type, trb); @@ -96,7 +96,7 @@ impl XhciCommandRing { if link_cnt > TRB_LINK_LIMIT { bail!("TRB reach link limit"); } - self.dequeue = trb.parameter; + self.dequeue = GuestAddress(trb.parameter); if trb.control & TRB_LK_TC == TRB_LK_TC { self.ccs = !self.ccs; } @@ -105,7 +105,10 @@ impl XhciCommandRing { .dequeue .checked_add(u64::from(TRB_SIZE)) .ok_or_else(|| { - UsbError::MemoryAccessOverflow(self.dequeue, u64::from(TRB_SIZE)) + UsbError::MemoryAccessOverflow( + self.dequeue.raw_value(), + u64::from(TRB_SIZE), + ) })?; return Ok(Some(trb)); } @@ -116,7 +119,7 @@ impl XhciCommandRing { /// XHCI Transfer Ring pub struct XhciTransferRing { pub mem: Arc, - pub dequeue: AtomicU64, + pub dequeue: Mutex, /// Consumer Cycle State pub ccs: AtomicBool, } @@ -125,22 +128,22 @@ impl XhciTransferRing { pub fn new(mem: &Arc) -> Self { Self { mem: mem.clone(), - dequeue: AtomicU64::new(0), + dequeue: Mutex::new(GuestAddress(0)), ccs: AtomicBool::new(true), } } pub fn init(&self, addr: u64) { - self.set_dequeue_ptr(addr); + self.set_dequeue_ptr(GuestAddress(addr)); self.set_cycle_bit(true); } - pub fn get_dequeue_ptr(&self) -> u64 { - self.dequeue.load(Ordering::Acquire) + pub fn get_dequeue_ptr(&self) -> GuestAddress { + *self.dequeue.lock().unwrap() } - pub fn set_dequeue_ptr(&self, addr: u64) { - self.dequeue.store(addr, Ordering::SeqCst); + pub fn set_dequeue_ptr(&self, addr: GuestAddress) { + *self.dequeue.lock().unwrap() = addr } pub fn get_cycle_bit(&self) -> bool { @@ -168,7 +171,7 @@ impl XhciTransferRing { } fence(Ordering::Acquire); let mut trb = read_trb(&self.mem, dequeue)?; - trb.addr = dequeue; + trb.addr = dequeue.raw_value(); trb.ccs = ccs; trace::usb_xhci_fetch_trb(&dequeue, &trb.parameter, &trb.status, &trb.control); let trb_type = trb.get_type(); @@ -177,15 +180,15 @@ impl XhciTransferRing { if link_cnt > TRB_LINK_LIMIT { bail!("TRB link over limit"); } - dequeue = trb.parameter; + dequeue = GuestAddress(trb.parameter); if trb.control & TRB_LK_TC == TRB_LK_TC { ccs = !ccs; } } else { td.push(trb); - dequeue = dequeue - .checked_add(u64::from(TRB_SIZE)) - .ok_or_else(|| UsbError::MemoryAccessOverflow(dequeue, u64::from(TRB_SIZE)))?; + dequeue = dequeue.checked_add(u64::from(TRB_SIZE)).ok_or_else(|| { + UsbError::MemoryAccessOverflow(dequeue.raw_value(), u64::from(TRB_SIZE)) + })?; if trb_type == TRBType::TrSetup { ctrl_td = true; } else if trb_type == TRBType::TrStatus { @@ -216,15 +219,15 @@ impl XhciTransferRing { } pub fn update_dequeue_to_ctx(&self, ctx: &mut [u32]) { - let dequeue = self.get_dequeue_ptr(); + let dequeue = self.get_dequeue_ptr().raw_value(); ctx[0] = dequeue as u32 | u32::from(self.get_cycle_bit()); ctx[1] = (dequeue >> 32) as u32; } } -fn read_trb(mem: &Arc, addr: u64) -> Result { +fn read_trb(mem: &Arc, addr: GuestAddress) -> Result { let mut buf = [0; TRB_SIZE as usize]; - dma_read_bytes(mem, GuestAddress(addr), &mut buf)?; + dma_read_bytes(mem, addr, &mut buf)?; let trb = XhciTRB { parameter: LittleEndian::read_u64(&buf), status: LittleEndian::read_u32(&buf[8..]), @@ -235,12 +238,12 @@ fn read_trb(mem: &Arc, addr: u64) -> Result { Ok(trb) } -fn read_cycle_bit(mem: &Arc, addr: u64) -> Result { +fn read_cycle_bit(mem: &Arc, addr: GuestAddress) -> Result { let addr = addr .checked_add(12) - .with_context(|| format!("Ring address overflow, {:x}", addr))?; + .ok_or_else(|| UsbError::MemoryAccessOverflow(addr.raw_value(), 12))?; let mut buf = [0]; - dma_read_u32(mem, GuestAddress(addr), &mut buf)?; + dma_read_u32(mem, addr, &mut buf)?; Ok(buf[0] & TRB_C == TRB_C) } -- Gitee From 75cf3ed0f3a4f8c258f16b2cd62c34de1bd9f945 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Thu, 15 Aug 2024 19:20:54 +0800 Subject: [PATCH 2076/2187] USB: Check whether some addresses related to the event ring are valid The event ring dequeue pointer and the event ring segment table base address must locate in guest ram. Signed-off-by: Jinhao Gao --- devices/src/usb/xhci/xhci_controller.rs | 2 +- devices/src/usb/xhci/xhci_regs.rs | 56 ++++++++++++++----------- devices/src/usb/xhci/xhci_ring.rs | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index ad92caa86..7ad659c52 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2361,7 +2361,7 @@ impl XhciDevice { pub(crate) fn reset_event_ring(&mut self, idx: u32) -> Result<()> { let mut locked_intr = self.intrs[idx as usize].lock().unwrap(); - if locked_intr.erstsz == 0 || locked_intr.erstba == 0 { + if locked_intr.erstsz == 0 || locked_intr.erstba.raw_value() == 0 { locked_intr.er_start = GuestAddress(0); locked_intr.er_size = 0; return Ok(()); diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 7062d52c7..02c92ce2d 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -183,9 +183,9 @@ pub struct XhciInterrupter { /// Event Ring Segment Table Size pub erstsz: u32, /// Event Ring Segment Table Base Address - pub erstba: u64, + pub erstba: GuestAddress, /// Event Ring Dequeue Pointer - pub erdp: u64, + pub erdp: GuestAddress, /// Event Ring Producer Cycle State pub er_pcs: bool, pub er_start: GuestAddress, @@ -209,8 +209,8 @@ impl XhciInterrupter { iman: 0, imod: 0, erstsz: 0, - erstba: 0, - erdp: 0, + erstba: GuestAddress(0), + erdp: GuestAddress(0), er_pcs: true, er_start: GuestAddress(0), er_size: 0, @@ -235,8 +235,8 @@ impl XhciInterrupter { self.iman = 0; self.imod = 0; self.erstsz = 0; - self.erstba = 0; - self.erdp = 0; + self.erstba = GuestAddress(0); + self.erdp = GuestAddress(0); self.er_pcs = true; self.er_start = GuestAddress(0); self.er_size = 0; @@ -254,15 +254,15 @@ impl XhciInterrupter { u64::from(TRB_SIZE * self.er_size), ) })?; - if self.erdp < self.er_start.raw_value() || self.erdp >= er_end.raw_value() { + if self.erdp < self.er_start || self.erdp >= er_end { bail!( - "DMA out of range, erdp {} er_start {:x} er_size {}", - self.erdp, + "DMA out of range, erdp {:x} er_start {:x} er_size {}", + self.erdp.raw_value(), self.er_start.raw_value(), self.er_size ); } - let dp_idx = (self.erdp - self.er_start.raw_value()) / u64::from(TRB_SIZE); + let dp_idx = (self.erdp.raw_value() - self.er_start.raw_value()) / u64::from(TRB_SIZE); if u64::from((self.er_ep_idx + 2) % self.er_size) == dp_idx { debug!("Event ring full error, idx {}", dp_idx); let event = XhciEvent::new(TRBType::ErHostController, TRBCCode::EventRingFullError); @@ -277,10 +277,11 @@ impl XhciInterrupter { } fn send_intr(&mut self) { - let pending = read_u32(self.erdp, 0) & ERDP_EHB == ERDP_EHB; - let mut erdp_low = read_u32(self.erdp, 0); + let erdp = self.erdp.raw_value(); + let pending = read_u32(erdp, 0) & ERDP_EHB == ERDP_EHB; + let mut erdp_low = read_u32(erdp, 0); erdp_low |= ERDP_EHB; - self.erdp = write_u64_low(self.erdp, erdp_low); + self.erdp = GuestAddress(write_u64_low(erdp, erdp_low)); self.iman |= IMAN_IP; self.enable_intr(); if pending { @@ -581,10 +582,10 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_INTR_REG_IMAN => locked_intr.iman, XHCI_INTR_REG_IMOD => locked_intr.imod, XHCI_INTR_REG_ERSTSZ => locked_intr.erstsz, - XHCI_INTR_REG_ERSTBA_LO => read_u32(locked_intr.erstba, 0), - XHCI_INTR_REG_ERSTBA_HI => read_u32(locked_intr.erstba, 1), - XHCI_INTR_REG_ERDP_LO => read_u32(locked_intr.erdp, 0), - XHCI_INTR_REG_ERDP_HI => read_u32(locked_intr.erdp, 1), + XHCI_INTR_REG_ERSTBA_LO => read_u32(locked_intr.erstba.raw_value(), 0), + XHCI_INTR_REG_ERSTBA_HI => read_u32(locked_intr.erstba.raw_value(), 1), + XHCI_INTR_REG_ERDP_LO => read_u32(locked_intr.erdp.raw_value(), 0), + XHCI_INTR_REG_ERDP_HI => read_u32(locked_intr.erdp.raw_value(), 1), _ => { error!( "Invalid offset {:x} for reading interrupter registers.", @@ -627,10 +628,12 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { XHCI_INTR_REG_IMOD => locked_intr.imod = value, XHCI_INTR_REG_ERSTSZ => locked_intr.erstsz = value & 0xffff, XHCI_INTR_REG_ERSTBA_LO => { - locked_intr.erstba = write_u64_low(locked_intr.erstba, value & 0xffffffc0); + let erstba = write_u64_low(locked_intr.erstba.raw_value(), value & 0xffffffc0); + locked_intr.erstba = GuestAddress(erstba); } XHCI_INTR_REG_ERSTBA_HI => { - locked_intr.erstba = write_u64_high(locked_intr.erstba, value); + let erstba = GuestAddress(write_u64_high(locked_intr.erstba.raw_value(), value)); + locked_intr.erstba = erstba; drop(locked_intr); if let Err(e) = xhci.reset_event_ring(idx) { error!("Failed to reset event ring: {:?}", e); @@ -640,10 +643,11 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { // ERDP_EHB is write 1 clear. let mut erdp_lo = value & !ERDP_EHB; if value & ERDP_EHB != ERDP_EHB { - let erdp_old = read_u32(locked_intr.erdp, 0); + let erdp_old = read_u32(locked_intr.erdp.raw_value(), 0); erdp_lo |= erdp_old & ERDP_EHB; } - locked_intr.erdp = write_u64_low(locked_intr.erdp, erdp_lo); + let erdp = write_u64_low(locked_intr.erdp.raw_value(), erdp_lo); + locked_intr.erdp = GuestAddress(erdp); if value & ERDP_EHB == ERDP_EHB { let erdp = locked_intr.erdp; let er_end = if let Some(addr) = locked_intr @@ -659,9 +663,10 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { ); return false; }; - if erdp >= locked_intr.er_start.raw_value() - && erdp < er_end.raw_value() - && (erdp - locked_intr.er_start.raw_value()) / u64::from(TRB_SIZE) + if erdp >= locked_intr.er_start + && erdp < er_end + && (erdp.raw_value() - locked_intr.er_start.raw_value()) + / u64::from(TRB_SIZE) != u64::from(locked_intr.er_ep_idx) { drop(locked_intr); @@ -670,7 +675,8 @@ pub fn build_runtime_ops(xhci_dev: &Arc>) -> RegionOps { } } XHCI_INTR_REG_ERDP_HI => { - locked_intr.erdp = write_u64_high(locked_intr.erdp, value); + let erdp = write_u64_high(locked_intr.erdp.raw_value(), value); + locked_intr.erdp = GuestAddress(erdp); } _ => { error!( diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index 0bfcee498..b7a5df43c 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -268,9 +268,9 @@ impl XhciEventRingSeg { } /// Fetch the event ring segment. - pub fn fetch_event_ring_seg(&mut self, addr: u64) -> Result<()> { + pub fn fetch_event_ring_seg(&mut self, addr: GuestAddress) -> Result<()> { let mut buf = [0_u8; TRB_SIZE as usize]; - dma_read_bytes(&self.mem, GuestAddress(addr), &mut buf)?; + dma_read_bytes(&self.mem, addr, &mut buf)?; self.addr_lo = LittleEndian::read_u32(&buf); self.addr_hi = LittleEndian::read_u32(&buf[4..]); self.size = LittleEndian::read_u32(&buf[8..]); -- Gitee From 742abd17775828f4b73a002e67194852a677d868 Mon Sep 17 00:00:00 2001 From: Jinhao Gao Date: Mon, 19 Aug 2024 21:41:37 +0800 Subject: [PATCH 2077/2187] USB: Check if dcbaap address is valid Check if dcbaap address is valid. Signed-off-by: Jinhao Gao --- devices/src/usb/xhci/xhci_controller.rs | 5 +++-- devices/src/usb/xhci/xhci_regs.rs | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 7ad659c52..8eca4640b 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -1459,9 +1459,10 @@ impl XhciDevice { fn get_device_context_addr(&self, slot_id: u32) -> Result { self.oper .dcbaap + .raw_value() .checked_add(u64::from(8 * slot_id)) .with_context(|| { - UsbError::MemoryAccessOverflow(self.oper.dcbaap, u64::from(8 * slot_id)) + UsbError::MemoryAccessOverflow(self.oper.dcbaap.raw_value(), u64::from(8 * slot_id)) }) } @@ -1700,7 +1701,7 @@ impl XhciDevice { } self.cancel_all_ep_transfers(slot_id, ep_id, TRBCCode::Invalid)?; let epctx = &mut self.slots[(slot_id - 1) as usize].endpoints[(ep_id - 1) as usize]; - if self.oper.dcbaap != 0 { + if self.oper.dcbaap.raw_value() != 0 { epctx.set_state(EP_DISABLED, None)?; } epctx.enabled = false; diff --git a/devices/src/usb/xhci/xhci_regs.rs b/devices/src/usb/xhci/xhci_regs.rs index 02c92ce2d..abf3128d4 100644 --- a/devices/src/usb/xhci/xhci_regs.rs +++ b/devices/src/usb/xhci/xhci_regs.rs @@ -124,7 +124,7 @@ pub struct XhciOperReg { /// Command Ring Control pub cmd_ring_ctrl: u64, /// Device Context Base Address Array Pointer - pub dcbaap: u64, + pub dcbaap: GuestAddress, /// Configure pub config: u32, } @@ -135,7 +135,7 @@ impl XhciOperReg { self.set_usb_status(USB_STS_HCH); self.dev_notify_ctrl = 0; self.cmd_ring_ctrl = 0; - self.dcbaap = 0; + self.dcbaap = GuestAddress(0); self.config = 0; } @@ -453,8 +453,8 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { // Table 5-24 shows read CRP always returns 0. 0 } - XHCI_OPER_REG_DCBAAP_LO => read_u32(locked_xhci.oper.dcbaap, 0), - XHCI_OPER_REG_DCBAAP_HI => read_u32(locked_xhci.oper.dcbaap, 1), + XHCI_OPER_REG_DCBAAP_LO => read_u32(locked_xhci.oper.dcbaap.raw_value(), 0), + XHCI_OPER_REG_DCBAAP_HI => read_u32(locked_xhci.oper.dcbaap.raw_value(), 1), XHCI_OPER_REG_CONFIG => locked_xhci.oper.config, _ => { error!( @@ -535,10 +535,12 @@ pub fn build_oper_ops(xhci_dev: &Arc>) -> RegionOps { locked_xhci.oper.cmd_ring_ctrl = write_u64_low(crc_hi, crc_lo); } XHCI_OPER_REG_DCBAAP_LO => { - locked_xhci.oper.dcbaap = write_u64_low(locked_xhci.oper.dcbaap, value & 0xffffffc0) + let dcbaap = write_u64_low(locked_xhci.oper.dcbaap.raw_value(), value & 0xffffffc0); + locked_xhci.oper.dcbaap = GuestAddress(dcbaap); } XHCI_OPER_REG_DCBAAP_HI => { - locked_xhci.oper.dcbaap = write_u64_high(locked_xhci.oper.dcbaap, value) + let dcbaap = write_u64_high(locked_xhci.oper.dcbaap.raw_value(), value); + locked_xhci.oper.dcbaap = GuestAddress(dcbaap); } XHCI_OPER_REG_CONFIG => locked_xhci.oper.config = value & 0xff, _ => { -- Gitee From 82985e259483670958bf40f44d13390267ab81c7 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 8 Sep 2024 00:59:03 +0800 Subject: [PATCH 2078/2187] xhci: fix potential left shift overflow Fix potential left shift overflow in `addr64_from_u32`. Signed-off-by: liuxiangdong --- devices/src/usb/xhci/xhci_controller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 8eca4640b..5ac003139 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -2519,7 +2519,7 @@ pub fn dma_write_u32( } fn addr64_from_u32(low: u32, high: u32) -> u64 { - (u64::from(high << 16) << 16) | u64::from(low) + (u64::from(high) << 32) | u64::from(low) } // | ep id | < = > | ep direction | ep number | -- Gitee From 89e74faf24aa5004e946ab1616f4bc0f9e15ec04 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 15 Aug 2024 09:14:31 +0800 Subject: [PATCH 2079/2187] StratoVirt: Add a Rust implemented OCI runtime named ozonec Cli of ozonec: ozonec [GLOBAL OPTIONS] [OPTIONS] ... --- ozonec/Cargo.toml | 23 +++++++++++++++ ozonec/src/main.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 ozonec/Cargo.toml create mode 100644 ozonec/src/main.rs diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml new file mode 100644 index 000000000..f80ccad0b --- /dev/null +++ b/ozonec/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ozonec" +version = "0.1.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "An OCI runtime implemented by Rust" + +[dependencies] +anyhow = "= 1.0.71" +clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } + +[workspace] + +[profile.dev] +panic = "unwind" + +[profile.release] +lto = true +strip = true +opt-level = 'z' +codegen-units = 1 +panic = "abort" diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs new file mode 100644 index 000000000..71b777bf4 --- /dev/null +++ b/ozonec/src/main.rs @@ -0,0 +1,72 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{path::PathBuf, process::exit}; + +use anyhow::Result; +use clap::{crate_description, Args, Parser, Subcommand}; + +// Global options which are not binded to any specific command. +#[derive(Args, Debug)] +struct GlobalOpts { + /// Root directory to store container state. + #[arg(short, long)] + root: Option, + /// Path of log file. + #[arg(short, long)] + log: Option, + /// Enable debug log level. + #[arg(short, long)] + debug: bool, +} + +// Standard commands supported by [OCI runtime-spec] +// (https://github.com/opencontainers/runtime-spec/blob/master/runtime.md) +// and [OCI Command Line Interface] +// (https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md). +#[derive(Subcommand, Debug)] +enum StandardCmd {} + +// Extended commands not documented in [OCI Command Line Interface]. +#[derive(Subcommand, Debug)] +enum ExtendCmd {} + +#[derive(Subcommand, Debug)] +enum Command { + #[command(flatten)] + Standard(StandardCmd), + #[command(flatten)] + Extend(ExtendCmd), +} + +#[derive(Parser, Debug)] +#[command(version, author, about = crate_description!())] +#[command(propagate_version = true)] +struct Cli { + #[command(flatten)] + global: GlobalOpts, + #[command(subcommand)] + cmd: Command, +} + +fn real_main() -> Result<()> { + let cli = Cli::parse(); + Ok(()) +} + +fn main() { + if let Err(e) = real_main() { + eprintln!("ERROR: {:?}", e); + exit(1); + } + exit(0); +} -- Gitee From cc251eed4d0bb68d26a33388e34f95b4c9fe3137 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 15 Aug 2024 18:17:34 +0800 Subject: [PATCH 2080/2187] ozonec/utils: Add logger.rs Write logs to the file specified by global command option --log, otherwise output logs to stderr. --- ozonec/Cargo.toml | 3 + ozonec/src/main.rs | 9 +- ozonec/src/utils/logger.rs | 238 +++++++++++++++++++++++++++++++++++++ ozonec/src/utils/mod.rs | 13 ++ 4 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 ozonec/src/utils/logger.rs create mode 100644 ozonec/src/utils/mod.rs diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index f80ccad0b..d853ab0ab 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -9,6 +9,9 @@ description = "An OCI runtime implemented by Rust" [dependencies] anyhow = "= 1.0.71" clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } +libc = "= 0.2.146" +log = { version = "= 0.4.18", features = ["std"]} +nix = "= 0.26.2" [workspace] diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index 71b777bf4..697528605 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -10,11 +10,15 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod utils; + use std::{path::PathBuf, process::exit}; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; +use crate::utils::logger; + // Global options which are not binded to any specific command. #[derive(Args, Debug)] struct GlobalOpts { @@ -60,6 +64,9 @@ struct Cli { fn real_main() -> Result<()> { let cli = Cli::parse(); + + logger::init(&cli.global.log, cli.global.debug).with_context(|| "Failed to init logger")?; + Ok(()) } diff --git a/ozonec/src/utils/logger.rs b/ozonec/src/utils/logger.rs new file mode 100644 index 000000000..17a35ff88 --- /dev/null +++ b/ozonec/src/utils/logger.rs @@ -0,0 +1,238 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::{remove_file, rename, File, OpenOptions}, + io::{stderr, Write}, + num::Wrapping, + os::unix::fs::OpenOptionsExt, + path::{Path, PathBuf}, + sync::Mutex, + time::UNIX_EPOCH, +}; + +use anyhow::{Context, Result}; +use log::{set_boxed_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record}; +use nix::unistd::{getpid, gettid}; + +// Maximum size of log file is 100MB. +const LOG_ROTATE_SIZE_MAX: usize = 100 * 1024 * 1024; +// Logs are retained for seven days at most. +const LOG_ROTATE_CNT_MAX: u8 = 7; + +struct LogRotate { + handler: Box, + path: String, + size: Wrapping, + created_day: i32, +} + +impl LogRotate { + fn rotate(&mut self, inc_size: usize) -> Result<()> { + if self.path.is_empty() { + return Ok(()); + } + + self.size += Wrapping(inc_size); + let seconds = wall_time().0; + let today = formatted_time(seconds)[2]; + if self.size < Wrapping(LOG_ROTATE_SIZE_MAX) && self.created_day == today { + return Ok(()); + } + + // Delete oldest log file. + let mut rotate_cnt = LOG_ROTATE_CNT_MAX - 1; + let olddest = format!("{}{}", self.path, rotate_cnt); + if Path::new(&olddest).exists() { + remove_file(&olddest).with_context(|| "Failed to delete olddest log")?; + } + + // Rename remaining logs. + let mut new_log = olddest; + while rotate_cnt != 0 { + let mut old_log = self.path.clone(); + + rotate_cnt -= 1; + if rotate_cnt != 0 { + old_log += &rotate_cnt.to_string(); + } + + if Path::new(&old_log).exists() { + rename(&old_log, &new_log) + .with_context(|| format!("Failed to rename {} to {}", old_log, new_log))?; + } + new_log = old_log; + } + + self.handler = Box::new( + open_log_file(&PathBuf::from(self.path.clone())) + .with_context(|| format!("Failed to convert {}", self.path))?, + ); + self.size = Wrapping(0); + self.created_day = today; + Ok(()) + } +} + +fn open_log_file(path: &PathBuf) -> Result { + OpenOptions::new() + .read(false) + .write(true) + .append(true) + .create(true) + .mode(0o640) + .open(path) + .with_context(|| "Failed to open log file") +} + +fn formatted_time(seconds: i64) -> [i32; 6] { + // SAFETY: an all-zero value is valid for libc::tm. + let mut ti: libc::tm = unsafe { std::mem::zeroed() }; + unsafe { + // SAFETY: seconds and ti are both local variables and valid. + libc::localtime_r(&seconds, &mut ti); + } + [ + ti.tm_year + 1900, + ti.tm_mon + 1, + ti.tm_mday, + ti.tm_hour, + ti.tm_min, + ti.tm_sec, + ] +} + +fn wall_time() -> (i64, i64) { + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + unsafe { + // SAFETY: ts is a local variable and valid. + libc::clock_gettime(libc::CLOCK_REALTIME, &mut ts); + } + (ts.tv_sec, ts.tv_nsec) +} + +fn formatted_now() -> String { + let (sec, nsec) = wall_time(); + let formatted_time = formatted_time(sec); + + format!( + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}:{:09}", + formatted_time[0], + formatted_time[1], + formatted_time[2], + formatted_time[3], + formatted_time[4], + formatted_time[5], + nsec + ) +} + +struct Logger { + rotate: Mutex, + level: Level, +} + +impl Logger { + fn new(path: &Option, level: Level) -> Result { + let (log_file, log_size, created_day) = match path { + Some(p) => { + let file = Box::new(open_log_file(&p)?); + let metadata = file.metadata().with_context(|| "Failed to get metadata")?; + let mod_time = metadata + .modified() + .with_context(|| "Failed to get modify time")?; + let seconds = mod_time + .duration_since(UNIX_EPOCH) + .with_context(|| "Failed to get duration time")? + .as_secs(); + let log_size = Wrapping(metadata.len() as usize); + let created_day = formatted_time(seconds as i64)[2]; + (file as Box, log_size, created_day) + } + None => (Box::new(stderr()) as Box, Wrapping(0), 0), + }; + + let rotate = Mutex::new(LogRotate { + handler: log_file, + path: path + .as_ref() + .unwrap_or(&PathBuf::new()) + .to_string_lossy() + .to_string(), + size: log_size, + created_day, + }); + Ok(Self { rotate, level }) + } +} + +impl Log for Logger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= self.level + } + + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + + let fmt_msg = format_args!( + "{:<5}: [{}][{}][{}: {}]:{}: {}\n", + formatted_now(), + getpid(), + gettid(), + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.level(), + record.args() + ) + .to_string(); + + let mut log_rotate = self.rotate.lock().unwrap(); + if let Err(e) = log_rotate.handler.write_all(fmt_msg.as_bytes()) { + eprintln!("Failed to log message: {:?}", e); + return; + } + if let Err(e) = log_rotate.rotate(fmt_msg.as_bytes().len()) { + eprintln!("Failed to rotate log files: {:?}", e); + } + } + + fn flush(&self) {} +} + +pub fn init(path: &Option, debug: bool) -> Result<()> { + let log_level = if debug { + Level::Debug + } else { + match std::env::var("OZONEC_LOG_LEVEL") { + Ok(level) => match level.to_lowercase().as_str() { + "error" => Level::Error, + "warn" => Level::Warn, + "info" => Level::Info, + "debug" => Level::Debug, + "trace" => Level::Trace, + _ => Level::Info, + }, + _ => Level::Info, + } + }; + + let logger = Box::new(Logger::new(path, log_level)?); + set_boxed_logger(logger) + .map(|_| set_max_level(LevelFilter::Trace)) + .with_context(|| "Logger has been already set")?; + Ok(()) +} diff --git a/ozonec/src/utils/mod.rs b/ozonec/src/utils/mod.rs new file mode 100644 index 000000000..3415cf8e8 --- /dev/null +++ b/ozonec/src/utils/mod.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod logger; -- Gitee From ca38c14f4a7cd50d0283607a78bc1ea69f267b14 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 15 Aug 2024 19:00:17 +0800 Subject: [PATCH 2081/2187] ozonec: Add crate oci_spec to interact with OCI specification --- ozonec/Cargo.toml | 1 + ozonec/oci_spec/Cargo.toml | 19 +++++++++++++++++++ ozonec/oci_spec/src/lib.rs | 17 +++++++++++++++++ ozonec/oci_spec/src/linux.rs | 11 +++++++++++ ozonec/oci_spec/src/posix.rs | 11 +++++++++++ ozonec/oci_spec/src/runtime.rs | 11 +++++++++++ ozonec/oci_spec/src/state.rs | 11 +++++++++++ ozonec/oci_spec/src/vm.rs | 11 +++++++++++ 8 files changed, 92 insertions(+) create mode 100644 ozonec/oci_spec/Cargo.toml create mode 100644 ozonec/oci_spec/src/lib.rs create mode 100644 ozonec/oci_spec/src/linux.rs create mode 100644 ozonec/oci_spec/src/posix.rs create mode 100644 ozonec/oci_spec/src/runtime.rs create mode 100644 ozonec/oci_spec/src/state.rs create mode 100644 ozonec/oci_spec/src/vm.rs diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index d853ab0ab..8cd9de1b1 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -12,6 +12,7 @@ clap = { version = "= 4.1.4", default-features = false, features = ["derive", "c libc = "= 0.2.146" log = { version = "= 0.4.18", features = ["std"]} nix = "= 0.26.2" +oci_spec = { path = "oci_spec" } [workspace] diff --git a/ozonec/oci_spec/Cargo.toml b/ozonec/oci_spec/Cargo.toml new file mode 100644 index 000000000..c9d2cbe92 --- /dev/null +++ b/ozonec/oci_spec/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "oci_spec" +version = "0.1.0" +authors = ["Huawei StratoVirt Team"] +edition = "2021" +license = "Mulan PSL v2" +description = "Open Container Initiative (OCI) Specifications in Rust" + +[dependencies] + +[profile.dev] +panic = "unwind" + +[profile.release] +lto = true +strip = true +opt-level = 'z' +codegen-units = 1 +panic = "abort" \ No newline at end of file diff --git a/ozonec/oci_spec/src/lib.rs b/ozonec/oci_spec/src/lib.rs new file mode 100644 index 000000000..c7b44ff05 --- /dev/null +++ b/ozonec/oci_spec/src/lib.rs @@ -0,0 +1,17 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub mod linux; +pub mod posix; +pub mod runtime; +pub mod state; +pub mod vm; diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs new file mode 100644 index 000000000..4524f4b26 --- /dev/null +++ b/ozonec/oci_spec/src/linux.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. diff --git a/ozonec/oci_spec/src/posix.rs b/ozonec/oci_spec/src/posix.rs new file mode 100644 index 000000000..4524f4b26 --- /dev/null +++ b/ozonec/oci_spec/src/posix.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. diff --git a/ozonec/oci_spec/src/runtime.rs b/ozonec/oci_spec/src/runtime.rs new file mode 100644 index 000000000..4524f4b26 --- /dev/null +++ b/ozonec/oci_spec/src/runtime.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. diff --git a/ozonec/oci_spec/src/state.rs b/ozonec/oci_spec/src/state.rs new file mode 100644 index 000000000..4524f4b26 --- /dev/null +++ b/ozonec/oci_spec/src/state.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. diff --git a/ozonec/oci_spec/src/vm.rs b/ozonec/oci_spec/src/vm.rs new file mode 100644 index 000000000..4524f4b26 --- /dev/null +++ b/ozonec/oci_spec/src/vm.rs @@ -0,0 +1,11 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. -- Gitee From 55d15ba6fc511289226d97cefc02bcccdbdb7657 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 16 Aug 2024 09:57:00 +0800 Subject: [PATCH 2082/2187] oci_spec/linux: Add namespace support --- ozonec/oci_spec/Cargo.toml | 4 + ozonec/oci_spec/src/linux.rs | 195 +++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) diff --git a/ozonec/oci_spec/Cargo.toml b/ozonec/oci_spec/Cargo.toml index c9d2cbe92..b18928f40 100644 --- a/ozonec/oci_spec/Cargo.toml +++ b/ozonec/oci_spec/Cargo.toml @@ -7,6 +7,10 @@ license = "Mulan PSL v2" description = "Open Container Initiative (OCI) Specifications in Rust" [dependencies] +anyhow = "= 1.0.71" +nix = "= 0.26.2" +serde = { version = "= 1.0.163", features = ["derive"] } +serde_json = "= 1.0.96" [profile.dev] panic = "unwind" diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 4524f4b26..0cb696f77 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -9,3 +9,198 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + +use std::path::PathBuf; + +use anyhow::{anyhow, Result}; +use nix::sched::CloneFlags; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, Hash)] +#[serde(rename_all = "snake_case")] +/// Available Linux namespaces. +pub enum NamespaceType { + Cgroup = 0x0200_0000, + Ipc = 0x0800_0000, + Network = 0x4000_0000, + Mount = 0x0002_0000, + Pid = 0x2000_0000, + Time = 0x0000_0080, + User = 0x1000_0000, + Uts = 0x0400_0000, +} + +impl TryInto for NamespaceType { + type Error = anyhow::Error; + + fn try_into(self) -> Result { + match self { + NamespaceType::Cgroup => Ok(CloneFlags::CLONE_NEWCGROUP), + NamespaceType::Ipc => Ok(CloneFlags::CLONE_NEWIPC), + NamespaceType::Network => Ok(CloneFlags::CLONE_NEWNET), + NamespaceType::Mount => Ok(CloneFlags::CLONE_NEWNS), + NamespaceType::Pid => Ok(CloneFlags::CLONE_NEWPID), + NamespaceType::Time => Err(anyhow!("Time namespace not supported with clone")), + NamespaceType::User => Ok(CloneFlags::CLONE_NEWUSER), + NamespaceType::Uts => Ok(CloneFlags::CLONE_NEWUTS), + } + } +} + +impl From for String { + fn from(ns_type: NamespaceType) -> Self { + match ns_type { + NamespaceType::Cgroup => String::from("cgroup"), + NamespaceType::Ipc => String::from("ipc"), + NamespaceType::Network => String::from("net"), + NamespaceType::Mount => String::from("mnt"), + NamespaceType::Pid => String::from("pid"), + NamespaceType::Time => String::from("time"), + NamespaceType::User => String::from("user"), + NamespaceType::Uts => String::from("uts"), + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Namespaces. +pub struct Namespace { + /// Namespace type. + #[serde(rename = "type")] + pub ns_type: NamespaceType, + /// Namespace file. If path is not specified, a new namespace is created. + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// User namespace mappings. +pub struct IdMapping { + /// Starting uid/gid in the container. + pub containerID: u32, + /// Starting uid/gid on the host to be mapped to containerID. + pub hostID: u32, + /// Number of ids to be mapped. + pub size: u32, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Offset for Time Namespace. +pub struct TimeOffsets { + #[serde(skip_serializing_if = "Option::is_none")] + /// Offset of clock (in seconds) in the container. + pub secs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Offset of clock (in nanoseconds) in the container. + pub nanosecs: Option, +} + +#[cfg(test)] +mod tests { + use serde_json; + + use super::*; + + #[test] + fn test_namespaces() { + let json = r#"{ + "namespaces": [ + { + "type": "pid", + "path": "/proc/1234/ns/pid" + }, + { + "type": "network", + "path": "/var/run/netns/neta" + }, + { + "type": "mount" + }, + { + "type": "ipc" + }, + { + "type": "uts" + }, + { + "type": "user" + }, + { + "type": "cgroup" + }, + { + "type": "time" + } + ] + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + namespaces: Vec, + } + + let ns: Section = serde_json::from_str(json).unwrap(); + assert_eq!(ns.namespaces.len(), 8); + assert_eq!(ns.namespaces[0].ns_type, NamespaceType::Pid); + assert_eq!(ns.namespaces[1].ns_type, NamespaceType::Network); + assert_eq!(ns.namespaces[2].ns_type, NamespaceType::Mount); + assert_eq!(ns.namespaces[3].ns_type, NamespaceType::Ipc); + assert_eq!(ns.namespaces[4].ns_type, NamespaceType::Uts); + assert_eq!(ns.namespaces[5].ns_type, NamespaceType::User); + assert_eq!(ns.namespaces[6].ns_type, NamespaceType::Cgroup); + assert_eq!(ns.namespaces[7].ns_type, NamespaceType::Time); + } + + #[test] + fn test_ids_mapping() { + let json = r#"{ + "uidMappings": [ + { + "containerID": 0, + "hostID": 1000, + "size": 32000 + } + ], + "gidMappings": [ + { + "containerID": 0, + "hostID": 1000, + "size": 32000 + } + ] + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + uidMappings: Vec, + gidMappings: Vec, + } + + let ids_mapping: Section = serde_json::from_str(json).unwrap(); + assert_eq!(ids_mapping.uidMappings.len(), 1); + assert_eq!(ids_mapping.uidMappings[0].size, 32000 as u32); + assert_eq!(ids_mapping.gidMappings.len(), 1); + assert_eq!(ids_mapping.gidMappings[0].size, 32000 as u32); + } + + #[test] + fn test_time_offsets() { + let json = r#"{ + "timeOffsets": { + "secs": 100 + } + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + timeOffsets: TimeOffsets, + } + + let time_offsets: Section = serde_json::from_str(json).unwrap(); + assert_eq!(time_offsets.timeOffsets.secs, Some(100)); + assert_eq!(time_offsets.timeOffsets.nanosecs, None); + } +} -- Gitee From 6ae25e0243bde003bd6a6e196eae48660b2aaffa Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 16 Aug 2024 11:18:22 +0800 Subject: [PATCH 2083/2187] oci_spec/linux: Add devices support --- ozonec/oci_spec/src/linux.rs | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 0cb696f77..1db46a939 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -96,6 +96,32 @@ pub struct TimeOffsets { pub nanosecs: Option, } +/// Devices available in the container. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Device { + /// Type of device. + #[serde(rename = "type")] + pub dev_type: String, + /// Full path to device inside container. + pub path: String, + /// Major number for the device. + #[serde(skip_serializing_if = "Option::is_none")] + pub major: Option, + /// Minor number for the device. + #[serde(skip_serializing_if = "Option::is_none")] + pub minor: Option, + /// File mode for the device. + #[serde(skip_serializing_if = "Option::is_none")] + pub fileMode: Option, + /// Id of device owner. + #[serde(skip_serializing_if = "Option::is_none")] + pub uid: Option, + /// Id of device group. + #[serde(skip_serializing_if = "Option::is_none")] + pub gid: Option, +} + #[cfg(test)] mod tests { use serde_json; @@ -203,4 +229,42 @@ mod tests { assert_eq!(time_offsets.timeOffsets.secs, Some(100)); assert_eq!(time_offsets.timeOffsets.nanosecs, None); } + + #[test] + fn test_devices() { + let json = r#"{ + "devices": [ + { + "path": "/dev/fuse", + "type": "c", + "major": 10, + "minor": 229, + "fileMode": 438, + "uid": 0, + "gid": 0 + }, + { + "path": "/dev/sda", + "type": "b", + "major": 8, + "minor": 0 + } + ] + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + devices: Vec, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.devices.len(), 2); + assert_eq!(section.devices[1].path, "/dev/sda"); + assert_eq!(section.devices[1].dev_type, "b"); + assert_eq!(section.devices[1].major, Some(8)); + assert_eq!(section.devices[1].minor, Some(0)); + assert_eq!(section.devices[1].fileMode, None); + assert_eq!(section.devices[1].uid, None); + assert_eq!(section.devices[1].gid, None); + } } -- Gitee From af23f3a6615c06bb4b90db308b56f0c7b37fc82b Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 16 Aug 2024 15:24:22 +0800 Subject: [PATCH 2084/2187] oci_spec/linux: Add cgroup support --- ozonec/oci_spec/src/linux.rs | 250 +++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 1db46a939..00f845f47 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -122,6 +122,256 @@ pub struct Device { pub gid: Option, } +fn default_device_type() -> String { + "a".to_string() +} + +/// Allowed device in Device Cgroup. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CgroupDevice { + /// Whether the entry is allowed or denied. + pub allow: bool, + /// Type of device. + #[serde(default = "default_device_type", rename = "type")] + pub dev_type: String, + /// Major number for the device. + #[serde(skip_serializing_if = "Option::is_none")] + pub major: Option, + /// Minor number for the device. + #[serde(skip_serializing_if = "Option::is_none")] + pub minor: Option, + /// Cgroup permissions for device. + #[serde(skip_serializing_if = "Option::is_none")] + pub access: Option, +} + +/// Cgroup subsystem to set limits on the container's memory usage. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct MemoryCgroup { + /// Limit of memory usage. + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + /// Soft limit of memory usage. + #[serde(skip_serializing_if = "Option::is_none")] + pub reservation: Option, + /// Limits of memory +Swap usage. + #[serde(skip_serializing_if = "Option::is_none")] + pub swap: Option, + /// Hard limit for kernel memory. + #[serde(skip_serializing_if = "Option::is_none")] + pub kernel: Option, + /// Hard limit for kernel TCP buffer memory. + #[serde(skip_serializing_if = "Option::is_none")] + pub kernelTCP: Option, + /// Swappiness parameter of vmscan. + #[serde(skip_serializing_if = "Option::is_none")] + pub swappiness: Option, + /// Enable or disable the OOM killer. + #[serde(skip_serializing_if = "Option::is_none")] + pub disableOOMKiller: Option, + /// Enable or disable hierarchical memory accounting. + #[serde(skip_serializing_if = "Option::is_none")] + pub useHierarchy: Option, + /// Enable container memory usage check before setting a new limit. + #[serde(skip_serializing_if = "Option::is_none")] + pub checkBeforeUpdate: Option, +} + +/// Cgroup subsystems cpu and cpusets. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CpuCgroup { + /// Relative share of CPU time available to the tasks in a cgroup. + #[serde(skip_serializing_if = "Option::is_none")] + pub shares: Option, + /// Total amount of time in microseconds for which all tasks in a + /// cgroup can run during one period. + #[serde(skip_serializing_if = "Option::is_none")] + pub quota: Option, + /// Maximum amount of accumulated time in microseconds for which + /// all tasks in a cgroup can run additionally for burst during + /// one period. + #[serde(skip_serializing_if = "Option::is_none")] + pub burst: Option, + /// Period of time in microseconds for how regularly a cgroup's access + /// to CPU resources should be reallocated (CFS scheduler only) + #[serde(skip_serializing_if = "Option::is_none")] + pub period: Option, + /// Period of time in microseconds for the longest continuous period + /// in which the tasks in a cgrouop have access to CPU resources. + #[serde(skip_serializing_if = "Option::is_none")] + pub realtimeRuntime: Option, + /// Same as period but applies to realtime scheduler only. + #[serde(skip_serializing_if = "Option::is_none")] + pub realtimePeriod: Option, + /// List of CPUs the container will run on. + #[serde(skip_serializing_if = "Option::is_none")] + pub cpus: Option, + /// List of memory nodes the container will run on. + #[serde(skip_serializing_if = "Option::is_none")] + pub mems: Option, + /// Cgroups are configured with minimum weight. + #[serde(skip_serializing_if = "Option::is_none")] + pub idle: Option, +} + +/// Per-device bandwidth weights. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct WeightDevice { + /// Major number for device. + pub major: i64, + /// Minor number for device. + pub minor: i64, + /// Bandwidth weight for the device. + #[serde(skip_serializing_if = "Option::is_none")] + pub weight: Option, + /// Bandwidth weight for the device while competing with the cgroup's + /// child cgroups (CFS scheduler only) + #[serde(skip_serializing_if = "Option::is_none")] + pub leafWeight: Option, +} + +/// Per-device bandwidth rate limits. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ThrottleDevice { + /// Major number for device. + pub major: i64, + /// Minor number for device. + pub minor: i64, + /// Bandwidth rate limit in bytes per second or IO rate limit for + /// the device. + pub rate: u64, +} + +/// Cgroup subsystem blkio which implements the block IO controller. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BlockIoCgroup { + /// Per-cgroup weight. + #[serde(skip_serializing_if = "Option::is_none")] + pub weight: Option, + /// Equivalents of weight for the purpose of deciding how much + /// weight tasks in the given cgroup has while competing with + /// the cgroup's child cgroups. + #[serde(skip_serializing_if = "Option::is_none")] + pub leafWeight: Option, + /// Array of per-device bandwidth weights. + #[serde(skip_serializing_if = "Option::is_none")] + pub weightDevice: Option>, + /// Array of per-device read bandwidth rate limits. + #[serde(skip_serializing_if = "Option::is_none")] + pub throttleReadBpsDevice: Option>, + /// Array of per-device write bandwidth rate limits. + #[serde(skip_serializing_if = "Option::is_none")] + pub throttleWriteBpsDevice: Option>, + /// Array of per-device read IO rate limits. + #[serde(skip_serializing_if = "Option::is_none")] + pub throttleReadIOPSDevice: Option>, + /// Array of per-device write IO rate limits. + #[serde(skip_serializing_if = "Option::is_none")] + pub throttleWriteIOPSDevice: Option>, +} + +/// hugetlb controller which allows to limit the HugeTLB reservations +/// (if supported) or usage (page fault). +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct HugetlbCgroup { + /// Hugepage size + pub pageSize: String, + /// Limit in bytes of hugepagesize HugeTLB reservations + /// (if supported) or usage. + pub limit: u64, +} + +/// Priority assigned to traffic originating from processes in the +/// group and egressing the system on various interfaces. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NetPriority { + /// Interface name. + pub name: String, + /// Priority applied to the interface. + pub priority: u32, +} + +/// Cgroup subsystems net_cls and net_prio. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NetworkCgroup { + /// Network class identifier the cgroup's network packets will + /// be tagged with. + #[serde(skip_serializing_if = "Option::is_none")] + pub classID: Option, + /// List of objects of the priorities assigned to traffic + /// originating from processes in the group and egressing the + /// system on various interfaces. + #[serde(skip_serializing_if = "Option::is_none")] + pub priorities: Option>, +} + +/// Cgroup subsystem pids. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PidsCgroup { + /// Maximum number of tasks in the cgroup. + pub limit: i64, +} + +/// Per-device rdma limit. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RdmaLimit { + /// Maximum number of hca_handles in the cgroup. + pub hcaHandles: Option, + /// Maximum number of hca_objects in the cgroup. + pub hcaObjects: Option, +} + +/// Cgroup subsystem rdma. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RdmaCgroup { + /// Rdma limit for mlx5_1. + #[serde(skip_serializing_if = "Option::is_none")] + pub mlx5_1: Option, + /// Rdma limit for mlx4_0. + #[serde(skip_serializing_if = "Option::is_none")] + pub mlx4_0: Option, + /// Rdma limit for rxe3. + #[serde(skip_serializing_if = "Option::is_none")] + pub rxe3: Option, +} + +/// Cgroups to restrict resource usage for a container and +/// handle device access. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Cgroups { + /// Device cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub devices: Option>, + /// Memory cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub memory: Option, + /// Cpu and Cpuset cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub cpu: Option, + /// Blkio cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub blockIO: Option, + /// Hugetlb cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub hugepageLimits: Option>, + /// Network cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub network: Option, + /// Pids cgroup settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub pids: Option, +} + #[cfg(test)] mod tests { use serde_json; -- Gitee From fbb2d72528e76446b5a935865d308ff811c01b73 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 16 Aug 2024 16:41:17 +0800 Subject: [PATCH 2085/2187] ozonec/linux: Add unit testcases for cpu/memory/blkio/device cgroup --- ozonec/oci_spec/src/linux.rs | 215 ++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 1 deletion(-) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 00f845f47..bffd1e3af 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -235,7 +235,7 @@ pub struct WeightDevice { /// Per-device bandwidth rate limits. #[allow(non_snake_case)] -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct ThrottleDevice { /// Major number for device. pub major: i64, @@ -517,4 +517,217 @@ mod tests { assert_eq!(section.devices[1].uid, None); assert_eq!(section.devices[1].gid, None); } + + #[test] + fn test_cgroup_devices() { + let json = r#"{ + "devices": [ + { + "allow": false + }, + { + "allow": true, + "type": "c", + "major": 10, + "minor": 229, + "access": "rw" + } + ] + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + devices: Vec, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.devices.len(), 2); + assert_eq!(section.devices[0].allow, false); + assert_eq!(section.devices[0].dev_type, "a"); + assert_eq!(section.devices[0].major, None); + assert_eq!(section.devices[0].minor, None); + assert_eq!(section.devices[0].access, None); + assert_eq!(section.devices[1].allow, true); + assert_eq!(section.devices[1].dev_type, "c"); + assert_eq!(section.devices[1].major, Some(10)); + assert_eq!(section.devices[1].minor, Some(229)); + assert_eq!(section.devices[1].access, Some("rw".to_string())); + } + + #[test] + fn test_cgroup_memory_01() { + let json = r#"{ + "memory": { + "limit": 536870912, + "reservation": 536870912, + "swap": 536870912, + "kernel": -1, + "kernelTCP": -1, + "swappiness": 0, + "disableOOMKiller": false + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + memory: MemoryCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.memory.limit, Some(536870912)); + assert_eq!(section.memory.reservation, Some(536870912)); + assert_eq!(section.memory.swap, Some(536870912)); + assert_eq!(section.memory.kernel, Some(-1)); + assert_eq!(section.memory.kernelTCP, Some(-1)); + assert_eq!(section.memory.swappiness, Some(0)); + assert_eq!(section.memory.disableOOMKiller, Some(false)); + assert_eq!(section.memory.useHierarchy, None); + assert_eq!(section.memory.checkBeforeUpdate, None); + } + + #[test] + fn test_cgroup_memory_02() { + let json = r#"{ + "memory": { + "useHierarchy": true, + "checkBeforeUpdate": true + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + memory: MemoryCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.memory.limit, None); + assert_eq!(section.memory.reservation, None); + assert_eq!(section.memory.swap, None); + assert_eq!(section.memory.kernel, None); + assert_eq!(section.memory.kernelTCP, None); + assert_eq!(section.memory.swappiness, None); + assert_eq!(section.memory.disableOOMKiller, None); + assert_eq!(section.memory.useHierarchy, Some(true)); + assert_eq!(section.memory.checkBeforeUpdate, Some(true)); + } + + #[test] + fn test_cgroup_cpu_01() { + let json = r#"{ + "cpu": { + "shares": 1024, + "quota": 1000000, + "burst": 1000000, + "period": 500000, + "realtimeRuntime": 950000, + "realtimePeriod": 1000000, + "cpus": "2-3", + "mems": "0-7", + "idle": 0 + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + cpu: CpuCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.cpu.shares, Some(1024)); + assert_eq!(section.cpu.quota, Some(1000000)); + assert_eq!(section.cpu.burst, Some(1000000)); + assert_eq!(section.cpu.period, Some(500000)); + assert_eq!(section.cpu.realtimeRuntime, Some(950000)); + assert_eq!(section.cpu.realtimePeriod, Some(1000000)); + assert_eq!(section.cpu.cpus, Some("2-3".to_string())); + assert_eq!(section.cpu.mems, Some("0-7".to_string())); + assert_eq!(section.cpu.idle, Some(0)); + } + + #[test] + fn test_cgroup_cpu_02() { + let json = r#"{ + "cpu": {} + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + cpu: CpuCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.cpu.shares, None); + assert_eq!(section.cpu.quota, None); + assert_eq!(section.cpu.burst, None); + assert_eq!(section.cpu.period, None); + assert_eq!(section.cpu.realtimeRuntime, None); + assert_eq!(section.cpu.realtimePeriod, None); + assert_eq!(section.cpu.cpus, None); + assert_eq!(section.cpu.mems, None); + assert_eq!(section.cpu.idle, None); + } + + #[test] + fn test_cgroup_blkio() { + let json = r#"{ + "blockIO": { + "weight": 10, + "leafWeight": 10, + "weightDevice": [ + { + "major": 8, + "minor": 0, + "weight": 500, + "leafWeight": 300 + }, + { + "major": 8, + "minor": 16 + } + ], + "throttleReadBpsDevice": [ + { + "major": 8, + "minor": 0, + "rate": 600 + }, + { + "major": 8, + "minor": 16, + "rate": 300 + } + ] + } + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + blockIO: BlockIoCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.blockIO.weight, Some(10)); + assert_eq!(section.blockIO.leafWeight, Some(10)); + assert_eq!(section.blockIO.throttleReadIOPSDevice, None); + assert_eq!(section.blockIO.throttleWriteBpsDevice, None); + assert_eq!(section.blockIO.throttleWriteIOPSDevice, None); + + let weight_device = section.blockIO.weightDevice.as_ref().unwrap(); + assert_eq!(weight_device.len(), 2); + assert_eq!(weight_device[0].major, 8); + assert_eq!(weight_device[0].minor, 0); + assert_eq!(weight_device[0].weight, Some(500)); + assert_eq!(weight_device[0].leafWeight, Some(300)); + assert_eq!(weight_device[1].major, 8); + assert_eq!(weight_device[1].minor, 16); + assert_eq!(weight_device[1].weight, None); + assert_eq!(weight_device[1].leafWeight, None); + + let throttle = section.blockIO.throttleReadBpsDevice.as_ref().unwrap(); + assert_eq!(throttle.len(), 2); + assert_eq!(throttle[1].major, 8); + assert_eq!(throttle[1].minor, 16); + assert_eq!(throttle[1].rate, 300); + } } -- Gitee From 7780021d29d74d68623ae9d3b3b37c3019d4fd7b Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 16 Aug 2024 17:41:13 +0800 Subject: [PATCH 2086/2187] ozonec/linux: Add unit testcases for hugetlb/network/rdma/pid cgroups --- ozonec/oci_spec/src/linux.rs | 117 ++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index bffd1e3af..236d0e844 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -289,7 +289,7 @@ pub struct HugetlbCgroup { /// Priority assigned to traffic originating from processes in the /// group and egressing the system on various interfaces. -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct NetPriority { /// Interface name. pub name: String, @@ -730,4 +730,119 @@ mod tests { assert_eq!(throttle[1].minor, 16); assert_eq!(throttle[1].rate, 300); } + + #[test] + fn test_cgroup_hugetlb() { + let json = r#"{ + "hugepageLimits": [ + { + "pageSize": "2MB", + "limit": 209715200 + } + ] + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + hugepageLimits: Vec, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.hugepageLimits[0].pageSize, "2MB"); + assert_eq!(section.hugepageLimits[0].limit, 209715200); + } + + #[test] + fn test_cgroup_network_01() { + let json = r#"{ + "network": { + "classID": 1048577, + "priorities": [ + { + "name": "eth0", + "priority": 500 + } + ] + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + network: NetworkCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.network.classID, Some(1048577)); + let priorities = section.network.priorities.as_ref().unwrap(); + assert_eq!(priorities[0].name, "eth0"); + assert_eq!(priorities[0].priority, 500); + } + + #[test] + fn test_cgroup_network_02() { + let json = r#"{ + "network": {} + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + network: NetworkCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.network.classID, None); + assert_eq!(section.network.priorities, None); + } + + #[test] + fn test_cgroup_pid() { + let json = r#"{ + "pids": { + "limit": 32771 + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + pids: PidsCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.pids.limit, 32771); + } + + #[test] + fn test_cgroup_rdma() { + let json = r#"{ + "rdma": { + "mlx5_1": { + "hcaHandles": 3, + "hcaObjects": 10000 + }, + "mlx4_0": { + "hcaObjects": 1000 + }, + "rxe3": { + "hcaHandles": 10000 + } + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + rdma: RdmaCgroup, + } + + let section: Section = serde_json::from_str(json).unwrap(); + let rdma_limit = section.rdma.mlx5_1.as_ref().unwrap(); + assert_eq!(rdma_limit.hcaHandles, Some(3)); + assert_eq!(rdma_limit.hcaObjects, Some(10000)); + let rdma_limit = section.rdma.mlx4_0.as_ref().unwrap(); + assert_eq!(rdma_limit.hcaHandles, None); + assert_eq!(rdma_limit.hcaObjects, Some(1000)); + let rdma_limit = section.rdma.rxe3.as_ref().unwrap(); + assert_eq!(rdma_limit.hcaHandles, Some(10000)); + assert_eq!(rdma_limit.hcaObjects, None); + } } -- Gitee From bf03ce70d12c45adf48a9c517b5512627789fa3a Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 16 Aug 2024 17:56:03 +0800 Subject: [PATCH 2087/2187] ozonec/linux: Add IntelRdt support --- ozonec/oci_spec/src/linux.rs | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 236d0e844..5d5c2458c 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -372,6 +372,28 @@ pub struct Cgroups { pub pids: Option, } +#[cfg(target_arch = "x86_64")] +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Intel Resource Director Technology +pub struct IntelRdt { + #[serde(skip_serializing_if = "Option::is_none")] + /// Identity for RDT Class of Service (CLOS). + pub closID: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Schema for L3 cache id and capacity bitmask (CBM). + pub l3CacheSchema: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Schema of memory bandwidth per L3 cache id. + pub memBwSchema: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// If Intel RDT CMT should be enabled. + pub enableCMT: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// If Intel RDT MBM should be enabled. + pub enableMBM: Option, +} + #[cfg(test)] mod tests { use serde_json; @@ -845,4 +867,40 @@ mod tests { assert_eq!(rdma_limit.hcaHandles, Some(10000)); assert_eq!(rdma_limit.hcaObjects, None); } + + #[cfg(target_arch = "x86_64")] + #[test] + fn test_intel_rdt() { + let json = r#"{ + "intelRdt": { + "closID": "guaranteed_group", + "l3CacheSchema": "L3:0=7f0;1=1f", + "memBwSchema": "MB:0=20;1=70", + "enableCMT": true, + "enableMBM": true + } + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + intelRdt: IntelRdt, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!( + section.intelRdt.closID, + Some("guaranteed_group".to_string()) + ); + assert_eq!( + section.intelRdt.l3CacheSchema, + Some("L3:0=7f0;1=1f".to_string()) + ); + assert_eq!( + section.intelRdt.memBwSchema, + Some("MB:0=20;1=70".to_string()) + ); + assert_eq!(section.intelRdt.enableCMT, Some(true)); + assert_eq!(section.intelRdt.enableMBM, Some(true)); + } } -- Gitee From 9897d7fbdd65b00aea645b52ed1daed6a1606f9f Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 18 Aug 2024 10:42:21 +0800 Subject: [PATCH 2088/2187] ozonec/linux: Add seccomp support --- ozonec/oci_spec/src/linux.rs | 131 +++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 5d5c2458c..88078d1c1 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -394,6 +394,98 @@ pub struct IntelRdt { pub enableMBM: Option, } +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[repr(u32)] +/// Action for seccomp rules. +pub enum SeccompAction { + ScmpActKill = 0x0000_0000, + ScmpActKillProcess = 0x8000_0000, + ScmpActTrap = 0x0003_0000, + ScmpActErrno = 0x0005_0001, + ScmpActNotify = 0x7fc0_0000, + ScmpActTrace = 0x7ff0_0001, + ScmpActLog = 0x7ffc_0000, + ScmpActAllow = 0x7fff_0000, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[repr(u32)] +/// Operator for syscall arguments in seccomp. +pub enum SeccompOp { + ScmpCmpNe = 1, + ScmpCmpLt = 2, + ScmpCmpLe = 3, + #[default] + ScmpCmpEq = 4, + ScmpCmpGe = 5, + ScmpCmpGt = 6, + ScmpCmpMaskedEq = 7, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// The specific syscall in seccomp. +pub struct SeccompSyscallArg { + /// Index for syscall arguments. + pub index: usize, + /// Value for syscall arguments. + pub value: u64, + #[serde(skip_serializing_if = "Option::is_none")] + /// Value for syscall arguments. + pub valueTwo: Option, + /// Operator for syscall arguments. + pub op: SeccompOp, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Match a syscall in seccomp. +pub struct SeccompSyscall { + /// Names of the syscalls. + pub names: Vec, + /// Action for seccomp rules. + pub action: SeccompAction, + #[serde(skip_serializing_if = "Option::is_none")] + /// Errno return code to use. + pub errnoRet: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Specific syscall in seccomp. + pub args: Option>, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Seccomp provides application sandboxing mechanism in the Linux kernel. +pub struct Seccomp { + /// Default action for seccomp. + pub defaultAction: SeccompAction, + #[serde(skip_serializing_if = "Option::is_none")] + /// Errno return code to use. + pub defaultErrnoRet: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Architecture used for system calls. + pub architectures: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// List of flags to use with seccomp. + pub flags: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Path of UNIX domain socket over which the runtime will send the + /// container process state data structure when the SCMP_ACT_NOTIFY + /// action is used. + pub listennerPath: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Seccomp file descriptor returned by the seccomp syscall. + pub seccompFd: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Opaque data to pass to the seccomp agent. + pub listenerMetadata: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Match a syscall in seccomp. + pub syscalls: Option>, +} + #[cfg(test)] mod tests { use serde_json; @@ -903,4 +995,43 @@ mod tests { assert_eq!(section.intelRdt.enableCMT, Some(true)); assert_eq!(section.intelRdt.enableMBM, Some(true)); } + + #[test] + fn test_seccomp() { + let json = r#"{ + "seccomp": { + "defaultAction": "SCMP_ACT_ALLOW", + "architectures": [ + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ], + "syscalls": [ + { + "names": [ + "getcwd", + "chmod" + ], + "action": "SCMP_ACT_ERRNO" + } + ] + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + seccomp: Seccomp, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.seccomp.defaultAction, SeccompAction::ScmpActAllow); + let architectures = section.seccomp.architectures.as_ref().unwrap(); + assert_eq!(architectures.len(), 2); + assert_eq!(architectures[0], "SCMP_ARCH_X86"); + assert_eq!(architectures[1], "SCMP_ARCH_X32"); + let syscall_names = section.seccomp.syscalls.as_ref().unwrap(); + assert_eq!(syscall_names[0].names.len(), 2); + assert_eq!(syscall_names[0].names[0], "getcwd"); + assert_eq!(syscall_names[0].names[1], "chmod"); + assert_eq!(syscall_names[0].action, SeccompAction::ScmpActErrno); + } } -- Gitee From 6d20966fec5236cf90eaffab0adad5c70c92c14f Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 18 Aug 2024 11:33:59 +0800 Subject: [PATCH 2089/2187] ozonec/linux: Add LinuxPlatform structure for Linux specific configuration --- ozonec/oci_spec/src/linux.rs | 90 +++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 88078d1c1..5fa0af26e 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use anyhow::{anyhow, Result}; use nix::sched::CloneFlags; @@ -486,6 +486,76 @@ pub struct Seccomp { pub syscalls: Option>, } +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Linux execution personality. +pub struct Personality { + /// Execution domain. + pub domain: String, + /// Additional flags to apply. + pub flags: Option>, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Linux-specific configuration. +pub struct LinuxPlatform { + /// A namespace wraps a global system resource in an abstraction. + pub namespaces: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + /// User namespace uid mappings from the host to the container. + pub uidMappings: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// User namespace gid mappings from the host to the container. + pub gidMappings: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Offset for Time Namespace. + pub timeOffsets: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Lists devices that MUST be available in the container. + pub devices: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Path to the cgroups. + pub cgroupsPath: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Rootfs's mount propagation. + pub rootfsPropagation: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Mask over the provided paths inside the container so + /// that they cannot be read. + pub maskedPaths: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Set the provided paths as readonly inside the container. + pub readonlyPaths: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Selinux context for the mounts in the container. + pub mountLabel: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Linux execution personality. + pub personality: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Configure a container's cgroups. + pub resources: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// The cgroup subsystem rdma. + pub rdma: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Allows cgroup v2 parameters to be to be set and modified + /// for the container. + pub unified: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Kernel parameters to be modified at runtime for the + /// container. + pub sysctl: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + /// Seccomp provides application sandboxing mechanism in + /// the Linux kernel. + pub seccomp: Option, + #[cfg(target_arch = "x86_64")] + #[serde(skip_serializing_if = "Option::is_none")] + /// Intel Resource Director Technology. + pub intelRdt: Option, +} + #[cfg(test)] mod tests { use serde_json; @@ -1034,4 +1104,22 @@ mod tests { assert_eq!(syscall_names[0].names[1], "chmod"); assert_eq!(syscall_names[0].action, SeccompAction::ScmpActErrno); } + + #[test] + fn test_personality() { + let json = r#"{ + "personality": { + "domain": "LINUX" + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + personality: Personality, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.personality.domain, "LINUX"); + assert_eq!(section.personality.flags, None); + } } -- Gitee From 7deaa49e05dbc2239fb4ed6e0da8c3201e04caec Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 18 Aug 2024 17:14:37 +0800 Subject: [PATCH 2090/2187] ozonec/oci_spec: Add process support --- ozonec/oci_spec/Cargo.toml | 1 + ozonec/oci_spec/src/lib.rs | 3 + ozonec/oci_spec/src/linux.rs | 104 ++++++++++++++++++++++++++++ ozonec/oci_spec/src/posix.rs | 43 ++++++++++++ ozonec/oci_spec/src/process.rs | 120 +++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+) create mode 100644 ozonec/oci_spec/src/process.rs diff --git a/ozonec/oci_spec/Cargo.toml b/ozonec/oci_spec/Cargo.toml index b18928f40..e5923ad52 100644 --- a/ozonec/oci_spec/Cargo.toml +++ b/ozonec/oci_spec/Cargo.toml @@ -8,6 +8,7 @@ description = "Open Container Initiative (OCI) Specifications in Rust" [dependencies] anyhow = "= 1.0.71" +libc = "= 0.2.146" nix = "= 0.26.2" serde = { version = "= 1.0.163", features = ["derive"] } serde_json = "= 1.0.96" diff --git a/ozonec/oci_spec/src/lib.rs b/ozonec/oci_spec/src/lib.rs index c7b44ff05..3f79329b0 100644 --- a/ozonec/oci_spec/src/lib.rs +++ b/ozonec/oci_spec/src/lib.rs @@ -10,8 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +#[cfg(target_os = "linux")] pub mod linux; +#[cfg(target_os = "linux")] pub mod posix; +pub mod process; pub mod runtime; pub mod state; pub mod vm; diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 5fa0af26e..234132ee7 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -556,6 +556,110 @@ pub struct LinuxPlatform { pub intelRdt: Option, } +/// Arrays that specifies the sets of capabilities for the process. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Capbilities { + /// Array of effective capabilities that are kept for the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub effective: Option>, + /// Array of bounding capabilities that are kept for the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub bounding: Option>, + /// Array of inheritable capabilities that are kept for the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub inheritable: Option>, + /// Array of permitted capabilities that are kept for the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub permitted: Option>, + /// Array of ambient capabilities that are kept for the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub ambient: Option>, +} + +/// Scheduling policy. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum SchedPolicy { + SchedOther, + SchedFifo, + SchedRr, + SchedBatch, + SchedIdle, +} + +impl From for libc::c_int { + fn from(value: SchedPolicy) -> Self { + match value { + SchedPolicy::SchedOther => libc::SCHED_OTHER, + SchedPolicy::SchedFifo => libc::SCHED_FIFO, + SchedPolicy::SchedRr => libc::SCHED_RR, + SchedPolicy::SchedBatch => libc::SCHED_BATCH, + SchedPolicy::SchedIdle => libc::SCHED_IDLE, + } + } +} + +/// Scheduler properties for the process. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Scheduler { + /// Scheduling policy. + pub policy: SchedPolicy, + /// Nice value for the process, affecting its priority. + #[serde(skip_serializing_if = "Option::is_none")] + pub nice: Option, + /// Static priority of the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub priority: Option, + /// Array of strings representing scheduling flags. + #[serde(skip_serializing_if = "Option::is_none")] + pub flags: Option>, + /// Amount of time in nanoseconds during which the process is + /// allowed to run in a given period, used by the deadline + /// scheduler. + #[serde(skip_serializing_if = "Option::is_none")] + pub runtime: Option, + /// Absolute deadline for the process to complete its execution, + /// used by the deadline scheduler. + #[serde(skip_serializing_if = "Option::is_none")] + pub deadline: Option, + /// Length of the period in nanoseconds used for determining the + /// process runtime, used by the deadline scheduler. + #[serde(skip_serializing_if = "Option::is_none")] + pub period: Option, +} + +/// I/O scheduling class. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum IoPriClass { + IoprioClassRt, + IoprioClassBe, + IoprioClassIdle, +} + +/// I/O priority settings for the container's processes within the +/// process group. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct IoPriority { + /// I/O scheduling class. + pub class: IoPriClass, + /// Priority level within the class. + pub priority: i64, +} + +/// CPU affinity used to execute the process. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ExecCpuAffinity { + /// List of CPUs a runtime parent process to be run on initially, + /// before the transition to container's cgroup. + #[serde(skip_serializing_if = "Option::is_none")] + pub initial: Option, + /// List of CPUs the process will be run on after the transition + /// to container's cgroup. + #[serde(skip_serializing_if = "Option::is_none", rename = "final")] + pub final_cpus: Option, +} + #[cfg(test)] mod tests { use serde_json; diff --git a/ozonec/oci_spec/src/posix.rs b/ozonec/oci_spec/src/posix.rs index 4524f4b26..03396e42d 100644 --- a/ozonec/oci_spec/src/posix.rs +++ b/ozonec/oci_spec/src/posix.rs @@ -9,3 +9,46 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + +use serde::{Deserialize, Serialize}; + +/// Container's root filesystem. +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct Root { + /// Path to the root filesystem for the container. + pub path: String, + #[serde(default)] + /// If true then the root filesystem MUST be read-only inside the container. + pub readonly: bool, +} + +/// Resource limits for the process. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Rlimits { + /// The platform resource being limited. + #[serde(rename = "type")] + pub rlimit_type: String, + /// Value of the limit enforced for the corresponding resource. + pub soft: u64, + /// Ceiling for the soft limit that could be set by an + /// unprivileged process. + pub hard: u64, +} + +/// The user for the process that allows specific control over which user +/// the process runs as. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct User { + /// User ID in the container namespace. + pub uid: u32, + /// Group ID in the container namespace. + pub gid: u32, + /// [umask][umask_2] of the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub umask: Option, + /// Additional group IDs in the container namespace to be added + /// to the process. + #[serde(skip_serializing_if = "Option::is_none")] + pub additionalGids: Option>, +} diff --git a/ozonec/oci_spec/src/process.rs b/ozonec/oci_spec/src/process.rs new file mode 100644 index 000000000..fc325812c --- /dev/null +++ b/ozonec/oci_spec/src/process.rs @@ -0,0 +1,120 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use serde::{Deserialize, Serialize}; + +use crate::{ + linux::{Capbilities, ExecCpuAffinity, IdMapping, IoPriority, Scheduler}, + posix::{Rlimits, User}, +}; + +/// Additional mounts beyond root. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Mount { + /// Destination of mount point: path inside container. + pub destination: String, + /// A device name, but can also be a file or directory name for bind mounts + /// or a dummy. + #[serde(skip_serializing_if = "Option::is_none")] + pub source: Option, + /// Mount options of the filesystem to be used. + #[serde(skip_serializing_if = "Option::is_none")] + pub options: Option>, + /// The type of the filesystem to be mounted. + #[serde(skip_serializing_if = "Option::is_none", rename = "type")] + pub fs_type: Option, + /// The mapping to convert UIDs from the source file system to the + /// destination mount point. + #[serde(skip_serializing_if = "Option::is_none")] + pub uidMappings: Option, + /// The mapping to convert GIDs from the source file system to the + /// destination mount point. + #[serde(skip_serializing_if = "Option::is_none")] + pub gidMappings: Option, +} + +/// Console size in characters of the terminal. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ConsoleSize { + /// Height size in characters. + #[serde(skip_serializing_if = "Option::is_none")] + pub height: Option, + /// Width size in characters. + #[serde(skip_serializing_if = "Option::is_none")] + pub width: Option, +} + +/// Container process. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Process { + /// Working directory that will be set for the executable. + pub cwd: String, + /// Similar semantics to IEEE Std 1003.1-2008 execvp's argv. + #[serde(skip_serializing_if = "Option::is_none")] + pub args: Option>, + /// Same semantics as IEEE Std 1003.1-2008's environ. + #[serde(skip_serializing_if = "Option::is_none")] + pub env: Option>, + /// Whether a terminal is attached to the process. + #[serde(default)] + pub terminal: bool, + /// Console size in characters of the terminal. + #[serde(skip_serializing_if = "Option::is_none")] + pub consoleSize: Option, + /// Full command line to be executed on Windows. + #[cfg(target_os = "windows")] + pub commandLine: Option, + /// Resource limits for the process. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub rlimits: Option>, + /// Name of the AppArmor profile for the process. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub apparmorProfile: Option, + /// Arrays that specifies the sets of capabilities for the process. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub capabilities: Option, + /// Setting noNewPrivileges to true prevents the process from + /// gaining additional privileges. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub noNewPrivileges: Option, + /// Oom-killer score in [pid]/oom_score_adj for the process's + /// [pid] in a proc pseudo-filesystem. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub oomScoreAdj: Option, + /// Scheduler properties for the process. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub scheduler: Option, + /// SELinux label for the process. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub selinuxLabel: Option, + /// I/O priority settings for the container's processes within + /// the process group. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ioPriority: Option, + /// CPU affinity used to execute the process. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execCPUAffinity: Option, + /// The user for the process that allows specific control over + /// which user the process runs as. + pub user: User, +} -- Gitee From 7fd797c58670246d135e4697674bf1ef0fc48653 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 19 Aug 2024 09:39:21 +0800 Subject: [PATCH 2091/2187] oci_spec/process: Add an unit testcase for process --- ozonec/oci_spec/src/process.rs | 147 +++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/ozonec/oci_spec/src/process.rs b/ozonec/oci_spec/src/process.rs index fc325812c..a32db540b 100644 --- a/ozonec/oci_spec/src/process.rs +++ b/ozonec/oci_spec/src/process.rs @@ -118,3 +118,150 @@ pub struct Process { /// which user the process runs as. pub user: User, } + +#[cfg(test)] +mod tests { + use crate::linux::IoPriClass; + + use super::*; + use serde_json; + + #[test] + fn test_process() { + let json = r#"{ + "process": { + "terminal": true, + "consoleSize": { + "height": 25, + "width": 80 + }, + "user": { + "uid": 1, + "gid": 1, + "umask": 63, + "additionalGids": [5, 6] + }, + "env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm" + ], + "cwd": "/root", + "args": [ + "sh" + ], + "apparmorProfile": "acme_secure_profile", + "selinuxLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675", + "ioPriority": { + "class": "IOPRIO_CLASS_IDLE", + "priority": 4 + }, + "noNewPrivileges": true, + "capabilities": { + "bounding": [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "permitted": [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "inheritable": [ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_NET_BIND_SERVICE" + ], + "effective": [ + "CAP_AUDIT_WRITE", + "CAP_KILL" + ], + "ambient": [ + "CAP_NET_BIND_SERVICE" + ] + }, + "rlimits": [ + { + "type": "RLIMIT_NOFILE", + "hard": 1024, + "soft": 1024 + } + ], + "execCPUAffinity": { + "initial": "7", + "final": "0-3,7" + } + } + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + process: Process, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.process.terminal, true); + let console_size = section.process.consoleSize.as_ref().unwrap(); + assert_eq!(console_size.height, Some(25)); + assert_eq!(console_size.width, Some(80)); + assert_eq!(section.process.user.uid, 1); + assert_eq!(section.process.user.gid, 1); + assert_eq!(section.process.user.umask, Some(63)); + assert_eq!(section.process.user.additionalGids, Some(vec![5, 6])); + let env = section.process.env.as_ref().unwrap(); + assert_eq!(env.len(), 2); + assert_eq!( + env[0], + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ); + assert_eq!(env[1], "TERM=xterm"); + assert_eq!(section.process.cwd, "/root"); + let args = section.process.args.as_ref().unwrap(); + assert_eq!(args.len(), 1); + assert_eq!(args[0], "sh"); + assert_eq!( + section.process.apparmorProfile, + Some("acme_secure_profile".to_string()) + ); + assert_eq!( + section.process.selinuxLabel, + Some("system_u:system_r:svirt_lxc_net_t:s0:c124,c675".to_string()) + ); + let io_pri = section.process.ioPriority.as_ref().unwrap(); + assert_eq!(io_pri.class, IoPriClass::IoprioClassIdle); + assert_eq!(io_pri.priority, 4); + assert_eq!(section.process.noNewPrivileges, Some(true)); + let caps = section.process.capabilities.as_ref().unwrap(); + let bonding_caps = caps.bounding.as_ref().unwrap(); + assert_eq!(bonding_caps.len(), 3); + assert_eq!(bonding_caps[0], "CAP_AUDIT_WRITE"); + assert_eq!(bonding_caps[1], "CAP_KILL"); + assert_eq!(bonding_caps[2], "CAP_NET_BIND_SERVICE"); + let permitted_caps = caps.permitted.as_ref().unwrap(); + assert_eq!(permitted_caps.len(), 3); + assert_eq!(permitted_caps[0], "CAP_AUDIT_WRITE"); + assert_eq!(permitted_caps[1], "CAP_KILL"); + assert_eq!(permitted_caps[2], "CAP_NET_BIND_SERVICE"); + let inheritable_caps = caps.inheritable.as_ref().unwrap(); + assert_eq!(inheritable_caps.len(), 3); + assert_eq!(inheritable_caps[0], "CAP_AUDIT_WRITE"); + assert_eq!(inheritable_caps[1], "CAP_KILL"); + assert_eq!(inheritable_caps[2], "CAP_NET_BIND_SERVICE"); + let effective_caps = caps.effective.as_ref().unwrap(); + assert_eq!(effective_caps.len(), 2); + assert_eq!(effective_caps[0], "CAP_AUDIT_WRITE"); + assert_eq!(effective_caps[1], "CAP_KILL"); + let ambient_caps = caps.ambient.as_ref().unwrap(); + assert_eq!(ambient_caps.len(), 1); + assert_eq!(ambient_caps[0], "CAP_NET_BIND_SERVICE"); + let rlimits = section.process.rlimits.as_ref().unwrap(); + assert_eq!(rlimits.len(), 1); + assert_eq!(rlimits[0].rlimit_type, "RLIMIT_NOFILE"); + assert_eq!(rlimits[0].hard, 1024); + assert_eq!(rlimits[0].soft, 1024); + let exec_cpu_affinity = section.process.execCPUAffinity.as_ref().unwrap(); + assert_eq!(exec_cpu_affinity.initial, Some("7".to_string())); + assert_eq!(exec_cpu_affinity.final_cpus, Some("0-3,7".to_string())); + } +} -- Gitee From 6dc8a78afb6b1b1e3ca12faecefba3c21425e0af Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 19 Aug 2024 10:55:22 +0800 Subject: [PATCH 2092/2187] oci_spec/vm: Add vm platform support --- ozonec/oci_spec/src/vm.rs | 125 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/ozonec/oci_spec/src/vm.rs b/ozonec/oci_spec/src/vm.rs index 4524f4b26..08d1a3548 100644 --- a/ozonec/oci_spec/src/vm.rs +++ b/ozonec/oci_spec/src/vm.rs @@ -9,3 +9,128 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + +use serde::{Deserialize, Serialize}; + +/// Hypervisor that manages the container virtual machine. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Hypervisor { + /// Path to the hypervisor binary that manages the container + /// virtual machine. + pub path: String, + /// Array of parameters to pass to the hypervisor. + #[serde(skip_serializing_if = "Option::is_none")] + pub parameters: Option>, +} + +/// Kernel to boot the container virtual machine with. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Kernel { + /// Path to the kernel used to boot the container virtual machine. + pub path: String, + #[serde(skip_serializing_if = "Option::is_none")] + /// Array of parameters to pass to the kernel. + pub parameters: Option>, + /// Path to an initial ramdisk to be used by the container + /// virtual machine. + #[serde(skip_serializing_if = "Option::is_none")] + pub initrd: Option, +} + +/// Image that contains the root filesystem for the container +/// virtual machine. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Image { + /// Path to the container virtual machine root image. + pub path: String, + /// Format of the container virtual machine root image. + pub format: String, +} + +/// Configuration for the hypervisor, kernel, and image. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VmPlatform { + /// Hypervisor that manages the container virtual machine. + #[serde(skip_serializing_if = "Option::is_none")] + pub hypervisor: Option, + /// Kernel to boot the container virtual machine with. + pub kernel: Kernel, + /// Image that contains the root filesystem for the container + /// virtual machine. + #[serde(skip_serializing_if = "Option::is_none")] + pub image: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + fn test_hypervisor() { + let json = r#"{ + "hypervisor": { + "path": "/path/to/vmm", + "parameters": ["opts1=foo", "opts2=bar"] + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + hypervisor: Hypervisor, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.hypervisor.path, "/path/to/vmm"); + let parameters = section.hypervisor.parameters.as_ref().unwrap(); + assert_eq!(parameters.len(), 2); + assert_eq!(parameters[0], "opts1=foo"); + assert_eq!(parameters[1], "opts2=bar"); + } + + #[test] + fn test_kernel() { + let json = r#"{ + "kernel": { + "path": "/path/to/vmlinuz", + "parameters": ["foo=bar", "hello world"], + "initrd": "/path/to/initrd.img" + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + kernel: Kernel, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.kernel.path, "/path/to/vmlinuz"); + let parameters = section.kernel.parameters.as_ref().unwrap(); + assert_eq!(parameters.len(), 2); + assert_eq!(parameters[0], "foo=bar"); + assert_eq!(parameters[1], "hello world"); + assert_eq!( + section.kernel.initrd, + Some("/path/to/initrd.img".to_string()) + ); + } + + #[test] + fn test_image() { + let json = r#"{ + "image": { + "path": "/path/to/vm/rootfs.img", + "format": "raw" + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + image: Image, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.image.path, "/path/to/vm/rootfs.img"); + assert_eq!(section.image.format, "raw"); + } +} -- Gitee From e28306756ccb5b383b2fb5e165afdc7e9b9eace9 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 19 Aug 2024 12:46:19 +0800 Subject: [PATCH 2093/2187] oci_spec/runtime: Add struct RuntimeConfig --- ozonec/oci_spec/src/lib.rs | 2 +- ozonec/oci_spec/src/posix.rs | 193 +++++++++++++++++++++++++++++++++ ozonec/oci_spec/src/process.rs | 35 +----- ozonec/oci_spec/src/runtime.rs | 83 ++++++++++++++ 4 files changed, 281 insertions(+), 32 deletions(-) diff --git a/ozonec/oci_spec/src/lib.rs b/ozonec/oci_spec/src/lib.rs index 3f79329b0..f0dd3fe5f 100644 --- a/ozonec/oci_spec/src/lib.rs +++ b/ozonec/oci_spec/src/lib.rs @@ -12,7 +12,7 @@ #[cfg(target_os = "linux")] pub mod linux; -#[cfg(target_os = "linux")] +#[cfg(target_family = "unix")] pub mod posix; pub mod process; pub mod runtime; diff --git a/ozonec/oci_spec/src/posix.rs b/ozonec/oci_spec/src/posix.rs index 03396e42d..b6ae8b377 100644 --- a/ozonec/oci_spec/src/posix.rs +++ b/ozonec/oci_spec/src/posix.rs @@ -52,3 +52,196 @@ pub struct User { #[serde(skip_serializing_if = "Option::is_none")] pub additionalGids: Option>, } + +/// Hook Entry. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct HookEntry { + /// Similar semantics to IEEE Std 1003.1-2008 execv's path. + pub path: String, + /// Same semantics as IEEE Std 1003.1-2008 execv's argv. + #[serde(skip_serializing_if = "Option::is_none")] + pub args: Option>, + /// Same semantics as IEEE Std 1003.1-2008's environ. + #[serde(skip_serializing_if = "Option::is_none")] + pub env: Option>, + /// Number of seconds before aborting the hook. + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout: Option, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Hooks { + /// Array of prestart hooks. + #[serde(skip_serializing_if = "Option::is_none")] + prestart: Option>, + /// Array of createRuntime hooks. + #[serde(skip_serializing_if = "Option::is_none")] + createRuntime: Option>, + /// Array of createContainer hooks. + #[serde(skip_serializing_if = "Option::is_none")] + createContainer: Option>, + /// Array of startContainer hooks. + #[serde(skip_serializing_if = "Option::is_none")] + startContainer: Option>, + /// Array of poststart hooks. + #[serde(skip_serializing_if = "Option::is_none")] + poststart: Option>, + /// Array of poststop hooks. + #[serde(skip_serializing_if = "Option::is_none")] + poststop: Option>, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + fn test_root() { + let json = r#"{ + "root": { + "path": "rootfs", + "readonly": true + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + root: Root, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.root.path, "rootfs"); + assert_eq!(section.root.readonly, true); + } + + #[test] + fn test_hooks() { + let json = r#"{ + "hooks": { + "prestart": [ + { + "path": "/usr/bin/fix-mounts", + "args": ["fix-mounts", "arg1", "arg2"], + "env": [ "key1=value1"] + }, + { + "path": "/usr/bin/setup-network" + } + ], + "createRuntime": [ + { + "path": "/usr/bin/fix-mounts", + "args": ["fix-mounts", "arg1", "arg2"], + "env": [ "key1=value1"] + }, + { + "path": "/usr/bin/setup-network" + } + ], + "createContainer": [ + { + "path": "/usr/bin/mount-hook", + "args": ["-mount", "arg1", "arg2"], + "env": [ "key1=value1"] + } + ], + "startContainer": [ + { + "path": "/usr/bin/refresh-ldcache" + } + ], + "poststart": [ + { + "path": "/usr/bin/notify-start", + "timeout": 5 + } + ], + "poststop": [ + { + "path": "/usr/sbin/cleanup.sh", + "args": ["cleanup.sh", "-f"] + } + ] + } + }"#; + + #[derive(Serialize, Deserialize)] + struct Section { + hooks: Hooks, + } + + let section: Section = serde_json::from_str(json).unwrap(); + let prestart = section.hooks.prestart.as_ref().unwrap(); + assert_eq!(prestart.len(), 2); + assert_eq!(prestart[0].path, "/usr/bin/fix-mounts"); + let args = prestart[0].args.as_ref().unwrap(); + assert_eq!(args.len(), 3); + assert_eq!(args[0], "fix-mounts"); + assert_eq!(args[1], "arg1"); + assert_eq!(args[2], "arg2"); + let env = prestart[0].env.as_ref().unwrap(); + assert_eq!(env.len(), 1); + assert_eq!(env[0], "key1=value1"); + assert_eq!(prestart[0].timeout, None); + assert_eq!(prestart[1].path, "/usr/bin/setup-network"); + assert_eq!(prestart[1].args, None); + assert_eq!(prestart[1].env, None); + assert_eq!(prestart[1].timeout, None); + + let create_runtime = section.hooks.createRuntime.as_ref().unwrap(); + assert_eq!(create_runtime.len(), 2); + assert_eq!(create_runtime[0].path, "/usr/bin/fix-mounts"); + let args = create_runtime[0].args.as_ref().unwrap(); + assert_eq!(args.len(), 3); + assert_eq!(args[0], "fix-mounts"); + assert_eq!(args[1], "arg1"); + assert_eq!(args[2], "arg2"); + let env = create_runtime[0].env.as_ref().unwrap(); + assert_eq!(env.len(), 1); + assert_eq!(env[0], "key1=value1"); + assert_eq!(create_runtime[0].timeout, None); + assert_eq!(create_runtime[1].path, "/usr/bin/setup-network"); + assert_eq!(create_runtime[1].args, None); + assert_eq!(create_runtime[1].env, None); + assert_eq!(create_runtime[1].timeout, None); + + let create_container = section.hooks.createContainer.as_ref().unwrap(); + assert_eq!(create_container.len(), 1); + assert_eq!(create_container[0].path, "/usr/bin/mount-hook"); + let args = create_container[0].args.as_ref().unwrap(); + assert_eq!(args.len(), 3); + assert_eq!(args[0], "-mount"); + assert_eq!(args[1], "arg1"); + assert_eq!(args[2], "arg2"); + let env = create_container[0].env.as_ref().unwrap(); + assert_eq!(env.len(), 1); + assert_eq!(env[0], "key1=value1"); + assert_eq!(create_container[0].timeout, None); + + let start_container = section.hooks.startContainer.as_ref().unwrap(); + assert_eq!(start_container.len(), 1); + assert_eq!(start_container[0].path, "/usr/bin/refresh-ldcache"); + assert_eq!(start_container[0].args, None); + assert_eq!(start_container[0].env, None); + assert_eq!(start_container[0].timeout, None); + + let poststart = section.hooks.poststart.as_ref().unwrap(); + assert_eq!(poststart.len(), 1); + assert_eq!(poststart[0].path, "/usr/bin/notify-start"); + assert_eq!(poststart[0].args, None); + assert_eq!(poststart[0].env, None); + assert_eq!(poststart[0].timeout, Some(5)); + + let poststop = section.hooks.poststop.as_ref().unwrap(); + assert_eq!(poststop.len(), 1); + assert_eq!(poststop[0].path, "/usr/sbin/cleanup.sh"); + let args = poststop[0].args.as_ref().unwrap(); + assert_eq!(args.len(), 2); + assert_eq!(args[0], "cleanup.sh"); + assert_eq!(args[1], "-f"); + assert_eq!(poststop[0].env, None); + assert_eq!(poststop[0].timeout, None); + } +} diff --git a/ozonec/oci_spec/src/process.rs b/ozonec/oci_spec/src/process.rs index a32db540b..a558d78b0 100644 --- a/ozonec/oci_spec/src/process.rs +++ b/ozonec/oci_spec/src/process.rs @@ -12,36 +12,10 @@ use serde::{Deserialize, Serialize}; -use crate::{ - linux::{Capbilities, ExecCpuAffinity, IdMapping, IoPriority, Scheduler}, - posix::{Rlimits, User}, -}; - -/// Additional mounts beyond root. -#[allow(non_snake_case)] -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Mount { - /// Destination of mount point: path inside container. - pub destination: String, - /// A device name, but can also be a file or directory name for bind mounts - /// or a dummy. - #[serde(skip_serializing_if = "Option::is_none")] - pub source: Option, - /// Mount options of the filesystem to be used. - #[serde(skip_serializing_if = "Option::is_none")] - pub options: Option>, - /// The type of the filesystem to be mounted. - #[serde(skip_serializing_if = "Option::is_none", rename = "type")] - pub fs_type: Option, - /// The mapping to convert UIDs from the source file system to the - /// destination mount point. - #[serde(skip_serializing_if = "Option::is_none")] - pub uidMappings: Option, - /// The mapping to convert GIDs from the source file system to the - /// destination mount point. - #[serde(skip_serializing_if = "Option::is_none")] - pub gidMappings: Option, -} +#[cfg(target_os = "linux")] +use crate::linux::{Capbilities, ExecCpuAffinity, IoPriority, Scheduler}; +#[cfg(target_family = "unix")] +use crate::posix::{Rlimits, User}; /// Console size in characters of the terminal. #[derive(Serialize, Deserialize, Debug, Clone)] @@ -194,7 +168,6 @@ mod tests { } }"#; - #[allow(non_snake_case)] #[derive(Serialize, Deserialize)] struct Section { process: Process, diff --git a/ozonec/oci_spec/src/runtime.rs b/ozonec/oci_spec/src/runtime.rs index 4524f4b26..8b6b327bd 100644 --- a/ozonec/oci_spec/src/runtime.rs +++ b/ozonec/oci_spec/src/runtime.rs @@ -9,3 +9,86 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + +use std::{collections::HashMap, fs::File, io::BufReader, path::Path}; + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; + +#[cfg(target_os = "linux")] +use crate::linux::IdMapping; +#[cfg(target_family = "unix")] +use crate::posix::Root; +use crate::{linux::LinuxPlatform, posix::Hooks, process::Process, vm::VmPlatform}; + +/// Additional mounts beyond root. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Mount { + /// Destination of mount point: path inside container. + pub destination: String, + /// A device name, but can also be a file or directory name for bind mounts + /// or a dummy. + #[serde(skip_serializing_if = "Option::is_none")] + pub source: Option, + /// Mount options of the filesystem to be used. + #[serde(skip_serializing_if = "Option::is_none")] + pub options: Option>, + /// The type of the filesystem to be mounted. + #[serde(skip_serializing_if = "Option::is_none", rename = "type")] + pub fs_type: Option, + /// The mapping to convert UIDs from the source file system to the + /// destination mount point. + #[serde(skip_serializing_if = "Option::is_none")] + pub uidMappings: Option, + /// The mapping to convert GIDs from the source file system to the + /// destination mount point. + #[serde(skip_serializing_if = "Option::is_none")] + pub gidMappings: Option, +} + +/// Metadata necessary to implement standard operations against the container. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RuntimeConfig { + /// Version of the Open Container Initiative Runtime Specification + /// with which the bundle complies. + pub ociVersion: String, + /// Container's root filesystem. + pub root: Root, + /// Additional mounts beyond root. + pub mounts: Vec, + /// Container process. + pub process: Process, + /// Container's hostname as seen by processes running inside the container. + #[serde(skip_serializing_if = "Option::is_none")] + pub hostname: Option, + /// Container's domainname as seen by processes running inside the + /// container. + #[serde(skip_serializing_if = "Option::is_none")] + pub domainname: Option, + /// Linux-specific section of the container configuration. + #[cfg(target_os = "linux")] + #[serde(skip_serializing_if = "Option::is_none")] + pub linux: Option, + /// Vm-specific section of the container configuration. + #[serde(skip_serializing_if = "Option::is_none")] + pub vm: Option, + /// Custom actions related to the lifecycle of the container. + #[cfg(target_family = "unix")] + #[serde(skip_serializing_if = "Option::is_none")] + pub hooks: Option, + /// Arbitrary metadata for the container. + #[serde(skip_serializing_if = "Option::is_none")] + pub annotations: Option>, +} + +impl RuntimeConfig { + pub fn from_file(path: &String) -> Result { + let file = File::open(Path::new(path)).with_context(|| "Failed to open config.json")?; + let reader = BufReader::new(file); + let config = + serde_json::from_reader(reader).with_context(|| "Failed to load config.json")?; + Ok(config) + } +} -- Gitee From 4a96ec5bc7332a62b0cb15173065b7cdd20b2107 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 19 Aug 2024 15:04:16 +0800 Subject: [PATCH 2094/2187] oci_spec/runtime: Add unit testcase for mounts --- ozonec/oci_spec/src/runtime.rs | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/ozonec/oci_spec/src/runtime.rs b/ozonec/oci_spec/src/runtime.rs index 8b6b327bd..f1286357b 100644 --- a/ozonec/oci_spec/src/runtime.rs +++ b/ozonec/oci_spec/src/runtime.rs @@ -92,3 +92,51 @@ impl RuntimeConfig { Ok(config) } } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + fn test_mounts() { + let json = r#"{ + "mounts": [ + { + "destination": "/proc", + "type": "proc", + "source": "proc" + }, + { + "destination": "/dev", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "nosuid", + "strictatime", + "mode=755", + "size=65536k" + ] + } + ] + }"#; + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize)] + struct Section { + mounts: Vec, + } + + let section: Section = serde_json::from_str(json).unwrap(); + assert_eq!(section.mounts.len(), 2); + assert_eq!(section.mounts[0].destination, "/proc"); + assert_eq!(section.mounts[0].fs_type, Some("proc".to_string())); + assert_eq!(section.mounts[0].source, Some("proc".to_string())); + let options = section.mounts[1].options.as_ref().unwrap(); + assert_eq!(options.len(), 4); + assert_eq!(options[0], "nosuid"); + assert_eq!(options[1], "strictatime"); + assert_eq!(options[2], "mode=755"); + assert_eq!(options[3], "size=65536k"); + } +} -- Gitee From a9048106dc644de0a50939a88782ebcb6cba82ac Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 19 Aug 2024 15:42:17 +0800 Subject: [PATCH 2095/2187] oci_spec/state: Add container state support --- ozonec/oci_spec/src/state.rs | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/ozonec/oci_spec/src/state.rs b/ozonec/oci_spec/src/state.rs index 4524f4b26..fe8180527 100644 --- a/ozonec/oci_spec/src/state.rs +++ b/ozonec/oci_spec/src/state.rs @@ -9,3 +9,82 @@ // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +/// Runtime state of the container. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Copy, Default)] +#[serde(rename_all = "lowercase")] +pub enum ContainerStatus { + Creating, + Created, + Running, + #[default] + Stopped, +} + +impl ToString for ContainerStatus { + fn to_string(&self) -> String { + match *self { + ContainerStatus::Creating => String::from("creating"), + ContainerStatus::Created => String::from("created"), + ContainerStatus::Running => String::from("running"), + ContainerStatus::Stopped => String::from("stopped"), + } + } +} + +/// The state of a container. +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct State { + /// Version of the Open Container Initiative Runtime Specification + /// with which the state complies. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub ociVersion: String, + /// Container's ID. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub id: String, + /// Runtime state of the container. + pub status: ContainerStatus, + /// ID of the container process. + #[serde(default)] + pub pid: i32, + /// Absolute path to the container's bundle directory. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub bundle: String, + /// List of annotations associated with the container. + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub annotations: HashMap, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + fn test_state() { + let json = r#"{ + "ociVersion": "0.2.0", + "id": "oci-container1", + "status": "running", + "pid": 4422, + "bundle": "/containers/redis", + "annotations": { + "myKey": "myValue" + } + }"#; + + let state: State = serde_json::from_str(json).unwrap(); + assert_eq!(state.ociVersion, "0.2.0"); + assert_eq!(state.id, "oci-container1"); + assert_eq!(state.status, ContainerStatus::Running); + assert_eq!(state.pid, 4422); + assert_eq!(state.bundle, "/containers/redis"); + assert!(state.annotations.contains_key("myKey")); + assert_eq!(state.annotations.get("myKey"), Some(&"myValue".to_string())); + } +} -- Gitee From c3bdfcdbdfefb7d632854c9b1879ef901cd97734 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 19 Aug 2024 21:57:39 +0800 Subject: [PATCH 2096/2187] ozonec/container: Add struct Launcher to launch container process --- ozonec/Cargo.toml | 4 ++ ozonec/src/container/launcher.rs | 110 +++++++++++++++++++++++++++++++ ozonec/src/container/mod.rs | 44 +++++++++++++ ozonec/src/container/process.rs | 49 ++++++++++++++ ozonec/src/container/state.rs | 99 ++++++++++++++++++++++++++++ 5 files changed, 306 insertions(+) create mode 100644 ozonec/src/container/launcher.rs create mode 100644 ozonec/src/container/mod.rs create mode 100644 ozonec/src/container/process.rs create mode 100644 ozonec/src/container/state.rs diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index 8cd9de1b1..b00a0b35e 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -8,11 +8,15 @@ description = "An OCI runtime implemented by Rust" [dependencies] anyhow = "= 1.0.71" +chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"] } clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } libc = "= 0.2.146" log = { version = "= 0.4.18", features = ["std"]} nix = "= 0.26.2" oci_spec = { path = "oci_spec" } +procfs = "0.14.0" +serde = { version = "= 1.0.163", features = ["derive"] } +serde_json = "= 1.0.96" [workspace] diff --git a/ozonec/src/container/launcher.rs b/ozonec/src/container/launcher.rs new file mode 100644 index 000000000..9887d9ebc --- /dev/null +++ b/ozonec/src/container/launcher.rs @@ -0,0 +1,110 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; + +use super::{process::Process, state::State, Container}; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Action { + Create, + Start, + Exec, +} + +pub struct Launcher { + pub bundle: PathBuf, + pub state_root: PathBuf, + /// init is set to true when creating a container. + pub init: bool, + pub runner: Box, + pub pid_file: Option, +} + +impl Launcher { + pub fn new( + bundle: &Path, + state_root: &Path, + init: bool, + runner: Box, + pid_file: Option, + ) -> Self { + Self { + bundle: bundle.to_path_buf(), + state_root: state_root.to_path_buf(), + init, + runner, + pid_file, + } + } + + pub fn launch(&mut self, action: Action) -> Result<()> { + if self.init { + self.spawn_container()?; + } else { + self.spawn_process(action)?; + } + + if let Some(pid_file) = self.pid_file.as_ref() { + let pid = self.runner.get_pid(); + std::fs::write(pid_file, format!("{}", pid)).with_context(|| "Failed to write pid")?; + } + + Ok(()) + } + + fn spawn_container(&mut self) -> Result<()> { + self.spawn_process(Action::Create)?; + + let mut state = self + .get_state() + .with_context(|| "Failed to get container state")?; + state.update(); + state.save().with_context(|| "Failed to save state")?; + Ok(()) + } + + fn spawn_process(&mut self, action: Action) -> Result<()> { + let mut process = self.get_process(); + match action { + Action::Create => self.runner.create(&mut process), + Action::Start => self.runner.start(), + Action::Exec => self.runner.exec(&mut process), + } + } + + fn get_process(&self) -> Process { + let config = self.runner.get_config(); + Process::new(&config.process, self.init) + } + + fn get_state(&self) -> Result { + let state = self.runner.get_oci_state()?; + let pid = self.runner.get_pid(); + let proc = procfs::process::Process::new(pid)?; + let start_time = proc + .stat() + .with_context(|| format!("Failed to access /proc/{}/status", pid))? + .starttime; + + Ok(State::new( + &self.state_root, + &self.bundle, + state, + start_time, + *self.runner.created_time(), + self.runner.get_config(), + )) + } +} diff --git a/ozonec/src/container/mod.rs b/ozonec/src/container/mod.rs new file mode 100644 index 000000000..611af779f --- /dev/null +++ b/ozonec/src/container/mod.rs @@ -0,0 +1,44 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod launcher; +mod process; +mod state; + +pub use launcher::{Action, Launcher}; +pub use process::Process; + +use std::time::SystemTime; + +use anyhow::Result; +use libc::pid_t; +use nix::sys::signal::Signal; + +use oci_spec::{runtime::RuntimeConfig, state::State as OciState}; + +pub trait Container { + fn get_config(&self) -> &RuntimeConfig; + + fn get_oci_state(&self) -> Result; + + fn get_pid(&self) -> pid_t; + + fn created_time(&self) -> &SystemTime; + + fn create(&mut self, process: &mut Process) -> Result<()>; + + fn start(&mut self) -> Result<()>; + + fn exec(&mut self, process: &mut Process) -> Result<()>; + + fn kill(&mut self, sig: Signal) -> Result<()>; +} diff --git a/ozonec/src/container/process.rs b/ozonec/src/container/process.rs new file mode 100644 index 000000000..962599aba --- /dev/null +++ b/ozonec/src/container/process.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + io::{stderr, stdin, stdout}, + os::fd::{AsRawFd, RawFd}, +}; + +use oci_spec::process::Process as OciProcess; + +pub struct Process { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, + pub term_master: Option, + pub init: bool, + pub tty: bool, + pub oci: OciProcess, +} + +impl Process { + pub fn new(oci: &OciProcess, init: bool) -> Self { + let mut p = Process { + stdin: None, + stdout: None, + stderr: None, + tty: oci.terminal, + term_master: None, + init, + oci: oci.clone(), + }; + + if !p.tty { + p.stdin = Some(stdin().as_raw_fd()); + p.stdout = Some(stdout().as_raw_fd()); + p.stderr = Some(stderr().as_raw_fd()); + } + p + } +} diff --git a/ozonec/src/container/state.rs b/ozonec/src/container/state.rs new file mode 100644 index 000000000..424e2d811 --- /dev/null +++ b/ozonec/src/container/state.rs @@ -0,0 +1,99 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::{DirBuilder, OpenOptions}, + os::unix::fs::DirBuilderExt, + path::{Path, PathBuf}, + time::SystemTime, +}; + +use anyhow::{Context, Result}; +use chrono::{DateTime, Utc}; +use libc::pid_t; +use nix::sys::stat::Mode; +use serde::{Deserialize, Serialize}; + +use oci_spec::{runtime::RuntimeConfig, state::State as OciState}; + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct State { + pub oci_version: String, + pub id: String, + pub pid: pid_t, + pub root: PathBuf, + pub bundle: PathBuf, + pub rootfs: String, + pub start_time: u64, + pub created_time: DateTime, + pub config: Option, +} + +impl State { + pub fn new( + root: &Path, + bundle: &Path, + oci_state: OciState, + start_time: u64, + created_time: SystemTime, + config: &RuntimeConfig, + ) -> Self { + Self { + oci_version: oci_state.ociVersion, + id: oci_state.id, + pid: oci_state.pid, + root: root.to_path_buf(), + bundle: bundle.to_path_buf(), + rootfs: config.root.path.clone(), + start_time, + created_time: DateTime::from(created_time), + config: Some(config.clone()), + } + } + + fn file_path(root: &Path, id: &str) -> PathBuf { + root.join(id).join("state.json") + } + + pub fn save(&self) -> Result<()> { + if !&self.root.exists() { + DirBuilder::new() + .recursive(true) + .mode(Mode::S_IRWXU.bits()) + .create(&self.root) + .with_context(|| "Failed to create root directory")?; + } + + let path = Self::file_path(&self.root, &self.id); + let state_file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .with_context(|| "Failed to open state file")?; + serde_json::to_writer(&state_file, self)?; + Ok(()) + } + + pub fn update(&mut self) { + let linux = self.config.as_mut().unwrap().linux.as_mut(); + if let Some(config) = linux { + for ns in &mut config.namespaces { + if ns.path.is_none() { + let ns_name: String = ns.ns_type.into(); + ns.path = Some(PathBuf::from(format!("/proc/{}/ns/{}", self.pid, ns_name))) + } + } + } + } +} -- Gitee From 57299ab9981525cd8f7645d91aa4952b86c8aeaf Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 20 Aug 2024 14:21:33 +0800 Subject: [PATCH 2097/2187] ozonec/linux: Add Linux container support --- ozonec/src/linux/container.rs | 179 ++++++++++++++++++++++++++++++ ozonec/src/linux/mod.rs | 16 +++ ozonec/src/linux/notify_socket.rs | 13 +++ 3 files changed, 208 insertions(+) create mode 100644 ozonec/src/linux/container.rs create mode 100644 ozonec/src/linux/mod.rs create mode 100644 ozonec/src/linux/notify_socket.rs diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs new file mode 100644 index 000000000..28c0de319 --- /dev/null +++ b/ozonec/src/linux/container.rs @@ -0,0 +1,179 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + collections::HashMap, + fs::{canonicalize, create_dir_all}, + path::{Path, PathBuf}, + time::SystemTime, +}; + +use anyhow::{anyhow, bail, Context, Result}; +use libc::pid_t; +use log::error; +use nix::{ + sys::signal::Signal, + unistd::{chown, getegid, geteuid}, +}; +use procfs::process::ProcState; + +use super::notify_socket::NOTIFY_SOCKET; +use crate::container::{Container, Process}; +use oci_spec::{ + runtime::RuntimeConfig, + state::{ContainerStatus, State}, +}; + +pub struct LinuxContainer { + pub id: String, + pub root: String, + pub config: RuntimeConfig, + pub pid: pid_t, + pub start_time: u64, + pub created_time: SystemTime, + pub console_socket: Option, +} + +impl LinuxContainer { + pub fn new( + id: &String, + root: &String, + config: &RuntimeConfig, + console_socket: &Option, + exist: &mut bool, + ) -> Result { + let container_dir = format!("{}/{}", root, id); + + Self::validate_config(config)?; + + if Path::new(container_dir.as_str()).exists() { + *exist = true; + bail!("Container {} already exists", id); + } + create_dir_all(container_dir.as_str()).map_err(|e| { + error!("Failed to create container directory: {}", e); + anyhow!(e).context("Failed to create container directory") + })?; + chown(container_dir.as_str(), Some(geteuid()), Some(getegid())) + .with_context(|| "Failed to chown container directory")?; + + Ok(Self { + id: id.clone(), + root: container_dir, + config: config.clone(), + pid: -1, + start_time: 0, + created_time: SystemTime::now(), + console_socket: console_socket.clone(), + }) + } + + fn validate_config(config: &RuntimeConfig) -> Result<()> { + if config.linux.is_none() { + bail!("There is no linux specific configuration in config.json for Linux container"); + } + Ok(()) + } + + fn container_status(&self) -> Result { + if self.pid == -1 { + return Ok(ContainerStatus::Creating); + } + + let proc = procfs::process::Process::new(self.pid); + // If error occurs when accessing /proc/, the process most likely has stopped. + if proc.is_err() { + return Ok(ContainerStatus::Stopped); + } + let proc_stat = proc + .unwrap() + .stat() + .with_context(|| format!("Failed to read /proc/{}/stat", self.pid))?; + // If starttime is not the same, then pid is reused, and the original process has stopped. + if proc_stat.starttime != self.start_time { + return Ok(ContainerStatus::Stopped); + } + + match proc_stat.state()? { + ProcState::Zombie | ProcState::Dead => Ok(ContainerStatus::Stopped), + _ => { + let notify_socket = PathBuf::from(&self.root).join(NOTIFY_SOCKET); + if notify_socket.exists() { + return Ok(ContainerStatus::Created); + } + Ok(ContainerStatus::Running) + } + } + } +} + +impl Container for LinuxContainer { + fn get_config(&self) -> &RuntimeConfig { + &self.config + } + + fn get_pid(&self) -> pid_t { + self.pid + } + + fn created_time(&self) -> &SystemTime { + &self.created_time + } + + fn get_oci_state(&self) -> Result { + let status = self.container_status()?; + let pid = if status != ContainerStatus::Stopped { + self.pid + } else { + 0 + }; + + let rootfs = canonicalize(&self.config.root.path.clone()) + .with_context(|| "Failed to canonicalize root path")?; + let bundle = match rootfs.parent() { + Some(p) => p + .to_str() + .ok_or(anyhow!("root path is not valid unicode"))? + .to_string(), + None => bail!("Failed to get bundle directory"), + }; + let annotations = if let Some(a) = self.config.annotations.clone() { + a + } else { + HashMap::new() + }; + Ok(State { + ociVersion: self.config.ociVersion.clone(), + id: self.id.clone(), + status, + pid, + bundle, + annotations, + }) + } + + fn create(&mut self, process: &mut Process) -> Result<()> { + Ok(()) + } + + fn start(&mut self) -> Result<()> { + Ok(()) + } + + fn exec(&mut self, process: &mut Process) -> Result<()> { + Ok(()) + } + + fn kill(&mut self, sig: Signal) -> Result<()> { + Ok(()) + } +} diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs new file mode 100644 index 000000000..05cf2fa5a --- /dev/null +++ b/ozonec/src/linux/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod container; +mod notify_socket; + +pub use container::LinuxContainer; diff --git a/ozonec/src/linux/notify_socket.rs b/ozonec/src/linux/notify_socket.rs new file mode 100644 index 000000000..2a7ed69e4 --- /dev/null +++ b/ozonec/src/linux/notify_socket.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +pub const NOTIFY_SOCKET: &str = "notify.sock"; -- Gitee From 95a660d4f8d52914c05eb4b2d894365369aab6a3 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 20 Aug 2024 14:45:18 +0800 Subject: [PATCH 2098/2187] ozonec/command: Add create.rs --- ozonec/src/commands/create.rs | 78 +++++++++++++++++++++++++++++++++++ ozonec/src/commands/mod.rs | 15 +++++++ ozonec/src/main.rs | 44 ++++++++++++++++++-- 3 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 ozonec/src/commands/create.rs create mode 100644 ozonec/src/commands/mod.rs diff --git a/ozonec/src/commands/create.rs b/ozonec/src/commands/create.rs new file mode 100644 index 000000000..22c924690 --- /dev/null +++ b/ozonec/src/commands/create.rs @@ -0,0 +1,78 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Ok, Result}; +use clap::{builder::NonEmptyStringValueParser, Parser}; + +use crate::container::{Action, Container, Launcher}; +use crate::linux::LinuxContainer; +use oci_spec::runtime::RuntimeConfig; + +#[derive(Parser, Debug)] +pub struct Create { + /// File to write the container PID to + #[arg(short, long)] + pub pid_file: Option, + /// Path to the bundle directory, defaults to the current working directory. + #[arg(short, long, default_value = ".")] + pub bundle: PathBuf, + /// Path to an AF_UNIX socket which will receive the pseudoterminal master + /// at a file descriptor. + #[arg(short, long)] + pub console_socket: Option, + /// Container ID to create. + #[arg(value_parser = NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, +} + +impl Create { + fn launcher(&self, root: &Path, exist: &mut bool) -> Result { + let bundle_path = self + .bundle + .canonicalize() + .with_context(|| "Failed to canonicalize bundle path")?; + let config_path = bundle_path + .join("config.json") + .to_string_lossy() + .to_string(); + let mut config = RuntimeConfig::from_file(&config_path)?; + let mut rootfs_path = PathBuf::from(config.root.path); + + if !rootfs_path.is_absolute() { + rootfs_path = bundle_path.join(rootfs_path); + } + config.root.path = rootfs_path.to_string_lossy().to_string(); + + let container: Box = Box::new(LinuxContainer::new( + &self.container_id, + &root.to_string_lossy().to_string(), + &config, + &self.console_socket, + exist, + )?); + Ok(Launcher::new( + &bundle_path, + root, + true, + container, + self.pid_file.clone(), + )) + } + + pub fn run(&self, root: &Path, exist: &mut bool) -> Result<()> { + let mut launcher = self.launcher(root, exist)?; + launcher.launch(Action::Create)?; + Ok(()) + } +} diff --git a/ozonec/src/commands/mod.rs b/ozonec/src/commands/mod.rs new file mode 100644 index 000000000..2472ae99f --- /dev/null +++ b/ozonec/src/commands/mod.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod create; + +pub use create::Create; diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index 697528605..dd88161aa 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -10,14 +10,23 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod commands; +mod container; +mod linux; mod utils; -use std::{path::PathBuf, process::exit}; +use std::{ + fs::remove_dir_all, + path::{Path, PathBuf}, + process::exit, +}; use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; +use log::info; +use nix::unistd::geteuid; -use crate::utils::logger; +use crate::{commands::Create, utils::logger}; // Global options which are not binded to any specific command. #[derive(Args, Debug)] @@ -38,7 +47,9 @@ struct GlobalOpts { // and [OCI Command Line Interface] // (https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md). #[derive(Subcommand, Debug)] -enum StandardCmd {} +enum StandardCmd { + Create(Create), +} // Extended commands not documented in [OCI Command Line Interface]. #[derive(Subcommand, Debug)] @@ -62,12 +73,37 @@ struct Cli { cmd: Command, } +fn cmd_run(command: Command, root: &Path) -> Result<()> { + match command { + Command::Standard(cmd) => match cmd { + StandardCmd::Create(create) => { + info!("Exec command: {:?}", create); + + let mut root_exist = false; + create.run(root, &mut root_exist).inspect_err(|_| { + if !root_exist { + let _ = remove_dir_all(root); + } + })? + } + }, + Command::Extend(cmd) => (), + } + Ok(()) +} + fn real_main() -> Result<()> { let cli = Cli::parse(); logger::init(&cli.global.log, cli.global.debug).with_context(|| "Failed to init logger")?; - Ok(()) + let root_path = if let Some(root) = cli.global.root { + root + } else { + let euid = geteuid(); + PathBuf::from(format!("/var/run/user/{}/ozonec", euid)) + }; + cmd_run(cli.cmd, &root_path) } fn main() { -- Gitee From b7a4100dedb6cfb22653cea655e1f0549190c964 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 21 Aug 2024 16:47:59 +0800 Subject: [PATCH 2099/2187] ozonec/utils: Add channel.rs --- ozonec/src/utils/channel.rs | 191 ++++++++++++++++++++++++++++++++++++ ozonec/src/utils/mod.rs | 4 + 2 files changed, 195 insertions(+) create mode 100644 ozonec/src/utils/channel.rs diff --git a/ozonec/src/utils/channel.rs b/ozonec/src/utils/channel.rs new file mode 100644 index 000000000..4e303d104 --- /dev/null +++ b/ozonec/src/utils/channel.rs @@ -0,0 +1,191 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fmt::Debug, + io::{IoSlice, IoSliceMut}, + marker::PhantomData, + mem, + os::fd::RawFd, + slice, +}; + +use anyhow::{bail, Context, Result}; +use nix::{ + sys::{ + socket::{ + recvmsg, sendmsg, setsockopt, socketpair, sockopt, AddressFamily, MsgFlags, SockFlag, + SockType, UnixAddr, + }, + time::TimeVal, + }, + unistd::{self, Pid}, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +// Wrapper for messages to be sent between parent and child processes. +#[derive(Debug, Serialize, Deserialize)] +pub enum Message { + IdMappingStart, + IdMappingDone, + InitReady(i32), + ContainerCreated, + ExecFailed(String), +} + +pub struct Sender { + fd: RawFd, + phantom: PhantomData, +} + +impl Sender +where + T: Serialize, +{ + pub fn close(&self) -> Result<()> { + Ok(unistd::close(self.fd)?) + } + + pub fn send(&self, msg: T) -> Result<()> { + let msg_vec = serde_json::to_vec(&msg).with_context(|| "Failed to load message")?; + let msg_len = msg_vec.len() as u64; + let iov = [ + IoSlice::new(unsafe { + slice::from_raw_parts((&msg_len as *const u64) as *const u8, mem::size_of::()) + }), + IoSlice::new(&msg_vec), + ]; + + sendmsg::(self.fd, &iov, &[], MsgFlags::empty(), None)?; + Ok(()) + } +} + +pub struct Receiver { + fd: RawFd, + phantom: PhantomData, +} + +impl Receiver +where + T: DeserializeOwned, +{ + pub fn close(&self) -> Result<()> { + Ok(unistd::close(self.fd)?) + } + + pub fn set_timeout(&self, timeout: i64) -> Result<()> { + let timeval = TimeVal::new(0, timeout); + setsockopt(self.fd, sockopt::ReceiveTimeout, &timeval) + .with_context(|| "Failed to set receiver end timeout")?; + Ok(()) + } + + fn max_len_iovec(&self) -> Result { + let mut len: u64 = 0; + // SAFETY: len and type "u64" are both valid. + let mut iov = [IoSliceMut::new(unsafe { + slice::from_raw_parts_mut((&mut len as *mut u64) as *mut u8, mem::size_of::()) + })]; + + recvmsg::(self.fd, &mut iov, None, MsgFlags::MSG_PEEK)?; + match len { + 0 => bail!("Failed to get maximum length"), + _ => Ok(len), + } + } + + pub fn recv(&self) -> Result { + let msg_len = self.max_len_iovec()?; + let mut received_len: u64 = 0; + let mut buf = vec![0u8; msg_len as usize]; + let bytes = { + let mut iov = [ + IoSliceMut::new(unsafe { + slice::from_raw_parts_mut( + (&mut received_len as *mut u64) as *mut u8, + mem::size_of::(), + ) + }), + IoSliceMut::new(&mut buf), + ]; + let mut cmsg = nix::cmsg_space!(T); + let msg = recvmsg::( + self.fd, + &mut iov, + Some(&mut cmsg), + MsgFlags::MSG_CMSG_CLOEXEC, + )?; + msg.bytes + }; + + match bytes { + 0 => bail!("Received zero length message"), + _ => Ok(serde_json::from_slice(&buf[..]) + .with_context(|| "Failed to read received message")?), + } + } +} + +pub struct Channel { + pub sender: Sender, + pub receiver: Receiver, +} + +impl Channel { + pub fn new() -> Result> { + let (sender_fd, receiver_fd) = socketpair( + AddressFamily::Unix, + SockType::SeqPacket, + None, + SockFlag::SOCK_CLOEXEC, + )?; + let sender = Sender { + fd: sender_fd, + phantom: PhantomData, + }; + let receiver = Receiver { + fd: receiver_fd, + phantom: PhantomData, + }; + + Ok(Channel { sender, receiver }) + } + + pub fn recv_container_created(&self) -> Result<()> { + let msg = self.receiver.recv()?; + match msg { + Message::ContainerCreated => Ok(()), + _ => bail!("Expect receiving ContainerCreated, but got {:?}", msg), + } + } + + pub fn send_container_created(&self) -> Result<()> { + self.sender + .send(Message::ContainerCreated) + .with_context(|| "Failed to send created message to parent process") + } + + pub fn recv_init_pid(&self) -> Result { + let msg = self.receiver.recv()?; + match msg { + Message::InitReady(pid) => Ok(Pid::from_raw(pid)), + _ => bail!("Expect receiving InitReady, but got {:?}", msg), + } + } + + pub fn send_init_pid(&self, pid: Pid) -> Result<()> { + self.sender + .send(Message::InitReady(pid.as_raw())) + .with_context(|| "Failed to send container process pid") + } +} diff --git a/ozonec/src/utils/mod.rs b/ozonec/src/utils/mod.rs index 3415cf8e8..461581f63 100644 --- a/ozonec/src/utils/mod.rs +++ b/ozonec/src/utils/mod.rs @@ -11,3 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod logger; + +mod channel; + +pub use channel::{Channel, Message}; -- Gitee From 04bdd7257716e89f2cc4999ffe985d5553a624b4 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 21 Aug 2024 16:50:46 +0800 Subject: [PATCH 2100/2187] ozonec/linux: Add notify_socket.rs --- ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/notify_socket.rs | 47 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index 05cf2fa5a..9aed65f47 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -14,3 +14,4 @@ mod container; mod notify_socket; pub use container::LinuxContainer; +pub use notify_socket::NotifyListener; diff --git a/ozonec/src/linux/notify_socket.rs b/ozonec/src/linux/notify_socket.rs index 2a7ed69e4..65ab4c9f0 100644 --- a/ozonec/src/linux/notify_socket.rs +++ b/ozonec/src/linux/notify_socket.rs @@ -10,4 +10,51 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::{ + env, + io::Read, + os::{fd::AsRawFd, unix::net::UnixListener}, + path::{Path, PathBuf}, +}; + +use anyhow::{bail, Context, Result}; +use nix::unistd::{self, chdir}; + pub const NOTIFY_SOCKET: &str = "notify.sock"; + +pub struct NotifyListener { + socket: UnixListener, +} + +impl NotifyListener { + pub fn new(root: PathBuf) -> Result { + // The length of path of Unix domain socket has the limit 108, which is smaller then + // the maximum length of file on Linux (255). + let cwd = + env::current_dir().with_context(|| "Current working directory value is invalid")?; + chdir(&root).with_context(|| "Failed to chdir to root directory")?; + let listener = + UnixListener::bind(NOTIFY_SOCKET).with_context(|| "Failed to bind notify socket")?; + chdir(&cwd).with_context(|| "Failed to chdir to previous working directory")?; + Ok(Self { socket: listener }) + } + + pub fn wait_for_start_container(&self) -> Result<()> { + match self.socket.accept() { + Ok((mut socket, _)) => { + let mut response = String::new(); + socket + .read_to_string(&mut response) + .with_context(|| "Invalid response from notify socket")?; + } + Err(e) => { + bail!("Failed to accept on notify socket: {}", e); + } + } + Ok(()) + } + + pub fn close(&self) -> Result<()> { + Ok(unistd::close(self.socket.as_raw_fd())?) + } +} -- Gitee From 4d66285ad58de8f5daab3b12175c4ec7d100cef5 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 21 Aug 2024 16:56:27 +0800 Subject: [PATCH 2101/2187] ozonec: Implement create command for Linux containers --- ozonec/Cargo.toml | 3 + ozonec/src/commands/create.rs | 1 + ozonec/src/container/launcher.rs | 17 +-- ozonec/src/container/mod.rs | 4 +- ozonec/src/linux/container.rs | 134 ++++++++++++++++++++- ozonec/src/linux/mod.rs | 2 + ozonec/src/{container => linux}/process.rs | 49 +++++++- ozonec/src/utils/error.rs | 21 ++++ ozonec/src/utils/mod.rs | 2 + 9 files changed, 217 insertions(+), 16 deletions(-) rename ozonec/src/{container => linux}/process.rs (45%) create mode 100644 ozonec/src/utils/error.rs diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index b00a0b35e..8c7eb5515 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -10,13 +10,16 @@ description = "An OCI runtime implemented by Rust" anyhow = "= 1.0.71" chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"] } clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } +clone3 = "0.2.3" libc = "= 0.2.146" log = { version = "= 0.4.18", features = ["std"]} nix = "= 0.26.2" oci_spec = { path = "oci_spec" } +prctl = "1.0.0" procfs = "0.14.0" serde = { version = "= 1.0.163", features = ["derive"] } serde_json = "= 1.0.96" +thiserror = "= 1.0.40" [workspace] diff --git a/ozonec/src/commands/create.rs b/ozonec/src/commands/create.rs index 22c924690..e4c802b00 100644 --- a/ozonec/src/commands/create.rs +++ b/ozonec/src/commands/create.rs @@ -19,6 +19,7 @@ use crate::container::{Action, Container, Launcher}; use crate::linux::LinuxContainer; use oci_spec::runtime::RuntimeConfig; +/// Create a container from a bundle directory #[derive(Parser, Debug)] pub struct Create { /// File to write the container PID to diff --git a/ozonec/src/container/launcher.rs b/ozonec/src/container/launcher.rs index 9887d9ebc..0e0e7eb1b 100644 --- a/ozonec/src/container/launcher.rs +++ b/ozonec/src/container/launcher.rs @@ -14,7 +14,9 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; -use super::{process::Process, state::State, Container}; +use crate::{linux::Process, utils::OzonecErr}; + +use super::{state::State, Container}; #[derive(Debug, Clone, Copy, PartialEq)] pub enum Action { @@ -25,7 +27,7 @@ pub enum Action { pub struct Launcher { pub bundle: PathBuf, - pub state_root: PathBuf, + pub root: PathBuf, /// init is set to true when creating a container. pub init: bool, pub runner: Box, @@ -35,14 +37,14 @@ pub struct Launcher { impl Launcher { pub fn new( bundle: &Path, - state_root: &Path, + root: &Path, init: bool, runner: Box, pid_file: Option, ) -> Self { Self { bundle: bundle.to_path_buf(), - state_root: state_root.to_path_buf(), + root: root.to_path_buf(), init, runner, pid_file, @@ -92,14 +94,15 @@ impl Launcher { fn get_state(&self) -> Result { let state = self.runner.get_oci_state()?; let pid = self.runner.get_pid(); - let proc = procfs::process::Process::new(pid)?; + let proc = + procfs::process::Process::new(pid).with_context(|| OzonecErr::ReadProcPid(pid))?; let start_time = proc .stat() - .with_context(|| format!("Failed to access /proc/{}/status", pid))? + .with_context(|| OzonecErr::ReadProcStat(pid))? .starttime; Ok(State::new( - &self.state_root, + &self.root, &self.bundle, state, start_time, diff --git a/ozonec/src/container/mod.rs b/ozonec/src/container/mod.rs index 611af779f..bc6905a60 100644 --- a/ozonec/src/container/mod.rs +++ b/ozonec/src/container/mod.rs @@ -11,11 +11,9 @@ // See the Mulan PSL v2 for more details. mod launcher; -mod process; mod state; pub use launcher::{Action, Launcher}; -pub use process::Process; use std::time::SystemTime; @@ -25,6 +23,8 @@ use nix::sys::signal::Signal; use oci_spec::{runtime::RuntimeConfig, state::State as OciState}; +use crate::linux::Process; + pub trait Container { fn get_config(&self) -> &RuntimeConfig; diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 28c0de319..20a9fd778 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -19,15 +19,22 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use libc::pid_t; -use log::error; +use log::{debug, error, info}; use nix::{ - sys::signal::Signal, + errno::Errno, + sys::{ + signal::Signal, + wait::{waitpid, WaitStatus}, + }, unistd::{chown, getegid, geteuid}, }; use procfs::process::ProcState; -use super::notify_socket::NOTIFY_SOCKET; -use crate::container::{Container, Process}; +use super::{notify_socket::NOTIFY_SOCKET, process::clone_process, NotifyListener, Process}; +use crate::{ + container::Container, + utils::{Channel, Message, OzonecErr}, +}; use oci_spec::{ runtime::RuntimeConfig, state::{ContainerStatus, State}, @@ -81,6 +88,64 @@ impl LinuxContainer { if config.linux.is_none() { bail!("There is no linux specific configuration in config.json for Linux container"); } + if config.process.args.is_none() { + bail!("args in process is not set in config.json."); + } + Ok(()) + } + + fn do_first_stage( + &mut self, + process: &mut Process, + parent_channel: &Channel, + fst_stage_channel: &Channel, + notify_listener: Option, + ) -> Result<()> { + debug!("First stage process start"); + + fst_stage_channel + .receiver + .close() + .with_context(|| "Failed to close receiver end of first stage channel")?; + + // Spawn a child process to perform the second stage to initialize container. + let init_pid = clone_process("ozonec:[2:INIT]", || { + self.do_second_stage(process, parent_channel, notify_listener) + .with_context(|| "Second stage process encounters errors")?; + Ok(0) + })?; + + // Send the final container pid to the parent process. + parent_channel.send_init_pid(init_pid)?; + + debug!("First stage process exit"); + Ok(()) + } + + fn do_second_stage( + &mut self, + process: &mut Process, + parent_channel: &Channel, + notify_listener: Option, + ) -> Result<()> { + debug!("Second stage process start"); + + // Tell the parent process that the init process has been cloned. + parent_channel.send_container_created()?; + parent_channel + .sender + .close() + .with_context(|| "Failed to close sender of parent channel")?; + + // Listening on the notify socket to start container. + if let Some(listener) = notify_listener { + listener.wait_for_start_container()?; + listener + .close() + .with_context(|| "Failed to close notify socket")?; + } + + debug!("Container process exit"); Ok(()) } @@ -97,7 +162,7 @@ impl LinuxContainer { let proc_stat = proc .unwrap() .stat() - .with_context(|| format!("Failed to read /proc/{}/stat", self.pid))?; + .with_context(|| OzonecErr::ReadProcStat(self.pid))?; // If starttime is not the same, then pid is reused, and the original process has stopped. if proc_stat.starttime != self.start_time { return Ok(ContainerStatus::Stopped); @@ -162,6 +227,65 @@ impl Container for LinuxContainer { } fn create(&mut self, process: &mut Process) -> Result<()> { + // Create notify socket to notify the container process to start. + let notify_listener = if process.init { + Some(NotifyListener::new(PathBuf::from(&self.root))?) + } else { + None + }; + + // Create channels to communicate with child processes. + let parent_channel = Channel::::new() + .with_context(|| "Failed to create message channel for parent process")?; + let fst_stage_channel = Channel::::new()?; + // Set receivers timeout: 50ms. + parent_channel.receiver.set_timeout(50000)?; + fst_stage_channel.receiver.set_timeout(50000)?; + + // Spawn a child process to perform Stage 1. + let fst_stage_pid = clone_process("ozonec:[1:CHILD]", || { + self.do_first_stage( + process, + &parent_channel, + &fst_stage_channel, + notify_listener, + ) + .with_context(|| "First stage process encounters errors")?; + Ok(0) + })?; + + let init_pid = parent_channel + .recv_init_pid() + .with_context(|| "Failed to receive init pid")?; + parent_channel.recv_container_created()?; + parent_channel + .receiver + .close() + .with_context(|| "Failed to close receiver end of parent channel")?; + + self.pid = init_pid.as_raw(); + self.start_time = procfs::process::Process::new(self.pid) + .with_context(|| OzonecErr::ReadProcPid(self.pid))? + .stat() + .with_context(|| OzonecErr::ReadProcStat(self.pid))? + .starttime; + + match waitpid(fst_stage_pid, None) { + Ok(WaitStatus::Exited(_, 0)) => (), + Ok(WaitStatus::Exited(_, s)) => { + info!("First stage process exits with status: {}", s); + } + Ok(WaitStatus::Signaled(_, sig, _)) => { + info!("First stage process killed by signal: {}", sig) + } + Ok(_) => (), + Err(Errno::ECHILD) => { + info!("First stage process has already been reaped"); + } + Err(e) => { + bail!("Failed to waitpid for first stage process: {e}"); + } + } Ok(()) } diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index 9aed65f47..ef049ae88 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -12,6 +12,8 @@ mod container; mod notify_socket; +mod process; pub use container::LinuxContainer; pub use notify_socket::NotifyListener; +pub use process::Process; diff --git a/ozonec/src/container/process.rs b/ozonec/src/linux/process.rs similarity index 45% rename from ozonec/src/container/process.rs rename to ozonec/src/linux/process.rs index 962599aba..597842496 100644 --- a/ozonec/src/container/process.rs +++ b/ozonec/src/linux/process.rs @@ -11,17 +11,22 @@ // See the Mulan PSL v2 for more details. use std::{ + ffi::CString, io::{stderr, stdin, stdout}, os::fd::{AsRawFd, RawFd}, }; +use anyhow::{anyhow, Context, Result}; +use clone3::Clone3; +use libc::SIGCHLD; +use nix::unistd::{self, Pid}; + use oci_spec::process::Process as OciProcess; pub struct Process { pub stdin: Option, pub stdout: Option, pub stderr: Option, - pub term_master: Option, pub init: bool, pub tty: bool, pub oci: OciProcess, @@ -34,7 +39,6 @@ impl Process { stdout: None, stderr: None, tty: oci.terminal, - term_master: None, init, oci: oci.clone(), }; @@ -46,4 +50,45 @@ impl Process { } p } + + pub fn exec_program(&self) -> ! { + // It has been make sure that args is not None in validate_config(). + let args = &self.oci.args.as_ref().unwrap(); + // args don't have 0 byte in the middle such as "hello\0world". + let exec_bin = CString::new(args[0].as_str().as_bytes()).unwrap(); + let args: Vec = args + .iter() + .map(|s| CString::new(s.as_bytes()).unwrap_or_default()) + .collect(); + + let _ = unistd::execvp(&exec_bin, &args).map_err(|e| match e { + nix::Error::UnknownErrno => std::process::exit(-2), + _ => std::process::exit(e as i32), + }); + + unreachable!() + } +} + +// Clone a new child process. +pub fn clone_process Result>(child_name: &str, cb: F) -> Result { + let mut clone3 = Clone3::default(); + clone3.exit_signal(SIGCHLD as u64); + + // SAFETY: FFI call with valid arguments. + match unsafe { clone3.call().with_context(|| "Clone3() error")? } { + 0 => { + prctl::set_name(child_name) + .map_err(|e| anyhow!("Failed to set process name: errno {}", e))?; + let ret = match cb() { + Err(e) => { + eprintln!("Child process exit with errors: {:?}", e); + -1 + } + Ok(exit_code) => exit_code, + }; + std::process::exit(ret); + } + pid => Ok(Pid::from_raw(pid)), + } } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs new file mode 100644 index 000000000..2df6bb529 --- /dev/null +++ b/ozonec/src/utils/error.rs @@ -0,0 +1,21 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum OzonecErr { + #[error("Failed to access /proc/{0}")] + ReadProcPid(i32), + #[error("Failed to access /proc/{0}/status")] + ReadProcStat(i32), +} diff --git a/ozonec/src/utils/mod.rs b/ozonec/src/utils/mod.rs index 461581f63..0f37aae80 100644 --- a/ozonec/src/utils/mod.rs +++ b/ozonec/src/utils/mod.rs @@ -13,5 +13,7 @@ pub mod logger; mod channel; +mod error; pub use channel::{Channel, Message}; +pub use error::OzonecErr; -- Gitee From be2ec481fcb5e523766eb2b1e372702c70e40ce7 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 22 Aug 2024 12:02:52 +0800 Subject: [PATCH 2102/2187] ozonec/linux: Add namespace.rs --- ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/namespace.rs | 66 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 ozonec/src/linux/namespace.rs diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index ef049ae88..03c78b934 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. mod container; +mod namespace; mod notify_socket; mod process; diff --git a/ozonec/src/linux/namespace.rs b/ozonec/src/linux/namespace.rs new file mode 100644 index 000000000..5928e3d00 --- /dev/null +++ b/ozonec/src/linux/namespace.rs @@ -0,0 +1,66 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::HashMap; + +use anyhow::{Context, Result}; +use nix::{ + fcntl::{self, OFlag}, + sched::{setns, unshare, CloneFlags}, + sys::stat::Mode, + unistd, +}; +use oci_spec::linux::{Namespace, NamespaceType}; + +pub struct NsController { + pub namespaces: HashMap, +} + +impl TryFrom> for NsController { + type Error = anyhow::Error; + + fn try_from(namespaces: Vec) -> Result { + Ok(NsController { + namespaces: namespaces + .iter() + .map(|ns| match ns.ns_type.try_into() { + Ok(flag) => Ok((flag, ns.clone())), + Err(e) => Err(e), + }) + .collect::>>()? + .into_iter() + .collect(), + }) + } +} + +impl NsController { + pub fn set_namespace(&self, ns_type: NamespaceType) -> Result<()> { + if let Some(ns) = self.get(ns_type)? { + match ns.path.clone() { + Some(path) => { + let fd = fcntl::open(&path, OFlag::empty(), Mode::empty()) + .with_context(|| format!("fcntl error at opening {}", path.display()))?; + setns(fd, ns_type.try_into()?).with_context(|| "Failed to setns")?; + unistd::close(fd).with_context(|| "Close fcntl fd error")?; + } + None => unshare(ns_type.try_into()?).with_context(|| "Failed to unshare")?, + } + } + Ok(()) + } + + pub fn get(&self, ns_type: NamespaceType) -> Result> { + let clone_flags: CloneFlags = ns_type.try_into()?; + Ok(self.namespaces.get(&clone_flags)) + } +} -- Gitee From b417902b0e561c3220de7b714735c53a5787e64d Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 22 Aug 2024 12:05:12 +0800 Subject: [PATCH 2103/2187] ozonec/utils: Add OzonecErr::OpenFile(String) --- ozonec/src/container/state.rs | 6 ++++-- ozonec/src/utils/error.rs | 2 ++ ozonec/src/utils/logger.rs | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ozonec/src/container/state.rs b/ozonec/src/container/state.rs index 424e2d811..ab97cc364 100644 --- a/ozonec/src/container/state.rs +++ b/ozonec/src/container/state.rs @@ -25,6 +25,8 @@ use serde::{Deserialize, Serialize}; use oci_spec::{runtime::RuntimeConfig, state::State as OciState}; +use crate::utils::OzonecErr; + #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(rename_all = "camelCase")] pub struct State { @@ -79,8 +81,8 @@ impl State { .write(true) .create(true) .truncate(true) - .open(path) - .with_context(|| "Failed to open state file")?; + .open(&path) + .with_context(|| OzonecErr::OpenFile(path.to_string_lossy().to_string()))?; serde_json::to_writer(&state_file, self)?; Ok(()) } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 2df6bb529..46cb6f01a 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -18,4 +18,6 @@ pub enum OzonecErr { ReadProcPid(i32), #[error("Failed to access /proc/{0}/status")] ReadProcStat(i32), + #[error("Failed to open {0}")] + OpenFile(String), } diff --git a/ozonec/src/utils/logger.rs b/ozonec/src/utils/logger.rs index 17a35ff88..472650230 100644 --- a/ozonec/src/utils/logger.rs +++ b/ozonec/src/utils/logger.rs @@ -24,6 +24,8 @@ use anyhow::{Context, Result}; use log::{set_boxed_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record}; use nix::unistd::{getpid, gettid}; +use super::OzonecErr; + // Maximum size of log file is 100MB. const LOG_ROTATE_SIZE_MAX: usize = 100 * 1024 * 1024; // Logs are retained for seven days at most. @@ -91,7 +93,7 @@ fn open_log_file(path: &PathBuf) -> Result { .create(true) .mode(0o640) .open(path) - .with_context(|| "Failed to open log file") + .with_context(|| OzonecErr::OpenFile(path.to_string_lossy().to_string())) } fn formatted_time(seconds: i64) -> [i32; 6] { -- Gitee From c438901fbe377dcdcd7e83bfd331838a600087b4 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 22 Aug 2024 12:06:05 +0800 Subject: [PATCH 2104/2187] ozonec/linux: Add namespace support --- ozonec/oci_spec/src/linux.rs | 12 +- ozonec/src/linux/container.rs | 207 ++++++++++++++++++++++++++++++++-- ozonec/src/linux/process.rs | 28 ++++- ozonec/src/utils/channel.rs | 24 ++++ 4 files changed, 256 insertions(+), 15 deletions(-) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 234132ee7..4de386f31 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -16,9 +16,9 @@ use anyhow::{anyhow, Result}; use nix::sched::CloneFlags; use serde::{Deserialize, Serialize}; +/// Available Linux namespaces. #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, Hash)] #[serde(rename_all = "snake_case")] -/// Available Linux namespaces. pub enum NamespaceType { Cgroup = 0x0200_0000, Ipc = 0x0800_0000, @@ -62,8 +62,8 @@ impl From for String { } } -#[derive(Serialize, Deserialize, Debug, Clone)] /// Namespaces. +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Namespace { /// Namespace type. #[serde(rename = "type")] @@ -73,9 +73,9 @@ pub struct Namespace { pub path: Option, } +/// UID/GID mapping. #[allow(non_snake_case)] #[derive(Serialize, Deserialize, Debug, Clone)] -/// User namespace mappings. pub struct IdMapping { /// Starting uid/gid in the container. pub containerID: u32, @@ -85,14 +85,14 @@ pub struct IdMapping { pub size: u32, } -#[derive(Serialize, Deserialize, Debug, Clone)] /// Offset for Time Namespace. +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct TimeOffsets { - #[serde(skip_serializing_if = "Option::is_none")] /// Offset of clock (in seconds) in the container. - pub secs: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub secs: Option, /// Offset of clock (in nanoseconds) in the container. + #[serde(skip_serializing_if = "Option::is_none")] pub nanosecs: Option, } diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 20a9fd778..4be88d302 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -12,13 +12,14 @@ use std::{ collections::HashMap, - fs::{canonicalize, create_dir_all}, + fs::{canonicalize, create_dir_all, OpenOptions}, + io::Write, path::{Path, PathBuf}, time::SystemTime, }; use anyhow::{anyhow, bail, Context, Result}; -use libc::pid_t; +use libc::{c_char, pid_t, setdomainname}; use log::{debug, error, info}; use nix::{ errno::Errno, @@ -26,16 +27,21 @@ use nix::{ signal::Signal, wait::{waitpid, WaitStatus}, }, - unistd::{chown, getegid, geteuid}, + unistd::{chown, getegid, geteuid, sethostname, Gid, Pid, Uid}, }; +use prctl::set_dumpable; use procfs::process::ProcState; -use super::{notify_socket::NOTIFY_SOCKET, process::clone_process, NotifyListener, Process}; +use super::{ + namespace::NsController, notify_socket::NOTIFY_SOCKET, process::clone_process, NotifyListener, + Process, +}; use crate::{ container::Container, utils::{Channel, Message, OzonecErr}, }; use oci_spec::{ + linux::{IdMapping, NamespaceType}, runtime::RuntimeConfig, state::{ContainerStatus, State}, }; @@ -103,11 +109,16 @@ impl LinuxContainer { ) -> Result<()> { debug!("First stage process start"); + self.set_user_namespace(parent_channel, fst_stage_channel, process)?; + fst_stage_channel .receiver .close() .with_context(|| "Failed to close receiver end of first stage channel")?; + // New pid namespace goes intto effect in cloned child processes. + self.set_pid_namespace()?; + // Spawn a child process to perform the second stage to initialize container. let init_pid = clone_process("ozonec:[2:INIT]", || { self.do_second_stage(process, parent_channel, notify_listener) @@ -130,6 +141,10 @@ impl LinuxContainer { ) -> Result<()> { debug!("Second stage process start"); + self.set_rest_namespaces()?; + + process.set_additional_gids()?; + // Tell the parent process that the init process has been cloned. parent_channel.send_container_created()?; parent_channel @@ -144,9 +159,7 @@ impl LinuxContainer { .close() .with_context(|| "Failed to close notify socket")?; } - - debug!("Container process exit"); - Ok(()) + process.exec_program(); } fn container_status(&self) -> Result { @@ -179,6 +192,175 @@ impl LinuxContainer { } } } + + fn ns_controller(&self) -> Result { + Ok(self + .config + .linux + .as_ref() + .unwrap() + .namespaces + .clone() + .try_into()?) + } + + fn set_user_namespace( + &self, + parent_channel: &Channel, + fst_stage_channel: &Channel, + process: &Process, + ) -> Result<()> { + let ns_controller: NsController = self.ns_controller()?; + + if let Some(ns) = ns_controller.get(NamespaceType::User)? { + ns_controller + .set_namespace(NamespaceType::User) + .with_context(|| "Failed to set user namespace")?; + + if ns.path.is_none() { + // Child process needs to be dumpable, otherwise the parent process is not + // allowed to write the uid/gid mappings. + set_dumpable(true).map_err(|e| anyhow!("Failed to set process dumpable: {e}"))?; + parent_channel + .send_id_mappings() + .with_context(|| "Failed to send id mappings")?; + fst_stage_channel + .recv_id_mappings_done() + .with_context(|| "Failed to receive id mappings done")?; + set_dumpable(false) + .map_err(|e| anyhow!("Failed to set process undumpable: {e}"))?; + } + + // After UID/GID mappings are configured, ozonec wants to make sure continue as + // the root user inside the new user namespace. This is required because the + // process of configuring the container process will require root, even though + // the root in the user namespace is likely mapped to an non-privileged user. + process.set_id(Gid::from_raw(0), Uid::from_raw(0))?; + } + Ok(()) + } + + fn is_namespace_set(&self, ns_type: NamespaceType) -> Result { + let ns_controller: NsController = self.ns_controller()?; + Ok(ns_controller.get(ns_type)?.is_some()) + } + + fn set_pid_namespace(&self) -> Result<()> { + let ns_controller = self.ns_controller()?; + + if ns_controller.get(NamespaceType::Pid)?.is_some() { + ns_controller + .set_namespace(NamespaceType::Pid) + .with_context(|| "Failed to set pid namespace")?; + } + Ok(()) + } + + fn set_rest_namespaces(&self) -> Result<()> { + let ns_config = &self.config.linux.as_ref().unwrap().namespaces; + let ns_controller: NsController = ns_config.clone().try_into()?; + + for ns in ns_config { + match ns.ns_type { + // User namespace and pid namespace have been set in the first stage. + // Mount namespace is going to be set later to avoid failure with + // existed namespaces. + NamespaceType::User | NamespaceType::Pid | NamespaceType::Mount => (), + _ => ns_controller.set_namespace(ns.ns_type).with_context(|| { + format!( + "Failed to set {} namespace", + >::into(ns.ns_type) + ) + })?, + } + + if ns.ns_type == NamespaceType::Uts && ns.path.is_none() { + if let Some(hostname) = &self.config.hostname { + sethostname(hostname).with_context(|| "Failed to set hostname")?; + } + if let Some(domainname) = &self.config.domainname { + let errno; + + // SAFETY: FFI call with valid arguments. + match unsafe { + setdomainname( + domainname.as_bytes().as_ptr() as *const c_char, + domainname.len(), + ) + } { + 0 => return Ok(()), + -1 => errno = nix::Error::last(), + _ => errno = nix::Error::UnknownErrno, + } + bail!("Failed to set domainname: {}", errno); + } + } + } + + ns_controller + .set_namespace(NamespaceType::Mount) + .with_context(|| "Failed to set mount namespace")?; + Ok(()) + } + + fn set_id_mappings( + &self, + parent_channel: &Channel, + fst_stage_channel: &Channel, + fst_stage_pid: &Pid, + ) -> Result<()> { + parent_channel + .recv_id_mappings() + .with_context(|| "Failed to receive id mappings")?; + LinuxContainer::set_groups(fst_stage_pid, false) + .with_context(|| "Failed to disable setting groups")?; + + if let Some(linux) = self.config.linux.as_ref() { + if let Some(uid_mappings) = linux.uidMappings.as_ref() { + self.write_id_mapping(uid_mappings, fst_stage_pid, "uid_map")?; + } + if let Some(gid_mappings) = linux.gidMappings.as_ref() { + self.write_id_mapping(gid_mappings, fst_stage_pid, "gid_map")?; + } + } + + fst_stage_channel + .send_id_mappings_done() + .with_context(|| "Failed to send id mapping done")?; + fst_stage_channel + .sender + .close() + .with_context(|| "Failed to close fst_stage_channel sender")?; + Ok(()) + } + + fn write_id_mapping(&self, mappings: &Vec, pid: &Pid, file: &str) -> Result<()> { + let path = format!("/proc/{}/{}", pid.as_raw().to_string(), file); + let mut opened_file = OpenOptions::new() + .write(true) + .open(&path) + .with_context(|| OzonecErr::OpenFile(path))?; + let mut id_mappings = String::from(""); + + for m in mappings { + let mapping = format!("{} {} {}\n", m.containerID, m.hostID, m.size); + id_mappings = id_mappings + &mapping; + } + opened_file + .write_all(&id_mappings.as_bytes()) + .with_context(|| "Failed to write id mappings")?; + Ok(()) + } + + fn set_groups(pid: &Pid, allow: bool) -> Result<()> { + let path = format!("/proc/{}/setgroups", pid.as_raw().to_string()); + if allow == true { + std::fs::write(&path, "allow")? + } else { + std::fs::write(&path, "deny")? + } + Ok(()) + } } impl Container for LinuxContainer { @@ -234,6 +416,13 @@ impl Container for LinuxContainer { None }; + // Make the process undumpable to avoid various race conditions that could cause + // processes in namespaces to join to access host resources (or execute code). + if !self.config.linux.as_ref().unwrap().namespaces.is_empty() { + prctl::set_dumpable(false) + .map_err(|e| anyhow!("Failed to set process undumpable: {}", e))?; + } + // Create channels to communicate with child processes. let parent_channel = Channel::::new() .with_context(|| "Failed to create message channel for parent process")?; @@ -254,6 +443,10 @@ impl Container for LinuxContainer { Ok(0) })?; + if self.is_namespace_set(NamespaceType::User)? { + self.set_id_mappings(&parent_channel, &fst_stage_channel, &fst_stage_pid)?; + } + let init_pid = parent_channel .recv_init_pid() .with_context(|| "Failed to receive init pid")?; diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 597842496..d6dc61ef1 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -12,14 +12,15 @@ use std::{ ffi::CString, + fs::read_to_string, io::{stderr, stdin, stdout}, os::fd::{AsRawFd, RawFd}, }; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use clone3::Clone3; use libc::SIGCHLD; -use nix::unistd::{self, Pid}; +use nix::unistd::{self, setresgid, setresuid, Gid, Pid, Uid}; use oci_spec::process::Process as OciProcess; @@ -51,6 +52,29 @@ impl Process { p } + pub fn set_additional_gids(&self) -> Result<()> { + if let Some(additional_gids) = &self.oci.user.additionalGids { + let setgroups = read_to_string("proc/self/setgroups") + .with_context(|| "Failed to read setgroups")?; + if setgroups.trim() == "deny" { + bail!("Cannot set additional gids as setgroup is desabled"); + } + + let gids: Vec = additional_gids + .iter() + .map(|gid| Gid::from_raw(*gid)) + .collect(); + unistd::setgroups(&gids).with_context(|| "Failed to set additional gids")?; + } + Ok(()) + } + + pub fn set_id(&self, gid: Gid, uid: Uid) -> Result<()> { + setresgid(gid, gid, gid).with_context(|| "Failed to setresgid")?; + setresuid(uid, uid, uid).with_context(|| "Failed to setresuid")?; + Ok(()) + } + pub fn exec_program(&self) -> ! { // It has been make sure that args is not None in validate_config(). let args = &self.oci.args.as_ref().unwrap(); diff --git a/ozonec/src/utils/channel.rs b/ozonec/src/utils/channel.rs index 4e303d104..b74c3a786 100644 --- a/ozonec/src/utils/channel.rs +++ b/ozonec/src/utils/channel.rs @@ -175,6 +175,18 @@ impl Channel { .with_context(|| "Failed to send created message to parent process") } + pub fn recv_id_mappings(&self) -> Result<()> { + let msg = self.receiver.recv()?; + match msg { + Message::IdMappingStart => Ok(()), + _ => bail!("Expect receiving IdMappingStart, but got {:?}", msg), + } + } + + pub fn send_id_mappings(&self) -> Result<()> { + self.sender.send(Message::IdMappingStart) + } + pub fn recv_init_pid(&self) -> Result { let msg = self.receiver.recv()?; match msg { @@ -183,6 +195,18 @@ impl Channel { } } + pub fn recv_id_mappings_done(&self) -> Result<()> { + let msg = self.receiver.recv()?; + match msg { + Message::IdMappingDone => Ok(()), + _ => bail!("Expect receiving IdMappingDone, but got {:?}", msg), + } + } + + pub fn send_id_mappings_done(&self) -> Result<()> { + self.sender.send(Message::IdMappingDone) + } + pub fn send_init_pid(&self, pid: Pid) -> Result<()> { self.sender .send(Message::InitReady(pid.as_raw())) -- Gitee From 8cb59f5f21bd83a433149709962abaca7051702f Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 22 Aug 2024 17:21:08 +0800 Subject: [PATCH 2105/2187] ozonec/linux: Add mount.rs --- ozonec/src/linux/container.rs | 6 +- ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/mount.rs | 195 ++++++++++++++++++++++++++++++++++ ozonec/src/utils/error.rs | 2 + 4 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 ozonec/src/linux/mount.rs diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 4be88d302..36f819e32 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -72,10 +72,8 @@ impl LinuxContainer { *exist = true; bail!("Container {} already exists", id); } - create_dir_all(container_dir.as_str()).map_err(|e| { - error!("Failed to create container directory: {}", e); - anyhow!(e).context("Failed to create container directory") - })?; + create_dir_all(container_dir.as_str()) + .with_context(|| OzonecErr::CreateDir(container_dir.clone()))?; chown(container_dir.as_str(), Some(geteuid()), Some(getegid())) .with_context(|| "Failed to chown container directory")?; diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index 03c78b934..c4374b3a6 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. mod container; +mod mount; mod namespace; mod notify_socket; mod process; diff --git a/ozonec/src/linux/mount.rs b/ozonec/src/linux/mount.rs new file mode 100644 index 000000000..a06efffe8 --- /dev/null +++ b/ozonec/src/linux/mount.rs @@ -0,0 +1,195 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::{self, canonicalize, create_dir_all, read_to_string, File}, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, Context, Result}; +use nix::mount::MsFlags; + +use oci_spec::runtime::Mount as OciMount; + +use crate::utils::OzonecErr; + +pub struct Mount { + rootfs: PathBuf, +} + +impl Mount { + pub fn new(rootfs: &PathBuf) -> Self { + Self { + rootfs: rootfs.clone(), + } + } + + fn get_mount_flag_data(&self, mount: &OciMount) -> (MsFlags, String) { + let mut ms_flags = MsFlags::empty(); + let mut data = Vec::new(); + + if let Some(options) = &mount.options { + for option in options { + if let Some((clear, flag)) = match option.as_str() { + "defaults" => Some((false, MsFlags::empty())), + "ro" => Some((false, MsFlags::MS_RDONLY)), + "rw" => Some((true, MsFlags::MS_RDONLY)), + "suid" => Some((true, MsFlags::MS_NOSUID)), + "nosuid" => Some((false, MsFlags::MS_NOSUID)), + "dev" => Some((true, MsFlags::MS_NODEV)), + "nodev" => Some((false, MsFlags::MS_NODEV)), + "exec" => Some((true, MsFlags::MS_NOEXEC)), + "noexec" => Some((false, MsFlags::MS_NOEXEC)), + "sync" => Some((false, MsFlags::MS_SYNCHRONOUS)), + "async" => Some((true, MsFlags::MS_SYNCHRONOUS)), + "dirsync" => Some((false, MsFlags::MS_DIRSYNC)), + "remount" => Some((false, MsFlags::MS_REMOUNT)), + "mand" => Some((false, MsFlags::MS_MANDLOCK)), + "nomand" => Some((true, MsFlags::MS_MANDLOCK)), + "atime" => Some((true, MsFlags::MS_NOATIME)), + "noatime" => Some((false, MsFlags::MS_NOATIME)), + "diratime" => Some((true, MsFlags::MS_NODIRATIME)), + "nodiratime" => Some((false, MsFlags::MS_NODIRATIME)), + "bind" => Some((false, MsFlags::MS_BIND)), + "rbind" => Some((false, MsFlags::MS_BIND | MsFlags::MS_REC)), + "unbindable" => Some((false, MsFlags::MS_UNBINDABLE)), + "runbindable" => Some((false, MsFlags::MS_UNBINDABLE | MsFlags::MS_REC)), + "private" => Some((false, MsFlags::MS_PRIVATE)), + "rprivate" => Some((false, MsFlags::MS_PRIVATE | MsFlags::MS_REC)), + "shared" => Some((false, MsFlags::MS_SHARED)), + "rshared" => Some((false, MsFlags::MS_SHARED | MsFlags::MS_REC)), + "slave" => Some((false, MsFlags::MS_SLAVE)), + "rslave" => Some((false, MsFlags::MS_SLAVE | MsFlags::MS_REC)), + "relatime" => Some((false, MsFlags::MS_RELATIME)), + "norelatime" => Some((true, MsFlags::MS_RELATIME)), + "strictatime" => Some((false, MsFlags::MS_STRICTATIME)), + "nostrictatime" => Some((true, MsFlags::MS_STRICTATIME)), + _ => None, + } { + if clear { + ms_flags &= !flag; + } else { + ms_flags |= flag; + } + continue; + } + data.push(option.as_str()); + } + } + (ms_flags, data.join(",")) + } + + fn do_one_mount(&self, mount: &OciMount, label: &Option) -> Result<()> { + let fs_type = mount.fs_type.as_deref(); + let (flag, mut data) = self.get_mount_flag_data(mount); + + if let Some(label) = label { + if fs_type != Some("proc") && fs_type != Some("sysfs") { + match data.is_empty() { + true => data = format!("context=\"{}\"", label), + false => data = format!("{},context=\"{}\"", data, label), + } + } + } + + // If destination begins with "/", then ignore the first "/". + let binding = self.rootfs.join(&mount.destination[1..]); + let dest_path = Path::new(&binding); + let binding = &mount + .source + .clone() + .ok_or(anyhow!("Mount source not set"))?; + let source = Path::new(&binding); + let canonicalized; + let src_path = match fs_type { + Some("bind") => { + canonicalized = canonicalize(source) + .with_context(|| format!("Failed to canonicalize {}", source.display()))?; + canonicalized.as_path() + } + _ => source, + }; + + match fs_type { + Some("bind") => { + let dir = if src_path.is_file() { + dest_path.parent().ok_or(anyhow!( + "Failed to get parent directory: {}", + dest_path.display() + ))? + } else { + dest_path + }; + + create_dir_all(dir) + .with_context(|| OzonecErr::CreateDir(dir.to_string_lossy().to_string()))?; + if src_path.is_file() && !dest_path.exists() { + File::create(dest_path) + .with_context(|| format!("Failed to create {}", dest_path.display()))?; + } + + nix::mount::mount( + Some(src_path), + dest_path, + None::<&str>, + MsFlags::MS_BIND | MsFlags::MS_REC, + Some(data.as_str()), + )?; + } + _ => { + create_dir_all(&dest_path).with_context(|| { + OzonecErr::CreateDir(dest_path.to_string_lossy().to_string()) + })?; + // Sysfs doesn't support duplicate mounting to one directory. + if self.is_mounted_sysfs_dir(&dest_path.to_string_lossy().to_string()) { + nix::mount::umount(dest_path) + .with_context(|| format!("Failed to umount {}", dest_path.display()))?; + } + nix::mount::mount( + Some(src_path), + dest_path, + fs_type, + flag, + Some(data.as_str()), + )?; + } + } + + Ok(()) + } + + fn is_mounted_sysfs_dir(&self, path: &str) -> bool { + if let Ok(metadata) = fs::metadata(path) { + if metadata.file_type().is_dir() { + if let Ok(mounts) = read_to_string("/proc/mounts") { + for line in mounts.lines() { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 3 && parts[1] == path && parts[2] == "sysfs" { + return true; + } + } + } + } + } + false + } + + pub fn do_mounts(&self, mounts: &Vec, label: &Option) -> Result<()> { + for mount in mounts { + match mount.fs_type.as_deref() { + Some("cgroup") => (), + _ => self.do_one_mount(mount, label)?, + } + } + Ok(()) + } +} diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 46cb6f01a..24c38ac66 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -20,4 +20,6 @@ pub enum OzonecErr { ReadProcStat(i32), #[error("Failed to open {0}")] OpenFile(String), + #[error("Failed to create directory {0}")] + CreateDir(String), } -- Gitee From 1c27ad10975761156756dee47b407bb77eea1a01 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 22 Aug 2024 18:31:56 +0800 Subject: [PATCH 2106/2187] ozonec/linux: Add support for mounting cgroups --- ozonec/src/linux/mount.rs | 112 ++++++++++++++++++++++++++++++++++++-- ozonec/src/utils/error.rs | 6 ++ 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/ozonec/src/linux/mount.rs b/ozonec/src/linux/mount.rs index a06efffe8..972be73ab 100644 --- a/ozonec/src/linux/mount.rs +++ b/ozonec/src/linux/mount.rs @@ -11,16 +11,25 @@ // See the Mulan PSL v2 for more details. use std::{ + collections::HashMap, fs::{self, canonicalize, create_dir_all, read_to_string, File}, path::{Path, PathBuf}, }; -use anyhow::{anyhow, Context, Result}; -use nix::mount::MsFlags; +use anyhow::{anyhow, bail, Context, Result}; +use nix::{ + mount::MsFlags, + sys::statfs::{statfs, CGROUP2_SUPER_MAGIC}, +}; +use crate::utils::OzonecErr; use oci_spec::runtime::Mount as OciMount; +use procfs::process::{MountInfo, Process}; -use crate::utils::OzonecErr; +enum CgroupType { + CgroupV1, + CgroupV2, +} pub struct Mount { rootfs: PathBuf, @@ -143,7 +152,8 @@ impl Mount { None::<&str>, MsFlags::MS_BIND | MsFlags::MS_REC, Some(data.as_str()), - )?; + ) + .with_context(|| OzonecErr::Mount(src_path.to_string_lossy().to_string()))?; } _ => { create_dir_all(&dest_path).with_context(|| { @@ -160,7 +170,8 @@ impl Mount { fs_type, flag, Some(data.as_str()), - )?; + ) + .with_context(|| OzonecErr::Mount(src_path.to_string_lossy().to_string()))?; } } @@ -186,10 +197,99 @@ impl Mount { pub fn do_mounts(&self, mounts: &Vec, label: &Option) -> Result<()> { for mount in mounts { match mount.fs_type.as_deref() { - Some("cgroup") => (), + Some("cgroup") => match self.cgroup_type()? { + CgroupType::CgroupV1 => self + .do_cgroup_mount(mount) + .with_context(|| "Failed to do cgroup mount")?, + CgroupType::CgroupV2 => bail!("Cgroup V2 is not supported now"), + }, _ => self.do_one_mount(mount, label)?, } } Ok(()) } + + fn do_cgroup_mount(&self, mount: &OciMount) -> Result<()> { + // If destination begins with "/", then ignore the first "/". + let binding = self.rootfs.join(&mount.destination[1..]); + let mut dest = Path::new(&binding); + nix::mount::mount( + Some("tmpfs"), + dest, + Some("tmpfs"), + MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV, + None::<&str>, + ) + .with_context(|| OzonecErr::Mount(String::from("tmpfs")))?; + + let process = Process::myself().with_context(|| OzonecErr::AccessProcSelf)?; + let mnt_info: Vec = + process.mountinfo().with_context(|| OzonecErr::GetMntInfo)?; + + let proc_cgroups: HashMap = process + .cgroups() + .with_context(|| "Failed to get cgroups belong to")? + .into_iter() + .map(|cgroup| (cgroup.controllers.join(","), cgroup.pathname)) + .collect(); + // Get all of available cgroup mount points. + let host_cgroups: Vec = mnt_info + .into_iter() + .filter(|m| m.fs_type == "cgroup") + .map(|m| m.mount_point) + .collect(); + for cg_path in host_cgroups { + let binding = self.rootfs.join( + cg_path + .strip_prefix("/") + .with_context(|| format!("Strip {} error", cg_path.display()))?, + ); + dest = Path::new(&binding); + let cg = cg_path + .file_name() + .ok_or(anyhow!("Failed to get controller file"))? + .to_str() + .ok_or(anyhow!( + "Convert {:?} to string error", + cg_path.file_name().unwrap() + ))?; + let proc_cg_key = if cg == "systemd" { + String::from("systemd") + } else { + cg.to_string() + }; + + if let Some(src) = proc_cgroups.get(&proc_cg_key) { + let source = cg_path.join(&src[1..]); + if !dest.exists() { + create_dir_all(dest).with_context(|| { + OzonecErr::CreateDir(dest.to_string_lossy().to_string()) + })?; + } + nix::mount::mount( + Some(&source), + dest, + Some("bind"), + MsFlags::MS_BIND | MsFlags::MS_REC, + None::<&str>, + ) + .with_context(|| OzonecErr::Mount(source.to_string_lossy().to_string()))?; + } + } + + Ok(()) + } + + fn cgroup_type(&self) -> Result { + let cgroup_path = Path::new("/sys/fs/cgroup"); + if !cgroup_path.exists() { + bail!("/sys/fs/cgroup doesn't exist."); + } + + let st = statfs(cgroup_path).with_context(|| "statfs /sys/fs/cgroup error")?; + if st.filesystem_type() == CGROUP2_SUPER_MAGIC { + return Ok(CgroupType::CgroupV2); + } + Ok(CgroupType::CgroupV1) + } } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 24c38ac66..9c00bd13b 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -22,4 +22,10 @@ pub enum OzonecErr { OpenFile(String), #[error("Failed to create directory {0}")] CreateDir(String), + #[error("Failed to mount {0}")] + Mount(String), + #[error("Failed to access /proc/self")] + AccessProcSelf, + #[error("Failed to get mountinfo")] + GetMntInfo, } -- Gitee From 4bdec1830e31bd81793aa43244a9a7961ff1749b Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 23 Aug 2024 09:21:23 +0800 Subject: [PATCH 2107/2187] ozonec/linux: Add device.rs --- ozonec/src/linux/device.rs | 230 +++++++++++++++++++++++++++++++++++++ ozonec/src/linux/mod.rs | 1 + 2 files changed, 231 insertions(+) create mode 100644 ozonec/src/linux/device.rs diff --git a/ozonec/src/linux/device.rs b/ozonec/src/linux/device.rs new file mode 100644 index 000000000..baa67ab17 --- /dev/null +++ b/ozonec/src/linux/device.rs @@ -0,0 +1,230 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::{create_dir_all, remove_file, File}, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, bail, Context, Result}; +use nix::{ + mount::MsFlags, + sys::stat::{makedev, mknod, Mode, SFlag}, + unistd::{chown, Gid, Uid}, +}; +use oci_spec::linux::Device as OciDevice; + +use crate::utils::OzonecErr; + +pub struct Device { + rootfs: PathBuf, +} + +impl Device { + pub fn new(rootfs: PathBuf) -> Self { + Self { rootfs } + } + + fn default_devices(&self) -> Vec { + vec![ + DeviceInfo { + path: self.rootfs.join("dev/null"), + dev_type: "c".to_string(), + major: 1, + minor: 3, + file_mode: Some(0o666u32), + uid: None, + gid: None, + }, + DeviceInfo { + path: self.rootfs.join("dev/zero"), + dev_type: "c".to_string(), + major: 1, + minor: 5, + file_mode: Some(0o666u32), + uid: None, + gid: None, + }, + DeviceInfo { + path: self.rootfs.join("dev/full"), + dev_type: "c".to_string(), + major: 1, + minor: 7, + file_mode: Some(0o666u32), + uid: None, + gid: None, + }, + DeviceInfo { + path: self.rootfs.join("dev/random"), + dev_type: "c".to_string(), + major: 1, + minor: 8, + file_mode: Some(0o666u32), + uid: None, + gid: None, + }, + DeviceInfo { + path: self.rootfs.join("dev/urandom"), + dev_type: "c".to_string(), + major: 1, + minor: 9, + file_mode: Some(0o666u32), + uid: None, + gid: None, + }, + DeviceInfo { + path: self.rootfs.join("dev/tty"), + dev_type: "c".to_string(), + major: 5, + minor: 0, + file_mode: Some(0o666u32), + uid: None, + gid: None, + }, + ] + } + + fn create_device_dir(&self, path: &PathBuf) -> Result<()> { + let dir = Path::new(path).parent().ok_or(anyhow!( + "Failed to get parent directory: {}", + path.display() + ))?; + if !dir.exists() { + create_dir_all(dir) + .with_context(|| OzonecErr::CreateDir(dir.to_string_lossy().to_string()))?; + } + Ok(()) + } + + fn get_sflag(&self, dev_type: &str) -> Result { + let sflag = match dev_type { + "c" => SFlag::S_IFCHR, + "b" => SFlag::S_IFBLK, + "u" => SFlag::S_IFCHR, + "p" => SFlag::S_IFIFO, + _ => bail!("Not supported device type: {}", dev_type), + }; + Ok(sflag) + } + + fn bind_device(&self, dev: &DeviceInfo) -> Result<()> { + self.create_device_dir(&dev.path)?; + + let binding = dev.path.to_string_lossy().to_string(); + let stripped_path = binding + .strip_prefix(&self.rootfs.to_string_lossy().to_string()) + .ok_or(anyhow!("Invalid device path"))?; + let src_path = PathBuf::from(stripped_path); + + if !dev.path.exists() { + File::create(&dev.path) + .with_context(|| format!("Failed to create {}", dev.path.display()))?; + } + nix::mount::mount( + Some(&src_path), + &dev.path, + Some("bind"), + MsFlags::MS_BIND, + None::<&str>, + ) + .with_context(|| OzonecErr::Mount(stripped_path.to_string()))?; + + Ok(()) + } + + fn mknod_device(&self, dev: &DeviceInfo) -> Result<()> { + self.create_device_dir(&dev.path)?; + + let sflag = self.get_sflag(&dev.dev_type)?; + let device = makedev(dev.major as u64, dev.minor as u64); + mknod( + &dev.path, + sflag, + Mode::from_bits_truncate(dev.file_mode.unwrap_or(0)), + device, + )?; + chown( + &dev.path, + dev.uid.map(Uid::from_raw), + dev.gid.map(Gid::from_raw), + ) + .with_context(|| "Failed to chown")?; + + Ok(()) + } + + pub fn create_default_devices(&self, mknod: bool) -> Result<()> { + let default_devs = self.default_devices(); + for dev in default_devs { + if mknod { + self.mknod_device(&dev) + .with_context(|| format!("Failed to mknod device: {}", dev.path.display()))?; + } else { + self.bind_device(&dev) + .with_context(|| format!("Failed to bind device: {}", dev.path.display()))?; + } + } + Ok(()) + } + + pub fn is_default_device(&self, dev: &OciDevice) -> bool { + for d in &self.default_devices() { + let path = self.rootfs.join(&dev.path.clone()[1..]); + if path == d.path { + return true; + } + } + return false; + } + + pub fn delete_device(&self, dev: &OciDevice) -> Result<()> { + let path = self.rootfs.join(&dev.path.clone()[1..]); + remove_file(&path).with_context(|| format!("Failed to delete {}", path.display()))?; + Ok(()) + } + + pub fn create_device(&self, dev: &OciDevice, mknod: bool) -> Result<()> { + let path = self.rootfs.join(&dev.path.clone()[1..]); + let major = dev + .major + .ok_or(anyhow!("major not set for device {}", dev.path))?; + let minor = dev + .minor + .ok_or(anyhow!("minor not set for device {}", dev.path))?; + let dev_info = DeviceInfo { + path, + dev_type: dev.dev_type.clone(), + major, + minor, + file_mode: dev.fileMode, + uid: dev.uid, + gid: dev.gid, + }; + + if mknod { + self.mknod_device(&dev_info)?; + } else { + self.bind_device(&dev_info)?; + } + Ok(()) + } +} + +struct DeviceInfo { + path: PathBuf, + dev_type: String, + major: i64, + minor: i64, + file_mode: Option, + uid: Option, + gid: Option, +} diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index c4374b3a6..ea85d5981 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. mod container; +mod device; mod mount; mod namespace; mod notify_socket; -- Gitee From 40c9a12623c76e8c65faae026a308bb936cfcbb4 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 23 Aug 2024 11:57:28 +0800 Subject: [PATCH 2108/2187] ozonec/linux: Add rootfs.rs --- ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/rootfs.rs | 231 +++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 ozonec/src/linux/rootfs.rs diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index ea85d5981..84fff70c5 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -16,6 +16,7 @@ mod mount; mod namespace; mod notify_socket; mod process; +mod rootfs; pub use container::LinuxContainer; pub use notify_socket::NotifyListener; diff --git a/ozonec/src/linux/rootfs.rs b/ozonec/src/linux/rootfs.rs new file mode 100644 index 000000000..ac67d3251 --- /dev/null +++ b/ozonec/src/linux/rootfs.rs @@ -0,0 +1,231 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + fs::remove_file, + os::unix::fs::symlink, + path::{Path, PathBuf}, +}; + +use anyhow::{bail, Context, Result}; +use nix::{ + fcntl::{open, OFlag}, + mount::{umount2, MntFlags, MsFlags}, + sys::stat::{umask, Mode}, + unistd::{chroot, fchdir, pivot_root}, + NixPath, +}; +use procfs::process::Process; + +use super::{device::Device, mount::Mount}; +use crate::utils::OzonecErr; +use oci_spec::{ + linux::Device as OciDevice, + runtime::{Mount as OciMount, RuntimeConfig}, +}; + +pub struct Rootfs { + pub path: PathBuf, + propagation_flags: MsFlags, + mounts: Vec, + // Should we mknod the device or bind one. + mknod_device: bool, + devices: Vec, +} + +impl Rootfs { + pub fn new( + path: PathBuf, + propagation: Option, + mounts: Vec, + mknod_device: bool, + devices: Vec, + ) -> Result { + if !path.exists() { + bail!("Rootfs directory not exist"); + } + + let propagation_flags = Self::get_mount_flags(propagation)?; + Ok(Self { + path, + propagation_flags, + mounts, + mknod_device, + devices, + }) + } + + fn get_mount_flags(propagation: Option) -> Result { + let flags = match propagation.as_deref() { + Some("shared") => MsFlags::MS_SHARED, + Some("private") => MsFlags::MS_PRIVATE, + Some("slave") => MsFlags::MS_SLAVE, + Some("unbindable") => MsFlags::MS_UNBINDABLE, + Some(_) => bail!("Invalid rootfsPropagation"), + None => MsFlags::MS_REC | MsFlags::MS_SLAVE, + }; + Ok(flags) + } + + fn set_propagation(&self) -> Result<()> { + nix::mount::mount( + None::<&str>, + Path::new("/"), + None::<&str>, + self.propagation_flags, + None::<&str>, + ) + .with_context(|| "Failed to set rootfs mount propagation")?; + Ok(()) + } + + fn mount(&self) -> Result<()> { + nix::mount::mount( + Some(&self.path), + &self.path, + None::<&str>, + MsFlags::MS_BIND | MsFlags::MS_REC, + None::<&str>, + )?; + Ok(()) + } + + fn make_parent_mount_private(&self) -> Result<()> { + let process = Process::myself().with_context(|| OzonecErr::AccessProcSelf)?; + let mount_info = process.mountinfo().with_context(|| OzonecErr::GetMntInfo)?; + + match mount_info + .into_iter() + .filter(|m| self.path.starts_with(&m.mount_point) && m.mount_point != self.path) + .map(|m| m.mount_point) + .max_by_key(|m| m.len()) + .as_ref() + { + Some(m) => { + nix::mount::mount(Some(m), m, None::<&str>, MsFlags::MS_PRIVATE, None::<&str>)? + } + None => (), + } + Ok(()) + } + + // OCI spec requires runtime MUST create the following symlinks if the source file exists after + // processing mounts: + // dev/fd -> /proc/self/fd + // dev/stdin -> /proc/self/fd/0 + // dev/stdout -> /proc/self/fd/1 + // dev/stderr -> /proc/self/fd/2 + fn set_default_symlinks(&self) -> Result<()> { + let link_pairs = vec![ + ((&self.path).join("dev/fd"), "/proc/self/fd"), + ((&self.path).join("dev/stdin"), "/proc/self/fd/0"), + ((&self.path).join("dev/stdout"), "/proc/self/fd/1"), + ((&self.path).join("dev/stderr"), "/proc/self/fd/2"), + ]; + + for pair in link_pairs { + let cloned_pair = pair.clone(); + symlink(pair.1, pair.0).with_context(|| { + format!( + "Failed to create symlink {} -> {}", + cloned_pair.0.display(), + cloned_pair.1 + ) + })?; + } + Ok(()) + } + + fn do_mounts(&self, config: &RuntimeConfig) -> Result<()> { + let mount = Mount::new(&self.path); + mount + .do_mounts(&self.mounts, &config.linux.as_ref().unwrap().mountLabel) + .with_context(|| "Failed to do mounts")?; + Ok(()) + } + + fn link_ptmx(&self) -> Result<()> { + let ptmx = self.path.clone().join("dev/ptmx"); + if ptmx.exists() { + remove_file(&ptmx).with_context(|| "Failed to delete ptmx")?; + } + symlink("pts/ptmx", &ptmx) + .with_context(|| format!("Failed to create symlink {} -> pts/ptmx", ptmx.display()))?; + Ok(()) + } + + fn create_default_devices(&self, mknod: bool) -> Result<()> { + let dev = Device::new(self.path.clone()); + dev.create_default_devices(mknod)?; + Ok(()) + } + + fn create_devices(&self, devices: &Vec, mknod: bool) -> Result<()> { + let dev = Device::new(self.path.clone()); + for d in devices { + if dev.is_default_device(d) { + dev.delete_device(d)?; + } + dev.create_device(d, mknod) + .with_context(|| format!("Failed to create device {}", d.path))?; + } + Ok(()) + } + + pub fn prepare_rootfs(&self, config: &RuntimeConfig) -> Result<()> { + self.set_propagation()?; + self.mount().with_context(|| "Failed to mount rootfs")?; + self.make_parent_mount_private() + .with_context(|| "Failed to make parent mount private")?; + self.do_mounts(config)?; + self.set_default_symlinks()?; + + let old_mode = umask(Mode::from_bits_truncate(0o000)); + self.create_default_devices(self.mknod_device)?; + self.create_devices(&self.devices, self.mknod_device)?; + umask(old_mode); + + self.link_ptmx()?; + Ok(()) + } + + pub fn chroot(path: &Path) -> Result<()> { + let new_root = open(path, OFlag::O_DIRECTORY | OFlag::O_RDONLY, Mode::empty()) + .with_context(|| OzonecErr::OpenFile(path.to_string_lossy().to_string()))?; + chroot(path)?; + fchdir(new_root).with_context(|| "Failed to chdir to new root directory")?; + Ok(()) + } + + pub fn pivot_root(path: &Path) -> Result<()> { + let new_root = open(path, OFlag::O_DIRECTORY | OFlag::O_RDONLY, Mode::empty()) + .with_context(|| OzonecErr::OpenFile(path.to_string_lossy().to_string()))?; + let old_root = open("/", OFlag::O_DIRECTORY | OFlag::O_RDONLY, Mode::empty()) + .with_context(|| OzonecErr::OpenFile("/".to_string()))?; + + pivot_root(path, path)?; + nix::mount::mount( + None::<&str>, + "/", + None::<&str>, + MsFlags::MS_SLAVE | MsFlags::MS_REC, + None::<&str>, + ) + .with_context(|| OzonecErr::Mount("/".to_string()))?; + + fchdir(old_root).with_context(|| "Failed to chdir to old root directory")?; + umount2(".", MntFlags::MNT_DETACH) + .with_context(|| "Failed to umount old root directory")?; + fchdir(new_root).with_context(|| "Failed to chdir to new root directory")?; + Ok(()) + } +} -- Gitee From 7e764d77f191e1c0f8f7eb0dcea390800fc78805 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 23 Aug 2024 12:06:03 +0800 Subject: [PATCH 2109/2187] ozonec/linux: Add support for setting oom_score_adj --- ozonec/src/linux/container.rs | 8 +++++++- ozonec/src/linux/process.rs | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 36f819e32..7c6faad25 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -20,7 +20,7 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use libc::{c_char, pid_t, setdomainname}; -use log::{debug, error, info}; +use log::{debug, info}; use nix::{ errno::Errno, sys::{ @@ -414,6 +414,12 @@ impl Container for LinuxContainer { None }; + // As /proc/self/oom_score_adj is not allowed to write unless privileged, + // set oom_score_adj before setting process undumpable. + process + .set_oom_score_adj() + .with_context(|| "Failed to set oom_score_adj")?; + // Make the process undumpable to avoid various race conditions that could cause // processes in namespaces to join to access host resources (or execute code). if !self.config.linux.as_ref().unwrap().namespaces.is_empty() { diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index d6dc61ef1..5d8793a27 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -12,7 +12,7 @@ use std::{ ffi::CString, - fs::read_to_string, + fs::{self, read_to_string}, io::{stderr, stdin, stdout}, os::fd::{AsRawFd, RawFd}, }; @@ -52,6 +52,13 @@ impl Process { p } + pub fn set_oom_score_adj(&self) -> Result<()> { + if let Some(score) = self.oci.oomScoreAdj { + fs::write("/proc/self/oom_score_adj", score.to_string().as_bytes())?; + } + Ok(()) + } + pub fn set_additional_gids(&self) -> Result<()> { if let Some(additional_gids) = &self.oci.user.additionalGids { let setgroups = read_to_string("proc/self/setgroups") -- Gitee From 3cff751c903f670b5dc863071afaf9d7a2db4ddc Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 23 Aug 2024 12:16:48 +0800 Subject: [PATCH 2110/2187] ozonec/linux: Add support for setting rlimits --- ozonec/Cargo.toml | 1 + ozonec/src/linux/container.rs | 3 +++ ozonec/src/linux/process.rs | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index 8c7eb5515..7dc5860b9 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -17,6 +17,7 @@ nix = "= 0.26.2" oci_spec = { path = "oci_spec" } prctl = "1.0.0" procfs = "0.14.0" +rlimit = "0.5.3" serde = { version = "= 1.0.163", features = ["derive"] } serde_json = "= 1.0.96" thiserror = "= 1.0.40" diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 7c6faad25..4a8c76423 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -114,6 +114,9 @@ impl LinuxContainer { .close() .with_context(|| "Failed to close receiver end of first stage channel")?; + process + .set_rlimits() + .with_context(|| "Failed to set rlimit")?; // New pid namespace goes intto effect in cloned child processes. self.set_pid_namespace()?; diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 5d8793a27..08f681129 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -15,6 +15,7 @@ use std::{ fs::{self, read_to_string}, io::{stderr, stdin, stdout}, os::fd::{AsRawFd, RawFd}, + str::FromStr, }; use anyhow::{anyhow, bail, Context, Result}; @@ -23,6 +24,7 @@ use libc::SIGCHLD; use nix::unistd::{self, setresgid, setresuid, Gid, Pid, Uid}; use oci_spec::process::Process as OciProcess; +use rlimit::{setrlimit, Resource, Rlim}; pub struct Process { pub stdin: Option, @@ -59,6 +61,20 @@ impl Process { Ok(()) } + pub fn set_rlimits(&self) -> Result<()> { + if let Some(rlimits) = self.oci.rlimits.as_ref() { + for rlimit in rlimits { + setrlimit( + Resource::from_str(&rlimit.rlimit_type) + .with_context(|| "rlimit type is ill-formatted")?, + Rlim::from_raw(rlimit.soft), + Rlim::from_raw(rlimit.hard), + )?; + } + } + Ok(()) + } + pub fn set_additional_gids(&self) -> Result<()> { if let Some(additional_gids) = &self.oci.user.additionalGids { let setgroups = read_to_string("proc/self/setgroups") -- Gitee From 71b805f20992cdad132c4a1f6470162cfda6f279 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 23 Aug 2024 14:29:21 +0800 Subject: [PATCH 2111/2187] ozonec/linux: Implement do_second_stage() to initialize container enviroment --- ozonec/src/linux/container.rs | 178 +++++++++++++++++++++++++++++++++- ozonec/src/linux/process.rs | 115 +++++++++++++++++++++- 2 files changed, 286 insertions(+), 7 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 4a8c76423..e1756d330 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -12,7 +12,7 @@ use std::{ collections::HashMap, - fs::{canonicalize, create_dir_all, OpenOptions}, + fs::{self, canonicalize, create_dir_all, OpenOptions}, io::Write, path::{Path, PathBuf}, time::SystemTime, @@ -23,11 +23,13 @@ use libc::{c_char, pid_t, setdomainname}; use log::{debug, info}; use nix::{ errno::Errno, + mount::MsFlags, sys::{ signal::Signal, + statfs::statfs, wait::{waitpid, WaitStatus}, }, - unistd::{chown, getegid, geteuid, sethostname, Gid, Pid, Uid}, + unistd::{self, chown, getegid, geteuid, sethostname, Gid, Pid, Uid}, }; use prctl::set_dumpable; use procfs::process::ProcState; @@ -38,10 +40,11 @@ use super::{ }; use crate::{ container::Container, + linux::rootfs::Rootfs, utils::{Channel, Message, OzonecErr}, }; use oci_spec::{ - linux::{IdMapping, NamespaceType}, + linux::{Device as OciDevice, IdMapping, NamespaceType}, runtime::RuntimeConfig, state::{ContainerStatus, State}, }; @@ -142,9 +145,67 @@ impl LinuxContainer { ) -> Result<()> { debug!("Second stage process start"); + unistd::setsid().with_context(|| "Failed to setsid")?; + process + .set_io_priority() + .with_context(|| "Failed to set io priority")?; + process + .set_scheduler() + .with_context(|| "Failed to set scheduler")?; + self.set_rest_namespaces()?; + process.set_no_new_privileges()?; + + if process.init { + let propagation = self + .config + .linux + .as_ref() + .unwrap() + .rootfsPropagation + .clone(); + let mknod_device = !self.is_namespace_set(NamespaceType::User)?; + let mut devices: Vec = Vec::new(); + if let Some(devs) = self.config.linux.as_ref().unwrap().devices.as_ref() { + devices = devs.clone() + }; + let rootfs = Rootfs::new( + self.config.root.path.clone().into(), + propagation, + self.config.mounts.clone(), + mknod_device, + devices, + )?; + rootfs.prepare_rootfs(&self.config)?; + + // Entering into rootfs jail. If mount namespace is specified, use pivot_root. + // Otherwise use chroot. + if self.is_namespace_set(NamespaceType::Mount)? { + Rootfs::pivot_root(&rootfs.path).with_context(|| "Failed to pivot_root")?; + } else { + Rootfs::chroot(&rootfs.path).with_context(|| "Failed to chroot")?; + } + + self.set_sysctl_parameters()?; + } else if !self.is_namespace_set(NamespaceType::Mount)? { + Rootfs::chroot(&PathBuf::from(self.config.root.path.clone())) + .with_context(|| "Failed to chroot")?; + } + + if self.config.root.readonly { + LinuxContainer::mount_rootfs_readonly()?; + } + self.set_readonly_paths()?; + self.set_masked_paths()?; + let chdir_cwd_ret = process.chdir_cwd().is_err(); process.set_additional_gids()?; + process.set_process_id()?; + if chdir_cwd_ret { + process.chdir_cwd()?; + } + process.clean_envs(); + process.set_envs(); // Tell the parent process that the init process has been cloned. parent_channel.send_container_created()?; @@ -163,6 +224,25 @@ impl LinuxContainer { process.exec_program(); } + fn mount_rootfs_readonly() -> Result<()> { + let ms_flags = MsFlags::MS_RDONLY | MsFlags::MS_REMOUNT | MsFlags::MS_BIND; + let root_path = Path::new("/"); + let fs_flags = statfs(root_path) + .with_context(|| "Statfs root directory error")? + .flags() + .bits(); + + nix::mount::mount( + None::<&str>, + root_path, + None::<&str>, + ms_flags | MsFlags::from_bits_truncate(fs_flags), + None::<&str>, + ) + .with_context(|| "Failed to remount rootfs readonly")?; + Ok(()) + } + fn container_status(&self) -> Result { if self.pid == -1 { return Ok(ContainerStatus::Creating); @@ -257,6 +337,86 @@ impl LinuxContainer { Ok(()) } + fn set_readonly_paths(&self) -> Result<()> { + if let Some(readonly_paths) = self.config.linux.as_ref().unwrap().readonlyPaths.clone() { + for p in readonly_paths { + let path = Path::new(&p); + if let Err(e) = nix::mount::mount( + Some(path), + path, + None::<&str>, + MsFlags::MS_BIND | MsFlags::MS_REC, + None::<&str>, + ) { + if matches!(e, Errno::ENOENT) { + return Ok(()); + } + bail!("Failed to make {} as recursive bind mount", path.display()); + } + + nix::mount::mount( + Some(path), + path, + None::<&str>, + MsFlags::MS_NOSUID + | MsFlags::MS_NODEV + | MsFlags::MS_NOEXEC + | MsFlags::MS_BIND + | MsFlags::MS_REMOUNT + | MsFlags::MS_RDONLY, + None::<&str>, + ) + .with_context(|| format!("Failed to remount {} readonly", path.display()))?; + } + } + Ok(()) + } + + fn set_masked_paths(&self) -> Result<()> { + let linux = self.config.linux.as_ref().unwrap(); + if let Some(masked_paths) = linux.maskedPaths.clone() { + for p in masked_paths { + let path = Path::new(&p); + if let Err(e) = nix::mount::mount( + Some(Path::new("/dev/null")), + path, + None::<&str>, + MsFlags::MS_BIND, + None::<&str>, + ) { + match e { + // Ignore if path doesn't exists. + Errno::ENOENT => (), + Errno::ENOTDIR => { + let label = match linux.mountLabel.clone() { + Some(l) => format!("context=\"{}\"", l), + None => "".to_string(), + }; + nix::mount::mount( + Some(Path::new("tmpfs")), + path, + Some("tmpfs"), + MsFlags::MS_RDONLY, + Some(label.as_str()), + ) + .with_context(|| { + format!( + "Failed to make {} as masked mount by tmpfs", + path.display() + ) + })?; + } + _ => bail!( + "Failed to make {} as masked mount by /dev/null", + path.display() + ), + } + } + } + } + Ok(()) + } + fn set_rest_namespaces(&self) -> Result<()> { let ns_config = &self.config.linux.as_ref().unwrap().namespaces; let ns_controller: NsController = ns_config.clone().try_into()?; @@ -362,6 +522,18 @@ impl LinuxContainer { } Ok(()) } + + fn set_sysctl_parameters(&self) -> Result<()> { + if let Some(sysctl_params) = self.config.linux.as_ref().unwrap().sysctl.clone() { + let sys_path = PathBuf::from("/proc/sys"); + for (param, value) in sysctl_params { + let path = sys_path.join(param.replace('.', "/")); + fs::write(&path, value.as_bytes()) + .with_context(|| format!("Failed to set {} to {}", path.display(), value))?; + } + } + Ok(()) + } } impl Container for LinuxContainer { diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 08f681129..a8e22ca0a 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -11,19 +11,22 @@ // See the Mulan PSL v2 for more details. use std::{ + env, ffi::CString, fs::{self, read_to_string}, io::{stderr, stdin, stdout}, + mem, os::fd::{AsRawFd, RawFd}, + path::PathBuf, str::FromStr, }; use anyhow::{anyhow, bail, Context, Result}; use clone3::Clone3; -use libc::SIGCHLD; -use nix::unistd::{self, setresgid, setresuid, Gid, Pid, Uid}; +use nix::unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}; -use oci_spec::process::Process as OciProcess; +use oci_spec::{linux::IoPriClass, process::Process as OciProcess}; +use prctl::set_no_new_privileges; use rlimit::{setrlimit, Resource, Rlim}; pub struct Process { @@ -75,6 +78,67 @@ impl Process { Ok(()) } + pub fn set_io_priority(&self) -> Result<()> { + if let Some(io_prio) = &self.oci.ioPriority { + let class = match io_prio.class { + IoPriClass::IoprioClassRt => 1i64, + IoPriClass::IoprioClassBe => 2i64, + IoPriClass::IoprioClassIdle => 3i64, + }; + // Who is a process id or thread id identifying a single process or + // thread. If who is 0, then operate on the calling process or thread. + let io_prio_who_process: libc::c_int = 1; + let io_prio_who_pid = 0; + // SAFETY: FFI call with valid arguments. + match unsafe { + libc::syscall( + libc::SYS_ioprio_set, + io_prio_who_process, + io_prio_who_pid, + (class << 13) | io_prio.priority, + ) + } { + 0 => Ok(()), + -1 => Err(nix::Error::last()), + _ => Err(nix::Error::UnknownErrno), + }?; + } + Ok(()) + } + + pub fn set_scheduler(&self) -> Result<()> { + if let Some(scheduler) = &self.oci.scheduler { + // SAFETY: FFI call with valid arguments. + let mut param: libc::sched_param = unsafe { mem::zeroed() }; + param.sched_priority = scheduler.priority.unwrap_or_default(); + // SAFETY: FFI call with valid arguments. + match unsafe { libc::sched_setscheduler(0, scheduler.policy.into(), ¶m) } { + 0 => Ok(()), + -1 => Err(nix::Error::last()), + _ => Err(nix::Error::UnknownErrno), + }?; + } + Ok(()) + } + + pub fn set_no_new_privileges(&self) -> Result<()> { + if let Some(no_new_privileges) = self.oci.noNewPrivileges { + if no_new_privileges { + set_no_new_privileges(true) + .map_err(|e| anyhow!("Failed to set no new privileges: {}", e))?; + } + } + Ok(()) + } + + pub fn chdir_cwd(&self) -> Result<()> { + if !self.oci.cwd.is_empty() { + chdir(&PathBuf::from(&self.oci.cwd)) + .with_context(|| format!("Failed to chdir to {}", &self.oci.cwd))?; + } + Ok(()) + } + pub fn set_additional_gids(&self) -> Result<()> { if let Some(additional_gids) = &self.oci.user.additionalGids { let setgroups = read_to_string("proc/self/setgroups") @@ -92,12 +156,55 @@ impl Process { Ok(()) } + pub fn set_process_id(&self) -> Result<()> { + let gid = Gid::from(self.oci.user.gid); + let uid = Uid::from(self.oci.user.uid); + self.set_id(gid, uid)?; + Ok(()) + } + pub fn set_id(&self, gid: Gid, uid: Uid) -> Result<()> { setresgid(gid, gid, gid).with_context(|| "Failed to setresgid")?; setresuid(uid, uid, uid).with_context(|| "Failed to setresuid")?; Ok(()) } + // Check and reserve valid environment variables. + // Invalid env vars may cause panic, refer to https://doc.rust-lang.org/std/env/fn.set_var.html#panics + // Key should not : + // * contain NULL character '\0' + // * contain ASCII character '=' + // * be empty + // Value should not: + // * contain NULL character '\0' + fn is_env_valid(env: &str) -> Option<(&str, &str)> { + // Split the env var by '=' to ensure there is no '=' in key, and there is only one '=' + // in the whole env var. + if let Some((key, value)) = env.split_once('=') { + if !key.is_empty() + && !key.as_bytes().contains(&b'\0') + && !value.as_bytes().contains(&b'\0') + { + return Some((key.trim(), value.trim())); + } + } + None + } + + pub fn set_envs(&self) { + if let Some(envs) = &self.oci.env { + for env in envs { + if let Some((key, value)) = Self::is_env_valid(env) { + env::set_var(key, value); + } + } + } + } + + pub fn clean_envs(&self) { + env::vars().for_each(|(key, value)| env::remove_var(key)); + } + pub fn exec_program(&self) -> ! { // It has been make sure that args is not None in validate_config(). let args = &self.oci.args.as_ref().unwrap(); @@ -120,7 +227,7 @@ impl Process { // Clone a new child process. pub fn clone_process Result>(child_name: &str, cb: F) -> Result { let mut clone3 = Clone3::default(); - clone3.exit_signal(SIGCHLD as u64); + clone3.exit_signal(libc::SIGCHLD as u64); // SAFETY: FFI call with valid arguments. match unsafe { clone3.call().with_context(|| "Clone3() error")? } { -- Gitee From cb75d866a566ba2eb343e52c861542f18c94953b Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 23 Aug 2024 15:27:34 +0800 Subject: [PATCH 2112/2187] ozonec/linux: Add tty support --- ozonec/src/linux/container.rs | 9 +++++ ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/process.rs | 17 +++++++++ ozonec/src/linux/terminal.rs | 69 +++++++++++++++++++++++++++++++++++ ozonec/src/utils/error.rs | 2 + 5 files changed, 98 insertions(+) create mode 100644 ozonec/src/linux/terminal.rs diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index e1756d330..2d50e0173 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -14,6 +14,7 @@ use std::{ collections::HashMap, fs::{self, canonicalize, create_dir_all, OpenOptions}, io::Write, + os::{fd::AsRawFd, unix::net::UnixStream}, path::{Path, PathBuf}, time::SystemTime, }; @@ -153,6 +154,14 @@ impl LinuxContainer { .set_scheduler() .with_context(|| "Failed to set scheduler")?; + if let Some(console_socket) = &self.console_socket { + let stream = UnixStream::connect(console_socket) + .with_context(|| "Failed to connect console socket")?; + process + .set_tty(Some(stream.as_raw_fd())) + .with_context(|| "Failed to set tty")?; + } + self.set_rest_namespaces()?; process.set_no_new_privileges()?; diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index 84fff70c5..b5dfc178b 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -17,6 +17,7 @@ mod namespace; mod notify_socket; mod process; mod rootfs; +mod terminal; pub use container::LinuxContainer; pub use notify_socket::NotifyListener; diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index a8e22ca0a..a15a137f1 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -29,6 +29,8 @@ use oci_spec::{linux::IoPriClass, process::Process as OciProcess}; use prctl::set_no_new_privileges; use rlimit::{setrlimit, Resource, Rlim}; +use super::terminal::{connect_stdio, setup_console}; + pub struct Process { pub stdin: Option, pub stdout: Option, @@ -57,6 +59,21 @@ impl Process { p } + pub fn set_tty(&self, console_fd: Option) -> Result<()> { + if self.tty && console_fd.is_some() { + setup_console(&console_fd.unwrap()).with_context(|| "Failed to setup console")?; + } else { + connect_stdio( + self.stdin.as_ref().unwrap(), + self.stdout.as_ref().unwrap(), + self.stderr.as_ref().unwrap(), + )?; + // SAFETY: FFI call with valid arguments. + unsafe { libc::ioctl(0, libc::TIOCSCTTY) }; + } + Ok(()) + } + pub fn set_oom_score_adj(&self) -> Result<()> { if let Some(score) = self.oci.oomScoreAdj { fs::write("/proc/self/oom_score_adj", score.to_string().as_bytes())?; diff --git a/ozonec/src/linux/terminal.rs b/ozonec/src/linux/terminal.rs new file mode 100644 index 000000000..e7490d2bc --- /dev/null +++ b/ozonec/src/linux/terminal.rs @@ -0,0 +1,69 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{ + io::IoSlice, + mem::ManuallyDrop, + os::fd::{AsRawFd, RawFd}, +}; + +use anyhow::{Context, Result}; +use nix::{ + pty::openpty, + sys::socket::{sendmsg, ControlMessage, MsgFlags, UnixAddr}, + unistd::{close, dup2}, +}; + +use crate::utils::OzonecErr; + +pub enum Stdio { + Stdin = 0, + Stdout = 1, + Stderr = 2, +} + +pub fn setup_console(console_fd: &RawFd) -> Result<()> { + let ret = openpty(None, None).with_context(|| "openpty error")?; + let pty_name: &[u8] = b"/dev/ptmx"; + let iov = [IoSlice::new(pty_name)]; + + // Use ManuallyDrop to keep fds open. + let master = ManuallyDrop::new(ret.master); + let slave = ManuallyDrop::new(ret.slave); + let fds = [master.as_raw_fd()]; + let cmsg = ControlMessage::ScmRights(&fds); + sendmsg::( + console_fd.as_raw_fd(), + &iov, + &[cmsg], + MsgFlags::empty(), + None, + ) + .with_context(|| "sendmsg error")?; + + // SAFETY: FFI call with valid arguments. + let slave_fd = slave.as_raw_fd(); + unsafe { libc::ioctl(slave_fd, libc::TIOCSCTTY) }; + connect_stdio(&slave_fd, &slave_fd, &slave_fd)?; + close(console_fd.as_raw_fd()).with_context(|| "Failed to close console socket")?; + Ok(()) +} + +pub fn connect_stdio(stdin: &RawFd, stdout: &RawFd, stderr: &RawFd) -> Result<()> { + dup2(*stdin, (Stdio::Stdin as i32).as_raw_fd()) + .with_context(|| OzonecErr::Dup2("stdin".to_string()))?; + dup2(*stdout, (Stdio::Stdout as i32).as_raw_fd()) + .with_context(|| OzonecErr::Dup2("stdout".to_string()))?; + dup2(*stderr, (Stdio::Stderr as i32).as_raw_fd()) + .with_context(|| OzonecErr::Dup2("stderr".to_string()))?; + Ok(()) +} diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 9c00bd13b..8f5f052d8 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -28,4 +28,6 @@ pub enum OzonecErr { AccessProcSelf, #[error("Failed to get mountinfo")] GetMntInfo, + #[error("Dup2 {0} error")] + Dup2(String), } -- Gitee From 2d90831128815d9614d00cbc714b344da349044c Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sat, 24 Aug 2024 12:16:25 +0800 Subject: [PATCH 2113/2187] ozonec/linux: Add capabilities support --- ozonec/Cargo.toml | 1 + ozonec/src/linux/container.rs | 6 ++ ozonec/src/linux/process.rs | 117 ++++++++++++++++++++++++++++++++-- ozonec/src/utils/error.rs | 4 ++ 4 files changed, 124 insertions(+), 4 deletions(-) diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index 7dc5860b9..290b13fe3 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -8,6 +8,7 @@ description = "An OCI runtime implemented by Rust" [dependencies] anyhow = "= 1.0.71" +caps = "0.5.5" chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"] } clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } clone3 = "0.2.3" diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 2d50e0173..9242b2ba4 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -210,6 +210,12 @@ impl LinuxContainer { let chdir_cwd_ret = process.chdir_cwd().is_err(); process.set_additional_gids()?; process.set_process_id()?; + process + .reset_capabilities() + .with_context(|| "Failed to reset capabilities")?; + process + .drop_capabilities() + .with_context(|| "Failed to drop capabilities")?; if chdir_cwd_ret { process.chdir_cwd()?; } diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index a15a137f1..46bb990e7 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -22,14 +22,15 @@ use std::{ }; use anyhow::{anyhow, bail, Context, Result}; +use caps::{self, CapSet, Capability, CapsHashSet}; use clone3::Clone3; use nix::unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}; - -use oci_spec::{linux::IoPriClass, process::Process as OciProcess}; -use prctl::set_no_new_privileges; +use prctl::{set_keep_capabilities, set_no_new_privileges}; use rlimit::{setrlimit, Resource, Rlim}; use super::terminal::{connect_stdio, setup_console}; +use crate::utils::OzonecErr; +use oci_spec::{linux::IoPriClass, process::Process as OciProcess}; pub struct Process { pub stdin: Option, @@ -156,6 +157,45 @@ impl Process { Ok(()) } + pub fn drop_capabilities(&self) -> Result<()> { + if let Some(caps) = self.oci.capabilities.as_ref() { + if let Some(bounding) = caps.bounding.as_ref() { + let all_caps = caps::read(None, CapSet::Bounding) + .with_context(|| OzonecErr::GetAllCaps("Bounding".to_string()))?; + let caps_hash_set = to_cap_set(bounding)?; + for cap in all_caps.difference(&caps_hash_set) { + caps::drop(None, CapSet::Bounding, *cap).with_context(|| { + format!("Failed to drop {} from bonding set", cap.to_string()) + })?; + } + } + if let Some(effective) = caps.effective.as_ref() { + caps::set(None, CapSet::Effective, &to_cap_set(effective)?) + .with_context(|| OzonecErr::SetCaps("Effective".to_string()))?; + } + if let Some(permitted) = caps.permitted.as_ref() { + caps::set(None, CapSet::Permitted, &to_cap_set(permitted)?) + .with_context(|| OzonecErr::SetCaps("Permitted".to_string()))?; + } + if let Some(inheritable) = caps.inheritable.as_ref() { + caps::set(None, CapSet::Inheritable, &to_cap_set(inheritable)?) + .with_context(|| OzonecErr::SetCaps("Inheritable".to_string()))?; + } + if let Some(ambient) = caps.ambient.as_ref() { + caps::set(None, CapSet::Ambient, &to_cap_set(ambient)?) + .with_context(|| OzonecErr::SetCaps("Ambient".to_string()))?; + } + } + Ok(()) + } + + pub fn reset_capabilities(&self) -> Result<()> { + let permitted = caps::read(None, CapSet::Permitted) + .with_context(|| OzonecErr::GetAllCaps("Permitted".to_string()))?; + caps::set(None, CapSet::Effective, &permitted)?; + Ok(()) + } + pub fn set_additional_gids(&self) -> Result<()> { if let Some(additional_gids) = &self.oci.user.additionalGids { let setgroups = read_to_string("proc/self/setgroups") @@ -181,8 +221,17 @@ impl Process { } pub fn set_id(&self, gid: Gid, uid: Uid) -> Result<()> { + set_keep_capabilities(true) + .map_err(|e| anyhow!("Failed to enable keeping capabilities: {}", e))?; setresgid(gid, gid, gid).with_context(|| "Failed to setresgid")?; setresuid(uid, uid, uid).with_context(|| "Failed to setresuid")?; + + let permitted = caps::read(None, CapSet::Permitted) + .with_context(|| OzonecErr::GetAllCaps("Permitted".to_string()))?; + caps::set(None, CapSet::Effective, &permitted) + .with_context(|| OzonecErr::SetCaps("Effective".to_string()))?; + set_keep_capabilities(false) + .map_err(|e| anyhow!("Failed to disable keeping capabilities: {}", e))?; Ok(()) } @@ -219,7 +268,7 @@ impl Process { } pub fn clean_envs(&self) { - env::vars().for_each(|(key, value)| env::remove_var(key)); + env::vars().for_each(|(key, _value)| env::remove_var(key)); } pub fn exec_program(&self) -> ! { @@ -263,3 +312,63 @@ pub fn clone_process Result>(child_name: &str, cb: F) -> Res pid => Ok(Pid::from_raw(pid)), } } + +fn to_cap_set(caps: &Vec) -> Result { + let mut caps_hash_set = CapsHashSet::new(); + + for c in caps { + let cap = to_cap(&c)?; + caps_hash_set.insert(cap); + } + Ok(caps_hash_set) +} + +fn to_cap(value: &str) -> Result { + let binding = value.to_uppercase(); + let stripped = binding.strip_prefix("CAP_").unwrap_or(&binding); + + match stripped { + "AUDIT_CONTROL" => Ok(Capability::CAP_AUDIT_CONTROL), + "AUDIT_READ" => Ok(Capability::CAP_AUDIT_READ), + "AUDIT_WRITE" => Ok(Capability::CAP_AUDIT_WRITE), + "BLOCK_SUSPEND" => Ok(Capability::CAP_BLOCK_SUSPEND), + "BPF" => Ok(Capability::CAP_BPF), + "CHECKPOINT_RESTORE" => Ok(Capability::CAP_CHECKPOINT_RESTORE), + "CHOWN" => Ok(Capability::CAP_CHOWN), + "DAC_OVERRIDE" => Ok(Capability::CAP_DAC_OVERRIDE), + "DAC_READ_SEARCH" => Ok(Capability::CAP_DAC_READ_SEARCH), + "FOWNER" => Ok(Capability::CAP_FOWNER), + "FSETID" => Ok(Capability::CAP_FSETID), + "IPC_LOCK" => Ok(Capability::CAP_IPC_LOCK), + "IPC_OWNER" => Ok(Capability::CAP_IPC_OWNER), + "KILL" => Ok(Capability::CAP_KILL), + "LEASE" => Ok(Capability::CAP_LEASE), + "LINUX_IMMUTABLE" => Ok(Capability::CAP_LINUX_IMMUTABLE), + "MAC_ADMIN" => Ok(Capability::CAP_MAC_ADMIN), + "MAC_OVERRIDE" => Ok(Capability::CAP_MAC_OVERRIDE), + "MKNOD" => Ok(Capability::CAP_MKNOD), + "NET_ADMIN" => Ok(Capability::CAP_NET_ADMIN), + "NET_BIND_SERVICE" => Ok(Capability::CAP_NET_BIND_SERVICE), + "NET_BROADCAST" => Ok(Capability::CAP_NET_BROADCAST), + "NET_RAW" => Ok(Capability::CAP_NET_RAW), + "PERFMON" => Ok(Capability::CAP_PERFMON), + "SETGID" => Ok(Capability::CAP_SETGID), + "SETFCAP" => Ok(Capability::CAP_SETFCAP), + "SETPCAP" => Ok(Capability::CAP_SETPCAP), + "SETUID" => Ok(Capability::CAP_SETUID), + "SYS_ADMIN" => Ok(Capability::CAP_SYS_ADMIN), + "SYS_BOOT" => Ok(Capability::CAP_SYS_BOOT), + "SYS_CHROOT" => Ok(Capability::CAP_SYS_CHROOT), + "SYS_MODULE" => Ok(Capability::CAP_SYS_MODULE), + "SYS_NICE" => Ok(Capability::CAP_SYS_NICE), + "SYS_PACCT" => Ok(Capability::CAP_SYS_PACCT), + "SYS_PTRACE" => Ok(Capability::CAP_SYS_PTRACE), + "SYS_RAWIO" => Ok(Capability::CAP_SYS_RAWIO), + "SYS_RESOURCE" => Ok(Capability::CAP_SYS_RESOURCE), + "SYS_TIME" => Ok(Capability::CAP_SYS_TIME), + "SYS_TTY_CONFIG" => Ok(Capability::CAP_SYS_TTY_CONFIG), + "SYSLOG" => Ok(Capability::CAP_SYSLOG), + "WAKE_ALARM" => Ok(Capability::CAP_WAKE_ALARM), + _ => bail!("Invalid capability: {}", value), + } +} diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 8f5f052d8..a078a9610 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -30,4 +30,8 @@ pub enum OzonecErr { GetMntInfo, #[error("Dup2 {0} error")] Dup2(String), + #[error("Failed to get all capabilities of {0} set")] + GetAllCaps(String), + #[error("Failed to set the capability set {0}")] + SetCaps(String), } -- Gitee From 297a14f95189bea939b4cde5b27a3b755c55ab72 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 26 Aug 2024 07:20:40 +0800 Subject: [PATCH 2114/2187] ozonec/linux: Add seccomp support --- ozonec/Cargo.toml | 1 + ozonec/src/linux/container.rs | 14 +++- ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/process.rs | 4 ++ ozonec/src/linux/seccomp.rs | 116 ++++++++++++++++++++++++++++++++++ ozonec/src/utils/error.rs | 2 + 6 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 ozonec/src/linux/seccomp.rs diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index 290b13fe3..c3e52cf50 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -13,6 +13,7 @@ chrono = { version = "0.4.31", default-features = false, features = ["clock", "s clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } clone3 = "0.2.3" libc = "= 0.2.146" +libseccomp = "0.3.0" log = { version = "= 0.4.18", features = ["std"]} nix = "= 0.26.2" oci_spec = { path = "oci_spec" } diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 9242b2ba4..5b0de9b0d 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -41,7 +41,7 @@ use super::{ }; use crate::{ container::Container, - linux::rootfs::Rootfs, + linux::{rootfs::Rootfs, seccomp::set_seccomp}, utils::{Channel, Message, OzonecErr}, }; use oci_spec::{ @@ -210,6 +210,13 @@ impl LinuxContainer { let chdir_cwd_ret = process.chdir_cwd().is_err(); process.set_additional_gids()?; process.set_process_id()?; + + // Without setting no new privileges, setting seccomp is a privileged operation. + if !process.no_new_privileges() { + if let Some(seccomp) = &self.config.linux.as_ref().unwrap().seccomp { + set_seccomp(seccomp).with_context(|| "Failed to set seccomp")?; + } + } process .reset_capabilities() .with_context(|| "Failed to reset capabilities")?; @@ -221,6 +228,11 @@ impl LinuxContainer { } process.clean_envs(); process.set_envs(); + if process.no_new_privileges() { + if let Some(seccomp) = &self.config.linux.as_ref().unwrap().seccomp { + set_seccomp(seccomp).with_context(|| "Failed to set seccomp")?; + } + } // Tell the parent process that the init process has been cloned. parent_channel.send_container_created()?; diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index b5dfc178b..4e09c1ebe 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -17,6 +17,7 @@ mod namespace; mod notify_socket; mod process; mod rootfs; +mod seccomp; mod terminal; pub use container::LinuxContainer; diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 46bb990e7..0a590c679 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -139,6 +139,10 @@ impl Process { Ok(()) } + pub fn no_new_privileges(&self) -> bool { + self.oci.noNewPrivileges.is_some() + } + pub fn set_no_new_privileges(&self) -> Result<()> { if let Some(no_new_privileges) = self.oci.noNewPrivileges { if no_new_privileges { diff --git a/ozonec/src/linux/seccomp.rs b/ozonec/src/linux/seccomp.rs new file mode 100644 index 000000000..e95866101 --- /dev/null +++ b/ozonec/src/linux/seccomp.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::vec; + +use anyhow::{bail, Context, Result}; + +use libseccomp::{ + ScmpAction, ScmpArch, ScmpArgCompare, ScmpCompareOp, ScmpFilterContext, ScmpSyscall, +}; +use oci_spec::linux::{Seccomp, SeccompAction as OciSeccompAction, SeccompOp}; + +use crate::utils::OzonecErr; + +fn parse_action(action: OciSeccompAction, errno: Option) -> ScmpAction { + let errno = errno.unwrap_or(libc::EPERM as u32); + match action { + OciSeccompAction::ScmpActKill => ScmpAction::KillThread, + OciSeccompAction::ScmpActKillProcess => ScmpAction::KillProcess, + OciSeccompAction::ScmpActTrap => ScmpAction::Trap, + OciSeccompAction::ScmpActErrno => ScmpAction::Errno(errno as i32), + OciSeccompAction::ScmpActTrace => ScmpAction::Trace(errno as u16), + OciSeccompAction::ScmpActLog => ScmpAction::Log, + OciSeccompAction::ScmpActAllow => ScmpAction::Allow, + _ => ScmpAction::KillThread, + } +} + +fn parse_cmp(op: SeccompOp) -> ScmpCompareOp { + match op { + SeccompOp::ScmpCmpNe => ScmpCompareOp::NotEqual, + SeccompOp::ScmpCmpLt => ScmpCompareOp::Less, + SeccompOp::ScmpCmpLe => ScmpCompareOp::LessOrEqual, + SeccompOp::ScmpCmpEq => ScmpCompareOp::Equal, + SeccompOp::ScmpCmpGe => ScmpCompareOp::GreaterEqual, + SeccompOp::ScmpCmpGt => ScmpCompareOp::Greater, + _ => ScmpCompareOp::Equal, + } +} + +fn check_seccomp(seccomp: &Seccomp) -> Result<()> { + // We don't support NOTIFY as the default action. When the seccomp filter + // is created with NOTIFY, the container process will have to communicate + // the returned fd to another process. Therefore, ozonec needs to call + // the WRITE syscall. And then READ and CLOSE syscalls are also needed to + // be enabled to use. + if seccomp.defaultAction == OciSeccompAction::ScmpActNotify { + bail!("SCMP_ACT_NOTIFY is not supported as the default action"); + } + if let Some(syscalls) = &seccomp.syscalls { + for syscall in syscalls { + if syscall.action == OciSeccompAction::ScmpActNotify { + for name in &syscall.names { + if name == "write" { + bail!("SCMP_ACT_NOTIFY is not supported to be used for write syscall"); + } + } + } + } + } + + Ok(()) +} + +pub fn set_seccomp(seccomp: &Seccomp) -> Result<()> { + check_seccomp(seccomp)?; + + let default_action = parse_action(seccomp.defaultAction, seccomp.defaultErrnoRet); + if let Some(syscalls) = &seccomp.syscalls { + let mut filter = ScmpFilterContext::new_filter(default_action)?; + #[cfg(target_arch = "x86_64")] + filter + .add_arch(ScmpArch::X8664) + .with_context(|| OzonecErr::AddScmpArch)?; + #[cfg(target_arch = "aarch64")] + filter + .add_arch(ScmpArch::Aarch64) + .with_context(|| OzonecErr::AddScmpArch)?; + + for syscall in syscalls { + let action = parse_action(syscall.action, syscall.errnoRet); + if action == default_action { + continue; + } + + for name in &syscall.names { + let sc = ScmpSyscall::from_name(name)?; + let mut comparators: Vec = vec![]; + if let Some(args) = &syscall.args { + for arg in args { + let op = parse_cmp(arg.op); + let cmp = ScmpArgCompare::new(arg.index as u32, op, arg.value); + comparators.push(cmp); + } + } + filter + .add_rule_conditional(action, sc, &comparators) + .with_context(|| "Failed to add conditional rule")?; + } + } + filter + .load() + .with_context(|| "Failed to load filter into the kernel")?; + } + + Ok(()) +} diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index a078a9610..0631f47b1 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -34,4 +34,6 @@ pub enum OzonecErr { GetAllCaps(String), #[error("Failed to set the capability set {0}")] SetCaps(String), + #[error("Failed to add architecture to seccomp filter")] + AddScmpArch, } -- Gitee From c2c87759e995371dd5109c990a86810d4c3bbe65 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 26 Aug 2024 09:07:08 +0800 Subject: [PATCH 2115/2187] ozonec/commands: Add support for starting container --- ozonec/src/commands/mod.rs | 2 ++ ozonec/src/commands/start.rs | 58 +++++++++++++++++++++++++++++++ ozonec/src/container/mod.rs | 1 + ozonec/src/container/state.rs | 24 +++++++++---- ozonec/src/linux/container.rs | 43 +++++++++++++++++++---- ozonec/src/linux/notify_socket.rs | 42 ++++++++++++++++++---- ozonec/src/main.rs | 6 ++++ ozonec/src/utils/error.rs | 2 ++ 8 files changed, 159 insertions(+), 19 deletions(-) create mode 100644 ozonec/src/commands/start.rs diff --git a/ozonec/src/commands/mod.rs b/ozonec/src/commands/mod.rs index 2472ae99f..0ed00ef1d 100644 --- a/ozonec/src/commands/mod.rs +++ b/ozonec/src/commands/mod.rs @@ -11,5 +11,7 @@ // See the Mulan PSL v2 for more details. mod create; +mod start; pub use create::Create; +pub use start::Start; diff --git a/ozonec/src/commands/start.rs b/ozonec/src/commands/start.rs new file mode 100644 index 000000000..782701e47 --- /dev/null +++ b/ozonec/src/commands/start.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::Path; + +use anyhow::{bail, Context, Result}; +use clap::Parser; +use oci_spec::state::ContainerStatus; + +use crate::{ + container::{Action, Container, Launcher, State}, + linux::LinuxContainer, +}; + +/// Start the user-specified code from process +#[derive(Parser, Debug)] +pub struct Start { + pub container_id: String, +} + +impl Start { + fn launcher(&self, root: &Path) -> Result { + let container_state = State::load(root, &self.container_id) + .with_context(|| "Failed to load container state")?; + let container = LinuxContainer::load_from_state(&container_state, &None)?; + let oci_status = container + .get_oci_state() + .with_context(|| "Failed to get oci state")? + .status; + + if oci_status != ContainerStatus::Created { + bail!("Can't start a container with {:?} status", oci_status); + } + + Ok(Launcher::new( + &container_state.bundle, + root, + false, + Box::new(container), + None, + )) + } + + pub fn run(&self, root: &Path) -> Result<()> { + let mut launcher = self.launcher(root)?; + launcher.launch(Action::Start)?; + Ok(()) + } +} diff --git a/ozonec/src/container/mod.rs b/ozonec/src/container/mod.rs index bc6905a60..ca576a38b 100644 --- a/ozonec/src/container/mod.rs +++ b/ozonec/src/container/mod.rs @@ -14,6 +14,7 @@ mod launcher; mod state; pub use launcher::{Action, Launcher}; +pub use state::State; use std::time::SystemTime; diff --git a/ozonec/src/container/state.rs b/ozonec/src/container/state.rs index ab97cc364..41ec2ec96 100644 --- a/ozonec/src/container/state.rs +++ b/ozonec/src/container/state.rs @@ -11,13 +11,13 @@ // See the Mulan PSL v2 for more details. use std::{ - fs::{DirBuilder, OpenOptions}, + fs::{DirBuilder, File, OpenOptions}, os::unix::fs::DirBuilderExt, path::{Path, PathBuf}, time::SystemTime, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use chrono::{DateTime, Utc}; use libc::pid_t; use nix::sys::stat::Mode; @@ -63,10 +63,6 @@ impl State { } } - fn file_path(root: &Path, id: &str) -> PathBuf { - root.join(id).join("state.json") - } - pub fn save(&self) -> Result<()> { if !&self.root.exists() { DirBuilder::new() @@ -98,4 +94,20 @@ impl State { } } } + + pub fn load(root: &Path, id: &str) -> Result { + let path = Self::file_path(root, id); + if !path.exists() { + bail!("Container {} doesn't exist", id); + } + + let state_file = File::open(&path) + .with_context(|| OzonecErr::OpenFile(path.to_string_lossy().to_string()))?; + let state = serde_json::from_reader(&state_file)?; + Ok(state) + } + + fn file_path(root: &Path, id: &str) -> PathBuf { + root.join(id).join("state.json") + } } diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 5b0de9b0d..5e4166e3b 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -30,24 +30,26 @@ use nix::{ statfs::statfs, wait::{waitpid, WaitStatus}, }, - unistd::{self, chown, getegid, geteuid, sethostname, Gid, Pid, Uid}, + unistd::{self, chown, getegid, geteuid, sethostname, unlink, Gid, Pid, Uid}, }; use prctl::set_dumpable; use procfs::process::ProcState; use super::{ - namespace::NsController, notify_socket::NOTIFY_SOCKET, process::clone_process, NotifyListener, - Process, + namespace::NsController, + notify_socket::{NotifySocket, NOTIFY_SOCKET}, + process::clone_process, + NotifyListener, Process, }; use crate::{ - container::Container, + container::{Container, State}, linux::{rootfs::Rootfs, seccomp::set_seccomp}, utils::{Channel, Message, OzonecErr}, }; use oci_spec::{ linux::{Device as OciDevice, IdMapping, NamespaceType}, runtime::RuntimeConfig, - state::{ContainerStatus, State}, + state::{ContainerStatus, State as OciState}, }; pub struct LinuxContainer { @@ -92,6 +94,24 @@ impl LinuxContainer { }) } + pub fn load_from_state(state: &State, console_socket: &Option) -> Result { + let root_path = format!("{}/{}", state.root.to_string_lossy().to_string(), &state.id); + let config = state + .config + .clone() + .ok_or(anyhow!("Can't find config in state"))?; + + Ok(Self { + id: state.id.clone(), + root: root_path, + config, + pid: state.pid, + start_time: state.start_time, + created_time: state.created_time.into(), + console_socket: console_socket.clone(), + }) + } + fn validate_config(config: &RuntimeConfig) -> Result<()> { if config.linux.is_none() { bail!("There is no linux specific configuration in config.json for Linux container"); @@ -576,7 +596,7 @@ impl Container for LinuxContainer { &self.created_time } - fn get_oci_state(&self) -> Result { + fn get_oci_state(&self) -> Result { let status = self.container_status()?; let pid = if status != ContainerStatus::Stopped { self.pid @@ -598,7 +618,7 @@ impl Container for LinuxContainer { } else { HashMap::new() }; - Ok(State { + Ok(OciState { ociVersion: self.config.ociVersion.clone(), id: self.id.clone(), status, @@ -689,6 +709,15 @@ impl Container for LinuxContainer { } fn start(&mut self) -> Result<()> { + let path = PathBuf::from(&self.root).join(NOTIFY_SOCKET); + let mut notify_socket = NotifySocket::new(&path); + + notify_socket.notify_container_start()?; + unlink(&path).with_context(|| "Failed to delete notify.sock")?; + self.start_time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .with_context(|| "Failed to get start time")? + .as_secs(); Ok(()) } diff --git a/ozonec/src/linux/notify_socket.rs b/ozonec/src/linux/notify_socket.rs index 65ab4c9f0..a2ccc6959 100644 --- a/ozonec/src/linux/notify_socket.rs +++ b/ozonec/src/linux/notify_socket.rs @@ -12,14 +12,19 @@ use std::{ env, - io::Read, - os::{fd::AsRawFd, unix::net::UnixListener}, - path::{Path, PathBuf}, + io::{Read, Write}, + os::{ + fd::AsRawFd, + unix::net::{UnixListener, UnixStream}, + }, + path::PathBuf, }; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use nix::unistd::{self, chdir}; +use crate::utils::OzonecErr; + pub const NOTIFY_SOCKET: &str = "notify.sock"; pub struct NotifyListener { @@ -30,8 +35,7 @@ impl NotifyListener { pub fn new(root: PathBuf) -> Result { // The length of path of Unix domain socket has the limit 108, which is smaller then // the maximum length of file on Linux (255). - let cwd = - env::current_dir().with_context(|| "Current working directory value is invalid")?; + let cwd = env::current_dir().with_context(|| OzonecErr::GetCurDir)?; chdir(&root).with_context(|| "Failed to chdir to root directory")?; let listener = UnixListener::bind(NOTIFY_SOCKET).with_context(|| "Failed to bind notify socket")?; @@ -58,3 +62,29 @@ impl NotifyListener { Ok(unistd::close(self.socket.as_raw_fd())?) } } + +pub struct NotifySocket { + path: PathBuf, +} + +impl NotifySocket { + pub fn new(path: &PathBuf) -> Self { + Self { path: path.into() } + } + + pub fn notify_container_start(&mut self) -> Result<()> { + let cwd = env::current_dir().with_context(|| OzonecErr::GetCurDir)?; + let root_path = self + .path + .parent() + .ok_or(anyhow!("Invalid notify socket path"))?; + chdir(root_path).with_context(|| "Failed to chdir to root directory")?; + + let mut stream = + UnixStream::connect(NOTIFY_SOCKET).with_context(|| "Failed to connect notify.sock")?; + stream.write_all(b"start container")?; + chdir(&cwd).with_context(|| "Failed to chdir to previous working directory")?; + + Ok(()) + } +} diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index dd88161aa..1d6dc9b76 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -23,6 +23,7 @@ use std::{ use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; +use commands::Start; use log::info; use nix::unistd::geteuid; @@ -49,6 +50,7 @@ struct GlobalOpts { #[derive(Subcommand, Debug)] enum StandardCmd { Create(Create), + Start(Start), } // Extended commands not documented in [OCI Command Line Interface]. @@ -86,6 +88,10 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { } })? } + StandardCmd::Start(start) => { + info!("Exec command: {:?}", start); + start.run(root)? + } }, Command::Extend(cmd) => (), } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 0631f47b1..db1e24303 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -36,4 +36,6 @@ pub enum OzonecErr { SetCaps(String), #[error("Failed to add architecture to seccomp filter")] AddScmpArch, + #[error("Failed to get current directory")] + GetCurDir, } -- Gitee From bf0076cf0d25c36db56fcdd87dad43507f550269 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 26 Aug 2024 09:30:50 +0800 Subject: [PATCH 2116/2187] ozonec/linux: Add apparmor support --- ozonec/src/linux/apparmor.rs | 44 +++++++++++++++++++++++++++++++++++ ozonec/src/linux/container.rs | 2 ++ ozonec/src/linux/mod.rs | 1 + ozonec/src/linux/process.rs | 15 +++++++++++- 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ozonec/src/linux/apparmor.rs diff --git a/ozonec/src/linux/apparmor.rs b/ozonec/src/linux/apparmor.rs new file mode 100644 index 000000000..1f91c59c4 --- /dev/null +++ b/ozonec/src/linux/apparmor.rs @@ -0,0 +1,44 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{fs, path::Path}; + +use anyhow::{Context, Result}; + +const APPARMOR_ENABLED_PATH: &str = "/sys/module/apparmor/parameters/enabled"; +const APPARMOR_INTERFACE: &str = "/proc/self/attr/apparmor/exec"; +const APPARMOR_LEGACY_INTERFACE: &str = "/proc/self/attr/exec"; + +pub fn is_enabled() -> Result { + let enabled = fs::read_to_string(APPARMOR_ENABLED_PATH) + .with_context(|| format!("Failed to read {}", APPARMOR_ENABLED_PATH))?; + Ok(enabled.starts_with('Y')) +} + +pub fn apply_profile(profile: &str) -> Result<()> { + if profile.is_empty() { + return Ok(()); + } + + // Try the module specific subdirectory. This is recommended to configure LSMs + // since Linux kernel 5.1. AppArmor has such a directory since Linux kernel 5.8. + match activate_profile(Path::new(APPARMOR_INTERFACE), profile) { + Ok(_) => Ok(()), + Err(_) => activate_profile(Path::new(APPARMOR_LEGACY_INTERFACE), profile) + .with_context(|| "Failed to apply apparmor profile"), + } +} + +fn activate_profile(path: &Path, profile: &str) -> Result<()> { + fs::write(path, format!("exec {}", profile))?; + Ok(()) +} diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 5e4166e3b..b01991cce 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -221,6 +221,8 @@ impl LinuxContainer { .with_context(|| "Failed to chroot")?; } + process.set_apparmor()?; + if self.config.root.readonly { LinuxContainer::mount_rootfs_readonly()?; } diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index 4e09c1ebe..bde83b87d 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +mod apparmor; mod container; mod device; mod mount; diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 0a590c679..2de80b2d3 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -28,7 +28,10 @@ use nix::unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}; use prctl::{set_keep_capabilities, set_no_new_privileges}; use rlimit::{setrlimit, Resource, Rlim}; -use super::terminal::{connect_stdio, setup_console}; +use super::{ + apparmor, + terminal::{connect_stdio, setup_console}, +}; use crate::utils::OzonecErr; use oci_spec::{linux::IoPriClass, process::Process as OciProcess}; @@ -193,6 +196,16 @@ impl Process { Ok(()) } + pub fn set_apparmor(&self) -> Result<()> { + if let Some(profile) = &self.oci.apparmorProfile { + if !apparmor::is_enabled()? { + bail!("Apparmor is disabled."); + } + apparmor::apply_profile(profile)?; + } + Ok(()) + } + pub fn reset_capabilities(&self) -> Result<()> { let permitted = caps::read(None, CapSet::Permitted) .with_context(|| OzonecErr::GetAllCaps("Permitted".to_string()))?; -- Gitee From c30503eb7ec960a4cca655c78d5706ba35ad2944 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 26 Aug 2024 17:50:41 +0800 Subject: [PATCH 2117/2187] ozonec/commands: Add support to executing command in container --- ozonec/src/commands/exec.rs | 117 ++++++++++++++++++++++++++++++++++ ozonec/src/commands/mod.rs | 2 + ozonec/src/commands/start.rs | 7 +- ozonec/src/linux/container.rs | 2 + ozonec/src/main.rs | 17 +++-- ozonec/src/utils/error.rs | 4 ++ 6 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 ozonec/src/commands/exec.rs diff --git a/ozonec/src/commands/exec.rs b/ozonec/src/commands/exec.rs new file mode 100644 index 000000000..0bc9b733d --- /dev/null +++ b/ozonec/src/commands/exec.rs @@ -0,0 +1,117 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use core::str; +use std::path::{Path, PathBuf}; + +use anyhow::{anyhow, bail, Context, Result}; +use clap::{builder::NonEmptyStringValueParser, Parser}; +use oci_spec::state::ContainerStatus; + +use crate::{ + container::{Action, Container, Launcher, State}, + linux::LinuxContainer, + utils::OzonecErr, +}; + +/// Execute a new process inside the container +#[derive(Debug, Parser)] +pub struct Exec { + /// Path to an AF_UNIX socket which will receive a file descriptor of the master end + /// of the console's pseudoterminal + #[arg(long)] + pub console_socket: Option, + /// Allocate a pseudio-TTY + #[arg(short, long)] + pub tty: bool, + /// Current working directory in the container + #[arg(long)] + pub cwd: Option, + /// Specify the file to write the process pid to + #[arg(long)] + pub pid_file: Option, + /// Specify environment variables + #[arg(short, long, value_parser = parse_key_val::, number_of_values = 1)] + pub env: Vec<(String, String)>, + /// Prevent the process from gaining additional privileges + #[arg(long)] + pub no_new_privs: bool, + /// Specify the container id + #[arg(value_parser = NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, + /// Specify the command to execute in the container + #[arg(required = false)] + pub command: Vec, +} + +fn parse_key_val(s: &str) -> Result<(T, U)> +where + T: str::FromStr, + T::Err: std::error::Error + Send + Sync + 'static, + U: str::FromStr, + U::Err: std::error::Error + Send + Sync + 'static, +{ + let pos = s + .find('=') + .ok_or(anyhow!("Invalid KEY=value: no '=' found in '{}'", s))?; + Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) +} + +impl Exec { + fn launcher(&self, root: &Path) -> Result { + let mut container_state = + State::load(root, &self.container_id).with_context(|| OzonecErr::LoadConState)?; + + if let Some(config) = container_state.config.as_mut() { + config.process.terminal = self.tty; + config.process.cwd = if let Some(cwd) = &self.cwd { + cwd.to_string_lossy().to_string() + } else { + String::from("/") + }; + + for (env_name, env_value) in &self.env { + config + .process + .env + .as_mut() + .unwrap() + .push(format!("{}={}", env_name, env_value)); + } + config.process.noNewPrivileges = Some(self.no_new_privs); + config.process.args = Some(self.command.clone()); + } + + let container = LinuxContainer::load_from_state(&container_state, &self.console_socket)?; + let oci_status = container + .get_oci_state() + .with_context(|| OzonecErr::GetOciState)? + .status; + if oci_status != ContainerStatus::Created && oci_status != ContainerStatus::Running { + bail!("Can't exec in container with {:?} state", oci_status); + } + + Ok(Launcher::new( + &container_state.bundle, + root, + false, + Box::new(container), + self.pid_file.clone(), + )) + } + + pub fn run(&self, root: &Path) -> Result<()> { + let mut launcher = self.launcher(root)?; + launcher.launch(Action::Exec)?; + Ok(()) + } +} diff --git a/ozonec/src/commands/mod.rs b/ozonec/src/commands/mod.rs index 0ed00ef1d..a8bd46912 100644 --- a/ozonec/src/commands/mod.rs +++ b/ozonec/src/commands/mod.rs @@ -11,7 +11,9 @@ // See the Mulan PSL v2 for more details. mod create; +mod exec; mod start; pub use create::Create; +pub use exec::Exec; pub use start::Start; diff --git a/ozonec/src/commands/start.rs b/ozonec/src/commands/start.rs index 782701e47..c8a68802b 100644 --- a/ozonec/src/commands/start.rs +++ b/ozonec/src/commands/start.rs @@ -19,6 +19,7 @@ use oci_spec::state::ContainerStatus; use crate::{ container::{Action, Container, Launcher, State}, linux::LinuxContainer, + utils::OzonecErr, }; /// Start the user-specified code from process @@ -29,12 +30,12 @@ pub struct Start { impl Start { fn launcher(&self, root: &Path) -> Result { - let container_state = State::load(root, &self.container_id) - .with_context(|| "Failed to load container state")?; + let container_state = + State::load(root, &self.container_id).with_context(|| OzonecErr::LoadConState)?; let container = LinuxContainer::load_from_state(&container_state, &None)?; let oci_status = container .get_oci_state() - .with_context(|| "Failed to get oci state")? + .with_context(|| OzonecErr::GetOciState)? .status; if oci_status != ContainerStatus::Created { diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index b01991cce..fc7f56485 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -724,6 +724,8 @@ impl Container for LinuxContainer { } fn exec(&mut self, process: &mut Process) -> Result<()> { + // process.init is false. + self.create(process)?; Ok(()) } diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index 1d6dc9b76..7c30dceed 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -23,7 +23,7 @@ use std::{ use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; -use commands::Start; +use commands::{Exec, Start}; use log::info; use nix::unistd::geteuid; @@ -55,7 +55,9 @@ enum StandardCmd { // Extended commands not documented in [OCI Command Line Interface]. #[derive(Subcommand, Debug)] -enum ExtendCmd {} +enum ExtendCmd { + Exec(Exec), +} #[derive(Subcommand, Debug)] enum Command { @@ -79,7 +81,7 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { match command { Command::Standard(cmd) => match cmd { StandardCmd::Create(create) => { - info!("Exec command: {:?}", create); + info!("Run command: {:?}", create); let mut root_exist = false; create.run(root, &mut root_exist).inspect_err(|_| { @@ -89,11 +91,16 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { })? } StandardCmd::Start(start) => { - info!("Exec command: {:?}", start); + info!("Run command: {:?}", start); start.run(root)? } }, - Command::Extend(cmd) => (), + Command::Extend(cmd) => match cmd { + ExtendCmd::Exec(exec) => { + info!("Run command: {:?}", exec); + exec.run(root)? + } + }, } Ok(()) } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index db1e24303..ffe137560 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -38,4 +38,8 @@ pub enum OzonecErr { AddScmpArch, #[error("Failed to get current directory")] GetCurDir, + #[error("Failed to load container state")] + LoadConState, + #[error("Failed to get oci state")] + GetOciState, } -- Gitee From 76c014cba2289e96bb5328185668b46800977912 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 26 Aug 2024 19:41:41 +0800 Subject: [PATCH 2118/2187] ozonec/commands: Add support for killing container --- ozonec/src/commands/kill.rs | 72 +++++++++++++++++++++++++++++++++++ ozonec/src/commands/mod.rs | 2 + ozonec/src/container/mod.rs | 2 +- ozonec/src/linux/container.rs | 13 ++++++- ozonec/src/main.rs | 7 +++- 5 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 ozonec/src/commands/kill.rs diff --git a/ozonec/src/commands/kill.rs b/ozonec/src/commands/kill.rs new file mode 100644 index 000000000..070b79322 --- /dev/null +++ b/ozonec/src/commands/kill.rs @@ -0,0 +1,72 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{path::Path, str::FromStr, thread::sleep, time::Duration}; + +use anyhow::{bail, Context, Result}; +use clap::{builder::NonEmptyStringValueParser, Parser}; +use nix::sys::signal::Signal; +use oci_spec::state::ContainerStatus; + +use crate::{ + container::{Container, State}, + linux::LinuxContainer, +}; + +/// Send a signal to the container process +#[derive(Parser, Debug)] +pub struct Kill { + /// Specify the container id + #[arg(value_parser = NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, + /// The signal to send to the container process + pub signal: String, +} + +impl Kill { + pub fn run(&self, root: &Path) -> Result<()> { + let container_state = State::load(root, &self.container_id)?; + let signal = parse_signal(&self.signal).with_context(|| "Invalid signal")?; + let container = LinuxContainer::load_from_state(&container_state, &None)?; + let mut status = container.get_oci_state()?.status; + + if status == ContainerStatus::Stopped { + bail!("The container is alread stopped"); + } + + container.kill(signal)?; + + let mut _retry = 0; + status = container.get_oci_state()?.status; + while status != ContainerStatus::Stopped { + sleep(Duration::from_millis(1)); + if _retry > 3 { + bail!("The container is still not stopped."); + } + status = container.get_oci_state()?.status; + _retry += 1; + } + Ok(()) + } +} + +fn parse_signal(signal: &str) -> Result { + if let Ok(num) = signal.parse::() { + return Ok(Signal::try_from(num)?); + } + + let mut uppercase_sig = signal.to_uppercase(); + if !uppercase_sig.starts_with("SIG") { + uppercase_sig = format!("SIG{}", &uppercase_sig); + } + Ok(Signal::from_str(&uppercase_sig)?) +} diff --git a/ozonec/src/commands/mod.rs b/ozonec/src/commands/mod.rs index a8bd46912..68d05ee31 100644 --- a/ozonec/src/commands/mod.rs +++ b/ozonec/src/commands/mod.rs @@ -12,8 +12,10 @@ mod create; mod exec; +mod kill; mod start; pub use create::Create; pub use exec::Exec; +pub use kill::Kill; pub use start::Start; diff --git a/ozonec/src/container/mod.rs b/ozonec/src/container/mod.rs index ca576a38b..3fbfa4c41 100644 --- a/ozonec/src/container/mod.rs +++ b/ozonec/src/container/mod.rs @@ -41,5 +41,5 @@ pub trait Container { fn exec(&mut self, process: &mut Process) -> Result<()>; - fn kill(&mut self, sig: Signal) -> Result<()>; + fn kill(&self, sig: Signal) -> Result<()>; } diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index fc7f56485..6d520cdac 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -26,7 +26,7 @@ use nix::{ errno::Errno, mount::MsFlags, sys::{ - signal::Signal, + signal::{kill, Signal}, statfs::statfs, wait::{waitpid, WaitStatus}, }, @@ -729,7 +729,16 @@ impl Container for LinuxContainer { Ok(()) } - fn kill(&mut self, sig: Signal) -> Result<()> { + fn kill(&self, sig: Signal) -> Result<()> { + let pid = Pid::from_raw(self.pid); + match kill(pid, None) { + Err(errno) => { + if errno != Errno::ESRCH { + bail!("Failed to kill process {}: {:?}", pid, errno); + } + } + Ok(_) => kill(pid, sig)?, + } Ok(()) } } diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index 7c30dceed..ea1e51292 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -23,7 +23,7 @@ use std::{ use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; -use commands::{Exec, Start}; +use commands::{Exec, Kill, Start}; use log::info; use nix::unistd::geteuid; @@ -51,6 +51,7 @@ struct GlobalOpts { enum StandardCmd { Create(Create), Start(Start), + Kill(Kill), } // Extended commands not documented in [OCI Command Line Interface]. @@ -94,6 +95,10 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { info!("Run command: {:?}", start); start.run(root)? } + StandardCmd::Kill(kill) => { + info!("Run command: {:?}", kill); + kill.run(root)? + } }, Command::Extend(cmd) => match cmd { ExtendCmd::Exec(exec) => { -- Gitee From 9402f3d41ff29ef098ef7a596b36357a29e9865e Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 27 Aug 2024 07:32:24 +0800 Subject: [PATCH 2119/2187] ozonec/commands: Add support for deleting container --- ozonec/src/commands/delete.rs | 52 +++++++++++++++++++++++++++++++++++ ozonec/src/commands/exec.rs | 11 +++----- ozonec/src/commands/kill.rs | 21 ++------------ ozonec/src/commands/mod.rs | 2 ++ ozonec/src/commands/start.rs | 7 ++--- ozonec/src/container/mod.rs | 2 ++ ozonec/src/container/state.rs | 8 +++++- ozonec/src/linux/container.rs | 52 +++++++++++++++++++++++++++++++++-- ozonec/src/main.rs | 7 ++++- 9 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 ozonec/src/commands/delete.rs diff --git a/ozonec/src/commands/delete.rs b/ozonec/src/commands/delete.rs new file mode 100644 index 000000000..67f712e4d --- /dev/null +++ b/ozonec/src/commands/delete.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::{fs, path::Path}; + +use anyhow::{bail, Result}; +use clap::{builder::NonEmptyStringValueParser, Parser}; + +use crate::{ + container::{Container, State}, + linux::LinuxContainer, +}; + +/// Release container resources after the container process has exited +#[derive(Debug, Parser)] +pub struct Delete { + /// Specify the container id + #[arg(value_parser = NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, + /// Force to delete the container (kill the container using SIGKILL) + #[arg(short, long)] + pub force: bool, +} + +impl Delete { + pub fn run(&self, root: &Path) -> Result<()> { + let state_dir = root.join(&self.container_id); + if !state_dir.exists() { + bail!("{} doesn't exist", state_dir.display()); + } + + let state = if let Ok(s) = State::load(root, &self.container_id) { + s + } else { + fs::remove_dir_all(state_dir)?; + return Ok(()); + }; + + let container = LinuxContainer::load_from_state(&state, &None)?; + container.delete(&state, self.force)?; + Ok(()) + } +} diff --git a/ozonec/src/commands/exec.rs b/ozonec/src/commands/exec.rs index 0bc9b733d..51347ff73 100644 --- a/ozonec/src/commands/exec.rs +++ b/ozonec/src/commands/exec.rs @@ -18,7 +18,7 @@ use clap::{builder::NonEmptyStringValueParser, Parser}; use oci_spec::state::ContainerStatus; use crate::{ - container::{Action, Container, Launcher, State}, + container::{Action, Launcher, State}, linux::LinuxContainer, utils::OzonecErr, }; @@ -92,12 +92,9 @@ impl Exec { } let container = LinuxContainer::load_from_state(&container_state, &self.console_socket)?; - let oci_status = container - .get_oci_state() - .with_context(|| OzonecErr::GetOciState)? - .status; - if oci_status != ContainerStatus::Created && oci_status != ContainerStatus::Running { - bail!("Can't exec in container with {:?} state", oci_status); + let status = container.status()?; + if status != ContainerStatus::Created && status != ContainerStatus::Running { + bail!("Can't exec in container with {:?} state", status); } Ok(Launcher::new( diff --git a/ozonec/src/commands/kill.rs b/ozonec/src/commands/kill.rs index 070b79322..d67a0079c 100644 --- a/ozonec/src/commands/kill.rs +++ b/ozonec/src/commands/kill.rs @@ -10,12 +10,11 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::{path::Path, str::FromStr, thread::sleep, time::Duration}; +use std::{path::Path, str::FromStr}; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use clap::{builder::NonEmptyStringValueParser, Parser}; use nix::sys::signal::Signal; -use oci_spec::state::ContainerStatus; use crate::{ container::{Container, State}, @@ -37,24 +36,8 @@ impl Kill { let container_state = State::load(root, &self.container_id)?; let signal = parse_signal(&self.signal).with_context(|| "Invalid signal")?; let container = LinuxContainer::load_from_state(&container_state, &None)?; - let mut status = container.get_oci_state()?.status; - - if status == ContainerStatus::Stopped { - bail!("The container is alread stopped"); - } container.kill(signal)?; - - let mut _retry = 0; - status = container.get_oci_state()?.status; - while status != ContainerStatus::Stopped { - sleep(Duration::from_millis(1)); - if _retry > 3 { - bail!("The container is still not stopped."); - } - status = container.get_oci_state()?.status; - _retry += 1; - } Ok(()) } } diff --git a/ozonec/src/commands/mod.rs b/ozonec/src/commands/mod.rs index 68d05ee31..a5751b550 100644 --- a/ozonec/src/commands/mod.rs +++ b/ozonec/src/commands/mod.rs @@ -11,11 +11,13 @@ // See the Mulan PSL v2 for more details. mod create; +mod delete; mod exec; mod kill; mod start; pub use create::Create; +pub use delete::Delete; pub use exec::Exec; pub use kill::Kill; pub use start::Start; diff --git a/ozonec/src/commands/start.rs b/ozonec/src/commands/start.rs index c8a68802b..33ce7dd6c 100644 --- a/ozonec/src/commands/start.rs +++ b/ozonec/src/commands/start.rs @@ -17,7 +17,7 @@ use clap::Parser; use oci_spec::state::ContainerStatus; use crate::{ - container::{Action, Container, Launcher, State}, + container::{Action, Launcher, State}, linux::LinuxContainer, utils::OzonecErr, }; @@ -33,10 +33,7 @@ impl Start { let container_state = State::load(root, &self.container_id).with_context(|| OzonecErr::LoadConState)?; let container = LinuxContainer::load_from_state(&container_state, &None)?; - let oci_status = container - .get_oci_state() - .with_context(|| OzonecErr::GetOciState)? - .status; + let oci_status = container.status()?; if oci_status != ContainerStatus::Created { bail!("Can't start a container with {:?} status", oci_status); diff --git a/ozonec/src/container/mod.rs b/ozonec/src/container/mod.rs index 3fbfa4c41..761e2517e 100644 --- a/ozonec/src/container/mod.rs +++ b/ozonec/src/container/mod.rs @@ -42,4 +42,6 @@ pub trait Container { fn exec(&mut self, process: &mut Process) -> Result<()>; fn kill(&self, sig: Signal) -> Result<()>; + + fn delete(&self, state: &State, force: bool) -> Result<()>; } diff --git a/ozonec/src/container/state.rs b/ozonec/src/container/state.rs index 41ec2ec96..e689a1ee6 100644 --- a/ozonec/src/container/state.rs +++ b/ozonec/src/container/state.rs @@ -11,7 +11,7 @@ // See the Mulan PSL v2 for more details. use std::{ - fs::{DirBuilder, File, OpenOptions}, + fs::{self, DirBuilder, File, OpenOptions}, os::unix::fs::DirBuilderExt, path::{Path, PathBuf}, time::SystemTime, @@ -107,6 +107,12 @@ impl State { Ok(state) } + pub fn remove_dir(&self) -> Result<()> { + let state_dir = &self.root.join(&self.id); + fs::remove_dir_all(state_dir).with_context(|| "Failed to remove state directory")?; + Ok(()) + } + fn file_path(root: &Path, id: &str) -> PathBuf { root.join(id).join("state.json") } diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 6d520cdac..26dbc6fb8 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -16,7 +16,8 @@ use std::{ io::Write, os::{fd::AsRawFd, unix::net::UnixStream}, path::{Path, PathBuf}, - time::SystemTime, + thread::sleep, + time::{Duration, SystemTime}, }; use anyhow::{anyhow, bail, Context, Result}; @@ -292,7 +293,7 @@ impl LinuxContainer { Ok(()) } - fn container_status(&self) -> Result { + fn get_container_status(&self) -> Result { if self.pid == -1 { return Ok(ContainerStatus::Creating); } @@ -323,6 +324,13 @@ impl LinuxContainer { } } + pub fn status(&self) -> Result { + Ok(self + .get_oci_state() + .with_context(|| OzonecErr::GetOciState)? + .status) + } + fn ns_controller(&self) -> Result { Ok(self .config @@ -599,7 +607,7 @@ impl Container for LinuxContainer { } fn get_oci_state(&self) -> Result { - let status = self.container_status()?; + let status = self.get_container_status()?; let pid = if status != ContainerStatus::Stopped { self.pid } else { @@ -730,6 +738,14 @@ impl Container for LinuxContainer { } fn kill(&self, sig: Signal) -> Result<()> { + let mut status = self.status()?; + if status == ContainerStatus::Stopped { + bail!("The container is already stopped"); + } + if status == ContainerStatus::Creating { + bail!("The container has not been created"); + } + let pid = Pid::from_raw(self.pid); match kill(pid, None) { Err(errno) => { @@ -739,6 +755,36 @@ impl Container for LinuxContainer { } Ok(_) => kill(pid, sig)?, } + + let mut _retry = 0; + status = self.status()?; + while status != ContainerStatus::Stopped { + sleep(Duration::from_millis(1)); + if _retry > 3 { + bail!("The container is still not stopped."); + } + status = self.status()?; + _retry += 1; + } + Ok(()) + } + + fn delete(&self, state: &State, force: bool) -> Result<()> { + match self.status()? { + ContainerStatus::Stopped => state.remove_dir()?, + _ => { + if force { + self.kill(Signal::SIGKILL) + .with_context(|| "Failed to kill the container by force")?; + state.remove_dir()?; + } else { + bail!( + "Failed to delete container {} which is not stopped", + &state.id + ); + } + } + } Ok(()) } } diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index ea1e51292..6eceaeb36 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -23,7 +23,7 @@ use std::{ use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; -use commands::{Exec, Kill, Start}; +use commands::{Delete, Exec, Kill, Start}; use log::info; use nix::unistd::geteuid; @@ -52,6 +52,7 @@ enum StandardCmd { Create(Create), Start(Start), Kill(Kill), + Delete(Delete), } // Extended commands not documented in [OCI Command Line Interface]. @@ -99,6 +100,10 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { info!("Run command: {:?}", kill); kill.run(root)? } + StandardCmd::Delete(delete) => { + info!("Run command: {:?}", delete); + delete.run(root)? + } }, Command::Extend(cmd) => match cmd { ExtendCmd::Exec(exec) => { -- Gitee From 4806a20dc083f2c5dc6265816b99df42fd5effe9 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 27 Aug 2024 07:51:32 +0800 Subject: [PATCH 2120/2187] ozonec/commands: Add support for quering container state --- ozonec/src/commands/mod.rs | 2 ++ ozonec/src/commands/state.rs | 55 ++++++++++++++++++++++++++++++++++++ ozonec/src/main.rs | 7 ++++- 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 ozonec/src/commands/state.rs diff --git a/ozonec/src/commands/mod.rs b/ozonec/src/commands/mod.rs index a5751b550..f8096f3f6 100644 --- a/ozonec/src/commands/mod.rs +++ b/ozonec/src/commands/mod.rs @@ -15,9 +15,11 @@ mod delete; mod exec; mod kill; mod start; +mod state; pub use create::Create; pub use delete::Delete; pub use exec::Exec; pub use kill::Kill; pub use start::Start; +pub use state::State; diff --git a/ozonec/src/commands/state.rs b/ozonec/src/commands/state.rs new file mode 100644 index 000000000..d667694ff --- /dev/null +++ b/ozonec/src/commands/state.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; +use clap::{builder::NonEmptyStringValueParser, Parser}; +use serde::{Deserialize, Serialize}; + +use crate::{container::State as ContainerState, linux::LinuxContainer}; + +/// Request the container state +#[derive(Debug, Parser)] +pub struct State { + /// Specify the container id + #[arg(value_parser = NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RuntimeState { + pub oci_version: String, + pub id: String, + pub status: String, + pub pid: i32, + pub bundle: PathBuf, +} + +impl State { + pub fn run(&self, root: &Path) -> Result<()> { + let state = ContainerState::load(root, &self.container_id)?; + let container = LinuxContainer::load_from_state(&state, &None)?; + let runtime_state = RuntimeState { + oci_version: state.oci_version, + id: state.id, + pid: state.pid, + status: container.status()?.to_string(), + bundle: state.bundle, + }; + let json_data = &serde_json::to_string_pretty(&runtime_state) + .with_context(|| "Failed to get json data of container state")?; + + println!("{}", json_data); + Ok(()) + } +} diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index 6eceaeb36..06d67a656 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -23,7 +23,7 @@ use std::{ use anyhow::{Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; -use commands::{Delete, Exec, Kill, Start}; +use commands::{Delete, Exec, Kill, Start, State}; use log::info; use nix::unistd::geteuid; @@ -51,6 +51,7 @@ struct GlobalOpts { enum StandardCmd { Create(Create), Start(Start), + State(State), Kill(Kill), Delete(Delete), } @@ -104,6 +105,10 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { info!("Run command: {:?}", delete); delete.run(root)? } + StandardCmd::State(state) => { + info!("Run command: {:?}", state); + state.run(root)? + } }, Command::Extend(cmd) => match cmd { ExtendCmd::Exec(exec) => { -- Gitee From 0780857b9c57ffbee231ad9fe99886880d53d95d Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 27 Aug 2024 07:52:39 +0800 Subject: [PATCH 2121/2187] ozonec: Add Cargo.lock --- ozonec/Cargo.lock | 910 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 910 insertions(+) create mode 100644 ozonec/Cargo.lock diff --git a/ozonec/Cargo.lock b/ozonec/Cargo.lock new file mode 100644 index 000000000..f2e9b7475 --- /dev/null +++ b/ozonec/Cargo.lock @@ -0,0 +1,910 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "caps" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.6", +] + +[[package]] +name = "clap" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clone3" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4e061ea30800291ca09663878f3953840a69b08ce244b3e8b26e894d9f60f" +dependencies = [ + "bitflags", + "uapi", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libseccomp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21c57fd8981a80019807b7b68118618d29a87177c63d704fc96e6ecd003ae5b3" +dependencies = [ + "bitflags", + "libc", + "libseccomp-sys", + "pkg-config", +] + +[[package]] +name = "libseccomp-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7cbbd4ad467251987c6e5b47d53b11a5a05add08f2447a9e2d70aef1e0d138" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", + "pin-utils", + "static_assertions", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oci_spec" +version = "0.1.0" +dependencies = [ + "anyhow", + "libc", + "nix", + "serde", + "serde_json", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "ozonec" +version = "0.1.0" +dependencies = [ + "anyhow", + "caps", + "chrono", + "clap", + "clone3", + "libc", + "libseccomp", + "log", + "nix", + "oci_spec", + "prctl", + "procfs", + "rlimit", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "prctl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52" +dependencies = [ + "libc", + "nix", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procfs" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69" +dependencies = [ + "bitflags", + "byteorder", + "chrono", + "flate2", + "hex", + "lazy_static", + "rustix", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rlimit" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a9ed03edbed449d6897c2092c71ab5f7b5fb80f6f0b1a3ed6d40a6f9fc0720" +dependencies = [ + "libc", +] + +[[package]] +name = "rustix" +version = "0.36.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "uapi" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf073840d1b16485bfe28b4e752eccee38d9ac53f152adf869708e3136561e6" +dependencies = [ + "cc", + "cfg-if", + "libc", + "uapi-proc", +] + +[[package]] +name = "uapi-proc" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54de46f980cea7b2ae8d8f7f9f1c35cf7062c68343e99345ef73758f8e60975a" +dependencies = [ + "lazy_static", + "libc", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.74", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -- Gitee From 97f718a634345e1b9dbbca8d29999d3219bafc3e Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 27 Aug 2024 08:04:39 +0800 Subject: [PATCH 2122/2187] ozonec/laucher: Add comments about process relationship --- ozonec/src/container/launcher.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ozonec/src/container/launcher.rs b/ozonec/src/container/launcher.rs index 0e0e7eb1b..ef68c3922 100644 --- a/ozonec/src/container/launcher.rs +++ b/ozonec/src/container/launcher.rs @@ -10,13 +10,32 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +// Linux container create flow: +// ozonec create | State 1 process | Stage 2 process | ozonec start +// | | | +// -> clone3 -> | | | +// <- mapping request <- | | | +// write uid/gid mappings | | | +// -> send mapping done -> | | | +// | set uid/gid | | +// | set pid namespace | | +// <- send stage 2 pid | | -> clone3 -> | +// | exit | set rest namespaces | +// | | pivot_root/chroot | +// | | set capabilities | +// | | set seccomp | +// < send ready <- | | +// | | wait for start signal | +// update pid file | | | ozonec start $id +// exit | | | <- send start signal +// | | execvp cmd | exit + use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; -use crate::{linux::Process, utils::OzonecErr}; - use super::{state::State, Container}; +use crate::{linux::Process, utils::OzonecErr}; #[derive(Debug, Clone, Copy, PartialEq)] pub enum Action { -- Gitee From 9e35e27dd0536fdcf72f38e3fb296a0f9a4f7641 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 28 Aug 2024 20:32:01 +0800 Subject: [PATCH 2123/2187] ozonec/oci_spec: Print failed reason loading config.json --- ozonec/oci_spec/src/runtime.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ozonec/oci_spec/src/runtime.rs b/ozonec/oci_spec/src/runtime.rs index f1286357b..d68dea0bd 100644 --- a/ozonec/oci_spec/src/runtime.rs +++ b/ozonec/oci_spec/src/runtime.rs @@ -12,7 +12,7 @@ use std::{collections::HashMap, fs::File, io::BufReader, path::Path}; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; #[cfg(target_os = "linux")] @@ -87,9 +87,7 @@ impl RuntimeConfig { pub fn from_file(path: &String) -> Result { let file = File::open(Path::new(path)).with_context(|| "Failed to open config.json")?; let reader = BufReader::new(file); - let config = - serde_json::from_reader(reader).with_context(|| "Failed to load config.json")?; - Ok(config) + serde_json::from_reader(reader).map_err(|e| anyhow!("Failed to load config.json: {:?}", e)) } } -- Gitee From 80cb5662509c23203dc518ccb825a9b25da5df57 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 29 Aug 2024 12:43:54 +0800 Subject: [PATCH 2124/2187] ozonec/oci_spec: Set default values when not set in config.json Although in OCI spec process.user.uid/gid, resources.devices.allow and seccomp.syscalls.args.index/value are required, but some users still don't set these values in config.json. So let's set default values to these parameters in config.json as runc/crun does. --- ozonec/oci_spec/src/linux.rs | 3 +++ ozonec/oci_spec/src/posix.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/ozonec/oci_spec/src/linux.rs b/ozonec/oci_spec/src/linux.rs index 4de386f31..6dafcd5a7 100644 --- a/ozonec/oci_spec/src/linux.rs +++ b/ozonec/oci_spec/src/linux.rs @@ -130,6 +130,7 @@ fn default_device_type() -> String { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CgroupDevice { /// Whether the entry is allowed or denied. + #[serde(default)] pub allow: bool, /// Type of device. #[serde(default = "default_device_type", rename = "type")] @@ -429,8 +430,10 @@ pub enum SeccompOp { /// The specific syscall in seccomp. pub struct SeccompSyscallArg { /// Index for syscall arguments. + #[serde(default)] pub index: usize, /// Value for syscall arguments. + #[serde(default)] pub value: u64, #[serde(skip_serializing_if = "Option::is_none")] /// Value for syscall arguments. diff --git a/ozonec/oci_spec/src/posix.rs b/ozonec/oci_spec/src/posix.rs index b6ae8b377..b284d5d36 100644 --- a/ozonec/oci_spec/src/posix.rs +++ b/ozonec/oci_spec/src/posix.rs @@ -41,8 +41,10 @@ pub struct Rlimits { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct User { /// User ID in the container namespace. + #[serde(default)] pub uid: u32, /// Group ID in the container namespace. + #[serde(default)] pub gid: u32, /// [umask][umask_2] of the user. #[serde(skip_serializing_if = "Option::is_none")] -- Gitee From db7487896ec40aa7c7494eddbe47704088898a4a Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 30 Aug 2024 17:49:49 +0800 Subject: [PATCH 2125/2187] typos: Allow the spelling closID --- _typos.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_typos.toml b/_typos.toml index 1d161a7f2..9bbf0e694 100644 --- a/_typos.toml +++ b/_typos.toml @@ -18,6 +18,8 @@ RTC_MIS = "RTC_MIS" SECCOMP_FILETER_FLAG_TSYNC = "SECCOMP_FILETER_FLAG_TSYNC" test_ths = "test_ths" UART_LSR_THRE = "UART_LSR_THRE" +closID = "closID" +CLOS = "CLOS" [default.extend-words] ba = "ba" -- Gitee From 56497d9cdc78014e69fa47ead9370c1023afb266 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 30 Aug 2024 23:24:56 +0800 Subject: [PATCH 2126/2187] ozonec: Fix compile issues with rustc 1.64 --- ozonec/Cargo.lock | 93 +---------------------- ozonec/Cargo.toml | 3 +- ozonec/src/linux/container.rs | 12 +-- ozonec/src/linux/notify_socket.rs | 6 +- ozonec/src/linux/process.rs | 18 ++--- ozonec/src/linux/terminal.rs | 2 +- ozonec/src/main.rs | 5 +- ozonec/src/utils/channel.rs | 2 +- ozonec/src/utils/clone.rs | 121 ++++++++++++++++++++++++++++++ ozonec/src/utils/mod.rs | 3 + ozonec/src/utils/prctl.rs | 94 +++++++++++++++++++++++ 11 files changed, 243 insertions(+), 116 deletions(-) create mode 100644 ozonec/src/utils/clone.rs create mode 100644 ozonec/src/utils/prctl.rs diff --git a/ozonec/Cargo.lock b/ozonec/Cargo.lock index f2e9b7475..6274f9c4e 100644 --- a/ozonec/Cargo.lock +++ b/ozonec/Cargo.lock @@ -8,15 +8,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -134,16 +125,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "clone3" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4e061ea30800291ca09663878f3953840a69b08ce244b3e8b26e894d9f60f" -dependencies = [ - "bitflags", - "uapi", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -288,12 +269,6 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - [[package]] name = "memoffset" version = "0.7.1" @@ -363,16 +338,15 @@ name = "ozonec" version = "0.1.0" dependencies = [ "anyhow", + "bitflags", "caps", "chrono", "clap", - "clone3", "libc", "libseccomp", "log", "nix", "oci_spec", - "prctl", "procfs", "rlimit", "serde", @@ -392,16 +366,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "prctl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52" -dependencies = [ - "libc", - "nix", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -459,35 +423,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - [[package]] name = "rlimit" version = "0.5.4" @@ -602,32 +537,6 @@ dependencies = [ "syn 2.0.74", ] -[[package]] -name = "uapi" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf073840d1b16485bfe28b4e752eccee38d9ac53f152adf869708e3136561e6" -dependencies = [ - "cc", - "cfg-if", - "libc", - "uapi-proc", -] - -[[package]] -name = "uapi-proc" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54de46f980cea7b2ae8d8f7f9f1c35cf7062c68343e99345ef73758f8e60975a" -dependencies = [ - "lazy_static", - "libc", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", -] - [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index c3e52cf50..ac0977b33 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -8,16 +8,15 @@ description = "An OCI runtime implemented by Rust" [dependencies] anyhow = "= 1.0.71" +bitflags = "= 1.3.2" caps = "0.5.5" chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"] } clap = { version = "= 4.1.4", default-features = false, features = ["derive", "cargo", "std", "help", "usage"] } -clone3 = "0.2.3" libc = "= 0.2.146" libseccomp = "0.3.0" log = { version = "= 0.4.18", features = ["std"]} nix = "= 0.26.2" oci_spec = { path = "oci_spec" } -prctl = "1.0.0" procfs = "0.14.0" rlimit = "0.5.3" serde = { version = "= 1.0.163", features = ["derive"] } diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 26dbc6fb8..ff818920a 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -14,7 +14,7 @@ use std::{ collections::HashMap, fs::{self, canonicalize, create_dir_all, OpenOptions}, io::Write, - os::{fd::AsRawFd, unix::net::UnixStream}, + os::unix::{io::AsRawFd, net::UnixStream}, path::{Path, PathBuf}, thread::sleep, time::{Duration, SystemTime}, @@ -33,7 +33,6 @@ use nix::{ }, unistd::{self, chown, getegid, geteuid, sethostname, unlink, Gid, Pid, Uid}, }; -use prctl::set_dumpable; use procfs::process::ProcState; use super::{ @@ -45,7 +44,7 @@ use super::{ use crate::{ container::{Container, State}, linux::{rootfs::Rootfs, seccomp::set_seccomp}, - utils::{Channel, Message, OzonecErr}, + utils::{prctl, Channel, Message, OzonecErr}, }; use oci_spec::{ linux::{Device as OciDevice, IdMapping, NamespaceType}, @@ -358,14 +357,15 @@ impl LinuxContainer { if ns.path.is_none() { // Child process needs to be dumpable, otherwise the parent process is not // allowed to write the uid/gid mappings. - set_dumpable(true).map_err(|e| anyhow!("Failed to set process dumpable: {e}"))?; + prctl::set_dumpable(true) + .map_err(|e| anyhow!("Failed to set process dumpable: {e}"))?; parent_channel .send_id_mappings() .with_context(|| "Failed to send id mappings")?; fst_stage_channel .recv_id_mappings_done() .with_context(|| "Failed to receive id mappings done")?; - set_dumpable(false) + prctl::set_dumpable(false) .map_err(|e| anyhow!("Failed to set process undumpable: {e}"))?; } @@ -656,7 +656,7 @@ impl Container for LinuxContainer { // processes in namespaces to join to access host resources (or execute code). if !self.config.linux.as_ref().unwrap().namespaces.is_empty() { prctl::set_dumpable(false) - .map_err(|e| anyhow!("Failed to set process undumpable: {}", e))?; + .map_err(|e| anyhow!("Failed to set process undumpable: errno {}", e))?; } // Create channels to communicate with child processes. diff --git a/ozonec/src/linux/notify_socket.rs b/ozonec/src/linux/notify_socket.rs index a2ccc6959..956c415ea 100644 --- a/ozonec/src/linux/notify_socket.rs +++ b/ozonec/src/linux/notify_socket.rs @@ -13,9 +13,9 @@ use std::{ env, io::{Read, Write}, - os::{ - fd::AsRawFd, - unix::net::{UnixListener, UnixStream}, + os::unix::{ + io::AsRawFd, + net::{UnixListener, UnixStream}, }, path::PathBuf, }; diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 2de80b2d3..7fac8cf7c 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -16,23 +16,21 @@ use std::{ fs::{self, read_to_string}, io::{stderr, stdin, stdout}, mem, - os::fd::{AsRawFd, RawFd}, + os::unix::io::{AsRawFd, RawFd}, path::PathBuf, str::FromStr, }; use anyhow::{anyhow, bail, Context, Result}; use caps::{self, CapSet, Capability, CapsHashSet}; -use clone3::Clone3; use nix::unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}; -use prctl::{set_keep_capabilities, set_no_new_privileges}; use rlimit::{setrlimit, Resource, Rlim}; use super::{ apparmor, terminal::{connect_stdio, setup_console}, }; -use crate::utils::OzonecErr; +use crate::utils::{prctl, Clone3, OzonecErr}; use oci_spec::{linux::IoPriClass, process::Process as OciProcess}; pub struct Process { @@ -149,7 +147,7 @@ impl Process { pub fn set_no_new_privileges(&self) -> Result<()> { if let Some(no_new_privileges) = self.oci.noNewPrivileges { if no_new_privileges { - set_no_new_privileges(true) + prctl::set_no_new_privileges(true) .map_err(|e| anyhow!("Failed to set no new privileges: {}", e))?; } } @@ -238,7 +236,7 @@ impl Process { } pub fn set_id(&self, gid: Gid, uid: Uid) -> Result<()> { - set_keep_capabilities(true) + prctl::set_keep_capabilities(true) .map_err(|e| anyhow!("Failed to enable keeping capabilities: {}", e))?; setresgid(gid, gid, gid).with_context(|| "Failed to setresgid")?; setresuid(uid, uid, uid).with_context(|| "Failed to setresuid")?; @@ -247,7 +245,7 @@ impl Process { .with_context(|| OzonecErr::GetAllCaps("Permitted".to_string()))?; caps::set(None, CapSet::Effective, &permitted) .with_context(|| OzonecErr::SetCaps("Effective".to_string()))?; - set_keep_capabilities(false) + prctl::set_keep_capabilities(false) .map_err(|e| anyhow!("Failed to disable keeping capabilities: {}", e))?; Ok(()) } @@ -312,8 +310,10 @@ pub fn clone_process Result>(child_name: &str, cb: F) -> Res let mut clone3 = Clone3::default(); clone3.exit_signal(libc::SIGCHLD as u64); - // SAFETY: FFI call with valid arguments. - match unsafe { clone3.call().with_context(|| "Clone3() error")? } { + match clone3 + .call() + .map_err(|e| anyhow!("Clone3() error: {}", e))? + { 0 => { prctl::set_name(child_name) .map_err(|e| anyhow!("Failed to set process name: errno {}", e))?; diff --git a/ozonec/src/linux/terminal.rs b/ozonec/src/linux/terminal.rs index e7490d2bc..664187936 100644 --- a/ozonec/src/linux/terminal.rs +++ b/ozonec/src/linux/terminal.rs @@ -13,7 +13,7 @@ use std::{ io::IoSlice, mem::ManuallyDrop, - os::fd::{AsRawFd, RawFd}, + os::unix::io::{AsRawFd, RawFd}, }; use anyhow::{Context, Result}; diff --git a/ozonec/src/main.rs b/ozonec/src/main.rs index 06d67a656..29c529bb9 100644 --- a/ozonec/src/main.rs +++ b/ozonec/src/main.rs @@ -21,7 +21,7 @@ use std::{ process::exit, }; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use clap::{crate_description, Args, Parser, Subcommand}; use commands::{Delete, Exec, Kill, Start, State}; use log::info; @@ -87,10 +87,11 @@ fn cmd_run(command: Command, root: &Path) -> Result<()> { info!("Run command: {:?}", create); let mut root_exist = false; - create.run(root, &mut root_exist).inspect_err(|_| { + create.run(root, &mut root_exist).map_err(|e| { if !root_exist { let _ = remove_dir_all(root); } + anyhow!(e) })? } StandardCmd::Start(start) => { diff --git a/ozonec/src/utils/channel.rs b/ozonec/src/utils/channel.rs index b74c3a786..97ff2dc7c 100644 --- a/ozonec/src/utils/channel.rs +++ b/ozonec/src/utils/channel.rs @@ -15,7 +15,7 @@ use std::{ io::{IoSlice, IoSliceMut}, marker::PhantomData, mem, - os::fd::RawFd, + os::unix::io::RawFd, slice, }; diff --git a/ozonec/src/utils/clone.rs b/ozonec/src/utils/clone.rs new file mode 100644 index 000000000..2476a2f3d --- /dev/null +++ b/ozonec/src/utils/clone.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::unix::io::{AsRawFd, RawFd}; + +use anyhow::{bail, Result}; +use nix::errno::errno; + +bitflags::bitflags! { + #[derive(Default)] + pub struct Flags: u64 { + const CHILD_CLEARTID = 0x00200000; + const CHILD_SETTID = 0x01000000; + const FILES = 0x00000400; + const FS = 0x00000200; + const INTO_CGROUP = 0x200000000; + const IO = 0x80000000; + const NEWCGROUP = 0x02000000; + const NEWIPC = 0x08000000; + const NEWNET = 0x40000000; + const NEWNS = 0x00020000; + const NEWPID = 0x20000000; + const NEWTIME = 0x00000080; + const NEWUSER = 0x10000000; + const NEWUTS = 0x04000000; + const PARENT = 0x00008000; + const PARENT_SETTID = 0x00100000; + const PIDFD = 0x00001000; + const PTRACE = 0x00002000; + const SETTLS = 0x00080000; + const SIGHAND = 0x00000800; + const SYSVSEM = 0x00040000; + const THREAD = 0x00010000; + const UNTRACED = 0x00800000; + const VFORK = 0x00004000; + const VM = 0x00000100; + } +} + +#[repr(C, align(8))] +#[derive(Debug, Default)] +pub struct CloneArgs { + pub flags: u64, + pub pid_fd: u64, + pub child_tid: u64, + pub parent_tid: u64, + pub exit_signal: u64, + pub stack: u64, + pub stack_size: u64, + pub tls: u64, + pub cgroup: u64, +} + +#[derive(Default)] +pub struct Clone3<'a> { + flags: Flags, + pidfd: Option<&'a mut RawFd>, + child_tid: Option<&'a mut libc::pid_t>, + parent_tid: Option<&'a mut libc::pid_t>, + exit_signal: u64, + stack: Option<&'a mut [u8]>, + tls: Option, + cgroup: Option<&'a dyn AsRawFd>, +} + +fn option_as_mut_ptr(o: &mut Option<&mut T>) -> *mut T { + match o { + Some(inner) => *inner as *mut T, + None => std::ptr::null_mut(), + } +} + +fn option_slice_as_mut_ptr(o: &mut Option<&mut [T]>) -> *mut T { + match o { + Some(inner) => inner.as_mut_ptr(), + None => std::ptr::null_mut(), + } +} + +impl<'a> Clone3<'a> { + pub fn exit_signal(&mut self, exit_signal: u64) -> &mut Self { + self.exit_signal = exit_signal; + self + } + + pub fn call(&mut self) -> Result { + let clone_args = CloneArgs { + flags: self.flags.bits(), + pid_fd: option_as_mut_ptr(&mut self.pidfd) as u64, + child_tid: option_as_mut_ptr(&mut self.child_tid) as u64, + parent_tid: option_as_mut_ptr(&mut self.parent_tid) as u64, + exit_signal: self.exit_signal, + stack: option_slice_as_mut_ptr(&mut self.stack) as u64, + stack_size: self.stack.as_ref().map(|stack| stack.len()).unwrap_or(0) as u64, + tls: self.tls.unwrap_or(0), + cgroup: self.cgroup.map(AsRawFd::as_raw_fd).unwrap_or(0) as u64, + }; + + // SAFETY: FFI call with valid arguments. + let ret = unsafe { + libc::syscall( + libc::SYS_clone3, + &clone_args as *const CloneArgs, + core::mem::size_of::(), + ) + }; + if ret == -1 { + bail!("errno {}", errno()); + } + Ok(ret as libc::pid_t) + } +} diff --git a/ozonec/src/utils/mod.rs b/ozonec/src/utils/mod.rs index 0f37aae80..69b9517d7 100644 --- a/ozonec/src/utils/mod.rs +++ b/ozonec/src/utils/mod.rs @@ -11,9 +11,12 @@ // See the Mulan PSL v2 for more details. pub mod logger; +pub mod prctl; mod channel; +mod clone; mod error; pub use channel::{Channel, Message}; +pub use clone::Clone3; pub use error::OzonecErr; diff --git a/ozonec/src/utils/prctl.rs b/ozonec/src/utils/prctl.rs new file mode 100644 index 000000000..5bc05441f --- /dev/null +++ b/ozonec/src/utils/prctl.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::ffi::CString; + +use anyhow::{bail, Result}; +use libc::{c_int, c_ulong, prctl}; +use nix::errno::errno; + +#[allow(non_camel_case_types)] +enum PrctlOption { + PR_SET_DUMPABLE = 4, + PR_SET_KEEPCAPS = 8, + PR_SET_NAME = 15, + PR_SET_NO_NEW_PRIVS = 38, +} + +pub fn set_dumpable(dumpable: bool) -> Result<()> { + // SAFETY: FFI call with valid arguments. + let ret = unsafe { + prctl( + PrctlOption::PR_SET_DUMPABLE as c_int, + dumpable as c_ulong, + 0, + 0, + 0, + ) + }; + if ret != 0 { + bail!("errno {}", errno()); + } + Ok(()) +} + +pub fn set_keep_capabilities(keep_capabilities: bool) -> Result<()> { + // SAFETY: FFI call with valid arguments. + let ret = unsafe { + prctl( + PrctlOption::PR_SET_KEEPCAPS as c_int, + keep_capabilities as c_ulong, + 0, + 0, + 0, + ) + }; + if ret != 0 { + bail!("errno {}", errno()); + } + Ok(()) +} + +pub fn set_no_new_privileges(new_privileges: bool) -> Result<()> { + // SAFETY: FFI call with valid arguments. + let ret = unsafe { + prctl( + PrctlOption::PR_SET_NO_NEW_PRIVS as c_int, + new_privileges as c_ulong, + 0, + 0, + 0, + ) + }; + if ret != 0 { + bail!("errno {}", errno()); + } + Ok(()) +} + +pub fn set_name(name: &str) -> Result<()> { + let binding = CString::new(name).unwrap(); + // SAFETY: FFI call with valid arguments. + let ret = unsafe { + prctl( + PrctlOption::PR_SET_NAME as c_int, + binding.as_ptr() as c_ulong, + 0, + 0, + 0, + ) + }; + if ret != 0 { + bail!("errno {}", errno()); + } + Ok(()) +} -- Gitee From b3467ab10d4bf083d432ee92ebc1e4d746acba08 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 6 Sep 2024 05:37:16 +0800 Subject: [PATCH 2127/2187] ozonec/linux: Provide /dev/console in container as default OCI spec requires /dev/console as a default device when terminal is enabled. --- ozonec/src/linux/container.rs | 20 +++++++-------- ozonec/src/linux/process.rs | 15 ++++++++--- ozonec/src/linux/terminal.rs | 48 +++++++++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index ff818920a..c53e29b01 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -14,7 +14,7 @@ use std::{ collections::HashMap, fs::{self, canonicalize, create_dir_all, OpenOptions}, io::Write, - os::unix::{io::AsRawFd, net::UnixStream}, + os::unix::net::UnixStream, path::{Path, PathBuf}, thread::sleep, time::{Duration, SystemTime}, @@ -174,14 +174,12 @@ impl LinuxContainer { .set_scheduler() .with_context(|| "Failed to set scheduler")?; - if let Some(console_socket) = &self.console_socket { - let stream = UnixStream::connect(console_socket) - .with_context(|| "Failed to connect console socket")?; - process - .set_tty(Some(stream.as_raw_fd())) - .with_context(|| "Failed to set tty")?; - } - + let console_stream = match &self.console_socket { + Some(cs) => { + Some(UnixStream::connect(cs).with_context(|| "Failed to connect console socket")?) + } + None => None, + }; self.set_rest_namespaces()?; process.set_no_new_privileges()?; @@ -221,8 +219,10 @@ impl LinuxContainer { .with_context(|| "Failed to chroot")?; } + process + .set_tty(console_stream, process.init) + .with_context(|| "Failed to set tty")?; process.set_apparmor()?; - if self.config.root.readonly { LinuxContainer::mount_rootfs_readonly()?; } diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 7fac8cf7c..d2f70f9bd 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -16,7 +16,10 @@ use std::{ fs::{self, read_to_string}, io::{stderr, stdin, stdout}, mem, - os::unix::io::{AsRawFd, RawFd}, + os::unix::{ + io::{AsRawFd, RawFd}, + net::UnixStream, + }, path::PathBuf, str::FromStr, }; @@ -61,9 +64,13 @@ impl Process { p } - pub fn set_tty(&self, console_fd: Option) -> Result<()> { - if self.tty && console_fd.is_some() { - setup_console(&console_fd.unwrap()).with_context(|| "Failed to setup console")?; + pub fn set_tty(&self, console_fd: Option, mount: bool) -> Result<()> { + if self.tty { + if console_fd.is_none() { + bail!("Terminal is specified, but no console socket set"); + } + setup_console(&console_fd.unwrap().as_raw_fd(), mount) + .with_context(|| "Failed to setup console")?; } else { connect_stdio( self.stdin.as_ref().unwrap(), diff --git a/ozonec/src/linux/terminal.rs b/ozonec/src/linux/terminal.rs index 664187936..6b11702e0 100644 --- a/ozonec/src/linux/terminal.rs +++ b/ozonec/src/linux/terminal.rs @@ -11,15 +11,23 @@ // See the Mulan PSL v2 for more details. use std::{ + fs::File, io::IoSlice, mem::ManuallyDrop, os::unix::io::{AsRawFd, RawFd}, + path::PathBuf, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use nix::{ - pty::openpty, - sys::socket::{sendmsg, ControlMessage, MsgFlags, UnixAddr}, + errno::errno, + fcntl::{open, OFlag}, + mount::MsFlags, + pty::{posix_openpt, ptsname, unlockpt}, + sys::{ + socket::{sendmsg, ControlMessage, MsgFlags, UnixAddr}, + stat::{fchmod, Mode}, + }, unistd::{close, dup2}, }; @@ -31,14 +39,12 @@ pub enum Stdio { Stderr = 2, } -pub fn setup_console(console_fd: &RawFd) -> Result<()> { - let ret = openpty(None, None).with_context(|| "openpty error")?; +pub fn setup_console(console_fd: &RawFd, mount: bool) -> Result<()> { + let master_fd = posix_openpt(OFlag::O_RDWR).with_context(|| "openpt error")?; let pty_name: &[u8] = b"/dev/ptmx"; let iov = [IoSlice::new(pty_name)]; - // Use ManuallyDrop to keep fds open. - let master = ManuallyDrop::new(ret.master); - let slave = ManuallyDrop::new(ret.slave); + let master = ManuallyDrop::new(master_fd.as_raw_fd()); let fds = [master.as_raw_fd()]; let cmsg = ControlMessage::ScmRights(&fds); sendmsg::( @@ -51,8 +57,30 @@ pub fn setup_console(console_fd: &RawFd) -> Result<()> { .with_context(|| "sendmsg error")?; // SAFETY: FFI call with valid arguments. - let slave_fd = slave.as_raw_fd(); - unsafe { libc::ioctl(slave_fd, libc::TIOCSCTTY) }; + let slave_name = unsafe { ptsname(&master_fd).with_context(|| "ptsname error")? }; + unlockpt(&master_fd).with_context(|| "unlockpt error")?; + let slave_path = PathBuf::from(&slave_name); + if mount { + let file = File::create("/dev/console").with_context(|| "Failed to create /dev/console")?; + fchmod(file.as_raw_fd(), Mode::from_bits_truncate(0o666u32)) + .with_context(|| "chmod error")?; + nix::mount::mount( + Some(&slave_path), + "/dev/console", + Some("bind"), + MsFlags::MS_BIND, + None::<&str>, + ) + .with_context(|| OzonecErr::Mount(slave_name.clone()))?; + } + + let slave_fd = open(&slave_path, OFlag::O_RDWR, Mode::empty()) + .with_context(|| OzonecErr::OpenFile(slave_name.clone()))?; + let slave = ManuallyDrop::new(slave_fd); + // SAFETY: FFI call with valid arguments. + if unsafe { libc::ioctl(slave.as_raw_fd(), libc::TIOCSCTTY) } != 0 { + bail!("TIOCSCTTY error: {}", errno()); + } connect_stdio(&slave_fd, &slave_fd, &slave_fd)?; close(console_fd.as_raw_fd()).with_context(|| "Failed to close console socket")?; Ok(()) -- Gitee From 22305c762a9a73ce2e0b49f8995429a6c8fe722e Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 8 Sep 2024 09:35:39 +0800 Subject: [PATCH 2128/2187] ozonec/device: Bind devices if mknod failed --- ozonec/src/linux/container.rs | 1 + ozonec/src/linux/device.rs | 18 +++++++++++++----- ozonec/src/utils/error.rs | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index c53e29b01..e54ee3e21 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -191,6 +191,7 @@ impl LinuxContainer { .unwrap() .rootfsPropagation .clone(); + // Container running in a user namespace is not allowed to do mknod. let mknod_device = !self.is_namespace_set(NamespaceType::User)?; let mut devices: Vec = Vec::new(); if let Some(devs) = self.config.linux.as_ref().unwrap().devices.as_ref() { diff --git a/ozonec/src/linux/device.rs b/ozonec/src/linux/device.rs index baa67ab17..90575a078 100644 --- a/ozonec/src/linux/device.rs +++ b/ozonec/src/linux/device.rs @@ -166,11 +166,14 @@ impl Device { let default_devs = self.default_devices(); for dev in default_devs { if mknod { - self.mknod_device(&dev) - .with_context(|| format!("Failed to mknod device: {}", dev.path.display()))?; + if self.mknod_device(&dev).is_err() { + self.bind_device(&dev).with_context(|| { + OzonecErr::BindDev(dev.path.to_string_lossy().to_string()) + })?; + } } else { self.bind_device(&dev) - .with_context(|| format!("Failed to bind device: {}", dev.path.display()))?; + .with_context(|| OzonecErr::BindDev(dev.path.to_string_lossy().to_string()))?; } } Ok(()) @@ -211,9 +214,14 @@ impl Device { }; if mknod { - self.mknod_device(&dev_info)?; + if self.mknod_device(&dev_info).is_err() { + self.bind_device(&dev_info).with_context(|| { + OzonecErr::BindDev(dev_info.path.to_string_lossy().to_string()) + })?; + } } else { - self.bind_device(&dev_info)?; + self.bind_device(&dev_info) + .with_context(|| OzonecErr::BindDev(dev_info.path.to_string_lossy().to_string()))?; } Ok(()) } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index ffe137560..142ef9fb2 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -42,4 +42,6 @@ pub enum OzonecErr { LoadConState, #[error("Failed to get oci state")] GetOciState, + #[error("Failed to bind device: {0}")] + BindDev(String), } -- Gitee From 82bb9a19f0d20f3932a5f938f3fcf10f953c46af Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Aug 2024 20:23:52 +0800 Subject: [PATCH 2129/2187] chardev_backend: Set type for numeric literal Signed-off-by: Keqian Zhu --- chardev_backend/src/chardev.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chardev_backend/src/chardev.rs b/chardev_backend/src/chardev.rs index d13d306e2..3f9e58b06 100644 --- a/chardev_backend/src/chardev.rs +++ b/chardev_backend/src/chardev.rs @@ -317,7 +317,7 @@ impl Chardev { fn write_buffer_sync(writer: Arc>, buf: Vec) -> Result<()> { let len = buf.len(); - let mut written = 0; + let mut written = 0_usize; let mut locked_writer = writer.lock().unwrap(); while written < len { @@ -339,7 +339,7 @@ fn write_buffer_async( ) -> Result { let len = buf.len(); let mut locked_writer = writer.lock().unwrap(); - let mut written = 0; + let mut written = 0_usize; while written < len { match locked_writer.write(&buf[written..len]) { -- Gitee From ef8e557841b0b451706a56431ddf7b91db87d405 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Thu, 22 Aug 2024 22:59:34 +0800 Subject: [PATCH 2130/2187] util: Fix possible truncation warnings Signed-off-by: Keqian Zhu --- util/src/logger.rs | 2 +- util/src/loop_context.rs | 4 ++-- util/src/pixman.rs | 5 +++++ util/src/seccomp.rs | 7 ++++--- util/src/tap.rs | 4 ++-- util/src/unix.rs | 10 +++++----- virtio/src/device/net.rs | 4 ++-- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/util/src/logger.rs b/util/src/logger.rs index c5f6aec7c..0f8634bbf 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -159,7 +159,7 @@ fn init_vm_logger( current_size = Wrapping(metadata.len() as usize); let mod_time = metadata.modified()?; let sec = mod_time.duration_since(UNIX_EPOCH)?.as_secs(); - create_day = get_format_time(sec as i64)[2]; + create_day = get_format_time(i64::try_from(sec)?)[2]; }; let rotate = Mutex::new(FileRotate { handler: logfile, diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 42c94c5f5..0f00ed073 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -11,7 +11,6 @@ // See the Mulan PSL v2 for more details. use std::collections::BTreeMap; -use std::fmt; use std::fmt::Debug; use std::io::Error; use std::os::unix::io::{AsRawFd, RawFd}; @@ -19,6 +18,7 @@ use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Barrier, Mutex, RwLock}; use std::time::{Duration, Instant}; +use std::{fmt, i32}; use anyhow::{anyhow, Context, Result}; use libc::{c_void, read, EFD_CLOEXEC, EFD_NONBLOCK}; @@ -681,7 +681,7 @@ impl EventLoopContext { } let time_out_ms = match time_out { - Some(t) => t.as_millis() as i32, + Some(t) => i32::try_from(t.as_millis()).unwrap_or(i32::MAX), None => -1, }; let ev_count = match self.epoll.wait(time_out_ms, &mut self.ready_events[..]) { diff --git a/util/src/pixman.rs b/util/src/pixman.rs index 50fc5d28f..5df34e03f 100644 --- a/util/src/pixman.rs +++ b/util/src/pixman.rs @@ -199,6 +199,7 @@ pub extern "C" fn virtio_gpu_unref_resource_callback( fn pixman_format_reshift(val: u32, ofs: u32, num: u32) -> u32 { ((val >> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3) } + pub fn pixman_format_bpp(val: u32) -> u8 { pixman_format_reshift(val, 24, 8) as u8 } @@ -206,15 +207,19 @@ pub fn pixman_format_bpp(val: u32) -> u8 { pub fn pixman_format_a(val: u32) -> u8 { pixman_format_reshift(val, 12, 4) as u8 } + pub fn pixman_format_r(val: u32) -> u8 { pixman_format_reshift(val, 8, 4) as u8 } + pub fn pixman_format_g(val: u32) -> u8 { pixman_format_reshift(val, 4, 4) as u8 } + pub fn pixman_format_b(val: u32) -> u8 { pixman_format_reshift(val, 0, 4) as u8 } + pub fn pixman_format_depth(val: u32) -> u8 { pixman_format_a(val) + pixman_format_r(val) + pixman_format_g(val) + pixman_format_b(val) } diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 41206a3bc..13827e920 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -348,7 +348,7 @@ impl BpfRule { inner_append.push(constraint_filter); inner_append.push(bpf_stmt(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)); - if !self.append(&mut inner_append) { + if !self.append_to_inner(&mut inner_append) { self.start_new_session(); self.add_constraint(cmp, args_idx, args_value) } else { @@ -379,7 +379,8 @@ impl BpfRule { } /// Add bpf_filters to `inner_rules`. - fn append(&mut self, bpf_filters: &mut Vec) -> bool { + fn append_to_inner(&mut self, bpf_filters: &mut Vec) -> bool { + // bpf_filters len is less than u8::MAX. let offset = bpf_filters.len() as u8; if let Some(jf_added) = self.header_rule.jf.checked_add(offset) { @@ -446,7 +447,7 @@ impl SyscallFilter { } let prog = SockFProg { - len: sock_bpf_vec.len() as u16, + len: u16::try_from(sock_bpf_vec.len())?, sock_filter: sock_bpf_vec.as_ptr(), }; let bpf_prog_ptr = &prog as *const SockFProg; diff --git a/util/src/tap.rs b/util/src/tap.rs index 55c98a93f..4752cd679 100644 --- a/util/src/tap.rs +++ b/util/src/tap.rs @@ -194,7 +194,7 @@ impl Tap { ret } - pub fn receive_packets(&self, iovecs: &[Iovec]) -> i32 { + pub fn receive_packets(&self, iovecs: &[Iovec]) -> isize { // SAFETY: the arguments of readv has been checked and is correct. let size = unsafe { libc::readv( @@ -202,7 +202,7 @@ impl Tap { iovecs.as_ptr() as *const libc::iovec, iovecs.len() as libc::c_int, ) - } as i32; + }; if size < 0 { let e = std::io::Error::last_os_error(); if e.kind() == std::io::ErrorKind::WouldBlock { diff --git a/util/src/unix.rs b/util/src/unix.rs index bf103ee34..af4de597c 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -301,9 +301,9 @@ impl UnixSock { // SAFETY: We checked the iovecs lens before. let iovecs_len = iovecs.len(); // SAFETY: We checked the out_fds lens before. - let cmsg_len = unsafe { CMSG_LEN((std::mem::size_of_val(out_fds)) as u32) }; + let cmsg_len = unsafe { CMSG_LEN(u32::try_from(std::mem::size_of_val(out_fds))?) }; // SAFETY: We checked the out_fds lens before. - let cmsg_capacity = unsafe { CMSG_SPACE((std::mem::size_of_val(out_fds)) as u32) }; + let cmsg_capacity = unsafe { CMSG_SPACE(u32::try_from(std::mem::size_of_val(out_fds))?) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be @@ -348,7 +348,7 @@ impl UnixSock { // SAFETY: msg parameters are valid. let write_count = unsafe { sendmsg(sock.as_raw_fd(), &msg, MSG_NOSIGNAL) }; - if write_count == -1 { + if write_count < 0 { Err(anyhow!( "Failed to send msg, err: {}", std::io::Error::last_os_error() @@ -372,7 +372,7 @@ impl UnixSock { // SAFETY: We check the iovecs lens before. let iovecs_len = iovecs.len(); // SAFETY: We check the in_fds lens before. - let cmsg_capacity = unsafe { CMSG_SPACE((std::mem::size_of_val(in_fds)) as u32) }; + let cmsg_capacity = unsafe { CMSG_SPACE(u32::try_from(std::mem::size_of_val(in_fds))?) }; let mut cmsg_buffer = vec![0_u64; cmsg_capacity as usize]; // In `musl` toolchain, msghdr has private member `__pad0` and `__pad1`, it can't be @@ -399,7 +399,7 @@ impl UnixSock { // SAFETY: msg parameters are valid. let total_read = unsafe { recvmsg(sock.as_raw_fd(), &mut msg, MSG_WAITALL) }; - if total_read == -1 { + if total_read < 0 { bail!( "Failed to recv msg, err: {}", std::io::Error::last_os_error() diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index 09f43aebc..fe7632dc4 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -741,7 +741,7 @@ impl NetIoQueue { -1 }; drop(locked_tap); - if size < (NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH) as i32 { + if size < (NET_HDR_LENGTH + ETHERNET_HDR_LENGTH + VLAN_TAG_LENGTH) as isize { queue.vring.push_back(); break; } @@ -769,7 +769,7 @@ impl NetIoQueue { queue .vring - .add_used(&self.mem_space, elem.index, size as u32) + .add_used(&self.mem_space, elem.index, u32::try_from(size)?) .with_context(|| { format!( "Failed to add used ring for net rx, index: {}, len: {}", -- Gitee From 0efb479b68f92f8b4fbce75c089a2eded9cc66c5 Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Aug 2024 10:23:37 +0800 Subject: [PATCH 2131/2187] util: Set type for numeric literal Signed-off-by: Keqian Zhu --- util/src/bitmap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 128a286c0..2147ef771 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -515,7 +515,7 @@ mod tests { assert!(bitmap.clear(64).is_ok()); assert!(bitmap.clear(128).is_ok()); - let mut offset = 0; + let mut offset = 0_usize; offset = bitmap.find_next_zero(offset).unwrap(); assert_eq!(offset, 0); offset = bitmap.find_next_zero(offset + 1).unwrap(); @@ -537,7 +537,7 @@ mod tests { assert!(bitmap.set(64).is_ok()); assert!(bitmap.set(128).is_ok()); - let mut offset = 0; + let mut offset = 0_usize; offset = bitmap.find_next_bit(offset).unwrap(); assert_eq!(offset, 0); offset = bitmap.find_next_bit(offset + 1).unwrap(); -- Gitee From 03e9539a3c58ed0cc6abba8e7ffa0a405b99402e Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Aug 2024 15:52:15 +0800 Subject: [PATCH 2132/2187] util: Fix arithmetic side effetcts warnings Signed-off-by: Keqian Zhu --- devices/src/legacy/rtc.rs | 8 +++++++- machine_manager/src/qmp/qmp_socket.rs | 2 +- util/src/arg_parser.rs | 3 +++ util/src/leak_bucket.rs | 27 +++++++++++++++------------ util/src/num_ops.rs | 22 ++++++++++------------ util/src/pixman.rs | 5 ++++- util/src/seccomp.rs | 8 ++++---- util/src/time.rs | 4 ++-- util/src/unix.rs | 7 +++++-- virtio/src/device/block.rs | 4 ++-- virtio/src/device/rng.rs | 2 +- 11 files changed, 54 insertions(+), 38 deletions(-) diff --git a/devices/src/legacy/rtc.rs b/devices/src/legacy/rtc.rs index 9fd4a0898..4c324bf5e 100644 --- a/devices/src/legacy/rtc.rs +++ b/devices/src/legacy/rtc.rs @@ -321,7 +321,13 @@ impl RTC { + bcd_to_bin(self.cmos_data[RTC_CENTURY_BCD as usize]) * 100; // Check rtc time is valid to prevent tick_offset overflow. - if year < 1970 || !(1..=12).contains(&mon) || !(1..=31).contains(&day) { + if year < 1970 + || !(1..=12).contains(&mon) + || !(1..=31).contains(&day) + || !(0..=24).contains(&hour) + || !(0..=60).contains(&min) + || !(0..=60).contains(&sec) + { warn!( "RTC: the updated rtc time {}-{}-{} may be invalid.", year, mon, day diff --git a/machine_manager/src/qmp/qmp_socket.rs b/machine_manager/src/qmp/qmp_socket.rs index d2ed49928..f0b4ace26 100644 --- a/machine_manager/src/qmp/qmp_socket.rs +++ b/machine_manager/src/qmp/qmp_socket.rs @@ -375,7 +375,7 @@ fn handle_qmp( // If flow over `LEAK_BUCKET_LIMIT` per seconds, discard the request and return // a `OperationThrottled` error. - if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), 1_u64) { + if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), 1_u32) { qmp_service.discard()?; let err_resp = qmp_schema::QmpErrorClass::OperationThrottled(LEAK_BUCKET_LIMIT); qmp_service diff --git a/util/src/arg_parser.rs b/util/src/arg_parser.rs index e92f052ad..1d6ddf910 100644 --- a/util/src/arg_parser.rs +++ b/util/src/arg_parser.rs @@ -608,6 +608,9 @@ impl<'a> ArgMatches<'a> { fn split_arg(args: &[String]) -> (&[String], &[String]) { if let Some(index) = args.iter().position(|arg| arg == ARG_SEPARATOR) { + if index == args.len() - 1 { + return (&args[..index], &[]); + } return (&args[..index], &args[index + 1..]); } (args, &[]) diff --git a/util/src/leak_bucket.rs b/util/src/leak_bucket.rs index 9390e17bf..23a1d9109 100644 --- a/util/src/leak_bucket.rs +++ b/util/src/leak_bucket.rs @@ -15,7 +15,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::Arc; use std::time::{Duration, Instant}; -use anyhow::Result; +use anyhow::{Context, Result}; use log::error; use vmm_sys_util::eventfd::EventFd; @@ -49,7 +49,9 @@ impl LeakBucket { /// * `units_ps` - units per second. pub fn new(units_ps: u64) -> Result { Ok(LeakBucket { - capacity: units_ps * ACCURACY_SCALE, + capacity: units_ps + .checked_mul(ACCURACY_SCALE) + .with_context(|| "capacity overflow")?, level: 0, prev_time: get_current_time(), timer_started: false, @@ -63,7 +65,7 @@ impl LeakBucket { /// # Arguments /// /// * `loop_context` - used for delay function call. - pub fn throttled(&mut self, loop_context: &mut EventLoopContext, need_units: u64) -> bool { + pub fn throttled(&mut self, loop_context: &mut EventLoopContext, need_units: u32) -> bool { // capacity value is zero, indicating that there is no need to limit if self.capacity == 0 { return false; @@ -75,10 +77,13 @@ impl LeakBucket { // update the water level let now = get_current_time(); let nanos = (now - self.prev_time).as_nanos(); - if nanos > u128::from(self.level * NANOSECONDS_PER_SECOND / self.capacity) { + let throttle_timeout = + u128::from(self.level) * u128::from(NANOSECONDS_PER_SECOND) / u128::from(self.capacity); + if nanos > throttle_timeout { self.level = 0; } else { - self.level -= nanos as u64 * self.capacity / NANOSECONDS_PER_SECOND; + self.level -= + (nanos * u128::from(self.capacity) / u128::from(NANOSECONDS_PER_SECOND)) as u64; } self.prev_time = now; @@ -92,19 +97,17 @@ impl LeakBucket { .unwrap_or_else(|e| error!("LeakBucket send event to device failed {:?}", e)); }); - loop_context.timer_add( - func, - Duration::from_nanos( - (self.level - self.capacity) * NANOSECONDS_PER_SECOND / self.capacity, - ), - ); + let timeout = + (self.level - self.capacity).saturating_mul(NANOSECONDS_PER_SECOND) / self.capacity; + loop_context.timer_add(func, Duration::from_nanos(timeout)); self.timer_started = true; return true; } - self.level += need_units * ACCURACY_SCALE; + let scaled_need = u64::from(need_units) * ACCURACY_SCALE; + self.level = self.level.saturating_add(scaled_need); false } diff --git a/util/src/num_ops.rs b/util/src/num_ops.rs index da9a6e994..f5ea59be2 100644 --- a/util/src/num_ops.rs +++ b/util/src/num_ops.rs @@ -35,7 +35,7 @@ use log::error; /// assert!(value == Some(1004)); /// ``` pub fn round_up(origin: u64, align: u64) -> Option { - match origin % align { + match origin.checked_rem(align)? { 0 => Some(origin), diff => origin.checked_add(align - diff), } @@ -58,7 +58,7 @@ pub fn round_up(origin: u64, align: u64) -> Option { /// assert!(value == Some(1000)); /// ``` pub fn round_down(origin: u64, align: u64) -> Option { - match origin % align { + match origin.checked_rem(align)? { 0 => Some(origin), diff => origin.checked_sub(diff), } @@ -81,14 +81,12 @@ pub fn round_down(origin: u64, align: u64) -> Option { /// assert!(value == Some(3)); /// ``` pub fn div_round_up(dividend: u64, divisor: u64) -> Option { - if let Some(res) = dividend.checked_div(divisor) { - if dividend % divisor == 0 { - return Some(res); - } else { - return Some(res + 1); - } + let res = dividend.checked_div(divisor)?; + if dividend.checked_rem(divisor)? == 0 { + Some(res) + } else { + Some(res + 1) } - None } /// Get the first half or second half of u64. @@ -203,7 +201,7 @@ pub fn write_u64_high(origin: u64, value: u32) -> u64 { /// assert!(value == 0xfa); /// ``` pub fn extract_u32(value: u32, start: u32, length: u32) -> Option { - if length > 32 - start { + if length > 32_u32.checked_sub(start)? { error!( "extract_u32: ( start {} length {} ) is out of range", start, length @@ -235,7 +233,7 @@ pub fn extract_u32(value: u32, start: u32, length: u32) -> Option { /// assert!(value == 0xffff); /// ``` pub fn extract_u64(value: u64, start: u32, length: u32) -> Option { - if length > 64 - start { + if length > 64_u32.checked_sub(start)? { error!( "extract_u64: ( start {} length {} ) is out of range", start, length @@ -271,7 +269,7 @@ pub fn extract_u64(value: u64, start: u32, length: u32) -> Option { /// assert!(value == 0xffba); /// ``` pub fn deposit_u32(value: u32, start: u32, length: u32, fieldval: u32) -> Option { - if length > 32 - start { + if length > 32_u32.checked_sub(start)? { error!( "deposit_u32: ( start {} length {} ) is out of range", start, length diff --git a/util/src/pixman.rs b/util/src/pixman.rs index 5df34e03f..4ec4d0741 100644 --- a/util/src/pixman.rs +++ b/util/src/pixman.rs @@ -221,7 +221,10 @@ pub fn pixman_format_b(val: u32) -> u8 { } pub fn pixman_format_depth(val: u32) -> u8 { - pixman_format_a(val) + pixman_format_r(val) + pixman_format_g(val) + pixman_format_b(val) + pixman_format_a(val) + .wrapping_add(pixman_format_r(val)) + .wrapping_add(pixman_format_g(val)) + .wrapping_add(pixman_format_b(val)) } extern "C" { diff --git a/util/src/seccomp.rs b/util/src/seccomp.rs index 13827e920..cce0facd2 100644 --- a/util/src/seccomp.rs +++ b/util/src/seccomp.rs @@ -213,10 +213,10 @@ impl SeccompData { offset_of!(SeccompData, arch) as u32 } - fn args(num: u32) -> u32 { + fn args(num: u8) -> u32 { let offset_of_u64 = offset_of!(SeccompData, args) - offset_of!(SeccompData, instruction_pointer); - offset_of!(SeccompData, args) as u32 + num * offset_of_u64 as u32 + offset_of!(SeccompData, args) as u32 + u32::from(num) * offset_of_u64 as u32 } } @@ -292,7 +292,7 @@ pub struct BpfRule { /// The first bpf_filter to compare syscall number. header_rule: SockFilter, /// The last args index. - args_idx_last: Option, + args_idx_last: Option, /// The inner rules to limit the arguments of syscall. inner_rules: Vec, /// The last bpf_filter to allow syscall. @@ -321,7 +321,7 @@ impl BpfRule { /// * `args_idx` - The index number of system call's arguments. /// * `args_value` - The value of args_num you want to limit. This value used with `cmp` /// together. - pub fn add_constraint(mut self, cmp: SeccompCmpOpt, args_idx: u32, args_value: u32) -> BpfRule { + pub fn add_constraint(mut self, cmp: SeccompCmpOpt, args_idx: u8, args_value: u32) -> BpfRule { if self.inner_rules.is_empty() { self.tail_rule = bpf_stmt(BPF_LD + BPF_W + BPF_ABS, SeccompData::nr()); } diff --git a/util/src/time.rs b/util/src/time.rs index e8fd8a568..449506615 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -50,8 +50,8 @@ pub fn get_format_time(sec: i64) -> [i32; 6] { } [ - ti.tm_year + 1900, - ti.tm_mon + 1, + ti.tm_year.saturating_add(1900), + ti.tm_mon.saturating_add(1), ti.tm_mday, ti.tm_hour, ti.tm_min, diff --git a/util/src/unix.rs b/util/src/unix.rs index af4de597c..d1d3813b4 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -424,17 +424,20 @@ impl UnixSock { // SAFETY: Input parameter is constant. let fd_count = (cmsg.cmsg_len as u64 - u64::from(unsafe { CMSG_LEN(0) })) as usize / size_of::(); + let new_in_fds_count = in_fds_count + .checked_add(fd_count) + .with_context(|| "fds count overflow")?; // SAFETY: // 1. the pointer of cmsg_ptr was created in this function and can be guaranteed not be null. // 2. the parameter of in_fds has been checked before. unsafe { copy_nonoverlapping( self.cmsg_data(cmsg_ptr), - in_fds[in_fds_count..(in_fds_count + fd_count)].as_mut_ptr(), + in_fds[in_fds_count..new_in_fds_count].as_mut_ptr(), fd_count, ); } - in_fds_count += fd_count; + in_fds_count = new_in_fds_count; } cmsg_ptr = self.get_next_cmsg(&msg, &cmsg, cmsg_ptr); diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index 3cd02f56a..ddc28fc9a 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -661,7 +661,7 @@ impl BlockIoHandler { // limit io operations if iops is configured if let Some(lb) = self.leak_bucket.as_mut() { if let Some(ctx) = EventLoop::get_ctx(self.iothread.as_ref()) { - if lb.throttled(ctx, 1_u64) { + if lb.throttled(ctx, 1_u32) { queue.vring.push_back(); break; } @@ -781,7 +781,7 @@ impl BlockIoHandler { // See whether we have been throttled. if let Some(lb) = self.leak_bucket.as_mut() { if let Some(ctx) = EventLoop::get_ctx(self.iothread.as_ref()) { - if lb.throttled(ctx, 0) { + if lb.throttled(ctx, 0_u32) { break; } } diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 5cc277b1d..e7db31b26 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -167,7 +167,7 @@ impl RngHandler { get_req_data_size(&elem.in_iovec).with_context(|| "Failed to get request size")?; if let Some(leak_bucket) = self.leak_bucket.as_mut() { - if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), u64::from(size)) { + if leak_bucket.throttled(EventLoop::get_ctx(None).unwrap(), size) { queue_lock.vring.push_back(); break; } -- Gitee From 2f7357424eaaa52ecc22c8706ad833cf7e8b0dcf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Fri, 23 Aug 2024 17:55:27 +0800 Subject: [PATCH 2133/2187] util: Fix possible out of bound array indexing Signed-off-by: Keqian Zhu --- util/src/bitmap.rs | 2 +- util/src/device_tree.rs | 5 ++++- util/src/loop_context.rs | 3 +-- util/src/unix.rs | 3 +++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/util/src/bitmap.rs b/util/src/bitmap.rs index 2147ef771..3d8111c67 100644 --- a/util/src/bitmap.rs +++ b/util/src/bitmap.rs @@ -230,7 +230,7 @@ impl Bitmap { /// /// * `num` - the input number. pub fn contain(&self, num: usize) -> Result { - if num > self.vol() { + if num >= self.vol() { return Err(anyhow!(UtilError::OutOfBound( num as u64, self.size() as u64 * T::len() as u64, diff --git a/util/src/device_tree.rs b/util/src/device_tree.rs index ebb50d0e2..db590271d 100644 --- a/util/src/device_tree.rs +++ b/util/src/device_tree.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use byteorder::{BigEndian, ByteOrder}; use crate::UtilError; @@ -142,6 +142,9 @@ impl FdtBuilder { let off_dt_strings = FDT_HEADER_SIZE + self.mem_reserve.len() + self.structure_blk.len(); let off_mem_rsvmap = FDT_HEADER_SIZE; + if self.fdt_header.len() < FDT_HEADER_SIZE { + bail!("fdt header size too small"); + } BigEndian::write_u32(&mut self.fdt_header[0..4], FDT_MAGIC); BigEndian::write_u32(&mut self.fdt_header[4..8], total_size as u32); BigEndian::write_u32(&mut self.fdt_header[8..12], off_dt_struct as u32); diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 0f00ed073..5bf07652a 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -702,8 +702,7 @@ impl EventLoopContext { let mut notifiers = Vec::new(); let status_locked = event.status.lock().unwrap(); if *status_locked == EventStatus::Alive { - for j in 0..event.handlers.len() { - let handler = &event.handlers[j]; + for handler in event.handlers.iter() { match handler(self.ready_events[i].event_set(), event.raw_fd) { None => {} Some(mut notifier) => { diff --git a/util/src/unix.rs b/util/src/unix.rs index d1d3813b4..54a27462d 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -427,6 +427,9 @@ impl UnixSock { let new_in_fds_count = in_fds_count .checked_add(fd_count) .with_context(|| "fds count overflow")?; + if new_in_fds_count > in_fds.len() { + bail!("in_fds is too small"); + } // SAFETY: // 1. the pointer of cmsg_ptr was created in this function and can be guaranteed not be null. // 2. the parameter of in_fds has been checked before. -- Gitee From 80d4dc06ede51b2074afb61909704d5d1a4bf577 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Mon, 9 Sep 2024 10:01:27 +0800 Subject: [PATCH 2134/2187] vhost: use debug log level To avoid too much log output, let's use debug log level instead of info level. Signed-off-by: Zhao Yi Min --- virtio/src/vhost/user/client.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 4a51c9294..9a06e8e12 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -20,7 +20,7 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use anyhow::{bail, Context, Result}; -use log::{error, info, warn}; +use log::{debug, error, info, warn}; use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use super::super::VhostOps; @@ -243,7 +243,7 @@ impl VhostUserMemInfo { let file_back = match fr.owner.get_file_backend() { Some(file_back_) => file_back_, _ => { - info!("It is not share memory for vhost user device"); + debug!("It is not share memory for vhost user device"); return Ok(()); } }; @@ -283,7 +283,7 @@ impl VhostUserMemInfo { let file_back = match fr.owner.get_file_backend() { None => { - info!("fr {:?} backend is not file, ignored", fr); + debug!("fr {:?} backend is not file, ignored", fr); return Ok(()); } Some(fb) => fb, -- Gitee From 83ea8ce7b8ff7b37463d84a0bed94ea32fb1204f Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Sat, 24 Aug 2024 19:34:37 +0800 Subject: [PATCH 2135/2187] util: Verify unsafe code blocks Signed-off-by: Keqian Zhu --- address_space/src/host_mmap.rs | 28 +++--- block_backend/src/qcow2/mod.rs | 15 ++- block_backend/src/raw.rs | 15 +-- devices/src/camera_backend/demo.rs | 3 +- devices/src/usb/mod.rs | 6 +- util/src/aio/mod.rs | 150 ++++++++++++++++++----------- util/src/aio/raw.rs | 81 ++++++++-------- util/src/file.rs | 4 + util/src/lib.rs | 1 - util/src/loop_context.rs | 14 +-- util/src/offsetof.rs | 2 + util/src/pixman.rs | 14 +-- util/src/syscall.rs | 57 ----------- util/src/unix.rs | 45 ++++++++- virtio/src/device/block.rs | 6 +- virtio/src/device/gpu.rs | 18 ++-- virtio/src/device/net.rs | 5 +- virtio/src/device/rng.rs | 15 +-- virtio/src/device/serial.rs | 3 +- virtio/src/lib.rs | 5 +- 20 files changed, 269 insertions(+), 218 deletions(-) delete mode 100644 util/src/syscall.rs diff --git a/address_space/src/host_mmap.rs b/address_space/src/host_mmap.rs index fa807a80e..d31d4fd34 100644 --- a/address_space/src/host_mmap.rs +++ b/address_space/src/host_mmap.rs @@ -25,10 +25,7 @@ use nix::unistd::{mkstemp, sysconf, unlink, SysconfVar}; use crate::{AddressRange, GuestAddress, Region}; use machine_manager::config::{HostMemPolicy, MachineMemConfig, MemZoneConfig}; -use util::{ - syscall::mbind, - unix::{do_mmap, host_page_size}, -}; +use util::unix::{do_mmap, host_page_size, mbind}; const MAX_PREALLOC_THREAD: i64 = 16; /// Verify existing pages in the mapping. @@ -370,15 +367,20 @@ fn set_host_memory_policy(mem_mappings: &Arc, zone: &MemZoneConf nmask = vec![0_u64; max_node]; } - mbind( - host_addr_start, - zone.size, - policy as u32, - nmask, - max_node as u64, - MPOL_MF_STRICT | MPOL_MF_MOVE, - ) - .with_context(|| "Failed to call mbind")?; + // SAFETY: + // 1. addr is managed by memory mapping, it can be guaranteed legal. + // 2. node_mask was created in this function. + // 3. Upper limit of max_node is MAX_NODES. + unsafe { + mbind( + host_addr_start, + zone.size, + policy as u32, + nmask, + max_node as u64, + MPOL_MF_STRICT | MPOL_MF_MOVE, + )?; + } Ok(()) } diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 308a77784..151b9255e 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1711,7 +1711,8 @@ impl BlockDriverOps for Qcow2Driver { Ok(HostRange::DataNotInit(cnt)) => { let (begin, end) = iovecs_split(left, cnt); left = end; - iovec_write_zero(&begin); + // SAFETY: iovecs is generated by address_space. + unsafe { iovec_write_zero(&begin) }; copied += cnt; } Err(e) => { @@ -2317,8 +2318,10 @@ mod test { let mut wbuf = vec![0; case.sz as usize]; let mut rbuf = vec![0; case.sz as usize]; - let wsz = iov_to_buf_direct(&case.wiovec, 0, &mut wbuf).unwrap(); - let rsz = iov_to_buf_direct(&case.riovec, 0, &mut rbuf).unwrap(); + // SAFETY: wiovec is valid. + let wsz = unsafe { iov_to_buf_direct(&case.wiovec, 0, &mut wbuf).unwrap() }; + // SAFETY: riovec is valid. + let rsz = unsafe { iov_to_buf_direct(&case.riovec, 0, &mut rbuf).unwrap() }; assert_eq!(wsz, case.sz as usize); assert_eq!(rsz, case.sz as usize); assert_eq!(wbuf, rbuf); @@ -2392,8 +2395,10 @@ mod test { let mut wbuf = vec![0; case.sz as usize]; let mut rbuf = vec![0; case.sz as usize]; - let wsz = iov_to_buf_direct(&case.wiovec, 0, &mut wbuf).unwrap(); - let rsz = iov_to_buf_direct(&case.riovec, 0, &mut rbuf).unwrap(); + // SAFETY: wiovec is valid. + let wsz = unsafe { iov_to_buf_direct(&case.wiovec, 0, &mut wbuf).unwrap() }; + // SAFETY: riovec is valid. + let rsz = unsafe { iov_to_buf_direct(&case.riovec, 0, &mut rbuf).unwrap() }; assert_eq!(wsz, case.sz as usize); assert_eq!(rsz, case.sz as usize); assert_eq!(wbuf, rbuf); diff --git a/block_backend/src/raw.rs b/block_backend/src/raw.rs index 0edbc75d0..d49578641 100644 --- a/block_backend/src/raw.rs +++ b/block_backend/src/raw.rs @@ -68,12 +68,15 @@ impl RawDriver { bail!("Failed to alloc memory for write."); } - let ret = raw_write( - self.driver.file.as_raw_fd(), - align_buf as u64, - write_size as usize, - 0, - ); + // SAFETY: align_buf is valid and large enough. + let ret = unsafe { + raw_write( + self.driver.file.as_raw_fd(), + align_buf as u64, + write_size as usize, + 0, + ) + }; // SAFETY: the memory is allocated in this function. unsafe { libc::free(align_buf) }; diff --git a/devices/src/camera_backend/demo.rs b/devices/src/camera_backend/demo.rs index 60732d635..725d3de84 100644 --- a/devices/src/camera_backend/demo.rs +++ b/devices/src/camera_backend/demo.rs @@ -467,7 +467,8 @@ impl CameraBackend for DemoCameraBackend { let start = frame_offset + copied; let end = start + cnt; let tmp = &locked_frame.image[start..end]; - mem_from_buf(tmp, iov.iov_base) + // SAFETY: iovecs is generated by address_space and len is not less than tmp's. + unsafe { mem_from_buf(tmp, iov.iov_base) } .with_context(|| format!("Failed to write data to {:x}", iov.iov_base))?; copied += cnt; } diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index 5670f16f4..ff4927f99 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -565,7 +565,8 @@ impl UsbPacket { } let cnt = min(iov.iov_len as usize, len - copied); let tmp = &vec[copied..(copied + cnt)]; - if let Err(e) = mem_from_buf(tmp, iov.iov_base) { + // SAFETY: iovecs is generated by address_space and len is not less than tmp's. + if let Err(e) = unsafe { mem_from_buf(tmp, iov.iov_base) } { error!("Failed to write mem: {:?}", e); } copied += cnt; @@ -580,7 +581,8 @@ impl UsbPacket { } let cnt = min(iov.iov_len as usize, len - copied); let tmp = &mut vec[copied..(copied + cnt)]; - if let Err(e) = mem_to_buf(tmp, iov.iov_base) { + // SAFETY: iovecs is generation by address_space and len is not less than tmp's. + if let Err(e) = unsafe { mem_to_buf(tmp, iov.iov_base) } { error!("Failed to read mem {:?}", e); } copied += cnt; diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index a401879bb..ac10fa16e 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -213,8 +213,10 @@ impl AioCb { pub fn rw_sync(&self) -> i32 { let mut ret = match self.opcode { - OpCode::Preadv => raw_readv(self.file_fd, &self.iovec, self.offset), - OpCode::Pwritev => raw_writev(self.file_fd, &self.iovec, self.offset), + // SAFETY: iovec of aiocb is valid. + OpCode::Preadv => unsafe { raw_readv(self.file_fd, &self.iovec, self.offset) }, + // SAFETY: iovec of aiocb is valid. + OpCode::Pwritev => unsafe { raw_writev(self.file_fd, &self.iovec, self.offset) }, _ => -1, }; if ret < 0 { @@ -266,9 +268,10 @@ impl AioCb { // If the buffer is full with zero and the operation is Pwritev, // It's equal to write zero operation. fn try_convert_to_write_zero(&mut self) { - if self.opcode == OpCode::Pwritev - && self.write_zeroes != WriteZeroesState::Off - && iovec_is_zero(&self.iovec) + if self.opcode == OpCode::Pwritev && + self.write_zeroes != WriteZeroesState::Off && + // SAFETY: iovec is generated by address_space. + unsafe { iovec_is_zero(&self.iovec) } { self.opcode = OpCode::WriteZeroes; if self.write_zeroes == WriteZeroesState::Unmap && self.discard { @@ -341,12 +344,15 @@ impl AioCb { loop { // Step1: Read file to bounce buffer. let nbytes = cmp::min(high_align - offset, buffer_len); - let len = raw_read( - self.file_fd, - bounce_buffer as u64, - nbytes as usize, - offset as usize, - ); + // SAFETY: bounce_buffer is valid and large enough. + let len = unsafe { + raw_read( + self.file_fd, + bounce_buffer as u64, + nbytes as usize, + offset as usize, + ) + }; if len < 0 { bail!("Failed to do raw read for misaligned read."); } @@ -370,7 +376,8 @@ impl AioCb { }; // Step2: Copy bounce buffer to iovec. - iov_from_buf_direct(iovecs, src).and_then(|v| { + // SAFETY: iovecs is generated by address_space. + unsafe { iov_from_buf_direct(iovecs, src) }.and_then(|v| { if v == real_nbytes as usize { Ok(()) } else { @@ -392,12 +399,15 @@ impl AioCb { // Load the head from file before fill iovec to buffer. let mut head_loaded = false; if self.offset as u64 > offset_align { - let len = raw_read( - self.file_fd, - bounce_buffer as u64, - self.req_align as usize, - offset_align as usize, - ); + // SAFETY: bounce_buffer is valid and large enough. + let len = unsafe { + raw_read( + self.file_fd, + bounce_buffer as u64, + self.req_align as usize, + offset_align as usize, + ) + }; if len < 0 || len as u32 != self.req_align { bail!("Failed to load head for misaligned write."); } @@ -418,12 +428,15 @@ impl AioCb { let real_nbytes = real_high - real_offset; if real_high == high && need_tail { - let len = raw_read( - self.file_fd, - bounce_buffer as u64 + nbytes - u64::from(self.req_align), - self.req_align as usize, - (offset + nbytes) as usize - self.req_align as usize, - ); + // SAFETY: bounce_buffer is valid and large enough. + let len = unsafe { + raw_read( + self.file_fd, + bounce_buffer as u64 + nbytes - u64::from(self.req_align), + self.req_align as usize, + (offset + nbytes) as usize - self.req_align as usize, + ) + }; if len < 0 || len as u32 != self.req_align { bail!("Failed to load tail for misaligned write."); } @@ -436,7 +449,8 @@ impl AioCb { real_nbytes as usize, ) }; - iov_to_buf_direct(iovecs, 0, dst).and_then(|v| { + // SAFETY: iovecs is generated by address_space. + unsafe { iov_to_buf_direct(iovecs, 0, dst) }.and_then(|v| { if v == real_nbytes as usize { Ok(()) } else { @@ -445,12 +459,15 @@ impl AioCb { })?; // Step2: Write bounce buffer to file. - let len = raw_write( - self.file_fd, - bounce_buffer as u64, - nbytes as usize, - offset as usize, - ); + // SAFETY: bounce_buffer is valid and large enough. + let len = unsafe { + raw_write( + self.file_fd, + bounce_buffer as u64, + nbytes as usize, + offset as usize, + ) + }; if len < 0 || len as u64 != nbytes { bail!("Failed to do raw write for misaligned write."); } @@ -713,22 +730,28 @@ impl Aio { } } -pub fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { - // SAFETY: all callers have valid hva address. - let mut slice = unsafe { std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()) }; - (&mut slice) - .write(buf) +/// # Safety +/// +/// Caller should has valid hva address. +pub unsafe fn mem_from_buf(buf: &[u8], hva: u64) -> Result<()> { + let mut slice = std::slice::from_raw_parts_mut(hva as *mut u8, buf.len()); + slice + .write_all(buf) .with_context(|| format!("Failed to write buf to hva:{})", hva))?; Ok(()) } /// Write buf to iovec and return the written number of bytes. -pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { +/// # Safety +/// +/// Caller should has valid iovec. +pub unsafe fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { let mut start: usize = 0; let mut end: usize = 0; for iov in iovec.iter() { end = cmp::min(start + iov.iov_len as usize, buf.len()); + // iov len is not less than buf's. mem_from_buf(&buf[start..end], iov.iov_base)?; if end >= buf.len() { break; @@ -738,16 +761,21 @@ pub fn iov_from_buf_direct(iovec: &[Iovec], buf: &[u8]) -> Result { Ok(end) } -pub fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { - // SAFETY: all callers have valid hva address. - let slice = unsafe { std::slice::from_raw_parts(hva as *const u8, buf.len()) }; - buf.write(slice) +/// # Safety +/// +/// Caller should has valid hva address. +pub unsafe fn mem_to_buf(mut buf: &mut [u8], hva: u64) -> Result<()> { + let slice = std::slice::from_raw_parts(hva as *const u8, buf.len()); + buf.write_all(slice) .with_context(|| format!("Failed to read buf from hva:{})", hva))?; Ok(()) } /// Read iovec to buf and return the read number of bytes. -pub fn iov_to_buf_direct(iovec: &[Iovec], offset: u64, buf: &mut [u8]) -> Result { +/// # Safety +/// +/// Caller should has valid iovec. +pub unsafe fn iov_to_buf_direct(iovec: &[Iovec], offset: u64, buf: &mut [u8]) -> Result { let mut iovec2: Option<&[Iovec]> = None; let mut start: usize = 0; let mut end: usize = 0; @@ -759,6 +787,7 @@ pub fn iov_to_buf_direct(iovec: &[Iovec], offset: u64, buf: &mut [u8]) -> Result for (index, iov) in iovec.iter().enumerate() { if iov.iov_len > offset { end = cmp::min((iov.iov_len - offset) as usize, buf.len()); + // iov len is not less than buf's. mem_to_buf(&mut buf[..end], iov.iov_base + offset)?; if end >= buf.len() || index >= (iovec.len() - 1) { return Ok(end); @@ -776,6 +805,7 @@ pub fn iov_to_buf_direct(iovec: &[Iovec], offset: u64, buf: &mut [u8]) -> Result for iov in iovec2.unwrap() { end = cmp::min(start + iov.iov_len as usize, buf.len()); + // iov len is not less than buf's. mem_to_buf(&mut buf[start..end], iov.iov_base)?; if end >= buf.len() { break; @@ -798,16 +828,16 @@ pub fn iov_discard_front_direct(iovec: &mut [Iovec], mut size: u64) -> Option<&m None } -fn iovec_is_zero(iovecs: &[Iovec]) -> bool { +// Caller should have valid hva iovec. +unsafe fn iovec_is_zero(iovecs: &[Iovec]) -> bool { let size = std::mem::size_of::() as u64; for iov in iovecs { if iov.iov_len % size != 0 { return false; } // SAFETY: iov_base and iov_len has been checked in pop_avail(). - let slice = unsafe { - std::slice::from_raw_parts(iov.iov_base as *const u64, (iov.iov_len / size) as usize) - }; + let slice = + std::slice::from_raw_parts(iov.iov_base as *const u64, (iov.iov_len / size) as usize); for val in slice.iter() { if *val != 0 { return false; @@ -838,12 +868,12 @@ pub fn iovecs_split(iovecs: Vec, mut size: u64) -> (Vec, Vec i64 { +/// # Safety +/// +/// Caller should has valid buf. +pub unsafe fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { let mut ret; loop { - // SAFETY: fd and buf is valid. - ret = unsafe { - pread( - fd as c_int, - buf as *mut c_void, - size as size_t, - offset as off_t, - ) as i64 - }; + ret = pread( + fd as c_int, + buf as *mut c_void, + size as size_t, + offset as off_t, + ) as i64; if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) { break; @@ -48,18 +48,18 @@ pub fn raw_read(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { ret } -pub fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { +/// # Safety +/// +/// Caller should has valid iovec. +pub unsafe fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { let mut ret; loop { - // SAFETY: fd and buf is valid. - ret = unsafe { - preadv( - fd as c_int, - iovec.as_ptr() as *const iovec, - iovec.len() as c_int, - offset as off_t, - ) as i64 - }; + ret = preadv( + fd as c_int, + iovec.as_ptr() as *const iovec, + iovec.len() as c_int, + offset as off_t, + ) as i64; if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) { break; @@ -76,18 +76,18 @@ pub fn raw_readv(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { ret } -pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { +/// # Safety +/// +/// Caller should has valid buf. +pub unsafe fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { let mut ret; loop { - // SAFETY: fd and buf is valid. - ret = unsafe { - pwrite( - fd as c_int, - buf as *mut c_void, - size as size_t, - offset as off_t, - ) as i64 - }; + ret = pwrite( + fd as c_int, + buf as *mut c_void, + size as size_t, + offset as off_t, + ) as i64; if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) { break; @@ -106,18 +106,19 @@ pub fn raw_write(fd: RawFd, buf: u64, size: usize, offset: usize) -> i64 { ret } -pub fn raw_writev(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { +/// # Safety +/// +/// Caller should has valid iovec. +pub unsafe fn raw_writev(fd: RawFd, iovec: &[Iovec], offset: usize) -> i64 { let mut ret; loop { - // SAFETY: fd and buf is valid. - ret = unsafe { - pwritev( - fd as c_int, - iovec.as_ptr() as *const iovec, - iovec.len() as c_int, - offset as off_t, - ) as i64 - }; + // Caller should has valid iovec. + ret = pwritev( + fd as c_int, + iovec.as_ptr() as *const iovec, + iovec.len() as c_int, + offset as off_t, + ) as i64; if !(ret < 0 && (nix::errno::errno() == libc::EINTR || nix::errno::errno() == libc::EAGAIN)) { break; diff --git a/util/src/file.rs b/util/src/file.rs index bacce3db8..c1efa02b2 100644 --- a/util/src/file.rs +++ b/util/src/file.rs @@ -76,6 +76,10 @@ pub fn get_file_alignment(file: &File, direct: bool) -> (u32, u32) { (MAX_FILE_ALIGN * 2) as libc::size_t, ) }; + if aligned_buffer.is_null() { + log::warn!("OOM occurs when get file alignment, assume max alignment"); + return (MAX_FILE_ALIGN, MAX_FILE_ALIGN); + } // Guess alignment requirement of request. let mut align = MIN_FILE_ALIGN; diff --git a/util/src/lib.rs b/util/src/lib.rs index a33cab3c8..60fa43d2f 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -34,7 +34,6 @@ pub mod ohos_binding; pub mod pixman; pub mod seccomp; pub mod socket; -pub mod syscall; pub mod tap; pub mod test_helper; pub mod thread_pool; diff --git a/util/src/loop_context.rs b/util/src/loop_context.rs index 5bf07652a..8e9569828 100644 --- a/util/src/loop_context.rs +++ b/util/src/loop_context.rs @@ -726,17 +726,11 @@ impl EventLoopContext { pub fn read_fd(fd: RawFd) -> u64 { let mut value: u64 = 0; - // SAFETY: this is called by notifier handler and notifier handler - // is executed with fd is is valid. The value is defined above thus - // valid too. - let ret = unsafe { - read( - fd, - &mut value as *mut u64 as *mut c_void, - std::mem::size_of::(), - ) - }; + let buf = &mut value as *mut u64 as *mut c_void; + let count = std::mem::size_of::(); + // SAFETY: The buf refers to local value and count equals to value size. + let ret = unsafe { read(fd, buf, count) }; if ret == -1 { warn!("Failed to read fd"); } diff --git a/util/src/offsetof.rs b/util/src/offsetof.rs index 2cc01699b..055b24e26 100644 --- a/util/src/offsetof.rs +++ b/util/src/offsetof.rs @@ -10,6 +10,8 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +// Note: This can be replaced with std::mem::offset_of! within higher rust version. + /// Macro: Calculate offset of specified field in a type. #[macro_export] macro_rules! __offset_of { diff --git a/util/src/pixman.rs b/util/src/pixman.rs index 4ec4d0741..2cd2e27bd 100644 --- a/util/src/pixman.rs +++ b/util/src/pixman.rs @@ -184,16 +184,18 @@ pub enum pixman_op_t { PIXMAN_OP_HSL_LUMINOSITY = 62, } -pub type pixman_image_destroy_func_t = ::std::option::Option< - unsafe extern "C" fn(image: *mut pixman_image_t, data: *mut libc::c_void), ->; +pub type pixman_image_destroy_func_t = + Option; -pub extern "C" fn virtio_gpu_unref_resource_callback( +/// # Safety +/// +/// Caller should has valid image and data. +pub unsafe extern "C" fn virtio_gpu_unref_resource_callback( _image: *mut pixman_image_t, data: *mut libc::c_void, ) { - // SAFETY: The safety of this function is guaranteed by caller. - unsafe { pixman_image_unref(data.cast()) }; + // The safety of this function is guaranteed by caller. + pixman_image_unref(data.cast()); } fn pixman_format_reshift(val: u32, ofs: u32, num: u32) -> u32 { diff --git a/util/src/syscall.rs b/util/src/syscall.rs deleted file mode 100644 index f6088a72f..000000000 --- a/util/src/syscall.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. -// -// StratoVirt is licensed under Mulan PSL v2. -// You can use this software according to the terms and conditions of the Mulan -// PSL v2. -// You may obtain a copy of Mulan PSL v2 at: -// http://license.coscl.org.cn/MulanPSL2 -// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY -// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -// See the Mulan PSL v2 for more details. - -use anyhow::{bail, Result}; -use libc::{c_void, syscall, SYS_mbind}; - -/// This function set memory policy for host NUMA node memory range. -/// -/// * Arguments -/// -/// * `addr` - The memory range starting with addr. -/// * `len` - Length of the memory range. -/// * `mode` - Memory policy mode. -/// * `node_mask` - node_mask specifies physical node ID. -/// * `max_node` - The max node. -/// * `flags` - Mode flags. -pub fn mbind( - addr: u64, - len: u64, - mode: u32, - node_mask: Vec, - max_node: u64, - flags: u32, -) -> Result<()> { - // SAFETY: - // 1. addr is managed by memory mapping, it can be guaranteed legal. - // 2. node_mask was created in function of set_host_memory_policy. - // 3. Upper limit of max_node is MAX_NODES. - let res = unsafe { - syscall( - SYS_mbind, - addr as *mut c_void, - len, - mode, - node_mask.as_ptr(), - max_node + 1, - flags, - ) - }; - if res < 0 { - bail!( - "Failed to apply host numa node policy, error is {}", - std::io::Error::last_os_error() - ); - } - - Ok(()) -} diff --git a/util/src/unix.rs b/util/src/unix.rs index 54a27462d..2fcebce41 100644 --- a/util/src/unix.rs +++ b/util/src/unix.rs @@ -19,8 +19,8 @@ use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; use anyhow::{anyhow, bail, Context, Result}; use libc::{ - c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, CMSG_LEN, CMSG_SPACE, MSG_NOSIGNAL, - MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, + c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, syscall, SYS_mbind, CMSG_LEN, CMSG_SPACE, + MSG_NOSIGNAL, MSG_WAITALL, SCM_RIGHTS, SOL_SOCKET, }; use log::error; use nix::unistd::{sysconf, SysconfVar}; @@ -142,6 +142,47 @@ unsafe fn set_memory_undumpable(host_addr: *mut libc::c_void, size: u64) { } } +/// This function set memory policy for host NUMA node memory range. +/// +/// * Arguments +/// +/// * `addr` - The memory range starting with addr. +/// * `len` - Length of the memory range. +/// * `mode` - Memory policy mode. +/// * `node_mask` - node_mask specifies physical node ID. +/// * `max_node` - The max node. +/// * `flags` - Mode flags. +/// +/// # Safety +/// +/// Caller should has valid params. +pub unsafe fn mbind( + addr: u64, + len: u64, + mode: u32, + node_mask: Vec, + max_node: u64, + flags: u32, +) -> Result<()> { + let res = syscall( + SYS_mbind, + addr as *mut c_void, + len, + mode, + node_mask.as_ptr(), + max_node + 1, + flags, + ); + if res < 0 { + bail!( + "Failed to apply host numa node policy, error is {}", + std::io::Error::last_os_error() + ); + } + + Ok(()) +} + /// Unix socket is a data communication endpoint for exchanging data /// between processes executing on the same host OS. pub struct UnixSock { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index ddc28fc9a..e6efd9dee 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -422,7 +422,8 @@ impl Request { VIRTIO_BLK_T_GET_ID => { let serial = serial_num.clone().unwrap_or_else(|| String::from("")); let serial_vec = get_serial_num_config(&serial); - let status = iov_from_buf_direct(&self.iovec, &serial_vec).map_or_else( + // SAFETY: iovec is generated by address_space. + let status = unsafe { iov_from_buf_direct(&self.iovec, &serial_vec) }.map_or_else( |e| { error!( "Failed to process block {} request for getting id, {:?}", @@ -475,7 +476,8 @@ impl Request { // Get and check the discard segment. let mut segment = DiscardWriteZeroesSeg::default(); - iov_to_buf_direct(&self.iovec, 0, segment.as_mut_bytes()).and_then(|v| { + // SAFETY: iovec is generated by address_space. + unsafe { iov_to_buf_direct(&self.iovec, 0, segment.as_mut_bytes()) }.and_then(|v| { if v as u64 == size { Ok(()) } else { diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 1f8fa7a3a..717a05e57 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -714,7 +714,8 @@ impl GpuIoHandler { } fn get_request(&mut self, header: &VirtioGpuRequest, req: &mut T) -> Result<()> { - iov_to_buf_direct(&header.out_iovec, 0, req.as_mut_bytes()).and_then(|size| { + // SAFETY: out_iovec is generated by address_space. + unsafe { iov_to_buf_direct(&header.out_iovec, 0, req.as_mut_bytes()) }.and_then(|size| { if size == size_of::() { Ok(()) } else { @@ -760,7 +761,8 @@ impl GpuIoHandler { header.ctx_id = req.header.ctx_id; } - let len = iov_from_buf_direct(&req.in_iovec, resp.as_bytes())?; + // SAFETY: in_iovec is generated by address_space. + let len = unsafe { iov_from_buf_direct(&req.in_iovec, resp.as_bytes())? }; if len != size_of::() { error!( "GuestError: An incomplete response will be used instead of the expected: expected \ @@ -1364,7 +1366,8 @@ impl GpuIoHandler { let data = get_image_data(res.pixman_image).cast() as *mut u8; if res.format == VIRTIO_GPU_FORMAT_MONOCHROME { - let v = iov_to_buf_direct(&res.iov, 0, &mut res.monochrome_cursor)?; + // SAFETY: iov is generated by address_space. + let v = unsafe { iov_to_buf_direct(&res.iov, 0, &mut res.monochrome_cursor)? }; if v != res.monochrome_cursor.len() { error!("No enough data is copied for transfer_to_host_2d with monochrome"); } @@ -1377,7 +1380,8 @@ impl GpuIoHandler { let trans_size = (trans_info.rect.height * stride) as usize; // SAFETY: offset_dst and trans_size do not exceeds data size. let dst = unsafe { from_raw_parts_mut(data.add(offset_dst), trans_size) }; - iov_to_buf_direct(&res.iov, trans_info.offset, dst).map(|v| { + // SAFETY: iov is generated by address_space. + unsafe { iov_to_buf_direct(&res.iov, trans_info.offset, dst) }.map(|v| { if v < trans_size { warn!("No enough data is copied for transfer_to_host_2d"); } @@ -1394,7 +1398,8 @@ impl GpuIoHandler { for _ in 0..trans_info.rect.height { // SAFETY: offset_dst and line_size do not exceeds data size. let dst = unsafe { from_raw_parts_mut(data.add(offset_dst), line_size) }; - iov_to_buf_direct(&res.iov, offset_src as u64, dst).map(|v| { + // SAFETY: iov is generated by address_space. + unsafe { iov_to_buf_direct(&res.iov, offset_src as u64, dst) }.map(|v| { if v < line_size { warn!("No enough data is copied for transfer_to_host_2d"); } @@ -1469,7 +1474,8 @@ impl GpuIoHandler { let ents_buf = // SAFETY: ents is guaranteed not be null and the range of ents_size has been limited. unsafe { from_raw_parts_mut(ents.as_mut_ptr() as *mut u8, ents_size as usize) }; - let v = iov_to_buf_direct(&req.out_iovec, head_size, ents_buf)?; + // SAFETY: out_iovec is generated by address_space. + let v = unsafe { iov_to_buf_direct(&req.out_iovec, head_size, ents_buf)? }; if v as u64 != ents_size { error!( "Virtio-GPU: Load no enough ents buf when attach backing, {} vs {}", diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index fe7632dc4..e2833d931 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -947,7 +947,10 @@ fn get_net_header(iovec: &[Iovec], buf: &mut [u8]) -> Result { .checked_add(elem.iov_len as usize) .with_context(|| "Overflow when getting the net header")?; end = cmp::min(end, buf.len()); - mem_to_buf(&mut buf[start..end], elem.iov_base)?; + // SAFETY: iovec is generated by address_space and len is not less than buf's. + unsafe { + mem_to_buf(&mut buf[start..end], elem.iov_base)?; + } if end >= buf.len() { break; } diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index e7db31b26..2040e8561 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -174,12 +174,15 @@ impl RngHandler { } let mut buffer = vec![0_u8; size as usize]; - let ret = raw_read( - self.random_file.as_raw_fd(), - buffer.as_mut_ptr() as u64, - size as usize, - 0, - ); + // SAFETY: buffer is valid and large enough. + let ret = unsafe { + raw_read( + self.random_file.as_raw_fd(), + buffer.as_mut_ptr() as u64, + size as usize, + 0, + ) + }; if ret < 0 { bail!("Failed to read random file, size: {}", size); } diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index ac093df84..4047bcb38 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -908,7 +908,8 @@ impl SerialControlHandler { msg_data.extend(extra); } - iov_from_buf_direct(&ctrl_vec, &msg_data).and_then(|size| { + // SAFETY: ctrl_vec is generated by address_space. + unsafe { iov_from_buf_direct(&ctrl_vec, &msg_data) }.and_then(|size| { if size != len { bail!( "Expected send msg length is {}, actual send length {}.", diff --git a/virtio/src/lib.rs b/virtio/src/lib.rs index 6d4e8805c..0c8e7673b 100644 --- a/virtio/src/lib.rs +++ b/virtio/src/lib.rs @@ -826,7 +826,10 @@ pub fn iov_to_buf( mem_space.get_address_map(cache, iov.addr, u64::from(iov.len), &mut addr_map)?; for addr in addr_map.into_iter() { end = cmp::min(start + addr.iov_len as usize, buf.len()); - mem_to_buf(&mut buf[start..end], addr.iov_base)?; + // SAFETY: addr_map is generated by address_space and len is not less than buf's. + unsafe { + mem_to_buf(&mut buf[start..end], addr.iov_base)?; + } if end >= buf.len() { return Ok(end); } -- Gitee From 8b62af1699cf120da73a30750ec201e7e6dcd919 Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 9 Sep 2024 15:24:43 +0800 Subject: [PATCH 2136/2187] scream: fix mst usecase fix mst usecase introduced by front-end pause bit Signed-off-by: zhanghan64 --- devices/src/misc/scream/mod.rs | 6 +++--- tests/mod_test/src/libdriver/ivshmem.rs | 27 +++++++++++++++---------- tests/mod_test/tests/scream_test.rs | 11 +++++++++- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index a6c8ca8bf..c3d25a1b1 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -58,10 +58,10 @@ const IVSHMEM_VOLUME_SYNC_VECTOR: u16 = 0; const IVSHMEM_STATUS_CHANGE_VECTOR: u16 = 1; const IVSHMEM_VECTORS_NR: u32 = 2; const IVSHMEM_BAR0_VOLUME: u64 = 240; -const IVSHMEM_BAR0_STATUS: u64 = 244; +pub const IVSHMEM_BAR0_STATUS: u64 = 244; -const STATUS_PLAY_BIT: u32 = 0x1; -const STATUS_START_BIT: u32 = 0x2; +pub const STATUS_PLAY_BIT: u32 = 0x1; +pub const STATUS_START_BIT: u32 = 0x2; const STATUS_MIC_AVAIL_BIT: u32 = 0x4; // A frame of back-end audio data is 50ms, and the next frame of audio data needs diff --git a/tests/mod_test/src/libdriver/ivshmem.rs b/tests/mod_test/src/libdriver/ivshmem.rs index 03d766295..aa456e0b9 100644 --- a/tests/mod_test/src/libdriver/ivshmem.rs +++ b/tests/mod_test/src/libdriver/ivshmem.rs @@ -19,16 +19,16 @@ use super::{ pub struct TestIvshmemDev { pub pci_dev: TestPciDev, - pub bar_addr: PCIBarAddr, - bar_idx: u8, + bar0_addr: PCIBarAddr, + pub bar2_addr: PCIBarAddr, } impl TestIvshmemDev { pub fn new(pci_bus: Rc>) -> Self { Self { pci_dev: TestPciDev::new(pci_bus), - bar_addr: 0, - bar_idx: 2, + bar0_addr: 0, + bar2_addr: 0, } } @@ -37,30 +37,35 @@ impl TestIvshmemDev { assert!(self.pci_dev.find_pci_device(devfn)); self.pci_dev.enable(); - self.bar_addr = self.pci_dev.io_map(self.bar_idx); + self.bar0_addr = self.pci_dev.io_map(0); + self.bar2_addr = self.pci_dev.io_map(2); } pub fn writeb(&mut self, offset: u64, value: u8) { - self.pci_dev.io_writeb(self.bar_addr, offset, value); + self.pci_dev.io_writeb(self.bar2_addr, offset, value); } pub fn writew(&mut self, offset: u64, value: u16) { - self.pci_dev.io_writew(self.bar_addr, offset, value); + self.pci_dev.io_writew(self.bar2_addr, offset, value); } pub fn writel(&mut self, offset: u64, value: u32) { - self.pci_dev.io_writel(self.bar_addr, offset, value); + self.pci_dev.io_writel(self.bar2_addr, offset, value); } pub fn writeq(&mut self, offset: u64, value: u64) { - self.pci_dev.io_writeq(self.bar_addr, offset, value); + self.pci_dev.io_writeq(self.bar2_addr, offset, value); } pub fn readw(&self, offset: u64) -> u16 { - self.pci_dev.io_readw(self.bar_addr, offset) + self.pci_dev.io_readw(self.bar2_addr, offset) } pub fn readl(&self, offset: u64) -> u32 { - self.pci_dev.io_readl(self.bar_addr, offset) + self.pci_dev.io_readl(self.bar2_addr, offset) + } + + pub fn writel_reg(&self, offset: u64, value: u32) { + self.pci_dev.io_writel(self.bar0_addr, offset, value); } } diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index ad2c9e68d..59cd2838f 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -22,7 +22,10 @@ use std::{ use core::time; -use devices::misc::scream::{ShmemHeader, ShmemStreamFmt, ShmemStreamHeader, SCREAM_MAGIC}; +use devices::misc::scream::{ + ShmemHeader, ShmemStreamFmt, ShmemStreamHeader, IVSHMEM_BAR0_STATUS, SCREAM_MAGIC, + STATUS_PLAY_BIT, STATUS_START_BIT, +}; use mod_test::{ libdriver::{ivshmem::TestIvshmemDev, machine::TestStdMachine}, libtest::{test_init, TestState, MACHINE_TYPE_ARG}, @@ -235,6 +238,9 @@ fn scream_playback_basic_test() { thread::sleep(time::Duration::from_millis(1000)); play_header_init(&mut ivshmem.borrow_mut()); + ivshmem + .borrow_mut + .writel_reg(IVSHMEM_BAR0_STATUS, STATUS_PLAY_BIT | STATUS_START_BIT); thread::sleep(time::Duration::from_millis(POLL_DELAY_MS)); @@ -342,6 +348,9 @@ fn scream_record_basic_test() { ivshmem.borrow_mut().init(pci_slot); record_header_init(&mut ivshmem.borrow_mut()); + ivshmem + .borrow_mut + .writel_reg(IVSHMEM_BAR0_STATUS, STATUS_START_BIT); let mut cnt = 0; let mut chunk_idx = 0; -- Gitee From 2dab521c4795ed2105e61e18a15dcef60327091e Mon Sep 17 00:00:00 2001 From: zhanghan64 Date: Mon, 9 Sep 2024 17:46:58 +0800 Subject: [PATCH 2137/2187] scream: add volume sync mst use case Signed-off-by: zhanghan64 --- devices/src/misc/scream/audio_demo.rs | 46 ++++++++++++++++++++++++- devices/src/misc/scream/mod.rs | 8 +++-- tests/mod_test/src/libdriver/ivshmem.rs | 7 ++++ tests/mod_test/tests/scream_test.rs | 40 ++++++++++++++++++--- 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/devices/src/misc/scream/audio_demo.rs b/devices/src/misc/scream/audio_demo.rs index 3576c43f3..c709c2164 100644 --- a/devices/src/misc/scream/audio_demo.rs +++ b/devices/src/misc/scream/audio_demo.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::sync::{Arc, Mutex, RwLock}; use std::{ fs::{File, OpenOptions}, io::{Read, Write}, @@ -19,7 +20,50 @@ use std::{ use core::time; use log::error; -use super::{AudioInterface, AudioStatus, ScreamDirection, StreamData}; +use super::{AudioExtension, AudioInterface, AudioStatus, ScreamDirection, StreamData}; +use crate::misc::ivshmem::Ivshmem; + +pub const INITIAL_VOLUME_VAL: u32 = 0xaa; +const IVSHMEM_VOLUME_SYNC_VECTOR: u16 = 0; + +pub struct DemoAudioVolume { + shm_dev: Arc>, + vol: RwLock, +} + +// SAFETY: all fields are protected by lock +unsafe impl Send for DemoAudioVolume {} +// SAFETY: all fields are protected by lock +unsafe impl Sync for DemoAudioVolume {} + +impl AudioExtension for DemoAudioVolume { + fn get_host_volume(&self) -> u32 { + *self.vol.read().unwrap() + } + + fn set_host_volume(&self, vol: u32) { + *self.vol.write().unwrap() = vol; + } +} + +impl DemoAudioVolume { + pub fn new(shm_dev: Arc>) -> Arc { + let vol = Arc::new(Self { + shm_dev, + vol: RwLock::new(0), + }); + vol.notify(INITIAL_VOLUME_VAL); + vol + } + + fn notify(&self, vol: u32) { + *self.vol.write().unwrap() = vol; + self.shm_dev + .lock() + .unwrap() + .trigger_msix(IVSHMEM_VOLUME_SYNC_VECTOR); + } +} pub struct AudioDemo { file: File, diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index c3d25a1b1..806f00bf0 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -12,7 +12,7 @@ #[cfg(feature = "scream_alsa")] mod alsa; -mod audio_demo; +pub mod audio_demo; #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] mod ohaudio; #[cfg(feature = "scream_pulseaudio")] @@ -31,7 +31,7 @@ use once_cell::sync::Lazy; #[cfg(feature = "scream_alsa")] use self::alsa::AlsaStreamData; -use self::audio_demo::AudioDemo; +use self::audio_demo::{AudioDemo, DemoAudioVolume}; use super::ivshmem::Ivshmem; use crate::pci::{le_read_u32, le_write_u32}; use crate::{Bus, Device}; @@ -57,7 +57,7 @@ pub const TARGET_LATENCY_MS: u32 = 50; const IVSHMEM_VOLUME_SYNC_VECTOR: u16 = 0; const IVSHMEM_STATUS_CHANGE_VECTOR: u16 = 1; const IVSHMEM_VECTORS_NR: u32 = 2; -const IVSHMEM_BAR0_VOLUME: u64 = 240; +pub const IVSHMEM_BAR0_VOLUME: u64 = 240; pub const IVSHMEM_BAR0_STATUS: u64 = 244; pub const STATUS_PLAY_BIT: u32 = 0x1; @@ -752,6 +752,8 @@ impl Scream { match self.config.interface { #[cfg(all(target_env = "ohos", feature = "scream_ohaudio"))] ScreamInterface::OhAudio => OhAudioVolume::new(_ivshmem), + ScreamInterface::Demo => DemoAudioVolume::new(_ivshmem), + #[allow(unreachable_patterns)] _ => Arc::new(AudioExtensionDummy {}), } } diff --git a/tests/mod_test/src/libdriver/ivshmem.rs b/tests/mod_test/src/libdriver/ivshmem.rs index aa456e0b9..edb5ef620 100644 --- a/tests/mod_test/src/libdriver/ivshmem.rs +++ b/tests/mod_test/src/libdriver/ivshmem.rs @@ -20,6 +20,7 @@ use super::{ pub struct TestIvshmemDev { pub pci_dev: TestPciDev, bar0_addr: PCIBarAddr, + bar1_addr: PCIBarAddr, pub bar2_addr: PCIBarAddr, } @@ -28,6 +29,7 @@ impl TestIvshmemDev { Self { pci_dev: TestPciDev::new(pci_bus), bar0_addr: 0, + bar1_addr: 0, bar2_addr: 0, } } @@ -38,6 +40,7 @@ impl TestIvshmemDev { self.pci_dev.enable(); self.bar0_addr = self.pci_dev.io_map(0); + self.bar1_addr = self.pci_dev.io_map(1); self.bar2_addr = self.pci_dev.io_map(2); } @@ -68,4 +71,8 @@ impl TestIvshmemDev { pub fn writel_reg(&self, offset: u64, value: u32) { self.pci_dev.io_writel(self.bar0_addr, offset, value); } + + pub fn readl_reg(&self, offset: u64) -> u32 { + self.pci_dev.io_readl(self.bar0_addr, offset) + } } diff --git a/tests/mod_test/tests/scream_test.rs b/tests/mod_test/tests/scream_test.rs index 59cd2838f..d7669fe09 100644 --- a/tests/mod_test/tests/scream_test.rs +++ b/tests/mod_test/tests/scream_test.rs @@ -23,8 +23,8 @@ use std::{ use core::time; use devices::misc::scream::{ - ShmemHeader, ShmemStreamFmt, ShmemStreamHeader, IVSHMEM_BAR0_STATUS, SCREAM_MAGIC, - STATUS_PLAY_BIT, STATUS_START_BIT, + audio_demo::INITIAL_VOLUME_VAL, ShmemHeader, ShmemStreamFmt, ShmemStreamHeader, + IVSHMEM_BAR0_STATUS, IVSHMEM_BAR0_VOLUME, SCREAM_MAGIC, STATUS_PLAY_BIT, STATUS_START_BIT, }; use mod_test::{ libdriver::{ivshmem::TestIvshmemDev, machine::TestStdMachine}, @@ -239,7 +239,7 @@ fn scream_playback_basic_test() { play_header_init(&mut ivshmem.borrow_mut()); ivshmem - .borrow_mut + .borrow_mut() .writel_reg(IVSHMEM_BAR0_STATUS, STATUS_PLAY_BIT | STATUS_START_BIT); thread::sleep(time::Duration::from_millis(POLL_DELAY_MS)); @@ -326,6 +326,38 @@ fn scream_playback_basic_test() { scream_tmp_clear(playback_path, record_path); } +/// scream device volume synchronization. +/// TestStep: +/// 1. Init scream device. +/// 2. Check volume's initial value. +/// 3. Set volume and read back to check. +/// 4. Stop VM. +/// Expect: +/// 1/2/3/4: success. +#[test] +fn scream_volume_sync_test() { + let pci_slot = 0x1; + let (playback_path, record_path) = get_audio_file_name(); + audio_data_init(playback_path.clone(), record_path.clone()); + let (ivshmem, test_state) = set_up( + IVSHMEM_DEFAULT_SIZE, + pci_slot, + playback_path.clone(), + record_path.clone(), + ); + ivshmem.borrow_mut().init(pci_slot); + + let init_val = ivshmem.borrow_mut().readl_reg(IVSHMEM_BAR0_VOLUME); + assert_eq!(init_val, INITIAL_VOLUME_VAL); + + ivshmem.borrow_mut().writel_reg(IVSHMEM_BAR0_VOLUME, 0xff); + let second_val = ivshmem.borrow_mut().readl_reg(IVSHMEM_BAR0_VOLUME); + assert_eq!(second_val, 0xff); + + test_state.borrow_mut().stop(); + scream_tmp_clear(playback_path, record_path); +} + /// scream device record audio. /// TestStep: /// 1. Init scream device and start recording. @@ -349,7 +381,7 @@ fn scream_record_basic_test() { record_header_init(&mut ivshmem.borrow_mut()); ivshmem - .borrow_mut + .borrow_mut() .writel_reg(IVSHMEM_BAR0_STATUS, STATUS_START_BIT); let mut cnt = 0; -- Gitee From 324ef7d640f61419af2ab9a75a2f2307cead52ae Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 10 Sep 2024 11:55:44 +0800 Subject: [PATCH 2138/2187] ozonec/tests: Add integration tests using Bats --- ozonec/tests/create.bats | 87 +++++++++++++++++++++++++++++++++++++++ ozonec/tests/helpers.bash | 46 +++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 ozonec/tests/create.bats create mode 100644 ozonec/tests/helpers.bash diff --git a/ozonec/tests/create.bats b/ozonec/tests/create.bats new file mode 100644 index 000000000..02d44c8b9 --- /dev/null +++ b/ozonec/tests/create.bats @@ -0,0 +1,87 @@ +#! /usr/bin/env bats + +load helpers + +setup_file() +{ + setup_bundle +} + +teardown_file() +{ + remove_test_dir +} + +setup() +{ + CONTAINER_ID=$(uuidgen) + ROOT_DIR="$DEFAULT_ROOT_DIR" +} + +teardown() +{ + if [ "$ROOT_DIR" == "$DEFAULT_ROOT_DIR" ]; then + ozonec kill "$CONTAINER_ID" 9 + ozonec delete "$CONTAINER_ID" + else + ozonec --root "$ROOT_DIR" kill "$CONTAINER_ID" 9 + ozonec --root "$ROOT_DIR" delete "$CONTAINER_ID" + fi +} + +@test "ozonec create" { + ozonec create "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "" + [ -d "$ROOT_DIR/$CONTAINER_ID" ] + [ -S "$ROOT_DIR/$CONTAINER_ID/notify.sock" ] + [ -f "$ROOT_DIR/$CONTAINER_ID/state.json" ] +} + +@test "ozonec create with absolute path of rootfs" { + local rootfs_dir="$(pwd)/rootfs" + update_config '.root.path = "'$rootfs_dir'"' + ozonec create "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "" +} + +@test "ozonec create with pidfile" { + ozonec create --pid-file ./pidfile "$CONTAINER_ID" 3>&- + local pid=$(cat ./pidfile) + check_container_status "$CONTAINER_ID" created "" "$pid" +} + +@test "ozonec create with duplicate id" { + ozonec create "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "" + ! ozonec create "$CONTAINER_ID" 3>&- +} + +@test "ozonec create with absolute bundle path" { + local bundle_dir="$(dirname `pwd`)/bundle" + ozonec create --bundle "$bundle_dir" "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "" +} + +@test "ozonec create with relative bundle path" { + local bundle_dir="../bundle" + ozonec create --bundle "$bundle_dir" "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "" +} + +@test "ozonec create with absolute root path" { + ROOT_DIR="$(dirname `pwd`)/root" + ozonec --root "$ROOT_DIR" create "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "$ROOT_DIR" + [ -d "$ROOT_DIR/$CONTAINER_ID" ] + [ -S "$ROOT_DIR/$CONTAINER_ID/notify.sock" ] + [ -f "$ROOT_DIR/$CONTAINER_ID/state.json" ] +} + +@test "ozonec create with relative root path" { + ROOT_DIR="../root" + ozonec --root "$ROOT_DIR" create "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "$ROOT_DIR" + [ -d "$ROOT_DIR/$CONTAINER_ID" ] + [ -S "$ROOT_DIR/$CONTAINER_ID/notify.sock" ] + [ -f "$ROOT_DIR/$CONTAINER_ID/state.json" ] +} \ No newline at end of file diff --git a/ozonec/tests/helpers.bash b/ozonec/tests/helpers.bash new file mode 100644 index 000000000..781fa6539 --- /dev/null +++ b/ozonec/tests/helpers.bash @@ -0,0 +1,46 @@ +#! /bin/bash + +bats_require_minimum_version 1.5.0 + +DEFAULT_ROOT_DIR="/var/run/user/$(echo $UID)/ozonec" + +# Reformat config.json file with jq command. +function update_config() +{ + jq "$@" config.json | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' config.json +} + +function setup_bundle() +{ + # Directory for each container. + TEST_DIR=$(mktemp -d "$BATS_RUN_TMPDIR/ozonec.XXXXXX") + chmod a+x "$TEST_DIR" "$BATS_RUN_TMPDIR" + + local bundle="$BATS_TEST_DIRNAME/bundle.tar.gz" + tar --exclude 'rootfs/dev/*' -C "$TEST_DIR" -xf "$bundle" + cd "$TEST_DIR/bundle" +} + +function remove_test_dir() +{ + rm -rf "$TEST_DIR" +} + +function check_container_status() { + local container_id="$1" + local state="$2" + local root="$3" + + if [ "$root" == "" ]; then + run ozonec state "$container_id" + else + run ozonec --root "$root" state "$container_id" + fi + [[ $status -eq 0 ]] + [[ "$output" == *"\"status\": \"$state\""* ]] + + if [ $# -gt 3 ]; then + local pid="$4" + [[ "$(expr match "$output" '.*"pid": \([0-9]*\).*')" == "$pid" ]] + fi +} \ No newline at end of file -- Gitee From 3781eacb89851bf38a05f8813bf029cc975c5431 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 10 Sep 2024 12:45:30 +0800 Subject: [PATCH 2139/2187] ozonec/tests: Add exec.bats --- ozonec/tests/exec.bats | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 ozonec/tests/exec.bats diff --git a/ozonec/tests/exec.bats b/ozonec/tests/exec.bats new file mode 100644 index 000000000..ffdf6c389 --- /dev/null +++ b/ozonec/tests/exec.bats @@ -0,0 +1,33 @@ +#! /usr/bin/env bats + +load helpers + +setup_file() +{ + setup_bundle + + export ROOT_DIR="$TEST_DIR/root" + export CONTAINER_ID=$(uuidgen) + + ozonec --root "$ROOT_DIR" create "$CONTAINER_ID" 3>&- + check_container_status "$CONTAINER_ID" created "$ROOT_DIR" + ozonec --root "$ROOT_DIR" start "$CONTAINER_ID" + check_container_status "$CONTAINER_ID" running "$ROOT_DIR" +} + +teardown_file() +{ + ozonec --root "$ROOT_DIR" kill "$CONTAINER_ID" 9 + ozonec --root "$ROOT_DIR" delete "$CONTAINER_ID" + remove_test_dir +} + +@test "ozonec exec" { + ozonec --root "$ROOT_DIR" exec "$CONTAINER_ID" -- ls -alh +} + +@test "ozonec exec with pidfile" { + ozonec --root "$ROOT_DIR" exec --pid-file pidfile "$CONTAINER_ID" -- ls -alh + local pid=$(cat pidfile) + [[ "$pid" -gt 0 ]] +} \ No newline at end of file -- Gitee From b32669b30f46d4ea0d061a0ba67d2d89288d5186 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Thu, 5 Sep 2024 11:57:00 +0800 Subject: [PATCH 2140/2187] ohaudio: support query host max&min volume value We used a middle-value for conversion between host and VM's volume. This way may introduce errors on conversions. Now we accquire host's volume value range, so we can do conversions directly. Fewer errors are introduced. --- devices/src/misc/scream/ohaudio.rs | 34 ++++++++++++++++++--- util/src/ohos_binding/audio/mod.rs | 18 +++++++++++ util/src/ohos_binding/hwf_adapter/volume.rs | 6 ++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index d746eecfd..752e4d292 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -30,6 +30,7 @@ use util::ohos_binding::audio::*; const STREAM_DATA_VEC_CAPACITY: usize = 15; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; +const SCREAM_MAX_VOLUME: u32 = 110; trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; @@ -532,16 +533,18 @@ impl AudioInterface for OhAudio { pub struct OhAudioVolume { shm_dev: Arc>, ohos_vol: RwLock, + ohos_vol_max: u32, + ohos_vol_min: u32, } -// SAFETY: all fields are protected by lock +// SAFETY: all unsafe fields are protected by lock unsafe impl Send for OhAudioVolume {} -// SAFETY: all fields are protected by lock +// SAFETY: all unsafe fields are protected by lock unsafe impl Sync for OhAudioVolume {} impl GuestVolumeNotifier for OhAudioVolume { fn notify(&self, vol: u32) { - *self.ohos_vol.write().unwrap() = vol; + *self.ohos_vol.write().unwrap() = self.to_guest_vol(vol); self.shm_dev .lock() .unwrap() @@ -555,7 +558,7 @@ impl AudioExtension for OhAudioVolume { } fn set_host_volume(&self, vol: u32) { - set_ohos_volume(vol); + set_ohos_volume(self.to_host_vol(vol)); } } @@ -563,11 +566,32 @@ impl OhAudioVolume { pub fn new(shm_dev: Arc>) -> Arc { let vol = Arc::new(Self { shm_dev, - ohos_vol: RwLock::new(get_ohos_volume()), + ohos_vol: RwLock::new(0), + ohos_vol_max: get_ohos_volume_max(), + ohos_vol_min: get_ohos_volume_min(), }); + *vol.ohos_vol.write().unwrap() = vol.to_guest_vol(get_ohos_volume()); register_guest_volume_notifier(vol.clone()); vol } + + fn to_guest_vol(&self, h_vol: u32) -> u32 { + if self.ohos_vol_max > self.ohos_vol_min { + return SCREAM_MAX_VOLUME * h_vol / (self.ohos_vol_max - self.ohos_vol_min); + } + 0 + } + + fn to_host_vol(&self, v_vol: u32) -> u32 { + if v_vol == 0 || self.ohos_vol_max <= self.ohos_vol_min { + return 0; + } + let res = (self.ohos_vol_max - self.ohos_vol_min) * v_vol / SCREAM_MAX_VOLUME + 1; + if res > self.ohos_vol_max { + return self.ohos_vol_max; + } + res + } } #[cfg(test)] diff --git a/util/src/ohos_binding/audio/mod.rs b/util/src/ohos_binding/audio/mod.rs index 1a1b600a0..613647198 100755 --- a/util/src/ohos_binding/audio/mod.rs +++ b/util/src/ohos_binding/audio/mod.rs @@ -397,6 +397,16 @@ impl OhVolume { unsafe { (self.capi.get_volume)() as u32 } } + fn get_max_volume(&self) -> u32 { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.get_max_volume)() as u32 } + } + + fn get_min_volume(&self) -> u32 { + // SAFETY: We call related API sequentially for specified ctx. + unsafe { (self.capi.get_min_volume)() as u32 } + } + fn set_ohos_volume(&self, volume: i32) { // SAFETY: We call related API sequentially for specified ctx. unsafe { (self.capi.set_volume)(volume) }; @@ -428,6 +438,14 @@ pub fn register_guest_volume_notifier(notifier: Arc) { .register_guest_notifier(notifier); } +pub fn get_ohos_volume_max() -> u32 { + OH_VOLUME_ADAPTER.read().unwrap().get_max_volume() +} + +pub fn get_ohos_volume_min() -> u32 { + OH_VOLUME_ADAPTER.read().unwrap().get_min_volume() +} + pub fn get_ohos_volume() -> u32 { OH_VOLUME_ADAPTER.read().unwrap().get_ohos_volume() } diff --git a/util/src/ohos_binding/hwf_adapter/volume.rs b/util/src/ohos_binding/hwf_adapter/volume.rs index 3de5e34bd..b730e1433 100644 --- a/util/src/ohos_binding/hwf_adapter/volume.rs +++ b/util/src/ohos_binding/hwf_adapter/volume.rs @@ -21,11 +21,15 @@ use crate::get_libfn; pub type VolumeChangedCallBack = unsafe extern "C" fn(c_int); type OhSysAudioGetVolumeFn = unsafe extern "C" fn() -> c_int; +type OhSysAudioGetMaxVolumeFn = unsafe extern "C" fn() -> c_int; +type OhSysAudioGetMinVolumeFn = unsafe extern "C" fn() -> c_int; type OhSysAudioSetVolumeFn = unsafe extern "C" fn(c_int); type OhSysAudioRegisterVolumeChangeFn = unsafe extern "C" fn(VolumeChangedCallBack) -> c_int; pub struct VolumeFuncTable { pub get_volume: RawSymbol, + pub get_max_volume: RawSymbol, + pub get_min_volume: RawSymbol, pub set_volume: RawSymbol, pub register_volume_change: RawSymbol, } @@ -34,6 +38,8 @@ impl VolumeFuncTable { pub unsafe fn new(library: &Library) -> Result { Ok(Self { get_volume: get_libfn!(library, OhSysAudioGetVolumeFn, OhSysAudioGetVolume), + get_max_volume: get_libfn!(library, OhSysAudioGetMaxVolumeFn, OhSysAudioGetMaxVolume), + get_min_volume: get_libfn!(library, OhSysAudioGetMinVolumeFn, OhSysAudioGetMinVolume), set_volume: get_libfn!(library, OhSysAudioSetVolumeFn, OhSysAudioSetVolume), register_volume_change: get_libfn!( library, -- Gitee From f9fce766ba1b6d0a4a5d5e7c5bc72fcd7c40153a Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Sun, 8 Sep 2024 02:16:36 +0800 Subject: [PATCH 2141/2187] time: use i64 to represent the return value of `gettime` `struct timespace` is defined in `time` as: pub struct timespec { pub tv_sec: time_t, #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] pub tv_nsec: i64, #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] pub tv_nsec: ::c_long, } So, we should not truncate i64 value to u32 in the public function `gettime`. Fix it. Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 15 +++++++++++++-- util/src/logger.rs | 4 ++-- util/src/time.rs | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 151b9255e..30c72b605 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -1013,6 +1013,17 @@ impl Qcow2Driver { // Alloc new snapshot table. let (date_sec, date_nsec) = gettime()?; + // Note: The `Snapshots` chapter in Qcow2 spec states: + // Snapshot table entry: + // Byte 16 - 19: Time at which the snapshot was taken in seconds since the + // Epoch + // Byte 20 - 23: Subsecond part of the time at which the snapshot was taken + // in nanoseconds + // + // 32 bits of seconds can represent a range of approximately 136 years since 1970. + // It's enough for current use. If an incorrect host time is used to inject error, + // there may be an issue of inaccurate creation time in the snapshot description. + // Considering compatibility, this issue of inaccurate time is acceptable. let snap = QcowSnapshot { l1_table_offset: new_l1_table_offset, l1_size: self.header.l1_size, @@ -1020,8 +1031,8 @@ impl Qcow2Driver { name, disk_size: self.virtual_disk_size(), vm_state_size: 0, - date_sec, - date_nsec, + date_sec: date_sec as u32, + date_nsec: date_nsec as u32, vm_clock_nsec, icount: u64::MAX, extra_data_size: size_of::() as u32, diff --git a/util/src/logger.rs b/util/src/logger.rs index 0f8634bbf..de35d83f6 100644 --- a/util/src/logger.rs +++ b/util/src/logger.rs @@ -34,7 +34,7 @@ fn format_now() -> String { println!("{:?}", e); (0, 0) }); - let format_time = get_format_time(i64::from(sec)); + let format_time = get_format_time(sec); format!( "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}", @@ -63,7 +63,7 @@ impl FileRotate { self.current_size += Wrapping(size_inc); let sec = gettime()?.0; - let today = get_format_time(i64::from(sec))[2]; + let today = get_format_time(sec)[2]; if self.current_size < Wrapping(LOG_ROTATE_SIZE_MAX) && self.create_day == today { return Ok(()); } diff --git a/util/src/time.rs b/util/src/time.rs index 449506615..ed9f4f338 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -33,9 +33,9 @@ pub fn mktime64(year: u64, mon: u64, day: u64, hour: u64, min: u64, sec: u64) -> } /// Get wall time. -pub fn gettime() -> Result<(u32, u32)> { +pub fn gettime() -> Result<(i64, i64)> { match clock_gettime(ClockId::CLOCK_REALTIME) { - Ok(ts) => Ok((ts.tv_sec() as u32, ts.tv_nsec() as u32)), + Ok(ts) => Ok((ts.tv_sec(), ts.tv_nsec())), Err(e) => bail!("clock_gettime failed: {:?}", e), } } -- Gitee From 3ef9e0b0cceda91d2090ad45ddf10275e2046327 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Wed, 11 Sep 2024 22:38:59 +0800 Subject: [PATCH 2142/2187] xhci: change type of `dequeue` in `struct XhciStreamContext` to `GuestAddress` Change type of `dequeue` in `struct XhciStreamContext` to `GuestAddress`. Signed-off-by: liuxiangdong --- devices/src/usb/xhci/xhci_controller.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 5ac003139..32705f4b7 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -434,14 +434,10 @@ impl XhciEpContext { let locked_stream = stream.lock().unwrap(); let output_addr = locked_stream.dequeue; let ring = locked_stream.ring.as_ref(); - dma_read_u32( - &self.mem, - GuestAddress(output_addr), - stream_ctx.as_mut_dwords(), - )?; + dma_read_u32(&self.mem, output_addr, stream_ctx.as_mut_dwords())?; ring.update_dequeue_to_ctx(stream_ctx.as_mut_dwords()); ring.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); - dma_write_u32(&self.mem, GuestAddress(output_addr), stream_ctx.as_dwords())?; + dma_write_u32(&self.mem, output_addr, stream_ctx.as_dwords())?; } dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; @@ -866,7 +862,7 @@ pub struct XhciStreamContext { /// Memory address space. mem: Arc, /// Dequeue pointer. - dequeue: u64, + dequeue: GuestAddress, /// Transfer Ring (no Secondary Streams for now). ring: Arc, /// Whether the context is up to date after reset. @@ -877,14 +873,14 @@ impl XhciStreamContext { fn new(mem: &Arc) -> Self { Self { mem: Arc::clone(mem), - dequeue: 0, + dequeue: GuestAddress(0), ring: Arc::new(XhciTransferRing::new(mem)), needs_refresh: true, } } fn init(&mut self, addr: u64) -> Result<()> { - self.dequeue = addr; + self.dequeue = GuestAddress(addr); self.refresh()?; Ok(()) } @@ -899,11 +895,7 @@ impl XhciStreamContext { fn refresh(&mut self) -> Result<()> { let mut stream_ctx = XhciStreamCtx::default(); - dma_read_u32( - &self.mem, - GuestAddress(self.dequeue), - stream_ctx.as_mut_dwords(), - )?; + dma_read_u32(&self.mem, self.dequeue, stream_ctx.as_mut_dwords())?; let dequeue = addr64_from_u32(stream_ctx.deq_lo & !0xf, stream_ctx.deq_hi); self.ring.init(dequeue); self.needs_refresh = false; -- Gitee From f78d5556397bb8901be5d1bdf9e2fa12da246b89 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 12 Sep 2024 04:29:10 +0800 Subject: [PATCH 2143/2187] xhci: change type of `slot_ctx_addr` in `struct XhciSlot` to `GuestAddress` Change type of `slot_ctx_addr` in `struct XhciSlot` to `GuestAddress`. Signed-off-by: liuxiangdong --- devices/src/usb/xhci/xhci_controller.rs | 61 +++++++++---------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index 32705f4b7..a49574a84 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -555,7 +555,7 @@ impl From for EpType { pub struct XhciSlot { pub enabled: bool, pub addressed: bool, - pub slot_ctx_addr: u64, + pub slot_ctx_addr: GuestAddress, pub usb_port: Option>>, pub endpoints: Vec, } @@ -570,7 +570,7 @@ impl XhciSlot { XhciSlot { enabled: false, addressed: false, - slot_ctx_addr: 0, + slot_ctx_addr: GuestAddress(0), usb_port: None, endpoints: eps, } @@ -579,18 +579,14 @@ impl XhciSlot { /// Get the slot context from the memory. fn get_slot_ctx(&self, mem: &Arc) -> Result { let mut slot_ctx = XhciSlotCtx::default(); - dma_read_u32( - mem, - GuestAddress(self.slot_ctx_addr), - slot_ctx.as_mut_dwords(), - )?; + dma_read_u32(mem, self.slot_ctx_addr, slot_ctx.as_mut_dwords())?; Ok(slot_ctx) } /// Get the slot state in slot context. fn get_slot_state_in_context(&self, mem: &Arc) -> Result { // Table 4-1: Device Slot State Code Definitions. - if self.slot_ctx_addr == 0 { + if self.slot_ctx_addr == GuestAddress(0) { return Ok(SLOT_DISABLED_ENABLED); } let slot_ctx = self.get_slot_ctx(mem)?; @@ -1315,7 +1311,7 @@ impl XhciDevice { self.slots[(slot_id - 1) as usize].enabled = false; self.slots[(slot_id - 1) as usize].addressed = false; self.slots[(slot_id - 1) as usize].usb_port = None; - self.slots[(slot_id - 1) as usize].slot_ctx_addr = 0; + self.slots[(slot_id - 1) as usize].slot_ctx_addr = GuestAddress(0); Ok(TRBCCode::Success) } @@ -1382,7 +1378,7 @@ impl XhciDevice { let mut locked_port = usb_port.lock().unwrap(); locked_port.slot_id = slot_id; self.slots[(slot_id - 1) as usize].usb_port = Some(usb_port.clone()); - self.slots[(slot_id - 1) as usize].slot_ctx_addr = octx; + self.slots[(slot_id - 1) as usize].slot_ctx_addr = GuestAddress(octx); let dev = locked_port.dev.as_ref().unwrap(); dev.lock().unwrap().reset(); if bsr { @@ -1484,7 +1480,7 @@ impl XhciDevice { slot_ctx.set_slot_state(SLOT_ADDRESSED); dma_write_u32( &self.mem_space, - GuestAddress(self.slots[(slot_id - 1) as usize].slot_ctx_addr), + self.slots[(slot_id - 1) as usize].slot_ctx_addr, slot_ctx.as_dwords(), )?; Ok(TRBCCode::Success) @@ -1514,7 +1510,7 @@ impl XhciDevice { } if ictl_ctx.add_flags & (1 << i) == 1 << i { self.disable_endpoint(slot_id, i)?; - self.enable_endpoint(slot_id, i, ictx, octx)?; + self.enable_endpoint(slot_id, i, ictx, octx.raw_value())?; } } // From section 4.6.6 Configure Endpoint of the spec: @@ -1539,7 +1535,7 @@ impl XhciDevice { slot_ctx.set_slot_state(SLOT_CONFIGURED); slot_ctx.set_context_entry(enabled_ep_idx); } - dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + dma_write_u32(&self.mem_space, octx, slot_ctx.as_dwords())?; Ok(TRBCCode::Success) } @@ -1578,14 +1574,10 @@ impl XhciDevice { islot_ctx.as_mut_dwords(), )?; let mut slot_ctx = XhciSlotCtx::default(); - dma_read_u32( - &self.mem_space, - GuestAddress(octx), - slot_ctx.as_mut_dwords(), - )?; + dma_read_u32(&self.mem_space, octx, slot_ctx.as_mut_dwords())?; slot_ctx.set_max_exit_latency(islot_ctx.get_max_exit_latency()); slot_ctx.set_interrupter_target(islot_ctx.get_interrupter_target()); - dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + dma_write_u32(&self.mem_space, octx, slot_ctx.as_dwords())?; } if ictl_ctx.add_flags & 0x2 == 0x2 { // Default control endpoint context. @@ -1599,21 +1591,16 @@ impl XhciDevice { iep_ctx.as_mut_dwords(), )?; let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32( - &self.mem_space, - GuestAddress( - // It is safe to use plus here because we previously verify the address. - octx + EP_CTX_OFFSET, - ), - ep_ctx.as_mut_dwords(), - )?; + let ep_ctx_addr = octx.checked_add(EP_CTX_OFFSET).with_context(|| { + format!( + "Endpoint Context access overflow, addr {:x} size {:x}", + octx.raw_value(), + EP_CTX_OFFSET + ) + })?; + dma_read_u32(&self.mem_space, ep_ctx_addr, ep_ctx.as_mut_dwords())?; ep_ctx.set_max_packet_size(iep_ctx.get_max_packet_size()); - dma_write_u32( - &self.mem_space, - // It is safe to use plus here because we previously verify the address. - GuestAddress(octx + EP_CTX_OFFSET), - ep_ctx.as_dwords(), - )?; + dma_write_u32(&self.mem_space, ep_ctx_addr, ep_ctx.as_dwords())?; } Ok(TRBCCode::Success) } @@ -1622,11 +1609,7 @@ impl XhciDevice { trace::usb_xhci_reset_device(&slot_id); let mut slot_ctx = XhciSlotCtx::default(); let octx = self.slots[(slot_id - 1) as usize].slot_ctx_addr; - dma_read_u32( - &self.mem_space, - GuestAddress(octx), - slot_ctx.as_mut_dwords(), - )?; + dma_read_u32(&self.mem_space, octx, slot_ctx.as_mut_dwords())?; let slot_state = (slot_ctx.dev_state >> SLOT_STATE_SHIFT) & SLOT_STATE_MASK; if slot_state != SLOT_ADDRESSED && slot_state != SLOT_CONFIGURED @@ -1641,7 +1624,7 @@ impl XhciDevice { slot_ctx.set_slot_state(SLOT_DEFAULT); slot_ctx.set_context_entry(1); slot_ctx.set_usb_device_address(0); - dma_write_u32(&self.mem_space, GuestAddress(octx), slot_ctx.as_dwords())?; + dma_write_u32(&self.mem_space, octx, slot_ctx.as_dwords())?; Ok(TRBCCode::Success) } -- Gitee From c5982d190eef8a48fc81dbc74ef04b0aa11b1533 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 12 Sep 2024 04:59:39 +0800 Subject: [PATCH 2144/2187] xhci: change type of `output_ctx_addr` in `struct XhciEpContext` to `GuestAddress` Change type of `output_ctx_addr` in `struct XhciEpContext` to `GuestAddress`. Signed-off-by: liuxiangdong --- devices/src/usb/xhci/xhci_controller.rs | 23 ++++++++++++----------- devices/src/usb/xhci/xhci_ring.rs | 10 +++------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/devices/src/usb/xhci/xhci_controller.rs b/devices/src/usb/xhci/xhci_controller.rs index a49574a84..14cd1351a 100644 --- a/devices/src/usb/xhci/xhci_controller.rs +++ b/devices/src/usb/xhci/xhci_controller.rs @@ -13,7 +13,7 @@ use std::collections::LinkedList; use std::mem::size_of; use std::slice::{from_raw_parts, from_raw_parts_mut}; -use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; @@ -191,7 +191,7 @@ impl XhciTransfer { self.epid, self.slotid, self.streamid ) })?; - ring.refresh_dequeue_ptr(self.ep_context.output_ctx_addr.load(Ordering::Acquire))?; + ring.refresh_dequeue_ptr(*self.ep_context.output_ctx_addr.lock().unwrap())?; return Ok(()); } @@ -320,7 +320,7 @@ pub struct XhciEpContext { enabled: bool, ring: Option>, ep_type: EpType, - output_ctx_addr: Arc, + output_ctx_addr: Arc>, state: Arc, interval: u32, mfindex_last: u64, @@ -339,7 +339,7 @@ impl XhciEpContext { enabled: false, ring: None, ep_type: EpType::Invalid, - output_ctx_addr: Arc::new(AtomicU64::new(0)), + output_ctx_addr: Arc::new(Mutex::new(GuestAddress(0))), state: Arc::new(AtomicU32::new(0)), interval: 0, mfindex_last: 0, @@ -356,7 +356,7 @@ impl XhciEpContext { fn init_ctx(&mut self, output_ctx: DmaAddr, ctx: &XhciEpCtx) -> Result<()> { let dequeue: DmaAddr = addr64_from_u32(ctx.deq_lo & !0xf, ctx.deq_hi); self.ep_type = ((ctx.ep_info2 >> EP_TYPE_SHIFT) & EP_TYPE_MASK).into(); - self.output_ctx_addr.store(output_ctx, Ordering::SeqCst); + *self.output_ctx_addr.lock().unwrap() = GuestAddress(output_ctx); self.max_pstreams = (ctx.ep_info >> EP_CTX_MAX_PSTREAMS_SHIFT) & EP_CTX_MAX_PSTREAMS_MASK; self.lsa = ((ctx.ep_info >> EP_CTX_LSA_SHIFT) & EP_CTX_LSA_MASK) != 0; self.interval = 1 << ((ctx.ep_info >> EP_CTX_INTERVAL_SHIFT) & EP_CTX_INTERVAL_MASK); @@ -388,11 +388,12 @@ impl XhciEpContext { /// Update the endpoint state and write the state to memory. fn set_state(&self, state: u32, stream_id: Option) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - let output_addr = self.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + let output_addr = self.output_ctx_addr.lock().unwrap(); + dma_read_u32(&self.mem, *output_addr, ep_ctx.as_mut_dwords())?; ep_ctx.ep_info &= !EP_STATE_MASK; ep_ctx.ep_info |= state; - dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + dma_write_u32(&self.mem, *output_addr, ep_ctx.as_dwords())?; + drop(output_addr); self.flush_dequeue_to_memory(stream_id)?; self.set_ep_state(state); trace::usb_xhci_set_state(self.epid, state); @@ -422,8 +423,8 @@ impl XhciEpContext { /// Stream Endpoints flush ring dequeue to both Endpoint and Stream context. fn flush_dequeue_to_memory(&self, stream_id: Option) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - let output_addr = self.output_ctx_addr.load(Ordering::Acquire); - dma_read_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_mut_dwords())?; + let output_addr = self.output_ctx_addr.lock().unwrap(); + dma_read_u32(&self.mem, *output_addr, ep_ctx.as_mut_dwords())?; if self.max_pstreams == 0 { let ring = self.get_ring(0)?; @@ -440,7 +441,7 @@ impl XhciEpContext { dma_write_u32(&self.mem, output_addr, stream_ctx.as_dwords())?; } - dma_write_u32(&self.mem, GuestAddress(output_addr), ep_ctx.as_dwords())?; + dma_write_u32(&self.mem, *output_addr, ep_ctx.as_dwords())?; Ok(()) } diff --git a/devices/src/usb/xhci/xhci_ring.rs b/devices/src/usb/xhci/xhci_ring.rs index b7a5df43c..35cda571b 100644 --- a/devices/src/usb/xhci/xhci_ring.rs +++ b/devices/src/usb/xhci/xhci_ring.rs @@ -206,15 +206,11 @@ impl XhciTransferRing { } /// Refresh dequeue pointer to output context. - pub fn refresh_dequeue_ptr(&self, output_ctx_addr: u64) -> Result<()> { + pub fn refresh_dequeue_ptr(&self, output_ctx_addr: GuestAddress) -> Result<()> { let mut ep_ctx = XhciEpCtx::default(); - dma_read_u32( - &self.mem, - GuestAddress(output_ctx_addr), - ep_ctx.as_mut_dwords(), - )?; + dma_read_u32(&self.mem, output_ctx_addr, ep_ctx.as_mut_dwords())?; self.update_dequeue_to_ctx(&mut ep_ctx.as_mut_dwords()[2..]); - dma_write_u32(&self.mem, GuestAddress(output_ctx_addr), ep_ctx.as_dwords())?; + dma_write_u32(&self.mem, output_ctx_addr, ep_ctx.as_dwords())?; Ok(()) } -- Gitee From d5be736f8077c5cc3db9c6190a5b2cfdf255e358 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sat, 31 Aug 2024 14:34:54 +0800 Subject: [PATCH 2145/2187] ozonec/container: Veryify current directory inside mount space rootfs If current working directory is outside mount space rootfs of the container process, getcwd() would fail. Fix CVE-2024-21626 --- ozonec/src/linux/container.rs | 3 +++ ozonec/src/linux/process.rs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index e54ee3e21..a980fc08a 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -249,6 +249,9 @@ impl LinuxContainer { if chdir_cwd_ret { process.chdir_cwd()?; } + // Ensure that the current working directory is inside the mount namespace root + // of the current container process. + Process::getcwd()?; process.clean_envs(); process.set_envs(); if process.no_new_privileges() { diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index d2f70f9bd..ae3add0d4 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -26,7 +26,10 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use caps::{self, CapSet, Capability, CapsHashSet}; -use nix::unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}; +use nix::{ + errno::Errno, + unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}, +}; use rlimit::{setrlimit, Resource, Rlim}; use super::{ @@ -310,6 +313,14 @@ impl Process { unreachable!() } + + pub fn getcwd() -> Result<()> { + unistd::getcwd().map_err(|e| match e { + Errno::ENOENT => anyhow!("Current working directory is out of container rootfs"), + _ => anyhow!("Failed to getcwd"), + })?; + Ok(()) + } } // Clone a new child process. -- Gitee From 539b7dfb5271141eaf9bc72f7ac4ab6c147303f6 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sat, 31 Aug 2024 19:34:16 +0800 Subject: [PATCH 2146/2187] ozonec/rootfs: Close fds when opening root directory Fix commit 818da30 (ozonec/linux: Add rootfs.rs) --- ozonec/src/linux/rootfs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ozonec/src/linux/rootfs.rs b/ozonec/src/linux/rootfs.rs index ac67d3251..a58d9e6b3 100644 --- a/ozonec/src/linux/rootfs.rs +++ b/ozonec/src/linux/rootfs.rs @@ -21,7 +21,7 @@ use nix::{ fcntl::{open, OFlag}, mount::{umount2, MntFlags, MsFlags}, sys::stat::{umask, Mode}, - unistd::{chroot, fchdir, pivot_root}, + unistd::{chroot, close, fchdir, pivot_root}, NixPath, }; use procfs::process::Process; @@ -226,6 +226,9 @@ impl Rootfs { umount2(".", MntFlags::MNT_DETACH) .with_context(|| "Failed to umount old root directory")?; fchdir(new_root).with_context(|| "Failed to chdir to new root directory")?; + + close(old_root).with_context(|| "Failed to close old_root")?; + close(new_root).with_context(|| "Failed to close new_root")?; Ok(()) } } -- Gitee From a11ac87a175dbf58986029d7c4f04ae04136fede Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 12 Sep 2024 18:28:10 +0800 Subject: [PATCH 2147/2187] address_space: don't return cache if AddressAttr is not the desired type Don't return cache if AddressAttr is not the required type. Otherwise, this wrong cache starting at 0 will cause subsequent address mapping errors. Fix: d46f7e5db43f1(address_space: add attribute for GuestAddress access) Signed-off-by: liuxiangdong --- address_space/src/address_space.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 88dd6d924..36106e954 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -634,10 +634,7 @@ impl AddressSpace { let start = range.addr_range.base.0; let end = range.addr_range.end_addr().0; // SAFETY: the size is in region range, and the type will be checked in get_host_address. - let host_base = unsafe { - self.get_host_address(GuestAddress(start), attr) - .unwrap_or(0) - }; + let host_base = unsafe { self.get_host_address(GuestAddress(start), attr) }?; let cache = RegionCache { reg_type, host_base, -- Gitee From f7faae5ed52cc74575d56e4ca67093131c1ec775 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Fri, 13 Sep 2024 07:00:38 +0800 Subject: [PATCH 2148/2187] pflash: delete useless region type check Region type of `rom` in `pflash` is set in `realize` function as `RegionType::RomDevice`. It will not be modified later. Remove these useless check. Fix: 44795e4c6ac5(Memory: Add an unsafe flag to the get host address function.) Signed-off-by: liuxiangdong --- devices/src/legacy/pflash.rs | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 6c3138e01..209c9ecdc 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -337,15 +337,9 @@ impl PFlash { ))); } - let attr = match mr.region_type() { - address_space::RegionType::Ram => AddressAttr::Ram, - address_space::RegionType::RomDevice => AddressAttr::RomDevice, - _ => bail!("Unexpected region type."), - }; - // SAFETY: size has been checked. let addr: u64 = unsafe { - mr.get_host_address(attr) + mr.get_host_address(AddressAttr::RomDevice) .with_context(|| "Failed to get host address.") }?; let ret = @@ -374,13 +368,8 @@ impl PFlash { data.len() as u64 ))); } - let attr = match mr.region_type() { - address_space::RegionType::Ram => AddressAttr::Ram, - address_space::RegionType::RomDevice => AddressAttr::RomDevice, - _ => bail!("Unexpected region type."), - }; // SAFETY: size has been checked. - let host_addr = unsafe { mr.get_host_address(attr).unwrap() }; + let host_addr = unsafe { mr.get_host_address(AddressAttr::RomDevice).unwrap() }; let src = // SAFETY: host_addr of the region is local allocated and sanity has been checked. unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; @@ -408,13 +397,8 @@ impl PFlash { data.len() as u64 ))); } - let attr = match mr.region_type() { - address_space::RegionType::Ram => AddressAttr::Ram, - address_space::RegionType::RomDevice => AddressAttr::RomDevice, - _ => bail!("Unexpected region type."), - }; // SAFETY: size has been checked. - let host_addr = unsafe { mr.get_host_address(attr).unwrap() }; + let host_addr = unsafe { mr.get_host_address(AddressAttr::RomDevice).unwrap() }; let mut dst = // SAFETY: host_addr of the region is local allocated and sanity has been checked. unsafe { std::slice::from_raw_parts_mut((host_addr + offset) as *mut u8, data.len()) }; -- Gitee From cd821fea3f2ce84df7f42b5c0e942cff2fb256f3 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 12 Sep 2024 20:33:21 +0800 Subject: [PATCH 2149/2187] virtio: move read/write_object_direct from address_space to virtio lib `read/write_object_direct` functions are only used in virtio lib. And they are unsafe. To avoid being mistakenly used by other modules, they will be moved to the virtio/split. Signed-off-by: liuxiangdong --- address_space/src/address_space.rs | 48 ------- trace/trace_info/memory.toml | 12 -- trace/trace_info/virtio.toml | 12 ++ virtio/src/device/balloon.rs | 6 +- virtio/src/device/block.rs | 43 ++---- virtio/src/device/gpu.rs | 36 ++--- virtio/src/device/net.rs | 34 ++--- virtio/src/device/rng.rs | 2 +- virtio/src/device/scsi_cntlr.rs | 11 +- virtio/src/device/serial.rs | 61 +++------ virtio/src/queue/mod.rs | 20 +-- virtio/src/queue/split.rs | 207 ++++++++++++++++------------- virtio/src/vhost/kernel/vsock.rs | 1 - virtio/src/vhost/user/client.rs | 4 +- 14 files changed, 201 insertions(+), 296 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 36106e954..bbc7cee56 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -12,7 +12,6 @@ use std::fmt; use std::fmt::Debug; -use std::io::Write; use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; @@ -24,7 +23,6 @@ use crate::{ AddressAttr, AddressRange, AddressSpaceError, FlatRange, GuestAddress, Listener, ListenerReqType, Region, RegionIoEventFd, RegionType, }; -use migration::{migration::Migratable, MigrationManager}; use util::aio::Iovec; use util::byte_code::ByteCode; @@ -768,29 +766,6 @@ impl AddressSpace { .with_context(|| "Failed to write object") } - /// Write an object to memory via host address. - /// - /// # Arguments - /// - /// * `data` - The object that will be written to the memory. - /// * `host_addr` - The start host address where the object will be written to. - /// - /// # Safety - /// - /// Make true that host_addr and std::mem::size_of::() are in the range of ram. - /// - /// # Note - /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub unsafe fn write_object_direct(&self, data: &T, host_addr: u64) -> Result<()> { - trace::address_space_write_direct(host_addr, std::mem::size_of::()); - // Mark vmm dirty page manually if live migration is active. - MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); - let mut dst = - std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()); - dst.write_all(data.as_bytes()) - .with_context(|| "Failed to write object via host address") - } - /// Read some data from memory to form an object. /// /// # Arguments @@ -811,29 +786,6 @@ impl AddressSpace { Ok(obj) } - /// Read some data from memory to form an object via host address. - /// - /// # Arguments - /// - /// * `hoat_addr` - The start host address where the data will be read from. - /// - /// # Safety - /// - /// Make true that host_addr and std::mem::size_of::() are in the range of ram. - /// - /// # Note - /// To use this method, it is necessary to implement `ByteCode` trait for your object. - pub unsafe fn read_object_direct(&self, host_addr: u64) -> Result { - trace::address_space_read_direct(host_addr, std::mem::size_of::()); - let mut obj = T::default(); - let mut dst = obj.as_mut_bytes(); - let src = std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()); - dst.write_all(src) - .with_context(|| "Failed to read object via host address")?; - - Ok(obj) - } - /// Update the topology of memory. pub fn update_topology(&self) -> Result<()> { trace::trace_scope_start!(address_update_topology); diff --git a/trace/trace_info/memory.toml b/trace/trace_info/memory.toml index 1a832e58f..c7d8171c5 100644 --- a/trace/trace_info/memory.toml +++ b/trace/trace_info/memory.toml @@ -10,18 +10,6 @@ args = "addr: &dyn fmt::Debug, count: u64" message = "Memory: flatview_write addr {:?}, count {}" enabled = true -[[events]] -name = "address_space_read_direct" -args = "host_addr: u64, count: usize" -message = "Memory: address_space_read_direct host_addr {}, count {}" -enabled = true - -[[events]] -name = "address_space_write_direct" -args = "host_addr: u64, count: usize" -message = "Memory: address_space_write_direct host_addr {}, count {}" -enabled = true - [[scopes]] name = "address_update_topology" args = "" diff --git a/trace/trace_info/virtio.toml b/trace/trace_info/virtio.toml index d30ba1f77..5d3240b00 100644 --- a/trace/trace_info/virtio.toml +++ b/trace/trace_info/virtio.toml @@ -309,3 +309,15 @@ name = "reporting_evt_handler" args = "" message = "Balloon: handle fpr message" enabled = true + +[[events]] +name = "virtio_read_object_direct" +args = "host_addr: u64, count: usize" +message = "Memory: virtio_read_object_direct host_addr {}, count {}" +enabled = true + +[[events]] +name = "virtio_write_object_direct" +args = "host_addr: u64, count: usize" +message = "Memory: virtio_write_object_direct host_addr {}, count {}" +enabled = true diff --git a/virtio/src/device/balloon.rs b/virtio/src/device/balloon.rs index 850f77cb6..e6ab32cde 100644 --- a/virtio/src/device/balloon.rs +++ b/virtio/src/device/balloon.rs @@ -658,7 +658,7 @@ impl BalloonIoHandler { } locked_queue .vring - .add_used(&self.mem_space, req.desc_index, req.elem_cnt) + .add_used(req.desc_index, req.elem_cnt) .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { @@ -693,7 +693,7 @@ impl BalloonIoHandler { } locked_queue .vring - .add_used(&self.mem_space, req.desc_index, req.elem_cnt) + .add_used(req.desc_index, req.elem_cnt) .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { @@ -741,7 +741,7 @@ impl BalloonIoHandler { locked_queue .vring - .add_used(&self.mem_space, req.desc_index, req.elem_cnt) + .add_used(req.desc_index, req.elem_cnt) .with_context(|| "Failed to add balloon response into used queue")?; (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { diff --git a/virtio/src/device/block.rs b/virtio/src/device/block.rs index e6efd9dee..45b919442 100644 --- a/virtio/src/device/block.rs +++ b/virtio/src/device/block.rs @@ -238,7 +238,7 @@ impl AioCompleteCb { let mut queue_lock = self.queue.lock().unwrap(); queue_lock .vring - .add_used(&self.mem_space, req.desc_index, req.in_len) + .add_used(req.desc_index, req.in_len) .with_context(|| { format!( "Failed to add used ring(blk io completion), index {}, len {}", @@ -247,10 +247,7 @@ impl AioCompleteCb { })?; trace::virtio_blk_complete_one_request(req.desc_index, req.in_len); - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { VirtioError::InterruptTrigger("blk io completion", VirtioInterruptType::Vring) @@ -732,12 +729,7 @@ impl BlockIoHandler { // Do not unlock or drop the locked_status in this function. let status; let mut locked_status; - let len = self - .queue - .lock() - .unwrap() - .vring - .avail_ring_len(&self.mem_space)?; + let len = self.queue.lock().unwrap().vring.avail_ring_len()?; if len > 0 { if let Some(block_backend) = self.block_backend.as_ref() { status = block_backend.lock().unwrap().get_status(); @@ -750,14 +742,7 @@ impl BlockIoHandler { let mut done = false; let mut iteration: u16 = 0; - while self - .queue - .lock() - .unwrap() - .vring - .avail_ring_len(&self.mem_space)? - != 0 - { + while self.queue.lock().unwrap().vring.avail_ring_len()? != 0 { // Do not stuck IO thread. iteration += 1; if iteration > MAX_ITERATION_PROCESS_QUEUE { @@ -766,19 +751,19 @@ impl BlockIoHandler { break; } - self.queue.lock().unwrap().vring.suppress_queue_notify( - &self.mem_space, - self.driver_features, - true, - )?; + self.queue + .lock() + .unwrap() + .vring + .suppress_queue_notify(self.driver_features, true)?; done = self.process_queue_internal()?; - self.queue.lock().unwrap().vring.suppress_queue_notify( - &self.mem_space, - self.driver_features, - false, - )?; + self.queue + .lock() + .unwrap() + .vring + .suppress_queue_notify(self.driver_features, false)?; // See whether we have been throttled. if let Some(lb) = self.leak_bucket.as_mut() { diff --git a/virtio/src/device/gpu.rs b/virtio/src/device/gpu.rs index 717a05e57..0725c09d4 100644 --- a/virtio/src/device/gpu.rs +++ b/virtio/src/device/gpu.rs @@ -727,20 +727,14 @@ impl GpuIoHandler { fn complete_one_request(&mut self, index: u16, len: u32) -> Result<()> { let mut queue_lock = self.ctrl_queue.lock().unwrap(); - queue_lock - .vring - .add_used(&self.mem_space, index, len) - .with_context(|| { - format!( - "Failed to add used ring(gpu ctrl), index {}, len {}", - index, len, - ) - })?; - - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + queue_lock.vring.add_used(index, len).with_context(|| { + format!( + "Failed to add used ring(gpu ctrl), index {}, len {}", + index, len, + ) + })?; + + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| "Failed to trigger interrupt(gpu ctrl)")?; trace::virtqueue_send_interrupt("Gpu", &*queue_lock as *const _ as u64); @@ -1603,17 +1597,11 @@ impl GpuIoHandler { } }; - queue - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| { - format!("Failed to add used ring(cursor), index {}", elem.index) - })?; + queue.vring.add_used(elem.index, 0).with_context(|| { + format!("Failed to add used ring(cursor), index {}", elem.index) + })?; - if queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) .with_context(|| { VirtioError::InterruptTrigger("gpu cursor", VirtioInterruptType::Vring) diff --git a/virtio/src/device/net.rs b/virtio/src/device/net.rs index e2833d931..4d7450897 100644 --- a/virtio/src/device/net.rs +++ b/virtio/src/device/net.rs @@ -621,13 +621,10 @@ impl NetCtrlHandler { locked_queue .vring - .add_used(&self.mem_space, elem.index, mem::size_of_val(&ack) as u32) + .add_used(elem.index, mem::size_of_val(&ack) as u32) .with_context(|| format!("Failed to add used ring {}", elem.index))?; - if locked_queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if locked_queue.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&locked_queue), false) .with_context(|| { VirtioError::InterruptTrigger("ctrl", VirtioInterruptType::Vring) @@ -715,7 +712,7 @@ impl NetIoQueue { if elem.desc_num == 0 { queue .vring - .suppress_queue_notify(&self.mem_space, self.driver_features, false) + .suppress_queue_notify(self.driver_features, false) .with_context(|| "Failed to enable rx queue notify")?; self.listen_state.lock().unwrap().set_queue_avail(false); break; @@ -769,7 +766,7 @@ impl NetIoQueue { queue .vring - .add_used(&self.mem_space, elem.index, u32::try_from(size)?) + .add_used(elem.index, u32::try_from(size)?) .with_context(|| { format!( "Failed to add used ring for net rx, index: {}, len: {}", @@ -777,10 +774,7 @@ impl NetIoQueue { ) })?; - if queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) .with_context(|| { VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) @@ -824,7 +818,7 @@ impl NetIoQueue { queue.vring.push_back(); queue .vring - .suppress_queue_notify(&self.mem_space, self.driver_features, true) + .suppress_queue_notify(self.driver_features, true) .with_context(|| "Failed to suppress tx queue notify")?; self.listen_state.lock().unwrap().set_tap_full(true); break; @@ -833,13 +827,10 @@ impl NetIoQueue { queue .vring - .add_used(&self.mem_space, elem.index, 0) + .add_used(elem.index, 0) .with_context(|| format!("Net tx: Failed to add used ring {}", elem.index))?; - if queue - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue), false) .with_context(|| { VirtioError::InterruptTrigger("net", VirtioInterruptType::Vring) @@ -1057,11 +1048,10 @@ impl NetIoHandler { net_queue.listen_state.lock().unwrap().set_queue_avail(true); let mut locked_queue = net_queue.rx.queue.lock().unwrap(); - if let Err(ref err) = locked_queue.vring.suppress_queue_notify( - &net_queue.mem_space, - net_queue.driver_features, - true, - ) { + if let Err(ref err) = locked_queue + .vring + .suppress_queue_notify(net_queue.driver_features, true) + { error!("Failed to suppress rx queue notify: {:?}", err); report_virtio_error( net_queue.interrupt_cb.clone(), diff --git a/virtio/src/device/rng.rs b/virtio/src/device/rng.rs index 2040e8561..68f17d84a 100644 --- a/virtio/src/device/rng.rs +++ b/virtio/src/device/rng.rs @@ -192,7 +192,7 @@ impl RngHandler { queue_lock .vring - .add_used(&self.mem_space, elem.index, size) + .add_used(elem.index, size) .with_context(|| { format!( "Failed to add used ring, index: {}, size: {}", diff --git a/virtio/src/device/scsi_cntlr.rs b/virtio/src/device/scsi_cntlr.rs index dc58be3a7..1c03b471d 100644 --- a/virtio/src/device/scsi_cntlr.rs +++ b/virtio/src/device/scsi_cntlr.rs @@ -544,11 +544,7 @@ impl VirtioScsiReq // DESC_CHAIN_MAX_TOTAL_LEN(1 << 32). So, it will not overflow here. queue_lock .vring - .add_used( - &self.mem_space, - self.desc_index, - self.data_len + (size_of::() as u32), - ) + .add_used(self.desc_index, self.data_len + (size_of::() as u32)) .with_context(|| { format!( "Failed to add used ring(scsi completion), index {}, len {}", @@ -556,10 +552,7 @@ impl VirtioScsiReq ) })?; - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { VirtioError::InterruptTrigger( diff --git a/virtio/src/device/serial.rs b/virtio/src/device/serial.rs index 4047bcb38..3141672bb 100644 --- a/virtio/src/device/serial.rs +++ b/virtio/src/device/serial.rs @@ -493,21 +493,15 @@ impl SerialPortHandler { trace::virtio_serial_output_data(iovec_size, size); } - queue_lock - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| { - format!( - "Failed to add used ring for virtio serial port output, index: {} len: {}", - elem.index, 0, - ) - })?; + queue_lock.vring.add_used(elem.index, 0).with_context(|| { + format!( + "Failed to add used ring for virtio serial port output, index: {} len: {}", + elem.index, 0, + ) + })?; } - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { VirtioError::InterruptTrigger( @@ -555,10 +549,9 @@ impl SerialPortHandler { } let mut queue_lock = self.input_queue.lock().unwrap(); - let _ = - queue_lock - .vring - .suppress_queue_notify(&self.mem_space, self.driver_features, !enable); + let _ = queue_lock + .vring + .suppress_queue_notify(self.driver_features, !enable); } fn input_handle_internal(&mut self, buffer: &[u8]) -> Result<()> { @@ -614,7 +607,7 @@ impl SerialPortHandler { queue_lock .vring - .add_used(&self.mem_space, elem.index, once_count as u32) + .add_used(elem.index, once_count as u32) .with_context(|| { format!( "Failed to add used ring for virtio serial port input: index {} len {}", @@ -622,10 +615,7 @@ impl SerialPortHandler { ) })?; - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { VirtioError::InterruptTrigger( @@ -761,21 +751,15 @@ impl SerialControlHandler { ); self.handle_control_message(&mut req); - queue_lock - .vring - .add_used(&self.mem_space, elem.index, 0) - .with_context(|| { - format!( - "Failed to add used ring for control port, index: {} len: {}.", - elem.index, 0 - ) - })?; + queue_lock.vring.add_used(elem.index, 0).with_context(|| { + format!( + "Failed to add used ring for control port, index: {} len: {}.", + elem.index, 0 + ) + })?; } - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { VirtioError::InterruptTrigger( @@ -922,7 +906,7 @@ impl SerialControlHandler { queue_lock .vring - .add_used(&self.mem_space, elem.index, len as u32) + .add_used(elem.index, len as u32) .with_context(|| { format!( "Failed to add used ring(serial input control queue), index {}, len {}", @@ -930,10 +914,7 @@ impl SerialControlHandler { ) })?; - if queue_lock - .vring - .should_notify(&self.mem_space, self.driver_features) - { + if queue_lock.vring.should_notify(self.driver_features) { (self.interrupt_cb)(&VirtioInterruptType::Vring, Some(&queue_lock), false) .with_context(|| { VirtioError::InterruptTrigger( diff --git a/virtio/src/queue/mod.rs b/virtio/src/queue/mod.rs index d226cd703..44f164731 100644 --- a/virtio/src/queue/mod.rs +++ b/virtio/src/queue/mod.rs @@ -126,32 +126,24 @@ pub trait VringOps { /// /// # Arguments /// - /// * `sys_mem` - Address space to which the vring belongs. /// * `index` - Index of descriptor in the virqueue descriptor table. /// * `len` - Total length of the descriptor chain which was used (written to). - fn add_used(&mut self, sys_mem: &Arc, index: u16, len: u32) -> Result<()>; + fn add_used(&mut self, index: u16, len: u32) -> Result<()>; /// Return true if guest needed to be notified. /// /// # Arguments /// - /// * `sys_mem` - Address space to which the vring belongs. /// * `features` - Bit mask of features negotiated by the backend and the frontend. - fn should_notify(&mut self, sys_mem: &Arc, features: u64) -> bool; + fn should_notify(&mut self, features: u64) -> bool; /// Give guest a hint to suppress virtqueue notification. /// /// # Arguments /// - /// * `sys_mem` - Address space to which the vring belongs. /// * `features` - Bit mask of features negotiated by the backend and the frontend. /// * `suppress` - Suppress virtqueue notification or not. - fn suppress_queue_notify( - &mut self, - sys_mem: &Arc, - features: u64, - suppress: bool, - ) -> Result<()>; + fn suppress_queue_notify(&mut self, features: u64, suppress: bool) -> Result<()>; /// Get the actual size of the vring. fn actual_size(&self) -> u16; @@ -160,13 +152,13 @@ pub trait VringOps { fn get_queue_config(&self) -> QueueConfig; /// The number of descriptor chains in the available ring. - fn avail_ring_len(&mut self, sys_mem: &Arc) -> Result; + fn avail_ring_len(&mut self) -> Result; /// Get the avail index of the vring. - fn get_avail_idx(&self, sys_mem: &Arc) -> Result; + fn get_avail_idx(&self) -> Result; /// Get the used index of the vring. - fn get_used_idx(&self, sys_mem: &Arc) -> Result; + fn get_used_idx(&self) -> Result; /// Get the region cache information of the SplitVring. fn get_cache(&self) -> &Option; diff --git a/virtio/src/queue/split.rs b/virtio/src/queue/split.rs index de35b0432..30a906ffc 100644 --- a/virtio/src/queue/split.rs +++ b/virtio/src/queue/split.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use std::cmp::min; +use std::io::Write; use std::mem::size_of; use std::num::Wrapping; use std::ops::{Deref, DerefMut}; @@ -28,6 +29,7 @@ use crate::{ report_virtio_error, virtio_has_feature, VirtioError, VirtioInterrupt, VIRTIO_F_RING_EVENT_IDX, }; use address_space::{AddressAttr, AddressSpace, GuestAddress, RegionCache, RegionType}; +use migration::{migration::Migratable, MigrationManager}; use util::byte_code::ByteCode; /// When host consumes a buffer, don't interrupt the guest. @@ -52,6 +54,51 @@ const VRING_IDX_POSITION: u64 = size_of::() as u64; /// The length of virtio descriptor. const DESCRIPTOR_LEN: u64 = size_of::() as u64; +/// Read some data from memory to form an object via host address. +/// +/// # Arguments +/// +/// * `hoat_addr` - The start host address where the data will be read from. +/// +/// # Safety +/// +/// Make true that host_addr and std::mem::size_of::() are in the range of ram. +/// +/// # Note +/// To use this method, it is necessary to implement `ByteCode` trait for your object. +unsafe fn read_object_direct(host_addr: u64) -> Result { + trace::virtio_read_object_direct(host_addr, std::mem::size_of::()); + let mut obj = T::default(); + let mut dst = obj.as_mut_bytes(); + let src = std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()); + dst.write_all(src) + .with_context(|| "Failed to read object via host address")?; + + Ok(obj) +} + +/// Write an object to memory via host address. +/// +/// # Arguments +/// +/// * `data` - The object that will be written to the memory. +/// * `host_addr` - The start host address where the object will be written to. +/// +/// # Safety +/// +/// Make true that host_addr and std::mem::size_of::() are in the range of ram. +/// +/// # Note +/// To use this method, it is necessary to implement `ByteCode` trait for your object. +unsafe fn write_object_direct(data: &T, host_addr: u64) -> Result<()> { + trace::virtio_write_object_direct(host_addr, std::mem::size_of::()); + // Mark vmm dirty page manually if live migration is active. + MigrationManager::mark_dirty_log(host_addr, data.as_bytes().len() as u64); + let mut dst = std::slice::from_raw_parts_mut(host_addr as *mut u8, std::mem::size_of::()); + dst.write_all(data.as_bytes()) + .with_context(|| "Failed to write object via host address") +} + #[derive(Default, Clone, Copy)] pub struct VirtioAddrCache { /// Host virtual address of the descriptor table. @@ -270,8 +317,7 @@ impl SplitVringDesc { })?; // SAFETY: dest_addr has been checked in SplitVringDesc::is_valid() and is guaranteed to be within the ram range. let desc = unsafe { - sys_mem - .read_object_direct::(desc_addr) + read_object_direct::(desc_addr) .with_context(|| VirtioError::ReadObjectErr("a descriptor", desc_addr)) }?; @@ -507,52 +553,48 @@ impl SplitVring { } /// Get the flags and idx of the available ring from guest memory. - fn get_avail_flags_idx(&self, sys_mem: &Arc) -> Result { + fn get_avail_flags_idx(&self) -> Result { // SAFETY: avail_ring_host is checked when addr_cache inited. unsafe { - sys_mem - .read_object_direct::(self.addr_cache.avail_ring_host) - .with_context(|| { - VirtioError::ReadObjectErr("avail flags idx", self.avail_ring.raw_value()) - }) + read_object_direct::(self.addr_cache.avail_ring_host).with_context( + || VirtioError::ReadObjectErr("avail flags idx", self.avail_ring.raw_value()), + ) } } /// Get the idx of the available ring from guest memory. - fn get_avail_idx(&self, sys_mem: &Arc) -> Result { - let flags_idx = self.get_avail_flags_idx(sys_mem)?; + fn get_avail_idx(&self) -> Result { + let flags_idx = self.get_avail_flags_idx()?; Ok(flags_idx.idx) } /// Get the flags of the available ring from guest memory. - fn get_avail_flags(&self, sys_mem: &Arc) -> Result { - let flags_idx = self.get_avail_flags_idx(sys_mem)?; + fn get_avail_flags(&self) -> Result { + let flags_idx = self.get_avail_flags_idx()?; Ok(flags_idx.flags) } /// Get the flags and idx of the used ring from guest memory. - fn get_used_flags_idx(&self, sys_mem: &Arc) -> Result { + fn get_used_flags_idx(&self) -> Result { // Make sure the idx read from sys_mem is new. fence(Ordering::SeqCst); // SAFETY: used_ring_host has been checked in set_addr_cache() and is guaranteed to be within the ram range. unsafe { - sys_mem - .read_object_direct::(self.addr_cache.used_ring_host) - .with_context(|| { - VirtioError::ReadObjectErr("used flags idx", self.used_ring.raw_value()) - }) + read_object_direct::(self.addr_cache.used_ring_host).with_context( + || VirtioError::ReadObjectErr("used flags idx", self.used_ring.raw_value()), + ) } } /// Get the index of the used ring from guest memory. - fn get_used_idx(&self, sys_mem: &Arc) -> Result { - let flag_idx = self.get_used_flags_idx(sys_mem)?; + fn get_used_idx(&self) -> Result { + let flag_idx = self.get_used_flags_idx()?; Ok(flag_idx.idx) } /// Set the used flags to suppress virtqueue notification or not - fn set_used_flags(&self, sys_mem: &Arc, suppress: bool) -> Result<()> { - let mut flags_idx = self.get_used_flags_idx(sys_mem)?; + fn set_used_flags(&self, suppress: bool) -> Result<()> { + let mut flags_idx = self.get_used_flags_idx()?; if suppress { flags_idx.flags |= VRING_USED_F_NO_NOTIFY; @@ -561,11 +603,7 @@ impl SplitVring { } // SAFETY: used_ring_host has been checked when addr_cache inited. unsafe { - sys_mem - .write_object_direct::( - &flags_idx, - self.addr_cache.used_ring_host, - ) + write_object_direct::(&flags_idx, self.addr_cache.used_ring_host) .with_context(|| { format!( "Failed to set used flags, used_ring: 0x{:X}", @@ -579,24 +617,23 @@ impl SplitVring { } /// Set the avail idx to the field of the event index for the available ring. - fn set_avail_event(&self, sys_mem: &Arc, event_idx: u16) -> Result<()> { + fn set_avail_event(&self, event_idx: u16) -> Result<()> { trace::virtqueue_set_avail_event(self as *const _ as u64, event_idx); let avail_event_offset = VRING_FLAGS_AND_IDX_LEN + USEDELEM_LEN * u64::from(self.actual_size()); // SAFETY: used_ring_host has been checked in set_addr_cache(). unsafe { - sys_mem - .write_object_direct( - &event_idx, - self.addr_cache.used_ring_host + avail_event_offset, + write_object_direct( + &event_idx, + self.addr_cache.used_ring_host + avail_event_offset, + ) + .with_context(|| { + format!( + "Failed to set avail event idx, used_ring: 0x{:X}, offset: {}", + self.used_ring.raw_value(), + avail_event_offset, ) - .with_context(|| { - format!( - "Failed to set avail event idx, used_ring: 0x{:X}, offset: {}", - self.used_ring.raw_value(), - avail_event_offset, - ) - }) + }) }?; // Make sure the data has been set. fence(Ordering::SeqCst); @@ -604,7 +641,7 @@ impl SplitVring { } /// Get the event index of the used ring from guest memory. - fn get_used_event(&self, sys_mem: &Arc) -> Result { + fn get_used_event(&self) -> Result { let used_event_offset = VRING_FLAGS_AND_IDX_LEN + AVAILELEM_LEN * u64::from(self.actual_size()); // Make sure the event idx read from sys_mem is new. @@ -614,8 +651,7 @@ impl SplitVring { let used_event_addr = self.addr_cache.avail_ring_host + used_event_offset; // SAFETY: used_event_addr is protected by virtio calculations and is guaranteed to be within the ram range. let used_event = unsafe { - sys_mem - .read_object_direct::(used_event_addr) + read_object_direct::(used_event_addr) .with_context(|| VirtioError::ReadObjectErr("used event id", used_event_addr)) }?; @@ -623,8 +659,8 @@ impl SplitVring { } /// Return true if VRING_AVAIL_F_NO_INTERRUPT is set. - fn is_avail_ring_no_interrupt(&self, sys_mem: &Arc) -> bool { - match self.get_avail_flags(sys_mem) { + fn is_avail_ring_no_interrupt(&self) -> bool { + match self.get_avail_flags() { Ok(avail_flags) => (avail_flags & VRING_AVAIL_F_NO_INTERRUPT) != 0, Err(ref e) => { warn!( @@ -637,9 +673,9 @@ impl SplitVring { } /// Return true if it's required to trigger interrupt for the used vring. - fn used_ring_need_event(&mut self, sys_mem: &Arc) -> bool { + fn used_ring_need_event(&mut self) -> bool { let old = self.last_signal_used; - let new = match self.get_used_idx(sys_mem) { + let new = match self.get_used_idx() { Ok(used_idx) => Wrapping(used_idx), Err(ref e) => { error!("Failed to get the status for notifying used vring: {:?}", e); @@ -647,7 +683,7 @@ impl SplitVring { } }; - let used_event_idx = match self.get_used_event(sys_mem) { + let used_event_idx = match self.get_used_event() { Ok(idx) => Wrapping(idx), Err(ref e) => { error!("Failed to get the status for notifying used vring: {:?}", e); @@ -779,11 +815,9 @@ impl SplitVring { let desc_index_addr = self.addr_cache.avail_ring_host + index_offset; // SAFETY: dest_index_addr is protected by virtio calculations and is guaranteed to be within the ram range. let desc_index = unsafe { - sys_mem - .read_object_direct::(desc_index_addr) - .with_context(|| { - VirtioError::ReadObjectErr("the index of descriptor", desc_index_addr) - }) + read_object_direct::(desc_index_addr).with_context(|| { + VirtioError::ReadObjectErr("the index of descriptor", desc_index_addr) + }) }?; let desc = SplitVringDesc::new( @@ -796,7 +830,7 @@ impl SplitVring { // Suppress queue notification related to current processing desc chain. if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { - self.set_avail_event(sys_mem, (next_avail + Wrapping(1)).0) + self.set_avail_event((next_avail + Wrapping(1)).0) .with_context(|| "Failed to set avail event for popping avail ring")?; } @@ -854,7 +888,7 @@ impl VringOps for SplitVring { fn pop_avail(&mut self, sys_mem: &Arc, features: u64) -> Result { let mut element = Element::new(0); - if !self.is_enabled() || self.avail_ring_len(sys_mem)? == 0 { + if !self.is_enabled() || self.avail_ring_len()? == 0 { return Ok(element); } @@ -877,7 +911,7 @@ impl VringOps for SplitVring { self.next_avail -= Wrapping(1); } - fn add_used(&mut self, sys_mem: &Arc, index: u16, len: u32) -> Result<()> { + fn add_used(&mut self, index: u16, len: u32) -> Result<()> { if index >= self.size { return Err(anyhow!(VirtioError::QueueIndex(index, self.size))); } @@ -892,8 +926,7 @@ impl VringOps for SplitVring { }; // SAFETY: used_elem_addr is guaranteed to be within ram range. unsafe { - sys_mem - .write_object_direct::(&used_elem, used_elem_addr) + write_object_direct::(&used_elem, used_elem_addr) .with_context(|| "Failed to write object for used element") }?; // Make sure used element is filled before updating used idx. @@ -902,12 +935,11 @@ impl VringOps for SplitVring { self.next_used += Wrapping(1); // SAFETY: used_ring_host has been checked when addr_cache inited. unsafe { - sys_mem - .write_object_direct( - &(self.next_used.0), - self.addr_cache.used_ring_host + VRING_IDX_POSITION, - ) - .with_context(|| "Failed to write next used idx") + write_object_direct( + &(self.next_used.0), + self.addr_cache.used_ring_host + VRING_IDX_POSITION, + ) + .with_context(|| "Failed to write next used idx") }?; // Make sure used index is exposed before notifying guest. fence(Ordering::SeqCst); @@ -919,28 +951,23 @@ impl VringOps for SplitVring { Ok(()) } - fn should_notify(&mut self, sys_mem: &Arc, features: u64) -> bool { + fn should_notify(&mut self, features: u64) -> bool { if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { - self.used_ring_need_event(sys_mem) + self.used_ring_need_event() } else { - !self.is_avail_ring_no_interrupt(sys_mem) + !self.is_avail_ring_no_interrupt() } } - fn suppress_queue_notify( - &mut self, - sys_mem: &Arc, - features: u64, - suppress: bool, - ) -> Result<()> { + fn suppress_queue_notify(&mut self, features: u64, suppress: bool) -> Result<()> { if !self.is_enabled() { bail!("queue is not ready"); } if virtio_has_feature(features, VIRTIO_F_RING_EVENT_IDX) { - self.set_avail_event(sys_mem, self.get_avail_idx(sys_mem)?)?; + self.set_avail_event(self.get_avail_idx()?)?; } else { - self.set_used_flags(sys_mem, suppress)?; + self.set_used_flags(suppress)?; } Ok(()) } @@ -956,18 +983,18 @@ impl VringOps for SplitVring { } /// The number of descriptor chains in the available ring. - fn avail_ring_len(&mut self, sys_mem: &Arc) -> Result { - let avail_idx = self.get_avail_idx(sys_mem).map(Wrapping)?; + fn avail_ring_len(&mut self) -> Result { + let avail_idx = self.get_avail_idx().map(Wrapping)?; Ok((avail_idx - self.next_avail).0) } - fn get_avail_idx(&self, sys_mem: &Arc) -> Result { - SplitVring::get_avail_idx(self, sys_mem) + fn get_avail_idx(&self) -> Result { + SplitVring::get_avail_idx(self) } - fn get_used_idx(&self, sys_mem: &Arc) -> Result { - SplitVring::get_used_idx(self, sys_mem) + fn get_used_idx(&self) -> Result { + SplitVring::get_used_idx(self) } fn get_cache(&self) -> &Option { @@ -987,7 +1014,7 @@ impl VringOps for SplitVring { let mut avail_bytes = 0_usize; let mut avail_idx = self.next_avail; - let end_idx = self.get_avail_idx(sys_mem).map(Wrapping)?; + let end_idx = self.get_avail_idx().map(Wrapping)?; while (end_idx - avail_idx).0 > 0 { let desc_info = self.get_desc_info(sys_mem, avail_idx, 0)?; @@ -1532,7 +1559,7 @@ mod tests { // the event idx of avail ring is equal to get_avail_event let event_idx = vring.get_avail_event(&sys_space).unwrap(); assert_eq!(event_idx, 1); - let avail_idx = vring.get_avail_idx(&sys_space).unwrap(); + let avail_idx = vring.get_avail_idx().unwrap(); assert_eq!(avail_idx, 1); } @@ -2018,7 +2045,7 @@ mod tests { // The event idx of avail ring is equal to get_avail_event. let event_idx = vring.get_avail_event(&sys_space).unwrap(); assert_eq!(event_idx, 1); - let avail_idx = vring.get_avail_idx(&sys_space).unwrap(); + let avail_idx = vring.get_avail_idx().unwrap(); assert_eq!(avail_idx, 1); } @@ -2056,7 +2083,7 @@ mod tests { assert!(vring.is_valid(&sys_space)); // it is false when the index is more than the size of queue - if let Err(err) = vring.add_used(&sys_space, QUEUE_SIZE, 100) { + if let Err(err) = vring.add_used(QUEUE_SIZE, 100) { if let Some(e) = err.downcast_ref::() { if let VirtioError::QueueIndex(offset, size) = e { assert_eq!(*offset, 256); @@ -2065,7 +2092,7 @@ mod tests { } } - assert!(vring.add_used(&sys_space, 10, 100).is_ok()); + assert!(vring.add_used(10, 100).is_ok()); let elem = vring.get_used_elem(&sys_space, 0).unwrap(); assert_eq!(elem.id, 10); assert_eq!(elem.len, 100); @@ -2108,7 +2135,7 @@ mod tests { // it's true when the feature of event idx and no interrupt for the avail ring is closed let features = 0_u64; assert!(vring.set_avail_ring_flags(&sys_space, 0).is_ok()); - assert!(vring.should_notify(&sys_space, features)); + assert!(vring.should_notify(features)); // it's false when the feature of event idx is closed and the feature of no interrupt for // the avail ring is open @@ -2116,7 +2143,7 @@ mod tests { assert!(vring .set_avail_ring_flags(&sys_space, VRING_AVAIL_F_NO_INTERRUPT) .is_ok()); - assert!(!vring.should_notify(&sys_space, features)); + assert!(!vring.should_notify(features)); // it's true when the feature of event idx is open and // (new - event_idx - Wrapping(1) < new -old) @@ -2124,20 +2151,20 @@ mod tests { vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 6).is_ok()); // event_idx - assert!(vring.should_notify(&sys_space, features)); + assert!(vring.should_notify(features)); // it's false when the feature of event idx is open and // (new - event_idx - Wrapping(1) > new - old) vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 1).is_ok()); // event_idx - assert!(!vring.should_notify(&sys_space, features)); + assert!(!vring.should_notify(features)); // it's false when the feature of event idx is open and // (new - event_idx - Wrapping(1) = new -old) vring.last_signal_used = Wrapping(5); // old assert!(vring.set_used_ring_idx(&sys_space, 10).is_ok()); // new assert!(vring.set_used_event_idx(&sys_space, 4).is_ok()); // event_idx - assert!(!vring.should_notify(&sys_space, features)); + assert!(!vring.should_notify(features)); } } diff --git a/virtio/src/vhost/kernel/vsock.rs b/virtio/src/vhost/kernel/vsock.rs index 53a387459..b67d21aeb 100644 --- a/virtio/src/vhost/kernel/vsock.rs +++ b/virtio/src/vhost/kernel/vsock.rs @@ -174,7 +174,6 @@ impl Vsock { event_queue_locked .vring .add_used( - &self.mem_space, element.index, VIRTIO_VSOCK_EVENT_TRANSPORT_RESET.as_bytes().len() as u32, ) diff --git a/virtio/src/vhost/user/client.rs b/virtio/src/vhost/user/client.rs index 4a51c9294..b58020a1c 100644 --- a/virtio/src/vhost/user/client.rs +++ b/virtio/src/vhost/user/client.rs @@ -405,7 +405,6 @@ pub struct VhostUserClient { client: Arc>, mem_info: VhostUserMemInfo, delete_evts: Vec, - mem_space: Arc, queues: Vec>>, queue_evts: Vec>, call_events: Vec>, @@ -441,7 +440,6 @@ impl VhostUserClient { client, mem_info, delete_evts: Vec::new(), - mem_space: mem_space.clone(), queues: Vec::new(), queue_evts: Vec::new(), call_events: Vec::new(), @@ -571,7 +569,7 @@ impl VhostUserClient { })?; // When spdk/ovs has been killed, stratovirt can not get the last avail // index in spdk/ovs, it can only use used index as last avail index. - let last_avail_idx = queue.vring.get_used_idx(&self.mem_space)?; + let last_avail_idx = queue.vring.get_used_idx()?; self.set_vring_base(queue_index, last_avail_idx) .with_context(|| { format!( -- Gitee From 9e06639529ea02778da173c92bf366159d8cf25a Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 17 Sep 2024 09:06:31 +0800 Subject: [PATCH 2150/2187] ozonec/tests: Add README.md --- ozonec/tests/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 ozonec/tests/README.md diff --git a/ozonec/tests/README.md b/ozonec/tests/README.md new file mode 100644 index 000000000..c33982d51 --- /dev/null +++ b/ozonec/tests/README.md @@ -0,0 +1,30 @@ +# Integration Tests + +ozonec uses [bats (Bash Automated Testing System)](https://github.com/bats-core/bats-core) framework to run +integration tests written in *bash*. + +## Before running tests + +Install [bats (Bash Automated Testing System)](https://github.com/bats-core/bats-core#installing-bats-from-source) from source: +``` +$ git clone https://github.com/bats-core/bats-core.git +$ cd bats-core +$ ./install.sh /usr/local +``` + +And *jq* is may also needed to modify json file in tests. + +## Running tests + +You can run tests using bats directly. For example: +``` +bats ./ +``` +Or you can just run a single test file. For example: +``` +bats create.bats +``` + +## Writing tests + +Please refer to [bats (Writing tests)](https://bats-core.readthedocs.io/en/stable/writing-tests.html). \ No newline at end of file -- Gitee From c29db424f4771079323ddbd78247a7feb9457940 Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Sat, 14 Sep 2024 16:03:16 +0800 Subject: [PATCH 2151/2187] AddressSpace: fix mst Mst use mmio_read/write to read/write the ram region. --- address_space/src/address_space.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs index 36106e954..263aa3145 100644 --- a/address_space/src/address_space.rs +++ b/address_space/src/address_space.rs @@ -66,7 +66,7 @@ impl FlatView { let region_base = fr.addr_range.base.unchecked_sub(fr.offset_in_region); let fr_remain = fr.addr_range.size - fr_offset; - if fr.owner.region_type() != region_type { + if !util::test_helper::is_test_enabled() && fr.owner.region_type() != region_type { // Read op RomDevice in I/O access mode as MMIO if region_type == RegionType::IO && fr.owner.region_type() == RegionType::RomDevice @@ -79,7 +79,9 @@ impl FlatView { } } - if region_type == RegionType::Ram || region_type == RegionType::RamDevice { + if fr.owner.region_type() == RegionType::Ram + || fr.owner.region_type() == RegionType::RamDevice + { l = std::cmp::min(l, fr_remain); } fr.owner.read(dst, region_base, region_offset, l)?; @@ -126,14 +128,17 @@ impl FlatView { let fr_remain = fr.addr_range.size - fr_offset; // Read/Write ops to RomDevice is MMIO. - if fr.owner.region_type() != region_type + if !util::test_helper::is_test_enabled() + && fr.owner.region_type() != region_type && !(region_type == RegionType::IO && fr.owner.region_type() == RegionType::RomDevice) { bail!("mismatch region type") } - if region_type == RegionType::Ram || region_type == RegionType::RamDevice { + if fr.owner.region_type() == RegionType::Ram + || fr.owner.region_type() == RegionType::RamDevice + { l = std::cmp::min(l, fr_remain); } fr.owner.write(src, region_base, region_offset, l)?; -- Gitee From f9182af1c10d5a84dcd8c4285c48aac0b13861f2 Mon Sep 17 00:00:00 2001 From: Xu Yandong Date: Fri, 20 Sep 2024 07:53:05 +0000 Subject: [PATCH 2152/2187] address_space: fix numa support in micro machine Signed-off-by: Xu Yandong --- machine/src/aarch64/micro.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/machine/src/aarch64/micro.rs b/machine/src/aarch64/micro.rs index 8cd32b5db..4cf525047 100644 --- a/machine/src/aarch64/micro.rs +++ b/machine/src/aarch64/micro.rs @@ -255,14 +255,33 @@ trait CompileFDTHelper { impl CompileFDTHelper for LightMachine { fn generate_memory_node(&self, fdt: &mut FdtBuilder) -> Result<()> { - let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let mem_size = self.base.sys_mem.memory_end_address().raw_value() - - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; - let node = "memory"; - let memory_node_dep = fdt.begin_node(node)?; - fdt.set_property_string("device_type", "memory")?; - fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; - fdt.end_node(memory_node_dep) + if self.base.numa_nodes.is_none() { + let mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let mem_size = self.base.sys_mem.memory_end_address().raw_value() + - MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + let node = "memory"; + let memory_node_dep = fdt.begin_node(node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; + fdt.end_node(memory_node_dep)?; + + return Ok(()); + } + + // Set NUMA node information. + let mut mem_base = MEM_LAYOUT[LayoutEntryType::Mem as usize].0; + for (id, node) in self.base.numa_nodes.as_ref().unwrap().iter().enumerate() { + let mem_size = node.1.size; + let node = format!("memory@{:x}", mem_base); + let memory_node_dep = fdt.begin_node(&node)?; + fdt.set_property_string("device_type", "memory")?; + fdt.set_property_array_u64("reg", &[mem_base, mem_size])?; + fdt.set_property_u32("numa-node-id", id as u32)?; + fdt.end_node(memory_node_dep)?; + mem_base += mem_size; + } + + Ok(()) } fn generate_chosen_node(&self, fdt: &mut FdtBuilder) -> Result<()> { -- Gitee From 8a2576e4b967b7c4a551469f32a8c4f6d9c2fb42 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Fri, 20 Sep 2024 09:12:09 +0800 Subject: [PATCH 2153/2187] ozonec/mount: Introduce openat2() to validate mount target A mount target path could be specified containing symlinks that would allow processes in the container to escape to the host filesystem. Nowadays Linux kernel provides the syscall openat2() which can effectively block symlink attacks. However, mount syscall only operates on paths, so we can get a file descriptor by openat2() and build the proc path "/proc/self/fd/xx" to do mounting. Fixes: CVE-2021-30465,CVE-2024-21626 --- ozonec/src/linux/mount.rs | 107 +++++++++++++++++--------------------- ozonec/src/utils/error.rs | 2 + ozonec/src/utils/mod.rs | 102 ++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 60 deletions(-) diff --git a/ozonec/src/linux/mount.rs b/ozonec/src/linux/mount.rs index 972be73ab..de9a1b12f 100644 --- a/ozonec/src/linux/mount.rs +++ b/ozonec/src/linux/mount.rs @@ -12,7 +12,7 @@ use std::{ collections::HashMap, - fs::{self, canonicalize, create_dir_all, read_to_string, File}, + fs::{self, canonicalize, create_dir_all, read_to_string}, path::{Path, PathBuf}, }; @@ -20,11 +20,12 @@ use anyhow::{anyhow, bail, Context, Result}; use nix::{ mount::MsFlags, sys::statfs::{statfs, CGROUP2_SUPER_MAGIC}, + unistd::close, }; +use procfs::process::{MountInfo, Process}; -use crate::utils::OzonecErr; +use crate::utils::{openat2_in_root, proc_fd_path, OzonecErr}; use oci_spec::runtime::Mount as OciMount; -use procfs::process::{MountInfo, Process}; enum CgroupType { CgroupV1, @@ -98,9 +99,8 @@ impl Mount { } fn do_one_mount(&self, mount: &OciMount, label: &Option) -> Result<()> { - let fs_type = mount.fs_type.as_deref(); - let (flag, mut data) = self.get_mount_flag_data(mount); - + let mut fs_type = mount.fs_type.as_deref(); + let (mut mnt_flags, mut data) = self.get_mount_flag_data(mount); if let Some(label) = label { if fs_type != Some("proc") && fs_type != Some("sysfs") { match data.is_empty() { @@ -110,16 +110,13 @@ impl Mount { } } - // If destination begins with "/", then ignore the first "/". - let binding = self.rootfs.join(&mount.destination[1..]); - let dest_path = Path::new(&binding); - let binding = &mount + let src_binding = mount .source .clone() .ok_or(anyhow!("Mount source not set"))?; - let source = Path::new(&binding); + let source = Path::new(&src_binding); let canonicalized; - let src_path = match fs_type { + let source = match fs_type { Some("bind") => { canonicalized = canonicalize(source) .with_context(|| format!("Failed to canonicalize {}", source.display()))?; @@ -127,54 +124,49 @@ impl Mount { } _ => source, }; + // Strip the first "/". + let target_binding = self.rootfs.join(&mount.destination[1..]); + let target = Path::new(&target_binding); match fs_type { Some("bind") => { - let dir = if src_path.is_file() { - dest_path.parent().ok_or(anyhow!( + let dir = if source.is_file() { + target.parent().ok_or(anyhow!( "Failed to get parent directory: {}", - dest_path.display() + target.display() ))? } else { - dest_path + target }; - create_dir_all(dir) .with_context(|| OzonecErr::CreateDir(dir.to_string_lossy().to_string()))?; - if src_path.is_file() && !dest_path.exists() { - File::create(dest_path) - .with_context(|| format!("Failed to create {}", dest_path.display()))?; - } - nix::mount::mount( - Some(src_path), - dest_path, - None::<&str>, - MsFlags::MS_BIND | MsFlags::MS_REC, - Some(data.as_str()), - ) - .with_context(|| OzonecErr::Mount(src_path.to_string_lossy().to_string()))?; + mnt_flags = MsFlags::MS_BIND | MsFlags::MS_REC; + fs_type = None::<&str>; } _ => { - create_dir_all(&dest_path).with_context(|| { - OzonecErr::CreateDir(dest_path.to_string_lossy().to_string()) - })?; // Sysfs doesn't support duplicate mounting to one directory. - if self.is_mounted_sysfs_dir(&dest_path.to_string_lossy().to_string()) { - nix::mount::umount(dest_path) - .with_context(|| format!("Failed to umount {}", dest_path.display()))?; + if self.is_mounted_sysfs_dir(&target.to_string_lossy().to_string()) { + nix::mount::umount(target) + .with_context(|| format!("Failed to umount {}", target.display()))?; } - nix::mount::mount( - Some(src_path), - dest_path, - fs_type, - flag, - Some(data.as_str()), - ) - .with_context(|| OzonecErr::Mount(src_path.to_string_lossy().to_string()))?; } } + let target_fd = openat2_in_root( + &Path::new(&self.rootfs), + &Path::new(&mount.destination[1..]), + !source.is_file(), + )?; + nix::mount::mount( + Some(source), + &proc_fd_path(target_fd), + fs_type, + mnt_flags, + Some(data.as_str()), + ) + .with_context(|| OzonecErr::Mount(source.to_string_lossy().to_string()))?; + close(target_fd).with_context(|| OzonecErr::CloseFd)?; Ok(()) } @@ -210,22 +202,22 @@ impl Mount { } fn do_cgroup_mount(&self, mount: &OciMount) -> Result<()> { - // If destination begins with "/", then ignore the first "/". - let binding = self.rootfs.join(&mount.destination[1..]); - let mut dest = Path::new(&binding); + // Strip the first "/". + let rel_target = Path::new(&mount.destination[1..]); + let target_fd = openat2_in_root(&Path::new(&self.rootfs), rel_target, true)?; nix::mount::mount( Some("tmpfs"), - dest, + &proc_fd_path(target_fd), Some("tmpfs"), MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV, None::<&str>, ) .with_context(|| OzonecErr::Mount(String::from("tmpfs")))?; + close(target_fd).with_context(|| OzonecErr::CloseFd)?; let process = Process::myself().with_context(|| OzonecErr::AccessProcSelf)?; let mnt_info: Vec = process.mountinfo().with_context(|| OzonecErr::GetMntInfo)?; - let proc_cgroups: HashMap = process .cgroups() .with_context(|| "Failed to get cgroups belong to")? @@ -239,12 +231,6 @@ impl Mount { .map(|m| m.mount_point) .collect(); for cg_path in host_cgroups { - let binding = self.rootfs.join( - cg_path - .strip_prefix("/") - .with_context(|| format!("Strip {} error", cg_path.display()))?, - ); - dest = Path::new(&binding); let cg = cg_path .file_name() .ok_or(anyhow!("Failed to get controller file"))? @@ -261,19 +247,20 @@ impl Mount { if let Some(src) = proc_cgroups.get(&proc_cg_key) { let source = cg_path.join(&src[1..]); - if !dest.exists() { - create_dir_all(dest).with_context(|| { - OzonecErr::CreateDir(dest.to_string_lossy().to_string()) - })?; - } + let rel_target = cg_path + .strip_prefix("/") + .with_context(|| format!("{} doesn't start with '/'", cg_path.display()))?; + let target_fd = openat2_in_root(&Path::new(&self.rootfs), rel_target, true)?; + nix::mount::mount( Some(&source), - dest, + &proc_fd_path(target_fd), Some("bind"), MsFlags::MS_BIND | MsFlags::MS_REC, None::<&str>, ) .with_context(|| OzonecErr::Mount(source.to_string_lossy().to_string()))?; + close(target_fd).with_context(|| OzonecErr::CloseFd)?; } } diff --git a/ozonec/src/utils/error.rs b/ozonec/src/utils/error.rs index 142ef9fb2..b3f93728d 100644 --- a/ozonec/src/utils/error.rs +++ b/ozonec/src/utils/error.rs @@ -44,4 +44,6 @@ pub enum OzonecErr { GetOciState, #[error("Failed to bind device: {0}")] BindDev(String), + #[error("Close fd error")] + CloseFd, } diff --git a/ozonec/src/utils/mod.rs b/ozonec/src/utils/mod.rs index 69b9517d7..59da672af 100644 --- a/ozonec/src/utils/mod.rs +++ b/ozonec/src/utils/mod.rs @@ -20,3 +20,105 @@ mod error; pub use channel::{Channel, Message}; pub use clone::Clone3; pub use error::OzonecErr; + +use std::{ + fs::create_dir_all, + mem, + os::unix::io::{AsRawFd, RawFd}, + path::{Path, PathBuf}, +}; + +use anyhow::{bail, Context, Result}; +use nix::{ + errno::errno, + fcntl::{open, OFlag}, + sys::stat::Mode, + NixPath, +}; + +struct OpenHow(libc::open_how); + +bitflags::bitflags! { + struct ResolveFlag: libc::c_ulonglong { + const RESOLVE_BENEATH = libc::RESOLVE_BENEATH; + const RESOLVE_IN_ROOT = libc::RESOLVE_IN_ROOT; + const RESOLVE_NO_MAGICLINKS = libc::RESOLVE_NO_MAGICLINKS; + const RESOLVE_NO_SYMLINKS = libc::RESOLVE_NO_SYMLINKS; + const RESOLVE_NO_XDEV = libc::RESOLVE_NO_XDEV; + } +} + +impl OpenHow { + fn new() -> Self { + // SAFETY: FFI call with valid arguments. + unsafe { mem::zeroed() } + } + + fn flags(mut self, flags: OFlag) -> Self { + let flags = flags.bits() as libc::c_ulonglong; + self.0.flags = flags; + self + } + + fn mode(mut self, mode: Mode) -> Self { + let mode = mode.bits() as libc::c_ulonglong; + self.0.mode = mode; + self + } + + fn resolve(mut self, resolve: ResolveFlag) -> Self { + let resolve = resolve.bits() as libc::c_ulonglong; + self.0.resolve = resolve; + self + } +} + +/// Get a file descriptor by openat2 with `root` path, relative `target` path in `root` +/// and whether is director or not. If the target directory or file doesn't exist, create +/// automatically. +pub fn openat2_in_root(root: &Path, target: &Path, is_dir: bool) -> Result { + let mut flags = OFlag::O_CLOEXEC; + let mode; + if is_dir { + flags |= OFlag::O_DIRECTORY | OFlag::O_PATH; + mode = Mode::empty(); + create_dir_all(root.join(target)) + .with_context(|| OzonecErr::CreateDir(target.to_string_lossy().to_string()))?; + } else { + flags |= OFlag::O_CREAT; + mode = Mode::S_IRWXU; + }; + + let mut open_how = OpenHow::new() + .flags(flags) + .mode(mode) + .resolve(ResolveFlag::RESOLVE_IN_ROOT); + let dirfd = open(root, flags & !OFlag::O_CREAT, Mode::empty()) + .with_context(|| OzonecErr::OpenFile(root.to_string_lossy().to_string()))?; + + // SAFETY: FFI call with valid arguments. + let fd = target + .with_nix_path(|p| unsafe { + libc::syscall( + libc::SYS_openat2, + dirfd.as_raw_fd(), + p.as_ptr(), + &mut open_how as *mut OpenHow, + mem::size_of::(), + ) + }) + .with_context(|| "with_nix_path error")?; + if fd < 0 { + bail!( + "openat2 {} error with RESOLVE_IN_ROOT: {}", + target.display(), + errno() + ); + } + Ok(RawFd::try_from(fd)?) +} + +/// Build path "/proc/self/fd/{}" with an opened file descriptor. +pub fn proc_fd_path(dirfd: RawFd) -> PathBuf { + PathBuf::from(format!("/proc/self/fd/{}", dirfd)) +} -- Gitee From 8da30ab18cedb1f4eb7ca130a6b18a0a652fc3d2 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Tue, 24 Sep 2024 15:10:46 +0800 Subject: [PATCH 2154/2187] ohcam:fix trace features Missing features are essential to trace ohcam, so add them. Signed-off-by: zhanghan --- Cargo.toml | 6 +++--- devices/Cargo.toml | 3 +++ machine/Cargo.toml | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bf79c900c..4c78415f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,9 +40,9 @@ vnc_auth = ["machine/vnc_auth"] ohui_srv = ["machine/ohui_srv"] ramfb = ["machine/ramfb"] virtio_gpu = ["machine/virtio_gpu"] -trace_to_logger = ["trace/trace_to_logger"] -trace_to_ftrace = ["trace/trace_to_ftrace"] -trace_to_hitrace = ["trace/trace_to_hitrace"] +trace_to_logger = ["trace/trace_to_logger", "machine/trace_to_logger"] +trace_to_ftrace = ["trace/trace_to_ftrace", "machine/trace_to_ftrace"] +trace_to_hitrace = ["trace/trace_to_hitrace", "machine/trace_to_hitrace"] hisysevent = ["hisysevent/hisysevent"] vfio = ["machine/vfio_device"] usb_uas = ["machine/usb_uas"] diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 932a0cdd1..3dca2b418 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -54,3 +54,6 @@ usb_camera_v4l2 = ["usb_camera", "dep:v4l2-sys-mit", "machine_manager/usb_camera usb_camera_oh = ["usb_camera", "machine_manager/usb_camera_oh", "util/usb_camera_oh"] ramfb = ["ui/console", "util/pixman"] usb_uas = [] +trace_to_logger = [] +trace_to_ftrace = [] +trace_to_hitrace = [] diff --git a/machine/Cargo.toml b/machine/Cargo.toml index 2c8a22b67..ecead47e1 100644 --- a/machine/Cargo.toml +++ b/machine/Cargo.toml @@ -58,3 +58,6 @@ vhost_vsock = ["virtio/vhost_vsock"] vhostuser_block = ["virtio/vhostuser_block"] vhostuser_net = ["virtio/vhostuser_net"] vhost_net = ["virtio/vhost_net"] +trace_to_logger = ["devices/trace_to_logger"] +trace_to_ftrace = ["devices/trace_to_ftrace"] +trace_to_hitrace = ["devices/trace_to_hitrace"] -- Gitee From 2f234ec88cb31d085d0a8067fdb3f4d1d4135325 Mon Sep 17 00:00:00 2001 From: Zhao Yi Min Date: Thu, 26 Sep 2024 17:03:23 +0800 Subject: [PATCH 2155/2187] scream: optimize capture logic This patch optimizes scream capture logic and fixes up some issues: 1. Simplify the logic of audio data transfer. 2. Use condvar to wait for audio data and the max timeout is 200ms. If timed out, reinit capture context. 3. This can fixup the issue that audio capture hangs up when the users did the host system suspend and resume. 4. This can fixup the issue that capture thread enters into infinite loop while waiting for audio data but the audio framework has error occurred. Signed-off-by: Zhao Yi Min --- devices/src/misc/scream/mod.rs | 4 - devices/src/misc/scream/ohaudio.rs | 146 +++++++++++++++-------------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/devices/src/misc/scream/mod.rs b/devices/src/misc/scream/mod.rs index 5272553ae..328bef208 100644 --- a/devices/src/misc/scream/mod.rs +++ b/devices/src/misc/scream/mod.rs @@ -436,7 +436,6 @@ impl StreamData { let header = &mut unsafe { std::slice::from_raw_parts_mut(hva as *mut ShmemHeader, 1) }[0]; let capt = &mut header.capt; - interface.lock().unwrap().pre_receive(self.start_addr, capt); while capt.is_started != 0 { cond.wait_if_paused(interface.clone()); @@ -766,9 +765,6 @@ impl Scream { pub trait AudioInterface: Send { fn send(&mut self, recv_data: &StreamData); - // For OHOS's audio task. It confirms shmem info. - #[allow(unused_variables)] - fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) {} fn receive(&mut self, recv_data: &StreamData) -> i32; fn destroy(&mut self); fn get_status(&self) -> AudioStatus; diff --git a/devices/src/misc/scream/ohaudio.rs b/devices/src/misc/scream/ohaudio.rs index 8040ea887..d011c04df 100755 --- a/devices/src/misc/scream/ohaudio.rs +++ b/devices/src/misc/scream/ohaudio.rs @@ -13,16 +13,16 @@ use std::collections::VecDeque; use std::os::raw::c_void; use std::sync::{ - atomic::{fence, AtomicBool, AtomicI32, Ordering}, - Arc, Mutex, RwLock, + atomic::{fence, AtomicBool, Ordering}, + Arc, Condvar, Mutex, RwLock, }; use std::{cmp, io::Read, ptr, thread, time::Duration}; -use log::error; +use log::{error, warn}; use crate::misc::ivshmem::Ivshmem; use crate::misc::scream::{ - AudioExtension, AudioInterface, AudioStatus, ScreamDirection, ShmemStreamHeader, StreamData, + AudioExtension, AudioInterface, AudioStatus, ScreamDirection, StreamData, IVSHMEM_VOLUME_SYNC_VECTOR, }; use util::ohos_binding::audio::*; @@ -31,11 +31,11 @@ const STREAM_DATA_VEC_CAPACITY: usize = 15; const FLUSH_DELAY_MS: u64 = 5; const FLUSH_DELAY_CNT: u64 = 200; const SCREAM_MAX_VOLUME: u32 = 110; +const CAPTURE_WAIT_TIMEOUT: u64 = 200; trait OhAudioProcess { fn init(&mut self, stream: &StreamData) -> bool; fn destroy(&mut self); - fn preprocess(&mut self, _start_addr: u64, _sh_header: &ShmemStreamHeader) {} fn process(&mut self, recv_data: &StreamData) -> i32; fn get_status(&self) -> AudioStatus; } @@ -293,15 +293,63 @@ impl OhAudioProcess for OhAudioRender { } } +struct CaptureStream { + cond: Condvar, + data: Mutex>, + expected: usize, +} + +impl Default for CaptureStream { + fn default() -> Self { + Self { + cond: Condvar::new(), + data: Mutex::new(Vec::with_capacity(1 << 20)), + expected: 0, + } + } +} + +impl CaptureStream { + fn wait_for_data(&mut self, buf: &mut [u8]) -> bool { + let mut locked_data = self.data.lock().unwrap(); + self.expected = buf.len(); + while locked_data.len() < self.expected { + let ret = self + .cond + .wait_timeout(locked_data, Duration::from_millis(CAPTURE_WAIT_TIMEOUT)) + .unwrap(); + if ret.1.timed_out() { + return false; + } + locked_data = ret.0; + } + buf.copy_from_slice(&locked_data[..self.expected]); + *locked_data = locked_data[self.expected..].to_vec(); + self.expected = 0; + true + } + + fn append_data(&mut self, buf: &[u8]) { + let mut locked_data = self.data.lock().unwrap(); + locked_data.extend_from_slice(buf); + if locked_data.len() > self.expected { + self.cond.notify_all(); + } + } + + fn reset(&mut self) { + let mut locked_data = self.data.lock().unwrap(); + locked_data.clear(); + self.expected = 0; + self.cond.notify_all(); + } +} + #[derive(Default)] struct OhAudioCapture { ctx: Option, - align: u32, - new_chunks: AtomicI32, - shm_addr: u64, - shm_len: u64, - cur_pos: u64, status: AudioStatus, + stream: CaptureStream, } impl OhAudioCapture { @@ -350,18 +398,10 @@ impl OhAudioProcess for OhAudioCapture { fn destroy(&mut self) { self.status = AudioStatus::Ready; self.ctx = None; + self.stream.reset(); trace::oh_scream_capture_destroy(); } - fn preprocess(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) { - self.align = sh_header.chunk_size; - self.new_chunks.store(0, Ordering::Release); - self.shm_addr = start_addr; - self.shm_len = u64::from(sh_header.max_chunks) * u64::from(sh_header.chunk_size); - self.cur_pos = - start_addr + u64::from(sh_header.chunk_idx) * u64::from(sh_header.chunk_size); - } - fn process(&mut self, recv_data: &StreamData) -> i32 { self.check_fmt_update(recv_data); @@ -375,15 +415,19 @@ impl OhAudioProcess for OhAudioCapture { self.destroy(); return -1; } - self.new_chunks.store(0, Ordering::Release); - while self.new_chunks.load(Ordering::Acquire) == 0 { - if self.status == AudioStatus::Error { - return -1; - } - thread::sleep(Duration::from_millis(10)); + // SAFETY: the buffer is from ivshmem and the caller ensures its validation. + let buf = unsafe { + std::slice::from_raw_parts_mut( + recv_data.audio_base as *mut u8, + recv_data.audio_size as usize, + ) + }; + if !self.stream.wait_for_data(buf) { + warn!("timed out to wait for capture audio data"); + self.status = AudioStatus::Error; + return 0; } - - self.new_chunks.load(Ordering::Acquire) + 1 } fn get_status(&self) -> AudioStatus { @@ -446,49 +490,13 @@ extern "C" fn on_read_data_cb( trace::trace_scope_start!(ohaudio_read_cb, args = (length)); - loop { - if capture.status != AudioStatus::Started { - return 0; - } - if capture.new_chunks.load(Ordering::Acquire) == 0 { - break; - } - } - if capture.align == 0 { - error!("on_read_data_cb, capture.align is 0"); + if capture.status != AudioStatus::Started { return 0; } - let old_pos = - capture.cur_pos - ((capture.cur_pos - capture.shm_addr) % u64::from(capture.align)); - let buf_end = capture.shm_addr + capture.shm_len; - let mut src_addr = buffer as u64; - let mut left = length as u64; - while left > 0 { - let len = cmp::min(left, buf_end - capture.cur_pos); - // SAFETY: we checked len. - unsafe { - ptr::copy_nonoverlapping( - src_addr as *const u8, - capture.cur_pos as *mut u8, - len as usize, - ) - }; - trace::oh_scream_on_read_data_cb(len as usize); - left -= len; - src_addr += len; - capture.cur_pos += len; - if capture.cur_pos == buf_end { - capture.cur_pos = capture.shm_addr; - } - } - let new_chunks = match capture.cur_pos <= old_pos { - true => (capture.shm_len - (old_pos - capture.cur_pos)) / u64::from(capture.align), - false => (capture.cur_pos - old_pos) / u64::from(capture.align), - }; - capture - .new_chunks - .store(new_chunks as i32, Ordering::Release); + // SAFETY: the buffer is checked above. + let buf = unsafe { std::slice::from_raw_parts(buffer as *mut u8, length as usize) }; + capture.stream.append_data(buf); 0 } @@ -517,10 +525,6 @@ impl AudioInterface for OhAudio { self.processor.process(recv_data); } - fn pre_receive(&mut self, start_addr: u64, sh_header: &ShmemStreamHeader) { - self.processor.preprocess(start_addr, sh_header); - } - fn receive(&mut self, recv_data: &StreamData) -> i32 { self.processor.process(recv_data) } -- Gitee From 47a4d1134cf4ffdae94e00c42779ad6d3bc40555 Mon Sep 17 00:00:00 2001 From: sujerry1991 Date: Thu, 10 Oct 2024 16:16:01 +0800 Subject: [PATCH 2156/2187] util: do not handle DISCARD in thread pool In qcow2, discard operation(fallocate) should not be execute in thread pool which will cause the following error: 1) discard operation release the qcow2 cluster A. 2) discard operation execute fallocate in the thread pool. 3) write operation alloc cluster A and write data to it. 4) fallocate in the thread pool clear the data in cluster A. The step 1)/2)/4) should be handle in one thread. So, handle discard operation synchronously. Signed-off-by: Yan Wang --- util/src/aio/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index ac10fa16e..4422bc7fc 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -564,7 +564,14 @@ impl Aio { pub fn submit_request(&mut self, mut cb: AioCb) -> Result<()> { trace::aio_submit_request(cb.file_fd, &cb.opcode, cb.offset, cb.nbytes); - if self.ctx.is_none() { + if self.ctx.is_none() + || [ + OpCode::Discard, + OpCode::WriteZeroes, + OpCode::WriteZeroesUnmap, + ] + .contains(&cb.opcode) + { return self.handle_sync_request(cb); } -- Gitee From 37a4aada908ab8137b33cda33d433e1fa281e821 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 00:06:01 +0800 Subject: [PATCH 2157/2187] ozonec/mount: Create a bind mount when bind option is set There is no filesystem called bind. Actually when bind option is set in mount options, i.e. MS_BIND is set, filesystem is ignored by mount systemcall. --- ozonec/src/linux/mount.rs | 55 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/ozonec/src/linux/mount.rs b/ozonec/src/linux/mount.rs index de9a1b12f..55ab45366 100644 --- a/ozonec/src/linux/mount.rs +++ b/ozonec/src/linux/mount.rs @@ -100,7 +100,7 @@ impl Mount { fn do_one_mount(&self, mount: &OciMount, label: &Option) -> Result<()> { let mut fs_type = mount.fs_type.as_deref(); - let (mut mnt_flags, mut data) = self.get_mount_flag_data(mount); + let (mnt_flags, mut data) = self.get_mount_flag_data(mount); if let Some(label) = label { if fs_type != Some("proc") && fs_type != Some("sysfs") { match data.is_empty() { @@ -114,42 +114,33 @@ impl Mount { .source .clone() .ok_or(anyhow!("Mount source not set"))?; - let source = Path::new(&src_binding); + let mut source = Path::new(&src_binding); let canonicalized; - let source = match fs_type { - Some("bind") => { - canonicalized = canonicalize(source) - .with_context(|| format!("Failed to canonicalize {}", source.display()))?; - canonicalized.as_path() - } - _ => source, - }; // Strip the first "/". let target_binding = self.rootfs.join(&mount.destination[1..]); let target = Path::new(&target_binding); - match fs_type { - Some("bind") => { - let dir = if source.is_file() { - target.parent().ok_or(anyhow!( - "Failed to get parent directory: {}", - target.display() - ))? - } else { - target - }; - create_dir_all(dir) - .with_context(|| OzonecErr::CreateDir(dir.to_string_lossy().to_string()))?; - - mnt_flags = MsFlags::MS_BIND | MsFlags::MS_REC; - fs_type = None::<&str>; - } - _ => { - // Sysfs doesn't support duplicate mounting to one directory. - if self.is_mounted_sysfs_dir(&target.to_string_lossy().to_string()) { - nix::mount::umount(target) - .with_context(|| format!("Failed to umount {}", target.display()))?; - } + if !(mnt_flags & MsFlags::MS_BIND).is_empty() { + canonicalized = canonicalize(source) + .with_context(|| format!("Failed to canonicalize {}", source.display()))?; + source = canonicalized.as_path(); + let dir = if source.is_file() { + target.parent().ok_or(anyhow!( + "Failed to get parent directory: {}", + target.display() + ))? + } else { + target + }; + create_dir_all(dir) + .with_context(|| OzonecErr::CreateDir(dir.to_string_lossy().to_string()))?; + // Actually when MS_BIND is set, filesystemtype is ignored by mount syscall. + fs_type = Some("bind"); + } else { + // Sysfs doesn't support duplicate mounting to one directory. + if self.is_mounted_sysfs_dir(&target.to_string_lossy().to_string()) { + nix::mount::umount(target) + .with_context(|| format!("Failed to umount {}", target.display()))?; } } -- Gitee From b6e176f07b80d23c929026f051f6905cad5c51ee Mon Sep 17 00:00:00 2001 From: Li Huachao Date: Fri, 11 Oct 2024 17:38:28 +0800 Subject: [PATCH 2158/2187] Pflash: fix unchecked add Signed-off-by: Li Huachao --- devices/src/legacy/pflash.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/devices/src/legacy/pflash.rs b/devices/src/legacy/pflash.rs index 209c9ecdc..5ea22a3ba 100644 --- a/devices/src/legacy/pflash.rs +++ b/devices/src/legacy/pflash.rs @@ -329,7 +329,11 @@ impl PFlash { } // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + u64::from(size) > mr.size() { + if offset + .checked_add(size as u64) + .map(|sum| sum > mr.size()) + .unwrap_or(true) + { return Err(anyhow!(LegacyError::PFlashWriteOverflow( mr.size(), offset, @@ -361,7 +365,11 @@ impl PFlash { fn read_data(&mut self, data: &mut [u8], offset: u64) -> Result<()> { // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + data.len() as u64 > mr.size() { + if offset + .checked_add(data.len() as u64) + .map(|sum| sum > mr.size()) + .unwrap_or(true) + { return Err(anyhow!(LegacyError::PFlashReadOverflow( mr.size(), offset, @@ -390,7 +398,11 @@ impl PFlash { ); // Unwrap is safe, because after realize function, rom isn't none. let mr = self.rom.as_ref().unwrap(); - if offset + data.len() as u64 > mr.size() { + if offset + .checked_add(data.len() as u64) + .map(|sum| sum > mr.size()) + .unwrap_or(true) + { return Err(anyhow!(LegacyError::PFlashWriteOverflow( mr.size(), offset, -- Gitee From 21d9c1f5aa3c99f7663e6c64d53fca04109eecb4 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 02:29:42 +0800 Subject: [PATCH 2159/2187] ozonec/seccomp: Add support for SCMP_CMP_MASKED_EQ valueTwo is set to 0 as default to be masked datum value only when op is SCMP_CMP_MASKED_EQ. --- ozonec/src/linux/seccomp.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ozonec/src/linux/seccomp.rs b/ozonec/src/linux/seccomp.rs index e95866101..36903d43e 100644 --- a/ozonec/src/linux/seccomp.rs +++ b/ozonec/src/linux/seccomp.rs @@ -35,7 +35,7 @@ fn parse_action(action: OciSeccompAction, errno: Option) -> ScmpAction { } } -fn parse_cmp(op: SeccompOp) -> ScmpCompareOp { +fn parse_cmp(op: SeccompOp, mask: u64) -> ScmpCompareOp { match op { SeccompOp::ScmpCmpNe => ScmpCompareOp::NotEqual, SeccompOp::ScmpCmpLt => ScmpCompareOp::Less, @@ -43,7 +43,7 @@ fn parse_cmp(op: SeccompOp) -> ScmpCompareOp { SeccompOp::ScmpCmpEq => ScmpCompareOp::Equal, SeccompOp::ScmpCmpGe => ScmpCompareOp::GreaterEqual, SeccompOp::ScmpCmpGt => ScmpCompareOp::Greater, - _ => ScmpCompareOp::Equal, + SeccompOp::ScmpCmpMaskedEq => ScmpCompareOp::MaskedEqual(mask), } } @@ -97,8 +97,13 @@ pub fn set_seccomp(seccomp: &Seccomp) -> Result<()> { let mut comparators: Vec = vec![]; if let Some(args) = &syscall.args { for arg in args { - let op = parse_cmp(arg.op); - let cmp = ScmpArgCompare::new(arg.index as u32, op, arg.value); + let op = parse_cmp(arg.op, arg.value); + let cmp = match arg.op { + SeccompOp::ScmpCmpMaskedEq => { + ScmpArgCompare::new(arg.index as u32, op, arg.valueTwo.unwrap_or(0)) + } + _ => ScmpArgCompare::new(arg.index as u32, op, arg.value), + }; comparators.push(cmp); } } -- Gitee From 412f6fbc7f634538fd32200ad2ffa9c1524fa69f Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Mon, 14 Oct 2024 15:13:07 +0300 Subject: [PATCH 2160/2187] list: Implement len, is_empty, iter and iter_mut for List Implement useful methods for List and change existing code to use them. This ensures encapsulation and makes code much more readable. Signed-off-by: goriainovstanislav --- devices/src/usb/usbhost/mod.rs | 10 ++-- machine/src/standard_common/mod.rs | 2 +- tests/mod_test/tests/net_test.rs | 2 +- util/src/aio/mod.rs | 6 +-- util/src/link_list.rs | 76 +++++++++++++++++++++++++++++- util/src/thread_pool.rs | 8 ++-- vfio/src/vfio_pci.rs | 2 +- 7 files changed, 88 insertions(+), 18 deletions(-) diff --git a/devices/src/usb/usbhost/mod.rs b/devices/src/usb/usbhost/mod.rs index bc79fd38f..17a888a56 100644 --- a/devices/src/usb/usbhost/mod.rs +++ b/devices/src/usb/usbhost/mod.rs @@ -843,18 +843,14 @@ impl UsbHost { } pub fn abort_host_transfers(&mut self) -> Result<()> { - let mut locked_requests = self.requests.lock().unwrap(); - for _i in 0..locked_requests.len { - let mut node = locked_requests.pop_head().unwrap(); - node.value.abort_req(); - locked_requests.add_tail(node); + for req in self.requests.lock().unwrap().iter_mut() { + req.abort_req(); } - drop(locked_requests); // Max counts of uncompleted request to be handled. let mut limit: i32 = 100; loop { - if self.requests.lock().unwrap().len == 0 { + if self.requests.lock().unwrap().is_empty() { return Ok(()); } let timeout = Some(Duration::from_millis(HANDLE_TIMEOUT_MS)); diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index b493c6f49..ee6b670e8 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1878,7 +1878,7 @@ impl DeviceInterface for StdMachine { } let qcow2_list = QCOW2_LIST.lock().unwrap(); - if qcow2_list.len() == 0 { + if qcow2_list.is_empty() { return Response::create_response( serde_json::to_value("There is no snapshot available.\r\n").unwrap(), None, diff --git a/tests/mod_test/tests/net_test.rs b/tests/mod_test/tests/net_test.rs index 19fba808e..34ffe1d36 100644 --- a/tests/mod_test/tests/net_test.rs +++ b/tests/mod_test/tests/net_test.rs @@ -332,7 +332,7 @@ impl ByteCode for VirtioNetHdr {} fn execute_cmd(cmd: String, check: bool) { let args = cmd.split(' ').collect::>(); - if args.len() <= 0 { + if args.is_empty() { return; } diff --git a/util/src/aio/mod.rs b/util/src/aio/mod.rs index 4422bc7fc..100dd0180 100644 --- a/util/src/aio/mod.rs +++ b/util/src/aio/mod.rs @@ -663,10 +663,10 @@ impl Aio { warn!("Can not process aio list with invalid ctx."); return Ok(()); } - while self.aio_in_queue.len > 0 && self.aio_in_flight.len < self.max_events { + while !self.aio_in_queue.is_empty() && self.aio_in_flight.len() < self.max_events { let mut iocbs = Vec::new(); - for _ in self.aio_in_flight.len..self.max_events { + for _ in self.aio_in_flight.len()..self.max_events { match self.aio_in_queue.pop_tail() { Some(node) => { iocbs.push(&node.value as *const AioCb); @@ -729,7 +729,7 @@ impl Aio { self.aio_in_queue.add_head(node); self.incomplete_cnt.fetch_add(1, Ordering::SeqCst); - if self.aio_in_queue.len + self.aio_in_flight.len >= self.max_events { + if self.aio_in_queue.len() + self.aio_in_flight.len() >= self.max_events { self.process_list()?; } diff --git a/util/src/link_list.rs b/util/src/link_list.rs index 264b5d6fd..a779bce17 100644 --- a/util/src/link_list.rs +++ b/util/src/link_list.rs @@ -23,7 +23,7 @@ pub struct Node { pub struct List { head: Option>>, tail: Option>>, - pub len: usize, + len: usize, marker: PhantomData>>, } @@ -139,4 +139,78 @@ impl List { node }) } + + #[inline(always)] + pub fn len(&self) -> usize { + self.len + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline(always)] + pub fn iter(&'_ self) -> impl Iterator { + Iter::new(self) + } + + #[inline(always)] + pub fn iter_mut(&'_ mut self) -> impl Iterator { + IterMut::new(self) + } +} + +struct Iter<'a, T> { + curr: Option>>, + list: PhantomData<&'a List>, +} + +impl<'a, T> Iter<'a, T> { + fn new(list: &'a List) -> Self { + Self { + curr: list.head, + list: PhantomData, + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.curr.map(|node| { + // SAFETY: node is guaranteed not to be null. + let node = unsafe { node.as_ref() }; + self.curr = node.next; + &node.value + }) + } +} + +struct IterMut<'a, T> { + curr: Option>>, + list: PhantomData<&'a mut List>, +} + +impl<'a, T> IterMut<'a, T> { + fn new(list: &'a mut List) -> Self { + Self { + curr: list.head, + list: PhantomData, + } + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + self.curr.map(|mut node| { + // SAFETY: node is guaranteed not to be null. + let node = unsafe { node.as_mut() }; + self.curr = node.next; + &mut node.value + }) + } } diff --git a/util/src/thread_pool.rs b/util/src/thread_pool.rs index 570d42561..63c8891d9 100644 --- a/util/src/thread_pool.rs +++ b/util/src/thread_pool.rs @@ -167,7 +167,7 @@ fn worker_thread(pool: Arc) { while locked_state.is_running() { let result; - if locked_state.req_lists.len == 0 { + if locked_state.req_lists.is_empty() { locked_state.blocked_threads += 1; match pool .request_cond @@ -186,7 +186,7 @@ fn worker_thread(pool: Arc) { locked_state.blocked_threads -= 1; if result.timed_out() - && locked_state.req_lists.len == 0 + && locked_state.req_lists.is_empty() && locked_state.total_threads > locked_state.min_threads { // If wait time_out and no pending task and current total number @@ -205,7 +205,7 @@ fn worker_thread(pool: Arc) { locked_state = pool.pool_state.lock().unwrap(); } locked_state.total_threads -= 1; - trace::thread_pool_exit_thread(&locked_state.total_threads, &locked_state.req_lists.len); + trace::thread_pool_exit_thread(&locked_state.total_threads, &locked_state.req_lists.len()); pool.stop_cond.notify_one(); pool.request_cond.notify_one(); @@ -243,7 +243,7 @@ mod test { } // Waiting for creating. - while pool.pool_state.lock().unwrap().req_lists.len != 0 { + while !pool.pool_state.lock().unwrap().req_lists.is_empty() { thread::sleep(time::Duration::from_millis(10)); let now = time::SystemTime::now(); diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index aea7891c7..d4c0dfee4 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -723,7 +723,7 @@ impl VfioPciDevice { fn vfio_enable_msix(&mut self) -> Result<()> { let mut gsi_routes = self.gsi_msi_routes.lock().unwrap(); - if gsi_routes.len() == 0 { + if gsi_routes.is_empty() { let irq_fd = create_new_eventfd().unwrap(); let gsi_route = GsiMsiRoute { irq_fd: Some(Arc::new(irq_fd)), -- Gitee From b4a9d14c79be904c85f8ca77ca71fde2203455f9 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Mon, 7 Oct 2024 16:47:17 +0300 Subject: [PATCH 2161/2187] thread_pool: Change ThreadPool to use std::LinkedList Use standard linked list implementation in thread pool because it doesn't require extra Box wrapping and provides more features. Signed-off-by: goriainovstanislav --- util/src/thread_pool.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/util/src/thread_pool.rs b/util/src/thread_pool.rs index 63c8891d9..48ac9af95 100644 --- a/util/src/thread_pool.rs +++ b/util/src/thread_pool.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::collections::LinkedList; use std::sync::{Arc, Condvar, Mutex}; use std::thread; use std::time::Duration; @@ -17,8 +18,6 @@ use std::time::Duration; use anyhow::{bail, Context, Result}; use log::error; -use crate::link_list::{List, Node}; - const MIN_THREADS: u64 = 1; const MAX_THREADS: u64 = 10; type PoolTask = Box; @@ -45,7 +44,7 @@ struct PoolState { /// The maximum number of threads that thread pool can create. max_threads: u64, /// List of pending tasks in the thread pool. - req_lists: List, + req_lists: LinkedList, } /// SAFETY: All the operations on req_lists are protected by the mutex, @@ -61,7 +60,7 @@ impl PoolState { pending_threads: 0, min_threads: MIN_THREADS, max_threads: MAX_THREADS, - req_lists: List::new(), + req_lists: LinkedList::new(), } } @@ -131,7 +130,7 @@ impl ThreadPool { if locked_state.spawn_thread_needed() { locked_state.spawn_thread(pool.clone())? } - locked_state.req_lists.add_tail(Box::new(Node::new(task))); + locked_state.req_lists.push_back(task); drop(locked_state); pool.request_cond.notify_one(); @@ -197,10 +196,10 @@ fn worker_thread(pool: Arc) { continue; } - let mut req = locked_state.req_lists.pop_head().unwrap(); + let mut req = locked_state.req_lists.pop_front().unwrap(); drop(locked_state); - (*req.value).run(); + req.run(); locked_state = pool.pool_state.lock().unwrap(); } -- Gitee From bddea9de69d79bd7babb3a7527e0d40a7479f2f2 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Thu, 18 Apr 2024 18:47:08 +0300 Subject: [PATCH 2162/2187] usb: Implement Debug for UsbDeviceRequest Make debug output for usb device request more comprehensible. Signed-off-by: goriainovstanislav --- devices/src/usb/config.rs | 2 ++ devices/src/usb/mod.rs | 62 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 1b34345f3..776efb95c 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -155,6 +155,8 @@ pub const USB_RECIPIENT_DEVICE: u8 = 0; pub const USB_RECIPIENT_INTERFACE: u8 = 1; pub const USB_RECIPIENT_ENDPOINT: u8 = 2; pub const USB_RECIPIENT_OTHER: u8 = 3; +pub const USB_TYPE_MASK: u8 = 3 << 5; +pub const USB_RECIPIENT_MASK: u8 = 0x1F; /// USB device request combination pub const USB_DEVICE_IN_REQUEST: u8 = diff --git a/devices/src/usb/mod.rs b/devices/src/usb/mod.rs index ff4927f99..110152b84 100644 --- a/devices/src/usb/mod.rs +++ b/devices/src/usb/mod.rs @@ -65,7 +65,7 @@ pub enum UsbPacketStatus { /// USB request used to transfer to USB device. #[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[derive(Copy, Clone, PartialEq, Eq, Default)] pub struct UsbDeviceRequest { pub request_type: u8, pub request: u8, @@ -76,6 +76,66 @@ pub struct UsbDeviceRequest { impl ByteCode for UsbDeviceRequest {} +impl std::fmt::Debug for UsbDeviceRequest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UsbDeviceRequest") + .field("request_type", &parse_request_type(self.request_type)) + .field("request", &parse_request(self.request)) + .field("value", &self.value) + .field("index", &self.index) + .field("length", &self.length) + .finish() + } +} + +fn parse_request_type(request_type: u8) -> String { + let mut ret = "".to_string(); + + match request_type & USB_DIRECTION_DEVICE_TO_HOST { + USB_DIRECTION_DEVICE_TO_HOST => ret.push_str("IN"), + _ => ret.push_str("OUT"), + } + + ret.push(' '); + + match request_type & USB_TYPE_MASK { + USB_TYPE_STANDARD => ret.push_str("STD"), + USB_TYPE_CLASS => ret.push_str("CLASS"), + USB_TYPE_VENDOR => ret.push_str("VEND"), + _ => ret.push_str("RSVD"), + } + + ret.push(' '); + + match request_type & USB_RECIPIENT_MASK { + USB_RECIPIENT_DEVICE => ret.push_str("DEV"), + USB_RECIPIENT_INTERFACE => ret.push_str("IFACE"), + USB_RECIPIENT_ENDPOINT => ret.push_str("EP"), + _ => ret.push_str("OTHER"), + } + + ret +} + +fn parse_request(request: u8) -> String { + match request { + USB_REQUEST_GET_STATUS => "GET STAT".to_string(), + USB_REQUEST_CLEAR_FEATURE => "CLR FEAT".to_string(), + USB_REQUEST_SET_FEATURE => "SET FEAT".to_string(), + USB_REQUEST_SET_ADDRESS => "SET ADDR".to_string(), + USB_REQUEST_GET_DESCRIPTOR => "GET DESC".to_string(), + USB_REQUEST_SET_DESCRIPTOR => "SET DESC".to_string(), + USB_REQUEST_GET_CONFIGURATION => "GET CONF".to_string(), + USB_REQUEST_SET_CONFIGURATION => "SET CONF".to_string(), + USB_REQUEST_GET_INTERFACE => "GET IFACE".to_string(), + USB_REQUEST_SET_INTERFACE => "SET IFACE".to_string(), + USB_REQUEST_SYNCH_FRAME => "SYN FRAME".to_string(), + USB_REQUEST_SET_SEL => "SET SEL".to_string(), + USB_REQUEST_SET_ISOCH_DELAY => "SET ISO DEL".to_string(), + _ => format!("UNKNOWN {}", request), + } +} + /// The data transmission channel. #[derive(Default, Clone, Copy)] pub struct UsbEndpoint { -- Gitee From e9546e04c81fff4019c8a9f47f3a67e1d97b793b Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 23 Apr 2024 11:58:08 +0300 Subject: [PATCH 2163/2187] usb: Support arbitrary capability descriptors for super speed devices Now super speed USB devices can set custom capability descriptors via set_capability_descriptors. Signed-off-by: goriainovstanislav --- devices/src/usb/descriptor.rs | 44 ++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/devices/src/usb/descriptor.rs b/devices/src/usb/descriptor.rs index 956dc3e94..55858cd4d 100644 --- a/devices/src/usb/descriptor.rs +++ b/devices/src/usb/descriptor.rs @@ -132,7 +132,7 @@ impl ByteCode for UsbBOSDescriptor {} #[allow(non_snake_case)] #[repr(C, packed)] #[derive(Copy, Clone, Debug, Default)] -struct UsbSuperSpeedCapDescriptor { +pub struct UsbSuperSpeedCapDescriptor { pub bLength: u8, pub bDescriptorType: u8, pub bDevCapabilityType: u8, @@ -221,6 +221,7 @@ pub struct UsbDescriptor { pub altsetting: Vec, pub interface_number: u32, pub strings: Vec, + pub capabilities: Vec, } impl UsbDescriptor { @@ -232,6 +233,7 @@ impl UsbDescriptor { altsetting: vec![0; USB_MAX_INTERFACES as usize], interface_number: 0, strings: Vec::new(), + capabilities: Vec::new(), } } @@ -374,20 +376,27 @@ impl UsbDescriptor { let mut cap_num = 0; if speed == USB_SPEED_SUPER { - let super_cap = UsbSuperSpeedCapDescriptor { - bLength: USB_DT_SS_CAP_SIZE, - bDescriptorType: USB_DT_DEVICE_CAPABILITY, - bDevCapabilityType: USB_SS_DEVICE_CAP, - bmAttributes: 0, - wSpeedsSupported: USB_SS_DEVICE_SPEED_SUPPORTED_SUPER, - bFunctionalitySupport: USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_SUPER, - bU1DevExitLat: 0xa, - wU2DevExitLat: 0x20, + let default_cap = if self.capabilities.is_empty() { + vec![UsbSuperSpeedCapDescriptor { + bLength: USB_DT_SS_CAP_SIZE, + bDescriptorType: USB_DT_DEVICE_CAPABILITY, + bDevCapabilityType: USB_SS_DEVICE_CAP, + bmAttributes: 0, + wSpeedsSupported: USB_SS_DEVICE_SPEED_SUPPORTED_SUPER, + bFunctionalitySupport: USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_SUPER, + bU1DevExitLat: 0xa, + wU2DevExitLat: 0x20, + }] + } else { + Vec::new() }; - let mut super_buf = super_cap.as_bytes().to_vec(); - cap_num += 1; - total += super_buf.len() as u16; - cap.append(&mut super_buf); + + for desc in default_cap.iter().chain(self.capabilities.iter()) { + let mut super_buf = (*desc).as_bytes().to_vec(); + cap_num += 1; + total += super_buf.len() as u16; + cap.append(&mut super_buf); + } } let bos = UsbBOSDescriptor { @@ -443,6 +452,9 @@ pub trait UsbDescriptorOps { /// Set interface descriptor with the Interface and Alternate Setting. fn set_interface_descriptor(&mut self, index: u32, v: u32) -> Result<()>; + /// Set super speed capability descriptors. + fn set_capability_descriptors(&mut self, caps: Vec); + /// Init all endpoint descriptors and reset the USB endpoint. fn init_endpoint(&mut self) -> Result<()>; @@ -517,6 +529,10 @@ impl UsbDescriptorOps for UsbDeviceBase { Ok(()) } + fn set_capability_descriptors(&mut self, caps: Vec) { + self.descriptor.capabilities = caps; + } + fn init_endpoint(&mut self) -> Result<()> { self.reset_usb_endpoint(); for i in 0..self.descriptor.interface_number { -- Gitee From 6f1236a9539ba3d03a3ce02e555c22c808883ea2 Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 23 Apr 2024 11:53:06 +0300 Subject: [PATCH 2164/2187] uas: Support real BOT interface for UAS alternate setting Replace fake BOT interface hack with a real BOT interface as UAS alternate setting. Usb-uas device now supports both protocols. Signed-off-by: goriainovstanislav --- devices/src/usb/config.rs | 2 + devices/src/usb/storage.rs | 75 ++++++++-------- devices/src/usb/uas.rs | 172 ++++++++++++++++++++++++++++++------- machine/src/lib.rs | 2 +- 4 files changed, 185 insertions(+), 66 deletions(-) diff --git a/devices/src/usb/config.rs b/devices/src/usb/config.rs index 776efb95c..aef89932a 100644 --- a/devices/src/usb/config.rs +++ b/devices/src/usb/config.rs @@ -213,7 +213,9 @@ pub const USB_DT_ENDPOINT_COMPANION: u8 = 48; /// USB SuperSpeed Device Capability. pub const USB_SS_DEVICE_CAP: u8 = 0x3; +pub const USB_SS_DEVICE_SPEED_SUPPORTED_HIGH: u16 = 1 << 2; pub const USB_SS_DEVICE_SPEED_SUPPORTED_SUPER: u16 = 1 << 3; +pub const USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_HIGH: u8 = 2; pub const USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_SUPER: u8 = 3; /// USB Descriptor size diff --git a/devices/src/usb/storage.rs b/devices/src/usb/storage.rs index bec309773..aaa04aa08 100644 --- a/devices/src/usb/storage.rs +++ b/devices/src/usb/storage.rs @@ -233,9 +233,9 @@ pub struct UsbStorageConfig { #[arg(long)] pub drive: String, #[arg(long)] - bus: Option, + pub(super) bus: Option, #[arg(long)] - port: Option, + pub(super) port: Option, } /// USB storage device. @@ -346,7 +346,42 @@ impl UsbStorage { }) } - fn handle_control_packet(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + pub fn do_realize(&mut self) -> Result<()> { + self.base.reset_usb_endpoint(); + self.base.speed = USB_SPEED_HIGH; + let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); + let prefix = &s[STR_SERIAL_STORAGE_INDEX as usize]; + s[STR_SERIAL_STORAGE_INDEX as usize] = self.base.generate_serial_number(prefix); + self.base.init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; + + // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not + // supported. + let scsidev_classtype = match self.drive_cfg.media.as_str() { + "disk" => "scsi-hd".to_string(), + _ => "scsi-cd".to_string(), + }; + let scsi_dev_cfg = ScsiDevConfig { + classtype: scsidev_classtype, + drive: self.dev_cfg.drive.clone(), + ..Default::default() + }; + let scsi_device = ScsiDevice::new( + scsi_dev_cfg, + self.drive_cfg.clone(), + self.drive_files.clone(), + None, + self.scsi_bus.clone(), + ); + let realized_scsi = scsi_device.realize()?; + self.scsi_dev = Some(realized_scsi.clone()); + + self.scsi_bus + .lock() + .unwrap() + .attach_child(get_scsi_key(0, 0), realized_scsi) + } + + pub fn handle_control_packet(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { match device_req.request_type { USB_ENDPOINT_OUT_REQUEST => { if device_req.request == USB_REQUEST_CLEAR_FEATURE { @@ -534,39 +569,7 @@ impl UsbDevice for UsbStorage { gen_base_func!(usb_device_base, usb_device_base_mut, UsbDeviceBase, base); fn realize(mut self) -> Result>> { - self.base.reset_usb_endpoint(); - self.base.speed = USB_SPEED_HIGH; - let mut s: Vec = DESC_STRINGS.iter().map(|&s| s.to_string()).collect(); - let prefix = &s[STR_SERIAL_STORAGE_INDEX as usize]; - s[STR_SERIAL_STORAGE_INDEX as usize] = self.base.generate_serial_number(prefix); - self.base.init_descriptor(DESC_DEVICE_STORAGE.clone(), s)?; - - // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not - // supported. - let scsidev_classtype = match self.drive_cfg.media.as_str() { - "disk" => "scsi-hd".to_string(), - _ => "scsi-cd".to_string(), - }; - let scsi_dev_cfg = ScsiDevConfig { - classtype: scsidev_classtype, - drive: self.dev_cfg.drive.clone(), - ..Default::default() - }; - let scsi_device = ScsiDevice::new( - scsi_dev_cfg, - self.drive_cfg.clone(), - self.drive_files.clone(), - None, - self.scsi_bus.clone(), - ); - let realized_scsi = scsi_device.realize()?; - self.scsi_dev = Some(realized_scsi.clone()); - - self.scsi_bus - .lock() - .unwrap() - .attach_child(get_scsi_key(0, 0), realized_scsi)?; - + self.do_realize()?; let storage: Arc> = Arc::new(Mutex::new(self)); Ok(storage) } diff --git a/devices/src/usb/uas.rs b/devices/src/usb/uas.rs index 04ce87453..301dff080 100644 --- a/devices/src/usb/uas.rs +++ b/devices/src/usb/uas.rs @@ -27,8 +27,9 @@ use super::config::*; use super::descriptor::{ UsbConfigDescriptor, UsbDescConfig, UsbDescDevice, UsbDescEndpoint, UsbDescIface, UsbDescriptorOps, UsbDeviceDescriptor, UsbEndpointDescriptor, UsbInterfaceDescriptor, - UsbSuperSpeedEndpointCompDescriptor, + UsbSuperSpeedCapDescriptor, UsbSuperSpeedEndpointCompDescriptor, }; +use super::storage::{UsbStorage, UsbStorageConfig, GET_MAX_LUN, MASS_STORAGE_RESET}; use super::xhci::xhci_controller::XhciDevice; use super::{ UsbDevice, UsbDeviceBase, UsbDeviceRequest, UsbPacket, UsbPacketStatus, @@ -42,8 +43,8 @@ use crate::ScsiBus::{ use crate::ScsiDisk::{ScsiDevConfig, ScsiDevice}; use crate::{Bus, Device}; use machine_manager::config::{DriveConfig, DriveFile}; -use util::byte_code::ByteCode; use util::gen_base_func; +use util::{aio::AioEngine, byte_code::ByteCode}; // Size of UasIUBody const UAS_IU_BODY_SIZE: usize = 30; @@ -87,6 +88,10 @@ const _UAS_TMF_QUERY_TASK: u8 = 0x80; const _UAS_TMF_QUERY_TASK_SET: u8 = 0x81; const _UAS_TMF_QUERY_ASYNC_EVENT: u8 = 0x82; +// Interface alt settings +const UAS_ALT_SETTING_BOT: u8 = 0; +const UAS_ALT_SETTING_UAS: u8 = 1; + #[derive(Parser, Clone, Debug)] #[command(no_binary_name(true))] pub struct UsbUasConfig { @@ -95,15 +100,25 @@ pub struct UsbUasConfig { #[arg(long)] pub drive: String, #[arg(long)] - pub id: Option, - #[arg(long)] - pub speed: Option, + pub id: String, #[arg(long)] bus: Option, #[arg(long)] port: Option, } +impl From for UsbStorageConfig { + fn from(uas_config: UsbUasConfig) -> Self { + Self { + classtype: uas_config.classtype, + id: String::new(), + drive: uas_config.drive, + bus: uas_config.bus, + port: uas_config.port, + } + } +} + pub struct UsbUas { base: UsbDeviceBase, uas_config: UsbUasConfig, @@ -114,6 +129,8 @@ pub struct UsbUas { commands: [Option; UAS_MAX_STREAMS + 1], statuses: [Option>>; UAS_MAX_STREAMS + 1], data: [Option>>; UAS_MAX_STREAMS + 1], + bot: UsbStorage, + is_bot: bool, } #[derive(Debug, Default, EnumCount)] @@ -296,7 +313,7 @@ static DESC_IFACE_UAS: Lazy> = Lazy::new(|| { bLength: USB_DT_INTERFACE_SIZE, bDescriptorType: USB_DT_INTERFACE, bInterfaceNumber: 0, - bAlternateSetting: 1, + bAlternateSetting: UAS_ALT_SETTING_UAS, bNumEndpoints: 4, bInterfaceClass: USB_CLASS_MASS_STORAGE, bInterfaceSubClass: USB_SUBCLASS_SCSI, @@ -425,27 +442,74 @@ static DESC_IFACE_UAS: Lazy> = Lazy::new(|| { }) }); -// NOTE: Fake BOT interface descriptor is needed here since Windows UASP driver always expects two -// interfaces: both BOT and UASP. It also anticipates the UASP descriptor to be the second one. -// Therefore, the first one can be a BOT storage stub. static DESC_IFACE_BOT: Lazy> = Lazy::new(|| { Arc::new(UsbDescIface { interface_desc: UsbInterfaceDescriptor { bLength: USB_DT_INTERFACE_SIZE, bDescriptorType: USB_DT_INTERFACE, bInterfaceNumber: 0, - bAlternateSetting: 0, - bNumEndpoints: 0, + bAlternateSetting: UAS_ALT_SETTING_BOT, + bNumEndpoints: 2, bInterfaceClass: USB_CLASS_MASS_STORAGE, bInterfaceSubClass: USB_SUBCLASS_SCSI, bInterfaceProtocol: USB_IFACE_PROTOCOL_BOT, iInterface: 0, }, other_desc: vec![], - endpoints: vec![], + endpoints: vec![ + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_DEVICE_TO_HOST | 0x01, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 1024, + bInterval: 0, + }, + extra: UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 15, + bmAttributes: 0, + wBytesPerInterval: 0, + } + .as_bytes() + .to_vec(), + }), + Arc::new(UsbDescEndpoint { + endpoint_desc: UsbEndpointDescriptor { + bLength: USB_DT_ENDPOINT_SIZE, + bDescriptorType: USB_DT_ENDPOINT, + bEndpointAddress: USB_DIRECTION_HOST_TO_DEVICE | 0x02, + bmAttributes: USB_ENDPOINT_ATTR_BULK, + wMaxPacketSize: 1024, + bInterval: 0, + }, + extra: UsbSuperSpeedEndpointCompDescriptor { + bLength: USB_DT_SS_EP_COMP_SIZE, + bDescriptorType: USB_DT_ENDPOINT_COMPANION, + bMaxBurst: 15, + bmAttributes: 0, + wBytesPerInterval: 0, + } + .as_bytes() + .to_vec(), + }), + ], }) }); +static DESC_CAP_UAS: UsbSuperSpeedCapDescriptor = UsbSuperSpeedCapDescriptor { + bLength: USB_DT_SS_CAP_SIZE, + bDescriptorType: USB_DT_DEVICE_CAPABILITY, + bDevCapabilityType: USB_SS_DEVICE_CAP, + bmAttributes: 0, + wSpeedsSupported: USB_SS_DEVICE_SPEED_SUPPORTED_SUPER | USB_SS_DEVICE_SPEED_SUPPORTED_HIGH, + bFunctionalitySupport: USB_SS_DEVICE_FUNCTIONALITY_SUPPORT_HIGH, + bU1DevExitLat: 0xA, + wU2DevExitLat: 0x20, +}; + fn complete_async_packet(packet: &Arc>) { let locked_packet = packet.lock().unwrap(); @@ -462,21 +526,24 @@ impl UsbUas { uas_config: UsbUasConfig, drive_cfg: DriveConfig, drive_files: Arc>>, - ) -> Self { - Self { - base: UsbDeviceBase::new( - uas_config.id.as_ref().unwrap().clone(), - USB_DEVICE_BUFFER_DEFAULT_LEN, - ), - uas_config, + ) -> Result { + if drive_cfg.aio != AioEngine::Off || drive_cfg.direct { + bail!("USB UAS: \"aio=off,direct=false\" must be configured."); + } + + Ok(Self { + base: UsbDeviceBase::new(uas_config.id.clone(), USB_DEVICE_BUFFER_DEFAULT_LEN), + uas_config: uas_config.clone(), scsi_bus: Arc::new(Mutex::new(ScsiBus::new("".to_string()))), scsi_device: None, - drive_cfg, - drive_files, + drive_cfg: drive_cfg.clone(), + drive_files: drive_files.clone(), commands: array::from_fn(|_| None), statuses: array::from_fn(|_| None), data: array::from_fn(|_| None), - } + bot: UsbStorage::new(uas_config.into(), drive_cfg, drive_files)?, + is_bot: true, + }) } fn cancel_io(&mut self) { @@ -485,6 +552,43 @@ impl UsbUas { self.data = array::from_fn(|_| None); } + /// Class (Mass Storage) specific requests. + fn handle_control_for_device(&mut self, packet: &mut UsbPacket, device_req: &UsbDeviceRequest) { + match device_req.request_type { + USB_ENDPOINT_OUT_REQUEST => { + if device_req.request == USB_REQUEST_CLEAR_FEATURE { + return; + } + } + USB_INTERFACE_CLASS_OUT_REQUEST => { + // NOTE: See USB Mass Storage Class specification: 3.1 Bulk-Only Mass Storage Reset + if device_req.request == MASS_STORAGE_RESET { + // Set storage state mode. + self.bot.handle_control_packet(packet, device_req); + self.cancel_io(); + return; + } + } + USB_INTERFACE_CLASS_IN_REQUEST => { + // NOTE: See USB Mass Storage Class specification: 3.2 Get Max LUN + if device_req.request == GET_MAX_LUN { + // Now only supports 1 LUN. + self.base.data_buf[0] = 0; + packet.actual_length = 1; + return; + } + } + _ => (), + } + + error!( + "UAS {} device unhandled control request {:?}.", + self.device_id(), + device_req + ); + packet.status = UsbPacketStatus::Stall; + } + fn handle_iu_command( &mut self, iu: &UasIU, @@ -732,7 +836,7 @@ impl UsbUas { match result { Ok(result) => result, Err(err) => { - error!("UAS {} device error: {:#?}.", self.device_id(), err); + error!("UAS {} device error: {:?}.", self.device_id(), err); UasPacketStatus::Completed } } @@ -750,6 +854,7 @@ impl UsbDevice for UsbUas { let prefix = &s[UsbUasStringId::SerialNumber as usize]; s[UsbUasStringId::SerialNumber as usize] = self.base.generate_serial_number(prefix); self.base.init_descriptor(DESC_DEVICE_UAS.clone(), s)?; + self.base.set_capability_descriptors(vec![DESC_CAP_UAS]); // NOTE: "aio=off,direct=false" must be configured and other aio/direct values are not // supported. @@ -775,6 +880,8 @@ impl UsbDevice for UsbUas { .lock() .unwrap() .attach_child(get_scsi_key(0, 0), realized_scsi)?; + + self.bot.do_realize()?; let uas = Arc::new(Mutex::new(self)); Ok(uas) } @@ -788,6 +895,8 @@ impl UsbDevice for UsbUas { self.base.remote_wakeup = 0; self.base.addr = 0; self.cancel_io(); + // Reset storage state. + self.bot.reset(); } fn handle_control(&mut self, packet: &Arc>, device_req: &UsbDeviceRequest) { @@ -798,6 +907,12 @@ impl UsbDevice for UsbUas { device_req.as_bytes(), ); + if device_req.request_type == USB_INTERFACE_OUT_REQUEST + && device_req.request == USB_REQUEST_SET_INTERFACE + { + self.is_bot = device_req.value != UAS_ALT_SETTING_UAS as u16; + } + match self .base .handle_control_for_descriptor(&mut locked_packet, device_req) @@ -811,12 +926,7 @@ impl UsbDevice for UsbUas { return; } - error!( - "UAS {} device unhandled control request {:?}.", - self.device_id(), - device_req - ); - locked_packet.status = UsbPacketStatus::Stall; + self.handle_control_for_device(&mut locked_packet, device_req); } Err(err) => { warn!( @@ -830,6 +940,10 @@ impl UsbDevice for UsbUas { } fn handle_data(&mut self, packet: &Arc>) { + if self.is_bot { + return self.bot.handle_data(packet); + } + let locked_packet = packet.lock().unwrap(); let stream = locked_packet.stream as usize; let ep_number = locked_packet.ep_number; diff --git a/machine/src/lib.rs b/machine/src/lib.rs index 484aaf256..8ec8ad778 100644 --- a/machine/src/lib.rs +++ b/machine/src/lib.rs @@ -1872,7 +1872,7 @@ pub trait MachineOps: MachineLifecycle { .drives .remove(&device_cfg.drive) .with_context(|| "No drive configured matched for usb uas device.")?; - let uas = UsbUas::new(device_cfg, drive_cfg, self.get_drive_files()); + let uas = UsbUas::new(device_cfg, drive_cfg, self.get_drive_files())?; uas.realize() .with_context(|| "Failed to realize usb uas device")? } -- Gitee From 2965179177d36967fbbafcce7498e3794865cbfb Mon Sep 17 00:00:00 2001 From: goriainovstanislav Date: Tue, 21 May 2024 15:42:22 +0300 Subject: [PATCH 2165/2187] uas: Support USB UAS device hotplug with qmp commands Add usb-uas entry for USB hotplug. Signed-off-by: goriainovstanislav --- machine/src/standard_common/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machine/src/standard_common/mod.rs b/machine/src/standard_common/mod.rs index b493c6f49..f33b8b186 100644 --- a/machine/src/standard_common/mod.rs +++ b/machine/src/standard_common/mod.rs @@ -1279,7 +1279,7 @@ impl DeviceInterface for StdMachine { ); } } - "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" | "usb-storage" => { + "usb-kbd" | "usb-tablet" | "usb-camera" | "usb-host" | "usb-storage" | "usb-uas" => { let cfg_args = locked_vmconfig.add_device_config(args.as_ref()); if let Err(e) = self.add_usb_device(&mut vm_config_clone, &cfg_args) { error!("{:?}", e); -- Gitee From 47103a9c8267d8b9b38e84c90210e2c10cf9c283 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Tue, 15 Oct 2024 09:57:23 +0800 Subject: [PATCH 2166/2187] drive: add log when opening file Print logs when opening files. Signed-off-by: liuxiangdong --- machine_manager/src/config/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index ce4c313b2..6f793d4e6 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -61,13 +61,14 @@ pub use vnc::*; use std::collections::HashMap; use std::fs::{canonicalize, File}; use std::io::Read; +use std::os::unix::io::AsRawFd; use std::path::Path; use std::str::FromStr; use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use clap::Parser; -use log::error; +use log::{error, info}; use serde::{Deserialize, Serialize}; use trace::{enable_state_by_type, set_state_by_pattern, TraceType}; @@ -314,6 +315,7 @@ impl VmConfig { req_align, buf_align, }; + info!("Open file {}, fd: {}", path, drive_file.file.as_raw_fd()); drive_files.insert(path.to_string(), drive_file); Ok(()) } -- Gitee From 8e7f521c6d27715830d6c7cd98d9079aa4746e67 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 15 Oct 2024 20:26:37 +0800 Subject: [PATCH 2167/2187] ozonec/clone: Fallback to clone() when clone3() failed clone3() is introduced in Linux Kernel 5.3. --- ozonec/src/linux/container.rs | 8 ++++---- ozonec/src/linux/process.rs | 38 +++++++++++++++++++++++++++-------- ozonec/src/utils/clone.rs | 14 ++++++++----- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index a980fc08a..f13475c53 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -127,7 +127,7 @@ impl LinuxContainer { process: &mut Process, parent_channel: &Channel, fst_stage_channel: &Channel, - notify_listener: Option, + notify_listener: &Option, ) -> Result<()> { debug!("First stage process start"); @@ -146,7 +146,7 @@ impl LinuxContainer { // Spawn a child process to perform the second stage to initialize container. let init_pid = clone_process("ozonec:[2:INIT]", || { - self.do_second_stage(process, parent_channel, notify_listener) + self.do_second_stage(process, parent_channel, ¬ify_listener) .with_context(|| "Second stage process encounters errors")?; Ok(0) })?; @@ -162,7 +162,7 @@ impl LinuxContainer { &mut self, process: &mut Process, parent_channel: &Channel, - notify_listener: Option, + notify_listener: &Option, ) -> Result<()> { debug!("Second stage process start"); @@ -677,7 +677,7 @@ impl Container for LinuxContainer { process, &parent_channel, &fst_stage_channel, - notify_listener, + ¬ify_listener, ) .with_context(|| "First stage process encounters errors")?; Ok(0) diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index ae3add0d4..6215a24b9 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -26,8 +26,10 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use caps::{self, CapSet, Capability, CapsHashSet}; +use libc::SIGCHLD; use nix::{ errno::Errno, + sched::{clone, CloneFlags}, unistd::{self, chdir, setresgid, setresuid, Gid, Pid, Uid}, }; use rlimit::{setrlimit, Resource, Rlim}; @@ -324,15 +326,35 @@ impl Process { } // Clone a new child process. -pub fn clone_process Result>(child_name: &str, cb: F) -> Result { +pub fn clone_process Result>(child_name: &str, mut cb: F) -> Result { let mut clone3 = Clone3::default(); - clone3.exit_signal(libc::SIGCHLD as u64); + clone3.exit_signal(SIGCHLD as u64); + + let mut ret = clone3.call(); + if ret.is_err() { + // clone3() may not be supported in the kernel, fallback to clone(); + let mut stack = [0; 1024 * 1024]; + ret = clone( + Box::new(|| match cb() { + Ok(r) => r as isize, + Err(e) => { + eprintln!("{}", e); + -1 + } + }), + &mut stack, + CloneFlags::empty(), + Some(SIGCHLD), + ) + .map_err(|e| anyhow!("Clone error: errno {}", e)); + } + + match ret { + Ok(pid) => { + if pid.as_raw() != 0 { + return Ok(pid); + } - match clone3 - .call() - .map_err(|e| anyhow!("Clone3() error: {}", e))? - { - 0 => { prctl::set_name(child_name) .map_err(|e| anyhow!("Failed to set process name: errno {}", e))?; let ret = match cb() { @@ -344,7 +366,7 @@ pub fn clone_process Result>(child_name: &str, cb: F) -> Res }; std::process::exit(ret); } - pid => Ok(Pid::from_raw(pid)), + Err(e) => bail!(e), } } diff --git a/ozonec/src/utils/clone.rs b/ozonec/src/utils/clone.rs index 2476a2f3d..2dd99e662 100644 --- a/ozonec/src/utils/clone.rs +++ b/ozonec/src/utils/clone.rs @@ -12,8 +12,9 @@ use std::os::unix::io::{AsRawFd, RawFd}; -use anyhow::{bail, Result}; -use nix::errno::errno; +use anyhow::{bail, Context, Result}; +use libc::pid_t; +use nix::{errno::errno, unistd::Pid}; bitflags::bitflags! { #[derive(Default)] @@ -92,7 +93,7 @@ impl<'a> Clone3<'a> { self } - pub fn call(&mut self) -> Result { + pub fn call(&mut self) -> Result { let clone_args = CloneArgs { flags: self.flags.bits(), pid_fd: option_as_mut_ptr(&mut self.pidfd) as u64, @@ -114,8 +115,11 @@ impl<'a> Clone3<'a> { ) }; if ret == -1 { - bail!("errno {}", errno()); + bail!("clone3 error: errno {}", errno()); } - Ok(ret as libc::pid_t) + + Ok(Pid::from_raw( + pid_t::try_from(ret).with_context(|| "Invalid pid")?, + )) } } -- Gitee From 1059e7986b000a6971932abae7071762b1dcb0d9 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 07:56:52 +0800 Subject: [PATCH 2168/2187] ozonec/device: Add unit test cases --- ozonec/src/linux/device.rs | 198 +++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/ozonec/src/linux/device.rs b/ozonec/src/linux/device.rs index 90575a078..a008fd0f1 100644 --- a/ozonec/src/linux/device.rs +++ b/ozonec/src/linux/device.rs @@ -236,3 +236,201 @@ struct DeviceInfo { uid: Option, gid: Option, } + +#[cfg(test)] +mod tests { + use std::{ + fs, + os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}, + }; + + use nix::mount::umount; + + use super::*; + + #[test] + fn test_mknod_dev() { + let rootfs = PathBuf::from("/tmp/ozonec/mknod_dev"); + create_dir_all(&rootfs).unwrap(); + let dev = Device::new(rootfs.clone()); + let path = rootfs.join("mknod_dev"); + if path.exists() { + remove_file(&path).unwrap(); + } + let dev_info = DeviceInfo { + path: path.clone(), + dev_type: "c".to_string(), + major: 1, + minor: 3, + file_mode: Some(0o644u32), + uid: Some(1000u32), + gid: Some(1000u32), + }; + + assert!(dev.mknod_device(&dev_info).is_ok()); + assert!(path.exists()); + + let metadata = fs::metadata(&path).unwrap(); + assert!(metadata.file_type().is_char_device()); + let major = (metadata.rdev() >> 8) as u32; + let minor = (metadata.rdev() & 0xff) as u32; + assert_eq!(major, 1); + assert_eq!(minor, 3); + let file_mode = metadata.permissions().mode(); + assert_eq!(file_mode & 0o777, 0o644u32); + assert_eq!(metadata.uid(), 1000); + assert_eq!(metadata.gid(), 1000); + + fs::remove_dir_all("/tmp/ozonec").unwrap(); + } + + #[test] + #[ignore = "mount may not be permitted"] + fn test_bind_dev() { + let rootfs = PathBuf::from("/tmp/ozonec/bind_dev"); + create_dir_all(&rootfs).unwrap(); + let dev_path = PathBuf::from("/mknod_dev"); + if dev_path.exists() { + remove_file(&dev_path).unwrap(); + } + let dev = makedev(1, 3); + mknod( + &dev_path, + SFlag::S_IFCHR, + Mode::from_bits_truncate(0o644u32), + dev, + ) + .unwrap(); + let dev_to_bind = Device::new(rootfs.clone()); + let binded_path = rootfs.join("mknod_dev"); + if binded_path.exists() { + umount(&binded_path).unwrap(); + remove_file(&binded_path).unwrap(); + } + let dev_info = DeviceInfo { + path: binded_path.clone(), + dev_type: "c".to_string(), + major: 1, + minor: 3, + file_mode: Some(0o644u32), + uid: Some(1000u32), + gid: Some(1000u32), + }; + + assert!(dev_to_bind.bind_device(&dev_info).is_ok()); + + let metadata = fs::metadata(&dev_path).unwrap(); + let binded_metadata = fs::metadata(&binded_path).unwrap(); + assert_eq!(binded_metadata.file_type(), metadata.file_type()); + assert_eq!(binded_metadata.rdev(), metadata.rdev()); + assert_eq!(binded_metadata.permissions(), metadata.permissions()); + assert_eq!(binded_metadata.uid(), metadata.uid()); + assert_eq!(binded_metadata.gid(), metadata.gid()); + + umount(&binded_path).unwrap(); + fs::remove_dir_all("/tmp/ozonec").unwrap(); + fs::remove_file(dev_path).unwrap(); + } + + #[test] + fn test_create_device() { + let oci_dev = OciDevice { + dev_type: "c".to_string(), + path: "/mknod_dev".to_string(), + major: Some(1), + minor: Some(3), + fileMode: Some(0o644u32), + uid: Some(1000), + gid: Some(1000), + }; + let rootfs = PathBuf::from("/tmp/ozonec/create_device"); + create_dir_all(&rootfs).unwrap(); + let path = rootfs.join("mknod_dev"); + if path.exists() { + remove_file(&path).unwrap(); + } + let dev = Device::new(rootfs.clone()); + + assert!(dev.create_device(&oci_dev, true).is_ok()); + assert!(path.exists()); + + let metadata = fs::metadata(&path).unwrap(); + assert!(metadata.file_type().is_char_device()); + let major = (metadata.rdev() >> 8) as u32; + let minor = (metadata.rdev() & 0xff) as u32; + assert_eq!(major, 1); + assert_eq!(minor, 3); + let file_mode = metadata.permissions().mode(); + assert_eq!(file_mode & 0o777, 0o644u32); + assert_eq!(metadata.uid(), 1000); + assert_eq!(metadata.gid(), 1000); + + fs::remove_dir_all("/tmp/ozonec").unwrap(); + } + + #[test] + fn test_delete_device() { + let oci_dev = OciDevice { + dev_type: "c".to_string(), + path: "/mknod_dev".to_string(), + major: Some(1), + minor: Some(3), + fileMode: Some(0o644u32), + uid: Some(1000), + gid: Some(1000), + }; + let rootfs = PathBuf::from("/tmp/ozonec/delete_device"); + create_dir_all(&rootfs).unwrap(); + let path = rootfs.join("mknod_dev"); + if path.exists() { + remove_file(&path).unwrap(); + } + let dev = Device::new(rootfs.clone()); + dev.create_device(&oci_dev, true).unwrap(); + + assert!(dev.delete_device(&oci_dev).is_ok()); + assert!(!path.exists()); + + fs::remove_dir_all("/tmp/ozonec").unwrap(); + } + + #[test] + fn test_default_device() { + let rootfs = PathBuf::from("/tmp/ozonec/default_device"); + let dev = Device::new(rootfs.clone()); + + let mut oci_dev = OciDevice { + dev_type: "c".to_string(), + path: "mknod_dev".to_string(), + major: Some(1), + minor: Some(3), + fileMode: Some(0o644u32), + uid: Some(1000), + gid: Some(1000), + }; + assert!(!dev.is_default_device(&oci_dev)); + oci_dev.path = "/dev/null".to_string(); + assert!(dev.is_default_device(&oci_dev)); + oci_dev.path = "/dev/zero".to_string(); + assert!(dev.is_default_device(&oci_dev)); + oci_dev.path = "/dev/full".to_string(); + assert!(dev.is_default_device(&oci_dev)); + oci_dev.path = "/dev/random".to_string(); + assert!(dev.is_default_device(&oci_dev)); + oci_dev.path = "/dev/urandom".to_string(); + assert!(dev.is_default_device(&oci_dev)); + oci_dev.path = "/dev/tty".to_string(); + assert!(dev.is_default_device(&oci_dev)); + } + + #[test] + fn test_get_sflag() { + let rootfs = PathBuf::from("/tmp/ozonec/test_get_sflag"); + let dev = Device::new(rootfs.clone()); + + assert_eq!(dev.get_sflag("c").unwrap(), SFlag::S_IFCHR); + assert_eq!(dev.get_sflag("b").unwrap(), SFlag::S_IFBLK); + assert_eq!(dev.get_sflag("p").unwrap(), SFlag::S_IFIFO); + assert_eq!(dev.get_sflag("u").unwrap(), SFlag::S_IFCHR); + } +} -- Gitee From a090686880dd1024bd42f75f585eb326b41db5c8 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 12:37:39 +0800 Subject: [PATCH 2169/2187] ozonec/mount: Add unit test cases --- ozonec/Cargo.lock | 103 +++++++++++++++++-- ozonec/Cargo.toml | 1 + ozonec/src/linux/mount.rs | 181 ++++++++++++++++++++++++++++++++++ ozonec/src/linux/namespace.rs | 17 ++++ 4 files changed, 294 insertions(+), 8 deletions(-) diff --git a/ozonec/Cargo.lock b/ozonec/Cargo.lock index 6274f9c4e..b67a79bbb 100644 --- a/ozonec/Cargo.lock +++ b/ozonec/Cargo.lock @@ -41,6 +41,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bumpalo" version = "3.16.0" @@ -97,7 +103,7 @@ version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "once_cell", @@ -150,6 +156,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "flate2" version = "1.0.31" @@ -160,6 +172,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "heck" version = "0.4.1" @@ -245,7 +263,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21c57fd8981a80019807b7b68118618d29a87177c63d704fc96e6ecd003ae5b3" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libseccomp-sys", "pkg-config", @@ -263,6 +281,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "log" version = "0.4.18" @@ -293,7 +317,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset", @@ -338,7 +362,7 @@ name = "ozonec" version = "0.1.0" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "caps", "chrono", "clap", @@ -349,6 +373,7 @@ dependencies = [ "oci_spec", "procfs", "rlimit", + "rusty-fork", "serde", "serde_json", "thiserror", @@ -405,15 +430,21 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "chrono", "flate2", "hex", "lazy_static", - "rustix", + "rustix 0.36.17", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.36" @@ -423,6 +454,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "rlimit" version = "0.5.4" @@ -438,14 +478,39 @@ version = "0.36.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", "windows-sys 0.45.0", ] +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.48.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.18" @@ -517,6 +582,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix 0.38.3", + "windows-sys 0.48.0", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -549,6 +627,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasm-bindgen" version = "0.2.93" diff --git a/ozonec/Cargo.toml b/ozonec/Cargo.toml index ac0977b33..a617a89a4 100644 --- a/ozonec/Cargo.toml +++ b/ozonec/Cargo.toml @@ -19,6 +19,7 @@ nix = "= 0.26.2" oci_spec = { path = "oci_spec" } procfs = "0.14.0" rlimit = "0.5.3" +rusty-fork = "0.3.0" serde = { version = "= 1.0.163", features = ["derive"] } serde_json = "= 1.0.96" thiserror = "= 1.0.40" diff --git a/ozonec/src/linux/mount.rs b/ozonec/src/linux/mount.rs index 55ab45366..af44bd3c8 100644 --- a/ozonec/src/linux/mount.rs +++ b/ozonec/src/linux/mount.rs @@ -27,6 +27,7 @@ use procfs::process::{MountInfo, Process}; use crate::utils::{openat2_in_root, proc_fd_path, OzonecErr}; use oci_spec::runtime::Mount as OciMount; +#[derive(PartialEq, Debug)] enum CgroupType { CgroupV1, CgroupV2, @@ -271,3 +272,183 @@ impl Mount { Ok(CgroupType::CgroupV1) } } + +#[cfg(test)] +mod tests { + use rusty_fork::rusty_fork_test; + + use crate::linux::namespace::tests::set_namespace; + use oci_spec::linux::NamespaceType; + + use super::*; + + fn init_mount(rootfs: &str) -> Mount { + let path = PathBuf::from(rootfs); + create_dir_all(&path).unwrap(); + Mount::new(&path) + } + + #[test] + fn test_is_mounted_sysfs_dir() { + let mut path = PathBuf::from("/test"); + let mut mnt = Mount::new(&path); + assert!(!mnt.is_mounted_sysfs_dir(path.to_str().unwrap())); + + path = PathBuf::from("/sys"); + mnt = Mount::new(&path); + assert!(mnt.is_mounted_sysfs_dir(path.to_str().unwrap())); + } + + #[test] + fn test_cgroup_type() { + let rootfs = PathBuf::from("/tmp/ozonec/test_cgroup_type"); + let mnt = Mount::new(&rootfs); + let cgroup_path = Path::new("/sys/fs/cgroup"); + + if !cgroup_path.exists() { + assert!(mnt.cgroup_type().is_err()); + } else { + let st = statfs(cgroup_path).unwrap(); + if st.filesystem_type() == CGROUP2_SUPER_MAGIC { + assert_eq!(mnt.cgroup_type().unwrap(), CgroupType::CgroupV2); + } else { + assert_eq!(mnt.cgroup_type().unwrap(), CgroupType::CgroupV1); + } + } + } + + #[test] + fn test_get_mount_flag_data() { + let rootfs = PathBuf::from("/test_get_mount_flag_data"); + let mnt = Mount::new(&rootfs); + let mut oci_mnt = OciMount { + destination: String::new(), + source: None, + options: Some(vec![ + String::from("defaults"), + String::from("rw"), + String::from("suid"), + String::from("dev"), + String::from("exec"), + String::from("async"), + String::from("nomand"), + String::from("atime"), + String::from("diratime"), + String::from("norelatime"), + String::from("nostrictatime"), + ]), + fs_type: None, + uidMappings: None, + gidMappings: None, + }; + + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!(flags, MsFlags::empty()); + + oci_mnt.options = Some(vec![ + String::from("ro"), + String::from("nosuid"), + String::from("nodev"), + String::from("noexec"), + String::from("sync"), + String::from("dirsync"), + String::from("remount"), + String::from("mand"), + String::from("noatime"), + String::from("nodiratime"), + String::from("bind"), + String::from("unbindable"), + String::from("private"), + String::from("shared"), + String::from("slave"), + String::from("relatime"), + String::from("strictatime"), + ]); + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!( + flags, + MsFlags::MS_RDONLY + | MsFlags::MS_NOSUID + | MsFlags::MS_NODEV + | MsFlags::MS_NOEXEC + | MsFlags::MS_SYNCHRONOUS + | MsFlags::MS_DIRSYNC + | MsFlags::MS_REMOUNT + | MsFlags::MS_MANDLOCK + | MsFlags::MS_NOATIME + | MsFlags::MS_NODIRATIME + | MsFlags::MS_BIND + | MsFlags::MS_UNBINDABLE + | MsFlags::MS_PRIVATE + | MsFlags::MS_SHARED + | MsFlags::MS_SLAVE + | MsFlags::MS_RELATIME + | MsFlags::MS_STRICTATIME + ); + + oci_mnt.options = Some(vec![String::from("rbind")]); + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!(flags, MsFlags::MS_BIND | MsFlags::MS_REC); + oci_mnt.options = Some(vec![String::from("runbindable")]); + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!(flags, MsFlags::MS_UNBINDABLE | MsFlags::MS_REC); + oci_mnt.options = Some(vec![String::from("rprivate")]); + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!(flags, MsFlags::MS_PRIVATE | MsFlags::MS_REC); + oci_mnt.options = Some(vec![String::from("rshared")]); + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!(flags, MsFlags::MS_SHARED | MsFlags::MS_REC); + oci_mnt.options = Some(vec![String::from("rslave")]); + let (flags, _data) = mnt.get_mount_flag_data(&oci_mnt); + assert_eq!(flags, MsFlags::MS_SLAVE | MsFlags::MS_REC); + } + + rusty_fork_test! { + #[test] + #[ignore = "unshare may not be permitted"] + fn test_do_mounts_cgroup() { + set_namespace(NamespaceType::Mount); + + let mounts = vec![OciMount { + destination: String::from("/sys/fs/cgroup"), + source: Some(String::from("cgroup")), + options: Some(vec![ + String::from("nosuid"), + String::from("noexec"), + String::from("nodev"), + String::from("relatime"), + String::from("ro"), + ]), + fs_type: Some(String::from("cgroup")), + uidMappings: None, + gidMappings: None, + }]; + let mnt = init_mount("/tmp/ozonec/test_do_mounts_cgroup"); + + assert!(mnt.do_mounts(&mounts, &None).is_ok()); + assert!(mnt.rootfs.join("sys/fs/cgroup").exists()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_do_mounts_bind() { + set_namespace(NamespaceType::Mount); + + let mounts = vec![OciMount { + destination: String::from("/dest"), + source: Some(String::from("/tmp/ozonec/test_do_mounts_bind/source")), + options: Some(vec![ + String::from("rbind") + ]), + fs_type: None, + uidMappings: None, + gidMappings: None, + }]; + let mnt = init_mount("/tmp/ozonec/test_do_mounts_bind"); + create_dir_all(&mnt.rootfs.join("source")).unwrap(); + + assert!(mnt.do_mounts(&mounts, &None).is_ok()); + assert!(mnt.rootfs.join("dest").exists()); + } + } +} diff --git a/ozonec/src/linux/namespace.rs b/ozonec/src/linux/namespace.rs index 5928e3d00..8adf610f3 100644 --- a/ozonec/src/linux/namespace.rs +++ b/ozonec/src/linux/namespace.rs @@ -64,3 +64,20 @@ impl NsController { Ok(self.namespaces.get(&clone_flags)) } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn set_namespace(ns_type: NamespaceType) { + let mut ns_ctrl = NsController { + namespaces: HashMap::new(), + }; + let ns = Namespace { + ns_type, + path: None, + }; + ns_ctrl.namespaces.insert(ns_type.try_into().unwrap(), ns); + ns_ctrl.set_namespace(ns_type).unwrap(); + } +} -- Gitee From 774212d80a5872c674770046a948ee6028166129 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 13:48:46 +0800 Subject: [PATCH 2170/2187] ozonec/notify_socket: Add unit test cases --- ozonec/src/linux/notify_socket.rs | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/ozonec/src/linux/notify_socket.rs b/ozonec/src/linux/notify_socket.rs index 956c415ea..356be384c 100644 --- a/ozonec/src/linux/notify_socket.rs +++ b/ozonec/src/linux/notify_socket.rs @@ -88,3 +88,42 @@ impl NotifySocket { Ok(()) } } + +#[cfg(test)] +mod test { + use std::fs::{create_dir_all, remove_dir_all}; + + use nix::sys::wait::{waitpid, WaitStatus}; + + use crate::linux::process::clone_process; + + use super::*; + + #[test] + fn test_notify_socket() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let root = PathBuf::from("/tmp/ozonec/notify_socket"); + create_dir_all(&root).unwrap(); + + let socket_path = root.join(NOTIFY_SOCKET); + let mut socket = NotifySocket::new(&socket_path); + let listener = NotifyListener::new(root.clone()).unwrap(); + let child = clone_process("notify_socket", || { + listener.wait_for_start_container().unwrap(); + Ok(1) + }) + .unwrap(); + socket.notify_container_start().unwrap(); + + match waitpid(child, None) { + Ok(WaitStatus::Exited(_, s)) => { + assert_eq!(s, 1); + } + Ok(_) => (), + Err(e) => { + panic!("Failed to waitpid for child process: {e}"); + } + } + } +} -- Gitee From 5d0083aacd487623c2e05f05506d779d99029267 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 15:07:22 +0800 Subject: [PATCH 2171/2187] ozonec/terminal: Add unit test cases --- ozonec/src/linux/terminal.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ozonec/src/linux/terminal.rs b/ozonec/src/linux/terminal.rs index 6b11702e0..26da7376e 100644 --- a/ozonec/src/linux/terminal.rs +++ b/ozonec/src/linux/terminal.rs @@ -95,3 +95,17 @@ pub fn connect_stdio(stdin: &RawFd, stdout: &RawFd, stderr: &RawFd) -> Result<() .with_context(|| OzonecErr::Dup2("stderr".to_string()))?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_connect_stdio() { + let stdin: RawFd = 0; + let stdout: RawFd = 0; + let stderr: RawFd = 0; + + assert!(connect_stdio(&stdin, &stdout, &stderr).is_ok()); + } +} -- Gitee From ef60f717b36beabefb4541d06b4fdcbd17057b6b Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 18:17:56 +0800 Subject: [PATCH 2172/2187] ozonec/container: Add unit test cases --- ozonec/src/linux/container.rs | 198 ++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index f13475c53..583a64f0d 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -792,3 +792,201 @@ impl Container for LinuxContainer { Ok(()) } } + +#[cfg(test)] +mod tests { + use chrono::DateTime; + use fs::{remove_dir_all, File}; + use nix::sys::stat::stat; + use unistd::getpid; + + use oci_spec::{ + linux::LinuxPlatform, + posix::{Root, User}, + process::Process as OciProcess, + }; + + use super::*; + + fn init_config() -> RuntimeConfig { + let root = Root { + path: String::from("/tmp/ozonec/bundle/rootfs"), + readonly: true, + }; + let user = User { + uid: 0, + gid: 0, + umask: None, + additionalGids: None, + }; + let process = OciProcess { + cwd: String::from("/"), + args: Some(vec![String::from("bash")]), + env: None, + terminal: false, + consoleSize: None, + rlimits: None, + apparmorProfile: None, + capabilities: None, + noNewPrivileges: None, + oomScoreAdj: None, + scheduler: None, + selinuxLabel: None, + ioPriority: None, + execCPUAffinity: None, + user, + }; + let linux = LinuxPlatform { + namespaces: Vec::new(), + uidMappings: None, + gidMappings: None, + timeOffsets: None, + devices: None, + cgroupsPath: None, + rootfsPropagation: None, + maskedPaths: None, + readonlyPaths: None, + mountLabel: None, + personality: None, + resources: None, + rdma: None, + unified: None, + sysctl: None, + seccomp: None, + #[cfg(target_arch = "x86_64")] + intelRdt: None, + }; + RuntimeConfig { + ociVersion: String::from("1.2"), + root, + mounts: Vec::new(), + process, + hostname: None, + domainname: None, + linux: Some(linux), + vm: None, + hooks: None, + annotations: None, + } + } + + #[test] + fn test_linux_container_new() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let config = init_config(); + let mut exist: bool = false; + let container = LinuxContainer::new( + &String::from("LinuxContainer_new"), + &String::from("/tmp/ozonec"), + &config, + &None, + &mut exist, + ) + .unwrap(); + + let root = Path::new(&container.root); + assert!(root.exists()); + let root_stat = stat(root).unwrap(); + assert_eq!(root_stat.st_uid, geteuid().as_raw()); + assert_eq!(root_stat.st_gid, getegid().as_raw()); + + assert!(LinuxContainer::new( + &String::from("LinuxContainer_new"), + &String::from("/tmp/ozonec"), + &config, + &None, + &mut exist, + ) + .is_err()); + assert_eq!(exist, true); + } + + #[test] + fn test_validate_config() { + let mut config = init_config(); + config.linux = None; + assert!(LinuxContainer::validate_config(&config).is_err()); + + let linux = LinuxPlatform { + namespaces: Vec::new(), + uidMappings: None, + gidMappings: None, + timeOffsets: None, + devices: None, + cgroupsPath: None, + rootfsPropagation: None, + maskedPaths: None, + readonlyPaths: None, + mountLabel: None, + personality: None, + resources: None, + rdma: None, + unified: None, + sysctl: None, + seccomp: None, + #[cfg(target_arch = "x86_64")] + intelRdt: None, + }; + config.process.args = None; + config.linux = Some(linux); + assert!(LinuxContainer::validate_config(&config).is_err()); + } + + #[test] + fn test_load_from_state() { + let mut state = State { + oci_version: String::from("1.2"), + id: String::from("load_from_state"), + pid: 0, + root: PathBuf::from("/tmp/ozonec/root"), + bundle: PathBuf::from("/tmp/ozonec/bundle"), + rootfs: String::from("/tmp/ozonec/bundle/rootfs"), + start_time: 0, + created_time: DateTime::from(SystemTime::now()), + config: None, + }; + assert!(LinuxContainer::load_from_state(&state, &None).is_err()); + + let config = init_config(); + state.config = Some(config); + assert!(LinuxContainer::load_from_state(&state, &None).is_ok()); + } + + #[test] + fn test_status() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let config = init_config(); + create_dir_all(&config.root.path).unwrap(); + let mut exist: bool = false; + let mut container = LinuxContainer::new( + &String::from("get_container_status"), + &String::from("/tmp/ozonec"), + &config, + &None, + &mut exist, + ) + .unwrap(); + container.pid = -1; + + assert_eq!(container.status().unwrap(), ContainerStatus::Creating); + + container.pid = 0; + assert_eq!(container.status().unwrap(), ContainerStatus::Stopped); + + container.pid = getpid().as_raw(); + assert_eq!(container.status().unwrap(), ContainerStatus::Stopped); + + let proc_stat = procfs::process::Process::new(container.pid) + .unwrap() + .stat() + .unwrap(); + container.start_time = proc_stat.starttime; + assert_eq!(container.status().unwrap(), ContainerStatus::Running); + + let notify_socket = PathBuf::from(&container.root).join(NOTIFY_SOCKET); + File::create(¬ify_socket).unwrap(); + assert_eq!(container.status().unwrap(), ContainerStatus::Created); + } +} -- Gitee From d5772dfb8a4da66e265db9b5d10fa9b41b431de2 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Tue, 24 Sep 2024 23:17:09 +0800 Subject: [PATCH 2173/2187] ozonec/process: Add unit test cases --- ozonec/src/linux/process.rs | 140 ++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 6215a24b9..e3c96224a 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -429,3 +429,143 @@ fn to_cap(value: &str) -> Result { _ => bail!("Invalid capability: {}", value), } } + +#[cfg(test)] +mod tests { + use std::path::Path; + + use nix::sys::resource::{getrlimit, Resource}; + use rusty_fork::rusty_fork_test; + + use oci_spec::{ + linux::{IoPriority, SchedPolicy, Scheduler}, + posix::{Rlimits, User}, + }; + + use super::*; + + fn init_oci_process() -> OciProcess { + let user = User { + uid: 0, + gid: 0, + umask: None, + additionalGids: None, + }; + OciProcess { + cwd: String::from("/"), + args: Some(vec![String::from("bash")]), + env: None, + terminal: false, + consoleSize: None, + rlimits: None, + apparmorProfile: None, + capabilities: None, + noNewPrivileges: None, + oomScoreAdj: None, + scheduler: None, + selinuxLabel: None, + ioPriority: None, + execCPUAffinity: None, + user, + } + } + + #[test] + fn test_process_new() { + let mut oci_process = init_oci_process(); + + let process = Process::new(&oci_process, false); + assert_eq!(process.stdin.unwrap(), stdin().as_raw_fd()); + assert_eq!(process.stdout.unwrap(), stdout().as_raw_fd()); + assert_eq!(process.stderr.unwrap(), stderr().as_raw_fd()); + + oci_process.terminal = true; + let process = Process::new(&oci_process, false); + assert!(process.stdin.is_none()); + assert!(process.stdout.is_none()); + assert!(process.stderr.is_none()); + } + + #[test] + fn test_set_tty() { + let mut oci_process = init_oci_process(); + + let process = Process::new(&oci_process, false); + assert!(process.set_tty(None, false).is_ok()); + + oci_process.terminal = true; + let process = Process::new(&oci_process, false); + assert!(process.set_tty(None, false).is_err()); + } + + rusty_fork_test! { + #[test] + #[ignore = "oom_score_adj may not be permitted to set"] + fn test_set_oom_score_adj() { + let mut oci_process = init_oci_process(); + oci_process.oomScoreAdj = Some(100); + let process = Process::new(&oci_process, false); + + assert!(process.set_oom_score_adj().is_ok()); + assert_eq!( + read_to_string(Path::new("/proc/self/oom_score_adj")).unwrap(), + String::from("100\n") + ); + } + + #[test] + #[ignore = "setrlimit may not be permitted"] + fn test_set_rlimits() { + let mut oci_process = init_oci_process(); + let rlimits = Rlimits { + rlimit_type: String::from("RLIMIT_CORE"), + soft: 10, + hard: 20, + }; + oci_process.rlimits = Some(vec![rlimits]); + let process = Process::new(&oci_process, false); + + assert!(process.set_rlimits().is_ok()); + assert_eq!(getrlimit(Resource::RLIMIT_CORE).unwrap().0, 10); + assert_eq!(getrlimit(Resource::RLIMIT_CORE).unwrap().1, 20); + } + + #[test] + fn test_set_io_priority() { + let mut oci_process = init_oci_process(); + let io_pri = IoPriority { + class: IoPriClass::IoprioClassBe, + priority: 7, + }; + oci_process.ioPriority = Some(io_pri.clone()); + let process = Process::new(&oci_process, false); + + assert!(process.set_io_priority().is_ok()); + + let io_prio_who_process: libc::c_int = 1; + let io_prio_who_pid = 0; + let ioprio = unsafe { + libc::syscall(libc::SYS_ioprio_get, io_prio_who_process, io_prio_who_pid) + }; + assert_eq!(ioprio, (2 as i64) << 13 | io_pri.priority); + } + + #[test] + fn test_set_scheduler() { + let mut oci_process = init_oci_process(); + let scheduler = Scheduler { + policy: SchedPolicy::SchedOther, + nice: None, + priority: None, + flags: None, + runtime: None, + deadline: None, + period: None, + }; + oci_process.scheduler = Some(scheduler); + let process = Process::new(&oci_process, false); + + assert!(process.set_scheduler().is_ok()); + } + } +} -- Gitee From d7ea98ffa9a42038105cee5eb8d9bd0b040cfc2c Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 25 Sep 2024 04:06:14 +0800 Subject: [PATCH 2174/2187] ozonec/process: Add more unit test cases --- ozonec/src/linux/process.rs | 112 +++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index e3c96224a..da93545a0 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -436,9 +436,10 @@ mod tests { use nix::sys::resource::{getrlimit, Resource}; use rusty_fork::rusty_fork_test; + use unistd::getcwd; use oci_spec::{ - linux::{IoPriority, SchedPolicy, Scheduler}, + linux::{Capbilities, IoPriority, SchedPolicy, Scheduler}, posix::{Rlimits, User}, }; @@ -498,6 +499,39 @@ mod tests { assert!(process.set_tty(None, false).is_err()); } + #[test] + fn test_chdir_cwd() { + let oci_process = init_oci_process(); + let process = Process::new(&oci_process, false); + + assert!(process.chdir_cwd().is_ok()); + assert_eq!(getcwd().unwrap().to_str().unwrap(), "/"); + } + + #[test] + fn test_set_envs() { + let mut oci_process = init_oci_process(); + oci_process.env = Some(vec![ + String::from("OZONEC_ENV_1=1"), + String::from("=OZONEC_ENV_2"), + String::from("OZONEC_ENV"), + ]); + let process = Process::new(&oci_process, false); + + process.set_envs(); + for (key, value) in env::vars() { + if key == "OZONEC_ENV_1" { + assert_eq!(value, "1"); + continue; + } + assert_ne!(value, "OZONEC_ENV_2"); + assert_ne!(key, "OZONEC_ENV"); + assert_ne!(value, "OZONEC_ENV"); + } + + env::remove_var("OZONEC_ENV_1"); + } + rusty_fork_test! { #[test] #[ignore = "oom_score_adj may not be permitted to set"] @@ -567,5 +601,81 @@ mod tests { assert!(process.set_scheduler().is_ok()); } + + #[test] + fn test_set_no_new_privileges() { + let mut oci_process = init_oci_process(); + oci_process.noNewPrivileges = Some(true); + let process = Process::new(&oci_process, false); + + assert!(process.set_no_new_privileges().is_ok()); + } + + #[test] + #[ignore = "capset may not be permitted"] + fn test_drop_capabilities() { + let mut oci_process = init_oci_process(); + let caps = Capbilities { + effective: Some(vec![ + String::from("CAP_DAC_OVERRIDE"), + String::from("CAP_DAC_READ_SEARCH"), + String::from("CAP_SETFCAP"), + ]), + bounding: Some(vec![ + String::from("CAP_DAC_OVERRIDE"), + String::from("CAP_DAC_READ_SEARCH"), + ]), + inheritable: Some(vec![String::from("CAP_DAC_READ_SEARCH")]), + permitted: Some(vec![ + String::from("CAP_DAC_OVERRIDE"), + String::from("CAP_DAC_READ_SEARCH"), + String::from("CAP_SETFCAP"), + ]), + ambient: Some(vec![String::from("CAP_DAC_READ_SEARCH")]), + }; + oci_process.capabilities = Some(caps); + let process = Process::new(&oci_process, false); + + assert!(process.drop_capabilities().is_ok()); + let mut caps = caps::read(None, CapSet::Bounding).unwrap(); + assert_eq!(caps.len(), 2); + assert!(caps.get(&Capability::CAP_DAC_OVERRIDE).is_some()); + assert!(caps.get(&Capability::CAP_DAC_READ_SEARCH).is_some()); + caps = caps::read(None, CapSet::Effective).unwrap(); + assert_eq!(caps.len(), 3); + assert!(caps.get(&Capability::CAP_DAC_OVERRIDE).is_some()); + assert!(caps.get(&Capability::CAP_DAC_READ_SEARCH).is_some()); + assert!(caps.get(&Capability::CAP_SETFCAP).is_some()); + caps = caps::read(None, CapSet::Inheritable).unwrap(); + assert_eq!(caps.len(), 1); + assert!(caps.get(&Capability::CAP_DAC_READ_SEARCH).is_some()); + caps = caps::read(None, CapSet::Permitted).unwrap(); + assert_eq!(caps.len(), 3); + assert!(caps.get(&Capability::CAP_DAC_OVERRIDE).is_some()); + assert!(caps.get(&Capability::CAP_DAC_READ_SEARCH).is_some()); + assert!(caps.get(&Capability::CAP_SETFCAP).is_some()); + caps = caps::read(None, CapSet::Ambient).unwrap(); + assert_eq!(caps.len(), 1); + assert!(caps.get(&Capability::CAP_DAC_READ_SEARCH).is_some()); + } + + #[test] + fn test_reset_capabilities() { + let oci_process = init_oci_process(); + let process = Process::new(&oci_process, false); + + assert!(process.reset_capabilities().is_ok()); + let permit_caps = caps::read(None, CapSet::Permitted).unwrap(); + let eff_caps = caps::read(None, CapSet::Effective).unwrap(); + assert_eq!(permit_caps, eff_caps); + } + + #[test] + fn test_clean_envs() { + let oci_process = init_oci_process(); + let process = Process::new(&oci_process, false); + process.clean_envs(); + assert_eq!(env::vars().count(), 0); + } } } -- Gitee From f4e244ddc9863633f2c6e1bd591b388e3891ecf4 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 29 Sep 2024 09:47:20 +0800 Subject: [PATCH 2175/2187] ozonec/namespace: Add unit test cases --- ozonec/src/linux/namespace.rs | 60 ++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/ozonec/src/linux/namespace.rs b/ozonec/src/linux/namespace.rs index 8adf610f3..819bd99ef 100644 --- a/ozonec/src/linux/namespace.rs +++ b/ozonec/src/linux/namespace.rs @@ -67,9 +67,18 @@ impl NsController { #[cfg(test)] pub mod tests { + use std::{path::PathBuf, thread::sleep, time::Duration}; + + use nix::sys::{ + signal::{self, Signal}, + wait::{waitpid, WaitStatus}, + }; + + use crate::linux::process::clone_process; + use super::*; - pub fn set_namespace(ns_type: NamespaceType) { + fn init_ns_controller(ns_type: NamespaceType) -> NsController { let mut ns_ctrl = NsController { namespaces: HashMap::new(), }; @@ -78,6 +87,55 @@ pub mod tests { path: None, }; ns_ctrl.namespaces.insert(ns_type.try_into().unwrap(), ns); + ns_ctrl + } + + pub fn set_namespace(ns_type: NamespaceType) { + let ns_ctrl = init_ns_controller(ns_type); ns_ctrl.set_namespace(ns_type).unwrap(); } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_namespace() { + let mut ns_ctrl = init_ns_controller(NamespaceType::Mount); + let fst_child = clone_process("test_set_namespace_with_unshare", || { + assert!(ns_ctrl.set_namespace(NamespaceType::Mount).is_ok()); + sleep(Duration::from_secs(10)); + Ok(1) + }) + .unwrap(); + + let ns_path = PathBuf::from(format!("/proc/{}/ns/mnt", fst_child.as_raw())); + ns_ctrl + .namespaces + .get_mut(&CloneFlags::CLONE_NEWNS) + .unwrap() + .path = Some(ns_path); + let sec_child = clone_process("test_set_namespace_with_setns", || { + assert!(ns_ctrl.set_namespace(NamespaceType::Mount).is_ok()); + Ok(1) + }) + .unwrap(); + + match waitpid(sec_child, None) { + Ok(WaitStatus::Exited(_, s)) => { + assert_eq!(s, 1); + } + Ok(_) => (), + Err(e) => { + panic!("Failed to waitpid for unshare process: {e}"); + } + } + signal::kill(fst_child.clone(), Signal::SIGKILL).unwrap(); + match waitpid(fst_child, None) { + Ok(WaitStatus::Exited(_, s)) => { + assert_eq!(s, 1); + } + Ok(_) => (), + Err(e) => { + panic!("Failed to waitpid for setns process: {e}"); + } + } + } } -- Gitee From a3a7f0dca46eadb6d6e2c5c517e1a96d5b69adc8 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 29 Sep 2024 14:26:51 +0800 Subject: [PATCH 2176/2187] ozonec/rootfs: Add unit test cases --- ozonec/src/linux/container.rs | 4 +- ozonec/src/linux/rootfs.rs | 153 ++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 2 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 583a64f0d..261e2fbd7 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -794,7 +794,7 @@ impl Container for LinuxContainer { } #[cfg(test)] -mod tests { +pub mod tests { use chrono::DateTime; use fs::{remove_dir_all, File}; use nix::sys::stat::stat; @@ -808,7 +808,7 @@ mod tests { use super::*; - fn init_config() -> RuntimeConfig { + pub fn init_config() -> RuntimeConfig { let root = Root { path: String::from("/tmp/ozonec/bundle/rootfs"), readonly: true, diff --git a/ozonec/src/linux/rootfs.rs b/ozonec/src/linux/rootfs.rs index a58d9e6b3..5dd0a314e 100644 --- a/ozonec/src/linux/rootfs.rs +++ b/ozonec/src/linux/rootfs.rs @@ -232,3 +232,156 @@ impl Rootfs { Ok(()) } } + +#[cfg(test)] +mod tests { + use std::fs::{self, create_dir_all, read_link, remove_dir_all}; + + use nix::unistd::chdir; + use rusty_fork::rusty_fork_test; + + use crate::linux::{container::tests::init_config, namespace::tests::set_namespace}; + use oci_spec::linux::NamespaceType; + + use super::*; + + fn init_rootfs(path: &str, propagation: Option, mounts: Vec) -> Rootfs { + let path = PathBuf::from(path); + create_dir_all(&path).unwrap(); + Rootfs::new(path, propagation, mounts, true, Vec::new()).unwrap() + } + + #[test] + fn test_rootfs_new() { + let path = PathBuf::from("/test_rootfs_new"); + assert!(Rootfs::new(path, None, Vec::new(), true, Vec::new()).is_err()); + } + + #[test] + fn test_get_mount_flags() { + assert_eq!( + Rootfs::get_mount_flags(Some(String::from("shared"))).unwrap(), + MsFlags::MS_SHARED + ); + assert_eq!( + Rootfs::get_mount_flags(Some(String::from("private"))).unwrap(), + MsFlags::MS_PRIVATE + ); + assert_eq!( + Rootfs::get_mount_flags(Some(String::from("slave"))).unwrap(), + MsFlags::MS_SLAVE + ); + assert_eq!( + Rootfs::get_mount_flags(Some(String::from("unbindable"))).unwrap(), + MsFlags::MS_UNBINDABLE + ); + assert_eq!( + Rootfs::get_mount_flags(None).unwrap(), + MsFlags::MS_REC | MsFlags::MS_SLAVE + ); + assert!(Rootfs::get_mount_flags(Some(String::from("unbind"))).is_err()); + } + + rusty_fork_test! { + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_propagation() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let rootfs = init_rootfs( + "/tmp/ozonec/test_set_propagation", + Some(String::from("shared")), + Vec::new(), + ); + + assert!(rootfs.set_propagation().is_ok()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_make_parent_mount_private() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + + let parent = PathBuf::from("/tmp/ozonec/test_make_parent_mount_private"); + create_dir_all(&parent).unwrap(); + nix::mount::mount( + Some(&parent), + &parent, + None::<&str>, + MsFlags::MS_BIND, + None::<&str>, + ) + .unwrap(); + let rootfs = init_rootfs( + "/tmp/ozonec/test_make_parent_mount_private/rootfs", + Some(String::from("shared")), + Vec::new(), + ); + + assert!(rootfs.make_parent_mount_private().is_ok()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_default_symlinks() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let mounts = vec![ + OciMount { + destination: String::from("/proc"), + source: Some(String::from("/proc")), + options: Some(Vec::new()), + fs_type: Some(String::from("proc")), + uidMappings: None, + gidMappings: None, + }, + OciMount { + destination: String::from("/dev"), + source: Some(String::from("tmpfs")), + options: Some(vec![ + String::from("nosuid"), + String::from("strictatime"), + String::from("mode=755"), + String::from("size=65536k"), + ]), + fs_type: Some(String::from("tmpfs")), + uidMappings: None, + gidMappings: None, + }, + ]; + let rootfs = init_rootfs( + "/tmp/ozonec/test_set_default_symlinks", + Some(String::from("shared")), + mounts, + ); + rootfs.mount().unwrap(); + + let mut config = init_config(); + config.root.path = rootfs.path.to_string_lossy().to_string(); + rootfs.do_mounts(&config).unwrap(); + + assert!(rootfs.set_default_symlinks().is_ok()); + chdir(&rootfs.path).unwrap(); + let mut path = PathBuf::from("dev/fd"); + let mut metadata = fs::symlink_metadata(&path).unwrap(); + assert!(metadata.is_symlink()); + assert_eq!(read_link(&path).unwrap(), PathBuf::from("/proc/self/fd")); + path = PathBuf::from("dev/stdin"); + metadata = fs::symlink_metadata(&path).unwrap(); + assert!(metadata.is_symlink()); + assert_eq!(read_link(&path).unwrap(), PathBuf::from("/proc/self/fd/0")); + path = PathBuf::from("dev/stdout"); + metadata = fs::symlink_metadata(&path).unwrap(); + assert!(metadata.is_symlink()); + assert_eq!(read_link(&path).unwrap(), PathBuf::from("/proc/self/fd/1")); + path = PathBuf::from("dev/stderr"); + metadata = fs::symlink_metadata(&path).unwrap(); + assert!(metadata.is_symlink()); + assert_eq!(read_link(&path).unwrap(), PathBuf::from("/proc/self/fd/2")); + } + } +} -- Gitee From ae5a691fa3f19947a7a4fe738cf95a8c01facd73 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 29 Sep 2024 17:13:13 +0800 Subject: [PATCH 2177/2187] ozonec/rootfs: Add more unit test cases --- ozonec/src/linux/device.rs | 6 +- ozonec/src/linux/rootfs.rs | 123 ++++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/ozonec/src/linux/device.rs b/ozonec/src/linux/device.rs index a008fd0f1..8ecc568b5 100644 --- a/ozonec/src/linux/device.rs +++ b/ozonec/src/linux/device.rs @@ -34,7 +34,7 @@ impl Device { Self { rootfs } } - fn default_devices(&self) -> Vec { + pub fn default_devices(&self) -> Vec { vec![ DeviceInfo { path: self.rootfs.join("dev/null"), @@ -227,8 +227,8 @@ impl Device { } } -struct DeviceInfo { - path: PathBuf, +pub struct DeviceInfo { + pub path: PathBuf, dev_type: String, major: i64, minor: i64, diff --git a/ozonec/src/linux/rootfs.rs b/ozonec/src/linux/rootfs.rs index 5dd0a314e..b7854a3f2 100644 --- a/ozonec/src/linux/rootfs.rs +++ b/ozonec/src/linux/rootfs.rs @@ -235,7 +235,10 @@ impl Rootfs { #[cfg(test)] mod tests { - use std::fs::{self, create_dir_all, read_link, remove_dir_all}; + use std::{ + fs::{self, create_dir_all, read_link, remove_dir_all}, + os::unix::fs::FileTypeExt, + }; use nix::unistd::chdir; use rusty_fork::rusty_fork_test; @@ -383,5 +386,123 @@ mod tests { assert!(metadata.is_symlink()); assert_eq!(read_link(&path).unwrap(), PathBuf::from("/proc/self/fd/2")); } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_link_ptmx() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let mounts = vec![OciMount { + destination: String::from("/dev"), + source: Some(String::from("tmpfs")), + options: Some(vec![ + String::from("nosuid"), + String::from("strictatime"), + String::from("mode=755"), + String::from("size=65536k"), + ]), + fs_type: Some(String::from("tmpfs")), + uidMappings: None, + gidMappings: None, + }]; + let rootfs = init_rootfs( + "/tmp/ozonec/test_link_ptmx", + Some(String::from("shared")), + mounts, + ); + let mut config = init_config(); + config.root.path = rootfs.path.to_string_lossy().to_string(); + rootfs.do_mounts(&config).unwrap(); + + assert!(rootfs.link_ptmx().is_ok()); + + chdir(&rootfs.path).unwrap(); + let path = PathBuf::from("dev/ptmx"); + let metadata = fs::symlink_metadata(&path).unwrap(); + assert!(metadata.is_symlink()); + assert_eq!(read_link(&path).unwrap(), PathBuf::from("pts/ptmx")); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_create_default_devices() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let mounts = vec![OciMount { + destination: String::from("/dev"), + source: Some(String::from("tmpfs")), + options: Some(vec![ + String::from("nosuid"), + String::from("strictatime"), + String::from("mode=755"), + String::from("size=65536k"), + ]), + fs_type: Some(String::from("tmpfs")), + uidMappings: None, + gidMappings: None, + }]; + let rootfs = init_rootfs( + "/tmp/ozonec/test_create_default_devices", + Some(String::from("shared")), + mounts, + ); + let mut config = init_config(); + config.root.path = rootfs.path.to_string_lossy().to_string(); + rootfs.do_mounts(&config).unwrap(); + + assert!(rootfs.create_default_devices(false).is_ok()); + for dev in Device::new(rootfs.path.clone()).default_devices() { + assert!(dev.path.exists()); + let metadata = fs::metadata(&dev.path).unwrap(); + assert!(metadata.file_type().is_char_device()); + } + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_create_devices() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + + let mounts = vec![OciMount { + destination: String::from("/dev"), + source: Some(String::from("tmpfs")), + options: Some(vec![ + String::from("nosuid"), + String::from("strictatime"), + String::from("mode=755"), + String::from("size=65536k"), + ]), + fs_type: Some(String::from("tmpfs")), + uidMappings: None, + gidMappings: None, + }]; + let rootfs = init_rootfs( + "/tmp/ozonec/test_create_devices", + Some(String::from("shared")), + mounts, + ); + let mut config = init_config(); + config.root.path = rootfs.path.to_string_lossy().to_string(); + rootfs.do_mounts(&config).unwrap(); + + let devices = vec![OciDevice { + dev_type: String::from("c"), + path: String::from("/dev/test"), + major: Some(1), + minor: Some(3), + fileMode: Some(0o666u32), + uid: None, + gid: None, + }]; + assert!(rootfs.create_devices(&devices, true).is_ok()); + let path = rootfs.path.join("dev/test"); + assert!(path.exists()); + let metadata = fs::metadata(&path).unwrap(); + assert!(metadata.file_type().is_char_device()); + } } } -- Gitee From e52c897a88c321478e4428538dc940475416724d Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 29 Sep 2024 20:06:39 +0800 Subject: [PATCH 2178/2187] ozonec/seccomp: Add unit test cases --- ozonec/src/linux/seccomp.rs | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/ozonec/src/linux/seccomp.rs b/ozonec/src/linux/seccomp.rs index 36903d43e..3fdc35e3c 100644 --- a/ozonec/src/linux/seccomp.rs +++ b/ozonec/src/linux/seccomp.rs @@ -119,3 +119,75 @@ pub fn set_seccomp(seccomp: &Seccomp) -> Result<()> { Ok(()) } + +#[cfg(test)] +mod tests { + use rusty_fork::rusty_fork_test; + + use oci_spec::linux::{SeccompSyscall, SeccompSyscallArg}; + + use super::*; + + #[test] + fn test_check_seccomp() { + let mut seccomp = Seccomp { + defaultAction: OciSeccompAction::ScmpActNotify, + defaultErrnoRet: None, + architectures: None, + flags: None, + listennerPath: None, + seccompFd: None, + listenerMetadata: None, + syscalls: None, + }; + assert!(check_seccomp(&seccomp).is_err()); + + seccomp.defaultAction = OciSeccompAction::ScmpActAllow; + let syscall = SeccompSyscall { + names: vec![String::from("write")], + action: OciSeccompAction::ScmpActNotify, + errnoRet: None, + args: None, + }; + seccomp.syscalls = Some(vec![syscall]); + assert!(check_seccomp(&seccomp).is_err()); + } + + rusty_fork_test! { + #[test] + fn test_set_seccomp() { + let mut seccomp = Seccomp { + defaultAction: OciSeccompAction::ScmpActAllow, + defaultErrnoRet: None, + architectures: None, + flags: None, + listennerPath: None, + seccompFd: None, + listenerMetadata: None, + syscalls: None, + }; + let syscall = SeccompSyscall { + names: vec![String::from("write")], + action: OciSeccompAction::ScmpActKill, + errnoRet: None, + args: Some(vec![ + SeccompSyscallArg { + index: 0, + value: 0, + valueTwo: Some(0), + op: SeccompOp::ScmpCmpEq, + }, + SeccompSyscallArg { + index: 2, + value: 0, + valueTwo: Some(0), + op: SeccompOp::ScmpCmpMaskedEq, + }, + ]), + }; + seccomp.syscalls = Some(vec![syscall]); + + assert!(set_seccomp(&seccomp).is_ok()); + } + } +} -- Gitee From 8fd872861d993e66da5bee3aae0b0d7ebd32eca2 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Sun, 29 Sep 2024 20:37:15 +0800 Subject: [PATCH 2179/2187] ozonec/channel: Add unit test cases --- ozonec/src/linux/mod.rs | 2 ++ ozonec/src/utils/channel.rs | 46 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index bde83b87d..2ea9b63f2 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -23,4 +23,6 @@ mod terminal; pub use container::LinuxContainer; pub use notify_socket::NotifyListener; +#[allow(unused_imports)] +pub use process::clone_process; pub use process::Process; diff --git a/ozonec/src/utils/channel.rs b/ozonec/src/utils/channel.rs index 97ff2dc7c..41b2b08b8 100644 --- a/ozonec/src/utils/channel.rs +++ b/ozonec/src/utils/channel.rs @@ -213,3 +213,49 @@ impl Channel { .with_context(|| "Failed to send container process pid") } } + +#[cfg(test)] +mod tests { + use nix::sys::wait::{waitpid, WaitStatus}; + use unistd::getpid; + + use crate::linux::clone_process; + + use super::*; + + #[test] + fn test_channel() { + let channel = Channel::::new().unwrap(); + let child = clone_process("test_channel", || { + channel.receiver.close().unwrap(); + + channel.send_container_created().unwrap(); + channel.send_init_pid(getpid()).unwrap(); + channel.send_id_mappings().unwrap(); + channel.send_id_mappings_done().unwrap(); + + channel.sender.close().unwrap(); + Ok(0) + }) + .unwrap(); + + channel.sender.close().unwrap(); + + channel.recv_container_created().unwrap(); + channel.recv_init_pid().unwrap(); + channel.recv_id_mappings().unwrap(); + channel.recv_id_mappings_done().unwrap(); + + channel.receiver.close().unwrap(); + + match waitpid(child, None) { + Ok(WaitStatus::Exited(_, s)) => { + assert_eq!(s, 0); + } + Ok(_) => (), + Err(e) => { + panic!("Failed to waitpid for child process: {e}"); + } + } + } +} -- Gitee From c67cf98ea808c328a2613e5ddd8b55c85c8fb025 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Mon, 30 Sep 2024 11:14:55 +0800 Subject: [PATCH 2180/2187] ozonec/logger: Add unit test cases --- ozonec/src/utils/logger.rs | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/ozonec/src/utils/logger.rs b/ozonec/src/utils/logger.rs index 472650230..1251de8a9 100644 --- a/ozonec/src/utils/logger.rs +++ b/ozonec/src/utils/logger.rs @@ -238,3 +238,55 @@ pub fn init(path: &Option, debug: bool) -> Result<()> { .with_context(|| "Logger has been already set")?; Ok(()) } + +#[cfg(test)] +mod tests { + use std::{fs, os::unix::fs::MetadataExt}; + + use super::*; + + #[test] + fn test_logger_init() { + assert!(init(&Some(PathBuf::from("/tmp/ozonec.log")), false).is_ok()); + remove_file(Path::new("/tmp/ozonec.log")).unwrap(); + } + + #[test] + fn test_logger_rotate() { + let log_file = PathBuf::from("/tmp/ozonec.log"); + let logger = Logger::new(&Some(log_file.clone()), Level::Debug).unwrap(); + let mut locked_rotate = logger.rotate.lock().unwrap(); + // Time in metadata are not changed as the file descriptor is still opened. + let inode = fs::metadata(&log_file).unwrap().ino(); + for i in 1..LOG_ROTATE_CNT_MAX { + let file = format!("{}{}", locked_rotate.path, i); + let path = Path::new(&file); + File::create(path).unwrap(); + } + + locked_rotate.size = Wrapping(0); + assert!(locked_rotate.rotate(1024).is_ok()); + let mut new_inode = fs::metadata(&log_file).unwrap().ino(); + assert_eq!(inode, new_inode); + + locked_rotate.size = Wrapping(LOG_ROTATE_SIZE_MAX); + assert!(locked_rotate.rotate(1024).is_ok()); + new_inode = fs::metadata(&log_file).unwrap().ino(); + assert_ne!(inode, new_inode); + assert_eq!(locked_rotate.size, Wrapping(0)); + + locked_rotate.size = Wrapping(0); + locked_rotate.created_day = formatted_time(wall_time().0)[2] - 1; + assert!(locked_rotate.rotate(1024).is_ok()); + new_inode = fs::metadata(&log_file).unwrap().ino(); + assert_ne!(inode, new_inode); + assert_eq!(locked_rotate.size, Wrapping(0)); + + for i in 1..LOG_ROTATE_CNT_MAX { + let file = format!("{}{}", locked_rotate.path, i); + let path = Path::new(&file); + remove_file(path).unwrap(); + } + remove_file(Path::new("/tmp/ozonec.log")).unwrap(); + } +} -- Gitee From ab5a8f6aa73a388b09a9c642805d3add87de9c15 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 2 Oct 2024 10:57:48 +0800 Subject: [PATCH 2181/2187] ozonec/oci_spec: Add a unit test case for ContainerStatus --- ozonec/oci_spec/src/state.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ozonec/oci_spec/src/state.rs b/ozonec/oci_spec/src/state.rs index fe8180527..960e3b0f1 100644 --- a/ozonec/oci_spec/src/state.rs +++ b/ozonec/oci_spec/src/state.rs @@ -87,4 +87,24 @@ mod tests { assert!(state.annotations.contains_key("myKey")); assert_eq!(state.annotations.get("myKey"), Some(&"myValue".to_string())); } + + #[test] + fn test_container_status_to_string() { + assert_eq!( + ContainerStatus::Creating.to_string(), + String::from("creating") + ); + assert_eq!( + ContainerStatus::Created.to_string(), + String::from("created") + ); + assert_eq!( + ContainerStatus::Running.to_string(), + String::from("running") + ); + assert_eq!( + ContainerStatus::Stopped.to_string(), + String::from("stopped") + ); + } } -- Gitee From b847e7bd32ab4cda4d78eac306e7f862734e276c Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 2 Oct 2024 11:11:18 +0800 Subject: [PATCH 2182/2187] ozonec/container: Add more unit test cases --- ozonec/src/linux/container.rs | 272 +++++++++++++++++++++++++++++++++- ozonec/src/linux/process.rs | 4 +- 2 files changed, 271 insertions(+), 5 deletions(-) diff --git a/ozonec/src/linux/container.rs b/ozonec/src/linux/container.rs index 261e2fbd7..0e627fb4f 100644 --- a/ozonec/src/linux/container.rs +++ b/ozonec/src/linux/container.rs @@ -795,15 +795,23 @@ impl Container for LinuxContainer { #[cfg(test)] pub mod tests { + use std::ffi::CStr; + use chrono::DateTime; - use fs::{remove_dir_all, File}; + use fs::{read_to_string, remove_dir_all, File}; + use libc::getdomainname; use nix::sys::stat::stat; - use unistd::getpid; + use rusty_fork::rusty_fork_test; + use unistd::{gethostname, getpid}; + use crate::linux::{ + mount::Mount, namespace::tests::set_namespace, process::tests::init_oci_process, + }; use oci_spec::{ - linux::LinuxPlatform, + linux::{LinuxPlatform, Namespace}, posix::{Root, User}, process::Process as OciProcess, + runtime::Mount as OciMount, }; use super::*; @@ -989,4 +997,262 @@ pub mod tests { File::create(¬ify_socket).unwrap(); assert_eq!(container.status().unwrap(), ContainerStatus::Created); } + + #[test] + fn test_is_namespace_set() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let mut config = init_config(); + config.linux.as_mut().unwrap().namespaces.push(Namespace { + ns_type: NamespaceType::Mount, + path: None, + }); + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_is_namespace_set"), + &String::from("/tmp/ozonec/test_is_namespace_set"), + &config, + &None, + &mut exist, + ) + .unwrap(); + + assert!(container.is_namespace_set(NamespaceType::Mount).unwrap()); + assert!(!container.is_namespace_set(NamespaceType::User).unwrap()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_pid_namespace() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let mut config = init_config(); + config.linux.as_mut().unwrap().namespaces.push(Namespace { + ns_type: NamespaceType::Pid, + path: None, + }); + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_set_pid_namespace"), + &String::from("/tmp/ozonec/test_set_pid_namespace"), + &config, + &None, + &mut exist, + ) + .unwrap(); + + assert!(container.set_pid_namespace().is_ok()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_id_mappings() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let mut config = init_config(); + let linux = config.linux.as_mut().unwrap(); + linux.namespaces = vec![Namespace { + ns_type: NamespaceType::User, + path: None, + }]; + linux.uidMappings = Some(vec![IdMapping { + containerID: 0, + hostID: 0, + size: 1000, + }]); + linux.gidMappings = Some(vec![IdMapping { + containerID: 0, + hostID: 0, + size: 1000, + }]); + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_set_id_mappings"), + &String::from("/tmp/ozonec/test_set_id_mappings"), + &config, + &None, + &mut exist, + ) + .unwrap(); + + let fst_channel = Channel::::new().unwrap(); + let sec_channel = Channel::::new().unwrap(); + let child = clone_process("test_set_id_mappings", || { + let process = Process::new(&init_oci_process(), false); + assert!(container + .set_user_namespace(&fst_channel, &sec_channel, &process) + .is_ok()); + Ok(1) + }) + .unwrap(); + + assert!(container + .set_id_mappings(&fst_channel, &sec_channel, &child) + .is_ok()); + let path = format!("/proc/{}/setgroups", child.as_raw().to_string()); + let setgroups = fs::read_to_string(path).unwrap(); + assert_eq!(setgroups.trim(), "deny"); + let path = format!("/proc/{}/uid_map", child.as_raw().to_string()); + let uid_map = fs::read_to_string(path).unwrap(); + let mut iter = uid_map.split_ascii_whitespace(); + assert_eq!(iter.next(), Some("0")); + assert_eq!(iter.next(), Some("0")); + assert_eq!(iter.next(), Some("1000")); + assert_eq!(iter.next(), None); + let path = format!("/proc/{}/gid_map", child.as_raw().to_string()); + let gid_map = fs::read_to_string(path).unwrap(); + let mut iter = gid_map.split_ascii_whitespace(); + assert_eq!(iter.next(), Some("0")); + assert_eq!(iter.next(), Some("0")); + assert_eq!(iter.next(), Some("1000")); + assert_eq!(iter.next(), None); + + match waitpid(child, None) { + Ok(WaitStatus::Exited(_, s)) => { + assert_eq!(s, 1); + } + Ok(_) => (), + Err(e) => { + panic!("Failed to waitpid for child process: {e}"); + } + } + } + + rusty_fork_test! { + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_readonly_paths() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let root = PathBuf::from("/tmp/ozonec/test_set_readonly_paths"); + let mut config = init_config(); + let path = root.to_string_lossy().to_string(); + config.linux.as_mut().unwrap().readonlyPaths = Some(vec![path.clone()]); + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_set_readonly_paths"), + &root.to_string_lossy().to_string(), + &config, + &None, + &mut exist, + ) + .unwrap(); + File::create(root.join("test")).unwrap(); + + assert!(container.set_readonly_paths().is_ok()); + let path = PathBuf::from(path).join("test"); + assert!(File::create(&path).is_err()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_masked_paths() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let root = PathBuf::from("/tmp/ozonec/test_set_masked_paths"); + let mut config = init_config(); + config.linux.as_mut().unwrap().maskedPaths = Some(vec![root.to_string_lossy().to_string()]); + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_set_masked_paths"), + &root.to_string_lossy().to_string(), + &config, + &None, + &mut exist, + ) + .unwrap(); + + File::create(root.join("test")).unwrap(); + assert!(container.set_masked_paths().is_ok()); + assert!(!root.join("test").exists()); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_rest_namespaces() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + let root = PathBuf::from("/tmp/ozonec/test_set_rest_namespaces"); + let mut config = init_config(); + config.linux.as_mut().unwrap().namespaces = vec![ + Namespace { + ns_type: NamespaceType::User, + path: None, + }, + Namespace { + ns_type: NamespaceType::Uts, + path: None, + }, + ]; + config.hostname = Some(String::from("test_set_rest_namespaces")); + config.domainname = Some(String::from("test_set_rest_namespaces")); + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_set_rest_namespaces"), + &root.to_string_lossy().to_string(), + &config, + &None, + &mut exist, + ) + .unwrap(); + + assert!(container.set_rest_namespaces().is_ok()); + assert_eq!( + gethostname().unwrap().to_str().unwrap(), + "test_set_rest_namespaces" + ); + let len = 100; + let mut domain: Vec = Vec::with_capacity(len); + unsafe { + getdomainname(domain.as_mut_ptr().cast(), len); + // Ensure always null-terminated. + domain.as_mut_ptr().wrapping_add(len - 1).write(0); + let len = CStr::from_ptr(domain.as_ptr().cast()).to_bytes().len(); + domain.set_len(len); + } + assert_eq!(String::from_utf8_lossy(&domain), "test_set_rest_namespaces"); + } + + #[test] + #[ignore = "unshare may not be permitted"] + fn test_set_sysctl_parameters() { + remove_dir_all("/tmp/ozonec").unwrap_or_default(); + + set_namespace(NamespaceType::Mount); + let root = PathBuf::from("/tmp/ozonec/test_set_sysctl_parameters"); + let mut config = init_config(); + config.linux.as_mut().unwrap().sysctl = Some(HashMap::new()); + let sysctl = &mut config.linux.as_mut().unwrap().sysctl; + sysctl + .as_mut() + .unwrap() + .insert(String::from("vm.oom_dump_tasks"), String::from("0")); + + let mut exist = false; + let container = LinuxContainer::new( + &String::from("test_set_sysctl_parameters"), + &root.to_string_lossy().to_string(), + &config, + &None, + &mut exist, + ) + .unwrap(); + + let mounts = vec![OciMount { + destination: String::from("/proc"), + source: Some(String::from("proc")), + options: None, + fs_type: Some(String::from("proc")), + uidMappings: None, + gidMappings: None, + }]; + let mnt = Mount::new(&root); + mnt.do_mounts(&mounts, &None).unwrap(); + + assert!(container.set_sysctl_parameters().is_ok()); + assert_eq!(read_to_string("/proc/sys/vm/oom_dump_tasks").unwrap().trim(), "0"); + } + } } diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index da93545a0..17b67cb6f 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -431,7 +431,7 @@ fn to_cap(value: &str) -> Result { } #[cfg(test)] -mod tests { +pub mod tests { use std::path::Path; use nix::sys::resource::{getrlimit, Resource}; @@ -445,7 +445,7 @@ mod tests { use super::*; - fn init_oci_process() -> OciProcess { + pub fn init_oci_process() -> OciProcess { let user = User { uid: 0, gid: 0, -- Gitee From 8ebee1a45f56dc48f10ec3c6ab949a6117055a19 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 9 Oct 2024 10:47:00 +0800 Subject: [PATCH 2183/2187] ozonec/exec: Add unit test cases --- ozonec/src/commands/exec.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ozonec/src/commands/exec.rs b/ozonec/src/commands/exec.rs index 51347ff73..ce15f572d 100644 --- a/ozonec/src/commands/exec.rs +++ b/ozonec/src/commands/exec.rs @@ -112,3 +112,17 @@ impl Exec { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_key_val() { + let (key, value): (String, String) = parse_key_val("OZONEC_LOG_LEVEL=info").unwrap(); + assert_eq!(key, "OZONEC_LOG_LEVEL"); + assert_eq!(value, "info"); + + assert!(parse_key_val::("OZONEC_LOG_LEVEL").is_err()); + } +} -- Gitee From be932178b637e4f24b6497f8512a50700e83999a Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 9 Oct 2024 10:54:54 +0800 Subject: [PATCH 2184/2187] ozonec/kill: Add unit test cases --- ozonec/src/commands/kill.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ozonec/src/commands/kill.rs b/ozonec/src/commands/kill.rs index d67a0079c..e9ab6350b 100644 --- a/ozonec/src/commands/kill.rs +++ b/ozonec/src/commands/kill.rs @@ -53,3 +53,19 @@ fn parse_signal(signal: &str) -> Result { } Ok(Signal::from_str(&uppercase_sig)?) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_signal() { + assert_eq!(parse_signal("9").unwrap(), Signal::SIGKILL); + assert_eq!(parse_signal("sigterm").unwrap(), Signal::SIGTERM); + assert_eq!(parse_signal("SIGBUS").unwrap(), Signal::SIGBUS); + assert_eq!(parse_signal("hup").unwrap(), Signal::SIGHUP); + assert_eq!(parse_signal("ABRT").unwrap(), Signal::SIGABRT); + assert!(parse_signal("100").is_err()); + assert!(parse_signal("ERROR").is_err()); + } +} -- Gitee From 7086477f21f46413998f27812af8d0fafad4b537 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Wed, 9 Oct 2024 22:50:34 +0800 Subject: [PATCH 2185/2187] ozonec/state: Add unit test cases --- ozonec/src/container/state.rs | 85 +++++++++++++++++++++++++++++++++++ ozonec/src/linux/mod.rs | 3 +- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/ozonec/src/container/state.rs b/ozonec/src/container/state.rs index e689a1ee6..659752a09 100644 --- a/ozonec/src/container/state.rs +++ b/ozonec/src/container/state.rs @@ -117,3 +117,88 @@ impl State { root.join(id).join("state.json") } } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use fs::{create_dir_all, remove_dir_all}; + use nix::unistd::getpid; + + use crate::linux::container::tests::init_config; + use oci_spec::{ + linux::{Namespace, NamespaceType}, + state::ContainerStatus, + }; + + use super::*; + + fn init_state(root: &Path, id: &str) -> State { + let oci_state = OciState { + ociVersion: String::from("1.2"), + id: String::from(id), + status: ContainerStatus::Created, + pid: 100, + bundle: root.to_string_lossy().to_string(), + annotations: HashMap::new(), + }; + State::new(root, root, oci_state, 0, SystemTime::now(), &init_config()) + } + + #[test] + fn test_state_update() { + let root = "/tmp/ozonec"; + remove_dir_all(root).unwrap_or_default(); + let mut state = init_state(Path::new(root), "test_state_update"); + state + .config + .as_mut() + .unwrap() + .linux + .as_mut() + .unwrap() + .namespaces + .push(Namespace { + ns_type: NamespaceType::Mount, + path: None, + }); + state.pid = getpid().as_raw(); + state.update(); + + for ns in &state + .config + .as_ref() + .unwrap() + .linux + .as_ref() + .unwrap() + .namespaces + { + assert_eq!( + ns.path.as_ref().unwrap().to_str().unwrap(), + format!( + "/proc/{}/ns/{}", + state.pid, + >::into(ns.ns_type) + ) + ); + } + } + + #[test] + fn test_state_load() { + let root = "/tmp/ozonec"; + remove_dir_all(root).unwrap_or_default(); + + let state = init_state(Path::new(root), "test_state_load"); + let dir = PathBuf::from(String::from(root)).join("test_state_load"); + create_dir_all(&dir).unwrap(); + + assert!(state.save().is_ok()); + assert!(dir.join("state.json").exists()); + let loaded_state = State::load(Path::new(root), "test_state_load").unwrap(); + assert_eq!(loaded_state.id, state.id); + assert!(state.remove_dir().is_ok()); + assert!(State::load(Path::new(root), "test_state_load").is_err()); + } +} diff --git a/ozonec/src/linux/mod.rs b/ozonec/src/linux/mod.rs index 2ea9b63f2..658f50c70 100644 --- a/ozonec/src/linux/mod.rs +++ b/ozonec/src/linux/mod.rs @@ -10,8 +10,9 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +pub mod container; + mod apparmor; -mod container; mod device; mod mount; mod namespace; -- Gitee From 3b1586552760585bbc14686b105b168206181285 Mon Sep 17 00:00:00 2001 From: frankyj915 Date: Thu, 10 Oct 2024 13:04:45 +0800 Subject: [PATCH 2186/2187] ozonec/process: Add unit test case for to_cap() --- ozonec/src/linux/process.rs | 97 +++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/ozonec/src/linux/process.rs b/ozonec/src/linux/process.rs index 17b67cb6f..2159727db 100644 --- a/ozonec/src/linux/process.rs +++ b/ozonec/src/linux/process.rs @@ -532,6 +532,103 @@ pub mod tests { env::remove_var("OZONEC_ENV_1"); } + #[test] + fn test_to_cap() { + assert_eq!( + to_cap("CAP_AUDIT_CONTROL").unwrap(), + Capability::CAP_AUDIT_CONTROL + ); + assert_eq!( + to_cap("CAP_AUDIT_READ").unwrap(), + Capability::CAP_AUDIT_READ + ); + assert_eq!( + to_cap("CAP_AUDIT_WRITE").unwrap(), + Capability::CAP_AUDIT_WRITE + ); + assert_eq!( + to_cap("CAP_BLOCK_SUSPEND").unwrap(), + Capability::CAP_BLOCK_SUSPEND + ); + assert_eq!(to_cap("CAP_BPF").unwrap(), Capability::CAP_BPF); + assert_eq!( + to_cap("CAP_CHECKPOINT_RESTORE").unwrap(), + Capability::CAP_CHECKPOINT_RESTORE + ); + assert_eq!(to_cap("CAP_CHOWN").unwrap(), Capability::CAP_CHOWN); + assert_eq!( + to_cap("CAP_DAC_OVERRIDE").unwrap(), + Capability::CAP_DAC_OVERRIDE + ); + assert_eq!( + to_cap("CAP_DAC_READ_SEARCH").unwrap(), + Capability::CAP_DAC_READ_SEARCH + ); + assert_eq!(to_cap("CAP_FOWNER").unwrap(), Capability::CAP_FOWNER); + assert_eq!(to_cap("CAP_FSETID").unwrap(), Capability::CAP_FSETID); + assert_eq!(to_cap("CAP_IPC_LOCK").unwrap(), Capability::CAP_IPC_LOCK); + assert_eq!(to_cap("CAP_IPC_OWNER").unwrap(), Capability::CAP_IPC_OWNER); + assert_eq!(to_cap("CAP_KILL").unwrap(), Capability::CAP_KILL); + assert_eq!(to_cap("CAP_LEASE").unwrap(), Capability::CAP_LEASE); + assert_eq!( + to_cap("CAP_LINUX_IMMUTABLE").unwrap(), + Capability::CAP_LINUX_IMMUTABLE + ); + assert_eq!(to_cap("CAP_MAC_ADMIN").unwrap(), Capability::CAP_MAC_ADMIN); + assert_eq!( + to_cap("CAP_MAC_OVERRIDE").unwrap(), + Capability::CAP_MAC_OVERRIDE + ); + assert_eq!(to_cap("CAP_MKNOD").unwrap(), Capability::CAP_MKNOD); + assert_eq!(to_cap("CAP_NET_ADMIN").unwrap(), Capability::CAP_NET_ADMIN); + assert_eq!( + to_cap("CAP_NET_BIND_SERVICE").unwrap(), + Capability::CAP_NET_BIND_SERVICE + ); + assert_eq!( + to_cap("CAP_NET_BROADCAST").unwrap(), + Capability::CAP_NET_BROADCAST + ); + assert_eq!(to_cap("CAP_NET_RAW").unwrap(), Capability::CAP_NET_RAW); + assert_eq!(to_cap("CAP_PERFMON").unwrap(), Capability::CAP_PERFMON); + assert_eq!(to_cap("CAP_SETGID").unwrap(), Capability::CAP_SETGID); + assert_eq!(to_cap("CAP_SETFCAP").unwrap(), Capability::CAP_SETFCAP); + assert_eq!(to_cap("CAP_SETPCAP").unwrap(), Capability::CAP_SETPCAP); + assert_eq!(to_cap("CAP_SETUID").unwrap(), Capability::CAP_SETUID); + assert_eq!(to_cap("CAP_SYS_ADMIN").unwrap(), Capability::CAP_SYS_ADMIN); + assert_eq!(to_cap("CAP_SYS_BOOT").unwrap(), Capability::CAP_SYS_BOOT); + assert_eq!( + to_cap("CAP_SYS_CHROOT").unwrap(), + Capability::CAP_SYS_CHROOT + ); + assert_eq!( + to_cap("CAP_SYS_MODULE").unwrap(), + Capability::CAP_SYS_MODULE + ); + assert_eq!(to_cap("CAP_SYS_NICE").unwrap(), Capability::CAP_SYS_NICE); + assert_eq!(to_cap("CAP_SYS_PACCT").unwrap(), Capability::CAP_SYS_PACCT); + assert_eq!( + to_cap("CAP_SYS_PTRACE").unwrap(), + Capability::CAP_SYS_PTRACE + ); + assert_eq!(to_cap("CAP_SYS_RAWIO").unwrap(), Capability::CAP_SYS_RAWIO); + assert_eq!( + to_cap("CAP_SYS_RESOURCE").unwrap(), + Capability::CAP_SYS_RESOURCE + ); + assert_eq!(to_cap("CAP_SYS_TIME").unwrap(), Capability::CAP_SYS_TIME); + assert_eq!( + to_cap("CAP_SYS_TTY_CONFIG").unwrap(), + Capability::CAP_SYS_TTY_CONFIG + ); + assert_eq!(to_cap("CAP_SYSLOG").unwrap(), Capability::CAP_SYSLOG); + assert_eq!( + to_cap("CAP_WAKE_ALARM").unwrap(), + Capability::CAP_WAKE_ALARM + ); + assert!(to_cap("CAP_TO_CAP").is_err()); + } + rusty_fork_test! { #[test] #[ignore = "oom_score_adj may not be permitted to set"] -- Gitee From 6775083eb7aef58259d07d10f94fd454d48f8b04 Mon Sep 17 00:00:00 2001 From: liuxiangdong Date: Thu, 10 Oct 2024 21:17:13 +0800 Subject: [PATCH 2187/2187] image: add `-r` to rename snapshot name Add `-r` in stratovirt-img snapshot method. eg: stratovirt-img snapshot -r old_snapshot_name new_snapshot_name img_path Signed-off-by: liuxiangdong --- block_backend/src/qcow2/mod.rs | 63 +++++++++++-- block_backend/src/qcow2/snapshot.rs | 8 +- docs/stratovirt-img.md | 4 +- image/src/img.rs | 141 +++++++++++++++++++++++++++- 4 files changed, 199 insertions(+), 17 deletions(-) diff --git a/block_backend/src/qcow2/mod.rs b/block_backend/src/qcow2/mod.rs index 30c72b605..a5a1fb19e 100644 --- a/block_backend/src/qcow2/mod.rs +++ b/block_backend/src/qcow2/mod.rs @@ -928,7 +928,7 @@ impl Qcow2Driver { if self.snapshot.snapshots_number() > 0 { new_snapshots_offset = self.alloc_cluster(new_snapshot_table_clusters, true)?; self.snapshot - .save_snapshot_table(new_snapshots_offset, &snap, false)?; + .save_snapshot_table(new_snapshots_offset, Some(&snap), false)?; } self.snapshot.snapshot_table_offset = new_snapshots_offset; @@ -968,7 +968,7 @@ impl Qcow2Driver { self.table.save_l1_table()?; // Update the snapshot information in qcow2 header. - self.update_snapshot_info_in_header(new_snapshots_offset, false)?; + self.update_snapshot_info_in_header(new_snapshots_offset, -1)?; // Discard unused clusters. self.refcount.sync_process_discards(OpCode::Discard); @@ -1048,7 +1048,7 @@ impl Qcow2Driver { // Append the new snapshot to the snapshot table and write new snapshot table to file. self.snapshot - .save_snapshot_table(new_snapshots_offset, &snap, true)?; + .save_snapshot_table(new_snapshots_offset, Some(&snap), true)?; // Free the old snapshot table cluster if snapshot exists. if self.header.snapshots_offset != 0 { @@ -1068,7 +1068,7 @@ impl Qcow2Driver { self.table.save_l1_table()?; // Update snapshot offset and num in qcow2 header. - self.update_snapshot_info_in_header(new_snapshots_offset, true)?; + self.update_snapshot_info_in_header(new_snapshots_offset, 1)?; // Add and update snapshot information in memory. self.snapshot.add_snapshot(snap); @@ -1080,14 +1080,10 @@ impl Qcow2Driver { Ok(()) } - fn update_snapshot_info_in_header(&mut self, snapshot_offset: u64, add: bool) -> Result<()> { + fn update_snapshot_info_in_header(&mut self, snapshot_offset: u64, add: i32) -> Result<()> { let mut new_header = self.header.clone(); new_header.snapshots_offset = snapshot_offset; - if add { - new_header.nb_snapshots += 1; - } else { - new_header.nb_snapshots -= 1; - } + new_header.nb_snapshots = (new_header.nb_snapshots as i32 + add) as u32; self.sync_aio .borrow_mut() .write_buffer(0, &new_header.to_vec())?; @@ -1422,6 +1418,11 @@ pub trait InternalSnapshotOps: Send + Sync { fn apply_snapshot(&mut self, name: String) -> Result<()>; fn list_snapshots(&self) -> String; fn get_status(&self) -> Arc>; + fn rename_snapshot( + &mut self, + old_snapshot_name: String, + new_snapshot_name: String, + ) -> Result<()>; } impl InternalSnapshotOps for Qcow2Driver { @@ -1461,6 +1462,48 @@ impl InternalSnapshotOps for Qcow2Driver { fn get_status(&self) -> Arc> { self.status.clone() } + + fn rename_snapshot( + &mut self, + old_snapshot_name: String, + new_snapshot_name: String, + ) -> Result<()> { + let snap_id = self.get_snapshot_by_name(&old_snapshot_name); + if snap_id < 0 { + bail!("Snapshot name {} doesn't exit!", old_snapshot_name); + } + + // update snapshot info in memory. + // Stratovirt-img will exit if next actions fail. And these modified snapshot information + // in memory will not affect. + self.snapshot.snapshots[snap_id as usize].name = new_snapshot_name; + + // write new snapshot info to new snapshot table. + let cluster_size = self.header.cluster_size(); + let snapshot_table_len = self.snapshot.snapshot_size; + let snapshot_table_clusters = bytes_to_clusters(snapshot_table_len, cluster_size).unwrap(); + let new_snapshots_offset = self.alloc_cluster(snapshot_table_clusters, true)?; + self.snapshot + .save_snapshot_table(new_snapshots_offset, None, true)?; + + // Delete old snapshot: Free the cluster of the old snapshot table. + self.refcount.update_refcount( + self.header.snapshots_offset, + snapshot_table_clusters, + -1, + false, + &Qcow2DiscardType::Snapshot, + )?; + self.flush()?; + + // Update the snapshot information in qcow2 header. + self.update_snapshot_info_in_header(new_snapshots_offset, 0)?; + + // Discard unused clusters. + self.refcount.sync_process_discards(OpCode::Discard); + + Ok(()) + } } // SAFETY: Send and Sync is not auto-implemented for raw pointer type in Aio. diff --git a/block_backend/src/qcow2/snapshot.rs b/block_backend/src/qcow2/snapshot.rs index 9e8345d56..f0b638f2a 100644 --- a/block_backend/src/qcow2/snapshot.rs +++ b/block_backend/src/qcow2/snapshot.rs @@ -98,18 +98,20 @@ impl InternalSnapshot { pub fn save_snapshot_table( &self, addr: u64, - extra_snap: &QcowSnapshot, + extra_snap: Option<&QcowSnapshot>, attach: bool, ) -> Result<()> { let mut buf = Vec::new(); for snap in &self.snapshots { - if !attach && snap.id == extra_snap.id { + if !attach && extra_snap.is_some() && snap.id == extra_snap.unwrap().id { continue; } buf.append(&mut snap.gen_snapshot_table_entry()); } if attach { - buf.append(&mut extra_snap.gen_snapshot_table_entry()); + if let Some(extra) = extra_snap { + buf.append(&mut extra.gen_snapshot_table_entry()); + } } self.sync_aio.borrow_mut().write_buffer(addr, &buf) } diff --git a/docs/stratovirt-img.md b/docs/stratovirt-img.md index 7021e5655..cca517a99 100644 --- a/docs/stratovirt-img.md +++ b/docs/stratovirt-img.md @@ -91,13 +91,14 @@ Operating internal snapshot for disk, it is only supported by qcow2. Command syntax: ```shell -snapshot [-l | -a snapshot_name | -c snapshot_name | -d snapshot_name] img_path +snapshot [-l | -a snapshot_name | -c snapshot_name | -d snapshot_name | -r old_snapshot_name new_snapshot_name] img_path ``` - -a snapshot_name: applies a snapshot (revert disk to saved state). - -c snapshot_name: creates a snapshot. - -d snapshot_name: deletes a snapshot. - -l: lists all snapshots in the given image. +- -r old_snapshot_name new_snapshot_name: change the name from 'old_Snapshot_name' to 'new_Snapshot_name'. Sample Configuration: @@ -106,6 +107,7 @@ stratovirt-img snapshot -c snapshot_name img_path stratovirt-img snapshot -a snapshot_name img_path stratovirt-img snapshot -d snapshot_name img_path stratovirt-img snapshot -l img_path +stratovirt-img snapshot -r old_snapshot_name new_snapshot_name img_path ``` Note: The internal snapshot is not supported by raw. diff --git a/image/src/img.rs b/image/src/img.rs index 3e359a3fc..1289889e1 100644 --- a/image/src/img.rs +++ b/image/src/img.rs @@ -37,6 +37,7 @@ enum SnapshotOperation { Delete, Apply, List, + Rename, } pub struct ImageFile { @@ -380,8 +381,11 @@ pub(crate) fn image_resize(mut args: Vec) -> Result<()> { } pub(crate) fn image_snapshot(args: Vec) -> Result<()> { - let mut arg_parser = - ArgsParse::create(vec!["l", "h", "help"], vec!["f", "c", "d", "a"], vec![]); + let mut arg_parser = ArgsParse::create( + vec!["l", "h", "help", "r"], + vec!["f", "c", "d", "a"], + vec![], + ); arg_parser.parse(args)?; if arg_parser.opt_present("h") || arg_parser.opt_present("help") { @@ -426,11 +430,21 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { snapshot_name = name; } + // Rename snapshot name. + let mut old_snapshot_name = String::from(""); + let mut new_snapshot_name = String::from(""); + if arg_parser.opt_present("r") { + snapshot_operation = Some(SnapshotOperation::Rename); + old_snapshot_name = arg_parser.free[0].clone(); + new_snapshot_name = arg_parser.free[1].clone(); + } + // Parse image path. let len = arg_parser.free.len(); let path = match len { 0 => bail!("Image snapshot requires path"), 1 => arg_parser.free[0].clone(), + 3 => arg_parser.free[2].clone(), _ => { let param = arg_parser.free[1].clone(); bail!("Unexpected argument: {}", param); @@ -475,6 +489,9 @@ pub(crate) fn image_snapshot(args: Vec) -> Result<()> { Some(SnapshotOperation::Apply) => { qcow2_driver.apply_snapshot(snapshot_name)?; } + Some(SnapshotOperation::Rename) => { + qcow2_driver.rename_snapshot(old_snapshot_name, new_snapshot_name)?; + } None => return Ok(()), }; @@ -531,7 +548,7 @@ create [-f fmt] [-o options] filename [size] info filename check [-r [leaks | all]] [-no_print_error] [-f fmt] filename resize [-f fmt] filename [+]size -snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename +snapshot [-l | -a snapshot | -c snapshot | -d snapshot | -r old_snapshot_name new_snapshot_name] filename Command parameters: 'filename' is a disk image filename @@ -552,6 +569,7 @@ Parameters to snapshot subcommand: '-c' creates a snapshot '-d' deletes a snapshot '-l' lists all snapshots in the given image + '-r' change the name of a snapshot "#, ); } @@ -1569,9 +1587,15 @@ mod test { let test_case = [ ("qcow2", "-c snapshot0 img_path", true), ("qcow2", "-f qcow2 -l img_path", true), + ("qcow2", "-r old_snapshot_name img_path", false), ("qcow2", "-d snapshot0 img_path", false), ("qcow2", "-a snapshot0 img_path", false), ("qcow2", "-c snapshot0 -l img_path", false), + ( + "raw", + "-r old_snapshot_name new_snapshot_name img_path", + false, + ), ("raw", "-f qcow2 -l img_path", false), ("raw", "-l img_path", false), ]; @@ -1683,6 +1707,117 @@ mod test { } } + /// Test the function of snapshot rename. + /// + /// TestStep: + /// 1. Create a new image. alloc a new cluster and write 1. + /// 2. Create snapshot named test_snapshot0, write 2 to the cluster. + /// 3. Create snapshot named test_snapshot1, write 3 to the cluster. + /// 4. Rename test_snapshot0 to test_snapshot0-new. + /// 5. Apply snapshot named test_snapshot0. + /// 6. Apply snapshot named test_snapshot0-new. + /// Expect: + /// 1. step 5 is failure and step 1/2/3/4/6 is success. + /// 2. The data read after snapshot apply is 2. + #[test] + fn test_snapshot_rename_basic() { + let path = "/tmp/test_snapshot_rename_basic.qcow2"; + let cluster_bits = 16; + let cluster_size = 1 << cluster_bits; + let refcount_bits = 16; + + // Create a new image. alloc a new cluster and write 1. + let test_image = TestQcow2Image::create(cluster_bits, refcount_bits, path, "+1G"); + let buf = vec![1_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + + // Create snapshot named test_snapshot0, write 2 to the cluster. + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot0".to_string(), + path.to_string() + ]) + .is_ok()); + let buf = vec![2_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + + // Create snapshot named test_snapshot1, write 3 to the cluster. + assert!(image_snapshot(vec![ + "-c".to_string(), + "test_snapshot1".to_string(), + path.to_string() + ]) + .is_ok()); + let buf = vec![3_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + + // Rename test_snapshot0 to test_snapshot0-new. + assert!(image_snapshot(vec![ + "-r".to_string(), + "test_snapshot0".to_string(), + "test_snapshot0-new".to_string(), + path.to_string() + ]) + .is_ok()); + + // Apply snapshot named test_snapshot0. + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot0".to_string(), + path.to_string() + ]) + .is_err()); + + // Apply snapshot named test_snapshot-new. + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot0-new".to_string(), + path.to_string() + ]) + .is_ok()); + + // The data read after snapshot apply is 2. + let buf = vec![0_u8; cluster_size as usize]; + assert!(test_image.read_data(0, &buf).is_ok()); + for elem in buf { + assert_eq!(elem, 1); + } + + // Rename non-existed snapshot name. + assert!(image_snapshot(vec![ + "-r".to_string(), + "test_snapshot11111".to_string(), + "test_snapshot11111-new".to_string(), + path.to_string() + ]) + .is_err()); + + let buf = vec![4_u8; cluster_size as usize]; + assert!(test_image.write_data(0, &buf).is_ok()); + + // Rename test_snapshot1 to test_snapshot123. + assert!(image_snapshot(vec![ + "-r".to_string(), + "test_snapshot1".to_string(), + "test_snapshot123".to_string(), + path.to_string() + ]) + .is_ok()); + + // Apply snapshot named test_snapshot123 + assert!(image_snapshot(vec![ + "-a".to_string(), + "test_snapshot123".to_string(), + path.to_string() + ]) + .is_ok()); + let buf = vec![0_u8; cluster_size as usize]; + assert!(test_image.read_data(0, &buf).is_ok()); + for elem in buf { + assert_eq!(elem, 2); + } + } + /// Test the function of resize image. /// /// TestStep: -- Gitee